diff -Nru wpa-1.0/COPYING wpa-2.1/COPYING --- wpa-1.0/COPYING 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/COPYING 2014-02-04 11:23:35.000000000 +0000 @@ -1,340 +1,22 @@ - GNU GENERAL PUBLIC LICENSE - Version 2, June 1991 +wpa_supplicant and hostapd +-------------------------- - Copyright (C) 1989, 1991 Free Software Foundation, Inc. - 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. +Copyright (c) 2002-2012, Jouni Malinen and contributors +All Rights Reserved. - Preamble - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -License is intended to guarantee your freedom to share and change free -software--to make sure the software is free for all its users. This -General Public License applies to most of the Free Software -Foundation's software and to any other program whose authors commit to -using it. (Some other Free Software Foundation software is covered by -the GNU Library General Public License instead.) You can apply it to -your programs, too. +See the README file for the current license terms. - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -this service if you wish), that you receive source code or can get it -if you want it, that you can change the software or use pieces of it -in new free programs; and that you know you can do these things. - - To protect your rights, we need to make restrictions that forbid -anyone to deny you these rights or to ask you to surrender the rights. -These restrictions translate to certain responsibilities for you if you -distribute copies of the software, or if you modify it. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must give the recipients all the rights that -you have. You must make sure that they, too, receive or can get the -source code. And you must show them these terms so they know their -rights. - - We protect your rights with two steps: (1) copyright the software, and -(2) offer you this license which gives you legal permission to copy, -distribute and/or modify the software. - - Also, for each author's protection and ours, we want to make certain -that everyone understands that there is no warranty for this free -software. If the software is modified by someone else and passed on, we -want its recipients to know that what they have is not the original, so -that any problems introduced by others will not reflect on the original -authors' reputations. - - Finally, any free program is threatened constantly by software -patents. We wish to avoid the danger that redistributors of a free -program will individually obtain patent licenses, in effect making the -program proprietary. To prevent this, we have made it clear that any -patent must be licensed for everyone's free use or not licensed at all. - - The precise terms and conditions for copying, distribution and -modification follow. - - GNU GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License applies to any program or other work which contains -a notice placed by the copyright holder saying it may be distributed -under the terms of this General Public License. The "Program", below, -refers to any such program or work, and a "work based on the Program" -means either the Program or any derivative work under copyright law: -that is to say, a work containing the Program or a portion of it, -either verbatim or with modifications and/or translated into another -language. (Hereinafter, translation is included without limitation in -the term "modification".) Each licensee is addressed as "you". - -Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running the Program is not restricted, and the output from the Program -is covered only if its contents constitute a work based on the -Program (independent of having been made by running the Program). -Whether that is true depends on what the Program does. - - 1. You may copy and distribute verbatim copies of the Program's -source code as you receive it, in any medium, provided that you -conspicuously and appropriately publish on each copy an appropriate -copyright notice and disclaimer of warranty; keep intact all the -notices that refer to this License and to the absence of any warranty; -and give any other recipients of the Program a copy of this License -along with the Program. - -You may charge a fee for the physical act of transferring a copy, and -you may at your option offer warranty protection in exchange for a fee. - - 2. You may modify your copy or copies of the Program or any portion -of it, thus forming a work based on the Program, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) You must cause the modified files to carry prominent notices - stating that you changed the files and the date of any change. - - b) You must cause any work that you distribute or publish, that in - whole or in part contains or is derived from the Program or any - part thereof, to be licensed as a whole at no charge to all third - parties under the terms of this License. - - c) If the modified program normally reads commands interactively - when run, you must cause it, when started running for such - interactive use in the most ordinary way, to print or display an - announcement including an appropriate copyright notice and a - notice that there is no warranty (or else, saying that you provide - a warranty) and that users may redistribute the program under - these conditions, and telling the user how to view a copy of this - License. (Exception: if the Program itself is interactive but - does not normally print such an announcement, your work based on - the Program is not required to print an announcement.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Program, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Program, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Program. - -In addition, mere aggregation of another work not based on the Program -with the Program (or with a work based on the Program) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may copy and distribute the Program (or a work based on it, -under Section 2) in object code or executable form under the terms of -Sections 1 and 2 above provided that you also do one of the following: - - a) Accompany it with the complete corresponding machine-readable - source code, which must be distributed under the terms of Sections - 1 and 2 above on a medium customarily used for software interchange; or, - - b) Accompany it with a written offer, valid for at least three - years, to give any third party, for a charge no more than your - cost of physically performing source distribution, a complete - machine-readable copy of the corresponding source code, to be - distributed under the terms of Sections 1 and 2 above on a medium - customarily used for software interchange; or, - - c) Accompany it with the information you received as to the offer - to distribute corresponding source code. (This alternative is - allowed only for noncommercial distribution and only if you - received the program in object code or executable form with such - an offer, in accord with Subsection b above.) - -The source code for a work means the preferred form of the work for -making modifications to it. For an executable work, complete source -code means all the source code for all modules it contains, plus any -associated interface definition files, plus the scripts used to -control compilation and installation of the executable. However, as a -special exception, the source code distributed need not include -anything that is normally distributed (in either source or binary -form) with the major components (compiler, kernel, and so on) of the -operating system on which the executable runs, unless that component -itself accompanies the executable. - -If distribution of executable or object code is made by offering -access to copy from a designated place, then offering equivalent -access to copy the source code from the same place counts as -distribution of the source code, even though third parties are not -compelled to copy the source along with the object code. - - 4. You may not copy, modify, sublicense, or distribute the Program -except as expressly provided under this License. Any attempt -otherwise to copy, modify, sublicense or distribute the Program is -void, and will automatically terminate your rights under this License. -However, parties who have received copies, or rights, from you under -this License will not have their licenses terminated so long as such -parties remain in full compliance. - - 5. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Program or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Program (or any work based on the -Program), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Program or works based on it. - - 6. Each time you redistribute the Program (or any work based on the -Program), the recipient automatically receives a license from the -original licensor to copy, distribute or modify the Program subject to -these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties to -this License. - - 7. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Program at all. For example, if a patent -license would not permit royalty-free redistribution of the Program by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Program. - -If any portion of this section is held invalid or unenforceable under -any particular circumstance, the balance of the section is intended to -apply and the section as a whole is intended to apply in other -circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system, which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 8. If the distribution and/or use of the Program is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Program under this License -may add an explicit geographical distribution limitation excluding -those countries, so that distribution is permitted only in or among -countries not thus excluded. In such case, this License incorporates -the limitation as if written in the body of this License. - - 9. The Free Software Foundation may publish revised and/or new versions -of the General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - -Each version is given a distinguishing version number. If the Program -specifies a version number of this License which applies to it and "any -later version", you have the option of following the terms and conditions -either of that version or of any later version published by the Free -Software Foundation. If the Program does not specify a version number of -this License, you may choose any version ever published by the Free Software -Foundation. - - 10. If you wish to incorporate parts of the Program into other free -programs whose distribution conditions are different, write to the author -to ask for permission. For software which is copyrighted by the Free -Software Foundation, write to the Free Software Foundation; we sometimes -make exceptions for this. Our decision will be guided by the two goals -of preserving the free status of all derivatives of our free software and -of promoting the sharing and reuse of software generally. - - NO WARRANTY - - 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY -FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN -OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES -PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED -OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS -TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE -PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, -REPAIR OR CORRECTION. - - 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR -REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, -INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING -OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED -TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY -YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER -PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE -POSSIBILITY OF SUCH DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) 19yy - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - - -Also add information on how to contact you by electronic and paper mail. - -If the program is interactive, make it output a short notice like this -when it starts in an interactive mode: - - Gnomovision version 69, Copyright (C) 19yy name of author - Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, the commands you use may -be called something other than `show w' and `show c'; they could even be -mouse-clicks or menu items--whatever suits your program. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the program, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the program - `Gnomovision' (which makes passes at compilers) written by James Hacker. - - , 1 April 1989 - Ty Coon, President of Vice - -This General Public License does not permit incorporating your program into -proprietary programs. If your program is a subroutine library, you may -consider it more useful to permit linking proprietary applications with the -library. If this is what you want to do, use the GNU Library General -Public License instead of this License. +This software was previously distributed under BSD/GPL v2 dual license +terms that allowed either of those license alternatives to be +selected. As of February 11, 2012, the project has chosen to use only +the BSD license option for future distribution. As such, the GPL v2 +license option is no longer used. It should be noted that the BSD +license option (the one with advertisement clause removed) is compatible +with GPL and as such, does not prevent use of this software in projects +that use GPL. + +Some of the files may still include pointers to GPL version 2 license +terms. However, such copyright and license notifications are maintained +only for attribution purposes and any distribution of this software +after February 11, 2012 is no longer under the GPL v2 option. diff -Nru wpa-1.0/debian/changelog wpa-2.1/debian/changelog --- wpa-1.0/debian/changelog 2013-11-19 01:31:02.000000000 +0000 +++ wpa-2.1/debian/changelog 2014-03-05 17:58:05.000000000 +0000 @@ -1,3 +1,21 @@ +wpa (2.1-0ubuntu1) trusty; urgency=medium + + * New upstream release (LP: #1099755) + * debian/get-orig-source: update for new git repository for the current + hostap/wpasupplicant versions. + * Dropped patches due to being applied upstream and included in the current + source tarball: + - debian/patches/11_wpa_gui_ftbfs_gcc_4_7.patch + - debian/patches/13_human_readable_signal.patch + - debian/patches/git_deinit_p2p_context_on_mgmt_remove_ff1f9c8.patch + - debian/patches/libnl3-includes.patch + * debian/patches/git_accept_client_cert_from_server.patch: revert the commit: + "OpenSSL: Do not accept SSL Client certificate for server", which breaks + many AAA servers that include both client and server EKUs. Cherry-picked + from hostap git commit b62d5b5. + + -- Mathieu Trudel-Lapierre Tue, 04 Mar 2014 16:13:24 -0500 + wpa (1.0-3ubuntu4) trusty; urgency=low * debian/patches/git_deinit_p2p_context_on_mgmt_remove_ff1f9c8.patch: diff -Nru wpa-1.0/debian/get-orig-source wpa-2.1/debian/get-orig-source --- wpa-1.0/debian/get-orig-source 2013-11-19 01:16:32.000000000 +0000 +++ wpa-2.1/debian/get-orig-source 2014-01-28 19:07:49.000000000 +0000 @@ -33,7 +33,7 @@ fi # clone upstream git repository -git clone git://w1.fi/srv/git/hostap-1.git "${TEMP_SOURCE}" +git clone git://w1.fi/srv/git/hostap.git "${TEMP_SOURCE}" if [ "$?" -ne 0 ] || [ ! -d "${TEMP_SOURCE}" ]; then echo "ERROR: cloning git://w1.fi/srv/git/hostap-1.git failed" >&2 rm -rf "${TEMP_SOURCE}" diff -Nru wpa-1.0/debian/patches/11_wpa_gui_ftbfs_gcc_4_7.patch wpa-2.1/debian/patches/11_wpa_gui_ftbfs_gcc_4_7.patch --- wpa-1.0/debian/patches/11_wpa_gui_ftbfs_gcc_4_7.patch 2013-11-19 01:16:32.000000000 +0000 +++ wpa-2.1/debian/patches/11_wpa_gui_ftbfs_gcc_4_7.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,23 +0,0 @@ -Description: Fix FTBFS with gcc/g++ 4.7 -Author: Kel Modderman -Bug-Debian: bugs.debian.org/667416 ---- a/wpa_supplicant/wpa_gui-qt4/wpagui.cpp -+++ b/wpa_supplicant/wpa_gui-qt4/wpagui.cpp -@@ -12,16 +12,12 @@ - * See README and COPYING for more details. - */ - --#ifdef __MINGW32__ --/* Need to get getopt() */ --#include --#endif -- - #ifdef CONFIG_NATIVE_WINDOWS - #include - #endif /* CONFIG_NATIVE_WINDOWS */ - - #include -+#include - #include - #include - #include diff -Nru wpa-1.0/debian/patches/13_human_readable_signal.patch wpa-2.1/debian/patches/13_human_readable_signal.patch --- wpa-1.0/debian/patches/13_human_readable_signal.patch 2013-11-19 01:16:32.000000000 +0000 +++ wpa-2.1/debian/patches/13_human_readable_signal.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,162 +0,0 @@ -Description: Display signal strength in dBm with visual indicator in the form - of a bar for scan results displayed by wpa_gui-qt4. Any signal > -35dBm is - treated as full signal bar, signals between range of -95<->-35dBm are - displayed linearly. Convert WEXT signal level value to scale that nl80211 - typically reports in dBm. The condition which differentiates 8-bit WEXT dBm - and regular dBm is probably fragile, but there is currently no way to know - what the driver is going to report for signal strength. - See also: - http://mail.gnome.org/archives/networkmanager-list/2009-November/msg00003.html - http://lists.shmoo.com/pipermail/hostap/2009-April/019682.html -Author: Kel Modderman -Bug-Debian: http://bugs.debian.org/630681 ---- ---- a/wpa_supplicant/wpa_gui-qt4/scanresults.cpp -+++ b/wpa_supplicant/wpa_gui-qt4/scanresults.cpp -@@ -15,6 +15,7 @@ - #include - - #include "scanresults.h" -+#include "signalbar.h" - #include "wpagui.h" - #include "networkconfig.h" - -@@ -33,6 +34,7 @@ ScanResults::ScanResults(QWidget *parent - wpagui = NULL; - scanResultsWidget->setItemsExpandable(FALSE); - scanResultsWidget->setRootIsDecorated(FALSE); -+ scanResultsWidget->setItemDelegate(new SignalBar(scanResultsWidget)); - } - - -@@ -91,7 +93,7 @@ void ScanResults::updateResults() - bssid = (*it).mid(pos); - else if ((*it).startsWith("freq=")) - freq = (*it).mid(pos); -- else if ((*it).startsWith("qual=")) -+ else if ((*it).startsWith("level=")) - signal = (*it).mid(pos); - else if ((*it).startsWith("flags=")) - flags = (*it).mid(pos); ---- /dev/null -+++ b/wpa_supplicant/wpa_gui-qt4/signalbar.h -@@ -0,0 +1,34 @@ -+/* -+ * wpa_gui - SignalBar class -+ * Copyright (c) 2011, Kel Modderman -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ * -+ * Alternatively, this software may be distributed under the terms of BSD -+ * license. -+ * -+ * See README and COPYING for more details. -+ */ -+ -+#ifndef SIGNALBAR_H -+#define SIGNALBAR_H -+ -+#include -+#include -+ -+class SignalBar : public QStyledItemDelegate -+{ -+ Q_OBJECT -+ -+public: -+ SignalBar(QObject *parent = 0); -+ ~SignalBar(); -+ -+ virtual void paint(QPainter *painter, -+ const QStyleOptionViewItem &option, -+ const QModelIndex &index) const ; -+}; -+ -+#endif /* SIGNALBAR_H */ ---- /dev/null -+++ b/wpa_supplicant/wpa_gui-qt4/signalbar.cpp -@@ -0,0 +1,64 @@ -+/* -+ * wpa_gui - SignalBar class -+ * Copyright (c) 2011, Kel Modderman -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ * -+ * Alternatively, this software may be distributed under the terms of BSD -+ * license. -+ * -+ * See README and COPYING for more details. -+ */ -+ -+#include -+#include -+ -+#include "signalbar.h" -+ -+ -+SignalBar::SignalBar(QObject *parent) -+ : QStyledItemDelegate(parent) -+{ -+} -+ -+ -+SignalBar::~SignalBar() -+{ -+} -+ -+ -+void SignalBar::paint(QPainter *painter, -+ const QStyleOptionViewItem &option, -+ const QModelIndex &index) const -+{ -+ QStyleOptionProgressBar opts; -+ int signal; -+ -+ if (index.column() != 3) { -+ QStyledItemDelegate::paint(painter, option, index); -+ return; -+ } -+ -+ if (index.data().toInt() > 0) -+ signal = 0 - (256 - index.data().toInt()); -+ else -+ signal = index.data().toInt(); -+ -+ opts.minimum = -95; -+ opts.maximum = -35; -+ if (signal < opts.minimum) -+ opts.progress = opts.minimum; -+ else if (signal > opts.maximum) -+ opts.progress = opts.maximum; -+ else -+ opts.progress = signal; -+ -+ opts.text = QString::number(signal) + " dBm"; -+ opts.textVisible = true; -+ opts.rect = option.rect; -+ -+ QApplication::style()->drawControl(QStyle::CE_ProgressBar, -+ &opts, painter); -+} ---- a/wpa_supplicant/wpa_gui-qt4/wpa_gui.pro -+++ b/wpa_supplicant/wpa_gui-qt4/wpa_gui.pro -@@ -34,6 +34,7 @@ HEADERS += wpamsg.h \ - wpagui.h \ - eventhistory.h \ - scanresults.h \ -+ signalbar.h \ - userdatarequest.h \ - networkconfig.h \ - addinterface.h \ -@@ -44,6 +45,7 @@ SOURCES += main.cpp \ - wpagui.cpp \ - eventhistory.cpp \ - scanresults.cpp \ -+ signalbar.cpp \ - userdatarequest.cpp \ - networkconfig.cpp \ - addinterface.cpp \ diff -Nru wpa-1.0/debian/patches/git_accept_client_cert_from_server.patch wpa-2.1/debian/patches/git_accept_client_cert_from_server.patch --- wpa-1.0/debian/patches/git_accept_client_cert_from_server.patch 1970-01-01 00:00:00.000000000 +0000 +++ wpa-2.1/debian/patches/git_accept_client_cert_from_server.patch 2014-03-05 15:04:27.000000000 +0000 @@ -0,0 +1,68 @@ +From b62d5b5450101676a0c05691b4bcd94e11426397 Mon Sep 17 00:00:00 2001 +From: Jouni Malinen +Date: Wed, 19 Feb 2014 09:56:02 +0000 +Subject: Revert "OpenSSL: Do not accept SSL Client certificate for server" + +This reverts commit 51e3eafb68e15e78e98ca955704be8a6c3a7b304. There are +too many deployed AAA servers that include both id-kp-clientAuth and +id-kp-serverAuth EKUs for this change to be acceptable as a generic rule +for AAA authentication server validation. OpenSSL enforces the policy of +not connecting if only id-kp-clientAuth is included. If a valid EKU is +listed with it, the connection needs to be accepted. + +Signed-off-by: Jouni Malinen +--- +diff --git a/src/crypto/tls.h b/src/crypto/tls.h +index 287fd33..feba13f 100644 +--- a/src/crypto/tls.h ++++ b/src/crypto/tls.h +@@ -41,8 +41,7 @@ enum tls_fail_reason { + TLS_FAIL_ALTSUBJECT_MISMATCH = 6, + TLS_FAIL_BAD_CERTIFICATE = 7, + TLS_FAIL_SERVER_CHAIN_PROBE = 8, +- TLS_FAIL_DOMAIN_SUFFIX_MISMATCH = 9, +- TLS_FAIL_SERVER_USED_CLIENT_CERT = 10 ++ TLS_FAIL_DOMAIN_SUFFIX_MISMATCH = 9 + }; + + union tls_event_data { +diff --git a/src/crypto/tls_openssl.c b/src/crypto/tls_openssl.c +index a13fa38..8cf1de8 100644 +--- a/src/crypto/tls_openssl.c ++++ b/src/crypto/tls_openssl.c +@@ -105,7 +105,6 @@ struct tls_connection { + unsigned int ca_cert_verify:1; + unsigned int cert_probe:1; + unsigned int server_cert_only:1; +- unsigned int server:1; + + u8 srv_cert_hash[32]; + +@@ -1480,16 +1479,6 @@ static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx) + TLS_FAIL_SERVER_CHAIN_PROBE); + } + +- if (!conn->server && err_cert && preverify_ok && depth == 0 && +- (err_cert->ex_flags & EXFLAG_XKUSAGE) && +- (err_cert->ex_xkusage & XKU_SSL_CLIENT)) { +- wpa_printf(MSG_WARNING, "TLS: Server used client certificate"); +- openssl_tls_fail_event(conn, err_cert, err, depth, buf, +- "Server used client certificate", +- TLS_FAIL_SERVER_USED_CLIENT_CERT); +- preverify_ok = 0; +- } +- + if (preverify_ok && context->event_cb != NULL) + context->event_cb(context->cb_ctx, + TLS_CERT_CHAIN_SUCCESS, NULL); +@@ -2541,8 +2530,6 @@ openssl_handshake(struct tls_connection *conn, const struct wpabuf *in_data, + int res; + struct wpabuf *out_data; + +- conn->server = !!server; +- + /* + * Give TLS handshake data from the server (if available) to OpenSSL + * for processing. +-- +cgit v0.9.2 diff -Nru wpa-1.0/debian/patches/git_deinit_p2p_context_on_mgmt_remove_ff1f9c8.patch wpa-2.1/debian/patches/git_deinit_p2p_context_on_mgmt_remove_ff1f9c8.patch --- wpa-1.0/debian/patches/git_deinit_p2p_context_on_mgmt_remove_ff1f9c8.patch 2013-11-19 01:17:50.000000000 +0000 +++ wpa-2.1/debian/patches/git_deinit_p2p_context_on_mgmt_remove_ff1f9c8.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,79 +0,0 @@ -From ff1f9c82a992b11b0141179dddc0782e51598533 Mon Sep 17 00:00:00 2001 -From: Jouni Malinen -Date: Thu, 10 May 2012 10:49:22 +0300 -Subject: [PATCH 1/1] P2P: Deinitialize global P2P context on P2P mgmt interface removal - -The P2P implementation assumes that the first wpa_s interface instance -is used to manage P2P operations and the P2P module maintains a pointer -to this interface in msg_ctx. This can result in issues (e.g., use of -freed memory) when the management interface is removed. Fix this by -deinitializing global P2P data if the interface that created it is -removed. This will disable P2P until the next interface is added. - -Signed-hostap: Jouni Malinen -intended-for: hostap-1 -(cherry picked from commit ab28911dbf77fa050459fb30d28037f54c04bde6) - -Conflicts: - - wpa_supplicant/wpa_supplicant.c ---- - wpa_supplicant/p2p_supplicant.c | 2 ++ - wpa_supplicant/wpa_supplicant.c | 8 ++++++++ - wpa_supplicant/wpa_supplicant_i.h | 1 + - 3 files changed, 11 insertions(+), 0 deletions(-) - -diff --git a/wpa_supplicant/p2p_supplicant.c b/wpa_supplicant/p2p_supplicant.c -index 049bd41..433bedc 100644 ---- a/wpa_supplicant/p2p_supplicant.c -+++ b/wpa_supplicant/p2p_supplicant.c -@@ -2380,6 +2380,7 @@ int wpas_p2p_init(struct wpa_global *global, struct wpa_supplicant *wpa_s) - global->p2p = p2p_init(&p2p); - if (global->p2p == NULL) - return -1; -+ global->p2p_init_wpa_s = wpa_s; - - for (i = 0; i < MAX_WPS_VENDOR_EXT; i++) { - if (wpa_s->conf->wps_vendor_ext[i] == NULL) -@@ -2470,6 +2471,7 @@ void wpas_p2p_deinit_global(struct wpa_global *global) - - p2p_deinit(global->p2p); - global->p2p = NULL; -+ global->p2p_init_wpa_s = NULL; - } - - -diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c -index a0ee064..d9ed1c3 100644 ---- a/wpa_supplicant/wpa_supplicant.c -+++ b/wpa_supplicant/wpa_supplicant.c -@@ -2419,6 +2419,14 @@ static void wpa_supplicant_deinit_iface(struct wpa_supplicant *wpa_s, - - wpa_supplicant_cleanup(wpa_s); - -+#ifdef CONFIG_P2P -+ if (wpa_s == wpa_s->global->p2p_init_wpa_s && wpa_s->global->p2p) { -+ wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Disable P2P since removing " -+ "the management interface is being removed"); -+ wpas_p2p_deinit_global(wpa_s->global); -+ } -+#endif /* CONFIG_P2P */ -+ - if (notify) - wpas_notify_iface_removed(wpa_s); - -diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h -index 7cab1ba..510f4ea 100644 ---- a/wpa_supplicant/wpa_supplicant_i.h -+++ b/wpa_supplicant/wpa_supplicant_i.h -@@ -219,6 +219,7 @@ struct wpa_global { - size_t drv_count; - struct os_time suspend_time; - struct p2p_data *p2p; -+ struct wpa_supplicant *p2p_init_wpa_s; - struct wpa_supplicant *p2p_group_formation; - u8 p2p_dev_addr[ETH_ALEN]; - struct dl_list p2p_srv_bonjour; /* struct p2p_srv_bonjour */ --- -1.7.4-rc1 - diff -Nru wpa-1.0/debian/patches/libnl3-includes.patch wpa-2.1/debian/patches/libnl3-includes.patch --- wpa-1.0/debian/patches/libnl3-includes.patch 2013-11-19 01:16:32.000000000 +0000 +++ wpa-2.1/debian/patches/libnl3-includes.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,17 +0,0 @@ -From: Dan Williams - -fix for libnl3 3.3 header location. - -Signed-off-by: Stefan Lippers-Hollmann - ---- a/src/drivers/drivers.mak -+++ b/src/drivers/drivers.mak -@@ -48,7 +48,7 @@ NEED_RFKILL=y - ifdef CONFIG_LIBNL32 - DRV_LIBS += -lnl-3 - DRV_LIBS += -lnl-genl-3 -- DRV_CFLAGS += -DCONFIG_LIBNL20 -+ DRV_CFLAGS += -DCONFIG_LIBNL20 `pkg-config --cflags libnl-3.0` - else - ifdef CONFIG_LIBNL_TINY - DRV_LIBS += -lnl-tiny diff -Nru wpa-1.0/debian/patches/series wpa-2.1/debian/patches/series --- wpa-1.0/debian/patches/series 2013-11-19 01:17:50.000000000 +0000 +++ wpa-2.1/debian/patches/series 2014-03-05 15:06:06.000000000 +0000 @@ -2,11 +2,8 @@ 02_dbus_group_policy.patch 06_wpa_gui_menu_exec_path.patch 07_dbus_service_syslog.patch -11_wpa_gui_ftbfs_gcc_4_7.patch 12_wpa_gui_knotify_support.patch -13_human_readable_signal.patch -libnl3-includes.patch dbus-activation-cmdline.patch session-ticket.patch EAP-TLS-server_fix-TLS-Message-length-validation.patch -git_deinit_p2p_context_on_mgmt_remove_ff1f9c8.patch +git_accept_client_cert_from_server.patch diff -Nru wpa-1.0/hostapd/android.config wpa-2.1/hostapd/android.config --- wpa-1.0/hostapd/android.config 1970-01-01 00:00:00.000000000 +0000 +++ wpa-2.1/hostapd/android.config 2014-02-04 11:23:35.000000000 +0000 @@ -0,0 +1,190 @@ +# Example hostapd build time configuration +# +# This file lists the configuration options that are used when building the +# hostapd binary. All lines starting with # are ignored. Configuration option +# lines must be commented out complete, if they are not to be included, i.e., +# just setting VARIABLE=n is not disabling that variable. +# +# This file is included in Makefile, so variables like CFLAGS and LIBS can also +# be modified from here. In most cass, these lines should use += in order not +# to override previous values of the variables. + +# Driver interface for Host AP driver +#CONFIG_DRIVER_HOSTAP=y + +# Driver interface for wired authenticator +#CONFIG_DRIVER_WIRED=y + +# Driver interface for madwifi driver +#CONFIG_DRIVER_MADWIFI=y +#CFLAGS += -I../../madwifi # change to the madwifi source directory + +# Driver interface for drivers using the nl80211 kernel interface +#CONFIG_DRIVER_NL80211=y +# driver_nl80211.c requires a rather new libnl (version 1.1) which may not be +# shipped with your distribution yet. If that is the case, you need to build +# newer libnl version and point the hostapd build to use it. +#LIBNL=/usr/src/libnl +#CFLAGS += -I$(LIBNL)/include +#LIBS += -L$(LIBNL)/lib +CONFIG_LIBNL20=y + +# Driver interface for FreeBSD net80211 layer (e.g., Atheros driver) +#CONFIG_DRIVER_BSD=y +#CFLAGS += -I/usr/local/include +#LIBS += -L/usr/local/lib +#LIBS_p += -L/usr/local/lib +#LIBS_c += -L/usr/local/lib + +# Driver interface for no driver (e.g., RADIUS server only) +#CONFIG_DRIVER_NONE=y + +# IEEE 802.11F/IAPP +#CONFIG_IAPP=y + +# 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) +# This version is an experimental implementation based on IEEE 802.11w/D1.0 +# draft and is subject to change since the standard has not yet been finalized. +# Driver support is also needed for IEEE 802.11w. +CONFIG_IEEE80211W=y + +# Integrated EAP server +#CONFIG_EAP=y + +# EAP-MD5 for the integrated EAP server +#CONFIG_EAP_MD5=y + +# EAP-TLS for the integrated EAP server +#CONFIG_EAP_TLS=y + +# EAP-MSCHAPv2 for the integrated EAP server +#CONFIG_EAP_MSCHAPV2=y + +# EAP-PEAP for the integrated EAP server +#CONFIG_EAP_PEAP=y + +# EAP-GTC for the integrated EAP server +#CONFIG_EAP_GTC=y + +# EAP-TTLS for the integrated EAP server +#CONFIG_EAP_TTLS=y + +# EAP-SIM for the integrated EAP server +#CONFIG_EAP_SIM=y + +# EAP-AKA for the integrated EAP server +#CONFIG_EAP_AKA=y + +# EAP-AKA' for the integrated EAP server +# This requires CONFIG_EAP_AKA to be enabled, too. +#CONFIG_EAP_AKA_PRIME=y + +# EAP-PAX for the integrated EAP server +#CONFIG_EAP_PAX=y + +# EAP-PSK for the integrated EAP server (this is _not_ needed for WPA-PSK) +#CONFIG_EAP_PSK=y + +# EAP-SAKE for the integrated EAP server +#CONFIG_EAP_SAKE=y + +# EAP-GPSK for the integrated EAP server +#CONFIG_EAP_GPSK=y +# Include support for optional SHA256 cipher suite in EAP-GPSK +#CONFIG_EAP_GPSK_SHA256=y + +# EAP-FAST for the integrated EAP server +# Note: Default OpenSSL package does not include support for all the +# functionality needed for EAP-FAST. If EAP-FAST is enabled with OpenSSL, +# the OpenSSL library must be patched (openssl-0.9.9-session-ticket.patch) +# to add the needed functions. +#CONFIG_EAP_FAST=y + +# Wi-Fi Protected Setup (WPS) +CONFIG_WPS=y +# Enable WSC 2.0 support +CONFIG_WPS2=y +# Enable UPnP support for external WPS Registrars +#CONFIG_WPS_UPNP=y + +# EAP-IKEv2 +#CONFIG_EAP_IKEV2=y + +# Trusted Network Connect (EAP-TNC) +#CONFIG_EAP_TNC=y + +# PKCS#12 (PFX) support (used to read private key and certificate file from +# a file that usually has extension .p12 or .pfx) +CONFIG_PKCS12=y + +# RADIUS authentication server. This provides access to the integrated EAP +# server from external hosts using RADIUS. +#CONFIG_RADIUS_SERVER=y + +# Build IPv6 support for RADIUS operations +CONFIG_IPV6=y + +# IEEE Std 802.11r-2008 (Fast BSS Transition) +#CONFIG_IEEE80211R=y + +# Use the hostapd's IEEE 802.11 authentication (ACL), but without +# the IEEE 802.11 Management capability (e.g., madwifi or FreeBSD/net80211) +#CONFIG_DRIVER_RADIUS_ACL=y + +# IEEE 802.11n (High Throughput) support +CONFIG_IEEE80211N=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. +#CONFIG_NO_STDOUT_DEBUG=y + +# Add support for writing debug log to Android logcat instead of standard output +CONFIG_ANDROID_LOG=y + +# Remove support for RADIUS accounting +#CONFIG_NO_ACCOUNTING=y + +# Remove support for RADIUS +CONFIG_NO_RADIUS=y + +# Remove support for VLANs +#CONFIG_NO_VLAN=y + +# Remove support for dumping internal state through control interface commands +# This can be used to reduce binary size at the cost of disabling a debugging +# option. +#CONFIG_NO_DUMP_STATE=y + +# Select wrapper for operatins system and C library specific functions +# unix = UNIX/POSIX like systems (default) +# win32 = Windows systems +# none = Empty template +CONFIG_OS=unix + +# Enable tracing code for developer debugging +# This tracks use of memory allocations and other registrations and reports +# incorrect use with a backtrace of call (or allocation) location. +#CONFIG_WPA_TRACE=y +# For BSD, comment out these. +#LIBS += -lexecinfo +#LIBS_p += -lexecinfo +#LIBS_c += -lexecinfo + +# Use libbfd to get more details for developer debugging +# This enables use of libbfd to get more detailed symbols for the backtraces +# generated by CONFIG_WPA_TRACE=y. +#CONFIG_WPA_TRACE_BFD=y +# For BSD, comment out these. +#LIBS += -lbfd -liberty -lz +#LIBS_p += -lbfd -liberty -lz +#LIBS_c += -lbfd -liberty -lz + +# Enable AP +CONFIG_AP=y diff -Nru wpa-1.0/hostapd/Android.mk wpa-2.1/hostapd/Android.mk --- wpa-1.0/hostapd/Android.mk 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/hostapd/Android.mk 2014-02-04 11:23:35.000000000 +0000 @@ -1,37 +1,55 @@ +# Copyright (C) 2008 The Android Open Source Project +# +# This software may be distributed under the terms of the BSD license. +# See README for more details. +# + LOCAL_PATH := $(call my-dir) WPA_BUILD_HOSTAPD := false -ifneq ($(TARGET_SIMULATOR),true) - ifneq ($(BOARD_HOSTAPD_DRIVER),) - WPA_BUILD_HOSTAPD := true - CONFIG_DRIVER_$(BOARD_HOSTAPD_DRIVER) := y - endif +ifneq ($(BOARD_HOSTAPD_DRIVER),) + WPA_BUILD_HOSTAPD := true + CONFIG_DRIVER_$(BOARD_HOSTAPD_DRIVER) := y endif -include $(LOCAL_PATH)/.config +ifeq ($(WPA_BUILD_HOSTAPD),true) + +include $(LOCAL_PATH)/android.config # To ignore possible wrong network configurations L_CFLAGS = -DWPA_IGNORE_CONFIG_ERRORS +L_CFLAGS += -DVERSION_STR_POSTFIX=\"-$(PLATFORM_VERSION)\" + # Set Android log name L_CFLAGS += -DANDROID_LOG_NAME=\"hostapd\" +ifeq ($(BOARD_WLAN_DEVICE), bcmdhd) +L_CFLAGS += -DANDROID_P2P +endif + +ifeq ($(BOARD_WLAN_DEVICE), qcwcn) +L_CFLAGS += -DANDROID_P2P +endif + +ifeq ($(BOARD_WLAN_DEVICE), mrvl) +L_CFLAGS += -DANDROID_P2P +endif + +# Use Android specific directory for control interface sockets +L_CFLAGS += -DCONFIG_CTRL_IFACE_CLIENT_DIR=\"/data/misc/wifi/sockets\" +L_CFLAGS += -DCONFIG_CTRL_IFACE_DIR=\"/data/system/hostapd\" + # To force sizeof(enum) = 4 ifeq ($(TARGET_ARCH),arm) L_CFLAGS += -mabi=aapcs-linux endif -# To allow non-ASCII characters in SSID -L_CFLAGS += -DWPA_UNICODE_SSID - -# OpenSSL is configured without engines on Android -L_CFLAGS += -DOPENSSL_NO_ENGINE - INCLUDES = $(LOCAL_PATH) INCLUDES += $(LOCAL_PATH)/src INCLUDES += $(LOCAL_PATH)/src/utils INCLUDES += external/openssl/include -INCLUDES += frameworks/base/cmds/keystore +INCLUDES += system/security/keystore/include ifdef CONFIG_DRIVER_NL80211 INCLUDES += external/libnl-headers endif @@ -65,6 +83,7 @@ OBJS += src/ap/authsrv.c OBJS += src/ap/ieee802_1x.c OBJS += src/ap/ap_config.c +OBJS += src/ap/eap_user_db.c OBJS += src/ap/ieee802_11_auth.c OBJS += src/ap/sta_info.c OBJS += src/ap/wpa_auth.c @@ -118,10 +137,10 @@ ifndef CONFIG_NO_DUMP_STATE -# define HOSTAPD_DUMP_STATE to include SIGUSR1 handler for dumping state to -# a file (undefine it, if you want to save in binary size) +# define HOSTAPD_DUMP_STATE to include support for dumping internal state +# through control interface commands (undefine it, if you want to save in +# binary size) L_CFLAGS += -DHOSTAPD_DUMP_STATE -OBJS += dump_state.c OBJS += src/eapol_auth/eapol_auth_dump.c endif @@ -131,6 +150,7 @@ else OBJS += src/radius/radius.c OBJS += src/radius/radius_client.c +OBJS += src/radius/radius_das.c endif ifdef CONFIG_NO_ACCOUNTING @@ -143,6 +163,12 @@ L_CFLAGS += -DCONFIG_NO_VLAN else OBJS += src/ap/vlan_init.c +ifdef CONFIG_VLAN_NETLINK +ifdef CONFIG_FULL_DYNAMIC_VLAN +OBJS += src/ap/vlan_util.c +endif +L_CFLAGS += -DCONFIG_VLAN_NETLINK +endif endif ifdef CONFIG_NO_CTRL_IFACE @@ -185,10 +211,26 @@ NEED_AES_UNWRAP=y endif +ifdef CONFIG_SAE +L_CFLAGS += -DCONFIG_SAE +OBJS += src/common/sae.c +NEED_ECC=y +NEED_DH_GROUPS=y +endif + +ifdef CONFIG_WNM +L_CFLAGS += -DCONFIG_WNM +OBJS += src/ap/wnm_ap.c +endif + ifdef CONFIG_IEEE80211N L_CFLAGS += -DCONFIG_IEEE80211N endif +ifdef CONFIG_IEEE80211AC +L_CFLAGS += -DCONFIG_IEEE80211AC +endif + include $(LOCAL_PATH)/src/drivers/drivers.mk OBJS += $(DRV_AP_OBJS) @@ -225,6 +267,14 @@ TLS_FUNCS=y endif +ifdef CONFIG_EAP_UNAUTH_TLS +L_CFLAGS += -DEAP_SERVER_UNAUTH_TLS +ifndef CONFIG_EAP_TLS +OBJS += src/eap_server/eap_server_tls.c +TLS_FUNCS=y +endif +endif + ifdef CONFIG_EAP_PEAP L_CFLAGS += -DEAP_SERVER_PEAP OBJS += src/eap_server/eap_server_peap.c @@ -301,7 +351,7 @@ L_CFLAGS += -DEAP_SERVER_GPSK OBJS += src/eap_server/eap_server_gpsk.c src/eap_common/eap_gpsk_common.c ifdef CONFIG_EAP_GPSK_SHA256 -L_CFLAGS += -DEAP_SERVER_GPSK_SHA256 +L_CFLAGS += -DEAP_GPSK_SHA256 endif NEED_SHA256=y NEED_AES_OMAC1=y @@ -313,6 +363,13 @@ NEED_SHA256=y endif +ifdef CONFIG_EAP_EKE +L_CFLAGS += -DEAP_SERVER_EKE +OBJS += src/eap_server/eap_server_eke.c src/eap_common/eap_eke_common.c +NEED_DH_GROUPS=y +NEED_DH_GROUPS_ALL=y +endif + ifdef CONFIG_EAP_VENDOR_TEST L_CFLAGS += -DEAP_SERVER_VENDOR_TEST OBJS += src/eap_server/eap_server_vendor_test.c @@ -351,25 +408,10 @@ NEED_MODEXP=y CONFIG_EAP=y -ifdef CONFIG_WPS_UFD -L_CFLAGS += -DCONFIG_WPS_UFD -OBJS += src/wps/wps_ufd.c -NEED_WPS_OOB=y -endif - ifdef CONFIG_WPS_NFC L_CFLAGS += -DCONFIG_WPS_NFC OBJS += src/wps/ndef.c -OBJS += src/wps/wps_nfc.c NEED_WPS_OOB=y -ifdef CONFIG_WPS_NFC_PN531 -PN531_PATH ?= /usr/local/src/nfc -L_CFLAGS += -DCONFIG_WPS_NFC_PN531 -L_CFLAGS += -I${PN531_PATH}/inc -OBJS += src/wps/wps_nfc_pn531.c -LIBS += ${PN531_PATH}/lib/wpsnfc.dll -LIBS += ${PN531_PATH}/lib/libnfc_mapping_pn53x.dll -endif endif ifdef NEED_WPS_OOB @@ -458,6 +500,15 @@ CONFIG_TLS=openssl endif +ifdef CONFIG_TLSV11 +L_CFLAGS += -DCONFIG_TLSV11 +endif + +ifdef CONFIG_TLSV12 +L_CFLAGS += -DCONFIG_TLSV12 +NEED_SHA256=y +endif + ifeq ($(CONFIG_TLS), openssl) ifdef TLS_FUNCS OBJS += src/crypto/tls_openssl.c @@ -476,10 +527,6 @@ ifdef TLS_FUNCS OBJS += src/crypto/tls_gnutls.c LIBS += -lgnutls -lgpg-error -ifdef CONFIG_GNUTLS_EXTRA -L_CFLAGS += -DCONFIG_GNUTLS_EXTRA -LIBS += -lgnutls-extra -endif endif OBJS += src/crypto/crypto_gnutls.c HOBJS += src/crypto/crypto_gnutls.c @@ -541,6 +588,9 @@ NEED_SHA256=y NEED_BASE64=y NEED_TLS_PRF=y +ifdef CONFIG_TLSV12 +NEED_TLS_PRF_SHA256=y +endif NEED_MODEXP=y NEED_CIPHER=y L_CFLAGS += -DCONFIG_TLS_INTERNAL @@ -655,14 +705,19 @@ SHA1OBJS = ifdef NEED_SHA1 +ifneq ($(CONFIG_TLS), openssl) SHA1OBJS += src/crypto/sha1.c +endif +SHA1OBJS += src/crypto/sha1-prf.c ifdef CONFIG_INTERNAL_SHA1 SHA1OBJS += src/crypto/sha1-internal.c ifdef NEED_FIPS186_2_PRF SHA1OBJS += src/crypto/fips_prf_internal.c endif endif +ifneq ($(CONFIG_TLS), openssl) SHA1OBJS += src/crypto/sha1-pbkdf2.c +endif ifdef NEED_T_PRF SHA1OBJS += src/crypto/sha1-tprf.c endif @@ -701,10 +756,17 @@ endif ifdef NEED_SHA256 +L_CFLAGS += -DCONFIG_SHA256 +ifneq ($(CONFIG_TLS), openssl) OBJS += src/crypto/sha256.c +endif +OBJS += src/crypto/sha256-prf.c ifdef CONFIG_INTERNAL_SHA256 OBJS += src/crypto/sha256-internal.c endif +ifdef NEED_TLS_PRF_SHA256 +OBJS += src/crypto/sha256-tlsprf.c +endif endif ifdef NEED_DH_GROUPS @@ -719,11 +781,16 @@ endif endif +ifdef NEED_ECC +L_CFLAGS += -DCONFIG_ECC +endif + ifdef CONFIG_NO_RANDOM_POOL L_CFLAGS += -DCONFIG_NO_RANDOM_POOL else OBJS += src/crypto/random.c HOBJS += src/crypto/random.c +HOBJS += src/utils/eloop.c HOBJS += $(SHA1OBJS) HOBJS += src/crypto/md5.c endif @@ -756,23 +823,50 @@ OBJS += src/ap/ap_list.c OBJS += src/ap/ieee802_11.c OBJS += src/ap/hw_features.c +OBJS += src/ap/dfs.c L_CFLAGS += -DNEED_AP_MLME endif ifdef CONFIG_IEEE80211N OBJS += src/ap/ieee802_11_ht.c endif +ifdef CONFIG_IEEE80211AC +OBJS += src/ap/ieee802_11_vht.c +endif + ifdef CONFIG_P2P_MANAGER L_CFLAGS += -DCONFIG_P2P_MANAGER OBJS += src/ap/p2p_hostapd.c endif +ifdef CONFIG_HS20 +L_CFLAGS += -DCONFIG_HS20 +OBJS += src/ap/hs20.c +CONFIG_INTERWORKING=y +endif + +ifdef CONFIG_INTERWORKING +L_CFLAGS += -DCONFIG_INTERWORKING +OBJS += src/common/gas.c +OBJS += src/ap/gas_serv.c +endif + OBJS += src/drivers/driver_common.c +ifdef CONFIG_ACS +L_CFLAGS += -DCONFIG_ACS +OBJS += src/ap/acs.c +LIBS += -lm +endif + ifdef CONFIG_NO_STDOUT_DEBUG L_CFLAGS += -DCONFIG_NO_STDOUT_DEBUG endif +ifdef CONFIG_DEBUG_LINUX_TRACING +L_CFLAGS += -DCONFIG_DEBUG_LINUX_TRACING +endif + ifdef CONFIG_DEBUG_FILE L_CFLAGS += -DCONFIG_DEBUG_FILE endif @@ -793,14 +887,12 @@ OBJS_c += src/utils/edit_simple.c endif -ifeq ($(WPA_BUILD_HOSTAPD),true) - ######################## include $(CLEAR_VARS) LOCAL_MODULE := hostapd_cli LOCAL_MODULE_TAGS := debug -LOCAL_SHARED_LIBRARIES := libc libcutils +LOCAL_SHARED_LIBRARIES := libc libcutils liblog LOCAL_CFLAGS := $(L_CFLAGS) LOCAL_SRC_FILES := $(OBJS_c) LOCAL_C_INCLUDES := $(INCLUDES) @@ -816,7 +908,7 @@ ifneq ($(BOARD_HOSTAPD_PRIVATE_LIB),) LOCAL_STATIC_LIBRARIES += $(BOARD_HOSTAPD_PRIVATE_LIB) endif -LOCAL_SHARED_LIBRARIES := libc libcutils libcrypto libssl +LOCAL_SHARED_LIBRARIES := libc libcutils liblog libcrypto libssl ifdef CONFIG_DRIVER_NL80211 LOCAL_STATIC_LIBRARIES += libnl_2 endif diff -Nru wpa-1.0/hostapd/ChangeLog wpa-2.1/hostapd/ChangeLog --- wpa-1.0/hostapd/ChangeLog 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/hostapd/ChangeLog 2014-02-04 11:23:35.000000000 +0000 @@ -1,6 +1,137 @@ ChangeLog for hostapd -2012-04-18 - v1.0 +2014-02-04 - v2.1 + * added support for simultaneous authentication of equals (SAE) for + stronger password-based authentication with WPA2-Personal + * added nl80211 functionality + - VHT configuration for nl80211 + - support split wiphy dump + - driver-based MAC ACL + - QoS Mapping configuration + * added fully automated regression testing with mac80211_hwsim + * allow ctrl_iface group to be specified on command line (-G) + * allow single hostapd process to control independent WPS interfaces + (wps_independent=1) instead of synchronized operations through all + configured interfaces within a process + * avoid processing received management frames multiple times when using + nl80211 with multiple BSSes + * added support for DFS (processing radar detection events, CAC, channel + re-selection) + * added EAP-EKE server + * added automatic channel selection (ACS) + * added option for using per-BSS (vif) configuration files with + -b: + * extended global control interface ADD/REMOVE commands to allow BSSes + of a radio to be removed individually without having to add/remove all + other BSSes of the radio at the same time + * added support for sending debug info to Linux tracing (-T on command + line) + * replace dump_file functionality with same information being available + through the hostapd control interface + * added support for using Protected Dual of Public Action frames for + GAS/ANQP exchanges when PMF is enabled + * added support for WPS+NFC updates + - improved protocol + - option to fetch and report alternative carrier records for external + NFC operations + * various bug fixes + +2013-01-12 - v2.0 + * added AP-STA-DISCONNECTED ctrl_iface event + * improved debug logging (human readable event names, interface name + included in more entries) + * added number of small changes to make it easier for static analyzers + to understand the implementation + * added a workaround for Windows 7 Michael MIC failure reporting and + use of the Secure bit in EAPOL-Key msg 3/4 + * fixed number of small bugs (see git logs for more details) + * changed OpenSSL to read full certificate chain from server_cert file + * nl80211: number of updates to use new cfg80211/nl80211 functionality + - replace monitor interface with nl80211 commands + - additional information for driver-based AP SME + * EAP-pwd: + - fix KDF for group 21 and zero-padding + - added support for fragmentation + - increased maximum number of hunting-and-pecking iterations + * avoid excessive Probe Response retries for broadcast Probe Request + frames (only with drivers using hostapd SME/MLME) + * added preliminary support for using TLS v1.2 (CONFIG_TLSV12=y) + * fixed WPS operation stopping on dual concurrent AP + * added wps_rf_bands configuration parameter for overriding RF Bands + value for WPS + * added support for getting per-device PSK from RADIUS Tunnel-Password + * added support for libnl 3.2 and newer + * increased initial group key handshake retransmit timeout to 500 ms + * added a workaround for 4-way handshake to update SNonce even after + having sent EAPOL-Key 3/4 to avoid issues with some supplicant + implementations that can change SNonce for each EAP-Key 2/4 + * added a workaround for EAPOL-Key 4/4 using incorrect type value in + WPA2 mode (some deployed stations use WPA type in that message) + * added a WPS workaround for mixed mode AP Settings with Windows 7 + * changed WPS AP PIN disabling mechanism to disable the PIN after 10 + consecutive failures in addition to using the exponential lockout + period + * added support for WFA Hotspot 2.0 + - GAS/ANQP advertisement of network information + - disable_dgaf parameter to disable downstream group-addressed + forwarding + * simplified licensing terms by selecting the BSD license as the only + alternative + * EAP-SIM: fixed re-authentication not to update pseudonym + * EAP-SIM: use Notification round before EAP-Failure + * EAP-AKA: added support for AT_COUNTER_TOO_SMALL + * EAP-AKA: skip AKA/Identity exchange if EAP identity is recognized + * EAP-AKA': fixed identity for MK derivation + * EAP-AKA': updated to RFC 5448 (username prefixes changed); note: this + breaks interoperability with older versions + * EAP-SIM/AKA: allow pseudonym to be used after unknown reauth id + * changed ANonce to be a random number instead of Counter-based + * added support for canceling WPS operations with hostapd_cli wps_cancel + * fixed EAP/WPS to PSK transition on reassociation in cases where + deauthentication is missed + * hlr_auc_gw enhancements: + - a new command line parameter -u can be used to enable updating of + SQN in Milenage file + - use 5 bit IND for SQN updates + - SQLite database can now be used to store Milenage information + * EAP-SIM/AKA DB: added optional use of SQLite database for pseudonyms + and reauth data + * added support for Chargeable-User-Identity (RFC 4372) + * added radius_auth_req_attr and radius_acct_req_attr configuration + parameters to allow adding/overriding of RADIUS attributes in + Access-Request and Accounting-Request packets + * added support for RADIUS dynamic authorization server (RFC 5176) + * added initial support for WNM operations + - BSS max idle period + - WNM-Sleep Mode + * added new WPS NFC ctrl_iface mechanism + - removed obsoleted WPS_OOB command (including support for deprecated + UFD config_method) + * added FT support for drivers that implement MLME internally + * added SA Query support for drivers that implement MLME internally + * removed default ACM=1 from AC_VO and AC_VI + * changed VENDOR-TEST EAP method to use proper private enterprise number + (this will not interoperate with older versions) + * added hostapd.conf parameter vendor_elements to allow arbitrary vendor + specific elements to be added to the Beacon and Probe Response frames + * added support for configuring GCMP cipher for IEEE 802.11ad + * added support for 256-bit AES with internal TLS implementation + * changed EAPOL transmission to use AC_VO if WMM is active + * fixed EAP-TLS/PEAP/TTLS/FAST server to validate TLS Message Length + correctly; invalid messages could have caused the hostapd process to + terminate before this fix [CVE-2012-4445] + * limit number of active wildcard PINs for WPS Registrar to one to avoid + confusing behavior with multiple wildcard PINs + * added a workaround for WPS PBC session overlap detection to avoid + interop issues with deployed station implementations that do not + remove active PBC indication from Probe Request frames properly + * added support for using SQLite for the eap_user database + * added Acct-Session-Id attribute into Access-Request messages + * fixed EAPOL frame transmission to non-QoS STAs with nl80211 + (do not send QoS frames if the STA did not negotiate use of QoS for + this association) + +2012-05-10 - v1.0 * Add channel selection support in hostapd. See hostapd.conf. * Add support for IEEE 802.11v Time Advertisement mechanism with UTC TSF offset. See hostapd.conf for config info. diff -Nru wpa-1.0/hostapd/config_file.c wpa-2.1/hostapd/config_file.c --- wpa-1.0/hostapd/config_file.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/hostapd/config_file.c 2014-02-04 11:23:35.000000000 +0000 @@ -1,15 +1,9 @@ /* * hostapd / Configuration file parser - * Copyright (c) 2003-2009, Jouni Malinen + * Copyright (c) 2003-2013, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "utils/includes.h" @@ -28,9 +22,6 @@ #include "config_file.h" -extern struct wpa_driver_ops *wpa_drivers[]; - - #ifndef CONFIG_NO_VLAN static int hostapd_config_read_vlan_file(struct hostapd_bss_config *bss, const char *fname) @@ -89,7 +80,7 @@ return -1; } - vlan = os_malloc(sizeof(*vlan)); + vlan = os_zalloc(sizeof(*vlan)); if (vlan == NULL) { wpa_printf(MSG_ERROR, "Out of memory while reading " "VLAN interfaces from '%s'", fname); @@ -97,14 +88,10 @@ return -1; } - os_memset(vlan, 0, sizeof(*vlan)); vlan->vlan_id = vlan_id; os_strlcpy(vlan->ifname, pos, sizeof(vlan->ifname)); - if (bss->vlan_tail) - bss->vlan_tail->next = vlan; - else - bss->vlan = vlan; - bss->vlan_tail = vlan; + vlan->next = bss->vlan; + bss->vlan = vlan; } fclose(f); @@ -173,7 +160,7 @@ if (*pos != '\0') vlan_id = atoi(pos); - newacl = os_realloc(*acl, (*num + 1) * sizeof(**acl)); + newacl = os_realloc_array(*acl, *num + 1, sizeof(**acl)); if (newacl == NULL) { wpa_printf(MSG_ERROR, "MAC list reallocation failed"); fclose(f); @@ -206,6 +193,12 @@ if (!fname) return 0; + if (os_strncmp(fname, "sqlite:", 7) == 0) { + os_free(conf->eap_user_sqlite); + conf->eap_user_sqlite = os_strdup(fname + 7); + return 0; + } + f = fopen(fname, "r"); if (!f) { wpa_printf(MSG_ERROR, "EAP user file '%s' not found.", fname); @@ -330,7 +323,7 @@ } num_methods++; - if (num_methods >= EAP_USER_MAX_METHODS) + if (num_methods >= EAP_MAX_METHODS) break; skip_eap: if (pos3 == NULL) @@ -481,7 +474,7 @@ int ret; static int server_index = 1; - nserv = os_realloc(*server, (*num_server + 1) * sizeof(*nserv)); + nserv = os_realloc_array(*server, *num_server + 1, sizeof(*nserv)); if (nserv == NULL) return -1; @@ -497,6 +490,100 @@ return ret; } + + +static struct hostapd_radius_attr * +hostapd_parse_radius_attr(const char *value) +{ + const char *pos; + char syntax; + struct hostapd_radius_attr *attr; + size_t len; + + attr = os_zalloc(sizeof(*attr)); + if (attr == NULL) + return NULL; + + attr->type = atoi(value); + + pos = os_strchr(value, ':'); + if (pos == NULL) { + attr->val = wpabuf_alloc(1); + if (attr->val == NULL) { + os_free(attr); + return NULL; + } + wpabuf_put_u8(attr->val, 0); + return attr; + } + + pos++; + if (pos[0] == '\0' || pos[1] != ':') { + os_free(attr); + return NULL; + } + syntax = *pos++; + pos++; + + switch (syntax) { + case 's': + attr->val = wpabuf_alloc_copy(pos, os_strlen(pos)); + break; + case 'x': + len = os_strlen(pos); + if (len & 1) + break; + len /= 2; + attr->val = wpabuf_alloc(len); + if (attr->val == NULL) + break; + if (hexstr2bin(pos, wpabuf_put(attr->val, len), len) < 0) { + wpabuf_free(attr->val); + os_free(attr); + return NULL; + } + break; + case 'd': + attr->val = wpabuf_alloc(4); + if (attr->val) + wpabuf_put_be32(attr->val, atoi(pos)); + break; + default: + os_free(attr); + return NULL; + } + + if (attr->val == NULL) { + os_free(attr); + return NULL; + } + + return attr; +} + + +static int hostapd_parse_das_client(struct hostapd_bss_config *bss, + const char *val) +{ + char *secret; + + secret = os_strchr(val, ' '); + if (secret == NULL) + return -1; + + secret++; + + if (hostapd_parse_ip_addr(val, &bss->radius_das_client_addr)) + return -1; + + os_free(bss->radius_das_shared_secret); + bss->radius_das_shared_secret = (u8 *) os_strdup(secret); + if (bss->radius_das_shared_secret == NULL) + return -1; + bss->radius_das_shared_secret_len = os_strlen(secret); + + return 0; +} #endif /* CONFIG_NO_RADIUS */ @@ -536,6 +623,12 @@ else if (os_strcmp(start, "WPA-EAP-SHA256") == 0) val |= WPA_KEY_MGMT_IEEE8021X_SHA256; #endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_SAE + else if (os_strcmp(start, "SAE") == 0) + val |= WPA_KEY_MGMT_SAE; + else if (os_strcmp(start, "FT-SAE") == 0) + val |= WPA_KEY_MGMT_FT_SAE; +#endif /* CONFIG_SAE */ else { wpa_printf(MSG_ERROR, "Line %d: invalid key_mgmt '%s'", line, start); @@ -561,47 +654,12 @@ static int hostapd_config_parse_cipher(int line, const char *value) { - int val = 0, last; - char *start, *end, *buf; - - buf = os_strdup(value); - if (buf == NULL) + int val = wpa_parse_cipher(value); + if (val < 0) { + wpa_printf(MSG_ERROR, "Line %d: invalid cipher '%s'.", + line, value); return -1; - start = buf; - - while (*start != '\0') { - while (*start == ' ' || *start == '\t') - start++; - if (*start == '\0') - break; - end = start; - while (*end != ' ' && *end != '\t' && *end != '\0') - end++; - last = *end == '\0'; - *end = '\0'; - if (os_strcmp(start, "CCMP") == 0) - val |= WPA_CIPHER_CCMP; - else if (os_strcmp(start, "TKIP") == 0) - val |= WPA_CIPHER_TKIP; - else if (os_strcmp(start, "WEP104") == 0) - val |= WPA_CIPHER_WEP104; - else if (os_strcmp(start, "WEP40") == 0) - val |= WPA_CIPHER_WEP40; - else if (os_strcmp(start, "NONE") == 0) - val |= WPA_CIPHER_NONE; - else { - wpa_printf(MSG_ERROR, "Line %d: invalid cipher '%s'.", - line, start); - os_free(buf); - return -1; - } - - if (last) - break; - start = end + 1; } - os_free(buf); - if (val == 0) { wpa_printf(MSG_ERROR, "Line %d: no cipher values configured.", line); @@ -646,14 +704,14 @@ } -static int hostapd_parse_rates(int **rate_list, char *val) +static int hostapd_parse_intlist(int **int_list, char *val) { int *list; int count; char *pos, *end; - os_free(*rate_list); - *rate_list = NULL; + os_free(*int_list); + *int_list = NULL; pos = val; count = 0; @@ -680,37 +738,39 @@ } list[count] = -1; - *rate_list = list; + *int_list = list; return 0; } static int hostapd_config_bss(struct hostapd_config *conf, const char *ifname) { - struct hostapd_bss_config *bss; + struct hostapd_bss_config **all, *bss; if (*ifname == '\0') return -1; - bss = os_realloc(conf->bss, (conf->num_bss + 1) * - sizeof(struct hostapd_bss_config)); - if (bss == NULL) { + all = os_realloc_array(conf->bss, conf->num_bss + 1, + sizeof(struct hostapd_bss_config *)); + if (all == NULL) { wpa_printf(MSG_ERROR, "Failed to allocate memory for " "multi-BSS entry"); return -1; } - conf->bss = bss; + conf->bss = all; - bss = &(conf->bss[conf->num_bss]); - os_memset(bss, 0, sizeof(*bss)); + bss = os_zalloc(sizeof(*bss)); + if (bss == NULL) + return -1; bss->radius = os_zalloc(sizeof(*bss->radius)); if (bss->radius == NULL) { wpa_printf(MSG_ERROR, "Failed to allocate memory for " "multi-BSS RADIUS data"); + os_free(bss); return -1; } - conf->num_bss++; + conf->bss[conf->num_bss++] = bss; conf->last_bss = bss; hostapd_config_defaults_bss(bss); @@ -817,78 +877,6 @@ } -static int hostapd_config_wmm_ac(struct hostapd_config *conf, char *name, - char *val) -{ - int num, v; - char *pos; - struct hostapd_wmm_ac_params *ac; - - /* skip 'wme_ac_' or 'wmm_ac_' prefix */ - pos = name + 7; - if (os_strncmp(pos, "be_", 3) == 0) { - num = 0; - pos += 3; - } else if (os_strncmp(pos, "bk_", 3) == 0) { - num = 1; - pos += 3; - } else if (os_strncmp(pos, "vi_", 3) == 0) { - num = 2; - pos += 3; - } else if (os_strncmp(pos, "vo_", 3) == 0) { - num = 3; - pos += 3; - } else { - wpa_printf(MSG_ERROR, "Unknown WMM name '%s'", pos); - return -1; - } - - ac = &conf->wmm_ac_params[num]; - - if (os_strcmp(pos, "aifs") == 0) { - v = atoi(val); - if (v < 1 || v > 255) { - wpa_printf(MSG_ERROR, "Invalid AIFS value %d", v); - return -1; - } - ac->aifs = v; - } else if (os_strcmp(pos, "cwmin") == 0) { - v = atoi(val); - if (v < 0 || v > 12) { - wpa_printf(MSG_ERROR, "Invalid cwMin value %d", v); - return -1; - } - ac->cwmin = v; - } else if (os_strcmp(pos, "cwmax") == 0) { - v = atoi(val); - if (v < 0 || v > 12) { - wpa_printf(MSG_ERROR, "Invalid cwMax value %d", v); - return -1; - } - ac->cwmax = v; - } else if (os_strcmp(pos, "txop_limit") == 0) { - v = atoi(val); - if (v < 0 || v > 0xffff) { - wpa_printf(MSG_ERROR, "Invalid txop value %d", v); - return -1; - } - ac->txop_limit = v; - } else if (os_strcmp(pos, "acm") == 0) { - v = atoi(val); - if (v < 0 || v > 1) { - wpa_printf(MSG_ERROR, "Invalid acm value %d", v); - return -1; - } - ac->admission_control_mandatory = v; - } else { - wpa_printf(MSG_ERROR, "Unknown wmm_ac_ field '%s'", pos); - return -1; - } - - return 0; -} - - #ifdef CONFIG_IEEE80211R static int add_r0kh(struct hostapd_bss_config *bss, char *value) { @@ -1040,214 +1028,577 @@ #endif /* CONFIG_IEEE80211N */ -static int hostapd_config_check_bss(struct hostapd_bss_config *bss, - struct hostapd_config *conf) +#ifdef CONFIG_IEEE80211AC +static int hostapd_config_vht_capab(struct hostapd_config *conf, + const char *capab) +{ + if (os_strstr(capab, "[MAX-MPDU-7991]")) + conf->vht_capab |= VHT_CAP_MAX_MPDU_LENGTH_7991; + if (os_strstr(capab, "[MAX-MPDU-11454]")) + conf->vht_capab |= VHT_CAP_MAX_MPDU_LENGTH_11454; + if (os_strstr(capab, "[VHT160]")) + conf->vht_capab |= VHT_CAP_SUPP_CHAN_WIDTH_160MHZ; + if (os_strstr(capab, "[VHT160-80PLUS80]")) + conf->vht_capab |= VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ; + if (os_strstr(capab, "[VHT160-80PLUS80]")) + conf->vht_capab |= VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ; + if (os_strstr(capab, "[RXLDPC]")) + conf->vht_capab |= VHT_CAP_RXLDPC; + if (os_strstr(capab, "[SHORT-GI-80]")) + conf->vht_capab |= VHT_CAP_SHORT_GI_80; + if (os_strstr(capab, "[SHORT-GI-160]")) + conf->vht_capab |= VHT_CAP_SHORT_GI_160; + if (os_strstr(capab, "[TX-STBC-2BY1]")) + conf->vht_capab |= VHT_CAP_TXSTBC; + if (os_strstr(capab, "[RX-STBC-1]")) + conf->vht_capab |= VHT_CAP_RXSTBC_1; + if (os_strstr(capab, "[RX-STBC-12]")) + conf->vht_capab |= VHT_CAP_RXSTBC_2; + if (os_strstr(capab, "[RX-STBC-123]")) + conf->vht_capab |= VHT_CAP_RXSTBC_3; + if (os_strstr(capab, "[RX-STBC-1234]")) + conf->vht_capab |= VHT_CAP_RXSTBC_4; + if (os_strstr(capab, "[SU-BEAMFORMER]")) + conf->vht_capab |= VHT_CAP_SU_BEAMFORMER_CAPABLE; + if (os_strstr(capab, "[SU-BEAMFORMEE]")) + conf->vht_capab |= VHT_CAP_SU_BEAMFORMEE_CAPABLE; + if (os_strstr(capab, "[BF-ANTENNA-2]") && + (conf->vht_capab & VHT_CAP_SU_BEAMFORMEE_CAPABLE)) + conf->vht_capab |= (1 << VHT_CAP_BEAMFORMEE_STS_OFFSET); + if (os_strstr(capab, "[SOUNDING-DIMENSION-2]") && + (conf->vht_capab & VHT_CAP_SU_BEAMFORMER_CAPABLE)) + conf->vht_capab |= (1 << VHT_CAP_SOUNDING_DIMENSION_OFFSET); + if (os_strstr(capab, "[MU-BEAMFORMER]")) + conf->vht_capab |= VHT_CAP_MU_BEAMFORMER_CAPABLE; + if (os_strstr(capab, "[MU-BEAMFORMEE]")) + conf->vht_capab |= VHT_CAP_MU_BEAMFORMEE_CAPABLE; + if (os_strstr(capab, "[VHT-TXOP-PS]")) + conf->vht_capab |= VHT_CAP_VHT_TXOP_PS; + if (os_strstr(capab, "[HTC-VHT]")) + conf->vht_capab |= VHT_CAP_HTC_VHT; + if (os_strstr(capab, "[MAX-A-MPDU-LEN-EXP0]")) + conf->vht_capab |= VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT; + if (os_strstr(capab, "[VHT-LINK-ADAPT2]") && + (conf->vht_capab & VHT_CAP_HTC_VHT)) + conf->vht_capab |= VHT_CAP_VHT_LINK_ADAPTATION_VHT_UNSOL_MFB; + if (os_strstr(capab, "[VHT-LINK-ADAPT3]") && + (conf->vht_capab & VHT_CAP_HTC_VHT)) + conf->vht_capab |= VHT_CAP_VHT_LINK_ADAPTATION_VHT_MRQ_MFB; + if (os_strstr(capab, "[RX-ANTENNA-PATTERN]")) + conf->vht_capab |= VHT_CAP_RX_ANTENNA_PATTERN; + if (os_strstr(capab, "[TX-ANTENNA-PATTERN]")) + conf->vht_capab |= VHT_CAP_TX_ANTENNA_PATTERN; + return 0; +} +#endif /* CONFIG_IEEE80211AC */ + + +#ifdef CONFIG_INTERWORKING +static int parse_roaming_consortium(struct hostapd_bss_config *bss, char *pos, + int line) { - if (bss->ieee802_1x && !bss->eap_server && - !bss->radius->auth_servers) { - wpa_printf(MSG_ERROR, "Invalid IEEE 802.1X configuration (no " - "EAP authenticator configured)."); + size_t len = os_strlen(pos); + u8 oi[MAX_ROAMING_CONSORTIUM_LEN]; + + struct hostapd_roaming_consortium *rc; + + if ((len & 1) || len < 2 * 3 || len / 2 > MAX_ROAMING_CONSORTIUM_LEN || + hexstr2bin(pos, oi, len / 2)) { + wpa_printf(MSG_ERROR, "Line %d: invalid roaming_consortium " + "'%s'", line, pos); + return -1; + } + len /= 2; + + rc = os_realloc_array(bss->roaming_consortium, + bss->roaming_consortium_count + 1, + sizeof(struct hostapd_roaming_consortium)); + if (rc == NULL) return -1; + + os_memcpy(rc[bss->roaming_consortium_count].oi, oi, len); + rc[bss->roaming_consortium_count].len = len; + + bss->roaming_consortium = rc; + bss->roaming_consortium_count++; + + return 0; +} + + +static int parse_lang_string(struct hostapd_lang_string **array, + unsigned int *count, char *pos) +{ + char *sep, *str = NULL; + size_t clen, nlen, slen; + struct hostapd_lang_string *ls; + int ret = -1; + + if (*pos == '"' || (*pos == 'P' && pos[1] == '"')) { + str = wpa_config_parse_string(pos, &slen); + if (!str) + return -1; + pos = str; } - if (bss->wpa && (bss->wpa_key_mgmt & WPA_KEY_MGMT_PSK) && - bss->ssid.wpa_psk == NULL && bss->ssid.wpa_passphrase == NULL && - bss->ssid.wpa_psk_file == NULL) { - wpa_printf(MSG_ERROR, "WPA-PSK enabled, but PSK or passphrase " - "is not configured."); + sep = os_strchr(pos, ':'); + if (sep == NULL) + goto fail; + *sep++ = '\0'; + + clen = os_strlen(pos); + if (clen < 2 || clen > sizeof(ls->lang)) + goto fail; + nlen = os_strlen(sep); + if (nlen > 252) + goto fail; + + ls = os_realloc_array(*array, *count + 1, + sizeof(struct hostapd_lang_string)); + if (ls == NULL) + goto fail; + + *array = ls; + ls = &(*array)[*count]; + (*count)++; + + os_memset(ls->lang, 0, sizeof(ls->lang)); + os_memcpy(ls->lang, pos, clen); + ls->name_len = nlen; + os_memcpy(ls->name, sep, nlen); + + ret = 0; +fail: + os_free(str); + return ret; +} + + +static int parse_venue_name(struct hostapd_bss_config *bss, char *pos, + int line) +{ + if (parse_lang_string(&bss->venue_name, &bss->venue_name_count, pos)) { + wpa_printf(MSG_ERROR, "Line %d: Invalid venue_name '%s'", + line, pos); return -1; } + return 0; +} - if (hostapd_mac_comp_empty(bss->bssid) != 0) { - size_t i; - for (i = 0; i < conf->num_bss; i++) { - if ((&conf->bss[i] != bss) && - (hostapd_mac_comp(conf->bss[i].bssid, - bss->bssid) == 0)) { - wpa_printf(MSG_ERROR, "Duplicate BSSID " MACSTR - " on interface '%s' and '%s'.", - MAC2STR(bss->bssid), - conf->bss[i].iface, bss->iface); - return -1; - } +static int parse_3gpp_cell_net(struct hostapd_bss_config *bss, char *buf, + int line) +{ + size_t count; + char *pos; + u8 *info = NULL, *ipos; + + /* format: [;][;...] */ + + count = 1; + for (pos = buf; *pos; pos++) { + if ((*pos < '0' && *pos > '9') && *pos != ';' && *pos != ',') + goto fail; + if (*pos == ';') + count++; + } + if (1 + count * 3 > 0x7f) + goto fail; + + info = os_zalloc(2 + 3 + count * 3); + if (info == NULL) + return -1; + + ipos = info; + *ipos++ = 0; /* GUD - Version 1 */ + *ipos++ = 3 + count * 3; /* User Data Header Length (UDHL) */ + *ipos++ = 0; /* PLMN List IEI */ + /* ext(b8) | Length of PLMN List value contents(b7..1) */ + *ipos++ = 1 + count * 3; + *ipos++ = count; /* Number of PLMNs */ + + pos = buf; + while (pos && *pos) { + char *mcc, *mnc; + size_t mnc_len; + + mcc = pos; + mnc = os_strchr(pos, ','); + if (mnc == NULL) + goto fail; + *mnc++ = '\0'; + pos = os_strchr(mnc, ';'); + if (pos) + *pos++ = '\0'; + + mnc_len = os_strlen(mnc); + if (os_strlen(mcc) != 3 || (mnc_len != 2 && mnc_len != 3)) + goto fail; + + /* BC coded MCC,MNC */ + /* MCC digit 2 | MCC digit 1 */ + *ipos++ = ((mcc[1] - '0') << 4) | (mcc[0] - '0'); + /* MNC digit 3 | MCC digit 3 */ + *ipos++ = (((mnc_len == 2) ? 0xf0 : ((mnc[2] - '0') << 4))) | + (mcc[2] - '0'); + /* MNC digit 2 | MNC digit 1 */ + *ipos++ = ((mnc[1] - '0') << 4) | (mnc[0] - '0'); + } + + os_free(bss->anqp_3gpp_cell_net); + bss->anqp_3gpp_cell_net = info; + bss->anqp_3gpp_cell_net_len = 2 + 3 + 3 * count; + wpa_hexdump(MSG_MSGDUMP, "3GPP Cellular Network information", + bss->anqp_3gpp_cell_net, bss->anqp_3gpp_cell_net_len); + + return 0; + +fail: + wpa_printf(MSG_ERROR, "Line %d: Invalid anqp_3gpp_cell_net: %s", + line, buf); + os_free(info); + return -1; +} + + +static int parse_nai_realm(struct hostapd_bss_config *bss, char *buf, int line) +{ + struct hostapd_nai_realm_data *realm; + size_t i, j, len; + int *offsets; + char *pos, *end, *rpos; + + offsets = os_calloc(bss->nai_realm_count * MAX_NAI_REALMS, + sizeof(int)); + if (offsets == NULL) + return -1; + + for (i = 0; i < bss->nai_realm_count; i++) { + realm = &bss->nai_realm_data[i]; + for (j = 0; j < MAX_NAI_REALMS; j++) { + offsets[i * MAX_NAI_REALMS + j] = + realm->realm[j] ? + realm->realm[j] - realm->realm_buf : -1; } } -#ifdef CONFIG_IEEE80211R - if ((bss->wpa_key_mgmt & - (WPA_KEY_MGMT_FT_PSK | WPA_KEY_MGMT_FT_IEEE8021X)) && - (bss->nas_identifier == NULL || - os_strlen(bss->nas_identifier) < 1 || - os_strlen(bss->nas_identifier) > FT_R0KH_ID_MAX_LEN)) { - wpa_printf(MSG_ERROR, "FT (IEEE 802.11r) requires " - "nas_identifier to be configured as a 1..48 octet " - "string"); + realm = os_realloc_array(bss->nai_realm_data, bss->nai_realm_count + 1, + sizeof(struct hostapd_nai_realm_data)); + if (realm == NULL) { + os_free(offsets); return -1; } -#endif /* CONFIG_IEEE80211R */ + bss->nai_realm_data = realm; -#ifdef CONFIG_IEEE80211N - if (conf->ieee80211n && - bss->ssid.security_policy == SECURITY_STATIC_WEP) { - bss->disable_11n = 1; - wpa_printf(MSG_ERROR, "HT (IEEE 802.11n) with WEP is not " - "allowed, disabling HT capabilities"); + /* patch the pointers after realloc */ + for (i = 0; i < bss->nai_realm_count; i++) { + realm = &bss->nai_realm_data[i]; + for (j = 0; j < MAX_NAI_REALMS; j++) { + int offs = offsets[i * MAX_NAI_REALMS + j]; + if (offs >= 0) + realm->realm[j] = realm->realm_buf + offs; + else + realm->realm[j] = NULL; + } } + os_free(offsets); + + realm = &bss->nai_realm_data[bss->nai_realm_count]; + os_memset(realm, 0, sizeof(*realm)); - if (conf->ieee80211n && bss->wpa && - !(bss->wpa_pairwise & WPA_CIPHER_CCMP) && - !(bss->rsn_pairwise & WPA_CIPHER_CCMP)) { - bss->disable_11n = 1; - wpa_printf(MSG_ERROR, "HT (IEEE 802.11n) with WPA/WPA2 " - "requires CCMP to be enabled, disabling HT " - "capabilities"); + pos = buf; + realm->encoding = atoi(pos); + pos = os_strchr(pos, ','); + if (pos == NULL) + goto fail; + pos++; + + end = os_strchr(pos, ','); + if (end) { + len = end - pos; + *end = '\0'; + } else { + len = os_strlen(pos); } -#endif /* CONFIG_IEEE80211N */ -#ifdef CONFIG_WPS2 - if (bss->wps_state && bss->ignore_broadcast_ssid) { - wpa_printf(MSG_INFO, "WPS: ignore_broadcast_ssid " - "configuration forced WPS to be disabled"); - bss->wps_state = 0; + if (len > MAX_NAI_REALMLEN) { + wpa_printf(MSG_ERROR, "Too long a realm string (%d > max %d " + "characters)", (int) len, MAX_NAI_REALMLEN); + goto fail; } + os_memcpy(realm->realm_buf, pos, len); + + if (end) + pos = end + 1; + else + pos = NULL; + + while (pos && *pos) { + struct hostapd_nai_realm_eap *eap; - if (bss->wps_state && bss->ssid.wep.keys_set && bss->wpa == 0) { - wpa_printf(MSG_INFO, "WPS: WEP configuration forced WPS to be " - "disabled"); - bss->wps_state = 0; + if (realm->eap_method_count >= MAX_NAI_EAP_METHODS) { + wpa_printf(MSG_ERROR, "Too many EAP methods"); + goto fail; + } + + eap = &realm->eap_method[realm->eap_method_count]; + realm->eap_method_count++; + + end = os_strchr(pos, ','); + if (end == NULL) + end = pos + os_strlen(pos); + + eap->eap_method = atoi(pos); + for (;;) { + pos = os_strchr(pos, '['); + if (pos == NULL || pos > end) + break; + pos++; + if (eap->num_auths >= MAX_NAI_AUTH_TYPES) { + wpa_printf(MSG_ERROR, "Too many auth params"); + goto fail; + } + eap->auth_id[eap->num_auths] = atoi(pos); + pos = os_strchr(pos, ':'); + if (pos == NULL || pos > end) + goto fail; + pos++; + eap->auth_val[eap->num_auths] = atoi(pos); + pos = os_strchr(pos, ']'); + if (pos == NULL || pos > end) + goto fail; + pos++; + eap->num_auths++; + } + + if (*end != ',') + break; + + pos = end + 1; + } + + /* Split realm list into null terminated realms */ + rpos = realm->realm_buf; + i = 0; + while (*rpos) { + if (i >= MAX_NAI_REALMS) { + wpa_printf(MSG_ERROR, "Too many realms"); + goto fail; + } + realm->realm[i++] = rpos; + rpos = os_strchr(rpos, ';'); + if (rpos == NULL) + break; + *rpos++ = '\0'; } -#endif /* CONFIG_WPS2 */ + + bss->nai_realm_count++; return 0; + +fail: + wpa_printf(MSG_ERROR, "Line %d: invalid nai_realm '%s'", line, buf); + return -1; } -static int hostapd_config_check(struct hostapd_config *conf) +static int parse_qos_map_set(struct hostapd_bss_config *bss, + char *buf, int line) { - size_t i; + u8 qos_map_set[16 + 2 * 21], count = 0; + char *pos = buf; + int val; - if (conf->ieee80211d && (!conf->country[0] || !conf->country[1])) { - wpa_printf(MSG_ERROR, "Cannot enable IEEE 802.11d without " - "setting the country_code"); - return -1; - } + for (;;) { + if (count == sizeof(qos_map_set)) { + wpa_printf(MSG_ERROR, "Line %d: Too many qos_map_set " + "parameters '%s'", line, buf); + return -1; + } - for (i = 0; i < conf->num_bss; i++) { - if (hostapd_config_check_bss(&conf->bss[i], conf)) + val = atoi(pos); + if (val > 255 || val < 0) { + wpa_printf(MSG_ERROR, "Line %d: Invalid qos_map_set " + "'%s'", line, buf); return -1; + } + + qos_map_set[count++] = val; + pos = os_strchr(pos, ','); + if (!pos) + break; + pos++; + } + + if (count < 16 || count & 1) { + wpa_printf(MSG_ERROR, "Line %d: Invalid qos_map_set '%s'", + line, buf); + return -1; } + os_memcpy(bss->qos_map_set, qos_map_set, count); + bss->qos_map_set_len = count; + return 0; } +#endif /* CONFIG_INTERWORKING */ -#ifdef CONFIG_INTERWORKING -static int parse_roaming_consortium(struct hostapd_bss_config *bss, char *pos, - int line) + +#ifdef CONFIG_HS20 +static int hs20_parse_conn_capab(struct hostapd_bss_config *bss, char *buf, + int line) { - size_t len = os_strlen(pos); - u8 oi[MAX_ROAMING_CONSORTIUM_LEN]; + u8 *conn_cap; + char *pos; - struct hostapd_roaming_consortium *rc; + if (bss->hs20_connection_capability_len >= 0xfff0) + return -1; - if ((len & 1) || len < 2 * 3 || len / 2 > MAX_ROAMING_CONSORTIUM_LEN || - hexstr2bin(pos, oi, len / 2)) { - wpa_printf(MSG_ERROR, "Line %d: invalid roaming_consortium " - "'%s'", line, pos); + conn_cap = os_realloc(bss->hs20_connection_capability, + bss->hs20_connection_capability_len + 4); + if (conn_cap == NULL) return -1; - } - len /= 2; - rc = os_realloc(bss->roaming_consortium, - sizeof(struct hostapd_roaming_consortium) * - (bss->roaming_consortium_count + 1)); - if (rc == NULL) + bss->hs20_connection_capability = conn_cap; + conn_cap += bss->hs20_connection_capability_len; + pos = buf; + conn_cap[0] = atoi(pos); + pos = os_strchr(pos, ':'); + if (pos == NULL) + return -1; + pos++; + WPA_PUT_LE16(conn_cap + 1, atoi(pos)); + pos = os_strchr(pos, ':'); + if (pos == NULL) return -1; + pos++; + conn_cap[3] = atoi(pos); + bss->hs20_connection_capability_len += 4; - os_memcpy(rc[bss->roaming_consortium_count].oi, oi, len); - rc[bss->roaming_consortium_count].len = len; + return 0; +} - bss->roaming_consortium = rc; - bss->roaming_consortium_count++; + +static int hs20_parse_wan_metrics(struct hostapd_bss_config *bss, char *buf, + int line) +{ + u8 *wan_metrics; + char *pos; + + /* :
:
    :
    :
      : */ + + wan_metrics = os_zalloc(13); + if (wan_metrics == NULL) + return -1; + + pos = buf; + /* WAN Info */ + if (hexstr2bin(pos, wan_metrics, 1) < 0) + goto fail; + pos += 2; + if (*pos != ':') + goto fail; + pos++; + + /* Downlink Speed */ + WPA_PUT_LE32(wan_metrics + 1, atoi(pos)); + pos = os_strchr(pos, ':'); + if (pos == NULL) + goto fail; + pos++; + + /* Uplink Speed */ + WPA_PUT_LE32(wan_metrics + 5, atoi(pos)); + pos = os_strchr(pos, ':'); + if (pos == NULL) + goto fail; + pos++; + + /* Downlink Load */ + wan_metrics[9] = atoi(pos); + pos = os_strchr(pos, ':'); + if (pos == NULL) + goto fail; + pos++; + + /* Uplink Load */ + wan_metrics[10] = atoi(pos); + pos = os_strchr(pos, ':'); + if (pos == NULL) + goto fail; + pos++; + + /* LMD */ + WPA_PUT_LE16(wan_metrics + 11, atoi(pos)); + + os_free(bss->hs20_wan_metrics); + bss->hs20_wan_metrics = wan_metrics; return 0; + +fail: + wpa_printf(MSG_ERROR, "Line %d: Invalid hs20_wan_metrics '%s'", + line, pos); + os_free(wan_metrics); + return -1; } -#endif /* CONFIG_INTERWORKING */ -/** - * hostapd_config_read - Read and parse a configuration file - * @fname: Configuration file name (including path, if needed) - * Returns: Allocated configuration data structure - */ -struct hostapd_config * hostapd_config_read(const char *fname) +static int hs20_parse_oper_friendly_name(struct hostapd_bss_config *bss, + char *pos, int line) { - struct hostapd_config *conf; - struct hostapd_bss_config *bss; - FILE *f; - char buf[256], *pos; - int line = 0; - int errors = 0; - int pairwise; - size_t i; + if (parse_lang_string(&bss->hs20_oper_friendly_name, + &bss->hs20_oper_friendly_name_count, pos)) { + wpa_printf(MSG_ERROR, "Line %d: Invalid " + "hs20_oper_friendly_name '%s'", line, pos); + return -1; + } + return 0; +} +#endif /* CONFIG_HS20 */ - f = fopen(fname, "r"); - if (f == NULL) { - wpa_printf(MSG_ERROR, "Could not open configuration file '%s' " - "for reading.", fname); + +#ifdef CONFIG_WPS_NFC +static struct wpabuf * hostapd_parse_bin(const char *buf) +{ + size_t len; + struct wpabuf *ret; + + len = os_strlen(buf); + if (len & 0x01) return NULL; - } + len /= 2; - conf = hostapd_config_defaults(); - if (conf == NULL) { - fclose(f); + ret = wpabuf_alloc(len); + if (ret == NULL) return NULL; - } - /* set default driver based on configuration */ - conf->driver = wpa_drivers[0]; - if (conf->driver == NULL) { - wpa_printf(MSG_ERROR, "No driver wrappers registered!"); - hostapd_config_free(conf); - fclose(f); + if (hexstr2bin(buf, wpabuf_put(ret, len), len)) { + wpabuf_free(ret); return NULL; } - bss = conf->last_bss = conf->bss; - - while (fgets(buf, sizeof(buf), f)) { - bss = conf->last_bss; - line++; + return ret; +} +#endif /* CONFIG_WPS_NFC */ - if (buf[0] == '#') - continue; - pos = buf; - while (*pos != '\0') { - if (*pos == '\n') { - *pos = '\0'; - break; - } - pos++; - } - if (buf[0] == '\0') - continue; - pos = os_strchr(buf, '='); - if (pos == NULL) { - wpa_printf(MSG_ERROR, "Line %d: invalid line '%s'", - line, buf); - errors++; - continue; - } - *pos = '\0'; - pos++; +static int hostapd_config_fill(struct hostapd_config *conf, + struct hostapd_bss_config *bss, + char *buf, char *pos, int line) +{ + int errors = 0; + { if (os_strcmp(buf, "interface") == 0) { - os_strlcpy(conf->bss[0].iface, pos, - sizeof(conf->bss[0].iface)); + os_strlcpy(conf->bss[0]->iface, pos, + sizeof(conf->bss[0]->iface)); } else if (os_strcmp(buf, "bridge") == 0) { os_strlcpy(bss->bridge, pos, sizeof(bss->bridge)); + } else if (os_strcmp(buf, "vlan_bridge") == 0) { + os_strlcpy(bss->vlan_bridge, pos, + sizeof(bss->vlan_bridge)); } else if (os_strcmp(buf, "wds_bridge") == 0) { os_strlcpy(bss->wds_bridge, pos, sizeof(bss->wds_bridge)); @@ -1280,7 +1631,8 @@ } else if (os_strcmp(buf, "logger_stdout") == 0) { bss->logger_stdout = atoi(pos); } else if (os_strcmp(buf, "dump_file") == 0) { - bss->dump_log_name = os_strdup(pos); + wpa_printf(MSG_INFO, "Line %d: DEPRECATED: 'dump_file' configuration variable is not used anymore", + line); } else if (os_strcmp(buf, "ssid") == 0) { bss->ssid.ssid_len = os_strlen(pos); if (bss->ssid.ssid_len > HOSTAPD_MAX_SSID_LEN || @@ -1291,9 +1643,24 @@ } else { os_memcpy(bss->ssid.ssid, pos, bss->ssid.ssid_len); - bss->ssid.ssid[bss->ssid.ssid_len] = '\0'; bss->ssid.ssid_set = 1; } + } else if (os_strcmp(buf, "ssid2") == 0) { + size_t slen; + char *str = wpa_config_parse_string(pos, &slen); + if (str == NULL || slen < 1 || + slen > HOSTAPD_MAX_SSID_LEN) { + wpa_printf(MSG_ERROR, "Line %d: invalid SSID " + "'%s'", line, pos); + errors++; + } else { + os_memcpy(bss->ssid.ssid, str, slen); + bss->ssid.ssid_len = slen; + bss->ssid.ssid_set = 1; + } + os_free(str); + } 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 && @@ -1322,16 +1689,22 @@ } } else if (os_strcmp(buf, "wds_sta") == 0) { bss->wds_sta = atoi(pos); + } else if (os_strcmp(buf, "start_disabled") == 0) { + bss->start_disabled = atoi(pos); } else if (os_strcmp(buf, "ap_isolate") == 0) { bss->isolate = atoi(pos); } else if (os_strcmp(buf, "ap_max_inactivity") == 0) { bss->ap_max_inactivity = atoi(pos); + } else if (os_strcmp(buf, "skip_inactivity_poll") == 0) { + 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, "ieee80211d") == 0) { conf->ieee80211d = atoi(pos); + } else if (os_strcmp(buf, "ieee80211h") == 0) { + conf->ieee80211h = atoi(pos); } else if (os_strcmp(buf, "ieee8021x") == 0) { bss->ieee802_1x = atoi(pos); } else if (os_strcmp(buf, "eapol_version") == 0) { @@ -1370,6 +1743,9 @@ bss->private_key_passwd = os_strdup(pos); } else if (os_strcmp(buf, "check_crl") == 0) { bss->check_crl = atoi(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, "dh_file") == 0) { os_free(bss->dh_file); bss->dh_file = os_strdup(pos); @@ -1442,7 +1818,7 @@ "allocate memory for " "eap_req_id_text", line); errors++; - continue; + return errors; } bss->eap_req_id_text_len = os_strlen(bss->eap_req_id_text); @@ -1562,6 +1938,51 @@ } else if (os_strcmp(buf, "radius_acct_interim_interval") == 0) { bss->acct_interim_interval = atoi(pos); + } else if (os_strcmp(buf, "radius_request_cui") == 0) { + bss->radius_request_cui = atoi(pos); + } else if (os_strcmp(buf, "radius_auth_req_attr") == 0) { + struct hostapd_radius_attr *attr, *a; + attr = hostapd_parse_radius_attr(pos); + if (attr == NULL) { + wpa_printf(MSG_ERROR, "Line %d: invalid " + "radius_auth_req_attr", line); + errors++; + } else if (bss->radius_auth_req_attr == NULL) { + bss->radius_auth_req_attr = attr; + } else { + a = bss->radius_auth_req_attr; + while (a->next) + a = a->next; + a->next = attr; + } + } else if (os_strcmp(buf, "radius_acct_req_attr") == 0) { + struct hostapd_radius_attr *attr, *a; + attr = hostapd_parse_radius_attr(pos); + if (attr == NULL) { + wpa_printf(MSG_ERROR, "Line %d: invalid " + "radius_acct_req_attr", line); + errors++; + } else if (bss->radius_acct_req_attr == NULL) { + bss->radius_acct_req_attr = attr; + } else { + a = bss->radius_acct_req_attr; + while (a->next) + a = a->next; + a->next = attr; + } + } else if (os_strcmp(buf, "radius_das_port") == 0) { + bss->radius_das_port = atoi(pos); + } else if (os_strcmp(buf, "radius_das_client") == 0) { + if (hostapd_parse_das_client(bss, pos) < 0) { + wpa_printf(MSG_ERROR, "Line %d: invalid " + "DAS client", line); + errors++; + } + } else if (os_strcmp(buf, "radius_das_time_window") == 0) { + 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); #endif /* CONFIG_NO_RADIUS */ } else if (os_strcmp(buf, "auth_algs") == 0) { bss->auth_algs = atoi(pos); @@ -1601,6 +2022,11 @@ } else { os_free(bss->ssid.wpa_passphrase); bss->ssid.wpa_passphrase = os_strdup(pos); + if (bss->ssid.wpa_passphrase) { + os_free(bss->ssid.wpa_psk); + bss->ssid.wpa_psk = NULL; + bss->ssid.wpa_passphrase_set = 1; + } } } else if (os_strcmp(buf, "wpa_psk") == 0) { os_free(bss->ssid.wpa_psk); @@ -1616,6 +2042,9 @@ errors++; } else { bss->ssid.wpa_psk->group = 1; + os_free(bss->ssid.wpa_passphrase); + bss->ssid.wpa_passphrase = NULL; + bss->ssid.wpa_psk_set = 1; } } else if (os_strcmp(buf, "wpa_psk_file") == 0) { os_free(bss->ssid.wpa_psk_file); @@ -1630,6 +2059,16 @@ hostapd_config_parse_key_mgmt(line, pos); if (bss->wpa_key_mgmt == -1) errors++; + } else if (os_strcmp(buf, "wpa_psk_radius") == 0) { + bss->wpa_psk_radius = atoi(pos); + if (bss->wpa_psk_radius != PSK_RADIUS_IGNORED && + bss->wpa_psk_radius != PSK_RADIUS_ACCEPTED && + bss->wpa_psk_radius != PSK_RADIUS_REQUIRED) { + wpa_printf(MSG_ERROR, "Line %d: unknown " + "wpa_psk_radius %d", + line, bss->wpa_psk_radius); + errors++; + } } else if (os_strcmp(buf, "wpa_pairwise") == 0) { bss->wpa_pairwise = hostapd_config_parse_cipher(line, pos); @@ -1676,7 +2115,7 @@ wpa_printf(MSG_DEBUG, "Line %d: Invalid " "mobility_domain '%s'", line, pos); errors++; - continue; + return errors; } } else if (os_strcmp(buf, "r1_key_holder") == 0) { if (os_strlen(pos) != 2 * FT_R1KH_ID_LEN || @@ -1685,7 +2124,7 @@ wpa_printf(MSG_DEBUG, "Line %d: Invalid " "r1_key_holder '%s'", line, pos); errors++; - continue; + return errors; } } else if (os_strcmp(buf, "r0_key_lifetime") == 0) { bss->r0_key_lifetime = atoi(pos); @@ -1696,14 +2135,14 @@ wpa_printf(MSG_DEBUG, "Line %d: Invalid " "r0kh '%s'", line, pos); errors++; - continue; + return errors; } } else if (os_strcmp(buf, "r1kh") == 0) { if (add_r1kh(bss, pos) < 0) { wpa_printf(MSG_DEBUG, "Line %d: Invalid " "r1kh '%s'", line, pos); errors++; - continue; + return errors; } } else if (os_strcmp(buf, "pmk_r1_push") == 0) { bss->pmk_r1_push = atoi(pos); @@ -1727,7 +2166,7 @@ wpa_printf(MSG_DEBUG, "ctrl_interface_group=%d" " (from group name '%s')", bss->ctrl_interface_gid, group); - continue; + return errors; } /* Group name not found - try to parse this as gid */ @@ -1736,7 +2175,7 @@ wpa_printf(MSG_DEBUG, "Line %d: Invalid group " "'%s'", line, group); errors++; - continue; + return errors; } bss->ctrl_interface_gid_set = 1; wpa_printf(MSG_DEBUG, "ctrl_interface_group=%d", @@ -1764,13 +2203,38 @@ conf->hw_mode = HOSTAPD_MODE_IEEE80211B; else if (os_strcmp(pos, "g") == 0) conf->hw_mode = HOSTAPD_MODE_IEEE80211G; + else if (os_strcmp(pos, "ad") == 0) + conf->hw_mode = HOSTAPD_MODE_IEEE80211AD; else { wpa_printf(MSG_ERROR, "Line %d: unknown " "hw_mode '%s'", line, pos); errors++; } + } else if (os_strcmp(buf, "wps_rf_bands") == 0) { + if (os_strcmp(pos, "a") == 0) + bss->wps_rf_bands = WPS_RF_50GHZ; + else if (os_strcmp(pos, "g") == 0 || + os_strcmp(pos, "b") == 0) + bss->wps_rf_bands = WPS_RF_24GHZ; + else if (os_strcmp(pos, "ag") == 0 || + os_strcmp(pos, "ga") == 0) + bss->wps_rf_bands = + WPS_RF_24GHZ | WPS_RF_50GHZ; + else { + wpa_printf(MSG_ERROR, "Line %d: unknown " + "wps_rf_band '%s'", line, pos); + errors++; + } } else if (os_strcmp(buf, "channel") == 0) { - conf->channel = atoi(pos); + if (os_strcmp(pos, "acs_survey") == 0) { +#ifndef CONFIG_ACS + wpa_printf(MSG_ERROR, "Line %d: tries to enable ACS but CONFIG_ACS disabled", + line); + errors++; +#endif /* CONFIG_ACS */ + conf->channel = 0; + } else + conf->channel = atoi(pos); } else if (os_strcmp(buf, "beacon_int") == 0) { int val = atoi(pos); /* MIB defines range as 1..65535, but very small values @@ -1785,6 +2249,16 @@ errors++; } else conf->beacon_int = val; +#ifdef CONFIG_ACS + } else if (os_strcmp(buf, "acs_num_scans") == 0) { + int val = atoi(pos); + if (val <= 0 || val > 100) { + wpa_printf(MSG_ERROR, "Line %d: invalid acs_num_scans %d (expected 1..100)", + line, val); + errors++; + } else + conf->acs_num_scans = val; +#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) { @@ -1820,13 +2294,14 @@ } else conf->send_probe_response = val; } else if (os_strcmp(buf, "supported_rates") == 0) { - if (hostapd_parse_rates(&conf->supported_rates, pos)) { + if (hostapd_parse_intlist(&conf->supported_rates, pos)) + { wpa_printf(MSG_ERROR, "Line %d: invalid rate " "list", line); errors++; } } else if (os_strcmp(buf, "basic_rates") == 0) { - if (hostapd_parse_rates(&conf->basic_rates, pos)) { + if (hostapd_parse_intlist(&conf->basic_rates, pos)) { wpa_printf(MSG_ERROR, "Line %d: invalid rate " "list", line); errors++; @@ -1865,6 +2340,15 @@ "read VLAN file '%s'", line, pos); errors++; } + } else if (os_strcmp(buf, "vlan_naming") == 0) { + bss->ssid.vlan_naming = atoi(pos); + if (bss->ssid.vlan_naming >= DYNAMIC_VLAN_NAMING_END || + bss->ssid.vlan_naming < 0) { + wpa_printf(MSG_ERROR, "Line %d: invalid " + "naming scheme %d", line, + bss->ssid.vlan_naming); + errors++; + } #ifdef CONFIG_FULL_DYNAMIC_VLAN } else if (os_strcmp(buf, "vlan_tagged_interface") == 0) { bss->ssid.vlan_tagged_interface = os_strdup(pos); @@ -1887,7 +2371,8 @@ bss->wmm_uapsd = atoi(pos); } else if (os_strncmp(buf, "wme_ac_", 7) == 0 || os_strncmp(buf, "wmm_ac_", 7) == 0) { - if (hostapd_config_wmm_ac(conf, buf, pos)) { + if (hostapd_config_wmm_ac(conf->wmm_ac_params, buf, + pos)) { wpa_printf(MSG_ERROR, "Line %d: invalid WMM " "ac item", line); errors++; @@ -1935,7 +2420,29 @@ } } else if (os_strcmp(buf, "require_ht") == 0) { conf->require_ht = atoi(pos); + } else if (os_strcmp(buf, "obss_interval") == 0) { + conf->obss_interval = atoi(pos); #endif /* CONFIG_IEEE80211N */ +#ifdef CONFIG_IEEE80211AC + } else if (os_strcmp(buf, "ieee80211ac") == 0) { + conf->ieee80211ac = atoi(pos); + } else if (os_strcmp(buf, "vht_capab") == 0) { + if (hostapd_config_vht_capab(conf, pos) < 0) { + wpa_printf(MSG_ERROR, "Line %d: invalid " + "vht_capab", line); + errors++; + } + } else if (os_strcmp(buf, "require_vht") == 0) { + conf->require_vht = atoi(pos); + } else if (os_strcmp(buf, "vht_oper_chwidth") == 0) { + conf->vht_oper_chwidth = atoi(pos); + } else if (os_strcmp(buf, "vht_oper_centr_freq_seg0_idx") == 0) + { + conf->vht_oper_centr_freq_seg0_idx = atoi(pos); + } else if (os_strcmp(buf, "vht_oper_centr_freq_seg1_idx") == 0) + { + conf->vht_oper_centr_freq_seg1_idx = atoi(pos); +#endif /* CONFIG_IEEE80211AC */ } else if (os_strcmp(buf, "max_listen_interval") == 0) { bss->max_listen_interval = atoi(pos); } else if (os_strcmp(buf, "disable_pmksa_caching") == 0) { @@ -1950,6 +2457,8 @@ "wps_state", line); errors++; } + } else if (os_strcmp(buf, "wps_independent") == 0) { + bss->wps_independent = atoi(pos); } else if (os_strcmp(buf, "ap_setup_locked") == 0) { bss->ap_setup_locked = atoi(pos); } else if (os_strcmp(buf, "uuid") == 0) { @@ -2059,6 +2568,32 @@ bss->upc = os_strdup(pos); } else if (os_strcmp(buf, "pbc_in_m1") == 0) { bss->pbc_in_m1 = atoi(pos); + } else if (os_strcmp(buf, "server_id") == 0) { + os_free(bss->server_id); + bss->server_id = os_strdup(pos); +#ifdef CONFIG_WPS_NFC + } else if (os_strcmp(buf, "wps_nfc_dev_pw_id") == 0) { + bss->wps_nfc_dev_pw_id = atoi(pos); + if (bss->wps_nfc_dev_pw_id < 0x10 || + bss->wps_nfc_dev_pw_id > 0xffff) { + wpa_printf(MSG_ERROR, "Line %d: Invalid " + "wps_nfc_dev_pw_id value", line); + errors++; + } + 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_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_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_pw_from_config = 1; +#endif /* CONFIG_WPS_NFC */ #endif /* CONFIG_WPS */ #ifdef CONFIG_P2P_MANAGER } else if (os_strcmp(buf, "manage_p2p") == 0) { @@ -2100,12 +2635,18 @@ wpa_printf(MSG_DEBUG, "Line %d: invalid " "time_zone", line); errors++; - continue; + return errors; } os_free(bss->time_zone); bss->time_zone = os_strdup(pos); if (bss->time_zone == NULL) errors++; +#ifdef CONFIG_WNM + } else if (os_strcmp(buf, "wnm_sleep_mode") == 0) { + bss->wnm_sleep_mode = atoi(pos); + } else if (os_strcmp(buf, "bss_transition") == 0) { + bss->bss_transition = atoi(pos); +#endif /* CONFIG_WNM */ #ifdef CONFIG_INTERWORKING } else if (os_strcmp(buf, "interworking") == 0) { bss->interworking = atoi(pos); @@ -2140,7 +2681,230 @@ } else if (os_strcmp(buf, "roaming_consortium") == 0) { if (parse_roaming_consortium(bss, pos, line) < 0) errors++; + } else if (os_strcmp(buf, "venue_name") == 0) { + if (parse_venue_name(bss, pos, line) < 0) + errors++; + } else if (os_strcmp(buf, "network_auth_type") == 0) { + u8 auth_type; + u16 redirect_url_len; + if (hexstr2bin(pos, &auth_type, 1)) { + wpa_printf(MSG_ERROR, "Line %d: Invalid " + "network_auth_type '%s'", + line, pos); + errors++; + return errors; + } + if (auth_type == 0 || auth_type == 2) + redirect_url_len = os_strlen(pos + 2); + else + redirect_url_len = 0; + os_free(bss->network_auth_type); + bss->network_auth_type = + os_malloc(redirect_url_len + 3 + 1); + if (bss->network_auth_type == NULL) { + errors++; + return errors; + } + *bss->network_auth_type = auth_type; + WPA_PUT_LE16(bss->network_auth_type + 1, + redirect_url_len); + if (redirect_url_len) + os_memcpy(bss->network_auth_type + 3, + pos + 2, redirect_url_len); + bss->network_auth_type_len = 3 + redirect_url_len; + } else if (os_strcmp(buf, "ipaddr_type_availability") == 0) { + if (hexstr2bin(pos, &bss->ipaddr_type_availability, 1)) + { + wpa_printf(MSG_ERROR, "Line %d: Invalid " + "ipaddr_type_availability '%s'", + line, pos); + bss->ipaddr_type_configured = 0; + errors++; + return errors; + } + bss->ipaddr_type_configured = 1; + } else if (os_strcmp(buf, "domain_name") == 0) { + int j, num_domains, domain_len, domain_list_len = 0; + char *tok_start, *tok_prev; + u8 *domain_list, *domain_ptr; + + domain_list_len = os_strlen(pos) + 1; + domain_list = os_malloc(domain_list_len); + if (domain_list == NULL) { + errors++; + return errors; + } + + domain_ptr = domain_list; + tok_prev = pos; + num_domains = 1; + while ((tok_prev = os_strchr(tok_prev, ','))) { + num_domains++; + tok_prev++; + } + tok_prev = pos; + for (j = 0; j < num_domains; j++) { + tok_start = os_strchr(tok_prev, ','); + if (tok_start) { + domain_len = tok_start - tok_prev; + *domain_ptr = domain_len; + os_memcpy(domain_ptr + 1, tok_prev, + domain_len); + domain_ptr += domain_len + 1; + tok_prev = ++tok_start; + } else { + domain_len = os_strlen(tok_prev); + *domain_ptr = domain_len; + os_memcpy(domain_ptr + 1, tok_prev, + domain_len); + domain_ptr += domain_len + 1; + } + } + + os_free(bss->domain_name); + bss->domain_name = domain_list; + bss->domain_name_len = domain_list_len; + } else if (os_strcmp(buf, "anqp_3gpp_cell_net") == 0) { + if (parse_3gpp_cell_net(bss, pos, line) < 0) + errors++; + } else if (os_strcmp(buf, "nai_realm") == 0) { + if (parse_nai_realm(bss, pos, line) < 0) + errors++; + } else if (os_strcmp(buf, "gas_frag_limit") == 0) { + bss->gas_frag_limit = atoi(pos); + } else if (os_strcmp(buf, "gas_comeback_delay") == 0) { + bss->gas_comeback_delay = atoi(pos); + } else if (os_strcmp(buf, "qos_map_set") == 0) { + if (parse_qos_map_set(bss, pos, line) < 0) + errors++; #endif /* CONFIG_INTERWORKING */ +#ifdef CONFIG_RADIUS_TEST + } else if (os_strcmp(buf, "dump_msk_file") == 0) { + os_free(bss->dump_msk_file); + bss->dump_msk_file = os_strdup(pos); +#endif /* CONFIG_RADIUS_TEST */ +#ifdef CONFIG_HS20 + } else if (os_strcmp(buf, "hs20") == 0) { + bss->hs20 = atoi(pos); + } else if (os_strcmp(buf, "disable_dgaf") == 0) { + bss->disable_dgaf = atoi(pos); + } else if (os_strcmp(buf, "hs20_oper_friendly_name") == 0) { + if (hs20_parse_oper_friendly_name(bss, pos, line) < 0) + errors++; + } else if (os_strcmp(buf, "hs20_wan_metrics") == 0) { + if (hs20_parse_wan_metrics(bss, pos, line) < 0) { + errors++; + return errors; + } + } else if (os_strcmp(buf, "hs20_conn_capab") == 0) { + if (hs20_parse_conn_capab(bss, pos, line) < 0) { + errors++; + return errors; + } + } else if (os_strcmp(buf, "hs20_operating_class") == 0) { + u8 *oper_class; + size_t oper_class_len; + oper_class_len = os_strlen(pos); + if (oper_class_len < 2 || (oper_class_len & 0x01)) { + wpa_printf(MSG_ERROR, "Line %d: Invalid " + "hs20_operating_class '%s'", + line, pos); + errors++; + return errors; + } + oper_class_len /= 2; + oper_class = os_malloc(oper_class_len); + if (oper_class == NULL) { + errors++; + return errors; + } + if (hexstr2bin(pos, oper_class, oper_class_len)) { + wpa_printf(MSG_ERROR, "Line %d: Invalid " + "hs20_operating_class '%s'", + line, pos); + os_free(oper_class); + errors++; + return errors; + } + os_free(bss->hs20_operating_class); + bss->hs20_operating_class = oper_class; + bss->hs20_operating_class_len = oper_class_len; +#endif /* CONFIG_HS20 */ +#ifdef CONFIG_TESTING_OPTIONS +#define PARSE_TEST_PROBABILITY(_val) \ + } else if (os_strcmp(buf, #_val) == 0) { \ + char *end; \ + \ + conf->_val = strtod(pos, &end); \ + if (*end || conf->_val < 0.0d || \ + conf->_val > 1.0d) { \ + wpa_printf(MSG_ERROR, \ + "Line %d: Invalid value '%s'", \ + line, pos); \ + errors++; \ + return errors; \ + } + PARSE_TEST_PROBABILITY(ignore_probe_probability) + PARSE_TEST_PROBABILITY(ignore_auth_probability) + 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, "bss_load_test") == 0) { + WPA_PUT_LE16(bss->bss_load_test, atoi(pos)); + pos = os_strchr(pos, ':'); + if (pos == NULL) { + wpa_printf(MSG_ERROR, "Line %d: Invalid " + "bss_load_test", line); + return 1; + } + pos++; + bss->bss_load_test[2] = atoi(pos); + pos = os_strchr(pos, ':'); + if (pos == NULL) { + wpa_printf(MSG_ERROR, "Line %d: Invalid " + "bss_load_test", line); + return 1; + } + pos++; + WPA_PUT_LE16(&bss->bss_load_test[3], atoi(pos)); + bss->bss_load_test_set = 1; +#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); + 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) + 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); + 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_groups") == 0) { + if (hostapd_parse_intlist(&bss->sae_groups, pos)) { + wpa_printf(MSG_ERROR, "Line %d: Invalid " + "sae_groups value '%s'", line, pos); + return 1; + } } else { wpa_printf(MSG_ERROR, "Line %d: unknown configuration " "item '%s'", line, buf); @@ -2148,66 +2912,84 @@ } } - fclose(f); + return errors; +} - for (i = 0; i < conf->num_bss; i++) { - bss = &conf->bss[i]; - if (bss->individual_wep_key_len == 0) { - /* individual keys are not use; can use key idx0 for - * broadcast keys */ - bss->broadcast_key_idx_min = 0; - } - - /* Select group cipher based on the enabled pairwise cipher - * suites */ - pairwise = 0; - if (bss->wpa & 1) - pairwise |= bss->wpa_pairwise; - if (bss->wpa & 2) { - if (bss->rsn_pairwise == 0) - bss->rsn_pairwise = bss->wpa_pairwise; - pairwise |= bss->rsn_pairwise; - } - if (pairwise & WPA_CIPHER_TKIP) - bss->wpa_group = WPA_CIPHER_TKIP; - else - bss->wpa_group = WPA_CIPHER_CCMP; - - bss->radius->auth_server = bss->radius->auth_servers; - bss->radius->acct_server = bss->radius->acct_servers; - - if (bss->wpa && bss->ieee802_1x) { - bss->ssid.security_policy = SECURITY_WPA; - } else if (bss->wpa) { - bss->ssid.security_policy = SECURITY_WPA_PSK; - } else if (bss->ieee802_1x) { - int cipher = WPA_CIPHER_NONE; - bss->ssid.security_policy = SECURITY_IEEE_802_1X; - bss->ssid.wep.default_len = bss->default_wep_key_len; - if (bss->default_wep_key_len) - cipher = bss->default_wep_key_len >= 13 ? - WPA_CIPHER_WEP104 : WPA_CIPHER_WEP40; - bss->wpa_group = cipher; - bss->wpa_pairwise = cipher; - bss->rsn_pairwise = cipher; - } else if (bss->ssid.wep.keys_set) { - int cipher = WPA_CIPHER_WEP40; - if (bss->ssid.wep.len[0] >= 13) - cipher = WPA_CIPHER_WEP104; - bss->ssid.security_policy = SECURITY_STATIC_WEP; - bss->wpa_group = cipher; - bss->wpa_pairwise = cipher; - bss->rsn_pairwise = cipher; - } else { - bss->ssid.security_policy = SECURITY_PLAINTEXT; - bss->wpa_group = WPA_CIPHER_NONE; - bss->wpa_pairwise = WPA_CIPHER_NONE; - bss->rsn_pairwise = WPA_CIPHER_NONE; +/** + * hostapd_config_read - Read and parse a configuration file + * @fname: Configuration file name (including path, if needed) + * Returns: Allocated configuration data structure + */ +struct hostapd_config * hostapd_config_read(const char *fname) +{ + struct hostapd_config *conf; + struct hostapd_bss_config *bss; + FILE *f; + char buf[512], *pos; + int line = 0; + int errors = 0; + size_t i; + + f = fopen(fname, "r"); + if (f == NULL) { + wpa_printf(MSG_ERROR, "Could not open configuration file '%s' " + "for reading.", fname); + return NULL; + } + + conf = hostapd_config_defaults(); + if (conf == NULL) { + fclose(f); + return NULL; + } + + /* set default driver based on configuration */ + conf->driver = wpa_drivers[0]; + if (conf->driver == NULL) { + wpa_printf(MSG_ERROR, "No driver wrappers registered!"); + hostapd_config_free(conf); + fclose(f); + return NULL; + } + + bss = conf->last_bss = conf->bss[0]; + + while (fgets(buf, sizeof(buf), f)) { + bss = conf->last_bss; + line++; + + if (buf[0] == '#') + continue; + pos = buf; + while (*pos != '\0') { + if (*pos == '\n') { + *pos = '\0'; + break; + } + pos++; } + if (buf[0] == '\0') + continue; + + pos = os_strchr(buf, '='); + if (pos == NULL) { + wpa_printf(MSG_ERROR, "Line %d: invalid line '%s'", + line, buf); + errors++; + continue; + } + *pos = '\0'; + pos++; + errors += hostapd_config_fill(conf, bss, buf, pos, line); } - if (hostapd_config_check(conf)) + fclose(f); + + for (i = 0; i < conf->num_bss; i++) + hostapd_set_security_params(conf->bss[i]); + + if (hostapd_config_check(conf, 1)) errors++; #ifndef WPA_IGNORE_CONFIG_ERRORS @@ -2221,3 +3003,28 @@ return conf; } + + +int hostapd_set_iface(struct hostapd_config *conf, + struct hostapd_bss_config *bss, char *field, char *value) +{ + int errors; + size_t i; + + errors = hostapd_config_fill(conf, bss, field, value, 0); + if (errors) { + wpa_printf(MSG_INFO, "Failed to set configuration field '%s' " + "to value '%s'", field, value); + return -1; + } + + for (i = 0; i < conf->num_bss; i++) + hostapd_set_security_params(conf->bss[i]); + + if (hostapd_config_check(conf, 0)) { + wpa_printf(MSG_ERROR, "Configuration check failed"); + return -1; + } + + return 0; +} diff -Nru wpa-1.0/hostapd/config_file.h wpa-2.1/hostapd/config_file.h --- wpa-1.0/hostapd/config_file.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/hostapd/config_file.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,19 +2,16 @@ * hostapd / Configuration file parser * Copyright (c) 2003-2009, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef CONFIG_FILE_H #define CONFIG_FILE_H struct hostapd_config * hostapd_config_read(const char *fname); +int hostapd_set_iface(struct hostapd_config *conf, + struct hostapd_bss_config *bss, char *field, + char *value); #endif /* CONFIG_FILE_H */ diff -Nru wpa-1.0/hostapd/ctrl_iface.c wpa-2.1/hostapd/ctrl_iface.c --- wpa-1.0/hostapd/ctrl_iface.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/hostapd/ctrl_iface.c 2014-02-04 11:23:35.000000000 +0000 @@ -1,15 +1,9 @@ /* * hostapd / UNIX domain socket -based control interface - * Copyright (c) 2004-2010, Jouni Malinen + * Copyright (c) 2004-2014, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "utils/includes.h" @@ -26,18 +20,21 @@ #include "common/ieee802_11_defs.h" #include "drivers/driver.h" #include "radius/radius_client.h" +#include "radius/radius_server.h" #include "ap/hostapd.h" #include "ap/ap_config.h" #include "ap/ieee802_1x.h" #include "ap/wpa_auth.h" #include "ap/ieee802_11.h" #include "ap/sta_info.h" -#include "ap/accounting.h" #include "ap/wps_hostapd.h" #include "ap/ctrl_iface_ap.h" #include "ap/ap_drv_ops.h" +#include "ap/wnm_ap.h" +#include "ap/wpa_auth.h" #include "wps/wps_defs.h" #include "wps/wps.h" +#include "config_file.h" #include "ctrl_iface.h" @@ -87,15 +84,15 @@ 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); - wpa_hexdump(MSG_DEBUG, "CTRL_IFACE monitor detached", - (u8 *) from->sun_path, - fromlen - - offsetof(struct sockaddr_un, sun_path)); return 0; } prev = dst; @@ -159,171 +156,6 @@ } -#ifdef CONFIG_P2P_MANAGER -static int p2p_manager_disconnect(struct hostapd_data *hapd, u16 stype, - u8 minor_reason_code, const u8 *addr) -{ - struct ieee80211_mgmt *mgmt; - int ret; - u8 *pos; - - if (hapd->driver->send_frame == NULL) - return -1; - - mgmt = os_zalloc(sizeof(*mgmt) + 100); - if (mgmt == NULL) - return -1; - - wpa_printf(MSG_DEBUG, "P2P: Disconnect STA " MACSTR " with minor " - "reason code %u (stype=%u)", - MAC2STR(addr), minor_reason_code, stype); - - mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, stype); - 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); - 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); - } else { - mgmt->u.disassoc.reason_code = - host_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID); - pos = (u8 *) (&mgmt->u.disassoc.reason_code + 1); - } - - *pos++ = WLAN_EID_VENDOR_SPECIFIC; - *pos++ = 4 + 3 + 1; - WPA_PUT_BE24(pos, OUI_WFA); - pos += 3; - *pos++ = P2P_OUI_TYPE; - - *pos++ = P2P_ATTR_MINOR_REASON_CODE; - WPA_PUT_LE16(pos, 1); - pos += 2; - *pos++ = minor_reason_code; - - ret = hapd->driver->send_frame(hapd->drv_priv, (u8 *) mgmt, - pos - (u8 *) mgmt, 1); - os_free(mgmt); - - return ret < 0 ? -1 : 0; -} -#endif /* CONFIG_P2P_MANAGER */ - - -static int hostapd_ctrl_iface_deauthenticate(struct hostapd_data *hapd, - const char *txtaddr) -{ - u8 addr[ETH_ALEN]; - struct sta_info *sta; - const char *pos; - - wpa_printf(MSG_DEBUG, "CTRL_IFACE DEAUTHENTICATE %s", txtaddr); - - if (hwaddr_aton(txtaddr, addr)) - return -1; - - pos = os_strstr(txtaddr, " test="); - if (pos) { - struct ieee80211_mgmt mgmt; - int encrypt; - if (hapd->driver->send_frame == NULL) - return -1; - pos += 6; - encrypt = atoi(pos); - os_memset(&mgmt, 0, sizeof(mgmt)); - mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, - WLAN_FC_STYPE_DEAUTH); - 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.deauth.reason_code = - host_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID); - if (hapd->driver->send_frame(hapd->drv_priv, (u8 *) &mgmt, - IEEE80211_HDRLEN + - sizeof(mgmt.u.deauth), - encrypt) < 0) - return -1; - return 0; - } - -#ifdef CONFIG_P2P_MANAGER - pos = os_strstr(txtaddr, " p2p="); - if (pos) { - return p2p_manager_disconnect(hapd, WLAN_FC_STYPE_DEAUTH, - atoi(pos + 5), addr); - } -#endif /* CONFIG_P2P_MANAGER */ - - hostapd_drv_sta_deauth(hapd, addr, WLAN_REASON_PREV_AUTH_NOT_VALID); - sta = ap_get_sta(hapd, addr); - if (sta) - ap_sta_deauthenticate(hapd, sta, - WLAN_REASON_PREV_AUTH_NOT_VALID); - else if (addr[0] == 0xff) - hostapd_free_stas(hapd); - - return 0; -} - - -static int hostapd_ctrl_iface_disassociate(struct hostapd_data *hapd, - const char *txtaddr) -{ - u8 addr[ETH_ALEN]; - struct sta_info *sta; - const char *pos; - - wpa_printf(MSG_DEBUG, "CTRL_IFACE DISASSOCIATE %s", txtaddr); - - if (hwaddr_aton(txtaddr, addr)) - return -1; - - pos = os_strstr(txtaddr, " test="); - if (pos) { - struct ieee80211_mgmt mgmt; - int encrypt; - if (hapd->driver->send_frame == NULL) - return -1; - pos += 6; - encrypt = atoi(pos); - os_memset(&mgmt, 0, sizeof(mgmt)); - mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, - WLAN_FC_STYPE_DISASSOC); - 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.disassoc.reason_code = - host_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID); - if (hapd->driver->send_frame(hapd->drv_priv, (u8 *) &mgmt, - IEEE80211_HDRLEN + - sizeof(mgmt.u.deauth), - encrypt) < 0) - return -1; - return 0; - } - -#ifdef CONFIG_P2P_MANAGER - pos = os_strstr(txtaddr, " p2p="); - if (pos) { - return p2p_manager_disconnect(hapd, WLAN_FC_STYPE_DISASSOC, - atoi(pos + 5), addr); - } -#endif /* CONFIG_P2P_MANAGER */ - - hostapd_drv_sta_disassoc(hapd, addr, WLAN_REASON_PREV_AUTH_NOT_VALID); - sta = ap_get_sta(hapd, addr); - if (sta) - ap_sta_disassociate(hapd, sta, - WLAN_REASON_PREV_AUTH_NOT_VALID); - else if (addr[0] == 0xff) - hostapd_free_stas(hapd); - - return 0; -} - - #ifdef CONFIG_IEEE80211W #ifdef NEED_AP_MLME static int hostapd_ctrl_iface_sa_query(struct hostapd_data *hapd, @@ -421,28 +253,219 @@ } -#ifdef CONFIG_WPS_OOB -static int hostapd_ctrl_iface_wps_oob(struct hostapd_data *hapd, char *txt) +#ifdef CONFIG_WPS_NFC +static int hostapd_ctrl_iface_wps_nfc_tag_read(struct hostapd_data *hapd, + char *pos) +{ + size_t len; + struct wpabuf *buf; + int ret; + + len = os_strlen(pos); + if (len & 0x01) + return -1; + len /= 2; + + buf = wpabuf_alloc(len); + if (buf == NULL) + return -1; + if (hexstr2bin(pos, wpabuf_put(buf, len), len) < 0) { + wpabuf_free(buf); + return -1; + } + + ret = hostapd_wps_nfc_tag_read(hapd, buf); + wpabuf_free(buf); + + return ret; +} + + +static int hostapd_ctrl_iface_wps_nfc_config_token(struct hostapd_data *hapd, + char *cmd, char *reply, + size_t max_len) +{ + int ndef; + struct wpabuf *buf; + int res; + + if (os_strcmp(cmd, "WPS") == 0) + ndef = 0; + else if (os_strcmp(cmd, "NDEF") == 0) + ndef = 1; + else + return -1; + + buf = hostapd_wps_nfc_config_token(hapd, ndef); + if (buf == NULL) + return -1; + + res = wpa_snprintf_hex_uppercase(reply, max_len, wpabuf_head(buf), + wpabuf_len(buf)); + reply[res++] = '\n'; + reply[res] = '\0'; + + wpabuf_free(buf); + + return res; +} + + +static int hostapd_ctrl_iface_wps_nfc_token_gen(struct hostapd_data *hapd, + char *reply, size_t max_len, + int ndef) +{ + struct wpabuf *buf; + int res; + + buf = hostapd_wps_nfc_token_gen(hapd, ndef); + if (buf == NULL) + return -1; + + res = wpa_snprintf_hex_uppercase(reply, max_len, wpabuf_head(buf), + wpabuf_len(buf)); + reply[res++] = '\n'; + reply[res] = '\0'; + + wpabuf_free(buf); + + return res; +} + + +static int hostapd_ctrl_iface_wps_nfc_token(struct hostapd_data *hapd, + char *cmd, char *reply, + size_t max_len) +{ + if (os_strcmp(cmd, "WPS") == 0) + return hostapd_ctrl_iface_wps_nfc_token_gen(hapd, reply, + max_len, 0); + + if (os_strcmp(cmd, "NDEF") == 0) + return hostapd_ctrl_iface_wps_nfc_token_gen(hapd, reply, + max_len, 1); + + if (os_strcmp(cmd, "enable") == 0) + return hostapd_wps_nfc_token_enable(hapd); + + if (os_strcmp(cmd, "disable") == 0) { + hostapd_wps_nfc_token_disable(hapd); + return 0; + } + + return -1; +} + + +static int hostapd_ctrl_iface_nfc_get_handover_sel(struct hostapd_data *hapd, + char *cmd, char *reply, + size_t max_len) +{ + struct wpabuf *buf; + int res; + char *pos; + int ndef; + + pos = os_strchr(cmd, ' '); + if (pos == NULL) + return -1; + *pos++ = '\0'; + + if (os_strcmp(cmd, "WPS") == 0) + ndef = 0; + else if (os_strcmp(cmd, "NDEF") == 0) + ndef = 1; + else + return -1; + + if (os_strcmp(pos, "WPS-CR") == 0) + buf = hostapd_wps_nfc_hs_cr(hapd, ndef); + else + buf = NULL; + if (buf == NULL) + return -1; + + res = wpa_snprintf_hex_uppercase(reply, max_len, wpabuf_head(buf), + wpabuf_len(buf)); + reply[res++] = '\n'; + reply[res] = '\0'; + + wpabuf_free(buf); + + return res; +} + + +static int hostapd_ctrl_iface_nfc_report_handover(struct hostapd_data *hapd, + char *cmd) { - char *path, *method, *name; + size_t len; + struct wpabuf *req, *sel; + int ret; + char *pos, *role, *type, *pos2; + + role = cmd; + pos = os_strchr(role, ' '); + if (pos == NULL) + return -1; + *pos++ = '\0'; + + type = pos; + pos = os_strchr(type, ' '); + if (pos == NULL) + return -1; + *pos++ = '\0'; + + pos2 = os_strchr(pos, ' '); + if (pos2 == NULL) + return -1; + *pos2++ = '\0'; + + len = os_strlen(pos); + if (len & 0x01) + return -1; + len /= 2; + + req = wpabuf_alloc(len); + if (req == NULL) + return -1; + if (hexstr2bin(pos, wpabuf_put(req, len), len) < 0) { + wpabuf_free(req); + return -1; + } - path = os_strchr(txt, ' '); - if (path == NULL) + len = os_strlen(pos2); + if (len & 0x01) { + wpabuf_free(req); return -1; - *path++ = '\0'; + } + len /= 2; - method = os_strchr(path, ' '); - if (method == NULL) + sel = wpabuf_alloc(len); + if (sel == NULL) { + wpabuf_free(req); + return -1; + } + if (hexstr2bin(pos2, wpabuf_put(sel, len), len) < 0) { + wpabuf_free(req); + wpabuf_free(sel); return -1; - *method++ = '\0'; + } - name = os_strchr(method, ' '); - if (name != NULL) - *name++ = '\0'; + if (os_strcmp(role, "RESP") == 0 && os_strcmp(type, "WPS") == 0) { + ret = hostapd_wps_nfc_report_handover(hapd, req, sel); + } else { + wpa_printf(MSG_DEBUG, "NFC: Unsupported connection handover " + "reported: role=%s type=%s", role, type); + ret = -1; + } + wpabuf_free(req); + wpabuf_free(sel); - return hostapd_wps_start_oob(hapd, txt, path, method, name); + return ret; } -#endif /* CONFIG_WPS_OOB */ + +#endif /* CONFIG_WPS_NFC */ static int hostapd_ctrl_iface_wps_ap_pin(struct hostapd_data *hapd, char *txt, @@ -523,62 +546,27 @@ return hostapd_wps_config_ap(hapd, ssid, auth, encr, key); } -#endif /* CONFIG_WPS */ -static int hostapd_ctrl_iface_ess_disassoc(struct hostapd_data *hapd, - const char *cmd) +static const char * pbc_status_str(enum pbc_status status) { - u8 addr[ETH_ALEN]; - const char *url; - u8 buf[1000], *pos; - struct ieee80211_mgmt *mgmt; - size_t url_len; - - if (hwaddr_aton(cmd, addr)) - return -1; - url = cmd + 17; - if (*url != ' ') - return -1; - url++; - url_len = os_strlen(url); - if (url_len > 255) - 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, 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.bss_tm_req.action = WNM_BSS_TRANS_MGMT_REQ; - mgmt->u.action.u.bss_tm_req.dialog_token = 1; - mgmt->u.action.u.bss_tm_req.req_mode = - WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT; - mgmt->u.action.u.bss_tm_req.disassoc_timer = host_to_le16(0); - mgmt->u.action.u.bss_tm_req.validity_interval = 0; - - pos = mgmt->u.action.u.bss_tm_req.variable; - - /* Session Information URL */ - *pos++ = url_len; - os_memcpy(pos, url, url_len); - pos += url_len; - - if (hostapd_drv_send_mlme(hapd, buf, pos - buf) < 0) { - wpa_printf(MSG_DEBUG, "Failed to send BSS Transition " - "Management Request frame"); - return -1; + switch (status) { + case WPS_PBC_STATUS_DISABLE: + return "Disabled"; + case WPS_PBC_STATUS_ACTIVE: + return "Active"; + case WPS_PBC_STATUS_TIMEOUT: + return "Timed-out"; + case WPS_PBC_STATUS_OVERLAP: + return "Overlap"; + default: + return "Unknown"; } - - return 0; } -static int hostapd_ctrl_iface_get_config(struct hostapd_data *hapd, - char *buf, size_t buflen) +static int hostapd_ctrl_iface_wps_get_status(struct hostapd_data *hapd, + char *buf, size_t buflen) { int ret; char *pos, *end; @@ -586,129 +574,336 @@ pos = buf; end = buf + buflen; - ret = os_snprintf(pos, end - pos, "bssid=" MACSTR "\n" - "ssid=%s\n", - MAC2STR(hapd->own_addr), - hapd->conf->ssid.ssid); + ret = os_snprintf(pos, end - pos, "PBC Status: %s\n", + pbc_status_str(hapd->wps_stats.pbc_status)); + if (ret < 0 || ret >= end - pos) return pos - buf; pos += ret; -#ifdef CONFIG_WPS - ret = os_snprintf(pos, end - pos, "wps_state=%s\n", - hapd->conf->wps_state == 0 ? "disabled" : - (hapd->conf->wps_state == 1 ? "not configured" : - "configured")); + ret = os_snprintf(pos, end - pos, "Last WPS result: %s\n", + (hapd->wps_stats.status == WPS_STATUS_SUCCESS ? + "Success": + (hapd->wps_stats.status == WPS_STATUS_FAILURE ? + "Failed" : "None"))); + if (ret < 0 || ret >= end - pos) return pos - buf; pos += ret; - if (hapd->conf->wps_state && hapd->conf->wpa && - hapd->conf->ssid.wpa_passphrase) { - ret = os_snprintf(pos, end - pos, "passphrase=%s\n", - hapd->conf->ssid.wpa_passphrase); + /* If status == Failure - Add possible Reasons */ + if(hapd->wps_stats.status == WPS_STATUS_FAILURE && + hapd->wps_stats.failure_reason > 0) { + ret = os_snprintf(pos, end - pos, + "Failure Reason: %s\n", + wps_ei_str(hapd->wps_stats.failure_reason)); + if (ret < 0 || ret >= end - pos) return pos - buf; pos += ret; } - if (hapd->conf->wps_state && hapd->conf->wpa && - hapd->conf->ssid.wpa_psk && - hapd->conf->ssid.wpa_psk->group) { - char hex[PMK_LEN * 2 + 1]; - wpa_snprintf_hex(hex, sizeof(hex), - hapd->conf->ssid.wpa_psk->psk, PMK_LEN); - ret = os_snprintf(pos, end - pos, "psk=%s\n", hex); + if (hapd->wps_stats.status) { + ret = os_snprintf(pos, end - pos, "Peer Address: " MACSTR "\n", + MAC2STR(hapd->wps_stats.peer_addr)); + if (ret < 0 || ret >= end - pos) return pos - buf; pos += ret; } + + return pos - buf; +} + #endif /* CONFIG_WPS */ - if (hapd->conf->wpa && hapd->conf->wpa_key_mgmt) { - ret = os_snprintf(pos, end - pos, "key_mgmt="); - if (ret < 0 || ret >= end - pos) - return pos - buf; - pos += ret; - if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK) { - ret = os_snprintf(pos, end - pos, "WPA-PSK "); - if (ret < 0 || ret >= end - pos) - return pos - buf; - pos += ret; - } - if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X) { - ret = os_snprintf(pos, end - pos, "WPA-EAP "); - if (ret < 0 || ret >= end - pos) - return pos - buf; - pos += ret; - } -#ifdef CONFIG_IEEE80211R - if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_PSK) { - ret = os_snprintf(pos, end - pos, "FT-PSK "); - if (ret < 0 || ret >= end - pos) - return pos - buf; - pos += ret; - } - if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X) { - ret = os_snprintf(pos, end - pos, "FT-EAP "); - if (ret < 0 || ret >= end - pos) - return pos - buf; - pos += ret; +#ifdef CONFIG_INTERWORKING + +static int hostapd_ctrl_iface_set_qos_map_set(struct hostapd_data *hapd, + const char *cmd) +{ + u8 qos_map_set[16 + 2 * 21], count = 0; + const char *pos = cmd; + int val, ret; + + for (;;) { + if (count == sizeof(qos_map_set)) { + wpa_printf(MSG_ERROR, "Too many qos_map_set parameters"); + return -1; } -#endif /* CONFIG_IEEE80211R */ -#ifdef CONFIG_IEEE80211W - if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK_SHA256) { - ret = os_snprintf(pos, end - pos, "WPA-PSK-SHA256 "); - if (ret < 0 || ret >= end - pos) - return pos - buf; - pos += ret; + + val = atoi(pos); + if (val < 0 || val > 255) { + wpa_printf(MSG_INFO, "Invalid QoS Map Set"); + return -1; } - if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256) { - ret = os_snprintf(pos, end - pos, "WPA-EAP-SHA256 "); - if (ret < 0 || ret >= end - pos) - return pos - buf; - pos += ret; - } -#endif /* CONFIG_IEEE80211W */ - ret = os_snprintf(pos, end - pos, "\n"); - if (ret < 0 || ret >= end - pos) - return pos - buf; - pos += ret; + qos_map_set[count++] = val; + pos = os_strchr(pos, ','); + if (!pos) + break; + pos++; } - if (hapd->conf->wpa && hapd->conf->wpa_group == WPA_CIPHER_CCMP) { - ret = os_snprintf(pos, end - pos, "group_cipher=CCMP\n"); + if (count < 16 || count & 1) { + wpa_printf(MSG_INFO, "Invalid QoS Map Set"); + return -1; + } + + ret = hostapd_drv_set_qos_map(hapd, qos_map_set, count); + if (ret) { + wpa_printf(MSG_INFO, "Failed to set QoS Map Set"); + return -1; + } + + os_memcpy(hapd->conf->qos_map_set, qos_map_set, count); + hapd->conf->qos_map_set_len = count; + + return 0; +} + + +static int hostapd_ctrl_iface_send_qos_map_conf(struct hostapd_data *hapd, + const char *cmd) +{ + u8 addr[ETH_ALEN]; + struct sta_info *sta; + struct wpabuf *buf; + u8 *qos_map_set = hapd->conf->qos_map_set; + u8 qos_map_set_len = hapd->conf->qos_map_set_len; + int ret; + + if (!qos_map_set_len) { + wpa_printf(MSG_INFO, "QoS Map Set is not set"); + return -1; + } + + if (hwaddr_aton(cmd, addr)) + return -1; + + sta = ap_get_sta(hapd, addr); + if (sta == NULL) { + wpa_printf(MSG_DEBUG, "Station " MACSTR " not found " + "for QoS Map Configuration message", + MAC2STR(addr)); + return -1; + } + + if (!sta->qos_map_enabled) { + wpa_printf(MSG_DEBUG, "Station " MACSTR " did not indicate " + "support for QoS Map", MAC2STR(addr)); + return -1; + } + + buf = wpabuf_alloc(2 + 2 + qos_map_set_len); + if (buf == NULL) + return -1; + + wpabuf_put_u8(buf, WLAN_ACTION_QOS); + wpabuf_put_u8(buf, QOS_QOS_MAP_CONFIG); + + /* QoS Map Set Element */ + wpabuf_put_u8(buf, WLAN_EID_QOS_MAP_SET); + wpabuf_put_u8(buf, qos_map_set_len); + wpabuf_put_data(buf, qos_map_set, qos_map_set_len); + + ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr, + wpabuf_head(buf), wpabuf_len(buf)); + wpabuf_free(buf); + + return ret; +} + +#endif /* CONFIG_INTERWORKING */ + + +#ifdef CONFIG_WNM + +static int hostapd_ctrl_iface_disassoc_imminent(struct hostapd_data *hapd, + const char *cmd) +{ + u8 addr[ETH_ALEN]; + int disassoc_timer; + struct sta_info *sta; + + if (hwaddr_aton(cmd, addr)) + return -1; + if (cmd[17] != ' ') + return -1; + disassoc_timer = atoi(cmd + 17); + + sta = ap_get_sta(hapd, addr); + if (sta == NULL) { + wpa_printf(MSG_DEBUG, "Station " MACSTR + " not found for disassociation imminent message", + MAC2STR(addr)); + return -1; + } + + return wnm_send_disassoc_imminent(hapd, sta, disassoc_timer); +} + + +static int hostapd_ctrl_iface_ess_disassoc(struct hostapd_data *hapd, + const char *cmd) +{ + u8 addr[ETH_ALEN]; + const char *url, *timerstr; + int disassoc_timer; + struct sta_info *sta; + + if (hwaddr_aton(cmd, addr)) + return -1; + + sta = ap_get_sta(hapd, addr); + if (sta == NULL) { + wpa_printf(MSG_DEBUG, "Station " MACSTR + " not found for ESS disassociation imminent message", + MAC2STR(addr)); + return -1; + } + + timerstr = cmd + 17; + if (*timerstr != ' ') + return -1; + timerstr++; + disassoc_timer = atoi(timerstr); + if (disassoc_timer < 0 || disassoc_timer > 65535) + return -1; + + url = os_strchr(timerstr, ' '); + if (url == NULL) + return -1; + url++; + + return wnm_send_ess_disassoc_imminent(hapd, sta, url, disassoc_timer); +} + +#endif /* CONFIG_WNM */ + + +static int hostapd_ctrl_iface_get_config(struct hostapd_data *hapd, + char *buf, size_t buflen) +{ + int ret; + char *pos, *end; + + pos = buf; + end = buf + buflen; + + ret = os_snprintf(pos, end - pos, "bssid=" MACSTR "\n" + "ssid=%s\n", + MAC2STR(hapd->own_addr), + wpa_ssid_txt(hapd->conf->ssid.ssid, + hapd->conf->ssid.ssid_len)); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + +#ifdef CONFIG_WPS + ret = os_snprintf(pos, end - pos, "wps_state=%s\n", + hapd->conf->wps_state == 0 ? "disabled" : + (hapd->conf->wps_state == 1 ? "not configured" : + "configured")); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + + if (hapd->conf->wps_state && hapd->conf->wpa && + hapd->conf->ssid.wpa_passphrase) { + ret = os_snprintf(pos, end - pos, "passphrase=%s\n", + hapd->conf->ssid.wpa_passphrase); if (ret < 0 || ret >= end - pos) return pos - buf; pos += ret; - } else if (hapd->conf->wpa && - hapd->conf->wpa_group == WPA_CIPHER_TKIP) { - ret = os_snprintf(pos, end - pos, "group_cipher=TKIP\n"); + } + + if (hapd->conf->wps_state && hapd->conf->wpa && + hapd->conf->ssid.wpa_psk && + hapd->conf->ssid.wpa_psk->group) { + char hex[PMK_LEN * 2 + 1]; + wpa_snprintf_hex(hex, sizeof(hex), + hapd->conf->ssid.wpa_psk->psk, PMK_LEN); + ret = os_snprintf(pos, end - pos, "psk=%s\n", hex); if (ret < 0 || ret >= end - pos) return pos - buf; pos += ret; } +#endif /* CONFIG_WPS */ - if ((hapd->conf->wpa & WPA_PROTO_RSN) && hapd->conf->rsn_pairwise) { - ret = os_snprintf(pos, end - pos, "rsn_pairwise_cipher="); + if (hapd->conf->wpa && hapd->conf->wpa_key_mgmt) { + ret = os_snprintf(pos, end - pos, "key_mgmt="); if (ret < 0 || ret >= end - pos) return pos - buf; pos += ret; - if (hapd->conf->rsn_pairwise & WPA_CIPHER_CCMP) { - ret = os_snprintf(pos, end - pos, "CCMP "); + if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK) { + ret = os_snprintf(pos, end - pos, "WPA-PSK "); if (ret < 0 || ret >= end - pos) return pos - buf; pos += ret; } - if (hapd->conf->rsn_pairwise & WPA_CIPHER_TKIP) { - ret = os_snprintf(pos, end - pos, "TKIP "); + if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X) { + ret = os_snprintf(pos, end - pos, "WPA-EAP "); if (ret < 0 || ret >= end - pos) return pos - buf; pos += ret; } +#ifdef CONFIG_IEEE80211R + if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_PSK) { + ret = os_snprintf(pos, end - pos, "FT-PSK "); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + } + if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X) { + ret = os_snprintf(pos, end - pos, "FT-EAP "); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + } +#endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_IEEE80211W + if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK_SHA256) { + ret = os_snprintf(pos, end - pos, "WPA-PSK-SHA256 "); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + } + if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256) { + ret = os_snprintf(pos, end - pos, "WPA-EAP-SHA256 "); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + } +#endif /* CONFIG_IEEE80211W */ + + ret = os_snprintf(pos, end - pos, "\n"); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + } + + if (hapd->conf->wpa) { + ret = os_snprintf(pos, end - pos, "group_cipher=%s\n", + wpa_cipher_txt(hapd->conf->wpa_group)); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + } + + if ((hapd->conf->wpa & WPA_PROTO_RSN) && hapd->conf->rsn_pairwise) { + ret = os_snprintf(pos, end - pos, "rsn_pairwise_cipher="); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + + ret = wpa_write_ciphers(pos, end, hapd->conf->rsn_pairwise, + " "); + if (ret < 0) + return pos - buf; + pos += ret; ret = os_snprintf(pos, end - pos, "\n"); if (ret < 0 || ret >= end - pos) @@ -722,18 +917,11 @@ return pos - buf; pos += ret; - if (hapd->conf->wpa_pairwise & WPA_CIPHER_CCMP) { - ret = os_snprintf(pos, end - pos, "CCMP "); - if (ret < 0 || ret >= end - pos) - return pos - buf; - pos += ret; - } - if (hapd->conf->wpa_pairwise & WPA_CIPHER_TKIP) { - ret = os_snprintf(pos, end - pos, "TKIP "); - if (ret < 0 || ret >= end - pos) - return pos - buf; - pos += ret; - } + ret = wpa_write_ciphers(pos, end, hapd->conf->rsn_pairwise, + " "); + if (ret < 0) + return pos - buf; + pos += ret; ret = os_snprintf(pos, end - pos, "\n"); if (ret < 0 || ret >= end - pos) @@ -777,9 +965,25 @@ wps_testing_dummy_cred = atoi(value); wpa_printf(MSG_DEBUG, "WPS: Testing - dummy_cred=%d", wps_testing_dummy_cred); + } else if (os_strcasecmp(cmd, "wps_corrupt_pkhash") == 0) { + wps_corrupt_pkhash = atoi(value); + wpa_printf(MSG_DEBUG, "WPS: Testing - wps_corrupt_pkhash=%d", + wps_corrupt_pkhash); #endif /* CONFIG_WPS_TESTING */ +#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); +#endif /* CONFIG_TESTING_OPTIONS */ } else { - ret = -1; + ret = hostapd_set_iface(hapd->iconf, hapd->conf, cmd, value); } return ret; @@ -804,11 +1008,161 @@ } +static int hostapd_ctrl_iface_enable(struct hostapd_iface *iface) +{ + if (hostapd_enable_iface(iface) < 0) { + wpa_printf(MSG_ERROR, "Enabling of interface failed"); + return -1; + } + return 0; +} + + +static int hostapd_ctrl_iface_reload(struct hostapd_iface *iface) +{ + if (hostapd_reload_iface(iface) < 0) { + wpa_printf(MSG_ERROR, "Reloading of interface failed"); + return -1; + } + return 0; +} + + +static int hostapd_ctrl_iface_disable(struct hostapd_iface *iface) +{ + if (hostapd_disable_iface(iface) < 0) { + wpa_printf(MSG_ERROR, "Disabling of interface failed"); + return -1; + } + return 0; +} + + +#ifdef CONFIG_TESTING_OPTIONS + +static int hostapd_ctrl_iface_radar(struct hostapd_data *hapd, char *cmd) +{ + union wpa_event_data data; + char *pos, *param; + enum wpa_event_type event; + + wpa_printf(MSG_DEBUG, "RADAR TEST: %s", cmd); + + os_memset(&data, 0, sizeof(data)); + + param = os_strchr(cmd, ' '); + if (param == NULL) + return -1; + *param++ = '\0'; + + if (os_strcmp(cmd, "DETECTED") == 0) + event = EVENT_DFS_RADAR_DETECTED; + else if (os_strcmp(cmd, "CAC-FINISHED") == 0) + event = EVENT_DFS_CAC_FINISHED; + else if (os_strcmp(cmd, "CAC-ABORTED") == 0) + event = EVENT_DFS_CAC_ABORTED; + else if (os_strcmp(cmd, "NOP-FINISHED") == 0) + event = EVENT_DFS_NOP_FINISHED; + else { + wpa_printf(MSG_DEBUG, "Unsupported RADAR test command: %s", + cmd); + return -1; + } + + pos = os_strstr(param, "freq="); + if (pos) + data.dfs_event.freq = atoi(pos + 5); + + pos = os_strstr(param, "ht_enabled=1"); + if (pos) + data.dfs_event.ht_enabled = 1; + + pos = os_strstr(param, "chan_offset="); + if (pos) + data.dfs_event.chan_offset = atoi(pos + 12); + + pos = os_strstr(param, "chan_width="); + if (pos) + data.dfs_event.chan_width = atoi(pos + 11); + + pos = os_strstr(param, "cf1="); + if (pos) + data.dfs_event.cf1 = atoi(pos + 4); + + pos = os_strstr(param, "cf2="); + if (pos) + data.dfs_event.cf2 = atoi(pos + 4); + + wpa_supplicant_event(hapd, event, &data); + + return 0; +} + + +static int hostapd_ctrl_iface_mgmt_tx(struct hostapd_data *hapd, char *cmd) +{ + size_t len; + u8 *buf; + int res; + + wpa_printf(MSG_DEBUG, "External MGMT TX: %s", cmd); + + len = os_strlen(cmd); + if (len & 1) + return -1; + len /= 2; + + buf = os_malloc(len); + if (buf == NULL) + return -1; + + if (hexstr2bin(cmd, buf, len) < 0) { + os_free(buf); + return -1; + } + + res = hostapd_drv_send_mlme(hapd, buf, len, 0); + os_free(buf); + return res; +} + +#endif /* CONFIG_TESTING_OPTIONS */ + + +static int hostapd_ctrl_iface_chan_switch(struct hostapd_data *hapd, char *pos) +{ +#ifdef NEED_AP_MLME + struct csa_settings settings; + int ret = hostapd_parse_csa_settings(pos, &settings); + + if (ret) + return ret; + + return hostapd_switch_channel(hapd, &settings); +#else /* NEED_AP_MLME */ + return -1; +#endif /* NEED_AP_MLME */ +} + + +static int hostapd_ctrl_iface_mib(struct hostapd_data *hapd, char *reply, + int reply_size, const char *param) +{ +#ifdef RADIUS_SERVER + if (os_strcmp(param, "radius_server") == 0) { + return radius_server_get_mib(hapd->radius_srv, reply, + reply_size); + } +#endif /* RADIUS_SERVER */ + return -1; +} + + static void hostapd_ctrl_iface_receive(int sock, void *eloop_ctx, void *sock_ctx) { struct hostapd_data *hapd = eloop_ctx; - char buf[256]; + char buf[4096]; int res; struct sockaddr_un from; socklen_t fromlen = sizeof(from); @@ -844,6 +1198,11 @@ } else if (os_strncmp(buf, "RELOG", 5) == 0) { if (wpa_debug_reopen_file() < 0) reply_len = -1; + } else if (os_strcmp(buf, "STATUS") == 0) { + reply_len = hostapd_ctrl_iface_status(hapd, reply, + reply_size); + } else if (os_strcmp(buf, "STATUS-DRIVER") == 0) { + reply_len = hostapd_drv_status(hapd, reply, reply_size); } else if (os_strcmp(buf, "MIB") == 0) { reply_len = ieee802_11_get_mib(hapd, reply, reply_size); if (reply_len >= 0) { @@ -873,6 +1232,9 @@ reply_len += res; } #endif /* CONFIG_NO_RADIUS */ + } else if (os_strncmp(buf, "MIB ", 4) == 0) { + reply_len = hostapd_ctrl_iface_mib(hapd, reply, reply_size, + buf + 4); } else if (os_strcmp(buf, "STA-FIRST") == 0) { reply_len = hostapd_ctrl_iface_sta_first(hapd, reply, reply_size); @@ -918,21 +1280,52 @@ } else if (os_strcmp(buf, "WPS_PBC") == 0) { if (hostapd_wps_button_pushed(hapd, NULL)) reply_len = -1; -#ifdef CONFIG_WPS_OOB - } else if (os_strncmp(buf, "WPS_OOB ", 8) == 0) { - if (hostapd_ctrl_iface_wps_oob(hapd, buf + 8)) + } else if (os_strcmp(buf, "WPS_CANCEL") == 0) { + if (hostapd_wps_cancel(hapd)) reply_len = -1; -#endif /* CONFIG_WPS_OOB */ } else if (os_strncmp(buf, "WPS_AP_PIN ", 11) == 0) { reply_len = hostapd_ctrl_iface_wps_ap_pin(hapd, buf + 11, reply, reply_size); } else if (os_strncmp(buf, "WPS_CONFIG ", 11) == 0) { if (hostapd_ctrl_iface_wps_config(hapd, buf + 11) < 0) reply_len = -1; + } else if (os_strncmp(buf, "WPS_GET_STATUS", 13) == 0) { + reply_len = hostapd_ctrl_iface_wps_get_status(hapd, reply, + reply_size); +#ifdef CONFIG_WPS_NFC + } else if (os_strncmp(buf, "WPS_NFC_TAG_READ ", 17) == 0) { + if (hostapd_ctrl_iface_wps_nfc_tag_read(hapd, buf + 17)) + reply_len = -1; + } else if (os_strncmp(buf, "WPS_NFC_CONFIG_TOKEN ", 21) == 0) { + reply_len = hostapd_ctrl_iface_wps_nfc_config_token( + hapd, buf + 21, reply, reply_size); + } else if (os_strncmp(buf, "WPS_NFC_TOKEN ", 14) == 0) { + reply_len = hostapd_ctrl_iface_wps_nfc_token( + hapd, buf + 14, reply, reply_size); + } else if (os_strncmp(buf, "NFC_GET_HANDOVER_SEL ", 21) == 0) { + reply_len = hostapd_ctrl_iface_nfc_get_handover_sel( + hapd, buf + 21, reply, reply_size); + } else if (os_strncmp(buf, "NFC_REPORT_HANDOVER ", 20) == 0) { + if (hostapd_ctrl_iface_nfc_report_handover(hapd, buf + 20)) + reply_len = -1; +#endif /* CONFIG_WPS_NFC */ #endif /* CONFIG_WPS */ +#ifdef CONFIG_INTERWORKING + } else if (os_strncmp(buf, "SET_QOS_MAP_SET ", 16) == 0) { + if (hostapd_ctrl_iface_set_qos_map_set(hapd, buf + 16)) + reply_len = -1; + } else if (os_strncmp(buf, "SEND_QOS_MAP_CONF ", 18) == 0) { + if (hostapd_ctrl_iface_send_qos_map_conf(hapd, buf + 18)) + reply_len = -1; +#endif /* CONFIG_INTERWORKING */ +#ifdef CONFIG_WNM + } else if (os_strncmp(buf, "DISASSOC_IMMINENT ", 18) == 0) { + if (hostapd_ctrl_iface_disassoc_imminent(hapd, buf + 18)) + reply_len = -1; } else if (os_strncmp(buf, "ESS_DISASSOC ", 13) == 0) { if (hostapd_ctrl_iface_ess_disassoc(hapd, buf + 13)) reply_len = -1; +#endif /* CONFIG_WNM */ } else if (os_strcmp(buf, "GET_CONFIG") == 0) { reply_len = hostapd_ctrl_iface_get_config(hapd, reply, reply_size); @@ -942,6 +1335,26 @@ } else if (os_strncmp(buf, "GET ", 4) == 0) { reply_len = hostapd_ctrl_iface_get(hapd, buf + 4, reply, reply_size); + } else if (os_strncmp(buf, "ENABLE", 6) == 0) { + if (hostapd_ctrl_iface_enable(hapd->iface)) + reply_len = -1; + } else if (os_strncmp(buf, "RELOAD", 6) == 0) { + if (hostapd_ctrl_iface_reload(hapd->iface)) + reply_len = -1; + } else if (os_strncmp(buf, "DISABLE", 7) == 0) { + if (hostapd_ctrl_iface_disable(hapd->iface)) + reply_len = -1; +#ifdef CONFIG_TESTING_OPTIONS + } else if (os_strncmp(buf, "RADAR ", 6) == 0) { + if (hostapd_ctrl_iface_radar(hapd, buf + 6)) + reply_len = -1; + } else if (os_strncmp(buf, "MGMT_TX ", 8) == 0) { + if (hostapd_ctrl_iface_mgmt_tx(hapd, buf + 8)) + reply_len = -1; +#endif /* CONFIG_TESTING_OPTIONS */ + } else if (os_strncmp(buf, "CHAN_SWITCH ", 12) == 0) { + if (hostapd_ctrl_iface_chan_switch(hapd, buf + 12)) + reply_len = -1; } else { os_memcpy(reply, "UNKNOWN COMMAND\n", 16); reply_len = 16; @@ -977,7 +1390,7 @@ } -static void hostapd_ctrl_iface_msg_cb(void *ctx, int level, +static void hostapd_ctrl_iface_msg_cb(void *ctx, int level, int global, const char *txt, size_t len) { struct hostapd_data *hapd = ctx; @@ -993,7 +1406,10 @@ int s = -1; char *fname = NULL; - hapd->ctrl_sock = -1; + if (hapd->ctrl_sock > -1) { + wpa_printf(MSG_DEBUG, "ctrl_iface already exists!"); + return 0; + } if (hapd->conf->ctrl_interface == NULL) return 0; @@ -1009,12 +1425,35 @@ } if (hapd->conf->ctrl_interface_gid_set && - chown(hapd->conf->ctrl_interface, 0, + chown(hapd->conf->ctrl_interface, -1, hapd->conf->ctrl_interface_gid) < 0) { perror("chown[ctrl_interface]"); return -1; } + 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) { + perror("chown[ctrl_interface]"); + return -1; + } + +#ifdef ANDROID + /* + * Android is using umask 0077 which would leave the control interface + * directory without group access. This breaks things since Wi-Fi + * framework assumes that this directory can be accessed by other + * applications in the wifi group. Fix this by adding group access even + * if umask value would prevent this. + */ + if (chmod(hapd->conf->ctrl_interface, S_IRWXU | S_IRWXG) < 0) { + wpa_printf(MSG_ERROR, "CTRL: Could not chmod directory: %s", + strerror(errno)); + /* Try to continue anyway */ + } +#endif /* ANDROID */ + if (os_strlen(hapd->conf->ctrl_interface) + 1 + os_strlen(hapd->conf->iface) >= sizeof(addr.sun_path)) goto fail; @@ -1050,7 +1489,7 @@ } if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - perror("bind(PF_UNIX)"); + perror("hostapd-ctrl-iface: bind(PF_UNIX)"); goto fail; } wpa_printf(MSG_DEBUG, "Successfully replaced leftover " @@ -1067,7 +1506,14 @@ } if (hapd->conf->ctrl_interface_gid_set && - chown(fname, 0, hapd->conf->ctrl_interface_gid) < 0) { + chown(fname, -1, hapd->conf->ctrl_interface_gid) < 0) { + perror("chown[ctrl_interface/ifname]"); + goto fail; + } + + if (!hapd->conf->ctrl_interface_gid_set && + hapd->iface->interfaces->ctrl_iface_group && + chown(fname, -1, hapd->iface->interfaces->ctrl_iface_group) < 0) { perror("chown[ctrl_interface/ifname]"); goto fail; } @@ -1118,7 +1564,10 @@ "directory not empty - leaving it " "behind"); } else { - perror("rmdir[ctrl_interface]"); + wpa_printf(MSG_ERROR, + "rmdir[ctrl_interface=%s]: %s", + hapd->conf->ctrl_interface, + strerror(errno)); } } } @@ -1131,6 +1580,250 @@ } } + +static int hostapd_ctrl_iface_add(struct hapd_interfaces *interfaces, + char *buf) +{ + if (hostapd_add_iface(interfaces, buf) < 0) { + wpa_printf(MSG_ERROR, "Adding interface %s failed", buf); + return -1; + } + return 0; +} + + +static int hostapd_ctrl_iface_remove(struct hapd_interfaces *interfaces, + char *buf) +{ + if (hostapd_remove_iface(interfaces, buf) < 0) { + wpa_printf(MSG_ERROR, "Removing interface %s failed", buf); + return -1; + } + return 0; +} + + +static void hostapd_ctrl_iface_flush(struct hapd_interfaces *interfaces) +{ +#ifdef CONFIG_WPS_TESTING + wps_version_number = 0x20; + wps_testing_dummy_cred = 0; + wps_corrupt_pkhash = 0; +#endif /* CONFIG_WPS_TESTING */ +} + + +static void hostapd_global_ctrl_iface_receive(int sock, void *eloop_ctx, + void *sock_ctx) +{ + void *interfaces = eloop_ctx; + char buf[256]; + int res; + struct sockaddr_un from; + socklen_t fromlen = sizeof(from); + char reply[24]; + int reply_len; + + res = recvfrom(sock, buf, sizeof(buf) - 1, 0, + (struct sockaddr *) &from, &fromlen); + if (res < 0) { + perror("recvfrom(ctrl_iface)"); + return; + } + buf[res] = '\0'; + wpa_printf(MSG_DEBUG, "Global ctrl_iface command: %s", buf); + + os_memcpy(reply, "OK\n", 3); + reply_len = 3; + + if (os_strcmp(buf, "PING") == 0) { + os_memcpy(reply, "PONG\n", 5); + reply_len = 5; + } else if (os_strncmp(buf, "RELOG", 5) == 0) { + if (wpa_debug_reopen_file() < 0) + reply_len = -1; + } else if (os_strcmp(buf, "FLUSH") == 0) { + hostapd_ctrl_iface_flush(interfaces); + } else if (os_strncmp(buf, "ADD ", 4) == 0) { + if (hostapd_ctrl_iface_add(interfaces, buf + 4) < 0) + reply_len = -1; + } else if (os_strncmp(buf, "REMOVE ", 7) == 0) { + if (hostapd_ctrl_iface_remove(interfaces, buf + 7) < 0) + reply_len = -1; + } else { + wpa_printf(MSG_DEBUG, "Unrecognized global ctrl_iface command " + "ignored"); + reply_len = -1; + } + + if (reply_len < 0) { + os_memcpy(reply, "FAIL\n", 5); + reply_len = 5; + } + + sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from, fromlen); +} + + +static char * hostapd_global_ctrl_iface_path(struct hapd_interfaces *interface) +{ + char *buf; + size_t len; + + if (interface->global_iface_path == NULL) + return NULL; + + len = os_strlen(interface->global_iface_path) + + os_strlen(interface->global_iface_name) + 2; + buf = os_malloc(len); + if (buf == NULL) + return NULL; + + os_snprintf(buf, len, "%s/%s", interface->global_iface_path, + interface->global_iface_name); + buf[len - 1] = '\0'; + return buf; +} + + +int hostapd_global_ctrl_iface_init(struct hapd_interfaces *interface) +{ + struct sockaddr_un addr; + int s = -1; + char *fname = NULL; + + if (interface->global_iface_path == NULL) { + wpa_printf(MSG_DEBUG, "ctrl_iface not configured!"); + return 0; + } + + if (mkdir(interface->global_iface_path, S_IRWXU | S_IRWXG) < 0) { + if (errno == EEXIST) { + wpa_printf(MSG_DEBUG, "Using existing control " + "interface directory."); + } else { + perror("mkdir[ctrl_interface]"); + goto fail; + } + } else if (interface->ctrl_iface_group && + chown(interface->global_iface_path, -1, + interface->ctrl_iface_group) < 0) { + perror("chown[ctrl_interface]"); + goto fail; + } + + if (os_strlen(interface->global_iface_path) + 1 + + os_strlen(interface->global_iface_name) >= sizeof(addr.sun_path)) + goto fail; + + s = socket(PF_UNIX, SOCK_DGRAM, 0); + if (s < 0) { + perror("socket(PF_UNIX)"); + goto fail; + } + + os_memset(&addr, 0, sizeof(addr)); +#ifdef __FreeBSD__ + addr.sun_len = sizeof(addr); +#endif /* __FreeBSD__ */ + addr.sun_family = AF_UNIX; + fname = hostapd_global_ctrl_iface_path(interface); + if (fname == NULL) + goto fail; + os_strlcpy(addr.sun_path, fname, sizeof(addr.sun_path)); + if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + wpa_printf(MSG_DEBUG, "ctrl_iface bind(PF_UNIX) failed: %s", + strerror(errno)); + if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + wpa_printf(MSG_DEBUG, "ctrl_iface exists, but does not" + " allow connections - assuming it was left" + "over from forced program termination"); + if (unlink(fname) < 0) { + perror("unlink[ctrl_iface]"); + wpa_printf(MSG_ERROR, "Could not unlink " + "existing ctrl_iface socket '%s'", + fname); + goto fail; + } + if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < + 0) { + perror("bind(PF_UNIX)"); + goto fail; + } + wpa_printf(MSG_DEBUG, "Successfully replaced leftover " + "ctrl_iface socket '%s'", fname); + } else { + wpa_printf(MSG_INFO, "ctrl_iface exists and seems to " + "be in use - cannot override it"); + wpa_printf(MSG_INFO, "Delete '%s' manually if it is " + "not used anymore", fname); + os_free(fname); + fname = NULL; + goto fail; + } + } + + if (interface->ctrl_iface_group && + chown(fname, -1, interface->ctrl_iface_group) < 0) { + perror("chown[ctrl_interface]"); + goto fail; + } + + if (chmod(fname, S_IRWXU | S_IRWXG) < 0) { + perror("chmod[ctrl_interface/ifname]"); + goto fail; + } + os_free(fname); + + interface->global_ctrl_sock = s; + eloop_register_read_sock(s, hostapd_global_ctrl_iface_receive, + interface, NULL); + + return 0; + +fail: + if (s >= 0) + close(s); + if (fname) { + unlink(fname); + os_free(fname); + } + return -1; +} + + +void hostapd_global_ctrl_iface_deinit(struct hapd_interfaces *interfaces) +{ + char *fname = NULL; + + if (interfaces->global_ctrl_sock > -1) { + eloop_unregister_read_sock(interfaces->global_ctrl_sock); + close(interfaces->global_ctrl_sock); + interfaces->global_ctrl_sock = -1; + fname = hostapd_global_ctrl_iface_path(interfaces); + if (fname) { + unlink(fname); + os_free(fname); + } + + if (interfaces->global_iface_path && + rmdir(interfaces->global_iface_path) < 0) { + if (errno == ENOTEMPTY) { + wpa_printf(MSG_DEBUG, "Control interface " + "directory not empty - leaving it " + "behind"); + } else { + wpa_printf(MSG_ERROR, + "rmdir[ctrl_interface=%s]: %s", + interfaces->global_iface_path, + strerror(errno)); + } + } + os_free(interfaces->global_iface_path); + interfaces->global_iface_path = NULL; + } +} + static void hostapd_ctrl_iface_send(struct hostapd_data *hapd, int level, const char *buf, size_t len) diff -Nru wpa-1.0/hostapd/ctrl_iface.h wpa-2.1/hostapd/ctrl_iface.h --- wpa-1.0/hostapd/ctrl_iface.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/hostapd/ctrl_iface.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * hostapd / UNIX domain socket -based control interface * Copyright (c) 2004, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef CTRL_IFACE_H @@ -18,6 +12,8 @@ #ifndef CONFIG_NO_CTRL_IFACE int hostapd_ctrl_iface_init(struct hostapd_data *hapd); void hostapd_ctrl_iface_deinit(struct hostapd_data *hapd); +int hostapd_global_ctrl_iface_init(struct hapd_interfaces *interface); +void hostapd_global_ctrl_iface_deinit(struct hapd_interfaces *interface); #else /* CONFIG_NO_CTRL_IFACE */ static inline int hostapd_ctrl_iface_init(struct hostapd_data *hapd) { @@ -27,6 +23,17 @@ static inline void hostapd_ctrl_iface_deinit(struct hostapd_data *hapd) { } + +static inline int +hostapd_global_ctrl_iface_init(struct hapd_interfaces *interface) +{ + return 0; +} + +static inline void +hostapd_global_ctrl_iface_deinit(struct hapd_interfaces *interface) +{ +} #endif /* CONFIG_NO_CTRL_IFACE */ #endif /* CTRL_IFACE_H */ diff -Nru wpa-1.0/hostapd/defconfig wpa-2.1/hostapd/defconfig --- wpa-1.0/hostapd/defconfig 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/hostapd/defconfig 2014-02-04 11:23:35.000000000 +0000 @@ -22,6 +22,19 @@ # Driver interface for drivers using the nl80211 kernel interface CONFIG_DRIVER_NL80211=y +# driver_nl80211.c requires libnl. If you are compiling it yourself +# you may need to point hostapd to your version of libnl. +# +#CFLAGS += -I$ +#LIBS += -L$ + +# Use libnl v2.0 (or 3.0) libraries. +#CONFIG_LIBNL20=y + +# Use libnl 3.2 libraries (if this is selected, CONFIG_LIBNL20 is ignored) +#CONFIG_LIBNL32=y + + # Driver interface for FreeBSD net80211 layer (e.g., Atheros driver) #CONFIG_DRIVER_BSD=y #CFLAGS += -I/usr/local/include @@ -96,10 +109,9 @@ #CONFIG_EAP_GPSK_SHA256=y # EAP-FAST for the integrated EAP server -# Note: Default OpenSSL package does not include support for all the -# functionality needed for EAP-FAST. If EAP-FAST is enabled with OpenSSL, -# the OpenSSL library must be patched (openssl-0.9.9-session-ticket.patch) -# to add the needed functions. +# Note: If OpenSSL is used as the TLS library, OpenSSL 1.0 or newer is needed +# for EAP-FAST support. Older OpenSSL releases would need to be patched, e.g., +# with openssl-0.9.8x-tls-extensions.patch, to add the needed functions. #CONFIG_EAP_FAST=y # Wi-Fi Protected Setup (WPS) @@ -108,6 +120,8 @@ #CONFIG_WPS2=y # Enable UPnP support for external WPS Registrars #CONFIG_WPS_UPNP=y +# Enable WPS support with NFC config method +#CONFIG_WPS_NFC=y # EAP-IKEv2 #CONFIG_EAP_IKEV2=y @@ -115,6 +129,9 @@ # Trusted Network Connect (EAP-TNC) #CONFIG_EAP_TNC=y +# EAP-EKE for the integrated EAP server +#CONFIG_EAP_EKE=y + # PKCS#12 (PFX) support (used to read private key and certificate file from # a file that usually has extension .p12 or .pfx) CONFIG_PKCS12=y @@ -136,6 +153,13 @@ # IEEE 802.11n (High Throughput) support #CONFIG_IEEE80211N=y +# Wireless Network Management (IEEE Std 802.11v-2011) +# Note: This is experimental and not complete implementation. +#CONFIG_WNM=y + +# IEEE 802.11ac (Very High Throughput) support +#CONFIG_IEEE80211AC=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. @@ -145,6 +169,12 @@ # Disabled by default. #CONFIG_DEBUG_FILE=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 +# same file, e.g., using trace-cmd. +#CONFIG_DEBUG_LINUX_TRACING=y + # Remove support for RADIUS accounting #CONFIG_NO_ACCOUNTING=y @@ -158,7 +188,11 @@ # automatically create bridge and VLAN interfaces if necessary. #CONFIG_FULL_DYNAMIC_VLAN=y -# Remove support for dumping state into a file on SIGUSR1 signal +# Use netlink-based kernel API for VLAN operations instead of ioctl() +# Note: This requires libnl 3.1 or newer. +#CONFIG_VLAN_NETLINK=y + +# Remove support for dumping internal state through control interface commands # This can be used to reduce binary size at the cost of disabling a debugging # option. #CONFIG_NO_DUMP_STATE=y @@ -224,6 +258,10 @@ # are used. #CONFIG_TLSV11=y +# TLS-based EAP methods require at least TLS v1.0. Newer version of TLS (v1.2) +# can be enabled to enable use of stronger crypto algorithms. +#CONFIG_TLSV12=y + # 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 @@ -244,3 +282,41 @@ # This can be used to enable functionality to improve interworking with # external networks. #CONFIG_INTERWORKING=y + +# Hotspot 2.0 +#CONFIG_HS20=y + +# Enable SQLite database support in hlr_auc_gw, EAP-SIM DB, and eap_user_file +#CONFIG_SQLITE=y + +# Testing options +# This can be used to enable some testing options (see also the example +# configuration file) that are really useful only for testing clients that +# connect to this hostapd. These options allow, for example, to drop a +# certain percentage of probe requests or auth/(re)assoc frames. +# +#CONFIG_TESTING_OPTIONS=y + +# Automatic Channel Selection +# This will allow hostapd to pick the channel automatically when channel is set +# to "acs_survey" or "0". Eventually, other ACS algorithms can be added in +# similar way. +# +# Automatic selection is currently only done through initialization, later on +# we hope to do background checks to keep us moving to more ideal channels as +# time goes by. ACS is currently only supported through the nl80211 driver and +# your driver must have survey dump capability that is filled by the driver +# during scanning. +# +# You can customize the ACS survey algorithm with the hostapd.conf variable +# acs_num_scans. +# +# Supported ACS drivers: +# * ath9k +# * ath5k +# * ath10k +# +# For more details refer to: +# http://wireless.kernel.org/en/users/Documentation/acs +# +#CONFIG_ACS=y diff -Nru wpa-1.0/hostapd/dump_state.c wpa-2.1/hostapd/dump_state.c --- wpa-1.0/hostapd/dump_state.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/hostapd/dump_state.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,186 +0,0 @@ -/* - * hostapd / State dump - * Copyright (c) 2002-2009, Jouni Malinen - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#include "utils/includes.h" -#include - -#include "utils/common.h" -#include "radius/radius_client.h" -#include "radius/radius_server.h" -#include "eapol_auth/eapol_auth_sm.h" -#include "eapol_auth/eapol_auth_sm_i.h" -#include "eap_server/eap.h" -#include "ap/hostapd.h" -#include "ap/ap_config.h" -#include "ap/sta_info.h" -#include "dump_state.h" - - -static void fprint_char(FILE *f, char c) -{ - if (c >= 32 && c < 127) - fprintf(f, "%c", c); - else - fprintf(f, "<%02x>", c); -} - - -static void ieee802_1x_dump_state(FILE *f, const char *prefix, - struct sta_info *sta) -{ - struct eapol_state_machine *sm = sta->eapol_sm; - if (sm == NULL) - return; - - fprintf(f, "%sIEEE 802.1X:\n", prefix); - - if (sm->identity) { - size_t i; - fprintf(f, "%sidentity=", prefix); - for (i = 0; i < sm->identity_len; i++) - fprint_char(f, sm->identity[i]); - fprintf(f, "\n"); - } - - fprintf(f, "%slast EAP type: Authentication Server: %d (%s) " - "Supplicant: %d (%s)\n", prefix, - sm->eap_type_authsrv, - eap_server_get_name(0, sm->eap_type_authsrv), - sm->eap_type_supp, eap_server_get_name(0, sm->eap_type_supp)); - - fprintf(f, "%scached_packets=%s\n", prefix, - sm->last_recv_radius ? "[RX RADIUS]" : ""); - - eapol_auth_dump_state(f, prefix, sm); -} - - -/** - * hostapd_dump_state - SIGUSR1 handler to dump hostapd state to a text file - */ -static void hostapd_dump_state(struct hostapd_data *hapd) -{ - FILE *f; - time_t now; - struct sta_info *sta; - int i; -#ifndef CONFIG_NO_RADIUS - char *buf; -#endif /* CONFIG_NO_RADIUS */ - - if (!hapd->conf->dump_log_name) { - wpa_printf(MSG_DEBUG, "Dump file not defined - ignoring dump " - "request"); - return; - } - - wpa_printf(MSG_DEBUG, "Dumping hostapd state to '%s'", - hapd->conf->dump_log_name); - f = fopen(hapd->conf->dump_log_name, "w"); - if (f == NULL) { - wpa_printf(MSG_WARNING, "Could not open dump file '%s' for " - "writing.", hapd->conf->dump_log_name); - return; - } - - time(&now); - fprintf(f, "hostapd state dump - %s", ctime(&now)); - fprintf(f, "num_sta=%d num_sta_non_erp=%d " - "num_sta_no_short_slot_time=%d\n" - "num_sta_no_short_preamble=%d\n", - hapd->num_sta, hapd->iface->num_sta_non_erp, - hapd->iface->num_sta_no_short_slot_time, - hapd->iface->num_sta_no_short_preamble); - - for (sta = hapd->sta_list; sta != NULL; sta = sta->next) { - fprintf(f, "\nSTA=" MACSTR "\n", MAC2STR(sta->addr)); - - fprintf(f, - " AID=%d flags=0x%x %s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s" - "\n" - " capability=0x%x listen_interval=%d\n", - sta->aid, - sta->flags, - (sta->flags & WLAN_STA_AUTH ? "[AUTH]" : ""), - (sta->flags & WLAN_STA_ASSOC ? "[ASSOC]" : ""), - (sta->flags & WLAN_STA_PS ? "[PS]" : ""), - (sta->flags & WLAN_STA_TIM ? "[TIM]" : ""), - (sta->flags & WLAN_STA_PERM ? "[PERM]" : ""), - (ap_sta_is_authorized(sta) ? "[AUTHORIZED]" : ""), - (sta->flags & WLAN_STA_PENDING_POLL ? "[PENDING_POLL" : - ""), - (sta->flags & WLAN_STA_SHORT_PREAMBLE ? - "[SHORT_PREAMBLE]" : ""), - (sta->flags & WLAN_STA_PREAUTH ? "[PREAUTH]" : ""), - (sta->flags & WLAN_STA_WMM ? "[WMM]" : ""), - (sta->flags & WLAN_STA_MFP ? "[MFP]" : ""), - (sta->flags & WLAN_STA_WPS ? "[WPS]" : ""), - (sta->flags & WLAN_STA_MAYBE_WPS ? "[MAYBE_WPS]" : ""), - (sta->flags & WLAN_STA_WDS ? "[WDS]" : ""), - (sta->flags & WLAN_STA_NONERP ? "[NonERP]" : ""), - (sta->flags & WLAN_STA_WPS2 ? "[WPS2]" : ""), - sta->capability, - sta->listen_interval); - - fprintf(f, " supported_rates="); - for (i = 0; i < sta->supported_rates_len; i++) - fprintf(f, "%02x ", sta->supported_rates[i]); - fprintf(f, "\n"); - - fprintf(f, - " timeout_next=%s\n", - (sta->timeout_next == STA_NULLFUNC ? "NULLFUNC POLL" : - (sta->timeout_next == STA_DISASSOC ? "DISASSOC" : - "DEAUTH"))); - - ieee802_1x_dump_state(f, " ", sta); - } - -#ifndef CONFIG_NO_RADIUS - buf = os_malloc(4096); - if (buf) { - int count = radius_client_get_mib(hapd->radius, buf, 4096); - if (count < 0) - count = 0; - else if (count > 4095) - count = 4095; - buf[count] = '\0'; - fprintf(f, "%s", buf); - -#ifdef RADIUS_SERVER - count = radius_server_get_mib(hapd->radius_srv, buf, 4096); - if (count < 0) - count = 0; - else if (count > 4095) - count = 4095; - buf[count] = '\0'; - fprintf(f, "%s", buf); -#endif /* RADIUS_SERVER */ - - os_free(buf); - } -#endif /* CONFIG_NO_RADIUS */ - fclose(f); -} - - -int handle_dump_state_iface(struct hostapd_iface *iface, void *ctx) -{ - size_t i; - - for (i = 0; i < iface->num_bss; i++) - hostapd_dump_state(iface->bss[i]); - - return 0; -} diff -Nru wpa-1.0/hostapd/dump_state.h wpa-2.1/hostapd/dump_state.h --- wpa-1.0/hostapd/dump_state.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/hostapd/dump_state.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,20 +0,0 @@ -/* - * hostapd / State dump - * Copyright (c) 2002-2009, Jouni Malinen - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#ifndef DUMP_STATE_H -#define DUMP_STATE_H - -int handle_dump_state_iface(struct hostapd_iface *iface, void *ctx); - -#endif /* DUMP_STATE_H */ diff -Nru wpa-1.0/hostapd/eap_register.c wpa-2.1/hostapd/eap_register.c --- wpa-1.0/hostapd/eap_register.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/hostapd/eap_register.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * EAP method registration * Copyright (c) 2004-2009, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -45,6 +39,11 @@ ret = eap_server_tls_register(); #endif /* EAP_SERVER_TLS */ +#ifdef EAP_SERVER_UNAUTH_TLS + if (ret == 0) + ret = eap_server_unauth_tls_register(); +#endif /* EAP_SERVER_TLS */ + #ifdef EAP_SERVER_MSCHAPV2 if (ret == 0) ret = eap_server_mschapv2_register(); @@ -135,5 +134,10 @@ ret = eap_server_pwd_register(); #endif /* EAP_SERVER_PWD */ +#ifdef EAP_SERVER_EKE + if (ret == 0) + ret = eap_server_eke_register(); +#endif /* EAP_SERVER_EKE */ + return ret; } diff -Nru wpa-1.0/hostapd/eap_register.h wpa-2.1/hostapd/eap_register.h --- wpa-1.0/hostapd/eap_register.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/hostapd/eap_register.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * EAP method registration * Copyright (c) 2004-2009, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef EAP_REGISTER_H diff -Nru wpa-1.0/hostapd/hlr_auc_gw.c wpa-2.1/hostapd/hlr_auc_gw.c --- wpa-1.0/hostapd/hlr_auc_gw.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/hostapd/hlr_auc_gw.c 2014-02-04 11:23:35.000000000 +0000 @@ -1,15 +1,9 @@ /* * HLR/AuC testing gateway for hostapd EAP-SIM/AKA database/authenticator - * Copyright (c) 2005-2007, Jouni Malinen + * Copyright (c) 2005-2007, 2012-2013, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. * * This is an example implementation of the EAP-SIM/AKA database/authentication * gateway interface to HLR/AuC. It is expected to be replaced with an @@ -24,6 +18,9 @@ * SIM-REQ-AUTH * SIM-RESP-AUTH Kc1:SRES1:RAND1 Kc2:SRES2:RAND2 [Kc3:SRES3:RAND3] * SIM-RESP-AUTH FAILURE + * GSM-AUTH-REQ RAND1:RAND2[:RAND3] + * GSM-AUTH-RESP Kc1:SRES1:Kc2:SRES2[:Kc3:SRES3] + * GSM-AUTH-RESP FAILURE * * EAP-AKA / UMTS query/response: * AKA-REQ-AUTH @@ -36,15 +33,26 @@ * IMSI and max_chal are sent as an ASCII string, * Kc/SRES/RAND/AUTN/IK/CK/RES/AUTS as hex strings. * - * The example implementation here reads GSM authentication triplets from a + * An example implementation here reads GSM authentication triplets from a * text file in IMSI:Kc:SRES:RAND format, IMSI in ASCII, other fields as hex * strings. This is used to simulate an HLR/AuC. As such, it is not very useful * for real life authentication, but it is useful both as an example - * implementation and for EAP-SIM testing. + * implementation and for EAP-SIM/AKA/AKA' testing. + * + * For a stronger example design, Milenage and GSM-Milenage algorithms can be + * used to dynamically generate authenticatipn information for EAP-AKA/AKA' and + * EAP-SIM, respectively, if Ki is known. + * + * SQN generation follows the not time-based Profile 2 described in + * 3GPP TS 33.102 Annex C.3.2. The length of IND is 5 bits by default, but this + * can be changed with a command line options if needed. */ #include "includes.h" #include +#ifdef CONFIG_SQLITE +#include +#endif /* CONFIG_SQLITE */ #include "common.h" #include "crypto/milenage.h" @@ -53,6 +61,11 @@ static const char *default_socket_path = "/tmp/hlr_auc_gw.sock"; static const char *socket_path; static int serv_sock = -1; +static char *milenage_file = NULL; +static int update_milenage = 0; +static int sqn_changes = 0; +static int ind_len = 5; +static int stdout_debug = 1; /* GSM triplets */ struct gsm_triplet { @@ -73,6 +86,7 @@ u8 opc[16]; u8 amf[2]; u8 sqn[6]; + int set; }; static struct milenage_parameters *milenage_db = NULL; @@ -87,6 +101,147 @@ #define EAP_AKA_CK_LEN 16 +#ifdef CONFIG_SQLITE + +static sqlite3 *sqlite_db = NULL; +static struct milenage_parameters db_tmp_milenage; + + +static int db_table_exists(sqlite3 *db, const char *name) +{ + char cmd[128]; + os_snprintf(cmd, sizeof(cmd), "SELECT 1 FROM %s;", name); + return sqlite3_exec(db, cmd, NULL, NULL, NULL) == SQLITE_OK; +} + + +static int db_table_create_milenage(sqlite3 *db) +{ + char *err = NULL; + const char *sql = + "CREATE TABLE milenage(" + " imsi INTEGER PRIMARY KEY NOT NULL," + " ki CHAR(32) NOT NULL," + " opc CHAR(32) NOT NULL," + " amf CHAR(4) NOT NULL," + " sqn CHAR(12) NOT NULL" + ");"; + + printf("Adding database table for milenage information\n"); + if (sqlite3_exec(db, sql, NULL, NULL, &err) != SQLITE_OK) { + printf("SQLite error: %s\n", err); + sqlite3_free(err); + return -1; + } + + return 0; +} + + +static sqlite3 * db_open(const char *db_file) +{ + sqlite3 *db; + + if (sqlite3_open(db_file, &db)) { + printf("Failed to open database %s: %s\n", + db_file, sqlite3_errmsg(db)); + sqlite3_close(db); + return NULL; + } + + if (!db_table_exists(db, "milenage") && + db_table_create_milenage(db) < 0) { + sqlite3_close(db); + return NULL; + } + + return db; +} + + +static int get_milenage_cb(void *ctx, int argc, char *argv[], char *col[]) +{ + struct milenage_parameters *m = ctx; + int i; + + m->set = 1; + + for (i = 0; i < argc; i++) { + if (os_strcmp(col[i], "ki") == 0 && argv[i] && + hexstr2bin(argv[i], m->ki, sizeof(m->ki))) { + printf("Invalid ki value in database\n"); + return -1; + } + + if (os_strcmp(col[i], "opc") == 0 && argv[i] && + hexstr2bin(argv[i], m->opc, sizeof(m->opc))) { + printf("Invalid opcvalue in database\n"); + return -1; + } + + if (os_strcmp(col[i], "amf") == 0 && argv[i] && + hexstr2bin(argv[i], m->amf, sizeof(m->amf))) { + printf("Invalid amf value in database\n"); + return -1; + } + + if (os_strcmp(col[i], "sqn") == 0 && argv[i] && + hexstr2bin(argv[i], m->sqn, sizeof(m->sqn))) { + printf("Invalid sqn value in database\n"); + return -1; + } + } + + return 0; +} + + +static struct milenage_parameters * db_get_milenage(const char *imsi_txt) +{ + char cmd[128]; + unsigned long long imsi; + + os_memset(&db_tmp_milenage, 0, sizeof(db_tmp_milenage)); + imsi = atoll(imsi_txt); + os_snprintf(db_tmp_milenage.imsi, sizeof(db_tmp_milenage.imsi), + "%llu", imsi); + os_snprintf(cmd, sizeof(cmd), + "SELECT ki,opc,amf,sqn FROM milenage WHERE imsi=%llu;", + imsi); + if (sqlite3_exec(sqlite_db, cmd, get_milenage_cb, &db_tmp_milenage, + NULL) != SQLITE_OK) + return NULL; + + if (!db_tmp_milenage.set) + return NULL; + return &db_tmp_milenage; +} + + +static int db_update_milenage_sqn(struct milenage_parameters *m) +{ + char cmd[128], val[13], *pos; + + if (sqlite_db == NULL) + return 0; + + pos = val; + pos += wpa_snprintf_hex(pos, sizeof(val), m->sqn, 6); + *pos = '\0'; + os_snprintf(cmd, sizeof(cmd), + "UPDATE milenage SET sqn='%s' WHERE imsi=%s;", + val, m->imsi); + if (sqlite3_exec(sqlite_db, cmd, NULL, NULL, NULL) != SQLITE_OK) { + printf("Failed to update SQN in database for IMSI %s\n", + m->imsi); + return -1; + } + return 0; +} + +#endif /* CONFIG_SQLITE */ + + static int open_socket(const char *path) { struct sockaddr_un addr; @@ -102,7 +257,7 @@ addr.sun_family = AF_UNIX; os_strlcpy(addr.sun_path, path, sizeof(addr.sun_path)); if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - perror("bind(PF_UNIX)"); + perror("hlr-auc-gw: bind(PF_UNIX)"); close(s); return -1; } @@ -216,7 +371,7 @@ gsm_db = g; g = NULL; } - free(g); + os_free(g); fclose(f); @@ -366,7 +521,7 @@ milenage_db = m; m = NULL; } - free(m); + os_free(m); fclose(f); @@ -374,6 +529,80 @@ } +static void update_milenage_file(const char *fname) +{ + FILE *f, *f2; + char buf[500], *pos; + char *end = buf + sizeof(buf); + struct milenage_parameters *m; + size_t imsi_len; + + f = fopen(fname, "r"); + if (f == NULL) { + printf("Could not open Milenage data file '%s'\n", fname); + return; + } + + snprintf(buf, sizeof(buf), "%s.new", fname); + f2 = fopen(buf, "w"); + if (f2 == NULL) { + printf("Could not write Milenage data file '%s'\n", buf); + fclose(f); + return; + } + + while (fgets(buf, sizeof(buf), f)) { + /* IMSI Ki OPc AMF SQN */ + buf[sizeof(buf) - 1] = '\0'; + + pos = strchr(buf, ' '); + if (buf[0] == '#' || pos == NULL || pos - buf >= 20) + goto no_update; + + imsi_len = pos - buf; + + for (m = milenage_db; m; m = m->next) { + if (strncmp(buf, m->imsi, imsi_len) == 0 && + m->imsi[imsi_len] == '\0') + break; + } + + if (!m) + goto no_update; + + pos = buf; + pos += snprintf(pos, end - pos, "%s ", m->imsi); + pos += wpa_snprintf_hex(pos, end - pos, m->ki, 16); + *pos++ = ' '; + pos += wpa_snprintf_hex(pos, end - pos, m->opc, 16); + *pos++ = ' '; + pos += wpa_snprintf_hex(pos, end - pos, m->amf, 2); + *pos++ = ' '; + pos += wpa_snprintf_hex(pos, end - pos, m->sqn, 6); + *pos++ = '\n'; + + no_update: + fprintf(f2, "%s", buf); + } + + fclose(f2); + fclose(f); + + snprintf(buf, sizeof(buf), "%s.bak", fname); + if (rename(fname, buf) < 0) { + perror("rename"); + return; + } + + snprintf(buf, sizeof(buf), "%s.new", fname); + if (rename(buf, fname) < 0) { + perror("rename"); + return; + } + +} + + static struct milenage_parameters * get_milenage(const char *imsi) { struct milenage_parameters *m = milenage_db; @@ -384,35 +613,39 @@ m = m->next; } +#ifdef CONFIG_SQLITE + if (!m) + m = db_get_milenage(imsi); +#endif /* CONFIG_SQLITE */ + return m; } -static void sim_req_auth(int s, struct sockaddr_un *from, socklen_t fromlen, - char *imsi) +static int sim_req_auth(char *imsi, char *resp, size_t resp_len) { int count, max_chal, ret; char *pos; - char reply[1000], *rpos, *rend; + char *rpos, *rend; struct milenage_parameters *m; struct gsm_triplet *g; - reply[0] = '\0'; + resp[0] = '\0'; pos = strchr(imsi, ' '); if (pos) { *pos++ = '\0'; max_chal = atoi(pos); - if (max_chal < 1 || max_chal < EAP_SIM_MAX_CHAL) + if (max_chal < 1 || max_chal > EAP_SIM_MAX_CHAL) max_chal = EAP_SIM_MAX_CHAL; } else max_chal = EAP_SIM_MAX_CHAL; - rend = &reply[sizeof(reply)]; - rpos = reply; + rend = resp + resp_len; + rpos = resp; ret = snprintf(rpos, rend - rpos, "SIM-RESP-AUTH %s", imsi); if (ret < 0 || ret >= rend - rpos) - return; + return -1; rpos += ret; m = get_milenage(imsi); @@ -420,7 +653,7 @@ u8 _rand[16], sres[4], kc[8]; for (count = 0; count < max_chal; count++) { if (random_get_bytes(_rand, 16) < 0) - return; + return -1; gsm_milenage(m->opc, m->ki, _rand, sres, kc); *rpos++ = ' '; rpos += wpa_snprintf_hex(rpos, rend - rpos, kc, 8); @@ -430,7 +663,7 @@ rpos += wpa_snprintf_hex(rpos, rend - rpos, _rand, 16); } *rpos = '\0'; - goto send; + return 0; } count = 0; @@ -454,23 +687,90 @@ printf("No GSM triplets found for %s\n", imsi); ret = snprintf(rpos, rend - rpos, " FAILURE"); if (ret < 0 || ret >= rend - rpos) - return; + return -1; rpos += ret; } -send: - printf("Send: %s\n", reply); - if (sendto(s, reply, rpos - reply, 0, - (struct sockaddr *) from, fromlen) < 0) - perror("send"); + return 0; +} + + +static int gsm_auth_req(char *imsi, char *resp, size_t resp_len) +{ + int count, ret; + char *pos, *rpos, *rend; + struct milenage_parameters *m; + + resp[0] = '\0'; + + pos = os_strchr(imsi, ' '); + if (!pos) + return -1; + *pos++ = '\0'; + + rend = resp + resp_len; + rpos = resp; + ret = os_snprintf(rpos, rend - rpos, "GSM-AUTH-RESP %s", imsi); + if (ret < 0 || ret >= rend - rpos) + return -1; + rpos += ret; + + m = get_milenage(imsi); + if (m) { + u8 _rand[16], sres[4], kc[8]; + for (count = 0; count < EAP_SIM_MAX_CHAL; count++) { + if (hexstr2bin(pos, _rand, 16) != 0) + return -1; + gsm_milenage(m->opc, m->ki, _rand, sres, kc); + *rpos++ = count == 0 ? ' ' : ':'; + rpos += wpa_snprintf_hex(rpos, rend - rpos, kc, 8); + *rpos++ = ':'; + rpos += wpa_snprintf_hex(rpos, rend - rpos, sres, 4); + pos += 16 * 2; + if (*pos != ':') + break; + pos++; + } + *rpos = '\0'; + return 0; + } + + printf("No GSM triplets found for %s\n", imsi); + ret = os_snprintf(rpos, rend - rpos, " FAILURE"); + if (ret < 0 || ret >= rend - rpos) + return -1; + rpos += ret; + + return 0; } -static void aka_req_auth(int s, struct sockaddr_un *from, socklen_t fromlen, - char *imsi) +static void inc_sqn(u8 *sqn) +{ + u64 val, seq, ind; + + /* + * SQN = SEQ | IND = SEQ1 | SEQ2 | IND + * + * The mechanism used here is not time-based, so SEQ2 is void and + * SQN = SEQ1 | IND. The length of IND is ind_len bits and the length + * of SEQ1 is 48 - ind_len bits. + */ + + /* Increment both SEQ and IND by one */ + val = ((u64) WPA_GET_BE32(sqn) << 16) | ((u64) WPA_GET_BE16(sqn + 4)); + seq = (val >> ind_len) + 1; + ind = (val + 1) & ((1 << ind_len) - 1); + val = (seq << ind_len) | ind; + WPA_PUT_BE32(sqn, val >> 16); + WPA_PUT_BE16(sqn + 4, val & 0xffff); +} + + +static int aka_req_auth(char *imsi, char *resp, size_t resp_len) { /* AKA-RESP-AUTH */ - char reply[1000], *pos, *end; + char *pos, *end; u8 _rand[EAP_AKA_RAND_LEN]; u8 autn[EAP_AKA_AUTN_LEN]; u8 ik[EAP_AKA_IK_LEN]; @@ -479,16 +779,23 @@ size_t res_len; int ret; struct milenage_parameters *m; + int failed = 0; m = get_milenage(imsi); if (m) { if (random_get_bytes(_rand, EAP_AKA_RAND_LEN) < 0) - return; + return -1; res_len = EAP_AKA_RES_MAX_LEN; - inc_byte_array(m->sqn, 6); - printf("AKA: Milenage with SQN=%02x%02x%02x%02x%02x%02x\n", - m->sqn[0], m->sqn[1], m->sqn[2], - m->sqn[3], m->sqn[4], m->sqn[5]); + inc_sqn(m->sqn); +#ifdef CONFIG_SQLITE + db_update_milenage_sqn(m); +#endif /* CONFIG_SQLITE */ + sqn_changes = 1; + if (stdout_debug) { + printf("AKA: Milenage with SQN=%02x%02x%02x%02x%02x%02x\n", + m->sqn[0], m->sqn[1], m->sqn[2], + m->sqn[3], m->sqn[4], m->sqn[5]); + } milenage_generate(m->opc, m->amf, m->ki, m->sqn, _rand, autn, ik, ck, res, &res_len); } else { @@ -502,16 +809,23 @@ memset(res, '2', EAP_AKA_RES_MAX_LEN); res_len = EAP_AKA_RES_MAX_LEN; #else /* AKA_USE_FIXED_TEST_VALUES */ - return; + failed = 1; #endif /* AKA_USE_FIXED_TEST_VALUES */ } - pos = reply; - end = &reply[sizeof(reply)]; + pos = resp; + end = resp + resp_len; ret = snprintf(pos, end - pos, "AKA-RESP-AUTH %s ", imsi); if (ret < 0 || ret >= end - pos) - return; + return -1; pos += ret; + if (failed) { + ret = snprintf(pos, end - pos, "FAILURE"); + if (ret < 0 || ret >= end - pos) + return -1; + pos += ret; + return 0; + } pos += wpa_snprintf_hex(pos, end - pos, _rand, EAP_AKA_RAND_LEN); *pos++ = ' '; pos += wpa_snprintf_hex(pos, end - pos, autn, EAP_AKA_AUTN_LEN); @@ -522,60 +836,87 @@ *pos++ = ' '; pos += wpa_snprintf_hex(pos, end - pos, res, res_len); - printf("Send: %s\n", reply); - - if (sendto(s, reply, pos - reply, 0, (struct sockaddr *) from, - fromlen) < 0) - perror("send"); + return 0; } -static void aka_auts(int s, struct sockaddr_un *from, socklen_t fromlen, - char *imsi) +static int aka_auts(char *imsi, char *resp, size_t resp_len) { char *auts, *__rand; u8 _auts[EAP_AKA_AUTS_LEN], _rand[EAP_AKA_RAND_LEN], sqn[6]; struct milenage_parameters *m; + resp[0] = '\0'; + /* AKA-AUTS */ auts = strchr(imsi, ' '); if (auts == NULL) - return; + return -1; *auts++ = '\0'; __rand = strchr(auts, ' '); if (__rand == NULL) - return; + return -1; *__rand++ = '\0'; - printf("AKA-AUTS: IMSI=%s AUTS=%s RAND=%s\n", imsi, auts, __rand); + if (stdout_debug) { + printf("AKA-AUTS: IMSI=%s AUTS=%s RAND=%s\n", + imsi, auts, __rand); + } if (hexstr2bin(auts, _auts, EAP_AKA_AUTS_LEN) || hexstr2bin(__rand, _rand, EAP_AKA_RAND_LEN)) { printf("Could not parse AUTS/RAND\n"); - return; + return -1; } m = get_milenage(imsi); if (m == NULL) { printf("Unknown IMSI: %s\n", imsi); - return; + return -1; } if (milenage_auts(m->opc, m->ki, _rand, _auts, sqn)) { printf("AKA-AUTS: Incorrect MAC-S\n"); } else { memcpy(m->sqn, sqn, 6); - printf("AKA-AUTS: Re-synchronized: " - "SQN=%02x%02x%02x%02x%02x%02x\n", - sqn[0], sqn[1], sqn[2], sqn[3], sqn[4], sqn[5]); + if (stdout_debug) { + printf("AKA-AUTS: Re-synchronized: " + "SQN=%02x%02x%02x%02x%02x%02x\n", + sqn[0], sqn[1], sqn[2], sqn[3], sqn[4], sqn[5]); + } +#ifdef CONFIG_SQLITE + db_update_milenage_sqn(m); +#endif /* CONFIG_SQLITE */ + sqn_changes = 1; } + + return 0; +} + + +static int process_cmd(char *cmd, char *resp, size_t resp_len) +{ + if (os_strncmp(cmd, "SIM-REQ-AUTH ", 13) == 0) + return sim_req_auth(cmd + 13, resp, resp_len); + + if (os_strncmp(cmd, "GSM-AUTH-REQ ", 13) == 0) + return gsm_auth_req(cmd + 13, resp, resp_len); + + if (os_strncmp(cmd, "AKA-REQ-AUTH ", 13) == 0) + return aka_req_auth(cmd + 13, resp, resp_len); + + if (os_strncmp(cmd, "AKA-AUTS ", 9) == 0) + return aka_auts(cmd + 9, resp, resp_len); + + printf("Unknown request: %s\n", cmd); + return -1; } static int process(int s) { - char buf[1000]; + char buf[1000], resp[1000]; struct sockaddr_un from; socklen_t fromlen; ssize_t res; @@ -597,14 +938,21 @@ printf("Received: %s\n", buf); - if (strncmp(buf, "SIM-REQ-AUTH ", 13) == 0) - sim_req_auth(s, &from, fromlen, buf + 13); - else if (strncmp(buf, "AKA-REQ-AUTH ", 13) == 0) - aka_req_auth(s, &from, fromlen, buf + 13); - else if (strncmp(buf, "AKA-AUTS ", 9) == 0) - aka_auts(s, &from, fromlen, buf + 9); - else - printf("Unknown request: %s\n", buf); + if (process_cmd(buf, resp, sizeof(resp)) < 0) { + printf("Failed to process request\n"); + return -1; + } + + if (resp[0] == '\0') { + printf("No response\n"); + return 0; + } + + printf("Send: %s\n", resp); + + if (sendto(s, resp, os_strlen(resp), 0, (struct sockaddr *) &from, + fromlen) < 0) + perror("send"); return 0; } @@ -615,22 +963,34 @@ struct gsm_triplet *g, *gprev; struct milenage_parameters *m, *prev; + if (update_milenage && milenage_file && sqn_changes) + update_milenage_file(milenage_file); + g = gsm_db; while (g) { gprev = g; g = g->next; - free(gprev); + os_free(gprev); } m = milenage_db; while (m) { prev = m; m = m->next; - free(prev); + os_free(prev); } - close(serv_sock); - unlink(socket_path); + if (serv_sock >= 0) + close(serv_sock); + if (socket_path) + unlink(socket_path); + +#ifdef CONFIG_SQLITE + if (sqlite_db) { + sqlite3_close(sqlite_db); + sqlite_db = NULL; + } +#endif /* CONFIG_SQLITE */ } @@ -645,18 +1005,30 @@ { printf("HLR/AuC testing gateway for hostapd EAP-SIM/AKA " "database/authenticator\n" - "Copyright (c) 2005-2007, Jouni Malinen \n" + "Copyright (c) 2005-2007, 2012-2013, Jouni Malinen \n" "\n" "usage:\n" - "hlr_auc_gw [-h] [-s] [-g] " - "[-m]\n" + "hlr_auc_gw [-hu] [-s] [-g] " + "[-m] \\\n" + " [-D] [-i] [command]\n" "\n" "options:\n" " -h = show this usage help\n" + " -u = update SQN in Milenage file on exit\n" " -s = path for UNIX domain socket\n" " (default: %s)\n" " -g = path for GSM authentication triplets\n" - " -m = path for Milenage keys\n", + " -m = path for Milenage keys\n" + " -D = path to SQLite database\n" + " -i = IND length for SQN (default: 5)\n" + "\n" + "If the optional command argument, like " + "\"AKA-REQ-AUTH \" is used, a single\n" + "command is processed with response sent to stdout. Otherwise, " + "hlr_auc_gw opens\n" + "a control interface and processes commands sent through it " + "(e.g., by EAP server\n" + "in hostapd).\n", default_socket_path); } @@ -664,52 +1036,106 @@ int main(int argc, char *argv[]) { int c; - char *milenage_file = NULL; char *gsm_triplet_file = NULL; + char *sqlite_db_file = NULL; + int ret = 0; + + if (os_program_init()) + return -1; socket_path = default_socket_path; for (;;) { - c = getopt(argc, argv, "g:hm:s:"); + c = getopt(argc, argv, "D:g:hi:m:s:u"); if (c < 0) break; switch (c) { + case 'D': +#ifdef CONFIG_SQLITE + sqlite_db_file = optarg; + break; +#else /* CONFIG_SQLITE */ + printf("No SQLite support included in the build\n"); + return -1; +#endif /* CONFIG_SQLITE */ case 'g': gsm_triplet_file = optarg; break; case 'h': usage(); return 0; + case 'i': + ind_len = atoi(optarg); + if (ind_len < 0 || ind_len > 32) { + printf("Invalid IND length\n"); + return -1; + } + break; case 'm': milenage_file = optarg; break; case 's': socket_path = optarg; break; + case 'u': + update_milenage = 1; + break; default: usage(); return -1; } } + if (!gsm_triplet_file && !milenage_file && !sqlite_db_file) { + usage(); + return -1; + } + +#ifdef CONFIG_SQLITE + if (sqlite_db_file && (sqlite_db = db_open(sqlite_db_file)) == NULL) + return -1; +#endif /* CONFIG_SQLITE */ + if (gsm_triplet_file && read_gsm_triplets(gsm_triplet_file) < 0) return -1; if (milenage_file && read_milenage(milenage_file) < 0) return -1; - serv_sock = open_socket(socket_path); - if (serv_sock < 0) - return -1; + if (optind == argc) { + serv_sock = open_socket(socket_path); + if (serv_sock < 0) + return -1; - printf("Listening for requests on %s\n", socket_path); + printf("Listening for requests on %s\n", socket_path); - atexit(cleanup); - signal(SIGTERM, handle_term); - signal(SIGINT, handle_term); + atexit(cleanup); + signal(SIGTERM, handle_term); + signal(SIGINT, handle_term); - for (;;) - process(serv_sock); + for (;;) + process(serv_sock); + } else { + char buf[1000]; + socket_path = NULL; + stdout_debug = 0; + if (process_cmd(argv[optind], buf, sizeof(buf)) < 0) { + printf("FAIL\n"); + ret = -1; + } else { + printf("%s\n", buf); + } + cleanup(); + } - return 0; +#ifdef CONFIG_SQLITE + if (sqlite_db) { + sqlite3_close(sqlite_db); + sqlite_db = NULL; + } +#endif /* CONFIG_SQLITE */ + + os_program_deinit(); + + return ret; } diff -Nru wpa-1.0/hostapd/hlr_auc_gw.txt wpa-2.1/hostapd/hlr_auc_gw.txt --- wpa-1.0/hostapd/hlr_auc_gw.txt 1970-01-01 00:00:00.000000000 +0000 +++ wpa-2.1/hostapd/hlr_auc_gw.txt 2014-02-04 11:23:35.000000000 +0000 @@ -0,0 +1,104 @@ +HLR/AuC testing gateway for hostapd EAP-SIM/AKA database/authenticator + +hlr_auc_gw is an example implementation of the EAP-SIM/AKA/AKA' +database/authentication gateway interface to HLR/AuC. It could be +replaced with an implementation of SS7 gateway to GSM/UMTS +authentication center (HLR/AuC). hostapd will send SIM/AKA +authentication queries over a UNIX domain socket to and external +program, e.g., hlr_auc_gw. + +hlr_auc_gw can be configured with GSM and UMTS authentication data with +text files: GSM triplet file (see hostapd.sim_db) and Milenage file (see +hlr_auc_gw.milenage_db). Milenage parameters can be used to generate +dynamic authentication data for EAP-SIM, EAP-AKA, and EAP-AKA' while the +GSM triplet data is used for a more static configuration (e.g., triplets +extracted from a SIM card). + +Alternatively, hlr_auc_gw can be built with support for an SQLite +database for more dynamic operations. This is enabled by adding +"CONFIG_SQLITE=y" into hostapd/.config before building hlr_auc_gw ("make +clean; make hlr_auc_gw" in this directory). + +hostapd is configured to use hlr_auc_gw with the eap_sim_db parameter in +hostapd.conf (e.g., "eap_sim_db=unix:/tmp/hlr_auc_gw.sock"). hlr_auc_gw +is configured with command line parameters: + +hlr_auc_gw [-hu] [-s] [-g] [-m] \ + [-D] [-i] + +options: + -h = show this usage help + -u = update SQN in Milenage file on exit + -s = path for UNIX domain socket + (default: /tmp/hlr_auc_gw.sock) + -g = path for GSM authentication triplets + -m = path for Milenage keys + -D = path to SQLite database + -i = IND length for SQN (default: 5) + + +The SQLite database can be initialized with sqlite, e.g., by running +following commands in "sqlite3 /path/to/hlr_auc_gw.db": + +CREATE TABLE milenage( + imsi INTEGER PRIMARY KEY NOT NULL, + ki CHAR(32) NOT NULL, + opc CHAR(32) NOT NULL, + amf CHAR(4) NOT NULL, + sqn CHAR(12) NOT NULL +); +INSERT INTO milenage(imsi,ki,opc,amf,sqn) VALUES( + 232010000000000, + '90dca4eda45b53cf0f12d7c9c3bc6a89', + 'cb9cccc4b9258e6dca4760379fb82581', + '61df', + '000000000000' +); +INSERT INTO milenage(imsi,ki,opc,amf,sqn) VALUES( + 555444333222111, + '5122250214c33e723a5dd523fc145fc0', + '981d464c7c52eb6e5036234984ad0bcf', + 'c3ab', + '16f3b3f70fc1' +); + + +hostapd (EAP server) can also be configured to store the EAP-SIM/AKA +pseudonyms and reauth information into a SQLite database. This is +configured with the db parameter within the eap_sim_db configuration +option. + + +"hlr_auc_gw -D /path/to/hlr_auc_gw.db" can then be used to fetch +Milenage parameters based on IMSI from the database. The database can be +updated dynamically while hlr_auc_gw is running to add/remove/modify +entries. + + +Example configuration files for hostapd to operate as a RADIUS +authentication server for EAP-SIM/AKA/AKA': + +hostapd.conf: + +driver=none +radius_server_clients=hostapd.radius_clients +eap_server=1 +eap_user_file=hostapd.eap_user +eap_sim_db=unix:/tmp/hlr_auc_gw.sock db=/tmp/eap_sim.db +eap_sim_aka_result_ind=1 + +hostapd.radius_clients: + +0.0.0.0/0 radius + +hostapd.eap_user: + +"0"* AKA +"1"* SIM +"2"* AKA +"3"* SIM +"4"* AKA +"5"* SIM +"6"* AKA' +"7"* AKA' +"8"* AKA' diff -Nru wpa-1.0/hostapd/hostapd_cli.c wpa-2.1/hostapd/hostapd_cli.c --- wpa-1.0/hostapd/hostapd_cli.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/hostapd/hostapd_cli.c 2014-02-04 11:23:35.000000000 +0000 @@ -1,15 +1,9 @@ /* * hostapd - command line interface for hostapd daemon - * Copyright (c) 2004-2011, Jouni Malinen + * Copyright (c) 2004-2014, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -24,32 +18,15 @@ static const char *hostapd_cli_version = "hostapd_cli v" VERSION_STR "\n" -"Copyright (c) 2004-2012, Jouni Malinen and contributors"; +"Copyright (c) 2004-2014, Jouni Malinen and contributors"; static const char *hostapd_cli_license = -"This program is free software. You can distribute it and/or modify it\n" -"under the terms of the GNU General Public License version 2.\n" -"\n" -"Alternatively, this software may be distributed under the terms of the\n" -"BSD license. See README and COPYING for more details.\n"; +"This software may be distributed under the terms of the BSD license.\n" +"See README for more details.\n"; static const char *hostapd_cli_full_license = -"This program is free software; you can redistribute it and/or modify\n" -"it under the terms of the GNU General Public License version 2 as\n" -"published by the Free Software Foundation.\n" -"\n" -"This program is distributed in the hope that it will be useful,\n" -"but WITHOUT ANY WARRANTY; without even the implied warranty of\n" -"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" -"GNU General Public License for more details.\n" -"\n" -"You should have received a copy of the GNU General Public License\n" -"along with this program; if not, write to the Free Software\n" -"Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA\n" -"\n" -"Alternatively, this software may be distributed under the terms of the\n" -"BSD license.\n" +"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" @@ -94,11 +71,15 @@ " 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" -#ifdef CONFIG_WPS_OOB -" wps_oob use WPS with out-of-band (UFD)\n" -#endif /* CONFIG_WPS_OOB */ +" 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" @@ -110,7 +91,12 @@ static struct wpa_ctrl *ctrl_conn; static int hostapd_cli_quit = 0; static int hostapd_cli_attached = 0; -static const char *ctrl_iface_dir = "/var/run/hostapd"; + +#ifndef CONFIG_CTRL_IFACE_DIR +#define CONFIG_CTRL_IFACE_DIR "/var/run/hostapd" +#endif /* CONFIG_CTRL_IFACE_DIR */ +static const char *ctrl_iface_dir = CONFIG_CTRL_IFACE_DIR; + static char *ctrl_ifname = NULL; static const char *pid_file = NULL; static const char *action_file = NULL; @@ -230,8 +216,21 @@ } +static int hostapd_cli_cmd_status(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + if (argc > 0 && os_strcmp(argv[0], "driver") == 0) + return wpa_ctrl_command(ctrl, "STATUS-DRIVER"); + return wpa_ctrl_command(ctrl, "STATUS"); +} + + static int hostapd_cli_cmd_mib(struct wpa_ctrl *ctrl, int argc, char *argv[]) { + if (argc > 0) { + char buf[100]; + os_snprintf(buf, sizeof(buf), "MIB %s", argv[0]); + return wpa_ctrl_command(ctrl, buf); + } return wpa_ctrl_command(ctrl, "MIB"); } @@ -284,12 +283,15 @@ static int hostapd_cli_cmd_sta(struct wpa_ctrl *ctrl, int argc, char *argv[]) { char buf[64]; - if (argc != 1) { - printf("Invalid 'sta' command - exactly one argument, STA " + if (argc < 1) { + printf("Invalid 'sta' command - at least one argument, STA " "address, is required.\n"); return -1; } - snprintf(buf, sizeof(buf), "STA %s", argv[0]); + if (argc > 1) + snprintf(buf, sizeof(buf), "STA %s %s", argv[0], argv[1]); + else + snprintf(buf, sizeof(buf), "STA %s", argv[0]); return wpa_ctrl_command(ctrl, buf); } @@ -415,38 +417,105 @@ } -#ifdef CONFIG_WPS_OOB -static int hostapd_cli_cmd_wps_oob(struct wpa_ctrl *ctrl, int argc, - char *argv[]) +static int hostapd_cli_cmd_wps_cancel(struct wpa_ctrl *ctrl, int argc, + char *argv[]) { - char cmd[256]; + return wpa_ctrl_command(ctrl, "WPS_CANCEL"); +} + + +#ifdef CONFIG_WPS_NFC +static int hostapd_cli_cmd_wps_nfc_tag_read(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + int ret; + char *buf; + size_t buflen; + + if (argc != 1) { + printf("Invalid 'wps_nfc_tag_read' command - one argument " + "is required.\n"); + return -1; + } + + buflen = 18 + os_strlen(argv[0]); + buf = os_malloc(buflen); + if (buf == NULL) + return -1; + os_snprintf(buf, buflen, "WPS_NFC_TAG_READ %s", argv[0]); + + ret = wpa_ctrl_command(ctrl, buf); + os_free(buf); + + return ret; +} + + +static int hostapd_cli_cmd_wps_nfc_config_token(struct wpa_ctrl *ctrl, + int argc, char *argv[]) +{ + char cmd[64]; int res; - if (argc != 3 && argc != 4) { - printf("Invalid WPS_OOB command: need three or four " - "arguments:\n" - "- DEV_TYPE: use 'ufd' or 'nfc'\n" - "- PATH: path of OOB device like '/mnt'\n" - "- METHOD: OOB method 'pin-e' or 'pin-r', " - "'cred'\n" - "- DEV_NAME: (only for NFC) device name like " - "'pn531'\n"); + if (argc != 1) { + printf("Invalid 'wps_nfc_config_token' command - one argument " + "is required.\n"); return -1; } - if (argc == 3) - res = os_snprintf(cmd, sizeof(cmd), "WPS_OOB %s %s %s", - argv[0], argv[1], argv[2]); - else - res = os_snprintf(cmd, sizeof(cmd), "WPS_OOB %s %s %s %s", - argv[0], argv[1], argv[2], argv[3]); + res = os_snprintf(cmd, sizeof(cmd), "WPS_NFC_CONFIG_TOKEN %s", + argv[0]); if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { - printf("Too long WPS_OOB command.\n"); + printf("Too long WPS_NFC_CONFIG_TOKEN command.\n"); return -1; } return wpa_ctrl_command(ctrl, cmd); } -#endif /* CONFIG_WPS_OOB */ + + +static int hostapd_cli_cmd_wps_nfc_token(struct wpa_ctrl *ctrl, + int argc, char *argv[]) +{ + char cmd[64]; + int res; + + if (argc != 1) { + printf("Invalid 'wps_nfc_token' command - one argument is " + "required.\n"); + return -1; + } + + res = os_snprintf(cmd, sizeof(cmd), "WPS_NFC_TOKEN %s", argv[0]); + if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { + printf("Too long WPS_NFC_TOKEN command.\n"); + return -1; + } + return wpa_ctrl_command(ctrl, cmd); +} + + +static int hostapd_cli_cmd_nfc_get_handover_sel(struct wpa_ctrl *ctrl, + int argc, char *argv[]) +{ + char cmd[64]; + int res; + + if (argc != 2) { + printf("Invalid 'nfc_get_handover_sel' command - two arguments " + "are required.\n"); + return -1; + } + + res = os_snprintf(cmd, sizeof(cmd), "NFC_GET_HANDOVER_SEL %s %s", + argv[0], argv[1]); + if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { + printf("Too long NFC_GET_HANDOVER_SEL command.\n"); + return -1; + } + return wpa_ctrl_command(ctrl, cmd); +} + +#endif /* CONFIG_WPS_NFC */ static int hostapd_cli_cmd_wps_ap_pin(struct wpa_ctrl *ctrl, int argc, @@ -470,6 +539,13 @@ } +static int hostapd_cli_cmd_wps_get_status(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "WPS_GET_STATUS"); +} + + static int hostapd_cli_cmd_wps_config(struct wpa_ctrl *ctrl, int argc, char *argv[]) { @@ -515,19 +591,19 @@ #endif /* CONFIG_WPS */ -static int hostapd_cli_cmd_ess_disassoc(struct wpa_ctrl *ctrl, int argc, - char *argv[]) +static int hostapd_cli_cmd_disassoc_imminent(struct wpa_ctrl *ctrl, int argc, + char *argv[]) { char buf[300]; int res; if (argc < 2) { - printf("Invalid 'ess_disassoc' command - two arguments (STA " - "addr and URL) are needed\n"); + printf("Invalid 'disassoc_imminent' command - two arguments " + "(STA addr and Disassociation Timer) are needed\n"); return -1; } - res = os_snprintf(buf, sizeof(buf), "ESS_DISASSOC %s %s", + res = os_snprintf(buf, sizeof(buf), "DISASSOC_IMMINENT %s %s", argv[0], argv[1]); if (res < 0 || res >= (int) sizeof(buf)) return -1; @@ -535,6 +611,26 @@ } +static int hostapd_cli_cmd_ess_disassoc(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char buf[300]; + int res; + + if (argc < 3) { + printf("Invalid 'ess_disassoc' command - three arguments (STA " + "addr, disassoc timer, and URL) are needed\n"); + return -1; + } + + res = os_snprintf(buf, sizeof(buf), "ESS_DISASSOC %s %s %s", + argv[0], argv[1], argv[2]); + if (res < 0 || res >= (int) sizeof(buf)) + return -1; + return wpa_ctrl_command(ctrl, buf); +} + + static int hostapd_cli_cmd_get_config(struct wpa_ctrl *ctrl, int argc, char *argv[]) { @@ -608,6 +704,45 @@ } +static int hostapd_cli_cmd_set_qos_map_set(struct wpa_ctrl *ctrl, + int argc, char *argv[]) +{ + char buf[200]; + int res; + + if (argc != 1) { + printf("Invalid 'set_qos_map_set' command - " + "one argument (comma delimited QoS map set) " + "is needed\n"); + return -1; + } + + res = os_snprintf(buf, sizeof(buf), "SET_QOS_MAP_SET %s", argv[0]); + if (res < 0 || res >= (int) sizeof(buf)) + return -1; + return wpa_ctrl_command(ctrl, buf); +} + + +static int hostapd_cli_cmd_send_qos_map_conf(struct wpa_ctrl *ctrl, + int argc, char *argv[]) +{ + char buf[50]; + int res; + + if (argc != 1) { + printf("Invalid 'send_qos_map_conf' command - " + "one argument (STA addr) is needed\n"); + return -1; + } + + res = os_snprintf(buf, sizeof(buf), "SEND_QOS_MAP_CONF %s", argv[0]); + if (res < 0 || res >= (int) sizeof(buf)) + return -1; + return wpa_ctrl_command(ctrl, buf); +} + + static int hostapd_cli_cmd_quit(struct wpa_ctrl *ctrl, int argc, char *argv[]) { hostapd_cli_quit = 1; @@ -721,6 +856,45 @@ } +static int hostapd_cli_cmd_chan_switch(struct wpa_ctrl *ctrl, + int argc, char *argv[]) +{ + char cmd[256]; + int res; + int i; + char *tmp; + int total; + + if (argc < 2) { + printf("Invalid chan_switch command: needs at least two " + "arguments (count and freq)\n" + "usage: [sec_channel_offset=] " + "[center_freq1=] [center_freq2=] [bandwidth=] " + "[blocktx] [ht|vht]\n"); + return -1; + } + + res = os_snprintf(cmd, sizeof(cmd), "CHAN_SWITCH %s %s", + argv[0], argv[1]); + if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { + printf("Too long CHAN_SWITCH command.\n"); + return -1; + } + + total = res; + for (i = 2; i < argc; i++) { + tmp = cmd + total; + res = os_snprintf(tmp, sizeof(cmd) - total, " %s", argv[i]); + if (res < 0 || (size_t) res >= sizeof(cmd) - total - 1) { + printf("Too long CHAN_SWITCH command.\n"); + return -1; + } + total += res; + } + return wpa_ctrl_command(ctrl, cmd); +} + + struct hostapd_cli_cmd { const char *cmd; int (*handler)(struct wpa_ctrl *ctrl, int argc, char *argv[]); @@ -730,6 +904,7 @@ { "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 }, @@ -742,12 +917,18 @@ { "wps_pin", hostapd_cli_cmd_wps_pin }, { "wps_check_pin", hostapd_cli_cmd_wps_check_pin }, { "wps_pbc", hostapd_cli_cmd_wps_pbc }, -#ifdef CONFIG_WPS_OOB - { "wps_oob", hostapd_cli_cmd_wps_oob }, -#endif /* CONFIG_WPS_OOB */ + { "wps_cancel", hostapd_cli_cmd_wps_cancel }, +#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 }, +#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 }, #endif /* CONFIG_WPS */ + { "disassoc_imminent", hostapd_cli_cmd_disassoc_imminent }, { "ess_disassoc", hostapd_cli_cmd_ess_disassoc }, { "get_config", hostapd_cli_cmd_get_config }, { "help", hostapd_cli_cmd_help }, @@ -757,6 +938,9 @@ { "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 }, { NULL, NULL } }; @@ -911,7 +1095,7 @@ 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, NULL, NULL, NULL); eloop_register_timeout(ping_interval, 0, hostapd_cli_ping, NULL, NULL); eloop_run(); diff -Nru wpa-1.0/hostapd/hostapd.conf wpa-2.1/hostapd/hostapd.conf --- wpa-1.0/hostapd/hostapd.conf 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/hostapd/hostapd.conf 2014-02-04 11:23:35.000000000 +0000 @@ -51,9 +51,6 @@ logger_stdout=-1 logger_stdout_level=2 -# Dump file for state information (on SIGUSR1) -dump_file=/tmp/hostapd.dump - # Interface for separate control program. If this is specified, hostapd # will create this directory and a UNIX domain socket for listening to requests # from external programs (CLI/GUI, etc.) for status information and @@ -84,6 +81,14 @@ # SSID to be used in IEEE 802.11 management frames ssid=test +# Alternative formats for configuring SSID +# (double quoted string, hexdump, printf-escaped string) +#ssid2="test" +#ssid2=74657374 +#ssid2=P"hello\nthere" + +# UTF-8 SSID: Whether the SSID is to be interpreted using UTF-8 encoding +#utf8_ssid=1 # Country code (ISO/IEC 3166-1). Used to set regulatory domain. # Set as needed to indicate country in which device is operating. @@ -97,7 +102,15 @@ # (default: 0 = disabled) #ieee80211d=1 +# Enable IEEE 802.11h. This enables radar detection and DFS support if +# available. DFS support is required on outdoor 5 GHz channels in most countries +# of the world. This can be used only with ieee80211d=1. +# (default: 0 = disabled) +#ieee80211h=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) # Default: IEEE 802.11b hw_mode=g @@ -105,8 +118,28 @@ # (default: 0, i.e., not set) # Please note that some drivers do not use this value from hostapd and the # channel will need to be configured separately with iwconfig. +# +# If CONFIG_ACS build option is enabled, the channel can be selected +# automatically at run time by setting channel=acs_survey or channel=0, both of +# which will enable the ACS survey based algorithm. channel=1 +# ACS tuning - Automatic Channel Selection +# See: http://wireless.kernel.org/en/users/Documentation/acs +# +# You can customize the ACS survey algorithm with following variables: +# +# acs_num_scans requirement is 1..100 - number of scans to be performed that +# are used to trigger survey data gathering of an underlying device driver. +# Scans are passive and typically take a little over 100ms (depending on the +# driver) on each available channel for given hw_mode. Increasing this value +# means sacrificing startup time and gathering more data wrt channel +# interference that may help choosing a better channel. This can also help fine +# tune the ACS scan time in case a driver has different scan dwell times. +# +# Defaults: +#acs_num_scans=5 + # Beacon interval in kus (1.024 ms) (default: 100; range 15..65535) beacon_int=100 @@ -196,6 +229,13 @@ # requests for broadcast SSID ignore_broadcast_ssid=0 +# Additional vendor specfic 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 +# one or more elements) +#vendor_elements=dd0411223301 + # TX queue parameters (EDCF / bursting) # tx_queue__ # queues: data0, data1, data2, data3, after_beacon, beacon @@ -339,6 +379,12 @@ # the STA with a data frame. # default: 300 (i.e., 5 minutes) #ap_max_inactivity=300 +# +# The inactivity polling can be disabled to disconnect stations based on +# inactivity timeout so that idle stations are more likely to be disconnected +# even if they are still in range of the AP. This can be done by setting +# skip_inactivity_poll to 1 (default 0). +#skip_inactivity_poll=0 # Disassociate stations based on excessive transmission failures or other # indications of connection loss. This depends on the driver capabilities and @@ -360,10 +406,19 @@ # use a separate bridge. #wds_bridge=wds-br0 +# Start the AP with beaconing disabled by default. +#start_disabled=0 + # Client isolation can be used to prevent low-level bridging of frames between # associated stations in the BSS. By default, this bridging is allowed. #ap_isolate=1 +# 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 +# :: +#bss_load_test=12:80:20000 + ##### IEEE 802.11n related configuration ###################################### # ieee80211n: Whether IEEE 802.11n (HT) is enabled @@ -410,6 +465,164 @@ # Require stations to support HT PHY (reject association if they do not) #require_ht=1 +# If set non-zero, require stations to perform scans of overlapping +# channels to test for stations which would be affected by 40 MHz traffic. +# This parameter sets the interval in seconds between these scans. This +# is useful only for testing that stations properly set the OBSS interval, +# since the other parameters in the OBSS scan parameters IE are set to 0. +#obss_interval=0 + +##### IEEE 802.11ac related configuration ##################################### + +# ieee80211ac: Whether IEEE 802.11ac (VHT) is enabled +# 0 = disabled (default) +# 1 = enabled +# Note: You will also need to enable WMM for full VHT functionality. +#ieee80211ac=1 + +# vht_capab: VHT capabilities (list of flags) +# +# vht_max_mpdu_len: [MAX-MPDU-7991] [MAX-MPDU-11454] +# Indicates maximum MPDU length +# 0 = 3895 octets (default) +# 1 = 7991 octets +# 2 = 11454 octets +# 3 = reserved +# +# supported_chan_width: [VHT160] [VHT160-80PLUS80] +# Indicates supported Channel widths +# 0 = 160 MHz & 80+80 channel widths are not supported (default) +# 1 = 160 MHz channel width is supported +# 2 = 160 MHz & 80+80 channel widths are supported +# 3 = reserved +# +# Rx LDPC coding capability: [RXLDPC] +# Indicates support for receiving LDPC coded pkts +# 0 = Not supported (default) +# 1 = Supported +# +# Short GI for 80 MHz: [SHORT-GI-80] +# Indicates short GI support for reception of packets transmitted with TXVECTOR +# params format equal to VHT and CBW = 80Mhz +# 0 = Not supported (default) +# 1 = Supported +# +# Short GI for 160 MHz: [SHORT-GI-160] +# Indicates short GI support for reception of packets transmitted with TXVECTOR +# params format equal to VHT and CBW = 160Mhz +# 0 = Not supported (default) +# 1 = Supported +# +# Tx STBC: [TX-STBC-2BY1] +# Indicates support for the transmission of at least 2x1 STBC +# 0 = Not supported (default) +# 1 = Supported +# +# Rx STBC: [RX-STBC-1] [RX-STBC-12] [RX-STBC-123] [RX-STBC-1234] +# Indicates support for the reception of PPDUs using STBC +# 0 = Not supported (default) +# 1 = support of one spatial stream +# 2 = support of one and two spatial streams +# 3 = support of one, two and three spatial streams +# 4 = support of one, two, three and four spatial streams +# 5,6,7 = reserved +# +# SU Beamformer Capable: [SU-BEAMFORMER] +# Indicates support for operation as a single user beamformer +# 0 = Not supported (default) +# 1 = Supported +# +# SU Beamformee Capable: [SU-BEAMFORMEE] +# Indicates support for operation as a single user beamformee +# 0 = Not supported (default) +# 1 = Supported +# +# Compressed Steering Number of Beamformer Antennas Supported: [BF-ANTENNA-2] +# Beamformee's capability indicating the maximum number of beamformer +# antennas the beamformee can support when sending compressed beamforming +# feedback +# If SU beamformer capable, set to maximum value minus 1 +# else reserved (default) +# +# Number of Sounding Dimensions: [SOUNDING-DIMENSION-2] +# Beamformer's capability indicating the maximum value of the NUM_STS parameter +# in the TXVECTOR of a VHT NDP +# If SU beamformer capable, set to maximum value minus 1 +# else reserved (default) +# +# MU Beamformer Capable: [MU-BEAMFORMER] +# Indicates support for operation as an MU beamformer +# 0 = Not supported or sent by Non-AP STA (default) +# 1 = Supported +# +# MU Beamformee Capable: [MU-BEAMFORMEE] +# Indicates support for operation as an MU beamformee +# 0 = Not supported or sent by AP (default) +# 1 = Supported +# +# 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 +# mode +# 1 = VHT AP supports VHT TXOP PS mode (OR) VHT Sta is in VHT TXOP power save +# mode +# +# +HTC-VHT Capable: [HTC-VHT] +# Indicates whether or not the STA supports receiving a VHT variant HT Control +# field. +# 0 = Not supported (default) +# 1 = supported +# +# Maximum A-MPDU Length Exponent: [MAX-A-MPDU-LEN-EXP0]..[MAX-A-MPDU-LEN-EXP7] +# Indicates the maximum length of A-MPDU pre-EOF padding that the STA can recv +# This field is an integer in the range of 0 to 7. +# The length defined by this field is equal to +# 2 pow(13 + Maximum A-MPDU Length Exponent) -1 octets +# +# VHT Link Adaptation Capable: [VHT-LINK-ADAPT2] [VHT-LINK-ADAPT3] +# Indicates whether or not the STA supports link adaptation using VHT variant +# HT Control field +# If +HTC-VHTcapable is 1 +# 0 = (no feedback) if the STA does not provide VHT MFB (default) +# 1 = reserved +# 2 = (Unsolicited) if the STA provides only unsolicited VHT MFB +# 3 = (Both) if the STA can provide VHT MFB in response to VHT MRQ and if the +# STA provides unsolicited VHT MFB +# Reserved if +HTC-VHTcapable is 0 +# +# Rx Antenna Pattern Consistency: [RX-ANTENNA-PATTERN] +# Indicates the possibility of Rx antenna pattern change +# 0 = Rx antenna pattern might change during the lifetime of an association +# 1 = Rx antenna pattern does not change during the lifetime of an association +# +# Tx Antenna Pattern Consistency: [TX-ANTENNA-PATTERN] +# Indicates the possibility of Tx antenna pattern change +# 0 = Tx antenna pattern might change during the lifetime of an association +# 1 = Tx antenna pattern does not change during the lifetime of an association +#vht_capab=[SHORT-GI-80][HTC-VHT] +# +# Require stations to support VHT PHY (reject association if they do not) +#require_vht=1 + +# 0 = 20 or 40 MHz operating Channel width +# 1 = 80 MHz channel width +# 2 = 160 MHz channel width +# 3 = 80+80 MHz channel width +#vht_oper_chwidth=1 +# +# center freq = 5 GHz + (5 * index) +# So index 42 gives center freq 5.210 GHz +# which is channel 42 in 5G band +# +#vht_oper_centr_freq_seg0_idx=42 +# +# center freq = 5 GHz + (5 * index) +# So index 159 gives center freq 5.795 GHz +# which is channel 159 in 5G band +# +#vht_oper_centr_freq_seg1_idx=159 + ##### IEEE 802.1X-2004 related configuration ################################## # Require IEEE 802.1X authorization @@ -466,6 +679,8 @@ eap_server=0 # Path for EAP server user database +# If SQLite support is included, this can be set to "sqlite:/path/to/sqlite.db" +# to use SQLite database instead of a text file. #eap_user_file=/etc/hostapd.eap_user # CA certificate (PEM or DER file) for EAP-TLS/PEAP/TTLS @@ -484,6 +699,11 @@ # Passphrase for private key #private_key_passwd=secret passphrase +# Server identity +# EAP methods that provide mechanism for authenticated server identity delivery +# use this value. If not set, "hostapd" is used as a default. +#server_id=server.example.com + # Enable CRL verification. # Note: hostapd does not yet support CRL downloading based on CDP. Thus, a # valid CRL signed by the CA is required to be included in the ca_cert file. @@ -495,6 +715,20 @@ # 2 = check all CRLs in the certificate path #check_crl=1 +# 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. +# This cache file can be updated, e.g., by running following command +# periodically to get an update from the OCSP responder: +# openssl ocsp \ +# -no_nonce \ +# -CAfile /etc/hostapd.ca.pem \ +# -issuer /etc/hostapd.ca.pem \ +# -cert /etc/hostapd.server.pem \ +# -url http://ocsp.example.com:8888/ \ +# -respout /tmp/ocsp-cache.der +#ocsp_stapling_response=/tmp/ocsp-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 @@ -510,12 +744,18 @@ # Fragment size for EAP methods #fragment_size=1400 +# Finite cyclic group for EAP-pwd. Number maps to group of domain parameters +# using the IANA repository for IKE (RFC 2409). +#pwd_group=19 + # Configuration data for EAP-SIM database/authentication gateway interface. # This is a text string in implementation specific format. The example # implementation in eap_sim_db.c uses this as the UNIX domain socket name for # the HLR/AuC gateway (e.g., hlr_auc_gw). In this case, the path uses "unix:" -# prefix. +# prefix. If hostapd is built with SQLite support (CONFIG_SQLITE=y in .config), +# database file can be described with an optional db= parameter. #eap_sim_db=unix:/tmp/hlr_auc_gw.sock +#eap_sim_db=unix:/tmp/hlr_auc_gw.sock db=/tmp/hostapd.db # 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 @@ -622,13 +862,18 @@ # 60 (1 minute). #radius_acct_interim_interval=600 +# Request Chargeable-User-Identity (RFC 4372) +# This parameter can be used to configure hostapd to request CUI from the +# RADIUS server by including Chargeable-User-Identity attribute into +# Access-Request packets. +#radius_request_cui=1 + # Dynamic VLAN mode; allow RADIUS authentication server to decide which VLAN # is used for the stations. This information is parsed from following RADIUS # attributes based on RFC 3580 and RFC 2868: Tunnel-Type (value 13 = VLAN), # Tunnel-Medium-Type (value 6 = IEEE 802), Tunnel-Private-Group-ID (value -# VLANID as a string). vlan_file option below must be configured if dynamic -# VLANs are used. Optionally, the local MAC ACL list (accept_mac_file) can be -# used to set static client MAC address to VLAN ID mapping. +# 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 # 2 = required; reject authentication if RADIUS server does not include VLAN ID @@ -640,6 +885,8 @@ # multiple BSSIDs or SSIDs. Each line in this text file is defining a new # interface and the line must include VLAN ID and interface name separated by # white space (space or tab). +# If no entries are provided by this file, the station is statically mapped +# to . interfaces. #vlan_file=/etc/hostapd.vlan # Interface where 802.1q tagged packets should appear when a RADIUS server is @@ -649,6 +896,67 @@ # to the bridge. #vlan_tagged_interface=eth0 +# Bridge (prefix) to add the wifi and the tagged interface to. This gets the +# VLAN ID appended. It defaults to brvlan%d if no tagged interface is given +# and br%s.%d if a tagged interface is given, provided %s = tagged interface +# and %d = VLAN ID. +#vlan_bridge=brvlan + +# When hostapd creates a VLAN interface on vlan_tagged_interfaces, it needs +# to know how to name it. +# 0 = vlan, e.g., vlan1 +# 1 = ., e.g. eth0.1 +#vlan_naming=0 + +# Arbitrary RADIUS attributes can be added into Access-Request and +# Accounting-Request packets by specifying the contents of the attributes with +# the following configuration parameters. There can be multiple of these to +# add multiple attributes. These parameters can also be used to override some +# of the attributes added automatically by hostapd. +# Format: [:] +# attr_id: RADIUS attribute type (e.g., 26 = Vendor-Specific) +# syntax: s = string (UTF-8), d = integer, x = octet string +# value: attribute value in format indicated by the syntax +# If syntax and value parts are omitted, a null value (single 0x00 octet) is +# used. +# +# Additional Access-Request attributes +# radius_auth_req_attr=[:] +# Examples: +# Operator-Name = "Operator" +#radius_auth_req_attr=126:s:Operator +# Service-Type = Framed (2) +#radius_auth_req_attr=6:d:2 +# Connect-Info = "testing" (this overrides the automatically generated value) +#radius_auth_req_attr=77:s:testing +# Same Connect-Info value set as a hexdump +#radius_auth_req_attr=77:x:74657374696e67 + +# +# Additional Accounting-Request attributes +# radius_acct_req_attr=[:] +# Examples: +# Operator-Name = "Operator" +#radius_acct_req_attr=126:s:Operator + +# Dynamic Authorization Extensions (RFC 5176) +# This mechanism can be used to allow dynamic changes to user session based on +# commands from a RADIUS server (or some other disconnect client that has the +# needed session information). For example, Disconnect message can be used to +# request an associated station to be disconnected. +# +# This is disabled by default. Set radius_das_port to non-zero UDP port +# number to enable. +#radius_das_port=3799 +# +# DAS client (the host that can send Disconnect/CoA requests) and shared secret +#radius_das_client=192.168.1.123 shared secret here +# +# DAS Event-Timestamp time window in seconds +#radius_das_time_window=300 +# +# DAS require Event-Timestamp +#radius_das_require_event_timestamp=1 ##### RADIUS authentication server configuration ############################## @@ -672,6 +980,7 @@ # Enable WPA. Setting this variable configures the AP to require WPA (either # WPA-PSK or WPA-RADIUS/EAP based on other configuration). For WPA-PSK, either # wpa_psk or wpa_passphrase must be set and wpa_key_mgmt must include WPA-PSK. +# Instead of wpa_psk / wpa_passphrase, wpa_psk_radius might suffice. # For WPA-RADIUS/EAP, ieee8021x must be set (but without dynamic WEP keys), # RADIUS authentication server must be configured, and WPA-EAP must be included # in wpa_key_mgmt. @@ -696,6 +1005,15 @@ # configuration reloads. #wpa_psk_file=/etc/hostapd.wpa_psk +# Optionally, WPA passphrase can be received from RADIUS authentication server +# This requires macaddr_acl to be set to 2 (RADIUS) +# 0 = disabled (default) +# 1 = optional; use default passphrase/psk if RADIUS server does not include +# Tunnel-Password +# 2 = required; reject authentication if RADIUS server does not include +# Tunnel-Password +#wpa_psk_radius=0 + # 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. @@ -784,6 +1102,19 @@ # 1 = enabled #okc=1 +# 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 + +# 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 +# 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 ##### IEEE 802.11r configuration ############################################## @@ -858,6 +1189,14 @@ # 2 = WPS enabled, configured #wps_state=2 +# Whether to manage this interface independently from other WPS interfaces +# By default, a single hostapd process applies WPS operations to all configured +# interfaces. This parameter can be used to disable that behavior for a subset +# of interfaces. If this is set to non-zero for an interface, WPS commands +# issued on that interface do not apply to other interfaces and WPS operations +# performed on other interfaces do not affect this interface. +#wps_independent=0 + # AP can be configured into a locked state where new WPS Registrar are not # accepted, but previously authorized Registrars (including the internal one) # can continue to add new Enrollees. @@ -1007,6 +1346,24 @@ # 12-digit, all-numeric code that identifies the consumer package. #upc=123456789012 +# WPS RF Bands (a = 5G, b = 2.4G, g = 2.4G, ag = dual band) +# This value should be set according to RF band(s) supported by the AP if +# hw_mode is not set. For dual band dual concurrent devices, this needs to be +# set to ag to allow both RF bands to be advertized. +#wps_rf_bands=ag + +# NFC password token for WPS +# These parameters can be used to configure a fixed NFC password token for the +# AP. This can be generated, e.g., with nfc_pw_token from wpa_supplicant. When +# these parameters are used, the AP is assumed to be deployed with a NFC tag +# that includes the matching NFC password token (e.g., written based on the +# NDEF record from nfc_pw_token). +# +#wps_nfc_dev_pw_id: Device Password ID (16..65535) +#wps_nfc_dh_pubkey: Hexdump of DH Public Key +#wps_nfc_dh_privkey: Hexdump of DH Private Key +#wps_nfc_dev_pw: Hexdump of Device Password + ##### Wi-Fi Direct (P2P) ###################################################### # Enable P2P Device management @@ -1034,6 +1391,16 @@ # stdoffset[dst[offset][,start[/time],end[/time]]] #time_zone=EST5 +# WNM-Sleep Mode (extended sleep mode for stations) +# 0 = disabled (default) +# 1 = enabled (allow stations to use WNM-Sleep Mode) +#wnm_sleep_mode=1 + +# BSS Transition Management +# 0 = disabled (default) +# 1 = enabled +#bss_transition=1 + ##### IEEE 802.11u-2011 ####################################################### # Enable Interworking service @@ -1087,11 +1454,180 @@ # Arbitrary number of Roaming Consortium OIs can be configured with each line # adding a new OI to the list. The first three entries are available through # Beacon and Probe Response frames. Any additional entry will be available only -# through ANQP queries. Each OI is between 3 and 15 octets and is configured a +# through ANQP queries. Each OI is between 3 and 15 octets and is configured as # a hexstring. #roaming_consortium=021122 #roaming_consortium=2233445566 +# Venue Name information +# This parameter can be used to configure one or more Venue Name Duples for +# Venue Name ANQP information. Each entry has a two or three character language +# code (ISO-639) separated by colon from the venue name string. +# Note that venue_group and venue_type have to be set for Venue Name +# information to be complete. +#venue_name=eng:Example venue +#venue_name=fin:Esimerkkipaikka +# Alternative format for language:value strings: +# (double quoted string, printf-escaped string) +#venue_name=P"eng:Example\nvenue" + +# Network Authentication Type +# This parameter indicates what type of network authentication is used in the +# network. +# format: [redirect URL] +# Network Authentication Type Indicator values: +# 00 = Acceptance of terms and conditions +# 01 = On-line enrollment supported +# 02 = http/https redirection +# 03 = DNS redirection +#network_auth_type=00 +#network_auth_type=02http://www.example.com/redirect/me/here/ + +# IP Address Type Availability +# format: <1-octet encoded value as hex str> +# (ipv4_type & 0x3f) << 2 | (ipv6_type & 0x3) +# ipv4_type: +# 0 = Address type not available +# 1 = Public IPv4 address available +# 2 = Port-restricted IPv4 address available +# 3 = Single NATed private IPv4 address available +# 4 = Double NATed private IPv4 address available +# 5 = Port-restricted IPv4 address and single NATed IPv4 address available +# 6 = Port-restricted IPv4 address and double NATed IPv4 address available +# 7 = Availability of the address type is not known +# ipv6_type: +# 0 = Address type not available +# 1 = Address type available +# 2 = Availability of the address type not known +#ipaddr_type_availability=14 + +# Domain Name +# format: [,] +#domain_name=example.com,another.example.com,yet-another.example.com + +# 3GPP Cellular Network information +# format: [;][;...] +#anqp_3gpp_cell_net=244,91;310,026;234,56 + +# NAI Realm information +# One or more realm can be advertised. Each nai_realm line adds a new realm to +# the set. These parameters provide information for stations using Interworking +# network selection to allow automatic connection to a network based on +# credentials. +# format: ,[,][,][,...] +# encoding: +# 0 = Realm formatted in accordance with IETF RFC 4282 +# 1 = UTF-8 formatted character string that is not formatted in +# accordance with IETF RFC 4282 +# NAI Realm(s): Semi-colon delimited NAI Realm(s) +# EAP Method: [:<[AuthParam1:Val1]>][<[AuthParam2:Val2]>][...] +# AuthParam (Table 8-188 in IEEE Std 802.11-2012): +# ID 2 = Non-EAP Inner Authentication Type +# 1 = PAP, 2 = CHAP, 3 = MSCHAP, 4 = MSCHAPV2 +# ID 3 = Inner authentication EAP Method Type +# ID 5 = Credential Type +# 1 = SIM, 2 = USIM, 3 = NFC Secure Element, 4 = Hardware Token, +# 5 = Softoken, 6 = Certificate, 7 = username/password, 9 = Anonymous, +# 10 = Vendor Specific +#nai_realm=0,example.com;example.net +# EAP methods EAP-TLS with certificate and EAP-TTLS/MSCHAPv2 with +# username/password +#nai_realm=0,example.org,13[5:6],21[2:4][5:7] + +# QoS Map Set configuration +# +# Comma delimited QoS Map Set in decimal values +# (see IEEE Std 802.11-2012, 8.4.2.97) +# +# format: +# [,],... +# +# There can be up to 21 optional DSCP Exceptions which are pairs of DSCP Value +# (0..63 or 255) and User Priority (0..7). This is followed by eight DSCP Range +# descriptions with DSCP Low Value and DSCP High Value pairs (0..63 or 255) for +# each UP starting from 0. If both low and high value are set to 255, the +# corresponding UP is not used. +# +# default: not set +#qos_map_set=53,2,22,6,8,15,0,7,255,255,16,31,32,39,255,255,40,47,255,255 + +##### Hotspot 2.0 ############################################################# + +# Enable Hotspot 2.0 support +#hs20=1 + +# Disable Downstream Group-Addressed Forwarding (DGAF) +# This can be used to configure a network where no group-addressed frames are +# allowed. The AP will not forward any group-address frames to the stations and +# random GTKs are issued for each station to prevent associated stations from +# forging such frames to other stations in the BSS. +#disable_dgaf=1 + +# Operator Friendly Name +# This parameter can be used to configure one or more Operator Friendly Name +# Duples. Each entry has a two or three character language code (ISO-639) +# separated by colon from the operator friendly name string. +#hs20_oper_friendly_name=eng:Example operator +#hs20_oper_friendly_name=fin:Esimerkkioperaattori + +# Connection Capability +# This can be used to advertise what type of IP traffic can be sent through the +# hotspot (e.g., due to firewall allowing/blocking protocols/ports). +# format: :: +# IP Protocol: 1 = ICMP, 6 = TCP, 17 = UDP +# Port Number: 0..65535 +# Status: 0 = Closed, 1 = Open, 2 = Unknown +# Each hs20_conn_capab line is added to the list of advertised tuples. +#hs20_conn_capab=1:0:2 +#hs20_conn_capab=6:22:1 +#hs20_conn_capab=17:5060:0 + +# WAN Metrics +# format: :
      :
        :
        :
          : +# WAN Info: B0-B1: Link Status, B2: Symmetric Link, B3: At Capabity +# (encoded as two hex digits) +# Link Status: 1 = Link up, 2 = Link down, 3 = Link in test state +# Downlink Speed: Estimate of WAN backhaul link current downlink speed in kbps; +# 1..4294967295; 0 = unknown +# Uplink Speed: Estimate of WAN backhaul link current uplink speed in kbps +# 1..4294967295; 0 = unknown +# Downlink Load: Current load of downlink WAN connection (scaled to 255 = 100%) +# Uplink Load: Current load of uplink WAN connection (scaled to 255 = 100%) +# Load Measurement Duration: Duration for measuring downlink/uplink load in +# tenths of a second (1..65535); 0 if load cannot be determined +#hs20_wan_metrics=01:8000:1000:80:240:3000 + +# Operating Class Indication +# List of operating classes the BSSes in this ESS use. The Global operating +# classes in Table E-4 of IEEE Std 802.11-2012 Annex E define the values that +# can be used in this. +# format: hexdump of operating class octets +# for example, operating classes 81 (2.4 GHz channels 1-13) and 115 (5 GHz +# channels 36-48): +#hs20_operating_class=5173 + +##### TESTING OPTIONS ######################################################### +# +# The options in this section are only available when the build configuration +# option CONFIG_TESTING_OPTIONS is set while compiling hostapd. They allow +# testing some scenarios that are otherwise difficult to reproduce. +# +# Ignore probe requests sent to hostapd with the given probability, must be a +# floating point number in the range [0, 1). +#ignore_probe_probability=0.0 +# +# Ignore authentication frames with the given probability +#ignore_auth_probability=0.0 +# +# Ignore association requests with the given probability +#ignore_assoc_probability=0.0 +# +# Ignore reassociation requests with the given probability +#ignore_reassoc_probability=0.0 +# +# Corrupt Key MIC in GTK rekey EAPOL-Key frames with the given probability +#corrupt_gtk_rekey_mic_probability=0.0 + ##### Multiple BSSID support ################################################## # # Above configuration is using the default interface (wlan#, or multi-SSID VLAN diff -Nru wpa-1.0/hostapd/hostapd.eap_user wpa-2.1/hostapd/hostapd.eap_user --- wpa-1.0/hostapd/hostapd.eap_user 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/hostapd/hostapd.eap_user 2014-02-04 11:23:35.000000000 +0000 @@ -69,6 +69,9 @@ "3"* SIM,TTLS,TLS,PEAP,AKA "4"* AKA,TTLS,TLS,PEAP,SIM "5"* SIM,TTLS,TLS,PEAP,AKA +"6"* AKA' +"7"* AKA' +"8"* AKA' # Wildcard for all other identities * PEAP,TTLS,TLS,SIM,AKA @@ -89,3 +92,6 @@ "3"* SIM [2] "4"* AKA [2] "5"* SIM [2] +"6"* AKA' [2] +"7"* AKA' [2] +"8"* AKA' [2] diff -Nru wpa-1.0/hostapd/hostapd.eap_user_sqlite wpa-2.1/hostapd/hostapd.eap_user_sqlite --- wpa-1.0/hostapd/hostapd.eap_user_sqlite 1970-01-01 00:00:00.000000000 +0000 +++ wpa-2.1/hostapd/hostapd.eap_user_sqlite 2014-02-04 11:23:35.000000000 +0000 @@ -0,0 +1,17 @@ +CREATE TABLE users( + identity TEXT PRIMARY KEY, + methods TEXT, + password TEXT, + phase2 INTEGER +); + +CREATE TABLE wildcards( + identity TEXT PRIMARY KEY, + methods TEXT +); + +INSERT INTO users(identity,methods,password,phase2) VALUES ('user','TTLS-MSCHAPV2','password',1); +INSERT INTO users(identity,methods,password,phase2) VALUES ('DOMAIN\mschapv2 user','TTLS-MSCHAPV2','password',1); + +INSERT INTO wildcards(identity,methods) VALUES ('','TTLS,TLS'); +INSERT INTO wildcards(identity,methods) VALUES ('0','AKA'); diff -Nru wpa-1.0/hostapd/main.c wpa-2.1/hostapd/main.c --- wpa-1.0/hostapd/main.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/hostapd/main.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,19 +2,14 @@ * hostapd / main() * Copyright (c) 2002-2011, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "utils/includes.h" #ifndef CONFIG_NATIVE_WINDOWS #include +#include #endif /* CONFIG_NATIVE_WINDOWS */ #include "utils/common.h" @@ -27,19 +22,12 @@ #include "eap_server/tncs.h" #include "ap/hostapd.h" #include "ap/ap_config.h" +#include "ap/ap_drv_ops.h" #include "config_file.h" #include "eap_register.h" -#include "dump_state.h" #include "ctrl_iface.h" -extern int wpa_debug_level; -extern int wpa_debug_show_keys; -extern int wpa_debug_timestamp; - -extern struct wpa_driver_ops *wpa_drivers[]; - - struct hapd_global { void **drv_priv; size_t drv_count; @@ -48,29 +36,6 @@ static struct hapd_global global; -struct hapd_interfaces { - size_t count; - struct hostapd_iface **iface; -}; - - -static int hostapd_for_each_interface(struct hapd_interfaces *interfaces, - int (*cb)(struct hostapd_iface *iface, - void *ctx), void *ctx) -{ - size_t i; - int ret; - - for (i = 0; i < interfaces->count; i++) { - ret = cb(interfaces->iface[i], ctx); - if (ret) - return ret; - } - - return 0; -} - - #ifndef CONFIG_NO_HOSTAPD_LOGGER static void hostapd_logger_cb(void *ctx, const u8 *addr, unsigned int module, int level, const char *txt, size_t len) @@ -141,7 +106,7 @@ if ((conf_stdout & module) && level >= conf_stdout_level) { wpa_debug_print_timestamp(); - printf("%s\n", format); + wpa_printf(MSG_INFO, "%s", format); } #ifndef CONFIG_NATIVE_WINDOWS @@ -175,68 +140,8 @@ /** - * hostapd_init - Allocate and initialize per-interface data - * @config_file: Path to the configuration file - * Returns: Pointer to the allocated interface data or %NULL on failure - * - * This function is used to allocate main data structures for per-interface - * data. The allocated data buffer will be freed by calling - * hostapd_cleanup_iface(). + * hostapd_driver_init - Preparate driver interface */ -static struct hostapd_iface * hostapd_init(const char *config_file) -{ - struct hostapd_iface *hapd_iface = NULL; - struct hostapd_config *conf = NULL; - struct hostapd_data *hapd; - size_t i; - - hapd_iface = os_zalloc(sizeof(*hapd_iface)); - if (hapd_iface == NULL) - goto fail; - - hapd_iface->reload_config = hostapd_reload_config; - hapd_iface->config_read_cb = hostapd_config_read; - hapd_iface->config_fname = os_strdup(config_file); - if (hapd_iface->config_fname == NULL) - goto fail; - hapd_iface->ctrl_iface_init = hostapd_ctrl_iface_init; - hapd_iface->ctrl_iface_deinit = hostapd_ctrl_iface_deinit; - hapd_iface->for_each_interface = hostapd_for_each_interface; - - conf = hostapd_config_read(hapd_iface->config_fname); - if (conf == NULL) - goto fail; - hapd_iface->conf = conf; - - hapd_iface->num_bss = conf->num_bss; - hapd_iface->bss = os_zalloc(conf->num_bss * - sizeof(struct hostapd_data *)); - if (hapd_iface->bss == NULL) - goto fail; - - for (i = 0; i < conf->num_bss; i++) { - hapd = hapd_iface->bss[i] = - hostapd_alloc_bss_data(hapd_iface, conf, - &conf->bss[i]); - if (hapd == NULL) - goto fail; - hapd->msg_ctx = hapd; - } - - return hapd_iface; - -fail: - if (conf) - hostapd_config_free(conf); - if (hapd_iface) { - os_free(hapd_iface->config_fname); - os_free(hapd_iface->bss); - os_free(hapd_iface); - } - return NULL; -} - - static int hostapd_driver_init(struct hostapd_iface *iface) { struct wpa_init_params params; @@ -276,13 +181,13 @@ } params.bssid = b; params.ifname = hapd->conf->iface; - params.ssid = (const u8 *) hapd->conf->ssid.ssid; + params.ssid = hapd->conf->ssid.ssid; params.ssid_len = hapd->conf->ssid.ssid_len; params.test_socket = hapd->conf->test_socket; params.use_pae_group_addr = hapd->conf->use_pae_group_addr; params.num_bridge = hapd->iface->num_bss; - params.bridge = os_zalloc(hapd->iface->num_bss * sizeof(char *)); + params.bridge = os_calloc(hapd->iface->num_bss, sizeof(char *)); if (params.bridge == NULL) return -1; for (i = 0; i < hapd->iface->num_bss; i++) { @@ -303,28 +208,26 @@ } if (hapd->driver->get_capa && - hapd->driver->get_capa(hapd->drv_priv, &capa) == 0) + hapd->driver->get_capa(hapd->drv_priv, &capa) == 0) { iface->drv_flags = capa.flags; + iface->probe_resp_offloads = capa.probe_resp_offloads; + 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; + } return 0; } -static void hostapd_interface_deinit_free(struct hostapd_iface *iface) -{ - const struct wpa_driver_ops *driver; - void *drv_priv; - if (iface == NULL) - return; - driver = iface->bss[0]->driver; - drv_priv = iface->bss[0]->drv_priv; - hostapd_interface_deinit(iface); - if (driver && driver->hapd_deinit) - driver->hapd_deinit(drv_priv); - hostapd_interface_free(iface); -} - - +/** + * hostapd_interface_init - Read configuration file and init BSS data + * + * 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. + */ static struct hostapd_iface * hostapd_interface_init(struct hapd_interfaces *interfaces, const char *config_fname, int debug) @@ -333,7 +236,7 @@ int k; wpa_printf(MSG_ERROR, "Configuration file: %s", config_fname); - iface = hostapd_init(config_fname); + iface = hostapd_init(interfaces, config_fname); if (!iface) return NULL; iface->interfaces = interfaces; @@ -343,8 +246,10 @@ iface->bss[0]->conf->logger_stdout_level--; } - if (hostapd_driver_init(iface) || - hostapd_setup_interface(iface)) { + if (iface->conf->bss[0]->iface[0] == '\0' && + !hostapd_drv_none(iface->bss[0])) { + wpa_printf(MSG_ERROR, "Interface name not specified in %s", + config_fname); hostapd_interface_deinit_free(iface); return NULL; } @@ -389,10 +294,7 @@ static void handle_dump_state(int sig, void *signal_ctx) { -#ifdef HOSTAPD_DUMP_STATE - struct hapd_interfaces *interfaces = signal_ctx; - hostapd_for_each_interface(interfaces, handle_dump_state_iface, NULL); -#endif /* HOSTAPD_DUMP_STATE */ + /* Not used anymore - ignore signal */ } #endif /* CONFIG_NATIVE_WINDOWS */ @@ -434,7 +336,7 @@ wpa_printf(MSG_ERROR, "No drivers enabled"); return -1; } - global.drv_priv = os_zalloc(global.drv_count * sizeof(void *)); + global.drv_priv = os_calloc(global.drv_count, sizeof(void *)); if (global.drv_priv == NULL) return -1; @@ -511,7 +413,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-2012, Jouni Malinen " + "Copyright (c) 2002-2014, Jouni Malinen " "and contributors\n"); } @@ -522,18 +424,26 @@ fprintf(stderr, "\n" "usage: hostapd [-hdBKtv] [-P ] [-e ] " - "\n" + "\\\n" + " [-g ] [-G ] \\\n" + " \n" "\n" "options:\n" " -h show this usage\n" " -d show more debug messages (-dd for even more)\n" " -B run daemon in the background\n" " -e entropy file\n" + " -g global control interface path\n" + " -G group for control interfaces\n" " -P PID file\n" " -K include key data in debug messages\n" #ifdef CONFIG_DEBUG_FILE " -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" + " (records all messages regardless of debug verbosity)\n" +#endif /* CONFIG_DEBUG_LINUX_TRACING */ " -t include timestamps in some debug messages\n" " -v show hostapd version\n"); @@ -544,27 +454,84 @@ static const char * hostapd_msg_ifname_cb(void *ctx) { struct hostapd_data *hapd = ctx; - if (hapd && hapd->iconf && hapd->iconf->bss) - return hapd->iconf->bss->iface; + if (hapd && hapd->iconf && hapd->iconf->bss && + hapd->iconf->num_bss > 0 && hapd->iconf->bss[0]) + return hapd->iconf->bss[0]->iface; return NULL; } +static int hostapd_get_global_ctrl_iface(struct hapd_interfaces *interfaces, + const char *path) +{ + char *pos; + os_free(interfaces->global_iface_path); + interfaces->global_iface_path = os_strdup(path); + if (interfaces->global_iface_path == NULL) + return -1; + pos = os_strrchr(interfaces->global_iface_path, '/'); + if (pos == NULL) { + wpa_printf(MSG_ERROR, "No '/' in the global control interface " + "file"); + os_free(interfaces->global_iface_path); + interfaces->global_iface_path = NULL; + return -1; + } + + *pos = '\0'; + interfaces->global_iface_name = pos + 1; + + return 0; +} + + +static int hostapd_get_ctrl_iface_group(struct hapd_interfaces *interfaces, + const char *group) +{ +#ifndef CONFIG_NATIVE_WINDOWS + struct group *grp; + grp = getgrnam(group); + if (grp == NULL) { + wpa_printf(MSG_ERROR, "Unknown group '%s'", group); + return -1; + } + interfaces->ctrl_iface_group = grp->gr_gid; +#endif /* CONFIG_NATIVE_WINDOWS */ + return 0; +} + + int main(int argc, char *argv[]) { struct hapd_interfaces interfaces; int ret = 1; - size_t i; + size_t i, j; int c, debug = 0, daemonize = 0; char *pid_file = NULL; const char *log_file = NULL; const char *entropy_file = NULL; + char **bss_config = NULL, **tmp_bss; + size_t num_bss_configs = 0; +#ifdef CONFIG_DEBUG_LINUX_TRACING + int enable_trace_dbg = 0; +#endif /* CONFIG_DEBUG_LINUX_TRACING */ if (os_program_init()) return -1; + os_memset(&interfaces, 0, sizeof(interfaces)); + interfaces.reload_config = hostapd_reload_config; + interfaces.config_read_cb = hostapd_config_read; + interfaces.for_each_interface = hostapd_for_each_interface; + interfaces.ctrl_iface_init = hostapd_ctrl_iface_init; + interfaces.ctrl_iface_deinit = hostapd_ctrl_iface_deinit; + interfaces.driver_init = hostapd_driver_init; + interfaces.global_iface_path = NULL; + interfaces.global_iface_name = NULL; + interfaces.global_ctrl_sock = -1; + for (;;) { - c = getopt(argc, argv, "Bde:f:hKP:tv"); + c = getopt(argc, argv, "b:Bde:f:hKP:Ttvg:G:"); if (c < 0) break; switch (c) { @@ -595,51 +562,144 @@ case 't': wpa_debug_timestamp++; break; +#ifdef CONFIG_DEBUG_LINUX_TRACING + case 'T': + enable_trace_dbg = 1; + break; +#endif /* CONFIG_DEBUG_LINUX_TRACING */ case 'v': show_version(); exit(1); break; - + case 'g': + if (hostapd_get_global_ctrl_iface(&interfaces, optarg)) + return -1; + break; + case 'G': + if (hostapd_get_ctrl_iface_group(&interfaces, optarg)) + return -1; + break; + case 'b': + tmp_bss = os_realloc_array(bss_config, + num_bss_configs + 1, + sizeof(char *)); + if (tmp_bss == NULL) + goto out; + bss_config = tmp_bss; + bss_config[num_bss_configs++] = optarg; + break; default: usage(); break; } } - if (optind == argc) + if (optind == argc && interfaces.global_iface_path == NULL && + num_bss_configs == 0) usage(); wpa_msg_register_ifname_cb(hostapd_msg_ifname_cb); if (log_file) wpa_debug_open_file(log_file); +#ifdef CONFIG_DEBUG_LINUX_TRACING + if (enable_trace_dbg) { + int tret = wpa_debug_open_linux_tracing(); + if (tret) { + wpa_printf(MSG_ERROR, "Failed to enable trace logging"); + return -1; + } + } +#endif /* CONFIG_DEBUG_LINUX_TRACING */ interfaces.count = argc - optind; - interfaces.iface = os_zalloc(interfaces.count * - sizeof(struct hostapd_iface *)); - if (interfaces.iface == NULL) { - wpa_printf(MSG_ERROR, "malloc failed"); - return -1; + if (interfaces.count || num_bss_configs) { + interfaces.iface = os_calloc(interfaces.count + num_bss_configs, + sizeof(struct hostapd_iface *)); + if (interfaces.iface == NULL) { + wpa_printf(MSG_ERROR, "malloc failed"); + return -1; + } } - if (hostapd_global_init(&interfaces, entropy_file)) + if (hostapd_global_init(&interfaces, entropy_file)) { + wpa_printf(MSG_ERROR, "Failed to initilize global context"); return -1; + } - /* Initialize interfaces */ + /* Allocate and parse configuration for full interface files */ for (i = 0; i < interfaces.count; i++) { interfaces.iface[i] = hostapd_interface_init(&interfaces, argv[optind + i], debug); - if (!interfaces.iface[i]) + if (!interfaces.iface[i]) { + wpa_printf(MSG_ERROR, "Failed to initialize interface"); goto out; + } } - if (hostapd_global_run(&interfaces, daemonize, pid_file)) + /* Allocate and parse configuration for per-BSS files */ + for (i = 0; i < num_bss_configs; i++) { + struct hostapd_iface *iface; + char *fname; + + wpa_printf(MSG_INFO, "BSS config: %s", bss_config[i]); + fname = os_strchr(bss_config[i], ':'); + if (fname == NULL) { + wpa_printf(MSG_ERROR, + "Invalid BSS config identifier '%s'", + bss_config[i]); + goto out; + } + *fname++ = '\0'; + iface = hostapd_interface_init_bss(&interfaces, bss_config[i], + fname, debug); + if (iface == NULL) + goto out; + for (j = 0; j < interfaces.count; j++) { + if (interfaces.iface[j] == iface) + break; + } + if (j == interfaces.count) { + struct hostapd_iface **tmp; + tmp = os_realloc_array(interfaces.iface, + interfaces.count + 1, + sizeof(struct hostapd_iface *)); + if (tmp == NULL) { + hostapd_interface_deinit_free(iface); + goto out; + } + interfaces.iface = tmp; + interfaces.iface[interfaces.count++] = iface; + } + } + + /* + * Enable configured interfaces. Depending on channel configuration, + * this may complete full initialization before returning or use a + * callback mechanism to complete setup in case of operations like HT + * co-ex scans, ACS, or DFS are needed to determine channel parameters. + * In such case, the interface will be enabled from eloop context within + * hostapd_global_run(). + */ + interfaces.terminate_on_error = interfaces.count; + for (i = 0; i < interfaces.count; i++) { + if (hostapd_driver_init(interfaces.iface[i]) || + hostapd_setup_interface(interfaces.iface[i])) + goto out; + } + + hostapd_global_ctrl_iface_init(&interfaces); + + if (hostapd_global_run(&interfaces, daemonize, pid_file)) { + wpa_printf(MSG_ERROR, "Failed to start eloop"); goto out; + } ret = 0; out: + hostapd_global_ctrl_iface_deinit(&interfaces); /* Deinitialize all interfaces */ for (i = 0; i < interfaces.count; i++) hostapd_interface_deinit_free(interfaces.iface[i]); @@ -650,6 +710,9 @@ if (log_file) wpa_debug_close_file(); + wpa_debug_close_linux_tracing(); + + os_free(bss_config); os_program_deinit(); diff -Nru wpa-1.0/hostapd/Makefile wpa-2.1/hostapd/Makefile --- wpa-1.0/hostapd/Makefile 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/hostapd/Makefile 2014-02-04 11:23:35.000000000 +0000 @@ -6,8 +6,8 @@ CFLAGS = -MMD -O2 -Wall -g endif -CFLAGS += -I../src -CFLAGS += -I../src/utils +CFLAGS += -I$(abspath ../src) +CFLAGS += -I$(abspath ../src/utils) # Uncomment following line and set the path to your kernel tree include # directory if your C library does not include all header files. @@ -15,6 +15,11 @@ -include .config +ifdef CONFIG_TESTING_OPTIONS +CFLAGS += -DCONFIG_TESTING_OPTIONS +CONFIG_WPS_TESTING=y +endif + ifndef CONFIG_OS ifdef CONFIG_NATIVE_WINDOWS CONFIG_OS=win32 @@ -43,6 +48,7 @@ OBJS += ../src/ap/authsrv.o OBJS += ../src/ap/ieee802_1x.o OBJS += ../src/ap/ap_config.o +OBJS += ../src/ap/eap_user_db.o OBJS += ../src/ap/ieee802_11_auth.o OBJS += ../src/ap/sta_info.o OBJS += ../src/ap/wpa_auth.o @@ -83,6 +89,14 @@ endif OBJS += ../src/utils/$(CONFIG_ELOOP).o OBJS_c += ../src/utils/$(CONFIG_ELOOP).o + +ifeq ($(CONFIG_ELOOP), eloop) +# Using glibc < 2.17 requires -lrt for clock_gettime() +LIBS += -lrt +LIBS_c += -lrt +LIBS_h += -lrt +endif + OBJS += ../src/utils/common.o OBJS += ../src/utils/wpa_debug.o OBJS_c += ../src/utils/wpa_debug.o @@ -96,11 +110,19 @@ OBJS += ../src/eapol_auth/eapol_auth_sm.o +ifdef CONFIG_CODE_COVERAGE +CFLAGS += -O0 -fprofile-arcs -ftest-coverage +LIBS += -lgcov +LIBS_c += -lgcov +LIBS_h += -lgcov +LIBS_n += -lgcov +endif + ifndef CONFIG_NO_DUMP_STATE -# define HOSTAPD_DUMP_STATE to include SIGUSR1 handler for dumping state to -# a file (undefine it, if you want to save in binary size) +# define HOSTAPD_DUMP_STATE to include support for dumping internal state +# through control interface commands (undefine it, if you want to save in +# binary size) CFLAGS += -DHOSTAPD_DUMP_STATE -OBJS += dump_state.o OBJS += ../src/eapol_auth/eapol_auth_dump.o endif @@ -110,6 +132,7 @@ else OBJS += ../src/radius/radius.o OBJS += ../src/radius/radius_client.o +OBJS += ../src/radius/radius_das.o endif ifdef CONFIG_NO_ACCOUNTING @@ -122,6 +145,12 @@ CFLAGS += -DCONFIG_NO_VLAN else OBJS += ../src/ap/vlan_init.o +ifdef CONFIG_VLAN_NETLINK +ifdef CONFIG_FULL_DYNAMIC_VLAN +OBJS += ../src/ap/vlan_util.o +endif +CFLAGS += -DCONFIG_VLAN_NETLINK +endif endif ifdef CONFIG_NO_CTRL_IFACE @@ -164,10 +193,26 @@ NEED_AES_UNWRAP=y endif +ifdef CONFIG_SAE +CFLAGS += -DCONFIG_SAE +OBJS += ../src/common/sae.o +NEED_ECC=y +NEED_DH_GROUPS=y +endif + +ifdef CONFIG_WNM +CFLAGS += -DCONFIG_WNM +OBJS += ../src/ap/wnm_ap.o +endif + ifdef CONFIG_IEEE80211N CFLAGS += -DCONFIG_IEEE80211N endif +ifdef CONFIG_IEEE80211AC +CFLAGS += -DCONFIG_IEEE80211AC +endif + include ../src/drivers/drivers.mak OBJS += $(DRV_AP_OBJS) CFLAGS += $(DRV_AP_CFLAGS) @@ -203,6 +248,14 @@ TLS_FUNCS=y endif +ifdef CONFIG_EAP_UNAUTH_TLS +CFLAGS += -DEAP_SERVER_UNAUTH_TLS +ifndef CONFIG_EAP_TLS +OBJS += ../src/eap_server/eap_server_tls.o +TLS_FUNCS=y +endif +endif + ifdef CONFIG_EAP_PEAP CFLAGS += -DEAP_SERVER_PEAP OBJS += ../src/eap_server/eap_server_peap.o @@ -279,7 +332,7 @@ CFLAGS += -DEAP_SERVER_GPSK OBJS += ../src/eap_server/eap_server_gpsk.o ../src/eap_common/eap_gpsk_common.o ifdef CONFIG_EAP_GPSK_SHA256 -CFLAGS += -DEAP_SERVER_GPSK_SHA256 +CFLAGS += -DEAP_GPSK_SHA256 endif NEED_SHA256=y NEED_AES_OMAC1=y @@ -291,6 +344,13 @@ NEED_SHA256=y endif +ifdef CONFIG_EAP_EKE +CFLAGS += -DEAP_SERVER_EKE +OBJS += ../src/eap_server/eap_server_eke.o ../src/eap_common/eap_eke_common.o +NEED_DH_GROUPS=y +NEED_DH_GROUPS_ALL=y +endif + ifdef CONFIG_EAP_VENDOR_TEST CFLAGS += -DEAP_SERVER_VENDOR_TEST OBJS += ../src/eap_server/eap_server_vendor_test.o @@ -329,25 +389,10 @@ NEED_MODEXP=y CONFIG_EAP=y -ifdef CONFIG_WPS_UFD -CFLAGS += -DCONFIG_WPS_UFD -OBJS += ../src/wps/wps_ufd.o -NEED_WPS_OOB=y -endif - ifdef CONFIG_WPS_NFC CFLAGS += -DCONFIG_WPS_NFC OBJS += ../src/wps/ndef.o -OBJS += ../src/wps/wps_nfc.o NEED_WPS_OOB=y -ifdef CONFIG_WPS_NFC_PN531 -PN531_PATH ?= /usr/local/src/nfc -CFLAGS += -DCONFIG_WPS_NFC_PN531 -CFLAGS += -I${PN531_PATH}/inc -OBJS += ../src/wps/wps_nfc_pn531.o -LIBS += ${PN531_PATH}/lib/wpsnfc.dll -LIBS += ${PN531_PATH}/lib/libnfc_mapping_pn53x.dll -endif endif ifdef NEED_WPS_OOB @@ -440,6 +485,11 @@ CFLAGS += -DCONFIG_TLSV11 endif +ifdef CONFIG_TLSV12 +CFLAGS += -DCONFIG_TLSV12 +NEED_SHA256=y +endif + ifeq ($(CONFIG_TLS), openssl) ifdef TLS_FUNCS OBJS += ../src/crypto/tls_openssl.o @@ -519,6 +569,9 @@ NEED_SHA256=y NEED_BASE64=y NEED_TLS_PRF=y +ifdef CONFIG_TLSV12 +NEED_TLS_PRF_SHA256=y +endif NEED_MODEXP=y NEED_CIPHER=y CFLAGS += -DCONFIG_TLS_INTERNAL @@ -632,14 +685,19 @@ endif ifdef NEED_SHA1 +ifneq ($(CONFIG_TLS), openssl) SHA1OBJS += ../src/crypto/sha1.o +endif +SHA1OBJS += ../src/crypto/sha1-prf.o ifdef CONFIG_INTERNAL_SHA1 SHA1OBJS += ../src/crypto/sha1-internal.o ifdef NEED_FIPS186_2_PRF SHA1OBJS += ../src/crypto/fips_prf_internal.o endif endif +ifneq ($(CONFIG_TLS), openssl) SHA1OBJS += ../src/crypto/sha1-pbkdf2.o +endif ifdef NEED_T_PRF SHA1OBJS += ../src/crypto/sha1-tprf.o endif @@ -678,10 +736,17 @@ endif ifdef NEED_SHA256 +CFLAGS += -DCONFIG_SHA256 +ifneq ($(CONFIG_TLS), openssl) OBJS += ../src/crypto/sha256.o +endif +OBJS += ../src/crypto/sha256-prf.o ifdef CONFIG_INTERNAL_SHA256 OBJS += ../src/crypto/sha256-internal.o endif +ifdef NEED_TLS_PRF_SHA256 +OBJS += ../src/crypto/sha256-tlsprf.o +endif endif ifdef NEED_DH_GROUPS @@ -696,6 +761,10 @@ endif endif +ifdef NEED_ECC +CFLAGS += -DCONFIG_ECC +endif + ifdef CONFIG_NO_RANDOM_POOL CFLAGS += -DCONFIG_NO_RANDOM_POOL else @@ -734,19 +803,32 @@ OBJS += ../src/ap/ap_list.o OBJS += ../src/ap/ieee802_11.o OBJS += ../src/ap/hw_features.o +OBJS += ../src/ap/dfs.o CFLAGS += -DNEED_AP_MLME endif ifdef CONFIG_IEEE80211N OBJS += ../src/ap/ieee802_11_ht.o endif +ifdef CONFIG_IEEE80211AC +OBJS += ../src/ap/ieee802_11_vht.o +endif + ifdef CONFIG_P2P_MANAGER CFLAGS += -DCONFIG_P2P_MANAGER OBJS += ../src/ap/p2p_hostapd.o endif +ifdef CONFIG_HS20 +CFLAGS += -DCONFIG_HS20 +OBJS += ../src/ap/hs20.o +CONFIG_INTERWORKING=y +endif + ifdef CONFIG_INTERWORKING CFLAGS += -DCONFIG_INTERWORKING +OBJS += ../src/common/gas.o +OBJS += ../src/ap/gas_serv.o endif OBJS += ../src/drivers/driver_common.o @@ -757,14 +839,30 @@ OBJS_c += ../src/utils/edit_simple.o endif +ifdef CONFIG_ACS +CFLAGS += -DCONFIG_ACS +OBJS += ../src/ap/acs.o +LIBS += -lm +endif + ifdef CONFIG_NO_STDOUT_DEBUG CFLAGS += -DCONFIG_NO_STDOUT_DEBUG endif +ifdef CONFIG_DEBUG_LINUX_TRACING +CFLAGS += -DCONFIG_DEBUG_LINUX_TRACING +endif + ifdef CONFIG_DEBUG_FILE CFLAGS += -DCONFIG_DEBUG_FILE endif +ifdef CONFIG_SQLITE +CFLAGS += -DCONFIG_SQLITE +LIBS += -lsqlite3 +LIBS_h += -lsqlite3 +endif + ALL=hostapd hostapd_cli all: verify_config $(ALL) @@ -776,9 +874,15 @@ E=true endif +ifdef CONFIG_CODE_COVERAGE +%.o: %.c + @$(E) " CC " $< + $(Q)cd $(dir $@); $(CC) -c -o $(notdir $@) $(CFLAGS) $(notdir $<) +else %.o: %.c $(Q)$(CC) -c -o $@ $(CFLAGS) $< @$(E) " CC " $< +endif verify_config: @if [ ! -r .config ]; then \ @@ -847,9 +951,15 @@ $(Q)$(CC) $(LDFLAGS) -o hlr_auc_gw $(HOBJS) $(LIBS_h) @$(E) " LD " $@ +lcov-html: + lcov -c -d .. > lcov.info + genhtml lcov.info --output-directory lcov-html + clean: $(MAKE) -C ../src clean rm -f core *~ *.o hostapd hostapd_cli nt_password_hash hlr_auc_gw - rm -f *.d + rm -f *.d *.gcno *.gcda *.gcov + rm -f lcov.info + rm -rf lcov-html -include $(OBJS:%.o=%.d) diff -Nru wpa-1.0/hostapd/nt_password_hash.c wpa-2.1/hostapd/nt_password_hash.c --- wpa-1.0/hostapd/nt_password_hash.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/hostapd/nt_password_hash.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * hostapd - Plaintext password to NtPasswordHash * Copyright (c) 2005, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff -Nru wpa-1.0/hostapd/README wpa-2.1/hostapd/README --- wpa-1.0/hostapd/README 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/hostapd/README 2014-02-04 11:23:35.000000000 +0000 @@ -2,37 +2,22 @@ Authenticator and RADIUS authentication server ================================================================ -Copyright (c) 2002-2012, Jouni Malinen and contributors +Copyright (c) 2002-2014, Jouni Malinen and contributors All Rights Reserved. -This program is dual-licensed under both the GPL version 2 and BSD -license. Either license may be used at your option. +This program is licensed under the BSD license (the one with +advertisement clause removed). + +If you are submitting changes to the project, please see CONTRIBUTIONS +file for more instructions. License ------- -GPL v2: - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License version 2 as -published by the Free Software Foundation. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - -(this copy of the license is in COPYING file) - - -Alternatively, this software may be distributed, used, and modified -under the terms of BSD license: +This software may be distributed, used, and modified under the terms of +BSD license: Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are diff -Nru wpa-1.0/hostapd/README-WPS wpa-2.1/hostapd/README-WPS --- wpa-1.0/hostapd/README-WPS 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/hostapd/README-WPS 2014-02-04 11:23:35.000000000 +0000 @@ -66,6 +66,10 @@ CONFIG_WPS2=y CONFIG_WPS_UPNP=y +Following parameter can be used to enable support for NFC config method: + +CONFIG_WPS_NFC=y + Following section shows an example runtime configuration (hostapd.conf) that enables WPS: @@ -289,3 +293,62 @@ This can be used to update the externally stored AP configuration and then update hostapd configuration (followed by restarting of hostapd). + + +WPS with NFC +------------ + +WPS can be used with NFC-based configuration method. An NFC tag +containing a password token from the Enrollee can be used to +authenticate the connection instead of the PIN. In addition, an NFC tag +with a configuration token can be used to transfer AP settings without +going through the WPS protocol. + +When the AP acts as an Enrollee, a local NFC tag with a password token +can be used by touching the NFC interface of an external Registrar. The +wps_nfc_token command is used to manage use of the NFC password token +from the AP. "wps_nfc_token enable" enables the use of the AP's NFC +password token (in place of AP PIN) and "wps_nfc_token disable" disables +the NFC password token. + +The NFC password token that is either pre-configured in the +configuration file (wps_nfc_dev_pw_id, wps_nfc_dh_pubkey, +wps_nfc_dh_privkey, wps_nfc_dev_pw) or generated dynamically with +"wps_nfc_token " command. The nfc_pw_token tool from +wpa_supplicant can be used to generate NFC password tokens during +manufacturing (each AP needs to have its own random keys). + +The "wps_nfc_config_token " command can be used to build an +NFC configuration token. The output value from this command is a hexdump +of the current AP configuration (WPS parameter requests this to include +only the WPS attributes; NDEF parameter requests additional NDEF +encapsulation to be included). This data needs to be written to an NFC +tag with an external program. Once written, the NFC configuration token +can be used to touch an NFC interface on a station to provision the +credentials needed to access the network. + +When the NFC device on the AP reads an NFC tag with a MIME media type +"application/vnd.wfa.wsc", the NDEF message payload (with or without +NDEF encapsulation) can be delivered to hostapd using the +following hostapd_cli command: + +wps_nfc_tag_read + +If the NFC tag contains a password token, the token is added to the +internal Registrar. This allows station Enrollee from which the password +token was received to run through WPS protocol to provision the +credential. + +"nfc_get_handover_sel " command can be used to build the +contents of a Handover Select Message for connection handover when this +does not depend on the contents of the Handover Request Message. The +first argument selects the format of the output data and the second +argument selects which type of connection handover is requested (WPS = +Wi-Fi handover as specified in WSC 2.0). + +"nfc_report_handover WPS +" is used to report completed NFC +connection handover. The first parameter indicates whether the local +device initiated or responded to the connection handover and the carrier +records are the selected carrier from the handover request and select +messages as a hexdump. diff -Nru wpa-1.0/hostapd/wps-ap-nfc.py wpa-2.1/hostapd/wps-ap-nfc.py --- wpa-1.0/hostapd/wps-ap-nfc.py 1970-01-01 00:00:00.000000000 +0000 +++ wpa-2.1/hostapd/wps-ap-nfc.py 2014-02-04 11:23:35.000000000 +0000 @@ -0,0 +1,282 @@ +#!/usr/bin/python +# +# Example nfcpy to hostapd wrapper for WPS NFC operations +# Copyright (c) 2012-2013, Jouni Malinen +# +# This software may be distributed under the terms of the BSD license. +# See README for more details. + +import os +import sys +import time +import argparse + +import nfc +import nfc.ndef +import nfc.llcp +import nfc.handover + +import logging + +import wpaspy + +wpas_ctrl = '/var/run/hostapd' +continue_loop = True + +def wpas_connect(): + ifaces = [] + 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 + return None + + if len(ifaces) < 1: + print "No hostapd control interface found" + return None + + for ctrl in ifaces: + try: + wpas = wpaspy.Ctrl(ctrl) + return wpas + except Exception, e: + pass + return None + + +def wpas_tag_read(message): + wpas = wpas_connect() + if (wpas == None): + return + if "FAIL" in wpas.request("WPS_NFC_TAG_READ " + str(message).encode("hex")): + return False + return True + + +def wpas_get_config_token(): + wpas = wpas_connect() + if (wpas == None): + return None + return wpas.request("WPS_NFC_CONFIG_TOKEN NDEF").rstrip().decode("hex") + + +def wpas_get_password_token(): + wpas = wpas_connect() + if (wpas == None): + return None + return wpas.request("WPS_NFC_TOKEN NDEF").rstrip().decode("hex") + + +def wpas_get_handover_sel(): + wpas = wpas_connect() + if (wpas == None): + return None + return wpas.request("NFC_GET_HANDOVER_SEL NDEF WPS-CR").rstrip().decode("hex") + + +def wpas_report_handover(req, sel): + wpas = wpas_connect() + if (wpas == None): + return None + return wpas.request("NFC_REPORT_HANDOVER RESP WPS " + + str(req).encode("hex") + " " + + str(sel).encode("hex")) + + +class HandoverServer(nfc.handover.HandoverServer): + def __init__(self, llc): + super(HandoverServer, self).__init__(llc) + self.ho_server_processing = False + self.success = False + + def process_request(self, request): + print "HandoverServer - request received" + try: + print "Parsed handover request: " + request.pretty() + except Exception, 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 + if carrier.type == "application/vnd.wfa.wsc": + print "WPS carrier type match - add WPS carrier record" + data = wpas_get_handover_sel() + if data is None: + print "Could not get handover select carrier record from hostapd" + continue + print "Handover select carrier record from hostapd:" + print data.encode("hex") + wpas_report_handover(carrier.record, data) + + message = nfc.ndef.Message(data); + sel.add_carrier(message[0], "active", message[1:]) + + print "Handover select:" + try: + print sel.pretty() + except Exception, e: + print e + print str(sel).encode("hex") + + print "Sending handover select" + self.success = True + return sel + + +def wps_tag_read(tag): + success = False + if len(tag.ndef.message): + for record in tag.ndef.message: + print "record type " + record.type + if record.type == "application/vnd.wfa.wsc": + print "WPS tag - send to hostapd" + success = wpas_tag_read(tag.ndef.message) + break + else: + print "Empty tag" + + return success + + +def rdwr_connected_write(tag): + print "Tag found - writing" + global write_data + tag.ndef.message = str(write_data) + print "Done - remove tag" + global only_one + if only_one: + global continue_loop + continue_loop = False + global write_wait_remove + while write_wait_remove and tag.is_present: + time.sleep(0.1) + +def wps_write_config_tag(clf, wait_remove=True): + print "Write WPS config token" + global write_data, write_wait_remove + write_wait_remove = wait_remove + write_data = wpas_get_config_token() + if write_data == None: + print "Could not get WPS config token from hostapd" + return + + print "Touch an NFC tag" + clf.connect(rdwr={'on-connect': rdwr_connected_write}) + + +def wps_write_password_tag(clf, wait_remove=True): + print "Write WPS password token" + global write_data, write_wait_remove + write_wait_remove = wait_remove + write_data = wpas_get_password_token() + if write_data == None: + print "Could not get WPS password token from hostapd" + return + + print "Touch an NFC tag" + clf.connect(rdwr={'on-connect': rdwr_connected_write}) + + +def rdwr_connected(tag): + global only_one, no_wait + print "Tag connected: " + str(tag) + + if tag.ndef: + print "NDEF tag: " + tag.type + try: + print tag.ndef.message.pretty() + except Exception, e: + print e + success = wps_tag_read(tag) + if only_one and success: + global continue_loop + continue_loop = False + else: + print "Not an NDEF tag - remove tag" + + return not no_wait + + +def llcp_startup(clf, llc): + print "Start LLCP server" + global srv + srv = HandoverServer(llc) + return llc + +def llcp_connected(llc): + print "P2P LLCP connected" + global wait_connection + wait_connection = False + global srv + srv.start() + return True + + +def main(): + clf = nfc.ContactlessFrontend() + + parser = argparse.ArgumentParser(description='nfcpy to hostapd integration for WPS NFC operations') + parser.add_argument('-d', const=logging.DEBUG, default=logging.INFO, + action='store_const', dest='loglevel', + help='verbose debug output') + parser.add_argument('-q', const=logging.WARNING, action='store_const', + dest='loglevel', help='be quiet') + parser.add_argument('--only-one', '-1', action='store_true', + help='run only one operation and exit') + parser.add_argument('--no-wait', action='store_true', + help='do not wait for tag to be removed before exiting') + parser.add_argument('command', choices=['write-config', + 'write-password'], + nargs='?') + args = parser.parse_args() + + global only_one + only_one = args.only_one + + global no_wait + no_wait = args.no_wait + + logging.basicConfig(level=args.loglevel) + + try: + if not clf.open("usb"): + print "Could not open connection with an NFC device" + raise SystemExit + + if args.command == "write-config": + wps_write_config_tag(clf, wait_remove=not args.no_wait) + raise SystemExit + + if args.command == "write-password": + wps_write_password_tag(clf, wait_remove=not args.no_wait) + raise SystemExit + + global continue_loop + while continue_loop: + print "Waiting for a tag or peer to be touched" + wait_connection = True + try: + if not clf.connect(rdwr={'on-connect': rdwr_connected}, + llcp={'on-startup': llcp_startup, + 'on-connect': llcp_connected}): + break + except Exception, e: + print "clf.connect failed" + + global srv + if only_one and srv and srv.success: + raise SystemExit + + except KeyboardInterrupt: + raise SystemExit + finally: + clf.close() + + raise SystemExit + +if __name__ == '__main__': + main() diff -Nru wpa-1.0/patches/openssl-0.9.8x-tls-extensions.patch wpa-2.1/patches/openssl-0.9.8x-tls-extensions.patch --- wpa-1.0/patches/openssl-0.9.8x-tls-extensions.patch 1970-01-01 00:00:00.000000000 +0000 +++ wpa-2.1/patches/openssl-0.9.8x-tls-extensions.patch 2014-02-04 11:23:35.000000000 +0000 @@ -0,0 +1,396 @@ +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.8x 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.8x.orig/ssl/s3_clnt.c openssl-0.9.8x/ssl/s3_clnt.c +--- openssl-0.9.8x.orig/ssl/s3_clnt.c 2011-12-26 21:38:28.000000000 +0200 ++++ openssl-0.9.8x/ssl/s3_clnt.c 2012-07-07 10:46:31.501140621 +0300 +@@ -757,6 +757,21 @@ 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); ++ } ++ } ++#endif /* OPENSSL_NO_TLSEXT */ ++ + if (j != 0 && j == s->session->session_id_length + && memcmp(p,s->session->session_id,j) == 0) + { +@@ -2725,11 +2740,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.8x.orig/ssl/s3_srvr.c openssl-0.9.8x/ssl/s3_srvr.c +--- openssl-0.9.8x.orig/ssl/s3_srvr.c 2012-02-16 17:21:17.000000000 +0200 ++++ openssl-0.9.8x/ssl/s3_srvr.c 2012-07-07 10:46:31.501140621 +0300 +@@ -1009,6 +1009,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 +@@ -1147,16 +1200,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.8x.orig/ssl/ssl_err.c openssl-0.9.8x/ssl/ssl_err.c +--- openssl-0.9.8x.orig/ssl/ssl_err.c 2012-03-12 16:50:55.000000000 +0200 ++++ openssl-0.9.8x/ssl/ssl_err.c 2012-07-07 10:46:31.501140621 +0300 +@@ -264,6 +264,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.8x.orig/ssl/ssl.h openssl-0.9.8x/ssl/ssl.h +--- openssl-0.9.8x.orig/ssl/ssl.h 2012-03-12 16:50:55.000000000 +0200 ++++ openssl-0.9.8x/ssl/ssl.h 2012-07-07 10:46:31.501140621 +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 + { +@@ -1050,6 +1054,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 +@@ -1663,6 +1679,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. +@@ -1866,6 +1891,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.8x.orig/ssl/ssl_sess.c openssl-0.9.8x/ssl/ssl_sess.c +--- openssl-0.9.8x.orig/ssl/ssl_sess.c 2010-02-01 18:48:40.000000000 +0200 ++++ openssl-0.9.8x/ssl/ssl_sess.c 2012-07-07 10:46:31.501140621 +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.8x.orig/ssl/t1_lib.c openssl-0.9.8x/ssl/t1_lib.c +--- openssl-0.9.8x.orig/ssl/t1_lib.c 2012-01-04 16:25:10.000000000 +0200 ++++ openssl-0.9.8x/ssl/t1_lib.c 2012-07-07 10:47:31.153140501 +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) +@@ -486,6 +508,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) + { +@@ -663,6 +694,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)) + { +@@ -920,6 +957,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.8x.orig/ssl/tls1.h openssl-0.9.8x/ssl/tls1.h +--- openssl-0.9.8x.orig/ssl/tls1.h 2009-11-08 16:51:54.000000000 +0200 ++++ openssl-0.9.8x/ssl/tls1.h 2012-07-07 10:46:31.501140621 +0300 +@@ -401,6 +401,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.8x.orig/util/ssleay.num openssl-0.9.8x/util/ssleay.num +--- openssl-0.9.8x.orig/util/ssleay.num 2008-06-05 13:57:21.000000000 +0300 ++++ openssl-0.9.8x/util/ssleay.num 2012-07-07 10:46:31.505140623 +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 diff -Nru wpa-1.0/README wpa-2.1/README --- wpa-1.0/README 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/README 2014-02-04 11:23:35.000000000 +0000 @@ -1,12 +1,11 @@ wpa_supplicant and hostapd -------------------------- -Copyright (c) 2002-2012, Jouni Malinen and contributors +Copyright (c) 2002-2014, Jouni Malinen and contributors All Rights Reserved. -These programs are dual-licensed under both the GPL version 2 and BSD -license (the one with advertisement clause removed). Either license -may be used at your option. +These programs are licensed under the BSD license (the one with +advertisement clause removed). If you are submitting changes to the project, please see CONTRIBUTIONS file for more instructions. @@ -26,26 +25,8 @@ License ------- -GPL v2: - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License version 2 as -published by the Free Software Foundation. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - -(this copy of the license is in COPYING file) - - -Alternatively, this software may be distributed, used, and modified -under the terms of BSD license: +This software may be distributed, used, and modified under the terms of +BSD license: Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are diff -Nru wpa-1.0/src/ap/accounting.c wpa-2.1/src/ap/accounting.c --- wpa-1.0/src/ap/accounting.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/ap/accounting.c 2014-02-04 11:23:35.000000000 +0000 @@ -1,22 +1,15 @@ /* * hostapd / RADIUS Accounting - * Copyright (c) 2002-2009, Jouni Malinen + * Copyright (c) 2002-2009, 2012, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * 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 "drivers/driver.h" #include "radius/radius.h" #include "radius/radius_client.h" #include "hostapd.h" @@ -32,8 +25,8 @@ * input/output octets and updates Acct-{Input,Output}-Gigawords. */ #define ACCT_DEFAULT_UPDATE_INTERVAL 300 -static void accounting_sta_get_id(struct hostapd_data *hapd, - struct sta_info *sta); +static void accounting_sta_interim(struct hostapd_data *hapd, + struct sta_info *sta); static struct radius_msg * accounting_msg(struct hostapd_data *hapd, @@ -45,11 +38,12 @@ u8 *val; size_t len; int i; + struct wpabuf *b; msg = radius_msg_new(RADIUS_CODE_ACCOUNTING_REQUEST, radius_client_get_id(hapd->radius)); if (msg == NULL) { - printf("Could not create net RADIUS packet\n"); + wpa_printf(MSG_INFO, "Could not create new RADIUS packet"); return NULL; } @@ -60,7 +54,7 @@ sta->acct_session_id_hi, sta->acct_session_id_lo); if (!radius_msg_add_attr(msg, RADIUS_ATTR_ACCT_SESSION_ID, (u8 *) buf, os_strlen(buf))) { - printf("Could not add Acct-Session-Id\n"); + wpa_printf(MSG_INFO, "Could not add Acct-Session-Id"); goto fail; } } else { @@ -69,20 +63,32 @@ if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_STATUS_TYPE, status_type)) { - printf("Could not add Acct-Status-Type\n"); + wpa_printf(MSG_INFO, "Could not add Acct-Status-Type"); goto fail; } - if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_AUTHENTIC, + 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)) { - printf("Could not add Acct-Authentic\n"); + 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); + + /* Use RADIUS ACL identity if 802.1X provides no identity */ + if (!val && sta->identity) { + val = (u8 *) sta->identity; + len = os_strlen(sta->identity); + } + + /* Use STA MAC if neither 802.1X nor RADIUS ACL provided + * identity */ if (!val) { os_snprintf(buf, sizeof(buf), RADIUS_ADDR_FORMAT, MAC2STR(sta->addr)); @@ -92,75 +98,16 @@ if (!radius_msg_add_attr(msg, RADIUS_ATTR_USER_NAME, val, len)) { - printf("Could not add User-Name\n"); + wpa_printf(MSG_INFO, "Could not add User-Name"); goto fail; } } - if (hapd->conf->own_ip_addr.af == AF_INET && - !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IP_ADDRESS, - (u8 *) &hapd->conf->own_ip_addr.u.v4, 4)) { - printf("Could not add NAS-IP-Address\n"); - goto fail; - } - -#ifdef CONFIG_IPV6 - if (hapd->conf->own_ip_addr.af == AF_INET6 && - !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IPV6_ADDRESS, - (u8 *) &hapd->conf->own_ip_addr.u.v6, 16)) { - printf("Could not add NAS-IPv6-Address\n"); - goto fail; - } -#endif /* CONFIG_IPV6 */ - - if (hapd->conf->nas_identifier && - !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IDENTIFIER, - (u8 *) hapd->conf->nas_identifier, - os_strlen(hapd->conf->nas_identifier))) { - printf("Could not add NAS-Identifier\n"); - goto fail; - } - - if (sta && - !radius_msg_add_attr_int32(msg, RADIUS_ATTR_NAS_PORT, sta->aid)) { - printf("Could not add NAS-Port\n"); + if (add_common_radius_attr(hapd, hapd->conf->radius_acct_req_attr, sta, + msg) < 0) goto fail; - } - - os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT ":%s", - MAC2STR(hapd->own_addr), hapd->conf->ssid.ssid); - if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLED_STATION_ID, - (u8 *) buf, os_strlen(buf))) { - printf("Could not add Called-Station-Id\n"); - goto fail; - } if (sta) { - os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT, - MAC2STR(sta->addr)); - if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLING_STATION_ID, - (u8 *) buf, os_strlen(buf))) { - printf("Could not add Calling-Station-Id\n"); - goto fail; - } - - if (!radius_msg_add_attr_int32( - msg, RADIUS_ATTR_NAS_PORT_TYPE, - RADIUS_NAS_PORT_TYPE_IEEE_802_11)) { - printf("Could not add NAS-Port-Type\n"); - goto fail; - } - - os_snprintf(buf, sizeof(buf), "CONNECT %d%sMbps %s", - radius_sta_rate(hapd, sta) / 2, - (radius_sta_rate(hapd, sta) & 1) ? ".5" : "", - radius_mode_txt(hapd)); - if (!radius_msg_add_attr(msg, RADIUS_ATTR_CONNECT_INFO, - (u8 *) buf, os_strlen(buf))) { - printf("Could not add Connect-Info\n"); - goto fail; - } - for (i = 0; ; i++) { val = ieee802_1x_get_radius_class(sta->eapol_sm, &len, i); @@ -169,10 +116,28 @@ if (!radius_msg_add_attr(msg, RADIUS_ATTR_CLASS, val, len)) { - printf("Could not add Class\n"); + wpa_printf(MSG_INFO, "Could not add Class"); goto fail; } } + + b = ieee802_1x_get_radius_cui(sta->eapol_sm); + if (b && + !radius_msg_add_attr(msg, + RADIUS_ATTR_CHARGEABLE_USER_IDENTITY, + wpabuf_head(b), wpabuf_len(b))) { + wpa_printf(MSG_ERROR, "Could not add CUI"); + goto fail; + } + + if (!b && sta->radius_cui && + !radius_msg_add_attr(msg, + RADIUS_ATTR_CHARGEABLE_USER_IDENTITY, + (u8 *) sta->radius_cui, + os_strlen(sta->radius_cui))) { + wpa_printf(MSG_ERROR, "Could not add CUI from ACL"); + goto fail; + } } return msg; @@ -236,20 +201,17 @@ void accounting_sta_start(struct hostapd_data *hapd, struct sta_info *sta) { struct radius_msg *msg; - struct os_time t; int interval; if (sta->acct_session_started) return; - accounting_sta_get_id(hapd, sta); 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); - os_get_time(&t); - sta->acct_session_start = t.sec; + 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; hostapd_drv_sta_clear_stats(hapd, sta->addr); @@ -279,6 +241,7 @@ struct radius_msg *msg; int cause = sta->acct_terminate_cause; struct hostap_sta_driver_data data; + struct os_reltime now_r, diff; struct os_time now; u32 gigawords; @@ -289,14 +252,16 @@ stop ? RADIUS_ACCT_STATUS_TYPE_STOP : RADIUS_ACCT_STATUS_TYPE_INTERIM_UPDATE); if (!msg) { - printf("Could not create RADIUS Accounting message\n"); + wpa_printf(MSG_INFO, "Could not create RADIUS Accounting message"); return; } + 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, - now.sec - sta->acct_session_start)) { - printf("Could not add Acct-Session-Time\n"); + diff.sec)) { + wpa_printf(MSG_INFO, "Could not add Acct-Session-Time"); goto fail; } @@ -304,19 +269,19 @@ if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_INPUT_PACKETS, data.rx_packets)) { - printf("Could not add Acct-Input-Packets\n"); + wpa_printf(MSG_INFO, "Could not add Acct-Input-Packets"); goto fail; } if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_OUTPUT_PACKETS, data.tx_packets)) { - printf("Could not add Acct-Output-Packets\n"); + wpa_printf(MSG_INFO, "Could not add Acct-Output-Packets"); goto fail; } if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_INPUT_OCTETS, data.rx_bytes)) { - printf("Could not add Acct-Input-Octets\n"); + wpa_printf(MSG_INFO, "Could not add Acct-Input-Octets"); goto fail; } gigawords = sta->acct_input_gigawords; @@ -327,13 +292,13 @@ !radius_msg_add_attr_int32( msg, RADIUS_ATTR_ACCT_INPUT_GIGAWORDS, gigawords)) { - printf("Could not add Acct-Input-Gigawords\n"); + wpa_printf(MSG_INFO, "Could not add Acct-Input-Gigawords"); goto fail; } if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_OUTPUT_OCTETS, data.tx_bytes)) { - printf("Could not add Acct-Output-Octets\n"); + wpa_printf(MSG_INFO, "Could not add Acct-Output-Octets"); goto fail; } gigawords = sta->acct_output_gigawords; @@ -344,14 +309,14 @@ !radius_msg_add_attr_int32( msg, RADIUS_ATTR_ACCT_OUTPUT_GIGAWORDS, gigawords)) { - printf("Could not add Acct-Output-Gigawords\n"); + 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)) { - printf("Could not add Event-Timestamp\n"); + wpa_printf(MSG_INFO, "Could not add Event-Timestamp"); goto fail; } @@ -361,7 +326,7 @@ if (stop && cause && !radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_TERMINATE_CAUSE, cause)) { - printf("Could not add Acct-Terminate-Cause\n"); + wpa_printf(MSG_INFO, "Could not add Acct-Terminate-Cause"); goto fail; } @@ -381,7 +346,8 @@ * @hapd: hostapd BSS data * @sta: The station */ -void accounting_sta_interim(struct hostapd_data *hapd, struct sta_info *sta) +static void accounting_sta_interim(struct hostapd_data *hapd, + struct sta_info *sta) { if (sta->acct_session_started) accounting_sta_report(hapd, sta, 0); @@ -408,7 +374,7 @@ } -static void accounting_sta_get_id(struct hostapd_data *hapd, +void accounting_sta_get_id(struct hostapd_data *hapd, struct sta_info *sta) { sta->acct_session_id_lo = hapd->acct_session_id_lo++; @@ -434,13 +400,12 @@ void *data) { if (radius_msg_get_hdr(msg)->code != RADIUS_CODE_ACCOUNTING_RESPONSE) { - printf("Unknown RADIUS message code\n"); + wpa_printf(MSG_INFO, "Unknown RADIUS message code"); return RADIUS_RX_UNKNOWN; } if (radius_msg_verify(msg, shared_secret, shared_secret_len, req, 0)) { - printf("Incoming RADIUS packet did not have correct " - "Authenticator - dropped\n"); + wpa_printf(MSG_INFO, "Incoming RADIUS packet did not have correct Authenticator - dropped"); return RADIUS_RX_INVALID_AUTHENTICATOR; } @@ -466,7 +431,7 @@ if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_TERMINATE_CAUSE, RADIUS_ACCT_TERMINATE_CAUSE_NAS_REBOOT)) { - printf("Could not add Acct-Terminate-Cause\n"); + wpa_printf(MSG_INFO, "Could not add Acct-Terminate-Cause"); radius_msg_free(msg); return; } diff -Nru wpa-1.0/src/ap/accounting.h wpa-2.1/src/ap/accounting.h --- wpa-1.0/src/ap/accounting.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/ap/accounting.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,21 +2,19 @@ * hostapd / RADIUS Accounting * Copyright (c) 2002-2005, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef ACCOUNTING_H #define ACCOUNTING_H -void accounting_sta_interim(struct hostapd_data *hapd, struct sta_info *sta); #ifdef CONFIG_NO_ACCOUNTING +static inline void accounting_sta_get_id(struct hostapd_data *hapd, + struct sta_info *sta) +{ +} + static inline void accounting_sta_start(struct hostapd_data *hapd, struct sta_info *sta) { @@ -36,6 +34,7 @@ { } #else /* CONFIG_NO_ACCOUNTING */ +void 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); diff -Nru wpa-1.0/src/ap/acs.c wpa-2.1/src/ap/acs.c --- wpa-1.0/src/ap/acs.c 1970-01-01 00:00:00.000000000 +0000 +++ wpa-2.1/src/ap/acs.c 2014-02-04 11:23:35.000000000 +0000 @@ -0,0 +1,802 @@ +/* + * ACS - Automatic Channel Selection module + * Copyright (c) 2011, Atheros Communications + * Copyright (c) 2013, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" +#include + +#include "utils/common.h" +#include "utils/list.h" +#include "common/ieee802_11_defs.h" +#include "common/wpa_ctrl.h" +#include "drivers/driver.h" +#include "hostapd.h" +#include "ap_drv_ops.h" +#include "ap_config.h" +#include "hw_features.h" +#include "acs.h" + +/* + * Automatic Channel Selection + * =========================== + * + * More info at + * ------------ + * http://wireless.kernel.org/en/users/Documentation/acs + * + * How to use + * ---------- + * - make sure you have CONFIG_ACS=y in hostapd's .config + * - use channel=0 or channel=acs to enable ACS + * + * How does it work + * ---------------- + * 1. passive scans are used to collect survey data + * (it is assumed that scan trigger collection of survey data in driver) + * 2. interference factor is calculated for each channel + * 3. ideal channel is picked depending on channel width by using adjacent + * channel interference factors + * + * Known limitations + * ----------------- + * - Current implementation depends heavily on the amount of time willing to + * spend gathering survey data during hostapd startup. Short traffic bursts + * may be missed and a suboptimal channel may be picked. + * - Ideal channel may end up overlapping a channel with 40 MHz intolerant BSS + * + * Todo / Ideas + * ------------ + * - implement other interference computation methods + * - BSS/RSSI based + * - spectral scan based + * (should be possibly to hook this up with current ACS scans) + * - add wpa_supplicant support (for P2P) + * - collect a histogram of interference over time allowing more educated + * guess about an ideal channel (perhaps CSA could be used to migrate AP to a + * new "better" channel while running) + * - include neighboring BSS scan to avoid conflicts with 40 MHz intolerant BSSs + * when choosing the ideal channel + * + * Survey interference factor implementation details + * ------------------------------------------------- + * Generic interference_factor in struct hostapd_channel_data is used. + * + * The survey interference factor is defined as the ratio of the + * observed busy time over the time we spent on the channel, + * this value is then amplified by the observed noise floor on + * the channel in comparison to the lowest noise floor observed + * on the entire band. + * + * This corresponds to: + * --- + * (busy time - tx time) / (active time - tx time) * 2^(chan_nf + band_min_nf) + * --- + * + * The coefficient of 2 reflects the way power in "far-field" + * radiation decreases as the square of distance from the antenna [1]. + * What this does is it decreases the observed busy time ratio if the + * noise observed was low but increases it if the noise was high, + * proportionally to the way "far field" radiation changes over + * distance. + * + * If channel busy time is not available the fallback is to use channel RX time. + * + * Since noise floor is in dBm it is necessary to convert it into Watts so that + * combined channel interference (e.g., HT40, which uses two channels) can be + * calculated easily. + * --- + * (busy time - tx time) / (active time - tx time) * + * 2^(10^(chan_nf/10) + 10^(band_min_nf/10)) + * --- + * + * However to account for cases where busy/rx time is 0 (channel load is then + * 0%) channel noise floor signal power is combined into the equation so a + * channel with lower noise floor is preferred. The equation becomes: + * --- + * 10^(chan_nf/5) + (busy time - tx time) / (active time - tx time) * + * 2^(10^(chan_nf/10) + 10^(band_min_nf/10)) + * --- + * + * All this "interference factor" is purely subjective and only time + * will tell how usable this is. By using the minimum noise floor we + * remove any possible issues due to card calibration. The computation + * of the interference factor then is dependent on what the card itself + * picks up as the minimum noise, not an actual real possible card + * noise value. + * + * Total interference computation details + * -------------------------------------- + * The above channel interference factor is calculated with no respect to + * target operational bandwidth. + * + * To find an ideal channel the above data is combined by taking into account + * the target operational bandwidth and selected band. E.g., on 2.4 GHz channels + * overlap with 20 MHz bandwidth, but there is no overlap for 20 MHz bandwidth + * on 5 GHz. + * + * Each valid and possible channel spec (i.e., channel + width) is taken and its + * interference factor is computed by summing up interferences of each channel + * it overlaps. The one with least total interference is picked up. + * + * Note: This implies base channel interference factor must be non-negative + * allowing easy summing up. + * + * Example ACS analysis printout + * ----------------------------- + * + * ACS: Trying survey-based ACS + * ACS: Survey analysis for channel 1 (2412 MHz) + * ACS: 1: min_nf=-113 interference_factor=0.0802469 nf=-113 time=162 busy=0 rx=13 + * ACS: 2: min_nf=-113 interference_factor=0.0745342 nf=-113 time=161 busy=0 rx=12 + * ACS: 3: min_nf=-113 interference_factor=0.0679012 nf=-113 time=162 busy=0 rx=11 + * ACS: 4: min_nf=-113 interference_factor=0.0310559 nf=-113 time=161 busy=0 rx=5 + * ACS: 5: min_nf=-113 interference_factor=0.0248447 nf=-113 time=161 busy=0 rx=4 + * ACS: * interference factor average: 0.0557166 + * ACS: Survey analysis for channel 2 (2417 MHz) + * ACS: 1: min_nf=-113 interference_factor=0.0185185 nf=-113 time=162 busy=0 rx=3 + * ACS: 2: min_nf=-113 interference_factor=0.0246914 nf=-113 time=162 busy=0 rx=4 + * ACS: 3: min_nf=-113 interference_factor=0.037037 nf=-113 time=162 busy=0 rx=6 + * ACS: 4: min_nf=-113 interference_factor=0.149068 nf=-113 time=161 busy=0 rx=24 + * ACS: 5: min_nf=-113 interference_factor=0.0248447 nf=-113 time=161 busy=0 rx=4 + * ACS: * interference factor average: 0.050832 + * ACS: Survey analysis for channel 3 (2422 MHz) + * ACS: 1: min_nf=-113 interference_factor=2.51189e-23 nf=-113 time=162 busy=0 rx=0 + * ACS: 2: min_nf=-113 interference_factor=0.0185185 nf=-113 time=162 busy=0 rx=3 + * ACS: 3: min_nf=-113 interference_factor=0.0186335 nf=-113 time=161 busy=0 rx=3 + * ACS: 4: min_nf=-113 interference_factor=0.0186335 nf=-113 time=161 busy=0 rx=3 + * ACS: 5: min_nf=-113 interference_factor=0.0186335 nf=-113 time=161 busy=0 rx=3 + * ACS: * interference factor average: 0.0148838 + * ACS: Survey analysis for channel 4 (2427 MHz) + * ACS: 1: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=162 busy=0 rx=0 + * ACS: 2: min_nf=-114 interference_factor=0.0555556 nf=-114 time=162 busy=0 rx=9 + * ACS: 3: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=161 busy=0 rx=0 + * ACS: 4: min_nf=-114 interference_factor=0.0186335 nf=-114 time=161 busy=0 rx=3 + * ACS: 5: min_nf=-114 interference_factor=0.00621118 nf=-114 time=161 busy=0 rx=1 + * ACS: * interference factor average: 0.0160801 + * ACS: Survey analysis for channel 5 (2432 MHz) + * ACS: 1: min_nf=-114 interference_factor=0.409938 nf=-113 time=161 busy=0 rx=66 + * ACS: 2: min_nf=-114 interference_factor=0.0432099 nf=-113 time=162 busy=0 rx=7 + * ACS: 3: min_nf=-114 interference_factor=0.0124224 nf=-113 time=161 busy=0 rx=2 + * ACS: 4: min_nf=-114 interference_factor=0.677019 nf=-113 time=161 busy=0 rx=109 + * ACS: 5: min_nf=-114 interference_factor=0.0186335 nf=-114 time=161 busy=0 rx=3 + * ACS: * interference factor average: 0.232244 + * ACS: Survey analysis for channel 6 (2437 MHz) + * ACS: 1: min_nf=-113 interference_factor=0.552795 nf=-113 time=161 busy=0 rx=89 + * ACS: 2: min_nf=-113 interference_factor=0.0807453 nf=-112 time=161 busy=0 rx=13 + * ACS: 3: min_nf=-113 interference_factor=0.0310559 nf=-113 time=161 busy=0 rx=5 + * ACS: 4: min_nf=-113 interference_factor=0.434783 nf=-112 time=161 busy=0 rx=70 + * ACS: 5: min_nf=-113 interference_factor=0.0621118 nf=-113 time=161 busy=0 rx=10 + * ACS: * interference factor average: 0.232298 + * ACS: Survey analysis for channel 7 (2442 MHz) + * ACS: 1: min_nf=-113 interference_factor=0.440994 nf=-112 time=161 busy=0 rx=71 + * ACS: 2: min_nf=-113 interference_factor=0.385093 nf=-113 time=161 busy=0 rx=62 + * ACS: 3: min_nf=-113 interference_factor=0.0372671 nf=-113 time=161 busy=0 rx=6 + * ACS: 4: min_nf=-113 interference_factor=0.0372671 nf=-113 time=161 busy=0 rx=6 + * ACS: 5: min_nf=-113 interference_factor=0.0745342 nf=-113 time=161 busy=0 rx=12 + * ACS: * interference factor average: 0.195031 + * ACS: Survey analysis for channel 8 (2447 MHz) + * ACS: 1: min_nf=-114 interference_factor=0.0496894 nf=-112 time=161 busy=0 rx=8 + * ACS: 2: min_nf=-114 interference_factor=0.0496894 nf=-114 time=161 busy=0 rx=8 + * ACS: 3: min_nf=-114 interference_factor=0.0372671 nf=-113 time=161 busy=0 rx=6 + * ACS: 4: min_nf=-114 interference_factor=0.12963 nf=-113 time=162 busy=0 rx=21 + * ACS: 5: min_nf=-114 interference_factor=0.166667 nf=-114 time=162 busy=0 rx=27 + * ACS: * interference factor average: 0.0865885 + * ACS: Survey analysis for channel 9 (2452 MHz) + * ACS: 1: min_nf=-114 interference_factor=0.0124224 nf=-114 time=161 busy=0 rx=2 + * ACS: 2: min_nf=-114 interference_factor=0.0310559 nf=-114 time=161 busy=0 rx=5 + * ACS: 3: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=161 busy=0 rx=0 + * ACS: 4: min_nf=-114 interference_factor=0.00617284 nf=-114 time=162 busy=0 rx=1 + * ACS: 5: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=162 busy=0 rx=0 + * ACS: * interference factor average: 0.00993022 + * ACS: Survey analysis for channel 10 (2457 MHz) + * ACS: 1: min_nf=-114 interference_factor=0.00621118 nf=-114 time=161 busy=0 rx=1 + * ACS: 2: min_nf=-114 interference_factor=0.00621118 nf=-114 time=161 busy=0 rx=1 + * ACS: 3: min_nf=-114 interference_factor=0.00621118 nf=-114 time=161 busy=0 rx=1 + * ACS: 4: min_nf=-114 interference_factor=0.0493827 nf=-114 time=162 busy=0 rx=8 + * ACS: 5: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=162 busy=0 rx=0 + * ACS: * interference factor average: 0.0136033 + * ACS: Survey analysis for channel 11 (2462 MHz) + * ACS: 1: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=161 busy=0 rx=0 + * ACS: 2: min_nf=-114 interference_factor=2.51189e-23 nf=-113 time=161 busy=0 rx=0 + * ACS: 3: min_nf=-114 interference_factor=2.51189e-23 nf=-113 time=161 busy=0 rx=0 + * ACS: 4: min_nf=-114 interference_factor=0.0432099 nf=-114 time=162 busy=0 rx=7 + * ACS: 5: min_nf=-114 interference_factor=0.0925926 nf=-114 time=162 busy=0 rx=15 + * ACS: * interference factor average: 0.0271605 + * ACS: Survey analysis for channel 12 (2467 MHz) + * ACS: 1: min_nf=-114 interference_factor=0.0621118 nf=-113 time=161 busy=0 rx=10 + * ACS: 2: min_nf=-114 interference_factor=0.00621118 nf=-114 time=161 busy=0 rx=1 + * ACS: 3: min_nf=-114 interference_factor=2.51189e-23 nf=-113 time=162 busy=0 rx=0 + * ACS: 4: min_nf=-114 interference_factor=2.51189e-23 nf=-113 time=162 busy=0 rx=0 + * ACS: 5: min_nf=-114 interference_factor=0.00617284 nf=-113 time=162 busy=0 rx=1 + * ACS: * interference factor average: 0.0148992 + * ACS: Survey analysis for channel 13 (2472 MHz) + * ACS: 1: min_nf=-114 interference_factor=0.0745342 nf=-114 time=161 busy=0 rx=12 + * ACS: 2: min_nf=-114 interference_factor=0.0555556 nf=-114 time=162 busy=0 rx=9 + * ACS: 3: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=162 busy=0 rx=0 + * ACS: 4: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=162 busy=0 rx=0 + * ACS: 5: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=162 busy=0 rx=0 + * ACS: * interference factor average: 0.0260179 + * ACS: Survey analysis for selected bandwidth 20MHz + * ACS: * channel 1: total interference = 0.121432 + * ACS: * channel 2: total interference = 0.137512 + * ACS: * channel 3: total interference = 0.369757 + * ACS: * channel 4: total interference = 0.546338 + * ACS: * channel 5: total interference = 0.690538 + * ACS: * channel 6: total interference = 0.762242 + * ACS: * channel 7: total interference = 0.756092 + * ACS: * channel 8: total interference = 0.537451 + * ACS: * channel 9: total interference = 0.332313 + * ACS: * channel 10: total interference = 0.152182 + * ACS: * channel 11: total interference = 0.0916111 + * ACS: * channel 12: total interference = 0.0816809 + * ACS: * channel 13: total interference = 0.0680776 + * ACS: Ideal channel is 13 (2472 MHz) with total interference factor of 0.0680776 + * + * [1] http://en.wikipedia.org/wiki/Near_and_far_field + */ + + +static int acs_request_scan(struct hostapd_iface *iface); + + +static void acs_clean_chan_surveys(struct hostapd_channel_data *chan) +{ + struct freq_survey *survey, *tmp; + + if (dl_list_empty(&chan->survey_list)) + return; + + dl_list_for_each_safe(survey, tmp, &chan->survey_list, + struct freq_survey, list) { + dl_list_del(&survey->list); + os_free(survey); + } +} + + +static void acs_cleanup(struct hostapd_iface *iface) +{ + int i; + struct hostapd_channel_data *chan; + + for (i = 0; i < iface->current_mode->num_channels; i++) { + chan = &iface->current_mode->channels[i]; + + if (chan->flag & HOSTAPD_CHAN_SURVEY_LIST_INITIALIZED) + acs_clean_chan_surveys(chan); + + dl_list_init(&chan->survey_list); + chan->flag |= HOSTAPD_CHAN_SURVEY_LIST_INITIALIZED; + chan->min_nf = 0; + } + + iface->chans_surveyed = 0; + iface->acs_num_completed_scans = 0; +} + + +static void acs_fail(struct hostapd_iface *iface) +{ + wpa_printf(MSG_ERROR, "ACS: Failed to start"); + acs_cleanup(iface); +} + + +static long double +acs_survey_interference_factor(struct freq_survey *survey, s8 min_nf) +{ + long double factor, busy, total; + + if (survey->filled & SURVEY_HAS_CHAN_TIME_BUSY) + busy = survey->channel_time_busy; + else if (survey->filled & SURVEY_HAS_CHAN_TIME_RX) + busy = survey->channel_time_rx; + else { + /* This shouldn't really happen as survey data is checked in + * acs_sanity_check() */ + wpa_printf(MSG_ERROR, "ACS: Survey data missing"); + return 0; + } + + total = survey->channel_time; + + if (survey->filled & SURVEY_HAS_CHAN_TIME_TX) { + busy -= survey->channel_time_tx; + total -= survey->channel_time_tx; + } + + /* TODO: figure out the best multiplier for noise floor base */ + factor = pow(10, survey->nf / 5.0L) + + (busy / total) * + pow(2, pow(10, (long double) survey->nf / 10.0L) - + pow(10, (long double) min_nf / 10.0L)); + + return factor; +} + + +static void +acs_survey_chan_interference_factor(struct hostapd_iface *iface, + struct hostapd_channel_data *chan) +{ + struct freq_survey *survey; + unsigned int i = 0; + long double int_factor = 0; + + if (dl_list_empty(&chan->survey_list)) + return; + + if (chan->flag & HOSTAPD_CHAN_DISABLED) + return; + + chan->interference_factor = 0; + + dl_list_for_each(survey, &chan->survey_list, struct freq_survey, list) + { + int_factor = acs_survey_interference_factor(survey, + iface->lowest_nf); + chan->interference_factor += int_factor; + wpa_printf(MSG_DEBUG, "ACS: %d: min_nf=%d interference_factor=%Lg nf=%d time=%lu busy=%lu rx=%lu", + ++i, chan->min_nf, int_factor, + survey->nf, (unsigned long) survey->channel_time, + (unsigned long) survey->channel_time_busy, + (unsigned long) survey->channel_time_rx); + } + + chan->interference_factor = chan->interference_factor / + dl_list_len(&chan->survey_list); +} + + +static int acs_usable_ht40_chan(struct hostapd_channel_data *chan) +{ + const int allowed[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 149, + 157, 184, 192 }; + 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)) { + wpa_printf(MSG_ERROR, "ACS: Survey is missing noise floor"); + return 0; + } + + if (!(survey->filled & SURVEY_HAS_CHAN_TIME)) { + wpa_printf(MSG_ERROR, "ACS: Survey is missing channel time"); + return 0; + } + + if (!(survey->filled & SURVEY_HAS_CHAN_TIME_BUSY) && + !(survey->filled & SURVEY_HAS_CHAN_TIME_RX)) { + wpa_printf(MSG_ERROR, "ACS: Survey is missing RX and busy time (at least one is required)"); + return 0; + } + + return 1; +} + + +static int acs_survey_list_is_sufficient(struct hostapd_channel_data *chan) +{ + struct freq_survey *survey; + + dl_list_for_each(survey, &chan->survey_list, struct freq_survey, list) + { + if (!acs_survey_is_sufficient(survey)) { + wpa_printf(MSG_ERROR, "ACS: Channel %d has insufficient survey data", + chan->chan); + return 0; + } + } + + return 1; + +} + + +static int acs_surveys_are_sufficient(struct hostapd_iface *iface) +{ + int i; + struct hostapd_channel_data *chan; + int valid = 0; + + 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++; + } + + /* We need at least survey data for one channel */ + return !!valid; +} + + +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; +} + + +static void acs_survey_all_chans_intereference_factor( + struct hostapd_iface *iface) +{ + int i; + struct hostapd_channel_data *chan; + + for (i = 0; i < iface->current_mode->num_channels; i++) { + chan = &iface->current_mode->channels[i]; + + if (!acs_usable_chan(chan)) + continue; + + wpa_printf(MSG_DEBUG, "ACS: Survey analysis for channel %d (%d MHz)", + chan->chan, chan->freq); + + acs_survey_chan_interference_factor(iface, chan); + + wpa_printf(MSG_DEBUG, "ACS: * interference factor average: %Lg", + chan->interference_factor); + } +} + + +static struct hostapd_channel_data *acs_find_chan(struct hostapd_iface *iface, + int freq) +{ + struct hostapd_channel_data *chan; + int i; + + for (i = 0; i < iface->current_mode->num_channels; i++) { + chan = &iface->current_mode->channels[i]; + + if (chan->flag & HOSTAPD_CHAN_DISABLED) + continue; + + if (chan->freq == freq) + return chan; + } + + return NULL; +} + + +/* + * At this point it's assumed chan->interface_factor has been computed. + * This function should be reusable regardless of interference computation + * option (survey, BSS, spectral, ...). chan->interference factor must be + * summable (i.e., must be always greater than zero). + */ +static struct hostapd_channel_data * +acs_find_ideal_chan(struct hostapd_iface *iface) +{ + struct hostapd_channel_data *chan, *adj_chan, *ideal_chan = NULL, + *rand_chan = NULL; + long double factor, ideal_factor = 0; + int i, j; + int n_chans = 1; + + /* TODO: HT40- support */ + + if (iface->conf->ieee80211n && + iface->conf->secondary_channel == -1) { + wpa_printf(MSG_ERROR, "ACS: HT40- is not supported yet. Please try HT40+"); + return NULL; + } + + if (iface->conf->ieee80211n && + iface->conf->secondary_channel) + n_chans = 2; + + if (iface->conf->ieee80211ac && + iface->conf->vht_oper_chwidth == 1) + n_chans = 4; + + /* TODO: VHT80+80, VHT160. Update acs_adjust_vht_center_freq() too. */ + + 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); + + for (i = 0; i < iface->current_mode->num_channels; i++) { + chan = &iface->current_mode->channels[i]; + + if (chan->flag & HOSTAPD_CHAN_DISABLED) + 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 && + iface->conf->ieee80211n && + iface->conf->secondary_channel && + !acs_usable_ht40_chan(chan)) { + wpa_printf(MSG_DEBUG, "ACS: Channel %d: not allowed as primary channel for HT40", + chan->chan); + continue; + } + + factor = 0; + if (acs_usable_chan(chan)) + factor = chan->interference_factor; + + for (j = 1; j < n_chans; j++) { + adj_chan = acs_find_chan(iface, chan->freq + (j * 20)); + if (!adj_chan) + break; + + if (acs_usable_chan(adj_chan)) + factor += adj_chan->interference_factor; + } + + if (j != n_chans) { + wpa_printf(MSG_DEBUG, "ACS: Channel %d: not enough bandwidth", + chan->chan); + continue; + } + + /* 2.4 GHz has overlapping 20 MHz channels. Include adjacent + * channel interference factor. */ + if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211B || + iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G) { + for (j = 0; j < n_chans; j++) { + /* TODO: perhaps a multiplier should be used + * here? */ + + adj_chan = acs_find_chan(iface, chan->freq + + (j * 20) - 5); + if (adj_chan && acs_usable_chan(adj_chan)) + factor += adj_chan->interference_factor; + + adj_chan = acs_find_chan(iface, chan->freq + + (j * 20) - 10); + if (adj_chan && acs_usable_chan(adj_chan)) + factor += adj_chan->interference_factor; + + adj_chan = acs_find_chan(iface, chan->freq + + (j * 20) + 5); + if (adj_chan && acs_usable_chan(adj_chan)) + factor += adj_chan->interference_factor; + + adj_chan = acs_find_chan(iface, chan->freq + + (j * 20) + 10); + if (adj_chan && acs_usable_chan(adj_chan)) + factor += adj_chan->interference_factor; + } + } + + wpa_printf(MSG_DEBUG, "ACS: * channel %d: total interference = %Lg", + chan->chan, factor); + + if (acs_usable_chan(chan) && + (!ideal_chan || factor < ideal_factor)) { + ideal_factor = factor; + ideal_chan = chan; + } + + /* This channel would at least be usable */ + if (!rand_chan) + rand_chan = chan; + } + + if (ideal_chan) { + wpa_printf(MSG_DEBUG, "ACS: Ideal channel is %d (%d MHz) with total interference factor of %Lg", + ideal_chan->chan, ideal_chan->freq, ideal_factor); + return ideal_chan; + } + + return rand_chan; +} + + +static void acs_adjust_vht_center_freq(struct hostapd_iface *iface) +{ + wpa_printf(MSG_DEBUG, "ACS: Adjusting VHT center frequency"); + + switch (iface->conf->vht_oper_chwidth) { + case VHT_CHANWIDTH_USE_HT: + iface->conf->vht_oper_centr_freq_seg0_idx = + iface->conf->channel + 2; + break; + case VHT_CHANWIDTH_80MHZ: + iface->conf->vht_oper_centr_freq_seg0_idx = + iface->conf->channel + 6; + 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"); + break; + } +} + + +static int acs_study_survey_based(struct hostapd_iface *iface) +{ + wpa_printf(MSG_DEBUG, "ACS: Trying survey-based ACS"); + + if (!iface->chans_surveyed) { + wpa_printf(MSG_ERROR, "ACS: Unable to collect survey data"); + return -1; + } + + if (!acs_surveys_are_sufficient(iface)) { + wpa_printf(MSG_ERROR, "ACS: Surveys have insufficient data"); + return -1; + } + + acs_survey_all_chans_intereference_factor(iface); + return 0; +} + + +static int acs_study_options(struct hostapd_iface *iface) +{ + int err; + + err = acs_study_survey_based(iface); + if (err == 0) + return 0; + + /* TODO: If no surveys are available/sufficient this is a good + * place to fallback to BSS-based ACS */ + + return -1; +} + + +static void acs_study(struct hostapd_iface *iface) +{ + struct hostapd_channel_data *ideal_chan; + int err; + + err = acs_study_options(iface); + if (err < 0) { + wpa_printf(MSG_ERROR, "ACS: All study options have failed"); + goto fail; + } + + ideal_chan = acs_find_ideal_chan(iface); + if (!ideal_chan) { + wpa_printf(MSG_ERROR, "ACS: Failed to compute ideal channel"); + err = -1; + goto fail; + } + + iface->conf->channel = ideal_chan->chan; + + if (iface->conf->ieee80211ac) + acs_adjust_vht_center_freq(iface); + + err = 0; +fail: + /* + * hostapd_setup_interface_complete() will return -1 on failure, + * 0 on success and 0 is HOSTAPD_CHAN_VALID :) + */ + if (hostapd_acs_completed(iface, err) == HOSTAPD_CHAN_VALID) { + acs_cleanup(iface); + return; + } + + /* This can possibly happen if channel parameters (secondary + * channel, center frequencies) are misconfigured */ + wpa_printf(MSG_ERROR, "ACS: Possibly channel configuration is invalid, please report this along with your config file."); + acs_fail(iface); +} + + +static void acs_scan_complete(struct hostapd_iface *iface) +{ + int err; + + iface->scan_cb = NULL; + + wpa_printf(MSG_DEBUG, "ACS: Using survey based algorithm (acs_num_scans=%d)", + iface->conf->acs_num_scans); + + err = hostapd_drv_get_survey(iface->bss[0], 0); + if (err) { + wpa_printf(MSG_ERROR, "ACS: Failed to get survey data"); + acs_fail(iface); + } + + if (++iface->acs_num_completed_scans < iface->conf->acs_num_scans) { + err = acs_request_scan(iface); + if (err) { + wpa_printf(MSG_ERROR, "ACS: Failed to request scan"); + goto fail; + } + + return; + } + + acs_study(iface); + return; +fail: + hostapd_acs_completed(iface, 1); + acs_fail(iface); +} + + +static int acs_request_scan(struct hostapd_iface *iface) +{ + struct wpa_driver_scan_params params; + struct hostapd_channel_data *chan; + int i, *freq; + + os_memset(¶ms, 0, sizeof(params)); + params.freqs = os_calloc(iface->current_mode->num_channels + 1, + sizeof(params.freqs[0])); + if (params.freqs == NULL) + return -1; + + freq = params.freqs; + for (i = 0; i < iface->current_mode->num_channels; i++) { + chan = &iface->current_mode->channels[i]; + if (chan->flag & HOSTAPD_CHAN_DISABLED) + continue; + + *freq++ = chan->freq; + } + *freq = 0; + + iface->scan_cb = acs_scan_complete; + + wpa_printf(MSG_DEBUG, "ACS: Scanning %d / %d", + iface->acs_num_completed_scans + 1, + iface->conf->acs_num_scans); + + if (hostapd_driver_scan(iface->bss[0], ¶ms) < 0) { + wpa_printf(MSG_ERROR, "ACS: Failed to request initial scan"); + acs_cleanup(iface); + return -1; + } + + os_free(params.freqs); + return 0; +} + + +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"); + + acs_cleanup(iface); + + err = acs_request_scan(iface); + if (err < 0) + return HOSTAPD_CHAN_INVALID; + + hostapd_set_state(iface, HAPD_IFACE_ACS); + wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, ACS_EVENT_STARTED); + + return HOSTAPD_CHAN_ACS; +} diff -Nru wpa-1.0/src/ap/acs.h wpa-2.1/src/ap/acs.h --- wpa-1.0/src/ap/acs.h 1970-01-01 00:00:00.000000000 +0000 +++ wpa-2.1/src/ap/acs.h 2014-02-04 11:23:35.000000000 +0000 @@ -0,0 +1,27 @@ +/* + * ACS - Automatic Channel Selection module + * Copyright (c) 2011, Atheros Communications + * Copyright (c) 2013, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef ACS_H +#define ACS_H + +#ifdef CONFIG_ACS + +enum hostapd_chan_status acs_init(struct hostapd_iface *iface); + +#else /* CONFIG_ACS */ + +static inline enum hostapd_chan_status acs_init(struct hostapd_iface *iface) +{ + wpa_printf(MSG_ERROR, "ACS was disabled on your build, rebuild hostapd with CONFIG_ACS=y or set channel"); + return HOSTAPD_CHAN_INVALID; +} + +#endif /* CONFIG_ACS */ + +#endif /* ACS_H */ diff -Nru wpa-1.0/src/ap/ap_config.c wpa-2.1/src/ap/ap_config.c --- wpa-1.0/src/ap/ap_config.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/ap/ap_config.c 2014-02-04 11:23:35.000000000 +0000 @@ -1,15 +1,9 @@ /* * hostapd / Configuration helper functions - * Copyright (c) 2003-2009, Jouni Malinen + * Copyright (c) 2003-2014, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "utils/includes.h" @@ -93,6 +87,10 @@ #ifdef CONFIG_IEEE80211R bss->ft_over_ds = 1; #endif /* CONFIG_IEEE80211R */ + + bss->radius_das_time_window = 300; + + bss->sae_anti_clogging_threshold = 5; } @@ -108,9 +106,9 @@ const struct hostapd_wmm_ac_params ac_be = { aCWmin, aCWmax, 3, 0, 0 }; /* best effort traffic */ const struct hostapd_wmm_ac_params ac_vi = /* video traffic */ - { aCWmin - 1, aCWmin, 2, 3000 / 32, 1 }; + { aCWmin - 1, aCWmin, 2, 3000 / 32, 0 }; const struct hostapd_wmm_ac_params ac_vo = /* voice traffic */ - { aCWmin - 2, aCWmin - 1, 2, 1500 / 32, 1 }; + { aCWmin - 2, aCWmin - 1, 2, 1500 / 32, 0 }; const struct hostapd_tx_queue_params txq_bk = { 7, ecw2cw(aCWmin), ecw2cw(aCWmax), 0 }; const struct hostapd_tx_queue_params txq_be = @@ -132,9 +130,17 @@ os_free(bss); return NULL; } + conf->bss = os_calloc(1, sizeof(struct hostapd_bss_config *)); + if (conf->bss == NULL) { + os_free(conf); + os_free(bss); + return NULL; + } + conf->bss[0] = bss; bss->radius = os_zalloc(sizeof(*bss->radius)); if (bss->radius == NULL) { + os_free(conf->bss); os_free(conf); os_free(bss); return NULL; @@ -143,7 +149,6 @@ hostapd_config_defaults_bss(bss); conf->num_bss = 1; - conf->bss = bss; conf->beacon_int = 100; conf->rts_threshold = -1; /* use driver default: 2347 */ @@ -162,6 +167,21 @@ conf->ht_capab = HT_CAP_INFO_SMPS_DISABLED; + conf->ap_table_max_size = 255; + conf->ap_table_expiration_time = 60; + +#ifdef CONFIG_TESTING_OPTIONS + conf->ignore_probe_probability = 0.0d; + conf->ignore_auth_probability = 0.0d; + conf->ignore_assoc_probability = 0.0d; + conf->ignore_reassoc_probability = 0.0d; + conf->corrupt_gtk_rekey_mic_probability = 0.0d; +#endif /* CONFIG_TESTING_OPTIONS */ + +#ifdef CONFIG_ACS + conf->acs_num_scans = 5; +#endif /* CONFIG_ACS */ + return conf; } @@ -342,6 +362,30 @@ } +struct hostapd_radius_attr * +hostapd_config_get_radius_attr(struct hostapd_radius_attr *attr, u8 type) +{ + for (; attr; attr = attr->next) { + if (attr->type == type) + return attr; + } + return NULL; +} + + +static void hostapd_config_free_radius_attr(struct hostapd_radius_attr *attr) +{ + struct hostapd_radius_attr *prev; + + while (attr) { + prev = attr; + attr = attr->next; + wpabuf_free(prev->val); + os_free(prev); + } +} + + static void hostapd_config_free_eap_user(struct hostapd_eap_user *user) { os_free(user->identity); @@ -360,7 +404,7 @@ } -static void hostapd_config_free_bss(struct hostapd_bss_config *conf) +void hostapd_config_free_bss(struct hostapd_bss_config *conf) { struct hostapd_wpa_psk *psk, *prev; struct hostapd_eap_user *user, *prev_user; @@ -388,22 +432,27 @@ user = user->next; hostapd_config_free_eap_user(prev_user); } + os_free(conf->eap_user_sqlite); - os_free(conf->dump_log_name); os_free(conf->eap_req_id_text); os_free(conf->accept_mac); os_free(conf->deny_mac); os_free(conf->nas_identifier); - hostapd_config_free_radius(conf->radius->auth_servers, - conf->radius->num_auth_servers); - hostapd_config_free_radius(conf->radius->acct_servers, - conf->radius->num_acct_servers); + if (conf->radius) { + hostapd_config_free_radius(conf->radius->auth_servers, + conf->radius->num_auth_servers); + hostapd_config_free_radius(conf->radius->acct_servers, + conf->radius->num_acct_servers); + } + hostapd_config_free_radius_attr(conf->radius_auth_req_attr); + hostapd_config_free_radius_attr(conf->radius_acct_req_attr); os_free(conf->rsn_preauth_interfaces); os_free(conf->ctrl_interface); os_free(conf->ca_cert); os_free(conf->server_cert); os_free(conf->private_key); os_free(conf->private_key_passwd); + os_free(conf->ocsp_stapling_response); os_free(conf->dh_file); os_free(conf->pac_opaque_encr_key); os_free(conf->eap_fast_a_id); @@ -412,20 +461,8 @@ os_free(conf->radius_server_clients); os_free(conf->test_socket); os_free(conf->radius); + os_free(conf->radius_das_shared_secret); hostapd_config_free_vlan(conf); - if (conf->ssid.dyn_vlan_keys) { - struct hostapd_ssid *ssid = &conf->ssid; - size_t i; - for (i = 0; i <= ssid->max_dyn_vlan_keys; i++) { - if (ssid->dyn_vlan_keys[i] == NULL) - continue; - hostapd_config_free_wep(ssid->dyn_vlan_keys[i]); - os_free(ssid->dyn_vlan_keys[i]); - } - os_free(ssid->dyn_vlan_keys); - ssid->dyn_vlan_keys = NULL; - } - os_free(conf->time_zone); #ifdef CONFIG_IEEE80211R @@ -468,9 +505,36 @@ os_free(conf->model_description); os_free(conf->model_url); os_free(conf->upc); + wpabuf_free(conf->wps_nfc_dh_pubkey); + wpabuf_free(conf->wps_nfc_dh_privkey); + wpabuf_free(conf->wps_nfc_dev_pw); #endif /* CONFIG_WPS */ os_free(conf->roaming_consortium); + os_free(conf->venue_name); + os_free(conf->nai_realm_data); + os_free(conf->network_auth_type); + os_free(conf->anqp_3gpp_cell_net); + os_free(conf->domain_name); + +#ifdef CONFIG_RADIUS_TEST + os_free(conf->dump_msk_file); +#endif /* CONFIG_RADIUS_TEST */ + +#ifdef CONFIG_HS20 + os_free(conf->hs20_oper_friendly_name); + os_free(conf->hs20_wan_metrics); + os_free(conf->hs20_connection_capability); + os_free(conf->hs20_operating_class); +#endif /* CONFIG_HS20 */ + + wpabuf_free(conf->vendor_elements); + + os_free(conf->sae_groups); + + os_free(conf->server_id); + + os_free(conf); } @@ -486,7 +550,7 @@ return; for (i = 0; i < conf->num_bss; i++) - hostapd_config_free_bss(&conf->bss[i]); + hostapd_config_free_bss(conf->bss[i]); os_free(conf->bss); os_free(conf->supported_rates); os_free(conf->basic_rates); @@ -546,11 +610,23 @@ } -const char * hostapd_get_vlan_id_ifname(struct hostapd_vlan *vlan, int vlan_id) +int hostapd_vlan_id_valid(struct hostapd_vlan *vlan, int vlan_id) { struct hostapd_vlan *v = vlan; while (v) { if (v->vlan_id == vlan_id || v->vlan_id == VLAN_ID_WILDCARD) + return 1; + v = v->next; + } + return 0; +} + + +const char * hostapd_get_vlan_id_ifname(struct hostapd_vlan *vlan, int vlan_id) +{ + struct hostapd_vlan *v = vlan; + while (v) { + if (v->vlan_id == vlan_id) return v->ifname; v = v->next; } @@ -559,14 +635,31 @@ const u8 * hostapd_get_psk(const struct hostapd_bss_config *conf, - const u8 *addr, const u8 *prev_psk) + const u8 *addr, const u8 *p2p_dev_addr, + const u8 *prev_psk) { struct hostapd_wpa_psk *psk; int next_ok = prev_psk == NULL; + if (p2p_dev_addr) { + wpa_printf(MSG_DEBUG, "Searching a PSK for " MACSTR + " p2p_dev_addr=" MACSTR " prev_psk=%p", + MAC2STR(addr), MAC2STR(p2p_dev_addr), prev_psk); + if (!is_zero_ether_addr(p2p_dev_addr)) + addr = NULL; /* Use P2P Device Address for matching */ + } else { + wpa_printf(MSG_DEBUG, "Searching a PSK for " MACSTR + " prev_psk=%p", + MAC2STR(addr), prev_psk); + } + for (psk = conf->ssid.wpa_psk; psk != NULL; psk = psk->next) { if (next_ok && - (psk->group || os_memcmp(psk->addr, addr, ETH_ALEN) == 0)) + (psk->group || + (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))) return psk->psk; if (psk->psk == prev_psk) @@ -577,55 +670,216 @@ } -const struct hostapd_eap_user * -hostapd_get_eap_user(const struct hostapd_bss_config *conf, const u8 *identity, - size_t identity_len, int phase2) -{ - struct hostapd_eap_user *user = conf->eap_user; - -#ifdef CONFIG_WPS - if (conf->wps_state && identity_len == WSC_ID_ENROLLEE_LEN && - os_memcmp(identity, WSC_ID_ENROLLEE, WSC_ID_ENROLLEE_LEN) == 0) { - static struct hostapd_eap_user wsc_enrollee; - os_memset(&wsc_enrollee, 0, sizeof(wsc_enrollee)); - wsc_enrollee.methods[0].method = eap_server_get_type( - "WSC", &wsc_enrollee.methods[0].vendor); - return &wsc_enrollee; - } - - if (conf->wps_state && identity_len == WSC_ID_REGISTRAR_LEN && - os_memcmp(identity, WSC_ID_REGISTRAR, WSC_ID_REGISTRAR_LEN) == 0) { - static struct hostapd_eap_user wsc_registrar; - os_memset(&wsc_registrar, 0, sizeof(wsc_registrar)); - wsc_registrar.methods[0].method = eap_server_get_type( - "WSC", &wsc_registrar.methods[0].vendor); - wsc_registrar.password = (u8 *) conf->ap_pin; - wsc_registrar.password_len = conf->ap_pin ? - os_strlen(conf->ap_pin) : 0; - return &wsc_registrar; +static int hostapd_config_check_bss(struct hostapd_bss_config *bss, + struct hostapd_config *conf, + int full_config) +{ + if (full_config && bss->ieee802_1x && !bss->eap_server && + !bss->radius->auth_servers) { + wpa_printf(MSG_ERROR, "Invalid IEEE 802.1X configuration (no " + "EAP authenticator configured)."); + return -1; } -#endif /* CONFIG_WPS */ - while (user) { - if (!phase2 && user->identity == NULL) { - /* Wildcard match */ - break; + if (bss->wpa) { + int wep, i; + + wep = bss->default_wep_key_len > 0 || + bss->individual_wep_key_len > 0; + for (i = 0; i < NUM_WEP_KEYS; i++) { + if (bss->ssid.wep.keys_set) { + wep = 1; + break; + } } - if (user->phase2 == !!phase2 && user->wildcard_prefix && - identity_len >= user->identity_len && - os_memcmp(user->identity, identity, user->identity_len) == - 0) { - /* Wildcard prefix match */ - break; + if (wep) { + wpa_printf(MSG_ERROR, "WEP configuration in a WPA network is not supported"); + return -1; } + } - if (user->phase2 == !!phase2 && - user->identity_len == identity_len && - os_memcmp(user->identity, identity, identity_len) == 0) - break; - user = user->next; + if (full_config && bss->wpa && + bss->wpa_psk_radius != PSK_RADIUS_IGNORED && + bss->macaddr_acl != USE_EXTERNAL_RADIUS_AUTH) { + wpa_printf(MSG_ERROR, "WPA-PSK using RADIUS enabled, but no " + "RADIUS checking (macaddr_acl=2) enabled."); + return -1; + } + + if (full_config && bss->wpa && (bss->wpa_key_mgmt & WPA_KEY_MGMT_PSK) && + bss->ssid.wpa_psk == NULL && bss->ssid.wpa_passphrase == NULL && + bss->ssid.wpa_psk_file == NULL && + (bss->wpa_psk_radius != PSK_RADIUS_REQUIRED || + bss->macaddr_acl != USE_EXTERNAL_RADIUS_AUTH)) { + wpa_printf(MSG_ERROR, "WPA-PSK enabled, but PSK or passphrase " + "is not configured."); + return -1; + } + + if (full_config && hostapd_mac_comp_empty(bss->bssid) != 0) { + size_t i; + + for (i = 0; i < conf->num_bss; i++) { + if (conf->bss[i] != bss && + (hostapd_mac_comp(conf->bss[i]->bssid, + bss->bssid) == 0)) { + wpa_printf(MSG_ERROR, "Duplicate BSSID " MACSTR + " on interface '%s' and '%s'.", + MAC2STR(bss->bssid), + conf->bss[i]->iface, bss->iface); + return -1; + } + } + } + +#ifdef CONFIG_IEEE80211R + if (full_config && wpa_key_mgmt_ft(bss->wpa_key_mgmt) && + (bss->nas_identifier == NULL || + os_strlen(bss->nas_identifier) < 1 || + os_strlen(bss->nas_identifier) > FT_R0KH_ID_MAX_LEN)) { + wpa_printf(MSG_ERROR, "FT (IEEE 802.11r) requires " + "nas_identifier to be configured as a 1..48 octet " + "string"); + return -1; + } +#endif /* CONFIG_IEEE80211R */ + +#ifdef CONFIG_IEEE80211N + if (full_config && conf->ieee80211n && + conf->hw_mode == HOSTAPD_MODE_IEEE80211B) { + bss->disable_11n = 1; + wpa_printf(MSG_ERROR, "HT (IEEE 802.11n) in 11b mode is not " + "allowed, disabling HT capabilites"); + } + + if (full_config && conf->ieee80211n && + bss->ssid.security_policy == SECURITY_STATIC_WEP) { + bss->disable_11n = 1; + wpa_printf(MSG_ERROR, "HT (IEEE 802.11n) with WEP is not " + "allowed, disabling HT capabilities"); + } + + if (full_config && conf->ieee80211n && 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_11n = 1; + wpa_printf(MSG_ERROR, "HT (IEEE 802.11n) with WPA/WPA2 " + "requires CCMP/GCMP to be enabled, disabling HT " + "capabilities"); + } +#endif /* CONFIG_IEEE80211N */ + +#ifdef CONFIG_WPS2 + if (full_config && bss->wps_state && bss->ignore_broadcast_ssid) { + wpa_printf(MSG_INFO, "WPS: ignore_broadcast_ssid " + "configuration forced WPS to be disabled"); + bss->wps_state = 0; + } + + if (full_config && bss->wps_state && + bss->ssid.wep.keys_set && bss->wpa == 0) { + wpa_printf(MSG_INFO, "WPS: WEP configuration forced WPS to be " + "disabled"); + bss->wps_state = 0; + } + + if (full_config && bss->wps_state && bss->wpa && + (!(bss->wpa & 2) || + !(bss->rsn_pairwise & WPA_CIPHER_CCMP))) { + wpa_printf(MSG_INFO, "WPS: WPA/TKIP configuration without " + "WPA2/CCMP forced WPS to be disabled"); + bss->wps_state = 0; + } +#endif /* CONFIG_WPS2 */ + +#ifdef CONFIG_HS20 + if (full_config && bss->hs20 && + (!(bss->wpa & 2) || + !(bss->rsn_pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP | + WPA_CIPHER_CCMP_256 | + WPA_CIPHER_GCMP_256)))) { + wpa_printf(MSG_ERROR, "HS 2.0: WPA2-Enterprise/CCMP " + "configuration is required for Hotspot 2.0 " + "functionality"); + return -1; + } +#endif /* CONFIG_HS20 */ + + return 0; +} + + +int hostapd_config_check(struct hostapd_config *conf, int full_config) +{ + size_t i; + + if (full_config && conf->ieee80211d && + (!conf->country[0] || !conf->country[1])) { + wpa_printf(MSG_ERROR, "Cannot enable IEEE 802.11d without " + "setting the country_code"); + return -1; + } + + if (full_config && conf->ieee80211h && !conf->ieee80211d) { + wpa_printf(MSG_ERROR, "Cannot enable IEEE 802.11h without " + "IEEE 802.11d enabled"); + return -1; + } + + for (i = 0; i < conf->num_bss; i++) { + if (hostapd_config_check_bss(conf->bss[i], conf, full_config)) + return -1; } - return user; + return 0; +} + + +void hostapd_set_security_params(struct hostapd_bss_config *bss) +{ + if (bss->individual_wep_key_len == 0) { + /* individual keys are not use; can use key idx0 for + * broadcast keys */ + bss->broadcast_key_idx_min = 0; + } + + 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); + + bss->radius->auth_server = bss->radius->auth_servers; + bss->radius->acct_server = bss->radius->acct_servers; + + if (bss->wpa && bss->ieee802_1x) { + bss->ssid.security_policy = SECURITY_WPA; + } else if (bss->wpa) { + bss->ssid.security_policy = SECURITY_WPA_PSK; + } else if (bss->ieee802_1x) { + int cipher = WPA_CIPHER_NONE; + bss->ssid.security_policy = SECURITY_IEEE_802_1X; + bss->ssid.wep.default_len = bss->default_wep_key_len; + if (bss->default_wep_key_len) + cipher = bss->default_wep_key_len >= 13 ? + WPA_CIPHER_WEP104 : WPA_CIPHER_WEP40; + bss->wpa_group = cipher; + bss->wpa_pairwise = cipher; + bss->rsn_pairwise = cipher; + } else if (bss->ssid.wep.keys_set) { + int cipher = WPA_CIPHER_WEP40; + if (bss->ssid.wep.len[0] >= 13) + cipher = WPA_CIPHER_WEP104; + bss->ssid.security_policy = SECURITY_STATIC_WEP; + bss->wpa_group = cipher; + bss->wpa_pairwise = cipher; + bss->rsn_pairwise = cipher; + } else { + bss->ssid.security_policy = SECURITY_PLAINTEXT; + bss->wpa_group = WPA_CIPHER_NONE; + bss->wpa_pairwise = WPA_CIPHER_NONE; + bss->rsn_pairwise = WPA_CIPHER_NONE; + } } diff -Nru wpa-1.0/src/ap/ap_config.h wpa-2.1/src/ap/ap_config.h --- wpa-1.0/src/ap/ap_config.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/ap/ap_config.h 2014-02-04 11:23:35.000000000 +0000 @@ -1,15 +1,9 @@ /* * hostapd / Configuration definitions and helpers functions - * Copyright (c) 2003-2009, Jouni Malinen + * Copyright (c) 2003-2012, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef HOSTAPD_CONFIG_H @@ -18,6 +12,7 @@ #include "common/defs.h" #include "ip_addr.h" #include "common/wpa_common.h" +#include "common/ieee802_11_common.h" #include "wps/wps.h" #define MAX_STA_COUNT 2007 @@ -54,9 +49,12 @@ } secpolicy; struct hostapd_ssid { - char ssid[HOSTAPD_MAX_SSID_LEN + 1]; + u8 ssid[HOSTAPD_MAX_SSID_LEN]; size_t ssid_len; - int ssid_set; + unsigned int ssid_set:1; + unsigned int utf8_ssid:1; + unsigned int wpa_passphrase_set:1; + unsigned int wpa_psk_set:1; char vlan[IFNAMSIZ + 1]; secpolicy security_policy; @@ -71,11 +69,13 @@ #define DYNAMIC_VLAN_OPTIONAL 1 #define DYNAMIC_VLAN_REQUIRED 2 int dynamic_vlan; +#define DYNAMIC_VLAN_NAMING_WITHOUT_DEVICE 0 +#define DYNAMIC_VLAN_NAMING_WITH_DEVICE 1 +#define DYNAMIC_VLAN_NAMING_END 2 + int vlan_naming; #ifdef CONFIG_FULL_DYNAMIC_VLAN char *vlan_tagged_interface; #endif /* CONFIG_FULL_DYNAMIC_VLAN */ - struct hostapd_wep_keys **dyn_vlan_keys; - size_t max_dyn_vlan_keys; }; @@ -97,14 +97,19 @@ }; #define PMK_LEN 32 +struct hostapd_sta_wpa_psk_short { + struct hostapd_sta_wpa_psk_short *next; + u8 psk[PMK_LEN]; +}; + struct hostapd_wpa_psk { struct hostapd_wpa_psk *next; int group; u8 psk[PMK_LEN]; u8 addr[ETH_ALEN]; + u8 p2p_dev_addr[ETH_ALEN]; }; -#define EAP_USER_MAX_METHODS 8 struct hostapd_eap_user { struct hostapd_eap_user *next; u8 *identity; @@ -112,7 +117,7 @@ struct { int vendor; u32 method; - } methods[EAP_USER_MAX_METHODS]; + } methods[EAP_MAX_METHODS]; u8 *password; size_t password_len; int phase2; @@ -123,6 +128,12 @@ int ttls_auth; /* EAP_TTLS_AUTH_* bitfield */ }; +struct hostapd_radius_attr { + u8 type; + struct wpabuf *val; + struct hostapd_radius_attr *next; +}; + #define NUM_TX_QUEUES 4 @@ -133,14 +144,6 @@ int burst; /* maximum burst time in 0.1 ms, i.e., 10 = 1 ms */ }; -struct hostapd_wmm_ac_params { - int cwmin; - int cwmax; - int aifs; - int txop_limit; /* in units of 32us */ - int admission_control_mandatory; -}; - #define MAX_ROAMING_CONSORTIUM_LEN 15 @@ -149,12 +152,36 @@ u8 oi[MAX_ROAMING_CONSORTIUM_LEN]; }; +struct hostapd_lang_string { + u8 lang[3]; + u8 name_len; + u8 name[252]; +}; + +#define MAX_NAI_REALMS 10 +#define MAX_NAI_REALMLEN 255 +#define MAX_NAI_EAP_METHODS 5 +#define MAX_NAI_AUTH_TYPES 4 +struct hostapd_nai_realm_data { + u8 encoding; + char realm_buf[MAX_NAI_REALMLEN + 1]; + char *realm[MAX_NAI_REALMS]; + u8 eap_method_count; + struct hostapd_nai_realm_eap { + u8 eap_method; + u8 num_auths; + u8 auth_id[MAX_NAI_AUTH_TYPES]; + u8 auth_val[MAX_NAI_AUTH_TYPES]; + } eap_method[MAX_NAI_EAP_METHODS]; +}; + /** * struct hostapd_bss_config - Per-BSS configuration */ struct hostapd_bss_config { char iface[IFNAMSIZ + 1]; char bridge[IFNAMSIZ + 1]; + char vlan_bridge[IFNAMSIZ + 1]; char wds_bridge[IFNAMSIZ + 1]; enum hostapd_logger_level logger_syslog_level, logger_stdout_level; @@ -162,8 +189,6 @@ unsigned int logger_syslog; /* module bitfield */ unsigned int logger_stdout; /* module bitfield */ - char *dump_log_name; /* file name for state dump (SIGUSR1) */ - int max_num_sta; /* maximum number of STAs in station table */ int dtim_period; @@ -173,11 +198,21 @@ int eap_server; /* Use internal EAP server instead of external * RADIUS server */ struct hostapd_eap_user *eap_user; + char *eap_user_sqlite; char *eap_sim_db; struct hostapd_ip_addr own_ip_addr; char *nas_identifier; struct hostapd_radius_servers *radius; int acct_interim_interval; + int radius_request_cui; + struct hostapd_radius_attr *radius_auth_req_attr; + struct hostapd_radius_attr *radius_acct_req_attr; + int radius_das_port; + unsigned int radius_das_time_window; + int radius_das_require_event_timestamp; + struct hostapd_ip_addr radius_das_client_addr; + u8 *radius_das_shared_secret; + size_t radius_das_shared_secret_len; struct hostapd_ssid ssid; @@ -207,6 +242,7 @@ int num_deny_mac; int wds_sta; int isolate; + int start_disabled; int auth_algs; /* bitfield of allowed IEEE 802.11 authentication * algorithms, WPA_AUTH_ALG_{OPEN,SHARED,LEAP} */ @@ -220,6 +256,11 @@ /* dot11AssociationSAQueryRetryTimeout (in TUs) */ int assoc_sa_query_retry_timeout; #endif /* CONFIG_IEEE80211W */ + enum { + PSK_RADIUS_IGNORED = 0, + PSK_RADIUS_ACCEPTED = 1, + PSK_RADIUS_REQUIRED = 2 + } wpa_psk_radius; int wpa_pairwise; int wpa_group; int wpa_group_rekey; @@ -254,6 +295,7 @@ char *private_key; char *private_key_passwd; int check_crl; + char *ocsp_stapling_response; char *dh_file; u8 *pac_opaque_encr_key; u8 *eap_fast_a_id; @@ -284,7 +326,7 @@ int wmm_enabled; int wmm_uapsd; - struct hostapd_vlan *vlan, *vlan_tail; + struct hostapd_vlan *vlan; macaddr bssid; @@ -300,6 +342,7 @@ int wps_state; #ifdef CONFIG_WPS + int wps_independent; int ap_setup_locked; u8 uuid[16]; char *wps_pin_requests; @@ -316,6 +359,7 @@ u8 *extra_cred; size_t extra_cred_len; int wps_cred_processing; + int force_per_enrollee_psk; u8 *ap_settings; size_t ap_settings_len; char *upnp_iface; @@ -325,8 +369,14 @@ char *model_url; char *upc; struct wpabuf *wps_vendor_ext[MAX_WPS_VENDOR_EXTENSIONS]; + int wps_nfc_pw_from_config; + int wps_nfc_dev_pw_id; + struct wpabuf *wps_nfc_dh_pubkey; + struct wpabuf *wps_nfc_dh_privkey; + struct wpabuf *wps_nfc_dev_pw; #endif /* CONFIG_WPS */ int pbc_in_m1; + char *server_id; #define P2P_ENABLED BIT(0) #define P2P_GROUP_OWNER BIT(1) @@ -334,17 +384,27 @@ #define P2P_MANAGE BIT(3) #define P2P_ALLOW_CROSS_CONNECTION BIT(4) int p2p; +#ifdef CONFIG_P2P + u8 ip_addr_go[4]; + u8 ip_addr_mask[4]; + u8 ip_addr_start[4]; + u8 ip_addr_end[4]; +#endif /* CONFIG_P2P */ int disassoc_low_ack; + int skip_inactivity_poll; #define TDLS_PROHIBIT BIT(0) #define TDLS_PROHIBIT_CHAN_SWITCH BIT(1) int tdls; int disable_11n; + int disable_11ac; /* IEEE 802.11v */ int time_advertisement; char *time_zone; + int wnm_sleep_mode; + int bss_transition; /* IEEE 802.11u - Interworking */ int interworking; @@ -361,6 +421,63 @@ /* IEEE 802.11u - Roaming Consortium list */ unsigned int roaming_consortium_count; struct hostapd_roaming_consortium *roaming_consortium; + + /* IEEE 802.11u - Venue Name duples */ + unsigned int venue_name_count; + struct hostapd_lang_string *venue_name; + + /* IEEE 802.11u - Network Authentication Type */ + u8 *network_auth_type; + size_t network_auth_type_len; + + /* IEEE 802.11u - IP Address Type Availability */ + u8 ipaddr_type_availability; + u8 ipaddr_type_configured; + + /* IEEE 802.11u - 3GPP Cellular Network */ + u8 *anqp_3gpp_cell_net; + size_t anqp_3gpp_cell_net_len; + + /* IEEE 802.11u - Domain Name */ + u8 *domain_name; + size_t domain_name_len; + + unsigned int nai_realm_count; + struct hostapd_nai_realm_data *nai_realm_data; + + u16 gas_comeback_delay; + int gas_frag_limit; + + u8 qos_map_set[16 + 2 * 21]; + unsigned int qos_map_set_len; + +#ifdef CONFIG_HS20 + int hs20; + int disable_dgaf; + unsigned int hs20_oper_friendly_name_count; + struct hostapd_lang_string *hs20_oper_friendly_name; + u8 *hs20_wan_metrics; + u8 *hs20_connection_capability; + size_t hs20_connection_capability_len; + u8 *hs20_operating_class; + u8 hs20_operating_class_len; +#endif /* CONFIG_HS20 */ + + u8 wps_rf_bands; /* RF bands for WPS (WPS_RF_*) */ + +#ifdef CONFIG_RADIUS_TEST + char *dump_msk_file; +#endif /* CONFIG_RADIUS_TEST */ + + struct wpabuf *vendor_elements; + + unsigned int sae_anti_clogging_threshold; + int *sae_groups; + +#ifdef CONFIG_TESTING_OPTIONS + u8 bss_load_test[5]; + u8 bss_load_test_set; +#endif /* CONFIG_TESTING_OPTIONS */ }; @@ -368,7 +485,7 @@ * struct hostapd_config - Per-radio interface configuration */ struct hostapd_config { - struct hostapd_bss_config *bss, *last_bss; + struct hostapd_bss_config **bss, *last_bss; size_t num_bss; u16 beacon_int; @@ -399,6 +516,8 @@ int ieee80211d; + int ieee80211h; /* DFS */ + struct hostapd_tx_queue_params tx_queue[NUM_TX_QUEUES]; /* @@ -415,6 +534,25 @@ int ieee80211n; int secondary_channel; int require_ht; + int obss_interval; + u32 vht_capab; + int ieee80211ac; + int require_vht; + u8 vht_oper_chwidth; + u8 vht_oper_centr_freq_seg0_idx; + u8 vht_oper_centr_freq_seg1_idx; + +#ifdef CONFIG_TESTING_OPTIONS + double ignore_probe_probability; + double ignore_auth_probability; + double ignore_assoc_probability; + double ignore_reassoc_probability; + double corrupt_gtk_rekey_mic_probability; +#endif /* CONFIG_TESTING_OPTIONS */ + +#ifdef CONFIG_ACS + unsigned int acs_num_scans; +#endif /* CONFIG_ACS */ }; @@ -422,6 +560,7 @@ 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_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); @@ -429,12 +568,15 @@ int hostapd_wep_key_cmp(struct hostapd_wep_keys *a, struct hostapd_wep_keys *b); const u8 * hostapd_get_psk(const struct hostapd_bss_config *conf, - const u8 *addr, const u8 *prev_psk); + const u8 *addr, const u8 *p2p_dev_addr, + const u8 *prev_psk); int hostapd_setup_wpa_psk(struct hostapd_bss_config *conf); +int hostapd_vlan_id_valid(struct hostapd_vlan *vlan, int vlan_id); const char * hostapd_get_vlan_id_ifname(struct hostapd_vlan *vlan, int vlan_id); -const struct hostapd_eap_user * -hostapd_get_eap_user(const struct hostapd_bss_config *conf, const u8 *identity, - size_t identity_len, int phase2); +struct hostapd_radius_attr * +hostapd_config_get_radius_attr(struct hostapd_radius_attr *attr, u8 type); +int hostapd_config_check(struct hostapd_config *conf, int full_config); +void hostapd_set_security_params(struct hostapd_bss_config *bss); #endif /* HOSTAPD_CONFIG_H */ diff -Nru wpa-1.0/src/ap/ap_drv_ops.c wpa-2.1/src/ap/ap_drv_ops.c --- wpa-1.0/src/ap/ap_drv_ops.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/ap/ap_drv_ops.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,27 +2,22 @@ * hostapd - Driver operations * Copyright (c) 2009-2010, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * 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 "drivers/driver.h" #include "common/ieee802_11_defs.h" #include "wps/wps.h" +#include "p2p/p2p.h" #include "hostapd.h" #include "ieee802_11.h" #include "sta_info.h" #include "ap_config.h" #include "p2p_hostapd.h" +#include "hs20.h" #include "ap_drv_ops.h" @@ -153,6 +148,38 @@ } #endif /* CONFIG_P2P_MANAGER */ +#ifdef CONFIG_WIFI_DISPLAY + 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); + 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); + + if (wpabuf_resize(&proberesp, pos - buf) != 0) + goto fail; + wpabuf_put_data(proberesp, buf, pos - buf); + } +#endif /* CONFIG_HS20 */ + + 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); + } + *beacon_ret = beacon; *proberesp_ret = proberesp; *assocresp_ret = assocresp; @@ -258,7 +285,7 @@ char force_ifname[IFNAMSIZ]; u8 if_addr[ETH_ALEN]; return hostapd_if_add(hapd, WPA_IF_AP_VLAN, ifname, hapd->own_addr, - NULL, NULL, force_ifname, if_addr, NULL); + NULL, NULL, force_ifname, if_addr, NULL, 0); } @@ -268,19 +295,19 @@ } -int hostapd_set_wds_sta(struct hostapd_data *hapd, const u8 *addr, int aid, - int val) +int hostapd_set_wds_sta(struct hostapd_data *hapd, char *ifname_wds, + const u8 *addr, int aid, int val) { const char *bridge = NULL; if (hapd->driver == NULL || hapd->driver->set_wds_sta == NULL) - return 0; + return -1; if (hapd->conf->wds_bridge[0]) bridge = hapd->conf->wds_bridge; else if (hapd->conf->bridge[0]) bridge = hapd->conf->bridge; return hapd->driver->set_wds_sta(hapd->drv_priv, addr, aid, val, - bridge); + bridge, ifname_wds); } @@ -318,7 +345,8 @@ const u8 *supp_rates, size_t supp_rates_len, u16 listen_interval, const struct ieee80211_ht_capabilities *ht_capab, - u32 flags) + const struct ieee80211_vht_capabilities *vht_capab, + u32 flags, u8 qosinfo) { struct hostapd_sta_add_params params; @@ -335,7 +363,9 @@ params.supp_rates_len = supp_rates_len; params.listen_interval = listen_interval; params.ht_capabilities = ht_capab; + params.vht_capabilities = vht_capab; params.flags = hostapd_sta_flags_to_drv(flags); + params.qosinfo = qosinfo; return hapd->driver->sta_add(hapd->drv_priv, ¶ms); } @@ -386,20 +416,21 @@ int hostapd_if_add(struct hostapd_data *hapd, enum wpa_driver_if_type type, const char *ifname, const u8 *addr, void *bss_ctx, void **drv_priv, char *force_ifname, u8 *if_addr, - const char *bridge) + const char *bridge, int use_existing) { if (hapd->driver == NULL || hapd->driver->if_add == NULL) return -1; return hapd->driver->if_add(hapd->drv_priv, type, ifname, addr, bss_ctx, drv_priv, force_ifname, if_addr, - bridge); + bridge, use_existing); } int hostapd_if_remove(struct hostapd_data *hapd, enum wpa_driver_if_type type, const char *ifname) { - if (hapd->driver == NULL || hapd->driver->if_remove == NULL) + if (hapd->driver == NULL || hapd->drv_priv == NULL || + hapd->driver->if_remove == NULL) return -1; return hapd->driver->if_remove(hapd->drv_priv, type, ifname); } @@ -432,20 +463,108 @@ } +int hostapd_set_freq_params(struct hostapd_freq_params *data, int mode, + int freq, int channel, int ht_enabled, + int vht_enabled, int sec_channel_offset, + int vht_oper_chwidth, int center_segment0, + int center_segment1, u32 vht_caps) +{ + int tmp; + + os_memset(data, 0, sizeof(*data)); + data->mode = mode; + data->freq = freq; + data->channel = channel; + data->ht_enabled = ht_enabled; + data->vht_enabled = vht_enabled; + data->sec_channel_offset = sec_channel_offset; + data->center_freq1 = freq + sec_channel_offset * 10; + data->center_freq2 = 0; + data->bandwidth = sec_channel_offset ? 40 : 20; + + /* + * This validation code is probably misplaced, maybe it should be + * in src/ap/hw_features.c and check the hardware support as well. + */ + if (data->vht_enabled) switch (vht_oper_chwidth) { + case VHT_CHANWIDTH_USE_HT: + if (center_segment1) + return -1; + if (5000 + center_segment0 * 5 != data->center_freq1 && + 2407 + center_segment0 * 5 != data->center_freq1) + return -1; + break; + case VHT_CHANWIDTH_80P80MHZ: + if (!(vht_caps & VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ)) { + wpa_printf(MSG_ERROR, + "80+80 channel width is not supported!"); + return -1; + } + if (center_segment1 == center_segment0 + 4 || + center_segment1 == center_segment0 - 4) + return -1; + data->center_freq2 = 5000 + center_segment1 * 5; + /* fall through */ + case VHT_CHANWIDTH_80MHZ: + data->bandwidth = 80; + if (vht_oper_chwidth == 1 && center_segment1) + return -1; + if (vht_oper_chwidth == 3 && !center_segment1) + return -1; + if (!sec_channel_offset) + return -1; + /* primary 40 part must match the HT configuration */ + tmp = (30 + freq - 5000 - center_segment0 * 5)/20; + tmp /= 2; + if (data->center_freq1 != 5000 + + center_segment0 * 5 - 20 + 40 * tmp) + return -1; + data->center_freq1 = 5000 + center_segment0 * 5; + break; + case VHT_CHANWIDTH_160MHZ: + data->bandwidth = 160; + if (!(vht_caps & (VHT_CAP_SUPP_CHAN_WIDTH_160MHZ | + VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ))) { + wpa_printf(MSG_ERROR, + "160MHZ channel width is not supported!"); + return -1; + } + if (center_segment1) + return -1; + if (!sec_channel_offset) + return -1; + /* primary 40 part must match the HT configuration */ + tmp = (70 + freq - 5000 - center_segment0 * 5)/20; + tmp /= 2; + if (data->center_freq1 != 5000 + + center_segment0 * 5 - 60 + 40 * tmp) + return -1; + data->center_freq1 = 5000 + center_segment0 * 5; + break; + } + + return 0; +} + + int hostapd_set_freq(struct hostapd_data *hapd, int mode, int freq, - int channel, int ht_enabled, int sec_channel_offset) + int channel, int ht_enabled, int vht_enabled, + int sec_channel_offset, int vht_oper_chwidth, + int center_segment0, int center_segment1) { struct hostapd_freq_params data; + + if (hostapd_set_freq_params(&data, mode, freq, channel, ht_enabled, + vht_enabled, sec_channel_offset, + vht_oper_chwidth, + center_segment0, center_segment1, + hapd->iface->current_mode->vht_capab)) + return -1; + if (hapd->driver == NULL) return 0; if (hapd->driver->set_freq == NULL) return 0; - os_memset(&data, 0, sizeof(data)); - data.mode = mode; - data.freq = freq; - data.channel = channel; - data.ht_enabled = ht_enabled; - data.sec_channel_offset = sec_channel_offset; return hapd->driver->set_freq(hapd->drv_priv, &data); } @@ -475,16 +594,6 @@ } -int hostapd_set_rate_sets(struct hostapd_data *hapd, int *supp_rates, - int *basic_rates, int mode) -{ - if (hapd->driver == NULL || hapd->driver->set_rate_sets == NULL) - return 0; - return hapd->driver->set_rate_sets(hapd->drv_priv, supp_rates, - basic_rates, mode); -} - - int hostapd_set_country(struct hostapd_data *hapd, const char *country) { if (hapd->driver == NULL || @@ -573,11 +682,11 @@ int hostapd_drv_send_mlme(struct hostapd_data *hapd, - const void *msg, size_t len) + const void *msg, size_t len, int noack) { if (hapd->driver == NULL || hapd->driver->send_mlme == NULL) return 0; - return hapd->driver->send_mlme(hapd->drv_priv, msg, len); + return hapd->driver->send_mlme(hapd->drv_priv, msg, len, noack); } @@ -599,3 +708,68 @@ return hapd->driver->sta_disassoc(hapd->drv_priv, hapd->own_addr, addr, reason); } + + +int hostapd_drv_wnm_oper(struct hostapd_data *hapd, enum wnm_oper oper, + const u8 *peer, u8 *buf, u16 *buf_len) +{ + if (hapd->driver == NULL || hapd->driver->wnm_oper == NULL) + return -1; + return hapd->driver->wnm_oper(hapd->drv_priv, oper, peer, buf, + buf_len); +} + + +int hostapd_drv_send_action(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, + hapd->own_addr, hapd->own_addr, data, + len, 0); +} + + +int hostapd_start_dfs_cac(struct hostapd_iface *iface, int mode, int freq, + int channel, int ht_enabled, int vht_enabled, + int sec_channel_offset, int vht_oper_chwidth, + int center_segment0, int center_segment1) +{ + struct hostapd_data *hapd = iface->bss[0]; + struct hostapd_freq_params data; + int res; + + if (!hapd->driver || !hapd->driver->start_dfs_cac) + return 0; + + if (!iface->conf->ieee80211h) { + wpa_printf(MSG_ERROR, "Can't start DFS CAC, DFS functionality " + "is not enabled"); + return -1; + } + + if (hostapd_set_freq_params(&data, mode, freq, channel, ht_enabled, + vht_enabled, sec_channel_offset, + vht_oper_chwidth, center_segment0, + center_segment1, + iface->current_mode->vht_capab)) + return -1; + + res = hapd->driver->start_dfs_cac(hapd->drv_priv, &data); + if (!res) + iface->cac_started = 1; + + return res; +} + + +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) + return 0; + return hapd->driver->set_qos_map(hapd->drv_priv, qos_map_set, + qos_map_set_len); +} diff -Nru wpa-1.0/src/ap/ap_drv_ops.h wpa-2.1/src/ap/ap_drv_ops.h --- wpa-1.0/src/ap/ap_drv_ops.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/ap/ap_drv_ops.h 2014-02-04 11:23:35.000000000 +0000 @@ -1,15 +1,9 @@ /* * hostapd - Driver operations - * Copyright (c) 2009, Jouni Malinen + * Copyright (c) 2009-2014, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef AP_DRV_OPS @@ -19,6 +13,8 @@ struct wpa_bss_params; struct wpa_driver_scan_params; struct ieee80211_ht_capabilities; +struct ieee80211_vht_capabilities; +struct hostapd_freq_params; u32 hostapd_sta_flags_to_drv(u32 flags); int hostapd_build_ap_extra_ies(struct hostapd_data *hapd, @@ -36,14 +32,15 @@ int enabled); int hostapd_vlan_if_add(struct hostapd_data *hapd, const char *ifname); int hostapd_vlan_if_remove(struct hostapd_data *hapd, const char *ifname); -int hostapd_set_wds_sta(struct hostapd_data *hapd, const u8 *addr, int aid, - int val); +int hostapd_set_wds_sta(struct hostapd_data *hapd, char *ifname_wds, + const u8 *addr, int aid, int val); int hostapd_sta_add(struct hostapd_data *hapd, const u8 *addr, u16 aid, u16 capability, const u8 *supp_rates, size_t supp_rates_len, u16 listen_interval, const struct ieee80211_ht_capabilities *ht_capab, - u32 flags); + const struct ieee80211_vht_capabilities *vht_capab, + u32 flags, u8 qosinfo); 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); @@ -52,7 +49,7 @@ int hostapd_if_add(struct hostapd_data *hapd, enum wpa_driver_if_type type, const char *ifname, const u8 *addr, void *bss_ctx, void **drv_priv, char *force_ifname, u8 *if_addr, - const char *bridge); + const char *bridge, int use_existing); int hostapd_if_remove(struct hostapd_data *hapd, enum wpa_driver_if_type type, const char *ifname); int hostapd_set_ieee8021x(struct hostapd_data *hapd, @@ -61,13 +58,13 @@ const u8 *addr, int idx, u8 *seq); int hostapd_flush(struct hostapd_data *hapd); int hostapd_set_freq(struct hostapd_data *hapd, int mode, int freq, - int channel, int ht_enabled, int sec_channel_offset); + int channel, int ht_enabled, int vht_enabled, + int sec_channel_offset, int vht_oper_chwidth, + int center_segment0, int center_segment1); int hostapd_set_rts(struct hostapd_data *hapd, int rts); int hostapd_set_frag(struct hostapd_data *hapd, int frag); int hostapd_sta_set_flags(struct hostapd_data *hapd, u8 *addr, int total_flags, int flags_or, int flags_and); -int hostapd_set_rate_sets(struct hostapd_data *hapd, int *supp_rates, - int *basic_rates, int mode); int hostapd_set_country(struct hostapd_data *hapd, const char *country); int hostapd_set_tx_queue_params(struct hostapd_data *hapd, int queue, int aifs, int cw_min, int cw_max, int burst_time); @@ -89,11 +86,14 @@ const u8 *seq, size_t seq_len, const u8 *key, size_t key_len); int hostapd_drv_send_mlme(struct hostapd_data *hapd, - const void *msg, size_t len); + const void *msg, size_t len, int noack); int hostapd_drv_sta_deauth(struct hostapd_data *hapd, const u8 *addr, int reason); int hostapd_drv_sta_disassoc(struct hostapd_data *hapd, const u8 *addr, int reason); +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_add_sta_node(struct hostapd_data *hapd, const u8 *addr, u16 auth_alg); int hostapd_sta_auth(struct hostapd_data *hapd, const u8 *addr, @@ -102,10 +102,26 @@ int reassoc, u16 status, const u8 *ie, size_t len); int hostapd_add_tspec(struct hostapd_data *hapd, const u8 *addr, u8 *tspec_ie, size_t tspec_ielen); +int hostapd_start_dfs_cac(struct hostapd_iface *iface, int mode, int freq, + int channel, int ht_enabled, int vht_enabled, + int sec_channel_offset, int vht_oper_chwidth, + int center_segment0, int center_segment1); +int hostapd_set_freq_params(struct hostapd_freq_params *data, int mode, + int freq, int channel, int ht_enabled, + int vht_enabled, int sec_channel_offset, + int vht_oper_chwidth, int center_segment0, + int center_segment1, u32 vht_caps); #include "drivers/driver.h" +int hostapd_drv_wnm_oper(struct hostapd_data *hapd, + enum wnm_oper oper, const u8 *peer, + u8 *buf, u16 *buf_len); + +int hostapd_drv_set_qos_map(struct hostapd_data *hapd, const u8 *qos_map_set, + u8 qos_map_set_len); + static inline int hostapd_drv_set_countermeasures(struct hostapd_data *hapd, int enabled) { @@ -170,6 +186,14 @@ return hapd->driver->sta_clear_stats(hapd->drv_priv, addr); } +static inline int hostapd_drv_set_acl(struct hostapd_data *hapd, + struct hostapd_acl_params *params) +{ + if (hapd->driver == NULL || hapd->driver->set_acl == NULL) + return 0; + return hapd->driver->set_acl(hapd->drv_priv, params); +} + static inline int hostapd_drv_set_ap(struct hostapd_data *hapd, struct wpa_driver_ap_params *params) { @@ -214,4 +238,46 @@ hapd->driver->poll_client(hapd->drv_priv, own_addr, addr, qos); } +static inline int hostapd_drv_get_survey(struct hostapd_data *hapd, + unsigned int freq) +{ + if (hapd->driver == NULL) + return -1; + if (!hapd->driver->get_survey) + return -1; + return hapd->driver->get_survey(hapd->drv_priv, freq); +} + +static inline int hostapd_get_country(struct hostapd_data *hapd, char *alpha2) +{ + if (hapd->driver == NULL || hapd->driver->get_country == NULL) + return -1; + return hapd->driver->get_country(hapd->drv_priv, alpha2); +} + +static inline const char * hostapd_drv_get_radio_name(struct hostapd_data *hapd) +{ + if (hapd->driver == NULL || hapd->drv_priv == NULL || + hapd->driver->get_radio_name == NULL) + return NULL; + return hapd->driver->get_radio_name(hapd->drv_priv); +} + +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; + + return hapd->driver->switch_channel(hapd->drv_priv, settings); +} + +static inline int hostapd_drv_status(struct hostapd_data *hapd, char *buf, + size_t buflen) +{ + if (hapd->driver == NULL || hapd->driver->status == NULL) + return -1; + return hapd->driver->status(hapd->drv_priv, buf, buflen); +} + #endif /* AP_DRV_OPS */ diff -Nru wpa-1.0/src/ap/ap_list.c wpa-2.1/src/ap/ap_list.c --- wpa-1.0/src/ap/ap_list.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/ap/ap_list.c 2014-02-04 11:23:35.000000000 +0000 @@ -4,14 +4,8 @@ * Copyright (c) 2003-2004, Instant802 Networks, Inc. * Copyright (c) 2006, Devicescape Software, Inc. * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "utils/includes.h" @@ -20,7 +14,6 @@ #include "utils/eloop.h" #include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" -#include "drivers/driver.h" #include "hostapd.h" #include "ap_config.h" #include "ieee802_11.h" @@ -56,7 +49,7 @@ } -struct ap_info * ap_get_ap(struct hostapd_iface *iface, const u8 *ap) +static struct ap_info * ap_get_ap(struct hostapd_iface *iface, const u8 *ap) { struct ap_info *s; @@ -93,34 +86,6 @@ } -static void ap_ap_iter_list_add(struct hostapd_iface *iface, - struct ap_info *ap) -{ - if (iface->ap_iter_list) { - ap->iter_prev = iface->ap_iter_list->iter_prev; - iface->ap_iter_list->iter_prev = ap; - } else - ap->iter_prev = ap; - ap->iter_next = iface->ap_iter_list; - iface->ap_iter_list = ap; -} - - -static void ap_ap_iter_list_del(struct hostapd_iface *iface, - struct ap_info *ap) -{ - if (iface->ap_iter_list == ap) - iface->ap_iter_list = ap->iter_next; - else - ap->iter_prev->iter_next = ap->iter_next; - - if (ap->iter_next) - ap->iter_next->iter_prev = ap->iter_prev; - else if (iface->ap_iter_list) - iface->ap_iter_list->iter_prev = ap->iter_prev; -} - - static void ap_ap_hash_add(struct hostapd_iface *iface, struct ap_info *ap) { ap->hnext = iface->ap_hash[STA_HASH(ap->addr)]; @@ -154,7 +119,6 @@ { ap_ap_hash_del(iface, ap); ap_ap_list_del(iface, ap); - ap_ap_iter_list_del(iface, ap); iface->num_ap--; os_free(ap); @@ -177,25 +141,6 @@ } -int ap_ap_for_each(struct hostapd_iface *iface, - int (*func)(struct ap_info *s, void *data), void *data) -{ - struct ap_info *s; - int ret = 0; - - s = iface->ap_list; - - while (s) { - ret = func(s, data); - if (ret) - break; - s = s->next; - } - - return ret; -} - - static struct ap_info * ap_ap_add(struct hostapd_iface *iface, const u8 *addr) { struct ap_info *ap; @@ -209,7 +154,6 @@ ap_ap_list_add(iface, ap); iface->num_ap++; ap_ap_hash_add(iface, ap); - ap_ap_iter_list_add(iface, ap); if (iface->num_ap > iface->conf->ap_table_max_size && ap != ap->prev) { wpa_printf(MSG_DEBUG, "Removing the least recently used AP " @@ -227,9 +171,7 @@ struct hostapd_frame_info *fi) { struct ap_info *ap; - struct os_time now; int new_ap = 0; - size_t len; int set_beacon = 0; if (iface->conf->ap_table_max_size < 1) @@ -245,37 +187,9 @@ new_ap = 1; } - ap->beacon_int = le_to_host16(mgmt->u.beacon.beacon_int); - ap->capability = le_to_host16(mgmt->u.beacon.capab_info); - - if (elems->ssid) { - len = elems->ssid_len; - if (len >= sizeof(ap->ssid)) - len = sizeof(ap->ssid) - 1; - os_memcpy(ap->ssid, elems->ssid, len); - ap->ssid[len] = '\0'; - ap->ssid_len = len; - } - - os_memset(ap->supported_rates, 0, WLAN_SUPP_RATES_MAX); - len = 0; - if (elems->supp_rates) { - len = elems->supp_rates_len; - if (len > WLAN_SUPP_RATES_MAX) - len = WLAN_SUPP_RATES_MAX; - os_memcpy(ap->supported_rates, elems->supp_rates, len); - } - if (elems->ext_supp_rates) { - int len2; - if (len + elems->ext_supp_rates_len > WLAN_SUPP_RATES_MAX) - len2 = WLAN_SUPP_RATES_MAX - len; - else - len2 = elems->ext_supp_rates_len; - os_memcpy(ap->supported_rates + len, elems->ext_supp_rates, - len2); - } - - ap->wpa = elems->wpa_ie != NULL; + merge_byte_arrays(ap->supported_rates, WLAN_SUPP_RATES_MAX, + elems->supp_rates, elems->supp_rates_len, + elems->ext_supp_rates, elems->ext_supp_rates_len); if (elems->erp_info && elems->erp_info_len == 1) ap->erp = elems->erp_info[0]; @@ -284,6 +198,8 @@ if (elems->ds_params && elems->ds_params_len == 1) ap->channel = elems->ds_params[0]; + else if (elems->ht_operation && elems->ht_operation_len >= 1) + ap->channel = elems->ht_operation[0]; else if (fi) ap->channel = fi->channel; @@ -292,13 +208,7 @@ else ap->ht_support = 0; - ap->num_beacons++; - os_get_time(&now); - ap->last_beacon = now.sec; - if (fi) { - ap->ssi_signal = fi->ssi_signal; - ap->datarate = fi->datarate; - } + os_get_reltime(&ap->last_beacon); if (!new_ap && ap != iface->ap_list) { /* move AP entry into the beginning of the list so that the @@ -310,30 +220,36 @@ if (!iface->olbc && ap_list_beacon_olbc(iface, ap)) { iface->olbc = 1; - wpa_printf(MSG_DEBUG, "OLBC AP detected: " MACSTR " - enable " - "protection", MAC2STR(ap->addr)); + wpa_printf(MSG_DEBUG, "OLBC AP detected: " MACSTR + " (channel %d) - enable protection", + MAC2STR(ap->addr), ap->channel); set_beacon++; } #ifdef CONFIG_IEEE80211N - if (!iface->olbc_ht && !ap->ht_support) { + if (!iface->olbc_ht && !ap->ht_support && + (ap->channel == 0 || + ap->channel == iface->conf->channel || + ap->channel == iface->conf->channel + + iface->conf->secondary_channel * 4)) { iface->olbc_ht = 1; hostapd_ht_operation_update(iface); wpa_printf(MSG_DEBUG, "OLBC HT AP detected: " MACSTR - " - enable protection", MAC2STR(ap->addr)); + " (channel %d) - enable protection", + MAC2STR(ap->addr), ap->channel); set_beacon++; } #endif /* CONFIG_IEEE80211N */ if (set_beacon) - ieee802_11_set_beacons(iface); + ieee802_11_update_beacons(iface); } static void ap_list_timer(void *eloop_ctx, void *timeout_ctx) { struct hostapd_iface *iface = eloop_ctx; - struct os_time now; + struct os_reltime now; struct ap_info *ap; int set_beacon = 0; @@ -342,12 +258,12 @@ if (!iface->ap_list) return; - os_get_time(&now); + os_get_reltime(&now); while (iface->ap_list) { ap = iface->ap_list->prev; - if (ap->last_beacon + iface->conf->ap_table_expiration_time >= - now.sec) + if (!os_reltime_expired(&now, &ap->last_beacon, + iface->conf->ap_table_expiration_time)) break; ap_free_ap(iface, ap); @@ -381,7 +297,7 @@ } if (set_beacon) - ieee802_11_set_beacons(iface); + ieee802_11_update_beacons(iface); } diff -Nru wpa-1.0/src/ap/ap_list.h wpa-2.1/src/ap/ap_list.h --- wpa-1.0/src/ap/ap_list.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/ap/ap_list.h 2014-02-04 11:23:35.000000000 +0000 @@ -4,14 +4,8 @@ * Copyright (c) 2003-2004, Instant802 Networks, Inc. * Copyright (c) 2006, Devicescape Software, Inc. * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef AP_LIST_H @@ -20,43 +14,24 @@ struct ap_info { /* Note: next/prev pointers are updated whenever a new beacon is * received because these are used to find the least recently used - * entries. iter_next/iter_prev are updated only when adding new BSSes - * and when removing old ones. These should be used when iterating - * through the table in a manner that allows beacons to be received - * during the iteration. */ + * entries. */ struct ap_info *next; /* next entry in AP list */ struct ap_info *prev; /* previous entry in AP list */ struct ap_info *hnext; /* next entry in hash table list */ - struct ap_info *iter_next; /* next entry in AP iteration list */ - struct ap_info *iter_prev; /* previous entry in AP iteration list */ u8 addr[6]; - u16 beacon_int; - u16 capability; u8 supported_rates[WLAN_SUPP_RATES_MAX]; - u8 ssid[33]; - size_t ssid_len; - int wpa; int erp; /* ERP Info or -1 if ERP info element not present */ int channel; - int datarate; /* in 100 kbps */ - int ssi_signal; int ht_support; - unsigned int num_beacons; /* number of beacon frames received */ - os_time_t last_beacon; - - int already_seen; /* whether API call AP-NEW has already fetched - * information about this AP */ + struct os_reltime last_beacon; }; struct ieee802_11_elems; struct hostapd_frame_info; -struct ap_info * ap_get_ap(struct hostapd_iface *iface, const u8 *sta); -int ap_ap_for_each(struct hostapd_iface *iface, - int (*func)(struct ap_info *s, void *data), void *data); void ap_list_process_beacon(struct hostapd_iface *iface, const struct ieee80211_mgmt *mgmt, struct ieee802_11_elems *elems, diff -Nru wpa-1.0/src/ap/ap_mlme.c wpa-2.1/src/ap/ap_mlme.c --- wpa-1.0/src/ap/ap_mlme.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/ap/ap_mlme.c 2014-02-04 11:23:35.000000000 +0000 @@ -4,14 +4,8 @@ * Copyright 2003-2004, Instant802 Networks, Inc. * Copyright 2005-2006, Devicescape Software, Inc. * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "utils/includes.h" diff -Nru wpa-1.0/src/ap/ap_mlme.h wpa-2.1/src/ap/ap_mlme.h --- wpa-1.0/src/ap/ap_mlme.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/ap/ap_mlme.h 2014-02-04 11:23:35.000000000 +0000 @@ -4,14 +4,8 @@ * Copyright 2003-2004, Instant802 Networks, Inc. * Copyright 2005-2006, Devicescape Software, Inc. * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef MLME_H diff -Nru wpa-1.0/src/ap/authsrv.c wpa-2.1/src/ap/authsrv.c --- wpa-1.0/src/ap/authsrv.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/ap/authsrv.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * Authentication server setup * Copyright (c) 2002-2009, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "utils/includes.h" @@ -60,7 +54,7 @@ struct eap_user *user) { const struct hostapd_eap_user *eap_user; - int i, count; + int i; eap_user = hostapd_get_eap_user(ctx, identity, identity_len, phase2); if (eap_user == NULL) @@ -70,10 +64,7 @@ return 0; os_memset(user, 0, sizeof(*user)); - count = EAP_USER_MAX_METHODS; - if (count > EAP_MAX_METHODS) - count = EAP_MAX_METHODS; - for (i = 0; i < count; i++) { + for (i = 0; i < EAP_MAX_METHODS; i++) { user->methods[i].vendor = eap_user->methods[i].vendor; user->methods[i].method = eap_user->methods[i].method; } @@ -101,7 +92,7 @@ os_memset(&srv, 0, sizeof(srv)); srv.client_file = conf->radius_server_clients; srv.auth_port = conf->radius_server_auth_port; - srv.conf_ctx = conf; + srv.conf_ctx = hapd; srv.eap_sim_db_priv = hapd->eap_sim_db_priv; srv.ssl_ctx = hapd->ssl_ctx; srv.msg_ctx = hapd->msg_ctx; @@ -120,6 +111,10 @@ srv.eap_req_id_text = conf->eap_req_id_text; srv.eap_req_id_text_len = conf->eap_req_id_text_len; srv.pwd_group = conf->pwd_group; + srv.server_id = conf->server_id ? conf->server_id : "hostapd"; +#ifdef CONFIG_RADIUS_TEST + srv.dump_msk_file = conf->dump_msk_file; +#endif /* CONFIG_RADIUS_TEST */ hapd->radius_srv = radius_server_init(&srv); if (hapd->radius_srv == NULL) { @@ -138,7 +133,7 @@ #ifdef EAP_TLS_FUNCS if (hapd->conf->eap_server && (hapd->conf->ca_cert || hapd->conf->server_cert || - hapd->conf->dh_file)) { + hapd->conf->private_key || hapd->conf->dh_file)) { struct tls_connection_params params; hapd->ssl_ctx = tls_init(NULL); @@ -154,6 +149,8 @@ params.private_key = hapd->conf->private_key; params.private_key_passwd = hapd->conf->private_key_passwd; params.dh_file = hapd->conf->dh_file; + params.ocsp_stapling_response = + hapd->conf->ocsp_stapling_response; if (tls_global_set_params(hapd->ssl_ctx, ¶ms)) { wpa_printf(MSG_ERROR, "Failed to set TLS parameters"); diff -Nru wpa-1.0/src/ap/authsrv.h wpa-2.1/src/ap/authsrv.h --- wpa-1.0/src/ap/authsrv.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/ap/authsrv.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * Authentication server setup * Copyright (c) 2002-2009, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef AUTHSRV_H diff -Nru wpa-1.0/src/ap/beacon.c wpa-2.1/src/ap/beacon.c --- wpa-1.0/src/ap/beacon.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/ap/beacon.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,16 +2,10 @@ * hostapd / IEEE 802.11 Management: Beacon and Probe Request/Response * Copyright (c) 2002-2004, Instant802 Networks, Inc. * Copyright (c) 2005-2006, Devicescape Software, Inc. - * Copyright (c) 2008-2009, Jouni Malinen + * Copyright (c) 2008-2012, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "utils/includes.h" @@ -21,7 +15,6 @@ #include "utils/common.h" #include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" -#include "drivers/driver.h" #include "wps/wps_defs.h" #include "p2p/p2p.h" #include "hostapd.h" @@ -33,10 +26,27 @@ #include "p2p_hostapd.h" #include "ap_drv_ops.h" #include "beacon.h" +#include "hs20.h" #ifdef NEED_AP_MLME +static u8 * hostapd_eid_bss_load(struct hostapd_data *hapd, u8 *eid, size_t len) +{ +#ifdef CONFIG_TESTING_OPTIONS + if (hapd->conf->bss_load_test_set) { + if (2 + 5 > len) + return eid; + *eid++ = WLAN_EID_BSS_LOAD; + *eid++ = 5; + os_memcpy(eid, hapd->conf->bss_load_test, 5); + eid += 5; + } +#endif /* CONFIG_TESTING_OPTIONS */ + return eid; +} + + static u8 ieee802_11_erp_info(struct hostapd_data *hapd) { u8 erp = 0; @@ -186,18 +196,249 @@ } -void handle_probe_req(struct hostapd_data *hapd, - const struct ieee80211_mgmt *mgmt, size_t len) +static u8 * hostapd_eid_csa(struct hostapd_data *hapd, u8 *eid) +{ + u8 chan; + + if (!hapd->iface->cs_freq_params.freq) + return eid; + + if (ieee80211_freq_to_chan(hapd->iface->cs_freq_params.freq, &chan) == + NUM_HOSTAPD_MODES) + return eid; + + *eid++ = WLAN_EID_CHANNEL_SWITCH; + *eid++ = 3; + *eid++ = hapd->iface->cs_block_tx; + *eid++ = chan; + *eid++ = hapd->iface->cs_count; + + return eid; +} + + +static u8 * hostapd_eid_secondary_channel(struct hostapd_data *hapd, u8 *eid) +{ + u8 sec_ch; + + if (!hapd->iface->cs_freq_params.sec_channel_offset) + return eid; + + if (hapd->iface->cs_freq_params.sec_channel_offset == -1) + sec_ch = HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW; + else if (hapd->iface->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; +} + + +static u8 * hostapd_add_csa_elems(struct hostapd_data *hapd, u8 *pos, + u8 *start, unsigned int *csa_counter_off) +{ + u8 *old_pos = pos; + + if (!csa_counter_off) + return pos; + + *csa_counter_off = 0; + pos = hostapd_eid_csa(hapd, pos); + + 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); + } + + return pos; +} + + +static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd, + struct sta_info *sta, + const struct ieee80211_mgmt *req, + int is_p2p, size_t *resp_len) { struct ieee80211_mgmt *resp; - struct ieee802_11_elems elems; - char *ssid; u8 *pos, *epos; + size_t buflen; + +#define MAX_PROBERESP_LEN 768 + buflen = MAX_PROBERESP_LEN; +#ifdef CONFIG_WPS + if (hapd->wps_probe_resp_ie) + buflen += wpabuf_len(hapd->wps_probe_resp_ie); +#endif /* CONFIG_WPS */ +#ifdef CONFIG_P2P + if (hapd->p2p_probe_resp_ie) + buflen += wpabuf_len(hapd->p2p_probe_resp_ie); +#endif /* CONFIG_P2P */ + if (hapd->conf->vendor_elements) + buflen += wpabuf_len(hapd->conf->vendor_elements); + resp = os_zalloc(buflen); + if (resp == NULL) + return NULL; + + epos = ((u8 *) resp) + MAX_PROBERESP_LEN; + + resp->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, + WLAN_FC_STYPE_PROBE_RESP); + if (req) + os_memcpy(resp->da, req->sa, ETH_ALEN); + os_memcpy(resp->sa, hapd->own_addr, ETH_ALEN); + + os_memcpy(resp->bssid, hapd->own_addr, ETH_ALEN); + resp->u.probe_resp.beacon_int = + host_to_le16(hapd->iconf->beacon_int); + + /* hardware or low-level driver will setup seq_ctrl and timestamp */ + resp->u.probe_resp.capab_info = + host_to_le16(hostapd_own_capab_info(hapd, sta, 1)); + + pos = resp->u.probe_resp.variable; + *pos++ = WLAN_EID_SSID; + *pos++ = hapd->conf->ssid.ssid_len; + os_memcpy(pos, hapd->conf->ssid.ssid, hapd->conf->ssid.ssid_len); + pos += hapd->conf->ssid.ssid_len; + + /* Supported rates */ + pos = hostapd_eid_supp_rates(hapd, pos); + + /* DS Params */ + pos = hostapd_eid_ds_params(hapd, pos); + + pos = hostapd_eid_country(hapd, pos, epos - pos); + + /* ERP Information element */ + pos = hostapd_eid_erp_info(hapd, pos); + + /* Extended supported rates */ + pos = hostapd_eid_ext_supp_rates(hapd, pos); + + /* RSN, MDIE, WPA */ + pos = hostapd_eid_wpa(hapd, pos, epos - pos); + + pos = hostapd_eid_bss_load(hapd, pos, epos - pos); + +#ifdef CONFIG_IEEE80211N + pos = hostapd_eid_ht_capabilities(hapd, pos); + pos = hostapd_eid_ht_operation(hapd, pos); +#endif /* CONFIG_IEEE80211N */ + + pos = hostapd_eid_ext_capab(hapd, pos); + + pos = hostapd_eid_time_adv(hapd, pos); + pos = hostapd_eid_time_zone(hapd, pos); + + pos = hostapd_eid_interworking(hapd, pos); + pos = hostapd_eid_adv_proto(hapd, pos); + pos = hostapd_eid_roaming_consortium(hapd, pos); + + pos = hostapd_add_csa_elems(hapd, pos, (u8 *)resp, + &hapd->iface->cs_c_off_proberesp); +#ifdef CONFIG_IEEE80211AC + pos = hostapd_eid_vht_capabilities(hapd, pos); + pos = hostapd_eid_vht_operation(hapd, pos); +#endif /* CONFIG_IEEE80211AC */ + + /* Wi-Fi Alliance WMM */ + pos = hostapd_eid_wmm(hapd, pos); + +#ifdef CONFIG_WPS + if (hapd->conf->wps_state && hapd->wps_probe_resp_ie) { + os_memcpy(pos, wpabuf_head(hapd->wps_probe_resp_ie), + wpabuf_len(hapd->wps_probe_resp_ie)); + pos += wpabuf_len(hapd->wps_probe_resp_ie); + } +#endif /* CONFIG_WPS */ + +#ifdef CONFIG_P2P + if ((hapd->conf->p2p & P2P_ENABLED) && is_p2p && + hapd->p2p_probe_resp_ie) { + os_memcpy(pos, wpabuf_head(hapd->p2p_probe_resp_ie), + wpabuf_len(hapd->p2p_probe_resp_ie)); + pos += wpabuf_len(hapd->p2p_probe_resp_ie); + } +#endif /* CONFIG_P2P */ +#ifdef CONFIG_P2P_MANAGER + if ((hapd->conf->p2p & (P2P_MANAGE | P2P_ENABLED | P2P_GROUP_OWNER)) == + P2P_MANAGE) + pos = hostapd_eid_p2p_manage(hapd, pos); +#endif /* CONFIG_P2P_MANAGER */ + +#ifdef CONFIG_HS20 + pos = hostapd_eid_hs20_indication(hapd, pos); +#endif /* CONFIG_HS20 */ + + if (hapd->conf->vendor_elements) { + os_memcpy(pos, wpabuf_head(hapd->conf->vendor_elements), + wpabuf_len(hapd->conf->vendor_elements)); + pos += wpabuf_len(hapd->conf->vendor_elements); + } + + *resp_len = pos - (u8 *) resp; + return (u8 *) resp; +} + + +enum ssid_match_result { + NO_SSID_MATCH, + EXACT_SSID_MATCH, + WILDCARD_SSID_MATCH +}; + +static enum ssid_match_result ssid_match(struct hostapd_data *hapd, + const u8 *ssid, size_t ssid_len, + const u8 *ssid_list, + size_t ssid_list_len) +{ + const u8 *pos, *end; + int wildcard = 0; + + if (ssid_len == 0) + wildcard = 1; + if (ssid_len == hapd->conf->ssid.ssid_len && + os_memcmp(ssid, hapd->conf->ssid.ssid, ssid_len) == 0) + return EXACT_SSID_MATCH; + + if (ssid_list == NULL) + return wildcard ? WILDCARD_SSID_MATCH : NO_SSID_MATCH; + + pos = ssid_list; + end = ssid_list + ssid_list_len; + while (pos + 1 <= end) { + if (pos + 2 + pos[1] > end) + break; + if (pos[1] == 0) + wildcard = 1; + if (pos[1] == hapd->conf->ssid.ssid_len && + os_memcmp(pos + 2, hapd->conf->ssid.ssid, pos[1]) == 0) + return EXACT_SSID_MATCH; + pos += 2 + pos[1]; + } + + return wildcard ? WILDCARD_SSID_MATCH : NO_SSID_MATCH; +} + + +void handle_probe_req(struct hostapd_data *hapd, + const struct ieee80211_mgmt *mgmt, size_t len, + int ssi_signal) +{ + u8 *resp; + struct ieee802_11_elems elems; const u8 *ie; - size_t ssid_len, ie_len; + size_t ie_len; struct sta_info *sta = NULL; - size_t buflen; - size_t i; + size_t i, resp_len; + int noack; + enum ssid_match_result res; ie = mgmt->u.probe_req.variable; if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req)) @@ -207,7 +448,7 @@ 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, - ie, ie_len) > 0) + ie, ie_len, ssi_signal) > 0) return; if (!hapd->iconf->send_probe_response) @@ -219,9 +460,6 @@ return; } - ssid = NULL; - ssid_len = 0; - if ((!elems.ssid || !elems.supp_rates)) { wpa_printf(MSG_DEBUG, "STA " MACSTR " sent probe request " "without SSID or supported rates element", @@ -256,7 +494,8 @@ } #endif /* CONFIG_P2P */ - if (hapd->conf->ignore_broadcast_ssid && elems.ssid_len == 0) { + if (hapd->conf->ignore_broadcast_ssid && elems.ssid_len == 0 && + elems.ssid_list_len == 0) { wpa_printf(MSG_MSGDUMP, "Probe Request from " MACSTR " for " "broadcast SSID ignored", MAC2STR(mgmt->sa)); return; @@ -274,25 +513,21 @@ } #endif /* CONFIG_P2P */ - if (elems.ssid_len == 0 || - (elems.ssid_len == hapd->conf->ssid.ssid_len && - os_memcmp(elems.ssid, hapd->conf->ssid.ssid, elems.ssid_len) == - 0)) { - ssid = hapd->conf->ssid.ssid; - ssid_len = hapd->conf->ssid.ssid_len; + res = ssid_match(hapd, elems.ssid, elems.ssid_len, + elems.ssid_list, elems.ssid_list_len); + if (res != NO_SSID_MATCH) { if (sta) sta->ssid_probe = &hapd->conf->ssid; - } - - if (!ssid) { + } else { if (!(mgmt->da[0] & 0x01)) { char ssid_txt[33]; ieee802_11_print_ssid(ssid_txt, elems.ssid, elems.ssid_len); wpa_printf(MSG_MSGDUMP, "Probe Request from " MACSTR - " for foreign SSID '%s' (DA " MACSTR ")", + " for foreign SSID '%s' (DA " MACSTR ")%s", MAC2STR(mgmt->sa), ssid_txt, - MAC2STR(mgmt->da)); + MAC2STR(mgmt->da), + elems.ssid_list ? " (SSID list)" : ""); } return; } @@ -327,126 +562,102 @@ } #endif /* CONFIG_INTERWORKING */ - /* TODO: verify that supp_rates contains at least one matching rate - * with AP configuration */ -#define MAX_PROBERESP_LEN 768 - buflen = MAX_PROBERESP_LEN; -#ifdef CONFIG_WPS - if (hapd->wps_probe_resp_ie) - buflen += wpabuf_len(hapd->wps_probe_resp_ie); -#endif /* CONFIG_WPS */ #ifdef CONFIG_P2P - if (hapd->p2p_probe_resp_ie) - buflen += wpabuf_len(hapd->p2p_probe_resp_ie); -#endif /* CONFIG_P2P */ - resp = os_zalloc(buflen); - if (resp == NULL) + if ((hapd->conf->p2p & P2P_GROUP_OWNER) && + supp_rates_11b_only(&elems)) { + /* Indicates support for 11b rates only */ + wpa_printf(MSG_EXCESSIVE, "P2P: Ignore Probe Request from " + MACSTR " with only 802.11b rates", + MAC2STR(mgmt->sa)); return; - epos = ((u8 *) resp) + MAX_PROBERESP_LEN; - - resp->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, - WLAN_FC_STYPE_PROBE_RESP); - os_memcpy(resp->da, mgmt->sa, ETH_ALEN); - os_memcpy(resp->sa, hapd->own_addr, ETH_ALEN); - - os_memcpy(resp->bssid, hapd->own_addr, ETH_ALEN); - resp->u.probe_resp.beacon_int = - host_to_le16(hapd->iconf->beacon_int); - - /* hardware or low-level driver will setup seq_ctrl and timestamp */ - resp->u.probe_resp.capab_info = - host_to_le16(hostapd_own_capab_info(hapd, sta, 1)); - - pos = resp->u.probe_resp.variable; - *pos++ = WLAN_EID_SSID; - *pos++ = ssid_len; - os_memcpy(pos, ssid, ssid_len); - pos += ssid_len; - - /* Supported rates */ - pos = hostapd_eid_supp_rates(hapd, pos); + } +#endif /* CONFIG_P2P */ - /* DS Params */ - pos = hostapd_eid_ds_params(hapd, pos); + /* TODO: verify that supp_rates contains at least one matching rate + * with AP configuration */ - pos = hostapd_eid_country(hapd, pos, epos - pos); +#ifdef CONFIG_TESTING_OPTIONS + if (hapd->iconf->ignore_probe_probability > 0.0d && + drand48() < hapd->iconf->ignore_probe_probability) { + wpa_printf(MSG_INFO, + "TESTING: ignoring probe request from " MACSTR, + MAC2STR(mgmt->sa)); + return; + } +#endif /* CONFIG_TESTING_OPTIONS */ - /* ERP Information element */ - pos = hostapd_eid_erp_info(hapd, pos); + resp = hostapd_gen_probe_resp(hapd, sta, mgmt, elems.p2p != NULL, + &resp_len); + if (resp == NULL) + return; - /* Extended supported rates */ - pos = hostapd_eid_ext_supp_rates(hapd, pos); + /* + * If this is a broadcast probe request, apply no ack policy to avoid + * excessive retries. + */ + noack = !!(res == WILDCARD_SSID_MATCH && + is_broadcast_ether_addr(mgmt->da)); - /* RSN, MDIE, WPA */ - pos = hostapd_eid_wpa(hapd, pos, epos - pos); + if (hostapd_drv_send_mlme(hapd, resp, resp_len, noack) < 0) + wpa_printf(MSG_INFO, "handle_probe_req: send failed"); -#ifdef CONFIG_IEEE80211N - pos = hostapd_eid_ht_capabilities(hapd, pos); - pos = hostapd_eid_ht_operation(hapd, pos); -#endif /* CONFIG_IEEE80211N */ + os_free(resp); - pos = hostapd_eid_ext_capab(hapd, pos); + wpa_printf(MSG_EXCESSIVE, "STA " MACSTR " sent probe request for %s " + "SSID", MAC2STR(mgmt->sa), + elems.ssid_len == 0 ? "broadcast" : "our"); +} - pos = hostapd_eid_time_adv(hapd, pos); - pos = hostapd_eid_time_zone(hapd, pos); - pos = hostapd_eid_interworking(hapd, pos); - pos = hostapd_eid_adv_proto(hapd, pos); - pos = hostapd_eid_roaming_consortium(hapd, pos); - - /* Wi-Fi Alliance WMM */ - pos = hostapd_eid_wmm(hapd, pos); +static u8 * hostapd_probe_resp_offloads(struct hostapd_data *hapd, + size_t *resp_len) +{ + /* check probe response offloading caps and print warnings */ + if (!(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_PROBE_RESP_OFFLOAD)) + return NULL; #ifdef CONFIG_WPS - if (hapd->conf->wps_state && hapd->wps_probe_resp_ie) { - os_memcpy(pos, wpabuf_head(hapd->wps_probe_resp_ie), - wpabuf_len(hapd->wps_probe_resp_ie)); - pos += wpabuf_len(hapd->wps_probe_resp_ie); - } + if (hapd->conf->wps_state && hapd->wps_probe_resp_ie && + (!(hapd->iface->probe_resp_offloads & + (WPA_DRIVER_PROBE_RESP_OFFLOAD_WPS | + WPA_DRIVER_PROBE_RESP_OFFLOAD_WPS2)))) + wpa_printf(MSG_WARNING, "Device is trying to offload WPS " + "Probe Response while not supporting this"); #endif /* CONFIG_WPS */ #ifdef CONFIG_P2P - if ((hapd->conf->p2p & P2P_ENABLED) && elems.p2p && - hapd->p2p_probe_resp_ie) { - os_memcpy(pos, wpabuf_head(hapd->p2p_probe_resp_ie), - wpabuf_len(hapd->p2p_probe_resp_ie)); - pos += wpabuf_len(hapd->p2p_probe_resp_ie); - } -#endif /* CONFIG_P2P */ -#ifdef CONFIG_P2P_MANAGER - if ((hapd->conf->p2p & (P2P_MANAGE | P2P_ENABLED | P2P_GROUP_OWNER)) == - P2P_MANAGE) - pos = hostapd_eid_p2p_manage(hapd, pos); -#endif /* CONFIG_P2P_MANAGER */ - - if (hostapd_drv_send_mlme(hapd, resp, pos - (u8 *) resp) < 0) - perror("handle_probe_req: send"); + if ((hapd->conf->p2p & P2P_ENABLED) && hapd->p2p_probe_resp_ie && + !(hapd->iface->probe_resp_offloads & + WPA_DRIVER_PROBE_RESP_OFFLOAD_P2P)) + wpa_printf(MSG_WARNING, "Device is trying to offload P2P " + "Probe Response while not supporting this"); +#endif /* CONFIG_P2P */ - os_free(resp); + if (hapd->conf->interworking && + !(hapd->iface->probe_resp_offloads & + WPA_DRIVER_PROBE_RESP_OFFLOAD_INTERWORKING)) + wpa_printf(MSG_WARNING, "Device is trying to offload " + "Interworking Probe Response while not supporting " + "this"); - wpa_printf(MSG_EXCESSIVE, "STA " MACSTR " sent probe request for %s " - "SSID", MAC2STR(mgmt->sa), - elems.ssid_len == 0 ? "broadcast" : "our"); + /* Generate a Probe Response template for the non-P2P case */ + return hostapd_gen_probe_resp(hapd, NULL, NULL, 0, resp_len); } #endif /* NEED_AP_MLME */ -void ieee802_11_set_beacon(struct hostapd_data *hapd) +int ieee802_11_build_ap_params(struct hostapd_data *hapd, + struct wpa_driver_ap_params *params) { struct ieee80211_mgmt *head = NULL; u8 *tail = NULL; size_t head_len = 0, tail_len = 0; - struct wpa_driver_ap_params params; - struct wpabuf *beacon, *proberesp, *assocresp; + u8 *resp = NULL; + size_t resp_len = 0; #ifdef NEED_AP_MLME u16 capab_info; u8 *pos, *tailpos; -#endif /* NEED_AP_MLME */ - - hapd->beacon_set_done = 1; - -#ifdef NEED_AP_MLME #define BEACON_HEAD_BUF_SIZE 256 #define BEACON_TAIL_BUF_SIZE 512 @@ -460,12 +671,14 @@ if (hapd->p2p_beacon_ie) tail_len += wpabuf_len(hapd->p2p_beacon_ie); #endif /* CONFIG_P2P */ + if (hapd->conf->vendor_elements) + tail_len += wpabuf_len(hapd->conf->vendor_elements); tailpos = tail = os_malloc(tail_len); if (head == NULL || tail == NULL) { wpa_printf(MSG_ERROR, "Failed to set beacon data"); os_free(head); os_free(tail); - return; + return -1; } head->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, @@ -520,6 +733,9 @@ tailpos = hostapd_eid_wpa(hapd, tailpos, tail + BEACON_TAIL_BUF_SIZE - tailpos); + tailpos = hostapd_eid_bss_load(hapd, tailpos, + tail + BEACON_TAIL_BUF_SIZE - tailpos); + #ifdef CONFIG_IEEE80211N tailpos = hostapd_eid_ht_capabilities(hapd, tailpos); tailpos = hostapd_eid_ht_operation(hapd, tailpos); @@ -536,6 +752,12 @@ 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->iface->cs_c_off_beacon); +#ifdef CONFIG_IEEE80211AC + tailpos = hostapd_eid_vht_capabilities(hapd, tailpos); + tailpos = hostapd_eid_vht_operation(hapd, tailpos); +#endif /* CONFIG_IEEE80211AC */ /* Wi-Fi Alliance WMM */ tailpos = hostapd_eid_wmm(hapd, tailpos); @@ -561,80 +783,159 @@ tailpos = hostapd_eid_p2p_manage(hapd, tailpos); #endif /* CONFIG_P2P_MANAGER */ +#ifdef CONFIG_HS20 + tailpos = hostapd_eid_hs20_indication(hapd, tailpos); +#endif /* CONFIG_HS20 */ + + if (hapd->conf->vendor_elements) { + os_memcpy(tailpos, wpabuf_head(hapd->conf->vendor_elements), + wpabuf_len(hapd->conf->vendor_elements)); + tailpos += wpabuf_len(hapd->conf->vendor_elements); + } + tail_len = tailpos > tail ? tailpos - tail : 0; + resp = hostapd_probe_resp_offloads(hapd, &resp_len); #endif /* NEED_AP_MLME */ - os_memset(¶ms, 0, sizeof(params)); - params.head = (u8 *) head; - params.head_len = head_len; - params.tail = tail; - params.tail_len = tail_len; - params.dtim_period = hapd->conf->dtim_period; - params.beacon_int = hapd->iconf->beacon_int; - params.ssid = (u8 *) hapd->conf->ssid.ssid; - params.ssid_len = hapd->conf->ssid.ssid_len; - params.pairwise_ciphers = hapd->conf->rsn_pairwise ? + os_memset(params, 0, sizeof(*params)); + params->head = (u8 *) head; + params->head_len = head_len; + params->tail = tail; + params->tail_len = tail_len; + params->proberesp = resp; + params->proberesp_len = resp_len; + params->dtim_period = hapd->conf->dtim_period; + params->beacon_int = hapd->iconf->beacon_int; + params->basic_rates = hapd->iface->basic_rates; + params->ssid = hapd->conf->ssid.ssid; + params->ssid_len = hapd->conf->ssid.ssid_len; + params->pairwise_ciphers = hapd->conf->rsn_pairwise ? hapd->conf->rsn_pairwise : hapd->conf->wpa_pairwise; - params.group_cipher = hapd->conf->wpa_group; - params.key_mgmt_suites = hapd->conf->wpa_key_mgmt; - params.auth_algs = hapd->conf->auth_algs; - params.wpa_version = hapd->conf->wpa; - params.privacy = hapd->conf->ssid.wep.keys_set || hapd->conf->wpa || + params->group_cipher = hapd->conf->wpa_group; + params->key_mgmt_suites = hapd->conf->wpa_key_mgmt; + params->auth_algs = hapd->conf->auth_algs; + params->wpa_version = hapd->conf->wpa; + params->privacy = hapd->conf->ssid.wep.keys_set || hapd->conf->wpa || (hapd->conf->ieee802_1x && (hapd->conf->default_wep_key_len || hapd->conf->individual_wep_key_len)); switch (hapd->conf->ignore_broadcast_ssid) { case 0: - params.hide_ssid = NO_SSID_HIDING; + params->hide_ssid = NO_SSID_HIDING; break; case 1: - params.hide_ssid = HIDDEN_SSID_ZERO_LEN; + params->hide_ssid = HIDDEN_SSID_ZERO_LEN; break; case 2: - params.hide_ssid = HIDDEN_SSID_ZERO_CONTENTS; + params->hide_ssid = HIDDEN_SSID_ZERO_CONTENTS; break; } - hostapd_build_ap_extra_ies(hapd, &beacon, &proberesp, &assocresp); - params.beacon_ies = beacon; - params.proberesp_ies = proberesp; - params.assocresp_ies = assocresp; - params.isolate = hapd->conf->isolate; + params->isolate = hapd->conf->isolate; #ifdef NEED_AP_MLME - params.cts_protect = !!(ieee802_11_erp_info(hapd) & + params->cts_protect = !!(ieee802_11_erp_info(hapd) & ERP_INFO_USE_PROTECTION); - params.preamble = hapd->iface->num_sta_no_short_preamble == 0 && + params->preamble = hapd->iface->num_sta_no_short_preamble == 0 && hapd->iconf->preamble == SHORT_PREAMBLE; if (hapd->iface->current_mode && hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G) - params.short_slot_time = + params->short_slot_time = hapd->iface->num_sta_no_short_slot_time > 0 ? 0 : 1; else - params.short_slot_time = -1; + params->short_slot_time = -1; if (!hapd->iconf->ieee80211n || hapd->conf->disable_11n) - params.ht_opmode = -1; + params->ht_opmode = -1; else - params.ht_opmode = hapd->iface->ht_op_mode; + params->ht_opmode = hapd->iface->ht_op_mode; #endif /* NEED_AP_MLME */ - params.interworking = hapd->conf->interworking; + params->interworking = hapd->conf->interworking; if (hapd->conf->interworking && !is_zero_ether_addr(hapd->conf->hessid)) - params.hessid = hapd->conf->hessid; - params.access_network_type = hapd->conf->access_network_type; - if (hostapd_drv_set_ap(hapd, ¶ms)) - wpa_printf(MSG_ERROR, "Failed to set beacon parameters"); + params->hessid = hapd->conf->hessid; + params->access_network_type = hapd->conf->access_network_type; + params->ap_max_inactivity = hapd->conf->ap_max_inactivity; +#ifdef CONFIG_HS20 + params->disable_dgaf = hapd->conf->disable_dgaf; +#endif /* CONFIG_HS20 */ + return 0; +} + + +void ieee802_11_free_ap_params(struct wpa_driver_ap_params *params) +{ + os_free(params->tail); + params->tail = NULL; + os_free(params->head); + params->head = NULL; + os_free(params->proberesp); + params->proberesp = NULL; +} + + +int ieee802_11_set_beacon(struct hostapd_data *hapd) +{ + struct wpa_driver_ap_params params; + struct wpabuf *beacon, *proberesp, *assocresp; + int res, ret = -1; + + if (hapd->iface->csa_in_progress) { + wpa_printf(MSG_ERROR, "Cannot set beacons during CSA period"); + return -1; + } + + hapd->beacon_set_done = 1; + + if (ieee802_11_build_ap_params(hapd, ¶ms) < 0) + return -1; + + if (hostapd_build_ap_extra_ies(hapd, &beacon, &proberesp, &assocresp) < + 0) + goto fail; + + params.beacon_ies = beacon; + params.proberesp_ies = proberesp; + params.assocresp_ies = assocresp; + + res = hostapd_drv_set_ap(hapd, ¶ms); hostapd_free_ap_extra_ies(hapd, beacon, proberesp, assocresp); + if (res) + wpa_printf(MSG_ERROR, "Failed to set beacon parameters"); + else + ret = 0; +fail: + ieee802_11_free_ap_params(¶ms); + return ret; +} - os_free(tail); - os_free(head); + +int ieee802_11_set_beacons(struct hostapd_iface *iface) +{ + size_t i; + int ret = 0; + + for (i = 0; i < iface->num_bss; i++) { + if (iface->bss[i]->started && + ieee802_11_set_beacon(iface->bss[i]) < 0) + ret = -1; + } + + return ret; } -void ieee802_11_set_beacons(struct hostapd_iface *iface) +/* only update beacons if started */ +int ieee802_11_update_beacons(struct hostapd_iface *iface) { size_t i; - for (i = 0; i < iface->num_bss; i++) - ieee802_11_set_beacon(iface->bss[i]); + int ret = 0; + + for (i = 0; i < iface->num_bss; i++) { + if (iface->bss[i]->beacon_set_done && iface->bss[i]->started && + ieee802_11_set_beacon(iface->bss[i]) < 0) + ret = -1; + } + + return ret; } #endif /* CONFIG_NATIVE_WINDOWS */ diff -Nru wpa-1.0/src/ap/beacon.h wpa-2.1/src/ap/beacon.h --- wpa-1.0/src/ap/beacon.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/ap/beacon.h 2014-02-04 11:23:35.000000000 +0000 @@ -3,14 +3,8 @@ * Copyright (c) 2002-2004, Instant802 Networks, Inc. * Copyright (c) 2005-2006, Devicescape Software, Inc. * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef BEACON_H @@ -19,8 +13,13 @@ struct ieee80211_mgmt; void handle_probe_req(struct hostapd_data *hapd, - const struct ieee80211_mgmt *mgmt, size_t len); -void ieee802_11_set_beacon(struct hostapd_data *hapd); -void ieee802_11_set_beacons(struct hostapd_iface *iface); + const struct ieee80211_mgmt *mgmt, size_t len, + int ssi_signal); +int ieee802_11_set_beacon(struct hostapd_data *hapd); +int ieee802_11_set_beacons(struct hostapd_iface *iface); +int ieee802_11_update_beacons(struct hostapd_iface *iface); +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); #endif /* BEACON_H */ diff -Nru wpa-1.0/src/ap/ctrl_iface_ap.c wpa-2.1/src/ap/ctrl_iface_ap.c --- wpa-1.0/src/ap/ctrl_iface_ap.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/ap/ctrl_iface_ap.c 2014-02-04 11:23:35.000000000 +0000 @@ -1,20 +1,16 @@ /* * Control interface for shared AP commands - * Copyright (c) 2004-2009, Jouni Malinen + * Copyright (c) 2004-2013, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * 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 "eapol_auth/eapol_auth_sm.h" #include "hostapd.h" #include "ieee802_1x.h" #include "wpa_auth.h" @@ -23,28 +19,110 @@ #include "wps_hostapd.h" #include "p2p_hostapd.h" #include "ctrl_iface_ap.h" +#include "ap_drv_ops.h" + + +static int hostapd_get_sta_tx_rx(struct hostapd_data *hapd, + struct sta_info *sta, + char *buf, size_t buflen) +{ + struct hostap_sta_driver_data data; + int ret; + + 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", + data.rx_packets, data.tx_packets, + data.rx_bytes, data.tx_bytes); + if (ret < 0 || (size_t) ret >= buflen) + return 0; + return ret; +} + + +static int hostapd_get_sta_conn_time(struct sta_info *sta, + char *buf, size_t buflen) +{ + struct os_reltime age; + int ret; + + if (!sta->connected_time.sec) + return 0; + + os_reltime_age(&sta->connected_time, &age); + + ret = os_snprintf(buf, buflen, "connected_time=%u\n", + (unsigned int) age.sec); + if (ret < 0 || (size_t) ret >= buflen) + return 0; + return ret; +} + + +static const char * timeout_next_str(int val) +{ + switch (val) { + case STA_NULLFUNC: + return "NULLFUNC POLL"; + case STA_DISASSOC: + return "DISASSOC"; + case STA_DEAUTH: + return "DEAUTH"; + case STA_REMOVE: + return "REMOVE"; + case STA_DISASSOC_FROM_CLI: + return "DISASSOC_FROM_CLI"; + } + + return "?"; +} static int hostapd_ctrl_iface_sta_mib(struct hostapd_data *hapd, struct sta_info *sta, char *buf, size_t buflen) { - int len, res, ret; + int len, res, ret, i; - if (sta == NULL) { - ret = os_snprintf(buf, buflen, "FAIL\n"); - if (ret < 0 || (size_t) ret >= buflen) - return 0; - return ret; - } + if (!sta) + return 0; len = 0; - ret = os_snprintf(buf + len, buflen - len, MACSTR "\n", + ret = os_snprintf(buf + len, buflen - len, MACSTR "\nflags=", MAC2STR(sta->addr)); if (ret < 0 || (size_t) ret >= buflen - len) return len; len += ret; + ret = ap_sta_flags_txt(sta->flags, buf + len, buflen - len); + if (ret < 0) + return len; + len += ret; + + ret = os_snprintf(buf + len, buflen - len, "\naid=%d\ncapability=0x%x\n" + "listen_interval=%d\nsupported_rates=", + sta->aid, sta->capability, sta->listen_interval); + if (ret < 0 || (size_t) ret >= buflen - len) + return len; + len += ret; + + for (i = 0; i < sta->supported_rates_len; i++) { + ret = os_snprintf(buf + len, buflen - len, "%02x%s", + sta->supported_rates[i], + i + 1 < sta->supported_rates_len ? " " : ""); + if (ret < 0 || (size_t) ret >= buflen - len) + return len; + len += ret; + } + + ret = os_snprintf(buf + len, buflen - len, "\ntimeout_next=%s\n", + timeout_next_str(sta->timeout_next)); + if (ret < 0 || (size_t) ret >= buflen - len) + return len; + len += ret; + res = ieee802_11_get_mib_sta(hapd, sta, buf + len, buflen - len); if (res >= 0) len += res; @@ -62,6 +140,9 @@ if (res >= 0) len += res; + len += hostapd_get_sta_tx_rx(hapd, sta, buf + len, buflen - len); + len += hostapd_get_sta_conn_time(sta, buf + len, buflen - len); + return len; } @@ -78,6 +159,8 @@ { u8 addr[ETH_ALEN]; int ret; + const char *pos; + struct sta_info *sta; if (hwaddr_aton(txtaddr, addr)) { ret = os_snprintf(buf, buflen, "FAIL\n"); @@ -85,8 +168,28 @@ return 0; return ret; } - return hostapd_ctrl_iface_sta_mib(hapd, ap_get_sta(hapd, addr), - buf, buflen); + + sta = ap_get_sta(hapd, addr); + if (sta == NULL) + return -1; + + pos = os_strchr(txtaddr, ' '); + if (pos) { + pos++; + +#ifdef HOSTAPD_DUMP_STATE + if (os_strcmp(pos, "eapol") == 0) { + if (sta->eapol_sm == NULL) + return -1; + return eapol_auth_dump_state(sta->eapol_sm, buf, + buflen); + } +#endif /* HOSTAPD_DUMP_STATE */ + + return -1; + } + + return hostapd_ctrl_iface_sta_mib(hapd, sta, buf, buflen); } @@ -103,6 +206,305 @@ if (ret < 0 || (size_t) ret >= buflen) return 0; return ret; - } + } + + if (!sta->next) + return 0; + return hostapd_ctrl_iface_sta_mib(hapd, sta->next, buf, buflen); } + + +#ifdef CONFIG_P2P_MANAGER +static int p2p_manager_disconnect(struct hostapd_data *hapd, u16 stype, + u8 minor_reason_code, const u8 *addr) +{ + struct ieee80211_mgmt *mgmt; + int ret; + u8 *pos; + + if (hapd->driver->send_frame == NULL) + return -1; + + mgmt = os_zalloc(sizeof(*mgmt) + 100); + if (mgmt == NULL) + return -1; + + wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "P2P: Disconnect STA " MACSTR + " with minor reason code %u (stype=%u)", + MAC2STR(addr), minor_reason_code, stype); + + mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, stype); + 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); + 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); + } else { + mgmt->u.disassoc.reason_code = + host_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID); + pos = (u8 *) (&mgmt->u.disassoc.reason_code + 1); + } + + *pos++ = WLAN_EID_VENDOR_SPECIFIC; + *pos++ = 4 + 3 + 1; + WPA_PUT_BE24(pos, OUI_WFA); + pos += 3; + *pos++ = P2P_OUI_TYPE; + + *pos++ = P2P_ATTR_MINOR_REASON_CODE; + WPA_PUT_LE16(pos, 1); + pos += 2; + *pos++ = minor_reason_code; + + ret = hapd->driver->send_frame(hapd->drv_priv, (u8 *) mgmt, + pos - (u8 *) mgmt, 1); + os_free(mgmt); + + return ret < 0 ? -1 : 0; +} +#endif /* CONFIG_P2P_MANAGER */ + + +int hostapd_ctrl_iface_deauthenticate(struct hostapd_data *hapd, + const char *txtaddr) +{ + u8 addr[ETH_ALEN]; + struct sta_info *sta; + const char *pos; + u16 reason = WLAN_REASON_PREV_AUTH_NOT_VALID; + + wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "CTRL_IFACE DEAUTHENTICATE %s", + txtaddr); + + if (hwaddr_aton(txtaddr, addr)) + return -1; + + pos = os_strstr(txtaddr, " test="); + if (pos) { + struct ieee80211_mgmt mgmt; + int encrypt; + if (hapd->driver->send_frame == NULL) + return -1; + pos += 6; + encrypt = atoi(pos); + os_memset(&mgmt, 0, sizeof(mgmt)); + mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, + WLAN_FC_STYPE_DEAUTH); + 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.deauth.reason_code = + host_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID); + if (hapd->driver->send_frame(hapd->drv_priv, (u8 *) &mgmt, + IEEE80211_HDRLEN + + sizeof(mgmt.u.deauth), + encrypt) < 0) + return -1; + return 0; + } + +#ifdef CONFIG_P2P_MANAGER + pos = os_strstr(txtaddr, " p2p="); + if (pos) { + return p2p_manager_disconnect(hapd, WLAN_FC_STYPE_DEAUTH, + atoi(pos + 5), addr); + } +#endif /* CONFIG_P2P_MANAGER */ + + pos = os_strstr(txtaddr, " reason="); + if (pos) + reason = atoi(pos + 8); + + hostapd_drv_sta_deauth(hapd, addr, reason); + sta = ap_get_sta(hapd, addr); + if (sta) + ap_sta_deauthenticate(hapd, sta, reason); + else if (addr[0] == 0xff) + hostapd_free_stas(hapd); + + return 0; +} + + +int hostapd_ctrl_iface_disassociate(struct hostapd_data *hapd, + const char *txtaddr) +{ + u8 addr[ETH_ALEN]; + struct sta_info *sta; + const char *pos; + u16 reason = WLAN_REASON_PREV_AUTH_NOT_VALID; + + wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "CTRL_IFACE DISASSOCIATE %s", + txtaddr); + + if (hwaddr_aton(txtaddr, addr)) + return -1; + + pos = os_strstr(txtaddr, " test="); + if (pos) { + struct ieee80211_mgmt mgmt; + int encrypt; + if (hapd->driver->send_frame == NULL) + return -1; + pos += 6; + encrypt = atoi(pos); + os_memset(&mgmt, 0, sizeof(mgmt)); + mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, + WLAN_FC_STYPE_DISASSOC); + 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.disassoc.reason_code = + host_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID); + if (hapd->driver->send_frame(hapd->drv_priv, (u8 *) &mgmt, + IEEE80211_HDRLEN + + sizeof(mgmt.u.deauth), + encrypt) < 0) + return -1; + return 0; + } + +#ifdef CONFIG_P2P_MANAGER + pos = os_strstr(txtaddr, " p2p="); + if (pos) { + return p2p_manager_disconnect(hapd, WLAN_FC_STYPE_DISASSOC, + atoi(pos + 5), addr); + } +#endif /* CONFIG_P2P_MANAGER */ + + pos = os_strstr(txtaddr, " reason="); + if (pos) + reason = atoi(pos + 8); + + hostapd_drv_sta_disassoc(hapd, addr, reason); + sta = ap_get_sta(hapd, addr); + if (sta) + ap_sta_disassociate(hapd, sta, reason); + else if (addr[0] == 0xff) + hostapd_free_stas(hapd); + + 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; + size_t i; + + ret = os_snprintf(buf + len, buflen - len, + "state=%s\n" + "phy=%s\n" + "freq=%d\n" + "num_sta_non_erp=%d\n" + "num_sta_no_short_slot_time=%d\n" + "num_sta_no_short_preamble=%d\n" + "olbc=%d\n" + "num_sta_ht_no_gf=%d\n" + "num_sta_no_ht=%d\n" + "num_sta_ht_20_mhz=%d\n" + "olbc_ht=%d\n" + "ht_op_mode=0x%x\n", + hostapd_state_text(iface->state), + iface->phy, + iface->freq, + iface->num_sta_non_erp, + iface->num_sta_no_short_slot_time, + iface->num_sta_no_short_preamble, + iface->olbc, + iface->num_sta_ht_no_gf, + iface->num_sta_no_ht, + iface->num_sta_ht_20mhz, + iface->olbc_ht, + iface->ht_op_mode); + if (ret < 0 || (size_t) ret >= buflen - len) + return len; + len += ret; + + ret = os_snprintf(buf + len, buflen - len, + "channel=%u\n" + "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", + 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); + if (ret < 0 || (size_t) ret >= buflen - len) + return len; + len += ret; + + for (i = 0; i < iface->num_bss; i++) { + struct hostapd_data *bss = iface->bss[i]; + ret = os_snprintf(buf + len, buflen - len, + "bss[%d]=%s\n" + "bssid[%d]=" MACSTR "\n" + "ssid[%d]=%s\n" + "num_sta[%d]=%d\n", + (int) i, bss->conf->iface, + (int) i, MAC2STR(bss->own_addr), + (int) i, + wpa_ssid_txt(bss->conf->ssid.ssid, + bss->conf->ssid.ssid_len), + (int) i, bss->num_sta); + if (ret < 0 || (size_t) ret >= buflen - len) + return len; + len += ret; + } + + return len; +} + + +int hostapd_parse_csa_settings(const char *pos, + struct csa_settings *settings) +{ + char *end; + + if (!settings) + return -1; + + os_memset(settings, 0, sizeof(*settings)); + settings->cs_count = strtol(pos, &end, 10); + if (pos == end) { + wpa_printf(MSG_ERROR, "chanswitch: invalid cs_count provided"); + return -1; + } + + settings->freq_params.freq = atoi(end); + if (settings->freq_params.freq == 0) { + wpa_printf(MSG_ERROR, "chanswitch: invalid freq provided"); + return -1; + } + +#define SET_CSA_SETTING(str) \ + do { \ + const char *pos2 = os_strstr(pos, " " #str "="); \ + if (pos2) { \ + pos2 += sizeof(" " #str "=") - 1; \ + settings->freq_params.str = atoi(pos2); \ + } \ + } while (0) + + SET_CSA_SETTING(center_freq1); + SET_CSA_SETTING(center_freq2); + SET_CSA_SETTING(bandwidth); + SET_CSA_SETTING(sec_channel_offset); + settings->freq_params.ht_enabled = !!os_strstr(pos, " ht"); + settings->freq_params.vht_enabled = !!os_strstr(pos, " vht"); + settings->block_tx = !!os_strstr(pos, " blocktx"); +#undef SET_CSA_SETTING + + return 0; +} diff -Nru wpa-1.0/src/ap/ctrl_iface_ap.h wpa-2.1/src/ap/ctrl_iface_ap.h --- wpa-1.0/src/ap/ctrl_iface_ap.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/ap/ctrl_iface_ap.h 2014-02-04 11:23:35.000000000 +0000 @@ -1,15 +1,9 @@ /* * Control interface for shared AP commands - * Copyright (c) 2004-2009, Jouni Malinen + * Copyright (c) 2004-2013, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef CTRL_IFACE_AP_H @@ -21,5 +15,14 @@ char *buf, size_t buflen); int hostapd_ctrl_iface_sta_next(struct hostapd_data *hapd, const char *txtaddr, char *buf, size_t buflen); +int hostapd_ctrl_iface_deauthenticate(struct hostapd_data *hapd, + const char *txtaddr); +int hostapd_ctrl_iface_disassociate(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); + #endif /* CTRL_IFACE_AP_H */ diff -Nru wpa-1.0/src/ap/dfs.c wpa-2.1/src/ap/dfs.c --- wpa-1.0/src/ap/dfs.c 1970-01-01 00:00:00.000000000 +0000 +++ wpa-2.1/src/ap/dfs.c 2014-02-04 11:23:35.000000000 +0000 @@ -0,0 +1,803 @@ +/* + * DFS - Dynamic Frequency Selection + * Copyright (c) 2002-2013, Jouni Malinen + * Copyright (c) 2013, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" + +#include "utils/common.h" +#include "common/ieee802_11_defs.h" +#include "common/wpa_ctrl.h" +#include "hostapd.h" +#include "ap_drv_ops.h" +#include "drivers/driver.h" +#include "dfs.h" + + +static int dfs_get_used_n_chans(struct hostapd_iface *iface) +{ + int n_chans = 1; + + if (iface->conf->ieee80211n && iface->conf->secondary_channel) + n_chans = 2; + + if (iface->conf->ieee80211ac) { + switch (iface->conf->vht_oper_chwidth) { + case VHT_CHANWIDTH_USE_HT: + break; + case VHT_CHANWIDTH_80MHZ: + n_chans = 4; + break; + case VHT_CHANWIDTH_160MHZ: + n_chans = 8; + break; + default: + break; + } + } + + return n_chans; +} + + +static int dfs_channel_available(struct hostapd_channel_data *chan, + int skip_radar) +{ + /* + * When radar detection happens, CSA is performed. However, there's no + * time for CAC, so radar channels must be skipped when finding a new + * channel for CSA. + */ + if (skip_radar && chan->flag & HOSTAPD_CHAN_RADAR) + return 0; + + if (chan->flag & HOSTAPD_CHAN_DISABLED) + return 0; + if ((chan->flag & HOSTAPD_CHAN_RADAR) && + ((chan->flag & HOSTAPD_CHAN_DFS_MASK) == + HOSTAPD_CHAN_DFS_UNAVAILABLE)) + return 0; + return 1; +} + + +static int dfs_is_chan_allowed(struct hostapd_channel_data *chan, int n_chans) +{ + /* + * The tables contain first valid channel number based on channel width. + * We will also choose this first channel as the control one. + */ + int allowed_40[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 149, 157, + 184, 192 }; + /* + * VHT80, valid channels based on center frequency: + * 42, 58, 106, 122, 138, 155 + */ + int allowed_80[] = { 36, 52, 100, 116, 132, 149 }; + int *allowed = allowed_40; + unsigned int i, allowed_no = 0; + + switch (n_chans) { + case 2: + allowed = allowed_40; + allowed_no = ARRAY_SIZE(allowed_40); + break; + case 4: + allowed = allowed_80; + allowed_no = ARRAY_SIZE(allowed_80); + break; + default: + wpa_printf(MSG_DEBUG, "Unknown width for %d channels", n_chans); + break; + } + + for (i = 0; i < allowed_no; i++) { + if (chan->chan == allowed[i]) + return 1; + } + + return 0; +} + + +static int dfs_chan_range_available(struct hostapd_hw_modes *mode, + int first_chan_idx, int num_chans, + int skip_radar) +{ + struct hostapd_channel_data *first_chan, *chan; + int i; + + if (first_chan_idx + num_chans >= mode->num_channels) + return 0; + + first_chan = &mode->channels[first_chan_idx]; + + for (i = 0; i < num_chans; i++) { + chan = &mode->channels[first_chan_idx + i]; + + if (first_chan->freq + i * 20 != chan->freq) + return 0; + + if (!dfs_channel_available(chan, skip_radar)) + return 0; + } + + return 1; +} + + +/* + * The function assumes HT40+ operation. + * Make sure to adjust the following variables after calling this: + * - hapd->secondary_channel + * - hapd->vht_oper_centr_freq_seg0_idx + * - hapd->vht_oper_centr_freq_seg1_idx + */ +static int dfs_find_channel(struct hostapd_iface *iface, + struct hostapd_channel_data **ret_chan, + int idx, int skip_radar) +{ + struct hostapd_hw_modes *mode; + struct hostapd_channel_data *chan; + int i, channel_idx = 0, n_chans; + + mode = iface->current_mode; + n_chans = dfs_get_used_n_chans(iface); + + wpa_printf(MSG_DEBUG, "DFS new chan checking %d channels", n_chans); + for (i = 0; i < mode->num_channels; i++) { + chan = &mode->channels[i]; + + /* Skip HT40/VHT incompatible channels */ + if (iface->conf->ieee80211n && + iface->conf->secondary_channel && + !dfs_is_chan_allowed(chan, n_chans)) + continue; + + /* Skip incompatible chandefs */ + if (!dfs_chan_range_available(mode, i, n_chans, skip_radar)) + continue; + + if (ret_chan && idx == channel_idx) { + wpa_printf(MSG_DEBUG, "Selected ch. #%d", chan->chan); + *ret_chan = chan; + return idx; + } + wpa_printf(MSG_DEBUG, "Adding channel: %d", chan->chan); + channel_idx++; + } + return channel_idx; +} + + +static void dfs_adjust_vht_center_freq(struct hostapd_iface *iface, + struct hostapd_channel_data *chan, + int secondary_channel, + u8 *vht_oper_centr_freq_seg0_idx, + u8 *vht_oper_centr_freq_seg1_idx) +{ + if (!iface->conf->ieee80211ac) + return; + + if (!chan) + return; + + *vht_oper_centr_freq_seg1_idx = 0; + + switch (iface->conf->vht_oper_chwidth) { + case VHT_CHANWIDTH_USE_HT: + if (secondary_channel == 1) + *vht_oper_centr_freq_seg0_idx = chan->chan + 2; + else if (secondary_channel == -1) + *vht_oper_centr_freq_seg0_idx = chan->chan - 2; + else + *vht_oper_centr_freq_seg0_idx = chan->chan; + break; + case VHT_CHANWIDTH_80MHZ: + *vht_oper_centr_freq_seg0_idx = chan->chan + 6; + break; + case VHT_CHANWIDTH_160MHZ: + *vht_oper_centr_freq_seg0_idx = chan->chan + 14; + break; + default: + wpa_printf(MSG_INFO, "DFS only VHT20/40/80/160 is supported now"); + break; + } + + wpa_printf(MSG_DEBUG, "DFS adjusting VHT center frequency: %d, %d", + *vht_oper_centr_freq_seg0_idx, + *vht_oper_centr_freq_seg1_idx); +} + + +/* Return start channel idx we will use for mode->channels[idx] */ +static int dfs_get_start_chan_idx(struct hostapd_iface *iface) +{ + struct hostapd_hw_modes *mode; + struct hostapd_channel_data *chan; + int channel_no = iface->conf->channel; + int res = -1, i; + + /* HT40- */ + if (iface->conf->ieee80211n && iface->conf->secondary_channel == -1) + channel_no -= 4; + + /* VHT */ + if (iface->conf->ieee80211ac) { + switch (iface->conf->vht_oper_chwidth) { + case VHT_CHANWIDTH_USE_HT: + break; + case VHT_CHANWIDTH_80MHZ: + channel_no = + iface->conf->vht_oper_centr_freq_seg0_idx - 6; + break; + case VHT_CHANWIDTH_160MHZ: + channel_no = + iface->conf->vht_oper_centr_freq_seg0_idx - 14; + break; + default: + wpa_printf(MSG_INFO, + "DFS only VHT20/40/80/160 is supported now"); + channel_no = -1; + break; + } + } + + /* Get idx */ + mode = iface->current_mode; + for (i = 0; i < mode->num_channels; i++) { + chan = &mode->channels[i]; + if (chan->chan == channel_no) { + res = i; + break; + } + } + + if (res == -1) + wpa_printf(MSG_DEBUG, "DFS chan_idx seems wrong: -1"); + + return res; +} + + +/* At least one channel have radar flag */ +static int dfs_check_chans_radar(struct hostapd_iface *iface, + int start_chan_idx, int n_chans) +{ + struct hostapd_channel_data *channel; + struct hostapd_hw_modes *mode; + int i, res = 0; + + mode = iface->current_mode; + + for (i = 0; i < n_chans; i++) { + channel = &mode->channels[start_chan_idx + i]; + if (channel->flag & HOSTAPD_CHAN_RADAR) + res++; + } + + return res; +} + + +/* All channels available */ +static int dfs_check_chans_available(struct hostapd_iface *iface, + int start_chan_idx, int n_chans) +{ + struct hostapd_channel_data *channel; + struct hostapd_hw_modes *mode; + int i; + + mode = iface->current_mode; + + for(i = 0; i < n_chans; i++) { + channel = &mode->channels[start_chan_idx + i]; + if ((channel->flag & HOSTAPD_CHAN_DFS_MASK) != + HOSTAPD_CHAN_DFS_AVAILABLE) + break; + } + + return i == n_chans; +} + + +/* At least one channel unavailable */ +static int dfs_check_chans_unavailable(struct hostapd_iface *iface, + int start_chan_idx, + int n_chans) +{ + struct hostapd_channel_data *channel; + struct hostapd_hw_modes *mode; + int i, res = 0; + + mode = iface->current_mode; + + for(i = 0; i < n_chans; i++) { + channel = &mode->channels[start_chan_idx + i]; + if (channel->flag & HOSTAPD_CHAN_DISABLED) + res++; + if ((channel->flag & HOSTAPD_CHAN_DFS_MASK) == + HOSTAPD_CHAN_DFS_UNAVAILABLE) + res++; + } + + return res; +} + + +static struct hostapd_channel_data * +dfs_get_valid_channel(struct hostapd_iface *iface, + int *secondary_channel, + u8 *vht_oper_centr_freq_seg0_idx, + u8 *vht_oper_centr_freq_seg1_idx, + int skip_radar) +{ + struct hostapd_hw_modes *mode; + struct hostapd_channel_data *chan = NULL; + int num_available_chandefs; + int chan_idx; + u32 _rand; + + wpa_printf(MSG_DEBUG, "DFS: Selecting random channel"); + + if (iface->current_mode == NULL) + return NULL; + + mode = iface->current_mode; + if (mode->mode != HOSTAPD_MODE_IEEE80211A) + return NULL; + + /* Get the count first */ + num_available_chandefs = dfs_find_channel(iface, NULL, 0, skip_radar); + if (num_available_chandefs == 0) + return NULL; + + os_get_random((u8 *) &_rand, sizeof(_rand)); + chan_idx = _rand % num_available_chandefs; + dfs_find_channel(iface, &chan, chan_idx, skip_radar); + + /* dfs_find_channel() calculations assume HT40+ */ + if (iface->conf->secondary_channel) + *secondary_channel = 1; + else + *secondary_channel = 0; + + dfs_adjust_vht_center_freq(iface, chan, + *secondary_channel, + vht_oper_centr_freq_seg0_idx, + vht_oper_centr_freq_seg1_idx); + + return chan; +} + + +static int set_dfs_state_freq(struct hostapd_iface *iface, int freq, u32 state) +{ + struct hostapd_hw_modes *mode; + struct hostapd_channel_data *chan = NULL; + int i; + + mode = iface->current_mode; + if (mode == NULL) + return 0; + + wpa_printf(MSG_DEBUG, "set_dfs_state 0x%X for %d MHz", state, freq); + for (i = 0; i < iface->current_mode->num_channels; i++) { + chan = &iface->current_mode->channels[i]; + if (chan->freq == freq) { + if (chan->flag & HOSTAPD_CHAN_RADAR) { + chan->flag &= ~HOSTAPD_CHAN_DFS_MASK; + chan->flag |= state; + return 1; /* Channel found */ + } + } + } + wpa_printf(MSG_WARNING, "Can't set DFS state for freq %d MHz", freq); + return 0; +} + + +static int set_dfs_state(struct hostapd_iface *iface, int freq, int ht_enabled, + int chan_offset, int chan_width, int cf1, + int cf2, u32 state) +{ + int n_chans = 1, i; + struct hostapd_hw_modes *mode; + int frequency = freq; + int ret = 0; + + mode = iface->current_mode; + if (mode == NULL) + return 0; + + if (mode->mode != HOSTAPD_MODE_IEEE80211A) { + wpa_printf(MSG_WARNING, "current_mode != IEEE80211A"); + return 0; + } + + /* Seems cf1 and chan_width is enough here */ + switch (chan_width) { + case CHAN_WIDTH_20_NOHT: + case CHAN_WIDTH_20: + n_chans = 1; + if (frequency == 0) + frequency = cf1; + break; + case CHAN_WIDTH_40: + n_chans = 2; + frequency = cf1 - 10; + break; + case CHAN_WIDTH_80: + n_chans = 4; + frequency = cf1 - 30; + break; + case CHAN_WIDTH_160: + n_chans = 8; + frequency = cf1 - 70; + break; + default: + wpa_printf(MSG_INFO, "DFS chan_width %d not supported", + chan_width); + break; + } + + wpa_printf(MSG_DEBUG, "DFS freq: %dMHz, n_chans: %d", frequency, + n_chans); + for (i = 0; i < n_chans; i++) { + ret += set_dfs_state_freq(iface, frequency, state); + frequency = frequency + 20; + } + + return ret; +} + + +static int dfs_are_channels_overlapped(struct hostapd_iface *iface, int freq, + int chan_width, int cf1, int cf2) +{ + int start_chan_idx; + struct hostapd_hw_modes *mode; + struct hostapd_channel_data *chan; + int n_chans, i, j, frequency = freq, radar_n_chans = 1; + u8 radar_chan; + int res = 0; + + /* Our configuration */ + mode = iface->current_mode; + start_chan_idx = dfs_get_start_chan_idx(iface); + n_chans = dfs_get_used_n_chans(iface); + + /* Check we are on DFS channel(s) */ + if (!dfs_check_chans_radar(iface, start_chan_idx, n_chans)) + return 0; + + /* Reported via radar event */ + switch (chan_width) { + case CHAN_WIDTH_20_NOHT: + case CHAN_WIDTH_20: + radar_n_chans = 1; + if (frequency == 0) + frequency = cf1; + break; + case CHAN_WIDTH_40: + radar_n_chans = 2; + frequency = cf1 - 10; + break; + case CHAN_WIDTH_80: + radar_n_chans = 4; + frequency = cf1 - 30; + break; + case CHAN_WIDTH_160: + radar_n_chans = 8; + frequency = cf1 - 70; + break; + default: + wpa_printf(MSG_INFO, "DFS chan_width %d not supported", + chan_width); + break; + } + + ieee80211_freq_to_chan(frequency, &radar_chan); + + for (i = 0; i < n_chans; i++) { + chan = &mode->channels[start_chan_idx + i]; + if (!(chan->flag & HOSTAPD_CHAN_RADAR)) + continue; + for (j = 0; j < radar_n_chans; j++) { + wpa_printf(MSG_DEBUG, "checking our: %d, radar: %d", + chan->chan, radar_chan + j * 4); + if (chan->chan == radar_chan + j * 4) + res++; + } + } + + wpa_printf(MSG_DEBUG, "overlapped: %d", res); + + return res; +} + + +/* + * Main DFS handler + * 1 - continue channel/ap setup + * 0 - channel/ap setup will be continued after CAC + * -1 - hit critical error + */ +int hostapd_handle_dfs(struct hostapd_iface *iface) +{ + struct hostapd_channel_data *channel; + int res, n_chans, start_chan_idx; + int skip_radar = 0; + + iface->cac_started = 0; + + do { + /* Get start (first) channel for current configuration */ + start_chan_idx = dfs_get_start_chan_idx(iface); + if (start_chan_idx == -1) + return -1; + + /* Get number of used channels, depend on width */ + n_chans = dfs_get_used_n_chans(iface); + + /* Check if any of configured channels require DFS */ + res = dfs_check_chans_radar(iface, start_chan_idx, n_chans); + wpa_printf(MSG_DEBUG, + "DFS %d channels required radar detection", + res); + if (!res) + return 1; + + /* Check if all channels are DFS available */ + res = dfs_check_chans_available(iface, start_chan_idx, n_chans); + wpa_printf(MSG_DEBUG, + "DFS all channels available, (SKIP CAC): %s", + res ? "yes" : "no"); + if (res) + return 1; + + /* Check if any of configured channels is unavailable */ + res = dfs_check_chans_unavailable(iface, start_chan_idx, + n_chans); + wpa_printf(MSG_DEBUG, "DFS %d chans unavailable - choose other channel: %s", + res, res ? "yes": "no"); + if (res) { + int sec; + u8 cf1, cf2; + + channel = dfs_get_valid_channel(iface, &sec, &cf1, &cf2, + skip_radar); + if (!channel) { + wpa_printf(MSG_ERROR, "could not get valid channel"); + return -1; + } + + iface->freq = channel->freq; + iface->conf->channel = channel->chan; + iface->conf->secondary_channel = sec; + iface->conf->vht_oper_centr_freq_seg0_idx = cf1; + iface->conf->vht_oper_centr_freq_seg1_idx = cf2; + } + } while (res); + + /* Finally start CAC */ + hostapd_set_state(iface, HAPD_IFACE_DFS); + wpa_printf(MSG_DEBUG, "DFS start CAC on %d MHz", iface->freq); + wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_CAC_START + "freq=%d chan=%d sec_chan=%d", + iface->freq, + iface->conf->channel, iface->conf->secondary_channel); + if (hostapd_start_dfs_cac(iface, iface->conf->hw_mode, + iface->freq, + iface->conf->channel, + iface->conf->ieee80211n, + iface->conf->ieee80211ac, + iface->conf->secondary_channel, + iface->conf->vht_oper_chwidth, + iface->conf->vht_oper_centr_freq_seg0_idx, + iface->conf->vht_oper_centr_freq_seg1_idx)) { + wpa_printf(MSG_DEBUG, "DFS start_dfs_cac() failed"); + return -1; + } + + return 0; +} + + +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) +{ + wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_CAC_COMPLETED + "success=%d freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d", + success, freq, ht_enabled, chan_offset, chan_width, cf1, cf2); + + if (success) { + /* Complete iface/ap configuration */ + 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); + } + + return 0; +} + + +static int hostapd_dfs_start_channel_switch_cac(struct hostapd_iface *iface) +{ + struct hostapd_channel_data *channel; + int secondary_channel; + u8 vht_oper_centr_freq_seg0_idx; + u8 vht_oper_centr_freq_seg1_idx; + int skip_radar = 0; + int err = 1; + + /* Radar detected during active CAC */ + iface->cac_started = 0; + channel = dfs_get_valid_channel(iface, &secondary_channel, + &vht_oper_centr_freq_seg0_idx, + &vht_oper_centr_freq_seg1_idx, + skip_radar); + + if (!channel) { + wpa_printf(MSG_ERROR, "No valid channel available"); + hostapd_setup_interface_complete(iface, err); + return err; + } + + wpa_printf(MSG_DEBUG, "DFS will switch to a new channel %d", + channel->chan); + wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_NEW_CHANNEL + "freq=%d chan=%d sec_chan=%d", channel->freq, + channel->chan, secondary_channel); + + iface->freq = channel->freq; + iface->conf->channel = channel->chan; + iface->conf->secondary_channel = secondary_channel; + iface->conf->vht_oper_centr_freq_seg0_idx = + vht_oper_centr_freq_seg0_idx; + iface->conf->vht_oper_centr_freq_seg1_idx = + vht_oper_centr_freq_seg1_idx; + err = 0; + + hostapd_setup_interface_complete(iface, err); + return err; +} + + +static int hostapd_dfs_start_channel_switch(struct hostapd_iface *iface) +{ + struct hostapd_channel_data *channel; + int secondary_channel; + u8 vht_oper_centr_freq_seg0_idx; + u8 vht_oper_centr_freq_seg1_idx; + int skip_radar = 1; + struct csa_settings csa_settings; + struct hostapd_data *hapd = iface->bss[0]; + int err = 1; + + wpa_printf(MSG_DEBUG, "%s called (CAC active: %s, CSA active: %s)", + __func__, iface->cac_started ? "yes" : "no", + iface->csa_in_progress ? "yes" : "no"); + + /* Check if CSA in progress */ + if (iface->csa_in_progress) + return 0; + + /* Check if active CAC */ + if (iface->cac_started) + return hostapd_dfs_start_channel_switch_cac(iface); + + /* Perform channel switch/CSA */ + channel = dfs_get_valid_channel(iface, &secondary_channel, + &vht_oper_centr_freq_seg0_idx, + &vht_oper_centr_freq_seg1_idx, + skip_radar); + + if (!channel) { + /* FIXME: Wait for channel(s) to become available */ + hostapd_disable_iface(iface); + return err; + } + + wpa_printf(MSG_DEBUG, "DFS will switch to a new channel %d", + channel->chan); + wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_NEW_CHANNEL + "freq=%d chan=%d sec_chan=%d", channel->freq, + channel->chan, secondary_channel); + + /* Setup CSA request */ + os_memset(&csa_settings, 0, sizeof(csa_settings)); + csa_settings.cs_count = 5; + csa_settings.block_tx = 1; + err = hostapd_set_freq_params(&csa_settings.freq_params, + iface->conf->hw_mode, + channel->freq, + channel->chan, + iface->conf->ieee80211n, + iface->conf->ieee80211ac, + secondary_channel, + iface->conf->vht_oper_chwidth, + vht_oper_centr_freq_seg0_idx, + vht_oper_centr_freq_seg1_idx, + iface->current_mode->vht_capab); + + if (err) { + wpa_printf(MSG_ERROR, "DFS failed to calculate CSA freq params"); + hostapd_disable_iface(iface); + return err; + } + + err = hostapd_switch_channel(hapd, &csa_settings); + if (err) { + wpa_printf(MSG_WARNING, "DFS failed to schedule CSA (%d) - trying fallback", + err); + iface->freq = channel->freq; + iface->conf->channel = channel->chan; + iface->conf->secondary_channel = secondary_channel; + iface->conf->vht_oper_centr_freq_seg0_idx = + vht_oper_centr_freq_seg0_idx; + iface->conf->vht_oper_centr_freq_seg1_idx = + vht_oper_centr_freq_seg1_idx; + + hostapd_disable_iface(iface); + hostapd_enable_iface(iface); + return 0; + } + + /* Channel configuration will be updated once CSA completes and + * ch_switch_notify event is received */ + + wpa_printf(MSG_DEBUG, "DFS waiting channel switch event"); + return 0; +} + + +int hostapd_dfs_radar_detected(struct hostapd_iface *iface, int freq, + int ht_enabled, int chan_offset, int chan_width, + int cf1, int cf2) +{ + int res; + + if (!iface->conf->ieee80211h) + return 0; + + wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_RADAR_DETECTED + "freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d", + freq, ht_enabled, chan_offset, chan_width, cf1, cf2); + + /* mark radar frequency as invalid */ + res = set_dfs_state(iface, freq, ht_enabled, chan_offset, + chan_width, cf1, cf2, + HOSTAPD_CHAN_DFS_UNAVAILABLE); + + /* Skip if reported radar event not overlapped our channels */ + res = dfs_are_channels_overlapped(iface, freq, chan_width, cf1, cf2); + if (!res) + return 0; + + /* radar detected while operating, switch the channel. */ + res = hostapd_dfs_start_channel_switch(iface); + + return res; +} + + +int hostapd_dfs_nop_finished(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_NOP_FINISHED + "freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d", + freq, ht_enabled, chan_offset, chan_width, cf1, cf2); + /* TODO add correct implementation here */ + set_dfs_state(iface, freq, ht_enabled, chan_offset, chan_width, + cf1, cf2, HOSTAPD_CHAN_DFS_USABLE); + return 0; +} diff -Nru wpa-1.0/src/ap/dfs.h wpa-2.1/src/ap/dfs.h --- wpa-1.0/src/ap/dfs.h 1970-01-01 00:00:00.000000000 +0000 +++ wpa-2.1/src/ap/dfs.h 2014-02-04 11:23:35.000000000 +0000 @@ -0,0 +1,25 @@ +/* + * DFS - Dynamic Frequency Selection + * Copyright (c) 2002-2013, Jouni Malinen + * Copyright (c) 2013, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ +#ifndef DFS_H +#define DFS_H + +int hostapd_handle_dfs(struct hostapd_iface *iface); + +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_radar_detected(struct hostapd_iface *iface, int freq, + int ht_enabled, + int chan_offset, int chan_width, + int cf1, int cf2); +int hostapd_dfs_nop_finished(struct hostapd_iface *iface, int freq, + int ht_enabled, + int chan_offset, int chan_width, int cf1, int cf2); + +#endif /* DFS_H */ diff -Nru wpa-1.0/src/ap/drv_callbacks.c wpa-2.1/src/ap/drv_callbacks.c --- wpa-1.0/src/ap/drv_callbacks.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/ap/drv_callbacks.c 2014-02-04 11:23:35.000000000 +0000 @@ -1,15 +1,9 @@ /* * hostapd / Callback functions for driver wrappers - * Copyright (c) 2002-2009, Jouni Malinen + * Copyright (c) 2002-2013, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "utils/includes.h" @@ -23,18 +17,19 @@ #include "crypto/random.h" #include "p2p/p2p.h" #include "wps/wps.h" +#include "wnm_ap.h" #include "hostapd.h" #include "ieee802_11.h" #include "sta_info.h" #include "accounting.h" #include "tkip_countermeasures.h" -#include "iapp.h" #include "ieee802_1x.h" #include "wpa_auth.h" -#include "wmm.h" #include "wps_hostapd.h" #include "ap_drv_ops.h" #include "ap_config.h" +#include "hw_features.h" +#include "dfs.h" int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, @@ -45,7 +40,13 @@ struct ieee802_11_elems elems; const u8 *ie; size_t ielen; +#ifdef CONFIG_IEEE80211R + u8 buf[sizeof(struct ieee80211_mgmt) + 1024]; + u8 *p = buf; +#endif /* CONFIG_IEEE80211R */ u16 reason = WLAN_REASON_UNSPECIFIED; + u16 status = WLAN_STATUS_SUCCESS; + const u8 *p2p_dev_addr = NULL; if (addr == NULL) { /* @@ -86,6 +87,7 @@ sta = ap_get_sta(hapd, addr); if (sta) { + ap_sta_no_session_timeout(hapd, sta); accounting_sta_stop(hapd, sta); /* @@ -95,8 +97,11 @@ sta->timeout_next = STA_NULLFUNC; } else { sta = ap_sta_add(hapd, addr); - if (sta == NULL) + if (sta == NULL) { + hostapd_drv_sta_disassoc(hapd, addr, + WLAN_REASON_DISASSOC_AP_BUSY); return -1; + } } sta->flags &= ~(WLAN_STA_WPS | WLAN_STA_MAYBE_WPS | WLAN_STA_WPS2); @@ -105,9 +110,27 @@ wpabuf_free(sta->p2p_ie); sta->p2p_ie = ieee802_11_vendor_ie_concat(req_ies, req_ies_len, P2P_IE_VENDOR_TYPE); + if (sta->p2p_ie) + p2p_dev_addr = p2p_get_go_dev_addr(sta->p2p_ie); } #endif /* CONFIG_P2P */ +#ifdef CONFIG_INTERWORKING + if (elems.ext_capab && elems.ext_capab_len > 4) { + if (elems.ext_capab[4] & 0x01) + sta->qos_map_enabled = 1; + } +#endif /* CONFIG_INTERWORKING */ + +#ifdef CONFIG_HS20 + wpabuf_free(sta->hs20_ie); + if (elems.hs20 && elems.hs20_len > 4) { + sta->hs20_ie = wpabuf_alloc_copy(elems.hs20 + 4, + elems.hs20_len - 4); + } else + sta->hs20_ie = NULL; +#endif /* CONFIG_HS20 */ + if (hapd->conf->wpa) { if (ie == NULL || ielen == 0) { #ifdef CONFIG_WPS @@ -144,34 +167,93 @@ if (sta->wpa_sm == NULL) sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth, - sta->addr); + sta->addr, + p2p_dev_addr); if (sta->wpa_sm == NULL) { wpa_printf(MSG_ERROR, "Failed to initialize WPA state " "machine"); return -1; } res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm, - ie, ielen, NULL, 0); + ie, ielen, + elems.mdie, elems.mdie_len); if (res != WPA_IE_OK) { wpa_printf(MSG_DEBUG, "WPA/RSN information element " "rejected? (res %u)", res); wpa_hexdump(MSG_DEBUG, "IE", ie, ielen); - if (res == WPA_INVALID_GROUP) + if (res == WPA_INVALID_GROUP) { reason = WLAN_REASON_GROUP_CIPHER_NOT_VALID; - else if (res == WPA_INVALID_PAIRWISE) + status = WLAN_STATUS_GROUP_CIPHER_NOT_VALID; + } else if (res == WPA_INVALID_PAIRWISE) { reason = WLAN_REASON_PAIRWISE_CIPHER_NOT_VALID; - else if (res == WPA_INVALID_AKMP) + status = WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID; + } else if (res == WPA_INVALID_AKMP) { reason = WLAN_REASON_AKMP_NOT_VALID; + status = WLAN_STATUS_AKMP_NOT_VALID; + } #ifdef CONFIG_IEEE80211W - else if (res == WPA_MGMT_FRAME_PROTECTION_VIOLATION) + else if (res == WPA_MGMT_FRAME_PROTECTION_VIOLATION) { reason = WLAN_REASON_INVALID_IE; - else if (res == WPA_INVALID_MGMT_GROUP_CIPHER) + 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; + } #endif /* CONFIG_IEEE80211W */ - else + else { reason = WLAN_REASON_INVALID_IE; + status = WLAN_STATUS_INVALID_IE; + } goto fail; } +#ifdef CONFIG_IEEE80211W + if ((sta->flags & 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 && + (sta->auth_alg != WLAN_AUTH_FT)) { + /* + * STA has already been associated with MFP and SA + * Query timeout has not been reached. Reject the + * association attempt temporarily and start SA Query, + * if one is not pending. + */ + + if (sta->sa_query_count == 0) + ap_sta_start_sa_query(hapd, sta); + +#ifdef CONFIG_IEEE80211R + status = WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY; + + p = hostapd_eid_assoc_comeback_time(hapd, sta, p); + + hostapd_sta_assoc(hapd, addr, reassoc, status, buf, + p - buf); +#endif /* CONFIG_IEEE80211R */ + return 0; + } + + if (wpa_auth_uses_mfp(sta->wpa_sm)) + sta->flags |= WLAN_STA_MFP; + else + sta->flags &= ~WLAN_STA_MFP; +#endif /* CONFIG_IEEE80211W */ + +#ifdef CONFIG_IEEE80211R + if (sta->auth_alg == WLAN_AUTH_FT) { + status = wpa_ft_validate_reassoc(sta->wpa_sm, req_ies, + req_ies_len); + if (status != WLAN_STATUS_SUCCESS) { + if (status == WLAN_STATUS_INVALID_PMKID) + reason = WLAN_REASON_INVALID_IE; + if (status == WLAN_STATUS_INVALID_MDIE) + reason = WLAN_REASON_INVALID_IE; + if (status == WLAN_STATUS_INVALID_FTIE) + reason = WLAN_REASON_INVALID_IE; + goto fail; + } + } +#endif /* CONFIG_IEEE80211R */ } else if (hapd->conf->wps_state) { #ifdef CONFIG_WPS struct wpabuf *wps; @@ -183,6 +265,7 @@ #ifdef CONFIG_WPS_STRICT if (wps && wps_validate_assoc_req(wps) < 0) { reason = WLAN_REASON_INVALID_IE; + status = WLAN_STATUS_INVALID_IE; wpabuf_free(wps); goto fail; } @@ -203,9 +286,25 @@ skip_wpa_check: #endif /* CONFIG_WPS */ +#ifdef CONFIG_IEEE80211R + p = wpa_sm_write_assoc_resp_ies(sta->wpa_sm, buf, sizeof(buf), + sta->auth_alg, req_ies, req_ies_len); + + hostapd_sta_assoc(hapd, addr, reassoc, status, buf, p - buf); +#else /* CONFIG_IEEE80211R */ + /* Keep compiler silent about unused variables */ + if (status) { + } +#endif /* CONFIG_IEEE80211R */ + new_assoc = (sta->flags & WLAN_STA_ASSOC) == 0; sta->flags |= WLAN_STA_AUTH | WLAN_STA_ASSOC; - wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC); + sta->flags &= ~WLAN_STA_WNM_SLEEP_MODE; + + if (reassoc && (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); hostapd_new_assoc_sta(hapd, sta, !new_assoc); @@ -221,6 +320,9 @@ return 0; fail: +#ifdef CONFIG_IEEE80211R + hostapd_sta_assoc(hapd, addr, reassoc, status, buf, p - buf); +#endif /* CONFIG_IEEE80211R */ hostapd_drv_sta_disassoc(hapd, sta->addr, reason); ap_free_sta(hapd, sta); return -1; @@ -279,8 +381,95 @@ } +void hostapd_event_ch_switch(struct hostapd_data *hapd, int freq, int ht, + int offset, int width, int cf1, int cf2) +{ +#ifdef NEED_AP_MLME + int channel, chwidth, seg0_idx = 0, seg1_idx = 0; + + hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_INFO, "driver had channel switch: " + "freq=%d, ht=%d, offset=%d, width=%d, cf1=%d, cf2=%d", + freq, ht, offset, width, cf1, cf2); + + hapd->iface->freq = freq; + + channel = hostapd_hw_get_channel(hapd, freq); + if (!channel) { + hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_WARNING, "driver switched to " + "bad channel!"); + return; + } + + switch (width) { + case CHAN_WIDTH_80: + chwidth = VHT_CHANWIDTH_80MHZ; + break; + case CHAN_WIDTH_80P80: + chwidth = VHT_CHANWIDTH_80P80MHZ; + break; + case CHAN_WIDTH_160: + chwidth = VHT_CHANWIDTH_160MHZ; + break; + case CHAN_WIDTH_20_NOHT: + case CHAN_WIDTH_20: + case CHAN_WIDTH_40: + default: + chwidth = VHT_CHANWIDTH_USE_HT; + break; + } + + switch (hapd->iface->current_mode->mode) { + case HOSTAPD_MODE_IEEE80211A: + if (cf1 > 5000) + seg0_idx = (cf1 - 5000) / 5; + if (cf2 > 5000) + seg1_idx = (cf2 - 5000) / 5; + break; + default: + seg0_idx = hostapd_hw_get_channel(hapd, cf1); + seg1_idx = hostapd_hw_get_channel(hapd, cf2); + break; + } + + hapd->iconf->channel = channel; + hapd->iconf->ieee80211n = ht; + 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; + + if (hapd->iface->csa_in_progress && + freq == hapd->iface->cs_freq_params.freq) { + hostapd_cleanup_cs_params(hapd); + + wpa_msg(hapd->msg_ctx, MSG_INFO, AP_CSA_FINISHED "freq=%d", + freq); + } +#endif /* NEED_AP_MLME */ +} + + +void hostapd_event_connect_failed_reason(struct hostapd_data *hapd, + const u8 *addr, int reason_code) +{ + switch (reason_code) { + case MAX_CLIENT_REACHED: + wpa_msg(hapd->msg_ctx, MSG_INFO, AP_REJECTED_MAX_STA MACSTR, + MAC2STR(addr)); + break; + case BLOCKED_CLIENT: + wpa_msg(hapd->msg_ctx, MSG_INFO, AP_REJECTED_BLOCKED_STA MACSTR, + MAC2STR(addr)); + break; + } +} + + int hostapd_probe_req_rx(struct hostapd_data *hapd, const u8 *sa, const u8 *da, - const u8 *bssid, const u8 *ie, size_t ie_len) + const u8 *bssid, const u8 *ie, size_t ie_len, + int ssi_signal) { size_t i; int ret = 0; @@ -291,7 +480,8 @@ random_add_randomness(sa, ETH_ALEN); for (i = 0; hapd->probereq_cb && i < hapd->num_probereq_cb; i++) { if (hapd->probereq_cb[i].cb(hapd->probereq_cb[i].ctx, - sa, da, bssid, ie, ie_len) > 0) { + sa, da, bssid, ie, ie_len, + ssi_signal) > 0) { ret = 1; break; } @@ -302,6 +492,119 @@ #ifdef HOSTAPD +#ifdef CONFIG_IEEE80211R +static void hostapd_notify_auth_ft_finish(void *ctx, const u8 *dst, + const u8 *bssid, + u16 auth_transaction, u16 status, + const u8 *ies, size_t ies_len) +{ + struct hostapd_data *hapd = ctx; + struct sta_info *sta; + + sta = ap_get_sta(hapd, dst); + if (sta == NULL) + return; + + hostapd_logger(hapd, dst, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, "authentication OK (FT)"); + sta->flags |= WLAN_STA_AUTH; + + hostapd_sta_auth(hapd, dst, auth_transaction, status, ies, ies_len); +} +#endif /* CONFIG_IEEE80211R */ + + +static void hostapd_notif_auth(struct hostapd_data *hapd, + struct auth_info *rx_auth) +{ + struct sta_info *sta; + u16 status = WLAN_STATUS_SUCCESS; + u8 resp_ies[2 + WLAN_AUTH_CHALLENGE_LEN]; + size_t resp_ies_len = 0; + + sta = ap_get_sta(hapd, rx_auth->peer); + if (!sta) { + sta = ap_sta_add(hapd, rx_auth->peer); + if (sta == NULL) { + status = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA; + goto fail; + } + } + sta->flags &= ~WLAN_STA_PREAUTH; + ieee802_1x_notify_pre_auth(sta->eapol_sm, 0); +#ifdef CONFIG_IEEE80211R + if (rx_auth->auth_type == WLAN_AUTH_FT && hapd->wpa_auth) { + sta->auth_alg = WLAN_AUTH_FT; + if (sta->wpa_sm == NULL) + sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth, + sta->addr, NULL); + if (sta->wpa_sm == NULL) { + wpa_printf(MSG_DEBUG, "FT: Failed to initialize WPA " + "state machine"); + status = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + wpa_ft_process_auth(sta->wpa_sm, rx_auth->bssid, + rx_auth->auth_transaction, rx_auth->ies, + rx_auth->ies_len, + hostapd_notify_auth_ft_finish, hapd); + return; + } +#endif /* CONFIG_IEEE80211R */ +fail: + hostapd_sta_auth(hapd, rx_auth->peer, rx_auth->auth_transaction + 1, + status, resp_ies, resp_ies_len); +} + + +static void hostapd_action_rx(struct hostapd_data *hapd, + struct rx_mgmt *drv_mgmt) +{ + struct ieee80211_mgmt *mgmt; + struct sta_info *sta; + size_t plen __maybe_unused; + u16 fc; + + if (drv_mgmt->frame_len < 24 + 1) + return; + + plen = drv_mgmt->frame_len - 24 - 1; + + mgmt = (struct ieee80211_mgmt *) drv_mgmt->frame; + fc = le_to_host16(mgmt->frame_control); + 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); + + sta = ap_get_sta(hapd, mgmt->sa); + if (sta == NULL) { + wpa_printf(MSG_DEBUG, "%s: station not found", __func__); + return; + } +#ifdef CONFIG_IEEE80211R + 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); + } +#endif /* CONFIG_IEEE80211R */ +#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); + } +#endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_WNM + if (mgmt->u.action.category == WLAN_ACTION_WNM) { + ieee802_11_rx_wnm_action_ap(hapd, mgmt, drv_mgmt->frame_len); + } +#endif /* CONFIG_WNM */ +} + + #ifdef NEED_AP_MLME #define HAPD_BROADCAST ((struct hostapd_data *) -1) @@ -338,17 +641,18 @@ } -static void hostapd_mgmt_rx(struct hostapd_data *hapd, struct rx_mgmt *rx_mgmt) +static int hostapd_mgmt_rx(struct hostapd_data *hapd, struct rx_mgmt *rx_mgmt) { struct hostapd_iface *iface = hapd->iface; const struct ieee80211_hdr *hdr; const u8 *bssid; struct hostapd_frame_info fi; + int ret; hdr = (const struct ieee80211_hdr *) rx_mgmt->frame; bssid = get_hdr_bssid(hdr, rx_mgmt->frame_len); if (bssid == NULL) - return; + return 0; hapd = get_hapd_bssid(iface, bssid); if (hapd == NULL) { @@ -363,7 +667,7 @@ WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_BEACON) hapd = iface->bss[0]; else - return; + return 0; } os_memset(&fi, 0, sizeof(fi)); @@ -372,53 +676,19 @@ if (hapd == HAPD_BROADCAST) { size_t i; - for (i = 0; i < iface->num_bss; i++) - ieee802_11_mgmt(iface->bss[i], rx_mgmt->frame, - rx_mgmt->frame_len, &fi); + ret = 0; + for (i = 0; i < iface->num_bss; i++) { + if (ieee802_11_mgmt(iface->bss[i], rx_mgmt->frame, + rx_mgmt->frame_len, &fi) > 0) + ret = 1; + } } else - ieee802_11_mgmt(hapd, rx_mgmt->frame, rx_mgmt->frame_len, &fi); + ret = ieee802_11_mgmt(hapd, rx_mgmt->frame, rx_mgmt->frame_len, + &fi); random_add_randomness(&fi, sizeof(fi)); -} - -static void hostapd_rx_action(struct hostapd_data *hapd, - struct rx_action *rx_action) -{ - struct rx_mgmt rx_mgmt; - u8 *buf; - struct ieee80211_hdr *hdr; - - wpa_printf(MSG_DEBUG, "EVENT_RX_ACTION DA=" MACSTR " SA=" MACSTR - " BSSID=" MACSTR " category=%u", - MAC2STR(rx_action->da), MAC2STR(rx_action->sa), - MAC2STR(rx_action->bssid), rx_action->category); - wpa_hexdump(MSG_MSGDUMP, "Received action frame contents", - rx_action->data, rx_action->len); - - buf = os_zalloc(24 + 1 + rx_action->len); - if (buf == NULL) - return; - hdr = (struct ieee80211_hdr *) buf; - hdr->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, - WLAN_FC_STYPE_ACTION); - if (rx_action->category == WLAN_ACTION_SA_QUERY) { - /* - * Assume frame was protected; it would have been dropped if - * not. - */ - hdr->frame_control |= host_to_le16(WLAN_FC_ISWEP); - } - os_memcpy(hdr->addr1, rx_action->da, ETH_ALEN); - os_memcpy(hdr->addr2, rx_action->sa, ETH_ALEN); - os_memcpy(hdr->addr3, rx_action->bssid, ETH_ALEN); - buf[24] = rx_action->category; - os_memcpy(buf + 24 + 1, rx_action->data, rx_action->len); - os_memset(&rx_mgmt, 0, sizeof(rx_mgmt)); - rx_mgmt.frame = buf; - rx_mgmt.frame_len = 24 + 1 + rx_action->len; - hostapd_mgmt_rx(hapd, &rx_mgmt); - os_free(buf); + return ret; } @@ -461,12 +731,15 @@ const u8 *data, size_t data_len) { struct hostapd_iface *iface = hapd->iface; + struct sta_info *sta; size_t j; for (j = 0; j < iface->num_bss; j++) { - if (ap_get_sta(iface->bss[j], src)) { - hapd = iface->bss[j]; - break; + if ((sta = ap_get_sta(iface->bss[j], src))) { + if (sta->flags & WLAN_STA_ASSOC) { + hapd = iface->bss[j]; + break; + } } } @@ -474,10 +747,140 @@ } +static struct hostapd_channel_data * hostapd_get_mode_channel( + struct hostapd_iface *iface, unsigned int freq) +{ + int i; + struct hostapd_channel_data *chan; + + 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; + } + + return NULL; +} + + +static void hostapd_update_nf(struct hostapd_iface *iface, + struct hostapd_channel_data *chan, + struct freq_survey *survey) +{ + if (!iface->chans_surveyed) { + chan->min_nf = survey->nf; + iface->lowest_nf = survey->nf; + } else { + if (dl_list_empty(&chan->survey_list)) + chan->min_nf = survey->nf; + else if (survey->nf < chan->min_nf) + chan->min_nf = survey->nf; + if (survey->nf < iface->lowest_nf) + iface->lowest_nf = survey->nf; + } +} + + +static void hostapd_event_get_survey(struct hostapd_data *hapd, + struct survey_results *survey_results) +{ + struct hostapd_iface *iface = hapd->iface; + struct freq_survey *survey, *tmp; + struct hostapd_channel_data *chan; + + if (dl_list_empty(&survey_results->survey_list)) { + wpa_printf(MSG_DEBUG, "No survey data received"); + return; + } + + dl_list_for_each_safe(survey, tmp, &survey_results->survey_list, + struct freq_survey, list) { + chan = hostapd_get_mode_channel(iface, survey->freq); + if (!chan) + continue; + if (chan->flag & HOSTAPD_CHAN_DISABLED) + continue; + + dl_list_del(&survey->list); + dl_list_add_tail(&chan->survey_list, &survey->list); + + hostapd_update_nf(iface, chan, survey); + + iface->chans_surveyed++; + } +} + + +#ifdef NEED_AP_MLME + +static void hostapd_event_dfs_radar_detected(struct hostapd_data *hapd, + struct dfs_event *radar) +{ + wpa_printf(MSG_DEBUG, "DFS radar detected on %d MHz", radar->freq); + hostapd_dfs_radar_detected(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) +{ + wpa_printf(MSG_DEBUG, "DFS CAC finished on %d MHz", radar->freq); + hostapd_dfs_complete_cac(hapd->iface, 1, radar->freq, radar->ht_enabled, + radar->chan_offset, radar->chan_width, + radar->cf1, radar->cf2); +} + + +static void hostapd_event_dfs_cac_aborted(struct hostapd_data *hapd, + struct dfs_event *radar) +{ + wpa_printf(MSG_DEBUG, "DFS CAC aborted on %d MHz", radar->freq); + hostapd_dfs_complete_cac(hapd->iface, 0, radar->freq, radar->ht_enabled, + radar->chan_offset, radar->chan_width, + radar->cf1, radar->cf2); +} + + +static void hostapd_event_dfs_nop_finished(struct hostapd_data *hapd, + struct dfs_event *radar) +{ + wpa_printf(MSG_DEBUG, "DFS NOP finished on %d MHz", radar->freq); + hostapd_dfs_nop_finished(hapd->iface, radar->freq, radar->ht_enabled, + radar->chan_offset, radar->chan_width, + radar->cf1, radar->cf2); +} + +#endif /* NEED_AP_MLME */ + + void wpa_supplicant_event(void *ctx, enum wpa_event_type event, union wpa_event_data *data) { struct hostapd_data *hapd = ctx; +#ifndef CONFIG_NO_STDOUT_DEBUG + int level = MSG_DEBUG; + + if (event == EVENT_RX_MGMT && data->rx_mgmt.frame && + data->rx_mgmt.frame_len >= 24) { + const struct ieee80211_hdr *hdr; + u16 fc; + hdr = (const struct ieee80211_hdr *) data->rx_mgmt.frame; + fc = le_to_host16(hdr->frame_control); + if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT && + WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_BEACON) + level = MSG_EXCESSIVE; + if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT && + WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_PROBE_REQ) + level = MSG_EXCESSIVE; + } + + wpa_dbg(hapd->msg_ctx, level, "Event %s (%d) received", + event_to_string(event), event); +#endif /* CONFIG_NO_STDOUT_DEBUG */ switch (event) { case EVENT_MICHAEL_MIC_FAILURE: @@ -513,6 +916,12 @@ break; } break; + case EVENT_EAPOL_TX_STATUS: + hostapd_eapol_tx_status(hapd, data->eapol_tx_status.dst, + data->eapol_tx_status.data, + data->eapol_tx_status.data_len, + data->eapol_tx_status.ack); + break; case EVENT_DRIVER_CLIENT_POLL_OK: hostapd_client_poll_ok(hapd, data->client_poll.addr); break; @@ -521,10 +930,14 @@ data->rx_from_unknown.addr, data->rx_from_unknown.wds); break; +#endif /* NEED_AP_MLME */ case EVENT_RX_MGMT: - hostapd_mgmt_rx(hapd, &data->rx_mgmt); - break; +#ifdef NEED_AP_MLME + if (hostapd_mgmt_rx(hapd, &data->rx_mgmt) > 0) + break; #endif /* NEED_AP_MLME */ + hostapd_action_rx(hapd, &data->rx_mgmt); + break; case EVENT_RX_PROBE_REQ: if (data->rx_probe_req.sa == NULL || data->rx_probe_req.ie == NULL) @@ -533,7 +946,8 @@ data->rx_probe_req.da, data->rx_probe_req.bssid, data->rx_probe_req.ie, - data->rx_probe_req.ie_len); + data->rx_probe_req.ie_len, + data->rx_probe_req.ssi_signal); break; case EVENT_NEW_STA: hostapd_event_new_sta(hapd, data->new_sta.addr); @@ -562,12 +976,57 @@ break; hostapd_event_sta_low_ack(hapd, data->low_ack.addr); break; + case EVENT_AUTH: + hostapd_notif_auth(hapd, &data->auth); + break; + case EVENT_CH_SWITCH: + if (!data) + break; + hostapd_event_ch_switch(hapd, data->ch_switch.freq, + data->ch_switch.ht_enabled, + data->ch_switch.ch_offset, + data->ch_switch.ch_width, + data->ch_switch.cf1, + data->ch_switch.cf2); + break; + case EVENT_CONNECT_FAILED_REASON: + if (!data) + break; + hostapd_event_connect_failed_reason( + hapd, data->connect_failed_reason.addr, + data->connect_failed_reason.code); + break; + case EVENT_SURVEY: + hostapd_event_get_survey(hapd, &data->survey_results); + break; #ifdef NEED_AP_MLME - case EVENT_RX_ACTION: - if (data->rx_action.da == NULL || data->rx_action.sa == NULL || - data->rx_action.bssid == NULL) + case EVENT_DFS_RADAR_DETECTED: + if (!data) break; - hostapd_rx_action(hapd, &data->rx_action); + hostapd_event_dfs_radar_detected(hapd, &data->dfs_event); + break; + case EVENT_DFS_CAC_FINISHED: + if (!data) + break; + hostapd_event_dfs_cac_finished(hapd, &data->dfs_event); + break; + case EVENT_DFS_CAC_ABORTED: + if (!data) + break; + hostapd_event_dfs_cac_aborted(hapd, &data->dfs_event); + break; + case EVENT_DFS_NOP_FINISHED: + if (!data) + break; + hostapd_event_dfs_nop_finished(hapd, &data->dfs_event); + break; + case EVENT_CHANNEL_LIST_CHANGED: + /* channel list changed (regulatory?), update channel list */ + /* TODO: check this. hostapd_get_hw_features() initializes + * too much stuff. */ + /* hostapd_get_hw_features(hapd->iface); */ + hostapd_channel_list_updated( + hapd->iface, data->channel_list_changed.initiator); break; #endif /* NEED_AP_MLME */ default: diff -Nru wpa-1.0/src/ap/eap_user_db.c wpa-2.1/src/ap/eap_user_db.c --- wpa-1.0/src/ap/eap_user_db.c 1970-01-01 00:00:00.000000000 +0000 +++ wpa-2.1/src/ap/eap_user_db.c 2014-02-04 11:23:35.000000000 +0000 @@ -0,0 +1,270 @@ +/* + * hostapd / EAP user database + * Copyright (c) 2012, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" +#ifdef CONFIG_SQLITE +#include +#endif /* CONFIG_SQLITE */ + +#include "common.h" +#include "eap_common/eap_wsc_common.h" +#include "eap_server/eap_methods.h" +#include "eap_server/eap.h" +#include "ap_config.h" +#include "hostapd.h" + +#ifdef CONFIG_SQLITE + +static void set_user_methods(struct hostapd_eap_user *user, const char *methods) +{ + char *buf, *start; + int num_methods; + + buf = os_strdup(methods); + if (buf == NULL) + return; + + os_memset(&user->methods, 0, sizeof(user->methods)); + num_methods = 0; + start = buf; + while (*start) { + char *pos3 = os_strchr(start, ','); + if (pos3) + *pos3++ = '\0'; + user->methods[num_methods].method = + eap_server_get_type(start, + &user->methods[num_methods].vendor); + if (user->methods[num_methods].vendor == EAP_VENDOR_IETF && + user->methods[num_methods].method == EAP_TYPE_NONE) { + if (os_strcmp(start, "TTLS-PAP") == 0) { + user->ttls_auth |= EAP_TTLS_AUTH_PAP; + goto skip_eap; + } + if (os_strcmp(start, "TTLS-CHAP") == 0) { + user->ttls_auth |= EAP_TTLS_AUTH_CHAP; + goto skip_eap; + } + if (os_strcmp(start, "TTLS-MSCHAP") == 0) { + user->ttls_auth |= EAP_TTLS_AUTH_MSCHAP; + goto skip_eap; + } + if (os_strcmp(start, "TTLS-MSCHAPV2") == 0) { + user->ttls_auth |= EAP_TTLS_AUTH_MSCHAPV2; + goto skip_eap; + } + wpa_printf(MSG_INFO, "DB: Unsupported EAP type '%s'", + start); + os_free(buf); + return; + } + + num_methods++; + if (num_methods >= EAP_MAX_METHODS) + break; + skip_eap: + if (pos3 == NULL) + break; + start = pos3; + } + + os_free(buf); +} + + +static int get_user_cb(void *ctx, int argc, char *argv[], char *col[]) +{ + struct hostapd_eap_user *user = ctx; + int i; + + for (i = 0; i < argc; i++) { + if (os_strcmp(col[i], "password") == 0 && argv[i]) { + os_free(user->password); + user->password_len = os_strlen(argv[i]); + user->password = (u8 *) os_strdup(argv[i]); + user->next = (void *) 1; + } else if (os_strcmp(col[i], "methods") == 0 && argv[i]) { + set_user_methods(user, argv[i]); + } + } + + return 0; +} + + +static int get_wildcard_cb(void *ctx, int argc, char *argv[], char *col[]) +{ + struct hostapd_eap_user *user = ctx; + int i, id = -1, methods = -1; + size_t len; + + for (i = 0; i < argc; i++) { + if (os_strcmp(col[i], "identity") == 0 && argv[i]) + id = i; + else if (os_strcmp(col[i], "methods") == 0 && argv[i]) + methods = i; + } + + if (id < 0 || methods < 0) + return 0; + + len = os_strlen(argv[id]); + if (len <= user->identity_len && + os_memcmp(argv[id], user->identity, len) == 0 && + (user->password == NULL || len > user->password_len)) { + os_free(user->password); + user->password_len = os_strlen(argv[id]); + user->password = (u8 *) os_strdup(argv[id]); + user->next = (void *) 1; + set_user_methods(user, argv[methods]); + } + + return 0; +} + + +static const struct hostapd_eap_user * +eap_user_sqlite_get(struct hostapd_data *hapd, const u8 *identity, + size_t identity_len, int phase2) +{ + sqlite3 *db; + struct hostapd_eap_user *user = NULL; + char id_str[256], cmd[300]; + size_t i; + + if (identity_len >= sizeof(id_str)) + return NULL; + os_memcpy(id_str, identity, identity_len); + id_str[identity_len] = '\0'; + for (i = 0; i < identity_len; i++) { + if (id_str[i] >= 'a' && id_str[i] <= 'z') + continue; + if (id_str[i] >= 'A' && id_str[i] <= 'Z') + continue; + if (id_str[i] >= '0' && id_str[i] <= '9') + continue; + if (id_str[i] == '-' || id_str[i] == '_' || id_str[i] == '.' || + id_str[i] == ',' || id_str[i] == '@' || id_str[i] == '\\' || + id_str[i] == '!' || id_str[i] == '#' || id_str[i] == '%' || + id_str[i] == '=' || id_str[i] == ' ') + continue; + wpa_printf(MSG_INFO, "DB: Unsupported character in identity"); + return NULL; + } + + os_free(hapd->tmp_eap_user.identity); + os_free(hapd->tmp_eap_user.password); + os_memset(&hapd->tmp_eap_user, 0, sizeof(hapd->tmp_eap_user)); + hapd->tmp_eap_user.phase2 = phase2; + hapd->tmp_eap_user.identity = os_zalloc(identity_len + 1); + if (hapd->tmp_eap_user.identity == NULL) + return NULL; + os_memcpy(hapd->tmp_eap_user.identity, identity, identity_len); + + if (sqlite3_open(hapd->conf->eap_user_sqlite, &db)) { + wpa_printf(MSG_INFO, "DB: Failed to open database %s: %s", + hapd->conf->eap_user_sqlite, sqlite3_errmsg(db)); + sqlite3_close(db); + return NULL; + } + + os_snprintf(cmd, sizeof(cmd), + "SELECT password,methods FROM users WHERE " + "identity='%s' AND phase2=%d;", id_str, phase2); + wpa_printf(MSG_DEBUG, "DB: %s", cmd); + if (sqlite3_exec(db, cmd, get_user_cb, &hapd->tmp_eap_user, NULL) != + SQLITE_OK) { + wpa_printf(MSG_DEBUG, "DB: Failed to complete SQL operation"); + } else if (hapd->tmp_eap_user.next) + user = &hapd->tmp_eap_user; + + if (user == NULL && !phase2) { + os_snprintf(cmd, sizeof(cmd), + "SELECT identity,methods FROM wildcards;"); + wpa_printf(MSG_DEBUG, "DB: %s", cmd); + if (sqlite3_exec(db, cmd, get_wildcard_cb, &hapd->tmp_eap_user, + NULL) != SQLITE_OK) { + wpa_printf(MSG_DEBUG, "DB: Failed to complete SQL " + "operation"); + } else if (hapd->tmp_eap_user.next) { + user = &hapd->tmp_eap_user; + os_free(user->identity); + user->identity = user->password; + user->identity_len = user->password_len; + user->password = NULL; + user->password_len = 0; + } + } + + sqlite3_close(db); + + return user; +} + +#endif /* CONFIG_SQLITE */ + + +const struct hostapd_eap_user * +hostapd_get_eap_user(struct hostapd_data *hapd, const u8 *identity, + size_t identity_len, int phase2) +{ + const struct hostapd_bss_config *conf = hapd->conf; + struct hostapd_eap_user *user = conf->eap_user; + +#ifdef CONFIG_WPS + if (conf->wps_state && identity_len == WSC_ID_ENROLLEE_LEN && + os_memcmp(identity, WSC_ID_ENROLLEE, WSC_ID_ENROLLEE_LEN) == 0) { + static struct hostapd_eap_user wsc_enrollee; + os_memset(&wsc_enrollee, 0, sizeof(wsc_enrollee)); + wsc_enrollee.methods[0].method = eap_server_get_type( + "WSC", &wsc_enrollee.methods[0].vendor); + return &wsc_enrollee; + } + + if (conf->wps_state && identity_len == WSC_ID_REGISTRAR_LEN && + os_memcmp(identity, WSC_ID_REGISTRAR, WSC_ID_REGISTRAR_LEN) == 0) { + static struct hostapd_eap_user wsc_registrar; + os_memset(&wsc_registrar, 0, sizeof(wsc_registrar)); + wsc_registrar.methods[0].method = eap_server_get_type( + "WSC", &wsc_registrar.methods[0].vendor); + wsc_registrar.password = (u8 *) conf->ap_pin; + wsc_registrar.password_len = conf->ap_pin ? + os_strlen(conf->ap_pin) : 0; + return &wsc_registrar; + } +#endif /* CONFIG_WPS */ + + while (user) { + if (!phase2 && user->identity == NULL) { + /* Wildcard match */ + break; + } + + if (user->phase2 == !!phase2 && user->wildcard_prefix && + identity_len >= user->identity_len && + os_memcmp(user->identity, identity, user->identity_len) == + 0) { + /* Wildcard prefix match */ + break; + } + + if (user->phase2 == !!phase2 && + user->identity_len == identity_len && + os_memcmp(user->identity, identity, identity_len) == 0) + break; + user = user->next; + } + +#ifdef CONFIG_SQLITE + if (user == NULL && conf->eap_user_sqlite) { + return eap_user_sqlite_get(hapd, identity, identity_len, + phase2); + } +#endif /* CONFIG_SQLITE */ + + return user; +} diff -Nru wpa-1.0/src/ap/gas_serv.c wpa-2.1/src/ap/gas_serv.c --- wpa-1.0/src/ap/gas_serv.c 1970-01-01 00:00:00.000000000 +0000 +++ wpa-2.1/src/ap/gas_serv.c 2014-02-04 11:23:35.000000000 +0000 @@ -0,0 +1,1199 @@ +/* + * Generic advertisement service (GAS) server + * Copyright (c) 2011-2014, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "common/ieee802_11_defs.h" +#include "common/gas.h" +#include "utils/eloop.h" +#include "hostapd.h" +#include "ap_config.h" +#include "ap_drv_ops.h" +#include "sta_info.h" +#include "gas_serv.h" + + +static void convert_to_protected_dual(struct wpabuf *msg) +{ + u8 *categ = wpabuf_mhead_u8(msg); + *categ = WLAN_ACTION_PROTECTED_DUAL; +} + + +static struct gas_dialog_info * +gas_dialog_create(struct hostapd_data *hapd, const u8 *addr, u8 dialog_token) +{ + struct sta_info *sta; + struct gas_dialog_info *dia = NULL; + int i, j; + + sta = ap_get_sta(hapd, addr); + if (!sta) { + /* + * We need a STA entry to be able to maintain state for + * the GAS query. + */ + wpa_printf(MSG_DEBUG, "ANQP: Add a temporary STA entry for " + "GAS query"); + sta = ap_sta_add(hapd, addr); + if (!sta) { + wpa_printf(MSG_DEBUG, "Failed to add STA " MACSTR + " for GAS query", MAC2STR(addr)); + return NULL; + } + sta->flags |= WLAN_STA_GAS; + /* + * The default inactivity is 300 seconds. We don't need + * it to be that long. + */ + ap_sta_session_timeout(hapd, sta, 5); + } else { + ap_sta_replenish_timeout(hapd, sta, 5); + } + + if (sta->gas_dialog == NULL) { + sta->gas_dialog = os_zalloc(GAS_DIALOG_MAX * + sizeof(struct gas_dialog_info)); + if (sta->gas_dialog == NULL) + return NULL; + } + + for (i = sta->gas_dialog_next, j = 0; j < GAS_DIALOG_MAX; i++, j++) { + if (i == GAS_DIALOG_MAX) + i = 0; + if (sta->gas_dialog[i].valid) + continue; + dia = &sta->gas_dialog[i]; + dia->valid = 1; + dia->index = i; + dia->dialog_token = dialog_token; + sta->gas_dialog_next = (++i == GAS_DIALOG_MAX) ? 0 : i; + return dia; + } + + wpa_msg(hapd->msg_ctx, MSG_ERROR, "ANQP: Could not create dialog for " + MACSTR " dialog_token %u. Consider increasing " + "GAS_DIALOG_MAX.", MAC2STR(addr), dialog_token); + + return NULL; +} + + +struct gas_dialog_info * +gas_serv_dialog_find(struct hostapd_data *hapd, const u8 *addr, + u8 dialog_token) +{ + struct sta_info *sta; + int i; + + sta = ap_get_sta(hapd, addr); + if (!sta) { + wpa_printf(MSG_DEBUG, "ANQP: could not find STA " MACSTR, + MAC2STR(addr)); + return NULL; + } + for (i = 0; sta->gas_dialog && i < GAS_DIALOG_MAX; i++) { + if (sta->gas_dialog[i].dialog_token != dialog_token || + !sta->gas_dialog[i].valid) + continue; + return &sta->gas_dialog[i]; + } + wpa_printf(MSG_DEBUG, "ANQP: Could not find dialog for " + MACSTR " dialog_token %u", MAC2STR(addr), dialog_token); + return NULL; +} + + +void gas_serv_dialog_clear(struct gas_dialog_info *dia) +{ + wpabuf_free(dia->sd_resp); + os_memset(dia, 0, sizeof(*dia)); +} + + +static void gas_serv_free_dialogs(struct hostapd_data *hapd, + const u8 *sta_addr) +{ + struct sta_info *sta; + int i; + + sta = ap_get_sta(hapd, sta_addr); + if (sta == NULL || sta->gas_dialog == NULL) + return; + + for (i = 0; i < GAS_DIALOG_MAX; i++) { + if (sta->gas_dialog[i].valid) + return; + } + + os_free(sta->gas_dialog); + sta->gas_dialog = NULL; +} + + +#ifdef CONFIG_HS20 +static void anqp_add_hs_capab_list(struct hostapd_data *hapd, + struct wpabuf *buf) +{ + u8 *len; + + 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_CAPABILITY_LIST); + wpabuf_put_u8(buf, 0); /* Reserved */ + wpabuf_put_u8(buf, HS20_STYPE_CAPABILITY_LIST); + if (hapd->conf->hs20_oper_friendly_name) + wpabuf_put_u8(buf, HS20_STYPE_OPERATOR_FRIENDLY_NAME); + if (hapd->conf->hs20_wan_metrics) + wpabuf_put_u8(buf, HS20_STYPE_WAN_METRICS); + if (hapd->conf->hs20_connection_capability) + wpabuf_put_u8(buf, HS20_STYPE_CONNECTION_CAPABILITY); + if (hapd->conf->nai_realm_data) + wpabuf_put_u8(buf, HS20_STYPE_NAI_HOME_REALM_QUERY); + if (hapd->conf->hs20_operating_class) + wpabuf_put_u8(buf, HS20_STYPE_OPERATING_CLASS); + gas_anqp_set_element_len(buf, len); +} +#endif /* CONFIG_HS20 */ + + +static void anqp_add_capab_list(struct hostapd_data *hapd, + struct wpabuf *buf) +{ + u8 *len; + + len = gas_anqp_add_element(buf, ANQP_CAPABILITY_LIST); + wpabuf_put_le16(buf, ANQP_CAPABILITY_LIST); + if (hapd->conf->venue_name) + wpabuf_put_le16(buf, ANQP_VENUE_NAME); + if (hapd->conf->network_auth_type) + wpabuf_put_le16(buf, ANQP_NETWORK_AUTH_TYPE); + if (hapd->conf->roaming_consortium) + wpabuf_put_le16(buf, ANQP_ROAMING_CONSORTIUM); + if (hapd->conf->ipaddr_type_configured) + wpabuf_put_le16(buf, ANQP_IP_ADDR_TYPE_AVAILABILITY); + if (hapd->conf->nai_realm_data) + wpabuf_put_le16(buf, ANQP_NAI_REALM); + if (hapd->conf->anqp_3gpp_cell_net) + wpabuf_put_le16(buf, ANQP_3GPP_CELLULAR_NETWORK); + if (hapd->conf->domain_name) + wpabuf_put_le16(buf, ANQP_DOMAIN_NAME); +#ifdef CONFIG_HS20 + anqp_add_hs_capab_list(hapd, buf); +#endif /* CONFIG_HS20 */ + gas_anqp_set_element_len(buf, len); +} + + +static void anqp_add_venue_name(struct hostapd_data *hapd, struct wpabuf *buf) +{ + if (hapd->conf->venue_name) { + u8 *len; + unsigned int i; + len = gas_anqp_add_element(buf, ANQP_VENUE_NAME); + wpabuf_put_u8(buf, hapd->conf->venue_group); + wpabuf_put_u8(buf, hapd->conf->venue_type); + for (i = 0; i < hapd->conf->venue_name_count; i++) { + struct hostapd_lang_string *vn; + vn = &hapd->conf->venue_name[i]; + wpabuf_put_u8(buf, 3 + vn->name_len); + wpabuf_put_data(buf, vn->lang, 3); + wpabuf_put_data(buf, vn->name, vn->name_len); + } + gas_anqp_set_element_len(buf, len); + } +} + + +static void anqp_add_network_auth_type(struct hostapd_data *hapd, + struct wpabuf *buf) +{ + if (hapd->conf->network_auth_type) { + wpabuf_put_le16(buf, ANQP_NETWORK_AUTH_TYPE); + wpabuf_put_le16(buf, hapd->conf->network_auth_type_len); + wpabuf_put_data(buf, hapd->conf->network_auth_type, + hapd->conf->network_auth_type_len); + } +} + + +static void anqp_add_roaming_consortium(struct hostapd_data *hapd, + struct wpabuf *buf) +{ + unsigned int i; + u8 *len; + + len = gas_anqp_add_element(buf, ANQP_ROAMING_CONSORTIUM); + for (i = 0; i < hapd->conf->roaming_consortium_count; i++) { + struct hostapd_roaming_consortium *rc; + rc = &hapd->conf->roaming_consortium[i]; + wpabuf_put_u8(buf, rc->len); + wpabuf_put_data(buf, rc->oi, rc->len); + } + gas_anqp_set_element_len(buf, len); +} + + +static void anqp_add_ip_addr_type_availability(struct hostapd_data *hapd, + struct wpabuf *buf) +{ + if (hapd->conf->ipaddr_type_configured) { + wpabuf_put_le16(buf, ANQP_IP_ADDR_TYPE_AVAILABILITY); + wpabuf_put_le16(buf, 1); + wpabuf_put_u8(buf, hapd->conf->ipaddr_type_availability); + } +} + + +static void anqp_add_nai_realm_eap(struct wpabuf *buf, + struct hostapd_nai_realm_data *realm) +{ + unsigned int i, j; + + wpabuf_put_u8(buf, realm->eap_method_count); + + for (i = 0; i < realm->eap_method_count; i++) { + struct hostapd_nai_realm_eap *eap = &realm->eap_method[i]; + wpabuf_put_u8(buf, 2 + (3 * eap->num_auths)); + wpabuf_put_u8(buf, eap->eap_method); + wpabuf_put_u8(buf, eap->num_auths); + for (j = 0; j < eap->num_auths; j++) { + wpabuf_put_u8(buf, eap->auth_id[j]); + wpabuf_put_u8(buf, 1); + wpabuf_put_u8(buf, eap->auth_val[j]); + } + } +} + + +static void anqp_add_nai_realm_data(struct wpabuf *buf, + struct hostapd_nai_realm_data *realm, + unsigned int realm_idx) +{ + u8 *realm_data_len; + + wpa_printf(MSG_DEBUG, "realm=%s, len=%d", realm->realm[realm_idx], + (int) os_strlen(realm->realm[realm_idx])); + realm_data_len = wpabuf_put(buf, 2); + wpabuf_put_u8(buf, realm->encoding); + wpabuf_put_u8(buf, os_strlen(realm->realm[realm_idx])); + wpabuf_put_str(buf, realm->realm[realm_idx]); + anqp_add_nai_realm_eap(buf, realm); + gas_anqp_set_element_len(buf, realm_data_len); +} + + +static int hs20_add_nai_home_realm_matches(struct hostapd_data *hapd, + struct wpabuf *buf, + const u8 *home_realm, + size_t home_realm_len) +{ + unsigned int i, j, k; + u8 num_realms, num_matching = 0, encoding, realm_len, *realm_list_len; + struct hostapd_nai_realm_data *realm; + const u8 *pos, *realm_name, *end; + struct { + unsigned int realm_data_idx; + unsigned int realm_idx; + } matches[10]; + + pos = home_realm; + end = pos + home_realm_len; + if (pos + 1 > end) { + wpa_hexdump(MSG_DEBUG, "Too short NAI Home Realm Query", + home_realm, home_realm_len); + return -1; + } + num_realms = *pos++; + + for (i = 0; i < num_realms && num_matching < 10; i++) { + if (pos + 2 > end) { + wpa_hexdump(MSG_DEBUG, + "Truncated NAI Home Realm Query", + home_realm, home_realm_len); + return -1; + } + encoding = *pos++; + realm_len = *pos++; + if (pos + realm_len > end) { + wpa_hexdump(MSG_DEBUG, + "Truncated NAI Home Realm Query", + home_realm, home_realm_len); + return -1; + } + realm_name = pos; + for (j = 0; j < hapd->conf->nai_realm_count && + num_matching < 10; j++) { + const u8 *rpos, *rend; + realm = &hapd->conf->nai_realm_data[j]; + if (encoding != realm->encoding) + continue; + + rpos = realm_name; + while (rpos < realm_name + realm_len && + num_matching < 10) { + for (rend = rpos; + rend < realm_name + realm_len; rend++) { + if (*rend == ';') + break; + } + for (k = 0; k < MAX_NAI_REALMS && + realm->realm[k] && + num_matching < 10; k++) { + if ((int) os_strlen(realm->realm[k]) != + rend - rpos || + os_strncmp((char *) rpos, + realm->realm[k], + rend - rpos) != 0) + continue; + matches[num_matching].realm_data_idx = + j; + matches[num_matching].realm_idx = k; + num_matching++; + } + rpos = rend + 1; + } + } + pos += realm_len; + } + + realm_list_len = gas_anqp_add_element(buf, ANQP_NAI_REALM); + wpabuf_put_le16(buf, num_matching); + + /* + * There are two ways to format. 1. each realm in a NAI Realm Data unit + * 2. all realms that share the same EAP methods in a NAI Realm Data + * unit. The first format is likely to be bigger in size than the + * second, but may be easier to parse and process by the receiver. + */ + for (i = 0; i < num_matching; i++) { + wpa_printf(MSG_DEBUG, "realm_idx %d, realm_data_idx %d", + matches[i].realm_data_idx, matches[i].realm_idx); + realm = &hapd->conf->nai_realm_data[matches[i].realm_data_idx]; + anqp_add_nai_realm_data(buf, realm, matches[i].realm_idx); + } + gas_anqp_set_element_len(buf, realm_list_len); + return 0; +} + + +static void anqp_add_nai_realm(struct hostapd_data *hapd, struct wpabuf *buf, + const u8 *home_realm, size_t home_realm_len, + int nai_realm, int nai_home_realm) +{ + if (nai_realm && hapd->conf->nai_realm_data) { + u8 *len; + unsigned int i, j; + len = gas_anqp_add_element(buf, ANQP_NAI_REALM); + wpabuf_put_le16(buf, hapd->conf->nai_realm_count); + for (i = 0; i < hapd->conf->nai_realm_count; i++) { + u8 *realm_data_len, *realm_len; + struct hostapd_nai_realm_data *realm; + + realm = &hapd->conf->nai_realm_data[i]; + realm_data_len = wpabuf_put(buf, 2); + wpabuf_put_u8(buf, realm->encoding); + realm_len = wpabuf_put(buf, 1); + for (j = 0; realm->realm[j]; j++) { + if (j > 0) + wpabuf_put_u8(buf, ';'); + wpabuf_put_str(buf, realm->realm[j]); + } + *realm_len = (u8 *) wpabuf_put(buf, 0) - realm_len - 1; + anqp_add_nai_realm_eap(buf, realm); + gas_anqp_set_element_len(buf, realm_data_len); + } + gas_anqp_set_element_len(buf, len); + } else if (nai_home_realm && hapd->conf->nai_realm_data) { + hs20_add_nai_home_realm_matches(hapd, buf, home_realm, + home_realm_len); + } +} + + +static void anqp_add_3gpp_cellular_network(struct hostapd_data *hapd, + struct wpabuf *buf) +{ + if (hapd->conf->anqp_3gpp_cell_net) { + wpabuf_put_le16(buf, ANQP_3GPP_CELLULAR_NETWORK); + wpabuf_put_le16(buf, + hapd->conf->anqp_3gpp_cell_net_len); + wpabuf_put_data(buf, hapd->conf->anqp_3gpp_cell_net, + hapd->conf->anqp_3gpp_cell_net_len); + } +} + + +static void anqp_add_domain_name(struct hostapd_data *hapd, struct wpabuf *buf) +{ + if (hapd->conf->domain_name) { + wpabuf_put_le16(buf, ANQP_DOMAIN_NAME); + wpabuf_put_le16(buf, hapd->conf->domain_name_len); + wpabuf_put_data(buf, hapd->conf->domain_name, + hapd->conf->domain_name_len); + } +} + + +#ifdef CONFIG_HS20 + +static void anqp_add_operator_friendly_name(struct hostapd_data *hapd, + struct wpabuf *buf) +{ + if (hapd->conf->hs20_oper_friendly_name) { + u8 *len; + unsigned int i; + 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_FRIENDLY_NAME); + wpabuf_put_u8(buf, 0); /* Reserved */ + for (i = 0; i < hapd->conf->hs20_oper_friendly_name_count; i++) + { + struct hostapd_lang_string *vn; + vn = &hapd->conf->hs20_oper_friendly_name[i]; + wpabuf_put_u8(buf, 3 + vn->name_len); + wpabuf_put_data(buf, vn->lang, 3); + wpabuf_put_data(buf, vn->name, vn->name_len); + } + gas_anqp_set_element_len(buf, len); + } +} + + +static void anqp_add_wan_metrics(struct hostapd_data *hapd, + struct wpabuf *buf) +{ + if (hapd->conf->hs20_wan_metrics) { + 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_WAN_METRICS); + wpabuf_put_u8(buf, 0); /* Reserved */ + wpabuf_put_data(buf, hapd->conf->hs20_wan_metrics, 13); + gas_anqp_set_element_len(buf, len); + } +} + + +static void anqp_add_connection_capability(struct hostapd_data *hapd, + struct wpabuf *buf) +{ + if (hapd->conf->hs20_connection_capability) { + 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_CONNECTION_CAPABILITY); + wpabuf_put_u8(buf, 0); /* Reserved */ + wpabuf_put_data(buf, hapd->conf->hs20_connection_capability, + hapd->conf->hs20_connection_capability_len); + gas_anqp_set_element_len(buf, len); + } +} + + +static void anqp_add_operating_class(struct hostapd_data *hapd, + struct wpabuf *buf) +{ + if (hapd->conf->hs20_operating_class) { + 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_OPERATING_CLASS); + wpabuf_put_u8(buf, 0); /* Reserved */ + wpabuf_put_data(buf, hapd->conf->hs20_operating_class, + hapd->conf->hs20_operating_class_len); + gas_anqp_set_element_len(buf, len); + } +} + +#endif /* CONFIG_HS20 */ + + +static struct wpabuf * +gas_serv_build_gas_resp_payload(struct hostapd_data *hapd, + unsigned int request, + struct gas_dialog_info *di, + const u8 *home_realm, size_t home_realm_len) +{ + struct wpabuf *buf; + + buf = wpabuf_alloc(1400); + if (buf == NULL) + return NULL; + + if (request & ANQP_REQ_CAPABILITY_LIST) + anqp_add_capab_list(hapd, buf); + if (request & ANQP_REQ_VENUE_NAME) + anqp_add_venue_name(hapd, buf); + if (request & ANQP_REQ_NETWORK_AUTH_TYPE) + anqp_add_network_auth_type(hapd, buf); + if (request & ANQP_REQ_ROAMING_CONSORTIUM) + anqp_add_roaming_consortium(hapd, buf); + if (request & ANQP_REQ_IP_ADDR_TYPE_AVAILABILITY) + anqp_add_ip_addr_type_availability(hapd, buf); + if (request & (ANQP_REQ_NAI_REALM | ANQP_REQ_NAI_HOME_REALM)) + anqp_add_nai_realm(hapd, buf, home_realm, home_realm_len, + request & ANQP_REQ_NAI_REALM, + request & ANQP_REQ_NAI_HOME_REALM); + if (request & ANQP_REQ_3GPP_CELLULAR_NETWORK) + anqp_add_3gpp_cellular_network(hapd, buf); + if (request & ANQP_REQ_DOMAIN_NAME) + anqp_add_domain_name(hapd, buf); + +#ifdef CONFIG_HS20 + if (request & ANQP_REQ_HS_CAPABILITY_LIST) + anqp_add_hs_capab_list(hapd, buf); + if (request & ANQP_REQ_OPERATOR_FRIENDLY_NAME) + anqp_add_operator_friendly_name(hapd, buf); + if (request & ANQP_REQ_WAN_METRICS) + anqp_add_wan_metrics(hapd, buf); + if (request & ANQP_REQ_CONNECTION_CAPABILITY) + anqp_add_connection_capability(hapd, buf); + if (request & ANQP_REQ_OPERATING_CLASS) + anqp_add_operating_class(hapd, buf); +#endif /* CONFIG_HS20 */ + + return buf; +} + + +static void gas_serv_clear_cached_ies(void *eloop_data, void *user_ctx) +{ + struct gas_dialog_info *dia = eloop_data; + + wpa_printf(MSG_DEBUG, "GAS: Timeout triggered, clearing dialog for " + "dialog token %d", dia->dialog_token); + + gas_serv_dialog_clear(dia); +} + + +struct anqp_query_info { + unsigned int request; + unsigned int remote_request; + const u8 *home_realm_query; + size_t home_realm_query_len; + u16 remote_delay; +}; + + +static void set_anqp_req(unsigned int bit, const char *name, int local, + unsigned int remote, u16 remote_delay, + struct anqp_query_info *qi) +{ + qi->request |= bit; + if (local) { + wpa_printf(MSG_DEBUG, "ANQP: %s (local)", name); + } else if (bit & remote) { + wpa_printf(MSG_DEBUG, "ANQP: %s (remote)", name); + qi->remote_request |= bit; + if (remote_delay > qi->remote_delay) + qi->remote_delay = remote_delay; + } else { + wpa_printf(MSG_DEBUG, "ANQP: %s not available", name); + } +} + + +static void rx_anqp_query_list_id(struct hostapd_data *hapd, u16 info_id, + struct anqp_query_info *qi) +{ + switch (info_id) { + case ANQP_CAPABILITY_LIST: + set_anqp_req(ANQP_REQ_CAPABILITY_LIST, "Capability List", 1, 0, + 0, qi); + break; + case ANQP_VENUE_NAME: + set_anqp_req(ANQP_REQ_VENUE_NAME, "Venue Name", + hapd->conf->venue_name != NULL, 0, 0, qi); + break; + case ANQP_NETWORK_AUTH_TYPE: + set_anqp_req(ANQP_REQ_NETWORK_AUTH_TYPE, "Network Auth Type", + hapd->conf->network_auth_type != NULL, + 0, 0, qi); + break; + case ANQP_ROAMING_CONSORTIUM: + set_anqp_req(ANQP_REQ_ROAMING_CONSORTIUM, "Roaming Consortium", + hapd->conf->roaming_consortium != NULL, 0, 0, qi); + break; + case ANQP_IP_ADDR_TYPE_AVAILABILITY: + set_anqp_req(ANQP_REQ_IP_ADDR_TYPE_AVAILABILITY, + "IP Addr Type Availability", + hapd->conf->ipaddr_type_configured, + 0, 0, qi); + break; + case ANQP_NAI_REALM: + set_anqp_req(ANQP_REQ_NAI_REALM, "NAI Realm", + hapd->conf->nai_realm_data != NULL, + 0, 0, qi); + break; + case ANQP_3GPP_CELLULAR_NETWORK: + set_anqp_req(ANQP_REQ_3GPP_CELLULAR_NETWORK, + "3GPP Cellular Network", + hapd->conf->anqp_3gpp_cell_net != NULL, + 0, 0, qi); + break; + case ANQP_DOMAIN_NAME: + set_anqp_req(ANQP_REQ_DOMAIN_NAME, "Domain Name", + hapd->conf->domain_name != NULL, + 0, 0, qi); + break; + default: + wpa_printf(MSG_DEBUG, "ANQP: Unsupported Info Id %u", + info_id); + break; + } +} + + +static void rx_anqp_query_list(struct hostapd_data *hapd, + const u8 *pos, const u8 *end, + struct anqp_query_info *qi) +{ + wpa_printf(MSG_DEBUG, "ANQP: %u Info IDs requested in Query list", + (unsigned int) (end - pos) / 2); + + while (pos + 2 <= end) { + rx_anqp_query_list_id(hapd, WPA_GET_LE16(pos), qi); + pos += 2; + } +} + + +#ifdef CONFIG_HS20 + +static void rx_anqp_hs_query_list(struct hostapd_data *hapd, u8 subtype, + struct anqp_query_info *qi) +{ + switch (subtype) { + case HS20_STYPE_CAPABILITY_LIST: + set_anqp_req(ANQP_REQ_HS_CAPABILITY_LIST, "HS Capability List", + 1, 0, 0, qi); + break; + case HS20_STYPE_OPERATOR_FRIENDLY_NAME: + set_anqp_req(ANQP_REQ_OPERATOR_FRIENDLY_NAME, + "Operator Friendly Name", + hapd->conf->hs20_oper_friendly_name != NULL, + 0, 0, qi); + break; + case HS20_STYPE_WAN_METRICS: + set_anqp_req(ANQP_REQ_WAN_METRICS, "WAN Metrics", + hapd->conf->hs20_wan_metrics != NULL, + 0, 0, qi); + break; + case HS20_STYPE_CONNECTION_CAPABILITY: + set_anqp_req(ANQP_REQ_CONNECTION_CAPABILITY, + "Connection Capability", + hapd->conf->hs20_connection_capability != NULL, + 0, 0, qi); + break; + case HS20_STYPE_OPERATING_CLASS: + set_anqp_req(ANQP_REQ_OPERATING_CLASS, "Operating Class", + hapd->conf->hs20_operating_class != NULL, + 0, 0, qi); + break; + default: + wpa_printf(MSG_DEBUG, "ANQP: Unsupported HS 2.0 subtype %u", + subtype); + break; + } +} + + +static void rx_anqp_hs_nai_home_realm(struct hostapd_data *hapd, + const u8 *pos, const u8 *end, + struct anqp_query_info *qi) +{ + qi->request |= ANQP_REQ_NAI_HOME_REALM; + qi->home_realm_query = pos; + qi->home_realm_query_len = end - pos; + if (hapd->conf->nai_realm_data != NULL) { + wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 NAI Home Realm Query " + "(local)"); + } else { + wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 NAI Home Realm Query not " + "available"); + } +} + + +static void rx_anqp_vendor_specific(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"); + return; + } + + oui = WPA_GET_BE24(pos); + pos += 3; + if (oui != OUI_WFA) { + wpa_printf(MSG_DEBUG, "ANQP: Unsupported vendor OUI %06x", + oui); + return; + } + + 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) { + case HS20_STYPE_QUERY_LIST: + wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 Query List"); + while (pos < end) { + rx_anqp_hs_query_list(hapd, *pos, qi); + pos++; + } + break; + case HS20_STYPE_NAI_HOME_REALM_QUERY: + rx_anqp_hs_nai_home_realm(hapd, pos, end, qi); + break; + default: + wpa_printf(MSG_DEBUG, "ANQP: Unsupported HS 2.0 query subtype " + "%u", subtype); + break; + } +} + +#endif /* CONFIG_HS20 */ + + +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 wpabuf *buf, *tx_buf; + + buf = gas_serv_build_gas_resp_payload(hapd, qi->request, NULL, + qi->home_realm_query, + qi->home_realm_query_len); + wpa_hexdump_buf(MSG_MSGDUMP, "ANQP: Locally generated ANQP responses", + buf); + if (!buf) + return; + + if (wpabuf_len(buf) > hapd->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, "ANQP: 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, "ANQP: Could not create dialog " + "for " MACSTR " (dialog token %u)", + MAC2STR(sa), dialog_token); + wpabuf_free(buf); + return; + } + di->prot = prot; + di->sd_resp = buf; + di->sd_resp_pos = 0; + tx_buf = gas_anqp_build_initial_resp_buf( + dialog_token, WLAN_STATUS_SUCCESS, comeback_delay, + NULL); + } else { + wpa_printf(MSG_DEBUG, "ANQP: Initial response (no comeback)"); + tx_buf = gas_anqp_build_initial_resp_buf( + dialog_token, WLAN_STATUS_SUCCESS, 0, buf); + 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_free(tx_buf); +} + + +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 *pos = data; + const u8 *end = data + len; + const u8 *next; + u8 dialog_token; + u16 slen; + struct anqp_query_info qi; + const u8 *adv_proto; + + if (len < 1 + 2) + return; + + os_memset(&qi, 0, sizeof(qi)); + + dialog_token = *pos++; + wpa_msg(hapd->msg_ctx, MSG_DEBUG, + "GAS: GAS Initial Request from " MACSTR " (dialog token %u) ", + MAC2STR(sa), dialog_token); + + if (*pos != WLAN_EID_ADV_PROTO) { + wpa_msg(hapd->msg_ctx, MSG_DEBUG, + "GAS: Unexpected IE in GAS Initial Request: %u", *pos); + return; + } + adv_proto = pos++; + + slen = *pos++; + next = pos + slen; + if (next > end || slen < 2) { + wpa_msg(hapd->msg_ctx, MSG_DEBUG, + "GAS: Invalid IE in GAS Initial Request"); + return; + } + pos++; /* skip QueryRespLenLimit and PAME-BI */ + + if (*pos != ACCESS_NETWORK_QUERY_PROTOCOL) { + struct wpabuf *buf; + wpa_msg(hapd->msg_ctx, MSG_DEBUG, + "GAS: Unsupported GAS advertisement protocol id %u", + *pos); + if (sa[0] & 0x01) + return; /* Invalid source address - drop silently */ + buf = gas_build_initial_resp( + dialog_token, WLAN_STATUS_GAS_ADV_PROTO_NOT_SUPPORTED, + 0, 2 + slen + 2); + if (buf == NULL) + return; + wpabuf_put_data(buf, adv_proto, 2 + slen); + 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)); + wpabuf_free(buf); + return; + } + + pos = next; + /* Query Request */ + if (pos + 2 > end) + return; + slen = WPA_GET_LE16(pos); + pos += 2; + if (pos + slen > end) + return; + end = pos + slen; + + /* ANQP Query Request */ + while (pos < end) { + u16 info_id, elen; + + if (pos + 4 > end) + return; + + info_id = WPA_GET_LE16(pos); + pos += 2; + elen = WPA_GET_LE16(pos); + pos += 2; + + if (pos + elen > end) { + wpa_printf(MSG_DEBUG, "ANQP: Invalid Query Request"); + return; + } + + switch (info_id) { + 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); + break; + } + + pos += elen; + } + + gas_serv_req_local_processing(hapd, sa, dialog_token, &qi, prot); +} + + +void gas_serv_tx_gas_response(struct hostapd_data *hapd, const u8 *dst, + struct gas_dialog_info *dialog) +{ + struct wpabuf *buf, *tx_buf; + u8 dialog_token = dialog->dialog_token; + size_t frag_len; + + if (dialog->sd_resp == NULL) { + buf = gas_serv_build_gas_resp_payload(hapd, + dialog->all_requested, + dialog, NULL, 0); + wpa_hexdump_buf(MSG_MSGDUMP, "ANQP: Generated ANQP responses", + buf); + if (!buf) + goto tx_gas_response_done; + dialog->sd_resp = buf; + dialog->sd_resp_pos = 0; + } + frag_len = wpabuf_len(dialog->sd_resp) - dialog->sd_resp_pos; + if (frag_len > hapd->gas_frag_limit || dialog->comeback_delay || + hapd->conf->gas_comeback_delay) { + u16 comeback_delay_tus = dialog->comeback_delay + + GAS_SERV_COMEBACK_DELAY_FUDGE; + u32 comeback_delay_secs, comeback_delay_usecs; + + if (hapd->conf->gas_comeback_delay) { + /* Testing - allow overriding of the delay value */ + comeback_delay_tus = hapd->conf->gas_comeback_delay; + } + + wpa_printf(MSG_DEBUG, "GAS: Response frag_len %u (frag limit " + "%u) and comeback delay %u, " + "requesting comebacks", (unsigned int) frag_len, + (unsigned int) hapd->gas_frag_limit, + dialog->comeback_delay); + tx_buf = gas_anqp_build_initial_resp_buf(dialog_token, + WLAN_STATUS_SUCCESS, + comeback_delay_tus, + NULL); + if (tx_buf) { + wpa_msg(hapd->msg_ctx, MSG_DEBUG, + "GAS: Tx GAS Initial Resp (comeback = 10TU)"); + if (dialog->prot) + convert_to_protected_dual(tx_buf); + hostapd_drv_send_action(hapd, hapd->iface->freq, 0, + dst, + wpabuf_head(tx_buf), + wpabuf_len(tx_buf)); + } + wpabuf_free(tx_buf); + + /* start a timer of 1.5 * comeback-delay */ + comeback_delay_tus = comeback_delay_tus + + (comeback_delay_tus / 2); + comeback_delay_secs = (comeback_delay_tus * 1024) / 1000000; + comeback_delay_usecs = (comeback_delay_tus * 1024) - + (comeback_delay_secs * 1000000); + eloop_register_timeout(comeback_delay_secs, + comeback_delay_usecs, + gas_serv_clear_cached_ies, dialog, + NULL); + goto tx_gas_response_done; + } + + buf = wpabuf_alloc_copy(wpabuf_head_u8(dialog->sd_resp) + + dialog->sd_resp_pos, frag_len); + if (buf == NULL) { + wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Buffer allocation " + "failed"); + goto tx_gas_response_done; + } + tx_buf = gas_anqp_build_initial_resp_buf(dialog_token, + WLAN_STATUS_SUCCESS, 0, buf); + wpabuf_free(buf); + if (tx_buf == NULL) + goto tx_gas_response_done; + wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Tx GAS Initial " + "Response (frag_id %d frag_len %d)", + dialog->sd_frag_id, (int) frag_len); + dialog->sd_frag_id++; + + if (dialog->prot) + convert_to_protected_dual(tx_buf); + hostapd_drv_send_action(hapd, hapd->iface->freq, 0, dst, + wpabuf_head(tx_buf), wpabuf_len(tx_buf)); + wpabuf_free(tx_buf); +tx_gas_response_done: + gas_serv_clear_cached_ies(dialog, NULL); +} + + +static void gas_serv_rx_gas_comeback_req(struct hostapd_data *hapd, + const u8 *sa, + const u8 *data, size_t len, int prot) +{ + struct gas_dialog_info *dialog; + struct wpabuf *buf, *tx_buf; + u8 dialog_token; + size_t frag_len; + int more = 0; + + wpa_hexdump(MSG_DEBUG, "GAS: RX GAS Comeback Request", data, len); + if (len < 1) + return; + dialog_token = *data; + wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Dialog Token: %u", + dialog_token); + + dialog = gas_serv_dialog_find(hapd, sa, dialog_token); + if (!dialog) { + wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: No pending SD " + "response fragment for " MACSTR " dialog token %u", + MAC2STR(sa), dialog_token); + + if (sa[0] & 0x01) + return; /* Invalid source address - drop silently */ + tx_buf = gas_anqp_build_comeback_resp_buf( + dialog_token, WLAN_STATUS_NO_OUTSTANDING_GAS_REQ, 0, 0, + 0, NULL); + if (tx_buf == NULL) + return; + goto send_resp; + } + + if (dialog->sd_resp == NULL) { + wpa_printf(MSG_DEBUG, "GAS: Remote request 0x%x received 0x%x", + dialog->requested, dialog->received); + if ((dialog->requested & dialog->received) != + dialog->requested) { + wpa_printf(MSG_DEBUG, "GAS: Did not receive response " + "from remote processing"); + gas_serv_dialog_clear(dialog); + tx_buf = gas_anqp_build_comeback_resp_buf( + dialog_token, + WLAN_STATUS_GAS_RESP_NOT_RECEIVED, 0, 0, 0, + NULL); + if (tx_buf == NULL) + return; + goto send_resp; + } + + buf = gas_serv_build_gas_resp_payload(hapd, + dialog->all_requested, + dialog, NULL, 0); + wpa_hexdump_buf(MSG_MSGDUMP, "ANQP: Generated ANQP responses", + buf); + if (!buf) + goto rx_gas_comeback_req_done; + dialog->sd_resp = buf; + dialog->sd_resp_pos = 0; + } + frag_len = wpabuf_len(dialog->sd_resp) - dialog->sd_resp_pos; + if (frag_len > hapd->gas_frag_limit) { + frag_len = hapd->gas_frag_limit; + more = 1; + } + wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: resp frag_len %u", + (unsigned int) frag_len); + buf = wpabuf_alloc_copy(wpabuf_head_u8(dialog->sd_resp) + + dialog->sd_resp_pos, frag_len); + if (buf == NULL) { + wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Failed to allocate " + "buffer"); + goto rx_gas_comeback_req_done; + } + tx_buf = gas_anqp_build_comeback_resp_buf(dialog_token, + WLAN_STATUS_SUCCESS, + dialog->sd_frag_id, + more, 0, buf); + wpabuf_free(buf); + if (tx_buf == NULL) + goto rx_gas_comeback_req_done; + wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Tx GAS Comeback Response " + "(frag_id %d more=%d frag_len=%d)", + dialog->sd_frag_id, more, (int) frag_len); + dialog->sd_frag_id++; + dialog->sd_resp_pos += frag_len; + + if (more) { + wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: %d more bytes remain " + "to be sent", + (int) (wpabuf_len(dialog->sd_resp) - + dialog->sd_resp_pos)); + } else { + wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: All fragments of " + "SD response sent"); + gas_serv_dialog_clear(dialog); + gas_serv_free_dialogs(hapd, sa); + } + +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)); + wpabuf_free(tx_buf); + return; + +rx_gas_comeback_req_done: + gas_serv_clear_cached_ies(dialog, NULL); +} + + +static void gas_serv_rx_public_action(void *ctx, const u8 *buf, size_t len, + int freq) +{ + struct hostapd_data *hapd = ctx; + const struct ieee80211_mgmt *mgmt; + size_t hdr_len; + const u8 *sa, *data; + int prot; + + mgmt = (const struct ieee80211_mgmt *) buf; + hdr_len = (const u8 *) &mgmt->u.action.u.vs_public_action.action - buf; + if (hdr_len > len) + return; + if (mgmt->u.action.category != WLAN_ACTION_PUBLIC && + mgmt->u.action.category != WLAN_ACTION_PROTECTED_DUAL) + return; + /* + * Note: Public Action and Protected Dual of Public Action frames share + * the same payload structure, so it is fine to use definitions of + * Public Action frames to process both. + */ + prot = mgmt->u.action.category == WLAN_ACTION_PROTECTED_DUAL; + sa = mgmt->sa; + len -= hdr_len; + data = &mgmt->u.action.u.public_action.action; + switch (data[0]) { + case WLAN_PA_GAS_INITIAL_REQ: + gas_serv_rx_gas_initial_req(hapd, sa, data + 1, len - 1, prot); + break; + case WLAN_PA_GAS_COMEBACK_REQ: + gas_serv_rx_gas_comeback_req(hapd, sa, data + 1, len - 1, prot); + break; + } +} + + +int gas_serv_init(struct hostapd_data *hapd) +{ + 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; +} + + +void gas_serv_deinit(struct hostapd_data *hapd) +{ +} diff -Nru wpa-1.0/src/ap/gas_serv.h wpa-2.1/src/ap/gas_serv.h --- wpa-1.0/src/ap/gas_serv.h 1970-01-01 00:00:00.000000000 +0000 +++ wpa-2.1/src/ap/gas_serv.h 2014-02-04 11:23:35.000000000 +0000 @@ -0,0 +1,72 @@ +/* + * Generic advertisement service (GAS) server + * Copyright (c) 2011-2012, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef GAS_SERV_H +#define GAS_SERV_H + +#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_NETWORK_AUTH_TYPE \ + (1 << (ANQP_NETWORK_AUTH_TYPE - ANQP_QUERY_LIST)) +#define ANQP_REQ_ROAMING_CONSORTIUM \ + (1 << (ANQP_ROAMING_CONSORTIUM - ANQP_QUERY_LIST)) +#define ANQP_REQ_IP_ADDR_TYPE_AVAILABILITY \ + (1 << (ANQP_IP_ADDR_TYPE_AVAILABILITY - ANQP_QUERY_LIST)) +#define ANQP_REQ_NAI_REALM \ + (1 << (ANQP_NAI_REALM - ANQP_QUERY_LIST)) +#define ANQP_REQ_3GPP_CELLULAR_NETWORK \ + (1 << (ANQP_3GPP_CELLULAR_NETWORK - ANQP_QUERY_LIST)) +#define ANQP_REQ_DOMAIN_NAME \ + (1 << (ANQP_DOMAIN_NAME - ANQP_QUERY_LIST)) +#define ANQP_REQ_HS_CAPABILITY_LIST \ + (0x10000 << HS20_STYPE_CAPABILITY_LIST) +#define ANQP_REQ_OPERATOR_FRIENDLY_NAME \ + (0x10000 << HS20_STYPE_OPERATOR_FRIENDLY_NAME) +#define ANQP_REQ_WAN_METRICS \ + (0x10000 << HS20_STYPE_WAN_METRICS) +#define ANQP_REQ_CONNECTION_CAPABILITY \ + (0x10000 << HS20_STYPE_CONNECTION_CAPABILITY) +#define ANQP_REQ_NAI_HOME_REALM \ + (0x10000 << HS20_STYPE_NAI_HOME_REALM_QUERY) +#define ANQP_REQ_OPERATING_CLASS \ + (0x10000 << HS20_STYPE_OPERATING_CLASS) + +/* To account for latencies between hostapd and external ANQP processor */ +#define GAS_SERV_COMEBACK_DELAY_FUDGE 10 +#define GAS_SERV_MIN_COMEBACK_DELAY 100 /* in TU */ + +struct gas_dialog_info { + u8 valid; + u8 index; + struct wpabuf *sd_resp; /* Fragmented response */ + u8 dialog_token; + size_t sd_resp_pos; /* Offset in sd_resp */ + u8 sd_frag_id; + u16 comeback_delay; + int prot; /* whether Protected Dual of Public Action frame is used */ + + unsigned int requested; + unsigned int received; + unsigned int all_requested; +}; + +struct hostapd_data; + +void gas_serv_tx_gas_response(struct hostapd_data *hapd, const u8 *dst, + struct gas_dialog_info *dialog); +struct gas_dialog_info * +gas_serv_dialog_find(struct hostapd_data *hapd, const u8 *addr, + u8 dialog_token); +void gas_serv_dialog_clear(struct gas_dialog_info *dialog); + +int gas_serv_init(struct hostapd_data *hapd); +void gas_serv_deinit(struct hostapd_data *hapd); + +#endif /* GAS_SERV_H */ diff -Nru wpa-1.0/src/ap/hostapd.c wpa-2.1/src/ap/hostapd.c --- wpa-1.0/src/ap/hostapd.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/ap/hostapd.c 2014-02-04 11:23:35.000000000 +0000 @@ -1,15 +1,9 @@ /* * hostapd / Initialization and configuration - * Copyright (c) 2002-2009, Jouni Malinen + * Copyright (c) 2002-2014, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "utils/includes.h" @@ -17,8 +11,9 @@ #include "utils/common.h" #include "utils/eloop.h" #include "common/ieee802_11_defs.h" +#include "common/wpa_ctrl.h" #include "radius/radius_client.h" -#include "drivers/driver.h" +#include "radius/radius_das.h" #include "hostapd.h" #include "authsrv.h" #include "sta_info.h" @@ -36,21 +31,52 @@ #include "ap_drv_ops.h" #include "ap_config.h" #include "p2p_hostapd.h" +#include "gas_serv.h" +#include "dfs.h" static int hostapd_flush_old_stations(struct hostapd_data *hapd, u16 reason); static int hostapd_setup_encryption(char *iface, struct hostapd_data *hapd); 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); + + +int hostapd_for_each_interface(struct hapd_interfaces *interfaces, + int (*cb)(struct hostapd_iface *iface, + void *ctx), void *ctx) +{ + size_t i; + int ret; + + for (i = 0; i < interfaces->count; i++) { + ret = cb(interfaces->iface[i], ctx); + if (ret) + return ret; + } -extern int wpa_debug_level; + return 0; +} static void hostapd_reload_bss(struct hostapd_data *hapd) { + struct hostapd_ssid *ssid; + #ifndef CONFIG_NO_RADIUS radius_client_reconfig(hapd->radius, hapd->conf->radius); #endif /* CONFIG_NO_RADIUS */ + ssid = &hapd->conf->ssid; + if (!ssid->wpa_psk_set && ssid->wpa_psk && !ssid->wpa_psk->next && + ssid->wpa_passphrase_set && ssid->wpa_passphrase) { + /* + * Force PSK to be derived again since SSID or passphrase may + * have changed. + */ + os_free(ssid->wpa_psk); + ssid->wpa_psk = NULL; + } if (hostapd_setup_wpa_psk(hapd->conf)) { wpa_printf(MSG_ERROR, "Failed to re-configure WPA PSK " "after reloading configuration"); @@ -85,7 +111,7 @@ hostapd_update_wps(hapd); if (hapd->conf->ssid.ssid_set && - hostapd_set_ssid(hapd, (u8 *) hapd->conf->ssid.ssid, + hostapd_set_ssid(hapd, hapd->conf->ssid.ssid, hapd->conf->ssid.ssid_len)) { wpa_printf(MSG_ERROR, "Could not set SSID for kernel driver"); /* try to continue */ @@ -94,18 +120,10 @@ } -int hostapd_reload_config(struct hostapd_iface *iface) +static void hostapd_clear_old(struct hostapd_iface *iface) { - struct hostapd_data *hapd = iface->bss[0]; - struct hostapd_config *newconf, *oldconf; size_t j; - if (iface->config_read_cb == NULL) - return -1; - newconf = iface->config_read_cb(iface->config_fname); - if (newconf == NULL) - return -1; - /* * Deauthenticate all stations since the new configuration may not * allow them to use the BSS anymore. @@ -121,6 +139,31 @@ radius_client_flush(iface->bss[j]->radius, 0); #endif /* CONFIG_NO_RADIUS */ } +} + + +int hostapd_reload_config(struct hostapd_iface *iface) +{ + struct hostapd_data *hapd = iface->bss[0]; + struct hostapd_config *newconf, *oldconf; + size_t j; + + if (iface->config_fname == NULL) { + /* Only in-memory config in use - assume it has been updated */ + hostapd_clear_old(iface); + for (j = 0; j < iface->num_bss; j++) + hostapd_reload_bss(iface->bss[j]); + return 0; + } + + if (iface->interfaces == NULL || + iface->interfaces->config_read_cb == NULL) + return -1; + newconf = iface->interfaces->config_read_cb(iface->config_fname); + if (newconf == NULL) + return -1; + + hostapd_clear_old(iface); oldconf = hapd->iconf; iface->conf = newconf; @@ -128,7 +171,7 @@ for (j = 0; j < iface->num_bss; j++) { hapd = iface->bss[j]; hapd->iconf = newconf; - hapd->conf = &newconf->bss[j]; + hapd->conf = newconf->bss[j]; hostapd_reload_bss(hapd); } @@ -190,48 +233,20 @@ errors++; } - if (ssid->dyn_vlan_keys) { - size_t i; - for (i = 0; i <= ssid->max_dyn_vlan_keys; i++) { - const char *ifname; - struct hostapd_wep_keys *key = ssid->dyn_vlan_keys[i]; - if (key == NULL) - continue; - ifname = hostapd_get_vlan_id_ifname(hapd->conf->vlan, - i); - if (ifname == NULL) - continue; - - idx = key->idx; - if (hostapd_drv_set_key(ifname, hapd, WPA_ALG_WEP, - broadcast_ether_addr, idx, 1, - NULL, 0, key->key[idx], - key->len[idx])) { - wpa_printf(MSG_WARNING, "Could not set " - "dynamic VLAN WEP encryption."); - errors++; - } - } - } - return errors; } -/** - * hostapd_cleanup - Per-BSS cleanup (deinitialization) - * @hapd: Pointer to BSS data - * - * This function is used to free all per-BSS data structures and resources. - * This gets called in a loop for each BSS between calls to - * hostapd_cleanup_iface_pre() and hostapd_cleanup_iface() when an interface - * is deinitialized. Most of the modules that are initialized in - * hostapd_setup_bss() are deinitialized here. - */ -static void hostapd_cleanup(struct hostapd_data *hapd) + +static void hostapd_free_hapd_data(struct hostapd_data *hapd) { - if (hapd->iface->ctrl_iface_deinit) - hapd->iface->ctrl_iface_deinit(hapd); + if (!hapd->started) { + wpa_printf(MSG_ERROR, "%s: Interface %s wasn't started", + __func__, hapd->conf->iface); + return; + } + hapd->started = 0; + wpa_printf(MSG_DEBUG, "%s(%s)", __func__, hapd->conf->iface); iapp_deinit(hapd->iapp); hapd->iapp = NULL; accounting_deinit(hapd); @@ -241,6 +256,8 @@ #ifndef CONFIG_NO_RADIUS radius_client_deinit(hapd->radius); hapd->radius = NULL; + radius_das_deinit(hapd->radius_das); + hapd->radius_das = NULL; #endif /* CONFIG_NO_RADIUS */ hostapd_deinit_wps(hapd); @@ -264,18 +281,47 @@ #endif /* CONFIG_P2P */ wpabuf_free(hapd->time_adv); + +#ifdef CONFIG_INTERWORKING + gas_serv_deinit(hapd); +#endif /* CONFIG_INTERWORKING */ + +#ifdef CONFIG_SQLITE + os_free(hapd->tmp_eap_user.identity); + os_free(hapd->tmp_eap_user.password); +#endif /* CONFIG_SQLITE */ } /** - * hostapd_cleanup_iface_pre - Preliminary per-interface cleanup - * @iface: Pointer to interface data + * hostapd_cleanup - Per-BSS cleanup (deinitialization) + * @hapd: Pointer to BSS data * - * This function is called before per-BSS data structures are deinitialized - * with hostapd_cleanup(). + * This function is used to free all per-BSS data structures and resources. + * Most of the modules that are initialized in hostapd_setup_bss() are + * deinitialized here. */ -static void hostapd_cleanup_iface_pre(struct hostapd_iface *iface) +static void hostapd_cleanup(struct hostapd_data *hapd) +{ + wpa_printf(MSG_DEBUG, "%s(hapd=%p (%s))", __func__, hapd, + hapd->conf->iface); + if (hapd->iface->interfaces && + hapd->iface->interfaces->ctrl_iface_deinit) + hapd->iface->interfaces->ctrl_iface_deinit(hapd); + hostapd_free_hapd_data(hapd); +} + + +static void hostapd_cleanup_iface_partial(struct hostapd_iface *iface) { + wpa_printf(MSG_DEBUG, "%s(%p)", __func__, iface); + hostapd_free_hw_features(iface->hw_features, iface->num_hw_features); + iface->hw_features = NULL; + os_free(iface->current_rates); + iface->current_rates = NULL; + os_free(iface->basic_rates); + iface->basic_rates = NULL; + ap_list_deinit(iface); } @@ -288,20 +334,29 @@ */ static void hostapd_cleanup_iface(struct hostapd_iface *iface) { - hostapd_free_hw_features(iface->hw_features, iface->num_hw_features); - iface->hw_features = NULL; - os_free(iface->current_rates); - iface->current_rates = NULL; - ap_list_deinit(iface); + wpa_printf(MSG_DEBUG, "%s(%p)", __func__, iface); + eloop_cancel_timeout(channel_list_update_timeout, iface, NULL); + + hostapd_cleanup_iface_partial(iface); hostapd_config_free(iface->conf); iface->conf = NULL; os_free(iface->config_fname); os_free(iface->bss); + wpa_printf(MSG_DEBUG, "%s: free iface=%p", __func__, iface); os_free(iface); } +static void hostapd_clear_wep(struct hostapd_data *hapd) +{ + if (hapd->drv_priv) { + hostapd_set_privacy(hapd, 0); + hostapd_broadcast_wep_clear(hapd); + } +} + + static int hostapd_setup_encryption(char *iface, struct hostapd_data *hapd) { int i; @@ -346,12 +401,13 @@ if (hostapd_drv_none(hapd) || hapd->drv_priv == NULL) return 0; - wpa_printf(MSG_DEBUG, "Flushing old station entries"); + wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "Flushing old station entries"); if (hostapd_flush(hapd)) { - wpa_printf(MSG_WARNING, "Could not connect to kernel driver."); + wpa_msg(hapd->msg_ctx, MSG_WARNING, "Could not connect to " + "kernel driver"); ret = -1; } - wpa_printf(MSG_DEBUG, "Deauthenticate all stations"); + 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); @@ -386,7 +442,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 (hostapd_mac_comp_empty(iface->conf->bss[j]->bssid) == 0) { if (j) auto_addr++; continue; @@ -394,7 +450,7 @@ for (i = 0; i < ETH_ALEN; i++) { mask[i] |= - iface->conf->bss[j].bssid[i] ^ + iface->conf->bss[j]->bssid[i] ^ hapd->own_addr[i]; } } @@ -459,7 +515,7 @@ size_t i; for (i = 0; i < conf->num_bss; i++) { - if (hostapd_mac_comp(conf->bss[i].bssid, a) == 0) { + if (hostapd_mac_comp(conf->bss[i]->bssid, a) == 0) { return 1; } } @@ -468,12 +524,93 @@ } +#ifndef CONFIG_NO_RADIUS + +static int hostapd_das_nas_mismatch(struct hostapd_data *hapd, + struct radius_das_attrs *attr) +{ + /* TODO */ + return 0; +} + + +static struct sta_info * hostapd_das_find_sta(struct hostapd_data *hapd, + struct radius_das_attrs *attr) +{ + struct sta_info *sta = NULL; + char buf[128]; + + if (attr->sta_addr) + sta = ap_get_sta(hapd, attr->sta_addr); + + if (sta == NULL && attr->acct_session_id && + attr->acct_session_id_len == 17) { + for (sta = hapd->sta_list; sta; sta = sta->next) { + 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) + break; + } + } + + if (sta == NULL && attr->cui) { + for (sta = hapd->sta_list; sta; sta = sta->next) { + struct wpabuf *cui; + cui = ieee802_1x_get_radius_cui(sta->eapol_sm); + if (cui && wpabuf_len(cui) == attr->cui_len && + os_memcmp(wpabuf_head(cui), attr->cui, + attr->cui_len) == 0) + break; + } + } + + if (sta == NULL && attr->user_name) { + for (sta = hapd->sta_list; sta; sta = sta->next) { + u8 *identity; + size_t identity_len; + identity = ieee802_1x_get_identity(sta->eapol_sm, + &identity_len); + if (identity && + identity_len == attr->user_name_len && + os_memcmp(identity, attr->user_name, identity_len) + == 0) + break; + } + } + + return sta; +} + + +static enum radius_das_res +hostapd_das_disconnect(void *ctx, struct radius_das_attrs *attr) +{ + struct hostapd_data *hapd = ctx; + struct sta_info *sta; + + if (hostapd_das_nas_mismatch(hapd, attr)) + return RADIUS_DAS_NAS_MISMATCH; + + sta = hostapd_das_find_sta(hapd, attr); + if (sta == NULL) + return RADIUS_DAS_SESSION_NOT_FOUND; + + hostapd_drv_sta_deauth(hapd, sta->addr, + WLAN_REASON_PREV_AUTH_NOT_VALID); + ap_sta_deauthenticate(hapd, sta, WLAN_REASON_PREV_AUTH_NOT_VALID); + + return RADIUS_DAS_SUCCESS; +} + +#endif /* CONFIG_NO_RADIUS */ /** * hostapd_setup_bss - Per-BSS setup (initialization) * @hapd: Pointer to BSS data - * @first: Whether this BSS is the first BSS of an interface + * @first: Whether this BSS is the first BSS of an interface; -1 = not first, + * but interface may exist * * This function is used to initialize all per-BSS data structures and * resources. This gets called in a loop for each BSS when an interface is @@ -488,7 +625,17 @@ char force_ifname[IFNAMSIZ]; u8 if_addr[ETH_ALEN]; - if (!first) { + wpa_printf(MSG_DEBUG, "%s(hapd=%p (%s), first=%d)", + __func__, hapd, hapd->conf->iface, first); + + if (hapd->started) { + wpa_printf(MSG_ERROR, "%s: Interface %s was already started", + __func__, hapd->conf->iface); + return -1; + } + hapd->started = 1; + + if (!first || first == -1) { if (hostapd_mac_comp_empty(hapd->conf->bssid) == 0) { /* Allocate the next available BSSID. */ do { @@ -513,9 +660,10 @@ hapd->conf->iface, hapd->own_addr, hapd, &hapd->drv_priv, force_ifname, if_addr, hapd->conf->bridge[0] ? hapd->conf->bridge : - NULL)) { + NULL, first == -1)) { wpa_printf(MSG_ERROR, "Failed to add BSS (BSSID=" MACSTR ")", MAC2STR(hapd->own_addr)); + hapd->interface_added = 0; return -1; } } @@ -556,14 +704,14 @@ set_ssid = 0; conf->ssid.ssid_len = ssid_len; os_memcpy(conf->ssid.ssid, ssid, conf->ssid.ssid_len); - conf->ssid.ssid[conf->ssid.ssid_len] = '\0'; } if (!hostapd_drv_none(hapd)) { wpa_printf(MSG_ERROR, "Using interface %s with hwaddr " MACSTR - " and ssid '%s'", + " and ssid \"%s\"", hapd->conf->iface, MAC2STR(hapd->own_addr), - hapd->conf->ssid.ssid); + wpa_ssid_txt(hapd->conf->ssid.ssid, + hapd->conf->ssid.ssid_len)); } if (hostapd_setup_wpa_psk(conf)) { @@ -573,7 +721,7 @@ /* Set SSID for the kernel driver (to be used in beacon and probe * response frames) */ - if (set_ssid && hostapd_set_ssid(hapd, (u8 *) conf->ssid.ssid, + if (set_ssid && hostapd_set_ssid(hapd, conf->ssid.ssid, conf->ssid.ssid_len)) { wpa_printf(MSG_ERROR, "Could not set SSID for kernel driver"); return -1; @@ -587,6 +735,27 @@ wpa_printf(MSG_ERROR, "RADIUS client initialization failed."); return -1; } + + if (hapd->conf->radius_das_port) { + struct radius_das_conf das_conf; + os_memset(&das_conf, 0, sizeof(das_conf)); + das_conf.port = hapd->conf->radius_das_port; + das_conf.shared_secret = hapd->conf->radius_das_shared_secret; + das_conf.shared_secret_len = + hapd->conf->radius_das_shared_secret_len; + das_conf.client_addr = &hapd->conf->radius_das_client_addr; + das_conf.time_window = hapd->conf->radius_das_time_window; + das_conf.require_event_timestamp = + hapd->conf->radius_das_require_event_timestamp; + das_conf.ctx = hapd; + das_conf.disconnect = hostapd_das_disconnect; + hapd->radius_das = radius_das_init(&das_conf); + if (hapd->radius_das == NULL) { + wpa_printf(MSG_ERROR, "RADIUS DAS initialization " + "failed."); + return -1; + } + } #endif /* CONFIG_NO_RADIUS */ if (hostapd_acl_init(hapd)) { @@ -619,18 +788,27 @@ return -1; } - if (hapd->iface->ctrl_iface_init && - hapd->iface->ctrl_iface_init(hapd)) { - wpa_printf(MSG_ERROR, "Failed to setup control interface"); +#ifdef CONFIG_INTERWORKING + if (gas_serv_init(hapd)) { + wpa_printf(MSG_ERROR, "GAS server initialization failed"); + return -1; + } + + if (conf->qos_map_set_len && + hostapd_drv_set_qos_map(hapd, conf->qos_map_set, + conf->qos_map_set_len)) { + wpa_printf(MSG_ERROR, "Failed to initialize QoS Map"); return -1; } +#endif /* CONFIG_INTERWORKING */ if (!hostapd_drv_none(hapd) && vlan_init(hapd)) { wpa_printf(MSG_ERROR, "VLAN initialization failed."); return -1; } - ieee802_11_set_beacon(hapd); + if (!hapd->conf->start_disabled && ieee802_11_set_beacon(hapd) < 0) + return -1; if (hapd->wpa_auth && wpa_init_keys(hapd->wpa_auth) < 0) return -1; @@ -661,11 +839,154 @@ } +static int hostapd_set_acl_list(struct hostapd_data *hapd, + struct mac_acl_entry *mac_acl, + int n_entries, u8 accept_acl) +{ + struct hostapd_acl_params *acl_params; + int i, err; + + acl_params = os_zalloc(sizeof(*acl_params) + + (n_entries * sizeof(acl_params->mac_acl[0]))); + if (!acl_params) + return -ENOMEM; + + for (i = 0; i < n_entries; i++) + os_memcpy(acl_params->mac_acl[i].addr, mac_acl[i].addr, + ETH_ALEN); + + acl_params->acl_policy = accept_acl; + acl_params->num_mac_acl = n_entries; + + err = hostapd_drv_set_acl(hapd, acl_params); + + os_free(acl_params); + + return err; +} + + +static void hostapd_set_acl(struct hostapd_data *hapd) +{ + struct hostapd_config *conf = hapd->iconf; + int err; + u8 accept_acl; + + if (hapd->iface->drv_max_acl_mac_addrs == 0) + return; + if (!(conf->bss[0]->num_accept_mac || conf->bss[0]->num_deny_mac)) + return; + + if (conf->bss[0]->macaddr_acl == DENY_UNLESS_ACCEPTED) { + if (conf->bss[0]->num_accept_mac) { + accept_acl = 1; + err = hostapd_set_acl_list(hapd, + conf->bss[0]->accept_mac, + conf->bss[0]->num_accept_mac, + accept_acl); + if (err) { + wpa_printf(MSG_DEBUG, "Failed to set accept acl"); + return; + } + } else { + wpa_printf(MSG_DEBUG, "Mismatch between ACL Policy & Accept/deny lists file"); + } + } else if (conf->bss[0]->macaddr_acl == ACCEPT_UNLESS_DENIED) { + if (conf->bss[0]->num_deny_mac) { + accept_acl = 0; + err = hostapd_set_acl_list(hapd, conf->bss[0]->deny_mac, + conf->bss[0]->num_deny_mac, + accept_acl); + if (err) { + wpa_printf(MSG_DEBUG, "Failed to set deny acl"); + return; + } + } else { + wpa_printf(MSG_DEBUG, "Mismatch between ACL Policy & Accept/deny lists file"); + } + } +} + + +static int start_ctrl_iface_bss(struct hostapd_data *hapd) +{ + if (!hapd->iface->interfaces || + !hapd->iface->interfaces->ctrl_iface_init) + return 0; + + if (hapd->iface->interfaces->ctrl_iface_init(hapd)) { + wpa_printf(MSG_ERROR, + "Failed to setup control interface for %s", + hapd->conf->iface); + return -1; + } + + return 0; +} + + +static int start_ctrl_iface(struct hostapd_iface *iface) +{ + size_t i; + + if (!iface->interfaces || !iface->interfaces->ctrl_iface_init) + return 0; + + for (i = 0; i < iface->num_bss; i++) { + struct hostapd_data *hapd = iface->bss[i]; + if (iface->interfaces->ctrl_iface_init(hapd)) { + wpa_printf(MSG_ERROR, + "Failed to setup control interface for %s", + hapd->conf->iface); + return -1; + } + } + + return 0; +} + + +static void channel_list_update_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct hostapd_iface *iface = eloop_ctx; + + if (!iface->wait_channel_update) { + wpa_printf(MSG_INFO, "Channel list update timeout, but interface was not waiting for it"); + return; + } + + /* + * It is possible that the existing channel list is acceptable, so try + * to proceed. + */ + wpa_printf(MSG_DEBUG, "Channel list update timeout - try to continue anyway"); + setup_interface2(iface); +} + + +void hostapd_channel_list_updated(struct hostapd_iface *iface, int initiator) +{ + if (!iface->wait_channel_update || initiator != REGDOM_SET_BY_USER) + return; + + wpa_printf(MSG_DEBUG, "Channel list updated - continue setup"); + eloop_cancel_timeout(channel_list_update_timeout, iface, NULL); + setup_interface2(iface); +} + + static int setup_interface(struct hostapd_iface *iface) { struct hostapd_data *hapd = iface->bss[0]; size_t i; - char country[4]; + + if (!iface->phy[0]) { + const char *phy = hostapd_drv_get_radio_name(hapd); + if (phy) { + wpa_printf(MSG_DEBUG, "phy: %s", phy); + os_strlcpy(iface->phy, phy, sizeof(iface->phy)); + } + } /* * Make sure that all BSSes get configured with a pointer to the same @@ -679,15 +1000,49 @@ if (hostapd_validate_bssid_configuration(iface)) return -1; + /* + * Initialize control interfaces early to allow external monitoring of + * channel setup operations that may take considerable amount of time + * especially for DFS cases. + */ + if (start_ctrl_iface(iface)) + return -1; + if (hapd->iconf->country[0] && hapd->iconf->country[1]) { + char country[4], previous_country[4]; + + hostapd_set_state(iface, HAPD_IFACE_COUNTRY_UPDATE); + if (hostapd_get_country(hapd, previous_country) < 0) + previous_country[0] = '\0'; + os_memcpy(country, hapd->iconf->country, 3); country[3] = '\0'; if (hostapd_set_country(hapd, country) < 0) { wpa_printf(MSG_ERROR, "Failed to set country code"); return -1; } + + wpa_printf(MSG_DEBUG, "Previous country code %s, new country code %s", + previous_country, country); + + if (os_strncmp(previous_country, country, 2) != 0) { + wpa_printf(MSG_DEBUG, "Continue interface setup after channel list update"); + iface->wait_channel_update = 1; + eloop_register_timeout(5, 0, + channel_list_update_timeout, + iface, NULL); + return 0; + } } + return setup_interface2(iface); +} + + +static int setup_interface2(struct hostapd_iface *iface) +{ + iface->wait_channel_update = 0; + if (hostapd_get_hw_features(iface)) { /* Not all drivers support this yet, so continue without hw * feature data. */ @@ -698,6 +1053,10 @@ "channel. (%d)", ret); return -1; } + if (ret == 1) { + wpa_printf(MSG_DEBUG, "Interface initialization will be completed in a callback (ACS)"); + return 0; + } ret = hostapd_check_ht_capab(iface); if (ret < 0) return -1; @@ -706,11 +1065,22 @@ "be completed in a callback"); return 0; } + + if (iface->conf->ieee80211h) + wpa_printf(MSG_DEBUG, "DFS support is enabled"); } return hostapd_setup_interface_complete(iface, 0); } +/** + * 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 hostapd_data *hapd = iface->bss[0]; @@ -719,22 +1089,39 @@ if (err) { wpa_printf(MSG_ERROR, "Interface initialization failed"); - eloop_terminate(); + hostapd_set_state(iface, HAPD_IFACE_DISABLED); + if (iface->interfaces && iface->interfaces->terminate_on_error) + eloop_terminate(); return -1; } wpa_printf(MSG_DEBUG, "Completing interface initialization"); - if (hapd->iconf->channel) { - iface->freq = hostapd_hw_get_freq(hapd, hapd->iconf->channel); + if (iface->conf->channel) { +#ifdef NEED_AP_MLME + int res; +#endif /* NEED_AP_MLME */ + + iface->freq = hostapd_hw_get_freq(hapd, iface->conf->channel); wpa_printf(MSG_DEBUG, "Mode: %s Channel: %d " "Frequency: %d MHz", - hostapd_hw_mode_txt(hapd->iconf->hw_mode), - hapd->iconf->channel, iface->freq); + hostapd_hw_mode_txt(iface->conf->hw_mode), + iface->conf->channel, iface->freq); + +#ifdef NEED_AP_MLME + /* Check DFS */ + res = hostapd_handle_dfs(iface); + if (res <= 0) + return res; +#endif /* NEED_AP_MLME */ if (hostapd_set_freq(hapd, hapd->iconf->hw_mode, iface->freq, hapd->iconf->channel, hapd->iconf->ieee80211n, - hapd->iconf->secondary_channel)) { + hapd->iconf->ieee80211ac, + hapd->iconf->secondary_channel, + hapd->iconf->vht_oper_chwidth, + hapd->iconf->vht_oper_centr_freq_seg0_idx, + hapd->iconf->vht_oper_centr_freq_seg1_idx)) { wpa_printf(MSG_ERROR, "Could not set channel for " "kernel driver"); return -1; @@ -742,7 +1129,7 @@ } if (iface->current_mode) { - if (hostapd_prepare_rates(hapd, iface->current_mode)) { + if (hostapd_prepare_rates(iface, iface->current_mode)) { wpa_printf(MSG_ERROR, "Failed to prepare rates " "table."); hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211, @@ -777,11 +1164,14 @@ if (hostapd_mac_comp_empty(hapd->conf->bssid) == 0) prev_addr = hapd->own_addr; } + hapd = iface->bss[0]; hostapd_tx_queue_params(iface); ap_list_init(iface); + hostapd_set_acl(hapd); + if (hostapd_driver_commit(hapd) < 0) { wpa_printf(MSG_ERROR, "%s: Failed to commit driver " "configuration", __func__); @@ -799,11 +1189,15 @@ return -1; } + hostapd_set_state(iface, HAPD_IFACE_ENABLED); + 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); wpa_printf(MSG_DEBUG, "%s: Setup of interface done.", iface->bss[0]->conf->iface); + if (iface->interfaces && iface->interfaces->terminate_on_error > 0) + iface->interfaces->terminate_on_error--; return 0; } @@ -818,6 +1212,12 @@ * and sets driver parameters based on the configuration. * Flushes old stations, sets the channel, encryption, * beacons, and WDS links based on the configuration. + * + * If interface setup requires more time, e.g., to perform HT co-ex scans, ACS, + * or DFS operations, this function returns 0 before such operations have been + * completed. The pending operations are registered into eloop and will be + * completed from eloop callbacks. Those callbacks end up calling + * hostapd_setup_interface_complete() once setup has been completed. */ int hostapd_setup_interface(struct hostapd_iface *iface) { @@ -861,38 +1261,676 @@ hapd->conf = bss; hapd->iface = hapd_iface; hapd->driver = hapd->iconf->driver; + hapd->ctrl_sock = -1; return hapd; } +static void hostapd_bss_deinit(struct hostapd_data *hapd) +{ + wpa_printf(MSG_DEBUG, "%s: deinit bss %s", __func__, + hapd->conf->iface); + hostapd_free_stas(hapd); + hostapd_flush_old_stations(hapd, WLAN_REASON_DEAUTH_LEAVING); + hostapd_clear_wep(hapd); + hostapd_cleanup(hapd); +} + + void hostapd_interface_deinit(struct hostapd_iface *iface) { - size_t j; + int j; + wpa_printf(MSG_DEBUG, "%s(%p)", __func__, iface); if (iface == NULL) return; - hostapd_cleanup_iface_pre(iface); - for (j = 0; j < iface->num_bss; j++) { - struct hostapd_data *hapd = iface->bss[j]; - hostapd_free_stas(hapd); - hostapd_flush_old_stations(hapd, WLAN_REASON_DEAUTH_LEAVING); - hostapd_cleanup(hapd); - } + eloop_cancel_timeout(channel_list_update_timeout, iface, NULL); + iface->wait_channel_update = 0; + + for (j = iface->num_bss - 1; j >= 0; j--) + hostapd_bss_deinit(iface->bss[j]); } void hostapd_interface_free(struct hostapd_iface *iface) { size_t j; - for (j = 0; j < iface->num_bss; j++) + wpa_printf(MSG_DEBUG, "%s(%p)", __func__, iface); + for (j = 0; j < iface->num_bss; j++) { + wpa_printf(MSG_DEBUG, "%s: free hapd %p", + __func__, iface->bss[j]); os_free(iface->bss[j]); + } hostapd_cleanup_iface(iface); } /** + * hostapd_init - Allocate and initialize per-interface data + * @config_file: Path to the configuration file + * Returns: Pointer to the allocated interface data or %NULL on failure + * + * This function is used to allocate main data structures for per-interface + * data. The allocated data buffer will be freed by calling + * hostapd_cleanup_iface(). + */ +struct hostapd_iface * hostapd_init(struct hapd_interfaces *interfaces, + const char *config_file) +{ + struct hostapd_iface *hapd_iface = NULL; + struct hostapd_config *conf = NULL; + struct hostapd_data *hapd; + size_t i; + + hapd_iface = os_zalloc(sizeof(*hapd_iface)); + if (hapd_iface == NULL) + goto fail; + + hapd_iface->config_fname = os_strdup(config_file); + if (hapd_iface->config_fname == NULL) + goto fail; + + conf = interfaces->config_read_cb(hapd_iface->config_fname); + if (conf == NULL) + goto fail; + hapd_iface->conf = conf; + + hapd_iface->num_bss = conf->num_bss; + hapd_iface->bss = os_calloc(conf->num_bss, + sizeof(struct hostapd_data *)); + if (hapd_iface->bss == NULL) + goto fail; + + for (i = 0; i < conf->num_bss; i++) { + hapd = hapd_iface->bss[i] = + hostapd_alloc_bss_data(hapd_iface, conf, + conf->bss[i]); + if (hapd == NULL) + goto fail; + hapd->msg_ctx = hapd; + } + + return hapd_iface; + +fail: + wpa_printf(MSG_ERROR, "Failed to set up interface with %s", + config_file); + if (conf) + hostapd_config_free(conf); + if (hapd_iface) { + os_free(hapd_iface->config_fname); + os_free(hapd_iface->bss); + wpa_printf(MSG_DEBUG, "%s: free iface %p", + __func__, hapd_iface); + os_free(hapd_iface); + } + return NULL; +} + + +static int ifname_in_use(struct hapd_interfaces *interfaces, const char *ifname) +{ + size_t i, j; + + for (i = 0; i < interfaces->count; i++) { + struct hostapd_iface *iface = interfaces->iface[i]; + for (j = 0; j < iface->num_bss; j++) { + struct hostapd_data *hapd = iface->bss[j]; + if (os_strcmp(ifname, hapd->conf->iface) == 0) + return 1; + } + } + + return 0; +} + + +/** + * hostapd_interface_init_bss - Read configuration file and init BSS data + * + * This function is used to parse configuration file for a BSS. This BSS is + * added to an existing interface sharing the same radio (if any) or a new + * interface is created if this is the first interface on a radio. This + * allocate memory for the BSS. No actual driver operations are started. + * + * This is similar to hostapd_interface_init(), but for a case where the + * configuration is used to add a single BSS instead of all BSSes for a radio. + */ +struct hostapd_iface * +hostapd_interface_init_bss(struct hapd_interfaces *interfaces, const char *phy, + const char *config_fname, int debug) +{ + struct hostapd_iface *new_iface = NULL, *iface = NULL; + struct hostapd_data *hapd; + int k; + size_t i, bss_idx; + + if (!phy || !*phy) + return NULL; + + for (i = 0; i < interfaces->count; i++) { + if (os_strcmp(interfaces->iface[i]->phy, phy) == 0) { + iface = interfaces->iface[i]; + break; + } + } + + wpa_printf(MSG_INFO, "Configuration file: %s (phy %s)%s", + config_fname, phy, iface ? "" : " --> new PHY"); + if (iface) { + struct hostapd_config *conf; + struct hostapd_bss_config **tmp_conf; + struct hostapd_data **tmp_bss; + struct hostapd_bss_config *bss; + const char *ifname; + + /* Add new BSS to existing iface */ + conf = interfaces->config_read_cb(config_fname); + if (conf == NULL) + return NULL; + if (conf->num_bss > 1) { + wpa_printf(MSG_ERROR, "Multiple BSSes specified in BSS-config"); + hostapd_config_free(conf); + return NULL; + } + + ifname = conf->bss[0]->iface; + if (ifname[0] != '\0' && ifname_in_use(interfaces, ifname)) { + wpa_printf(MSG_ERROR, + "Interface name %s already in use", ifname); + hostapd_config_free(conf); + return NULL; + } + + tmp_conf = os_realloc_array( + iface->conf->bss, iface->conf->num_bss + 1, + sizeof(struct hostapd_bss_config *)); + tmp_bss = os_realloc_array(iface->bss, iface->num_bss + 1, + sizeof(struct hostapd_data *)); + if (tmp_bss) + iface->bss = tmp_bss; + if (tmp_conf) { + iface->conf->bss = tmp_conf; + iface->conf->last_bss = tmp_conf[0]; + } + if (tmp_bss == NULL || tmp_conf == NULL) { + hostapd_config_free(conf); + return NULL; + } + bss = iface->conf->bss[iface->conf->num_bss] = conf->bss[0]; + iface->conf->num_bss++; + + hapd = hostapd_alloc_bss_data(iface, iface->conf, bss); + if (hapd == NULL) { + iface->conf->num_bss--; + hostapd_config_free(conf); + return NULL; + } + iface->conf->last_bss = bss; + iface->bss[iface->num_bss] = hapd; + hapd->msg_ctx = hapd; + + bss_idx = iface->num_bss++; + conf->num_bss--; + conf->bss[0] = NULL; + hostapd_config_free(conf); + } else { + /* Add a new iface with the first BSS */ + new_iface = iface = hostapd_init(interfaces, config_fname); + if (!iface) + return NULL; + os_strlcpy(iface->phy, phy, sizeof(iface->phy)); + iface->interfaces = interfaces; + bss_idx = 0; + } + + for (k = 0; k < debug; k++) { + if (iface->bss[bss_idx]->conf->logger_stdout_level > 0) + iface->bss[bss_idx]->conf->logger_stdout_level--; + } + + if (iface->conf->bss[bss_idx]->iface[0] == '\0' && + !hostapd_drv_none(iface->bss[bss_idx])) { + wpa_printf(MSG_ERROR, "Interface name not specified in %s", + config_fname); + if (new_iface) + hostapd_interface_deinit_free(new_iface); + return NULL; + } + + return iface; +} + + +void hostapd_interface_deinit_free(struct hostapd_iface *iface) +{ + const struct wpa_driver_ops *driver; + void *drv_priv; + + wpa_printf(MSG_DEBUG, "%s(%p)", __func__, iface); + if (iface == NULL) + return; + wpa_printf(MSG_DEBUG, "%s: num_bss=%u conf->num_bss=%u", + __func__, (unsigned int) iface->num_bss, + (unsigned int) iface->conf->num_bss); + driver = iface->bss[0]->driver; + drv_priv = iface->bss[0]->drv_priv; + hostapd_interface_deinit(iface); + wpa_printf(MSG_DEBUG, "%s: driver=%p drv_priv=%p -> hapd_deinit", + __func__, driver, drv_priv); + if (driver && driver->hapd_deinit && drv_priv) + driver->hapd_deinit(drv_priv); + hostapd_interface_free(iface); +} + + +int hostapd_enable_iface(struct hostapd_iface *hapd_iface) +{ + if (hapd_iface->bss[0]->drv_priv != NULL) { + wpa_printf(MSG_ERROR, "Interface %s already enabled", + hapd_iface->conf->bss[0]->iface); + return -1; + } + + wpa_printf(MSG_DEBUG, "Enable interface %s", + hapd_iface->conf->bss[0]->iface); + + if (hostapd_config_check(hapd_iface->conf, 1) < 0) { + wpa_printf(MSG_INFO, "Invalid configuration - cannot enable"); + return -1; + } + + if (hapd_iface->interfaces == NULL || + hapd_iface->interfaces->driver_init == NULL || + hapd_iface->interfaces->driver_init(hapd_iface)) + return -1; + + if (hostapd_setup_interface(hapd_iface)) { + const struct wpa_driver_ops *driver; + void *drv_priv; + + driver = hapd_iface->bss[0]->driver; + drv_priv = hapd_iface->bss[0]->drv_priv; + wpa_printf(MSG_DEBUG, "%s: driver=%p drv_priv=%p -> hapd_deinit", + __func__, driver, drv_priv); + if (driver && driver->hapd_deinit && drv_priv) { + driver->hapd_deinit(drv_priv); + hapd_iface->bss[0]->drv_priv = NULL; + } + return -1; + } + + return 0; +} + + +int hostapd_reload_iface(struct hostapd_iface *hapd_iface) +{ + size_t j; + + wpa_printf(MSG_DEBUG, "Reload interface %s", + hapd_iface->conf->bss[0]->iface); + for (j = 0; j < hapd_iface->num_bss; j++) + hostapd_set_security_params(hapd_iface->conf->bss[j]); + if (hostapd_config_check(hapd_iface->conf, 1) < 0) { + wpa_printf(MSG_ERROR, "Updated configuration is invalid"); + return -1; + } + hostapd_clear_old(hapd_iface); + for (j = 0; j < hapd_iface->num_bss; j++) + hostapd_reload_bss(hapd_iface->bss[j]); + + return 0; +} + + +int hostapd_disable_iface(struct hostapd_iface *hapd_iface) +{ + size_t j; + const struct wpa_driver_ops *driver; + void *drv_priv; + + if (hapd_iface == NULL) + return -1; + wpa_msg(hapd_iface->bss[0]->msg_ctx, MSG_INFO, AP_EVENT_DISABLED); + driver = hapd_iface->bss[0]->driver; + drv_priv = hapd_iface->bss[0]->drv_priv; + + /* whatever hostapd_interface_deinit does */ + for (j = 0; j < hapd_iface->num_bss; j++) { + struct hostapd_data *hapd = hapd_iface->bss[j]; + hostapd_free_stas(hapd); + hostapd_flush_old_stations(hapd, WLAN_REASON_DEAUTH_LEAVING); + hostapd_clear_wep(hapd); + hostapd_free_hapd_data(hapd); + } + + wpa_printf(MSG_DEBUG, "%s: driver=%p drv_priv=%p -> hapd_deinit", + __func__, driver, drv_priv); + if (driver && driver->hapd_deinit && drv_priv) { + driver->hapd_deinit(drv_priv); + hapd_iface->bss[0]->drv_priv = NULL; + } + + /* From hostapd_cleanup_iface: These were initialized in + * hostapd_setup_interface and hostapd_setup_interface_complete + */ + hostapd_cleanup_iface_partial(hapd_iface); + + wpa_printf(MSG_DEBUG, "Interface %s disabled", + hapd_iface->bss[0]->conf->iface); + hostapd_set_state(hapd_iface, HAPD_IFACE_DISABLED); + return 0; +} + + +static struct hostapd_iface * +hostapd_iface_alloc(struct hapd_interfaces *interfaces) +{ + struct hostapd_iface **iface, *hapd_iface; + + iface = os_realloc_array(interfaces->iface, interfaces->count + 1, + sizeof(struct hostapd_iface *)); + if (iface == NULL) + return NULL; + interfaces->iface = iface; + hapd_iface = interfaces->iface[interfaces->count] = + os_zalloc(sizeof(*hapd_iface)); + if (hapd_iface == NULL) { + wpa_printf(MSG_ERROR, "%s: Failed to allocate memory for " + "the interface", __func__); + return NULL; + } + interfaces->count++; + hapd_iface->interfaces = interfaces; + + return hapd_iface; +} + + +static struct hostapd_config * +hostapd_config_alloc(struct hapd_interfaces *interfaces, const char *ifname, + const char *ctrl_iface) +{ + struct hostapd_bss_config *bss; + struct hostapd_config *conf; + + /* Allocates memory for bss and conf */ + conf = hostapd_config_defaults(); + if (conf == NULL) { + wpa_printf(MSG_ERROR, "%s: Failed to allocate memory for " + "configuration", __func__); + return NULL; + } + + conf->driver = wpa_drivers[0]; + if (conf->driver == NULL) { + wpa_printf(MSG_ERROR, "No driver wrappers registered!"); + hostapd_config_free(conf); + return NULL; + } + + bss = conf->last_bss = conf->bss[0]; + + os_strlcpy(bss->iface, ifname, sizeof(bss->iface)); + bss->ctrl_interface = os_strdup(ctrl_iface); + if (bss->ctrl_interface == NULL) { + hostapd_config_free(conf); + return NULL; + } + + /* Reading configuration file skipped, will be done in SET! + * From reading the configuration till the end has to be done in + * SET + */ + return conf; +} + + +static struct hostapd_iface * hostapd_data_alloc( + struct hapd_interfaces *interfaces, struct hostapd_config *conf) +{ + size_t i; + struct hostapd_iface *hapd_iface = + interfaces->iface[interfaces->count - 1]; + struct hostapd_data *hapd; + + hapd_iface->conf = conf; + hapd_iface->num_bss = conf->num_bss; + + hapd_iface->bss = os_zalloc(conf->num_bss * + sizeof(struct hostapd_data *)); + if (hapd_iface->bss == NULL) + return NULL; + + for (i = 0; i < conf->num_bss; i++) { + hapd = hapd_iface->bss[i] = + hostapd_alloc_bss_data(hapd_iface, conf, conf->bss[i]); + if (hapd == NULL) + return NULL; + hapd->msg_ctx = hapd; + } + + hapd_iface->interfaces = interfaces; + + return hapd_iface; +} + + +int hostapd_add_iface(struct hapd_interfaces *interfaces, char *buf) +{ + struct hostapd_config *conf = NULL; + struct hostapd_iface *hapd_iface = NULL, *new_iface = NULL; + struct hostapd_data *hapd; + char *ptr; + size_t i, j; + const char *conf_file = NULL, *phy_name = NULL; + + if (os_strncmp(buf, "bss_config=", 11) == 0) { + char *pos; + phy_name = buf + 11; + pos = os_strchr(phy_name, ':'); + if (!pos) + return -1; + *pos++ = '\0'; + conf_file = pos; + if (!os_strlen(conf_file)) + return -1; + + hapd_iface = hostapd_interface_init_bss(interfaces, phy_name, + conf_file, 0); + if (!hapd_iface) + return -1; + for (j = 0; j < interfaces->count; j++) { + if (interfaces->iface[j] == hapd_iface) + break; + } + if (j == interfaces->count) { + struct hostapd_iface **tmp; + tmp = os_realloc_array(interfaces->iface, + interfaces->count + 1, + sizeof(struct hostapd_iface *)); + if (!tmp) { + hostapd_interface_deinit_free(hapd_iface); + return -1; + } + interfaces->iface = tmp; + interfaces->iface[interfaces->count++] = hapd_iface; + new_iface = hapd_iface; + } + + if (new_iface) { + if (interfaces->driver_init(hapd_iface) || + hostapd_setup_interface(hapd_iface)) { + interfaces->count--; + goto fail; + } + } else { + /* Assign new BSS with bss[0]'s driver info */ + hapd = hapd_iface->bss[hapd_iface->num_bss - 1]; + hapd->driver = hapd_iface->bss[0]->driver; + hapd->drv_priv = hapd_iface->bss[0]->drv_priv; + os_memcpy(hapd->own_addr, hapd_iface->bss[0]->own_addr, + ETH_ALEN); + + if (start_ctrl_iface_bss(hapd) < 0 || + (hapd_iface->state == HAPD_IFACE_ENABLED && + hostapd_setup_bss(hapd, -1))) { + hapd_iface->conf->num_bss--; + hapd_iface->num_bss--; + wpa_printf(MSG_DEBUG, "%s: free hapd %p %s", + __func__, hapd, hapd->conf->iface); + os_free(hapd); + return -1; + } + } + return 0; + } + + ptr = os_strchr(buf, ' '); + if (ptr == NULL) + return -1; + *ptr++ = '\0'; + + if (os_strncmp(ptr, "config=", 7) == 0) + conf_file = ptr + 7; + + for (i = 0; i < interfaces->count; i++) { + if (!os_strcmp(interfaces->iface[i]->conf->bss[0]->iface, + buf)) { + wpa_printf(MSG_INFO, "Cannot add interface - it " + "already exists"); + return -1; + } + } + + hapd_iface = hostapd_iface_alloc(interfaces); + if (hapd_iface == NULL) { + wpa_printf(MSG_ERROR, "%s: Failed to allocate memory " + "for interface", __func__); + goto fail; + } + + if (conf_file && interfaces->config_read_cb) { + conf = interfaces->config_read_cb(conf_file); + if (conf && conf->bss) + os_strlcpy(conf->bss[0]->iface, buf, + sizeof(conf->bss[0]->iface)); + } else + conf = hostapd_config_alloc(interfaces, buf, ptr); + if (conf == NULL || conf->bss == NULL) { + wpa_printf(MSG_ERROR, "%s: Failed to allocate memory " + "for configuration", __func__); + goto fail; + } + + hapd_iface = hostapd_data_alloc(interfaces, conf); + if (hapd_iface == NULL) { + wpa_printf(MSG_ERROR, "%s: Failed to allocate memory " + "for hostapd", __func__); + goto fail; + } + + if (start_ctrl_iface(hapd_iface) < 0) + goto fail; + + wpa_printf(MSG_INFO, "Add interface '%s'", conf->bss[0]->iface); + + return 0; + +fail: + if (conf) + hostapd_config_free(conf); + if (hapd_iface) { + if (hapd_iface->bss) { + for (i = 0; i < hapd_iface->num_bss; i++) { + hapd = hapd_iface->bss[i]; + if (hapd && hapd_iface->interfaces && + hapd_iface->interfaces->ctrl_iface_deinit) + hapd_iface->interfaces-> + ctrl_iface_deinit(hapd); + wpa_printf(MSG_DEBUG, "%s: free hapd %p (%s)", + __func__, hapd_iface->bss[i], + hapd_iface->bss[i]->conf->iface); + os_free(hapd_iface->bss[i]); + } + os_free(hapd_iface->bss); + } + wpa_printf(MSG_DEBUG, "%s: free iface %p", + __func__, hapd_iface); + os_free(hapd_iface); + } + return -1; +} + + +static int hostapd_remove_bss(struct hostapd_iface *iface, unsigned int idx) +{ + size_t i; + + wpa_printf(MSG_INFO, "Remove BSS '%s'", iface->conf->bss[idx]->iface); + + /* Remove hostapd_data only if it has already been initialized */ + if (idx < iface->num_bss) { + struct hostapd_data *hapd = iface->bss[idx]; + + hostapd_bss_deinit(hapd); + wpa_printf(MSG_DEBUG, "%s: free hapd %p (%s)", + __func__, hapd, hapd->conf->iface); + hostapd_config_free_bss(hapd->conf); + os_free(hapd); + + iface->num_bss--; + + for (i = idx; i < iface->num_bss; i++) + iface->bss[i] = iface->bss[i + 1]; + } else { + hostapd_config_free_bss(iface->conf->bss[idx]); + iface->conf->bss[idx] = NULL; + } + + iface->conf->num_bss--; + for (i = idx; i < iface->conf->num_bss; i++) + iface->conf->bss[i] = iface->conf->bss[i + 1]; + + return 0; +} + + +int hostapd_remove_iface(struct hapd_interfaces *interfaces, char *buf) +{ + struct hostapd_iface *hapd_iface; + size_t i, j, k = 0; + + for (i = 0; i < interfaces->count; i++) { + hapd_iface = interfaces->iface[i]; + if (hapd_iface == NULL) + return -1; + if (!os_strcmp(hapd_iface->conf->bss[0]->iface, buf)) { + wpa_printf(MSG_INFO, "Remove interface '%s'", buf); + hostapd_interface_deinit_free(hapd_iface); + k = i; + while (k < (interfaces->count - 1)) { + interfaces->iface[k] = + interfaces->iface[k + 1]; + k++; + } + interfaces->count--; + return 0; + } + + for (j = 0; j < hapd_iface->conf->num_bss; j++) { + if (!os_strcmp(hapd_iface->conf->bss[j]->iface, buf)) + return hostapd_remove_bss(hapd_iface, j); + } + } + return -1; +} + + +/** * hostapd_new_assoc_sta - Notify that a new station associated with the AP * @hapd: Pointer to BSS data * @sta: Pointer to the associated STA data @@ -930,8 +1968,10 @@ /* Start accounting here, if IEEE 802.1X and WPA are not used. * IEEE 802.1X/WPA code will start accounting after the station has * been authorized. */ - if (!hapd->conf->ieee802_1x && !hapd->conf->wpa) + if (!hapd->conf->ieee802_1x && !hapd->conf->wpa) { + os_get_reltime(&sta->connected_time); accounting_sta_start(hapd, sta); + } /* Start IEEE 802.1X authentication process for new stations */ ieee802_1x_new_station(hapd, sta); @@ -941,4 +1981,275 @@ 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), + hapd->conf->ap_max_inactivity); + eloop_cancel_timeout(ap_handle_timer, hapd, sta); + eloop_register_timeout(hapd->conf->ap_max_inactivity, 0, + ap_handle_timer, hapd, sta); + } +} + + +const char * hostapd_state_text(enum hostapd_iface_state s) +{ + switch (s) { + case HAPD_IFACE_UNINITIALIZED: + return "UNINITIALIZED"; + case HAPD_IFACE_DISABLED: + return "DISABLED"; + case HAPD_IFACE_COUNTRY_UPDATE: + return "COUNTRY_UPDATE"; + case HAPD_IFACE_ACS: + return "ACS"; + case HAPD_IFACE_HT_SCAN: + return "HT_SCAN"; + case HAPD_IFACE_DFS: + return "DFS"; + case HAPD_IFACE_ENABLED: + return "ENABLED"; + } + + return "UNKNOWN"; } + + +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->state = s; +} + + +#ifdef NEED_AP_MLME + +static void free_beacon_data(struct beacon_data *beacon) +{ + os_free(beacon->head); + beacon->head = NULL; + os_free(beacon->tail); + beacon->tail = NULL; + os_free(beacon->probe_resp); + beacon->probe_resp = NULL; + os_free(beacon->beacon_ies); + beacon->beacon_ies = NULL; + os_free(beacon->proberesp_ies); + beacon->proberesp_ies = NULL; + os_free(beacon->assocresp_ies); + beacon->assocresp_ies = NULL; +} + + +static int hostapd_build_beacon_data(struct hostapd_iface *iface, + struct beacon_data *beacon) +{ + struct wpabuf *beacon_extra, *proberesp_extra, *assocresp_extra; + struct wpa_driver_ap_params params; + int ret; + struct hostapd_data *hapd = iface->bss[0]; + + os_memset(beacon, 0, sizeof(*beacon)); + ret = ieee802_11_build_ap_params(hapd, ¶ms); + if (ret < 0) + return ret; + + ret = hostapd_build_ap_extra_ies(hapd, &beacon_extra, + &proberesp_extra, + &assocresp_extra); + if (ret) + goto free_ap_params; + + ret = -1; + beacon->head = os_malloc(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); + 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); + 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)); + 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)); + 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)); + 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); + } + + ret = 0; +free_beacon: + /* if the function fails, the caller should not free beacon data */ + if (ret) + free_beacon_data(beacon); + +free_ap_extra_ies: + hostapd_free_ap_extra_ies(hapd, beacon_extra, proberesp_extra, + assocresp_extra); +free_ap_params: + ieee802_11_free_ap_params(¶ms); + return ret; +} + + +/* + * 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. + */ +static int hostapd_change_config_freq(struct hostapd_data *hapd, + struct hostapd_config *conf, + struct hostapd_freq_params *params, + struct hostapd_freq_params *old_params) +{ + int channel; + + if (!params->channel) { + /* check if the new channel is supported by hw */ + channel = hostapd_hw_get_channel(hapd, params->freq); + if (!channel) + return -1; + } else { + channel = params->channel; + } + + /* 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; + } + + conf->channel = channel; + conf->ieee80211n = params->ht_enabled; + conf->secondary_channel = params->sec_channel_offset; + + /* TODO: maybe call here hostapd_config_check here? */ + + return 0; +} + + +static int hostapd_fill_csa_settings(struct hostapd_iface *iface, + struct csa_settings *settings) +{ + struct hostapd_freq_params old_freq; + int ret; + + os_memset(&old_freq, 0, sizeof(old_freq)); + if (!iface || !iface->freq || iface->csa_in_progress) + return -1; + + ret = hostapd_change_config_freq(iface->bss[0], iface->conf, + &settings->freq_params, + &old_freq); + if (ret) + return ret; + + ret = hostapd_build_beacon_data(iface, &settings->beacon_after); + + /* change back the configuration */ + hostapd_change_config_freq(iface->bss[0], iface->conf, + &old_freq, NULL); + + if (ret) + return ret; + + /* set channel switch parameters for csa ie */ + iface->cs_freq_params = settings->freq_params; + iface->cs_count = settings->cs_count; + iface->cs_block_tx = settings->block_tx; + + ret = hostapd_build_beacon_data(iface, &settings->beacon_csa); + if (ret) { + free_beacon_data(&settings->beacon_after); + return ret; + } + + settings->counter_offset_beacon = iface->cs_c_off_beacon; + settings->counter_offset_presp = iface->cs_c_off_proberesp; + + return 0; +} + + +void hostapd_cleanup_cs_params(struct hostapd_data *hapd) +{ + os_memset(&hapd->iface->cs_freq_params, 0, + sizeof(hapd->iface->cs_freq_params)); + hapd->iface->cs_count = 0; + hapd->iface->cs_block_tx = 0; + hapd->iface->cs_c_off_beacon = 0; + hapd->iface->cs_c_off_proberesp = 0; + hapd->iface->csa_in_progress = 0; +} + + +int hostapd_switch_channel(struct hostapd_data *hapd, + struct csa_settings *settings) +{ + int ret; + ret = hostapd_fill_csa_settings(hapd->iface, settings); + if (ret) + return ret; + + ret = hostapd_drv_switch_channel(hapd, settings); + free_beacon_data(&settings->beacon_csa); + free_beacon_data(&settings->beacon_after); + + if (ret) { + /* if we failed, clean cs parameters */ + hostapd_cleanup_cs_params(hapd); + return ret; + } + + hapd->iface->csa_in_progress = 1; + return 0; +} + +#endif /* NEED_AP_MLME */ diff -Nru wpa-1.0/src/ap/hostapd.h wpa-2.1/src/ap/hostapd.h --- wpa-1.0/src/ap/hostapd.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/ap/hostapd.h 2014-02-04 11:23:35.000000000 +0000 @@ -1,38 +1,64 @@ /* * hostapd / Initialization and configuration - * Copyright (c) 2002-2009, Jouni Malinen + * Copyright (c) 2002-2013, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef HOSTAPD_H #define HOSTAPD_H #include "common/defs.h" +#include "ap_config.h" +#include "drivers/driver.h" -struct wpa_driver_ops; struct wpa_ctrl_dst; struct radius_server_data; struct upnp_wps_device_sm; -struct hapd_interfaces; struct hostapd_data; struct sta_info; -struct hostap_sta_driver_data; struct ieee80211_ht_capabilities; struct full_dynamic_vlan; enum wps_event; union wps_event_data; +struct hostapd_iface; +struct hostapd_dynamic_iface; + +struct hapd_interfaces { + int (*reload_config)(struct hostapd_iface *iface); + struct hostapd_config * (*config_read_cb)(const char *config_fname); + int (*ctrl_iface_init)(struct hostapd_data *hapd); + void (*ctrl_iface_deinit)(struct hostapd_data *hapd); + int (*for_each_interface)(struct hapd_interfaces *interfaces, + int (*cb)(struct hostapd_iface *iface, + void *ctx), void *ctx); + int (*driver_init)(struct hostapd_iface *iface); + + size_t count; + size_t count_dynamic; + int global_ctrl_sock; + char *global_iface_path; + char *global_iface_name; +#ifndef CONFIG_NATIVE_WINDOWS + gid_t ctrl_iface_group; +#endif /* CONFIG_NATIVE_WINDOWS */ + struct hostapd_iface **iface; + struct hostapd_dynamic_iface **dynamic_iface; + + size_t terminate_on_error; +}; + +enum hostapd_chan_status { + HOSTAPD_CHAN_VALID = 0, /* channel is ready */ + HOSTAPD_CHAN_INVALID = 1, /* no usable channel found */ + HOSTAPD_CHAN_ACS = 2, /* ACS work being performed */ +}; + struct hostapd_probereq_cb { int (*cb)(void *ctx, const u8 *sa, const u8 *da, const u8 *bssid, - const u8 *ie, size_t ie_len); + const u8 *ie, size_t ie_len, int ssi_signal); void *ctx; }; @@ -46,7 +72,26 @@ struct hostapd_frame_info { u32 channel; u32 datarate; - u32 ssi_signal; + int ssi_signal; /* dBm */ +}; + +enum wps_status { + WPS_STATUS_SUCCESS = 1, + WPS_STATUS_FAILURE +}; + +enum pbc_status { + WPS_PBC_STATUS_DISABLE, + WPS_PBC_STATUS_ACTIVE, + WPS_PBC_STATUS_TIMEOUT, + WPS_PBC_STATUS_OVERLAP +}; + +struct wps_stat { + enum wps_status status; + enum wps_error_indication failure_reason; + enum pbc_status pbc_status; + u8 peer_addr[ETH_ALEN]; }; @@ -58,6 +103,7 @@ struct hostapd_config *iconf; struct hostapd_bss_config *conf; int interface_added; /* virtual interface added for this BSS */ + unsigned int started:1; u8 own_addr[ETH_ALEN]; @@ -86,6 +132,7 @@ struct radius_client_data *radius; u32 acct_session_id_hi, acct_session_id_lo; + struct radius_das_data *radius_das; struct iapp_data *iapp; @@ -96,7 +143,7 @@ struct eapol_authenticator *eapol_auth; struct rsn_preauth_interface *preauth_iface; - time_t michael_mic_failure; + struct os_reltime michael_mic_failure; int michael_mic_failures; int tkip_countermeasures; @@ -128,6 +175,8 @@ unsigned int ap_pin_failures_consecutive; struct upnp_wps_device_sm *wps_upnp; unsigned int ap_pin_lockout_time; + + struct wps_stat wps_stats; #endif /* CONFIG_WPS */ struct hostapd_probereq_cb *probereq_cb; @@ -136,6 +185,9 @@ void (*public_action_cb)(void *ctx, const u8 *buf, size_t len, int freq); void *public_action_cb_ctx; + void (*public_action_cb2)(void *ctx, const u8 *buf, size_t len, + int freq); + void *public_action_cb2_ctx; int (*vendor_action_cb)(void *ctx, const u8 *buf, size_t len, int freq); @@ -156,6 +208,11 @@ void (*setup_complete_cb)(void *ctx); void *setup_complete_cb_ctx; + void (*new_psk_cb)(void *ctx, const u8 *mac_addr, + const u8 *p2p_dev_addr, const u8 *psk, + size_t psk_len); + void *new_psk_cb_ctx; + #ifdef CONFIG_P2P struct p2p_data *p2p; struct p2p_group *p2p_group; @@ -170,6 +227,23 @@ int noa_start; int noa_duration; #endif /* CONFIG_P2P */ +#ifdef CONFIG_INTERWORKING + size_t gas_frag_limit; +#endif /* CONFIG_INTERWORKING */ + +#ifdef CONFIG_SQLITE + struct hostapd_eap_user tmp_eap_user; +#endif /* CONFIG_SQLITE */ + +#ifdef CONFIG_SAE + /** Key used for generating SAE anti-clogging tokens */ + u8 sae_token_key[8]; + struct os_reltime last_sae_token_key_update; +#endif /* CONFIG_SAE */ + +#ifdef CONFIG_TESTING_OPTIONS + int ext_mgmt_frame_handling; +#endif /* CONFIG_TESTING_OPTIONS */ }; @@ -179,20 +253,44 @@ struct hostapd_iface { struct hapd_interfaces *interfaces; void *owner; - int (*reload_config)(struct hostapd_iface *iface); - struct hostapd_config * (*config_read_cb)(const char *config_fname); char *config_fname; struct hostapd_config *conf; + char phy[16]; /* Name of the PHY (radio) */ + + enum hostapd_iface_state { + HAPD_IFACE_UNINITIALIZED, + HAPD_IFACE_DISABLED, + HAPD_IFACE_COUNTRY_UPDATE, + HAPD_IFACE_ACS, + HAPD_IFACE_HT_SCAN, + HAPD_IFACE_DFS, + HAPD_IFACE_ENABLED + } state; size_t num_bss; struct hostapd_data **bss; + unsigned int wait_channel_update:1; + unsigned int cac_started: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]; - struct ap_info *ap_iter_list; unsigned int drv_flags; + + /* + * A bitmap of supported protocols for probe response offload. See + * struct wpa_driver_capa in driver.h + */ + unsigned int probe_resp_offloads; + + /* extended capabilities supported by the driver */ + const u8 *extended_capa, *extended_capa_mask; + unsigned int extended_capa_len; + + unsigned int drv_max_acl_mac_addrs; + struct hostapd_hw_modes *hw_features; int num_hw_features; struct hostapd_hw_modes *current_mode; @@ -200,6 +298,7 @@ * current_mode->channels */ int num_rates; struct hostapd_rate_data *current_rates; + int *basic_rates; int freq; u16 hw_flags; @@ -229,17 +328,44 @@ int olbc_ht; u16 ht_op_mode; - void (*scan_cb)(struct hostapd_iface *iface); - int (*ctrl_iface_init)(struct hostapd_data *hapd); - void (*ctrl_iface_deinit)(struct hostapd_data *hapd); + /* surveying helpers */ - int (*for_each_interface)(struct hapd_interfaces *interfaces, - int (*cb)(struct hostapd_iface *iface, - void *ctx), void *ctx); + /* number of channels surveyed */ + unsigned int chans_surveyed; + + /* lowest observed noise floor in dBm */ + s8 lowest_nf; + + /* channel switch parameters */ + struct hostapd_freq_params cs_freq_params; + u8 cs_count; + int cs_block_tx; + unsigned int cs_c_off_beacon; + unsigned int cs_c_off_proberesp; + int csa_in_progress; + +#ifdef CONFIG_ACS + unsigned int acs_num_completed_scans; +#endif /* CONFIG_ACS */ + + void (*scan_cb)(struct hostapd_iface *iface); +}; + +/** + * struct hostapd_dynamic_iface - hostapd per dynamically allocated + * or added interface data structure + */ +struct hostapd_dynamic_iface { + char parent[IFNAMSIZ + 1]; + char iface[IFNAMSIZ + 1]; + unsigned int usage; }; /* hostapd.c */ +int hostapd_for_each_interface(struct hapd_interfaces *interfaces, + int (*cb)(struct hostapd_iface *iface, + void *ctx), void *ctx); int hostapd_reload_config(struct hostapd_iface *iface); struct hostapd_data * hostapd_alloc_bss_data(struct hostapd_iface *hapd_iface, @@ -249,14 +375,32 @@ 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_init(struct hapd_interfaces *interfaces, + const char *config_file); +struct hostapd_iface * +hostapd_interface_init_bss(struct hapd_interfaces *interfaces, const char *phy, + const char *config_fname, int debug); void hostapd_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta, int reassoc); +void hostapd_interface_deinit_free(struct hostapd_iface *iface); +int hostapd_enable_iface(struct hostapd_iface *hapd_iface); +int hostapd_reload_iface(struct hostapd_iface *hapd_iface); +int hostapd_disable_iface(struct hostapd_iface *hapd_iface); +int hostapd_add_iface(struct hapd_interfaces *ifaces, char *buf); +int hostapd_remove_iface(struct hapd_interfaces *ifaces, char *buf); +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_switch_channel(struct hostapd_data *hapd, + struct csa_settings *settings); +void hostapd_cleanup_cs_params(struct hostapd_data *hapd); /* utils.c */ int hostapd_register_probereq_cb(struct hostapd_data *hapd, int (*cb)(void *ctx, const u8 *sa, const u8 *da, const u8 *bssid, - const u8 *ie, size_t ie_len), + const u8 *ie, size_t ie_len, + int ssi_signal), void *ctx); void hostapd_prune_associations(struct hostapd_data *hapd, const u8 *addr); @@ -265,7 +409,16 @@ const u8 *ie, size_t ielen, int reassoc); void hostapd_notif_disassoc(struct hostapd_data *hapd, const u8 *addr); void hostapd_event_sta_low_ack(struct hostapd_data *hapd, const u8 *addr); +void hostapd_event_connect_failed_reason(struct hostapd_data *hapd, + const u8 *addr, int reason_code); int hostapd_probe_req_rx(struct hostapd_data *hapd, const u8 *sa, const u8 *da, - const u8 *bssid, const u8 *ie, size_t ie_len); + const u8 *bssid, const u8 *ie, size_t ie_len, + int ssi_signal); +void hostapd_event_ch_switch(struct hostapd_data *hapd, int freq, int ht, + int offset, int width, int cf1, int cf2); + +const struct hostapd_eap_user * +hostapd_get_eap_user(struct hostapd_data *hapd, const u8 *identity, + size_t identity_len, int phase2); #endif /* HOSTAPD_H */ diff -Nru wpa-1.0/src/ap/hs20.c wpa-2.1/src/ap/hs20.c --- wpa-1.0/src/ap/hs20.c 1970-01-01 00:00:00.000000000 +0000 +++ wpa-2.1/src/ap/hs20.c 2014-02-04 11:23:35.000000000 +0000 @@ -0,0 +1,31 @@ +/* + * Hotspot 2.0 AP ANQP processing + * Copyright (c) 2009, Atheros Communications, Inc. + * Copyright (c) 2011-2012, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "common/ieee802_11_defs.h" +#include "hostapd.h" +#include "ap_config.h" +#include "hs20.h" + + +u8 * hostapd_eid_hs20_indication(struct hostapd_data *hapd, u8 *eid) +{ + if (!hapd->conf->hs20) + return eid; + *eid++ = WLAN_EID_VENDOR_SPECIFIC; + *eid++ = 5; + WPA_PUT_BE24(eid, OUI_WFA); + eid += 3; + *eid++ = HS20_INDICATION_OUI_TYPE; + /* Hotspot Configuration: DGAF Enabled */ + *eid++ = hapd->conf->disable_dgaf ? 0x01 : 0x00; + return eid; +} diff -Nru wpa-1.0/src/ap/hs20.h wpa-2.1/src/ap/hs20.h --- wpa-1.0/src/ap/hs20.h 1970-01-01 00:00:00.000000000 +0000 +++ wpa-2.1/src/ap/hs20.h 2014-02-04 11:23:35.000000000 +0000 @@ -0,0 +1,16 @@ +/* + * Hotspot 2.0 AP ANQP processing + * Copyright (c) 2011-2012, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef HS20_H +#define HS20_H + +struct hostapd_data; + +u8 * hostapd_eid_hs20_indication(struct hostapd_data *hapd, u8 *eid); + +#endif /* HS20_H */ diff -Nru wpa-1.0/src/ap/hw_features.c wpa-2.1/src/ap/hw_features.c --- wpa-1.0/src/ap/hw_features.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/ap/hw_features.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,16 +2,10 @@ * hostapd / Hardware feature query and different modes * Copyright 2002-2003, Instant802 Networks, Inc. * Copyright 2005-2006, Devicescape Software, Inc. - * Copyright (c) 2008-2009, Jouni Malinen + * Copyright (c) 2008-2012, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "utils/includes.h" @@ -20,10 +14,11 @@ #include "utils/eloop.h" #include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" -#include "drivers/driver.h" +#include "common/wpa_ctrl.h" #include "hostapd.h" #include "ap_config.h" #include "ap_drv_ops.h" +#include "acs.h" #include "hw_features.h" @@ -44,6 +39,36 @@ } +#ifndef CONFIG_NO_STDOUT_DEBUG +static char * dfs_info(struct hostapd_channel_data *chan) +{ + static char info[256]; + char *state; + + switch (chan->flag & HOSTAPD_CHAN_DFS_MASK) { + case HOSTAPD_CHAN_DFS_UNKNOWN: + state = "unknown"; + break; + case HOSTAPD_CHAN_DFS_USABLE: + state = "usable"; + break; + case HOSTAPD_CHAN_DFS_UNAVAILABLE: + state = "unavailable"; + break; + case HOSTAPD_CHAN_DFS_AVAILABLE: + state = "available"; + break; + default: + return ""; + } + os_snprintf(info, sizeof(info), " (DFS state = %s)", state); + info[sizeof(info) - 1] = '\0'; + + return info; +} +#endif /* CONFIG_NO_STDOUT_DEBUG */ + + int hostapd_get_hw_features(struct hostapd_iface *iface) { struct hostapd_data *hapd = iface->bss[0]; @@ -70,30 +95,40 @@ for (i = 0; i < num_modes; i++) { struct hostapd_hw_modes *feature = &modes[i]; + int dfs_enabled = hapd->iconf->ieee80211h && + (iface->drv_flags & WPA_DRIVER_FLAGS_RADAR); + /* set flag for channels we can use in current regulatory * domain */ for (j = 0; j < feature->num_channels; j++) { + int dfs = 0; + /* * Disable all channels that are marked not to allow - * IBSS operation or active scanning. In addition, - * disable all channels that require radar detection, - * since that (in addition to full DFS) is not yet - * supported. + * IBSS operation or active scanning. + * Use radar channels only if the driver supports DFS. */ - if (feature->channels[j].flag & - (HOSTAPD_CHAN_NO_IBSS | - HOSTAPD_CHAN_PASSIVE_SCAN | - HOSTAPD_CHAN_RADAR)) + if ((feature->channels[j].flag & + HOSTAPD_CHAN_RADAR) && dfs_enabled) { + dfs = 1; + } else if (feature->channels[j].flag & + (HOSTAPD_CHAN_NO_IBSS | + HOSTAPD_CHAN_PASSIVE_SCAN | + HOSTAPD_CHAN_RADAR)) { feature->channels[j].flag |= HOSTAPD_CHAN_DISABLED; + } + if (feature->channels[j].flag & HOSTAPD_CHAN_DISABLED) continue; + wpa_printf(MSG_MSGDUMP, "Allowed channel: mode=%d " - "chan=%d freq=%d MHz max_tx_power=%d dBm", + "chan=%d freq=%d MHz max_tx_power=%d dBm%s", feature->mode, feature->channels[j].chan, feature->channels[j].freq, - feature->channels[j].max_tx_power); + feature->channels[j].max_tx_power, + dfs ? dfs_info(&feature->channels[j]) : ""); } } @@ -101,7 +136,7 @@ } -int hostapd_prepare_rates(struct hostapd_data *hapd, +int hostapd_prepare_rates(struct hostapd_iface *iface, struct hostapd_hw_modes *mode) { int i, num_basic_rates = 0; @@ -110,8 +145,8 @@ int basic_rates_g[] = { 10, 20, 55, 110, -1 }; int *basic_rates; - if (hapd->iconf->basic_rates) - basic_rates = hapd->iconf->basic_rates; + if (iface->conf->basic_rates) + basic_rates = iface->conf->basic_rates; else switch (mode->mode) { case HOSTAPD_MODE_IEEE80211A: basic_rates = basic_rates_a; @@ -122,22 +157,28 @@ case HOSTAPD_MODE_IEEE80211G: basic_rates = basic_rates_g; break; + case HOSTAPD_MODE_IEEE80211AD: + return 0; /* No basic rates for 11ad */ default: return -1; } - if (hostapd_set_rate_sets(hapd, hapd->iconf->supported_rates, - basic_rates, mode->mode)) { - wpa_printf(MSG_ERROR, "Failed to update rate sets in kernel " - "module"); - } - - os_free(hapd->iface->current_rates); - hapd->iface->num_rates = 0; - - hapd->iface->current_rates = - os_zalloc(mode->num_rates * sizeof(struct hostapd_rate_data)); - if (!hapd->iface->current_rates) { + i = 0; + while (basic_rates[i] >= 0) + i++; + if (i) + i++; /* -1 termination */ + os_free(iface->basic_rates); + iface->basic_rates = os_malloc(i * sizeof(int)); + if (iface->basic_rates) + os_memcpy(iface->basic_rates, basic_rates, i * sizeof(int)); + + os_free(iface->current_rates); + iface->num_rates = 0; + + iface->current_rates = + os_calloc(mode->num_rates, sizeof(struct hostapd_rate_data)); + if (!iface->current_rates) { wpa_printf(MSG_ERROR, "Failed to allocate memory for rate " "table."); return -1; @@ -146,27 +187,27 @@ for (i = 0; i < mode->num_rates; i++) { struct hostapd_rate_data *rate; - if (hapd->iconf->supported_rates && - !hostapd_rate_found(hapd->iconf->supported_rates, + if (iface->conf->supported_rates && + !hostapd_rate_found(iface->conf->supported_rates, mode->rates[i])) continue; - rate = &hapd->iface->current_rates[hapd->iface->num_rates]; + rate = &iface->current_rates[iface->num_rates]; rate->rate = mode->rates[i]; if (hostapd_rate_found(basic_rates, rate->rate)) { rate->flags |= HOSTAPD_RATE_BASIC; num_basic_rates++; } wpa_printf(MSG_DEBUG, "RATE[%d] rate=%d flags=0x%x", - hapd->iface->num_rates, rate->rate, rate->flags); - hapd->iface->num_rates++; + iface->num_rates, rate->rate, rate->flags); + iface->num_rates++; } - if ((hapd->iface->num_rates == 0 || num_basic_rates == 0) && - (!hapd->iconf->ieee80211n || !hapd->iconf->require_ht)) { + if ((iface->num_rates == 0 || num_basic_rates == 0) && + (!iface->conf->ieee80211n || !iface->conf->require_ht)) { wpa_printf(MSG_ERROR, "No rates remaining in supported/basic " "rate sets (%d,%d).", - hapd->iface->num_rates, num_basic_rates); + iface->num_rates, num_basic_rates); return -1; } @@ -223,7 +264,7 @@ first = sec_chan; ok = 0; - for (k = 0; k < sizeof(allowed) / sizeof(allowed[0]); k++) { + for (k = 0; k < ARRAY_SIZE(allowed); k++) { if (first == allowed[k]) { ok = 1; break; @@ -414,7 +455,7 @@ int res; /* Check list of neighboring BSSes (from scan) to see whether 40 MHz is - * allowed per IEEE 802.11n/D7.0, 11.14.3.2 */ + * allowed per IEEE Std 802.11-2012, 10.15.3.2 */ iface->scan_cb = NULL; @@ -437,7 +478,6 @@ iface->conf->channel + iface->conf->secondary_channel * 4); iface->conf->secondary_channel = 0; - iface->conf->ht_capab &= ~HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET; } res = ieee80211n_allowed_ht40_channel_pair(iface); @@ -445,6 +485,87 @@ } +static void ieee80211n_scan_channels_2g4(struct hostapd_iface *iface, + struct wpa_driver_scan_params *params) +{ + /* Scan only the affected frequency range */ + int pri_freq, sec_freq; + int affected_start, affected_end; + int i, pos; + struct hostapd_hw_modes *mode; + + if (iface->current_mode == NULL) + return; + + pri_freq = hostapd_hw_get_freq(iface->bss[0], iface->conf->channel); + if (iface->conf->secondary_channel > 0) + sec_freq = pri_freq + 20; + else + sec_freq = pri_freq - 20; + affected_start = (pri_freq + sec_freq) / 2 - 25; + affected_end = (pri_freq + sec_freq) / 2 + 25; + wpa_printf(MSG_DEBUG, "40 MHz affected channel range: [%d,%d] MHz", + affected_start, affected_end); + + mode = iface->current_mode; + params->freqs = os_calloc(mode->num_channels + 1, sizeof(int)); + if (params->freqs == NULL) + return; + pos = 0; + + for (i = 0; i < mode->num_channels; i++) { + struct hostapd_channel_data *chan = &mode->channels[i]; + if (chan->flag & HOSTAPD_CHAN_DISABLED) + continue; + if (chan->freq < affected_start || + chan->freq > affected_end) + continue; + params->freqs[pos++] = chan->freq; + } +} + + +static void ieee80211n_scan_channels_5g(struct hostapd_iface *iface, + struct wpa_driver_scan_params *params) +{ + /* Scan only the affected frequency range */ + int pri_freq; + int affected_start, affected_end; + int i, pos; + struct hostapd_hw_modes *mode; + + if (iface->current_mode == NULL) + return; + + pri_freq = hostapd_hw_get_freq(iface->bss[0], iface->conf->channel); + if (iface->conf->secondary_channel > 0) { + affected_start = pri_freq - 10; + affected_end = pri_freq + 30; + } else { + affected_start = pri_freq - 30; + affected_end = pri_freq + 10; + } + wpa_printf(MSG_DEBUG, "40 MHz affected channel range: [%d,%d] MHz", + affected_start, affected_end); + + mode = iface->current_mode; + params->freqs = os_calloc(mode->num_channels + 1, sizeof(int)); + if (params->freqs == NULL) + return; + pos = 0; + + for (i = 0; i < mode->num_channels; i++) { + struct hostapd_channel_data *chan = &mode->channels[i]; + if (chan->flag & HOSTAPD_CHAN_DISABLED) + continue; + if (chan->freq < affected_start || + chan->freq > affected_end) + continue; + params->freqs[pos++] = chan->freq; + } +} + + static int ieee80211n_check_40mhz(struct hostapd_iface *iface) { struct wpa_driver_scan_params params; @@ -452,15 +573,21 @@ if (!iface->conf->secondary_channel) return 0; /* HT40 not used */ + hostapd_set_state(iface, HAPD_IFACE_HT_SCAN); wpa_printf(MSG_DEBUG, "Scan for neighboring BSSes prior to enabling " "40 MHz channel"); os_memset(¶ms, 0, sizeof(params)); - /* TODO: scan only the needed frequency */ + if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G) + ieee80211n_scan_channels_2g4(iface, ¶ms); + else + ieee80211n_scan_channels_5g(iface, ¶ms); if (hostapd_driver_scan(iface->bss[0], ¶ms) < 0) { wpa_printf(MSG_ERROR, "Failed to request a scan of " "neighboring BSSes"); + os_free(params.freqs); return -1; } + os_free(params.freqs); iface->scan_cb = ieee80211n_check_scan; return 1; @@ -564,6 +691,92 @@ return 1; } + +#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 cap, + const char *name) +{ + u32 hw_max = hw & cap; + u32 conf_val = conf & cap; + + if (conf_val > hw_max) { + int offset = find_first_bit(cap); + wpa_printf(MSG_ERROR, "Configured VHT capability [%s] exceeds max value supported by the driver (%d > %d)", + name, conf_val >> offset, hw_max >> offset); + return 0; + } + return 1; +} + + +static int ieee80211ac_supported_vht_capab(struct hostapd_iface *iface) +{ + u32 hw = iface->current_mode->vht_capab; + u32 conf = iface->conf->vht_capab; + + wpa_printf(MSG_DEBUG, "hw vht capab: 0x%x, conf vht capab: 0x%x", + hw, conf); + +#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)) \ + 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); + 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; +} +#endif /* CONFIG_IEEE80211AC */ + #endif /* CONFIG_IEEE80211N */ @@ -575,6 +788,10 @@ return 0; if (!ieee80211n_supported_ht_capab(iface)) return -1; +#ifdef CONFIG_IEEE80211AC + if (!ieee80211ac_supported_vht_capab(iface)) + return -1; +#endif /* CONFIG_IEEE80211AC */ ret = ieee80211n_check_40mhz(iface); if (ret) return ret; @@ -586,6 +803,131 @@ } +static int hostapd_is_usable_chan(struct hostapd_iface *iface, + int channel, int primary) +{ + int i; + struct hostapd_channel_data *chan; + + for (i = 0; i < iface->current_mode->num_channels; i++) { + chan = &iface->current_mode->channels[i]; + if (chan->chan != channel) + continue; + + if (!(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%s", + primary ? "" : "Configured HT40 secondary ", + i, chan->chan, chan->flag, + chan->flag & HOSTAPD_CHAN_NO_IBSS ? " NO-IBSS" : "", + chan->flag & HOSTAPD_CHAN_PASSIVE_SCAN ? + " PASSIVE-SCAN" : "", + chan->flag & HOSTAPD_CHAN_RADAR ? " RADAR" : ""); + } + + return 0; +} + + +static int hostapd_is_usable_chans(struct hostapd_iface *iface) +{ + if (!hostapd_is_usable_chan(iface, iface->conf->channel, 1)) + return 0; + + if (!iface->conf->secondary_channel) + return 1; + + return hostapd_is_usable_chan(iface, iface->conf->channel + + iface->conf->secondary_channel * 4, 0); +} + + +static enum hostapd_chan_status +hostapd_check_chans(struct hostapd_iface *iface) +{ + if (iface->conf->channel) { + if (hostapd_is_usable_chans(iface)) + return HOSTAPD_CHAN_VALID; + else + return HOSTAPD_CHAN_INVALID; + } + + /* + * The user set channel=0 or channel=acs_survey + * which is used to trigger ACS. + */ + + switch (acs_init(iface)) { + case HOSTAPD_CHAN_ACS: + return HOSTAPD_CHAN_ACS; + case HOSTAPD_CHAN_VALID: + case HOSTAPD_CHAN_INVALID: + default: + return HOSTAPD_CHAN_INVALID; + } +} + + +static void hostapd_notify_bad_chans(struct hostapd_iface *iface) +{ + hostapd_logger(iface->bss[0], NULL, + HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_WARNING, + "Configured channel (%d) not found from the " + "channel list of current mode (%d) %s", + iface->conf->channel, + iface->current_mode->mode, + hostapd_hw_mode_txt(iface->current_mode->mode)); + hostapd_logger(iface->bss[0], NULL, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_WARNING, + "Hardware does not support configured channel"); +} + + +int hostapd_acs_completed(struct hostapd_iface *iface, int err) +{ + int ret = -1; + + if (err) + goto out; + + switch (hostapd_check_chans(iface)) { + case HOSTAPD_CHAN_VALID: + wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, + ACS_EVENT_COMPLETED "freq=%d channel=%d", + hostapd_hw_get_freq(iface->bss[0], + iface->conf->channel), + iface->conf->channel); + break; + case HOSTAPD_CHAN_ACS: + wpa_printf(MSG_ERROR, "ACS error - reported complete, but no result available"); + wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, ACS_EVENT_FAILED); + hostapd_notify_bad_chans(iface); + goto out; + case HOSTAPD_CHAN_INVALID: + default: + wpa_printf(MSG_ERROR, "ACS picked unusable channels"); + wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, ACS_EVENT_FAILED); + hostapd_notify_bad_chans(iface); + goto out; + } + + ret = hostapd_check_ht_capab(iface); + if (ret < 0) + goto out; + if (ret == 1) { + wpa_printf(MSG_DEBUG, "Interface initialization will be completed in a callback"); + return 0; + } + + ret = 0; +out: + return hostapd_setup_interface_complete(iface, ret); +} + + /** * hostapd_select_hw_mode - Select the hardware mode * @iface: Pointer to interface data. @@ -596,7 +938,7 @@ */ int hostapd_select_hw_mode(struct hostapd_iface *iface) { - int i, j, ok; + int i; if (iface->num_hw_features < 1) return -1; @@ -621,73 +963,16 @@ return -2; } - ok = 0; - for (j = 0; j < iface->current_mode->num_channels; j++) { - struct hostapd_channel_data *chan = - &iface->current_mode->channels[j]; - if (chan->chan == iface->conf->channel) { - if (chan->flag & HOSTAPD_CHAN_DISABLED) { - wpa_printf(MSG_ERROR, - "channel [%i] (%i) is disabled for " - "use in AP mode, flags: 0x%x", - j, chan->chan, chan->flag); - } else { - ok = 1; - break; - } - } - } - if (ok && iface->conf->secondary_channel) { - int sec_ok = 0; - int sec_chan = iface->conf->channel + - iface->conf->secondary_channel * 4; - for (j = 0; j < iface->current_mode->num_channels; j++) { - struct hostapd_channel_data *chan = - &iface->current_mode->channels[j]; - if (!(chan->flag & HOSTAPD_CHAN_DISABLED) && - (chan->chan == sec_chan)) { - sec_ok = 1; - break; - } - } - if (!sec_ok) { - hostapd_logger(iface->bss[0], NULL, - HOSTAPD_MODULE_IEEE80211, - HOSTAPD_LEVEL_WARNING, - "Configured HT40 secondary channel " - "(%d) not found from the channel list " - "of current mode (%d) %s", - sec_chan, iface->current_mode->mode, - hostapd_hw_mode_txt( - iface->current_mode->mode)); - ok = 0; - } - } - if (iface->conf->channel == 0) { - /* TODO: could request a scan of neighboring BSSes and select - * the channel automatically */ - wpa_printf(MSG_ERROR, "Channel not configured " - "(hw_mode/channel in hostapd.conf)"); + switch (hostapd_check_chans(iface)) { + case HOSTAPD_CHAN_VALID: + return 0; + case HOSTAPD_CHAN_ACS: /* ACS will run and later complete */ + return 1; + case HOSTAPD_CHAN_INVALID: + default: + hostapd_notify_bad_chans(iface); return -3; } - if (ok == 0 && iface->conf->channel != 0) { - hostapd_logger(iface->bss[0], NULL, - HOSTAPD_MODULE_IEEE80211, - HOSTAPD_LEVEL_WARNING, - "Configured channel (%d) not found from the " - "channel list of current mode (%d) %s", - iface->conf->channel, - iface->current_mode->mode, - hostapd_hw_mode_txt(iface->current_mode->mode)); - iface->current_mode = NULL; - } - - if (iface->current_mode == NULL) { - hostapd_logger(iface->bss[0], NULL, HOSTAPD_MODULE_IEEE80211, - HOSTAPD_LEVEL_WARNING, - "Hardware does not support configured channel"); - return -4; - } return 0; } @@ -702,6 +987,8 @@ return "IEEE 802.11b"; case HOSTAPD_MODE_IEEE80211G: return "IEEE 802.11g"; + case HOSTAPD_MODE_IEEE80211AD: + return "IEEE 802.11ad"; default: return "UNKNOWN"; } diff -Nru wpa-1.0/src/ap/hw_features.h wpa-2.1/src/ap/hw_features.h --- wpa-1.0/src/ap/hw_features.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/ap/hw_features.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,15 +2,10 @@ * hostapd / Hardware feature query and different modes * Copyright 2002-2003, Instant802 Networks, Inc. * Copyright 2005-2006, Devicescape Software, Inc. + * Copyright (c) 2008-2011, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef HW_FEATURES_H @@ -20,12 +15,13 @@ void hostapd_free_hw_features(struct hostapd_hw_modes *hw_features, size_t num_hw_features); int hostapd_get_hw_features(struct hostapd_iface *iface); +int hostapd_acs_completed(struct hostapd_iface *iface, int err); int hostapd_select_hw_mode(struct hostapd_iface *iface); const char * hostapd_hw_mode_txt(int mode); int hostapd_hw_get_freq(struct hostapd_data *hapd, int chan); int hostapd_hw_get_channel(struct hostapd_data *hapd, int freq); int hostapd_check_ht_capab(struct hostapd_iface *iface); -int hostapd_prepare_rates(struct hostapd_data *hapd, +int hostapd_prepare_rates(struct hostapd_iface *iface, struct hostapd_hw_modes *mode); #else /* NEED_AP_MLME */ static inline void @@ -59,7 +55,7 @@ return 0; } -static inline int hostapd_prepare_rates(struct hostapd_data *hapd, +static inline int hostapd_prepare_rates(struct hostapd_iface *iface, struct hostapd_hw_modes *mode) { return 0; diff -Nru wpa-1.0/src/ap/iapp.c wpa-2.1/src/ap/iapp.c --- wpa-1.0/src/ap/iapp.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/ap/iapp.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * hostapd / IEEE 802.11F-2003 Inter-Access Point Protocol (IAPP) * Copyright (c) 2002-2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. * * Note: IEEE 802.11F-2003 was a experimental use specification. It has expired * and IEEE has withdrawn it. In other words, it is likely better to look at @@ -210,7 +204,7 @@ addr.sin_port = htons(IAPP_UDP_PORT); if (sendto(iapp->udp_sock, buf, (char *) (add + 1) - buf, 0, (struct sockaddr *) &addr, sizeof(addr)) < 0) - perror("sendto[IAPP-ADD]"); + wpa_printf(MSG_INFO, "sendto[IAPP-ADD]: %s", strerror(errno)); } @@ -237,7 +231,7 @@ * FIX: what is correct RW with 802.11? */ if (send(iapp->packet_sock, &msg, sizeof(msg), 0) < 0) - perror("send[L2 Update]"); + wpa_printf(MSG_INFO, "send[L2 Update]: %s", strerror(errno)); } @@ -282,8 +276,8 @@ struct sta_info *sta; if (len != sizeof(*add)) { - printf("Invalid IAPP-ADD packet length %d (expected %lu)\n", - len, (unsigned long) sizeof(*add)); + wpa_printf(MSG_INFO, "Invalid IAPP-ADD packet length %d (expected %lu)", + len, (unsigned long) sizeof(*add)); return; } @@ -332,7 +326,8 @@ len = recvfrom(iapp->udp_sock, buf, sizeof(buf), 0, (struct sockaddr *) &from, &fromlen); if (len < 0) { - perror("recvfrom"); + wpa_printf(MSG_INFO, "iapp_receive_udp - recvfrom: %s", + strerror(errno)); return; } @@ -356,17 +351,18 @@ hdr->version, hdr->command, be_to_host16(hdr->identifier), hlen); if (hdr->version != IAPP_VERSION) { - printf("Dropping IAPP frame with unknown version %d\n", - hdr->version); + wpa_printf(MSG_INFO, "Dropping IAPP frame with unknown version %d", + hdr->version); return; } if (hlen > len) { - printf("Underflow IAPP frame (hlen=%d len=%d)\n", hlen, len); + wpa_printf(MSG_INFO, "Underflow IAPP frame (hlen=%d len=%d)", + hlen, len); return; } if (hlen < len) { - printf("Ignoring %d extra bytes from IAPP frame\n", - len - hlen); + wpa_printf(MSG_INFO, "Ignoring %d extra bytes from IAPP frame", + len - hlen); len = hlen; } @@ -382,7 +378,7 @@ /* TODO: process */ break; default: - printf("Unknown IAPP command %d\n", hdr->command); + wpa_printf(MSG_INFO, "Unknown IAPP command %d", hdr->command); break; } } @@ -409,7 +405,8 @@ iapp->udp_sock = socket(PF_INET, SOCK_DGRAM, 0); if (iapp->udp_sock < 0) { - perror("socket[PF_INET,SOCK_DGRAM]"); + wpa_printf(MSG_INFO, "iapp_init - socket[PF_INET,SOCK_DGRAM]: %s", + strerror(errno)); iapp_deinit(iapp); return NULL; } @@ -417,35 +414,38 @@ os_memset(&ifr, 0, sizeof(ifr)); os_strlcpy(ifr.ifr_name, iface, sizeof(ifr.ifr_name)); if (ioctl(iapp->udp_sock, SIOCGIFINDEX, &ifr) != 0) { - perror("ioctl(SIOCGIFINDEX)"); + wpa_printf(MSG_INFO, "iapp_init - ioctl(SIOCGIFINDEX): %s", + strerror(errno)); iapp_deinit(iapp); return NULL; } ifindex = ifr.ifr_ifindex; if (ioctl(iapp->udp_sock, SIOCGIFADDR, &ifr) != 0) { - perror("ioctl(SIOCGIFADDR)"); + wpa_printf(MSG_INFO, "iapp_init - ioctl(SIOCGIFADDR): %s", + strerror(errno)); iapp_deinit(iapp); return NULL; } paddr = (struct sockaddr_in *) &ifr.ifr_addr; if (paddr->sin_family != AF_INET) { - printf("Invalid address family %i (SIOCGIFADDR)\n", - paddr->sin_family); + wpa_printf(MSG_INFO, "IAPP: Invalid address family %i (SIOCGIFADDR)", + paddr->sin_family); iapp_deinit(iapp); return NULL; } iapp->own.s_addr = paddr->sin_addr.s_addr; if (ioctl(iapp->udp_sock, SIOCGIFBRDADDR, &ifr) != 0) { - perror("ioctl(SIOCGIFBRDADDR)"); + wpa_printf(MSG_INFO, "iapp_init - ioctl(SIOCGIFBRDADDR): %s", + strerror(errno)); iapp_deinit(iapp); return NULL; } paddr = (struct sockaddr_in *) &ifr.ifr_addr; if (paddr->sin_family != AF_INET) { - printf("Invalid address family %i (SIOCGIFBRDADDR)\n", - paddr->sin_family); + wpa_printf(MSG_INFO, "Invalid address family %i (SIOCGIFBRDADDR)", + paddr->sin_family); iapp_deinit(iapp); return NULL; } @@ -456,7 +456,8 @@ uaddr.sin_port = htons(IAPP_UDP_PORT); if (bind(iapp->udp_sock, (struct sockaddr *) &uaddr, sizeof(uaddr)) < 0) { - perror("bind[UDP]"); + wpa_printf(MSG_INFO, "iapp_init - bind[UDP]: %s", + strerror(errno)); iapp_deinit(iapp); return NULL; } @@ -467,14 +468,16 @@ mreq.imr_ifindex = 0; if (setsockopt(iapp->udp_sock, SOL_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) { - perror("setsockopt[UDP,IP_ADD_MEMBERSHIP]"); + wpa_printf(MSG_INFO, "iapp_init - setsockopt[UDP,IP_ADD_MEMBERSHIP]: %s", + strerror(errno)); iapp_deinit(iapp); return NULL; } iapp->packet_sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); if (iapp->packet_sock < 0) { - perror("socket[PF_PACKET,SOCK_RAW]"); + wpa_printf(MSG_INFO, "iapp_init - socket[PF_PACKET,SOCK_RAW]: %s", + strerror(errno)); iapp_deinit(iapp); return NULL; } @@ -484,19 +487,20 @@ addr.sll_ifindex = ifindex; if (bind(iapp->packet_sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - perror("bind[PACKET]"); + wpa_printf(MSG_INFO, "iapp_init - bind[PACKET]: %s", + strerror(errno)); iapp_deinit(iapp); return NULL; } if (eloop_register_read_sock(iapp->udp_sock, iapp_receive_udp, iapp, NULL)) { - printf("Could not register read socket for IAPP.\n"); + wpa_printf(MSG_INFO, "Could not register read socket for IAPP"); iapp_deinit(iapp); return NULL; } - printf("IEEE 802.11F (IAPP) using interface %s\n", iface); + wpa_printf(MSG_INFO, "IEEE 802.11F (IAPP) using interface %s", iface); /* TODO: For levels 2 and 3: send RADIUS Initiate-Request, receive * RADIUS Initiate-Accept or Initiate-Reject. IAPP port should actually @@ -521,7 +525,8 @@ mreq.imr_ifindex = 0; if (setsockopt(iapp->udp_sock, SOL_IP, IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) { - perror("setsockopt[UDP,IP_DEL_MEMBERSHIP]"); + wpa_printf(MSG_INFO, "iapp_deinit - setsockopt[UDP,IP_DEL_MEMBERSHIP]: %s", + strerror(errno)); } eloop_unregister_read_sock(iapp->udp_sock); diff -Nru wpa-1.0/src/ap/iapp.h wpa-2.1/src/ap/iapp.h --- wpa-1.0/src/ap/iapp.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/ap/iapp.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * hostapd / IEEE 802.11F-2003 Inter-Access Point Protocol (IAPP) * Copyright (c) 2002-2005, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef IAPP_H diff -Nru wpa-1.0/src/ap/ieee802_11_auth.c wpa-2.1/src/ap/ieee802_11_auth.c --- wpa-1.0/src/ap/ieee802_11_auth.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/ap/ieee802_11_auth.c 2014-02-04 11:23:35.000000000 +0000 @@ -1,15 +1,9 @@ /* * hostapd / IEEE 802.11 authentication (ACL) - * Copyright (c) 2003-2009, Jouni Malinen + * Copyright (c) 2003-2012, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. * * Access control list for IEEE 802.11 authentication can uses statically * configured ACL from configuration files or an external RADIUS server. @@ -21,30 +15,35 @@ #include "utils/common.h" #include "utils/eloop.h" +#include "crypto/sha1.h" #include "radius/radius.h" #include "radius/radius_client.h" #include "hostapd.h" #include "ap_config.h" #include "ap_drv_ops.h" #include "ieee802_11.h" +#include "ieee802_1x.h" #include "ieee802_11_auth.h" #define RADIUS_ACL_TIMEOUT 30 struct hostapd_cached_radius_acl { - os_time_t timestamp; + struct os_reltime timestamp; macaddr addr; int accepted; /* HOSTAPD_ACL_* */ struct hostapd_cached_radius_acl *next; u32 session_timeout; u32 acct_interim_interval; int vlan_id; + struct hostapd_sta_wpa_psk_short *psk; + char *identity; + char *radius_cui; }; struct hostapd_acl_query_data { - os_time_t timestamp; + struct os_reltime timestamp; u8 radius_id; macaddr addr; u8 *auth_msg; /* IEEE 802.11 authentication frame from station */ @@ -54,6 +53,15 @@ #ifndef CONFIG_NO_RADIUS +static void hostapd_acl_cache_free_entry(struct hostapd_cached_radius_acl *e) +{ + os_free(e->identity); + os_free(e->radius_cui); + hostapd_free_psk_list(e->psk); + os_free(e); +} + + static void hostapd_acl_cache_free(struct hostapd_cached_radius_acl *acl_cache) { struct hostapd_cached_radius_acl *prev; @@ -61,38 +69,74 @@ while (acl_cache) { prev = acl_cache; acl_cache = acl_cache->next; - os_free(prev); + hostapd_acl_cache_free_entry(prev); + } +} + + +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; + + /* 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; } 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, int *vlan_id, + struct hostapd_sta_wpa_psk_short **psk, + char **identity, char **radius_cui) { struct hostapd_cached_radius_acl *entry; - struct os_time now; + struct os_reltime now; - os_get_time(&now); - entry = hapd->acl_cache; + os_get_reltime(&now); - while (entry) { - if (os_memcmp(entry->addr, addr, ETH_ALEN) == 0) { - if (now.sec - entry->timestamp > RADIUS_ACL_TIMEOUT) - return -1; /* entry has expired */ - if (entry->accepted == HOSTAPD_ACL_ACCEPT_TIMEOUT) - if (session_timeout) - *session_timeout = - entry->session_timeout; - if (acct_interim_interval) - *acct_interim_interval = - entry->acct_interim_interval; - if (vlan_id) - *vlan_id = entry->vlan_id; - return entry->accepted; - } + for (entry = hapd->acl_cache; entry; entry = entry->next) { + if (os_memcmp(entry->addr, addr, ETH_ALEN) != 0) + continue; - entry = entry->next; + if (os_reltime_expired(&now, &entry->timestamp, + RADIUS_ACL_TIMEOUT)) + return -1; /* entry has expired */ + if (entry->accepted == HOSTAPD_ACL_ACCEPT_TIMEOUT) + if (session_timeout) + *session_timeout = entry->session_timeout; + if (acct_interim_interval) + *acct_interim_interval = + entry->acct_interim_interval; + if (vlan_id) + *vlan_id = entry->vlan_id; + copy_psk_list(psk, entry->psk); + if (identity) { + if (entry->identity) + *identity = os_strdup(entry->identity); + else + *identity = NULL; + } + if (radius_cui) { + if (entry->radius_cui) + *radius_cui = os_strdup(entry->radius_cui); + else + *radius_cui = NULL; + } + return entry->accepted; } return -1; @@ -138,37 +182,9 @@ goto fail; } - if (hapd->conf->own_ip_addr.af == AF_INET && - !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IP_ADDRESS, - (u8 *) &hapd->conf->own_ip_addr.u.v4, 4)) { - wpa_printf(MSG_DEBUG, "Could not add NAS-IP-Address"); - goto fail; - } - -#ifdef CONFIG_IPV6 - if (hapd->conf->own_ip_addr.af == AF_INET6 && - !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IPV6_ADDRESS, - (u8 *) &hapd->conf->own_ip_addr.u.v6, 16)) { - wpa_printf(MSG_DEBUG, "Could not add NAS-IPv6-Address"); - goto fail; - } -#endif /* CONFIG_IPV6 */ - - if (hapd->conf->nas_identifier && - !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IDENTIFIER, - (u8 *) hapd->conf->nas_identifier, - os_strlen(hapd->conf->nas_identifier))) { - wpa_printf(MSG_DEBUG, "Could not add NAS-Identifier"); + if (add_common_radius_attr(hapd, hapd->conf->radius_auth_req_attr, + NULL, msg) < 0) goto fail; - } - - os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT ":%s", - MAC2STR(hapd->own_addr), hapd->conf->ssid.ssid); - if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLED_STATION_ID, - (u8 *) buf, os_strlen(buf))) { - wpa_printf(MSG_DEBUG, "Could not add Called-Station-Id"); - goto fail; - } os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT, MAC2STR(addr)); @@ -178,12 +194,6 @@ goto fail; } - if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_NAS_PORT_TYPE, - RADIUS_NAS_PORT_TYPE_IEEE_802_11)) { - wpa_printf(MSG_DEBUG, "Could not add NAS-Port-Type"); - goto fail; - } - os_snprintf(buf, sizeof(buf), "CONNECT 11Mbps 802.11b"); if (!radius_msg_add_attr(msg, RADIUS_ATTR_CONNECT_INFO, (u8 *) buf, os_strlen(buf))) { @@ -211,11 +221,19 @@ * @session_timeout: Buffer for returning session timeout (from RADIUS) * @acct_interim_interval: Buffer for returning account interval (from RADIUS) * @vlan_id: Buffer for returning VLAN ID + * @psk: Linked list buffer for returning WPA PSK + * @identity: Buffer for returning identity (from RADIUS) + * @radius_cui: Buffer for returning CUI (from RADIUS) * Returns: HOSTAPD_ACL_ACCEPT, HOSTAPD_ACL_REJECT, or HOSTAPD_ACL_PENDING + * + * The caller is responsible for freeing the returned *identity and *radius_cui + * values with os_free(). */ 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, int *vlan_id, + struct hostapd_sta_wpa_psk_short **psk, + char **identity, char **radius_cui) { if (session_timeout) *session_timeout = 0; @@ -223,6 +241,12 @@ *acct_interim_interval = 0; if (vlan_id) *vlan_id = 0; + if (psk) + *psk = NULL; + if (identity) + *identity = NULL; + if (radius_cui) + *radius_cui = NULL; if (hostapd_maclist_found(hapd->conf->accept_mac, hapd->conf->num_accept_mac, addr, vlan_id)) @@ -242,12 +266,12 @@ return HOSTAPD_ACL_REJECT; #else /* CONFIG_NO_RADIUS */ struct hostapd_acl_query_data *query; - struct os_time t; /* 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); + vlan_id, psk, + identity, radius_cui); if (res == HOSTAPD_ACL_ACCEPT || res == HOSTAPD_ACL_ACCEPT_TIMEOUT) return res; @@ -259,6 +283,14 @@ if (os_memcmp(query->addr, addr, ETH_ALEN) == 0) { /* pending query in RADIUS retransmit queue; * do not generate a new one */ + if (identity) { + os_free(*identity); + *identity = NULL; + } + if (radius_cui) { + os_free(*radius_cui); + *radius_cui = NULL; + } return HOSTAPD_ACL_PENDING; } query = query->next; @@ -273,8 +305,7 @@ wpa_printf(MSG_ERROR, "malloc for query data failed"); return HOSTAPD_ACL_REJECT; } - os_get_time(&t); - query->timestamp = t.sec; + os_get_reltime(&query->timestamp); os_memcpy(query->addr, addr, ETH_ALEN); if (hostapd_radius_acl_query(hapd, addr, query)) { wpa_printf(MSG_DEBUG, "Failed to send Access-Request " @@ -306,7 +337,8 @@ #ifndef CONFIG_NO_RADIUS -static void hostapd_acl_expire_cache(struct hostapd_data *hapd, os_time_t now) +static void hostapd_acl_expire_cache(struct hostapd_data *hapd, + struct os_reltime *now) { struct hostapd_cached_radius_acl *prev, *entry, *tmp; @@ -314,7 +346,8 @@ entry = hapd->acl_cache; while (entry) { - if (now - entry->timestamp > RADIUS_ACL_TIMEOUT) { + if (os_reltime_expired(now, &entry->timestamp, + RADIUS_ACL_TIMEOUT)) { wpa_printf(MSG_DEBUG, "Cached ACL entry for " MACSTR " has expired.", MAC2STR(entry->addr)); if (prev) @@ -324,7 +357,7 @@ hostapd_drv_set_radius_acl_expire(hapd, entry->addr); tmp = entry; entry = entry->next; - os_free(tmp); + hostapd_acl_cache_free_entry(tmp); continue; } @@ -335,7 +368,7 @@ static void hostapd_acl_expire_queries(struct hostapd_data *hapd, - os_time_t now) + struct os_reltime *now) { struct hostapd_acl_query_data *prev, *entry, *tmp; @@ -343,7 +376,8 @@ entry = hapd->acl_queries; while (entry) { - if (now - entry->timestamp > RADIUS_ACL_TIMEOUT) { + if (os_reltime_expired(now, &entry->timestamp, + RADIUS_ACL_TIMEOUT)) { wpa_printf(MSG_DEBUG, "ACL query for " MACSTR " has expired.", MAC2STR(entry->addr)); if (prev) @@ -371,16 +405,64 @@ static void hostapd_acl_expire(void *eloop_ctx, void *timeout_ctx) { struct hostapd_data *hapd = eloop_ctx; - struct os_time now; + struct os_reltime now; - os_get_time(&now); - hostapd_acl_expire_cache(hapd, now.sec); - hostapd_acl_expire_queries(hapd, now.sec); + os_get_reltime(&now); + hostapd_acl_expire_cache(hapd, &now); + hostapd_acl_expire_queries(hapd, &now); eloop_register_timeout(10, 0, hostapd_acl_expire, hapd, NULL); } +static void decode_tunnel_passwords(struct hostapd_data *hapd, + const u8 *shared_secret, + size_t shared_secret_len, + struct radius_msg *msg, + struct radius_msg *req, + struct hostapd_cached_radius_acl *cache) +{ + int passphraselen; + char *passphrase, *strpassphrase; + size_t i; + struct hostapd_sta_wpa_psk_short *psk; + + /* + * Decode all tunnel passwords as PSK and save them into a linked list. + */ + for (i = 0; ; i++) { + passphrase = radius_msg_get_tunnel_password( + msg, &passphraselen, shared_secret, shared_secret_len, + req, i); + /* + * Passphrase is NULL iff there is no i-th Tunnel-Password + * attribute in msg. + */ + if (passphrase == NULL) + break; + /* + * 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); + psk->next = cache->psk; + cache->psk = psk; + psk = NULL; + } + os_free(strpassphrase); + os_free(psk); + os_free(passphrase); + } +} + + /** * hostapd_acl_recv_radius - Process incoming RADIUS Authentication messages * @msg: RADIUS response message @@ -400,7 +482,6 @@ struct hostapd_acl_query_data *query, *prev; struct hostapd_cached_radius_acl *cache; struct radius_hdr *hdr = radius_msg_get_hdr(msg); - struct os_time t; query = hapd->acl_queries; prev = NULL; @@ -435,10 +516,12 @@ wpa_printf(MSG_DEBUG, "Failed to add ACL cache entry"); goto done; } - os_get_time(&t); - cache->timestamp = t.sec; + os_get_reltime(&cache->timestamp); os_memcpy(cache->addr, query->addr, sizeof(cache->addr)); if (hdr->code == RADIUS_CODE_ACCESS_ACCEPT) { + u8 *buf; + size_t len; + if (radius_msg_get_attr_int32(msg, RADIUS_ATTR_SESSION_TIMEOUT, &cache->session_timeout) == 0) cache->accepted = HOSTAPD_ACL_ACCEPT_TIMEOUT; @@ -457,6 +540,27 @@ } cache->vlan_id = radius_msg_get_vlanid(msg); + + decode_tunnel_passwords(hapd, shared_secret, shared_secret_len, + msg, req, cache); + + if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_USER_NAME, + &buf, &len, NULL) == 0) { + cache->identity = os_zalloc(len + 1); + if (cache->identity) + os_memcpy(cache->identity, buf, len); + } + if (radius_msg_get_attr_ptr( + msg, RADIUS_ATTR_CHARGEABLE_USER_IDENTITY, + &buf, &len, NULL) == 0) { + cache->radius_cui = os_zalloc(len + 1); + if (cache->radius_cui) + os_memcpy(cache->radius_cui, buf, len); + } + + if (hapd->conf->wpa_psk_radius == PSK_RADIUS_REQUIRED && + !cache->psk) + cache->accepted = HOSTAPD_ACL_REJECT; } else cache->accepted = HOSTAPD_ACL_REJECT; cache->next = hapd->acl_cache; @@ -527,3 +631,13 @@ hostapd_acl_query_free(prev); } } + + +void hostapd_free_psk_list(struct hostapd_sta_wpa_psk_short *psk) +{ + while (psk) { + struct hostapd_sta_wpa_psk_short *prev = psk; + psk = psk->next; + os_free(prev); + } +} diff -Nru wpa-1.0/src/ap/ieee802_11_auth.h wpa-2.1/src/ap/ieee802_11_auth.h --- wpa-1.0/src/ap/ieee802_11_auth.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/ap/ieee802_11_auth.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * hostapd / IEEE 802.11 authentication (ACL) * Copyright (c) 2003-2005, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef IEEE802_11_AUTH_H @@ -24,8 +18,11 @@ 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, int *vlan_id, + struct hostapd_sta_wpa_psk_short **psk, + char **identity, char **radius_cui); 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); #endif /* IEEE802_11_AUTH_H */ diff -Nru wpa-1.0/src/ap/ieee802_11.c wpa-2.1/src/ap/ieee802_11.c --- wpa-1.0/src/ap/ieee802_11.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/ap/ieee802_11.c 2014-02-04 11:23:35.000000000 +0000 @@ -1,15 +1,9 @@ /* * hostapd / IEEE 802.11 Management - * Copyright (c) 2002-2011, Jouni Malinen + * Copyright (c) 2002-2013, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "utils/includes.h" @@ -19,10 +13,12 @@ #include "utils/common.h" #include "utils/eloop.h" #include "crypto/crypto.h" -#include "drivers/driver.h" +#include "crypto/sha256.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 "radius/radius.h" #include "radius/radius_client.h" #include "p2p/p2p.h" @@ -40,6 +36,7 @@ #include "ap_mlme.h" #include "p2p_hostapd.h" #include "ap_drv_ops.h" +#include "wnm_ap.h" #include "ieee802_11.h" @@ -55,6 +52,8 @@ num = hapd->iface->num_rates; if (hapd->iconf->ieee80211n && hapd->iconf->require_ht) num++; + if (hapd->iconf->ieee80211ac && hapd->iconf->require_vht) + num++; if (num > 8) { /* rest of the rates are encoded in Extended supported * rates element */ @@ -72,9 +71,15 @@ pos++; } - if (hapd->iconf->ieee80211n && hapd->iconf->require_ht && - hapd->iface->num_rates < 8) + if (hapd->iconf->ieee80211n && hapd->iconf->require_ht && count < 8) { + count++; *pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_HT_PHY; + } + + if (hapd->iconf->ieee80211ac && hapd->iconf->require_vht && count < 8) { + count++; + *pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_VHT_PHY; + } return pos; } @@ -91,6 +96,8 @@ num = hapd->iface->num_rates; if (hapd->iconf->ieee80211n && hapd->iconf->require_ht) num++; + if (hapd->iconf->ieee80211ac && hapd->iconf->require_vht) + num++; if (num <= 8) return eid; num -= 8; @@ -109,9 +116,17 @@ pos++; } - if (hapd->iconf->ieee80211n && hapd->iconf->require_ht && - hapd->iface->num_rates >= 8) - *pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_HT_PHY; + if (hapd->iconf->ieee80211n && hapd->iconf->require_ht) { + count++; + if (count > 8) + *pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_HT_PHY; + } + + if (hapd->iconf->ieee80211ac && hapd->iconf->require_vht) { + count++; + if (count > 8) + *pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_VHT_PHY; + } return pos; } @@ -223,13 +238,8 @@ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "authentication OK (shared key)"); -#ifdef IEEE80211_REQUIRE_AUTH_ACK - /* Station will be marked authenticated if it ACKs the - * authentication reply. */ -#else sta->flags |= WLAN_STA_AUTH; wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH); -#endif os_free(sta->challenge); sta->challenge = NULL; @@ -269,8 +279,8 @@ " auth_alg=%d auth_transaction=%d resp=%d (IE len=%lu)", MAC2STR(dst), auth_alg, auth_transaction, resp, (unsigned long) ies_len); - if (hostapd_drv_send_mlme(hapd, reply, rlen) < 0) - perror("send_auth_reply: send"); + if (hostapd_drv_send_mlme(hapd, reply, rlen, 0) < 0) + wpa_printf(MSG_INFO, "send_auth_reply: send"); os_free(buf); } @@ -302,6 +312,222 @@ #endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_SAE + +static struct wpabuf * auth_process_sae_commit(struct hostapd_data *hapd, + struct sta_info *sta) +{ + struct wpabuf *buf; + + if (hapd->conf->ssid.wpa_passphrase == NULL) { + wpa_printf(MSG_DEBUG, "SAE: No password available"); + return NULL; + } + + if (sae_prepare_commit(hapd->own_addr, sta->addr, + (u8 *) hapd->conf->ssid.wpa_passphrase, + os_strlen(hapd->conf->ssid.wpa_passphrase), + sta->sae) < 0) { + wpa_printf(MSG_DEBUG, "SAE: Could not pick PWE"); + return NULL; + } + + if (sae_process_commit(sta->sae) < 0) { + wpa_printf(MSG_DEBUG, "SAE: Failed to process peer commit"); + return NULL; + } + + buf = wpabuf_alloc(SAE_COMMIT_MAX_LEN); + if (buf == NULL) + return NULL; + sae_write_commit(sta->sae, buf, NULL); + + return buf; +} + + +static struct wpabuf * auth_build_sae_confirm(struct hostapd_data *hapd, + struct sta_info *sta) +{ + struct wpabuf *buf; + + buf = wpabuf_alloc(SAE_CONFIRM_MAX_LEN); + if (buf == NULL) + return NULL; + + sae_write_confirm(sta->sae, buf); + + return buf; +} + + +static int use_sae_anti_clogging(struct hostapd_data *hapd) +{ + struct sta_info *sta; + unsigned int open = 0; + + if (hapd->conf->sae_anti_clogging_threshold == 0) + return 1; + + for (sta = hapd->sta_list; sta; sta = sta->next) { + if (!sta->sae) + continue; + if (sta->sae->state != SAE_COMMITTED && + sta->sae->state != SAE_CONFIRMED) + continue; + open++; + if (open >= hapd->conf->sae_anti_clogging_threshold) + return 1; + } + + return 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]; + + 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(token, mac, SHA256_MAC_LEN) != 0) + return -1; + + return 0; +} + + +static struct wpabuf * auth_build_token_req(struct hostapd_data *hapd, + const u8 *addr) +{ + struct wpabuf *buf; + u8 *token; + struct os_reltime now; + + 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)) { + if (random_get_bytes(hapd->sae_token_key, + sizeof(hapd->sae_token_key)) < 0) + return NULL; + wpa_hexdump(MSG_DEBUG, "SAE: Updated token key", + hapd->sae_token_key, sizeof(hapd->sae_token_key)); + hapd->last_sae_token_key_update = now; + } + + buf = wpabuf_alloc(SHA256_MAC_LEN); + if (buf == NULL) + return NULL; + + token = wpabuf_put(buf, SHA256_MAC_LEN); + hmac_sha256(hapd->sae_token_key, sizeof(hapd->sae_token_key), + addr, ETH_ALEN, token); + + return buf; +} + + +static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta, + const struct ieee80211_mgmt *mgmt, size_t len, + u8 auth_transaction) +{ + u16 resp = WLAN_STATUS_SUCCESS; + struct wpabuf *data = NULL; + + if (!sta->sae) { + if (auth_transaction != 1) + return; + sta->sae = os_zalloc(sizeof(*sta->sae)); + if (sta->sae == NULL) + return; + sta->sae->state = SAE_NOTHING; + } + + if (auth_transaction == 1) { + const u8 *token = NULL; + size_t token_len = 0; + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "start SAE authentication (RX commit)"); + 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); + 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; + } + + if (resp == WLAN_STATUS_SUCCESS) { + if (!token && use_sae_anti_clogging(hapd)) { + wpa_printf(MSG_DEBUG, "SAE: Request anti-" + "clogging token from " MACSTR, + MAC2STR(sta->addr)); + data = auth_build_token_req(hapd, sta->addr); + resp = WLAN_STATUS_ANTI_CLOGGING_TOKEN_REQ; + } else { + data = auth_process_sae_commit(hapd, sta); + if (data == NULL) + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + else + sta->sae->state = SAE_COMMITTED; + } + } + } else if (auth_transaction == 2) { + if (sta->sae->state != SAE_COMMITTED) { + hostapd_logger(hapd, sta->addr, + HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "SAE confirm before commit"); + resp = WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION; + } + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "SAE authentication (RX confirm)"); + if (sae_check_confirm(sta->sae, mgmt->u.auth.variable, + ((u8 *) mgmt) + len - + mgmt->u.auth.variable) < 0) { + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + } else { + resp = WLAN_STATUS_SUCCESS; + sta->flags |= WLAN_STA_AUTH; + wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH); + sta->auth_alg = WLAN_AUTH_SAE; + mlme_authenticate_indication(hapd, sta); + + data = auth_build_sae_confirm(hapd, sta); + if (data == NULL) + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + else { + sta->sae->state = SAE_ACCEPTED; + sae_clear_temp_data(sta->sae); + } + } + } else { + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "unexpected SAE authentication transaction %u", + auth_transaction); + resp = WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION; + } + + sta->auth_alg = WLAN_AUTH_SAE; + + send_auth_reply(hapd, mgmt->sa, mgmt->bssid, WLAN_AUTH_SAE, + auth_transaction, resp, + data ? wpabuf_head(data) : (u8 *) "", + data ? wpabuf_len(data) : 0); + wpabuf_free(data); +} +#endif /* CONFIG_SAE */ + + static void handle_auth(struct hostapd_data *hapd, const struct ieee80211_mgmt *mgmt, size_t len) { @@ -313,15 +539,28 @@ const u8 *challenge = NULL; u32 session_timeout, acct_interim_interval; int vlan_id = 0; + struct hostapd_sta_wpa_psk_short *psk = NULL; u8 resp_ies[2 + WLAN_AUTH_CHALLENGE_LEN]; size_t resp_ies_len = 0; + char *identity = NULL; + char *radius_cui = NULL; if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth)) { - printf("handle_auth - too short payload (len=%lu)\n", - (unsigned long) len); + wpa_printf(MSG_INFO, "handle_auth - too short payload (len=%lu)", + (unsigned long) len); return; } +#ifdef CONFIG_TESTING_OPTIONS + if (hapd->iconf->ignore_auth_probability > 0.0d && + drand48() < hapd->iconf->ignore_auth_probability) { + wpa_printf(MSG_INFO, + "TESTING: ignoring auth frame from " MACSTR, + MAC2STR(mgmt->sa)); + return; + } +#endif /* CONFIG_TESTING_OPTIONS */ + 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); @@ -347,40 +586,44 @@ if (!(((hapd->conf->auth_algs & WPA_AUTH_ALG_OPEN) && auth_alg == WLAN_AUTH_OPEN) || #ifdef CONFIG_IEEE80211R - (hapd->conf->wpa && - (hapd->conf->wpa_key_mgmt & - (WPA_KEY_MGMT_FT_IEEE8021X | WPA_KEY_MGMT_FT_PSK)) && + (hapd->conf->wpa && wpa_key_mgmt_ft(hapd->conf->wpa_key_mgmt) && auth_alg == WLAN_AUTH_FT) || #endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_SAE + (hapd->conf->wpa && wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt) && + auth_alg == WLAN_AUTH_SAE) || +#endif /* CONFIG_SAE */ ((hapd->conf->auth_algs & WPA_AUTH_ALG_SHARED) && auth_alg == WLAN_AUTH_SHARED_KEY))) { - printf("Unsupported authentication algorithm (%d)\n", - auth_alg); + wpa_printf(MSG_INFO, "Unsupported authentication algorithm (%d)", + auth_alg); resp = WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG; goto fail; } - if (!(auth_transaction == 1 || + if (!(auth_transaction == 1 || auth_alg == WLAN_AUTH_SAE || (auth_alg == WLAN_AUTH_SHARED_KEY && auth_transaction == 3))) { - printf("Unknown authentication transaction number (%d)\n", - auth_transaction); + wpa_printf(MSG_INFO, "Unknown authentication transaction number (%d)", + auth_transaction); resp = WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION; goto fail; } if (os_memcmp(mgmt->sa, hapd->own_addr, ETH_ALEN) == 0) { - printf("Station " MACSTR " not allowed to authenticate.\n", - MAC2STR(mgmt->sa)); + wpa_printf(MSG_INFO, "Station " MACSTR " not allowed to authenticate", + MAC2STR(mgmt->sa)); resp = WLAN_STATUS_UNSPECIFIED_FAILURE; goto fail; } res = hostapd_allowed_address(hapd, mgmt->sa, (u8 *) mgmt, len, &session_timeout, - &acct_interim_interval, &vlan_id); + &acct_interim_interval, &vlan_id, + &psk, &identity, &radius_cui); + if (res == HOSTAPD_ACL_REJECT) { - printf("Station " MACSTR " not allowed to authenticate.\n", - MAC2STR(mgmt->sa)); + wpa_printf(MSG_INFO, "Station " MACSTR " not allowed to authenticate", + MAC2STR(mgmt->sa)); resp = WLAN_STATUS_UNSPECIFIED_FAILURE; goto fail; } @@ -396,13 +639,12 @@ sta = ap_sta_add(hapd, mgmt->sa); if (!sta) { - resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA; goto fail; } if (vlan_id > 0) { - if (hostapd_get_vlan_id_ifname(hapd->conf->vlan, - vlan_id) == NULL) { + 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", @@ -415,6 +657,19 @@ 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; + } + + 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); @@ -430,15 +685,10 @@ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "authentication OK (open system)"); -#ifdef IEEE80211_REQUIRE_AUTH_ACK - /* Station will be marked authenticated if it ACKs the - * authentication reply. */ -#else sta->flags |= WLAN_STA_AUTH; wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH); sta->auth_alg = WLAN_AUTH_OPEN; mlme_authenticate_indication(hapd, sta); -#endif break; case WLAN_AUTH_SHARED_KEY: resp = auth_shared_key(hapd, sta, auth_transaction, challenge, @@ -458,7 +708,7 @@ sta->auth_alg = WLAN_AUTH_FT; if (sta->wpa_sm == NULL) sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth, - sta->addr); + sta->addr, NULL); if (sta->wpa_sm == NULL) { wpa_printf(MSG_DEBUG, "FT: Failed to initialize WPA " "state machine"); @@ -473,9 +723,18 @@ /* handle_auth_ft_finish() callback will complete auth. */ return; #endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_SAE + case WLAN_AUTH_SAE: + handle_auth_sae(hapd, sta, mgmt, len, auth_transaction); + return; +#endif /* CONFIG_SAE */ } fail: + os_free(identity); + 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); } @@ -539,15 +798,22 @@ const u8 *wmm_ie, size_t wmm_ie_len) { sta->flags &= ~WLAN_STA_WMM; + sta->qosinfo = 0; if (wmm_ie && hapd->conf->wmm_enabled) { - if (!hostapd_eid_wmm_valid(hapd, wmm_ie, wmm_ie_len)) + struct wmm_information_element *wmm; + + if (!hostapd_eid_wmm_valid(hapd, wmm_ie, wmm_ie_len)) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, HOSTAPD_LEVEL_DEBUG, "invalid WMM element in association " "request"); - else - sta->flags |= WLAN_STA_WMM; + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + sta->flags |= WLAN_STA_WMM; + wmm = (struct wmm_information_element *) wmm_ie; + sta->qosinfo = wmm->qos_info; } return WLAN_STATUS_SUCCESS; } @@ -563,35 +829,35 @@ return WLAN_STATUS_UNSPECIFIED_FAILURE; } - if (elems->supp_rates_len > sizeof(sta->supported_rates)) { + if (elems->supp_rates_len + elems->ext_supp_rates_len > + sizeof(sta->supported_rates)) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, - "Invalid supported rates element length %d", - elems->supp_rates_len); + "Invalid supported rates element length %d+%d", + elems->supp_rates_len, + elems->ext_supp_rates_len); return WLAN_STATUS_UNSPECIFIED_FAILURE; } - os_memset(sta->supported_rates, 0, sizeof(sta->supported_rates)); - os_memcpy(sta->supported_rates, elems->supp_rates, - elems->supp_rates_len); - sta->supported_rates_len = elems->supp_rates_len; - - if (elems->ext_supp_rates) { - if (elems->supp_rates_len + elems->ext_supp_rates_len > - sizeof(sta->supported_rates)) { - hostapd_logger(hapd, sta->addr, - HOSTAPD_MODULE_IEEE80211, - HOSTAPD_LEVEL_DEBUG, - "Invalid supported rates element length" - " %d+%d", elems->supp_rates_len, - elems->ext_supp_rates_len); - return WLAN_STATUS_UNSPECIFIED_FAILURE; - } + sta->supported_rates_len = merge_byte_arrays( + sta->supported_rates, sizeof(sta->supported_rates), + elems->supp_rates, elems->supp_rates_len, + elems->ext_supp_rates, elems->ext_supp_rates_len); + + return WLAN_STATUS_SUCCESS; +} - os_memcpy(sta->supported_rates + elems->supp_rates_len, - elems->ext_supp_rates, elems->ext_supp_rates_len); - sta->supported_rates_len += elems->ext_supp_rates_len; + +static u16 check_ext_capab(struct hostapd_data *hapd, struct sta_info *sta, + const u8 *ext_capab_ie, size_t ext_capab_ie_len) +{ +#ifdef CONFIG_INTERWORKING + /* check for QoS Map support */ + if (ext_capab_ie_len >= 5) { + if (ext_capab_ie[4] & 0x01) + sta->qos_map_enabled = 1; } +#endif /* CONFIG_INTERWORKING */ return WLAN_STATUS_SUCCESS; } @@ -604,6 +870,7 @@ u16 resp; const u8 *wpa_ie; size_t wpa_ie_len; + const u8 *p2p_dev_addr = NULL; if (ieee802_11_parse_elems(ies, ies_len, &elems, 1) == ParseFailed) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, @@ -618,6 +885,9 @@ resp = check_wmm(hapd, sta, elems.wmm, elems.wmm_len); if (resp != WLAN_STATUS_SUCCESS) return resp; + resp = check_ext_capab(hapd, sta, elems.ext_capab, elems.ext_capab_len); + if (resp != WLAN_STATUS_SUCCESS) + return resp; resp = copy_supp_rates(hapd, sta, &elems); if (resp != WLAN_STATUS_SUCCESS) return resp; @@ -635,6 +905,33 @@ } #endif /* CONFIG_IEEE80211N */ +#ifdef CONFIG_IEEE80211AC + resp = copy_sta_vht_capab(hapd, sta, elems.vht_capabilities, + elems.vht_capabilities_len); + if (resp != WLAN_STATUS_SUCCESS) + return resp; + if (hapd->iconf->ieee80211ac && hapd->iconf->require_vht && + !(sta->flags & WLAN_STA_VHT)) { + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_INFO, "Station does not support " + "mandatory VHT PHY - reject association"); + return WLAN_STATUS_ASSOC_DENIED_NO_VHT; + } +#endif /* CONFIG_IEEE80211AC */ + +#ifdef CONFIG_P2P + if (elems.p2p) { + wpabuf_free(sta->p2p_ie); + sta->p2p_ie = ieee802_11_vendor_ie_concat(ies, ies_len, + P2P_IE_VENDOR_TYPE); + if (sta->p2p_ie) + p2p_dev_addr = p2p_get_go_dev_addr(sta->p2p_ie); + } else { + wpabuf_free(sta->p2p_ie); + sta->p2p_ie = NULL; + } +#endif /* CONFIG_P2P */ + if ((hapd->conf->wpa & WPA_PROTO_RSN) && elems.rsn_ie) { wpa_ie = elems.rsn_ie; wpa_ie_len = elems.rsn_ie_len; @@ -686,7 +983,8 @@ wpa_ie_len += 2; if (sta->wpa_sm == NULL) sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth, - sta->addr); + sta->addr, + p2p_dev_addr); if (sta->wpa_sm == NULL) { wpa_printf(MSG_WARNING, "Failed to initialize WPA " "state machine"); @@ -757,8 +1055,20 @@ } #endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_SAE + if (wpa_auth_uses_sae(sta->wpa_sm) && + sta->auth_alg != WLAN_AUTH_SAE && + !(sta->auth_alg == WLAN_AUTH_FT && + wpa_auth_uses_ft_sae(sta->wpa_sm))) { + wpa_printf(MSG_DEBUG, "SAE: " MACSTR " tried to use " + "SAE AKM after non-SAE auth_alg %u", + MAC2STR(sta->addr), sta->auth_alg); + return WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG; + } +#endif /* CONFIG_SAE */ + #ifdef CONFIG_IEEE80211N - if ((sta->flags & WLAN_STA_HT) && + if ((sta->flags & (WLAN_STA_HT | WLAN_STA_VHT)) && wpa_auth_get_pairwise(sta->wpa_sm) == WPA_CIPHER_TKIP) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, @@ -772,19 +1082,18 @@ wpa_auth_sta_no_wpa(sta->wpa_sm); #ifdef CONFIG_P2P - if (elems.p2p) { - wpabuf_free(sta->p2p_ie); - sta->p2p_ie = ieee802_11_vendor_ie_concat(ies, ies_len, - P2P_IE_VENDOR_TYPE); - - } else { - wpabuf_free(sta->p2p_ie); - sta->p2p_ie = NULL; - } - p2p_group_notif_assoc(hapd->p2p_group, sta->addr, ies, ies_len); #endif /* CONFIG_P2P */ +#ifdef CONFIG_HS20 + wpabuf_free(sta->hs20_ie); + if (elems.hs20 && elems.hs20_len > 4) { + sta->hs20_ie = wpabuf_alloc_copy(elems.hs20 + 4, + elems.hs20_len - 4); + } else + sta->hs20_ie = NULL; +#endif /* CONFIG_HS20 */ + return WLAN_STATUS_SUCCESS; } @@ -805,7 +1114,7 @@ send_len = IEEE80211_HDRLEN + sizeof(reply.u.deauth); reply.u.deauth.reason_code = host_to_le16(reason_code); - if (hostapd_drv_send_mlme(hapd, &reply, send_len) < 0) + if (hostapd_drv_send_mlme(hapd, &reply, send_len, 0) < 0) wpa_printf(MSG_INFO, "Failed to send deauth: %s", strerror(errno)); } @@ -862,7 +1171,15 @@ p = hostapd_eid_ht_operation(hapd, p); #endif /* CONFIG_IEEE80211N */ +#ifdef CONFIG_IEEE80211AC + p = hostapd_eid_vht_capabilities(hapd, p); + p = hostapd_eid_vht_operation(hapd, p); +#endif /* CONFIG_IEEE80211AC */ + p = hostapd_eid_ext_capab(hapd, p); + p = hostapd_eid_bss_max_idle_period(hapd, p); + if (sta->qos_map_enabled) + p = hostapd_eid_qos_map_set(hapd, p); if (sta->flags & WLAN_STA_WMM) p = hostapd_eid_wmm(hapd, p); @@ -911,7 +1228,7 @@ send_len += p - reply->u.assoc_resp.variable; - if (hostapd_drv_send_mlme(hapd, reply, send_len) < 0) + if (hostapd_drv_send_mlme(hapd, reply, send_len, 0) < 0) wpa_printf(MSG_INFO, "Failed to send assoc resp: %s", strerror(errno)); } @@ -929,11 +1246,31 @@ if (len < IEEE80211_HDRLEN + (reassoc ? sizeof(mgmt->u.reassoc_req) : sizeof(mgmt->u.assoc_req))) { - printf("handle_assoc(reassoc=%d) - too short payload (len=%lu)" - "\n", reassoc, (unsigned long) len); + wpa_printf(MSG_INFO, "handle_assoc(reassoc=%d) - too short payload (len=%lu)", + reassoc, (unsigned long) len); return; } +#ifdef CONFIG_TESTING_OPTIONS + if (reassoc) { + if (hapd->iconf->ignore_reassoc_probability > 0.0d && + drand48() < hapd->iconf->ignore_reassoc_probability) { + wpa_printf(MSG_INFO, + "TESTING: ignoring reassoc request from " + MACSTR, MAC2STR(mgmt->sa)); + return; + } + } else { + if (hapd->iconf->ignore_assoc_probability > 0.0d && + drand48() < hapd->iconf->ignore_assoc_probability) { + wpa_printf(MSG_INFO, + "TESTING: ignoring assoc request from " + MACSTR, MAC2STR(mgmt->sa)); + return; + } + } +#endif /* CONFIG_TESTING_OPTIONS */ + if (reassoc) { capab_info = le_to_host16(mgmt->u.reassoc_req.capab_info); listen_interval = le_to_host16( @@ -1096,8 +1433,8 @@ struct sta_info *sta; if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.disassoc)) { - printf("handle_disassoc - too short payload (len=%lu)\n", - (unsigned long) len); + wpa_printf(MSG_INFO, "handle_disassoc - too short payload (len=%lu)", + (unsigned long) len); return; } @@ -1107,8 +1444,8 @@ sta = ap_get_sta(hapd, mgmt->sa); if (sta == NULL) { - printf("Station " MACSTR " trying to disassociate, but it " - "is not associated.\n", MAC2STR(mgmt->sa)); + wpa_printf(MSG_INFO, "Station " MACSTR " trying to disassociate, but it is not associated", + MAC2STR(mgmt->sa)); return; } @@ -1182,8 +1519,8 @@ struct ieee802_11_elems elems; if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.beacon)) { - printf("handle_beacon - too short payload (len=%lu)\n", - (unsigned long) len); + wpa_printf(MSG_INFO, "handle_beacon - too short payload (len=%lu)", + (unsigned long) len); return; } @@ -1198,9 +1535,9 @@ #ifdef CONFIG_IEEE80211W -static void hostapd_sa_query_action(struct hostapd_data *hapd, - const struct ieee80211_mgmt *mgmt, - size_t len) +static int hostapd_sa_query_action(struct hostapd_data *hapd, + const struct ieee80211_mgmt *mgmt, + size_t len) { const u8 *end; @@ -1209,12 +1546,13 @@ if (((u8 *) mgmt) + len < end) { wpa_printf(MSG_DEBUG, "IEEE 802.11: Too short SA Query Action " "frame (len=%lu)", (unsigned long) len); - return; + 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; } @@ -1226,74 +1564,81 @@ #endif /* CONFIG_IEEE80211W */ -static void handle_action(struct hostapd_data *hapd, - const struct ieee80211_mgmt *mgmt, size_t len) +static int handle_action(struct hostapd_data *hapd, + const struct ieee80211_mgmt *mgmt, size_t len) { -#if defined(CONFIG_IEEE80211W) || defined(CONFIG_IEEE80211R) struct sta_info *sta; sta = ap_get_sta(hapd, mgmt->sa); -#endif /* CONFIG_IEEE80211W || CONFIG_IEEE80211R */ if (len < IEEE80211_HDRLEN + 1) { hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "handle_action - too short payload (len=%lu)", (unsigned long) len); - return; + return 0; + } + + 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); + return 0; } #ifdef CONFIG_IEEE80211W if (sta && (sta->flags & WLAN_STA_MFP) && - !(mgmt->frame_control & host_to_le16(WLAN_FC_ISWEP) && - robust_action_frame(mgmt->u.action.category))) { + !(mgmt->frame_control & host_to_le16(WLAN_FC_ISWEP)) && + robust_action_frame(mgmt->u.action.category)) { hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "Dropped unprotected Robust Action frame from " "an MFP STA"); - return; + return 0; } #endif /* CONFIG_IEEE80211W */ switch (mgmt->u.action.category) { #ifdef CONFIG_IEEE80211R case WLAN_ACTION_FT: - { - if (sta == NULL || !(sta->flags & WLAN_STA_ASSOC)) { - wpa_printf(MSG_DEBUG, "IEEE 802.11: Ignored FT Action " - "frame from unassociated STA " MACSTR, - MAC2STR(mgmt->sa)); - return; - } - if (wpa_ft_action_rx(sta->wpa_sm, (u8 *) &mgmt->u.action, len - IEEE80211_HDRLEN)) break; - - return; - } + return 1; #endif /* CONFIG_IEEE80211R */ case WLAN_ACTION_WMM: hostapd_wmm_action(hapd, mgmt, len); - return; + return 1; #ifdef CONFIG_IEEE80211W case WLAN_ACTION_SA_QUERY: - hostapd_sa_query_action(hapd, mgmt, len); - return; + return hostapd_sa_query_action(hapd, mgmt, len); #endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_WNM + case WLAN_ACTION_WNM: + ieee802_11_rx_wnm_action_ap(hapd, mgmt, len); + return 1; +#endif /* CONFIG_WNM */ case WLAN_ACTION_PUBLIC: + case WLAN_ACTION_PROTECTED_DUAL: if (hapd->public_action_cb) { hapd->public_action_cb(hapd->public_action_cb_ctx, (u8 *) mgmt, len, hapd->iface->freq); - return; } + if (hapd->public_action_cb2) { + hapd->public_action_cb2(hapd->public_action_cb2_ctx, + (u8 *) mgmt, len, + hapd->iface->freq); + } + if (hapd->public_action_cb || hapd->public_action_cb2) + return 1; break; case WLAN_ACTION_VENDOR_SPECIFIC: if (hapd->vendor_action_cb) { if (hapd->vendor_action_cb(hapd->vendor_action_cb_ctx, (u8 *) mgmt, len, hapd->iface->freq) == 0) - return; + return 1; } break; } @@ -1316,16 +1661,21 @@ "frame back to sender"); resp = os_malloc(len); if (resp == NULL) - return; + 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); resp->u.action.category |= 0x80; - hostapd_drv_send_mlme(hapd, resp, len); + if (hostapd_drv_send_mlme(hapd, resp, len, 0) < 0) { + wpa_printf(MSG_ERROR, "IEEE 802.11: Failed to send " + "Action frame"); + } os_free(resp); } + + return 1; } @@ -1342,15 +1692,29 @@ * addition, it can be called to re-inserted pending frames (e.g., when using * external RADIUS server as an MAC ACL). */ -void ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len, - struct hostapd_frame_info *fi) +int ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len, + struct hostapd_frame_info *fi) { struct ieee80211_mgmt *mgmt; int broadcast; u16 fc, stype; + int ret = 0; + +#ifdef CONFIG_TESTING_OPTIONS + if (hapd->ext_mgmt_frame_handling) { + 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-RX %s", hex); + os_free(hex); + } + return 1; + } +#endif /* CONFIG_TESTING_OPTIONS */ if (len < 24) - return; + return 0; mgmt = (struct ieee80211_mgmt *) buf; fc = le_to_host16(mgmt->frame_control); @@ -1358,7 +1722,7 @@ if (stype == WLAN_FC_STYPE_BEACON) { handle_beacon(hapd, mgmt, len, fi); - return; + return 1; } broadcast = mgmt->bssid[0] == 0xff && mgmt->bssid[1] == 0xff && @@ -1372,15 +1736,15 @@ stype == WLAN_FC_STYPE_ACTION) && #endif /* CONFIG_P2P */ os_memcmp(mgmt->bssid, hapd->own_addr, ETH_ALEN) != 0) { - printf("MGMT: BSSID=" MACSTR " not our address\n", - MAC2STR(mgmt->bssid)); - return; + wpa_printf(MSG_INFO, "MGMT: BSSID=" MACSTR " not our address", + MAC2STR(mgmt->bssid)); + return 0; } if (stype == WLAN_FC_STYPE_PROBE_REQ) { - handle_probe_req(hapd, mgmt, len); - return; + handle_probe_req(hapd, mgmt, len, fi->ssi_signal); + return 1; } if (os_memcmp(mgmt->da, hapd->own_addr, ETH_ALEN) != 0) { @@ -1388,33 +1752,38 @@ HOSTAPD_LEVEL_DEBUG, "MGMT: DA=" MACSTR " not our address", MAC2STR(mgmt->da)); - return; + return 0; } switch (stype) { case WLAN_FC_STYPE_AUTH: wpa_printf(MSG_DEBUG, "mgmt::auth"); handle_auth(hapd, mgmt, len); + ret = 1; break; case WLAN_FC_STYPE_ASSOC_REQ: wpa_printf(MSG_DEBUG, "mgmt::assoc_req"); handle_assoc(hapd, mgmt, len, 0); + ret = 1; break; case WLAN_FC_STYPE_REASSOC_REQ: wpa_printf(MSG_DEBUG, "mgmt::reassoc_req"); handle_assoc(hapd, mgmt, len, 1); + ret = 1; break; case WLAN_FC_STYPE_DISASSOC: wpa_printf(MSG_DEBUG, "mgmt::disassoc"); handle_disassoc(hapd, mgmt, len); + ret = 1; break; case WLAN_FC_STYPE_DEAUTH: wpa_msg(hapd->msg_ctx, MSG_DEBUG, "mgmt::deauth"); handle_deauth(hapd, mgmt, len); + ret = 1; break; case WLAN_FC_STYPE_ACTION: wpa_printf(MSG_DEBUG, "mgmt::action"); - handle_action(hapd, mgmt, len); + ret = handle_action(hapd, mgmt, len); break; default: hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, @@ -1422,6 +1791,8 @@ "unknown mgmt frame subtype %d", stype); break; } + + return ret; } @@ -1440,8 +1811,8 @@ } if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth)) { - printf("handle_auth_cb - too short payload (len=%lu)\n", - (unsigned long) len); + wpa_printf(MSG_INFO, "handle_auth_cb - too short payload (len=%lu)", + (unsigned long) len); return; } @@ -1451,8 +1822,8 @@ sta = ap_get_sta(hapd, mgmt->da); if (!sta) { - printf("handle_auth_cb: STA " MACSTR " not found\n", - MAC2STR(mgmt->da)); + wpa_printf(MSG_INFO, "handle_auth_cb: STA " MACSTR " not found", + MAC2STR(mgmt->da)); return; } @@ -1466,6 +1837,30 @@ } +static void hostapd_set_wds_encryption(struct hostapd_data *hapd, + struct sta_info *sta, + char *ifname_wds) +{ + int i; + struct hostapd_ssid *ssid = sta->ssid; + + if (hapd->conf->ieee802_1x || hapd->conf->wpa) + return; + + for (i = 0; i < 4; i++) { + if (ssid->wep.key[i] && + hostapd_drv_set_key(ifname_wds, hapd, WPA_ALG_WEP, NULL, i, + i == ssid->wep.idx, NULL, 0, + ssid->wep.key[i], ssid->wep.len[i])) { + wpa_printf(MSG_WARNING, + "Could not set WEP keys for WDS interface; %s", + ifname_wds); + break; + } + } +} + + static void handle_assoc_cb(struct hostapd_data *hapd, const struct ieee80211_mgmt *mgmt, size_t len, int reassoc, int ok) @@ -1474,18 +1869,27 @@ 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", + MAC2STR(mgmt->da)); + return; + } if (!ok) { hostapd_logger(hapd, mgmt->da, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "did not acknowledge association response"); - return; - } - - if (len < IEEE80211_HDRLEN + (reassoc ? sizeof(mgmt->u.reassoc_resp) : - sizeof(mgmt->u.assoc_resp))) { - printf("handle_assoc_cb(reassoc=%d) - too short payload " - "(len=%lu)\n", reassoc, (unsigned long) len); + sta->flags &= ~WLAN_STA_ASSOC_REQ_OK; return; } @@ -1494,13 +1898,6 @@ else status = le_to_host16(mgmt->u.assoc_resp.status_code); - sta = ap_get_sta(hapd, mgmt->da); - if (!sta) { - printf("handle_assoc_cb: STA " MACSTR " not found\n", - MAC2STR(mgmt->da)); - return; - } - if (status != WLAN_STATUS_SUCCESS) goto fail; @@ -1516,6 +1913,7 @@ if (sta->flags & WLAN_STA_ASSOC) new_assoc = 0; sta->flags |= WLAN_STA_ASSOC; + sta->flags &= ~WLAN_STA_WNM_SLEEP_MODE; if ((!hapd->conf->ieee802_1x && !hapd->conf->wpa) || sta->auth_alg == WLAN_AUTH_FT) { /* @@ -1545,12 +1943,17 @@ 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)) { + sta->flags & WLAN_STA_VHT ? &vht_cap : NULL, + sta->flags, sta->qosinfo)) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_NOTICE, "Could not add STA to kernel driver"); @@ -1561,8 +1964,15 @@ goto fail; } - if (sta->flags & WLAN_STA_WDS) - hostapd_set_wds_sta(hapd, sta->addr, sta->aid, 1); + 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) { /* @@ -1660,6 +2070,14 @@ const struct ieee80211_mgmt *mgmt; mgmt = (const struct ieee80211_mgmt *) buf; +#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); + return; + } +#endif /* CONFIG_TESTING_OPTIONS */ + switch (stype) { case WLAN_FC_STYPE_AUTH: wpa_printf(MSG_DEBUG, "mgmt::auth cb"); @@ -1688,7 +2106,7 @@ wpa_printf(MSG_DEBUG, "mgmt::action cb"); break; default: - printf("unknown mgmt cb frame subtype %d\n", stype); + wpa_printf(MSG_INFO, "unknown mgmt cb frame subtype %d", stype); break; } } @@ -1739,6 +2157,33 @@ } +void hostapd_eapol_tx_status(struct hostapd_data *hapd, const u8 *dst, + const u8 *data, size_t len, int ack) +{ + struct sta_info *sta; + struct hostapd_iface *iface = hapd->iface; + + sta = ap_get_sta(hapd, dst); + if (sta == NULL && iface->num_bss > 1) { + size_t j; + for (j = 0; j < iface->num_bss; j++) { + hapd = iface->bss[j]; + sta = ap_get_sta(hapd, dst); + if (sta) + break; + } + } + if (sta == NULL || !(sta->flags & WLAN_STA_ASSOC)) { + wpa_printf(MSG_DEBUG, "Ignore TX status for Data frame to STA " + MACSTR " that is not currently associated", + MAC2STR(dst)); + return; + } + + ieee802_1x_eapol_tx_status(hapd, sta, data, len, ack); +} + + void hostapd_client_poll_ok(struct hostapd_data *hapd, const u8 *addr) { struct sta_info *sta; @@ -1772,12 +2217,22 @@ sta = ap_get_sta(hapd, src); if (sta && (sta->flags & WLAN_STA_ASSOC)) { + if (!hapd->conf->wds_sta) + return; + if (wds && !(sta->flags & WLAN_STA_WDS)) { + int ret; + char ifname_wds[IFNAMSIZ + 1]; + wpa_printf(MSG_DEBUG, "Enable 4-address WDS mode for " "STA " MACSTR " (aid %u)", MAC2STR(sta->addr), sta->aid); sta->flags |= WLAN_STA_WDS; - hostapd_set_wds_sta(hapd, sta->addr, sta->aid, 1); + ret = hostapd_set_wds_sta(hapd, ifname_wds, + sta->addr, sta->aid, 1); + if (!ret) + hostapd_set_wds_encryption(hapd, sta, + ifname_wds); } return; } diff -Nru wpa-1.0/src/ap/ieee802_11.h wpa-2.1/src/ap/ieee802_11.h --- wpa-1.0/src/ap/ieee802_11.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/ap/ieee802_11.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * hostapd / IEEE 802.11 Management * Copyright (c) 2002-2009, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef IEEE802_11_H @@ -21,8 +15,8 @@ struct hostapd_frame_info; struct ieee80211_ht_capabilities; -void ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len, - struct hostapd_frame_info *fi); +int ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len, + struct hostapd_frame_info *fi); void ieee802_11_mgmt_cb(struct hostapd_data *hapd, const u8 *buf, size_t len, u16 stype, int ok); void ieee802_11_print_ssid(char *buf, const u8 *ssid, u8 len); @@ -47,21 +41,31 @@ u16 hostapd_own_capab_info(struct hostapd_data *hapd, struct sta_info *sta, int probe); u8 * hostapd_eid_ext_capab(struct hostapd_data *hapd, u8 *eid); +u8 * hostapd_eid_qos_map_set(struct hostapd_data *hapd, u8 *eid); u8 * hostapd_eid_supp_rates(struct hostapd_data *hapd, u8 *eid); 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_vht_operation(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); void hostapd_get_ht_capab(struct hostapd_data *hapd, struct ieee80211_ht_capabilities *ht_cap, struct ieee80211_ht_capabilities *neg_ht_cap); +void hostapd_get_vht_capab(struct hostapd_data *hapd, + struct ieee80211_vht_capabilities *vht_cap, + struct ieee80211_vht_capabilities *neg_vht_cap); u16 copy_sta_ht_capab(struct hostapd_data *hapd, struct sta_info *sta, const u8 *ht_capab, size_t ht_capab_len); void update_ht_state(struct hostapd_data *hapd, struct sta_info *sta); +u16 copy_sta_vht_capab(struct hostapd_data *hapd, struct sta_info *sta, + const u8 *vht_capab, size_t vht_capab_len); void hostapd_tx_status(struct hostapd_data *hapd, const u8 *addr, const u8 *buf, size_t len, int ack); +void hostapd_eapol_tx_status(struct hostapd_data *hapd, const u8 *dst, + const u8 *data, size_t len, int ack); void ieee802_11_rx_from_unknown(struct hostapd_data *hapd, const u8 *src, int wds); u8 * hostapd_eid_assoc_comeback_time(struct hostapd_data *hapd, @@ -76,5 +80,6 @@ u8 * hostapd_eid_time_zone(struct hostapd_data *hapd, u8 *eid); int hostapd_update_time_adv(struct hostapd_data *hapd); void hostapd_client_poll_ok(struct hostapd_data *hapd, const u8 *addr); +u8 * hostapd_eid_bss_max_idle_period(struct hostapd_data *hapd, u8 *eid); #endif /* IEEE802_11_H */ diff -Nru wpa-1.0/src/ap/ieee802_11_ht.c wpa-2.1/src/ap/ieee802_11_ht.c --- wpa-1.0/src/ap/ieee802_11_ht.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/ap/ieee802_11_ht.c 2014-02-04 11:23:35.000000000 +0000 @@ -3,21 +3,14 @@ * Copyright (c) 2002-2009, Jouni Malinen * Copyright (c) 2007-2008, Intel Corporation * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * 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 "drivers/driver.h" #include "hostapd.h" #include "ap_config.h" #include "sta_info.h" @@ -50,6 +43,22 @@ pos += sizeof(*cap); + if (hapd->iconf->obss_interval) { + struct ieee80211_obss_scan_parameters *scan_params; + + *pos++ = WLAN_EID_OVERLAPPING_BSS_SCAN_PARAMS; + *pos++ = sizeof(*scan_params); + + scan_params = (struct ieee80211_obss_scan_parameters *) pos; + os_memset(scan_params, 0, sizeof(*scan_params)); + scan_params->width_trigger_scan_interval = + host_to_le16(hapd->iconf->obss_interval); + + /* TODO: Fill in more parameters (supplicant ignores them) */ + + pos += sizeof(*scan_params); + } + return pos; } @@ -133,8 +142,7 @@ new_op_mode = 0; if (iface->num_sta_no_ht) new_op_mode = OP_MODE_MIXED; - else if ((iface->conf->ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET) - && iface->num_sta_ht_20mhz) + else if (iface->conf->secondary_channel && iface->num_sta_ht_20mhz) new_op_mode = OP_MODE_20MHZ_HT_STA_ASSOCED; else if (iface->olbc_ht) new_op_mode = OP_MODE_MAY_BE_LEGACY_STAS; diff -Nru wpa-1.0/src/ap/ieee802_11_shared.c wpa-2.1/src/ap/ieee802_11_shared.c --- wpa-1.0/src/ap/ieee802_11_shared.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/ap/ieee802_11_shared.c 2014-02-04 11:23:35.000000000 +0000 @@ -1,15 +1,9 @@ /* * hostapd / IEEE 802.11 Management - * Copyright (c) 2002-2010, Jouni Malinen + * Copyright (c) 2002-2012, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "utils/includes.h" @@ -30,13 +24,13 @@ { u8 *pos = eid; u32 timeout, tu; - struct os_time now, passed; + struct os_reltime now, passed; *pos++ = WLAN_EID_TIMEOUT_INTERVAL; *pos++ = 5; *pos++ = WLAN_TIMEOUT_ASSOC_COMEBACK; - os_get_time(&now); - os_time_sub(&now, &sta->sa_query_start, &passed); + os_get_reltime(&now); + os_reltime_sub(&now, &sta->sa_query_start, &passed); tu = (passed.sec * 1000000 + passed.usec) / 1024; if (hapd->conf->assoc_sa_query_max_timeout > tu) timeout = hapd->conf->assoc_sa_query_max_timeout - tu; @@ -74,13 +68,13 @@ 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) - perror("ieee802_11_send_sa_query_req: send"); + if (hostapd_drv_send_mlme(hapd, &mgmt, end - (u8 *) &mgmt, 0) < 0) + wpa_printf(MSG_INFO, "ieee802_11_send_sa_query_req: send failed"); } -void ieee802_11_send_sa_query_resp(struct hostapd_data *hapd, - const u8 *sa, const u8 *trans_id) +static void ieee802_11_send_sa_query_resp(struct hostapd_data *hapd, + const u8 *sa, const u8 *trans_id) { struct sta_info *sta; struct ieee80211_mgmt resp; @@ -112,8 +106,8 @@ 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) - perror("ieee80211_mgmt_sa_query_request: send"); + if (hostapd_drv_send_mlme(hapd, &resp, end - (u8 *) &resp, 0) < 0) + wpa_printf(MSG_INFO, "ieee80211_mgmt_sa_query_request: send failed"); } @@ -170,39 +164,106 @@ #endif /* CONFIG_IEEE80211W */ +static void hostapd_ext_capab_byte(struct hostapd_data *hapd, u8 *pos, int idx) +{ + *pos = 0x00; + + switch (idx) { + case 0: /* Bits 0-7 */ + break; + case 1: /* Bits 8-15 */ + break; + case 2: /* Bits 16-23 */ + if (hapd->conf->wnm_sleep_mode) + *pos |= 0x02; /* Bit 17 - WNM-Sleep Mode */ + if (hapd->conf->bss_transition) + *pos |= 0x08; /* Bit 19 - BSS Transition */ + break; + case 3: /* Bits 24-31 */ +#ifdef CONFIG_WNM + *pos |= 0x02; /* Bit 25 - SSID List */ +#endif /* CONFIG_WNM */ + if (hapd->conf->time_advertisement == 2) + *pos |= 0x08; /* Bit 27 - UTC TSF Offset */ + if (hapd->conf->interworking) + *pos |= 0x80; /* Bit 31 - Interworking */ + break; + case 4: /* Bits 32-39 */ + if (hapd->conf->qos_map_set_len) + *pos |= 0x01; /* Bit 32 - QoS Map */ + if (hapd->conf->tdls & TDLS_PROHIBIT) + *pos |= 0x40; /* Bit 38 - TDLS Prohibited */ + if (hapd->conf->tdls & TDLS_PROHIBIT_CHAN_SWITCH) { + /* Bit 39 - TDLS Channel Switching Prohibited */ + *pos |= 0x80; + } + break; + case 5: /* Bits 40-47 */ + break; + case 6: /* Bits 48-55 */ + if (hapd->conf->ssid.utf8_ssid) + *pos |= 0x01; /* Bit 48 - UTF-8 SSID */ + break; + } +} + + u8 * hostapd_eid_ext_capab(struct hostapd_data *hapd, u8 *eid) { u8 *pos = eid; - u8 len = 0; + u8 len = 0, i; if (hapd->conf->tdls & (TDLS_PROHIBIT | TDLS_PROHIBIT_CHAN_SWITCH)) len = 5; if (len < 4 && hapd->conf->interworking) len = 4; + if (len < 3 && hapd->conf->wnm_sleep_mode) + len = 3; + if (len < 7 && hapd->conf->ssid.utf8_ssid) + len = 7; +#ifdef CONFIG_WNM + if (len < 4) + len = 4; +#endif /* CONFIG_WNM */ + if (len < hapd->iface->extended_capa_len) + len = hapd->iface->extended_capa_len; if (len == 0) return eid; *pos++ = WLAN_EID_EXT_CAPAB; *pos++ = len; - *pos++ = 0x00; - *pos++ = 0x00; - *pos++ = 0x00; + for (i = 0; i < len; i++, pos++) { + hostapd_ext_capab_byte(hapd, pos, i); - *pos = 0x00; - if (hapd->conf->time_advertisement == 2) - *pos |= 0x08; /* Bit 27 - UTC TSF Offset */ - if (hapd->conf->interworking) - *pos |= 0x80; /* Bit 31 - Interworking */ - pos++; + if (i < hapd->iface->extended_capa_len) { + *pos &= ~hapd->iface->extended_capa_mask[i]; + *pos |= hapd->iface->extended_capa[i]; + } + } - if (len < 5) - return pos; - *pos = 0x00; - if (hapd->conf->tdls & TDLS_PROHIBIT) - *pos |= 0x40; /* Bit 38 - TDLS Prohibited */ - if (hapd->conf->tdls & TDLS_PROHIBIT_CHAN_SWITCH) - *pos |= 0x80; /* Bit 39 - TDLS Channel Switching Prohibited */ - pos++; + while (len > 0 && eid[1 + len] == 0) { + len--; + eid[1] = len; + } + if (len == 0) + return eid; + + return eid + 2 + len; +} + + +u8 * hostapd_eid_qos_map_set(struct hostapd_data *hapd, u8 *eid) +{ + u8 *pos = eid; + u8 len = hapd->conf->qos_map_set_len; + + if (!len) + return eid; + + *pos++ = WLAN_EID_QOS_MAP_SET; + *pos++ = len; + os_memcpy(pos, hapd->conf->qos_map_set, len); + pos += len; return pos; } @@ -403,3 +464,31 @@ return 0; } + + +u8 * hostapd_eid_bss_max_idle_period(struct hostapd_data *hapd, u8 *eid) +{ + u8 *pos = eid; + +#ifdef CONFIG_WNM + if (hapd->conf->ap_max_inactivity > 0) { + unsigned int val; + *pos++ = WLAN_EID_BSS_MAX_IDLE_PERIOD; + *pos++ = 3; + val = hapd->conf->ap_max_inactivity; + if (val > 68000) + val = 68000; + val *= 1000; + val /= 1024; + if (val == 0) + val = 1; + if (val > 65535) + val = 65535; + WPA_PUT_LE16(pos, val); + pos += 2; + *pos++ = 0x00; /* TODO: Protected Keep-Alive Required */ + } +#endif /* CONFIG_WNM */ + + return pos; +} diff -Nru wpa-1.0/src/ap/ieee802_11_vht.c wpa-2.1/src/ap/ieee802_11_vht.c --- wpa-1.0/src/ap/ieee802_11_vht.c 1970-01-01 00:00:00.000000000 +0000 +++ wpa-2.1/src/ap/ieee802_11_vht.c 2014-02-04 11:23:35.000000000 +0000 @@ -0,0 +1,171 @@ +/* + * hostapd / IEEE 802.11ac VHT + * Copyright (c) 2002-2009, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of BSD license + * + * See README and COPYING 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 "sta_info.h" +#include "beacon.h" +#include "ieee802_11.h" + + +u8 * hostapd_eid_vht_capabilities(struct hostapd_data *hapd, u8 *eid) +{ + struct ieee80211_vht_capabilities *cap; + u8 *pos = eid; + + if (!hapd->iconf->ieee80211ac || !hapd->iface->current_mode || + hapd->conf->disable_11ac) + return eid; + + *pos++ = WLAN_EID_VHT_CAP; + *pos++ = sizeof(*cap); + + cap = (struct ieee80211_vht_capabilities *) pos; + os_memset(cap, 0, sizeof(*cap)); + cap->vht_capabilities_info = host_to_le32( + hapd->iface->conf->vht_capab); + + /* Supported MCS set comes from hw */ + os_memcpy(&cap->vht_supported_mcs_set, + hapd->iface->current_mode->vht_mcs_set, 8); + + pos += sizeof(*cap); + + return pos; +} + + +u8 * hostapd_eid_vht_operation(struct hostapd_data *hapd, u8 *eid) +{ + struct ieee80211_vht_operation *oper; + u8 *pos = eid; + + if (!hapd->iconf->ieee80211ac || hapd->conf->disable_11ac) + return eid; + + *pos++ = WLAN_EID_VHT_OPERATION; + *pos++ = sizeof(*oper); + + oper = (struct ieee80211_vht_operation *) pos; + os_memset(oper, 0, sizeof(*oper)); + + /* + * center freq = 5 GHz + (5 * index) + * So index 42 gives center freq 5.210 GHz + * which is channel 42 in 5G band + */ + oper->vht_op_info_chan_center_freq_seg0_idx = + hapd->iconf->vht_oper_centr_freq_seg0_idx; + oper->vht_op_info_chan_center_freq_seg1_idx = + hapd->iconf->vht_oper_centr_freq_seg1_idx; + + oper->vht_op_info_chwidth = hapd->iconf->vht_oper_chwidth; + + /* VHT Basic MCS set comes from hw */ + /* Hard code 1 stream, MCS0-7 is a min Basic VHT MCS rates */ + oper->vht_basic_mcs_set = host_to_le16(0xfffc); + pos += sizeof(*oper); + + return pos; +} + + +u16 copy_sta_vht_capab(struct hostapd_data *hapd, struct sta_info *sta, + const u8 *vht_capab, size_t vht_capab_len) +{ + /* Disable VHT caps for STAs associated to no-VHT BSSes. */ + if (!vht_capab || + vht_capab_len < sizeof(struct ieee80211_vht_capabilities) || + hapd->conf->disable_11ac) { + sta->flags &= ~WLAN_STA_VHT; + os_free(sta->vht_capabilities); + sta->vht_capabilities = NULL; + return WLAN_STATUS_SUCCESS; + } + + if (sta->vht_capabilities == NULL) { + sta->vht_capabilities = + os_zalloc(sizeof(struct ieee80211_vht_capabilities)); + if (sta->vht_capabilities == NULL) + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + sta->flags |= WLAN_STA_VHT; + os_memcpy(sta->vht_capabilities, vht_capab, + sizeof(struct ieee80211_vht_capabilities)); + + return WLAN_STATUS_SUCCESS; +} + +void hostapd_get_vht_capab(struct hostapd_data *hapd, + struct ieee80211_vht_capabilities *vht_cap, + struct ieee80211_vht_capabilities *neg_vht_cap) +{ + u32 cap, own_cap, sym_caps; + + if (vht_cap == NULL) + return; + os_memcpy(neg_vht_cap, vht_cap, sizeof(*neg_vht_cap)); + + cap = le_to_host32(neg_vht_cap->vht_capabilities_info); + own_cap = hapd->iconf->vht_capab; + + /* mask out symmetric VHT capabilities we don't support */ + sym_caps = VHT_CAP_SHORT_GI_80 | VHT_CAP_SHORT_GI_160; + cap &= ~sym_caps | (own_cap & sym_caps); + + /* mask out beamformer/beamformee caps if not supported */ + if (!(own_cap & VHT_CAP_SU_BEAMFORMER_CAPABLE)) + cap &= ~(VHT_CAP_SU_BEAMFORMEE_CAPABLE | + VHT_CAP_BEAMFORMEE_STS_MAX); + + if (!(own_cap & VHT_CAP_SU_BEAMFORMEE_CAPABLE)) + cap &= ~(VHT_CAP_SU_BEAMFORMER_CAPABLE | + VHT_CAP_SOUNDING_DIMENSION_MAX); + + if (!(own_cap & VHT_CAP_MU_BEAMFORMER_CAPABLE)) + cap &= ~VHT_CAP_MU_BEAMFORMEE_CAPABLE; + + if (!(own_cap & VHT_CAP_MU_BEAMFORMEE_CAPABLE)) + cap &= ~VHT_CAP_MU_BEAMFORMER_CAPABLE; + + /* mask channel widths we don't support */ + switch (own_cap & VHT_CAP_SUPP_CHAN_WIDTH_MASK) { + case VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ: + break; + case VHT_CAP_SUPP_CHAN_WIDTH_160MHZ: + if (cap & VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ) { + cap &= ~VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ; + cap |= VHT_CAP_SUPP_CHAN_WIDTH_160MHZ; + } + break; + default: + cap &= ~VHT_CAP_SUPP_CHAN_WIDTH_MASK; + break; + } + + if (!(cap & VHT_CAP_SUPP_CHAN_WIDTH_MASK)) + cap &= ~VHT_CAP_SHORT_GI_160; + + /* + * if we don't support RX STBC, mask out TX STBC in the STA's HT caps + * if we don't support TX STBC, mask out RX STBC in the STA's HT caps + */ + if (!(own_cap & VHT_CAP_RXSTBC_MASK)) + cap &= ~VHT_CAP_TXSTBC; + if (!(own_cap & VHT_CAP_TXSTBC)) + cap &= ~VHT_CAP_RXSTBC_MASK; + + neg_vht_cap->vht_capabilities_info = host_to_le32(cap); +} diff -Nru wpa-1.0/src/ap/ieee802_1x.c wpa-2.1/src/ap/ieee802_1x.c --- wpa-1.0/src/ap/ieee802_1x.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/ap/ieee802_1x.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * hostapd / IEEE 802.1X-2004 Authenticator * Copyright (c) 2002-2012, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "utils/includes.h" @@ -20,7 +14,6 @@ #include "crypto/crypto.h" #include "crypto/random.h" #include "common/ieee802_11_defs.h" -#include "common/wpa_ctrl.h" #include "radius/radius.h" #include "radius/radius_client.h" #include "eap_server/eap.h" @@ -36,6 +29,7 @@ #include "pmksa_cache_auth.h" #include "ap_config.h" #include "ap_drv_ops.h" +#include "wps_hostapd.h" #include "ieee802_1x.h" @@ -73,8 +67,9 @@ if (sta->flags & WLAN_STA_PREAUTH) { rsn_preauth_send(hapd, sta, buf, len); } else { - hostapd_drv_hapd_send_eapol(hapd, sta->addr, buf, len, - encrypt, sta->flags); + hostapd_drv_hapd_send_eapol( + hapd, sta->addr, buf, len, + encrypt, hostapd_sta_flags_to_drv(sta->flags)); } os_free(buf); @@ -102,12 +97,15 @@ } if (res && errno != ENOENT) { - printf("Could not set station " MACSTR " flags for kernel " - "driver (errno=%d).\n", MAC2STR(sta->addr), errno); + wpa_printf(MSG_DEBUG, "Could not set station " MACSTR + " flags for kernel driver (errno=%d).", + MAC2STR(sta->addr), errno); } - if (authorized) + if (authorized) { + os_get_reltime(&sta->connected_time); accounting_sta_start(hapd, sta); + } } @@ -133,7 +131,7 @@ hdr = (struct ieee802_1x_hdr *) buf; key = (struct ieee802_1x_eapol_key *) (hdr + 1); key->type = EAPOL_KEY_TYPE_RC4; - key->key_length = htons(key_len); + WPA_PUT_BE16(key->key_length, key_len); wpa_get_ntp_timestamp(key->replay_counter); if (random_get_bytes(key->key_iv, sizeof(key->key_iv))) { @@ -190,114 +188,10 @@ } -#ifndef CONFIG_NO_VLAN -static struct hostapd_wep_keys * -ieee802_1x_group_alloc(struct hostapd_data *hapd, const char *ifname) -{ - struct hostapd_wep_keys *key; - - key = os_zalloc(sizeof(*key)); - if (key == NULL) - return NULL; - - key->default_len = hapd->conf->default_wep_key_len; - - if (key->idx >= hapd->conf->broadcast_key_idx_max || - key->idx < hapd->conf->broadcast_key_idx_min) - key->idx = hapd->conf->broadcast_key_idx_min; - else - key->idx++; - - if (!key->key[key->idx]) - key->key[key->idx] = os_malloc(key->default_len); - if (key->key[key->idx] == NULL || - random_get_bytes(key->key[key->idx], key->default_len)) { - printf("Could not generate random WEP key (dynamic VLAN).\n"); - os_free(key->key[key->idx]); - key->key[key->idx] = NULL; - os_free(key); - return NULL; - } - key->len[key->idx] = key->default_len; - - wpa_printf(MSG_DEBUG, "%s: Default WEP idx %d for dynamic VLAN\n", - ifname, key->idx); - wpa_hexdump_key(MSG_DEBUG, "Default WEP key (dynamic VLAN)", - key->key[key->idx], key->len[key->idx]); - - if (hostapd_drv_set_key(ifname, hapd, WPA_ALG_WEP, - broadcast_ether_addr, key->idx, 1, - NULL, 0, key->key[key->idx], - key->len[key->idx])) - printf("Could not set dynamic VLAN WEP encryption key.\n"); - - hostapd_set_drv_ieee8021x(hapd, ifname, 1); - - return key; -} - - -static struct hostapd_wep_keys * -ieee802_1x_get_group(struct hostapd_data *hapd, struct hostapd_ssid *ssid, - size_t vlan_id) -{ - const char *ifname; - - if (vlan_id == 0) - return &ssid->wep; - - if (vlan_id <= ssid->max_dyn_vlan_keys && ssid->dyn_vlan_keys && - ssid->dyn_vlan_keys[vlan_id]) - return ssid->dyn_vlan_keys[vlan_id]; - - wpa_printf(MSG_DEBUG, "IEEE 802.1X: Creating new group " - "state machine for VLAN ID %lu", - (unsigned long) vlan_id); - - ifname = hostapd_get_vlan_id_ifname(hapd->conf->vlan, vlan_id); - if (ifname == NULL) { - wpa_printf(MSG_DEBUG, "IEEE 802.1X: Unknown VLAN ID %lu - " - "cannot create group key state machine", - (unsigned long) vlan_id); - return NULL; - } - - if (ssid->dyn_vlan_keys == NULL) { - int size = (vlan_id + 1) * sizeof(ssid->dyn_vlan_keys[0]); - ssid->dyn_vlan_keys = os_zalloc(size); - if (ssid->dyn_vlan_keys == NULL) - return NULL; - ssid->max_dyn_vlan_keys = vlan_id; - } - - if (ssid->max_dyn_vlan_keys < vlan_id) { - struct hostapd_wep_keys **na; - int size = (vlan_id + 1) * sizeof(ssid->dyn_vlan_keys[0]); - na = os_realloc(ssid->dyn_vlan_keys, size); - if (na == NULL) - return NULL; - ssid->dyn_vlan_keys = na; - os_memset(&ssid->dyn_vlan_keys[ssid->max_dyn_vlan_keys + 1], 0, - (vlan_id - ssid->max_dyn_vlan_keys) * - sizeof(ssid->dyn_vlan_keys[0])); - ssid->max_dyn_vlan_keys = vlan_id; - } - - ssid->dyn_vlan_keys[vlan_id] = ieee802_1x_group_alloc(hapd, ifname); - - return ssid->dyn_vlan_keys[vlan_id]; -} -#endif /* CONFIG_NO_VLAN */ - - void ieee802_1x_tx_key(struct hostapd_data *hapd, struct sta_info *sta) { struct eapol_authenticator *eapol = hapd->eapol_auth; struct eapol_state_machine *sm = sta->eapol_sm; -#ifndef CONFIG_NO_VLAN - struct hostapd_wep_keys *key = NULL; - int vlan_id; -#endif /* CONFIG_NO_VLAN */ if (sm == NULL || !sm->eap_if->eapKeyData) return; @@ -306,18 +200,12 @@ MAC2STR(sta->addr)); #ifndef CONFIG_NO_VLAN - vlan_id = sta->vlan_id; - if (vlan_id < 0 || vlan_id > MAX_VLAN_ID) - vlan_id = 0; - - if (vlan_id) { - key = ieee802_1x_get_group(hapd, sta->ssid, vlan_id); - if (key && key->key[key->idx]) - ieee802_1x_tx_key_one(hapd, sta, key->idx, 1, - key->key[key->idx], - key->len[key->idx]); - } else + if (sta->vlan_id > 0 && sta->vlan_id <= MAX_VLAN_ID) { + wpa_printf(MSG_ERROR, "Using WEP with vlans is not supported."); + return; + } #endif /* CONFIG_NO_VLAN */ + if (eapol->default_wep_key) { ieee802_1x_tx_key_one(hapd, sta, eapol->default_wep_key_idx, 1, eapol->default_wep_key, @@ -359,6 +247,8 @@ const char *radius_mode_txt(struct hostapd_data *hapd) { switch (hapd->iface->conf->hw_mode) { + case HOSTAPD_MODE_IEEE80211AD: + return "802.11ad"; case HOSTAPD_MODE_IEEE80211A: return "802.11a"; case HOSTAPD_MODE_IEEE80211G: @@ -401,133 +291,198 @@ /* Save station identity for future RADIUS packets */ os_free(sm->identity); - sm->identity = os_malloc(identity_len + 1); + sm->identity = (u8 *) dup_binstr(identity, identity_len); if (sm->identity == NULL) { sm->identity_len = 0; return; } - os_memcpy(sm->identity, identity, identity_len); sm->identity_len = identity_len; - sm->identity[identity_len] = '\0'; hostapd_logger(hapd, sm->addr, HOSTAPD_MODULE_IEEE8021X, HOSTAPD_LEVEL_DEBUG, "STA identity '%s'", sm->identity); sm->dot1xAuthEapolRespIdFramesRx++; } -static void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd, - struct sta_info *sta, - const u8 *eap, size_t len) +static int add_common_radius_sta_attr(struct hostapd_data *hapd, + struct hostapd_radius_attr *req_attr, + struct sta_info *sta, + struct radius_msg *msg) { - struct radius_msg *msg; char buf[128]; - struct eapol_state_machine *sm = sta->eapol_sm; - if (sm == NULL) - return; + if (!hostapd_config_get_radius_attr(req_attr, + RADIUS_ATTR_NAS_PORT) && + !radius_msg_add_attr_int32(msg, RADIUS_ATTR_NAS_PORT, sta->aid)) { + wpa_printf(MSG_ERROR, "Could not add NAS-Port"); + return -1; + } - ieee802_1x_learn_identity(hapd, sm, eap, len); + os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT, + MAC2STR(sta->addr)); + buf[sizeof(buf) - 1] = '\0'; + if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLING_STATION_ID, + (u8 *) buf, os_strlen(buf))) { + wpa_printf(MSG_ERROR, "Could not add Calling-Station-Id"); + return -1; + } - wpa_printf(MSG_DEBUG, "Encapsulating EAP message into a RADIUS " - "packet"); + if (sta->flags & WLAN_STA_PREAUTH) { + os_strlcpy(buf, "IEEE 802.11i Pre-Authentication", + sizeof(buf)); + } else { + os_snprintf(buf, sizeof(buf), "CONNECT %d%sMbps %s", + radius_sta_rate(hapd, sta) / 2, + (radius_sta_rate(hapd, sta) & 1) ? ".5" : "", + radius_mode_txt(hapd)); + buf[sizeof(buf) - 1] = '\0'; + } + if (!hostapd_config_get_radius_attr(req_attr, + RADIUS_ATTR_CONNECT_INFO) && + !radius_msg_add_attr(msg, RADIUS_ATTR_CONNECT_INFO, + (u8 *) buf, os_strlen(buf))) { + wpa_printf(MSG_ERROR, "Could not add Connect-Info"); + return -1; + } - sm->radius_identifier = radius_client_get_id(hapd->radius); - msg = radius_msg_new(RADIUS_CODE_ACCESS_REQUEST, - sm->radius_identifier); - if (msg == NULL) { - printf("Could not create net RADIUS packet\n"); - return; + 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 (!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"); + return -1; + } } - radius_msg_make_authenticator(msg, (u8 *) sta, sizeof(*sta)); + return 0; +} - if (sm->identity && - !radius_msg_add_attr(msg, RADIUS_ATTR_USER_NAME, - sm->identity, sm->identity_len)) { - printf("Could not add User-Name\n"); - goto fail; - } - if (hapd->conf->own_ip_addr.af == AF_INET && +int add_common_radius_attr(struct hostapd_data *hapd, + struct hostapd_radius_attr *req_attr, + struct sta_info *sta, + struct radius_msg *msg) +{ + char buf[128]; + struct hostapd_radius_attr *attr; + + if (!hostapd_config_get_radius_attr(req_attr, + RADIUS_ATTR_NAS_IP_ADDRESS) && + hapd->conf->own_ip_addr.af == AF_INET && !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IP_ADDRESS, (u8 *) &hapd->conf->own_ip_addr.u.v4, 4)) { - printf("Could not add NAS-IP-Address\n"); - goto fail; + wpa_printf(MSG_ERROR, "Could not add NAS-IP-Address"); + return -1; } #ifdef CONFIG_IPV6 - if (hapd->conf->own_ip_addr.af == AF_INET6 && + if (!hostapd_config_get_radius_attr(req_attr, + RADIUS_ATTR_NAS_IPV6_ADDRESS) && + hapd->conf->own_ip_addr.af == AF_INET6 && !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IPV6_ADDRESS, (u8 *) &hapd->conf->own_ip_addr.u.v6, 16)) { - printf("Could not add NAS-IPv6-Address\n"); - goto fail; + wpa_printf(MSG_ERROR, "Could not add NAS-IPv6-Address"); + return -1; } #endif /* CONFIG_IPV6 */ - if (hapd->conf->nas_identifier && + if (!hostapd_config_get_radius_attr(req_attr, + RADIUS_ATTR_NAS_IDENTIFIER) && + hapd->conf->nas_identifier && !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IDENTIFIER, (u8 *) hapd->conf->nas_identifier, os_strlen(hapd->conf->nas_identifier))) { - printf("Could not add NAS-Identifier\n"); - goto fail; - } - - if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_NAS_PORT, sta->aid)) { - printf("Could not add NAS-Port\n"); - goto fail; + wpa_printf(MSG_ERROR, "Could not add NAS-Identifier"); + return -1; } os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT ":%s", - MAC2STR(hapd->own_addr), hapd->conf->ssid.ssid); + MAC2STR(hapd->own_addr), + wpa_ssid_txt(hapd->conf->ssid.ssid, + hapd->conf->ssid.ssid_len)); buf[sizeof(buf) - 1] = '\0'; - if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLED_STATION_ID, + 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))) { - printf("Could not add Called-Station-Id\n"); - goto fail; + wpa_printf(MSG_ERROR, "Could not add Called-Station-Id"); + return -1; } - os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT, - MAC2STR(sta->addr)); - buf[sizeof(buf) - 1] = '\0'; - if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLING_STATION_ID, - (u8 *) buf, os_strlen(buf))) { - printf("Could not add Calling-Station-Id\n"); - goto fail; + if (!hostapd_config_get_radius_attr(req_attr, + RADIUS_ATTR_NAS_PORT_TYPE) && + !radius_msg_add_attr_int32(msg, RADIUS_ATTR_NAS_PORT_TYPE, + RADIUS_NAS_PORT_TYPE_IEEE_802_11)) { + wpa_printf(MSG_ERROR, "Could not add NAS-Port-Type"); + return -1; } - /* TODO: should probably check MTU from driver config; 2304 is max for - * IEEE 802.11, but use 1400 to avoid problems with too large packets - */ - if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_FRAMED_MTU, 1400)) { - printf("Could not add Framed-MTU\n"); - goto fail; + if (sta && add_common_radius_sta_attr(hapd, req_attr, sta, msg) < 0) + return -1; + + for (attr = req_attr; attr; attr = attr->next) { + if (!radius_msg_add_attr(msg, attr->type, + wpabuf_head(attr->val), + wpabuf_len(attr->val))) { + wpa_printf(MSG_ERROR, "Could not add RADIUS " + "attribute"); + return -1; + } } - if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_NAS_PORT_TYPE, - RADIUS_NAS_PORT_TYPE_IEEE_802_11)) { - printf("Could not add NAS-Port-Type\n"); - goto fail; + return 0; +} + + +static 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; + + if (sm == NULL) + return; + + ieee802_1x_learn_identity(hapd, sm, eap, len); + + wpa_printf(MSG_DEBUG, "Encapsulating EAP message into a RADIUS " + "packet"); + + sm->radius_identifier = radius_client_get_id(hapd->radius); + msg = radius_msg_new(RADIUS_CODE_ACCESS_REQUEST, + sm->radius_identifier); + if (msg == NULL) { + wpa_printf(MSG_INFO, "Could not create new RADIUS packet"); + return; } - if (sta->flags & WLAN_STA_PREAUTH) { - os_strlcpy(buf, "IEEE 802.11i Pre-Authentication", - sizeof(buf)); - } else { - os_snprintf(buf, sizeof(buf), "CONNECT %d%sMbps %s", - radius_sta_rate(hapd, sta) / 2, - (radius_sta_rate(hapd, sta) & 1) ? ".5" : "", - radius_mode_txt(hapd)); - buf[sizeof(buf) - 1] = '\0'; + radius_msg_make_authenticator(msg, (u8 *) sta, sizeof(*sta)); + + if (sm->identity && + !radius_msg_add_attr(msg, RADIUS_ATTR_USER_NAME, + sm->identity, sm->identity_len)) { + wpa_printf(MSG_INFO, "Could not add User-Name"); + goto fail; } - if (!radius_msg_add_attr(msg, RADIUS_ATTR_CONNECT_INFO, - (u8 *) buf, os_strlen(buf))) { - printf("Could not add Connect-Info\n"); + + if (add_common_radius_attr(hapd, hapd->conf->radius_auth_req_attr, sta, + msg) < 0) + goto fail; + + /* TODO: should probably check MTU from driver config; 2304 is max for + * IEEE 802.11, but use 1400 to avoid problems with too large packets + */ + if (!hostapd_config_get_radius_attr(hapd->conf->radius_auth_req_attr, + RADIUS_ATTR_FRAMED_MTU) && + !radius_msg_add_attr_int32(msg, RADIUS_ATTR_FRAMED_MTU, 1400)) { + wpa_printf(MSG_INFO, "Could not add Framed-MTU"); goto fail; } if (eap && !radius_msg_add_eap(msg, eap, len)) { - printf("Could not add EAP-Message\n"); + wpa_printf(MSG_INFO, "Could not add EAP-Message"); goto fail; } @@ -539,8 +494,7 @@ int res = radius_msg_copy_attr(msg, sm->last_recv_radius, RADIUS_ATTR_STATE); if (res < 0) { - printf("Could not copy State attribute from previous " - "Access-Challenge\n"); + wpa_printf(MSG_INFO, "Could not copy State attribute from previous Access-Challenge"); goto fail; } if (res > 0) { @@ -548,6 +502,25 @@ } } + if (hapd->conf->radius_request_cui) { + const u8 *cui; + size_t cui_len; + /* Add previously learned CUI or nul CUI to request CUI */ + if (sm->radius_cui) { + cui = wpabuf_head(sm->radius_cui); + cui_len = wpabuf_len(sm->radius_cui); + } else { + cui = (const u8 *) "\0"; + cui_len = 1; + } + if (!radius_msg_add_attr(msg, + RADIUS_ATTR_CHARGEABLE_USER_IDENTITY, + cui, cui_len)) { + wpa_printf(MSG_ERROR, "Could not add CUI"); + goto fail; + } + } + if (radius_client_send(hapd->radius, msg, RADIUS_AUTH, sta->addr) < 0) goto fail; @@ -571,7 +544,7 @@ data = (u8 *) (eap + 1); if (len < sizeof(*eap) + 1) { - printf("handle_eap_response: too short response data\n"); + wpa_printf(MSG_INFO, "handle_eap_response: too short response data"); return; } @@ -599,7 +572,7 @@ u16 eap_len; if (len < sizeof(*eap)) { - printf(" too short EAP packet\n"); + wpa_printf(MSG_INFO, " too short EAP packet"); return; } @@ -653,7 +626,8 @@ flags |= EAPOL_SM_FROM_PMKSA_CACHE; } return eapol_auth_alloc(hapd->eapol_auth, sta->addr, flags, - sta->wps_ie, sta->p2p_ie, sta); + sta->wps_ie, sta->p2p_ie, sta, + sta->identity, sta->radius_cui); } @@ -691,7 +665,7 @@ } if (len < sizeof(*hdr)) { - printf(" too short IEEE 802.1X packet\n"); + wpa_printf(MSG_INFO, " too short IEEE 802.1X packet"); return; } @@ -701,7 +675,7 @@ hdr->version, hdr->type, datalen); if (len - sizeof(*hdr) < datalen) { - printf(" frame too short for this IEEE 802.1X packet\n"); + wpa_printf(MSG_INFO, " frame too short for this IEEE 802.1X packet"); if (sta->eapol_sm) sta->eapol_sm->dot1xAuthEapLengthErrorFramesRx++; return; @@ -985,6 +959,7 @@ #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); @@ -996,9 +971,8 @@ static void ieee802_1x_decapsulate_radius(struct hostapd_data *hapd, struct sta_info *sta) { - u8 *eap; - size_t len; - struct eap_hdr *hdr; + struct wpabuf *eap; + const struct eap_hdr *hdr; int eap_type = -1; char buf[64]; struct radius_msg *msg; @@ -1012,7 +986,7 @@ msg = sm->last_recv_radius; - eap = radius_msg_get_eap(msg, &len); + eap = radius_msg_get_eap(msg); if (eap == NULL) { /* RFC 3579, Chap. 2.6.3: * RADIUS server SHOULD NOT send Access-Reject/no EAP-Message @@ -1024,19 +998,19 @@ return; } - if (len < sizeof(*hdr)) { + if (wpabuf_len(eap) < sizeof(*hdr)) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, HOSTAPD_LEVEL_WARNING, "too short EAP packet " "received from authentication server"); - os_free(eap); + wpabuf_free(eap); sm->eap_if->aaaEapNoReq = TRUE; return; } - if (len > sizeof(*hdr)) - eap_type = eap[sizeof(*hdr)]; + if (wpabuf_len(eap) > sizeof(*hdr)) + eap_type = (wpabuf_head_u8(eap))[sizeof(*hdr)]; - hdr = (struct eap_hdr *) eap; + hdr = wpabuf_head(eap); switch (hdr->code) { case EAP_CODE_REQUEST: if (eap_type >= 0) @@ -1071,7 +1045,7 @@ sm->eap_if->aaaEapReq = TRUE; wpabuf_free(sm->eap_if->aaaEapReqData); - sm->eap_if->aaaEapReqData = wpabuf_alloc_ext_data(eap, len); + sm->eap_if->aaaEapReqData = eap; } @@ -1136,7 +1110,7 @@ if (count <= 0) return; - nclass = os_zalloc(count * sizeof(struct radius_attr_data)); + nclass = os_calloc(count, sizeof(struct radius_attr_data)); if (nclass == NULL) return; @@ -1187,13 +1161,10 @@ NULL) < 0) return; - identity = os_malloc(len + 1); + identity = (u8 *) dup_binstr(buf, len); if (identity == NULL) return; - os_memcpy(identity, buf, len); - identity[len] = '\0'; - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, HOSTAPD_LEVEL_DEBUG, "old identity '%s' updated with " "User-Name from Access-Accept '%s'", @@ -1206,6 +1177,32 @@ } +/* Update CUI based on Chargeable-User-Identity attribute in Access-Accept */ +static void ieee802_1x_update_sta_cui(struct hostapd_data *hapd, + struct sta_info *sta, + struct radius_msg *msg) +{ + struct eapol_state_machine *sm = sta->eapol_sm; + struct wpabuf *cui; + u8 *buf; + size_t len; + + if (sm == NULL) + return; + + if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_CHARGEABLE_USER_IDENTITY, + &buf, &len, NULL) < 0) + return; + + cui = wpabuf_alloc_copy(buf, len); + if (cui == NULL) + return; + + wpabuf_free(sm->radius_cui); + sm->radius_cui = cui; +} + + struct sta_id_search { u8 identifier; struct eapol_state_machine *sm; @@ -1280,15 +1277,14 @@ "EAP-Message"); } else if (radius_msg_verify(msg, shared_secret, shared_secret_len, req, 1)) { - printf("Incoming RADIUS packet did not have correct " - "Message-Authenticator - dropped\n"); + wpa_printf(MSG_INFO, "Incoming RADIUS packet did not have correct Message-Authenticator - dropped"); return RADIUS_RX_INVALID_AUTHENTICATOR; } if (hdr->code != RADIUS_CODE_ACCESS_ACCEPT && hdr->code != RADIUS_CODE_ACCESS_REJECT && hdr->code != RADIUS_CODE_ACCESS_CHALLENGE) { - printf("Unknown RADIUS message code\n"); + wpa_printf(MSG_INFO, "Unknown RADIUS message code"); return RADIUS_RX_UNKNOWN; } @@ -1332,8 +1328,7 @@ sta->vlan_id = radius_msg_get_vlanid(msg); } if (sta->vlan_id > 0 && - hostapd_get_vlan_id_ifname(hapd->conf->vlan, - sta->vlan_id)) { + hostapd_vlan_id_valid(hapd->conf->vlan, sta->vlan_id)) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS, HOSTAPD_LEVEL_INFO, @@ -1365,6 +1360,7 @@ shared_secret_len); ieee802_1x_store_radius_class(hapd, sta, msg); ieee802_1x_update_sta_identity(hapd, sta, msg); + ieee802_1x_update_sta_cui(hapd, sta, msg); if (sm->eap_if->eapKeyAvailable && wpa_auth_pmksa_add(sta->wpa_sm, sm->eapol_key_crypt, session_timeout_set ? @@ -1430,6 +1426,9 @@ * request and we cannot continue EAP processing (EAP-Failure * could only be sent if the EAP peer actually replied). */ + wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "EAP Timeout, STA " MACSTR, + MAC2STR(sta->addr)); + sm->eap_if->portEnabled = FALSE; ap_sta_disconnect(hapd, sta, sta->addr, WLAN_REASON_PREV_AUTH_NOT_VALID); @@ -1449,7 +1448,7 @@ if (eapol->default_wep_key == NULL || random_get_bytes(eapol->default_wep_key, hapd->conf->default_wep_key_len)) { - printf("Could not generate random WEP key.\n"); + wpa_printf(MSG_INFO, "Could not generate random WEP key"); os_free(eapol->default_wep_key); eapol->default_wep_key = NULL; return -1; @@ -1582,19 +1581,15 @@ { struct hostapd_data *hapd = ctx; const struct hostapd_eap_user *eap_user; - int i, count; + int i; - eap_user = hostapd_get_eap_user(hapd->conf, identity, - identity_len, phase2); + eap_user = hostapd_get_eap_user(hapd, identity, identity_len, phase2); if (eap_user == NULL) return -1; os_memset(user, 0, sizeof(*user)); user->phase2 = phase2; - count = EAP_USER_MAX_METHODS; - if (count > EAP_MAX_METHODS) - count = EAP_MAX_METHODS; - for (i = 0; i < count; i++) { + for (i = 0; i < EAP_MAX_METHODS; i++) { user->methods[i].vendor = eap_user->methods[i].vendor; user->methods[i].method = eap_user->methods[i].method; } @@ -1723,6 +1718,13 @@ conf.fragment_size = hapd->conf->fragment_size; conf.pwd_group = hapd->conf->pwd_group; conf.pbc_in_m1 = hapd->conf->pbc_in_m1; + if (hapd->conf->server_id) { + conf.server_id = (const u8 *) hapd->conf->server_id; + conf.server_id_len = os_strlen(hapd->conf->server_id); + } else { + conf.server_id = (const u8 *) "hostapd"; + conf.server_id_len = 7; + } os_memset(&cb, 0, sizeof(cb)); cb.eapol_send = ieee802_1x_eapol_send; @@ -1783,15 +1785,13 @@ const u8 *buf, size_t len, int ack) { struct ieee80211_hdr *hdr; - struct ieee802_1x_hdr *xhdr; - struct ieee802_1x_eapol_key *key; u8 *pos; const unsigned char rfc1042_hdr[ETH_ALEN] = { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 }; if (sta == NULL) return -1; - if (len < sizeof(*hdr) + sizeof(rfc1042_hdr) + 2 + sizeof(*xhdr)) + if (len < sizeof(*hdr) + sizeof(rfc1042_hdr) + 2) return 0; hdr = (struct ieee80211_hdr *) buf; @@ -1803,16 +1803,30 @@ return 0; pos += 2; - xhdr = (struct ieee802_1x_hdr *) pos; - pos += sizeof(*xhdr); + return ieee802_1x_eapol_tx_status(hapd, sta, pos, buf + len - pos, + ack); +} + +int ieee802_1x_eapol_tx_status(struct hostapd_data *hapd, struct sta_info *sta, + const u8 *buf, int len, int ack) +{ + const struct ieee802_1x_hdr *xhdr = + (const struct ieee802_1x_hdr *) buf; + const u8 *pos = buf + sizeof(*xhdr); + struct ieee802_1x_eapol_key *key; + + if (len < (int) sizeof(*xhdr)) + return 0; wpa_printf(MSG_DEBUG, "IEEE 802.1X: " MACSTR " TX status - version=%d " "type=%d length=%d - ack=%d", MAC2STR(sta->addr), xhdr->version, xhdr->type, be_to_host16(xhdr->length), ack); - if (xhdr->type == IEEE802_1X_TYPE_EAPOL_KEY && - pos + sizeof(struct wpa_eapol_key) <= buf + len) { + if (xhdr->type != IEEE802_1X_TYPE_EAPOL_KEY) + return 0; + + if (pos + sizeof(struct wpa_eapol_key) <= buf + len) { const struct wpa_eapol_key *wpa; wpa = (const struct wpa_eapol_key *) pos; if (wpa->type == EAPOL_KEY_TYPE_RSN || @@ -1826,8 +1840,7 @@ * retransmitted in case of failure. Try to re-send failed EAPOL-Key * packets couple of times because otherwise STA keys become * unsynchronized with AP. */ - if (xhdr->type == IEEE802_1X_TYPE_EAPOL_KEY && !ack && - pos + sizeof(*key) <= buf + len) { + if (!ack && pos + sizeof(*key) <= buf + len) { key = (struct ieee802_1x_eapol_key *) pos; hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, HOSTAPD_LEVEL_DEBUG, "did not Ack EAPOL-Key " @@ -1871,6 +1884,14 @@ } +struct wpabuf * ieee802_1x_get_radius_cui(struct eapol_state_machine *sm) +{ + if (sm == NULL) + return NULL; + return sm->radius_cui; +} + + const u8 * ieee802_1x_get_key(struct eapol_state_machine *sm, size_t *len) { *len = 0; @@ -1931,7 +1952,7 @@ { int len = 0, ret; struct eapol_state_machine *sm = sta->eapol_sm; - struct os_time t; + struct os_reltime diff; if (sm == NULL) return 0; @@ -2046,7 +2067,7 @@ len += ret; /* dot1xAuthSessionStatsTable */ - os_get_time(&t); + os_reltime_age(&sta->acct_session_start, &diff); ret = os_snprintf(buf + len, buflen - len, /* TODO: dot1xAuthSessionOctetsRx */ /* TODO: dot1xAuthSessionOctetsTx */ @@ -2061,12 +2082,23 @@ (wpa_key_mgmt_wpa_ieee8021x( wpa_auth_sta_key_mgmt(sta->wpa_sm))) ? 1 : 2, - (unsigned int) (t.sec - sta->acct_session_start), + (unsigned int) diff.sec, sm->identity); if (ret < 0 || (size_t) ret >= buflen - len) return len; len += ret; + ret = os_snprintf(buf + len, buflen - len, + "last_eap_type_as=%d (%s)\n" + "last_eap_type_sta=%d (%s)\n", + sm->eap_type_authsrv, + eap_server_get_name(0, sm->eap_type_authsrv), + sm->eap_type_supp, + eap_server_get_name(0, sm->eap_type_supp)); + if (ret < 0 || (size_t) ret >= buflen - len) + return len; + len += ret; + return len; } @@ -2099,8 +2131,8 @@ * EAP-FAST with anonymous provisioning, may require another * EAPOL authentication to be started to complete connection. */ - wpa_printf(MSG_DEBUG, "IEEE 802.1X: Force disconnection after " - "EAP-Failure"); + 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. @@ -2108,5 +2140,6 @@ os_sleep(0, 10000); ap_sta_disconnect(hapd, sta, sta->addr, WLAN_REASON_IEEE_802_1X_AUTH_FAILED); + hostapd_wps_eap_completed(hapd); } } diff -Nru wpa-1.0/src/ap/ieee802_1x.h wpa-2.1/src/ap/ieee802_1x.h --- wpa-1.0/src/ap/ieee802_1x.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/ap/ieee802_1x.h 2014-02-04 11:23:35.000000000 +0000 @@ -1,15 +1,9 @@ /* * hostapd / IEEE 802.1X-2004 Authenticator - * Copyright (c) 2002-2007, Jouni Malinen + * Copyright (c) 2002-2012, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef IEEE802_1X_H @@ -20,38 +14,8 @@ struct eapol_state_machine; struct hostapd_config; struct hostapd_bss_config; - -#ifdef _MSC_VER -#pragma pack(push, 1) -#endif /* _MSC_VER */ - -/* RFC 3580, 4. RC4 EAPOL-Key Frame */ - -struct ieee802_1x_eapol_key { - u8 type; - u16 key_length; - u8 replay_counter[8]; /* does not repeat within the life of the keying - * material used to encrypt the Key field; - * 64-bit NTP timestamp MAY be used here */ - u8 key_iv[16]; /* cryptographically random number */ - u8 key_index; /* key flag in the most significant bit: - * 0 = broadcast (default key), - * 1 = unicast (key mapping key); key index is in the - * 7 least significant bits */ - u8 key_signature[16]; /* HMAC-MD5 message integrity check computed with - * MS-MPPE-Send-Key as the key */ - - /* followed by key: if packet body length = 44 + key length, then the - * key field (of key_length bytes) contains the key in encrypted form; - * if packet body length = 44, key field is absent and key_length - * represents the number of least significant octets from - * MS-MPPE-Send-Key attribute to be used as the keying material; - * RC4 key used in encryption = Key-IV + MS-MPPE-Recv-Key */ -} STRUCT_PACKED; - -#ifdef _MSC_VER -#pragma pack(pop) -#endif /* _MSC_VER */ +struct hostapd_radius_attr; +struct radius_msg; void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf, @@ -68,9 +32,12 @@ void ieee802_1x_deinit(struct hostapd_data *hapd); int ieee802_1x_tx_status(struct hostapd_data *hapd, struct sta_info *sta, const u8 *buf, size_t len, int ack); +int ieee802_1x_eapol_tx_status(struct hostapd_data *hapd, struct sta_info *sta, + const u8 *data, int len, int ack); u8 * ieee802_1x_get_identity(struct eapol_state_machine *sm, size_t *len); u8 * ieee802_1x_get_radius_class(struct eapol_state_machine *sm, size_t *len, int idx); +struct wpabuf * ieee802_1x_get_radius_cui(struct eapol_state_machine *sm); const u8 * ieee802_1x_get_key(struct eapol_state_machine *sm, size_t *len); void ieee802_1x_notify_port_enabled(struct eapol_state_machine *sm, int enabled); @@ -86,4 +53,9 @@ const char *radius_mode_txt(struct hostapd_data *hapd); int radius_sta_rate(struct hostapd_data *hapd, struct sta_info *sta); +int add_common_radius_attr(struct hostapd_data *hapd, + struct hostapd_radius_attr *req_attr, + struct sta_info *sta, + struct radius_msg *msg); + #endif /* IEEE802_1X_H */ diff -Nru wpa-1.0/src/ap/Makefile wpa-2.1/src/ap/Makefile --- wpa-1.0/src/ap/Makefile 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/ap/Makefile 2014-02-04 11:23:35.000000000 +0000 @@ -2,7 +2,7 @@ @echo Nothing to be made. clean: - rm -f *~ *.o *.d + rm -f *~ *.o *.d *.gcno *.gcda *.gcov install: @echo Nothing to be made. diff -Nru wpa-1.0/src/ap/p2p_hostapd.c wpa-2.1/src/ap/p2p_hostapd.c --- wpa-1.0/src/ap/p2p_hostapd.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/ap/p2p_hostapd.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * hostapd / P2P integration * Copyright (c) 2009-2010, Atheros Communications * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "utils/includes.h" diff -Nru wpa-1.0/src/ap/p2p_hostapd.h wpa-2.1/src/ap/p2p_hostapd.h --- wpa-1.0/src/ap/p2p_hostapd.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/ap/p2p_hostapd.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * hostapd / P2P integration * Copyright (c) 2009-2010, Atheros Communications * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef P2P_HOSTAPD_H diff -Nru wpa-1.0/src/ap/peerkey_auth.c wpa-2.1/src/ap/peerkey_auth.c --- wpa-1.0/src/ap/peerkey_auth.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/ap/peerkey_auth.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * hostapd - PeerKey for Direct Link Setup (DLS) * Copyright (c) 2006-2009, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "utils/includes.h" diff -Nru wpa-1.0/src/ap/pmksa_cache_auth.c wpa-2.1/src/ap/pmksa_cache_auth.c --- wpa-1.0/src/ap/pmksa_cache_auth.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/ap/pmksa_cache_auth.c 2014-02-04 11:23:35.000000000 +0000 @@ -1,15 +1,9 @@ /* * hostapd - PMKSA cache for IEEE 802.11i RSN - * Copyright (c) 2004-2008, Jouni Malinen + * Copyright (c) 2004-2008, 2012, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "utils/includes.h" @@ -46,6 +40,7 @@ if (entry == NULL) return; os_free(entry->identity); + wpabuf_free(entry->cui); #ifndef CONFIG_NO_RADIUS radius_free_class(&entry->radius_class); #endif /* CONFIG_NO_RADIUS */ @@ -53,8 +48,8 @@ } -static void pmksa_cache_free_entry(struct rsn_pmksa_cache *pmksa, - struct rsn_pmksa_cache_entry *entry) +void pmksa_cache_free_entry(struct rsn_pmksa_cache *pmksa, + struct rsn_pmksa_cache_entry *entry) { struct rsn_pmksa_cache_entry *pos, *prev; @@ -96,15 +91,13 @@ static void pmksa_cache_expire(void *eloop_ctx, void *timeout_ctx) { struct rsn_pmksa_cache *pmksa = eloop_ctx; - struct os_time now; + struct os_reltime now; - os_get_time(&now); + os_get_reltime(&now); while (pmksa->pmksa && pmksa->pmksa->expiration <= now.sec) { - struct rsn_pmksa_cache_entry *entry = pmksa->pmksa; - pmksa->pmksa = entry->next; wpa_printf(MSG_DEBUG, "RSN: expired PMKSA cache entry for " - MACSTR, MAC2STR(entry->spa)); - pmksa_cache_free_entry(pmksa, entry); + MACSTR, MAC2STR(pmksa->pmksa->spa)); + pmksa_cache_free_entry(pmksa, pmksa->pmksa); } pmksa_cache_set_expiration(pmksa); @@ -114,12 +107,12 @@ static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa) { int sec; - struct os_time now; + struct os_reltime now; eloop_cancel_timeout(pmksa_cache_expire, pmksa, NULL); if (pmksa->pmksa == NULL) return; - os_get_time(&now); + os_get_reltime(&now); sec = pmksa->pmksa->expiration - now.sec; if (sec < 0) sec = 0; @@ -142,6 +135,9 @@ } } + if (eapol->radius_cui) + entry->cui = wpabuf_dup(eapol->radius_cui); + #ifndef CONFIG_NO_RADIUS radius_copy_class(&entry->radius_class, &eapol->radius_class); #endif /* CONFIG_NO_RADIUS */ @@ -169,6 +165,11 @@ eapol->identity, eapol->identity_len); } + if (entry->cui) { + wpabuf_free(eapol->radius_cui); + eapol->radius_cui = wpabuf_dup(entry->cui); + } + #ifndef CONFIG_NO_RADIUS radius_free_class(&eapol->radius_class); radius_copy_class(&eapol->radius_class, &entry->radius_class); @@ -208,6 +209,8 @@ pmksa->pmkid[PMKID_HASH(entry->pmkid)] = entry; pmksa->pmksa_count++; + if (prev == NULL) + pmksa_cache_set_expiration(pmksa); wpa_printf(MSG_DEBUG, "RSN: added PMKSA cache entry for " MACSTR, MAC2STR(entry->spa)); wpa_hexdump(MSG_DEBUG, "RSN: added PMKID", entry->pmkid, PMKID_LEN); @@ -238,7 +241,7 @@ struct eapol_state_machine *eapol, int akmp) { struct rsn_pmksa_cache_entry *entry, *pos; - struct os_time now; + struct os_reltime now; if (pmk_len > PMK_LEN) return NULL; @@ -250,7 +253,7 @@ entry->pmk_len = pmk_len; rsn_pmkid(pmk, pmk_len, aa, spa, entry->pmkid, wpa_key_mgmt_sha256(akmp)); - os_get_time(&now); + os_get_reltime(&now); entry->expiration = now.sec; if (session_timeout > 0) entry->expiration += session_timeout; @@ -305,6 +308,8 @@ old_entry->identity_len); } } + if (old_entry->cui) + entry->cui = wpabuf_dup(old_entry->cui); #ifndef CONFIG_NO_RADIUS radius_copy_class(&entry->radius_class, &old_entry->radius_class); #endif /* CONFIG_NO_RADIUS */ diff -Nru wpa-1.0/src/ap/pmksa_cache_auth.h wpa-2.1/src/ap/pmksa_cache_auth.h --- wpa-1.0/src/ap/pmksa_cache_auth.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/ap/pmksa_cache_auth.h 2014-02-04 11:23:35.000000000 +0000 @@ -1,15 +1,9 @@ /* * hostapd - PMKSA cache for IEEE 802.11i RSN - * Copyright (c) 2004-2008, Jouni Malinen + * Copyright (c) 2004-2008, 2012, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef PMKSA_CACHE_H @@ -31,6 +25,7 @@ u8 *identity; size_t identity_len; + struct wpabuf *cui; struct radius_class_data radius_class; u8 eap_type_authsrv; int vlan_id; @@ -60,5 +55,7 @@ const u8 *aa, const u8 *pmkid); void pmksa_cache_to_eapol_data(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); #endif /* PMKSA_CACHE_H */ diff -Nru wpa-1.0/src/ap/preauth_auth.c wpa-2.1/src/ap/preauth_auth.c --- wpa-1.0/src/ap/preauth_auth.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/ap/preauth_auth.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * hostapd - Authenticator for IEEE 802.11i RSN pre-authentication * Copyright (c) 2004-2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "utils/includes.h" diff -Nru wpa-1.0/src/ap/preauth_auth.h wpa-2.1/src/ap/preauth_auth.h --- wpa-1.0/src/ap/preauth_auth.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/ap/preauth_auth.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * hostapd - Authenticator for IEEE 802.11i RSN pre-authentication * Copyright (c) 2004-2005, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef PREAUTH_H diff -Nru wpa-1.0/src/ap/sta_info.c wpa-2.1/src/ap/sta_info.c --- wpa-1.0/src/ap/sta_info.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/ap/sta_info.c 2014-02-04 11:23:35.000000000 +0000 @@ -1,15 +1,9 @@ /* * hostapd / Station table - * Copyright (c) 2002-2011, Jouni Malinen + * Copyright (c) 2002-2013, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "utils/includes.h" @@ -18,14 +12,15 @@ #include "utils/eloop.h" #include "common/ieee802_11_defs.h" #include "common/wpa_ctrl.h" +#include "common/sae.h" #include "radius/radius.h" #include "radius/radius_client.h" -#include "drivers/driver.h" #include "p2p/p2p.h" #include "hostapd.h" #include "accounting.h" #include "ieee802_1x.h" #include "ieee802_11.h" +#include "ieee802_11_auth.h" #include "wpa_auth.h" #include "preauth_auth.h" #include "ap_config.h" @@ -34,6 +29,7 @@ #include "vlan_init.h" #include "p2p_hostapd.h" #include "ap_drv_ops.h" +#include "gas_serv.h" #include "sta_info.h" static void ap_sta_remove_in_other_bss(struct hostapd_data *hapd, @@ -73,6 +69,30 @@ } +#ifdef CONFIG_P2P +struct sta_info * ap_get_sta_p2p(struct hostapd_data *hapd, const u8 *addr) +{ + struct sta_info *sta; + + for (sta = hapd->sta_list; sta; sta = sta->next) { + const u8 *p2p_dev_addr; + + if (sta->p2p_ie == NULL) + continue; + + p2p_dev_addr = p2p_get_go_dev_addr(sta->p2p_ie); + if (p2p_dev_addr == NULL) + continue; + + if (os_memcmp(p2p_dev_addr, addr, ETH_ALEN) == 0) + return sta; + } + + return NULL; +} +#endif /* CONFIG_P2P */ + + static void ap_sta_list_del(struct hostapd_data *hapd, struct sta_info *sta) { struct sta_info *tmp; @@ -132,7 +152,7 @@ ap_sta_set_authorized(hapd, sta, 0); if (sta->flags & WLAN_STA_WDS) - hostapd_set_wds_sta(hapd, sta->addr, sta->aid, 0); + hostapd_set_wds_sta(hapd, NULL, sta->addr, sta->aid, 0); if (!(sta->flags & WLAN_STA_PREAUTH)) hostapd_drv_sta_remove(hapd, sta->addr); @@ -200,6 +220,8 @@ if (set_beacon) ieee802_11_set_beacons(hapd->iface); + wpa_printf(MSG_DEBUG, "%s: cancel ap_handle_timer for " MACSTR, + __func__, MAC2STR(sta->addr)); eloop_cancel_timeout(ap_handle_timer, hapd, sta); eloop_cancel_timeout(ap_handle_session_timer, hapd, sta); eloop_cancel_timeout(ap_sta_deauth_cb_timeout, hapd, sta); @@ -209,7 +231,8 @@ wpa_auth_sta_deinit(sta->wpa_sm); rsn_preauth_free_station(hapd, sta); #ifndef CONFIG_NO_RADIUS - radius_client_flush_auth(hapd->radius, sta->addr); + if (hapd->radius) + radius_client_flush_auth(hapd->radius, sta->addr); #endif /* CONFIG_NO_RADIUS */ os_free(sta->last_assoc_req); @@ -224,10 +247,29 @@ p2p_group_notif_disassoc(hapd->p2p_group, sta->addr); #endif /* CONFIG_P2P */ +#ifdef CONFIG_INTERWORKING + if (sta->gas_dialog) { + int i; + for (i = 0; i < GAS_DIALOG_MAX; i++) + gas_serv_dialog_clear(&sta->gas_dialog[i]); + os_free(sta->gas_dialog); + } +#endif /* CONFIG_INTERWORKING */ + wpabuf_free(sta->wps_ie); wpabuf_free(sta->p2p_ie); + wpabuf_free(sta->hs20_ie); os_free(sta->ht_capabilities); + os_free(sta->vht_capabilities); + hostapd_free_psk_list(sta->psk); + os_free(sta->identity); + os_free(sta->radius_cui); + +#ifdef CONFIG_SAE + sae_clear_data(sta->sae); + os_free(sta->sae); +#endif /* CONFIG_SAE */ os_free(sta); } @@ -266,7 +308,11 @@ struct hostapd_data *hapd = eloop_ctx; struct sta_info *sta = timeout_ctx; 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, + sta->timeout_next); if (sta->timeout_next == STA_REMOVE) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_INFO, "deauthenticated due to " @@ -279,6 +325,12 @@ (sta->timeout_next == STA_NULLFUNC || sta->timeout_next == STA_DISASSOC)) { int inactive_sec; + /* + * Add random value to timeout so that we don't end up bouncing + * all stations at the same time if we have lots of associated + * stations that are idle (but keep re-associating). + */ + int fuzz = os_random() % 20; inactive_sec = hostapd_drv_get_inact_sec(hapd, sta->addr); if (inactive_sec == -1) { wpa_msg(hapd->msg_ctx, MSG_DEBUG, @@ -290,7 +342,7 @@ * Anyway, try again after the next inactivity timeout, * but do not disconnect the station now. */ - next_time = hapd->conf->ap_max_inactivity; + next_time = hapd->conf->ap_max_inactivity + fuzz; } else if (inactive_sec < hapd->conf->ap_max_inactivity && sta->flags & WLAN_STA_ASSOC) { /* station activity detected; reset timeout state */ @@ -298,7 +350,7 @@ "Station " MACSTR " has been active %is ago", MAC2STR(sta->addr), inactive_sec); sta->timeout_next = STA_NULLFUNC; - next_time = hapd->conf->ap_max_inactivity - + next_time = hapd->conf->ap_max_inactivity + fuzz - inactive_sec; } else { wpa_msg(hapd->msg_ctx, MSG_DEBUG, @@ -306,12 +358,16 @@ "inactive too long: %d sec, max allowed: %d", MAC2STR(sta->addr), inactive_sec, hapd->conf->ap_max_inactivity); + + if (hapd->conf->skip_inactivity_poll) + sta->timeout_next = STA_DISASSOC; } } if ((sta->flags & WLAN_STA_ASSOC) && sta->timeout_next == STA_DISASSOC && - !(sta->flags & WLAN_STA_PENDING_POLL)) { + !(sta->flags & WLAN_STA_PENDING_POLL) && + !hapd->conf->skip_inactivity_poll) { wpa_msg(hapd->msg_ctx, MSG_DEBUG, "Station " MACSTR " has ACKed data poll", MAC2STR(sta->addr)); /* data nullfunc frame poll did not produce TX errors; assume @@ -321,6 +377,9 @@ } if (next_time) { + wpa_printf(MSG_DEBUG, "%s: register ap_handle_timer timeout " + "for " MACSTR " (%lu seconds)", + __func__, MAC2STR(sta->addr), next_time); eloop_register_timeout(next_time, 0, ap_handle_timer, hapd, sta); return; @@ -335,28 +394,35 @@ } else if (sta->timeout_next != STA_REMOVE) { int deauth = sta->timeout_next == STA_DEAUTH; - wpa_printf(MSG_DEBUG, "Sending %s info to STA " MACSTR, - deauth ? "deauthentication" : "disassociation", - MAC2STR(sta->addr)); + wpa_dbg(hapd->msg_ctx, MSG_DEBUG, + "Timeout, sending %s info to STA " MACSTR, + deauth ? "deauthentication" : "disassociation", + MAC2STR(sta->addr)); if (deauth) { hostapd_drv_sta_deauth( hapd, sta->addr, WLAN_REASON_PREV_AUTH_NOT_VALID); } else { - hostapd_drv_sta_disassoc( - hapd, sta->addr, - WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY); + reason = (sta->timeout_next == STA_DISASSOC) ? + WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY : + WLAN_REASON_PREV_AUTH_NOT_VALID; + + hostapd_drv_sta_disassoc(hapd, sta->addr, reason); } } switch (sta->timeout_next) { case STA_NULLFUNC: sta->timeout_next = STA_DISASSOC; + wpa_printf(MSG_DEBUG, "%s: register ap_handle_timer timeout " + "for " MACSTR " (%d seconds - AP_DISASSOC_DELAY)", + __func__, MAC2STR(sta->addr), AP_DISASSOC_DELAY); eloop_register_timeout(AP_DISASSOC_DELAY, 0, ap_handle_timer, hapd, sta); break; case STA_DISASSOC: + case STA_DISASSOC_FROM_CLI: ap_sta_set_authorized(hapd, sta, 0); sta->flags &= ~WLAN_STA_ASSOC; ieee802_1x_notify_port_enabled(sta->eapol_sm, 0); @@ -368,17 +434,22 @@ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_INFO, "disassociated due to " "inactivity"); + reason = (sta->timeout_next == STA_DISASSOC) ? + WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY : + WLAN_REASON_PREV_AUTH_NOT_VALID; sta->timeout_next = STA_DEAUTH; + wpa_printf(MSG_DEBUG, "%s: register ap_handle_timer timeout " + "for " MACSTR " (%d seconds - AP_DEAUTH_DELAY)", + __func__, MAC2STR(sta->addr), AP_DEAUTH_DELAY); eloop_register_timeout(AP_DEAUTH_DELAY, 0, ap_handle_timer, hapd, sta); - mlme_disassociate_indication( - hapd, sta, WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY); + mlme_disassociate_indication(hapd, sta, reason); break; case STA_DEAUTH: case STA_REMOVE: hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_INFO, "deauthenticated due to " - "inactivity"); + "inactivity (timer DEAUTH/REMOVE)"); if (!sta->acct_terminate_cause) sta->acct_terminate_cause = RADIUS_ACCT_TERMINATE_CAUSE_IDLE_TIMEOUT; @@ -397,8 +468,14 @@ struct sta_info *sta = timeout_ctx; u8 addr[ETH_ALEN]; - if (!(sta->flags & WLAN_STA_AUTH)) + if (!(sta->flags & WLAN_STA_AUTH)) { + if (sta->flags & WLAN_STA_GAS) { + wpa_printf(MSG_DEBUG, "GAS: Remove temporary STA " + "entry " MACSTR, MAC2STR(sta->addr)); + ap_free_sta(hapd, sta); + } return; + } mlme_deauthenticate_indication(hapd, sta, WLAN_REASON_PREV_AUTH_NOT_VALID); @@ -413,6 +490,18 @@ } +void ap_sta_replenish_timeout(struct hostapd_data *hapd, struct sta_info *sta, + u32 session_timeout) +{ + if (eloop_replenish_timeout(session_timeout, 0, + ap_handle_session_timer, hapd, sta) == 1) { + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, "setting session timeout " + "to %d seconds", session_timeout); + } +} + + void ap_sta_session_timeout(struct hostapd_data *hapd, struct sta_info *sta, u32 session_timeout) { @@ -453,10 +542,18 @@ return NULL; } sta->acct_interim_interval = hapd->conf->acct_interim_interval; + accounting_sta_get_id(hapd, sta); + + if (!(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_INACTIVITY_TIMER)) { + wpa_printf(MSG_DEBUG, "%s: register ap_handle_timer timeout " + "for " MACSTR " (%d seconds - ap_max_inactivity)", + __func__, MAC2STR(addr), + hapd->conf->ap_max_inactivity); + eloop_register_timeout(hapd->conf->ap_max_inactivity, 0, + ap_handle_timer, hapd, sta); + } /* initialize STA info data */ - eloop_register_timeout(hapd->conf->ap_max_inactivity, 0, - ap_handle_timer, hapd, sta); os_memcpy(sta->addr, addr, ETH_ALEN); sta->next = hapd->sta_list; hapd->sta_list = sta; @@ -525,9 +622,14 @@ { wpa_printf(MSG_DEBUG, "%s: disassociate STA " MACSTR, hapd->conf->iface, MAC2STR(sta->addr)); - sta->flags &= ~WLAN_STA_ASSOC; + sta->flags &= ~(WLAN_STA_ASSOC | WLAN_STA_ASSOC_REQ_OK); 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)", + __func__, MAC2STR(sta->addr), + AP_MAX_INACTIVITY_AFTER_DISASSOC); eloop_cancel_timeout(ap_handle_timer, hapd, sta); eloop_register_timeout(AP_MAX_INACTIVITY_AFTER_DISASSOC, 0, ap_handle_timer, hapd, sta); @@ -561,6 +663,11 @@ sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC); ap_sta_set_authorized(hapd, sta, 0); sta->timeout_next = STA_REMOVE; + wpa_printf(MSG_DEBUG, "%s: reschedule ap_handle_timer timeout " + "for " MACSTR " (%d seconds - " + "AP_MAX_INACTIVITY_AFTER_DEAUTH)", + __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, ap_handle_timer, hapd, sta); @@ -576,6 +683,23 @@ } +#ifdef CONFIG_WPS +int ap_sta_wps_cancel(struct hostapd_data *hapd, + struct sta_info *sta, void *ctx) +{ + if (sta && (sta->flags & WLAN_STA_WPS)) { + ap_sta_deauthenticate(hapd, sta, + WLAN_REASON_PREV_AUTH_NOT_VALID); + wpa_printf(MSG_DEBUG, "WPS: %s: Deauth sta=" MACSTR, + __func__, MAC2STR(sta->addr)); + return 1; + } + + return 0; +} +#endif /* CONFIG_WPS */ + + int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta, int old_vlanid) { @@ -605,15 +729,19 @@ if (sta->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 (vlan->vlan_id == sta->vlan_id || - vlan->vlan_id == VLAN_ID_WILDCARD) { - iface = vlan->ifname; + 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; } if (sta->vlan_id > 0 && vlan == NULL) { @@ -696,9 +824,9 @@ int ap_check_sa_query_timeout(struct hostapd_data *hapd, struct sta_info *sta) { u32 tu; - struct os_time now, passed; - os_get_time(&now); - os_time_sub(&now, &sta->sa_query_start, &passed); + struct os_reltime now, passed; + os_get_reltime(&now); + os_reltime_sub(&now, &sta->sa_query_start, &passed); tu = (passed.sec * 1000000 + passed.usec) / 1024; if (hapd->conf->assoc_sa_query_max_timeout < tu) { hostapd_logger(hapd, sta->addr, @@ -728,13 +856,14 @@ ap_check_sa_query_timeout(hapd, sta)) return; - nbuf = os_realloc(sta->sa_query_trans_id, - (sta->sa_query_count + 1) * WLAN_SA_QUERY_TR_ID_LEN); + nbuf = os_realloc_array(sta->sa_query_trans_id, + sta->sa_query_count + 1, + WLAN_SA_QUERY_TR_ID_LEN); if (nbuf == NULL) return; if (sta->sa_query_count == 0) { /* Starting a new SA Query procedure */ - os_get_time(&sta->sa_query_start); + os_get_reltime(&sta->sa_query_start); } trans_id = nbuf + sta->sa_query_count * WLAN_SA_QUERY_TR_ID_LEN; sta->sa_query_trans_id = nbuf; @@ -751,9 +880,7 @@ HOSTAPD_LEVEL_DEBUG, "association SA Query attempt %d", sta->sa_query_count); -#ifdef NEED_AP_MLME ieee802_11_send_sa_query_req(hapd, sta->addr, trans_id); -#endif /* NEED_AP_MLME */ } @@ -778,51 +905,60 @@ int authorized) { const u8 *dev_addr = NULL; + char buf[100]; +#ifdef CONFIG_P2P + u8 addr[ETH_ALEN]; + u8 ip_addr_buf[4]; +#endif /* CONFIG_P2P */ + if (!!authorized == !!(sta->flags & WLAN_STA_AUTHORIZED)) return; #ifdef CONFIG_P2P - dev_addr = p2p_group_get_dev_addr(hapd->p2p_group, sta->addr); + if (hapd->p2p_group == NULL) { + if (sta->p2p_ie != NULL && + p2p_parse_dev_addr_in_p2p_ie(sta->p2p_ie, addr) == 0) + dev_addr = addr; + } else + dev_addr = p2p_group_get_dev_addr(hapd->p2p_group, sta->addr); #endif /* CONFIG_P2P */ + if (dev_addr) + os_snprintf(buf, sizeof(buf), MACSTR " p2p_dev_addr=" MACSTR, + MAC2STR(sta->addr), MAC2STR(dev_addr)); + else + os_snprintf(buf, sizeof(buf), MACSTR, MAC2STR(sta->addr)); + if (authorized) { - if (dev_addr) - wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_CONNECTED - MACSTR " p2p_dev_addr=" MACSTR, - MAC2STR(sta->addr), MAC2STR(dev_addr)); - else - wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_CONNECTED - MACSTR, MAC2STR(sta->addr)); + char ip_addr[100]; + ip_addr[0] = '\0'; +#ifdef CONFIG_P2P + if (wpa_auth_get_ip_addr(sta->wpa_sm, ip_addr_buf) == 0) { + os_snprintf(ip_addr, sizeof(ip_addr), + " ip_addr=%u.%u.%u.%u", + ip_addr_buf[0], ip_addr_buf[1], + ip_addr_buf[2], ip_addr_buf[3]); + } +#endif /* CONFIG_P2P */ + + wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_CONNECTED "%s%s", + buf, ip_addr); + if (hapd->msg_ctx_parent && - hapd->msg_ctx_parent != hapd->msg_ctx && dev_addr) - wpa_msg(hapd->msg_ctx_parent, MSG_INFO, - AP_STA_CONNECTED MACSTR " p2p_dev_addr=" - MACSTR, - MAC2STR(sta->addr), MAC2STR(dev_addr)); - else if (hapd->msg_ctx_parent && - hapd->msg_ctx_parent != hapd->msg_ctx) - wpa_msg(hapd->msg_ctx_parent, MSG_INFO, - AP_STA_CONNECTED MACSTR, MAC2STR(sta->addr)); + 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); sta->flags |= WLAN_STA_AUTHORIZED; } else { - if (dev_addr) - wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_DISCONNECTED - MACSTR " p2p_dev_addr=" MACSTR, - MAC2STR(sta->addr), MAC2STR(dev_addr)); - else - wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_DISCONNECTED - MACSTR, MAC2STR(sta->addr)); + wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_DISCONNECTED "%s", buf); + if (hapd->msg_ctx_parent && - hapd->msg_ctx_parent != hapd->msg_ctx && dev_addr) - wpa_msg(hapd->msg_ctx_parent, MSG_INFO, - AP_STA_DISCONNECTED MACSTR " p2p_dev_addr=" - MACSTR, MAC2STR(sta->addr), MAC2STR(dev_addr)); - else if (hapd->msg_ctx_parent && - hapd->msg_ctx_parent != hapd->msg_ctx) - wpa_msg(hapd->msg_ctx_parent, MSG_INFO, - AP_STA_DISCONNECTED MACSTR, - MAC2STR(sta->addr)); + hapd->msg_ctx_parent != hapd->msg_ctx) + wpa_msg_no_global(hapd->msg_ctx_parent, MSG_INFO, + AP_STA_DISCONNECTED "%s", buf); + sta->flags &= ~WLAN_STA_AUTHORIZED; } @@ -848,6 +984,11 @@ 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 " + "for " MACSTR " (%d seconds - " + "AP_MAX_INACTIVITY_AFTER_DEAUTH)", + __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, ap_handle_timer, hapd, sta); @@ -884,3 +1025,33 @@ eloop_cancel_timeout(ap_sta_disassoc_cb_timeout, hapd, sta); ap_sta_disassoc_cb_timeout(hapd, sta); } + + +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", + (flags & WLAN_STA_AUTH ? "[AUTH]" : ""), + (flags & WLAN_STA_ASSOC ? "[ASSOC]" : ""), + (flags & WLAN_STA_AUTHORIZED ? "[AUTHORIZED]" : ""), + (flags & WLAN_STA_PENDING_POLL ? "[PENDING_POLL" : + ""), + (flags & WLAN_STA_SHORT_PREAMBLE ? + "[SHORT_PREAMBLE]" : ""), + (flags & WLAN_STA_PREAUTH ? "[PREAUTH]" : ""), + (flags & WLAN_STA_WMM ? "[WMM]" : ""), + (flags & WLAN_STA_MFP ? "[MFP]" : ""), + (flags & WLAN_STA_WPS ? "[WPS]" : ""), + (flags & WLAN_STA_MAYBE_WPS ? "[MAYBE_WPS]" : ""), + (flags & WLAN_STA_WDS ? "[WDS]" : ""), + (flags & WLAN_STA_NONERP ? "[NonERP]" : ""), + (flags & WLAN_STA_WPS2 ? "[WPS2]" : ""), + (flags & WLAN_STA_GAS ? "[GAS]" : ""), + (flags & WLAN_STA_VHT ? "[VHT]" : ""), + (flags & WLAN_STA_WNM_SLEEP_MODE ? + "[WNM_SLEEP_MODE]" : "")); + + return res; +} diff -Nru wpa-1.0/src/ap/sta_info.h wpa-2.1/src/ap/sta_info.h --- wpa-1.0/src/ap/sta_info.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/ap/sta_info.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * hostapd / Station table * Copyright (c) 2002-2011, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef STA_INFO_H @@ -18,9 +12,6 @@ /* STA flags */ #define WLAN_STA_AUTH BIT(0) #define WLAN_STA_ASSOC BIT(1) -#define WLAN_STA_PS BIT(2) -#define WLAN_STA_TIM BIT(3) -#define WLAN_STA_PERM BIT(4) #define WLAN_STA_AUTHORIZED BIT(5) #define WLAN_STA_PENDING_POLL BIT(6) /* pending activity poll not ACKed */ #define WLAN_STA_SHORT_PREAMBLE BIT(7) @@ -33,6 +24,9 @@ #define WLAN_STA_WDS BIT(14) #define WLAN_STA_ASSOC_REQ_OK BIT(15) #define WLAN_STA_WPS2 BIT(16) +#define WLAN_STA_GAS BIT(17) +#define WLAN_STA_VHT BIT(18) +#define WLAN_STA_WNM_SLEEP_MODE BIT(19) #define WLAN_STA_PENDING_DISASSOC_CB BIT(29) #define WLAN_STA_PENDING_DEAUTH_CB BIT(30) #define WLAN_STA_NONERP BIT(31) @@ -52,6 +46,7 @@ u16 listen_interval; /* or beacon_int for APs */ u8 supported_rates[WLAN_SUPP_RATES_MAX]; int supported_rates_len; + u8 qosinfo; /* Valid when WLAN_STA_WMM is set */ unsigned int nonerp_set:1; unsigned int no_short_slot_time_set:1; @@ -60,12 +55,14 @@ unsigned int no_ht_set:1; unsigned int ht_20mhz_set:1; unsigned int no_p2p_set:1; + unsigned int qos_map_enabled:1; u16 auth_alg; u8 previous_ap[6]; enum { - STA_NULLFUNC = 0, STA_DISASSOC, STA_DEAUTH, STA_REMOVE + STA_NULLFUNC = 0, STA_DISASSOC, STA_DEAUTH, STA_REMOVE, + STA_DISASSOC_FROM_CLI } timeout_next; u16 deauth_reason; @@ -79,7 +76,7 @@ u32 acct_session_id_hi; u32 acct_session_id_lo; - time_t acct_session_start; + struct os_reltime acct_session_start; int acct_session_started; int acct_terminate_cause; /* Acct-Terminate-Cause */ int acct_interim_interval; /* Acct-Interim-Interval */ @@ -98,8 +95,14 @@ struct hostapd_ssid *ssid_probe; /* SSID selection based on ProbeReq */ int vlan_id; + /* PSKs from RADIUS authentication server */ + struct hostapd_sta_wpa_psk_short *psk; + + char *identity; /* User-Name from RADIUS */ + char *radius_cui; /* Chargeable-User-Identity from RADIUS */ struct ieee80211_ht_capabilities *ht_capabilities; + struct ieee80211_vht_capabilities *vht_capabilities; #ifdef CONFIG_IEEE80211W int sa_query_count; /* number of pending SA Query requests; @@ -108,11 +111,24 @@ u8 *sa_query_trans_id; /* buffer of WLAN_SA_QUERY_TR_ID_LEN * * sa_query_count octets of pending SA Query * transaction identifiers */ - struct os_time sa_query_start; + struct os_reltime sa_query_start; #endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_INTERWORKING +#define GAS_DIALOG_MAX 8 /* Max concurrent dialog number */ + struct gas_dialog_info *gas_dialog; + u8 gas_dialog_next; +#endif /* CONFIG_INTERWORKING */ + 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 */ + + struct os_reltime connected_time; + +#ifdef CONFIG_SAE + struct sae_data *sae; +#endif /* CONFIG_SAE */ }; @@ -139,11 +155,13 @@ void *ctx), void *ctx); struct sta_info * ap_get_sta(struct hostapd_data *hapd, const u8 *sta); +struct sta_info * ap_get_sta_p2p(struct hostapd_data *hapd, const u8 *addr); void ap_sta_hash_add(struct hostapd_data *hapd, struct sta_info *sta); void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta); -void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta); void hostapd_free_stas(struct hostapd_data *hapd); void ap_handle_timer(void *eloop_ctx, void *timeout_ctx); +void ap_sta_replenish_timeout(struct hostapd_data *hapd, struct sta_info *sta, + u32 session_timeout); void ap_sta_session_timeout(struct hostapd_data *hapd, struct sta_info *sta, u32 session_timeout); void ap_sta_no_session_timeout(struct hostapd_data *hapd, @@ -153,6 +171,10 @@ u16 reason); void ap_sta_deauthenticate(struct hostapd_data *hapd, struct sta_info *sta, u16 reason); +#ifdef CONFIG_WPS +int ap_sta_wps_cancel(struct hostapd_data *hapd, + struct sta_info *sta, void *ctx); +#endif /* CONFIG_WPS */ int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta, int old_vlanid); void ap_sta_start_sa_query(struct hostapd_data *hapd, struct sta_info *sta); @@ -171,4 +193,6 @@ 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); +int ap_sta_flags_txt(u32 flags, char *buf, size_t buflen); + #endif /* STA_INFO_H */ diff -Nru wpa-1.0/src/ap/tkip_countermeasures.c wpa-2.1/src/ap/tkip_countermeasures.c --- wpa-1.0/src/ap/tkip_countermeasures.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/ap/tkip_countermeasures.c 2014-02-04 11:23:35.000000000 +0000 @@ -1,15 +1,9 @@ /* * hostapd / TKIP countermeasures - * Copyright (c) 2002-2011, Jouni Malinen + * Copyright (c) 2002-2012, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "utils/includes.h" @@ -17,6 +11,7 @@ #include "utils/common.h" #include "utils/eloop.h" #include "common/ieee802_11_defs.h" +#include "radius/radius.h" #include "hostapd.h" #include "sta_info.h" #include "ap_mlme.h" @@ -50,12 +45,17 @@ eloop_cancel_timeout(ieee80211_tkip_countermeasures_stop, hapd, NULL); eloop_register_timeout(60, 0, ieee80211_tkip_countermeasures_stop, hapd, NULL); - for (sta = hapd->sta_list; sta != NULL; sta = sta->next) { + while ((sta = hapd->sta_list)) { + sta->acct_terminate_cause = + RADIUS_ACCT_TERMINATE_CAUSE_ADMIN_RESET; + if (sta->flags & WLAN_STA_AUTH) { + mlme_deauthenticate_indication( + hapd, sta, + WLAN_REASON_MICHAEL_MIC_FAILURE); + } hostapd_drv_sta_deauth(hapd, sta->addr, WLAN_REASON_MICHAEL_MIC_FAILURE); - ap_sta_set_authorized(hapd, sta, 0); - sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC); - hostapd_drv_sta_remove(hapd, sta->addr); + ap_free_sta(hapd, sta); } } @@ -66,9 +66,10 @@ } -void michael_mic_failure(struct hostapd_data *hapd, const u8 *addr, int local) +int michael_mic_failure(struct hostapd_data *hapd, const u8 *addr, int local) { - struct os_time now; + struct os_reltime now; + int ret = 0; if (addr && local) { struct sta_info *sta = ap_get_sta(hapd, addr); @@ -84,17 +85,21 @@ "MLME-MICHAELMICFAILURE.indication " "for not associated STA (" MACSTR ") ignored", MAC2STR(addr)); - return; + return ret; } } - os_get_time(&now); - if (now.sec > hapd->michael_mic_failure + 60) { + os_get_reltime(&now); + if (os_reltime_expired(&now, &hapd->michael_mic_failure, 60)) { hapd->michael_mic_failures = 1; } else { hapd->michael_mic_failures++; - if (hapd->michael_mic_failures > 1) + if (hapd->michael_mic_failures > 1) { ieee80211_tkip_countermeasures_start(hapd); + ret = 1; + } } - hapd->michael_mic_failure = now.sec; + hapd->michael_mic_failure = now; + + return ret; } diff -Nru wpa-1.0/src/ap/tkip_countermeasures.h wpa-2.1/src/ap/tkip_countermeasures.h --- wpa-1.0/src/ap/tkip_countermeasures.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/ap/tkip_countermeasures.h 2014-02-04 11:23:35.000000000 +0000 @@ -1,21 +1,15 @@ /* * hostapd / TKIP countermeasures - * Copyright (c) 2002-2011, Jouni Malinen + * Copyright (c) 2002-2012, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef TKIP_COUNTERMEASURES_H #define TKIP_COUNTERMEASURES_H -void michael_mic_failure(struct hostapd_data *hapd, const u8 *addr, int local); +int michael_mic_failure(struct hostapd_data *hapd, const u8 *addr, int local); void ieee80211_tkip_countermeasures_deinit(struct hostapd_data *hapd); #endif /* TKIP_COUNTERMEASURES_H */ diff -Nru wpa-1.0/src/ap/utils.c wpa-2.1/src/ap/utils.c --- wpa-1.0/src/ap/utils.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/ap/utils.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * AP mode helper functions * Copyright (c) 2009, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -23,13 +17,14 @@ int hostapd_register_probereq_cb(struct hostapd_data *hapd, int (*cb)(void *ctx, const u8 *sa, const u8 *da, const u8 *bssid, - const u8 *ie, size_t ie_len), + const u8 *ie, size_t ie_len, + int ssi_signal), void *ctx) { struct hostapd_probereq_cb *n; - n = os_realloc(hapd->probereq_cb, (hapd->num_probereq_cb + 1) * - sizeof(struct hostapd_probereq_cb)); + n = os_realloc_array(hapd->probereq_cb, hapd->num_probereq_cb + 1, + sizeof(struct hostapd_probereq_cb)); if (n == NULL) return -1; @@ -83,7 +78,8 @@ struct prune_data data; data.hapd = hapd; data.addr = addr; - if (hapd->iface->for_each_interface) - hapd->iface->for_each_interface(hapd->iface->interfaces, - prune_associations, &data); + if (hapd->iface->interfaces && + hapd->iface->interfaces->for_each_interface) + hapd->iface->interfaces->for_each_interface( + hapd->iface->interfaces, prune_associations, &data); } diff -Nru wpa-1.0/src/ap/vlan_init.c wpa-2.1/src/ap/vlan_init.c --- wpa-1.0/src/ap/vlan_init.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/ap/vlan_init.c 2014-02-04 11:23:35.000000000 +0000 @@ -4,14 +4,8 @@ * Copyright 2005-2006, Devicescape Software, Inc. * Copyright (c) 2009, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "utils/includes.h" @@ -21,6 +15,7 @@ #include "ap_config.h" #include "ap_drv_ops.h" #include "vlan_init.h" +#include "vlan_util.h" #ifdef CONFIG_FULL_DYNAMIC_VLAN @@ -335,7 +330,9 @@ } -static int vlan_rem(const char *if_name) +#ifndef CONFIG_VLAN_NETLINK + +int vlan_rem(const char *if_name) { int fd; struct vlan_ioctl_args if_request; @@ -378,7 +375,7 @@ returns 1 if the interface already exists returns 0 otherwise */ -static int vlan_add(const char *if_name, int vid) +int vlan_add(const char *if_name, int vid, const char *vlan_if_name) { int fd; struct vlan_ioctl_args if_request; @@ -474,6 +471,125 @@ return 0; } +#endif /* CONFIG_VLAN_NETLINK */ + + +/** + * Increase the usage counter for given parent/ifname combination. + * If create is set, then this iface is added to the global list. + * Returns + * -1 on error + * 0 if iface is not in list + * 1 if iface is in list (was there or has been added) + */ +static int hapd_get_dynamic_iface(const char *parent, const char *ifname, + int create, struct hostapd_data *hapd) +{ + size_t i; + struct hostapd_dynamic_iface *j = NULL, **tmp; + struct hapd_interfaces *hapd_global = hapd->iface->interfaces; + + if (!parent) + parent = ""; + + for (i = 0; i < hapd_global->count_dynamic; i++) { + j = hapd_global->dynamic_iface[i]; + if (os_strncmp(j->iface, ifname, sizeof(j->iface)) == 0 && + os_strncmp(j->parent, parent, sizeof(j->parent)) == 0) + break; + } + if (i < hapd_global->count_dynamic) { + j->usage++; + return 1; + } + + /* new entry required */ + if (!create) + return 0; + + j = os_zalloc(sizeof(*j)); + if (!j) + return -1; + os_strlcpy(j->iface, ifname, sizeof(j->iface)); + os_strlcpy(j->parent, parent, sizeof(j->parent)); + + tmp = os_realloc_array(hapd_global->dynamic_iface, i + 1, + sizeof(*hapd_global->dynamic_iface)); + if (!tmp) { + wpa_printf(MSG_ERROR, "VLAN: Failed to allocate memory in %s", + __func__); + return -1; + } + hapd_global->count_dynamic++; + hapd_global->dynamic_iface = tmp; + hapd_global->dynamic_iface[i] = j; + + return 1; +} + + +/** + * Decrease the usage counter for given ifname. + * Returns + * -1 on error or if iface was not found + * 0 if iface was found and is still present + * 1 if iface was removed from global list + */ +static int hapd_put_dynamic_iface(const char *parent, const char *ifname, + struct hostapd_data *hapd) +{ + size_t i; + struct hostapd_dynamic_iface *j = NULL, **tmp; + struct hapd_interfaces *hapd_glob = hapd->iface->interfaces; + + if (!parent) + parent = ""; + + for (i = 0; i < hapd_glob->count_dynamic; i++) { + j = hapd_glob->dynamic_iface[i]; + if (os_strncmp(j->iface, ifname, sizeof(j->iface)) == 0 && + os_strncmp(j->parent, parent, sizeof(j->parent)) == 0) + break; + } + + if (i == hapd_glob->count_dynamic) { + /* + * Interface not in global list. This can happen if alloc in + * _get_ failed. + */ + return -1; + } + + if (j->usage > 0) { + j->usage--; + return 0; + } + + os_free(j); + for (; i < hapd_glob->count_dynamic - 1; i++) + hapd_glob->dynamic_iface[i] = hapd_glob->dynamic_iface[i + 1]; + hapd_glob->dynamic_iface[hapd_glob->count_dynamic - 1] = NULL; + hapd_glob->count_dynamic--; + + if (hapd_glob->count_dynamic == 0) { + os_free(hapd_glob->dynamic_iface); + hapd_glob->dynamic_iface = NULL; + return 1; + } + + tmp = os_realloc_array(hapd_glob->dynamic_iface, + hapd_glob->count_dynamic, + sizeof(*hapd_glob->dynamic_iface)); + if (!tmp) { + wpa_printf(MSG_ERROR, "VLAN: Failed to release memory in %s", + __func__); + return -1; + } + hapd_glob->dynamic_iface = tmp; + + return 1; +} + static void vlan_newlink(char *ifname, struct hostapd_data *hapd) { @@ -481,35 +597,65 @@ 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 ret; wpa_printf(MSG_DEBUG, "VLAN: vlan_newlink(%s)", ifname); while (vlan) { if (os_strcmp(ifname, vlan->ifname) == 0) { - os_snprintf(br_name, sizeof(br_name), "brvlan%d", - vlan->vlan_id); + 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 (!br_addbr(br_name)) + ret = br_addbr(br_name); + if (hapd_get_dynamic_iface(NULL, br_name, ret == 0, + hapd)) vlan->clean |= DVLAN_CLEAN_BR; ifconfig_up(br_name); if (tagged_interface) { - - if (!vlan_add(tagged_interface, vlan->vlan_id)) + 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); + + ifconfig_up(tagged_interface); + ret = vlan_add(tagged_interface, vlan->vlan_id, + vlan_ifname); + if (hapd_get_dynamic_iface(NULL, vlan_ifname, + ret == 0, hapd)) vlan->clean |= DVLAN_CLEAN_VLAN; - os_snprintf(vlan_ifname, sizeof(vlan_ifname), - "vlan%d", vlan->vlan_id); - - if (!br_addif(br_name, vlan_ifname)) + ret = br_addif(br_name, vlan_ifname); + if (hapd_get_dynamic_iface(br_name, + vlan_ifname, + ret == 0, hapd)) vlan->clean |= DVLAN_CLEAN_VLAN_PORT; ifconfig_up(vlan_ifname); } - if (!br_addif(br_name, ifname)) + ret = br_addif(br_name, ifname); + if (hapd_get_dynamic_iface(br_name, ifname, ret == 0, + hapd)) vlan->clean |= DVLAN_CLEAN_WLAN_PORT; ifconfig_up(ifname); @@ -527,6 +673,7 @@ 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; wpa_printf(MSG_DEBUG, "VLAN: vlan_dellink(%s)", ifname); @@ -534,24 +681,48 @@ while (vlan) { if (os_strcmp(ifname, vlan->ifname) == 0) { - os_snprintf(br_name, sizeof(br_name), "brvlan%d", - vlan->vlan_id); + 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) + if ((vlan->clean & DVLAN_CLEAN_WLAN_PORT) && + hapd_put_dynamic_iface(br_name, vlan->ifname, hapd)) br_delif(br_name, vlan->ifname); if (tagged_interface) { - os_snprintf(vlan_ifname, sizeof(vlan_ifname), - "vlan%d", vlan->vlan_id); - if (vlan->clean & DVLAN_CLEAN_VLAN_PORT) + 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); + if ((vlan->clean & DVLAN_CLEAN_VLAN_PORT) && + hapd_put_dynamic_iface(br_name, vlan_ifname, + hapd)) br_delif(br_name, vlan_ifname); ifconfig_down(vlan_ifname); - if (vlan->clean & DVLAN_CLEAN_VLAN) + if ((vlan->clean & DVLAN_CLEAN_VLAN) && + hapd_put_dynamic_iface(NULL, vlan_ifname, + hapd)) vlan_rem(vlan_ifname); } if ((vlan->clean & DVLAN_CLEAN_BR) && + hapd_put_dynamic_iface(NULL, br_name, hapd) && br_getnumports(br_name) == 0) { ifconfig_down(br_name); br_delbr(br_name); @@ -682,7 +853,12 @@ if (priv == NULL) return NULL; - vlan_set_name_type(VLAN_NAME_TYPE_PLUS_VID_NO_PAD); +#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) { @@ -808,6 +984,24 @@ hapd->full_dynamic_vlan = full_dynamic_vlan_init(hapd); #endif /* CONFIG_FULL_DYNAMIC_VLAN */ + if (hapd->conf->ssid.dynamic_vlan != DYNAMIC_VLAN_DISABLED && + !hapd->conf->vlan) { + /* dynamic vlans enabled but no (or empty) vlan_file given */ + struct hostapd_vlan *vlan; + vlan = os_zalloc(sizeof(*vlan)); + if (vlan == NULL) { + wpa_printf(MSG_ERROR, "Out of memory while assigning " + "VLAN interfaces"); + return -1; + } + + vlan->vlan_id = VLAN_ID_WILDCARD; + os_snprintf(vlan->ifname, sizeof(vlan->ifname), "%s.#", + hapd->conf->iface); + vlan->next = hapd->conf->vlan; + hapd->conf->vlan = vlan; + } + if (vlan_dynamic_add(hapd, hapd->conf->vlan)) return -1; @@ -821,6 +1015,7 @@ #ifdef CONFIG_FULL_DYNAMIC_VLAN full_dynamic_vlan_deinit(hapd->full_dynamic_vlan); + hapd->full_dynamic_vlan = NULL; #endif /* CONFIG_FULL_DYNAMIC_VLAN */ } diff -Nru wpa-1.0/src/ap/vlan_init.h wpa-2.1/src/ap/vlan_init.h --- wpa-1.0/src/ap/vlan_init.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/ap/vlan_init.h 2014-02-04 11:23:35.000000000 +0000 @@ -3,14 +3,8 @@ * Copyright 2003, Instant802 Networks, Inc. * Copyright 2005, Devicescape Software, Inc. * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef VLAN_INIT_H diff -Nru wpa-1.0/src/ap/vlan_util.c wpa-2.1/src/ap/vlan_util.c --- wpa-1.0/src/ap/vlan_util.c 1970-01-01 00:00:00.000000000 +0000 +++ wpa-2.1/src/ap/vlan_util.c 2014-02-04 11:23:35.000000000 +0000 @@ -0,0 +1,177 @@ +/* + * hostapd / VLAN netlink api + * Copyright (c) 2012, Michael Braun + * + * 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 +#include +#include +#include + +#include "utils/common.h" +#include "utils/eloop.h" +#include "hostapd.h" +#include "vlan_util.h" + +/* + * Add a vlan interface with name 'vlan_if_name', 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 ret = -1; + struct nl_sock *handle = NULL; + struct nl_cache *cache = NULL; + struct rtnl_link *rlink = NULL; + int if_idx = 0; + + wpa_printf(MSG_DEBUG, "VLAN: vlan_add(if_name=%s, vid=%d, " + "vlan_if_name=%s)", if_name, vid, vlan_if_name); + + if ((os_strlen(if_name) + 1) > IFNAMSIZ) { + wpa_printf(MSG_ERROR, "VLAN: Interface name too long: '%s'", + if_name); + return -1; + } + + if ((os_strlen(vlan_if_name) + 1) > IFNAMSIZ) { + wpa_printf(MSG_ERROR, "VLAN: Interface name too long: '%s'", + vlan_if_name); + return -1; + } + + handle = nl_socket_alloc(); + if (!handle) { + wpa_printf(MSG_ERROR, "VLAN: failed to open netlink socket"); + goto vlan_add_error; + } + + if (nl_connect(handle, NETLINK_ROUTE) < 0) { + wpa_printf(MSG_ERROR, "VLAN: failed to connect to netlink"); + goto vlan_add_error; + } + + if (rtnl_link_alloc_cache(handle, AF_UNSPEC, &cache) < 0) { + cache = NULL; + wpa_printf(MSG_ERROR, "VLAN: failed to alloc cache"); + 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 ((rlink = rtnl_link_get_by_name(cache, vlan_if_name))) { + /* link does exist */ + rtnl_link_put(rlink); + rlink = NULL; + wpa_printf(MSG_ERROR, "VLAN: interface %s already exists", + vlan_if_name); + ret = 1; + goto vlan_add_error; + } + + rlink = rtnl_link_alloc(); + if (!rlink) { + wpa_printf(MSG_ERROR, "VLAN: failed to allocate new link"); + goto vlan_add_error; + } + + if (rtnl_link_set_type(rlink, "vlan") < 0) { + wpa_printf(MSG_ERROR, "VLAN: failed to set link type"); + goto vlan_add_error; + } + + rtnl_link_set_link(rlink, if_idx); + rtnl_link_set_name(rlink, vlan_if_name); + + if (rtnl_link_vlan_set_id(rlink, vid) < 0) { + wpa_printf(MSG_ERROR, "VLAN: failed to set link vlan id"); + goto vlan_add_error; + } + + if (rtnl_link_add(handle, rlink, NLM_F_CREATE) < 0) { + wpa_printf(MSG_ERROR, "VLAN: failed to create link %s for " + "vlan %d on %s (%d)", + vlan_if_name, vid, if_name, if_idx); + goto vlan_add_error; + } + + ret = 0; + +vlan_add_error: + if (rlink) + rtnl_link_put(rlink); + if (cache) + nl_cache_free(cache); + if (handle) + nl_socket_free(handle); + return ret; +} + + +int vlan_rem(const char *if_name) +{ + int 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); + + handle = nl_socket_alloc(); + if (!handle) { + wpa_printf(MSG_ERROR, "VLAN: failed to open netlink socket"); + goto vlan_rem_error; + } + + if (nl_connect(handle, NETLINK_ROUTE) < 0) { + wpa_printf(MSG_ERROR, "VLAN: failed to connect to netlink"); + goto vlan_rem_error; + } + + if (rtnl_link_alloc_cache(handle, AF_UNSPEC, &cache) < 0) { + cache = NULL; + wpa_printf(MSG_ERROR, "VLAN: failed to alloc cache"); + 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); + goto vlan_rem_error; + } + + if (rtnl_link_delete(handle, rlink) < 0) { + wpa_printf(MSG_ERROR, "VLAN: failed to remove link %s", + if_name); + goto vlan_rem_error; + } + + ret = 0; + +vlan_rem_error: + if (rlink) + rtnl_link_put(rlink); + if (cache) + nl_cache_free(cache); + if (handle) + nl_socket_free(handle); + return ret; +} diff -Nru wpa-1.0/src/ap/vlan_util.h wpa-2.1/src/ap/vlan_util.h --- wpa-1.0/src/ap/vlan_util.h 1970-01-01 00:00:00.000000000 +0000 +++ wpa-2.1/src/ap/vlan_util.h 2014-02-04 11:23:35.000000000 +0000 @@ -0,0 +1,15 @@ +/* + * hostapd / VLAN netlink api + * Copyright (c) 2012, Michael Braun + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef VLAN_UTIL_H +#define VLAN_UTIL_H + +int vlan_add(const char *if_name, int vid, const char *vlan_if_name); +int vlan_rem(const char *if_name); + +#endif /* VLAN_UTIL_H */ diff -Nru wpa-1.0/src/ap/wmm.c wpa-2.1/src/ap/wmm.c --- wpa-1.0/src/ap/wmm.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/ap/wmm.c 2014-02-04 11:23:35.000000000 +0000 @@ -4,14 +4,8 @@ * Copyright 2005-2006, Devicescape Software, Inc. * Copyright (c) 2009, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "utils/includes.h" @@ -72,7 +66,8 @@ wmm->version = WMM_VERSION; wmm->qos_info = hapd->parameter_set_count & 0xf; - if (hapd->conf->wmm_uapsd) + if (hapd->conf->wmm_uapsd && + (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_AP_UAPSD)) wmm->qos_info |= 0x80; wmm->reserved = 0; @@ -155,8 +150,8 @@ os_memcpy(t, tspec, sizeof(struct wmm_tspec_element)); len = ((u8 *) (t + 1)) - buf; - if (hostapd_drv_send_mlme(hapd, m, len) < 0) - perror("wmm_send_action: send"); + if (hostapd_drv_send_mlme(hapd, m, len, 0) < 0) + wpa_printf(MSG_INFO, "wmm_send_action: send failed"); } diff -Nru wpa-1.0/src/ap/wmm.h wpa-2.1/src/ap/wmm.h --- wpa-1.0/src/ap/wmm.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/ap/wmm.h 2014-02-04 11:23:35.000000000 +0000 @@ -3,14 +3,8 @@ * Copyright 2002-2003, Instant802 Networks, Inc. * Copyright 2005-2006, Devicescape Software, Inc. * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef WME_H diff -Nru wpa-1.0/src/ap/wnm_ap.c wpa-2.1/src/ap/wnm_ap.c --- wpa-1.0/src/ap/wnm_ap.c 1970-01-01 00:00:00.000000000 +0000 +++ wpa-2.1/src/ap/wnm_ap.c 2014-02-04 11:23:35.000000000 +0000 @@ -0,0 +1,508 @@ +/* + * hostapd - WNM + * Copyright (c) 2011-2013, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" + +#include "utils/common.h" +#include "utils/eloop.h" +#include "common/ieee802_11_defs.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 "wnm_ap.h" + +#define MAX_TFS_IE_LEN 1024 + + +/* get the TFS IE from driver */ +static int ieee80211_11_get_tfs_ie(struct hostapd_data *hapd, const u8 *addr, + u8 *buf, u16 *buf_len, enum wnm_oper oper) +{ + wpa_printf(MSG_DEBUG, "%s: TFS get operation %d", __func__, oper); + + return hostapd_drv_wnm_oper(hapd, oper, addr, buf, buf_len); +} + + +/* set the TFS IE to driver */ +static int ieee80211_11_set_tfs_ie(struct hostapd_data *hapd, const u8 *addr, + u8 *buf, u16 *buf_len, enum wnm_oper oper) +{ + wpa_printf(MSG_DEBUG, "%s: TFS set operation %d", __func__, oper); + + return hostapd_drv_wnm_oper(hapd, oper, addr, buf, buf_len); +} + + +/* MLME-SLEEPMODE.response */ +static int ieee802_11_send_wnmsleep_resp(struct hostapd_data *hapd, + const u8 *addr, u8 dialog_token, + u8 action_type, u16 intval) +{ + struct ieee80211_mgmt *mgmt; + int res; + size_t len; + 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; + u16 wnmtfs_ie_len; + u8 *pos; + struct sta_info *sta; + enum wnm_oper tfs_oper = action_type == WNM_SLEEP_MODE_ENTER ? + WNM_SLEEP_TFS_RESP_IE_ADD : WNM_SLEEP_TFS_RESP_IE_NONE; + + sta = ap_get_sta(hapd, addr); + if (sta == NULL) { + wpa_printf(MSG_DEBUG, "%s: station not found", __func__); + return -EINVAL; + } + + /* WNM-Sleep Mode IE */ + os_memset(&wnmsleep_ie, 0, sizeof(struct wnm_sleep_element)); + wnmsleep_ie_len = sizeof(struct wnm_sleep_element); + wnmsleep_ie.eid = WLAN_EID_WNMSLEEP; + wnmsleep_ie.len = wnmsleep_ie_len - 2; + wnmsleep_ie.action_type = action_type; + wnmsleep_ie.status = WNM_STATUS_SLEEP_ACCEPT; + wnmsleep_ie.intval = host_to_le16(intval); + + /* TFS IE(s) */ + wnmtfs_ie = os_zalloc(MAX_TFS_IE_LEN); + if (wnmtfs_ie == NULL) + return -1; + if (ieee80211_11_get_tfs_ie(hapd, addr, wnmtfs_ie, &wnmtfs_ie_len, + tfs_oper)) { + wnmtfs_ie_len = 0; + os_free(wnmtfs_ie); + wnmtfs_ie = NULL; + } + +#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); + if (mgmt == NULL) { + wpa_printf(MSG_DEBUG, "MLME: Failed to allocate buffer for " + "WNM-Sleep Response action frame"); + return -1; + } + 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->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, + WLAN_FC_STYPE_ACTION); + mgmt->u.action.category = WLAN_ACTION_WNM; + mgmt->u.action.u.wnm_sleep_resp.action = WNM_SLEEP_MODE_RESP; + mgmt->u.action.u.wnm_sleep_resp.dialogtoken = dialog_token; + 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) || + action_type != WNM_SLEEP_MODE_EXIT) { + mgmt->u.action.u.wnm_sleep_resp.keydata_len = 0; + } else { + gtk_elem_len = wpa_wnmsleep_gtk_subelem(sta->wpa_sm, pos); + pos += gtk_elem_len; + wpa_printf(MSG_DEBUG, "Pass 4, gtk_len = %d", + (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; + } + igtk_elem_len = res; + pos += igtk_elem_len; + wpa_printf(MSG_DEBUG, "Pass 4 igtk_len = %d", + (int) igtk_elem_len); +#endif /* CONFIG_IEEE80211W */ + + WPA_PUT_LE16((u8 *) + &mgmt->u.action.u.wnm_sleep_resp.keydata_len, + gtk_elem_len + igtk_elem_len); + } + os_memcpy(pos, &wnmsleep_ie, wnmsleep_ie_len); + /* copy TFS IE here */ + pos += wnmsleep_ie_len; + if (wnmtfs_ie) + os_memcpy(pos, wnmtfs_ie, wnmtfs_ie_len); + + len = 1 + sizeof(mgmt->u.action.u.wnm_sleep_resp) + gtk_elem_len + + igtk_elem_len + wnmsleep_ie_len + wnmtfs_ie_len; + + /* In driver, response frame should be forced to sent when STA is in + * PS mode */ + res = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, + mgmt->da, &mgmt->u.action.category, len); + + if (!res) { + wpa_printf(MSG_DEBUG, "Successfully send WNM-Sleep Response " + "frame"); + + /* when entering wnmsleep + * 1. pause the node in driver + * 2. mark the node so that AP won't update GTK/IGTK during + * WNM Sleep + */ + if (wnmsleep_ie.status == WNM_STATUS_SLEEP_ACCEPT && + wnmsleep_ie.action_type == WNM_SLEEP_MODE_ENTER) { + sta->flags |= WLAN_STA_WNM_SLEEP_MODE; + hostapd_drv_wnm_oper(hapd, WNM_SLEEP_ENTER_CONFIRM, + addr, NULL, NULL); + wpa_set_wnmsleep(sta->wpa_sm, 1); + } + /* when exiting wnmsleep + * 1. unmark the node + * 2. start GTK/IGTK update if MFP is not used + * 3. unpause the node in driver + */ + if ((wnmsleep_ie.status == WNM_STATUS_SLEEP_ACCEPT || + wnmsleep_ie.status == + WNM_STATUS_SLEEP_EXIT_ACCEPT_GTK_UPDATE) && + wnmsleep_ie.action_type == WNM_SLEEP_MODE_EXIT) { + sta->flags &= ~WLAN_STA_WNM_SLEEP_MODE; + 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)) + wpa_wnmsleep_rekey_gtk(sta->wpa_sm); + } + } else + wpa_printf(MSG_DEBUG, "Fail to send WNM-Sleep Response frame"); + +#undef MAX_GTK_SUBELEM_LEN +#undef MAX_IGTK_SUBELEM_LEN + os_free(wnmtfs_ie); + os_free(mgmt); + return res; +} + + +static void ieee802_11_rx_wnmsleep_req(struct hostapd_data *hapd, + const u8 *addr, const u8 *frm, int len) +{ + /* Dialog Token [1] | WNM-Sleep Mode IE | TFS Response IE */ + const u8 *pos = frm; + u8 dialog_token; + struct wnm_sleep_element *wnmsleep_ie = NULL; + /* multiple TFS Req IE (assuming consecutive) */ + u8 *tfsreq_ie_start = NULL; + u8 *tfsreq_ie_end = NULL; + u16 tfsreq_ie_len = 0; + + 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) + 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; + } else + wpa_printf(MSG_DEBUG, "WNM: EID %d not recognized", + *pos); + pos += ie_len + 2; + } + + if (!wnmsleep_ie) { + wpa_printf(MSG_DEBUG, "No WNM-Sleep IE found"); + return; + } + + if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_ENTER && + tfsreq_ie_start && tfsreq_ie_end && + tfsreq_ie_end - tfsreq_ie_start >= 0) { + tfsreq_ie_len = (tfsreq_ie_end + tfsreq_ie_end[1] + 2) - + tfsreq_ie_start; + wpa_printf(MSG_DEBUG, "TFS Req IE(s) found"); + /* pass the TFS Req IE(s) to driver for processing */ + if (ieee80211_11_set_tfs_ie(hapd, addr, tfsreq_ie_start, + &tfsreq_ie_len, + WNM_SLEEP_TFS_REQ_IE_SET)) + wpa_printf(MSG_DEBUG, "Fail to set TFS Req IE"); + } + + ieee802_11_send_wnmsleep_resp(hapd, addr, dialog_token, + wnmsleep_ie->action_type, + le_to_host16(wnmsleep_ie->intval)); + + if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_EXIT) { + /* clear the tfs after sending the resp frame */ + ieee80211_11_set_tfs_ie(hapd, addr, tfsreq_ie_start, + &tfsreq_ie_len, WNM_SLEEP_TFS_IE_DEL); + } +} + + +static int ieee802_11_send_bss_trans_mgmt_request(struct hostapd_data *hapd, + const u8 *addr, + u8 dialog_token, + const char *url) +{ + struct ieee80211_mgmt *mgmt; + size_t url_len, 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)); + if (mgmt == NULL) + return -1; + 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->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, + WLAN_FC_STYPE_ACTION); + mgmt->u.action.category = WLAN_ACTION_WNM; + mgmt->u.action.u.bss_tm_req.action = WNM_BSS_TRANS_MGMT_REQ; + mgmt->u.action.u.bss_tm_req.dialog_token = dialog_token; + mgmt->u.action.u.bss_tm_req.req_mode = 0; + 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 " + "validity_interval=%u", + MAC2STR(addr), dialog_token, + mgmt->u.action.u.bss_tm_req.req_mode, + le_to_host16(mgmt->u.action.u.bss_tm_req.disassoc_timer), + mgmt->u.action.u.bss_tm_req.validity_interval); + + len = pos - &mgmt->u.action.category; + res = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, + mgmt->da, &mgmt->u.action.category, len); + os_free(mgmt); + return res; +} + + +static void ieee802_11_rx_bss_trans_mgmt_query(struct hostapd_data *hapd, + const u8 *addr, const u8 *frm, + size_t len) +{ + u8 dialog_token, reason; + const u8 *pos, *end; + + if (len < 2) { + wpa_printf(MSG_DEBUG, "WNM: Ignore too short BSS Transition Management Query from " + MACSTR, MAC2STR(addr)); + return; + } + + pos = frm; + end = pos + len; + dialog_token = *pos++; + reason = *pos++; + + wpa_printf(MSG_DEBUG, "WNM: BSS Transition Management Query from " + MACSTR " dialog_token=%u reason=%u", + MAC2STR(addr), dialog_token, reason); + + 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); +} + + +static void ieee802_11_rx_bss_trans_mgmt_resp(struct hostapd_data *hapd, + const u8 *addr, const u8 *frm, + size_t len) +{ + u8 dialog_token, status_code, bss_termination_delay; + const u8 *pos, *end; + + if (len < 3) { + wpa_printf(MSG_DEBUG, "WNM: Ignore too short BSS Transition Management Response from " + MACSTR, MAC2STR(addr)); + return; + } + + pos = frm; + end = pos + len; + dialog_token = *pos++; + status_code = *pos++; + bss_termination_delay = *pos++; + + wpa_printf(MSG_DEBUG, "WNM: BSS Transition Management Response from " + MACSTR " dialog_token=%u status_code=%u " + "bss_termination_delay=%u", MAC2STR(addr), dialog_token, + status_code, bss_termination_delay); + + 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; + } + wpa_printf(MSG_DEBUG, "WNM: Target BSSID: " MACSTR, + MAC2STR(pos)); + pos += ETH_ALEN; + } + + wpa_hexdump(MSG_DEBUG, "WNM: BSS Transition Candidate List Entries", + pos, end - pos); +} + + +int ieee802_11_rx_wnm_action_ap(struct hostapd_data *hapd, + const struct ieee80211_mgmt *mgmt, size_t len) +{ + u8 action; + const u8 *payload; + size_t plen; + + if (len < IEEE80211_HDRLEN + 2) + return -1; + + payload = &mgmt->u.action.category; + payload++; + action = *payload++; + plen = (((const u8 *) mgmt) + len) - payload; + + switch (action) { + case WNM_BSS_TRANS_MGMT_QUERY: + ieee802_11_rx_bss_trans_mgmt_query(hapd, mgmt->sa, payload, + plen); + return 0; + case WNM_BSS_TRANS_MGMT_RESP: + ieee802_11_rx_bss_trans_mgmt_resp(hapd, mgmt->sa, payload, + plen); + return 0; + case WNM_SLEEP_MODE_REQ: + ieee802_11_rx_wnmsleep_req(hapd, mgmt->sa, payload, plen); + return 0; + } + + wpa_printf(MSG_DEBUG, "WNM: Unsupported WNM Action %u from " MACSTR, + action, MAC2STR(mgmt->sa)); + return -1; +} + + +int wnm_send_disassoc_imminent(struct hostapd_data *hapd, + struct sta_info *sta, int disassoc_timer) +{ + u8 buf[1000], *pos; + struct ieee80211_mgmt *mgmt; + + 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.bss_tm_req.action = WNM_BSS_TRANS_MGMT_REQ; + mgmt->u.action.u.bss_tm_req.dialog_token = 1; + mgmt->u.action.u.bss_tm_req.req_mode = + WNM_BSS_TM_REQ_DISASSOC_IMMINENT; + mgmt->u.action.u.bss_tm_req.disassoc_timer = + host_to_le16(disassoc_timer); + mgmt->u.action.u.bss_tm_req.validity_interval = 0; + + pos = mgmt->u.action.u.bss_tm_req.variable; + + wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Request frame to indicate imminent disassociation (disassoc_timer=%d) to " + MACSTR, disassoc_timer, MAC2STR(sta->addr)); + if (hostapd_drv_send_mlme(hapd, buf, pos - buf, 0) < 0) { + wpa_printf(MSG_DEBUG, "Failed to send BSS Transition " + "Management Request frame"); + return -1; + } + + return 0; +} + + +int wnm_send_ess_disassoc_imminent(struct hostapd_data *hapd, + struct sta_info *sta, const char *url, + int disassoc_timer) +{ + u8 buf[1000], *pos; + struct ieee80211_mgmt *mgmt; + size_t url_len; + + 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.bss_tm_req.action = WNM_BSS_TRANS_MGMT_REQ; + mgmt->u.action.u.bss_tm_req.dialog_token = 1; + mgmt->u.action.u.bss_tm_req.req_mode = + WNM_BSS_TM_REQ_DISASSOC_IMMINENT | + WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT; + mgmt->u.action.u.bss_tm_req.disassoc_timer = + host_to_le16(disassoc_timer); + mgmt->u.action.u.bss_tm_req.validity_interval = 0x01; + + pos = mgmt->u.action.u.bss_tm_req.variable; + + /* Session Information URL */ + url_len = os_strlen(url); + if (url_len > 255) + return -1; + *pos++ = url_len; + os_memcpy(pos, url, url_len); + pos += url_len; + + if (hostapd_drv_send_mlme(hapd, buf, pos - buf, 0) < 0) { + wpa_printf(MSG_DEBUG, "Failed to send BSS Transition " + "Management Request frame"); + return -1; + } + + /* send disassociation frame after time-out */ + if (disassoc_timer) { + int timeout, beacon_int; + + /* + * Prevent STA from reconnecting using cached PMKSA to force + * full authentication with the authentication server (which may + * decide to reject the connection), + */ + wpa_auth_pmksa_remove(hapd->wpa_auth, sta->addr); + + beacon_int = hapd->iconf->beacon_int; + if (beacon_int < 1) + beacon_int = 100; /* best guess */ + /* Calculate timeout in ms based on beacon_int in TU */ + timeout = disassoc_timer * beacon_int * 128 / 125; + wpa_printf(MSG_DEBUG, "Disassociation timer for " MACSTR + " set to %d ms", MAC2STR(sta->addr), timeout); + + sta->timeout_next = STA_DISASSOC_FROM_CLI; + eloop_cancel_timeout(ap_handle_timer, hapd, sta); + eloop_register_timeout(timeout / 1000, + timeout % 1000 * 1000, + ap_handle_timer, hapd, sta); + } + + return 0; +} diff -Nru wpa-1.0/src/ap/wnm_ap.h wpa-2.1/src/ap/wnm_ap.h --- wpa-1.0/src/ap/wnm_ap.h 1970-01-01 00:00:00.000000000 +0000 +++ wpa-2.1/src/ap/wnm_ap.h 2014-02-04 11:23:35.000000000 +0000 @@ -0,0 +1,22 @@ +/* + * IEEE 802.11v WNM related functions and structures + * Copyright (c) 2011-2013, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef WNM_AP_H +#define WNM_AP_H + +struct sta_info; + +int ieee802_11_rx_wnm_action_ap(struct hostapd_data *hapd, + const struct ieee80211_mgmt *mgmt, size_t len); +int wnm_send_disassoc_imminent(struct hostapd_data *hapd, + struct sta_info *sta, int disassoc_timer); +int wnm_send_ess_disassoc_imminent(struct hostapd_data *hapd, + struct sta_info *sta, const char *url, + int disassoc_timer); + +#endif /* WNM_AP_H */ diff -Nru wpa-1.0/src/ap/wpa_auth.c wpa-2.1/src/ap/wpa_auth.c --- wpa-1.0/src/ap/wpa_auth.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/ap/wpa_auth.c 2014-02-04 11:23:35.000000000 +0000 @@ -1,15 +1,9 @@ /* - * hostapd - IEEE 802.11i-2004 / WPA Authenticator - * Copyright (c) 2004-2009, Jouni Malinen + * IEEE 802.11 RSN / WPA Authenticator + * Copyright (c) 2004-2013, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "utils/includes.h" @@ -17,6 +11,7 @@ #include "utils/common.h" #include "utils/eloop.h" #include "utils/state_machine.h" +#include "utils/bitfield.h" #include "common/ieee802_11_defs.h" #include "crypto/aes_wrap.h" #include "crypto/crypto.h" @@ -52,6 +47,7 @@ static const u32 dot11RSNAConfigPairwiseUpdateCount = 4; static const u32 eapol_key_timeout_first = 100; /* ms */ static const u32 eapol_key_timeout_subseq = 1000; /* ms */ +static const u32 eapol_key_timeout_first_group = 500; /* ms */ /* TODO: make these configurable */ static const int dot11RSNAConfigPMKLifetime = 43200; @@ -59,11 +55,12 @@ static const int dot11RSNAConfigSATimeout = 60; -static inline void wpa_auth_mic_failure_report( +static inline int wpa_auth_mic_failure_report( struct wpa_authenticator *wpa_auth, const u8 *addr) { if (wpa_auth->cb.mic_failure_report) - wpa_auth->cb.mic_failure_report(wpa_auth->cb.ctx, addr); + return wpa_auth->cb.mic_failure_report(wpa_auth->cb.ctx, addr); + return 0; } @@ -86,11 +83,14 @@ static inline const u8 * wpa_auth_get_psk(struct wpa_authenticator *wpa_auth, - const u8 *addr, const u8 *prev_psk) + 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, prev_psk); + return wpa_auth->cb.get_psk(wpa_auth->cb.ctx, addr, p2p_dev_addr, + prev_psk); } @@ -194,6 +194,7 @@ { 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); } @@ -282,30 +283,12 @@ } -static void wpa_group_set_key_len(struct wpa_group *group, int cipher) -{ - switch (cipher) { - case WPA_CIPHER_CCMP: - group->GTK_len = 16; - break; - case WPA_CIPHER_TKIP: - group->GTK_len = 32; - break; - case WPA_CIPHER_WEP104: - group->GTK_len = 13; - break; - case WPA_CIPHER_WEP40: - group->GTK_len = 5; - break; - } -} - - static int wpa_group_init_gmk_and_counter(struct wpa_authenticator *wpa_auth, struct wpa_group *group) { - u8 buf[ETH_ALEN + 8 + sizeof(group)]; + u8 buf[ETH_ALEN + 8 + sizeof(unsigned long)]; u8 rkey[32]; + unsigned long ptr; if (random_get_bytes(group->GMK, WPA_GMK_LEN) < 0) return -1; @@ -317,7 +300,8 @@ */ os_memcpy(buf, wpa_auth->addr, ETH_ALEN); wpa_get_ntp_timestamp(buf + ETH_ALEN); - os_memcpy(buf + ETH_ALEN + 8, &group, sizeof(group)); + ptr = (unsigned long) group; + os_memcpy(buf + ETH_ALEN + 8, &ptr, sizeof(ptr)); if (random_get_bytes(rkey, sizeof(rkey)) < 0) return -1; @@ -342,8 +326,7 @@ group->GTKAuthenticator = TRUE; group->vlan_id = vlan_id; - - wpa_group_set_key_len(group, wpa_auth->conf.wpa_group); + group->GTK_len = wpa_cipher_key_len(wpa_auth->conf.wpa_group); if (random_pool_ready() != 1) { wpa_printf(MSG_INFO, "WPA: Not enough entropy in random pool " @@ -442,6 +425,17 @@ wpa_rekey_gtk, wpa_auth, NULL); } +#ifdef CONFIG_P2P + if (WPA_GET_BE32(conf->ip_addr_start)) { + int count = WPA_GET_BE32(conf->ip_addr_end) - + WPA_GET_BE32(conf->ip_addr_start) + 1; + if (count > 1000) + count = 1000; + if (count > 0) + wpa_auth->ip_pool = bitfield_alloc(count); + } +#endif /* CONFIG_P2P */ + return wpa_auth; } @@ -455,6 +449,8 @@ wpa_group_sm_step(wpa_auth, group); group->GInit = FALSE; wpa_group_sm_step(wpa_auth, group); + if (group->wpa_group_state == WPA_GROUP_FATAL_FAILURE) + return -1; return 0; } @@ -482,6 +478,11 @@ wpa_auth->ft_pmk_cache = NULL; #endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_P2P + bitfield_free(wpa_auth->ip_pool); +#endif /* CONFIG_P2P */ + + os_free(wpa_auth->wpa_ie); group = wpa_auth->group; @@ -518,7 +519,7 @@ * configuration. */ group = wpa_auth->group; - wpa_group_set_key_len(group, wpa_auth->conf.wpa_group); + group->GTK_len = wpa_cipher_key_len(wpa_auth->conf.wpa_group); group->GInit = TRUE; wpa_group_sm_step(wpa_auth, group); group->GInit = FALSE; @@ -529,14 +530,20 @@ struct wpa_state_machine * -wpa_auth_sta_init(struct wpa_authenticator *wpa_auth, const u8 *addr) +wpa_auth_sta_init(struct wpa_authenticator *wpa_auth, const u8 *addr, + const u8 *p2p_dev_addr) { struct wpa_state_machine *sm; + if (wpa_auth->group->wpa_group_state == WPA_GROUP_FATAL_FAILURE) + return NULL; + sm = os_zalloc(sizeof(struct wpa_state_machine)); if (sm == NULL) return NULL; os_memcpy(sm->addr, addr, ETH_ALEN); + if (p2p_dev_addr) + os_memcpy(sm->p2p_dev_addr, p2p_dev_addr, ETH_ALEN); sm->wpa_auth = wpa_auth; sm->group = wpa_auth->group; @@ -593,6 +600,19 @@ static void wpa_free_sta_sm(struct wpa_state_machine *sm) { +#ifdef CONFIG_P2P + if (WPA_GET_BE32(sm->ip_addr)) { + u32 start; + wpa_printf(MSG_DEBUG, "P2P: Free assigned IP " + "address %u.%u.%u.%u from " MACSTR, + sm->ip_addr[0], sm->ip_addr[1], + sm->ip_addr[2], sm->ip_addr[3], + MAC2STR(sm->addr)); + start = WPA_GET_BE32(sm->wpa_auth->conf.ip_addr_start); + bitfield_clear(sm->wpa_auth->ip_pool, + WPA_GET_BE32(sm->ip_addr) - start); + } +#endif /* CONFIG_P2P */ if (sm->GUpdateStationKeys) { sm->group->GKeyDoneStations--; sm->GUpdateStationKeys = FALSE; @@ -645,14 +665,14 @@ } -static int wpa_replay_counter_valid(struct wpa_state_machine *sm, +static int wpa_replay_counter_valid(struct wpa_key_replay_counter *ctr, const u8 *replay_counter) { int i; for (i = 0; i < RSNA_MAX_EAPOL_RETRIES; i++) { - if (!sm->key_replay[i].valid) + if (!ctr[i].valid) break; - if (os_memcmp(replay_counter, sm->key_replay[i].counter, + if (os_memcmp(replay_counter, ctr[i].counter, WPA_REPLAY_COUNTER_LEN) == 0) return 1; } @@ -660,6 +680,20 @@ } +static void wpa_replay_counter_mark_invalid(struct wpa_key_replay_counter *ctr, + const u8 *replay_counter) +{ + int i; + for (i = 0; i < RSNA_MAX_EAPOL_RETRIES; i++) { + if (ctr[i].valid && + (replay_counter == NULL || + os_memcmp(replay_counter, ctr[i].counter, + WPA_REPLAY_COUNTER_LEN) == 0)) + ctr[i].valid = FALSE; + } +} + + #ifdef CONFIG_IEEE80211R static int ft_check_msg_2_of_4(struct wpa_authenticator *wpa_auth, struct wpa_state_machine *sm, @@ -710,8 +744,8 @@ #endif /* CONFIG_IEEE80211R */ -static void wpa_receive_error_report(struct wpa_authenticator *wpa_auth, - struct wpa_state_machine *sm, int group) +static int wpa_receive_error_report(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm, int group) { /* Supplicant reported a Michael MIC error */ wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO, @@ -728,7 +762,8 @@ "ignore Michael MIC failure report since " "pairwise cipher is not TKIP"); } else { - wpa_auth_mic_failure_report(wpa_auth, sm->addr); + if (wpa_auth_mic_failure_report(wpa_auth, sm->addr) > 0) + return 1; /* STA entry was removed */ sm->dot11RSNAStatsTKIPRemoteMICFailures++; wpa_auth->dot11RSNAStatsTKIPRemoteMICFailures++; } @@ -738,6 +773,7 @@ * Authenticator may do it, let's change the keys now anyway. */ wpa_request_new_ptk(sm); + return 0; } @@ -839,7 +875,8 @@ 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) { + if (sm->pairwise == WPA_CIPHER_CCMP || + sm->pairwise == WPA_CIPHER_GCMP) { if (wpa_use_aes_cmac(sm) && ver != WPA_KEY_INFO_TYPE_AES_128_CMAC) { wpa_auth_logger(wpa_auth, sm->addr, @@ -855,7 +892,7 @@ wpa_auth_logger(wpa_auth, sm->addr, LOGGER_WARNING, "did not use HMAC-SHA1-AES " - "with CCMP"); + "with CCMP/GCMP"); return; } } @@ -873,11 +910,44 @@ } if (!(key_info & WPA_KEY_INFO_REQUEST) && - !wpa_replay_counter_valid(sm, key->replay_counter)) { + !wpa_replay_counter_valid(sm->key_replay, key->replay_counter)) { int i; - wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG, - "received EAPOL-Key %s with unexpected " - "replay counter", msgtxt); + + if (msg == PAIRWISE_2 && + wpa_replay_counter_valid(sm->prev_key_replay, + key->replay_counter) && + sm->wpa_ptk_state == WPA_PTK_PTKINITNEGOTIATING && + os_memcmp(sm->SNonce, key->key_nonce, WPA_NONCE_LEN) != 0) + { + /* + * Some supplicant implementations (e.g., Windows XP + * WZC) update SNonce for each EAPOL-Key 2/4. This + * breaks the workaround on accepting any of the + * pending requests, so allow the SNonce to be updated + * even if we have already sent out EAPOL-Key 3/4. + */ + wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG, + "Process SNonce update from STA " + "based on retransmitted EAPOL-Key " + "1/4"); + sm->update_snonce = 1; + wpa_replay_counter_mark_invalid(sm->prev_key_replay, + key->replay_counter); + goto continue_processing; + } + + if (msg == PAIRWISE_2 && + wpa_replay_counter_valid(sm->prev_key_replay, + key->replay_counter) && + sm->wpa_ptk_state == WPA_PTK_PTKINITNEGOTIATING) { + wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG, + "ignore retransmitted EAPOL-Key %s - " + "SNonce did not change", msgtxt); + } else { + wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG, + "received EAPOL-Key %s with " + "unexpected replay counter", msgtxt); + } for (i = 0; i < RSNA_MAX_EAPOL_RETRIES; i++) { if (!sm->key_replay[i].valid) break; @@ -890,10 +960,13 @@ return; } +continue_processing: switch (msg) { case PAIRWISE_2: if (sm->wpa_ptk_state != WPA_PTK_PTKSTART && - sm->wpa_ptk_state != WPA_PTK_PTKCALCNEGOTIATING) { + sm->wpa_ptk_state != WPA_PTK_PTKCALCNEGOTIATING && + (!sm->update_snonce || + sm->wpa_ptk_state != WPA_PTK_PTKINITNEGOTIATING)) { wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO, "received EAPOL-Key msg 2/4 in " "invalid state (%d) - dropped", @@ -914,9 +987,7 @@ wpa_printf(MSG_DEBUG, "WPA: Reject 4-way handshake to " "collect more entropy for random number " "generation"); - sm->group->reject_4way_hs_for_entropy = FALSE; random_mark_pool_ready(); - sm->group->first_sta_seen = FALSE; wpa_sta_disconnect(wpa_auth, sm->addr); return; } @@ -959,6 +1030,26 @@ 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 || @@ -1022,7 +1113,7 @@ } sm->MICVerified = FALSE; - if (sm->PTK_valid) { + if (sm->PTK_valid && !sm->update_snonce) { if (wpa_verify_key_mic(&sm->PTK, data, data_len)) { wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, "received EAPOL-Key with invalid MIC"); @@ -1056,9 +1147,10 @@ #endif /* CONFIG_PEERKEY */ return; } else if (key_info & WPA_KEY_INFO_ERROR) { - wpa_receive_error_report( - wpa_auth, sm, - !(key_info & WPA_KEY_INFO_KEY_TYPE)); + if (wpa_receive_error_report( + wpa_auth, sm, + !(key_info & WPA_KEY_INFO_KEY_TYPE)) > 0) + return; /* STA entry was removed */ } else if (key_info & WPA_KEY_INFO_KEY_TYPE) { wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, "received EAPOL-Key Request for new " @@ -1080,12 +1172,30 @@ wpa_rekey_gtk(wpa_auth, NULL); } } else { - /* Do not allow the same key replay counter to be reused. This - * does also invalidate all other pending replay counters if - * retransmissions were used, i.e., we will only process one of - * the pending replies and ignore rest if more than one is - * received. */ - sm->key_replay[0].valid = FALSE; + /* Do not allow the same key replay counter to be reused. */ + wpa_replay_counter_mark_invalid(sm->key_replay, + key->replay_counter); + + if (msg == PAIRWISE_2) { + /* + * Maintain a copy of the pending EAPOL-Key frames in + * case the EAPOL-Key frame was retransmitted. This is + * needed to allow EAPOL-Key msg 2/4 reply to another + * pending msg 1/4 to update the SNonce to work around + * unexpected supplicant behavior. + */ + os_memcpy(sm->prev_key_replay, sm->key_replay, + sizeof(sm->key_replay)); + } else { + os_memset(sm->prev_key_replay, 0, + sizeof(sm->prev_key_replay)); + } + + /* + * Make sure old valid counters are not accepted anymore and + * do not get copied again. + */ + wpa_replay_counter_mark_invalid(sm->key_replay, NULL); } #ifdef CONFIG_PEERKEY @@ -1178,12 +1288,12 @@ version = force_version; else if (wpa_use_aes_cmac(sm)) version = WPA_KEY_INFO_TYPE_AES_128_CMAC; - else if (sm->pairwise == WPA_CIPHER_CCMP) + else if (sm->pairwise != WPA_CIPHER_TKIP) version = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES; else version = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4; - pairwise = key_info & WPA_KEY_INFO_KEY_TYPE; + pairwise = !!(key_info & WPA_KEY_INFO_KEY_TYPE); wpa_printf(MSG_DEBUG, "WPA: Send EAPOL(version=%d secure=%d mic=%d " "ack=%d install=%d pairwise=%d kde_len=%lu keyidx=%d " @@ -1225,20 +1335,7 @@ WPA_PUT_BE16(key->key_info, key_info); alg = pairwise ? sm->pairwise : wpa_auth->conf.wpa_group; - switch (alg) { - case WPA_CIPHER_CCMP: - WPA_PUT_BE16(key->key_length, 16); - break; - case WPA_CIPHER_TKIP: - WPA_PUT_BE16(key->key_length, 32); - break; - case WPA_CIPHER_WEP40: - WPA_PUT_BE16(key->key_length, 5); - break; - case WPA_CIPHER_WEP104: - WPA_PUT_BE16(key->key_length, 13); - break; - } + WPA_PUT_BE16(key->key_length, wpa_cipher_key_len(alg)); if (key_info & WPA_KEY_INFO_SMK_MESSAGE) WPA_PUT_BE16(key->key_length, 0); @@ -1311,6 +1408,16 @@ } wpa_eapol_key_mic(sm->PTK.kck, version, (u8 *) hdr, len, key->key_mic); +#ifdef CONFIG_TESTING_OPTIONS + if (!pairwise && + wpa_auth->conf.corrupt_gtk_rekey_mic_probability > 0.0d && + drand48() < + wpa_auth->conf.corrupt_gtk_rekey_mic_probability) { + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, + "Corrupting group EAPOL-Key Key MIC"); + key->key_mic[0]++; + } +#endif /* CONFIG_TESTING_OPTIONS */ } wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_inc_EapolFramesTx, @@ -1339,7 +1446,8 @@ ctr = pairwise ? sm->TimeoutCtr : sm->GTimeoutCtr; if (ctr == 1 && wpa_auth->conf.tx_status) - timeout_ms = eapol_key_timeout_first; + timeout_ms = pairwise ? eapol_key_timeout_first : + eapol_key_timeout_first_group; else timeout_ms = eapol_key_timeout_subseq; if (pairwise && ctr == 1 && !(key_info & WPA_KEY_INFO_MIC)) @@ -1470,22 +1578,6 @@ } -static enum wpa_alg wpa_alg_enum(int alg) -{ - switch (alg) { - case WPA_CIPHER_CCMP: - return WPA_ALG_CCMP; - case WPA_CIPHER_TKIP: - return WPA_ALG_TKIP; - case WPA_CIPHER_WEP104: - case WPA_CIPHER_WEP40: - return WPA_ALG_WEP; - default: - return WPA_ALG_NONE; - } -} - - SM_STATE(WPA_PTK, INITIALIZE) { SM_ENTRY_MA(WPA_PTK, INITIALIZE, wpa_ptk); @@ -1543,9 +1635,11 @@ } -static void wpa_group_first_station(struct wpa_authenticator *wpa_auth, - struct wpa_group *group) +static void wpa_group_ensure_init(struct wpa_authenticator *wpa_auth, + struct wpa_group *group) { + if (group->first_sta_seen) + return; /* * System has run bit further than at the time hostapd was started * potentially very early during boot up. This provides better chances @@ -1559,7 +1653,11 @@ wpa_printf(MSG_INFO, "WPA: Not enough entropy in random pool " "to proceed - reject first 4-way handshake"); group->reject_4way_hs_for_entropy = TRUE; + } else { + group->first_sta_seen = TRUE; + group->reject_4way_hs_for_entropy = FALSE; } + wpa_group_init_gmk_and_counter(wpa_auth, group); wpa_gtk_update(wpa_auth, group); wpa_group_config_group_keys(wpa_auth, group); @@ -1570,16 +1668,26 @@ { SM_ENTRY_MA(WPA_PTK, AUTHENTICATION2, wpa_ptk); - if (!sm->group->first_sta_seen) { - wpa_group_first_station(sm->wpa_auth, sm->group); - sm->group->first_sta_seen = TRUE; - } + wpa_group_ensure_init(sm->wpa_auth, sm->group); + sm->ReAuthenticationRequest = FALSE; - os_memcpy(sm->ANonce, sm->group->Counter, WPA_NONCE_LEN); + /* + * Definition of ANonce selection in IEEE Std 802.11i-2004 is somewhat + * ambiguous. The Authenticator state machine uses a counter that is + * incremented by one for each 4-way handshake. However, the security + * analysis of 4-way handshake points out that unpredictable nonces + * help in preventing precomputation attacks. Instead of the state + * machine definition, use an unpredictable nonce value here to provide + * stronger protection against potential precomputation attacks. + */ + if (random_get_bytes(sm->ANonce, WPA_NONCE_LEN)) { + wpa_printf(MSG_ERROR, "WPA: Failed to get random data for " + "ANonce."); + sm->Disconnect = TRUE; + return; + } wpa_hexdump(MSG_DEBUG, "WPA: Assign ANonce", sm->ANonce, WPA_NONCE_LEN); - inc_byte_array(sm->group->Counter, WPA_NONCE_LEN); - sm->ReAuthenticationRequest = FALSE; /* IEEE 802.11i does not clear TimeoutCtr here, but this is more * logical place than INITIALIZE since AUTHENTICATION2 can be * re-entered on ReAuthenticationRequest without going through @@ -1631,7 +1739,7 @@ { const u8 *psk; SM_ENTRY_MA(WPA_PTK, INITPSK, wpa_ptk); - psk = wpa_auth_get_psk(sm->wpa_auth, sm->addr, NULL); + psk = wpa_auth_get_psk(sm->wpa_auth, sm->addr, sm->p2p_dev_addr, NULL); if (psk) { os_memcpy(sm->PMK, psk, PMK_LEN); #ifdef CONFIG_IEEE80211R @@ -1694,7 +1802,7 @@ static int wpa_derive_ptk(struct wpa_state_machine *sm, const u8 *pmk, struct wpa_ptk *ptk) { - size_t ptk_len = sm->pairwise == WPA_CIPHER_CCMP ? 48 : 64; + size_t ptk_len = sm->pairwise != WPA_CIPHER_TKIP ? 48 : 64; #ifdef CONFIG_IEEE80211R if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) return wpa_auth_derive_ptk_ft(sm, pmk, ptk, ptk_len); @@ -1717,13 +1825,15 @@ SM_ENTRY_MA(WPA_PTK, PTKCALCNEGOTIATING, wpa_ptk); sm->EAPOLKeyReceived = FALSE; + sm->update_snonce = FALSE; /* WPA with IEEE 802.1X: use the derived PMK from EAP * WPA-PSK: iterate through possible PSKs and select the one matching * the packet */ for (;;) { if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) { - pmk = wpa_auth_get_psk(sm->wpa_auth, sm->addr, pmk); + pmk = wpa_auth_get_psk(sm->wpa_auth, sm->addr, + sm->p2p_dev_addr, pmk); if (pmk == NULL) break; } else @@ -1808,6 +1918,7 @@ { struct wpa_igtk_kde igtk; struct wpa_group *gsm = sm->group; + u8 rsc[WPA_KEY_RSC_LEN]; if (!sm->mgmt_frame_prot) return pos; @@ -1815,9 +1926,19 @@ igtk.keyid[0] = gsm->GN_igtk; igtk.keyid[1] = 0; if (gsm->wpa_group_state != WPA_GROUP_SETKEYSDONE || - wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN_igtk, igtk.pn) < 0) + wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN_igtk, rsc) < 0) os_memset(igtk.pn, 0, sizeof(igtk.pn)); + else + os_memcpy(igtk.pn, rsc, sizeof(igtk.pn)); os_memcpy(igtk.igtk, gsm->IGTK[gsm->GN_igtk - 4], WPA_IGTK_LEN); + if (sm->wpa_auth->conf.disable_gtk) { + /* + * Provide unique random IGTK to each STA to prevent use of + * IGTK in the BSS. + */ + if (random_get_bytes(igtk.igtk, WPA_IGTK_LEN) < 0) + return pos; + } pos = wpa_add_kde(pos, RSN_KEY_DATA_IGTK, (const u8 *) &igtk, sizeof(igtk), NULL, 0); @@ -1842,7 +1963,7 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING) { - u8 rsc[WPA_KEY_RSC_LEN], *_rsc, *gtk, *kde, *pos; + u8 rsc[WPA_KEY_RSC_LEN], *_rsc, *gtk, *kde, *pos, dummy_gtk[32]; size_t gtk_len, kde_len; struct wpa_group *gsm = sm->group; u8 *wpa_ie; @@ -1880,6 +2001,15 @@ secure = 1; gtk = gsm->GTK[gsm->GN - 1]; gtk_len = gsm->GTK_len; + if (sm->wpa_auth->conf.disable_gtk) { + /* + * Provide unique random GTK to each STA to prevent use + * of GTK in the BSS. + */ + if (random_get_bytes(dummy_gtk, gtk_len) < 0) + return; + gtk = dummy_gtk; + } keyidx = gsm->GN; _rsc = rsc; encr = 1; @@ -1915,6 +2045,10 @@ kde_len += 300; /* FTIE + 2 * TIE */ } #endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_P2P + if (WPA_GET_BE32(sm->ip_addr) > 0) + kde_len += 2 + RSN_SELECTOR_LEN + 3 * 4; +#endif /* CONFIG_P2P */ kde = os_malloc(kde_len); if (kde == NULL) return; @@ -1976,6 +2110,16 @@ pos += 4; } #endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_P2P + if (WPA_GET_BE32(sm->ip_addr) > 0) { + u8 addr[3 * 4]; + os_memcpy(addr, sm->ip_addr, 4); + os_memcpy(addr + 4, sm->wpa_auth->conf.ip_addr_mask, 4); + os_memcpy(addr + 8, sm->wpa_auth->conf.ip_addr_go, 4); + pos = wpa_add_kde(pos, WFA_KEY_DATA_IP_ADDR_ALLOC, + addr, sizeof(addr), NULL, 0); + } +#endif /* CONFIG_P2P */ wpa_send_eapol(sm->wpa_auth, sm, (secure ? WPA_KEY_INFO_SECURE : 0) | WPA_KEY_INFO_MIC | @@ -1991,15 +2135,8 @@ SM_ENTRY_MA(WPA_PTK, PTKINITDONE, wpa_ptk); sm->EAPOLKeyReceived = FALSE; if (sm->Pair) { - enum wpa_alg alg; - int klen; - if (sm->pairwise == WPA_CIPHER_TKIP) { - alg = WPA_ALG_TKIP; - klen = 32; - } else { - alg = WPA_ALG_CCMP; - klen = 16; - } + enum wpa_alg alg = wpa_cipher_to_alg(sm->pairwise); + int klen = wpa_cipher_key_len(sm->pairwise); if (wpa_auth_set_key(sm->wpa_auth, 0, alg, sm->addr, 0, sm->PTK.tk1, klen)) { wpa_sta_disconnect(sm->wpa_auth, sm->addr); @@ -2100,7 +2237,8 @@ } break; case WPA_PTK_INITPSK: - if (wpa_auth_get_psk(sm->wpa_auth, sm->addr, NULL)) + if (wpa_auth_get_psk(sm->wpa_auth, sm->addr, sm->p2p_dev_addr, + NULL)) SM_ENTER(WPA_PTK, PTKSTART); else { wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_INFO, @@ -2136,8 +2274,10 @@ SM_ENTER(WPA_PTK, PTKINITNEGOTIATING); break; case WPA_PTK_PTKINITNEGOTIATING: - if (sm->EAPOLKeyReceived && !sm->EAPOLKeyRequest && - sm->EAPOLKeyPairwise && sm->MICVerified) + if (sm->update_snonce) + SM_ENTER(WPA_PTK, PTKCALCNEGOTIATING); + else if (sm->EAPOLKeyReceived && !sm->EAPOLKeyRequest && + sm->EAPOLKeyPairwise && sm->MICVerified) SM_ENTER(WPA_PTK, PTKINITDONE); else if (sm->TimeoutCtr > (int) dot11RSNAConfigPairwiseUpdateCount) { @@ -2174,6 +2314,7 @@ struct wpa_group *gsm = sm->group; u8 *kde, *pos, hdr[2]; size_t kde_len; + u8 *gtk, dummy_gtk[32]; SM_ENTRY_MA(WPA_PTK_GROUP, REKEYNEGOTIATING, wpa_ptk_group); @@ -2194,6 +2335,16 @@ wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, "sending 1/2 msg of Group Key Handshake"); + gtk = gsm->GTK[gsm->GN - 1]; + if (sm->wpa_auth->conf.disable_gtk) { + /* + * Provide unique random GTK to each STA to prevent use + * of GTK in the BSS. + */ + if (random_get_bytes(dummy_gtk, gsm->GTK_len) < 0) + return; + gtk = dummy_gtk; + } if (sm->wpa == WPA_VERSION_WPA2) { kde_len = 2 + RSN_SELECTOR_LEN + 2 + gsm->GTK_len + ieee80211w_kde_len(sm); @@ -2205,10 +2356,10 @@ hdr[0] = gsm->GN & 0x03; hdr[1] = 0; pos = wpa_add_kde(pos, RSN_KEY_DATA_GROUPKEY, hdr, 2, - gsm->GTK[gsm->GN - 1], gsm->GTK_len); + gtk, gsm->GTK_len); pos = ieee80211w_kde_add(sm, pos); } else { - kde = gsm->GTK[gsm->GN - 1]; + kde = gtk; pos = kde + gsm->GTK_len; } @@ -2334,6 +2485,9 @@ static int wpa_group_update_sta(struct wpa_state_machine *sm, void *ctx) { + if (ctx != NULL && ctx != sm->group) + return 0; + if (sm->wpa_ptk_state != WPA_PTK_PTKINITDONE) { wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, "Not in PTKINITDONE; skip Group Key update"); @@ -2351,6 +2505,10 @@ "marking station for GTK rekeying"); } + /* Do not rekey GTK/IGTK when STA is in WNM-Sleep Mode */ + if (sm->is_wnmsleep) + return 0; + sm->group->GKeyDoneStations++; sm->GUpdateStationKeys = TRUE; @@ -2359,6 +2517,87 @@ } +#ifdef CONFIG_WNM +/* update GTK when exiting WNM-Sleep Mode */ +void wpa_wnmsleep_rekey_gtk(struct wpa_state_machine *sm) +{ + if (sm == NULL || sm->is_wnmsleep) + return; + + wpa_group_update_sta(sm, NULL); +} + + +void wpa_set_wnmsleep(struct wpa_state_machine *sm, int flag) +{ + if (sm) + sm->is_wnmsleep = !!flag; +} + + +int wpa_wnmsleep_gtk_subelem(struct wpa_state_machine *sm, u8 *pos) +{ + struct wpa_group *gsm = sm->group; + u8 *start = pos; + + /* + * GTK subelement: + * Sub-elem ID[1] | Length[1] | Key Info[2] | Key Length[1] | RSC[8] | + * Key[5..32] + */ + *pos++ = WNM_SLEEP_SUBELEM_GTK; + *pos++ = 11 + gsm->GTK_len; + /* Key ID in B0-B1 of Key Info */ + WPA_PUT_LE16(pos, gsm->GN & 0x03); + pos += 2; + *pos++ = gsm->GTK_len; + if (wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN, pos) != 0) + return 0; + pos += 8; + os_memcpy(pos, gsm->GTK[gsm->GN - 1], gsm->GTK_len); + pos += gsm->GTK_len; + + wpa_printf(MSG_DEBUG, "WNM: GTK Key ID %u in WNM-Sleep Mode exit", + gsm->GN); + wpa_hexdump_key(MSG_DEBUG, "WNM: GTK in WNM-Sleep Mode exit", + gsm->GTK[gsm->GN - 1], gsm->GTK_len); + + return pos - start; +} + + +#ifdef CONFIG_IEEE80211W +int wpa_wnmsleep_igtk_subelem(struct wpa_state_machine *sm, u8 *pos) +{ + struct wpa_group *gsm = sm->group; + u8 *start = pos; + + /* + * IGTK subelement: + * Sub-elem ID[1] | Length[1] | KeyID[2] | PN[6] | Key[16] + */ + *pos++ = WNM_SLEEP_SUBELEM_IGTK; + *pos++ = 2 + 6 + WPA_IGTK_LEN; + WPA_PUT_LE16(pos, gsm->GN_igtk); + pos += 2; + if (wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN_igtk, pos) != 0) + return 0; + pos += 6; + + os_memcpy(pos, gsm->IGTK[gsm->GN_igtk - 4], WPA_IGTK_LEN); + pos += WPA_IGTK_LEN; + + wpa_printf(MSG_DEBUG, "WNM: IGTK Key ID %u in WNM-Sleep Mode exit", + gsm->GN_igtk); + wpa_hexdump_key(MSG_DEBUG, "WNM: IGTK in WNM-Sleep Mode exit", + gsm->IGTK[gsm->GN_igtk - 4], WPA_IGTK_LEN); + + return pos - start; +} +#endif /* CONFIG_IEEE80211W */ +#endif /* CONFIG_WNM */ + + static void wpa_group_setkeys(struct wpa_authenticator *wpa_auth, struct wpa_group *group) { @@ -2388,7 +2627,7 @@ group->GKeyDoneStations); group->GKeyDoneStations = 0; } - wpa_auth_for_each_sta(wpa_auth, wpa_group_update_sta, NULL); + wpa_auth_for_each_sta(wpa_auth, wpa_group_update_sta, group); wpa_printf(MSG_DEBUG, "wpa_group_setkeys: GKeyDoneStations=%d", group->GKeyDoneStations); } @@ -2400,7 +2639,7 @@ int ret = 0; if (wpa_auth_set_key(wpa_auth, group->vlan_id, - wpa_alg_enum(wpa_auth->conf.wpa_group), + wpa_cipher_to_alg(wpa_auth->conf.wpa_group), broadcast_ether_addr, group->GN, group->GTK[group->GN - 1], group->GTK_len) < 0) ret = -1; @@ -2418,6 +2657,29 @@ } +static int wpa_group_disconnect_cb(struct wpa_state_machine *sm, void *ctx) +{ + if (sm->group == ctx) { + wpa_printf(MSG_DEBUG, "WPA: Mark STA " MACSTR + " for discconnection due to fatal failure", + MAC2STR(sm->addr)); + sm->Disconnect = TRUE; + } + + return 0; +} + + +static void wpa_group_fatal_failure(struct wpa_authenticator *wpa_auth, + struct wpa_group *group) +{ + wpa_printf(MSG_DEBUG, "WPA: group state machine entering state FATAL_FAILURE"); + group->changed = TRUE; + group->wpa_group_state = WPA_GROUP_FATAL_FAILURE; + wpa_auth_for_each_sta(wpa_auth, wpa_group_disconnect_cb, group); +} + + static int wpa_group_setkeysdone(struct wpa_authenticator *wpa_auth, struct wpa_group *group) { @@ -2426,8 +2688,10 @@ group->changed = TRUE; group->wpa_group_state = WPA_GROUP_SETKEYSDONE; - if (wpa_group_config_group_keys(wpa_auth, group) < 0) + if (wpa_group_config_group_keys(wpa_auth, group) < 0) { + wpa_group_fatal_failure(wpa_auth, group); return -1; + } return 0; } @@ -2438,6 +2702,8 @@ { if (group->GInit) { wpa_group_gtk_init(wpa_auth, group); + } else if (group->wpa_group_state == WPA_GROUP_FATAL_FAILURE) { + /* Do not allow group operations */ } else if (group->wpa_group_state == WPA_GROUP_GTK_INIT && group->GTKAuthenticator) { wpa_group_setkeysdone(wpa_auth, group); @@ -2540,23 +2806,6 @@ } -static int wpa_cipher_bits(int cipher) -{ - switch (cipher) { - case WPA_CIPHER_CCMP: - return 128; - case WPA_CIPHER_TKIP: - return 256; - case WPA_CIPHER_WEP104: - return 104; - case WPA_CIPHER_WEP40: - return 40; - default: - return 0; - } -} - - #define RSN_SUITE "%02x-%02x-%02x-%d" #define RSN_SUITE_ARG(s) \ ((s) >> 24) & 0xff, ((s) >> 16) & 0xff, ((s) >> 8) & 0xff, (s) & 0xff @@ -2619,7 +2868,7 @@ !!wpa_auth->conf.wpa_strict_rekey, dot11RSNAConfigGroupUpdateCount, dot11RSNAConfigPairwiseUpdateCount, - wpa_cipher_bits(wpa_auth->conf.wpa_group), + wpa_cipher_key_len(wpa_auth->conf.wpa_group) * 8, dot11RSNAConfigPMKLifetime, dot11RSNAConfigPMKReauthThreshold, dot11RSNAConfigSATimeout, @@ -2662,29 +2911,10 @@ /* dot11RSNAStatsEntry */ - if (sm->wpa == WPA_VERSION_WPA) { - if (sm->pairwise == WPA_CIPHER_CCMP) - pairwise = WPA_CIPHER_SUITE_CCMP; - else if (sm->pairwise == WPA_CIPHER_TKIP) - pairwise = WPA_CIPHER_SUITE_TKIP; - else if (sm->pairwise == WPA_CIPHER_WEP104) - pairwise = WPA_CIPHER_SUITE_WEP104; - else if (sm->pairwise == WPA_CIPHER_WEP40) - pairwise = WPA_CIPHER_SUITE_WEP40; - else if (sm->pairwise == WPA_CIPHER_NONE) - pairwise = WPA_CIPHER_SUITE_NONE; - } else if (sm->wpa == WPA_VERSION_WPA2) { - if (sm->pairwise == WPA_CIPHER_CCMP) - pairwise = RSN_CIPHER_SUITE_CCMP; - else if (sm->pairwise == WPA_CIPHER_TKIP) - pairwise = RSN_CIPHER_SUITE_TKIP; - else if (sm->pairwise == WPA_CIPHER_WEP104) - pairwise = RSN_CIPHER_SUITE_WEP104; - else if (sm->pairwise == WPA_CIPHER_WEP40) - pairwise = RSN_CIPHER_SUITE_WEP40; - else if (sm->pairwise == WPA_CIPHER_NONE) - pairwise = RSN_CIPHER_SUITE_NONE; - } else + pairwise = wpa_cipher_to_suite(sm->wpa == WPA_VERSION_WPA2 ? + WPA_PROTO_RSN : WPA_PROTO_WPA, + sm->pairwise); + if (pairwise == 0) return 0; ret = os_snprintf( @@ -2822,6 +3052,22 @@ } +void wpa_auth_pmksa_remove(struct wpa_authenticator *wpa_auth, + const u8 *sta_addr) +{ + struct rsn_pmksa_cache_entry *pmksa; + + if (wpa_auth == NULL || wpa_auth->pmksa == NULL) + return; + pmksa = pmksa_cache_auth_get(wpa_auth->pmksa, sta_addr, NULL); + if (pmksa) { + wpa_printf(MSG_DEBUG, "WPA: Remove PMKSA cache entry for " + MACSTR " based on request", MAC2STR(sta_addr)); + pmksa_cache_free_entry(wpa_auth->pmksa, pmksa); + } +} + + static struct wpa_group * wpa_auth_add_group(struct wpa_authenticator *wpa_auth, int vlan_id) { @@ -2866,6 +3112,9 @@ if (sm->group == group) return 0; + if (group->wpa_group_state == WPA_GROUP_FATAL_FAILURE) + return -1; + wpa_printf(MSG_DEBUG, "WPA: Moving STA " MACSTR " to use group state " "machine for VLAN ID %d", MAC2STR(sm->addr), vlan_id); @@ -2902,3 +3151,30 @@ wpa_send_eapol_timeout, wpa_auth, sm); } } + + +int wpa_auth_uses_sae(struct wpa_state_machine *sm) +{ + if (sm == NULL) + return 0; + return wpa_key_mgmt_sae(sm->wpa_key_mgmt); +} + + +int wpa_auth_uses_ft_sae(struct wpa_state_machine *sm) +{ + if (sm == NULL) + return 0; + return sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_SAE; +} + + +#ifdef CONFIG_P2P +int wpa_auth_get_ip_addr(struct wpa_state_machine *sm, u8 *addr) +{ + if (sm == NULL || WPA_GET_BE32(sm->ip_addr) == 0) + return -1; + os_memcpy(addr, sm->ip_addr, 4); + return 0; +} +#endif /* CONFIG_P2P */ diff -Nru wpa-1.0/src/ap/wpa_auth_ft.c wpa-2.1/src/ap/wpa_auth_ft.c --- wpa-1.0/src/ap/wpa_auth_ft.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/ap/wpa_auth_ft.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * hostapd - IEEE 802.11r - Fast BSS Transition * Copyright (c) 2004-2009, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "utils/includes.h" @@ -24,7 +18,6 @@ #include "wmm.h" #include "wpa_auth.h" #include "wpa_auth_i.h" -#include "wpa_auth_ie.h" #ifdef CONFIG_IEEE80211R @@ -59,6 +52,19 @@ } +static int wpa_ft_add_tspec(struct wpa_authenticator *wpa_auth, + const u8 *sta_addr, + u8 *tspec_ie, size_t tspec_ielen) +{ + if (wpa_auth->cb.add_tspec == NULL) { + wpa_printf(MSG_DEBUG, "FT: add_tspec is not initialized"); + return -1; + } + return wpa_auth->cb.add_tspec(wpa_auth->cb.ctx, sta_addr, tspec_ie, + tspec_ielen); +} + + int wpa_write_mdie(struct wpa_auth_config *conf, u8 *buf, size_t len) { u8 *pos = buf; @@ -323,6 +329,7 @@ os_memcpy(f.pmk_r0_name, pmk_r0_name, WPA_PMK_NAME_LEN); os_memcpy(f.r1kh_id, wpa_auth->conf.r1_key_holder, FT_R1KH_ID_LEN); os_memcpy(f.s1kh_id, s1kh_id, ETH_ALEN); + os_memset(f.pad, 0, sizeof(f.pad)); if (aes_wrap(r0kh->key, (FT_R0KH_R1KH_PULL_DATA_LEN + 7) / 8, f.nonce, frame.nonce) < 0) @@ -410,7 +417,7 @@ pad_len = 8 - pad_len; if (key_len + pad_len < 16) pad_len += 8; - if (pad_len) { + if (pad_len && key_len < sizeof(keybuf)) { os_memcpy(keybuf, gsm->GTK[gsm->GN - 1], key_len); os_memset(keybuf + key_len, 0, pad_len); keybuf[key_len] = 0xdd; @@ -478,7 +485,8 @@ #endif /* CONFIG_IEEE80211W */ -static u8 * wpa_ft_process_rdie(u8 *pos, u8 *end, u8 id, u8 descr_count, +static u8 * wpa_ft_process_rdie(struct wpa_state_machine *sm, + u8 *pos, u8 *end, u8 id, u8 descr_count, const u8 *ies, size_t ies_len) { struct ieee802_11_elems parse; @@ -511,7 +519,7 @@ } #ifdef NEED_AP_MLME - if (parse.wmm_tspec) { + if (parse.wmm_tspec && sm->wpa_auth->conf.ap_mlme) { struct wmm_tspec_element *tspec; int res; @@ -548,13 +556,35 @@ } #endif /* NEED_AP_MLME */ + if (parse.wmm_tspec && !sm->wpa_auth->conf.ap_mlme) { + struct wmm_tspec_element *tspec; + int res; + + tspec = (struct wmm_tspec_element *) pos; + os_memcpy(tspec, parse.wmm_tspec - 2, sizeof(*tspec)); + res = wpa_ft_add_tspec(sm->wpa_auth, sm->addr, pos, + sizeof(*tspec)); + if (res >= 0) { + if (res) + rdie->status_code = host_to_le16(res); + else { + /* TSPEC accepted; include updated TSPEC in + * response */ + rdie->descr_count = 1; + pos += sizeof(*tspec); + } + return pos; + } + } + wpa_printf(MSG_DEBUG, "FT: No supported resource requested"); rdie->status_code = host_to_le16(WLAN_STATUS_UNSPECIFIED_FAILURE); return pos; } -static u8 * wpa_ft_process_ric(u8 *pos, u8 *end, const u8 *ric, size_t ric_len) +static u8 * wpa_ft_process_ric(struct wpa_state_machine *sm, u8 *pos, u8 *end, + const u8 *ric, size_t ric_len) { const u8 *rpos, *start; const struct rsn_rdie *rdie; @@ -576,7 +606,7 @@ break; rpos += 2 + rpos[1]; } - pos = wpa_ft_process_rdie(pos, end, rdie->id, + pos = wpa_ft_process_rdie(sm, pos, end, rdie->id, rdie->descr_count, start, rpos - start); } @@ -603,8 +633,7 @@ conf = &sm->wpa_auth->conf; - if (sm->wpa_key_mgmt != WPA_KEY_MGMT_FT_IEEE8021X && - sm->wpa_key_mgmt != WPA_KEY_MGMT_FT_PSK) + if (!wpa_key_mgmt_ft(sm->wpa_key_mgmt)) return pos; end = pos + max_len; @@ -685,7 +714,8 @@ ric_start = pos; if (wpa_ft_parse_ies(req_ies, req_ies_len, &parse) == 0 && parse.ric) { - pos = wpa_ft_process_ric(pos, end, parse.ric, parse.ric_len); + pos = wpa_ft_process_ric(sm, pos, end, parse.ric, + parse.ric_len); if (auth_alg == WLAN_AUTH_FT) _ftie->mic_control[1] += ieee802_11_ie_count(ric_start, @@ -724,13 +754,9 @@ int klen; /* MLME-SETKEYS.request(PTK) */ - if (sm->pairwise == WPA_CIPHER_TKIP) { - alg = WPA_ALG_TKIP; - klen = 32; - } else if (sm->pairwise == WPA_CIPHER_CCMP) { - alg = WPA_ALG_CCMP; - klen = 16; - } else { + alg = wpa_cipher_to_alg(sm->pairwise); + klen = wpa_cipher_key_len(sm->pairwise); + if (!wpa_cipher_valid_pairwise(sm->pairwise)) { wpa_printf(MSG_DEBUG, "FT: Unknown pairwise alg 0x%x - skip " "PTK configuration", sm->pairwise); return; @@ -852,7 +878,7 @@ wpa_hexdump(MSG_DEBUG, "FT: Generated ANonce", sm->ANonce, WPA_NONCE_LEN); - ptk_len = pairwise != WPA_CIPHER_CCMP ? 64 : 48; + ptk_len = pairwise == WPA_CIPHER_TKIP ? 64 : 48; wpa_pmk_r1_to_ptk(pmk_r1, sm->SNonce, sm->ANonce, sm->addr, sm->wpa_auth->addr, pmk_r1_name, (u8 *) &sm->PTK, ptk_len, ptk_name); @@ -1068,8 +1094,16 @@ if (os_memcmp(mic, ftie->mic, 16) != 0) { wpa_printf(MSG_DEBUG, "FT: Invalid MIC in FTIE"); + wpa_printf(MSG_DEBUG, "FT: addr=" MACSTR " auth_addr=" MACSTR, + MAC2STR(sm->addr), MAC2STR(sm->wpa_auth->addr)); wpa_hexdump(MSG_MSGDUMP, "FT: Received MIC", ftie->mic, 16); wpa_hexdump(MSG_MSGDUMP, "FT: Calculated MIC", mic, 16); + wpa_hexdump(MSG_MSGDUMP, "FT: MDIE", + parse.mdie - 2, parse.mdie_len + 2); + wpa_hexdump(MSG_MSGDUMP, "FT: FTIE", + parse.ftie - 2, parse.ftie_len + 2); + wpa_hexdump(MSG_MSGDUMP, "FT: RSN", + parse.rsn - 2, parse.rsn_len + 2); return WLAN_STATUS_INVALID_FTIE; } @@ -1132,6 +1166,8 @@ /* RRB - Forward action frame to the target AP */ frame = os_malloc(sizeof(*frame) + len); + if (frame == NULL) + return -1; frame->frame_type = RSN_REMOTE_FRAME_TYPE_FT_RRB; frame->packet_type = FT_PACKET_REQUEST; frame->action_length = host_to_le16(len); @@ -1182,6 +1218,10 @@ rlen = 2 + 2 * ETH_ALEN + 2 + resp_ies_len; frame = os_malloc(sizeof(*frame) + rlen); + if (frame == NULL) { + os_free(resp_ies); + return -1; + } frame->frame_type = RSN_REMOTE_FRAME_TYPE_FT_RRB; frame->packet_type = FT_PACKET_RESPONSE; frame->action_length = host_to_le16(rlen); @@ -1277,6 +1317,7 @@ wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", r.pmk_r1_name, WPA_PMK_NAME_LEN); r.pairwise = host_to_le16(pairwise); + os_memset(r.pad, 0, sizeof(r.pad)); if (aes_wrap(r1kh->key, (FT_R0KH_R1KH_RESP_DATA_LEN + 7) / 8, r.nonce, resp.nonce) < 0) { @@ -1580,6 +1621,7 @@ 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)); if (aes_wrap(r1kh->key, (FT_R0KH_R1KH_PUSH_DATA_LEN + 7) / 8, f.timestamp, frame.timestamp) < 0) return; diff -Nru wpa-1.0/src/ap/wpa_auth_glue.c wpa-2.1/src/ap/wpa_auth_glue.c --- wpa-1.0/src/ap/wpa_auth_glue.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/ap/wpa_auth_glue.c 2014-02-04 11:23:35.000000000 +0000 @@ -1,26 +1,20 @@ /* * hostapd / WPA authenticator glue code - * Copyright (c) 2002-2011, Jouni Malinen + * Copyright (c) 2002-2012, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * 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/sae.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 "drivers/driver.h" #include "hostapd.h" #include "ieee802_1x.h" #include "preauth_auth.h" @@ -29,9 +23,11 @@ #include "ap_drv_ops.h" #include "ap_config.h" #include "wpa_auth.h" +#include "wpa_auth_glue.h" static void hostapd_wpa_auth_conf(struct hostapd_bss_config *conf, + struct hostapd_config *iconf, struct wpa_auth_config *wconf) { os_memset(wconf, 0, sizeof(*wconf)); @@ -75,6 +71,19 @@ wconf->pmk_r1_push = conf->pmk_r1_push; wconf->ft_over_ds = conf->ft_over_ds; #endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_HS20 + wconf->disable_gtk = conf->disable_dgaf; +#endif /* CONFIG_HS20 */ +#ifdef CONFIG_TESTING_OPTIONS + wconf->corrupt_gtk_rekey_mic_probability = + iconf->corrupt_gtk_rekey_mic_probability; +#endif /* CONFIG_TESTING_OPTIONS */ +#ifdef CONFIG_P2P + os_memcpy(wconf->ip_addr_go, conf->ip_addr_go, 4); + os_memcpy(wconf->ip_addr_mask, conf->ip_addr_mask, 4); + 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 */ } @@ -114,10 +123,10 @@ } -static void hostapd_wpa_auth_mic_failure_report(void *ctx, const u8 *addr) +static int hostapd_wpa_auth_mic_failure_report(void *ctx, const u8 *addr) { struct hostapd_data *hapd = ctx; - michael_mic_failure(hapd, addr, 0); + return michael_mic_failure(hapd, addr, 0); } @@ -182,10 +191,38 @@ static const u8 * hostapd_wpa_auth_get_psk(void *ctx, const u8 *addr, + const u8 *p2p_dev_addr, const u8 *prev_psk) { struct hostapd_data *hapd = ctx; - return hostapd_get_psk(hapd->conf, addr, prev_psk); + struct sta_info *sta = ap_get_sta(hapd, addr); + const u8 *psk; + +#ifdef CONFIG_SAE + if (sta && sta->auth_alg == WLAN_AUTH_SAE) { + if (!sta->sae || prev_psk) + return NULL; + return sta->sae->pmk; + } +#endif /* CONFIG_SAE */ + + psk = hostapd_get_psk(hapd->conf, addr, p2p_dev_addr, prev_psk); + /* + * This is about to iterate over all psks, prev_psk gives the last + * returned psk which should not be returned again. + * logic list (all hostapd_get_psk; all sta->psk) + */ + if (sta && sta->psk && !psk) { + struct hostapd_sta_wpa_psk_short *pos; + psk = sta->psk->psk; + for (pos = sta->psk; pos; pos = pos->next) { + if (pos->psk == prev_psk) { + psk = pos->next ? pos->next->psk : NULL; + break; + } + } + } + return psk; } @@ -296,12 +333,13 @@ { struct hostapd_data *hapd = ctx; struct wpa_auth_iface_iter_data data; - if (hapd->iface->for_each_interface == NULL) + if (hapd->iface->interfaces == NULL || + hapd->iface->interfaces->for_each_interface == NULL) return -1; data.cb = cb; data.cb_ctx = cb_ctx; - return hapd->iface->for_each_interface(hapd->iface->interfaces, - wpa_auth_iface_iter, &data); + return hapd->iface->interfaces->for_each_interface( + hapd->iface->interfaces, wpa_auth_iface_iter, &data); } @@ -353,16 +391,17 @@ int ret; #ifdef CONFIG_IEEE80211R - if (proto == ETH_P_RRB && hapd->iface->for_each_interface) { + if (proto == ETH_P_RRB && hapd->iface->interfaces && + hapd->iface->interfaces->for_each_interface) { int res; struct wpa_auth_ft_iface_iter_data idata; idata.src_hapd = hapd; idata.dst = dst; idata.data = data; idata.data_len = data_len; - res = hapd->iface->for_each_interface(hapd->iface->interfaces, - hostapd_wpa_auth_ft_iter, - &idata); + res = hapd->iface->interfaces->for_each_interface( + hapd->iface->interfaces, hostapd_wpa_auth_ft_iter, + &idata); if (res == 1) return data_len; } @@ -415,7 +454,7 @@ os_memcpy(m->bssid, hapd->own_addr, ETH_ALEN); os_memcpy(&m->u, data, data_len); - res = hostapd_drv_send_mlme(hapd, (u8 *) m, mlen); + res = hostapd_drv_send_mlme(hapd, (u8 *) m, mlen, 0); os_free(m); return res; } @@ -427,6 +466,9 @@ struct hostapd_data *hapd = ctx; struct sta_info *sta; + if (hostapd_add_sta_node(hapd, sta_addr, WLAN_AUTH_FT) < 0) + return NULL; + sta = ap_sta_add(hapd, sta_addr); if (sta == NULL) return NULL; @@ -435,7 +477,7 @@ return sta->wpa_sm; } - sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth, sta->addr); + sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth, sta->addr, NULL); if (sta->wpa_sm == NULL) { ap_free_sta(hapd, sta); return NULL; @@ -460,6 +502,14 @@ len - sizeof(*ethhdr)); } + +static int hostapd_wpa_auth_add_tspec(void *ctx, const u8 *sta_addr, + u8 *tspec_ie, size_t tspec_ielen) +{ + struct hostapd_data *hapd = ctx; + return hostapd_add_tspec(hapd, sta_addr, tspec_ie, tspec_ielen); +} + #endif /* CONFIG_IEEE80211R */ @@ -470,9 +520,11 @@ const u8 *wpa_ie; size_t wpa_ie_len; - hostapd_wpa_auth_conf(hapd->conf, &_conf); + hostapd_wpa_auth_conf(hapd->conf, hapd->iconf, &_conf); if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_EAPOL_TX_STATUS) _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; @@ -491,6 +543,7 @@ #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); if (hapd->wpa_auth == NULL) { @@ -541,7 +594,7 @@ void hostapd_reconfig_wpa(struct hostapd_data *hapd) { struct wpa_auth_config wpa_auth_conf; - hostapd_wpa_auth_conf(hapd->conf, &wpa_auth_conf); + hostapd_wpa_auth_conf(hapd->conf, hapd->iconf, &wpa_auth_conf); wpa_reconfig(hapd->wpa_auth, &wpa_auth_conf); } @@ -570,5 +623,6 @@ #ifdef CONFIG_IEEE80211R l2_packet_deinit(hapd->l2); + hapd->l2 = NULL; #endif /* CONFIG_IEEE80211R */ } diff -Nru wpa-1.0/src/ap/wpa_auth_glue.h wpa-2.1/src/ap/wpa_auth_glue.h --- wpa-1.0/src/ap/wpa_auth_glue.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/ap/wpa_auth_glue.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * hostapd / WPA authenticator glue code * Copyright (c) 2002-2009, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef WPA_AUTH_GLUE_H diff -Nru wpa-1.0/src/ap/wpa_auth.h wpa-2.1/src/ap/wpa_auth.h --- wpa-1.0/src/ap/wpa_auth.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/ap/wpa_auth.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * hostapd - IEEE 802.11i-2004 / WPA Authenticator * Copyright (c) 2004-2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef WPA_AUTH_H @@ -164,6 +158,17 @@ int pmk_r1_push; int ft_over_ds; #endif /* CONFIG_IEEE80211R */ + int disable_gtk; + int ap_mlme; +#ifdef CONFIG_TESTING_OPTIONS + double corrupt_gtk_rekey_mic_probability; +#endif /* CONFIG_TESTING_OPTIONS */ +#ifdef CONFIG_P2P + u8 ip_addr_go[4]; + u8 ip_addr_mask[4]; + u8 ip_addr_start[4]; + u8 ip_addr_end[4]; +#endif /* CONFIG_P2P */ }; typedef enum { @@ -181,11 +186,12 @@ void (*logger)(void *ctx, const u8 *addr, logger_level level, const char *txt); void (*disconnect)(void *ctx, const u8 *addr, u16 reason); - void (*mic_failure_report)(void *ctx, const u8 *addr); + int (*mic_failure_report)(void *ctx, const u8 *addr); void (*set_eapol)(void *ctx, const u8 *addr, wpa_eapol_variable var, int value); int (*get_eapol)(void *ctx, const u8 *addr, wpa_eapol_variable var); - const u8 * (*get_psk)(void *ctx, const u8 *addr, const u8 *prev_psk); + const u8 * (*get_psk)(void *ctx, const u8 *addr, const u8 *p2p_dev_addr, + const u8 *prev_psk); int (*get_msk)(void *ctx, const u8 *addr, u8 *msk, size_t *len); int (*set_key)(void *ctx, int vlan_id, enum wpa_alg alg, const u8 *addr, int idx, u8 *key, size_t key_len); @@ -202,6 +208,8 @@ struct wpa_state_machine * (*add_sta)(void *ctx, const u8 *sta_addr); int (*send_ft_action)(void *ctx, const u8 *dst, const u8 *data, size_t data_len); + int (*add_tspec)(void *ctx, const u8 *sta_addr, u8 *tspec_ie, + size_t tspec_ielen); #endif /* CONFIG_IEEE80211R */ }; @@ -226,7 +234,8 @@ const u8 *mdie, size_t mdie_len); int wpa_auth_uses_mfp(struct wpa_state_machine *sm); struct wpa_state_machine * -wpa_auth_sta_init(struct wpa_authenticator *wpa_auth, const u8 *addr); +wpa_auth_sta_init(struct wpa_authenticator *wpa_auth, const u8 *addr, + const u8 *p2p_dev_addr); int wpa_auth_sta_associated(struct wpa_authenticator *wpa_auth, struct wpa_state_machine *sm); void wpa_auth_sta_no_wpa(struct wpa_state_machine *sm); @@ -262,6 +271,8 @@ const u8 *pmk, size_t len, const u8 *sta_addr, int session_timeout, struct eapol_state_machine *eapol); +void wpa_auth_pmksa_remove(struct wpa_authenticator *wpa_auth, + const u8 *sta_addr); int wpa_auth_sta_set_vlan(struct wpa_state_machine *sm, int vlan_id); void wpa_auth_eapol_key_tx_status(struct wpa_authenticator *wpa_auth, struct wpa_state_machine *sm, int ack); @@ -284,4 +295,14 @@ void wpa_ft_push_pmk_r1(struct wpa_authenticator *wpa_auth, const u8 *addr); #endif /* CONFIG_IEEE80211R */ +void wpa_wnmsleep_rekey_gtk(struct wpa_state_machine *sm); +void wpa_set_wnmsleep(struct wpa_state_machine *sm, int flag); +int wpa_wnmsleep_gtk_subelem(struct wpa_state_machine *sm, u8 *pos); +int wpa_wnmsleep_igtk_subelem(struct wpa_state_machine *sm, u8 *pos); + +int wpa_auth_uses_sae(struct wpa_state_machine *sm); +int wpa_auth_uses_ft_sae(struct wpa_state_machine *sm); + +int wpa_auth_get_ip_addr(struct wpa_state_machine *sm, u8 *addr); + #endif /* WPA_AUTH_H */ diff -Nru wpa-1.0/src/ap/wpa_auth_ie.c wpa-2.1/src/ap/wpa_auth_ie.c --- wpa-1.0/src/ap/wpa_auth_ie.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/ap/wpa_auth_ie.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * hostapd - WPA/RSN IE and KDE definitions * Copyright (c) 2004-2008, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "utils/includes.h" @@ -35,6 +29,7 @@ struct wpa_ie_hdr *hdr; int num_suites; u8 *pos, *count; + u32 suite; hdr = (struct wpa_ie_hdr *) buf; hdr->elem_id = WLAN_EID_VENDOR_SPECIFIC; @@ -42,46 +37,25 @@ WPA_PUT_LE16(hdr->version, WPA_VERSION); pos = (u8 *) (hdr + 1); - if (conf->wpa_group == WPA_CIPHER_CCMP) { - RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_CCMP); - } else if (conf->wpa_group == WPA_CIPHER_TKIP) { - RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_TKIP); - } else if (conf->wpa_group == WPA_CIPHER_WEP104) { - RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_WEP104); - } else if (conf->wpa_group == WPA_CIPHER_WEP40) { - RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_WEP40); - } else { + suite = wpa_cipher_to_suite(WPA_PROTO_WPA, conf->wpa_group); + if (suite == 0) { wpa_printf(MSG_DEBUG, "Invalid group cipher (%d).", conf->wpa_group); return -1; } + RSN_SELECTOR_PUT(pos, suite); pos += WPA_SELECTOR_LEN; - num_suites = 0; count = pos; pos += 2; - if (conf->wpa_pairwise & WPA_CIPHER_CCMP) { - RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_CCMP); - pos += WPA_SELECTOR_LEN; - num_suites++; - } - if (conf->wpa_pairwise & WPA_CIPHER_TKIP) { - RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_TKIP); - pos += WPA_SELECTOR_LEN; - num_suites++; - } - if (conf->wpa_pairwise & WPA_CIPHER_NONE) { - RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_NONE); - pos += WPA_SELECTOR_LEN; - num_suites++; - } - + num_suites = wpa_cipher_put_suites(pos, conf->wpa_pairwise); if (num_suites == 0) { wpa_printf(MSG_DEBUG, "Invalid pairwise cipher (%d).", conf->wpa_pairwise); return -1; } + pos += num_suites * WPA_SELECTOR_LEN; WPA_PUT_LE16(count, num_suites); num_suites = 0; @@ -118,28 +92,23 @@ const u8 *pmkid) { struct rsn_ie_hdr *hdr; - int num_suites; + int num_suites, res; u8 *pos, *count; u16 capab; + u32 suite; hdr = (struct rsn_ie_hdr *) buf; hdr->elem_id = WLAN_EID_RSN; WPA_PUT_LE16(hdr->version, RSN_VERSION); pos = (u8 *) (hdr + 1); - if (conf->wpa_group == WPA_CIPHER_CCMP) { - RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP); - } else if (conf->wpa_group == WPA_CIPHER_TKIP) { - RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_TKIP); - } else if (conf->wpa_group == WPA_CIPHER_WEP104) { - RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_WEP104); - } else if (conf->wpa_group == WPA_CIPHER_WEP40) { - RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_WEP40); - } else { + suite = wpa_cipher_to_suite(WPA_PROTO_RSN, conf->wpa_group); + if (suite == 0) { wpa_printf(MSG_DEBUG, "Invalid group cipher (%d).", conf->wpa_group); return -1; } + RSN_SELECTOR_PUT(pos, suite); pos += RSN_SELECTOR_LEN; num_suites = 0; @@ -154,21 +123,9 @@ } #endif /* CONFIG_RSN_TESTING */ - if (conf->rsn_pairwise & WPA_CIPHER_CCMP) { - RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP); - pos += RSN_SELECTOR_LEN; - num_suites++; - } - if (conf->rsn_pairwise & WPA_CIPHER_TKIP) { - RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_TKIP); - pos += RSN_SELECTOR_LEN; - num_suites++; - } - if (conf->rsn_pairwise & WPA_CIPHER_NONE) { - RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_NONE); - pos += RSN_SELECTOR_LEN; - num_suites++; - } + res = rsn_cipher_put_suites(pos, conf->rsn_pairwise); + num_suites += res; + pos += res * RSN_SELECTOR_LEN; #ifdef CONFIG_RSN_TESTING if (rsn_testing) { @@ -231,6 +188,18 @@ num_suites++; } #endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_SAE + if (conf->wpa_key_mgmt & WPA_KEY_MGMT_SAE) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_SAE); + pos += RSN_SELECTOR_LEN; + num_suites++; + } + if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_SAE) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_SAE); + pos += RSN_SELECTOR_LEN; + num_suites++; + } +#endif /* CONFIG_SAE */ #ifdef CONFIG_RSN_TESTING if (rsn_testing) { @@ -341,8 +310,7 @@ pos += res; } #ifdef CONFIG_IEEE80211R - if (wpa_auth->conf.wpa_key_mgmt & - (WPA_KEY_MGMT_FT_IEEE8021X | WPA_KEY_MGMT_FT_PSK)) { + if (wpa_key_mgmt_ft(wpa_auth->conf.wpa_key_mgmt)) { res = wpa_write_mdie(&wpa_auth->conf, pos, buf + sizeof(buf) - pos); if (res < 0) @@ -451,36 +419,28 @@ else if (data.key_mgmt & WPA_KEY_MGMT_PSK_SHA256) selector = RSN_AUTH_KEY_MGMT_PSK_SHA256; #endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_SAE + else if (data.key_mgmt & WPA_KEY_MGMT_SAE) + selector = RSN_AUTH_KEY_MGMT_SAE; + else if (data.key_mgmt & WPA_KEY_MGMT_FT_SAE) + selector = RSN_AUTH_KEY_MGMT_FT_SAE; +#endif /* CONFIG_SAE */ else if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X) 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; wpa_auth->dot11RSNAAuthenticationSuiteSelected = selector; - selector = RSN_CIPHER_SUITE_CCMP; - if (data.pairwise_cipher & WPA_CIPHER_CCMP) + selector = wpa_cipher_to_suite(WPA_PROTO_RSN, + data.pairwise_cipher); + if (!selector) selector = RSN_CIPHER_SUITE_CCMP; - else if (data.pairwise_cipher & WPA_CIPHER_TKIP) - selector = RSN_CIPHER_SUITE_TKIP; - else if (data.pairwise_cipher & WPA_CIPHER_WEP104) - selector = RSN_CIPHER_SUITE_WEP104; - else if (data.pairwise_cipher & WPA_CIPHER_WEP40) - selector = RSN_CIPHER_SUITE_WEP40; - else if (data.pairwise_cipher & WPA_CIPHER_NONE) - selector = RSN_CIPHER_SUITE_NONE; wpa_auth->dot11RSNAPairwiseCipherSelected = selector; - selector = RSN_CIPHER_SUITE_CCMP; - if (data.group_cipher & WPA_CIPHER_CCMP) + selector = wpa_cipher_to_suite(WPA_PROTO_RSN, + data.group_cipher); + if (!selector) selector = RSN_CIPHER_SUITE_CCMP; - else if (data.group_cipher & WPA_CIPHER_TKIP) - selector = RSN_CIPHER_SUITE_TKIP; - else if (data.group_cipher & WPA_CIPHER_WEP104) - selector = RSN_CIPHER_SUITE_WEP104; - else if (data.group_cipher & WPA_CIPHER_WEP40) - selector = RSN_CIPHER_SUITE_WEP40; - else if (data.group_cipher & WPA_CIPHER_NONE) - selector = RSN_CIPHER_SUITE_NONE; wpa_auth->dot11RSNAGroupCipherSelected = selector; } else { res = wpa_parse_wpa_ie_wpa(wpa_ie, wpa_ie_len, &data); @@ -492,30 +452,16 @@ selector = WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X; wpa_auth->dot11RSNAAuthenticationSuiteSelected = selector; - selector = WPA_CIPHER_SUITE_TKIP; - if (data.pairwise_cipher & WPA_CIPHER_CCMP) - selector = WPA_CIPHER_SUITE_CCMP; - else if (data.pairwise_cipher & WPA_CIPHER_TKIP) - selector = WPA_CIPHER_SUITE_TKIP; - else if (data.pairwise_cipher & WPA_CIPHER_WEP104) - selector = WPA_CIPHER_SUITE_WEP104; - else if (data.pairwise_cipher & WPA_CIPHER_WEP40) - selector = WPA_CIPHER_SUITE_WEP40; - else if (data.pairwise_cipher & WPA_CIPHER_NONE) - selector = WPA_CIPHER_SUITE_NONE; + selector = wpa_cipher_to_suite(WPA_PROTO_WPA, + data.pairwise_cipher); + if (!selector) + selector = RSN_CIPHER_SUITE_TKIP; wpa_auth->dot11RSNAPairwiseCipherSelected = selector; - selector = WPA_CIPHER_SUITE_TKIP; - if (data.group_cipher & WPA_CIPHER_CCMP) - selector = WPA_CIPHER_SUITE_CCMP; - else if (data.group_cipher & WPA_CIPHER_TKIP) + selector = wpa_cipher_to_suite(WPA_PROTO_WPA, + data.group_cipher); + if (!selector) selector = WPA_CIPHER_SUITE_TKIP; - else if (data.group_cipher & WPA_CIPHER_WEP104) - selector = WPA_CIPHER_SUITE_WEP104; - else if (data.group_cipher & WPA_CIPHER_WEP40) - selector = WPA_CIPHER_SUITE_WEP40; - else if (data.group_cipher & WPA_CIPHER_NONE) - selector = WPA_CIPHER_SUITE_NONE; wpa_auth->dot11RSNAGroupCipherSelected = selector; } if (res) { @@ -551,6 +497,12 @@ else if (key_mgmt & WPA_KEY_MGMT_PSK_SHA256) sm->wpa_key_mgmt = WPA_KEY_MGMT_PSK_SHA256; #endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_SAE + else if (key_mgmt & WPA_KEY_MGMT_SAE) + sm->wpa_key_mgmt = WPA_KEY_MGMT_SAE; + else if (key_mgmt & WPA_KEY_MGMT_FT_SAE) + sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_SAE; +#endif /* CONFIG_SAE */ else if (key_mgmt & WPA_KEY_MGMT_IEEE8021X) sm->wpa_key_mgmt = WPA_KEY_MGMT_IEEE8021X; else @@ -612,10 +564,9 @@ } #endif /* CONFIG_IEEE80211R */ - if (ciphers & WPA_CIPHER_CCMP) - sm->pairwise = WPA_CIPHER_CCMP; - else - sm->pairwise = WPA_CIPHER_TKIP; + sm->pairwise = wpa_pick_pairwise_cipher(ciphers, 0); + if (sm->pairwise < 0) + return WPA_INVALID_PAIRWISE; /* TODO: clear WPA/WPA2 state if STA changes from one to another */ if (wpa_ie[0] == WLAN_EID_RSN) @@ -757,6 +708,25 @@ } #endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_P2P + if (pos[1] >= RSN_SELECTOR_LEN + 1 && + RSN_SELECTOR_GET(pos + 2) == WFA_KEY_DATA_IP_ADDR_REQ) { + ie->ip_addr_req = pos + 2 + RSN_SELECTOR_LEN; + wpa_hexdump(MSG_DEBUG, "WPA: IP Address Request in EAPOL-Key", + ie->ip_addr_req, pos[1] - RSN_SELECTOR_LEN); + return 0; + } + + if (pos[1] >= RSN_SELECTOR_LEN + 3 * 4 && + RSN_SELECTOR_GET(pos + 2) == WFA_KEY_DATA_IP_ADDR_ALLOC) { + ie->ip_addr_alloc = pos + 2 + RSN_SELECTOR_LEN; + wpa_hexdump(MSG_DEBUG, + "WPA: IP Address Allocation in EAPOL-Key", + ie->ip_addr_alloc, pos[1] - RSN_SELECTOR_LEN); + return 0; + } +#endif /* CONFIG_P2P */ + return 0; } diff -Nru wpa-1.0/src/ap/wpa_auth_ie.h wpa-2.1/src/ap/wpa_auth_ie.h --- wpa-1.0/src/ap/wpa_auth_ie.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/ap/wpa_auth_ie.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * hostapd - WPA/RSN IE and KDE definitions * Copyright (c) 2004-2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef WPA_AUTH_IE_H @@ -45,6 +39,10 @@ const u8 *ftie; size_t ftie_len; #endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_P2P + const u8 *ip_addr_req; + const u8 *ip_addr_alloc; +#endif /* CONFIG_P2P */ }; int wpa_parse_kde_ies(const u8 *buf, size_t len, diff -Nru wpa-1.0/src/ap/wpa_auth_i.h wpa-2.1/src/ap/wpa_auth_i.h --- wpa-1.0/src/ap/wpa_auth_i.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/ap/wpa_auth_i.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * hostapd - IEEE 802.11i-2004 / WPA Authenticator: Internal definitions * Copyright (c) 2004-2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef WPA_AUTH_I_H @@ -32,6 +26,7 @@ struct wpa_group *group; u8 addr[ETH_ALEN]; + u8 p2p_dev_addr[ETH_ALEN]; enum { WPA_PTK_INITIALIZE, WPA_PTK_DISCONNECT, WPA_PTK_DISCONNECTED, @@ -69,10 +64,11 @@ Boolean pairwise_set; int keycount; Boolean Pair; - struct { + struct wpa_key_replay_counter { u8 counter[WPA_REPLAY_COUNTER_LEN]; Boolean valid; - } key_replay[RSNA_MAX_EAPOL_RETRIES]; + } key_replay[RSNA_MAX_EAPOL_RETRIES], + prev_key_replay[RSNA_MAX_EAPOL_RETRIES]; Boolean PInitAKeys; /* WPA only, not in IEEE 802.11i */ Boolean PTKRequest; /* not in IEEE 802.11i state machine */ Boolean has_GTK; @@ -87,10 +83,12 @@ unsigned int started:1; unsigned int mgmt_frame_prot:1; unsigned int rx_eapol_key_secure:1; + unsigned int update_snonce:1; #ifdef CONFIG_IEEE80211R unsigned int ft_completed:1; unsigned int pmk_r1_name_valid:1; #endif /* CONFIG_IEEE80211R */ + unsigned int is_wnmsleep:1; u8 req_replay_counter[WPA_REPLAY_COUNTER_LEN]; int req_replay_counter_used; @@ -123,6 +121,10 @@ #endif /* CONFIG_IEEE80211R */ int pending_1_of_4_timeout; + +#ifdef CONFIG_P2P + u8 ip_addr[4]; +#endif /* CONFIG_P2P */ }; @@ -141,7 +143,8 @@ enum { WPA_GROUP_GTK_INIT = 0, - WPA_GROUP_SETKEYS, WPA_GROUP_SETKEYSDONE + WPA_GROUP_SETKEYS, WPA_GROUP_SETKEYSDONE, + WPA_GROUP_FATAL_FAILURE } wpa_group_state; u8 GMK[WPA_GMK_LEN]; @@ -186,6 +189,10 @@ struct rsn_pmksa_cache *pmksa; struct wpa_ft_pmk_cache *ft_pmk_cache; + +#ifdef CONFIG_P2P + struct bitfield *ip_pool; +#endif /* CONFIG_P2P */ }; diff -Nru wpa-1.0/src/ap/wps_hostapd.c wpa-2.1/src/ap/wps_hostapd.c --- wpa-1.0/src/ap/wps_hostapd.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/ap/wps_hostapd.c 2014-02-04 11:23:35.000000000 +0000 @@ -1,15 +1,9 @@ /* * hostapd / WPS integration - * Copyright (c) 2008-2010, Jouni Malinen + * Copyright (c) 2008-2012, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "utils/includes.h" @@ -17,7 +11,6 @@ #include "utils/common.h" #include "utils/eloop.h" #include "utils/uuid.h" -#include "crypto/dh_groups.h" #include "common/wpa_ctrl.h" #include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" @@ -26,6 +19,7 @@ #include "wps/wps.h" #include "wps/wps_defs.h" #include "wps/wps_dev_attr.h" +#include "wps/wps_attr_parse.h" #include "hostapd.h" #include "ap_config.h" #include "ap_drv_ops.h" @@ -43,13 +37,15 @@ static int hostapd_wps_probe_req_rx(void *ctx, const u8 *addr, const u8 *da, const u8 *bssid, - const u8 *ie, size_t ie_len); + const u8 *ie, size_t ie_len, + int ssi_signal); static void hostapd_wps_ap_pin_timeout(void *eloop_data, void *user_ctx); struct wps_for_each_data { int (*func)(struct hostapd_data *h, void *ctx); void *ctx; + struct hostapd_data *calling_hapd; }; @@ -62,7 +58,14 @@ return 0; for (j = 0; j < iface->num_bss; j++) { struct hostapd_data *hapd = iface->bss[j]; - int ret = data->func(hapd, data->ctx); + int ret; + + if (hapd != data->calling_hapd && + (hapd->conf->wps_independent || + data->calling_hapd->conf->wps_independent)) + continue; + + ret = data->func(hapd, data->ctx); if (ret) return ret; } @@ -79,22 +82,33 @@ struct wps_for_each_data data; data.func = func; data.ctx = ctx; - if (iface->for_each_interface == NULL) + data.calling_hapd = hapd; + if (iface->interfaces == NULL || + iface->interfaces->for_each_interface == NULL) return wps_for_each(iface, &data); - return iface->for_each_interface(iface->interfaces, wps_for_each, - &data); + return iface->interfaces->for_each_interface(iface->interfaces, + wps_for_each, &data); } -static int hostapd_wps_new_psk_cb(void *ctx, const u8 *mac_addr, const u8 *psk, +static int hostapd_wps_new_psk_cb(void *ctx, const u8 *mac_addr, + const u8 *p2p_dev_addr, const u8 *psk, size_t psk_len) { struct hostapd_data *hapd = ctx; struct hostapd_wpa_psk *p; struct hostapd_ssid *ssid = &hapd->conf->ssid; - wpa_printf(MSG_DEBUG, "Received new WPA/WPA2-PSK from WPS for STA " - MACSTR, MAC2STR(mac_addr)); + if (is_zero_ether_addr(p2p_dev_addr)) { + wpa_printf(MSG_DEBUG, + "Received new WPA/WPA2-PSK from WPS for STA " MACSTR, + MAC2STR(mac_addr)); + } else { + wpa_printf(MSG_DEBUG, + "Received new WPA/WPA2-PSK from WPS for STA " MACSTR + " P2P Device Addr " MACSTR, + MAC2STR(mac_addr), MAC2STR(p2p_dev_addr)); + } wpa_hexdump_key(MSG_DEBUG, "Per-device PSK", psk, psk_len); if (psk_len != PMK_LEN) { @@ -108,8 +122,14 @@ if (p == NULL) return -1; os_memcpy(p->addr, mac_addr, ETH_ALEN); + os_memcpy(p->p2p_dev_addr, p2p_dev_addr, ETH_ALEN); os_memcpy(p->psk, psk, PMK_LEN); + if (hapd->new_psk_cb) { + hapd->new_psk_cb(hapd->new_psk_cb_ctx, mac_addr, p2p_dev_addr, + psk, psk_len); + } + p->next = ssid->wpa_psk; ssid->wpa_psk = p; @@ -189,19 +209,23 @@ struct wps_stop_reg_data { struct hostapd_data *current_hapd; const u8 *uuid_e; + const u8 *dev_pw; + size_t dev_pw_len; }; static int wps_stop_registrar(struct hostapd_data *hapd, void *ctx) { struct wps_stop_reg_data *data = ctx; if (hapd != data->current_hapd && hapd->wps != NULL) - wps_registrar_complete(hapd->wps->registrar, data->uuid_e); + wps_registrar_complete(hapd->wps->registrar, data->uuid_e, + data->dev_pw, data->dev_pw_len); return 0; } static void hostapd_wps_reg_success_cb(void *ctx, const u8 *mac_addr, - const u8 *uuid_e) + const u8 *uuid_e, const u8 *dev_pw, + size_t dev_pw_len) { struct hostapd_data *hapd = ctx; char uuid[40]; @@ -215,6 +239,8 @@ mac_addr, uuid_e); data.current_hapd = hapd; data.uuid_e = uuid_e; + data.dev_pw = dev_pw; + data.dev_pw_len = dev_pw_len; hostapd_wps_for_each(hapd, wps_stop_registrar, &data); } @@ -253,13 +279,28 @@ struct hostapd_iface *iface = eloop_data; wpa_printf(MSG_DEBUG, "WPS: Reload configuration data"); - if (iface->reload_config(iface) < 0) { + if (iface->interfaces == NULL || + iface->interfaces->reload_config(iface) < 0) { wpa_printf(MSG_WARNING, "WPS: Failed to reload the updated " "configuration"); } } +void hostapd_wps_eap_completed(struct hostapd_data *hapd) +{ + /* + * Reduce race condition of the station trying to reconnect immediately + * after AP reconfiguration through WPS by rescheduling the reload + * timeout to happen after EAP completion rather than the originally + * scheduled 100 ms after new configuration became known. + */ + if (eloop_deplete_timeout(0, 0, wps_reload_config, hapd->iface, NULL) == + 1) + wpa_printf(MSG_DEBUG, "WPS: Reschedule immediate configuration reload"); +} + + static void hapd_new_ap_event(struct hostapd_data *hapd, const u8 *attr, size_t attr_len) { @@ -274,6 +315,114 @@ } +static int hapd_wps_reconfig_in_memory(struct hostapd_data *hapd, + const struct wps_credential *cred) +{ + struct hostapd_bss_config *bss = hapd->conf; + + wpa_printf(MSG_DEBUG, "WPS: Updating in-memory configuration"); + + bss->wps_state = 2; + if (cred->ssid_len <= HOSTAPD_MAX_SSID_LEN) { + os_memcpy(bss->ssid.ssid, cred->ssid, cred->ssid_len); + bss->ssid.ssid_len = cred->ssid_len; + bss->ssid.ssid_set = 1; + } + + if ((cred->auth_type & (WPS_AUTH_WPA2 | WPS_AUTH_WPA2PSK)) && + (cred->auth_type & (WPS_AUTH_WPA | WPS_AUTH_WPAPSK))) + bss->wpa = 3; + else if (cred->auth_type & (WPS_AUTH_WPA2 | WPS_AUTH_WPA2PSK)) + bss->wpa = 2; + else if (cred->auth_type & (WPS_AUTH_WPA | WPS_AUTH_WPAPSK)) + bss->wpa = 1; + else + bss->wpa = 0; + + if (bss->wpa) { + if (cred->auth_type & (WPS_AUTH_WPA2 | WPS_AUTH_WPA)) + bss->wpa_key_mgmt = WPA_KEY_MGMT_IEEE8021X; + if (cred->auth_type & (WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK)) + bss->wpa_key_mgmt = WPA_KEY_MGMT_PSK; + + bss->wpa_pairwise = 0; + if (cred->encr_type & WPS_ENCR_AES) + bss->wpa_pairwise |= WPA_CIPHER_CCMP; + if (cred->encr_type & WPS_ENCR_TKIP) + bss->wpa_pairwise |= WPA_CIPHER_TKIP; + bss->rsn_pairwise = bss->wpa_pairwise; + bss->wpa_group = wpa_select_ap_group_cipher(bss->wpa, + bss->wpa_pairwise, + bss->rsn_pairwise); + + 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); + if (bss->ssid.wpa_passphrase) + os_memcpy(bss->ssid.wpa_passphrase, cred->key, + cred->key_len); + os_free(bss->ssid.wpa_psk); + bss->ssid.wpa_psk = NULL; + } else if (cred->key_len == 64) { + os_free(bss->ssid.wpa_psk); + bss->ssid.wpa_psk = + os_zalloc(sizeof(struct hostapd_wpa_psk)); + if (bss->ssid.wpa_psk && + hexstr2bin((const char *) cred->key, + bss->ssid.wpa_psk->psk, PMK_LEN) == 0) { + bss->ssid.wpa_psk->group = 1; + os_free(bss->ssid.wpa_passphrase); + bss->ssid.wpa_passphrase = NULL; + } + } + bss->auth_algs = 1; + } else { + if ((cred->auth_type & WPS_AUTH_OPEN) && + (cred->auth_type & WPS_AUTH_SHARED)) + bss->auth_algs = 3; + else if (cred->auth_type & WPS_AUTH_SHARED) + bss->auth_algs = 2; + else + bss->auth_algs = 1; + if (cred->encr_type & WPS_ENCR_WEP && cred->key_idx > 0 && + cred->key_idx <= 4) { + struct hostapd_wep_keys *wep = &bss->ssid.wep; + int idx = cred->key_idx; + if (idx) + idx--; + wep->idx = idx; + if (cred->key_len == 10 || cred->key_len == 26) { + os_free(wep->key[idx]); + wep->key[idx] = os_malloc(cred->key_len / 2); + if (wep->key[idx] == NULL || + hexstr2bin((const char *) cred->key, + wep->key[idx], + cred->key_len / 2)) + return -1; + wep->len[idx] = cred->key_len / 2; + } else { + os_free(wep->key[idx]); + wep->key[idx] = os_malloc(cred->key_len); + if (wep->key[idx] == NULL) + return -1; + os_memcpy(wep->key[idx], cred->key, + cred->key_len); + wep->len[idx] = cred->key_len; + } + wep->keys_set = 1; + } + } + + /* Schedule configuration reload after short period of time to allow + * EAP-WSC to be finished. + */ + eloop_register_timeout(0, 100000, wps_reload_config, hapd->iface, + NULL); + + return 0; +} + + static int hapd_wps_cred_cb(struct hostapd_data *hapd, void *ctx) { const struct wps_credential *cred = ctx; @@ -340,6 +489,8 @@ } hapd->wps->wps_state = WPS_STATE_CONFIGURED; + if (hapd->iface->config_fname == NULL) + return hapd_wps_reconfig_in_memory(hapd, cred); len = os_strlen(hapd->iface->config_fname) + 5; tmp_fname = os_malloc(len); if (tmp_fname == NULL) @@ -367,10 +518,17 @@ fprintf(nconf, "wps_state=2\n"); - fprintf(nconf, "ssid="); - for (i = 0; i < cred->ssid_len; i++) - fputc(cred->ssid[i], nconf); - fprintf(nconf, "\n"); + if (is_hex(cred->ssid, cred->ssid_len)) { + fprintf(nconf, "ssid2="); + for (i = 0; i < cred->ssid_len; i++) + fprintf(nconf, "%02x", cred->ssid[i]); + fprintf(nconf, "\n"); + } else { + fprintf(nconf, "ssid="); + for (i = 0; i < cred->ssid_len; i++) + fputc(cred->ssid[i], nconf); + fprintf(nconf, "\n"); + } if ((cred->auth_type & (WPS_AUTH_WPA2 | WPS_AUTH_WPA2PSK)) && (cred->auth_type & (WPS_AUTH_WPA | WPS_AUTH_WPAPSK))) @@ -460,6 +618,7 @@ multi_bss = 1; if (!multi_bss && (str_starts(buf, "ssid=") || + str_starts(buf, "ssid2=") || str_starts(buf, "auth_algs=") || str_starts(buf, "wep_default_key=") || str_starts(buf, "wep_key") || @@ -577,6 +736,12 @@ static void hostapd_pwd_auth_fail(struct hostapd_data *hapd, struct wps_event_pwd_auth_fail *data) { + /* Update WPS Status - Authentication Failure */ + wpa_printf(MSG_DEBUG, "WPS: Authentication failure update"); + hapd->wps_stats.status = WPS_STATUS_FAILURE; + hapd->wps_stats.failure_reason = WPS_EI_AUTH_FAILURE; + os_memcpy(hapd->wps_stats.peer_addr, data->peer_macaddr, ETH_ALEN); + hostapd_wps_for_each(hapd, wps_pwd_auth_fail, data); } @@ -604,21 +769,59 @@ } -static const char * wps_event_fail_reason[NUM_WPS_EI_VALUES] = { - "No Error", /* WPS_EI_NO_ERROR */ - "TKIP Only Prohibited", /* WPS_EI_SECURITY_TKIP_ONLY_PROHIBITED */ - "WEP Prohibited" /* WPS_EI_SECURITY_WEP_PROHIBITED */ -}; +static void hostapd_wps_event_pbc_overlap(struct hostapd_data *hapd) +{ + /* Update WPS Status - PBC Overlap */ + hapd->wps_stats.pbc_status = WPS_PBC_STATUS_OVERLAP; +} + + +static void hostapd_wps_event_pbc_timeout(struct hostapd_data *hapd) +{ + /* Update WPS PBC Status:PBC Timeout */ + hapd->wps_stats.pbc_status = WPS_PBC_STATUS_TIMEOUT; +} + + +static void hostapd_wps_event_pbc_active(struct hostapd_data *hapd) +{ + /* Update WPS PBC status - Active */ + hapd->wps_stats.pbc_status = WPS_PBC_STATUS_ACTIVE; +} + + +static void hostapd_wps_event_pbc_disable(struct hostapd_data *hapd) +{ + /* Update WPS PBC status - Active */ + hapd->wps_stats.pbc_status = WPS_PBC_STATUS_DISABLE; +} + + +static void hostapd_wps_event_success(struct hostapd_data *hapd, + struct wps_event_success *success) +{ + /* Update WPS status - Success */ + hapd->wps_stats.pbc_status = WPS_PBC_STATUS_DISABLE; + hapd->wps_stats.status = WPS_STATUS_SUCCESS; + os_memcpy(hapd->wps_stats.peer_addr, success->peer_macaddr, ETH_ALEN); +} + static void hostapd_wps_event_fail(struct hostapd_data *hapd, struct wps_event_fail *fail) { + /* Update WPS status - Failure */ + hapd->wps_stats.status = WPS_STATUS_FAILURE; + os_memcpy(hapd->wps_stats.peer_addr, fail->peer_macaddr, ETH_ALEN); + + hapd->wps_stats.failure_reason = fail->error_indication; + if (fail->error_indication > 0 && fail->error_indication < NUM_WPS_EI_VALUES) { wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_FAIL "msg=%d config_error=%d reason=%d (%s)", fail->msg, fail->config_error, fail->error_indication, - wps_event_fail_reason[fail->error_indication]); + wps_ei_str(fail->error_indication)); } else { wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_FAIL "msg=%d config_error=%d", @@ -640,17 +843,28 @@ hostapd_wps_event_fail(hapd, &data->fail); break; case WPS_EV_SUCCESS: + hostapd_wps_event_success(hapd, &data->success); wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_SUCCESS); break; case WPS_EV_PWD_AUTH_FAIL: hostapd_pwd_auth_fail(hapd, &data->pwd_auth_fail); break; case WPS_EV_PBC_OVERLAP: + hostapd_wps_event_pbc_overlap(hapd); wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_OVERLAP); break; case WPS_EV_PBC_TIMEOUT: + hostapd_wps_event_pbc_timeout(hapd); wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_TIMEOUT); break; + case WPS_EV_PBC_ACTIVE: + hostapd_wps_event_pbc_active(hapd); + wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_ACTIVE); + break; + case WPS_EV_PBC_DISABLE: + hostapd_wps_event_pbc_disable(hapd); + wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_DISABLE); + break; case WPS_EV_ER_AP_ADD: break; case WPS_EV_ER_AP_REMOVE: @@ -672,6 +886,15 @@ } +static int hostapd_wps_rf_band_cb(void *ctx) +{ + struct hostapd_data *hapd = ctx; + + return hapd->iconf->hw_mode == HOSTAPD_MODE_IEEE80211A ? + WPS_RF_50GHZ : WPS_RF_24GHZ; /* FIX: dualband AP */ +} + + static void hostapd_wps_clear_ies(struct hostapd_data *hapd) { wpabuf_free(hapd->wps_beacon_ie); @@ -693,7 +916,8 @@ return 0; for (j = 0; j < iface->num_bss; j++) { struct hostapd_data *hapd = iface->bss[j]; - if (hapd->wps && !is_nil_uuid(hapd->wps->uuid)) { + if (hapd->wps && !hapd->conf->wps_independent && + !is_nil_uuid(hapd->wps->uuid)) { *uuid = hapd->wps->uuid; return 1; } @@ -706,10 +930,12 @@ static const u8 * get_own_uuid(struct hostapd_iface *iface) { const u8 *uuid; - if (iface->for_each_interface == NULL) + if (iface->interfaces == NULL || + iface->interfaces->for_each_interface == NULL) return NULL; uuid = NULL; - iface->for_each_interface(iface->interfaces, get_uuid_cb, &uuid); + iface->interfaces->for_each_interface(iface->interfaces, get_uuid_cb, + &uuid); return uuid; } @@ -725,10 +951,11 @@ static int interface_count(struct hostapd_iface *iface) { int count = 0; - if (iface->for_each_interface == NULL) + if (iface->interfaces == NULL || + iface->interfaces->for_each_interface == NULL) return 0; - iface->for_each_interface(iface->interfaces, count_interface_cb, - &count); + iface->interfaces->for_each_interface(iface->interfaces, + count_interface_cb, &count); return count; } @@ -775,6 +1002,7 @@ wps->cred_cb = hostapd_wps_cred_cb; wps->event_cb = hostapd_wps_event_cb; + wps->rf_band_cb = hostapd_wps_rf_band_cb; wps->cb_ctx = hapd; os_memset(&cfg, 0, sizeof(cfg)); @@ -783,7 +1011,7 @@ if (is_nil_uuid(hapd->conf->uuid)) { const u8 *uuid; uuid = get_own_uuid(hapd->iface); - if (uuid) { + if (uuid && !conf->wps_independent) { os_memcpy(wps->uuid, uuid, UUID_LEN); wpa_hexdump(MSG_DEBUG, "WPS: Clone UUID from another " "interface", wps->uuid, UUID_LEN); @@ -838,8 +1066,14 @@ } wps->dev.os_version = WPA_GET_BE32(hapd->conf->os_version); - wps->dev.rf_bands = hapd->iconf->hw_mode == HOSTAPD_MODE_IEEE80211A ? - WPS_RF_50GHZ : WPS_RF_24GHZ; /* FIX: dualband AP */ + + if (conf->wps_rf_bands) { + wps->dev.rf_bands = conf->wps_rf_bands; + } else { + wps->dev.rf_bands = + hapd->iconf->hw_mode == HOSTAPD_MODE_IEEE80211A ? + WPS_RF_50GHZ : WPS_RF_24GHZ; /* FIX: dualband AP */ + } if (conf->wpa & WPA_PROTO_RSN) { if (conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK) @@ -935,8 +1169,12 @@ if (conf->ssid.security_policy == SECURITY_STATIC_WEP) cfg.static_wep_only = 1; cfg.dualband = interface_count(hapd->iface) > 1; + if ((wps->dev.rf_bands & (WPS_RF_50GHZ | WPS_RF_24GHZ)) == + (WPS_RF_50GHZ | WPS_RF_24GHZ)) + cfg.dualband = 1; if (cfg.dualband) wpa_printf(MSG_DEBUG, "WPS: Dualband AP"); + cfg.force_per_enrollee_psk = conf->force_per_enrollee_psk; wps->registrar = wps_registrar_init(wps, &cfg); if (wps->registrar == NULL) { @@ -984,10 +1222,26 @@ } +static void hostapd_wps_nfc_clear(struct wps_context *wps) +{ +#ifdef CONFIG_WPS_NFC + wpa_printf(MSG_DEBUG, "WPS: Clear NFC Tag context %p", wps); + wps->ap_nfc_dev_pw_id = 0; + wpabuf_free(wps->ap_nfc_dh_pubkey); + wps->ap_nfc_dh_pubkey = NULL; + wpabuf_free(wps->ap_nfc_dh_privkey); + wps->ap_nfc_dh_privkey = NULL; + wpabuf_free(wps->ap_nfc_dev_pw); + wps->ap_nfc_dev_pw = NULL; +#endif /* CONFIG_WPS_NFC */ +} + + void hostapd_deinit_wps(struct hostapd_data *hapd) { eloop_cancel_timeout(hostapd_wps_reenable_ap_pin, hapd, NULL); eloop_cancel_timeout(hostapd_wps_ap_pin_timeout, hapd, NULL); + eloop_cancel_timeout(wps_reload_config, hapd->iface, NULL); if (hapd->wps == NULL) return; #ifdef CONFIG_WPS_UPNP @@ -998,9 +1252,8 @@ wps_device_data_free(&hapd->wps->dev); wpabuf_free(hapd->wps->dh_pubkey); wpabuf_free(hapd->wps->dh_privkey); - wpabuf_free(hapd->wps->oob_conf.pubkey_hash); - wpabuf_free(hapd->wps->oob_conf.dev_password); wps_free_pending_msgs(hapd->wps->upnp_msgs); + hostapd_wps_nfc_clear(hapd->wps); os_free(hapd->wps); hapd->wps = NULL; hostapd_wps_clear_ies(hapd); @@ -1098,63 +1351,28 @@ } -#ifdef CONFIG_WPS_OOB -int hostapd_wps_start_oob(struct hostapd_data *hapd, char *device_type, - char *path, char *method, char *name) +static int wps_cancel(struct hostapd_data *hapd, void *ctx) { - struct wps_context *wps = hapd->wps; - struct oob_device_data *oob_dev; - - oob_dev = wps_get_oob_device(device_type); - if (oob_dev == NULL) - return -1; - oob_dev->device_path = path; - oob_dev->device_name = name; - wps->oob_conf.oob_method = wps_get_oob_method(method); - - if (wps->oob_conf.oob_method == OOB_METHOD_DEV_PWD_R) { - /* - * Use pre-configured DH keys in order to be able to write the - * key hash into the OOB file. - */ - wpabuf_free(wps->dh_pubkey); - wpabuf_free(wps->dh_privkey); - wps->dh_privkey = NULL; - wps->dh_pubkey = dh_init(dh_groups_get(WPS_DH_GROUP), - &wps->dh_privkey); - wps->dh_pubkey = wpabuf_zeropad(wps->dh_pubkey, 192); - if (wps->dh_pubkey == NULL) { - wpa_printf(MSG_ERROR, "WPS: Failed to initialize " - "Diffie-Hellman handshake"); - return -1; - } - } - - if (wps_process_oob(wps, oob_dev, 1) < 0) - goto error; + if (hapd->wps == NULL) + return 0; - if ((wps->oob_conf.oob_method == OOB_METHOD_DEV_PWD_E || - wps->oob_conf.oob_method == OOB_METHOD_DEV_PWD_R) && - hostapd_wps_add_pin(hapd, NULL, "any", - wpabuf_head(wps->oob_conf.dev_password), 0) < - 0) - goto error; + wps_registrar_wps_cancel(hapd->wps->registrar); + ap_for_each_sta(hapd, ap_sta_wps_cancel, NULL); return 0; +} -error: - wpabuf_free(wps->dh_pubkey); - wps->dh_pubkey = NULL; - wpabuf_free(wps->dh_privkey); - wps->dh_privkey = NULL; - return -1; + +int hostapd_wps_cancel(struct hostapd_data *hapd) +{ + return hostapd_wps_for_each(hapd, wps_cancel, NULL); } -#endif /* CONFIG_WPS_OOB */ static int hostapd_wps_probe_req_rx(void *ctx, const u8 *addr, const u8 *da, const u8 *bssid, - const u8 *ie, size_t ie_len) + const u8 *ie, size_t ie_len, + int ssi_signal) { struct hostapd_data *hapd = ctx; struct wpabuf *wps_ie; @@ -1471,3 +1689,314 @@ return wps_registrar_config_ap(hapd->wps->registrar, &cred); } + + +#ifdef CONFIG_WPS_NFC + +struct wps_nfc_password_token_data { + const u8 *oob_dev_pw; + size_t oob_dev_pw_len; + int added; +}; + + +static int wps_add_nfc_password_token(struct hostapd_data *hapd, void *ctx) +{ + struct wps_nfc_password_token_data *data = ctx; + int ret; + + if (hapd->wps == NULL) + return 0; + ret = wps_registrar_add_nfc_password_token(hapd->wps->registrar, + data->oob_dev_pw, + data->oob_dev_pw_len); + if (ret == 0) + data->added++; + return ret; +} + + +static int hostapd_wps_add_nfc_password_token(struct hostapd_data *hapd, + struct wps_parse_attr *attr) +{ + struct wps_nfc_password_token_data data; + + data.oob_dev_pw = attr->oob_dev_password; + data.oob_dev_pw_len = attr->oob_dev_password_len; + data.added = 0; + if (hostapd_wps_for_each(hapd, wps_add_nfc_password_token, &data) < 0) + return -1; + return data.added ? 0 : -1; +} + + +static int hostapd_wps_nfc_tag_process(struct hostapd_data *hapd, + const struct wpabuf *wps) +{ + struct wps_parse_attr attr; + + wpa_hexdump_buf(MSG_DEBUG, "WPS: Received NFC tag payload", wps); + + if (wps_parse_msg(wps, &attr)) { + wpa_printf(MSG_DEBUG, "WPS: Ignore invalid data from NFC tag"); + return -1; + } + + if (attr.oob_dev_password) + return hostapd_wps_add_nfc_password_token(hapd, &attr); + + wpa_printf(MSG_DEBUG, "WPS: Ignore unrecognized NFC tag"); + return -1; +} + + +int hostapd_wps_nfc_tag_read(struct hostapd_data *hapd, + const struct wpabuf *data) +{ + const struct wpabuf *wps = data; + struct wpabuf *tmp = NULL; + int ret; + + if (wpabuf_len(data) < 4) + return -1; + + if (*wpabuf_head_u8(data) != 0x10) { + /* Assume this contains full NDEF record */ + tmp = ndef_parse_wifi(data); + if (tmp == NULL) { + wpa_printf(MSG_DEBUG, "WPS: Could not parse NDEF"); + return -1; + } + wps = tmp; + } + + ret = hostapd_wps_nfc_tag_process(hapd, wps); + wpabuf_free(tmp); + return ret; +} + + +struct wpabuf * hostapd_wps_nfc_config_token(struct hostapd_data *hapd, + int ndef) +{ + struct wpabuf *ret; + + if (hapd->wps == NULL) + return NULL; + + ret = wps_get_oob_cred(hapd->wps, hostapd_wps_rf_band_cb(hapd), + hapd->iconf->channel); + if (ndef && ret) { + struct wpabuf *tmp; + tmp = ndef_build_wifi(ret); + wpabuf_free(ret); + if (tmp == NULL) + return NULL; + ret = tmp; + } + + return ret; +} + + +struct wpabuf * hostapd_wps_nfc_hs_cr(struct hostapd_data *hapd, int ndef) +{ + struct wpabuf *ret; + + if (hapd->wps == NULL) + return NULL; + + if (hapd->conf->wps_nfc_dh_pubkey == NULL) { + struct wps_context *wps = hapd->wps; + if (wps_nfc_gen_dh(&hapd->conf->wps_nfc_dh_pubkey, + &hapd->conf->wps_nfc_dh_privkey) < 0) + return NULL; + hostapd_wps_nfc_clear(wps); + wps->ap_nfc_dev_pw_id = DEV_PW_NFC_CONNECTION_HANDOVER; + wps->ap_nfc_dh_pubkey = + wpabuf_dup(hapd->conf->wps_nfc_dh_pubkey); + wps->ap_nfc_dh_privkey = + wpabuf_dup(hapd->conf->wps_nfc_dh_privkey); + if (!wps->ap_nfc_dh_pubkey || !wps->ap_nfc_dh_privkey) { + hostapd_wps_nfc_clear(wps); + return NULL; + } + } + + ret = wps_build_nfc_handover_sel(hapd->wps, + hapd->conf->wps_nfc_dh_pubkey, + hapd->own_addr, hapd->iface->freq); + + if (ndef && ret) { + struct wpabuf *tmp; + tmp = ndef_build_wifi(ret); + wpabuf_free(ret); + if (tmp == NULL) + return NULL; + ret = tmp; + } + + return ret; +} + + +int hostapd_wps_nfc_report_handover(struct hostapd_data *hapd, + const struct wpabuf *req, + const struct wpabuf *sel) +{ + struct wpabuf *wps; + int ret = -1; + u16 wsc_len; + const u8 *pos; + struct wpabuf msg; + struct wps_parse_attr attr; + u16 dev_pw_id; + + /* + * Enrollee/station is always initiator of the NFC connection handover, + * so use the request message here to find Enrollee public key hash. + */ + wps = ndef_parse_wifi(req); + if (wps == NULL) + return -1; + wpa_printf(MSG_DEBUG, "WPS: Received application/vnd.wfa.wsc " + "payload from NFC connection handover"); + wpa_hexdump_buf(MSG_DEBUG, "WPS: NFC payload", wps); + if (wpabuf_len(wps) < 2) { + wpa_printf(MSG_DEBUG, "WPS: Too short Wi-Fi Handover Request " + "Message"); + goto out; + } + pos = wpabuf_head(wps); + wsc_len = WPA_GET_BE16(pos); + if (wsc_len > wpabuf_len(wps) - 2) { + wpa_printf(MSG_DEBUG, "WPS: Invalid WSC attribute length (%u) " + "in rt Wi-Fi Handover Request Message", wsc_len); + goto out; + } + pos += 2; + + wpa_hexdump(MSG_DEBUG, + "WPS: WSC attributes in Wi-Fi Handover Request Message", + pos, wsc_len); + if (wsc_len < wpabuf_len(wps) - 2) { + wpa_hexdump(MSG_DEBUG, + "WPS: Ignore extra data after WSC attributes", + pos + wsc_len, wpabuf_len(wps) - 2 - wsc_len); + } + + wpabuf_set(&msg, pos, wsc_len); + ret = wps_parse_msg(&msg, &attr); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "WPS: Could not parse WSC attributes in " + "Wi-Fi Handover Request Message"); + goto out; + } + + if (attr.oob_dev_password == NULL || + attr.oob_dev_password_len < WPS_OOB_PUBKEY_HASH_LEN + 2) { + wpa_printf(MSG_DEBUG, "WPS: No Out-of-Band Device Password " + "included in Wi-Fi Handover Request Message"); + ret = -1; + goto out; + } + + if (attr.uuid_e == NULL) { + wpa_printf(MSG_DEBUG, "WPS: No UUID-E included in Wi-Fi " + "Handover Request Message"); + ret = -1; + goto out; + } + + wpa_hexdump(MSG_DEBUG, "WPS: UUID-E", attr.uuid_e, WPS_UUID_LEN); + + wpa_hexdump(MSG_DEBUG, "WPS: Out-of-Band Device Password", + attr.oob_dev_password, attr.oob_dev_password_len); + dev_pw_id = WPA_GET_BE16(attr.oob_dev_password + + WPS_OOB_PUBKEY_HASH_LEN); + if (dev_pw_id != DEV_PW_NFC_CONNECTION_HANDOVER) { + wpa_printf(MSG_DEBUG, "WPS: Unexpected OOB Device Password ID " + "%u in Wi-Fi Handover Request Message", dev_pw_id); + ret = -1; + goto out; + } + wpa_hexdump(MSG_DEBUG, "WPS: Enrollee Public Key hash", + attr.oob_dev_password, WPS_OOB_PUBKEY_HASH_LEN); + + ret = wps_registrar_add_nfc_pw_token(hapd->wps->registrar, + attr.oob_dev_password, + DEV_PW_NFC_CONNECTION_HANDOVER, + NULL, 0, 1); + +out: + wpabuf_free(wps); + return ret; +} + + +struct wpabuf * hostapd_wps_nfc_token_gen(struct hostapd_data *hapd, int ndef) +{ + if (hapd->conf->wps_nfc_pw_from_config) { + return wps_nfc_token_build(ndef, + hapd->conf->wps_nfc_dev_pw_id, + hapd->conf->wps_nfc_dh_pubkey, + hapd->conf->wps_nfc_dev_pw); + } + + return wps_nfc_token_gen(ndef, &hapd->conf->wps_nfc_dev_pw_id, + &hapd->conf->wps_nfc_dh_pubkey, + &hapd->conf->wps_nfc_dh_privkey, + &hapd->conf->wps_nfc_dev_pw); +} + + +int hostapd_wps_nfc_token_enable(struct hostapd_data *hapd) +{ + struct wps_context *wps = hapd->wps; + struct wpabuf *pw; + + if (wps == NULL) + return -1; + + if (!hapd->conf->wps_nfc_dh_pubkey || + !hapd->conf->wps_nfc_dh_privkey || + !hapd->conf->wps_nfc_dev_pw || + !hapd->conf->wps_nfc_dev_pw_id) + return -1; + + hostapd_wps_nfc_clear(wps); + wpa_printf(MSG_DEBUG, + "WPS: Enable NFC Tag (Dev Pw Id %u) for AP interface %s (context %p)", + hapd->conf->wps_nfc_dev_pw_id, hapd->conf->iface, wps); + wps->ap_nfc_dev_pw_id = hapd->conf->wps_nfc_dev_pw_id; + wps->ap_nfc_dh_pubkey = wpabuf_dup(hapd->conf->wps_nfc_dh_pubkey); + wps->ap_nfc_dh_privkey = wpabuf_dup(hapd->conf->wps_nfc_dh_privkey); + pw = hapd->conf->wps_nfc_dev_pw; + wps->ap_nfc_dev_pw = wpabuf_alloc( + wpabuf_len(pw) * 2 + 1); + if (wps->ap_nfc_dev_pw) { + wpa_snprintf_hex_uppercase( + (char *) wpabuf_put(wps->ap_nfc_dev_pw, + wpabuf_len(pw) * 2), + wpabuf_len(pw) * 2 + 1, + wpabuf_head(pw), wpabuf_len(pw)); + } + + if (!wps->ap_nfc_dh_pubkey || !wps->ap_nfc_dh_privkey || + !wps->ap_nfc_dev_pw) { + hostapd_wps_nfc_clear(wps); + return -1; + } + + return 0; +} + + +void hostapd_wps_nfc_token_disable(struct hostapd_data *hapd) +{ + wpa_printf(MSG_DEBUG, "WPS: Disable NFC token for AP interface %s", + hapd->conf->iface); + hostapd_wps_nfc_clear(hapd->wps); +} + +#endif /* CONFIG_WPS_NFC */ diff -Nru wpa-1.0/src/ap/wps_hostapd.h wpa-2.1/src/ap/wps_hostapd.h --- wpa-1.0/src/ap/wps_hostapd.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/ap/wps_hostapd.h 2014-02-04 11:23:35.000000000 +0000 @@ -1,15 +1,9 @@ /* * hostapd / WPS integration - * Copyright (c) 2008-2010, Jouni Malinen + * Copyright (c) 2008-2012, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef WPS_HOSTAPD_H @@ -22,12 +16,12 @@ int hostapd_init_wps_complete(struct hostapd_data *hapd); void hostapd_deinit_wps(struct hostapd_data *hapd); void hostapd_update_wps(struct hostapd_data *hapd); +void hostapd_wps_eap_completed(struct hostapd_data *hapd); int hostapd_wps_add_pin(struct hostapd_data *hapd, const u8 *addr, const char *uuid, const char *pin, int timeout); int hostapd_wps_button_pushed(struct hostapd_data *hapd, const u8 *p2p_dev_addr); -int hostapd_wps_start_oob(struct hostapd_data *hapd, char *device_type, - char *path, char *method, char *name); +int hostapd_wps_cancel(struct hostapd_data *hapd); int hostapd_wps_get_mib_sta(struct hostapd_data *hapd, const u8 *addr, char *buf, size_t buflen); void hostapd_wps_ap_pin_disable(struct hostapd_data *hapd); @@ -38,6 +32,17 @@ void hostapd_wps_update_ie(struct hostapd_data *hapd); int hostapd_wps_config_ap(struct hostapd_data *hapd, const char *ssid, const char *auth, const char *encr, const char *key); +int hostapd_wps_nfc_tag_read(struct hostapd_data *hapd, + const struct wpabuf *data); +struct wpabuf * hostapd_wps_nfc_config_token(struct hostapd_data *hapd, + int ndef); +struct wpabuf * hostapd_wps_nfc_hs_cr(struct hostapd_data *hapd, int ndef); +int hostapd_wps_nfc_report_handover(struct hostapd_data *hapd, + const struct wpabuf *req, + const struct wpabuf *sel); +struct wpabuf * hostapd_wps_nfc_token_gen(struct hostapd_data *hapd, int ndef); +int hostapd_wps_nfc_token_enable(struct hostapd_data *hapd); +void hostapd_wps_nfc_token_disable(struct hostapd_data *hapd); #else /* CONFIG_WPS */ @@ -60,6 +65,10 @@ { } +static inline void hostapd_wps_eap_completed(struct hostapd_data *hapd) +{ +} + static inline int hostapd_wps_get_mib_sta(struct hostapd_data *hapd, const u8 *addr, char *buf, size_t buflen) @@ -72,6 +81,11 @@ { return 0; } + +static inline int hostapd_wps_cancel(struct hostapd_data *hapd) +{ + return 0; +} #endif /* CONFIG_WPS */ diff -Nru wpa-1.0/src/common/defs.h wpa-2.1/src/common/defs.h --- wpa-1.0/src/common/defs.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/common/defs.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * WPA Supplicant - Common definitions * Copyright (c) 2004-2008, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef DEFS_H @@ -29,9 +23,15 @@ #define WPA_CIPHER_WEP104 BIT(2) #define WPA_CIPHER_TKIP BIT(3) #define WPA_CIPHER_CCMP BIT(4) -#ifdef CONFIG_IEEE80211W #define WPA_CIPHER_AES_128_CMAC BIT(5) -#endif /* CONFIG_IEEE80211W */ +#define WPA_CIPHER_GCMP BIT(6) +#define WPA_CIPHER_SMS4 BIT(7) +#define WPA_CIPHER_GCMP_256 BIT(8) +#define WPA_CIPHER_CCMP_256 BIT(9) +#define WPA_CIPHER_BIP_GMAC_128 BIT(11) +#define WPA_CIPHER_BIP_GMAC_256 BIT(12) +#define WPA_CIPHER_BIP_CMAC_256 BIT(13) +#define WPA_CIPHER_GTK_NOT_USED BIT(14) #define WPA_KEY_MGMT_IEEE8021X BIT(0) #define WPA_KEY_MGMT_PSK BIT(1) @@ -43,11 +43,17 @@ #define WPA_KEY_MGMT_IEEE8021X_SHA256 BIT(7) #define WPA_KEY_MGMT_PSK_SHA256 BIT(8) #define WPA_KEY_MGMT_WPS BIT(9) +#define WPA_KEY_MGMT_SAE BIT(10) +#define WPA_KEY_MGMT_FT_SAE BIT(11) +#define WPA_KEY_MGMT_WAPI_PSK BIT(12) +#define WPA_KEY_MGMT_WAPI_CERT BIT(13) +#define WPA_KEY_MGMT_CCKM BIT(14) static inline int wpa_key_mgmt_wpa_ieee8021x(int akm) { return !!(akm & (WPA_KEY_MGMT_IEEE8021X | WPA_KEY_MGMT_FT_IEEE8021X | + WPA_KEY_MGMT_CCKM | WPA_KEY_MGMT_IEEE8021X_SHA256)); } @@ -55,13 +61,22 @@ { return !!(akm & (WPA_KEY_MGMT_PSK | WPA_KEY_MGMT_FT_PSK | - WPA_KEY_MGMT_PSK_SHA256)); + WPA_KEY_MGMT_PSK_SHA256 | + WPA_KEY_MGMT_SAE | + WPA_KEY_MGMT_FT_SAE)); } 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_IEEE8021X | + WPA_KEY_MGMT_FT_SAE)); +} + +static inline int wpa_key_mgmt_sae(int akm) +{ + return !!(akm & (WPA_KEY_MGMT_SAE | + WPA_KEY_MGMT_FT_SAE)); } static inline int wpa_key_mgmt_sha256(int akm) @@ -73,17 +88,30 @@ static inline int wpa_key_mgmt_wpa(int akm) { return wpa_key_mgmt_wpa_ieee8021x(akm) || - wpa_key_mgmt_wpa_psk(akm); + wpa_key_mgmt_wpa_psk(akm) || + wpa_key_mgmt_sae(akm); +} + +static inline int wpa_key_mgmt_wpa_any(int akm) +{ + return wpa_key_mgmt_wpa(akm) || (akm & WPA_KEY_MGMT_WPA_NONE); +} + +static inline int wpa_key_mgmt_cckm(int akm) +{ + return akm == WPA_KEY_MGMT_CCKM; } #define WPA_PROTO_WPA BIT(0) #define WPA_PROTO_RSN BIT(1) +#define WPA_PROTO_WAPI BIT(2) #define WPA_AUTH_ALG_OPEN BIT(0) #define WPA_AUTH_ALG_SHARED BIT(1) #define WPA_AUTH_ALG_LEAP BIT(2) #define WPA_AUTH_ALG_FT BIT(3) +#define WPA_AUTH_ALG_SAE BIT(4) enum wpa_alg { @@ -92,34 +120,15 @@ WPA_ALG_TKIP, WPA_ALG_CCMP, WPA_ALG_IGTK, - WPA_ALG_PMK -}; - -/** - * enum wpa_cipher - Cipher suites - */ -enum wpa_cipher { - CIPHER_NONE, - CIPHER_WEP40, - CIPHER_TKIP, - CIPHER_CCMP, - CIPHER_WEP104 -}; - -/** - * enum wpa_key_mgmt - Key management suites - */ -enum wpa_key_mgmt { - KEY_MGMT_802_1X, - KEY_MGMT_PSK, - KEY_MGMT_NONE, - KEY_MGMT_802_1X_NO_WPA, - KEY_MGMT_WPA_NONE, - KEY_MGMT_FT_802_1X, - KEY_MGMT_FT_PSK, - KEY_MGMT_802_1X_SHA256, - KEY_MGMT_PSK_SHA256, - KEY_MGMT_WPS + WPA_ALG_PMK, + WPA_ALG_GCMP, + WPA_ALG_SMS4, + WPA_ALG_KRK, + WPA_ALG_GCMP_256, + WPA_ALG_CCMP_256, + WPA_ALG_BIP_GMAC_128, + WPA_ALG_BIP_GMAC_256, + WPA_ALG_BIP_CMAC_256 }; /** @@ -254,8 +263,9 @@ enum mfp_options { NO_MGMT_FRAME_PROTECTION = 0, MGMT_FRAME_PROTECTION_OPTIONAL = 1, - MGMT_FRAME_PROTECTION_REQUIRED = 2 + MGMT_FRAME_PROTECTION_REQUIRED = 2, }; +#define MGMT_FRAME_PROTECTION_DEFAULT 3 /** * enum hostapd_hw_mode - Hardware mode @@ -264,6 +274,7 @@ HOSTAPD_MODE_IEEE80211B, HOSTAPD_MODE_IEEE80211G, HOSTAPD_MODE_IEEE80211A, + HOSTAPD_MODE_IEEE80211AD, NUM_HOSTAPD_MODES }; @@ -278,7 +289,11 @@ WPA_CTRL_REQ_EAP_PIN, WPA_CTRL_REQ_EAP_OTP, WPA_CTRL_REQ_EAP_PASSPHRASE, + WPA_CTRL_REQ_SIM, NUM_WPA_CTRL_REQS }; +/* Maximum number of EAP methods to store for EAP server user information */ +#define EAP_MAX_METHODS 8 + #endif /* DEFS_H */ diff -Nru wpa-1.0/src/common/eapol_common.h wpa-2.1/src/common/eapol_common.h --- wpa-1.0/src/common/eapol_common.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/common/eapol_common.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * EAPOL definitions shared between hostapd and wpa_supplicant * Copyright (c) 2002-2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef EAPOL_COMMON_H @@ -44,4 +38,44 @@ enum { EAPOL_KEY_TYPE_RC4 = 1, EAPOL_KEY_TYPE_RSN = 2, EAPOL_KEY_TYPE_WPA = 254 }; + +#define IEEE8021X_REPLAY_COUNTER_LEN 8 +#define IEEE8021X_KEY_SIGN_LEN 16 +#define IEEE8021X_KEY_IV_LEN 16 + +#define IEEE8021X_KEY_INDEX_FLAG 0x80 +#define IEEE8021X_KEY_INDEX_MASK 0x03 + +#ifdef _MSC_VER +#pragma pack(push, 1) +#endif /* _MSC_VER */ + +struct ieee802_1x_eapol_key { + u8 type; + /* Note: key_length is unaligned */ + u8 key_length[2]; + /* does not repeat within the life of the keying material used to + * encrypt the Key field; 64-bit NTP timestamp MAY be used here */ + u8 replay_counter[IEEE8021X_REPLAY_COUNTER_LEN]; + u8 key_iv[IEEE8021X_KEY_IV_LEN]; /* cryptographically random number */ + u8 key_index; /* key flag in the most significant bit: + * 0 = broadcast (default key), + * 1 = unicast (key mapping key); key index is in the + * 7 least significant bits */ + /* HMAC-MD5 message integrity check computed with MS-MPPE-Send-Key as + * the key */ + u8 key_signature[IEEE8021X_KEY_SIGN_LEN]; + + /* followed by key: if packet body length = 44 + key length, then the + * key field (of key_length bytes) contains the key in encrypted form; + * if packet body length = 44, key field is absent and key_length + * represents the number of least significant octets from + * MS-MPPE-Send-Key attribute to be used as the keying material; + * RC4 key used in encryption = Key-IV + MS-MPPE-Recv-Key */ +} STRUCT_PACKED; + +#ifdef _MSC_VER +#pragma pack(pop) +#endif /* _MSC_VER */ + #endif /* EAPOL_COMMON_H */ diff -Nru wpa-1.0/src/common/gas.c wpa-2.1/src/common/gas.c --- wpa-1.0/src/common/gas.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/common/gas.c 2014-02-04 11:23:35.000000000 +0000 @@ -1,16 +1,10 @@ /* * Generic advertisement service (GAS) (IEEE 802.11u) * Copyright (c) 2009, Atheros Communications - * Copyright (c) 2011, Qualcomm Atheros + * Copyright (c) 2011-2012, Qualcomm Atheros * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -37,7 +31,7 @@ } -static struct wpabuf * gas_build_initial_req(u8 dialog_token, size_t size) +struct wpabuf * gas_build_initial_req(u8 dialog_token, size_t size) { return gas_build_req(WLAN_PA_GAS_INITIAL_REQ, dialog_token, size); diff -Nru wpa-1.0/src/common/gas.h wpa-2.1/src/common/gas.h --- wpa-1.0/src/common/gas.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/common/gas.h 2014-02-04 11:23:35.000000000 +0000 @@ -1,21 +1,16 @@ /* * Generic advertisement service (GAS) (IEEE 802.11u) * Copyright (c) 2009, Atheros Communications - * Copyright (c) 2011, Qualcomm Atheros + * Copyright (c) 2011-2012, Qualcomm Atheros * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef GAS_H #define GAS_H +struct wpabuf * gas_build_initial_req(u8 dialog_token, size_t size); struct wpabuf * gas_build_comeback_req(u8 dialog_token); struct wpabuf * gas_build_initial_resp(u8 dialog_token, u16 status_code, u16 comeback_delay, size_t size); diff -Nru wpa-1.0/src/common/ieee802_11_common.c wpa-2.1/src/common/ieee802_11_common.c --- wpa-1.0/src/common/ieee802_11_common.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/common/ieee802_11_common.c 2014-02-04 11:23:35.000000000 +0000 @@ -1,20 +1,15 @@ /* * IEEE 802.11 Common routines - * Copyright (c) 2002-2009, Jouni Malinen + * Copyright (c) 2002-2013, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" #include "common.h" +#include "defs.h" #include "ieee802_11_defs.h" #include "ieee802_11_common.h" @@ -103,6 +98,16 @@ elems->p2p = pos; elems->p2p_len = elen; break; + case WFD_OUI_TYPE: + /* Wi-Fi Alliance - WFD IE */ + elems->wfd = pos; + elems->wfd_len = elen; + break; + case HS20_INDICATION_OUI_TYPE: + /* Hotspot 2.0 */ + elems->hs20 = pos; + elems->hs20_len = elen; + break; default: wpa_printf(MSG_MSGDUMP, "Unknown WFA " "information element ignored " @@ -184,25 +189,12 @@ elems->supp_rates = pos; elems->supp_rates_len = elen; break; - case WLAN_EID_FH_PARAMS: - elems->fh_params = pos; - elems->fh_params_len = elen; - break; case WLAN_EID_DS_PARAMS: elems->ds_params = pos; elems->ds_params_len = elen; break; case WLAN_EID_CF_PARAMS: - elems->cf_params = pos; - elems->cf_params_len = elen; - break; case WLAN_EID_TIM: - elems->tim = pos; - elems->tim_len = elen; - break; - case WLAN_EID_IBSS_PARAMS: - elems->ibss_params = pos; - elems->ibss_params_len = elen; break; case WLAN_EID_CHALLENGE: elems->challenge = pos; @@ -227,8 +219,6 @@ elems->rsn_ie_len = elen; break; case WLAN_EID_PWR_CAPABILITY: - elems->power_cap = pos; - elems->power_cap_len = elen; break; case WLAN_EID_SUPPORTED_CHANNELS: elems->supp_channels = pos; @@ -254,6 +244,14 @@ elems->ht_operation = pos; elems->ht_operation_len = elen; break; + case WLAN_EID_VHT_CAP: + elems->vht_capabilities = pos; + elems->vht_capabilities_len = elen; + break; + case WLAN_EID_VHT_OPERATION: + elems->vht_operation = pos; + elems->vht_operation_len = elen; + break; case WLAN_EID_LINK_ID: if (elen < 18) break; @@ -263,6 +261,25 @@ elems->interworking = pos; elems->interworking_len = elen; break; + case WLAN_EID_QOS_MAP_SET: + if (elen < 16) + break; + elems->qos_map_set = pos; + elems->qos_map_set_len = elen; + break; + case WLAN_EID_EXT_CAPAB: + elems->ext_capab = pos; + elems->ext_capab_len = elen; + break; + case WLAN_EID_BSS_MAX_IDLE_PERIOD: + if (elen < 3) + break; + elems->bss_max_idle_period = pos; + break; + case WLAN_EID_SSID_LIST: + elems->ssid_list = pos; + elems->ssid_list_len = elen; + break; default: unknown++; if (!show_errors) @@ -389,3 +406,133 @@ return NULL; } } + + +int hostapd_config_wmm_ac(struct hostapd_wmm_ac_params wmm_ac_params[], + const char *name, const char *val) +{ + int num, v; + const char *pos; + struct hostapd_wmm_ac_params *ac; + + /* skip 'wme_ac_' or 'wmm_ac_' prefix */ + pos = name + 7; + if (os_strncmp(pos, "be_", 3) == 0) { + num = 0; + pos += 3; + } else if (os_strncmp(pos, "bk_", 3) == 0) { + num = 1; + pos += 3; + } else if (os_strncmp(pos, "vi_", 3) == 0) { + num = 2; + pos += 3; + } else if (os_strncmp(pos, "vo_", 3) == 0) { + num = 3; + pos += 3; + } else { + wpa_printf(MSG_ERROR, "Unknown WMM name '%s'", pos); + return -1; + } + + ac = &wmm_ac_params[num]; + + if (os_strcmp(pos, "aifs") == 0) { + v = atoi(val); + if (v < 1 || v > 255) { + wpa_printf(MSG_ERROR, "Invalid AIFS value %d", v); + return -1; + } + ac->aifs = v; + } else if (os_strcmp(pos, "cwmin") == 0) { + v = atoi(val); + if (v < 0 || v > 12) { + wpa_printf(MSG_ERROR, "Invalid cwMin value %d", v); + return -1; + } + ac->cwmin = v; + } else if (os_strcmp(pos, "cwmax") == 0) { + v = atoi(val); + if (v < 0 || v > 12) { + wpa_printf(MSG_ERROR, "Invalid cwMax value %d", v); + return -1; + } + ac->cwmax = v; + } else if (os_strcmp(pos, "txop_limit") == 0) { + v = atoi(val); + if (v < 0 || v > 0xffff) { + wpa_printf(MSG_ERROR, "Invalid txop value %d", v); + return -1; + } + ac->txop_limit = v; + } else if (os_strcmp(pos, "acm") == 0) { + v = atoi(val); + if (v < 0 || v > 1) { + wpa_printf(MSG_ERROR, "Invalid acm value %d", v); + return -1; + } + ac->admission_control_mandatory = v; + } else { + wpa_printf(MSG_ERROR, "Unknown wmm_ac_ field '%s'", pos); + return -1; + } + + return 0; +} + + +enum hostapd_hw_mode ieee80211_freq_to_chan(int freq, u8 *channel) +{ + enum hostapd_hw_mode mode = NUM_HOSTAPD_MODES; + + if (freq >= 2412 && freq <= 2472) { + mode = HOSTAPD_MODE_IEEE80211G; + *channel = (freq - 2407) / 5; + } else if (freq == 2484) { + mode = HOSTAPD_MODE_IEEE80211B; + *channel = 14; + } else if (freq >= 4900 && freq < 5000) { + mode = HOSTAPD_MODE_IEEE80211A; + *channel = (freq - 4000) / 5; + } else if (freq >= 5000 && freq < 5900) { + mode = HOSTAPD_MODE_IEEE80211A; + *channel = (freq - 5000) / 5; + } else if (freq >= 56160 + 2160 * 1 && freq <= 56160 + 2160 * 4) { + mode = HOSTAPD_MODE_IEEE80211AD; + *channel = (freq - 56160) / 2160; + } + + return mode; +} + + +static int is_11b(u8 rate) +{ + return rate == 0x02 || rate == 0x04 || rate == 0x0b || rate == 0x16; +} + + +int supp_rates_11b_only(struct ieee802_11_elems *elems) +{ + int num_11b = 0, num_others = 0; + int i; + + if (elems->supp_rates == NULL && elems->ext_supp_rates == NULL) + return 0; + + for (i = 0; elems->supp_rates && i < elems->supp_rates_len; i++) { + if (is_11b(elems->supp_rates[i])) + num_11b++; + else + num_others++; + } + + for (i = 0; elems->ext_supp_rates && i < elems->ext_supp_rates_len; + i++) { + if (is_11b(elems->ext_supp_rates[i])) + num_11b++; + else + num_others++; + } + + return num_11b > 0 && num_others == 0; +} diff -Nru wpa-1.0/src/common/ieee802_11_common.h wpa-2.1/src/common/ieee802_11_common.h --- wpa-1.0/src/common/ieee802_11_common.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/common/ieee802_11_common.h 2014-02-04 11:23:35.000000000 +0000 @@ -1,15 +1,9 @@ /* * IEEE 802.11 Common routines - * Copyright (c) 2002-2009, Jouni Malinen + * Copyright (c) 2002-2012, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef IEEE802_11_COMMON_H @@ -19,11 +13,7 @@ struct ieee802_11_elems { const u8 *ssid; const u8 *supp_rates; - const u8 *fh_params; const u8 *ds_params; - const u8 *cf_params; - const u8 *tim; - const u8 *ibss_params; const u8 *challenge; const u8 *erp_info; const u8 *ext_supp_rates; @@ -32,25 +22,28 @@ const u8 *wmm; /* WMM Information or Parameter Element */ const u8 *wmm_tspec; const u8 *wps_ie; - const u8 *power_cap; const u8 *supp_channels; const u8 *mdie; const u8 *ftie; const u8 *timeout_int; const u8 *ht_capabilities; const u8 *ht_operation; + const u8 *vht_capabilities; + const u8 *vht_operation; const u8 *vendor_ht_cap; const u8 *p2p; + const u8 *wfd; const u8 *link_id; const u8 *interworking; + const u8 *qos_map_set; + const u8 *hs20; + const u8 *ext_capab; + const u8 *bss_max_idle_period; + const u8 *ssid_list; u8 ssid_len; u8 supp_rates_len; - u8 fh_params_len; u8 ds_params_len; - u8 cf_params_len; - u8 tim_len; - u8 ibss_params_len; u8 challenge_len; u8 erp_info_len; u8 ext_supp_rates_len; @@ -59,16 +52,22 @@ u8 wmm_len; /* 7 = WMM Information; 24 = WMM Parameter */ u8 wmm_tspec_len; u8 wps_ie_len; - u8 power_cap_len; u8 supp_channels_len; u8 mdie_len; u8 ftie_len; u8 timeout_int_len; u8 ht_capabilities_len; u8 ht_operation_len; + u8 vht_capabilities_len; + u8 vht_operation_len; u8 vendor_ht_cap_len; u8 p2p_len; + u8 wfd_len; u8 interworking_len; + u8 qos_map_set_len; + u8 hs20_len; + u8 ext_capab_len; + u8 ssid_list_len; }; typedef enum { ParseOK = 0, ParseUnknown = 1, ParseFailed = -1 } ParseRes; @@ -82,4 +81,18 @@ struct ieee80211_hdr; const u8 * get_hdr_bssid(const struct ieee80211_hdr *hdr, size_t len); +struct hostapd_wmm_ac_params { + int cwmin; + int cwmax; + int aifs; + int txop_limit; /* in units of 32us */ + int admission_control_mandatory; +}; + +int hostapd_config_wmm_ac(struct hostapd_wmm_ac_params wmm_ac_params[], + const char *name, const char *val); +enum hostapd_hw_mode ieee80211_freq_to_chan(int freq, u8 *channel); + +int supp_rates_11b_only(struct ieee802_11_elems *elems); + #endif /* IEEE802_11_COMMON_H */ diff -Nru wpa-1.0/src/common/ieee802_11_defs.h wpa-2.1/src/common/ieee802_11_defs.h --- wpa-1.0/src/common/ieee802_11_defs.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/common/ieee802_11_defs.h 2014-02-04 11:23:35.000000000 +0000 @@ -3,14 +3,8 @@ * Copyright (c) 2002-2009, Jouni Malinen * Copyright (c) 2007-2008 Intel Corporation * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef IEEE802_11_DEFS_H @@ -82,6 +76,7 @@ #define WLAN_AUTH_OPEN 0 #define WLAN_AUTH_SHARED_KEY 1 #define WLAN_AUTH_FT 2 +#define WLAN_AUTH_SAE 3 #define WLAN_AUTH_LEAP 128 #define WLAN_AUTH_CHALLENGE_LEN 128 @@ -163,7 +158,10 @@ #define WLAN_STATUS_REQ_REFUSED_SSPN 67 #define WLAN_STATUS_REQ_REFUSED_UNAUTH_ACCESS 68 #define WLAN_STATUS_INVALID_RSNIE 72 +#define WLAN_STATUS_ANTI_CLOGGING_TOKEN_REQ 76 +#define WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED 77 #define WLAN_STATUS_TRANSMISSION_FAILURE 79 +#define WLAN_STATUS_ASSOC_DENIED_NO_VHT 104 /* Reason codes (IEEE 802.11-2007, 7.3.1.7, Table 7-22) */ #define WLAN_REASON_UNSPECIFIED 1 @@ -206,6 +204,7 @@ #define WLAN_EID_TIM 5 #define WLAN_EID_IBSS_PARAMS 6 #define WLAN_EID_COUNTRY 7 +#define WLAN_EID_BSS_LOAD 11 #define WLAN_EID_CHALLENGE 16 /* EIDs defined by IEEE 802.11h - START */ #define WLAN_EID_PWR_CONSTRAINT 32 @@ -221,25 +220,44 @@ /* EIDs defined by IEEE 802.11h - END */ #define WLAN_EID_ERP_INFO 42 #define WLAN_EID_HT_CAP 45 +#define WLAN_EID_QOS 46 #define WLAN_EID_RSN 48 #define WLAN_EID_EXT_SUPP_RATES 50 #define WLAN_EID_MOBILITY_DOMAIN 54 #define WLAN_EID_FAST_BSS_TRANSITION 55 #define WLAN_EID_TIMEOUT_INTERVAL 56 #define WLAN_EID_RIC_DATA 57 +#define WLAN_EID_SUPPORTED_OPERATING_CLASSES 59 #define WLAN_EID_HT_OPERATION 61 #define WLAN_EID_SECONDARY_CHANNEL_OFFSET 62 +#define WLAN_EID_WAPI 68 #define WLAN_EID_TIME_ADVERTISEMENT 69 #define WLAN_EID_20_40_BSS_COEXISTENCE 72 #define WLAN_EID_20_40_BSS_INTOLERANT 73 #define WLAN_EID_OVERLAPPING_BSS_SCAN_PARAMS 74 #define WLAN_EID_MMIE 76 +#define WLAN_EID_SSID_LIST 84 +#define WLAN_EID_BSS_MAX_IDLE_PERIOD 90 +#define WLAN_EID_TFS_REQ 91 +#define WLAN_EID_TFS_RESP 92 +#define WLAN_EID_WNMSLEEP 93 #define WLAN_EID_TIME_ZONE 98 #define WLAN_EID_LINK_ID 101 #define WLAN_EID_INTERWORKING 107 #define WLAN_EID_ADV_PROTO 108 +#define WLAN_EID_QOS_MAP_SET 110 #define WLAN_EID_ROAMING_CONSORTIUM 111 #define WLAN_EID_EXT_CAPAB 127 +#define WLAN_EID_CCKM 156 +#define WLAN_EID_VHT_CAP 191 +#define WLAN_EID_VHT_OPERATION 192 +#define WLAN_EID_VHT_EXTENDED_BSS_LOAD 193 +#define WLAN_EID_VHT_WIDE_BW_CHSWITCH 194 +#define WLAN_EID_VHT_TRANSMIT_POWER_ENVELOPE 195 +#define WLAN_EID_VHT_CHANNEL_SWITCH_WRAPPER 196 +#define WLAN_EID_VHT_AID 197 +#define WLAN_EID_VHT_QUIET_CHANNEL 198 +#define WLAN_EID_VHT_OPERATING_MODE_NOTIFICATION 199 #define WLAN_EID_VENDOR_SPECIFIC 221 @@ -253,6 +271,7 @@ #define WLAN_ACTION_FT 6 #define WLAN_ACTION_HT 7 #define WLAN_ACTION_SA_QUERY 8 +#define WLAN_ACTION_PROTECTED_DUAL 9 #define WLAN_ACTION_WNM 10 #define WLAN_ACTION_UNPROTECTED_WNM 11 #define WLAN_ACTION_TDLS 12 @@ -260,6 +279,7 @@ #define WLAN_ACTION_VENDOR_SPECIFIC 127 /* Public action codes */ +#define WLAN_PA_20_40_BSS_COEX 0 #define WLAN_PA_VENDOR_SPECIFIC 9 #define WLAN_PA_GAS_INITIAL_REQ 10 #define WLAN_PA_GAS_INITIAL_RESP 11 @@ -267,6 +287,19 @@ #define WLAN_PA_GAS_COMEBACK_RESP 13 #define WLAN_TDLS_DISCOVERY_RESPONSE 14 +/* Protected Dual of Public Action frames */ +#define WLAN_PROT_DSE_ENABLEMENT 1 +#define WLAN_PROT_DSE_DEENABLEMENT 2 +#define WLAN_PROT_EXT_CSA 4 +#define WLAN_PROT_MEASUREMENT_REQ 5 +#define WLAN_PROT_MEASUREMENT_REPORT 6 +#define WLAN_PROT_DSE_POWER_CONSTRAINT 8 +#define WLAN_PROT_VENDOR_SPECIFIC 9 +#define WLAN_PROT_GAS_INITIAL_REQ 10 +#define WLAN_PROT_GAS_INITIAL_RESP 11 +#define WLAN_PROT_GAS_COMEBACK_REQ 12 +#define WLAN_PROT_GAS_COMEBACK_RESP 13 + /* SA Query Action frame (IEEE 802.11w/D8.0, 7.4.9) */ #define WLAN_SA_QUERY_REQUEST 0 #define WLAN_SA_QUERY_RESPONSE 1 @@ -493,6 +526,17 @@ } STRUCT_PACKED sa_query_resp; struct { u8 action; + u8 dialogtoken; + u8 variable[0]; + } STRUCT_PACKED wnm_sleep_req; + struct { + u8 action; + u8 dialogtoken; + le16 keydata_len; + u8 variable[0]; + } STRUCT_PACKED wnm_sleep_resp; + struct { + u8 action; u8 variable[0]; } STRUCT_PACKED public_action; struct { @@ -513,12 +557,33 @@ * Entries */ u8 variable[0]; } STRUCT_PACKED bss_tm_req; + struct { + u8 action; /* 8 */ + u8 dialog_token; + u8 status_code; + u8 bss_termination_delay; + /* Target BSSID (optional), + * BSS Transition Candidate List + * Entries (optional) */ + u8 variable[0]; + } STRUCT_PACKED bss_tm_resp; + struct { + u8 action; /* 6 */ + u8 dialog_token; + u8 query_reason; + /* BSS Transition Candidate List + * Entries (optional) */ + u8 variable[0]; + } STRUCT_PACKED bss_tm_query; } u; } STRUCT_PACKED action; } u; } STRUCT_PACKED; +/* Rx MCS bitmask is in the first 77 bits of supported_mcs_set */ +#define IEEE80211_HT_MCS_MASK_LEN 10 + struct ieee80211_ht_capabilities { le16 ht_capabilities_info; u8 a_mpdu_params; @@ -537,6 +602,35 @@ u8 basic_set[16]; } STRUCT_PACKED; + +struct ieee80211_obss_scan_parameters { + le16 scan_passive_dwell; + le16 scan_active_dwell; + le16 width_trigger_scan_interval; + le16 scan_passive_total_per_channel; + le16 scan_active_total_per_channel; + le16 channel_transition_delay_factor; + le16 scan_activity_threshold; +} STRUCT_PACKED; + + +struct ieee80211_vht_capabilities { + le32 vht_capabilities_info; + struct { + le16 rx_map; + le16 rx_highest; + le16 tx_map; + le16 tx_highest; + } vht_supported_mcs_set; +} STRUCT_PACKED; + +struct ieee80211_vht_operation { + u8 vht_op_info_chwidth; + u8 vht_op_info_chan_center_freq_seg0_idx; + u8 vht_op_info_chan_center_freq_seg1_idx; + le16 vht_basic_mcs_set; +} STRUCT_PACKED; + #ifdef _MSC_VER #pragma pack(pop) #endif /* _MSC_VER */ @@ -618,7 +712,7 @@ #define OP_MODE_MIXED 3 #define HT_INFO_OPERATION_MODE_OP_MODE_MASK \ - ((le16) (0x0001 | 0x0002)) + (0x0001 | 0x0002) #define HT_INFO_OPERATION_MODE_OP_MODE_OFFSET 0 #define HT_INFO_OPERATION_MODE_NON_GF_DEVS_PRESENT ((u8) BIT(2)) #define HT_INFO_OPERATION_MODE_TRANSMIT_BURST_LIMIT ((u8) BIT(3)) @@ -631,14 +725,60 @@ #define HT_INFO_STBC_PARAM_PCO_ACTIVE ((u16) BIT(10)) #define HT_INFO_STBC_PARAM_PCO_PHASE ((u16) BIT(11)) +#define BSS_MEMBERSHIP_SELECTOR_VHT_PHY 126 #define BSS_MEMBERSHIP_SELECTOR_HT_PHY 127 +/* VHT Defines */ +#define VHT_CAP_MAX_MPDU_LENGTH_7991 ((u32) BIT(0)) +#define VHT_CAP_MAX_MPDU_LENGTH_11454 ((u32) BIT(1)) +#define VHT_CAP_MAX_MPDU_LENGTH_MASK ((u32) BIT(0) | BIT(1)) +#define VHT_CAP_SUPP_CHAN_WIDTH_160MHZ ((u32) BIT(2)) +#define VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ ((u32) BIT(3)) +#define VHT_CAP_SUPP_CHAN_WIDTH_MASK ((u32) BIT(2) | BIT(3)) +#define VHT_CAP_RXLDPC ((u32) BIT(4)) +#define VHT_CAP_SHORT_GI_80 ((u32) BIT(5)) +#define VHT_CAP_SHORT_GI_160 ((u32) BIT(6)) +#define VHT_CAP_TXSTBC ((u32) BIT(7)) +#define VHT_CAP_RXSTBC_1 ((u32) BIT(8)) +#define VHT_CAP_RXSTBC_2 ((u32) BIT(9)) +#define VHT_CAP_RXSTBC_3 ((u32) BIT(8) | BIT(9)) +#define VHT_CAP_RXSTBC_4 ((u32) BIT(10)) +#define VHT_CAP_RXSTBC_MASK ((u32) BIT(8) | BIT(9) | \ + BIT(10)) +#define VHT_CAP_SU_BEAMFORMER_CAPABLE ((u32) BIT(11)) +#define VHT_CAP_SU_BEAMFORMEE_CAPABLE ((u32) BIT(12)) +#define VHT_CAP_BEAMFORMEE_STS_MAX ((u32) BIT(13) | \ + BIT(14) | BIT(15)) +#define VHT_CAP_BEAMFORMEE_STS_OFFSET 13 +#define VHT_CAP_SOUNDING_DIMENSION_MAX ((u32) BIT(16) | \ + BIT(17) | BIT(18)) +#define VHT_CAP_SOUNDING_DIMENSION_OFFSET 16 +#define VHT_CAP_MU_BEAMFORMER_CAPABLE ((u32) BIT(19)) +#define VHT_CAP_MU_BEAMFORMEE_CAPABLE ((u32) BIT(20)) +#define VHT_CAP_VHT_TXOP_PS ((u32) BIT(21)) +#define VHT_CAP_HTC_VHT ((u32) BIT(22)) +#define VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT ((u32) BIT(23) | \ + BIT(24) | BIT(25)) +#define VHT_CAP_VHT_LINK_ADAPTATION_VHT_UNSOL_MFB ((u32) BIT(27)) +#define VHT_CAP_VHT_LINK_ADAPTATION_VHT_MRQ_MFB ((u32) BIT(26) | BIT(27)) +#define VHT_CAP_RX_ANTENNA_PATTERN ((u32) BIT(28)) +#define VHT_CAP_TX_ANTENNA_PATTERN ((u32) BIT(29)) + +/* VHT channel widths */ +#define VHT_CHANWIDTH_USE_HT 0 +#define VHT_CHANWIDTH_80MHZ 1 +#define VHT_CHANWIDTH_160MHZ 2 +#define VHT_CHANWIDTH_80P80MHZ 3 + #define OUI_MICROSOFT 0x0050f2 /* Microsoft (also used in Wi-Fi specs) * 00:50:F2 */ #define WPA_IE_VENDOR_TYPE 0x0050f201 #define WPS_IE_VENDOR_TYPE 0x0050f204 #define OUI_WFA 0x506f9a #define P2P_IE_VENDOR_TYPE 0x506f9a09 +#define WFD_IE_VENDOR_TYPE 0x506f9a0a +#define WFD_OUI_TYPE 10 +#define HS20_IE_VENDOR_TYPE 0x506f9a10 #define WMM_OUI_TYPE 2 #define WMM_OUI_SUBTYPE_INFORMATION_ELEMENT 0 @@ -677,6 +817,10 @@ } STRUCT_PACKED; +#define WMM_QOSINFO_STA_AC_MASK 0x0f +#define WMM_QOSINFO_STA_SP_MASK 0x03 +#define WMM_QOSINFO_STA_SP_SHIFT 5 + #define WMM_AC_AIFSN_MASK 0x0f #define WMM_AC_AIFNS_SHIFT 0 #define WMM_AC_ACM 0x10 @@ -748,6 +892,16 @@ }; +#define HS20_INDICATION_OUI_TYPE 16 +#define HS20_ANQP_OUI_TYPE 17 +#define HS20_STYPE_QUERY_LIST 1 +#define HS20_STYPE_CAPABILITY_LIST 2 +#define HS20_STYPE_OPERATOR_FRIENDLY_NAME 3 +#define HS20_STYPE_WAN_METRICS 4 +#define HS20_STYPE_CONNECTION_CAPABILITY 5 +#define HS20_STYPE_NAI_HOME_REALM_QUERY 6 +#define HS20_STYPE_OPERATING_CLASS 7 + /* Wi-Fi Direct (P2P) */ #define P2P_OUI_TYPE 9 @@ -772,6 +926,7 @@ P2P_ATTR_INTERFACE = 16, P2P_ATTR_OPERATING_CHANNEL = 17, P2P_ATTR_INVITATION_FLAGS = 18, + P2P_ATTR_OOB_GO_NEG_CHANNEL = 19, P2P_ATTR_VENDOR_SPECIFIC = 221 }; @@ -793,6 +948,7 @@ #define P2P_GROUP_CAPAB_CROSS_CONN BIT(4) #define P2P_GROUP_CAPAB_PERSISTENT_RECONN BIT(5) #define P2P_GROUP_CAPAB_GROUP_FORMATION BIT(6) +#define P2P_GROUP_CAPAB_IP_ADDR_ALLOCATION BIT(7) /* Invitation Flags */ #define P2P_INVITATION_FLAGS_TYPE BIT(0) @@ -817,6 +973,12 @@ P2P_SC_FAIL_REJECTED_BY_USER = 11, }; +enum p2p_role_indication { + P2P_DEVICE_NOT_IN_GROUP = 0x00, + P2P_CLIENT_IN_A_GROUP = 0x01, + P2P_GO_IN_A_GROUP = 0x02, +}; + #define P2P_WILDCARD_SSID "DIRECT-" #define P2P_WILDCARD_SSID_LEN 7 @@ -846,6 +1008,7 @@ P2P_SERV_BONJOUR = 1, P2P_SERV_UPNP = 2, P2P_SERV_WS_DISCOVERY = 3, + P2P_SERV_WIFI_DISPLAY = 4, P2P_SERV_VENDOR_SPECIFIC = 255 }; @@ -857,6 +1020,20 @@ }; +enum wifi_display_subelem { + WFD_SUBELEM_DEVICE_INFO = 0, + WFD_SUBELEM_ASSOCIATED_BSSID = 1, + WFD_SUBELEM_AUDIO_FORMATS = 2, + WFD_SUBELEM_VIDEO_FORMATS = 3, + WFD_SUBELEM_3D_VIDEO_FORMATS = 4, + WFD_SUBELEM_CONTENT_PROTECTION = 5, + WFD_SUBELEM_COUPLED_SINK = 6, + WFD_SUBELEM_EXT_CAPAB = 7, + WFD_SUBELEM_LOCAL_IP_ADDRESS = 8, + WFD_SUBELEM_SESSION_INFO = 9 +}; + + #define OUI_BROADCOM 0x00904c /* Broadcom (Epigram) */ #define VENDOR_HT_CAPAB_OUI_TYPE 0x33 /* 00-90-4c:0x33 */ @@ -869,10 +1046,27 @@ #define WLAN_CIPHER_SUITE_CCMP 0x000FAC04 #define WLAN_CIPHER_SUITE_WEP104 0x000FAC05 #define WLAN_CIPHER_SUITE_AES_CMAC 0x000FAC06 +#define WLAN_CIPHER_SUITE_NO_GROUP_ADDR 0x000FAC07 +#define WLAN_CIPHER_SUITE_GCMP 0x000FAC08 +#define WLAN_CIPHER_SUITE_GCMP_256 0x000FAC09 +#define WLAN_CIPHER_SUITE_CCMP_256 0x000FAC0A +#define WLAN_CIPHER_SUITE_BIP_GMAC_128 0x000FAC0B +#define WLAN_CIPHER_SUITE_BIP_GMAC_256 0x000FAC0C +#define WLAN_CIPHER_SUITE_BIP_CMAC_256 0x000FAC0D + +#define WLAN_CIPHER_SUITE_SMS4 0x00147201 + +#define WLAN_CIPHER_SUITE_CKIP 0x00409600 +#define WLAN_CIPHER_SUITE_CKIP_CMIC 0x00409601 +#define WLAN_CIPHER_SUITE_CMIC 0x00409602 +#define WLAN_CIPHER_SUITE_KRK 0x004096FF /* for nl80211 use only */ /* AKM suite selectors */ #define WLAN_AKM_SUITE_8021X 0x000FAC01 #define WLAN_AKM_SUITE_PSK 0x000FAC02 +#define WLAN_AKM_SUITE_FT_8021X 0x000FAC03 +#define WLAN_AKM_SUITE_FT_PSK 0x000FAC04 +#define WLAN_AKM_SUITE_CCKM 0x00409600 /* IEEE 802.11v - WNM Action field values */ @@ -914,4 +1108,86 @@ #define WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED BIT(3) #define WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT BIT(4) +/* IEEE Std 802.11-2012 - Table 8-253 */ +enum bss_trans_mgmt_status_code { + WNM_BSS_TM_ACCEPT = 0, + WNM_BSS_TM_REJECT_UNSPECIFIED = 1, + WNM_BSS_TM_REJECT_INSUFFICIENT_BEACON = 2, + WNM_BSS_TM_REJECT_INSUFFICIENT_CAPABITY = 3, + WNM_BSS_TM_REJECT_UNDESIRED = 4, + WNM_BSS_TM_REJECT_DELAY_REQUEST = 5, + WNM_BSS_TM_REJECT_STA_CANDIDATE_LIST_PROVIDED = 6, + WNM_BSS_TM_REJECT_NO_SUITABLE_CANDIDATES = 7, + WNM_BSS_TM_REJECT_LEAVING_ESS = 8 +}; + +#define WNM_NEIGHBOR_TSF 1 +#define WNM_NEIGHBOR_CONDENSED_COUNTRY_STRING 2 +#define WNM_NEIGHBOR_BSS_TRANSITION_CANDIDATE 3 +#define WNM_NEIGHBOR_BSS_TERMINATION_DURATION 4 +#define WNM_NEIGHBOR_BEARING 5 +#define WNM_NEIGHBOR_MEASUREMENT_PILOT 66 +#define WNM_NEIGHBOR_RRM_ENABLED_CAPABILITIES 70 +#define WNM_NEIGHBOR_MULTIPLE_BSSID 71 + +/* QoS action */ +enum qos_action { + QOS_ADDTS_REQ = 0, + QOS_ADDTS_RESP = 1, + QOS_DELTS = 2, + QOS_SCHEDULE = 3, + QOS_QOS_MAP_CONFIG = 4, +}; + +/* IEEE Std 802.11-2012, 8.4.2.62 20/40 BSS Coexistence element */ +#define WLAN_20_40_BSS_COEX_INFO_REQ BIT(0) +#define WLAN_20_40_BSS_COEX_40MHZ_INTOL BIT(1) +#define WLAN_20_40_BSS_COEX_20MHZ_WIDTH_REQ BIT(2) +#define WLAN_20_40_BSS_COEX_OBSS_EXEMPT_REQ BIT(3) +#define WLAN_20_40_BSS_COEX_OBSS_EXEMPT_GRNT BIT(4) + +struct ieee80211_2040_bss_coex_ie { + u8 element_id; + u8 length; + u8 coex_param; +} STRUCT_PACKED; + +struct ieee80211_2040_intol_chan_report { + u8 element_id; + u8 length; + u8 op_class; + u8 variable[0]; /* Channel List */ +} STRUCT_PACKED; + +/* IEEE 802.11v - WNM-Sleep Mode element */ +struct wnm_sleep_element { + u8 eid; /* WLAN_EID_WNMSLEEP */ + u8 len; + u8 action_type; /* WNM_SLEEP_ENTER/WNM_SLEEP_MODE_EXIT */ + u8 status; + le16 intval; +} STRUCT_PACKED; + +#define WNM_SLEEP_MODE_ENTER 0 +#define WNM_SLEEP_MODE_EXIT 1 + +enum wnm_sleep_mode_response_status { + WNM_STATUS_SLEEP_ACCEPT = 0, + WNM_STATUS_SLEEP_EXIT_ACCEPT_GTK_UPDATE = 1, + WNM_STATUS_DENIED_ACTION = 2, + WNM_STATUS_DENIED_TMP = 3, + WNM_STATUS_DENIED_KEY = 4, + WNM_STATUS_DENIED_OTHER_WNM_SERVICE = 5 +}; + +/* WNM-Sleep Mode subelement IDs */ +enum wnm_sleep_mode_subelement_id { + WNM_SLEEP_SUBELEM_GTK = 0, + WNM_SLEEP_SUBELEM_IGTK = 1 +}; + +/* Channel Switch modes (802.11h) */ +#define CHAN_SWITCH_MODE_ALLOW_TX 0 +#define CHAN_SWITCH_MODE_BLOCK_TX 1 + #endif /* IEEE802_11_DEFS_H */ diff -Nru wpa-1.0/src/common/Makefile wpa-2.1/src/common/Makefile --- wpa-1.0/src/common/Makefile 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/common/Makefile 2014-02-04 11:23:35.000000000 +0000 @@ -2,7 +2,7 @@ @echo Nothing to be made. clean: - rm -f *~ *.o *.d + rm -f *~ *.o *.d *.gcno *.gcda *.gcov install: @echo Nothing to be made. diff -Nru wpa-1.0/src/common/privsep_commands.h wpa-2.1/src/common/privsep_commands.h --- wpa-1.0/src/common/privsep_commands.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/common/privsep_commands.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * WPA Supplicant - privilege separation commands * Copyright (c) 2007-2009, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef PRIVSEP_COMMANDS_H diff -Nru wpa-1.0/src/common/qca-vendor.h wpa-2.1/src/common/qca-vendor.h --- wpa-1.0/src/common/qca-vendor.h 1970-01-01 00:00:00.000000000 +0000 +++ wpa-2.1/src/common/qca-vendor.h 2014-02-04 11:23:35.000000000 +0000 @@ -0,0 +1,51 @@ +/* + * Qualcomm Atheros OUI and vendor specific assignments + * Copyright (c) 2014, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef QCA_VENDOR_H +#define QCA_VENDOR_H + +/* + * This file is a registry of identifier assignments from the Qualcomm Atheros + * OUI 00:13:74 for purposes other than MAC address assignment. New identifiers + * can be assigned through normal review process for changes to the upstream + * hostap.git repository. + */ + +#define OUI_QCA 0x001374 + +/** + * enum qca_nl80211_vendor_subcmds - QCA nl80211 vendor command identifiers + * + * @QCA_NL80211_VENDOR_SUBCMD_UNSPEC: Reserved value 0 + * + * @QCA_NL80211_VENDOR_SUBCMD_TEST: Test command/event + * + * @QCA_NL80211_VENDOR_SUBCMD_AVOID_FREQUENCY: Recommendation of frequency + * ranges to avoid to reduce issues due to interference or internal + * co-existence information in the driver. The event data structure is + * defined in struct qca_avoid_freq_list. + */ +enum qca_nl80211_vendor_subcmds { + QCA_NL80211_VENDOR_SUBCMD_UNSPEC = 0, + QCA_NL80211_VENDOR_SUBCMD_TEST = 1, + /* subcmds 2..9 not yet allocated */ + QCA_NL80211_VENDOR_SUBCMD_AVOID_FREQUENCY = 10, +}; + + +struct qca_avoid_freq_range { + u32 start_freq; + u32 end_freq; +} STRUCT_PACKED; + +struct qca_avoid_freq_list { + u32 count; + struct qca_avoid_freq_range range[0]; +} STRUCT_PACKED; + +#endif /* QCA_VENDOR_H */ diff -Nru wpa-1.0/src/common/sae.c wpa-2.1/src/common/sae.c --- wpa-1.0/src/common/sae.c 1970-01-01 00:00:00.000000000 +0000 +++ wpa-2.1/src/common/sae.c 2014-02-04 11:23:35.000000000 +0000 @@ -0,0 +1,1047 @@ +/* + * Simultaneous authentication of equals + * Copyright (c) 2012-2013, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "crypto/crypto.h" +#include "crypto/sha256.h" +#include "crypto/random.h" +#include "crypto/dh_groups.h" +#include "ieee802_11_defs.h" +#include "sae.h" + + +int sae_set_group(struct sae_data *sae, int group) +{ + struct sae_temporary_data *tmp; + + sae_clear_data(sae); + tmp = sae->tmp = os_zalloc(sizeof(*tmp)); + if (tmp == NULL) + return -1; + + /* First, check if this is an ECC group */ + tmp->ec = crypto_ec_init(group); + if (tmp->ec) { + sae->group = group; + tmp->prime_len = crypto_ec_prime_len(tmp->ec); + tmp->prime = crypto_ec_get_prime(tmp->ec); + tmp->order = crypto_ec_get_order(tmp->ec); + return 0; + } + + /* Not an ECC group, check FFC */ + tmp->dh = dh_groups_get(group); + if (tmp->dh) { + sae->group = group; + tmp->prime_len = tmp->dh->prime_len; + if (tmp->prime_len > SAE_MAX_PRIME_LEN) { + sae_clear_data(sae); + return -1; + } + + tmp->prime_buf = crypto_bignum_init_set(tmp->dh->prime, + tmp->prime_len); + if (tmp->prime_buf == NULL) { + sae_clear_data(sae); + return -1; + } + tmp->prime = tmp->prime_buf; + + tmp->order_buf = crypto_bignum_init_set(tmp->dh->order, + tmp->dh->order_len); + if (tmp->order_buf == NULL) { + sae_clear_data(sae); + return -1; + } + tmp->order = tmp->order_buf; + + return 0; + } + + /* Unsupported group */ + return -1; +} + + +void sae_clear_temp_data(struct sae_data *sae) +{ + struct sae_temporary_data *tmp; + if (sae == NULL || sae->tmp == NULL) + return; + tmp = sae->tmp; + crypto_ec_deinit(tmp->ec); + crypto_bignum_deinit(tmp->prime_buf, 0); + crypto_bignum_deinit(tmp->order_buf, 0); + crypto_bignum_deinit(tmp->sae_rand, 1); + crypto_bignum_deinit(tmp->pwe_ffc, 1); + crypto_bignum_deinit(tmp->own_commit_scalar, 0); + crypto_bignum_deinit(tmp->own_commit_element_ffc, 0); + crypto_bignum_deinit(tmp->peer_commit_element_ffc, 0); + crypto_ec_point_deinit(tmp->pwe_ecc, 1); + crypto_ec_point_deinit(tmp->own_commit_element_ecc, 0); + crypto_ec_point_deinit(tmp->peer_commit_element_ecc, 0); + os_free(sae->tmp); + sae->tmp = NULL; +} + + +void sae_clear_data(struct sae_data *sae) +{ + if (sae == NULL) + return; + sae_clear_temp_data(sae); + crypto_bignum_deinit(sae->peer_commit_scalar, 0); + os_memset(sae, 0, sizeof(*sae)); +} + + +static void buf_shift_right(u8 *buf, size_t len, size_t bits) +{ + size_t i; + for (i = len - 1; i > 0; i--) + buf[i] = (buf[i - 1] << (8 - bits)) | (buf[i] >> bits); + buf[0] >>= bits; +} + + +static struct crypto_bignum * sae_get_rand(struct sae_data *sae) +{ + u8 val[SAE_MAX_PRIME_LEN]; + int iter = 0; + struct crypto_bignum *bn = NULL; + int order_len_bits = crypto_bignum_bits(sae->tmp->order); + size_t order_len = (order_len_bits + 7) / 8; + + if (order_len > sizeof(val)) + return NULL; + + for (;;) { + if (iter++ > 100) + return NULL; + if (random_get_bytes(val, order_len) < 0) + return NULL; + if (order_len_bits % 8) + buf_shift_right(val, order_len, 8 - order_len_bits % 8); + bn = crypto_bignum_init_set(val, order_len); + if (bn == NULL) + return NULL; + if (crypto_bignum_is_zero(bn) || + crypto_bignum_is_one(bn) || + crypto_bignum_cmp(bn, sae->tmp->order) >= 0) + continue; + break; + } + + os_memset(val, 0, order_len); + return bn; +} + + +static struct crypto_bignum * sae_get_rand_and_mask(struct sae_data *sae) +{ + crypto_bignum_deinit(sae->tmp->sae_rand, 1); + sae->tmp->sae_rand = sae_get_rand(sae); + if (sae->tmp->sae_rand == NULL) + return NULL; + return sae_get_rand(sae); +} + + +static void sae_pwd_seed_key(const u8 *addr1, const u8 *addr2, u8 *key) +{ + wpa_printf(MSG_DEBUG, "SAE: PWE derivation - addr1=" MACSTR + " addr2=" MACSTR, MAC2STR(addr1), MAC2STR(addr2)); + if (os_memcmp(addr1, addr2, ETH_ALEN) > 0) { + os_memcpy(key, addr1, ETH_ALEN); + os_memcpy(key + ETH_ALEN, addr2, ETH_ALEN); + } else { + os_memcpy(key, addr2, ETH_ALEN); + os_memcpy(key + ETH_ALEN, addr1, ETH_ALEN); + } +} + + +static int sae_test_pwd_seed_ecc(struct sae_data *sae, const u8 *pwd_seed, + struct crypto_ec_point *pwe) +{ + u8 pwd_value[SAE_MAX_ECC_PRIME_LEN], prime[SAE_MAX_ECC_PRIME_LEN]; + struct crypto_bignum *x; + int y_bit; + size_t bits; + + if (crypto_bignum_to_bin(sae->tmp->prime, prime, sizeof(prime), + sae->tmp->prime_len) < 0) + return -1; + + wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-seed", pwd_seed, SHA256_MAC_LEN); + + /* pwd-value = KDF-z(pwd-seed, "SAE Hunting and Pecking", p) */ + bits = crypto_ec_prime_len_bits(sae->tmp->ec); + sha256_prf_bits(pwd_seed, SHA256_MAC_LEN, "SAE Hunting and Pecking", + prime, sae->tmp->prime_len, pwd_value, bits); + if (bits % 8) + buf_shift_right(pwd_value, sizeof(pwd_value), 8 - bits % 8); + wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-value", + pwd_value, sae->tmp->prime_len); + + if (os_memcmp(pwd_value, prime, sae->tmp->prime_len) >= 0) + return 0; + + y_bit = pwd_seed[SHA256_MAC_LEN - 1] & 0x01; + + x = crypto_bignum_init_set(pwd_value, sae->tmp->prime_len); + if (x == NULL) + return -1; + if (crypto_ec_point_solve_y_coord(sae->tmp->ec, pwe, x, y_bit) < 0) { + crypto_bignum_deinit(x, 0); + wpa_printf(MSG_DEBUG, "SAE: No solution found"); + return 0; + } + crypto_bignum_deinit(x, 0); + + wpa_printf(MSG_DEBUG, "SAE: PWE found"); + + return 1; +} + + +static int sae_test_pwd_seed_ffc(struct sae_data *sae, const u8 *pwd_seed, + struct crypto_bignum *pwe) +{ + u8 pwd_value[SAE_MAX_PRIME_LEN]; + size_t bits = sae->tmp->prime_len * 8; + u8 exp[1]; + struct crypto_bignum *a, *b; + int res; + + wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-seed", pwd_seed, SHA256_MAC_LEN); + + /* pwd-value = KDF-z(pwd-seed, "SAE Hunting and Pecking", p) */ + sha256_prf_bits(pwd_seed, SHA256_MAC_LEN, "SAE Hunting and Pecking", + sae->tmp->dh->prime, sae->tmp->prime_len, pwd_value, + bits); + if (bits % 8) + buf_shift_right(pwd_value, sizeof(pwd_value), 8 - bits % 8); + wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-value", pwd_value, + sae->tmp->prime_len); + + if (os_memcmp(pwd_value, sae->tmp->dh->prime, sae->tmp->prime_len) >= 0) + { + wpa_printf(MSG_DEBUG, "SAE: pwd-value >= p"); + return 0; + } + + /* PWE = pwd-value^((p-1)/r) modulo p */ + + a = crypto_bignum_init_set(pwd_value, sae->tmp->prime_len); + + if (sae->tmp->dh->safe_prime) { + /* + * r = (p-1)/2 for the group used here, so this becomes: + * PWE = pwd-value^2 modulo p + */ + exp[0] = 2; + b = crypto_bignum_init_set(exp, sizeof(exp)); + } else { + /* Calculate exponent: (p-1)/r */ + exp[0] = 1; + b = crypto_bignum_init_set(exp, sizeof(exp)); + if (b == NULL || + crypto_bignum_sub(sae->tmp->prime, b, b) < 0 || + crypto_bignum_div(b, sae->tmp->order, b) < 0) { + crypto_bignum_deinit(b, 0); + b = NULL; + } + } + + if (a == NULL || b == NULL) + res = -1; + else + res = crypto_bignum_exptmod(a, b, sae->tmp->prime, pwe); + + crypto_bignum_deinit(a, 0); + crypto_bignum_deinit(b, 0); + + if (res < 0) { + wpa_printf(MSG_DEBUG, "SAE: Failed to calculate PWE"); + return -1; + } + + /* if (PWE > 1) --> found */ + if (crypto_bignum_is_zero(pwe) || crypto_bignum_is_one(pwe)) { + wpa_printf(MSG_DEBUG, "SAE: PWE <= 1"); + return 0; + } + + wpa_printf(MSG_DEBUG, "SAE: PWE found"); + return 1; +} + + +static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1, + const u8 *addr2, const u8 *password, + size_t password_len) +{ + u8 counter, k = 4; + u8 addrs[2 * ETH_ALEN]; + const u8 *addr[2]; + size_t len[2]; + int found = 0; + struct crypto_ec_point *pwe_tmp; + + if (sae->tmp->pwe_ecc == NULL) { + sae->tmp->pwe_ecc = crypto_ec_point_init(sae->tmp->ec); + if (sae->tmp->pwe_ecc == NULL) + return -1; + } + pwe_tmp = crypto_ec_point_init(sae->tmp->ec); + if (pwe_tmp == NULL) + return -1; + + wpa_hexdump_ascii_key(MSG_DEBUG, "SAE: password", + password, password_len); + + /* + * H(salt, ikm) = HMAC-SHA256(salt, ikm) + * pwd-seed = H(MAX(STA-A-MAC, STA-B-MAC) || MIN(STA-A-MAC, STA-B-MAC), + * password || counter) + */ + sae_pwd_seed_key(addr1, addr2, addrs); + + addr[0] = password; + len[0] = password_len; + addr[1] = &counter; + len[1] = sizeof(counter); + + /* + * Continue for at least k iterations to protect against side-channel + * attacks that attempt to determine the number of iterations required + * in the loop. + */ + for (counter = 1; counter < k || !found; counter++) { + u8 pwd_seed[SHA256_MAC_LEN]; + int res; + + if (counter > 200) { + /* This should not happen in practice */ + wpa_printf(MSG_DEBUG, "SAE: Failed to derive PWE"); + break; + } + + wpa_printf(MSG_DEBUG, "SAE: counter = %u", counter); + if (hmac_sha256_vector(addrs, sizeof(addrs), 2, addr, len, + pwd_seed) < 0) + break; + res = sae_test_pwd_seed_ecc(sae, pwd_seed, + found ? pwe_tmp : + sae->tmp->pwe_ecc); + if (res < 0) + break; + if (res == 0) + continue; + if (found) { + wpa_printf(MSG_DEBUG, "SAE: Ignore this PWE (one was " + "already selected)"); + } else { + wpa_printf(MSG_DEBUG, "SAE: Use this PWE"); + found = 1; + } + } + + crypto_ec_point_deinit(pwe_tmp, 1); + + return found ? 0 : -1; +} + + +static int sae_derive_pwe_ffc(struct sae_data *sae, const u8 *addr1, + const u8 *addr2, const u8 *password, + size_t password_len) +{ + u8 counter; + u8 addrs[2 * ETH_ALEN]; + const u8 *addr[2]; + size_t len[2]; + int found = 0; + + if (sae->tmp->pwe_ffc == NULL) { + sae->tmp->pwe_ffc = crypto_bignum_init(); + if (sae->tmp->pwe_ffc == NULL) + return -1; + } + + wpa_hexdump_ascii_key(MSG_DEBUG, "SAE: password", + password, password_len); + + /* + * H(salt, ikm) = HMAC-SHA256(salt, ikm) + * pwd-seed = H(MAX(STA-A-MAC, STA-B-MAC) || MIN(STA-A-MAC, STA-B-MAC), + * password || counter) + */ + sae_pwd_seed_key(addr1, addr2, addrs); + + addr[0] = password; + len[0] = password_len; + addr[1] = &counter; + len[1] = sizeof(counter); + + for (counter = 1; !found; counter++) { + u8 pwd_seed[SHA256_MAC_LEN]; + int res; + + if (counter > 200) { + /* This should not happen in practice */ + wpa_printf(MSG_DEBUG, "SAE: Failed to derive PWE"); + break; + } + + wpa_printf(MSG_DEBUG, "SAE: counter = %u", counter); + if (hmac_sha256_vector(addrs, sizeof(addrs), 2, addr, len, + pwd_seed) < 0) + break; + res = sae_test_pwd_seed_ffc(sae, pwd_seed, sae->tmp->pwe_ffc); + if (res < 0) + break; + if (res > 0) { + wpa_printf(MSG_DEBUG, "SAE: Use this PWE"); + found = 1; + } + } + + return found ? 0 : -1; +} + + +static int sae_derive_commit_element_ecc(struct sae_data *sae, + struct crypto_bignum *mask) +{ + /* COMMIT-ELEMENT = inverse(scalar-op(mask, PWE)) */ + if (!sae->tmp->own_commit_element_ecc) { + sae->tmp->own_commit_element_ecc = + crypto_ec_point_init(sae->tmp->ec); + if (!sae->tmp->own_commit_element_ecc) + return -1; + } + + if (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) { + wpa_printf(MSG_DEBUG, "SAE: Could not compute commit-element"); + return -1; + } + + return 0; +} + + +static int sae_derive_commit_element_ffc(struct sae_data *sae, + struct crypto_bignum *mask) +{ + /* COMMIT-ELEMENT = inverse(scalar-op(mask, PWE)) */ + if (!sae->tmp->own_commit_element_ffc) { + sae->tmp->own_commit_element_ffc = crypto_bignum_init(); + if (!sae->tmp->own_commit_element_ffc) + return -1; + } + + if (crypto_bignum_exptmod(sae->tmp->pwe_ffc, mask, sae->tmp->prime, + sae->tmp->own_commit_element_ffc) < 0 || + crypto_bignum_inverse(sae->tmp->own_commit_element_ffc, + sae->tmp->prime, + sae->tmp->own_commit_element_ffc) < 0) { + wpa_printf(MSG_DEBUG, "SAE: Could not compute commit-element"); + return -1; + } + + return 0; +} + + +static int sae_derive_commit(struct sae_data *sae) +{ + struct crypto_bignum *mask; + int ret = -1; + + mask = sae_get_rand_and_mask(sae); + if (mask == NULL) { + wpa_printf(MSG_DEBUG, "SAE: Could not get rand/mask"); + return -1; + } + + /* commit-scalar = (rand + mask) modulo r */ + if (!sae->tmp->own_commit_scalar) { + sae->tmp->own_commit_scalar = crypto_bignum_init(); + if (!sae->tmp->own_commit_scalar) + goto fail; + } + crypto_bignum_add(sae->tmp->sae_rand, mask, + sae->tmp->own_commit_scalar); + crypto_bignum_mod(sae->tmp->own_commit_scalar, sae->tmp->order, + sae->tmp->own_commit_scalar); + + if (sae->tmp->ec && sae_derive_commit_element_ecc(sae, mask) < 0) + goto fail; + if (sae->tmp->dh && sae_derive_commit_element_ffc(sae, mask) < 0) + goto fail; + + ret = 0; +fail: + crypto_bignum_deinit(mask, 1); + return ret; +} + + +int sae_prepare_commit(const u8 *addr1, const u8 *addr2, + const u8 *password, size_t password_len, + struct sae_data *sae) +{ + if (sae->tmp->ec && sae_derive_pwe_ecc(sae, addr1, addr2, password, + password_len) < 0) + return -1; + if (sae->tmp->dh && sae_derive_pwe_ffc(sae, addr1, addr2, password, + password_len) < 0) + return -1; + if (sae_derive_commit(sae) < 0) + return -1; + return 0; +} + + +static int sae_derive_k_ecc(struct sae_data *sae, u8 *k) +{ + struct crypto_ec_point *K; + int ret = -1; + + K = crypto_ec_point_init(sae->tmp->ec); + if (K == NULL) + goto fail; + + /* + * K = scalar-op(rand, (elem-op(scalar-op(peer-commit-scalar, PWE), + * PEER-COMMIT-ELEMENT))) + * If K is identity element (point-at-infinity), reject + * k = F(K) (= x coordinate) + */ + + if (crypto_ec_point_mul(sae->tmp->ec, sae->tmp->pwe_ecc, + sae->peer_commit_scalar, K) < 0 || + crypto_ec_point_add(sae->tmp->ec, K, + sae->tmp->peer_commit_element_ecc, K) < 0 || + crypto_ec_point_mul(sae->tmp->ec, K, sae->tmp->sae_rand, K) < 0 || + crypto_ec_point_is_at_infinity(sae->tmp->ec, K) || + crypto_ec_point_to_bin(sae->tmp->ec, K, k, NULL) < 0) { + wpa_printf(MSG_DEBUG, "SAE: Failed to calculate K and k"); + goto fail; + } + + wpa_hexdump_key(MSG_DEBUG, "SAE: k", k, sae->tmp->prime_len); + + ret = 0; +fail: + crypto_ec_point_deinit(K, 1); + return ret; +} + + +static int sae_derive_k_ffc(struct sae_data *sae, u8 *k) +{ + struct crypto_bignum *K; + int ret = -1; + + K = crypto_bignum_init(); + if (K == NULL) + goto fail; + + /* + * K = scalar-op(rand, (elem-op(scalar-op(peer-commit-scalar, PWE), + * PEER-COMMIT-ELEMENT))) + * If K is identity element (one), reject. + * k = F(K) (= x coordinate) + */ + + if (crypto_bignum_exptmod(sae->tmp->pwe_ffc, sae->peer_commit_scalar, + sae->tmp->prime, K) < 0 || + crypto_bignum_mulmod(K, sae->tmp->peer_commit_element_ffc, + sae->tmp->prime, K) < 0 || + crypto_bignum_exptmod(K, sae->tmp->sae_rand, sae->tmp->prime, K) < 0 + || + crypto_bignum_is_one(K) || + crypto_bignum_to_bin(K, k, SAE_MAX_PRIME_LEN, sae->tmp->prime_len) < + 0) { + wpa_printf(MSG_DEBUG, "SAE: Failed to calculate K and k"); + goto fail; + } + + wpa_hexdump_key(MSG_DEBUG, "SAE: k", k, sae->tmp->prime_len); + + ret = 0; +fail: + crypto_bignum_deinit(K, 1); + return ret; +} + + +static int sae_derive_keys(struct sae_data *sae, const u8 *k) +{ + u8 null_key[SAE_KEYSEED_KEY_LEN], val[SAE_MAX_PRIME_LEN]; + u8 keyseed[SHA256_MAC_LEN]; + u8 keys[SAE_KCK_LEN + SAE_PMK_LEN]; + struct crypto_bignum *tmp; + int ret = -1; + + tmp = crypto_bignum_init(); + if (tmp == NULL) + goto fail; + + /* keyseed = H(<0>32, k) + * KCK || PMK = KDF-512(keyseed, "SAE KCK and PMK", + * (commit-scalar + peer-commit-scalar) modulo r) + * PMKID = L((commit-scalar + peer-commit-scalar) modulo r, 0, 128) + */ + + os_memset(null_key, 0, sizeof(null_key)); + hmac_sha256(null_key, sizeof(null_key), k, sae->tmp->prime_len, + keyseed); + wpa_hexdump_key(MSG_DEBUG, "SAE: keyseed", keyseed, sizeof(keyseed)); + + crypto_bignum_add(sae->tmp->own_commit_scalar, sae->peer_commit_scalar, + tmp); + crypto_bignum_mod(tmp, sae->tmp->order, tmp); + crypto_bignum_to_bin(tmp, val, sizeof(val), sae->tmp->prime_len); + wpa_hexdump(MSG_DEBUG, "SAE: PMKID", val, SAE_PMKID_LEN); + sha256_prf(keyseed, sizeof(keyseed), "SAE KCK and PMK", + val, sae->tmp->prime_len, keys, sizeof(keys)); + os_memcpy(sae->tmp->kck, keys, SAE_KCK_LEN); + os_memcpy(sae->pmk, keys + SAE_KCK_LEN, SAE_PMK_LEN); + wpa_hexdump_key(MSG_DEBUG, "SAE: KCK", sae->tmp->kck, SAE_KCK_LEN); + wpa_hexdump_key(MSG_DEBUG, "SAE: PMK", sae->pmk, SAE_PMK_LEN); + + ret = 0; +fail: + crypto_bignum_deinit(tmp, 0); + return ret; +} + + +int sae_process_commit(struct sae_data *sae) +{ + u8 k[SAE_MAX_PRIME_LEN]; + if ((sae->tmp->ec && sae_derive_k_ecc(sae, k) < 0) || + (sae->tmp->dh && sae_derive_k_ffc(sae, k) < 0) || + sae_derive_keys(sae, k) < 0) + return -1; + return 0; +} + + +void sae_write_commit(struct sae_data *sae, struct wpabuf *buf, + const struct wpabuf *token) +{ + u8 *pos; + wpabuf_put_le16(buf, sae->group); /* Finite Cyclic Group */ + if (token) + wpabuf_put_buf(buf, token); + pos = wpabuf_put(buf, sae->tmp->prime_len); + crypto_bignum_to_bin(sae->tmp->own_commit_scalar, pos, + sae->tmp->prime_len, sae->tmp->prime_len); + wpa_hexdump(MSG_DEBUG, "SAE: own commit-scalar", + pos, sae->tmp->prime_len); + if (sae->tmp->ec) { + pos = wpabuf_put(buf, 2 * sae->tmp->prime_len); + crypto_ec_point_to_bin(sae->tmp->ec, + sae->tmp->own_commit_element_ecc, + pos, pos + sae->tmp->prime_len); + wpa_hexdump(MSG_DEBUG, "SAE: own commit-element(x)", + pos, sae->tmp->prime_len); + wpa_hexdump(MSG_DEBUG, "SAE: own commit-element(y)", + pos + sae->tmp->prime_len, sae->tmp->prime_len); + } else { + pos = wpabuf_put(buf, sae->tmp->prime_len); + crypto_bignum_to_bin(sae->tmp->own_commit_element_ffc, pos, + sae->tmp->prime_len, sae->tmp->prime_len); + wpa_hexdump(MSG_DEBUG, "SAE: own commit-element", + pos, sae->tmp->prime_len); + } +} + + +static u16 sae_group_allowed(struct sae_data *sae, int *allowed_groups, + u16 group) +{ + if (allowed_groups) { + int i; + for (i = 0; allowed_groups[i] > 0; i++) { + if (allowed_groups[i] == group) + break; + } + if (allowed_groups[i] != group) { + wpa_printf(MSG_DEBUG, "SAE: Proposed group %u not " + "enabled in the current configuration", + group); + return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED; + } + } + + if (sae->state == SAE_COMMITTED && group != sae->group) { + wpa_printf(MSG_DEBUG, "SAE: Do not allow group to be changed"); + return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED; + } + + if (group != sae->group && sae_set_group(sae, group) < 0) { + wpa_printf(MSG_DEBUG, "SAE: Unsupported Finite Cyclic Group %u", + group); + return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED; + } + + if (sae->tmp == NULL) { + wpa_printf(MSG_DEBUG, "SAE: Group information not yet initialized"); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + if (sae->tmp->dh && !allowed_groups) { + wpa_printf(MSG_DEBUG, "SAE: Do not allow FFC group %u without " + "explicit configuration enabling it", group); + return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED; + } + + return WLAN_STATUS_SUCCESS; +} + + +static void sae_parse_commit_token(struct sae_data *sae, const u8 **pos, + const u8 *end, const u8 **token, + size_t *token_len) +{ + if (*pos + (sae->tmp->ec ? 3 : 2) * sae->tmp->prime_len < end) { + size_t tlen = end - (*pos + (sae->tmp->ec ? 3 : 2) * + sae->tmp->prime_len); + wpa_hexdump(MSG_DEBUG, "SAE: Anti-Clogging Token", *pos, tlen); + if (token) + *token = *pos; + if (token_len) + *token_len = tlen; + *pos += tlen; + } else { + if (token) + *token = NULL; + if (token_len) + *token_len = 0; + } +} + + +static u16 sae_parse_commit_scalar(struct sae_data *sae, const u8 **pos, + const u8 *end) +{ + struct crypto_bignum *peer_scalar; + + if (*pos + sae->tmp->prime_len > end) { + wpa_printf(MSG_DEBUG, "SAE: Not enough data for scalar"); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + peer_scalar = crypto_bignum_init_set(*pos, sae->tmp->prime_len); + if (peer_scalar == NULL) + return WLAN_STATUS_UNSPECIFIED_FAILURE; + + /* + * IEEE Std 802.11-2012, 11.3.8.6.1: If there is a protocol instance for + * the peer and it is in Authenticated state, the new Commit Message + * shall be dropped if the peer-scalar is identical to the one used in + * the existing protocol instance. + */ + if (sae->state == SAE_ACCEPTED && sae->peer_commit_scalar && + crypto_bignum_cmp(sae->peer_commit_scalar, peer_scalar) == 0) { + wpa_printf(MSG_DEBUG, "SAE: Do not accept re-use of previous " + "peer-commit-scalar"); + crypto_bignum_deinit(peer_scalar, 0); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + /* 0 < scalar < r */ + if (crypto_bignum_is_zero(peer_scalar) || + crypto_bignum_cmp(peer_scalar, sae->tmp->order) >= 0) { + wpa_printf(MSG_DEBUG, "SAE: Invalid peer scalar"); + crypto_bignum_deinit(peer_scalar, 0); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + + crypto_bignum_deinit(sae->peer_commit_scalar, 0); + sae->peer_commit_scalar = peer_scalar; + wpa_hexdump(MSG_DEBUG, "SAE: Peer commit-scalar", + *pos, sae->tmp->prime_len); + *pos += sae->tmp->prime_len; + + return WLAN_STATUS_SUCCESS; +} + + +static u16 sae_parse_commit_element_ecc(struct sae_data *sae, const u8 *pos, + const u8 *end) +{ + u8 prime[SAE_MAX_ECC_PRIME_LEN]; + + if (pos + 2 * sae->tmp->prime_len > end) { + wpa_printf(MSG_DEBUG, "SAE: Not enough data for " + "commit-element"); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + if (crypto_bignum_to_bin(sae->tmp->prime, prime, sizeof(prime), + sae->tmp->prime_len) < 0) + return WLAN_STATUS_UNSPECIFIED_FAILURE; + + /* element x and y coordinates < p */ + if (os_memcmp(pos, prime, sae->tmp->prime_len) >= 0 || + os_memcmp(pos + sae->tmp->prime_len, prime, + sae->tmp->prime_len) >= 0) { + wpa_printf(MSG_DEBUG, "SAE: Invalid coordinates in peer " + "element"); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + wpa_hexdump(MSG_DEBUG, "SAE: Peer commit-element(x)", + pos, sae->tmp->prime_len); + wpa_hexdump(MSG_DEBUG, "SAE: Peer commit-element(y)", + pos + sae->tmp->prime_len, sae->tmp->prime_len); + + crypto_ec_point_deinit(sae->tmp->peer_commit_element_ecc, 0); + sae->tmp->peer_commit_element_ecc = + crypto_ec_point_from_bin(sae->tmp->ec, pos); + if (sae->tmp->peer_commit_element_ecc == NULL) + return WLAN_STATUS_UNSPECIFIED_FAILURE; + + if (!crypto_ec_point_is_on_curve(sae->tmp->ec, + sae->tmp->peer_commit_element_ecc)) { + wpa_printf(MSG_DEBUG, "SAE: Peer element is not on curve"); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + return WLAN_STATUS_SUCCESS; +} + + +static u16 sae_parse_commit_element_ffc(struct sae_data *sae, const u8 *pos, + const u8 *end) +{ + struct crypto_bignum *res; + + if (pos + sae->tmp->prime_len > end) { + wpa_printf(MSG_DEBUG, "SAE: Not enough data for " + "commit-element"); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + wpa_hexdump(MSG_DEBUG, "SAE: Peer commit-element", pos, + sae->tmp->prime_len); + + crypto_bignum_deinit(sae->tmp->peer_commit_element_ffc, 0); + sae->tmp->peer_commit_element_ffc = + crypto_bignum_init_set(pos, sae->tmp->prime_len); + if (sae->tmp->peer_commit_element_ffc == NULL) + return WLAN_STATUS_UNSPECIFIED_FAILURE; + if (crypto_bignum_is_zero(sae->tmp->peer_commit_element_ffc) || + crypto_bignum_is_one(sae->tmp->peer_commit_element_ffc) || + crypto_bignum_cmp(sae->tmp->peer_commit_element_ffc, + sae->tmp->prime) >= 0) { + wpa_printf(MSG_DEBUG, "SAE: Invalid peer element"); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + /* scalar-op(r, ELEMENT) = 1 modulo p */ + res = crypto_bignum_init(); + if (res == NULL || + crypto_bignum_exptmod(sae->tmp->peer_commit_element_ffc, + sae->tmp->order, sae->tmp->prime, res) < 0 || + !crypto_bignum_is_one(res)) { + wpa_printf(MSG_DEBUG, "SAE: Invalid peer element (scalar-op)"); + crypto_bignum_deinit(res, 0); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + crypto_bignum_deinit(res, 0); + + return WLAN_STATUS_SUCCESS; +} + + +static u16 sae_parse_commit_element(struct sae_data *sae, const u8 *pos, + const u8 *end) +{ + if (sae->tmp->dh) + return sae_parse_commit_element_ffc(sae, pos, end); + return sae_parse_commit_element_ecc(sae, pos, end); +} + + +u16 sae_parse_commit(struct sae_data *sae, const u8 *data, size_t len, + const u8 **token, size_t *token_len, int *allowed_groups) +{ + const u8 *pos = data, *end = data + len; + u16 res; + + /* Check Finite Cyclic Group */ + if (pos + 2 > end) + return WLAN_STATUS_UNSPECIFIED_FAILURE; + res = sae_group_allowed(sae, allowed_groups, WPA_GET_LE16(pos)); + if (res != WLAN_STATUS_SUCCESS) + return res; + pos += 2; + + /* Optional Anti-Clogging Token */ + sae_parse_commit_token(sae, &pos, end, token, token_len); + + /* commit-scalar */ + res = sae_parse_commit_scalar(sae, &pos, end); + if (res != WLAN_STATUS_SUCCESS) + return res; + + /* commit-element */ + return sae_parse_commit_element(sae, pos, end); +} + + +static void sae_cn_confirm(struct sae_data *sae, const u8 *sc, + const struct crypto_bignum *scalar1, + const u8 *element1, size_t element1_len, + const struct crypto_bignum *scalar2, + const u8 *element2, size_t element2_len, + u8 *confirm) +{ + const u8 *addr[5]; + size_t len[5]; + u8 scalar_b1[SAE_MAX_PRIME_LEN], scalar_b2[SAE_MAX_PRIME_LEN]; + + /* Confirm + * CN(key, X, Y, Z, ...) = + * HMAC-SHA256(key, D2OS(X) || D2OS(Y) || D2OS(Z) | ...) + * confirm = CN(KCK, send-confirm, commit-scalar, COMMIT-ELEMENT, + * peer-commit-scalar, PEER-COMMIT-ELEMENT) + * verifier = CN(KCK, peer-send-confirm, peer-commit-scalar, + * PEER-COMMIT-ELEMENT, commit-scalar, COMMIT-ELEMENT) + */ + addr[0] = sc; + len[0] = 2; + crypto_bignum_to_bin(scalar1, scalar_b1, sizeof(scalar_b1), + sae->tmp->prime_len); + addr[1] = scalar_b1; + len[1] = sae->tmp->prime_len; + addr[2] = element1; + len[2] = element1_len; + crypto_bignum_to_bin(scalar2, scalar_b2, sizeof(scalar_b2), + sae->tmp->prime_len); + addr[3] = scalar_b2; + len[3] = sae->tmp->prime_len; + addr[4] = element2; + len[4] = element2_len; + hmac_sha256_vector(sae->tmp->kck, sizeof(sae->tmp->kck), 5, addr, len, + confirm); +} + + +static void sae_cn_confirm_ecc(struct sae_data *sae, const u8 *sc, + const struct crypto_bignum *scalar1, + const struct crypto_ec_point *element1, + const struct crypto_bignum *scalar2, + const struct crypto_ec_point *element2, + u8 *confirm) +{ + u8 element_b1[2 * SAE_MAX_ECC_PRIME_LEN]; + u8 element_b2[2 * SAE_MAX_ECC_PRIME_LEN]; + + crypto_ec_point_to_bin(sae->tmp->ec, element1, element_b1, + element_b1 + sae->tmp->prime_len); + crypto_ec_point_to_bin(sae->tmp->ec, element2, element_b2, + element_b2 + sae->tmp->prime_len); + + sae_cn_confirm(sae, sc, scalar1, element_b1, 2 * sae->tmp->prime_len, + scalar2, element_b2, 2 * sae->tmp->prime_len, confirm); +} + + +static void sae_cn_confirm_ffc(struct sae_data *sae, const u8 *sc, + const struct crypto_bignum *scalar1, + const struct crypto_bignum *element1, + const struct crypto_bignum *scalar2, + const struct crypto_bignum *element2, + u8 *confirm) +{ + u8 element_b1[SAE_MAX_PRIME_LEN]; + u8 element_b2[SAE_MAX_PRIME_LEN]; + + crypto_bignum_to_bin(element1, element_b1, sizeof(element_b1), + sae->tmp->prime_len); + crypto_bignum_to_bin(element2, element_b2, sizeof(element_b2), + sae->tmp->prime_len); + + sae_cn_confirm(sae, sc, scalar1, element_b1, sae->tmp->prime_len, + scalar2, element_b2, sae->tmp->prime_len, confirm); +} + + +void sae_write_confirm(struct sae_data *sae, struct wpabuf *buf) +{ + const u8 *sc; + + /* Send-Confirm */ + sc = wpabuf_put(buf, 0); + wpabuf_put_le16(buf, sae->send_confirm); + sae->send_confirm++; + + if (sae->tmp->ec) + sae_cn_confirm_ecc(sae, sc, sae->tmp->own_commit_scalar, + sae->tmp->own_commit_element_ecc, + sae->peer_commit_scalar, + sae->tmp->peer_commit_element_ecc, + wpabuf_put(buf, SHA256_MAC_LEN)); + else + sae_cn_confirm_ffc(sae, sc, sae->tmp->own_commit_scalar, + sae->tmp->own_commit_element_ffc, + sae->peer_commit_scalar, + sae->tmp->peer_commit_element_ffc, + wpabuf_put(buf, SHA256_MAC_LEN)); +} + + +int sae_check_confirm(struct sae_data *sae, const u8 *data, size_t len) +{ + u8 verifier[SHA256_MAC_LEN]; + + if (len < 2 + SHA256_MAC_LEN) { + wpa_printf(MSG_DEBUG, "SAE: Too short confirm message"); + return -1; + } + + wpa_printf(MSG_DEBUG, "SAE: peer-send-confirm %u", WPA_GET_LE16(data)); + + if (sae->tmp->ec) + sae_cn_confirm_ecc(sae, data, sae->peer_commit_scalar, + sae->tmp->peer_commit_element_ecc, + sae->tmp->own_commit_scalar, + sae->tmp->own_commit_element_ecc, + verifier); + else + sae_cn_confirm_ffc(sae, data, sae->peer_commit_scalar, + sae->tmp->peer_commit_element_ffc, + sae->tmp->own_commit_scalar, + sae->tmp->own_commit_element_ffc, + verifier); + + if (os_memcmp(verifier, data + 2, SHA256_MAC_LEN) != 0) { + wpa_printf(MSG_DEBUG, "SAE: Confirm mismatch"); + wpa_hexdump(MSG_DEBUG, "SAE: Received confirm", + data + 2, SHA256_MAC_LEN); + wpa_hexdump(MSG_DEBUG, "SAE: Calculated verifier", + verifier, SHA256_MAC_LEN); + return -1; + } + + return 0; +} diff -Nru wpa-1.0/src/common/sae.h wpa-2.1/src/common/sae.h --- wpa-1.0/src/common/sae.h 1970-01-01 00:00:00.000000000 +0000 +++ wpa-2.1/src/common/sae.h 2014-02-04 11:23:35.000000000 +0000 @@ -0,0 +1,64 @@ +/* + * Simultaneous authentication of equals + * Copyright (c) 2012-2013, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef SAE_H +#define SAE_H + +#define SAE_KCK_LEN 32 +#define SAE_PMK_LEN 32 +#define SAE_PMKID_LEN 16 +#define SAE_KEYSEED_KEY_LEN 32 +#define SAE_MAX_PRIME_LEN 512 +#define SAE_MAX_ECC_PRIME_LEN 66 +#define SAE_COMMIT_MAX_LEN (2 + 3 * SAE_MAX_PRIME_LEN) +#define SAE_CONFIRM_MAX_LEN (2 + SAE_MAX_PRIME_LEN) + +struct sae_temporary_data { + u8 kck[SAE_KCK_LEN]; + struct crypto_bignum *own_commit_scalar; + struct crypto_bignum *own_commit_element_ffc; + struct crypto_ec_point *own_commit_element_ecc; + struct crypto_bignum *peer_commit_element_ffc; + struct crypto_ec_point *peer_commit_element_ecc; + struct crypto_ec_point *pwe_ecc; + struct crypto_bignum *pwe_ffc; + struct crypto_bignum *sae_rand; + struct crypto_ec *ec; + int prime_len; + const struct dh_group *dh; + const struct crypto_bignum *prime; + const struct crypto_bignum *order; + struct crypto_bignum *prime_buf; + struct crypto_bignum *order_buf; +}; + +struct sae_data { + enum { SAE_NOTHING, SAE_COMMITTED, SAE_CONFIRMED, SAE_ACCEPTED } state; + u16 send_confirm; + u8 pmk[SAE_PMK_LEN]; + struct crypto_bignum *peer_commit_scalar; + int group; + struct sae_temporary_data *tmp; +}; + +int sae_set_group(struct sae_data *sae, int group); +void sae_clear_temp_data(struct sae_data *sae); +void sae_clear_data(struct sae_data *sae); + +int sae_prepare_commit(const u8 *addr1, const u8 *addr2, + const u8 *password, size_t password_len, + struct sae_data *sae); +int sae_process_commit(struct sae_data *sae); +void sae_write_commit(struct sae_data *sae, struct wpabuf *buf, + const struct wpabuf *token); +u16 sae_parse_commit(struct sae_data *sae, const u8 *data, size_t len, + const u8 **token, size_t *token_len, int *allowed_groups); +void sae_write_confirm(struct sae_data *sae, struct wpabuf *buf); +int sae_check_confirm(struct sae_data *sae, const u8 *data, size_t len); + +#endif /* SAE_H */ diff -Nru wpa-1.0/src/common/version.h wpa-2.1/src/common/version.h --- wpa-1.0/src/common/version.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/common/version.h 2014-02-04 11:23:35.000000000 +0000 @@ -5,6 +5,6 @@ #define VERSION_STR_POSTFIX "" #endif /* VERSION_STR_POSTFIX */ -#define VERSION_STR "1.0" VERSION_STR_POSTFIX +#define VERSION_STR "2.1" VERSION_STR_POSTFIX #endif /* VERSION_H */ diff -Nru wpa-1.0/src/common/wpa_common.c wpa-2.1/src/common/wpa_common.c --- wpa-1.0/src/common/wpa_common.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/common/wpa_common.c 2014-02-04 11:23:35.000000000 +0000 @@ -1,15 +1,9 @@ /* * WPA/RSN - Shared functions for supplicant and authenticator - * Copyright (c) 2002-2008, Jouni Malinen + * Copyright (c) 2002-2013, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -49,8 +43,10 @@ u8 hash[SHA1_MAC_LEN]; switch (ver) { +#ifndef CONFIG_FIPS case WPA_KEY_INFO_TYPE_HMAC_MD5_RC4: return hmac_md5(key, 16, buf, len, mic); +#endif /* CONFIG_FIPS */ case WPA_KEY_INFO_TYPE_HMAC_SHA1_AES: if (hmac_sha1(key, 16, buf, len, hash)) return -1; @@ -339,7 +335,6 @@ #endif /* CONFIG_IEEE80211R */ -#ifndef CONFIG_NO_WPA2 static int rsn_selector_to_bitfield(const u8 *s) { if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_NONE) @@ -356,6 +351,18 @@ if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_AES_128_CMAC) return WPA_CIPHER_AES_128_CMAC; #endif /* CONFIG_IEEE80211W */ + if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_GCMP) + return WPA_CIPHER_GCMP; + if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_CCMP_256) + return WPA_CIPHER_CCMP_256; + if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_GCMP_256) + return WPA_CIPHER_GCMP_256; + if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_BIP_GMAC_128) + return WPA_CIPHER_BIP_GMAC_128; + if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_BIP_GMAC_256) + return WPA_CIPHER_BIP_GMAC_256; + if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_BIP_CMAC_256) + return WPA_CIPHER_BIP_CMAC_256; return 0; } @@ -378,9 +385,14 @@ if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_PSK_SHA256) return WPA_KEY_MGMT_PSK_SHA256; #endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_SAE + if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_SAE) + return WPA_KEY_MGMT_SAE; + if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_FT_SAE) + return WPA_KEY_MGMT_FT_SAE; +#endif /* CONFIG_SAE */ return 0; } -#endif /* CONFIG_NO_WPA2 */ /** @@ -393,7 +405,6 @@ int wpa_parse_wpa_ie_rsn(const u8 *rsn_ie, size_t rsn_ie_len, struct wpa_ie_data *data) { -#ifndef CONFIG_NO_WPA2 const struct rsn_ie_hdr *hdr; const u8 *pos; int left; @@ -547,9 +558,6 @@ } return 0; -#else /* CONFIG_NO_WPA2 */ - return -1; -#endif /* CONFIG_NO_WPA2 */ } @@ -912,6 +920,14 @@ return "CCMP"; case WPA_CIPHER_CCMP | WPA_CIPHER_TKIP: return "CCMP+TKIP"; + case WPA_CIPHER_GCMP: + return "GCMP"; + case WPA_CIPHER_GCMP_256: + return "GCMP-256"; + case WPA_CIPHER_CCMP_256: + return "CCMP-256"; + case WPA_CIPHER_GTK_NOT_USED: + return "GTK_NOT_USED"; default: return "UNKNOWN"; } @@ -1073,3 +1089,348 @@ return added; } #endif /* CONFIG_IEEE80211R */ + + +int wpa_cipher_key_len(int cipher) +{ + switch (cipher) { + case WPA_CIPHER_CCMP_256: + case WPA_CIPHER_GCMP_256: + return 32; + case WPA_CIPHER_CCMP: + case WPA_CIPHER_GCMP: + return 16; + case WPA_CIPHER_TKIP: + return 32; + case WPA_CIPHER_WEP104: + return 13; + case WPA_CIPHER_WEP40: + return 5; + } + + return 0; +} + + +int wpa_cipher_rsc_len(int cipher) +{ + switch (cipher) { + case WPA_CIPHER_CCMP_256: + case WPA_CIPHER_GCMP_256: + case WPA_CIPHER_CCMP: + case WPA_CIPHER_GCMP: + case WPA_CIPHER_TKIP: + return 6; + case WPA_CIPHER_WEP104: + case WPA_CIPHER_WEP40: + return 0; + } + + return 0; +} + + +int wpa_cipher_to_alg(int cipher) +{ + switch (cipher) { + case WPA_CIPHER_CCMP_256: + return WPA_ALG_CCMP_256; + case WPA_CIPHER_GCMP_256: + return WPA_ALG_GCMP_256; + case WPA_CIPHER_CCMP: + return WPA_ALG_CCMP; + case WPA_CIPHER_GCMP: + return WPA_ALG_GCMP; + case WPA_CIPHER_TKIP: + return WPA_ALG_TKIP; + case WPA_CIPHER_WEP104: + case WPA_CIPHER_WEP40: + return WPA_ALG_WEP; + } + return WPA_ALG_NONE; +} + + +int wpa_cipher_valid_pairwise(int cipher) +{ + return cipher == WPA_CIPHER_CCMP_256 || + cipher == WPA_CIPHER_GCMP_256 || + cipher == WPA_CIPHER_CCMP || + cipher == WPA_CIPHER_GCMP || + cipher == WPA_CIPHER_TKIP; +} + + +u32 wpa_cipher_to_suite(int proto, int cipher) +{ + if (cipher & WPA_CIPHER_CCMP_256) + return RSN_CIPHER_SUITE_CCMP_256; + if (cipher & WPA_CIPHER_GCMP_256) + return RSN_CIPHER_SUITE_GCMP_256; + if (cipher & WPA_CIPHER_CCMP) + return (proto == WPA_PROTO_RSN ? + RSN_CIPHER_SUITE_CCMP : WPA_CIPHER_SUITE_CCMP); + if (cipher & WPA_CIPHER_GCMP) + return RSN_CIPHER_SUITE_GCMP; + if (cipher & WPA_CIPHER_TKIP) + return (proto == WPA_PROTO_RSN ? + RSN_CIPHER_SUITE_TKIP : WPA_CIPHER_SUITE_TKIP); + if (cipher & WPA_CIPHER_WEP104) + return (proto == WPA_PROTO_RSN ? + RSN_CIPHER_SUITE_WEP104 : WPA_CIPHER_SUITE_WEP104); + if (cipher & WPA_CIPHER_WEP40) + return (proto == WPA_PROTO_RSN ? + RSN_CIPHER_SUITE_WEP40 : WPA_CIPHER_SUITE_WEP40); + if (cipher & WPA_CIPHER_NONE) + return (proto == WPA_PROTO_RSN ? + RSN_CIPHER_SUITE_NONE : WPA_CIPHER_SUITE_NONE); + if (cipher & WPA_CIPHER_GTK_NOT_USED) + return RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED; + return 0; +} + + +int rsn_cipher_put_suites(u8 *pos, int ciphers) +{ + int num_suites = 0; + + if (ciphers & WPA_CIPHER_CCMP_256) { + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP_256); + pos += RSN_SELECTOR_LEN; + num_suites++; + } + if (ciphers & WPA_CIPHER_GCMP_256) { + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_GCMP_256); + pos += RSN_SELECTOR_LEN; + num_suites++; + } + if (ciphers & WPA_CIPHER_CCMP) { + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP); + pos += RSN_SELECTOR_LEN; + num_suites++; + } + if (ciphers & WPA_CIPHER_GCMP) { + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_GCMP); + pos += RSN_SELECTOR_LEN; + num_suites++; + } + if (ciphers & WPA_CIPHER_TKIP) { + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_TKIP); + pos += RSN_SELECTOR_LEN; + num_suites++; + } + if (ciphers & WPA_CIPHER_NONE) { + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_NONE); + pos += RSN_SELECTOR_LEN; + num_suites++; + } + + return num_suites; +} + + +int wpa_cipher_put_suites(u8 *pos, int ciphers) +{ + int num_suites = 0; + + if (ciphers & WPA_CIPHER_CCMP) { + RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_CCMP); + pos += WPA_SELECTOR_LEN; + num_suites++; + } + if (ciphers & WPA_CIPHER_TKIP) { + RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_TKIP); + pos += WPA_SELECTOR_LEN; + num_suites++; + } + if (ciphers & WPA_CIPHER_NONE) { + RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_NONE); + pos += WPA_SELECTOR_LEN; + num_suites++; + } + + return num_suites; +} + + +int wpa_pick_pairwise_cipher(int ciphers, int none_allowed) +{ + if (ciphers & WPA_CIPHER_CCMP_256) + return WPA_CIPHER_CCMP_256; + if (ciphers & WPA_CIPHER_GCMP_256) + return WPA_CIPHER_GCMP_256; + if (ciphers & WPA_CIPHER_CCMP) + return WPA_CIPHER_CCMP; + if (ciphers & WPA_CIPHER_GCMP) + return WPA_CIPHER_GCMP; + if (ciphers & WPA_CIPHER_TKIP) + return WPA_CIPHER_TKIP; + if (none_allowed && (ciphers & WPA_CIPHER_NONE)) + return WPA_CIPHER_NONE; + return -1; +} + + +int wpa_pick_group_cipher(int ciphers) +{ + if (ciphers & WPA_CIPHER_CCMP_256) + return WPA_CIPHER_CCMP_256; + if (ciphers & WPA_CIPHER_GCMP_256) + return WPA_CIPHER_GCMP_256; + if (ciphers & WPA_CIPHER_CCMP) + return WPA_CIPHER_CCMP; + if (ciphers & WPA_CIPHER_GCMP) + return WPA_CIPHER_GCMP; + if (ciphers & WPA_CIPHER_GTK_NOT_USED) + return WPA_CIPHER_GTK_NOT_USED; + if (ciphers & WPA_CIPHER_TKIP) + return WPA_CIPHER_TKIP; + if (ciphers & WPA_CIPHER_WEP104) + return WPA_CIPHER_WEP104; + if (ciphers & WPA_CIPHER_WEP40) + return WPA_CIPHER_WEP40; + return -1; +} + + +int wpa_parse_cipher(const char *value) +{ + int val = 0, last; + char *start, *end, *buf; + + buf = os_strdup(value); + if (buf == NULL) + return -1; + start = buf; + + while (*start != '\0') { + while (*start == ' ' || *start == '\t') + start++; + if (*start == '\0') + break; + end = start; + while (*end != ' ' && *end != '\t' && *end != '\0') + end++; + last = *end == '\0'; + *end = '\0'; + if (os_strcmp(start, "CCMP-256") == 0) + val |= WPA_CIPHER_CCMP_256; + else if (os_strcmp(start, "GCMP-256") == 0) + val |= WPA_CIPHER_GCMP_256; + else if (os_strcmp(start, "CCMP") == 0) + val |= WPA_CIPHER_CCMP; + else if (os_strcmp(start, "GCMP") == 0) + val |= WPA_CIPHER_GCMP; + else if (os_strcmp(start, "TKIP") == 0) + val |= WPA_CIPHER_TKIP; + else if (os_strcmp(start, "WEP104") == 0) + val |= WPA_CIPHER_WEP104; + else if (os_strcmp(start, "WEP40") == 0) + val |= WPA_CIPHER_WEP40; + else if (os_strcmp(start, "NONE") == 0) + val |= WPA_CIPHER_NONE; + else if (os_strcmp(start, "GTK_NOT_USED") == 0) + val |= WPA_CIPHER_GTK_NOT_USED; + else { + os_free(buf); + return -1; + } + + if (last) + break; + start = end + 1; + } + os_free(buf); + + return val; +} + + +int wpa_write_ciphers(char *start, char *end, int ciphers, const char *delim) +{ + char *pos = start; + int ret; + + if (ciphers & WPA_CIPHER_CCMP_256) { + ret = os_snprintf(pos, end - pos, "%sCCMP-256", + pos == start ? "" : delim); + if (ret < 0 || ret >= end - pos) + return -1; + pos += ret; + } + if (ciphers & WPA_CIPHER_GCMP_256) { + ret = os_snprintf(pos, end - pos, "%sGCMP-256", + pos == start ? "" : delim); + if (ret < 0 || ret >= end - pos) + return -1; + pos += ret; + } + if (ciphers & WPA_CIPHER_CCMP) { + ret = os_snprintf(pos, end - pos, "%sCCMP", + pos == start ? "" : delim); + if (ret < 0 || ret >= end - pos) + return -1; + pos += ret; + } + if (ciphers & WPA_CIPHER_GCMP) { + ret = os_snprintf(pos, end - pos, "%sGCMP", + pos == start ? "" : delim); + if (ret < 0 || ret >= end - pos) + return -1; + pos += ret; + } + if (ciphers & WPA_CIPHER_TKIP) { + ret = os_snprintf(pos, end - pos, "%sTKIP", + pos == start ? "" : delim); + if (ret < 0 || ret >= end - pos) + return -1; + pos += ret; + } + if (ciphers & WPA_CIPHER_WEP104) { + ret = os_snprintf(pos, end - pos, "%sWEP104", + pos == start ? "" : delim); + if (ret < 0 || ret >= end - pos) + return -1; + pos += ret; + } + if (ciphers & WPA_CIPHER_WEP40) { + ret = os_snprintf(pos, end - pos, "%sWEP40", + pos == start ? "" : delim); + if (ret < 0 || ret >= end - pos) + return -1; + pos += ret; + } + if (ciphers & WPA_CIPHER_NONE) { + ret = os_snprintf(pos, end - pos, "%sNONE", + pos == start ? "" : delim); + if (ret < 0 || ret >= end - pos) + return -1; + pos += ret; + } + + return pos - start; +} + + +int wpa_select_ap_group_cipher(int wpa, int wpa_pairwise, int rsn_pairwise) +{ + int pairwise = 0; + + /* Select group cipher based on the enabled pairwise cipher suites */ + if (wpa & 1) + pairwise |= wpa_pairwise; + if (wpa & 2) + pairwise |= rsn_pairwise; + + if (pairwise & WPA_CIPHER_TKIP) + return WPA_CIPHER_TKIP; + if ((pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP)) == WPA_CIPHER_GCMP) + return WPA_CIPHER_GCMP; + if ((pairwise & (WPA_CIPHER_GCMP_256 | WPA_CIPHER_CCMP | + WPA_CIPHER_GCMP)) == WPA_CIPHER_GCMP_256) + return WPA_CIPHER_GCMP_256; + if ((pairwise & (WPA_CIPHER_CCMP_256 | WPA_CIPHER_CCMP | + WPA_CIPHER_GCMP)) == WPA_CIPHER_CCMP_256) + return WPA_CIPHER_CCMP_256; + return WPA_CIPHER_CCMP; +} diff -Nru wpa-1.0/src/common/wpa_common.h wpa-2.1/src/common/wpa_common.h --- wpa-1.0/src/common/wpa_common.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/common/wpa_common.h 2014-02-04 11:23:35.000000000 +0000 @@ -1,15 +1,9 @@ /* * WPA definitions shared between hostapd and wpa_supplicant - * Copyright (c) 2002-2008, Jouni Malinen + * Copyright (c) 2002-2013, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef WPA_COMMON_H @@ -26,6 +20,14 @@ #define WPA_GMK_LEN 32 #define WPA_GTK_MAX_LEN 32 +#define WPA_ALLOWED_PAIRWISE_CIPHERS \ +(WPA_CIPHER_CCMP | WPA_CIPHER_GCMP | WPA_CIPHER_TKIP | WPA_CIPHER_NONE | \ +WPA_CIPHER_GCMP_256 | WPA_CIPHER_CCMP_256) +#define WPA_ALLOWED_GROUP_CIPHERS \ +(WPA_CIPHER_CCMP | WPA_CIPHER_GCMP | WPA_CIPHER_TKIP | WPA_CIPHER_WEP104 | \ +WPA_CIPHER_WEP40 | WPA_CIPHER_GCMP_256 | WPA_CIPHER_CCMP_256 | \ +WPA_CIPHER_GTK_NOT_USED) + #define WPA_SELECTOR_LEN 4 #define WPA_VERSION 1 #define RSN_SELECTOR_LEN 4 @@ -38,6 +40,7 @@ #define WPA_AUTH_KEY_MGMT_NONE RSN_SELECTOR(0x00, 0x50, 0xf2, 0) #define WPA_AUTH_KEY_MGMT_UNSPEC_802_1X RSN_SELECTOR(0x00, 0x50, 0xf2, 1) #define WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X RSN_SELECTOR(0x00, 0x50, 0xf2, 2) +#define WPA_AUTH_KEY_MGMT_CCKM RSN_SELECTOR(0x00, 0x40, 0x96, 0) #define WPA_CIPHER_SUITE_NONE RSN_SELECTOR(0x00, 0x50, 0xf2, 0) #define WPA_CIPHER_SUITE_WEP40 RSN_SELECTOR(0x00, 0x50, 0xf2, 1) #define WPA_CIPHER_SUITE_TKIP RSN_SELECTOR(0x00, 0x50, 0xf2, 2) @@ -57,6 +60,13 @@ #define RSN_AUTH_KEY_MGMT_802_1X_SHA256 RSN_SELECTOR(0x00, 0x0f, 0xac, 5) #define RSN_AUTH_KEY_MGMT_PSK_SHA256 RSN_SELECTOR(0x00, 0x0f, 0xac, 6) #define RSN_AUTH_KEY_MGMT_TPK_HANDSHAKE RSN_SELECTOR(0x00, 0x0f, 0xac, 7) +#define RSN_AUTH_KEY_MGMT_SAE RSN_SELECTOR(0x00, 0x0f, 0xac, 8) +#define RSN_AUTH_KEY_MGMT_FT_SAE RSN_SELECTOR(0x00, 0x0f, 0xac, 9) +#define RSN_AUTH_KEY_MGMT_802_1X_SUITE_B RSN_SELECTOR(0x00, 0x0f, 0xac, 11) +#define RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_384 RSN_SELECTOR(0x00, 0x0f, 0xac, 12) +#define RSN_AUTH_KEY_MGMT_FT_802_1X_SUITE_B_384 \ +RSN_SELECTOR(0x00, 0x0f, 0xac, 13) +#define RSN_AUTH_KEY_MGMT_CCKM RSN_SELECTOR(0x00, 0x40, 0x96, 0x00) #define RSN_CIPHER_SUITE_NONE RSN_SELECTOR(0x00, 0x0f, 0xac, 0) #define RSN_CIPHER_SUITE_WEP40 RSN_SELECTOR(0x00, 0x0f, 0xac, 1) @@ -70,6 +80,12 @@ #define RSN_CIPHER_SUITE_AES_128_CMAC RSN_SELECTOR(0x00, 0x0f, 0xac, 6) #endif /* CONFIG_IEEE80211W */ #define RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED RSN_SELECTOR(0x00, 0x0f, 0xac, 7) +#define RSN_CIPHER_SUITE_GCMP RSN_SELECTOR(0x00, 0x0f, 0xac, 8) +#define RSN_CIPHER_SUITE_GCMP_256 RSN_SELECTOR(0x00, 0x0f, 0xac, 9) +#define RSN_CIPHER_SUITE_CCMP_256 RSN_SELECTOR(0x00, 0x0f, 0xac, 10) +#define RSN_CIPHER_SUITE_BIP_GMAC_128 RSN_SELECTOR(0x00, 0x0f, 0xac, 11) +#define RSN_CIPHER_SUITE_BIP_GMAC_256 RSN_SELECTOR(0x00, 0x0f, 0xac, 12) +#define RSN_CIPHER_SUITE_BIP_CMAC_256 RSN_SELECTOR(0x00, 0x0f, 0xac, 13) /* EAPOL-Key Key Data Encapsulation * GroupKey and PeerKey require encryption, otherwise, encryption is optional. @@ -89,6 +105,12 @@ #ifdef CONFIG_IEEE80211W #define RSN_KEY_DATA_IGTK RSN_SELECTOR(0x00, 0x0f, 0xac, 9) #endif /* CONFIG_IEEE80211W */ +#define RSN_KEY_DATA_KEYID RSN_SELECTOR(0x00, 0x0f, 0xac, 10) +#define RSN_KEY_DATA_MULTIBAND_GTK RSN_SELECTOR(0x00, 0x0f, 0xac, 11) +#define RSN_KEY_DATA_MULTIBAND_KEYID RSN_SELECTOR(0x00, 0x0f, 0xac, 12) + +#define WFA_KEY_DATA_IP_ADDR_REQ RSN_SELECTOR(0x50, 0x6f, 0x9a, 4) +#define WFA_KEY_DATA_IP_ADDR_ALLOC RSN_SELECTOR(0x50, 0x6f, 0x9a, 5) #define WPA_OUI_TYPE RSN_SELECTOR(0x00, 0x50, 0xf2, 1) @@ -381,4 +403,17 @@ int wpa_ft_parse_ies(const u8 *ies, size_t ies_len, struct wpa_ft_ies *parse); +int wpa_cipher_key_len(int cipher); +int wpa_cipher_rsc_len(int cipher); +int wpa_cipher_to_alg(int cipher); +int wpa_cipher_valid_pairwise(int cipher); +u32 wpa_cipher_to_suite(int proto, int cipher); +int rsn_cipher_put_suites(u8 *pos, int ciphers); +int wpa_cipher_put_suites(u8 *pos, int ciphers); +int wpa_pick_pairwise_cipher(int ciphers, int none_allowed); +int wpa_pick_group_cipher(int ciphers); +int wpa_parse_cipher(const char *value); +int wpa_write_ciphers(char *start, char *end, int ciphers, const char *delim); +int wpa_select_ap_group_cipher(int wpa, int wpa_pairwise, int rsn_pairwise); + #endif /* WPA_COMMON_H */ diff -Nru wpa-1.0/src/common/wpa_ctrl.c wpa-2.1/src/common/wpa_ctrl.c --- wpa-1.0/src/common/wpa_ctrl.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/common/wpa_ctrl.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * wpa_supplicant/hostapd control interface library * Copyright (c) 2004-2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -18,7 +12,12 @@ #ifdef CONFIG_CTRL_IFACE_UNIX #include +#include +#include #endif /* CONFIG_CTRL_IFACE_UNIX */ +#ifdef CONFIG_CTRL_IFACE_UDP_REMOTE +#include +#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */ #ifdef ANDROID #include @@ -50,6 +49,8 @@ struct sockaddr_in local; struct sockaddr_in dest; char *cookie; + char *remote_ifname; + char *remote_ip; #endif /* CONFIG_CTRL_IFACE_UDP */ #ifdef CONFIG_CTRL_IFACE_UNIX int s; @@ -79,6 +80,10 @@ int ret; size_t res; int tries = 0; + int flags; + + if (ctrl_path == NULL) + return NULL; ctrl = os_malloc(sizeof(*ctrl)); if (ctrl == NULL) @@ -124,13 +129,27 @@ #ifdef ANDROID chmod(ctrl->local.sun_path, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); chown(ctrl->local.sun_path, AID_SYSTEM, AID_WIFI); + + if (os_strncmp(ctrl_path, "@android:", 9) == 0) { + if (socket_local_client_connect( + ctrl->s, ctrl_path + 9, + ANDROID_SOCKET_NAMESPACE_RESERVED, + SOCK_DGRAM) < 0) { + close(ctrl->s); + unlink(ctrl->local.sun_path); + os_free(ctrl); + return NULL; + } + return ctrl; + } + /* * If the ctrl_path isn't an absolute pathname, assume that * it's the name of a socket in the Android reserved namespace. * Otherwise, it's a normal UNIX domain socket appearing in the * filesystem. */ - if (ctrl_path != NULL && *ctrl_path != '/') { + if (*ctrl_path != '/') { char buf[21]; os_snprintf(buf, sizeof(buf), "wpa_%s", ctrl_path); if (socket_local_client_connect( @@ -147,12 +166,18 @@ #endif /* ANDROID */ ctrl->dest.sun_family = AF_UNIX; - res = os_strlcpy(ctrl->dest.sun_path, ctrl_path, - sizeof(ctrl->dest.sun_path)); - if (res >= sizeof(ctrl->dest.sun_path)) { - close(ctrl->s); - os_free(ctrl); - return NULL; + if (os_strncmp(ctrl_path, "@abstract:", 10) == 0) { + ctrl->dest.sun_path[0] = '\0'; + os_strlcpy(ctrl->dest.sun_path + 1, ctrl_path + 10, + sizeof(ctrl->dest.sun_path) - 1); + } else { + res = os_strlcpy(ctrl->dest.sun_path, ctrl_path, + sizeof(ctrl->dest.sun_path)); + if (res >= sizeof(ctrl->dest.sun_path)) { + close(ctrl->s); + os_free(ctrl); + return NULL; + } } if (connect(ctrl->s, (struct sockaddr *) &ctrl->dest, sizeof(ctrl->dest)) < 0) { @@ -162,6 +187,19 @@ return NULL; } + /* + * Make socket non-blocking so that we don't hang forever if + * target dies unexpectedly. + */ + flags = fcntl(ctrl->s, F_GETFL); + if (flags >= 0) { + flags |= O_NONBLOCK; + if (fcntl(ctrl->s, F_SETFL, flags) < 0) { + perror("fcntl(ctrl->s, O_NONBLOCK)"); + /* Not fatal, continue on.*/ + } + } + return ctrl; } @@ -191,7 +229,6 @@ struct dirent entry; struct dirent *result; size_t dirnamelen; - int prefixlen = os_strlen(CONFIG_CTRL_IFACE_CLIENT_PREFIX); size_t maxcopy; char pathname[PATH_MAX]; char *namep; @@ -208,11 +245,8 @@ namep = pathname + dirnamelen; maxcopy = PATH_MAX - dirnamelen; while (readdir_r(dir, &entry, &result) == 0 && result != NULL) { - if (os_strncmp(entry.d_name, CONFIG_CTRL_IFACE_CLIENT_PREFIX, - prefixlen) == 0) { - if (os_strlcpy(namep, entry.d_name, maxcopy) < maxcopy) - unlink(pathname); - } + if (os_strlcpy(namep, entry.d_name, maxcopy) < maxcopy) + unlink(pathname); } closedir(dir); } @@ -236,6 +270,9 @@ struct wpa_ctrl *ctrl; char buf[128]; size_t len; +#ifdef CONFIG_CTRL_IFACE_UDP_REMOTE + struct hostent *h; +#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */ ctrl = os_malloc(sizeof(*ctrl)); if (ctrl == NULL) @@ -250,7 +287,11 @@ } ctrl->local.sin_family = AF_INET; +#ifdef CONFIG_CTRL_IFACE_UDP_REMOTE + ctrl->local.sin_addr.s_addr = INADDR_ANY; +#else /* CONFIG_CTRL_IFACE_UDP_REMOTE */ ctrl->local.sin_addr.s_addr = htonl((127 << 24) | 1); +#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */ if (bind(ctrl->s, (struct sockaddr *) &ctrl->local, sizeof(ctrl->local)) < 0) { close(ctrl->s); @@ -261,10 +302,48 @@ ctrl->dest.sin_family = AF_INET; ctrl->dest.sin_addr.s_addr = htonl((127 << 24) | 1); ctrl->dest.sin_port = htons(WPA_CTRL_IFACE_PORT); + +#ifdef CONFIG_CTRL_IFACE_UDP_REMOTE + if (ctrl_path) { + char *port, *name; + int port_id; + + name = os_strdup(ctrl_path); + if (name == NULL) { + close(ctrl->s); + os_free(ctrl); + return NULL; + } + port = os_strchr(name, ':'); + + if (port) { + port_id = atoi(&port[1]); + port[0] = '\0'; + } else + port_id = WPA_CTRL_IFACE_PORT; + + h = gethostbyname(name); + ctrl->remote_ip = os_strdup(name); + os_free(name); + if (h == NULL) { + perror("gethostbyname"); + close(ctrl->s); + os_free(ctrl->remote_ip); + os_free(ctrl); + return NULL; + } + ctrl->dest.sin_port = htons(port_id); + os_memcpy(h->h_addr, (char *) &ctrl->dest.sin_addr.s_addr, + h->h_length); + } else + ctrl->remote_ip = os_strdup("localhost"); +#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */ + if (connect(ctrl->s, (struct sockaddr *) &ctrl->dest, sizeof(ctrl->dest)) < 0) { perror("connect"); close(ctrl->s); + os_free(ctrl->remote_ip); os_free(ctrl); return NULL; } @@ -275,14 +354,31 @@ ctrl->cookie = os_strdup(buf); } + if (wpa_ctrl_request(ctrl, "IFNAME", 6, buf, &len, NULL) == 0) { + buf[len] = '\0'; + ctrl->remote_ifname = os_strdup(buf); + } + return ctrl; } +char * wpa_ctrl_get_remote_ifname(struct wpa_ctrl *ctrl) +{ +#define WPA_CTRL_MAX_PS_NAME 100 + static char ps[WPA_CTRL_MAX_PS_NAME] = {}; + os_snprintf(ps, WPA_CTRL_MAX_PS_NAME, "%s/%s", + ctrl->remote_ip, ctrl->remote_ifname); + return ps; +} + + void wpa_ctrl_close(struct wpa_ctrl *ctrl) { close(ctrl->s); os_free(ctrl->cookie); + os_free(ctrl->remote_ifname); + os_free(ctrl->remote_ip); os_free(ctrl); } @@ -295,6 +391,7 @@ void (*msg_cb)(char *msg, size_t len)) { struct timeval tv; + struct os_reltime started_at; int res; fd_set rfds; const char *_cmd; @@ -321,7 +418,30 @@ _cmd_len = cmd_len; } + errno = 0; + started_at.sec = 0; + started_at.usec = 0; +retry_send: if (send(ctrl->s, _cmd, _cmd_len, 0) < 0) { + if (errno == EAGAIN || errno == EBUSY || errno == EWOULDBLOCK) + { + /* + * Must be a non-blocking socket... Try for a bit + * longer before giving up. + */ + if (started_at.sec == 0) + os_get_reltime(&started_at); + else { + struct os_reltime n; + os_get_reltime(&n); + /* Try for a few seconds. */ + if (os_reltime_expired(&n, &started_at, 5)) + goto send_err; + } + os_sleep(1, 0); + goto retry_send; + } + send_err: os_free(cmd_buf); return -1; } diff -Nru wpa-1.0/src/common/wpa_ctrl.h wpa-2.1/src/common/wpa_ctrl.h --- wpa-1.0/src/common/wpa_ctrl.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/common/wpa_ctrl.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * wpa_supplicant/hostapd control interface library * Copyright (c) 2004-2006, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef WPA_CTRL_H @@ -50,10 +44,18 @@ #define WPA_EVENT_EAP_PEER_CERT "CTRL-EVENT-EAP-PEER-CERT " /** EAP TLS certificate chain validation error */ #define WPA_EVENT_EAP_TLS_CERT_ERROR "CTRL-EVENT-EAP-TLS-CERT-ERROR " +/** EAP status */ +#define WPA_EVENT_EAP_STATUS "CTRL-EVENT-EAP-STATUS " /** EAP authentication completed successfully */ #define WPA_EVENT_EAP_SUCCESS "CTRL-EVENT-EAP-SUCCESS " /** EAP authentication failed (EAP-Failure received) */ #define WPA_EVENT_EAP_FAILURE "CTRL-EVENT-EAP-FAILURE " +/** Network block temporarily disabled (e.g., due to authentication failure) */ +#define WPA_EVENT_TEMP_DISABLED "CTRL-EVENT-SSID-TEMP-DISABLED " +/** Temporarily disabled network block re-enabled */ +#define WPA_EVENT_REENABLED "CTRL-EVENT-SSID-REENABLED " +/** New scan started */ +#define WPA_EVENT_SCAN_STARTED "CTRL-EVENT-SCAN-STARTED " /** New scan results available */ #define WPA_EVENT_SCAN_RESULTS "CTRL-EVENT-SCAN-RESULTS " /** wpa_supplicant state change */ @@ -63,6 +65,17 @@ /** A BSS entry was removed (followed by BSS entry id and BSSID) */ #define WPA_EVENT_BSS_REMOVED "CTRL-EVENT-BSS-REMOVED " +/** RSN IBSS 4-way handshakes completed with specified peer */ +#define IBSS_RSN_COMPLETED "IBSS-RSN-COMPLETED " + +/** Notification of frequency conflict due to a concurrent operation. + * + * The indicated network is disabled and needs to be re-enabled before it can + * be used again. + */ +#define WPA_EVENT_FREQ_CONFLICT "CTRL-EVENT-FREQ-CONFLICT " +/** Frequency ranges that the driver recommends to avoid */ +#define WPA_EVENT_AVOID_FREQ "CTRL-EVENT-AVOID-FREQ " /** WPS overlap detected in PBC mode */ #define WPS_EVENT_OVERLAP "WPS-OVERLAP-DETECTED " /** Available WPS AP with active PBC found in scan results */ @@ -84,6 +97,10 @@ #define WPS_EVENT_SUCCESS "WPS-SUCCESS " /** WPS enrollment attempt timed out and was terminated */ #define WPS_EVENT_TIMEOUT "WPS-TIMEOUT " +/* PBC mode was activated */ +#define WPS_EVENT_ACTIVE "WPS-PBC-ACTIVE " +/* PBC mode was disabled */ +#define WPS_EVENT_DISABLE "WPS-PBC-DISABLE " #define WPS_EVENT_ENROLLEE_SEEN "WPS-ENROLLEE-SEEN " @@ -122,6 +139,8 @@ #define P2P_EVENT_PROV_DISC_PBC_REQ "P2P-PROV-DISC-PBC-REQ " /* parameters: */ #define P2P_EVENT_PROV_DISC_PBC_RESP "P2P-PROV-DISC-PBC-RESP " +/* parameters: */ +#define P2P_EVENT_PROV_DISC_FAILURE "P2P-PROV-DISC-FAILURE" /* parameters: */ #define P2P_EVENT_SERV_DISC_REQ "P2P-SERV-DISC-REQ " /* parameters: */ @@ -129,9 +148,28 @@ #define P2P_EVENT_INVITATION_RECEIVED "P2P-INVITATION-RECEIVED " #define P2P_EVENT_INVITATION_RESULT "P2P-INVITATION-RESULT " #define P2P_EVENT_FIND_STOPPED "P2P-FIND-STOPPED " +#define P2P_EVENT_PERSISTENT_PSK_FAIL "P2P-PERSISTENT-PSK-FAIL id=" +#define P2P_EVENT_PRESENCE_RESPONSE "P2P-PRESENCE-RESPONSE " +#define P2P_EVENT_NFC_BOTH_GO "P2P-NFC-BOTH-GO " +#define P2P_EVENT_NFC_PEER_CLIENT "P2P-NFC-PEER-CLIENT " +#define P2P_EVENT_NFC_WHILE_CLIENT "P2P-NFC-WHILE-CLIENT " + +/* parameters: */ +#define ESS_DISASSOC_IMMINENT "ESS-DISASSOC-IMMINENT " +#define P2P_EVENT_REMOVE_AND_REFORM_GROUP "P2P-REMOVE-AND-REFORM-GROUP " #define INTERWORKING_AP "INTERWORKING-AP " #define INTERWORKING_NO_MATCH "INTERWORKING-NO-MATCH " +#define INTERWORKING_ALREADY_CONNECTED "INTERWORKING-ALREADY-CONNECTED " + +#define GAS_RESPONSE_INFO "GAS-RESPONSE-INFO " +/* parameters: */ +#define GAS_QUERY_START "GAS-QUERY-START " +/* parameters: */ +#define GAS_QUERY_DONE "GAS-QUERY-DONE " + +#define EXT_RADIO_WORK_START "EXT-RADIO-WORK-START " +#define EXT_RADIO_WORK_TIMEOUT "EXT-RADIO-WORK-TIMEOUT " /* hostapd control interface - fixed message prefixes */ #define WPS_EVENT_PIN_NEEDED "WPS-PIN-NEEDED " @@ -144,6 +182,46 @@ #define AP_STA_CONNECTED "AP-STA-CONNECTED " #define AP_STA_DISCONNECTED "AP-STA-DISCONNECTED " +#define AP_REJECTED_MAX_STA "AP-REJECTED-MAX-STA " +#define AP_REJECTED_BLOCKED_STA "AP-REJECTED-BLOCKED-STA " + +#define AP_EVENT_ENABLED "AP-ENABLED " +#define AP_EVENT_DISABLED "AP-DISABLED " + +#define ACS_EVENT_STARTED "ACS-STARTED " +#define ACS_EVENT_COMPLETED "ACS-COMPLETED " +#define ACS_EVENT_FAILED "ACS-FAILED " + +#define DFS_EVENT_RADAR_DETECTED "DFS-RADAR-DETECTED " +#define DFS_EVENT_NEW_CHANNEL "DFS-NEW-CHANNEL " +#define DFS_EVENT_CAC_START "DFS-CAC-START " +#define DFS_EVENT_CAC_COMPLETED "DFS-CAC-COMPLETED " +#define DFS_EVENT_NOP_FINISHED "DFS-NOP-FINISHED " + +#define AP_CSA_FINISHED "AP-CSA-FINISHED " + +/* BSS command information masks */ + +#define WPA_BSS_MASK_ALL 0xFFFDFFFF +#define WPA_BSS_MASK_ID BIT(0) +#define WPA_BSS_MASK_BSSID BIT(1) +#define WPA_BSS_MASK_FREQ BIT(2) +#define WPA_BSS_MASK_BEACON_INT BIT(3) +#define WPA_BSS_MASK_CAPABILITIES BIT(4) +#define WPA_BSS_MASK_QUAL BIT(5) +#define WPA_BSS_MASK_NOISE BIT(6) +#define WPA_BSS_MASK_LEVEL BIT(7) +#define WPA_BSS_MASK_TSF BIT(8) +#define WPA_BSS_MASK_AGE BIT(9) +#define WPA_BSS_MASK_IE BIT(10) +#define WPA_BSS_MASK_FLAGS BIT(11) +#define WPA_BSS_MASK_SSID BIT(12) +#define WPA_BSS_MASK_WPS_SCAN BIT(13) +#define WPA_BSS_MASK_P2P_SCAN BIT(14) +#define WPA_BSS_MASK_INTERNETW BIT(15) +#define WPA_BSS_MASK_WIFI_DISPLAY BIT(16) +#define WPA_BSS_MASK_DELIM BIT(17) + /* wpa_supplicant/hostapd control interface access */ @@ -269,6 +347,8 @@ */ int wpa_ctrl_get_fd(struct wpa_ctrl *ctrl); +char * wpa_ctrl_get_remote_ifname(struct wpa_ctrl *ctrl); + #ifdef ANDROID /** * wpa_ctrl_cleanup() - Delete any local UNIX domain socket files that @@ -281,8 +361,11 @@ #endif /* ANDROID */ #ifdef CONFIG_CTRL_IFACE_UDP +/* Port range for multiple wpa_supplicant instances and multiple VIFs */ #define WPA_CTRL_IFACE_PORT 9877 +#define WPA_CTRL_IFACE_PORT_LIMIT 50 /* decremented from start */ #define WPA_GLOBAL_CTRL_IFACE_PORT 9878 +#define WPA_GLOBAL_CTRL_IFACE_PORT_LIMIT 20 /* incremented from start */ #endif /* CONFIG_CTRL_IFACE_UDP */ diff -Nru wpa-1.0/src/crypto/aes-cbc.c wpa-2.1/src/crypto/aes-cbc.c --- wpa-1.0/src/crypto/aes-cbc.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/crypto/aes-cbc.c 2014-02-04 11:23:35.000000000 +0000 @@ -3,14 +3,8 @@ * * Copyright (c) 2003-2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff -Nru wpa-1.0/src/crypto/aes-ccm.c wpa-2.1/src/crypto/aes-ccm.c --- wpa-1.0/src/crypto/aes-ccm.c 1970-01-01 00:00:00.000000000 +0000 +++ wpa-2.1/src/crypto/aes-ccm.c 2014-02-04 11:23:35.000000000 +0000 @@ -0,0 +1,212 @@ +/* + * Counter with CBC-MAC (CCM) with AES + * + * Copyright (c) 2010-2012, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "aes.h" +#include "aes_wrap.h" + + +static void xor_aes_block(u8 *dst, const u8 *src) +{ + u32 *d = (u32 *) dst; + u32 *s = (u32 *) src; + *d++ ^= *s++; + *d++ ^= *s++; + *d++ ^= *s++; + *d++ ^= *s++; +} + + +static void aes_ccm_auth_start(void *aes, size_t M, size_t L, const u8 *nonce, + const u8 *aad, size_t aad_len, size_t plain_len, + u8 *x) +{ + u8 aad_buf[2 * AES_BLOCK_SIZE]; + u8 b[AES_BLOCK_SIZE]; + + /* Authentication */ + /* B_0: Flags | Nonce N | l(m) */ + b[0] = aad_len ? 0x40 : 0 /* Adata */; + b[0] |= (((M - 2) / 2) /* M' */ << 3); + b[0] |= (L - 1) /* L' */; + os_memcpy(&b[1], nonce, 15 - L); + WPA_PUT_BE16(&b[AES_BLOCK_SIZE - L], plain_len); + + wpa_hexdump_key(MSG_EXCESSIVE, "CCM B_0", b, AES_BLOCK_SIZE); + aes_encrypt(aes, b, x); /* X_1 = E(K, B_0) */ + + if (!aad_len) + return; + + WPA_PUT_BE16(aad_buf, aad_len); + os_memcpy(aad_buf + 2, aad, aad_len); + os_memset(aad_buf + 2 + aad_len, 0, sizeof(aad_buf) - 2 - aad_len); + + xor_aes_block(aad_buf, x); + aes_encrypt(aes, aad_buf, x); /* X_2 = E(K, X_1 XOR B_1) */ + + if (aad_len > AES_BLOCK_SIZE - 2) { + xor_aes_block(&aad_buf[AES_BLOCK_SIZE], x); + /* X_3 = E(K, X_2 XOR B_2) */ + aes_encrypt(aes, &aad_buf[AES_BLOCK_SIZE], x); + } +} + + +static void aes_ccm_auth(void *aes, const u8 *data, size_t len, u8 *x) +{ + size_t last = len % AES_BLOCK_SIZE; + size_t i; + + for (i = 0; i < len / AES_BLOCK_SIZE; i++) { + /* X_i+1 = E(K, X_i XOR B_i) */ + xor_aes_block(x, data); + data += AES_BLOCK_SIZE; + aes_encrypt(aes, x, x); + } + if (last) { + /* XOR zero-padded last block */ + for (i = 0; i < last; i++) + x[i] ^= *data++; + aes_encrypt(aes, x, x); + } +} + + +static void aes_ccm_encr_start(size_t L, const u8 *nonce, u8 *a) +{ + /* A_i = Flags | Nonce N | Counter i */ + a[0] = L - 1; /* Flags = L' */ + os_memcpy(&a[1], nonce, 15 - L); +} + + +static void aes_ccm_encr(void *aes, size_t L, const u8 *in, size_t len, u8 *out, + u8 *a) +{ + size_t last = len % AES_BLOCK_SIZE; + size_t i; + + /* crypt = msg XOR (S_1 | S_2 | ... | S_n) */ + for (i = 1; i <= len / AES_BLOCK_SIZE; i++) { + WPA_PUT_BE16(&a[AES_BLOCK_SIZE - 2], i); + /* S_i = E(K, A_i) */ + aes_encrypt(aes, a, out); + xor_aes_block(out, in); + out += AES_BLOCK_SIZE; + in += AES_BLOCK_SIZE; + } + if (last) { + WPA_PUT_BE16(&a[AES_BLOCK_SIZE - 2], i); + aes_encrypt(aes, a, out); + /* XOR zero-padded last block */ + for (i = 0; i < last; i++) + *out++ ^= *in++; + } +} + + +static void aes_ccm_encr_auth(void *aes, size_t M, u8 *x, u8 *a, u8 *auth) +{ + size_t i; + u8 tmp[AES_BLOCK_SIZE]; + + wpa_hexdump_key(MSG_EXCESSIVE, "CCM T", x, M); + /* U = T XOR S_0; S_0 = E(K, A_0) */ + WPA_PUT_BE16(&a[AES_BLOCK_SIZE - 2], 0); + aes_encrypt(aes, a, tmp); + for (i = 0; i < M; i++) + auth[i] = x[i] ^ tmp[i]; + wpa_hexdump_key(MSG_EXCESSIVE, "CCM U", auth, M); +} + + +static void aes_ccm_decr_auth(void *aes, size_t M, u8 *a, const u8 *auth, u8 *t) +{ + size_t i; + u8 tmp[AES_BLOCK_SIZE]; + + wpa_hexdump_key(MSG_EXCESSIVE, "CCM U", auth, M); + /* U = T XOR S_0; S_0 = E(K, A_0) */ + WPA_PUT_BE16(&a[AES_BLOCK_SIZE - 2], 0); + aes_encrypt(aes, a, tmp); + for (i = 0; i < M; i++) + t[i] = auth[i] ^ tmp[i]; + wpa_hexdump_key(MSG_EXCESSIVE, "CCM T", t, M); +} + + +/* AES-CCM with fixed L=2 and aad_len <= 30 assumption */ +int aes_ccm_ae(const u8 *key, size_t key_len, const u8 *nonce, + size_t M, const u8 *plain, size_t plain_len, + const u8 *aad, size_t aad_len, u8 *crypt, u8 *auth) +{ + const size_t L = 2; + void *aes; + u8 x[AES_BLOCK_SIZE], a[AES_BLOCK_SIZE]; + + if (aad_len > 30 || M > AES_BLOCK_SIZE) + return -1; + + aes = aes_encrypt_init(key, key_len); + if (aes == NULL) + return -1; + + aes_ccm_auth_start(aes, M, L, nonce, aad, aad_len, plain_len, x); + aes_ccm_auth(aes, plain, plain_len, x); + + /* Encryption */ + aes_ccm_encr_start(L, nonce, a); + aes_ccm_encr(aes, L, plain, plain_len, crypt, a); + aes_ccm_encr_auth(aes, M, x, a, auth); + + aes_encrypt_deinit(aes); + + return 0; +} + + +/* AES-CCM with fixed L=2 and aad_len <= 30 assumption */ +int aes_ccm_ad(const u8 *key, size_t key_len, const u8 *nonce, + size_t M, const u8 *crypt, size_t crypt_len, + const u8 *aad, size_t aad_len, const u8 *auth, u8 *plain) +{ + const size_t L = 2; + void *aes; + u8 x[AES_BLOCK_SIZE], a[AES_BLOCK_SIZE]; + u8 t[AES_BLOCK_SIZE]; + + if (aad_len > 30 || M > AES_BLOCK_SIZE) + return -1; + + aes = aes_encrypt_init(key, key_len); + if (aes == NULL) + return -1; + + /* Decryption */ + aes_ccm_encr_start(L, nonce, a); + aes_ccm_decr_auth(aes, M, a, auth, t); + + /* plaintext = msg XOR (S_1 | S_2 | ... | S_n) */ + aes_ccm_encr(aes, L, crypt, crypt_len, plain, a); + + aes_ccm_auth_start(aes, M, L, nonce, aad, aad_len, crypt_len, x); + aes_ccm_auth(aes, plain, crypt_len, x); + + aes_encrypt_deinit(aes); + + if (os_memcmp(x, t, M) != 0) { + wpa_printf(MSG_EXCESSIVE, "CCM: Auth mismatch"); + return -1; + } + + return 0; +} diff -Nru wpa-1.0/src/crypto/aes-ctr.c wpa-2.1/src/crypto/aes-ctr.c --- wpa-1.0/src/crypto/aes-ctr.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/crypto/aes-ctr.c 2014-02-04 11:23:35.000000000 +0000 @@ -3,14 +3,8 @@ * * Copyright (c) 2003-2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff -Nru wpa-1.0/src/crypto/aes-eax.c wpa-2.1/src/crypto/aes-eax.c --- wpa-1.0/src/crypto/aes-eax.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/crypto/aes-eax.c 2014-02-04 11:23:35.000000000 +0000 @@ -3,14 +3,8 @@ * * Copyright (c) 2003-2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff -Nru wpa-1.0/src/crypto/aes-encblock.c wpa-2.1/src/crypto/aes-encblock.c --- wpa-1.0/src/crypto/aes-encblock.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/crypto/aes-encblock.c 2014-02-04 11:23:35.000000000 +0000 @@ -3,14 +3,8 @@ * * Copyright (c) 2003-2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff -Nru wpa-1.0/src/crypto/aes-gcm.c wpa-2.1/src/crypto/aes-gcm.c --- wpa-1.0/src/crypto/aes-gcm.c 1970-01-01 00:00:00.000000000 +0000 +++ wpa-2.1/src/crypto/aes-gcm.c 2014-02-04 11:23:35.000000000 +0000 @@ -0,0 +1,327 @@ +/* + * Galois/Counter Mode (GCM) and GMAC with AES + * + * Copyright (c) 2012, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "aes.h" +#include "aes_wrap.h" + +static void inc32(u8 *block) +{ + u32 val; + val = WPA_GET_BE32(block + AES_BLOCK_SIZE - 4); + val++; + WPA_PUT_BE32(block + AES_BLOCK_SIZE - 4, val); +} + + +static void xor_block(u8 *dst, const u8 *src) +{ + u32 *d = (u32 *) dst; + u32 *s = (u32 *) src; + *d++ ^= *s++; + *d++ ^= *s++; + *d++ ^= *s++; + *d++ ^= *s++; +} + + +static void shift_right_block(u8 *v) +{ + u32 val; + + val = WPA_GET_BE32(v + 12); + val >>= 1; + if (v[11] & 0x01) + val |= 0x80000000; + WPA_PUT_BE32(v + 12, val); + + val = WPA_GET_BE32(v + 8); + val >>= 1; + if (v[7] & 0x01) + val |= 0x80000000; + WPA_PUT_BE32(v + 8, val); + + val = WPA_GET_BE32(v + 4); + val >>= 1; + if (v[3] & 0x01) + val |= 0x80000000; + WPA_PUT_BE32(v + 4, val); + + val = WPA_GET_BE32(v); + val >>= 1; + WPA_PUT_BE32(v, val); +} + + +/* Multiplication in GF(2^128) */ +static void gf_mult(const u8 *x, const u8 *y, u8 *z) +{ + u8 v[16]; + int i, j; + + os_memset(z, 0, 16); /* Z_0 = 0^128 */ + os_memcpy(v, y, 16); /* V_0 = Y */ + + for (i = 0; i < 16; i++) { + for (j = 0; j < 8; j++) { + if (x[i] & BIT(7 - j)) { + /* Z_(i + 1) = Z_i XOR V_i */ + xor_block(z, v); + } else { + /* Z_(i + 1) = Z_i */ + } + + if (v[15] & 0x01) { + /* V_(i + 1) = (V_i >> 1) XOR R */ + shift_right_block(v); + /* R = 11100001 || 0^120 */ + v[0] ^= 0xe1; + } else { + /* V_(i + 1) = V_i >> 1 */ + shift_right_block(v); + } + } + } +} + + +static void ghash_start(u8 *y) +{ + /* Y_0 = 0^128 */ + os_memset(y, 0, 16); +} + + +static void ghash(const u8 *h, const u8 *x, size_t xlen, u8 *y) +{ + size_t m, i; + const u8 *xpos = x; + u8 tmp[16]; + + m = xlen / 16; + + for (i = 0; i < m; i++) { + /* Y_i = (Y^(i-1) XOR X_i) dot H */ + xor_block(y, xpos); + xpos += 16; + + /* dot operation: + * multiplication operation for binary Galois (finite) field of + * 2^128 elements */ + gf_mult(y, h, tmp); + os_memcpy(y, tmp, 16); + } + + if (x + xlen > xpos) { + /* Add zero padded last block */ + size_t last = x + xlen - xpos; + os_memcpy(tmp, xpos, last); + os_memset(tmp + last, 0, sizeof(tmp) - last); + + /* Y_i = (Y^(i-1) XOR X_i) dot H */ + xor_block(y, tmp); + + /* dot operation: + * multiplication operation for binary Galois (finite) field of + * 2^128 elements */ + gf_mult(y, h, tmp); + os_memcpy(y, tmp, 16); + } + + /* Return Y_m */ +} + + +static void aes_gctr(void *aes, const u8 *icb, const u8 *x, size_t xlen, u8 *y) +{ + size_t i, n, last; + u8 cb[AES_BLOCK_SIZE], tmp[AES_BLOCK_SIZE]; + const u8 *xpos = x; + u8 *ypos = y; + + if (xlen == 0) + return; + + n = xlen / 16; + + os_memcpy(cb, icb, AES_BLOCK_SIZE); + /* Full blocks */ + for (i = 0; i < n; i++) { + aes_encrypt(aes, cb, ypos); + xor_block(ypos, xpos); + xpos += AES_BLOCK_SIZE; + ypos += AES_BLOCK_SIZE; + inc32(cb); + } + + last = x + xlen - xpos; + if (last) { + /* Last, partial block */ + aes_encrypt(aes, cb, tmp); + for (i = 0; i < last; i++) + *ypos++ = *xpos++ ^ tmp[i]; + } +} + + +static void * aes_gcm_init_hash_subkey(const u8 *key, size_t key_len, u8 *H) +{ + void *aes; + + aes = aes_encrypt_init(key, key_len); + if (aes == NULL) + return NULL; + + /* Generate hash subkey H = AES_K(0^128) */ + os_memset(H, 0, AES_BLOCK_SIZE); + aes_encrypt(aes, H, H); + wpa_hexdump_key(MSG_EXCESSIVE, "Hash subkey H for GHASH", + H, AES_BLOCK_SIZE); + return aes; +} + + +static void aes_gcm_prepare_j0(const u8 *iv, size_t iv_len, const u8 *H, u8 *J0) +{ + u8 len_buf[16]; + + if (iv_len == 12) { + /* Prepare block J_0 = IV || 0^31 || 1 [len(IV) = 96] */ + os_memcpy(J0, iv, iv_len); + os_memset(J0 + iv_len, 0, AES_BLOCK_SIZE - iv_len); + J0[AES_BLOCK_SIZE - 1] = 0x01; + } else { + /* + * s = 128 * ceil(len(IV)/128) - len(IV) + * J_0 = GHASH_H(IV || 0^(s+64) || [len(IV)]_64) + */ + ghash_start(J0); + ghash(H, iv, iv_len, J0); + WPA_PUT_BE64(len_buf, 0); + WPA_PUT_BE64(len_buf + 8, iv_len * 8); + ghash(H, len_buf, sizeof(len_buf), J0); + } +} + + +static void aes_gcm_gctr(void *aes, const u8 *J0, const u8 *in, size_t len, + u8 *out) +{ + u8 J0inc[AES_BLOCK_SIZE]; + + if (len == 0) + return; + + os_memcpy(J0inc, J0, AES_BLOCK_SIZE); + inc32(J0inc); + aes_gctr(aes, J0inc, in, len, out); +} + + +static void aes_gcm_ghash(const u8 *H, const u8 *aad, size_t aad_len, + const u8 *crypt, size_t crypt_len, u8 *S) +{ + u8 len_buf[16]; + + /* + * u = 128 * ceil[len(C)/128] - len(C) + * v = 128 * ceil[len(A)/128] - len(A) + * S = GHASH_H(A || 0^v || C || 0^u || [len(A)]64 || [len(C)]64) + * (i.e., zero padded to block size A || C and lengths of each in bits) + */ + ghash_start(S); + ghash(H, aad, aad_len, S); + ghash(H, crypt, crypt_len, S); + WPA_PUT_BE64(len_buf, aad_len * 8); + WPA_PUT_BE64(len_buf + 8, crypt_len * 8); + ghash(H, len_buf, sizeof(len_buf), S); + + wpa_hexdump_key(MSG_EXCESSIVE, "S = GHASH_H(...)", S, 16); +} + + +/** + * aes_gcm_ae - GCM-AE_K(IV, P, A) + */ +int aes_gcm_ae(const u8 *key, size_t key_len, const u8 *iv, size_t iv_len, + const u8 *plain, size_t plain_len, + const u8 *aad, size_t aad_len, u8 *crypt, u8 *tag) +{ + u8 H[AES_BLOCK_SIZE]; + u8 J0[AES_BLOCK_SIZE]; + u8 S[16]; + void *aes; + + aes = aes_gcm_init_hash_subkey(key, key_len, H); + if (aes == NULL) + return -1; + + aes_gcm_prepare_j0(iv, iv_len, H, J0); + + /* C = GCTR_K(inc_32(J_0), P) */ + aes_gcm_gctr(aes, J0, plain, plain_len, crypt); + + aes_gcm_ghash(H, aad, aad_len, crypt, plain_len, S); + + /* T = MSB_t(GCTR_K(J_0, S)) */ + aes_gctr(aes, J0, S, sizeof(S), tag); + + /* Return (C, T) */ + + aes_encrypt_deinit(aes); + + return 0; +} + + +/** + * aes_gcm_ad - GCM-AD_K(IV, C, A, T) + */ +int aes_gcm_ad(const u8 *key, size_t key_len, const u8 *iv, size_t iv_len, + const u8 *crypt, size_t crypt_len, + const u8 *aad, size_t aad_len, const u8 *tag, u8 *plain) +{ + u8 H[AES_BLOCK_SIZE]; + u8 J0[AES_BLOCK_SIZE]; + u8 S[16], T[16]; + void *aes; + + aes = aes_gcm_init_hash_subkey(key, key_len, H); + if (aes == NULL) + return -1; + + aes_gcm_prepare_j0(iv, iv_len, H, J0); + + /* P = GCTR_K(inc_32(J_0), C) */ + aes_gcm_gctr(aes, J0, crypt, crypt_len, plain); + + aes_gcm_ghash(H, aad, aad_len, crypt, crypt_len, S); + + /* T' = MSB_t(GCTR_K(J_0, S)) */ + aes_gctr(aes, J0, S, sizeof(S), T); + + aes_encrypt_deinit(aes); + + if (os_memcmp(tag, T, 16) != 0) { + wpa_printf(MSG_EXCESSIVE, "GCM: Tag mismatch"); + return -1; + } + + return 0; +} + + +int aes_gmac(const u8 *key, size_t key_len, const u8 *iv, size_t iv_len, + const u8 *aad, size_t aad_len, u8 *tag) +{ + return aes_gcm_ae(key, key_len, iv, iv_len, NULL, 0, aad, aad_len, NULL, + tag); +} diff -Nru wpa-1.0/src/crypto/aes.h wpa-2.1/src/crypto/aes.h --- wpa-1.0/src/crypto/aes.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/crypto/aes.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * AES functions * Copyright (c) 2003-2006, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef AES_H diff -Nru wpa-1.0/src/crypto/aes_i.h wpa-2.1/src/crypto/aes_i.h --- wpa-1.0/src/crypto/aes_i.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/crypto/aes_i.h 2014-02-04 11:23:35.000000000 +0000 @@ -1,15 +1,9 @@ /* * AES (Rijndael) cipher - * Copyright (c) 2003-2005, Jouni Malinen + * Copyright (c) 2003-2012, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef AES_I_H @@ -50,6 +44,10 @@ #define TE432(i) (Te4[((i) >> 8) & 0xff] & 0x00ff0000) #define TE443(i) (Te4[(i) & 0xff] & 0x0000ff00) #define TE414(i) (Te4[((i) >> 24) & 0xff] & 0x000000ff) +#define TE411(i) (Te4[((i) >> 24) & 0xff] & 0xff000000) +#define TE422(i) (Te4[((i) >> 16) & 0xff] & 0x00ff0000) +#define TE433(i) (Te4[((i) >> 8) & 0xff] & 0x0000ff00) +#define TE444(i) (Te4[(i) & 0xff] & 0x000000ff) #define TE4(i) (Te4[(i)] & 0x000000ff) #define TD0(i) Td0[((i) >> 24) & 0xff] @@ -86,6 +84,10 @@ #define TE432(i) (Te0[((i) >> 8) & 0xff] & 0x00ff0000) #define TE443(i) (Te0[(i) & 0xff] & 0x0000ff00) #define TE414(i) ((Te0[((i) >> 24) & 0xff] >> 8) & 0x000000ff) +#define TE411(i) ((Te0[((i) >> 24) & 0xff] << 8) & 0xff000000) +#define TE422(i) (Te0[((i) >> 16) & 0xff] & 0x00ff0000) +#define TE433(i) (Te0[((i) >> 8) & 0xff] & 0x0000ff00) +#define TE444(i) ((Te0[(i) & 0xff] >> 8) & 0x000000ff) #define TE4(i) ((Te0[(i)] >> 8) & 0x000000ff) #define TD0(i) Td0[((i) >> 24) & 0xff] @@ -115,8 +117,9 @@ (ct)[2] = (u8)((st) >> 8); (ct)[3] = (u8)(st); } #endif -#define AES_PRIV_SIZE (4 * 44) +#define AES_PRIV_SIZE (4 * 4 * 15 + 4) +#define AES_PRIV_NR_POS (4 * 15) -void rijndaelKeySetupEnc(u32 rk[/*44*/], const u8 cipherKey[]); +int rijndaelKeySetupEnc(u32 rk[], const u8 cipherKey[], int keyBits); #endif /* AES_I_H */ diff -Nru wpa-1.0/src/crypto/aes-internal.c wpa-2.1/src/crypto/aes-internal.c --- wpa-1.0/src/crypto/aes-internal.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/crypto/aes-internal.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,23 +2,16 @@ * AES (Rijndael) cipher * * Modifications to public domain implementation: - * - support only 128-bit keys * - cleanup * - use C pre-processor to make it easier to change S table access * - added option (AES_SMALL_TABLES) for reducing code size by about 8 kB at * cost of reduced throughput (quite small difference on Pentium 4, * 10-25% when using -O1 or -O2 optimization) * - * Copyright (c) 2003-2005, Jouni Malinen + * Copyright (c) 2003-2012, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -783,7 +776,7 @@ * * @return the number of rounds for the given cipher key size. */ -void rijndaelKeySetupEnc(u32 rk[/*44*/], const u8 cipherKey[]) +int rijndaelKeySetupEnc(u32 rk[], const u8 cipherKey[], int keyBits) { int i; u32 temp; @@ -792,14 +785,61 @@ rk[1] = GETU32(cipherKey + 4); rk[2] = GETU32(cipherKey + 8); rk[3] = GETU32(cipherKey + 12); - for (i = 0; i < 10; i++) { - temp = rk[3]; - rk[4] = rk[0] ^ - TE421(temp) ^ TE432(temp) ^ TE443(temp) ^ TE414(temp) ^ - RCON(i); - rk[5] = rk[1] ^ rk[4]; - rk[6] = rk[2] ^ rk[5]; - rk[7] = rk[3] ^ rk[6]; - rk += 4; + + if (keyBits == 128) { + for (i = 0; i < 10; i++) { + temp = rk[3]; + rk[4] = rk[0] ^ TE421(temp) ^ TE432(temp) ^ + TE443(temp) ^ TE414(temp) ^ RCON(i); + rk[5] = rk[1] ^ rk[4]; + rk[6] = rk[2] ^ rk[5]; + rk[7] = rk[3] ^ rk[6]; + rk += 4; + } + return 10; + } + + rk[4] = GETU32(cipherKey + 16); + rk[5] = GETU32(cipherKey + 20); + + if (keyBits == 192) { + for (i = 0; i < 8; i++) { + temp = rk[5]; + rk[6] = rk[0] ^ TE421(temp) ^ TE432(temp) ^ + TE443(temp) ^ TE414(temp) ^ RCON(i); + rk[7] = rk[1] ^ rk[6]; + rk[8] = rk[2] ^ rk[7]; + rk[9] = rk[3] ^ rk[8]; + if (i == 7) + return 12; + rk[10] = rk[4] ^ rk[9]; + rk[11] = rk[5] ^ rk[10]; + rk += 6; + } } + + rk[6] = GETU32(cipherKey + 24); + rk[7] = GETU32(cipherKey + 28); + + if (keyBits == 256) { + for (i = 0; i < 7; i++) { + temp = rk[7]; + rk[8] = rk[0] ^ TE421(temp) ^ TE432(temp) ^ + TE443(temp) ^ TE414(temp) ^ RCON(i); + rk[9] = rk[1] ^ rk[8]; + rk[10] = rk[2] ^ rk[9]; + rk[11] = rk[3] ^ rk[10]; + if (i == 6) + return 14; + temp = rk[11]; + rk[12] = rk[4] ^ TE411(temp) ^ TE422(temp) ^ + TE433(temp) ^ TE444(temp); + rk[13] = rk[5] ^ rk[12]; + rk[14] = rk[6] ^ rk[13]; + rk[15] = rk[7] ^ rk[14]; + rk += 8; + } + } + + return -1; } diff -Nru wpa-1.0/src/crypto/aes-internal-dec.c wpa-2.1/src/crypto/aes-internal-dec.c --- wpa-1.0/src/crypto/aes-internal-dec.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/crypto/aes-internal-dec.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,23 +2,16 @@ * AES (Rijndael) cipher - decrypt * * Modifications to public domain implementation: - * - support only 128-bit keys * - cleanup * - use C pre-processor to make it easier to change S table access * - added option (AES_SMALL_TABLES) for reducing code size by about 8 kB at * cost of reduced throughput (quite small difference on Pentium 4, * 10-25% when using -O1 or -O2 optimization) * - * Copyright (c) 2003-2005, Jouni Malinen + * Copyright (c) 2003-2012, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -32,13 +25,15 @@ * * @return the number of rounds for the given cipher key size. */ -void rijndaelKeySetupDec(u32 rk[/*44*/], const u8 cipherKey[]) +static int rijndaelKeySetupDec(u32 rk[], const u8 cipherKey[], int keyBits) { - int Nr = 10, i, j; + int Nr, i, j; u32 temp; /* expand the cipher key: */ - rijndaelKeySetupEnc(rk, cipherKey); + Nr = rijndaelKeySetupEnc(rk, cipherKey, keyBits); + if (Nr < 0) + return Nr; /* invert the order of the round keys: */ for (i = 0, j = 4*Nr; i < j; i += 4, j -= 4) { temp = rk[i ]; rk[i ] = rk[j ]; rk[j ] = temp; @@ -57,24 +52,30 @@ TD3_(TE4((rk[j] ) & 0xff)); } } + + return Nr; } void * aes_decrypt_init(const u8 *key, size_t len) { u32 *rk; - if (len != 16) - return NULL; + int res; rk = os_malloc(AES_PRIV_SIZE); if (rk == NULL) return NULL; - rijndaelKeySetupDec(rk, key); + res = rijndaelKeySetupDec(rk, key, len * 8); + if (res < 0) { + os_free(rk); + return NULL; + } + rk[AES_PRIV_NR_POS] = res; return rk; } -static void rijndaelDecrypt(const u32 rk[/*44*/], const u8 ct[16], u8 pt[16]) +static void rijndaelDecrypt(const u32 rk[/*44*/], int Nr, const u8 ct[16], + u8 pt[16]) { u32 s0, s1, s2, s3, t0, t1, t2, t3; - const int Nr = 10; #ifndef FULL_UNROLL int r; #endif /* ?FULL_UNROLL */ @@ -105,6 +106,14 @@ ROUND(7,t,s); ROUND(8,s,t); ROUND(9,t,s); + if (Nr > 10) { + ROUND(10,s,t); + ROUND(11,t,s); + if (Nr > 12) { + ROUND(12,s,t); + ROUND(13,t,s); + } + } rk += Nr << 2; @@ -140,7 +149,8 @@ void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain) { - rijndaelDecrypt(ctx, crypt, plain); + u32 *rk = ctx; + rijndaelDecrypt(ctx, rk[AES_PRIV_NR_POS], crypt, plain); } diff -Nru wpa-1.0/src/crypto/aes-internal-enc.c wpa-2.1/src/crypto/aes-internal-enc.c --- wpa-1.0/src/crypto/aes-internal-enc.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/crypto/aes-internal-enc.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,23 +2,16 @@ * AES (Rijndael) cipher - encrypt * * Modifications to public domain implementation: - * - support only 128-bit keys * - cleanup * - use C pre-processor to make it easier to change S table access * - added option (AES_SMALL_TABLES) for reducing code size by about 8 kB at * cost of reduced throughput (quite small difference on Pentium 4, * 10-25% when using -O1 or -O2 optimization) * - * Copyright (c) 2003-2005, Jouni Malinen + * Copyright (c) 2003-2012, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -27,10 +20,9 @@ #include "crypto.h" #include "aes_i.h" -void rijndaelEncrypt(const u32 rk[/*44*/], const u8 pt[16], u8 ct[16]) +static void rijndaelEncrypt(const u32 rk[], int Nr, const u8 pt[16], u8 ct[16]) { u32 s0, s1, s2, s3, t0, t1, t2, t3; - const int Nr = 10; #ifndef FULL_UNROLL int r; #endif /* ?FULL_UNROLL */ @@ -61,6 +53,14 @@ ROUND(7,t,s); ROUND(8,s,t); ROUND(9,t,s); + if (Nr > 10) { + ROUND(10,s,t); + ROUND(11,t,s); + if (Nr > 12) { + ROUND(12,s,t); + ROUND(13,t,s); + } + } rk += Nr << 2; @@ -98,19 +98,24 @@ void * aes_encrypt_init(const u8 *key, size_t len) { u32 *rk; - if (len != 16) - return NULL; + int res; rk = os_malloc(AES_PRIV_SIZE); if (rk == NULL) return NULL; - rijndaelKeySetupEnc(rk, key); + res = rijndaelKeySetupEnc(rk, key, len * 8); + if (res < 0) { + os_free(rk); + return NULL; + } + rk[AES_PRIV_NR_POS] = res; return rk; } void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt) { - rijndaelEncrypt(ctx, plain, crypt); + u32 *rk = ctx; + rijndaelEncrypt(ctx, rk[AES_PRIV_NR_POS], plain, crypt); } diff -Nru wpa-1.0/src/crypto/aes-omac1.c wpa-2.1/src/crypto/aes-omac1.c --- wpa-1.0/src/crypto/aes-omac1.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/crypto/aes-omac1.c 2014-02-04 11:23:35.000000000 +0000 @@ -3,14 +3,8 @@ * * Copyright (c) 2003-2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff -Nru wpa-1.0/src/crypto/aes-unwrap.c wpa-2.1/src/crypto/aes-unwrap.c --- wpa-1.0/src/crypto/aes-unwrap.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/crypto/aes-unwrap.c 2014-02-04 11:23:35.000000000 +0000 @@ -3,14 +3,8 @@ * * Copyright (c) 2003-2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff -Nru wpa-1.0/src/crypto/aes-wrap.c wpa-2.1/src/crypto/aes-wrap.c --- wpa-1.0/src/crypto/aes-wrap.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/crypto/aes-wrap.c 2014-02-04 11:23:35.000000000 +0000 @@ -3,14 +3,8 @@ * * Copyright (c) 2003-2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff -Nru wpa-1.0/src/crypto/aes_wrap.h wpa-2.1/src/crypto/aes_wrap.h --- wpa-1.0/src/crypto/aes_wrap.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/crypto/aes_wrap.h 2014-02-04 11:23:35.000000000 +0000 @@ -6,17 +6,13 @@ * - AES-128 CTR mode encryption * - AES-128 EAX mode encryption/decryption * - AES-128 CBC + * - AES-GCM + * - AES-CCM * - * Copyright (c) 2003-2007, Jouni Malinen + * Copyright (c) 2003-2012, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef AES_WRAP_H @@ -44,5 +40,25 @@ size_t data_len); int __must_check aes_128_cbc_decrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len); +int __must_check aes_gcm_ae(const u8 *key, size_t key_len, + const u8 *iv, size_t iv_len, + const u8 *plain, size_t plain_len, + const u8 *aad, size_t aad_len, + u8 *crypt, u8 *tag); +int __must_check aes_gcm_ad(const u8 *key, size_t key_len, + const u8 *iv, size_t iv_len, + const u8 *crypt, size_t crypt_len, + const u8 *aad, size_t aad_len, const u8 *tag, + u8 *plain); +int __must_check aes_gmac(const u8 *key, size_t key_len, + const u8 *iv, size_t iv_len, + const u8 *aad, size_t aad_len, u8 *tag); +int __must_check aes_ccm_ae(const u8 *key, size_t key_len, const u8 *nonce, + size_t M, const u8 *plain, size_t plain_len, + const u8 *aad, size_t aad_len, u8 *crypt, u8 *auth); +int __must_check aes_ccm_ad(const u8 *key, size_t key_len, const u8 *nonce, + size_t M, const u8 *crypt, size_t crypt_len, + const u8 *aad, size_t aad_len, const u8 *auth, + u8 *plain); #endif /* AES_WRAP_H */ diff -Nru wpa-1.0/src/crypto/crypto_cryptoapi.c wpa-2.1/src/crypto/crypto_cryptoapi.c --- wpa-1.0/src/crypto/crypto_cryptoapi.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/crypto/crypto_cryptoapi.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * Crypto wrapper for Microsoft CryptoAPI * Copyright (c) 2005-2009, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff -Nru wpa-1.0/src/crypto/crypto_gnutls.c wpa-2.1/src/crypto/crypto_gnutls.c --- wpa-1.0/src/crypto/crypto_gnutls.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/crypto/crypto_gnutls.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * WPA Supplicant / wrapper functions for libgcrypt * Copyright (c) 2004-2009, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff -Nru wpa-1.0/src/crypto/crypto.h wpa-2.1/src/crypto/crypto.h --- wpa-1.0/src/crypto/crypto.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/crypto/crypto.h 2014-02-04 11:23:35.000000000 +0000 @@ -1,15 +1,9 @@ /* - * WPA Supplicant / wrapper functions for crypto libraries - * Copyright (c) 2004-2009, Jouni Malinen + * Wrapper functions for crypto libraries + * Copyright (c) 2004-2013, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. * * This file defines the cryptographic functions that need to be implemented * for wpa_supplicant and hostapd. When TLS is not used, internal @@ -47,21 +41,6 @@ */ int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac); -#ifdef CONFIG_FIPS -/** - * md5_vector_non_fips_allow - MD5 hash for data vector (non-FIPS use allowed) - * @num_elem: Number of elements in the data vector - * @addr: Pointers to the data areas - * @len: Lengths of the data blocks - * @mac: Buffer for the hash - * Returns: 0 on success, -1 on failure - */ -int md5_vector_non_fips_allow(size_t num_elem, const u8 *addr[], - const size_t *len, u8 *mac); -#else /* CONFIG_FIPS */ -#define md5_vector_non_fips_allow md5_vector -#endif /* CONFIG_FIPS */ - /** * sha1_vector - SHA-1 hash for data vector @@ -155,7 +134,8 @@ enum crypto_hash_alg { CRYPTO_HASH_ALG_MD5, CRYPTO_HASH_ALG_SHA1, - CRYPTO_HASH_ALG_HMAC_MD5, CRYPTO_HASH_ALG_HMAC_SHA1 + CRYPTO_HASH_ALG_HMAC_MD5, CRYPTO_HASH_ALG_HMAC_SHA1, + CRYPTO_HASH_ALG_SHA256, CRYPTO_HASH_ALG_HMAC_SHA256 }; struct crypto_hash; @@ -466,4 +446,340 @@ int rc4_skip(const u8 *key, size_t keylen, size_t skip, u8 *data, size_t data_len); +/** + * crypto_get_random - Generate cryptographically strong pseudy-random bytes + * @buf: Buffer for data + * @len: Number of bytes to generate + * Returns: 0 on success, -1 on failure + * + * If the PRNG does not have enough entropy to ensure unpredictable byte + * sequence, this functions must return -1. + */ +int crypto_get_random(void *buf, size_t len); + + +/** + * struct crypto_bignum - bignum + * + * Internal data structure for bignum implementation. The contents is specific + * to the used crypto library. + */ +struct crypto_bignum; + +/** + * crypto_bignum_init - Allocate memory for bignum + * Returns: Pointer to allocated bignum or %NULL on failure + */ +struct crypto_bignum * crypto_bignum_init(void); + +/** + * crypto_bignum_init_set - Allocate memory for bignum and set the value + * @buf: Buffer with unsigned binary value + * @len: Length of buf in octets + * Returns: Pointer to allocated bignum or %NULL on failure + */ +struct crypto_bignum * crypto_bignum_init_set(const u8 *buf, size_t len); + +/** + * crypto_bignum_deinit - Free bignum + * @n: Bignum from crypto_bignum_init() or crypto_bignum_init_set() + * @clear: Whether to clear the value from memory + */ +void crypto_bignum_deinit(struct crypto_bignum *n, int clear); + +/** + * crypto_bignum_to_bin - Set binary buffer to unsigned bignum + * @a: Bignum + * @buf: Buffer for the binary number + * @len: Length of @buf in octets + * @padlen: Length in octets to pad the result to or 0 to indicate no padding + * Returns: Number of octets written on success, -1 on failure + */ +int crypto_bignum_to_bin(const struct crypto_bignum *a, + u8 *buf, size_t buflen, size_t padlen); + +/** + * crypto_bignum_add - c = a + b + * @a: Bignum + * @b: Bignum + * @c: Bignum; used to store the result of a + b + * Returns: 0 on success, -1 on failure + */ +int crypto_bignum_add(const struct crypto_bignum *a, + const struct crypto_bignum *b, + struct crypto_bignum *c); + +/** + * crypto_bignum_mod - c = a % b + * @a: Bignum + * @b: Bignum + * @c: Bignum; used to store the result of a % b + * Returns: 0 on success, -1 on failure + */ +int crypto_bignum_mod(const struct crypto_bignum *a, + const struct crypto_bignum *b, + struct crypto_bignum *c); + +/** + * crypto_bignum_exptmod - Modular exponentiation: d = a^b (mod c) + * @a: Bignum; base + * @b: Bignum; exponent + * @c: Bignum; modulus + * @d: Bignum; used to store the result of a^b (mod c) + * Returns: 0 on success, -1 on failure + */ +int crypto_bignum_exptmod(const struct crypto_bignum *a, + const struct crypto_bignum *b, + const struct crypto_bignum *c, + struct crypto_bignum *d); + +/** + * crypto_bignum_rshift - b = a >> n + * @a: Bignum + * @n: Number of bits to shift + * @b: Bignum; used to store the result of a >> n + * Returns: 0 on success, -1 on failure + */ +int crypto_bignum_rshift(const struct crypto_bignum *a, int n, + struct crypto_bignum *b); + +/** + * crypto_bignum_inverse - Inverse a bignum so that a * c = 1 (mod b) + * @a: Bignum + * @b: Bignum + * @c: Bignum; used to store the result + * Returns: 0 on success, -1 on failure + */ +int crypto_bignum_inverse(const struct crypto_bignum *a, + const struct crypto_bignum *b, + struct crypto_bignum *c); + +/** + * crypto_bignum_sub - c = a - b + * @a: Bignum + * @b: Bignum + * @c: Bignum; used to store the result of a - b + * Returns: 0 on success, -1 on failure + */ +int crypto_bignum_sub(const struct crypto_bignum *a, + const struct crypto_bignum *b, + struct crypto_bignum *c); + +/** + * crypto_bignum_div - c = a / b + * @a: Bignum + * @b: Bignum + * @c: Bignum; used to store the result of a / b + * Returns: 0 on success, -1 on failure + */ +int crypto_bignum_div(const struct crypto_bignum *a, + const struct crypto_bignum *b, + struct crypto_bignum *c); + +/** + * crypto_bignum_mulmod - d = a * b (mod c) + * @a: Bignum + * @b: Bignum + * @c: Bignum + * @d: Bignum; used to store the result of (a * b) % c + * Returns: 0 on success, -1 on failure + */ +int crypto_bignum_mulmod(const struct crypto_bignum *a, + const struct crypto_bignum *b, + const struct crypto_bignum *c, + struct crypto_bignum *d); + +/** + * crypto_bignum_cmp - Compare two bignums + * @a: Bignum + * @b: Bignum + * Returns: -1 if a < b, 0 if a == b, or 1 if a > b + */ +int crypto_bignum_cmp(const struct crypto_bignum *a, + const struct crypto_bignum *b); + +/** + * crypto_bignum_bits - Get size of a bignum in bits + * @a: Bignum + * Returns: Number of bits in the bignum + */ +int crypto_bignum_bits(const struct crypto_bignum *a); + +/** + * crypto_bignum_is_zero - Is the given bignum zero + * @a: Bignum + * Returns: 1 if @a is zero or 0 if not + */ +int crypto_bignum_is_zero(const struct crypto_bignum *a); + +/** + * crypto_bignum_is_one - Is the given bignum one + * @a: Bignum + * Returns: 1 if @a is one or 0 if not + */ +int crypto_bignum_is_one(const struct crypto_bignum *a); + +/** + * struct crypto_ec - Elliptic curve context + * + * Internal data structure for EC implementation. The contents is specific + * to the used crypto library. + */ +struct crypto_ec; + +/** + * crypto_ec_init - Initialize elliptic curve context + * @group: Identifying number for the ECC group (IANA "Group Description" + * attribute registrty for RFC 2409) + * Returns: Pointer to EC context or %NULL on failure + */ +struct crypto_ec * crypto_ec_init(int group); + +/** + * crypto_ec_deinit - Deinitialize elliptic curve context + * @e: EC context from crypto_ec_init() + */ +void crypto_ec_deinit(struct crypto_ec *e); + +/** + * crypto_ec_prime_len - Get length of the prime in octets + * @e: EC context from crypto_ec_init() + * Returns: Length of the prime defining the group + */ +size_t crypto_ec_prime_len(struct crypto_ec *e); + +/** + * crypto_ec_prime_len_bits - Get length of the prime in bits + * @e: EC context from crypto_ec_init() + * Returns: Length of the prime defining the group in bits + */ +size_t crypto_ec_prime_len_bits(struct crypto_ec *e); + +/** + * crypto_ec_get_prime - Get prime defining an EC group + * @e: EC context from crypto_ec_init() + * Returns: Prime (bignum) defining the group + */ +const struct crypto_bignum * crypto_ec_get_prime(struct crypto_ec *e); + +/** + * crypto_ec_get_order - Get order of an EC group + * @e: EC context from crypto_ec_init() + * Returns: Order (bignum) of the group + */ +const struct crypto_bignum * crypto_ec_get_order(struct crypto_ec *e); + +/** + * struct crypto_ec_point - Elliptic curve point + * + * Internal data structure for EC implementation to represent a point. The + * contents is specific to the used crypto library. + */ +struct crypto_ec_point; + +/** + * crypto_ec_point_init - Initialize data for an EC point + * @e: EC context from crypto_ec_init() + * Returns: Pointer to EC point data or %NULL on failure + */ +struct crypto_ec_point * crypto_ec_point_init(struct crypto_ec *e); + +/** + * crypto_ec_point_deinit - Deinitialize EC point data + * @p: EC point data from crypto_ec_point_init() + * @clear: Whether to clear the EC point value from memory + */ +void crypto_ec_point_deinit(struct crypto_ec_point *p, int clear); + +/** + * crypto_ec_point_to_bin - Write EC point value as binary data + * @e: EC context from crypto_ec_init() + * @p: EC point data from crypto_ec_point_init() + * @x: Buffer for writing the binary data for x coordinate or %NULL if not used + * @y: Buffer for writing the binary data for y coordinate or %NULL if not used + * Returns: 0 on success, -1 on failure + * + * This function can be used to write an EC point as binary data in a format + * that has the x and y coordinates in big endian byte order fields padded to + * the length of the prime defining the group. + */ +int crypto_ec_point_to_bin(struct crypto_ec *e, + const struct crypto_ec_point *point, u8 *x, u8 *y); + +/** + * crypto_ec_point_from_bin - Create EC point from binary data + * @e: EC context from crypto_ec_init() + * @val: Binary data to read the EC point from + * Returns: Pointer to EC point data or %NULL on failure + * + * This function readers x and y coordinates of the EC point from the provided + * buffer assuming the values are in big endian byte order with fields padded to + * the length of the prime defining the group. + */ +struct crypto_ec_point * crypto_ec_point_from_bin(struct crypto_ec *e, + const u8 *val); + +/** + * crypto_bignum_add - c = a + b + * @e: EC context from crypto_ec_init() + * @a: Bignum + * @b: Bignum + * @c: Bignum; used to store the result of a + b + * Returns: 0 on success, -1 on failure + */ +int crypto_ec_point_add(struct crypto_ec *e, const struct crypto_ec_point *a, + const struct crypto_ec_point *b, + struct crypto_ec_point *c); + +/** + * crypto_bignum_mul - res = b * p + * @e: EC context from crypto_ec_init() + * @p: EC point + * @b: Bignum + * @res: EC point; used to store the result of b * p + * Returns: 0 on success, -1 on failure + */ +int crypto_ec_point_mul(struct crypto_ec *e, const struct crypto_ec_point *p, + const struct crypto_bignum *b, + struct crypto_ec_point *res); + +/** + * crypto_ec_point_invert - Compute inverse of an EC point + * @e: EC context from crypto_ec_init() + * @p: EC point to invert (and result of the operation) + * Returns: 0 on success, -1 on failure + */ +int crypto_ec_point_invert(struct crypto_ec *e, struct crypto_ec_point *p); + +/** + * crypto_ec_point_solve_y_coord - Solve y coordinate for an x coordinate + * @e: EC context from crypto_ec_init() + * @p: EC point to use for the returning the result + * @x: x coordinate + * @y_bit: y-bit (0 or 1) for selecting the y value to use + * Returns: 0 on success, -1 on failure + */ +int crypto_ec_point_solve_y_coord(struct crypto_ec *e, + struct crypto_ec_point *p, + const struct crypto_bignum *x, int y_bit); + +/** + * crypto_ec_point_is_at_infinity - Check whether EC point is neutral element + * @e: EC context from crypto_ec_init() + * @p: EC point + * Returns: 1 if the specified EC point is the neutral element of the group or + * 0 if not + */ +int crypto_ec_point_is_at_infinity(struct crypto_ec *e, + const struct crypto_ec_point *p); + +/** + * crypto_ec_point_is_on_curve - Check whether EC point is on curve + * @e: EC context from crypto_ec_init() + * @p: EC point + * Returns: 1 if the specified EC point is on the curve or 0 if not + */ +int crypto_ec_point_is_on_curve(struct crypto_ec *e, + const struct crypto_ec_point *p); + #endif /* CRYPTO_H */ diff -Nru wpa-1.0/src/crypto/crypto_internal.c wpa-2.1/src/crypto/crypto_internal.c --- wpa-1.0/src/crypto/crypto_internal.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/crypto/crypto_internal.c 2014-02-04 11:23:35.000000000 +0000 @@ -1,21 +1,16 @@ /* * Crypto wrapper for internal crypto implementation - * Copyright (c) 2006-2009, Jouni Malinen + * Copyright (c) 2006-2011, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" #include "common.h" #include "crypto.h" +#include "sha256_i.h" #include "sha1_i.h" #include "md5_i.h" @@ -24,6 +19,9 @@ union { struct MD5Context md5; struct SHA1Context sha1; +#ifdef CONFIG_SHA256 + struct sha256_state sha256; +#endif /* CONFIG_SHA256 */ } u; u8 key[64]; size_t key_len; @@ -35,7 +33,7 @@ { struct crypto_hash *ctx; u8 k_pad[64]; - u8 tk[20]; + u8 tk[32]; size_t i; ctx = os_zalloc(sizeof(*ctx)); @@ -51,6 +49,11 @@ case CRYPTO_HASH_ALG_SHA1: SHA1Init(&ctx->u.sha1); break; +#ifdef CONFIG_SHA256 + case CRYPTO_HASH_ALG_SHA256: + sha256_init(&ctx->u.sha256); + break; +#endif /* CONFIG_SHA256 */ case CRYPTO_HASH_ALG_HMAC_MD5: if (key_len > sizeof(k_pad)) { MD5Init(&ctx->u.md5); @@ -89,6 +92,27 @@ SHA1Init(&ctx->u.sha1); SHA1Update(&ctx->u.sha1, k_pad, sizeof(k_pad)); break; +#ifdef CONFIG_SHA256 + case CRYPTO_HASH_ALG_HMAC_SHA256: + if (key_len > sizeof(k_pad)) { + sha256_init(&ctx->u.sha256); + sha256_process(&ctx->u.sha256, key, key_len); + sha256_done(&ctx->u.sha256, tk); + key = tk; + key_len = 32; + } + os_memcpy(ctx->key, key, key_len); + ctx->key_len = key_len; + + os_memcpy(k_pad, key, key_len); + if (key_len < sizeof(k_pad)) + os_memset(k_pad + key_len, 0, sizeof(k_pad) - key_len); + for (i = 0; i < sizeof(k_pad); i++) + k_pad[i] ^= 0x36; + sha256_init(&ctx->u.sha256); + sha256_process(&ctx->u.sha256, k_pad, sizeof(k_pad)); + break; +#endif /* CONFIG_SHA256 */ default: os_free(ctx); return NULL; @@ -112,6 +136,14 @@ case CRYPTO_HASH_ALG_HMAC_SHA1: SHA1Update(&ctx->u.sha1, data, len); break; +#ifdef CONFIG_SHA256 + case CRYPTO_HASH_ALG_SHA256: + case CRYPTO_HASH_ALG_HMAC_SHA256: + sha256_process(&ctx->u.sha256, data, len); + break; +#endif /* CONFIG_SHA256 */ + default: + break; } } @@ -148,6 +180,17 @@ *len = 20; SHA1Final(mac, &ctx->u.sha1); break; +#ifdef CONFIG_SHA256 + case CRYPTO_HASH_ALG_SHA256: + if (*len < 32) { + *len = 32; + os_free(ctx); + return -1; + } + *len = 32; + sha256_done(&ctx->u.sha256, mac); + break; +#endif /* CONFIG_SHA256 */ case CRYPTO_HASH_ALG_HMAC_MD5: if (*len < 16) { *len = 16; @@ -188,6 +231,31 @@ SHA1Update(&ctx->u.sha1, mac, 20); SHA1Final(mac, &ctx->u.sha1); break; +#ifdef CONFIG_SHA256 + case CRYPTO_HASH_ALG_HMAC_SHA256: + if (*len < 32) { + *len = 32; + os_free(ctx); + return -1; + } + *len = 32; + + sha256_done(&ctx->u.sha256, mac); + + os_memcpy(k_pad, ctx->key, ctx->key_len); + os_memset(k_pad + ctx->key_len, 0, + sizeof(k_pad) - ctx->key_len); + for (i = 0; i < sizeof(k_pad); i++) + k_pad[i] ^= 0x5c; + sha256_init(&ctx->u.sha256); + sha256_process(&ctx->u.sha256, k_pad, sizeof(k_pad)); + sha256_process(&ctx->u.sha256, mac, 32); + sha256_done(&ctx->u.sha256, mac); + break; +#endif /* CONFIG_SHA256 */ + default: + os_free(ctx); + return -1; } os_free(ctx); diff -Nru wpa-1.0/src/crypto/crypto_internal-cipher.c wpa-2.1/src/crypto/crypto_internal-cipher.c --- wpa-1.0/src/crypto/crypto_internal-cipher.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/crypto/crypto_internal-cipher.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * Crypto wrapper for internal crypto implementation - Cipher wrappers * Copyright (c) 2006-2009, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -30,7 +24,6 @@ } rc4; struct { u8 cbc[32]; - size_t block_size; void *ctx_enc; void *ctx_dec; } aes; @@ -69,10 +62,6 @@ os_memcpy(ctx->u.rc4.key, key, key_len); break; case CRYPTO_CIPHER_ALG_AES: - if (key_len > sizeof(ctx->u.aes.cbc)) { - os_free(ctx); - return NULL; - } ctx->u.aes.ctx_enc = aes_encrypt_init(key, key_len); if (ctx->u.aes.ctx_enc == NULL) { os_free(ctx); @@ -84,8 +73,7 @@ os_free(ctx); return NULL; } - ctx->u.aes.block_size = key_len; - os_memcpy(ctx->u.aes.cbc, iv, ctx->u.aes.block_size); + os_memcpy(ctx->u.aes.cbc, iv, AES_BLOCK_SIZE); break; case CRYPTO_CIPHER_ALG_3DES: if (key_len != 24) { @@ -126,18 +114,17 @@ ctx->u.rc4.used_bytes += len; break; case CRYPTO_CIPHER_ALG_AES: - if (len % ctx->u.aes.block_size) + if (len % AES_BLOCK_SIZE) return -1; - blocks = len / ctx->u.aes.block_size; + blocks = len / AES_BLOCK_SIZE; for (i = 0; i < blocks; i++) { - for (j = 0; j < ctx->u.aes.block_size; j++) + for (j = 0; j < AES_BLOCK_SIZE; j++) ctx->u.aes.cbc[j] ^= plain[j]; aes_encrypt(ctx->u.aes.ctx_enc, ctx->u.aes.cbc, ctx->u.aes.cbc); - os_memcpy(crypt, ctx->u.aes.cbc, - ctx->u.aes.block_size); - plain += ctx->u.aes.block_size; - crypt += ctx->u.aes.block_size; + os_memcpy(crypt, ctx->u.aes.cbc, AES_BLOCK_SIZE); + plain += AES_BLOCK_SIZE; + crypt += AES_BLOCK_SIZE; } break; case CRYPTO_CIPHER_ALG_3DES: @@ -191,17 +178,17 @@ ctx->u.rc4.used_bytes += len; break; case CRYPTO_CIPHER_ALG_AES: - if (len % ctx->u.aes.block_size) + if (len % AES_BLOCK_SIZE) return -1; - blocks = len / ctx->u.aes.block_size; + blocks = len / AES_BLOCK_SIZE; for (i = 0; i < blocks; i++) { - os_memcpy(tmp, crypt, ctx->u.aes.block_size); + os_memcpy(tmp, crypt, AES_BLOCK_SIZE); aes_decrypt(ctx->u.aes.ctx_dec, crypt, plain); - for (j = 0; j < ctx->u.aes.block_size; j++) + for (j = 0; j < AES_BLOCK_SIZE; j++) plain[j] ^= ctx->u.aes.cbc[j]; - os_memcpy(ctx->u.aes.cbc, tmp, ctx->u.aes.block_size); - plain += ctx->u.aes.block_size; - crypt += ctx->u.aes.block_size; + os_memcpy(ctx->u.aes.cbc, tmp, AES_BLOCK_SIZE); + plain += AES_BLOCK_SIZE; + crypt += AES_BLOCK_SIZE; } break; case CRYPTO_CIPHER_ALG_3DES: diff -Nru wpa-1.0/src/crypto/crypto_internal-modexp.c wpa-2.1/src/crypto/crypto_internal-modexp.c --- wpa-1.0/src/crypto/crypto_internal-modexp.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/crypto/crypto_internal-modexp.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * Crypto wrapper for internal crypto implementation - modexp * Copyright (c) 2006-2009, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff -Nru wpa-1.0/src/crypto/crypto_internal-rsa.c wpa-2.1/src/crypto/crypto_internal-rsa.c --- wpa-1.0/src/crypto/crypto_internal-rsa.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/crypto/crypto_internal-rsa.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * Crypto wrapper for internal crypto implementation - RSA parts * Copyright (c) 2006-2009, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -17,7 +11,6 @@ #include "common.h" #include "crypto.h" #include "tls/rsa.h" -#include "tls/bignum.h" #include "tls/pkcs1.h" #include "tls/pkcs8.h" diff -Nru wpa-1.0/src/crypto/crypto_libtomcrypt.c wpa-2.1/src/crypto/crypto_libtomcrypt.c --- wpa-1.0/src/crypto/crypto_libtomcrypt.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/crypto/crypto_libtomcrypt.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * WPA Supplicant / Crypto wrapper for LibTomCrypt (for internal TLSv1) * Copyright (c) 2005-2006, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff -Nru wpa-1.0/src/crypto/crypto_none.c wpa-2.1/src/crypto/crypto_none.c --- wpa-1.0/src/crypto/crypto_none.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/crypto/crypto_none.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * WPA Supplicant / Empty template functions for crypto wrapper * Copyright (c) 2005, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff -Nru wpa-1.0/src/crypto/crypto_nss.c wpa-2.1/src/crypto/crypto_nss.c --- wpa-1.0/src/crypto/crypto_nss.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/crypto/crypto_nss.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * Crypto wrapper functions for NSS * Copyright (c) 2009, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff -Nru wpa-1.0/src/crypto/crypto_openssl.c wpa-2.1/src/crypto/crypto_openssl.c --- wpa-1.0/src/crypto/crypto_openssl.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/crypto/crypto_openssl.c 2014-02-04 11:23:35.000000000 +0000 @@ -1,15 +1,9 @@ /* - * WPA Supplicant / wrapper functions for libcrypto - * Copyright (c) 2004-2009, Jouni Malinen + * Wrapper functions for OpenSSL libcrypto + * Copyright (c) 2004-2013, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -20,10 +14,20 @@ #include #include #include +#include +#include +#ifdef CONFIG_OPENSSL_CMAC +#include +#endif /* CONFIG_OPENSSL_CMAC */ +#ifdef CONFIG_ECC +#include +#endif /* CONFIG_ECC */ #include "common.h" #include "wpabuf.h" #include "dh_group5.h" +#include "sha1.h" +#include "sha256.h" #include "crypto.h" #if OPENSSL_VERSION_NUMBER < 0x00907000 @@ -74,21 +78,14 @@ #define NO_SHA256_WRAPPER #endif -static int openssl_digest_vector(const EVP_MD *type, int non_fips, - size_t num_elem, const u8 *addr[], - const size_t *len, u8 *mac) +static int openssl_digest_vector(const EVP_MD *type, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) { EVP_MD_CTX ctx; size_t i; unsigned int mac_len; EVP_MD_CTX_init(&ctx); -#ifdef CONFIG_FIPS -#ifdef OPENSSL_FIPS - if (non_fips) - EVP_MD_CTX_set_flags(&ctx, EVP_MD_CTX_FLAG_NON_FIPS_ALLOW); -#endif /* OPENSSL_FIPS */ -#endif /* CONFIG_FIPS */ if (!EVP_DigestInit_ex(&ctx, type, NULL)) { wpa_printf(MSG_ERROR, "OpenSSL: EVP_DigestInit_ex failed: %s", ERR_error_string(ERR_get_error(), NULL)); @@ -114,7 +111,7 @@ int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) { - return openssl_digest_vector(EVP_md4(), 0, num_elem, addr, len, mac); + return openssl_digest_vector(EVP_md4(), num_elem, addr, len, mac); } @@ -178,22 +175,13 @@ int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) { - return openssl_digest_vector(EVP_md5(), 0, num_elem, addr, len, mac); -} - - -#ifdef CONFIG_FIPS -int md5_vector_non_fips_allow(size_t num_elem, const u8 *addr[], - const size_t *len, u8 *mac) -{ - return openssl_digest_vector(EVP_md5(), 1, num_elem, addr, len, mac); + return openssl_digest_vector(EVP_md5(), num_elem, addr, len, mac); } -#endif /* CONFIG_FIPS */ int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) { - return openssl_digest_vector(EVP_sha1(), 0, num_elem, addr, len, mac); + return openssl_digest_vector(EVP_sha1(), num_elem, addr, len, mac); } @@ -201,60 +189,124 @@ int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) { - return openssl_digest_vector(EVP_sha256(), 0, num_elem, addr, len, - mac); + return openssl_digest_vector(EVP_sha256(), num_elem, addr, len, mac); } #endif /* NO_SHA256_WRAPPER */ +static const EVP_CIPHER * aes_get_evp_cipher(size_t keylen) +{ + switch (keylen) { + case 16: + return EVP_aes_128_ecb(); + case 24: + return EVP_aes_192_ecb(); + case 32: + return EVP_aes_256_ecb(); + } + + return NULL; +} + + void * aes_encrypt_init(const u8 *key, size_t len) { - AES_KEY *ak; - ak = os_malloc(sizeof(*ak)); - if (ak == NULL) + EVP_CIPHER_CTX *ctx; + const EVP_CIPHER *type; + + type = aes_get_evp_cipher(len); + if (type == NULL) + return NULL; + + ctx = os_malloc(sizeof(*ctx)); + if (ctx == NULL) return NULL; - if (AES_set_encrypt_key(key, 8 * len, ak) < 0) { - os_free(ak); + EVP_CIPHER_CTX_init(ctx); + if (EVP_EncryptInit_ex(ctx, type, NULL, key, NULL) != 1) { + os_free(ctx); return NULL; } - return ak; + EVP_CIPHER_CTX_set_padding(ctx, 0); + return ctx; } void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt) { - AES_encrypt(plain, crypt, ctx); + EVP_CIPHER_CTX *c = ctx; + int clen = 16; + if (EVP_EncryptUpdate(c, crypt, &clen, plain, 16) != 1) { + wpa_printf(MSG_ERROR, "OpenSSL: EVP_EncryptUpdate failed: %s", + ERR_error_string(ERR_get_error(), NULL)); + } } void aes_encrypt_deinit(void *ctx) { - os_free(ctx); + EVP_CIPHER_CTX *c = ctx; + u8 buf[16]; + int len = sizeof(buf); + if (EVP_EncryptFinal_ex(c, buf, &len) != 1) { + wpa_printf(MSG_ERROR, "OpenSSL: EVP_EncryptFinal_ex failed: " + "%s", ERR_error_string(ERR_get_error(), NULL)); + } + if (len != 0) { + wpa_printf(MSG_ERROR, "OpenSSL: Unexpected padding length %d " + "in AES encrypt", len); + } + EVP_CIPHER_CTX_cleanup(c); + os_free(c); } void * aes_decrypt_init(const u8 *key, size_t len) { - AES_KEY *ak; - ak = os_malloc(sizeof(*ak)); - if (ak == NULL) + EVP_CIPHER_CTX *ctx; + const EVP_CIPHER *type; + + type = aes_get_evp_cipher(len); + if (type == NULL) + return NULL; + + ctx = os_malloc(sizeof(*ctx)); + if (ctx == NULL) return NULL; - if (AES_set_decrypt_key(key, 8 * len, ak) < 0) { - os_free(ak); + EVP_CIPHER_CTX_init(ctx); + if (EVP_DecryptInit_ex(ctx, type, NULL, key, NULL) != 1) { + os_free(ctx); return NULL; } - return ak; + EVP_CIPHER_CTX_set_padding(ctx, 0); + return ctx; } void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain) { - AES_decrypt(crypt, plain, ctx); + EVP_CIPHER_CTX *c = ctx; + int plen = 16; + if (EVP_DecryptUpdate(c, plain, &plen, crypt, 16) != 1) { + wpa_printf(MSG_ERROR, "OpenSSL: EVP_DecryptUpdate failed: %s", + ERR_error_string(ERR_get_error(), NULL)); + } } void aes_decrypt_deinit(void *ctx) { + EVP_CIPHER_CTX *c = ctx; + u8 buf[16]; + int len = sizeof(buf); + if (EVP_DecryptFinal_ex(c, buf, &len) != 1) { + wpa_printf(MSG_ERROR, "OpenSSL: EVP_DecryptFinal_ex failed: " + "%s", ERR_error_string(ERR_get_error(), NULL)); + } + if (len != 0) { + wpa_printf(MSG_ERROR, "OpenSSL: Unexpected padding length %d " + "in AES decrypt", len); + } + EVP_CIPHER_CTX_cleanup(c); os_free(ctx); } @@ -458,6 +510,41 @@ } +void * dh5_init_fixed(const struct wpabuf *priv, const struct wpabuf *publ) +{ + DH *dh; + + dh = DH_new(); + if (dh == NULL) + return NULL; + + dh->g = BN_new(); + if (dh->g == NULL || BN_set_word(dh->g, 2) != 1) + goto err; + + dh->p = get_group5_prime(); + if (dh->p == NULL) + goto err; + + dh->priv_key = BN_bin2bn(wpabuf_head(priv), wpabuf_len(priv), NULL); + if (dh->priv_key == NULL) + goto err; + + dh->pub_key = BN_bin2bn(wpabuf_head(publ), wpabuf_len(publ), NULL); + if (dh->pub_key == NULL) + goto err; + + if (DH_generate_key(dh) != 1) + goto err; + + return dh; + +err: + DH_free(dh); + return NULL; +} + + struct wpabuf * dh5_derive_shared(void *ctx, const struct wpabuf *peer_public, const struct wpabuf *own_private) { @@ -503,3 +590,646 @@ dh = ctx; DH_free(dh); } + + +struct crypto_hash { + HMAC_CTX ctx; +}; + + +struct crypto_hash * crypto_hash_init(enum crypto_hash_alg alg, const u8 *key, + size_t key_len) +{ + struct crypto_hash *ctx; + const EVP_MD *md; + + switch (alg) { +#ifndef OPENSSL_NO_MD5 + case CRYPTO_HASH_ALG_HMAC_MD5: + md = EVP_md5(); + break; +#endif /* OPENSSL_NO_MD5 */ +#ifndef OPENSSL_NO_SHA + case CRYPTO_HASH_ALG_HMAC_SHA1: + md = EVP_sha1(); + break; +#endif /* OPENSSL_NO_SHA */ +#ifndef OPENSSL_NO_SHA256 +#ifdef CONFIG_SHA256 + case CRYPTO_HASH_ALG_HMAC_SHA256: + md = EVP_sha256(); + break; +#endif /* CONFIG_SHA256 */ +#endif /* OPENSSL_NO_SHA256 */ + default: + return NULL; + } + + ctx = os_zalloc(sizeof(*ctx)); + if (ctx == NULL) + return NULL; + HMAC_CTX_init(&ctx->ctx); + +#if OPENSSL_VERSION_NUMBER < 0x00909000 + HMAC_Init_ex(&ctx->ctx, key, key_len, md, NULL); +#else /* openssl < 0.9.9 */ + if (HMAC_Init_ex(&ctx->ctx, key, key_len, md, NULL) != 1) { + os_free(ctx); + return NULL; + } +#endif /* openssl < 0.9.9 */ + + return ctx; +} + + +void crypto_hash_update(struct crypto_hash *ctx, const u8 *data, size_t len) +{ + if (ctx == NULL) + return; + HMAC_Update(&ctx->ctx, data, len); +} + + +int crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len) +{ + unsigned int mdlen; + int res; + + if (ctx == NULL) + return -2; + + if (mac == NULL || len == NULL) { + os_free(ctx); + return 0; + } + + mdlen = *len; +#if OPENSSL_VERSION_NUMBER < 0x00909000 + HMAC_Final(&ctx->ctx, mac, &mdlen); + res = 1; +#else /* openssl < 0.9.9 */ + res = HMAC_Final(&ctx->ctx, mac, &mdlen); +#endif /* openssl < 0.9.9 */ + HMAC_CTX_cleanup(&ctx->ctx); + os_free(ctx); + + if (res == 1) { + *len = mdlen; + return 0; + } + + return -1; +} + + +int pbkdf2_sha1(const char *passphrase, const u8 *ssid, size_t ssid_len, + int iterations, u8 *buf, size_t buflen) +{ +#if OPENSSL_VERSION_NUMBER < 0x00908000 + if (PKCS5_PBKDF2_HMAC_SHA1(passphrase, os_strlen(passphrase), + (unsigned char *) ssid, + ssid_len, 4096, buflen, buf) != 1) + return -1; +#else /* openssl < 0.9.8 */ + if (PKCS5_PBKDF2_HMAC_SHA1(passphrase, os_strlen(passphrase), ssid, + ssid_len, 4096, buflen, buf) != 1) + return -1; +#endif /* openssl < 0.9.8 */ + return 0; +} + + +int hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + HMAC_CTX ctx; + size_t i; + unsigned int mdlen; + int res; + + HMAC_CTX_init(&ctx); +#if OPENSSL_VERSION_NUMBER < 0x00909000 + HMAC_Init_ex(&ctx, key, key_len, EVP_sha1(), NULL); +#else /* openssl < 0.9.9 */ + if (HMAC_Init_ex(&ctx, key, key_len, EVP_sha1(), NULL) != 1) + return -1; +#endif /* openssl < 0.9.9 */ + + for (i = 0; i < num_elem; i++) + HMAC_Update(&ctx, addr[i], len[i]); + + mdlen = 20; +#if OPENSSL_VERSION_NUMBER < 0x00909000 + HMAC_Final(&ctx, mac, &mdlen); + res = 1; +#else /* openssl < 0.9.9 */ + res = HMAC_Final(&ctx, mac, &mdlen); +#endif /* openssl < 0.9.9 */ + HMAC_CTX_cleanup(&ctx); + + return res == 1 ? 0 : -1; +} + + +int hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len, + u8 *mac) +{ + return hmac_sha1_vector(key, key_len, 1, &data, &data_len, mac); +} + + +#ifdef CONFIG_SHA256 + +int hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + HMAC_CTX ctx; + size_t i; + unsigned int mdlen; + int res; + + HMAC_CTX_init(&ctx); +#if OPENSSL_VERSION_NUMBER < 0x00909000 + HMAC_Init_ex(&ctx, key, key_len, EVP_sha256(), NULL); +#else /* openssl < 0.9.9 */ + if (HMAC_Init_ex(&ctx, key, key_len, EVP_sha256(), NULL) != 1) + return -1; +#endif /* openssl < 0.9.9 */ + + for (i = 0; i < num_elem; i++) + HMAC_Update(&ctx, addr[i], len[i]); + + mdlen = 32; +#if OPENSSL_VERSION_NUMBER < 0x00909000 + HMAC_Final(&ctx, mac, &mdlen); + res = 1; +#else /* openssl < 0.9.9 */ + res = HMAC_Final(&ctx, mac, &mdlen); +#endif /* openssl < 0.9.9 */ + HMAC_CTX_cleanup(&ctx); + + return res == 1 ? 0 : -1; +} + + +int hmac_sha256(const u8 *key, size_t key_len, const u8 *data, + size_t data_len, u8 *mac) +{ + return hmac_sha256_vector(key, key_len, 1, &data, &data_len, mac); +} + +#endif /* CONFIG_SHA256 */ + + +int crypto_get_random(void *buf, size_t len) +{ + if (RAND_bytes(buf, len) != 1) + return -1; + return 0; +} + + +#ifdef CONFIG_OPENSSL_CMAC +int omac1_aes_128_vector(const u8 *key, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + CMAC_CTX *ctx; + int ret = -1; + size_t outlen, i; + + ctx = CMAC_CTX_new(); + if (ctx == NULL) + return -1; + + if (!CMAC_Init(ctx, key, 16, EVP_aes_128_cbc(), NULL)) + goto fail; + for (i = 0; i < num_elem; i++) { + if (!CMAC_Update(ctx, addr[i], len[i])) + goto fail; + } + if (!CMAC_Final(ctx, mac, &outlen) || outlen != 16) + goto fail; + + ret = 0; +fail: + CMAC_CTX_free(ctx); + return ret; +} + + +int omac1_aes_128(const u8 *key, const u8 *data, size_t data_len, u8 *mac) +{ + return omac1_aes_128_vector(key, 1, &data, &data_len, mac); +} +#endif /* CONFIG_OPENSSL_CMAC */ + + +struct crypto_bignum * crypto_bignum_init(void) +{ + return (struct crypto_bignum *) BN_new(); +} + + +struct crypto_bignum * crypto_bignum_init_set(const u8 *buf, size_t len) +{ + BIGNUM *bn = BN_bin2bn(buf, len, NULL); + return (struct crypto_bignum *) bn; +} + + +void crypto_bignum_deinit(struct crypto_bignum *n, int clear) +{ + if (clear) + BN_clear_free((BIGNUM *) n); + else + BN_free((BIGNUM *) n); +} + + +int crypto_bignum_to_bin(const struct crypto_bignum *a, + u8 *buf, size_t buflen, size_t padlen) +{ + int num_bytes, offset; + + if (padlen > buflen) + return -1; + + num_bytes = BN_num_bytes((const BIGNUM *) a); + if ((size_t) num_bytes > buflen) + return -1; + if (padlen > (size_t) num_bytes) + offset = padlen - num_bytes; + else + offset = 0; + + os_memset(buf, 0, offset); + BN_bn2bin((const BIGNUM *) a, buf + offset); + + return num_bytes + offset; +} + + +int crypto_bignum_add(const struct crypto_bignum *a, + const struct crypto_bignum *b, + struct crypto_bignum *c) +{ + return BN_add((BIGNUM *) c, (const BIGNUM *) a, (const BIGNUM *) b) ? + 0 : -1; +} + + +int crypto_bignum_mod(const struct crypto_bignum *a, + const struct crypto_bignum *b, + struct crypto_bignum *c) +{ + int res; + BN_CTX *bnctx; + + bnctx = BN_CTX_new(); + if (bnctx == NULL) + return -1; + res = BN_mod((BIGNUM *) c, (const BIGNUM *) a, (const BIGNUM *) b, + bnctx); + BN_CTX_free(bnctx); + + return res ? 0 : -1; +} + + +int crypto_bignum_exptmod(const struct crypto_bignum *a, + const struct crypto_bignum *b, + const struct crypto_bignum *c, + struct crypto_bignum *d) +{ + int res; + BN_CTX *bnctx; + + bnctx = BN_CTX_new(); + if (bnctx == NULL) + return -1; + res = BN_mod_exp((BIGNUM *) d, (const BIGNUM *) a, (const BIGNUM *) b, + (const BIGNUM *) c, bnctx); + BN_CTX_free(bnctx); + + return res ? 0 : -1; +} + + +int crypto_bignum_rshift(const struct crypto_bignum *a, int n, + struct crypto_bignum *b) +{ + return BN_rshift((BIGNUM *) b, (const BIGNUM *) a, n) ? 0 : -1; +} + + +int crypto_bignum_inverse(const struct crypto_bignum *a, + const struct crypto_bignum *b, + struct crypto_bignum *c) +{ + BIGNUM *res; + BN_CTX *bnctx; + + bnctx = BN_CTX_new(); + if (bnctx == NULL) + return -1; + res = BN_mod_inverse((BIGNUM *) c, (const BIGNUM *) a, + (const BIGNUM *) b, bnctx); + BN_CTX_free(bnctx); + + return res ? 0 : -1; +} + + +int crypto_bignum_sub(const struct crypto_bignum *a, + const struct crypto_bignum *b, + struct crypto_bignum *c) +{ + return BN_sub((BIGNUM *) c, (const BIGNUM *) a, (const BIGNUM *) b) ? + 0 : -1; +} + + +int crypto_bignum_div(const struct crypto_bignum *a, + const struct crypto_bignum *b, + struct crypto_bignum *c) +{ + int res; + + BN_CTX *bnctx; + + bnctx = BN_CTX_new(); + if (bnctx == NULL) + return -1; + res = BN_div((BIGNUM *) c, NULL, (const BIGNUM *) a, + (const BIGNUM *) b, bnctx); + BN_CTX_free(bnctx); + + return res ? 0 : -1; +} + + +int crypto_bignum_mulmod(const struct crypto_bignum *a, + const struct crypto_bignum *b, + const struct crypto_bignum *c, + struct crypto_bignum *d) +{ + int res; + + BN_CTX *bnctx; + + bnctx = BN_CTX_new(); + if (bnctx == NULL) + return -1; + res = BN_mod_mul((BIGNUM *) d, (const BIGNUM *) a, (const BIGNUM *) b, + (const BIGNUM *) c, bnctx); + BN_CTX_free(bnctx); + + return res ? 0 : -1; +} + + +int crypto_bignum_cmp(const struct crypto_bignum *a, + const struct crypto_bignum *b) +{ + return BN_cmp((const BIGNUM *) a, (const BIGNUM *) b); +} + + +int crypto_bignum_bits(const struct crypto_bignum *a) +{ + return BN_num_bits((const BIGNUM *) a); +} + + +int crypto_bignum_is_zero(const struct crypto_bignum *a) +{ + return BN_is_zero((const BIGNUM *) a); +} + + +int crypto_bignum_is_one(const struct crypto_bignum *a) +{ + return BN_is_one((const BIGNUM *) a); +} + + +#ifdef CONFIG_ECC + +struct crypto_ec { + EC_GROUP *group; + BN_CTX *bnctx; + BIGNUM *prime; + BIGNUM *order; +}; + +struct crypto_ec * crypto_ec_init(int group) +{ + struct crypto_ec *e; + int nid; + + /* Map from IANA registry for IKE D-H groups to OpenSSL NID */ + switch (group) { + case 19: + nid = NID_X9_62_prime256v1; + break; + case 20: + nid = NID_secp384r1; + break; + case 21: + nid = NID_secp521r1; + break; + case 25: + nid = NID_X9_62_prime192v1; + break; + case 26: + nid = NID_secp224r1; + break; + default: + return NULL; + } + + e = os_zalloc(sizeof(*e)); + if (e == NULL) + return NULL; + + e->bnctx = BN_CTX_new(); + e->group = EC_GROUP_new_by_curve_name(nid); + e->prime = BN_new(); + e->order = BN_new(); + if (e->group == NULL || e->bnctx == NULL || e->prime == NULL || + e->order == NULL || + !EC_GROUP_get_curve_GFp(e->group, e->prime, NULL, NULL, e->bnctx) || + !EC_GROUP_get_order(e->group, e->order, e->bnctx)) { + crypto_ec_deinit(e); + e = NULL; + } + + return e; +} + + +void crypto_ec_deinit(struct crypto_ec *e) +{ + if (e == NULL) + return; + BN_free(e->order); + EC_GROUP_free(e->group); + BN_CTX_free(e->bnctx); + os_free(e); +} + + +struct crypto_ec_point * crypto_ec_point_init(struct crypto_ec *e) +{ + if (e == NULL) + return NULL; + return (struct crypto_ec_point *) EC_POINT_new(e->group); +} + + +size_t crypto_ec_prime_len(struct crypto_ec *e) +{ + return BN_num_bytes(e->prime); +} + + +size_t crypto_ec_prime_len_bits(struct crypto_ec *e) +{ + return BN_num_bits(e->prime); +} + + +const struct crypto_bignum * crypto_ec_get_prime(struct crypto_ec *e) +{ + return (const struct crypto_bignum *) e->prime; +} + + +const struct crypto_bignum * crypto_ec_get_order(struct crypto_ec *e) +{ + return (const struct crypto_bignum *) e->order; +} + + +void crypto_ec_point_deinit(struct crypto_ec_point *p, int clear) +{ + if (clear) + EC_POINT_clear_free((EC_POINT *) p); + else + EC_POINT_free((EC_POINT *) p); +} + + +int crypto_ec_point_to_bin(struct crypto_ec *e, + const struct crypto_ec_point *point, u8 *x, u8 *y) +{ + BIGNUM *x_bn, *y_bn; + int ret = -1; + int len = BN_num_bytes(e->prime); + + x_bn = BN_new(); + y_bn = BN_new(); + + if (x_bn && y_bn && + EC_POINT_get_affine_coordinates_GFp(e->group, (EC_POINT *) point, + x_bn, y_bn, e->bnctx)) { + if (x) { + crypto_bignum_to_bin((struct crypto_bignum *) x_bn, + x, len, len); + } + if (y) { + crypto_bignum_to_bin((struct crypto_bignum *) y_bn, + y, len, len); + } + ret = 0; + } + + BN_free(x_bn); + BN_free(y_bn); + return ret; +} + + +struct crypto_ec_point * crypto_ec_point_from_bin(struct crypto_ec *e, + const u8 *val) +{ + BIGNUM *x, *y; + EC_POINT *elem; + int len = BN_num_bytes(e->prime); + + x = BN_bin2bn(val, len, NULL); + y = BN_bin2bn(val + len, len, NULL); + elem = EC_POINT_new(e->group); + if (x == NULL || y == NULL || elem == NULL) { + BN_free(x); + BN_free(y); + EC_POINT_free(elem); + return NULL; + } + + if (!EC_POINT_set_affine_coordinates_GFp(e->group, elem, x, y, + e->bnctx)) { + EC_POINT_free(elem); + elem = NULL; + } + + BN_free(x); + BN_free(y); + + return (struct crypto_ec_point *) elem; +} + + +int crypto_ec_point_add(struct crypto_ec *e, const struct crypto_ec_point *a, + const struct crypto_ec_point *b, + struct crypto_ec_point *c) +{ + return EC_POINT_add(e->group, (EC_POINT *) c, (const EC_POINT *) a, + (const EC_POINT *) b, e->bnctx) ? 0 : -1; +} + + +int crypto_ec_point_mul(struct crypto_ec *e, const struct crypto_ec_point *p, + const struct crypto_bignum *b, + struct crypto_ec_point *res) +{ + return EC_POINT_mul(e->group, (EC_POINT *) res, NULL, + (const EC_POINT *) p, (const BIGNUM *) b, e->bnctx) + ? 0 : -1; +} + + +int crypto_ec_point_invert(struct crypto_ec *e, struct crypto_ec_point *p) +{ + return EC_POINT_invert(e->group, (EC_POINT *) p, e->bnctx) ? 0 : -1; +} + + +int crypto_ec_point_solve_y_coord(struct crypto_ec *e, + struct crypto_ec_point *p, + const struct crypto_bignum *x, int y_bit) +{ + if (!EC_POINT_set_compressed_coordinates_GFp(e->group, (EC_POINT *) p, + (const BIGNUM *) x, y_bit, + e->bnctx) || + !EC_POINT_is_on_curve(e->group, (EC_POINT *) p, e->bnctx)) + return -1; + return 0; +} + + +int crypto_ec_point_is_at_infinity(struct crypto_ec *e, + const struct crypto_ec_point *p) +{ + return EC_POINT_is_at_infinity(e->group, (const EC_POINT *) p); +} + + +int crypto_ec_point_is_on_curve(struct crypto_ec *e, + const struct crypto_ec_point *p) +{ + return EC_POINT_is_on_curve(e->group, (const EC_POINT *) p, e->bnctx); +} + +#endif /* CONFIG_ECC */ diff -Nru wpa-1.0/src/crypto/des_i.h wpa-2.1/src/crypto/des_i.h --- wpa-1.0/src/crypto/des_i.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/crypto/des_i.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * DES and 3DES-EDE ciphers * Copyright (c) 2006-2009, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef DES_I_H diff -Nru wpa-1.0/src/crypto/des-internal.c wpa-2.1/src/crypto/des-internal.c --- wpa-1.0/src/crypto/des-internal.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/crypto/des-internal.c 2014-02-04 11:23:35.000000000 +0000 @@ -4,14 +4,8 @@ * Modifications to LibTomCrypt implementation: * Copyright (c) 2006-2009, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff -Nru wpa-1.0/src/crypto/dh_group5.c wpa-2.1/src/crypto/dh_group5.c --- wpa-1.0/src/crypto/dh_group5.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/crypto/dh_group5.c 2014-02-04 11:23:35.000000000 +0000 @@ -1,15 +1,9 @@ /* * Diffie-Hellman group 5 operations - * Copyright (c) 2009, Jouni Malinen + * Copyright (c) 2009, 2012, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -22,11 +16,17 @@ void * dh5_init(struct wpabuf **priv, struct wpabuf **publ) { *publ = dh_init(dh_groups_get(5), priv); - if (*publ == 0) + if (*publ == NULL) return NULL; return (void *) 1; } + +void * dh5_init_fixed(const struct wpabuf *priv, const struct wpabuf *publ) +{ + return (void *) 1; +} + struct wpabuf * dh5_derive_shared(void *ctx, const struct wpabuf *peer_public, const struct wpabuf *own_private) diff -Nru wpa-1.0/src/crypto/dh_group5.h wpa-2.1/src/crypto/dh_group5.h --- wpa-1.0/src/crypto/dh_group5.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/crypto/dh_group5.h 2014-02-04 11:23:35.000000000 +0000 @@ -1,21 +1,16 @@ /* * Diffie-Hellman group 5 operations - * Copyright (c) 2009, Jouni Malinen + * Copyright (c) 2009, 2012, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef DH_GROUP5_H #define DH_GROUP5_H void * dh5_init(struct wpabuf **priv, struct wpabuf **publ); +void * dh5_init_fixed(const struct wpabuf *priv, const struct wpabuf *publ); struct wpabuf * dh5_derive_shared(void *ctx, const struct wpabuf *peer_public, const struct wpabuf *own_private); void dh5_free(void *ctx); diff -Nru wpa-1.0/src/crypto/dh_groups.c wpa-2.1/src/crypto/dh_groups.c --- wpa-1.0/src/crypto/dh_groups.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/crypto/dh_groups.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * Diffie-Hellman groups * Copyright (c) 2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -41,6 +35,20 @@ 0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x3A, 0x36, 0x20, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; +static const u8 dh_group1_order[96] = { + 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xE4, 0x87, 0xED, 0x51, 0x10, 0xB4, 0x61, 0x1A, + 0x62, 0x63, 0x31, 0x45, 0xC0, 0x6E, 0x0E, 0x68, + 0x94, 0x81, 0x27, 0x04, 0x45, 0x33, 0xE6, 0x3A, + 0x01, 0x05, 0xDF, 0x53, 0x1D, 0x89, 0xCD, 0x91, + 0x28, 0xA5, 0x04, 0x3C, 0xC7, 0x1A, 0x02, 0x6E, + 0xF7, 0xCA, 0x8C, 0xD9, 0xE6, 0x9D, 0x21, 0x8D, + 0x98, 0x15, 0x85, 0x36, 0xF9, 0x2F, 0x8A, 0x1B, + 0xA7, 0xF0, 0x9A, 0xB6, 0xB6, 0xA8, 0xE1, 0x22, + 0xF2, 0x42, 0xDA, 0xBB, 0x31, 0x2F, 0x3F, 0x63, + 0x7A, 0x26, 0x21, 0x74, 0xD3, 0x1D, 0x1B, 0x10, + 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF +}; /* RFC 4306, B.2. Group 2 - 1024 Bit MODP * Generator: 2 @@ -65,6 +73,24 @@ 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE6, 0x53, 0x81, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; +static const u8 dh_group2_order[128] = { + 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xE4, 0x87, 0xED, 0x51, 0x10, 0xB4, 0x61, 0x1A, + 0x62, 0x63, 0x31, 0x45, 0xC0, 0x6E, 0x0E, 0x68, + 0x94, 0x81, 0x27, 0x04, 0x45, 0x33, 0xE6, 0x3A, + 0x01, 0x05, 0xDF, 0x53, 0x1D, 0x89, 0xCD, 0x91, + 0x28, 0xA5, 0x04, 0x3C, 0xC7, 0x1A, 0x02, 0x6E, + 0xF7, 0xCA, 0x8C, 0xD9, 0xE6, 0x9D, 0x21, 0x8D, + 0x98, 0x15, 0x85, 0x36, 0xF9, 0x2F, 0x8A, 0x1B, + 0xA7, 0xF0, 0x9A, 0xB6, 0xB6, 0xA8, 0xE1, 0x22, + 0xF2, 0x42, 0xDA, 0xBB, 0x31, 0x2F, 0x3F, 0x63, + 0x7A, 0x26, 0x21, 0x74, 0xD3, 0x1B, 0xF6, 0xB5, + 0x85, 0xFF, 0xAE, 0x5B, 0x7A, 0x03, 0x5B, 0xF6, + 0xF7, 0x1C, 0x35, 0xFD, 0xAD, 0x44, 0xCF, 0xD2, + 0xD7, 0x4F, 0x92, 0x08, 0xBE, 0x25, 0x8F, 0xF3, + 0x24, 0x94, 0x33, 0x28, 0xF6, 0x73, 0x29, 0xC0, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF +}; #endif /* ALL_DH_GROUPS */ @@ -99,6 +125,32 @@ 0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x23, 0x73, 0x27, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; +static const u8 dh_group5_order[192] = { + 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xE4, 0x87, 0xED, 0x51, 0x10, 0xB4, 0x61, 0x1A, + 0x62, 0x63, 0x31, 0x45, 0xC0, 0x6E, 0x0E, 0x68, + 0x94, 0x81, 0x27, 0x04, 0x45, 0x33, 0xE6, 0x3A, + 0x01, 0x05, 0xDF, 0x53, 0x1D, 0x89, 0xCD, 0x91, + 0x28, 0xA5, 0x04, 0x3C, 0xC7, 0x1A, 0x02, 0x6E, + 0xF7, 0xCA, 0x8C, 0xD9, 0xE6, 0x9D, 0x21, 0x8D, + 0x98, 0x15, 0x85, 0x36, 0xF9, 0x2F, 0x8A, 0x1B, + 0xA7, 0xF0, 0x9A, 0xB6, 0xB6, 0xA8, 0xE1, 0x22, + 0xF2, 0x42, 0xDA, 0xBB, 0x31, 0x2F, 0x3F, 0x63, + 0x7A, 0x26, 0x21, 0x74, 0xD3, 0x1B, 0xF6, 0xB5, + 0x85, 0xFF, 0xAE, 0x5B, 0x7A, 0x03, 0x5B, 0xF6, + 0xF7, 0x1C, 0x35, 0xFD, 0xAD, 0x44, 0xCF, 0xD2, + 0xD7, 0x4F, 0x92, 0x08, 0xBE, 0x25, 0x8F, 0xF3, + 0x24, 0x94, 0x33, 0x28, 0xF6, 0x72, 0x2D, 0x9E, + 0xE1, 0x00, 0x3E, 0x5C, 0x50, 0xB1, 0xDF, 0x82, + 0xCC, 0x6D, 0x24, 0x1B, 0x0E, 0x2A, 0xE9, 0xCD, + 0x34, 0x8B, 0x1F, 0xD4, 0x7E, 0x92, 0x67, 0xAF, + 0xC1, 0xB2, 0xAE, 0x91, 0xEE, 0x51, 0xD6, 0xCB, + 0x0E, 0x31, 0x79, 0xAB, 0x10, 0x42, 0xA9, 0x5D, + 0xCF, 0x6A, 0x94, 0x83, 0xB8, 0x4B, 0x4B, 0x36, + 0xB3, 0x86, 0x1A, 0xA7, 0x25, 0x5E, 0x4C, 0x02, + 0x78, 0xBA, 0x36, 0x04, 0x65, 0x11, 0xB9, 0x93, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF +}; #ifdef ALL_DH_GROUPS @@ -141,6 +193,40 @@ 0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAC, 0xAA, 0x68, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; +static const u8 dh_group14_order[256] = { + 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xE4, 0x87, 0xED, 0x51, 0x10, 0xB4, 0x61, 0x1A, + 0x62, 0x63, 0x31, 0x45, 0xC0, 0x6E, 0x0E, 0x68, + 0x94, 0x81, 0x27, 0x04, 0x45, 0x33, 0xE6, 0x3A, + 0x01, 0x05, 0xDF, 0x53, 0x1D, 0x89, 0xCD, 0x91, + 0x28, 0xA5, 0x04, 0x3C, 0xC7, 0x1A, 0x02, 0x6E, + 0xF7, 0xCA, 0x8C, 0xD9, 0xE6, 0x9D, 0x21, 0x8D, + 0x98, 0x15, 0x85, 0x36, 0xF9, 0x2F, 0x8A, 0x1B, + 0xA7, 0xF0, 0x9A, 0xB6, 0xB6, 0xA8, 0xE1, 0x22, + 0xF2, 0x42, 0xDA, 0xBB, 0x31, 0x2F, 0x3F, 0x63, + 0x7A, 0x26, 0x21, 0x74, 0xD3, 0x1B, 0xF6, 0xB5, + 0x85, 0xFF, 0xAE, 0x5B, 0x7A, 0x03, 0x5B, 0xF6, + 0xF7, 0x1C, 0x35, 0xFD, 0xAD, 0x44, 0xCF, 0xD2, + 0xD7, 0x4F, 0x92, 0x08, 0xBE, 0x25, 0x8F, 0xF3, + 0x24, 0x94, 0x33, 0x28, 0xF6, 0x72, 0x2D, 0x9E, + 0xE1, 0x00, 0x3E, 0x5C, 0x50, 0xB1, 0xDF, 0x82, + 0xCC, 0x6D, 0x24, 0x1B, 0x0E, 0x2A, 0xE9, 0xCD, + 0x34, 0x8B, 0x1F, 0xD4, 0x7E, 0x92, 0x67, 0xAF, + 0xC1, 0xB2, 0xAE, 0x91, 0xEE, 0x51, 0xD6, 0xCB, + 0x0E, 0x31, 0x79, 0xAB, 0x10, 0x42, 0xA9, 0x5D, + 0xCF, 0x6A, 0x94, 0x83, 0xB8, 0x4B, 0x4B, 0x36, + 0xB3, 0x86, 0x1A, 0xA7, 0x25, 0x5E, 0x4C, 0x02, + 0x78, 0xBA, 0x36, 0x04, 0x65, 0x0C, 0x10, 0xBE, + 0x19, 0x48, 0x2F, 0x23, 0x17, 0x1B, 0x67, 0x1D, + 0xF1, 0xCF, 0x3B, 0x96, 0x0C, 0x07, 0x43, 0x01, + 0xCD, 0x93, 0xC1, 0xD1, 0x76, 0x03, 0xD1, 0x47, + 0xDA, 0xE2, 0xAE, 0xF8, 0x37, 0xA6, 0x29, 0x64, + 0xEF, 0x15, 0xE5, 0xFB, 0x4A, 0xAC, 0x0B, 0x8C, + 0x1C, 0xCA, 0xA4, 0xBE, 0x75, 0x4A, 0xB5, 0x72, + 0x8A, 0xE9, 0x13, 0x0C, 0x4C, 0x7D, 0x02, 0x88, + 0x0A, 0xB9, 0x47, 0x2D, 0x45, 0x56, 0x55, 0x34, + 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF +}; /* RFC 3526, 4. Group 15 - 3072 Bit MODP * Generator: 2 @@ -197,6 +283,56 @@ 0x4B, 0x82, 0xD1, 0x20, 0xA9, 0x3A, 0xD2, 0xCA, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; +static const u8 dh_group15_order[384] = { + 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xE4, 0x87, 0xED, 0x51, 0x10, 0xB4, 0x61, 0x1A, + 0x62, 0x63, 0x31, 0x45, 0xC0, 0x6E, 0x0E, 0x68, + 0x94, 0x81, 0x27, 0x04, 0x45, 0x33, 0xE6, 0x3A, + 0x01, 0x05, 0xDF, 0x53, 0x1D, 0x89, 0xCD, 0x91, + 0x28, 0xA5, 0x04, 0x3C, 0xC7, 0x1A, 0x02, 0x6E, + 0xF7, 0xCA, 0x8C, 0xD9, 0xE6, 0x9D, 0x21, 0x8D, + 0x98, 0x15, 0x85, 0x36, 0xF9, 0x2F, 0x8A, 0x1B, + 0xA7, 0xF0, 0x9A, 0xB6, 0xB6, 0xA8, 0xE1, 0x22, + 0xF2, 0x42, 0xDA, 0xBB, 0x31, 0x2F, 0x3F, 0x63, + 0x7A, 0x26, 0x21, 0x74, 0xD3, 0x1B, 0xF6, 0xB5, + 0x85, 0xFF, 0xAE, 0x5B, 0x7A, 0x03, 0x5B, 0xF6, + 0xF7, 0x1C, 0x35, 0xFD, 0xAD, 0x44, 0xCF, 0xD2, + 0xD7, 0x4F, 0x92, 0x08, 0xBE, 0x25, 0x8F, 0xF3, + 0x24, 0x94, 0x33, 0x28, 0xF6, 0x72, 0x2D, 0x9E, + 0xE1, 0x00, 0x3E, 0x5C, 0x50, 0xB1, 0xDF, 0x82, + 0xCC, 0x6D, 0x24, 0x1B, 0x0E, 0x2A, 0xE9, 0xCD, + 0x34, 0x8B, 0x1F, 0xD4, 0x7E, 0x92, 0x67, 0xAF, + 0xC1, 0xB2, 0xAE, 0x91, 0xEE, 0x51, 0xD6, 0xCB, + 0x0E, 0x31, 0x79, 0xAB, 0x10, 0x42, 0xA9, 0x5D, + 0xCF, 0x6A, 0x94, 0x83, 0xB8, 0x4B, 0x4B, 0x36, + 0xB3, 0x86, 0x1A, 0xA7, 0x25, 0x5E, 0x4C, 0x02, + 0x78, 0xBA, 0x36, 0x04, 0x65, 0x0C, 0x10, 0xBE, + 0x19, 0x48, 0x2F, 0x23, 0x17, 0x1B, 0x67, 0x1D, + 0xF1, 0xCF, 0x3B, 0x96, 0x0C, 0x07, 0x43, 0x01, + 0xCD, 0x93, 0xC1, 0xD1, 0x76, 0x03, 0xD1, 0x47, + 0xDA, 0xE2, 0xAE, 0xF8, 0x37, 0xA6, 0x29, 0x64, + 0xEF, 0x15, 0xE5, 0xFB, 0x4A, 0xAC, 0x0B, 0x8C, + 0x1C, 0xCA, 0xA4, 0xBE, 0x75, 0x4A, 0xB5, 0x72, + 0x8A, 0xE9, 0x13, 0x0C, 0x4C, 0x7D, 0x02, 0x88, + 0x0A, 0xB9, 0x47, 0x2D, 0x45, 0x55, 0x62, 0x16, + 0xD6, 0x99, 0x8B, 0x86, 0x82, 0x28, 0x3D, 0x19, + 0xD4, 0x2A, 0x90, 0xD5, 0xEF, 0x8E, 0x5D, 0x32, + 0x76, 0x7D, 0xC2, 0x82, 0x2C, 0x6D, 0xF7, 0x85, + 0x45, 0x75, 0x38, 0xAB, 0xAE, 0x83, 0x06, 0x3E, + 0xD9, 0xCB, 0x87, 0xC2, 0xD3, 0x70, 0xF2, 0x63, + 0xD5, 0xFA, 0xD7, 0x46, 0x6D, 0x84, 0x99, 0xEB, + 0x8F, 0x46, 0x4A, 0x70, 0x25, 0x12, 0xB0, 0xCE, + 0xE7, 0x71, 0xE9, 0x13, 0x0D, 0x69, 0x77, 0x35, + 0xF8, 0x97, 0xFD, 0x03, 0x6C, 0xC5, 0x04, 0x32, + 0x6C, 0x3B, 0x01, 0x39, 0x9F, 0x64, 0x35, 0x32, + 0x29, 0x0F, 0x95, 0x8C, 0x0B, 0xBD, 0x90, 0x06, + 0x5D, 0xF0, 0x8B, 0xAB, 0xBD, 0x30, 0xAE, 0xB6, + 0x3B, 0x84, 0xC4, 0x60, 0x5D, 0x6C, 0xA3, 0x71, + 0x04, 0x71, 0x27, 0xD0, 0x3A, 0x72, 0xD5, 0x98, + 0xA1, 0xED, 0xAD, 0xFE, 0x70, 0x7E, 0x88, 0x47, + 0x25, 0xC1, 0x68, 0x90, 0x54, 0x9D, 0x69, 0x65, + 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF +}; /* RFC 3526, 5. Group 16 - 4096 Bit MODP * Generator: 2 @@ -269,6 +405,72 @@ 0x4D, 0xF4, 0x35, 0xC9, 0x34, 0x06, 0x31, 0x99, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; +static const u8 dh_group16_order[512] = { + 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xE4, 0x87, 0xED, 0x51, 0x10, 0xB4, 0x61, 0x1A, + 0x62, 0x63, 0x31, 0x45, 0xC0, 0x6E, 0x0E, 0x68, + 0x94, 0x81, 0x27, 0x04, 0x45, 0x33, 0xE6, 0x3A, + 0x01, 0x05, 0xDF, 0x53, 0x1D, 0x89, 0xCD, 0x91, + 0x28, 0xA5, 0x04, 0x3C, 0xC7, 0x1A, 0x02, 0x6E, + 0xF7, 0xCA, 0x8C, 0xD9, 0xE6, 0x9D, 0x21, 0x8D, + 0x98, 0x15, 0x85, 0x36, 0xF9, 0x2F, 0x8A, 0x1B, + 0xA7, 0xF0, 0x9A, 0xB6, 0xB6, 0xA8, 0xE1, 0x22, + 0xF2, 0x42, 0xDA, 0xBB, 0x31, 0x2F, 0x3F, 0x63, + 0x7A, 0x26, 0x21, 0x74, 0xD3, 0x1B, 0xF6, 0xB5, + 0x85, 0xFF, 0xAE, 0x5B, 0x7A, 0x03, 0x5B, 0xF6, + 0xF7, 0x1C, 0x35, 0xFD, 0xAD, 0x44, 0xCF, 0xD2, + 0xD7, 0x4F, 0x92, 0x08, 0xBE, 0x25, 0x8F, 0xF3, + 0x24, 0x94, 0x33, 0x28, 0xF6, 0x72, 0x2D, 0x9E, + 0xE1, 0x00, 0x3E, 0x5C, 0x50, 0xB1, 0xDF, 0x82, + 0xCC, 0x6D, 0x24, 0x1B, 0x0E, 0x2A, 0xE9, 0xCD, + 0x34, 0x8B, 0x1F, 0xD4, 0x7E, 0x92, 0x67, 0xAF, + 0xC1, 0xB2, 0xAE, 0x91, 0xEE, 0x51, 0xD6, 0xCB, + 0x0E, 0x31, 0x79, 0xAB, 0x10, 0x42, 0xA9, 0x5D, + 0xCF, 0x6A, 0x94, 0x83, 0xB8, 0x4B, 0x4B, 0x36, + 0xB3, 0x86, 0x1A, 0xA7, 0x25, 0x5E, 0x4C, 0x02, + 0x78, 0xBA, 0x36, 0x04, 0x65, 0x0C, 0x10, 0xBE, + 0x19, 0x48, 0x2F, 0x23, 0x17, 0x1B, 0x67, 0x1D, + 0xF1, 0xCF, 0x3B, 0x96, 0x0C, 0x07, 0x43, 0x01, + 0xCD, 0x93, 0xC1, 0xD1, 0x76, 0x03, 0xD1, 0x47, + 0xDA, 0xE2, 0xAE, 0xF8, 0x37, 0xA6, 0x29, 0x64, + 0xEF, 0x15, 0xE5, 0xFB, 0x4A, 0xAC, 0x0B, 0x8C, + 0x1C, 0xCA, 0xA4, 0xBE, 0x75, 0x4A, 0xB5, 0x72, + 0x8A, 0xE9, 0x13, 0x0C, 0x4C, 0x7D, 0x02, 0x88, + 0x0A, 0xB9, 0x47, 0x2D, 0x45, 0x55, 0x62, 0x16, + 0xD6, 0x99, 0x8B, 0x86, 0x82, 0x28, 0x3D, 0x19, + 0xD4, 0x2A, 0x90, 0xD5, 0xEF, 0x8E, 0x5D, 0x32, + 0x76, 0x7D, 0xC2, 0x82, 0x2C, 0x6D, 0xF7, 0x85, + 0x45, 0x75, 0x38, 0xAB, 0xAE, 0x83, 0x06, 0x3E, + 0xD9, 0xCB, 0x87, 0xC2, 0xD3, 0x70, 0xF2, 0x63, + 0xD5, 0xFA, 0xD7, 0x46, 0x6D, 0x84, 0x99, 0xEB, + 0x8F, 0x46, 0x4A, 0x70, 0x25, 0x12, 0xB0, 0xCE, + 0xE7, 0x71, 0xE9, 0x13, 0x0D, 0x69, 0x77, 0x35, + 0xF8, 0x97, 0xFD, 0x03, 0x6C, 0xC5, 0x04, 0x32, + 0x6C, 0x3B, 0x01, 0x39, 0x9F, 0x64, 0x35, 0x32, + 0x29, 0x0F, 0x95, 0x8C, 0x0B, 0xBD, 0x90, 0x06, + 0x5D, 0xF0, 0x8B, 0xAB, 0xBD, 0x30, 0xAE, 0xB6, + 0x3B, 0x84, 0xC4, 0x60, 0x5D, 0x6C, 0xA3, 0x71, + 0x04, 0x71, 0x27, 0xD0, 0x3A, 0x72, 0xD5, 0x98, + 0xA1, 0xED, 0xAD, 0xFE, 0x70, 0x7E, 0x88, 0x47, + 0x25, 0xC1, 0x68, 0x90, 0x54, 0x90, 0x84, 0x00, + 0x8D, 0x39, 0x1E, 0x09, 0x53, 0xC3, 0xF3, 0x6B, + 0xC4, 0x38, 0xCD, 0x08, 0x5E, 0xDD, 0x2D, 0x93, + 0x4C, 0xE1, 0x93, 0x8C, 0x35, 0x7A, 0x71, 0x1E, + 0x0D, 0x4A, 0x34, 0x1A, 0x5B, 0x0A, 0x85, 0xED, + 0x12, 0xC1, 0xF4, 0xE5, 0x15, 0x6A, 0x26, 0x74, + 0x6D, 0xDD, 0xE1, 0x6D, 0x82, 0x6F, 0x47, 0x7C, + 0x97, 0x47, 0x7E, 0x0A, 0x0F, 0xDF, 0x65, 0x53, + 0x14, 0x3E, 0x2C, 0xA3, 0xA7, 0x35, 0xE0, 0x2E, + 0xCC, 0xD9, 0x4B, 0x27, 0xD0, 0x48, 0x61, 0xD1, + 0x11, 0x9D, 0xD0, 0xC3, 0x28, 0xAD, 0xF3, 0xF6, + 0x8F, 0xB0, 0x94, 0xB8, 0x67, 0x71, 0x6B, 0xD7, + 0xDC, 0x0D, 0xEE, 0xBB, 0x10, 0xB8, 0x24, 0x0E, + 0x68, 0x03, 0x48, 0x93, 0xEA, 0xD8, 0x2D, 0x54, + 0xC9, 0xDA, 0x75, 0x4C, 0x46, 0xC7, 0xEE, 0xE0, + 0xC3, 0x7F, 0xDB, 0xEE, 0x48, 0x53, 0x60, 0x47, + 0xA6, 0xFA, 0x1A, 0xE4, 0x9A, 0x03, 0x18, 0xCC, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF +}; /* RFC 3526, 6. Group 17 - 6144 Bit MODP * Generator: 2 @@ -373,6 +575,104 @@ 0xE6, 0x94, 0xF9, 0x1E, 0x6D, 0xCC, 0x40, 0x24, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; +static const u8 dh_group17_order[768] = { + 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xE4, 0x87, 0xED, 0x51, 0x10, 0xB4, 0x61, 0x1A, + 0x62, 0x63, 0x31, 0x45, 0xC0, 0x6E, 0x0E, 0x68, + 0x94, 0x81, 0x27, 0x04, 0x45, 0x33, 0xE6, 0x3A, + 0x01, 0x05, 0xDF, 0x53, 0x1D, 0x89, 0xCD, 0x91, + 0x28, 0xA5, 0x04, 0x3C, 0xC7, 0x1A, 0x02, 0x6E, + 0xF7, 0xCA, 0x8C, 0xD9, 0xE6, 0x9D, 0x21, 0x8D, + 0x98, 0x15, 0x85, 0x36, 0xF9, 0x2F, 0x8A, 0x1B, + 0xA7, 0xF0, 0x9A, 0xB6, 0xB6, 0xA8, 0xE1, 0x22, + 0xF2, 0x42, 0xDA, 0xBB, 0x31, 0x2F, 0x3F, 0x63, + 0x7A, 0x26, 0x21, 0x74, 0xD3, 0x1B, 0xF6, 0xB5, + 0x85, 0xFF, 0xAE, 0x5B, 0x7A, 0x03, 0x5B, 0xF6, + 0xF7, 0x1C, 0x35, 0xFD, 0xAD, 0x44, 0xCF, 0xD2, + 0xD7, 0x4F, 0x92, 0x08, 0xBE, 0x25, 0x8F, 0xF3, + 0x24, 0x94, 0x33, 0x28, 0xF6, 0x72, 0x2D, 0x9E, + 0xE1, 0x00, 0x3E, 0x5C, 0x50, 0xB1, 0xDF, 0x82, + 0xCC, 0x6D, 0x24, 0x1B, 0x0E, 0x2A, 0xE9, 0xCD, + 0x34, 0x8B, 0x1F, 0xD4, 0x7E, 0x92, 0x67, 0xAF, + 0xC1, 0xB2, 0xAE, 0x91, 0xEE, 0x51, 0xD6, 0xCB, + 0x0E, 0x31, 0x79, 0xAB, 0x10, 0x42, 0xA9, 0x5D, + 0xCF, 0x6A, 0x94, 0x83, 0xB8, 0x4B, 0x4B, 0x36, + 0xB3, 0x86, 0x1A, 0xA7, 0x25, 0x5E, 0x4C, 0x02, + 0x78, 0xBA, 0x36, 0x04, 0x65, 0x0C, 0x10, 0xBE, + 0x19, 0x48, 0x2F, 0x23, 0x17, 0x1B, 0x67, 0x1D, + 0xF1, 0xCF, 0x3B, 0x96, 0x0C, 0x07, 0x43, 0x01, + 0xCD, 0x93, 0xC1, 0xD1, 0x76, 0x03, 0xD1, 0x47, + 0xDA, 0xE2, 0xAE, 0xF8, 0x37, 0xA6, 0x29, 0x64, + 0xEF, 0x15, 0xE5, 0xFB, 0x4A, 0xAC, 0x0B, 0x8C, + 0x1C, 0xCA, 0xA4, 0xBE, 0x75, 0x4A, 0xB5, 0x72, + 0x8A, 0xE9, 0x13, 0x0C, 0x4C, 0x7D, 0x02, 0x88, + 0x0A, 0xB9, 0x47, 0x2D, 0x45, 0x55, 0x62, 0x16, + 0xD6, 0x99, 0x8B, 0x86, 0x82, 0x28, 0x3D, 0x19, + 0xD4, 0x2A, 0x90, 0xD5, 0xEF, 0x8E, 0x5D, 0x32, + 0x76, 0x7D, 0xC2, 0x82, 0x2C, 0x6D, 0xF7, 0x85, + 0x45, 0x75, 0x38, 0xAB, 0xAE, 0x83, 0x06, 0x3E, + 0xD9, 0xCB, 0x87, 0xC2, 0xD3, 0x70, 0xF2, 0x63, + 0xD5, 0xFA, 0xD7, 0x46, 0x6D, 0x84, 0x99, 0xEB, + 0x8F, 0x46, 0x4A, 0x70, 0x25, 0x12, 0xB0, 0xCE, + 0xE7, 0x71, 0xE9, 0x13, 0x0D, 0x69, 0x77, 0x35, + 0xF8, 0x97, 0xFD, 0x03, 0x6C, 0xC5, 0x04, 0x32, + 0x6C, 0x3B, 0x01, 0x39, 0x9F, 0x64, 0x35, 0x32, + 0x29, 0x0F, 0x95, 0x8C, 0x0B, 0xBD, 0x90, 0x06, + 0x5D, 0xF0, 0x8B, 0xAB, 0xBD, 0x30, 0xAE, 0xB6, + 0x3B, 0x84, 0xC4, 0x60, 0x5D, 0x6C, 0xA3, 0x71, + 0x04, 0x71, 0x27, 0xD0, 0x3A, 0x72, 0xD5, 0x98, + 0xA1, 0xED, 0xAD, 0xFE, 0x70, 0x7E, 0x88, 0x47, + 0x25, 0xC1, 0x68, 0x90, 0x54, 0x90, 0x84, 0x00, + 0x8D, 0x39, 0x1E, 0x09, 0x53, 0xC3, 0xF3, 0x6B, + 0xC4, 0x38, 0xCD, 0x08, 0x5E, 0xDD, 0x2D, 0x93, + 0x4C, 0xE1, 0x93, 0x8C, 0x35, 0x7A, 0x71, 0x1E, + 0x0D, 0x4A, 0x34, 0x1A, 0x5B, 0x0A, 0x85, 0xED, + 0x12, 0xC1, 0xF4, 0xE5, 0x15, 0x6A, 0x26, 0x74, + 0x6D, 0xDD, 0xE1, 0x6D, 0x82, 0x6F, 0x47, 0x7C, + 0x97, 0x47, 0x7E, 0x0A, 0x0F, 0xDF, 0x65, 0x53, + 0x14, 0x3E, 0x2C, 0xA3, 0xA7, 0x35, 0xE0, 0x2E, + 0xCC, 0xD9, 0x4B, 0x27, 0xD0, 0x48, 0x61, 0xD1, + 0x11, 0x9D, 0xD0, 0xC3, 0x28, 0xAD, 0xF3, 0xF6, + 0x8F, 0xB0, 0x94, 0xB8, 0x67, 0x71, 0x6B, 0xD7, + 0xDC, 0x0D, 0xEE, 0xBB, 0x10, 0xB8, 0x24, 0x0E, + 0x68, 0x03, 0x48, 0x93, 0xEA, 0xD8, 0x2D, 0x54, + 0xC9, 0xDA, 0x75, 0x4C, 0x46, 0xC7, 0xEE, 0xE0, + 0xC3, 0x7F, 0xDB, 0xEE, 0x48, 0x53, 0x60, 0x47, + 0xA6, 0xFA, 0x1A, 0xE4, 0x9A, 0x01, 0x42, 0x49, + 0x1B, 0x61, 0xFD, 0x5A, 0x69, 0x3E, 0x38, 0x13, + 0x60, 0xEA, 0x6E, 0x59, 0x30, 0x13, 0x23, 0x6F, + 0x64, 0xBA, 0x8F, 0x3B, 0x1E, 0xDD, 0x1B, 0xDE, + 0xFC, 0x7F, 0xCA, 0x03, 0x56, 0xCF, 0x29, 0x87, + 0x72, 0xED, 0x9C, 0x17, 0xA0, 0x98, 0x00, 0xD7, + 0x58, 0x35, 0x29, 0xF6, 0xC8, 0x13, 0xEC, 0x18, + 0x8B, 0xCB, 0x93, 0xD8, 0x43, 0x2D, 0x44, 0x8C, + 0x6D, 0x1F, 0x6D, 0xF5, 0xE7, 0xCD, 0x8A, 0x76, + 0xA2, 0x67, 0x36, 0x5D, 0x67, 0x6A, 0x5D, 0x8D, + 0xED, 0xBF, 0x8A, 0x23, 0xF3, 0x66, 0x12, 0xA5, + 0x99, 0x90, 0x28, 0xA8, 0x95, 0xEB, 0xD7, 0xA1, + 0x37, 0xDC, 0x7A, 0x00, 0x9B, 0xC6, 0x69, 0x5F, + 0xAC, 0xC1, 0xE5, 0x00, 0xE3, 0x25, 0xC9, 0x76, + 0x78, 0x19, 0x75, 0x0A, 0xE8, 0xB9, 0x0E, 0x81, + 0xFA, 0x41, 0x6B, 0xE7, 0x37, 0x3A, 0x7F, 0x7B, + 0x6A, 0xAF, 0x38, 0x17, 0xA3, 0x4C, 0x06, 0x41, + 0x5A, 0xD4, 0x20, 0x18, 0xC8, 0x05, 0x8E, 0x4F, + 0x2C, 0xF3, 0xE4, 0xBF, 0xDF, 0x63, 0xF4, 0x79, + 0x91, 0xD4, 0xBD, 0x3F, 0x1B, 0x66, 0x44, 0x5F, + 0x07, 0x8E, 0xA2, 0xDB, 0xFF, 0xAC, 0x2D, 0x62, + 0xA5, 0xEA, 0x03, 0xD9, 0x15, 0xA0, 0xAA, 0x55, + 0x66, 0x47, 0xB6, 0xBF, 0x5F, 0xA4, 0x70, 0xEC, + 0x0A, 0x66, 0x2F, 0x69, 0x07, 0xC0, 0x1B, 0xF0, + 0x53, 0xCB, 0x8A, 0xF7, 0x79, 0x4D, 0xF1, 0x94, + 0x03, 0x50, 0xEA, 0xC5, 0xDB, 0xE2, 0xED, 0x3B, + 0x7A, 0xA8, 0x55, 0x1E, 0xC5, 0x0F, 0xDF, 0xF8, + 0x75, 0x8C, 0xE6, 0x58, 0xD1, 0x89, 0xEA, 0xAE, + 0x6D, 0x2B, 0x64, 0xF6, 0x17, 0x79, 0x4B, 0x19, + 0x1C, 0x3F, 0xF4, 0x6B, 0xB7, 0x1E, 0x02, 0x34, + 0x02, 0x1F, 0x47, 0xB3, 0x1F, 0xA4, 0x30, 0x77, + 0x09, 0x5F, 0x96, 0xAD, 0x85, 0xBA, 0x3A, 0x6B, + 0x73, 0x4A, 0x7C, 0x8F, 0x36, 0xE6, 0x20, 0x12, + 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF +}; /* RFC 3526, 7. Group 18 - 8192 Bit MODP * Generator: 2 @@ -509,29 +809,367 @@ 0x60, 0xC9, 0x80, 0xDD, 0x98, 0xED, 0xD3, 0xDF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; +static const u8 dh_group18_order[1024] = { + 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xE4, 0x87, 0xED, 0x51, 0x10, 0xB4, 0x61, 0x1A, + 0x62, 0x63, 0x31, 0x45, 0xC0, 0x6E, 0x0E, 0x68, + 0x94, 0x81, 0x27, 0x04, 0x45, 0x33, 0xE6, 0x3A, + 0x01, 0x05, 0xDF, 0x53, 0x1D, 0x89, 0xCD, 0x91, + 0x28, 0xA5, 0x04, 0x3C, 0xC7, 0x1A, 0x02, 0x6E, + 0xF7, 0xCA, 0x8C, 0xD9, 0xE6, 0x9D, 0x21, 0x8D, + 0x98, 0x15, 0x85, 0x36, 0xF9, 0x2F, 0x8A, 0x1B, + 0xA7, 0xF0, 0x9A, 0xB6, 0xB6, 0xA8, 0xE1, 0x22, + 0xF2, 0x42, 0xDA, 0xBB, 0x31, 0x2F, 0x3F, 0x63, + 0x7A, 0x26, 0x21, 0x74, 0xD3, 0x1B, 0xF6, 0xB5, + 0x85, 0xFF, 0xAE, 0x5B, 0x7A, 0x03, 0x5B, 0xF6, + 0xF7, 0x1C, 0x35, 0xFD, 0xAD, 0x44, 0xCF, 0xD2, + 0xD7, 0x4F, 0x92, 0x08, 0xBE, 0x25, 0x8F, 0xF3, + 0x24, 0x94, 0x33, 0x28, 0xF6, 0x72, 0x2D, 0x9E, + 0xE1, 0x00, 0x3E, 0x5C, 0x50, 0xB1, 0xDF, 0x82, + 0xCC, 0x6D, 0x24, 0x1B, 0x0E, 0x2A, 0xE9, 0xCD, + 0x34, 0x8B, 0x1F, 0xD4, 0x7E, 0x92, 0x67, 0xAF, + 0xC1, 0xB2, 0xAE, 0x91, 0xEE, 0x51, 0xD6, 0xCB, + 0x0E, 0x31, 0x79, 0xAB, 0x10, 0x42, 0xA9, 0x5D, + 0xCF, 0x6A, 0x94, 0x83, 0xB8, 0x4B, 0x4B, 0x36, + 0xB3, 0x86, 0x1A, 0xA7, 0x25, 0x5E, 0x4C, 0x02, + 0x78, 0xBA, 0x36, 0x04, 0x65, 0x0C, 0x10, 0xBE, + 0x19, 0x48, 0x2F, 0x23, 0x17, 0x1B, 0x67, 0x1D, + 0xF1, 0xCF, 0x3B, 0x96, 0x0C, 0x07, 0x43, 0x01, + 0xCD, 0x93, 0xC1, 0xD1, 0x76, 0x03, 0xD1, 0x47, + 0xDA, 0xE2, 0xAE, 0xF8, 0x37, 0xA6, 0x29, 0x64, + 0xEF, 0x15, 0xE5, 0xFB, 0x4A, 0xAC, 0x0B, 0x8C, + 0x1C, 0xCA, 0xA4, 0xBE, 0x75, 0x4A, 0xB5, 0x72, + 0x8A, 0xE9, 0x13, 0x0C, 0x4C, 0x7D, 0x02, 0x88, + 0x0A, 0xB9, 0x47, 0x2D, 0x45, 0x55, 0x62, 0x16, + 0xD6, 0x99, 0x8B, 0x86, 0x82, 0x28, 0x3D, 0x19, + 0xD4, 0x2A, 0x90, 0xD5, 0xEF, 0x8E, 0x5D, 0x32, + 0x76, 0x7D, 0xC2, 0x82, 0x2C, 0x6D, 0xF7, 0x85, + 0x45, 0x75, 0x38, 0xAB, 0xAE, 0x83, 0x06, 0x3E, + 0xD9, 0xCB, 0x87, 0xC2, 0xD3, 0x70, 0xF2, 0x63, + 0xD5, 0xFA, 0xD7, 0x46, 0x6D, 0x84, 0x99, 0xEB, + 0x8F, 0x46, 0x4A, 0x70, 0x25, 0x12, 0xB0, 0xCE, + 0xE7, 0x71, 0xE9, 0x13, 0x0D, 0x69, 0x77, 0x35, + 0xF8, 0x97, 0xFD, 0x03, 0x6C, 0xC5, 0x04, 0x32, + 0x6C, 0x3B, 0x01, 0x39, 0x9F, 0x64, 0x35, 0x32, + 0x29, 0x0F, 0x95, 0x8C, 0x0B, 0xBD, 0x90, 0x06, + 0x5D, 0xF0, 0x8B, 0xAB, 0xBD, 0x30, 0xAE, 0xB6, + 0x3B, 0x84, 0xC4, 0x60, 0x5D, 0x6C, 0xA3, 0x71, + 0x04, 0x71, 0x27, 0xD0, 0x3A, 0x72, 0xD5, 0x98, + 0xA1, 0xED, 0xAD, 0xFE, 0x70, 0x7E, 0x88, 0x47, + 0x25, 0xC1, 0x68, 0x90, 0x54, 0x90, 0x84, 0x00, + 0x8D, 0x39, 0x1E, 0x09, 0x53, 0xC3, 0xF3, 0x6B, + 0xC4, 0x38, 0xCD, 0x08, 0x5E, 0xDD, 0x2D, 0x93, + 0x4C, 0xE1, 0x93, 0x8C, 0x35, 0x7A, 0x71, 0x1E, + 0x0D, 0x4A, 0x34, 0x1A, 0x5B, 0x0A, 0x85, 0xED, + 0x12, 0xC1, 0xF4, 0xE5, 0x15, 0x6A, 0x26, 0x74, + 0x6D, 0xDD, 0xE1, 0x6D, 0x82, 0x6F, 0x47, 0x7C, + 0x97, 0x47, 0x7E, 0x0A, 0x0F, 0xDF, 0x65, 0x53, + 0x14, 0x3E, 0x2C, 0xA3, 0xA7, 0x35, 0xE0, 0x2E, + 0xCC, 0xD9, 0x4B, 0x27, 0xD0, 0x48, 0x61, 0xD1, + 0x11, 0x9D, 0xD0, 0xC3, 0x28, 0xAD, 0xF3, 0xF6, + 0x8F, 0xB0, 0x94, 0xB8, 0x67, 0x71, 0x6B, 0xD7, + 0xDC, 0x0D, 0xEE, 0xBB, 0x10, 0xB8, 0x24, 0x0E, + 0x68, 0x03, 0x48, 0x93, 0xEA, 0xD8, 0x2D, 0x54, + 0xC9, 0xDA, 0x75, 0x4C, 0x46, 0xC7, 0xEE, 0xE0, + 0xC3, 0x7F, 0xDB, 0xEE, 0x48, 0x53, 0x60, 0x47, + 0xA6, 0xFA, 0x1A, 0xE4, 0x9A, 0x01, 0x42, 0x49, + 0x1B, 0x61, 0xFD, 0x5A, 0x69, 0x3E, 0x38, 0x13, + 0x60, 0xEA, 0x6E, 0x59, 0x30, 0x13, 0x23, 0x6F, + 0x64, 0xBA, 0x8F, 0x3B, 0x1E, 0xDD, 0x1B, 0xDE, + 0xFC, 0x7F, 0xCA, 0x03, 0x56, 0xCF, 0x29, 0x87, + 0x72, 0xED, 0x9C, 0x17, 0xA0, 0x98, 0x00, 0xD7, + 0x58, 0x35, 0x29, 0xF6, 0xC8, 0x13, 0xEC, 0x18, + 0x8B, 0xCB, 0x93, 0xD8, 0x43, 0x2D, 0x44, 0x8C, + 0x6D, 0x1F, 0x6D, 0xF5, 0xE7, 0xCD, 0x8A, 0x76, + 0xA2, 0x67, 0x36, 0x5D, 0x67, 0x6A, 0x5D, 0x8D, + 0xED, 0xBF, 0x8A, 0x23, 0xF3, 0x66, 0x12, 0xA5, + 0x99, 0x90, 0x28, 0xA8, 0x95, 0xEB, 0xD7, 0xA1, + 0x37, 0xDC, 0x7A, 0x00, 0x9B, 0xC6, 0x69, 0x5F, + 0xAC, 0xC1, 0xE5, 0x00, 0xE3, 0x25, 0xC9, 0x76, + 0x78, 0x19, 0x75, 0x0A, 0xE8, 0xB9, 0x0E, 0x81, + 0xFA, 0x41, 0x6B, 0xE7, 0x37, 0x3A, 0x7F, 0x7B, + 0x6A, 0xAF, 0x38, 0x17, 0xA3, 0x4C, 0x06, 0x41, + 0x5A, 0xD4, 0x20, 0x18, 0xC8, 0x05, 0x8E, 0x4F, + 0x2C, 0xF3, 0xE4, 0xBF, 0xDF, 0x63, 0xF4, 0x79, + 0x91, 0xD4, 0xBD, 0x3F, 0x1B, 0x66, 0x44, 0x5F, + 0x07, 0x8E, 0xA2, 0xDB, 0xFF, 0xAC, 0x2D, 0x62, + 0xA5, 0xEA, 0x03, 0xD9, 0x15, 0xA0, 0xAA, 0x55, + 0x66, 0x47, 0xB6, 0xBF, 0x5F, 0xA4, 0x70, 0xEC, + 0x0A, 0x66, 0x2F, 0x69, 0x07, 0xC0, 0x1B, 0xF0, + 0x53, 0xCB, 0x8A, 0xF7, 0x79, 0x4D, 0xF1, 0x94, + 0x03, 0x50, 0xEA, 0xC5, 0xDB, 0xE2, 0xED, 0x3B, + 0x7A, 0xA8, 0x55, 0x1E, 0xC5, 0x0F, 0xDF, 0xF8, + 0x75, 0x8C, 0xE6, 0x58, 0xD1, 0x89, 0xEA, 0xAE, + 0x6D, 0x2B, 0x64, 0xF6, 0x17, 0x79, 0x4B, 0x19, + 0x1C, 0x3F, 0xF4, 0x6B, 0xB7, 0x1E, 0x02, 0x34, + 0x02, 0x1F, 0x47, 0xB3, 0x1F, 0xA4, 0x30, 0x77, + 0x09, 0x5F, 0x96, 0xAD, 0x85, 0xBA, 0x3A, 0x6B, + 0x73, 0x4A, 0x7C, 0x8F, 0x36, 0xDF, 0x08, 0xAC, + 0xBA, 0x51, 0xC9, 0x37, 0x89, 0x7F, 0x72, 0xF2, + 0x1C, 0x3B, 0xBE, 0x5B, 0x54, 0x99, 0x6F, 0xC6, + 0x6C, 0x5F, 0x62, 0x68, 0x39, 0xDC, 0x98, 0xDD, + 0x1D, 0xE4, 0x19, 0x5B, 0x46, 0xCE, 0xE9, 0x80, + 0x3A, 0x0F, 0xD3, 0xDF, 0xC5, 0x7E, 0x23, 0xF6, + 0x92, 0xBB, 0x7B, 0x49, 0xB5, 0xD2, 0x12, 0x33, + 0x1D, 0x55, 0xB1, 0xCE, 0x2D, 0x72, 0x7A, 0xB4, + 0x1A, 0x11, 0xDA, 0x3A, 0x15, 0xF8, 0xE4, 0xBC, + 0x11, 0xC7, 0x8B, 0x65, 0xF1, 0xCE, 0xB2, 0x96, + 0xF1, 0xFE, 0xDC, 0x5F, 0x7E, 0x42, 0x45, 0x6C, + 0x91, 0x11, 0x17, 0x02, 0x52, 0x01, 0xBE, 0x03, + 0x89, 0xF5, 0xAB, 0xD4, 0x0D, 0x11, 0xF8, 0x63, + 0x9A, 0x39, 0xFE, 0x32, 0x36, 0x75, 0x18, 0x35, + 0xA5, 0xE5, 0xE4, 0x43, 0x17, 0xC1, 0xC2, 0xEE, + 0xFD, 0x4E, 0xA5, 0xBF, 0xD1, 0x60, 0x43, 0xF4, + 0x3C, 0xB4, 0x19, 0x81, 0xF6, 0xAD, 0xEE, 0x9D, + 0x03, 0x15, 0x9E, 0x7A, 0xD9, 0xD1, 0x3C, 0x53, + 0x36, 0x95, 0x09, 0xFC, 0x1F, 0xA2, 0x7C, 0x16, + 0xEF, 0x98, 0x87, 0x70, 0x3A, 0x55, 0xB5, 0x1B, + 0x22, 0xCB, 0xF4, 0x4C, 0xD0, 0x12, 0xAE, 0xE0, + 0xB2, 0x79, 0x8E, 0x62, 0x84, 0x23, 0x42, 0x8E, + 0xFC, 0xD5, 0xA4, 0x0C, 0xAE, 0xF6, 0xBF, 0x50, + 0xD8, 0xEA, 0x88, 0x5E, 0xBF, 0x73, 0xA6, 0xB9, + 0xFD, 0x79, 0xB5, 0xE1, 0x8F, 0x67, 0xD1, 0x34, + 0x1A, 0xC8, 0x23, 0x7A, 0x75, 0xC3, 0xCF, 0xC9, + 0x20, 0x04, 0xA1, 0xC5, 0xA4, 0x0E, 0x36, 0x6B, + 0xC4, 0x4D, 0x00, 0x17, 0x6A, 0xF7, 0x1C, 0x15, + 0xE4, 0x8C, 0x86, 0xD3, 0x7E, 0x01, 0x37, 0x23, + 0xCA, 0xAC, 0x72, 0x23, 0xAB, 0x3B, 0xF4, 0xD5, + 0x4F, 0x18, 0x28, 0x71, 0x3B, 0x2B, 0x4A, 0x6F, + 0xE4, 0x0F, 0xAB, 0x74, 0x40, 0x5C, 0xB7, 0x38, + 0xB0, 0x64, 0xC0, 0x6E, 0xCC, 0x76, 0xE9, 0xEF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF +}; + +/* + * RFC 5114, 2.1. + * Group 22 - 1024-bit MODP Group with 160-bit Prime Order Subgroup + */ +static const u8 dh_group22_generator[] = { + 0xA4, 0xD1, 0xCB, 0xD5, 0xC3, 0xFD, 0x34, 0x12, + 0x67, 0x65, 0xA4, 0x42, 0xEF, 0xB9, 0x99, 0x05, + 0xF8, 0x10, 0x4D, 0xD2, 0x58, 0xAC, 0x50, 0x7F, + 0xD6, 0x40, 0x6C, 0xFF, 0x14, 0x26, 0x6D, 0x31, + 0x26, 0x6F, 0xEA, 0x1E, 0x5C, 0x41, 0x56, 0x4B, + 0x77, 0x7E, 0x69, 0x0F, 0x55, 0x04, 0xF2, 0x13, + 0x16, 0x02, 0x17, 0xB4, 0xB0, 0x1B, 0x88, 0x6A, + 0x5E, 0x91, 0x54, 0x7F, 0x9E, 0x27, 0x49, 0xF4, + 0xD7, 0xFB, 0xD7, 0xD3, 0xB9, 0xA9, 0x2E, 0xE1, + 0x90, 0x9D, 0x0D, 0x22, 0x63, 0xF8, 0x0A, 0x76, + 0xA6, 0xA2, 0x4C, 0x08, 0x7A, 0x09, 0x1F, 0x53, + 0x1D, 0xBF, 0x0A, 0x01, 0x69, 0xB6, 0xA2, 0x8A, + 0xD6, 0x62, 0xA4, 0xD1, 0x8E, 0x73, 0xAF, 0xA3, + 0x2D, 0x77, 0x9D, 0x59, 0x18, 0xD0, 0x8B, 0xC8, + 0x85, 0x8F, 0x4D, 0xCE, 0xF9, 0x7C, 0x2A, 0x24, + 0x85, 0x5E, 0x6E, 0xEB, 0x22, 0xB3, 0xB2, 0xE5 +}; +static const u8 dh_group22_prime[] = { + 0xB1, 0x0B, 0x8F, 0x96, 0xA0, 0x80, 0xE0, 0x1D, + 0xDE, 0x92, 0xDE, 0x5E, 0xAE, 0x5D, 0x54, 0xEC, + 0x52, 0xC9, 0x9F, 0xBC, 0xFB, 0x06, 0xA3, 0xC6, + 0x9A, 0x6A, 0x9D, 0xCA, 0x52, 0xD2, 0x3B, 0x61, + 0x60, 0x73, 0xE2, 0x86, 0x75, 0xA2, 0x3D, 0x18, + 0x98, 0x38, 0xEF, 0x1E, 0x2E, 0xE6, 0x52, 0xC0, + 0x13, 0xEC, 0xB4, 0xAE, 0xA9, 0x06, 0x11, 0x23, + 0x24, 0x97, 0x5C, 0x3C, 0xD4, 0x9B, 0x83, 0xBF, + 0xAC, 0xCB, 0xDD, 0x7D, 0x90, 0xC4, 0xBD, 0x70, + 0x98, 0x48, 0x8E, 0x9C, 0x21, 0x9A, 0x73, 0x72, + 0x4E, 0xFF, 0xD6, 0xFA, 0xE5, 0x64, 0x47, 0x38, + 0xFA, 0xA3, 0x1A, 0x4F, 0xF5, 0x5B, 0xCC, 0xC0, + 0xA1, 0x51, 0xAF, 0x5F, 0x0D, 0xC8, 0xB4, 0xBD, + 0x45, 0xBF, 0x37, 0xDF, 0x36, 0x5C, 0x1A, 0x65, + 0xE6, 0x8C, 0xFD, 0xA7, 0x6D, 0x4D, 0xA7, 0x08, + 0xDF, 0x1F, 0xB2, 0xBC, 0x2E, 0x4A, 0x43, 0x71 +}; +static const u8 dh_group22_order[] = { + 0xF5, 0x18, 0xAA, 0x87, 0x81, 0xA8, 0xDF, 0x27, + 0x8A, 0xBA, 0x4E, 0x7D, 0x64, 0xB7, 0xCB, 0x9D, + 0x49, 0x46, 0x23, 0x53 +}; + +/* + * RFC 5114, 2.2. + * Group 23 - 2048-bit MODP Group with 224-bit Prime Order Subgroup + */ +static const u8 dh_group23_generator[] = { + 0xAC, 0x40, 0x32, 0xEF, 0x4F, 0x2D, 0x9A, 0xE3, + 0x9D, 0xF3, 0x0B, 0x5C, 0x8F, 0xFD, 0xAC, 0x50, + 0x6C, 0xDE, 0xBE, 0x7B, 0x89, 0x99, 0x8C, 0xAF, + 0x74, 0x86, 0x6A, 0x08, 0xCF, 0xE4, 0xFF, 0xE3, + 0xA6, 0x82, 0x4A, 0x4E, 0x10, 0xB9, 0xA6, 0xF0, + 0xDD, 0x92, 0x1F, 0x01, 0xA7, 0x0C, 0x4A, 0xFA, + 0xAB, 0x73, 0x9D, 0x77, 0x00, 0xC2, 0x9F, 0x52, + 0xC5, 0x7D, 0xB1, 0x7C, 0x62, 0x0A, 0x86, 0x52, + 0xBE, 0x5E, 0x90, 0x01, 0xA8, 0xD6, 0x6A, 0xD7, + 0xC1, 0x76, 0x69, 0x10, 0x19, 0x99, 0x02, 0x4A, + 0xF4, 0xD0, 0x27, 0x27, 0x5A, 0xC1, 0x34, 0x8B, + 0xB8, 0xA7, 0x62, 0xD0, 0x52, 0x1B, 0xC9, 0x8A, + 0xE2, 0x47, 0x15, 0x04, 0x22, 0xEA, 0x1E, 0xD4, + 0x09, 0x93, 0x9D, 0x54, 0xDA, 0x74, 0x60, 0xCD, + 0xB5, 0xF6, 0xC6, 0xB2, 0x50, 0x71, 0x7C, 0xBE, + 0xF1, 0x80, 0xEB, 0x34, 0x11, 0x8E, 0x98, 0xD1, + 0x19, 0x52, 0x9A, 0x45, 0xD6, 0xF8, 0x34, 0x56, + 0x6E, 0x30, 0x25, 0xE3, 0x16, 0xA3, 0x30, 0xEF, + 0xBB, 0x77, 0xA8, 0x6F, 0x0C, 0x1A, 0xB1, 0x5B, + 0x05, 0x1A, 0xE3, 0xD4, 0x28, 0xC8, 0xF8, 0xAC, + 0xB7, 0x0A, 0x81, 0x37, 0x15, 0x0B, 0x8E, 0xEB, + 0x10, 0xE1, 0x83, 0xED, 0xD1, 0x99, 0x63, 0xDD, + 0xD9, 0xE2, 0x63, 0xE4, 0x77, 0x05, 0x89, 0xEF, + 0x6A, 0xA2, 0x1E, 0x7F, 0x5F, 0x2F, 0xF3, 0x81, + 0xB5, 0x39, 0xCC, 0xE3, 0x40, 0x9D, 0x13, 0xCD, + 0x56, 0x6A, 0xFB, 0xB4, 0x8D, 0x6C, 0x01, 0x91, + 0x81, 0xE1, 0xBC, 0xFE, 0x94, 0xB3, 0x02, 0x69, + 0xED, 0xFE, 0x72, 0xFE, 0x9B, 0x6A, 0xA4, 0xBD, + 0x7B, 0x5A, 0x0F, 0x1C, 0x71, 0xCF, 0xFF, 0x4C, + 0x19, 0xC4, 0x18, 0xE1, 0xF6, 0xEC, 0x01, 0x79, + 0x81, 0xBC, 0x08, 0x7F, 0x2A, 0x70, 0x65, 0xB3, + 0x84, 0xB8, 0x90, 0xD3, 0x19, 0x1F, 0x2B, 0xFA +}; +static const u8 dh_group23_prime[] = { + 0xAD, 0x10, 0x7E, 0x1E, 0x91, 0x23, 0xA9, 0xD0, + 0xD6, 0x60, 0xFA, 0xA7, 0x95, 0x59, 0xC5, 0x1F, + 0xA2, 0x0D, 0x64, 0xE5, 0x68, 0x3B, 0x9F, 0xD1, + 0xB5, 0x4B, 0x15, 0x97, 0xB6, 0x1D, 0x0A, 0x75, + 0xE6, 0xFA, 0x14, 0x1D, 0xF9, 0x5A, 0x56, 0xDB, + 0xAF, 0x9A, 0x3C, 0x40, 0x7B, 0xA1, 0xDF, 0x15, + 0xEB, 0x3D, 0x68, 0x8A, 0x30, 0x9C, 0x18, 0x0E, + 0x1D, 0xE6, 0xB8, 0x5A, 0x12, 0x74, 0xA0, 0xA6, + 0x6D, 0x3F, 0x81, 0x52, 0xAD, 0x6A, 0xC2, 0x12, + 0x90, 0x37, 0xC9, 0xED, 0xEF, 0xDA, 0x4D, 0xF8, + 0xD9, 0x1E, 0x8F, 0xEF, 0x55, 0xB7, 0x39, 0x4B, + 0x7A, 0xD5, 0xB7, 0xD0, 0xB6, 0xC1, 0x22, 0x07, + 0xC9, 0xF9, 0x8D, 0x11, 0xED, 0x34, 0xDB, 0xF6, + 0xC6, 0xBA, 0x0B, 0x2C, 0x8B, 0xBC, 0x27, 0xBE, + 0x6A, 0x00, 0xE0, 0xA0, 0xB9, 0xC4, 0x97, 0x08, + 0xB3, 0xBF, 0x8A, 0x31, 0x70, 0x91, 0x88, 0x36, + 0x81, 0x28, 0x61, 0x30, 0xBC, 0x89, 0x85, 0xDB, + 0x16, 0x02, 0xE7, 0x14, 0x41, 0x5D, 0x93, 0x30, + 0x27, 0x82, 0x73, 0xC7, 0xDE, 0x31, 0xEF, 0xDC, + 0x73, 0x10, 0xF7, 0x12, 0x1F, 0xD5, 0xA0, 0x74, + 0x15, 0x98, 0x7D, 0x9A, 0xDC, 0x0A, 0x48, 0x6D, + 0xCD, 0xF9, 0x3A, 0xCC, 0x44, 0x32, 0x83, 0x87, + 0x31, 0x5D, 0x75, 0xE1, 0x98, 0xC6, 0x41, 0xA4, + 0x80, 0xCD, 0x86, 0xA1, 0xB9, 0xE5, 0x87, 0xE8, + 0xBE, 0x60, 0xE6, 0x9C, 0xC9, 0x28, 0xB2, 0xB9, + 0xC5, 0x21, 0x72, 0xE4, 0x13, 0x04, 0x2E, 0x9B, + 0x23, 0xF1, 0x0B, 0x0E, 0x16, 0xE7, 0x97, 0x63, + 0xC9, 0xB5, 0x3D, 0xCF, 0x4B, 0xA8, 0x0A, 0x29, + 0xE3, 0xFB, 0x73, 0xC1, 0x6B, 0x8E, 0x75, 0xB9, + 0x7E, 0xF3, 0x63, 0xE2, 0xFF, 0xA3, 0x1F, 0x71, + 0xCF, 0x9D, 0xE5, 0x38, 0x4E, 0x71, 0xB8, 0x1C, + 0x0A, 0xC4, 0xDF, 0xFE, 0x0C, 0x10, 0xE6, 0x4F +}; +static const u8 dh_group23_order[] = { + 0x80, 0x1C, 0x0D, 0x34, 0xC5, 0x8D, 0x93, 0xFE, + 0x99, 0x71, 0x77, 0x10, 0x1F, 0x80, 0x53, 0x5A, + 0x47, 0x38, 0xCE, 0xBC, 0xBF, 0x38, 0x9A, 0x99, + 0xB3, 0x63, 0x71, 0xEB +}; + +/* + * RFC 5114, 2.3. + * Group 24 - 2048-bit MODP Group with 256-bit Prime Order Subgroup + */ +static const u8 dh_group24_generator[] = { + 0x3F, 0xB3, 0x2C, 0x9B, 0x73, 0x13, 0x4D, 0x0B, + 0x2E, 0x77, 0x50, 0x66, 0x60, 0xED, 0xBD, 0x48, + 0x4C, 0xA7, 0xB1, 0x8F, 0x21, 0xEF, 0x20, 0x54, + 0x07, 0xF4, 0x79, 0x3A, 0x1A, 0x0B, 0xA1, 0x25, + 0x10, 0xDB, 0xC1, 0x50, 0x77, 0xBE, 0x46, 0x3F, + 0xFF, 0x4F, 0xED, 0x4A, 0xAC, 0x0B, 0xB5, 0x55, + 0xBE, 0x3A, 0x6C, 0x1B, 0x0C, 0x6B, 0x47, 0xB1, + 0xBC, 0x37, 0x73, 0xBF, 0x7E, 0x8C, 0x6F, 0x62, + 0x90, 0x12, 0x28, 0xF8, 0xC2, 0x8C, 0xBB, 0x18, + 0xA5, 0x5A, 0xE3, 0x13, 0x41, 0x00, 0x0A, 0x65, + 0x01, 0x96, 0xF9, 0x31, 0xC7, 0x7A, 0x57, 0xF2, + 0xDD, 0xF4, 0x63, 0xE5, 0xE9, 0xEC, 0x14, 0x4B, + 0x77, 0x7D, 0xE6, 0x2A, 0xAA, 0xB8, 0xA8, 0x62, + 0x8A, 0xC3, 0x76, 0xD2, 0x82, 0xD6, 0xED, 0x38, + 0x64, 0xE6, 0x79, 0x82, 0x42, 0x8E, 0xBC, 0x83, + 0x1D, 0x14, 0x34, 0x8F, 0x6F, 0x2F, 0x91, 0x93, + 0xB5, 0x04, 0x5A, 0xF2, 0x76, 0x71, 0x64, 0xE1, + 0xDF, 0xC9, 0x67, 0xC1, 0xFB, 0x3F, 0x2E, 0x55, + 0xA4, 0xBD, 0x1B, 0xFF, 0xE8, 0x3B, 0x9C, 0x80, + 0xD0, 0x52, 0xB9, 0x85, 0xD1, 0x82, 0xEA, 0x0A, + 0xDB, 0x2A, 0x3B, 0x73, 0x13, 0xD3, 0xFE, 0x14, + 0xC8, 0x48, 0x4B, 0x1E, 0x05, 0x25, 0x88, 0xB9, + 0xB7, 0xD2, 0xBB, 0xD2, 0xDF, 0x01, 0x61, 0x99, + 0xEC, 0xD0, 0x6E, 0x15, 0x57, 0xCD, 0x09, 0x15, + 0xB3, 0x35, 0x3B, 0xBB, 0x64, 0xE0, 0xEC, 0x37, + 0x7F, 0xD0, 0x28, 0x37, 0x0D, 0xF9, 0x2B, 0x52, + 0xC7, 0x89, 0x14, 0x28, 0xCD, 0xC6, 0x7E, 0xB6, + 0x18, 0x4B, 0x52, 0x3D, 0x1D, 0xB2, 0x46, 0xC3, + 0x2F, 0x63, 0x07, 0x84, 0x90, 0xF0, 0x0E, 0xF8, + 0xD6, 0x47, 0xD1, 0x48, 0xD4, 0x79, 0x54, 0x51, + 0x5E, 0x23, 0x27, 0xCF, 0xEF, 0x98, 0xC5, 0x82, + 0x66, 0x4B, 0x4C, 0x0F, 0x6C, 0xC4, 0x16, 0x59 +}; +static const u8 dh_group24_prime[] = { + 0x87, 0xA8, 0xE6, 0x1D, 0xB4, 0xB6, 0x66, 0x3C, + 0xFF, 0xBB, 0xD1, 0x9C, 0x65, 0x19, 0x59, 0x99, + 0x8C, 0xEE, 0xF6, 0x08, 0x66, 0x0D, 0xD0, 0xF2, + 0x5D, 0x2C, 0xEE, 0xD4, 0x43, 0x5E, 0x3B, 0x00, + 0xE0, 0x0D, 0xF8, 0xF1, 0xD6, 0x19, 0x57, 0xD4, + 0xFA, 0xF7, 0xDF, 0x45, 0x61, 0xB2, 0xAA, 0x30, + 0x16, 0xC3, 0xD9, 0x11, 0x34, 0x09, 0x6F, 0xAA, + 0x3B, 0xF4, 0x29, 0x6D, 0x83, 0x0E, 0x9A, 0x7C, + 0x20, 0x9E, 0x0C, 0x64, 0x97, 0x51, 0x7A, 0xBD, + 0x5A, 0x8A, 0x9D, 0x30, 0x6B, 0xCF, 0x67, 0xED, + 0x91, 0xF9, 0xE6, 0x72, 0x5B, 0x47, 0x58, 0xC0, + 0x22, 0xE0, 0xB1, 0xEF, 0x42, 0x75, 0xBF, 0x7B, + 0x6C, 0x5B, 0xFC, 0x11, 0xD4, 0x5F, 0x90, 0x88, + 0xB9, 0x41, 0xF5, 0x4E, 0xB1, 0xE5, 0x9B, 0xB8, + 0xBC, 0x39, 0xA0, 0xBF, 0x12, 0x30, 0x7F, 0x5C, + 0x4F, 0xDB, 0x70, 0xC5, 0x81, 0xB2, 0x3F, 0x76, + 0xB6, 0x3A, 0xCA, 0xE1, 0xCA, 0xA6, 0xB7, 0x90, + 0x2D, 0x52, 0x52, 0x67, 0x35, 0x48, 0x8A, 0x0E, + 0xF1, 0x3C, 0x6D, 0x9A, 0x51, 0xBF, 0xA4, 0xAB, + 0x3A, 0xD8, 0x34, 0x77, 0x96, 0x52, 0x4D, 0x8E, + 0xF6, 0xA1, 0x67, 0xB5, 0xA4, 0x18, 0x25, 0xD9, + 0x67, 0xE1, 0x44, 0xE5, 0x14, 0x05, 0x64, 0x25, + 0x1C, 0xCA, 0xCB, 0x83, 0xE6, 0xB4, 0x86, 0xF6, + 0xB3, 0xCA, 0x3F, 0x79, 0x71, 0x50, 0x60, 0x26, + 0xC0, 0xB8, 0x57, 0xF6, 0x89, 0x96, 0x28, 0x56, + 0xDE, 0xD4, 0x01, 0x0A, 0xBD, 0x0B, 0xE6, 0x21, + 0xC3, 0xA3, 0x96, 0x0A, 0x54, 0xE7, 0x10, 0xC3, + 0x75, 0xF2, 0x63, 0x75, 0xD7, 0x01, 0x41, 0x03, + 0xA4, 0xB5, 0x43, 0x30, 0xC1, 0x98, 0xAF, 0x12, + 0x61, 0x16, 0xD2, 0x27, 0x6E, 0x11, 0x71, 0x5F, + 0x69, 0x38, 0x77, 0xFA, 0xD7, 0xEF, 0x09, 0xCA, + 0xDB, 0x09, 0x4A, 0xE9, 0x1E, 0x1A, 0x15, 0x97 +}; +static const u8 dh_group24_order[] = { + 0x8C, 0xF8, 0x36, 0x42, 0xA7, 0x09, 0xA0, 0x97, + 0xB4, 0x47, 0x99, 0x76, 0x40, 0x12, 0x9D, 0xA2, + 0x99, 0xB1, 0xA4, 0x7D, 0x1E, 0xB3, 0x75, 0x0B, + 0xA3, 0x08, 0xB0, 0xFE, 0x64, 0xF5, 0xFB, 0xD3 +}; #endif /* ALL_DH_GROUPS */ -#define DH_GROUP(id) \ +#define DH_GROUP(id,safe) \ { id, dh_group ## id ## _generator, sizeof(dh_group ## id ## _generator), \ -dh_group ## id ## _prime, sizeof(dh_group ## id ## _prime) } +dh_group ## id ## _prime, sizeof(dh_group ## id ## _prime), \ +dh_group ## id ## _order, sizeof(dh_group ## id ## _order), safe } static struct dh_group dh_groups[] = { - DH_GROUP(5), + DH_GROUP(5, 1), #ifdef ALL_DH_GROUPS - DH_GROUP(1), - DH_GROUP(2), - DH_GROUP(14), - DH_GROUP(15), - DH_GROUP(16), - DH_GROUP(17), - DH_GROUP(18) + DH_GROUP(1, 1), + DH_GROUP(2, 1), + DH_GROUP(14, 1), + DH_GROUP(15, 1), + DH_GROUP(16, 1), + DH_GROUP(17, 1), + DH_GROUP(18, 1), + DH_GROUP(22, 0), + DH_GROUP(23, 0), + DH_GROUP(24, 0) #endif /* ALL_DH_GROUPS */ }; -#define NUM_DH_GROUPS (sizeof(dh_groups) / sizeof(dh_groups[0])) +#define NUM_DH_GROUPS ARRAY_SIZE(dh_groups) const struct dh_group * dh_groups_get(int id) diff -Nru wpa-1.0/src/crypto/dh_groups.h wpa-2.1/src/crypto/dh_groups.h --- wpa-1.0/src/crypto/dh_groups.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/crypto/dh_groups.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * Diffie-Hellman groups * Copyright (c) 2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef DH_GROUPS_H @@ -21,6 +15,9 @@ size_t generator_len; const u8 *prime; size_t prime_len; + const u8 *order; + size_t order_len; + unsigned int safe_prime:1; }; const struct dh_group * dh_groups_get(int id); diff -Nru wpa-1.0/src/crypto/fips_prf_cryptoapi.c wpa-2.1/src/crypto/fips_prf_cryptoapi.c --- wpa-1.0/src/crypto/fips_prf_cryptoapi.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/crypto/fips_prf_cryptoapi.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * FIPS 186-2 PRF for Microsoft CryptoAPI * Copyright (c) 2009, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff -Nru wpa-1.0/src/crypto/fips_prf_gnutls.c wpa-2.1/src/crypto/fips_prf_gnutls.c --- wpa-1.0/src/crypto/fips_prf_gnutls.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/crypto/fips_prf_gnutls.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * FIPS 186-2 PRF for libgcrypt * Copyright (c) 2004-2009, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff -Nru wpa-1.0/src/crypto/fips_prf_internal.c wpa-2.1/src/crypto/fips_prf_internal.c --- wpa-1.0/src/crypto/fips_prf_internal.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/crypto/fips_prf_internal.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * FIPS 186-2 PRF for internal crypto implementation * Copyright (c) 2006-2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff -Nru wpa-1.0/src/crypto/fips_prf_nss.c wpa-2.1/src/crypto/fips_prf_nss.c --- wpa-1.0/src/crypto/fips_prf_nss.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/crypto/fips_prf_nss.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * FIPS 186-2 PRF for NSS * Copyright (c) 2009, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff -Nru wpa-1.0/src/crypto/fips_prf_openssl.c wpa-2.1/src/crypto/fips_prf_openssl.c --- wpa-1.0/src/crypto/fips_prf_openssl.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/crypto/fips_prf_openssl.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * FIPS 186-2 PRF for libcrypto * Copyright (c) 2004-2005, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -37,13 +31,14 @@ u8 *xpos = x; u32 carry; - if (seed_len > sizeof(xkey)) + if (seed_len < sizeof(xkey)) + os_memset(xkey + seed_len, 0, sizeof(xkey) - seed_len); + else seed_len = sizeof(xkey); /* FIPS 186-2 + change notice 1 */ os_memcpy(xkey, seed, seed_len); - os_memset(xkey + seed_len, 0, 64 - seed_len); t[0] = 0x67452301; t[1] = 0xEFCDAB89; t[2] = 0x98BADCFE; diff -Nru wpa-1.0/src/crypto/Makefile wpa-2.1/src/crypto/Makefile --- wpa-1.0/src/crypto/Makefile 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/crypto/Makefile 2014-02-04 11:23:35.000000000 +0000 @@ -1,7 +1,7 @@ all: libcrypto.a clean: - rm -f *~ *.o *.d libcrypto.a + rm -f *~ *.o *.d *.gcno *.gcda *.gcov libcrypto.a install: @echo Nothing to be made. @@ -12,12 +12,15 @@ CFLAGS += -DCONFIG_TLS_INTERNAL_CLIENT CFLAGS += -DCONFIG_TLS_INTERNAL_SERVER #CFLAGS += -DALL_DH_GROUPS +CFLAGS += -DCONFIG_SHA256 LIB_OBJS= \ aes-cbc.o \ + aes-ccm.o \ aes-ctr.o \ aes-eax.o \ aes-encblock.o \ + aes-gcm.o \ aes-internal.o \ aes-internal-dec.o \ aes-internal-enc.o \ @@ -30,16 +33,18 @@ md4-internal.o \ md5.o \ md5-internal.o \ - md5-non-fips.o \ milenage.o \ ms_funcs.o \ rc4.o \ sha1.o \ sha1-internal.o \ sha1-pbkdf2.o \ + sha1-prf.o \ sha1-tlsprf.o \ sha1-tprf.o \ sha256.o \ + sha256-prf.o \ + sha256-tlsprf.o \ sha256-internal.o LIB_OBJS += crypto_internal.o diff -Nru wpa-1.0/src/crypto/md4-internal.c wpa-2.1/src/crypto/md4-internal.c --- wpa-1.0/src/crypto/md4-internal.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/crypto/md4-internal.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * MD4 hash implementation * Copyright (c) 2006, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff -Nru wpa-1.0/src/crypto/md5.c wpa-2.1/src/crypto/md5.c --- wpa-1.0/src/crypto/md5.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/crypto/md5.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * MD5 hash implementation and interface functions * Copyright (c) 2003-2005, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff -Nru wpa-1.0/src/crypto/md5.h wpa-2.1/src/crypto/md5.h --- wpa-1.0/src/crypto/md5.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/crypto/md5.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * MD5 hash implementation and interface functions * Copyright (c) 2003-2009, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef MD5_H @@ -21,15 +15,5 @@ const u8 *addr[], const size_t *len, u8 *mac); int hmac_md5(const u8 *key, size_t key_len, const u8 *data, size_t data_len, u8 *mac); -#ifdef CONFIG_FIPS -int hmac_md5_vector_non_fips_allow(const u8 *key, size_t key_len, - size_t num_elem, const u8 *addr[], - const size_t *len, u8 *mac); -int hmac_md5_non_fips_allow(const u8 *key, size_t key_len, const u8 *data, - size_t data_len, u8 *mac); -#else /* CONFIG_FIPS */ -#define hmac_md5_vector_non_fips_allow hmac_md5_vector -#define hmac_md5_non_fips_allow hmac_md5 -#endif /* CONFIG_FIPS */ #endif /* MD5_H */ diff -Nru wpa-1.0/src/crypto/md5_i.h wpa-2.1/src/crypto/md5_i.h --- wpa-1.0/src/crypto/md5_i.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/crypto/md5_i.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * MD5 internal definitions * Copyright (c) 2003-2005, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef MD5_I_H diff -Nru wpa-1.0/src/crypto/md5-internal.c wpa-2.1/src/crypto/md5-internal.c --- wpa-1.0/src/crypto/md5-internal.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/crypto/md5-internal.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * MD5 hash implementation and interface functions * Copyright (c) 2003-2005, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -182,8 +176,8 @@ byteReverse(ctx->in, 14); /* Append length in bits and transform */ - ((u32 *) ctx->in)[14] = ctx->bits[0]; - ((u32 *) ctx->in)[15] = ctx->bits[1]; + ((u32 *) aliasing_hide_typecast(ctx->in, u32))[14] = ctx->bits[0]; + ((u32 *) aliasing_hide_typecast(ctx->in, u32))[15] = ctx->bits[1]; MD5Transform(ctx->buf, (u32 *) ctx->in); byteReverse((unsigned char *) ctx->buf, 4); diff -Nru wpa-1.0/src/crypto/md5-non-fips.c wpa-2.1/src/crypto/md5-non-fips.c --- wpa-1.0/src/crypto/md5-non-fips.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/crypto/md5-non-fips.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,113 +0,0 @@ -/* - * MD5 hash implementation and interface functions (non-FIPS allowed cases) - * Copyright (c) 2003-2009, Jouni Malinen - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#include "includes.h" - -#include "common.h" -#include "md5.h" -#include "crypto.h" - - -/** - * hmac_md5_vector_non_fips_allow - HMAC-MD5 over data vector (RFC 2104) - * @key: Key for HMAC operations - * @key_len: Length of the key in bytes - * @num_elem: Number of elements in the data vector - * @addr: Pointers to the data areas - * @len: Lengths of the data blocks - * @mac: Buffer for the hash (16 bytes) - * Returns: 0 on success, -1 on failure - */ -int hmac_md5_vector_non_fips_allow(const u8 *key, size_t key_len, - size_t num_elem, const u8 *addr[], - const size_t *len, u8 *mac) -{ - u8 k_pad[64]; /* padding - key XORd with ipad/opad */ - u8 tk[16]; - const u8 *_addr[6]; - size_t i, _len[6]; - - if (num_elem > 5) { - /* - * Fixed limit on the number of fragments to avoid having to - * allocate memory (which could fail). - */ - return -1; - } - - /* if key is longer than 64 bytes reset it to key = MD5(key) */ - if (key_len > 64) { - if (md5_vector_non_fips_allow(1, &key, &key_len, tk)) - return -1; - key = tk; - key_len = 16; - } - - /* the HMAC_MD5 transform looks like: - * - * MD5(K XOR opad, MD5(K XOR ipad, text)) - * - * where K is an n byte key - * ipad is the byte 0x36 repeated 64 times - * opad is the byte 0x5c repeated 64 times - * and text is the data being protected */ - - /* start out by storing key in ipad */ - os_memset(k_pad, 0, sizeof(k_pad)); - os_memcpy(k_pad, key, key_len); - - /* XOR key with ipad values */ - for (i = 0; i < 64; i++) - k_pad[i] ^= 0x36; - - /* perform inner MD5 */ - _addr[0] = k_pad; - _len[0] = 64; - for (i = 0; i < num_elem; i++) { - _addr[i + 1] = addr[i]; - _len[i + 1] = len[i]; - } - if (md5_vector_non_fips_allow(1 + num_elem, _addr, _len, mac)) - return -1; - - os_memset(k_pad, 0, sizeof(k_pad)); - os_memcpy(k_pad, key, key_len); - /* XOR key with opad values */ - for (i = 0; i < 64; i++) - k_pad[i] ^= 0x5c; - - /* perform outer MD5 */ - _addr[0] = k_pad; - _len[0] = 64; - _addr[1] = mac; - _len[1] = MD5_MAC_LEN; - return md5_vector_non_fips_allow(2, _addr, _len, mac); -} - - -/** - * hmac_md5_non_fips_allow - HMAC-MD5 over data buffer (RFC 2104) - * @key: Key for HMAC operations - * @key_len: Length of the key in bytes - * @data: Pointers to the data area - * @data_len: Length of the data area - * @mac: Buffer for the hash (16 bytes) - * Returns: 0 on success, -1 on failure - */ -int hmac_md5_non_fips_allow(const u8 *key, size_t key_len, const u8 *data, - size_t data_len, u8 *mac) -{ - return hmac_md5_vector_non_fips_allow(key, key_len, 1, &data, - &data_len, mac); -} diff -Nru wpa-1.0/src/crypto/milenage.c wpa-2.1/src/crypto/milenage.c --- wpa-1.0/src/crypto/milenage.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/crypto/milenage.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * 3GPP AKA - Milenage algorithm (3GPP TS 35.205, .206, .207, .208) * Copyright (c) 2006-2007 * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. * * This file implements an example authentication algorithm defined for 3GPP * AKA. This can be used to implement a simple HLR/AuC into hlr_auc_gw to allow diff -Nru wpa-1.0/src/crypto/milenage.h wpa-2.1/src/crypto/milenage.h --- wpa-1.0/src/crypto/milenage.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/crypto/milenage.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * UMTS AKA - Milenage algorithm (3GPP TS 35.205, .206, .207, .208) * Copyright (c) 2006-2007 * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef MILENAGE_H diff -Nru wpa-1.0/src/crypto/ms_funcs.c wpa-2.1/src/crypto/ms_funcs.c --- wpa-1.0/src/crypto/ms_funcs.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/crypto/ms_funcs.c 2014-02-04 11:23:35.000000000 +0000 @@ -1,15 +1,9 @@ /* * WPA Supplicant / shared MSCHAPV2 helper functions / RFC 2433 / RFC 2759 - * Copyright (c) 2004-2009, Jouni Malinen + * Copyright (c) 2004-2012, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -179,8 +173,9 @@ u8 challenge[8]; u8 password_hash[16]; - challenge_hash(peer_challenge, auth_challenge, username, username_len, - challenge); + if (challenge_hash(peer_challenge, auth_challenge, username, + username_len, challenge)) + return -1; if (nt_password_hash(password, password_len, password_hash)) return -1; challenge_response(challenge, password_hash, response); @@ -266,8 +261,9 @@ if (sha1_vector(3, addr1, len1, response)) return -1; - challenge_hash(peer_challenge, auth_challenge, username, username_len, - challenge); + if (challenge_hash(peer_challenge, auth_challenge, username, + username_len, challenge)) + return -1; return sha1_vector(3, addr2, len2, response); } diff -Nru wpa-1.0/src/crypto/ms_funcs.h wpa-2.1/src/crypto/ms_funcs.h --- wpa-1.0/src/crypto/ms_funcs.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/crypto/ms_funcs.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * WPA Supplicant / shared MSCHAPV2 helper functions / RFC 2433 / RFC 2759 * Copyright (c) 2004-2009, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef MS_FUNCS_H diff -Nru wpa-1.0/src/crypto/random.c wpa-2.1/src/crypto/random.c --- wpa-1.0/src/crypto/random.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/crypto/random.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * Random number generator * Copyright (c) 2010-2011, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. * * This random number generator is used to provide additional entropy to the * one provided by the operating system (os_get_random()) for session key @@ -35,6 +29,7 @@ #include "utils/common.h" #include "utils/eloop.h" +#include "crypto/crypto.h" #include "sha1.h" #include "random.h" @@ -134,8 +129,6 @@ static unsigned int count = 0; count++; - wpa_printf(MSG_MSGDUMP, "Add randomness: count=%u entropy=%u", - count, entropy); if (entropy > MIN_COLLECT_ENTROPY && (count & 0x3ff) != 0) { /* * No need to add more entropy at this point, so save CPU and @@ -143,6 +136,8 @@ */ return; } + wpa_printf(MSG_EXCESSIVE, "Add randomness: count=%u entropy=%u", + count, entropy); os_get_time(&t); wpa_hexdump_key(MSG_EXCESSIVE, "random pool", @@ -183,6 +178,27 @@ *bytes++ ^= tmp[i]; left -= siz; } + +#ifdef CONFIG_FIPS + /* Mix in additional entropy from the crypto module */ + left = len; + while (left) { + size_t siz, i; + u8 tmp[EXTRACT_LEN]; + if (crypto_get_random(tmp, sizeof(tmp)) < 0) { + wpa_printf(MSG_ERROR, "random: No entropy available " + "for generating strong random bytes"); + return -1; + } + wpa_hexdump_key(MSG_EXCESSIVE, "random from crypto module", + tmp, sizeof(tmp)); + siz = left > EXTRACT_LEN ? EXTRACT_LEN : left; + for (i = 0; i < siz; i++) + *bytes++ ^= tmp[i]; + left -= siz; + } +#endif /* CONFIG_FIPS */ + wpa_hexdump_key(MSG_EXCESSIVE, "mixed random", buf, len); if (entropy < len) diff -Nru wpa-1.0/src/crypto/random.h wpa-2.1/src/crypto/random.h --- wpa-1.0/src/crypto/random.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/crypto/random.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * Random number generator * Copyright (c) 2010-2011, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef RANDOM_H diff -Nru wpa-1.0/src/crypto/rc4.c wpa-2.1/src/crypto/rc4.c --- wpa-1.0/src/crypto/rc4.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/crypto/rc4.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * RC4 stream cipher * Copyright (c) 2002-2005, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff -Nru wpa-1.0/src/crypto/sha1.c wpa-2.1/src/crypto/sha1.c --- wpa-1.0/src/crypto/sha1.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/crypto/sha1.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * SHA1 hash implementation and interface functions * Copyright (c) 2003-2005, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -108,56 +102,3 @@ { return hmac_sha1_vector(key, key_len, 1, &data, &data_len, mac); } - - -/** - * sha1_prf - SHA1-based Pseudo-Random Function (PRF) (IEEE 802.11i, 8.5.1.1) - * @key: Key for PRF - * @key_len: Length of the key in bytes - * @label: A unique label for each purpose of the PRF - * @data: Extra data to bind into the key - * @data_len: Length of the data - * @buf: Buffer for the generated pseudo-random key - * @buf_len: Number of bytes of key to generate - * Returns: 0 on success, -1 of failure - * - * This function is used to derive new, cryptographically separate keys from a - * given key (e.g., PMK in IEEE 802.11i). - */ -int sha1_prf(const u8 *key, size_t key_len, const char *label, - const u8 *data, size_t data_len, u8 *buf, size_t buf_len) -{ - u8 counter = 0; - size_t pos, plen; - u8 hash[SHA1_MAC_LEN]; - size_t label_len = os_strlen(label) + 1; - const unsigned char *addr[3]; - size_t len[3]; - - addr[0] = (u8 *) label; - len[0] = label_len; - addr[1] = data; - len[1] = data_len; - addr[2] = &counter; - len[2] = 1; - - pos = 0; - while (pos < buf_len) { - plen = buf_len - pos; - if (plen >= SHA1_MAC_LEN) { - if (hmac_sha1_vector(key, key_len, 3, addr, len, - &buf[pos])) - return -1; - pos += SHA1_MAC_LEN; - } else { - if (hmac_sha1_vector(key, key_len, 3, addr, len, - hash)) - return -1; - os_memcpy(&buf[pos], hash, plen); - break; - } - counter++; - } - - return 0; -} diff -Nru wpa-1.0/src/crypto/sha1.h wpa-2.1/src/crypto/sha1.h --- wpa-1.0/src/crypto/sha1.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/crypto/sha1.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * SHA1 hash implementation and interface functions * Copyright (c) 2003-2009, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef SHA1_H @@ -25,9 +19,9 @@ const u8 *data, size_t data_len, u8 *buf, size_t buf_len); int sha1_t_prf(const u8 *key, size_t key_len, const char *label, const u8 *seed, size_t seed_len, u8 *buf, size_t buf_len); -int __must_check tls_prf(const u8 *secret, size_t secret_len, - const char *label, const u8 *seed, size_t seed_len, - u8 *out, size_t outlen); -int pbkdf2_sha1(const char *passphrase, const char *ssid, size_t ssid_len, +int __must_check tls_prf_sha1_md5(const u8 *secret, size_t secret_len, + const char *label, const u8 *seed, + size_t seed_len, u8 *out, size_t outlen); +int pbkdf2_sha1(const char *passphrase, const u8 *ssid, size_t ssid_len, int iterations, u8 *buf, size_t buflen); #endif /* SHA1_H */ diff -Nru wpa-1.0/src/crypto/sha1_i.h wpa-2.1/src/crypto/sha1_i.h --- wpa-1.0/src/crypto/sha1_i.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/crypto/sha1_i.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * SHA1 internal definitions * Copyright (c) 2003-2005, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef SHA1_I_H diff -Nru wpa-1.0/src/crypto/sha1-internal.c wpa-2.1/src/crypto/sha1-internal.c --- wpa-1.0/src/crypto/sha1-internal.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/crypto/sha1-internal.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * SHA1 hash implementation and interface functions * Copyright (c) 2003-2005, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff -Nru wpa-1.0/src/crypto/sha1-pbkdf2.c wpa-2.1/src/crypto/sha1-pbkdf2.c --- wpa-1.0/src/crypto/sha1-pbkdf2.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/crypto/sha1-pbkdf2.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,24 +2,16 @@ * SHA1-based key derivation function (PBKDF2) for IEEE 802.11i * Copyright (c) 2003-2005, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" #include "common.h" #include "sha1.h" -#include "md5.h" -#include "crypto.h" -static int pbkdf2_sha1_f(const char *passphrase, const char *ssid, +static int pbkdf2_sha1_f(const char *passphrase, const u8 *ssid, size_t ssid_len, int iterations, unsigned int count, u8 *digest) { @@ -30,7 +22,7 @@ size_t len[2]; size_t passphrase_len = os_strlen(passphrase); - addr[0] = (u8 *) ssid; + addr[0] = ssid; len[0] = ssid_len; addr[1] = count_buf; len[1] = 4; @@ -77,7 +69,7 @@ * iterations is set to 4096 and buflen to 32. This function is described in * IEEE Std 802.11-2004, Clause H.4. The main construction is from PKCS#5 v2.0. */ -int pbkdf2_sha1(const char *passphrase, const char *ssid, size_t ssid_len, +int pbkdf2_sha1(const char *passphrase, const u8 *ssid, size_t ssid_len, int iterations, u8 *buf, size_t buflen) { unsigned int count = 0; diff -Nru wpa-1.0/src/crypto/sha1-prf.c wpa-2.1/src/crypto/sha1-prf.c --- wpa-1.0/src/crypto/sha1-prf.c 1970-01-01 00:00:00.000000000 +0000 +++ wpa-2.1/src/crypto/sha1-prf.c 2014-02-04 11:23:35.000000000 +0000 @@ -0,0 +1,66 @@ +/* + * SHA1-based PRF + * Copyright (c) 2003-2005, 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 "sha1.h" +#include "crypto.h" + + +/** + * sha1_prf - SHA1-based Pseudo-Random Function (PRF) (IEEE 802.11i, 8.5.1.1) + * @key: Key for PRF + * @key_len: Length of the key in bytes + * @label: A unique label for each purpose of the PRF + * @data: Extra data to bind into the key + * @data_len: Length of the data + * @buf: Buffer for the generated pseudo-random key + * @buf_len: Number of bytes of key to generate + * Returns: 0 on success, -1 of failure + * + * This function is used to derive new, cryptographically separate keys from a + * given key (e.g., PMK in IEEE 802.11i). + */ +int sha1_prf(const u8 *key, size_t key_len, const char *label, + const u8 *data, size_t data_len, u8 *buf, size_t buf_len) +{ + u8 counter = 0; + size_t pos, plen; + u8 hash[SHA1_MAC_LEN]; + size_t label_len = os_strlen(label) + 1; + const unsigned char *addr[3]; + size_t len[3]; + + addr[0] = (u8 *) label; + len[0] = label_len; + addr[1] = data; + len[1] = data_len; + addr[2] = &counter; + len[2] = 1; + + pos = 0; + while (pos < buf_len) { + plen = buf_len - pos; + if (plen >= SHA1_MAC_LEN) { + if (hmac_sha1_vector(key, key_len, 3, addr, len, + &buf[pos])) + return -1; + pos += SHA1_MAC_LEN; + } else { + if (hmac_sha1_vector(key, key_len, 3, addr, len, + hash)) + return -1; + os_memcpy(&buf[pos], hash, plen); + break; + } + counter++; + } + + return 0; +} diff -Nru wpa-1.0/src/crypto/sha1-tlsprf.c wpa-2.1/src/crypto/sha1-tlsprf.c --- wpa-1.0/src/crypto/sha1-tlsprf.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/crypto/sha1-tlsprf.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * TLS PRF (SHA1 + MD5) * Copyright (c) 2003-2005, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -17,11 +11,10 @@ #include "common.h" #include "sha1.h" #include "md5.h" -#include "crypto.h" /** - * tls_prf - Pseudo-Random Function for TLS (TLS-PRF, RFC 2246) + * tls_prf_sha1_md5 - Pseudo-Random Function for TLS (TLS-PRF, RFC 2246) * @secret: Key for PRF * @secret_len: Length of the key in bytes * @label: A unique label for each purpose of the PRF @@ -34,8 +27,8 @@ * This function is used to derive new, cryptographically separate keys from a * given key in TLS. This PRF is defined in RFC 2246, Chapter 5. */ -int tls_prf(const u8 *secret, size_t secret_len, const char *label, - const u8 *seed, size_t seed_len, u8 *out, size_t outlen) +int tls_prf_sha1_md5(const u8 *secret, size_t secret_len, const char *label, + const u8 *seed, size_t seed_len, u8 *out, size_t outlen) { size_t L_S1, L_S2, i; const u8 *S1, *S2; @@ -78,19 +71,16 @@ S2--; } - hmac_md5_vector_non_fips_allow(S1, L_S1, 2, &MD5_addr[1], &MD5_len[1], - A_MD5); + hmac_md5_vector(S1, L_S1, 2, &MD5_addr[1], &MD5_len[1], A_MD5); hmac_sha1_vector(S2, L_S2, 2, &SHA1_addr[1], &SHA1_len[1], A_SHA1); MD5_pos = MD5_MAC_LEN; SHA1_pos = SHA1_MAC_LEN; for (i = 0; i < outlen; i++) { if (MD5_pos == MD5_MAC_LEN) { - hmac_md5_vector_non_fips_allow(S1, L_S1, 3, MD5_addr, - MD5_len, P_MD5); + hmac_md5_vector(S1, L_S1, 3, MD5_addr, MD5_len, P_MD5); MD5_pos = 0; - hmac_md5_non_fips_allow(S1, L_S1, A_MD5, MD5_MAC_LEN, - A_MD5); + hmac_md5(S1, L_S1, A_MD5, MD5_MAC_LEN, A_MD5); } if (SHA1_pos == SHA1_MAC_LEN) { hmac_sha1_vector(S2, L_S2, 3, SHA1_addr, SHA1_len, diff -Nru wpa-1.0/src/crypto/sha1-tprf.c wpa-2.1/src/crypto/sha1-tprf.c --- wpa-1.0/src/crypto/sha1-tprf.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/crypto/sha1-tprf.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * SHA1 T-PRF for EAP-FAST * Copyright (c) 2003-2005, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff -Nru wpa-1.0/src/crypto/sha256.c wpa-2.1/src/crypto/sha256.c --- wpa-1.0/src/crypto/sha256.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/crypto/sha256.c 2014-02-04 11:23:35.000000000 +0000 @@ -1,15 +1,9 @@ /* * SHA-256 hash implementation and interface functions - * Copyright (c) 2003-2007, Jouni Malinen + * Copyright (c) 2003-2012, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -27,9 +21,10 @@ * @addr: Pointers to the data areas * @len: Lengths of the data blocks * @mac: Buffer for the hash (32 bytes) + * Returns: 0 on success, -1 on failure */ -void hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem, - const u8 *addr[], const size_t *len, u8 *mac) +int hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) { unsigned char k_pad[64]; /* padding - key XORd with ipad/opad */ unsigned char tk[32]; @@ -41,12 +36,13 @@ * Fixed limit on the number of fragments to avoid having to * allocate memory (which could fail). */ - return; + return -1; } /* if key is longer than 64 bytes reset it to key = SHA256(key) */ if (key_len > 64) { - sha256_vector(1, &key, &key_len, tk); + if (sha256_vector(1, &key, &key_len, tk) < 0) + return -1; key = tk; key_len = 32; } @@ -74,7 +70,8 @@ _addr[i + 1] = addr[i]; _len[i + 1] = len[i]; } - sha256_vector(1 + num_elem, _addr, _len, mac); + if (sha256_vector(1 + num_elem, _addr, _len, mac) < 0) + return -1; os_memset(k_pad, 0, sizeof(k_pad)); os_memcpy(k_pad, key, key_len); @@ -87,7 +84,7 @@ _len[0] = 64; _addr[1] = mac; _len[1] = SHA256_MAC_LEN; - sha256_vector(2, _addr, _len, mac); + return sha256_vector(2, _addr, _len, mac); } @@ -97,61 +94,11 @@ * @key_len: Length of the key in bytes * @data: Pointers to the data area * @data_len: Length of the data area - * @mac: Buffer for the hash (20 bytes) - */ -void hmac_sha256(const u8 *key, size_t key_len, const u8 *data, - size_t data_len, u8 *mac) -{ - hmac_sha256_vector(key, key_len, 1, &data, &data_len, mac); -} - - -/** - * sha256_prf - SHA256-based Pseudo-Random Function (IEEE 802.11r, 8.5.1.5.2) - * @key: Key for PRF - * @key_len: Length of the key in bytes - * @label: A unique label for each purpose of the PRF - * @data: Extra data to bind into the key - * @data_len: Length of the data - * @buf: Buffer for the generated pseudo-random key - * @buf_len: Number of bytes of key to generate - * - * This function is used to derive new, cryptographically separate keys from a - * given key. + * @mac: Buffer for the hash (32 bytes) + * Returns: 0 on success, -1 on failure */ -void sha256_prf(const u8 *key, size_t key_len, const char *label, - const u8 *data, size_t data_len, u8 *buf, size_t buf_len) +int hmac_sha256(const u8 *key, size_t key_len, const u8 *data, + size_t data_len, u8 *mac) { - u16 counter = 1; - size_t pos, plen; - u8 hash[SHA256_MAC_LEN]; - const u8 *addr[4]; - size_t len[4]; - u8 counter_le[2], length_le[2]; - - addr[0] = counter_le; - len[0] = 2; - addr[1] = (u8 *) label; - len[1] = os_strlen(label); - addr[2] = data; - len[2] = data_len; - addr[3] = length_le; - len[3] = sizeof(length_le); - - WPA_PUT_LE16(length_le, buf_len * 8); - pos = 0; - while (pos < buf_len) { - plen = buf_len - pos; - WPA_PUT_LE16(counter_le, counter); - if (plen >= SHA256_MAC_LEN) { - hmac_sha256_vector(key, key_len, 4, addr, len, - &buf[pos]); - pos += SHA256_MAC_LEN; - } else { - hmac_sha256_vector(key, key_len, 4, addr, len, hash); - os_memcpy(&buf[pos], hash, plen); - break; - } - counter++; - } + return hmac_sha256_vector(key, key_len, 1, &data, &data_len, mac); } diff -Nru wpa-1.0/src/crypto/sha256.h wpa-2.1/src/crypto/sha256.h --- wpa-1.0/src/crypto/sha256.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/crypto/sha256.h 2014-02-04 11:23:35.000000000 +0000 @@ -1,15 +1,9 @@ /* * SHA256 hash implementation and interface functions - * Copyright (c) 2003-2006, Jouni Malinen + * Copyright (c) 2003-2013, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef SHA256_H @@ -17,11 +11,17 @@ #define SHA256_MAC_LEN 32 -void hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem, - const u8 *addr[], const size_t *len, u8 *mac); -void hmac_sha256(const u8 *key, size_t key_len, const u8 *data, - size_t data_len, u8 *mac); +int hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac); +int hmac_sha256(const u8 *key, size_t key_len, const u8 *data, + size_t data_len, u8 *mac); void sha256_prf(const u8 *key, size_t key_len, const char *label, const u8 *data, size_t data_len, u8 *buf, size_t buf_len); +void sha256_prf_bits(const u8 *key, size_t key_len, const char *label, + const u8 *data, size_t data_len, u8 *buf, + size_t buf_len_bits); +void tls_prf_sha256(const u8 *secret, size_t secret_len, + const char *label, const u8 *seed, size_t seed_len, + u8 *out, size_t outlen); #endif /* SHA256_H */ diff -Nru wpa-1.0/src/crypto/sha256_i.h wpa-2.1/src/crypto/sha256_i.h --- wpa-1.0/src/crypto/sha256_i.h 1970-01-01 00:00:00.000000000 +0000 +++ wpa-2.1/src/crypto/sha256_i.h 2014-02-04 11:23:35.000000000 +0000 @@ -0,0 +1,25 @@ +/* + * SHA-256 internal definitions + * Copyright (c) 2003-2011, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef SHA256_I_H +#define SHA256_I_H + +#define SHA256_BLOCK_SIZE 64 + +struct sha256_state { + u64 length; + u32 state[8], curlen; + u8 buf[SHA256_BLOCK_SIZE]; +}; + +void sha256_init(struct sha256_state *md); +int sha256_process(struct sha256_state *md, const unsigned char *in, + unsigned long inlen); +int sha256_done(struct sha256_state *md, unsigned char *out); + +#endif /* SHA256_I_H */ diff -Nru wpa-1.0/src/crypto/sha256-internal.c wpa-2.1/src/crypto/sha256-internal.c --- wpa-1.0/src/crypto/sha256-internal.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/crypto/sha256-internal.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,35 +2,17 @@ * SHA-256 hash implementation and interface functions * Copyright (c) 2003-2011, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" #include "common.h" #include "sha256.h" +#include "sha256_i.h" #include "crypto.h" -#define SHA256_BLOCK_SIZE 64 - -struct sha256_state { - u64 length; - u32 state[8], curlen; - u8 buf[SHA256_BLOCK_SIZE]; -}; - -static void sha256_init(struct sha256_state *md); -static int sha256_process(struct sha256_state *md, const unsigned char *in, - unsigned long inlen); -static int sha256_done(struct sha256_state *md, unsigned char *out); - /** * sha256_vector - SHA256 hash for data vector @@ -139,7 +121,7 @@ /* Initialize the hash state */ -static void sha256_init(struct sha256_state *md) +void sha256_init(struct sha256_state *md) { md->curlen = 0; md->length = 0; @@ -160,8 +142,8 @@ @param inlen The length of the data (octets) @return CRYPT_OK if successful */ -static int sha256_process(struct sha256_state *md, const unsigned char *in, - unsigned long inlen) +int sha256_process(struct sha256_state *md, const unsigned char *in, + unsigned long inlen) { unsigned long n; @@ -200,7 +182,7 @@ @param out [out] The destination of the hash (32 bytes) @return CRYPT_OK if successful */ -static int sha256_done(struct sha256_state *md, unsigned char *out) +int sha256_done(struct sha256_state *md, unsigned char *out) { int i; diff -Nru wpa-1.0/src/crypto/sha256-prf.c wpa-2.1/src/crypto/sha256-prf.c --- wpa-1.0/src/crypto/sha256-prf.c 1970-01-01 00:00:00.000000000 +0000 +++ wpa-2.1/src/crypto/sha256-prf.c 2014-02-04 11:23:35.000000000 +0000 @@ -0,0 +1,98 @@ +/* + * SHA256-based PRF (IEEE 802.11r) + * Copyright (c) 2003-2013, 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 "sha256.h" +#include "crypto.h" + + +/** + * sha256_prf - SHA256-based Pseudo-Random Function (IEEE 802.11r, 8.5.1.5.2) + * @key: Key for PRF + * @key_len: Length of the key in bytes + * @label: A unique label for each purpose of the PRF + * @data: Extra data to bind into the key + * @data_len: Length of the data + * @buf: Buffer for the generated pseudo-random key + * @buf_len: Number of bytes of key to generate + * + * This function is used to derive new, cryptographically separate keys from a + * given key. + */ +void sha256_prf(const u8 *key, size_t key_len, const char *label, + const u8 *data, size_t data_len, u8 *buf, size_t buf_len) +{ + sha256_prf_bits(key, key_len, label, data, data_len, buf, buf_len * 8); +} + + +/** + * sha256_prf_bits - IEEE Std 802.11-2012, 11.6.1.7.2 Key derivation function + * @key: Key for KDF + * @key_len: Length of the key in bytes + * @label: A unique label for each purpose of the PRF + * @data: Extra data to bind into the key + * @data_len: Length of the data + * @buf: Buffer for the generated pseudo-random key + * @buf_len: Number of bits of key to generate + * + * This function is used to derive new, cryptographically separate keys from a + * given key. If the requested buf_len is not divisible by eight, the least + * significant 1-7 bits of the last octet in the output are not part of the + * requested output. + */ +void sha256_prf_bits(const u8 *key, size_t key_len, const char *label, + const u8 *data, size_t data_len, u8 *buf, + size_t buf_len_bits) +{ + u16 counter = 1; + size_t pos, plen; + u8 hash[SHA256_MAC_LEN]; + const u8 *addr[4]; + size_t len[4]; + u8 counter_le[2], length_le[2]; + size_t buf_len = (buf_len_bits + 7) / 8; + + addr[0] = counter_le; + len[0] = 2; + addr[1] = (u8 *) label; + len[1] = os_strlen(label); + addr[2] = data; + len[2] = data_len; + addr[3] = length_le; + len[3] = sizeof(length_le); + + WPA_PUT_LE16(length_le, buf_len_bits); + pos = 0; + while (pos < buf_len) { + plen = buf_len - pos; + WPA_PUT_LE16(counter_le, counter); + if (plen >= SHA256_MAC_LEN) { + hmac_sha256_vector(key, key_len, 4, addr, len, + &buf[pos]); + pos += SHA256_MAC_LEN; + } else { + hmac_sha256_vector(key, key_len, 4, addr, len, hash); + os_memcpy(&buf[pos], hash, plen); + pos += plen; + break; + } + counter++; + } + + /* + * Mask out unused bits in the last octet if it does not use all the + * bits. + */ + if (buf_len_bits % 8) { + u8 mask = 0xff << (8 - buf_len_bits % 8); + buf[pos - 1] &= mask; + } +} diff -Nru wpa-1.0/src/crypto/sha256-tlsprf.c wpa-2.1/src/crypto/sha256-tlsprf.c --- wpa-1.0/src/crypto/sha256-tlsprf.c 1970-01-01 00:00:00.000000000 +0000 +++ wpa-2.1/src/crypto/sha256-tlsprf.c 2014-02-04 11:23:35.000000000 +0000 @@ -0,0 +1,66 @@ +/* + * TLS PRF P_SHA256 + * Copyright (c) 2011, 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 "sha256.h" + + +/** + * tls_prf_sha256 - Pseudo-Random Function for TLS v1.2 (P_SHA256, RFC 5246) + * @secret: Key for PRF + * @secret_len: Length of the key in bytes + * @label: A unique label for each purpose of the PRF + * @seed: Seed value to bind into the key + * @seed_len: Length of the seed + * @out: Buffer for the generated pseudo-random key + * @outlen: Number of bytes of key to generate + * Returns: 0 on success, -1 on failure. + * + * This function is used to derive new, cryptographically separate keys from a + * given key in TLS. This PRF is defined in RFC 2246, Chapter 5. + */ +void tls_prf_sha256(const u8 *secret, size_t secret_len, const char *label, + const u8 *seed, size_t seed_len, u8 *out, size_t outlen) +{ + size_t clen; + u8 A[SHA256_MAC_LEN]; + u8 P[SHA256_MAC_LEN]; + size_t pos; + const unsigned char *addr[3]; + size_t len[3]; + + addr[0] = A; + len[0] = SHA256_MAC_LEN; + addr[1] = (unsigned char *) label; + len[1] = os_strlen(label); + addr[2] = seed; + len[2] = seed_len; + + /* + * RFC 5246, Chapter 5 + * A(0) = seed, A(i) = HMAC(secret, A(i-1)) + * P_hash = HMAC(secret, A(1) + seed) + HMAC(secret, A(2) + seed) + .. + * PRF(secret, label, seed) = P_SHA256(secret, label + seed) + */ + + hmac_sha256_vector(secret, secret_len, 2, &addr[1], &len[1], A); + + pos = 0; + while (pos < outlen) { + hmac_sha256_vector(secret, secret_len, 3, addr, len, P); + hmac_sha256(secret, secret_len, A, SHA256_MAC_LEN, A); + + clen = outlen - pos; + if (clen > SHA256_MAC_LEN) + clen = SHA256_MAC_LEN; + os_memcpy(out + pos, P, clen); + pos += clen; + } +} diff -Nru wpa-1.0/src/crypto/tls_gnutls.c wpa-2.1/src/crypto/tls_gnutls.c --- wpa-1.0/src/crypto/tls_gnutls.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/crypto/tls_gnutls.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * SSL/TLS interface functions for GnuTLS * Copyright (c) 2004-2011, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -131,8 +125,6 @@ } -extern int wpa_debug_show_keys; - void * tls_init(const struct tls_config *conf) { struct tls_global *global; diff -Nru wpa-1.0/src/crypto/tls.h wpa-2.1/src/crypto/tls.h --- wpa-1.0/src/crypto/tls.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/crypto/tls.h 2014-02-04 11:23:35.000000000 +0000 @@ -1,15 +1,9 @@ /* * SSL/TLS interface definition - * Copyright (c) 2004-2010, Jouni Malinen + * Copyright (c) 2004-2013, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef TLS_H @@ -27,8 +21,10 @@ }; enum tls_event { + TLS_CERT_CHAIN_SUCCESS, TLS_CERT_CHAIN_FAILURE, - TLS_PEER_CERTIFICATE + TLS_PEER_CERTIFICATE, + TLS_ALERT }; /* @@ -44,7 +40,9 @@ TLS_FAIL_SUBJECT_MISMATCH = 5, TLS_FAIL_ALTSUBJECT_MISMATCH = 6, TLS_FAIL_BAD_CERTIFICATE = 7, - TLS_FAIL_SERVER_CHAIN_PROBE = 8 + TLS_FAIL_SERVER_CHAIN_PROBE = 8, + TLS_FAIL_DOMAIN_SUFFIX_MISMATCH = 9, + TLS_FAIL_SERVER_USED_CLIENT_CERT = 10 }; union tls_event_data { @@ -63,6 +61,12 @@ const u8 *hash; size_t hash_len; } peer_cert; + + struct { + int is_local; + const char *type; + const char *description; + } alert; }; struct tls_config { @@ -79,6 +83,9 @@ #define TLS_CONN_ALLOW_SIGN_RSA_MD5 BIT(0) #define TLS_CONN_DISABLE_TIME_CHECKS BIT(1) +#define TLS_CONN_DISABLE_SESSION_TICKET BIT(2) +#define TLS_CONN_REQUEST_OCSP BIT(3) +#define TLS_CONN_REQUIRE_OCSP BIT(4) /** * struct tls_connection_params - Parameters for TLS connection @@ -91,6 +98,8 @@ * %NULL to allow all subjects * @altsubject_match: String to match in the alternative subject of the peer * certificate or %NULL to allow all alternative subjects + * @suffix_match: String to suffix match in the dNSName or CN of the peer + * certificate or %NULL to allow all domain names * @client_cert: File or reference name for client X.509 certificate in PEM or * DER format * @client_cert_blob: client_cert as inlined data or %NULL if not used @@ -114,6 +123,8 @@ * @cert_id: the certificate's id when using engine * @ca_cert_id: the CA certificate's id when using engine * @flags: Parameter options (TLS_CONN_*) + * @ocsp_stapling_response: DER encoded file with cached OCSP stapling response + * or %NULL if OCSP is not enabled * * TLS connection parameters to be configured with tls_connection_set_params() * and tls_global_set_params(). @@ -130,6 +141,7 @@ const char *ca_path; const char *subject_match; const char *altsubject_match; + const char *suffix_match; const char *client_cert; const u8 *client_cert_blob; size_t client_cert_blob_len; @@ -150,6 +162,7 @@ const char *ca_cert_id; unsigned int flags; + const char *ocsp_stapling_response; }; @@ -305,7 +318,7 @@ * not exported from the TLS library, tls_connection_prf() is required so that * further keying material can be derived from the master secret. If not * implemented, the function will still need to be defined, but it can just - * return -1. Example implementation of this function is in tls_prf() function + * return -1. Example implementation of this function is in tls_prf_sha1_md5() * when it is called with seed set to client_random|server_random (or * server_random|client_random). */ @@ -347,6 +360,12 @@ const struct wpabuf *in_data, struct wpabuf **appl_data); +struct wpabuf * tls_connection_handshake2(void *tls_ctx, + struct tls_connection *conn, + const struct wpabuf *in_data, + struct wpabuf **appl_data, + int *more_data_needed); + /** * tls_connection_server_handshake - Process TLS handshake (server side) * @tls_ctx: TLS context data from tls_init() @@ -392,6 +411,11 @@ struct tls_connection *conn, const struct wpabuf *in_data); +struct wpabuf * tls_connection_decrypt2(void *tls_ctx, + struct tls_connection *conn, + const struct wpabuf *in_data, + int *more_data_needed); + /** * tls_connection_resumed - Was session resumption used * @tls_ctx: TLS context data from tls_init() diff -Nru wpa-1.0/src/crypto/tls_internal.c wpa-2.1/src/crypto/tls_internal.c --- wpa-1.0/src/crypto/tls_internal.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/crypto/tls_internal.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * TLS interface functions and an internal TLS implementation * Copyright (c) 2004-2011, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. * * This file interface functions for hostapd/wpa_supplicant to use the * integrated TLSv1 implementation. @@ -332,6 +326,17 @@ const struct wpabuf *in_data, struct wpabuf **appl_data) { + return tls_connection_handshake2(tls_ctx, conn, in_data, appl_data, + NULL); +} + + +struct wpabuf * tls_connection_handshake2(void *tls_ctx, + struct tls_connection *conn, + const struct wpabuf *in_data, + struct wpabuf **appl_data, + int *need_more_data) +{ #ifdef CONFIG_TLS_INTERNAL_CLIENT u8 *res, *ad; size_t res_len, ad_len; @@ -344,7 +349,7 @@ res = tlsv1_client_handshake(conn->client, in_data ? wpabuf_head(in_data) : NULL, in_data ? wpabuf_len(in_data) : 0, - &res_len, &ad, &ad_len); + &res_len, &ad, &ad_len, need_more_data); if (res == NULL) return NULL; out = wpabuf_alloc_ext_data(res, res_len); @@ -455,23 +460,23 @@ struct tls_connection *conn, const struct wpabuf *in_data) { + return tls_connection_decrypt2(tls_ctx, conn, in_data, NULL); +} + + +struct wpabuf * tls_connection_decrypt2(void *tls_ctx, + struct tls_connection *conn, + const struct wpabuf *in_data, + int *need_more_data) +{ + if (need_more_data) + *need_more_data = 0; + #ifdef CONFIG_TLS_INTERNAL_CLIENT if (conn->client) { - struct wpabuf *buf; - int res; - buf = wpabuf_alloc((wpabuf_len(in_data) + 500) * 3); - if (buf == NULL) - return NULL; - res = tlsv1_client_decrypt(conn->client, wpabuf_head(in_data), - wpabuf_len(in_data), - wpabuf_mhead(buf), - wpabuf_size(buf)); - if (res < 0) { - wpabuf_free(buf); - return NULL; - } - wpabuf_put(buf, res); - return buf; + return tlsv1_client_decrypt(conn->client, wpabuf_head(in_data), + wpabuf_len(in_data), + need_more_data); } #endif /* CONFIG_TLS_INTERNAL_CLIENT */ #ifdef CONFIG_TLS_INTERNAL_SERVER diff -Nru wpa-1.0/src/crypto/tls_none.c wpa-2.1/src/crypto/tls_none.c --- wpa-1.0/src/crypto/tls_none.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/crypto/tls_none.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * SSL/TLS interface functions for no TLS case * Copyright (c) 2004-2009, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff -Nru wpa-1.0/src/crypto/tls_nss.c wpa-2.1/src/crypto/tls_nss.c --- wpa-1.0/src/crypto/tls_nss.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/crypto/tls_nss.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * SSL/TLS interface functions for NSS * Copyright (c) 2009, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff -Nru wpa-1.0/src/crypto/tls_openssl.c wpa-2.1/src/crypto/tls_openssl.c --- wpa-1.0/src/crypto/tls_openssl.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/crypto/tls_openssl.c 2014-02-04 11:23:35.000000000 +0000 @@ -1,24 +1,20 @@ /* * SSL/TLS interface functions for OpenSSL - * Copyright (c) 2004-2011, Jouni Malinen + * Copyright (c) 2004-2013, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" #ifndef CONFIG_SMARTCARD #ifndef OPENSSL_NO_ENGINE +#ifndef ANDROID #define OPENSSL_NO_ENGINE #endif #endif +#endif #include #include @@ -28,11 +24,6 @@ #include #endif /* OPENSSL_NO_ENGINE */ -#ifdef ANDROID -#include -#include "keystore_get.h" -#endif /* ANDROID */ - #include "common.h" #include "crypto.h" #include "tls.h" @@ -43,6 +34,10 @@ #define OPENSSL_d2i_TYPE unsigned char ** #endif +#if defined(SSL_CTX_get_app_data) && defined(SSL_CTX_set_app_data) +#define OPENSSL_SUPPORTS_CTX_APP_DATA +#endif + #ifdef SSL_F_SSL_SET_SESSION_TICKET_EXT #ifdef SSL_OP_NO_TICKET /* @@ -53,26 +48,51 @@ #endif #endif +#ifdef SSL_set_tlsext_status_type +#ifndef OPENSSL_NO_TLSEXT +#define HAVE_OCSP +#include +#endif /* OPENSSL_NO_TLSEXT */ +#endif /* SSL_set_tlsext_status_type */ + +#ifdef ANDROID +#include +#include + +static BIO * BIO_from_keystore(const char *key) +{ + BIO *bio = NULL; + uint8_t *value = NULL; + int length = keystore_get(key, strlen(key), &value); + if (length != -1 && (bio = BIO_new(BIO_s_mem())) != NULL) + BIO_write(bio, value, length); + free(value); + return bio; +} +#endif /* ANDROID */ + static int tls_openssl_ref_count = 0; -struct tls_global { +struct tls_context { void (*event_cb)(void *ctx, enum tls_event ev, union tls_event_data *data); void *cb_ctx; int cert_in_cb; + char *ocsp_stapling_response; }; -static struct tls_global *tls_global = NULL; +static struct tls_context *tls_global = NULL; struct tls_connection { + struct tls_context *context; SSL *ssl; BIO *ssl_in, *ssl_out; #ifndef OPENSSL_NO_ENGINE ENGINE *engine; /* functional reference to the engine */ EVP_PKEY *private_key; /* the private key if using engine */ #endif /* OPENSSL_NO_ENGINE */ - char *subject_match, *altsubject_match; + char *subject_match, *altsubject_match, *suffix_match; int read_alerts, write_alerts, failed; tls_session_ticket_cb session_ticket_cb; @@ -85,13 +105,32 @@ unsigned int ca_cert_verify:1; unsigned int cert_probe:1; unsigned int server_cert_only:1; + unsigned int server:1; u8 srv_cert_hash[32]; unsigned int flags; + + X509 *peer_cert; + X509 *peer_issuer; + X509 *peer_issuer_issuer; }; +static struct tls_context * tls_context_new(const struct tls_config *conf) +{ + struct tls_context *context = os_zalloc(sizeof(*context)); + if (context == NULL) + return NULL; + if (conf) { + context->event_cb = conf->event_cb; + context->cb_ctx = conf->cb_ctx; + context->cert_in_cb = conf->cert_in_cb; + } + return context; +} + + #ifdef CONFIG_NO_STDOUT_DEBUG static void _tls_show_errors(void) @@ -517,6 +556,7 @@ wpa_printf(MSG_DEBUG, "SSL: %s:%s", str, SSL_state_string_long(ssl)); } else if (where & SSL_CB_ALERT) { + struct tls_connection *conn = SSL_get_app_data((SSL *) ssl); wpa_printf(MSG_INFO, "SSL: SSL3 alert: %s:%s:%s", where & SSL_CB_READ ? "read (remote end reported an error)" : @@ -524,13 +564,20 @@ SSL_alert_type_string_long(ret), SSL_alert_desc_string_long(ret)); if ((ret >> 8) == SSL3_AL_FATAL) { - struct tls_connection *conn = - SSL_get_app_data((SSL *) ssl); if (where & SSL_CB_READ) conn->read_alerts++; else conn->write_alerts++; } + if (conn->context->event_cb != NULL) { + union tls_event_data ev; + struct tls_context *context = conn->context; + os_memset(&ev, 0, sizeof(ev)); + ev.alert.is_local = !(where & SSL_CB_READ); + ev.alert.type = SSL_alert_type_string_long(ret); + ev.alert.description = SSL_alert_desc_string_long(ret); + context->event_cb(context->cb_ctx, TLS_ALERT, &ev); + } } else if (where & SSL_CB_EXIT && ret <= 0) { wpa_printf(MSG_DEBUG, "SSL: %s:%s in %s", str, ret == 0 ? "failed" : "error", @@ -687,17 +734,12 @@ void * tls_init(const struct tls_config *conf) { SSL_CTX *ssl; + struct tls_context *context; if (tls_openssl_ref_count == 0) { - tls_global = os_zalloc(sizeof(*tls_global)); - if (tls_global == NULL) + tls_global = context = tls_context_new(conf); + if (context == NULL) return NULL; - if (conf) { - tls_global->event_cb = conf->event_cb; - tls_global->cb_ctx = conf->cb_ctx; - tls_global->cert_in_cb = conf->cert_in_cb; - } - #ifdef CONFIG_FIPS #ifdef OPENSSL_FIPS if (conf && conf->fips_mode) { @@ -706,6 +748,8 @@ "mode"); ERR_load_crypto_strings(); ERR_print_errors_fp(stderr); + os_free(tls_global); + tls_global = NULL; return NULL; } else wpa_printf(MSG_INFO, "Running in FIPS mode"); @@ -714,6 +758,8 @@ if (conf && conf->fips_mode) { wpa_printf(MSG_ERROR, "FIPS mode requested, but not " "supported"); + os_free(tls_global); + tls_global = NULL; return NULL; } #endif /* OPENSSL_FIPS */ @@ -739,14 +785,35 @@ #endif /* OPENSSL_NO_RC2 */ PKCS12_PBE_add(); #endif /* PKCS12_FUNCS */ + } else { + context = tls_global; +#ifdef OPENSSL_SUPPORTS_CTX_APP_DATA + /* Newer OpenSSL can store app-data per-SSL */ + context = tls_context_new(conf); + if (context == NULL) + return NULL; +#endif /* OPENSSL_SUPPORTS_CTX_APP_DATA */ } tls_openssl_ref_count++; ssl = SSL_CTX_new(TLSv1_method()); - if (ssl == NULL) + if (ssl == NULL) { + tls_openssl_ref_count--; +#ifdef OPENSSL_SUPPORTS_CTX_APP_DATA + if (context != tls_global) + os_free(context); +#endif /* OPENSSL_SUPPORTS_CTX_APP_DATA */ + if (tls_openssl_ref_count == 0) { + os_free(tls_global); + tls_global = NULL; + } return NULL; + } SSL_CTX_set_info_callback(ssl, ssl_info_cb); +#ifdef OPENSSL_SUPPORTS_CTX_APP_DATA + SSL_CTX_set_app_data(ssl, context); +#endif /* OPENSSL_SUPPORTS_CTX_APP_DATA */ #ifndef OPENSSL_NO_ENGINE if (conf && @@ -772,6 +839,11 @@ void tls_deinit(void *ssl_ctx) { SSL_CTX *ssl = ssl_ctx; +#ifdef OPENSSL_SUPPORTS_CTX_APP_DATA + struct tls_context *context = SSL_CTX_get_app_data(ssl); + if (context != tls_global) + os_free(context); +#endif /* OPENSSL_SUPPORTS_CTX_APP_DATA */ SSL_CTX_free(ssl); tls_openssl_ref_count--; @@ -783,6 +855,8 @@ ERR_remove_state(0); ERR_free_strings(); EVP_cleanup(); + os_free(tls_global->ocsp_stapling_response); + tls_global->ocsp_stapling_response = NULL; os_free(tls_global); tls_global = NULL; } @@ -799,16 +873,21 @@ wpa_printf(MSG_ERROR, "ENGINE: Engine ID not set"); return -1; } +#ifndef ANDROID if (pin == NULL) { wpa_printf(MSG_ERROR, "ENGINE: Smartcard PIN not set"); return -1; } +#endif if (key_id == NULL) { wpa_printf(MSG_ERROR, "ENGINE: Key Id not set"); return -1; } ERR_clear_error(); +#ifdef ANDROID + ENGINE_load_dynamic(); +#endif conn->engine = ENGINE_by_id(engine_id); if (!conn->engine) { wpa_printf(MSG_ERROR, "ENGINE: engine %s not available [%s]", @@ -823,11 +902,13 @@ } wpa_printf(MSG_DEBUG, "ENGINE: engine initialized"); +#ifndef ANDROID if (ENGINE_ctrl_cmd_string(conn->engine, "PIN", pin, 0) == 0) { wpa_printf(MSG_ERROR, "ENGINE: cannot set pin [%s]", ERR_error_string(ERR_get_error(), NULL)); goto err; } +#endif /* load private key first in-case PIN is required for cert */ conn->private_key = ENGINE_load_private_key(conn->engine, key_id, NULL, NULL); @@ -908,6 +989,10 @@ SSL_CTX *ssl = ssl_ctx; struct tls_connection *conn; long options; + struct tls_context *context = tls_global; +#ifdef OPENSSL_SUPPORTS_CTX_APP_DATA + context = SSL_CTX_get_app_data(ssl); +#endif /* OPENSSL_SUPPORTS_CTX_APP_DATA */ conn = os_zalloc(sizeof(*conn)); if (conn == NULL) @@ -920,6 +1005,7 @@ return NULL; } + conn->context = context; SSL_set_app_data(conn->ssl, conn); options = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_SINGLE_DH_USE; @@ -961,6 +1047,7 @@ tls_engine_deinit(conn); os_free(conn->subject_match); os_free(conn->altsubject_match); + os_free(conn->suffix_match); os_free(conn->session_ticket); os_free(conn); } @@ -1051,6 +1138,104 @@ } +#ifndef CONFIG_NATIVE_WINDOWS +static int domain_suffix_match(const u8 *val, size_t len, const char *match) +{ + size_t i, match_len; + + /* Check for embedded nuls that could mess up suffix matching */ + for (i = 0; i < len; i++) { + if (val[i] == '\0') { + wpa_printf(MSG_DEBUG, "TLS: Embedded null in a string - reject"); + return 0; + } + } + + match_len = os_strlen(match); + if (match_len > len) + return 0; + + if (os_strncasecmp((const char *) val + len - match_len, match, + match_len) != 0) + return 0; /* no match */ + + if (match_len == len) + return 1; /* exact match */ + + if (val[len - match_len - 1] == '.') + return 1; /* full label match completes suffix match */ + + wpa_printf(MSG_DEBUG, "TLS: Reject due to incomplete label match"); + return 0; +} +#endif /* CONFIG_NATIVE_WINDOWS */ + + +static int tls_match_suffix(X509 *cert, const char *match) +{ +#ifdef CONFIG_NATIVE_WINDOWS + /* wincrypt.h has conflicting X509_NAME definition */ + return -1; +#else /* CONFIG_NATIVE_WINDOWS */ + GENERAL_NAME *gen; + void *ext; + int i; + int dns_name = 0; + X509_NAME *name; + + wpa_printf(MSG_DEBUG, "TLS: Match domain against suffix %s", match); + + ext = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL); + + for (i = 0; ext && i < sk_GENERAL_NAME_num(ext); i++) { + gen = sk_GENERAL_NAME_value(ext, i); + if (gen->type != GEN_DNS) + continue; + dns_name++; + wpa_hexdump_ascii(MSG_DEBUG, "TLS: Certificate dNSName", + gen->d.dNSName->data, + gen->d.dNSName->length); + if (domain_suffix_match(gen->d.dNSName->data, + gen->d.dNSName->length, match) == 1) { + wpa_printf(MSG_DEBUG, "TLS: Suffix match in dNSName found"); + return 1; + } + } + + if (dns_name) { + wpa_printf(MSG_DEBUG, "TLS: None of the dNSName(s) matched"); + return 0; + } + + name = X509_get_subject_name(cert); + i = -1; + for (;;) { + X509_NAME_ENTRY *e; + ASN1_STRING *cn; + + i = X509_NAME_get_index_by_NID(name, NID_commonName, i); + if (i == -1) + break; + e = X509_NAME_get_entry(name, i); + if (e == NULL) + continue; + cn = X509_NAME_ENTRY_get_data(e); + if (cn == NULL) + continue; + wpa_hexdump_ascii(MSG_DEBUG, "TLS: Certificate commonName", + cn->data, cn->length); + if (domain_suffix_match(cn->data, cn->length, match) == 1) { + wpa_printf(MSG_DEBUG, "TLS: Suffix match in commonName found"); + return 1; + } + } + + wpa_printf(MSG_DEBUG, "TLS: No CommonName suffix match found"); + return 0; +#endif /* CONFIG_NATIVE_WINDOWS */ +} + + static enum tls_fail_reason openssl_tls_fail_reason(int err) { switch (err) { @@ -1115,8 +1300,9 @@ { union tls_event_data ev; struct wpabuf *cert = NULL; + struct tls_context *context = conn->context; - if (tls_global->event_cb == NULL) + if (context->event_cb == NULL) return; cert = get_x509_cert(err_cert); @@ -1127,7 +1313,7 @@ ev.cert_fail.subject = subject; ev.cert_fail.reason_txt = err_str; ev.cert_fail.cert = cert; - tls_global->event_cb(tls_global->cb_ctx, TLS_CERT_CHAIN_FAILURE, &ev); + context->event_cb(context->cb_ctx, TLS_CERT_CHAIN_FAILURE, &ev); wpabuf_free(cert); } @@ -1138,15 +1324,16 @@ { struct wpabuf *cert = NULL; union tls_event_data ev; + struct tls_context *context = conn->context; #ifdef CONFIG_SHA256 u8 hash[32]; #endif /* CONFIG_SHA256 */ - if (tls_global->event_cb == NULL) + if (context->event_cb == NULL) return; os_memset(&ev, 0, sizeof(ev)); - if (conn->cert_probe || tls_global->cert_in_cb) { + if (conn->cert_probe || context->cert_in_cb) { cert = get_x509_cert(err_cert); ev.peer_cert.cert = cert; } @@ -1164,7 +1351,7 @@ #endif /* CONFIG_SHA256 */ ev.peer_cert.depth = depth; ev.peer_cert.subject = subject; - tls_global->event_cb(tls_global->cb_ctx, TLS_PEER_CERTIFICATE, &ev); + context->event_cb(context->cb_ctx, TLS_PEER_CERTIFICATE, &ev); wpabuf_free(cert); } @@ -1176,7 +1363,8 @@ int err, depth; SSL *ssl; struct tls_connection *conn; - char *match, *altmatch; + struct tls_context *context; + char *match, *altmatch, *suffix_match; const char *err_str; err_cert = X509_STORE_CTX_get_current_cert(x509_ctx); @@ -1189,8 +1377,18 @@ conn = SSL_get_app_data(ssl); if (conn == NULL) return 0; + + if (depth == 0) + conn->peer_cert = err_cert; + else if (depth == 1) + conn->peer_issuer = err_cert; + else if (depth == 2) + conn->peer_issuer_issuer = err_cert; + + context = conn->context; match = conn->subject_match; altmatch = conn->altsubject_match; + suffix_match = conn->suffix_match; if (!preverify_ok && !conn->ca_cert_verify) preverify_ok = 1; @@ -1259,6 +1457,14 @@ openssl_tls_fail_event(conn, err_cert, err, depth, buf, "AltSubject mismatch", TLS_FAIL_ALTSUBJECT_MISMATCH); + } else if (depth == 0 && suffix_match && + !tls_match_suffix(err_cert, suffix_match)) { + wpa_printf(MSG_WARNING, "TLS: Domain suffix match '%s' not found", + suffix_match); + preverify_ok = 0; + openssl_tls_fail_event(conn, err_cert, err, depth, buf, + "Domain suffix mismatch", + TLS_FAIL_DOMAIN_SUFFIX_MISMATCH); } else openssl_tls_cert_event(conn, err_cert, depth, buf); @@ -1271,6 +1477,20 @@ TLS_FAIL_SERVER_CHAIN_PROBE); } + if (!conn->server && err_cert && preverify_ok && depth == 0 && + (err_cert->ex_flags & EXFLAG_XKUSAGE) && + (err_cert->ex_xkusage & XKU_SSL_CLIENT)) { + wpa_printf(MSG_WARNING, "TLS: Server used client certificate"); + openssl_tls_fail_event(conn, err_cert, err, depth, buf, + "Server used client certificate", + TLS_FAIL_SERVER_USED_CLIENT_CERT); + preverify_ok = 0; + } + + if (preverify_ok && context->event_cb != NULL) + context->event_cb(context->cb_ctx, + TLS_CERT_CHAIN_SUCCESS, NULL); + return preverify_ok; } @@ -1308,19 +1528,6 @@ #endif /* OPENSSL_NO_STDIO */ -#ifdef ANDROID -static BIO * BIO_from_keystore(const char *key) -{ - BIO *bio = NULL; - char value[KEYSTORE_MESSAGE_SIZE]; - int length = keystore_get(key, strlen(key), value); - if (length != -1 && (bio = BIO_new(BIO_s_mem())) != NULL) - BIO_write(bio, value, length); - return bio; -} -#endif /* ANDROID */ - - static int tls_connection_ca_cert(void *_ssl_ctx, struct tls_connection *conn, const char *ca_cert, const u8 *ca_cert_blob, size_t ca_cert_blob_len, const char *ca_path) @@ -1530,7 +1737,8 @@ static int tls_connection_set_subject_match(struct tls_connection *conn, const char *subject_match, - const char *altsubject_match) + const char *altsubject_match, + const char *suffix_match) { os_free(conn->subject_match); conn->subject_match = NULL; @@ -1548,6 +1756,14 @@ return -1; } + os_free(conn->suffix_match); + conn->suffix_match = NULL; + if (suffix_match) { + conn->suffix_match = os_strdup(suffix_match); + if (conn->suffix_match == NULL) + return -1; + } + return 0; } @@ -1663,6 +1879,7 @@ if (SSL_CTX_use_certificate_file(ssl_ctx, client_cert, SSL_FILETYPE_ASN1) != 1 && + SSL_CTX_use_certificate_chain_file(ssl_ctx, client_cert) != 1 && SSL_CTX_use_certificate_file(ssl_ctx, client_cert, SSL_FILETYPE_PEM) != 1) { tls_show_errors(MSG_INFO, __func__, @@ -1914,6 +2131,8 @@ wpa_printf(MSG_DEBUG, "OpenSSL: %s - added CA certificate from engine " "to certificate store", __func__); SSL_set_verify(conn->ssl, SSL_VERIFY_PEER, tls_verify_cb); + conn->ca_cert_verify = 1; + return 0; #else /* OPENSSL_NO_ENGINE */ @@ -2008,26 +2227,6 @@ break; } -#ifdef ANDROID - if (!ok && private_key && - os_strncmp("keystore://", private_key, 11) == 0) { - BIO *bio = BIO_from_keystore(&private_key[11]); - EVP_PKEY *pkey = NULL; - if (bio) { - pkey = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL); - BIO_free(bio); - } - if (pkey) { - if (SSL_use_PrivateKey(conn->ssl, pkey) == 1) { - wpa_printf(MSG_DEBUG, "OpenSSL: Private key " - "from keystore"); - ok = 1; - } - EVP_PKEY_free(pkey); - } - } -#endif /* ANDROID */ - while (!ok && private_key) { #ifndef OPENSSL_NO_STDIO if (SSL_use_PrivateKey_file(conn->ssl, private_key, @@ -2077,7 +2276,7 @@ ERR_clear_error(); SSL_CTX_set_default_passwd_cb(ssl_ctx, NULL); os_free(passwd); - + if (!SSL_check_private_key(conn->ssl)) { tls_show_errors(MSG_INFO, __func__, "Private key failed " "verification"); @@ -2123,7 +2322,7 @@ os_free(passwd); ERR_clear_error(); SSL_CTX_set_default_passwd_cb(ssl_ctx, NULL); - + if (!SSL_CTX_check_private_key(ssl_ctx)) { tls_show_errors(MSG_INFO, __func__, "Private key failed verification"); @@ -2285,6 +2484,11 @@ int tls_connection_get_keys(void *ssl_ctx, struct tls_connection *conn, struct tls_keys *keys) { +#ifdef CONFIG_FIPS + wpa_printf(MSG_ERROR, "OpenSSL: TLS keys cannot be exported in FIPS " + "mode"); + return -1; +#else /* CONFIG_FIPS */ SSL *ssl; if (conn == NULL || keys == NULL) @@ -2302,6 +2506,7 @@ keys->server_random_len = SSL3_RANDOM_SIZE; return 0; +#endif /* CONFIG_FIPS */ } @@ -2309,6 +2514,19 @@ const char *label, int server_random_first, u8 *out, size_t out_len) { +#if OPENSSL_VERSION_NUMBER >= 0x10001000L + SSL *ssl; + if (conn == NULL) + return -1; + if (server_random_first) + return -1; + ssl = conn->ssl; + if (SSL_export_keying_material(ssl, out, out_len, label, + os_strlen(label), NULL, 0, 0) == 1) { + wpa_printf(MSG_DEBUG, "OpenSSL: Using internal PRF"); + return 0; + } +#endif return -1; } @@ -2320,6 +2538,8 @@ int res; struct wpabuf *out_data; + conn->server = !!server; + /* * Give TLS handshake data from the server (if available) to OpenSSL * for processing. @@ -2676,6 +2896,222 @@ } +#ifdef HAVE_OCSP + +static void ocsp_debug_print_resp(OCSP_RESPONSE *rsp) +{ +#ifndef CONFIG_NO_STDOUT_DEBUG + BIO *out; + size_t rlen; + char *txt; + int res; + + if (wpa_debug_level > MSG_DEBUG) + return; + + out = BIO_new(BIO_s_mem()); + if (!out) + return; + + OCSP_RESPONSE_print(out, rsp, 0); + rlen = BIO_ctrl_pending(out); + txt = os_malloc(rlen + 1); + if (!txt) { + BIO_free(out); + return; + } + + res = BIO_read(out, txt, rlen); + if (res > 0) { + txt[res] = '\0'; + wpa_printf(MSG_DEBUG, "OpenSSL: OCSP Response\n%s", txt); + } + os_free(txt); + BIO_free(out); +#endif /* CONFIG_NO_STDOUT_DEBUG */ +} + + +static int ocsp_resp_cb(SSL *s, void *arg) +{ + struct tls_connection *conn = arg; + const unsigned char *p; + int len, status, reason; + OCSP_RESPONSE *rsp; + OCSP_BASICRESP *basic; + OCSP_CERTID *id; + ASN1_GENERALIZEDTIME *produced_at, *this_update, *next_update; + X509_STORE *store; + STACK_OF(X509) *certs = NULL; + + len = SSL_get_tlsext_status_ocsp_resp(s, &p); + if (!p) { + wpa_printf(MSG_DEBUG, "OpenSSL: No OCSP response received"); + return (conn->flags & TLS_CONN_REQUIRE_OCSP) ? 0 : 1; + } + + wpa_hexdump(MSG_DEBUG, "OpenSSL: OCSP response", p, len); + + rsp = d2i_OCSP_RESPONSE(NULL, &p, len); + if (!rsp) { + wpa_printf(MSG_INFO, "OpenSSL: Failed to parse OCSP response"); + return 0; + } + + ocsp_debug_print_resp(rsp); + + status = OCSP_response_status(rsp); + if (status != OCSP_RESPONSE_STATUS_SUCCESSFUL) { + wpa_printf(MSG_INFO, "OpenSSL: OCSP responder error %d (%s)", + status, OCSP_response_status_str(status)); + return 0; + } + + basic = OCSP_response_get1_basic(rsp); + if (!basic) { + wpa_printf(MSG_INFO, "OpenSSL: Could not find BasicOCSPResponse"); + return 0; + } + + store = SSL_CTX_get_cert_store(s->ctx); + if (conn->peer_issuer) { + wpa_printf(MSG_DEBUG, "OpenSSL: Add issuer"); + X509_print_fp(stdout, conn->peer_issuer); + + if (X509_STORE_add_cert(store, conn->peer_issuer) != 1) { + tls_show_errors(MSG_INFO, __func__, + "OpenSSL: Could not add issuer to certificate store\n"); + } + certs = sk_X509_new_null(); + if (certs) { + X509 *cert; + cert = X509_dup(conn->peer_issuer); + if (cert && !sk_X509_push(certs, cert)) { + tls_show_errors( + MSG_INFO, __func__, + "OpenSSL: Could not add issuer to OCSP responder trust store\n"); + X509_free(cert); + sk_X509_free(certs); + certs = NULL; + } + if (conn->peer_issuer_issuer) { + cert = X509_dup(conn->peer_issuer_issuer); + if (cert && !sk_X509_push(certs, cert)) { + tls_show_errors( + MSG_INFO, __func__, + "OpenSSL: Could not add issuer to OCSP responder trust store\n"); + X509_free(cert); + } + } + } + } + + status = OCSP_basic_verify(basic, certs, store, OCSP_TRUSTOTHER); + sk_X509_pop_free(certs, X509_free); + if (status <= 0) { + tls_show_errors(MSG_INFO, __func__, + "OpenSSL: OCSP response failed verification"); + OCSP_BASICRESP_free(basic); + OCSP_RESPONSE_free(rsp); + return 0; + } + + wpa_printf(MSG_DEBUG, "OpenSSL: OCSP response verification succeeded"); + + if (!conn->peer_cert) { + wpa_printf(MSG_DEBUG, "OpenSSL: Peer certificate not available for OCSP status check"); + OCSP_BASICRESP_free(basic); + OCSP_RESPONSE_free(rsp); + return 0; + } + + if (!conn->peer_issuer) { + wpa_printf(MSG_DEBUG, "OpenSSL: Peer issuer certificate not available for OCSP status check"); + OCSP_BASICRESP_free(basic); + OCSP_RESPONSE_free(rsp); + return 0; + } + + id = OCSP_cert_to_id(NULL, conn->peer_cert, conn->peer_issuer); + if (!id) { + wpa_printf(MSG_DEBUG, "OpenSSL: Could not create OCSP certificate identifier"); + OCSP_BASICRESP_free(basic); + OCSP_RESPONSE_free(rsp); + return 0; + } + + if (!OCSP_resp_find_status(basic, id, &status, &reason, &produced_at, + &this_update, &next_update)) { + wpa_printf(MSG_INFO, "OpenSSL: Could not find current server certificate from OCSP response%s", + (conn->flags & TLS_CONN_REQUIRE_OCSP) ? "" : + " (OCSP not required)"); + OCSP_BASICRESP_free(basic); + OCSP_RESPONSE_free(rsp); + return (conn->flags & TLS_CONN_REQUIRE_OCSP) ? 0 : 1; + } + + if (!OCSP_check_validity(this_update, next_update, 5 * 60, -1)) { + tls_show_errors(MSG_INFO, __func__, + "OpenSSL: OCSP status times invalid"); + OCSP_BASICRESP_free(basic); + OCSP_RESPONSE_free(rsp); + return 0; + } + + OCSP_BASICRESP_free(basic); + OCSP_RESPONSE_free(rsp); + + wpa_printf(MSG_DEBUG, "OpenSSL: OCSP status for server certificate: %s", + OCSP_cert_status_str(status)); + + if (status == V_OCSP_CERTSTATUS_GOOD) + return 1; + if (status == V_OCSP_CERTSTATUS_REVOKED) + return 0; + if (conn->flags & TLS_CONN_REQUIRE_OCSP) { + wpa_printf(MSG_DEBUG, "OpenSSL: OCSP status unknown, but OCSP required"); + return 0; + } + wpa_printf(MSG_DEBUG, "OpenSSL: OCSP status unknown, but OCSP was not required, so allow connection to continue"); + return 1; +} + + +static int ocsp_status_cb(SSL *s, void *arg) +{ + char *tmp; + char *resp; + size_t len; + + if (tls_global->ocsp_stapling_response == NULL) { + wpa_printf(MSG_DEBUG, "OpenSSL: OCSP status callback - no response configured"); + return SSL_TLSEXT_ERR_OK; + } + + resp = os_readfile(tls_global->ocsp_stapling_response, &len); + if (resp == NULL) { + wpa_printf(MSG_DEBUG, "OpenSSL: OCSP status callback - could not read response file"); + /* TODO: Build OCSPResponse with responseStatus = internalError + */ + return SSL_TLSEXT_ERR_OK; + } + wpa_printf(MSG_DEBUG, "OpenSSL: OCSP status callback - send cached response"); + tmp = OPENSSL_malloc(len); + if (tmp == NULL) { + os_free(resp); + return SSL_TLSEXT_ERR_ALERT_FATAL; + } + + os_memcpy(tmp, resp, len); + os_free(resp); + SSL_set_tlsext_status_ocsp_resp(s, tmp, len); + + return SSL_TLSEXT_ERR_OK; +} + +#endif /* HAVE_OCSP */ + + int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, const struct tls_connection_params *params) { @@ -2700,7 +3136,8 @@ } if (tls_connection_set_subject_match(conn, params->subject_match, - params->altsubject_match)) + params->altsubject_match, + params->suffix_match)) return -1; if (params->engine && params->ca_cert_id) { @@ -2741,6 +3178,24 @@ return -1; } +#ifdef SSL_OP_NO_TICKET + if (params->flags & TLS_CONN_DISABLE_SESSION_TICKET) + SSL_set_options(conn->ssl, SSL_OP_NO_TICKET); +#ifdef SSL_clear_options + else + SSL_clear_options(conn->ssl, SSL_OP_NO_TICKET); +#endif /* SSL_clear_options */ +#endif /* SSL_OP_NO_TICKET */ + +#ifdef HAVE_OCSP + if (params->flags & TLS_CONN_REQUEST_OCSP) { + SSL_CTX *ssl_ctx = tls_ctx; + SSL_set_tlsext_status_type(conn->ssl, TLSEXT_STATUSTYPE_ocsp); + SSL_CTX_set_tlsext_status_cb(ssl_ctx, ocsp_resp_cb); + SSL_CTX_set_tlsext_status_arg(ssl_ctx, conn); + } +#endif /* HAVE_OCSP */ + conn->flags = params->flags; tls_get_errors(tls_ctx); @@ -2776,6 +3231,26 @@ return -1; } +#ifdef SSL_OP_NO_TICKET + if (params->flags & TLS_CONN_DISABLE_SESSION_TICKET) + SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_TICKET); +#ifdef SSL_CTX_clear_options + else + SSL_CTX_clear_options(ssl_ctx, SSL_OP_NO_TICKET); +#endif /* SSL_clear_options */ +#endif /* SSL_OP_NO_TICKET */ + +#ifdef HAVE_OCSP + SSL_CTX_set_tlsext_status_cb(ssl_ctx, ocsp_status_cb); + SSL_CTX_set_tlsext_status_arg(ssl_ctx, ssl_ctx); + os_free(tls_global->ocsp_stapling_response); + if (params->ocsp_stapling_response) + tls_global->ocsp_stapling_response = + os_strdup(params->ocsp_stapling_response); + else + tls_global->ocsp_stapling_response = NULL; +#endif /* HAVE_OCSP */ + return 0; } @@ -2785,6 +3260,7 @@ { const EVP_CIPHER *c; const EVP_MD *h; + int md_size; if (conn == NULL || conn->ssl == NULL || conn->ssl->enc_read_ctx == NULL || @@ -2798,9 +3274,20 @@ #else h = conn->ssl->read_hash; #endif + if (h) + md_size = EVP_MD_size(h); +#if OPENSSL_VERSION_NUMBER >= 0x10000000L + else if (conn->ssl->s3) + md_size = conn->ssl->s3->tmp.new_mac_secret_size; +#endif + else + return -1; + wpa_printf(MSG_DEBUG, "OpenSSL: keyblock size: key_len=%d MD_size=%d " + "IV_len=%d", EVP_CIPHER_key_length(c), md_size, + EVP_CIPHER_iv_length(c)); return 2 * (EVP_CIPHER_key_length(c) + - EVP_MD_size(h) + + md_size + EVP_CIPHER_iv_length(c)); } diff -Nru wpa-1.0/src/crypto/tls_schannel.c wpa-2.1/src/crypto/tls_schannel.c --- wpa-1.0/src/crypto/tls_schannel.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/crypto/tls_schannel.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * SSL/TLS interface functions for Microsoft Schannel * Copyright (c) 2005-2009, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ /* diff -Nru wpa-1.0/src/drivers/android_drv.h wpa-2.1/src/drivers/android_drv.h --- wpa-1.0/src/drivers/android_drv.h 1970-01-01 00:00:00.000000000 +0000 +++ wpa-2.1/src/drivers/android_drv.h 2014-02-04 11:23:35.000000000 +0000 @@ -0,0 +1,56 @@ +/* + * Android driver interface + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef ANDROID_DRV_H +#define ANDROID_DRV_H + +#define WPA_EVENT_DRIVER_STATE "CTRL-EVENT-DRIVER-STATE " + +#define MAX_SSID_LEN 32 + +#define MAX_DRV_CMD_SIZE 248 +#define DRV_NUMBER_SEQUENTIAL_ERRORS 4 + +#define WEXT_PNOSETUP_HEADER "PNOSETUP " +#define WEXT_PNOSETUP_HEADER_SIZE 9 +#define WEXT_PNO_TLV_PREFIX 'S' +#define WEXT_PNO_TLV_VERSION '1' +#define WEXT_PNO_TLV_SUBVERSION '2' +#define WEXT_PNO_TLV_RESERVED '0' +#define WEXT_PNO_VERSION_SIZE 4 +#define WEXT_PNO_AMOUNT 16 +#define WEXT_PNO_SSID_SECTION 'S' +/* SSID header size is SSID section type above + SSID length */ +#define WEXT_PNO_SSID_HEADER_SIZE 2 +#define WEXT_PNO_SCAN_INTERVAL_SECTION 'T' +#define WEXT_PNO_SCAN_INTERVAL_LENGTH 2 +#define WEXT_PNO_SCAN_INTERVAL 30 +/* Scan interval size is scan interval section type + scan interval length + * above */ +#define WEXT_PNO_SCAN_INTERVAL_SIZE (1 + WEXT_PNO_SCAN_INTERVAL_LENGTH) +#define WEXT_PNO_REPEAT_SECTION 'R' +#define WEXT_PNO_REPEAT_LENGTH 1 +#define WEXT_PNO_REPEAT 4 +/* Repeat section size is Repeat section type + Repeat value length above */ +#define WEXT_PNO_REPEAT_SIZE (1 + WEXT_PNO_REPEAT_LENGTH) +#define WEXT_PNO_MAX_REPEAT_SECTION 'M' +#define WEXT_PNO_MAX_REPEAT_LENGTH 1 +#define WEXT_PNO_MAX_REPEAT 3 +/* Max Repeat section size is Max Repeat section type + Max Repeat value length + * above */ +#define WEXT_PNO_MAX_REPEAT_SIZE (1 + WEXT_PNO_MAX_REPEAT_LENGTH) +/* This corresponds to the size of all sections expect SSIDs */ +#define WEXT_PNO_NONSSID_SECTIONS_SIZE \ +(WEXT_PNO_SCAN_INTERVAL_SIZE + WEXT_PNO_REPEAT_SIZE + WEXT_PNO_MAX_REPEAT_SIZE) +/* PNO Max command size is total of header, version, ssid and other sections + + * Null termination */ +#define WEXT_PNO_MAX_COMMAND_SIZE \ + (WEXT_PNOSETUP_HEADER_SIZE + WEXT_PNO_VERSION_SIZE \ + + WEXT_PNO_AMOUNT * (WEXT_PNO_SSID_HEADER_SIZE + MAX_SSID_LEN) \ + + WEXT_PNO_NONSSID_SECTIONS_SIZE + 1) + +#endif /* ANDROID_DRV_H */ diff -Nru wpa-1.0/src/drivers/Apple80211.h wpa-2.1/src/drivers/Apple80211.h --- wpa-1.0/src/drivers/Apple80211.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/drivers/Apple80211.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,156 +0,0 @@ -#ifndef APPLE80211_H -#define APPLE80211_H - -/* - * Apple80211 framework definitions - * This is an undocumented interface and the definitions here are based on - * information from MacStumbler (http://www.macstumbler.com/Apple80211.h) and - * whatever related information can be found with google and experiments ;-). - */ - -typedef struct __WirelessRef *WirelessRef; -typedef SInt32 WirelessError; -#define errWirelessNoError 0 - -typedef struct WirelessInfo { - UInt16 link_qual; - UInt16 comms_qual; - UInt16 signal; - UInt16 noise; - UInt16 port_stat; - UInt16 client_mode; - UInt16 res1; - UInt16 power; - UInt16 res2; - UInt8 bssID[6]; - UInt8 ssid[34]; -} WirelessInfo; - -typedef struct WirelessInfo2 { - /* TODO - these are probably not in correct order or complete */ - WirelessInfo info1; - UInt8 macAddress[6]; -} WirelessInfo2; - -typedef struct WirelessNetworkInfo { - UInt16 channel; - UInt16 noise; - UInt16 signal; - UInt8 bssid[6]; - UInt16 beacon_int; - UInt16 capability; - UInt16 ssid_len; - UInt8 ssid[32]; -} WirelessNetworkInfo; - -typedef int wirelessKeyType; /* TODO */ - -int WirelessIsAvailable(void); -WirelessError WirelessAttach(WirelessRef *ref, UInt32 res); -WirelessError WirelessDetach(WirelessRef ref); -WirelessError WirelessPrivate(WirelessRef ref, void *in_ptr, int in_bytes, - void *out_ptr, int out_bytes); -WirelessError WirelessSetEnabled(WirelessRef ref, UInt8 enabled); -WirelessError WirelessGetEnabled(WirelessRef ref, UInt8 *enabled); -WirelessError WirelessSetPower(WirelessRef ref, UInt8 power); -WirelessError WirelessGetPower(WirelessRef ref, UInt8 *power); -WirelessError WirelessGetInfo(WirelessRef ref, WirelessInfo *info); -WirelessError WirelessGetInfo2(WirelessRef ref, WirelessInfo2 *info); -WirelessError WirelessScan(WirelessRef ref, CFArrayRef *results, - UInt32 strip_dups); -WirelessError WirelessScanSplit(WirelessRef ref, CFArrayRef *ap_results, - CFArrayRef *ibss_results, UInt32 strip_dups); -WirelessError WirelessDirectedScan(WirelessRef ref, CFArrayRef *results, - UInt32 strip_dups, CFStringRef ssid); -WirelessError WirelessDirectedScan2(WirelessRef ref, CFDataRef ssid, - UInt32 strip_dups, CFArrayRef *results); -WirelessError WirelessJoin(WirelessRef ref, CFStringRef ssid); -WirelessError WirelessJoinWEP(WirelessRef ref, CFStringRef ssid, - CFStringRef passwd); -WirelessError WirelessJoin8021x(WirelessRef ref, CFStringRef ssid); -/* - * Set WEP key - * ref: wireless reference from WirelessAttach() - * type: ? - * key_idx: 0..3 - * key_len: 13 for WEP-104 or 0 for clearing the key - * key: Pointer to the key or %NULL if key_len = 0 - */ -WirelessError WirelessSetKey(WirelessRef ref, wirelessKeyType type, - int key_idx, int key_len, - const unsigned char *key); -/* - * Set WPA key (e.g., PMK for 4-way handshake) - * ref: wireless reference from WirelessAttach() - * type: 0..4; 1 = PMK - * key_len: 16, 32, or 0 - * key: Pointer to the key or %NULL if key_len = 0 - */ -WirelessError WirelessSetWPAKey(WirelessRef ref, wirelessKeyType type, - int key_len, const unsigned char *key); -WirelessError WirelessAssociate(WirelessRef ref, int type, CFDataRef ssid, - CFStringRef key); -WirelessError WirelessAssociate2(WirelessRef ref, CFDictionaryRef scan_res, - CFStringRef key); -WirelessError WirelessDisassociate(WirelessRef ref); - -/* - * Get a copy of scan results for the given SSID - * The returned dictionary includes following entries: - * beaconInterval: CFNumber(kCFNumberSInt32Type) - * SSID: CFData buffer of the SSID - * isWPA: CFNumber(kCFNumberSInt32Type); 0 = not used, 1 = WPA, -128 = WPA2 - * name: Name of the network (SSID string) - * BSSID: CFData buffer of the BSSID - * channel: CFNumber(kCFNumberSInt32Type) - * signal: CFNumber(kCFNumberSInt32Type) - * appleIE: CFData - * WPSNOPINRequired: CFBoolean - * noise: CFNumber(kCFNumberSInt32Type) - * capability: CFNumber(kCFNumberSInt32Type) - * uniCipher: CFArray of CFNumber(kCFNumberSInt32Type) - * appleIE_Version: CFNumber(kCFNumberSInt32Type) - * appleIE_Robust: CFBoolean - * WPSConfigured: CFBoolean - * scanWasDirected: CFBoolean - * appleIE_Product: CFNumber(kCFNumberSInt32Type) - * authModes: CFArray of CFNumber(kCFNumberSInt32Type) - * multiCipher: CFNumber(kCFNumberSInt32Type) - */ -CFDictionaryRef WirelessSafeDirectedScanCopy(WirelessRef ref, CFDataRef ssid); - -/* - * Get information about the current association - * The returned dictionary includes following entries: - * keyData: CFData buffer of the key (e.g., 32-octet PSK) - * multiCipher: CFNumber(kCFNumberSInt32Type); 0 = none, 5 = CCMP? - * channel: CFNumber(kCFNumberSInt32Type) - * isIBSS: CFBoolean - * authMode: CFNumber(kCFNumberSInt32Type); 2 = WPA-Personal; 3 = open, - * 129 = WPA2-Enterprise - * isWPA: CFNumber(kCFNumberSInt32Type); 0 = not used, 1 = WPA, -128 == WPA2 - * SSID: CFData buffer of the SSID - * cipherMode: CFNumber(kCFNumberSInt32Type); 0 = none, 4 = CCMP? - */ -CFDictionaryRef WirelessGetAssociationInfo(WirelessRef ref); - -WirelessError WirelessConfigure(WirelessRef ref); - -/* - * Get ASP information - * The returned dictionary includes following entries: - * Version: version number (e.g., 3.0) - * Channel: channel (e.g., 1) - * Vendor: vendor (e.g., 2) - */ -CFDictionaryRef WirelessGetInfoASP(void); - -/* - * Get a copy of the interface dictionary - * The returned dictionary has a key,value pairs for wireless interfaces. - * The key is the interface name and the value is the driver identifier, e.g., - * en1: com.apple.driver.AirPort.Atheros - */ -CFDictionaryRef WirelessCopyInterfaceDict(void); - -#endif /* APPLE80211_H */ diff -Nru wpa-1.0/src/drivers/driver_atheros.c wpa-2.1/src/drivers/driver_atheros.c --- wpa-1.0/src/drivers/driver_atheros.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/drivers/driver_atheros.c 2014-02-04 11:23:35.000000000 +0000 @@ -5,14 +5,8 @@ * Copyright (c) 2005-2007, Jouni Malinen * Copyright (c) 2009, Atheros Communications * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -20,6 +14,12 @@ #include #include "common.h" +#include "eloop.h" +#include "common/ieee802_11_defs.h" +#include "l2_packet/l2_packet.h" +#include "p2p/p2p.h" + +#include "common.h" #ifndef _BYTE_ORDER #ifdef WORDS_BIGENDIAN #define _BYTE_ORDER _BIG_ENDIAN @@ -39,13 +39,13 @@ #ifdef CONFIG_WPS #include +#endif /* CONFIG_WPS */ #ifndef ETH_P_80211_RAW #define ETH_P_80211_RAW 0x0019 #endif -#endif /* CONFIG_WPS */ -#include "wireless_copy.h" +#include "linux_wext.h" #include "driver.h" #include "eloop.h" @@ -73,6 +73,7 @@ struct wpabuf *wpa_ie; struct wpabuf *wps_beacon_ie; struct wpabuf *wps_probe_resp_ie; + u8 own_addr[ETH_ALEN]; }; static int atheros_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr, @@ -323,8 +324,7 @@ } #endif /* CONFIG_IEEE80211W */ - wpa_printf(MSG_DEBUG, "%s: rsn capabilities=0x%x", - __func__, params->rsn_preauth); + wpa_printf(MSG_DEBUG, "%s: rsn capabilities=0x%x", __func__, v); if (set80211param(drv, IEEE80211_PARAM_RSNCAPS, v)) { printf("Unable to set RSN capabilities to 0x%x\n", v); return -1; @@ -354,19 +354,16 @@ return atheros_set_privacy(drv, 0); } if (!params->wpa && !params->ieee802_1x) { - hostapd_logger(drv->hapd, NULL, HOSTAPD_MODULE_DRIVER, - HOSTAPD_LEVEL_WARNING, "No 802.1X or WPA enabled!"); + wpa_printf(MSG_WARNING, "No 802.1X or WPA enabled!"); return -1; } if (params->wpa && atheros_configure_wpa(drv, params) != 0) { - hostapd_logger(drv->hapd, NULL, HOSTAPD_MODULE_DRIVER, - HOSTAPD_LEVEL_WARNING, "Error configuring WPA state!"); + wpa_printf(MSG_WARNING, "Error configuring WPA state!"); return -1; } if (set80211param(priv, IEEE80211_PARAM_AUTHMODE, (params->wpa ? IEEE80211_AUTH_WPA : IEEE80211_AUTH_8021X))) { - hostapd_logger(drv->hapd, NULL, HOSTAPD_MODULE_DRIVER, - HOSTAPD_LEVEL_WARNING, "Error enabling WPA/802.1X!"); + wpa_printf(MSG_WARNING, "Error enabling WPA/802.1X!"); return -1; } @@ -732,8 +729,8 @@ } #ifdef CONFIG_WPS -static void atheros_raw_receive(void *ctx, const u8 *src_addr, const u8 *buf, - size_t len) +static void atheros_raw_recv_wps(void *ctx, const u8 *src_addr, const u8 *buf, + size_t len) { struct atheros_driver_data *drv = ctx; const struct ieee80211_mgmt *mgmt; @@ -762,28 +759,270 @@ } #endif /* CONFIG_WPS */ -static int atheros_receive_probe_req(struct atheros_driver_data *drv) +#ifdef CONFIG_IEEE80211R +static void atheros_raw_recv_11r(void *ctx, const u8 *src_addr, const u8 *buf, + size_t len) +{ + struct atheros_driver_data *drv = ctx; + union wpa_event_data event; + const struct ieee80211_mgmt *mgmt; + u16 fc; + u16 stype; + int ielen; + const u8 *iebuf; + + /* Do 11R processing for ASSOC/AUTH/FT ACTION frames */ + if (len < IEEE80211_HDRLEN) + return; + mgmt = (const struct ieee80211_mgmt *) buf; + + fc = le_to_host16(mgmt->frame_control); + + if (WLAN_FC_GET_TYPE(fc) != WLAN_FC_TYPE_MGMT) + return; + stype = WLAN_FC_GET_STYPE(fc); + + wpa_printf(MSG_DEBUG, "%s: subtype 0x%x len %d", __func__, stype, + (int) len); + + if (os_memcmp(drv->own_addr, mgmt->bssid, ETH_ALEN) != 0) { + wpa_printf(MSG_DEBUG, "%s: BSSID does not match - ignore", + __func__); + return; + } + switch (stype) { + case WLAN_FC_STYPE_ASSOC_REQ: + if (len - IEEE80211_HDRLEN < sizeof(mgmt->u.assoc_req)) + break; + ielen = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.assoc_req)); + iebuf = mgmt->u.assoc_req.variable; + drv_event_assoc(drv->hapd, mgmt->sa, iebuf, ielen, 0); + break; + case WLAN_FC_STYPE_REASSOC_REQ: + if (len - IEEE80211_HDRLEN < sizeof(mgmt->u.reassoc_req)) + break; + ielen = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.reassoc_req)); + iebuf = mgmt->u.reassoc_req.variable; + drv_event_assoc(drv->hapd, mgmt->sa, iebuf, ielen, 1); + break; + case WLAN_FC_STYPE_ACTION: + os_memset(&event, 0, sizeof(event)); + event.rx_mgmt.frame = buf; + event.rx_mgmt.frame_len = len; + wpa_supplicant_event(drv->hapd, EVENT_RX_MGMT, &event); + break; + case WLAN_FC_STYPE_AUTH: + if (len - IEEE80211_HDRLEN < sizeof(mgmt->u.auth)) + break; + os_memset(&event, 0, sizeof(event)); + os_memcpy(event.auth.peer, mgmt->sa, ETH_ALEN); + os_memcpy(event.auth.bssid, mgmt->bssid, ETH_ALEN); + event.auth.auth_type = le_to_host16(mgmt->u.auth.auth_alg); + event.auth.status_code = + le_to_host16(mgmt->u.auth.status_code); + event.auth.auth_transaction = + le_to_host16(mgmt->u.auth.auth_transaction); + event.auth.ies = mgmt->u.auth.variable; + event.auth.ies_len = len - IEEE80211_HDRLEN - + sizeof(mgmt->u.auth); + wpa_supplicant_event(drv->hapd, EVENT_AUTH, &event); + break; + default: + break; + } +} +#endif /* CONFIG_IEEE80211R */ + +#ifdef CONFIG_HS20 +static void atheros_raw_recv_hs20(void *ctx, const u8 *src_addr, const u8 *buf, + size_t len) +{ + struct atheros_driver_data *drv = ctx; + const struct ieee80211_mgmt *mgmt; + u16 fc; + union wpa_event_data event; + + /* Send the Action frame for HS20 processing */ + + if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.action.category) + + sizeof(mgmt->u.action.u.public_action)) + return; + + mgmt = (const struct ieee80211_mgmt *) buf; + + fc = le_to_host16(mgmt->frame_control); + if (WLAN_FC_GET_TYPE(fc) != WLAN_FC_TYPE_MGMT || + WLAN_FC_GET_STYPE(fc) != WLAN_FC_STYPE_ACTION || + mgmt->u.action.category != WLAN_ACTION_PUBLIC) + return; + + wpa_printf(MSG_DEBUG, "%s:Received Public Action frame", __func__); + + os_memset(&event, 0, sizeof(event)); + event.rx_mgmt.frame = (const u8 *) mgmt; + event.rx_mgmt.frame_len = len; + wpa_supplicant_event(drv->hapd, EVENT_RX_MGMT, &event); +} + +#endif /* CONFIG_HS20 */ + + +static int atheros_set_qos_map(void *ctx, const u8 *qos_map_set, + u8 qos_map_set_len) +{ +#ifdef CONFIG_ATHEROS_QOS_MAP + struct atheros_driver_data *drv = ctx; + struct ieee80211req_athdbg req; + struct ieee80211_qos_map *qos_map = &req.data.qos_map; + struct iwreq iwr; + int i, up_start; + + if (qos_map_set_len < 16 || qos_map_set_len > 58 || + qos_map_set_len & 1) { + wpa_printf(MSG_ERROR, "Invalid QoS Map"); + return -1; + } else { + memset(&req, 0, sizeof(struct ieee80211req_athdbg)); + req.cmd = IEEE80211_DBGREQ_SETQOSMAPCONF; + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->iface, sizeof(iwr.ifr_name)); + iwr.u.data.pointer = (void *) &req; + iwr.u.data.length = sizeof(struct ieee80211req_athdbg); + } + + qos_map->valid = 1; + qos_map->num_dscp_except = (qos_map_set_len - 16) / 2; + if (qos_map->num_dscp_except) { + for (i = 0; i < qos_map->num_dscp_except; i++) { + qos_map->dscp_exception[i].dscp = qos_map_set[i * 2]; + qos_map->dscp_exception[i].up = qos_map_set[i * 2 + 1]; + } + } + + up_start = qos_map_set_len - 16; + for (i = 0; i < IEEE80211_MAX_QOS_UP_RANGE; i++) { + qos_map->up[i].low = qos_map_set[up_start + (i * 2)]; + qos_map->up[i].high = qos_map_set[up_start + (i * 2) + 1]; + } + + if (ioctl(drv->ioctl_sock, IEEE80211_IOCTL_DBGREQ, &iwr) < 0) { + perror("ioctl[IEEE80211_IOCTL_DBGREQ]"); + wpa_printf(MSG_DEBUG, "%s: %s: Failed to set QoS Map", + __func__, drv->iface); + return -1; + } +#endif /* CONFIG_ATHEROS_QOS_MAP */ + + return 0; +} + +#if defined(CONFIG_WNM) && !defined(CONFIG_IEEE80211R) +static void atheros_raw_recv_11v(void *ctx, const u8 *src_addr, const u8 *buf, + size_t len) +{ + struct atheros_driver_data *drv = ctx; + union wpa_event_data event; + const struct ieee80211_mgmt *mgmt; + u16 fc; + u16 stype; + + /* Do 11R processing for WNM ACTION frames */ + if (len < IEEE80211_HDRLEN) + return; + mgmt = (const struct ieee80211_mgmt *) buf; + + fc = le_to_host16(mgmt->frame_control); + + if (WLAN_FC_GET_TYPE(fc) != WLAN_FC_TYPE_MGMT) + return; + stype = WLAN_FC_GET_STYPE(fc); + + wpa_printf(MSG_DEBUG, "%s: subtype 0x%x len %d", __func__, stype, + (int) len); + + if (os_memcmp(drv->own_addr, mgmt->bssid, ETH_ALEN) != 0) { + wpa_printf(MSG_DEBUG, "%s: BSSID does not match - ignore", + __func__); + return; + } + + switch (stype) { + case WLAN_FC_STYPE_ACTION: + os_memset(&event, 0, sizeof(event)); + event.rx_mgmt.frame = buf; + event.rx_mgmt.frame_len = len; + wpa_supplicant_event(drv->hapd, EVENT_RX_MGMT, &event); + break; + default: + break; + } +} +#endif /* CONFIG_WNM */ + +#if defined(CONFIG_WPS) || defined(CONFIG_IEEE80211R) || defined(CONFIG_WNM) +static void atheros_raw_receive(void *ctx, const u8 *src_addr, const u8 *buf, + size_t len) { - int ret = 0; #ifdef CONFIG_WPS + atheros_raw_recv_wps(ctx, src_addr, buf, len); +#endif /* CONFIG_WPS */ +#ifdef CONFIG_IEEE80211R + atheros_raw_recv_11r(ctx, src_addr, buf, len); +#endif /* CONFIG_IEEE80211R */ +#if defined(CONFIG_WNM) && !defined(CONFIG_IEEE80211R) + atheros_raw_recv_11v(ctx, src_addr, buf, len); +#endif /* CONFIG_WNM */ +#ifdef CONFIG_HS20 + atheros_raw_recv_hs20(ctx, src_addr, buf, len); +#endif /* CONFIG_HS20 */ +} +#endif /* CONFIG_WPS || CONFIG_IEEE80211R */ + +static int atheros_receive_pkt(struct atheros_driver_data *drv) +{ + int ret = 0; struct ieee80211req_set_filter filt; wpa_printf(MSG_DEBUG, "%s Enter", __func__); - filt.app_filterype = IEEE80211_FILTER_TYPE_PROBE_REQ; - - ret = set80211priv(drv, IEEE80211_IOCTL_FILTERFRAME, &filt, - sizeof(struct ieee80211req_set_filter)); - if (ret) - return ret; + filt.app_filterype = 0; +#ifdef CONFIG_WPS + filt.app_filterype |= IEEE80211_FILTER_TYPE_PROBE_REQ; +#endif /* CONFIG_WPS */ +#ifdef CONFIG_IEEE80211R + filt.app_filterype |= (IEEE80211_FILTER_TYPE_ASSOC_REQ | + IEEE80211_FILTER_TYPE_AUTH | + IEEE80211_FILTER_TYPE_ACTION); +#endif +#ifdef CONFIG_WNM + filt.app_filterype |= IEEE80211_FILTER_TYPE_ACTION; +#endif /* CONFIG_WNM */ +#ifdef CONFIG_HS20 + filt.app_filterype |= IEEE80211_FILTER_TYPE_ACTION; +#endif /* CONFIG_HS20 */ + if (filt.app_filterype) { + ret = set80211priv(drv, IEEE80211_IOCTL_FILTERFRAME, &filt, + sizeof(struct ieee80211req_set_filter)); + if (ret) + return ret; + } +#if defined(CONFIG_WPS) || defined(CONFIG_IEEE80211R) drv->sock_raw = l2_packet_init(drv->iface, NULL, ETH_P_80211_RAW, atheros_raw_receive, drv, 1); if (drv->sock_raw == NULL) return -1; -#endif /* CONFIG_WPS */ +#endif /* CONFIG_WPS || CONFIG_IEEE80211R */ return ret; } +static int atheros_reset_appfilter(struct atheros_driver_data *drv) +{ + struct ieee80211req_set_filter filt; + filt.app_filterype = 0; + return set80211priv(drv, IEEE80211_IOCTL_FILTERFRAME, &filt, + sizeof(struct ieee80211req_set_filter)); +} + #ifdef CONFIG_WPS static int atheros_set_wps_ie(void *priv, const u8 *ie, size_t len, u32 frametype) @@ -852,6 +1091,84 @@ #define atheros_set_ap_wps_ie NULL #endif /* CONFIG_WPS */ +#ifdef CONFIG_IEEE80211R +static int +atheros_sta_auth(void *priv, const u8 *own_addr, const u8 *addr, u16 seq, + u16 status_code, const u8 *ie, size_t len) +{ + struct atheros_driver_data *drv = priv; + struct ieee80211req_mlme mlme; + int ret; + + wpa_printf(MSG_DEBUG, "%s: addr=%s status_code=%d", + __func__, ether_sprintf(addr), status_code); + + mlme.im_op = IEEE80211_MLME_AUTH; + mlme.im_reason = status_code; + mlme.im_seq = seq; + os_memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN); + mlme.im_optie_len = len; + if (len) { + if (len < IEEE80211_MAX_OPT_IE) { + os_memcpy(mlme.im_optie, ie, len); + } else { + wpa_printf(MSG_DEBUG, "%s: Not enough space to copy " + "opt_ie STA (addr " MACSTR " reason %d, " + "ie_len %d)", + __func__, MAC2STR(addr), status_code, + (int) len); + return -1; + } + } + ret = set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, sizeof(mlme)); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "%s: Failed to auth STA (addr " MACSTR + " reason %d)", + __func__, MAC2STR(addr), status_code); + } + return ret; +} + +static int +atheros_sta_assoc(void *priv, const u8 *own_addr, const u8 *addr, + int reassoc, u16 status_code, const u8 *ie, size_t len) +{ + struct atheros_driver_data *drv = priv; + struct ieee80211req_mlme mlme; + int ret; + + wpa_printf(MSG_DEBUG, "%s: addr=%s status_code=%d reassoc %d", + __func__, ether_sprintf(addr), status_code, reassoc); + + if (reassoc) + mlme.im_op = IEEE80211_MLME_REASSOC; + else + mlme.im_op = IEEE80211_MLME_ASSOC; + mlme.im_reason = status_code; + os_memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN); + mlme.im_optie_len = len; + if (len) { + if (len < IEEE80211_MAX_OPT_IE) { + os_memcpy(mlme.im_optie, ie, len); + } else { + wpa_printf(MSG_DEBUG, "%s: Not enough space to copy " + "opt_ie STA (addr " MACSTR " reason %d, " + "ie_len %d)", + __func__, MAC2STR(addr), status_code, + (int) len); + return -1; + } + } + ret = set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, sizeof(mlme)); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "%s: Failed to assoc STA (addr " MACSTR + " reason %d)", + __func__, MAC2STR(addr), status_code); + } + return ret; +} +#endif /* CONFIG_IEEE80211R */ + static void atheros_new_sta(struct atheros_driver_data *drv, u8 addr[IEEE80211_ADDR_LEN]) { @@ -980,6 +1297,9 @@ * so all are enabled for WPS... ugh. */ wpa_supplicant_event(drv->hapd, EVENT_WPS_BUTTON_PUSHED, NULL); +#endif /* CONFIG_WPS */ +#if defined(CONFIG_WPS) || defined(CONFIG_IEEE80211R) || defined(CONFIG_HS20) +#define MGMT_FRAM_TAG_SIZE 30 /* hardcoded in driver */ } else if (strncmp(custom, "Manage.prob_req ", 16) == 0) { /* * Atheros driver uses a hack to pass Probe Request frames as a @@ -987,16 +1307,134 @@ * packet sniffing) didn't work when bridging. * Format: "Manage.prob_req " | zero padding | frame */ -#define WPS_FRAM_TAG_SIZE 30 /* hardcoded in driver */ int len = atoi(custom + 16); - if (len < 0 || custom + WPS_FRAM_TAG_SIZE + len > end) { + if (len < 0 || custom + MGMT_FRAM_TAG_SIZE + len > end) { wpa_printf(MSG_DEBUG, "Invalid Manage.prob_req event " "length %d", len); return; } atheros_raw_receive(drv, NULL, - (u8 *) custom + WPS_FRAM_TAG_SIZE, len); -#endif /* CONFIG_WPS */ + (u8 *) custom + MGMT_FRAM_TAG_SIZE, len); + } else if (strncmp(custom, "Manage.assoc_req ", 17) == 0) { + /* Format: "Manage.assoc_req " | zero padding | + * frame */ + int len = atoi(custom + 17); + if (len < 0 || custom + MGMT_FRAM_TAG_SIZE + len > end) { + wpa_printf(MSG_DEBUG, "Invalid Manage.prob_req/" + "assoc_req/auth event length %d", len); + return; + } + atheros_raw_receive(drv, NULL, + (u8 *) custom + MGMT_FRAM_TAG_SIZE, len); + } else if (strncmp(custom, "Manage.action ", 14) == 0) { + /* Format: "Manage.assoc_req " | zero padding | + * frame */ + int len = atoi(custom + 14); + if (len < 0 || custom + MGMT_FRAM_TAG_SIZE + len > end) { + wpa_printf(MSG_DEBUG, "Invalid Manage.prob_req/" + "assoc_req/auth event length %d", len); + return; + } + atheros_raw_receive(drv, NULL, + (u8 *) custom + MGMT_FRAM_TAG_SIZE, len); + } else if (strncmp(custom, "Manage.auth ", 12) == 0) { + /* Format: "Manage.auth " | zero padding | frame + */ + int len = atoi(custom + 12); + if (len < 0 || custom + MGMT_FRAM_TAG_SIZE + len > end) { + wpa_printf(MSG_DEBUG, "Invalid Manage.prob_req/" + "assoc_req/auth event length %d", len); + return; + } + atheros_raw_receive(drv, NULL, + (u8 *) custom + MGMT_FRAM_TAG_SIZE, len); +#endif /* CONFIG_WPS or CONFIG_IEEE80211R */ + } +} + +/* +* Handle size of data problem. WEXT only allows data of 256 bytes for custom +* events, and p2p data can be much bigger. So the athr driver sends a small +* event telling me to collect the big data with an ioctl. +* On the first event, send all pending events to supplicant. +*/ +static void fetch_pending_big_events(struct atheros_driver_data *drv) +{ + union wpa_event_data event; + const struct ieee80211_mgmt *mgmt; + u8 tbuf[IW_PRIV_SIZE_MASK]; /* max size is 2047 bytes */ + u16 fc, stype; + struct iwreq iwr; + size_t data_len; + u32 freq, frame_type; + + while (1) { + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); + + iwr.u.data.pointer = (void *) tbuf; + iwr.u.data.length = sizeof(tbuf); + iwr.u.data.flags = IEEE80211_IOC_P2P_FETCH_FRAME; + + if (ioctl(drv->ioctl_sock, IEEE80211_IOCTL_P2P_BIG_PARAM, &iwr) + < 0) { + if (errno == ENOSPC) { + wpa_printf(MSG_DEBUG, "%s:%d exit", + __func__, __LINE__); + return; + } + wpa_printf(MSG_DEBUG, "athr: %s: P2P_BIG_PARAM[" + "P2P_FETCH_FRAME] failed: %s", + __func__, strerror(errno)); + return; + } + data_len = iwr.u.data.length; + wpa_hexdump(MSG_DEBUG, "athr: P2P_FETCH_FRAME data", + (u8 *) tbuf, data_len); + if (data_len < sizeof(freq) + sizeof(frame_type) + 24) { + wpa_printf(MSG_DEBUG, "athr: frame too short"); + continue; + } + os_memcpy(&freq, tbuf, sizeof(freq)); + os_memcpy(&frame_type, &tbuf[sizeof(freq)], + sizeof(frame_type)); + mgmt = (void *) &tbuf[sizeof(freq) + sizeof(frame_type)]; + data_len -= sizeof(freq) + sizeof(frame_type); + + if (frame_type == IEEE80211_EV_RX_MGMT) { + fc = le_to_host16(mgmt->frame_control); + stype = WLAN_FC_GET_STYPE(fc); + + wpa_printf(MSG_DEBUG, "athr: EV_RX_MGMT stype=%u " + "freq=%u len=%u", stype, freq, (int) data_len); + + if (stype == WLAN_FC_STYPE_ACTION) { + os_memset(&event, 0, sizeof(event)); + event.rx_mgmt.frame = (const u8 *) mgmt; + event.rx_mgmt.frame_len = data_len; + wpa_supplicant_event(drv->hapd, EVENT_RX_MGMT, + &event); + continue; + } + } else { + wpa_printf(MSG_DEBUG, "athr: %s unknown type %d", + __func__, frame_type); + continue; + } + } +} + +static void +atheros_wireless_event_atheros_custom(struct atheros_driver_data *drv, + int opcode, char *buf, int len) +{ + switch (opcode) { + case IEEE80211_EV_RX_MGMT: + wpa_printf(MSG_DEBUG, "WEXT: EV_RX_MGMT"); + fetch_pending_big_events(drv); + break; + default: + break; } } @@ -1055,8 +1493,15 @@ return; /* XXX */ memcpy(buf, custom, iwe->u.data.length); buf[iwe->u.data.length] = '\0'; - atheros_wireless_event_wireless_custom( - drv, buf, buf + iwe->u.data.length); + + if (iwe->u.data.flags != 0) { + atheros_wireless_event_atheros_custom( + drv, (int) iwe->u.data.flags, + buf, len); + } else { + atheros_wireless_event_wireless_custom( + drv, buf, buf + iwe->u.data.length); + } free(buf); break; } @@ -1245,6 +1690,7 @@ goto bad; if (l2_packet_get_own_addr(drv->sock_xmit, params->own_addr)) goto bad; + os_memcpy(drv->own_addr, params->own_addr, ETH_ALEN); if (params->bridge[0]) { wpa_printf(MSG_DEBUG, "Configure bridge %s for EAPOL traffic.", params->bridge[0]); @@ -1278,13 +1724,17 @@ linux_set_iface_flags(drv->ioctl_sock, drv->iface, 0); atheros_set_privacy(drv, 0); /* default to no privacy */ - atheros_receive_probe_req(drv); + if (atheros_receive_pkt(drv)) + goto bad; if (atheros_wireless_event_init(drv)) goto bad; return drv; bad: + atheros_reset_appfilter(drv); + if (drv->sock_raw) + l2_packet_deinit(drv->sock_raw); if (drv->sock_recv != NULL && drv->sock_recv != drv->sock_xmit) l2_packet_deinit(drv->sock_recv); if (drv->sock_xmit != NULL) @@ -1302,6 +1752,7 @@ { struct atheros_driver_data *drv = priv; + atheros_reset_appfilter(drv); netlink_deinit(drv->netlink); (void) linux_set_iface_flags(drv->ioctl_sock, drv->iface, 0); if (drv->ioctl_sock >= 0) @@ -1348,7 +1799,6 @@ memset(&iwr, 0, sizeof(iwr)); os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); iwr.u.essid.pointer = (caddr_t) buf; - iwr.u.essid.length = len; iwr.u.essid.length = (len > IW_ESSID_MAX_SIZE) ? IW_ESSID_MAX_SIZE : len; @@ -1421,6 +1871,290 @@ return 0; } + +#ifdef CONFIG_IEEE80211R + +static int atheros_send_mgmt(void *priv, const u8 *frm, size_t data_len, + int noack) +{ + struct atheros_driver_data *drv = priv; + u8 buf[1510]; + const struct ieee80211_mgmt *mgmt; + struct ieee80211req_mgmtbuf *mgmt_frm; + + mgmt = (const struct ieee80211_mgmt *) frm; + wpa_printf(MSG_DEBUG, "%s frmlen = %lu " MACSTR, __func__, + (unsigned long) data_len, MAC2STR(mgmt->da)); + mgmt_frm = (struct ieee80211req_mgmtbuf *) buf; + memcpy(mgmt_frm->macaddr, (u8 *)mgmt->da, IEEE80211_ADDR_LEN); + mgmt_frm->buflen = data_len; + if (&mgmt_frm->buf[0] + data_len > buf + sizeof(buf)) { + wpa_printf(MSG_INFO, "atheros: Too long frame for " + "atheros_send_mgmt (%u)", (unsigned int) data_len); + return -1; + } + os_memcpy(&mgmt_frm->buf[0], frm, data_len); + return set80211priv(drv, IEEE80211_IOCTL_SEND_MGMT, mgmt_frm, + sizeof(struct ieee80211req_mgmtbuf) + data_len); +} + + +static int atheros_add_tspec(void *priv, const u8 *addr, u8 *tspec_ie, + size_t tspec_ielen) +{ + struct atheros_driver_data *drv = priv; + int retv; + struct ieee80211req_res req; + struct ieee80211req_res_addts *addts = &req.u.addts; + + wpa_printf(MSG_DEBUG, "%s", __func__); + req.type = IEEE80211_RESREQ_ADDTS; + os_memcpy(&req.macaddr[0], addr, IEEE80211_ADDR_LEN); + os_memcpy(addts->tspecie, tspec_ie, tspec_ielen); + retv = set80211priv(drv, IEEE80211_IOCTL_RES_REQ, &req, + sizeof(struct ieee80211req_res)); + if (retv < 0) { + wpa_printf(MSG_DEBUG, "%s IEEE80211_IOCTL_RES_REQ FAILED " + "retv = %d", __func__, retv); + return -1; + } + os_memcpy(tspec_ie, addts->tspecie, tspec_ielen); + return addts->status; +} + + +static int atheros_add_sta_node(void *priv, const u8 *addr, u16 auth_alg) +{ + struct atheros_driver_data *drv = priv; + struct ieee80211req_res req; + struct ieee80211req_res_addnode *addnode = &req.u.addnode; + + wpa_printf(MSG_DEBUG, "%s", __func__); + req.type = IEEE80211_RESREQ_ADDNODE; + os_memcpy(&req.macaddr[0], addr, IEEE80211_ADDR_LEN); + addnode->auth_alg = auth_alg; + return set80211priv(drv, IEEE80211_IOCTL_RES_REQ, &req, + sizeof(struct ieee80211req_res)); +} + +#endif /* CONFIG_IEEE80211R */ + + +/* Use only to set a big param, get will not work. */ +static int +set80211big(struct atheros_driver_data *drv, int op, const void *data, int len) +{ + struct iwreq iwr; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); + + iwr.u.data.pointer = (void *) data; + iwr.u.data.length = len; + iwr.u.data.flags = op; + wpa_printf(MSG_DEBUG, "%s: op=0x%x=%d (%s) len=0x%x", + __func__, op, op, athr_get_param_name(op), len); + + if (ioctl(drv->ioctl_sock, IEEE80211_IOCTL_P2P_BIG_PARAM, &iwr) < 0) { + wpa_printf(MSG_DEBUG, "%s: op=0x%x (%s) subop=0x%x=%d " + "value=0x%x,0x%x failed: %d (%s)", + __func__, op, athr_get_ioctl_name(op), iwr.u.mode, + iwr.u.mode, iwr.u.data.length, + iwr.u.data.flags, errno, strerror(errno)); + return -1; + } + return 0; +} + + +static int atheros_send_action(void *priv, unsigned int freq, + unsigned int wait, + const u8 *dst, const u8 *src, + const u8 *bssid, + const u8 *data, size_t data_len, int no_cck) +{ + struct atheros_driver_data *drv = priv; + struct ieee80211_p2p_send_action *act; + int res; + + act = os_zalloc(sizeof(*act) + data_len); + if (act == NULL) + return -1; + act->freq = freq; + os_memcpy(act->dst_addr, dst, ETH_ALEN); + os_memcpy(act->src_addr, src, ETH_ALEN); + os_memcpy(act->bssid, bssid, ETH_ALEN); + os_memcpy(act + 1, data, data_len); + wpa_printf(MSG_DEBUG, "%s: freq=%d, wait=%u, dst=" MACSTR ", src=" + MACSTR ", bssid=" MACSTR, + __func__, act->freq, wait, MAC2STR(act->dst_addr), + MAC2STR(act->src_addr), MAC2STR(act->bssid)); + wpa_hexdump(MSG_MSGDUMP, "athr: act", (u8 *) act, sizeof(*act)); + wpa_hexdump(MSG_MSGDUMP, "athr: data", data, data_len); + + res = set80211big(drv, IEEE80211_IOC_P2P_SEND_ACTION, + act, sizeof(*act) + data_len); + os_free(act); + return res; +} + + +#if defined(CONFIG_WNM) && defined(IEEE80211_APPIE_FRAME_WNM) +static int athr_wnm_tfs(struct atheros_driver_data *drv, const u8* peer, + u8 *ie, u16 *len, enum wnm_oper oper) +{ +#define IEEE80211_APPIE_MAX 1024 /* max appie buffer size */ + u8 buf[IEEE80211_APPIE_MAX]; + struct ieee80211req_getset_appiebuf *tfs_ie; + u16 val; + + wpa_printf(MSG_DEBUG, "atheros: ifname=%s, WNM TFS IE oper=%d " MACSTR, + drv->iface, oper, MAC2STR(peer)); + + switch (oper) { + case WNM_SLEEP_TFS_REQ_IE_SET: + if (*len > IEEE80211_APPIE_MAX - + sizeof(struct ieee80211req_getset_appiebuf)) { + wpa_printf(MSG_DEBUG, "TFS Req IE(s) too large"); + return -1; + } + tfs_ie = (struct ieee80211req_getset_appiebuf *) buf; + tfs_ie->app_frmtype = IEEE80211_APPIE_FRAME_WNM; + tfs_ie->app_buflen = ETH_ALEN + 2 + 2 + *len; + + /* Command header for driver */ + os_memcpy(&(tfs_ie->app_buf[0]), peer, ETH_ALEN); + val = oper; + os_memcpy(&(tfs_ie->app_buf[0]) + ETH_ALEN, &val, 2); + val = *len; + os_memcpy(&(tfs_ie->app_buf[0]) + ETH_ALEN + 2, &val, 2); + + /* copy the ie */ + os_memcpy(&(tfs_ie->app_buf[0]) + ETH_ALEN + 2 + 2, ie, *len); + + if (set80211priv(drv, IEEE80211_IOCTL_SET_APPIEBUF, tfs_ie, + IEEE80211_APPIE_MAX)) { + wpa_printf(MSG_DEBUG, "%s: Failed to set WNM TFS IE: " + "%s", __func__, strerror(errno)); + return -1; + } + break; + case WNM_SLEEP_TFS_RESP_IE_ADD: + tfs_ie = (struct ieee80211req_getset_appiebuf *) buf; + tfs_ie->app_frmtype = IEEE80211_APPIE_FRAME_WNM; + tfs_ie->app_buflen = IEEE80211_APPIE_MAX - + sizeof(struct ieee80211req_getset_appiebuf); + /* Command header for driver */ + os_memcpy(&(tfs_ie->app_buf[0]), peer, ETH_ALEN); + val = oper; + os_memcpy(&(tfs_ie->app_buf[0]) + ETH_ALEN, &val, 2); + val = 0; + os_memcpy(&(tfs_ie->app_buf[0]) + ETH_ALEN + 2, &val, 2); + + if (set80211priv(drv, IEEE80211_IOCTL_GET_APPIEBUF, tfs_ie, + IEEE80211_APPIE_MAX)) { + wpa_printf(MSG_DEBUG, "%s: Failed to get WNM TFS IE: " + "%s", __func__, strerror(errno)); + return -1; + } + + *len = tfs_ie->app_buflen; + os_memcpy(ie, &(tfs_ie->app_buf[0]), *len); + wpa_printf(MSG_DEBUG, "atheros: %c len=%d", tfs_ie->app_buf[0], + *len); + break; + case WNM_SLEEP_TFS_RESP_IE_NONE: + *len = 0; + break; + case WNM_SLEEP_TFS_IE_DEL: + tfs_ie = (struct ieee80211req_getset_appiebuf *) buf; + tfs_ie->app_frmtype = IEEE80211_APPIE_FRAME_WNM; + tfs_ie->app_buflen = IEEE80211_APPIE_MAX - + sizeof(struct ieee80211req_getset_appiebuf); + /* Command header for driver */ + os_memcpy(&(tfs_ie->app_buf[0]), peer, ETH_ALEN); + val = oper; + os_memcpy(&(tfs_ie->app_buf[0]) + ETH_ALEN, &val, 2); + val = 0; + os_memcpy(&(tfs_ie->app_buf[0]) + ETH_ALEN + 2, &val, 2); + + if (set80211priv(drv, IEEE80211_IOCTL_SET_APPIEBUF, tfs_ie, + IEEE80211_APPIE_MAX)) { + wpa_printf(MSG_DEBUG, "%s: Failed to set WNM TFS IE: " + "%s", __func__, strerror(errno)); + return -1; + } + break; + default: + wpa_printf(MSG_DEBUG, "Unsupported TFS oper %d", oper); + break; + } + + return 0; +} + + +static int atheros_wnm_sleep(struct atheros_driver_data *drv, + const u8 *peer, enum wnm_oper oper) +{ + u8 *data, *pos; + size_t dlen; + int ret; + u16 val; + + wpa_printf(MSG_DEBUG, "atheros: WNM-Sleep Oper %d, " MACSTR, + oper, MAC2STR(peer)); + + dlen = ETH_ALEN + 2 + 2; + data = os_malloc(dlen); + if (data == NULL) + return -1; + + /* Command header for driver */ + pos = data; + os_memcpy(pos, peer, ETH_ALEN); + pos += ETH_ALEN; + + val = oper; + os_memcpy(pos, &val, 2); + pos += 2; + + val = 0; + os_memcpy(pos, &val, 2); + + ret = atheros_set_wps_ie(drv, data, dlen, IEEE80211_APPIE_FRAME_WNM); + + os_free(data); + + return ret; +} + + +static int atheros_wnm_oper(void *priv, enum wnm_oper oper, const u8 *peer, + u8 *buf, u16 *buf_len) +{ + struct atheros_driver_data *drv = priv; + + switch (oper) { + case WNM_SLEEP_ENTER_CONFIRM: + case WNM_SLEEP_ENTER_FAIL: + case WNM_SLEEP_EXIT_CONFIRM: + case WNM_SLEEP_EXIT_FAIL: + return atheros_wnm_sleep(drv, peer, oper); + case WNM_SLEEP_TFS_REQ_IE_SET: + case WNM_SLEEP_TFS_RESP_IE_ADD: + case WNM_SLEEP_TFS_RESP_IE_NONE: + case WNM_SLEEP_TFS_IE_DEL: + return athr_wnm_tfs(drv, peer, buf, buf_len, oper); + default: + wpa_printf(MSG_DEBUG, "atheros: Unsupported WNM operation %d", + oper); + return -1; + } +} +#endif /* CONFIG_WNM && IEEE80211_APPIE_FRAME_WNM */ + + const struct wpa_driver_ops wpa_driver_atheros_ops = { .name = "atheros", .hapd_init = atheros_init, @@ -1444,4 +2178,16 @@ .set_ap_wps_ie = atheros_set_ap_wps_ie, .set_authmode = atheros_set_authmode, .set_ap = atheros_set_ap, +#ifdef CONFIG_IEEE80211R + .sta_assoc = atheros_sta_assoc, + .sta_auth = atheros_sta_auth, + .send_mlme = atheros_send_mgmt, + .add_tspec = atheros_add_tspec, + .add_sta_node = atheros_add_sta_node, +#endif /* CONFIG_IEEE80211R */ + .send_action = atheros_send_action, +#if defined(CONFIG_WNM) && defined(IEEE80211_APPIE_FRAME_WNM) + .wnm_oper = atheros_wnm_oper, +#endif /* CONFIG_WNM && IEEE80211_APPIE_FRAME_WNM */ + .set_qos_map = atheros_set_qos_map, }; diff -Nru wpa-1.0/src/drivers/driver_broadcom.c wpa-2.1/src/drivers/driver_broadcom.c --- wpa-1.0/src/drivers/driver_broadcom.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/drivers/driver_broadcom.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,599 +0,0 @@ -/* - * WPA Supplicant - driver interaction with old Broadcom wl.o driver - * Copyright (c) 2004, Nikki Chumkov - * Copyright (c) 2004, Jouni Malinen - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - * - * Please note that the newer Broadcom driver ("hybrid Linux driver") supports - * Linux wireless extensions and does not need (or even work) with this old - * driver wrapper. Use driver_wext.c with that driver. - */ - -#include "includes.h" - -#include - -#include "common.h" - -#if 0 -#include -#include /* the L2 protocols */ -#else -#include -#include /* The L2 protocols */ -#endif -#include -#include - -/* wlioctl.h is a Broadcom header file and it is available, e.g., from Linksys - * WRT54G GPL tarball. */ -#include - -#include "driver.h" -#include "eloop.h" - -struct wpa_driver_broadcom_data { - void *ctx; - int ioctl_sock; - int event_sock; - char ifname[IFNAMSIZ + 1]; -}; - - -#ifndef WLC_DEAUTHENTICATE -#define WLC_DEAUTHENTICATE 143 -#endif -#ifndef WLC_DEAUTHENTICATE_WITH_REASON -#define WLC_DEAUTHENTICATE_WITH_REASON 201 -#endif -#ifndef WLC_SET_TKIP_COUNTERMEASURES -#define WLC_SET_TKIP_COUNTERMEASURES 202 -#endif - -#if !defined(PSK_ENABLED) /* NEW driver interface */ -#define WL_VERSION 360130 -/* wireless authentication bit vector */ -#define WPA_ENABLED 1 -#define PSK_ENABLED 2 - -#define WAUTH_WPA_ENABLED(wauth) ((wauth) & WPA_ENABLED) -#define WAUTH_PSK_ENABLED(wauth) ((wauth) & PSK_ENABLED) -#define WAUTH_ENABLED(wauth) ((wauth) & (WPA_ENABLED | PSK_ENABLED)) - -#define WSEC_PRIMARY_KEY WL_PRIMARY_KEY - -typedef wl_wsec_key_t wsec_key_t; -#endif - -typedef struct { - uint32 val; - struct ether_addr ea; - uint16 res; -} wlc_deauth_t; - - -static void wpa_driver_broadcom_scan_timeout(void *eloop_ctx, - void *timeout_ctx); - -static int broadcom_ioctl(struct wpa_driver_broadcom_data *drv, int cmd, - void *buf, int len) -{ - struct ifreq ifr; - wl_ioctl_t ioc; - int ret = 0; - - wpa_printf(MSG_MSGDUMP, "BROADCOM: wlioctl(%s,%d,len=%d,val=%p)", - drv->ifname, cmd, len, buf); - /* wpa_hexdump(MSG_MSGDUMP, "BROADCOM: wlioctl buf", buf, len); */ - - ioc.cmd = cmd; - ioc.buf = buf; - ioc.len = len; - os_strlcpy(ifr.ifr_name, drv->ifname, IFNAMSIZ); - ifr.ifr_data = (caddr_t) &ioc; - if ((ret = ioctl(drv->ioctl_sock, SIOCDEVPRIVATE, &ifr)) < 0) { - if (cmd != WLC_GET_MAGIC) - perror(ifr.ifr_name); - wpa_printf(MSG_MSGDUMP, "BROADCOM: wlioctl cmd=%d res=%d", - cmd, ret); - } - - return ret; -} - -static int wpa_driver_broadcom_get_bssid(void *priv, u8 *bssid) -{ - struct wpa_driver_broadcom_data *drv = priv; - if (broadcom_ioctl(drv, WLC_GET_BSSID, bssid, ETH_ALEN) == 0) - return 0; - - os_memset(bssid, 0, ETH_ALEN); - return -1; -} - -static int wpa_driver_broadcom_get_ssid(void *priv, u8 *ssid) -{ - struct wpa_driver_broadcom_data *drv = priv; - wlc_ssid_t s; - - if (broadcom_ioctl(drv, WLC_GET_SSID, &s, sizeof(s)) == -1) - return -1; - - os_memcpy(ssid, s.SSID, s.SSID_len); - return s.SSID_len; -} - -static int wpa_driver_broadcom_set_wpa(void *priv, int enable) -{ - struct wpa_driver_broadcom_data *drv = priv; - unsigned int wauth, wsec; - struct ether_addr ea; - - os_memset(&ea, enable ? 0xff : 0, sizeof(ea)); - if (broadcom_ioctl(drv, WLC_GET_WPA_AUTH, &wauth, sizeof(wauth)) == - -1 || - broadcom_ioctl(drv, WLC_GET_WSEC, &wsec, sizeof(wsec)) == -1) - return -1; - - if (enable) { - wauth = PSK_ENABLED; - wsec = TKIP_ENABLED; - } else { - wauth = 255; - wsec &= ~(TKIP_ENABLED | AES_ENABLED); - } - - if (broadcom_ioctl(drv, WLC_SET_WPA_AUTH, &wauth, sizeof(wauth)) == - -1 || - broadcom_ioctl(drv, WLC_SET_WSEC, &wsec, sizeof(wsec)) == -1) - return -1; - - /* FIX: magic number / error handling? */ - broadcom_ioctl(drv, 122, &ea, sizeof(ea)); - - return 0; -} - -static int wpa_driver_broadcom_set_key(const char *ifname, void *priv, - enum wpa_alg alg, - const u8 *addr, int key_idx, int set_tx, - const u8 *seq, size_t seq_len, - const u8 *key, size_t key_len) -{ - struct wpa_driver_broadcom_data *drv = priv; - int ret; - wsec_key_t wkt; - - os_memset(&wkt, 0, sizeof wkt); - wpa_printf(MSG_MSGDUMP, "BROADCOM: SET %sKEY[%d] alg=%d", - set_tx ? "PRIMARY " : "", key_idx, alg); - if (key && key_len > 0) - wpa_hexdump_key(MSG_MSGDUMP, "BROADCOM: key", key, key_len); - - switch (alg) { - case WPA_ALG_NONE: - wkt.algo = CRYPTO_ALGO_OFF; - break; - case WPA_ALG_WEP: - wkt.algo = CRYPTO_ALGO_WEP128; /* CRYPTO_ALGO_WEP1? */ - break; - case WPA_ALG_TKIP: - wkt.algo = 0; /* CRYPTO_ALGO_TKIP? */ - break; - case WPA_ALG_CCMP: - wkt.algo = 0; /* CRYPTO_ALGO_AES_CCM; - * AES_OCB_MSDU, AES_OCB_MPDU? */ - break; - default: - wkt.algo = CRYPTO_ALGO_NALG; - break; - } - - if (seq && seq_len > 0) - wpa_hexdump(MSG_MSGDUMP, "BROADCOM: SEQ", seq, seq_len); - - if (addr) - wpa_hexdump(MSG_MSGDUMP, "BROADCOM: addr", addr, ETH_ALEN); - - wkt.index = key_idx; - wkt.len = key_len; - if (key && key_len > 0) { - os_memcpy(wkt.data, key, key_len); - if (key_len == 32) { - /* hack hack hack XXX */ - os_memcpy(&wkt.data[16], &key[24], 8); - os_memcpy(&wkt.data[24], &key[16], 8); - } - } - /* wkt.algo = CRYPTO_ALGO_...; */ - wkt.flags = set_tx ? 0 : WSEC_PRIMARY_KEY; - if (addr && set_tx) - os_memcpy(&wkt.ea, addr, sizeof(wkt.ea)); - ret = broadcom_ioctl(drv, WLC_SET_KEY, &wkt, sizeof(wkt)); - if (addr && set_tx) { - /* FIX: magic number / error handling? */ - broadcom_ioctl(drv, 121, &wkt.ea, sizeof(wkt.ea)); - } - return ret; -} - - -static void wpa_driver_broadcom_event_receive(int sock, void *ctx, - void *sock_ctx) -{ - char buf[8192]; - int left; - wl_wpa_header_t *wwh; - union wpa_event_data data; - u8 *resp_ies = NULL; - - if ((left = recv(sock, buf, sizeof buf, 0)) < 0) - return; - - wpa_hexdump(MSG_DEBUG, "RECEIVE EVENT", (u8 *) buf, left); - - if ((size_t) left < sizeof(wl_wpa_header_t)) - return; - - wwh = (wl_wpa_header_t *) buf; - - if (wwh->snap.type != WL_WPA_ETHER_TYPE) - return; - if (os_memcmp(&wwh->snap, wl_wpa_snap_template, 6) != 0) - return; - - os_memset(&data, 0, sizeof(data)); - - switch (wwh->type) { - case WLC_ASSOC_MSG: - left -= WL_WPA_HEADER_LEN; - wpa_printf(MSG_DEBUG, "BROADCOM: ASSOC MESSAGE (left: %d)", - left); - if (left > 0) { - resp_ies = os_malloc(left); - if (resp_ies == NULL) - return; - os_memcpy(resp_ies, buf + WL_WPA_HEADER_LEN, left); - data.assoc_info.resp_ies = resp_ies; - data.assoc_info.resp_ies_len = left; - } - - wpa_supplicant_event(ctx, EVENT_ASSOC, &data); - os_free(resp_ies); - break; - case WLC_DISASSOC_MSG: - wpa_printf(MSG_DEBUG, "BROADCOM: DISASSOC MESSAGE"); - wpa_supplicant_event(ctx, EVENT_DISASSOC, NULL); - break; - case WLC_PTK_MIC_MSG: - wpa_printf(MSG_DEBUG, "BROADCOM: PTK MIC MSG MESSAGE"); - data.michael_mic_failure.unicast = 1; - wpa_supplicant_event(ctx, EVENT_MICHAEL_MIC_FAILURE, &data); - break; - case WLC_GTK_MIC_MSG: - wpa_printf(MSG_DEBUG, "BROADCOM: GTK MIC MSG MESSAGE"); - data.michael_mic_failure.unicast = 0; - wpa_supplicant_event(ctx, EVENT_MICHAEL_MIC_FAILURE, &data); - break; - default: - wpa_printf(MSG_DEBUG, "BROADCOM: UNKNOWN MESSAGE (%d)", - wwh->type); - break; - } -} - -static void * wpa_driver_broadcom_init(void *ctx, const char *ifname) -{ - int s; - struct sockaddr_ll ll; - struct wpa_driver_broadcom_data *drv; - struct ifreq ifr; - - /* open socket to kernel */ - if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { - perror("socket"); - return NULL; - } - /* do it */ - os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); - if (ioctl(s, SIOCGIFINDEX, &ifr) < 0) { - perror(ifr.ifr_name); - return NULL; - } - - - drv = os_zalloc(sizeof(*drv)); - if (drv == NULL) - return NULL; - drv->ctx = ctx; - os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname)); - drv->ioctl_sock = s; - - s = socket(PF_PACKET, SOCK_RAW, ntohs(ETH_P_802_2)); - if (s < 0) { - perror("socket(PF_PACKET, SOCK_RAW, ntohs(ETH_P_802_2))"); - close(drv->ioctl_sock); - os_free(drv); - return NULL; - } - - os_memset(&ll, 0, sizeof(ll)); - ll.sll_family = AF_PACKET; - ll.sll_protocol = ntohs(ETH_P_802_2); - ll.sll_ifindex = ifr.ifr_ifindex; - ll.sll_hatype = 0; - ll.sll_pkttype = PACKET_HOST; - ll.sll_halen = 0; - - if (bind(s, (struct sockaddr *) &ll, sizeof(ll)) < 0) { - perror("bind(netlink)"); - close(s); - close(drv->ioctl_sock); - os_free(drv); - return NULL; - } - - eloop_register_read_sock(s, wpa_driver_broadcom_event_receive, ctx, - NULL); - drv->event_sock = s; - wpa_driver_broadcom_set_wpa(drv, 1); - - return drv; -} - -static void wpa_driver_broadcom_deinit(void *priv) -{ - struct wpa_driver_broadcom_data *drv = priv; - wpa_driver_broadcom_set_wpa(drv, 0); - eloop_cancel_timeout(wpa_driver_broadcom_scan_timeout, drv, drv->ctx); - eloop_unregister_read_sock(drv->event_sock); - close(drv->event_sock); - close(drv->ioctl_sock); - os_free(drv); -} - -static int wpa_driver_broadcom_set_countermeasures(void *priv, - int enabled) -{ -#if 0 - struct wpa_driver_broadcom_data *drv = priv; - /* FIX: ? */ - return broadcom_ioctl(drv, WLC_SET_TKIP_COUNTERMEASURES, &enabled, - sizeof(enabled)); -#else - return 0; -#endif -} - -static int wpa_driver_broadcom_set_drop_unencrypted(void *priv, int enabled) -{ - struct wpa_driver_broadcom_data *drv = priv; - /* SET_EAP_RESTRICT, SET_WEP_RESTRICT */ - int _restrict = (enabled ? 1 : 0); - - if (broadcom_ioctl(drv, WLC_SET_WEP_RESTRICT, - &_restrict, sizeof(_restrict)) < 0 || - broadcom_ioctl(drv, WLC_SET_EAP_RESTRICT, - &_restrict, sizeof(_restrict)) < 0) - return -1; - - return 0; -} - -static void wpa_driver_broadcom_scan_timeout(void *eloop_ctx, - void *timeout_ctx) -{ - wpa_printf(MSG_DEBUG, "Scan timeout - try to get results"); - wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL); -} - -static int wpa_driver_broadcom_scan(void *priv, - struct wpa_driver_scan_params *params) -{ - struct wpa_driver_broadcom_data *drv = priv; - wlc_ssid_t wst = { 0, "" }; - const u8 *ssid = params->ssids[0].ssid; - size_t ssid_len = params->ssids[0].ssid_len; - - if (ssid && ssid_len > 0 && ssid_len <= sizeof(wst.SSID)) { - wst.SSID_len = ssid_len; - os_memcpy(wst.SSID, ssid, ssid_len); - } - - if (broadcom_ioctl(drv, WLC_SCAN, &wst, sizeof(wst)) < 0) - return -1; - - eloop_cancel_timeout(wpa_driver_broadcom_scan_timeout, drv, drv->ctx); - eloop_register_timeout(3, 0, wpa_driver_broadcom_scan_timeout, drv, - drv->ctx); - return 0; -} - - -static const int frequency_list[] = { - 2412, 2417, 2422, 2427, 2432, 2437, 2442, - 2447, 2452, 2457, 2462, 2467, 2472, 2484 -}; - -struct bss_ie_hdr { - u8 elem_id; - u8 len; - u8 oui[3]; - /* u8 oui_type; */ - /* u16 version; */ -} __attribute__ ((packed)); - -static struct wpa_scan_results * -wpa_driver_broadcom_get_scan_results(void *priv) -{ - struct wpa_driver_broadcom_data *drv = priv; - char *buf; - wl_scan_results_t *wsr; - wl_bss_info_t *wbi; - size_t ap_num; - struct wpa_scan_results *res; - - buf = os_malloc(WLC_IOCTL_MAXLEN); - if (buf == NULL) - return NULL; - - wsr = (wl_scan_results_t *) buf; - - wsr->buflen = WLC_IOCTL_MAXLEN - sizeof(wsr); - wsr->version = 107; - wsr->count = 0; - - if (broadcom_ioctl(drv, WLC_SCAN_RESULTS, buf, WLC_IOCTL_MAXLEN) < 0) { - os_free(buf); - return NULL; - } - - res = os_zalloc(sizeof(*res)); - if (res == NULL) { - os_free(buf); - return NULL; - } - - res->res = os_zalloc(wsr->count * sizeof(struct wpa_scan_res *)); - if (res->res == NULL) { - os_free(res); - os_free(buf); - return NULL; - } - - for (ap_num = 0, wbi = wsr->bss_info; ap_num < wsr->count; ++ap_num) { - struct wpa_scan_res *r; - r = os_malloc(sizeof(*r) + wbi->ie_length); - if (r == NULL) - break; - res->res[res->num++] = r; - - os_memcpy(r->bssid, &wbi->BSSID, ETH_ALEN); - r->freq = frequency_list[wbi->channel - 1]; - /* get ie's */ - os_memcpy(r + 1, wbi + 1, wbi->ie_length); - r->ie_len = wbi->ie_length; - - wbi = (wl_bss_info_t *) ((u8 *) wbi + wbi->length); - } - - wpa_printf(MSG_MSGDUMP, "Received %d bytes of scan results (%lu " - "BSSes)", - wsr->buflen, (unsigned long) ap_num); - - os_free(buf); - return res; - } - -static int wpa_driver_broadcom_deauthenticate(void *priv, const u8 *addr, - int reason_code) -{ - struct wpa_driver_broadcom_data *drv = priv; - wlc_deauth_t wdt; - wdt.val = reason_code; - os_memcpy(&wdt.ea, addr, sizeof wdt.ea); - wdt.res = 0x7fff; - return broadcom_ioctl(drv, WLC_DEAUTHENTICATE_WITH_REASON, &wdt, - sizeof(wdt)); -} - -static int wpa_driver_broadcom_disassociate(void *priv, const u8 *addr, - int reason_code) -{ - struct wpa_driver_broadcom_data *drv = priv; - return broadcom_ioctl(drv, WLC_DISASSOC, NULL, 0); -} - -static int -wpa_driver_broadcom_associate(void *priv, - struct wpa_driver_associate_params *params) -{ - struct wpa_driver_broadcom_data *drv = priv; - wlc_ssid_t s; - int infra = 1; - int auth = 0; - int wsec = 4; - int dummy; - int wpa_auth; - int ret; - - ret = wpa_driver_broadcom_set_drop_unencrypted( - drv, params->drop_unencrypted); - - s.SSID_len = params->ssid_len; - os_memcpy(s.SSID, params->ssid, params->ssid_len); - - switch (params->pairwise_suite) { - case CIPHER_WEP40: - case CIPHER_WEP104: - wsec = 1; - break; - - case CIPHER_TKIP: - wsec = 2; - break; - - case CIPHER_CCMP: - wsec = 4; - break; - - default: - wsec = 0; - break; - } - - switch (params->key_mgmt_suite) { - case KEY_MGMT_802_1X: - wpa_auth = 1; - break; - - case KEY_MGMT_PSK: - wpa_auth = 2; - break; - - default: - wpa_auth = 255; - break; - } - - /* printf("broadcom_associate: %u %u %u\n", pairwise_suite, - * group_suite, key_mgmt_suite); - * broadcom_ioctl(ifname, WLC_GET_WSEC, &wsec, sizeof(wsec)); - * wl join uses wlc_sec_wep here, not wlc_set_wsec */ - - if (broadcom_ioctl(drv, WLC_SET_WSEC, &wsec, sizeof(wsec)) < 0 || - broadcom_ioctl(drv, WLC_SET_WPA_AUTH, &wpa_auth, - sizeof(wpa_auth)) < 0 || - broadcom_ioctl(drv, WLC_GET_WEP, &dummy, sizeof(dummy)) < 0 || - broadcom_ioctl(drv, WLC_SET_INFRA, &infra, sizeof(infra)) < 0 || - broadcom_ioctl(drv, WLC_SET_AUTH, &auth, sizeof(auth)) < 0 || - broadcom_ioctl(drv, WLC_SET_WEP, &wsec, sizeof(wsec)) < 0 || - broadcom_ioctl(drv, WLC_SET_SSID, &s, sizeof(s)) < 0) - return -1; - - return ret; -} - -const struct wpa_driver_ops wpa_driver_broadcom_ops = { - .name = "broadcom", - .desc = "Broadcom wl.o driver", - .get_bssid = wpa_driver_broadcom_get_bssid, - .get_ssid = wpa_driver_broadcom_get_ssid, - .set_key = wpa_driver_broadcom_set_key, - .init = wpa_driver_broadcom_init, - .deinit = wpa_driver_broadcom_deinit, - .set_countermeasures = wpa_driver_broadcom_set_countermeasures, - .scan2 = wpa_driver_broadcom_scan, - .get_scan_results2 = wpa_driver_broadcom_get_scan_results, - .deauthenticate = wpa_driver_broadcom_deauthenticate, - .disassociate = wpa_driver_broadcom_disassociate, - .associate = wpa_driver_broadcom_associate, -}; diff -Nru wpa-1.0/src/drivers/driver_bsd.c wpa-2.1/src/drivers/driver_bsd.c --- wpa-1.0/src/drivers/driver_bsd.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/drivers/driver_bsd.c 2014-02-04 11:23:35.000000000 +0000 @@ -3,14 +3,8 @@ * Copyright (c) 2004, Sam Leffler * Copyright (c) 2004, 2Wire, Inc * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -67,6 +61,9 @@ int prev_roaming; /* roaming state to restore on deinit */ int prev_privacy; /* privacy state to restore on deinit */ int prev_wpa; /* wpa state to restore on deinit */ + enum ieee80211_opmode opmode; /* operation mode */ + char *event_buf; + size_t event_buf_len; }; /* Generic functions for hostapd and wpa_supplicant */ @@ -271,10 +268,15 @@ return -1; } - if (enable) + if (enable) { + if (ifr.ifr_flags & IFF_UP) + return 0; ifr.ifr_flags |= IFF_UP; - else + } else { + if (!(ifr.ifr_flags & IFF_UP)) + return 0; ifr.ifr_flags &= ~IFF_UP; + } if (ioctl(drv->sock, SIOCSIFFLAGS, &ifr) < 0) { perror("ioctl[SIOCSIFFLAGS]"); @@ -290,6 +292,9 @@ size_t seq_len, const u8 *key, size_t key_len) { struct ieee80211req_key wk; +#ifdef IEEE80211_KEY_NOREPLAY + struct bsd_driver_data *drv = priv; +#endif /* IEEE80211_KEY_NOREPLAY */ wpa_printf(MSG_DEBUG, "%s: alg=%d addr=%p key_idx=%d set_tx=%d " "seq_len=%zu key_len=%zu", __func__, alg, addr, key_idx, @@ -344,6 +349,16 @@ } if (wk.ik_keyix != IEEE80211_KEYIX_NONE && set_tx) wk.ik_flags |= IEEE80211_KEY_DEFAULT; +#ifndef HOSTAPD +#ifdef IEEE80211_KEY_NOREPLAY + /* + * Ignore replay failures in IBSS and AHDEMO mode. + */ + if (drv->opmode == IEEE80211_M_IBSS || + drv->opmode == IEEE80211_M_AHDEMO) + wk.ik_flags |= IEEE80211_KEY_NOREPLAY; +#endif /* IEEE80211_KEY_NOREPLAY */ +#endif /* HOSTAPD */ wk.ik_keylen = key_len; if (seq) { #ifdef WORDS_BIGENDIAN @@ -479,26 +494,6 @@ return bsd_ctrl_iface(priv, 1); } -static int -bsd_set_sta_authorized(void *priv, const u8 *addr, - int total_flags, int flags_or, int flags_and) -{ - int authorized = -1; - - /* For now, only support setting Authorized flag */ - if (flags_or & WPA_STA_AUTHORIZED) - authorized = 1; - if (!(flags_and & WPA_STA_AUTHORIZED)) - authorized = 0; - - if (authorized < 0) - return 0; - - return bsd_send_mlme_param(priv, authorized ? - IEEE80211_MLME_AUTHORIZE : - IEEE80211_MLME_UNAUTHORIZE, 0, addr); -} - static void bsd_new_sta(void *priv, void *ctx, u8 addr[IEEE80211_ADDR_LEN]) { @@ -591,7 +586,7 @@ return 0; } -static int +static size_t rtbuf_len(void) { size_t len; @@ -728,37 +723,26 @@ bsd_wireless_event_receive(int sock, void *ctx, void *sock_ctx) { struct bsd_driver_data *drv = ctx; - char *buf; struct if_announcemsghdr *ifan; struct rt_msghdr *rtm; struct ieee80211_michael_event *mic; struct ieee80211_join_event *join; struct ieee80211_leave_event *leave; - int n, len; + int n; union wpa_event_data data; - len = rtbuf_len(); - - buf = os_malloc(len); - if (buf == NULL) { - wpa_printf(MSG_ERROR, "%s os_malloc() failed\n", __func__); - return; - } - - n = read(sock, buf, len); + n = read(sock, drv->event_buf, drv->event_buf_len); if (n < 0) { if (errno != EINTR && errno != EAGAIN) wpa_printf(MSG_ERROR, "%s read() failed: %s\n", __func__, strerror(errno)); - os_free(buf); return; } - rtm = (struct rt_msghdr *) buf; + rtm = (struct rt_msghdr *) drv->event_buf; if (rtm->rtm_version != RTM_VERSION) { wpa_printf(MSG_DEBUG, "Invalid routing message version=%d", rtm->rtm_version); - os_free(buf); return; } ifan = (struct if_announcemsghdr *) rtm; @@ -799,7 +783,6 @@ } break; } - os_free(buf); } static void @@ -816,7 +799,15 @@ drv = os_zalloc(sizeof(struct bsd_driver_data)); if (drv == NULL) { - printf("Could not allocate memory for bsd driver data\n"); + wpa_printf(MSG_ERROR, "Could not allocate memory for bsd driver data"); + return NULL; + } + + drv->event_buf_len = rtbuf_len(); + + drv->event_buf = os_malloc(drv->event_buf_len); + if (drv->event_buf == NULL) { + wpa_printf(MSG_ERROR, "%s: os_malloc() failed", __func__); goto bad; } @@ -859,6 +850,7 @@ l2_packet_deinit(drv->sock_xmit); if (drv->sock >= 0) close(drv->sock); + os_free(drv->event_buf); if (drv != NULL) os_free(drv); return NULL; @@ -879,9 +871,37 @@ close(drv->sock); if (drv->sock_xmit != NULL) l2_packet_deinit(drv->sock_xmit); + os_free(drv->event_buf); os_free(drv); } + +static int +bsd_commit(void *priv) +{ + return bsd_ctrl_iface(priv, 1); +} + + +static int +bsd_set_sta_authorized(void *priv, const u8 *addr, + int total_flags, int flags_or, int flags_and) +{ + int authorized = -1; + + /* For now, only support setting Authorized flag */ + if (flags_or & WPA_STA_AUTHORIZED) + authorized = 1; + if (!(flags_and & WPA_STA_AUTHORIZED)) + authorized = 0; + + if (authorized < 0) + return 0; + + return bsd_send_mlme_param(priv, authorized ? + IEEE80211_MLME_AUTHORIZE : + IEEE80211_MLME_UNAUTHORIZE, 0, addr); +} #else /* HOSTAPD */ static int @@ -979,13 +999,6 @@ } static int -wpa_driver_bsd_disassociate(void *priv, const u8 *addr, int reason_code) -{ - return bsd_send_mlme_param(priv, IEEE80211_MLME_DISASSOC, reason_code, - addr); -} - -static int wpa_driver_bsd_set_auth_alg(void *priv, int auth_alg) { int authmode; @@ -1066,9 +1079,9 @@ if (wpa_driver_bsd_set_wpa_ie(drv, params->wpa_ie, params->wpa_ie_len) < 0) return -1; - privacy = !(params->pairwise_suite == CIPHER_NONE && - params->group_suite == CIPHER_NONE && - params->key_mgmt_suite == KEY_MGMT_NONE && + privacy = !(params->pairwise_suite == WPA_CIPHER_NONE && + params->group_suite == WPA_CIPHER_NONE && + params->key_mgmt_suite == WPA_KEY_MGMT_NONE && params->wpa_ie_len == 0); wpa_printf(MSG_DEBUG, "%s: set PRIVACY %u", __func__, privacy); @@ -1164,7 +1177,6 @@ wpa_driver_bsd_event_receive(int sock, void *ctx, void *sock_ctx) { struct bsd_driver_data *drv = sock_ctx; - char *buf; struct if_announcemsghdr *ifan; struct if_msghdr *ifm; struct rt_msghdr *rtm; @@ -1172,30 +1184,20 @@ struct ieee80211_michael_event *mic; struct ieee80211_leave_event *leave; struct ieee80211_join_event *join; - int n, len; - - len = rtbuf_len(); + int n; - buf = os_malloc(len); - if (buf == NULL) { - wpa_printf(MSG_ERROR, "%s os_malloc() failed\n", __func__); - return; - } - - n = read(sock, buf, len); + n = read(sock, drv->event_buf, drv->event_buf_len); if (n < 0) { if (errno != EINTR && errno != EAGAIN) wpa_printf(MSG_ERROR, "%s read() failed: %s\n", __func__, strerror(errno)); - os_free(buf); return; } - rtm = (struct rt_msghdr *) buf; + rtm = (struct rt_msghdr *) drv->event_buf; if (rtm->rtm_version != RTM_VERSION) { wpa_printf(MSG_DEBUG, "Invalid routing message version=%d", rtm->rtm_version); - os_free(buf); return; } os_memset(&event, 0, sizeof(event)); @@ -1210,7 +1212,6 @@ case IFAN_DEPARTURE: event.interface_status.ievent = EVENT_INTERFACE_REMOVED; default: - os_free(buf); return; } wpa_printf(MSG_DEBUG, "RTM_IFANNOUNCE: Interface '%s' %s", @@ -1283,7 +1284,6 @@ } break; } - os_free(buf); } static void @@ -1308,6 +1308,11 @@ result->caps = sr->isr_capinfo; result->qual = sr->isr_rssi; result->noise = sr->isr_noise; + /* + * the rssi value reported by the kernel is in 0.5dB steps relative to + * the reported noise floor. see ieee80211_node.h for details. + */ + result->level = sr->isr_rssi / 2 + sr->isr_noise; pos = (u8 *)(result + 1); @@ -1334,8 +1339,8 @@ result->ie_len = pos - (u8 *)(result + 1); - tmp = os_realloc(res->res, - (res->num + 1) * sizeof(struct wpa_scan_res *)); + tmp = os_realloc_array(res->res, res->num + 1, + sizeof(struct wpa_scan_res *)); if (tmp == NULL) { os_free(result); return; @@ -1449,6 +1454,33 @@ return 0; } +static enum ieee80211_opmode +get80211opmode(struct bsd_driver_data *drv) +{ + struct ifmediareq ifmr; + + (void) memset(&ifmr, 0, sizeof(ifmr)); + (void) os_strlcpy(ifmr.ifm_name, drv->ifname, sizeof(ifmr.ifm_name)); + + if (ioctl(drv->sock, SIOCGIFMEDIA, (caddr_t)&ifmr) >= 0) { + if (ifmr.ifm_current & IFM_IEEE80211_ADHOC) { + if (ifmr.ifm_current & IFM_FLAG0) + return IEEE80211_M_AHDEMO; + else + return IEEE80211_M_IBSS; + } + if (ifmr.ifm_current & IFM_IEEE80211_HOSTAP) + return IEEE80211_M_HOSTAP; + if (ifmr.ifm_current & IFM_IEEE80211_MONITOR) + return IEEE80211_M_MONITOR; +#ifdef IEEE80211_M_MBSS + if (ifmr.ifm_current & IFM_IEEE80211_MBSS) + return IEEE80211_M_MBSS; +#endif /* IEEE80211_M_MBSS */ + } + return IEEE80211_M_STA; +} + static void * wpa_driver_bsd_init(void *ctx, const char *ifname) { @@ -1459,6 +1491,15 @@ drv = os_zalloc(sizeof(*drv)); if (drv == NULL) return NULL; + + drv->event_buf_len = rtbuf_len(); + + drv->event_buf = os_malloc(drv->event_buf_len); + if (drv->event_buf == NULL) { + wpa_printf(MSG_ERROR, "%s: os_malloc() failed", __func__); + goto fail1; + } + /* * NB: We require the interface name be mappable to an index. * This implies we do not support having wpa_supplicant @@ -1474,6 +1515,12 @@ drv->sock = socket(PF_INET, SOCK_DGRAM, 0); if (drv->sock < 0) goto fail1; + + os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname)); + /* Down interface during setup. */ + if (bsd_ctrl_iface(drv, 0) < 0) + goto fail; + drv->route = socket(PF_ROUTE, SOCK_RAW, 0); if (drv->route < 0) goto fail; @@ -1481,11 +1528,6 @@ wpa_driver_bsd_event_receive, ctx, drv); drv->ctx = ctx; - os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname)); - - /* Down interface during setup. */ - if (bsd_ctrl_iface(drv, 0) < 0) - goto fail; if (!GETPARAM(drv, IEEE80211_IOC_ROAMING, drv->prev_roaming)) { wpa_printf(MSG_DEBUG, "%s: failed to get roaming state: %s", @@ -1506,10 +1548,13 @@ if (wpa_driver_bsd_capa(drv)) goto fail; + drv->opmode = get80211opmode(drv); + return drv; fail: close(drv->sock); fail1: + os_free(drv->event_buf); os_free(drv); return NULL; #undef GETPARAM @@ -1535,6 +1580,7 @@ l2_packet_deinit(drv->sock_xmit); (void) close(drv->route); /* ioctl socket */ (void) close(drv->sock); /* event socket */ + os_free(drv->event_buf); os_free(drv); } @@ -1561,6 +1607,8 @@ .read_sta_data = bsd_read_sta_driver_data, .sta_disassoc = bsd_sta_disassoc, .sta_deauth = bsd_sta_deauth, + .sta_set_flags = bsd_set_sta_authorized, + .commit = bsd_commit, #else /* HOSTAPD */ .init = wpa_driver_bsd_init, .deinit = wpa_driver_bsd_deinit, @@ -1570,7 +1618,6 @@ .scan2 = wpa_driver_bsd_scan, .get_scan_results2 = wpa_driver_bsd_get_scan_results2, .deauthenticate = wpa_driver_bsd_deauthenticate, - .disassociate = wpa_driver_bsd_disassociate, .associate = wpa_driver_bsd_associate, .get_capa = wpa_driver_bsd_get_capa, #endif /* HOSTAPD */ @@ -1580,6 +1627,5 @@ .hapd_set_ssid = bsd_set_ssid, .hapd_get_ssid = bsd_get_ssid, .hapd_send_eapol = bsd_send_eapol, - .sta_set_flags = bsd_set_sta_authorized, .set_generic_elem = bsd_set_opt_ie, }; diff -Nru wpa-1.0/src/drivers/driver_common.c wpa-2.1/src/drivers/driver_common.c --- wpa-1.0/src/drivers/driver_common.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/drivers/driver_common.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * Common driver-related functions * Copyright (c) 2003-2011, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -55,7 +49,6 @@ E2S(TX_STATUS); E2S(RX_FROM_UNKNOWN); E2S(RX_MGMT); - E2S(RX_ACTION); E2S(REMAIN_ON_CHANNEL); E2S(CANCEL_REMAIN_ON_CHANNEL); E2S(MLME_RX); @@ -71,17 +64,21 @@ E2S(UNPROT_DEAUTH); E2S(UNPROT_DISASSOC); E2S(STATION_LOW_ACK); - E2S(P2P_DEV_FOUND); - E2S(P2P_GO_NEG_REQ_RX); - E2S(P2P_GO_NEG_COMPLETED); - E2S(P2P_PROV_DISC_REQUEST); - E2S(P2P_PROV_DISC_RESPONSE); - E2S(P2P_SD_REQUEST); - E2S(P2P_SD_RESPONSE); E2S(IBSS_PEER_LOST); E2S(DRIVER_GTK_REKEY); E2S(SCHED_SCAN_STOPPED); E2S(DRIVER_CLIENT_POLL_OK); + E2S(EAPOL_TX_STATUS); + E2S(CH_SWITCH); + E2S(WNM); + E2S(CONNECT_FAILED_REASON); + E2S(DFS_RADAR_DETECTED); + E2S(DFS_CAC_FINISHED); + E2S(DFS_CAC_ABORTED); + E2S(DFS_NOP_FINISHED); + E2S(SURVEY); + E2S(SCAN_STARTED); + E2S(AVOID_FREQUENCIES); } return "UNKNOWN"; diff -Nru wpa-1.0/src/drivers/driver.h wpa-2.1/src/drivers/driver.h --- wpa-1.0/src/drivers/driver.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/drivers/driver.h 2014-02-04 11:23:35.000000000 +0000 @@ -1,15 +1,9 @@ /* * Driver interface definition - * Copyright (c) 2003-2010, Jouni Malinen + * Copyright (c) 2003-2014, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. * * This file defines a driver interface used by both %wpa_supplicant and * hostapd. The first part of the file defines data structures used in various @@ -26,6 +20,7 @@ #define WPA_SUPPLICANT_DRIVER_VERSION 4 #include "common/defs.h" +#include "utils/list.h" #define HOSTAPD_CHAN_DISABLED 0x00000001 #define HOSTAPD_CHAN_PASSIVE_SCAN 0x00000002 @@ -34,6 +29,26 @@ #define HOSTAPD_CHAN_HT40PLUS 0x00000010 #define HOSTAPD_CHAN_HT40MINUS 0x00000020 #define HOSTAPD_CHAN_HT40 0x00000040 +#define HOSTAPD_CHAN_SURVEY_LIST_INITIALIZED 0x00000080 + +#define HOSTAPD_CHAN_DFS_UNKNOWN 0x00000000 +#define HOSTAPD_CHAN_DFS_USABLE 0x00000100 +#define HOSTAPD_CHAN_DFS_UNAVAILABLE 0x00000200 +#define HOSTAPD_CHAN_DFS_AVAILABLE 0x00000300 +#define HOSTAPD_CHAN_DFS_MASK 0x00000300 + +#define HOSTAPD_CHAN_VHT_10_70 0x00000800 +#define HOSTAPD_CHAN_VHT_30_50 0x00001000 +#define HOSTAPD_CHAN_VHT_50_30 0x00002000 +#define HOSTAPD_CHAN_VHT_70_10 0x00004000 + +enum reg_change_initiator { + REGDOM_SET_BY_CORE, + REGDOM_SET_BY_USER, + REGDOM_SET_BY_DRIVER, + REGDOM_SET_BY_COUNTRY_IE, + REGDOM_BEACON_HINT, +}; /** * struct hostapd_channel_data - Channel information @@ -47,7 +62,7 @@ /** * freq - Frequency in MHz */ - short freq; + int freq; /** * flag - Channel flags (HOSTAPD_CHAN_*) @@ -55,12 +70,33 @@ int flag; /** - * max_tx_power - maximum transmit power in dBm + * max_tx_power - Regulatory transmit power limit in dBm */ u8 max_tx_power; + + /* + * survey_list - Linked list of surveys + */ + struct dl_list survey_list; + + /** + * min_nf - Minimum observed noise floor, in dBm, based on all + * surveyed channel data + */ + s8 min_nf; + +#ifdef CONFIG_ACS + /** + * interference_factor - Computed interference factor on this + * channel (used internally in src/ap/acs.c; driver wrappers do not + * need to set this) + */ + long double interference_factor; +#endif /* CONFIG_ACS */ }; #define HOSTAPD_MODE_FLAG_HT_INFO_KNOWN BIT(0) +#define HOSTAPD_MODE_FLAG_VHT_INFO_KNOWN BIT(1) /** * struct hostapd_hw_modes - Supported hardware mode information @@ -106,6 +142,16 @@ */ u8 a_mpdu_params; + /** + * vht_capab - VHT (IEEE 802.11ac) capabilities + */ + u32 vht_capab; + + /** + * vht_mcs_set - VHT MCS (IEEE 802.11ac) rate parameters + */ + u8 vht_mcs_set[8]; + unsigned int flags; /* HOSTAPD_MODE_FLAG_* */ }; @@ -118,6 +164,13 @@ #define IEEE80211_CAP_IBSS 0x0002 #define IEEE80211_CAP_PRIVACY 0x0010 +/* DMG (60 GHz) IEEE 802.11ad */ +/* type - bits 0..1 */ +#define IEEE80211_CAP_DMG_MASK 0x0003 +#define IEEE80211_CAP_DMG_IBSS 0x0001 /* Tx by: STA */ +#define IEEE80211_CAP_DMG_PBSS 0x0002 /* Tx by: PCP */ +#define IEEE80211_CAP_DMG_AP 0x0003 /* Tx by: AP */ + #define WPA_SCAN_QUAL_INVALID BIT(0) #define WPA_SCAN_NOISE_INVALID BIT(1) #define WPA_SCAN_LEVEL_INVALID BIT(2) @@ -176,10 +229,12 @@ * struct wpa_scan_results - Scan results * @res: Array of pointers to allocated variable length scan result entries * @num: Number of entries in the scan result array + * @fetch_time: Time when the results were fetched from the driver */ struct wpa_scan_results { struct wpa_scan_res **res; size_t num; + struct os_reltime fetch_time; }; /** @@ -270,6 +325,15 @@ size_t num_filter_ssids; /** + * filter_rssi - Filter by RSSI + * + * The driver may filter scan results in firmware to reduce host + * wakeups and thereby save power. Specify the RSSI threshold in s32 + * dBm. + */ + s32 filter_rssi; + + /** * p2p_probe - Used to disable CCK (802.11b) rates for P2P probes * * When set, the driver is expected to remove rates 1, 2, 5.5, and 11 @@ -277,6 +341,21 @@ * and not to transmit the frames at any of those rates. */ u8 p2p_probe; + + /** + * only_new_results - Request driver to report only new results + * + * This is used to request the driver to report only BSSes that have + * been detected after this scan request has been started, i.e., to + * flush old cached BSS entries. + */ + int only_new_results; + + /* + * NOTE: Whenever adding new parameters here, please make sure + * wpa_scan_clone_params() and wpa_scan_free_params() get updated with + * matching changes. + */ }; /** @@ -301,6 +380,9 @@ */ int p2p; + const u8 *sae_data; + size_t sae_data_len; + }; enum wps_mode { @@ -339,6 +421,13 @@ int freq; /** + * bg_scan_period - Background scan period in seconds, 0 to disable + * background scan, or -1 to indicate no change to default driver + * configuration + */ + int bg_scan_period; + + /** * wpa_ie - WPA information element for (Re)Association Request * WPA information element to be included in (Re)Association * Request (including information element id and length). Use @@ -369,25 +458,25 @@ unsigned int wpa_proto; /** - * pairwise_suite - Selected pairwise cipher suite + * pairwise_suite - Selected pairwise cipher suite (WPA_CIPHER_*) * * This is usually ignored if @wpa_ie is used. */ - enum wpa_cipher pairwise_suite; + unsigned int pairwise_suite; /** - * group_suite - Selected group cipher suite + * group_suite - Selected group cipher suite (WPA_CIPHER_*) * * This is usually ignored if @wpa_ie is used. */ - enum wpa_cipher group_suite; + unsigned int group_suite; /** - * key_mgmt_suite - Selected key management suite + * key_mgmt_suite - Selected key management suite (WPA_KEY_MGMT_*) * * This is usually ignored if @wpa_ie is used. */ - enum wpa_key_mgmt key_mgmt_suite; + unsigned int key_mgmt_suite; /** * auth_alg - Allowed authentication algorithms @@ -516,6 +605,39 @@ * STA mode: bits 0..3 UAPSD enabled for VO,VI,BK,BE */ int uapsd; + + /** + * fixed_bssid - Whether to force this BSSID in IBSS mode + * 1 = Fix this BSSID and prevent merges. + * 0 = Do not fix BSSID. + */ + int fixed_bssid; + + /** + * disable_ht - Disable HT (IEEE 802.11n) for this connection + */ + int disable_ht; + + /** + * HT Capabilities over-rides. Only bits set in the mask will be used, + * and not all values are used by the kernel anyway. Currently, MCS, + * MPDU and MSDU fields are used. + */ + const u8 *htcaps; /* struct ieee80211_ht_capabilities * */ + const u8 *htcaps_mask; /* struct ieee80211_ht_capabilities * */ + +#ifdef CONFIG_VHT_OVERRIDES + /** + * disable_vht - Disable VHT for this connection + */ + int disable_vht; + + /** + * VHT capability overrides. + */ + const struct ieee80211_vht_capabilities *vhtcaps; + const struct ieee80211_vht_capabilities *vhtcaps_mask; +#endif /* CONFIG_VHT_OVERRIDES */ }; enum hide_ssid { @@ -528,7 +650,7 @@ /** * head - Beacon head from IEEE 802.11 header to IEs before TIM IE */ - const u8 *head; + u8 *head; /** * head_len - Length of the head buffer in octets @@ -538,7 +660,7 @@ /** * tail - Beacon tail following TIM IE */ - const u8 *tail; + u8 *tail; /** * tail_len - Length of the tail buffer in octets @@ -556,6 +678,27 @@ int beacon_int; /** + * basic_rates: -1 terminated array of basic rates in 100 kbps + * + * This parameter can be used to set a specific basic rate set for the + * BSS. If %NULL, default basic rate set is used. + */ + int *basic_rates; + + /** + * proberesp - Probe Response template + * + * This is used by drivers that reply to Probe Requests internally in + * AP mode and require the full Probe Response template. + */ + u8 *proberesp; + + /** + * proberesp_len - Length of the proberesp buffer in octets + */ + size_t proberesp_len; + + /** * ssid - The SSID to use in Beacon/Probe Response frames */ const u8 *ssid; @@ -628,7 +771,7 @@ * isolate - Whether to isolate frames between associated stations * * If this is non-zero, the AP is requested to disable forwarding of - * frames between association stations. + * frames between associated stations. */ int isolate; @@ -672,6 +815,18 @@ * enabled. */ u8 access_network_type; + + /** + * ap_max_inactivity - Timeout in seconds to detect STA's inactivity + * + * This is used by driver which advertises this capability. + */ + int ap_max_inactivity; + + /** + * disable_dgaf - Whether group-addressed frames are disabled + */ + int disable_dgaf; }; /** @@ -685,12 +840,21 @@ #define WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE 0x00000010 #define WPA_DRIVER_CAPA_KEY_MGMT_FT 0x00000020 #define WPA_DRIVER_CAPA_KEY_MGMT_FT_PSK 0x00000040 +#define WPA_DRIVER_CAPA_KEY_MGMT_WAPI_PSK 0x00000080 unsigned int key_mgmt; #define WPA_DRIVER_CAPA_ENC_WEP40 0x00000001 #define WPA_DRIVER_CAPA_ENC_WEP104 0x00000002 #define WPA_DRIVER_CAPA_ENC_TKIP 0x00000004 #define WPA_DRIVER_CAPA_ENC_CCMP 0x00000008 +#define WPA_DRIVER_CAPA_ENC_WEP128 0x00000010 +#define WPA_DRIVER_CAPA_ENC_GCMP 0x00000020 +#define WPA_DRIVER_CAPA_ENC_GCMP_256 0x00000040 +#define WPA_DRIVER_CAPA_ENC_CCMP_256 0x00000080 +#define WPA_DRIVER_CAPA_ENC_BIP 0x00000100 +#define WPA_DRIVER_CAPA_ENC_BIP_GMAC_128 0x00000200 +#define WPA_DRIVER_CAPA_ENC_BIP_GMAC_256 0x00000400 +#define WPA_DRIVER_CAPA_ENC_BIP_CMAC_256 0x00000800 unsigned int enc; #define WPA_DRIVER_AUTH_OPEN 0x00000001 @@ -714,8 +878,7 @@ #define WPA_DRIVER_FLAGS_AP 0x00000040 /* Driver needs static WEP key setup after association has been completed */ #define WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC_DONE 0x00000080 -/* Driver takes care of P2P management operations */ -#define WPA_DRIVER_FLAGS_P2P_MGMT 0x00000100 +/* unused: 0x00000100 */ /* Driver supports concurrent P2P operations */ #define WPA_DRIVER_FLAGS_P2P_CONCURRENT 0x00000200 /* @@ -723,10 +886,9 @@ * it cannot be used for P2P group operations or non-P2P purposes. */ #define WPA_DRIVER_FLAGS_P2P_DEDICATED_INTERFACE 0x00000400 -/* This interface is P2P capable (P2P Device, GO, or P2P Client */ +/* This interface is P2P capable (P2P GO or P2P Client) */ #define WPA_DRIVER_FLAGS_P2P_CAPABLE 0x00000800 -/* Driver supports concurrent operations on multiple channels */ -#define WPA_DRIVER_FLAGS_MULTI_CHANNEL_CONCURRENT 0x00001000 +/* unused: 0x00001000 */ /* * Driver uses the initial interface for P2P management interface and non-P2P * purposes (e.g., connect to infra AP), but this interface cannot be used for @@ -751,6 +913,28 @@ #define WPA_DRIVER_FLAGS_TDLS_SUPPORT 0x00080000 /* Driver requires external TDLS setup/teardown/discovery */ #define WPA_DRIVER_FLAGS_TDLS_EXTERNAL_SETUP 0x00100000 +/* Driver indicates support for Probe Response offloading in AP mode */ +#define WPA_DRIVER_FLAGS_PROBE_RESP_OFFLOAD 0x00200000 +/* Driver supports U-APSD in AP mode */ +#define WPA_DRIVER_FLAGS_AP_UAPSD 0x00400000 +/* Driver supports inactivity timer in AP mode */ +#define WPA_DRIVER_FLAGS_INACTIVITY_TIMER 0x00800000 +/* Driver expects user space implementation of MLME in AP mode */ +#define WPA_DRIVER_FLAGS_AP_MLME 0x01000000 +/* Driver supports SAE with user space SME */ +#define WPA_DRIVER_FLAGS_SAE 0x02000000 +/* Driver makes use of OBSS scan mechanism in wpa_supplicant */ +#define WPA_DRIVER_FLAGS_OBSS_SCAN 0x04000000 +/* Driver supports IBSS (Ad-hoc) mode */ +#define WPA_DRIVER_FLAGS_IBSS 0x08000000 +/* Driver supports radar detection */ +#define WPA_DRIVER_FLAGS_RADAR 0x10000000 +/* Driver supports a dedicated interface for P2P Device */ +#define WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE 0x20000000 +/* Driver supports QoS Mapping */ +#define WPA_DRIVER_FLAGS_QOS_MAPPING 0x40000000 +/* Driver supports CSA in AP mode */ +#define WPA_DRIVER_FLAGS_AP_CSA 0x80000000 unsigned int flags; int max_scan_ssids; @@ -768,6 +952,36 @@ * supports in AP mode */ unsigned int max_stations; + + /** + * probe_resp_offloads - Bitmap of supported protocols by the driver + * for Probe Response offloading. + */ +/* Driver Probe Response offloading support for WPS ver. 1 */ +#define WPA_DRIVER_PROBE_RESP_OFFLOAD_WPS 0x00000001 +/* Driver Probe Response offloading support for WPS ver. 2 */ +#define WPA_DRIVER_PROBE_RESP_OFFLOAD_WPS2 0x00000002 +/* Driver Probe Response offloading support for P2P */ +#define WPA_DRIVER_PROBE_RESP_OFFLOAD_P2P 0x00000004 +/* Driver Probe Response offloading support for IEEE 802.11u (Interworking) */ +#define WPA_DRIVER_PROBE_RESP_OFFLOAD_INTERWORKING 0x00000008 + unsigned int probe_resp_offloads; + + unsigned int max_acl_mac_addrs; + + /** + * Number of supported concurrent channels + */ + unsigned int num_multichan_concurrent; + + /** + * extended_capa - extended capabilities in driver/device + * + * Must be allocated and freed by driver and the pointers must be + * valid for the lifetime of the driver, i.e., freed in deinit() + */ + const u8 *extended_capa, *extended_capa_mask; + unsigned int extended_capa_len; }; @@ -793,18 +1007,45 @@ size_t supp_rates_len; u16 listen_interval; const struct ieee80211_ht_capabilities *ht_capabilities; + const struct ieee80211_vht_capabilities *vht_capabilities; u32 flags; /* bitmask of WPA_STA_* flags */ int set; /* Set STA parameters instead of add */ + u8 qosinfo; + const u8 *ext_capab; + size_t ext_capab_len; + const u8 *supp_channels; + size_t supp_channels_len; + const u8 *supp_oper_classes; + size_t supp_oper_classes_len; }; struct hostapd_freq_params { int mode; int freq; int channel; + /* for HT */ int ht_enabled; int sec_channel_offset; /* 0 = HT40 disabled, -1 = HT40 enabled, * secondary channel below primary, 1 = HT40 * enabled, secondary channel above primary */ + + /* for VHT */ + int vht_enabled; + + /* valid for both HT and VHT, center_freq2 is non-zero + * only for bandwidth 80 and an 80+80 channel */ + int center_freq1, center_freq2; + int bandwidth; +}; + +struct mac_address { + u8 addr[ETH_ALEN]; +}; + +struct hostapd_acl_params { + u8 acl_policy; + unsigned int num_mac_acl; + struct mac_address mac_acl[0]; }; enum wpa_driver_if_type { @@ -842,7 +1083,13 @@ * WPA_IF_P2P_GROUP - P2P Group interface (will become either * WPA_IF_P2P_GO or WPA_IF_P2P_CLIENT, but the role is not yet known) */ - WPA_IF_P2P_GROUP + WPA_IF_P2P_GROUP, + + /** + * WPA_IF_P2P_DEVICE - P2P Device interface is used to indentify the + * abstracted P2P Device function in the driver + */ + WPA_IF_P2P_DEVICE }; struct wpa_init_params { @@ -881,17 +1128,6 @@ #define WPA_STA_MFP BIT(3) #define WPA_STA_TDLS_PEER BIT(4) -/** - * struct p2p_params - P2P parameters for driver-based P2P management - */ -struct p2p_params { - const char *dev_name; - u8 pri_dev_type[8]; -#define DRV_MAX_SEC_DEV_TYPES 5 - u8 sec_dev_type[DRV_MAX_SEC_DEV_TYPES][8]; - size_t num_sec_dev_types; -}; - enum tdls_oper { TDLS_DISCOVERY_REQ, TDLS_SETUP, @@ -902,6 +1138,34 @@ TDLS_DISABLE }; +enum wnm_oper { + WNM_SLEEP_ENTER_CONFIRM, + WNM_SLEEP_ENTER_FAIL, + WNM_SLEEP_EXIT_CONFIRM, + WNM_SLEEP_EXIT_FAIL, + WNM_SLEEP_TFS_REQ_IE_ADD, /* STA requests driver to add TFS req IE */ + WNM_SLEEP_TFS_REQ_IE_NONE, /* STA requests empty TFS req IE */ + WNM_SLEEP_TFS_REQ_IE_SET, /* AP requests driver to set TFS req IE for + * a STA */ + WNM_SLEEP_TFS_RESP_IE_ADD, /* AP requests driver to add TFS resp IE + * for a STA */ + WNM_SLEEP_TFS_RESP_IE_NONE, /* AP requests empty TFS resp IE */ + WNM_SLEEP_TFS_RESP_IE_SET, /* AP requests driver to set TFS resp IE + * for a STA */ + WNM_SLEEP_TFS_IE_DEL /* AP delete the TFS IE */ +}; + +/* 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 +}; + /** * struct wpa_signal_info - Information about channel signal quality */ @@ -909,8 +1173,65 @@ u32 frequency; int above_threshold; int current_signal; + int avg_signal; int current_noise; int current_txrate; + enum chan_width chanwidth; + int center_frq1; + int center_frq2; +}; + +/** + * struct beacon_data - Beacon data + * @head: Head portion of Beacon frame (before TIM IE) + * @tail: Tail portion of Beacon frame (after TIM IE) + * @beacon_ies: Extra information element(s) to add into Beacon frames or %NULL + * @proberesp_ies: Extra information element(s) to add into Probe Response + * frames or %NULL + * @assocresp_ies: Extra information element(s) to add into (Re)Association + * Response frames or %NULL + * @probe_resp: Probe Response frame template + * @head_len: Length of @head + * @tail_len: Length of @tail + * @beacon_ies_len: Length of beacon_ies in octets + * @proberesp_ies_len: Length of proberesp_ies in octets + * @proberesp_ies_len: Length of proberesp_ies in octets + * @probe_resp_len: Length of probe response template (@probe_resp) + */ +struct beacon_data { + u8 *head, *tail; + u8 *beacon_ies; + u8 *proberesp_ies; + u8 *assocresp_ies; + u8 *probe_resp; + + size_t head_len, tail_len; + size_t beacon_ies_len; + size_t proberesp_ies_len; + size_t assocresp_ies_len; + size_t probe_resp_len; +}; + +/** + * struct csa_settings - Settings for channel switch command + * @cs_count: Count in Beacon frames (TBTT) to perform the switch + * @block_tx: 1 - block transmission for CSA period + * @freq_params: Next channel frequency parameter + * @beacon_csa: Beacon/probe resp/asooc resp info for CSA period + * @beacon_after: Next beacon/probe resp/asooc resp info + * @counter_offset_beacon: Offset to the count field in beacon's tail + * @counter_offset_presp: Offset to the count field in probe resp. + */ +struct csa_settings { + u8 cs_count; + u8 block_tx; + + struct hostapd_freq_params freq_params; + struct beacon_data beacon_csa; + struct beacon_data beacon_after; + + u16 counter_offset_beacon; + u16 counter_offset_presp; }; /** @@ -961,7 +1282,10 @@ * @ifname: Interface name (for multi-SSID/VLAN support) * @priv: private driver interface data * @alg: encryption algorithm (%WPA_ALG_NONE, %WPA_ALG_WEP, - * %WPA_ALG_TKIP, %WPA_ALG_CCMP, %WPA_ALG_IGTK, %WPA_ALG_PMK); + * %WPA_ALG_TKIP, %WPA_ALG_CCMP, %WPA_ALG_IGTK, %WPA_ALG_PMK, + * %WPA_ALG_GCMP, %WPA_ALG_GCMP_256, %WPA_ALG_CCMP_256, + * %WPA_ALG_BIP_GMAC_128, %WPA_ALG_BIP_GMAC_256, + * %WPA_ALG_BIP_CMAC_256); * %WPA_ALG_NONE clears the key. * @addr: Address of the peer STA (BSSID of the current AP when setting * pairwise key in station mode), ff:ff:ff:ff:ff:ff for @@ -978,11 +1302,11 @@ * for Rx keys (in most cases, this is only used with broadcast * keys and set to zero for unicast keys); %NULL if not set * @seq_len: length of the seq, depends on the algorithm: - * TKIP: 6 octets, CCMP: 6 octets, IGTK: 6 octets + * TKIP: 6 octets, CCMP/GCMP: 6 octets, IGTK: 6 octets * @key: key buffer; TKIP: 16-byte temporal key, 8-byte Tx Mic key, * 8-byte Rx Mic Key * @key_len: length of the key buffer in octets (WEP: 5 or 13, - * TKIP: 32, CCMP: 16, IGTK: 16) + * TKIP: 32, CCMP/GCMP: 16, IGTK: 16) * * Returns: 0 on success, -1 on failure * @@ -1079,17 +1403,6 @@ int (*deauthenticate)(void *priv, const u8 *addr, int reason_code); /** - * disassociate - Request driver to disassociate - * @priv: private driver interface data - * @addr: peer address (BSSID of the AP) - * @reason_code: 16-bit reason code to be sent in the disassociation - * frame - * - * Returns: 0 on success, -1 on failure - */ - int (*disassociate)(void *priv, const u8 *addr, int reason_code); - - /** * associate - Request driver to associate * @priv: private driver interface data * @params: association parameters @@ -1276,9 +1589,11 @@ * @priv: Private driver interface data * @data: IEEE 802.11 management frame with IEEE 802.11 header * @data_len: Size of the management frame + * @noack: Do not wait for this frame to be acked (disable retries) * Returns: 0 on success, -1 on failure */ - int (*send_mlme)(void *priv, const u8 *data, size_t data_len); + int (*send_mlme)(void *priv, const u8 *data, size_t data_len, + int noack); /** * update_ft_ies - Update FT (IEEE 802.11r) IEs @@ -1335,6 +1650,14 @@ int (*set_country)(void *priv, const char *alpha2); /** + * get_country - Get country + * @priv: Private driver interface data + * @alpha2: Buffer for returning country code (at least 3 octets) + * Returns: 0 on success, -1 on failure + */ + int (*get_country)(void *priv, char *alpha2); + + /** * global_init - Global driver initialization * Returns: Pointer to private data (global), %NULL on failure * @@ -1428,6 +1751,16 @@ int (*set_ap)(void *priv, struct wpa_driver_ap_params *params); /** + * set_acl - Set ACL in AP mode + * @priv: Private driver interface data + * @params: Parameters to configure ACL + * Returns: 0 on success, -1 on failure + * + * This is used only for the drivers which support MAC address ACL. + */ + int (*set_acl)(void *priv, struct hostapd_acl_params *params); + + /** * hapd_init - Initialize driver interface (hostapd only) * @hapd: Pointer to hostapd context * @params: Configuration for the driver wrapper @@ -1485,9 +1818,9 @@ * Returns: 0 on success, -1 on failure * * This function is used to fetch the last used TSC/packet number for - * a TKIP, CCMP, or BIP/IGTK key. It is mainly used with group keys, so - * there is no strict requirement on implementing support for unicast - * keys (i.e., addr != %NULL). + * a TKIP, CCMP, GCMP, or BIP/IGTK key. It is mainly used with group + * keys, so there is no strict requirement on implementing support for + * unicast keys (i.e., addr != %NULL). */ int (*get_seqnum)(const char *ifname, void *priv, const u8 *addr, int idx, u8 *seq); @@ -1520,7 +1853,7 @@ int (*set_generic_elem)(void *priv, const u8 *elem, size_t elem_len); /** - * read_sta_data - Fetch station data (AP only) + * read_sta_data - Fetch station data * @priv: Private driver interface data * @data: Buffer for returning station information * @addr: MAC address of the station @@ -1683,17 +2016,6 @@ int total_flags, int flags_or, int flags_and); /** - * set_rate_sets - Set supported and basic rate sets (AP only) - * @priv: Private driver interface data - * @supp_rates: -1 terminated array of supported rates in 100 kbps - * @basic_rates: -1 terminated array of basic rates in 100 kbps - * @mode: hardware mode (HOSTAPD_MODE_*) - * Returns: 0 on success, -1 on failure - */ - int (*set_rate_sets)(void *priv, int *supp_rates, int *basic_rates, - int mode); - - /** * set_tx_queue_params - Set TX queue parameters * @priv: Private driver interface data * @queue: Queue number (0 = VO, 1 = VI, 2 = BE, 3 = BK) @@ -1721,12 +2043,13 @@ * (this may differ from the requested addr if the driver cannot * change interface address) * @bridge: Bridge interface to use or %NULL if no bridge configured + * @use_existing: Whether to allow existing interface to be used * Returns: 0 on success, -1 on failure */ int (*if_add)(void *priv, enum wpa_driver_if_type type, const char *ifname, const u8 *addr, void *bss_ctx, void **drv_priv, char *force_ifname, u8 *if_addr, - const char *bridge); + const char *bridge, int use_existing); /** * if_remove - Remove a virtual interface @@ -1847,10 +2170,12 @@ * @val: 1 = bind to 4-address WDS; 0 = unbind * @bridge_ifname: Bridge interface to use for the WDS station or %NULL * to indicate that bridge is not to be used + * @ifname_wds: Buffer to return the interface name for the new WDS + * station or %NULL to indicate name is not returned. * Returns: 0 on success, -1 on failure */ int (*set_wds_sta)(void *priv, const u8 *addr, int aid, int val, - const char *bridge_ifname); + const char *bridge_ifname, char *ifname_wds); /** * send_action - Transmit an Action frame @@ -1901,7 +2226,7 @@ * * This command is used to request the driver to remain awake on the * specified channel for the specified duration and report received - * Action frames with EVENT_RX_ACTION events. Optionally, received + * Action frames with EVENT_RX_MGMT events. Optionally, received * Probe Request frames may also be requested to be reported by calling * probe_req_report(). These will be reported with EVENT_RX_PROBE_REQ. * @@ -1952,8 +2277,9 @@ * Returns: 0 on success, -1 on failure (or if not supported) * * This optional function can be used to disable AP mode related - * configuration and change the driver mode to station mode to allow - * normal station operations like scanning to be completed. + * configuration. If the interface was not dynamically added, + * change the driver mode to station mode to allow normal station + * operations like scanning to be completed. */ int (*deinit_ap)(void *priv); @@ -1962,8 +2288,9 @@ * @priv: Private driver interface data * Returns: 0 on success, -1 on failure (or if not supported) * - * This optional function can be used to disable P2P client mode. It - * can be used to change the interface type back to station mode. + * This optional function can be used to disable P2P client mode. If the + * interface was not dynamically added, change the interface type back + * to station mode. */ int (*deinit_p2p_cli)(void *priv); @@ -2081,222 +2408,6 @@ const char * (*get_radio_name)(void *priv); /** - * p2p_find - Start P2P Device Discovery - * @priv: Private driver interface data - * @timeout: Timeout for find operation in seconds or 0 for no timeout - * @type: Device Discovery type (enum p2p_discovery_type) - * Returns: 0 on success, -1 on failure - * - * This function is only used if the driver implements P2P management, - * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in - * struct wpa_driver_capa. - */ - int (*p2p_find)(void *priv, unsigned int timeout, int type); - - /** - * p2p_stop_find - Stop P2P Device Discovery - * @priv: Private driver interface data - * Returns: 0 on success, -1 on failure - * - * This function is only used if the driver implements P2P management, - * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in - * struct wpa_driver_capa. - */ - int (*p2p_stop_find)(void *priv); - - /** - * p2p_listen - Start P2P Listen state for specified duration - * @priv: Private driver interface data - * @timeout: Listen state duration in milliseconds - * Returns: 0 on success, -1 on failure - * - * This function can be used to request the P2P module to keep the - * device discoverable on the listen channel for an extended set of - * time. At least in its current form, this is mainly used for testing - * purposes and may not be of much use for normal P2P operations. - * - * This function is only used if the driver implements P2P management, - * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in - * struct wpa_driver_capa. - */ - int (*p2p_listen)(void *priv, unsigned int timeout); - - /** - * p2p_connect - Start P2P group formation (GO negotiation) - * @priv: Private driver interface data - * @peer_addr: MAC address of the peer P2P client - * @wps_method: enum p2p_wps_method value indicating config method - * @go_intent: Local GO intent value (1..15) - * @own_interface_addr: Intended interface address to use with the - * group - * @force_freq: The only allowed channel frequency in MHz or 0 - * @persistent_group: Whether to create persistent group - * Returns: 0 on success, -1 on failure - * - * This function is only used if the driver implements P2P management, - * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in - * struct wpa_driver_capa. - */ - int (*p2p_connect)(void *priv, const u8 *peer_addr, int wps_method, - int go_intent, const u8 *own_interface_addr, - unsigned int force_freq, int persistent_group); - - /** - * wps_success_cb - Report successfully completed WPS provisioning - * @priv: Private driver interface data - * @peer_addr: Peer address - * Returns: 0 on success, -1 on failure - * - * This function is used to report successfully completed WPS - * provisioning during group formation in both GO/Registrar and - * client/Enrollee roles. - * - * This function is only used if the driver implements P2P management, - * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in - * struct wpa_driver_capa. - */ - int (*wps_success_cb)(void *priv, const u8 *peer_addr); - - /** - * p2p_group_formation_failed - Report failed WPS provisioning - * @priv: Private driver interface data - * Returns: 0 on success, -1 on failure - * - * This function is used to report failed group formation. This can - * happen either due to failed WPS provisioning or due to 15 second - * timeout during the provisioning phase. - * - * This function is only used if the driver implements P2P management, - * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in - * struct wpa_driver_capa. - */ - int (*p2p_group_formation_failed)(void *priv); - - /** - * p2p_set_params - Set P2P parameters - * @priv: Private driver interface data - * @params: P2P parameters - * Returns: 0 on success, -1 on failure - * - * This function is only used if the driver implements P2P management, - * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in - * struct wpa_driver_capa. - */ - int (*p2p_set_params)(void *priv, const struct p2p_params *params); - - /** - * p2p_prov_disc_req - Send Provision Discovery Request - * @priv: Private driver interface data - * @peer_addr: MAC address of the peer P2P client - * @config_methods: WPS Config Methods value (only one bit set) - * Returns: 0 on success, -1 on failure - * - * This function can be used to request a discovered P2P peer to - * display a PIN (config_methods = WPS_CONFIG_DISPLAY) or be prepared - * to enter a PIN from us (config_methods = WPS_CONFIG_KEYPAD). The - * Provision Discovery Request frame is transmitted once immediately - * and if no response is received, the frame will be sent again - * whenever the target device is discovered during device dsicovery - * (start with a p2p_find() call). Response from the peer is indicated - * with the EVENT_P2P_PROV_DISC_RESPONSE event. - * - * This function is only used if the driver implements P2P management, - * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in - * struct wpa_driver_capa. - */ - int (*p2p_prov_disc_req)(void *priv, const u8 *peer_addr, - u16 config_methods, int join); - - /** - * p2p_sd_request - Schedule a service discovery query - * @priv: Private driver interface data - * @dst: Destination peer or %NULL to apply for all peers - * @tlvs: P2P Service Query TLV(s) - * Returns: Reference to the query or 0 on failure - * - * Response to the query is indicated with the - * EVENT_P2P_SD_RESPONSE driver event. - * - * This function is only used if the driver implements P2P management, - * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in - * struct wpa_driver_capa. - */ - u64 (*p2p_sd_request)(void *priv, const u8 *dst, - const struct wpabuf *tlvs); - - /** - * p2p_sd_cancel_request - Cancel a pending service discovery query - * @priv: Private driver interface data - * @req: Query reference from p2p_sd_request() - * Returns: 0 on success, -1 on failure - * - * This function is only used if the driver implements P2P management, - * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in - * struct wpa_driver_capa. - */ - int (*p2p_sd_cancel_request)(void *priv, u64 req); - - /** - * p2p_sd_response - Send response to a service discovery query - * @priv: Private driver interface data - * @freq: Frequency from EVENT_P2P_SD_REQUEST event - * @dst: Destination address from EVENT_P2P_SD_REQUEST event - * @dialog_token: Dialog token from EVENT_P2P_SD_REQUEST event - * @resp_tlvs: P2P Service Response TLV(s) - * Returns: 0 on success, -1 on failure - * - * This function is called as a response to the request indicated with - * the EVENT_P2P_SD_REQUEST driver event. - * - * This function is only used if the driver implements P2P management, - * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in - * struct wpa_driver_capa. - */ - int (*p2p_sd_response)(void *priv, int freq, const u8 *dst, - u8 dialog_token, - const struct wpabuf *resp_tlvs); - - /** - * p2p_service_update - Indicate a change in local services - * @priv: Private driver interface data - * Returns: 0 on success, -1 on failure - * - * This function needs to be called whenever there is a change in - * availability of the local services. This will increment the - * Service Update Indicator value which will be used in SD Request and - * Response frames. - * - * This function is only used if the driver implements P2P management, - * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in - * struct wpa_driver_capa. - */ - int (*p2p_service_update)(void *priv); - - /** - * p2p_reject - Reject peer device (explicitly block connections) - * @priv: Private driver interface data - * @addr: MAC address of the peer - * Returns: 0 on success, -1 on failure - */ - int (*p2p_reject)(void *priv, const u8 *addr); - - /** - * p2p_invite - Invite a P2P Device into a group - * @priv: Private driver interface data - * @peer: Device Address of the peer P2P Device - * @role: Local role in the group - * @bssid: Group BSSID or %NULL if not known - * @ssid: Group SSID - * @ssid_len: Length of ssid in octets - * @go_dev_addr: Forced GO Device Address or %NULL if none - * @persistent_group: Whether this is to reinvoke a persistent group - * Returns: 0 on success, -1 on failure - */ - int (*p2p_invite)(void *priv, const u8 *peer, int role, - const u8 *bssid, const u8 *ssid, size_t ssid_len, - const u8 *go_dev_addr, int persistent_group); - - /** * send_tdls_mgmt - for sending TDLS management packets * @priv: private driver interface data * @dst: Destination (peer) MAC address @@ -2327,6 +2438,27 @@ int (*tdls_oper)(void *priv, enum tdls_oper oper, const u8 *peer); /** + * wnm_oper - Notify driver of the WNM frame reception + * @priv: Private driver interface data + * @oper: WNM operation. See %enum wnm_oper + * @peer: Destination (peer) MAC address + * @buf: Buffer for the driver to fill in (for getting IE) + * @buf_len: Return the len of buf + * Returns: 0 on success, negative (<0) on failure + */ + int (*wnm_oper)(void *priv, enum wnm_oper oper, const u8 *peer, + u8 *buf, u16 *buf_len); + + /** + * set_qos_map - Set QoS Map + * @priv: Private driver interface data + * @qos_map_set: QoS Map + * @qos_map_set_len: Length of QoS Map + */ + int (*set_qos_map)(void *priv, const u8 *qos_map_set, + u8 qos_map_set_len); + + /** * signal_poll - Get current connection information * @priv: Private driver interface data * @signal_info: Connection info structure @@ -2347,6 +2479,18 @@ */ int (*set_authmode)(void *priv, int authmode); +#ifdef ANDROID + /** + * driver_cmd - Execute driver-specific command + * @priv: Private driver interface data + * @cmd: Command to execute + * @buf: Return buffer + * @buf_len: Buffer length + * Returns: 0 on success, -1 on failure + */ + int (*driver_cmd)(void *priv, char *cmd, char *buf, size_t buf_len); +#endif /* ANDROID */ + /** * set_rekey_info - Set rekey information * @priv: Private driver interface data @@ -2462,6 +2606,83 @@ */ void (*poll_client)(void *priv, const u8 *own_addr, const u8 *addr, int qos); + + /** + * radio_disable - Disable/enable radio + * @priv: Private driver interface data + * @disabled: 1=disable 0=enable radio + * Returns: 0 on success, -1 on failure + * + * This optional command is for testing purposes. It can be used to + * disable the radio on a testbed device to simulate out-of-radio-range + * conditions. + */ + int (*radio_disable)(void *priv, int disabled); + + /** + * switch_channel - Announce channel switch and migrate the GO to the + * given frequency + * @priv: Private driver interface data + * @settings: Settings for CSA period and new channel + * Returns: 0 on success, -1 on failure + * + * This function is used to move the GO to the legacy STA channel to + * avoid frequency conflict in single channel concurrency. + */ + int (*switch_channel)(void *priv, struct csa_settings *settings); + + /** + * start_dfs_cac - Listen for radar interference on the channel + * @priv: Private driver interface data + * @freq: Channel parameters + * Returns: 0 on success, -1 on failure + */ + int (*start_dfs_cac)(void *priv, struct hostapd_freq_params *freq); + + /** + * stop_ap - Removes beacon from AP + * @priv: Private driver interface data + * Returns: 0 on success, -1 on failure (or if not supported) + * + * This optional function can be used to disable AP mode related + * configuration. Unlike deinit_ap, it does not change to station + * mode. + */ + int (*stop_ap)(void *priv); + + /** + * get_survey - Retrieve survey data + * @priv: Private driver interface data + * @freq: If set, survey data for the specified frequency is only + * being requested. If not set, all survey data is requested. + * Returns: 0 on success, -1 on failure + * + * Use this to retrieve: + * + * - the observed channel noise floor + * - the amount of time we have spent on the channel + * - the amount of time during which we have spent on the channel that + * the radio has determined the medium is busy and we cannot + * transmit + * - the amount of time we have spent receiving data + * - the amount of time we have spent transmitting data + * + * This data can be used for spectrum heuristics. One example is + * Automatic Channel Selection (ACS). The channel survey data is + * kept on a linked list on the channel data, one entry is added + * for each survey. The min_nf of the channel is updated for each + * survey. + */ + int (*get_survey)(void *priv, unsigned int freq); + + /** + * status - Get driver interface status information + * @priv: Private driver interface data + * @buf: Buffer for printing tou the status information + * @buflen: Maximum length of the buffer + * Returns: Length of written status information or -1 on failure + */ + int (*status)(void *priv, char *buf, size_t buflen); }; @@ -2675,15 +2896,6 @@ EVENT_RX_MGMT, /** - * EVENT_RX_ACTION - Action frame received - * - * This event is used to indicate when an Action frame has been - * received. Information about the received frame is included in - * union wpa_event_data::rx_action. - */ - EVENT_RX_ACTION, - - /** * EVENT_REMAIN_ON_CHANNEL - Remain-on-channel duration started * * This event is used to indicate when the driver has started the @@ -2830,38 +3042,6 @@ EVENT_STATION_LOW_ACK, /** - * EVENT_P2P_DEV_FOUND - Report a discovered P2P device - * - * This event is used only if the driver implements P2P management - * internally. Event data is stored in - * union wpa_event_data::p2p_dev_found. - */ - EVENT_P2P_DEV_FOUND, - - /** - * EVENT_P2P_GO_NEG_REQ_RX - Report reception of GO Negotiation Request - * - * This event is used only if the driver implements P2P management - * internally. Event data is stored in - * union wpa_event_data::p2p_go_neg_req_rx. - */ - EVENT_P2P_GO_NEG_REQ_RX, - - /** - * EVENT_P2P_GO_NEG_COMPLETED - Report completion of GO Negotiation - * - * This event is used only if the driver implements P2P management - * internally. Event data is stored in - * union wpa_event_data::p2p_go_neg_completed. - */ - EVENT_P2P_GO_NEG_COMPLETED, - - EVENT_P2P_PROV_DISC_REQUEST, - EVENT_P2P_PROV_DISC_RESPONSE, - EVENT_P2P_SD_REQUEST, - EVENT_P2P_SD_RESPONSE, - - /** * EVENT_IBSS_PEER_LOST - IBSS peer not reachable anymore */ EVENT_IBSS_PEER_LOST, @@ -2887,11 +3067,135 @@ * This event indicates that the station responded to the poll * initiated with @poll_client. */ - EVENT_DRIVER_CLIENT_POLL_OK + EVENT_DRIVER_CLIENT_POLL_OK, + + /** + * EVENT_EAPOL_TX_STATUS - notify of EAPOL TX status + */ + EVENT_EAPOL_TX_STATUS, + + /** + * EVENT_CH_SWITCH - AP or GO decided to switch channels + * + * Described in wpa_event_data.ch_switch + * */ + EVENT_CH_SWITCH, + + /** + * EVENT_WNM - Request WNM operation + * + * This event can be used to request a WNM operation to be performed. + */ + EVENT_WNM, + + /** + * EVENT_CONNECT_FAILED_REASON - Connection failure reason in AP mode + * + * This event indicates that the driver reported a connection failure + * with the specified client (for example, max client reached, etc.) in + * AP mode. + */ + EVENT_CONNECT_FAILED_REASON, + + /** + * EVENT_RADAR_DETECTED - Notify of radar detection + * + * A radar has been detected on the supplied frequency, hostapd should + * react accordingly (e.g., change channel). + */ + EVENT_DFS_RADAR_DETECTED, + + /** + * EVENT_CAC_FINISHED - Notify that channel availability check has been completed + * + * After a successful CAC, the channel can be marked clear and used. + */ + EVENT_DFS_CAC_FINISHED, + + /** + * EVENT_CAC_ABORTED - Notify that channel availability check has been aborted + * + * The CAC was not successful, and the channel remains in the previous + * state. This may happen due to a radar beeing detected or other + * external influences. + */ + EVENT_DFS_CAC_ABORTED, + + /** + * EVENT_DFS_CAC_NOP_FINISHED - Notify that non-occupancy period is over + * + * The channel which was previously unavailable is now available again. + */ + EVENT_DFS_NOP_FINISHED, + + /** + * EVENT_SURVEY - Received survey data + * + * This event gets triggered when a driver query is issued for survey + * data and the requested data becomes available. The returned data is + * stored in struct survey_results. The results provide at most one + * survey entry for each frequency and at minimum will provide one + * survey entry for one frequency. The survey data can be os_malloc()'d + * and then os_free()'d, so the event callback must only copy data. + */ + EVENT_SURVEY, + + /** + * EVENT_SCAN_STARTED - Scan started + * + * This indicates that driver has started a scan operation either based + * on a request from wpa_supplicant/hostapd or from another application. + * EVENT_SCAN_RESULTS is used to indicate when the scan has been + * completed (either successfully or by getting cancelled). + */ + EVENT_SCAN_STARTED, + + /** + * EVENT_AVOID_FREQUENCIES - Received avoid frequency range + * + * This event indicates a set of frequency ranges that should be avoided + * to reduce issues due to interference or internal co-existence + * information in the driver. + */ + EVENT_AVOID_FREQUENCIES }; /** + * struct freq_survey - Channel survey info + * + * @ifidx: Interface index in which this survey was observed + * @freq: Center of frequency of the surveyed channel + * @nf: Channel noise floor in dBm + * @channel_time: Amount of time in ms the radio spent on the channel + * @channel_time_busy: Amount of time in ms the radio detected some signal + * that indicated to the radio the channel was not clear + * @channel_time_rx: Amount of time the radio spent receiving data + * @channel_time_tx: Amount of time the radio spent transmitting data + * @filled: bitmask indicating which fields have been reported, see + * SURVEY_HAS_* defines. + * @list: Internal list pointers + */ +struct freq_survey { + u32 ifidx; + unsigned int freq; + s8 nf; + u64 channel_time; + u64 channel_time_busy; + u64 channel_time_rx; + u64 channel_time_tx; + unsigned int filled; + struct dl_list list; +}; + +#define SURVEY_HAS_NF BIT(0) +#define SURVEY_HAS_CHAN_TIME BIT(1) +#define SURVEY_HAS_CHAN_TIME_BUSY BIT(2) +#define SURVEY_HAS_CHAN_TIME_RX BIT(3) +#define SURVEY_HAS_CHAN_TIME_TX BIT(4) + + +/** * union wpa_event_data - Additional data for wpa_supplicant_event() calls */ union wpa_event_data { @@ -3002,6 +3306,11 @@ * ie_len - Length of ie buffer in octets */ size_t ie_len; + + /** + * locally_generated - Whether the frame was locally generated + */ + int locally_generated; } disassoc_info; /** @@ -3028,6 +3337,11 @@ * ie_len - Length of ie buffer in octets */ size_t ie_len; + + /** + * locally_generated - Whether the frame was locally generated + */ + int locally_generated; } deauth_info; /** @@ -3080,6 +3394,24 @@ } tdls; /** + * struct wnm - Data for EVENT_WNM + */ + struct wnm { + u8 addr[ETH_ALEN]; + enum { + WNM_OPER_SLEEP, + } oper; + enum { + WNM_SLEEP_ENTER, + WNM_SLEEP_EXIT + } sleep_action; + int sleep_intval; + u16 reason_code; + u8 *buf; + u16 buf_len; + } wnm; + + /** * struct ft_ies - FT information elements (EVENT_FT_RESPONSE) * * During FT (IEEE 802.11r) authentication sequence, the driver is @@ -3193,48 +3525,17 @@ const u8 *frame; size_t frame_len; u32 datarate; - u32 ssi_signal; - } rx_mgmt; - - /** - * struct rx_action - Data for EVENT_RX_ACTION events - */ - struct rx_action { - /** - * da - Destination address of the received Action frame - */ - const u8 *da; - - /** - * sa - Source address of the received Action frame - */ - const u8 *sa; - - /** - * bssid - Address 3 of the received Action frame - */ - const u8 *bssid; /** - * category - Action frame category - */ - u8 category; - - /** - * data - Action frame body after category field - */ - const u8 *data; - - /** - * len - Length of data in octets + * freq - Frequency (in MHz) on which the frame was received */ - size_t len; + int freq; /** - * freq - Frequency (in MHz) on which the frame was received + * ssi_signal - Signal strength in dBm (or 0 if not available) */ - int freq; - } rx_action; + int ssi_signal; + } rx_mgmt; /** * struct remain_on_channel - Data for EVENT_REMAIN_ON_CHANNEL events @@ -3311,6 +3612,11 @@ * ie_len - Length of ie buffer in octets */ size_t ie_len; + + /** + * signal - signal strength in dBm (or 0 if not available) + */ + int ssi_signal; } rx_probe_req; /** @@ -3369,66 +3675,6 @@ } low_ack; /** - * struct p2p_dev_found - Data for EVENT_P2P_DEV_FOUND - */ - struct p2p_dev_found { - const u8 *addr; - const u8 *dev_addr; - const u8 *pri_dev_type; - const char *dev_name; - u16 config_methods; - u8 dev_capab; - u8 group_capab; - } p2p_dev_found; - - /** - * struct p2p_go_neg_req_rx - Data for EVENT_P2P_GO_NEG_REQ_RX - */ - struct p2p_go_neg_req_rx { - const u8 *src; - u16 dev_passwd_id; - } p2p_go_neg_req_rx; - - /** - * struct p2p_go_neg_completed - Data for EVENT_P2P_GO_NEG_COMPLETED - */ - struct p2p_go_neg_completed { - struct p2p_go_neg_results *res; - } p2p_go_neg_completed; - - struct p2p_prov_disc_req { - const u8 *peer; - u16 config_methods; - const u8 *dev_addr; - const u8 *pri_dev_type; - const char *dev_name; - u16 supp_config_methods; - u8 dev_capab; - u8 group_capab; - } p2p_prov_disc_req; - - struct p2p_prov_disc_resp { - const u8 *peer; - u16 config_methods; - } p2p_prov_disc_resp; - - struct p2p_sd_req { - int freq; - const u8 *sa; - u8 dialog_token; - u16 update_indic; - const u8 *tlvs; - size_t tlvs_len; - } p2p_sd_req; - - struct p2p_sd_resp { - const u8 *sa; - u16 update_indic; - const u8 *tlvs; - size_t tlvs_len; - } p2p_sd_resp; - - /** * struct ibss_peer_lost - Data for EVENT_IBSS_PEER_LOST */ struct ibss_peer_lost { @@ -3450,6 +3696,93 @@ struct client_poll { u8 addr[ETH_ALEN]; } client_poll; + + /** + * struct eapol_tx_status + * @dst: Original destination + * @data: Data starting with IEEE 802.1X header (!) + * @data_len: Length of data + * @ack: Indicates ack or lost frame + * + * This corresponds to hapd_send_eapol if the frame sent + * there isn't just reported as EVENT_TX_STATUS. + */ + struct eapol_tx_status { + const u8 *dst; + const u8 *data; + int data_len; + int ack; + } eapol_tx_status; + + /** + * struct ch_switch + * @freq: Frequency of new channel in MHz + * @ht_enabled: Whether this is an HT channel + * @ch_offset: Secondary channel offset + * @ch_width: Channel width + * @cf1: Center frequency 1 + * @cf2: Center frequency 2 + */ + struct ch_switch { + int freq; + int ht_enabled; + int ch_offset; + enum chan_width ch_width; + int cf1; + int cf2; + } ch_switch; + + /** + * struct connect_failed - Data for EVENT_CONNECT_FAILED_REASON + * @addr: Remote client address + * @code: Reason code for connection failure + */ + struct connect_failed_reason { + u8 addr[ETH_ALEN]; + enum { + MAX_CLIENT_REACHED, + BLOCKED_CLIENT + } code; + } connect_failed_reason; + + /** + * struct dfs_event - Data for radar detected events + * @freq: Frequency of the channel in MHz + */ + struct dfs_event { + int freq; + int ht_enabled; + int chan_offset; + enum chan_width chan_width; + int cf1; + int cf2; + } dfs_event; + + /** + * survey_results - Survey result data for EVENT_SURVEY + * @freq_filter: Requested frequency survey filter, 0 if request + * was for all survey data + * @survey_list: Linked list of survey data + */ + struct survey_results { + unsigned int freq_filter; + struct dl_list survey_list; /* struct freq_survey */ + } survey_results; + + /** + * channel_list_changed - Data for EVENT_CHANNEL_LIST_CHANGED + * @initiator: Initiator of the regulatory change + */ + struct channel_list_changed { + enum reg_change_initiator initiator; + } channel_list_changed; + + /** + * freq_range - List of frequency ranges + * + * This is used as the data with EVENT_AVOID_FREQUENCIES. + */ + struct wpa_freq_range_list freq_range; }; /** @@ -3508,4 +3841,7 @@ /* Convert wpa_event_type to a string for logging */ const char * event_to_string(enum wpa_event_type event); +/* NULL terminated array of linked in driver wrappers */ +extern struct wpa_driver_ops *wpa_drivers[]; + #endif /* DRIVER_H */ diff -Nru wpa-1.0/src/drivers/driver_hostap.c wpa-2.1/src/drivers/driver_hostap.c --- wpa-1.0/src/drivers/driver_hostap.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/drivers/driver_hostap.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,20 +2,14 @@ * Driver interaction with Linux Host AP driver * Copyright (c) 2003-2005, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" #include -#include "wireless_copy.h" +#include "linux_wext.h" #include "common.h" #include "driver.h" #include "driver_wext.h" @@ -23,8 +17,6 @@ #include "driver_hostap.h" -#ifdef HOSTAPD - #include #include @@ -272,7 +264,7 @@ } -static int hostap_send_mlme(void *priv, const u8 *msg, size_t len) +static int hostap_send_mlme(void *priv, const u8 *msg, size_t len, int noack) { struct hostap_driver_data *drv = priv; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) msg; @@ -321,7 +313,7 @@ pos += 2; memcpy(pos, data, data_len); - res = hostap_send_mlme(drv, (u8 *) hdr, len); + res = hostap_send_mlme(drv, (u8 *) hdr, len, 0); if (res < 0) { wpa_printf(MSG_ERROR, "hostap_send_eapol - packet len: %lu - " "failed: %d (%s)", @@ -1053,7 +1045,7 @@ memcpy(mgmt.bssid, own_addr, ETH_ALEN); mgmt.u.deauth.reason_code = host_to_le16(reason); return hostap_send_mlme(drv, (u8 *) &mgmt, IEEE80211_HDRLEN + - sizeof(mgmt.u.deauth)); + sizeof(mgmt.u.deauth), 0); } @@ -1090,7 +1082,7 @@ memcpy(mgmt.bssid, own_addr, ETH_ALEN); mgmt.u.disassoc.reason_code = host_to_le16(reason); return hostap_send_mlme(drv, (u8 *) &mgmt, IEEE80211_HDRLEN + - sizeof(mgmt.u.disassoc)); + sizeof(mgmt.u.disassoc), 0); } @@ -1168,495 +1160,14 @@ os_memcpy(hdr.IEEE80211_BSSID_FROMDS, own_addr, ETH_ALEN); os_memcpy(hdr.IEEE80211_SA_FROMDS, own_addr, ETH_ALEN); - hostap_send_mlme(priv, (u8 *)&hdr, sizeof(hdr)); -} - -#else /* HOSTAPD */ - -struct wpa_driver_hostap_data { - void *wext; /* private data for driver_wext */ - void *ctx; - char ifname[IFNAMSIZ + 1]; - int sock; - int current_mode; /* infra/adhoc */ -}; - - -static int wpa_driver_hostap_set_auth_alg(void *priv, int auth_alg); - - -static int hostapd_ioctl(struct wpa_driver_hostap_data *drv, - struct prism2_hostapd_param *param, - int len, int show_err) -{ - struct iwreq iwr; - - os_memset(&iwr, 0, sizeof(iwr)); - os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); - iwr.u.data.pointer = (caddr_t) param; - iwr.u.data.length = len; - - if (ioctl(drv->sock, PRISM2_IOCTL_HOSTAPD, &iwr) < 0) { - int ret = errno; - if (show_err) - perror("ioctl[PRISM2_IOCTL_HOSTAPD]"); - return ret; - } - - return 0; -} - - -static int wpa_driver_hostap_set_wpa_ie(struct wpa_driver_hostap_data *drv, - const u8 *wpa_ie, size_t wpa_ie_len) -{ - struct prism2_hostapd_param *param; - int res; - size_t blen = PRISM2_HOSTAPD_GENERIC_ELEMENT_HDR_LEN + wpa_ie_len; - if (blen < sizeof(*param)) - blen = sizeof(*param); - - param = os_zalloc(blen); - if (param == NULL) - return -1; - - param->cmd = PRISM2_HOSTAPD_SET_GENERIC_ELEMENT; - param->u.generic_elem.len = wpa_ie_len; - os_memcpy(param->u.generic_elem.data, wpa_ie, wpa_ie_len); - res = hostapd_ioctl(drv, param, blen, 1); - - os_free(param); - - return res; -} - - -static int prism2param(struct wpa_driver_hostap_data *drv, int param, - int value) -{ - struct iwreq iwr; - int *i, ret = 0; - - os_memset(&iwr, 0, sizeof(iwr)); - os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); - i = (int *) iwr.u.name; - *i++ = param; - *i++ = value; - - if (ioctl(drv->sock, PRISM2_IOCTL_PRISM2_PARAM, &iwr) < 0) { - perror("ioctl[PRISM2_IOCTL_PRISM2_PARAM]"); - ret = -1; - } - return ret; -} - - -static int wpa_driver_hostap_set_wpa(void *priv, int enabled) -{ - struct wpa_driver_hostap_data *drv = priv; - int ret = 0; - - wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled); - - if (!enabled && wpa_driver_hostap_set_wpa_ie(drv, NULL, 0) < 0) - ret = -1; - if (prism2param(drv, PRISM2_PARAM_HOST_ROAMING, enabled ? 2 : 0) < 0) - ret = -1; - if (prism2param(drv, PRISM2_PARAM_WPA, enabled) < 0) - ret = -1; - - return ret; + hostap_send_mlme(priv, (u8 *)&hdr, sizeof(hdr), 0); } -static void show_set_key_error(struct prism2_hostapd_param *param) -{ - switch (param->u.crypt.err) { - case HOSTAP_CRYPT_ERR_UNKNOWN_ALG: - wpa_printf(MSG_INFO, "Unknown algorithm '%s'.", - param->u.crypt.alg); - wpa_printf(MSG_INFO, "You may need to load kernel module to " - "register that algorithm."); - wpa_printf(MSG_INFO, "E.g., 'modprobe hostap_crypt_wep' for " - "WEP."); - break; - case HOSTAP_CRYPT_ERR_UNKNOWN_ADDR: - wpa_printf(MSG_INFO, "Unknown address " MACSTR ".", - MAC2STR(param->sta_addr)); - break; - case HOSTAP_CRYPT_ERR_CRYPT_INIT_FAILED: - wpa_printf(MSG_INFO, "Crypt algorithm initialization failed."); - break; - case HOSTAP_CRYPT_ERR_KEY_SET_FAILED: - wpa_printf(MSG_INFO, "Key setting failed."); - break; - case HOSTAP_CRYPT_ERR_TX_KEY_SET_FAILED: - wpa_printf(MSG_INFO, "TX key index setting failed."); - break; - case HOSTAP_CRYPT_ERR_CARD_CONF_FAILED: - wpa_printf(MSG_INFO, "Card configuration failed."); - break; - } -} - - -static int wpa_driver_hostap_set_key(const char *ifname, void *priv, - enum wpa_alg alg, const u8 *addr, - int key_idx, int set_tx, - const u8 *seq, size_t seq_len, - const u8 *key, size_t key_len) -{ - struct wpa_driver_hostap_data *drv = priv; - struct prism2_hostapd_param *param; - u8 *buf; - size_t blen; - int ret = 0; - char *alg_name; - - switch (alg) { - case WPA_ALG_NONE: - alg_name = "none"; - break; - case WPA_ALG_WEP: - alg_name = "WEP"; - break; - case WPA_ALG_TKIP: - alg_name = "TKIP"; - break; - case WPA_ALG_CCMP: - alg_name = "CCMP"; - break; - default: - return -1; - } - - wpa_printf(MSG_DEBUG, "%s: alg=%s key_idx=%d set_tx=%d seq_len=%lu " - "key_len=%lu", __FUNCTION__, alg_name, key_idx, set_tx, - (unsigned long) seq_len, (unsigned long) key_len); - - if (seq_len > 8) - return -2; - - blen = sizeof(*param) + key_len; - buf = os_zalloc(blen); - if (buf == NULL) - return -1; - - param = (struct prism2_hostapd_param *) buf; - param->cmd = PRISM2_SET_ENCRYPTION; - /* TODO: In theory, STA in client mode can use five keys; four default - * keys for receiving (with keyidx 0..3) and one individual key for - * both transmitting and receiving (keyidx 0) _unicast_ packets. Now, - * keyidx 0 is reserved for this unicast use and default keys can only - * use keyidx 1..3 (i.e., default key with keyidx 0 is not supported). - * This should be fine for more or less all cases, but for completeness - * sake, the driver could be enhanced to support the missing key. */ -#if 0 - if (addr == NULL) - os_memset(param->sta_addr, 0xff, ETH_ALEN); - else - os_memcpy(param->sta_addr, addr, ETH_ALEN); -#else - os_memset(param->sta_addr, 0xff, ETH_ALEN); -#endif - os_strlcpy((char *) param->u.crypt.alg, alg_name, - HOSTAP_CRYPT_ALG_NAME_LEN); - param->u.crypt.flags = set_tx ? HOSTAP_CRYPT_FLAG_SET_TX_KEY : 0; - param->u.crypt.idx = key_idx; - if (seq) - os_memcpy(param->u.crypt.seq, seq, seq_len); - param->u.crypt.key_len = key_len; - os_memcpy((u8 *) (param + 1), key, key_len); - - if (hostapd_ioctl(drv, param, blen, 1)) { - wpa_printf(MSG_WARNING, "Failed to set encryption."); - show_set_key_error(param); - ret = -1; - } - os_free(buf); - - return ret; -} - - -static int wpa_driver_hostap_set_countermeasures(void *priv, int enabled) -{ - struct wpa_driver_hostap_data *drv = priv; - wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled); - return prism2param(drv, PRISM2_PARAM_TKIP_COUNTERMEASURES, enabled); -} - - -static int wpa_driver_hostap_reset(struct wpa_driver_hostap_data *drv, - int type) -{ - struct iwreq iwr; - int *i, ret = 0; - - wpa_printf(MSG_DEBUG, "%s: type=%d", __FUNCTION__, type); - - os_memset(&iwr, 0, sizeof(iwr)); - os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); - i = (int *) iwr.u.name; - *i++ = type; - - if (ioctl(drv->sock, PRISM2_IOCTL_RESET, &iwr) < 0) { - perror("ioctl[PRISM2_IOCTL_RESET]"); - ret = -1; - } - return ret; -} - - -static int wpa_driver_hostap_mlme(struct wpa_driver_hostap_data *drv, - const u8 *addr, int cmd, int reason_code) -{ - struct prism2_hostapd_param param; - int ret; - - /* There does not seem to be a better way of deauthenticating or - * disassociating with Prism2/2.5/3 than sending the management frame - * and then resetting the Port0 to make sure both the AP and the STA - * end up in disconnected state. */ - os_memset(¶m, 0, sizeof(param)); - param.cmd = PRISM2_HOSTAPD_MLME; - os_memcpy(param.sta_addr, addr, ETH_ALEN); - param.u.mlme.cmd = cmd; - param.u.mlme.reason_code = reason_code; - ret = hostapd_ioctl(drv, ¶m, sizeof(param), 1); - if (ret == 0) { - os_sleep(0, 100000); - ret = wpa_driver_hostap_reset(drv, 2); - } - return ret; -} - - -static int wpa_driver_hostap_deauthenticate(void *priv, const u8 *addr, - int reason_code) -{ - struct wpa_driver_hostap_data *drv = priv; - wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); - return wpa_driver_hostap_mlme(drv, addr, MLME_STA_DEAUTH, - reason_code); -} - - -static int wpa_driver_hostap_disassociate(void *priv, const u8 *addr, - int reason_code) -{ - struct wpa_driver_hostap_data *drv = priv; - wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); - return wpa_driver_hostap_mlme(drv, addr, MLME_STA_DISASSOC, - reason_code); -} - - -static int -wpa_driver_hostap_associate(void *priv, - struct wpa_driver_associate_params *params) -{ - struct wpa_driver_hostap_data *drv = priv; - int ret = 0; - int allow_unencrypted_eapol; - - wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); - - if (prism2param(drv, PRISM2_PARAM_DROP_UNENCRYPTED, - params->drop_unencrypted) < 0) - ret = -1; - if (wpa_driver_hostap_set_auth_alg(drv, params->auth_alg) < 0) - ret = -1; - if (params->mode != drv->current_mode) { - /* At the moment, Host AP driver requires host_roaming=2 for - * infrastructure mode and host_roaming=0 for adhoc. */ - if (prism2param(drv, PRISM2_PARAM_HOST_ROAMING, - params->mode == IEEE80211_MODE_IBSS ? 0 : 2) < - 0) { - wpa_printf(MSG_DEBUG, "%s: failed to set host_roaming", - __func__); - } - drv->current_mode = params->mode; - } - - if (prism2param(drv, PRISM2_PARAM_PRIVACY_INVOKED, - params->key_mgmt_suite != KEY_MGMT_NONE) < 0) - ret = -1; - if (wpa_driver_hostap_set_wpa_ie(drv, params->wpa_ie, - params->wpa_ie_len) < 0) - ret = -1; - if (wpa_driver_wext_set_mode(drv->wext, params->mode) < 0) - ret = -1; - if (params->freq && - wpa_driver_wext_set_freq(drv->wext, params->freq) < 0) - ret = -1; - if (wpa_driver_wext_set_ssid(drv->wext, params->ssid, params->ssid_len) - < 0) - ret = -1; - if (wpa_driver_wext_set_bssid(drv->wext, params->bssid) < 0) - ret = -1; - - /* Allow unencrypted EAPOL messages even if pairwise keys are set when - * not using WPA. IEEE 802.1X specifies that these frames are not - * encrypted, but WPA encrypts them when pairwise keys are in use. */ - if (params->key_mgmt_suite == KEY_MGMT_802_1X || - params->key_mgmt_suite == KEY_MGMT_PSK) - allow_unencrypted_eapol = 0; - else - allow_unencrypted_eapol = 1; - - if (prism2param(drv, PRISM2_PARAM_IEEE_802_1X, - allow_unencrypted_eapol) < 0) { - wpa_printf(MSG_DEBUG, "hostap: Failed to configure " - "ieee_802_1x param"); - /* Ignore this error.. driver_hostap.c can also be used with - * other drivers that do not support this prism2_param. */ - } - - return ret; -} - - -static int wpa_driver_hostap_scan(void *priv, - struct wpa_driver_scan_params *params) -{ - struct wpa_driver_hostap_data *drv = priv; - struct prism2_hostapd_param param; - int ret; - const u8 *ssid = params->ssids[0].ssid; - size_t ssid_len = params->ssids[0].ssid_len; - - if (ssid == NULL) { - /* Use standard Linux Wireless Extensions ioctl if possible - * because some drivers using hostap code in wpa_supplicant - * might not support Host AP specific scan request (with SSID - * info). */ - return wpa_driver_wext_scan(drv->wext, params); - } - - if (ssid_len > 32) - ssid_len = 32; - - os_memset(¶m, 0, sizeof(param)); - param.cmd = PRISM2_HOSTAPD_SCAN_REQ; - param.u.scan_req.ssid_len = ssid_len; - os_memcpy(param.u.scan_req.ssid, ssid, ssid_len); - ret = hostapd_ioctl(drv, ¶m, sizeof(param), 1); - - /* Not all drivers generate "scan completed" wireless event, so try to - * read results after a timeout. */ - eloop_cancel_timeout(wpa_driver_wext_scan_timeout, drv->wext, - drv->ctx); - eloop_register_timeout(3, 0, wpa_driver_wext_scan_timeout, drv->wext, - drv->ctx); - - return ret; -} - - -static int wpa_driver_hostap_set_auth_alg(void *priv, int auth_alg) -{ - struct wpa_driver_hostap_data *drv = priv; - int algs = 0; - - if (auth_alg & WPA_AUTH_ALG_OPEN) - algs |= 1; - if (auth_alg & WPA_AUTH_ALG_SHARED) - algs |= 2; - if (auth_alg & WPA_AUTH_ALG_LEAP) - algs |= 4; - if (algs == 0) - algs = 1; /* at least one algorithm should be set */ - - return prism2param(drv, PRISM2_PARAM_AP_AUTH_ALGS, algs); -} - - -static int wpa_driver_hostap_get_bssid(void *priv, u8 *bssid) -{ - struct wpa_driver_hostap_data *drv = priv; - return wpa_driver_wext_get_bssid(drv->wext, bssid); -} - - -static int wpa_driver_hostap_get_ssid(void *priv, u8 *ssid) -{ - struct wpa_driver_hostap_data *drv = priv; - return wpa_driver_wext_get_ssid(drv->wext, ssid); -} - - -static struct wpa_scan_results * wpa_driver_hostap_get_scan_results(void *priv) -{ - struct wpa_driver_hostap_data *drv = priv; - return wpa_driver_wext_get_scan_results(drv->wext); -} - - -static int wpa_driver_hostap_set_operstate(void *priv, int state) -{ - struct wpa_driver_hostap_data *drv = priv; - return wpa_driver_wext_set_operstate(drv->wext, state); -} - - -static void * wpa_driver_hostap_init(void *ctx, const char *ifname) -{ - struct wpa_driver_hostap_data *drv; - - drv = os_zalloc(sizeof(*drv)); - if (drv == NULL) - return NULL; - drv->wext = wpa_driver_wext_init(ctx, ifname); - if (drv->wext == NULL) { - os_free(drv); - return NULL; - } - - drv->ctx = ctx; - os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname)); - drv->sock = socket(PF_INET, SOCK_DGRAM, 0); - if (drv->sock < 0) { - perror("socket"); - wpa_driver_wext_deinit(drv->wext); - os_free(drv); - return NULL; - } - - if (os_strncmp(ifname, "wlan", 4) == 0) { - /* - * Host AP driver may use both wlan# and wifi# interface in - * wireless events. - */ - char ifname2[IFNAMSIZ + 1]; - os_strlcpy(ifname2, ifname, sizeof(ifname2)); - os_memcpy(ifname2, "wifi", 4); - wpa_driver_wext_alternative_ifindex(drv->wext, ifname2); - } - - wpa_driver_hostap_set_wpa(drv, 1); - - return drv; -} - - -static void wpa_driver_hostap_deinit(void *priv) -{ - struct wpa_driver_hostap_data *drv = priv; - wpa_driver_hostap_set_wpa(drv, 0); - wpa_driver_wext_deinit(drv->wext); - close(drv->sock); - os_free(drv); -} - -#endif /* HOSTAPD */ - - const struct wpa_driver_ops wpa_driver_hostap_ops = { .name = "hostap", .desc = "Host AP driver (Intersil Prism2/2.5/3)", .set_key = wpa_driver_hostap_set_key, -#ifdef HOSTAPD .hapd_init = hostap_init, .hapd_deinit = hostap_driver_deinit, .set_ieee8021x = hostap_set_ieee8021x, @@ -1679,17 +1190,4 @@ .set_ap_wps_ie = hostap_set_ap_wps_ie, .set_freq = hostap_set_freq, .poll_client = wpa_driver_hostap_poll_client, -#else /* HOSTAPD */ - .get_bssid = wpa_driver_hostap_get_bssid, - .get_ssid = wpa_driver_hostap_get_ssid, - .set_countermeasures = wpa_driver_hostap_set_countermeasures, - .scan2 = wpa_driver_hostap_scan, - .get_scan_results2 = wpa_driver_hostap_get_scan_results, - .deauthenticate = wpa_driver_hostap_deauthenticate, - .disassociate = wpa_driver_hostap_disassociate, - .associate = wpa_driver_hostap_associate, - .init = wpa_driver_hostap_init, - .deinit = wpa_driver_hostap_deinit, - .set_operstate = wpa_driver_hostap_set_operstate, -#endif /* HOSTAPD */ }; diff -Nru wpa-1.0/src/drivers/driver_hostap.h wpa-2.1/src/drivers/driver_hostap.h --- wpa-1.0/src/drivers/driver_hostap.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/drivers/driver_hostap.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * Driver interaction with Linux Host AP driver * Copyright (c) 2002-2006, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef HOSTAP_DRIVER_H diff -Nru wpa-1.0/src/drivers/driver_iphone.m wpa-2.1/src/drivers/driver_iphone.m --- wpa-1.0/src/drivers/driver_iphone.m 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/drivers/driver_iphone.m 1970-01-01 00:00:00.000000000 +0000 @@ -1,466 +0,0 @@ -/* - * WPA Supplicant - iPhone/iPod touch Apple80211 driver interface - * Copyright (c) 2007, Jouni Malinen - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#include "includes.h" -#define Boolean __DummyBoolean -#include -#undef Boolean - -#include "common.h" -#include "driver.h" -#include "eloop.h" -#include "common/ieee802_11_defs.h" - -#include "MobileApple80211.h" - -struct wpa_driver_iphone_data { - void *ctx; - Apple80211Ref wireless_ctx; - CFArrayRef scan_results; - int ctrl_power; -}; - - -static const void * cfdict_get_key_str(CFDictionaryRef dict, const char *key) -{ - const void *res; - CFStringRef str = CFStringCreateWithCString(kCFAllocatorDefault, key, - kCFStringEncodingMacRoman); - if (str == NULL) - return NULL; - - res = CFDictionaryGetValue(dict, str); - CFRelease(str); - return res; -} - - -static int wpa_driver_iphone_get_ssid(void *priv, u8 *ssid) -{ - struct wpa_driver_iphone_data *drv = priv; - CFDataRef data; - int err, len; - - err = Apple80211CopyValue(drv->wireless_ctx, APPLE80211_VALUE_SSID, 0, - &data); - if (err != 0) { - wpa_printf(MSG_DEBUG, "iPhone: Apple80211CopyValue(SSID) " - "failed: %d", err); - return -1; - } - - len = CFDataGetLength(data); - if (len > 32) { - CFRelease(data); - return -1; - } - os_memcpy(ssid, CFDataGetBytePtr(data), len); - CFRelease(data); - - return len; -} - - -static int wpa_driver_iphone_get_bssid(void *priv, u8 *bssid) -{ - struct wpa_driver_iphone_data *drv = priv; - CFStringRef data; - int err; - int a1, a2, a3, a4, a5, a6; - - err = Apple80211CopyValue(drv->wireless_ctx, APPLE80211_VALUE_BSSID, 0, - &data); - if (err != 0) { - wpa_printf(MSG_DEBUG, "iPhone: Apple80211CopyValue(BSSID) " - "failed: %d", err); - return -1; - } - - sscanf(CFStringGetCStringPtr(data, kCFStringEncodingMacRoman), - "%x:%x:%x:%x:%x:%x", &a1, &a2, &a3, &a4, &a5, &a6); - bssid[0] = a1; - bssid[1] = a2; - bssid[2] = a3; - bssid[3] = a4; - bssid[4] = a5; - bssid[5] = a6; - - CFRelease(data); - - return 0; -} - - -static void wpa_driver_iphone_scan_timeout(void *eloop_ctx, void *timeout_ctx) -{ - wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL); -} - - -static int wpa_driver_iphone_scan(void *priv, const u8 *ssid, size_t ssid_len) -{ - struct wpa_driver_iphone_data *drv = priv; - int err; - - if (drv->scan_results) { - CFRelease(drv->scan_results); - drv->scan_results = NULL; - } - - err = Apple80211Scan(drv->wireless_ctx, &drv->scan_results, NULL); - if (err) { - wpa_printf(MSG_DEBUG, "iPhone: Apple80211Scan failed: %d", - err); - return -1; - } - - eloop_register_timeout(0, 0, wpa_driver_iphone_scan_timeout, drv, - drv->ctx); - return 0; -} - - -static int wpa_driver_iphone_get_scan_results(void *priv, - struct wpa_scan_result *results, - size_t max_size) -{ - struct wpa_driver_iphone_data *drv = priv; - size_t i, num; - - if (drv->scan_results == NULL) - return 0; - - num = CFArrayGetCount(drv->scan_results); - if (num > max_size) - num = max_size; - os_memset(results, 0, num * sizeof(struct wpa_scan_result)); - - for (i = 0; i < num; i++) { - struct wpa_scan_result *res = &results[i]; - CFDictionaryRef dict = - CFArrayGetValueAtIndex(drv->scan_results, i); - CFDataRef data; - CFStringRef str; - CFNumberRef num; - int val; - - data = cfdict_get_key_str(dict, "SSID"); - if (data) { - res->ssid_len = CFDataGetLength(data); - if (res->ssid_len > 32) - res->ssid_len = 32; - os_memcpy(res->ssid, CFDataGetBytePtr(data), - res->ssid_len); - } - - str = cfdict_get_key_str(dict, "BSSID"); - if (str) { - int a1, a2, a3, a4, a5, a6; - sscanf(CFStringGetCStringPtr( - str, kCFStringEncodingMacRoman), - "%x:%x:%x:%x:%x:%x", - &a1, &a2, &a3, &a4, &a5, &a6); - res->bssid[0] = a1; - res->bssid[1] = a2; - res->bssid[2] = a3; - res->bssid[3] = a4; - res->bssid[4] = a5; - res->bssid[5] = a6; - } - - num = cfdict_get_key_str(dict, "CAPABILITIES"); - if (num) { - if (CFNumberGetValue(num, kCFNumberSInt32Type, &val)) - res->caps = val; - } - - num = cfdict_get_key_str(dict, "CHANNEL"); - if (num) { - if (CFNumberGetValue(num, kCFNumberSInt32Type, &val)) - res->freq = 2407 + val * 5; - } - - num = cfdict_get_key_str(dict, "RSSI"); - if (num) { - if (CFNumberGetValue(num, kCFNumberSInt32Type, &val)) - res->level = val; - } - - num = cfdict_get_key_str(dict, "NOISE"); - if (num) { - if (CFNumberGetValue(num, kCFNumberSInt32Type, &val)) - res->noise = val; - } - - data = cfdict_get_key_str(dict, "IE"); - if (data) { - u8 *ptr = (u8 *) CFDataGetBytePtr(data); - int len = CFDataGetLength(data); - u8 *pos = ptr, *end = ptr + len; - - while (pos + 2 < end) { - if (pos + 2 + pos[1] > end) - break; - if (pos[0] == WLAN_EID_RSN && - pos[1] <= SSID_MAX_WPA_IE_LEN) { - os_memcpy(res->rsn_ie, pos, - 2 + pos[1]); - res->rsn_ie_len = 2 + pos[1]; - } - if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && - pos[1] > 4 && pos[2] == 0x00 && - pos[3] == 0x50 && pos[4] == 0xf2 && - pos[5] == 0x01) { - os_memcpy(res->wpa_ie, pos, - 2 + pos[1]); - res->wpa_ie_len = 2 + pos[1]; - } - - pos = pos + 2 + pos[1]; - } - } - } - - return num; -} - - -static void wpa_driver_iphone_assoc_timeout(void *eloop_ctx, void *timeout_ctx) -{ - struct wpa_driver_iphone_data *drv = eloop_ctx; - u8 bssid[ETH_ALEN]; - - if (wpa_driver_iphone_get_bssid(drv, bssid) != 0) { - eloop_register_timeout(1, 0, wpa_driver_iphone_assoc_timeout, - drv, drv->ctx); - return; - } - - wpa_supplicant_event(timeout_ctx, EVENT_ASSOC, NULL); -} - - -static int wpa_driver_iphone_associate( - void *priv, struct wpa_driver_associate_params *params) -{ - struct wpa_driver_iphone_data *drv = priv; - int i, num, err; - size_t ssid_len; - CFDictionaryRef bss = NULL; - - /* - * TODO: Consider generating parameters instead of just using an entry - * from scan results in order to support ap_scan=2. - */ - - if (drv->scan_results == NULL) { - wpa_printf(MSG_DEBUG, "iPhone: No scan results - cannot " - "associate"); - return -1; - } - - num = CFArrayGetCount(drv->scan_results); - - for (i = 0; i < num; i++) { - CFDictionaryRef dict = - CFArrayGetValueAtIndex(drv->scan_results, i); - CFDataRef data; - - data = cfdict_get_key_str(dict, "SSID"); - if (data == NULL) - continue; - - ssid_len = CFDataGetLength(data); - if (ssid_len != params->ssid_len || - os_memcmp(CFDataGetBytePtr(data), params->ssid, ssid_len) - != 0) - continue; - - bss = dict; - break; - } - - if (bss == NULL) { - wpa_printf(MSG_DEBUG, "iPhone: Could not find SSID from scan " - "results - cannot associate"); - return -1; - } - - wpa_printf(MSG_DEBUG, "iPhone: Trying to associate with a BSS found " - "from scan results"); - - err = Apple80211Associate(drv->wireless_ctx, bss, NULL); - if (err) { - wpa_printf(MSG_DEBUG, "iPhone: Apple80211Associate() failed: " - "%d", err); - return -1; - } - - /* - * Driver is actually already associated; report association from an - * eloop callback. - */ - eloop_cancel_timeout(wpa_driver_iphone_assoc_timeout, drv, drv->ctx); - eloop_register_timeout(0, 0, wpa_driver_iphone_assoc_timeout, drv, - drv->ctx); - - return 0; -} - - -static int wpa_driver_iphone_set_key(void *priv, wpa_alg alg, const u8 *addr, - int key_idx, int set_tx, const u8 *seq, - size_t seq_len, const u8 *key, - size_t key_len) -{ - /* - * TODO: Need to either support configuring PMK for 4-way handshake or - * PTK for TKIP/CCMP. - */ - return -1; -} - - -static int wpa_driver_iphone_get_capa(void *priv, struct wpa_driver_capa *capa) -{ - os_memset(capa, 0, sizeof(*capa)); - - capa->key_mgmt = WPA_DRIVER_CAPA_KEY_MGMT_WPA | - WPA_DRIVER_CAPA_KEY_MGMT_WPA2 | - WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK | - WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK; - capa->enc = WPA_DRIVER_CAPA_ENC_WEP40 | WPA_DRIVER_CAPA_ENC_WEP104 | - WPA_DRIVER_CAPA_ENC_TKIP | WPA_DRIVER_CAPA_ENC_CCMP; - capa->auth = WPA_DRIVER_AUTH_OPEN | WPA_DRIVER_AUTH_SHARED | - WPA_DRIVER_AUTH_LEAP; - capa->flags = WPA_DRIVER_FLAGS_4WAY_HANDSHAKE; - - return 0; -} - - -static void * wpa_driver_iphone_init(void *ctx, const char *ifname) -{ - struct wpa_driver_iphone_data *drv; - int err; - char power; - CFStringRef name; - CFDictionaryRef dict; - - drv = os_zalloc(sizeof(*drv)); - if (drv == NULL) - return NULL; - drv->ctx = ctx; - err = Apple80211Open(&drv->wireless_ctx); - if (err) { - wpa_printf(MSG_ERROR, "iPhone: Apple80211Open failed: %d", - err); - os_free(drv); - return NULL; - } - - name = CFStringCreateWithCString(kCFAllocatorDefault, ifname, - kCFStringEncodingISOLatin1); - if (name == NULL) { - wpa_printf(MSG_ERROR, "iPhone: ifname -> CFString failed"); - Apple80211Close(drv->wireless_ctx); - os_free(drv); - return NULL; - } - - err = Apple80211BindToInterface(drv->wireless_ctx, name); - CFRelease(name); - - if (err) { - wpa_printf(MSG_ERROR, "iPhone: Apple80211BindToInterface " - "failed: %d", err); - Apple80211Close(drv->wireless_ctx); - os_free(drv); - return NULL; - } - - err = Apple80211GetPower(drv->wireless_ctx, &power); - if (err) - wpa_printf(MSG_DEBUG, "iPhone: Apple80211GetPower failed: %d", - err); - - wpa_printf(MSG_DEBUG, "iPhone: Power=%d", power); - - if (!power) { - drv->ctrl_power = 1; - err = Apple80211SetPower(drv->wireless_ctx, 1); - if (err) { - wpa_printf(MSG_DEBUG, "iPhone: Apple80211SetPower " - "failed: %d", err); - Apple80211Close(drv->wireless_ctx); - os_free(drv); - return NULL; - } - } - - err = Apple80211GetInfoCopy(drv->wireless_ctx, &dict); - if (err == 0) { - CFShow(dict); - CFRelease(dict); - } else { - printf("Apple80211GetInfoCopy: %d\n", err); - } - - return drv; -} - - -static void wpa_driver_iphone_deinit(void *priv) -{ - struct wpa_driver_iphone_data *drv = priv; - int err; - - eloop_cancel_timeout(wpa_driver_iphone_scan_timeout, drv, drv->ctx); - eloop_cancel_timeout(wpa_driver_iphone_assoc_timeout, drv, drv->ctx); - - if (drv->ctrl_power) { - wpa_printf(MSG_DEBUG, "iPhone: Power down the interface"); - err = Apple80211SetPower(drv->wireless_ctx, 0); - if (err) { - wpa_printf(MSG_DEBUG, "iPhone: Apple80211SetPower(0) " - "failed: %d", err); - } - } - - err = Apple80211Close(drv->wireless_ctx); - if (err) { - wpa_printf(MSG_DEBUG, "iPhone: Apple80211Close failed: %d", - err); - } - - if (drv->scan_results) - CFRelease(drv->scan_results); - - os_free(drv); -} - - -const struct wpa_driver_ops wpa_driver_iphone_ops = { - .name = "iphone", - .desc = "iPhone/iPod touch Apple80211 driver", - .get_ssid = wpa_driver_iphone_get_ssid, - .get_bssid = wpa_driver_iphone_get_bssid, - .init = wpa_driver_iphone_init, - .deinit = wpa_driver_iphone_deinit, - .scan = wpa_driver_iphone_scan, - .get_scan_results = wpa_driver_iphone_get_scan_results, - .associate = wpa_driver_iphone_associate, - .set_key = wpa_driver_iphone_set_key, - .get_capa = wpa_driver_iphone_get_capa, -}; diff -Nru wpa-1.0/src/drivers/driver_madwifi.c wpa-2.1/src/drivers/driver_madwifi.c --- wpa-1.0/src/drivers/driver_madwifi.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/drivers/driver_madwifi.c 2014-02-04 11:23:35.000000000 +0000 @@ -1,22 +1,15 @@ /* - * WPA Supplicant - driver interaction with MADWIFI 802.11 driver + * hostapd - driver interaction with MADWIFI 802.11 driver * Copyright (c) 2004, Sam Leffler * Copyright (c) 2004, Video54 Technologies * Copyright (c) 2004-2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. + * This software may be distributed under the terms of the BSD license. + * See README for more details. * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - * - * While this driver wrapper supports both AP (hostapd) and station - * (wpa_supplicant) operations, the station side is deprecated and - * driver_wext.c should be used instead. This driver wrapper should only be - * used with hostapd for AP mode functionality. + * This driver wrapper is only for hostapd AP mode functionality. Station + * (wpa_supplicant) operations with madwifi are supported by the driver_wext.c + * wrapper. */ #include "includes.h" @@ -27,7 +20,7 @@ #include "driver_wext.h" #include "eloop.h" #include "common/ieee802_11_defs.h" -#include "wireless_copy.h" +#include "linux_wext.h" /* * Avoid conflicts with wpa_supplicant definitions by undefining a definition. @@ -71,8 +64,6 @@ #define WPA_KEY_RSC_LEN 8 -#ifdef HOSTAPD - #include "priv_netlink.h" #include "netlink.h" #include "linux_ioctl.h" @@ -190,7 +181,7 @@ #endif /* MADWIFI_NG */ int idx = op - first; if (first <= op && - idx < (int) (sizeof(opnames) / sizeof(opnames[0])) && + idx < (int) ARRAY_SIZE(opnames) && opnames[idx]) perror(opnames[idx]); else @@ -330,19 +321,16 @@ IEEE80211_AUTH_AUTO); } if (!params->wpa && !params->ieee802_1x) { - hostapd_logger(drv->hapd, NULL, HOSTAPD_MODULE_DRIVER, - HOSTAPD_LEVEL_WARNING, "No 802.1X or WPA enabled!"); + wpa_printf(MSG_WARNING, "No 802.1X or WPA enabled!"); return -1; } if (params->wpa && madwifi_configure_wpa(drv, params) != 0) { - hostapd_logger(drv->hapd, NULL, HOSTAPD_MODULE_DRIVER, - HOSTAPD_LEVEL_WARNING, "Error configuring WPA state!"); + wpa_printf(MSG_WARNING, "Error configuring WPA state!"); return -1; } if (set80211param(priv, IEEE80211_PARAM_AUTHMODE, (params->wpa ? IEEE80211_AUTH_WPA : IEEE80211_AUTH_8021X))) { - hostapd_logger(drv->hapd, NULL, HOSTAPD_MODULE_DRIVER, - HOSTAPD_LEVEL_WARNING, "Error enabling WPA/802.1X!"); + wpa_printf(MSG_WARNING, "Error enabling WPA/802.1X!"); return -1; } @@ -1294,554 +1282,11 @@ return linux_set_iface_flags(drv->ioctl_sock, drv->iface, 1); } -#else /* HOSTAPD */ - -struct wpa_driver_madwifi_data { - void *wext; /* private data for driver_wext */ - void *ctx; - char ifname[IFNAMSIZ + 1]; - int sock; -}; - -static int wpa_driver_madwifi_set_auth_alg(void *priv, int auth_alg); -static int wpa_driver_madwifi_set_probe_req_ie(void *priv, const u8 *ies, - size_t ies_len); - - -static int -set80211priv(struct wpa_driver_madwifi_data *drv, int op, void *data, int len, - int show_err) -{ - struct iwreq iwr; - - os_memset(&iwr, 0, sizeof(iwr)); - os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); - if (len < IFNAMSIZ && - op != IEEE80211_IOCTL_SET_APPIEBUF) { - /* - * Argument data fits inline; put it there. - */ - os_memcpy(iwr.u.name, data, len); - } else { - /* - * Argument data too big for inline transfer; setup a - * parameter block instead; the kernel will transfer - * the data for the driver. - */ - iwr.u.data.pointer = data; - iwr.u.data.length = len; - } - - if (ioctl(drv->sock, op, &iwr) < 0) { - if (show_err) { -#ifdef MADWIFI_NG - int first = IEEE80211_IOCTL_SETPARAM; - int last = IEEE80211_IOCTL_KICKMAC; - static const char *opnames[] = { - "ioctl[IEEE80211_IOCTL_SETPARAM]", - "ioctl[IEEE80211_IOCTL_GETPARAM]", - "ioctl[IEEE80211_IOCTL_SETMODE]", - "ioctl[IEEE80211_IOCTL_GETMODE]", - "ioctl[IEEE80211_IOCTL_SETWMMPARAMS]", - "ioctl[IEEE80211_IOCTL_GETWMMPARAMS]", - "ioctl[IEEE80211_IOCTL_SETCHANLIST]", - "ioctl[IEEE80211_IOCTL_GETCHANLIST]", - "ioctl[IEEE80211_IOCTL_CHANSWITCH]", - NULL, - "ioctl[IEEE80211_IOCTL_SET_APPIEBUF]", - "ioctl[IEEE80211_IOCTL_GETSCANRESULTS]", - NULL, - "ioctl[IEEE80211_IOCTL_GETCHANINFO]", - "ioctl[IEEE80211_IOCTL_SETOPTIE]", - "ioctl[IEEE80211_IOCTL_GETOPTIE]", - "ioctl[IEEE80211_IOCTL_SETMLME]", - NULL, - "ioctl[IEEE80211_IOCTL_SETKEY]", - NULL, - "ioctl[IEEE80211_IOCTL_DELKEY]", - NULL, - "ioctl[IEEE80211_IOCTL_ADDMAC]", - NULL, - "ioctl[IEEE80211_IOCTL_DELMAC]", - NULL, - "ioctl[IEEE80211_IOCTL_WDSMAC]", - NULL, - "ioctl[IEEE80211_IOCTL_WDSDELMAC]", - NULL, - "ioctl[IEEE80211_IOCTL_KICKMAC]", - }; -#else /* MADWIFI_NG */ - int first = IEEE80211_IOCTL_SETPARAM; - int last = IEEE80211_IOCTL_CHANLIST; - static const char *opnames[] = { - "ioctl[IEEE80211_IOCTL_SETPARAM]", - "ioctl[IEEE80211_IOCTL_GETPARAM]", - "ioctl[IEEE80211_IOCTL_SETKEY]", - "ioctl[IEEE80211_IOCTL_GETKEY]", - "ioctl[IEEE80211_IOCTL_DELKEY]", - NULL, - "ioctl[IEEE80211_IOCTL_SETMLME]", - NULL, - "ioctl[IEEE80211_IOCTL_SETOPTIE]", - "ioctl[IEEE80211_IOCTL_GETOPTIE]", - "ioctl[IEEE80211_IOCTL_ADDMAC]", - NULL, - "ioctl[IEEE80211_IOCTL_DELMAC]", - NULL, - "ioctl[IEEE80211_IOCTL_CHANLIST]", - }; -#endif /* MADWIFI_NG */ - int idx = op - first; - if (first <= op && op <= last && - idx < (int) (sizeof(opnames) / sizeof(opnames[0])) - && opnames[idx]) - perror(opnames[idx]); - else - perror("ioctl[unknown???]"); - } - return -1; - } - return 0; -} - -static int -set80211param(struct wpa_driver_madwifi_data *drv, int op, int arg, - int show_err) -{ - struct iwreq iwr; - - os_memset(&iwr, 0, sizeof(iwr)); - os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); - iwr.u.mode = op; - os_memcpy(iwr.u.name+sizeof(u32), &arg, sizeof(arg)); - - if (ioctl(drv->sock, IEEE80211_IOCTL_SETPARAM, &iwr) < 0) { - if (show_err) - perror("ioctl[IEEE80211_IOCTL_SETPARAM]"); - return -1; - } - return 0; -} - -static int -wpa_driver_madwifi_set_wpa_ie(struct wpa_driver_madwifi_data *drv, - const u8 *wpa_ie, size_t wpa_ie_len) -{ - struct iwreq iwr; - - os_memset(&iwr, 0, sizeof(iwr)); - os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); - /* NB: SETOPTIE is not fixed-size so must not be inlined */ - iwr.u.data.pointer = (void *) wpa_ie; - iwr.u.data.length = wpa_ie_len; - - if (ioctl(drv->sock, IEEE80211_IOCTL_SETOPTIE, &iwr) < 0) { - perror("ioctl[IEEE80211_IOCTL_SETOPTIE]"); - return -1; - } - return 0; -} - -static int -wpa_driver_madwifi_del_key(struct wpa_driver_madwifi_data *drv, int key_idx, - const u8 *addr) -{ - struct ieee80211req_del_key wk; - - wpa_printf(MSG_DEBUG, "%s: keyidx=%d", __FUNCTION__, key_idx); - os_memset(&wk, 0, sizeof(wk)); - wk.idk_keyix = key_idx; - if (addr != NULL) - os_memcpy(wk.idk_macaddr, addr, IEEE80211_ADDR_LEN); - - return set80211priv(drv, IEEE80211_IOCTL_DELKEY, &wk, sizeof(wk), 1); -} - -static int -wpa_driver_madwifi_set_key(const char *ifname, void *priv, enum wpa_alg alg, - const u8 *addr, int key_idx, int set_tx, - const u8 *seq, size_t seq_len, - const u8 *key, size_t key_len) -{ - struct wpa_driver_madwifi_data *drv = priv; - struct ieee80211req_key wk; - char *alg_name; - u_int8_t cipher; - - if (alg == WPA_ALG_NONE) - return wpa_driver_madwifi_del_key(drv, key_idx, addr); - - switch (alg) { - case WPA_ALG_WEP: - if (addr == NULL || os_memcmp(addr, "\xff\xff\xff\xff\xff\xff", - ETH_ALEN) == 0) { - /* - * madwifi did not seem to like static WEP key - * configuration with IEEE80211_IOCTL_SETKEY, so use - * Linux wireless extensions ioctl for this. - */ - return wpa_driver_wext_set_key(ifname, drv->wext, alg, - addr, key_idx, set_tx, - seq, seq_len, - key, key_len); - } - alg_name = "WEP"; - cipher = IEEE80211_CIPHER_WEP; - break; - case WPA_ALG_TKIP: - alg_name = "TKIP"; - cipher = IEEE80211_CIPHER_TKIP; - break; - case WPA_ALG_CCMP: - alg_name = "CCMP"; - cipher = IEEE80211_CIPHER_AES_CCM; - break; - default: - wpa_printf(MSG_DEBUG, "%s: unknown/unsupported algorithm %d", - __FUNCTION__, alg); - return -1; - } - - wpa_printf(MSG_DEBUG, "%s: alg=%s key_idx=%d set_tx=%d seq_len=%lu " - "key_len=%lu", __FUNCTION__, alg_name, key_idx, set_tx, - (unsigned long) seq_len, (unsigned long) key_len); - - if (seq_len > sizeof(u_int64_t)) { - wpa_printf(MSG_DEBUG, "%s: seq_len %lu too big", - __FUNCTION__, (unsigned long) seq_len); - return -2; - } - if (key_len > sizeof(wk.ik_keydata)) { - wpa_printf(MSG_DEBUG, "%s: key length %lu too big", - __FUNCTION__, (unsigned long) key_len); - return -3; - } - - os_memset(&wk, 0, sizeof(wk)); - wk.ik_type = cipher; - wk.ik_flags = IEEE80211_KEY_RECV; - if (addr == NULL || - os_memcmp(addr, "\xff\xff\xff\xff\xff\xff", ETH_ALEN) == 0) - wk.ik_flags |= IEEE80211_KEY_GROUP; - if (set_tx) { - wk.ik_flags |= IEEE80211_KEY_XMIT | IEEE80211_KEY_DEFAULT; - os_memcpy(wk.ik_macaddr, addr, IEEE80211_ADDR_LEN); - } else - os_memset(wk.ik_macaddr, 0, IEEE80211_ADDR_LEN); - wk.ik_keyix = key_idx; - wk.ik_keylen = key_len; -#ifdef WORDS_BIGENDIAN - if (seq) { - size_t i; - u8 tmp[WPA_KEY_RSC_LEN]; - os_memset(tmp, 0, sizeof(tmp)); - for (i = 0; i < seq_len; i++) - tmp[WPA_KEY_RSC_LEN - i - 1] = seq[i]; - os_memcpy(&wk.ik_keyrsc, tmp, WPA_KEY_RSC_LEN); - } -#else /* WORDS_BIGENDIAN */ - if (seq) - os_memcpy(&wk.ik_keyrsc, seq, seq_len); -#endif /* WORDS_BIGENDIAN */ - os_memcpy(wk.ik_keydata, key, key_len); - - return set80211priv(drv, IEEE80211_IOCTL_SETKEY, &wk, sizeof(wk), 1); -} - -static int -wpa_driver_madwifi_set_countermeasures(void *priv, int enabled) -{ - struct wpa_driver_madwifi_data *drv = priv; - wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled); - return set80211param(drv, IEEE80211_PARAM_COUNTERMEASURES, enabled, 1); -} - -static int -wpa_driver_madwifi_deauthenticate(void *priv, const u8 *addr, int reason_code) -{ - struct wpa_driver_madwifi_data *drv = priv; - struct ieee80211req_mlme mlme; - - wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); - mlme.im_op = IEEE80211_MLME_DEAUTH; - mlme.im_reason = reason_code; - os_memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN); - return set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, sizeof(mlme), 1); -} - -static int -wpa_driver_madwifi_disassociate(void *priv, const u8 *addr, int reason_code) -{ - struct wpa_driver_madwifi_data *drv = priv; - struct ieee80211req_mlme mlme; - - wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); - mlme.im_op = IEEE80211_MLME_DISASSOC; - mlme.im_reason = reason_code; - os_memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN); - return set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, sizeof(mlme), 1); -} - -static int -wpa_driver_madwifi_associate(void *priv, - struct wpa_driver_associate_params *params) -{ - struct wpa_driver_madwifi_data *drv = priv; - struct ieee80211req_mlme mlme; - int ret = 0, privacy = 1; - - wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); - - if (set80211param(drv, IEEE80211_PARAM_DROPUNENCRYPTED, - params->drop_unencrypted, 1) < 0) - ret = -1; - if (wpa_driver_madwifi_set_auth_alg(drv, params->auth_alg) < 0) - ret = -1; - - /* - * NB: Don't need to set the freq or cipher-related state as - * this is implied by the bssid which is used to locate - * the scanned node state which holds it. The ssid is - * needed to disambiguate an AP that broadcasts multiple - * ssid's but uses the same bssid. - */ - /* XXX error handling is wrong but unclear what to do... */ - if (wpa_driver_madwifi_set_wpa_ie(drv, params->wpa_ie, - params->wpa_ie_len) < 0) - ret = -1; - - if (params->pairwise_suite == CIPHER_NONE && - params->group_suite == CIPHER_NONE && - params->key_mgmt_suite == KEY_MGMT_NONE && - params->wpa_ie_len == 0) - privacy = 0; - - if (set80211param(drv, IEEE80211_PARAM_PRIVACY, privacy, 1) < 0) - ret = -1; - - if (params->wpa_ie_len && - set80211param(drv, IEEE80211_PARAM_WPA, - params->wpa_ie[0] == WLAN_EID_RSN ? 2 : 1, 1) < 0) - ret = -1; - - if (params->bssid == NULL) { - /* ap_scan=2 mode - driver takes care of AP selection and - * roaming */ - /* FIX: this does not seem to work; would probably need to - * change something in the driver */ - if (set80211param(drv, IEEE80211_PARAM_ROAMING, 0, 1) < 0) - ret = -1; - - if (wpa_driver_wext_set_ssid(drv->wext, params->ssid, - params->ssid_len) < 0) - ret = -1; - } else { - if (set80211param(drv, IEEE80211_PARAM_ROAMING, 2, 1) < 0) - ret = -1; - if (wpa_driver_wext_set_ssid(drv->wext, params->ssid, - params->ssid_len) < 0) - ret = -1; - os_memset(&mlme, 0, sizeof(mlme)); - mlme.im_op = IEEE80211_MLME_ASSOC; - os_memcpy(mlme.im_macaddr, params->bssid, IEEE80211_ADDR_LEN); - if (set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, - sizeof(mlme), 1) < 0) { - wpa_printf(MSG_DEBUG, "%s: SETMLME[ASSOC] failed", - __func__); - ret = -1; - } - } - - return ret; -} - -static int -wpa_driver_madwifi_set_auth_alg(void *priv, int auth_alg) -{ - struct wpa_driver_madwifi_data *drv = priv; - int authmode; - - if ((auth_alg & WPA_AUTH_ALG_OPEN) && - (auth_alg & WPA_AUTH_ALG_SHARED)) - authmode = IEEE80211_AUTH_AUTO; - else if (auth_alg & WPA_AUTH_ALG_SHARED) - authmode = IEEE80211_AUTH_SHARED; - else - authmode = IEEE80211_AUTH_OPEN; - - return set80211param(drv, IEEE80211_PARAM_AUTHMODE, authmode, 1); -} - -static int -wpa_driver_madwifi_scan(void *priv, struct wpa_driver_scan_params *params) -{ - struct wpa_driver_madwifi_data *drv = priv; - struct iwreq iwr; - int ret = 0; - const u8 *ssid = params->ssids[0].ssid; - size_t ssid_len = params->ssids[0].ssid_len; - - wpa_driver_madwifi_set_probe_req_ie(drv, params->extra_ies, - params->extra_ies_len); - - os_memset(&iwr, 0, sizeof(iwr)); - os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); - - /* set desired ssid before scan */ - /* FIX: scan should not break the current association, so using - * set_ssid may not be the best way of doing this.. */ - if (wpa_driver_wext_set_ssid(drv->wext, ssid, ssid_len) < 0) - ret = -1; - - if (ioctl(drv->sock, SIOCSIWSCAN, &iwr) < 0) { - perror("ioctl[SIOCSIWSCAN]"); - ret = -1; - } - - /* - * madwifi delivers a scan complete event so no need to poll, but - * register a backup timeout anyway to make sure that we recover even - * if the driver does not send this event for any reason. This timeout - * will only be used if the event is not delivered (event handler will - * cancel the timeout). - */ - eloop_cancel_timeout(wpa_driver_wext_scan_timeout, drv->wext, - drv->ctx); - eloop_register_timeout(30, 0, wpa_driver_wext_scan_timeout, drv->wext, - drv->ctx); - - return ret; -} - -static int wpa_driver_madwifi_get_bssid(void *priv, u8 *bssid) -{ - struct wpa_driver_madwifi_data *drv = priv; - return wpa_driver_wext_get_bssid(drv->wext, bssid); -} - - -static int wpa_driver_madwifi_get_ssid(void *priv, u8 *ssid) -{ - struct wpa_driver_madwifi_data *drv = priv; - return wpa_driver_wext_get_ssid(drv->wext, ssid); -} - - -static struct wpa_scan_results * -wpa_driver_madwifi_get_scan_results(void *priv) -{ - struct wpa_driver_madwifi_data *drv = priv; - return wpa_driver_wext_get_scan_results(drv->wext); -} - - -static int wpa_driver_madwifi_set_operstate(void *priv, int state) -{ - struct wpa_driver_madwifi_data *drv = priv; - return wpa_driver_wext_set_operstate(drv->wext, state); -} - - -static int wpa_driver_madwifi_set_probe_req_ie(void *priv, const u8 *ies, - size_t ies_len) -{ - struct ieee80211req_getset_appiebuf *probe_req_ie; - int ret; - - probe_req_ie = os_malloc(sizeof(*probe_req_ie) + ies_len); - if (probe_req_ie == NULL) - return -1; - - probe_req_ie->app_frmtype = IEEE80211_APPIE_FRAME_PROBE_REQ; - probe_req_ie->app_buflen = ies_len; - os_memcpy(probe_req_ie->app_buf, ies, ies_len); - - ret = set80211priv(priv, IEEE80211_IOCTL_SET_APPIEBUF, probe_req_ie, - sizeof(struct ieee80211req_getset_appiebuf) + - ies_len, 1); - - os_free(probe_req_ie); - - return ret; -} - - -static void * wpa_driver_madwifi_init(void *ctx, const char *ifname) -{ - struct wpa_driver_madwifi_data *drv; - - drv = os_zalloc(sizeof(*drv)); - if (drv == NULL) - return NULL; - drv->wext = wpa_driver_wext_init(ctx, ifname); - if (drv->wext == NULL) - goto fail; - - drv->ctx = ctx; - os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname)); - drv->sock = socket(PF_INET, SOCK_DGRAM, 0); - if (drv->sock < 0) - goto fail2; - - if (set80211param(drv, IEEE80211_PARAM_ROAMING, 2, 1) < 0) { - wpa_printf(MSG_DEBUG, "%s: failed to set wpa_supplicant-based " - "roaming", __FUNCTION__); - goto fail3; - } - - if (set80211param(drv, IEEE80211_PARAM_WPA, 3, 1) < 0) { - wpa_printf(MSG_DEBUG, "%s: failed to enable WPA support", - __FUNCTION__); - goto fail3; - } - - return drv; - -fail3: - close(drv->sock); -fail2: - wpa_driver_wext_deinit(drv->wext); -fail: - os_free(drv); - return NULL; -} - - -static void wpa_driver_madwifi_deinit(void *priv) -{ - struct wpa_driver_madwifi_data *drv = priv; - - if (wpa_driver_madwifi_set_wpa_ie(drv, NULL, 0) < 0) { - wpa_printf(MSG_DEBUG, "%s: failed to clear WPA IE", - __FUNCTION__); - } - if (set80211param(drv, IEEE80211_PARAM_ROAMING, 0, 1) < 0) { - wpa_printf(MSG_DEBUG, "%s: failed to enable driver-based " - "roaming", __FUNCTION__); - } - if (set80211param(drv, IEEE80211_PARAM_PRIVACY, 0, 1) < 0) { - wpa_printf(MSG_DEBUG, "%s: failed to disable forced Privacy " - "flag", __FUNCTION__); - } - if (set80211param(drv, IEEE80211_PARAM_WPA, 0, 1) < 0) { - wpa_printf(MSG_DEBUG, "%s: failed to disable WPA", - __FUNCTION__); - } - - wpa_driver_wext_deinit(drv->wext); - - close(drv->sock); - os_free(drv); -} - -#endif /* HOSTAPD */ - const struct wpa_driver_ops wpa_driver_madwifi_ops = { .name = "madwifi", .desc = "MADWIFI 802.11 support (Atheros, etc.)", .set_key = wpa_driver_madwifi_set_key, -#ifdef HOSTAPD .hapd_init = madwifi_init, .hapd_deinit = madwifi_deinit, .set_ieee8021x = madwifi_set_ieee8021x, @@ -1861,17 +1306,4 @@ .commit = madwifi_commit, .set_ap_wps_ie = madwifi_set_ap_wps_ie, .set_freq = madwifi_set_freq, -#else /* HOSTAPD */ - .get_bssid = wpa_driver_madwifi_get_bssid, - .get_ssid = wpa_driver_madwifi_get_ssid, - .init = wpa_driver_madwifi_init, - .deinit = wpa_driver_madwifi_deinit, - .set_countermeasures = wpa_driver_madwifi_set_countermeasures, - .scan2 = wpa_driver_madwifi_scan, - .get_scan_results2 = wpa_driver_madwifi_get_scan_results, - .deauthenticate = wpa_driver_madwifi_deauthenticate, - .disassociate = wpa_driver_madwifi_disassociate, - .associate = wpa_driver_madwifi_associate, - .set_operstate = wpa_driver_madwifi_set_operstate, -#endif /* HOSTAPD */ }; diff -Nru wpa-1.0/src/drivers/driver_ndis_.c wpa-2.1/src/drivers/driver_ndis_.c --- wpa-1.0/src/drivers/driver_ndis_.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/drivers/driver_ndis_.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * WPA Supplicant - Windows/NDIS driver interface - event processing * Copyright (c) 2004-2005, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff -Nru wpa-1.0/src/drivers/driver_ndis.c wpa-2.1/src/drivers/driver_ndis.c --- wpa-1.0/src/drivers/driver_ndis.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/drivers/driver_ndis.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * WPA Supplicant - Windows/NDIS driver interface * Copyright (c) 2004-2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifdef __CYGWIN__ @@ -731,14 +725,6 @@ } -static int wpa_driver_ndis_disassociate(void *priv, const u8 *addr, - int reason_code) -{ - struct wpa_driver_ndis_data *drv = priv; - return wpa_driver_ndis_disconnect(drv); -} - - static void wpa_driver_ndis_scan_timeout(void *eloop_ctx, void *timeout_ctx) { wpa_printf(MSG_DEBUG, "Scan timeout - try to get results"); @@ -864,7 +850,7 @@ os_free(b); return NULL; } - results->res = os_zalloc(count * sizeof(struct wpa_scan_res *)); + results->res = os_calloc(count, sizeof(struct wpa_scan_res *)); if (results->res == NULL) { os_free(results); os_free(b); @@ -1088,8 +1074,8 @@ /* Try to continue anyway */ } - if (params->key_mgmt_suite == KEY_MGMT_NONE || - params->key_mgmt_suite == KEY_MGMT_802_1X_NO_WPA) { + if (params->key_mgmt_suite == WPA_KEY_MGMT_NONE || + params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_NO_WPA) { /* Re-set WEP keys if static WEP configuration is used. */ int i; for (i = 0; i < 4; i++) { @@ -1116,12 +1102,12 @@ priv_mode = Ndis802_11PrivFilterAcceptAll; } else if (params->wpa_ie[0] == WLAN_EID_RSN) { priv_mode = Ndis802_11PrivFilter8021xWEP; - if (params->key_mgmt_suite == KEY_MGMT_PSK) + if (params->key_mgmt_suite == WPA_KEY_MGMT_PSK) auth_mode = Ndis802_11AuthModeWPA2PSK; else auth_mode = Ndis802_11AuthModeWPA2; #ifdef CONFIG_WPS - } else if (params->key_mgmt_suite == KEY_MGMT_WPS) { + } else if (params->key_mgmt_suite == WPA_KEY_MGMT_WPS) { auth_mode = Ndis802_11AuthModeOpen; priv_mode = Ndis802_11PrivFilterAcceptAll; if (params->wps == WPS_MODE_PRIVACY) { @@ -1143,35 +1129,35 @@ #endif /* CONFIG_WPS */ } else { priv_mode = Ndis802_11PrivFilter8021xWEP; - if (params->key_mgmt_suite == KEY_MGMT_WPA_NONE) + if (params->key_mgmt_suite == WPA_KEY_MGMT_WPA_NONE) auth_mode = Ndis802_11AuthModeWPANone; - else if (params->key_mgmt_suite == KEY_MGMT_PSK) + else if (params->key_mgmt_suite == WPA_KEY_MGMT_PSK) auth_mode = Ndis802_11AuthModeWPAPSK; else auth_mode = Ndis802_11AuthModeWPA; } switch (params->pairwise_suite) { - case CIPHER_CCMP: + case WPA_CIPHER_CCMP: encr = Ndis802_11Encryption3Enabled; break; - case CIPHER_TKIP: + case WPA_CIPHER_TKIP: encr = Ndis802_11Encryption2Enabled; break; - case CIPHER_WEP40: - case CIPHER_WEP104: + case WPA_CIPHER_WEP40: + case WPA_CIPHER_WEP104: encr = Ndis802_11Encryption1Enabled; break; - case CIPHER_NONE: + case WPA_CIPHER_NONE: #ifdef CONFIG_WPS if (params->wps == WPS_MODE_PRIVACY) { encr = Ndis802_11Encryption1Enabled; break; } #endif /* CONFIG_WPS */ - if (params->group_suite == CIPHER_CCMP) + if (params->group_suite == WPA_CIPHER_CCMP) encr = Ndis802_11Encryption3Enabled; - else if (params->group_suite == CIPHER_TKIP) + else if (params->group_suite == WPA_CIPHER_TKIP) encr = Ndis802_11Encryption2Enabled; else encr = Ndis802_11EncryptionDisabled; @@ -2124,14 +2110,8 @@ dlen = dpos - desc; else dlen = os_strlen(desc); - drv->adapter_desc = os_malloc(dlen + 1); - if (drv->adapter_desc) { - os_memcpy(drv->adapter_desc, desc, dlen); - drv->adapter_desc[dlen] = '\0'; - } - + drv->adapter_desc = dup_binstr(desc, dlen); os_free(b); - if (drv->adapter_desc == NULL) return -1; @@ -2298,14 +2278,8 @@ } else { dlen = os_strlen(desc[i]); } - drv->adapter_desc = os_malloc(dlen + 1); - if (drv->adapter_desc) { - os_memcpy(drv->adapter_desc, desc[i], dlen); - drv->adapter_desc[dlen] = '\0'; - } - + drv->adapter_desc = dup_binstr(desc[i], dlen); os_free(names); - if (drv->adapter_desc == NULL) return -1; @@ -3229,7 +3203,6 @@ wpa_driver_ndis_ops.init = wpa_driver_ndis_init; wpa_driver_ndis_ops.deinit = wpa_driver_ndis_deinit; wpa_driver_ndis_ops.deauthenticate = wpa_driver_ndis_deauthenticate; - wpa_driver_ndis_ops.disassociate = wpa_driver_ndis_disassociate; wpa_driver_ndis_ops.associate = wpa_driver_ndis_associate; wpa_driver_ndis_ops.add_pmkid = wpa_driver_ndis_add_pmkid; wpa_driver_ndis_ops.remove_pmkid = wpa_driver_ndis_remove_pmkid; diff -Nru wpa-1.0/src/drivers/driver_ndis.h wpa-2.1/src/drivers/driver_ndis.h --- wpa-1.0/src/drivers/driver_ndis.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/drivers/driver_ndis.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * WPA Supplicant - Windows/NDIS driver interface * Copyright (c) 2004-2006, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef DRIVER_NDIS_H diff -Nru wpa-1.0/src/drivers/driver_nl80211.c wpa-2.1/src/drivers/driver_nl80211.c --- wpa-1.0/src/drivers/driver_nl80211.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/drivers/driver_nl80211.c 2014-02-04 11:23:35.000000000 +0000 @@ -1,19 +1,13 @@ /* * Driver interaction with Linux nl80211/cfg80211 - * Copyright (c) 2002-2010, Jouni Malinen + * Copyright (c) 2002-2014, Jouni Malinen * Copyright (c) 2003-2004, Instant802 Networks, Inc. * Copyright (c) 2005-2006, Devicescape Software, Inc. * Copyright (c) 2007, Johannes Berg * Copyright (c) 2009-2010, Atheros Communications * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -28,11 +22,13 @@ #include #include #include +#include #include "nl80211_copy.h" #include "common.h" #include "eloop.h" #include "utils/list.h" +#include "common/qca-vendor.h" #include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" #include "l2_packet/l2_packet.h" @@ -43,6 +39,29 @@ #include "rfkill.h" #include "driver.h" +#ifndef SO_WIFI_STATUS +# if defined(__sparc__) +# define SO_WIFI_STATUS 0x0025 +# elif defined(__parisc__) +# define SO_WIFI_STATUS 0x4022 +# else +# define SO_WIFI_STATUS 41 +# endif + +# define SCM_WIFI_STATUS SO_WIFI_STATUS +#endif + +#ifndef SO_EE_ORIGIN_TXSTATUS +#define SO_EE_ORIGIN_TXSTATUS 4 +#endif + +#ifndef PACKET_TX_TIMESTAMP +#define PACKET_TX_TIMESTAMP 16 +#endif + +#ifdef ANDROID +#include "android_drv.h" +#endif /* ANDROID */ #ifdef CONFIG_LIBNL20 /* libnl 2.0 compatibility code */ #define nl_handle nl_sock @@ -88,65 +107,73 @@ nl_handle_destroy(handle); } - -static inline int __genl_ctrl_alloc_cache(struct nl_handle *h, - struct nl_cache **cache) -{ - struct nl_cache *tmp = genl_ctrl_alloc_cache(h); - if (!tmp) - return -ENOMEM; - *cache = tmp; - return 0; -} -#define genl_ctrl_alloc_cache __genl_ctrl_alloc_cache #endif /* CONFIG_LIBNL20 */ -struct nl80211_handles { - struct nl_handle *handle; - struct nl_cache *cache; -}; +#ifdef ANDROID +/* system/core/libnl_2 does not include nl_socket_set_nonblocking() */ +static int android_nl_socket_set_nonblocking(struct nl_handle *handle) +{ + return fcntl(nl_socket_get_fd(handle), F_SETFL, O_NONBLOCK); +} +#undef nl_socket_set_nonblocking +#define nl_socket_set_nonblocking(h) android_nl_socket_set_nonblocking(h) +#endif /* ANDROID */ -static int nl_create_handles(struct nl80211_handles *handles, struct nl_cb *cb, - const char *dbg) +static struct nl_handle * nl_create_handle(struct nl_cb *cb, const char *dbg) { - if (!handles) - return -1; + struct nl_handle *handle; - handles->handle = nl80211_handle_alloc(cb); - if (handles->handle == NULL) { + handle = nl80211_handle_alloc(cb); + if (handle == NULL) { wpa_printf(MSG_ERROR, "nl80211: Failed to allocate netlink " "callbacks (%s)", dbg); - return -1; + return NULL; } - if (genl_connect(handles->handle)) { + if (genl_connect(handle)) { wpa_printf(MSG_ERROR, "nl80211: Failed to connect to generic " "netlink (%s)", dbg); - goto err; - } - - if (genl_ctrl_alloc_cache(handles->handle, &handles->cache) < 0) { - wpa_printf(MSG_ERROR, "nl80211: Failed to allocate generic " - "netlink cache (%s)", dbg); - goto err; + nl80211_handle_destroy(handle); + return NULL; } - return 0; -err: - nl80211_handle_destroy(handles->handle); - return -1; + return handle; } -static void nl_destroy_handles(struct nl80211_handles *handles) +static void nl_destroy_handles(struct nl_handle **handle) { - if (handles->handle == NULL) + if (*handle == NULL) return; - nl_cache_free(handles->cache); - nl80211_handle_destroy(handles->handle); - handles->handle = NULL; + nl80211_handle_destroy(*handle); + *handle = NULL; +} + + +#if __WORDSIZE == 64 +#define ELOOP_SOCKET_INVALID (intptr_t) 0x8888888888888889ULL +#else +#define ELOOP_SOCKET_INVALID (intptr_t) 0x88888889ULL +#endif + +static void nl80211_register_eloop_read(struct nl_handle **handle, + eloop_sock_handler handler, + void *eloop_data) +{ + nl_socket_set_nonblocking(*handle); + eloop_register_read_sock(nl_socket_get_fd(*handle), handler, + eloop_data, *handle); + *handle = (void *) (((intptr_t) *handle) ^ ELOOP_SOCKET_INVALID); +} + + +static void nl80211_destroy_eloop_handle(struct nl_handle **handle) +{ + *handle = (void *) (((intptr_t) *handle) ^ ELOOP_SOCKET_INVALID); + eloop_unregister_read_sock(nl_socket_get_fd(*handle)); + nl_destroy_handles(handle); } @@ -167,31 +194,61 @@ struct nl80211_global { struct dl_list interfaces; int if_add_ifindex; + u64 if_add_wdevid; + int if_add_wdevid_set; struct netlink_data *netlink; struct nl_cb *nl_cb; - struct nl80211_handles nl; - struct genl_family *nl80211; + struct nl_handle *nl; + int nl80211_id; int ioctl_sock; /* socket for ioctl() use */ + + struct nl_handle *nl_event; +}; + +struct nl80211_wiphy_data { + struct dl_list list; + struct dl_list bsss; + struct dl_list drvs; + + struct nl_handle *nl_beacons; + struct nl_cb *nl_cb; + + int wiphy_idx; }; static void nl80211_global_deinit(void *priv); -static void wpa_driver_nl80211_deinit(void *priv); struct i802_bss { struct wpa_driver_nl80211_data *drv; struct i802_bss *next; int ifindex; + u64 wdev_id; char ifname[IFNAMSIZ + 1]; char brname[IFNAMSIZ]; unsigned int beacon_set:1; unsigned int added_if_into_bridge:1; unsigned int added_bridge:1; + unsigned int in_deinit:1; + unsigned int wdev_id_set:1; + unsigned int added_if:1; + + u8 addr[ETH_ALEN]; + + int freq; + int if_dynamic; + + void *ctx; + struct nl_handle *nl_preq, *nl_mgmt; + struct nl_cb *nl_cb; + + struct nl80211_wiphy_data *wiphy_data; + struct dl_list wiphy_list; }; struct wpa_driver_nl80211_data { struct nl80211_global *global; struct dl_list list; - u8 addr[ETH_ALEN]; + struct dl_list wiphy_list; char phyname[32]; void *ctx; int ifindex; @@ -200,16 +257,25 @@ int ignore_if_down_event; struct rfkill_data *rfkill; struct wpa_driver_capa capa; + u8 *extended_capa, *extended_capa_mask; + unsigned int extended_capa_len; int has_capability; int operstate; int scan_complete_events; + enum scan_states { + NO_SCAN, SCAN_REQUESTED, SCAN_STARTED, SCAN_COMPLETED, + SCAN_ABORTED, SCHED_SCAN_STARTED, SCHED_SCAN_STOPPED, + SCHED_SCAN_RESULTS + } scan_state; - struct nl80211_handles nl_event, nl_preq; + struct nl_cb *nl_cb; u8 auth_bssid[ETH_ALEN]; + u8 auth_attempt_bssid[ETH_ALEN]; u8 bssid[ETH_ALEN]; + u8 prev_bssid[ETH_ALEN]; int associated; u8 ssid[32]; size_t ssid_len; @@ -219,84 +285,249 @@ int monitor_sock; int monitor_ifidx; - int no_monitor_iface_capab; + int monitor_refcount; unsigned int disabled_11b_rates:1; unsigned int pending_remain_on_chan:1; unsigned int in_interface_list:1; + unsigned int device_ap_sme:1; + unsigned int poll_command_supported:1; + unsigned int data_tx_status:1; + unsigned int scan_for_auth:1; + unsigned int retry_auth:1; + unsigned int use_monitor:1; + unsigned int ignore_next_local_disconnect:1; + unsigned int allow_p2p_device:1; + unsigned int hostapd:1; + unsigned int start_mode_ap:1; + unsigned int start_iface_up:1; u64 remain_on_chan_cookie; u64 send_action_cookie; unsigned int last_mgmt_freq; - unsigned int ap_oper_freq; struct wpa_driver_scan_filter *filter_ssids; size_t num_filter_ssids; - struct i802_bss first_bss; + struct i802_bss *first_bss; -#ifdef CONFIG_AP - struct l2_packet_data *l2; -#endif /* CONFIG_AP */ + int eapol_tx_sock; -#ifdef HOSTAPD int eapol_sock; /* socket for EAPOL frames */ int default_if_indices[16]; int *if_indices; int num_if_indices; - int last_freq; - int last_freq_ht; -#endif /* HOSTAPD */ + /* From failed authentication command */ + int auth_freq; + u8 auth_bssid_[ETH_ALEN]; + u8 auth_ssid[32]; + size_t auth_ssid_len; + int auth_alg; + u8 *auth_ie; + size_t auth_ie_len; + u8 auth_wep_key[4][16]; + size_t auth_wep_key_len[4]; + int auth_wep_tx_keyidx; + int auth_local_state_change; + int auth_p2p; }; +static void wpa_driver_nl80211_deinit(struct i802_bss *bss); static void wpa_driver_nl80211_scan_timeout(void *eloop_ctx, void *timeout_ctx); static int wpa_driver_nl80211_set_mode(struct i802_bss *bss, enum nl80211_iftype nlmode); static int -wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv); +wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv, + const u8 *set_addr, int first); static int wpa_driver_nl80211_mlme(struct wpa_driver_nl80211_data *drv, const u8 *addr, int cmd, u16 reason_code, int local_state_change); static void nl80211_remove_monitor_interface( struct wpa_driver_nl80211_data *drv); -static int nl80211_send_frame_cmd(struct wpa_driver_nl80211_data *drv, +static int nl80211_send_frame_cmd(struct i802_bss *bss, unsigned int freq, unsigned int wait, const u8 *buf, size_t buf_len, u64 *cookie, - int no_cck); -static int wpa_driver_nl80211_probe_req_report(void *priv, int report); + int no_cck, int no_ack, int offchanok); +static int nl80211_register_frame(struct i802_bss *bss, + struct nl_handle *hl_handle, + u16 type, const u8 *match, size_t match_len); +static int wpa_driver_nl80211_probe_req_report(struct i802_bss *bss, + int report); +#ifdef ANDROID +static int android_pno_start(struct i802_bss *bss, + struct wpa_driver_scan_params *params); +static int android_pno_stop(struct i802_bss *bss); +extern int wpa_driver_nl80211_driver_cmd(void *priv, char *cmd, char *buf, + size_t buf_len); +#endif /* ANDROID */ +#ifdef ANDROID_P2P +int wpa_driver_set_p2p_noa(void *priv, u8 count, int start, int duration); +int wpa_driver_get_p2p_noa(void *priv, u8 *buf, size_t len); +int wpa_driver_set_p2p_ps(void *priv, int legacy_ps, int opp_ps, int ctwindow); +int wpa_driver_set_ap_wps_p2p_ie(void *priv, const struct wpabuf *beacon, + const struct wpabuf *proberesp, + const struct wpabuf *assocresp); +#endif /* ANDROID_P2P */ -#ifdef HOSTAPD static void add_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx); static void del_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx); static int have_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx); -static int wpa_driver_nl80211_if_remove(void *priv, +static int wpa_driver_nl80211_if_remove(struct i802_bss *bss, enum wpa_driver_if_type type, const char *ifname); -#else /* HOSTAPD */ -static inline void add_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx) -{ -} -static inline void del_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx) -{ -} +static int wpa_driver_nl80211_set_freq(struct i802_bss *bss, + struct hostapd_freq_params *freq); +static int nl80211_disable_11b_rates(struct wpa_driver_nl80211_data *drv, + int ifindex, int disabled); + +static int nl80211_leave_ibss(struct wpa_driver_nl80211_data *drv); +static int wpa_driver_nl80211_authenticate_retry( + struct wpa_driver_nl80211_data *drv); + +static int i802_set_iface_flags(struct i802_bss *bss, int up); + -static inline int have_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx) +static const char * nl80211_command_to_string(enum nl80211_commands cmd) { - return 0; +#define C2S(x) case x: return #x; + switch (cmd) { + C2S(NL80211_CMD_UNSPEC) + C2S(NL80211_CMD_GET_WIPHY) + C2S(NL80211_CMD_SET_WIPHY) + C2S(NL80211_CMD_NEW_WIPHY) + C2S(NL80211_CMD_DEL_WIPHY) + C2S(NL80211_CMD_GET_INTERFACE) + C2S(NL80211_CMD_SET_INTERFACE) + C2S(NL80211_CMD_NEW_INTERFACE) + C2S(NL80211_CMD_DEL_INTERFACE) + C2S(NL80211_CMD_GET_KEY) + C2S(NL80211_CMD_SET_KEY) + C2S(NL80211_CMD_NEW_KEY) + C2S(NL80211_CMD_DEL_KEY) + C2S(NL80211_CMD_GET_BEACON) + C2S(NL80211_CMD_SET_BEACON) + C2S(NL80211_CMD_START_AP) + C2S(NL80211_CMD_STOP_AP) + C2S(NL80211_CMD_GET_STATION) + C2S(NL80211_CMD_SET_STATION) + C2S(NL80211_CMD_NEW_STATION) + C2S(NL80211_CMD_DEL_STATION) + C2S(NL80211_CMD_GET_MPATH) + C2S(NL80211_CMD_SET_MPATH) + C2S(NL80211_CMD_NEW_MPATH) + C2S(NL80211_CMD_DEL_MPATH) + C2S(NL80211_CMD_SET_BSS) + C2S(NL80211_CMD_SET_REG) + C2S(NL80211_CMD_REQ_SET_REG) + C2S(NL80211_CMD_GET_MESH_CONFIG) + C2S(NL80211_CMD_SET_MESH_CONFIG) + C2S(NL80211_CMD_SET_MGMT_EXTRA_IE) + C2S(NL80211_CMD_GET_REG) + C2S(NL80211_CMD_GET_SCAN) + C2S(NL80211_CMD_TRIGGER_SCAN) + C2S(NL80211_CMD_NEW_SCAN_RESULTS) + C2S(NL80211_CMD_SCAN_ABORTED) + C2S(NL80211_CMD_REG_CHANGE) + C2S(NL80211_CMD_AUTHENTICATE) + C2S(NL80211_CMD_ASSOCIATE) + C2S(NL80211_CMD_DEAUTHENTICATE) + C2S(NL80211_CMD_DISASSOCIATE) + C2S(NL80211_CMD_MICHAEL_MIC_FAILURE) + C2S(NL80211_CMD_REG_BEACON_HINT) + C2S(NL80211_CMD_JOIN_IBSS) + C2S(NL80211_CMD_LEAVE_IBSS) + C2S(NL80211_CMD_TESTMODE) + C2S(NL80211_CMD_CONNECT) + C2S(NL80211_CMD_ROAM) + C2S(NL80211_CMD_DISCONNECT) + C2S(NL80211_CMD_SET_WIPHY_NETNS) + C2S(NL80211_CMD_GET_SURVEY) + C2S(NL80211_CMD_NEW_SURVEY_RESULTS) + C2S(NL80211_CMD_SET_PMKSA) + C2S(NL80211_CMD_DEL_PMKSA) + C2S(NL80211_CMD_FLUSH_PMKSA) + C2S(NL80211_CMD_REMAIN_ON_CHANNEL) + C2S(NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL) + C2S(NL80211_CMD_SET_TX_BITRATE_MASK) + C2S(NL80211_CMD_REGISTER_FRAME) + C2S(NL80211_CMD_FRAME) + C2S(NL80211_CMD_FRAME_TX_STATUS) + C2S(NL80211_CMD_SET_POWER_SAVE) + C2S(NL80211_CMD_GET_POWER_SAVE) + C2S(NL80211_CMD_SET_CQM) + C2S(NL80211_CMD_NOTIFY_CQM) + C2S(NL80211_CMD_SET_CHANNEL) + C2S(NL80211_CMD_SET_WDS_PEER) + C2S(NL80211_CMD_FRAME_WAIT_CANCEL) + C2S(NL80211_CMD_JOIN_MESH) + C2S(NL80211_CMD_LEAVE_MESH) + C2S(NL80211_CMD_UNPROT_DEAUTHENTICATE) + C2S(NL80211_CMD_UNPROT_DISASSOCIATE) + C2S(NL80211_CMD_NEW_PEER_CANDIDATE) + C2S(NL80211_CMD_GET_WOWLAN) + C2S(NL80211_CMD_SET_WOWLAN) + C2S(NL80211_CMD_START_SCHED_SCAN) + C2S(NL80211_CMD_STOP_SCHED_SCAN) + C2S(NL80211_CMD_SCHED_SCAN_RESULTS) + C2S(NL80211_CMD_SCHED_SCAN_STOPPED) + C2S(NL80211_CMD_SET_REKEY_OFFLOAD) + C2S(NL80211_CMD_PMKSA_CANDIDATE) + C2S(NL80211_CMD_TDLS_OPER) + C2S(NL80211_CMD_TDLS_MGMT) + C2S(NL80211_CMD_UNEXPECTED_FRAME) + C2S(NL80211_CMD_PROBE_CLIENT) + C2S(NL80211_CMD_REGISTER_BEACONS) + C2S(NL80211_CMD_UNEXPECTED_4ADDR_FRAME) + C2S(NL80211_CMD_SET_NOACK_MAP) + C2S(NL80211_CMD_CH_SWITCH_NOTIFY) + C2S(NL80211_CMD_START_P2P_DEVICE) + C2S(NL80211_CMD_STOP_P2P_DEVICE) + C2S(NL80211_CMD_CONN_FAILED) + C2S(NL80211_CMD_SET_MCAST_RATE) + C2S(NL80211_CMD_SET_MAC_ACL) + C2S(NL80211_CMD_RADAR_DETECT) + C2S(NL80211_CMD_GET_PROTOCOL_FEATURES) + C2S(NL80211_CMD_UPDATE_FT_IES) + C2S(NL80211_CMD_FT_EVENT) + C2S(NL80211_CMD_CRIT_PROTOCOL_START) + C2S(NL80211_CMD_CRIT_PROTOCOL_STOP) + C2S(NL80211_CMD_GET_COALESCE) + C2S(NL80211_CMD_SET_COALESCE) + C2S(NL80211_CMD_CHANNEL_SWITCH) + C2S(NL80211_CMD_VENDOR) + C2S(NL80211_CMD_SET_QOS_MAP) + default: + return "NL80211_CMD_UNKNOWN"; + } +#undef C2S } -#endif /* HOSTAPD */ -static int i802_set_freq(void *priv, struct hostapd_freq_params *freq); -static int nl80211_disable_11b_rates(struct wpa_driver_nl80211_data *drv, - int ifindex, int disabled); -static int nl80211_leave_ibss(struct wpa_driver_nl80211_data *drv); +/* Converts nl80211_chan_width to a common format */ +static enum chan_width convert2width(int width) +{ + switch (width) { + case NL80211_CHAN_WIDTH_20_NOHT: + return CHAN_WIDTH_20_NOHT; + case NL80211_CHAN_WIDTH_20: + return CHAN_WIDTH_20; + case NL80211_CHAN_WIDTH_40: + return CHAN_WIDTH_40; + case NL80211_CHAN_WIDTH_80: + return CHAN_WIDTH_80; + case NL80211_CHAN_WIDTH_80P80: + return CHAN_WIDTH_80P80; + case NL80211_CHAN_WIDTH_160: + return CHAN_WIDTH_160; + } + return CHAN_WIDTH_UNKNOWN; +} static int is_ap_interface(enum nl80211_iftype nlmode) @@ -313,13 +544,22 @@ } -static int is_p2p_interface(enum nl80211_iftype nlmode) +static int is_p2p_net_interface(enum nl80211_iftype nlmode) { return (nlmode == NL80211_IFTYPE_P2P_CLIENT || nlmode == NL80211_IFTYPE_P2P_GO); } +static void nl80211_mark_disconnected(struct wpa_driver_nl80211_data *drv) +{ + if (drv->associated) + os_memcpy(drv->prev_bssid, drv->bssid, ETH_ALEN); + drv->associated = 0; + os_memset(drv->bssid, 0, ETH_ALEN); +} + + struct nl80211_bss_info_arg { struct wpa_driver_nl80211_data *drv; struct wpa_scan_results *res; @@ -360,7 +600,7 @@ } -static int send_and_recv(struct wpa_driver_nl80211_data *drv, +static int send_and_recv(struct nl80211_global *global, struct nl_handle *nl_handle, struct nl_msg *msg, int (*valid_handler)(struct nl_msg *, void *), void *valid_data) @@ -368,7 +608,7 @@ struct nl_cb *cb; int err = -ENOMEM; - cb = nl_cb_clone(drv->global->nl_cb); + cb = nl_cb_clone(global->nl_cb); if (!cb) goto out; @@ -386,8 +626,14 @@ nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, valid_handler, valid_data); - while (err > 0) - nl_recvmsgs(nl_handle, cb); + while (err > 0) { + int res = nl_recvmsgs(nl_handle, cb); + if (res) { + wpa_printf(MSG_INFO, + "nl80211: %s->nl_recvmsgs failed: %d", + __func__, res); + } + } out: nl_cb_put(cb); nlmsg_free(msg); @@ -395,13 +641,23 @@ } +static int send_and_recv_msgs_global(struct nl80211_global *global, + struct nl_msg *msg, + int (*valid_handler)(struct nl_msg *, void *), + void *valid_data) +{ + return send_and_recv(global, global->nl, msg, valid_handler, + valid_data); +} + + static int send_and_recv_msgs(struct wpa_driver_nl80211_data *drv, struct nl_msg *msg, int (*valid_handler)(struct nl_msg *, void *), void *valid_data) { - return send_and_recv(drv, drv->global->nl.handle, msg, valid_handler, - valid_data); + return send_and_recv(drv->global, drv->global->nl, msg, + valid_handler, valid_data); } @@ -411,6 +667,19 @@ }; +static int nl80211_set_iface_id(struct nl_msg *msg, struct i802_bss *bss) +{ + if (bss->wdev_id_set) + NLA_PUT_U64(msg, NL80211_ATTR_WDEV, bss->wdev_id); + else + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, bss->ifindex); + return 0; + +nla_put_failure: + return -1; +} + + static int family_handler(struct nl_msg *msg, void *arg) { struct family_data *res = arg; @@ -442,7 +711,7 @@ } -static int nl_get_multicast_id(struct wpa_driver_nl80211_data *drv, +static int nl_get_multicast_id(struct nl80211_global *global, const char *family, const char *group) { struct nl_msg *msg; @@ -452,12 +721,11 @@ msg = nlmsg_alloc(); if (!msg) return -ENOMEM; - genlmsg_put(msg, 0, 0, - genl_ctrl_resolve(drv->global->nl.handle, "nlctrl"), + genlmsg_put(msg, 0, 0, genl_ctrl_resolve(global->nl, "nlctrl"), 0, 0, CTRL_CMD_GETFAMILY, 0); NLA_PUT_STRING(msg, CTRL_ATTR_FAMILY_NAME, family); - ret = send_and_recv_msgs(drv, msg, family_handler, &res); + ret = send_and_recv_msgs_global(global, msg, family_handler, &res); msg = NULL; if (ret == 0) ret = res.id; @@ -471,166 +739,492 @@ static void * nl80211_cmd(struct wpa_driver_nl80211_data *drv, struct nl_msg *msg, int flags, uint8_t cmd) { - return genlmsg_put(msg, 0, 0, genl_family_get_id(drv->global->nl80211), + return genlmsg_put(msg, 0, 0, drv->global->nl80211_id, 0, flags, cmd, 0); } -static int wpa_driver_nl80211_get_bssid(void *priv, u8 *bssid) +struct wiphy_idx_data { + int wiphy_idx; + enum nl80211_iftype nlmode; + u8 *macaddr; +}; + + +static int netdev_info_handler(struct nl_msg *msg, void *arg) { - struct i802_bss *bss = priv; - struct wpa_driver_nl80211_data *drv = bss->drv; - if (!drv->associated) - return -1; - os_memcpy(bssid, drv->bssid, ETH_ALEN); - return 0; + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct wiphy_idx_data *info = arg; + + nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + + if (tb[NL80211_ATTR_WIPHY]) + info->wiphy_idx = nla_get_u32(tb[NL80211_ATTR_WIPHY]); + + if (tb[NL80211_ATTR_IFTYPE]) + info->nlmode = nla_get_u32(tb[NL80211_ATTR_IFTYPE]); + + if (tb[NL80211_ATTR_MAC] && info->macaddr) + os_memcpy(info->macaddr, nla_data(tb[NL80211_ATTR_MAC]), + ETH_ALEN); + + return NL_SKIP; } -static int wpa_driver_nl80211_get_ssid(void *priv, u8 *ssid) +static int nl80211_get_wiphy_index(struct i802_bss *bss) { - struct i802_bss *bss = priv; - struct wpa_driver_nl80211_data *drv = bss->drv; - if (!drv->associated) - return -1; - os_memcpy(ssid, drv->ssid, drv->ssid_len); - return drv->ssid_len; + struct nl_msg *msg; + struct wiphy_idx_data data = { + .wiphy_idx = -1, + .macaddr = NULL, + }; + + msg = nlmsg_alloc(); + if (!msg) + return NL80211_IFTYPE_UNSPECIFIED; + + nl80211_cmd(bss->drv, msg, 0, NL80211_CMD_GET_INTERFACE); + + if (nl80211_set_iface_id(msg, bss) < 0) + goto nla_put_failure; + + if (send_and_recv_msgs(bss->drv, msg, netdev_info_handler, &data) == 0) + return data.wiphy_idx; + msg = NULL; +nla_put_failure: + nlmsg_free(msg); + return -1; } -static void wpa_driver_nl80211_event_link(struct wpa_driver_nl80211_data *drv, - char *buf, size_t len, int del) +static enum nl80211_iftype nl80211_get_ifmode(struct i802_bss *bss) { - union wpa_event_data event; + struct nl_msg *msg; + struct wiphy_idx_data data = { + .nlmode = NL80211_IFTYPE_UNSPECIFIED, + .macaddr = NULL, + }; - os_memset(&event, 0, sizeof(event)); - if (len > sizeof(event.interface_status.ifname)) - len = sizeof(event.interface_status.ifname) - 1; - os_memcpy(event.interface_status.ifname, buf, len); - event.interface_status.ievent = del ? EVENT_INTERFACE_REMOVED : - EVENT_INTERFACE_ADDED; - - wpa_printf(MSG_DEBUG, "RTM_%sLINK, IFLA_IFNAME: Interface '%s' %s", - del ? "DEL" : "NEW", - event.interface_status.ifname, - del ? "removed" : "added"); - - if (os_strcmp(drv->first_bss.ifname, event.interface_status.ifname) == 0) { - if (del) { - if (drv->if_removed) { - wpa_printf(MSG_DEBUG, "nl80211: if_removed " - "already set - ignore event"); - return; - } - drv->if_removed = 1; - } else { - if (if_nametoindex(drv->first_bss.ifname) == 0) { - wpa_printf(MSG_DEBUG, "nl80211: Interface %s " - "does not exist - ignore " - "RTM_NEWLINK", - drv->first_bss.ifname); - return; - } - if (!drv->if_removed) { - wpa_printf(MSG_DEBUG, "nl80211: if_removed " - "already cleared - ignore event"); - return; - } - drv->if_removed = 0; - } - } + msg = nlmsg_alloc(); + if (!msg) + return -1; - wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS, &event); + nl80211_cmd(bss->drv, msg, 0, NL80211_CMD_GET_INTERFACE); + + if (nl80211_set_iface_id(msg, bss) < 0) + goto nla_put_failure; + + if (send_and_recv_msgs(bss->drv, msg, netdev_info_handler, &data) == 0) + return data.nlmode; + msg = NULL; +nla_put_failure: + nlmsg_free(msg); + return NL80211_IFTYPE_UNSPECIFIED; } -static int wpa_driver_nl80211_own_ifname(struct wpa_driver_nl80211_data *drv, - u8 *buf, size_t len) +static int nl80211_get_macaddr(struct i802_bss *bss) { - int attrlen, rta_len; - struct rtattr *attr; + struct nl_msg *msg; + struct wiphy_idx_data data = { + .macaddr = bss->addr, + }; - attrlen = len; - attr = (struct rtattr *) buf; + msg = nlmsg_alloc(); + if (!msg) + return NL80211_IFTYPE_UNSPECIFIED; - rta_len = RTA_ALIGN(sizeof(struct rtattr)); - while (RTA_OK(attr, attrlen)) { - if (attr->rta_type == IFLA_IFNAME) { - if (os_strcmp(((char *) attr) + rta_len, drv->first_bss.ifname) - == 0) - return 1; - else - break; - } - attr = RTA_NEXT(attr, attrlen); - } + nl80211_cmd(bss->drv, msg, 0, NL80211_CMD_GET_INTERFACE); + if (nl80211_set_iface_id(msg, bss) < 0) + goto nla_put_failure; - return 0; + return send_and_recv_msgs(bss->drv, msg, netdev_info_handler, &data); + +nla_put_failure: + nlmsg_free(msg); + return NL80211_IFTYPE_UNSPECIFIED; } -static int wpa_driver_nl80211_own_ifindex(struct wpa_driver_nl80211_data *drv, - int ifindex, u8 *buf, size_t len) +static int nl80211_register_beacons(struct wpa_driver_nl80211_data *drv, + struct nl80211_wiphy_data *w) { - if (drv->ifindex == ifindex) - return 1; + struct nl_msg *msg; + int ret = -1; - if (drv->if_removed && wpa_driver_nl80211_own_ifname(drv, buf, len)) { - drv->first_bss.ifindex = if_nametoindex(drv->first_bss.ifname); - wpa_printf(MSG_DEBUG, "nl80211: Update ifindex for a removed " - "interface"); - wpa_driver_nl80211_finish_drv_init(drv); - return 1; - } + msg = nlmsg_alloc(); + if (!msg) + return -1; - return 0; + nl80211_cmd(drv, msg, 0, NL80211_CMD_REGISTER_BEACONS); + + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, w->wiphy_idx); + + ret = send_and_recv(drv->global, w->nl_beacons, msg, NULL, NULL); + msg = NULL; + if (ret) { + wpa_printf(MSG_DEBUG, "nl80211: Register beacons command " + "failed: ret=%d (%s)", + ret, strerror(-ret)); + goto nla_put_failure; + } + ret = 0; +nla_put_failure: + nlmsg_free(msg); + return ret; } -static struct wpa_driver_nl80211_data * -nl80211_find_drv(struct nl80211_global *global, int idx, u8 *buf, size_t len) +static void nl80211_recv_beacons(int sock, void *eloop_ctx, void *handle) { - struct wpa_driver_nl80211_data *drv; - dl_list_for_each(drv, &global->interfaces, - struct wpa_driver_nl80211_data, list) { - if (wpa_driver_nl80211_own_ifindex(drv, idx, buf, len) || - have_ifidx(drv, idx)) - return drv; + struct nl80211_wiphy_data *w = eloop_ctx; + int res; + + wpa_printf(MSG_EXCESSIVE, "nl80211: Beacon event message available"); + + res = nl_recvmsgs(handle, w->nl_cb); + if (res) { + wpa_printf(MSG_INFO, "nl80211: %s->nl_recvmsgs failed: %d", + __func__, res); } - return NULL; } -static void wpa_driver_nl80211_event_rtm_newlink(void *ctx, - struct ifinfomsg *ifi, - u8 *buf, size_t len) +static int process_beacon_event(struct nl_msg *msg, void *arg) { - struct nl80211_global *global = ctx; + struct nl80211_wiphy_data *w = arg; struct wpa_driver_nl80211_data *drv; - int attrlen, rta_len; - struct rtattr *attr; - u32 brid = 0; - char namebuf[IFNAMSIZ]; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + union wpa_event_data event; - drv = nl80211_find_drv(global, ifi->ifi_index, buf, len); - if (!drv) { - wpa_printf(MSG_DEBUG, "nl80211: Ignore event for foreign " - "ifindex %d", ifi->ifi_index); - return; + nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + + if (gnlh->cmd != NL80211_CMD_FRAME) { + wpa_printf(MSG_DEBUG, "nl80211: Unexpected beacon event? (%d)", + gnlh->cmd); + return NL_SKIP; } - wpa_printf(MSG_DEBUG, "RTM_NEWLINK: operstate=%d ifi_flags=0x%x " - "(%s%s%s%s)", - drv->operstate, 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 (!tb[NL80211_ATTR_FRAME]) + return NL_SKIP; - if (!drv->if_disabled && !(ifi->ifi_flags & IFF_UP)) { + dl_list_for_each(drv, &w->drvs, struct wpa_driver_nl80211_data, + wiphy_list) { + os_memset(&event, 0, sizeof(event)); + event.rx_mgmt.frame = nla_data(tb[NL80211_ATTR_FRAME]); + event.rx_mgmt.frame_len = nla_len(tb[NL80211_ATTR_FRAME]); + wpa_supplicant_event(drv->ctx, EVENT_RX_MGMT, &event); + } + + return NL_SKIP; +} + + +static struct nl80211_wiphy_data * +nl80211_get_wiphy_data_ap(struct i802_bss *bss) +{ + static DEFINE_DL_LIST(nl80211_wiphys); + struct nl80211_wiphy_data *w; + int wiphy_idx, found = 0; + struct i802_bss *tmp_bss; + + if (bss->wiphy_data != NULL) + return bss->wiphy_data; + + wiphy_idx = nl80211_get_wiphy_index(bss); + + dl_list_for_each(w, &nl80211_wiphys, struct nl80211_wiphy_data, list) { + if (w->wiphy_idx == wiphy_idx) + goto add; + } + + /* alloc new one */ + w = os_zalloc(sizeof(*w)); + if (w == NULL) + return NULL; + w->wiphy_idx = wiphy_idx; + dl_list_init(&w->bsss); + dl_list_init(&w->drvs); + + w->nl_cb = nl_cb_alloc(NL_CB_DEFAULT); + if (!w->nl_cb) { + os_free(w); + return NULL; + } + nl_cb_set(w->nl_cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, no_seq_check, NULL); + nl_cb_set(w->nl_cb, NL_CB_VALID, NL_CB_CUSTOM, process_beacon_event, + w); + + w->nl_beacons = nl_create_handle(bss->drv->global->nl_cb, + "wiphy beacons"); + if (w->nl_beacons == NULL) { + os_free(w); + return NULL; + } + + if (nl80211_register_beacons(bss->drv, w)) { + nl_destroy_handles(&w->nl_beacons); + os_free(w); + return NULL; + } + + nl80211_register_eloop_read(&w->nl_beacons, nl80211_recv_beacons, w); + + dl_list_add(&nl80211_wiphys, &w->list); + +add: + /* drv entry for this bss already there? */ + dl_list_for_each(tmp_bss, &w->bsss, struct i802_bss, wiphy_list) { + if (tmp_bss->drv == bss->drv) { + found = 1; + break; + } + } + /* if not add it */ + if (!found) + dl_list_add(&w->drvs, &bss->drv->wiphy_list); + + dl_list_add(&w->bsss, &bss->wiphy_list); + bss->wiphy_data = w; + return w; +} + + +static void nl80211_put_wiphy_data_ap(struct i802_bss *bss) +{ + struct nl80211_wiphy_data *w = bss->wiphy_data; + struct i802_bss *tmp_bss; + int found = 0; + + if (w == NULL) + return; + bss->wiphy_data = NULL; + dl_list_del(&bss->wiphy_list); + + /* still any for this drv present? */ + dl_list_for_each(tmp_bss, &w->bsss, struct i802_bss, wiphy_list) { + if (tmp_bss->drv == bss->drv) { + found = 1; + break; + } + } + /* if not remove it */ + if (!found) + dl_list_del(&bss->drv->wiphy_list); + + if (!dl_list_empty(&w->bsss)) + return; + + nl80211_destroy_eloop_handle(&w->nl_beacons); + + nl_cb_put(w->nl_cb); + dl_list_del(&w->list); + os_free(w); +} + + +static int wpa_driver_nl80211_get_bssid(void *priv, u8 *bssid) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + if (!drv->associated) + return -1; + os_memcpy(bssid, drv->bssid, ETH_ALEN); + return 0; +} + + +static int wpa_driver_nl80211_get_ssid(void *priv, u8 *ssid) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + if (!drv->associated) + return -1; + os_memcpy(ssid, drv->ssid, drv->ssid_len); + return drv->ssid_len; +} + + +static void wpa_driver_nl80211_event_newlink( + struct wpa_driver_nl80211_data *drv, char *ifname) +{ + union wpa_event_data event; + + if (os_strcmp(drv->first_bss->ifname, ifname) == 0) { + if (if_nametoindex(drv->first_bss->ifname) == 0) { + wpa_printf(MSG_DEBUG, "nl80211: Interface %s does not exist - ignore RTM_NEWLINK", + drv->first_bss->ifname); + return; + } + if (!drv->if_removed) + return; + wpa_printf(MSG_DEBUG, "nl80211: Mark if_removed=0 for %s based on RTM_NEWLINK event", + drv->first_bss->ifname); + drv->if_removed = 0; + } + + os_memset(&event, 0, sizeof(event)); + os_strlcpy(event.interface_status.ifname, ifname, + sizeof(event.interface_status.ifname)); + event.interface_status.ievent = EVENT_INTERFACE_ADDED; + wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS, &event); +} + + +static void wpa_driver_nl80211_event_dellink( + struct wpa_driver_nl80211_data *drv, char *ifname) +{ + union wpa_event_data event; + + if (os_strcmp(drv->first_bss->ifname, ifname) == 0) { + if (drv->if_removed) { + wpa_printf(MSG_DEBUG, "nl80211: if_removed already set - ignore RTM_DELLINK event for %s", + ifname); + return; + } + wpa_printf(MSG_DEBUG, "RTM_DELLINK: Interface '%s' removed - mark if_removed=1", + ifname); + drv->if_removed = 1; + } else { + wpa_printf(MSG_DEBUG, "RTM_DELLINK: Interface '%s' removed", + ifname); + } + + os_memset(&event, 0, sizeof(event)); + os_strlcpy(event.interface_status.ifname, ifname, + sizeof(event.interface_status.ifname)); + event.interface_status.ievent = EVENT_INTERFACE_REMOVED; + wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS, &event); +} + + +static int wpa_driver_nl80211_own_ifname(struct wpa_driver_nl80211_data *drv, + u8 *buf, size_t len) +{ + int attrlen, rta_len; + struct rtattr *attr; + + attrlen = len; + attr = (struct rtattr *) buf; + + rta_len = RTA_ALIGN(sizeof(struct rtattr)); + while (RTA_OK(attr, attrlen)) { + if (attr->rta_type == IFLA_IFNAME) { + if (os_strcmp(((char *) attr) + rta_len, + drv->first_bss->ifname) == 0) + return 1; + else + break; + } + attr = RTA_NEXT(attr, attrlen); + } + + return 0; +} + + +static int wpa_driver_nl80211_own_ifindex(struct wpa_driver_nl80211_data *drv, + int ifindex, u8 *buf, size_t len) +{ + if (drv->ifindex == ifindex) + return 1; + + if (drv->if_removed && wpa_driver_nl80211_own_ifname(drv, buf, len)) { + wpa_printf(MSG_DEBUG, "nl80211: Update ifindex for a removed " + "interface"); + wpa_driver_nl80211_finish_drv_init(drv, NULL, 0); + return 1; + } + + return 0; +} + + +static struct wpa_driver_nl80211_data * +nl80211_find_drv(struct nl80211_global *global, int idx, u8 *buf, size_t len) +{ + struct wpa_driver_nl80211_data *drv; + dl_list_for_each(drv, &global->interfaces, + struct wpa_driver_nl80211_data, list) { + if (wpa_driver_nl80211_own_ifindex(drv, idx, buf, len) || + have_ifidx(drv, idx)) + return drv; + } + return NULL; +} + + +static void wpa_driver_nl80211_event_rtm_newlink(void *ctx, + struct ifinfomsg *ifi, + u8 *buf, size_t len) +{ + struct nl80211_global *global = ctx; + struct wpa_driver_nl80211_data *drv; + int attrlen; + struct rtattr *attr; + u32 brid = 0; + char namebuf[IFNAMSIZ]; + char ifname[IFNAMSIZ + 1]; + char extra[100], *pos, *end; + + drv = nl80211_find_drv(global, ifi->ifi_index, buf, len); + if (!drv) { + wpa_printf(MSG_DEBUG, "nl80211: Ignore RTM_NEWLINK event for foreign ifindex %d", + ifi->ifi_index); + return; + } + + extra[0] = '\0'; + pos = extra; + end = pos + sizeof(extra); + ifname[0] = '\0'; + + attrlen = len; + attr = (struct rtattr *) buf; + while (RTA_OK(attr, attrlen)) { + switch (attr->rta_type) { + case IFLA_IFNAME: + if (RTA_PAYLOAD(attr) >= IFNAMSIZ) + break; + os_memcpy(ifname, RTA_DATA(attr), RTA_PAYLOAD(attr)); + ifname[RTA_PAYLOAD(attr)] = '\0'; + break; + case IFLA_MASTER: + brid = nla_get_u32((struct nlattr *) attr); + pos += os_snprintf(pos, end - pos, " master=%u", brid); + break; + case IFLA_WIRELESS: + pos += os_snprintf(pos, end - pos, " wext"); + break; + case IFLA_OPERSTATE: + pos += os_snprintf(pos, end - pos, " operstate=%u", + nla_get_u32((struct nlattr *) attr)); + break; + case IFLA_LINKMODE: + pos += os_snprintf(pos, end - pos, " linkmode=%u", + nla_get_u32((struct nlattr *) attr)); + break; + } + attr = RTA_NEXT(attr, attrlen); + } + extra[sizeof(extra) - 1] = '\0'; + + wpa_printf(MSG_DEBUG, "RTM_NEWLINK: ifi_index=%d ifname=%s%s ifi_flags=0x%x (%s%s%s%s)", + ifi->ifi_index, ifname, extra, 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 (!drv->if_disabled && !(ifi->ifi_flags & IFF_UP)) { if (if_indextoname(ifi->ifi_index, namebuf) && linux_iface_up(drv->global->ioctl_sock, - drv->first_bss.ifname) > 0) { + drv->first_bss->ifname) > 0) { wpa_printf(MSG_DEBUG, "nl80211: Ignore interface down " "event since interface %s is up", namebuf); return; @@ -650,18 +1244,18 @@ if (drv->if_disabled && (ifi->ifi_flags & IFF_UP)) { if (if_indextoname(ifi->ifi_index, namebuf) && linux_iface_up(drv->global->ioctl_sock, - drv->first_bss.ifname) == 0) { + drv->first_bss->ifname) == 0) { wpa_printf(MSG_DEBUG, "nl80211: Ignore interface up " "event since interface %s is down", namebuf); - } else if (if_nametoindex(drv->first_bss.ifname) == 0) { + } else if (if_nametoindex(drv->first_bss->ifname) == 0) { wpa_printf(MSG_DEBUG, "nl80211: Ignore interface up " "event since interface %s does not exist", - drv->first_bss.ifname); + drv->first_bss->ifname); } else if (drv->if_removed) { wpa_printf(MSG_DEBUG, "nl80211: Ignore interface up " "event since interface %s is marked " - "removed", drv->first_bss.ifname); + "removed", drv->first_bss->ifname); } else { wpa_printf(MSG_DEBUG, "nl80211: Interface up"); drv->if_disabled = 0; @@ -678,24 +1272,15 @@ */ if (drv->operstate == 1 && (ifi->ifi_flags & (IFF_LOWER_UP | IFF_DORMANT)) == IFF_LOWER_UP && - !(ifi->ifi_flags & IFF_RUNNING)) + !(ifi->ifi_flags & IFF_RUNNING)) { + wpa_printf(MSG_DEBUG, "nl80211: Set IF_OPER_UP again based on ifi_flags and expected operstate"); netlink_send_oper_ifla(drv->global->netlink, drv->ifindex, -1, IF_OPER_UP); - - attrlen = len; - attr = (struct rtattr *) buf; - rta_len = RTA_ALIGN(sizeof(struct rtattr)); - while (RTA_OK(attr, attrlen)) { - if (attr->rta_type == IFLA_IFNAME) { - wpa_driver_nl80211_event_link( - drv, - ((char *) attr) + rta_len, - attr->rta_len - rta_len, 0); - } else if (attr->rta_type == IFLA_MASTER) - brid = nla_get_u32((struct nlattr *) attr); - attr = RTA_NEXT(attr, attrlen); } + if (ifname[0]) + wpa_driver_nl80211_event_newlink(drv, ifname); + if (ifi->ifi_family == AF_BRIDGE && brid) { /* device has been added to bridge */ if_indextoname(brid, namebuf); @@ -712,32 +1297,40 @@ { struct nl80211_global *global = ctx; struct wpa_driver_nl80211_data *drv; - int attrlen, rta_len; + int attrlen; struct rtattr *attr; u32 brid = 0; + char ifname[IFNAMSIZ + 1]; drv = nl80211_find_drv(global, ifi->ifi_index, buf, len); if (!drv) { - wpa_printf(MSG_DEBUG, "nl80211: Ignore dellink event for " - "foreign ifindex %d", ifi->ifi_index); + wpa_printf(MSG_DEBUG, "nl80211: Ignore RTM_DELLINK event for foreign ifindex %d", + ifi->ifi_index); return; } + ifname[0] = '\0'; + attrlen = len; attr = (struct rtattr *) buf; - - rta_len = RTA_ALIGN(sizeof(struct rtattr)); while (RTA_OK(attr, attrlen)) { - if (attr->rta_type == IFLA_IFNAME) { - wpa_driver_nl80211_event_link( - drv, - ((char *) attr) + rta_len, - attr->rta_len - rta_len, 1); - } else if (attr->rta_type == IFLA_MASTER) + switch (attr->rta_type) { + case IFLA_IFNAME: + if (RTA_PAYLOAD(attr) >= IFNAMSIZ) + break; + os_memcpy(ifname, RTA_DATA(attr), RTA_PAYLOAD(attr)); + ifname[RTA_PAYLOAD(attr)] = '\0'; + break; + case IFLA_MASTER: brid = nla_get_u32((struct nlattr *) attr); + break; + } attr = RTA_NEXT(attr, attrlen); } + if (ifname[0]) + wpa_driver_nl80211_event_dellink(drv, ifname); + if (ifi->ifi_family == AF_BRIDGE && brid) { /* device has been removed from bridge */ char namebuf[IFNAMSIZ]; @@ -755,6 +1348,7 @@ const struct ieee80211_mgmt *mgmt; union wpa_event_data event; + wpa_printf(MSG_DEBUG, "nl80211: Authenticate event"); mgmt = (const struct ieee80211_mgmt *) frame; if (len < 24 + sizeof(mgmt->u.auth)) { wpa_printf(MSG_DEBUG, "nl80211: Too short association event " @@ -763,9 +1357,12 @@ } os_memcpy(drv->auth_bssid, mgmt->sa, ETH_ALEN); + os_memset(drv->auth_attempt_bssid, 0, ETH_ALEN); os_memset(&event, 0, sizeof(event)); os_memcpy(event.auth.peer, mgmt->sa, ETH_ALEN); event.auth.auth_type = le_to_host16(mgmt->u.auth.auth_alg); + event.auth.auth_transaction = + le_to_host16(mgmt->u.auth.auth_transaction); event.auth.status_code = le_to_host16(mgmt->u.auth.status_code); if (len > 24 + sizeof(mgmt->u.auth)) { event.auth.ies = mgmt->u.auth.variable; @@ -797,7 +1394,9 @@ wpa_printf(MSG_DEBUG, "nl80211: Operating frequency for the " "associated BSS from scan results: %u MHz", arg.assoc_freq); - return arg.assoc_freq ? arg.assoc_freq : drv->assoc_freq; + if (arg.assoc_freq) + drv->assoc_freq = arg.assoc_freq; + return drv->assoc_freq; } wpa_printf(MSG_DEBUG, "nl80211: Scan result fetch failed: ret=%d " "(%s)", ret, strerror(-ret)); @@ -814,6 +1413,7 @@ union wpa_event_data event; u16 status; + wpa_printf(MSG_DEBUG, "nl80211: Associate event"); mgmt = (const struct ieee80211_mgmt *) frame; if (len < 24 + sizeof(mgmt->u.assoc_resp)) { wpa_printf(MSG_DEBUG, "nl80211: Too short association event " @@ -839,6 +1439,7 @@ drv->associated = 1; os_memcpy(drv->bssid, mgmt->sa, ETH_ALEN); + os_memcpy(drv->prev_bssid, mgmt->sa, ETH_ALEN); os_memset(&event, 0, sizeof(event)); if (len > 24 + sizeof(mgmt->u.assoc_resp)) { @@ -870,6 +1471,11 @@ return; } + if (cmd == NL80211_CMD_CONNECT) + wpa_printf(MSG_DEBUG, "nl80211: Connect event"); + else if (cmd == NL80211_CMD_ROAM) + wpa_printf(MSG_DEBUG, "nl80211: Roam event"); + os_memset(&event, 0, sizeof(event)); if (cmd == NL80211_CMD_CONNECT && nla_get_u16(status) != WLAN_STATUS_SUCCESS) { @@ -885,8 +1491,10 @@ } drv->associated = 1; - if (addr) + if (addr) { os_memcpy(drv->bssid, nla_data(addr), ETH_ALEN); + os_memcpy(drv->prev_bssid, drv->bssid, ETH_ALEN); + } if (req_ie) { event.assoc_info.req_ies = nla_data(req_ie); @@ -904,9 +1512,11 @@ static void mlme_event_disconnect(struct wpa_driver_nl80211_data *drv, - struct nlattr *reason, struct nlattr *addr) + struct nlattr *reason, struct nlattr *addr, + struct nlattr *by_ap) { union wpa_event_data data; + unsigned int locally_generated = by_ap == NULL; if (drv->capa.flags & WPA_DRIVER_FLAGS_SME) { /* @@ -918,11 +1528,120 @@ return; } - drv->associated = 0; + if (drv->ignore_next_local_disconnect) { + drv->ignore_next_local_disconnect = 0; + if (locally_generated) { + wpa_printf(MSG_DEBUG, "nl80211: Ignore disconnect " + "event triggered during reassociation"); + return; + } + wpa_printf(MSG_WARNING, "nl80211: Was expecting local " + "disconnect but got another disconnect " + "event first"); + } + + wpa_printf(MSG_DEBUG, "nl80211: Disconnect event"); + nl80211_mark_disconnected(drv); os_memset(&data, 0, sizeof(data)); if (reason) - data.disassoc_info.reason_code = nla_get_u16(reason); - wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, &data); + data.deauth_info.reason_code = nla_get_u16(reason); + data.deauth_info.locally_generated = by_ap == NULL; + wpa_supplicant_event(drv->ctx, EVENT_DEAUTH, &data); +} + + +static int calculate_chan_offset(int width, int freq, int cf1, int cf2) +{ + int freq1 = 0; + + switch (convert2width(width)) { + case CHAN_WIDTH_20_NOHT: + case CHAN_WIDTH_20: + return 0; + case CHAN_WIDTH_40: + freq1 = cf1 - 10; + break; + case CHAN_WIDTH_80: + freq1 = cf1 - 30; + break; + case CHAN_WIDTH_160: + freq1 = cf1 - 70; + break; + case CHAN_WIDTH_UNKNOWN: + case CHAN_WIDTH_80P80: + /* FIXME: implement this */ + return 0; + } + + return (abs(freq - freq1) / 20) % 2 == 0 ? 1 : -1; +} + + +static void mlme_event_ch_switch(struct wpa_driver_nl80211_data *drv, + struct nlattr *ifindex, struct nlattr *freq, + struct nlattr *type, struct nlattr *bw, + struct nlattr *cf1, struct nlattr *cf2) +{ + struct i802_bss *bss; + union wpa_event_data data; + int ht_enabled = 1; + int chan_offset = 0; + int ifidx; + + wpa_printf(MSG_DEBUG, "nl80211: Channel switch event"); + + if (!freq) + return; + + ifidx = nla_get_u32(ifindex); + for (bss = drv->first_bss; bss; bss = bss->next) + if (bss->ifindex == ifidx) + break; + + if (bss == NULL) { + wpa_printf(MSG_WARNING, "nl80211: Unknown ifindex (%d) for channel switch, ignoring", + ifidx); + return; + } + + if (type) { + switch (nla_get_u32(type)) { + case NL80211_CHAN_NO_HT: + ht_enabled = 0; + break; + case NL80211_CHAN_HT20: + break; + case NL80211_CHAN_HT40PLUS: + chan_offset = 1; + break; + case NL80211_CHAN_HT40MINUS: + chan_offset = -1; + break; + } + } else if (bw && cf1) { + /* This can happen for example with VHT80 ch switch */ + chan_offset = calculate_chan_offset(nla_get_u32(bw), + nla_get_u32(freq), + nla_get_u32(cf1), + cf2 ? nla_get_u32(cf2) : 0); + } else { + wpa_printf(MSG_WARNING, "nl80211: Unknown secondary channel information - following channel definition calculations may fail"); + } + + os_memset(&data, 0, sizeof(data)); + data.ch_switch.freq = nla_get_u32(freq); + data.ch_switch.ht_enabled = ht_enabled; + data.ch_switch.ch_offset = chan_offset; + if (bw) + data.ch_switch.ch_width = convert2width(nla_get_u32(bw)); + if (cf1) + data.ch_switch.cf1 = nla_get_u32(cf1); + if (cf2) + data.ch_switch.cf2 = nla_get_u32(cf2); + + bss->freq = data.ch_switch.freq; + + wpa_supplicant_event(drv->ctx, EVENT_CH_SWITCH, &data); } @@ -952,62 +1671,67 @@ static void mlme_event_mgmt(struct wpa_driver_nl80211_data *drv, - struct nlattr *freq, const u8 *frame, size_t len) + struct nlattr *freq, struct nlattr *sig, + const u8 *frame, size_t len) { const struct ieee80211_mgmt *mgmt; union wpa_event_data event; u16 fc, stype; + int ssi_signal = 0; + int rx_freq = 0; + wpa_printf(MSG_MSGDUMP, "nl80211: Frame event"); mgmt = (const struct ieee80211_mgmt *) frame; if (len < 24) { - wpa_printf(MSG_DEBUG, "nl80211: Too short action frame"); + wpa_printf(MSG_DEBUG, "nl80211: Too short management frame"); return; } fc = le_to_host16(mgmt->frame_control); stype = WLAN_FC_GET_STYPE(fc); + if (sig) + ssi_signal = (s32) nla_get_u32(sig); + os_memset(&event, 0, sizeof(event)); if (freq) { - event.rx_action.freq = nla_get_u32(freq); - drv->last_mgmt_freq = event.rx_action.freq; - } - if (stype == WLAN_FC_STYPE_ACTION) { - event.rx_action.da = mgmt->da; - event.rx_action.sa = mgmt->sa; - event.rx_action.bssid = mgmt->bssid; - event.rx_action.category = mgmt->u.action.category; - event.rx_action.data = &mgmt->u.action.category + 1; - event.rx_action.len = frame + len - event.rx_action.data; - wpa_supplicant_event(drv->ctx, EVENT_RX_ACTION, &event); - } else { - event.rx_mgmt.frame = frame; - event.rx_mgmt.frame_len = len; - wpa_supplicant_event(drv->ctx, EVENT_RX_MGMT, &event); + event.rx_mgmt.freq = nla_get_u32(freq); + rx_freq = drv->last_mgmt_freq = event.rx_mgmt.freq; } + wpa_printf(MSG_DEBUG, + "nl80211: RX frame freq=%d ssi_signal=%d stype=%u len=%u", + rx_freq, ssi_signal, stype, (unsigned int) len); + event.rx_mgmt.frame = frame; + event.rx_mgmt.frame_len = len; + event.rx_mgmt.ssi_signal = ssi_signal; + wpa_supplicant_event(drv->ctx, EVENT_RX_MGMT, &event); } -static void mlme_event_action_tx_status(struct wpa_driver_nl80211_data *drv, - struct nlattr *cookie, const u8 *frame, - size_t len, struct nlattr *ack) +static void mlme_event_mgmt_tx_status(struct wpa_driver_nl80211_data *drv, + struct nlattr *cookie, const u8 *frame, + size_t len, struct nlattr *ack) { union wpa_event_data event; const struct ieee80211_hdr *hdr; u16 fc; - u64 cookie_val; - if (!cookie) - return; + wpa_printf(MSG_DEBUG, "nl80211: Frame TX status event"); + if (!is_ap_interface(drv->nlmode)) { + u64 cookie_val; - cookie_val = nla_get_u64(cookie); - wpa_printf(MSG_DEBUG, "nl80211: Action TX status: cookie=0%llx%s " - "(ack=%d)", - (long long unsigned int) cookie_val, - cookie_val == drv->send_action_cookie ? - " (match)" : " (unknown)", ack != NULL); - if (cookie_val != drv->send_action_cookie) - return; + if (!cookie) + return; + + cookie_val = nla_get_u64(cookie); + wpa_printf(MSG_DEBUG, "nl80211: Action TX status:" + " cookie=0%llx%s (ack=%d)", + (long long unsigned int) cookie_val, + cookie_val == drv->send_action_cookie ? + " (match)" : " (unknown)", ack != NULL); + if (cookie_val != drv->send_action_cookie) + return; + } hdr = (const struct ieee80211_hdr *) frame; fc = le_to_host16(hdr->frame_control); @@ -1032,10 +1756,31 @@ const u8 *bssid = NULL; u16 reason_code = 0; + if (type == EVENT_DEAUTH) + wpa_printf(MSG_DEBUG, "nl80211: Deauthenticate event"); + else + wpa_printf(MSG_DEBUG, "nl80211: Disassociate event"); + mgmt = (const struct ieee80211_mgmt *) frame; if (len >= 24) { bssid = mgmt->bssid; + if ((drv->capa.flags & WPA_DRIVER_FLAGS_SME) && + !drv->associated && + os_memcmp(bssid, drv->auth_bssid, ETH_ALEN) != 0 && + os_memcmp(bssid, drv->auth_attempt_bssid, ETH_ALEN) != 0 && + os_memcmp(bssid, drv->prev_bssid, ETH_ALEN) == 0) { + /* + * Avoid issues with some roaming cases where + * disconnection event for the old AP may show up after + * we have started connection with the new AP. + */ + wpa_printf(MSG_DEBUG, "nl80211: Ignore deauth/disassoc event from old AP " MACSTR " when already authenticating with " MACSTR, + MAC2STR(bssid), + MAC2STR(drv->auth_attempt_bssid)); + return; + } + if (drv->associated != 0 && os_memcmp(bssid, drv->bssid, ETH_ALEN) != 0 && os_memcmp(bssid, drv->auth_bssid, ETH_ALEN) != 0) { @@ -1051,7 +1796,7 @@ } } - drv->associated = 0; + nl80211_mark_disconnected(drv); os_memset(&event, 0, sizeof(event)); /* Note: Same offset for Reason Code in both frame subtypes */ @@ -1059,6 +1804,8 @@ reason_code = le_to_host16(mgmt->u.deauth.reason_code); if (type == EVENT_DISASSOC) { + event.disassoc_info.locally_generated = + !os_memcmp(mgmt->sa, drv->first_bss->addr, ETH_ALEN); event.disassoc_info.addr = bssid; event.disassoc_info.reason_code = reason_code; if (frame + len > mgmt->u.disassoc.variable) { @@ -1067,6 +1814,8 @@ mgmt->u.disassoc.variable; } } else { + event.deauth_info.locally_generated = + !os_memcmp(mgmt->sa, drv->first_bss->addr, ETH_ALEN); event.deauth_info.addr = bssid; event.deauth_info.reason_code = reason_code; if (frame + len > mgmt->u.deauth.variable) { @@ -1088,6 +1837,11 @@ union wpa_event_data event; u16 reason_code = 0; + if (type == EVENT_UNPROT_DEAUTH) + wpa_printf(MSG_DEBUG, "nl80211: Unprot Deauthenticate event"); + else + wpa_printf(MSG_DEBUG, "nl80211: Unprot Disassociate event"); + if (len < 24) return; @@ -1112,24 +1866,49 @@ } -static void mlme_event(struct wpa_driver_nl80211_data *drv, +static void mlme_event(struct i802_bss *bss, enum nl80211_commands cmd, struct nlattr *frame, struct nlattr *addr, struct nlattr *timed_out, struct nlattr *freq, struct nlattr *ack, - struct nlattr *cookie) + struct nlattr *cookie, struct nlattr *sig) { + struct wpa_driver_nl80211_data *drv = bss->drv; + const u8 *data; + size_t len; + if (timed_out && addr) { mlme_timeout_event(drv, cmd, addr); return; } if (frame == NULL) { - wpa_printf(MSG_DEBUG, "nl80211: MLME event %d without frame " - "data", cmd); + wpa_printf(MSG_DEBUG, + "nl80211: MLME event %d (%s) without frame data", + cmd, nl80211_command_to_string(cmd)); return; } - wpa_printf(MSG_DEBUG, "nl80211: MLME event %d", cmd); + data = nla_data(frame); + len = nla_len(frame); + if (len < 4 + 2 * ETH_ALEN) { + wpa_printf(MSG_MSGDUMP, "nl80211: MLME event %d (%s) on %s(" + MACSTR ") - too short", + cmd, nl80211_command_to_string(cmd), bss->ifname, + MAC2STR(bss->addr)); + return; + } + wpa_printf(MSG_MSGDUMP, "nl80211: MLME event %d (%s) on %s(" MACSTR + ") A1=" MACSTR " A2=" MACSTR, cmd, + nl80211_command_to_string(cmd), bss->ifname, + MAC2STR(bss->addr), MAC2STR(data + 4), + MAC2STR(data + 4 + ETH_ALEN)); + if (cmd != NL80211_CMD_FRAME_TX_STATUS && !(data[4] & 0x01) && + os_memcmp(bss->addr, data + 4, ETH_ALEN) != 0 && + os_memcmp(bss->addr, data + 4 + ETH_ALEN, ETH_ALEN) != 0) { + wpa_printf(MSG_MSGDUMP, "nl80211: %s: Ignore MLME frame event " + "for foreign address", bss->ifname); + return; + } wpa_hexdump(MSG_MSGDUMP, "nl80211: MLME event frame", nla_data(frame), nla_len(frame)); @@ -1149,11 +1928,12 @@ nla_data(frame), nla_len(frame)); break; case NL80211_CMD_FRAME: - mlme_event_mgmt(drv, freq, nla_data(frame), nla_len(frame)); + mlme_event_mgmt(drv, freq, sig, nla_data(frame), + nla_len(frame)); break; case NL80211_CMD_FRAME_TX_STATUS: - mlme_event_action_tx_status(drv, cookie, nla_data(frame), - nla_len(frame), ack); + mlme_event_mgmt_tx_status(drv, cookie, nla_data(frame), + nla_len(frame), ack); break; case NL80211_CMD_UNPROT_DEAUTHENTICATE: mlme_event_unprot_disconnect(drv, EVENT_UNPROT_DEAUTH, @@ -1169,7 +1949,7 @@ } -static void mlme_event_michael_mic_failure(struct wpa_driver_nl80211_data *drv, +static void mlme_event_michael_mic_failure(struct i802_bss *bss, struct nlattr *tb[]) { union wpa_event_data data; @@ -1201,7 +1981,7 @@ wpa_printf(MSG_DEBUG, "nl80211: Key Id %d", key_id); } - wpa_supplicant_event(drv->ctx, EVENT_MICHAEL_MIC_FAILURE, &data); + wpa_supplicant_event(bss->ctx, EVENT_MICHAEL_MIC_FAILURE, &data); } @@ -1214,6 +1994,7 @@ return; } os_memcpy(drv->bssid, nla_data(tb[NL80211_ATTR_MAC]), ETH_ALEN); + drv->associated = 1; wpa_printf(MSG_DEBUG, "nl80211: IBSS " MACSTR " joined", MAC2STR(drv->bssid)); @@ -1270,6 +2051,34 @@ } +static void mlme_event_ft_event(struct wpa_driver_nl80211_data *drv, + struct nlattr *tb[]) +{ + union wpa_event_data data; + + os_memset(&data, 0, sizeof(data)); + + if (tb[NL80211_ATTR_IE]) { + data.ft_ies.ies = nla_data(tb[NL80211_ATTR_IE]); + data.ft_ies.ies_len = nla_len(tb[NL80211_ATTR_IE]); + } + + if (tb[NL80211_ATTR_IE_RIC]) { + data.ft_ies.ric_ies = nla_data(tb[NL80211_ATTR_IE_RIC]); + data.ft_ies.ric_ies_len = nla_len(tb[NL80211_ATTR_IE_RIC]); + } + + if (tb[NL80211_ATTR_MAC]) + os_memcpy(data.ft_ies.target_ap, + nla_data(tb[NL80211_ATTR_MAC]), ETH_ALEN); + + wpa_printf(MSG_DEBUG, "nl80211: FT event target_ap " MACSTR, + MAC2STR(data.ft_ies.target_ap)); + + wpa_supplicant_event(drv->ctx, EVENT_FT_RESPONSE, &data); +} + + static void send_scan_event(struct wpa_driver_nl80211_data *drv, int aborted, struct nlattr *tb[]) { @@ -1281,6 +2090,14 @@ int freqs[MAX_REPORT_FREQS]; int num_freqs = 0; + if (drv->scan_for_auth) { + drv->scan_for_auth = 0; + wpa_printf(MSG_DEBUG, "nl80211: Scan results for missing " + "cfg80211 BSS entry"); + wpa_driver_nl80211_authenticate_retry(drv); + return; + } + os_memset(&event, 0, sizeof(event)); info = &event.scan_info; info->aborted = aborted; @@ -1291,21 +2108,36 @@ &info->ssids[info->num_ssids]; s->ssid = nla_data(nl); s->ssid_len = nla_len(nl); + wpa_printf(MSG_DEBUG, "nl80211: Scan probed for SSID '%s'", + wpa_ssid_txt(s->ssid, s->ssid_len)); info->num_ssids++; if (info->num_ssids == WPAS_MAX_SCAN_SSIDS) break; } } if (tb[NL80211_ATTR_SCAN_FREQUENCIES]) { + char msg[200], *pos, *end; + int res; + + pos = msg; + end = pos + sizeof(msg); + *pos = '\0'; + nla_for_each_nested(nl, tb[NL80211_ATTR_SCAN_FREQUENCIES], rem) { freqs[num_freqs] = nla_get_u32(nl); + res = os_snprintf(pos, end - pos, " %d", + freqs[num_freqs]); + if (res > 0 && end - pos > res) + pos += res; num_freqs++; if (num_freqs == MAX_REPORT_FREQS - 1) break; } info->freqs = freqs; info->num_freqs = num_freqs; + wpa_printf(MSG_DEBUG, "nl80211: Scan included frequencies:%s", + msg); } wpa_supplicant_event(drv->ctx, EVENT_SCAN_RESULTS, &event); } @@ -1318,6 +2150,7 @@ struct nlattr *sinfo[NL80211_STA_INFO_MAX + 1]; static struct nla_policy policy[NL80211_STA_INFO_MAX + 1] = { [NL80211_STA_INFO_SIGNAL] = { .type = NLA_U8 }, + [NL80211_STA_INFO_SIGNAL_AVG] = { .type = NLA_U8 }, }; struct nlattr *rinfo[NL80211_RATE_INFO_MAX + 1]; static struct nla_policy rate_policy[NL80211_RATE_INFO_MAX + 1] = { @@ -1340,6 +2173,12 @@ sig_change->current_signal = (s8) nla_get_u8(sinfo[NL80211_STA_INFO_SIGNAL]); + if (sinfo[NL80211_STA_INFO_SIGNAL_AVG]) + sig_change->avg_signal = + (s8) nla_get_u8(sinfo[NL80211_STA_INFO_SIGNAL_AVG]); + else + sig_change->avg_signal = 0; + if (sinfo[NL80211_STA_INFO_TX_BITRATE]) { if (nla_parse_nested(rinfo, NL80211_RATE_INFO_MAX, sinfo[NL80211_STA_INFO_TX_BITRATE], @@ -1602,7 +2441,7 @@ addr = nla_data(tb[NL80211_ATTR_MAC]); wpa_printf(MSG_DEBUG, "nl80211: New station " MACSTR, MAC2STR(addr)); - if (is_ap_interface(drv->nlmode) && drv->no_monitor_iface_capab) { + if (is_ap_interface(drv->nlmode) && drv->device_ap_sme) { u8 *ies = NULL; size_t ies_len = 0; if (tb[NL80211_ATTR_IE]) { @@ -1635,7 +2474,7 @@ wpa_printf(MSG_DEBUG, "nl80211: Delete station " MACSTR, MAC2STR(addr)); - if (is_ap_interface(drv->nlmode) && drv->no_monitor_iface_capab) { + if (is_ap_interface(drv->nlmode) && drv->device_ap_sme) { drv_event_disassoc(drv->ctx, addr); return; } @@ -1705,6 +2544,8 @@ }; union wpa_event_data data; + wpa_printf(MSG_DEBUG, "nl80211: PMKSA candidate event"); + if (!tb[NL80211_ATTR_PMKSA_CANDIDATE]) return; if (nla_parse_nested(cand, MAX_NL80211_PMKSA_CANDIDATE, @@ -1725,58 +2566,350 @@ } -static int process_event(struct nl_msg *msg, void *arg) +static void nl80211_client_probe_event(struct wpa_driver_nl80211_data *drv, + struct nlattr **tb) { - struct wpa_driver_nl80211_data *drv = arg; - struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); - struct nlattr *tb[NL80211_ATTR_MAX + 1]; + union wpa_event_data data; - nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), - genlmsg_attrlen(gnlh, 0), NULL); + wpa_printf(MSG_DEBUG, "nl80211: Probe client event"); - if (tb[NL80211_ATTR_IFINDEX]) { - int ifindex = nla_get_u32(tb[NL80211_ATTR_IFINDEX]); - if (ifindex != drv->ifindex && !have_ifidx(drv, ifindex)) { - wpa_printf(MSG_DEBUG, "nl80211: Ignored event (cmd=%d)" - " for foreign interface (ifindex %d)", - gnlh->cmd, ifindex); - return NL_SKIP; + if (!tb[NL80211_ATTR_MAC] || !tb[NL80211_ATTR_ACK]) + return; + + os_memset(&data, 0, sizeof(data)); + os_memcpy(data.client_poll.addr, + nla_data(tb[NL80211_ATTR_MAC]), ETH_ALEN); + + wpa_supplicant_event(drv->ctx, EVENT_DRIVER_CLIENT_POLL_OK, &data); +} + + +static void nl80211_tdls_oper_event(struct wpa_driver_nl80211_data *drv, + struct nlattr **tb) +{ + union wpa_event_data data; + + wpa_printf(MSG_DEBUG, "nl80211: TDLS operation event"); + + if (!tb[NL80211_ATTR_MAC] || !tb[NL80211_ATTR_TDLS_OPERATION]) + return; + + os_memset(&data, 0, sizeof(data)); + os_memcpy(data.tdls.peer, nla_data(tb[NL80211_ATTR_MAC]), ETH_ALEN); + switch (nla_get_u8(tb[NL80211_ATTR_TDLS_OPERATION])) { + case NL80211_TDLS_SETUP: + wpa_printf(MSG_DEBUG, "nl80211: TDLS setup request for peer " + MACSTR, MAC2STR(data.tdls.peer)); + data.tdls.oper = TDLS_REQUEST_SETUP; + break; + case NL80211_TDLS_TEARDOWN: + wpa_printf(MSG_DEBUG, "nl80211: TDLS teardown request for peer " + MACSTR, MAC2STR(data.tdls.peer)); + data.tdls.oper = TDLS_REQUEST_TEARDOWN; + break; + default: + wpa_printf(MSG_DEBUG, "nl80211: Unsupported TDLS operatione " + "event"); + return; + } + if (tb[NL80211_ATTR_REASON_CODE]) { + data.tdls.reason_code = + nla_get_u16(tb[NL80211_ATTR_REASON_CODE]); + } + + wpa_supplicant_event(drv->ctx, EVENT_TDLS, &data); +} + + +static void nl80211_stop_ap(struct wpa_driver_nl80211_data *drv, + struct nlattr **tb) +{ + wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_UNAVAILABLE, NULL); +} + + +static void nl80211_connect_failed_event(struct wpa_driver_nl80211_data *drv, + struct nlattr **tb) +{ + union wpa_event_data data; + u32 reason; + + wpa_printf(MSG_DEBUG, "nl80211: Connect failed event"); + + if (!tb[NL80211_ATTR_MAC] || !tb[NL80211_ATTR_CONN_FAILED_REASON]) + return; + + os_memset(&data, 0, sizeof(data)); + os_memcpy(data.connect_failed_reason.addr, + nla_data(tb[NL80211_ATTR_MAC]), ETH_ALEN); + + reason = nla_get_u32(tb[NL80211_ATTR_CONN_FAILED_REASON]); + switch (reason) { + case NL80211_CONN_FAIL_MAX_CLIENTS: + wpa_printf(MSG_DEBUG, "nl80211: Max client reached"); + data.connect_failed_reason.code = MAX_CLIENT_REACHED; + break; + case NL80211_CONN_FAIL_BLOCKED_CLIENT: + wpa_printf(MSG_DEBUG, "nl80211: Blocked client " MACSTR + " tried to connect", + MAC2STR(data.connect_failed_reason.addr)); + data.connect_failed_reason.code = BLOCKED_CLIENT; + break; + default: + wpa_printf(MSG_DEBUG, "nl8021l: Unknown connect failed reason " + "%u", reason); + return; + } + + wpa_supplicant_event(drv->ctx, EVENT_CONNECT_FAILED_REASON, &data); +} + + +static void nl80211_radar_event(struct wpa_driver_nl80211_data *drv, + struct nlattr **tb) +{ + union wpa_event_data data; + enum nl80211_radar_event event_type; + + if (!tb[NL80211_ATTR_WIPHY_FREQ] || !tb[NL80211_ATTR_RADAR_EVENT]) + return; + + os_memset(&data, 0, sizeof(data)); + data.dfs_event.freq = nla_get_u32(tb[NL80211_ATTR_WIPHY_FREQ]); + event_type = nla_get_u32(tb[NL80211_ATTR_RADAR_EVENT]); + + /* Check HT params */ + if (tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) { + data.dfs_event.ht_enabled = 1; + data.dfs_event.chan_offset = 0; + + switch (nla_get_u32(tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE])) { + case NL80211_CHAN_NO_HT: + data.dfs_event.ht_enabled = 0; + break; + case NL80211_CHAN_HT20: + break; + case NL80211_CHAN_HT40PLUS: + data.dfs_event.chan_offset = 1; + break; + case NL80211_CHAN_HT40MINUS: + data.dfs_event.chan_offset = -1; + break; + } + } + + /* Get VHT params */ + if (tb[NL80211_ATTR_CHANNEL_WIDTH]) + data.dfs_event.chan_width = + convert2width(nla_get_u32( + tb[NL80211_ATTR_CHANNEL_WIDTH])); + if (tb[NL80211_ATTR_CENTER_FREQ1]) + data.dfs_event.cf1 = nla_get_u32(tb[NL80211_ATTR_CENTER_FREQ1]); + if (tb[NL80211_ATTR_CENTER_FREQ2]) + data.dfs_event.cf2 = nla_get_u32(tb[NL80211_ATTR_CENTER_FREQ2]); + + wpa_printf(MSG_DEBUG, "nl80211: DFS event on freq %d MHz, ht: %d, offset: %d, width: %d, cf1: %dMHz, cf2: %dMHz", + data.dfs_event.freq, data.dfs_event.ht_enabled, + data.dfs_event.chan_offset, data.dfs_event.chan_width, + data.dfs_event.cf1, data.dfs_event.cf2); + + switch (event_type) { + case NL80211_RADAR_DETECTED: + wpa_supplicant_event(drv->ctx, EVENT_DFS_RADAR_DETECTED, &data); + break; + case NL80211_RADAR_CAC_FINISHED: + wpa_supplicant_event(drv->ctx, EVENT_DFS_CAC_FINISHED, &data); + break; + case NL80211_RADAR_CAC_ABORTED: + wpa_supplicant_event(drv->ctx, EVENT_DFS_CAC_ABORTED, &data); + break; + case NL80211_RADAR_NOP_FINISHED: + wpa_supplicant_event(drv->ctx, EVENT_DFS_NOP_FINISHED, &data); + break; + default: + wpa_printf(MSG_DEBUG, "nl80211: Unknown radar event %d " + "received", event_type); + break; + } +} + + +static void nl80211_spurious_frame(struct i802_bss *bss, struct nlattr **tb, + int wds) +{ + struct wpa_driver_nl80211_data *drv = bss->drv; + union wpa_event_data event; + + if (!tb[NL80211_ATTR_MAC]) + return; + + os_memset(&event, 0, sizeof(event)); + event.rx_from_unknown.bssid = bss->addr; + event.rx_from_unknown.addr = nla_data(tb[NL80211_ATTR_MAC]); + event.rx_from_unknown.wds = wds; + + wpa_supplicant_event(drv->ctx, EVENT_RX_FROM_UNKNOWN, &event); +} + + +static void qca_nl80211_avoid_freq(struct wpa_driver_nl80211_data *drv, + const u8 *data, size_t len) +{ + u32 i, count; + union wpa_event_data event; + struct wpa_freq_range *range = NULL; + const struct qca_avoid_freq_list *freq_range; + + freq_range = (const struct qca_avoid_freq_list *) data; + if (len < sizeof(freq_range->count)) + return; + + count = freq_range->count; + if (len < sizeof(freq_range->count) + + count * sizeof(struct qca_avoid_freq_range)) { + wpa_printf(MSG_DEBUG, "nl80211: Ignored too short avoid frequency list (len=%u)", + (unsigned int) len); + return; + } + + if (count > 0) { + range = os_calloc(count, sizeof(struct wpa_freq_range)); + if (range == NULL) + return; + } + + os_memset(&event, 0, sizeof(event)); + for (i = 0; i < count; i++) { + unsigned int idx = event.freq_range.num; + range[idx].min = freq_range->range[i].start_freq; + range[idx].max = freq_range->range[i].end_freq; + wpa_printf(MSG_DEBUG, "nl80211: Avoid frequency range: %u-%u", + range[idx].min, range[idx].max); + if (range[idx].min > range[idx].max) { + wpa_printf(MSG_DEBUG, "nl80211: Ignore invalid frequency range"); + continue; } + event.freq_range.num++; + } + event.freq_range.range = range; + + wpa_supplicant_event(drv->ctx, EVENT_AVOID_FREQUENCIES, &event); + + os_free(range); +} + + +static void nl80211_vendor_event_qca(struct wpa_driver_nl80211_data *drv, + u32 subcmd, u8 *data, size_t len) +{ + switch (subcmd) { + case QCA_NL80211_VENDOR_SUBCMD_AVOID_FREQUENCY: + qca_nl80211_avoid_freq(drv, data, len); + break; + default: + wpa_printf(MSG_DEBUG, + "nl80211: Ignore unsupported QCA vendor event %u", + subcmd); + break; + } +} + + +static void nl80211_vendor_event(struct wpa_driver_nl80211_data *drv, + struct nlattr **tb) +{ + u32 vendor_id, subcmd, wiphy = 0; + int wiphy_idx; + u8 *data = NULL; + size_t len = 0; + + if (!tb[NL80211_ATTR_VENDOR_ID] || + !tb[NL80211_ATTR_VENDOR_SUBCMD]) + return; + + vendor_id = nla_get_u32(tb[NL80211_ATTR_VENDOR_ID]); + subcmd = nla_get_u32(tb[NL80211_ATTR_VENDOR_SUBCMD]); + + if (tb[NL80211_ATTR_WIPHY]) + wiphy = nla_get_u32(tb[NL80211_ATTR_WIPHY]); + + wpa_printf(MSG_DEBUG, "nl80211: Vendor event: wiphy=%u vendor_id=0x%x subcmd=%u", + wiphy, vendor_id, subcmd); + + if (tb[NL80211_ATTR_VENDOR_DATA]) { + data = nla_data(tb[NL80211_ATTR_VENDOR_DATA]); + len = nla_len(tb[NL80211_ATTR_VENDOR_DATA]); + wpa_hexdump(MSG_MSGDUMP, "nl80211: Vendor data", data, len); + } + + wiphy_idx = nl80211_get_wiphy_index(drv->first_bss); + if (wiphy_idx >= 0 && wiphy_idx != (int) wiphy) { + wpa_printf(MSG_DEBUG, "nl80211: Ignore vendor event for foreign wiphy %u (own: %d)", + wiphy, wiphy_idx); + return; + } + + switch (vendor_id) { + case OUI_QCA: + nl80211_vendor_event_qca(drv, subcmd, data, len); + break; + default: + wpa_printf(MSG_DEBUG, "nl80211: Ignore unsupported vendor event"); + break; } +} + + +static void do_process_drv_event(struct i802_bss *bss, int cmd, + struct nlattr **tb) +{ + struct wpa_driver_nl80211_data *drv = bss->drv; + union wpa_event_data data; + + wpa_printf(MSG_DEBUG, "nl80211: Drv Event %d (%s) received for %s", + cmd, nl80211_command_to_string(cmd), bss->ifname); if (drv->ap_scan_as_station != NL80211_IFTYPE_UNSPECIFIED && - (gnlh->cmd == NL80211_CMD_NEW_SCAN_RESULTS || - gnlh->cmd == NL80211_CMD_SCAN_ABORTED)) { - wpa_driver_nl80211_set_mode(&drv->first_bss, + (cmd == NL80211_CMD_NEW_SCAN_RESULTS || + cmd == NL80211_CMD_SCAN_ABORTED)) { + wpa_driver_nl80211_set_mode(drv->first_bss, drv->ap_scan_as_station); drv->ap_scan_as_station = NL80211_IFTYPE_UNSPECIFIED; } - switch (gnlh->cmd) { + switch (cmd) { case NL80211_CMD_TRIGGER_SCAN: - wpa_printf(MSG_DEBUG, "nl80211: Scan trigger"); + wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Scan trigger"); + drv->scan_state = SCAN_STARTED; + wpa_supplicant_event(drv->ctx, EVENT_SCAN_STARTED, NULL); break; case NL80211_CMD_START_SCHED_SCAN: - wpa_printf(MSG_DEBUG, "nl80211: Sched scan started"); + wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Sched scan started"); + drv->scan_state = SCHED_SCAN_STARTED; break; case NL80211_CMD_SCHED_SCAN_STOPPED: - wpa_printf(MSG_DEBUG, "nl80211: Sched scan stopped"); + wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Sched scan stopped"); + drv->scan_state = SCHED_SCAN_STOPPED; wpa_supplicant_event(drv->ctx, EVENT_SCHED_SCAN_STOPPED, NULL); break; case NL80211_CMD_NEW_SCAN_RESULTS: - wpa_printf(MSG_DEBUG, "nl80211: New scan results available"); + wpa_dbg(drv->ctx, MSG_DEBUG, + "nl80211: New scan results available"); + drv->scan_state = SCAN_COMPLETED; drv->scan_complete_events = 1; eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, drv->ctx); send_scan_event(drv, 0, tb); break; case NL80211_CMD_SCHED_SCAN_RESULTS: - wpa_printf(MSG_DEBUG, - "nl80211: New sched scan results available"); + wpa_dbg(drv->ctx, MSG_DEBUG, + "nl80211: New sched scan results available"); + drv->scan_state = SCHED_SCAN_RESULTS; send_scan_event(drv, 0, tb); break; case NL80211_CMD_SCAN_ABORTED: - wpa_printf(MSG_DEBUG, "nl80211: Scan aborted"); + wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Scan aborted"); + drv->scan_state = SCAN_ABORTED; /* * Need to indicate that scan results are available in order * not to make wpa_supplicant stop its scanning. @@ -1789,29 +2922,39 @@ case NL80211_CMD_ASSOCIATE: case NL80211_CMD_DEAUTHENTICATE: case NL80211_CMD_DISASSOCIATE: - case NL80211_CMD_FRAME: case NL80211_CMD_FRAME_TX_STATUS: case NL80211_CMD_UNPROT_DEAUTHENTICATE: case NL80211_CMD_UNPROT_DISASSOCIATE: - mlme_event(drv, gnlh->cmd, tb[NL80211_ATTR_FRAME], + mlme_event(bss, cmd, tb[NL80211_ATTR_FRAME], tb[NL80211_ATTR_MAC], tb[NL80211_ATTR_TIMED_OUT], tb[NL80211_ATTR_WIPHY_FREQ], tb[NL80211_ATTR_ACK], - tb[NL80211_ATTR_COOKIE]); + tb[NL80211_ATTR_COOKIE], + tb[NL80211_ATTR_RX_SIGNAL_DBM]); break; case NL80211_CMD_CONNECT: case NL80211_CMD_ROAM: - mlme_event_connect(drv, gnlh->cmd, + mlme_event_connect(drv, cmd, tb[NL80211_ATTR_STATUS_CODE], tb[NL80211_ATTR_MAC], tb[NL80211_ATTR_REQ_IE], tb[NL80211_ATTR_RESP_IE]); break; + case NL80211_CMD_CH_SWITCH_NOTIFY: + mlme_event_ch_switch(drv, + tb[NL80211_ATTR_IFINDEX], + tb[NL80211_ATTR_WIPHY_FREQ], + tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE], + tb[NL80211_ATTR_CHANNEL_WIDTH], + tb[NL80211_ATTR_CENTER_FREQ1], + tb[NL80211_ATTR_CENTER_FREQ2]); + break; case NL80211_CMD_DISCONNECT: mlme_event_disconnect(drv, tb[NL80211_ATTR_REASON_CODE], - tb[NL80211_ATTR_MAC]); + tb[NL80211_ATTR_MAC], + tb[NL80211_ATTR_DISCONNECTED_BY_AP]); break; case NL80211_CMD_MICHAEL_MIC_FAILURE: - mlme_event_michael_mic_failure(drv, tb); + mlme_event_michael_mic_failure(bss, tb); break; case NL80211_CMD_JOIN_IBSS: mlme_event_join_ibss(drv, tb); @@ -1827,13 +2970,40 @@ break; case NL80211_CMD_REG_CHANGE: wpa_printf(MSG_DEBUG, "nl80211: Regulatory domain change"); + if (tb[NL80211_ATTR_REG_INITIATOR] == NULL) + break; + os_memset(&data, 0, sizeof(data)); + switch (nla_get_u8(tb[NL80211_ATTR_REG_INITIATOR])) { + case NL80211_REGDOM_SET_BY_CORE: + data.channel_list_changed.initiator = + REGDOM_SET_BY_CORE; + break; + case NL80211_REGDOM_SET_BY_USER: + data.channel_list_changed.initiator = + REGDOM_SET_BY_USER; + break; + case NL80211_REGDOM_SET_BY_DRIVER: + data.channel_list_changed.initiator = + REGDOM_SET_BY_DRIVER; + break; + case NL80211_REGDOM_SET_BY_COUNTRY_IE: + data.channel_list_changed.initiator = + REGDOM_SET_BY_COUNTRY_IE; + break; + default: + wpa_printf(MSG_DEBUG, "nl80211: Unknown reg change initiator %d received", + nla_get_u8(tb[NL80211_ATTR_REG_INITIATOR])); + break; + } wpa_supplicant_event(drv->ctx, EVENT_CHANNEL_LIST_CHANGED, - NULL); + &data); break; case NL80211_CMD_REG_BEACON_HINT: wpa_printf(MSG_DEBUG, "nl80211: Regulatory beacon hint"); + os_memset(&data, 0, sizeof(data)); + data.channel_list_changed.initiator = REGDOM_BEACON_HINT; wpa_supplicant_event(drv->ctx, EVENT_CHANNEL_LIST_CHANGED, - NULL); + &data); break; case NL80211_CMD_NEW_STATION: nl80211_new_station_event(drv, tb); @@ -1847,55 +3017,188 @@ case NL80211_CMD_PMKSA_CANDIDATE: nl80211_pmksa_candidate_event(drv, tb); break; + case NL80211_CMD_PROBE_CLIENT: + nl80211_client_probe_event(drv, tb); + break; + case NL80211_CMD_TDLS_OPER: + nl80211_tdls_oper_event(drv, tb); + break; + case NL80211_CMD_CONN_FAILED: + nl80211_connect_failed_event(drv, tb); + break; + case NL80211_CMD_FT_EVENT: + mlme_event_ft_event(drv, tb); + break; + case NL80211_CMD_RADAR_DETECT: + nl80211_radar_event(drv, tb); + break; + case NL80211_CMD_STOP_AP: + nl80211_stop_ap(drv, tb); + break; + case NL80211_CMD_VENDOR: + nl80211_vendor_event(drv, tb); + break; default: - wpa_printf(MSG_DEBUG, "nl80211: Ignored unknown event " - "(cmd=%d)", gnlh->cmd); + wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Ignored unknown event " + "(cmd=%d)", cmd); break; } - - return NL_SKIP; } -static void wpa_driver_nl80211_event_receive(int sock, void *eloop_ctx, - void *handle) +static int process_drv_event(struct nl_msg *msg, void *arg) { - struct nl_cb *cb; - struct wpa_driver_nl80211_data *drv = eloop_ctx; + struct wpa_driver_nl80211_data *drv = arg; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + struct i802_bss *bss; + int ifidx = -1; - wpa_printf(MSG_DEBUG, "nl80211: Event message available"); + nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); - cb = nl_cb_clone(drv->global->nl_cb); - if (!cb) - return; - nl_cb_set(cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, no_seq_check, NULL); - nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, process_event, drv); - nl_recvmsgs(handle, cb); - nl_cb_put(cb); + if (tb[NL80211_ATTR_IFINDEX]) { + ifidx = nla_get_u32(tb[NL80211_ATTR_IFINDEX]); + + for (bss = drv->first_bss; bss; bss = bss->next) + if (ifidx == -1 || ifidx == bss->ifindex) { + do_process_drv_event(bss, gnlh->cmd, tb); + return NL_SKIP; + } + wpa_printf(MSG_DEBUG, + "nl80211: Ignored event (cmd=%d) for foreign interface (ifindex %d)", + gnlh->cmd, ifidx); + } else if (tb[NL80211_ATTR_WDEV]) { + u64 wdev_id = nla_get_u64(tb[NL80211_ATTR_WDEV]); + wpa_printf(MSG_DEBUG, "nl80211: Process event on P2P device"); + for (bss = drv->first_bss; bss; bss = bss->next) { + if (bss->wdev_id_set && wdev_id == bss->wdev_id) { + do_process_drv_event(bss, gnlh->cmd, tb); + return NL_SKIP; + } + } + wpa_printf(MSG_DEBUG, + "nl80211: Ignored event (cmd=%d) for foreign interface (wdev 0x%llx)", + gnlh->cmd, (long long unsigned int) wdev_id); + } + + return NL_SKIP; } -/** - * wpa_driver_nl80211_set_country - ask nl80211 to set the regulatory domain - * @priv: driver_nl80211 private data - * @alpha2_arg: country to which to switch to - * Returns: 0 on success, -1 on failure - * - * This asks nl80211 to set the regulatory domain for given - * country ISO / IEC alpha2. - */ -static int wpa_driver_nl80211_set_country(void *priv, const char *alpha2_arg) +static int process_global_event(struct nl_msg *msg, void *arg) { - struct i802_bss *bss = priv; - struct wpa_driver_nl80211_data *drv = bss->drv; - char alpha2[3]; - struct nl_msg *msg; + struct nl80211_global *global = arg; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + struct wpa_driver_nl80211_data *drv, *tmp; + int ifidx = -1; + struct i802_bss *bss; + u64 wdev_id = 0; + int wdev_id_set = 0; - msg = nlmsg_alloc(); - if (!msg) - return -ENOMEM; + nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); - alpha2[0] = alpha2_arg[0]; + if (tb[NL80211_ATTR_IFINDEX]) + ifidx = nla_get_u32(tb[NL80211_ATTR_IFINDEX]); + else if (tb[NL80211_ATTR_WDEV]) { + wdev_id = nla_get_u64(tb[NL80211_ATTR_WDEV]); + wdev_id_set = 1; + } + + dl_list_for_each_safe(drv, tmp, &global->interfaces, + struct wpa_driver_nl80211_data, list) { + for (bss = drv->first_bss; bss; bss = bss->next) { + if ((ifidx == -1 && !wdev_id_set) || + ifidx == bss->ifindex || + (wdev_id_set && bss->wdev_id_set && + wdev_id == bss->wdev_id)) { + do_process_drv_event(bss, gnlh->cmd, tb); + return NL_SKIP; + } + } + } + + return NL_SKIP; +} + + +static int process_bss_event(struct nl_msg *msg, void *arg) +{ + struct i802_bss *bss = arg; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + + nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + + wpa_printf(MSG_DEBUG, "nl80211: BSS Event %d (%s) received for %s", + gnlh->cmd, nl80211_command_to_string(gnlh->cmd), + bss->ifname); + + switch (gnlh->cmd) { + case NL80211_CMD_FRAME: + case NL80211_CMD_FRAME_TX_STATUS: + mlme_event(bss, gnlh->cmd, tb[NL80211_ATTR_FRAME], + tb[NL80211_ATTR_MAC], tb[NL80211_ATTR_TIMED_OUT], + tb[NL80211_ATTR_WIPHY_FREQ], tb[NL80211_ATTR_ACK], + tb[NL80211_ATTR_COOKIE], + tb[NL80211_ATTR_RX_SIGNAL_DBM]); + break; + case NL80211_CMD_UNEXPECTED_FRAME: + nl80211_spurious_frame(bss, tb, 0); + break; + case NL80211_CMD_UNEXPECTED_4ADDR_FRAME: + nl80211_spurious_frame(bss, tb, 1); + break; + default: + wpa_printf(MSG_DEBUG, "nl80211: Ignored unknown event " + "(cmd=%d)", gnlh->cmd); + break; + } + + return NL_SKIP; +} + + +static void wpa_driver_nl80211_event_receive(int sock, void *eloop_ctx, + void *handle) +{ + struct nl_cb *cb = eloop_ctx; + int res; + + wpa_printf(MSG_MSGDUMP, "nl80211: Event message available"); + + res = nl_recvmsgs(handle, cb); + if (res) { + wpa_printf(MSG_INFO, "nl80211: %s->nl_recvmsgs failed: %d", + __func__, res); + } +} + + +/** + * wpa_driver_nl80211_set_country - ask nl80211 to set the regulatory domain + * @priv: driver_nl80211 private data + * @alpha2_arg: country to which to switch to + * Returns: 0 on success, -1 on failure + * + * This asks nl80211 to set the regulatory domain for given + * country ISO / IEC alpha2. + */ +static int wpa_driver_nl80211_set_country(void *priv, const char *alpha2_arg) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + char alpha2[3]; + struct nl_msg *msg; + + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + + alpha2[0] = alpha2_arg[0]; alpha2[1] = alpha2_arg[1]; alpha2[2] = '\0'; @@ -1911,244 +3214,588 @@ } +static int nl80211_get_country(struct nl_msg *msg, void *arg) +{ + char *alpha2 = arg; + struct nlattr *tb_msg[NL80211_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + + nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + if (!tb_msg[NL80211_ATTR_REG_ALPHA2]) { + wpa_printf(MSG_DEBUG, "nl80211: No country information available"); + return NL_SKIP; + } + os_strlcpy(alpha2, nla_data(tb_msg[NL80211_ATTR_REG_ALPHA2]), 3); + return NL_SKIP; +} + + +static int wpa_driver_nl80211_get_country(void *priv, char *alpha2) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + int ret; + + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + + nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_REG); + alpha2[0] = '\0'; + ret = send_and_recv_msgs(drv, msg, nl80211_get_country, alpha2); + if (!alpha2[0]) + ret = -1; + + return ret; +} + + +static int protocol_feature_handler(struct nl_msg *msg, void *arg) +{ + u32 *feat = arg; + struct nlattr *tb_msg[NL80211_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + + nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + + if (tb_msg[NL80211_ATTR_PROTOCOL_FEATURES]) + *feat = nla_get_u32(tb_msg[NL80211_ATTR_PROTOCOL_FEATURES]); + + return NL_SKIP; +} + + +static u32 get_nl80211_protocol_features(struct wpa_driver_nl80211_data *drv) +{ + u32 feat = 0; + struct nl_msg *msg; + + msg = nlmsg_alloc(); + if (!msg) + goto nla_put_failure; + + nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_PROTOCOL_FEATURES); + if (send_and_recv_msgs(drv, msg, protocol_feature_handler, &feat) == 0) + return feat; + + msg = NULL; +nla_put_failure: + nlmsg_free(msg); + return 0; +} + + struct wiphy_info_data { + struct wpa_driver_nl80211_data *drv; struct wpa_driver_capa *capa; + unsigned int num_multichan_concurrent; + unsigned int error:1; + unsigned int device_ap_sme:1; + unsigned int poll_command_supported:1; + unsigned int data_tx_status:1; + unsigned int monitor_supported:1; + unsigned int auth_supported:1; + unsigned int connect_supported:1; + unsigned int p2p_go_supported:1; + unsigned int p2p_client_supported:1; + unsigned int p2p_concurrent:1; + unsigned int channel_switch_supported:1; + unsigned int set_qos_map_supported:1; }; -static int wiphy_info_handler(struct nl_msg *msg, void *arg) +static unsigned int probe_resp_offload_support(int supp_protocols) { - struct nlattr *tb[NL80211_ATTR_MAX + 1]; - struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); - struct wiphy_info_data *info = arg; - int p2p_go_supported = 0, p2p_client_supported = 0; - int p2p_concurrent = 0; - int auth_supported = 0, connect_supported = 0; - struct wpa_driver_capa *capa = info->capa; + unsigned int prot = 0; + + if (supp_protocols & NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS) + prot |= WPA_DRIVER_PROBE_RESP_OFFLOAD_WPS; + if (supp_protocols & NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2) + prot |= WPA_DRIVER_PROBE_RESP_OFFLOAD_WPS2; + if (supp_protocols & NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P) + prot |= WPA_DRIVER_PROBE_RESP_OFFLOAD_P2P; + if (supp_protocols & NL80211_PROBE_RESP_OFFLOAD_SUPPORT_80211U) + prot |= WPA_DRIVER_PROBE_RESP_OFFLOAD_INTERWORKING; + + return prot; +} + + +static void wiphy_info_supported_iftypes(struct wiphy_info_data *info, + struct nlattr *tb) +{ + struct nlattr *nl_mode; + int i; + + if (tb == NULL) + return; + + nla_for_each_nested(nl_mode, tb, i) { + switch (nla_type(nl_mode)) { + case NL80211_IFTYPE_AP: + info->capa->flags |= WPA_DRIVER_FLAGS_AP; + break; + case NL80211_IFTYPE_ADHOC: + info->capa->flags |= WPA_DRIVER_FLAGS_IBSS; + break; + case NL80211_IFTYPE_P2P_DEVICE: + info->capa->flags |= + WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE; + break; + case NL80211_IFTYPE_P2P_GO: + info->p2p_go_supported = 1; + break; + case NL80211_IFTYPE_P2P_CLIENT: + info->p2p_client_supported = 1; + break; + case NL80211_IFTYPE_MONITOR: + info->monitor_supported = 1; + break; + } + } +} + + +static int wiphy_info_iface_comb_process(struct wiphy_info_data *info, + struct nlattr *nl_combi) +{ + struct nlattr *tb_comb[NUM_NL80211_IFACE_COMB]; + struct nlattr *tb_limit[NUM_NL80211_IFACE_LIMIT]; + struct nlattr *nl_limit, *nl_mode; + int err, rem_limit, rem_mode; + int combination_has_p2p = 0, combination_has_mgd = 0; static struct nla_policy iface_combination_policy[NUM_NL80211_IFACE_COMB] = { [NL80211_IFACE_COMB_LIMITS] = { .type = NLA_NESTED }, [NL80211_IFACE_COMB_MAXNUM] = { .type = NLA_U32 }, [NL80211_IFACE_COMB_STA_AP_BI_MATCH] = { .type = NLA_FLAG }, [NL80211_IFACE_COMB_NUM_CHANNELS] = { .type = NLA_U32 }, + [NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS] = { .type = NLA_U32 }, }, iface_limit_policy[NUM_NL80211_IFACE_LIMIT] = { [NL80211_IFACE_LIMIT_TYPES] = { .type = NLA_NESTED }, [NL80211_IFACE_LIMIT_MAX] = { .type = NLA_U32 }, }; - nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), - genlmsg_attrlen(gnlh, 0), NULL); - - if (tb[NL80211_ATTR_MAX_NUM_SCAN_SSIDS]) - capa->max_scan_ssids = - nla_get_u8(tb[NL80211_ATTR_MAX_NUM_SCAN_SSIDS]); - - if (tb[NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS]) - capa->max_sched_scan_ssids = - nla_get_u8(tb[NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS]); - - if (tb[NL80211_ATTR_MAX_MATCH_SETS]) - capa->max_match_sets = - nla_get_u8(tb[NL80211_ATTR_MAX_MATCH_SETS]); + err = nla_parse_nested(tb_comb, MAX_NL80211_IFACE_COMB, + nl_combi, iface_combination_policy); + if (err || !tb_comb[NL80211_IFACE_COMB_LIMITS] || + !tb_comb[NL80211_IFACE_COMB_MAXNUM] || + !tb_comb[NL80211_IFACE_COMB_NUM_CHANNELS]) + return 0; /* broken combination */ + + if (tb_comb[NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS]) + info->capa->flags |= WPA_DRIVER_FLAGS_RADAR; + + nla_for_each_nested(nl_limit, tb_comb[NL80211_IFACE_COMB_LIMITS], + rem_limit) { + err = nla_parse_nested(tb_limit, MAX_NL80211_IFACE_LIMIT, + nl_limit, iface_limit_policy); + if (err || !tb_limit[NL80211_IFACE_LIMIT_TYPES]) + return 0; /* broken combination */ - if (tb[NL80211_ATTR_SUPPORTED_IFTYPES]) { - struct nlattr *nl_mode; - int i; nla_for_each_nested(nl_mode, - tb[NL80211_ATTR_SUPPORTED_IFTYPES], i) { - switch (nla_type(nl_mode)) { - case NL80211_IFTYPE_AP: - capa->flags |= WPA_DRIVER_FLAGS_AP; - break; - case NL80211_IFTYPE_P2P_GO: - p2p_go_supported = 1; - break; - case NL80211_IFTYPE_P2P_CLIENT: - p2p_client_supported = 1; - break; - } - } + tb_limit[NL80211_IFACE_LIMIT_TYPES], + rem_mode) { + int ift = nla_type(nl_mode); + if (ift == NL80211_IFTYPE_P2P_GO || + ift == NL80211_IFTYPE_P2P_CLIENT) + combination_has_p2p = 1; + if (ift == NL80211_IFTYPE_STATION) + combination_has_mgd = 1; + } + if (combination_has_p2p && combination_has_mgd) + break; } - if (tb[NL80211_ATTR_INTERFACE_COMBINATIONS]) { - struct nlattr *nl_combi; - int rem_combi; - - nla_for_each_nested(nl_combi, - tb[NL80211_ATTR_INTERFACE_COMBINATIONS], - rem_combi) { - struct nlattr *tb_comb[NUM_NL80211_IFACE_COMB]; - struct nlattr *tb_limit[NUM_NL80211_IFACE_LIMIT]; - struct nlattr *nl_limit, *nl_mode; - int err, rem_limit, rem_mode; - int combination_has_p2p = 0, combination_has_mgd = 0; - - err = nla_parse_nested(tb_comb, MAX_NL80211_IFACE_COMB, - nl_combi, - iface_combination_policy); - if (err || !tb_comb[NL80211_IFACE_COMB_LIMITS] || - !tb_comb[NL80211_IFACE_COMB_MAXNUM] || - !tb_comb[NL80211_IFACE_COMB_NUM_CHANNELS]) - goto broken_combination; - - nla_for_each_nested(nl_limit, - tb_comb[NL80211_IFACE_COMB_LIMITS], - rem_limit) { - err = nla_parse_nested(tb_limit, - MAX_NL80211_IFACE_LIMIT, - nl_limit, - iface_limit_policy); - if (err || - !tb_limit[NL80211_IFACE_LIMIT_TYPES]) - goto broken_combination; - - nla_for_each_nested( - nl_mode, - tb_limit[NL80211_IFACE_LIMIT_TYPES], - rem_mode) { - int ift = nla_type(nl_mode); - if (ift == NL80211_IFTYPE_P2P_GO || - ift == NL80211_IFTYPE_P2P_CLIENT) - combination_has_p2p = 1; - if (ift == NL80211_IFTYPE_STATION) - combination_has_mgd = 1; - } - if (combination_has_p2p && combination_has_mgd) - break; - } - - if (combination_has_p2p && combination_has_mgd) { - p2p_concurrent = 1; - break; - } - -broken_combination: - ; - } + if (combination_has_p2p && combination_has_mgd) { + info->p2p_concurrent = 1; + info->num_multichan_concurrent = + nla_get_u32(tb_comb[NL80211_IFACE_COMB_NUM_CHANNELS]); + return 1; } - if (tb[NL80211_ATTR_SUPPORTED_COMMANDS]) { - struct nlattr *nl_cmd; - int i; + return 0; +} - nla_for_each_nested(nl_cmd, - tb[NL80211_ATTR_SUPPORTED_COMMANDS], i) { - switch (nla_get_u32(nl_cmd)) { - case NL80211_CMD_AUTHENTICATE: - auth_supported = 1; - break; - case NL80211_CMD_CONNECT: - connect_supported = 1; - break; - case NL80211_CMD_START_SCHED_SCAN: - /* - * Disabled for 1.x for now as it is - * broken there due to the way it ends - * up getting used. - capa->sched_scan_supported = 1; - */ - break; - } - } - } - if (tb[NL80211_ATTR_OFFCHANNEL_TX_OK]) { - wpa_printf(MSG_DEBUG, "nl80211: Using driver-based " - "off-channel TX"); - capa->flags |= WPA_DRIVER_FLAGS_OFFCHANNEL_TX; - } +static void wiphy_info_iface_comb(struct wiphy_info_data *info, + struct nlattr *tb) +{ + struct nlattr *nl_combi; + int rem_combi; - if (tb[NL80211_ATTR_ROAM_SUPPORT]) { - wpa_printf(MSG_DEBUG, "nl80211: Using driver-based roaming"); - capa->flags |= WPA_DRIVER_FLAGS_BSS_SELECTION; + if (tb == NULL) + return; + + nla_for_each_nested(nl_combi, tb, rem_combi) { + if (wiphy_info_iface_comb_process(info, nl_combi) > 0) + break; } +} - /* default to 5000 since early versions of mac80211 don't set it */ - capa->max_remain_on_chan = 5000; - if (tb[NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION]) - capa->max_remain_on_chan = - nla_get_u32(tb[NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION]); - - if (auth_supported) - capa->flags |= WPA_DRIVER_FLAGS_SME; - else if (!connect_supported) { - wpa_printf(MSG_INFO, "nl80211: Driver does not support " - "authentication/association or connect commands"); - info->error = 1; - } +static void wiphy_info_supp_cmds(struct wiphy_info_data *info, + struct nlattr *tb) +{ + struct nlattr *nl_cmd; + int i; - if (p2p_go_supported && p2p_client_supported) - capa->flags |= WPA_DRIVER_FLAGS_P2P_CAPABLE; - if (p2p_concurrent) { - wpa_printf(MSG_DEBUG, "nl80211: Use separate P2P group " - "interface (driver advertised support)"); - capa->flags |= WPA_DRIVER_FLAGS_P2P_CONCURRENT; - capa->flags |= WPA_DRIVER_FLAGS_P2P_MGMT_AND_NON_P2P; - } + if (tb == NULL) + return; - if (tb[NL80211_ATTR_TDLS_SUPPORT]) { - wpa_printf(MSG_DEBUG, "nl80211: TDLS supported"); - capa->flags |= WPA_DRIVER_FLAGS_TDLS_SUPPORT; - - if (tb[NL80211_ATTR_TDLS_EXTERNAL_SETUP]) { - wpa_printf(MSG_DEBUG, "nl80211: TDLS external setup"); - capa->flags |= - WPA_DRIVER_FLAGS_TDLS_EXTERNAL_SETUP; + nla_for_each_nested(nl_cmd, tb, i) { + switch (nla_get_u32(nl_cmd)) { + case NL80211_CMD_AUTHENTICATE: + info->auth_supported = 1; + break; + case NL80211_CMD_CONNECT: + info->connect_supported = 1; + break; + case NL80211_CMD_START_SCHED_SCAN: + info->capa->sched_scan_supported = 1; + break; + case NL80211_CMD_PROBE_CLIENT: + info->poll_command_supported = 1; + break; + case NL80211_CMD_CHANNEL_SWITCH: + info->channel_switch_supported = 1; + break; + case NL80211_CMD_SET_QOS_MAP: + info->set_qos_map_supported = 1; + break; } } - - return NL_SKIP; } -static int wpa_driver_nl80211_get_info(struct wpa_driver_nl80211_data *drv, - struct wiphy_info_data *info) +static void wiphy_info_cipher_suites(struct wiphy_info_data *info, + struct nlattr *tb) { - struct nl_msg *msg; + int i, num; + u32 *ciphers; - os_memset(info, 0, sizeof(*info)); - info->capa = &drv->capa; + if (tb == NULL) + return; - msg = nlmsg_alloc(); - if (!msg) - return -1; + num = nla_len(tb) / sizeof(u32); + ciphers = nla_data(tb); + for (i = 0; i < num; i++) { + u32 c = ciphers[i]; - nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_WIPHY); + wpa_printf(MSG_DEBUG, "nl80211: Supported cipher %02x-%02x-%02x:%d", + c >> 24, (c >> 16) & 0xff, + (c >> 8) & 0xff, c & 0xff); + switch (c) { + case WLAN_CIPHER_SUITE_CCMP_256: + info->capa->enc |= WPA_DRIVER_CAPA_ENC_CCMP_256; + break; + case WLAN_CIPHER_SUITE_GCMP_256: + info->capa->enc |= WPA_DRIVER_CAPA_ENC_GCMP_256; + break; + case WLAN_CIPHER_SUITE_CCMP: + info->capa->enc |= WPA_DRIVER_CAPA_ENC_CCMP; + break; + case WLAN_CIPHER_SUITE_GCMP: + info->capa->enc |= WPA_DRIVER_CAPA_ENC_GCMP; + break; + case WLAN_CIPHER_SUITE_TKIP: + info->capa->enc |= WPA_DRIVER_CAPA_ENC_TKIP; + break; + case WLAN_CIPHER_SUITE_WEP104: + info->capa->enc |= WPA_DRIVER_CAPA_ENC_WEP104; + break; + case WLAN_CIPHER_SUITE_WEP40: + info->capa->enc |= WPA_DRIVER_CAPA_ENC_WEP40; + break; + case WLAN_CIPHER_SUITE_AES_CMAC: + info->capa->enc |= WPA_DRIVER_CAPA_ENC_BIP; + break; + case WLAN_CIPHER_SUITE_BIP_GMAC_128: + info->capa->enc |= WPA_DRIVER_CAPA_ENC_BIP_GMAC_128; + break; + case WLAN_CIPHER_SUITE_BIP_GMAC_256: + info->capa->enc |= WPA_DRIVER_CAPA_ENC_BIP_GMAC_256; + break; + case WLAN_CIPHER_SUITE_BIP_CMAC_256: + info->capa->enc |= WPA_DRIVER_CAPA_ENC_BIP_CMAC_256; + break; + } + } +} - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->first_bss.ifindex); - if (send_and_recv_msgs(drv, msg, wiphy_info_handler, info) == 0) - return 0; - msg = NULL; -nla_put_failure: - nlmsg_free(msg); - return -1; +static void wiphy_info_max_roc(struct wpa_driver_capa *capa, + struct nlattr *tb) +{ + if (tb) + capa->max_remain_on_chan = nla_get_u32(tb); } -static int wpa_driver_nl80211_capa(struct wpa_driver_nl80211_data *drv) +static void wiphy_info_tdls(struct wpa_driver_capa *capa, struct nlattr *tdls, + struct nlattr *ext_setup) { - struct wiphy_info_data info; - if (wpa_driver_nl80211_get_info(drv, &info)) - return -1; + if (tdls == NULL) + return; - if (info.error) - return -1; + wpa_printf(MSG_DEBUG, "nl80211: TDLS supported"); + capa->flags |= WPA_DRIVER_FLAGS_TDLS_SUPPORT; + + if (ext_setup) { + wpa_printf(MSG_DEBUG, "nl80211: TDLS external setup"); + capa->flags |= WPA_DRIVER_FLAGS_TDLS_EXTERNAL_SETUP; + } +} + + +static void wiphy_info_feature_flags(struct wiphy_info_data *info, + struct nlattr *tb) +{ + u32 flags; + struct wpa_driver_capa *capa = info->capa; + + if (tb == NULL) + return; + + flags = nla_get_u32(tb); + + if (flags & NL80211_FEATURE_SK_TX_STATUS) + info->data_tx_status = 1; + + if (flags & NL80211_FEATURE_INACTIVITY_TIMER) + capa->flags |= WPA_DRIVER_FLAGS_INACTIVITY_TIMER; + + if (flags & NL80211_FEATURE_SAE) + capa->flags |= WPA_DRIVER_FLAGS_SAE; + + if (flags & NL80211_FEATURE_NEED_OBSS_SCAN) + capa->flags |= WPA_DRIVER_FLAGS_OBSS_SCAN; +} + + +static void wiphy_info_probe_resp_offload(struct wpa_driver_capa *capa, + struct nlattr *tb) +{ + u32 protocols; + + if (tb == NULL) + return; + + protocols = nla_get_u32(tb); + wpa_printf(MSG_DEBUG, "nl80211: Supports Probe Response offload in AP " + "mode"); + capa->flags |= WPA_DRIVER_FLAGS_PROBE_RESP_OFFLOAD; + capa->probe_resp_offloads = probe_resp_offload_support(protocols); +} + + +static int wiphy_info_handler(struct nl_msg *msg, void *arg) +{ + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct wiphy_info_data *info = arg; + struct wpa_driver_capa *capa = info->capa; + struct wpa_driver_nl80211_data *drv = info->drv; + + nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + + if (tb[NL80211_ATTR_WIPHY_NAME]) + os_strlcpy(drv->phyname, + nla_get_string(tb[NL80211_ATTR_WIPHY_NAME]), + sizeof(drv->phyname)); + if (tb[NL80211_ATTR_MAX_NUM_SCAN_SSIDS]) + capa->max_scan_ssids = + nla_get_u8(tb[NL80211_ATTR_MAX_NUM_SCAN_SSIDS]); + + if (tb[NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS]) + capa->max_sched_scan_ssids = + nla_get_u8(tb[NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS]); + + if (tb[NL80211_ATTR_MAX_MATCH_SETS]) + capa->max_match_sets = + nla_get_u8(tb[NL80211_ATTR_MAX_MATCH_SETS]); + + if (tb[NL80211_ATTR_MAC_ACL_MAX]) + capa->max_acl_mac_addrs = + nla_get_u8(tb[NL80211_ATTR_MAC_ACL_MAX]); + + wiphy_info_supported_iftypes(info, tb[NL80211_ATTR_SUPPORTED_IFTYPES]); + wiphy_info_iface_comb(info, tb[NL80211_ATTR_INTERFACE_COMBINATIONS]); + wiphy_info_supp_cmds(info, tb[NL80211_ATTR_SUPPORTED_COMMANDS]); + wiphy_info_cipher_suites(info, tb[NL80211_ATTR_CIPHER_SUITES]); + + if (tb[NL80211_ATTR_OFFCHANNEL_TX_OK]) { + wpa_printf(MSG_DEBUG, "nl80211: Using driver-based " + "off-channel TX"); + capa->flags |= WPA_DRIVER_FLAGS_OFFCHANNEL_TX; + } + + if (tb[NL80211_ATTR_ROAM_SUPPORT]) { + wpa_printf(MSG_DEBUG, "nl80211: Using driver-based roaming"); + capa->flags |= WPA_DRIVER_FLAGS_BSS_SELECTION; + } + + wiphy_info_max_roc(capa, + tb[NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION]); + + if (tb[NL80211_ATTR_SUPPORT_AP_UAPSD]) + capa->flags |= WPA_DRIVER_FLAGS_AP_UAPSD; + + wiphy_info_tdls(capa, tb[NL80211_ATTR_TDLS_SUPPORT], + tb[NL80211_ATTR_TDLS_EXTERNAL_SETUP]); + + if (tb[NL80211_ATTR_DEVICE_AP_SME]) + info->device_ap_sme = 1; + + wiphy_info_feature_flags(info, tb[NL80211_ATTR_FEATURE_FLAGS]); + wiphy_info_probe_resp_offload(capa, + tb[NL80211_ATTR_PROBE_RESP_OFFLOAD]); + + if (tb[NL80211_ATTR_EXT_CAPA] && tb[NL80211_ATTR_EXT_CAPA_MASK] && + drv->extended_capa == NULL) { + drv->extended_capa = + os_malloc(nla_len(tb[NL80211_ATTR_EXT_CAPA])); + if (drv->extended_capa) { + os_memcpy(drv->extended_capa, + nla_data(tb[NL80211_ATTR_EXT_CAPA]), + nla_len(tb[NL80211_ATTR_EXT_CAPA])); + drv->extended_capa_len = + nla_len(tb[NL80211_ATTR_EXT_CAPA]); + } + drv->extended_capa_mask = + os_malloc(nla_len(tb[NL80211_ATTR_EXT_CAPA])); + if (drv->extended_capa_mask) { + os_memcpy(drv->extended_capa_mask, + nla_data(tb[NL80211_ATTR_EXT_CAPA]), + nla_len(tb[NL80211_ATTR_EXT_CAPA])); + } else { + os_free(drv->extended_capa); + drv->extended_capa = NULL; + drv->extended_capa_len = 0; + } + } + + if (tb[NL80211_ATTR_VENDOR_DATA]) { + struct nlattr *nl; + int rem; + + nla_for_each_nested(nl, tb[NL80211_ATTR_VENDOR_DATA], rem) { + struct nl80211_vendor_cmd_info *vinfo; + if (nla_len(nl) != sizeof(*vinfo)) { + wpa_printf(MSG_DEBUG, "nl80211: Unexpected vendor data info"); + continue; + } + vinfo = nla_data(nl); + wpa_printf(MSG_DEBUG, "nl80211: Supported vendor command: vendor_id=0x%x subcmd=%u", + vinfo->vendor_id, vinfo->subcmd); + } + } + + if (tb[NL80211_ATTR_VENDOR_EVENTS]) { + struct nlattr *nl; + int rem; + + nla_for_each_nested(nl, tb[NL80211_ATTR_VENDOR_EVENTS], rem) { + struct nl80211_vendor_cmd_info *vinfo; + if (nla_len(nl) != sizeof(*vinfo)) { + wpa_printf(MSG_DEBUG, "nl80211: Unexpected vendor data info"); + continue; + } + vinfo = nla_data(nl); + wpa_printf(MSG_DEBUG, "nl80211: Supported vendor event: vendor_id=0x%x subcmd=%u", + vinfo->vendor_id, vinfo->subcmd); + } + } + + return NL_SKIP; +} + + +static int wpa_driver_nl80211_get_info(struct wpa_driver_nl80211_data *drv, + struct wiphy_info_data *info) +{ + u32 feat; + struct nl_msg *msg; + + os_memset(info, 0, sizeof(*info)); + info->capa = &drv->capa; + info->drv = drv; + + msg = nlmsg_alloc(); + if (!msg) + return -1; + + feat = get_nl80211_protocol_features(drv); + if (feat & NL80211_PROTOCOL_FEATURE_SPLIT_WIPHY_DUMP) + nl80211_cmd(drv, msg, NLM_F_DUMP, NL80211_CMD_GET_WIPHY); + else + nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_WIPHY); + + NLA_PUT_FLAG(msg, NL80211_ATTR_SPLIT_WIPHY_DUMP); + if (nl80211_set_iface_id(msg, drv->first_bss) < 0) + goto nla_put_failure; + + if (send_and_recv_msgs(drv, msg, wiphy_info_handler, info)) + return -1; + + if (info->auth_supported) + drv->capa.flags |= WPA_DRIVER_FLAGS_SME; + else if (!info->connect_supported) { + wpa_printf(MSG_INFO, "nl80211: Driver does not support " + "authentication/association or connect commands"); + info->error = 1; + } + + if (info->p2p_go_supported && info->p2p_client_supported) + drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_CAPABLE; + if (info->p2p_concurrent) { + wpa_printf(MSG_DEBUG, "nl80211: Use separate P2P group " + "interface (driver advertised support)"); + drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_CONCURRENT; + drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_MGMT_AND_NON_P2P; + } + if (info->num_multichan_concurrent > 1) { + wpa_printf(MSG_DEBUG, "nl80211: Enable multi-channel " + "concurrent (driver advertised support)"); + drv->capa.num_multichan_concurrent = + info->num_multichan_concurrent; + } + + /* default to 5000 since early versions of mac80211 don't set it */ + if (!drv->capa.max_remain_on_chan) + drv->capa.max_remain_on_chan = 5000; + + if (info->channel_switch_supported) + drv->capa.flags |= WPA_DRIVER_FLAGS_AP_CSA; + + return 0; +nla_put_failure: + nlmsg_free(msg); + return -1; +} + + +static int wpa_driver_nl80211_capa(struct wpa_driver_nl80211_data *drv) +{ + struct wiphy_info_data info; + if (wpa_driver_nl80211_get_info(drv, &info)) + return -1; + + if (info.error) + return -1; drv->has_capability = 1; - /* For now, assume TKIP, CCMP, WPA, WPA2 are supported */ drv->capa.key_mgmt = WPA_DRIVER_CAPA_KEY_MGMT_WPA | WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK | WPA_DRIVER_CAPA_KEY_MGMT_WPA2 | WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK; - drv->capa.enc = WPA_DRIVER_CAPA_ENC_WEP40 | - WPA_DRIVER_CAPA_ENC_WEP104 | - WPA_DRIVER_CAPA_ENC_TKIP | - WPA_DRIVER_CAPA_ENC_CCMP; drv->capa.auth = WPA_DRIVER_AUTH_OPEN | WPA_DRIVER_AUTH_SHARED | WPA_DRIVER_AUTH_LEAP; @@ -2156,7 +3803,50 @@ drv->capa.flags |= WPA_DRIVER_FLAGS_SANE_ERROR_CODES; drv->capa.flags |= WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC_DONE; drv->capa.flags |= WPA_DRIVER_FLAGS_EAPOL_TX_STATUS; - drv->capa.flags |= WPA_DRIVER_FLAGS_DEAUTH_TX_STATUS; + + if (!info.device_ap_sme) { + drv->capa.flags |= WPA_DRIVER_FLAGS_DEAUTH_TX_STATUS; + + /* + * No AP SME is currently assumed to also indicate no AP MLME + * in the driver/firmware. + */ + drv->capa.flags |= WPA_DRIVER_FLAGS_AP_MLME; + } + + drv->device_ap_sme = info.device_ap_sme; + drv->poll_command_supported = info.poll_command_supported; + drv->data_tx_status = info.data_tx_status; + if (info.set_qos_map_supported) + drv->capa.flags |= WPA_DRIVER_FLAGS_QOS_MAPPING; + + /* + * If poll command and tx status are supported, mac80211 is new enough + * to have everything we need to not need monitor interfaces. + */ + drv->use_monitor = !info.poll_command_supported || !info.data_tx_status; + + if (drv->device_ap_sme && drv->use_monitor) { + /* + * Non-mac80211 drivers may not support monitor interface. + * Make sure we do not get stuck with incorrect capability here + * by explicitly testing this. + */ + if (!info.monitor_supported) { + wpa_printf(MSG_DEBUG, "nl80211: Disable use_monitor " + "with device_ap_sme since no monitor mode " + "support detected"); + drv->use_monitor = 0; + } + } + + /* + * If we aren't going to use monitor interfaces, but the + * driver doesn't support data TX status, we won't get TX + * status for EAPOL frames. + */ + if (!drv->use_monitor && !info.data_tx_status) + drv->capa.flags &= ~WPA_DRIVER_FLAGS_EAPOL_TX_STATUS; return 0; } @@ -2200,6 +3890,8 @@ static int wpa_driver_nl80211_init_nl_global(struct nl80211_global *global) { + int ret; + global->nl_cb = nl_cb_alloc(NL_CB_DEFAULT); if (global->nl_cb == NULL) { wpa_printf(MSG_ERROR, "nl80211: Failed to allocate netlink " @@ -2207,54 +3899,44 @@ return -1; } - if (nl_create_handles(&global->nl, global->nl_cb, "nl")) - return -1; + global->nl = nl_create_handle(global->nl_cb, "nl"); + if (global->nl == NULL) + goto err; - global->nl80211 = genl_ctrl_search_by_name(global->nl.cache, - "nl80211"); - if (global->nl80211 == NULL) { + global->nl80211_id = genl_ctrl_resolve(global->nl, "nl80211"); + if (global->nl80211_id < 0) { wpa_printf(MSG_ERROR, "nl80211: 'nl80211' generic netlink not " "found"); - return -1; + goto err; } - return 0; -} - - -static int wpa_driver_nl80211_init_nl(struct wpa_driver_nl80211_data *drv) -{ - struct nl80211_global *global = drv->global; - int ret; - - /* Initialize generic netlink and nl80211 */ - - if (nl_create_handles(&drv->nl_event, global->nl_cb, "event")) - goto err3; + global->nl_event = nl_create_handle(global->nl_cb, "event"); + if (global->nl_event == NULL) + goto err; - ret = nl_get_multicast_id(drv, "nl80211", "scan"); + ret = nl_get_multicast_id(global, "nl80211", "scan"); if (ret >= 0) - ret = nl_socket_add_membership(drv->nl_event.handle, ret); + ret = nl_socket_add_membership(global->nl_event, ret); if (ret < 0) { wpa_printf(MSG_ERROR, "nl80211: Could not add multicast " "membership for scan events: %d (%s)", ret, strerror(-ret)); - goto err4; + goto err; } - ret = nl_get_multicast_id(drv, "nl80211", "mlme"); + ret = nl_get_multicast_id(global, "nl80211", "mlme"); if (ret >= 0) - ret = nl_socket_add_membership(drv->nl_event.handle, ret); + ret = nl_socket_add_membership(global->nl_event, ret); if (ret < 0) { wpa_printf(MSG_ERROR, "nl80211: Could not add multicast " "membership for mlme events: %d (%s)", ret, strerror(-ret)); - goto err4; + goto err; } - ret = nl_get_multicast_id(drv, "nl80211", "regulatory"); + ret = nl_get_multicast_id(global, "nl80211", "regulatory"); if (ret >= 0) - ret = nl_socket_add_membership(drv->nl_event.handle, ret); + ret = nl_socket_add_membership(global->nl_event, ret); if (ret < 0) { wpa_printf(MSG_DEBUG, "nl80211: Could not add multicast " "membership for regulatory events: %d (%s)", @@ -2262,19 +3944,53 @@ /* Continue without regulatory events */ } - eloop_register_read_sock(nl_socket_get_fd(drv->nl_event.handle), - wpa_driver_nl80211_event_receive, drv, - drv->nl_event.handle); + ret = nl_get_multicast_id(global, "nl80211", "vendor"); + if (ret >= 0) + ret = nl_socket_add_membership(global->nl_event, ret); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "nl80211: Could not add multicast " + "membership for vendor events: %d (%s)", + ret, strerror(-ret)); + /* Continue without vendor events */ + } + + nl_cb_set(global->nl_cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, + no_seq_check, NULL); + nl_cb_set(global->nl_cb, NL_CB_VALID, NL_CB_CUSTOM, + process_global_event, global); + + nl80211_register_eloop_read(&global->nl_event, + wpa_driver_nl80211_event_receive, + global->nl_cb); return 0; -err4: - nl_destroy_handles(&drv->nl_event); -err3: +err: + nl_destroy_handles(&global->nl_event); + nl_destroy_handles(&global->nl); + nl_cb_put(global->nl_cb); + global->nl_cb = NULL; return -1; } +static int wpa_driver_nl80211_init_nl(struct wpa_driver_nl80211_data *drv) +{ + drv->nl_cb = nl_cb_alloc(NL_CB_DEFAULT); + if (!drv->nl_cb) { + wpa_printf(MSG_ERROR, "nl80211: Failed to alloc cb struct"); + return -1; + } + + nl_cb_set(drv->nl_cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, + no_seq_check, NULL); + nl_cb_set(drv->nl_cb, NL_CB_VALID, NL_CB_CUSTOM, + process_drv_event, drv); + + return 0; +} + + static void wpa_driver_nl80211_rfkill_blocked(void *ctx) { wpa_printf(MSG_DEBUG, "nl80211: RFKILL blocked"); @@ -2289,8 +4005,7 @@ { struct wpa_driver_nl80211_data *drv = ctx; wpa_printf(MSG_DEBUG, "nl80211: RFKILL unblocked"); - if (linux_set_iface_flags(drv->global->ioctl_sock, - drv->first_bss.ifname, 1)) { + if (i802_set_iface_flags(drv->first_bss, 1)) { wpa_printf(MSG_DEBUG, "nl80211: Could not set interface UP " "after rfkill unblock"); return; @@ -2299,59 +4014,90 @@ } -static void nl80211_get_phy_name(struct wpa_driver_nl80211_data *drv) +static void wpa_driver_nl80211_handle_eapol_tx_status(int sock, + void *eloop_ctx, + void *handle) { - /* Find phy (radio) to which this interface belongs */ - char buf[90], *pos; - int f, rv; + struct wpa_driver_nl80211_data *drv = eloop_ctx; + u8 data[2048]; + struct msghdr msg; + struct iovec entry; + u8 control[512]; + struct cmsghdr *cmsg; + int res, found_ee = 0, found_wifi = 0, acked = 0; + union wpa_event_data event; - drv->phyname[0] = '\0'; - snprintf(buf, sizeof(buf) - 1, "/sys/class/net/%s/phy80211/name", - drv->first_bss.ifname); - f = open(buf, O_RDONLY); - if (f < 0) { - wpa_printf(MSG_DEBUG, "Could not open file %s: %s", - buf, strerror(errno)); + memset(&msg, 0, sizeof(msg)); + msg.msg_iov = &entry; + msg.msg_iovlen = 1; + entry.iov_base = data; + entry.iov_len = sizeof(data); + msg.msg_control = &control; + msg.msg_controllen = sizeof(control); + + res = recvmsg(sock, &msg, MSG_ERRQUEUE); + /* if error or not fitting 802.3 header, return */ + if (res < 14) return; + + for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) + { + if (cmsg->cmsg_level == SOL_SOCKET && + cmsg->cmsg_type == SCM_WIFI_STATUS) { + int *ack; + + found_wifi = 1; + ack = (void *)CMSG_DATA(cmsg); + acked = *ack; + } + + if (cmsg->cmsg_level == SOL_PACKET && + cmsg->cmsg_type == PACKET_TX_TIMESTAMP) { + struct sock_extended_err *err = + (struct sock_extended_err *)CMSG_DATA(cmsg); + + if (err->ee_origin == SO_EE_ORIGIN_TXSTATUS) + found_ee = 1; + } } - rv = read(f, drv->phyname, sizeof(drv->phyname) - 1); - close(f); - if (rv < 0) { - wpa_printf(MSG_DEBUG, "Could not read file %s: %s", - buf, strerror(errno)); + if (!found_ee || !found_wifi) return; - } - drv->phyname[rv] = '\0'; - pos = os_strchr(drv->phyname, '\n'); - if (pos) - *pos = '\0'; - wpa_printf(MSG_DEBUG, "nl80211: interface %s in phy %s", - drv->first_bss.ifname, drv->phyname); + memset(&event, 0, sizeof(event)); + event.eapol_tx_status.dst = data; + event.eapol_tx_status.data = data + 14; + event.eapol_tx_status.data_len = res - 14; + event.eapol_tx_status.ack = acked; + wpa_supplicant_event(drv->ctx, EVENT_EAPOL_TX_STATUS, &event); } -#ifdef CONFIG_AP -static void nl80211_l2_read(void *ctx, const u8 *src_addr, const u8 *buf, - size_t len) +static int nl80211_init_bss(struct i802_bss *bss) { - wpa_printf(MSG_DEBUG, "nl80211: l2_packet read %u", - (unsigned int) len); + bss->nl_cb = nl_cb_alloc(NL_CB_DEFAULT); + if (!bss->nl_cb) + return -1; + + nl_cb_set(bss->nl_cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, + no_seq_check, NULL); + nl_cb_set(bss->nl_cb, NL_CB_VALID, NL_CB_CUSTOM, + process_bss_event, bss); + + return 0; } -#endif /* CONFIG_AP */ -/** - * wpa_driver_nl80211_init - Initialize nl80211 driver interface - * @ctx: context to be used when calling wpa_supplicant functions, - * e.g., wpa_supplicant_event() - * @ifname: interface name, e.g., wlan0 - * @global_priv: private driver global data from global_init() - * Returns: Pointer to private data, %NULL on failure - */ -static void * wpa_driver_nl80211_init(void *ctx, const char *ifname, - void *global_priv) +static void nl80211_destroy_bss(struct i802_bss *bss) +{ + nl_cb_put(bss->nl_cb); + bss->nl_cb = NULL; +} + + +static void * wpa_driver_nl80211_drv_init(void *ctx, const char *ifname, + void *global_priv, int hostapd, + const u8 *set_addr) { struct wpa_driver_nl80211_data *drv; struct rfkill_config *rcfg; @@ -2364,11 +4110,24 @@ return NULL; drv->global = global_priv; drv->ctx = ctx; - bss = &drv->first_bss; + drv->hostapd = !!hostapd; + drv->eapol_sock = -1; + drv->num_if_indices = sizeof(drv->default_if_indices) / sizeof(int); + drv->if_indices = drv->default_if_indices; + + drv->first_bss = os_zalloc(sizeof(*drv->first_bss)); + if (!drv->first_bss) { + os_free(drv); + return NULL; + } + bss = drv->first_bss; bss->drv = drv; + bss->ctx = ctx; + os_strlcpy(bss->ifname, ifname, sizeof(bss->ifname)); drv->monitor_ifidx = -1; drv->monitor_sock = -1; + drv->eapol_tx_sock = -1; drv->ap_scan_as_station = NL80211_IFTYPE_UNSPECIFIED; if (wpa_driver_nl80211_init_nl(drv)) { @@ -2376,7 +4135,8 @@ return NULL; } - nl80211_get_phy_name(drv); + if (nl80211_init_bss(bss)) + goto failed; rcfg = os_zalloc(sizeof(*rcfg)); if (rcfg == NULL) @@ -2391,13 +4151,33 @@ os_free(rcfg); } - if (wpa_driver_nl80211_finish_drv_init(drv)) + if (linux_iface_up(drv->global->ioctl_sock, ifname) > 0) + drv->start_iface_up = 1; + + if (wpa_driver_nl80211_finish_drv_init(drv, set_addr, 1)) + goto failed; + + drv->eapol_tx_sock = socket(PF_PACKET, SOCK_DGRAM, 0); + if (drv->eapol_tx_sock < 0) goto failed; -#ifdef CONFIG_AP - drv->l2 = l2_packet_init(ifname, NULL, ETH_P_EAPOL, - nl80211_l2_read, drv, 0); -#endif /* CONFIG_AP */ + if (drv->data_tx_status) { + int enabled = 1; + + if (setsockopt(drv->eapol_tx_sock, SOL_SOCKET, SO_WIFI_STATUS, + &enabled, sizeof(enabled)) < 0) { + wpa_printf(MSG_DEBUG, + "nl80211: wifi status sockopt failed\n"); + drv->data_tx_status = 0; + if (!drv->use_monitor) + drv->capa.flags &= + ~WPA_DRIVER_FLAGS_EAPOL_TX_STATUS; + } else { + eloop_register_read_sock(drv->eapol_tx_sock, + wpa_driver_nl80211_handle_eapol_tx_status, + drv, NULL); + } + } if (drv->global) { dl_list_add(&drv->global->interfaces, &drv->list); @@ -2412,24 +4192,48 @@ } -static int nl80211_register_frame(struct wpa_driver_nl80211_data *drv, +/** + * wpa_driver_nl80211_init - Initialize nl80211 driver interface + * @ctx: context to be used when calling wpa_supplicant functions, + * e.g., wpa_supplicant_event() + * @ifname: interface name, e.g., wlan0 + * @global_priv: private driver global data from global_init() + * Returns: Pointer to private data, %NULL on failure + */ +static void * wpa_driver_nl80211_init(void *ctx, const char *ifname, + void *global_priv) +{ + return wpa_driver_nl80211_drv_init(ctx, ifname, global_priv, 0, NULL); +} + + +static int nl80211_register_frame(struct i802_bss *bss, struct nl_handle *nl_handle, u16 type, const u8 *match, size_t match_len) { + struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; int ret = -1; + char buf[30]; msg = nlmsg_alloc(); if (!msg) return -1; + buf[0] = '\0'; + wpa_snprintf_hex(buf, sizeof(buf), match, match_len); + wpa_printf(MSG_DEBUG, "nl80211: Register frame type=0x%x nl_handle=%p match=%s", + type, nl_handle, buf); + nl80211_cmd(drv, msg, 0, NL80211_CMD_REGISTER_ACTION); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + if (nl80211_set_iface_id(msg, bss) < 0) + goto nla_put_failure; + NLA_PUT_U16(msg, NL80211_ATTR_FRAME_TYPE, type); NLA_PUT(msg, NL80211_ATTR_FRAME_MATCH, match_len, match); - ret = send_and_recv(drv, nl_handle, msg, NULL, NULL); + ret = send_and_recv(drv->global, nl_handle, msg, NULL, NULL); msg = NULL; if (ret) { wpa_printf(MSG_DEBUG, "nl80211: Register frame command " @@ -2446,69 +4250,243 @@ } -static int nl80211_register_action_frame(struct wpa_driver_nl80211_data *drv, - const u8 *match, size_t match_len) +static int nl80211_alloc_mgmt_handle(struct i802_bss *bss) { - u16 type = (WLAN_FC_TYPE_MGMT << 2) | (WLAN_FC_STYPE_ACTION << 4); - return nl80211_register_frame(drv, drv->nl_event.handle, - type, match, match_len); -} + struct wpa_driver_nl80211_data *drv = bss->drv; + if (bss->nl_mgmt) { + wpa_printf(MSG_DEBUG, "nl80211: Mgmt reporting " + "already on! (nl_mgmt=%p)", bss->nl_mgmt); + return -1; + } -static int nl80211_register_action_frames(struct wpa_driver_nl80211_data *drv) -{ + bss->nl_mgmt = nl_create_handle(drv->nl_cb, "mgmt"); + if (bss->nl_mgmt == NULL) + return -1; + + return 0; +} + + +static void nl80211_mgmt_handle_register_eloop(struct i802_bss *bss) +{ + nl80211_register_eloop_read(&bss->nl_mgmt, + wpa_driver_nl80211_event_receive, + bss->nl_cb); +} + + +static int nl80211_register_action_frame(struct i802_bss *bss, + const u8 *match, size_t match_len) +{ + u16 type = (WLAN_FC_TYPE_MGMT << 2) | (WLAN_FC_STYPE_ACTION << 4); + return nl80211_register_frame(bss, bss->nl_mgmt, + type, match, match_len); +} + + +static int nl80211_mgmt_subscribe_non_ap(struct i802_bss *bss) +{ + struct wpa_driver_nl80211_data *drv = bss->drv; + int ret = 0; + + if (nl80211_alloc_mgmt_handle(bss)) + return -1; + wpa_printf(MSG_DEBUG, "nl80211: Subscribe to mgmt frames with non-AP " + "handle %p", bss->nl_mgmt); + + if (drv->nlmode == NL80211_IFTYPE_ADHOC) { + u16 type = (WLAN_FC_TYPE_MGMT << 2) | (WLAN_FC_STYPE_AUTH << 4); + + /* register for any AUTH message */ + nl80211_register_frame(bss, bss->nl_mgmt, type, NULL, 0); + } + +#ifdef CONFIG_INTERWORKING + /* QoS Map Configure */ + if (nl80211_register_action_frame(bss, (u8 *) "\x01\x04", 2) < 0) + ret = -1; +#endif /* CONFIG_INTERWORKING */ #if defined(CONFIG_P2P) || defined(CONFIG_INTERWORKING) /* GAS Initial Request */ - if (nl80211_register_action_frame(drv, (u8 *) "\x04\x0a", 2) < 0) - return -1; + if (nl80211_register_action_frame(bss, (u8 *) "\x04\x0a", 2) < 0) + ret = -1; /* GAS Initial Response */ - if (nl80211_register_action_frame(drv, (u8 *) "\x04\x0b", 2) < 0) - return -1; + if (nl80211_register_action_frame(bss, (u8 *) "\x04\x0b", 2) < 0) + ret = -1; /* GAS Comeback Request */ - if (nl80211_register_action_frame(drv, (u8 *) "\x04\x0c", 2) < 0) - return -1; + if (nl80211_register_action_frame(bss, (u8 *) "\x04\x0c", 2) < 0) + ret = -1; /* GAS Comeback Response */ - if (nl80211_register_action_frame(drv, (u8 *) "\x04\x0d", 2) < 0) - return -1; + if (nl80211_register_action_frame(bss, (u8 *) "\x04\x0d", 2) < 0) + ret = -1; + /* Protected GAS Initial Request */ + if (nl80211_register_action_frame(bss, (u8 *) "\x09\x0a", 2) < 0) + ret = -1; + /* Protected GAS Initial Response */ + if (nl80211_register_action_frame(bss, (u8 *) "\x09\x0b", 2) < 0) + ret = -1; + /* Protected GAS Comeback Request */ + if (nl80211_register_action_frame(bss, (u8 *) "\x09\x0c", 2) < 0) + ret = -1; + /* Protected GAS Comeback Response */ + if (nl80211_register_action_frame(bss, (u8 *) "\x09\x0d", 2) < 0) + ret = -1; #endif /* CONFIG_P2P || CONFIG_INTERWORKING */ #ifdef CONFIG_P2P /* P2P Public Action */ - if (nl80211_register_action_frame(drv, + if (nl80211_register_action_frame(bss, (u8 *) "\x04\x09\x50\x6f\x9a\x09", 6) < 0) - return -1; + ret = -1; /* P2P Action */ - if (nl80211_register_action_frame(drv, + if (nl80211_register_action_frame(bss, (u8 *) "\x7f\x50\x6f\x9a\x09", 5) < 0) - return -1; + ret = -1; #endif /* CONFIG_P2P */ #ifdef CONFIG_IEEE80211W /* SA Query Response */ - if (nl80211_register_action_frame(drv, (u8 *) "\x08\x01", 2) < 0) - return -1; + if (nl80211_register_action_frame(bss, (u8 *) "\x08\x01", 2) < 0) + ret = -1; #endif /* CONFIG_IEEE80211W */ #ifdef CONFIG_TDLS if ((drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_SUPPORT)) { /* TDLS Discovery Response */ - if (nl80211_register_action_frame(drv, (u8 *) "\x04\x0e", 2) < + if (nl80211_register_action_frame(bss, (u8 *) "\x04\x0e", 2) < 0) - return -1; + ret = -1; } #endif /* CONFIG_TDLS */ /* FT Action frames */ - if (nl80211_register_action_frame(drv, (u8 *) "\x06", 1) < 0) - return -1; + if (nl80211_register_action_frame(bss, (u8 *) "\x06", 1) < 0) + ret = -1; else drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FT | WPA_DRIVER_CAPA_KEY_MGMT_FT_PSK; /* WNM - BSS Transition Management Request */ - if (nl80211_register_action_frame(drv, (u8 *) "\x0a\x07", 2) < 0) + if (nl80211_register_action_frame(bss, (u8 *) "\x0a\x07", 2) < 0) + ret = -1; + /* WNM-Sleep Mode Response */ + if (nl80211_register_action_frame(bss, (u8 *) "\x0a\x11", 2) < 0) + ret = -1; + + nl80211_mgmt_handle_register_eloop(bss); + + return ret; +} + + +static int nl80211_register_spurious_class3(struct i802_bss *bss) +{ + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + int ret = -1; + + msg = nlmsg_alloc(); + if (!msg) + return -1; + + nl80211_cmd(drv, msg, 0, NL80211_CMD_UNEXPECTED_FRAME); + + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, bss->ifindex); + + ret = send_and_recv(drv->global, bss->nl_mgmt, msg, NULL, NULL); + msg = NULL; + if (ret) { + wpa_printf(MSG_DEBUG, "nl80211: Register spurious class3 " + "failed: ret=%d (%s)", + ret, strerror(-ret)); + goto nla_put_failure; + } + ret = 0; +nla_put_failure: + nlmsg_free(msg); + return ret; +} + + +static int nl80211_mgmt_subscribe_ap(struct i802_bss *bss) +{ + static const int stypes[] = { + WLAN_FC_STYPE_AUTH, + WLAN_FC_STYPE_ASSOC_REQ, + WLAN_FC_STYPE_REASSOC_REQ, + WLAN_FC_STYPE_DISASSOC, + WLAN_FC_STYPE_DEAUTH, + WLAN_FC_STYPE_ACTION, + WLAN_FC_STYPE_PROBE_REQ, +/* Beacon doesn't work as mac80211 doesn't currently allow + * it, but it wouldn't really be the right thing anyway as + * it isn't per interface ... maybe just dump the scan + * results periodically for OLBC? + */ +// WLAN_FC_STYPE_BEACON, + }; + unsigned int i; + + if (nl80211_alloc_mgmt_handle(bss)) + return -1; + wpa_printf(MSG_DEBUG, "nl80211: Subscribe to mgmt frames with AP " + "handle %p", bss->nl_mgmt); + + for (i = 0; i < ARRAY_SIZE(stypes); i++) { + if (nl80211_register_frame(bss, bss->nl_mgmt, + (WLAN_FC_TYPE_MGMT << 2) | + (stypes[i] << 4), + NULL, 0) < 0) { + goto out_err; + } + } + + if (nl80211_register_spurious_class3(bss)) + goto out_err; + + if (nl80211_get_wiphy_data_ap(bss) == NULL) + goto out_err; + + nl80211_mgmt_handle_register_eloop(bss); + return 0; + +out_err: + nl_destroy_handles(&bss->nl_mgmt); + return -1; +} + + +static int nl80211_mgmt_subscribe_ap_dev_sme(struct i802_bss *bss) +{ + if (nl80211_alloc_mgmt_handle(bss)) return -1; + wpa_printf(MSG_DEBUG, "nl80211: Subscribe to mgmt frames with AP " + "handle %p (device SME)", bss->nl_mgmt); + + if (nl80211_register_frame(bss, bss->nl_mgmt, + (WLAN_FC_TYPE_MGMT << 2) | + (WLAN_FC_STYPE_ACTION << 4), + NULL, 0) < 0) + goto out_err; + nl80211_mgmt_handle_register_eloop(bss); return 0; + +out_err: + nl_destroy_handles(&bss->nl_mgmt); + return -1; +} + + +static void nl80211_mgmt_unsubscribe(struct i802_bss *bss, const char *reason) +{ + if (bss->nl_mgmt == NULL) + return; + wpa_printf(MSG_DEBUG, "nl80211: Unsubscribe mgmt frames handle %p " + "(%s)", bss->nl_mgmt, reason); + nl80211_destroy_eloop_handle(&bss->nl_mgmt); + + nl80211_put_wiphy_data_ap(bss); } @@ -2518,28 +4496,129 @@ } +static void nl80211_del_p2pdev(struct i802_bss *bss) +{ + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + int ret; + + msg = nlmsg_alloc(); + if (!msg) + return; + + nl80211_cmd(drv, msg, 0, NL80211_CMD_DEL_INTERFACE); + NLA_PUT_U64(msg, NL80211_ATTR_WDEV, bss->wdev_id); + + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + msg = NULL; + + wpa_printf(MSG_DEBUG, "nl80211: Delete P2P Device %s (0x%llx): %s", + bss->ifname, (long long unsigned int) bss->wdev_id, + strerror(-ret)); + +nla_put_failure: + nlmsg_free(msg); +} + + +static int nl80211_set_p2pdev(struct i802_bss *bss, int start) +{ + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + int ret = -1; + + msg = nlmsg_alloc(); + if (!msg) + return -1; + + if (start) + nl80211_cmd(drv, msg, 0, NL80211_CMD_START_P2P_DEVICE); + else + nl80211_cmd(drv, msg, 0, NL80211_CMD_STOP_P2P_DEVICE); + + NLA_PUT_U64(msg, NL80211_ATTR_WDEV, bss->wdev_id); + + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + msg = NULL; + + wpa_printf(MSG_DEBUG, "nl80211: %s P2P Device %s (0x%llx): %s", + start ? "Start" : "Stop", + bss->ifname, (long long unsigned int) bss->wdev_id, + strerror(-ret)); + +nla_put_failure: + nlmsg_free(msg); + return ret; +} + + +static int i802_set_iface_flags(struct i802_bss *bss, int up) +{ + enum nl80211_iftype nlmode; + + nlmode = nl80211_get_ifmode(bss); + if (nlmode != NL80211_IFTYPE_P2P_DEVICE) { + return linux_set_iface_flags(bss->drv->global->ioctl_sock, + bss->ifname, up); + } + + /* P2P Device has start/stop which is equivalent */ + return nl80211_set_p2pdev(bss, up); +} + + static int -wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv) +wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv, + const u8 *set_addr, int first) { - struct i802_bss *bss = &drv->first_bss; + struct i802_bss *bss = drv->first_bss; int send_rfkill_event = 0; + enum nl80211_iftype nlmode; drv->ifindex = if_nametoindex(bss->ifname); - drv->first_bss.ifindex = drv->ifindex; + bss->ifindex = drv->ifindex; + bss->wdev_id = drv->global->if_add_wdevid; + bss->wdev_id_set = drv->global->if_add_wdevid_set; + + bss->if_dynamic = drv->ifindex == drv->global->if_add_ifindex; + bss->if_dynamic = bss->if_dynamic || drv->global->if_add_wdevid_set; + drv->global->if_add_wdevid_set = 0; -#ifndef HOSTAPD - /* - * Make sure the interface starts up in station mode unless this is a - * dynamically added interface (e.g., P2P) that was already configured - * with proper iftype. - */ - if (drv->ifindex != drv->global->if_add_ifindex && - wpa_driver_nl80211_set_mode(bss, NL80211_IFTYPE_STATION) < 0) { - wpa_printf(MSG_ERROR, "nl80211: Could not configure driver to " - "use managed mode"); + if (wpa_driver_nl80211_capa(drv)) + return -1; + + wpa_printf(MSG_DEBUG, "nl80211: interface %s in phy %s", + bss->ifname, drv->phyname); + + if (set_addr && + (linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 0) || + linux_set_ifhwaddr(drv->global->ioctl_sock, bss->ifname, + set_addr))) + return -1; + + if (first && nl80211_get_ifmode(bss) == NL80211_IFTYPE_AP) + drv->start_mode_ap = 1; + + if (drv->hostapd) + nlmode = NL80211_IFTYPE_AP; + else if (bss->if_dynamic) + nlmode = nl80211_get_ifmode(bss); + else + nlmode = NL80211_IFTYPE_STATION; + + if (wpa_driver_nl80211_set_mode(bss, nlmode) < 0) { + wpa_printf(MSG_ERROR, "nl80211: Could not configure driver mode"); return -1; } + if (nlmode == NL80211_IFTYPE_P2P_DEVICE) { + int ret = nl80211_set_p2pdev(bss, 1); + if (ret < 0) + wpa_printf(MSG_ERROR, "nl80211: Could not start P2P device"); + nl80211_get_macaddr(bss); + return ret; + } + if (linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 1)) { if (rfkill_is_blocked(drv->rfkill)) { wpa_printf(MSG_DEBUG, "nl80211: Could not yet enable " @@ -2554,27 +4633,14 @@ } } - netlink_send_oper_ifla(drv->global->netlink, drv->ifindex, - 1, IF_OPER_DORMANT); -#endif /* HOSTAPD */ - - if (wpa_driver_nl80211_capa(drv)) - return -1; + if (!drv->hostapd) + netlink_send_oper_ifla(drv->global->netlink, drv->ifindex, + 1, IF_OPER_DORMANT); if (linux_get_ifhwaddr(drv->global->ioctl_sock, bss->ifname, - drv->addr)) + bss->addr)) return -1; - if (nl80211_register_action_frames(drv) < 0) { - wpa_printf(MSG_DEBUG, "nl80211: Failed to register Action " - "frame processing - ignore for now"); - /* - * Older kernel versions did not support this, so ignore the - * error for now. Some functionality may not be available - * because of this. - */ - } - if (send_rfkill_event) { eloop_register_timeout(0, 0, wpa_driver_nl80211_send_rfkill, drv, drv->ctx); @@ -2592,6 +4658,8 @@ if (!msg) return -ENOMEM; + wpa_printf(MSG_DEBUG, "nl80211: Remove beacon (ifindex=%d)", + drv->ifindex); nl80211_cmd(drv, msg, 0, NL80211_CMD_DEL_BEACON); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); @@ -2604,22 +4672,22 @@ /** * wpa_driver_nl80211_deinit - Deinitialize nl80211 driver interface - * @priv: Pointer to private nl80211 data from wpa_driver_nl80211_init() + * @bss: Pointer to private nl80211 data from wpa_driver_nl80211_init() * * Shut down driver interface and processing of driver events. Free * private data buffer if one was allocated in wpa_driver_nl80211_init(). */ -static void wpa_driver_nl80211_deinit(void *priv) +static void wpa_driver_nl80211_deinit(struct i802_bss *bss) { - struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; -#ifdef CONFIG_AP - if (drv->l2) - l2_packet_deinit(drv->l2); -#endif /* CONFIG_AP */ + bss->in_deinit = 1; + if (drv->data_tx_status) + eloop_unregister_read_sock(drv->eapol_tx_sock); + if (drv->eapol_tx_sock >= 0) + close(drv->eapol_tx_sock); - if (drv->nl_preq.handle) + if (bss->nl_preq) wpa_driver_nl80211_probe_req_report(bss, 0); if (bss->added_if_into_bridge) { if (linux_br_del_if(drv->global->ioctl_sock, bss->brname, @@ -2640,15 +4708,6 @@ if (is_ap_interface(drv->nlmode)) wpa_driver_nl80211_del_beacon(drv); -#ifdef HOSTAPD - if (drv->last_freq_ht) { - /* Clear HT flags from the driver */ - struct hostapd_freq_params freq; - os_memset(&freq, 0, sizeof(freq)); - freq.freq = drv->last_freq; - i802_set_freq(priv, &freq); - } - if (drv->eapol_sock >= 0) { eloop_unregister_read_sock(drv->eapol_sock); close(drv->eapol_sock); @@ -2656,7 +4715,6 @@ if (drv->if_indices != drv->default_if_indices) os_free(drv->if_indices); -#endif /* HOSTAPD */ if (drv->disabled_11b_rates) nl80211_disable_11b_rates(drv, drv->ifindex, 0); @@ -2667,17 +4725,31 @@ eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, drv->ctx); - (void) linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 0); - wpa_driver_nl80211_set_mode(bss, NL80211_IFTYPE_STATION); + if (!drv->start_iface_up) + (void) i802_set_iface_flags(bss, 0); + if (drv->nlmode != NL80211_IFTYPE_P2P_DEVICE) { + if (!drv->hostapd || !drv->start_mode_ap) + wpa_driver_nl80211_set_mode(bss, + NL80211_IFTYPE_STATION); + nl80211_mgmt_unsubscribe(bss, "deinit"); + } else { + nl80211_mgmt_unsubscribe(bss, "deinit"); + nl80211_del_p2pdev(bss); + } + nl_cb_put(drv->nl_cb); - eloop_unregister_read_sock(nl_socket_get_fd(drv->nl_event.handle)); - nl_destroy_handles(&drv->nl_event); + nl80211_destroy_bss(drv->first_bss); os_free(drv->filter_ssids); + os_free(drv->auth_ie); + if (drv->in_interface_list) dl_list_del(&drv->list); + os_free(drv->extended_capa); + os_free(drv->extended_capa_mask); + os_free(drv->first_bss); os_free(drv); } @@ -2694,7 +4766,7 @@ { struct wpa_driver_nl80211_data *drv = eloop_ctx; if (drv->ap_scan_as_station != NL80211_IFTYPE_UNSPECIFIED) { - wpa_driver_nl80211_set_mode(&drv->first_bss, + wpa_driver_nl80211_set_mode(drv->first_bss, drv->ap_scan_as_station); drv->ap_scan_as_station = NL80211_IFTYPE_UNSPECIFIED; } @@ -2703,78 +4775,122 @@ } -/** - * wpa_driver_nl80211_scan - Request the driver to initiate scan - * @priv: Pointer to private driver data from wpa_driver_nl80211_init() - * @params: Scan parameters - * Returns: 0 on success, -1 on failure - */ -static int wpa_driver_nl80211_scan(void *priv, - struct wpa_driver_scan_params *params) +static struct nl_msg * +nl80211_scan_common(struct wpa_driver_nl80211_data *drv, u8 cmd, + struct wpa_driver_scan_params *params, u64 *wdev_id) { - struct i802_bss *bss = priv; - struct wpa_driver_nl80211_data *drv = bss->drv; - int ret = 0, timeout; - struct nl_msg *msg, *ssids, *freqs, *rates; + struct nl_msg *msg; size_t i; msg = nlmsg_alloc(); - ssids = nlmsg_alloc(); - freqs = nlmsg_alloc(); - rates = nlmsg_alloc(); - if (!msg || !ssids || !freqs || !rates) { - nlmsg_free(msg); - nlmsg_free(ssids); - nlmsg_free(freqs); - nlmsg_free(rates); - return -1; - } + if (!msg) + return NULL; - os_free(drv->filter_ssids); - drv->filter_ssids = params->filter_ssids; - params->filter_ssids = NULL; - drv->num_filter_ssids = params->num_filter_ssids; + nl80211_cmd(drv, msg, 0, cmd); - nl80211_cmd(drv, msg, 0, NL80211_CMD_TRIGGER_SCAN); + if (!wdev_id) + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + else + NLA_PUT_U64(msg, NL80211_ATTR_WDEV, *wdev_id); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + if (params->num_ssids) { + struct nlattr *ssids; - for (i = 0; i < params->num_ssids; i++) { - wpa_hexdump_ascii(MSG_MSGDUMP, "nl80211: Scan SSID", - params->ssids[i].ssid, - params->ssids[i].ssid_len); - NLA_PUT(ssids, i + 1, params->ssids[i].ssid_len, - params->ssids[i].ssid); + ssids = nla_nest_start(msg, NL80211_ATTR_SCAN_SSIDS); + if (ssids == NULL) + goto fail; + for (i = 0; i < params->num_ssids; i++) { + wpa_hexdump_ascii(MSG_MSGDUMP, "nl80211: Scan SSID", + params->ssids[i].ssid, + params->ssids[i].ssid_len); + if (nla_put(msg, i + 1, params->ssids[i].ssid_len, + params->ssids[i].ssid) < 0) + goto fail; + } + nla_nest_end(msg, ssids); } - if (params->num_ssids) - nla_put_nested(msg, NL80211_ATTR_SCAN_SSIDS, ssids); if (params->extra_ies) { - wpa_hexdump_ascii(MSG_MSGDUMP, "nl80211: Scan extra IEs", - params->extra_ies, params->extra_ies_len); - NLA_PUT(msg, NL80211_ATTR_IE, params->extra_ies_len, - params->extra_ies); + wpa_hexdump(MSG_MSGDUMP, "nl80211: Scan extra IEs", + params->extra_ies, params->extra_ies_len); + if (nla_put(msg, NL80211_ATTR_IE, params->extra_ies_len, + params->extra_ies) < 0) + goto fail; } if (params->freqs) { + struct nlattr *freqs; + freqs = nla_nest_start(msg, NL80211_ATTR_SCAN_FREQUENCIES); + if (freqs == NULL) + goto fail; for (i = 0; params->freqs[i]; i++) { wpa_printf(MSG_MSGDUMP, "nl80211: Scan frequency %u " "MHz", params->freqs[i]); - NLA_PUT_U32(freqs, i + 1, params->freqs[i]); + if (nla_put_u32(msg, i + 1, params->freqs[i]) < 0) + goto fail; } - nla_put_nested(msg, NL80211_ATTR_SCAN_FREQUENCIES, freqs); + nla_nest_end(msg, freqs); + } + + os_free(drv->filter_ssids); + drv->filter_ssids = params->filter_ssids; + params->filter_ssids = NULL; + drv->num_filter_ssids = params->num_filter_ssids; + + if (params->only_new_results) { + wpa_printf(MSG_DEBUG, "nl80211: Add NL80211_SCAN_FLAG_FLUSH"); + NLA_PUT_U32(msg, NL80211_ATTR_SCAN_FLAGS, + NL80211_SCAN_FLAG_FLUSH); } + return msg; + +fail: +nla_put_failure: + nlmsg_free(msg); + return NULL; +} + + +/** + * wpa_driver_nl80211_scan - Request the driver to initiate scan + * @bss: Pointer to private driver data from wpa_driver_nl80211_init() + * @params: Scan parameters + * Returns: 0 on success, -1 on failure + */ +static int wpa_driver_nl80211_scan(struct i802_bss *bss, + struct wpa_driver_scan_params *params) +{ + struct wpa_driver_nl80211_data *drv = bss->drv; + int ret = -1, timeout; + struct nl_msg *msg = NULL; + + wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: scan request"); + drv->scan_for_auth = 0; + + msg = nl80211_scan_common(drv, NL80211_CMD_TRIGGER_SCAN, params, + bss->wdev_id_set ? &bss->wdev_id : NULL); + if (!msg) + return -1; + if (params->p2p_probe) { + struct nlattr *rates; + + wpa_printf(MSG_DEBUG, "nl80211: P2P probe - mask SuppRates"); + + rates = nla_nest_start(msg, NL80211_ATTR_SCAN_SUPP_RATES); + if (rates == NULL) + goto nla_put_failure; + /* * Remove 2.4 GHz rates 1, 2, 5.5, 11 Mbps from supported rates * by masking out everything else apart from the OFDM rates 6, * 9, 12, 18, 24, 36, 48, 54 Mbps from non-MCS rates. All 5 GHz * rates are left enabled. */ - NLA_PUT(rates, NL80211_BAND_2GHZ, 8, + NLA_PUT(msg, NL80211_BAND_2GHZ, 8, "\x0c\x12\x18\x24\x30\x48\x60\x6c"); - nla_put_nested(msg, NL80211_ATTR_SCAN_SUPP_RATES, rates); + nla_nest_end(msg, rates); NLA_PUT_FLAG(msg, NL80211_ATTR_TX_NO_CCK_RATE); } @@ -2784,8 +4900,7 @@ if (ret) { wpa_printf(MSG_DEBUG, "nl80211: Scan trigger failed: ret=%d " "(%s)", ret, strerror(-ret)); -#ifdef HOSTAPD - if (is_ap_interface(drv->nlmode)) { + if (drv->hostapd && is_ap_interface(drv->nlmode)) { /* * mac80211 does not allow scan requests in AP mode, so * try to do this in station mode. @@ -2794,7 +4909,7 @@ bss, NL80211_IFTYPE_STATION)) goto nla_put_failure; - if (wpa_driver_nl80211_scan(drv, params)) { + if (wpa_driver_nl80211_scan(bss, params)) { wpa_driver_nl80211_set_mode(bss, drv->nlmode); goto nla_put_failure; } @@ -2804,11 +4919,9 @@ ret = 0; } else goto nla_put_failure; -#else /* HOSTAPD */ - goto nla_put_failure; -#endif /* HOSTAPD */ } + drv->scan_state = SCAN_REQUESTED; /* Not all drivers generate "scan completed" wireless event, so try to * read results after a timeout. */ timeout = 10; @@ -2827,10 +4940,7 @@ drv, drv->ctx); nla_put_failure: - nlmsg_free(ssids); nlmsg_free(msg); - nlmsg_free(freqs); - nlmsg_free(rates); return ret; } @@ -2848,80 +4958,73 @@ { struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; - int ret = 0; - struct nl_msg *msg, *ssids, *freqs, *match_set_ssid, *match_sets; + int ret = -1; + struct nl_msg *msg; size_t i; - msg = nlmsg_alloc(); - ssids = nlmsg_alloc(); - freqs = nlmsg_alloc(); - if (!msg || !ssids || !freqs) { - nlmsg_free(msg); - nlmsg_free(ssids); - nlmsg_free(freqs); - return -1; - } - - os_free(drv->filter_ssids); - drv->filter_ssids = params->filter_ssids; - params->filter_ssids = NULL; - drv->num_filter_ssids = params->num_filter_ssids; + wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: sched_scan request"); - nl80211_cmd(drv, msg, 0, NL80211_CMD_START_SCHED_SCAN); +#ifdef ANDROID + if (!drv->capa.sched_scan_supported) + return android_pno_start(bss, params); +#endif /* ANDROID */ - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + msg = nl80211_scan_common(drv, NL80211_CMD_START_SCHED_SCAN, params, + bss->wdev_id_set ? &bss->wdev_id : NULL); + if (!msg) + goto nla_put_failure; NLA_PUT_U32(msg, NL80211_ATTR_SCHED_SCAN_INTERVAL, interval); - if (drv->num_filter_ssids) { - match_sets = nlmsg_alloc(); + if ((drv->num_filter_ssids && + (int) drv->num_filter_ssids <= drv->capa.max_match_sets) || + params->filter_rssi) { + struct nlattr *match_sets; + match_sets = nla_nest_start(msg, NL80211_ATTR_SCHED_SCAN_MATCH); + if (match_sets == NULL) + goto nla_put_failure; for (i = 0; i < drv->num_filter_ssids; i++) { + struct nlattr *match_set_ssid; wpa_hexdump_ascii(MSG_MSGDUMP, "nl80211: Sched scan filter SSID", drv->filter_ssids[i].ssid, drv->filter_ssids[i].ssid_len); - match_set_ssid = nlmsg_alloc(); - nla_put(match_set_ssid, - NL80211_ATTR_SCHED_SCAN_MATCH_SSID, + match_set_ssid = nla_nest_start(msg, i + 1); + if (match_set_ssid == NULL) + goto nla_put_failure; + NLA_PUT(msg, NL80211_ATTR_SCHED_SCAN_MATCH_SSID, drv->filter_ssids[i].ssid_len, drv->filter_ssids[i].ssid); + if (params->filter_rssi) + NLA_PUT_U32(msg, + NL80211_SCHED_SCAN_MATCH_ATTR_RSSI, + params->filter_rssi); - nla_put_nested(match_sets, i + 1, match_set_ssid); - - nlmsg_free(match_set_ssid); + nla_nest_end(msg, match_set_ssid); } - nla_put_nested(msg, NL80211_ATTR_SCHED_SCAN_MATCH, - match_sets); - nlmsg_free(match_sets); - } - - for (i = 0; i < params->num_ssids; i++) { - wpa_hexdump_ascii(MSG_MSGDUMP, "nl80211: Sched scan SSID", - params->ssids[i].ssid, - params->ssids[i].ssid_len); - NLA_PUT(ssids, i + 1, params->ssids[i].ssid_len, - params->ssids[i].ssid); - } - if (params->num_ssids) - nla_put_nested(msg, NL80211_ATTR_SCAN_SSIDS, ssids); - - if (params->extra_ies) { - wpa_hexdump_ascii(MSG_MSGDUMP, "nl80211: Sched scan extra IEs", - params->extra_ies, params->extra_ies_len); - NLA_PUT(msg, NL80211_ATTR_IE, params->extra_ies_len, - params->extra_ies); - } - - if (params->freqs) { - for (i = 0; params->freqs[i]; i++) { - wpa_printf(MSG_MSGDUMP, "nl80211: Scan frequency %u " - "MHz", params->freqs[i]); - NLA_PUT_U32(freqs, i + 1, params->freqs[i]); + /* + * Due to backward compatibility code, newer kernels treat this + * matchset (with only an RSSI filter) as the default for all + * other matchsets, unless it's the only one, in which case the + * matchset will actually allow all SSIDs above the RSSI. + */ + if (params->filter_rssi) { + struct nlattr *match_set_rssi; + match_set_rssi = nla_nest_start(msg, 0); + if (match_set_rssi == NULL) + goto nla_put_failure; + NLA_PUT_U32(msg, NL80211_SCHED_SCAN_MATCH_ATTR_RSSI, + params->filter_rssi); + wpa_printf(MSG_MSGDUMP, + "nl80211: Sched scan RSSI filter %d dBm", + params->filter_rssi); + nla_nest_end(msg, match_set_rssi); } - nla_put_nested(msg, NL80211_ATTR_SCAN_FREQUENCIES, freqs); + + nla_nest_end(msg, match_sets); } ret = send_and_recv_msgs(drv, msg, NULL, NULL); @@ -2939,9 +5042,7 @@ "scan interval %d msec", ret, interval); nla_put_failure: - nlmsg_free(ssids); nlmsg_free(msg); - nlmsg_free(freqs); return ret; } @@ -2958,6 +5059,11 @@ int ret = 0; struct nl_msg *msg; +#ifdef ANDROID + if (!drv->capa.sched_scan_supported) + return android_pno_stop(bss); +#endif /* ANDROID */ + msg = nlmsg_alloc(); if (!msg) return -1; @@ -3157,7 +5263,8 @@ * BSSID,SSID pair is seen on multiple channels. wpa_supplicant does * not use frequency as a separate key in the BSS table, so filter out * duplicated entries. Prefer associated BSS entry in such a case in - * order to get the correct frequency into the BSS table. + * order to get the correct frequency into the BSS table. Similarly, + * prefer newer entries over older. */ for (i = 0; i < res->num; i++) { const u8 *s1, *s2; @@ -3175,8 +5282,9 @@ wpa_printf(MSG_DEBUG, "nl80211: Remove duplicated scan result " "for " MACSTR, MAC2STR(r->bssid)); - if ((r->flags & WPA_SCAN_ASSOCIATED) && - !(res->res[i]->flags & WPA_SCAN_ASSOCIATED)) { + if (((r->flags & WPA_SCAN_ASSOCIATED) && + !(res->res[i]->flags & WPA_SCAN_ASSOCIATED)) || + r->age < res->res[i]->age) { os_free(res->res[i]); res->res[i] = r; } else @@ -3184,8 +5292,8 @@ return NL_SKIP; } - tmp = os_realloc(res->res, - (res->num + 1) * sizeof(struct wpa_scan_res *)); + tmp = os_realloc_array(res->res, res->num + 1, + sizeof(struct wpa_scan_res *)); if (tmp == NULL) { os_free(r); return NL_SKIP; @@ -3277,7 +5385,8 @@ goto nla_put_failure; nl80211_cmd(drv, msg, NLM_F_DUMP, NL80211_CMD_GET_SCAN); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + if (nl80211_set_iface_id(msg, drv->first_bss) < 0) + goto nla_put_failure; arg.drv = drv; arg.res = res; @@ -3341,25 +5450,121 @@ } -static int wpa_driver_nl80211_set_key(const char *ifname, void *priv, +static u32 wpa_alg_to_cipher_suite(enum wpa_alg alg, size_t key_len) +{ + switch (alg) { + case WPA_ALG_WEP: + if (key_len == 5) + return WLAN_CIPHER_SUITE_WEP40; + return WLAN_CIPHER_SUITE_WEP104; + case WPA_ALG_TKIP: + return WLAN_CIPHER_SUITE_TKIP; + case WPA_ALG_CCMP: + return WLAN_CIPHER_SUITE_CCMP; + case WPA_ALG_GCMP: + return WLAN_CIPHER_SUITE_GCMP; + case WPA_ALG_CCMP_256: + return WLAN_CIPHER_SUITE_CCMP_256; + case WPA_ALG_GCMP_256: + return WLAN_CIPHER_SUITE_GCMP_256; + case WPA_ALG_IGTK: + return WLAN_CIPHER_SUITE_AES_CMAC; + case WPA_ALG_BIP_GMAC_128: + return WLAN_CIPHER_SUITE_BIP_GMAC_128; + case WPA_ALG_BIP_GMAC_256: + return WLAN_CIPHER_SUITE_BIP_GMAC_256; + case WPA_ALG_BIP_CMAC_256: + return WLAN_CIPHER_SUITE_BIP_CMAC_256; + case WPA_ALG_SMS4: + return WLAN_CIPHER_SUITE_SMS4; + case WPA_ALG_KRK: + return WLAN_CIPHER_SUITE_KRK; + case WPA_ALG_NONE: + case WPA_ALG_PMK: + wpa_printf(MSG_ERROR, "nl80211: Unexpected encryption algorithm %d", + alg); + return 0; + } + + wpa_printf(MSG_ERROR, "nl80211: Unsupported encryption algorithm %d", + alg); + return 0; +} + + +static u32 wpa_cipher_to_cipher_suite(unsigned int cipher) +{ + switch (cipher) { + case WPA_CIPHER_CCMP_256: + return WLAN_CIPHER_SUITE_CCMP_256; + case WPA_CIPHER_GCMP_256: + return WLAN_CIPHER_SUITE_GCMP_256; + case WPA_CIPHER_CCMP: + return WLAN_CIPHER_SUITE_CCMP; + case WPA_CIPHER_GCMP: + return WLAN_CIPHER_SUITE_GCMP; + case WPA_CIPHER_TKIP: + return WLAN_CIPHER_SUITE_TKIP; + case WPA_CIPHER_WEP104: + return WLAN_CIPHER_SUITE_WEP104; + case WPA_CIPHER_WEP40: + return WLAN_CIPHER_SUITE_WEP40; + } + + return 0; +} + + +static int wpa_cipher_to_cipher_suites(unsigned int ciphers, u32 suites[], + int max_suites) +{ + int num_suites = 0; + + if (num_suites < max_suites && ciphers & WPA_CIPHER_CCMP_256) + suites[num_suites++] = WLAN_CIPHER_SUITE_CCMP_256; + if (num_suites < max_suites && ciphers & WPA_CIPHER_GCMP_256) + suites[num_suites++] = WLAN_CIPHER_SUITE_GCMP_256; + if (num_suites < max_suites && ciphers & WPA_CIPHER_CCMP) + suites[num_suites++] = WLAN_CIPHER_SUITE_CCMP; + if (num_suites < max_suites && ciphers & WPA_CIPHER_GCMP) + suites[num_suites++] = WLAN_CIPHER_SUITE_GCMP; + if (num_suites < max_suites && ciphers & WPA_CIPHER_TKIP) + suites[num_suites++] = WLAN_CIPHER_SUITE_TKIP; + if (num_suites < max_suites && ciphers & WPA_CIPHER_WEP104) + suites[num_suites++] = WLAN_CIPHER_SUITE_WEP104; + if (num_suites < max_suites && ciphers & WPA_CIPHER_WEP40) + suites[num_suites++] = WLAN_CIPHER_SUITE_WEP40; + + return num_suites; +} + + +static int wpa_driver_nl80211_set_key(const char *ifname, struct i802_bss *bss, enum wpa_alg alg, const u8 *addr, int key_idx, int set_tx, const u8 *seq, size_t seq_len, const u8 *key, size_t key_len) { - struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; - int ifindex = if_nametoindex(ifname); + int ifindex; struct nl_msg *msg; int ret; + int tdls = 0; + + /* Ignore for P2P Device */ + if (drv->nlmode == NL80211_IFTYPE_P2P_DEVICE) + return 0; - wpa_printf(MSG_DEBUG, "%s: ifindex=%d alg=%d addr=%p key_idx=%d " + ifindex = if_nametoindex(ifname); + wpa_printf(MSG_DEBUG, "%s: ifindex=%d (%s) alg=%d addr=%p key_idx=%d " "set_tx=%d seq_len=%lu key_len=%lu", - __func__, ifindex, alg, addr, key_idx, set_tx, + __func__, ifindex, ifname, alg, addr, key_idx, set_tx, (unsigned long) seq_len, (unsigned long) key_len); #ifdef CONFIG_TDLS - if (key_idx == -1) + if (key_idx == -1) { key_idx = 0; + tdls = 1; + } #endif /* CONFIG_TDLS */ msg = nlmsg_alloc(); @@ -3371,33 +5576,8 @@ } else { nl80211_cmd(drv, msg, 0, NL80211_CMD_NEW_KEY); NLA_PUT(msg, NL80211_ATTR_KEY_DATA, key_len, key); - switch (alg) { - case WPA_ALG_WEP: - if (key_len == 5) - NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER, - WLAN_CIPHER_SUITE_WEP40); - else - NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER, - WLAN_CIPHER_SUITE_WEP104); - break; - case WPA_ALG_TKIP: - NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER, - WLAN_CIPHER_SUITE_TKIP); - break; - case WPA_ALG_CCMP: - NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER, - WLAN_CIPHER_SUITE_CCMP); - break; - case WPA_ALG_IGTK: - NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER, - WLAN_CIPHER_SUITE_AES_CMAC); - break; - default: - wpa_printf(MSG_ERROR, "%s: Unsupported encryption " - "algorithm %d", __func__, alg); - nlmsg_free(msg); - return -1; - } + NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER, + wpa_alg_to_cipher_suite(alg, key_len)); } if (seq && seq_len) @@ -3413,18 +5593,15 @@ NL80211_KEYTYPE_GROUP); } } else if (addr && is_broadcast_ether_addr(addr)) { - struct nl_msg *types; - int err; + struct nlattr *types; + wpa_printf(MSG_DEBUG, " broadcast key"); - types = nlmsg_alloc(); + + types = nla_nest_start(msg, NL80211_ATTR_KEY_DEFAULT_TYPES); if (!types) goto nla_put_failure; - NLA_PUT_FLAG(types, NL80211_KEY_DEFAULT_TYPE_MULTICAST); - err = nla_put_nested(msg, NL80211_ATTR_KEY_DEFAULT_TYPES, - types); - nlmsg_free(types); - if (err) - goto nla_put_failure; + NLA_PUT_FLAG(msg, NL80211_KEY_DEFAULT_TYPE_MULTICAST); + nla_nest_end(msg, types); } NLA_PUT_U8(msg, NL80211_ATTR_KEY_IDX, key_idx); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifindex); @@ -3440,7 +5617,7 @@ * If we failed or don't need to set the default TX key (below), * we're done here. */ - if (ret || !set_tx || alg == WPA_ALG_NONE) + if (ret || !set_tx || alg == WPA_ALG_NONE || tdls) return ret; if (is_ap_interface(drv->nlmode) && addr && !is_broadcast_ether_addr(addr)) @@ -3458,29 +5635,21 @@ else NLA_PUT_FLAG(msg, NL80211_ATTR_KEY_DEFAULT); if (addr && is_broadcast_ether_addr(addr)) { - struct nl_msg *types; - int err; - types = nlmsg_alloc(); + struct nlattr *types; + + types = nla_nest_start(msg, NL80211_ATTR_KEY_DEFAULT_TYPES); if (!types) goto nla_put_failure; - NLA_PUT_FLAG(types, NL80211_KEY_DEFAULT_TYPE_MULTICAST); - err = nla_put_nested(msg, NL80211_ATTR_KEY_DEFAULT_TYPES, - types); - nlmsg_free(types); - if (err) - goto nla_put_failure; + NLA_PUT_FLAG(msg, NL80211_KEY_DEFAULT_TYPE_MULTICAST); + nla_nest_end(msg, types); } else if (addr) { - struct nl_msg *types; - int err; - types = nlmsg_alloc(); + struct nlattr *types; + + types = nla_nest_start(msg, NL80211_ATTR_KEY_DEFAULT_TYPES); if (!types) goto nla_put_failure; - NLA_PUT_FLAG(types, NL80211_KEY_DEFAULT_TYPE_UNICAST); - err = nla_put_nested(msg, NL80211_ATTR_KEY_DEFAULT_TYPES, - types); - nlmsg_free(types); - if (err) - goto nla_put_failure; + NLA_PUT_FLAG(msg, NL80211_KEY_DEFAULT_TYPE_UNICAST); + nla_nest_end(msg, types); } ret = send_and_recv_msgs(drv, msg, NULL, NULL); @@ -3513,30 +5682,8 @@ NLA_PUT_U8(msg, NL80211_KEY_IDX, key_idx); - switch (alg) { - case WPA_ALG_WEP: - if (key_len == 5) - NLA_PUT_U32(msg, NL80211_KEY_CIPHER, - WLAN_CIPHER_SUITE_WEP40); - else - NLA_PUT_U32(msg, NL80211_KEY_CIPHER, - WLAN_CIPHER_SUITE_WEP104); - break; - case WPA_ALG_TKIP: - NLA_PUT_U32(msg, NL80211_KEY_CIPHER, WLAN_CIPHER_SUITE_TKIP); - break; - case WPA_ALG_CCMP: - NLA_PUT_U32(msg, NL80211_KEY_CIPHER, WLAN_CIPHER_SUITE_CCMP); - break; - case WPA_ALG_IGTK: - NLA_PUT_U32(msg, NL80211_KEY_CIPHER, - WLAN_CIPHER_SUITE_AES_CMAC); - break; - default: - wpa_printf(MSG_ERROR, "%s: Unsupported encryption " - "algorithm %d", __func__, alg); - return -1; - } + NLA_PUT_U32(msg, NL80211_KEY_CIPHER, + wpa_alg_to_cipher_suite(alg, key_len)); if (seq && seq_len) NLA_PUT(msg, NL80211_KEY_SEQ, seq_len, seq); @@ -3626,15 +5773,17 @@ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); NLA_PUT_U16(msg, NL80211_ATTR_REASON_CODE, reason_code); - NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr); + if (addr) + NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr); if (local_state_change) NLA_PUT_FLAG(msg, NL80211_ATTR_LOCAL_STATE_CHANGE); ret = send_and_recv_msgs(drv, msg, NULL, NULL); msg = NULL; if (ret) { - wpa_printf(MSG_DEBUG, "nl80211: MLME command failed: ret=%d " - "(%s)", ret, strerror(-ret)); + wpa_dbg(drv->ctx, MSG_DEBUG, + "nl80211: MLME command failed: reason=%u ret=%d (%s)", + reason_code, ret, strerror(-ret)); goto nla_put_failure; } ret = 0; @@ -3646,26 +5795,34 @@ static int wpa_driver_nl80211_disconnect(struct wpa_driver_nl80211_data *drv, - const u8 *addr, int reason_code) + int reason_code) { - wpa_printf(MSG_DEBUG, "%s(addr=" MACSTR " reason_code=%d)", - __func__, MAC2STR(addr), reason_code); - drv->associated = 0; - return wpa_driver_nl80211_mlme(drv, addr, NL80211_CMD_DISCONNECT, - reason_code, 0); + int ret; + + wpa_printf(MSG_DEBUG, "%s(reason_code=%d)", __func__, reason_code); + nl80211_mark_disconnected(drv); + /* Disconnect command doesn't need BSSID - it uses cached value */ + ret = wpa_driver_nl80211_mlme(drv, NULL, NL80211_CMD_DISCONNECT, + reason_code, 0); + /* + * For locally generated disconnect, supplicant already generates a + * DEAUTH event, so ignore the event from NL80211. + */ + drv->ignore_next_local_disconnect = ret == 0; + + return ret; } -static int wpa_driver_nl80211_deauthenticate(void *priv, const u8 *addr, - int reason_code) +static int wpa_driver_nl80211_deauthenticate(struct i802_bss *bss, + const u8 *addr, int reason_code) { - struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME)) - return wpa_driver_nl80211_disconnect(drv, addr, reason_code); + return wpa_driver_nl80211_disconnect(drv, reason_code); wpa_printf(MSG_DEBUG, "%s(addr=" MACSTR " reason_code=%d)", __func__, MAC2STR(addr), reason_code); - drv->associated = 0; + nl80211_mark_disconnected(drv); if (drv->nlmode == NL80211_IFTYPE_ADHOC) return nl80211_leave_ibss(drv); return wpa_driver_nl80211_mlme(drv, addr, NL80211_CMD_DEAUTHENTICATE, @@ -3673,38 +5830,77 @@ } -static int wpa_driver_nl80211_disassociate(void *priv, const u8 *addr, - int reason_code) +static void nl80211_copy_auth_params(struct wpa_driver_nl80211_data *drv, + struct wpa_driver_auth_params *params) { - struct i802_bss *bss = priv; - struct wpa_driver_nl80211_data *drv = bss->drv; - if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME)) - return wpa_driver_nl80211_disconnect(drv, addr, reason_code); - wpa_printf(MSG_DEBUG, "%s", __func__); - drv->associated = 0; - return wpa_driver_nl80211_mlme(drv, addr, NL80211_CMD_DISASSOCIATE, - reason_code, 0); + int i; + + drv->auth_freq = params->freq; + drv->auth_alg = params->auth_alg; + drv->auth_wep_tx_keyidx = params->wep_tx_keyidx; + drv->auth_local_state_change = params->local_state_change; + drv->auth_p2p = params->p2p; + + if (params->bssid) + os_memcpy(drv->auth_bssid_, params->bssid, ETH_ALEN); + else + os_memset(drv->auth_bssid_, 0, ETH_ALEN); + + if (params->ssid) { + os_memcpy(drv->auth_ssid, params->ssid, params->ssid_len); + drv->auth_ssid_len = params->ssid_len; + } else + drv->auth_ssid_len = 0; + + + os_free(drv->auth_ie); + drv->auth_ie = NULL; + drv->auth_ie_len = 0; + if (params->ie) { + drv->auth_ie = os_malloc(params->ie_len); + if (drv->auth_ie) { + os_memcpy(drv->auth_ie, params->ie, params->ie_len); + drv->auth_ie_len = params->ie_len; + } + } + + for (i = 0; i < 4; i++) { + if (params->wep_key[i] && params->wep_key_len[i] && + params->wep_key_len[i] <= 16) { + os_memcpy(drv->auth_wep_key[i], params->wep_key[i], + params->wep_key_len[i]); + drv->auth_wep_key_len[i] = params->wep_key_len[i]; + } else + drv->auth_wep_key_len[i] = 0; + } } static int wpa_driver_nl80211_authenticate( - void *priv, struct wpa_driver_auth_params *params) + struct i802_bss *bss, struct wpa_driver_auth_params *params) { - struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; int ret = -1, i; struct nl_msg *msg; enum nl80211_auth_type type; enum nl80211_iftype nlmode; int count = 0; + int is_retry; - drv->associated = 0; + is_retry = drv->retry_auth; + drv->retry_auth = 0; + + nl80211_mark_disconnected(drv); os_memset(drv->auth_bssid, 0, ETH_ALEN); + if (params->bssid) + os_memcpy(drv->auth_attempt_bssid, params->bssid, ETH_ALEN); + else + os_memset(drv->auth_attempt_bssid, 0, ETH_ALEN); /* FIX: IBSS mode */ nlmode = params->p2p ? NL80211_IFTYPE_P2P_CLIENT : NL80211_IFTYPE_STATION; if (drv->nlmode != nlmode && - wpa_driver_nl80211_set_mode(priv, nlmode) < 0) + wpa_driver_nl80211_set_mode(bss, nlmode) < 0) return -1; retry: @@ -3720,7 +5916,7 @@ for (i = 0; i < 4; i++) { if (!params->wep_key[i]) continue; - wpa_driver_nl80211_set_key(bss->ifname, priv, WPA_ALG_WEP, + wpa_driver_nl80211_set_key(bss->ifname, bss, WPA_ALG_WEP, NULL, i, i == params->wep_tx_keyidx, NULL, 0, params->wep_key[i], @@ -3753,6 +5949,12 @@ wpa_hexdump(MSG_DEBUG, " * IEs", params->ie, params->ie_len); if (params->ie) NLA_PUT(msg, NL80211_ATTR_IE, params->ie_len, params->ie); + if (params->sae_data) { + wpa_hexdump(MSG_DEBUG, " * SAE data", params->sae_data, + params->sae_data_len); + NLA_PUT(msg, NL80211_ATTR_SAE_DATA, params->sae_data_len, + params->sae_data); + } if (params->auth_alg & WPA_AUTH_ALG_OPEN) type = NL80211_AUTHTYPE_OPEN_SYSTEM; else if (params->auth_alg & WPA_AUTH_ALG_SHARED) @@ -3761,6 +5963,8 @@ type = NL80211_AUTHTYPE_NETWORK_EAP; else if (params->auth_alg & WPA_AUTH_ALG_FT) type = NL80211_AUTHTYPE_FT; + else if (params->auth_alg & WPA_AUTH_ALG_SAE) + type = NL80211_AUTHTYPE_SAE; else goto nla_put_failure; wpa_printf(MSG_DEBUG, " * Auth Type %d", type); @@ -3773,8 +5977,9 @@ ret = send_and_recv_msgs(drv, msg, NULL, NULL); msg = NULL; if (ret) { - wpa_printf(MSG_DEBUG, "nl80211: MLME command failed: ret=%d " - "(%s)", ret, strerror(-ret)); + wpa_dbg(drv->ctx, MSG_DEBUG, + "nl80211: MLME command failed (auth): ret=%d (%s)", + ret, strerror(-ret)); count++; if (ret == -EALREADY && count == 1 && params->bssid && !params->local_state_change) { @@ -3791,6 +5996,49 @@ nlmsg_free(msg); goto retry; } + + if (ret == -ENOENT && params->freq && !is_retry) { + /* + * cfg80211 has likely expired the BSS entry even + * though it was previously available in our internal + * BSS table. To recover quickly, start a single + * channel scan on the specified channel. + */ + struct wpa_driver_scan_params scan; + int freqs[2]; + + os_memset(&scan, 0, sizeof(scan)); + scan.num_ssids = 1; + if (params->ssid) { + scan.ssids[0].ssid = params->ssid; + scan.ssids[0].ssid_len = params->ssid_len; + } + freqs[0] = params->freq; + freqs[1] = 0; + scan.freqs = freqs; + wpa_printf(MSG_DEBUG, "nl80211: Trigger single " + "channel scan to refresh cfg80211 BSS " + "entry"); + ret = wpa_driver_nl80211_scan(bss, &scan); + if (ret == 0) { + nl80211_copy_auth_params(drv, params); + drv->scan_for_auth = 1; + } + } else if (is_retry) { + /* + * Need to indicate this with an event since the return + * value from the retry is not delivered to core code. + */ + union wpa_event_data event; + wpa_printf(MSG_DEBUG, "nl80211: Authentication retry " + "failed"); + os_memset(&event, 0, sizeof(event)); + os_memcpy(event.timeout_event.addr, drv->auth_bssid_, + ETH_ALEN); + wpa_supplicant_event(drv->ctx, EVENT_AUTH_TIMED_OUT, + &event); + } + goto nla_put_failure; } ret = 0; @@ -3803,189 +6051,328 @@ } +static int wpa_driver_nl80211_authenticate_retry( + struct wpa_driver_nl80211_data *drv) +{ + struct wpa_driver_auth_params params; + struct i802_bss *bss = drv->first_bss; + int i; + + wpa_printf(MSG_DEBUG, "nl80211: Try to authenticate again"); + + os_memset(¶ms, 0, sizeof(params)); + params.freq = drv->auth_freq; + params.auth_alg = drv->auth_alg; + params.wep_tx_keyidx = drv->auth_wep_tx_keyidx; + params.local_state_change = drv->auth_local_state_change; + params.p2p = drv->auth_p2p; + + if (!is_zero_ether_addr(drv->auth_bssid_)) + params.bssid = drv->auth_bssid_; + + if (drv->auth_ssid_len) { + params.ssid = drv->auth_ssid; + params.ssid_len = drv->auth_ssid_len; + } + + params.ie = drv->auth_ie; + params.ie_len = drv->auth_ie_len; + + for (i = 0; i < 4; i++) { + if (drv->auth_wep_key_len[i]) { + params.wep_key[i] = drv->auth_wep_key[i]; + params.wep_key_len[i] = drv->auth_wep_key_len[i]; + } + } + + drv->retry_auth = 1; + return wpa_driver_nl80211_authenticate(bss, ¶ms); +} + + struct phy_info_arg { u16 *num_modes; struct hostapd_hw_modes *modes; + int last_mode, last_chan_idx; }; -static int phy_info_handler(struct nl_msg *msg, void *arg) +static void phy_info_ht_capa(struct hostapd_hw_modes *mode, struct nlattr *capa, + struct nlattr *ampdu_factor, + struct nlattr *ampdu_density, + struct nlattr *mcs_set) { - struct nlattr *tb_msg[NL80211_ATTR_MAX + 1]; - struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); - struct phy_info_arg *phy_info = arg; + if (capa) + mode->ht_capab = nla_get_u16(capa); - struct nlattr *tb_band[NL80211_BAND_ATTR_MAX + 1]; + if (ampdu_factor) + mode->a_mpdu_params |= nla_get_u8(ampdu_factor) & 0x03; - struct nlattr *tb_freq[NL80211_FREQUENCY_ATTR_MAX + 1]; - static struct nla_policy freq_policy[NL80211_FREQUENCY_ATTR_MAX + 1] = { - [NL80211_FREQUENCY_ATTR_FREQ] = { .type = NLA_U32 }, - [NL80211_FREQUENCY_ATTR_DISABLED] = { .type = NLA_FLAG }, - [NL80211_FREQUENCY_ATTR_PASSIVE_SCAN] = { .type = NLA_FLAG }, - [NL80211_FREQUENCY_ATTR_NO_IBSS] = { .type = NLA_FLAG }, - [NL80211_FREQUENCY_ATTR_RADAR] = { .type = NLA_FLAG }, - [NL80211_FREQUENCY_ATTR_MAX_TX_POWER] = { .type = NLA_U32 }, - }; + if (ampdu_density) + mode->a_mpdu_params |= nla_get_u8(ampdu_density) << 2; - struct nlattr *tb_rate[NL80211_BITRATE_ATTR_MAX + 1]; - static struct nla_policy rate_policy[NL80211_BITRATE_ATTR_MAX + 1] = { - [NL80211_BITRATE_ATTR_RATE] = { .type = NLA_U32 }, - [NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE] = { .type = NLA_FLAG }, - }; + if (mcs_set && nla_len(mcs_set) >= 16) { + u8 *mcs; + mcs = nla_data(mcs_set); + os_memcpy(mode->mcs_set, mcs, 16); + } +} - struct nlattr *nl_band; - struct nlattr *nl_freq; - struct nlattr *nl_rate; - int rem_band, rem_freq, rem_rate; - struct hostapd_hw_modes *mode; - int idx, mode_is_set; - nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), - genlmsg_attrlen(gnlh, 0), NULL); +static void phy_info_vht_capa(struct hostapd_hw_modes *mode, + struct nlattr *capa, + struct nlattr *mcs_set) +{ + if (capa) + mode->vht_capab = nla_get_u32(capa); - if (!tb_msg[NL80211_ATTR_WIPHY_BANDS]) - return NL_SKIP; + if (mcs_set && nla_len(mcs_set) >= 8) { + u8 *mcs; + mcs = nla_data(mcs_set); + os_memcpy(mode->vht_mcs_set, mcs, 8); + } +} - nla_for_each_nested(nl_band, tb_msg[NL80211_ATTR_WIPHY_BANDS], rem_band) { - mode = os_realloc(phy_info->modes, (*phy_info->num_modes + 1) * sizeof(*mode)); - if (!mode) - return NL_SKIP; - phy_info->modes = mode; - mode_is_set = 0; +static void phy_info_freq(struct hostapd_hw_modes *mode, + struct hostapd_channel_data *chan, + struct nlattr *tb_freq[]) +{ + u8 channel; + chan->freq = nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_FREQ]); + chan->flag = 0; + if (ieee80211_freq_to_chan(chan->freq, &channel) != NUM_HOSTAPD_MODES) + chan->chan = channel; - mode = &phy_info->modes[*(phy_info->num_modes)]; - memset(mode, 0, sizeof(*mode)); - mode->flags = HOSTAPD_MODE_FLAG_HT_INFO_KNOWN; - *(phy_info->num_modes) += 1; + if (tb_freq[NL80211_FREQUENCY_ATTR_DISABLED]) + chan->flag |= HOSTAPD_CHAN_DISABLED; + if (tb_freq[NL80211_FREQUENCY_ATTR_NO_IR]) + chan->flag |= HOSTAPD_CHAN_PASSIVE_SCAN | HOSTAPD_CHAN_NO_IBSS; + if (tb_freq[NL80211_FREQUENCY_ATTR_RADAR]) + chan->flag |= HOSTAPD_CHAN_RADAR; - nla_parse(tb_band, NL80211_BAND_ATTR_MAX, nla_data(nl_band), - nla_len(nl_band), NULL); + if (tb_freq[NL80211_FREQUENCY_ATTR_DFS_STATE]) { + enum nl80211_dfs_state state = + nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_DFS_STATE]); - if (tb_band[NL80211_BAND_ATTR_HT_CAPA]) { - mode->ht_capab = nla_get_u16( - tb_band[NL80211_BAND_ATTR_HT_CAPA]); + switch (state) { + case NL80211_DFS_USABLE: + chan->flag |= HOSTAPD_CHAN_DFS_USABLE; + break; + case NL80211_DFS_AVAILABLE: + chan->flag |= HOSTAPD_CHAN_DFS_AVAILABLE; + break; + case NL80211_DFS_UNAVAILABLE: + chan->flag |= HOSTAPD_CHAN_DFS_UNAVAILABLE; + break; } + } +} - if (tb_band[NL80211_BAND_ATTR_HT_AMPDU_FACTOR]) { - mode->a_mpdu_params |= nla_get_u8( - tb_band[NL80211_BAND_ATTR_HT_AMPDU_FACTOR]) & - 0x03; - } - if (tb_band[NL80211_BAND_ATTR_HT_AMPDU_DENSITY]) { - mode->a_mpdu_params |= nla_get_u8( - tb_band[NL80211_BAND_ATTR_HT_AMPDU_DENSITY]) << - 2; - } +static int phy_info_freqs(struct phy_info_arg *phy_info, + struct hostapd_hw_modes *mode, struct nlattr *tb) +{ + static struct nla_policy freq_policy[NL80211_FREQUENCY_ATTR_MAX + 1] = { + [NL80211_FREQUENCY_ATTR_FREQ] = { .type = NLA_U32 }, + [NL80211_FREQUENCY_ATTR_DISABLED] = { .type = NLA_FLAG }, + [NL80211_FREQUENCY_ATTR_NO_IR] = { .type = NLA_FLAG }, + [NL80211_FREQUENCY_ATTR_RADAR] = { .type = NLA_FLAG }, + [NL80211_FREQUENCY_ATTR_MAX_TX_POWER] = { .type = NLA_U32 }, + [NL80211_FREQUENCY_ATTR_DFS_STATE] = { .type = NLA_U32 }, + }; + int new_channels = 0; + struct hostapd_channel_data *channel; + struct nlattr *tb_freq[NL80211_FREQUENCY_ATTR_MAX + 1]; + struct nlattr *nl_freq; + int rem_freq, idx; - if (tb_band[NL80211_BAND_ATTR_HT_MCS_SET] && - nla_len(tb_band[NL80211_BAND_ATTR_HT_MCS_SET])) { - u8 *mcs; - mcs = nla_data(tb_band[NL80211_BAND_ATTR_HT_MCS_SET]); - os_memcpy(mode->mcs_set, mcs, 16); - } + if (tb == NULL) + return NL_OK; - nla_for_each_nested(nl_freq, tb_band[NL80211_BAND_ATTR_FREQS], rem_freq) { - nla_parse(tb_freq, NL80211_FREQUENCY_ATTR_MAX, nla_data(nl_freq), - nla_len(nl_freq), freq_policy); - if (!tb_freq[NL80211_FREQUENCY_ATTR_FREQ]) - continue; - mode->num_channels++; - } + nla_for_each_nested(nl_freq, tb, rem_freq) { + nla_parse(tb_freq, NL80211_FREQUENCY_ATTR_MAX, + nla_data(nl_freq), nla_len(nl_freq), freq_policy); + if (!tb_freq[NL80211_FREQUENCY_ATTR_FREQ]) + continue; + new_channels++; + } - mode->channels = os_zalloc(mode->num_channels * sizeof(struct hostapd_channel_data)); - if (!mode->channels) - return NL_SKIP; + channel = os_realloc_array(mode->channels, + mode->num_channels + new_channels, + sizeof(struct hostapd_channel_data)); + if (!channel) + return NL_SKIP; - idx = 0; + mode->channels = channel; + mode->num_channels += new_channels; - nla_for_each_nested(nl_freq, tb_band[NL80211_BAND_ATTR_FREQS], rem_freq) { - nla_parse(tb_freq, NL80211_FREQUENCY_ATTR_MAX, nla_data(nl_freq), - nla_len(nl_freq), freq_policy); - if (!tb_freq[NL80211_FREQUENCY_ATTR_FREQ]) - continue; + idx = phy_info->last_chan_idx; - mode->channels[idx].freq = nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_FREQ]); - mode->channels[idx].flag = 0; + nla_for_each_nested(nl_freq, tb, rem_freq) { + nla_parse(tb_freq, NL80211_FREQUENCY_ATTR_MAX, + nla_data(nl_freq), nla_len(nl_freq), freq_policy); + if (!tb_freq[NL80211_FREQUENCY_ATTR_FREQ]) + continue; + phy_info_freq(mode, &mode->channels[idx], tb_freq); + idx++; + } + phy_info->last_chan_idx = idx; - if (!mode_is_set) { - /* crude heuristic */ - if (mode->channels[idx].freq < 4000) - mode->mode = HOSTAPD_MODE_IEEE80211B; - else - mode->mode = HOSTAPD_MODE_IEEE80211A; - mode_is_set = 1; - } + return NL_OK; +} - /* crude heuristic */ - if (mode->channels[idx].freq < 4000) - if (mode->channels[idx].freq == 2484) - mode->channels[idx].chan = 14; - else - mode->channels[idx].chan = (mode->channels[idx].freq - 2407) / 5; - else - mode->channels[idx].chan = mode->channels[idx].freq/5 - 1000; - if (tb_freq[NL80211_FREQUENCY_ATTR_DISABLED]) - mode->channels[idx].flag |= - HOSTAPD_CHAN_DISABLED; - if (tb_freq[NL80211_FREQUENCY_ATTR_PASSIVE_SCAN]) - mode->channels[idx].flag |= - HOSTAPD_CHAN_PASSIVE_SCAN; - if (tb_freq[NL80211_FREQUENCY_ATTR_NO_IBSS]) - mode->channels[idx].flag |= - HOSTAPD_CHAN_NO_IBSS; - if (tb_freq[NL80211_FREQUENCY_ATTR_RADAR]) - mode->channels[idx].flag |= - HOSTAPD_CHAN_RADAR; - - if (tb_freq[NL80211_FREQUENCY_ATTR_MAX_TX_POWER] && - !tb_freq[NL80211_FREQUENCY_ATTR_DISABLED]) - mode->channels[idx].max_tx_power = - nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_MAX_TX_POWER]) / 100; - - idx++; - } - - nla_for_each_nested(nl_rate, tb_band[NL80211_BAND_ATTR_RATES], rem_rate) { - nla_parse(tb_rate, NL80211_BITRATE_ATTR_MAX, nla_data(nl_rate), - nla_len(nl_rate), rate_policy); - if (!tb_rate[NL80211_BITRATE_ATTR_RATE]) - continue; - mode->num_rates++; - } +static int phy_info_rates(struct hostapd_hw_modes *mode, struct nlattr *tb) +{ + static struct nla_policy rate_policy[NL80211_BITRATE_ATTR_MAX + 1] = { + [NL80211_BITRATE_ATTR_RATE] = { .type = NLA_U32 }, + [NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE] = + { .type = NLA_FLAG }, + }; + struct nlattr *tb_rate[NL80211_BITRATE_ATTR_MAX + 1]; + struct nlattr *nl_rate; + int rem_rate, idx; - mode->rates = os_zalloc(mode->num_rates * sizeof(int)); - if (!mode->rates) - return NL_SKIP; + if (tb == NULL) + return NL_OK; - idx = 0; + nla_for_each_nested(nl_rate, tb, rem_rate) { + nla_parse(tb_rate, NL80211_BITRATE_ATTR_MAX, + nla_data(nl_rate), nla_len(nl_rate), + rate_policy); + if (!tb_rate[NL80211_BITRATE_ATTR_RATE]) + continue; + mode->num_rates++; + } - nla_for_each_nested(nl_rate, tb_band[NL80211_BAND_ATTR_RATES], rem_rate) { - nla_parse(tb_rate, NL80211_BITRATE_ATTR_MAX, nla_data(nl_rate), - nla_len(nl_rate), rate_policy); - if (!tb_rate[NL80211_BITRATE_ATTR_RATE]) - continue; - mode->rates[idx] = nla_get_u32(tb_rate[NL80211_BITRATE_ATTR_RATE]); + mode->rates = os_calloc(mode->num_rates, sizeof(int)); + if (!mode->rates) + return NL_SKIP; - /* crude heuristic */ - if (mode->mode == HOSTAPD_MODE_IEEE80211B && - mode->rates[idx] > 200) - mode->mode = HOSTAPD_MODE_IEEE80211G; + idx = 0; - idx++; - } + nla_for_each_nested(nl_rate, tb, rem_rate) { + nla_parse(tb_rate, NL80211_BITRATE_ATTR_MAX, + nla_data(nl_rate), nla_len(nl_rate), + rate_policy); + if (!tb_rate[NL80211_BITRATE_ATTR_RATE]) + continue; + mode->rates[idx] = nla_get_u32( + tb_rate[NL80211_BITRATE_ATTR_RATE]); + idx++; } - return NL_SKIP; + return NL_OK; } -static struct hostapd_hw_modes * -wpa_driver_nl80211_add_11b(struct hostapd_hw_modes *modes, u16 *num_modes) -{ - u16 m; - struct hostapd_hw_modes *mode11g = NULL, *nmodes, *mode; - int i, mode11g_idx = -1; + +static int phy_info_band(struct phy_info_arg *phy_info, struct nlattr *nl_band) +{ + struct nlattr *tb_band[NL80211_BAND_ATTR_MAX + 1]; + struct hostapd_hw_modes *mode; + int ret; + + if (phy_info->last_mode != nl_band->nla_type) { + mode = os_realloc_array(phy_info->modes, + *phy_info->num_modes + 1, + sizeof(*mode)); + if (!mode) + return NL_SKIP; + phy_info->modes = mode; + + mode = &phy_info->modes[*(phy_info->num_modes)]; + os_memset(mode, 0, sizeof(*mode)); + mode->mode = NUM_HOSTAPD_MODES; + mode->flags = HOSTAPD_MODE_FLAG_HT_INFO_KNOWN | + HOSTAPD_MODE_FLAG_VHT_INFO_KNOWN; + + /* + * Unsupported VHT MCS stream is defined as value 3, so the VHT + * MCS RX/TX map must be initialized with 0xffff to mark all 8 + * possible streams as unsupported. This will be overridden if + * driver advertises VHT support. + */ + mode->vht_mcs_set[0] = 0xff; + mode->vht_mcs_set[1] = 0xff; + mode->vht_mcs_set[4] = 0xff; + mode->vht_mcs_set[5] = 0xff; + + *(phy_info->num_modes) += 1; + phy_info->last_mode = nl_band->nla_type; + phy_info->last_chan_idx = 0; + } else + mode = &phy_info->modes[*(phy_info->num_modes) - 1]; + + nla_parse(tb_band, NL80211_BAND_ATTR_MAX, nla_data(nl_band), + nla_len(nl_band), NULL); + + phy_info_ht_capa(mode, tb_band[NL80211_BAND_ATTR_HT_CAPA], + tb_band[NL80211_BAND_ATTR_HT_AMPDU_FACTOR], + tb_band[NL80211_BAND_ATTR_HT_AMPDU_DENSITY], + tb_band[NL80211_BAND_ATTR_HT_MCS_SET]); + phy_info_vht_capa(mode, tb_band[NL80211_BAND_ATTR_VHT_CAPA], + tb_band[NL80211_BAND_ATTR_VHT_MCS_SET]); + ret = phy_info_freqs(phy_info, mode, tb_band[NL80211_BAND_ATTR_FREQS]); + if (ret != NL_OK) + return ret; + ret = phy_info_rates(mode, tb_band[NL80211_BAND_ATTR_RATES]); + if (ret != NL_OK) + return ret; + + return NL_OK; +} + + +static int phy_info_handler(struct nl_msg *msg, void *arg) +{ + struct nlattr *tb_msg[NL80211_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct phy_info_arg *phy_info = arg; + struct nlattr *nl_band; + int rem_band; + + nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + + if (!tb_msg[NL80211_ATTR_WIPHY_BANDS]) + return NL_SKIP; + + nla_for_each_nested(nl_band, tb_msg[NL80211_ATTR_WIPHY_BANDS], rem_band) + { + int res = phy_info_band(phy_info, nl_band); + if (res != NL_OK) + return res; + } + + return NL_SKIP; +} + + +static struct hostapd_hw_modes * +wpa_driver_nl80211_postprocess_modes(struct hostapd_hw_modes *modes, + u16 *num_modes) +{ + u16 m; + struct hostapd_hw_modes *mode11g = NULL, *nmodes, *mode; + int i, mode11g_idx = -1; + + /* heuristic to set up modes */ + for (m = 0; m < *num_modes; m++) { + if (!modes[m].num_channels) + continue; + if (modes[m].channels[0].freq < 4000) { + modes[m].mode = HOSTAPD_MODE_IEEE80211B; + for (i = 0; i < modes[m].num_rates; i++) { + if (modes[m].rates[i] > 200) { + modes[m].mode = HOSTAPD_MODE_IEEE80211G; + break; + } + } + } else if (modes[m].channels[0].freq > 50000) + modes[m].mode = HOSTAPD_MODE_IEEE80211AD; + else + modes[m].mode = HOSTAPD_MODE_IEEE80211A; + } /* If only 802.11g mode is included, use it to construct matching * 802.11b mode data. */ @@ -4000,7 +6387,7 @@ if (mode11g_idx < 0) return modes; /* 2.4 GHz band not supported at all */ - nmodes = os_realloc(modes, (*num_modes + 1) * sizeof(*nmodes)); + nmodes = os_realloc_array(modes, *num_modes + 1, sizeof(*nmodes)); if (nmodes == NULL) return modes; /* Could not add 802.11b mode */ @@ -4084,9 +6471,42 @@ } -static void nl80211_reg_rule_ht40(struct nlattr *tb[], +static void nl80211_reg_rule_max_eirp(u32 start, u32 end, u32 max_eirp, + struct phy_info_arg *results) +{ + u16 m; + + for (m = 0; m < *results->num_modes; m++) { + int c; + struct hostapd_hw_modes *mode = &results->modes[m]; + + for (c = 0; c < mode->num_channels; c++) { + struct hostapd_channel_data *chan = &mode->channels[c]; + if ((u32) chan->freq - 10 >= start && + (u32) chan->freq + 10 <= end) + chan->max_tx_power = max_eirp; + } + } +} + + +static void nl80211_reg_rule_ht40(u32 start, u32 end, struct phy_info_arg *results) { + u16 m; + + for (m = 0; m < *results->num_modes; m++) { + if (!(results->modes[m].ht_capab & + HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)) + continue; + nl80211_set_ht40_mode(&results->modes[m], start, end); + } +} + + +static void nl80211_reg_rule_sec(struct nlattr *tb[], + struct phy_info_arg *results) +{ u32 start, end, max_bw; u16 m; @@ -4099,21 +6519,41 @@ end = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_END]) / 1000; max_bw = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_MAX_BW]) / 1000; - wpa_printf(MSG_DEBUG, "nl80211: %u-%u @ %u MHz", - start, end, max_bw); - if (max_bw < 40) + if (max_bw < 20) return; for (m = 0; m < *results->num_modes; m++) { if (!(results->modes[m].ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)) continue; - nl80211_set_ht40_mode(&results->modes[m], start, end); + nl80211_set_ht40_mode_sec(&results->modes[m], start, end); } } -static void nl80211_reg_rule_sec(struct nlattr *tb[], +static void nl80211_set_vht_mode(struct hostapd_hw_modes *mode, int start, + int end) +{ + int c; + + for (c = 0; c < mode->num_channels; c++) { + struct hostapd_channel_data *chan = &mode->channels[c]; + if (chan->freq - 10 >= start && chan->freq + 70 <= end) + chan->flag |= HOSTAPD_CHAN_VHT_10_70; + + if (chan->freq - 30 >= start && chan->freq + 50 <= end) + chan->flag |= HOSTAPD_CHAN_VHT_30_50; + + if (chan->freq - 50 >= start && chan->freq + 30 <= end) + chan->flag |= HOSTAPD_CHAN_VHT_50_30; + + if (chan->freq - 70 >= start && chan->freq + 10 <= end) + chan->flag |= HOSTAPD_CHAN_VHT_70_10; + } +} + + +static void nl80211_reg_rule_vht(struct nlattr *tb[], struct phy_info_arg *results) { u32 start, end, max_bw; @@ -4128,14 +6568,35 @@ end = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_END]) / 1000; max_bw = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_MAX_BW]) / 1000; - if (max_bw < 20) + if (max_bw < 80) return; for (m = 0; m < *results->num_modes; m++) { if (!(results->modes[m].ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)) continue; - nl80211_set_ht40_mode_sec(&results->modes[m], start, end); + /* TODO: use a real VHT support indication */ + if (!results->modes[m].vht_capab) + continue; + + nl80211_set_vht_mode(&results->modes[m], start, end); + } +} + + +static const char * dfs_domain_name(enum nl80211_dfs_regions region) +{ + switch (region) { + case NL80211_DFS_UNSET: + return "DFS-UNSET"; + case NL80211_DFS_FCC: + return "DFS-FCC"; + case NL80211_DFS_ETSI: + return "DFS-ETSI"; + case NL80211_DFS_JP: + return "DFS-JP"; + default: + return "DFS-invalid"; } } @@ -4166,14 +6627,39 @@ return NL_SKIP; } - wpa_printf(MSG_DEBUG, "nl80211: Regulatory information - country=%s", - (char *) nla_data(tb_msg[NL80211_ATTR_REG_ALPHA2])); + if (tb_msg[NL80211_ATTR_DFS_REGION]) { + enum nl80211_dfs_regions dfs_domain; + dfs_domain = nla_get_u8(tb_msg[NL80211_ATTR_DFS_REGION]); + wpa_printf(MSG_DEBUG, "nl80211: Regulatory information - country=%s (%s)", + (char *) nla_data(tb_msg[NL80211_ATTR_REG_ALPHA2]), + dfs_domain_name(dfs_domain)); + } else { + wpa_printf(MSG_DEBUG, "nl80211: Regulatory information - country=%s", + (char *) nla_data(tb_msg[NL80211_ATTR_REG_ALPHA2])); + } nla_for_each_nested(nl_rule, tb_msg[NL80211_ATTR_REG_RULES], rem_rule) { + u32 start, end, max_eirp = 0, max_bw = 0; nla_parse(tb_rule, NL80211_FREQUENCY_ATTR_MAX, nla_data(nl_rule), nla_len(nl_rule), reg_policy); - nl80211_reg_rule_ht40(tb_rule, results); + if (tb_rule[NL80211_ATTR_FREQ_RANGE_START] == NULL || + tb_rule[NL80211_ATTR_FREQ_RANGE_END] == NULL) + continue; + start = nla_get_u32(tb_rule[NL80211_ATTR_FREQ_RANGE_START]) / 1000; + end = nla_get_u32(tb_rule[NL80211_ATTR_FREQ_RANGE_END]) / 1000; + if (tb_rule[NL80211_ATTR_POWER_RULE_MAX_EIRP]) + max_eirp = nla_get_u32(tb_rule[NL80211_ATTR_POWER_RULE_MAX_EIRP]) / 100; + if (tb_rule[NL80211_ATTR_FREQ_RANGE_MAX_BW]) + max_bw = nla_get_u32(tb_rule[NL80211_ATTR_FREQ_RANGE_MAX_BW]) / 1000; + + wpa_printf(MSG_DEBUG, "nl80211: %u-%u @ %u MHz %u mBm", + start, end, max_bw, max_eirp); + if (max_bw >= 40) + nl80211_reg_rule_ht40(start, end, results); + if (tb_rule[NL80211_ATTR_POWER_RULE_MAX_EIRP]) + nl80211_reg_rule_max_eirp(start, end, max_eirp, + results); } nla_for_each_nested(nl_rule, tb_msg[NL80211_ATTR_REG_RULES], rem_rule) @@ -4183,12 +6669,19 @@ nl80211_reg_rule_sec(tb_rule, results); } + nla_for_each_nested(nl_rule, tb_msg[NL80211_ATTR_REG_RULES], rem_rule) + { + nla_parse(tb_rule, NL80211_FREQUENCY_ATTR_MAX, + nla_data(nl_rule), nla_len(nl_rule), reg_policy); + nl80211_reg_rule_vht(tb_rule, results); + } + return NL_SKIP; } -static int nl80211_set_ht40_flags(struct wpa_driver_nl80211_data *drv, - struct phy_info_arg *results) +static int nl80211_set_regulatory_flags(struct wpa_driver_nl80211_data *drv, + struct phy_info_arg *results) { struct nl_msg *msg; @@ -4204,12 +6697,14 @@ static struct hostapd_hw_modes * wpa_driver_nl80211_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags) { + u32 feat; struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; struct phy_info_arg result = { .num_modes = num_modes, .modes = NULL, + .last_mode = -1, }; *num_modes = 0; @@ -4219,13 +6714,19 @@ if (!msg) return NULL; - nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_WIPHY); + feat = get_nl80211_protocol_features(drv); + if (feat & NL80211_PROTOCOL_FEATURE_SPLIT_WIPHY_DUMP) + nl80211_cmd(drv, msg, NLM_F_DUMP, NL80211_CMD_GET_WIPHY); + else + nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_WIPHY); + NLA_PUT_FLAG(msg, NL80211_ATTR_SPLIT_WIPHY_DUMP); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); if (send_and_recv_msgs(drv, msg, phy_info_handler, &result) == 0) { - nl80211_set_ht40_flags(drv, &result); - return wpa_driver_nl80211_add_11b(result.modes, num_modes); + nl80211_set_regulatory_flags(drv, &result); + return wpa_driver_nl80211_postprocess_modes(result.modes, + num_modes); } msg = NULL; nla_put_failure: @@ -4234,9 +6735,9 @@ } -static int wpa_driver_nl80211_send_frame(struct wpa_driver_nl80211_data *drv, - const void *data, size_t len, - int encrypt) +static int wpa_driver_nl80211_send_mntr(struct wpa_driver_nl80211_data *drv, + const void *data, size_t len, + int encrypt, int noack) { __u8 rtap_hdr[] = { 0x00, 0x00, /* radiotap version */ @@ -4267,6 +6768,7 @@ .msg_flags = 0, }; int res; + u16 txflags = 0; if (encrypt) rtap_hdr[8] |= IEEE80211_RADIOTAP_F_WEP; @@ -4277,6 +6779,10 @@ return -1; } + if (noack) + txflags |= IEEE80211_RADIOTAP_F_TX_NOACK; + WPA_PUT_LE16(&rtap_hdr[12], txflags); + res = sendmsg(drv->monitor_sock, &msg, 0); if (res < 0) { wpa_printf(MSG_INFO, "nl80211: sendmsg: %s", strerror(errno)); @@ -4286,10 +6792,59 @@ } -static int wpa_driver_nl80211_send_mlme(void *priv, const u8 *data, - size_t data_len) +static int wpa_driver_nl80211_send_frame(struct i802_bss *bss, + const void *data, size_t len, + int encrypt, int noack, + unsigned int freq, int no_cck, + int offchanok, unsigned int wait_time) +{ + struct wpa_driver_nl80211_data *drv = bss->drv; + u64 cookie; + int res; + + if (freq == 0) { + wpa_printf(MSG_DEBUG, "nl80211: send_frame - Use bss->freq=%u", + bss->freq); + freq = bss->freq; + } + + if (drv->use_monitor) { + wpa_printf(MSG_DEBUG, "nl80211: send_frame(freq=%u bss->freq=%u) -> send_mntr", + freq, bss->freq); + return wpa_driver_nl80211_send_mntr(drv, data, len, + encrypt, noack); + } + + wpa_printf(MSG_DEBUG, "nl80211: send_frame -> send_frame_cmd"); + res = nl80211_send_frame_cmd(bss, freq, wait_time, data, len, + &cookie, no_cck, noack, offchanok); + if (res == 0 && !noack) { + const struct ieee80211_mgmt *mgmt; + u16 fc; + + mgmt = (const struct ieee80211_mgmt *) data; + fc = le_to_host16(mgmt->frame_control); + if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT && + WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_ACTION) { + wpa_printf(MSG_MSGDUMP, + "nl80211: Update send_action_cookie from 0x%llx to 0x%llx", + (long long unsigned int) + drv->send_action_cookie, + (long long unsigned int) cookie); + drv->send_action_cookie = cookie; + } + } + + return res; +} + + +static int wpa_driver_nl80211_send_mlme(struct i802_bss *bss, const u8 *data, + size_t data_len, int noack, + unsigned int freq, int no_cck, + int offchanok, + unsigned int wait_time) { - struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; struct ieee80211_mgmt *mgmt; int encrypt = 1; @@ -4297,8 +6852,11 @@ mgmt = (struct ieee80211_mgmt *) data; fc = le_to_host16(mgmt->frame_control); + wpa_printf(MSG_DEBUG, "nl80211: send_mlme - noack=%d freq=%u no_cck=%d offchanok=%d wait_time=%u fc=0x%x nlmode=%d", + noack, freq, no_cck, offchanok, wait_time, fc, drv->nlmode); - if (is_sta_interface(drv->nlmode) && + if ((is_sta_interface(drv->nlmode) || + drv->nlmode == NL80211_IFTYPE_P2P_DEVICE) && WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT && WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_PROBE_RESP) { /* @@ -4306,14 +6864,28 @@ * but it works due to the single-threaded nature * of wpa_supplicant. */ - return nl80211_send_frame_cmd(drv, drv->last_mgmt_freq, 0, - data, data_len, NULL, 1); - } - - if (drv->no_monitor_iface_capab && is_ap_interface(drv->nlmode)) { - return nl80211_send_frame_cmd(drv, drv->ap_oper_freq, 0, + if (freq == 0) { + wpa_printf(MSG_DEBUG, "nl80211: Use last_mgmt_freq=%d", + drv->last_mgmt_freq); + freq = drv->last_mgmt_freq; + } + return nl80211_send_frame_cmd(bss, freq, 0, + data, data_len, NULL, 1, noack, + 1); + } + + if (drv->device_ap_sme && is_ap_interface(drv->nlmode)) { + if (freq == 0) { + wpa_printf(MSG_DEBUG, "nl80211: Use bss->freq=%d", + bss->freq); + freq = bss->freq; + } + return nl80211_send_frame_cmd(bss, freq, + (int) freq == bss->freq ? 0 : + wait_time, data, data_len, - &drv->send_action_cookie, 0); + &drv->send_action_cookie, + no_cck, noack, offchanok); } if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT && @@ -4330,11 +6902,16 @@ encrypt = 0; } - return wpa_driver_nl80211_send_frame(drv, data, data_len, encrypt); + wpa_printf(MSG_DEBUG, "nl80211: send_mlme -> send_frame"); + return wpa_driver_nl80211_send_frame(bss, data, data_len, encrypt, + noack, freq, no_cck, offchanok, + wait_time); } -static int nl80211_set_ap_isolate(struct i802_bss *bss, int enabled) +static int nl80211_set_bss(struct i802_bss *bss, int cts, int preamble, + int slot, int ht_opmode, int ap_isolate, + int *basic_rates) { struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; @@ -4345,41 +6922,89 @@ nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_BSS); + if (cts >= 0) + NLA_PUT_U8(msg, NL80211_ATTR_BSS_CTS_PROT, cts); + if (preamble >= 0) + NLA_PUT_U8(msg, NL80211_ATTR_BSS_SHORT_PREAMBLE, preamble); + if (slot >= 0) + NLA_PUT_U8(msg, NL80211_ATTR_BSS_SHORT_SLOT_TIME, slot); + if (ht_opmode >= 0) + NLA_PUT_U16(msg, NL80211_ATTR_BSS_HT_OPMODE, ht_opmode); + if (ap_isolate >= 0) + NLA_PUT_U8(msg, NL80211_ATTR_AP_ISOLATE, ap_isolate); + + if (basic_rates) { + u8 rates[NL80211_MAX_SUPP_RATES]; + u8 rates_len = 0; + int i; + + for (i = 0; i < NL80211_MAX_SUPP_RATES && basic_rates[i] >= 0; + i++) + rates[rates_len++] = basic_rates[i] / 5; + + NLA_PUT(msg, NL80211_ATTR_BSS_BASIC_RATES, rates_len, rates); + } + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(bss->ifname)); - NLA_PUT_U8(msg, NL80211_ATTR_AP_ISOLATE, enabled); return send_and_recv_msgs(drv, msg, NULL, NULL); nla_put_failure: + nlmsg_free(msg); return -ENOBUFS; } -static int nl80211_set_bss(struct i802_bss *bss, int cts, int preamble, - int slot, int ht_opmode) +static int wpa_driver_nl80211_set_acl(void *priv, + struct hostapd_acl_params *params) { + struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; + struct nlattr *acl; + unsigned int i; + int ret = 0; + + if (!(drv->capa.max_acl_mac_addrs)) + return -ENOTSUP; + + if (params->num_mac_acl > drv->capa.max_acl_mac_addrs) + return -ENOTSUP; msg = nlmsg_alloc(); if (!msg) return -ENOMEM; - nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_BSS); + wpa_printf(MSG_DEBUG, "nl80211: Set %s ACL (num_mac_acl=%u)", + params->acl_policy ? "Accept" : "Deny", params->num_mac_acl); - if (cts >= 0) - NLA_PUT_U8(msg, NL80211_ATTR_BSS_CTS_PROT, cts); - if (preamble >= 0) - NLA_PUT_U8(msg, NL80211_ATTR_BSS_SHORT_PREAMBLE, preamble); - if (slot >= 0) - NLA_PUT_U8(msg, NL80211_ATTR_BSS_SHORT_SLOT_TIME, slot); - if (ht_opmode >= 0) - NLA_PUT_U16(msg, NL80211_ATTR_BSS_HT_OPMODE, ht_opmode); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(bss->ifname)); + nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_MAC_ACL); - return send_and_recv_msgs(drv, msg, NULL, NULL); - nla_put_failure: + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + + NLA_PUT_U32(msg, NL80211_ATTR_ACL_POLICY, params->acl_policy ? + NL80211_ACL_POLICY_DENY_UNLESS_LISTED : + NL80211_ACL_POLICY_ACCEPT_UNLESS_LISTED); + + acl = nla_nest_start(msg, NL80211_ATTR_MAC_ADDRS); + if (acl == NULL) + goto nla_put_failure; + + for (i = 0; i < params->num_mac_acl; i++) + NLA_PUT(msg, i + 1, ETH_ALEN, params->mac_acl[i].addr); + + nla_nest_end(msg, acl); + + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + msg = NULL; + if (ret) { + wpa_printf(MSG_DEBUG, "nl80211: Failed to set MAC ACL: %d (%s)", + ret, strerror(-ret)); + } + +nla_put_failure: nlmsg_free(msg); - return -ENOBUFS; + + return ret; } @@ -4394,7 +7019,7 @@ int beacon_set; int ifindex = if_nametoindex(bss->ifname); int num_suites; - u32 suites[10]; + u32 suites[10], suite; u32 ver; beacon_set = bss->beacon_set; @@ -4409,29 +7034,49 @@ cmd = NL80211_CMD_SET_BEACON; nl80211_cmd(drv, msg, 0, cmd); + wpa_hexdump(MSG_DEBUG, "nl80211: Beacon head", + params->head, params->head_len); NLA_PUT(msg, NL80211_ATTR_BEACON_HEAD, params->head_len, params->head); + wpa_hexdump(MSG_DEBUG, "nl80211: Beacon tail", + params->tail, params->tail_len); NLA_PUT(msg, NL80211_ATTR_BEACON_TAIL, params->tail_len, params->tail); + wpa_printf(MSG_DEBUG, "nl80211: ifindex=%d", ifindex); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifindex); + wpa_printf(MSG_DEBUG, "nl80211: beacon_int=%d", params->beacon_int); NLA_PUT_U32(msg, NL80211_ATTR_BEACON_INTERVAL, params->beacon_int); + wpa_printf(MSG_DEBUG, "nl80211: dtim_period=%d", params->dtim_period); NLA_PUT_U32(msg, NL80211_ATTR_DTIM_PERIOD, params->dtim_period); + wpa_hexdump_ascii(MSG_DEBUG, "nl80211: ssid", + params->ssid, params->ssid_len); NLA_PUT(msg, NL80211_ATTR_SSID, params->ssid_len, params->ssid); + if (params->proberesp && params->proberesp_len) { + wpa_hexdump(MSG_DEBUG, "nl80211: proberesp (offload)", + params->proberesp, params->proberesp_len); + NLA_PUT(msg, NL80211_ATTR_PROBE_RESP, params->proberesp_len, + params->proberesp); + } switch (params->hide_ssid) { case NO_SSID_HIDING: + wpa_printf(MSG_DEBUG, "nl80211: hidden SSID not in use"); NLA_PUT_U32(msg, NL80211_ATTR_HIDDEN_SSID, NL80211_HIDDEN_SSID_NOT_IN_USE); break; case HIDDEN_SSID_ZERO_LEN: + wpa_printf(MSG_DEBUG, "nl80211: hidden SSID zero len"); NLA_PUT_U32(msg, NL80211_ATTR_HIDDEN_SSID, NL80211_HIDDEN_SSID_ZERO_LEN); break; case HIDDEN_SSID_ZERO_CONTENTS: + wpa_printf(MSG_DEBUG, "nl80211: hidden SSID zero contents"); NLA_PUT_U32(msg, NL80211_ATTR_HIDDEN_SSID, NL80211_HIDDEN_SSID_ZERO_CONTENTS); break; } + wpa_printf(MSG_DEBUG, "nl80211: privacy=%d", params->privacy); if (params->privacy) NLA_PUT_FLAG(msg, NL80211_ATTR_PRIVACY); + wpa_printf(MSG_DEBUG, "nl80211: auth_algs=0x%x", params->auth_algs); if ((params->auth_algs & (WPA_AUTH_ALG_OPEN | WPA_AUTH_ALG_SHARED)) == (WPA_AUTH_ALG_OPEN | WPA_AUTH_ALG_SHARED)) { /* Leave out the attribute */ @@ -4442,6 +7087,7 @@ NLA_PUT_U32(msg, NL80211_ATTR_AUTH_TYPE, NL80211_AUTHTYPE_OPEN_SYSTEM); + wpa_printf(MSG_DEBUG, "nl80211: wpa_version=0x%x", params->wpa_version); ver = 0; if (params->wpa_version & WPA_PROTO_WPA) ver |= NL80211_WPA_VERSION_1; @@ -4450,6 +7096,8 @@ if (ver) NLA_PUT_U32(msg, NL80211_ATTR_WPA_VERSIONS, ver); + wpa_printf(MSG_DEBUG, "nl80211: key_mgmt_suites=0x%x", + params->key_mgmt_suites); num_suites = 0; if (params->key_mgmt_suites & WPA_KEY_MGMT_IEEE8021X) suites[num_suites++] = WLAN_AKM_SUITE_8021X; @@ -4460,70 +7108,62 @@ num_suites * sizeof(u32), suites); } - num_suites = 0; - if (params->pairwise_ciphers & WPA_CIPHER_CCMP) - suites[num_suites++] = WLAN_CIPHER_SUITE_CCMP; - if (params->pairwise_ciphers & WPA_CIPHER_TKIP) - suites[num_suites++] = WLAN_CIPHER_SUITE_TKIP; - if (params->pairwise_ciphers & WPA_CIPHER_WEP104) - suites[num_suites++] = WLAN_CIPHER_SUITE_WEP104; - if (params->pairwise_ciphers & WPA_CIPHER_WEP40) - suites[num_suites++] = WLAN_CIPHER_SUITE_WEP40; + if (params->key_mgmt_suites & WPA_KEY_MGMT_IEEE8021X && + params->pairwise_ciphers & (WPA_CIPHER_WEP104 | WPA_CIPHER_WEP40)) + NLA_PUT_FLAG(msg, NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT); + + wpa_printf(MSG_DEBUG, "nl80211: pairwise_ciphers=0x%x", + params->pairwise_ciphers); + num_suites = wpa_cipher_to_cipher_suites(params->pairwise_ciphers, + suites, ARRAY_SIZE(suites)); if (num_suites) { NLA_PUT(msg, NL80211_ATTR_CIPHER_SUITES_PAIRWISE, num_suites * sizeof(u32), suites); } - switch (params->group_cipher) { - case WPA_CIPHER_CCMP: - NLA_PUT_U32(msg, NL80211_ATTR_CIPHER_SUITE_GROUP, - WLAN_CIPHER_SUITE_CCMP); - break; - case WPA_CIPHER_TKIP: - NLA_PUT_U32(msg, NL80211_ATTR_CIPHER_SUITE_GROUP, - WLAN_CIPHER_SUITE_TKIP); - break; - case WPA_CIPHER_WEP104: - NLA_PUT_U32(msg, NL80211_ATTR_CIPHER_SUITE_GROUP, - WLAN_CIPHER_SUITE_WEP104); - break; - case WPA_CIPHER_WEP40: - NLA_PUT_U32(msg, NL80211_ATTR_CIPHER_SUITE_GROUP, - WLAN_CIPHER_SUITE_WEP40); - break; - } + wpa_printf(MSG_DEBUG, "nl80211: group_cipher=0x%x", + params->group_cipher); + suite = wpa_cipher_to_cipher_suite(params->group_cipher); + if (suite) + NLA_PUT_U32(msg, NL80211_ATTR_CIPHER_SUITE_GROUP, suite); if (params->beacon_ies) { + wpa_hexdump_buf(MSG_DEBUG, "nl80211: beacon_ies", + params->beacon_ies); NLA_PUT(msg, NL80211_ATTR_IE, wpabuf_len(params->beacon_ies), wpabuf_head(params->beacon_ies)); } if (params->proberesp_ies) { + wpa_hexdump_buf(MSG_DEBUG, "nl80211: proberesp_ies", + params->proberesp_ies); NLA_PUT(msg, NL80211_ATTR_IE_PROBE_RESP, wpabuf_len(params->proberesp_ies), wpabuf_head(params->proberesp_ies)); } if (params->assocresp_ies) { + wpa_hexdump_buf(MSG_DEBUG, "nl80211: assocresp_ies", + params->assocresp_ies); NLA_PUT(msg, NL80211_ATTR_IE_ASSOC_RESP, wpabuf_len(params->assocresp_ies), wpabuf_head(params->assocresp_ies)); } + if (drv->capa.flags & WPA_DRIVER_FLAGS_INACTIVITY_TIMER) { + wpa_printf(MSG_DEBUG, "nl80211: ap_max_inactivity=%d", + params->ap_max_inactivity); + NLA_PUT_U16(msg, NL80211_ATTR_INACTIVITY_TIMEOUT, + params->ap_max_inactivity); + } + ret = send_and_recv_msgs(drv, msg, NULL, NULL); if (ret) { wpa_printf(MSG_DEBUG, "nl80211: Beacon set failed: %d (%s)", ret, strerror(-ret)); } else { bss->beacon_set = 1; - ret = nl80211_set_ap_isolate(bss, params->isolate); - if (!params->isolate && ret) { - wpa_printf(MSG_DEBUG, "nl80211: Ignore AP isolation " - "configuration error since isolation is " - "not used"); - ret = 0; - } - nl80211_set_bss(bss, params->cts_protect, params->preamble, - params->short_slot_time, params->ht_opmode); + params->short_slot_time, params->ht_opmode, + params->isolate, params->basic_rates); } return ret; nla_put_failure: @@ -4532,26 +7172,41 @@ } -static int wpa_driver_nl80211_set_freq(struct wpa_driver_nl80211_data *drv, - int freq, int ht_enabled, - int sec_channel_offset) +static int nl80211_put_freq_params(struct nl_msg *msg, + struct hostapd_freq_params *freq) { - struct nl_msg *msg; - int ret; - - wpa_printf(MSG_DEBUG, "nl80211: Set freq %d (ht_enabled=%d " - "sec_channel_offset=%d)", - freq, ht_enabled, sec_channel_offset); - msg = nlmsg_alloc(); - if (!msg) - return -1; - - nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_WIPHY); - - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); - NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq); - if (ht_enabled) { - switch (sec_channel_offset) { + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq->freq); + if (freq->vht_enabled) { + switch (freq->bandwidth) { + case 20: + NLA_PUT_U32(msg, NL80211_ATTR_CHANNEL_WIDTH, + NL80211_CHAN_WIDTH_20); + break; + case 40: + NLA_PUT_U32(msg, NL80211_ATTR_CHANNEL_WIDTH, + NL80211_CHAN_WIDTH_40); + break; + case 80: + if (freq->center_freq2) + NLA_PUT_U32(msg, NL80211_ATTR_CHANNEL_WIDTH, + NL80211_CHAN_WIDTH_80P80); + else + NLA_PUT_U32(msg, NL80211_ATTR_CHANNEL_WIDTH, + NL80211_CHAN_WIDTH_80); + break; + case 160: + NLA_PUT_U32(msg, NL80211_ATTR_CHANNEL_WIDTH, + NL80211_CHAN_WIDTH_160); + break; + default: + return -EINVAL; + } + NLA_PUT_U32(msg, NL80211_ATTR_CENTER_FREQ1, freq->center_freq1); + if (freq->center_freq2) + NLA_PUT_U32(msg, NL80211_ATTR_CENTER_FREQ2, + freq->center_freq2); + } else if (freq->ht_enabled) { + switch (freq->sec_channel_offset) { case -1: NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, NL80211_CHAN_HT40MINUS); @@ -4566,13 +7221,42 @@ break; } } + return 0; + +nla_put_failure: + return -ENOBUFS; +} + + +static int wpa_driver_nl80211_set_freq(struct i802_bss *bss, + struct hostapd_freq_params *freq) +{ + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + int ret; + + wpa_printf(MSG_DEBUG, + "nl80211: Set freq %d (ht_enabled=%d, vht_enabled=%d, bandwidth=%d MHz, cf1=%d MHz, cf2=%d MHz)", + freq->freq, freq->ht_enabled, freq->vht_enabled, + freq->bandwidth, freq->center_freq1, freq->center_freq2); + msg = nlmsg_alloc(); + if (!msg) + return -1; + + nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_WIPHY); + + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + if (nl80211_put_freq_params(msg, freq) < 0) + goto nla_put_failure; ret = send_and_recv_msgs(drv, msg, NULL, NULL); msg = NULL; - if (ret == 0) + if (ret == 0) { + bss->freq = freq->freq; return 0; + } wpa_printf(MSG_DEBUG, "nl80211: Failed to set channel (freq=%d): " - "%d (%s)", freq, ret, strerror(-ret)); + "%d (%s)", freq->freq, ret, strerror(-ret)); nla_put_failure: nlmsg_free(msg); return -1; @@ -4615,6 +7299,8 @@ if (!msg) return -ENOMEM; + wpa_printf(MSG_DEBUG, "nl80211: %s STA " MACSTR, + params->set ? "Set" : "Add", MAC2STR(params->addr)); nl80211_cmd(drv, msg, 0, params->set ? NL80211_CMD_SET_STATION : NL80211_CMD_NEW_STATION); @@ -4622,23 +7308,96 @@ NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, params->addr); NLA_PUT(msg, NL80211_ATTR_STA_SUPPORTED_RATES, params->supp_rates_len, params->supp_rates); + wpa_hexdump(MSG_DEBUG, " * supported rates", params->supp_rates, + params->supp_rates_len); if (!params->set) { - NLA_PUT_U16(msg, NL80211_ATTR_STA_AID, params->aid); + if (params->aid) { + wpa_printf(MSG_DEBUG, " * aid=%u", params->aid); + NLA_PUT_U16(msg, NL80211_ATTR_STA_AID, params->aid); + } else { + /* + * cfg80211 validates that AID is non-zero, so we have + * to make this a non-zero value for the TDLS case where + * a dummy STA entry is used for now. + */ + wpa_printf(MSG_DEBUG, " * aid=1 (TDLS workaround)"); + NLA_PUT_U16(msg, NL80211_ATTR_STA_AID, 1); + } + wpa_printf(MSG_DEBUG, " * listen_interval=%u", + params->listen_interval); NLA_PUT_U16(msg, NL80211_ATTR_STA_LISTEN_INTERVAL, params->listen_interval); + } else if (params->aid && (params->flags & WPA_STA_TDLS_PEER)) { + wpa_printf(MSG_DEBUG, " * peer_aid=%u", params->aid); + NLA_PUT_U16(msg, NL80211_ATTR_PEER_AID, params->aid); } if (params->ht_capabilities) { + wpa_hexdump(MSG_DEBUG, " * ht_capabilities", + (u8 *) params->ht_capabilities, + sizeof(*params->ht_capabilities)); NLA_PUT(msg, NL80211_ATTR_HT_CAPABILITY, sizeof(*params->ht_capabilities), params->ht_capabilities); } + if (params->vht_capabilities) { + wpa_hexdump(MSG_DEBUG, " * vht_capabilities", + (u8 *) params->vht_capabilities, + sizeof(*params->vht_capabilities)); + NLA_PUT(msg, NL80211_ATTR_VHT_CAPABILITY, + sizeof(*params->vht_capabilities), + params->vht_capabilities); + } + + wpa_printf(MSG_DEBUG, " * capability=0x%x", params->capability); + NLA_PUT_U16(msg, NL80211_ATTR_STA_CAPABILITY, params->capability); + + if (params->ext_capab) { + wpa_hexdump(MSG_DEBUG, " * ext_capab", + params->ext_capab, params->ext_capab_len); + NLA_PUT(msg, NL80211_ATTR_STA_EXT_CAPABILITY, + params->ext_capab_len, params->ext_capab); + } + + if (params->supp_channels) { + wpa_hexdump(MSG_DEBUG, " * supported channels", + params->supp_channels, params->supp_channels_len); + NLA_PUT(msg, NL80211_ATTR_STA_SUPPORTED_CHANNELS, + params->supp_channels_len, params->supp_channels); + } + + if (params->supp_oper_classes) { + wpa_hexdump(MSG_DEBUG, " * supported operating classes", + params->supp_oper_classes, + params->supp_oper_classes_len); + NLA_PUT(msg, NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES, + params->supp_oper_classes_len, + params->supp_oper_classes); + } + os_memset(&upd, 0, sizeof(upd)); upd.mask = sta_flags_nl80211(params->flags); upd.set = upd.mask; + wpa_printf(MSG_DEBUG, " * flags set=0x%x mask=0x%x", + upd.set, upd.mask); NLA_PUT(msg, NL80211_ATTR_STA_FLAGS2, sizeof(upd), &upd); - ret = send_and_recv_msgs(drv, msg, NULL, NULL); + if (params->flags & WPA_STA_WMM) { + struct nlattr *wme = nla_nest_start(msg, NL80211_ATTR_STA_WME); + + if (!wme) + goto nla_put_failure; + + wpa_printf(MSG_DEBUG, " * qosinfo=0x%x", params->qosinfo); + NLA_PUT_U8(msg, NL80211_STA_WME_UAPSD_QUEUES, + params->qosinfo & WMM_QOSINFO_STA_AC_MASK); + NLA_PUT_U8(msg, NL80211_STA_WME_MAX_SP, + (params->qosinfo >> WMM_QOSINFO_STA_SP_SHIFT) & + WMM_QOSINFO_STA_SP_MASK); + nla_nest_end(msg, wme); + } + + ret = send_and_recv_msgs(drv, msg, NULL, NULL); msg = NULL; if (ret) wpa_printf(MSG_DEBUG, "nl80211: NL80211_CMD_%s_STATION " @@ -4652,9 +7411,8 @@ } -static int wpa_driver_nl80211_sta_remove(void *priv, const u8 *addr) +static int wpa_driver_nl80211_sta_remove(struct i802_bss *bss, const u8 *addr) { - struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; int ret; @@ -4670,6 +7428,9 @@ NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr); ret = send_and_recv_msgs(drv, msg, NULL, NULL); + wpa_printf(MSG_DEBUG, "nl80211: sta_remove -> DEL_STATION %s " MACSTR + " --> %d (%s)", + bss->ifname, MAC2STR(addr), ret, strerror(-ret)); if (ret == -ENOENT) return 0; return ret; @@ -4714,12 +7475,20 @@ return "STATION"; case NL80211_IFTYPE_AP: return "AP"; + case NL80211_IFTYPE_AP_VLAN: + return "AP_VLAN"; + case NL80211_IFTYPE_WDS: + return "WDS"; case NL80211_IFTYPE_MONITOR: return "MONITOR"; + case NL80211_IFTYPE_MESH_POINT: + return "MESH_POINT"; case NL80211_IFTYPE_P2P_CLIENT: return "P2P_CLIENT"; case NL80211_IFTYPE_P2P_GO: return "P2P_GO"; + case NL80211_IFTYPE_P2P_DEVICE: + return "P2P_DEVICE"; default: return "unknown"; } @@ -4729,9 +7498,11 @@ static int nl80211_create_iface_once(struct wpa_driver_nl80211_data *drv, const char *ifname, enum nl80211_iftype iftype, - const u8 *addr, int wds) + const u8 *addr, int wds, + int (*handler)(struct nl_msg *, void *), + void *arg) { - struct nl_msg *msg, *flags = NULL; + struct nl_msg *msg; int ifidx; int ret = -ENOBUFS; @@ -4743,30 +7514,26 @@ return -1; nl80211_cmd(drv, msg, 0, NL80211_CMD_NEW_INTERFACE); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + if (nl80211_set_iface_id(msg, drv->first_bss) < 0) + goto nla_put_failure; NLA_PUT_STRING(msg, NL80211_ATTR_IFNAME, ifname); NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, iftype); if (iftype == NL80211_IFTYPE_MONITOR) { - int err; + struct nlattr *flags; - flags = nlmsg_alloc(); + flags = nla_nest_start(msg, NL80211_ATTR_MNTR_FLAGS); if (!flags) goto nla_put_failure; - NLA_PUT_FLAG(flags, NL80211_MNTR_FLAG_COOK_FRAMES); - - err = nla_put_nested(msg, NL80211_ATTR_MNTR_FLAGS, flags); - - nlmsg_free(flags); + NLA_PUT_FLAG(msg, NL80211_MNTR_FLAG_COOK_FRAMES); - if (err) - goto nla_put_failure; + nla_nest_end(msg, flags); } else if (wds) { NLA_PUT_U8(msg, NL80211_ATTR_4ADDR, wds); } - ret = send_and_recv_msgs(drv, msg, NULL, NULL); + ret = send_and_recv_msgs(drv, msg, handler, arg); msg = NULL; if (ret) { nla_put_failure: @@ -4776,6 +7543,9 @@ return ret; } + if (iftype == NL80211_IFTYPE_P2P_DEVICE) + return 0; + ifidx = if_nametoindex(ifname); wpa_printf(MSG_DEBUG, "nl80211: New interface %s created: ifindex=%d", ifname, ifidx); @@ -4798,14 +7568,22 @@ static int nl80211_create_iface(struct wpa_driver_nl80211_data *drv, const char *ifname, enum nl80211_iftype iftype, - const u8 *addr, int wds) + const u8 *addr, int wds, + int (*handler)(struct nl_msg *, void *), + void *arg, int use_existing) { int ret; - ret = nl80211_create_iface_once(drv, ifname, iftype, addr, wds); + ret = nl80211_create_iface_once(drv, ifname, iftype, addr, wds, handler, + arg); /* if error occurred and interface exists already */ if (ret == -ENFILE && if_nametoindex(ifname)) { + if (use_existing) { + wpa_printf(MSG_DEBUG, "nl80211: Continue using existing interface %s", + ifname); + return -ENFILE; + } wpa_printf(MSG_INFO, "Try to remove and re-create %s", ifname); /* Try to remove the interface that was already there. */ @@ -4813,10 +7591,10 @@ /* Try to create the interface again */ ret = nl80211_create_iface_once(drv, ifname, iftype, addr, - wds); + wds, handler, arg); } - if (ret >= 0 && is_p2p_interface(iftype)) + if (ret >= 0 && is_p2p_net_interface(iftype)) nl80211_disable_11b_rates(drv, ret, 1); return ret; @@ -4907,12 +7685,13 @@ len = recv(sock, buf, sizeof(buf), 0); if (len < 0) { - perror("recv"); + wpa_printf(MSG_ERROR, "nl80211: Monitor socket recv failed: %s", + strerror(errno)); return; } if (ieee80211_radiotap_iterator_init(&iter, (void*)buf, len)) { - printf("received invalid radiotap frame\n"); + wpa_printf(MSG_INFO, "nl80211: received invalid radiotap frame"); return; } @@ -4921,7 +7700,8 @@ if (ret == -ENOENT) break; if (ret) { - printf("received invalid radiotap frame (%d)\n", ret); + wpa_printf(MSG_INFO, "nl80211: received invalid radiotap frame (%d)", + ret); return; } switch (iter.this_arg_index) { @@ -4945,8 +7725,8 @@ case IEEE80211_RADIOTAP_RATE: datarate = *iter.this_arg * 5; break; - case IEEE80211_RADIOTAP_DB_ANTSIGNAL: - ssi_signal = *iter.this_arg; + case IEEE80211_RADIOTAP_DBM_ANTSIGNAL: + ssi_signal = (s8) *iter.this_arg; break; } } @@ -5071,7 +7851,7 @@ }; static struct sock_fprog msock_filter = { - .len = sizeof(msock_filter_insns)/sizeof(msock_filter_insns[0]), + .len = ARRAY_SIZE(msock_filter_insns), .filter = msock_filter_insns, }; @@ -5106,7 +7886,8 @@ if (setsockopt(s, SOL_SOCKET, SO_ATTACH_FILTER, &msock_filter, sizeof(msock_filter))) { - perror("SO_ATTACH_FILTER"); + wpa_printf(MSG_ERROR, "nl80211: setsockopt(SO_ATTACH_FILTER) failed: %s", + strerror(errno)); return -1; } @@ -5117,6 +7898,13 @@ static void nl80211_remove_monitor_interface( struct wpa_driver_nl80211_data *drv) { + if (drv->monitor_refcount > 0) + drv->monitor_refcount--; + wpa_printf(MSG_DEBUG, "nl80211: Remove monitor interface: refcount=%d", + drv->monitor_refcount); + if (drv->monitor_refcount > 0) + return; + if (drv->monitor_ifidx >= 0) { nl80211_remove_iface(drv, drv->monitor_ifidx); drv->monitor_ifidx = -1; @@ -5137,29 +7925,42 @@ int optval; socklen_t optlen; - if (os_strncmp(drv->first_bss.ifname, "p2p-", 4) == 0) { + if (drv->monitor_ifidx >= 0) { + drv->monitor_refcount++; + wpa_printf(MSG_DEBUG, "nl80211: Re-use existing monitor interface: refcount=%d", + drv->monitor_refcount); + return 0; + } + + if (os_strncmp(drv->first_bss->ifname, "p2p-", 4) == 0) { /* * P2P interface name is of the format p2p-%s-%d. For monitor * interface name corresponding to P2P GO, replace "p2p-" with * "mon-" to retain the same interface name length and to * indicate that it is a monitor interface. */ - snprintf(buf, IFNAMSIZ, "mon-%s", drv->first_bss.ifname + 4); + snprintf(buf, IFNAMSIZ, "mon-%s", drv->first_bss->ifname + 4); } else { /* Non-P2P interface with AP functionality. */ - snprintf(buf, IFNAMSIZ, "mon.%s", drv->first_bss.ifname); + snprintf(buf, IFNAMSIZ, "mon.%s", drv->first_bss->ifname); } buf[IFNAMSIZ - 1] = '\0'; drv->monitor_ifidx = nl80211_create_iface(drv, buf, NL80211_IFTYPE_MONITOR, NULL, - 0); + 0, NULL, NULL, 0); if (drv->monitor_ifidx == -EOPNOTSUPP) { + /* + * This is backward compatibility for a few versions of + * the kernel only that didn't advertise the right + * attributes for the only driver that then supported + * AP mode w/o monitor -- ath6kl. + */ wpa_printf(MSG_DEBUG, "nl80211: Driver does not support " "monitor interface type - try to run without it"); - drv->no_monitor_iface_capab = 1; + drv->device_ap_sme = 1; } if (drv->monitor_ifidx < 0) @@ -5173,7 +7974,8 @@ ll.sll_ifindex = drv->monitor_ifidx; drv->monitor_sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); if (drv->monitor_sock < 0) { - perror("socket[PF_PACKET,SOCK_RAW]"); + wpa_printf(MSG_ERROR, "nl80211: socket[PF_PACKET,SOCK_RAW] failed: %s", + strerror(errno)); goto error; } @@ -5184,7 +7986,8 @@ } if (bind(drv->monitor_sock, (struct sockaddr *) &ll, sizeof(ll)) < 0) { - perror("monitor socket bind"); + wpa_printf(MSG_ERROR, "nl80211: monitor socket bind failed: %s", + strerror(errno)); goto error; } @@ -5192,16 +7995,18 @@ optval = 20; if (setsockopt (drv->monitor_sock, SOL_SOCKET, SO_PRIORITY, &optval, optlen)) { - perror("Failed to set socket priority"); + wpa_printf(MSG_ERROR, "nl80211: Failed to set socket priority: %s", + strerror(errno)); goto error; } if (eloop_register_read_sock(drv->monitor_sock, handle_monitor_read, drv, NULL)) { - printf("Could not register monitor read socket\n"); + wpa_printf(MSG_INFO, "nl80211: Could not register monitor read socket"); goto error; } + drv->monitor_refcount++; return 0; error: nl80211_remove_monitor_interface(drv); @@ -5209,22 +8014,90 @@ } -#ifdef CONFIG_AP +static int nl80211_setup_ap(struct i802_bss *bss) +{ + struct wpa_driver_nl80211_data *drv = bss->drv; + + wpa_printf(MSG_DEBUG, "nl80211: Setup AP(%s) - device_ap_sme=%d use_monitor=%d", + bss->ifname, drv->device_ap_sme, drv->use_monitor); + + /* + * Disable Probe Request reporting unless we need it in this way for + * devices that include the AP SME, in the other case (unless using + * monitor iface) we'll get it through the nl_mgmt socket instead. + */ + if (!drv->device_ap_sme) + wpa_driver_nl80211_probe_req_report(bss, 0); + + if (!drv->device_ap_sme && !drv->use_monitor) + if (nl80211_mgmt_subscribe_ap(bss)) + return -1; + + if (drv->device_ap_sme && !drv->use_monitor) + if (nl80211_mgmt_subscribe_ap_dev_sme(bss)) + return -1; + + if (!drv->device_ap_sme && drv->use_monitor && + nl80211_create_monitor_interface(drv) && + !drv->device_ap_sme) + return -1; + + if (drv->device_ap_sme && + wpa_driver_nl80211_probe_req_report(bss, 1) < 0) { + wpa_printf(MSG_DEBUG, "nl80211: Failed to enable " + "Probe Request frame reporting in AP mode"); + /* Try to survive without this */ + } + + return 0; +} + + +static void nl80211_teardown_ap(struct i802_bss *bss) +{ + struct wpa_driver_nl80211_data *drv = bss->drv; + + wpa_printf(MSG_DEBUG, "nl80211: Teardown AP(%s) - device_ap_sme=%d use_monitor=%d", + bss->ifname, drv->device_ap_sme, drv->use_monitor); + if (drv->device_ap_sme) { + wpa_driver_nl80211_probe_req_report(bss, 0); + if (!drv->use_monitor) + nl80211_mgmt_unsubscribe(bss, "AP teardown (dev SME)"); + } else if (drv->use_monitor) + nl80211_remove_monitor_interface(drv); + else + nl80211_mgmt_unsubscribe(bss, "AP teardown"); + + bss->beacon_set = 0; +} + + static int nl80211_send_eapol_data(struct i802_bss *bss, const u8 *addr, const u8 *data, - size_t data_len, const u8 *own_addr) + size_t data_len) { - if (bss->drv->l2 == NULL) { - wpa_printf(MSG_DEBUG, "nl80211: No l2_packet to send EAPOL"); + struct sockaddr_ll ll; + int ret; + + if (bss->drv->eapol_tx_sock < 0) { + wpa_printf(MSG_DEBUG, "nl80211: No socket to send EAPOL"); return -1; } - if (l2_packet_send(bss->drv->l2, addr, ETH_P_EAPOL, data, data_len) < - 0) - return -1; - return 0; + os_memset(&ll, 0, sizeof(ll)); + ll.sll_family = AF_PACKET; + ll.sll_ifindex = bss->ifindex; + ll.sll_protocol = htons(ETH_P_PAE); + ll.sll_halen = ETH_ALEN; + os_memcpy(ll.sll_addr, addr, ETH_ALEN); + ret = sendto(bss->drv->eapol_tx_sock, data, data_len, 0, + (struct sockaddr *) &ll, sizeof(ll)); + if (ret < 0) + wpa_printf(MSG_ERROR, "nl80211: EAPOL TX: %s", + strerror(errno)); + + return ret; } -#endif /* CONFIG_AP */ static const u8 rfc1042_header[6] = { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 }; @@ -5241,18 +8114,15 @@ int res; int qos = flags & WPA_STA_WMM; -#ifdef CONFIG_AP - if (drv->no_monitor_iface_capab) - return nl80211_send_eapol_data(bss, addr, data, data_len, - own_addr); -#endif /* CONFIG_AP */ + if (drv->device_ap_sme || !drv->use_monitor) + return nl80211_send_eapol_data(bss, addr, data, data_len); len = sizeof(*hdr) + (qos ? 2 : 0) + sizeof(rfc1042_header) + 2 + data_len; hdr = os_zalloc(len); if (hdr == NULL) { - printf("malloc() failed for i802_send_data(len=%lu)\n", - (unsigned long) len); + wpa_printf(MSG_INFO, "nl80211: Failed to allocate EAPOL buffer(len=%lu)", + (unsigned long) len); return -1; } @@ -5272,8 +8142,8 @@ pos = (u8 *) (hdr + 1); if (qos) { - /* add an empty QoS header if needed */ - pos[0] = 0; + /* Set highest priority in QoS header */ + pos[0] = 7; pos[1] = 0; pos += 2; } @@ -5284,7 +8154,8 @@ pos += 2; memcpy(pos, data, data_len); - res = wpa_driver_nl80211_send_frame(drv, (u8 *) hdr, len, encrypt); + res = wpa_driver_nl80211_send_frame(bss, (u8 *) hdr, len, encrypt, 0, + 0, 0, 0, 0); if (res < 0) { wpa_printf(MSG_ERROR, "i802_send_eapol - packet len: %lu - " "failed: %d (%s)", @@ -5302,19 +8173,14 @@ { struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; - struct nl_msg *msg, *flags = NULL; + struct nl_msg *msg; + struct nlattr *flags; struct nl80211_sta_flag_update upd; msg = nlmsg_alloc(); if (!msg) return -ENOMEM; - flags = nlmsg_alloc(); - if (!flags) { - nlmsg_free(msg); - return -ENOMEM; - } - nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_STATION); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, @@ -5325,35 +8191,34 @@ * Backwards compatibility version using NL80211_ATTR_STA_FLAGS. This * can be removed eventually. */ + flags = nla_nest_start(msg, NL80211_ATTR_STA_FLAGS); + if (!flags) + goto nla_put_failure; if (total_flags & WPA_STA_AUTHORIZED) - NLA_PUT_FLAG(flags, NL80211_STA_FLAG_AUTHORIZED); + NLA_PUT_FLAG(msg, NL80211_STA_FLAG_AUTHORIZED); if (total_flags & WPA_STA_WMM) - NLA_PUT_FLAG(flags, NL80211_STA_FLAG_WME); + NLA_PUT_FLAG(msg, NL80211_STA_FLAG_WME); if (total_flags & WPA_STA_SHORT_PREAMBLE) - NLA_PUT_FLAG(flags, NL80211_STA_FLAG_SHORT_PREAMBLE); + NLA_PUT_FLAG(msg, NL80211_STA_FLAG_SHORT_PREAMBLE); if (total_flags & WPA_STA_MFP) - NLA_PUT_FLAG(flags, NL80211_STA_FLAG_MFP); + NLA_PUT_FLAG(msg, NL80211_STA_FLAG_MFP); if (total_flags & WPA_STA_TDLS_PEER) - NLA_PUT_FLAG(flags, NL80211_STA_FLAG_TDLS_PEER); + NLA_PUT_FLAG(msg, NL80211_STA_FLAG_TDLS_PEER); - if (nla_put_nested(msg, NL80211_ATTR_STA_FLAGS, flags)) - goto nla_put_failure; + nla_nest_end(msg, flags); os_memset(&upd, 0, sizeof(upd)); upd.mask = sta_flags_nl80211(flags_or | ~flags_and); upd.set = sta_flags_nl80211(flags_or); NLA_PUT(msg, NL80211_ATTR_STA_FLAGS2, sizeof(upd), &upd); - nlmsg_free(flags); - return send_and_recv_msgs(drv, msg, NULL, NULL); nla_put_failure: nlmsg_free(msg); - nlmsg_free(flags); return -ENOBUFS; } @@ -5361,7 +8226,10 @@ static int wpa_driver_nl80211_ap(struct wpa_driver_nl80211_data *drv, struct wpa_driver_associate_params *params) { - enum nl80211_iftype nlmode; + enum nl80211_iftype nlmode, old_mode; + struct hostapd_freq_params freq = { + .freq = params->freq, + }; if (params->p2p) { wpa_printf(MSG_DEBUG, "nl80211: Setup AP operations for P2P " @@ -5370,23 +8238,19 @@ } else nlmode = NL80211_IFTYPE_AP; - if (wpa_driver_nl80211_set_mode(&drv->first_bss, nlmode) || - wpa_driver_nl80211_set_freq(drv, params->freq, 0, 0)) { + old_mode = drv->nlmode; + if (wpa_driver_nl80211_set_mode(drv->first_bss, nlmode)) { nl80211_remove_monitor_interface(drv); return -1; } - if (drv->no_monitor_iface_capab) { - if (wpa_driver_nl80211_probe_req_report(&drv->first_bss, 1) < 0) - { - wpa_printf(MSG_DEBUG, "nl80211: Failed to enable " - "Probe Request frame reporting in AP mode"); - /* Try to survive without this */ - } + if (wpa_driver_nl80211_set_freq(drv->first_bss, &freq)) { + if (old_mode != nlmode) + wpa_driver_nl80211_set_mode(drv->first_bss, old_mode); + nl80211_remove_monitor_interface(drv); + return -1; } - drv->ap_oper_freq = params->freq; - return 0; } @@ -5414,6 +8278,12 @@ wpa_printf(MSG_DEBUG, "nl80211: Leave IBSS request sent successfully"); nla_put_failure: + if (wpa_driver_nl80211_set_mode(drv->first_bss, + NL80211_IFTYPE_STATION)) { + wpa_printf(MSG_INFO, "nl80211: Failed to set interface into " + "station mode"); + } + nlmsg_free(msg); return ret; } @@ -5428,7 +8298,7 @@ wpa_printf(MSG_DEBUG, "nl80211: Join IBSS (ifindex=%d)", drv->ifindex); - if (wpa_driver_nl80211_set_mode(&drv->first_bss, + if (wpa_driver_nl80211_set_mode(drv->first_bss, NL80211_IFTYPE_ADHOC)) { wpa_printf(MSG_INFO, "nl80211: Failed to set interface into " "IBSS mode"); @@ -5460,6 +8330,20 @@ if (ret) goto nla_put_failure; + if (params->bssid && params->fixed_bssid) { + wpa_printf(MSG_DEBUG, " * BSSID=" MACSTR, + MAC2STR(params->bssid)); + NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, params->bssid); + } + + if (params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X || + params->key_mgmt_suite == WPA_KEY_MGMT_PSK || + params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SHA256 || + params->key_mgmt_suite == WPA_KEY_MGMT_PSK_SHA256) { + wpa_printf(MSG_DEBUG, " * control port"); + NLA_PUT_FLAG(msg, NL80211_ATTR_CONTROL_PORT); + } + if (params->wpa_ie) { wpa_hexdump(MSG_DEBUG, " * Extra IEs for Beacon/Probe Response frames", @@ -5493,81 +8377,32 @@ } -static unsigned int nl80211_get_assoc_bssid(struct wpa_driver_nl80211_data *drv, - u8 *bssid) +static int nl80211_connect_common(struct wpa_driver_nl80211_data *drv, + struct wpa_driver_associate_params *params, + struct nl_msg *msg) { - struct nl_msg *msg; - int ret; - struct nl80211_bss_info_arg arg; - - os_memset(&arg, 0, sizeof(arg)); - msg = nlmsg_alloc(); - if (!msg) - goto nla_put_failure; - - nl80211_cmd(drv, msg, NLM_F_DUMP, NL80211_CMD_GET_SCAN); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); - arg.drv = drv; - ret = send_and_recv_msgs(drv, msg, bss_info_handler, &arg); - msg = NULL; - if (ret == 0) { - if (is_zero_ether_addr(arg.assoc_bssid)) - return -ENOTCONN; - os_memcpy(bssid, arg.assoc_bssid, ETH_ALEN); - return 0; - } - wpa_printf(MSG_DEBUG, "nl80211: Scan result fetch failed: ret=%d " - "(%s)", ret, strerror(-ret)); -nla_put_failure: - nlmsg_free(msg); - return drv->assoc_freq; -} - - -static int nl80211_disconnect(struct wpa_driver_nl80211_data *drv, - const u8 *bssid) -{ - u8 addr[ETH_ALEN]; - - if (bssid == NULL) { - int res = nl80211_get_assoc_bssid(drv, addr); - if (res) - return res; - bssid = addr; - } - - return wpa_driver_nl80211_disconnect(drv, bssid, - WLAN_REASON_PREV_AUTH_NOT_VALID); -} - - -static int wpa_driver_nl80211_connect( - struct wpa_driver_nl80211_data *drv, - struct wpa_driver_associate_params *params) -{ - struct nl_msg *msg; - enum nl80211_auth_type type; - int ret = 0; - int algs; - - msg = nlmsg_alloc(); - if (!msg) - return -1; - - wpa_printf(MSG_DEBUG, "nl80211: Connect (ifindex=%d)", drv->ifindex); - nl80211_cmd(drv, msg, 0, NL80211_CMD_CONNECT); - - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); if (params->bssid) { wpa_printf(MSG_DEBUG, " * bssid=" MACSTR, MAC2STR(params->bssid)); NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, params->bssid); } + if (params->freq) { wpa_printf(MSG_DEBUG, " * freq=%d", params->freq); NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, params->freq); + drv->assoc_freq = params->freq; + } else + drv->assoc_freq = 0; + + if (params->bg_scan_period >= 0) { + wpa_printf(MSG_DEBUG, " * bg scan period=%d", + params->bg_scan_period); + NLA_PUT_U16(msg, NL80211_ATTR_BG_SCAN_PERIOD, + params->bg_scan_period); } + if (params->ssid) { wpa_hexdump_ascii(MSG_DEBUG, " * SSID", params->ssid, params->ssid_len); @@ -5578,39 +8413,12 @@ os_memcpy(drv->ssid, params->ssid, params->ssid_len); drv->ssid_len = params->ssid_len; } + wpa_hexdump(MSG_DEBUG, " * IEs", params->wpa_ie, params->wpa_ie_len); if (params->wpa_ie) NLA_PUT(msg, NL80211_ATTR_IE, params->wpa_ie_len, params->wpa_ie); - algs = 0; - if (params->auth_alg & WPA_AUTH_ALG_OPEN) - algs++; - if (params->auth_alg & WPA_AUTH_ALG_SHARED) - algs++; - if (params->auth_alg & WPA_AUTH_ALG_LEAP) - algs++; - if (algs > 1) { - wpa_printf(MSG_DEBUG, " * Leave out Auth Type for automatic " - "selection"); - goto skip_auth_type; - } - - if (params->auth_alg & WPA_AUTH_ALG_OPEN) - type = NL80211_AUTHTYPE_OPEN_SYSTEM; - else if (params->auth_alg & WPA_AUTH_ALG_SHARED) - type = NL80211_AUTHTYPE_SHARED_KEY; - else if (params->auth_alg & WPA_AUTH_ALG_LEAP) - type = NL80211_AUTHTYPE_NETWORK_EAP; - else if (params->auth_alg & WPA_AUTH_ALG_FT) - type = NL80211_AUTHTYPE_FT; - else - goto nla_put_failure; - - wpa_printf(MSG_DEBUG, " * Auth Type %d", type); - NLA_PUT_U32(msg, NL80211_ATTR_AUTH_TYPE, type); - -skip_auth_type: if (params->wpa_proto) { enum nl80211_wpa_versions ver = 0; @@ -5623,57 +8431,39 @@ NLA_PUT_U32(msg, NL80211_ATTR_WPA_VERSIONS, ver); } - if (params->pairwise_suite != CIPHER_NONE) { - int cipher; - - switch (params->pairwise_suite) { - case CIPHER_WEP40: - cipher = WLAN_CIPHER_SUITE_WEP40; - break; - case CIPHER_WEP104: - cipher = WLAN_CIPHER_SUITE_WEP104; - break; - case CIPHER_CCMP: - cipher = WLAN_CIPHER_SUITE_CCMP; - break; - case CIPHER_TKIP: - default: - cipher = WLAN_CIPHER_SUITE_TKIP; - break; - } + if (params->pairwise_suite != WPA_CIPHER_NONE) { + u32 cipher = wpa_cipher_to_cipher_suite(params->pairwise_suite); + wpa_printf(MSG_DEBUG, " * pairwise=0x%x", cipher); NLA_PUT_U32(msg, NL80211_ATTR_CIPHER_SUITES_PAIRWISE, cipher); } - if (params->group_suite != CIPHER_NONE) { - int cipher; - - switch (params->group_suite) { - case CIPHER_WEP40: - cipher = WLAN_CIPHER_SUITE_WEP40; - break; - case CIPHER_WEP104: - cipher = WLAN_CIPHER_SUITE_WEP104; - break; - case CIPHER_CCMP: - cipher = WLAN_CIPHER_SUITE_CCMP; - break; - case CIPHER_TKIP: - default: - cipher = WLAN_CIPHER_SUITE_TKIP; - break; - } + if (params->group_suite != WPA_CIPHER_NONE) { + u32 cipher = wpa_cipher_to_cipher_suite(params->group_suite); + wpa_printf(MSG_DEBUG, " * group=0x%x", cipher); NLA_PUT_U32(msg, NL80211_ATTR_CIPHER_SUITE_GROUP, cipher); } - if (params->key_mgmt_suite == KEY_MGMT_802_1X || - params->key_mgmt_suite == KEY_MGMT_PSK) { + if (params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X || + params->key_mgmt_suite == WPA_KEY_MGMT_PSK || + params->key_mgmt_suite == WPA_KEY_MGMT_FT_IEEE8021X || + params->key_mgmt_suite == WPA_KEY_MGMT_FT_PSK || + params->key_mgmt_suite == WPA_KEY_MGMT_CCKM) { int mgmt = WLAN_AKM_SUITE_PSK; switch (params->key_mgmt_suite) { - case KEY_MGMT_802_1X: + case WPA_KEY_MGMT_CCKM: + mgmt = WLAN_AKM_SUITE_CCKM; + break; + case WPA_KEY_MGMT_IEEE8021X: mgmt = WLAN_AKM_SUITE_8021X; break; - case KEY_MGMT_PSK: + case WPA_KEY_MGMT_FT_IEEE8021X: + mgmt = WLAN_AKM_SUITE_FT_8021X; + break; + case WPA_KEY_MGMT_FT_PSK: + mgmt = WLAN_AKM_SUITE_FT_PSK; + break; + case WPA_KEY_MGMT_PSK: default: mgmt = WLAN_AKM_SUITE_PSK; break; @@ -5681,145 +8471,172 @@ NLA_PUT_U32(msg, NL80211_ATTR_AKM_SUITES, mgmt); } - ret = nl80211_set_conn_keys(params, msg); - if (ret) - goto nla_put_failure; - - ret = send_and_recv_msgs(drv, msg, NULL, NULL); - msg = NULL; - if (ret) { - wpa_printf(MSG_DEBUG, "nl80211: MLME connect failed: ret=%d " - "(%s)", ret, strerror(-ret)); - /* - * cfg80211 does not currently accept new connection if we are - * already connected. As a workaround, force disconnection and - * try again once the driver indicates it completed - * disconnection. - */ - if (ret == -EALREADY) - nl80211_disconnect(drv, params->bssid); - goto nla_put_failure; - } - ret = 0; - wpa_printf(MSG_DEBUG, "nl80211: Connect request send successfully"); + NLA_PUT_FLAG(msg, NL80211_ATTR_CONTROL_PORT); -nla_put_failure: - nlmsg_free(msg); - return ret; + if (params->mgmt_frame_protection == MGMT_FRAME_PROTECTION_REQUIRED) + NLA_PUT_U32(msg, NL80211_ATTR_USE_MFP, NL80211_MFP_REQUIRED); -} + if (params->disable_ht) + NLA_PUT_FLAG(msg, NL80211_ATTR_DISABLE_HT); + if (params->htcaps && params->htcaps_mask) { + int sz = sizeof(struct ieee80211_ht_capabilities); + NLA_PUT(msg, NL80211_ATTR_HT_CAPABILITY, sz, params->htcaps); + NLA_PUT(msg, NL80211_ATTR_HT_CAPABILITY_MASK, sz, + params->htcaps_mask); + } -static int wpa_driver_nl80211_associate( - void *priv, struct wpa_driver_associate_params *params) -{ - struct i802_bss *bss = priv; - struct wpa_driver_nl80211_data *drv = bss->drv; - int ret = -1; - struct nl_msg *msg; +#ifdef CONFIG_VHT_OVERRIDES + if (params->disable_vht) { + wpa_printf(MSG_DEBUG, " * VHT disabled"); + NLA_PUT_FLAG(msg, NL80211_ATTR_DISABLE_VHT); + } - if (params->mode == IEEE80211_MODE_AP) - return wpa_driver_nl80211_ap(drv, params); + if (params->vhtcaps && params->vhtcaps_mask) { + int sz = sizeof(struct ieee80211_vht_capabilities); + NLA_PUT(msg, NL80211_ATTR_VHT_CAPABILITY, sz, params->vhtcaps); + NLA_PUT(msg, NL80211_ATTR_VHT_CAPABILITY_MASK, sz, + params->vhtcaps_mask); + } +#endif /* CONFIG_VHT_OVERRIDES */ - if (params->mode == IEEE80211_MODE_IBSS) - return wpa_driver_nl80211_ibss(drv, params); + if (params->p2p) + wpa_printf(MSG_DEBUG, " * P2P group"); - if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME)) { - enum nl80211_iftype nlmode = params->p2p ? - NL80211_IFTYPE_P2P_CLIENT : NL80211_IFTYPE_STATION; + return 0; +nla_put_failure: + return -1; +} - if (wpa_driver_nl80211_set_mode(priv, nlmode) < 0) - return -1; - return wpa_driver_nl80211_connect(drv, params); - } - drv->associated = 0; +static int wpa_driver_nl80211_try_connect( + struct wpa_driver_nl80211_data *drv, + struct wpa_driver_associate_params *params) +{ + struct nl_msg *msg; + enum nl80211_auth_type type; + int ret; + int algs; msg = nlmsg_alloc(); if (!msg) return -1; - wpa_printf(MSG_DEBUG, "nl80211: Associate (ifindex=%d)", - drv->ifindex); - nl80211_cmd(drv, msg, 0, NL80211_CMD_ASSOCIATE); + wpa_printf(MSG_DEBUG, "nl80211: Connect (ifindex=%d)", drv->ifindex); + nl80211_cmd(drv, msg, 0, NL80211_CMD_CONNECT); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); - if (params->bssid) { - wpa_printf(MSG_DEBUG, " * bssid=" MACSTR, - MAC2STR(params->bssid)); - NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, params->bssid); + ret = nl80211_connect_common(drv, params, msg); + if (ret) + goto nla_put_failure; + + algs = 0; + if (params->auth_alg & WPA_AUTH_ALG_OPEN) + algs++; + if (params->auth_alg & WPA_AUTH_ALG_SHARED) + algs++; + if (params->auth_alg & WPA_AUTH_ALG_LEAP) + algs++; + if (algs > 1) { + wpa_printf(MSG_DEBUG, " * Leave out Auth Type for automatic " + "selection"); + goto skip_auth_type; } - if (params->freq) { - wpa_printf(MSG_DEBUG, " * freq=%d", params->freq); - NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, params->freq); - drv->assoc_freq = params->freq; - } else - drv->assoc_freq = 0; - if (params->ssid) { - wpa_hexdump_ascii(MSG_DEBUG, " * SSID", - params->ssid, params->ssid_len); - NLA_PUT(msg, NL80211_ATTR_SSID, params->ssid_len, - params->ssid); - if (params->ssid_len > sizeof(drv->ssid)) - goto nla_put_failure; - os_memcpy(drv->ssid, params->ssid, params->ssid_len); - drv->ssid_len = params->ssid_len; + + if (params->auth_alg & WPA_AUTH_ALG_OPEN) + type = NL80211_AUTHTYPE_OPEN_SYSTEM; + else if (params->auth_alg & WPA_AUTH_ALG_SHARED) + type = NL80211_AUTHTYPE_SHARED_KEY; + else if (params->auth_alg & WPA_AUTH_ALG_LEAP) + type = NL80211_AUTHTYPE_NETWORK_EAP; + else if (params->auth_alg & WPA_AUTH_ALG_FT) + type = NL80211_AUTHTYPE_FT; + else + goto nla_put_failure; + + wpa_printf(MSG_DEBUG, " * Auth Type %d", type); + NLA_PUT_U32(msg, NL80211_ATTR_AUTH_TYPE, type); + +skip_auth_type: + ret = nl80211_set_conn_keys(params, msg); + if (ret) + goto nla_put_failure; + + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + msg = NULL; + if (ret) { + wpa_printf(MSG_DEBUG, "nl80211: MLME connect failed: ret=%d " + "(%s)", ret, strerror(-ret)); + goto nla_put_failure; } - wpa_hexdump(MSG_DEBUG, " * IEs", params->wpa_ie, params->wpa_ie_len); - if (params->wpa_ie) - NLA_PUT(msg, NL80211_ATTR_IE, params->wpa_ie_len, - params->wpa_ie); + ret = 0; + wpa_printf(MSG_DEBUG, "nl80211: Connect request send successfully"); + +nla_put_failure: + nlmsg_free(msg); + return ret; - if (params->pairwise_suite != CIPHER_NONE) { - int cipher; +} - switch (params->pairwise_suite) { - case CIPHER_WEP40: - cipher = WLAN_CIPHER_SUITE_WEP40; - break; - case CIPHER_WEP104: - cipher = WLAN_CIPHER_SUITE_WEP104; - break; - case CIPHER_CCMP: - cipher = WLAN_CIPHER_SUITE_CCMP; - break; - case CIPHER_TKIP: - default: - cipher = WLAN_CIPHER_SUITE_TKIP; - break; - } - wpa_printf(MSG_DEBUG, " * pairwise=0x%x", cipher); - NLA_PUT_U32(msg, NL80211_ATTR_CIPHER_SUITES_PAIRWISE, cipher); + +static int wpa_driver_nl80211_connect( + struct wpa_driver_nl80211_data *drv, + struct wpa_driver_associate_params *params) +{ + int ret = wpa_driver_nl80211_try_connect(drv, params); + if (ret == -EALREADY) { + /* + * cfg80211 does not currently accept new connections if + * we are already connected. As a workaround, force + * disconnection and try again. + */ + wpa_printf(MSG_DEBUG, "nl80211: Explicitly " + "disconnecting before reassociation " + "attempt"); + if (wpa_driver_nl80211_disconnect( + drv, WLAN_REASON_PREV_AUTH_NOT_VALID)) + return -1; + ret = wpa_driver_nl80211_try_connect(drv, params); } + return ret; +} - if (params->group_suite != CIPHER_NONE) { - int cipher; - switch (params->group_suite) { - case CIPHER_WEP40: - cipher = WLAN_CIPHER_SUITE_WEP40; - break; - case CIPHER_WEP104: - cipher = WLAN_CIPHER_SUITE_WEP104; - break; - case CIPHER_CCMP: - cipher = WLAN_CIPHER_SUITE_CCMP; - break; - case CIPHER_TKIP: - default: - cipher = WLAN_CIPHER_SUITE_TKIP; - break; - } - wpa_printf(MSG_DEBUG, " * group=0x%x", cipher); - NLA_PUT_U32(msg, NL80211_ATTR_CIPHER_SUITE_GROUP, cipher); +static int wpa_driver_nl80211_associate( + void *priv, struct wpa_driver_associate_params *params) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + int ret; + struct nl_msg *msg; + + if (params->mode == IEEE80211_MODE_AP) + return wpa_driver_nl80211_ap(drv, params); + + if (params->mode == IEEE80211_MODE_IBSS) + return wpa_driver_nl80211_ibss(drv, params); + + if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME)) { + enum nl80211_iftype nlmode = params->p2p ? + NL80211_IFTYPE_P2P_CLIENT : NL80211_IFTYPE_STATION; + + if (wpa_driver_nl80211_set_mode(priv, nlmode) < 0) + return -1; + return wpa_driver_nl80211_connect(drv, params); } -#ifdef CONFIG_IEEE80211W - if (params->mgmt_frame_protection == MGMT_FRAME_PROTECTION_REQUIRED) - NLA_PUT_U32(msg, NL80211_ATTR_USE_MFP, NL80211_MFP_REQUIRED); -#endif /* CONFIG_IEEE80211W */ + nl80211_mark_disconnected(drv); - NLA_PUT_FLAG(msg, NL80211_ATTR_CONTROL_PORT); + msg = nlmsg_alloc(); + if (!msg) + return -1; + + wpa_printf(MSG_DEBUG, "nl80211: Associate (ifindex=%d)", + drv->ifindex); + nl80211_cmd(drv, msg, 0, NL80211_CMD_ASSOCIATE); + + ret = nl80211_connect_common(drv, params, msg); + if (ret) + goto nla_put_failure; if (params->prev_bssid) { wpa_printf(MSG_DEBUG, " * prev_bssid=" MACSTR, @@ -5828,14 +8645,12 @@ params->prev_bssid); } - if (params->p2p) - wpa_printf(MSG_DEBUG, " * P2P group"); - ret = send_and_recv_msgs(drv, msg, NULL, NULL); msg = NULL; if (ret) { - wpa_printf(MSG_DEBUG, "nl80211: MLME command failed: ret=%d " - "(%s)", ret, strerror(-ret)); + wpa_dbg(drv->ctx, MSG_DEBUG, + "nl80211: MLME command failed (assoc): ret=%d (%s)", + ret, strerror(-ret)); nl80211_dump_scan(drv); goto nla_put_failure; } @@ -5863,7 +8678,8 @@ return -ENOMEM; nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_INTERFACE); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifindex); + if (nl80211_set_iface_id(msg, drv->first_bss) < 0) + goto nla_put_failure; NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, mode); ret = send_and_recv_msgs(drv, msg, NULL, NULL); @@ -5885,13 +8701,21 @@ int ret = -1; int i; int was_ap = is_ap_interface(drv->nlmode); + int res; + + res = nl80211_set_mode(drv, drv->ifindex, nlmode); + if (res && nlmode == nl80211_get_ifmode(bss)) + res = 0; - if (nl80211_set_mode(drv, drv->ifindex, nlmode) == 0) { + if (res == 0) { drv->nlmode = nlmode; ret = 0; goto done; } + if (res == -ENODEV) + return -1; + if (nlmode == drv->nlmode) { wpa_printf(MSG_DEBUG, "nl80211: Interface already in " "requested mode - ignore error"); @@ -5906,9 +8730,7 @@ wpa_printf(MSG_DEBUG, "nl80211: Try mode change after setting " "interface down"); for (i = 0; i < 10; i++) { - int res; - res = linux_set_iface_flags(drv->global->ioctl_sock, - bss->ifname, 0); + res = i802_set_iface_flags(bss, 0); if (res == -EACCES || res == -ENODEV) break; if (res == 0) { @@ -5917,8 +8739,7 @@ ret = nl80211_set_mode(drv, drv->ifindex, nlmode); if (ret == -EACCES) break; - res = linux_set_iface_flags(drv->global->ioctl_sock, - bss->ifname, 1); + res = i802_set_iface_flags(bss, 1); if (res && !ret) ret = -1; else if (ret != -EBUSY) @@ -5937,33 +8758,35 @@ } done: - if (!ret && is_ap_interface(nlmode)) { - /* Setup additional AP mode functionality if needed */ - if (!drv->no_monitor_iface_capab && drv->monitor_ifidx < 0 && - nl80211_create_monitor_interface(drv) && - !drv->no_monitor_iface_capab) - return -1; - } else if (!ret && !is_ap_interface(nlmode)) { - /* Remove additional AP mode functionality */ - if (was_ap && drv->no_monitor_iface_capab) - wpa_driver_nl80211_probe_req_report(bss, 0); - nl80211_remove_monitor_interface(drv); - bss->beacon_set = 0; + if (ret) { + wpa_printf(MSG_DEBUG, "nl80211: Interface mode change to %d " + "from %d failed", nlmode, drv->nlmode); + return ret; } - if (!ret && is_p2p_interface(drv->nlmode)) { + if (is_p2p_net_interface(nlmode)) nl80211_disable_11b_rates(drv, drv->ifindex, 1); - drv->disabled_11b_rates = 1; - } else if (!ret && drv->disabled_11b_rates) { + else if (drv->disabled_11b_rates) nl80211_disable_11b_rates(drv, drv->ifindex, 0); - drv->disabled_11b_rates = 0; + + if (is_ap_interface(nlmode)) { + nl80211_mgmt_unsubscribe(bss, "start AP"); + /* Setup additional AP mode functionality if needed */ + if (nl80211_setup_ap(bss)) + return -1; + } else if (was_ap) { + /* Remove additional AP mode functionality */ + nl80211_teardown_ap(bss); + } else { + nl80211_mgmt_unsubscribe(bss, "mode change"); } - if (ret) - wpa_printf(MSG_DEBUG, "nl80211: Interface mode change to %d " - "from %d failed", nlmode, drv->nlmode); + if (!bss->in_deinit && !is_ap_interface(nlmode) && + nl80211_mgmt_subscribe_non_ap(bss) < 0) + wpa_printf(MSG_DEBUG, "nl80211: Failed to register Action " + "frame processing - ignore for now"); - return ret; + return 0; } @@ -5975,6 +8798,18 @@ if (!drv->has_capability) return -1; os_memcpy(capa, &drv->capa, sizeof(*capa)); + if (drv->extended_capa && drv->extended_capa_mask) { + capa->extended_capa = drv->extended_capa; + capa->extended_capa_mask = drv->extended_capa_mask; + capa->extended_capa_len = drv->extended_capa_len; + } + + if ((capa->flags & WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE) && + !drv->allow_p2p_device) { + wpa_printf(MSG_DEBUG, "nl80211: Do not indicate P2P_DEVICE support (p2p_device=1 driver param not specified)"); + capa->flags &= ~WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE; + } + return 0; } @@ -5984,8 +8819,9 @@ struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; - wpa_printf(MSG_DEBUG, "%s: operstate %d->%d (%s)", - __func__, drv->operstate, state, state ? "UP" : "DORMANT"); + wpa_printf(MSG_DEBUG, "nl80211: Set %s operstate %d->%d (%s)", + bss->ifname, drv->operstate, state, + state ? "UP" : "DORMANT"); drv->operstate = state; return netlink_send_oper_ifla(drv->global->netlink, drv->ifindex, -1, state ? IF_OPER_UP : IF_OPER_DORMANT); @@ -5998,6 +8834,15 @@ struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; struct nl80211_sta_flag_update upd; + int ret = -ENOBUFS; + + if (!drv->associated && is_zero_ether_addr(drv->bssid) && !authorized) { + wpa_printf(MSG_DEBUG, "nl80211: Skip set_supp_port(unauthorized) while not associated"); + return 0; + } + + wpa_printf(MSG_DEBUG, "nl80211: Set supplicant port %sauthorized for " + MACSTR, authorized ? "" : "un", MAC2STR(drv->bssid)); msg = nlmsg_alloc(); if (!msg) @@ -6015,10 +8860,15 @@ upd.set = BIT(NL80211_STA_FLAG_AUTHORIZED); NLA_PUT(msg, NL80211_ATTR_STA_FLAGS2, sizeof(upd), &upd); - return send_and_recv_msgs(drv, msg, NULL, NULL); + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + msg = NULL; + if (!ret) + return 0; nla_put_failure: nlmsg_free(msg); - return -ENOBUFS; + wpa_printf(MSG_DEBUG, "nl80211: Failed to set STA flag: %d (%s)", + ret, strerror(-ret)); + return ret; } @@ -6026,14 +8876,10 @@ static int i802_set_freq(void *priv, struct hostapd_freq_params *freq) { struct i802_bss *bss = priv; - struct wpa_driver_nl80211_data *drv = bss->drv; - return wpa_driver_nl80211_set_freq(drv, freq->freq, freq->ht_enabled, - freq->sec_channel_offset); + return wpa_driver_nl80211_set_freq(bss, freq); } -#if defined(HOSTAPD) || defined(CONFIG_AP) - static inline int min_int(int a, int b) { if (a < b) @@ -6090,35 +8936,6 @@ } -static int i802_set_rate_sets(void *priv, int *supp_rates, int *basic_rates, - int mode) -{ - struct i802_bss *bss = priv; - struct wpa_driver_nl80211_data *drv = bss->drv; - struct nl_msg *msg; - u8 rates[NL80211_MAX_SUPP_RATES]; - u8 rates_len = 0; - int i; - - msg = nlmsg_alloc(); - if (!msg) - return -ENOMEM; - - nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_BSS); - - for (i = 0; i < NL80211_MAX_SUPP_RATES && basic_rates[i] >= 0; i++) - rates[rates_len++] = basic_rates[i] / 5; - - NLA_PUT(msg, NL80211_ATTR_BSS_BASIC_RATES, rates_len, rates); - - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(bss->ifname)); - - return send_and_recv_msgs(drv, msg, NULL, NULL); - nla_put_failure: - return -ENOBUFS; -} - - static int i802_set_rts(void *priv, int rts) { struct i802_bss *bss = priv; @@ -6190,11 +9007,14 @@ struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; + int res; msg = nlmsg_alloc(); if (!msg) return -1; + wpa_printf(MSG_DEBUG, "nl80211: flush -> DEL_STATION %s (all)", + bss->ifname); nl80211_cmd(drv, msg, 0, NL80211_CMD_DEL_STATION); /* @@ -6203,7 +9023,12 @@ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(bss->ifname)); - return send_and_recv_msgs(drv, msg, NULL, NULL); + res = send_and_recv_msgs(drv, msg, NULL, NULL); + if (res) { + wpa_printf(MSG_DEBUG, "nl80211: Station flush failed: ret=%d " + "(%s)", res, strerror(-res)); + } + return res; nla_put_failure: nlmsg_free(msg); return -ENOBUFS; @@ -6222,6 +9047,7 @@ [NL80211_STA_INFO_TX_BYTES] = { .type = NLA_U32 }, [NL80211_STA_INFO_RX_PACKETS] = { .type = NLA_U32 }, [NL80211_STA_INFO_TX_PACKETS] = { .type = NLA_U32 }, + [NL80211_STA_INFO_TX_FAILED] = { .type = NLA_U32 }, }; nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), @@ -6257,14 +9083,17 @@ if (stats[NL80211_STA_INFO_TX_PACKETS]) data->tx_packets = nla_get_u32(stats[NL80211_STA_INFO_TX_PACKETS]); + if (stats[NL80211_STA_INFO_TX_FAILED]) + data->tx_retry_failed = + nla_get_u32(stats[NL80211_STA_INFO_TX_FAILED]); return NL_SKIP; } -static int i802_read_sta_data(void *priv, struct hostap_sta_driver_data *data, +static int i802_read_sta_data(struct i802_bss *bss, + struct hostap_sta_driver_data *data, const u8 *addr) { - struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; @@ -6344,10 +9173,9 @@ } -static int i802_set_sta_vlan(void *priv, const u8 *addr, +static int i802_set_sta_vlan(struct i802_bss *bss, const u8 *addr, const char *ifname, int vlan_id) { - struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; int ret = -ENOBUFS; @@ -6356,6 +9184,10 @@ if (!msg) return -ENOMEM; + wpa_printf(MSG_DEBUG, "nl80211: %s[%d]: set_sta_vlan(" MACSTR + ", ifname=%s[%d], vlan_id=%d)", + bss->ifname, if_nametoindex(bss->ifname), + MAC2STR(addr), ifname, if_nametoindex(ifname), vlan_id); nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_STATION); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, @@ -6404,8 +9236,12 @@ int reason) { struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; struct ieee80211_mgmt mgmt; + if (drv->device_ap_sme) + return wpa_driver_nl80211_sta_remove(bss, addr); + memset(&mgmt, 0, sizeof(mgmt)); mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, WLAN_FC_STYPE_DEAUTH); @@ -6415,7 +9251,8 @@ mgmt.u.deauth.reason_code = host_to_le16(reason); return wpa_driver_nl80211_send_mlme(bss, (u8 *) &mgmt, IEEE80211_HDRLEN + - sizeof(mgmt.u.deauth)); + sizeof(mgmt.u.deauth), 0, 0, 0, 0, + 0); } @@ -6423,8 +9260,12 @@ int reason) { struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; struct ieee80211_mgmt mgmt; + if (drv->device_ap_sme) + return wpa_driver_nl80211_sta_remove(bss, addr); + memset(&mgmt, 0, sizeof(mgmt)); mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, WLAN_FC_STYPE_DISASSOC); @@ -6434,12 +9275,10 @@ mgmt.u.disassoc.reason_code = host_to_le16(reason); return wpa_driver_nl80211_send_mlme(bss, (u8 *) &mgmt, IEEE80211_HDRLEN + - sizeof(mgmt.u.disassoc)); + sizeof(mgmt.u.disassoc), 0, 0, 0, 0, + 0); } -#endif /* HOSTAPD || CONFIG_AP */ - -#ifdef HOSTAPD static void add_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx) { @@ -6460,8 +9299,8 @@ else old = NULL; - drv->if_indices = os_realloc(old, - sizeof(int) * (drv->num_if_indices + 1)); + drv->if_indices = os_realloc_array(old, drv->num_if_indices + 1, + sizeof(int)); if (!drv->if_indices) { if (!old) drv->if_indices = drv->default_if_indices; @@ -6505,29 +9344,40 @@ static int i802_set_wds_sta(void *priv, const u8 *addr, int aid, int val, - const char *bridge_ifname) + const char *bridge_ifname, char *ifname_wds) { struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; char name[IFNAMSIZ + 1]; os_snprintf(name, sizeof(name), "%s.sta%d", bss->ifname, aid); + if (ifname_wds) + os_strlcpy(ifname_wds, name, IFNAMSIZ + 1); + wpa_printf(MSG_DEBUG, "nl80211: Set WDS STA addr=" MACSTR " aid=%d val=%d name=%s", MAC2STR(addr), aid, val, name); if (val) { if (!if_nametoindex(name)) { if (nl80211_create_iface(drv, name, NL80211_IFTYPE_AP_VLAN, - NULL, 1) < 0) + bss->addr, 1, NULL, NULL, 0) < + 0) return -1; if (bridge_ifname && linux_br_add_if(drv->global->ioctl_sock, bridge_ifname, name) < 0) return -1; } - linux_set_iface_flags(drv->global->ioctl_sock, name, 1); + if (linux_set_iface_flags(drv->global->ioctl_sock, name, 1)) { + wpa_printf(MSG_ERROR, "nl80211: Failed to set WDS STA " + "interface %s up", name); + } return i802_set_sta_vlan(priv, addr, name, 0); } else { + if (bridge_ifname) + linux_br_del_if(drv->global->ioctl_sock, bridge_ifname, + name); + i802_set_sta_vlan(priv, addr, bss->ifname, 0); return wpa_driver_nl80211_if_remove(priv, WPA_IF_AP_VLAN, name); @@ -6546,7 +9396,8 @@ len = recvfrom(sock, buf, sizeof(buf), 0, (struct sockaddr *)&lladdr, &fromlen); if (len < 0) { - perror("recv"); + wpa_printf(MSG_ERROR, "nl80211: EAPOL recv failed: %s", + strerror(errno)); return; } @@ -6619,14 +9470,13 @@ int ifindex, br_ifindex; int br_added = 0; - bss = wpa_driver_nl80211_init(hapd, params->ifname, - params->global_priv); + bss = wpa_driver_nl80211_drv_init(hapd, params->ifname, + params->global_priv, 1, + params->bssid); if (bss == NULL) return NULL; drv = bss->drv; - drv->nlmode = NL80211_IFTYPE_AP; - drv->eapol_sock = -1; if (linux_br_get(brname, params->ifname) == 0) { wpa_printf(MSG_DEBUG, "nl80211: Interface %s is in bridge %s", @@ -6637,8 +9487,6 @@ br_ifindex = 0; } - drv->num_if_indices = sizeof(drv->default_if_indices) / sizeof(int); - drv->if_indices = drv->default_if_indices; for (i = 0; i < params->num_bridge; i++) { if (params->bridge[i]) { ifindex = if_nametoindex(params->bridge[i]); @@ -6655,37 +9503,20 @@ /* start listening for EAPOL on the default AP interface */ add_ifidx(drv, drv->ifindex); - if (linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 0)) - goto failed; - - if (params->bssid) { - if (linux_set_ifhwaddr(drv->global->ioctl_sock, bss->ifname, - params->bssid)) - goto failed; - } - - if (wpa_driver_nl80211_set_mode(bss, drv->nlmode)) { - wpa_printf(MSG_ERROR, "nl80211: Failed to set interface %s " - "into AP mode", bss->ifname); - goto failed; - } - if (params->num_bridge && params->bridge[0] && i802_check_bridge(drv, bss, params->bridge[0], params->ifname) < 0) goto failed; - if (linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 1)) - goto failed; - drv->eapol_sock = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_PAE)); if (drv->eapol_sock < 0) { - perror("socket(PF_PACKET, SOCK_DGRAM, ETH_P_PAE)"); + wpa_printf(MSG_ERROR, "nl80211: socket(PF_PACKET, SOCK_DGRAM, ETH_P_PAE) failed: %s", + strerror(errno)); goto failed; } if (eloop_register_read_sock(drv->eapol_sock, handle_eapol, drv, NULL)) { - printf("Could not register read socket for eapol\n"); + wpa_printf(MSG_INFO, "nl80211: Could not register read socket for eapol"); goto failed; } @@ -6693,6 +9524,8 @@ params->own_addr)) goto failed; + memcpy(bss->addr, params->own_addr, ETH_ALEN); + return bss; failed: @@ -6703,11 +9536,10 @@ static void i802_deinit(void *priv) { - wpa_driver_nl80211_deinit(priv); + struct i802_bss *bss = priv; + wpa_driver_nl80211_deinit(bss); } -#endif /* HOSTAPD */ - static enum nl80211_iftype wpa_driver_nl80211_if_type( enum wpa_driver_if_type type) @@ -6724,6 +9556,8 @@ return NL80211_IFTYPE_AP; case WPA_IF_P2P_GO: return NL80211_IFTYPE_P2P_GO; + case WPA_IF_P2P_DEVICE: + return NL80211_IFTYPE_P2P_DEVICE; } return -1; } @@ -6736,7 +9570,7 @@ struct wpa_driver_nl80211_data *drv; dl_list_for_each(drv, &global->interfaces, struct wpa_driver_nl80211_data, list) { - if (os_memcmp(addr, drv->addr, ETH_ALEN) == 0) + if (os_memcmp(addr, drv->first_bss->addr, ETH_ALEN) == 0) return 1; } return 0; @@ -6751,9 +9585,9 @@ if (!drv->global) return -1; - os_memcpy(new_addr, drv->addr, ETH_ALEN); + os_memcpy(new_addr, drv->first_bss->addr, ETH_ALEN); for (idx = 0; idx < 64; idx++) { - new_addr[0] = drv->addr[0] | 0x02; + new_addr[0] = drv->first_bss->addr[0] | 0x02; new_addr[0] ^= idx << 2; if (!nl80211_addr_in_use(drv->global, new_addr)) break; @@ -6770,42 +9604,88 @@ #endif /* CONFIG_P2P */ +struct wdev_info { + u64 wdev_id; + int wdev_id_set; + u8 macaddr[ETH_ALEN]; +}; + +static int nl80211_wdev_handler(struct nl_msg *msg, void *arg) +{ + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + struct wdev_info *wi = arg; + + nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + if (tb[NL80211_ATTR_WDEV]) { + wi->wdev_id = nla_get_u64(tb[NL80211_ATTR_WDEV]); + wi->wdev_id_set = 1; + } + + if (tb[NL80211_ATTR_MAC]) + os_memcpy(wi->macaddr, nla_data(tb[NL80211_ATTR_MAC]), + ETH_ALEN); + + return NL_SKIP; +} + + static int wpa_driver_nl80211_if_add(void *priv, enum wpa_driver_if_type type, const char *ifname, const u8 *addr, void *bss_ctx, void **drv_priv, char *force_ifname, u8 *if_addr, - const char *bridge) + const char *bridge, int use_existing) { + enum nl80211_iftype nlmode; struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; int ifidx; -#ifdef HOSTAPD - struct i802_bss *new_bss = NULL; - - if (type == WPA_IF_AP_BSS) { - new_bss = os_zalloc(sizeof(*new_bss)); - if (new_bss == NULL) - return -1; - } -#endif /* HOSTAPD */ + int added = 1; if (addr) os_memcpy(if_addr, addr, ETH_ALEN); - ifidx = nl80211_create_iface(drv, ifname, - wpa_driver_nl80211_if_type(type), addr, - 0); - if (ifidx < 0) { -#ifdef HOSTAPD - os_free(new_bss); -#endif /* HOSTAPD */ - return -1; - } + nlmode = wpa_driver_nl80211_if_type(type); + if (nlmode == NL80211_IFTYPE_P2P_DEVICE) { + struct wdev_info p2pdev_info; + + os_memset(&p2pdev_info, 0, sizeof(p2pdev_info)); + ifidx = nl80211_create_iface(drv, ifname, nlmode, addr, + 0, nl80211_wdev_handler, + &p2pdev_info, use_existing); + if (!p2pdev_info.wdev_id_set || ifidx != 0) { + wpa_printf(MSG_ERROR, "nl80211: Failed to create a P2P Device interface %s", + ifname); + return -1; + } - if (!addr && - linux_get_ifhwaddr(drv->global->ioctl_sock, bss->ifname, - if_addr) < 0) { - nl80211_remove_iface(drv, ifidx); - return -1; + drv->global->if_add_wdevid = p2pdev_info.wdev_id; + drv->global->if_add_wdevid_set = p2pdev_info.wdev_id_set; + if (!is_zero_ether_addr(p2pdev_info.macaddr)) + os_memcpy(if_addr, p2pdev_info.macaddr, ETH_ALEN); + wpa_printf(MSG_DEBUG, "nl80211: New P2P Device interface %s (0x%llx) created", + ifname, + (long long unsigned int) p2pdev_info.wdev_id); + } else { + ifidx = nl80211_create_iface(drv, ifname, nlmode, addr, + 0, NULL, NULL, use_existing); + if (use_existing && ifidx == -ENFILE) { + added = 0; + ifidx = if_nametoindex(ifname); + } else if (ifidx < 0) { + return -1; + } + } + + if (!addr) { + if (drv->nlmode == NL80211_IFTYPE_P2P_DEVICE) + os_memcpy(if_addr, bss->addr, ETH_ALEN); + else if (linux_get_ifhwaddr(drv->global->ioctl_sock, + bss->ifname, if_addr) < 0) { + if (added) + nl80211_remove_iface(drv, ifidx); + return -1; + } } #ifdef CONFIG_P2P @@ -6813,16 +9693,14 @@ (type == WPA_IF_P2P_CLIENT || type == WPA_IF_P2P_GROUP || type == WPA_IF_P2P_GO)) { /* Enforce unique P2P Interface Address */ - u8 new_addr[ETH_ALEN], own_addr[ETH_ALEN]; + u8 new_addr[ETH_ALEN]; - if (linux_get_ifhwaddr(drv->global->ioctl_sock, bss->ifname, - own_addr) < 0 || - linux_get_ifhwaddr(drv->global->ioctl_sock, ifname, + if (linux_get_ifhwaddr(drv->global->ioctl_sock, ifname, new_addr) < 0) { nl80211_remove_iface(drv, ifidx); return -1; } - if (os_memcmp(own_addr, new_addr, ETH_ALEN) == 0) { + if (nl80211_addr_in_use(drv->global, new_addr)) { wpa_printf(MSG_DEBUG, "nl80211: Allocate new address " "for P2P group interface"); if (nl80211_p2p_interface_addr(drv, new_addr) < 0) { @@ -6839,17 +9717,25 @@ } #endif /* CONFIG_P2P */ -#ifdef HOSTAPD - if (bridge && - i802_check_bridge(drv, new_bss, bridge, ifname) < 0) { - wpa_printf(MSG_ERROR, "nl80211: Failed to add the new " - "interface %s to a bridge %s", ifname, bridge); - nl80211_remove_iface(drv, ifidx); - os_free(new_bss); - return -1; - } - if (type == WPA_IF_AP_BSS) { + struct i802_bss *new_bss = os_zalloc(sizeof(*new_bss)); + if (new_bss == NULL) { + if (added) + nl80211_remove_iface(drv, ifidx); + return -1; + } + + if (bridge && + i802_check_bridge(drv, new_bss, bridge, ifname) < 0) { + wpa_printf(MSG_ERROR, "nl80211: Failed to add the new " + "interface %s to a bridge %s", + ifname, bridge); + if (added) + nl80211_remove_iface(drv, ifidx); + os_free(new_bss); + return -1; + } + if (linux_set_iface_flags(drv->global->ioctl_sock, ifname, 1)) { nl80211_remove_iface(drv, ifidx); @@ -6857,14 +9743,22 @@ return -1; } os_strlcpy(new_bss->ifname, ifname, IFNAMSIZ); + os_memcpy(new_bss->addr, if_addr, ETH_ALEN); new_bss->ifindex = ifidx; new_bss->drv = drv; - new_bss->next = drv->first_bss.next; - drv->first_bss.next = new_bss; + new_bss->next = drv->first_bss->next; + new_bss->freq = drv->first_bss->freq; + new_bss->ctx = bss_ctx; + new_bss->added_if = added; + drv->first_bss->next = new_bss; if (drv_priv) *drv_priv = new_bss; + nl80211_init_bss(new_bss); + + /* Subscribe management frames for this WPA_IF_AP_BSS */ + if (nl80211_setup_ap(new_bss)) + return -1; } -#endif /* HOSTAPD */ if (drv->global) drv->global->if_add_ifindex = ifidx; @@ -6873,20 +9767,21 @@ } -static int wpa_driver_nl80211_if_remove(void *priv, +static int wpa_driver_nl80211_if_remove(struct i802_bss *bss, enum wpa_driver_if_type type, const char *ifname) { - struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; int ifindex = if_nametoindex(ifname); - wpa_printf(MSG_DEBUG, "nl80211: %s(type=%d ifname=%s) ifindex=%d", - __func__, type, ifname, ifindex); - if (ifindex <= 0) - return -1; + wpa_printf(MSG_DEBUG, "nl80211: %s(type=%d ifname=%s) ifindex=%d added_if=%d", + __func__, type, ifname, ifindex, bss->added_if); + if (ifindex > 0 && (bss->added_if || bss->ifindex != ifindex)) + nl80211_remove_iface(drv, ifindex); + + if (type != WPA_IF_AP_BSS) + return 0; -#ifdef HOSTAPD if (bss->added_if_into_bridge) { if (linux_br_del_if(drv->global->ioctl_sock, bss->brname, bss->ifname) < 0) @@ -6900,20 +9795,17 @@ "bridge %s: %s", bss->brname, strerror(errno)); } -#endif /* HOSTAPD */ - - nl80211_remove_iface(drv, ifindex); - -#ifdef HOSTAPD - if (type != WPA_IF_AP_BSS) - return 0; - if (bss != &drv->first_bss) { + if (bss != drv->first_bss) { struct i802_bss *tbss; - for (tbss = &drv->first_bss; tbss; tbss = tbss->next) { + wpa_printf(MSG_DEBUG, "nl80211: Not the first BSS - remove it"); + for (tbss = drv->first_bss; tbss; tbss = tbss->next) { if (tbss->next == bss) { tbss->next = bss->next; + /* Unsubscribe management frames */ + nl80211_teardown_ap(bss); + nl80211_destroy_bss(bss); os_free(bss); bss = NULL; break; @@ -6922,826 +9814,1926 @@ if (bss) wpa_printf(MSG_INFO, "nl80211: %s - could not find " "BSS %p in the list", __func__, bss); + } else { + wpa_printf(MSG_DEBUG, "nl80211: First BSS - reassign context"); + nl80211_teardown_ap(bss); + if (!bss->added_if && !drv->first_bss->next) + wpa_driver_nl80211_del_beacon(drv); + nl80211_destroy_bss(bss); + if (!bss->added_if) + i802_set_iface_flags(bss, 0); + if (drv->first_bss->next) { + drv->first_bss = drv->first_bss->next; + drv->ctx = drv->first_bss->ctx; + os_free(bss); + } else { + wpa_printf(MSG_DEBUG, "nl80211: No second BSS to reassign context to"); + } + } + + return 0; +} + + +static int cookie_handler(struct nl_msg *msg, void *arg) +{ + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + u64 *cookie = arg; + nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + if (tb[NL80211_ATTR_COOKIE]) + *cookie = nla_get_u64(tb[NL80211_ATTR_COOKIE]); + return NL_SKIP; +} + + +static int nl80211_send_frame_cmd(struct i802_bss *bss, + unsigned int freq, unsigned int wait, + const u8 *buf, size_t buf_len, + u64 *cookie_out, int no_cck, int no_ack, + int offchanok) +{ + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + u64 cookie; + int ret = -1; + + msg = nlmsg_alloc(); + if (!msg) + return -1; + + wpa_printf(MSG_MSGDUMP, "nl80211: CMD_FRAME freq=%u wait=%u no_cck=%d " + "no_ack=%d offchanok=%d", + freq, wait, no_cck, no_ack, offchanok); + wpa_hexdump(MSG_MSGDUMP, "CMD_FRAME", buf, buf_len); + nl80211_cmd(drv, msg, 0, NL80211_CMD_FRAME); + + if (nl80211_set_iface_id(msg, bss) < 0) + goto nla_put_failure; + if (freq) + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq); + if (wait) + NLA_PUT_U32(msg, NL80211_ATTR_DURATION, wait); + if (offchanok && (drv->capa.flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX)) + NLA_PUT_FLAG(msg, NL80211_ATTR_OFFCHANNEL_TX_OK); + if (no_cck) + NLA_PUT_FLAG(msg, NL80211_ATTR_TX_NO_CCK_RATE); + if (no_ack) + NLA_PUT_FLAG(msg, NL80211_ATTR_DONT_WAIT_FOR_ACK); + + NLA_PUT(msg, NL80211_ATTR_FRAME, buf_len, buf); + + cookie = 0; + ret = send_and_recv_msgs(drv, msg, cookie_handler, &cookie); + msg = NULL; + if (ret) { + wpa_printf(MSG_DEBUG, "nl80211: Frame command failed: ret=%d " + "(%s) (freq=%u wait=%u)", ret, strerror(-ret), + freq, wait); + goto nla_put_failure; + } + wpa_printf(MSG_MSGDUMP, "nl80211: Frame TX command accepted%s; " + "cookie 0x%llx", no_ack ? " (no ACK)" : "", + (long long unsigned int) cookie); + + if (cookie_out) + *cookie_out = no_ack ? (u64) -1 : cookie; + +nla_put_failure: + nlmsg_free(msg); + return ret; +} + + +static int wpa_driver_nl80211_send_action(struct i802_bss *bss, + unsigned int freq, + unsigned int wait_time, + const u8 *dst, const u8 *src, + const u8 *bssid, + const u8 *data, size_t data_len, + int no_cck) +{ + struct wpa_driver_nl80211_data *drv = bss->drv; + int ret = -1; + u8 *buf; + struct ieee80211_hdr *hdr; + + wpa_printf(MSG_DEBUG, "nl80211: Send Action frame (ifindex=%d, " + "freq=%u MHz wait=%d ms no_cck=%d)", + drv->ifindex, freq, wait_time, no_cck); + + buf = os_zalloc(24 + data_len); + if (buf == NULL) + return ret; + os_memcpy(buf + 24, data, data_len); + hdr = (struct ieee80211_hdr *) buf; + hdr->frame_control = + IEEE80211_FC(WLAN_FC_TYPE_MGMT, WLAN_FC_STYPE_ACTION); + os_memcpy(hdr->addr1, dst, ETH_ALEN); + os_memcpy(hdr->addr2, src, ETH_ALEN); + os_memcpy(hdr->addr3, bssid, ETH_ALEN); + + if (is_ap_interface(drv->nlmode) && + (!(drv->capa.flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX) || + (int) freq == bss->freq || drv->device_ap_sme || + !drv->use_monitor)) + ret = wpa_driver_nl80211_send_mlme(bss, buf, 24 + data_len, + 0, freq, no_cck, 1, + wait_time); + else + ret = nl80211_send_frame_cmd(bss, freq, wait_time, buf, + 24 + data_len, + &drv->send_action_cookie, + no_cck, 0, 1); + + os_free(buf); + return ret; +} + + +static void wpa_driver_nl80211_send_action_cancel_wait(void *priv) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + int ret; + + msg = nlmsg_alloc(); + if (!msg) + return; + + wpa_printf(MSG_DEBUG, "nl80211: Cancel TX frame wait: cookie=0x%llx", + (long long unsigned int) drv->send_action_cookie); + nl80211_cmd(drv, msg, 0, NL80211_CMD_FRAME_WAIT_CANCEL); + + if (nl80211_set_iface_id(msg, bss) < 0) + goto nla_put_failure; + NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, drv->send_action_cookie); + + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + msg = NULL; + if (ret) + wpa_printf(MSG_DEBUG, "nl80211: wait cancel failed: ret=%d " + "(%s)", ret, strerror(-ret)); + + nla_put_failure: + nlmsg_free(msg); +} + + +static int wpa_driver_nl80211_remain_on_channel(void *priv, unsigned int freq, + unsigned int duration) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + int ret; + u64 cookie; + + msg = nlmsg_alloc(); + if (!msg) + return -1; + + nl80211_cmd(drv, msg, 0, NL80211_CMD_REMAIN_ON_CHANNEL); + + if (nl80211_set_iface_id(msg, bss) < 0) + goto nla_put_failure; + + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq); + NLA_PUT_U32(msg, NL80211_ATTR_DURATION, duration); + + cookie = 0; + ret = send_and_recv_msgs(drv, msg, cookie_handler, &cookie); + msg = NULL; + if (ret == 0) { + wpa_printf(MSG_DEBUG, "nl80211: Remain-on-channel cookie " + "0x%llx for freq=%u MHz duration=%u", + (long long unsigned int) cookie, freq, duration); + drv->remain_on_chan_cookie = cookie; + drv->pending_remain_on_chan = 1; + return 0; + } + wpa_printf(MSG_DEBUG, "nl80211: Failed to request remain-on-channel " + "(freq=%d duration=%u): %d (%s)", + freq, duration, ret, strerror(-ret)); +nla_put_failure: + nlmsg_free(msg); + return -1; +} + + +static int wpa_driver_nl80211_cancel_remain_on_channel(void *priv) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + int ret; + + if (!drv->pending_remain_on_chan) { + wpa_printf(MSG_DEBUG, "nl80211: No pending remain-on-channel " + "to cancel"); + return -1; + } + + wpa_printf(MSG_DEBUG, "nl80211: Cancel remain-on-channel with cookie " + "0x%llx", + (long long unsigned int) drv->remain_on_chan_cookie); + + msg = nlmsg_alloc(); + if (!msg) + return -1; + + nl80211_cmd(drv, msg, 0, NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL); + + if (nl80211_set_iface_id(msg, bss) < 0) + goto nla_put_failure; + + NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, drv->remain_on_chan_cookie); + + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + msg = NULL; + if (ret == 0) + return 0; + wpa_printf(MSG_DEBUG, "nl80211: Failed to cancel remain-on-channel: " + "%d (%s)", ret, strerror(-ret)); +nla_put_failure: + nlmsg_free(msg); + return -1; +} + + +static int wpa_driver_nl80211_probe_req_report(struct i802_bss *bss, int report) +{ + struct wpa_driver_nl80211_data *drv = bss->drv; + + if (!report) { + if (bss->nl_preq && drv->device_ap_sme && + is_ap_interface(drv->nlmode)) { + /* + * Do not disable Probe Request reporting that was + * enabled in nl80211_setup_ap(). + */ + wpa_printf(MSG_DEBUG, "nl80211: Skip disabling of " + "Probe Request reporting nl_preq=%p while " + "in AP mode", bss->nl_preq); + } else if (bss->nl_preq) { + wpa_printf(MSG_DEBUG, "nl80211: Disable Probe Request " + "reporting nl_preq=%p", bss->nl_preq); + nl80211_destroy_eloop_handle(&bss->nl_preq); + } + return 0; + } + + if (bss->nl_preq) { + wpa_printf(MSG_DEBUG, "nl80211: Probe Request reporting " + "already on! nl_preq=%p", bss->nl_preq); + return 0; + } + + bss->nl_preq = nl_create_handle(drv->global->nl_cb, "preq"); + if (bss->nl_preq == NULL) + return -1; + wpa_printf(MSG_DEBUG, "nl80211: Enable Probe Request " + "reporting nl_preq=%p", bss->nl_preq); + + if (nl80211_register_frame(bss, bss->nl_preq, + (WLAN_FC_TYPE_MGMT << 2) | + (WLAN_FC_STYPE_PROBE_REQ << 4), + NULL, 0) < 0) + goto out_err; + + nl80211_register_eloop_read(&bss->nl_preq, + wpa_driver_nl80211_event_receive, + bss->nl_cb); + + return 0; + + out_err: + nl_destroy_handles(&bss->nl_preq); + return -1; +} + + +static int nl80211_disable_11b_rates(struct wpa_driver_nl80211_data *drv, + int ifindex, int disabled) +{ + struct nl_msg *msg; + struct nlattr *bands, *band; + int ret; + + msg = nlmsg_alloc(); + if (!msg) + return -1; + + nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_TX_BITRATE_MASK); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifindex); + + bands = nla_nest_start(msg, NL80211_ATTR_TX_RATES); + if (!bands) + goto nla_put_failure; + + /* + * Disable 2 GHz rates 1, 2, 5.5, 11 Mbps by masking out everything + * else apart from 6, 9, 12, 18, 24, 36, 48, 54 Mbps from non-MCS + * rates. All 5 GHz rates are left enabled. + */ + band = nla_nest_start(msg, NL80211_BAND_2GHZ); + if (!band) + goto nla_put_failure; + if (disabled) { + NLA_PUT(msg, NL80211_TXRATE_LEGACY, 8, + "\x0c\x12\x18\x24\x30\x48\x60\x6c"); + } + nla_nest_end(msg, band); + + nla_nest_end(msg, bands); + + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + msg = NULL; + if (ret) { + wpa_printf(MSG_DEBUG, "nl80211: Set TX rates failed: ret=%d " + "(%s)", ret, strerror(-ret)); + } else + drv->disabled_11b_rates = disabled; + + return ret; + +nla_put_failure: + nlmsg_free(msg); + return -1; +} + + +static int wpa_driver_nl80211_deinit_ap(void *priv) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + if (!is_ap_interface(drv->nlmode)) + return -1; + wpa_driver_nl80211_del_beacon(drv); + + /* + * If the P2P GO interface was dynamically added, then it is + * possible that the interface change to station is not possible. + */ + if (drv->nlmode == NL80211_IFTYPE_P2P_GO && bss->if_dynamic) + return 0; + + return wpa_driver_nl80211_set_mode(priv, NL80211_IFTYPE_STATION); +} + + +static int wpa_driver_nl80211_stop_ap(void *priv) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + if (!is_ap_interface(drv->nlmode)) + return -1; + wpa_driver_nl80211_del_beacon(drv); + bss->beacon_set = 0; + return 0; +} + + +static int wpa_driver_nl80211_deinit_p2p_cli(void *priv) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + if (drv->nlmode != NL80211_IFTYPE_P2P_CLIENT) + return -1; + + /* + * If the P2P Client interface was dynamically added, then it is + * possible that the interface change to station is not possible. + */ + if (bss->if_dynamic) + return 0; + + return wpa_driver_nl80211_set_mode(priv, NL80211_IFTYPE_STATION); +} + + +static void wpa_driver_nl80211_resume(void *priv) +{ + struct i802_bss *bss = priv; + + if (i802_set_iface_flags(bss, 1)) + wpa_printf(MSG_DEBUG, "nl80211: Failed to set interface up on resume event"); +} + + +static int nl80211_send_ft_action(void *priv, u8 action, const u8 *target_ap, + const u8 *ies, size_t ies_len) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + int ret; + u8 *data, *pos; + size_t data_len; + const u8 *own_addr = bss->addr; + + if (action != 1) { + wpa_printf(MSG_ERROR, "nl80211: Unsupported send_ft_action " + "action %d", action); + return -1; + } + + /* + * Action frame payload: + * Category[1] = 6 (Fast BSS Transition) + * Action[1] = 1 (Fast BSS Transition Request) + * STA Address + * Target AP Address + * FT IEs + */ + + data_len = 2 + 2 * ETH_ALEN + ies_len; + data = os_malloc(data_len); + if (data == NULL) + return -1; + pos = data; + *pos++ = 0x06; /* FT Action category */ + *pos++ = action; + os_memcpy(pos, own_addr, ETH_ALEN); + pos += ETH_ALEN; + os_memcpy(pos, target_ap, ETH_ALEN); + pos += ETH_ALEN; + os_memcpy(pos, ies, ies_len); + + ret = wpa_driver_nl80211_send_action(bss, drv->assoc_freq, 0, + drv->bssid, own_addr, drv->bssid, + data, data_len, 0); + os_free(data); + + return ret; +} + + +static int nl80211_signal_monitor(void *priv, int threshold, int hysteresis) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + struct nlattr *cqm; + int ret = -1; + + wpa_printf(MSG_DEBUG, "nl80211: Signal monitor threshold=%d " + "hysteresis=%d", threshold, hysteresis); + + msg = nlmsg_alloc(); + if (!msg) + return -1; + + nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_CQM); + + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, bss->ifindex); + + cqm = nla_nest_start(msg, NL80211_ATTR_CQM); + if (cqm == NULL) + goto nla_put_failure; + + NLA_PUT_U32(msg, NL80211_ATTR_CQM_RSSI_THOLD, threshold); + NLA_PUT_U32(msg, NL80211_ATTR_CQM_RSSI_HYST, hysteresis); + nla_nest_end(msg, cqm); + + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + msg = NULL; + +nla_put_failure: + nlmsg_free(msg); + return ret; +} + + +static int get_channel_width(struct nl_msg *msg, void *arg) +{ + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct wpa_signal_info *sig_change = arg; + + nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + + sig_change->center_frq1 = -1; + sig_change->center_frq2 = -1; + sig_change->chanwidth = CHAN_WIDTH_UNKNOWN; + + if (tb[NL80211_ATTR_CHANNEL_WIDTH]) { + sig_change->chanwidth = convert2width( + nla_get_u32(tb[NL80211_ATTR_CHANNEL_WIDTH])); + if (tb[NL80211_ATTR_CENTER_FREQ1]) + sig_change->center_frq1 = + nla_get_u32(tb[NL80211_ATTR_CENTER_FREQ1]); + if (tb[NL80211_ATTR_CENTER_FREQ2]) + sig_change->center_frq2 = + nla_get_u32(tb[NL80211_ATTR_CENTER_FREQ2]); + } + + return NL_SKIP; +} + + +static int nl80211_get_channel_width(struct wpa_driver_nl80211_data *drv, + struct wpa_signal_info *sig) +{ + struct nl_msg *msg; + + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + + nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_INTERFACE); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + + return send_and_recv_msgs(drv, msg, get_channel_width, sig); + +nla_put_failure: + nlmsg_free(msg); + return -ENOBUFS; +} + + +static int nl80211_signal_poll(void *priv, struct wpa_signal_info *si) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + int res; + + os_memset(si, 0, sizeof(*si)); + res = nl80211_get_link_signal(drv, si); + if (res != 0) + return res; + + res = nl80211_get_channel_width(drv, si); + if (res != 0) + return res; + + return nl80211_get_link_noise(drv, si); +} + + +static int wpa_driver_nl80211_shared_freq(void *priv) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct wpa_driver_nl80211_data *driver; + int freq = 0; + + /* + * If the same PHY is in connected state with some other interface, + * then retrieve the assoc freq. + */ + wpa_printf(MSG_DEBUG, "nl80211: Get shared freq for PHY %s", + drv->phyname); + + dl_list_for_each(driver, &drv->global->interfaces, + struct wpa_driver_nl80211_data, list) { + if (drv == driver || + os_strcmp(drv->phyname, driver->phyname) != 0 || + !driver->associated) + continue; + + wpa_printf(MSG_DEBUG, "nl80211: Found a match for PHY %s - %s " + MACSTR, + driver->phyname, driver->first_bss->ifname, + MAC2STR(driver->first_bss->addr)); + if (is_ap_interface(driver->nlmode)) + freq = driver->first_bss->freq; + else + freq = nl80211_get_assoc_freq(driver); + wpa_printf(MSG_DEBUG, "nl80211: Shared freq for PHY %s: %d", + drv->phyname, freq); + } + + if (!freq) + wpa_printf(MSG_DEBUG, "nl80211: No shared interface for " + "PHY (%s) in associated state", drv->phyname); + + return freq; +} + + +static int nl80211_send_frame(void *priv, const u8 *data, size_t data_len, + int encrypt) +{ + struct i802_bss *bss = priv; + return wpa_driver_nl80211_send_frame(bss, data, data_len, encrypt, 0, + 0, 0, 0, 0); +} + + +static int nl80211_set_param(void *priv, const char *param) +{ + wpa_printf(MSG_DEBUG, "nl80211: driver param='%s'", param); + if (param == NULL) + return 0; + +#ifdef CONFIG_P2P + if (os_strstr(param, "use_p2p_group_interface=1")) { + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + + wpa_printf(MSG_DEBUG, "nl80211: Use separate P2P group " + "interface"); + drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_CONCURRENT; + drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_MGMT_AND_NON_P2P; + } + + if (os_strstr(param, "p2p_device=1")) { + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + drv->allow_p2p_device = 1; + } +#endif /* CONFIG_P2P */ + + if (os_strstr(param, "use_monitor=1")) { + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + drv->use_monitor = 1; + } + + if (os_strstr(param, "force_connect_cmd=1")) { + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + drv->capa.flags &= ~WPA_DRIVER_FLAGS_SME; + } + + return 0; +} + + +static void * nl80211_global_init(void) +{ + struct nl80211_global *global; + struct netlink_config *cfg; + + global = os_zalloc(sizeof(*global)); + if (global == NULL) + return NULL; + global->ioctl_sock = -1; + dl_list_init(&global->interfaces); + global->if_add_ifindex = -1; + + cfg = os_zalloc(sizeof(*cfg)); + if (cfg == NULL) + goto err; + + cfg->ctx = global; + cfg->newlink_cb = wpa_driver_nl80211_event_rtm_newlink; + cfg->dellink_cb = wpa_driver_nl80211_event_rtm_dellink; + global->netlink = netlink_init(cfg); + if (global->netlink == NULL) { + os_free(cfg); + goto err; + } + + if (wpa_driver_nl80211_init_nl_global(global) < 0) + goto err; + + global->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0); + if (global->ioctl_sock < 0) { + wpa_printf(MSG_ERROR, "nl80211: socket(PF_INET,SOCK_DGRAM) failed: %s", + strerror(errno)); + goto err; + } + + return global; + +err: + nl80211_global_deinit(global); + return NULL; +} + + +static void nl80211_global_deinit(void *priv) +{ + struct nl80211_global *global = priv; + if (global == NULL) + return; + if (!dl_list_empty(&global->interfaces)) { + wpa_printf(MSG_ERROR, "nl80211: %u interface(s) remain at " + "nl80211_global_deinit", + dl_list_len(&global->interfaces)); + } + + if (global->netlink) + netlink_deinit(global->netlink); + + nl_destroy_handles(&global->nl); + + if (global->nl_event) + nl80211_destroy_eloop_handle(&global->nl_event); + + nl_cb_put(global->nl_cb); + + if (global->ioctl_sock >= 0) + close(global->ioctl_sock); + + os_free(global); +} + + +static const char * nl80211_get_radio_name(void *priv) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + return drv->phyname; +} + + +static int nl80211_pmkid(struct i802_bss *bss, int cmd, const u8 *bssid, + const u8 *pmkid) +{ + struct nl_msg *msg; + + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + + nl80211_cmd(bss->drv, msg, 0, cmd); + + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(bss->ifname)); + if (pmkid) + NLA_PUT(msg, NL80211_ATTR_PMKID, 16, pmkid); + if (bssid) + NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid); + + return send_and_recv_msgs(bss->drv, msg, NULL, NULL); + nla_put_failure: + nlmsg_free(msg); + return -ENOBUFS; +} + + +static int nl80211_add_pmkid(void *priv, const u8 *bssid, const u8 *pmkid) +{ + struct i802_bss *bss = priv; + wpa_printf(MSG_DEBUG, "nl80211: Add PMKID for " MACSTR, MAC2STR(bssid)); + return nl80211_pmkid(bss, NL80211_CMD_SET_PMKSA, bssid, pmkid); +} + + +static int nl80211_remove_pmkid(void *priv, const u8 *bssid, const u8 *pmkid) +{ + struct i802_bss *bss = priv; + wpa_printf(MSG_DEBUG, "nl80211: Delete PMKID for " MACSTR, + MAC2STR(bssid)); + return nl80211_pmkid(bss, NL80211_CMD_DEL_PMKSA, bssid, pmkid); +} + + +static int nl80211_flush_pmkid(void *priv) +{ + struct i802_bss *bss = priv; + wpa_printf(MSG_DEBUG, "nl80211: Flush PMKIDs"); + return nl80211_pmkid(bss, NL80211_CMD_FLUSH_PMKSA, NULL, NULL); +} + + +static void clean_survey_results(struct survey_results *survey_results) +{ + struct freq_survey *survey, *tmp; + + if (dl_list_empty(&survey_results->survey_list)) + return; + + dl_list_for_each_safe(survey, tmp, &survey_results->survey_list, + struct freq_survey, list) { + dl_list_del(&survey->list); + os_free(survey); + } +} + + +static void add_survey(struct nlattr **sinfo, u32 ifidx, + struct dl_list *survey_list) +{ + struct freq_survey *survey; + + survey = os_zalloc(sizeof(struct freq_survey)); + if (!survey) + return; + + survey->ifidx = ifidx; + survey->freq = nla_get_u32(sinfo[NL80211_SURVEY_INFO_FREQUENCY]); + survey->filled = 0; + + if (sinfo[NL80211_SURVEY_INFO_NOISE]) { + survey->nf = (int8_t) + nla_get_u8(sinfo[NL80211_SURVEY_INFO_NOISE]); + survey->filled |= SURVEY_HAS_NF; + } + + if (sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME]) { + survey->channel_time = + nla_get_u64(sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME]); + survey->filled |= SURVEY_HAS_CHAN_TIME; } -#endif /* HOSTAPD */ - return 0; + if (sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME_BUSY]) { + survey->channel_time_busy = + nla_get_u64(sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME_BUSY]); + survey->filled |= SURVEY_HAS_CHAN_TIME_BUSY; + } + + if (sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME_RX]) { + survey->channel_time_rx = + nla_get_u64(sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME_RX]); + survey->filled |= SURVEY_HAS_CHAN_TIME_RX; + } + + if (sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME_TX]) { + survey->channel_time_tx = + nla_get_u64(sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME_TX]); + survey->filled |= SURVEY_HAS_CHAN_TIME_TX; + } + + wpa_printf(MSG_DEBUG, "nl80211: Freq survey dump event (freq=%d MHz noise=%d channel_time=%ld busy_time=%ld tx_time=%ld rx_time=%ld filled=%04x)", + survey->freq, + survey->nf, + (unsigned long int) survey->channel_time, + (unsigned long int) survey->channel_time_busy, + (unsigned long int) survey->channel_time_tx, + (unsigned long int) survey->channel_time_rx, + survey->filled); + + dl_list_add_tail(survey_list, &survey->list); } -static int cookie_handler(struct nl_msg *msg, void *arg) +static int check_survey_ok(struct nlattr **sinfo, u32 surveyed_freq, + unsigned int freq_filter) { - struct nlattr *tb[NL80211_ATTR_MAX + 1]; - struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); - u64 *cookie = arg; - nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), - genlmsg_attrlen(gnlh, 0), NULL); - if (tb[NL80211_ATTR_COOKIE]) - *cookie = nla_get_u64(tb[NL80211_ATTR_COOKIE]); - return NL_SKIP; + if (!freq_filter) + return 1; + + return freq_filter == surveyed_freq; } -static int nl80211_send_frame_cmd(struct wpa_driver_nl80211_data *drv, - unsigned int freq, unsigned int wait, - const u8 *buf, size_t buf_len, - u64 *cookie_out, int no_cck) +static int survey_handler(struct nl_msg *msg, void *arg) { - struct nl_msg *msg; - u64 cookie; - int ret = -1; + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct nlattr *sinfo[NL80211_SURVEY_INFO_MAX + 1]; + struct survey_results *survey_results; + u32 surveyed_freq = 0; + u32 ifidx; - msg = nlmsg_alloc(); - if (!msg) - return -1; + static struct nla_policy survey_policy[NL80211_SURVEY_INFO_MAX + 1] = { + [NL80211_SURVEY_INFO_FREQUENCY] = { .type = NLA_U32 }, + [NL80211_SURVEY_INFO_NOISE] = { .type = NLA_U8 }, + }; - nl80211_cmd(drv, msg, 0, NL80211_CMD_FRAME); + survey_results = (struct survey_results *) arg; - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); - NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq); - if (wait) - NLA_PUT_U32(msg, NL80211_ATTR_DURATION, wait); - NLA_PUT_FLAG(msg, NL80211_ATTR_OFFCHANNEL_TX_OK); - if (no_cck) - NLA_PUT_FLAG(msg, NL80211_ATTR_TX_NO_CCK_RATE); + nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); - NLA_PUT(msg, NL80211_ATTR_FRAME, buf_len, buf); + if (!tb[NL80211_ATTR_IFINDEX]) + return NL_SKIP; - cookie = 0; - ret = send_and_recv_msgs(drv, msg, cookie_handler, &cookie); - msg = NULL; - if (ret) { - wpa_printf(MSG_DEBUG, "nl80211: Frame command failed: ret=%d " - "(%s) (freq=%u wait=%u)", ret, strerror(-ret), - freq, wait); - goto nla_put_failure; - } - wpa_printf(MSG_DEBUG, "nl80211: Frame TX command accepted; " - "cookie 0x%llx", (long long unsigned int) cookie); + ifidx = nla_get_u32(tb[NL80211_ATTR_IFINDEX]); - if (cookie_out) - *cookie_out = cookie; + if (!tb[NL80211_ATTR_SURVEY_INFO]) + return NL_SKIP; -nla_put_failure: - nlmsg_free(msg); - return ret; -} + if (nla_parse_nested(sinfo, NL80211_SURVEY_INFO_MAX, + tb[NL80211_ATTR_SURVEY_INFO], + survey_policy)) + return NL_SKIP; + if (!sinfo[NL80211_SURVEY_INFO_FREQUENCY]) { + wpa_printf(MSG_ERROR, "nl80211: Invalid survey data"); + return NL_SKIP; + } -static int wpa_driver_nl80211_send_action(void *priv, unsigned int freq, - unsigned int wait_time, - const u8 *dst, const u8 *src, - const u8 *bssid, - const u8 *data, size_t data_len, - int no_cck) -{ - struct i802_bss *bss = priv; - struct wpa_driver_nl80211_data *drv = bss->drv; - int ret = -1; - u8 *buf; - struct ieee80211_hdr *hdr; + surveyed_freq = nla_get_u32(sinfo[NL80211_SURVEY_INFO_FREQUENCY]); - wpa_printf(MSG_DEBUG, "nl80211: Send Action frame (ifindex=%d, " - "wait=%d ms no_cck=%d)", drv->ifindex, wait_time, no_cck); + if (!check_survey_ok(sinfo, surveyed_freq, + survey_results->freq_filter)) + return NL_SKIP; - buf = os_zalloc(24 + data_len); - if (buf == NULL) - return ret; - os_memcpy(buf + 24, data, data_len); - hdr = (struct ieee80211_hdr *) buf; - hdr->frame_control = - IEEE80211_FC(WLAN_FC_TYPE_MGMT, WLAN_FC_STYPE_ACTION); - os_memcpy(hdr->addr1, dst, ETH_ALEN); - os_memcpy(hdr->addr2, src, ETH_ALEN); - os_memcpy(hdr->addr3, bssid, ETH_ALEN); + if (survey_results->freq_filter && + survey_results->freq_filter != surveyed_freq) { + wpa_printf(MSG_EXCESSIVE, "nl80211: Ignoring survey data for freq %d MHz", + surveyed_freq); + return NL_SKIP; + } - if (is_ap_interface(drv->nlmode)) - ret = wpa_driver_nl80211_send_mlme(priv, buf, 24 + data_len); - else - ret = nl80211_send_frame_cmd(drv, freq, wait_time, buf, - 24 + data_len, - &drv->send_action_cookie, - no_cck); + add_survey(sinfo, ifidx, &survey_results->survey_list); - os_free(buf); - return ret; + return NL_SKIP; } -static void wpa_driver_nl80211_send_action_cancel_wait(void *priv) +static int wpa_driver_nl80211_get_survey(void *priv, unsigned int freq) { struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; - int ret; + int err = -ENOBUFS; + union wpa_event_data data; + struct survey_results *survey_results; + + os_memset(&data, 0, sizeof(data)); + survey_results = &data.survey_results; + + dl_list_init(&survey_results->survey_list); msg = nlmsg_alloc(); if (!msg) - return; + goto nla_put_failure; - nl80211_cmd(drv, msg, 0, NL80211_CMD_FRAME_WAIT_CANCEL); + nl80211_cmd(drv, msg, NLM_F_DUMP, NL80211_CMD_GET_SURVEY); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); - NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, drv->send_action_cookie); - ret = send_and_recv_msgs(drv, msg, NULL, NULL); - msg = NULL; - if (ret) - wpa_printf(MSG_DEBUG, "nl80211: wait cancel failed: ret=%d " - "(%s)", ret, strerror(-ret)); + if (freq) + data.survey_results.freq_filter = freq; - nla_put_failure: - nlmsg_free(msg); + do { + wpa_printf(MSG_DEBUG, "nl80211: Fetch survey data"); + err = send_and_recv_msgs(drv, msg, survey_handler, + survey_results); + } while (err > 0); + + if (err) { + wpa_printf(MSG_ERROR, "nl80211: Failed to process survey data"); + goto out_clean; + } + + wpa_supplicant_event(drv->ctx, EVENT_SURVEY, &data); + +out_clean: + clean_survey_results(survey_results); +nla_put_failure: + return err; } -static int wpa_driver_nl80211_remain_on_channel(void *priv, unsigned int freq, - unsigned int duration) +static void nl80211_set_rekey_info(void *priv, const u8 *kek, const u8 *kck, + const u8 *replay_ctr) { struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; + struct nlattr *replay_nested; struct nl_msg *msg; - int ret; - u64 cookie; msg = nlmsg_alloc(); if (!msg) - return -1; + return; - nl80211_cmd(drv, msg, 0, NL80211_CMD_REMAIN_ON_CHANNEL); + nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_REKEY_OFFLOAD); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); - NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq); - NLA_PUT_U32(msg, NL80211_ATTR_DURATION, duration); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, bss->ifindex); - cookie = 0; - ret = send_and_recv_msgs(drv, msg, cookie_handler, &cookie); - msg = NULL; - if (ret == 0) { - wpa_printf(MSG_DEBUG, "nl80211: Remain-on-channel cookie " - "0x%llx for freq=%u MHz duration=%u", - (long long unsigned int) cookie, freq, duration); - drv->remain_on_chan_cookie = cookie; - drv->pending_remain_on_chan = 1; - return 0; - } - wpa_printf(MSG_DEBUG, "nl80211: Failed to request remain-on-channel " - "(freq=%d duration=%u): %d (%s)", - freq, duration, ret, strerror(-ret)); -nla_put_failure: + replay_nested = nla_nest_start(msg, NL80211_ATTR_REKEY_DATA); + if (!replay_nested) + goto nla_put_failure; + + NLA_PUT(msg, NL80211_REKEY_DATA_KEK, NL80211_KEK_LEN, kek); + NLA_PUT(msg, NL80211_REKEY_DATA_KCK, NL80211_KCK_LEN, kck); + NLA_PUT(msg, NL80211_REKEY_DATA_REPLAY_CTR, NL80211_REPLAY_CTR_LEN, + replay_ctr); + + nla_nest_end(msg, replay_nested); + + send_and_recv_msgs(drv, msg, NULL, NULL); + return; + nla_put_failure: nlmsg_free(msg); - return -1; } -static int wpa_driver_nl80211_cancel_remain_on_channel(void *priv) +static void nl80211_send_null_frame(struct i802_bss *bss, const u8 *own_addr, + const u8 *addr, int qos) { - struct i802_bss *bss = priv; - struct wpa_driver_nl80211_data *drv = bss->drv; - struct nl_msg *msg; - int ret; - - if (!drv->pending_remain_on_chan) { - wpa_printf(MSG_DEBUG, "nl80211: No pending remain-on-channel " - "to cancel"); - return -1; - } + /* send data frame to poll STA and check whether + * this frame is ACKed */ + struct { + struct ieee80211_hdr hdr; + u16 qos_ctl; + } STRUCT_PACKED nulldata; + size_t size; - wpa_printf(MSG_DEBUG, "nl80211: Cancel remain-on-channel with cookie " - "0x%llx", - (long long unsigned int) drv->remain_on_chan_cookie); + /* Send data frame to poll STA and check whether this frame is ACKed */ - msg = nlmsg_alloc(); - if (!msg) - return -1; + os_memset(&nulldata, 0, sizeof(nulldata)); - nl80211_cmd(drv, msg, 0, NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL); + if (qos) { + nulldata.hdr.frame_control = + IEEE80211_FC(WLAN_FC_TYPE_DATA, + WLAN_FC_STYPE_QOS_NULL); + size = sizeof(nulldata); + } else { + nulldata.hdr.frame_control = + IEEE80211_FC(WLAN_FC_TYPE_DATA, + WLAN_FC_STYPE_NULLFUNC); + size = sizeof(struct ieee80211_hdr); + } - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); - NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, drv->remain_on_chan_cookie); + nulldata.hdr.frame_control |= host_to_le16(WLAN_FC_FROMDS); + os_memcpy(nulldata.hdr.IEEE80211_DA_FROMDS, addr, ETH_ALEN); + os_memcpy(nulldata.hdr.IEEE80211_BSSID_FROMDS, own_addr, ETH_ALEN); + os_memcpy(nulldata.hdr.IEEE80211_SA_FROMDS, own_addr, ETH_ALEN); - ret = send_and_recv_msgs(drv, msg, NULL, NULL); - msg = NULL; - if (ret == 0) - return 0; - wpa_printf(MSG_DEBUG, "nl80211: Failed to cancel remain-on-channel: " - "%d (%s)", ret, strerror(-ret)); -nla_put_failure: - nlmsg_free(msg); - return -1; + if (wpa_driver_nl80211_send_mlme(bss, (u8 *) &nulldata, size, 0, 0, 0, + 0, 0) < 0) + wpa_printf(MSG_DEBUG, "nl80211_send_null_frame: Failed to " + "send poll frame"); } - -static int wpa_driver_nl80211_probe_req_report(void *priv, int report) +static void nl80211_poll_client(void *priv, const u8 *own_addr, const u8 *addr, + int qos) { struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; - if (!report) { - if (drv->nl_preq.handle && drv->no_monitor_iface_capab && - is_ap_interface(drv->nlmode)) { - /* - * Do not disable Probe Request reporting that was - * enabled in nl80211_setup_ap(). - */ - wpa_printf(MSG_DEBUG, "nl80211: Skip disabling of " - "Probe Request reporting nl_preq=%p while " - "in AP mode", drv->nl_preq.handle); - } else if (drv->nl_preq.handle) { - wpa_printf(MSG_DEBUG, "nl80211: Disable Probe Request " - "reporting nl_preq=%p", drv->nl_preq.handle); - eloop_unregister_read_sock( - nl_socket_get_fd(drv->nl_preq.handle)); - nl_destroy_handles(&drv->nl_preq); - } - return 0; - } - - if (drv->nl_preq.handle) { - wpa_printf(MSG_DEBUG, "nl80211: Probe Request reporting " - "already on!"); - return 0; + if (!drv->poll_command_supported) { + nl80211_send_null_frame(bss, own_addr, addr, qos); + return; } - if (nl_create_handles(&drv->nl_preq, drv->global->nl_cb, "preq")) - return -1; - - if (nl80211_register_frame(drv, drv->nl_preq.handle, - (WLAN_FC_TYPE_MGMT << 2) | - (WLAN_FC_STYPE_PROBE_REQ << 4), - NULL, 0) < 0) - goto out_err; + msg = nlmsg_alloc(); + if (!msg) + return; - eloop_register_read_sock(nl_socket_get_fd(drv->nl_preq.handle), - wpa_driver_nl80211_event_receive, drv, - drv->nl_preq.handle); + nl80211_cmd(drv, msg, 0, NL80211_CMD_PROBE_CLIENT); - return 0; + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, bss->ifindex); + NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr); - out_err: - nl_destroy_handles(&drv->nl_preq); - return -1; + send_and_recv_msgs(drv, msg, NULL, NULL); + return; + nla_put_failure: + nlmsg_free(msg); } -static int nl80211_disable_11b_rates(struct wpa_driver_nl80211_data *drv, - int ifindex, int disabled) +static int nl80211_set_power_save(struct i802_bss *bss, int enabled) { struct nl_msg *msg; - struct nlattr *bands, *band; - int ret; msg = nlmsg_alloc(); if (!msg) - return -1; + return -ENOMEM; - nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_TX_BITRATE_MASK); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifindex); + nl80211_cmd(bss->drv, msg, 0, NL80211_CMD_SET_POWER_SAVE); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, bss->ifindex); + NLA_PUT_U32(msg, NL80211_ATTR_PS_STATE, + enabled ? NL80211_PS_ENABLED : NL80211_PS_DISABLED); + return send_and_recv_msgs(bss->drv, msg, NULL, NULL); +nla_put_failure: + nlmsg_free(msg); + return -ENOBUFS; +} - bands = nla_nest_start(msg, NL80211_ATTR_TX_RATES); - if (!bands) - goto nla_put_failure; - /* - * Disable 2 GHz rates 1, 2, 5.5, 11 Mbps by masking out everything - * else apart from 6, 9, 12, 18, 24, 36, 48, 54 Mbps from non-MCS - * rates. All 5 GHz rates are left enabled. - */ - band = nla_nest_start(msg, NL80211_BAND_2GHZ); - if (!band) - goto nla_put_failure; - if (disabled) { - NLA_PUT(msg, NL80211_TXRATE_LEGACY, 8, - "\x0c\x12\x18\x24\x30\x48\x60\x6c"); - } - nla_nest_end(msg, band); +static int nl80211_set_p2p_powersave(void *priv, int legacy_ps, int opp_ps, + int ctwindow) +{ + struct i802_bss *bss = priv; - nla_nest_end(msg, bands); + wpa_printf(MSG_DEBUG, "nl80211: set_p2p_powersave (legacy_ps=%d " + "opp_ps=%d ctwindow=%d)", legacy_ps, opp_ps, ctwindow); - ret = send_and_recv_msgs(drv, msg, NULL, NULL); - msg = NULL; - if (ret) { - wpa_printf(MSG_DEBUG, "nl80211: Set TX rates failed: ret=%d " - "(%s)", ret, strerror(-ret)); + if (opp_ps != -1 || ctwindow != -1) { +#ifdef ANDROID_P2P + wpa_driver_set_p2p_ps(priv, legacy_ps, opp_ps, ctwindow); +#else /* ANDROID_P2P */ + return -1; /* Not yet supported */ +#endif /* ANDROID_P2P */ } - return ret; + if (legacy_ps == -1) + return 0; + if (legacy_ps != 0 && legacy_ps != 1) + return -1; /* Not yet supported */ -nla_put_failure: - nlmsg_free(msg); - return -1; + return nl80211_set_power_save(bss, legacy_ps); } -static int wpa_driver_nl80211_deinit_ap(void *priv) +static int nl80211_start_radar_detection(void *priv, + struct hostapd_freq_params *freq) { struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; - if (!is_ap_interface(drv->nlmode)) - return -1; - wpa_driver_nl80211_del_beacon(drv); - return wpa_driver_nl80211_set_mode(priv, NL80211_IFTYPE_STATION); -} + struct nl_msg *msg; + int ret; + wpa_printf(MSG_DEBUG, "nl80211: Start radar detection (CAC) %d MHz (ht_enabled=%d, vht_enabled=%d, bandwidth=%d MHz, cf1=%d MHz, cf2=%d MHz)", + freq->freq, freq->ht_enabled, freq->vht_enabled, + freq->bandwidth, freq->center_freq1, freq->center_freq2); -static int wpa_driver_nl80211_deinit_p2p_cli(void *priv) -{ - struct i802_bss *bss = priv; - struct wpa_driver_nl80211_data *drv = bss->drv; - if (drv->nlmode != NL80211_IFTYPE_P2P_CLIENT) + if (!(drv->capa.flags & WPA_DRIVER_FLAGS_RADAR)) { + wpa_printf(MSG_DEBUG, "nl80211: Driver does not support radar " + "detection"); return -1; - return wpa_driver_nl80211_set_mode(priv, NL80211_IFTYPE_STATION); -} + } + msg = nlmsg_alloc(); + if (!msg) + return -1; -static void wpa_driver_nl80211_resume(void *priv) -{ - struct i802_bss *bss = priv; - struct wpa_driver_nl80211_data *drv = bss->drv; - if (linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 1)) { - wpa_printf(MSG_DEBUG, "nl80211: Failed to set interface up on " - "resume event"); + nl80211_cmd(bss->drv, msg, 0, NL80211_CMD_RADAR_DETECT); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq->freq); + + if (freq->vht_enabled) { + switch (freq->bandwidth) { + case 20: + NLA_PUT_U32(msg, NL80211_ATTR_CHANNEL_WIDTH, + NL80211_CHAN_WIDTH_20); + break; + case 40: + NLA_PUT_U32(msg, NL80211_ATTR_CHANNEL_WIDTH, + NL80211_CHAN_WIDTH_40); + break; + case 80: + if (freq->center_freq2) + NLA_PUT_U32(msg, NL80211_ATTR_CHANNEL_WIDTH, + NL80211_CHAN_WIDTH_80P80); + else + NLA_PUT_U32(msg, NL80211_ATTR_CHANNEL_WIDTH, + NL80211_CHAN_WIDTH_80); + break; + case 160: + NLA_PUT_U32(msg, NL80211_ATTR_CHANNEL_WIDTH, + NL80211_CHAN_WIDTH_160); + break; + default: + return -1; + } + NLA_PUT_U32(msg, NL80211_ATTR_CENTER_FREQ1, freq->center_freq1); + if (freq->center_freq2) + NLA_PUT_U32(msg, NL80211_ATTR_CENTER_FREQ2, + freq->center_freq2); + } else if (freq->ht_enabled) { + switch (freq->sec_channel_offset) { + case -1: + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, + NL80211_CHAN_HT40MINUS); + break; + case 1: + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, + NL80211_CHAN_HT40PLUS); + break; + default: + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, + NL80211_CHAN_HT20); + break; + } } + + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + if (ret == 0) + return 0; + wpa_printf(MSG_DEBUG, "nl80211: Failed to start radar detection: " + "%d (%s)", ret, strerror(-ret)); +nla_put_failure: + return -1; } +#ifdef CONFIG_TDLS -static int nl80211_send_ft_action(void *priv, u8 action, const u8 *target_ap, - const u8 *ies, size_t ies_len) +static int nl80211_send_tdls_mgmt(void *priv, const u8 *dst, u8 action_code, + u8 dialog_token, u16 status_code, + const u8 *buf, size_t len) { struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; - int ret; - u8 *data, *pos; - size_t data_len; - u8 own_addr[ETH_ALEN]; + struct nl_msg *msg; - if (linux_get_ifhwaddr(drv->global->ioctl_sock, bss->ifname, - own_addr) < 0) - return -1; + if (!(drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_SUPPORT)) + return -EOPNOTSUPP; - if (action != 1) { - wpa_printf(MSG_ERROR, "nl80211: Unsupported send_ft_action " - "action %d", action); - return -1; - } + if (!dst) + return -EINVAL; - /* - * Action frame payload: - * Category[1] = 6 (Fast BSS Transition) - * Action[1] = 1 (Fast BSS Transition Request) - * STA Address - * Target AP Address - * FT IEs - */ + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; - data_len = 2 + 2 * ETH_ALEN + ies_len; - data = os_malloc(data_len); - if (data == NULL) - return -1; - pos = data; - *pos++ = 0x06; /* FT Action category */ - *pos++ = action; - os_memcpy(pos, own_addr, ETH_ALEN); - pos += ETH_ALEN; - os_memcpy(pos, target_ap, ETH_ALEN); - pos += ETH_ALEN; - os_memcpy(pos, ies, ies_len); + nl80211_cmd(drv, msg, 0, NL80211_CMD_TDLS_MGMT); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, dst); + NLA_PUT_U8(msg, NL80211_ATTR_TDLS_ACTION, action_code); + NLA_PUT_U8(msg, NL80211_ATTR_TDLS_DIALOG_TOKEN, dialog_token); + NLA_PUT_U16(msg, NL80211_ATTR_STATUS_CODE, status_code); + NLA_PUT(msg, NL80211_ATTR_IE, len, buf); - ret = wpa_driver_nl80211_send_action(bss, drv->assoc_freq, 0, - drv->bssid, own_addr, drv->bssid, - data, data_len, 0); - os_free(data); + return send_and_recv_msgs(drv, msg, NULL, NULL); - return ret; +nla_put_failure: + nlmsg_free(msg); + return -ENOBUFS; } -static int nl80211_signal_monitor(void *priv, int threshold, int hysteresis) +static int nl80211_tdls_oper(void *priv, enum tdls_oper oper, const u8 *peer) { struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; - struct nl_msg *msg, *cqm = NULL; + struct nl_msg *msg; + enum nl80211_tdls_operation nl80211_oper; - wpa_printf(MSG_DEBUG, "nl80211: Signal monitor threshold=%d " - "hysteresis=%d", threshold, hysteresis); + if (!(drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_SUPPORT)) + return -EOPNOTSUPP; + + switch (oper) { + case TDLS_DISCOVERY_REQ: + nl80211_oper = NL80211_TDLS_DISCOVERY_REQ; + break; + case TDLS_SETUP: + nl80211_oper = NL80211_TDLS_SETUP; + break; + case TDLS_TEARDOWN: + nl80211_oper = NL80211_TDLS_TEARDOWN; + break; + case TDLS_ENABLE_LINK: + nl80211_oper = NL80211_TDLS_ENABLE_LINK; + break; + case TDLS_DISABLE_LINK: + nl80211_oper = NL80211_TDLS_DISABLE_LINK; + break; + case TDLS_ENABLE: + return 0; + case TDLS_DISABLE: + return 0; + default: + return -EINVAL; + } msg = nlmsg_alloc(); if (!msg) - return -1; - - nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_CQM); - - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, bss->ifindex); - - cqm = nlmsg_alloc(); - if (cqm == NULL) - return -1; + return -ENOMEM; - NLA_PUT_U32(cqm, NL80211_ATTR_CQM_RSSI_THOLD, threshold); - NLA_PUT_U32(cqm, NL80211_ATTR_CQM_RSSI_HYST, hysteresis); - nla_put_nested(msg, NL80211_ATTR_CQM, cqm); + nl80211_cmd(drv, msg, 0, NL80211_CMD_TDLS_OPER); + NLA_PUT_U8(msg, NL80211_ATTR_TDLS_OPERATION, nl80211_oper); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, peer); - if (send_and_recv_msgs(drv, msg, NULL, NULL) == 0) - return 0; - msg = NULL; + return send_and_recv_msgs(drv, msg, NULL, NULL); nla_put_failure: - nlmsg_free(cqm); nlmsg_free(msg); - return -1; + return -ENOBUFS; } +#endif /* CONFIG TDLS */ -static int nl80211_signal_poll(void *priv, struct wpa_signal_info *si) -{ - struct i802_bss *bss = priv; - struct wpa_driver_nl80211_data *drv = bss->drv; - int res; - os_memset(si, 0, sizeof(*si)); - res = nl80211_get_link_signal(drv, si); - if (res != 0) - return res; +#ifdef ANDROID - return nl80211_get_link_noise(drv, si); -} +typedef struct android_wifi_priv_cmd { + char *buf; + int used_len; + int total_len; +} android_wifi_priv_cmd; +static int drv_errors = 0; -static int nl80211_send_frame(void *priv, const u8 *data, size_t data_len, - int encrypt) +static void wpa_driver_send_hang_msg(struct wpa_driver_nl80211_data *drv) { - struct i802_bss *bss = priv; - struct wpa_driver_nl80211_data *drv = bss->drv; - return wpa_driver_nl80211_send_frame(drv, data, data_len, encrypt); + drv_errors++; + if (drv_errors > DRV_NUMBER_SEQUENTIAL_ERRORS) { + drv_errors = 0; + wpa_msg(drv->ctx, MSG_INFO, WPA_EVENT_DRIVER_STATE "HANGED"); + } } -static int nl80211_set_param(void *priv, const char *param) +static int android_priv_cmd(struct i802_bss *bss, const char *cmd) { - wpa_printf(MSG_DEBUG, "nl80211: driver param='%s'", param); - if (param == NULL) - return 0; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct ifreq ifr; + android_wifi_priv_cmd priv_cmd; + char buf[MAX_DRV_CMD_SIZE]; + int ret; -#ifdef CONFIG_P2P - if (os_strstr(param, "use_p2p_group_interface=1")) { - struct i802_bss *bss = priv; - struct wpa_driver_nl80211_data *drv = bss->drv; + os_memset(&ifr, 0, sizeof(ifr)); + os_memset(&priv_cmd, 0, sizeof(priv_cmd)); + os_strlcpy(ifr.ifr_name, bss->ifname, IFNAMSIZ); + + os_memset(buf, 0, sizeof(buf)); + os_strlcpy(buf, cmd, sizeof(buf)); + + priv_cmd.buf = buf; + priv_cmd.used_len = sizeof(buf); + priv_cmd.total_len = sizeof(buf); + ifr.ifr_data = &priv_cmd; - wpa_printf(MSG_DEBUG, "nl80211: Use separate P2P group " - "interface"); - drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_CONCURRENT; - drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_MGMT_AND_NON_P2P; + ret = ioctl(drv->global->ioctl_sock, SIOCDEVPRIVATE + 1, &ifr); + if (ret < 0) { + wpa_printf(MSG_ERROR, "%s: failed to issue private commands", + __func__); + wpa_driver_send_hang_msg(drv); + return ret; } -#endif /* CONFIG_P2P */ + drv_errors = 0; return 0; } -static void * nl80211_global_init(void) +static int android_pno_start(struct i802_bss *bss, + struct wpa_driver_scan_params *params) { - struct nl80211_global *global; - struct netlink_config *cfg; - - global = os_zalloc(sizeof(*global)); - if (global == NULL) - return NULL; - global->ioctl_sock = -1; - dl_list_init(&global->interfaces); - global->if_add_ifindex = -1; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct ifreq ifr; + android_wifi_priv_cmd priv_cmd; + int ret = 0, i = 0, bp; + char buf[WEXT_PNO_MAX_COMMAND_SIZE]; + + bp = WEXT_PNOSETUP_HEADER_SIZE; + os_memcpy(buf, WEXT_PNOSETUP_HEADER, bp); + buf[bp++] = WEXT_PNO_TLV_PREFIX; + buf[bp++] = WEXT_PNO_TLV_VERSION; + buf[bp++] = WEXT_PNO_TLV_SUBVERSION; + buf[bp++] = WEXT_PNO_TLV_RESERVED; + + while (i < WEXT_PNO_AMOUNT && (size_t) i < params->num_ssids) { + /* Check that there is enough space needed for 1 more SSID, the + * other sections and null termination */ + if ((bp + WEXT_PNO_SSID_HEADER_SIZE + MAX_SSID_LEN + + WEXT_PNO_NONSSID_SECTIONS_SIZE + 1) >= (int) sizeof(buf)) + break; + wpa_hexdump_ascii(MSG_DEBUG, "For PNO Scan", + params->ssids[i].ssid, + params->ssids[i].ssid_len); + buf[bp++] = WEXT_PNO_SSID_SECTION; + buf[bp++] = params->ssids[i].ssid_len; + os_memcpy(&buf[bp], params->ssids[i].ssid, + params->ssids[i].ssid_len); + bp += params->ssids[i].ssid_len; + i++; + } + + buf[bp++] = WEXT_PNO_SCAN_INTERVAL_SECTION; + os_snprintf(&buf[bp], WEXT_PNO_SCAN_INTERVAL_LENGTH + 1, "%x", + WEXT_PNO_SCAN_INTERVAL); + bp += WEXT_PNO_SCAN_INTERVAL_LENGTH; + + buf[bp++] = WEXT_PNO_REPEAT_SECTION; + os_snprintf(&buf[bp], WEXT_PNO_REPEAT_LENGTH + 1, "%x", + WEXT_PNO_REPEAT); + bp += WEXT_PNO_REPEAT_LENGTH; + + buf[bp++] = WEXT_PNO_MAX_REPEAT_SECTION; + os_snprintf(&buf[bp], WEXT_PNO_MAX_REPEAT_LENGTH + 1, "%x", + WEXT_PNO_MAX_REPEAT); + bp += WEXT_PNO_MAX_REPEAT_LENGTH + 1; + + memset(&ifr, 0, sizeof(ifr)); + memset(&priv_cmd, 0, sizeof(priv_cmd)); + os_strlcpy(ifr.ifr_name, bss->ifname, IFNAMSIZ); + + priv_cmd.buf = buf; + priv_cmd.used_len = bp; + priv_cmd.total_len = bp; + ifr.ifr_data = &priv_cmd; - cfg = os_zalloc(sizeof(*cfg)); - if (cfg == NULL) - goto err; + ret = ioctl(drv->global->ioctl_sock, SIOCDEVPRIVATE + 1, &ifr); - cfg->ctx = global; - cfg->newlink_cb = wpa_driver_nl80211_event_rtm_newlink; - cfg->dellink_cb = wpa_driver_nl80211_event_rtm_dellink; - global->netlink = netlink_init(cfg); - if (global->netlink == NULL) { - os_free(cfg); - goto err; + if (ret < 0) { + wpa_printf(MSG_ERROR, "ioctl[SIOCSIWPRIV] (pnosetup): %d", + ret); + wpa_driver_send_hang_msg(drv); + return ret; } - if (wpa_driver_nl80211_init_nl_global(global) < 0) - goto err; + drv_errors = 0; - global->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0); - if (global->ioctl_sock < 0) { - perror("socket(PF_INET,SOCK_DGRAM)"); - goto err; - } + return android_priv_cmd(bss, "PNOFORCE 1"); +} - return global; -err: - nl80211_global_deinit(global); - return NULL; +static int android_pno_stop(struct i802_bss *bss) +{ + return android_priv_cmd(bss, "PNOFORCE 0"); } +#endif /* ANDROID */ -static void nl80211_global_deinit(void *priv) + +static int driver_nl80211_set_key(const char *ifname, void *priv, + enum wpa_alg alg, const u8 *addr, + int key_idx, int set_tx, + const u8 *seq, size_t seq_len, + const u8 *key, size_t key_len) { - struct nl80211_global *global = priv; - if (global == NULL) - return; - if (!dl_list_empty(&global->interfaces)) { - wpa_printf(MSG_ERROR, "nl80211: %u interface(s) remain at " - "nl80211_global_deinit", - dl_list_len(&global->interfaces)); - } + struct i802_bss *bss = priv; + return wpa_driver_nl80211_set_key(ifname, bss, alg, addr, key_idx, + set_tx, seq, seq_len, key, key_len); +} - if (global->netlink) - netlink_deinit(global->netlink); - if (global->nl80211) - genl_family_put(global->nl80211); - nl_destroy_handles(&global->nl); +static int driver_nl80211_scan2(void *priv, + struct wpa_driver_scan_params *params) +{ + struct i802_bss *bss = priv; + return wpa_driver_nl80211_scan(bss, params); +} - if (global->nl_cb) - nl_cb_put(global->nl_cb); - if (global->ioctl_sock >= 0) - close(global->ioctl_sock); +static int driver_nl80211_deauthenticate(void *priv, const u8 *addr, + int reason_code) +{ + struct i802_bss *bss = priv; + return wpa_driver_nl80211_deauthenticate(bss, addr, reason_code); +} - os_free(global); + +static int driver_nl80211_authenticate(void *priv, + struct wpa_driver_auth_params *params) +{ + struct i802_bss *bss = priv; + return wpa_driver_nl80211_authenticate(bss, params); } -static const char * nl80211_get_radio_name(void *priv) +static void driver_nl80211_deinit(void *priv) { struct i802_bss *bss = priv; - struct wpa_driver_nl80211_data *drv = bss->drv; - return drv->phyname; + wpa_driver_nl80211_deinit(bss); } -static int nl80211_pmkid(struct i802_bss *bss, int cmd, const u8 *bssid, - const u8 *pmkid) +static int driver_nl80211_if_remove(void *priv, enum wpa_driver_if_type type, + const char *ifname) { - struct nl_msg *msg; + struct i802_bss *bss = priv; + return wpa_driver_nl80211_if_remove(bss, type, ifname); +} - msg = nlmsg_alloc(); - if (!msg) - return -ENOMEM; - nl80211_cmd(bss->drv, msg, 0, cmd); +static int driver_nl80211_send_mlme(void *priv, const u8 *data, + size_t data_len, int noack) +{ + struct i802_bss *bss = priv; + return wpa_driver_nl80211_send_mlme(bss, data, data_len, noack, + 0, 0, 0, 0); +} - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(bss->ifname)); - if (pmkid) - NLA_PUT(msg, NL80211_ATTR_PMKID, 16, pmkid); - if (bssid) - NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid); - return send_and_recv_msgs(bss->drv, msg, NULL, NULL); - nla_put_failure: - nlmsg_free(msg); - return -ENOBUFS; +static int driver_nl80211_sta_remove(void *priv, const u8 *addr) +{ + struct i802_bss *bss = priv; + return wpa_driver_nl80211_sta_remove(bss, addr); } -static int nl80211_add_pmkid(void *priv, const u8 *bssid, const u8 *pmkid) +static int driver_nl80211_set_sta_vlan(void *priv, const u8 *addr, + const char *ifname, int vlan_id) { struct i802_bss *bss = priv; - wpa_printf(MSG_DEBUG, "nl80211: Add PMKID for " MACSTR, MAC2STR(bssid)); - return nl80211_pmkid(bss, NL80211_CMD_SET_PMKSA, bssid, pmkid); + return i802_set_sta_vlan(bss, addr, ifname, vlan_id); } -static int nl80211_remove_pmkid(void *priv, const u8 *bssid, const u8 *pmkid) +static int driver_nl80211_read_sta_data(void *priv, + struct hostap_sta_driver_data *data, + const u8 *addr) { struct i802_bss *bss = priv; - wpa_printf(MSG_DEBUG, "nl80211: Delete PMKID for " MACSTR, - MAC2STR(bssid)); - return nl80211_pmkid(bss, NL80211_CMD_DEL_PMKSA, bssid, pmkid); + return i802_read_sta_data(bss, data, addr); } -static int nl80211_flush_pmkid(void *priv) +static int driver_nl80211_send_action(void *priv, unsigned int freq, + unsigned int wait_time, + const u8 *dst, const u8 *src, + const u8 *bssid, + const u8 *data, size_t data_len, + int no_cck) { struct i802_bss *bss = priv; - wpa_printf(MSG_DEBUG, "nl80211: Flush PMKIDs"); - return nl80211_pmkid(bss, NL80211_CMD_FLUSH_PMKSA, NULL, NULL); + return wpa_driver_nl80211_send_action(bss, freq, wait_time, dst, src, + bssid, data, data_len, no_cck); } -static void nl80211_set_rekey_info(void *priv, const u8 *kek, const u8 *kck, - const u8 *replay_ctr) +static int driver_nl80211_probe_req_report(void *priv, int report) { struct i802_bss *bss = priv; - struct wpa_driver_nl80211_data *drv = bss->drv; - struct nlattr *replay_nested; + return wpa_driver_nl80211_probe_req_report(bss, report); +} + + +static int wpa_driver_nl80211_update_ft_ies(void *priv, const u8 *md, + const u8 *ies, size_t ies_len) +{ + int ret; struct nl_msg *msg; + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + u16 mdid = WPA_GET_LE16(md); msg = nlmsg_alloc(); if (!msg) - return; - - nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_REKEY_OFFLOAD); - - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, bss->ifindex); + return -ENOMEM; - replay_nested = nla_nest_start(msg, NL80211_ATTR_REKEY_DATA); - if (!replay_nested) - goto nla_put_failure; + wpa_printf(MSG_DEBUG, "nl80211: Updating FT IEs"); + nl80211_cmd(drv, msg, 0, NL80211_CMD_UPDATE_FT_IES); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + NLA_PUT(msg, NL80211_ATTR_IE, ies_len, ies); + NLA_PUT_U16(msg, NL80211_ATTR_MDID, mdid); - NLA_PUT(msg, NL80211_REKEY_DATA_KEK, NL80211_KEK_LEN, kek); - NLA_PUT(msg, NL80211_REKEY_DATA_KCK, NL80211_KCK_LEN, kck); - NLA_PUT(msg, NL80211_REKEY_DATA_REPLAY_CTR, NL80211_REPLAY_CTR_LEN, - replay_ctr); + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + if (ret) { + wpa_printf(MSG_DEBUG, "nl80211: update_ft_ies failed " + "err=%d (%s)", ret, strerror(-ret)); + } - nla_nest_end(msg, replay_nested); + return ret; - send_and_recv_msgs(drv, msg, NULL, NULL); - return; - nla_put_failure: +nla_put_failure: nlmsg_free(msg); + return -ENOBUFS; } -static void nl80211_poll_client(void *priv, const u8 *own_addr, const u8 *addr, - int qos) +const u8 * wpa_driver_nl80211_get_macaddr(void *priv) { struct i802_bss *bss = priv; - struct { - struct ieee80211_hdr hdr; - u16 qos_ctl; - } STRUCT_PACKED nulldata; - size_t size; + struct wpa_driver_nl80211_data *drv = bss->drv; - /* Send data frame to poll STA and check whether this frame is ACKed */ + if (drv->nlmode != NL80211_IFTYPE_P2P_DEVICE) + return NULL; - os_memset(&nulldata, 0, sizeof(nulldata)); + return bss->addr; +} - if (qos) { - nulldata.hdr.frame_control = - IEEE80211_FC(WLAN_FC_TYPE_DATA, - WLAN_FC_STYPE_QOS_NULL); - size = sizeof(nulldata); - } else { - nulldata.hdr.frame_control = - IEEE80211_FC(WLAN_FC_TYPE_DATA, - WLAN_FC_STYPE_NULLFUNC); - size = sizeof(struct ieee80211_hdr); - } - nulldata.hdr.frame_control |= host_to_le16(WLAN_FC_FROMDS); - os_memcpy(nulldata.hdr.IEEE80211_DA_FROMDS, addr, ETH_ALEN); - os_memcpy(nulldata.hdr.IEEE80211_BSSID_FROMDS, own_addr, ETH_ALEN); - os_memcpy(nulldata.hdr.IEEE80211_SA_FROMDS, own_addr, ETH_ALEN); +static const char * scan_state_str(enum scan_states scan_state) +{ + switch (scan_state) { + case NO_SCAN: + return "NO_SCAN"; + case SCAN_REQUESTED: + return "SCAN_REQUESTED"; + case SCAN_STARTED: + return "SCAN_STARTED"; + case SCAN_COMPLETED: + return "SCAN_COMPLETED"; + case SCAN_ABORTED: + return "SCAN_ABORTED"; + case SCHED_SCAN_STARTED: + return "SCHED_SCAN_STARTED"; + case SCHED_SCAN_STOPPED: + return "SCHED_SCAN_STOPPED"; + case SCHED_SCAN_RESULTS: + return "SCHED_SCAN_RESULTS"; + } - if (wpa_driver_nl80211_send_mlme(bss, (u8 *) &nulldata, size) < 0) - wpa_printf(MSG_DEBUG, "nl80211_send_null_frame: Failed to " - "send poll frame"); + return "??"; } -#ifdef CONFIG_TDLS - -static int nl80211_send_tdls_mgmt(void *priv, const u8 *dst, u8 action_code, - u8 dialog_token, u16 status_code, - const u8 *buf, size_t len) +static int wpa_driver_nl80211_status(void *priv, char *buf, size_t buflen) { struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; + int res; + char *pos, *end; + + pos = buf; + end = buf + buflen; + + res = os_snprintf(pos, end - pos, + "ifindex=%d\n" + "ifname=%s\n" + "brname=%s\n" + "addr=" MACSTR "\n" + "freq=%d\n" + "%s%s%s%s%s", + bss->ifindex, + bss->ifname, + bss->brname, + MAC2STR(bss->addr), + bss->freq, + bss->beacon_set ? "beacon_set=1\n" : "", + bss->added_if_into_bridge ? + "added_if_into_bridge=1\n" : "", + bss->added_bridge ? "added_bridge=1\n" : "", + bss->in_deinit ? "in_deinit=1\n" : "", + bss->if_dynamic ? "if_dynamic=1\n" : ""); + if (res < 0 || res >= end - pos) + return pos - buf; + pos += res; + + if (bss->wdev_id_set) { + res = os_snprintf(pos, end - pos, "wdev_id=%llu\n", + (unsigned long long) bss->wdev_id); + if (res < 0 || res >= end - pos) + return pos - buf; + pos += res; + } + + res = os_snprintf(pos, end - pos, + "phyname=%s\n" + "drv_ifindex=%d\n" + "operstate=%d\n" + "scan_state=%s\n" + "auth_bssid=" MACSTR "\n" + "auth_attempt_bssid=" MACSTR "\n" + "bssid=" MACSTR "\n" + "prev_bssid=" MACSTR "\n" + "associated=%d\n" + "assoc_freq=%u\n" + "monitor_sock=%d\n" + "monitor_ifidx=%d\n" + "monitor_refcount=%d\n" + "last_mgmt_freq=%u\n" + "eapol_tx_sock=%d\n" + "%s%s%s%s%s%s%s%s%s%s%s%s%s", + drv->phyname, + drv->ifindex, + drv->operstate, + scan_state_str(drv->scan_state), + MAC2STR(drv->auth_bssid), + MAC2STR(drv->auth_attempt_bssid), + MAC2STR(drv->bssid), + MAC2STR(drv->prev_bssid), + drv->associated, + drv->assoc_freq, + drv->monitor_sock, + drv->monitor_ifidx, + drv->monitor_refcount, + drv->last_mgmt_freq, + drv->eapol_tx_sock, + drv->ignore_if_down_event ? + "ignore_if_down_event=1\n" : "", + drv->scan_complete_events ? + "scan_complete_events=1\n" : "", + drv->disabled_11b_rates ? + "disabled_11b_rates=1\n" : "", + drv->pending_remain_on_chan ? + "pending_remain_on_chan=1\n" : "", + drv->in_interface_list ? "in_interface_list=1\n" : "", + drv->device_ap_sme ? "device_ap_sme=1\n" : "", + drv->poll_command_supported ? + "poll_command_supported=1\n" : "", + drv->data_tx_status ? "data_tx_status=1\n" : "", + drv->scan_for_auth ? "scan_for_auth=1\n" : "", + drv->retry_auth ? "retry_auth=1\n" : "", + drv->use_monitor ? "use_monitor=1\n" : "", + drv->ignore_next_local_disconnect ? + "ignore_next_local_disconnect=1\n" : "", + drv->allow_p2p_device ? "allow_p2p_device=1\n" : ""); + if (res < 0 || res >= end - pos) + return pos - buf; + pos += res; + + if (drv->has_capability) { + res = os_snprintf(pos, end - pos, + "capa.key_mgmt=0x%x\n" + "capa.enc=0x%x\n" + "capa.auth=0x%x\n" + "capa.flags=0x%x\n" + "capa.max_scan_ssids=%d\n" + "capa.max_sched_scan_ssids=%d\n" + "capa.sched_scan_supported=%d\n" + "capa.max_match_sets=%d\n" + "capa.max_remain_on_chan=%u\n" + "capa.max_stations=%u\n" + "capa.probe_resp_offloads=0x%x\n" + "capa.max_acl_mac_addrs=%u\n" + "capa.num_multichan_concurrent=%u\n", + drv->capa.key_mgmt, + drv->capa.enc, + drv->capa.auth, + drv->capa.flags, + drv->capa.max_scan_ssids, + drv->capa.max_sched_scan_ssids, + drv->capa.sched_scan_supported, + drv->capa.max_match_sets, + drv->capa.max_remain_on_chan, + drv->capa.max_stations, + drv->capa.probe_resp_offloads, + drv->capa.max_acl_mac_addrs, + drv->capa.num_multichan_concurrent); + if (res < 0 || res >= end - pos) + return pos - buf; + pos += res; + } + + return pos - buf; +} + + +static int set_beacon_data(struct nl_msg *msg, struct beacon_data *settings) +{ + if (settings->head) + NLA_PUT(msg, NL80211_ATTR_BEACON_HEAD, + settings->head_len, settings->head); + + if (settings->tail) + NLA_PUT(msg, NL80211_ATTR_BEACON_TAIL, + settings->tail_len, settings->tail); + + if (settings->beacon_ies) + NLA_PUT(msg, NL80211_ATTR_IE, + settings->beacon_ies_len, settings->beacon_ies); + + if (settings->proberesp_ies) + NLA_PUT(msg, NL80211_ATTR_IE_PROBE_RESP, + settings->proberesp_ies_len, settings->proberesp_ies); + + if (settings->assocresp_ies) + NLA_PUT(msg, + NL80211_ATTR_IE_ASSOC_RESP, + settings->assocresp_ies_len, settings->assocresp_ies); + + if (settings->probe_resp) + NLA_PUT(msg, NL80211_ATTR_PROBE_RESP, + settings->probe_resp_len, settings->probe_resp); + + return 0; + +nla_put_failure: + return -ENOBUFS; +} + + +static int nl80211_switch_channel(void *priv, struct csa_settings *settings) +{ struct nl_msg *msg; + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nlattr *beacon_csa; + int ret = -ENOBUFS; - if (!(drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_SUPPORT)) + wpa_printf(MSG_DEBUG, "nl80211: Channel switch request (cs_count=%u block_tx=%u freq=%d width=%d cf1=%d cf2=%d)", + settings->cs_count, settings->block_tx, + settings->freq_params.freq, settings->freq_params.bandwidth, + settings->freq_params.center_freq1, + settings->freq_params.center_freq2); + + if (!(drv->capa.flags & WPA_DRIVER_FLAGS_AP_CSA)) { + wpa_printf(MSG_DEBUG, "nl80211: Driver does not support channel switch command"); return -EOPNOTSUPP; + } - if (!dst) + if ((drv->nlmode != NL80211_IFTYPE_AP) && + (drv->nlmode != NL80211_IFTYPE_P2P_GO)) + return -EOPNOTSUPP; + + /* check settings validity */ + if (!settings->beacon_csa.tail || + ((settings->beacon_csa.tail_len <= + settings->counter_offset_beacon) || + (settings->beacon_csa.tail[settings->counter_offset_beacon] != + settings->cs_count))) + return -EINVAL; + + if (settings->beacon_csa.probe_resp && + ((settings->beacon_csa.probe_resp_len <= + settings->counter_offset_presp) || + (settings->beacon_csa.probe_resp[settings->counter_offset_presp] != + settings->cs_count))) return -EINVAL; msg = nlmsg_alloc(); if (!msg) return -ENOMEM; - nl80211_cmd(drv, msg, 0, NL80211_CMD_TDLS_MGMT); + nl80211_cmd(drv, msg, 0, NL80211_CMD_CHANNEL_SWITCH); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); - NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, dst); - NLA_PUT_U8(msg, NL80211_ATTR_TDLS_ACTION, action_code); - NLA_PUT_U8(msg, NL80211_ATTR_TDLS_DIALOG_TOKEN, dialog_token); - NLA_PUT_U16(msg, NL80211_ATTR_STATUS_CODE, status_code); - NLA_PUT(msg, NL80211_ATTR_IE, len, buf); + NLA_PUT_U32(msg, NL80211_ATTR_CH_SWITCH_COUNT, settings->cs_count); + ret = nl80211_put_freq_params(msg, &settings->freq_params); + if (ret) + goto error; - return send_and_recv_msgs(drv, msg, NULL, NULL); + if (settings->block_tx) + NLA_PUT_FLAG(msg, NL80211_ATTR_CH_SWITCH_BLOCK_TX); + + /* beacon_after params */ + ret = set_beacon_data(msg, &settings->beacon_after); + if (ret) + goto error; + + /* beacon_csa params */ + beacon_csa = nla_nest_start(msg, NL80211_ATTR_CSA_IES); + if (!beacon_csa) + goto nla_put_failure; + + ret = set_beacon_data(msg, &settings->beacon_csa); + if (ret) + goto error; + + NLA_PUT_U16(msg, NL80211_ATTR_CSA_C_OFF_BEACON, + settings->counter_offset_beacon); + + if (settings->beacon_csa.probe_resp) + NLA_PUT_U16(msg, NL80211_ATTR_CSA_C_OFF_PRESP, + settings->counter_offset_presp); + + nla_nest_end(msg, beacon_csa); + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + if (ret) { + wpa_printf(MSG_DEBUG, "nl80211: switch_channel failed err=%d (%s)", + ret, strerror(-ret)); + } + return ret; nla_put_failure: + ret = -ENOBUFS; +error: nlmsg_free(msg); - return -ENOBUFS; + wpa_printf(MSG_DEBUG, "nl80211: Could not build channel switch request"); + return ret; } -static int nl80211_tdls_oper(void *priv, enum tdls_oper oper, const u8 *peer) +static int nl80211_set_qos_map(void *priv, const u8 *qos_map_set, + u8 qos_map_set_len) { struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; - enum nl80211_tdls_operation nl80211_oper; - - if (!(drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_SUPPORT)) - return -EOPNOTSUPP; - - switch (oper) { - case TDLS_DISCOVERY_REQ: - nl80211_oper = NL80211_TDLS_DISCOVERY_REQ; - break; - case TDLS_SETUP: - nl80211_oper = NL80211_TDLS_SETUP; - break; - case TDLS_TEARDOWN: - nl80211_oper = NL80211_TDLS_TEARDOWN; - break; - case TDLS_ENABLE_LINK: - nl80211_oper = NL80211_TDLS_ENABLE_LINK; - break; - case TDLS_DISABLE_LINK: - nl80211_oper = NL80211_TDLS_DISABLE_LINK; - break; - case TDLS_ENABLE: - return 0; - case TDLS_DISABLE: - return 0; - default: - return -EINVAL; - } + int ret; msg = nlmsg_alloc(); if (!msg) return -ENOMEM; - nl80211_cmd(drv, msg, 0, NL80211_CMD_TDLS_OPER); - NLA_PUT_U8(msg, NL80211_ATTR_TDLS_OPERATION, nl80211_oper); + wpa_hexdump(MSG_DEBUG, "nl80211: Setting QoS Map", + qos_map_set, qos_map_set_len); + + nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_QOS_MAP); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); - NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, peer); + NLA_PUT(msg, NL80211_ATTR_QOS_MAP, qos_map_set_len, qos_map_set); - return send_and_recv_msgs(drv, msg, NULL, NULL); + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + if (ret) + wpa_printf(MSG_DEBUG, "nl80211: Setting QoS Map failed"); + + return ret; nla_put_failure: nlmsg_free(msg); return -ENOBUFS; } -#endif /* CONFIG TDLS */ - const struct wpa_driver_ops wpa_driver_nl80211_ops = { .name = "nl80211", .desc = "Linux nl80211/cfg80211", .get_bssid = wpa_driver_nl80211_get_bssid, .get_ssid = wpa_driver_nl80211_get_ssid, - .set_key = wpa_driver_nl80211_set_key, - .scan2 = wpa_driver_nl80211_scan, + .set_key = driver_nl80211_set_key, + .scan2 = driver_nl80211_scan2, .sched_scan = wpa_driver_nl80211_sched_scan, .stop_sched_scan = wpa_driver_nl80211_stop_sched_scan, .get_scan_results2 = wpa_driver_nl80211_get_scan_results, - .deauthenticate = wpa_driver_nl80211_deauthenticate, - .disassociate = wpa_driver_nl80211_disassociate, - .authenticate = wpa_driver_nl80211_authenticate, + .deauthenticate = driver_nl80211_deauthenticate, + .authenticate = driver_nl80211_authenticate, .associate = wpa_driver_nl80211_associate, .global_init = nl80211_global_init, .global_deinit = nl80211_global_deinit, .init2 = wpa_driver_nl80211_init, - .deinit = wpa_driver_nl80211_deinit, + .deinit = driver_nl80211_deinit, .get_capa = wpa_driver_nl80211_get_capa, .set_operstate = wpa_driver_nl80211_set_operstate, .set_supp_port = wpa_driver_nl80211_set_supp_port, .set_country = wpa_driver_nl80211_set_country, + .get_country = wpa_driver_nl80211_get_country, .set_ap = wpa_driver_nl80211_set_ap, + .set_acl = wpa_driver_nl80211_set_acl, .if_add = wpa_driver_nl80211_if_add, - .if_remove = wpa_driver_nl80211_if_remove, - .send_mlme = wpa_driver_nl80211_send_mlme, + .if_remove = driver_nl80211_if_remove, + .send_mlme = driver_nl80211_send_mlme, .get_hw_feature_data = wpa_driver_nl80211_get_hw_feature_data, .sta_add = wpa_driver_nl80211_sta_add, - .sta_remove = wpa_driver_nl80211_sta_remove, + .sta_remove = driver_nl80211_sta_remove, .hapd_send_eapol = wpa_driver_nl80211_hapd_send_eapol, .sta_set_flags = wpa_driver_nl80211_sta_set_flags, -#ifdef HOSTAPD .hapd_init = i802_init, .hapd_deinit = i802_deinit, .set_wds_sta = i802_set_wds_sta, -#endif /* HOSTAPD */ -#if defined(HOSTAPD) || defined(CONFIG_AP) .get_seqnum = i802_get_seqnum, .flush = i802_flush, - .read_sta_data = i802_read_sta_data, .get_inact_sec = i802_get_inact_sec, .sta_clear_stats = i802_sta_clear_stats, .set_rts = i802_set_rts, .set_frag = i802_set_frag, .set_tx_queue_params = i802_set_tx_queue_params, - .set_sta_vlan = i802_set_sta_vlan, - .set_rate_sets = i802_set_rate_sets, + .set_sta_vlan = driver_nl80211_set_sta_vlan, .sta_deauth = i802_sta_deauth, .sta_disassoc = i802_sta_disassoc, -#endif /* HOSTAPD || CONFIG_AP */ + .read_sta_data = driver_nl80211_read_sta_data, .set_freq = i802_set_freq, - .send_action = wpa_driver_nl80211_send_action, + .send_action = driver_nl80211_send_action, .send_action_cancel_wait = wpa_driver_nl80211_send_action_cancel_wait, .remain_on_channel = wpa_driver_nl80211_remain_on_channel, .cancel_remain_on_channel = wpa_driver_nl80211_cancel_remain_on_channel, - .probe_req_report = wpa_driver_nl80211_probe_req_report, + .probe_req_report = driver_nl80211_probe_req_report, .deinit_ap = wpa_driver_nl80211_deinit_ap, .deinit_p2p_cli = wpa_driver_nl80211_deinit_p2p_cli, .resume = wpa_driver_nl80211_resume, @@ -7749,6 +11741,7 @@ .signal_monitor = nl80211_signal_monitor, .signal_poll = nl80211_signal_poll, .send_frame = nl80211_send_frame, + .shared_freq = wpa_driver_nl80211_shared_freq, .set_param = nl80211_set_param, .get_radio_name = nl80211_get_radio_name, .add_pmkid = nl80211_add_pmkid, @@ -7756,8 +11749,25 @@ .flush_pmkid = nl80211_flush_pmkid, .set_rekey_info = nl80211_set_rekey_info, .poll_client = nl80211_poll_client, + .set_p2p_powersave = nl80211_set_p2p_powersave, + .start_dfs_cac = nl80211_start_radar_detection, + .stop_ap = wpa_driver_nl80211_stop_ap, #ifdef CONFIG_TDLS .send_tdls_mgmt = nl80211_send_tdls_mgmt, .tdls_oper = nl80211_tdls_oper, #endif /* CONFIG_TDLS */ + .update_ft_ies = wpa_driver_nl80211_update_ft_ies, + .get_mac_addr = wpa_driver_nl80211_get_macaddr, + .get_survey = wpa_driver_nl80211_get_survey, + .status = wpa_driver_nl80211_status, + .switch_channel = nl80211_switch_channel, +#ifdef ANDROID_P2P + .set_noa = wpa_driver_set_p2p_noa, + .get_noa = wpa_driver_get_p2p_noa, + .set_ap_wps_ie = wpa_driver_set_ap_wps_p2p_ie, +#endif /* ANDROID_P2P */ +#ifdef ANDROID + .driver_cmd = wpa_driver_nl80211_driver_cmd, +#endif /* ANDROID */ + .set_qos_map = nl80211_set_qos_map, }; diff -Nru wpa-1.0/src/drivers/driver_none.c wpa-2.1/src/drivers/driver_none.c --- wpa-1.0/src/drivers/driver_none.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/drivers/driver_none.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * Driver interface for RADIUS server or WPS ER only (no driver) * Copyright (c) 2008, Atheros Communications * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff -Nru wpa-1.0/src/drivers/driver_openbsd.c wpa-2.1/src/drivers/driver_openbsd.c --- wpa-1.0/src/drivers/driver_openbsd.c 1970-01-01 00:00:00.000000000 +0000 +++ wpa-2.1/src/drivers/driver_openbsd.c 2014-02-04 11:23:35.000000000 +0000 @@ -0,0 +1,136 @@ +/* + * Driver interaction with OpenBSD net80211 layer + * Copyright (c) 2013, Mark Kettenis + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" +#include + +#include +#include +#include +#include + +#include "common.h" +#include "driver.h" + +struct openbsd_driver_data { + char ifname[IFNAMSIZ + 1]; + void *ctx; + + int sock; /* open socket for 802.11 ioctls */ +}; + + +static int +wpa_driver_openbsd_get_ssid(void *priv, u8 *ssid) +{ + struct openbsd_driver_data *drv = priv; + struct ieee80211_nwid nwid; + struct ifreq ifr; + + os_memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, drv->ifname, sizeof(ifr.ifr_name)); + ifr.ifr_data = (void *)&nwid; + if (ioctl(drv->sock, SIOCG80211NWID, &ifr) < 0 || + nwid.i_len > IEEE80211_NWID_LEN) + return -1; + + os_memcpy(ssid, nwid.i_nwid, nwid.i_len); + return nwid.i_len; +} + +static int +wpa_driver_openbsd_get_bssid(void *priv, u8 *bssid) +{ + struct openbsd_driver_data *drv = priv; + struct ieee80211_bssid id; + + os_strlcpy(id.i_name, drv->ifname, sizeof(id.i_name)); + if (ioctl(drv->sock, SIOCG80211BSSID, &id) < 0) + return -1; + + os_memcpy(bssid, id.i_bssid, IEEE80211_ADDR_LEN); + return 0; +} + + +static int +wpa_driver_openbsd_get_capa(void *priv, struct wpa_driver_capa *capa) +{ + os_memset(capa, 0, sizeof(*capa)); + capa->flags = WPA_DRIVER_FLAGS_4WAY_HANDSHAKE; + return 0; +} + + +static int +wpa_driver_openbsd_set_key(const char *ifname, void *priv, enum wpa_alg alg, + const unsigned char *addr, int key_idx, int set_tx, const u8 *seq, + size_t seq_len, const u8 *key, size_t key_len) +{ + struct openbsd_driver_data *drv = priv; + struct ieee80211_keyavail keyavail; + + if (alg != WPA_ALG_PMK || key_len > IEEE80211_PMK_LEN) + return -1; + + memset(&keyavail, 0, sizeof(keyavail)); + os_strlcpy(keyavail.i_name, drv->ifname, sizeof(keyavail.i_name)); + if (wpa_driver_openbsd_get_bssid(priv, keyavail.i_macaddr) < 0) + return -1; + memcpy(keyavail.i_key, key, key_len); + + if (ioctl(drv->sock, SIOCS80211KEYAVAIL, &keyavail) < 0) + return -1; + + return 0; +} + +static void * +wpa_driver_openbsd_init(void *ctx, const char *ifname) +{ + struct openbsd_driver_data *drv; + + drv = os_zalloc(sizeof(*drv)); + if (drv == NULL) + return NULL; + + drv->sock = socket(PF_INET, SOCK_DGRAM, 0); + if (drv->sock < 0) + goto fail; + + drv->ctx = ctx; + os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname)); + + return drv; + +fail: + os_free(drv); + return NULL; +} + + +static void +wpa_driver_openbsd_deinit(void *priv) +{ + struct openbsd_driver_data *drv = priv; + + close(drv->sock); + os_free(drv); +} + + +const struct wpa_driver_ops wpa_driver_openbsd_ops = { + .name = "openbsd", + .desc = "OpenBSD 802.11 support", + .get_ssid = wpa_driver_openbsd_get_ssid, + .get_bssid = wpa_driver_openbsd_get_bssid, + .get_capa = wpa_driver_openbsd_get_capa, + .set_key = wpa_driver_openbsd_set_key, + .init = wpa_driver_openbsd_init, + .deinit = wpa_driver_openbsd_deinit, +}; diff -Nru wpa-1.0/src/drivers/driver_osx.m wpa-2.1/src/drivers/driver_osx.m --- wpa-1.0/src/drivers/driver_osx.m 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/drivers/driver_osx.m 1970-01-01 00:00:00.000000000 +0000 @@ -1,459 +0,0 @@ -/* - * WPA Supplicant - Mac OS X Apple80211 driver interface - * Copyright (c) 2007, Jouni Malinen - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#include "includes.h" -#define Boolean __DummyBoolean -#include -#undef Boolean - -#include "common.h" -#include "driver.h" -#include "eloop.h" -#include "common/ieee802_11_defs.h" - -#include "Apple80211.h" - -struct wpa_driver_osx_data { - void *ctx; - WirelessRef wireless_ctx; - CFArrayRef scan_results; -}; - - -#ifndef CONFIG_NO_STDOUT_DEBUG -extern int wpa_debug_level; - -static void dump_dict_cb(const void *key, const void *value, void *context) -{ - if (MSG_DEBUG < wpa_debug_level) - return; - - wpa_printf(MSG_DEBUG, "Key:"); - CFShow(key); - wpa_printf(MSG_DEBUG, "Value:"); - CFShow(value); -} -#endif /* CONFIG_NO_STDOUT_DEBUG */ - - -static void wpa_driver_osx_dump_dict(CFDictionaryRef dict, const char *title) -{ -#ifndef CONFIG_NO_STDOUT_DEBUG - wpa_printf(MSG_DEBUG, "OSX: Dump dictionary %s - %u entries", - title, (unsigned int) CFDictionaryGetCount(dict)); - CFDictionaryApplyFunction(dict, dump_dict_cb, NULL); -#endif /* CONFIG_NO_STDOUT_DEBUG */ -} - - -static int wpa_driver_osx_get_ssid(void *priv, u8 *ssid) -{ - struct wpa_driver_osx_data *drv = priv; - WirelessError err; - WirelessInfo info; - int len; - - err = WirelessGetInfo(drv->wireless_ctx, &info); - if (err) { - wpa_printf(MSG_DEBUG, "OSX: WirelessGetInfo failed: %d", - (int) err); - return -1; - } - if (!info.power) { - wpa_printf(MSG_DEBUG, "OSX: Wireless device power off"); - return -1; - } - - for (len = 0; len < 32; len++) - if (info.ssid[len] == 0) - break; - - os_memcpy(ssid, info.ssid, len); - return len; -} - - -static int wpa_driver_osx_get_bssid(void *priv, u8 *bssid) -{ - struct wpa_driver_osx_data *drv = priv; - WirelessError err; - WirelessInfo info; - - err = WirelessGetInfo(drv->wireless_ctx, &info); - if (err) { - wpa_printf(MSG_DEBUG, "OSX: WirelessGetInfo failed: %d", - (int) err); - return -1; - } - if (!info.power) { - wpa_printf(MSG_DEBUG, "OSX: Wireless device power off"); - return -1; - } - - os_memcpy(bssid, info.bssID, ETH_ALEN); - return 0; -} - - -static void wpa_driver_osx_scan_timeout(void *eloop_ctx, void *timeout_ctx) -{ - wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL); -} - - -static int wpa_driver_osx_scan(void *priv, struct wpa_driver_scan_params *params) -{ - struct wpa_driver_osx_data *drv = priv; - WirelessError err; - const u8 *ssid = params->ssids[0].ssid; - size_t ssid_len = params->ssids[0].ssid_len; - - if (drv->scan_results) { - CFRelease(drv->scan_results); - drv->scan_results = NULL; - } - - if (ssid) { - CFStringRef data; - data = CFStringCreateWithBytes(kCFAllocatorDefault, - ssid, ssid_len, - kCFStringEncodingISOLatin1, - FALSE); - if (data == NULL) { - wpa_printf(MSG_DEBUG, "CFStringCreateWithBytes " - "failed"); - return -1; - } - - err = WirelessDirectedScan(drv->wireless_ctx, - &drv->scan_results, 0, data); - CFRelease(data); - if (err) { - wpa_printf(MSG_DEBUG, "OSX: WirelessDirectedScan " - "failed: 0x%08x", (unsigned int) err); - return -1; - } - } else { - err = WirelessScan(drv->wireless_ctx, &drv->scan_results, 0); - if (err) { - wpa_printf(MSG_DEBUG, "OSX: WirelessScan failed: " - "0x%08x", (unsigned int) err); - return -1; - } - } - - eloop_register_timeout(0, 0, wpa_driver_osx_scan_timeout, drv, - drv->ctx); - return 0; -} - - -static void wpa_driver_osx_add_scan_entry(struct wpa_scan_results *res, - WirelessNetworkInfo *info) -{ - struct wpa_scan_res *result, **tmp; - size_t extra_len; - u8 *pos; - - extra_len = 2 + info->ssid_len; - - result = os_zalloc(sizeof(*result) + extra_len); - if (result == NULL) - return; - os_memcpy(result->bssid, info->bssid, ETH_ALEN); - result->freq = 2407 + info->channel * 5; - //result->beacon_int =; - result->caps = info->capability; - //result->qual = info->signal; - result->noise = info->noise; - - pos = (u8 *)(result + 1); - - *pos++ = WLAN_EID_SSID; - *pos++ = info->ssid_len; - os_memcpy(pos, info->ssid, info->ssid_len); - pos += info->ssid_len; - - result->ie_len = pos - (u8 *)(result + 1); - - tmp = os_realloc(res->res, - (res->num + 1) * sizeof(struct wpa_scan_res *)); - if (tmp == NULL) { - os_free(result); - return; - } - tmp[res->num++] = result; - res->res = tmp; -} - - -static struct wpa_scan_results * wpa_driver_osx_get_scan_results(void *priv) -{ - struct wpa_driver_osx_data *drv = priv; - struct wpa_scan_results *res; - size_t i, num; - - if (drv->scan_results == NULL) - return 0; - - num = CFArrayGetCount(drv->scan_results); - - res = os_zalloc(sizeof(*res)); - if (res == NULL) - return NULL; - - for (i = 0; i < num; i++) - wpa_driver_osx_add_scan_entry(res, (WirelessNetworkInfo *) - CFDataGetBytePtr(CFArrayGetValueAtIndex( - drv->scan_results, i))); - - return res; -} - - -static void wpa_driver_osx_assoc_timeout(void *eloop_ctx, void *timeout_ctx) -{ - struct wpa_driver_osx_data *drv = eloop_ctx; - u8 bssid[ETH_ALEN]; - CFDictionaryRef ai; - - if (wpa_driver_osx_get_bssid(drv, bssid) != 0) { - eloop_register_timeout(1, 0, wpa_driver_osx_assoc_timeout, - drv, drv->ctx); - return; - } - - ai = WirelessGetAssociationInfo(drv->wireless_ctx); - if (ai) { - wpa_driver_osx_dump_dict(ai, "WirelessGetAssociationInfo"); - CFRelease(ai); - } else { - wpa_printf(MSG_DEBUG, "OSX: Failed to get association info"); - } - - wpa_supplicant_event(timeout_ctx, EVENT_ASSOC, NULL); -} - - -static int wpa_driver_osx_associate(void *priv, - struct wpa_driver_associate_params *params) -{ - struct wpa_driver_osx_data *drv = priv; - WirelessError err; - CFDataRef ssid; - CFStringRef key; - int assoc_type; - - ssid = CFDataCreate(kCFAllocatorDefault, params->ssid, - params->ssid_len); - if (ssid == NULL) - return -1; - - /* TODO: support for WEP */ - if (params->key_mgmt_suite == KEY_MGMT_PSK) { - if (params->passphrase == NULL) - return -1; - key = CFStringCreateWithCString(kCFAllocatorDefault, - params->passphrase, - kCFStringEncodingISOLatin1); - if (key == NULL) { - CFRelease(ssid); - return -1; - } - } else - key = NULL; - - if (params->key_mgmt_suite == KEY_MGMT_NONE) - assoc_type = 0; - else - assoc_type = 4; - - wpa_printf(MSG_DEBUG, "OSX: WirelessAssociate(type=%d key=%p)", - assoc_type, key); - err = WirelessAssociate(drv->wireless_ctx, assoc_type, ssid, key); - CFRelease(ssid); - if (key) - CFRelease(key); - if (err) { - wpa_printf(MSG_DEBUG, "OSX: WirelessAssociate failed: 0x%08x", - (unsigned int) err); - return -1; - } - - /* - * Driver is actually already associated; report association from an - * eloop callback. - */ - eloop_cancel_timeout(wpa_driver_osx_assoc_timeout, drv, drv->ctx); - eloop_register_timeout(0, 0, wpa_driver_osx_assoc_timeout, drv, - drv->ctx); - - return 0; -} - - -static int wpa_driver_osx_set_key(const char *ifname, void *priv, - enum wpa_alg alg, const u8 *addr, - int key_idx, int set_tx, const u8 *seq, - size_t seq_len, const u8 *key, - size_t key_len) -{ - struct wpa_driver_osx_data *drv = priv; - WirelessError err; - - if (alg == WPA_ALG_WEP) { - err = WirelessSetKey(drv->wireless_ctx, 1, key_idx, key_len, - key); - if (err != 0) { - wpa_printf(MSG_DEBUG, "OSX: WirelessSetKey failed: " - "0x%08x", (unsigned int) err); - return -1; - } - - return 0; - } - - if (alg == WPA_ALG_PMK) { - err = WirelessSetWPAKey(drv->wireless_ctx, 1, key_len, key); - if (err != 0) { - wpa_printf(MSG_DEBUG, "OSX: WirelessSetWPAKey failed: " - "0x%08x", (unsigned int) err); - return -1; - } - return 0; - } - - wpa_printf(MSG_DEBUG, "OSX: Unsupported set_key alg %d", alg); - return -1; -} - - -static int wpa_driver_osx_get_capa(void *priv, struct wpa_driver_capa *capa) -{ - os_memset(capa, 0, sizeof(*capa)); - - capa->key_mgmt = WPA_DRIVER_CAPA_KEY_MGMT_WPA | - WPA_DRIVER_CAPA_KEY_MGMT_WPA2 | - WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK | - WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK; - capa->enc = WPA_DRIVER_CAPA_ENC_WEP40 | WPA_DRIVER_CAPA_ENC_WEP104 | - WPA_DRIVER_CAPA_ENC_TKIP | WPA_DRIVER_CAPA_ENC_CCMP; - capa->auth = WPA_DRIVER_AUTH_OPEN | WPA_DRIVER_AUTH_SHARED | - WPA_DRIVER_AUTH_LEAP; - capa->flags = WPA_DRIVER_FLAGS_4WAY_HANDSHAKE; - - return 0; -} - - -static void * wpa_driver_osx_init(void *ctx, const char *ifname) -{ - struct wpa_driver_osx_data *drv; - WirelessError err; - u8 enabled, power; - - if (!WirelessIsAvailable()) { - wpa_printf(MSG_ERROR, "OSX: No wireless interface available"); - return NULL; - } - - drv = os_zalloc(sizeof(*drv)); - if (drv == NULL) - return NULL; - drv->ctx = ctx; - err = WirelessAttach(&drv->wireless_ctx, 0); - if (err) { - wpa_printf(MSG_ERROR, "OSX: WirelessAttach failed: %d", - (int) err); - os_free(drv); - return NULL; - } - - err = WirelessGetEnabled(drv->wireless_ctx, &enabled); - if (err) - wpa_printf(MSG_DEBUG, "OSX: WirelessGetEnabled failed: 0x%08x", - (unsigned int) err); - err = WirelessGetPower(drv->wireless_ctx, &power); - if (err) - wpa_printf(MSG_DEBUG, "OSX: WirelessGetPower failed: 0x%08x", - (unsigned int) err); - - wpa_printf(MSG_DEBUG, "OSX: Enabled=%d Power=%d", enabled, power); - - if (!enabled) { - err = WirelessSetEnabled(drv->wireless_ctx, 1); - if (err) { - wpa_printf(MSG_DEBUG, "OSX: WirelessSetEnabled failed:" - " 0x%08x", (unsigned int) err); - WirelessDetach(drv->wireless_ctx); - os_free(drv); - return NULL; - } - } - - if (!power) { - err = WirelessSetPower(drv->wireless_ctx, 1); - if (err) { - wpa_printf(MSG_DEBUG, "OSX: WirelessSetPower failed: " - "0x%08x", (unsigned int) err); - WirelessDetach(drv->wireless_ctx); - os_free(drv); - return NULL; - } - } - - return drv; -} - - -static void wpa_driver_osx_deinit(void *priv) -{ - struct wpa_driver_osx_data *drv = priv; - WirelessError err; - - eloop_cancel_timeout(wpa_driver_osx_scan_timeout, drv, drv->ctx); - eloop_cancel_timeout(wpa_driver_osx_assoc_timeout, drv, drv->ctx); - - err = WirelessSetPower(drv->wireless_ctx, 0); - if (err) { - wpa_printf(MSG_DEBUG, "OSX: WirelessSetPower(0) failed: " - "0x%08x", (unsigned int) err); - } - - err = WirelessDetach(drv->wireless_ctx); - if (err) { - wpa_printf(MSG_DEBUG, "OSX: WirelessDetach failed: 0x%08x", - (unsigned int) err); - } - - if (drv->scan_results) - CFRelease(drv->scan_results); - - os_free(drv); -} - - -const struct wpa_driver_ops wpa_driver_osx_ops = { - .name = "osx", - .desc = "Mac OS X Apple80211 driver", - .get_ssid = wpa_driver_osx_get_ssid, - .get_bssid = wpa_driver_osx_get_bssid, - .init = wpa_driver_osx_init, - .deinit = wpa_driver_osx_deinit, - .scan2 = wpa_driver_osx_scan, - .get_scan_results2 = wpa_driver_osx_get_scan_results, - .associate = wpa_driver_osx_associate, - .set_key = wpa_driver_osx_set_key, - .get_capa = wpa_driver_osx_get_capa, -}; diff -Nru wpa-1.0/src/drivers/driver_privsep.c wpa-2.1/src/drivers/driver_privsep.c --- wpa-1.0/src/drivers/driver_privsep.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/drivers/driver_privsep.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * WPA Supplicant - privilege separated driver interface * Copyright (c) 2007-2009, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -158,7 +152,7 @@ return NULL; } - results->res = os_zalloc(num * sizeof(struct wpa_scan_res *)); + results->res = os_calloc(num, sizeof(struct wpa_scan_res *)); if (results->res == NULL) { os_free(results); os_free(buf); @@ -310,17 +304,6 @@ } -static int wpa_driver_privsep_disassociate(void *priv, const u8 *addr, - int reason_code) -{ - //struct wpa_driver_privsep_data *drv = priv; - wpa_printf(MSG_DEBUG, "%s addr=" MACSTR " reason_code=%d", - __func__, MAC2STR(addr), reason_code); - wpa_printf(MSG_DEBUG, "%s - TODO", __func__); - return 0; -} - - static void wpa_driver_privsep_event_assoc(void *ctx, enum wpa_event_type event, u8 *buf, size_t len) @@ -657,7 +640,7 @@ os_strlcpy(addr.sun_path, drv->own_socket_path, sizeof(addr.sun_path)); if (bind(drv->priv_socket, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - perror("bind(PF_UNIX)"); + perror("privsep-set-params priv-sock: bind(PF_UNIX)"); close(drv->priv_socket); drv->priv_socket = -1; unlink(drv->own_socket_path); @@ -682,7 +665,7 @@ os_strlcpy(addr.sun_path, drv->own_cmd_path, sizeof(addr.sun_path)); if (bind(drv->cmd_socket, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - perror("bind(PF_UNIX)"); + perror("privsep-set-params cmd-sock: bind(PF_UNIX)"); close(drv->cmd_socket); drv->cmd_socket = -1; unlink(drv->own_cmd_path); @@ -742,7 +725,6 @@ .set_param = wpa_driver_privsep_set_param, .scan2 = wpa_driver_privsep_scan, .deauthenticate = wpa_driver_privsep_deauthenticate, - .disassociate = wpa_driver_privsep_disassociate, .associate = wpa_driver_privsep_associate, .get_capa = wpa_driver_privsep_get_capa, .get_mac_addr = wpa_driver_privsep_get_mac_addr, diff -Nru wpa-1.0/src/drivers/driver_ralink.c wpa-2.1/src/drivers/driver_ralink.c --- wpa-1.0/src/drivers/driver_ralink.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/drivers/driver_ralink.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,1498 +0,0 @@ -/* - * WPA Supplicant - driver interaction with Ralink Wireless Client - * Copyright (c) 2003-2006, Jouni Malinen - * Copyright (c) 2007, Snowpin Lee - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - * - */ - -#include "includes.h" -#include - -#include "wireless_copy.h" -#include "common.h" -#include "driver.h" -#include "l2_packet/l2_packet.h" -#include "eloop.h" -#include "common/ieee802_11_defs.h" -#include "priv_netlink.h" -#include "netlink.h" -#include "linux_ioctl.h" -#include "driver_ralink.h" - -static void wpa_driver_ralink_scan_timeout(void *eloop_ctx, void *timeout_ctx); - -#define MAX_SSID_LEN 32 - -struct wpa_driver_ralink_data { - void *ctx; - int ioctl_sock; - struct netlink_data *netlink; - char ifname[IFNAMSIZ + 1]; - u8 *assoc_req_ies; - size_t assoc_req_ies_len; - u8 *assoc_resp_ies; - size_t assoc_resp_ies_len; - int no_of_pmkid; - struct ndis_pmkid_entry *pmkid; - int we_version_compiled; - int ap_scan; - int scanning_done; - u8 g_driver_down; - BOOLEAN bAddWepKey; -}; - -static int ralink_set_oid(struct wpa_driver_ralink_data *drv, - unsigned short oid, char *data, int len) -{ - char *buf; - struct iwreq iwr; - - buf = os_zalloc(len); - if (buf == NULL) - return -1; - os_memset(&iwr, 0, sizeof(iwr)); - os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); - iwr.u.data.flags = oid; - iwr.u.data.flags |= OID_GET_SET_TOGGLE; - - if (data) - os_memcpy(buf, data, len); - - iwr.u.data.pointer = (caddr_t) buf; - iwr.u.data.length = len; - - if (ioctl(drv->ioctl_sock, RT_PRIV_IOCTL, &iwr) < 0) { - wpa_printf(MSG_DEBUG, "%s: oid=0x%x len (%d) failed", - __func__, oid, len); - os_free(buf); - return -1; - } - os_free(buf); - return 0; -} - -static int -ralink_get_new_driver_flag(struct wpa_driver_ralink_data *drv) -{ - struct iwreq iwr; - UCHAR enabled = 0; - - os_memset(&iwr, 0, sizeof(iwr)); - os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); - iwr.u.data.pointer = (UCHAR*) &enabled; - iwr.u.data.flags = RT_OID_NEW_DRIVER; - - if (ioctl(drv->ioctl_sock, RT_PRIV_IOCTL, &iwr) < 0) { - wpa_printf(MSG_DEBUG, "%s: failed", __func__); - return 0; - } - - return (enabled == 1) ? 1 : 0; -} - -static int wpa_driver_ralink_get_bssid(void *priv, u8 *bssid) -{ - struct wpa_driver_ralink_data *drv = priv; - struct iwreq iwr; - int ret = 0; - - if (drv->g_driver_down == 1) - return -1; - - wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); - - os_memset(&iwr, 0, sizeof(iwr)); - os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); - - if (ioctl(drv->ioctl_sock, SIOCGIWAP, &iwr) < 0) { - perror("ioctl[SIOCGIWAP]"); - ret = -1; - } - os_memcpy(bssid, iwr.u.ap_addr.sa_data, ETH_ALEN); - - return ret; -} - -static int wpa_driver_ralink_get_ssid(void *priv, u8 *ssid) -{ - struct wpa_driver_ralink_data *drv = priv; -#if 0 - struct wpa_supplicant *wpa_s = drv->ctx; - struct wpa_ssid *entry; -#endif - int ssid_len; - u8 bssid[ETH_ALEN]; - u8 ssid_str[MAX_SSID_LEN]; - struct iwreq iwr; -#if 0 - int result = 0; -#endif - int ret = 0; -#if 0 - BOOLEAN ieee8021x_mode = FALSE; - BOOLEAN ieee8021x_required_key = FALSE; -#endif - - if (drv->g_driver_down == 1) - return -1; - - wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); - - os_memset(&iwr, 0, sizeof(iwr)); - os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); - iwr.u.essid.pointer = (caddr_t) ssid; - iwr.u.essid.length = 32; - - if (ioctl(drv->ioctl_sock, SIOCGIWESSID, &iwr) < 0) { - perror("ioctl[SIOCGIWESSID]"); - ret = -1; - } else - ret = iwr.u.essid.length; - - if (ret <= 0) - return ret; - - ssid_len = ret; - os_memset(ssid_str, 0, MAX_SSID_LEN); - os_memcpy(ssid_str, ssid, ssid_len); - - if (drv->ap_scan == 0) { - /* Read BSSID form driver */ - if (wpa_driver_ralink_get_bssid(priv, bssid) < 0) { - wpa_printf(MSG_WARNING, "Could not read BSSID from " - "driver."); - return ret; - } - -#if 0 - entry = wpa_s->conf->ssid; - while (entry) { - if (!entry->disabled && ssid_len == entry->ssid_len && - os_memcmp(ssid_str, entry->ssid, ssid_len) == 0 && - (!entry->bssid_set || - os_memcmp(bssid, entry->bssid, ETH_ALEN) == 0)) { - /* match the config of driver */ - result = 1; - break; - } - entry = entry->next; - } - - if (result) { - wpa_printf(MSG_DEBUG, "Ready to set 802.1x mode and " - "ieee_required_keys parameters to driver"); - - /* set 802.1x mode and ieee_required_keys parameter */ - if (entry->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA) { - if ((entry->eapol_flags & (EAPOL_FLAG_REQUIRE_KEY_UNICAST | EAPOL_FLAG_REQUIRE_KEY_BROADCAST))) - ieee8021x_required_key = TRUE; - ieee8021x_mode = TRUE; - } - - if (ralink_set_oid(drv, OID_802_11_SET_IEEE8021X, (char *) &ieee8021x_mode, sizeof(BOOLEAN)) < 0) - { - wpa_printf(MSG_DEBUG, "RALINK: Failed to set OID_802_11_SET_IEEE8021X(%d)", (int) ieee8021x_mode); - } - else - { - wpa_printf(MSG_DEBUG, "ieee8021x_mode is %s", ieee8021x_mode ? "TRUE" : "FALSE"); - } - - if (ralink_set_oid(drv, OID_802_11_SET_IEEE8021X_REQUIRE_KEY, (char *) &ieee8021x_required_key, sizeof(BOOLEAN)) < 0) - { - wpa_printf(MSG_DEBUG, "ERROR: Failed to set OID_802_11_SET_IEEE8021X_REQUIRE_KEY(%d)", (int) ieee8021x_required_key); - } - else - { - wpa_printf(MSG_DEBUG, "ieee8021x_required_key is %s and eapol_flag(%d)", ieee8021x_required_key ? "TRUE" : "FALSE", - entry->eapol_flags); - } - } -#endif - } - - return ret; -} - -static int wpa_driver_ralink_set_ssid(struct wpa_driver_ralink_data *drv, - const u8 *ssid, size_t ssid_len) -{ - NDIS_802_11_SSID *buf; - int ret = 0; - struct iwreq iwr; - - wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); - - buf = os_zalloc(sizeof(NDIS_802_11_SSID)); - if (buf == NULL) - return -1; - os_memset(buf, 0, sizeof(buf)); - buf->SsidLength = ssid_len; - os_memcpy(buf->Ssid, ssid, ssid_len); - os_memset(&iwr, 0, sizeof(iwr)); - os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); - - iwr.u.data.flags = OID_802_11_SSID; - iwr.u.data.flags |= OID_GET_SET_TOGGLE; - iwr.u.data.pointer = (caddr_t) buf; - iwr.u.data.length = sizeof(NDIS_802_11_SSID); - - if (ioctl(drv->ioctl_sock, RT_PRIV_IOCTL, &iwr) < 0) { - perror("ioctl[RT_PRIV_IOCTL] -- OID_802_11_SSID"); - ret = -1; - } - os_free(buf); - return ret; -} - -static void wpa_driver_ralink_event_pmkid(struct wpa_driver_ralink_data *drv, - const u8 *data, size_t data_len) -{ - NDIS_802_11_PMKID_CANDIDATE_LIST *pmkid; - size_t i; - union wpa_event_data event; - - wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); - - if (data_len < 8) { - wpa_printf(MSG_DEBUG, "RALINK: Too short PMKID Candidate List " - "Event (len=%lu)", (unsigned long) data_len); - return; - } - pmkid = (NDIS_802_11_PMKID_CANDIDATE_LIST *) data; - wpa_printf(MSG_DEBUG, "RALINK: PMKID Candidate List Event - Version %d" - " NumCandidates %d", - (int) pmkid->Version, (int) pmkid->NumCandidates); - - if (pmkid->Version != 1) { - wpa_printf(MSG_DEBUG, "RALINK: Unsupported PMKID Candidate " - "List Version %d", (int) pmkid->Version); - return; - } - - if (data_len < 8 + pmkid->NumCandidates * sizeof(PMKID_CANDIDATE)) { - wpa_printf(MSG_DEBUG, "RALINK: PMKID Candidate List " - "underflow"); - - return; - } - - - - os_memset(&event, 0, sizeof(event)); - for (i = 0; i < pmkid->NumCandidates; i++) { - PMKID_CANDIDATE *p = &pmkid->CandidateList[i]; - wpa_printf(MSG_DEBUG, "RALINK: %lu: " MACSTR " Flags 0x%x", - (unsigned long) i, MAC2STR(p->BSSID), - (int) p->Flags); - os_memcpy(event.pmkid_candidate.bssid, p->BSSID, ETH_ALEN); - event.pmkid_candidate.index = i; - event.pmkid_candidate.preauth = - p->Flags & NDIS_802_11_PMKID_CANDIDATE_PREAUTH_ENABLED; - wpa_supplicant_event(drv->ctx, EVENT_PMKID_CANDIDATE, - &event); - } -} - -static int wpa_driver_ralink_set_pmkid(struct wpa_driver_ralink_data *drv) -{ - int len, count, i, ret; - struct ndis_pmkid_entry *entry; - NDIS_802_11_PMKID *p; - - wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); - - count = 0; - entry = drv->pmkid; - while (entry) { - count++; - if (count >= drv->no_of_pmkid) - break; - entry = entry->next; - } - len = 8 + count * sizeof(BSSID_INFO); - p = os_zalloc(len); - if (p == NULL) - return -1; - p->Length = len; - p->BSSIDInfoCount = count; - entry = drv->pmkid; - for (i = 0; i < count; i++) { - os_memcpy(&p->BSSIDInfo[i].BSSID, entry->bssid, ETH_ALEN); - os_memcpy(&p->BSSIDInfo[i].PMKID, entry->pmkid, 16); - entry = entry->next; - } - wpa_hexdump(MSG_MSGDUMP, "NDIS: OID_802_11_PMKID", - (const u8 *) p, len); - ret = ralink_set_oid(drv, OID_802_11_PMKID, (char *) p, len); - os_free(p); - return ret; -} - -static int wpa_driver_ralink_add_pmkid(void *priv, const u8 *bssid, - const u8 *pmkid) -{ - struct wpa_driver_ralink_data *drv = priv; - struct ndis_pmkid_entry *entry, *prev; - - if (drv->g_driver_down == 1) - return -1; - - wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); - - if (drv->no_of_pmkid == 0) - return 0; - - prev = NULL; - entry = drv->pmkid; - while (entry) { - if (os_memcmp(entry->bssid, bssid, ETH_ALEN) == 0) - break; - prev = entry; - entry = entry->next; - } - - if (entry) { - /* Replace existing entry for this BSSID and move it into the - * beginning of the list. */ - os_memcpy(entry->pmkid, pmkid, 16); - if (prev) { - prev->next = entry->next; - entry->next = drv->pmkid; - drv->pmkid = entry; - } - } else { - entry = os_malloc(sizeof(*entry)); - if (entry) { - os_memcpy(entry->bssid, bssid, ETH_ALEN); - os_memcpy(entry->pmkid, pmkid, 16); - entry->next = drv->pmkid; - drv->pmkid = entry; - } - } - - return wpa_driver_ralink_set_pmkid(drv); -} - - -static int wpa_driver_ralink_remove_pmkid(void *priv, const u8 *bssid, - const u8 *pmkid) -{ - struct wpa_driver_ralink_data *drv = priv; - struct ndis_pmkid_entry *entry, *prev; - - if (drv->g_driver_down == 1) - return -1; - - wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); - - if (drv->no_of_pmkid == 0) - return 0; - - entry = drv->pmkid; - prev = NULL; - drv->pmkid = NULL; - while (entry) { - if (os_memcmp(entry->bssid, bssid, ETH_ALEN) == 0 && - os_memcmp(entry->pmkid, pmkid, 16) == 0) { - if (prev) - prev->next = entry->next; - else - drv->pmkid = entry->next; - os_free(entry); - break; - } - prev = entry; - entry = entry->next; - } - return wpa_driver_ralink_set_pmkid(drv); -} - - -static int wpa_driver_ralink_flush_pmkid(void *priv) -{ - struct wpa_driver_ralink_data *drv = priv; - NDIS_802_11_PMKID p; - struct ndis_pmkid_entry *pmkid, *prev; - - if (drv->g_driver_down == 1) - return -1; - - wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); - - if (drv->no_of_pmkid == 0) - return 0; - - pmkid = drv->pmkid; - drv->pmkid = NULL; - while (pmkid) { - prev = pmkid; - pmkid = pmkid->next; - os_free(prev); - } - - os_memset(&p, 0, sizeof(p)); - p.Length = 8; - p.BSSIDInfoCount = 0; - wpa_hexdump(MSG_MSGDUMP, "NDIS: OID_802_11_PMKID (flush)", - (const u8 *) &p, 8); - return ralink_set_oid(drv, OID_802_11_PMKID, (char *) &p, 8); -} - -static void -wpa_driver_ralink_event_wireless_custom(struct wpa_driver_ralink_data *drv, - void *ctx, char *custom) -{ - union wpa_event_data data; - u8 *req_ies = NULL, *resp_ies = NULL; - - wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); - - wpa_printf(MSG_DEBUG, "Custom wireless event: '%s'", custom); - - os_memset(&data, 0, sizeof(data)); - /* Host AP driver */ - if (os_strncmp(custom, "MLME-MICHAELMICFAILURE.indication", 33) == 0) { - /* receive a MICFAILURE report */ - data.michael_mic_failure.unicast = - os_strstr(custom, " unicast") != NULL; - /* TODO: parse parameters(?) */ - wpa_supplicant_event(ctx, EVENT_MICHAEL_MIC_FAILURE, &data); - } else if (os_strncmp(custom, "ASSOCINFO_ReqIEs=", 17) == 0) { - /* receive assoc. req. IEs */ - char *spos; - int bytes; - - spos = custom + 17; - /*get IE's length */ - /* - * bytes = strlen(spos); ==> bug, bytes may less than original - * size by using this way to get size. snowpin 20070312 - * if (!bytes) - * return; - */ - bytes = drv->assoc_req_ies_len; - - req_ies = os_malloc(bytes); - if (req_ies == NULL) - return; - os_memcpy(req_ies, spos, bytes); - data.assoc_info.req_ies = req_ies; - data.assoc_info.req_ies_len = bytes; - - /* skip the '\0' byte */ - spos += bytes + 1; - - data.assoc_info.resp_ies = NULL; - data.assoc_info.resp_ies_len = 0; - - if (os_strncmp(spos, " RespIEs=", 9) == 0) { - /* receive assoc. resp. IEs */ - spos += 9; - /* get IE's length */ - bytes = os_strlen(spos); - if (!bytes) - goto done; - - resp_ies = os_malloc(bytes); - if (resp_ies == NULL) - goto done; - os_memcpy(resp_ies, spos, bytes); - data.assoc_info.resp_ies = resp_ies; - data.assoc_info.resp_ies_len = bytes; - } - - wpa_supplicant_event(ctx, EVENT_ASSOCINFO, &data); - - done: - /* free allocated memory */ - os_free(resp_ies); - os_free(req_ies); - } -} - -static void ralink_interface_up(struct wpa_driver_ralink_data *drv) -{ - union wpa_event_data event; - int enable_wpa_supplicant = 0; - drv->g_driver_down = 0; - os_memset(&event, 0, sizeof(event)); - os_snprintf(event.interface_status.ifname, - sizeof(event.interface_status.ifname), "%s", drv->ifname); - - event.interface_status.ievent = EVENT_INTERFACE_ADDED; - wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS, &event); - - if (drv->ap_scan == 1) - enable_wpa_supplicant = 1; - else - enable_wpa_supplicant = 2; - /* trigger driver support wpa_supplicant */ - if (ralink_set_oid(drv, RT_OID_WPA_SUPPLICANT_SUPPORT, - (PCHAR) &enable_wpa_supplicant, sizeof(UCHAR)) < 0) - { - wpa_printf(MSG_INFO, "RALINK: Failed to set " - "RT_OID_WPA_SUPPLICANT_SUPPORT(%d)", - (int) enable_wpa_supplicant); - wpa_printf(MSG_ERROR, "ralink. Driver does not support " - "wpa_supplicant"); - } -} - -static void -wpa_driver_ralink_event_wireless(struct wpa_driver_ralink_data *drv, - void *ctx, char *data, int len) -{ - struct iw_event iwe_buf, *iwe = &iwe_buf; - char *pos, *end, *custom, *buf, *assoc_info_buf, *info_pos; -#if 0 - BOOLEAN ieee8021x_required_key = FALSE; -#endif - - wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); - - assoc_info_buf = info_pos = NULL; - pos = data; - end = data + len; - - while (pos + IW_EV_LCP_LEN <= end) { - /* Event data may be unaligned, so make a local, aligned copy - * before processing. */ - os_memcpy(&iwe_buf, pos, IW_EV_LCP_LEN); - wpa_printf(MSG_DEBUG, "Wireless event: cmd=0x%x len=%d", - iwe->cmd, iwe->len); - if (iwe->len <= IW_EV_LCP_LEN) - return; - - custom = pos + IW_EV_POINT_LEN; - - if (drv->we_version_compiled > 18 && iwe->cmd == IWEVCUSTOM) { - /* WE-19 removed the pointer from struct iw_point */ - char *dpos = (char *) &iwe_buf.u.data.length; - int dlen = dpos - (char *) &iwe_buf; - os_memcpy(dpos, pos + IW_EV_LCP_LEN, - sizeof(struct iw_event) - dlen); - } else { - os_memcpy(&iwe_buf, pos, sizeof(struct iw_event)); - custom += IW_EV_POINT_OFF; - } - - switch (iwe->cmd) { - case IWEVCUSTOM: - if (custom + iwe->u.data.length > end) - return; - buf = os_malloc(iwe->u.data.length + 1); - if (buf == NULL) - return; - os_memcpy(buf, custom, iwe->u.data.length); - buf[iwe->u.data.length] = '\0'; - - if (drv->ap_scan == 1) { - if ((iwe->u.data.flags == RT_ASSOC_EVENT_FLAG) - || (iwe->u.data.flags == - RT_REQIE_EVENT_FLAG) || - (iwe->u.data.flags == RT_RESPIE_EVENT_FLAG) - || (iwe->u.data.flags == - RT_ASSOCINFO_EVENT_FLAG)) { - if (drv->scanning_done == 0) { - os_free(buf); - return; - } - } - } - - if (iwe->u.data.flags == RT_ASSOC_EVENT_FLAG) { - wpa_supplicant_event(ctx, EVENT_ASSOC, NULL); - wpa_printf(MSG_DEBUG, "Custom wireless event: " - "receive ASSOCIATED_EVENT !!!"); - } else if (iwe->u.data.flags == RT_REQIE_EVENT_FLAG) { - wpa_printf(MSG_DEBUG, "Custom wireless event: " - "receive ReqIEs !!!"); - drv->assoc_req_ies = - os_malloc(iwe->u.data.length); - if (drv->assoc_req_ies == NULL) { - os_free(buf); - return; - } - - drv->assoc_req_ies_len = iwe->u.data.length; - os_memcpy(drv->assoc_req_ies, custom, - iwe->u.data.length); - } else if (iwe->u.data.flags == RT_RESPIE_EVENT_FLAG) { - wpa_printf(MSG_DEBUG, "Custom wireless event: " - "receive RespIEs !!!"); - drv->assoc_resp_ies = - os_malloc(iwe->u.data.length); - if (drv->assoc_resp_ies == NULL) { - os_free(drv->assoc_req_ies); - drv->assoc_req_ies = NULL; - os_free(buf); - return; - } - - drv->assoc_resp_ies_len = iwe->u.data.length; - os_memcpy(drv->assoc_resp_ies, custom, - iwe->u.data.length); - } else if (iwe->u.data.flags == - RT_ASSOCINFO_EVENT_FLAG) { - wpa_printf(MSG_DEBUG, "Custom wireless event: " - "receive ASSOCINFO_EVENT !!!"); - - assoc_info_buf = - os_zalloc(drv->assoc_req_ies_len + - drv->assoc_resp_ies_len + 1); - - if (assoc_info_buf == NULL) { - os_free(drv->assoc_req_ies); - drv->assoc_req_ies = NULL; - os_free(drv->assoc_resp_ies); - drv->assoc_resp_ies = NULL; - os_free(buf); - return; - } - - if (drv->assoc_req_ies) { - os_memcpy(assoc_info_buf, - drv->assoc_req_ies, - drv->assoc_req_ies_len); - } - info_pos = assoc_info_buf + - drv->assoc_req_ies_len; - if (drv->assoc_resp_ies) { - os_memcpy(info_pos, - drv->assoc_resp_ies, - drv->assoc_resp_ies_len); - } - assoc_info_buf[drv->assoc_req_ies_len + - drv->assoc_resp_ies_len] = '\0'; - wpa_driver_ralink_event_wireless_custom( - drv, ctx, assoc_info_buf); - os_free(drv->assoc_req_ies); - drv->assoc_req_ies = NULL; - os_free(drv->assoc_resp_ies); - drv->assoc_resp_ies = NULL; - os_free(assoc_info_buf); - } else if (iwe->u.data.flags == RT_DISASSOC_EVENT_FLAG) - { - wpa_printf(MSG_DEBUG, "Custom wireless event: " - "receive DISASSOCIATED_EVENT !!!"); - wpa_supplicant_event(ctx, EVENT_DISASSOC, - NULL); - } else if (iwe->u.data.flags == RT_PMKIDCAND_FLAG) { - wpa_printf(MSG_DEBUG, "Custom wireless event: " - "receive PMKIDCAND_EVENT !!!"); - wpa_driver_ralink_event_pmkid( - drv, (const u8 *) custom, - iwe->u.data.length); - } else if (iwe->u.data.flags == RT_INTERFACE_DOWN) { - drv->g_driver_down = 1; - eloop_terminate(); - } else if (iwe->u.data.flags == RT_INTERFACE_UP) { - ralink_interface_up(drv); - } else { - wpa_driver_ralink_event_wireless_custom( - drv, ctx, buf); - } - os_free(buf); - break; - } - - pos += iwe->len; - } -} - -static void -wpa_driver_ralink_event_rtm_newlink(void *ctx, struct ifinfomsg *ifi, - u8 *buf, size_t len) -{ - struct wpa_driver_ralink_data *drv = ctx; - int attrlen, rta_len; - struct rtattr *attr; - - wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); - - wpa_hexdump(MSG_DEBUG, "ifi: ", (u8 *) ifi, sizeof(struct ifinfomsg)); - - attrlen = len; - wpa_printf(MSG_DEBUG, "attrlen=%d", attrlen); - attr = (struct rtattr *) buf; - wpa_hexdump(MSG_DEBUG, "attr1: ", (u8 *) attr, sizeof(struct rtattr)); - rta_len = RTA_ALIGN(sizeof(struct rtattr)); - wpa_hexdump(MSG_DEBUG, "attr2: ", (u8 *)attr,rta_len); - while (RTA_OK(attr, attrlen)) { - wpa_printf(MSG_DEBUG, "rta_type=%02x\n", attr->rta_type); - if (attr->rta_type == IFLA_WIRELESS) { - wpa_driver_ralink_event_wireless( - drv, ctx, - ((char *) attr) + rta_len, - attr->rta_len - rta_len); - } - attr = RTA_NEXT(attr, attrlen); - wpa_hexdump(MSG_DEBUG, "attr3: ", - (u8 *) attr, sizeof(struct rtattr)); - } -} - -static int -ralink_get_we_version_compiled(struct wpa_driver_ralink_data *drv) -{ - struct iwreq iwr; - UINT we_version_compiled = 0; - - os_memset(&iwr, 0, sizeof(iwr)); - os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); - iwr.u.data.pointer = (caddr_t) &we_version_compiled; - iwr.u.data.flags = RT_OID_WE_VERSION_COMPILED; - - if (ioctl(drv->ioctl_sock, RT_PRIV_IOCTL, &iwr) < 0) { - wpa_printf(MSG_DEBUG, "%s: failed", __func__); - return -1; - } - - drv->we_version_compiled = we_version_compiled; - - return 0; -} - -static void * wpa_driver_ralink_init(void *ctx, const char *ifname) -{ - int s; - struct wpa_driver_ralink_data *drv; - struct ifreq ifr; - UCHAR enable_wpa_supplicant = 0; - struct netlink_config *cfg; - - wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); - - /* open socket to kernel */ - if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { - perror("socket"); - return NULL; - } - /* do it */ - os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); - - if (ioctl(s, SIOCGIFINDEX, &ifr) < 0) { - perror(ifr.ifr_name); - return NULL; - } - - drv = os_zalloc(sizeof(*drv)); - if (drv == NULL) - return NULL; - - drv->scanning_done = 1; - drv->ap_scan = 1; /* for now - let's assume ap_scan=1 is used */ - drv->ctx = ctx; - os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname)); - drv->ioctl_sock = s; - drv->g_driver_down = 0; - - cfg = os_zalloc(sizeof(*cfg)); - if (cfg == NULL) { - close(drv->ioctl_sock); - os_free(drv); - return NULL; - } - cfg->ctx = drv; - cfg->newlink_cb = wpa_driver_ralink_event_rtm_newlink; - drv->netlink = netlink_init(cfg); - if (drv->netlink == NULL) { - os_free(cfg); - close(drv->ioctl_sock); - os_free(drv); - return NULL; - } - - drv->no_of_pmkid = 4; /* Number of PMKID saved supported */ - - linux_set_iface_flags(drv->ioctl_sock, drv->ifname, 1); - ralink_get_we_version_compiled(drv); - wpa_driver_ralink_flush_pmkid(drv); - - if (drv->ap_scan == 1) - enable_wpa_supplicant = 1; - else - enable_wpa_supplicant = 2; - /* trigger driver support wpa_supplicant */ - if (ralink_set_oid(drv, RT_OID_WPA_SUPPLICANT_SUPPORT, - (PCHAR) &enable_wpa_supplicant, sizeof(UCHAR)) < 0) - { - wpa_printf(MSG_DEBUG, "RALINK: Failed to set " - "RT_OID_WPA_SUPPLICANT_SUPPORT(%d)", - (int) enable_wpa_supplicant); - wpa_printf(MSG_ERROR, "RALINK: Driver does not support " - "wpa_supplicant"); - close(s); - close(drv->ioctl_sock); - os_free(drv); - return NULL; - } - - if (drv->ap_scan == 1) - drv->scanning_done = 0; - - return drv; -} - -static void wpa_driver_ralink_deinit(void *priv) -{ - struct wpa_driver_ralink_data *drv = priv; - UCHAR enable_wpa_supplicant; - - wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); - - enable_wpa_supplicant = 0; - - if (drv->g_driver_down == 0) { - /* trigger driver disable wpa_supplicant support */ - if (ralink_set_oid(drv, RT_OID_WPA_SUPPLICANT_SUPPORT, - (char *) &enable_wpa_supplicant, - sizeof(BOOLEAN)) < 0) { - wpa_printf(MSG_DEBUG, "RALINK: Failed to set " - "RT_OID_WPA_SUPPLICANT_SUPPORT(%d)", - (int) enable_wpa_supplicant); - } - - wpa_driver_ralink_flush_pmkid(drv); - - sleep(1); - /* linux_set_iface_flags(drv->ioctl_sock, drv->ifname, 0); */ - } - - eloop_cancel_timeout(wpa_driver_ralink_scan_timeout, drv, drv->ctx); - netlink_deinit(drv->netlink); - close(drv->ioctl_sock); - os_free(drv); -} - -static void wpa_driver_ralink_scan_timeout(void *eloop_ctx, void *timeout_ctx) -{ - struct wpa_driver_ralink_data *drv = eloop_ctx; - - wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); - - wpa_printf(MSG_DEBUG, "Scan timeout - try to get results"); - wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL); - - drv->scanning_done = 1; - -} - -static int wpa_driver_ralink_scan(void *priv, - struct wpa_driver_scan_params *params) -{ - struct wpa_driver_ralink_data *drv = priv; - struct iwreq iwr; - int ret = 0; - - if (drv->g_driver_down == 1) - return -1; - - wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); - -#if 0 - if (ssid_len > IW_ESSID_MAX_SIZE) { - wpa_printf(MSG_DEBUG, "%s: too long SSID (%lu)", - __FUNCTION__, (unsigned long) ssid_len); - return -1; - } - - /* wpa_driver_ralink_set_ssid(drv, ssid, ssid_len); */ -#endif - - if (ralink_set_oid(drv, RT_OID_WPS_PROBE_REQ_IE, - (char *) params->extra_ies, params->extra_ies_len) < - 0) { - wpa_printf(MSG_DEBUG, "RALINK: Failed to set " - "RT_OID_WPS_PROBE_REQ_IE"); - } - - os_memset(&iwr, 0, sizeof(iwr)); - os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); - - if (ioctl(drv->ioctl_sock, SIOCSIWSCAN, &iwr) < 0) { - perror("ioctl[SIOCSIWSCAN]"); - ret = -1; - } - - /* Not all drivers generate "scan completed" wireless event, so try to - * read results after a timeout. */ - eloop_cancel_timeout(wpa_driver_ralink_scan_timeout, drv, drv->ctx); - eloop_register_timeout(4, 0, wpa_driver_ralink_scan_timeout, drv, - drv->ctx); - - drv->scanning_done = 0; - - return ret; -} - -static struct wpa_scan_results * -wpa_driver_ralink_get_scan_results(void *priv) -{ - struct wpa_driver_ralink_data *drv = priv; - UCHAR *buf = NULL; - size_t buf_len; - NDIS_802_11_BSSID_LIST_EX *wsr; - NDIS_WLAN_BSSID_EX *wbi; - struct iwreq iwr; - size_t ap_num; - u8 *pos; - struct wpa_scan_results *res; - - if (drv->g_driver_down == 1) - return NULL; - wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); - - if (drv->we_version_compiled >= 17) - buf_len = 8192; - else - buf_len = 4096; - - for (;;) { - buf = os_zalloc(buf_len); - iwr.u.data.length = buf_len; - if (buf == NULL) - return NULL; - - wsr = (NDIS_802_11_BSSID_LIST_EX *) buf; - - wsr->NumberOfItems = 0; - os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); - iwr.u.data.pointer = (void *) buf; - iwr.u.data.flags = OID_802_11_BSSID_LIST; - - if (ioctl(drv->ioctl_sock, RT_PRIV_IOCTL, &iwr) == 0) - break; - - if (errno == E2BIG && buf_len < 65535) { - os_free(buf); - buf = NULL; - buf_len *= 2; - if (buf_len > 65535) - buf_len = 65535; /* 16-bit length field */ - wpa_printf(MSG_DEBUG, "Scan results did not fit - " - "trying larger buffer (%lu bytes)", - (unsigned long) buf_len); - } else { - perror("ioctl[RT_PRIV_IOCTL]"); - os_free(buf); - return NULL; - } - } - - res = os_zalloc(sizeof(*res)); - if (res == NULL) { - os_free(buf); - return NULL; - } - - res->res = os_zalloc(wsr->NumberOfItems * - sizeof(struct wpa_scan_res *)); - if (res->res == NULL) { - os_free(res); - os_free(buf); - return NULL; - } - - for (ap_num = 0, wbi = wsr->Bssid; ap_num < wsr->NumberOfItems; - ++ap_num) { - struct wpa_scan_res *r = NULL; - size_t extra_len = 0, var_ie_len = 0; - u8 *pos2; - - /* SSID data element */ - extra_len += 2 + wbi->Ssid.SsidLength; - var_ie_len = wbi->IELength - sizeof(NDIS_802_11_FIXED_IEs); - r = os_zalloc(sizeof(*r) + extra_len + var_ie_len); - if (r == NULL) - break; - res->res[res->num++] = r; - - wpa_printf(MSG_DEBUG, "SSID - %s", wbi->Ssid.Ssid); - /* get ie's */ - wpa_hexdump(MSG_DEBUG, "RALINK: AP IEs", - (u8 *) &wbi->IEs[0], wbi->IELength); - - os_memcpy(r->bssid, wbi->MacAddress, ETH_ALEN); - - extra_len += (2 + wbi->Ssid.SsidLength); - r->ie_len = extra_len + var_ie_len; - pos2 = (u8 *) (r + 1); - - /* - * Generate a fake SSID IE since the driver did not report - * a full IE list. - */ - *pos2++ = WLAN_EID_SSID; - *pos2++ = wbi->Ssid.SsidLength; - os_memcpy(pos2, wbi->Ssid.Ssid, wbi->Ssid.SsidLength); - pos2 += wbi->Ssid.SsidLength; - - r->freq = (wbi->Configuration.DSConfig / 1000); - - pos = (u8 *) wbi + sizeof(*wbi) - 1; - - pos += sizeof(NDIS_802_11_FIXED_IEs) - 2; - os_memcpy(&(r->caps), pos, 2); - pos += 2; - - if (wbi->IELength > sizeof(NDIS_802_11_FIXED_IEs)) - os_memcpy(pos2, pos, var_ie_len); - - wbi = (NDIS_WLAN_BSSID_EX *) ((u8 *) wbi + wbi->Length); - } - - os_free(buf); - return res; -} - -static int ralink_set_auth_mode(struct wpa_driver_ralink_data *drv, - NDIS_802_11_AUTHENTICATION_MODE mode) -{ - NDIS_802_11_AUTHENTICATION_MODE auth_mode = mode; - - wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); - - if (ralink_set_oid(drv, OID_802_11_AUTHENTICATION_MODE, - (char *) &auth_mode, sizeof(auth_mode)) < 0) { - wpa_printf(MSG_DEBUG, "RALINK: Failed to set " - "OID_802_11_AUTHENTICATION_MODE (%d)", - (int) auth_mode); - return -1; - } - return 0; -} - -static int ralink_set_encr_type(struct wpa_driver_ralink_data *drv, - NDIS_802_11_WEP_STATUS encr_type) -{ - NDIS_802_11_WEP_STATUS wep_status = encr_type; - - wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); - - if (ralink_set_oid(drv, OID_802_11_WEP_STATUS, - (char *) &wep_status, sizeof(wep_status)) < 0) { - wpa_printf(MSG_DEBUG, "RALINK: Failed to set " - "OID_802_11_WEP_STATUS (%d)", - (int) wep_status); - return -1; - } - return 0; -} - - -static int wpa_driver_ralink_remove_key(struct wpa_driver_ralink_data *drv, - int key_idx, const u8 *addr, - const u8 *bssid, int pairwise) -{ - NDIS_802_11_REMOVE_KEY rkey; - NDIS_802_11_KEY_INDEX _index; - int res, res2; - - wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); - - os_memset(&rkey, 0, sizeof(rkey)); - - rkey.Length = sizeof(rkey); - rkey.KeyIndex = key_idx; - - if (pairwise) - rkey.KeyIndex |= 1 << 30; - - os_memcpy(rkey.BSSID, bssid, ETH_ALEN); - - res = ralink_set_oid(drv, OID_802_11_REMOVE_KEY, (char *) &rkey, - sizeof(rkey)); - - /* AlbertY@20060210 removed it */ - if (0 /* !pairwise */) { - res2 = ralink_set_oid(drv, OID_802_11_REMOVE_WEP, - (char *) &_index, sizeof(_index)); - } else - res2 = 0; - - if (res < 0 && res2 < 0) - return res; - return 0; -} - -static int wpa_driver_ralink_add_wep(struct wpa_driver_ralink_data *drv, - int pairwise, int key_idx, int set_tx, - const u8 *key, size_t key_len) -{ - NDIS_802_11_WEP *wep; - size_t len; - int res; - - wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); - - len = 12 + key_len; - wep = os_zalloc(len); - if (wep == NULL) - return -1; - - wep->Length = len; - wep->KeyIndex = key_idx; - - if (set_tx) - wep->KeyIndex |= 0x80000000; - - wep->KeyLength = key_len; - os_memcpy(wep->KeyMaterial, key, key_len); - - wpa_hexdump_key(MSG_MSGDUMP, "RALINK: OID_802_11_ADD_WEP", - (const u8 *) wep, len); - res = ralink_set_oid(drv, OID_802_11_ADD_WEP, (char *) wep, len); - - os_free(wep); - - return res; -} - -static int wpa_driver_ralink_set_key(const char *ifname, void *priv, - enum wpa_alg alg, const u8 *addr, - int key_idx, int set_tx, - const u8 *seq, size_t seq_len, - const u8 *key, size_t key_len) -{ - struct wpa_driver_ralink_data *drv = priv; - size_t len, i; - NDIS_802_11_KEY *nkey; - int res, pairwise; - u8 bssid[ETH_ALEN]; - - if (drv->g_driver_down == 1) - return -1; - - wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); - - drv->bAddWepKey = FALSE; - - if (addr == NULL || is_broadcast_ether_addr(addr)) { - /* Group Key */ - pairwise = 0; - wpa_driver_ralink_get_bssid(drv, bssid); - } else { - /* Pairwise Key */ - pairwise = 1; - os_memcpy(bssid, addr, ETH_ALEN); - } - - if (alg == WPA_ALG_NONE || key_len == 0) { - return wpa_driver_ralink_remove_key(drv, key_idx, addr, bssid, - pairwise); - } - - if (alg == WPA_ALG_WEP) { - drv->bAddWepKey = TRUE; - return wpa_driver_ralink_add_wep(drv, pairwise, key_idx, - set_tx, key, key_len); - } - - len = 12 + 6 + 6 + 8 + key_len; - - nkey = os_zalloc(len); - if (nkey == NULL) - return -1; - - nkey->Length = len; - nkey->KeyIndex = key_idx; - - if (set_tx) - nkey->KeyIndex |= 1 << 31; - - if (pairwise) - nkey->KeyIndex |= 1 << 30; - - if (seq && seq_len) - nkey->KeyIndex |= 1 << 29; - - nkey->KeyLength = key_len; - os_memcpy(nkey->BSSID, bssid, ETH_ALEN); - - if (seq && seq_len) { - for (i = 0; i < seq_len; i++) - nkey->KeyRSC |= seq[i] << (i * 8); - } - if (alg == WPA_ALG_TKIP && key_len == 32) { - os_memcpy(nkey->KeyMaterial, key, 16); - os_memcpy(nkey->KeyMaterial + 16, key + 24, 8); - os_memcpy(nkey->KeyMaterial + 24, key + 16, 8); - } else { - os_memcpy(nkey->KeyMaterial, key, key_len); - } - - wpa_printf(MSG_DEBUG, "%s: alg=%d key_idx=%d set_tx=%d seq_len=%lu " - "key_len=%lu", __FUNCTION__, alg, key_idx, set_tx, - (unsigned long) seq_len, (unsigned long) key_len); - - wpa_hexdump_key(MSG_MSGDUMP, "RALINK: OID_802_11_ADD_KEY", - (const u8 *) nkey, len); - res = ralink_set_oid(drv, OID_802_11_ADD_KEY, (char *) nkey, len); - os_free(nkey); - - return res; -} - -static int wpa_driver_ralink_disassociate(void *priv, const u8 *addr, - int reason_code) -{ - struct wpa_driver_ralink_data *drv = priv; - - if (drv->g_driver_down == 1) - return -1; - wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); - if (ralink_set_oid(drv, OID_802_11_DISASSOCIATE, " ", 4) < 0) { - wpa_printf(MSG_DEBUG, "RALINK: Failed to set " - "OID_802_11_DISASSOCIATE"); - } - - return 0; -} - -static int wpa_driver_ralink_deauthenticate(void *priv, const u8 *addr, - int reason_code) -{ - struct wpa_driver_ralink_data *drv = priv; - - wpa_printf(MSG_DEBUG, "g_driver_down = %d", drv->g_driver_down); - - if (drv->g_driver_down == 1) - return -1; - - wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); - if (ralink_get_new_driver_flag(drv) == 0) { - return wpa_driver_ralink_disassociate(priv, addr, reason_code); - } else { - MLME_DEAUTH_REQ_STRUCT mlme; - os_memset(&mlme, 0, sizeof(MLME_DEAUTH_REQ_STRUCT)); - mlme.Reason = reason_code; - os_memcpy(mlme.Addr, addr, MAC_ADDR_LEN); - return ralink_set_oid(drv, OID_802_11_DEAUTHENTICATION, - (char *) &mlme, - sizeof(MLME_DEAUTH_REQ_STRUCT)); - } -} - -static int wpa_driver_ralink_set_gen_ie(void *priv, const u8 *ie, - size_t ie_len) -{ - struct wpa_driver_ralink_data *drv = priv; - struct iwreq iwr; - int ret = 0; - - os_memset(&iwr, 0, sizeof(iwr)); - os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); - iwr.u.data.pointer = (caddr_t) ie; - iwr.u.data.length = ie_len; - - wpa_hexdump(MSG_DEBUG, "wpa_driver_ralink_set_gen_ie: ", - (u8 *) ie, ie_len); - - if (ioctl(drv->ioctl_sock, SIOCSIWGENIE, &iwr) < 0) { - perror("ioctl[SIOCSIWGENIE]"); - ret = -1; - } - - return ret; -} - -static int -wpa_driver_ralink_associate(void *priv, - struct wpa_driver_associate_params *params) -{ - struct wpa_driver_ralink_data *drv = priv; - - NDIS_802_11_NETWORK_INFRASTRUCTURE mode; - NDIS_802_11_AUTHENTICATION_MODE auth_mode; - NDIS_802_11_WEP_STATUS encr; - BOOLEAN ieee8021xMode; - BOOLEAN ieee8021x_required_key = TRUE; - - if (drv->g_driver_down == 1) - return -1; - wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); - - if (params->mode == IEEE80211_MODE_IBSS) - mode = Ndis802_11IBSS; - else - mode = Ndis802_11Infrastructure; - - if (ralink_set_oid(drv, OID_802_11_INFRASTRUCTURE_MODE, - (char *) &mode, sizeof(mode)) < 0) { - wpa_printf(MSG_DEBUG, "RALINK: Failed to set " - "OID_802_11_INFRASTRUCTURE_MODE (%d)", - (int) mode); - /* Try to continue anyway */ - } - - if (params->key_mgmt_suite == KEY_MGMT_WPS) { - UCHAR enable_wps = 0x80; - /* trigger driver support wpa_supplicant */ - if (ralink_set_oid(drv, RT_OID_WPA_SUPPLICANT_SUPPORT, - (PCHAR) &enable_wps, sizeof(UCHAR)) < 0) { - wpa_printf(MSG_INFO, "RALINK: Failed to set " - "RT_OID_WPA_SUPPLICANT_SUPPORT (%d)", - (int) enable_wps); - } - - wpa_driver_ralink_set_gen_ie(priv, params->wpa_ie, - params->wpa_ie_len); - - ralink_set_auth_mode(drv, Ndis802_11AuthModeOpen); - - ralink_set_encr_type(drv, Ndis802_11EncryptionDisabled); - } else { -#ifdef CONFIG_WPS - UCHAR enable_wpa_supplicant; - - if (drv->ap_scan == 1) - enable_wpa_supplicant = 0x01; - else - enable_wpa_supplicant = 0x02; - - /* trigger driver support wpa_supplicant */ - if (ralink_set_oid(drv, RT_OID_WPA_SUPPLICANT_SUPPORT, - (PCHAR) &enable_wpa_supplicant, - sizeof(UCHAR)) < 0) { - wpa_printf(MSG_DEBUG, "RALINK: Failed to set " - "RT_OID_WPA_SUPPLICANT_SUPPORT (%d)", - (int) enable_wpa_supplicant); - } - - wpa_driver_ralink_set_gen_ie(priv, (u8 *) "", 0); -#endif /* CONFIG_WPS */ - - if (params->wpa_ie == NULL || params->wpa_ie_len == 0) { - if (params->auth_alg & WPA_AUTH_ALG_SHARED) { - if (params->auth_alg & WPA_AUTH_ALG_OPEN) - auth_mode = Ndis802_11AuthModeAutoSwitch; - else - auth_mode = Ndis802_11AuthModeShared; - } else - auth_mode = Ndis802_11AuthModeOpen; - } else if (params->wpa_ie[0] == WLAN_EID_RSN) { - if (params->key_mgmt_suite == KEY_MGMT_PSK) - auth_mode = Ndis802_11AuthModeWPA2PSK; - else - auth_mode = Ndis802_11AuthModeWPA2; - } else { - if (params->key_mgmt_suite == KEY_MGMT_WPA_NONE) - auth_mode = Ndis802_11AuthModeWPANone; - else if (params->key_mgmt_suite == KEY_MGMT_PSK) - auth_mode = Ndis802_11AuthModeWPAPSK; - else - auth_mode = Ndis802_11AuthModeWPA; - } - - switch (params->pairwise_suite) { - case CIPHER_CCMP: - encr = Ndis802_11Encryption3Enabled; - break; - case CIPHER_TKIP: - encr = Ndis802_11Encryption2Enabled; - break; - case CIPHER_WEP40: - case CIPHER_WEP104: - encr = Ndis802_11Encryption1Enabled; - break; - case CIPHER_NONE: - if (params->group_suite == CIPHER_CCMP) - encr = Ndis802_11Encryption3Enabled; - else if (params->group_suite == CIPHER_TKIP) - encr = Ndis802_11Encryption2Enabled; - else - encr = Ndis802_11EncryptionDisabled; - break; - default: - encr = Ndis802_11EncryptionDisabled; - break; - } - - ralink_set_auth_mode(drv, auth_mode); - - /* notify driver that IEEE8021x mode is enabled */ - if (params->key_mgmt_suite == KEY_MGMT_802_1X_NO_WPA) { - ieee8021xMode = TRUE; - if (drv->bAddWepKey) - ieee8021x_required_key = FALSE; - } else - ieee8021xMode = FALSE; - - if (ralink_set_oid(drv, OID_802_11_SET_IEEE8021X_REQUIRE_KEY, - (char *) &ieee8021x_required_key, - sizeof(BOOLEAN)) < 0) { - wpa_printf(MSG_DEBUG, "ERROR: Failed to set " - "OID_802_11_SET_IEEE8021X_REQUIRE_KEY(%d)", - (int) ieee8021x_required_key); - } else { - wpa_printf(MSG_DEBUG, "ieee8021x_required_key is %s", - ieee8021x_required_key ? "TRUE" : "FALSE"); - } - - if (ralink_set_oid(drv, OID_802_11_SET_IEEE8021X, - (char *) &ieee8021xMode, sizeof(BOOLEAN)) < - 0) { - wpa_printf(MSG_DEBUG, "RALINK: Failed to set " - "OID_802_11_SET_IEEE8021X(%d)", - (int) ieee8021xMode); - } - - ralink_set_encr_type(drv, encr); - - if ((ieee8021xMode == FALSE) && - (encr == Ndis802_11Encryption1Enabled)) { - /* static WEP */ - int enabled = 0; - if (ralink_set_oid(drv, OID_802_11_DROP_UNENCRYPTED, - (char *) &enabled, sizeof(enabled)) - < 0) { - wpa_printf(MSG_DEBUG, "RALINK: Failed to set " - "OID_802_11_DROP_UNENCRYPTED(%d)", - (int) encr); - } - } - } - - return wpa_driver_ralink_set_ssid(drv, params->ssid, params->ssid_len); -} - -static int -wpa_driver_ralink_set_countermeasures(void *priv, int enabled) -{ - struct wpa_driver_ralink_data *drv = priv; - if (drv->g_driver_down == 1) - return -1; - wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled); - return ralink_set_oid(drv, OID_SET_COUNTERMEASURES, (char *) &enabled, - sizeof(int)); -} - -const struct wpa_driver_ops wpa_driver_ralink_ops = { - .name = "ralink", - .desc = "Ralink Wireless Client driver", - .get_bssid = wpa_driver_ralink_get_bssid, - .get_ssid = wpa_driver_ralink_get_ssid, - .set_key = wpa_driver_ralink_set_key, - .init = wpa_driver_ralink_init, - .deinit = wpa_driver_ralink_deinit, - .set_countermeasures = wpa_driver_ralink_set_countermeasures, - .scan2 = wpa_driver_ralink_scan, - .get_scan_results2 = wpa_driver_ralink_get_scan_results, - .deauthenticate = wpa_driver_ralink_deauthenticate, - .disassociate = wpa_driver_ralink_disassociate, - .associate = wpa_driver_ralink_associate, - .add_pmkid = wpa_driver_ralink_add_pmkid, - .remove_pmkid = wpa_driver_ralink_remove_pmkid, - .flush_pmkid = wpa_driver_ralink_flush_pmkid, -}; diff -Nru wpa-1.0/src/drivers/driver_ralink.h wpa-2.1/src/drivers/driver_ralink.h --- wpa-1.0/src/drivers/driver_ralink.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/drivers/driver_ralink.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,383 +0,0 @@ -/* - * WPA Supplicant - driver_ralink exported functions - * Copyright (c) 2003-2005, Jouni Malinen - * Copyright (c) 2007, Snowpin Lee - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -// Ralink defined OIDs -#if WIRELESS_EXT <= 11 -#ifndef SIOCDEVPRIVATE -#define SIOCDEVPRIVATE 0x8BE0 -#endif -#define SIOCIWFIRSTPRIV SIOCDEVPRIVATE -#endif - -#define RT_PRIV_IOCTL (SIOCIWFIRSTPRIV + 0x0E) -#define RTPRIV_IOCTL_SET (SIOCIWFIRSTPRIV + 0x02) - -// IEEE 802.11 OIDs & Ralink defined OIDs ****** - -// (RaConfig Set/QueryInform) ==> -#define OID_GET_SET_TOGGLE 0x8000 - -#define OID_802_11_ADD_WEP 0x0112 -#define OID_802_11_REMOVE_WEP 0x0113 -#define OID_802_11_DISASSOCIATE 0x0114 -#define OID_802_11_PRIVACY_FILTER 0x0118 -#define OID_802_11_ASSOCIATION_INFORMATION 0x011E -#define OID_802_11_BSSID_LIST_SCAN 0x0508 -#define OID_802_11_SSID 0x0509 -#define OID_802_11_BSSID 0x050A -#define OID_802_11_WEP_STATUS 0x0510 -#define OID_802_11_AUTHENTICATION_MODE 0x0511 -#define OID_802_11_INFRASTRUCTURE_MODE 0x0512 -#define OID_802_11_TX_POWER_LEVEL 0x0517 -#define OID_802_11_REMOVE_KEY 0x0519 -#define OID_802_11_ADD_KEY 0x0520 -#define OID_802_11_DEAUTHENTICATION 0x0526 -#define OID_802_11_DROP_UNENCRYPTED 0x0527 -#define OID_802_11_BSSID_LIST 0x0609 -#define OID_802_3_CURRENT_ADDRESS 0x060A -#define OID_SET_COUNTERMEASURES 0x0616 -#define OID_802_11_SET_IEEE8021X 0x0617 // For IEEE8021x mode -#define OID_802_11_SET_IEEE8021X_REQUIRE_KEY 0x0618 // For DynamicWEP in IEEE802.1x mode -#define OID_802_11_PMKID 0x0620 -#define RT_OID_WPA_SUPPLICANT_SUPPORT 0x0621 // for trigger driver enable/disable wpa_supplicant support -#define RT_OID_WE_VERSION_COMPILED 0x0622 -#define RT_OID_NEW_DRIVER 0x0623 -#define RT_OID_WPS_PROBE_REQ_IE 0x0625 - -#define PACKED __attribute__ ((packed)) - -//wpa_supplicant event flags -#define RT_ASSOC_EVENT_FLAG 0x0101 -#define RT_DISASSOC_EVENT_FLAG 0x0102 -#define RT_REQIE_EVENT_FLAG 0x0103 -#define RT_RESPIE_EVENT_FLAG 0x0104 -#define RT_ASSOCINFO_EVENT_FLAG 0x0105 -#define RT_PMKIDCAND_FLAG 0x0106 -#define RT_INTERFACE_DOWN 0x0107 -#define RT_INTERFACE_UP 0x0108 - -// -// IEEE 802.11 Structures and definitions -// -// new types for Media Specific Indications - -#ifndef ULONG -#define CHAR char -#define INT int -#define SHORT int -#define UINT u32 -#undef ULONG -//#define ULONG u32 -#define ULONG unsigned long /* 32-bit in 32-bit CPU or 64-bit in 64-bit CPU */ -#define USHORT unsigned short -#define UCHAR unsigned char - -#define uint32 u32 -#define uint8 u8 - - -#define BOOLEAN u8 -//#define LARGE_INTEGER s64 -#define VOID void -#define LONG long -#define LONGLONG s64 -#define ULONGLONG u64 -typedef VOID *PVOID; -typedef CHAR *PCHAR; -typedef UCHAR *PUCHAR; -typedef USHORT *PUSHORT; -typedef LONG *PLONG; -typedef ULONG *PULONG; - -typedef union _LARGE_INTEGER { - struct { - ULONG LowPart; - LONG HighPart; - }vv; - struct { - ULONG LowPart; - LONG HighPart; - } u; - s64 QuadPart; -} LARGE_INTEGER; - -#endif - -#define NDIS_802_11_LENGTH_SSID 32 -#define NDIS_802_11_LENGTH_RATES 8 -#define NDIS_802_11_LENGTH_RATES_EX 16 -#define MAX_LEN_OF_SSID 32 -#define MAC_ADDR_LEN 6 - -typedef UCHAR NDIS_802_11_MAC_ADDRESS[6]; - -// mask for authentication/integrity fields -#define NDIS_802_11_AUTH_REQUEST_AUTH_FIELDS 0x0f - -#define NDIS_802_11_AUTH_REQUEST_REAUTH 0x01 -#define NDIS_802_11_AUTH_REQUEST_KEYUPDATE 0x02 -#define NDIS_802_11_AUTH_REQUEST_PAIRWISE_ERROR 0x06 -#define NDIS_802_11_AUTH_REQUEST_GROUP_ERROR 0x0E - -// Added new types for OFDM 5G and 2.4G -typedef enum _NDIS_802_11_NETWORK_TYPE -{ - Ndis802_11FH, - Ndis802_11DS, - Ndis802_11OFDM5, - Ndis802_11OFDM24, - Ndis802_11Automode, - Ndis802_11NetworkTypeMax // not a real type, defined as an upper bound -} NDIS_802_11_NETWORK_TYPE, *PNDIS_802_11_NETWORK_TYPE; - -// -// Received Signal Strength Indication -// -typedef LONG NDIS_802_11_RSSI; // in dBm - -typedef struct _NDIS_802_11_CONFIGURATION_FH -{ - ULONG Length; // Length of structure - ULONG HopPattern; // As defined by 802.11, MSB set - ULONG HopSet; // to one if non-802.11 - ULONG DwellTime; // units are Kusec -} NDIS_802_11_CONFIGURATION_FH, *PNDIS_802_11_CONFIGURATION_FH; - -typedef struct _NDIS_802_11_CONFIGURATION -{ - ULONG Length; // Length of structure - ULONG BeaconPeriod; // units are Kusec - ULONG ATIMWindow; // units are Kusec - ULONG DSConfig; // Frequency, units are kHz - NDIS_802_11_CONFIGURATION_FH FHConfig; -} NDIS_802_11_CONFIGURATION, *PNDIS_802_11_CONFIGURATION; - -typedef ULONG NDIS_802_11_KEY_INDEX; -typedef ULONGLONG NDIS_802_11_KEY_RSC; - -// Key mapping keys require a BSSID -typedef struct _NDIS_802_11_KEY -{ - UINT Length; // Length of this structure - UINT KeyIndex; - UINT KeyLength; // length of key in bytes - NDIS_802_11_MAC_ADDRESS BSSID; - NDIS_802_11_KEY_RSC KeyRSC; - UCHAR KeyMaterial[1]; // variable length depending on above field -} NDIS_802_11_KEY, *PNDIS_802_11_KEY; - -typedef struct _NDIS_802_11_REMOVE_KEY -{ - UINT Length; // Length of this structure - UINT KeyIndex; - NDIS_802_11_MAC_ADDRESS BSSID; -} NDIS_802_11_REMOVE_KEY, *PNDIS_802_11_REMOVE_KEY; - -typedef struct PACKED _NDIS_802_11_WEP -{ - UINT Length; // Length of this structure - UINT KeyIndex; // 0 is the per-client key, 1-N are the - // global keys - UINT KeyLength; // length of key in bytes - UCHAR KeyMaterial[1];// variable length depending on above field -} NDIS_802_11_WEP, *PNDIS_802_11_WEP; - - -typedef enum _NDIS_802_11_NETWORK_INFRASTRUCTURE -{ - Ndis802_11IBSS, - Ndis802_11Infrastructure, - Ndis802_11AutoUnknown, - Ndis802_11InfrastructureMax // Not a real value, defined as upper bound -} NDIS_802_11_NETWORK_INFRASTRUCTURE, *PNDIS_802_11_NETWORK_INFRASTRUCTURE; - -// PMKID Structures -typedef UCHAR NDIS_802_11_PMKID_VALUE[16]; - -typedef struct _BSSID_INFO -{ - NDIS_802_11_MAC_ADDRESS BSSID; - NDIS_802_11_PMKID_VALUE PMKID; -} BSSID_INFO, *PBSSID_INFO; - -typedef struct _NDIS_802_11_PMKID -{ - ULONG Length; - ULONG BSSIDInfoCount; - BSSID_INFO BSSIDInfo[1]; -} NDIS_802_11_PMKID, *PNDIS_802_11_PMKID; - -//Added new types for PMKID Candidate lists. -typedef struct _PMKID_CANDIDATE { - NDIS_802_11_MAC_ADDRESS BSSID; - ULONG Flags; -} PMKID_CANDIDATE, *PPMKID_CANDIDATE; - -typedef struct _NDIS_802_11_PMKID_CANDIDATE_LIST -{ - ULONG Version; // Version of the structure - ULONG NumCandidates; // No. of pmkid candidates - PMKID_CANDIDATE CandidateList[1]; -} NDIS_802_11_PMKID_CANDIDATE_LIST, *PNDIS_802_11_PMKID_CANDIDATE_LIST; - -//Flags for PMKID Candidate list structure -#define NDIS_802_11_PMKID_CANDIDATE_PREAUTH_ENABLED 0x01 - -// Add new authentication modes -typedef enum _NDIS_802_11_AUTHENTICATION_MODE -{ - Ndis802_11AuthModeOpen, - Ndis802_11AuthModeShared, - Ndis802_11AuthModeAutoSwitch, - Ndis802_11AuthModeWPA, - Ndis802_11AuthModeWPAPSK, - Ndis802_11AuthModeWPANone, - Ndis802_11AuthModeWPA2, - Ndis802_11AuthModeWPA2PSK, - Ndis802_11AuthModeMax // Not a real mode, defined as upper bound -} NDIS_802_11_AUTHENTICATION_MODE, *PNDIS_802_11_AUTHENTICATION_MODE; - -typedef UCHAR NDIS_802_11_RATES[NDIS_802_11_LENGTH_RATES]; // Set of 8 data rates -typedef UCHAR NDIS_802_11_RATES_EX[NDIS_802_11_LENGTH_RATES_EX]; // Set of 16 data rates - -typedef struct PACKED _NDIS_802_11_SSID -{ - INT SsidLength; // length of SSID field below, in bytes; - // this can be zero. - UCHAR Ssid[NDIS_802_11_LENGTH_SSID]; // SSID information field -} NDIS_802_11_SSID, *PNDIS_802_11_SSID; - - -typedef struct PACKED _NDIS_WLAN_BSSID -{ - ULONG Length; // Length of this structure - NDIS_802_11_MAC_ADDRESS MacAddress; // BSSID - UCHAR Reserved[2]; - NDIS_802_11_SSID Ssid; // SSID - ULONG Privacy; // WEP encryption requirement - NDIS_802_11_RSSI Rssi; // receive signal - // strength in dBm - NDIS_802_11_NETWORK_TYPE NetworkTypeInUse; - NDIS_802_11_CONFIGURATION Configuration; - NDIS_802_11_NETWORK_INFRASTRUCTURE InfrastructureMode; - NDIS_802_11_RATES SupportedRates; -} NDIS_WLAN_BSSID, *PNDIS_WLAN_BSSID; - -typedef struct PACKED _NDIS_802_11_BSSID_LIST -{ - UINT NumberOfItems; // in list below, at least 1 - NDIS_WLAN_BSSID Bssid[1]; -} NDIS_802_11_BSSID_LIST, *PNDIS_802_11_BSSID_LIST; - -// Added Capabilities, IELength and IEs for each BSSID -typedef struct PACKED _NDIS_WLAN_BSSID_EX -{ - ULONG Length; // Length of this structure - NDIS_802_11_MAC_ADDRESS MacAddress; // BSSID - UCHAR Reserved[2]; - NDIS_802_11_SSID Ssid; // SSID - UINT Privacy; // WEP encryption requirement - NDIS_802_11_RSSI Rssi; // receive signal - // strength in dBm - NDIS_802_11_NETWORK_TYPE NetworkTypeInUse; - NDIS_802_11_CONFIGURATION Configuration; - NDIS_802_11_NETWORK_INFRASTRUCTURE InfrastructureMode; - NDIS_802_11_RATES_EX SupportedRates; - ULONG IELength; - UCHAR IEs[1]; -} NDIS_WLAN_BSSID_EX, *PNDIS_WLAN_BSSID_EX; - -typedef struct PACKED _NDIS_802_11_BSSID_LIST_EX -{ - UINT NumberOfItems; // in list below, at least 1 - NDIS_WLAN_BSSID_EX Bssid[1]; -} NDIS_802_11_BSSID_LIST_EX, *PNDIS_802_11_BSSID_LIST_EX; - -typedef struct PACKED _NDIS_802_11_FIXED_IEs -{ - UCHAR Timestamp[8]; - USHORT BeaconInterval; - USHORT Capabilities; -} NDIS_802_11_FIXED_IEs, *PNDIS_802_11_FIXED_IEs; - -// Added new encryption types -// Also aliased typedef to new name -typedef enum _NDIS_802_11_WEP_STATUS -{ - Ndis802_11WEPEnabled, - Ndis802_11Encryption1Enabled = Ndis802_11WEPEnabled, - Ndis802_11WEPDisabled, - Ndis802_11EncryptionDisabled = Ndis802_11WEPDisabled, - Ndis802_11WEPKeyAbsent, - Ndis802_11Encryption1KeyAbsent = Ndis802_11WEPKeyAbsent, - Ndis802_11WEPNotSupported, - Ndis802_11EncryptionNotSupported = Ndis802_11WEPNotSupported, - Ndis802_11Encryption2Enabled, - Ndis802_11Encryption2KeyAbsent, - Ndis802_11Encryption3Enabled, - Ndis802_11Encryption3KeyAbsent -} NDIS_802_11_WEP_STATUS, *PNDIS_802_11_WEP_STATUS, - NDIS_802_11_ENCRYPTION_STATUS, *PNDIS_802_11_ENCRYPTION_STATUS; - -typedef enum _NDIS_802_11_RELOAD_DEFAULTS -{ - Ndis802_11ReloadWEPKeys -} NDIS_802_11_RELOAD_DEFAULTS, *PNDIS_802_11_RELOAD_DEFAULTS; - -#define NDIS_802_11_AI_REQFI_CAPABILITIES 1 -#define NDIS_802_11_AI_REQFI_LISTENINTERVAL 2 -#define NDIS_802_11_AI_REQFI_CURRENTAPADDRESS 4 - -#define NDIS_802_11_AI_RESFI_CAPABILITIES 1 -#define NDIS_802_11_AI_RESFI_STATUSCODE 2 -#define NDIS_802_11_AI_RESFI_ASSOCIATIONID 4 - -typedef struct _NDIS_802_11_AI_REQFI -{ - USHORT Capabilities; - USHORT ListenInterval; - NDIS_802_11_MAC_ADDRESS CurrentAPAddress; -} NDIS_802_11_AI_REQFI, *PNDIS_802_11_AI_REQFI; - -typedef struct _NDIS_802_11_AI_RESFI -{ - USHORT Capabilities; - USHORT StatusCode; - USHORT AssociationId; -} NDIS_802_11_AI_RESFI, *PNDIS_802_11_AI_RESFI; - -typedef struct _NDIS_802_11_ASSOCIATION_INFORMATION -{ - ULONG Length; - USHORT AvailableRequestFixedIEs; - NDIS_802_11_AI_REQFI RequestFixedIEs; - ULONG RequestIELength; - ULONG OffsetRequestIEs; - USHORT AvailableResponseFixedIEs; - NDIS_802_11_AI_RESFI ResponseFixedIEs; - ULONG ResponseIELength; - ULONG OffsetResponseIEs; -} NDIS_802_11_ASSOCIATION_INFORMATION, *PNDIS_802_11_ASSOCIATION_INFORMATION; - -struct ndis_pmkid_entry { - struct ndis_pmkid_entry *next; - u8 bssid[ETH_ALEN]; - u8 pmkid[16]; -}; - -typedef struct _MLME_DEAUTH_REQ_STRUCT { - UCHAR Addr[MAC_ADDR_LEN]; - USHORT Reason; -} MLME_DEAUTH_REQ_STRUCT, *PMLME_DEAUTH_REQ_STRUCT; diff -Nru wpa-1.0/src/drivers/driver_roboswitch.c wpa-2.1/src/drivers/driver_roboswitch.c --- wpa-1.0/src/drivers/driver_roboswitch.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/drivers/driver_roboswitch.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * WPA Supplicant - roboswitch driver interface * Copyright (c) 2008-2009 Jouke Witteveen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff -Nru wpa-1.0/src/drivers/drivers.c wpa-2.1/src/drivers/drivers.c --- wpa-1.0/src/drivers/drivers.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/drivers/drivers.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,18 +2,13 @@ * Driver interface list * Copyright (c) 2004-2005, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ -#include "includes.h" - +#include "utils/includes.h" +#include "utils/common.h" +#include "driver.h" #ifdef CONFIG_DRIVER_WEXT extern struct wpa_driver_ops wpa_driver_wext_ops; /* driver_wext.c */ @@ -27,12 +22,12 @@ #ifdef CONFIG_DRIVER_MADWIFI extern struct wpa_driver_ops wpa_driver_madwifi_ops; /* driver_madwifi.c */ #endif /* CONFIG_DRIVER_MADWIFI */ -#ifdef CONFIG_DRIVER_BROADCOM -extern struct wpa_driver_ops wpa_driver_broadcom_ops; /* driver_broadcom.c */ -#endif /* CONFIG_DRIVER_BROADCOM */ #ifdef CONFIG_DRIVER_BSD extern struct wpa_driver_ops wpa_driver_bsd_ops; /* driver_bsd.c */ #endif /* CONFIG_DRIVER_BSD */ +#ifdef CONFIG_DRIVER_OPENBSD +extern struct wpa_driver_ops wpa_driver_openbsd_ops; /* driver_openbsd.c */ +#endif /* CONFIG_DRIVER_OPENBSD */ #ifdef CONFIG_DRIVER_NDIS extern struct wpa_driver_ops wpa_driver_ndis_ops; /* driver_ndis.c */ #endif /* CONFIG_DRIVER_NDIS */ @@ -42,15 +37,6 @@ #ifdef CONFIG_DRIVER_TEST extern struct wpa_driver_ops wpa_driver_test_ops; /* driver_test.c */ #endif /* CONFIG_DRIVER_TEST */ -#ifdef CONFIG_DRIVER_RALINK -extern struct wpa_driver_ops wpa_driver_ralink_ops; /* driver_ralink.c */ -#endif /* CONFIG_DRIVER_RALINK */ -#ifdef CONFIG_DRIVER_OSX -extern struct wpa_driver_ops wpa_driver_osx_ops; /* driver_osx.m */ -#endif /* CONFIG_DRIVER_OSX */ -#ifdef CONFIG_DRIVER_IPHONE -extern struct wpa_driver_ops wpa_driver_iphone_ops; /* driver_iphone.m */ -#endif /* CONFIG_DRIVER_IPHONE */ #ifdef CONFIG_DRIVER_ROBOSWITCH /* driver_roboswitch.c */ extern struct wpa_driver_ops wpa_driver_roboswitch_ops; @@ -65,24 +51,24 @@ struct wpa_driver_ops *wpa_drivers[] = { -#ifdef CONFIG_DRIVER_WEXT - &wpa_driver_wext_ops, -#endif /* CONFIG_DRIVER_WEXT */ #ifdef CONFIG_DRIVER_NL80211 &wpa_driver_nl80211_ops, #endif /* CONFIG_DRIVER_NL80211 */ +#ifdef CONFIG_DRIVER_WEXT + &wpa_driver_wext_ops, +#endif /* CONFIG_DRIVER_WEXT */ #ifdef CONFIG_DRIVER_HOSTAP &wpa_driver_hostap_ops, #endif /* CONFIG_DRIVER_HOSTAP */ #ifdef CONFIG_DRIVER_MADWIFI &wpa_driver_madwifi_ops, #endif /* CONFIG_DRIVER_MADWIFI */ -#ifdef CONFIG_DRIVER_BROADCOM - &wpa_driver_broadcom_ops, -#endif /* CONFIG_DRIVER_BROADCOM */ #ifdef CONFIG_DRIVER_BSD &wpa_driver_bsd_ops, #endif /* CONFIG_DRIVER_BSD */ +#ifdef CONFIG_DRIVER_OPENBSD + &wpa_driver_openbsd_ops, +#endif /* CONFIG_DRIVER_OPENBSD */ #ifdef CONFIG_DRIVER_NDIS &wpa_driver_ndis_ops, #endif /* CONFIG_DRIVER_NDIS */ @@ -92,15 +78,6 @@ #ifdef CONFIG_DRIVER_TEST &wpa_driver_test_ops, #endif /* CONFIG_DRIVER_TEST */ -#ifdef CONFIG_DRIVER_RALINK - &wpa_driver_ralink_ops, -#endif /* CONFIG_DRIVER_RALINK */ -#ifdef CONFIG_DRIVER_OSX - &wpa_driver_osx_ops, -#endif /* CONFIG_DRIVER_OSX */ -#ifdef CONFIG_DRIVER_IPHONE - &wpa_driver_iphone_ops, -#endif /* CONFIG_DRIVER_IPHONE */ #ifdef CONFIG_DRIVER_ROBOSWITCH &wpa_driver_roboswitch_ops, #endif /* CONFIG_DRIVER_ROBOSWITCH */ diff -Nru wpa-1.0/src/drivers/drivers.mak wpa-2.1/src/drivers/drivers.mak --- wpa-1.0/src/drivers/drivers.mak 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/drivers/drivers.mak 2014-02-04 11:23:35.000000000 +0000 @@ -12,29 +12,11 @@ ##### COMMON DRIVERS -ifdef CONFIG_DRIVER_HOSTAP -DRV_CFLAGS += -DCONFIG_DRIVER_HOSTAP -DRV_OBJS += ../src/drivers/driver_hostap.o -CONFIG_WIRELESS_EXTENSION=y -NEED_AP_MLME=y -NEED_NETLINK=y -NEED_LINUX_IOCTL=y -endif - ifdef CONFIG_DRIVER_WIRED DRV_CFLAGS += -DCONFIG_DRIVER_WIRED DRV_OBJS += ../src/drivers/driver_wired.o endif -ifdef CONFIG_DRIVER_MADWIFI -DRV_CFLAGS += -DCONFIG_DRIVER_MADWIFI -DRV_OBJS += ../src/drivers/driver_madwifi.o -CONFIG_WIRELESS_EXTENSION=y -CONFIG_L2_PACKET=linux -NEED_NETLINK=y -NEED_LINUX_IOCTL=y -endif - ifdef CONFIG_DRIVER_NL80211 DRV_CFLAGS += -DCONFIG_DRIVER_NL80211 DRV_OBJS += ../src/drivers/driver_nl80211.o @@ -48,7 +30,7 @@ ifdef CONFIG_LIBNL32 DRV_LIBS += -lnl-3 DRV_LIBS += -lnl-genl-3 - DRV_CFLAGS += -DCONFIG_LIBNL20 + DRV_CFLAGS += -DCONFIG_LIBNL20 -I/usr/include/libnl3 else ifdef CONFIG_LIBNL_TINY DRV_LIBS += -lnl-tiny @@ -73,6 +55,14 @@ CONFIG_DNET_PCAP=y endif +ifdef CONFIG_DRIVER_OPENBSD +ifndef CONFIG_L2_PACKET +CONFIG_L2_PACKET=freebsd +endif +DRV_CFLAGS += -DCONFIG_DRIVER_OPENBSD +DRV_OBJS += ../src/drivers/driver_openbsd.o +endif + ifdef CONFIG_DRIVER_TEST DRV_CFLAGS += -DCONFIG_DRIVER_TEST DRV_OBJS += ../src/drivers/driver_test.o @@ -86,6 +76,24 @@ ##### PURE AP DRIVERS +ifdef CONFIG_DRIVER_HOSTAP +DRV_AP_CFLAGS += -DCONFIG_DRIVER_HOSTAP +DRV_AP_OBJS += ../src/drivers/driver_hostap.o +CONFIG_WIRELESS_EXTENSION=y +NEED_AP_MLME=y +NEED_NETLINK=y +NEED_LINUX_IOCTL=y +endif + +ifdef CONFIG_DRIVER_MADWIFI +DRV_AP_CFLAGS += -DCONFIG_DRIVER_MADWIFI +DRV_AP_OBJS += ../src/drivers/driver_madwifi.o +CONFIG_WIRELESS_EXTENSION=y +CONFIG_L2_PACKET=linux +NEED_NETLINK=y +NEED_LINUX_IOCTL=y +endif + ifdef CONFIG_DRIVER_ATHEROS DRV_AP_CFLAGS += -DCONFIG_DRIVER_ATHEROS DRV_AP_OBJS += ../src/drivers/driver_atheros.o @@ -104,18 +112,6 @@ NEED_RFKILL=y endif -ifdef CONFIG_DRIVER_RALINK -DRV_WPA_CFLAGS += -DCONFIG_DRIVER_RALINK -DRV_WPA_OBJS += ../src/drivers/driver_ralink.o -NEED_NETLINK=y -NEED_LINUX_IOCTL=y -endif - -ifdef CONFIG_DRIVER_BROADCOM -DRV_WPA_CFLAGS += -DCONFIG_DRIVER_BROADCOM -DRV_WPA_OBJS += ../src/drivers/driver_broadcom.o -endif - ifdef CONFIG_DRIVER_NDIS DRV_WPA_CFLAGS += -DCONFIG_DRIVER_NDIS DRV_WPA_OBJS += ../src/drivers/driver_ndis.o @@ -131,20 +127,6 @@ endif endif -ifdef CONFIG_DRIVER_OSX -DRV_WPA_CFLAGS += -DCONFIG_DRIVER_OSX -DRV_WPA_OBJS += ../src/drivers/driver_osx.o -DRV_WPA_LDFLAGS += -framework CoreFoundation -DRV_WPA_LDFLAGS += -F/System/Library/PrivateFrameworks -framework Apple80211 -endif - -ifdef CONFIG_DRIVER_IPHONE -DRV_WPA_CFLAGS += -DCONFIG_DRIVER_IPHONE -DRV_WPA_OBJS += ../src/drivers/driver_iphone.o -DRV_WPA_OBJS += ../src/drivers/MobileApple80211.o -DRV_WPA_LDFLAGS += -framework CoreFoundation -endif - ifdef CONFIG_DRIVER_ROBOSWITCH DRV_WPA_CFLAGS += -DCONFIG_DRIVER_ROBOSWITCH DRV_WPA_OBJS += ../src/drivers/driver_roboswitch.o @@ -168,6 +150,28 @@ DRV_OBJS += ../src/drivers/rfkill.o endif +ifdef CONFIG_VLAN_NETLINK +ifdef CONFIG_FULL_DYNAMIC_VLAN +ifdef CONFIG_LIBNL32 + DRV_LIBS += -lnl-3 + DRV_LIBS += -lnl-genl-3 + DRV_LIBS += -lnl-route-3 + DRV_CFLAGS += -DCONFIG_LIBNL20 +else + ifdef CONFIG_LIBNL_TINY + DRV_LIBS += -lnl-tiny + else + DRV_LIBS += -lnl + endif + + ifdef CONFIG_LIBNL20 + DRV_LIBS += -lnl-genl + DRV_LIBS += -lnl-route + DRV_CFLAGS += -DCONFIG_LIBNL20 + endif +endif +endif +endif ##### COMMON VARS DRV_BOTH_CFLAGS := $(DRV_CFLAGS) $(DRV_WPA_CFLAGS) $(DRV_AP_CFLAGS) diff -Nru wpa-1.0/src/drivers/drivers.mk wpa-2.1/src/drivers/drivers.mk --- wpa-1.0/src/drivers/drivers.mk 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/drivers/drivers.mk 2014-02-04 11:23:35.000000000 +0000 @@ -12,29 +12,11 @@ ##### COMMON DRIVERS -ifdef CONFIG_DRIVER_HOSTAP -DRV_CFLAGS += -DCONFIG_DRIVER_HOSTAP -DRV_OBJS += src/drivers/driver_hostap.c -CONFIG_WIRELESS_EXTENSION=y -NEED_AP_MLME=y -NEED_NETLINK=y -NEED_LINUX_IOCTL=y -endif - ifdef CONFIG_DRIVER_WIRED DRV_CFLAGS += -DCONFIG_DRIVER_WIRED DRV_OBJS += src/drivers/driver_wired.c endif -ifdef CONFIG_DRIVER_MADWIFI -DRV_CFLAGS += -DCONFIG_DRIVER_MADWIFI -DRV_OBJS += src/drivers/driver_madwifi.c -CONFIG_WIRELESS_EXTENSION=y -CONFIG_L2_PACKET=linux -NEED_NETLINK=y -NEED_LINUX_IOCTL=y -endif - ifdef CONFIG_DRIVER_NL80211 DRV_CFLAGS += -DCONFIG_DRIVER_NL80211 DRV_OBJS += src/drivers/driver_nl80211.c @@ -48,7 +30,7 @@ ifdef CONFIG_LIBNL32 DRV_LIBS += -lnl-3 DRV_LIBS += -lnl-genl-3 - DRV_CFLAGS += -DCONFIG_LIBNL20 + DRV_CFLAGS += -DCONFIG_LIBNL20 -I/usr/include/libnl3 else ifdef CONFIG_LIBNL_TINY DRV_LIBS += -lnl-tiny @@ -73,6 +55,14 @@ CONFIG_DNET_PCAP=y endif +ifdef CONFIG_DRIVER_OPENBSD +ifndef CONFIG_L2_PACKET +CONFIG_L2_PACKET=freebsd +endif +DRV_CFLAGS += -DCONFIG_DRIVER_OPENBSD +DRV_OBJS += src/drivers/driver_openbsd.c +endif + ifdef CONFIG_DRIVER_TEST DRV_CFLAGS += -DCONFIG_DRIVER_TEST DRV_OBJS += src/drivers/driver_test.c @@ -86,6 +76,24 @@ ##### PURE AP DRIVERS +ifdef CONFIG_DRIVER_HOSTAP +DRV_AP_CFLAGS += -DCONFIG_DRIVER_HOSTAP +DRV_AP_OBJS += src/drivers/driver_hostap.c +CONFIG_WIRELESS_EXTENSION=y +NEED_AP_MLME=y +NEED_NETLINK=y +NEED_LINUX_IOCTL=y +endif + +ifdef CONFIG_DRIVER_MADWIFI +DRV_AP_CFLAGS += -DCONFIG_DRIVER_MADWIFI +DRV_AP_OBJS += src/drivers/driver_madwifi.c +CONFIG_WIRELESS_EXTENSION=y +CONFIG_L2_PACKET=linux +NEED_NETLINK=y +NEED_LINUX_IOCTL=y +endif + ifdef CONFIG_DRIVER_ATHEROS DRV_AP_CFLAGS += -DCONFIG_DRIVER_ATHEROS DRV_AP_OBJS += src/drivers/driver_atheros.c @@ -104,18 +112,6 @@ NEED_RFKILL=y endif -ifdef CONFIG_DRIVER_RALINK -DRV_WPA_CFLAGS += -DCONFIG_DRIVER_RALINK -DRV_WPA_OBJS += src/drivers/driver_ralink.c -NEED_NETLINK=y -NEED_LINUX_IOCTL=y -endif - -ifdef CONFIG_DRIVER_BROADCOM -DRV_WPA_CFLAGS += -DCONFIG_DRIVER_BROADCOM -DRV_WPA_OBJS += src/drivers/driver_broadcom.c -endif - ifdef CONFIG_DRIVER_NDIS DRV_WPA_CFLAGS += -DCONFIG_DRIVER_NDIS DRV_WPA_OBJS += src/drivers/driver_ndis.c @@ -131,20 +127,6 @@ endif endif -ifdef CONFIG_DRIVER_OSX -DRV_WPA_CFLAGS += -DCONFIG_DRIVER_OSX -DRV_WPA_OBJS += src/drivers/driver_osx.c -DRV_WPA_LDFLAGS += -framework CoreFoundation -DRV_WPA_LDFLAGS += -F/System/Library/PrivateFrameworks -framework Apple80211 -endif - -ifdef CONFIG_DRIVER_IPHONE -DRV_WPA_CFLAGS += -DCONFIG_DRIVER_IPHONE -DRV_WPA_OBJS += src/drivers/driver_iphone.c -DRV_WPA_OBJS += src/drivers/MobileApple80211.c -DRV_WPA_LDFLAGS += -framework CoreFoundation -endif - ifdef CONFIG_DRIVER_ROBOSWITCH DRV_WPA_CFLAGS += -DCONFIG_DRIVER_ROBOSWITCH DRV_WPA_OBJS += src/drivers/driver_roboswitch.c @@ -172,6 +154,29 @@ DRV_CFLAGS += -DCONFIG_DRIVER_CUSTOM endif +ifdef CONFIG_VLAN_NETLINK +ifdef CONFIG_FULL_DYNAMIC_VLAN +ifdef CONFIG_LIBNL32 + DRV_LIBS += -lnl-3 + DRV_LIBS += -lnl-genl-3 + DRV_LIBS += -lnl-route-3 + DRV_CFLAGS += -DCONFIG_LIBNL20 +else + ifdef CONFIG_LIBNL_TINY + DRV_LIBS += -lnl-tiny + else + DRV_LIBS += -lnl + endif + + ifdef CONFIG_LIBNL20 + DRV_LIBS += -lnl-genl + DRV_LIBS += -lnl-route + DRV_CFLAGS += -DCONFIG_LIBNL20 + endif +endif +endif +endif + ##### COMMON VARS DRV_BOTH_CFLAGS := $(DRV_CFLAGS) $(DRV_WPA_CFLAGS) $(DRV_AP_CFLAGS) DRV_WPA_CFLAGS += $(DRV_CFLAGS) diff -Nru wpa-1.0/src/drivers/driver_test.c wpa-2.1/src/drivers/driver_test.c --- wpa-1.0/src/drivers/driver_test.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/drivers/driver_test.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * Testing driver interface for a simulated network driver * Copyright (c) 2004-2010, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ /* Make sure we get winsock2.h for Windows build to get sockaddr_storage */ @@ -34,7 +28,6 @@ #include "common/ieee802_11_defs.h" #include "crypto/sha1.h" #include "l2_packet/l2_packet.h" -#include "p2p/p2p.h" #include "wps/wps.h" #include "driver.h" @@ -108,20 +101,6 @@ unsigned int remain_on_channel_duration; int current_freq; - - struct p2p_data *p2p; - unsigned int off_channel_freq; - struct wpabuf *pending_action_tx; - u8 pending_action_src[ETH_ALEN]; - u8 pending_action_dst[ETH_ALEN]; - u8 pending_action_bssid[ETH_ALEN]; - unsigned int pending_action_freq; - unsigned int pending_action_no_cck; - unsigned int pending_listen_freq; - unsigned int pending_listen_duration; - int pending_p2p_scan; - struct sockaddr *probe_from; - socklen_t probe_from_len; }; @@ -131,7 +110,6 @@ static void wpa_driver_test_close_test_socket( struct wpa_driver_test_data *drv); static void test_remain_on_channel_timeout(void *eloop_ctx, void *timeout_ctx); -static int wpa_driver_test_init_p2p(struct wpa_driver_test_data *drv); static void test_driver_free_bss(struct test_driver_bss *bss) @@ -309,7 +287,7 @@ static int wpa_driver_test_send_mlme(void *priv, const u8 *data, - size_t data_len) + size_t data_len, int noack) { struct test_driver_bss *dbss = priv; struct wpa_driver_test_data *drv = dbss->drv; @@ -485,34 +463,6 @@ event.tx_status.ack = ret >= 0; wpa_supplicant_event(drv->ctx, EVENT_TX_STATUS, &event); -#ifdef CONFIG_P2P - if (drv->p2p && - WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT && - WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_ACTION) { - if (drv->pending_action_tx == NULL) { - wpa_printf(MSG_DEBUG, "P2P: Ignore Action TX status - " - "no pending operation"); - return ret; - } - - if (os_memcmp(hdr->addr1, drv->pending_action_dst, ETH_ALEN) != - 0) { - wpa_printf(MSG_DEBUG, "P2P: Ignore Action TX status - " - "unknown destination address"); - return ret; - } - - wpabuf_free(drv->pending_action_tx); - drv->pending_action_tx = NULL; - - p2p_send_action_cb(drv->p2p, drv->pending_action_freq, - drv->pending_action_dst, - drv->pending_action_src, - drv->pending_action_bssid, - ret >= 0); - } -#endif /* CONFIG_P2P */ - return ret; } @@ -559,10 +509,6 @@ event.rx_probe_req.ie = ie; event.rx_probe_req.ie_len = ielen; wpa_supplicant_event(drv->ctx, EVENT_RX_PROBE_REQ, &event); -#ifdef CONFIG_P2P - if (drv->p2p) - p2p_probe_req_rx(drv->p2p, sa, NULL, NULL, ie, ielen); -#endif /* CONFIG_P2P */ } dl_list_for_each(bss, &drv->bss, struct test_driver_bss, list) { @@ -1065,7 +1011,7 @@ const char *ifname, const u8 *addr, void *bss_ctx, void **drv_priv, char *force_ifname, u8 *if_addr, - const char *bridge) + const char *bridge, int use_existing) { struct test_driver_bss *dbss = priv; struct wpa_driver_test_data *drv = dbss->drv; @@ -1281,7 +1227,7 @@ alen = sizeof(addr_un); } if (bind(drv->test_socket, addr, alen) < 0) { - perror("bind(PF_UNIX)"); + perror("test-driver-init: bind(PF_UNIX)"); close(drv->test_socket); if (drv->own_socket_path) unlink(drv->own_socket_path); @@ -1319,23 +1265,7 @@ static void wpa_driver_test_scan_timeout(void *eloop_ctx, void *timeout_ctx) { - struct wpa_driver_test_data *drv = eloop_ctx; wpa_printf(MSG_DEBUG, "Scan timeout - try to get results"); - if (drv->pending_p2p_scan && drv->p2p) { -#ifdef CONFIG_P2P - size_t i; - for (i = 0; i < drv->num_scanres; i++) { - struct wpa_scan_res *bss = drv->scanres[i]; - if (p2p_scan_res_handler(drv->p2p, bss->bssid, - bss->freq, bss->level, - (const u8 *) (bss + 1), - bss->ie_len) > 0) - return; - } - p2p_scan_res_handled(drv->p2p); -#endif /* CONFIG_P2P */ - return; - } wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL); } @@ -1484,7 +1414,7 @@ if (res == NULL) return NULL; - res->res = os_zalloc(drv->num_scanres * sizeof(struct wpa_scan_res *)); + res->res = os_calloc(drv->num_scanres, sizeof(struct wpa_scan_res *)); if (res->res == NULL) { os_free(res); return NULL; @@ -1720,20 +1650,6 @@ } -static int wpa_driver_test_disassociate(void *priv, const u8 *addr, - int reason_code) -{ - struct test_driver_bss *dbss = priv; - struct wpa_driver_test_data *drv = dbss->drv; - wpa_printf(MSG_DEBUG, "%s addr=" MACSTR " reason_code=%d", - __func__, MAC2STR(addr), reason_code); - os_memset(dbss->bssid, 0, ETH_ALEN); - drv->associated = 0; - wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, NULL); - return wpa_driver_test_send_disassoc(drv); -} - - static const u8 * wpa_scan_get_ie(const struct wpa_scan_res *res, u8 ie) { const u8 *end, *pos; @@ -1967,30 +1883,8 @@ data_len - (mgmt->u.probe_req.variable - data); wpa_supplicant_event(drv->ctx, EVENT_RX_PROBE_REQ, &event); -#ifdef CONFIG_P2P - if (drv->p2p) - p2p_probe_req_rx(drv->p2p, mgmt->sa, - mgmt->da, mgmt->bssid, - event.rx_probe_req.ie, - event.rx_probe_req.ie_len); -#endif /* CONFIG_P2P */ } } - -#ifdef CONFIG_P2P - if (drv->p2p && - WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT && - WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_ACTION) { - size_t hdr_len; - hdr_len = (const u8 *) - &mgmt->u.action.u.vs_public_action.action - data; - p2p_rx_action(drv->p2p, mgmt->da, mgmt->sa, mgmt->bssid, - mgmt->u.action.category, - &mgmt->u.action.u.vs_public_action.action, - data_len - hdr_len, freq); - } -#endif /* CONFIG_P2P */ - } @@ -2006,29 +1900,6 @@ bss = dl_list_first(&drv->bss, struct test_driver_bss, list); /* data: optional [ STA-addr | ' ' | IEs(hex) ] */ -#ifdef CONFIG_P2P - if (drv->probe_req_report && drv->p2p && data_len) { - const char *d = (const char *) data; - u8 sa[ETH_ALEN]; - u8 ie[512]; - size_t ielen; - - if (hwaddr_aton(d, sa)) - return; - d += 18; - while (*d == ' ') - d++; - ielen = os_strlen(d) / 2; - if (ielen > sizeof(ie)) - ielen = sizeof(ie); - if (hexstr2bin(d, ie, ielen) < 0) - ielen = 0; - drv->probe_from = from; - drv->probe_from_len = fromlen; - p2p_probe_req_rx(drv->p2p, sa, NULL, NULL, ie, ielen); - drv->probe_from = NULL; - } -#endif /* CONFIG_P2P */ if (!drv->ibss) return; @@ -2185,12 +2056,6 @@ struct test_client_socket *cli, *prev; int i; -#ifdef CONFIG_P2P - if (drv->p2p) - p2p_deinit(drv->p2p); - wpabuf_free(drv->pending_action_tx); -#endif /* CONFIG_P2P */ - cli = drv->cli; while (cli) { prev = cli; @@ -2258,7 +2123,7 @@ os_strlcpy(addr.sun_path, drv->own_socket_path, sizeof(addr.sun_path)); if (bind(drv->test_socket, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - perror("bind(PF_UNIX)"); + perror("test-driver-attach: bind(PF_UNIX)"); close(drv->test_socket); unlink(drv->own_socket_path); os_free(drv->own_socket_path); @@ -2387,13 +2252,6 @@ drv->use_associnfo = 1; } - if (os_strstr(param, "p2p_mgmt=1")) { - wpa_printf(MSG_DEBUG, "test_driver: Use internal P2P " - "management"); - if (wpa_driver_test_init_p2p(drv) < 0) - return -1; - } - return 0; } @@ -2483,8 +2341,6 @@ static int wpa_driver_test_get_capa(void *priv, struct wpa_driver_capa *capa) { - struct test_driver_bss *dbss = priv; - struct wpa_driver_test_data *drv = dbss->drv; os_memset(capa, 0, sizeof(*capa)); capa->key_mgmt = WPA_DRIVER_CAPA_KEY_MGMT_WPA | WPA_DRIVER_CAPA_KEY_MGMT_WPA2 | @@ -2500,8 +2356,6 @@ capa->auth = WPA_DRIVER_AUTH_OPEN | WPA_DRIVER_AUTH_SHARED | WPA_DRIVER_AUTH_LEAP; - if (drv->p2p) - capa->flags |= WPA_DRIVER_FLAGS_P2P_MGMT; capa->flags |= WPA_DRIVER_FLAGS_AP; capa->flags |= WPA_DRIVER_FLAGS_P2P_CONCURRENT; capa->flags |= WPA_DRIVER_FLAGS_P2P_DEDICATED_INTERFACE; @@ -2576,15 +2430,14 @@ *num_modes = 3; *flags = 0; - modes = os_zalloc(*num_modes * sizeof(struct hostapd_hw_modes)); + modes = os_calloc(*num_modes, sizeof(struct hostapd_hw_modes)); if (modes == NULL) return NULL; modes[0].mode = HOSTAPD_MODE_IEEE80211G; modes[0].num_channels = 11; modes[0].num_rates = 12; - modes[0].channels = - os_zalloc(11 * sizeof(struct hostapd_channel_data)); - modes[0].rates = os_zalloc(modes[0].num_rates * sizeof(int)); + modes[0].channels = os_calloc(11, sizeof(struct hostapd_channel_data)); + modes[0].rates = os_calloc(modes[0].num_rates, sizeof(int)); if (modes[0].channels == NULL || modes[0].rates == NULL) goto fail; for (i = 0; i < 11; i++) { @@ -2608,9 +2461,8 @@ modes[1].mode = HOSTAPD_MODE_IEEE80211B; modes[1].num_channels = 11; modes[1].num_rates = 4; - modes[1].channels = - os_zalloc(11 * sizeof(struct hostapd_channel_data)); - modes[1].rates = os_zalloc(modes[1].num_rates * sizeof(int)); + modes[1].channels = os_calloc(11, sizeof(struct hostapd_channel_data)); + modes[1].rates = os_calloc(modes[1].num_rates, sizeof(int)); if (modes[1].channels == NULL || modes[1].rates == NULL) goto fail; for (i = 0; i < 11; i++) { @@ -2626,8 +2478,8 @@ modes[2].mode = HOSTAPD_MODE_IEEE80211A; modes[2].num_channels = 1; modes[2].num_rates = 8; - modes[2].channels = os_zalloc(sizeof(struct hostapd_channel_data)); - modes[2].rates = os_zalloc(modes[2].num_rates * sizeof(int)); + modes[2].channels = os_calloc(1, sizeof(struct hostapd_channel_data)); + modes[2].rates = os_calloc(modes[2].num_rates, sizeof(int)); if (modes[2].channels == NULL || modes[2].rates == NULL) goto fail; modes[2].channels[0].chan = 60; @@ -2705,39 +2557,12 @@ os_memcpy(hdr->addr2, src, ETH_ALEN); os_memcpy(hdr->addr3, bssid, ETH_ALEN); - ret = wpa_driver_test_send_mlme(priv, buf, 24 + data_len); + ret = wpa_driver_test_send_mlme(priv, buf, 24 + data_len, 0); os_free(buf); return ret; } -#ifdef CONFIG_P2P -static void test_send_action_cb(void *eloop_ctx, void *timeout_ctx) -{ - struct wpa_driver_test_data *drv = eloop_ctx; - - if (drv->pending_action_tx == NULL) - return; - - if (drv->off_channel_freq != drv->pending_action_freq) { - wpa_printf(MSG_DEBUG, "P2P: Pending Action frame TX " - "waiting for another freq=%u", - drv->pending_action_freq); - return; - } - wpa_printf(MSG_DEBUG, "P2P: Sending pending Action frame to " - MACSTR, MAC2STR(drv->pending_action_dst)); - wpa_driver_test_send_action(drv, drv->pending_action_freq, 0, - drv->pending_action_dst, - drv->pending_action_src, - drv->pending_action_bssid, - wpabuf_head(drv->pending_action_tx), - wpabuf_len(drv->pending_action_tx), - drv->pending_action_no_cck); -} -#endif /* CONFIG_P2P */ - - static void test_remain_on_channel_timeout(void *eloop_ctx, void *timeout_ctx) { struct wpa_driver_test_data *drv = eloop_ctx; @@ -2749,9 +2574,6 @@ data.remain_on_channel.freq = drv->remain_on_channel_freq; data.remain_on_channel.duration = drv->remain_on_channel_duration; - if (drv->p2p) - drv->off_channel_freq = 0; - drv->remain_on_channel_freq = 0; wpa_supplicant_event(drv->ctx, EVENT_CANCEL_REMAIN_ON_CHANNEL, &data); @@ -2785,18 +2607,6 @@ data.remain_on_channel.duration = duration; wpa_supplicant_event(drv->ctx, EVENT_REMAIN_ON_CHANNEL, &data); -#ifdef CONFIG_P2P - if (drv->p2p) { - drv->off_channel_freq = drv->remain_on_channel_freq; - test_send_action_cb(drv, NULL); - if (drv->off_channel_freq == drv->pending_listen_freq) { - p2p_listen_cb(drv->p2p, drv->pending_listen_freq, - drv->pending_listen_duration); - drv->pending_listen_freq = 0; - } - } -#endif /* CONFIG_P2P */ - return 0; } @@ -2824,461 +2634,6 @@ } -#ifdef CONFIG_P2P - -static int wpa_driver_test_p2p_find(void *priv, unsigned int timeout, int type) -{ - struct wpa_driver_test_data *drv = priv; - wpa_printf(MSG_DEBUG, "%s(timeout=%u)", __func__, timeout); - if (!drv->p2p) - return -1; - return p2p_find(drv->p2p, timeout, type, 0, NULL, NULL); -} - - -static int wpa_driver_test_p2p_stop_find(void *priv) -{ - struct wpa_driver_test_data *drv = priv; - wpa_printf(MSG_DEBUG, "%s", __func__); - if (!drv->p2p) - return -1; - p2p_stop_find(drv->p2p); - return 0; -} - - -static int wpa_driver_test_p2p_listen(void *priv, unsigned int timeout) -{ - struct wpa_driver_test_data *drv = priv; - wpa_printf(MSG_DEBUG, "%s(timeout=%u)", __func__, timeout); - if (!drv->p2p) - return -1; - return p2p_listen(drv->p2p, timeout); -} - - -static int wpa_driver_test_p2p_connect(void *priv, const u8 *peer_addr, - int wps_method, int go_intent, - const u8 *own_interface_addr, - unsigned int force_freq, - int persistent_group) -{ - struct wpa_driver_test_data *drv = priv; - wpa_printf(MSG_DEBUG, "%s(peer_addr=" MACSTR " wps_method=%d " - "go_intent=%d " - "own_interface_addr=" MACSTR " force_freq=%u " - "persistent_group=%d)", - __func__, MAC2STR(peer_addr), wps_method, go_intent, - MAC2STR(own_interface_addr), force_freq, persistent_group); - if (!drv->p2p) - return -1; - return p2p_connect(drv->p2p, peer_addr, wps_method, go_intent, - own_interface_addr, force_freq, persistent_group); -} - - -static int wpa_driver_test_wps_success_cb(void *priv, const u8 *peer_addr) -{ - struct wpa_driver_test_data *drv = priv; - wpa_printf(MSG_DEBUG, "%s(peer_addr=" MACSTR ")", - __func__, MAC2STR(peer_addr)); - if (!drv->p2p) - return -1; - p2p_wps_success_cb(drv->p2p, peer_addr); - return 0; -} - - -static int wpa_driver_test_p2p_group_formation_failed(void *priv) -{ - struct wpa_driver_test_data *drv = priv; - wpa_printf(MSG_DEBUG, "%s", __func__); - if (!drv->p2p) - return -1; - p2p_group_formation_failed(drv->p2p); - return 0; -} - - -static int wpa_driver_test_p2p_set_params(void *priv, - const struct p2p_params *params) -{ - struct wpa_driver_test_data *drv = priv; - wpa_printf(MSG_DEBUG, "%s", __func__); - if (!drv->p2p) - return -1; - if (p2p_set_dev_name(drv->p2p, params->dev_name) < 0 || - p2p_set_pri_dev_type(drv->p2p, params->pri_dev_type) < 0 || - p2p_set_sec_dev_types(drv->p2p, params->sec_dev_type, - params->num_sec_dev_types) < 0) - return -1; - return 0; -} - - -static int test_p2p_scan(void *ctx, enum p2p_scan_type type, int freq, - unsigned int num_req_dev_types, - const u8 *req_dev_types, const u8 *dev_id) -{ - struct wpa_driver_test_data *drv = ctx; - struct wpa_driver_scan_params params; - int ret; - struct wpabuf *wps_ie, *ies; - int social_channels[] = { 2412, 2437, 2462, 0, 0 }; - size_t ielen; - - wpa_printf(MSG_DEBUG, "%s(type=%d freq=%d)", - __func__, type, freq); - - os_memset(¶ms, 0, sizeof(params)); - - /* P2P Wildcard SSID */ - params.num_ssids = 1; - params.ssids[0].ssid = (u8 *) P2P_WILDCARD_SSID; - params.ssids[0].ssid_len = P2P_WILDCARD_SSID_LEN; - -#if 0 /* TODO: WPS IE */ - wpa_s->wps->dev.p2p = 1; - wps_ie = wps_build_probe_req_ie(0, &wpa_s->wps->dev, wpa_s->wps->uuid, - WPS_REQ_ENROLLEE); -#else - wps_ie = wpabuf_alloc(1); -#endif - if (wps_ie == NULL) - return -1; - - ielen = p2p_scan_ie_buf_len(drv->p2p); - ies = wpabuf_alloc(wpabuf_len(wps_ie) + ielen); - if (ies == NULL) { - wpabuf_free(wps_ie); - return -1; - } - wpabuf_put_buf(ies, wps_ie); - wpabuf_free(wps_ie); - - p2p_scan_ie(drv->p2p, ies, dev_id); - - params.extra_ies = wpabuf_head(ies); - params.extra_ies_len = wpabuf_len(ies); - - switch (type) { - case P2P_SCAN_SOCIAL: - params.freqs = social_channels; - break; - case P2P_SCAN_FULL: - break; - case P2P_SCAN_SPECIFIC: - social_channels[0] = freq; - social_channels[1] = 0; - params.freqs = social_channels; - break; - case P2P_SCAN_SOCIAL_PLUS_ONE: - social_channels[3] = freq; - params.freqs = social_channels; - break; - } - - drv->pending_p2p_scan = 1; - ret = wpa_driver_test_scan(drv, ¶ms); - - wpabuf_free(ies); - - return ret; -} - - -static int test_send_action(void *ctx, unsigned int freq, const u8 *dst, - const u8 *src, const u8 *bssid, const u8 *buf, - size_t len, unsigned int wait_time) -{ - struct wpa_driver_test_data *drv = ctx; - - wpa_printf(MSG_DEBUG, "%s(freq=%u dst=" MACSTR " src=" MACSTR - " bssid=" MACSTR " len=%d", - __func__, freq, MAC2STR(dst), MAC2STR(src), MAC2STR(bssid), - (int) len); - if (freq <= 0) { - wpa_printf(MSG_WARNING, "P2P: No frequency specified for " - "action frame TX"); - return -1; - } - - if (drv->pending_action_tx) { - wpa_printf(MSG_DEBUG, "P2P: Dropped pending Action frame TX " - "to " MACSTR, MAC2STR(drv->pending_action_dst)); - wpabuf_free(drv->pending_action_tx); - } - drv->pending_action_tx = wpabuf_alloc(len); - if (drv->pending_action_tx == NULL) - return -1; - wpabuf_put_data(drv->pending_action_tx, buf, len); - os_memcpy(drv->pending_action_src, src, ETH_ALEN); - os_memcpy(drv->pending_action_dst, dst, ETH_ALEN); - os_memcpy(drv->pending_action_bssid, bssid, ETH_ALEN); - drv->pending_action_freq = freq; - drv->pending_action_no_cck = 1; - - if (drv->off_channel_freq == freq) { - /* Already on requested channel; send immediately */ - /* TODO: Would there ever be need to extend the current - * duration on the channel? */ - eloop_cancel_timeout(test_send_action_cb, drv, NULL); - eloop_register_timeout(0, 0, test_send_action_cb, drv, NULL); - return 0; - } - - wpa_printf(MSG_DEBUG, "P2P: Schedule Action frame to be transmitted " - "once the driver gets to the requested channel"); - if (wpa_driver_test_remain_on_channel(drv, freq, wait_time) < 0) { - wpa_printf(MSG_DEBUG, "P2P: Failed to request driver " - "to remain on channel (%u MHz) for Action " - "Frame TX", freq); - return -1; - } - - return 0; -} - - -static void test_send_action_done(void *ctx) -{ - wpa_printf(MSG_DEBUG, "%s", __func__); - /* TODO */ -} - - -static void test_go_neg_completed(void *ctx, struct p2p_go_neg_results *res) -{ - struct wpa_driver_test_data *drv = ctx; - union wpa_event_data event; - wpa_printf(MSG_DEBUG, "%s", __func__); - os_memset(&event, 0, sizeof(event)); - event.p2p_go_neg_completed.res = res; - wpa_supplicant_event(drv->ctx, EVENT_P2P_GO_NEG_COMPLETED, &event); -} - - -static void test_go_neg_req_rx(void *ctx, const u8 *src, u16 dev_passwd_id) -{ - struct wpa_driver_test_data *drv = ctx; - union wpa_event_data event; - wpa_printf(MSG_DEBUG, "%s(src=" MACSTR ")", __func__, MAC2STR(src)); - os_memset(&event, 0, sizeof(event)); - event.p2p_go_neg_req_rx.src = src; - event.p2p_go_neg_req_rx.dev_passwd_id = dev_passwd_id; - wpa_supplicant_event(drv->ctx, EVENT_P2P_GO_NEG_REQ_RX, &event); -} - - -static void test_dev_found(void *ctx, const u8 *addr, - const struct p2p_peer_info *info, int new_device) -{ - struct wpa_driver_test_data *drv = ctx; - union wpa_event_data event; - char devtype[WPS_DEV_TYPE_BUFSIZE]; - wpa_printf(MSG_DEBUG, "%s(" MACSTR " p2p_dev_addr=" MACSTR - " pri_dev_type=%s name='%s' config_methods=0x%x " - "dev_capab=0x%x group_capab=0x%x)", - __func__, MAC2STR(addr), MAC2STR(info->p2p_device_addr), - wps_dev_type_bin2str(info->pri_dev_type, devtype, - sizeof(devtype)), - info->device_name, info->config_methods, info->dev_capab, - info->group_capab); - - os_memset(&event, 0, sizeof(event)); - event.p2p_dev_found.addr = addr; - event.p2p_dev_found.dev_addr = info->p2p_device_addr; - event.p2p_dev_found.pri_dev_type = info->pri_dev_type; - event.p2p_dev_found.dev_name = info->device_name; - event.p2p_dev_found.config_methods = info->config_methods; - event.p2p_dev_found.dev_capab = info->dev_capab; - event.p2p_dev_found.group_capab = info->group_capab; - wpa_supplicant_event(drv->ctx, EVENT_P2P_DEV_FOUND, &event); -} - - -static int test_start_listen(void *ctx, unsigned int freq, - unsigned int duration, - const struct wpabuf *probe_resp_ie) -{ - struct wpa_driver_test_data *drv = ctx; - - wpa_printf(MSG_DEBUG, "%s(freq=%u duration=%u)", - __func__, freq, duration); - - if (wpa_driver_test_probe_req_report(drv, 1) < 0) - return -1; - - drv->pending_listen_freq = freq; - drv->pending_listen_duration = duration; - - if (wpa_driver_test_remain_on_channel(drv, freq, duration) < 0) { - drv->pending_listen_freq = 0; - return -1; - } - - return 0; -} - - -static void test_stop_listen(void *ctx) -{ - wpa_printf(MSG_DEBUG, "%s", __func__); - /* TODO */ -} - - -static int test_send_probe_resp(void *ctx, const struct wpabuf *buf) -{ - struct wpa_driver_test_data *drv = ctx; - char resp[512], *pos, *end; - int ret; - const struct ieee80211_mgmt *mgmt; - const u8 *ie, *ie_end; - - wpa_printf(MSG_DEBUG, "%s", __func__); - wpa_hexdump_buf(MSG_MSGDUMP, "Probe Response", buf); - if (wpabuf_len(buf) < 24) - return -1; - if (!drv->probe_from) { - wpa_printf(MSG_DEBUG, "%s: probe_from not set", __func__); - return -1; - } - - pos = resp; - end = resp + sizeof(resp); - - mgmt = wpabuf_head(buf); - - /* reply: SCANRESP BSSID SSID IEs */ - ret = os_snprintf(pos, end - pos, "SCANRESP " MACSTR " ", - MAC2STR(mgmt->bssid)); - if (ret < 0 || ret >= end - pos) - return -1; - pos += ret; - - ie = mgmt->u.probe_resp.variable; - ie_end = wpabuf_head_u8(buf) + wpabuf_len(buf); - if (ie_end - ie < 2 || ie[0] != WLAN_EID_SSID || - ie + 2 + ie[1] > ie_end) - return -1; - pos += wpa_snprintf_hex(pos, end - pos, ie + 2, ie[1]); - - ret = os_snprintf(pos, end - pos, " "); - if (ret < 0 || ret >= end - pos) - return -1; - pos += ret; - pos += wpa_snprintf_hex(pos, end - pos, ie, ie_end - ie); - - sendto(drv->test_socket, resp, pos - resp, 0, - drv->probe_from, drv->probe_from_len); - - return 0; -} - - -static void test_sd_request(void *ctx, int freq, const u8 *sa, u8 dialog_token, - u16 update_indic, const u8 *tlvs, size_t tlvs_len) -{ - wpa_printf(MSG_DEBUG, "%s", __func__); - /* TODO */ -} - - -static void test_sd_response(void *ctx, const u8 *sa, u16 update_indic, - const u8 *tlvs, size_t tlvs_len) -{ - wpa_printf(MSG_DEBUG, "%s", __func__); - /* TODO */ -} - - -static void test_prov_disc_req(void *ctx, const u8 *peer, u16 config_methods, - const u8 *dev_addr, const u8 *pri_dev_type, - const char *dev_name, u16 supp_config_methods, - u8 dev_capab, u8 group_capab, - const u8 *group_id, size_t group_id_len) -{ - wpa_printf(MSG_DEBUG, "%s(peer=" MACSTR " config_methods=0x%x)", - __func__, MAC2STR(peer), config_methods); - /* TODO */ -} - - -static void test_prov_disc_resp(void *ctx, const u8 *peer, u16 config_methods) -{ - wpa_printf(MSG_DEBUG, "%s(peer=" MACSTR " config_methods=0x%x)", - __func__, MAC2STR(peer), config_methods); - /* TODO */ -} - -#endif /* CONFIG_P2P */ - - -static int wpa_driver_test_init_p2p(struct wpa_driver_test_data *drv) -{ -#ifdef CONFIG_P2P - struct p2p_config p2p; - unsigned int r; - int i; - - os_memset(&p2p, 0, sizeof(p2p)); - p2p.msg_ctx = drv->ctx; - p2p.cb_ctx = drv; - p2p.p2p_scan = test_p2p_scan; - p2p.send_action = test_send_action; - p2p.send_action_done = test_send_action_done; - p2p.go_neg_completed = test_go_neg_completed; - p2p.go_neg_req_rx = test_go_neg_req_rx; - p2p.dev_found = test_dev_found; - p2p.start_listen = test_start_listen; - p2p.stop_listen = test_stop_listen; - p2p.send_probe_resp = test_send_probe_resp; - p2p.sd_request = test_sd_request; - p2p.sd_response = test_sd_response; - p2p.prov_disc_req = test_prov_disc_req; - p2p.prov_disc_resp = test_prov_disc_resp; - - os_memcpy(p2p.dev_addr, drv->own_addr, ETH_ALEN); - - p2p.reg_class = 12; /* TODO: change depending on location */ - /* - * Pick one of the social channels randomly as the listen - * channel. - */ - os_get_random((u8 *) &r, sizeof(r)); - p2p.channel = 1 + (r % 3) * 5; - - /* TODO: change depending on location */ - p2p.op_reg_class = 12; - /* - * For initial tests, pick the operation channel randomly. - * TODO: Use scan results (etc.) to select the best channel. - */ - p2p.op_channel = 1 + r % 11; - - os_memcpy(p2p.country, "US ", 3); - - /* FIX: fetch available channels from the driver */ - p2p.channels.reg_classes = 1; - p2p.channels.reg_class[0].reg_class = 12; /* US/12 = 2.4 GHz band */ - p2p.channels.reg_class[0].channels = 11; - for (i = 0; i < 11; i++) - p2p.channels.reg_class[0].channel[i] = i + 1; - - p2p.max_peers = 100; - - drv->p2p = p2p_init(&p2p); - if (drv->p2p == NULL) - return -1; - return 0; -#else /* CONFIG_P2P */ - wpa_printf(MSG_INFO, "driver_test: P2P support not included"); - return -1; -#endif /* CONFIG_P2P */ -} - - const struct wpa_driver_ops wpa_driver_test_ops = { "test", "wpa_supplicant test driver", @@ -3304,7 +2659,6 @@ .deinit = wpa_driver_test_deinit, .set_param = wpa_driver_test_set_param, .deauthenticate = wpa_driver_test_deauthenticate, - .disassociate = wpa_driver_test_disassociate, .associate = wpa_driver_test_associate, .get_capa = wpa_driver_test_get_capa, .get_mac_addr = wpa_driver_test_get_mac_addr, @@ -3321,14 +2675,4 @@ .remain_on_channel = wpa_driver_test_remain_on_channel, .cancel_remain_on_channel = wpa_driver_test_cancel_remain_on_channel, .probe_req_report = wpa_driver_test_probe_req_report, -#ifdef CONFIG_P2P - .p2p_find = wpa_driver_test_p2p_find, - .p2p_stop_find = wpa_driver_test_p2p_stop_find, - .p2p_listen = wpa_driver_test_p2p_listen, - .p2p_connect = wpa_driver_test_p2p_connect, - .wps_success_cb = wpa_driver_test_wps_success_cb, - .p2p_group_formation_failed = - wpa_driver_test_p2p_group_formation_failed, - .p2p_set_params = wpa_driver_test_p2p_set_params, -#endif /* CONFIG_P2P */ }; diff -Nru wpa-1.0/src/drivers/driver_wext.c wpa-2.1/src/drivers/driver_wext.c --- wpa-1.0/src/drivers/driver_wext.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/drivers/driver_wext.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * Driver interaction with generic Linux Wireless Extensions * Copyright (c) 2003-2010, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. * * This file implements a driver interface for the Linux Wireless Extensions. * When used with WE-18 or newer, this interface can be used as-is with number @@ -25,7 +19,7 @@ #include #include -#include "wireless_copy.h" +#include "linux_wext.h" #include "common.h" #include "eloop.h" #include "common/ieee802_11_defs.h" @@ -37,7 +31,6 @@ #include "driver.h" #include "driver_wext.h" - static int wpa_driver_wext_flush_pmkid(void *priv); static int wpa_driver_wext_get_range(void *priv); static int wpa_driver_wext_finish_drv_init(struct wpa_driver_wext_data *drv); @@ -497,11 +490,9 @@ "IWEVCUSTOM length"); return; } - buf = os_malloc(iwe->u.data.length + 1); + buf = dup_binstr(custom, iwe->u.data.length); if (buf == NULL) return; - os_memcpy(buf, custom, iwe->u.data.length); - buf[iwe->u.data.length] = '\0'; wpa_driver_wext_event_wireless_custom(drv->ctx, buf); os_free(buf); break; @@ -1403,8 +1394,8 @@ if (data->ie) os_memcpy(pos, data->ie, data->ie_len); - tmp = os_realloc(res->res, - (res->num + 1) * sizeof(struct wpa_scan_res *)); + tmp = os_realloc_array(res->res, res->num + 1, + sizeof(struct wpa_scan_res *)); if (tmp == NULL) { os_free(r); return; @@ -1564,6 +1555,7 @@ } drv->capa.enc |= WPA_DRIVER_CAPA_ENC_WEP40 | WPA_DRIVER_CAPA_ENC_WEP104; + drv->capa.enc |= WPA_DRIVER_CAPA_ENC_WEP128; if (range->enc_capa & IW_ENC_CAPA_CIPHER_TKIP) drv->capa.enc |= WPA_DRIVER_CAPA_ENC_TKIP; if (range->enc_capa & IW_ENC_CAPA_CIPHER_CCMP) @@ -1859,10 +1851,8 @@ { struct iwreq iwr; const u8 null_bssid[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 }; -#ifndef ANDROID u8 ssid[32]; int i; -#endif /* ANDROID */ /* * Only force-disconnect when the card is in infrastructure mode, @@ -1883,7 +1873,6 @@ "selection on disconnect"); } -#ifndef ANDROID if (drv->cfg80211) { /* * cfg80211 supports SIOCSIWMLME commands, so there is @@ -1909,7 +1898,6 @@ wpa_printf(MSG_DEBUG, "WEXT: Failed to set bogus " "SSID to disconnect"); } -#endif /* ANDROID */ } } @@ -1926,18 +1914,6 @@ } -static int wpa_driver_wext_disassociate(void *priv, const u8 *addr, - int reason_code) -{ - struct wpa_driver_wext_data *drv = priv; - int ret; - wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); - ret = wpa_driver_wext_mlme(drv, addr, IW_MLME_DISASSOC, reason_code); - wpa_driver_wext_disconnect(drv); - return ret; -} - - static int wpa_driver_wext_set_gen_ie(void *priv, const u8 *ie, size_t ie_len) { @@ -1962,15 +1938,15 @@ int wpa_driver_wext_cipher2wext(int cipher) { switch (cipher) { - case CIPHER_NONE: + case WPA_CIPHER_NONE: return IW_AUTH_CIPHER_NONE; - case CIPHER_WEP40: + case WPA_CIPHER_WEP40: return IW_AUTH_CIPHER_WEP40; - case CIPHER_TKIP: + case WPA_CIPHER_TKIP: return IW_AUTH_CIPHER_TKIP; - case CIPHER_CCMP: + case WPA_CIPHER_CCMP: return IW_AUTH_CIPHER_CCMP; - case CIPHER_WEP104: + case WPA_CIPHER_WEP104: return IW_AUTH_CIPHER_WEP104; default: return 0; @@ -1981,10 +1957,10 @@ int wpa_driver_wext_keymgmt2wext(int keymgmt) { switch (keymgmt) { - case KEY_MGMT_802_1X: - case KEY_MGMT_802_1X_NO_WPA: + case WPA_KEY_MGMT_IEEE8021X: + case WPA_KEY_MGMT_IEEE8021X_NO_WPA: return IW_AUTH_KEY_MGMT_802_1X; - case KEY_MGMT_PSK: + case WPA_KEY_MGMT_PSK: return IW_AUTH_KEY_MGMT_PSK; default: return 0; @@ -2101,9 +2077,9 @@ if (wpa_driver_wext_set_auth_param(drv, IW_AUTH_KEY_MGMT, value) < 0) ret = -1; - value = params->key_mgmt_suite != KEY_MGMT_NONE || - params->pairwise_suite != CIPHER_NONE || - params->group_suite != CIPHER_NONE || + value = params->key_mgmt_suite != WPA_KEY_MGMT_NONE || + params->pairwise_suite != WPA_CIPHER_NONE || + params->group_suite != WPA_CIPHER_NONE || params->wpa_ie_len; if (wpa_driver_wext_set_auth_param(drv, IW_AUTH_PRIVACY_INVOKED, value) < 0) @@ -2112,8 +2088,8 @@ /* Allow unencrypted EAPOL messages even if pairwise keys are set when * not using WPA. IEEE 802.1X specifies that these frames are not * encrypted, but WPA encrypts them when pairwise keys are in use. */ - if (params->key_mgmt_suite == KEY_MGMT_802_1X || - params->key_mgmt_suite == KEY_MGMT_PSK) + if (params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X || + params->key_mgmt_suite == WPA_KEY_MGMT_PSK) allow_unencrypted_eapol = 0; else allow_unencrypted_eapol = 1; @@ -2350,7 +2326,6 @@ .scan2 = wpa_driver_wext_scan, .get_scan_results2 = wpa_driver_wext_get_scan_results, .deauthenticate = wpa_driver_wext_deauthenticate, - .disassociate = wpa_driver_wext_disassociate, .associate = wpa_driver_wext_associate, .init = wpa_driver_wext_init, .deinit = wpa_driver_wext_deinit, diff -Nru wpa-1.0/src/drivers/driver_wext.h wpa-2.1/src/drivers/driver_wext.h --- wpa-1.0/src/drivers/driver_wext.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/drivers/driver_wext.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * WPA Supplicant - driver_wext exported functions * Copyright (c) 2003-2005, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef DRIVER_WEXT_H diff -Nru wpa-1.0/src/drivers/driver_wired.c wpa-2.1/src/drivers/driver_wired.c --- wpa-1.0/src/drivers/driver_wired.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/drivers/driver_wired.c 2014-02-04 11:23:35.000000000 +0000 @@ -3,14 +3,8 @@ * Copyright (c) 2005-2009, Jouni Malinen * Copyright (c) 2004, Gunter Burchardt * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -23,6 +17,7 @@ #endif /* __linux__ */ #if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__) #include +#include #endif /* defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__) */ #ifdef __sun__ #include @@ -460,6 +455,34 @@ } +#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__) +static int wpa_driver_wired_get_ifstatus(const char *ifname, int *status) +{ + struct ifmediareq ifmr; + int s; + + s = socket(PF_INET, SOCK_DGRAM, 0); + if (s < 0) { + perror("socket"); + return -1; + } + + os_memset(&ifmr, 0, sizeof(ifmr)); + os_strlcpy(ifmr.ifm_name, ifname, IFNAMSIZ); + if (ioctl(s, SIOCGIFMEDIA, (caddr_t) &ifmr) < 0) { + perror("ioctl[SIOCGIFMEDIA]"); + close(s); + return -1; + } + close(s); + *status = (ifmr.ifm_status & (IFM_ACTIVE | IFM_AVALID)) == + (IFM_ACTIVE | IFM_AVALID); + + return 0; +} +#endif /* defined(__FreeBSD__) || defined(__DragonFly__) || defined(FreeBSD_kernel__) */ + + static int wpa_driver_wired_multi(const char *ifname, const u8 *addr, int add) { struct ifreq ifr; @@ -568,6 +591,16 @@ __func__); drv->iff_allmulti = 1; } +#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__) + { + int status; + wpa_printf(MSG_DEBUG, "%s: waiting for link to become active", + __func__); + while (wpa_driver_wired_get_ifstatus(ifname, &status) == 0 && + status == 0) + sleep(1); + } +#endif /* defined(__FreeBSD__) || defined(__DragonFly__) || defined(FreeBSD_kernel__) */ return drv; } diff -Nru wpa-1.0/src/drivers/linux_ioctl.c wpa-2.1/src/drivers/linux_ioctl.c --- wpa-1.0/src/drivers/linux_ioctl.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/drivers/linux_ioctl.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * Linux ioctl helper functions for driver wrappers * Copyright (c) 2002-2010, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "utils/includes.h" @@ -51,8 +45,9 @@ if (ioctl(sock, SIOCSIFFLAGS, &ifr) != 0) { ret = errno ? -errno : -999; - wpa_printf(MSG_ERROR, "Could not set interface %s flags: %s", - ifname, strerror(errno)); + wpa_printf(MSG_ERROR, "Could not set interface %s flags (%s): " + "%s", + ifname, dev_up ? "UP" : "DOWN", strerror(errno)); return ret; } @@ -209,11 +204,14 @@ int linux_br_get(char *brname, const char *ifname) { char path[128], brlink[128], *pos; + ssize_t res; + os_snprintf(path, sizeof(path), "/sys/class/net/%s/brport/bridge", ifname); - os_memset(brlink, 0, sizeof(brlink)); - if (readlink(path, brlink, sizeof(brlink) - 1) < 0) + res = readlink(path, brlink, sizeof(brlink)); + if (res < 0 || (size_t) res >= sizeof(brlink)) return -1; + brlink[res] = '\0'; pos = os_strrchr(brlink, '/'); if (pos == NULL) return -1; diff -Nru wpa-1.0/src/drivers/linux_ioctl.h wpa-2.1/src/drivers/linux_ioctl.h --- wpa-1.0/src/drivers/linux_ioctl.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/drivers/linux_ioctl.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * Linux ioctl helper functions for driver wrappers * Copyright (c) 2002-2010, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef LINUX_IOCTL_H diff -Nru wpa-1.0/src/drivers/linux_wext.h wpa-2.1/src/drivers/linux_wext.h --- wpa-1.0/src/drivers/linux_wext.h 1970-01-01 00:00:00.000000000 +0000 +++ wpa-2.1/src/drivers/linux_wext.h 2014-02-04 11:23:35.000000000 +0000 @@ -0,0 +1,45 @@ +/* + * Driver interaction with generic Linux Wireless Extensions + * Copyright (c) 2003-2011, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef LINUX_WEXT_H +#define LINUX_WEXT_H + +#ifndef ANDROID + +/* + * Avoid including other kernel header to avoid conflicts with C library + * headers. + */ +#define _LINUX_TYPES_H +#define _LINUX_SOCKET_H +#define _LINUX_IF_H + +#include +#include +typedef __uint32_t __u32; +typedef __int32_t __s32; +typedef __uint16_t __u16; +typedef __int16_t __s16; +typedef __uint8_t __u8; +#ifndef __user +#define __user +#endif /* __user */ + +#endif /* ANDROID */ + +#include + +#ifndef IW_ENCODE_ALG_PMK +#define IW_ENCODE_ALG_PMK 4 +#endif + +#ifndef IW_ENC_CAPA_4WAY_HANDSHAKE +#define IW_ENC_CAPA_4WAY_HANDSHAKE 0x00000010 +#endif + +#endif /* LINUX_WEXT_H */ diff -Nru wpa-1.0/src/drivers/Makefile wpa-2.1/src/drivers/Makefile --- wpa-1.0/src/drivers/Makefile 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/drivers/Makefile 2014-02-04 11:23:35.000000000 +0000 @@ -2,7 +2,7 @@ @echo Nothing to be made. clean: - rm -f *~ *.o *.d + rm -f *~ *.o *.d *.gcno *.gcda *.gcov rm -f build.wpa_supplicant build.hostapd install: diff -Nru wpa-1.0/src/drivers/MobileApple80211.c wpa-2.1/src/drivers/MobileApple80211.c --- wpa-1.0/src/drivers/MobileApple80211.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/drivers/MobileApple80211.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,189 +0,0 @@ -#include "includes.h" -#include - -#include "common.h" - -#include -#include "MobileApple80211.h" - -/* - * Code for dynamically loading Apple80211 functions from Aeropuerto to avoid - * having to link with full Preferences.framework. - */ - -static void *aeropuerto = NULL; - - -int _Apple80211Initialized(void) -{ - return aeropuerto ? 1 : 0; -} - - -static int (*__Apple80211Open)(Apple80211Ref *ctx) = NULL; - -int Apple80211Open(Apple80211Ref *ctx) -{ - return __Apple80211Open(ctx); -} - - -static int (*__Apple80211Close)(Apple80211Ref ctx) = NULL; - -int Apple80211Close(Apple80211Ref ctx) -{ - return __Apple80211Close(ctx); -} - - -static int (*__Apple80211GetIfListCopy)(Apple80211Ref handle, CFArrayRef *list) - = NULL; - -int Apple80211GetIfListCopy(Apple80211Ref handle, CFArrayRef *list) -{ - return __Apple80211GetIfListCopy(handle, list); -} - - -static int (*__Apple80211BindToInterface)(Apple80211Ref handle, - CFStringRef interface) = NULL; - -int Apple80211BindToInterface(Apple80211Ref handle, - CFStringRef interface) -{ - return __Apple80211BindToInterface(handle, interface); -} - - -static int (*__Apple80211GetInterfaceNameCopy)(Apple80211Ref handle, - CFStringRef *name) = NULL; - -int Apple80211GetInterfaceNameCopy(Apple80211Ref handle, - CFStringRef *name) -{ - return __Apple80211GetInterfaceNameCopy(handle, name); -} - - -static int (*__Apple80211GetInfoCopy)(Apple80211Ref handle, - CFDictionaryRef *info) = NULL; - -int Apple80211GetInfoCopy(Apple80211Ref handle, - CFDictionaryRef *info) -{ - return __Apple80211GetInfoCopy(handle, info); -} - - -static int (*__Apple80211GetPower)(Apple80211Ref handle, char *pwr) = NULL; - -int Apple80211GetPower(Apple80211Ref handle, char *pwr) -{ - return __Apple80211GetPower(handle, pwr); -} - - -static int (*__Apple80211SetPower)(Apple80211Ref handle, char pwr) = NULL; - -int Apple80211SetPower(Apple80211Ref handle, char pwr) -{ - return __Apple80211SetPower(handle, pwr); -} - - -static int (*__Apple80211Scan)(Apple80211Ref handle, CFArrayRef *list, - CFDictionaryRef parameters) = NULL; - -int Apple80211Scan(Apple80211Ref handle, CFArrayRef *list, - CFDictionaryRef parameters) -{ - return __Apple80211Scan(handle, list, parameters); -} - - -static int (*__Apple80211Associate)(Apple80211Ref handle, CFDictionaryRef bss, - CFStringRef password) = NULL; - -int Apple80211Associate(Apple80211Ref handle, CFDictionaryRef bss, - CFStringRef password) -{ - return __Apple80211Associate(handle, bss, password); -} - - -static int (*__Apple80211AssociateAndCopyInfo)(Apple80211Ref handle, - CFDictionaryRef bss, - CFStringRef password, - CFDictionaryRef *info) = - NULL; - -int Apple80211AssociateAndCopyInfo(Apple80211Ref handle, CFDictionaryRef bss, - CFStringRef password, CFDictionaryRef *info) -{ - return __Apple80211AssociateAndCopyInfo(handle, bss, password, info); -} - - -static int (*__Apple80211CopyValue)(Apple80211Ref handle, int field, - CFDictionaryRef arg2, void *value) = NULL; - -int Apple80211CopyValue(Apple80211Ref handle, int field, CFDictionaryRef arg2, - void *value) -{ - return __Apple80211CopyValue(handle, field, arg2, value); -} - - -#define DLSYM(s) \ -do { \ - __ ## s = dlsym(aeropuerto, #s); \ - if (__ ## s == NULL) { \ - wpa_printf(MSG_ERROR, "MobileApple80211: Could not resolve " \ - "symbol '" #s "' (%s)", dlerror()); \ - err = 1; \ - } \ -} while (0) - - -__attribute__ ((constructor)) -void _Apple80211_constructor(void) -{ - const char *fname = "/System/Library/SystemConfiguration/" - "Aeropuerto.bundle/Aeropuerto"; - int err = 0; - - aeropuerto = dlopen(fname, RTLD_LAZY); - if (!aeropuerto) { - wpa_printf(MSG_ERROR, "MobileApple80211: Failed to open %s " - "for symbols", fname); - return; - } - - DLSYM(Apple80211Open); - DLSYM(Apple80211Close); - DLSYM(Apple80211GetIfListCopy); - DLSYM(Apple80211BindToInterface); - DLSYM(Apple80211GetInterfaceNameCopy); - DLSYM(Apple80211GetInfoCopy); - DLSYM(Apple80211GetPower); - DLSYM(Apple80211SetPower); - DLSYM(Apple80211Scan); - DLSYM(Apple80211Associate); - DLSYM(Apple80211AssociateAndCopyInfo); - DLSYM(Apple80211CopyValue); - - if (err) { - dlclose(aeropuerto); - aeropuerto = NULL; - } -} - - -__attribute__ ((destructor)) -void _Apple80211_destructor(void) -{ - if (aeropuerto) { - dlclose(aeropuerto); - aeropuerto = NULL; - } -} diff -Nru wpa-1.0/src/drivers/MobileApple80211.h wpa-2.1/src/drivers/MobileApple80211.h --- wpa-1.0/src/drivers/MobileApple80211.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/drivers/MobileApple80211.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,43 +0,0 @@ -#ifndef MOBILEAPPLE80211_H -#define MOBILEAPPLE80211_H - -/* - * MobileApple80211 interface for iPhone/iPod touch - * These functions are available from Aeropuerto. - */ - -struct Apple80211; -typedef struct Apple80211 *Apple80211Ref; - -int Apple80211Open(Apple80211Ref *ctx); -int Apple80211Close(Apple80211Ref ctx); -int Apple80211GetIfListCopy(Apple80211Ref handle, CFArrayRef *list); -int Apple80211BindToInterface(Apple80211Ref handle, - CFStringRef interface); -int Apple80211GetInterfaceNameCopy(Apple80211Ref handle, - CFStringRef *name); -int Apple80211GetInfoCopy(Apple80211Ref handle, - CFDictionaryRef *info); -int Apple80211GetPower(Apple80211Ref handle, char *pwr); -int Apple80211SetPower(Apple80211Ref handle, char pwr); - -/* parameters can be NULL; returns scan results in CFArrayRef *list; - * caller will need to free with CFRelease() */ -int Apple80211Scan(Apple80211Ref handle, CFArrayRef *list, - CFDictionaryRef parameters); - -int Apple80211Associate(Apple80211Ref handle, CFDictionaryRef bss, - CFStringRef password); -int Apple80211AssociateAndCopyInfo(Apple80211Ref handle, CFDictionaryRef bss, - CFStringRef password, - CFDictionaryRef *info); - -enum { - APPLE80211_VALUE_SSID = 1, - APPLE80211_VALUE_BSSID = 9 -}; - -int Apple80211CopyValue(Apple80211Ref handle, int field, CFDictionaryRef arg2, - void *value); - -#endif /* MOBILEAPPLE80211_H */ diff -Nru wpa-1.0/src/drivers/ndis_events.c wpa-2.1/src/drivers/ndis_events.c --- wpa-1.0/src/drivers/ndis_events.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/drivers/ndis_events.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * ndis_events - Receive NdisMIndicateStatus() events using WMI * Copyright (c) 2004-2006, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #define _WIN32_WINNT 0x0400 diff -Nru wpa-1.0/src/drivers/netlink.c wpa-2.1/src/drivers/netlink.c --- wpa-1.0/src/drivers/netlink.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/drivers/netlink.c 2014-02-04 11:23:35.000000000 +0000 @@ -1,15 +1,9 @@ /* * Netlink helper functions for driver wrappers - * Copyright (c) 2002-2009, Jouni Malinen + * Copyright (c) 2002-2014, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -103,8 +97,6 @@ if (netlink == NULL) return NULL; - netlink->cfg = cfg; - netlink->sock = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); if (netlink->sock < 0) { wpa_printf(MSG_ERROR, "netlink: Failed to open netlink " @@ -127,6 +119,8 @@ eloop_register_read_sock(netlink->sock, netlink_receive, netlink, NULL); + netlink->cfg = cfg; + return netlink; } @@ -143,6 +137,35 @@ os_free(netlink); } + +static const char * linkmode_str(int mode) +{ + switch (mode) { + case -1: + return "no change"; + case 0: + return "kernel-control"; + case 1: + return "userspace-control"; + } + return "?"; +} + + +static const char * operstate_str(int state) +{ + switch (state) { + case -1: + return "no change"; + case IF_OPER_DORMANT: + return "IF_OPER_DORMANT"; + case IF_OPER_UP: + return "IF_OPER_UP"; + } + return "?"; +} + + int netlink_send_oper_ifla(struct netlink_data *netlink, int ifindex, int linkmode, int operstate) { @@ -190,8 +213,9 @@ RTA_LENGTH(sizeof(char)); } - wpa_printf(MSG_DEBUG, "netlink: Operstate: linkmode=%d, operstate=%d", - linkmode, operstate); + wpa_printf(MSG_DEBUG, "netlink: Operstate: ifindex=%d linkmode=%d (%s), operstate=%d (%s)", + ifindex, linkmode, linkmode_str(linkmode), + operstate, operstate_str(operstate)); ret = send(netlink->sock, &req, req.hdr.nlmsg_len, 0); if (ret < 0) { diff -Nru wpa-1.0/src/drivers/netlink.h wpa-2.1/src/drivers/netlink.h --- wpa-1.0/src/drivers/netlink.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/drivers/netlink.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * Netlink helper functions for driver wrappers * Copyright (c) 2002-2009, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef NETLINK_H diff -Nru wpa-1.0/src/drivers/nl80211_copy.h wpa-2.1/src/drivers/nl80211_copy.h --- wpa-1.0/src/drivers/nl80211_copy.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/drivers/nl80211_copy.h 2014-02-04 11:23:35.000000000 +0000 @@ -27,6 +27,8 @@ #include +#define NL80211_GENL_NAME "nl80211" + /** * DOC: Station handling * @@ -36,7 +38,21 @@ * The station is still assumed to belong to the AP interface it was added * to. * - * TODO: need more info? + * Station handling varies per interface type and depending on the driver's + * capabilities. + * + * For drivers supporting TDLS with external setup (WIPHY_FLAG_SUPPORTS_TDLS + * and WIPHY_FLAG_TDLS_EXTERNAL_SETUP), the station lifetime is as follows: + * - a setup station entry is added, not yet authorized, without any rate + * or capability information, this just exists to avoid race conditions + * - when the TDLS setup is done, a single NL80211_CMD_SET_STATION is valid + * to add rate and capability information to the station and at the same + * time mark it authorized. + * - %NL80211_TDLS_ENABLE_LINK is then used + * - after this, the only valid operation is to remove it by tearing down + * the TDLS link (%NL80211_TDLS_DISABLE_LINK) + * + * TODO: need more info for other interface types */ /** @@ -110,6 +126,31 @@ */ /** + * DOC: packet coalesce support + * + * In most cases, host that receives IPv4 and IPv6 multicast/broadcast + * packets does not do anything with these packets. Therefore the + * reception of these unwanted packets causes unnecessary processing + * and power consumption. + * + * Packet coalesce feature helps to reduce number of received interrupts + * to host by buffering these packets in firmware/hardware for some + * predefined time. Received interrupt will be generated when one of the + * following events occur. + * a) Expiration of hardware timer whose expiration time is set to maximum + * coalescing delay of matching coalesce rule. + * b) Coalescing buffer in hardware reaches it's limit. + * c) Packet doesn't match any of the configured coalesce rules. + * + * User needs to configure following parameters for creating a coalesce + * rule. + * a) Maximum coalescing delay + * b) List of packet patterns which needs to be matched + * c) Condition for coalescence. pattern 'match' or 'no match' + * Multiple such rules can be created. + */ + +/** * enum nl80211_commands - supported nl80211 commands * * @NL80211_CMD_UNSPEC: unspecified command to catch errors @@ -118,8 +159,9 @@ * to get a list of all present wiphys. * @NL80211_CMD_SET_WIPHY: set wiphy parameters, needs %NL80211_ATTR_WIPHY or * %NL80211_ATTR_IFINDEX; can be used to set %NL80211_ATTR_WIPHY_NAME, - * %NL80211_ATTR_WIPHY_TXQ_PARAMS, %NL80211_ATTR_WIPHY_FREQ, - * %NL80211_ATTR_WIPHY_CHANNEL_TYPE, %NL80211_ATTR_WIPHY_RETRY_SHORT, + * %NL80211_ATTR_WIPHY_TXQ_PARAMS, %NL80211_ATTR_WIPHY_FREQ (and the + * attributes determining the channel width; this is used for setting + * monitor mode channel), %NL80211_ATTR_WIPHY_RETRY_SHORT, * %NL80211_ATTR_WIPHY_RETRY_LONG, %NL80211_ATTR_WIPHY_FRAG_THRESHOLD, * and/or %NL80211_ATTR_WIPHY_RTS_THRESHOLD. * However, for setting the channel, see %NL80211_CMD_SET_CHANNEL @@ -169,7 +211,10 @@ * %NL80211_ATTR_HIDDEN_SSID, %NL80211_ATTR_CIPHERS_PAIRWISE, * %NL80211_ATTR_CIPHER_GROUP, %NL80211_ATTR_WPA_VERSIONS, * %NL80211_ATTR_AKM_SUITES, %NL80211_ATTR_PRIVACY, - * %NL80211_ATTR_AUTH_TYPE and %NL80211_ATTR_INACTIVITY_TIMEOUT. + * %NL80211_ATTR_AUTH_TYPE, %NL80211_ATTR_INACTIVITY_TIMEOUT, + * %NL80211_ATTR_ACL_POLICY and %NL80211_ATTR_MAC_ADDRS. + * The channel to use can be set on the interface or be given using the + * %NL80211_ATTR_WIPHY_FREQ and the attributes determining channel width. * @NL80211_CMD_NEW_BEACON: old alias for %NL80211_CMD_START_AP * @NL80211_CMD_STOP_AP: Stop AP operation on the given interface * @NL80211_CMD_DEL_BEACON: old alias for %NL80211_CMD_STOP_AP @@ -275,6 +320,12 @@ * @NL80211_CMD_NEW_SURVEY_RESULTS: survey data notification (as a reply to * NL80211_CMD_GET_SURVEY and on the "scan" multicast group) * + * @NL80211_CMD_SET_PMKSA: Add a PMKSA cache entry, using %NL80211_ATTR_MAC + * (for the BSSID) and %NL80211_ATTR_PMKID. + * @NL80211_CMD_DEL_PMKSA: Delete a PMKSA cache entry, using %NL80211_ATTR_MAC + * (for the BSSID) and %NL80211_ATTR_PMKID. + * @NL80211_CMD_FLUSH_PMKSA: Flush all PMKSA cache entries. + * * @NL80211_CMD_REG_CHANGE: indicates to userspace the regulatory domain * has been changed and provides details of the request information * that caused the change such as who initiated the regulatory request @@ -365,8 +416,8 @@ * requests to connect to a specified network but without separating * auth and assoc steps. For this, you need to specify the SSID in a * %NL80211_ATTR_SSID attribute, and can optionally specify the association - * IEs in %NL80211_ATTR_IE, %NL80211_ATTR_AUTH_TYPE, %NL80211_ATTR_MAC, - * %NL80211_ATTR_WIPHY_FREQ, %NL80211_ATTR_CONTROL_PORT, + * IEs in %NL80211_ATTR_IE, %NL80211_ATTR_AUTH_TYPE, %NL80211_ATTR_USE_MFP, + * %NL80211_ATTR_MAC, %NL80211_ATTR_WIPHY_FREQ, %NL80211_ATTR_CONTROL_PORT, * %NL80211_ATTR_CONTROL_PORT_ETHERTYPE and * %NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT. * Background scan period can optionally be @@ -393,8 +444,7 @@ * a response while being associated to an AP on another channel. * %NL80211_ATTR_IFINDEX is used to specify which interface (and thus * radio) is used. %NL80211_ATTR_WIPHY_FREQ is used to specify the - * frequency for the operation and %NL80211_ATTR_WIPHY_CHANNEL_TYPE may be - * optionally used to specify additional channel parameters. + * frequency for the operation. * %NL80211_ATTR_DURATION is used to specify the duration in milliseconds * to remain on the channel. This command is also used as an event to * notify when the requested duration starts (it may take a while for the @@ -432,12 +482,11 @@ * as an event indicating reception of a frame that was not processed in * kernel code, but is for us (i.e., which may need to be processed in a * user space application). %NL80211_ATTR_FRAME is used to specify the - * frame contents (including header). %NL80211_ATTR_WIPHY_FREQ (and - * optionally %NL80211_ATTR_WIPHY_CHANNEL_TYPE) is used to indicate on - * which channel the frame is to be transmitted or was received. If this - * channel is not the current channel (remain-on-channel or the - * operational channel) the device will switch to the given channel and - * transmit the frame, optionally waiting for a response for the time + * frame contents (including header). %NL80211_ATTR_WIPHY_FREQ is used + * to indicate on which channel the frame is to be transmitted or was + * received. If this channel is not the current channel (remain-on-channel + * or the operational channel) the device will switch to the given channel + * and transmit the frame, optionally waiting for a response for the time * specified using %NL80211_ATTR_DURATION. When called, this operation * returns a cookie (%NL80211_ATTR_COOKIE) that will be included with the * TX status event pertaining to the TX request. @@ -454,6 +503,10 @@ * the frame. * @NL80211_CMD_ACTION_TX_STATUS: Alias for @NL80211_CMD_FRAME_TX_STATUS for * backward compatibility. + * + * @NL80211_CMD_SET_POWER_SAVE: Set powersave, using %NL80211_ATTR_PS_STATE + * @NL80211_CMD_GET_POWER_SAVE: Get powersave status in %NL80211_ATTR_PS_STATE + * * @NL80211_CMD_SET_CQM: Connection quality monitor configuration. This command * is used to configure connection quality monitoring notification trigger * levels. @@ -461,8 +514,8 @@ * command is used as an event to indicate the that a trigger level was * reached. * @NL80211_CMD_SET_CHANNEL: Set the channel (using %NL80211_ATTR_WIPHY_FREQ - * and %NL80211_ATTR_WIPHY_CHANNEL_TYPE) the given interface (identifed - * by %NL80211_ATTR_IFINDEX) shall operate on. + * and the attributes determining channel width) the given interface + * (identifed by %NL80211_ATTR_IFINDEX) shall operate on. * In case multiple channels are supported by the device, the mechanism * with which it switches channels is implementation-defined. * When a monitor interface is given, it can only switch channel while @@ -487,9 +540,11 @@ * @NL80211_CMD_NEW_PEER_CANDIDATE: Notification on the reception of a * beacon or probe response from a compatible mesh peer. This is only * sent while no station information (sta_info) exists for the new peer - * candidate and when @NL80211_MESH_SETUP_USERSPACE_AUTH is set. On - * reception of this notification, userspace may decide to create a new - * station (@NL80211_CMD_NEW_STATION). To stop this notification from + * candidate and when @NL80211_MESH_SETUP_USERSPACE_AUTH, + * @NL80211_MESH_SETUP_USERSPACE_AMPE, or + * @NL80211_MESH_SETUP_USERSPACE_MPM is set. On reception of this + * notification, userspace may decide to create a new station + * (@NL80211_CMD_NEW_STATION). To stop this notification from * reoccurring, the userspace authentication daemon may want to create the * new station with the AUTHENTICATED flag unset and maybe change it later * depending on the authentication result. @@ -501,6 +556,12 @@ * command with the %NL80211_ATTR_WOWLAN_TRIGGERS attribute. For * more background information, see * http://wireless.kernel.org/en/users/Documentation/WoWLAN. + * The @NL80211_CMD_SET_WOWLAN command can also be used as a notification + * from the driver reporting the wakeup reason. In this case, the + * @NL80211_ATTR_WOWLAN_TRIGGERS attribute will contain the reason + * for the wakeup, if it was caused by wireless. If it is not present + * in the wakeup notification, the wireless device didn't cause the + * wakeup but reports that it was woken up. * * @NL80211_CMD_SET_REKEY_OFFLOAD: This command is used give the driver * the necessary information for supporting GTK rekey offload. This @@ -514,7 +575,138 @@ * of PMKSA caching dandidates. * * @NL80211_CMD_TDLS_OPER: Perform a high-level TDLS command (e.g. link setup). - * @NL80211_CMD_TDLS_MGMT: Send a TDLS management frame. + * In addition, this can be used as an event to request userspace to take + * actions on TDLS links (set up a new link or tear down an existing one). + * In such events, %NL80211_ATTR_TDLS_OPERATION indicates the requested + * operation, %NL80211_ATTR_MAC contains the peer MAC address, and + * %NL80211_ATTR_REASON_CODE the reason code to be used (only with + * %NL80211_TDLS_TEARDOWN). + * @NL80211_CMD_TDLS_MGMT: Send a TDLS management frame. The + * %NL80211_ATTR_TDLS_ACTION attribute determines the type of frame to be + * sent. Public Action codes (802.11-2012 8.1.5.1) will be sent as + * 802.11 management frames, while TDLS action codes (802.11-2012 + * 8.5.13.1) will be encapsulated and sent as data frames. The currently + * supported Public Action code is %WLAN_PUB_ACTION_TDLS_DISCOVER_RES + * and the currently supported TDLS actions codes are given in + * &enum ieee80211_tdls_actioncode. + * + * @NL80211_CMD_UNEXPECTED_FRAME: Used by an application controlling an AP + * (or GO) interface (i.e. hostapd) to ask for unexpected frames to + * implement sending deauth to stations that send unexpected class 3 + * frames. Also used as the event sent by the kernel when such a frame + * is received. + * For the event, the %NL80211_ATTR_MAC attribute carries the TA and + * other attributes like the interface index are present. + * If used as the command it must have an interface index and you can + * only unsubscribe from the event by closing the socket. Subscription + * is also for %NL80211_CMD_UNEXPECTED_4ADDR_FRAME events. + * + * @NL80211_CMD_UNEXPECTED_4ADDR_FRAME: Sent as an event indicating that the + * associated station identified by %NL80211_ATTR_MAC sent a 4addr frame + * and wasn't already in a 4-addr VLAN. The event will be sent similarly + * to the %NL80211_CMD_UNEXPECTED_FRAME event, to the same listener. + * + * @NL80211_CMD_PROBE_CLIENT: Probe an associated station on an AP interface + * by sending a null data frame to it and reporting when the frame is + * acknowleged. This is used to allow timing out inactive clients. Uses + * %NL80211_ATTR_IFINDEX and %NL80211_ATTR_MAC. The command returns a + * direct reply with an %NL80211_ATTR_COOKIE that is later used to match + * up the event with the request. The event includes the same data and + * has %NL80211_ATTR_ACK set if the frame was ACKed. + * + * @NL80211_CMD_REGISTER_BEACONS: Register this socket to receive beacons from + * other BSSes when any interfaces are in AP mode. This helps implement + * OLBC handling in hostapd. Beacons are reported in %NL80211_CMD_FRAME + * messages. Note that per PHY only one application may register. + * + * @NL80211_CMD_SET_NOACK_MAP: sets a bitmap for the individual TIDs whether + * No Acknowledgement Policy should be applied. + * + * @NL80211_CMD_CH_SWITCH_NOTIFY: An AP or GO may decide to switch channels + * independently of the userspace SME, send this event indicating + * %NL80211_ATTR_IFINDEX is now on %NL80211_ATTR_WIPHY_FREQ and the + * attributes determining channel width. + * + * @NL80211_CMD_START_P2P_DEVICE: Start the given P2P Device, identified by + * its %NL80211_ATTR_WDEV identifier. It must have been created with + * %NL80211_CMD_NEW_INTERFACE previously. After it has been started, the + * P2P Device can be used for P2P operations, e.g. remain-on-channel and + * public action frame TX. + * @NL80211_CMD_STOP_P2P_DEVICE: Stop the given P2P Device, identified by + * its %NL80211_ATTR_WDEV identifier. + * + * @NL80211_CMD_CONN_FAILED: connection request to an AP failed; used to + * notify userspace that AP has rejected the connection request from a + * station, due to particular reason. %NL80211_ATTR_CONN_FAILED_REASON + * is used for this. + * + * @NL80211_CMD_SET_MCAST_RATE: Change the rate used to send multicast frames + * for IBSS or MESH vif. + * + * @NL80211_CMD_SET_MAC_ACL: sets ACL for MAC address based access control. + * This is to be used with the drivers advertising the support of MAC + * address based access control. List of MAC addresses is passed in + * %NL80211_ATTR_MAC_ADDRS and ACL policy is passed in + * %NL80211_ATTR_ACL_POLICY. Driver will enable ACL with this list, if it + * is not already done. The new list will replace any existing list. Driver + * will clear its ACL when the list of MAC addresses passed is empty. This + * command is used in AP/P2P GO mode. Driver has to make sure to clear its + * ACL list during %NL80211_CMD_STOP_AP. + * + * @NL80211_CMD_RADAR_DETECT: Start a Channel availability check (CAC). Once + * a radar is detected or the channel availability scan (CAC) has finished + * or was aborted, or a radar was detected, usermode will be notified with + * this event. This command is also used to notify userspace about radars + * while operating on this channel. + * %NL80211_ATTR_RADAR_EVENT is used to inform about the type of the + * event. + * + * @NL80211_CMD_GET_PROTOCOL_FEATURES: Get global nl80211 protocol features, + * i.e. features for the nl80211 protocol rather than device features. + * Returns the features in the %NL80211_ATTR_PROTOCOL_FEATURES bitmap. + * + * @NL80211_CMD_UPDATE_FT_IES: Pass down the most up-to-date Fast Transition + * Information Element to the WLAN driver + * + * @NL80211_CMD_FT_EVENT: Send a Fast transition event from the WLAN driver + * to the supplicant. This will carry the target AP's MAC address along + * with the relevant Information Elements. This event is used to report + * received FT IEs (MDIE, FTIE, RSN IE, TIE, RICIE). + * + * @NL80211_CMD_CRIT_PROTOCOL_START: Indicates user-space will start running + * a critical protocol that needs more reliability in the connection to + * complete. + * + * @NL80211_CMD_CRIT_PROTOCOL_STOP: Indicates the connection reliability can + * return back to normal. + * + * @NL80211_CMD_GET_COALESCE: Get currently supported coalesce rules. + * @NL80211_CMD_SET_COALESCE: Configure coalesce rules or clear existing rules. + * + * @NL80211_CMD_CHANNEL_SWITCH: Perform a channel switch by announcing the + * the new channel information (Channel Switch Announcement - CSA) + * in the beacon for some time (as defined in the + * %NL80211_ATTR_CH_SWITCH_COUNT parameter) and then change to the + * new channel. Userspace provides the new channel information (using + * %NL80211_ATTR_WIPHY_FREQ and the attributes determining channel + * width). %NL80211_ATTR_CH_SWITCH_BLOCK_TX may be supplied to inform + * other station that transmission must be blocked until the channel + * switch is complete. + * + * @NL80211_CMD_VENDOR: Vendor-specified command/event. The command is specified + * by the %NL80211_ATTR_VENDOR_ID attribute and a sub-command in + * %NL80211_ATTR_VENDOR_SUBCMD. Parameter(s) can be transported in + * %NL80211_ATTR_VENDOR_DATA. + * For feature advertisement, the %NL80211_ATTR_VENDOR_DATA attribute is + * used in the wiphy data as a nested attribute containing descriptions + * (&struct nl80211_vendor_cmd_info) of the supported vendor commands. + * This may also be sent as an event with the same attributes. + * + * @NL80211_CMD_SET_QOS_MAP: Set Interworking QoS mapping for IP DSCP values. + * The QoS mapping information is included in %NL80211_ATTR_QOS_MAP. If + * that attribute is not included, QoS mapping is disabled. Since this + * QoS mapping is relevant for IP packets, it is only valid during an + * association. This is cleared on disassociation and AP restart. * * @NL80211_CMD_MAX: highest used command number * @__NL80211_CMD_AFTER_LAST: internal use @@ -647,6 +839,46 @@ NL80211_CMD_TDLS_OPER, NL80211_CMD_TDLS_MGMT, + NL80211_CMD_UNEXPECTED_FRAME, + + NL80211_CMD_PROBE_CLIENT, + + NL80211_CMD_REGISTER_BEACONS, + + NL80211_CMD_UNEXPECTED_4ADDR_FRAME, + + NL80211_CMD_SET_NOACK_MAP, + + NL80211_CMD_CH_SWITCH_NOTIFY, + + NL80211_CMD_START_P2P_DEVICE, + NL80211_CMD_STOP_P2P_DEVICE, + + NL80211_CMD_CONN_FAILED, + + NL80211_CMD_SET_MCAST_RATE, + + NL80211_CMD_SET_MAC_ACL, + + NL80211_CMD_RADAR_DETECT, + + NL80211_CMD_GET_PROTOCOL_FEATURES, + + NL80211_CMD_UPDATE_FT_IES, + NL80211_CMD_FT_EVENT, + + NL80211_CMD_CRIT_PROTOCOL_START, + NL80211_CMD_CRIT_PROTOCOL_STOP, + + NL80211_CMD_GET_COALESCE, + NL80211_CMD_SET_COALESCE, + + NL80211_CMD_CHANNEL_SWITCH, + + NL80211_CMD_VENDOR, + + NL80211_CMD_SET_QOS_MAP, + /* add new commands above here */ /* used to define NL80211_CMD_MAX below */ @@ -667,6 +899,8 @@ #define NL80211_CMD_DISASSOCIATE NL80211_CMD_DISASSOCIATE #define NL80211_CMD_REG_BEACON_HINT NL80211_CMD_REG_BEACON_HINT +#define NL80211_ATTR_FEATURE_FLAGS NL80211_ATTR_FEATURE_FLAGS + /* source-level API compatibility */ #define NL80211_CMD_GET_MESH_PARAMS NL80211_CMD_GET_MESH_CONFIG #define NL80211_CMD_SET_MESH_PARAMS NL80211_CMD_SET_MESH_CONFIG @@ -681,14 +915,26 @@ * /sys/class/ieee80211//index * @NL80211_ATTR_WIPHY_NAME: wiphy name (used for renaming) * @NL80211_ATTR_WIPHY_TXQ_PARAMS: a nested array of TX queue parameters - * @NL80211_ATTR_WIPHY_FREQ: frequency of the selected channel in MHz + * @NL80211_ATTR_WIPHY_FREQ: frequency of the selected channel in MHz, + * defines the channel together with the (deprecated) + * %NL80211_ATTR_WIPHY_CHANNEL_TYPE attribute or the attributes + * %NL80211_ATTR_CHANNEL_WIDTH and if needed %NL80211_ATTR_CENTER_FREQ1 + * and %NL80211_ATTR_CENTER_FREQ2 + * @NL80211_ATTR_CHANNEL_WIDTH: u32 attribute containing one of the values + * of &enum nl80211_chan_width, describing the channel width. See the + * documentation of the enum for more information. + * @NL80211_ATTR_CENTER_FREQ1: Center frequency of the first part of the + * channel, used for anything but 20 MHz bandwidth + * @NL80211_ATTR_CENTER_FREQ2: Center frequency of the second part of the + * channel, used only for 80+80 MHz bandwidth * @NL80211_ATTR_WIPHY_CHANNEL_TYPE: included with NL80211_ATTR_WIPHY_FREQ - * if HT20 or HT40 are allowed (i.e., 802.11n disabled if not included): + * if HT20 or HT40 are to be used (i.e., HT disabled if not included): * NL80211_CHAN_NO_HT = HT not allowed (i.e., same as not including * this attribute) * NL80211_CHAN_HT20 = HT20 only * NL80211_CHAN_HT40MINUS = secondary channel is below the primary channel * NL80211_CHAN_HT40PLUS = secondary channel is above the primary channel + * This attribute is now deprecated. * @NL80211_ATTR_WIPHY_RETRY_SHORT: TX retry limit for frames whose length is * less than or equal to the RTS threshold; allowed range: 1..255; * dot11ShortRetryLimit; u8 @@ -708,6 +954,9 @@ * @NL80211_ATTR_IFNAME: network interface name * @NL80211_ATTR_IFTYPE: type of virtual interface, see &enum nl80211_iftype * + * @NL80211_ATTR_WDEV: wireless device identifier, used for pseudo-devices + * that don't have a netdev (u64) + * * @NL80211_ATTR_MAC: MAC address (various uses) * * @NL80211_ATTR_KEY_DATA: (temporal) key data; for TKIP this consists of @@ -718,6 +967,13 @@ * section 7.3.2.25.1, e.g. 0x000FAC04) * @NL80211_ATTR_KEY_SEQ: transmit key sequence number (IV/PN) for TKIP and * CCMP keys, each six bytes in little endian + * @NL80211_ATTR_KEY_DEFAULT: Flag attribute indicating the key is default key + * @NL80211_ATTR_KEY_DEFAULT_MGMT: Flag attribute indicating the key is the + * default management key + * @NL80211_ATTR_CIPHER_SUITES_PAIRWISE: For crypto settings for connect or + * other commands, indicates which pairwise cipher suites are used + * @NL80211_ATTR_CIPHER_SUITE_GROUP: For crypto settings for connect or + * other commands, indicates which group cipher suite is used * * @NL80211_ATTR_BEACON_INTERVAL: beacon interval in TU * @NL80211_ATTR_DTIM_PERIOD: DTIM period for beaconing @@ -742,7 +998,8 @@ * consisting of a nested array. * * @NL80211_ATTR_MESH_ID: mesh id (1-32 bytes). - * @NL80211_ATTR_STA_PLINK_ACTION: action to perform on the mesh peer link. + * @NL80211_ATTR_STA_PLINK_ACTION: action to perform on the mesh peer link + * (see &enum nl80211_plink_action). * @NL80211_ATTR_MPATH_NEXT_HOP: MAC address of the next hop for a mesh path. * @NL80211_ATTR_MPATH_INFO: information about a mesh_path, part of mesh path * info given for %NL80211_CMD_GET_MPATH, nested attribute described at @@ -757,7 +1014,7 @@ * to query the CRDA to retrieve one regulatory domain. This attribute can * also be used by userspace to query the kernel for the currently set * regulatory domain. We chose an alpha2 as that is also used by the - * IEEE-802.11d country information element to identify a country. + * IEEE-802.11 country information element to identify a country. * Users can also simply ask the wireless core to set regulatory domain * to a specific alpha2. * @NL80211_ATTR_REG_RULES: a nested array of regulatory domain regulatory @@ -845,7 +1102,7 @@ * @NL80211_ATTR_USE_MFP: Whether management frame protection (IEEE 802.11w) is * used for the association (&enum nl80211_mfp, represented as a u32); * this attribute can be used - * with %NL80211_CMD_ASSOCIATE request + * with %NL80211_CMD_ASSOCIATE and %NL80211_CMD_CONNECT requests * * @NL80211_ATTR_STA_FLAGS2: Attribute containing a * &struct nl80211_sta_flag_update. @@ -953,6 +1210,8 @@ * @NL80211_ATTR_ACK: Flag attribute indicating that the frame was * acknowledged by the recipient. * + * @NL80211_ATTR_PS_STATE: powersave state, using &enum nl80211_ps_state values. + * * @NL80211_ATTR_CQM: connection quality monitor configuration in a * nested attribute with %NL80211_ATTR_CQM_* sub-attributes. * @@ -1010,7 +1269,7 @@ * flag isn't set, the frame will be rejected. This is also used as an * nl80211 capability flag. * - * @NL80211_ATTR_BSS_HTOPMODE: HT operation mode (u16) + * @NL80211_ATTR_BSS_HT_OPMODE: HT operation mode (u16) * * @NL80211_ATTR_KEY_DEFAULT_TYPES: A nested attribute containing flags * attributes, specifying what a key should be set as default as. @@ -1023,10 +1282,10 @@ * @NL80211_ATTR_SUPPORT_MESH_AUTH: Currently, this means the underlying driver * allows auth frames in a mesh to be passed to userspace for processing via * the @NL80211_MESH_SETUP_USERSPACE_AUTH flag. - * @NL80211_ATTR_STA_PLINK_STATE: The state of a mesh peer link as - * defined in &enum nl80211_plink_state. Used when userspace is - * driving the peer link management state machine. - * @NL80211_MESH_SETUP_USERSPACE_AMPE must be enabled. + * @NL80211_ATTR_STA_PLINK_STATE: The state of a mesh peer link as defined in + * &enum nl80211_plink_state. Used when userspace is driving the peer link + * management state machine. @NL80211_MESH_SETUP_USERSPACE_AMPE or + * @NL80211_MESH_SETUP_USERSPACE_MPM must be enabled. * * @NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED: indicates, as part of the wiphy * capabilities, the supported WoWLAN triggers @@ -1034,10 +1293,10 @@ * indicate which WoW triggers should be enabled. This is also * used by %NL80211_CMD_GET_WOWLAN to get the currently enabled WoWLAN * triggers. - + * * @NL80211_ATTR_SCHED_SCAN_INTERVAL: Interval between scheduled scan * cycles, in msecs. - + * * @NL80211_ATTR_SCHED_SCAN_MATCH: Nested attribute with one or more * sets of attributes to match during scheduled scans. Only BSSs * that match any of the sets will be reported. These are @@ -1064,7 +1323,7 @@ * are managed in software: interfaces of these types aren't subject to * any restrictions in their number or combinations. * - * @%NL80211_ATTR_REKEY_DATA: nested attribute containing the information + * @NL80211_ATTR_REKEY_DATA: nested attribute containing the information * necessary for GTK rekeying in the device, see &enum nl80211_rekey_data. * * @NL80211_ATTR_SCAN_SUPP_RATES: rates per to be advertised as supported in scan, @@ -1131,7 +1390,6 @@ * @NL80211_ATTR_FEATURE_FLAGS: This u32 attribute contains flags from * &enum nl80211_feature_flags and is advertised in wiphy information. * @NL80211_ATTR_PROBE_RESP_OFFLOAD: Indicates that the HW responds to probe - * * requests while operating in AP-mode. * This attribute holds a bitmap of the supported protocols for * offloading (see &enum nl80211_probe_resp_offload_support_attr). @@ -1171,6 +1429,132 @@ * @NL80211_ATTR_BG_SCAN_PERIOD: Background scan period in seconds * or 0 to disable background scan. * + * @NL80211_ATTR_USER_REG_HINT_TYPE: type of regulatory hint passed from + * userspace. If unset it is assumed the hint comes directly from + * a user. If set code could specify exactly what type of source + * was used to provide the hint. For the different types of + * allowed user regulatory hints see nl80211_user_reg_hint_type. + * + * @NL80211_ATTR_CONN_FAILED_REASON: The reason for which AP has rejected + * the connection request from a station. nl80211_connect_failed_reason + * enum has different reasons of connection failure. + * + * @NL80211_ATTR_SAE_DATA: SAE elements in Authentication frames. This starts + * with the Authentication transaction sequence number field. + * + * @NL80211_ATTR_VHT_CAPABILITY: VHT Capability information element (from + * association request when used with NL80211_CMD_NEW_STATION) + * + * @NL80211_ATTR_SCAN_FLAGS: scan request control flags (u32) + * + * @NL80211_ATTR_P2P_CTWINDOW: P2P GO Client Traffic Window (u8), used with + * the START_AP and SET_BSS commands + * @NL80211_ATTR_P2P_OPPPS: P2P GO opportunistic PS (u8), used with the + * START_AP and SET_BSS commands. This can have the values 0 or 1; + * if not given in START_AP 0 is assumed, if not given in SET_BSS + * no change is made. + * + * @NL80211_ATTR_LOCAL_MESH_POWER_MODE: local mesh STA link-specific power mode + * defined in &enum nl80211_mesh_power_mode. + * + * @NL80211_ATTR_ACL_POLICY: ACL policy, see &enum nl80211_acl_policy, + * carried in a u32 attribute + * + * @NL80211_ATTR_MAC_ADDRS: Array of nested MAC addresses, used for + * MAC ACL. + * + * @NL80211_ATTR_MAC_ACL_MAX: u32 attribute to advertise the maximum + * number of MAC addresses that a device can support for MAC + * ACL. + * + * @NL80211_ATTR_RADAR_EVENT: Type of radar event for notification to userspace, + * contains a value of enum nl80211_radar_event (u32). + * + * @NL80211_ATTR_EXT_CAPA: 802.11 extended capabilities that the kernel driver + * has and handles. The format is the same as the IE contents. See + * 802.11-2012 8.4.2.29 for more information. + * @NL80211_ATTR_EXT_CAPA_MASK: Extended capabilities that the kernel driver + * has set in the %NL80211_ATTR_EXT_CAPA value, for multibit fields. + * + * @NL80211_ATTR_STA_CAPABILITY: Station capabilities (u16) are advertised to + * the driver, e.g., to enable TDLS power save (PU-APSD). + * + * @NL80211_ATTR_STA_EXT_CAPABILITY: Station extended capabilities are + * advertised to the driver, e.g., to enable TDLS off channel operations + * and PU-APSD. + * + * @NL80211_ATTR_PROTOCOL_FEATURES: global nl80211 feature flags, see + * &enum nl80211_protocol_features, the attribute is a u32. + * + * @NL80211_ATTR_SPLIT_WIPHY_DUMP: flag attribute, userspace supports + * receiving the data for a single wiphy split across multiple + * messages, given with wiphy dump message + * + * @NL80211_ATTR_MDID: Mobility Domain Identifier + * + * @NL80211_ATTR_IE_RIC: Resource Information Container Information + * Element + * + * @NL80211_ATTR_CRIT_PROT_ID: critical protocol identifier requiring increased + * reliability, see &enum nl80211_crit_proto_id (u16). + * @NL80211_ATTR_MAX_CRIT_PROT_DURATION: duration in milliseconds in which + * the connection should have increased reliability (u16). + * + * @NL80211_ATTR_PEER_AID: Association ID for the peer TDLS station (u16). + * This is similar to @NL80211_ATTR_STA_AID but with a difference of being + * allowed to be used with the first @NL80211_CMD_SET_STATION command to + * update a TDLS peer STA entry. + * + * @NL80211_ATTR_COALESCE_RULE: Coalesce rule information. + * + * @NL80211_ATTR_CH_SWITCH_COUNT: u32 attribute specifying the number of TBTT's + * until the channel switch event. + * @NL80211_ATTR_CH_SWITCH_BLOCK_TX: flag attribute specifying that transmission + * must be blocked on the current channel (before the channel switch + * operation). + * @NL80211_ATTR_CSA_IES: Nested set of attributes containing the IE information + * for the time while performing a channel switch. + * @NL80211_ATTR_CSA_C_OFF_BEACON: Offset of the channel switch counter + * field in the beacons tail (%NL80211_ATTR_BEACON_TAIL). + * @NL80211_ATTR_CSA_C_OFF_PRESP: Offset of the channel switch counter + * field in the probe response (%NL80211_ATTR_PROBE_RESP). + * + * @NL80211_ATTR_RXMGMT_FLAGS: flags for nl80211_send_mgmt(), u32. + * As specified in the &enum nl80211_rxmgmt_flags. + * + * @NL80211_ATTR_STA_SUPPORTED_CHANNELS: array of supported channels. + * + * @NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES: array of supported + * supported operating classes. + * + * @NL80211_ATTR_HANDLE_DFS: A flag indicating whether user space + * controls DFS operation in IBSS mode. If the flag is included in + * %NL80211_CMD_JOIN_IBSS request, the driver will allow use of DFS + * channels and reports radar events to userspace. Userspace is required + * to react to radar events, e.g. initiate a channel switch or leave the + * IBSS network. + * + * @NL80211_ATTR_SUPPORT_5_MHZ: A flag indicating that the device supports + * 5 MHz channel bandwidth. + * @NL80211_ATTR_SUPPORT_10_MHZ: A flag indicating that the device supports + * 10 MHz channel bandwidth. + * + * @NL80211_ATTR_OPMODE_NOTIF: Operating mode field from Operating Mode + * Notification Element based on association request when used with + * %NL80211_CMD_NEW_STATION; u8 attribute. + * + * @NL80211_ATTR_VENDOR_ID: The vendor ID, either a 24-bit OUI or, if + * %NL80211_VENDOR_ID_IS_LINUX is set, a special Linux ID (not used yet) + * @NL80211_ATTR_VENDOR_SUBCMD: vendor sub-command + * @NL80211_ATTR_VENDOR_DATA: data for the vendor command, if any; this + * attribute is also used for vendor command feature advertisement + * @NL80211_ATTR_VENDOR_EVENTS: used for event list advertising in the wiphy + * info, containing a nested array of possible events + * + * @NL80211_ATTR_QOS_MAP: IP DSCP mapping for Interworking QoS mapping. This + * data is in the format defined for the payload of the QoS Map Set element + * in IEEE Std 802.11-2012, 8.4.2.97. + * * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use */ @@ -1422,6 +1806,83 @@ NL80211_ATTR_BG_SCAN_PERIOD, + NL80211_ATTR_WDEV, + + NL80211_ATTR_USER_REG_HINT_TYPE, + + NL80211_ATTR_CONN_FAILED_REASON, + + NL80211_ATTR_SAE_DATA, + + NL80211_ATTR_VHT_CAPABILITY, + + NL80211_ATTR_SCAN_FLAGS, + + NL80211_ATTR_CHANNEL_WIDTH, + NL80211_ATTR_CENTER_FREQ1, + NL80211_ATTR_CENTER_FREQ2, + + NL80211_ATTR_P2P_CTWINDOW, + NL80211_ATTR_P2P_OPPPS, + + NL80211_ATTR_LOCAL_MESH_POWER_MODE, + + NL80211_ATTR_ACL_POLICY, + + NL80211_ATTR_MAC_ADDRS, + + NL80211_ATTR_MAC_ACL_MAX, + + NL80211_ATTR_RADAR_EVENT, + + NL80211_ATTR_EXT_CAPA, + NL80211_ATTR_EXT_CAPA_MASK, + + NL80211_ATTR_STA_CAPABILITY, + NL80211_ATTR_STA_EXT_CAPABILITY, + + NL80211_ATTR_PROTOCOL_FEATURES, + NL80211_ATTR_SPLIT_WIPHY_DUMP, + + NL80211_ATTR_DISABLE_VHT, + NL80211_ATTR_VHT_CAPABILITY_MASK, + + NL80211_ATTR_MDID, + NL80211_ATTR_IE_RIC, + + NL80211_ATTR_CRIT_PROT_ID, + NL80211_ATTR_MAX_CRIT_PROT_DURATION, + + NL80211_ATTR_PEER_AID, + + NL80211_ATTR_COALESCE_RULE, + + NL80211_ATTR_CH_SWITCH_COUNT, + NL80211_ATTR_CH_SWITCH_BLOCK_TX, + NL80211_ATTR_CSA_IES, + NL80211_ATTR_CSA_C_OFF_BEACON, + NL80211_ATTR_CSA_C_OFF_PRESP, + + NL80211_ATTR_RXMGMT_FLAGS, + + NL80211_ATTR_STA_SUPPORTED_CHANNELS, + + NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES, + + NL80211_ATTR_HANDLE_DFS, + + NL80211_ATTR_SUPPORT_5_MHZ, + NL80211_ATTR_SUPPORT_10_MHZ, + + NL80211_ATTR_OPMODE_NOTIF, + + NL80211_ATTR_VENDOR_ID, + NL80211_ATTR_VENDOR_SUBCMD, + NL80211_ATTR_VENDOR_DATA, + NL80211_ATTR_VENDOR_EVENTS, + + NL80211_ATTR_QOS_MAP, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, @@ -1456,6 +1917,7 @@ #define NL80211_ATTR_AKM_SUITES NL80211_ATTR_AKM_SUITES #define NL80211_ATTR_KEY NL80211_ATTR_KEY #define NL80211_ATTR_KEYS NL80211_ATTR_KEYS +#define NL80211_ATTR_FEATURE_FLAGS NL80211_ATTR_FEATURE_FLAGS #define NL80211_MAX_SUPP_RATES 32 #define NL80211_MAX_SUPP_HT_RATES 77 @@ -1464,10 +1926,18 @@ #define NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY 16 #define NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY 24 #define NL80211_HT_CAPABILITY_LEN 26 +#define NL80211_VHT_CAPABILITY_LEN 12 #define NL80211_MAX_NR_CIPHER_SUITES 5 #define NL80211_MAX_NR_AKM_SUITES 2 +#define NL80211_MIN_REMAIN_ON_CHANNEL_TIME 10 + +/* default RSSI threshold for scan results if none specified. */ +#define NL80211_SCAN_RSSI_THOLD_OFF -300 + +#define NL80211_CQM_TXE_MAX_INTVL 1800 + /** * enum nl80211_iftype - (virtual) interface types * @@ -1483,6 +1953,10 @@ * @NL80211_IFTYPE_MESH_POINT: mesh point * @NL80211_IFTYPE_P2P_CLIENT: P2P client * @NL80211_IFTYPE_P2P_GO: P2P group owner + * @NL80211_IFTYPE_P2P_DEVICE: P2P device interface type, this is not a netdev + * and therefore can't be created in the normal ways, use the + * %NL80211_CMD_START_P2P_DEVICE and %NL80211_CMD_STOP_P2P_DEVICE + * commands to create and destroy one * @NL80211_IFTYPE_MAX: highest interface type number currently defined * @NUM_NL80211_IFTYPES: number of defined interface types * @@ -1501,6 +1975,7 @@ NL80211_IFTYPE_MESH_POINT, NL80211_IFTYPE_P2P_CLIENT, NL80211_IFTYPE_P2P_GO, + NL80211_IFTYPE_P2P_DEVICE, /* keep last */ NUM_NL80211_IFTYPES, @@ -1520,7 +1995,14 @@ * @NL80211_STA_FLAG_WME: station is WME/QoS capable * @NL80211_STA_FLAG_MFP: station uses management frame protection * @NL80211_STA_FLAG_AUTHENTICATED: station is authenticated - * @NL80211_STA_FLAG_TDLS_PEER: station is a TDLS peer + * @NL80211_STA_FLAG_TDLS_PEER: station is a TDLS peer -- this flag should + * only be used in managed mode (even in the flags mask). Note that the + * flag can't be changed, it is only valid while adding a station, and + * attempts to change it will silently be ignored (rather than rejected + * as errors.) + * @NL80211_STA_FLAG_ASSOCIATED: station is associated; used with drivers + * that support %NL80211_FEATURE_FULL_AP_CLIENT_STATE to transition a + * previously added station into associated state * @NL80211_STA_FLAG_MAX: highest station flag number currently defined * @__NL80211_STA_FLAG_AFTER_LAST: internal use */ @@ -1532,12 +2014,15 @@ NL80211_STA_FLAG_MFP, NL80211_STA_FLAG_AUTHENTICATED, NL80211_STA_FLAG_TDLS_PEER, + NL80211_STA_FLAG_ASSOCIATED, /* keep last */ __NL80211_STA_FLAG_AFTER_LAST, NL80211_STA_FLAG_MAX = __NL80211_STA_FLAG_AFTER_LAST - 1 }; +#define NL80211_STA_FLAG_MAX_OLD_API NL80211_STA_FLAG_TDLS_PEER + /** * struct nl80211_sta_flag_update - station flags mask/set * @mask: mask of station flags to set @@ -1555,13 +2040,26 @@ * * These attribute types are used with %NL80211_STA_INFO_TXRATE * when getting information about the bitrate of a station. + * There are 2 attributes for bitrate, a legacy one that represents + * a 16-bit value, and new one that represents a 32-bit value. + * If the rate value fits into 16 bit, both attributes are reported + * with the same value. If the rate is too high to fit into 16 bits + * (>6.5535Gbps) only 32-bit attribute is included. + * User space tools encouraged to use the 32-bit attribute and fall + * back to the 16-bit one for compatibility with older kernels. * * @__NL80211_RATE_INFO_INVALID: attribute number 0 is reserved * @NL80211_RATE_INFO_BITRATE: total bitrate (u16, 100kbit/s) * @NL80211_RATE_INFO_MCS: mcs index for 802.11n (u8) - * @NL80211_RATE_INFO_40_MHZ_WIDTH: 40 Mhz dualchannel bitrate + * @NL80211_RATE_INFO_40_MHZ_WIDTH: 40 MHz dualchannel bitrate * @NL80211_RATE_INFO_SHORT_GI: 400ns guard interval + * @NL80211_RATE_INFO_BITRATE32: total bitrate (u32, 100kbit/s) * @NL80211_RATE_INFO_MAX: highest rate_info number currently defined + * @NL80211_RATE_INFO_VHT_MCS: MCS index for VHT (u8) + * @NL80211_RATE_INFO_VHT_NSS: number of streams in VHT (u8) + * @NL80211_RATE_INFO_80_MHZ_WIDTH: 80 MHz VHT rate + * @NL80211_RATE_INFO_80P80_MHZ_WIDTH: 80+80 MHz VHT rate + * @NL80211_RATE_INFO_160_MHZ_WIDTH: 160 MHz VHT rate * @__NL80211_RATE_INFO_AFTER_LAST: internal use */ enum nl80211_rate_info { @@ -1570,6 +2068,12 @@ NL80211_RATE_INFO_MCS, NL80211_RATE_INFO_40_MHZ_WIDTH, NL80211_RATE_INFO_SHORT_GI, + NL80211_RATE_INFO_BITRATE32, + NL80211_RATE_INFO_VHT_MCS, + NL80211_RATE_INFO_VHT_NSS, + NL80211_RATE_INFO_80_MHZ_WIDTH, + NL80211_RATE_INFO_80P80_MHZ_WIDTH, + NL80211_RATE_INFO_160_MHZ_WIDTH, /* keep last */ __NL80211_RATE_INFO_AFTER_LAST, @@ -1616,6 +2120,8 @@ * @NL80211_STA_INFO_INACTIVE_TIME: time since last activity (u32, msecs) * @NL80211_STA_INFO_RX_BYTES: total received bytes (u32, from this station) * @NL80211_STA_INFO_TX_BYTES: total transmitted bytes (u32, to this station) + * @NL80211_STA_INFO_RX_BYTES64: total received bytes (u64, from this station) + * @NL80211_STA_INFO_TX_BYTES64: total transmitted bytes (u64, to this station) * @NL80211_STA_INFO_SIGNAL: signal strength of last received PPDU (u8, dBm) * @NL80211_STA_INFO_TX_BITRATE: current unicast tx rate, nested attribute * containing info as possible, see &enum nl80211_rate_info @@ -1634,6 +2140,17 @@ * @NL80211_STA_INFO_BSS_PARAM: current station's view of BSS, nested attribute * containing info as possible, see &enum nl80211_sta_bss_param * @NL80211_STA_INFO_CONNECTED_TIME: time since the station is last connected + * @NL80211_STA_INFO_STA_FLAGS: Contains a struct nl80211_sta_flag_update. + * @NL80211_STA_INFO_BEACON_LOSS: count of times beacon loss was detected (u32) + * @NL80211_STA_INFO_T_OFFSET: timing offset with respect to this STA (s64) + * @NL80211_STA_INFO_LOCAL_PM: local mesh STA link-specific power mode + * @NL80211_STA_INFO_PEER_PM: peer mesh STA link-specific power mode + * @NL80211_STA_INFO_NONPEER_PM: neighbor mesh STA power save mode towards + * non-peer STA + * @NL80211_STA_INFO_CHAIN_SIGNAL: per-chain signal strength of last PPDU + * Contains a nested array of signal strength attributes (u8, dBm) + * @NL80211_STA_INFO_CHAIN_SIGNAL_AVG: per-chain signal strength average + * Same format as NL80211_STA_INFO_CHAIN_SIGNAL. * @__NL80211_STA_INFO_AFTER_LAST: internal * @NL80211_STA_INFO_MAX: highest possible station info attribute */ @@ -1655,6 +2172,16 @@ NL80211_STA_INFO_RX_BITRATE, NL80211_STA_INFO_BSS_PARAM, NL80211_STA_INFO_CONNECTED_TIME, + NL80211_STA_INFO_STA_FLAGS, + NL80211_STA_INFO_BEACON_LOSS, + NL80211_STA_INFO_T_OFFSET, + NL80211_STA_INFO_LOCAL_PM, + NL80211_STA_INFO_PEER_PM, + NL80211_STA_INFO_NONPEER_PM, + NL80211_STA_INFO_RX_BYTES64, + NL80211_STA_INFO_TX_BYTES64, + NL80211_STA_INFO_CHAIN_SIGNAL, + NL80211_STA_INFO_CHAIN_SIGNAL_AVG, /* keep last */ __NL80211_STA_INFO_AFTER_LAST, @@ -1724,6 +2251,9 @@ * @NL80211_BAND_ATTR_HT_CAPA: HT capabilities, as in the HT information IE * @NL80211_BAND_ATTR_HT_AMPDU_FACTOR: A-MPDU factor, as in 11n * @NL80211_BAND_ATTR_HT_AMPDU_DENSITY: A-MPDU density, as in 11n + * @NL80211_BAND_ATTR_VHT_MCS_SET: 32-byte attribute containing the MCS set as + * defined in 802.11ac + * @NL80211_BAND_ATTR_VHT_CAPA: VHT capabilities, as in the HT information IE * @NL80211_BAND_ATTR_MAX: highest band attribute currently defined * @__NL80211_BAND_ATTR_AFTER_LAST: internal use */ @@ -1737,6 +2267,9 @@ NL80211_BAND_ATTR_HT_AMPDU_FACTOR, NL80211_BAND_ATTR_HT_AMPDU_DENSITY, + NL80211_BAND_ATTR_VHT_MCS_SET, + NL80211_BAND_ATTR_VHT_CAPA, + /* keep last */ __NL80211_BAND_ATTR_AFTER_LAST, NL80211_BAND_ATTR_MAX = __NL80211_BAND_ATTR_AFTER_LAST - 1 @@ -1750,14 +2283,27 @@ * @NL80211_FREQUENCY_ATTR_FREQ: Frequency in MHz * @NL80211_FREQUENCY_ATTR_DISABLED: Channel is disabled in current * regulatory domain. - * @NL80211_FREQUENCY_ATTR_PASSIVE_SCAN: Only passive scanning is - * permitted on this channel in current regulatory domain. - * @NL80211_FREQUENCY_ATTR_NO_IBSS: IBSS networks are not permitted - * on this channel in current regulatory domain. + * @NL80211_FREQUENCY_ATTR_NO_IR: no mechanisms that initiate radiation + * are permitted on this channel, this includes sending probe + * requests, or modes of operation that require beaconing. * @NL80211_FREQUENCY_ATTR_RADAR: Radar detection is mandatory * on this channel in current regulatory domain. * @NL80211_FREQUENCY_ATTR_MAX_TX_POWER: Maximum transmission power in mBm * (100 * dBm). + * @NL80211_FREQUENCY_ATTR_DFS_STATE: current state for DFS + * (enum nl80211_dfs_state) + * @NL80211_FREQUENCY_ATTR_DFS_TIME: time in miliseconds for how long + * this channel is in this DFS state. + * @NL80211_FREQUENCY_ATTR_NO_HT40_MINUS: HT40- isn't possible with this + * channel as the control channel + * @NL80211_FREQUENCY_ATTR_NO_HT40_PLUS: HT40+ isn't possible with this + * channel as the control channel + * @NL80211_FREQUENCY_ATTR_NO_80MHZ: any 80 MHz channel using this channel + * as the primary or any of the secondary channels isn't possible, + * this includes 80+80 channels + * @NL80211_FREQUENCY_ATTR_NO_160MHZ: any 160 MHz (but not 80+80) channel + * using this channel as the primary or any of the secondary channels + * isn't possible * @NL80211_FREQUENCY_ATTR_MAX: highest frequency attribute number * currently defined * @__NL80211_FREQUENCY_ATTR_AFTER_LAST: internal use @@ -1766,10 +2312,16 @@ __NL80211_FREQUENCY_ATTR_INVALID, NL80211_FREQUENCY_ATTR_FREQ, NL80211_FREQUENCY_ATTR_DISABLED, - NL80211_FREQUENCY_ATTR_PASSIVE_SCAN, - NL80211_FREQUENCY_ATTR_NO_IBSS, + NL80211_FREQUENCY_ATTR_NO_IR, + __NL80211_FREQUENCY_ATTR_NO_IBSS, NL80211_FREQUENCY_ATTR_RADAR, NL80211_FREQUENCY_ATTR_MAX_TX_POWER, + NL80211_FREQUENCY_ATTR_DFS_STATE, + NL80211_FREQUENCY_ATTR_DFS_TIME, + NL80211_FREQUENCY_ATTR_NO_HT40_MINUS, + NL80211_FREQUENCY_ATTR_NO_HT40_PLUS, + NL80211_FREQUENCY_ATTR_NO_80MHZ, + NL80211_FREQUENCY_ATTR_NO_160MHZ, /* keep last */ __NL80211_FREQUENCY_ATTR_AFTER_LAST, @@ -1777,6 +2329,9 @@ }; #define NL80211_FREQUENCY_ATTR_MAX_TX_POWER NL80211_FREQUENCY_ATTR_MAX_TX_POWER +#define NL80211_FREQUENCY_ATTR_PASSIVE_SCAN NL80211_FREQUENCY_ATTR_NO_IR +#define NL80211_FREQUENCY_ATTR_NO_IBSS NL80211_FREQUENCY_ATTR_NO_IR +#define NL80211_FREQUENCY_ATTR_NO_IR NL80211_FREQUENCY_ATTR_NO_IR /** * enum nl80211_bitrate_attr - bitrate attributes @@ -1888,6 +2443,8 @@ * @__NL80211_SCHED_SCAN_MATCH_ATTR_INVALID: attribute number 0 is reserved * @NL80211_SCHED_SCAN_MATCH_ATTR_SSID: SSID to be used for matching, * only report BSS with matching SSID. + * @NL80211_SCHED_SCAN_MATCH_ATTR_RSSI: RSSI threshold (in dBm) for reporting a + * BSS in scan results. Filtering is turned off if not specified. * @NL80211_SCHED_SCAN_MATCH_ATTR_MAX: highest scheduled scan filter * attribute number currently defined * @__NL80211_SCHED_SCAN_MATCH_ATTR_AFTER_LAST: internal use @@ -1895,7 +2452,8 @@ enum nl80211_sched_scan_match_attr { __NL80211_SCHED_SCAN_MATCH_ATTR_INVALID, - NL80211_ATTR_SCHED_SCAN_MATCH_SSID, + NL80211_SCHED_SCAN_MATCH_ATTR_SSID, + NL80211_SCHED_SCAN_MATCH_ATTR_RSSI, /* keep last */ __NL80211_SCHED_SCAN_MATCH_ATTR_AFTER_LAST, @@ -1903,6 +2461,9 @@ __NL80211_SCHED_SCAN_MATCH_ATTR_AFTER_LAST - 1 }; +/* only for backward compatibility */ +#define NL80211_ATTR_SCHED_SCAN_MATCH_SSID NL80211_SCHED_SCAN_MATCH_ATTR_SSID + /** * enum nl80211_reg_rule_flags - regulatory rule flags * @@ -1913,8 +2474,9 @@ * @NL80211_RRF_DFS: DFS support is required to be used * @NL80211_RRF_PTP_ONLY: this is only for Point To Point links * @NL80211_RRF_PTMP_ONLY: this is only for Point To Multi Point links - * @NL80211_RRF_PASSIVE_SCAN: passive scan is required - * @NL80211_RRF_NO_IBSS: no IBSS is allowed + * @NL80211_RRF_NO_IR: no mechanisms that initiate radiation are allowed, + * this includes probe requests or modes of operation that require + * beaconing. */ enum nl80211_reg_rule_flags { NL80211_RRF_NO_OFDM = 1<<0, @@ -1924,8 +2486,50 @@ NL80211_RRF_DFS = 1<<4, NL80211_RRF_PTP_ONLY = 1<<5, NL80211_RRF_PTMP_ONLY = 1<<6, - NL80211_RRF_PASSIVE_SCAN = 1<<7, - NL80211_RRF_NO_IBSS = 1<<8, + NL80211_RRF_NO_IR = 1<<7, + __NL80211_RRF_NO_IBSS = 1<<8, +}; + +#define NL80211_RRF_PASSIVE_SCAN NL80211_RRF_NO_IR +#define NL80211_RRF_NO_IBSS NL80211_RRF_NO_IR +#define NL80211_RRF_NO_IR NL80211_RRF_NO_IR + +/* For backport compatibility with older userspace */ +#define NL80211_RRF_NO_IR_ALL (NL80211_RRF_NO_IR | __NL80211_RRF_NO_IBSS) + +/** + * enum nl80211_dfs_regions - regulatory DFS regions + * + * @NL80211_DFS_UNSET: Country has no DFS master region specified + * @NL80211_DFS_FCC: Country follows DFS master rules from FCC + * @NL80211_DFS_ETSI: Country follows DFS master rules from ETSI + * @NL80211_DFS_JP: Country follows DFS master rules from JP/MKK/Telec + */ +enum nl80211_dfs_regions { + NL80211_DFS_UNSET = 0, + NL80211_DFS_FCC = 1, + NL80211_DFS_ETSI = 2, + NL80211_DFS_JP = 3, +}; + +/** + * enum nl80211_user_reg_hint_type - type of user regulatory hint + * + * @NL80211_USER_REG_HINT_USER: a user sent the hint. This is always + * assumed if the attribute is not set. + * @NL80211_USER_REG_HINT_CELL_BASE: the hint comes from a cellular + * base station. Device drivers that have been tested to work + * properly to support this type of hint can enable these hints + * by setting the NL80211_FEATURE_CELL_BASE_REG_HINTS feature + * capability on the struct wiphy. The wireless core will + * ignore all cell base station hints until at least one device + * present has been registered with the wireless core that + * has listed NL80211_FEATURE_CELL_BASE_REG_HINTS as a + * supported feature. + */ +enum nl80211_user_reg_hint_type { + NL80211_USER_REG_HINT_USER = 0, + NL80211_USER_REG_HINT_CELL_BASE = 1, }; /** @@ -1981,6 +2585,8 @@ * @NL80211_MNTR_FLAG_OTHER_BSS: disable BSSID filtering * @NL80211_MNTR_FLAG_COOK_FRAMES: report frames after processing. * overrides all other flags. + * @NL80211_MNTR_FLAG_ACTIVE: use the configured MAC address + * and ACK incoming unicast packets. * * @__NL80211_MNTR_FLAG_AFTER_LAST: internal use * @NL80211_MNTR_FLAG_MAX: highest possible monitor flag @@ -1992,6 +2598,7 @@ NL80211_MNTR_FLAG_CONTROL, NL80211_MNTR_FLAG_OTHER_BSS, NL80211_MNTR_FLAG_COOK_FRAMES, + NL80211_MNTR_FLAG_ACTIVE, /* keep last */ __NL80211_MNTR_FLAG_AFTER_LAST, @@ -1999,6 +2606,34 @@ }; /** + * enum nl80211_mesh_power_mode - mesh power save modes + * + * @NL80211_MESH_POWER_UNKNOWN: The mesh power mode of the mesh STA is + * not known or has not been set yet. + * @NL80211_MESH_POWER_ACTIVE: Active mesh power mode. The mesh STA is + * in Awake state all the time. + * @NL80211_MESH_POWER_LIGHT_SLEEP: Light sleep mode. The mesh STA will + * alternate between Active and Doze states, but will wake up for + * neighbor's beacons. + * @NL80211_MESH_POWER_DEEP_SLEEP: Deep sleep mode. The mesh STA will + * alternate between Active and Doze states, but may not wake up + * for neighbor's beacons. + * + * @__NL80211_MESH_POWER_AFTER_LAST - internal use + * @NL80211_MESH_POWER_MAX - highest possible power save level + */ + +enum nl80211_mesh_power_mode { + NL80211_MESH_POWER_UNKNOWN, + NL80211_MESH_POWER_ACTIVE, + NL80211_MESH_POWER_LIGHT_SLEEP, + NL80211_MESH_POWER_DEEP_SLEEP, + + __NL80211_MESH_POWER_AFTER_LAST, + NL80211_MESH_POWER_MAX = __NL80211_MESH_POWER_AFTER_LAST - 1 +}; + +/** * enum nl80211_meshconf_params - mesh configuration parameters * * Mesh configuration parameters. These can be changed while the mesh is @@ -2007,73 +2642,102 @@ * @__NL80211_MESHCONF_INVALID: internal use * * @NL80211_MESHCONF_RETRY_TIMEOUT: specifies the initial retry timeout in - * millisecond units, used by the Peer Link Open message + * millisecond units, used by the Peer Link Open message * * @NL80211_MESHCONF_CONFIRM_TIMEOUT: specifies the initial confirm timeout, in - * millisecond units, used by the peer link management to close a peer link + * millisecond units, used by the peer link management to close a peer link * * @NL80211_MESHCONF_HOLDING_TIMEOUT: specifies the holding timeout, in - * millisecond units + * millisecond units * * @NL80211_MESHCONF_MAX_PEER_LINKS: maximum number of peer links allowed - * on this mesh interface + * on this mesh interface * * @NL80211_MESHCONF_MAX_RETRIES: specifies the maximum number of peer link - * open retries that can be sent to establish a new peer link instance in a - * mesh + * open retries that can be sent to establish a new peer link instance in a + * mesh * * @NL80211_MESHCONF_TTL: specifies the value of TTL field set at a source mesh - * point. + * point. * - * @NL80211_MESHCONF_AUTO_OPEN_PLINKS: whether we should automatically - * open peer links when we detect compatible mesh peers. + * @NL80211_MESHCONF_AUTO_OPEN_PLINKS: whether we should automatically open + * peer links when we detect compatible mesh peers. Disabled if + * @NL80211_MESH_SETUP_USERSPACE_MPM or @NL80211_MESH_SETUP_USERSPACE_AMPE are + * set. * * @NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES: the number of action frames - * containing a PREQ that an MP can send to a particular destination (path - * target) + * containing a PREQ that an MP can send to a particular destination (path + * target) * * @NL80211_MESHCONF_PATH_REFRESH_TIME: how frequently to refresh mesh paths - * (in milliseconds) + * (in milliseconds) * * @NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT: minimum length of time to wait - * until giving up on a path discovery (in milliseconds) + * until giving up on a path discovery (in milliseconds) * * @NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT: The time (in TUs) for which mesh - * points receiving a PREQ shall consider the forwarding information from the - * root to be valid. (TU = time unit) + * points receiving a PREQ shall consider the forwarding information from + * the root to be valid. (TU = time unit) * * @NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL: The minimum interval of time (in - * TUs) during which an MP can send only one action frame containing a PREQ - * reference element + * TUs) during which an MP can send only one action frame containing a PREQ + * reference element * * @NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME: The interval of time (in TUs) - * that it takes for an HWMP information element to propagate across the mesh + * that it takes for an HWMP information element to propagate across the + * mesh * * @NL80211_MESHCONF_HWMP_ROOTMODE: whether root mode is enabled or not * * @NL80211_MESHCONF_ELEMENT_TTL: specifies the value of TTL field set at a - * source mesh point for path selection elements. + * source mesh point for path selection elements. * * @NL80211_MESHCONF_HWMP_RANN_INTERVAL: The interval of time (in TUs) between - * root announcements are transmitted. + * root announcements are transmitted. * * @NL80211_MESHCONF_GATE_ANNOUNCEMENTS: Advertise that this mesh station has - * access to a broader network beyond the MBSS. This is done via Root - * Announcement frames. + * access to a broader network beyond the MBSS. This is done via Root + * Announcement frames. * * @NL80211_MESHCONF_HWMP_PERR_MIN_INTERVAL: The minimum interval of time (in - * TUs) during which a mesh STA can send only one Action frame containing a - * PERR element. + * TUs) during which a mesh STA can send only one Action frame containing a + * PERR element. * * @NL80211_MESHCONF_FORWARDING: set Mesh STA as forwarding or non-forwarding - * or forwarding entity (default is TRUE - forwarding entity) + * or forwarding entity (default is TRUE - forwarding entity) * * @NL80211_MESHCONF_RSSI_THRESHOLD: RSSI threshold in dBm. This specifies the - * threshold for average signal strength of candidate station to establish - * a peer link. + * threshold for average signal strength of candidate station to establish + * a peer link. + * + * @NL80211_MESHCONF_SYNC_OFFSET_MAX_NEIGHBOR: maximum number of neighbors + * to synchronize to for 11s default synchronization method + * (see 11C.12.2.2) + * + * @NL80211_MESHCONF_HT_OPMODE: set mesh HT protection mode. * * @NL80211_MESHCONF_ATTR_MAX: highest possible mesh configuration attribute * + * @NL80211_MESHCONF_HWMP_PATH_TO_ROOT_TIMEOUT: The time (in TUs) for + * which mesh STAs receiving a proactive PREQ shall consider the forwarding + * information to the root mesh STA to be valid. + * + * @NL80211_MESHCONF_HWMP_ROOT_INTERVAL: The interval of time (in TUs) between + * proactive PREQs are transmitted. + * + * @NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL: The minimum interval of time + * (in TUs) during which a mesh STA can send only one Action frame + * containing a PREQ element for root path confirmation. + * + * @NL80211_MESHCONF_POWER_MODE: Default mesh power mode for new peer links. + * type &enum nl80211_mesh_power_mode (u32) + * + * @NL80211_MESHCONF_AWAKE_WINDOW: awake window duration (in TUs) + * + * @NL80211_MESHCONF_PLINK_TIMEOUT: If no tx activity is seen from a STA we've + * established peering with for longer than this time (in seconds), then + * remove it from the STA's list of peers. Default is 30 minutes. + * * @__NL80211_MESHCONF_ATTR_AFTER_LAST: internal use */ enum nl80211_meshconf_params { @@ -2098,6 +2762,14 @@ NL80211_MESHCONF_HWMP_PERR_MIN_INTERVAL, NL80211_MESHCONF_FORWARDING, NL80211_MESHCONF_RSSI_THRESHOLD, + NL80211_MESHCONF_SYNC_OFFSET_MAX_NEIGHBOR, + NL80211_MESHCONF_HT_OPMODE, + NL80211_MESHCONF_HWMP_PATH_TO_ROOT_TIMEOUT, + NL80211_MESHCONF_HWMP_ROOT_INTERVAL, + NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL, + NL80211_MESHCONF_POWER_MODE, + NL80211_MESHCONF_AWAKE_WINDOW, + NL80211_MESHCONF_PLINK_TIMEOUT, /* keep last */ __NL80211_MESHCONF_ATTR_AFTER_LAST, @@ -2113,30 +2785,44 @@ * @__NL80211_MESH_SETUP_INVALID: Internal use * * @NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL: Enable this option to use a - * vendor specific path selection algorithm or disable it to use the default - * HWMP. + * vendor specific path selection algorithm or disable it to use the + * default HWMP. * * @NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC: Enable this option to use a - * vendor specific path metric or disable it to use the default Airtime - * metric. + * vendor specific path metric or disable it to use the default Airtime + * metric. * * @NL80211_MESH_SETUP_IE: Information elements for this mesh, for instance, a - * robust security network ie, or a vendor specific information element that - * vendors will use to identify the path selection methods and metrics in use. + * robust security network ie, or a vendor specific information element + * that vendors will use to identify the path selection methods and + * metrics in use. * * @NL80211_MESH_SETUP_USERSPACE_AUTH: Enable this option if an authentication - * daemon will be authenticating mesh candidates. + * daemon will be authenticating mesh candidates. * * @NL80211_MESH_SETUP_USERSPACE_AMPE: Enable this option if an authentication - * daemon will be securing peer link frames. AMPE is a secured version of Mesh - * Peering Management (MPM) and is implemented with the assistance of a - * userspace daemon. When this flag is set, the kernel will send peer - * management frames to a userspace daemon that will implement AMPE - * functionality (security capabilities selection, key confirmation, and key - * management). When the flag is unset (default), the kernel can autonomously - * complete (unsecured) mesh peering without the need of a userspace daemon. + * daemon will be securing peer link frames. AMPE is a secured version of + * Mesh Peering Management (MPM) and is implemented with the assistance of + * a userspace daemon. When this flag is set, the kernel will send peer + * management frames to a userspace daemon that will implement AMPE + * functionality (security capabilities selection, key confirmation, and + * key management). When the flag is unset (default), the kernel can + * autonomously complete (unsecured) mesh peering without the need of a + * userspace daemon. + * + * @NL80211_MESH_SETUP_ENABLE_VENDOR_SYNC: Enable this option to use a + * vendor specific synchronization method or disable it to use the default + * neighbor offset synchronization + * + * @NL80211_MESH_SETUP_USERSPACE_MPM: Enable this option if userspace will + * implement an MPM which handles peer allocation and state. + * + * @NL80211_MESH_SETUP_AUTH_PROTOCOL: Inform the kernel of the authentication + * method (u8, as defined in IEEE 8.4.2.100.6, e.g. 0x1 for SAE). + * Default is no authentication method required. * * @NL80211_MESH_SETUP_ATTR_MAX: highest possible mesh setup attribute number + * * @__NL80211_MESH_SETUP_ATTR_AFTER_LAST: Internal use */ enum nl80211_mesh_setup_params { @@ -2146,6 +2832,9 @@ NL80211_MESH_SETUP_IE, NL80211_MESH_SETUP_USERSPACE_AUTH, NL80211_MESH_SETUP_USERSPACE_AMPE, + NL80211_MESH_SETUP_ENABLE_VENDOR_SYNC, + NL80211_MESH_SETUP_USERSPACE_MPM, + NL80211_MESH_SETUP_AUTH_PROTOCOL, /* keep last */ __NL80211_MESH_SETUP_ATTR_AFTER_LAST, @@ -2155,7 +2844,7 @@ /** * enum nl80211_txq_attr - TX queue parameter attributes * @__NL80211_TXQ_ATTR_INVALID: Attribute number 0 is reserved - * @NL80211_TXQ_ATTR_QUEUE: TX queue identifier (NL80211_TXQ_Q_*) + * @NL80211_TXQ_ATTR_AC: AC identifier (NL80211_AC_*) * @NL80211_TXQ_ATTR_TXOP: Maximum burst time in units of 32 usecs, 0 meaning * disabled * @NL80211_TXQ_ATTR_CWMIN: Minimum contention window [a value of the form @@ -2168,7 +2857,7 @@ */ enum nl80211_txq_attr { __NL80211_TXQ_ATTR_INVALID, - NL80211_TXQ_ATTR_QUEUE, + NL80211_TXQ_ATTR_AC, NL80211_TXQ_ATTR_TXOP, NL80211_TXQ_ATTR_CWMIN, NL80211_TXQ_ATTR_CWMAX, @@ -2179,13 +2868,30 @@ NL80211_TXQ_ATTR_MAX = __NL80211_TXQ_ATTR_AFTER_LAST - 1 }; -enum nl80211_txq_q { - NL80211_TXQ_Q_VO, - NL80211_TXQ_Q_VI, - NL80211_TXQ_Q_BE, - NL80211_TXQ_Q_BK -}; - +enum nl80211_ac { + NL80211_AC_VO, + NL80211_AC_VI, + NL80211_AC_BE, + NL80211_AC_BK, + NL80211_NUM_ACS +}; + +/* backward compat */ +#define NL80211_TXQ_ATTR_QUEUE NL80211_TXQ_ATTR_AC +#define NL80211_TXQ_Q_VO NL80211_AC_VO +#define NL80211_TXQ_Q_VI NL80211_AC_VI +#define NL80211_TXQ_Q_BE NL80211_AC_BE +#define NL80211_TXQ_Q_BK NL80211_AC_BK + +/** + * enum nl80211_channel_type - channel type + * @NL80211_CHAN_NO_HT: 20 MHz, non-HT channel + * @NL80211_CHAN_HT20: 20 MHz HT channel + * @NL80211_CHAN_HT40MINUS: HT40 channel, secondary channel + * below the control channel + * @NL80211_CHAN_HT40PLUS: HT40 channel, secondary channel + * above the control channel + */ enum nl80211_channel_type { NL80211_CHAN_NO_HT, NL80211_CHAN_HT20, @@ -2194,6 +2900,51 @@ }; /** + * enum nl80211_chan_width - channel width definitions + * + * These values are used with the %NL80211_ATTR_CHANNEL_WIDTH + * attribute. + * + * @NL80211_CHAN_WIDTH_20_NOHT: 20 MHz, non-HT channel + * @NL80211_CHAN_WIDTH_20: 20 MHz HT channel + * @NL80211_CHAN_WIDTH_40: 40 MHz channel, the %NL80211_ATTR_CENTER_FREQ1 + * attribute must be provided as well + * @NL80211_CHAN_WIDTH_80: 80 MHz channel, the %NL80211_ATTR_CENTER_FREQ1 + * attribute must be provided as well + * @NL80211_CHAN_WIDTH_80P80: 80+80 MHz channel, the %NL80211_ATTR_CENTER_FREQ1 + * and %NL80211_ATTR_CENTER_FREQ2 attributes must be provided as well + * @NL80211_CHAN_WIDTH_160: 160 MHz channel, the %NL80211_ATTR_CENTER_FREQ1 + * attribute must be provided as well + * @NL80211_CHAN_WIDTH_5: 5 MHz OFDM channel + * @NL80211_CHAN_WIDTH_10: 10 MHz OFDM channel + */ +enum nl80211_chan_width { + NL80211_CHAN_WIDTH_20_NOHT, + NL80211_CHAN_WIDTH_20, + NL80211_CHAN_WIDTH_40, + NL80211_CHAN_WIDTH_80, + NL80211_CHAN_WIDTH_80P80, + NL80211_CHAN_WIDTH_160, + NL80211_CHAN_WIDTH_5, + NL80211_CHAN_WIDTH_10, +}; + +/** + * enum nl80211_bss_scan_width - control channel width for a BSS + * + * These values are used with the %NL80211_BSS_CHAN_WIDTH attribute. + * + * @NL80211_BSS_CHAN_WIDTH_20: control channel is 20 MHz wide or compatible + * @NL80211_BSS_CHAN_WIDTH_10: control channel is 10 MHz wide + * @NL80211_BSS_CHAN_WIDTH_5: control channel is 5 MHz wide + */ +enum nl80211_bss_scan_width { + NL80211_BSS_CHAN_WIDTH_20, + NL80211_BSS_CHAN_WIDTH_10, + NL80211_BSS_CHAN_WIDTH_5, +}; + +/** * enum nl80211_bss - netlink attributes for a BSS * * @__NL80211_BSS_INVALID: invalid @@ -2217,6 +2968,8 @@ * @NL80211_BSS_BEACON_IES: binary attribute containing the raw information * elements from a Beacon frame (bin); not present if no Beacon frame has * yet been received + * @NL80211_BSS_CHAN_WIDTH: channel width of the control channel + * (u32, enum nl80211_bss_scan_width) * @__NL80211_BSS_AFTER_LAST: internal * @NL80211_BSS_MAX: highest BSS attribute */ @@ -2233,6 +2986,7 @@ NL80211_BSS_STATUS, NL80211_BSS_SEEN_MS_AGO, NL80211_BSS_BEACON_IES, + NL80211_BSS_CHAN_WIDTH, /* keep last */ __NL80211_BSS_AFTER_LAST, @@ -2261,6 +3015,7 @@ * @NL80211_AUTHTYPE_SHARED_KEY: Shared Key authentication (WEP only) * @NL80211_AUTHTYPE_FT: Fast BSS Transition (IEEE 802.11r) * @NL80211_AUTHTYPE_NETWORK_EAP: Network EAP (some Cisco APs and mainly LEAP) + * @NL80211_AUTHTYPE_SAE: Simultaneous authentication of equals * @__NL80211_AUTHTYPE_NUM: internal * @NL80211_AUTHTYPE_MAX: maximum valid auth algorithm * @NL80211_AUTHTYPE_AUTOMATIC: determine automatically (if necessary by @@ -2272,6 +3027,7 @@ NL80211_AUTHTYPE_SHARED_KEY, NL80211_AUTHTYPE_FT, NL80211_AUTHTYPE_NETWORK_EAP, + NL80211_AUTHTYPE_SAE, /* keep last */ __NL80211_AUTHTYPE_NUM, @@ -2371,31 +3127,52 @@ * in an array of rates as defined in IEEE 802.11 7.3.2.2 (u8 values with * 1 = 500 kbps) but without the IE length restriction (at most * %NL80211_MAX_SUPP_RATES in a single array). - * @NL80211_TXRATE_MCS: HT (MCS) rates allowed for TX rate selection + * @NL80211_TXRATE_HT: HT (MCS) rates allowed for TX rate selection * in an array of MCS numbers. + * @NL80211_TXRATE_VHT: VHT rates allowed for TX rate selection, + * see &struct nl80211_txrate_vht * @__NL80211_TXRATE_AFTER_LAST: internal * @NL80211_TXRATE_MAX: highest TX rate attribute */ enum nl80211_tx_rate_attributes { __NL80211_TXRATE_INVALID, NL80211_TXRATE_LEGACY, - NL80211_TXRATE_MCS, + NL80211_TXRATE_HT, + NL80211_TXRATE_VHT, /* keep last */ __NL80211_TXRATE_AFTER_LAST, NL80211_TXRATE_MAX = __NL80211_TXRATE_AFTER_LAST - 1 }; +#define NL80211_TXRATE_MCS NL80211_TXRATE_HT +#define NL80211_VHT_NSS_MAX 8 + +/** + * struct nl80211_txrate_vht - VHT MCS/NSS txrate bitmap + * @mcs: MCS bitmap table for each NSS (array index 0 for 1 stream, etc.) + */ +struct nl80211_txrate_vht { + __u16 mcs[NL80211_VHT_NSS_MAX]; +}; + /** * enum nl80211_band - Frequency band * @NL80211_BAND_2GHZ: 2.4 GHz ISM band * @NL80211_BAND_5GHZ: around 5 GHz band (4.9 - 5.7 GHz) + * @NL80211_BAND_60GHZ: around 60 GHz band (58.32 - 64.80 GHz) */ enum nl80211_band { NL80211_BAND_2GHZ, NL80211_BAND_5GHZ, + NL80211_BAND_60GHZ, }; +/** + * enum nl80211_ps_state - powersave state + * @NL80211_PS_DISABLED: powersave is disabled + * @NL80211_PS_ENABLED: powersave is enabled + */ enum nl80211_ps_state { NL80211_PS_DISABLED, NL80211_PS_ENABLED, @@ -2413,6 +3190,17 @@ * @NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT: RSSI threshold event * @NL80211_ATTR_CQM_PKT_LOSS_EVENT: a u32 value indicating that this many * consecutive packets were not acknowledged by the peer + * @NL80211_ATTR_CQM_TXE_RATE: TX error rate in %. Minimum % of TX failures + * during the given %NL80211_ATTR_CQM_TXE_INTVL before an + * %NL80211_CMD_NOTIFY_CQM with reported %NL80211_ATTR_CQM_TXE_RATE and + * %NL80211_ATTR_CQM_TXE_PKTS is generated. + * @NL80211_ATTR_CQM_TXE_PKTS: number of attempted packets in a given + * %NL80211_ATTR_CQM_TXE_INTVL before %NL80211_ATTR_CQM_TXE_RATE is + * checked. + * @NL80211_ATTR_CQM_TXE_INTVL: interval in seconds. Specifies the periodic + * interval in which %NL80211_ATTR_CQM_TXE_PKTS and + * %NL80211_ATTR_CQM_TXE_RATE must be satisfied before generating an + * %NL80211_CMD_NOTIFY_CQM. Set to 0 to turn off TX error reporting. * @__NL80211_ATTR_CQM_AFTER_LAST: internal * @NL80211_ATTR_CQM_MAX: highest key attribute */ @@ -2422,6 +3210,9 @@ NL80211_ATTR_CQM_RSSI_HYST, NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT, NL80211_ATTR_CQM_PKT_LOSS_EVENT, + NL80211_ATTR_CQM_TXE_RATE, + NL80211_ATTR_CQM_TXE_PKTS, + NL80211_ATTR_CQM_TXE_INTVL, /* keep last */ __NL80211_ATTR_CQM_AFTER_LAST, @@ -2434,10 +3225,14 @@ * configured threshold * @NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH: The RSSI is higher than the * configured threshold + * @NL80211_CQM_RSSI_BEACON_LOSS_EVENT: The device experienced beacon loss. + * (Note that deauth/disassoc will still follow if the AP is not + * available. This event might get used as roaming event, etc.) */ enum nl80211_cqm_rssi_threshold_event { NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW, NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH, + NL80211_CQM_RSSI_BEACON_LOSS_EVENT, }; @@ -2454,49 +3249,65 @@ }; /** - * enum nl80211_wowlan_packet_pattern_attr - WoWLAN packet pattern attribute - * @__NL80211_WOWLAN_PKTPAT_INVALID: invalid number for nested attribute - * @NL80211_WOWLAN_PKTPAT_PATTERN: the pattern, values where the mask has + * enum nl80211_packet_pattern_attr - packet pattern attribute + * @__NL80211_PKTPAT_INVALID: invalid number for nested attribute + * @NL80211_PKTPAT_PATTERN: the pattern, values where the mask has * a zero bit are ignored - * @NL80211_WOWLAN_PKTPAT_MASK: pattern mask, must be long enough to have + * @NL80211_PKTPAT_MASK: pattern mask, must be long enough to have * a bit for each byte in the pattern. The lowest-order bit corresponds * to the first byte of the pattern, but the bytes of the pattern are * in a little-endian-like format, i.e. the 9th byte of the pattern * corresponds to the lowest-order bit in the second byte of the mask. * For example: The match 00:xx:00:00:xx:00:00:00:00:xx:xx:xx (where * xx indicates "don't care") would be represented by a pattern of - * twelve zero bytes, and a mask of "0xed,0x07". + * twelve zero bytes, and a mask of "0xed,0x01". * Note that the pattern matching is done as though frames were not * 802.11 frames but 802.3 frames, i.e. the frame is fully unpacked * first (including SNAP header unpacking) and then matched. - * @NUM_NL80211_WOWLAN_PKTPAT: number of attributes - * @MAX_NL80211_WOWLAN_PKTPAT: max attribute number + * @NL80211_PKTPAT_OFFSET: packet offset, pattern is matched after + * these fixed number of bytes of received packet + * @NUM_NL80211_PKTPAT: number of attributes + * @MAX_NL80211_PKTPAT: max attribute number */ -enum nl80211_wowlan_packet_pattern_attr { - __NL80211_WOWLAN_PKTPAT_INVALID, - NL80211_WOWLAN_PKTPAT_MASK, - NL80211_WOWLAN_PKTPAT_PATTERN, +enum nl80211_packet_pattern_attr { + __NL80211_PKTPAT_INVALID, + NL80211_PKTPAT_MASK, + NL80211_PKTPAT_PATTERN, + NL80211_PKTPAT_OFFSET, - NUM_NL80211_WOWLAN_PKTPAT, - MAX_NL80211_WOWLAN_PKTPAT = NUM_NL80211_WOWLAN_PKTPAT - 1, + NUM_NL80211_PKTPAT, + MAX_NL80211_PKTPAT = NUM_NL80211_PKTPAT - 1, }; /** - * struct nl80211_wowlan_pattern_support - pattern support information + * struct nl80211_pattern_support - packet pattern support information * @max_patterns: maximum number of patterns supported * @min_pattern_len: minimum length of each pattern * @max_pattern_len: maximum length of each pattern + * @max_pkt_offset: maximum Rx packet offset * * This struct is carried in %NL80211_WOWLAN_TRIG_PKT_PATTERN when - * that is part of %NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED in the - * capability information given by the kernel to userspace. + * that is part of %NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED or in + * %NL80211_ATTR_COALESCE_RULE_PKT_PATTERN when that is part of + * %NL80211_ATTR_COALESCE_RULE in the capability information given + * by the kernel to userspace. */ -struct nl80211_wowlan_pattern_support { +struct nl80211_pattern_support { __u32 max_patterns; __u32 min_pattern_len; __u32 max_pattern_len; + __u32 max_pkt_offset; } __attribute__((packed)); +/* only for backward compatibility */ +#define __NL80211_WOWLAN_PKTPAT_INVALID __NL80211_PKTPAT_INVALID +#define NL80211_WOWLAN_PKTPAT_MASK NL80211_PKTPAT_MASK +#define NL80211_WOWLAN_PKTPAT_PATTERN NL80211_PKTPAT_PATTERN +#define NL80211_WOWLAN_PKTPAT_OFFSET NL80211_PKTPAT_OFFSET +#define NUM_NL80211_WOWLAN_PKTPAT NUM_NL80211_PKTPAT +#define MAX_NL80211_WOWLAN_PKTPAT MAX_NL80211_PKTPAT +#define nl80211_wowlan_pattern_support nl80211_pattern_support + /** * enum nl80211_wowlan_triggers - WoWLAN trigger definitions * @__NL80211_WOWLAN_TRIG_INVALID: invalid number for nested attributes @@ -2510,12 +3321,17 @@ * @NL80211_WOWLAN_TRIG_PKT_PATTERN: wake up on the specified packet patterns * which are passed in an array of nested attributes, each nested attribute * defining a with attributes from &struct nl80211_wowlan_trig_pkt_pattern. - * Each pattern defines a wakeup packet. The matching is done on the MSDU, - * i.e. as though the packet was an 802.3 packet, so the pattern matching - * is done after the packet is converted to the MSDU. + * Each pattern defines a wakeup packet. Packet offset is associated with + * each pattern which is used while matching the pattern. The matching is + * done on the MSDU, i.e. as though the packet was an 802.3 packet, so the + * pattern matching is done after the packet is converted to the MSDU. * * In %NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED, it is a binary attribute - * carrying a &struct nl80211_wowlan_pattern_support. + * carrying a &struct nl80211_pattern_support. + * + * When reporting wakeup. it is a u32 attribute containing the 0-based + * index of the pattern that caused the wakeup, in the patterns passed + * to the kernel when configuring. * @NL80211_WOWLAN_TRIG_GTK_REKEY_SUPPORTED: Not a real trigger, and cannot be * used when setting, used only to indicate that GTK rekeying is supported * by the device (flag) @@ -2526,8 +3342,36 @@ * @NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE: wake up on 4-way handshake (flag) * @NL80211_WOWLAN_TRIG_RFKILL_RELEASE: wake up when rfkill is released * (on devices that have rfkill in the device) (flag) + * @NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211: For wakeup reporting only, contains + * the 802.11 packet that caused the wakeup, e.g. a deauth frame. The frame + * may be truncated, the @NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211_LEN + * attribute contains the original length. + * @NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211_LEN: Original length of the 802.11 + * packet, may be bigger than the @NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211 + * attribute if the packet was truncated somewhere. + * @NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023: For wakeup reporting only, contains the + * 802.11 packet that caused the wakeup, e.g. a magic packet. The frame may + * be truncated, the @NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023_LEN attribute + * contains the original length. + * @NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023_LEN: Original length of the 802.3 + * packet, may be bigger than the @NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023 + * attribute if the packet was truncated somewhere. + * @NL80211_WOWLAN_TRIG_TCP_CONNECTION: TCP connection wake, see DOC section + * "TCP connection wakeup" for more details. This is a nested attribute + * containing the exact information for establishing and keeping alive + * the TCP connection. + * @NL80211_WOWLAN_TRIG_TCP_WAKEUP_MATCH: For wakeup reporting only, the + * wakeup packet was received on the TCP connection + * @NL80211_WOWLAN_TRIG_WAKEUP_TCP_CONNLOST: For wakeup reporting only, the + * TCP connection was lost or failed to be established + * @NL80211_WOWLAN_TRIG_WAKEUP_TCP_NOMORETOKENS: For wakeup reporting only, + * the TCP connection ran out of tokens to use for data to send to the + * service * @NUM_NL80211_WOWLAN_TRIG: number of wake on wireless triggers * @MAX_NL80211_WOWLAN_TRIG: highest wowlan trigger attribute number + * + * These nested attributes are used to configure the wakeup triggers and + * to report the wakeup reason(s). */ enum nl80211_wowlan_triggers { __NL80211_WOWLAN_TRIG_INVALID, @@ -2540,6 +3384,14 @@ NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST, NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE, NL80211_WOWLAN_TRIG_RFKILL_RELEASE, + NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211, + NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211_LEN, + NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023, + NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023_LEN, + NL80211_WOWLAN_TRIG_TCP_CONNECTION, + NL80211_WOWLAN_TRIG_WAKEUP_TCP_MATCH, + NL80211_WOWLAN_TRIG_WAKEUP_TCP_CONNLOST, + NL80211_WOWLAN_TRIG_WAKEUP_TCP_NOMORETOKENS, /* keep last */ NUM_NL80211_WOWLAN_TRIG, @@ -2547,6 +3399,165 @@ }; /** + * DOC: TCP connection wakeup + * + * Some devices can establish a TCP connection in order to be woken up by a + * packet coming in from outside their network segment, or behind NAT. If + * configured, the device will establish a TCP connection to the given + * service, and periodically send data to that service. The first data + * packet is usually transmitted after SYN/ACK, also ACKing the SYN/ACK. + * The data packets can optionally include a (little endian) sequence + * number (in the TCP payload!) that is generated by the device, and, also + * optionally, a token from a list of tokens. This serves as a keep-alive + * with the service, and for NATed connections, etc. + * + * During this keep-alive period, the server doesn't send any data to the + * client. When receiving data, it is compared against the wakeup pattern + * (and mask) and if it matches, the host is woken up. Similarly, if the + * connection breaks or cannot be established to start with, the host is + * also woken up. + * + * Developer's note: ARP offload is required for this, otherwise TCP + * response packets might not go through correctly. + */ + +/** + * struct nl80211_wowlan_tcp_data_seq - WoWLAN TCP data sequence + * @start: starting value + * @offset: offset of sequence number in packet + * @len: length of the sequence value to write, 1 through 4 + * + * Note: don't confuse with the TCP sequence number(s), this is for the + * keepalive packet payload. The actual value is written into the packet + * in little endian. + */ +struct nl80211_wowlan_tcp_data_seq { + __u32 start, offset, len; +}; + +/** + * struct nl80211_wowlan_tcp_data_token - WoWLAN TCP data token config + * @offset: offset of token in packet + * @len: length of each token + * @token_stream: stream of data to be used for the tokens, the length must + * be a multiple of @len for this to make sense + */ +struct nl80211_wowlan_tcp_data_token { + __u32 offset, len; + __u8 token_stream[]; +}; + +/** + * struct nl80211_wowlan_tcp_data_token_feature - data token features + * @min_len: minimum token length + * @max_len: maximum token length + * @bufsize: total available token buffer size (max size of @token_stream) + */ +struct nl80211_wowlan_tcp_data_token_feature { + __u32 min_len, max_len, bufsize; +}; + +/** + * enum nl80211_wowlan_tcp_attrs - WoWLAN TCP connection parameters + * @__NL80211_WOWLAN_TCP_INVALID: invalid number for nested attributes + * @NL80211_WOWLAN_TCP_SRC_IPV4: source IPv4 address (in network byte order) + * @NL80211_WOWLAN_TCP_DST_IPV4: destination IPv4 address + * (in network byte order) + * @NL80211_WOWLAN_TCP_DST_MAC: destination MAC address, this is given because + * route lookup when configured might be invalid by the time we suspend, + * and doing a route lookup when suspending is no longer possible as it + * might require ARP querying. + * @NL80211_WOWLAN_TCP_SRC_PORT: source port (u16); optional, if not given a + * socket and port will be allocated + * @NL80211_WOWLAN_TCP_DST_PORT: destination port (u16) + * @NL80211_WOWLAN_TCP_DATA_PAYLOAD: data packet payload, at least one byte. + * For feature advertising, a u32 attribute holding the maximum length + * of the data payload. + * @NL80211_WOWLAN_TCP_DATA_PAYLOAD_SEQ: data packet sequence configuration + * (if desired), a &struct nl80211_wowlan_tcp_data_seq. For feature + * advertising it is just a flag + * @NL80211_WOWLAN_TCP_DATA_PAYLOAD_TOKEN: data packet token configuration, + * see &struct nl80211_wowlan_tcp_data_token and for advertising see + * &struct nl80211_wowlan_tcp_data_token_feature. + * @NL80211_WOWLAN_TCP_DATA_INTERVAL: data interval in seconds, maximum + * interval in feature advertising (u32) + * @NL80211_WOWLAN_TCP_WAKE_PAYLOAD: wake packet payload, for advertising a + * u32 attribute holding the maximum length + * @NL80211_WOWLAN_TCP_WAKE_MASK: Wake packet payload mask, not used for + * feature advertising. The mask works like @NL80211_PKTPAT_MASK + * but on the TCP payload only. + * @NUM_NL80211_WOWLAN_TCP: number of TCP attributes + * @MAX_NL80211_WOWLAN_TCP: highest attribute number + */ +enum nl80211_wowlan_tcp_attrs { + __NL80211_WOWLAN_TCP_INVALID, + NL80211_WOWLAN_TCP_SRC_IPV4, + NL80211_WOWLAN_TCP_DST_IPV4, + NL80211_WOWLAN_TCP_DST_MAC, + NL80211_WOWLAN_TCP_SRC_PORT, + NL80211_WOWLAN_TCP_DST_PORT, + NL80211_WOWLAN_TCP_DATA_PAYLOAD, + NL80211_WOWLAN_TCP_DATA_PAYLOAD_SEQ, + NL80211_WOWLAN_TCP_DATA_PAYLOAD_TOKEN, + NL80211_WOWLAN_TCP_DATA_INTERVAL, + NL80211_WOWLAN_TCP_WAKE_PAYLOAD, + NL80211_WOWLAN_TCP_WAKE_MASK, + + /* keep last */ + NUM_NL80211_WOWLAN_TCP, + MAX_NL80211_WOWLAN_TCP = NUM_NL80211_WOWLAN_TCP - 1 +}; + +/** + * struct nl80211_coalesce_rule_support - coalesce rule support information + * @max_rules: maximum number of rules supported + * @pat: packet pattern support information + * @max_delay: maximum supported coalescing delay in msecs + * + * This struct is carried in %NL80211_ATTR_COALESCE_RULE in the + * capability information given by the kernel to userspace. + */ +struct nl80211_coalesce_rule_support { + __u32 max_rules; + struct nl80211_pattern_support pat; + __u32 max_delay; +} __attribute__((packed)); + +/** + * enum nl80211_attr_coalesce_rule - coalesce rule attribute + * @__NL80211_COALESCE_RULE_INVALID: invalid number for nested attribute + * @NL80211_ATTR_COALESCE_RULE_DELAY: delay in msecs used for packet coalescing + * @NL80211_ATTR_COALESCE_RULE_CONDITION: condition for packet coalescence, + * see &enum nl80211_coalesce_condition. + * @NL80211_ATTR_COALESCE_RULE_PKT_PATTERN: packet offset, pattern is matched + * after these fixed number of bytes of received packet + * @NUM_NL80211_ATTR_COALESCE_RULE: number of attributes + * @NL80211_ATTR_COALESCE_RULE_MAX: max attribute number + */ +enum nl80211_attr_coalesce_rule { + __NL80211_COALESCE_RULE_INVALID, + NL80211_ATTR_COALESCE_RULE_DELAY, + NL80211_ATTR_COALESCE_RULE_CONDITION, + NL80211_ATTR_COALESCE_RULE_PKT_PATTERN, + + /* keep last */ + NUM_NL80211_ATTR_COALESCE_RULE, + NL80211_ATTR_COALESCE_RULE_MAX = NUM_NL80211_ATTR_COALESCE_RULE - 1 +}; + +/** + * enum nl80211_coalesce_condition - coalesce rule conditions + * @NL80211_COALESCE_CONDITION_MATCH: coalaesce Rx packets when patterns + * in a rule are matched. + * @NL80211_COALESCE_CONDITION_NO_MATCH: coalesce Rx packets when patterns + * in a rule are not matched. + */ +enum nl80211_coalesce_condition { + NL80211_COALESCE_CONDITION_MATCH, + NL80211_COALESCE_CONDITION_NO_MATCH +}; + +/** * enum nl80211_iface_limit_attrs - limit attributes * @NL80211_IFACE_LIMIT_UNSPEC: (reserved) * @NL80211_IFACE_LIMIT_MAX: maximum number of interfaces that @@ -2582,6 +3593,8 @@ * the infrastructure network's beacon interval. * @NL80211_IFACE_COMB_NUM_CHANNELS: u32 attribute specifying how many * different channels may be used within this group. + * @NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS: u32 attribute containing the bitmap + * of supported channel widths for radar detection. * @NUM_NL80211_IFACE_COMB: number of attributes * @MAX_NL80211_IFACE_COMB: highest attribute number * @@ -2614,6 +3627,7 @@ NL80211_IFACE_COMB_MAXNUM, NL80211_IFACE_COMB_STA_AP_BI_MATCH, NL80211_IFACE_COMB_NUM_CHANNELS, + NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS, /* keep last */ NUM_NL80211_IFACE_COMB, @@ -2653,6 +3667,23 @@ MAX_NL80211_PLINK_STATES = NUM_NL80211_PLINK_STATES - 1 }; +/** + * enum nl80211_plink_action - actions to perform in mesh peers + * + * @NL80211_PLINK_ACTION_NO_ACTION: perform no action + * @NL80211_PLINK_ACTION_OPEN: start mesh peer link establishment + * @NL80211_PLINK_ACTION_BLOCK: block traffic from this mesh peer + * @NUM_NL80211_PLINK_ACTIONS: number of possible actions + */ +enum plink_actions { + NL80211_PLINK_ACTION_NO_ACTION, + NL80211_PLINK_ACTION_OPEN, + NL80211_PLINK_ACTION_BLOCK, + + NUM_NL80211_PLINK_ACTIONS, +}; + + #define NL80211_KCK_LEN 16 #define NL80211_KEK_LEN 16 #define NL80211_REPLAY_CTR_LEN 8 @@ -2767,11 +3798,69 @@ * @NL80211_FEATURE_HT_IBSS: This driver supports IBSS with HT datarates. * @NL80211_FEATURE_INACTIVITY_TIMER: This driver takes care of freeing up * the connected inactive stations in AP mode. + * @NL80211_FEATURE_CELL_BASE_REG_HINTS: This driver has been tested + * to work properly to suppport receiving regulatory hints from + * cellular base stations. + * @NL80211_FEATURE_P2P_DEVICE_NEEDS_CHANNEL: If this is set, an active + * P2P Device (%NL80211_IFTYPE_P2P_DEVICE) requires its own channel + * in the interface combinations, even when it's only used for scan + * and remain-on-channel. This could be due to, for example, the + * remain-on-channel implementation requiring a channel context. + * @NL80211_FEATURE_SAE: This driver supports simultaneous authentication of + * equals (SAE) with user space SME (NL80211_CMD_AUTHENTICATE) in station + * mode + * @NL80211_FEATURE_LOW_PRIORITY_SCAN: This driver supports low priority scan + * @NL80211_FEATURE_SCAN_FLUSH: Scan flush is supported + * @NL80211_FEATURE_AP_SCAN: Support scanning using an AP vif + * @NL80211_FEATURE_VIF_TXPOWER: The driver supports per-vif TX power setting + * @NL80211_FEATURE_NEED_OBSS_SCAN: The driver expects userspace to perform + * OBSS scans and generate 20/40 BSS coex reports. This flag is used only + * for drivers implementing the CONNECT API, for AUTH/ASSOC it is implied. + * @NL80211_FEATURE_P2P_GO_CTWIN: P2P GO implementation supports CT Window + * setting + * @NL80211_FEATURE_P2P_GO_OPPPS: P2P GO implementation supports opportunistic + * powersave + * @NL80211_FEATURE_FULL_AP_CLIENT_STATE: The driver supports full state + * transitions for AP clients. Without this flag (and if the driver + * doesn't have the AP SME in the device) the driver supports adding + * stations only when they're associated and adds them in associated + * state (to later be transitioned into authorized), with this flag + * they should be added before even sending the authentication reply + * and then transitioned into authenticated, associated and authorized + * states using station flags. + * Note that even for drivers that support this, the default is to add + * stations in authenticated/associated state, so to add unauthenticated + * stations the authenticated/associated bits have to be set in the mask. + * @NL80211_FEATURE_ADVERTISE_CHAN_LIMITS: cfg80211 advertises channel limits + * (HT40, VHT 80/160 MHz) if this flag is set + * @NL80211_FEATURE_USERSPACE_MPM: This driver supports a userspace Mesh + * Peering Management entity which may be implemented by registering for + * beacons or NL80211_CMD_NEW_PEER_CANDIDATE events. The mesh beacon is + * still generated by the driver. + * @NL80211_FEATURE_ACTIVE_MONITOR: This driver supports an active monitor + * interface. An active monitor interface behaves like a normal monitor + * interface, but gets added to the driver. It ensures that incoming + * unicast packets directed at the configured interface address get ACKed. */ enum nl80211_feature_flags { - NL80211_FEATURE_SK_TX_STATUS = 1 << 0, - NL80211_FEATURE_HT_IBSS = 1 << 1, - NL80211_FEATURE_INACTIVITY_TIMER = 1 << 2, + NL80211_FEATURE_SK_TX_STATUS = 1 << 0, + NL80211_FEATURE_HT_IBSS = 1 << 1, + NL80211_FEATURE_INACTIVITY_TIMER = 1 << 2, + NL80211_FEATURE_CELL_BASE_REG_HINTS = 1 << 3, + NL80211_FEATURE_P2P_DEVICE_NEEDS_CHANNEL = 1 << 4, + NL80211_FEATURE_SAE = 1 << 5, + NL80211_FEATURE_LOW_PRIORITY_SCAN = 1 << 6, + NL80211_FEATURE_SCAN_FLUSH = 1 << 7, + NL80211_FEATURE_AP_SCAN = 1 << 8, + NL80211_FEATURE_VIF_TXPOWER = 1 << 9, + NL80211_FEATURE_NEED_OBSS_SCAN = 1 << 10, + NL80211_FEATURE_P2P_GO_CTWIN = 1 << 11, + NL80211_FEATURE_P2P_GO_OPPPS = 1 << 12, + /* bit 13 is reserved */ + NL80211_FEATURE_ADVERTISE_CHAN_LIMITS = 1 << 14, + NL80211_FEATURE_FULL_AP_CLIENT_STATE = 1 << 15, + NL80211_FEATURE_USERSPACE_MPM = 1 << 16, + NL80211_FEATURE_ACTIVE_MONITOR = 1 << 17, }; /** @@ -2795,4 +3884,157 @@ NL80211_PROBE_RESP_OFFLOAD_SUPPORT_80211U = 1<<3, }; +/** + * enum nl80211_connect_failed_reason - connection request failed reasons + * @NL80211_CONN_FAIL_MAX_CLIENTS: Maximum number of clients that can be + * handled by the AP is reached. + * @NL80211_CONN_FAIL_BLOCKED_CLIENT: Connection request is rejected due to ACL. + */ +enum nl80211_connect_failed_reason { + NL80211_CONN_FAIL_MAX_CLIENTS, + NL80211_CONN_FAIL_BLOCKED_CLIENT, +}; + +/** + * enum nl80211_scan_flags - scan request control flags + * + * Scan request control flags are used to control the handling + * of NL80211_CMD_TRIGGER_SCAN and NL80211_CMD_START_SCHED_SCAN + * requests. + * + * @NL80211_SCAN_FLAG_LOW_PRIORITY: scan request has low priority + * @NL80211_SCAN_FLAG_FLUSH: flush cache before scanning + * @NL80211_SCAN_FLAG_AP: force a scan even if the interface is configured + * as AP and the beaconing has already been configured. This attribute is + * dangerous because will destroy stations performance as a lot of frames + * will be lost while scanning off-channel, therefore it must be used only + * when really needed + */ +enum nl80211_scan_flags { + NL80211_SCAN_FLAG_LOW_PRIORITY = 1<<0, + NL80211_SCAN_FLAG_FLUSH = 1<<1, + NL80211_SCAN_FLAG_AP = 1<<2, +}; + +/** + * enum nl80211_acl_policy - access control policy + * + * Access control policy is applied on a MAC list set by + * %NL80211_CMD_START_AP and %NL80211_CMD_SET_MAC_ACL, to + * be used with %NL80211_ATTR_ACL_POLICY. + * + * @NL80211_ACL_POLICY_ACCEPT_UNLESS_LISTED: Deny stations which are + * listed in ACL, i.e. allow all the stations which are not listed + * in ACL to authenticate. + * @NL80211_ACL_POLICY_DENY_UNLESS_LISTED: Allow the stations which are listed + * in ACL, i.e. deny all the stations which are not listed in ACL. + */ +enum nl80211_acl_policy { + NL80211_ACL_POLICY_ACCEPT_UNLESS_LISTED, + NL80211_ACL_POLICY_DENY_UNLESS_LISTED, +}; + +/** + * enum nl80211_radar_event - type of radar event for DFS operation + * + * Type of event to be used with NL80211_ATTR_RADAR_EVENT to inform userspace + * about detected radars or success of the channel available check (CAC) + * + * @NL80211_RADAR_DETECTED: A radar pattern has been detected. The channel is + * now unusable. + * @NL80211_RADAR_CAC_FINISHED: Channel Availability Check has been finished, + * the channel is now available. + * @NL80211_RADAR_CAC_ABORTED: Channel Availability Check has been aborted, no + * change to the channel status. + * @NL80211_RADAR_NOP_FINISHED: The Non-Occupancy Period for this channel is + * over, channel becomes usable. + */ +enum nl80211_radar_event { + NL80211_RADAR_DETECTED, + NL80211_RADAR_CAC_FINISHED, + NL80211_RADAR_CAC_ABORTED, + NL80211_RADAR_NOP_FINISHED, +}; + +/** + * enum nl80211_dfs_state - DFS states for channels + * + * Channel states used by the DFS code. + * + * @NL80211_DFS_USABLE: The channel can be used, but channel availability + * check (CAC) must be performed before using it for AP or IBSS. + * @NL80211_DFS_UNAVAILABLE: A radar has been detected on this channel, it + * is therefore marked as not available. + * @NL80211_DFS_AVAILABLE: The channel has been CAC checked and is available. + */ +enum nl80211_dfs_state { + NL80211_DFS_USABLE, + NL80211_DFS_UNAVAILABLE, + NL80211_DFS_AVAILABLE, +}; + +/** + * enum enum nl80211_protocol_features - nl80211 protocol features + * @NL80211_PROTOCOL_FEATURE_SPLIT_WIPHY_DUMP: nl80211 supports splitting + * wiphy dumps (if requested by the application with the attribute + * %NL80211_ATTR_SPLIT_WIPHY_DUMP. Also supported is filtering the + * wiphy dump by %NL80211_ATTR_WIPHY, %NL80211_ATTR_IFINDEX or + * %NL80211_ATTR_WDEV. + */ +enum nl80211_protocol_features { + NL80211_PROTOCOL_FEATURE_SPLIT_WIPHY_DUMP = 1 << 0, +}; + +/** + * enum nl80211_crit_proto_id - nl80211 critical protocol identifiers + * + * @NL80211_CRIT_PROTO_UNSPEC: protocol unspecified. + * @NL80211_CRIT_PROTO_DHCP: BOOTP or DHCPv6 protocol. + * @NL80211_CRIT_PROTO_EAPOL: EAPOL protocol. + * @NL80211_CRIT_PROTO_APIPA: APIPA protocol. + * @NUM_NL80211_CRIT_PROTO: must be kept last. + */ +enum nl80211_crit_proto_id { + NL80211_CRIT_PROTO_UNSPEC, + NL80211_CRIT_PROTO_DHCP, + NL80211_CRIT_PROTO_EAPOL, + NL80211_CRIT_PROTO_APIPA, + /* add other protocols before this one */ + NUM_NL80211_CRIT_PROTO +}; + +/* maximum duration for critical protocol measures */ +#define NL80211_CRIT_PROTO_MAX_DURATION 5000 /* msec */ + +/** + * enum nl80211_rxmgmt_flags - flags for received management frame. + * + * Used by cfg80211_rx_mgmt() + * + * @NL80211_RXMGMT_FLAG_ANSWERED: frame was answered by device/driver. + */ +enum nl80211_rxmgmt_flags { + NL80211_RXMGMT_FLAG_ANSWERED = 1 << 0, +}; + +/* + * If this flag is unset, the lower 24 bits are an OUI, if set + * a Linux nl80211 vendor ID is used (no such IDs are allocated + * yet, so that's not valid so far) + */ +#define NL80211_VENDOR_ID_IS_LINUX 0x80000000 + +/** + * struct nl80211_vendor_cmd_info - vendor command data + * @vendor_id: If the %NL80211_VENDOR_ID_IS_LINUX flag is clear, then the + * value is a 24-bit OUI; if it is set then a separately allocated ID + * may be used, but no such IDs are allocated yet. New IDs should be + * added to this file when needed. + * @subcmd: sub-command ID for the command + */ +struct nl80211_vendor_cmd_info { + __u32 vendor_id; + __u32 subcmd; +}; + #endif /* __LINUX_NL80211_H */ diff -Nru wpa-1.0/src/drivers/priv_netlink.h wpa-2.1/src/drivers/priv_netlink.h --- wpa-1.0/src/drivers/priv_netlink.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/drivers/priv_netlink.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * wpa_supplicant - Private copy of Linux netlink/rtnetlink definitions. * Copyright (c) 2003-2005, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef PRIV_NETLINK_H @@ -75,6 +69,7 @@ (struct rtattr *) (((char *)(rta)) + RTA_ALIGN((rta)->rta_len))) #define RTA_LENGTH(len) (RTA_ALIGN(sizeof(struct rtattr)) + (len)) #define RTA_DATA(rta) ((void *) (((char *) (rta)) + RTA_LENGTH(0))) +#define RTA_PAYLOAD(rta) ((int) ((rta)->rta_len) - RTA_LENGTH(0)) struct sockaddr_nl diff -Nru wpa-1.0/src/drivers/rfkill.c wpa-2.1/src/drivers/rfkill.c --- wpa-1.0/src/drivers/rfkill.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/drivers/rfkill.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * Linux rfkill helper functions for driver wrappers * Copyright (c) 2010, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff -Nru wpa-1.0/src/drivers/rfkill.h wpa-2.1/src/drivers/rfkill.h --- wpa-1.0/src/drivers/rfkill.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/drivers/rfkill.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * Linux rfkill helper functions for driver wrappers * Copyright (c) 2010, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef RFKILL_H diff -Nru wpa-1.0/src/drivers/wireless_copy.h wpa-2.1/src/drivers/wireless_copy.h --- wpa-1.0/src/drivers/wireless_copy.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/drivers/wireless_copy.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,1183 +0,0 @@ -/* This is based on Linux Wireless Extensions header file from WIRELESS_EXT 22. - * I have just removed kernel related headers and added some typedefs etc. to - * make this easier to include into user space programs. - * Jouni Malinen, 2005-03-12. - */ - - -/* - * This file define a set of standard wireless extensions - * - * Version : 22 16.3.07 - * - * Authors : Jean Tourrilhes - HPL - - * Copyright (c) 1997-2007 Jean Tourrilhes, All Rights Reserved. - */ - -#ifndef _LINUX_WIRELESS_H -#define _LINUX_WIRELESS_H - -/************************** DOCUMENTATION **************************/ -/* - * Initial APIs (1996 -> onward) : - * ----------------------------- - * Basically, the wireless extensions are for now a set of standard ioctl - * call + /proc/net/wireless - * - * The entry /proc/net/wireless give statistics and information on the - * driver. - * This is better than having each driver having its entry because - * its centralised and we may remove the driver module safely. - * - * Ioctl are used to configure the driver and issue commands. This is - * better than command line options of insmod because we may want to - * change dynamically (while the driver is running) some parameters. - * - * The ioctl mechanimsm are copied from standard devices ioctl. - * We have the list of command plus a structure descibing the - * data exchanged... - * Note that to add these ioctl, I was obliged to modify : - * # net/core/dev.c (two place + add include) - * # net/ipv4/af_inet.c (one place + add include) - * - * /proc/net/wireless is a copy of /proc/net/dev. - * We have a structure for data passed from the driver to /proc/net/wireless - * Too add this, I've modified : - * # net/core/dev.c (two other places) - * # include/linux/netdevice.h (one place) - * # include/linux/proc_fs.h (one place) - * - * New driver API (2002 -> onward) : - * ------------------------------- - * This file is only concerned with the user space API and common definitions. - * The new driver API is defined and documented in : - * # include/net/iw_handler.h - * - * Note as well that /proc/net/wireless implementation has now moved in : - * # net/core/wireless.c - * - * Wireless Events (2002 -> onward) : - * -------------------------------- - * Events are defined at the end of this file, and implemented in : - * # net/core/wireless.c - * - * Other comments : - * -------------- - * Do not add here things that are redundant with other mechanisms - * (drivers init, ifconfig, /proc/net/dev, ...) and with are not - * wireless specific. - * - * These wireless extensions are not magic : each driver has to provide - * support for them... - * - * IMPORTANT NOTE : As everything in the kernel, this is very much a - * work in progress. Contact me if you have ideas of improvements... - */ - -/***************************** INCLUDES *****************************/ - - /* jkm - replaced linux headers with C library headers, added typedefs */ -#ifdef ANDROID -#include /* for __u* and __s* typedefs */ -#include /* for "struct sockaddr" et al */ -#include /* for IFNAMSIZ and co... */ -#else /* ANDROID */ -#include -#include -typedef __uint32_t __u32; -typedef __int32_t __s32; -typedef __uint16_t __u16; -typedef __int16_t __s16; -typedef __uint8_t __u8; -#ifndef __user -#define __user -#endif /* __user */ -#endif /* ANDROID */ - -/***************************** VERSION *****************************/ -/* - * This constant is used to know the availability of the wireless - * extensions and to know which version of wireless extensions it is - * (there is some stuff that will be added in the future...) - * I just plan to increment with each new version. - */ -#define WIRELESS_EXT 22 - -/* - * Changes : - * - * V2 to V3 - * -------- - * Alan Cox start some incompatibles changes. I've integrated a bit more. - * - Encryption renamed to Encode to avoid US regulation problems - * - Frequency changed from float to struct to avoid problems on old 386 - * - * V3 to V4 - * -------- - * - Add sensitivity - * - * V4 to V5 - * -------- - * - Missing encoding definitions in range - * - Access points stuff - * - * V5 to V6 - * -------- - * - 802.11 support (ESSID ioctls) - * - * V6 to V7 - * -------- - * - define IW_ESSID_MAX_SIZE and IW_MAX_AP - * - * V7 to V8 - * -------- - * - Changed my e-mail address - * - More 802.11 support (nickname, rate, rts, frag) - * - List index in frequencies - * - * V8 to V9 - * -------- - * - Support for 'mode of operation' (ad-hoc, managed...) - * - Support for unicast and multicast power saving - * - Change encoding to support larger tokens (>64 bits) - * - Updated iw_params (disable, flags) and use it for NWID - * - Extracted iw_point from iwreq for clarity - * - * V9 to V10 - * --------- - * - Add PM capability to range structure - * - Add PM modifier : MAX/MIN/RELATIVE - * - Add encoding option : IW_ENCODE_NOKEY - * - Add TxPower ioctls (work like TxRate) - * - * V10 to V11 - * ---------- - * - Add WE version in range (help backward/forward compatibility) - * - Add retry ioctls (work like PM) - * - * V11 to V12 - * ---------- - * - Add SIOCSIWSTATS to get /proc/net/wireless programatically - * - Add DEV PRIVATE IOCTL to avoid collisions in SIOCDEVPRIVATE space - * - Add new statistics (frag, retry, beacon) - * - Add average quality (for user space calibration) - * - * V12 to V13 - * ---------- - * - Document creation of new driver API. - * - Extract union iwreq_data from struct iwreq (for new driver API). - * - Rename SIOCSIWNAME as SIOCSIWCOMMIT - * - * V13 to V14 - * ---------- - * - Wireless Events support : define struct iw_event - * - Define additional specific event numbers - * - Add "addr" and "param" fields in union iwreq_data - * - AP scanning stuff (SIOCSIWSCAN and friends) - * - * V14 to V15 - * ---------- - * - Add IW_PRIV_TYPE_ADDR for struct sockaddr private arg - * - Make struct iw_freq signed (both m & e), add explicit padding - * - Add IWEVCUSTOM for driver specific event/scanning token - * - Add IW_MAX_GET_SPY for driver returning a lot of addresses - * - Add IW_TXPOW_RANGE for range of Tx Powers - * - Add IWEVREGISTERED & IWEVEXPIRED events for Access Points - * - Add IW_MODE_MONITOR for passive monitor - * - * V15 to V16 - * ---------- - * - Increase the number of bitrates in iw_range to 32 (for 802.11g) - * - Increase the number of frequencies in iw_range to 32 (for 802.11b+a) - * - Reshuffle struct iw_range for increases, add filler - * - Increase IW_MAX_AP to 64 for driver returning a lot of addresses - * - Remove IW_MAX_GET_SPY because conflict with enhanced spy support - * - Add SIOCSIWTHRSPY/SIOCGIWTHRSPY and "struct iw_thrspy" - * - Add IW_ENCODE_TEMP and iw_range->encoding_login_index - * - * V16 to V17 - * ---------- - * - Add flags to frequency -> auto/fixed - * - Document (struct iw_quality *)->updated, add new flags (INVALID) - * - Wireless Event capability in struct iw_range - * - Add support for relative TxPower (yick !) - * - * V17 to V18 (From Jouni Malinen ) - * ---------- - * - Add support for WPA/WPA2 - * - Add extended encoding configuration (SIOCSIWENCODEEXT and - * SIOCGIWENCODEEXT) - * - Add SIOCSIWGENIE/SIOCGIWGENIE - * - Add SIOCSIWMLME - * - Add SIOCSIWPMKSA - * - Add struct iw_range bit field for supported encoding capabilities - * - Add optional scan request parameters for SIOCSIWSCAN - * - Add SIOCSIWAUTH/SIOCGIWAUTH for setting authentication and WPA - * related parameters (extensible up to 4096 parameter values) - * - Add wireless events: IWEVGENIE, IWEVMICHAELMICFAILURE, - * IWEVASSOCREQIE, IWEVASSOCRESPIE, IWEVPMKIDCAND - * - * V18 to V19 - * ---------- - * - Remove (struct iw_point *)->pointer from events and streams - * - Remove header includes to help user space - * - Increase IW_ENCODING_TOKEN_MAX from 32 to 64 - * - Add IW_QUAL_ALL_UPDATED and IW_QUAL_ALL_INVALID macros - * - Add explicit flag to tell stats are in dBm : IW_QUAL_DBM - * - Add IW_IOCTL_IDX() and IW_EVENT_IDX() macros - * - * V19 to V20 - * ---------- - * - RtNetlink requests support (SET/GET) - * - * V20 to V21 - * ---------- - * - Remove (struct net_device *)->get_wireless_stats() - * - Change length in ESSID and NICK to strlen() instead of strlen()+1 - * - Add IW_RETRY_SHORT/IW_RETRY_LONG retry modifiers - * - Power/Retry relative values no longer * 100000 - * - Add explicit flag to tell stats are in 802.11k RCPI : IW_QUAL_RCPI - * - * V21 to V22 - * ---------- - * - Prevent leaking of kernel space in stream on 64 bits. - */ - -/**************************** CONSTANTS ****************************/ - -/* -------------------------- IOCTL LIST -------------------------- */ - -/* Wireless Identification */ -#define SIOCSIWCOMMIT 0x8B00 /* Commit pending changes to driver */ -#define SIOCGIWNAME 0x8B01 /* get name == wireless protocol */ -/* SIOCGIWNAME is used to verify the presence of Wireless Extensions. - * Common values : "IEEE 802.11-DS", "IEEE 802.11-FH", "IEEE 802.11b"... - * Don't put the name of your driver there, it's useless. */ - -/* Basic operations */ -#define SIOCSIWNWID 0x8B02 /* set network id (pre-802.11) */ -#define SIOCGIWNWID 0x8B03 /* get network id (the cell) */ -#define SIOCSIWFREQ 0x8B04 /* set channel/frequency (Hz) */ -#define SIOCGIWFREQ 0x8B05 /* get channel/frequency (Hz) */ -#define SIOCSIWMODE 0x8B06 /* set operation mode */ -#define SIOCGIWMODE 0x8B07 /* get operation mode */ -#define SIOCSIWSENS 0x8B08 /* set sensitivity (dBm) */ -#define SIOCGIWSENS 0x8B09 /* get sensitivity (dBm) */ - -/* Informative stuff */ -#define SIOCSIWRANGE 0x8B0A /* Unused */ -#define SIOCGIWRANGE 0x8B0B /* Get range of parameters */ -#define SIOCSIWPRIV 0x8B0C /* Unused */ -#define SIOCGIWPRIV 0x8B0D /* get private ioctl interface info */ -#define SIOCSIWSTATS 0x8B0E /* Unused */ -#define SIOCGIWSTATS 0x8B0F /* Get /proc/net/wireless stats */ -/* SIOCGIWSTATS is strictly used between user space and the kernel, and - * is never passed to the driver (i.e. the driver will never see it). */ - -/* Spy support (statistics per MAC address - used for Mobile IP support) */ -#define SIOCSIWSPY 0x8B10 /* set spy addresses */ -#define SIOCGIWSPY 0x8B11 /* get spy info (quality of link) */ -#define SIOCSIWTHRSPY 0x8B12 /* set spy threshold (spy event) */ -#define SIOCGIWTHRSPY 0x8B13 /* get spy threshold */ - -/* Access Point manipulation */ -#define SIOCSIWAP 0x8B14 /* set access point MAC addresses */ -#define SIOCGIWAP 0x8B15 /* get access point MAC addresses */ -#define SIOCGIWAPLIST 0x8B17 /* Deprecated in favor of scanning */ -#define SIOCSIWSCAN 0x8B18 /* trigger scanning (list cells) */ -#define SIOCGIWSCAN 0x8B19 /* get scanning results */ - -/* 802.11 specific support */ -#define SIOCSIWESSID 0x8B1A /* set ESSID (network name) */ -#define SIOCGIWESSID 0x8B1B /* get ESSID */ -#define SIOCSIWNICKN 0x8B1C /* set node name/nickname */ -#define SIOCGIWNICKN 0x8B1D /* get node name/nickname */ -/* As the ESSID and NICKN are strings up to 32 bytes long, it doesn't fit - * within the 'iwreq' structure, so we need to use the 'data' member to - * point to a string in user space, like it is done for RANGE... */ - -/* Other parameters useful in 802.11 and some other devices */ -#define SIOCSIWRATE 0x8B20 /* set default bit rate (bps) */ -#define SIOCGIWRATE 0x8B21 /* get default bit rate (bps) */ -#define SIOCSIWRTS 0x8B22 /* set RTS/CTS threshold (bytes) */ -#define SIOCGIWRTS 0x8B23 /* get RTS/CTS threshold (bytes) */ -#define SIOCSIWFRAG 0x8B24 /* set fragmentation thr (bytes) */ -#define SIOCGIWFRAG 0x8B25 /* get fragmentation thr (bytes) */ -#define SIOCSIWTXPOW 0x8B26 /* set transmit power (dBm) */ -#define SIOCGIWTXPOW 0x8B27 /* get transmit power (dBm) */ -#define SIOCSIWRETRY 0x8B28 /* set retry limits and lifetime */ -#define SIOCGIWRETRY 0x8B29 /* get retry limits and lifetime */ - -/* Encoding stuff (scrambling, hardware security, WEP...) */ -#define SIOCSIWENCODE 0x8B2A /* set encoding token & mode */ -#define SIOCGIWENCODE 0x8B2B /* get encoding token & mode */ -/* Power saving stuff (power management, unicast and multicast) */ -#define SIOCSIWPOWER 0x8B2C /* set Power Management settings */ -#define SIOCGIWPOWER 0x8B2D /* get Power Management settings */ - -/* WPA : Generic IEEE 802.11 informatiom element (e.g., for WPA/RSN/WMM). - * This ioctl uses struct iw_point and data buffer that includes IE id and len - * fields. More than one IE may be included in the request. Setting the generic - * IE to empty buffer (len=0) removes the generic IE from the driver. Drivers - * are allowed to generate their own WPA/RSN IEs, but in these cases, drivers - * are required to report the used IE as a wireless event, e.g., when - * associating with an AP. */ -#define SIOCSIWGENIE 0x8B30 /* set generic IE */ -#define SIOCGIWGENIE 0x8B31 /* get generic IE */ - -/* WPA : IEEE 802.11 MLME requests */ -#define SIOCSIWMLME 0x8B16 /* request MLME operation; uses - * struct iw_mlme */ -/* WPA : Authentication mode parameters */ -#define SIOCSIWAUTH 0x8B32 /* set authentication mode params */ -#define SIOCGIWAUTH 0x8B33 /* get authentication mode params */ - -/* WPA : Extended version of encoding configuration */ -#define SIOCSIWENCODEEXT 0x8B34 /* set encoding token & mode */ -#define SIOCGIWENCODEEXT 0x8B35 /* get encoding token & mode */ - -/* WPA2 : PMKSA cache management */ -#define SIOCSIWPMKSA 0x8B36 /* PMKSA cache operation */ - -/* -------------------- DEV PRIVATE IOCTL LIST -------------------- */ - -/* These 32 ioctl are wireless device private, for 16 commands. - * Each driver is free to use them for whatever purpose it chooses, - * however the driver *must* export the description of those ioctls - * with SIOCGIWPRIV and *must* use arguments as defined below. - * If you don't follow those rules, DaveM is going to hate you (reason : - * it make mixed 32/64bit operation impossible). - */ -#define SIOCIWFIRSTPRIV 0x8BE0 -#define SIOCIWLASTPRIV 0x8BFF -/* Previously, we were using SIOCDEVPRIVATE, but we now have our - * separate range because of collisions with other tools such as - * 'mii-tool'. - * We now have 32 commands, so a bit more space ;-). - * Also, all 'even' commands are only usable by root and don't return the - * content of ifr/iwr to user (but you are not obliged to use the set/get - * convention, just use every other two command). More details in iwpriv.c. - * And I repeat : you are not forced to use them with iwpriv, but you - * must be compliant with it. - */ - -/* ------------------------- IOCTL STUFF ------------------------- */ - -/* The first and the last (range) */ -#define SIOCIWFIRST 0x8B00 -#define SIOCIWLAST SIOCIWLASTPRIV /* 0x8BFF */ -#define IW_IOCTL_IDX(cmd) ((cmd) - SIOCIWFIRST) -#define IW_HANDLER(id, func) \ - [IW_IOCTL_IDX(id)] = func - -/* Odd : get (world access), even : set (root access) */ -#define IW_IS_SET(cmd) (!((cmd) & 0x1)) -#define IW_IS_GET(cmd) ((cmd) & 0x1) - -/* ----------------------- WIRELESS EVENTS ----------------------- */ -/* Those are *NOT* ioctls, do not issue request on them !!! */ -/* Most events use the same identifier as ioctl requests */ - -#define IWEVTXDROP 0x8C00 /* Packet dropped to excessive retry */ -#define IWEVQUAL 0x8C01 /* Quality part of statistics (scan) */ -#define IWEVCUSTOM 0x8C02 /* Driver specific ascii string */ -#define IWEVREGISTERED 0x8C03 /* Discovered a new node (AP mode) */ -#define IWEVEXPIRED 0x8C04 /* Expired a node (AP mode) */ -#define IWEVGENIE 0x8C05 /* Generic IE (WPA, RSN, WMM, ..) - * (scan results); This includes id and - * length fields. One IWEVGENIE may - * contain more than one IE. Scan - * results may contain one or more - * IWEVGENIE events. */ -#define IWEVMICHAELMICFAILURE 0x8C06 /* Michael MIC failure - * (struct iw_michaelmicfailure) - */ -#define IWEVASSOCREQIE 0x8C07 /* IEs used in (Re)Association Request. - * The data includes id and length - * fields and may contain more than one - * IE. This event is required in - * Managed mode if the driver - * generates its own WPA/RSN IE. This - * should be sent just before - * IWEVREGISTERED event for the - * association. */ -#define IWEVASSOCRESPIE 0x8C08 /* IEs used in (Re)Association - * Response. The data includes id and - * length fields and may contain more - * than one IE. This may be sent - * between IWEVASSOCREQIE and - * IWEVREGISTERED events for the - * association. */ -#define IWEVPMKIDCAND 0x8C09 /* PMKID candidate for RSN - * pre-authentication - * (struct iw_pmkid_cand) */ - -#define IWEVFIRST 0x8C00 -#define IW_EVENT_IDX(cmd) ((cmd) - IWEVFIRST) - -/* ------------------------- PRIVATE INFO ------------------------- */ -/* - * The following is used with SIOCGIWPRIV. It allow a driver to define - * the interface (name, type of data) for its private ioctl. - * Privates ioctl are SIOCIWFIRSTPRIV -> SIOCIWLASTPRIV - */ - -#define IW_PRIV_TYPE_MASK 0x7000 /* Type of arguments */ -#define IW_PRIV_TYPE_NONE 0x0000 -#define IW_PRIV_TYPE_BYTE 0x1000 /* Char as number */ -#define IW_PRIV_TYPE_CHAR 0x2000 /* Char as character */ -#define IW_PRIV_TYPE_INT 0x4000 /* 32 bits int */ -#define IW_PRIV_TYPE_FLOAT 0x5000 /* struct iw_freq */ -#define IW_PRIV_TYPE_ADDR 0x6000 /* struct sockaddr */ - -#define IW_PRIV_SIZE_FIXED 0x0800 /* Variable or fixed number of args */ - -#define IW_PRIV_SIZE_MASK 0x07FF /* Max number of those args */ - -/* - * Note : if the number of args is fixed and the size < 16 octets, - * instead of passing a pointer we will put args in the iwreq struct... - */ - -/* ----------------------- OTHER CONSTANTS ----------------------- */ - -/* Maximum frequencies in the range struct */ -#define IW_MAX_FREQUENCIES 32 -/* Note : if you have something like 80 frequencies, - * don't increase this constant and don't fill the frequency list. - * The user will be able to set by channel anyway... */ - -/* Maximum bit rates in the range struct */ -#define IW_MAX_BITRATES 32 - -/* Maximum tx powers in the range struct */ -#define IW_MAX_TXPOWER 8 -/* Note : if you more than 8 TXPowers, just set the max and min or - * a few of them in the struct iw_range. */ - -/* Maximum of address that you may set with SPY */ -#define IW_MAX_SPY 8 - -/* Maximum of address that you may get in the - list of access points in range */ -#define IW_MAX_AP 64 - -/* Maximum size of the ESSID and NICKN strings */ -#define IW_ESSID_MAX_SIZE 32 - -/* Modes of operation */ -#define IW_MODE_AUTO 0 /* Let the driver decides */ -#define IW_MODE_ADHOC 1 /* Single cell network */ -#define IW_MODE_INFRA 2 /* Multi cell network, roaming, ... */ -#define IW_MODE_MASTER 3 /* Synchronisation master or Access Point */ -#define IW_MODE_REPEAT 4 /* Wireless Repeater (forwarder) */ -#define IW_MODE_SECOND 5 /* Secondary master/repeater (backup) */ -#define IW_MODE_MONITOR 6 /* Passive monitor (listen only) */ -#define IW_MODE_MESH 7 /* Mesh (IEEE 802.11s) network */ - -/* Statistics flags (bitmask in updated) */ -#define IW_QUAL_QUAL_UPDATED 0x01 /* Value was updated since last read */ -#define IW_QUAL_LEVEL_UPDATED 0x02 -#define IW_QUAL_NOISE_UPDATED 0x04 -#define IW_QUAL_ALL_UPDATED 0x07 -#define IW_QUAL_DBM 0x08 /* Level + Noise are dBm */ -#define IW_QUAL_QUAL_INVALID 0x10 /* Driver doesn't provide value */ -#define IW_QUAL_LEVEL_INVALID 0x20 -#define IW_QUAL_NOISE_INVALID 0x40 -#define IW_QUAL_RCPI 0x80 /* Level + Noise are 802.11k RCPI */ -#define IW_QUAL_ALL_INVALID 0x70 - -/* Frequency flags */ -#define IW_FREQ_AUTO 0x00 /* Let the driver decides */ -#define IW_FREQ_FIXED 0x01 /* Force a specific value */ - -/* Maximum number of size of encoding token available - * they are listed in the range structure */ -#define IW_MAX_ENCODING_SIZES 8 - -/* Maximum size of the encoding token in bytes */ -#define IW_ENCODING_TOKEN_MAX 64 /* 512 bits (for now) */ - -/* Flags for encoding (along with the token) */ -#define IW_ENCODE_INDEX 0x00FF /* Token index (if needed) */ -#define IW_ENCODE_FLAGS 0xFF00 /* Flags defined below */ -#define IW_ENCODE_MODE 0xF000 /* Modes defined below */ -#define IW_ENCODE_DISABLED 0x8000 /* Encoding disabled */ -#define IW_ENCODE_ENABLED 0x0000 /* Encoding enabled */ -#define IW_ENCODE_RESTRICTED 0x4000 /* Refuse non-encoded packets */ -#define IW_ENCODE_OPEN 0x2000 /* Accept non-encoded packets */ -#define IW_ENCODE_NOKEY 0x0800 /* Key is write only, so not present */ -#define IW_ENCODE_TEMP 0x0400 /* Temporary key */ - -/* Power management flags available (along with the value, if any) */ -#define IW_POWER_ON 0x0000 /* No details... */ -#define IW_POWER_TYPE 0xF000 /* Type of parameter */ -#define IW_POWER_PERIOD 0x1000 /* Value is a period/duration of */ -#define IW_POWER_TIMEOUT 0x2000 /* Value is a timeout (to go asleep) */ -#define IW_POWER_MODE 0x0F00 /* Power Management mode */ -#define IW_POWER_UNICAST_R 0x0100 /* Receive only unicast messages */ -#define IW_POWER_MULTICAST_R 0x0200 /* Receive only multicast messages */ -#define IW_POWER_ALL_R 0x0300 /* Receive all messages though PM */ -#define IW_POWER_FORCE_S 0x0400 /* Force PM procedure for sending unicast */ -#define IW_POWER_REPEATER 0x0800 /* Repeat broadcast messages in PM period */ -#define IW_POWER_MODIFIER 0x000F /* Modify a parameter */ -#define IW_POWER_MIN 0x0001 /* Value is a minimum */ -#define IW_POWER_MAX 0x0002 /* Value is a maximum */ -#define IW_POWER_RELATIVE 0x0004 /* Value is not in seconds/ms/us */ - -/* Transmit Power flags available */ -#define IW_TXPOW_TYPE 0x00FF /* Type of value */ -#define IW_TXPOW_DBM 0x0000 /* Value is in dBm */ -#define IW_TXPOW_MWATT 0x0001 /* Value is in mW */ -#define IW_TXPOW_RELATIVE 0x0002 /* Value is in arbitrary units */ -#define IW_TXPOW_RANGE 0x1000 /* Range of value between min/max */ - -/* Retry limits and lifetime flags available */ -#define IW_RETRY_ON 0x0000 /* No details... */ -#define IW_RETRY_TYPE 0xF000 /* Type of parameter */ -#define IW_RETRY_LIMIT 0x1000 /* Maximum number of retries*/ -#define IW_RETRY_LIFETIME 0x2000 /* Maximum duration of retries in us */ -#define IW_RETRY_MODIFIER 0x00FF /* Modify a parameter */ -#define IW_RETRY_MIN 0x0001 /* Value is a minimum */ -#define IW_RETRY_MAX 0x0002 /* Value is a maximum */ -#define IW_RETRY_RELATIVE 0x0004 /* Value is not in seconds/ms/us */ -#define IW_RETRY_SHORT 0x0010 /* Value is for short packets */ -#define IW_RETRY_LONG 0x0020 /* Value is for long packets */ - -/* Scanning request flags */ -#define IW_SCAN_DEFAULT 0x0000 /* Default scan of the driver */ -#define IW_SCAN_ALL_ESSID 0x0001 /* Scan all ESSIDs */ -#define IW_SCAN_THIS_ESSID 0x0002 /* Scan only this ESSID */ -#define IW_SCAN_ALL_FREQ 0x0004 /* Scan all Frequencies */ -#define IW_SCAN_THIS_FREQ 0x0008 /* Scan only this Frequency */ -#define IW_SCAN_ALL_MODE 0x0010 /* Scan all Modes */ -#define IW_SCAN_THIS_MODE 0x0020 /* Scan only this Mode */ -#define IW_SCAN_ALL_RATE 0x0040 /* Scan all Bit-Rates */ -#define IW_SCAN_THIS_RATE 0x0080 /* Scan only this Bit-Rate */ -/* struct iw_scan_req scan_type */ -#define IW_SCAN_TYPE_ACTIVE 0 -#define IW_SCAN_TYPE_PASSIVE 1 -/* Maximum size of returned data */ -#define IW_SCAN_MAX_DATA 4096 /* In bytes */ - -/* Scan capability flags - in (struct iw_range *)->scan_capa */ -#define IW_SCAN_CAPA_NONE 0x00 -#define IW_SCAN_CAPA_ESSID 0x01 -#define IW_SCAN_CAPA_BSSID 0x02 -#define IW_SCAN_CAPA_CHANNEL 0x04 -#define IW_SCAN_CAPA_MODE 0x08 -#define IW_SCAN_CAPA_RATE 0x10 -#define IW_SCAN_CAPA_TYPE 0x20 -#define IW_SCAN_CAPA_TIME 0x40 - -/* Max number of char in custom event - use multiple of them if needed */ -#define IW_CUSTOM_MAX 256 /* In bytes */ - -/* Generic information element */ -#define IW_GENERIC_IE_MAX 1024 - -/* MLME requests (SIOCSIWMLME / struct iw_mlme) */ -#define IW_MLME_DEAUTH 0 -#define IW_MLME_DISASSOC 1 -#define IW_MLME_AUTH 2 -#define IW_MLME_ASSOC 3 - -/* SIOCSIWAUTH/SIOCGIWAUTH struct iw_param flags */ -#define IW_AUTH_INDEX 0x0FFF -#define IW_AUTH_FLAGS 0xF000 -/* SIOCSIWAUTH/SIOCGIWAUTH parameters (0 .. 4095) - * (IW_AUTH_INDEX mask in struct iw_param flags; this is the index of the - * parameter that is being set/get to; value will be read/written to - * struct iw_param value field) */ -#define IW_AUTH_WPA_VERSION 0 -#define IW_AUTH_CIPHER_PAIRWISE 1 -#define IW_AUTH_CIPHER_GROUP 2 -#define IW_AUTH_KEY_MGMT 3 -#define IW_AUTH_TKIP_COUNTERMEASURES 4 -#define IW_AUTH_DROP_UNENCRYPTED 5 -#define IW_AUTH_80211_AUTH_ALG 6 -#define IW_AUTH_WPA_ENABLED 7 -#define IW_AUTH_RX_UNENCRYPTED_EAPOL 8 -#define IW_AUTH_ROAMING_CONTROL 9 -#define IW_AUTH_PRIVACY_INVOKED 10 -#define IW_AUTH_CIPHER_GROUP_MGMT 11 -#define IW_AUTH_MFP 12 - -/* IW_AUTH_WPA_VERSION values (bit field) */ -#define IW_AUTH_WPA_VERSION_DISABLED 0x00000001 -#define IW_AUTH_WPA_VERSION_WPA 0x00000002 -#define IW_AUTH_WPA_VERSION_WPA2 0x00000004 - -/* IW_AUTH_PAIRWISE_CIPHER, IW_AUTH_GROUP_CIPHER, and IW_AUTH_CIPHER_GROUP_MGMT - * values (bit field) */ -#define IW_AUTH_CIPHER_NONE 0x00000001 -#define IW_AUTH_CIPHER_WEP40 0x00000002 -#define IW_AUTH_CIPHER_TKIP 0x00000004 -#define IW_AUTH_CIPHER_CCMP 0x00000008 -#define IW_AUTH_CIPHER_WEP104 0x00000010 -#define IW_AUTH_CIPHER_AES_CMAC 0x00000020 - -/* IW_AUTH_KEY_MGMT values (bit field) */ -#define IW_AUTH_KEY_MGMT_802_1X 1 -#define IW_AUTH_KEY_MGMT_PSK 2 - -/* IW_AUTH_80211_AUTH_ALG values (bit field) */ -#define IW_AUTH_ALG_OPEN_SYSTEM 0x00000001 -#define IW_AUTH_ALG_SHARED_KEY 0x00000002 -#define IW_AUTH_ALG_LEAP 0x00000004 - -/* IW_AUTH_ROAMING_CONTROL values */ -#define IW_AUTH_ROAMING_ENABLE 0 /* driver/firmware based roaming */ -#define IW_AUTH_ROAMING_DISABLE 1 /* user space program used for roaming - * control */ - -/* IW_AUTH_MFP (management frame protection) values */ -#define IW_AUTH_MFP_DISABLED 0 /* MFP disabled */ -#define IW_AUTH_MFP_OPTIONAL 1 /* MFP optional */ -#define IW_AUTH_MFP_REQUIRED 2 /* MFP required */ - -/* SIOCSIWENCODEEXT definitions */ -#define IW_ENCODE_SEQ_MAX_SIZE 8 -/* struct iw_encode_ext ->alg */ -#define IW_ENCODE_ALG_NONE 0 -#define IW_ENCODE_ALG_WEP 1 -#define IW_ENCODE_ALG_TKIP 2 -#define IW_ENCODE_ALG_CCMP 3 -#define IW_ENCODE_ALG_PMK 4 -#define IW_ENCODE_ALG_AES_CMAC 5 -/* struct iw_encode_ext ->ext_flags */ -#define IW_ENCODE_EXT_TX_SEQ_VALID 0x00000001 -#define IW_ENCODE_EXT_RX_SEQ_VALID 0x00000002 -#define IW_ENCODE_EXT_GROUP_KEY 0x00000004 -#define IW_ENCODE_EXT_SET_TX_KEY 0x00000008 - -/* IWEVMICHAELMICFAILURE : struct iw_michaelmicfailure ->flags */ -#define IW_MICFAILURE_KEY_ID 0x00000003 /* Key ID 0..3 */ -#define IW_MICFAILURE_GROUP 0x00000004 -#define IW_MICFAILURE_PAIRWISE 0x00000008 -#define IW_MICFAILURE_STAKEY 0x00000010 -#define IW_MICFAILURE_COUNT 0x00000060 /* 1 or 2 (0 = count not supported) - */ - -/* Bit field values for enc_capa in struct iw_range */ -#define IW_ENC_CAPA_WPA 0x00000001 -#define IW_ENC_CAPA_WPA2 0x00000002 -#define IW_ENC_CAPA_CIPHER_TKIP 0x00000004 -#define IW_ENC_CAPA_CIPHER_CCMP 0x00000008 -#define IW_ENC_CAPA_4WAY_HANDSHAKE 0x00000010 - -/* Event capability macros - in (struct iw_range *)->event_capa - * Because we have more than 32 possible events, we use an array of - * 32 bit bitmasks. Note : 32 bits = 0x20 = 2^5. */ -#define IW_EVENT_CAPA_BASE(cmd) ((cmd >= SIOCIWFIRSTPRIV) ? \ - (cmd - SIOCIWFIRSTPRIV + 0x60) : \ - (cmd - SIOCIWFIRST)) -#define IW_EVENT_CAPA_INDEX(cmd) (IW_EVENT_CAPA_BASE(cmd) >> 5) -#define IW_EVENT_CAPA_MASK(cmd) (1 << (IW_EVENT_CAPA_BASE(cmd) & 0x1F)) -/* Event capability constants - event autogenerated by the kernel - * This list is valid for most 802.11 devices, customise as needed... */ -#define IW_EVENT_CAPA_K_0 (IW_EVENT_CAPA_MASK(0x8B04) | \ - IW_EVENT_CAPA_MASK(0x8B06) | \ - IW_EVENT_CAPA_MASK(0x8B1A)) -#define IW_EVENT_CAPA_K_1 (IW_EVENT_CAPA_MASK(0x8B2A)) -/* "Easy" macro to set events in iw_range (less efficient) */ -#define IW_EVENT_CAPA_SET(event_capa, cmd) (event_capa[IW_EVENT_CAPA_INDEX(cmd)] |= IW_EVENT_CAPA_MASK(cmd)) -#define IW_EVENT_CAPA_SET_KERNEL(event_capa) {event_capa[0] |= IW_EVENT_CAPA_K_0; event_capa[1] |= IW_EVENT_CAPA_K_1; } - - -/****************************** TYPES ******************************/ - -/* --------------------------- SUBTYPES --------------------------- */ -/* - * Generic format for most parameters that fit in an int - */ -struct iw_param -{ - __s32 value; /* The value of the parameter itself */ - __u8 fixed; /* Hardware should not use auto select */ - __u8 disabled; /* Disable the feature */ - __u16 flags; /* Various specifc flags (if any) */ -}; - -/* - * For all data larger than 16 octets, we need to use a - * pointer to memory allocated in user space. - */ -struct iw_point -{ - void __user *pointer; /* Pointer to the data (in user space) */ - __u16 length; /* number of fields or size in bytes */ - __u16 flags; /* Optional params */ -}; - -#ifdef __KERNEL__ -#ifdef CONFIG_COMPAT - -#include - -struct compat_iw_point { - compat_caddr_t pointer; - __u16 length; - __u16 flags; -}; -#endif -#endif - -/* - * A frequency - * For numbers lower than 10^9, we encode the number in 'm' and - * set 'e' to 0 - * For number greater than 10^9, we divide it by the lowest power - * of 10 to get 'm' lower than 10^9, with 'm'= f / (10^'e')... - * The power of 10 is in 'e', the result of the division is in 'm'. - */ -struct iw_freq -{ - __s32 m; /* Mantissa */ - __s16 e; /* Exponent */ - __u8 i; /* List index (when in range struct) */ - __u8 flags; /* Flags (fixed/auto) */ -}; - -/* - * Quality of the link - */ -struct iw_quality -{ - __u8 qual; /* link quality (%retries, SNR, - %missed beacons or better...) */ - __u8 level; /* signal level (dBm) */ - __u8 noise; /* noise level (dBm) */ - __u8 updated; /* Flags to know if updated */ -}; - -/* - * Packet discarded in the wireless adapter due to - * "wireless" specific problems... - * Note : the list of counter and statistics in net_device_stats - * is already pretty exhaustive, and you should use that first. - * This is only additional stats... - */ -struct iw_discarded -{ - __u32 nwid; /* Rx : Wrong nwid/essid */ - __u32 code; /* Rx : Unable to code/decode (WEP) */ - __u32 fragment; /* Rx : Can't perform MAC reassembly */ - __u32 retries; /* Tx : Max MAC retries num reached */ - __u32 misc; /* Others cases */ -}; - -/* - * Packet/Time period missed in the wireless adapter due to - * "wireless" specific problems... - */ -struct iw_missed -{ - __u32 beacon; /* Missed beacons/superframe */ -}; - -/* - * Quality range (for spy threshold) - */ -struct iw_thrspy -{ - struct sockaddr addr; /* Source address (hw/mac) */ - struct iw_quality qual; /* Quality of the link */ - struct iw_quality low; /* Low threshold */ - struct iw_quality high; /* High threshold */ -}; - -/* - * Optional data for scan request - * - * Note: these optional parameters are controlling parameters for the - * scanning behavior, these do not apply to getting scan results - * (SIOCGIWSCAN). Drivers are expected to keep a local BSS table and - * provide a merged results with all BSSes even if the previous scan - * request limited scanning to a subset, e.g., by specifying an SSID. - * Especially, scan results are required to include an entry for the - * current BSS if the driver is in Managed mode and associated with an AP. - */ -struct iw_scan_req -{ - __u8 scan_type; /* IW_SCAN_TYPE_{ACTIVE,PASSIVE} */ - __u8 essid_len; - __u8 num_channels; /* num entries in channel_list; - * 0 = scan all allowed channels */ - __u8 flags; /* reserved as padding; use zero, this may - * be used in the future for adding flags - * to request different scan behavior */ - struct sockaddr bssid; /* ff:ff:ff:ff:ff:ff for broadcast BSSID or - * individual address of a specific BSS */ - - /* - * Use this ESSID if IW_SCAN_THIS_ESSID flag is used instead of using - * the current ESSID. This allows scan requests for specific ESSID - * without having to change the current ESSID and potentially breaking - * the current association. - */ - __u8 essid[IW_ESSID_MAX_SIZE]; - - /* - * Optional parameters for changing the default scanning behavior. - * These are based on the MLME-SCAN.request from IEEE Std 802.11. - * TU is 1.024 ms. If these are set to 0, driver is expected to use - * reasonable default values. min_channel_time defines the time that - * will be used to wait for the first reply on each channel. If no - * replies are received, next channel will be scanned after this. If - * replies are received, total time waited on the channel is defined by - * max_channel_time. - */ - __u32 min_channel_time; /* in TU */ - __u32 max_channel_time; /* in TU */ - - struct iw_freq channel_list[IW_MAX_FREQUENCIES]; -}; - -/* ------------------------- WPA SUPPORT ------------------------- */ - -/* - * Extended data structure for get/set encoding (this is used with - * SIOCSIWENCODEEXT/SIOCGIWENCODEEXT. struct iw_point and IW_ENCODE_* - * flags are used in the same way as with SIOCSIWENCODE/SIOCGIWENCODE and - * only the data contents changes (key data -> this structure, including - * key data). - * - * If the new key is the first group key, it will be set as the default - * TX key. Otherwise, default TX key index is only changed if - * IW_ENCODE_EXT_SET_TX_KEY flag is set. - * - * Key will be changed with SIOCSIWENCODEEXT in all cases except for - * special "change TX key index" operation which is indicated by setting - * key_len = 0 and ext_flags |= IW_ENCODE_EXT_SET_TX_KEY. - * - * tx_seq/rx_seq are only used when respective - * IW_ENCODE_EXT_{TX,RX}_SEQ_VALID flag is set in ext_flags. Normal - * TKIP/CCMP operation is to set RX seq with SIOCSIWENCODEEXT and start - * TX seq from zero whenever key is changed. SIOCGIWENCODEEXT is normally - * used only by an Authenticator (AP or an IBSS station) to get the - * current TX sequence number. Using TX_SEQ_VALID for SIOCSIWENCODEEXT and - * RX_SEQ_VALID for SIOCGIWENCODEEXT are optional, but can be useful for - * debugging/testing. - */ -struct iw_encode_ext -{ - __u32 ext_flags; /* IW_ENCODE_EXT_* */ - __u8 tx_seq[IW_ENCODE_SEQ_MAX_SIZE]; /* LSB first */ - __u8 rx_seq[IW_ENCODE_SEQ_MAX_SIZE]; /* LSB first */ - struct sockaddr addr; /* ff:ff:ff:ff:ff:ff for broadcast/multicast - * (group) keys or unicast address for - * individual keys */ - __u16 alg; /* IW_ENCODE_ALG_* */ - __u16 key_len; - __u8 key[0]; -}; - -/* SIOCSIWMLME data */ -struct iw_mlme -{ - __u16 cmd; /* IW_MLME_* */ - __u16 reason_code; - struct sockaddr addr; -}; - -/* SIOCSIWPMKSA data */ -#define IW_PMKSA_ADD 1 -#define IW_PMKSA_REMOVE 2 -#define IW_PMKSA_FLUSH 3 - -#define IW_PMKID_LEN 16 - -struct iw_pmksa -{ - __u32 cmd; /* IW_PMKSA_* */ - struct sockaddr bssid; - __u8 pmkid[IW_PMKID_LEN]; -}; - -/* IWEVMICHAELMICFAILURE data */ -struct iw_michaelmicfailure -{ - __u32 flags; - struct sockaddr src_addr; - __u8 tsc[IW_ENCODE_SEQ_MAX_SIZE]; /* LSB first */ -}; - -/* IWEVPMKIDCAND data */ -#define IW_PMKID_CAND_PREAUTH 0x00000001 /* RNS pre-authentication enabled */ -struct iw_pmkid_cand -{ - __u32 flags; /* IW_PMKID_CAND_* */ - __u32 index; /* the smaller the index, the higher the - * priority */ - struct sockaddr bssid; -}; - -/* ------------------------ WIRELESS STATS ------------------------ */ -/* - * Wireless statistics (used for /proc/net/wireless) - */ -struct iw_statistics -{ - __u16 status; /* Status - * - device dependent for now */ - - struct iw_quality qual; /* Quality of the link - * (instant/mean/max) */ - struct iw_discarded discard; /* Packet discarded counts */ - struct iw_missed miss; /* Packet missed counts */ -}; - -/* ------------------------ IOCTL REQUEST ------------------------ */ -/* - * This structure defines the payload of an ioctl, and is used - * below. - * - * Note that this structure should fit on the memory footprint - * of iwreq (which is the same as ifreq), which mean a max size of - * 16 octets = 128 bits. Warning, pointers might be 64 bits wide... - * You should check this when increasing the structures defined - * above in this file... - */ -union iwreq_data -{ - /* Config - generic */ - char name[IFNAMSIZ]; - /* Name : used to verify the presence of wireless extensions. - * Name of the protocol/provider... */ - - struct iw_point essid; /* Extended network name */ - struct iw_param nwid; /* network id (or domain - the cell) */ - struct iw_freq freq; /* frequency or channel : - * 0-1000 = channel - * > 1000 = frequency in Hz */ - - struct iw_param sens; /* signal level threshold */ - struct iw_param bitrate; /* default bit rate */ - struct iw_param txpower; /* default transmit power */ - struct iw_param rts; /* RTS threshold threshold */ - struct iw_param frag; /* Fragmentation threshold */ - __u32 mode; /* Operation mode */ - struct iw_param retry; /* Retry limits & lifetime */ - - struct iw_point encoding; /* Encoding stuff : tokens */ - struct iw_param power; /* PM duration/timeout */ - struct iw_quality qual; /* Quality part of statistics */ - - struct sockaddr ap_addr; /* Access point address */ - struct sockaddr addr; /* Destination address (hw/mac) */ - - struct iw_param param; /* Other small parameters */ - struct iw_point data; /* Other large parameters */ -}; - -/* - * The structure to exchange data for ioctl. - * This structure is the same as 'struct ifreq', but (re)defined for - * convenience... - * Do I need to remind you about structure size (32 octets) ? - */ -struct iwreq -{ - union - { - char ifrn_name[IFNAMSIZ]; /* if name, e.g. "eth0" */ - } ifr_ifrn; - - /* Data part (defined just above) */ - union iwreq_data u; -}; - -/* -------------------------- IOCTL DATA -------------------------- */ -/* - * For those ioctl which want to exchange mode data that what could - * fit in the above structure... - */ - -/* - * Range of parameters - */ - -struct iw_range -{ - /* Informative stuff (to choose between different interface) */ - __u32 throughput; /* To give an idea... */ - /* In theory this value should be the maximum benchmarked - * TCP/IP throughput, because with most of these devices the - * bit rate is meaningless (overhead an co) to estimate how - * fast the connection will go and pick the fastest one. - * I suggest people to play with Netperf or any benchmark... - */ - - /* NWID (or domain id) */ - __u32 min_nwid; /* Minimal NWID we are able to set */ - __u32 max_nwid; /* Maximal NWID we are able to set */ - - /* Old Frequency (backward compat - moved lower ) */ - __u16 old_num_channels; - __u8 old_num_frequency; - - /* Scan capabilities */ - __u8 scan_capa; /* IW_SCAN_CAPA_* bit field */ - - /* Wireless event capability bitmasks */ - __u32 event_capa[6]; - - /* signal level threshold range */ - __s32 sensitivity; - - /* Quality of link & SNR stuff */ - /* Quality range (link, level, noise) - * If the quality is absolute, it will be in the range [0 ; max_qual], - * if the quality is dBm, it will be in the range [max_qual ; 0]. - * Don't forget that we use 8 bit arithmetics... */ - struct iw_quality max_qual; /* Quality of the link */ - /* This should contain the average/typical values of the quality - * indicator. This should be the threshold between a "good" and - * a "bad" link (example : monitor going from green to orange). - * Currently, user space apps like quality monitors don't have any - * way to calibrate the measurement. With this, they can split - * the range between 0 and max_qual in different quality level - * (using a geometric subdivision centered on the average). - * I expect that people doing the user space apps will feedback - * us on which value we need to put in each driver... */ - struct iw_quality avg_qual; /* Quality of the link */ - - /* Rates */ - __u8 num_bitrates; /* Number of entries in the list */ - __s32 bitrate[IW_MAX_BITRATES]; /* list, in bps */ - - /* RTS threshold */ - __s32 min_rts; /* Minimal RTS threshold */ - __s32 max_rts; /* Maximal RTS threshold */ - - /* Frag threshold */ - __s32 min_frag; /* Minimal frag threshold */ - __s32 max_frag; /* Maximal frag threshold */ - - /* Power Management duration & timeout */ - __s32 min_pmp; /* Minimal PM period */ - __s32 max_pmp; /* Maximal PM period */ - __s32 min_pmt; /* Minimal PM timeout */ - __s32 max_pmt; /* Maximal PM timeout */ - __u16 pmp_flags; /* How to decode max/min PM period */ - __u16 pmt_flags; /* How to decode max/min PM timeout */ - __u16 pm_capa; /* What PM options are supported */ - - /* Encoder stuff */ - __u16 encoding_size[IW_MAX_ENCODING_SIZES]; /* Different token sizes */ - __u8 num_encoding_sizes; /* Number of entry in the list */ - __u8 max_encoding_tokens; /* Max number of tokens */ - /* For drivers that need a "login/passwd" form */ - __u8 encoding_login_index; /* token index for login token */ - - /* Transmit power */ - __u16 txpower_capa; /* What options are supported */ - __u8 num_txpower; /* Number of entries in the list */ - __s32 txpower[IW_MAX_TXPOWER]; /* list, in bps */ - - /* Wireless Extension version info */ - __u8 we_version_compiled; /* Must be WIRELESS_EXT */ - __u8 we_version_source; /* Last update of source */ - - /* Retry limits and lifetime */ - __u16 retry_capa; /* What retry options are supported */ - __u16 retry_flags; /* How to decode max/min retry limit */ - __u16 r_time_flags; /* How to decode max/min retry life */ - __s32 min_retry; /* Minimal number of retries */ - __s32 max_retry; /* Maximal number of retries */ - __s32 min_r_time; /* Minimal retry lifetime */ - __s32 max_r_time; /* Maximal retry lifetime */ - - /* Frequency */ - __u16 num_channels; /* Number of channels [0; num - 1] */ - __u8 num_frequency; /* Number of entry in the list */ - struct iw_freq freq[IW_MAX_FREQUENCIES]; /* list */ - /* Note : this frequency list doesn't need to fit channel numbers, - * because each entry contain its channel index */ - - __u32 enc_capa; /* IW_ENC_CAPA_* bit field */ -}; - -/* - * Private ioctl interface information - */ - -struct iw_priv_args -{ - __u32 cmd; /* Number of the ioctl to issue */ - __u16 set_args; /* Type and number of args */ - __u16 get_args; /* Type and number of args */ - char name[IFNAMSIZ]; /* Name of the extension */ -}; - -/* ----------------------- WIRELESS EVENTS ----------------------- */ -/* - * Wireless events are carried through the rtnetlink socket to user - * space. They are encapsulated in the IFLA_WIRELESS field of - * a RTM_NEWLINK message. - */ - -/* - * A Wireless Event. Contains basically the same data as the ioctl... - */ -struct iw_event -{ - __u16 len; /* Real length of this stuff */ - __u16 cmd; /* Wireless IOCTL */ - union iwreq_data u; /* IOCTL fixed payload */ -}; - -/* Size of the Event prefix (including padding and alignement junk) */ -#define IW_EV_LCP_LEN (sizeof(struct iw_event) - sizeof(union iwreq_data)) -/* Size of the various events */ -#define IW_EV_CHAR_LEN (IW_EV_LCP_LEN + IFNAMSIZ) -#define IW_EV_UINT_LEN (IW_EV_LCP_LEN + sizeof(__u32)) -#define IW_EV_FREQ_LEN (IW_EV_LCP_LEN + sizeof(struct iw_freq)) -#define IW_EV_PARAM_LEN (IW_EV_LCP_LEN + sizeof(struct iw_param)) -#define IW_EV_ADDR_LEN (IW_EV_LCP_LEN + sizeof(struct sockaddr)) -#define IW_EV_QUAL_LEN (IW_EV_LCP_LEN + sizeof(struct iw_quality)) - -/* iw_point events are special. First, the payload (extra data) come at - * the end of the event, so they are bigger than IW_EV_POINT_LEN. Second, - * we omit the pointer, so start at an offset. */ -#define IW_EV_POINT_OFF (((char *) &(((struct iw_point *) NULL)->length)) - \ - (char *) NULL) -#define IW_EV_POINT_LEN (IW_EV_LCP_LEN + sizeof(struct iw_point) - \ - IW_EV_POINT_OFF) - -#ifdef __KERNEL__ -#ifdef CONFIG_COMPAT -struct __compat_iw_event { - __u16 len; /* Real length of this stuff */ - __u16 cmd; /* Wireless IOCTL */ - compat_caddr_t pointer; -}; -#define IW_EV_COMPAT_LCP_LEN offsetof(struct __compat_iw_event, pointer) -#define IW_EV_COMPAT_POINT_OFF offsetof(struct compat_iw_point, length) - -/* Size of the various events for compat */ -#define IW_EV_COMPAT_CHAR_LEN (IW_EV_COMPAT_LCP_LEN + IFNAMSIZ) -#define IW_EV_COMPAT_UINT_LEN (IW_EV_COMPAT_LCP_LEN + sizeof(__u32)) -#define IW_EV_COMPAT_FREQ_LEN (IW_EV_COMPAT_LCP_LEN + sizeof(struct iw_freq)) -#define IW_EV_COMPAT_PARAM_LEN (IW_EV_COMPAT_LCP_LEN + sizeof(struct iw_param)) -#define IW_EV_COMPAT_ADDR_LEN (IW_EV_COMPAT_LCP_LEN + sizeof(struct sockaddr)) -#define IW_EV_COMPAT_QUAL_LEN (IW_EV_COMPAT_LCP_LEN + sizeof(struct iw_quality)) -#define IW_EV_COMPAT_POINT_LEN \ - (IW_EV_COMPAT_LCP_LEN + sizeof(struct compat_iw_point) - \ - IW_EV_COMPAT_POINT_OFF) -#endif -#endif - -/* Size of the Event prefix when packed in stream */ -#define IW_EV_LCP_PK_LEN (4) -/* Size of the various events when packed in stream */ -#define IW_EV_CHAR_PK_LEN (IW_EV_LCP_PK_LEN + IFNAMSIZ) -#define IW_EV_UINT_PK_LEN (IW_EV_LCP_PK_LEN + sizeof(__u32)) -#define IW_EV_FREQ_PK_LEN (IW_EV_LCP_PK_LEN + sizeof(struct iw_freq)) -#define IW_EV_PARAM_PK_LEN (IW_EV_LCP_PK_LEN + sizeof(struct iw_param)) -#define IW_EV_ADDR_PK_LEN (IW_EV_LCP_PK_LEN + sizeof(struct sockaddr)) -#define IW_EV_QUAL_PK_LEN (IW_EV_LCP_PK_LEN + sizeof(struct iw_quality)) -#define IW_EV_POINT_PK_LEN (IW_EV_LCP_PK_LEN + 4) - -#endif /* _LINUX_WIRELESS_H */ diff -Nru wpa-1.0/src/eap_common/chap.c wpa-2.1/src/eap_common/chap.c --- wpa-1.0/src/eap_common/chap.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/eap_common/chap.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * CHAP-MD5 (RFC 1994) * Copyright (c) 2007-2009, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff -Nru wpa-1.0/src/eap_common/chap.h wpa-2.1/src/eap_common/chap.h --- wpa-1.0/src/eap_common/chap.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/eap_common/chap.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * CHAP-MD5 (RFC 1994) * Copyright (c) 2007-2009, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef CHAP_H diff -Nru wpa-1.0/src/eap_common/eap_common.c wpa-2.1/src/eap_common/eap_common.c --- wpa-1.0/src/eap_common/eap_common.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/eap_common/eap_common.c 2014-02-04 11:23:35.000000000 +0000 @@ -1,15 +1,9 @@ /* * EAP common peer/server definitions - * Copyright (c) 2004-2007, Jouni Malinen + * Copyright (c) 2004-2012, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -19,6 +13,41 @@ #include "eap_common.h" /** + * eap_hdr_len_valid - Validate EAP header length field + * @msg: EAP frame (starting with EAP header) + * @min_payload: Minimum payload length needed + * Returns: 1 for valid header, 0 for invalid + * + * This is a helper function that does minimal validation of EAP messages. The + * length field is verified to be large enough to include the header and not + * too large to go beyond the end of the buffer. + */ +int eap_hdr_len_valid(const struct wpabuf *msg, size_t min_payload) +{ + const struct eap_hdr *hdr; + size_t len; + + if (msg == NULL) + return 0; + + hdr = wpabuf_head(msg); + + if (wpabuf_len(msg) < sizeof(*hdr)) { + wpa_printf(MSG_INFO, "EAP: Too short EAP frame"); + return 0; + } + + len = be_to_host16(hdr->length); + if (len < sizeof(*hdr) + min_payload || len > wpabuf_len(msg)) { + wpa_printf(MSG_INFO, "EAP: Invalid EAP length"); + return 0; + } + + return 1; +} + + +/** * eap_hdr_validate - Validate EAP header * @vendor: Expected EAP Vendor-Id (0 = IETF) * @eap_type: Expected EAP type number @@ -41,19 +70,11 @@ const u8 *pos; size_t len; - hdr = wpabuf_head(msg); - - if (wpabuf_len(msg) < sizeof(*hdr)) { - wpa_printf(MSG_INFO, "EAP: Too short EAP frame"); + if (!eap_hdr_len_valid(msg, 1)) return NULL; - } + hdr = wpabuf_head(msg); len = be_to_host16(hdr->length); - if (len < sizeof(*hdr) + 1 || len > wpabuf_len(msg)) { - wpa_printf(MSG_INFO, "EAP: Invalid EAP length"); - return NULL; - } - pos = (const u8 *) (hdr + 1); if (*pos == EAP_TYPE_EXPANDED) { diff -Nru wpa-1.0/src/eap_common/eap_common.h wpa-2.1/src/eap_common/eap_common.h --- wpa-1.0/src/eap_common/eap_common.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/eap_common/eap_common.h 2014-02-04 11:23:35.000000000 +0000 @@ -1,15 +1,9 @@ /* * EAP common peer/server definitions - * Copyright (c) 2004-2007, Jouni Malinen + * Copyright (c) 2004-2012, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef EAP_COMMON_H @@ -17,6 +11,7 @@ #include "wpabuf.h" +int eap_hdr_len_valid(const struct wpabuf *msg, size_t min_payload); const u8 * eap_hdr_validate(int vendor, EapType eap_type, const struct wpabuf *msg, size_t *plen); struct wpabuf * eap_msg_alloc(int vendor, EapType type, size_t payload_len, diff -Nru wpa-1.0/src/eap_common/eap_defs.h wpa-2.1/src/eap_common/eap_defs.h --- wpa-1.0/src/eap_common/eap_defs.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/eap_common/eap_defs.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * EAP server/peer: Shared EAP definitions * Copyright (c) 2004-2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef EAP_DEFS_H @@ -66,9 +60,10 @@ EAP_TYPE_PSK = 47 /* RFC 4764 */, EAP_TYPE_SAKE = 48 /* RFC 4763 */, EAP_TYPE_IKEV2 = 49 /* RFC 5106 */, - EAP_TYPE_AKA_PRIME = 50 /* draft-arkko-eap-aka-kdf-10.txt */, + EAP_TYPE_AKA_PRIME = 50 /* RFC 5448 */, EAP_TYPE_GPSK = 51 /* RFC 5433 */, EAP_TYPE_PWD = 52 /* RFC 5931 */, + EAP_TYPE_EKE = 53 /* RFC 6124 */, EAP_TYPE_EXPANDED = 254 /* RFC 3748 */ } EapType; @@ -77,9 +72,13 @@ enum { EAP_VENDOR_IETF = 0, EAP_VENDOR_MICROSOFT = 0x000137 /* Microsoft */, - EAP_VENDOR_WFA = 0x00372A /* Wi-Fi Alliance */ + EAP_VENDOR_WFA = 0x00372A /* Wi-Fi Alliance */, + EAP_VENDOR_HOSTAP = 39068 /* hostapd/wpa_supplicant project */ }; +#define EAP_VENDOR_UNAUTH_TLS EAP_VENDOR_HOSTAP +#define EAP_VENDOR_TYPE_UNAUTH_TLS 1 + #define EAP_MSK_LEN 64 #define EAP_EMSK_LEN 64 diff -Nru wpa-1.0/src/eap_common/eap_eke_common.c wpa-2.1/src/eap_common/eap_eke_common.c --- wpa-1.0/src/eap_common/eap_eke_common.c 1970-01-01 00:00:00.000000000 +0000 +++ wpa-2.1/src/eap_common/eap_eke_common.c 2014-02-04 11:23:35.000000000 +0000 @@ -0,0 +1,768 @@ +/* + * EAP server/peer: EAP-EKE shared routines + * Copyright (c) 2011-2013, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "crypto/aes.h" +#include "crypto/aes_wrap.h" +#include "crypto/crypto.h" +#include "crypto/dh_groups.h" +#include "crypto/random.h" +#include "crypto/sha1.h" +#include "crypto/sha256.h" +#include "eap_common/eap_defs.h" +#include "eap_eke_common.h" + + +static int eap_eke_dh_len(u8 group) +{ + switch (group) { + case EAP_EKE_DHGROUP_EKE_2: + return 128; + case EAP_EKE_DHGROUP_EKE_5: + return 192; + case EAP_EKE_DHGROUP_EKE_14: + return 256; + case EAP_EKE_DHGROUP_EKE_15: + return 384; + case EAP_EKE_DHGROUP_EKE_16: + return 512; + } + + return -1; +} + + +static int eap_eke_dhcomp_len(u8 dhgroup, u8 encr) +{ + int dhlen; + + dhlen = eap_eke_dh_len(dhgroup); + if (dhlen < 0) + return -1; + if (encr != EAP_EKE_ENCR_AES128_CBC) + return -1; + return AES_BLOCK_SIZE + dhlen; +} + + +static const struct dh_group * eap_eke_dh_group(u8 group) +{ + switch (group) { + case EAP_EKE_DHGROUP_EKE_2: + return dh_groups_get(2); + case EAP_EKE_DHGROUP_EKE_5: + return dh_groups_get(5); + case EAP_EKE_DHGROUP_EKE_14: + return dh_groups_get(14); + case EAP_EKE_DHGROUP_EKE_15: + return dh_groups_get(15); + case EAP_EKE_DHGROUP_EKE_16: + return dh_groups_get(16); + } + + return NULL; +} + + +static int eap_eke_dh_generator(u8 group) +{ + switch (group) { + case EAP_EKE_DHGROUP_EKE_2: + return 5; + case EAP_EKE_DHGROUP_EKE_5: + return 31; + case EAP_EKE_DHGROUP_EKE_14: + return 11; + case EAP_EKE_DHGROUP_EKE_15: + return 5; + case EAP_EKE_DHGROUP_EKE_16: + return 5; + } + + return -1; +} + + +static int eap_eke_pnonce_len(u8 mac) +{ + int mac_len; + + if (mac == EAP_EKE_MAC_HMAC_SHA1) + mac_len = SHA1_MAC_LEN; + else if (mac == EAP_EKE_MAC_HMAC_SHA2_256) + mac_len = SHA256_MAC_LEN; + else + return -1; + + return AES_BLOCK_SIZE + 16 + mac_len; +} + + +static int eap_eke_pnonce_ps_len(u8 mac) +{ + int mac_len; + + if (mac == EAP_EKE_MAC_HMAC_SHA1) + mac_len = SHA1_MAC_LEN; + else if (mac == EAP_EKE_MAC_HMAC_SHA2_256) + mac_len = SHA256_MAC_LEN; + else + return -1; + + return AES_BLOCK_SIZE + 2 * 16 + mac_len; +} + + +static int eap_eke_prf_len(u8 prf) +{ + if (prf == EAP_EKE_PRF_HMAC_SHA1) + return 20; + if (prf == EAP_EKE_PRF_HMAC_SHA2_256) + return 32; + return -1; +} + + +static int eap_eke_nonce_len(u8 prf) +{ + int prf_len; + + prf_len = eap_eke_prf_len(prf); + if (prf_len < 0) + return -1; + + if (prf_len > 2 * 16) + return (prf_len + 1) / 2; + + return 16; +} + + +static int eap_eke_auth_len(u8 prf) +{ + switch (prf) { + case EAP_EKE_PRF_HMAC_SHA1: + return SHA1_MAC_LEN; + case EAP_EKE_PRF_HMAC_SHA2_256: + return SHA256_MAC_LEN; + } + + return -1; +} + + +int eap_eke_dh_init(u8 group, u8 *ret_priv, u8 *ret_pub) +{ + int generator; + u8 gen; + const struct dh_group *dh; + size_t pub_len, i; + + generator = eap_eke_dh_generator(group); + if (generator < 0 || generator > 255) + return -1; + gen = generator; + + dh = eap_eke_dh_group(group); + if (dh == NULL) + return -1; + + /* x = random number 2 .. p-1 */ + if (random_get_bytes(ret_priv, dh->prime_len)) + return -1; + if (os_memcmp(ret_priv, dh->prime, dh->prime_len) > 0) { + /* Make sure private value is smaller than prime */ + ret_priv[0] = 0; + } + for (i = 0; i < dh->prime_len - 1; i++) { + if (ret_priv[i]) + break; + } + if (i == dh->prime_len - 1 && (ret_priv[i] == 0 || ret_priv[i] == 1)) + return -1; + wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: DH private value", + ret_priv, dh->prime_len); + + /* y = g ^ x (mod p) */ + pub_len = dh->prime_len; + if (crypto_mod_exp(&gen, 1, ret_priv, dh->prime_len, + dh->prime, dh->prime_len, ret_pub, &pub_len) < 0) + return -1; + if (pub_len < dh->prime_len) { + size_t pad = dh->prime_len - pub_len; + os_memmove(ret_pub + pad, ret_pub, pub_len); + os_memset(ret_pub, 0, pad); + } + + wpa_hexdump(MSG_DEBUG, "EAP-EKE: DH public value", + ret_pub, dh->prime_len); + + return 0; +} + + +static int eap_eke_prf(u8 prf, const u8 *key, size_t key_len, const u8 *data, + size_t data_len, const u8 *data2, size_t data2_len, + u8 *res) +{ + const u8 *addr[2]; + size_t len[2]; + size_t num_elem = 1; + + addr[0] = data; + len[0] = data_len; + if (data2) { + num_elem++; + addr[1] = data2; + len[1] = data2_len; + } + + if (prf == EAP_EKE_PRF_HMAC_SHA1) + return hmac_sha1_vector(key, key_len, num_elem, addr, len, res); + if (prf == EAP_EKE_PRF_HMAC_SHA2_256) + return hmac_sha256_vector(key, key_len, num_elem, addr, len, + res); + return -1; +} + + +static int eap_eke_prf_hmac_sha1(const u8 *key, size_t key_len, const u8 *data, + size_t data_len, u8 *res, size_t len) +{ + u8 hash[SHA1_MAC_LEN]; + u8 idx; + const u8 *addr[3]; + size_t vlen[3]; + int ret; + + idx = 0; + addr[0] = hash; + vlen[0] = SHA1_MAC_LEN; + addr[1] = data; + vlen[1] = data_len; + addr[2] = &idx; + vlen[2] = 1; + + while (len > 0) { + idx++; + if (idx == 1) + ret = hmac_sha1_vector(key, key_len, 2, &addr[1], + &vlen[1], hash); + else + ret = hmac_sha1_vector(key, key_len, 3, addr, vlen, + hash); + if (ret < 0) + return -1; + if (len > SHA1_MAC_LEN) { + os_memcpy(res, hash, SHA1_MAC_LEN); + res += SHA1_MAC_LEN; + len -= SHA1_MAC_LEN; + } else { + os_memcpy(res, hash, len); + len = 0; + } + } + + return 0; +} + + +static int eap_eke_prf_hmac_sha256(const u8 *key, size_t key_len, const u8 *data, + size_t data_len, u8 *res, size_t len) +{ + u8 hash[SHA256_MAC_LEN]; + u8 idx; + const u8 *addr[3]; + size_t vlen[3]; + int ret; + + idx = 0; + addr[0] = hash; + vlen[0] = SHA256_MAC_LEN; + addr[1] = data; + vlen[1] = data_len; + addr[2] = &idx; + vlen[2] = 1; + + while (len > 0) { + idx++; + if (idx == 1) + ret = hmac_sha256_vector(key, key_len, 2, &addr[1], + &vlen[1], hash); + else + ret = hmac_sha256_vector(key, key_len, 3, addr, vlen, + hash); + if (ret < 0) + return -1; + if (len > SHA256_MAC_LEN) { + os_memcpy(res, hash, SHA256_MAC_LEN); + res += SHA256_MAC_LEN; + len -= SHA256_MAC_LEN; + } else { + os_memcpy(res, hash, len); + len = 0; + } + } + + return 0; +} + + +static int eap_eke_prfplus(u8 prf, const u8 *key, size_t key_len, + const u8 *data, size_t data_len, u8 *res, size_t len) +{ + if (prf == EAP_EKE_PRF_HMAC_SHA1) + return eap_eke_prf_hmac_sha1(key, key_len, data, data_len, res, + len); + if (prf == EAP_EKE_PRF_HMAC_SHA2_256) + return eap_eke_prf_hmac_sha256(key, key_len, data, data_len, + res, len); + return -1; +} + + +int eap_eke_derive_key(struct eap_eke_session *sess, + const u8 *password, size_t password_len, + const u8 *id_s, size_t id_s_len, const u8 *id_p, + size_t id_p_len, u8 *key) +{ + u8 zeros[EAP_EKE_MAX_HASH_LEN]; + u8 temp[EAP_EKE_MAX_HASH_LEN]; + size_t key_len = 16; /* Only AES-128-CBC is used here */ + u8 *id; + + /* temp = prf(0+, password) */ + os_memset(zeros, 0, sess->prf_len); + if (eap_eke_prf(sess->prf, zeros, sess->prf_len, + password, password_len, NULL, 0, temp) < 0) + return -1; + wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: temp = prf(0+, password)", + temp, sess->prf_len); + + /* key = prf+(temp, ID_S | ID_P) */ + id = os_malloc(id_s_len + id_p_len); + if (id == NULL) + return -1; + os_memcpy(id, id_s, id_s_len); + os_memcpy(id + id_s_len, id_p, id_p_len); + wpa_hexdump_ascii(MSG_DEBUG, "EAP-EKE: ID_S | ID_P", + id, id_s_len + id_p_len); + if (eap_eke_prfplus(sess->prf, temp, sess->prf_len, + id, id_s_len + id_p_len, key, key_len) < 0) { + os_free(id); + return -1; + } + os_free(id); + wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: key = prf+(temp, ID_S | ID_P)", + key, key_len); + + return 0; +} + + +int eap_eke_dhcomp(struct eap_eke_session *sess, const u8 *key, const u8 *dhpub, + u8 *ret_dhcomp) +{ + u8 pub[EAP_EKE_MAX_DH_LEN]; + int dh_len; + u8 iv[AES_BLOCK_SIZE]; + + dh_len = eap_eke_dh_len(sess->dhgroup); + if (dh_len < 0) + return -1; + + /* + * DHComponent = Encr(key, y) + * + * All defined DH groups use primes that have length devisible by 16, so + * no need to do extra padding for y (= pub). + */ + if (sess->encr != EAP_EKE_ENCR_AES128_CBC) + return -1; + if (random_get_bytes(iv, AES_BLOCK_SIZE)) + return -1; + wpa_hexdump(MSG_DEBUG, "EAP-EKE: IV for Encr(key, y)", + iv, AES_BLOCK_SIZE); + os_memcpy(pub, dhpub, dh_len); + if (aes_128_cbc_encrypt(key, iv, pub, dh_len) < 0) + return -1; + os_memcpy(ret_dhcomp, iv, AES_BLOCK_SIZE); + os_memcpy(ret_dhcomp + AES_BLOCK_SIZE, pub, dh_len); + wpa_hexdump(MSG_DEBUG, "EAP-EKE: DHComponent = Encr(key, y)", + ret_dhcomp, AES_BLOCK_SIZE + dh_len); + + return 0; +} + + +int eap_eke_shared_secret(struct eap_eke_session *sess, const u8 *key, + const u8 *dhpriv, const u8 *peer_dhcomp) +{ + u8 zeros[EAP_EKE_MAX_HASH_LEN]; + u8 peer_pub[EAP_EKE_MAX_DH_LEN]; + u8 modexp[EAP_EKE_MAX_DH_LEN]; + size_t len; + const struct dh_group *dh; + + if (sess->encr != EAP_EKE_ENCR_AES128_CBC) + return -1; + + dh = eap_eke_dh_group(sess->dhgroup); + if (dh == NULL) + return -1; + + /* Decrypt peer DHComponent */ + os_memcpy(peer_pub, peer_dhcomp + AES_BLOCK_SIZE, dh->prime_len); + if (aes_128_cbc_decrypt(key, peer_dhcomp, peer_pub, dh->prime_len) < 0) { + wpa_printf(MSG_INFO, "EAP-EKE: Failed to decrypt DHComponent"); + return -1; + } + wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: Decrypted peer DH pubkey", + peer_pub, dh->prime_len); + + /* SharedSecret = prf(0+, g ^ (x_s * x_p) (mod p)) */ + len = dh->prime_len; + if (crypto_mod_exp(peer_pub, dh->prime_len, dhpriv, dh->prime_len, + dh->prime, dh->prime_len, modexp, &len) < 0) + return -1; + if (len < dh->prime_len) { + size_t pad = dh->prime_len - len; + os_memmove(modexp + pad, modexp, len); + os_memset(modexp, 0, pad); + } + + os_memset(zeros, 0, sess->auth_len); + if (eap_eke_prf(sess->prf, zeros, sess->auth_len, modexp, dh->prime_len, + NULL, 0, sess->shared_secret) < 0) + return -1; + wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: SharedSecret", + sess->shared_secret, sess->auth_len); + + return 0; +} + + +int eap_eke_derive_ke_ki(struct eap_eke_session *sess, + const u8 *id_s, size_t id_s_len, + const u8 *id_p, size_t id_p_len) +{ + u8 buf[EAP_EKE_MAX_KE_LEN + EAP_EKE_MAX_KI_LEN]; + size_t ke_len, ki_len; + u8 *data; + size_t data_len; + const char *label = "EAP-EKE Keys"; + size_t label_len; + + /* + * Ke | Ki = prf+(SharedSecret, "EAP-EKE Keys" | ID_S | ID_P) + * Ke = encryption key + * Ki = integrity protection key + * Length of each key depends on the selected algorithms. + */ + + if (sess->encr == EAP_EKE_ENCR_AES128_CBC) + ke_len = 16; + else + return -1; + + if (sess->mac == EAP_EKE_PRF_HMAC_SHA1) + ki_len = 20; + else if (sess->mac == EAP_EKE_PRF_HMAC_SHA2_256) + ki_len = 32; + else + return -1; + + label_len = os_strlen(label); + data_len = label_len + id_s_len + id_p_len; + data = os_malloc(data_len); + if (data == NULL) + return -1; + os_memcpy(data, label, label_len); + os_memcpy(data + label_len, id_s, id_s_len); + os_memcpy(data + label_len + id_s_len, id_p, id_p_len); + if (eap_eke_prfplus(sess->prf, sess->shared_secret, sess->prf_len, + data, data_len, buf, ke_len + ki_len) < 0) { + os_free(data); + return -1; + } + + os_memcpy(sess->ke, buf, ke_len); + wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: Ke", sess->ke, ke_len); + os_memcpy(sess->ki, buf + ke_len, ki_len); + wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: Ki", sess->ki, ki_len); + + os_free(data); + return 0; +} + + +int eap_eke_derive_ka(struct eap_eke_session *sess, + const u8 *id_s, size_t id_s_len, + const u8 *id_p, size_t id_p_len, + const u8 *nonce_p, const u8 *nonce_s) +{ + u8 *data, *pos; + size_t data_len; + const char *label = "EAP-EKE Ka"; + size_t label_len; + + /* + * Ka = prf+(SharedSecret, "EAP-EKE Ka" | ID_S | ID_P | Nonce_P | + * Nonce_S) + * Ka = authentication key + * Length of the key depends on the selected algorithms. + */ + + label_len = os_strlen(label); + data_len = label_len + id_s_len + id_p_len + 2 * sess->nonce_len; + data = os_malloc(data_len); + if (data == NULL) + return -1; + pos = data; + os_memcpy(pos, label, label_len); + pos += label_len; + os_memcpy(pos, id_s, id_s_len); + pos += id_s_len; + os_memcpy(pos, id_p, id_p_len); + pos += id_p_len; + os_memcpy(pos, nonce_p, sess->nonce_len); + pos += sess->nonce_len; + os_memcpy(pos, nonce_s, sess->nonce_len); + if (eap_eke_prfplus(sess->prf, sess->shared_secret, sess->prf_len, + data, data_len, sess->ka, sess->prf_len) < 0) { + os_free(data); + return -1; + } + os_free(data); + + wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: Ka", sess->ka, sess->prf_len); + + return 0; +} + + +int eap_eke_derive_msk(struct eap_eke_session *sess, + const u8 *id_s, size_t id_s_len, + const u8 *id_p, size_t id_p_len, + const u8 *nonce_p, const u8 *nonce_s, + u8 *msk, u8 *emsk) +{ + u8 *data, *pos; + size_t data_len; + const char *label = "EAP-EKE Exported Keys"; + size_t label_len; + u8 buf[EAP_MSK_LEN + EAP_EMSK_LEN]; + + /* + * MSK | EMSK = prf+(SharedSecret, "EAP-EKE Exported Keys" | ID_S | + * ID_P | Nonce_P | Nonce_S) + */ + + label_len = os_strlen(label); + data_len = label_len + id_s_len + id_p_len + 2 * sess->nonce_len; + data = os_malloc(data_len); + if (data == NULL) + return -1; + pos = data; + os_memcpy(pos, label, label_len); + pos += label_len; + os_memcpy(pos, id_s, id_s_len); + pos += id_s_len; + os_memcpy(pos, id_p, id_p_len); + pos += id_p_len; + os_memcpy(pos, nonce_p, sess->nonce_len); + pos += sess->nonce_len; + os_memcpy(pos, nonce_s, sess->nonce_len); + if (eap_eke_prfplus(sess->prf, sess->shared_secret, sess->prf_len, + data, data_len, buf, EAP_MSK_LEN + EAP_EMSK_LEN) < + 0) { + os_free(data); + return -1; + } + os_free(data); + + os_memcpy(msk, buf, EAP_MSK_LEN); + os_memcpy(emsk, buf + EAP_MSK_LEN, EAP_EMSK_LEN); + os_memset(buf, 0, sizeof(buf)); + + wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: MSK", msk, EAP_MSK_LEN); + wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: EMSK", msk, EAP_EMSK_LEN); + + return 0; +} + + +static int eap_eke_mac(u8 mac, const u8 *key, const u8 *data, size_t data_len, + u8 *res) +{ + if (mac == EAP_EKE_MAC_HMAC_SHA1) + return hmac_sha1(key, SHA1_MAC_LEN, data, data_len, res); + if (mac == EAP_EKE_MAC_HMAC_SHA2_256) + return hmac_sha256(key, SHA256_MAC_LEN, data, data_len, res); + return -1; +} + + +int eap_eke_prot(struct eap_eke_session *sess, + const u8 *data, size_t data_len, + u8 *prot, size_t *prot_len) +{ + size_t block_size, icv_len, pad; + u8 *pos, *iv, *e; + + if (sess->encr == EAP_EKE_ENCR_AES128_CBC) + block_size = AES_BLOCK_SIZE; + else + return -1; + + if (sess->mac == EAP_EKE_PRF_HMAC_SHA1) + icv_len = SHA1_MAC_LEN; + else if (sess->mac == EAP_EKE_PRF_HMAC_SHA2_256) + icv_len = SHA256_MAC_LEN; + else + return -1; + + pad = data_len % block_size; + if (pad) + pad = block_size - pad; + + if (*prot_len < block_size + data_len + pad + icv_len) { + wpa_printf(MSG_INFO, "EAP-EKE: Not enough room for Prot() data"); + } + pos = prot; + + if (random_get_bytes(pos, block_size)) + return -1; + iv = pos; + wpa_hexdump(MSG_DEBUG, "EAP-EKE: IV for Prot()", iv, block_size); + pos += block_size; + + e = pos; + os_memcpy(pos, data, data_len); + pos += data_len; + if (pad) { + if (random_get_bytes(pos, pad)) + return -1; + pos += pad; + } + + if (aes_128_cbc_encrypt(sess->ke, iv, e, data_len + pad) < 0) + return -1; + + if (eap_eke_mac(sess->mac, sess->ki, e, data_len + pad, pos) < 0) + return -1; + pos += icv_len; + + *prot_len = pos - prot; + return 0; +} + + +int eap_eke_decrypt_prot(struct eap_eke_session *sess, + const u8 *prot, size_t prot_len, + u8 *data, size_t *data_len) +{ + size_t block_size, icv_len; + u8 icv[EAP_EKE_MAX_HASH_LEN]; + + if (sess->encr == EAP_EKE_ENCR_AES128_CBC) + block_size = AES_BLOCK_SIZE; + else + return -1; + + if (sess->mac == EAP_EKE_PRF_HMAC_SHA1) + icv_len = SHA1_MAC_LEN; + else if (sess->mac == EAP_EKE_PRF_HMAC_SHA2_256) + icv_len = SHA256_MAC_LEN; + else + return -1; + + if (prot_len < 2 * block_size + icv_len) + return -1; + if ((prot_len - icv_len) % block_size) + return -1; + + if (eap_eke_mac(sess->mac, sess->ki, prot + block_size, + prot_len - block_size - icv_len, icv) < 0) + return -1; + if (os_memcmp(icv, prot + prot_len - icv_len, icv_len) != 0) { + wpa_printf(MSG_INFO, "EAP-EKE: ICV mismatch in Prot() data"); + return -1; + } + + if (*data_len < prot_len - block_size - icv_len) { + wpa_printf(MSG_INFO, "EAP-EKE: Not enough room for decrypted Prot() data"); + return -1; + } + + *data_len = prot_len - block_size - icv_len; + os_memcpy(data, prot + block_size, *data_len); + if (aes_128_cbc_decrypt(sess->ke, prot, data, *data_len) < 0) { + wpa_printf(MSG_INFO, "EAP-EKE: Failed to decrypt Prot() data"); + return -1; + } + wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: Decrypted Prot() data", + data, *data_len); + + return 0; +} + + +int eap_eke_auth(struct eap_eke_session *sess, const char *label, + const struct wpabuf *msgs, u8 *auth) +{ + wpa_printf(MSG_DEBUG, "EAP-EKE: Auth(%s)", label); + wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: Ka for Auth", + sess->ka, sess->auth_len); + wpa_hexdump_buf(MSG_MSGDUMP, "EAP-EKE: Messages for Auth", msgs); + return eap_eke_prf(sess->prf, sess->ka, sess->auth_len, + (const u8 *) label, os_strlen(label), + wpabuf_head(msgs), wpabuf_len(msgs), auth); +} + + +int eap_eke_session_init(struct eap_eke_session *sess, u8 dhgroup, u8 encr, + u8 prf, u8 mac) +{ + sess->dhgroup = dhgroup; + sess->encr = encr; + sess->prf = prf; + sess->mac = mac; + + sess->prf_len = eap_eke_prf_len(prf); + if (sess->prf_len < 0) + return -1; + sess->nonce_len = eap_eke_nonce_len(prf); + if (sess->nonce_len < 0) + return -1; + sess->auth_len = eap_eke_auth_len(prf); + if (sess->auth_len < 0) + return -1; + sess->dhcomp_len = eap_eke_dhcomp_len(sess->dhgroup, sess->encr); + if (sess->dhcomp_len < 0) + return -1; + sess->pnonce_len = eap_eke_pnonce_len(sess->mac); + if (sess->pnonce_len < 0) + return -1; + sess->pnonce_ps_len = eap_eke_pnonce_ps_len(sess->mac); + if (sess->pnonce_ps_len < 0) + return -1; + + return 0; +} + + +void eap_eke_session_clean(struct eap_eke_session *sess) +{ + os_memset(sess->shared_secret, 0, EAP_EKE_MAX_HASH_LEN); + os_memset(sess->ke, 0, EAP_EKE_MAX_KE_LEN); + os_memset(sess->ki, 0, EAP_EKE_MAX_KI_LEN); + os_memset(sess->ka, 0, EAP_EKE_MAX_KA_LEN); +} diff -Nru wpa-1.0/src/eap_common/eap_eke_common.h wpa-2.1/src/eap_common/eap_eke_common.h --- wpa-1.0/src/eap_common/eap_eke_common.h 1970-01-01 00:00:00.000000000 +0000 +++ wpa-2.1/src/eap_common/eap_eke_common.h 2014-02-04 11:23:35.000000000 +0000 @@ -0,0 +1,114 @@ +/* + * EAP server/peer: EAP-EKE shared routines + * Copyright (c) 2011-2013, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef EAP_EKE_COMMON_H +#define EAP_EKE_COMMON_H + +/* EKE Exchange */ +#define EAP_EKE_ID 1 +#define EAP_EKE_COMMIT 2 +#define EAP_EKE_CONFIRM 3 +#define EAP_EKE_FAILURE 4 + +/* Diffie-Hellman Group Registry */ +#define EAP_EKE_DHGROUP_EKE_2 1 +#define EAP_EKE_DHGROUP_EKE_5 2 +#define EAP_EKE_DHGROUP_EKE_14 3 /* mandatory to implement */ +#define EAP_EKE_DHGROUP_EKE_15 4 +#define EAP_EKE_DHGROUP_EKE_16 5 + +/* Encryption Algorithm Registry */ +#define EAP_EKE_ENCR_AES128_CBC 1 /* mandatory to implement */ + +/* Pseudo Random Function Registry */ +#define EAP_EKE_PRF_HMAC_SHA1 1 /* mandatory to implement */ +#define EAP_EKE_PRF_HMAC_SHA2_256 2 + +/* Keyed Message Digest (MAC) Registry */ +#define EAP_EKE_MAC_HMAC_SHA1 1 /* mandatory to implement */ +#define EAP_EKE_MAC_HMAC_SHA2_256 2 + +/* Identity Type Registry */ +#define EAP_EKE_ID_OPAQUE 1 +#define EAP_EKE_ID_NAI 2 +#define EAP_EKE_ID_IPv4 3 +#define EAP_EKE_ID_IPv6 4 +#define EAP_EKE_ID_FQDN 5 +#define EAP_EKE_ID_DN 6 + +/* Failure-Code */ +#define EAP_EKE_FAIL_NO_ERROR 1 +#define EAP_EKE_FAIL_PROTO_ERROR 2 +#define EAP_EKE_FAIL_PASSWD_NOT_FOUND 3 +#define EAP_EKE_FAIL_AUTHENTICATION_FAIL 4 +#define EAP_EKE_FAIL_AUTHORIZATION_FAIL 5 +#define EAP_EKE_FAIL_NO_PROPOSAL_CHOSEN 6 +#define EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR 0xffffffff + +#define EAP_EKE_MAX_DH_LEN 512 +#define EAP_EKE_MAX_HASH_LEN 32 +#define EAP_EKE_MAX_KEY_LEN 16 +#define EAP_EKE_MAX_KE_LEN 16 +#define EAP_EKE_MAX_KI_LEN 32 +#define EAP_EKE_MAX_KA_LEN 32 +#define EAP_EKE_MAX_NONCE_LEN 16 + +struct eap_eke_session { + /* Selected proposal */ + u8 dhgroup; + u8 encr; + u8 prf; + u8 mac; + + u8 shared_secret[EAP_EKE_MAX_HASH_LEN]; + u8 ke[EAP_EKE_MAX_KE_LEN]; + u8 ki[EAP_EKE_MAX_KI_LEN]; + u8 ka[EAP_EKE_MAX_KA_LEN]; + + int prf_len; + int nonce_len; + int auth_len; + int dhcomp_len; + int pnonce_len; + int pnonce_ps_len; +}; + +int eap_eke_session_init(struct eap_eke_session *sess, u8 dhgroup, u8 encr, + u8 prf, u8 mac); +void eap_eke_session_clean(struct eap_eke_session *sess); +int eap_eke_dh_init(u8 group, u8 *ret_priv, u8 *ret_pub); +int eap_eke_derive_key(struct eap_eke_session *sess, + const u8 *password, size_t password_len, + const u8 *id_s, size_t id_s_len, const u8 *id_p, + size_t id_p_len, u8 *key); +int eap_eke_dhcomp(struct eap_eke_session *sess, const u8 *key, const u8 *dhpub, + u8 *ret_dhcomp); +int eap_eke_shared_secret(struct eap_eke_session *sess, const u8 *key, + const u8 *dhpriv, const u8 *peer_dhcomp); +int eap_eke_derive_ke_ki(struct eap_eke_session *sess, + const u8 *id_s, size_t id_s_len, + const u8 *id_p, size_t id_p_len); +int eap_eke_derive_ka(struct eap_eke_session *sess, + const u8 *id_s, size_t id_s_len, + const u8 *id_p, size_t id_p_len, + const u8 *nonce_p, const u8 *nonce_s); +int eap_eke_derive_msk(struct eap_eke_session *sess, + const u8 *id_s, size_t id_s_len, + const u8 *id_p, size_t id_p_len, + const u8 *nonce_p, const u8 *nonce_s, + u8 *msk, u8 *emsk); +int eap_eke_prot(struct eap_eke_session *sess, + const u8 *data, size_t data_len, + u8 *prot, size_t *prot_len); +int eap_eke_decrypt_prot(struct eap_eke_session *sess, + const u8 *prot, size_t prot_len, + u8 *data, size_t *data_len); +int eap_eke_auth(struct eap_eke_session *sess, const char *label, + const struct wpabuf *msgs, u8 *auth); + +#endif /* EAP_EKE_COMMON_H */ diff -Nru wpa-1.0/src/eap_common/eap_fast_common.c wpa-2.1/src/eap_common/eap_fast_common.c --- wpa-1.0/src/eap_common/eap_fast_common.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/eap_common/eap_fast_common.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * EAP-FAST common helper functions (RFC 4851) * Copyright (c) 2008, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -133,9 +127,9 @@ wpa_hexdump_key(MSG_MSGDUMP, "EAP-FAST: master_secret for key " "expansion", keys.master_key, keys.master_key_len); - if (tls_prf(keys.master_key, keys.master_key_len, - label, rnd, keys.client_random_len + - keys.server_random_len, out, block_size + len)) + if (tls_prf_sha1_md5(keys.master_key, keys.master_key_len, + label, rnd, keys.client_random_len + + keys.server_random_len, out, block_size + len)) goto fail; os_free(rnd); os_memmove(out, out + block_size, len); diff -Nru wpa-1.0/src/eap_common/eap_fast_common.h wpa-2.1/src/eap_common/eap_fast_common.h --- wpa-1.0/src/eap_common/eap_fast_common.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/eap_common/eap_fast_common.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * EAP-FAST definitions (RFC 4851) * Copyright (c) 2004-2008, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef EAP_FAST_H diff -Nru wpa-1.0/src/eap_common/eap_gpsk_common.c wpa-2.1/src/eap_common/eap_gpsk_common.c --- wpa-1.0/src/eap_common/eap_gpsk_common.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/eap_common/eap_gpsk_common.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * EAP server/peer: EAP-GPSK shared routines * Copyright (c) 2006-2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -342,6 +336,141 @@ os_free(seed); + return ret; +} + + +static int eap_gpsk_derive_mid_helper(u32 csuite_specifier, + u8 *kdf_out, size_t kdf_out_len, + const u8 *psk, const u8 *seed, + size_t seed_len, u8 method_type) +{ + u8 *pos, *data; + size_t data_len; + int (*gkdf)(const u8 *_psk, const u8 *_data, size_t _data_len, + u8 *buf, size_t len); + + gkdf = NULL; + switch (csuite_specifier) { + case EAP_GPSK_CIPHER_AES: + gkdf = eap_gpsk_gkdf_cmac; + break; +#ifdef EAP_GPSK_SHA256 + case EAP_GPSK_CIPHER_SHA256: + gkdf = eap_gpsk_gkdf_sha256; + break; +#endif /* EAP_GPSK_SHA256 */ + default: + wpa_printf(MSG_DEBUG, "EAP-GPSK: Unknown cipher %d used in " + "Session-Id derivation", csuite_specifier); + return -1; + } + +#define SID_LABEL "Method ID" + /* "Method ID" || EAP_Method_Type || CSuite_Sel || inputString */ + data_len = strlen(SID_LABEL) + 1 + 6 + seed_len; + data = os_malloc(data_len); + if (data == NULL) + return -1; + pos = data; + os_memcpy(pos, SID_LABEL, strlen(SID_LABEL)); + pos += strlen(SID_LABEL); +#undef SID_LABEL + os_memcpy(pos, &method_type, 1); + pos += 1; + WPA_PUT_BE32(pos, EAP_GPSK_VENDOR_IETF); /* CSuite/Vendor = IETF */ + pos += 4; + WPA_PUT_BE16(pos, csuite_specifier); /* CSuite/Specifier */ + pos += 2; + os_memcpy(pos, seed, seed_len); /* inputString */ + wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Data to Method ID derivation", + data, data_len); + + if (gkdf(psk, data, data_len, kdf_out, kdf_out_len) < 0) { + os_free(data); + return -1; + } + os_free(data); + wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Method ID", kdf_out, kdf_out_len); + + return 0; +} + + +/** + * eap_gpsk_session_id - Derive EAP-GPSK Session ID + * @psk: Pre-shared key + * @psk_len: Length of psk in bytes + * @vendor: CSuite/Vendor + * @specifier: CSuite/Specifier + * @rand_peer: 32-byte RAND_Peer + * @rand_server: 32-byte RAND_Server + * @id_peer: ID_Peer + * @id_peer_len: Length of ID_Peer + * @id_server: ID_Server + * @id_server_len: Length of ID_Server + * @method_type: EAP Authentication Method Type + * @sid: Buffer for 17-byte Session ID + * @sid_len: Buffer for returning length of Session ID + * Returns: 0 on success, -1 on failure + */ +int eap_gpsk_derive_session_id(const u8 *psk, size_t psk_len, int vendor, + int specifier, + const u8 *rand_peer, const u8 *rand_server, + const u8 *id_peer, size_t id_peer_len, + const u8 *id_server, size_t id_server_len, + u8 method_type, u8 *sid, size_t *sid_len) +{ + u8 *seed, *pos; + u8 kdf_out[16]; + size_t seed_len; + int ret; + + wpa_printf(MSG_DEBUG, "EAP-GPSK: Deriving Session ID(%d:%d)", + vendor, specifier); + + if (vendor != EAP_GPSK_VENDOR_IETF) + return -1; + + wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: PSK", psk, psk_len); + + /* + * inputString = RAND_Peer || ID_Peer || RAND_Server || ID_Server + * (= seed) + * KS = 16, CSuite_Sel = 0x00000000 0x0001 + * Method-ID = GKDF-16 (zero, "Method ID" || EAP_Method_Type || + * CSuite_Sel || inputString) + */ + seed_len = 2 * EAP_GPSK_RAND_LEN + id_server_len + id_peer_len; + seed = os_malloc(seed_len); + if (seed == NULL) { + wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to allocate memory " + "for Session-Id derivation"); + return -1; + } + + pos = seed; + os_memcpy(pos, rand_peer, EAP_GPSK_RAND_LEN); + pos += EAP_GPSK_RAND_LEN; + os_memcpy(pos, id_peer, id_peer_len); + pos += id_peer_len; + os_memcpy(pos, rand_server, EAP_GPSK_RAND_LEN); + pos += EAP_GPSK_RAND_LEN; + os_memcpy(pos, id_server, id_server_len); + pos += id_server_len; + wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Seed", seed, seed_len); + + ret = eap_gpsk_derive_mid_helper(specifier, + kdf_out, sizeof(kdf_out), + psk, seed, seed_len, + method_type); + + sid[0] = method_type; + os_memcpy(sid + 1, kdf_out, sizeof(kdf_out)); + *sid_len = 1 + sizeof(kdf_out); + + os_free(seed); + return ret; } diff -Nru wpa-1.0/src/eap_common/eap_gpsk_common.h wpa-2.1/src/eap_common/eap_gpsk_common.h --- wpa-1.0/src/eap_common/eap_gpsk_common.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/eap_common/eap_gpsk_common.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * EAP server/peer: EAP-GPSK shared routines * Copyright (c) 2006-2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef EAP_GPSK_COMMON_H @@ -59,6 +53,12 @@ const u8 *id_server, size_t id_server_len, u8 *msk, u8 *emsk, u8 *sk, size_t *sk_len, u8 *pk, size_t *pk_len); +int eap_gpsk_derive_session_id(const u8 *psk, size_t psk_len, int vendor, + int specifier, + const u8 *rand_peer, const u8 *rand_server, + const u8 *id_peer, size_t id_peer_len, + const u8 *id_server, size_t id_server_len, + u8 method_type, u8 *sid, size_t *sid_len); size_t eap_gpsk_mic_len(int vendor, int specifier); int eap_gpsk_compute_mic(const u8 *sk, size_t sk_len, int vendor, int specifier, const u8 *data, size_t len, u8 *mic); diff -Nru wpa-1.0/src/eap_common/eap_ikev2_common.c wpa-2.1/src/eap_common/eap_ikev2_common.c --- wpa-1.0/src/eap_common/eap_ikev2_common.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/eap_common/eap_ikev2_common.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * EAP-IKEv2 common routines * Copyright (c) 2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff -Nru wpa-1.0/src/eap_common/eap_ikev2_common.h wpa-2.1/src/eap_common/eap_ikev2_common.h --- wpa-1.0/src/eap_common/eap_ikev2_common.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/eap_common/eap_ikev2_common.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * EAP-IKEv2 definitions * Copyright (c) 2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef EAP_IKEV2_COMMON_H diff -Nru wpa-1.0/src/eap_common/eap_pax_common.c wpa-2.1/src/eap_common/eap_pax_common.c --- wpa-1.0/src/eap_common/eap_pax_common.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/eap_common/eap_pax_common.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * EAP server/peer: EAP-PAX shared routines * Copyright (c) 2005, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff -Nru wpa-1.0/src/eap_common/eap_pax_common.h wpa-2.1/src/eap_common/eap_pax_common.h --- wpa-1.0/src/eap_common/eap_pax_common.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/eap_common/eap_pax_common.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * EAP server/peer: EAP-PAX shared routines * Copyright (c) 2005-2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef EAP_PAX_COMMON_H diff -Nru wpa-1.0/src/eap_common/eap_peap_common.c wpa-2.1/src/eap_common/eap_peap_common.c --- wpa-1.0/src/eap_common/eap_peap_common.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/eap_common/eap_peap_common.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * EAP-PEAP common routines * Copyright (c) 2008-2011, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff -Nru wpa-1.0/src/eap_common/eap_peap_common.h wpa-2.1/src/eap_common/eap_peap_common.h --- wpa-1.0/src/eap_common/eap_peap_common.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/eap_common/eap_peap_common.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * EAP-PEAP common routines * Copyright (c) 2008-2011, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef EAP_PEAP_COMMON_H diff -Nru wpa-1.0/src/eap_common/eap_psk_common.c wpa-2.1/src/eap_common/eap_psk_common.c --- wpa-1.0/src/eap_common/eap_psk_common.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/eap_common/eap_psk_common.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * EAP server/peer: EAP-PSK shared routines * Copyright (c) 2004-2006, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff -Nru wpa-1.0/src/eap_common/eap_psk_common.h wpa-2.1/src/eap_common/eap_psk_common.h --- wpa-1.0/src/eap_common/eap_psk_common.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/eap_common/eap_psk_common.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * EAP server/peer: EAP-PSK shared routines * Copyright (c) 2004-2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef EAP_PSK_COMMON_H diff -Nru wpa-1.0/src/eap_common/eap_pwd_common.c wpa-2.1/src/eap_common/eap_pwd_common.c --- wpa-1.0/src/eap_common/eap_pwd_common.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/eap_common/eap_pwd_common.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,82 +2,82 @@ * EAP server/peer: EAP-pwd shared routines * Copyright (c) 2010, Dan Harkins * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the BSD license. - * - * Alternatively, this software may be distributed under the terms of the - * GNU General Public License version 2 as published by the Free Software - * Foundation. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" #include "common.h" +#include "crypto/sha256.h" +#include "crypto/crypto.h" #include "eap_defs.h" #include "eap_pwd_common.h" /* The random function H(x) = HMAC-SHA256(0^32, x) */ -void H_Init(HMAC_CTX *ctx) +struct crypto_hash * eap_pwd_h_init(void) { - u8 allzero[SHA256_DIGEST_LENGTH]; - - os_memset(allzero, 0, SHA256_DIGEST_LENGTH); - HMAC_Init(ctx, allzero, SHA256_DIGEST_LENGTH, EVP_sha256()); + u8 allzero[SHA256_MAC_LEN]; + os_memset(allzero, 0, SHA256_MAC_LEN); + return crypto_hash_init(CRYPTO_HASH_ALG_HMAC_SHA256, allzero, + SHA256_MAC_LEN); } -void H_Update(HMAC_CTX *ctx, const u8 *data, int len) +void eap_pwd_h_update(struct crypto_hash *hash, const u8 *data, size_t len) { - HMAC_Update(ctx, data, len); + crypto_hash_update(hash, data, len); } -void H_Final(HMAC_CTX *ctx, u8 *digest) +void eap_pwd_h_final(struct crypto_hash *hash, u8 *digest) { - unsigned int mdlen = SHA256_DIGEST_LENGTH; - - HMAC_Final(ctx, digest, &mdlen); - HMAC_CTX_cleanup(ctx); + size_t len = SHA256_MAC_LEN; + crypto_hash_finish(hash, digest, &len); } /* a counter-based KDF based on NIST SP800-108 */ -void eap_pwd_kdf(u8 *key, int keylen, u8 *label, int labellen, - u8 *result, int resultbitlen) +static int eap_pwd_kdf(const u8 *key, size_t keylen, const u8 *label, + size_t labellen, u8 *result, size_t resultbitlen) { - HMAC_CTX hctx; - unsigned char digest[SHA256_DIGEST_LENGTH]; + struct crypto_hash *hash; + u8 digest[SHA256_MAC_LEN]; u16 i, ctr, L; - int resultbytelen, len = 0; - unsigned int mdlen = SHA256_DIGEST_LENGTH; - unsigned char mask = 0xff; + size_t resultbytelen, len = 0, mdlen; - resultbytelen = (resultbitlen + 7)/8; + resultbytelen = (resultbitlen + 7) / 8; ctr = 0; L = htons(resultbitlen); while (len < resultbytelen) { - ctr++; i = htons(ctr); - HMAC_Init(&hctx, key, keylen, EVP_sha256()); + ctr++; + i = htons(ctr); + hash = crypto_hash_init(CRYPTO_HASH_ALG_HMAC_SHA256, + key, keylen); + if (hash == NULL) + return -1; if (ctr > 1) - HMAC_Update(&hctx, digest, mdlen); - HMAC_Update(&hctx, (u8 *) &i, sizeof(u16)); - HMAC_Update(&hctx, label, labellen); - HMAC_Update(&hctx, (u8 *) &L, sizeof(u16)); - HMAC_Final(&hctx, digest, &mdlen); - if ((len + (int) mdlen) > resultbytelen) + crypto_hash_update(hash, digest, SHA256_MAC_LEN); + crypto_hash_update(hash, (u8 *) &i, sizeof(u16)); + crypto_hash_update(hash, label, labellen); + crypto_hash_update(hash, (u8 *) &L, sizeof(u16)); + mdlen = SHA256_MAC_LEN; + if (crypto_hash_finish(hash, digest, &mdlen) < 0) + return -1; + if ((len + mdlen) > resultbytelen) os_memcpy(result + len, digest, resultbytelen - len); else os_memcpy(result + len, digest, mdlen); len += mdlen; - HMAC_CTX_cleanup(&hctx); } /* since we're expanding to a bit length, mask off the excess */ if (resultbitlen % 8) { + u8 mask = 0xff; mask <<= (8 - (resultbitlen % 8)); result[resultbytelen - 1] &= mask; } + + return 0; } @@ -91,9 +91,10 @@ u8 *id_peer, int id_peer_len, u8 *token) { BIGNUM *x_candidate = NULL, *rnd = NULL, *cofactor = NULL; - HMAC_CTX ctx; - unsigned char pwe_digest[SHA256_DIGEST_LENGTH], *prfbuf = NULL, ctr; - int nid, is_odd, primebitlen, primebytelen, ret = 0; + struct crypto_hash *hash; + unsigned char pwe_digest[SHA256_MAC_LEN], *prfbuf = NULL, ctr; + int nid, is_odd, ret = 0; + size_t primebytelen, primebitlen; switch (num) { /* from IANA registry for IKE D-H groups */ case 19: @@ -160,7 +161,7 @@ os_memset(prfbuf, 0, primebytelen); ctr = 0; while (1) { - if (ctr > 10) { + if (ctr > 30) { wpa_printf(MSG_INFO, "EAP-pwd: unable to find random " "point on curve for group %d, something's " "fishy", num); @@ -173,20 +174,23 @@ * pwd-seed = H(token | peer-id | server-id | password | * counter) */ - H_Init(&ctx); - H_Update(&ctx, token, sizeof(u32)); - H_Update(&ctx, id_peer, id_peer_len); - H_Update(&ctx, id_server, id_server_len); - H_Update(&ctx, password, password_len); - H_Update(&ctx, &ctr, sizeof(ctr)); - H_Final(&ctx, pwe_digest); - - BN_bin2bn(pwe_digest, SHA256_DIGEST_LENGTH, rnd); - - eap_pwd_kdf(pwe_digest, SHA256_DIGEST_LENGTH, - (unsigned char *) "EAP-pwd Hunting And Pecking", - os_strlen("EAP-pwd Hunting And Pecking"), - prfbuf, primebitlen); + hash = eap_pwd_h_init(); + if (hash == NULL) + goto fail; + eap_pwd_h_update(hash, token, sizeof(u32)); + eap_pwd_h_update(hash, id_peer, id_peer_len); + eap_pwd_h_update(hash, id_server, id_server_len); + eap_pwd_h_update(hash, password, password_len); + eap_pwd_h_update(hash, &ctr, sizeof(ctr)); + eap_pwd_h_final(hash, pwe_digest); + + BN_bin2bn(pwe_digest, SHA256_MAC_LEN, rnd); + + if (eap_pwd_kdf(pwe_digest, SHA256_MAC_LEN, + (u8 *) "EAP-pwd Hunting And Pecking", + os_strlen("EAP-pwd Hunting And Pecking"), + prfbuf, primebitlen) < 0) + goto fail; BN_bin2bn(prfbuf, primebytelen, x_candidate); @@ -258,11 +262,13 @@ if (0) { fail: EC_GROUP_free(grp->group); + grp->group = NULL; EC_POINT_free(grp->pwe); + grp->pwe = NULL; BN_free(grp->order); + grp->order = NULL; BN_free(grp->prime); - os_free(grp); - grp = NULL; + grp->prime = NULL; ret = 1; } /* cleanliness and order.... */ @@ -277,12 +283,12 @@ int compute_keys(EAP_PWD_group *grp, BN_CTX *bnctx, BIGNUM *k, BIGNUM *peer_scalar, BIGNUM *server_scalar, - u8 *commit_peer, u8 *commit_server, + u8 *confirm_peer, u8 *confirm_server, u32 *ciphersuite, u8 *msk, u8 *emsk) { - HMAC_CTX ctx; - u8 mk[SHA256_DIGEST_LENGTH], *cruft; - u8 session_id[SHA256_DIGEST_LENGTH + 1]; + struct crypto_hash *hash; + u8 mk[SHA256_MAC_LEN], *cruft; + u8 session_id[SHA256_MAC_LEN + 1]; u8 msk_emsk[EAP_MSK_LEN + EAP_EMSK_LEN]; int offset; @@ -294,37 +300,46 @@ * scal_s) */ session_id[0] = EAP_TYPE_PWD; - H_Init(&ctx); - H_Update(&ctx, (u8 *)ciphersuite, sizeof(u32)); + hash = eap_pwd_h_init(); + if (hash == NULL) { + os_free(cruft); + return -1; + } + eap_pwd_h_update(hash, (u8 *) ciphersuite, sizeof(u32)); offset = BN_num_bytes(grp->order) - BN_num_bytes(peer_scalar); os_memset(cruft, 0, BN_num_bytes(grp->prime)); BN_bn2bin(peer_scalar, cruft + offset); - H_Update(&ctx, cruft, BN_num_bytes(grp->order)); + eap_pwd_h_update(hash, cruft, BN_num_bytes(grp->order)); offset = BN_num_bytes(grp->order) - BN_num_bytes(server_scalar); os_memset(cruft, 0, BN_num_bytes(grp->prime)); BN_bn2bin(server_scalar, cruft + offset); - H_Update(&ctx, cruft, BN_num_bytes(grp->order)); - H_Final(&ctx, &session_id[1]); + eap_pwd_h_update(hash, cruft, BN_num_bytes(grp->order)); + eap_pwd_h_final(hash, &session_id[1]); - /* then compute MK = H(k | commit-peer | commit-server) */ - H_Init(&ctx); + /* then compute MK = H(k | confirm-peer | confirm-server) */ + hash = eap_pwd_h_init(); + if (hash == NULL) { + os_free(cruft); + return -1; + } offset = BN_num_bytes(grp->prime) - BN_num_bytes(k); os_memset(cruft, 0, BN_num_bytes(grp->prime)); BN_bn2bin(k, cruft + offset); - H_Update(&ctx, cruft, BN_num_bytes(grp->prime)); - H_Update(&ctx, commit_peer, SHA256_DIGEST_LENGTH); - H_Update(&ctx, commit_server, SHA256_DIGEST_LENGTH); - H_Final(&ctx, mk); + eap_pwd_h_update(hash, cruft, BN_num_bytes(grp->prime)); + os_free(cruft); + eap_pwd_h_update(hash, confirm_peer, SHA256_MAC_LEN); + eap_pwd_h_update(hash, confirm_server, SHA256_MAC_LEN); + eap_pwd_h_final(hash, mk); /* stretch the mk with the session-id to get MSK | EMSK */ - eap_pwd_kdf(mk, SHA256_DIGEST_LENGTH, - session_id, SHA256_DIGEST_LENGTH+1, - msk_emsk, (EAP_MSK_LEN + EAP_EMSK_LEN) * 8); + if (eap_pwd_kdf(mk, SHA256_MAC_LEN, + session_id, SHA256_MAC_LEN + 1, + msk_emsk, (EAP_MSK_LEN + EAP_EMSK_LEN) * 8) < 0) { + return -1; + } os_memcpy(msk, msk_emsk, EAP_MSK_LEN); os_memcpy(emsk, msk_emsk + EAP_MSK_LEN, EAP_EMSK_LEN); - os_free(cruft); - return 1; } diff -Nru wpa-1.0/src/eap_common/eap_pwd_common.h wpa-2.1/src/eap_common/eap_pwd_common.h --- wpa-1.0/src/eap_common/eap_pwd_common.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/eap_common/eap_pwd_common.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,24 +2,16 @@ * EAP server/peer: EAP-pwd shared definitions * Copyright (c) 2009, Dan Harkins * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the BSD license. - * - * Alternatively, this software may be distributed under the terms of the - * GNU General Public License version 2 as published by the Free Software - * Foundation. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef EAP_PWD_COMMON_H #define EAP_PWD_COMMON_H #include -#include #include #include -#include /* * definition of a finite cyclic group @@ -35,23 +27,19 @@ /* * EAP-pwd header, included on all payloads + * L(1 bit) | M(1 bit) | exch(6 bits) | total_length(if L is set) */ -struct eap_pwd_hdr { - u8 l_bit:1; - u8 m_bit:1; - u8 exch:6; - u8 total_length[0]; /* included when l_bit is set */ -} STRUCT_PACKED; +#define EAP_PWD_HDR_SIZE 1 #define EAP_PWD_OPCODE_ID_EXCH 1 #define EAP_PWD_OPCODE_COMMIT_EXCH 2 #define EAP_PWD_OPCODE_CONFIRM_EXCH 3 -#define EAP_PWD_GET_LENGTH_BIT(x) ((x)->lm_exch & 0x80) -#define EAP_PWD_SET_LENGTH_BIT(x) ((x)->lm_exch |= 0x80) -#define EAP_PWD_GET_MORE_BIT(x) ((x)->lm_exch & 0x40) -#define EAP_PWD_SET_MORE_BIT(x) ((x)->lm_exch |= 0x40) -#define EAP_PWD_GET_EXCHANGE(x) ((x)->lm_exch & 0x3f) -#define EAP_PWD_SET_EXCHANGE(x,y) ((x)->lm_exch |= (y)) +#define EAP_PWD_GET_LENGTH_BIT(x) ((x) & 0x80) +#define EAP_PWD_SET_LENGTH_BIT(x) ((x) |= 0x80) +#define EAP_PWD_GET_MORE_BIT(x) ((x) & 0x40) +#define EAP_PWD_SET_MORE_BIT(x) ((x) |= 0x40) +#define EAP_PWD_GET_EXCHANGE(x) ((x) & 0x3f) +#define EAP_PWD_SET_EXCHANGE(x,y) ((x) |= (y)) /* EAP-pwd-ID payload */ struct eap_pwd_id { @@ -72,8 +60,8 @@ int, u8 *); int compute_keys(EAP_PWD_group *, BN_CTX *, BIGNUM *, BIGNUM *, BIGNUM *, u8 *, u8 *, u32 *, u8 *, u8 *); -void H_Init(HMAC_CTX *); -void H_Update(HMAC_CTX *, const u8 *, int); -void H_Final(HMAC_CTX *, u8 *); +struct crypto_hash * eap_pwd_h_init(void); +void eap_pwd_h_update(struct crypto_hash *hash, const u8 *data, size_t len); +void eap_pwd_h_final(struct crypto_hash *hash, u8 *digest); #endif /* EAP_PWD_COMMON_H */ diff -Nru wpa-1.0/src/eap_common/eap_sake_common.c wpa-2.1/src/eap_common/eap_sake_common.c --- wpa-1.0/src/eap_common/eap_sake_common.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/eap_common/eap_sake_common.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * EAP server/peer: EAP-SAKE shared routines * Copyright (c) 2006-2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff -Nru wpa-1.0/src/eap_common/eap_sake_common.h wpa-2.1/src/eap_common/eap_sake_common.h --- wpa-1.0/src/eap_common/eap_sake_common.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/eap_common/eap_sake_common.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * EAP server/peer: EAP-SAKE shared routines * Copyright (c) 2006-2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef EAP_SAKE_COMMON_H diff -Nru wpa-1.0/src/eap_common/eap_sim_common.c wpa-2.1/src/eap_common/eap_sim_common.c --- wpa-1.0/src/eap_common/eap_sim_common.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/eap_common/eap_sim_common.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * EAP peer/server: EAP-SIM/AKA/AKA' shared routines * Copyright (c) 2004-2008, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff -Nru wpa-1.0/src/eap_common/eap_sim_common.h wpa-2.1/src/eap_common/eap_sim_common.h --- wpa-1.0/src/eap_common/eap_sim_common.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/eap_common/eap_sim_common.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * EAP peer/server: EAP-SIM/AKA/AKA' shared routines * Copyright (c) 2004-2008, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef EAP_SIM_COMMON_H diff -Nru wpa-1.0/src/eap_common/eap_tlv_common.h wpa-2.1/src/eap_common/eap_tlv_common.h --- wpa-1.0/src/eap_common/eap_tlv_common.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/eap_common/eap_tlv_common.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * EAP-TLV definitions (draft-josefsson-pppext-eap-tls-eap-10.txt) * Copyright (c) 2004-2008, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef EAP_TLV_COMMON_H diff -Nru wpa-1.0/src/eap_common/eap_ttls.h wpa-2.1/src/eap_common/eap_ttls.h --- wpa-1.0/src/eap_common/eap_ttls.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/eap_common/eap_ttls.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * EAP server/peer: EAP-TTLS (RFC 5281) * Copyright (c) 2004-2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef EAP_TTLS_H diff -Nru wpa-1.0/src/eap_common/eap_wsc_common.c wpa-2.1/src/eap_common/eap_wsc_common.c --- wpa-1.0/src/eap_common/eap_wsc_common.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/eap_common/eap_wsc_common.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * EAP-WSC common routines for Wi-Fi Protected Setup * Copyright (c) 2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff -Nru wpa-1.0/src/eap_common/eap_wsc_common.h wpa-2.1/src/eap_common/eap_wsc_common.h --- wpa-1.0/src/eap_common/eap_wsc_common.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/eap_common/eap_wsc_common.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * EAP-WSC definitions for Wi-Fi Protected Setup * Copyright (c) 2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef EAP_WSC_COMMON_H diff -Nru wpa-1.0/src/eap_common/ikev2_common.c wpa-2.1/src/eap_common/ikev2_common.c --- wpa-1.0/src/eap_common/ikev2_common.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/eap_common/ikev2_common.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * IKEv2 common routines for initiator and responder * Copyright (c) 2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -27,7 +21,7 @@ { AUTH_HMAC_MD5_96, 16, 12 } }; -#define NUM_INTEG_ALGS (sizeof(ikev2_integ_algs) / sizeof(ikev2_integ_algs[0])) +#define NUM_INTEG_ALGS ARRAY_SIZE(ikev2_integ_algs) static struct ikev2_prf_alg ikev2_prf_algs[] = { @@ -35,7 +29,7 @@ { PRF_HMAC_MD5, 16, 16 } }; -#define NUM_PRF_ALGS (sizeof(ikev2_prf_algs) / sizeof(ikev2_prf_algs[0])) +#define NUM_PRF_ALGS ARRAY_SIZE(ikev2_prf_algs) static struct ikev2_encr_alg ikev2_encr_algs[] = { @@ -43,7 +37,7 @@ { ENCR_3DES, 24, 8 } }; -#define NUM_ENCR_ALGS (sizeof(ikev2_encr_algs) / sizeof(ikev2_encr_algs[0])) +#define NUM_ENCR_ALGS ARRAY_SIZE(ikev2_encr_algs) const struct ikev2_integ_alg * ikev2_get_integ(int id) diff -Nru wpa-1.0/src/eap_common/ikev2_common.h wpa-2.1/src/eap_common/ikev2_common.h --- wpa-1.0/src/eap_common/ikev2_common.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/eap_common/ikev2_common.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * IKEv2 definitions * Copyright (c) 2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef IKEV2_COMMON_H diff -Nru wpa-1.0/src/eap_common/Makefile wpa-2.1/src/eap_common/Makefile --- wpa-1.0/src/eap_common/Makefile 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/eap_common/Makefile 2014-02-04 11:23:35.000000000 +0000 @@ -2,7 +2,7 @@ @echo Nothing to be made. clean: - rm -f *~ *.o *.d + rm -f *~ *.o *.d *.gcno *.gcda *.gcov install: @echo Nothing to be made. diff -Nru wpa-1.0/src/eapol_auth/eapol_auth_dump.c wpa-2.1/src/eapol_auth/eapol_auth_dump.c --- wpa-1.0/src/eapol_auth/eapol_auth_dump.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/eapol_auth/eapol_auth_dump.c 2014-02-04 11:23:35.000000000 +0000 @@ -1,15 +1,9 @@ /* * IEEE 802.1X-2004 Authenticator - State dump - * Copyright (c) 2002-2009, Jouni Malinen + * Copyright (c) 2002-2013, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -124,108 +118,172 @@ } -void eapol_auth_dump_state(FILE *f, const char *prefix, - struct eapol_state_machine *sm) +int eapol_auth_dump_state(struct eapol_state_machine *sm, char *buf, + size_t buflen) { - fprintf(f, "%sEAPOL state machine:\n", prefix); - fprintf(f, "%s aWhile=%d quietWhile=%d reAuthWhen=%d\n", prefix, - sm->aWhile, sm->quietWhile, sm->reAuthWhen); + char *pos, *end; + int ret; + + pos = buf; + end = pos + buflen; + + ret = os_snprintf(pos, end - pos, "aWhile=%d\nquietWhile=%d\n" + "reAuthWhen=%d\n", + sm->aWhile, sm->quietWhile, sm->reAuthWhen); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + #define _SB(b) ((b) ? "TRUE" : "FALSE") - fprintf(f, - "%s authAbort=%s authFail=%s authPortStatus=%s authStart=%s\n" - "%s authTimeout=%s authSuccess=%s eapFail=%s eapolEap=%s\n" - "%s eapSuccess=%s eapTimeout=%s initialize=%s " - "keyAvailable=%s\n" - "%s keyDone=%s keyRun=%s keyTxEnabled=%s portControl=%s\n" - "%s portEnabled=%s portValid=%s reAuthenticate=%s\n", - prefix, _SB(sm->authAbort), _SB(sm->authFail), - port_state_txt(sm->authPortStatus), _SB(sm->authStart), - prefix, _SB(sm->authTimeout), _SB(sm->authSuccess), - _SB(sm->eap_if->eapFail), _SB(sm->eapolEap), - prefix, _SB(sm->eap_if->eapSuccess), - _SB(sm->eap_if->eapTimeout), - _SB(sm->initialize), _SB(sm->eap_if->eapKeyAvailable), - prefix, _SB(sm->keyDone), _SB(sm->keyRun), - _SB(sm->keyTxEnabled), port_type_txt(sm->portControl), - prefix, _SB(sm->eap_if->portEnabled), _SB(sm->portValid), - _SB(sm->reAuthenticate)); - - fprintf(f, "%s Authenticator PAE:\n" - "%s state=%s\n" - "%s eapolLogoff=%s eapolStart=%s eapRestart=%s\n" - "%s portMode=%s reAuthCount=%d\n" - "%s quietPeriod=%d reAuthMax=%d\n" - "%s authEntersConnecting=%d\n" - "%s authEapLogoffsWhileConnecting=%d\n" - "%s authEntersAuthenticating=%d\n" - "%s authAuthSuccessesWhileAuthenticating=%d\n" - "%s authAuthTimeoutsWhileAuthenticating=%d\n" - "%s authAuthFailWhileAuthenticating=%d\n" - "%s authAuthEapStartsWhileAuthenticating=%d\n" - "%s authAuthEapLogoffWhileAuthenticating=%d\n" - "%s authAuthReauthsWhileAuthenticated=%d\n" - "%s authAuthEapStartsWhileAuthenticated=%d\n" - "%s authAuthEapLogoffWhileAuthenticated=%d\n", - prefix, prefix, auth_pae_state_txt(sm->auth_pae_state), prefix, - _SB(sm->eapolLogoff), _SB(sm->eapolStart), - _SB(sm->eap_if->eapRestart), - prefix, port_type_txt(sm->portMode), sm->reAuthCount, - prefix, sm->quietPeriod, sm->reAuthMax, - prefix, sm->authEntersConnecting, - prefix, sm->authEapLogoffsWhileConnecting, - prefix, sm->authEntersAuthenticating, - prefix, sm->authAuthSuccessesWhileAuthenticating, - prefix, sm->authAuthTimeoutsWhileAuthenticating, - prefix, sm->authAuthFailWhileAuthenticating, - prefix, sm->authAuthEapStartsWhileAuthenticating, - prefix, sm->authAuthEapLogoffWhileAuthenticating, - prefix, sm->authAuthReauthsWhileAuthenticated, - prefix, sm->authAuthEapStartsWhileAuthenticated, - prefix, sm->authAuthEapLogoffWhileAuthenticated); - - fprintf(f, "%s Backend Authentication:\n" - "%s state=%s\n" - "%s eapNoReq=%s eapReq=%s eapResp=%s\n" - "%s serverTimeout=%d\n" - "%s backendResponses=%d\n" - "%s backendAccessChallenges=%d\n" - "%s backendOtherRequestsToSupplicant=%d\n" - "%s backendAuthSuccesses=%d\n" - "%s backendAuthFails=%d\n", - prefix, prefix, - be_auth_state_txt(sm->be_auth_state), - prefix, _SB(sm->eap_if->eapNoReq), _SB(sm->eap_if->eapReq), - _SB(sm->eap_if->eapResp), - prefix, sm->serverTimeout, - prefix, sm->backendResponses, - prefix, sm->backendAccessChallenges, - prefix, sm->backendOtherRequestsToSupplicant, - prefix, sm->backendAuthSuccesses, - prefix, sm->backendAuthFails); - - fprintf(f, "%s Reauthentication Timer:\n" - "%s state=%s\n" - "%s reAuthPeriod=%d reAuthEnabled=%s\n", prefix, prefix, - reauth_timer_state_txt(sm->reauth_timer_state), prefix, - sm->reAuthPeriod, _SB(sm->reAuthEnabled)); - - fprintf(f, "%s Authenticator Key Transmit:\n" - "%s state=%s\n", prefix, prefix, - auth_key_tx_state_txt(sm->auth_key_tx_state)); - - fprintf(f, "%s Key Receive:\n" - "%s state=%s\n" - "%s rxKey=%s\n", prefix, prefix, - key_rx_state_txt(sm->key_rx_state), prefix, _SB(sm->rxKey)); - - fprintf(f, "%s Controlled Directions:\n" - "%s state=%s\n" - "%s adminControlledDirections=%s " - "operControlledDirections=%s\n" - "%s operEdge=%s\n", prefix, prefix, - ctrl_dir_state_txt(sm->ctrl_dir_state), - prefix, ctrl_dir_txt(sm->adminControlledDirections), - ctrl_dir_txt(sm->operControlledDirections), - prefix, _SB(sm->operEdge)); + ret = os_snprintf(pos, end - pos, + "authAbort=%s\n" + "authFail=%s\n" + "authPortStatus=%s\n" + "authStart=%s\n" + "authTimeout=%s\n" + "authSuccess=%s\n" + "eapFail=%s\n" + "eapolEap=%s\n" + "eapSuccess=%s\n" + "eapTimeout=%s\n" + "initialize=%s\n" + "keyAvailable=%s\n" + "keyDone=%s\n" + "keyRun=%s\n" + "keyTxEnabled=%s\n" + "portControl=%s\n" + "portEnabled=%s\n" + "portValid=%s\n" + "reAuthenticate=%s\n", + _SB(sm->authAbort), + _SB(sm->authFail), + port_state_txt(sm->authPortStatus), + _SB(sm->authStart), + _SB(sm->authTimeout), + _SB(sm->authSuccess), + _SB(sm->eap_if->eapFail), + _SB(sm->eapolEap), + _SB(sm->eap_if->eapSuccess), + _SB(sm->eap_if->eapTimeout), + _SB(sm->initialize), + _SB(sm->eap_if->eapKeyAvailable), + _SB(sm->keyDone), _SB(sm->keyRun), + _SB(sm->keyTxEnabled), + port_type_txt(sm->portControl), + _SB(sm->eap_if->portEnabled), + _SB(sm->portValid), + _SB(sm->reAuthenticate)); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + + ret = os_snprintf(pos, end - pos, + "auth_pae_state=%s\n" + "eapolLogoff=%s\n" + "eapolStart=%s\n" + "eapRestart=%s\n" + "portMode=%s\n" + "reAuthCount=%d\n" + "quietPeriod=%d\n" + "reAuthMax=%d\n" + "authEntersConnecting=%d\n" + "authEapLogoffsWhileConnecting=%d\n" + "authEntersAuthenticating=%d\n" + "authAuthSuccessesWhileAuthenticating=%d\n" + "authAuthTimeoutsWhileAuthenticating=%d\n" + "authAuthFailWhileAuthenticating=%d\n" + "authAuthEapStartsWhileAuthenticating=%d\n" + "authAuthEapLogoffWhileAuthenticating=%d\n" + "authAuthReauthsWhileAuthenticated=%d\n" + "authAuthEapStartsWhileAuthenticated=%d\n" + "authAuthEapLogoffWhileAuthenticated=%d\n", + auth_pae_state_txt(sm->auth_pae_state), + _SB(sm->eapolLogoff), + _SB(sm->eapolStart), + _SB(sm->eap_if->eapRestart), + port_type_txt(sm->portMode), + sm->reAuthCount, + sm->quietPeriod, sm->reAuthMax, + sm->authEntersConnecting, + sm->authEapLogoffsWhileConnecting, + sm->authEntersAuthenticating, + sm->authAuthSuccessesWhileAuthenticating, + sm->authAuthTimeoutsWhileAuthenticating, + sm->authAuthFailWhileAuthenticating, + sm->authAuthEapStartsWhileAuthenticating, + sm->authAuthEapLogoffWhileAuthenticating, + sm->authAuthReauthsWhileAuthenticated, + sm->authAuthEapStartsWhileAuthenticated, + sm->authAuthEapLogoffWhileAuthenticated); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + + ret = os_snprintf(pos, end - pos, + "be_auth_state=%s\n" + "eapNoReq=%s\n" + "eapReq=%s\n" + "eapResp=%s\n" + "serverTimeout=%d\n" + "backendResponses=%d\n" + "backendAccessChallenges=%d\n" + "backendOtherRequestsToSupplicant=%d\n" + "backendAuthSuccesses=%d\n" + "backendAuthFails=%d\n", + be_auth_state_txt(sm->be_auth_state), + _SB(sm->eap_if->eapNoReq), + _SB(sm->eap_if->eapReq), + _SB(sm->eap_if->eapResp), + sm->serverTimeout, + sm->backendResponses, + sm->backendAccessChallenges, + sm->backendOtherRequestsToSupplicant, + sm->backendAuthSuccesses, + sm->backendAuthFails); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + + ret = os_snprintf(pos, end - pos, + "reauth_timer_state=%s\n" + "reAuthPeriod=%d\n" + "reAuthEnabled=%s\n", + reauth_timer_state_txt(sm->reauth_timer_state), + sm->reAuthPeriod, + _SB(sm->reAuthEnabled)); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + + ret = os_snprintf(pos, end - pos, + "auth_key_tx_state=%s\n", + auth_key_tx_state_txt(sm->auth_key_tx_state)); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + + ret = os_snprintf(pos, end - pos, + "key_rx_state=%s\n" + "rxKey=%s\n", + key_rx_state_txt(sm->key_rx_state), + _SB(sm->rxKey)); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + + ret = os_snprintf(pos, end - pos, + "ctrl_dir_state=%s\n" + "adminControlledDirections=%s\n" + "operControlledDirections=%s\n" + "operEdge=%s\n", + ctrl_dir_state_txt(sm->ctrl_dir_state), + ctrl_dir_txt(sm->adminControlledDirections), + ctrl_dir_txt(sm->operControlledDirections), + _SB(sm->operEdge)); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; #undef _SB + + return pos - buf; } diff -Nru wpa-1.0/src/eapol_auth/eapol_auth_sm.c wpa-2.1/src/eapol_auth/eapol_auth_sm.c --- wpa-1.0/src/eapol_auth/eapol_auth_sm.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/eapol_auth/eapol_auth_sm.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * IEEE 802.1X-2004 Authenticator - EAPOL state machine * Copyright (c) 2002-2009, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -763,7 +757,8 @@ struct eapol_state_machine * eapol_auth_alloc(struct eapol_authenticator *eapol, const u8 *addr, int flags, const struct wpabuf *assoc_wps_ie, - const struct wpabuf *assoc_p2p_ie, void *sta_ctx) + const struct wpabuf *assoc_p2p_ie, void *sta_ctx, + const char *identity, const char *radius_cui) { struct eapol_state_machine *sm; struct eap_config eap_conf; @@ -835,6 +830,8 @@ eap_conf.fragment_size = eapol->conf.fragment_size; eap_conf.pwd_group = eapol->conf.pwd_group; eap_conf.pbc_in_m1 = eapol->conf.pbc_in_m1; + eap_conf.server_id = eapol->conf.server_id; + eap_conf.server_id_len = eapol->conf.server_id_len; sm->eap = eap_server_sm_init(sm, &eapol_cb, &eap_conf); if (sm->eap == NULL) { eapol_auth_free(sm); @@ -844,6 +841,15 @@ eapol_auth_initialize(sm); + if (identity) { + sm->identity = (u8 *) os_strdup(identity); + if (sm->identity) + sm->identity_len = os_strlen(identity); + } + if (radius_cui) + sm->radius_cui = wpabuf_alloc_copy(radius_cui, + os_strlen(radius_cui)); + return sm; } @@ -1041,6 +1047,8 @@ os_free(dst->eap_req_id_text); dst->pwd_group = src->pwd_group; dst->pbc_in_m1 = src->pbc_in_m1; + dst->server_id = src->server_id; + dst->server_id_len = src->server_id_len; if (src->eap_req_id_text) { dst->eap_req_id_text = os_malloc(src->eap_req_id_text_len); if (dst->eap_req_id_text == NULL) @@ -1054,6 +1062,10 @@ } if (src->pac_opaque_encr_key) { dst->pac_opaque_encr_key = os_malloc(16); + if (dst->pac_opaque_encr_key == NULL) { + os_free(dst->eap_req_id_text); + return -1; + } os_memcpy(dst->pac_opaque_encr_key, src->pac_opaque_encr_key, 16); } else @@ -1062,6 +1074,7 @@ dst->eap_fast_a_id = os_malloc(src->eap_fast_a_id_len); if (dst->eap_fast_a_id == NULL) { os_free(dst->eap_req_id_text); + os_free(dst->pac_opaque_encr_key); return -1; } os_memcpy(dst->eap_fast_a_id, src->eap_fast_a_id, @@ -1073,6 +1086,7 @@ dst->eap_fast_a_id_info = os_strdup(src->eap_fast_a_id_info); if (dst->eap_fast_a_id_info == NULL) { os_free(dst->eap_req_id_text); + os_free(dst->pac_opaque_encr_key); os_free(dst->eap_fast_a_id); return -1; } diff -Nru wpa-1.0/src/eapol_auth/eapol_auth_sm.h wpa-2.1/src/eapol_auth/eapol_auth_sm.h --- wpa-1.0/src/eapol_auth/eapol_auth_sm.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/eapol_auth/eapol_auth_sm.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * IEEE 802.1X-2004 Authenticator - EAPOL state machine * Copyright (c) 2002-2009, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef EAPOL_AUTH_SM_H @@ -43,6 +37,8 @@ int fragment_size; u16 pwd_group; int pbc_in_m1; + const u8 *server_id; + size_t server_id_len; /* Opaque context pointer to owner data for callback functions */ void *ctx; @@ -83,11 +79,12 @@ struct eapol_state_machine * eapol_auth_alloc(struct eapol_authenticator *eapol, const u8 *addr, int flags, const struct wpabuf *assoc_wps_ie, - const struct wpabuf *assoc_p2p_ie, void *sta_ctx); + const struct wpabuf *assoc_p2p_ie, void *sta_ctx, + const char *identity, const char *radius_cui); void eapol_auth_free(struct eapol_state_machine *sm); void eapol_auth_step(struct eapol_state_machine *sm); -void eapol_auth_dump_state(FILE *f, const char *prefix, - struct eapol_state_machine *sm); +int eapol_auth_dump_state(struct eapol_state_machine *sm, char *buf, + size_t buflen); int eapol_auth_eap_pending_cb(struct eapol_state_machine *sm, void *ctx); #endif /* EAPOL_AUTH_SM_H */ diff -Nru wpa-1.0/src/eapol_auth/eapol_auth_sm_i.h wpa-2.1/src/eapol_auth/eapol_auth_sm_i.h --- wpa-1.0/src/eapol_auth/eapol_auth_sm_i.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/eapol_auth/eapol_auth_sm_i.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * IEEE 802.1X-2004 Authenticator - EAPOL state machine (internal definitions) * Copyright (c) 2002-2009, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef EAPOL_AUTH_SM_I_H @@ -163,6 +157,7 @@ * Authentication server */ u8 eap_type_supp; /* EAP type of the last EAP packet from Supplicant */ struct radius_class_data radius_class; + struct wpabuf *radius_cui; /* Chargeable-User-Identity */ /* Keys for encrypting and signing EAPOL-Key frames */ u8 *eapol_key_sign; diff -Nru wpa-1.0/src/eapol_auth/Makefile wpa-2.1/src/eapol_auth/Makefile --- wpa-1.0/src/eapol_auth/Makefile 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/eapol_auth/Makefile 2014-02-04 11:23:35.000000000 +0000 @@ -2,7 +2,7 @@ @echo Nothing to be made. clean: - rm -f *~ *.o *.d + rm -f *~ *.o *.d *.gcno *.gcda *.gcov install: @echo Nothing to be made. diff -Nru wpa-1.0/src/eapol_supp/eapol_supp_sm.c wpa-2.1/src/eapol_supp/eapol_supp_sm.c --- wpa-1.0/src/eapol_supp/eapol_supp_sm.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/eapol_supp/eapol_supp_sm.c 2014-02-04 11:23:35.000000000 +0000 @@ -1,15 +1,9 @@ /* * EAPOL supplicant state machines - * Copyright (c) 2004-2008, Jouni Malinen + * Copyright (c) 2004-2012, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -22,6 +16,7 @@ #include "crypto/md5.h" #include "common/eapol_common.h" #include "eap_peer/eap.h" +#include "eap_peer/eap_proxy.h" #include "eapol_supp_sm.h" #define STATE_MACHINE_DATA struct eapol_sm @@ -142,47 +137,14 @@ Boolean cached_pmk; Boolean unicast_key_received, broadcast_key_received; -}; + Boolean force_authorized_update; -#define IEEE8021X_REPLAY_COUNTER_LEN 8 -#define IEEE8021X_KEY_SIGN_LEN 16 -#define IEEE8021X_KEY_IV_LEN 16 - -#define IEEE8021X_KEY_INDEX_FLAG 0x80 -#define IEEE8021X_KEY_INDEX_MASK 0x03 - -#ifdef _MSC_VER -#pragma pack(push, 1) -#endif /* _MSC_VER */ - -struct ieee802_1x_eapol_key { - u8 type; - /* Note: key_length is unaligned */ - u8 key_length[2]; - /* does not repeat within the life of the keying material used to - * encrypt the Key field; 64-bit NTP timestamp MAY be used here */ - u8 replay_counter[IEEE8021X_REPLAY_COUNTER_LEN]; - u8 key_iv[IEEE8021X_KEY_IV_LEN]; /* cryptographically random number */ - u8 key_index; /* key flag in the most significant bit: - * 0 = broadcast (default key), - * 1 = unicast (key mapping key); key index is in the - * 7 least significant bits */ - /* HMAC-MD5 message integrity check computed with MS-MPPE-Send-Key as - * the key */ - u8 key_signature[IEEE8021X_KEY_SIGN_LEN]; - - /* followed by key: if packet body length = 44 + key length, then the - * key field (of key_length bytes) contains the key in encrypted form; - * if packet body length = 44, key field is absent and key_length - * represents the number of least significant octets from - * MS-MPPE-Send-Key attribute to be used as the keying material; - * RC4 key used in encryption = Key-IV + MS-MPPE-Recv-Key */ -} STRUCT_PACKED; - -#ifdef _MSC_VER -#pragma pack(pop) -#endif /* _MSC_VER */ +#ifdef CONFIG_EAP_PROXY + Boolean use_eap_proxy; + struct eap_proxy_sm *eap_proxy; +#endif /* CONFIG_EAP_PROXY */ +}; static void eapol_sm_txLogoff(struct eapol_sm *sm); @@ -251,7 +213,6 @@ SM_ENTRY(SUPP_PAE, LOGOFF); eapol_sm_txLogoff(sm); sm->logoffSent = TRUE; - sm->suppPortStatus = Unauthorized; eapol_sm_set_port_unauthorized(sm); } @@ -262,12 +223,20 @@ sm->sPortMode = Auto; sm->startCount = 0; sm->logoffSent = FALSE; - sm->suppPortStatus = Unauthorized; eapol_sm_set_port_unauthorized(sm); sm->suppAbort = TRUE; sm->unicast_key_received = FALSE; sm->broadcast_key_received = FALSE; + + /* + * IEEE Std 802.1X-2004 does not clear heldWhile here, but doing so + * allows the timer tick to be stopped more quickly when the port is + * not enabled. Since this variable is used only within HELD state, + * clearing it on initialization does not change actual state machine + * behavior. + */ + sm->heldWhile = 0; } @@ -318,7 +287,6 @@ SM_ENTRY(SUPP_PAE, HELD); sm->heldWhile = sm->heldPeriod; eapol_enable_timer_tick(sm); - sm->suppPortStatus = Unauthorized; eapol_sm_set_port_unauthorized(sm); sm->cb_status = EAPOL_CB_FAILURE; } @@ -327,7 +295,6 @@ SM_STATE(SUPP_PAE, AUTHENTICATED) { SM_ENTRY(SUPP_PAE, AUTHENTICATED); - sm->suppPortStatus = Authorized; eapol_sm_set_port_authorized(sm); sm->cb_status = EAPOL_CB_SUCCESS; } @@ -343,7 +310,6 @@ SM_STATE(SUPP_PAE, S_FORCE_AUTH) { SM_ENTRY(SUPP_PAE, S_FORCE_AUTH); - sm->suppPortStatus = Authorized; eapol_sm_set_port_authorized(sm); sm->sPortMode = ForceAuthorized; } @@ -352,7 +318,6 @@ SM_STATE(SUPP_PAE, S_FORCE_UNAUTH) { SM_ENTRY(SUPP_PAE, S_FORCE_UNAUTH); - sm->suppPortStatus = Unauthorized; eapol_sm_set_port_unauthorized(sm); sm->sPortMode = ForceUnauthorized; eapol_sm_txLogoff(sm); @@ -500,6 +465,17 @@ sm->keyRun = TRUE; sm->suppSuccess = TRUE; +#ifdef CONFIG_EAP_PROXY + if (sm->use_eap_proxy) { + if (eap_proxy_key_available(sm->eap_proxy)) { + /* New key received - clear IEEE 802.1X EAPOL-Key replay + * counter */ + sm->replay_counter_valid = FALSE; + } + return; + } +#endif /* CONFIG_EAP_PROXY */ + if (eap_key_available(sm->eap)) { /* New key received - clear IEEE 802.1X EAPOL-Key replay * counter */ @@ -535,6 +511,15 @@ SM_ENTRY(SUPP_BE, INITIALIZE); eapol_sm_abortSupp(sm); sm->suppAbort = FALSE; + + /* + * IEEE Std 802.1X-2004 does not clear authWhile here, but doing so + * allows the timer tick to be stopped more quickly when the port is + * not enabled. Since this variable is used only within RECEIVE state, + * clearing it on initialization does not change actual state machine + * behavior. + */ + sm->authWhile = 0; } @@ -652,6 +637,7 @@ static void eapol_sm_processKey(struct eapol_sm *sm) { +#ifndef CONFIG_FIPS struct ieee802_1x_hdr *hdr; struct ieee802_1x_eapol_key *key; struct eap_key_data keydata; @@ -659,6 +645,7 @@ u8 ekey[IEEE8021X_KEY_IV_LEN + IEEE8021X_ENCR_KEY_LEN]; int key_len, res, sign_key_len, encr_key_len; u16 rx_key_length; + size_t plen; wpa_printf(MSG_DEBUG, "EAPOL: processKey"); if (sm->last_rx_key == NULL) @@ -671,9 +658,12 @@ return; } + if (sm->last_rx_key_len < sizeof(*hdr) + sizeof(*key)) + return; hdr = (struct ieee802_1x_hdr *) sm->last_rx_key; key = (struct ieee802_1x_eapol_key *) (hdr + 1); - if (sizeof(*hdr) + be_to_host16(hdr->length) > sm->last_rx_key_len) { + plen = be_to_host16(hdr->length); + if (sizeof(*hdr) + plen > sm->last_rx_key_len || plen < sizeof(*key)) { wpa_printf(MSG_WARNING, "EAPOL: Too short EAPOL-Key frame"); return; } @@ -739,7 +729,7 @@ } wpa_printf(MSG_DEBUG, "EAPOL: EAPOL-Key key signature verified"); - key_len = be_to_host16(hdr->length) - sizeof(*key); + key_len = plen - sizeof(*key); if (key_len > 32 || rx_key_length > 32) { wpa_printf(MSG_WARNING, "EAPOL: Too long key data length %d", key_len ? key_len : rx_key_length); @@ -810,6 +800,7 @@ sm->ctx->eapol_done_cb(sm->ctx->ctx); } } +#endif /* CONFIG_FIPS */ } @@ -828,6 +819,19 @@ struct wpabuf *resp; wpa_printf(MSG_DEBUG, "EAPOL: txSuppRsp"); + +#ifdef CONFIG_EAP_PROXY + if (sm->use_eap_proxy) { + /* Get EAP Response from EAP Proxy */ + resp = eap_proxy_get_eapRespData(sm->eap_proxy); + if (resp == NULL) { + wpa_printf(MSG_WARNING, "EAPOL: txSuppRsp - EAP Proxy " + "response data not available"); + return; + } + } else +#endif /* CONFIG_EAP_PROXY */ + resp = eap_get_eapRespData(sm->eap); if (resp == NULL) { wpa_printf(MSG_WARNING, "EAPOL: txSuppRsp - EAP response data " @@ -872,14 +876,24 @@ static void eapol_sm_set_port_authorized(struct eapol_sm *sm) { - if (sm->ctx->port_cb) + int cb; + + cb = sm->suppPortStatus != Authorized || sm->force_authorized_update; + sm->force_authorized_update = FALSE; + sm->suppPortStatus = Authorized; + if (cb && sm->ctx->port_cb) sm->ctx->port_cb(sm->ctx->ctx, 1); } static void eapol_sm_set_port_unauthorized(struct eapol_sm *sm) { - if (sm->ctx->port_cb) + int cb; + + cb = sm->suppPortStatus != Unauthorized || sm->force_authorized_update; + sm->force_authorized_update = FALSE; + sm->suppPortStatus = Unauthorized; + if (cb && sm->ctx->port_cb) sm->ctx->port_cb(sm->ctx->ctx, 0); } @@ -905,6 +919,13 @@ SM_STEP_RUN(SUPP_PAE); SM_STEP_RUN(KEY_RX); SM_STEP_RUN(SUPP_BE); +#ifdef CONFIG_EAP_PROXY + if (sm->use_eap_proxy) { + /* Drive the EAP proxy state machine */ + if (eap_proxy_sm_step(sm->eap_proxy, sm->eap)) + sm->changed = TRUE; + } else +#endif /* CONFIG_EAP_PROXY */ if (eap_peer_sm_step(sm->eap)) sm->changed = TRUE; if (!sm->changed) @@ -919,9 +940,15 @@ } if (sm->ctx->cb && sm->cb_status != EAPOL_CB_IN_PROGRESS) { - int success = sm->cb_status == EAPOL_CB_SUCCESS ? 1 : 0; + enum eapol_supp_result result; + if (sm->cb_status == EAPOL_CB_SUCCESS) + result = EAPOL_SUPP_RESULT_SUCCESS; + else if (eap_peer_was_failure_expected(sm->eap)) + result = EAPOL_SUPP_RESULT_EXPECTED_FAILURE; + else + result = EAPOL_SUPP_RESULT_FAILURE; sm->cb_status = EAPOL_CB_IN_PROGRESS; - sm->ctx->cb(sm, success, sm->ctx->cb_ctx); + sm->ctx->cb(sm, result, sm->ctx->cb_ctx); } } @@ -1092,6 +1119,13 @@ len += ret; } +#ifdef CONFIG_EAP_PROXY + if (sm->use_eap_proxy) + len += eap_proxy_sm_get_status(sm->eap_proxy, + buf + len, buflen - len, + verbose); + else +#endif /* CONFIG_EAP_PROXY */ len += eap_sm_get_status(sm->eap, buf + len, buflen - len, verbose); return len; @@ -1236,6 +1270,24 @@ switch (hdr->type) { case IEEE802_1X_TYPE_EAP_PACKET: + if (sm->conf.workaround) { + /* + * An AP has been reported to send out EAP message with + * undocumented code 10 at some point near the + * completion of EAP authentication. This can result in + * issues with the unexpected EAP message triggering + * restart of EAPOL authentication. Avoid this by + * skipping the message without advancing the state + * machine. + */ + const struct eap_hdr *ehdr = + (const struct eap_hdr *) (hdr + 1); + if (plen >= sizeof(*ehdr) && ehdr->code == 10) { + wpa_printf(MSG_DEBUG, "EAPOL: Ignore EAP packet with unknown code 10"); + break; + } + } + if (sm->cached_pmk) { /* Trying to use PMKSA caching, but Authenticator did * not seem to have a matching entry. Need to restart @@ -1249,6 +1301,16 @@ wpa_printf(MSG_DEBUG, "EAPOL: Received EAP-Packet " "frame"); sm->eapolEap = TRUE; +#ifdef CONFIG_EAP_PROXY + if (sm->use_eap_proxy) { + eap_proxy_packet_update( + sm->eap_proxy, + wpabuf_mhead_u8(sm->eapReqData), + wpabuf_len(sm->eapReqData)); + wpa_printf(MSG_DEBUG, "EAPOL: eap_proxy " + "EAP Req updated"); + } +#endif /* CONFIG_EAP_PROXY */ eapol_sm_step(sm); } break; @@ -1321,6 +1383,8 @@ return; wpa_printf(MSG_DEBUG, "EAPOL: External notification - " "portEnabled=%d", enabled); + if (sm->portEnabled != enabled) + sm->force_authorized_update = TRUE; sm->portEnabled = enabled; eapol_sm_step(sm); } @@ -1409,6 +1473,9 @@ return; sm->config = config; +#ifdef CONFIG_EAP_PROXY + sm->use_eap_proxy = eap_proxy_notify_config(sm->eap_proxy, config) > 0; +#endif /* CONFIG_EAP_PROXY */ if (conf == NULL) return; @@ -1417,10 +1484,17 @@ sm->conf.required_keys = conf->required_keys; sm->conf.fast_reauth = conf->fast_reauth; sm->conf.workaround = conf->workaround; +#ifdef CONFIG_EAP_PROXY + if (sm->use_eap_proxy) { + /* Using EAP Proxy, so skip EAP state machine update */ + return; + } +#endif /* CONFIG_EAP_PROXY */ if (sm->eap) { eap_set_fast_reauth(sm->eap, conf->fast_reauth); eap_set_workaround(sm->eap, conf->workaround); eap_set_force_disabled(sm->eap, conf->eap_disabled); + eap_set_external_sim(sm->eap, conf->external_sim); } } @@ -1441,6 +1515,22 @@ const u8 *eap_key; size_t eap_len; +#ifdef CONFIG_EAP_PROXY + if (sm->use_eap_proxy) { + /* Get key from EAP proxy */ + if (sm == NULL || !eap_proxy_key_available(sm->eap_proxy)) { + wpa_printf(MSG_DEBUG, "EAPOL: EAP key not available"); + return -1; + } + eap_key = eap_proxy_get_eapKeyData(sm->eap_proxy, &eap_len); + if (eap_key == NULL) { + wpa_printf(MSG_DEBUG, "EAPOL: Failed to get " + "eapKeyData"); + return -1; + } + goto key_fetched; + } +#endif /* CONFIG_EAP_PROXY */ if (sm == NULL || !eap_key_available(sm->eap)) { wpa_printf(MSG_DEBUG, "EAPOL: EAP key not available"); return -1; @@ -1450,6 +1540,9 @@ wpa_printf(MSG_DEBUG, "EAPOL: Failed to get eapKeyData"); return -1; } +#ifdef CONFIG_EAP_PROXY +key_fetched: +#endif /* CONFIG_EAP_PROXY */ if (len > eap_len) { wpa_printf(MSG_DEBUG, "EAPOL: Requested key length (%lu) not " "available (len=%lu)", @@ -1474,6 +1567,10 @@ { if (sm) { sm->userLogoff = logoff; + if (!logoff) { + /* If there is a delayed txStart queued, start now. */ + sm->startWhen = 0; + } eapol_sm_step(sm); } } @@ -1491,10 +1588,7 @@ if (sm == NULL) return; wpa_printf(MSG_DEBUG, "EAPOL: PMKSA caching was used - skip EAPOL"); - sm->SUPP_PAE_state = SUPP_PAE_AUTHENTICATED; - sm->suppPortStatus = Authorized; - eapol_sm_set_port_authorized(sm); - sm->portValid = TRUE; + sm->eapSuccess = TRUE; eap_notify_success(sm->eap); eapol_sm_step(sm); } @@ -1529,7 +1623,6 @@ return; sm->cached_pmk = FALSE; sm->SUPP_PAE_state = SUPP_PAE_CONNECTING; - sm->suppPortStatus = Unauthorized; eapol_sm_set_port_unauthorized(sm); /* Make sure we do not start sending EAPOL-Start frames first, but @@ -1766,7 +1859,8 @@ switch (variable) { case EAPOL_idleWhile: sm->idleWhile = value; - eapol_enable_timer_tick(sm); + if (sm->idleWhile > 0) + eapol_enable_timer_tick(sm); break; } } @@ -1835,6 +1929,26 @@ cert_hash, cert); } + +static void eapol_sm_notify_status(void *ctx, const char *status, + const char *parameter) +{ + struct eapol_sm *sm = ctx; + + if (sm->ctx->status_cb) + sm->ctx->status_cb(sm->ctx->ctx, status, parameter); +} + + +static void eapol_sm_set_anon_id(void *ctx, const u8 *id, size_t len) +{ + struct eapol_sm *sm = ctx; + + if (sm->ctx->set_anon_id) + sm->ctx->set_anon_id(sm->ctx->ctx, id, len); +} + + static struct eapol_callbacks eapol_cb = { eapol_sm_get_config, @@ -1847,7 +1961,9 @@ eapol_sm_get_config_blob, eapol_sm_notify_pending, eapol_sm_eap_param_needed, - eapol_sm_notify_cert + eapol_sm_notify_cert, + eapol_sm_notify_status, + eapol_sm_set_anon_id }; @@ -1891,7 +2007,16 @@ return NULL; } +#ifdef CONFIG_EAP_PROXY + sm->use_eap_proxy = FALSE; + sm->eap_proxy = eap_proxy_init(sm, &eapol_cb, sm->ctx->msg_ctx); + if (sm->eap_proxy == NULL) { + wpa_printf(MSG_ERROR, "Unable to initialize EAP Proxy"); + } +#endif /* CONFIG_EAP_PROXY */ + /* Initialize EAPOL state machines */ + sm->force_authorized_update = TRUE; sm->initialize = TRUE; eapol_sm_step(sm); sm->initialize = FALSE; @@ -1917,8 +2042,39 @@ eloop_cancel_timeout(eapol_sm_step_timeout, NULL, sm); eloop_cancel_timeout(eapol_port_timers_tick, NULL, sm); eap_peer_sm_deinit(sm->eap); +#ifdef CONFIG_EAP_PROXY + eap_proxy_deinit(sm->eap_proxy); +#endif /* CONFIG_EAP_PROXY */ os_free(sm->last_rx_key); wpabuf_free(sm->eapReqData); os_free(sm->ctx); os_free(sm); } + + +void eapol_sm_set_ext_pw_ctx(struct eapol_sm *sm, + struct ext_password_data *ext) +{ + if (sm && sm->eap) + eap_sm_set_ext_pw_ctx(sm->eap, ext); +} + + +int eapol_sm_failed(struct eapol_sm *sm) +{ + if (sm == NULL) + return 0; + return !sm->eapSuccess && sm->eapFail; +} + + +int eapol_sm_get_eap_proxy_imsi(struct eapol_sm *sm, char *imsi, size_t *len) +{ +#ifdef CONFIG_EAP_PROXY + if (sm->eap_proxy == NULL) + return -1; + return eap_proxy_get_imsi(sm->eap_proxy, imsi, len); +#else /* CONFIG_EAP_PROXY */ + return -1; +#endif /* CONFIG_EAP_PROXY */ +} diff -Nru wpa-1.0/src/eapol_supp/eapol_supp_sm.h wpa-2.1/src/eapol_supp/eapol_supp_sm.h --- wpa-1.0/src/eapol_supp/eapol_supp_sm.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/eapol_supp/eapol_supp_sm.h 2014-02-04 11:23:35.000000000 +0000 @@ -1,15 +1,9 @@ /* * EAPOL supplicant state machines - * Copyright (c) 2004-2008, Jouni Malinen + * Copyright (c) 2004-2012, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef EAPOL_SUPP_SM_H @@ -59,11 +53,22 @@ * eap_disabled - Whether EAP is disabled */ int eap_disabled; + + /** + * external_sim - Use external processing for SIM/USIM operations + */ + int external_sim; }; struct eapol_sm; struct wpa_config_blob; +enum eapol_supp_result { + EAPOL_SUPP_RESULT_FAILURE, + EAPOL_SUPP_RESULT_SUCCESS, + EAPOL_SUPP_RESULT_EXPECTED_FAILURE +}; + /** * struct eapol_ctx - Global (for all networks) EAPOL state machine context */ @@ -84,7 +89,7 @@ /** * cb - Function to be called when EAPOL negotiation has been completed * @eapol: Pointer to EAPOL state machine data - * @success: Whether the authentication was completed successfully + * @result: Whether the authentication was completed successfully * @ctx: Pointer to context data (cb_ctx) * * This optional callback function will be called when the EAPOL @@ -92,7 +97,8 @@ * EAPOL state machine to process the key and terminate the EAPOL state * machine. Currently, this is used only in RSN pre-authentication. */ - void (*cb)(struct eapol_sm *eapol, int success, void *ctx); + void (*cb)(struct eapol_sm *eapol, enum eapol_supp_result result, + void *ctx); /** * cb_ctx - Callback context for cb() @@ -236,10 +242,28 @@ * cert_in_cb - Include server certificates in callback */ int cert_in_cb; + + /** + * status_cb - Notification of a change in EAP status + * @ctx: Callback context (ctx) + * @status: Step in the process of EAP authentication + * @parameter: Step-specific parameter, e.g., EAP method name + */ + void (*status_cb)(void *ctx, const char *status, + const char *parameter); + + /** + * set_anon_id - Set or add anonymous identity + * @ctx: eapol_ctx from eap_peer_sm_init() call + * @id: Anonymous identity (e.g., EAP-SIM pseudonym) + * @len: Length of anonymous identity in octets + */ + void (*set_anon_id)(void *ctx, const u8 *id, size_t len); }; struct eap_peer_config; +struct ext_password_data; #ifdef IEEE8021X_EAPOL struct eapol_sm *eapol_sm_init(struct eapol_ctx *ctx); @@ -272,6 +296,10 @@ void eapol_sm_notify_lower_layer_success(struct eapol_sm *sm, int in_eapol_sm); void eapol_sm_invalidate_cached_session(struct eapol_sm *sm); const char * eapol_sm_get_method_name(struct eapol_sm *sm); +void eapol_sm_set_ext_pw_ctx(struct eapol_sm *sm, + struct ext_password_data *ext); +int eapol_sm_failed(struct eapol_sm *sm); +int eapol_sm_get_eap_proxy_imsi(struct eapol_sm *sm, char *imsi, size_t *len); #else /* IEEE8021X_EAPOL */ static inline struct eapol_sm *eapol_sm_init(struct eapol_ctx *ctx) { @@ -363,6 +391,14 @@ { return NULL; } +static inline void eapol_sm_set_ext_pw_ctx(struct eapol_sm *sm, + struct ext_password_data *ext) +{ +} +static inline int eapol_sm_failed(struct eapol_sm *sm) +{ + return 0; +} #endif /* IEEE8021X_EAPOL */ #endif /* EAPOL_SUPP_SM_H */ diff -Nru wpa-1.0/src/eapol_supp/Makefile wpa-2.1/src/eapol_supp/Makefile --- wpa-1.0/src/eapol_supp/Makefile 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/eapol_supp/Makefile 2014-02-04 11:23:35.000000000 +0000 @@ -2,7 +2,7 @@ @echo Nothing to be made. clean: - rm -f *~ *.o *.d + rm -f *~ *.o *.d *.gcno *.gcda *.gcov install: @echo Nothing to be made. diff -Nru wpa-1.0/src/eap_peer/eap_aka.c wpa-2.1/src/eap_peer/eap_aka.c --- wpa-1.0/src/eap_peer/eap_aka.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/eap_peer/eap_aka.c 2014-02-04 11:23:35.000000000 +0000 @@ -1,15 +1,9 @@ /* - * EAP peer method: EAP-AKA (RFC 4187) and EAP-AKA' (draft-arkko-eap-aka-kdf) - * Copyright (c) 2004-2008, Jouni Malinen + * EAP peer method: EAP-AKA (RFC 4187) and EAP-AKA' (RFC 5448) + * Copyright (c) 2004-2012, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -96,6 +90,7 @@ { struct eap_aka_data *data; const char *phase1 = eap_get_config_phase1(sm); + struct eap_peer_config *config = eap_get_config(sm); data = os_zalloc(sizeof(*data)); if (data == NULL) @@ -108,6 +103,15 @@ data->result_ind = phase1 && os_strstr(phase1, "result_ind=1") != NULL; + if (config && config->anonymous_identity) { + data->pseudonym = os_malloc(config->anonymous_identity_len); + if (data->pseudonym) { + os_memcpy(data->pseudonym, config->anonymous_identity, + config->anonymous_identity_len); + data->pseudonym_len = config->anonymous_identity_len; + } + } + return data; } @@ -138,6 +142,89 @@ } +static int eap_aka_ext_sim_req(struct eap_sm *sm, struct eap_aka_data *data) +{ + char req[200], *pos, *end; + + wpa_printf(MSG_DEBUG, "EAP-AKA: Use external USIM processing"); + pos = req; + end = pos + sizeof(req); + pos += os_snprintf(pos, end - pos, "UMTS-AUTH"); + pos += os_snprintf(pos, end - pos, ":"); + pos += wpa_snprintf_hex(pos, end - pos, data->rand, EAP_AKA_RAND_LEN); + pos += os_snprintf(pos, end - pos, ":"); + pos += wpa_snprintf_hex(pos, end - pos, data->autn, EAP_AKA_AUTN_LEN); + + eap_sm_request_sim(sm, req); + return 1; +} + + +static int eap_aka_ext_sim_result(struct eap_sm *sm, struct eap_aka_data *data, + struct eap_peer_config *conf) +{ + char *resp, *pos; + + wpa_printf(MSG_DEBUG, + "EAP-AKA: Use result from external USIM processing"); + + resp = conf->external_sim_resp; + conf->external_sim_resp = NULL; + + if (os_strncmp(resp, "UMTS-AUTS:", 10) == 0) { + pos = resp + 10; + if (hexstr2bin(pos, data->auts, EAP_AKA_AUTS_LEN) < 0) + goto invalid; + wpa_hexdump_key(MSG_DEBUG, "EAP-AKA: AUTS", data->auts, + EAP_AKA_AUTS_LEN); + os_free(resp); + return -2; + } + + if (os_strncmp(resp, "UMTS-AUTH:", 10) != 0) { + wpa_printf(MSG_DEBUG, "EAP-AKA: Unrecognized external USIM processing response"); + os_free(resp); + return -1; + } + + pos = resp + 10; + wpa_hexdump(MSG_DEBUG, "EAP-AKA: RAND", data->rand, EAP_AKA_RAND_LEN); + + if (hexstr2bin(pos, data->ik, EAP_AKA_IK_LEN) < 0) + goto invalid; + wpa_hexdump_key(MSG_DEBUG, "EAP-AKA: IK", data->ik, EAP_AKA_IK_LEN); + pos += EAP_AKA_IK_LEN * 2; + if (*pos != ':') + goto invalid; + pos++; + + if (hexstr2bin(pos, data->ck, EAP_AKA_CK_LEN) < 0) + goto invalid; + wpa_hexdump_key(MSG_DEBUG, "EAP-AKA: CK", data->ck, EAP_AKA_CK_LEN); + pos += EAP_AKA_CK_LEN * 2; + if (*pos != ':') + goto invalid; + pos++; + + data->res_len = os_strlen(pos) / 2; + if (data->res_len > EAP_AKA_RES_MAX_LEN) { + data->res_len = 0; + goto invalid; + } + if (hexstr2bin(pos, data->res, data->res_len) < 0) + goto invalid; + wpa_hexdump_key(MSG_DEBUG, "EAP-AKA: RES", data->res, data->res_len); + + os_free(resp); + return 0; + +invalid: + wpa_printf(MSG_DEBUG, "EAP-AKA: Invalid external USIM processing UMTS-AUTH response"); + os_free(resp); + return -1; +} + + static int eap_aka_umts_auth(struct eap_sm *sm, struct eap_aka_data *data) { struct eap_peer_config *conf; @@ -147,6 +234,14 @@ conf = eap_get_config(sm); if (conf == NULL) return -1; + + if (sm->external_sim) { + if (conf->external_sim_resp) + return eap_aka_ext_sim_result(sm, data, conf); + else + return eap_aka_ext_sim_req(sm, data); + } + if (conf->pcsc) { return scard_umts_auth(sm->scard_ctx, data->rand, data->autn, data->res, &data->res_len, @@ -233,13 +328,15 @@ #define CLEAR_REAUTH_ID 0x02 #define CLEAR_EAP_ID 0x04 -static void eap_aka_clear_identities(struct eap_aka_data *data, int id) +static void eap_aka_clear_identities(struct eap_sm *sm, + struct eap_aka_data *data, int id) { if ((id & CLEAR_PSEUDONYM) && data->pseudonym) { wpa_printf(MSG_DEBUG, "EAP-AKA: forgetting old pseudonym"); os_free(data->pseudonym); data->pseudonym = NULL; data->pseudonym_len = 0; + eap_set_anon_id(sm, NULL, 0); } if ((id & CLEAR_REAUTH_ID) && data->reauth_id) { wpa_printf(MSG_DEBUG, "EAP-AKA: forgetting old reauth_id"); @@ -294,6 +391,7 @@ realm, realm_len); } data->pseudonym_len = attr->next_pseudonym_len + realm_len; + eap_set_anon_id(sm, data->pseudonym, data->pseudonym_len); } if (attr->next_reauth_id) { @@ -431,6 +529,8 @@ data->num_id_req = 0; data->num_notification = 0; + wpa_printf(MSG_DEBUG, "EAP-AKA: Send Client-Error (error code %d)", + err); msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method, EAP_AKA_SUBTYPE_CLIENT_ERROR); eap_sim_msg_add(msg, EAP_SIM_AT_CLIENT_ERROR_CODE, err, NULL, 0); @@ -492,16 +592,16 @@ data->pseudonym) { identity = data->pseudonym; identity_len = data->pseudonym_len; - eap_aka_clear_identities(data, CLEAR_REAUTH_ID); + eap_aka_clear_identities(sm, data, CLEAR_REAUTH_ID); } else if (id_req != NO_ID_REQ) { identity = eap_get_config_identity(sm, &identity_len); if (identity) { - eap_aka_clear_identities(data, CLEAR_PSEUDONYM | + eap_aka_clear_identities(sm, data, CLEAR_PSEUDONYM | CLEAR_REAUTH_ID); } } if (id_req != NO_ID_REQ) - eap_aka_clear_identities(data, CLEAR_EAP_ID); + eap_aka_clear_identities(sm, data, CLEAR_EAP_ID); wpa_printf(MSG_DEBUG, "Generating EAP-AKA Identity (id=%d)", id); msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method, @@ -852,6 +952,9 @@ wpa_printf(MSG_WARNING, "EAP-AKA: UMTS authentication " "failed (AUTN seq# -> AUTS)"); return eap_aka_synchronization_failure(data, id); + } else if (res > 0) { + wpa_printf(MSG_DEBUG, "EAP-AKA: Wait for external USIM processing"); + return NULL; } else if (res) { wpa_printf(MSG_WARNING, "EAP-AKA: UMTS authentication failed"); return eap_aka_client_error(data, id, @@ -904,7 +1007,7 @@ * other words, if no new identities are received, full * authentication will be used on next reauthentication (using * pseudonym identity or permanent identity). */ - eap_aka_clear_identities(data, CLEAR_REAUTH_ID | CLEAR_EAP_ID); + eap_aka_clear_identities(sm, data, CLEAR_REAUTH_ID | CLEAR_EAP_ID); if (attr->encr_data) { u8 *decrypted; @@ -1132,7 +1235,7 @@ data->nonce_s, data->mk, data->msk, data->emsk); } - eap_aka_clear_identities(data, CLEAR_REAUTH_ID | CLEAR_EAP_ID); + eap_aka_clear_identities(sm, data, CLEAR_REAUTH_ID | CLEAR_EAP_ID); eap_aka_learn_ids(sm, data, &eattr); if (data->result_ind && attr->result_ind) @@ -1148,7 +1251,8 @@ if (data->counter > EAP_AKA_MAX_FAST_REAUTHS) { wpa_printf(MSG_DEBUG, "EAP-AKA: Maximum number of " "fast reauths performed - force fullauth"); - eap_aka_clear_identities(data, CLEAR_REAUTH_ID | CLEAR_EAP_ID); + eap_aka_clear_identities(sm, data, + CLEAR_REAUTH_ID | CLEAR_EAP_ID); } os_free(decrypted); return eap_aka_response_reauth(data, id, 0, data->nonce_s); @@ -1266,7 +1370,7 @@ static void eap_aka_deinit_for_reauth(struct eap_sm *sm, void *priv) { struct eap_aka_data *data = priv; - eap_aka_clear_identities(data, CLEAR_EAP_ID); + eap_aka_clear_identities(sm, data, CLEAR_EAP_ID); data->prev_id = -1; wpabuf_free(data->id_msgs); data->id_msgs = NULL; @@ -1330,6 +1434,28 @@ } +static u8 * eap_aka_get_session_id(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_aka_data *data = priv; + u8 *id; + + if (data->state != SUCCESS) + return NULL; + + *len = 1 + EAP_AKA_RAND_LEN + EAP_AKA_AUTN_LEN; + id = os_malloc(*len); + if (id == NULL) + return NULL; + + id[0] = data->eap_method; + os_memcpy(id + 1, data->rand, EAP_AKA_RAND_LEN); + os_memcpy(id + 1 + EAP_AKA_RAND_LEN, data->autn, EAP_AKA_AUTN_LEN); + wpa_hexdump(MSG_DEBUG, "EAP-AKA: Derived Session-Id", id, *len); + + return id; +} + + static u8 * eap_aka_get_emsk(struct eap_sm *sm, void *priv, size_t *len) { struct eap_aka_data *data = priv; @@ -1364,6 +1490,7 @@ eap->process = eap_aka_process; eap->isKeyAvailable = eap_aka_isKeyAvailable; eap->getKey = eap_aka_getKey; + eap->getSessionId = eap_aka_get_session_id; eap->has_reauth_data = eap_aka_has_reauth_data; eap->deinit_for_reauth = eap_aka_deinit_for_reauth; eap->init_for_reauth = eap_aka_init_for_reauth; @@ -1394,6 +1521,7 @@ eap->process = eap_aka_process; eap->isKeyAvailable = eap_aka_isKeyAvailable; eap->getKey = eap_aka_getKey; + eap->getSessionId = eap_aka_get_session_id; eap->has_reauth_data = eap_aka_has_reauth_data; eap->deinit_for_reauth = eap_aka_deinit_for_reauth; eap->init_for_reauth = eap_aka_init_for_reauth; diff -Nru wpa-1.0/src/eap_peer/eap.c wpa-2.1/src/eap_peer/eap.c --- wpa-1.0/src/eap_peer/eap.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/eap_peer/eap.c 2014-02-04 11:23:35.000000000 +0000 @@ -1,15 +1,9 @@ /* * EAP peer state machines (RFC 4137) - * Copyright (c) 2004-2010, Jouni Malinen + * Copyright (c) 2004-2014, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. * * This file implements the Peer State Machine as defined in RFC 4137. The used * states and state transitions match mostly with the RFC. However, there are @@ -26,6 +20,7 @@ #include "common.h" #include "pcsc_funcs.h" #include "state_machine.h" +#include "ext_password.h" #include "crypto/crypto.h" #include "crypto/tls.h" #include "common/wpa_ctrl.h" @@ -87,8 +82,21 @@ } +static void eap_notify_status(struct eap_sm *sm, const char *status, + const char *parameter) +{ + wpa_printf(MSG_DEBUG, "EAP: Status notification: %s (param=%s)", + status, parameter); + if (sm->eapol_cb->notify_status) + sm->eapol_cb->notify_status(sm->eapol_ctx, status, parameter); +} + + static void eap_deinit_prev_method(struct eap_sm *sm, const char *txt) { + ext_password_free(sm->ext_pw_buf); + sm->ext_pw_buf = NULL; + if (sm->m == NULL || sm->eap_method_priv == NULL) return; @@ -153,6 +161,8 @@ eapol_set_bool(sm, EAPOL_eapFail, FALSE); os_free(sm->eapKeyData); sm->eapKeyData = NULL; + os_free(sm->eapSessionId); + sm->eapSessionId = NULL; sm->eapKeyAvailable = FALSE; eapol_set_bool(sm, EAPOL_eapRestart, FALSE); sm->lastId = -1; /* new session - make sure this does not match with @@ -169,6 +179,7 @@ eapol_set_bool(sm, EAPOL_eapNoResp, FALSE); sm->num_rounds = 0; sm->prev_failure = 0; + sm->expected_failure = 0; } @@ -181,6 +192,12 @@ { SM_ENTRY(EAP, DISABLED); sm->num_rounds = 0; + /* + * RFC 4137 does not describe clearing of idleWhile here, but doing so + * allows the timer tick to be stopped more quickly when EAP is not in + * use. + */ + eapol_set_int(sm, EAPOL_idleWhile, 0); } @@ -219,6 +236,7 @@ { int reinit; EapType method; + const struct eap_method *eap_method; SM_ENTRY(EAP, GET_METHOD); @@ -227,18 +245,24 @@ else method = sm->reqMethod; + eap_method = eap_peer_get_eap_method(sm->reqVendor, method); + if (!eap_sm_allowMethod(sm, sm->reqVendor, method)) { wpa_printf(MSG_DEBUG, "EAP: vendor %u method %u not allowed", sm->reqVendor, method); wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_PROPOSED_METHOD "vendor=%u method=%u -> NAK", sm->reqVendor, method); + eap_notify_status(sm, "refuse proposed method", + eap_method ? eap_method->name : "unknown"); goto nak; } wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_PROPOSED_METHOD "vendor=%u method=%u", sm->reqVendor, method); + eap_notify_status(sm, "accept proposed method", + eap_method ? eap_method->name : "unknown"); /* * RFC 4137 does not define specific operation for fast * re-authentication (session resumption). The design here is to allow @@ -262,7 +286,7 @@ sm->selectedMethod = sm->reqMethod; if (sm->m == NULL) - sm->m = eap_peer_get_eap_method(sm->reqVendor, method); + sm->m = eap_method; if (!sm->m) { wpa_printf(MSG_DEBUG, "EAP: Could not find selected method: " "vendor %d method %d", @@ -327,6 +351,7 @@ { struct wpabuf *eapReqData; struct eap_method_ret ret; + int min_len = 1; SM_ENTRY(EAP, METHOD); if (sm->m == NULL) { @@ -335,6 +360,10 @@ } eapReqData = eapol_get_eapReqData(sm); + if (sm->m->vendor == EAP_VENDOR_IETF && sm->m->method == EAP_TYPE_LEAP) + min_len = 0; /* LEAP uses EAP-Success without payload */ + if (!eap_hdr_len_valid(eapReqData, min_len)) + return; /* * Get ignore, methodState, decision, allowNotifications, and @@ -360,10 +389,11 @@ sm->eapRespData = sm->m->process(sm, sm->eap_method_priv, &ret, eapReqData); wpa_printf(MSG_DEBUG, "EAP: method process -> ignore=%s " - "methodState=%s decision=%s", + "methodState=%s decision=%s eapRespData=%p", ret.ignore ? "TRUE" : "FALSE", eap_sm_method_state_txt(ret.methodState), - eap_sm_decision_txt(ret.decision)); + eap_sm_decision_txt(ret.decision), + sm->eapRespData); sm->ignore = ret.ignore; if (sm->ignore) @@ -377,6 +407,15 @@ os_free(sm->eapKeyData); sm->eapKeyData = sm->m->getKey(sm, sm->eap_method_priv, &sm->eapKeyDataLen); + os_free(sm->eapSessionId); + sm->eapSessionId = NULL; + if (sm->m->getSessionId) { + sm->eapSessionId = sm->m->getSessionId( + sm, sm->eap_method_priv, + &sm->eapSessionIdLen); + wpa_hexdump(MSG_DEBUG, "EAP: Session-Id", + sm->eapSessionId, sm->eapSessionIdLen); + } } } @@ -395,8 +434,10 @@ sm->lastId = sm->reqId; sm->lastRespData = wpabuf_dup(sm->eapRespData); eapol_set_bool(sm, EAPOL_eapResp, TRUE); - } else + } else { + wpa_printf(MSG_DEBUG, "EAP: No eapRespData available"); sm->lastRespData = NULL; + } eapol_set_bool(sm, EAPOL_eapReq, FALSE); eapol_set_int(sm, EAPOL_idleWhile, sm->ClientTimeout); } @@ -423,6 +464,8 @@ SM_ENTRY(EAP, IDENTITY); eapReqData = eapol_get_eapReqData(sm); + if (!eap_hdr_len_valid(eapReqData, 1)) + return; eap_sm_processIdentity(sm, eapReqData); wpabuf_free(sm->eapRespData); sm->eapRespData = NULL; @@ -439,6 +482,8 @@ SM_ENTRY(EAP, NOTIFICATION); eapReqData = eapol_get_eapReqData(sm); + if (!eap_hdr_len_valid(eapReqData, 1)) + return; eap_sm_processNotify(sm, eapReqData); wpabuf_free(sm->eapRespData); sm->eapRespData = NULL; @@ -683,8 +728,19 @@ SM_ENTER(EAP, SEND_RESPONSE); break; case EAP_METHOD: + /* + * Note: RFC 4137 uses methodState == DONE && decision == FAIL + * as the condition. eapRespData == NULL here is used to allow + * final EAP method response to be sent without having to change + * all methods to either use methodState MAY_CONT or leaving + * decision to something else than FAIL in cases where the only + * expected response is EAP-Failure. + */ if (sm->ignore) SM_ENTER(EAP, DISCARD); + else if (sm->methodState == METHOD_DONE && + sm->decision == DECISION_FAIL && !sm->eapRespData) + SM_ENTER(EAP, FAILURE); else SM_ENTER(EAP, SEND_RESPONSE); break; @@ -856,12 +912,17 @@ static void eap_sm_processIdentity(struct eap_sm *sm, const struct wpabuf *req) { - const struct eap_hdr *hdr = wpabuf_head(req); - const u8 *pos = (const u8 *) (hdr + 1); - pos++; + const u8 *pos; + size_t msg_len; wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_STARTED "EAP authentication started"); + eap_notify_status(sm, "started", ""); + + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_IDENTITY, req, + &msg_len); + if (pos == NULL) + return; /* * RFC 3748 - 5.1: Identity @@ -873,15 +934,80 @@ /* TODO: could save displayable message so that it can be shown to the * user in case of interaction is required */ wpa_hexdump_ascii(MSG_DEBUG, "EAP: EAP-Request Identity data", - pos, be_to_host16(hdr->length) - 5); + pos, msg_len); } #ifdef PCSC_FUNCS + +/* + * Rules for figuring out MNC length based on IMSI for SIM cards that do not + * include MNC length field. + */ +static int mnc_len_from_imsi(const char *imsi) +{ + char mcc_str[4]; + unsigned int mcc; + + os_memcpy(mcc_str, imsi, 3); + mcc_str[3] = '\0'; + mcc = atoi(mcc_str); + + if (mcc == 228) + return 2; /* Networks in Switzerland use 2-digit MNC */ + if (mcc == 244) + return 2; /* Networks in Finland use 2-digit MNC */ + + return -1; +} + + +static int eap_sm_append_3gpp_realm(struct eap_sm *sm, char *imsi, + size_t max_len, size_t *imsi_len) +{ + int mnc_len; + char *pos, mnc[4]; + + if (*imsi_len + 36 > max_len) { + wpa_printf(MSG_WARNING, "No room for realm in IMSI buffer"); + return -1; + } + + /* MNC (2 or 3 digits) */ + mnc_len = scard_get_mnc_len(sm->scard_ctx); + if (mnc_len < 0) + mnc_len = mnc_len_from_imsi(imsi); + if (mnc_len < 0) { + wpa_printf(MSG_INFO, "Failed to get MNC length from (U)SIM " + "assuming 3"); + mnc_len = 3; + } + + if (mnc_len == 2) { + mnc[0] = '0'; + mnc[1] = imsi[3]; + mnc[2] = imsi[4]; + } else if (mnc_len == 3) { + mnc[0] = imsi[3]; + mnc[1] = imsi[4]; + mnc[2] = imsi[5]; + } + mnc[3] = '\0'; + + pos = imsi + *imsi_len; + pos += os_snprintf(pos, imsi + max_len - pos, + "@wlan.mnc%s.mcc%c%c%c.3gppnetwork.org", + mnc, imsi[0], imsi[1], imsi[2]); + *imsi_len = pos - imsi; + + return 0; +} + + static int eap_sm_imsi_identity(struct eap_sm *sm, struct eap_peer_config *conf) { - int aka = 0; + enum { EAP_SM_SIM, EAP_SM_AKA, EAP_SM_AKA_PRIME } method = EAP_SM_SIM; char imsi[100]; size_t imsi_len; struct eap_method_type *m = conf->eap_methods; @@ -900,11 +1026,23 @@ return -1; } + if (eap_sm_append_3gpp_realm(sm, imsi, sizeof(imsi), &imsi_len) < 0) { + wpa_printf(MSG_WARNING, "Could not add realm to SIM identity"); + return -1; + } + wpa_hexdump_ascii(MSG_DEBUG, "IMSI + realm", (u8 *) imsi, imsi_len); + for (i = 0; m && (m[i].vendor != EAP_VENDOR_IETF || m[i].method != EAP_TYPE_NONE); i++) { if (m[i].vendor == EAP_VENDOR_IETF && + m[i].method == EAP_TYPE_AKA_PRIME) { + method = EAP_SM_AKA_PRIME; + break; + } + + if (m[i].vendor == EAP_VENDOR_IETF && m[i].method == EAP_TYPE_AKA) { - aka = 1; + method = EAP_SM_AKA; break; } } @@ -917,12 +1055,23 @@ return -1; } - conf->identity[0] = aka ? '0' : '1'; + switch (method) { + case EAP_SM_SIM: + conf->identity[0] = '1'; + break; + case EAP_SM_AKA: + conf->identity[0] = '0'; + break; + case EAP_SM_AKA_PRIME: + conf->identity[0] = '6'; + break; + } os_memcpy(conf->identity + 1, imsi, imsi_len); conf->identity_len = 1 + imsi_len; return 0; } + #endif /* PCSC_FUNCS */ @@ -1155,10 +1304,12 @@ break; case EAP_CODE_SUCCESS: wpa_printf(MSG_DEBUG, "EAP: Received EAP-Success"); + eap_notify_status(sm, "completion", "success"); sm->rxSuccess = TRUE; break; case EAP_CODE_FAILURE: wpa_printf(MSG_DEBUG, "EAP: Received EAP-Failure"); + eap_notify_status(sm, "completion", "failure"); sm->rxFailure = TRUE; break; default: @@ -1176,6 +1327,10 @@ char *hash_hex = NULL; switch (ev) { + case TLS_CERT_CHAIN_SUCCESS: + eap_notify_status(sm, "remote certificate verification", + "success"); + break; case TLS_CERT_CHAIN_FAILURE: wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_TLS_CERT_ERROR "reason=%d depth=%d subject='%s' err='%s'", @@ -1183,6 +1338,8 @@ data->cert_fail.depth, data->cert_fail.subject, data->cert_fail.reason_txt); + eap_notify_status(sm, "remote certificate verification", + data->cert_fail.reason_txt); break; case TLS_PEER_CERTIFICATE: if (!sm->eapol_cb->notify_cert) @@ -1203,6 +1360,14 @@ data->peer_cert.subject, hash_hex, data->peer_cert.cert); break; + case TLS_ALERT: + if (data->alert.is_local) + eap_notify_status(sm, "local TLS alert", + data->alert.description); + else + eap_notify_status(sm, "remote TLS alert", + data->alert.description); + break; } os_free(hash_hex); @@ -1257,6 +1422,13 @@ return NULL; } + sm->ssl_ctx2 = tls_init(&tlsconf); + if (sm->ssl_ctx2 == NULL) { + wpa_printf(MSG_INFO, "SSL: Failed to initialize TLS " + "context (2)."); + /* Run without separate TLS context within TLS tunnel */ + } + return sm; } @@ -1274,6 +1446,8 @@ return; eap_deinit_prev_method(sm, "EAP deinit"); eap_sm_abort(sm); + if (sm->ssl_ctx2) + tls_deinit(sm->ssl_ctx2); tls_deinit(sm->ssl_ctx); os_free(sm); } @@ -1316,6 +1490,8 @@ sm->eapRespData = NULL; os_free(sm->eapKeyData); sm->eapKeyData = NULL; + os_free(sm->eapSessionId); + sm->eapSessionId = NULL; /* This is not clearly specified in the EAP statemachines draft, but * it seems necessary to make sure that some of the EAPOL variables get @@ -1477,7 +1653,8 @@ const char *msg, size_t msglen) { struct eap_peer_config *config; - char *txt = NULL, *tmp; + const char *txt = NULL; + char *tmp; if (sm == NULL) return; @@ -1520,6 +1697,9 @@ case WPA_CTRL_REQ_EAP_PASSPHRASE: config->pending_req_passphrase++; break; + case WPA_CTRL_REQ_SIM: + txt = msg; + break; default: return; } @@ -1631,6 +1811,17 @@ /** + * eap_sm_request_sim - Request external SIM processing + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @req: EAP method specific request + */ +void eap_sm_request_sim(struct eap_sm *sm, const char *req) +{ + eap_sm_request(sm, WPA_CTRL_REQ_SIM, req, os_strlen(req)); +} + + +/** * eap_sm_notify_ctrl_attached - Notification of attached monitor * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() * @@ -1793,6 +1984,27 @@ } +static int eap_get_ext_password(struct eap_sm *sm, + struct eap_peer_config *config) +{ + char *name; + + if (config->password == NULL) + return -1; + + name = os_zalloc(config->password_len + 1); + if (name == NULL) + return -1; + os_memcpy(name, config->password, config->password_len); + + ext_password_free(sm->ext_pw_buf); + sm->ext_pw_buf = ext_password_get(sm->ext_pw, name); + os_free(name); + + return sm->ext_pw_buf == NULL ? -1 : 0; +} + + /** * eap_get_config_password - Get password from the network configuration * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() @@ -1804,6 +2016,14 @@ struct eap_peer_config *config = eap_get_config(sm); if (config == NULL) return NULL; + + if (config->flags & EAP_CONFIG_FLAGS_EXT_PASSWORD) { + if (eap_get_ext_password(sm, config) < 0) + return NULL; + *len = wpabuf_len(sm->ext_pw_buf); + return wpabuf_head(sm->ext_pw_buf); + } + *len = config->password_len; return config->password; } @@ -1823,6 +2043,16 @@ struct eap_peer_config *config = eap_get_config(sm); if (config == NULL) return NULL; + + if (config->flags & EAP_CONFIG_FLAGS_EXT_PASSWORD) { + if (eap_get_ext_password(sm, config) < 0) + return NULL; + if (hash) + *hash = 0; + *len = wpabuf_len(sm->ext_pw_buf); + return wpabuf_head(sm->ext_pw_buf); + } + *len = config->password_len; if (hash) *hash = !!(config->flags & EAP_CONFIG_FLAGS_PASSWORD_NTHASH); @@ -1976,6 +2206,28 @@ /** + * eap_get_eapSessionId - Get Session-Id from EAP state machine + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @len: Pointer to variable that will be set to number of bytes in the session + * Returns: Pointer to the EAP Session-Id or %NULL on failure + * + * Fetch EAP Session-Id from the EAP state machine. The Session-Id is available + * only after a successful authentication. EAP state machine continues to manage + * the Session-Id and the caller must not change or free the returned data. + */ +const u8 * eap_get_eapSessionId(struct eap_sm *sm, size_t *len) +{ + if (sm == NULL || sm->eapSessionId == NULL) { + *len = 0; + return NULL; + } + + *len = sm->eapSessionIdLen; + return sm->eapSessionId; +} + + +/** * eap_get_eapKeyData - Get master session key (MSK) from EAP state machine * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() * @len: Pointer to variable that will be set to number of bytes in the key @@ -2084,6 +2336,17 @@ } +/** + * eap_set_external_sim - Set external_sim flag + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @external_sim: Whether external SIM/USIM processing is used + */ +void eap_set_external_sim(struct eap_sm *sm, int external_sim) +{ + sm->external_sim = external_sim; +} + + /** * eap_notify_pending - Notify that EAP method is ready to re-process a request * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() @@ -2134,3 +2397,30 @@ return 1; } + + +void eap_sm_set_ext_pw_ctx(struct eap_sm *sm, struct ext_password_data *ext) +{ + ext_password_free(sm->ext_pw_buf); + sm->ext_pw_buf = NULL; + sm->ext_pw = ext; +} + + +/** + * eap_set_anon_id - Set or add anonymous identity + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @id: Anonymous identity (e.g., EAP-SIM pseudonym) or %NULL to clear + * @len: Length of anonymous identity in octets + */ +void eap_set_anon_id(struct eap_sm *sm, const u8 *id, size_t len) +{ + if (sm->eapol_cb->set_anon_id) + sm->eapol_cb->set_anon_id(sm->eapol_ctx, id, len); +} + + +int eap_peer_was_failure_expected(struct eap_sm *sm) +{ + return sm->expected_failure; +} diff -Nru wpa-1.0/src/eap_peer/eap_config.h wpa-2.1/src/eap_peer/eap_config.h --- wpa-1.0/src/eap_peer/eap_config.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/eap_peer/eap_config.h 2014-02-04 11:23:35.000000000 +0000 @@ -1,15 +1,9 @@ /* * EAP peer configuration data - * Copyright (c) 2003-2008, Jouni Malinen + * Copyright (c) 2003-2013, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef EAP_CONFIG_H @@ -41,6 +35,9 @@ * * If not set, the identity field will be used for both unencrypted and * protected fields. + * + * This field can also be used with EAP-SIM/AKA/AKA' to store the + * pseudonym identity. */ u8 *anonymous_identity; @@ -211,6 +208,24 @@ u8 *altsubject_match; /** + * domain_suffix_match - Constraint for server domain name + * + * If set, this FQDN is used as a suffix match requirement for the + * server certificate in SubjectAltName dNSName element(s). If a + * matching dNSName is found, this constraint is met. If no dNSName + * values are present, this constraint is matched against SubjetName CN + * using same suffix match comparison. Suffix match here means that the + * host/domain name is compared one label at a time starting from the + * top-level domain and all the labels in domain_suffix_match shall be + * included in the certificate. The certificate may include additional + * sub-level labels in addition to the required labels. + * + * For example, domain_suffix_match=example.com would match + * test.example.com but would not match test-example.com. + */ + char *domain_suffix_match; + + /** * ca_cert2 - File path to CA certificate file (PEM/DER) (Phase 2) * * This file can have one or more trusted CA certificates. If ca_cert2 @@ -306,6 +321,14 @@ u8 *altsubject_match2; /** + * domain_suffix_match2 - Constraint for server domain name + * + * This field is like domain_suffix_match, but used for phase 2 (inside + * EAP-TTLS/PEAP/FAST tunnel) authentication. + */ + char *domain_suffix_match2; + + /** * eap_methods - Allowed EAP methods * * (vendor=EAP_VENDOR_IETF,method=EAP_TYPE_NONE) terminated list of @@ -625,6 +648,7 @@ int fragment_size; #define EAP_CONFIG_FLAGS_PASSWORD_NTHASH BIT(0) +#define EAP_CONFIG_FLAGS_EXT_PASSWORD BIT(1) /** * flags - Network configuration flags (bitfield) * @@ -632,8 +656,28 @@ * for the network parameters. * bit 0 = password is represented as a 16-byte NtPasswordHash value * instead of plaintext password + * bit 1 = password is stored in external storage; the value in the + * password field is the name of that external entry */ u32 flags; + + /** + * ocsp - Whether to use/require OCSP to check server certificate + * + * 0 = do not use OCSP stapling (TLS certificate status extension) + * 1 = try to use OCSP stapling, but not require response + * 2 = require valid OCSP stapling response + */ + int ocsp; + + /** + * external_sim_resp - Response from external SIM processing + * + * This field should not be set in configuration step. It is only used + * internally when control interface is used to request external + * SIM/USIM processing. + */ + char *external_sim_resp; }; diff -Nru wpa-1.0/src/eap_peer/eap_eke.c wpa-2.1/src/eap_peer/eap_eke.c --- wpa-1.0/src/eap_peer/eap_eke.c 1970-01-01 00:00:00.000000000 +0000 +++ wpa-2.1/src/eap_peer/eap_eke.c 2014-02-04 11:23:35.000000000 +0000 @@ -0,0 +1,765 @@ +/* + * EAP peer method: EAP-EKE (RFC 6124) + * Copyright (c) 2013, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "crypto/random.h" +#include "eap_peer/eap_i.h" +#include "eap_common/eap_eke_common.h" + +struct eap_eke_data { + enum { + IDENTITY, COMMIT, CONFIRM, SUCCESS, FAILURE + } state; + u8 msk[EAP_MSK_LEN]; + u8 emsk[EAP_EMSK_LEN]; + u8 *peerid; + size_t peerid_len; + u8 *serverid; + size_t serverid_len; + u8 dh_priv[EAP_EKE_MAX_DH_LEN]; + struct eap_eke_session sess; + u8 nonce_p[EAP_EKE_MAX_NONCE_LEN]; + u8 nonce_s[EAP_EKE_MAX_NONCE_LEN]; + struct wpabuf *msgs; + u8 dhgroup; /* forced DH group or 0 to allow all supported */ + u8 encr; /* forced encryption algorithm or 0 to allow all supported */ + u8 prf; /* forced PRF or 0 to allow all supported */ + u8 mac; /* forced MAC or 0 to allow all supported */ +}; + + +static const char * eap_eke_state_txt(int state) +{ + switch (state) { + case IDENTITY: + return "IDENTITY"; + case COMMIT: + return "COMMIT"; + case CONFIRM: + return "CONFIRM"; + case SUCCESS: + return "SUCCESS"; + case FAILURE: + return "FAILURE"; + default: + return "?"; + } +} + + +static void eap_eke_state(struct eap_eke_data *data, int state) +{ + wpa_printf(MSG_DEBUG, "EAP-EKE: %s -> %s", + eap_eke_state_txt(data->state), eap_eke_state_txt(state)); + data->state = state; +} + + +static void eap_eke_deinit(struct eap_sm *sm, void *priv); + + +static void * eap_eke_init(struct eap_sm *sm) +{ + struct eap_eke_data *data; + const u8 *identity, *password; + size_t identity_len, password_len; + const char *phase1; + + password = eap_get_config_password(sm, &password_len); + if (!password) { + wpa_printf(MSG_INFO, "EAP-EKE: No password configured"); + return NULL; + } + + data = os_zalloc(sizeof(*data)); + if (data == NULL) + return NULL; + eap_eke_state(data, IDENTITY); + + identity = eap_get_config_identity(sm, &identity_len); + if (identity) { + data->peerid = os_malloc(identity_len); + if (data->peerid == NULL) { + eap_eke_deinit(sm, data); + return NULL; + } + os_memcpy(data->peerid, identity, identity_len); + data->peerid_len = identity_len; + } + + phase1 = eap_get_config_phase1(sm); + if (phase1) { + const char *pos; + + pos = os_strstr(phase1, "dhgroup="); + if (pos) { + data->dhgroup = atoi(pos + 8); + wpa_printf(MSG_DEBUG, "EAP-EKE: Forced dhgroup %u", + data->dhgroup); + } + + pos = os_strstr(phase1, "encr="); + if (pos) { + data->encr = atoi(pos + 5); + wpa_printf(MSG_DEBUG, "EAP-EKE: Forced encr %u", + data->encr); + } + + pos = os_strstr(phase1, "prf="); + if (pos) { + data->prf = atoi(pos + 4); + wpa_printf(MSG_DEBUG, "EAP-EKE: Forced prf %u", + data->prf); + } + + pos = os_strstr(phase1, "mac="); + if (pos) { + data->mac = atoi(pos + 4); + wpa_printf(MSG_DEBUG, "EAP-EKE: Forced mac %u", + data->mac); + } + } + + return data; +} + + +static void eap_eke_deinit(struct eap_sm *sm, void *priv) +{ + struct eap_eke_data *data = priv; + eap_eke_session_clean(&data->sess); + os_free(data->serverid); + os_free(data->peerid); + wpabuf_free(data->msgs); + os_free(data); +} + + +static struct wpabuf * eap_eke_build_msg(struct eap_eke_data *data, int id, + size_t length, u8 eke_exch) +{ + struct wpabuf *msg; + size_t plen; + + plen = 1 + length; + + msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_EKE, plen, + EAP_CODE_RESPONSE, id); + if (msg == NULL) { + wpa_printf(MSG_ERROR, "EAP-EKE: Failed to allocate memory"); + return NULL; + } + + wpabuf_put_u8(msg, eke_exch); + + return msg; +} + + +static int eap_eke_supp_dhgroup(u8 dhgroup) +{ + return dhgroup == EAP_EKE_DHGROUP_EKE_2 || + dhgroup == EAP_EKE_DHGROUP_EKE_5 || + dhgroup == EAP_EKE_DHGROUP_EKE_14 || + dhgroup == EAP_EKE_DHGROUP_EKE_15 || + dhgroup == EAP_EKE_DHGROUP_EKE_16; +} + + +static int eap_eke_supp_encr(u8 encr) +{ + return encr == EAP_EKE_ENCR_AES128_CBC; +} + + +static int eap_eke_supp_prf(u8 prf) +{ + return prf == EAP_EKE_PRF_HMAC_SHA1 || + prf == EAP_EKE_PRF_HMAC_SHA2_256; +} + + +static int eap_eke_supp_mac(u8 mac) +{ + return mac == EAP_EKE_MAC_HMAC_SHA1 || + mac == EAP_EKE_MAC_HMAC_SHA2_256; +} + + +static struct wpabuf * eap_eke_build_fail(struct eap_eke_data *data, + struct eap_method_ret *ret, + const struct wpabuf *reqData, + u32 failure_code) +{ + struct wpabuf *resp; + + wpa_printf(MSG_DEBUG, "EAP-EKE: Sending EAP-EKE-Failure/Response - code=0x%x", + failure_code); + + resp = eap_eke_build_msg(data, eap_get_id(reqData), 4, EAP_EKE_FAILURE); + if (resp) + wpabuf_put_be32(resp, failure_code); + + os_memset(data->dh_priv, 0, sizeof(data->dh_priv)); + eap_eke_session_clean(&data->sess); + + eap_eke_state(data, FAILURE); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + ret->allowNotifications = FALSE; + + return resp; +} + + +static struct wpabuf * eap_eke_process_id(struct eap_eke_data *data, + struct eap_method_ret *ret, + const struct wpabuf *reqData, + const u8 *payload, + size_t payload_len) +{ + struct wpabuf *resp; + unsigned num_prop, i; + const u8 *pos, *end; + const u8 *prop = NULL; + u8 idtype; + + if (data->state != IDENTITY) { + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_PROTO_ERROR); + } + + wpa_printf(MSG_DEBUG, "EAP-EKE: Received EAP-EKE-ID/Request"); + + if (payload_len < 2 + 4) { + wpa_printf(MSG_DEBUG, "EAP-EKE: Too short ID/Request Data"); + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_PROTO_ERROR); + } + + pos = payload; + end = payload + payload_len; + + num_prop = *pos++; + pos++; /* Ignore Reserved field */ + + if (pos + num_prop * 4 > end) { + wpa_printf(MSG_DEBUG, "EAP-EKE: Too short ID/Request Data (num_prop=%u)", + num_prop); + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_PROTO_ERROR); + } + + for (i = 0; i < num_prop; i++) { + const u8 *tmp = pos; + + wpa_printf(MSG_DEBUG, "EAP-EKE: Proposal #%u: dh=%u encr=%u prf=%u mac=%u", + i, pos[0], pos[1], pos[2], pos[3]); + pos += 4; + + if ((data->dhgroup && data->dhgroup != *tmp) || + !eap_eke_supp_dhgroup(*tmp)) + continue; + tmp++; + if ((data->encr && data->encr != *tmp) || + !eap_eke_supp_encr(*tmp)) + continue; + tmp++; + if ((data->prf && data->prf != *tmp) || + !eap_eke_supp_prf(*tmp)) + continue; + tmp++; + if ((data->mac && data->mac != *tmp) || + !eap_eke_supp_mac(*tmp)) + continue; + + prop = tmp - 3; + if (eap_eke_session_init(&data->sess, prop[0], prop[1], prop[2], + prop[3]) < 0) { + prop = NULL; + continue; + } + + wpa_printf(MSG_DEBUG, "EAP-EKE: Selected proposal"); + break; + } + + if (prop == NULL) { + wpa_printf(MSG_DEBUG, "EAP-EKE: No acceptable proposal found"); + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_NO_PROPOSAL_CHOSEN); + } + + pos += (num_prop - i - 1) * 4; + + if (pos == end) { + wpa_printf(MSG_DEBUG, "EAP-EKE: Too short ID/Request Data to include IDType/Identity"); + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_PROTO_ERROR); + } + + idtype = *pos++; + wpa_printf(MSG_DEBUG, "EAP-EKE: Server IDType %u", idtype); + wpa_hexdump_ascii(MSG_DEBUG, "EAP-EKE: Server Identity", + pos, end - pos); + os_free(data->serverid); + data->serverid = os_malloc(end - pos); + if (data->serverid == NULL) { + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + } + os_memcpy(data->serverid, pos, end - pos); + data->serverid_len = end - pos; + + wpa_printf(MSG_DEBUG, "EAP-EKE: Sending EAP-EKE-ID/Response"); + + resp = eap_eke_build_msg(data, eap_get_id(reqData), + 2 + 4 + 1 + data->peerid_len, + EAP_EKE_ID); + if (resp == NULL) { + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + } + + wpabuf_put_u8(resp, 1); /* NumProposals */ + wpabuf_put_u8(resp, 0); /* Reserved */ + wpabuf_put_data(resp, prop, 4); /* Selected Proposal */ + wpabuf_put_u8(resp, EAP_EKE_ID_NAI); + if (data->peerid) + wpabuf_put_data(resp, data->peerid, data->peerid_len); + + wpabuf_free(data->msgs); + data->msgs = wpabuf_alloc(wpabuf_len(reqData) + wpabuf_len(resp)); + if (data->msgs == NULL) { + wpabuf_free(resp); + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + } + wpabuf_put_buf(data->msgs, reqData); + wpabuf_put_buf(data->msgs, resp); + + eap_eke_state(data, COMMIT); + + return resp; +} + + +static struct wpabuf * eap_eke_process_commit(struct eap_sm *sm, + struct eap_eke_data *data, + struct eap_method_ret *ret, + const struct wpabuf *reqData, + const u8 *payload, + size_t payload_len) +{ + struct wpabuf *resp; + const u8 *pos, *end, *dhcomp; + size_t prot_len; + u8 *rpos; + u8 key[EAP_EKE_MAX_KEY_LEN]; + u8 pub[EAP_EKE_MAX_DH_LEN]; + const u8 *password; + size_t password_len; + + if (data->state != COMMIT) { + wpa_printf(MSG_DEBUG, "EAP-EKE: EAP-EKE-Commit/Request received in unexpected state (%d)", data->state); + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_PROTO_ERROR); + } + + wpa_printf(MSG_DEBUG, "EAP-EKE: Received EAP-EKE-Commit/Request"); + + password = eap_get_config_password(sm, &password_len); + if (password == NULL) { + wpa_printf(MSG_INFO, "EAP-EKE: No password configured!"); + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_PASSWD_NOT_FOUND); + } + + pos = payload; + end = payload + payload_len; + + if (pos + data->sess.dhcomp_len > end) { + wpa_printf(MSG_DEBUG, "EAP-EKE: Too short EAP-EKE-Commit"); + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_PROTO_ERROR); + } + + wpa_hexdump(MSG_DEBUG, "EAP-EKE: DHComponent_S", + pos, data->sess.dhcomp_len); + dhcomp = pos; + pos += data->sess.dhcomp_len; + wpa_hexdump(MSG_DEBUG, "EAP-EKE: CBValue", pos, end - pos); + + /* + * temp = prf(0+, password) + * key = prf+(temp, ID_S | ID_P) + */ + if (eap_eke_derive_key(&data->sess, password, password_len, + data->serverid, data->serverid_len, + data->peerid, data->peerid_len, key) < 0) { + wpa_printf(MSG_INFO, "EAP-EKE: Failed to derive key"); + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + } + + /* + * y_p = g ^ x_p (mod p) + * x_p = random number 2 .. p-1 + */ + if (eap_eke_dh_init(data->sess.dhgroup, data->dh_priv, pub) < 0) { + wpa_printf(MSG_INFO, "EAP-EKE: Failed to initialize DH"); + os_memset(key, 0, sizeof(key)); + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + } + + if (eap_eke_shared_secret(&data->sess, key, data->dh_priv, dhcomp) < 0) + { + wpa_printf(MSG_INFO, "EAP-EKE: Failed to derive shared secret"); + os_memset(key, 0, sizeof(key)); + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + } + + if (eap_eke_derive_ke_ki(&data->sess, + data->serverid, data->serverid_len, + data->peerid, data->peerid_len) < 0) { + wpa_printf(MSG_INFO, "EAP-EKE: Failed to derive Ke/Ki"); + os_memset(key, 0, sizeof(key)); + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + } + + wpa_printf(MSG_DEBUG, "EAP-EKE: Sending EAP-EKE-Commit/Response"); + + resp = eap_eke_build_msg(data, eap_get_id(reqData), + data->sess.dhcomp_len + data->sess.pnonce_len, + EAP_EKE_COMMIT); + if (resp == NULL) { + os_memset(key, 0, sizeof(key)); + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + } + + /* DHComponent_P = Encr(key, y_p) */ + rpos = wpabuf_put(resp, data->sess.dhcomp_len); + if (eap_eke_dhcomp(&data->sess, key, pub, rpos) < 0) { + wpa_printf(MSG_INFO, "EAP-EKE: Failed to build DHComponent_S"); + os_memset(key, 0, sizeof(key)); + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + } + os_memset(key, 0, sizeof(key)); + + wpa_hexdump(MSG_DEBUG, "EAP-EKE: DHComponent_P", + rpos, data->sess.dhcomp_len); + + if (random_get_bytes(data->nonce_p, data->sess.nonce_len)) { + wpabuf_free(resp); + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + } + wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: Nonce_P", + data->nonce_p, data->sess.nonce_len); + prot_len = wpabuf_tailroom(resp); + if (eap_eke_prot(&data->sess, data->nonce_p, data->sess.nonce_len, + wpabuf_put(resp, 0), &prot_len) < 0) { + wpabuf_free(resp); + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + } + wpa_hexdump(MSG_DEBUG, "EAP-EKE: PNonce_P", + wpabuf_put(resp, 0), prot_len); + wpabuf_put(resp, prot_len); + + /* TODO: CBValue */ + + if (wpabuf_resize(&data->msgs, wpabuf_len(reqData) + wpabuf_len(resp)) + < 0) { + wpabuf_free(resp); + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + } + wpabuf_put_buf(data->msgs, reqData); + wpabuf_put_buf(data->msgs, resp); + + eap_eke_state(data, CONFIRM); + + return resp; +} + + +static struct wpabuf * eap_eke_process_confirm(struct eap_eke_data *data, + struct eap_method_ret *ret, + const struct wpabuf *reqData, + const u8 *payload, + size_t payload_len) +{ + struct wpabuf *resp; + const u8 *pos, *end; + size_t prot_len; + u8 nonces[2 * EAP_EKE_MAX_NONCE_LEN]; + u8 auth_s[EAP_EKE_MAX_HASH_LEN]; + size_t decrypt_len; + u8 *auth; + + if (data->state != CONFIRM) { + wpa_printf(MSG_DEBUG, "EAP-EKE: EAP-EKE-Confirm/Request received in unexpected state (%d)", + data->state); + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_PROTO_ERROR); + } + + wpa_printf(MSG_DEBUG, "EAP-EKE: Received EAP-EKE-Confirm/Request"); + + pos = payload; + end = payload + payload_len; + + if (pos + data->sess.pnonce_ps_len + data->sess.prf_len > end) { + wpa_printf(MSG_DEBUG, "EAP-EKE: Too short EAP-EKE-Commit"); + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_PROTO_ERROR); + } + + decrypt_len = sizeof(nonces); + if (eap_eke_decrypt_prot(&data->sess, pos, data->sess.pnonce_ps_len, + nonces, &decrypt_len) < 0) { + wpa_printf(MSG_INFO, "EAP-EKE: Failed to decrypt PNonce_PS"); + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_AUTHENTICATION_FAIL); + } + if (decrypt_len != (size_t) 2 * data->sess.nonce_len) { + wpa_printf(MSG_INFO, "EAP-EKE: PNonce_PS protected data length does not match length of Nonce_P and Nonce_S"); + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_AUTHENTICATION_FAIL); + } + wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: Received Nonce_P | Nonce_S", + nonces, 2 * data->sess.nonce_len); + if (os_memcmp(data->nonce_p, nonces, data->sess.nonce_len) != 0) { + wpa_printf(MSG_INFO, "EAP-EKE: Received Nonce_P does not match trnsmitted Nonce_P"); + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_AUTHENTICATION_FAIL); + } + + os_memcpy(data->nonce_s, nonces + data->sess.nonce_len, + data->sess.nonce_len); + wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: Nonce_S", + data->nonce_s, data->sess.nonce_len); + + if (eap_eke_derive_ka(&data->sess, data->serverid, data->serverid_len, + data->peerid, data->peerid_len, + data->nonce_p, data->nonce_s) < 0) { + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + } + + if (eap_eke_auth(&data->sess, "EAP-EKE server", data->msgs, auth_s) < 0) + { + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + } + wpa_hexdump(MSG_DEBUG, "EAP-EKE: Auth_S", auth_s, data->sess.prf_len); + if (os_memcmp(auth_s, pos + data->sess.pnonce_ps_len, + data->sess.prf_len) != 0) { + wpa_printf(MSG_INFO, "EAP-EKE: Auth_S does not match"); + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_AUTHENTICATION_FAIL); + } + + wpa_printf(MSG_DEBUG, "EAP-EKE: Sending EAP-EKE-Confirm/Response"); + + resp = eap_eke_build_msg(data, eap_get_id(reqData), + data->sess.pnonce_len + data->sess.prf_len, + EAP_EKE_CONFIRM); + if (resp == NULL) { + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + } + + prot_len = wpabuf_tailroom(resp); + if (eap_eke_prot(&data->sess, data->nonce_s, data->sess.nonce_len, + wpabuf_put(resp, 0), &prot_len) < 0) { + wpabuf_free(resp); + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + } + wpabuf_put(resp, prot_len); + + auth = wpabuf_put(resp, data->sess.prf_len); + if (eap_eke_auth(&data->sess, "EAP-EKE peer", data->msgs, auth) < 0) { + wpabuf_free(resp); + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + } + wpa_hexdump(MSG_DEBUG, "EAP-EKE: Auth_P", auth, data->sess.prf_len); + + if (eap_eke_derive_msk(&data->sess, data->serverid, data->serverid_len, + data->peerid, data->peerid_len, + data->nonce_s, data->nonce_p, + data->msk, data->emsk) < 0) { + wpa_printf(MSG_INFO, "EAP-EKE: Failed to derive MSK/EMSK"); + wpabuf_free(resp); + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + } + + os_memset(data->dh_priv, 0, sizeof(data->dh_priv)); + eap_eke_session_clean(&data->sess); + + eap_eke_state(data, SUCCESS); + ret->methodState = METHOD_MAY_CONT; + ret->decision = DECISION_COND_SUCC; + ret->allowNotifications = FALSE; + + return resp; +} + + +static struct wpabuf * eap_eke_process_failure(struct eap_eke_data *data, + struct eap_method_ret *ret, + const struct wpabuf *reqData, + const u8 *payload, + size_t payload_len) +{ + wpa_printf(MSG_DEBUG, "EAP-EKE: Received EAP-EKE-Failure/Request"); + + if (payload_len < 4) { + wpa_printf(MSG_DEBUG, "EAP-EKE: Too short EAP-EKE-Failure"); + } else { + u32 code; + code = WPA_GET_BE32(payload); + wpa_printf(MSG_INFO, "EAP-EKE: Failure-Code 0x%x", code); + } + + return eap_eke_build_fail(data, ret, reqData, EAP_EKE_FAIL_NO_ERROR); +} + + +static struct wpabuf * eap_eke_process(struct eap_sm *sm, void *priv, + struct eap_method_ret *ret, + const struct wpabuf *reqData) +{ + struct eap_eke_data *data = priv; + struct wpabuf *resp; + const u8 *pos, *end; + size_t len; + u8 eke_exch; + + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_EKE, reqData, &len); + if (pos == NULL || len < 1) { + ret->ignore = TRUE; + return NULL; + } + + end = pos + len; + eke_exch = *pos++; + + wpa_printf(MSG_DEBUG, "EAP-EKE: Received frame: exch %d", eke_exch); + wpa_hexdump(MSG_DEBUG, "EAP-EKE: Received Data", pos, end - pos); + + ret->ignore = FALSE; + ret->methodState = METHOD_MAY_CONT; + ret->decision = DECISION_FAIL; + ret->allowNotifications = TRUE; + + switch (eke_exch) { + case EAP_EKE_ID: + resp = eap_eke_process_id(data, ret, reqData, pos, end - pos); + break; + case EAP_EKE_COMMIT: + resp = eap_eke_process_commit(sm, data, ret, reqData, + pos, end - pos); + break; + case EAP_EKE_CONFIRM: + resp = eap_eke_process_confirm(data, ret, reqData, + pos, end - pos); + break; + case EAP_EKE_FAILURE: + resp = eap_eke_process_failure(data, ret, reqData, + pos, end - pos); + break; + default: + wpa_printf(MSG_DEBUG, "EAP-EKE: Ignoring message with unknown EKE-Exch %d", eke_exch); + ret->ignore = TRUE; + return NULL; + } + + if (ret->methodState == METHOD_DONE) + ret->allowNotifications = FALSE; + + return resp; +} + + +static Boolean eap_eke_isKeyAvailable(struct eap_sm *sm, void *priv) +{ + struct eap_eke_data *data = priv; + return data->state == SUCCESS; +} + + +static u8 * eap_eke_getKey(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_eke_data *data = priv; + u8 *key; + + if (data->state != SUCCESS) + return NULL; + + key = os_malloc(EAP_MSK_LEN); + if (key == NULL) + return NULL; + os_memcpy(key, data->msk, EAP_MSK_LEN); + *len = EAP_MSK_LEN; + + return key; +} + + +static u8 * eap_eke_get_emsk(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_eke_data *data = priv; + u8 *key; + + if (data->state != SUCCESS) + return NULL; + + key = os_malloc(EAP_EMSK_LEN); + if (key == NULL) + return NULL; + os_memcpy(key, data->emsk, EAP_EMSK_LEN); + *len = EAP_EMSK_LEN; + + return key; +} + + +int eap_peer_eke_register(void) +{ + struct eap_method *eap; + int ret; + + eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, + EAP_VENDOR_IETF, EAP_TYPE_EKE, "EKE"); + if (eap == NULL) + return -1; + + eap->init = eap_eke_init; + eap->deinit = eap_eke_deinit; + eap->process = eap_eke_process; + eap->isKeyAvailable = eap_eke_isKeyAvailable; + eap->getKey = eap_eke_getKey; + eap->get_emsk = eap_eke_get_emsk; + + ret = eap_peer_method_register(eap); + if (ret) + eap_peer_method_free(eap); + return ret; +} diff -Nru wpa-1.0/src/eap_peer/eap_fast.c wpa-2.1/src/eap_peer/eap_fast.c --- wpa-1.0/src/eap_peer/eap_fast.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/eap_peer/eap_fast.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * EAP peer method: EAP-FAST (RFC 4851) * Copyright (c) 2004-2008, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -59,6 +53,8 @@ int session_ticket_used; u8 key_data[EAP_FAST_KEY_LEN]; + u8 *session_id; + size_t id_len; u8 emsk[EAP_EMSK_LEN]; int success; @@ -175,7 +171,7 @@ data->phase2_type.vendor = EAP_VENDOR_IETF; data->phase2_type.method = EAP_TYPE_NONE; - if (eap_peer_tls_ssl_init(sm, &data->ssl, config)) { + if (eap_peer_tls_ssl_init(sm, &data->ssl, config, EAP_TYPE_FAST)) { wpa_printf(MSG_INFO, "EAP-FAST: Failed to initialize SSL."); eap_fast_deinit(sm, data); return NULL; @@ -200,14 +196,22 @@ "workarounds"); } + if (!config->pac_file) { + wpa_printf(MSG_INFO, "EAP-FAST: No PAC file configured"); + eap_fast_deinit(sm, data); + return NULL; + } + if (data->use_pac_binary_format && eap_fast_load_pac_bin(sm, &data->pac, config->pac_file) < 0) { + wpa_printf(MSG_INFO, "EAP-FAST: Failed to load PAC file"); eap_fast_deinit(sm, data); return NULL; } if (!data->use_pac_binary_format && eap_fast_load_pac(sm, &data->pac, config->pac_file) < 0) { + wpa_printf(MSG_INFO, "EAP-FAST: Failed to load PAC file"); eap_fast_deinit(sm, data); return NULL; } @@ -244,6 +248,7 @@ pac = pac->next; eap_fast_free_pac(prev); } + os_free(data->session_id); wpabuf_free(data->pending_phase2_req); os_free(data); } @@ -791,6 +796,21 @@ return NULL; } + if (!data->anon_provisioning && data->phase2_success) { + os_free(data->session_id); + data->session_id = eap_peer_tls_derive_session_id( + sm, &data->ssl, EAP_TYPE_FAST, &data->id_len); + if (data->session_id) { + wpa_hexdump(MSG_DEBUG, "EAP-FAST: Derived Session-Id", + data->session_id, data->id_len); + } else { + wpa_printf(MSG_ERROR, "EAP-FAST: Failed to derive " + "Session-Id"); + wpabuf_free(resp); + return NULL; + } + } + pos = wpabuf_put(resp, sizeof(struct eap_tlv_crypto_binding_tlv)); eap_fast_write_crypto_binding((struct eap_tlv_crypto_binding_tlv *) pos, _bind, cmk); @@ -1035,6 +1055,7 @@ } wpa_printf(MSG_DEBUG, "EAP-FAST: Send PAC-Acknowledgement TLV " "- Provisioning completed successfully"); + sm->expected_failure = 1; } else { /* * This is PAC refreshing, i.e., normal authentication that is @@ -1232,6 +1253,7 @@ "provisioning completed successfully."); ret->methodState = METHOD_DONE; ret->decision = DECISION_FAIL; + sm->expected_failure = 1; } else { wpa_printf(MSG_DEBUG, "EAP-FAST: Authentication " "completed successfully."); @@ -1610,6 +1632,8 @@ os_free(data); return NULL; } + os_free(data->session_id); + data->session_id = NULL; if (data->phase2_priv && data->phase2_method && data->phase2_method->init_for_reauth) data->phase2_method->init_for_reauth(sm, data->phase2_priv); @@ -1668,6 +1692,25 @@ } +static u8 * eap_fast_get_session_id(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_fast_data *data = priv; + u8 *id; + + if (!data->success) + return NULL; + + id = os_malloc(data->id_len); + if (id == NULL) + return NULL; + + *len = data->id_len; + os_memcpy(id, data->session_id, data->id_len); + + return id; +} + + static u8 * eap_fast_get_emsk(struct eap_sm *sm, void *priv, size_t *len) { struct eap_fast_data *data = priv; @@ -1702,6 +1745,7 @@ eap->process = eap_fast_process; eap->isKeyAvailable = eap_fast_isKeyAvailable; eap->getKey = eap_fast_getKey; + eap->getSessionId = eap_fast_get_session_id; eap->get_status = eap_fast_get_status; #if 0 eap->has_reauth_data = eap_fast_has_reauth_data; diff -Nru wpa-1.0/src/eap_peer/eap_fast_pac.c wpa-2.1/src/eap_peer/eap_fast_pac.c --- wpa-1.0/src/eap_peer/eap_fast_pac.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/eap_peer/eap_fast_pac.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * EAP peer method: EAP-FAST PAC file processing * Copyright (c) 2004-2006, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -428,8 +422,12 @@ if (eap_fast_init_pac_data(sm, pac_file, &rc) < 0) return 0; - if (eap_fast_read_line(&rc, &pos) < 0 || - os_strcmp(pac_file_hdr, rc.buf) != 0) + if (eap_fast_read_line(&rc, &pos) < 0) { + /* empty file - assume it is fine to overwrite */ + eap_fast_deinit_pac_data(&rc); + return 0; + } + if (os_strcmp(pac_file_hdr, rc.buf) != 0) err = "Unrecognized header line"; while (!err && eap_fast_read_line(&rc, &pos) == 0) { diff -Nru wpa-1.0/src/eap_peer/eap_fast_pac.h wpa-2.1/src/eap_peer/eap_fast_pac.h --- wpa-1.0/src/eap_peer/eap_fast_pac.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/eap_peer/eap_fast_pac.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * EAP peer method: EAP-FAST PAC file processing * Copyright (c) 2004-2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef EAP_FAST_PAC_H diff -Nru wpa-1.0/src/eap_peer/eap_gpsk.c wpa-2.1/src/eap_peer/eap_gpsk.c --- wpa-1.0/src/eap_peer/eap_gpsk.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/eap_peer/eap_gpsk.c 2014-02-04 11:23:35.000000000 +0000 @@ -1,15 +1,9 @@ /* * EAP peer method: EAP-GPSK (RFC 5433) - * Copyright (c) 2006-2008, Jouni Malinen + * Copyright (c) 2006-2014, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -29,8 +23,8 @@ size_t sk_len; u8 pk[EAP_GPSK_MAX_PK_LEN]; size_t pk_len; - u8 session_id; - int session_id_set; + u8 session_id[128]; + size_t id_len; u8 *id_peer; size_t id_peer_len; u8 *id_server; @@ -39,6 +33,7 @@ int specifier; /* CSuite/Specifier */ u8 *psk; size_t psk_len; + u16 forced_cipher; /* force cipher or 0 to allow all supported */ }; @@ -86,6 +81,7 @@ struct eap_gpsk_data *data; const u8 *identity, *password; size_t identity_len, password_len; + const char *phase1; password = eap_get_config_password(sm, &password_len); if (password == NULL) { @@ -109,6 +105,18 @@ data->id_peer_len = identity_len; } + phase1 = eap_get_config_phase1(sm); + if (phase1) { + const char *pos; + + pos = os_strstr(phase1, "cipher="); + if (pos) { + data->forced_cipher = atoi(pos + 7); + wpa_printf(MSG_DEBUG, "EAP-GPSK: Forced cipher %u", + data->forced_cipher); + } + } + data->psk = os_malloc(password_len); if (data->psk == NULL) { eap_gpsk_deinit(sm, data); @@ -201,7 +209,9 @@ i, vendor, specifier); if (data->vendor == EAP_GPSK_VENDOR_IETF && data->specifier == EAP_GPSK_CIPHER_RESERVED && - eap_gpsk_supported_ciphersuite(vendor, specifier)) { + eap_gpsk_supported_ciphersuite(vendor, specifier) && + (!data->forced_cipher || data->forced_cipher == specifier)) + { data->vendor = vendor; data->specifier = specifier; } @@ -279,6 +289,7 @@ pos = eap_gpsk_process_csuite_list(sm, data, &csuite_list, &csuite_list_len, pos, end); if (pos == NULL) { + ret->methodState = METHOD_DONE; eap_gpsk_state(data, FAILURE); return NULL; } @@ -360,6 +371,21 @@ return NULL; } + if (eap_gpsk_derive_session_id(data->psk, data->psk_len, + data->vendor, data->specifier, + data->rand_peer, data->rand_server, + data->id_peer, data->id_peer_len, + data->id_server, data->id_server_len, + EAP_TYPE_GPSK, + data->session_id, &data->id_len) < 0) { + wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to derive Session-Id"); + eap_gpsk_state(data, FAILURE); + wpabuf_free(resp); + return NULL; + } + wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Derived Session-Id", + data->session_id, data->id_len); + /* No PD_Payload_1 */ wpabuf_put_be16(resp, 0); @@ -714,6 +740,24 @@ } +static u8 * eap_gpsk_get_session_id(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_gpsk_data *data = priv; + u8 *sid; + + if (data->state != SUCCESS) + return NULL; + + sid = os_malloc(data->id_len); + if (sid == NULL) + return NULL; + os_memcpy(sid, data->session_id, data->id_len); + *len = data->id_len; + + return sid; +} + + int eap_peer_gpsk_register(void) { struct eap_method *eap; @@ -730,6 +774,7 @@ eap->isKeyAvailable = eap_gpsk_isKeyAvailable; eap->getKey = eap_gpsk_getKey; eap->get_emsk = eap_gpsk_get_emsk; + eap->getSessionId = eap_gpsk_get_session_id; ret = eap_peer_method_register(eap); if (ret) diff -Nru wpa-1.0/src/eap_peer/eap_gtc.c wpa-2.1/src/eap_peer/eap_gtc.c --- wpa-1.0/src/eap_peer/eap_gtc.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/eap_peer/eap_gtc.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * EAP peer method: EAP-GTC (RFC 3748) * Copyright (c) 2004-2006, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff -Nru wpa-1.0/src/eap_peer/eap.h wpa-2.1/src/eap_peer/eap.h --- wpa-1.0/src/eap_peer/eap.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/eap_peer/eap.h 2014-02-04 11:23:35.000000000 +0000 @@ -1,15 +1,9 @@ /* * EAP peer state machine functions (RFC 4137) - * Copyright (c) 2004-2007, Jouni Malinen + * Copyright (c) 2004-2012, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef EAP_H @@ -232,6 +226,23 @@ */ void (*notify_cert)(void *ctx, int depth, const char *subject, const char *cert_hash, const struct wpabuf *cert); + + /** + * notify_status - Notification of the current EAP state + * @ctx: eapol_ctx from eap_peer_sm_init() call + * @status: Step in the process of EAP authentication + * @parameter: Step-specific parameter, e.g., EAP method name + */ + void (*notify_status)(void *ctx, const char *status, + const char *parameter); + + /** + * set_anon_id - Set or add anonymous identity + * @ctx: eapol_ctx from eap_peer_sm_init() call + * @id: Anonymous identity (e.g., EAP-SIM pseudonym) or %NULL to clear + * @len: Length of anonymous identity in octets + */ + void (*set_anon_id)(void *ctx, const u8 *id, size_t len); }; /** @@ -285,6 +296,7 @@ void eap_sm_request_pin(struct eap_sm *sm); void eap_sm_request_otp(struct eap_sm *sm, const char *msg, size_t msg_len); void eap_sm_request_passphrase(struct eap_sm *sm); +void eap_sm_request_sim(struct eap_sm *sm, const char *req); void eap_sm_notify_ctrl_attached(struct eap_sm *sm); u32 eap_get_phase2_type(const char *name, int *vendor); struct eap_method_type * eap_get_phase2_types(struct eap_peer_config *config, @@ -292,9 +304,11 @@ void eap_set_fast_reauth(struct eap_sm *sm, int enabled); void eap_set_workaround(struct eap_sm *sm, unsigned int workaround); void eap_set_force_disabled(struct eap_sm *sm, int disabled); +void eap_set_external_sim(struct eap_sm *sm, int external_sim); int eap_key_available(struct eap_sm *sm); void eap_notify_success(struct eap_sm *sm); void eap_notify_lower_layer_success(struct eap_sm *sm); +const u8 * eap_get_eapSessionId(struct eap_sm *sm, size_t *len); const u8 * eap_get_eapKeyData(struct eap_sm *sm, size_t *len); struct wpabuf * eap_get_eapRespData(struct eap_sm *sm); void eap_register_scard_ctx(struct eap_sm *sm, void *ctx); @@ -303,6 +317,11 @@ int eap_is_wps_pbc_enrollee(struct eap_peer_config *conf); int eap_is_wps_pin_enrollee(struct eap_peer_config *conf); +struct ext_password_data; +void eap_sm_set_ext_pw_ctx(struct eap_sm *sm, struct ext_password_data *ext); +void eap_set_anon_id(struct eap_sm *sm, const u8 *id, size_t len); +int eap_peer_was_failure_expected(struct eap_sm *sm); + #endif /* IEEE8021X_EAPOL */ #endif /* EAP_H */ diff -Nru wpa-1.0/src/eap_peer/eap_i.h wpa-2.1/src/eap_peer/eap_i.h --- wpa-1.0/src/eap_peer/eap_i.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/eap_peer/eap_i.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * EAP peer state machines internal structures (RFC 4137) * Copyright (c) 2004-2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef EAP_I_H @@ -267,6 +261,19 @@ * private data or this function may derive the key. */ u8 * (*get_emsk)(struct eap_sm *sm, void *priv, size_t *len); + + /** + * getSessionId - Get EAP method specific Session-Id + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @priv: Pointer to private EAP method data from eap_method::init() + * @len: Pointer to a variable to store Session-Id length + * Returns: Session-Id or %NULL if not available + * + * This function can be used to get the Session-Id from the EAP method. + * The Session-Id may already be stored in the method-specific private + * data or this function may derive the Session-Id. + */ + u8 * (*getSessionId)(struct eap_sm *sm, void *priv, size_t *len); }; @@ -304,6 +311,8 @@ Boolean eapKeyAvailable; /* peer to lower layer */ u8 *eapKeyData; /* peer to lower layer */ size_t eapKeyDataLen; /* peer to lower layer */ + u8 *eapSessionId; /* peer to lower layer */ + size_t eapSessionIdLen; /* peer to lower layer */ const struct eap_method *m; /* selected EAP method */ /* not defined in RFC 4137 */ Boolean changed; @@ -323,6 +332,7 @@ void *msg_ctx; void *scard_ctx; void *ssl_ctx; + void *ssl_ctx2; unsigned int workaround; @@ -335,6 +345,13 @@ struct wps_context *wps; int prev_failure; + + struct ext_password_data *ext_pw; + struct wpabuf *ext_pw_buf; + + int external_sim; + + unsigned int expected_failure:1; }; const u8 * eap_get_config_identity(struct eap_sm *sm, size_t *len); diff -Nru wpa-1.0/src/eap_peer/eap_ikev2.c wpa-2.1/src/eap_peer/eap_ikev2.c --- wpa-1.0/src/eap_peer/eap_ikev2.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/eap_peer/eap_ikev2.c 2014-02-04 11:23:35.000000000 +0000 @@ -1,15 +1,9 @@ /* * EAP-IKEv2 peer (RFC 5106) - * Copyright (c) 2007, Jouni Malinen + * Copyright (c) 2007-2014, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -66,6 +60,7 @@ struct eap_ikev2_data *data; const u8 *identity, *password; size_t identity_len, password_len; + int fragment_size; identity = eap_get_config_identity(sm, &identity_len); if (identity == NULL) { @@ -77,7 +72,11 @@ if (data == NULL) return NULL; data->state = WAIT_START; - data->fragment_size = IKEV2_FRAGMENT_SIZE; + fragment_size = eap_get_config_fragment_size(sm); + if (fragment_size <= 0) + data->fragment_size = IKEV2_FRAGMENT_SIZE; + else + data->fragment_size = fragment_size; data->ikev2.state = SA_INIT; data->ikev2.peer_auth = PEER_AUTH_SECRET; data->ikev2.key_pad = (u8 *) os_strdup("Key Pad for EAP-IKEv2"); @@ -481,6 +480,36 @@ } +static u8 * eap_ikev2_get_session_id(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_ikev2_data *data = priv; + u8 *sid; + size_t sid_len; + size_t offset; + + if (data->state != DONE || !data->keymat_ok) + return NULL; + + sid_len = 1 + data->ikev2.i_nonce_len + data->ikev2.r_nonce_len; + sid = os_malloc(sid_len); + if (sid) { + offset = 0; + sid[offset] = EAP_TYPE_IKEV2; + offset++; + os_memcpy(sid + offset, data->ikev2.i_nonce, + data->ikev2.i_nonce_len); + offset += data->ikev2.i_nonce_len; + os_memcpy(sid + offset, data->ikev2.r_nonce, + data->ikev2.r_nonce_len); + *len = sid_len; + wpa_hexdump(MSG_DEBUG, "EAP-IKEV2: Derived Session-Id", + sid, sid_len); + } + + return sid; +} + + int eap_peer_ikev2_register(void) { struct eap_method *eap; @@ -498,6 +527,7 @@ eap->isKeyAvailable = eap_ikev2_isKeyAvailable; eap->getKey = eap_ikev2_getKey; eap->get_emsk = eap_ikev2_get_emsk; + eap->getSessionId = eap_ikev2_get_session_id; ret = eap_peer_method_register(eap); if (ret) diff -Nru wpa-1.0/src/eap_peer/eap_leap.c wpa-2.1/src/eap_peer/eap_leap.c --- wpa-1.0/src/eap_peer/eap_leap.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/eap_peer/eap_leap.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * EAP peer method: LEAP * Copyright (c) 2004-2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff -Nru wpa-1.0/src/eap_peer/eap_md5.c wpa-2.1/src/eap_peer/eap_md5.c --- wpa-1.0/src/eap_peer/eap_md5.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/eap_peer/eap_md5.c 2014-02-04 11:23:35.000000000 +0000 @@ -1,15 +1,9 @@ /* * EAP peer method: EAP-MD5 (RFC 3748 and RFC 1994) - * Copyright (c) 2004-2006, Jouni Malinen + * Copyright (c) 2004-2012, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -92,7 +86,13 @@ id = eap_get_id(resp); rpos = wpabuf_put(resp, CHAP_MD5_LEN); - chap_md5(id, password, password_len, challenge, challenge_len, rpos); + if (chap_md5(id, password, password_len, challenge, challenge_len, + rpos)) { + wpa_printf(MSG_INFO, "EAP-MD5: CHAP MD5 operation failed"); + ret->ignore = TRUE; + wpabuf_free(resp); + return NULL; + } wpa_hexdump(MSG_MSGDUMP, "EAP-MD5: Response", rpos, CHAP_MD5_LEN); return resp; diff -Nru wpa-1.0/src/eap_peer/eap_methods.c wpa-2.1/src/eap_peer/eap_methods.c --- wpa-1.0/src/eap_peer/eap_methods.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/eap_peer/eap_methods.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * EAP peer: Method registration * Copyright (c) 2004-2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff -Nru wpa-1.0/src/eap_peer/eap_methods.h wpa-2.1/src/eap_peer/eap_methods.h --- wpa-1.0/src/eap_peer/eap_methods.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/eap_peer/eap_methods.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * EAP peer: Method registration * Copyright (c) 2004-2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef EAP_METHODS_H @@ -91,6 +85,7 @@ /* EAP peer method registration calls for statically linked in methods */ int eap_peer_md5_register(void); int eap_peer_tls_register(void); +int eap_peer_unauth_tls_register(void); int eap_peer_mschapv2_register(void); int eap_peer_peap_register(void); int eap_peer_ttls_register(void); @@ -110,5 +105,6 @@ int eap_peer_vendor_test_register(void); int eap_peer_tnc_register(void); int eap_peer_pwd_register(void); +int eap_peer_eke_register(void); #endif /* EAP_METHODS_H */ diff -Nru wpa-1.0/src/eap_peer/eap_mschapv2.c wpa-2.1/src/eap_peer/eap_mschapv2.c --- wpa-1.0/src/eap_peer/eap_mschapv2.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/eap_peer/eap_mschapv2.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * EAP peer method: EAP-MSCHAPV2 (draft-kamath-pppext-eap-mschapv2-00.txt) * Copyright (c) 2004-2008, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. * * This file implements EAP peer part of EAP-MSCHAPV2 method (EAP type 26). * draft-kamath-pppext-eap-mschapv2-00.txt defines the Microsoft EAP CHAP @@ -310,7 +304,9 @@ "EAP-MSCHAPV2: Password changed successfully"); data->prev_error = 0; os_free(config->password); - if (config->flags & EAP_CONFIG_FLAGS_PASSWORD_NTHASH) { + if (config->flags & EAP_CONFIG_FLAGS_EXT_PASSWORD) { + /* TODO: update external storage */ + } else if (config->flags & EAP_CONFIG_FLAGS_PASSWORD_NTHASH) { config->password = os_malloc(16); config->password_len = 16; if (config->password) { @@ -648,10 +644,8 @@ * must allocate a large enough temporary buffer to create that since * the received message does not include nul termination. */ - buf = os_malloc(len + 1); + buf = dup_binstr(msdata, len); if (buf) { - os_memcpy(buf, msdata, len); - buf[len] = '\0'; retry = eap_mschapv2_failure_txt(sm, data, buf); os_free(buf); } diff -Nru wpa-1.0/src/eap_peer/eap_otp.c wpa-2.1/src/eap_peer/eap_otp.c --- wpa-1.0/src/eap_peer/eap_otp.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/eap_peer/eap_otp.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * EAP peer method: EAP-OTP (RFC 3748) * Copyright (c) 2004-2006, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff -Nru wpa-1.0/src/eap_peer/eap_pax.c wpa-2.1/src/eap_peer/eap_pax.c --- wpa-1.0/src/eap_peer/eap_pax.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/eap_peer/eap_pax.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * EAP peer method: EAP-PAX (RFC 4746) * Copyright (c) 2005-2008, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff -Nru wpa-1.0/src/eap_peer/eap_peap.c wpa-2.1/src/eap_peer/eap_peap.c --- wpa-1.0/src/eap_peer/eap_peap.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/eap_peer/eap_peap.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * EAP peer method: EAP-PEAP (draft-josefsson-pppext-eap-tls-eap-10.txt) * Copyright (c) 2004-2008, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -28,7 +22,6 @@ /* Maximum supported PEAP version * 0 = Microsoft's PEAP version 0; draft-kamath-pppext-peapv0-00.txt * 1 = draft-josefsson-ppext-eap-tls-eap-05.txt - * 2 = draft-josefsson-ppext-eap-tls-eap-10.txt */ #define EAP_PEAP_VERSION 1 @@ -62,6 +55,8 @@ int resuming; /* starting a resumed session */ int reauth; /* reauthentication */ u8 *key_data; + u8 *session_id; + size_t id_len; struct wpabuf *pending_phase2_req; enum { NO_BINDING, OPTIONAL_BINDING, REQUIRE_BINDING } crypto_binding; @@ -165,7 +160,7 @@ data->phase2_type.vendor = EAP_VENDOR_IETF; data->phase2_type.method = EAP_TYPE_NONE; - if (eap_peer_tls_ssl_init(sm, &data->ssl, config)) { + if (eap_peer_tls_ssl_init(sm, &data->ssl, config, EAP_TYPE_PEAP)) { wpa_printf(MSG_INFO, "EAP-PEAP: Failed to initialize SSL."); eap_peap_deinit(sm, data); return NULL; @@ -185,6 +180,7 @@ os_free(data->phase2_types); eap_peer_tls_ssl_deinit(sm, &data->ssl); os_free(data->key_data); + os_free(data->session_id); wpabuf_free(data->pending_phase2_req); os_free(data); } @@ -318,8 +314,6 @@ len[1] = 1; tlv_type = EAP_TLV_CRYPTO_BINDING_TLV; - if (data->peap_version >= 2) - tlv_type |= EAP_TLV_TYPE_MANDATORY; wpabuf_put_be16(buf, tlv_type); wpabuf_put_be16(buf, 56); @@ -583,33 +577,6 @@ } -static struct wpabuf * eap_peapv2_tlv_eap_payload(struct wpabuf *buf) -{ - struct wpabuf *e; - struct eap_tlv_hdr *tlv; - - if (buf == NULL) - return NULL; - - /* Encapsulate EAP packet in EAP-Payload TLV */ - wpa_printf(MSG_DEBUG, "EAP-PEAPv2: Add EAP-Payload TLV"); - e = wpabuf_alloc(sizeof(*tlv) + wpabuf_len(buf)); - if (e == NULL) { - wpa_printf(MSG_DEBUG, "EAP-PEAPv2: Failed to allocate memory " - "for TLV encapsulation"); - wpabuf_free(buf); - return NULL; - } - tlv = wpabuf_put(e, sizeof(*tlv)); - tlv->tlv_type = host_to_be16(EAP_TLV_TYPE_MANDATORY | - EAP_TLV_EAP_PAYLOAD_TLV); - tlv->length = host_to_be16(wpabuf_len(buf)); - wpabuf_put_buf(e, buf); - wpabuf_free(buf); - return e; -} - - static int eap_peap_phase2_request(struct eap_sm *sm, struct eap_peap_data *data, struct eap_method_ret *ret, @@ -840,49 +807,6 @@ in_decrypted = nmsg; } - if (data->peap_version >= 2) { - struct eap_tlv_hdr *tlv; - struct wpabuf *nmsg; - - if (wpabuf_len(in_decrypted) < sizeof(*tlv) + sizeof(*hdr)) { - wpa_printf(MSG_INFO, "EAP-PEAPv2: Too short Phase 2 " - "EAP TLV"); - wpabuf_free(in_decrypted); - return 0; - } - tlv = wpabuf_mhead(in_decrypted); - if ((be_to_host16(tlv->tlv_type) & 0x3fff) != - EAP_TLV_EAP_PAYLOAD_TLV) { - wpa_printf(MSG_INFO, "EAP-PEAPv2: Not an EAP TLV"); - wpabuf_free(in_decrypted); - return 0; - } - if (sizeof(*tlv) + be_to_host16(tlv->length) > - wpabuf_len(in_decrypted)) { - wpa_printf(MSG_INFO, "EAP-PEAPv2: Invalid EAP TLV " - "length"); - wpabuf_free(in_decrypted); - return 0; - } - hdr = (struct eap_hdr *) (tlv + 1); - if (be_to_host16(hdr->length) > be_to_host16(tlv->length)) { - wpa_printf(MSG_INFO, "EAP-PEAPv2: No room for full " - "EAP packet in EAP TLV"); - wpabuf_free(in_decrypted); - return 0; - } - - nmsg = wpabuf_alloc(be_to_host16(hdr->length)); - if (nmsg == NULL) { - wpabuf_free(in_decrypted); - return 0; - } - - wpabuf_put_data(nmsg, hdr, be_to_host16(hdr->length)); - wpabuf_free(in_decrypted); - in_decrypted = nmsg; - } - hdr = wpabuf_mhead(in_decrypted); if (wpabuf_len(in_decrypted) < sizeof(*hdr)) { wpa_printf(MSG_INFO, "EAP-PEAP: Too short Phase 2 " @@ -999,11 +923,6 @@ wpa_hexdump_buf_key(MSG_DEBUG, "EAP-PEAP: Encrypting Phase 2 data", resp); /* PEAP version changes */ - if (data->peap_version >= 2) { - resp = eap_peapv2_tlv_eap_payload(resp); - if (resp == NULL) - return -1; - } if (wpabuf_len(resp) >= 5 && wpabuf_head_u8(resp)[0] == EAP_CODE_RESPONSE && eap_get_type(resp) == EAP_TYPE_TLV) @@ -1094,7 +1013,7 @@ * label, "client EAP encryption", instead. Use the old * label by default, but allow it to be configured with * phase1 parameter peaplabel=1. */ - if (data->peap_version > 1 || data->force_new_label) + if (data->force_new_label) label = "client PEAP encryption"; else label = "client EAP encryption"; @@ -1113,6 +1032,20 @@ "derive key"); } + os_free(data->session_id); + data->session_id = + eap_peer_tls_derive_session_id(sm, &data->ssl, + EAP_TYPE_PEAP, + &data->id_len); + if (data->session_id) { + wpa_hexdump(MSG_DEBUG, + "EAP-PEAP: Derived Session-Id", + data->session_id, data->id_len); + } else { + wpa_printf(MSG_ERROR, "EAP-PEAP: Failed to " + "derive Session-Id"); + } + if (sm->workaround && data->resuming) { /* * At least few RADIUS servers (Aegis v1.1.6; @@ -1184,6 +1117,8 @@ struct eap_peap_data *data = priv; os_free(data->key_data); data->key_data = NULL; + os_free(data->session_id); + data->session_id = NULL; if (eap_peer_tls_reauth_init(sm, &data->ssl)) { os_free(data); return NULL; @@ -1266,6 +1201,25 @@ } +static u8 * eap_peap_get_session_id(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_peap_data *data = priv; + u8 *id; + + if (data->session_id == NULL || !data->phase2_success) + return NULL; + + id = os_malloc(data->id_len); + if (id == NULL) + return NULL; + + *len = data->id_len; + os_memcpy(id, data->session_id, data->id_len); + + return id; +} + + int eap_peer_peap_register(void) { struct eap_method *eap; @@ -1285,6 +1239,7 @@ eap->has_reauth_data = eap_peap_has_reauth_data; eap->deinit_for_reauth = eap_peap_deinit_for_reauth; eap->init_for_reauth = eap_peap_init_for_reauth; + eap->getSessionId = eap_peap_get_session_id; ret = eap_peer_method_register(eap); if (ret) diff -Nru wpa-1.0/src/eap_peer/eap_proxy_dummy.c wpa-2.1/src/eap_peer/eap_proxy_dummy.c --- wpa-1.0/src/eap_peer/eap_proxy_dummy.c 1970-01-01 00:00:00.000000000 +0000 +++ wpa-2.1/src/eap_peer/eap_proxy_dummy.c 2014-02-04 11:23:35.000000000 +0000 @@ -0,0 +1,77 @@ +/* + * EAP proxy - dummy implementation for build testing + * Copyright (c) 2013 Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "eap_proxy.h" + +struct eap_proxy_sm * +eap_proxy_init(void *eapol_ctx, struct eapol_callbacks *eapol_cb, + void *msg_ctx) +{ + return NULL; +} + + +void eap_proxy_deinit(struct eap_proxy_sm *eap_proxy) +{ +} + + +int eap_proxy_key_available(struct eap_proxy_sm *sm) +{ + return 0; +} + + +const u8 * eap_proxy_get_eapKeyData(struct eap_proxy_sm *sm, size_t *len) +{ + return NULL; +} + + +struct wpabuf * eap_proxy_get_eapRespData(struct eap_proxy_sm *sm) +{ + return NULL; +} + + +int eap_proxy_sm_step(struct eap_proxy_sm *sm, struct eap_sm *eap_sm) +{ + return 0; +} + + +enum eap_proxy_status +eap_proxy_packet_update(struct eap_proxy_sm *eap_proxy, u8 *eapReqData, + int eapReqDataLen) +{ + return EAP_PROXY_FAILURE; +} + + +int eap_proxy_sm_get_status(struct eap_proxy_sm *sm, char *buf, size_t buflen, + int verbose) +{ + return 0; +} + + +int eap_proxy_get_imsi(struct eap_proxy_sm *eap_proxy, char *imsi_buf, + size_t *imsi_len) +{ + return -1; +} + + +int eap_proxy_notify_config(struct eap_proxy_sm *sm, + struct eap_peer_config *config) +{ + return -1; +} diff -Nru wpa-1.0/src/eap_peer/eap_proxy.h wpa-2.1/src/eap_peer/eap_proxy.h --- wpa-1.0/src/eap_peer/eap_proxy.h 1970-01-01 00:00:00.000000000 +0000 +++ wpa-2.1/src/eap_peer/eap_proxy.h 2014-02-04 11:23:35.000000000 +0000 @@ -0,0 +1,49 @@ +/* + * EAP proxy definitions + * Copyright (c) 2011-2013 Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef EAP_PROXY_H +#define EAP_PROXY_H + +struct eap_proxy_sm; +struct eapol_callbacks; +struct eap_sm; +struct eap_peer_config; + +enum eap_proxy_status { + EAP_PROXY_FAILURE = 0x00, + EAP_PROXY_SUCCESS +}; + +struct eap_proxy_sm * +eap_proxy_init(void *eapol_ctx, struct eapol_callbacks *eapol_cb, + void *msg_ctx); + +void eap_proxy_deinit(struct eap_proxy_sm *eap_proxy); + +int eap_proxy_key_available(struct eap_proxy_sm *sm); + +const u8 * eap_proxy_get_eapKeyData(struct eap_proxy_sm *sm, size_t *len); + +struct wpabuf * eap_proxy_get_eapRespData(struct eap_proxy_sm *sm); + +int eap_proxy_sm_step(struct eap_proxy_sm *sm, struct eap_sm *eap_sm); + +enum eap_proxy_status +eap_proxy_packet_update(struct eap_proxy_sm *eap_proxy, u8 *eapReqData, + int eapReqDataLen); + +int eap_proxy_sm_get_status(struct eap_proxy_sm *sm, char *buf, size_t buflen, + int verbose); + +int eap_proxy_get_imsi(struct eap_proxy_sm *eap_proxy, char *imsi_buf, + size_t *imsi_len); + +int eap_proxy_notify_config(struct eap_proxy_sm *sm, + struct eap_peer_config *config); + +#endif /* EAP_PROXY_H */ diff -Nru wpa-1.0/src/eap_peer/eap_psk.c wpa-2.1/src/eap_peer/eap_psk.c --- wpa-1.0/src/eap_peer/eap_psk.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/eap_peer/eap_psk.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * EAP peer method: EAP-PSK (RFC 4764) * Copyright (c) 2004-2008, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. * * Note: EAP-PSK is an EAP authentication method and as such, completely * different from WPA-PSK. This file is not needed for WPA-PSK functionality. @@ -27,6 +21,7 @@ struct eap_psk_data { enum { PSK_INIT, PSK_MAC_SENT, PSK_DONE } state; u8 rand_p[EAP_PSK_RAND_LEN]; + u8 rand_s[EAP_PSK_RAND_LEN]; u8 ak[EAP_PSK_AK_LEN], kdk[EAP_PSK_KDK_LEN], tek[EAP_PSK_TEK_LEN]; u8 *id_s, *id_p; size_t id_s_len, id_p_len; @@ -118,6 +113,7 @@ } wpa_hexdump(MSG_DEBUG, "EAP-PSK: RAND_S", hdr1->rand_s, EAP_PSK_RAND_LEN); + os_memcpy(data->rand_s, hdr1->rand_s, EAP_PSK_RAND_LEN); os_free(data->id_s); data->id_s_len = len - sizeof(*hdr1); data->id_s = os_malloc(data->id_s_len); @@ -440,6 +436,28 @@ } +static u8 * eap_psk_get_session_id(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_psk_data *data = priv; + u8 *id; + + if (data->state != PSK_DONE) + return NULL; + + *len = 1 + 2 * EAP_PSK_RAND_LEN; + id = os_malloc(*len); + if (id == NULL) + return NULL; + + id[0] = EAP_TYPE_PSK; + os_memcpy(id + 1, data->rand_p, EAP_PSK_RAND_LEN); + os_memcpy(id + 1 + EAP_PSK_RAND_LEN, data->rand_s, EAP_PSK_RAND_LEN); + wpa_hexdump(MSG_DEBUG, "EAP-PSK: Derived Session-Id", id, *len); + + return id; +} + + static u8 * eap_psk_get_emsk(struct eap_sm *sm, void *priv, size_t *len) { struct eap_psk_data *data = priv; @@ -474,6 +492,7 @@ eap->process = eap_psk_process; eap->isKeyAvailable = eap_psk_isKeyAvailable; eap->getKey = eap_psk_getKey; + eap->getSessionId = eap_psk_get_session_id; eap->get_emsk = eap_psk_get_emsk; ret = eap_peer_method_register(eap); diff -Nru wpa-1.0/src/eap_peer/eap_pwd.c wpa-2.1/src/eap_peer/eap_pwd.c --- wpa-1.0/src/eap_peer/eap_pwd.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/eap_peer/eap_pwd.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,19 +2,14 @@ * EAP peer method: EAP-pwd (RFC 5931) * Copyright (c) 2010, Dan Harkins * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the BSD license. - * - * Alternatively, this software may be distributed under the terms of the - * GNU General Public License version 2 as published by the Free Software - * Foundation. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" #include "common.h" +#include "crypto/sha256.h" #include "eap_peer/eap_i.h" #include "eap_common/eap_pwd_common.h" @@ -32,6 +27,12 @@ u16 group_num; EAP_PWD_group *grp; + struct wpabuf *inbuf; + size_t in_frag_pos; + struct wpabuf *outbuf; + size_t out_frag_pos; + size_t mtu; + BIGNUM *k; BIGNUM *private_value; BIGNUM *server_scalar; @@ -69,7 +70,7 @@ static void eap_pwd_state(struct eap_pwd_data *data, int state) { - wpa_printf(MSG_INFO, "EAP-PWD: %s -> %s", + wpa_printf(MSG_DEBUG, "EAP-PWD: %s -> %s", eap_pwd_state_txt(data->state), eap_pwd_state_txt(state)); data->state = state; } @@ -80,6 +81,7 @@ struct eap_pwd_data *data; const u8 *identity, *password; size_t identity_len, password_len; + int fragment_size; password = eap_get_config_password(sm, &password_len); if (password == NULL) { @@ -124,6 +126,14 @@ os_memcpy(data->password, password, password_len); data->password_len = password_len; + data->out_frag_pos = data->in_frag_pos = 0; + data->inbuf = data->outbuf = NULL; + fragment_size = eap_get_config_fragment_size(sm); + if (fragment_size <= 0) + data->mtu = 1020; /* default from RFC 5931 */ + else + data->mtu = fragment_size; + data->state = PWD_ID_Req; return data; @@ -174,23 +184,24 @@ } -static struct wpabuf * +static void eap_pwd_perform_id_exchange(struct eap_sm *sm, struct eap_pwd_data *data, struct eap_method_ret *ret, const struct wpabuf *reqData, const u8 *payload, size_t payload_len) { struct eap_pwd_id *id; - struct wpabuf *resp; if (data->state != PWD_ID_Req) { ret->ignore = TRUE; - return NULL; + eap_pwd_state(data, FAILURE); + return; } if (payload_len < sizeof(struct eap_pwd_id)) { ret->ignore = TRUE; - return NULL; + eap_pwd_state(data, FAILURE); + return; } id = (struct eap_pwd_id *) payload; @@ -198,16 +209,18 @@ if ((id->random_function != EAP_PWD_DEFAULT_RAND_FUNC) || (id->prf != EAP_PWD_DEFAULT_PRF)) { ret->ignore = TRUE; - return NULL; + eap_pwd_state(data, FAILURE); + return; } - wpa_printf(MSG_DEBUG, "EAP-PWD (peer): server said group %d", + wpa_printf(MSG_DEBUG, "EAP-PWD (peer): using group %d", data->group_num); data->id_server = os_malloc(payload_len - sizeof(struct eap_pwd_id)); if (data->id_server == NULL) { wpa_printf(MSG_INFO, "EAP-PWD: memory allocation id fail"); - return NULL; + eap_pwd_state(data, FAILURE); + return; } data->id_server_len = payload_len - sizeof(struct eap_pwd_id); os_memcpy(data->id_server, id->identity, data->id_server_len); @@ -218,7 +231,8 @@ NULL) { wpa_printf(MSG_INFO, "EAP-PWD: failed to allocate memory for " "group"); - return NULL; + eap_pwd_state(data, FAILURE); + return; } /* compute PWE */ @@ -228,39 +242,36 @@ data->id_peer, data->id_peer_len, id->token)) { wpa_printf(MSG_INFO, "EAP-PWD (peer): unable to compute PWE"); - return NULL; + eap_pwd_state(data, FAILURE); + return; } - wpa_printf(MSG_INFO, "EAP-PWD (peer): computed %d bit PWE...", + wpa_printf(MSG_DEBUG, "EAP-PWD (peer): computed %d bit PWE...", BN_num_bits(data->grp->prime)); - resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PWD, - 1 + sizeof(struct eap_pwd_id) + data->id_peer_len, - EAP_CODE_RESPONSE, eap_get_id(reqData)); - if (resp == NULL) - return NULL; - - wpabuf_put_u8(resp, EAP_PWD_OPCODE_ID_EXCH); - wpabuf_put_be16(resp, data->group_num); - wpabuf_put_u8(resp, EAP_PWD_DEFAULT_RAND_FUNC); - wpabuf_put_u8(resp, EAP_PWD_DEFAULT_PRF); - wpabuf_put_data(resp, id->token, sizeof(id->token)); - wpabuf_put_u8(resp, EAP_PWD_PREP_NONE); - wpabuf_put_data(resp, data->id_peer, data->id_peer_len); + data->outbuf = wpabuf_alloc(sizeof(struct eap_pwd_id) + + data->id_peer_len); + if (data->outbuf == NULL) { + eap_pwd_state(data, FAILURE); + return; + } + wpabuf_put_be16(data->outbuf, data->group_num); + wpabuf_put_u8(data->outbuf, EAP_PWD_DEFAULT_RAND_FUNC); + wpabuf_put_u8(data->outbuf, EAP_PWD_DEFAULT_PRF); + wpabuf_put_data(data->outbuf, id->token, sizeof(id->token)); + wpabuf_put_u8(data->outbuf, EAP_PWD_PREP_NONE); + wpabuf_put_data(data->outbuf, data->id_peer, data->id_peer_len); eap_pwd_state(data, PWD_Commit_Req); - - return resp; } -static struct wpabuf * +static void eap_pwd_perform_commit_exchange(struct eap_sm *sm, struct eap_pwd_data *data, struct eap_method_ret *ret, const struct wpabuf *reqData, const u8 *payload, size_t payload_len) { - struct wpabuf *resp = NULL; EC_POINT *K = NULL, *point = NULL; BIGNUM *mask = NULL, *x = NULL, *y = NULL, *cofactor = NULL; u16 offset; @@ -422,19 +433,15 @@ offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y); BN_bn2bin(y, element + BN_num_bytes(data->grp->prime) + offset); - resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PWD, - sizeof(struct eap_pwd_hdr) + - BN_num_bytes(data->grp->order) + - (2 * BN_num_bytes(data->grp->prime)), - EAP_CODE_RESPONSE, eap_get_id(reqData)); - if (resp == NULL) + data->outbuf = wpabuf_alloc(BN_num_bytes(data->grp->order) + + 2 * BN_num_bytes(data->grp->prime)); + if (data->outbuf == NULL) goto fin; - wpabuf_put_u8(resp, EAP_PWD_OPCODE_COMMIT_EXCH); - /* we send the element as (x,y) follwed by the scalar */ - wpabuf_put_data(resp, element, (2 * BN_num_bytes(data->grp->prime))); - wpabuf_put_data(resp, scalar, BN_num_bytes(data->grp->order)); + wpabuf_put_data(data->outbuf, element, + 2 * BN_num_bytes(data->grp->prime)); + wpabuf_put_data(data->outbuf, scalar, BN_num_bytes(data->grp->order)); fin: os_free(scalar); @@ -444,27 +451,24 @@ BN_free(cofactor); EC_POINT_free(K); EC_POINT_free(point); - if (resp == NULL) + if (data->outbuf == NULL) eap_pwd_state(data, FAILURE); else eap_pwd_state(data, PWD_Confirm_Req); - - return resp; } -static struct wpabuf * +static void eap_pwd_perform_confirm_exchange(struct eap_sm *sm, struct eap_pwd_data *data, struct eap_method_ret *ret, const struct wpabuf *reqData, const u8 *payload, size_t payload_len) { - struct wpabuf *resp = NULL; BIGNUM *x = NULL, *y = NULL; - HMAC_CTX ctx; + struct crypto_hash *hash; u32 cs; u16 grp; - u8 conf[SHA256_DIGEST_LENGTH], *cruft = NULL, *ptr; + u8 conf[SHA256_MAC_LEN], *cruft = NULL, *ptr; int offset; /* @@ -482,7 +486,7 @@ /* each component of the cruft will be at most as big as the prime */ if (((cruft = os_malloc(BN_num_bytes(data->grp->prime))) == NULL) || ((x = BN_new()) == NULL) || ((y = BN_new()) == NULL)) { - wpa_printf(MSG_INFO, "EAP-PWD (server): debug allocation " + wpa_printf(MSG_INFO, "EAP-PWD (server): confirm allocation " "fail"); goto fin; } @@ -491,7 +495,9 @@ * server's commit is H(k | server_element | server_scalar | * peer_element | peer_scalar | ciphersuite) */ - H_Init(&ctx); + hash = eap_pwd_h_init(); + if (hash == NULL) + goto fin; /* * zero the memory each time because this is mod prime math and some @@ -500,7 +506,7 @@ os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(data->k); BN_bn2bin(data->k, cruft + offset); - H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime)); + eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); /* server element: x, y */ if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group, @@ -513,18 +519,18 @@ os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x); BN_bn2bin(x, cruft + offset); - H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime)); + eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y); BN_bn2bin(y, cruft + offset); - H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime)); + eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); /* server scalar */ os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); offset = BN_num_bytes(data->grp->order) - BN_num_bytes(data->server_scalar); BN_bn2bin(data->server_scalar, cruft + offset); - H_Update(&ctx, cruft, BN_num_bytes(data->grp->order)); + eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order)); /* my element: x, y */ if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group, @@ -538,27 +544,27 @@ os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x); BN_bn2bin(x, cruft + offset); - H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime)); + eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y); BN_bn2bin(y, cruft + offset); - H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime)); + eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); /* my scalar */ os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); offset = BN_num_bytes(data->grp->order) - BN_num_bytes(data->my_scalar); BN_bn2bin(data->my_scalar, cruft + offset); - H_Update(&ctx, cruft, BN_num_bytes(data->grp->order)); + eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order)); /* the ciphersuite */ - H_Update(&ctx, (u8 *) &cs, sizeof(u32)); + eap_pwd_h_update(hash, (u8 *) &cs, sizeof(u32)); /* random function fin */ - H_Final(&ctx, conf); + eap_pwd_h_final(hash, conf); ptr = (u8 *) payload; - if (os_memcmp(conf, ptr, SHA256_DIGEST_LENGTH)) { + if (os_memcmp(conf, ptr, SHA256_MAC_LEN)) { wpa_printf(MSG_INFO, "EAP-PWD (peer): confirm did not verify"); goto fin; } @@ -570,13 +576,15 @@ * H(k | peer_element | peer_scalar | server_element | server_scalar | * ciphersuite) */ - H_Init(&ctx); + hash = eap_pwd_h_init(); + if (hash == NULL) + goto fin; /* k */ os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(data->k); BN_bn2bin(data->k, cruft + offset); - H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime)); + eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); /* my element */ if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group, @@ -589,18 +597,18 @@ os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x); BN_bn2bin(x, cruft + offset); - H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime)); + eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y); BN_bn2bin(y, cruft + offset); - H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime)); + eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); /* my scalar */ os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); offset = BN_num_bytes(data->grp->order) - BN_num_bytes(data->my_scalar); BN_bn2bin(data->my_scalar, cruft + offset); - H_Update(&ctx, cruft, BN_num_bytes(data->grp->order)); + eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order)); /* server element: x, y */ if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group, @@ -613,33 +621,24 @@ os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x); BN_bn2bin(x, cruft + offset); - H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime)); + eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y); BN_bn2bin(y, cruft + offset); - H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime)); + eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); /* server scalar */ os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); offset = BN_num_bytes(data->grp->order) - BN_num_bytes(data->server_scalar); BN_bn2bin(data->server_scalar, cruft + offset); - H_Update(&ctx, cruft, BN_num_bytes(data->grp->order)); + eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order)); /* the ciphersuite */ - H_Update(&ctx, (u8 *) &cs, sizeof(u32)); + eap_pwd_h_update(hash, (u8 *) &cs, sizeof(u32)); /* all done */ - H_Final(&ctx, conf); - - resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PWD, - sizeof(struct eap_pwd_hdr) + SHA256_DIGEST_LENGTH, - EAP_CODE_RESPONSE, eap_get_id(reqData)); - if (resp == NULL) - goto fin; - - wpabuf_put_u8(resp, EAP_PWD_OPCODE_CONFIRM_EXCH); - wpabuf_put_data(resp, conf, SHA256_DIGEST_LENGTH); + eap_pwd_h_final(hash, conf); if (compute_keys(data->grp, data->bnctx, data->k, data->my_scalar, data->server_scalar, conf, ptr, @@ -649,20 +648,24 @@ goto fin; } + data->outbuf = wpabuf_alloc(SHA256_MAC_LEN); + if (data->outbuf == NULL) + goto fin; + + wpabuf_put_data(data->outbuf, conf, SHA256_MAC_LEN); + fin: os_free(cruft); BN_free(x); BN_free(y); ret->methodState = METHOD_DONE; - if (resp == NULL) { + if (data->outbuf == NULL) { ret->decision = DECISION_FAIL; eap_pwd_state(data, FAILURE); } else { ret->decision = DECISION_UNCOND_SUCC; eap_pwd_state(data, SUCCESS); } - - return resp; } @@ -672,42 +675,203 @@ { struct eap_pwd_data *data = priv; struct wpabuf *resp = NULL; - const u8 *pos; + const u8 *pos, *buf; size_t len; - u8 exch; + u16 tot_len = 0; + u8 lm_exch; pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PWD, reqData, &len); if ((pos == NULL) || (len < 1)) { + wpa_printf(MSG_DEBUG, "EAP-pwd: Got a frame but pos is %s and " + "len is %d", + pos == NULL ? "NULL" : "not NULL", (int) len); ret->ignore = TRUE; return NULL; } - wpa_printf(MSG_INFO, "EAP-pwd: Received frame: opcode %d", *pos); - ret->ignore = FALSE; ret->methodState = METHOD_MAY_CONT; ret->decision = DECISION_FAIL; ret->allowNotifications = FALSE; - exch = *pos & 0x3f; - switch (exch) { - case EAP_PWD_OPCODE_ID_EXCH: - resp = eap_pwd_perform_id_exchange(sm, data, ret, reqData, - pos + 1, len - 1); + lm_exch = *pos; + pos++; /* skip over the bits and the exch */ + len--; + + /* + * we're fragmenting so send out the next fragment + */ + if (data->out_frag_pos) { + /* + * this should be an ACK + */ + if (len) + wpa_printf(MSG_INFO, "Bad Response! Fragmenting but " + "not an ACK"); + + wpa_printf(MSG_DEBUG, "EAP-pwd: Got an ACK for a fragment"); + /* + * check if there are going to be more fragments + */ + len = wpabuf_len(data->outbuf) - data->out_frag_pos; + if ((len + EAP_PWD_HDR_SIZE) > data->mtu) { + len = data->mtu - EAP_PWD_HDR_SIZE; + EAP_PWD_SET_MORE_BIT(lm_exch); + } + resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PWD, + EAP_PWD_HDR_SIZE + len, + EAP_CODE_RESPONSE, eap_get_id(reqData)); + if (resp == NULL) { + wpa_printf(MSG_INFO, "Unable to allocate memory for " + "next fragment!"); + return NULL; + } + wpabuf_put_u8(resp, lm_exch); + buf = wpabuf_head_u8(data->outbuf); + wpabuf_put_data(resp, buf + data->out_frag_pos, len); + data->out_frag_pos += len; + /* + * this is the last fragment so get rid of the out buffer + */ + if (data->out_frag_pos >= wpabuf_len(data->outbuf)) { + wpabuf_free(data->outbuf); + data->outbuf = NULL; + data->out_frag_pos = 0; + } + wpa_printf(MSG_DEBUG, "EAP-pwd: Send %s fragment of %d bytes", + data->out_frag_pos == 0 ? "last" : "next", + (int) len); + return resp; + } + + /* + * see if this is a fragment that needs buffering + * + * if it's the first fragment there'll be a length field + */ + if (EAP_PWD_GET_LENGTH_BIT(lm_exch)) { + tot_len = WPA_GET_BE16(pos); + wpa_printf(MSG_DEBUG, "EAP-pwd: Incoming fragments whose " + "total length = %d", tot_len); + data->inbuf = wpabuf_alloc(tot_len); + if (data->inbuf == NULL) { + wpa_printf(MSG_INFO, "Out of memory to buffer " + "fragments!"); + return NULL; + } + pos += sizeof(u16); + len -= sizeof(u16); + } + /* + * buffer and ACK the fragment + */ + if (EAP_PWD_GET_MORE_BIT(lm_exch)) { + data->in_frag_pos += len; + if (data->in_frag_pos > wpabuf_size(data->inbuf)) { + wpa_printf(MSG_INFO, "EAP-pwd: Buffer overflow attack " + "detected (%d vs. %d)!", + (int) data->in_frag_pos, + (int) wpabuf_len(data->inbuf)); + wpabuf_free(data->inbuf); + data->in_frag_pos = 0; + return NULL; + } + wpabuf_put_data(data->inbuf, pos, len); + + resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PWD, + EAP_PWD_HDR_SIZE, + EAP_CODE_RESPONSE, eap_get_id(reqData)); + if (resp != NULL) + wpabuf_put_u8(resp, (EAP_PWD_GET_EXCHANGE(lm_exch))); + wpa_printf(MSG_DEBUG, "EAP-pwd: ACKing a %d byte fragment", + (int) len); + return resp; + } + /* + * we're buffering and this is the last fragment + */ + if (data->in_frag_pos) { + wpabuf_put_data(data->inbuf, pos, len); + wpa_printf(MSG_DEBUG, "EAP-pwd: Last fragment, %d bytes", + (int) len); + data->in_frag_pos += len; + pos = wpabuf_head_u8(data->inbuf); + len = data->in_frag_pos; + } + wpa_printf(MSG_DEBUG, "EAP-pwd: processing frame: exch %d, len %d", + EAP_PWD_GET_EXCHANGE(lm_exch), (int) len); + + switch (EAP_PWD_GET_EXCHANGE(lm_exch)) { + case EAP_PWD_OPCODE_ID_EXCH: + eap_pwd_perform_id_exchange(sm, data, ret, reqData, + pos, len); break; - case EAP_PWD_OPCODE_COMMIT_EXCH: - resp = eap_pwd_perform_commit_exchange(sm, data, ret, reqData, - pos + 1, len - 1); + case EAP_PWD_OPCODE_COMMIT_EXCH: + eap_pwd_perform_commit_exchange(sm, data, ret, reqData, + pos, len); break; - case EAP_PWD_OPCODE_CONFIRM_EXCH: - resp = eap_pwd_perform_confirm_exchange(sm, data, ret, reqData, - pos + 1, len - 1); + case EAP_PWD_OPCODE_CONFIRM_EXCH: + eap_pwd_perform_confirm_exchange(sm, data, ret, reqData, + pos, len); break; - default: + default: wpa_printf(MSG_INFO, "EAP-pwd: Ignoring message with unknown " - "opcode %d", exch); + "opcode %d", lm_exch); break; } + /* + * if we buffered the just processed input now's the time to free it + */ + if (data->in_frag_pos) { + wpabuf_free(data->inbuf); + data->in_frag_pos = 0; + } + + if (data->outbuf == NULL) + return NULL; /* generic failure */ + + /* + * we have output! Do we need to fragment it? + */ + len = wpabuf_len(data->outbuf); + if ((len + EAP_PWD_HDR_SIZE) > data->mtu) { + resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PWD, data->mtu, + EAP_CODE_RESPONSE, eap_get_id(reqData)); + /* + * if so it's the first so include a length field + */ + EAP_PWD_SET_LENGTH_BIT(lm_exch); + EAP_PWD_SET_MORE_BIT(lm_exch); + tot_len = len; + /* + * keep the packet at the MTU + */ + len = data->mtu - EAP_PWD_HDR_SIZE - sizeof(u16); + wpa_printf(MSG_DEBUG, "EAP-pwd: Fragmenting output, total " + "length = %d", tot_len); + } else { + resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PWD, + EAP_PWD_HDR_SIZE + len, + EAP_CODE_RESPONSE, eap_get_id(reqData)); + } + if (resp == NULL) + return NULL; + + wpabuf_put_u8(resp, lm_exch); + if (EAP_PWD_GET_LENGTH_BIT(lm_exch)) { + wpabuf_put_be16(resp, tot_len); + data->out_frag_pos += len; + } + buf = wpabuf_head_u8(data->outbuf); + wpabuf_put_data(resp, buf, len); + /* + * if we're not fragmenting then there's no need to carry this around + */ + if (data->out_frag_pos == 0) { + wpabuf_free(data->outbuf); + data->outbuf = NULL; + data->out_frag_pos = 0; + } return resp; } diff -Nru wpa-1.0/src/eap_peer/eap_sake.c wpa-2.1/src/eap_peer/eap_sake.c --- wpa-1.0/src/eap_peer/eap_sake.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/eap_peer/eap_sake.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * EAP peer method: EAP-SAKE (RFC 4763) * Copyright (c) 2006-2008, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -458,6 +452,28 @@ } +static u8 * eap_sake_get_session_id(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_sake_data *data = priv; + u8 *id; + + if (data->state != SUCCESS) + return NULL; + + *len = 1 + 2 * EAP_SAKE_RAND_LEN; + id = os_malloc(*len); + if (id == NULL) + return NULL; + + id[0] = EAP_TYPE_SAKE; + os_memcpy(id + 1, data->rand_s, EAP_SAKE_RAND_LEN); + os_memcpy(id + 1 + EAP_SAKE_RAND_LEN, data->rand_s, EAP_SAKE_RAND_LEN); + wpa_hexdump(MSG_DEBUG, "EAP-SAKE: Derived Session-Id", id, *len); + + return id; +} + + static u8 * eap_sake_get_emsk(struct eap_sm *sm, void *priv, size_t *len) { struct eap_sake_data *data = priv; @@ -491,6 +507,7 @@ eap->process = eap_sake_process; eap->isKeyAvailable = eap_sake_isKeyAvailable; eap->getKey = eap_sake_getKey; + eap->getSessionId = eap_sake_get_session_id; eap->get_emsk = eap_sake_get_emsk; ret = eap_peer_method_register(eap); diff -Nru wpa-1.0/src/eap_peer/eap_sim.c wpa-2.1/src/eap_peer/eap_sim.c --- wpa-1.0/src/eap_peer/eap_sim.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/eap_peer/eap_sim.c 2014-02-04 11:23:35.000000000 +0000 @@ -1,15 +1,9 @@ /* * EAP peer method: EAP-SIM (RFC 4186) - * Copyright (c) 2004-2008, Jouni Malinen + * Copyright (c) 2004-2012, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -123,6 +117,15 @@ NULL; } + if (config && config->anonymous_identity) { + data->pseudonym = os_malloc(config->anonymous_identity_len); + if (data->pseudonym) { + os_memcpy(data->pseudonym, config->anonymous_identity, + config->anonymous_identity_len); + data->pseudonym_len = config->anonymous_identity_len; + } + } + eap_sim_state(data, CONTINUE); return data; @@ -142,6 +145,80 @@ } +static int eap_sim_ext_sim_req(struct eap_sm *sm, struct eap_sim_data *data) +{ + char req[200], *pos, *end; + size_t i; + + wpa_printf(MSG_DEBUG, "EAP-SIM: Use external SIM processing"); + pos = req; + end = pos + sizeof(req); + pos += os_snprintf(pos, end - pos, "GSM-AUTH"); + for (i = 0; i < data->num_chal; i++) { + pos += os_snprintf(pos, end - pos, ":"); + pos += wpa_snprintf_hex(pos, end - pos, data->rand[i], + GSM_RAND_LEN); + } + + eap_sm_request_sim(sm, req); + return 1; +} + + +static int eap_sim_ext_sim_result(struct eap_sm *sm, struct eap_sim_data *data, + struct eap_peer_config *conf) +{ + char *resp, *pos; + size_t i; + + wpa_printf(MSG_DEBUG, + "EAP-SIM: Use result from external SIM processing"); + + resp = conf->external_sim_resp; + conf->external_sim_resp = NULL; + + if (os_strncmp(resp, "GSM-AUTH:", 9) != 0) { + wpa_printf(MSG_DEBUG, "EAP-SIM: Unrecognized external SIM processing response"); + os_free(resp); + return -1; + } + + pos = resp + 9; + for (i = 0; i < data->num_chal; i++) { + wpa_hexdump(MSG_DEBUG, "EAP-SIM: RAND", + data->rand[i], GSM_RAND_LEN); + + if (hexstr2bin(pos, data->kc[i], EAP_SIM_KC_LEN) < 0) + goto invalid; + wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: Kc", + data->kc[i], EAP_SIM_KC_LEN); + pos += EAP_SIM_KC_LEN * 2; + if (*pos != ':') + goto invalid; + pos++; + + if (hexstr2bin(pos, data->sres[i], EAP_SIM_SRES_LEN) < 0) + goto invalid; + wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: SRES", + data->sres[i], EAP_SIM_SRES_LEN); + pos += EAP_SIM_SRES_LEN * 2; + if (i + 1 < data->num_chal) { + if (*pos != ':') + goto invalid; + pos++; + } + } + + os_free(resp); + return 0; + +invalid: + wpa_printf(MSG_DEBUG, "EAP-SIM: Invalid external SIM processing GSM-AUTH response"); + os_free(resp); + return -1; +} + + static int eap_sim_gsm_auth(struct eap_sm *sm, struct eap_sim_data *data) { struct eap_peer_config *conf; @@ -151,6 +228,14 @@ conf = eap_get_config(sm); if (conf == NULL) return -1; + + if (sm->external_sim) { + if (conf->external_sim_resp) + return eap_sim_ext_sim_result(sm, data, conf); + else + return eap_sim_ext_sim_req(sm, data); + } + if (conf->pcsc) { if (scard_gsm_auth(sm->scard_ctx, data->rand[0], data->sres[0], data->kc[0]) || @@ -264,13 +349,15 @@ #define CLEAR_REAUTH_ID 0x02 #define CLEAR_EAP_ID 0x04 -static void eap_sim_clear_identities(struct eap_sim_data *data, int id) +static void eap_sim_clear_identities(struct eap_sm *sm, + struct eap_sim_data *data, int id) { if ((id & CLEAR_PSEUDONYM) && data->pseudonym) { wpa_printf(MSG_DEBUG, "EAP-SIM: forgetting old pseudonym"); os_free(data->pseudonym); data->pseudonym = NULL; data->pseudonym_len = 0; + eap_set_anon_id(sm, NULL, 0); } if ((id & CLEAR_REAUTH_ID) && data->reauth_id) { wpa_printf(MSG_DEBUG, "EAP-SIM: forgetting old reauth_id"); @@ -325,6 +412,7 @@ realm, realm_len); } data->pseudonym_len = attr->next_pseudonym_len + realm_len; + eap_set_anon_id(sm, data->pseudonym, data->pseudonym_len); } if (attr->next_reauth_id) { @@ -358,6 +446,8 @@ data->num_id_req = 0; data->num_notification = 0; + wpa_printf(MSG_DEBUG, "EAP-SIM: Send Client-Error (error code %d)", + err); msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, EAP_TYPE_SIM, EAP_SIM_SUBTYPE_CLIENT_ERROR); eap_sim_msg_add(msg, EAP_SIM_AT_CLIENT_ERROR_CODE, err, NULL, 0); @@ -382,16 +472,16 @@ data->pseudonym) { identity = data->pseudonym; identity_len = data->pseudonym_len; - eap_sim_clear_identities(data, CLEAR_REAUTH_ID); + eap_sim_clear_identities(sm, data, CLEAR_REAUTH_ID); } else if (id_req != NO_ID_REQ) { identity = eap_get_config_identity(sm, &identity_len); if (identity) { - eap_sim_clear_identities(data, CLEAR_PSEUDONYM | + eap_sim_clear_identities(sm, data, CLEAR_PSEUDONYM | CLEAR_REAUTH_ID); } } if (id_req != NO_ID_REQ) - eap_sim_clear_identities(data, CLEAR_EAP_ID); + eap_sim_clear_identities(sm, data, CLEAR_EAP_ID); wpa_printf(MSG_DEBUG, "Generating EAP-SIM Start (id=%d)", id); msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, @@ -438,7 +528,8 @@ static struct wpabuf * eap_sim_response_reauth(struct eap_sim_data *data, - u8 id, int counter_too_small) + u8 id, int counter_too_small, + const u8 *nonce_s) { struct eap_sim_msg *msg; unsigned int counter; @@ -473,7 +564,7 @@ } wpa_printf(MSG_DEBUG, " AT_MAC"); eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC); - return eap_sim_msg_finish(msg, data->k_aut, data->nonce_s, + return eap_sim_msg_finish(msg, data->k_aut, nonce_s, EAP_SIM_NONCE_S_LEN); } @@ -596,6 +687,7 @@ const u8 *identity; size_t identity_len; struct eap_sim_attrs eattr; + int res; wpa_printf(MSG_DEBUG, "EAP-SIM: subtype Challenge"); data->reauth = 0; @@ -639,8 +731,13 @@ os_memcpy(data->rand, attr->rand, attr->num_chal * GSM_RAND_LEN); data->num_chal = attr->num_chal; - - if (eap_sim_gsm_auth(sm, data)) { + + res = eap_sim_gsm_auth(sm, data); + if (res > 0) { + wpa_printf(MSG_DEBUG, "EAP-SIM: Wait for external SIM processing"); + return NULL; + } + if (res) { wpa_printf(MSG_WARNING, "EAP-SIM: GSM authentication failed"); return eap_sim_client_error(data, id, EAP_SIM_UNABLE_TO_PROCESS_PACKET); @@ -673,7 +770,7 @@ * other words, if no new reauth identity is received, full * authentication will be used on next reauthentication (using * pseudonym identity or permanent identity). */ - eap_sim_clear_identities(data, CLEAR_REAUTH_ID | CLEAR_EAP_ID); + eap_sim_clear_identities(sm, data, CLEAR_REAUTH_ID | CLEAR_EAP_ID); if (attr->encr_data) { u8 *decrypted; @@ -869,7 +966,7 @@ data->reauth_id = NULL; data->reauth_id_len = 0; os_free(decrypted); - return eap_sim_response_reauth(data, id, 1); + return eap_sim_response_reauth(data, id, 1, eattr.nonce_s); } data->counter = eattr.counter; @@ -881,7 +978,7 @@ data->reauth_id, data->reauth_id_len, data->nonce_s, data->mk, data->msk, data->emsk); - eap_sim_clear_identities(data, CLEAR_REAUTH_ID | CLEAR_EAP_ID); + eap_sim_clear_identities(sm, data, CLEAR_REAUTH_ID | CLEAR_EAP_ID); eap_sim_learn_ids(sm, data, &eattr); if (data->result_ind && attr->result_ind) @@ -897,10 +994,11 @@ if (data->counter > EAP_SIM_MAX_FAST_REAUTHS) { wpa_printf(MSG_DEBUG, "EAP-SIM: Maximum number of " "fast reauths performed - force fullauth"); - eap_sim_clear_identities(data, CLEAR_REAUTH_ID | CLEAR_EAP_ID); + eap_sim_clear_identities(sm, data, + CLEAR_REAUTH_ID | CLEAR_EAP_ID); } os_free(decrypted); - return eap_sim_response_reauth(data, id, 0); + return eap_sim_response_reauth(data, id, 0, data->nonce_s); } @@ -1008,7 +1106,7 @@ static void eap_sim_deinit_for_reauth(struct eap_sm *sm, void *priv) { struct eap_sim_data *data = priv; - eap_sim_clear_identities(data, CLEAR_EAP_ID); + eap_sim_clear_identities(sm, data, CLEAR_EAP_ID); data->use_result_ind = 0; } @@ -1074,6 +1172,29 @@ } +static u8 * eap_sim_get_session_id(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_sim_data *data = priv; + u8 *id; + + if (data->state != SUCCESS) + return NULL; + + *len = 1 + data->num_chal * GSM_RAND_LEN + EAP_SIM_NONCE_MT_LEN; + id = os_malloc(*len); + if (id == NULL) + return NULL; + + id[0] = EAP_TYPE_SIM; + os_memcpy(id + 1, data->rand, data->num_chal * GSM_RAND_LEN); + os_memcpy(id + 1 + data->num_chal * GSM_RAND_LEN, data->nonce_mt, + EAP_SIM_NONCE_MT_LEN); + wpa_hexdump(MSG_DEBUG, "EAP-SIM: Derived Session-Id", id, *len); + + return id; +} + + static u8 * eap_sim_get_emsk(struct eap_sm *sm, void *priv, size_t *len) { struct eap_sim_data *data = priv; @@ -1108,6 +1229,7 @@ eap->process = eap_sim_process; eap->isKeyAvailable = eap_sim_isKeyAvailable; eap->getKey = eap_sim_getKey; + eap->getSessionId = eap_sim_get_session_id; eap->has_reauth_data = eap_sim_has_reauth_data; eap->deinit_for_reauth = eap_sim_deinit_for_reauth; eap->init_for_reauth = eap_sim_init_for_reauth; diff -Nru wpa-1.0/src/eap_peer/eap_tls.c wpa-2.1/src/eap_peer/eap_tls.c --- wpa-1.0/src/eap_peer/eap_tls.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/eap_peer/eap_tls.c 2014-02-04 11:23:35.000000000 +0000 @@ -1,15 +1,9 @@ /* * EAP peer method: EAP-TLS (RFC 2716) - * Copyright (c) 2004-2008, Jouni Malinen + * Copyright (c) 2004-2008, 2012, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -27,6 +21,10 @@ struct eap_tls_data { struct eap_ssl_data ssl; u8 *key_data; + u8 *session_id; + size_t id_len; + void *ssl_ctx; + u8 eap_type; }; @@ -46,7 +44,10 @@ if (data == NULL) return NULL; - if (eap_peer_tls_ssl_init(sm, &data->ssl, config)) { + data->ssl_ctx = sm->init_phase2 && sm->ssl_ctx2 ? sm->ssl_ctx2 : + sm->ssl_ctx; + + if (eap_peer_tls_ssl_init(sm, &data->ssl, config, EAP_TYPE_TLS)) { wpa_printf(MSG_INFO, "EAP-TLS: Failed to initialize SSL."); eap_tls_deinit(sm, data); if (config->engine) { @@ -64,10 +65,39 @@ return NULL; } + data->eap_type = EAP_TYPE_TLS; + return data; } +#ifdef EAP_UNAUTH_TLS +static void * eap_unauth_tls_init(struct eap_sm *sm) +{ + struct eap_tls_data *data; + struct eap_peer_config *config = eap_get_config(sm); + + data = os_zalloc(sizeof(*data)); + if (data == NULL) + return NULL; + + data->ssl_ctx = sm->init_phase2 && sm->ssl_ctx2 ? sm->ssl_ctx2 : + sm->ssl_ctx; + + if (eap_peer_tls_ssl_init(sm, &data->ssl, config, + EAP_UNAUTH_TLS_TYPE)) { + wpa_printf(MSG_INFO, "EAP-TLS: Failed to initialize SSL."); + eap_tls_deinit(sm, data); + return NULL; + } + + data->eap_type = EAP_UNAUTH_TLS_TYPE; + + return data; +} +#endif /* EAP_UNAUTH_TLS */ + + static void eap_tls_deinit(struct eap_sm *sm, void *priv) { struct eap_tls_data *data = priv; @@ -75,6 +105,7 @@ return; eap_peer_tls_ssl_deinit(sm, &data->ssl); os_free(data->key_data); + os_free(data->session_id); os_free(data); } @@ -111,7 +142,7 @@ return resp; } - return eap_peer_tls_build_ack(id, EAP_TYPE_TLS, 0); + return eap_peer_tls_build_ack(id, data->eap_type, 0); } @@ -137,6 +168,17 @@ } else { wpa_printf(MSG_INFO, "EAP-TLS: Failed to derive key"); } + + os_free(data->session_id); + data->session_id = eap_peer_tls_derive_session_id(sm, &data->ssl, + EAP_TYPE_TLS, + &data->id_len); + if (data->session_id) { + wpa_hexdump(MSG_DEBUG, "EAP-TLS: Derived Session-Id", + data->session_id, data->id_len); + } else { + wpa_printf(MSG_ERROR, "EAP-TLS: Failed to derive Session-Id"); + } } @@ -151,7 +193,7 @@ const u8 *pos; struct eap_tls_data *data = priv; - pos = eap_peer_tls_process_init(sm, &data->ssl, EAP_TYPE_TLS, ret, + pos = eap_peer_tls_process_init(sm, &data->ssl, data->eap_type, ret, reqData, &left, &flags); if (pos == NULL) return NULL; @@ -164,19 +206,19 @@ } resp = NULL; - res = eap_peer_tls_process_helper(sm, &data->ssl, EAP_TYPE_TLS, 0, id, - pos, left, &resp); + res = eap_peer_tls_process_helper(sm, &data->ssl, data->eap_type, 0, + id, pos, left, &resp); if (res < 0) { return eap_tls_failure(sm, data, ret, res, resp, id); } - if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) + if (tls_connection_established(data->ssl_ctx, data->ssl.conn)) eap_tls_success(sm, data, ret); if (res == 1) { wpabuf_free(resp); - return eap_peer_tls_build_ack(id, EAP_TYPE_TLS, 0); + return eap_peer_tls_build_ack(id, data->eap_type, 0); } return resp; @@ -186,7 +228,7 @@ static Boolean eap_tls_has_reauth_data(struct eap_sm *sm, void *priv) { struct eap_tls_data *data = priv; - return tls_connection_established(sm->ssl_ctx, data->ssl.conn); + return tls_connection_established(data->ssl_ctx, data->ssl.conn); } @@ -200,6 +242,8 @@ struct eap_tls_data *data = priv; os_free(data->key_data); data->key_data = NULL; + os_free(data->session_id); + data->session_id = NULL; if (eap_peer_tls_reauth_init(sm, &data->ssl)) { os_free(data); return NULL; @@ -261,6 +305,25 @@ } +static u8 * eap_tls_get_session_id(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_tls_data *data = priv; + u8 *id; + + if (data->session_id == NULL) + return NULL; + + id = os_malloc(data->id_len); + if (id == NULL) + return NULL; + + *len = data->id_len; + os_memcpy(id, data->session_id, data->id_len); + + return id; +} + + int eap_peer_tls_register(void) { struct eap_method *eap; @@ -276,6 +339,37 @@ eap->process = eap_tls_process; eap->isKeyAvailable = eap_tls_isKeyAvailable; eap->getKey = eap_tls_getKey; + eap->getSessionId = eap_tls_get_session_id; + eap->get_status = eap_tls_get_status; + eap->has_reauth_data = eap_tls_has_reauth_data; + eap->deinit_for_reauth = eap_tls_deinit_for_reauth; + eap->init_for_reauth = eap_tls_init_for_reauth; + eap->get_emsk = eap_tls_get_emsk; + + ret = eap_peer_method_register(eap); + if (ret) + eap_peer_method_free(eap); + return ret; +} + + +#ifdef EAP_UNAUTH_TLS +int eap_peer_unauth_tls_register(void) +{ + struct eap_method *eap; + int ret; + + eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, + EAP_VENDOR_UNAUTH_TLS, + EAP_VENDOR_TYPE_UNAUTH_TLS, "UNAUTH-TLS"); + if (eap == NULL) + return -1; + + eap->init = eap_unauth_tls_init; + eap->deinit = eap_tls_deinit; + eap->process = eap_tls_process; + eap->isKeyAvailable = eap_tls_isKeyAvailable; + eap->getKey = eap_tls_getKey; eap->get_status = eap_tls_get_status; eap->has_reauth_data = eap_tls_has_reauth_data; eap->deinit_for_reauth = eap_tls_deinit_for_reauth; @@ -287,3 +381,4 @@ eap_peer_method_free(eap); return ret; } +#endif /* EAP_UNAUTH_TLS */ diff -Nru wpa-1.0/src/eap_peer/eap_tls_common.c wpa-2.1/src/eap_peer/eap_tls_common.c --- wpa-1.0/src/eap_peer/eap_tls_common.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/eap_peer/eap_tls_common.c 2014-02-04 11:23:35.000000000 +0000 @@ -1,15 +1,9 @@ /* * EAP peer: EAP-TLS/PEAP/TTLS/FAST common functions - * Copyright (c) 2004-2009, Jouni Malinen + * Copyright (c) 2004-2013, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -22,6 +16,18 @@ #include "eap_config.h" +static struct wpabuf * eap_tls_msg_alloc(EapType type, size_t payload_len, + u8 code, u8 identifier) +{ + if (type == EAP_UNAUTH_TLS_TYPE) + return eap_msg_alloc(EAP_VENDOR_UNAUTH_TLS, + EAP_VENDOR_TYPE_UNAUTH_TLS, payload_len, + code, identifier); + return eap_msg_alloc(EAP_VENDOR_IETF, type, payload_len, code, + identifier); +} + + static int eap_tls_check_blob(struct eap_sm *sm, const char **name, const u8 **data, size_t *data_len) { @@ -54,6 +60,10 @@ params->flags |= TLS_CONN_ALLOW_SIGN_RSA_MD5; if (os_strstr(txt, "tls_disable_time_checks=1")) params->flags |= TLS_CONN_DISABLE_TIME_CHECKS; + if (os_strstr(txt, "tls_disable_session_ticket=1")) + params->flags |= TLS_CONN_DISABLE_SESSION_TICKET; + if (os_strstr(txt, "tls_disable_session_ticket=0")) + params->flags &= ~TLS_CONN_DISABLE_SESSION_TICKET; } @@ -68,6 +78,7 @@ params->dh_file = (char *) config->dh_file; params->subject_match = (char *) config->subject_match; params->altsubject_match = (char *) config->altsubject_match; + params->suffix_match = config->domain_suffix_match; params->engine = config->engine; params->engine_id = config->engine_id; params->pin = config->pin; @@ -89,6 +100,7 @@ params->dh_file = (char *) config->dh_file2; params->subject_match = (char *) config->subject_match2; params->altsubject_match = (char *) config->altsubject_match2; + params->suffix_match = config->domain_suffix_match2; params->engine = config->engine2; params->engine_id = config->engine2_id; params->pin = config->pin2; @@ -105,6 +117,18 @@ struct eap_peer_config *config, int phase2) { os_memset(params, 0, sizeof(*params)); + if (sm->workaround && data->eap_type != EAP_TYPE_FAST) { + /* + * Some deployed authentication servers seem to be unable to + * handle the TLS Session Ticket extension (they are supposed + * to ignore unrecognized TLS extensions, but end up rejecting + * the ClientHello instead). As a workaround, disable use of + * TLS Sesson Ticket extension for EAP-TLS, EAP-PEAP, and + * EAP-TTLS (EAP-FAST uses session ticket, so any server that + * supports EAP-FAST does not need this workaround). + */ + params->flags |= TLS_CONN_DISABLE_SESSION_TICKET; + } if (phase2) { wpa_printf(MSG_DEBUG, "TLS: using phase2 config options"); eap_tls_params_from_conf2(params, config); @@ -142,14 +166,18 @@ { int res; - data->conn = tls_connection_init(sm->ssl_ctx); + if (config->ocsp) + params->flags |= TLS_CONN_REQUEST_OCSP; + if (config->ocsp == 2) + params->flags |= TLS_CONN_REQUIRE_OCSP; + data->conn = tls_connection_init(data->ssl_ctx); if (data->conn == NULL) { wpa_printf(MSG_INFO, "SSL: Failed to initialize new TLS " "connection"); return -1; } - res = tls_connection_set_params(sm->ssl_ctx, data->conn, params); + res = tls_connection_set_params(data->ssl_ctx, data->conn, params); if (res == TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED) { /* * At this point with the pkcs11 engine the PIN might be wrong. @@ -168,13 +196,13 @@ config->pin = NULL; eap_sm_request_pin(sm); sm->ignore = TRUE; - tls_connection_deinit(sm->ssl_ctx, data->conn); + tls_connection_deinit(data->ssl_ctx, data->conn); data->conn = NULL; return -1; } else if (res) { wpa_printf(MSG_INFO, "TLS: Failed to set TLS connection " "parameters"); - tls_connection_deinit(sm->ssl_ctx, data->conn); + tls_connection_deinit(data->ssl_ctx, data->conn); data->conn = NULL; return -1; } @@ -188,13 +216,14 @@ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() * @data: Data for TLS processing * @config: Pointer to the network configuration + * @eap_type: EAP method used in Phase 1 (EAP_TYPE_TLS/PEAP/TTLS/FAST) * Returns: 0 on success, -1 on failure * * This function is used to initialize shared TLS functionality for EAP-TLS, * EAP-PEAP, EAP-TTLS, and EAP-FAST. */ int eap_peer_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data, - struct eap_peer_config *config) + struct eap_peer_config *config, u8 eap_type) { struct tls_connection_params params; @@ -202,7 +231,10 @@ return -1; data->eap = sm; + data->eap_type = eap_type; data->phase2 = sm->init_phase2; + data->ssl_ctx = sm->init_phase2 && sm->ssl_ctx2 ? sm->ssl_ctx2 : + sm->ssl_ctx; if (eap_tls_params_from_conf(sm, data, ¶ms, config, data->phase2) < 0) return -1; @@ -240,7 +272,7 @@ */ void eap_peer_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data) { - tls_connection_deinit(sm->ssl_ctx, data->conn); + tls_connection_deinit(data->ssl_ctx, data->conn); eap_peer_tls_reset_input(data); eap_peer_tls_reset_output(data); } @@ -263,7 +295,9 @@ u8 * eap_peer_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data, const char *label, size_t len) { +#ifndef CONFIG_FIPS struct tls_keys keys; +#endif /* CONFIG_FIPS */ u8 *rnd = NULL, *out; out = os_malloc(len); @@ -271,16 +305,17 @@ return NULL; /* First, try to use TLS library function for PRF, if available. */ - if (tls_connection_prf(sm->ssl_ctx, data->conn, label, 0, out, len) == - 0) + if (tls_connection_prf(data->ssl_ctx, data->conn, label, 0, out, len) + == 0) return out; +#ifndef CONFIG_FIPS /* * TLS library did not support key generation, so get the needed TLS * session parameters and use an internal implementation of TLS PRF to * derive the key. */ - if (tls_connection_get_keys(sm->ssl_ctx, data->conn, &keys)) + if (tls_connection_get_keys(data->ssl_ctx, data->conn, &keys)) goto fail; if (keys.client_random == NULL || keys.server_random == NULL || @@ -294,15 +329,16 @@ os_memcpy(rnd + keys.client_random_len, keys.server_random, keys.server_random_len); - if (tls_prf(keys.master_key, keys.master_key_len, - label, rnd, keys.client_random_len + - keys.server_random_len, out, len)) + if (tls_prf_sha1_md5(keys.master_key, keys.master_key_len, + label, rnd, keys.client_random_len + + keys.server_random_len, out, len)) goto fail; os_free(rnd); return out; fail: +#endif /* CONFIG_FIPS */ os_free(out); os_free(rnd); return NULL; @@ -310,6 +346,52 @@ /** + * eap_peer_tls_derive_session_id - Derive a Session-Id based on TLS data + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @data: Data for TLS processing + * @eap_type: EAP method used in Phase 1 (EAP_TYPE_TLS/PEAP/TTLS/FAST) + * @len: Pointer to length of the session ID generated + * Returns: Pointer to allocated Session-Id on success or %NULL on failure + * + * This function derive the Session-Id based on the TLS session data + * (client/server random and method type). + * + * The caller is responsible for freeing the returned buffer. + */ +u8 * eap_peer_tls_derive_session_id(struct eap_sm *sm, + struct eap_ssl_data *data, u8 eap_type, + size_t *len) +{ + struct tls_keys keys; + u8 *out; + + /* + * TLS library did not support session ID generation, + * so get the needed TLS session parameters + */ + if (tls_connection_get_keys(sm->ssl_ctx, data->conn, &keys)) + return NULL; + + if (keys.client_random == NULL || keys.server_random == NULL || + keys.master_key == NULL) + return NULL; + + *len = 1 + keys.client_random_len + keys.server_random_len; + out = os_malloc(*len); + if (out == NULL) + return NULL; + + /* Session-Id = EAP type || client.random || server.random */ + out[0] = eap_type; + os_memcpy(out + 1, keys.client_random, keys.client_random_len); + os_memcpy(out + 1 + keys.client_random_len, keys.server_random, + keys.server_random_len); + + return out; +} + + +/** * eap_peer_tls_reassemble_fragment - Reassemble a received fragment * @data: Data for TLS processing * @in_data: Next incoming TLS segment @@ -447,14 +529,14 @@ WPA_ASSERT(data->tls_out == NULL); } appl_data = NULL; - data->tls_out = tls_connection_handshake(sm->ssl_ctx, data->conn, + data->tls_out = tls_connection_handshake(data->ssl_ctx, data->conn, msg, &appl_data); eap_peer_tls_reset_input(data); if (appl_data && - tls_connection_established(sm->ssl_ctx, data->conn) && - !tls_connection_get_failed(sm->ssl_ctx, data->conn)) { + tls_connection_established(data->ssl_ctx, data->conn) && + !tls_connection_get_failed(data->ssl_ctx, data->conn)) { wpa_hexdump_buf_key(MSG_MSGDUMP, "SSL: Application data", appl_data); *out_data = appl_data; @@ -520,9 +602,8 @@ length_included = 1; } - *out_data = eap_msg_alloc(EAP_VENDOR_IETF, eap_type, - 1 + length_included * 4 + len, - EAP_CODE_RESPONSE, id); + *out_data = eap_tls_msg_alloc(eap_type, 1 + length_included * 4 + len, + EAP_CODE_RESPONSE, id); if (*out_data == NULL) return -1; @@ -622,7 +703,7 @@ return -1; } - if (tls_connection_get_failed(sm->ssl_ctx, data->conn)) { + if (tls_connection_get_failed(data->ssl_ctx, data->conn)) { /* TLS processing has failed - return error */ wpa_printf(MSG_DEBUG, "SSL: Failed - tls_out available to " "report error"); @@ -660,8 +741,7 @@ { struct wpabuf *resp; - resp = eap_msg_alloc(EAP_VENDOR_IETF, eap_type, 1, EAP_CODE_RESPONSE, - id); + resp = eap_tls_msg_alloc(eap_type, 1, EAP_CODE_RESPONSE, id); if (resp == NULL) return NULL; wpa_printf(MSG_DEBUG, "SSL: Building ACK (type=%d id=%d ver=%d)", @@ -681,7 +761,7 @@ { eap_peer_tls_reset_input(data); eap_peer_tls_reset_output(data); - return tls_connection_shutdown(sm->ssl_ctx, data->conn); + return tls_connection_shutdown(data->ssl_ctx, data->conn); } @@ -700,7 +780,8 @@ char name[128]; int len = 0, ret; - if (tls_get_cipher(sm->ssl_ctx, data->conn, name, sizeof(name)) == 0) { + if (tls_get_cipher(data->ssl_ctx, data->conn, name, sizeof(name)) == 0) + { ret = os_snprintf(buf + len, buflen - len, "EAP TLS cipher=%s\n", name); if (ret < 0 || (size_t) ret >= buflen - len) @@ -747,13 +828,19 @@ size_t left; unsigned int tls_msg_len; - if (tls_get_errors(sm->ssl_ctx)) { + if (tls_get_errors(data->ssl_ctx)) { wpa_printf(MSG_INFO, "SSL: TLS errors detected"); ret->ignore = TRUE; return NULL; } - pos = eap_hdr_validate(EAP_VENDOR_IETF, eap_type, reqData, &left); + if (eap_type == EAP_UNAUTH_TLS_TYPE) + pos = eap_hdr_validate(EAP_VENDOR_UNAUTH_TLS, + EAP_VENDOR_TYPE_UNAUTH_TLS, reqData, + &left); + else + pos = eap_hdr_validate(EAP_VENDOR_IETF, eap_type, reqData, + &left); if (pos == NULL) { ret->ignore = TRUE; return NULL; @@ -794,6 +881,14 @@ } pos += 4; left -= 4; + + if (left > tls_msg_len) { + wpa_printf(MSG_INFO, "SSL: TLS Message Length (%d " + "bytes) smaller than this fragment (%d " + "bytes)", (int) tls_msg_len, (int) left); + ret->ignore = TRUE; + return NULL; + } } ret->ignore = FALSE; @@ -855,7 +950,7 @@ if (msg == NULL) return need_more_input ? 1 : -1; - *in_decrypted = tls_connection_decrypt(sm->ssl_ctx, data->conn, msg); + *in_decrypted = tls_connection_decrypt(data->ssl_ctx, data->conn, msg); eap_peer_tls_reset_input(data); if (*in_decrypted == NULL) { wpa_printf(MSG_INFO, "SSL: Failed to decrypt Phase 2 data"); @@ -883,8 +978,8 @@ { if (in_data) { eap_peer_tls_reset_output(data); - data->tls_out = tls_connection_encrypt(sm->ssl_ctx, data->conn, - in_data); + data->tls_out = tls_connection_encrypt(data->ssl_ctx, + data->conn, in_data); if (data->tls_out == NULL) { wpa_printf(MSG_INFO, "SSL: Failed to encrypt Phase 2 " "data (in_len=%lu)", @@ -949,8 +1044,8 @@ "method '%s'", start); } else { num_methods++; - _methods = os_realloc(methods, - num_methods * sizeof(*methods)); + _methods = os_realloc_array(methods, num_methods, + sizeof(*methods)); if (_methods == NULL) { os_free(methods); os_free(buf); diff -Nru wpa-1.0/src/eap_peer/eap_tls_common.h wpa-2.1/src/eap_peer/eap_tls_common.h --- wpa-1.0/src/eap_peer/eap_tls_common.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/eap_peer/eap_tls_common.h 2014-02-04 11:23:35.000000000 +0000 @@ -1,15 +1,9 @@ /* * EAP peer: EAP-TLS/PEAP/TTLS/FAST common functions - * Copyright (c) 2004-2009, Jouni Malinen + * Copyright (c) 2004-2009, 2012, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef EAP_TLS_COMMON_H @@ -69,6 +63,16 @@ * eap - EAP state machine allocated with eap_peer_sm_init() */ struct eap_sm *eap; + + /** + * ssl_ctx - TLS library context to use for the connection + */ + void *ssl_ctx; + + /** + * eap_type - EAP method used in Phase 1 (EAP_TYPE_TLS/PEAP/TTLS/FAST) + */ + u8 eap_type; }; @@ -81,12 +85,18 @@ /* could be up to 128 bytes, but only the first 64 bytes are used */ #define EAP_TLS_KEY_LEN 64 +/* dummy type used as a flag for UNAUTH-TLS */ +#define EAP_UNAUTH_TLS_TYPE 255 + int eap_peer_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data, - struct eap_peer_config *config); + struct eap_peer_config *config, u8 eap_type); void eap_peer_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data); u8 * eap_peer_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data, const char *label, size_t len); +u8 * eap_peer_tls_derive_session_id(struct eap_sm *sm, + struct eap_ssl_data *data, u8 eap_type, + size_t *len); int eap_peer_tls_process_helper(struct eap_sm *sm, struct eap_ssl_data *data, EapType eap_type, int peap_version, u8 id, const u8 *in_data, size_t in_len, diff -Nru wpa-1.0/src/eap_peer/eap_tnc.c wpa-2.1/src/eap_peer/eap_tnc.c --- wpa-1.0/src/eap_peer/eap_tnc.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/eap_peer/eap_tnc.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,20 +2,13 @@ * EAP peer method: EAP-TNC (Trusted Network Connect) * Copyright (c) 2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" #include "common.h" -#include "base64.h" #include "eap_i.h" #include "tncc.h" diff -Nru wpa-1.0/src/eap_peer/eap_ttls.c wpa-2.1/src/eap_peer/eap_ttls.c --- wpa-1.0/src/eap_peer/eap_ttls.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/eap_peer/eap_ttls.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * EAP peer method: EAP-TTLS (RFC 5281) * Copyright (c) 2004-2011, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -60,6 +54,8 @@ int resuming; /* starting a resumed session */ int reauth; /* reauthentication */ u8 *key_data; + u8 *session_id; + size_t id_len; struct wpabuf *pending_phase2_req; @@ -116,7 +112,7 @@ data->phase2_eap_type.method = EAP_TYPE_NONE; } - if (eap_peer_tls_ssl_init(sm, &data->ssl, config)) { + if (eap_peer_tls_ssl_init(sm, &data->ssl, config, EAP_TYPE_TTLS)) { wpa_printf(MSG_INFO, "EAP-TTLS: Failed to initialize SSL."); eap_ttls_deinit(sm, data); return NULL; @@ -146,6 +142,7 @@ os_free(data->phase2_eap_types); eap_peer_tls_ssl_deinit(sm, &data->ssl); os_free(data->key_data); + os_free(data->session_id); wpabuf_free(data->pending_phase2_req); os_free(data); } @@ -228,6 +225,17 @@ wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: Derived key", data->key_data, EAP_TLS_KEY_LEN); + os_free(data->session_id); + data->session_id = eap_peer_tls_derive_session_id(sm, &data->ssl, + EAP_TYPE_TTLS, + &data->id_len); + if (data->session_id) { + wpa_hexdump(MSG_DEBUG, "EAP-TTLS: Derived Session-Id", + data->session_id, data->id_len); + } else { + wpa_printf(MSG_ERROR, "EAP-TTLS: Failed to derive Session-Id"); + } + return 0; } @@ -407,6 +415,7 @@ struct eap_method_ret *ret, struct wpabuf **resp) { +#ifdef EAP_MSCHAPv2 struct wpabuf *msg; u8 *buf, *pos, *challenge, *peer_challenge; const u8 *identity, *password; @@ -494,6 +503,10 @@ } return 0; +#else /* EAP_MSCHAPv2 */ + wpa_printf(MSG_ERROR, "EAP-TTLS: MSCHAPv2 not included in the build"); + return -1; +#endif /* EAP_MSCHAPv2 */ } @@ -1048,6 +1061,7 @@ struct eap_method_ret *ret, struct ttls_parse_avp *parse) { +#ifdef EAP_MSCHAPv2 if (parse->mschapv2_error) { wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Received " "MS-CHAP-Error - failed"); @@ -1096,6 +1110,10 @@ * with EAP-Success after this. */ return 1; +#else /* EAP_MSCHAPv2 */ + wpa_printf(MSG_ERROR, "EAP-TTLS: MSCHAPv2 not included in the build"); + return -1; +#endif /* EAP_MSCHAPv2 */ } @@ -1524,6 +1542,8 @@ struct eap_ttls_data *data = priv; os_free(data->key_data); data->key_data = NULL; + os_free(data->session_id); + data->session_id = NULL; if (eap_peer_tls_reauth_init(sm, &data->ssl)) { os_free(data); return NULL; @@ -1608,6 +1628,25 @@ } +static u8 * eap_ttls_get_session_id(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_ttls_data *data = priv; + u8 *id; + + if (data->session_id == NULL || !data->phase2_success) + return NULL; + + id = os_malloc(data->id_len); + if (id == NULL) + return NULL; + + *len = data->id_len; + os_memcpy(id, data->session_id, data->id_len); + + return id; +} + + int eap_peer_ttls_register(void) { struct eap_method *eap; @@ -1623,6 +1662,7 @@ eap->process = eap_ttls_process; eap->isKeyAvailable = eap_ttls_isKeyAvailable; eap->getKey = eap_ttls_getKey; + eap->getSessionId = eap_ttls_get_session_id; eap->get_status = eap_ttls_get_status; eap->has_reauth_data = eap_ttls_has_reauth_data; eap->deinit_for_reauth = eap_ttls_deinit_for_reauth; diff -Nru wpa-1.0/src/eap_peer/eap_vendor_test.c wpa-2.1/src/eap_peer/eap_vendor_test.c --- wpa-1.0/src/eap_peer/eap_vendor_test.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/eap_peer/eap_vendor_test.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * EAP peer method: Test method for vendor specific (expanded) EAP type * Copyright (c) 2005-2006, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. * * This file implements a vendor specific test method using EAP expanded types. * This is only for test use and must not be used for authentication since no @@ -25,7 +19,7 @@ #endif /* TEST_PENDING_REQUEST */ -#define EAP_VENDOR_ID 0xfffefd +#define EAP_VENDOR_ID EAP_VENDOR_HOSTAP #define EAP_VENDOR_TYPE 0xfcfbfaf9 diff -Nru wpa-1.0/src/eap_peer/eap_wsc.c wpa-2.1/src/eap_peer/eap_wsc.c --- wpa-1.0/src/eap_peer/eap_wsc.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/eap_peer/eap_wsc.c 2014-02-04 11:23:35.000000000 +0000 @@ -1,15 +1,9 @@ /* * EAP-WSC peer for Wi-Fi Protected Setup - * Copyright (c) 2007-2009, Jouni Malinen + * Copyright (c) 2007-2009, 2012, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -83,25 +77,33 @@ else len = end - pos; if ((len & 1) || len > 2 * sizeof(cred->ssid) || - hexstr2bin(pos, cred->ssid, len / 2)) + hexstr2bin(pos, cred->ssid, len / 2)) { + wpa_printf(MSG_DEBUG, "EAP-WSC: Invalid new_ssid"); return -1; + } cred->ssid_len = len / 2; pos = os_strstr(params, "new_auth="); - if (pos == NULL) + if (pos == NULL) { + wpa_printf(MSG_DEBUG, "EAP-WSC: Missing new_auth"); return -1; + } if (os_strncmp(pos + 9, "OPEN", 4) == 0) cred->auth_type = WPS_AUTH_OPEN; else if (os_strncmp(pos + 9, "WPAPSK", 6) == 0) cred->auth_type = WPS_AUTH_WPAPSK; else if (os_strncmp(pos + 9, "WPA2PSK", 7) == 0) cred->auth_type = WPS_AUTH_WPA2PSK; - else + else { + wpa_printf(MSG_DEBUG, "EAP-WSC: Unknown new_auth"); return -1; + } pos = os_strstr(params, "new_encr="); - if (pos == NULL) + if (pos == NULL) { + wpa_printf(MSG_DEBUG, "EAP-WSC: Missing new_encr"); return -1; + } if (os_strncmp(pos + 9, "NONE", 4) == 0) cred->encr_type = WPS_ENCR_NONE; else if (os_strncmp(pos + 9, "WEP", 3) == 0) @@ -110,8 +112,10 @@ cred->encr_type = WPS_ENCR_TKIP; else if (os_strncmp(pos + 9, "CCMP", 4) == 0) cred->encr_type = WPS_ENCR_AES; - else + else { + wpa_printf(MSG_DEBUG, "EAP-WSC: Unknown new_encr"); return -1; + } pos = os_strstr(params, "new_key="); if (pos == NULL) @@ -123,8 +127,10 @@ else len = end - pos; if ((len & 1) || len > 2 * sizeof(cred->key) || - hexstr2bin(pos, cred->key, len / 2)) + hexstr2bin(pos, cred->key, len / 2)) { + wpa_printf(MSG_DEBUG, "EAP-WSC: Invalid new_key"); return -1; + } cred->key_len = len / 2; return 1; @@ -138,11 +144,13 @@ size_t identity_len; int registrar; struct wps_config cfg; - const char *pos; + const char *pos, *end; const char *phase1; struct wps_context *wps; struct wps_credential new_ap_settings; int res; + int nfc = 0; + u8 pkhash[WPS_OOB_PUBKEY_HASH_LEN]; wps = sm->wps; if (wps == NULL) { @@ -190,26 +198,57 @@ while (*pos != '\0' && *pos != ' ') pos++; cfg.pin_len = pos - (const char *) cfg.pin; + if (cfg.pin_len == 6 && + os_strncmp((const char *) cfg.pin, "nfc-pw", 6) == 0) { + cfg.pin = NULL; + cfg.pin_len = 0; + nfc = 1; + } } else { pos = os_strstr(phase1, "pbc=1"); if (pos) cfg.pbc = 1; } - if (cfg.pin == NULL && !cfg.pbc) { + pos = os_strstr(phase1, "dev_pw_id="); + if (pos) { + u16 id = atoi(pos + 10); + if (id == DEV_PW_NFC_CONNECTION_HANDOVER) + nfc = 1; + if (cfg.pin || id == DEV_PW_NFC_CONNECTION_HANDOVER) + cfg.dev_pw_id = id; + } + + if (cfg.pin == NULL && !cfg.pbc && !nfc) { wpa_printf(MSG_INFO, "EAP-WSC: PIN or PBC not set in phase1 " "configuration data"); os_free(data); return NULL; } - pos = os_strstr(phase1, "dev_pw_id="); - if (pos && cfg.pin) - cfg.dev_pw_id = atoi(pos + 10); + pos = os_strstr(phase1, " pkhash="); + if (pos) { + size_t len; + pos += 8; + end = os_strchr(pos, ' '); + if (end) + len = end - pos; + else + len = os_strlen(pos); + if (len != 2 * WPS_OOB_PUBKEY_HASH_LEN || + hexstr2bin(pos, pkhash, WPS_OOB_PUBKEY_HASH_LEN)) { + wpa_printf(MSG_INFO, "EAP-WSC: Invalid pkhash"); + os_free(data); + return NULL; + } + cfg.peer_pubkey_hash = pkhash; + } res = eap_wsc_new_ap_settings(&new_ap_settings, phase1); if (res < 0) { os_free(data); + wpa_printf(MSG_DEBUG, "EAP-WSC: Failed to parse new AP " + "settings"); return NULL; } if (res == 1) { @@ -221,6 +260,7 @@ data->wps = wps_init(&cfg); if (data->wps == NULL) { os_free(data); + wpa_printf(MSG_DEBUG, "EAP-WSC: wps_init failed"); return NULL; } res = eap_get_config_fragment_size(sm); diff -Nru wpa-1.0/src/eap_peer/ikev2.c wpa-2.1/src/eap_peer/ikev2.c --- wpa-1.0/src/eap_peer/ikev2.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/eap_peer/ikev2.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * IKEv2 responder (RFC 4306) for EAP-IKEV2 * Copyright (c) 2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -1263,6 +1257,7 @@ wpabuf_free(msg); return NULL; } + wpabuf_free(plain); data->state = IKEV2_FAILED; } else { /* HDR, N */ diff -Nru wpa-1.0/src/eap_peer/ikev2.h wpa-2.1/src/eap_peer/ikev2.h --- wpa-1.0/src/eap_peer/ikev2.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/eap_peer/ikev2.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * IKEv2 responder (RFC 4306) for EAP-IKEV2 * Copyright (c) 2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef IKEV2_H diff -Nru wpa-1.0/src/eap_peer/Makefile wpa-2.1/src/eap_peer/Makefile --- wpa-1.0/src/eap_peer/Makefile 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/eap_peer/Makefile 2014-02-04 11:23:35.000000000 +0000 @@ -2,7 +2,7 @@ @echo Nothing to be made. clean: - rm -f *~ *.o *.so *.d + rm -f *~ *.o *.so *.d *.gcno *.gcda *.gcov install: if ls *.so >/dev/null 2>&1; then \ diff -Nru wpa-1.0/src/eap_peer/mschapv2.c wpa-2.1/src/eap_peer/mschapv2.c --- wpa-1.0/src/eap_peer/mschapv2.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/eap_peer/mschapv2.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * MSCHAPV2 (RFC 2759) * Copyright (c) 2004-2008, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -69,22 +63,28 @@ if (pwhash) { wpa_hexdump_key(MSG_DEBUG, "MSCHAPV2: password hash", password, password_len); - generate_nt_response_pwhash(auth_challenge, peer_challenge, - username, username_len, - password, nt_response); - generate_authenticator_response_pwhash( - password, peer_challenge, auth_challenge, - username, username_len, nt_response, auth_response); + if (generate_nt_response_pwhash(auth_challenge, peer_challenge, + username, username_len, + password, nt_response) || + generate_authenticator_response_pwhash( + password, peer_challenge, auth_challenge, + username, username_len, nt_response, + auth_response)) + return -1; } else { wpa_hexdump_ascii_key(MSG_DEBUG, "MSCHAPV2: password", password, password_len); - generate_nt_response(auth_challenge, peer_challenge, - username, username_len, - password, password_len, nt_response); - generate_authenticator_response(password, password_len, - peer_challenge, auth_challenge, - username, username_len, - nt_response, auth_response); + if (generate_nt_response(auth_challenge, peer_challenge, + username, username_len, + password, password_len, + nt_response) || + generate_authenticator_response(password, password_len, + peer_challenge, + auth_challenge, + username, username_len, + nt_response, + auth_response)) + return -1; } wpa_hexdump(MSG_DEBUG, "MSCHAPV2: NT Response", nt_response, MSCHAPV2_NT_RESPONSE_LEN); @@ -100,7 +100,8 @@ hash_nt_password_hash(password_hash, password_hash_hash)) return -1; } - get_master_key(password_hash_hash, nt_response, master_key); + if (get_master_key(password_hash_hash, nt_response, master_key)) + return -1; wpa_hexdump_key(MSG_DEBUG, "MSCHAPV2: Master Key", master_key, MSCHAPV2_MASTER_KEY_LEN); diff -Nru wpa-1.0/src/eap_peer/mschapv2.h wpa-2.1/src/eap_peer/mschapv2.h --- wpa-1.0/src/eap_peer/mschapv2.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/eap_peer/mschapv2.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * MSCHAPV2 (RFC 2759) * Copyright (c) 2004-2008, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef MSCHAPV2_H diff -Nru wpa-1.0/src/eap_peer/tncc.c wpa-2.1/src/eap_peer/tncc.c --- wpa-1.0/src/eap_peer/tncc.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/eap_peer/tncc.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * EAP-TNC - TNCC (IF-IMC and IF-TNCCS) * Copyright (c) 2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -747,12 +741,10 @@ enum tncc_process_res res = TNCCS_PROCESS_OK_NO_RECOMMENDATION; int recommendation_msg = 0; - buf = os_malloc(len + 1); + buf = dup_binstr(msg, len); if (buf == NULL) return TNCCS_PROCESS_ERROR; - os_memcpy(buf, msg, len); - buf[len] = '\0'; start = os_strstr(buf, ""); if (start == NULL || end == NULL || start > end) { diff -Nru wpa-1.0/src/eap_peer/tncc.h wpa-2.1/src/eap_peer/tncc.h --- wpa-1.0/src/eap_peer/tncc.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/eap_peer/tncc.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * EAP-TNC - TNCC (IF-IMC and IF-TNCCS) * Copyright (c) 2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef TNCC_H diff -Nru wpa-1.0/src/eap_server/eap.h wpa-2.1/src/eap_server/eap.h --- wpa-1.0/src/eap_server/eap.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/eap_server/eap.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * hostapd / EAP Full Authenticator state machine (RFC 4137) * Copyright (c) 2004-2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef EAP_H @@ -22,8 +16,6 @@ struct eap_sm; -#define EAP_MAX_METHODS 8 - #define EAP_TTLS_AUTH_PAP 1 #define EAP_TTLS_AUTH_CHAP 2 #define EAP_TTLS_AUTH_MSCHAP 4 @@ -112,6 +104,9 @@ int fragment_size; int pbc_in_m1; + + const u8 *server_id; + size_t server_id_len; }; diff -Nru wpa-1.0/src/eap_server/eap_i.h wpa-2.1/src/eap_server/eap_i.h --- wpa-1.0/src/eap_server/eap_i.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/eap_server/eap_i.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * hostapd / EAP Authenticator state machine internal structures (RFC 4137) * Copyright (c) 2004-2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef EAP_I_H @@ -157,7 +151,7 @@ int user_eap_method_index; int init_phase2; void *ssl_ctx; - void *eap_sim_db_priv; + struct eap_sim_db_data *eap_sim_db_priv; Boolean backend_auth; Boolean update_user; int eap_server; @@ -194,6 +188,9 @@ int fragment_size; int pbc_in_m1; + + const u8 *server_id; + size_t server_id_len; }; int eap_user_get(struct eap_sm *sm, const u8 *identity, size_t identity_len, diff -Nru wpa-1.0/src/eap_server/eap_methods.h wpa-2.1/src/eap_server/eap_methods.h --- wpa-1.0/src/eap_server/eap_methods.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/eap_server/eap_methods.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * EAP server method registration * Copyright (c) 2004-2009, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef EAP_SERVER_METHODS_H @@ -32,6 +26,7 @@ int eap_server_identity_register(void); int eap_server_md5_register(void); int eap_server_tls_register(void); +int eap_server_unauth_tls_register(void); int eap_server_mschapv2_register(void); int eap_server_peap_register(void); int eap_server_tlv_register(void); @@ -50,5 +45,6 @@ int eap_server_ikev2_register(void); int eap_server_tnc_register(void); int eap_server_pwd_register(void); +int eap_server_eke_register(void); #endif /* EAP_SERVER_METHODS_H */ diff -Nru wpa-1.0/src/eap_server/eap_server_aka.c wpa-2.1/src/eap_server/eap_server_aka.c --- wpa-1.0/src/eap_server/eap_server_aka.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/eap_server/eap_server_aka.c 2014-02-04 11:23:35.000000000 +0000 @@ -1,15 +1,9 @@ /* - * hostapd / EAP-AKA (RFC 4187) and EAP-AKA' (draft-arkko-eap-aka-kdf) - * Copyright (c) 2005-2008, Jouni Malinen + * hostapd / EAP-AKA (RFC 4187) and EAP-AKA' (RFC 5448) + * Copyright (c) 2005-2012, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -55,12 +49,12 @@ u8 *network_name; size_t network_name_len; u16 kdf; + int identity_round; + char permanent[20]; /* Permanent username */ }; -static void eap_aka_determine_identity(struct eap_sm *sm, - struct eap_aka_data *data, - int before_identity, int after_reauth); +static void eap_aka_fullauth(struct eap_sm *sm, struct eap_aka_data *data); static const char * eap_aka_state_txt(int state) @@ -93,6 +87,96 @@ } +static int eap_aka_check_identity_reauth(struct eap_sm *sm, + struct eap_aka_data *data, + const char *username) +{ + if (data->eap_method == EAP_TYPE_AKA_PRIME && + username[0] != EAP_AKA_PRIME_REAUTH_ID_PREFIX) + return 0; + if (data->eap_method == EAP_TYPE_AKA && + username[0] != EAP_AKA_REAUTH_ID_PREFIX) + return 0; + + wpa_printf(MSG_DEBUG, "EAP-AKA: Reauth username '%s'", username); + data->reauth = eap_sim_db_get_reauth_entry(sm->eap_sim_db_priv, + username); + if (data->reauth == NULL) { + wpa_printf(MSG_DEBUG, "EAP-AKA: Unknown reauth identity - " + "request full auth identity"); + /* Remain in IDENTITY state for another round */ + return 0; + } + + wpa_printf(MSG_DEBUG, "EAP-AKA: Using fast re-authentication"); + os_strlcpy(data->permanent, data->reauth->permanent, + sizeof(data->permanent)); + data->counter = data->reauth->counter; + if (data->eap_method == EAP_TYPE_AKA_PRIME) { + os_memcpy(data->k_encr, data->reauth->k_encr, + EAP_SIM_K_ENCR_LEN); + os_memcpy(data->k_aut, data->reauth->k_aut, + EAP_AKA_PRIME_K_AUT_LEN); + os_memcpy(data->k_re, data->reauth->k_re, + EAP_AKA_PRIME_K_RE_LEN); + } else { + os_memcpy(data->mk, data->reauth->mk, EAP_SIM_MK_LEN); + } + + eap_aka_state(data, REAUTH); + return 1; +} + + +static void eap_aka_check_identity(struct eap_sm *sm, + struct eap_aka_data *data) +{ + char *username; + + /* Check if we already know the identity from EAP-Response/Identity */ + + username = sim_get_username(sm->identity, sm->identity_len); + if (username == NULL) + return; + + if (eap_aka_check_identity_reauth(sm, data, username) > 0) { + os_free(username); + /* + * Since re-auth username was recognized, skip AKA/Identity + * exchange. + */ + return; + } + + if ((data->eap_method == EAP_TYPE_AKA_PRIME && + username[0] == EAP_AKA_PRIME_PSEUDONYM_PREFIX) || + (data->eap_method == EAP_TYPE_AKA && + username[0] == EAP_AKA_PSEUDONYM_PREFIX)) { + const char *permanent; + wpa_printf(MSG_DEBUG, "EAP-AKA: Pseudonym username '%s'", + username); + permanent = eap_sim_db_get_permanent( + sm->eap_sim_db_priv, username); + if (permanent == NULL) { + os_free(username); + wpa_printf(MSG_DEBUG, "EAP-AKA: Unknown pseudonym " + "identity - request permanent identity"); + /* Remain in IDENTITY state for another round */ + return; + } + os_strlcpy(data->permanent, permanent, + sizeof(data->permanent)); + /* + * Since pseudonym username was recognized, skip AKA/Identity + * exchange. + */ + eap_aka_fullauth(sm, data); + } + + os_free(username); +} + + static void * eap_aka_init(struct eap_sm *sm) { struct eap_aka_data *data; @@ -109,8 +193,8 @@ data->eap_method = EAP_TYPE_AKA; data->state = IDENTITY; - eap_aka_determine_identity(sm, data, 1, 0); data->pending_id = -1; + eap_aka_check_identity(sm, data); return data; } @@ -142,8 +226,8 @@ data->network_name_len = os_strlen(network_name); data->state = IDENTITY; - eap_aka_determine_identity(sm, data, 1, 0); data->pending_id = -1; + eap_aka_check_identity(sm, data); return data; } @@ -270,11 +354,8 @@ wpa_printf(MSG_DEBUG, "EAP-AKA: Generating Identity"); msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, data->eap_method, EAP_AKA_SUBTYPE_IDENTITY); - if (eap_sim_db_identity_known(sm->eap_sim_db_priv, sm->identity, - sm->identity_len)) { - wpa_printf(MSG_DEBUG, " AT_PERMANENT_ID_REQ"); - eap_sim_msg_add(msg, EAP_SIM_AT_PERMANENT_ID_REQ, 0, NULL, 0); - } else { + data->identity_round++; + if (data->identity_round == 1) { /* * RFC 4187, Chap. 4.1.4 recommends that identity from EAP is * ignored and the AKA/Identity is used to request the @@ -282,6 +363,19 @@ */ wpa_printf(MSG_DEBUG, " AT_ANY_ID_REQ"); eap_sim_msg_add(msg, EAP_SIM_AT_ANY_ID_REQ, 0, NULL, 0); + } else if (data->identity_round > 3) { + /* Cannot use more than three rounds of Identity messages */ + eap_sim_msg_free(msg); + return NULL; + } else if (sm->identity && sm->identity_len > 0 && + (sm->identity[0] == EAP_AKA_REAUTH_ID_PREFIX || + sm->identity[0] == EAP_AKA_PRIME_REAUTH_ID_PREFIX)) { + /* Reauth id may have expired - try fullauth */ + wpa_printf(MSG_DEBUG, " AT_FULLAUTH_ID_REQ"); + eap_sim_msg_add(msg, EAP_SIM_AT_FULLAUTH_ID_REQ, 0, NULL, 0); + } else { + wpa_printf(MSG_DEBUG, " AT_PERMANENT_ID_REQ"); + eap_sim_msg_add(msg, EAP_SIM_AT_PERMANENT_ID_REQ, 0, NULL, 0); } buf = eap_sim_msg_finish(msg, NULL, NULL, 0); if (eap_aka_add_id_msg(data, buf) < 0) { @@ -300,7 +394,10 @@ os_free(data->next_pseudonym); if (nonce_s == NULL) { data->next_pseudonym = - eap_sim_db_get_next_pseudonym(sm->eap_sim_db_priv, 1); + eap_sim_db_get_next_pseudonym( + sm->eap_sim_db_priv, + data->eap_method == EAP_TYPE_AKA_PRIME ? + EAP_SIM_DB_AKA_PRIME : EAP_SIM_DB_AKA); } else { /* Do not update pseudonym during re-authentication */ data->next_pseudonym = NULL; @@ -308,7 +405,10 @@ os_free(data->next_reauth_id); if (data->counter <= EAP_AKA_MAX_FAST_REAUTHS) { data->next_reauth_id = - eap_sim_db_get_next_reauth_id(sm->eap_sim_db_priv, 1); + eap_sim_db_get_next_reauth_id( + sm->eap_sim_db_priv, + data->eap_method == EAP_TYPE_AKA_PRIME ? + EAP_SIM_DB_AKA_PRIME : EAP_SIM_DB_AKA); } else { wpa_printf(MSG_DEBUG, "EAP-AKA: Max fast re-authentication " "count exceeded - force full authentication"); @@ -612,92 +712,83 @@ static void eap_aka_determine_identity(struct eap_sm *sm, - struct eap_aka_data *data, - int before_identity, int after_reauth) + struct eap_aka_data *data) { - const u8 *identity; - size_t identity_len; - int res; + char *username; - identity = NULL; - identity_len = 0; + wpa_hexdump_ascii(MSG_DEBUG, "EAP-AKA: Identity", + sm->identity, sm->identity_len); - if (after_reauth && data->reauth) { - identity = data->reauth->identity; - identity_len = data->reauth->identity_len; - } else if (sm->identity && sm->identity_len > 0 && - sm->identity[0] == EAP_AKA_PERMANENT_PREFIX) { - identity = sm->identity; - identity_len = sm->identity_len; - } else { - identity = eap_sim_db_get_permanent(sm->eap_sim_db_priv, - sm->identity, - sm->identity_len, - &identity_len); - if (identity == NULL) { - data->reauth = eap_sim_db_get_reauth_entry( - sm->eap_sim_db_priv, sm->identity, - sm->identity_len); - if (data->reauth && - data->reauth->aka_prime != - (data->eap_method == EAP_TYPE_AKA_PRIME)) { - wpa_printf(MSG_DEBUG, "EAP-AKA: Reauth data " - "was for different AKA version"); - data->reauth = NULL; - } - if (data->reauth) { - wpa_printf(MSG_DEBUG, "EAP-AKA: Using fast " - "re-authentication"); - identity = data->reauth->identity; - identity_len = data->reauth->identity_len; - data->counter = data->reauth->counter; - if (data->eap_method == EAP_TYPE_AKA_PRIME) { - os_memcpy(data->k_encr, - data->reauth->k_encr, - EAP_SIM_K_ENCR_LEN); - os_memcpy(data->k_aut, - data->reauth->k_aut, - EAP_AKA_PRIME_K_AUT_LEN); - os_memcpy(data->k_re, - data->reauth->k_re, - EAP_AKA_PRIME_K_RE_LEN); - } else { - os_memcpy(data->mk, data->reauth->mk, - EAP_SIM_MK_LEN); - } - } - } + username = sim_get_username(sm->identity, sm->identity_len); + if (username == NULL) { + data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; + eap_aka_state(data, NOTIFICATION); + return; } - if (identity == NULL || - eap_sim_db_identity_known(sm->eap_sim_db_priv, sm->identity, - sm->identity_len) < 0) { - if (before_identity) { - wpa_printf(MSG_DEBUG, "EAP-AKA: Permanent user name " - "not known - send AKA-Identity request"); - eap_aka_state(data, IDENTITY); - return; - } else { - wpa_printf(MSG_DEBUG, "EAP-AKA: Unknown whether the " - "permanent user name is known; try to use " - "it"); - /* eap_sim_db_get_aka_auth() will report failure, if - * this identity is not known. */ - } + if (eap_aka_check_identity_reauth(sm, data, username) > 0) { + os_free(username); + return; } - wpa_hexdump_ascii(MSG_DEBUG, "EAP-AKA: Identity", - identity, identity_len); + if (((data->eap_method == EAP_TYPE_AKA_PRIME && + username[0] == EAP_AKA_PRIME_REAUTH_ID_PREFIX) || + (data->eap_method == EAP_TYPE_AKA && + username[0] == EAP_AKA_REAUTH_ID_PREFIX)) && + data->identity_round == 1) { + /* Remain in IDENTITY state for another round to request full + * auth identity since we did not recognize reauth id */ + os_free(username); + return; + } - if (!after_reauth && data->reauth) { - eap_aka_state(data, REAUTH); + if ((data->eap_method == EAP_TYPE_AKA_PRIME && + username[0] == EAP_AKA_PRIME_PSEUDONYM_PREFIX) || + (data->eap_method == EAP_TYPE_AKA && + username[0] == EAP_AKA_PSEUDONYM_PREFIX)) { + const char *permanent; + wpa_printf(MSG_DEBUG, "EAP-AKA: Pseudonym username '%s'", + username); + permanent = eap_sim_db_get_permanent( + sm->eap_sim_db_priv, username); + os_free(username); + if (permanent == NULL) { + wpa_printf(MSG_DEBUG, "EAP-AKA: Unknown pseudonym " + "identity - request permanent identity"); + /* Remain in IDENTITY state for another round */ + return; + } + os_strlcpy(data->permanent, permanent, + sizeof(data->permanent)); + } else if ((data->eap_method == EAP_TYPE_AKA_PRIME && + username[0] == EAP_AKA_PRIME_PERMANENT_PREFIX) || + (data->eap_method == EAP_TYPE_AKA && + username[0] == EAP_AKA_PERMANENT_PREFIX)) { + wpa_printf(MSG_DEBUG, "EAP-AKA: Permanent username '%s'", + username); + os_strlcpy(data->permanent, username, sizeof(data->permanent)); + os_free(username); + } else { + wpa_printf(MSG_DEBUG, "EAP-AKA: Unrecognized username '%s'", + username); + os_free(username); + data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; + eap_aka_state(data, NOTIFICATION); return; } - res = eap_sim_db_get_aka_auth(sm->eap_sim_db_priv, identity, - identity_len, data->rand, data->autn, - data->ik, data->ck, data->res, - &data->res_len, sm); + eap_aka_fullauth(sm, data); +} + + +static void eap_aka_fullauth(struct eap_sm *sm, struct eap_aka_data *data) +{ + size_t identity_len; + int res; + + res = eap_sim_db_get_aka_auth(sm->eap_sim_db_priv, data->permanent, + data->rand, data->autn, data->ik, + data->ck, data->res, &data->res_len, sm); if (res == EAP_SIM_DB_PENDING) { wpa_printf(MSG_DEBUG, "EAP-AKA: AKA authentication data " "not yet available - pending request"); @@ -742,7 +833,7 @@ sm->identity, identity_len); if (data->eap_method == EAP_TYPE_AKA_PRIME) { - eap_aka_prime_derive_keys(identity, identity_len, data->ik, + eap_aka_prime_derive_keys(sm->identity, identity_len, data->ik, data->ck, data->k_encr, data->k_aut, data->k_re, data->msk, data->emsk); } else { @@ -761,6 +852,8 @@ struct wpabuf *respData, struct eap_sim_attrs *attr) { + u8 *new_identity; + wpa_printf(MSG_DEBUG, "EAP-AKA: Processing Identity"); if (attr->mac || attr->iv || attr->encr_data) { @@ -771,17 +864,30 @@ return; } - if (attr->identity) { - os_free(sm->identity); - sm->identity = os_malloc(attr->identity_len); - if (sm->identity) { - os_memcpy(sm->identity, attr->identity, - attr->identity_len); - sm->identity_len = attr->identity_len; - } + /* + * We always request identity with AKA/Identity, so the peer is + * required to have replied with one. + */ + if (!attr->identity || attr->identity_len == 0) { + wpa_printf(MSG_DEBUG, "EAP-AKA: Peer did not provide any " + "identity"); + data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; + eap_aka_state(data, NOTIFICATION); + return; + } + + new_identity = os_malloc(attr->identity_len); + if (new_identity == NULL) { + data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; + eap_aka_state(data, NOTIFICATION); + return; } + os_free(sm->identity); + sm->identity = new_identity; + os_memcpy(sm->identity, attr->identity, attr->identity_len); + sm->identity_len = attr->identity_len; - eap_aka_determine_identity(sm, data, 0, 0); + eap_aka_determine_identity(sm, data); if (eap_get_id(respData) == data->pending_id) { data->pending_id = -1; eap_aka_add_id_msg(data, respData); @@ -806,9 +912,6 @@ struct wpabuf *respData, struct eap_sim_attrs *attr) { - const u8 *identity; - size_t identity_len; - wpa_printf(MSG_DEBUG, "EAP-AKA: Processing Challenge"); #ifdef EAP_SERVER_AKA_PRIME @@ -881,16 +984,8 @@ } else eap_aka_state(data, SUCCESS); - identity = eap_sim_db_get_permanent(sm->eap_sim_db_priv, sm->identity, - sm->identity_len, &identity_len); - if (identity == NULL) { - identity = sm->identity; - identity_len = sm->identity_len; - } - if (data->next_pseudonym) { - eap_sim_db_add_pseudonym(sm->eap_sim_db_priv, identity, - identity_len, + eap_sim_db_add_pseudonym(sm->eap_sim_db_priv, data->permanent, data->next_pseudonym); data->next_pseudonym = NULL; } @@ -898,16 +993,15 @@ if (data->eap_method == EAP_TYPE_AKA_PRIME) { #ifdef EAP_SERVER_AKA_PRIME eap_sim_db_add_reauth_prime(sm->eap_sim_db_priv, - identity, - identity_len, + data->permanent, data->next_reauth_id, data->counter + 1, data->k_encr, data->k_aut, data->k_re); #endif /* EAP_SERVER_AKA_PRIME */ } else { - eap_sim_db_add_reauth(sm->eap_sim_db_priv, identity, - identity_len, + eap_sim_db_add_reauth(sm->eap_sim_db_priv, + data->permanent, data->next_reauth_id, data->counter + 1, data->mk); @@ -936,9 +1030,8 @@ * maintaining a local flag stating whether this AUTS has already been * reported. */ if (!data->auts_reported && - eap_sim_db_resynchronize(sm->eap_sim_db_priv, sm->identity, - sm->identity_len, attr->auts, - data->rand)) { + eap_sim_db_resynchronize(sm->eap_sim_db_priv, data->permanent, + attr->auts, data->rand)) { wpa_printf(MSG_WARNING, "EAP-AKA: Resynchronization failed"); data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; eap_aka_state(data, NOTIFICATION); @@ -946,8 +1039,8 @@ } data->auts_reported = 1; - /* Try again after resynchronization */ - eap_aka_determine_identity(sm, data, 0, 0); + /* Remain in CHALLENGE state to re-try after resynchronization */ + eap_aka_fullauth(sm, data); } @@ -958,8 +1051,6 @@ { struct eap_sim_attrs eattr; u8 *decrypted = NULL; - const u8 *identity, *id2; - size_t identity_len, id2_len; wpa_printf(MSG_DEBUG, "EAP-AKA: Processing Reauthentication"); @@ -1002,7 +1093,7 @@ wpa_printf(MSG_DEBUG, "EAP-AKA: Re-authentication response " "included AT_COUNTER_TOO_SMALL - starting full " "authentication"); - eap_aka_determine_identity(sm, data, 0, 1); + eap_aka_fullauth(sm, data); return; } @@ -1013,35 +1104,19 @@ } else eap_aka_state(data, SUCCESS); - if (data->reauth) { - identity = data->reauth->identity; - identity_len = data->reauth->identity_len; - } else { - identity = sm->identity; - identity_len = sm->identity_len; - } - - id2 = eap_sim_db_get_permanent(sm->eap_sim_db_priv, identity, - identity_len, &id2_len); - if (id2) { - identity = id2; - identity_len = id2_len; - } - if (data->next_reauth_id) { if (data->eap_method == EAP_TYPE_AKA_PRIME) { #ifdef EAP_SERVER_AKA_PRIME eap_sim_db_add_reauth_prime(sm->eap_sim_db_priv, - identity, - identity_len, + data->permanent, data->next_reauth_id, data->counter + 1, data->k_encr, data->k_aut, data->k_re); #endif /* EAP_SERVER_AKA_PRIME */ } else { - eap_sim_db_add_reauth(sm->eap_sim_db_priv, identity, - identity_len, + eap_sim_db_add_reauth(sm->eap_sim_db_priv, + data->permanent, data->next_reauth_id, data->counter + 1, data->mk); diff -Nru wpa-1.0/src/eap_server/eap_server.c wpa-2.1/src/eap_server/eap_server.c --- wpa-1.0/src/eap_server/eap_server.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/eap_server/eap_server.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * hostapd / EAP Full Authenticator state machine (RFC 4137) * Copyright (c) 2004-2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. * * This state machine is based on the full authenticator state machine defined * in RFC 4137. However, to support backend authentication in RADIUS @@ -281,6 +275,11 @@ { SM_ENTRY(EAP, INTEGRITY_CHECK); + if (!eap_hdr_len_valid(sm->eap_if.eapRespData, 1)) { + sm->ignore = TRUE; + return; + } + if (sm->m->check) { sm->ignore = sm->m->check(sm, sm->eap_method_priv, sm->eap_if.eapRespData); @@ -315,6 +314,9 @@ { SM_ENTRY(EAP, METHOD_RESPONSE); + if (!eap_hdr_len_valid(sm->eap_if.eapRespData, 1)) + return; + sm->m->process(sm, sm->eap_method_priv, sm->eap_if.eapRespData); if (sm->m->isDone(sm, sm->eap_method_priv)) { eap_sm_Policy_update(sm, NULL, 0); @@ -341,6 +343,7 @@ SM_ENTRY(EAP, PROPOSE_METHOD); +try_another_method: type = eap_sm_Policy_getNextMethod(sm, &vendor); if (vendor == EAP_VENDOR_IETF) sm->currentMethod = type; @@ -358,8 +361,14 @@ "method %d", sm->currentMethod); sm->m = NULL; sm->currentMethod = EAP_TYPE_NONE; + goto try_another_method; } } + if (sm->m == NULL) { + wpa_printf(MSG_DEBUG, "EAP: Could not find suitable EAP method"); + sm->decision = DECISION_FAILURE; + return; + } if (sm->currentMethod == EAP_TYPE_IDENTITY || sm->currentMethod == EAP_TYPE_NOTIFICATION) sm->methodState = METHOD_CONTINUE; @@ -386,6 +395,9 @@ } sm->m = NULL; + if (!eap_hdr_len_valid(sm->eap_if.eapRespData, 1)) + return; + nak = wpabuf_head(sm->eap_if.eapRespData); if (nak && wpabuf_len(sm->eap_if.eapRespData) > sizeof(*nak)) { len = be_to_host16(nak->length); @@ -697,6 +709,15 @@ SM_ENTER(EAP, METHOD_RESPONSE); break; case EAP_METHOD_REQUEST: + if (sm->m == NULL) { + /* + * This transition is not mentioned in RFC 4137, but it + * is needed to handle cleanly a case where EAP method + * initialization fails. + */ + SM_ENTER(EAP, FAILURE); + break; + } SM_ENTER(EAP, SEND_REQUEST); break; case EAP_METHOD_RESPONSE: @@ -1273,6 +1294,8 @@ sm->fragment_size = conf->fragment_size; sm->pwd_group = conf->pwd_group; sm->pbc_in_m1 = conf->pbc_in_m1; + sm->server_id = conf->server_id; + sm->server_id_len = conf->server_id_len; wpa_printf(MSG_DEBUG, "EAP: Server state machine created"); diff -Nru wpa-1.0/src/eap_server/eap_server_eke.c wpa-2.1/src/eap_server/eap_server_eke.c --- wpa-1.0/src/eap_server/eap_server_eke.c 1970-01-01 00:00:00.000000000 +0000 +++ wpa-2.1/src/eap_server/eap_server_eke.c 2014-02-04 11:23:35.000000000 +0000 @@ -0,0 +1,793 @@ +/* + * hostapd / EAP-EKE (RFC 6124) server + * Copyright (c) 2013, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "crypto/random.h" +#include "eap_server/eap_i.h" +#include "eap_common/eap_eke_common.h" + + +struct eap_eke_data { + enum { + IDENTITY, COMMIT, CONFIRM, FAILURE_REPORT, SUCCESS, FAILURE + } state; + u8 msk[EAP_MSK_LEN]; + u8 emsk[EAP_EMSK_LEN]; + u8 *peerid; + size_t peerid_len; + u8 peerid_type; + u8 serverid_type; + u8 dh_priv[EAP_EKE_MAX_DH_LEN]; + u8 key[EAP_EKE_MAX_KEY_LEN]; + struct eap_eke_session sess; + u8 nonce_p[EAP_EKE_MAX_NONCE_LEN]; + u8 nonce_s[EAP_EKE_MAX_NONCE_LEN]; + struct wpabuf *msgs; + int phase2; + u32 failure_code; +}; + + +static const char * eap_eke_state_txt(int state) +{ + switch (state) { + case IDENTITY: + return "IDENTITY"; + case COMMIT: + return "COMMIT"; + case CONFIRM: + return "CONFIRM"; + case FAILURE_REPORT: + return "FAILURE_REPORT"; + case SUCCESS: + return "SUCCESS"; + case FAILURE: + return "FAILURE"; + default: + return "?"; + } +} + + +static void eap_eke_state(struct eap_eke_data *data, int state) +{ + wpa_printf(MSG_DEBUG, "EAP-EKE: %s -> %s", + eap_eke_state_txt(data->state), + eap_eke_state_txt(state)); + data->state = state; +} + + +static void eap_eke_fail(struct eap_eke_data *data, u32 code) +{ + wpa_printf(MSG_DEBUG, "EAP-EKE: Failure - code 0x%x", code); + data->failure_code = code; + eap_eke_state(data, FAILURE_REPORT); +} + + +static void * eap_eke_init(struct eap_sm *sm) +{ + struct eap_eke_data *data; + size_t i; + + data = os_zalloc(sizeof(*data)); + if (data == NULL) + return NULL; + eap_eke_state(data, IDENTITY); + + data->serverid_type = EAP_EKE_ID_OPAQUE; + for (i = 0; i < sm->server_id_len; i++) { + if (sm->server_id[i] == '.' && + data->serverid_type == EAP_EKE_ID_OPAQUE) + data->serverid_type = EAP_EKE_ID_FQDN; + if (sm->server_id[i] == '@') + data->serverid_type = EAP_EKE_ID_NAI; + } + + data->phase2 = sm->init_phase2; + + return data; +} + + +static void eap_eke_reset(struct eap_sm *sm, void *priv) +{ + struct eap_eke_data *data = priv; + eap_eke_session_clean(&data->sess); + os_free(data->peerid); + wpabuf_free(data->msgs); + os_free(data); +} + + +static struct wpabuf * eap_eke_build_msg(struct eap_eke_data *data, + u8 id, size_t length, u8 eke_exch) +{ + struct wpabuf *msg; + size_t plen; + + plen = 1 + length; + + msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_EKE, plen, + EAP_CODE_REQUEST, id); + if (msg == NULL) { + wpa_printf(MSG_ERROR, "EAP-EKE: Failed to allocate memory"); + return NULL; + } + + wpabuf_put_u8(msg, eke_exch); + + return msg; +} + + +static int supported_proposal(const u8 *pos) +{ + if (pos[0] == EAP_EKE_DHGROUP_EKE_16 && + pos[1] == EAP_EKE_ENCR_AES128_CBC && + pos[2] == EAP_EKE_PRF_HMAC_SHA2_256 && + pos[3] == EAP_EKE_MAC_HMAC_SHA2_256) + return 1; + + if (pos[0] == EAP_EKE_DHGROUP_EKE_15 && + pos[1] == EAP_EKE_ENCR_AES128_CBC && + pos[2] == EAP_EKE_PRF_HMAC_SHA2_256 && + pos[3] == EAP_EKE_MAC_HMAC_SHA2_256) + return 1; + + if (pos[0] == EAP_EKE_DHGROUP_EKE_14 && + pos[1] == EAP_EKE_ENCR_AES128_CBC && + pos[2] == EAP_EKE_PRF_HMAC_SHA2_256 && + pos[3] == EAP_EKE_MAC_HMAC_SHA2_256) + return 1; + + if (pos[0] == EAP_EKE_DHGROUP_EKE_14 && + pos[1] == EAP_EKE_ENCR_AES128_CBC && + pos[2] == EAP_EKE_PRF_HMAC_SHA1 && + pos[3] == EAP_EKE_MAC_HMAC_SHA1) + return 1; + + return 0; +} + + +static struct wpabuf * eap_eke_build_failure(struct eap_eke_data *data, u8 id) +{ + struct wpabuf *msg; + + wpa_printf(MSG_DEBUG, "EAP-EKE: Request/Failure: Failure-Code=0x%x", + data->failure_code); + + msg = eap_eke_build_msg(data, id, 4, EAP_EKE_FAILURE); + if (msg == NULL) { + eap_eke_state(data, FAILURE); + return NULL; + } + wpabuf_put_be32(msg, data->failure_code); + + return msg; +} + + +static struct wpabuf * eap_eke_build_identity(struct eap_sm *sm, + struct eap_eke_data *data, + u8 id) +{ + struct wpabuf *msg; + size_t plen; + + wpa_printf(MSG_DEBUG, "EAP-EKE: Request/Identity"); + + plen = 2 + 4 * 4 + 1 + sm->server_id_len; + msg = eap_eke_build_msg(data, id, plen, EAP_EKE_ID); + if (msg == NULL) + return NULL; + + wpabuf_put_u8(msg, 4); /* NumProposals */ + wpabuf_put_u8(msg, 0); /* Reserved */ + + /* Proposal - DH Group 16 with AES128-CBC and SHA256 */ + wpabuf_put_u8(msg, EAP_EKE_DHGROUP_EKE_16); /* Group Description */ + wpabuf_put_u8(msg, EAP_EKE_ENCR_AES128_CBC); /* Encryption */ + wpabuf_put_u8(msg, EAP_EKE_PRF_HMAC_SHA2_256); /* PRF */ + wpabuf_put_u8(msg, EAP_EKE_MAC_HMAC_SHA2_256); /* MAC */ + + /* Proposal - DH Group 15 with AES128-CBC and SHA256 */ + wpabuf_put_u8(msg, EAP_EKE_DHGROUP_EKE_15); /* Group Description */ + wpabuf_put_u8(msg, EAP_EKE_ENCR_AES128_CBC); /* Encryption */ + wpabuf_put_u8(msg, EAP_EKE_PRF_HMAC_SHA2_256); /* PRF */ + wpabuf_put_u8(msg, EAP_EKE_MAC_HMAC_SHA2_256); /* MAC */ + + /* Proposal - DH Group 14 with AES128-CBC and SHA256 */ + wpabuf_put_u8(msg, EAP_EKE_DHGROUP_EKE_14); /* Group Description */ + wpabuf_put_u8(msg, EAP_EKE_ENCR_AES128_CBC); /* Encryption */ + wpabuf_put_u8(msg, EAP_EKE_PRF_HMAC_SHA2_256); /* PRF */ + wpabuf_put_u8(msg, EAP_EKE_MAC_HMAC_SHA2_256); /* MAC */ + + /* + * Proposal - DH Group 14 with AES128-CBC and SHA1 + * (mandatory to implement algorithms) + */ + wpabuf_put_u8(msg, EAP_EKE_DHGROUP_EKE_14); /* Group Description */ + wpabuf_put_u8(msg, EAP_EKE_ENCR_AES128_CBC); /* Encryption */ + wpabuf_put_u8(msg, EAP_EKE_PRF_HMAC_SHA1); /* PRF */ + wpabuf_put_u8(msg, EAP_EKE_MAC_HMAC_SHA1); /* MAC */ + + /* Server IDType + Identity */ + wpabuf_put_u8(msg, data->serverid_type); + wpabuf_put_data(msg, sm->server_id, sm->server_id_len); + + wpabuf_free(data->msgs); + data->msgs = wpabuf_dup(msg); + if (data->msgs == NULL) { + wpabuf_free(msg); + return NULL; + } + + return msg; +} + + +static struct wpabuf * eap_eke_build_commit(struct eap_sm *sm, + struct eap_eke_data *data, u8 id) +{ + struct wpabuf *msg; + u8 pub[EAP_EKE_MAX_DH_LEN]; + + wpa_printf(MSG_DEBUG, "EAP-EKE: Request/Commit"); + + if (sm->user == NULL || sm->user->password == NULL) { + wpa_printf(MSG_INFO, "EAP-EKE: Password with not configured"); + eap_eke_fail(data, EAP_EKE_FAIL_PASSWD_NOT_FOUND); + return eap_eke_build_failure(data, id); + } + + if (eap_eke_derive_key(&data->sess, sm->user->password, + sm->user->password_len, + sm->server_id, sm->server_id_len, + data->peerid, data->peerid_len, data->key) < 0) { + wpa_printf(MSG_INFO, "EAP-EKE: Failed to derive key"); + eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + return eap_eke_build_failure(data, id); + } + + msg = eap_eke_build_msg(data, id, data->sess.dhcomp_len, + EAP_EKE_COMMIT); + if (msg == NULL) { + eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + return eap_eke_build_failure(data, id); + } + + /* + * y_s = g ^ x_s (mod p) + * x_s = random number 2 .. p-1 + * temp = prf(0+, password) + * key = prf+(temp, ID_S | ID_P) + * DHComponent_S = Encr(key, y_s) + */ + + if (eap_eke_dh_init(data->sess.dhgroup, data->dh_priv, pub) < 0) { + wpa_printf(MSG_INFO, "EAP-EKE: Failed to initialize DH"); + eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + return eap_eke_build_failure(data, id); + } + + if (eap_eke_dhcomp(&data->sess, data->key, pub, + wpabuf_put(msg, data->sess.dhcomp_len)) + < 0) { + wpa_printf(MSG_INFO, "EAP-EKE: Failed to build DHComponent_S"); + wpabuf_free(msg); + eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + return eap_eke_build_failure(data, id); + } + + if (wpabuf_resize(&data->msgs, wpabuf_len(msg)) < 0) { + wpabuf_free(msg); + eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + return eap_eke_build_failure(data, id); + } + wpabuf_put_buf(data->msgs, msg); + + return msg; +} + + +static struct wpabuf * eap_eke_build_confirm(struct eap_sm *sm, + struct eap_eke_data *data, u8 id) +{ + struct wpabuf *msg; + size_t plen, prot_len; + u8 nonces[2 * EAP_EKE_MAX_NONCE_LEN]; + u8 *auth; + + wpa_printf(MSG_DEBUG, "EAP-EKE: Request/Confirm"); + + plen = data->sess.pnonce_ps_len + data->sess.prf_len; + msg = eap_eke_build_msg(data, id, plen, EAP_EKE_CONFIRM); + if (msg == NULL) { + eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + return eap_eke_build_failure(data, id); + } + + if (random_get_bytes(data->nonce_s, data->sess.nonce_len)) { + wpabuf_free(msg); + eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + return eap_eke_build_failure(data, id); + } + wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: Nonce_S", + data->nonce_s, data->sess.nonce_len); + + os_memcpy(nonces, data->nonce_p, data->sess.nonce_len); + os_memcpy(nonces + data->sess.nonce_len, data->nonce_s, + data->sess.nonce_len); + prot_len = wpabuf_tailroom(msg); + if (eap_eke_prot(&data->sess, nonces, 2 * data->sess.nonce_len, + wpabuf_put(msg, 0), &prot_len) < 0) { + wpabuf_free(msg); + eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + return eap_eke_build_failure(data, id); + } + wpabuf_put(msg, prot_len); + + if (eap_eke_derive_ka(&data->sess, + sm->server_id, sm->server_id_len, + data->peerid, data->peerid_len, + data->nonce_p, data->nonce_s) < 0) { + wpabuf_free(msg); + eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + return eap_eke_build_failure(data, id); + } + + auth = wpabuf_put(msg, data->sess.prf_len); + if (eap_eke_auth(&data->sess, "EAP-EKE server", data->msgs, auth) < 0) { + wpabuf_free(msg); + eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + return eap_eke_build_failure(data, id); + } + wpa_hexdump(MSG_DEBUG, "EAP-EKE: Auth_S", auth, data->sess.prf_len); + + return msg; +} + + +static struct wpabuf * eap_eke_buildReq(struct eap_sm *sm, void *priv, u8 id) +{ + struct eap_eke_data *data = priv; + + switch (data->state) { + case IDENTITY: + return eap_eke_build_identity(sm, data, id); + case COMMIT: + return eap_eke_build_commit(sm, data, id); + case CONFIRM: + return eap_eke_build_confirm(sm, data, id); + case FAILURE_REPORT: + return eap_eke_build_failure(data, id); + default: + wpa_printf(MSG_DEBUG, "EAP-EKE: Unknown state %d in buildReq", + data->state); + break; + } + return NULL; +} + + +static Boolean eap_eke_check(struct eap_sm *sm, void *priv, + struct wpabuf *respData) +{ + struct eap_eke_data *data = priv; + size_t len; + const u8 *pos; + u8 eke_exch; + + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_EKE, respData, &len); + if (pos == NULL || len < 1) { + wpa_printf(MSG_INFO, "EAP-EKE: Invalid frame"); + return TRUE; + } + + eke_exch = *pos; + wpa_printf(MSG_DEBUG, "EAP-EKE: Received frame: EKE-Exch=%d", eke_exch); + + if (data->state == IDENTITY && eke_exch == EAP_EKE_ID) + return FALSE; + + if (data->state == COMMIT && eke_exch == EAP_EKE_COMMIT) + return FALSE; + + if (data->state == CONFIRM && eke_exch == EAP_EKE_CONFIRM) + return FALSE; + + if (eke_exch == EAP_EKE_FAILURE) + return FALSE; + + wpa_printf(MSG_INFO, "EAP-EKE: Unexpected EKE-Exch=%d in state=%d", + eke_exch, data->state); + + return TRUE; +} + + +static void eap_eke_process_identity(struct eap_sm *sm, + struct eap_eke_data *data, + const struct wpabuf *respData, + const u8 *payload, size_t payloadlen) +{ + const u8 *pos, *end; + int i; + + wpa_printf(MSG_DEBUG, "EAP-EKE: Received Response/Identity"); + + if (data->state != IDENTITY) { + eap_eke_fail(data, EAP_EKE_FAIL_PROTO_ERROR); + return; + } + + pos = payload; + end = payload + payloadlen; + + if (pos + 2 + 4 + 1 > end) { + wpa_printf(MSG_INFO, "EAP-EKE: Too short EAP-EKE-ID payload"); + eap_eke_fail(data, EAP_EKE_FAIL_PROTO_ERROR); + return; + } + + if (*pos != 1) { + wpa_printf(MSG_INFO, "EAP-EKE: Unexpected NumProposals %d (expected 1)", + *pos); + eap_eke_fail(data, EAP_EKE_FAIL_PROTO_ERROR); + return; + } + + pos += 2; + + if (!supported_proposal(pos)) { + wpa_printf(MSG_INFO, "EAP-EKE: Unexpected Proposal (%u:%u:%u:%u)", + pos[0], pos[1], pos[2], pos[3]); + eap_eke_fail(data, EAP_EKE_FAIL_PROTO_ERROR); + return; + } + + wpa_printf(MSG_DEBUG, "EAP-EKE: Selected Proposal (%u:%u:%u:%u)", + pos[0], pos[1], pos[2], pos[3]); + if (eap_eke_session_init(&data->sess, pos[0], pos[1], pos[2], pos[3]) < + 0) { + eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + return; + } + pos += 4; + + data->peerid_type = *pos++; + os_free(data->peerid); + data->peerid = os_malloc(end - pos); + if (data->peerid == NULL) { + wpa_printf(MSG_INFO, "EAP-EKE: Failed to allocate memory for peerid"); + eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + return; + } + os_memcpy(data->peerid, pos, end - pos); + data->peerid_len = end - pos; + wpa_printf(MSG_DEBUG, "EAP-EKE: Peer IDType %u", data->peerid_type); + wpa_hexdump_ascii(MSG_DEBUG, "EAP-EKE: Peer Identity", + data->peerid, data->peerid_len); + + if (eap_user_get(sm, data->peerid, data->peerid_len, data->phase2)) { + wpa_printf(MSG_INFO, "EAP-EKE: Peer Identity not found from user database"); + eap_eke_fail(data, EAP_EKE_FAIL_PASSWD_NOT_FOUND); + return; + } + + for (i = 0; i < EAP_MAX_METHODS; i++) { + if (sm->user->methods[i].vendor == EAP_VENDOR_IETF && + sm->user->methods[i].method == EAP_TYPE_EKE) + break; + } + if (i == EAP_MAX_METHODS) { + wpa_printf(MSG_INFO, "EAP-EKE: Matching user entry does not allow EAP-EKE"); + eap_eke_fail(data, EAP_EKE_FAIL_PASSWD_NOT_FOUND); + return; + } + + if (sm->user->password == NULL || sm->user->password_len == 0) { + wpa_printf(MSG_INFO, "EAP-EKE: No password configured for peer"); + eap_eke_fail(data, EAP_EKE_FAIL_PASSWD_NOT_FOUND); + return; + } + + if (wpabuf_resize(&data->msgs, wpabuf_len(respData)) < 0) { + eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + return; + } + wpabuf_put_buf(data->msgs, respData); + + eap_eke_state(data, COMMIT); +} + + +static void eap_eke_process_commit(struct eap_sm *sm, + struct eap_eke_data *data, + const struct wpabuf *respData, + const u8 *payload, size_t payloadlen) +{ + const u8 *pos, *end, *dhcomp, *pnonce; + size_t decrypt_len; + + wpa_printf(MSG_DEBUG, "EAP-EKE: Received Response/Commit"); + + if (data->state != COMMIT) { + eap_eke_fail(data, EAP_EKE_FAIL_PROTO_ERROR); + return; + } + + pos = payload; + end = payload + payloadlen; + + if (pos + data->sess.dhcomp_len + data->sess.pnonce_len > end) { + wpa_printf(MSG_DEBUG, "EAP-EKE: Too short EAP-EKE-Commit"); + eap_eke_fail(data, EAP_EKE_FAIL_PROTO_ERROR); + return; + } + + wpa_hexdump(MSG_DEBUG, "EAP-EKE: DHComponent_P", + pos, data->sess.dhcomp_len); + dhcomp = pos; + pos += data->sess.dhcomp_len; + wpa_hexdump(MSG_DEBUG, "EAP-EKE: PNonce_P", pos, data->sess.pnonce_len); + pnonce = pos; + pos += data->sess.pnonce_len; + wpa_hexdump(MSG_DEBUG, "EAP-EKE: CBValue", pos, end - pos); + + if (eap_eke_shared_secret(&data->sess, data->key, data->dh_priv, dhcomp) + < 0) { + wpa_printf(MSG_INFO, "EAP-EKE: Failed to derive shared secret"); + eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + return; + } + + if (eap_eke_derive_ke_ki(&data->sess, + sm->server_id, sm->server_id_len, + data->peerid, data->peerid_len) < 0) { + wpa_printf(MSG_INFO, "EAP-EKE: Failed to derive Ke/Ki"); + eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + return; + } + + decrypt_len = sizeof(data->nonce_p); + if (eap_eke_decrypt_prot(&data->sess, pnonce, data->sess.pnonce_len, + data->nonce_p, &decrypt_len) < 0) { + wpa_printf(MSG_INFO, "EAP-EKE: Failed to decrypt PNonce_P"); + eap_eke_fail(data, EAP_EKE_FAIL_AUTHENTICATION_FAIL); + return; + } + if (decrypt_len < (size_t) data->sess.nonce_len) { + wpa_printf(MSG_INFO, "EAP-EKE: PNonce_P protected data too short to include Nonce_P"); + eap_eke_fail(data, EAP_EKE_FAIL_AUTHENTICATION_FAIL); + return; + } + wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: Nonce_P", + data->nonce_p, data->sess.nonce_len); + + if (wpabuf_resize(&data->msgs, wpabuf_len(respData)) < 0) { + eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + return; + } + wpabuf_put_buf(data->msgs, respData); + + eap_eke_state(data, CONFIRM); +} + + +static void eap_eke_process_confirm(struct eap_sm *sm, + struct eap_eke_data *data, + const struct wpabuf *respData, + const u8 *payload, size_t payloadlen) +{ + size_t decrypt_len; + u8 nonce[EAP_EKE_MAX_NONCE_LEN]; + u8 auth_p[EAP_EKE_MAX_HASH_LEN]; + + wpa_printf(MSG_DEBUG, "EAP-EKE: Received Response/Confirm"); + + if (data->state != CONFIRM) { + eap_eke_fail(data, EAP_EKE_FAIL_PROTO_ERROR); + return; + } + + wpa_printf(MSG_DEBUG, "EAP-EKE: Received Response/Confirm"); + + if (payloadlen < (size_t) data->sess.pnonce_len + data->sess.prf_len) { + wpa_printf(MSG_DEBUG, "EAP-EKE: Too short EAP-EKE-Confirm"); + eap_eke_fail(data, EAP_EKE_FAIL_PROTO_ERROR); + return; + } + + decrypt_len = sizeof(nonce); + if (eap_eke_decrypt_prot(&data->sess, payload, data->sess.pnonce_len, + nonce, &decrypt_len) < 0) { + wpa_printf(MSG_INFO, "EAP-EKE: Failed to decrypt PNonce_S"); + eap_eke_fail(data, EAP_EKE_FAIL_AUTHENTICATION_FAIL); + return; + } + if (decrypt_len < (size_t) data->sess.nonce_len) { + wpa_printf(MSG_INFO, "EAP-EKE: PNonce_S protected data too short to include Nonce_S"); + eap_eke_fail(data, EAP_EKE_FAIL_AUTHENTICATION_FAIL); + return; + } + wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: Received Nonce_S", + nonce, data->sess.nonce_len); + if (os_memcmp(nonce, data->nonce_s, data->sess.nonce_len) != 0) { + wpa_printf(MSG_INFO, "EAP-EKE: Received Nonce_S does not match previously sent Nonce_S"); + eap_eke_fail(data, EAP_EKE_FAIL_AUTHENTICATION_FAIL); + return; + } + + if (eap_eke_auth(&data->sess, "EAP-EKE peer", data->msgs, auth_p) < 0) { + wpa_printf(MSG_INFO, "EAP-EKE: Could not derive Auth_P"); + eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + return; + } + wpa_hexdump(MSG_DEBUG, "EAP-EKE: Auth_P", auth_p, data->sess.prf_len); + if (os_memcmp(auth_p, payload + data->sess.pnonce_len, + data->sess.prf_len) != 0) { + wpa_printf(MSG_INFO, "EAP-EKE: Auth_P does not match"); + eap_eke_fail(data, EAP_EKE_FAIL_AUTHENTICATION_FAIL); + return; + } + + if (eap_eke_derive_msk(&data->sess, sm->server_id, sm->server_id_len, + data->peerid, data->peerid_len, + data->nonce_s, data->nonce_p, + data->msk, data->emsk) < 0) { + wpa_printf(MSG_INFO, "EAP-EKE: Failed to derive MSK/EMSK"); + eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + return; + } + + os_memset(data->dh_priv, 0, sizeof(data->dh_priv)); + os_memset(data->key, 0, sizeof(data->key)); + eap_eke_session_clean(&data->sess); + + eap_eke_state(data, SUCCESS); +} + + +static void eap_eke_process_failure(struct eap_sm *sm, + struct eap_eke_data *data, + const struct wpabuf *respData, + const u8 *payload, size_t payloadlen) +{ + u32 code; + + wpa_printf(MSG_DEBUG, "EAP-EKE: Received Response/Failure"); + + if (payloadlen < 4) { + wpa_printf(MSG_DEBUG, "EAP-EKE: Too short EAP-EKE-Failure"); + eap_eke_state(data, FAILURE); + return; + } + + code = WPA_GET_BE32(payload); + wpa_printf(MSG_DEBUG, "EAP-EKE: Peer reported failure code 0x%x", code); + + eap_eke_state(data, FAILURE); +} + + +static void eap_eke_process(struct eap_sm *sm, void *priv, + struct wpabuf *respData) +{ + struct eap_eke_data *data = priv; + u8 eke_exch; + size_t len; + const u8 *pos, *end; + + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_EKE, respData, &len); + if (pos == NULL || len < 1) + return; + + eke_exch = *pos; + end = pos + len; + pos++; + + wpa_hexdump(MSG_DEBUG, "EAP-EKE: Received payload", pos, end - pos); + + switch (eke_exch) { + case EAP_EKE_ID: + eap_eke_process_identity(sm, data, respData, pos, end - pos); + break; + case EAP_EKE_COMMIT: + eap_eke_process_commit(sm, data, respData, pos, end - pos); + break; + case EAP_EKE_CONFIRM: + eap_eke_process_confirm(sm, data, respData, pos, end - pos); + break; + case EAP_EKE_FAILURE: + eap_eke_process_failure(sm, data, respData, pos, end - pos); + break; + } +} + + +static Boolean eap_eke_isDone(struct eap_sm *sm, void *priv) +{ + struct eap_eke_data *data = priv; + return data->state == SUCCESS || data->state == FAILURE; +} + + +static u8 * eap_eke_getKey(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_eke_data *data = priv; + u8 *key; + + if (data->state != SUCCESS) + return NULL; + + key = os_malloc(EAP_MSK_LEN); + if (key == NULL) + return NULL; + os_memcpy(key, data->msk, EAP_MSK_LEN); + *len = EAP_MSK_LEN; + + return key; +} + + +static u8 * eap_eke_get_emsk(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_eke_data *data = priv; + u8 *key; + + if (data->state != SUCCESS) + return NULL; + + key = os_malloc(EAP_EMSK_LEN); + if (key == NULL) + return NULL; + os_memcpy(key, data->emsk, EAP_EMSK_LEN); + *len = EAP_EMSK_LEN; + + return key; +} + + +static Boolean eap_eke_isSuccess(struct eap_sm *sm, void *priv) +{ + struct eap_eke_data *data = priv; + return data->state == SUCCESS; +} + + +int eap_server_eke_register(void) +{ + struct eap_method *eap; + int ret; + + eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, + EAP_VENDOR_IETF, EAP_TYPE_EKE, "EKE"); + if (eap == NULL) + return -1; + + eap->init = eap_eke_init; + eap->reset = eap_eke_reset; + eap->buildReq = eap_eke_buildReq; + eap->check = eap_eke_check; + eap->process = eap_eke_process; + eap->isDone = eap_eke_isDone; + eap->getKey = eap_eke_getKey; + eap->isSuccess = eap_eke_isSuccess; + eap->get_emsk = eap_eke_get_emsk; + + ret = eap_server_method_register(eap); + if (ret) + eap_server_method_free(eap); + return ret; +} diff -Nru wpa-1.0/src/eap_server/eap_server_fast.c wpa-2.1/src/eap_server/eap_server_fast.c --- wpa-1.0/src/eap_server/eap_server_fast.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/eap_server/eap_server_fast.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * EAP-FAST server (RFC 4851) * Copyright (c) 2004-2008, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff -Nru wpa-1.0/src/eap_server/eap_server_gpsk.c wpa-2.1/src/eap_server/eap_server_gpsk.c --- wpa-1.0/src/eap_server/eap_server_gpsk.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/eap_server/eap_server_gpsk.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * hostapd / EAP-GPSK (RFC 5433) server * Copyright (c) 2006-2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -32,8 +26,6 @@ size_t pk_len; u8 *id_peer; size_t id_peer_len; - u8 *id_server; - size_t id_server_len; #define MAX_NUM_CSUITES 2 struct eap_gpsk_csuite csuite_list[MAX_NUM_CSUITES]; size_t csuite_count; @@ -77,11 +69,6 @@ return NULL; data->state = GPSK_1; - /* TODO: add support for configuring ID_Server */ - data->id_server = (u8 *) os_strdup("hostapd"); - if (data->id_server) - data->id_server_len = os_strlen((char *) data->id_server); - data->csuite_count = 0; if (eap_gpsk_supported_ciphersuite(EAP_GPSK_VENDOR_IETF, EAP_GPSK_CIPHER_AES)) { @@ -107,7 +94,6 @@ static void eap_gpsk_reset(struct eap_sm *sm, void *priv) { struct eap_gpsk_data *data = priv; - os_free(data->id_server); os_free(data->id_peer); os_free(data); } @@ -129,7 +115,7 @@ wpa_hexdump(MSG_MSGDUMP, "EAP-GPSK: RAND_Server", data->rand_server, EAP_GPSK_RAND_LEN); - len = 1 + 2 + data->id_server_len + EAP_GPSK_RAND_LEN + 2 + + len = 1 + 2 + sm->server_id_len + EAP_GPSK_RAND_LEN + 2 + data->csuite_count * sizeof(struct eap_gpsk_csuite); req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_GPSK, len, EAP_CODE_REQUEST, id); @@ -141,8 +127,8 @@ } wpabuf_put_u8(req, EAP_GPSK_OPCODE_GPSK_1); - wpabuf_put_be16(req, data->id_server_len); - wpabuf_put_data(req, data->id_server, data->id_server_len); + wpabuf_put_be16(req, sm->server_id_len); + wpabuf_put_data(req, sm->server_id, sm->server_id_len); wpabuf_put_data(req, data->rand_server, EAP_GPSK_RAND_LEN); wpabuf_put_be16(req, data->csuite_count * sizeof(struct eap_gpsk_csuite)); @@ -164,7 +150,7 @@ wpa_printf(MSG_DEBUG, "EAP-GPSK: Request/GPSK-3"); miclen = eap_gpsk_mic_len(data->vendor, data->specifier); - len = 1 + 2 * EAP_GPSK_RAND_LEN + 2 + data->id_server_len + + len = 1 + 2 * EAP_GPSK_RAND_LEN + 2 + sm->server_id_len + sizeof(struct eap_gpsk_csuite) + 2 + miclen; req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_GPSK, len, EAP_CODE_REQUEST, id); @@ -180,8 +166,8 @@ wpabuf_put_data(req, data->rand_peer, EAP_GPSK_RAND_LEN); wpabuf_put_data(req, data->rand_server, EAP_GPSK_RAND_LEN); - wpabuf_put_be16(req, data->id_server_len); - wpabuf_put_data(req, data->id_server, data->id_server_len); + wpabuf_put_be16(req, sm->server_id_len); + wpabuf_put_data(req, sm->server_id, sm->server_id_len); csuite = wpabuf_put(req, sizeof(*csuite)); WPA_PUT_BE32(csuite->vendor, data->vendor); WPA_PUT_BE16(csuite->specifier, data->specifier); @@ -307,8 +293,8 @@ eap_gpsk_state(data, FAILURE); return; } - if (alen != data->id_server_len || - os_memcmp(pos, data->id_server, alen) != 0) { + if (alen != sm->server_id_len || + os_memcmp(pos, sm->server_id, alen) != 0) { wpa_printf(MSG_DEBUG, "EAP-GPSK: ID_Server in GPSK-1 and " "GPSK-2 did not match"); eap_gpsk_state(data, FAILURE); @@ -422,7 +408,7 @@ data->vendor, data->specifier, data->rand_peer, data->rand_server, data->id_peer, data->id_peer_len, - data->id_server, data->id_server_len, + sm->server_id, sm->server_id_len, data->msk, data->emsk, data->sk, &data->sk_len, data->pk, &data->pk_len) < 0) { diff -Nru wpa-1.0/src/eap_server/eap_server_gtc.c wpa-2.1/src/eap_server/eap_server_gtc.c --- wpa-1.0/src/eap_server/eap_server_gtc.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/eap_server/eap_server_gtc.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * hostapd / EAP-GTC (RFC 3748) * Copyright (c) 2004-2006, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff -Nru wpa-1.0/src/eap_server/eap_server_identity.c wpa-2.1/src/eap_server/eap_server_identity.c --- wpa-1.0/src/eap_server/eap_server_identity.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/eap_server/eap_server_identity.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * hostapd / EAP-Identity * Copyright (c) 2004-2006, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff -Nru wpa-1.0/src/eap_server/eap_server_ikev2.c wpa-2.1/src/eap_server/eap_server_ikev2.c --- wpa-1.0/src/eap_server/eap_server_ikev2.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/eap_server/eap_server_ikev2.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * EAP-IKEv2 server (RFC 5106) * Copyright (c) 2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -109,8 +103,11 @@ data->ikev2.proposal.encr = ENCR_AES_CBC; data->ikev2.proposal.dh = DH_GROUP2_1024BIT_MODP; - data->ikev2.IDi = (u8 *) os_strdup("hostapd"); - data->ikev2.IDi_len = 7; + data->ikev2.IDi = os_malloc(sm->server_id_len); + if (data->ikev2.IDi == NULL) + goto failed; + os_memcpy(data->ikev2.IDi, sm->server_id, sm->server_id_len); + data->ikev2.IDi_len = sm->server_id_len; data->ikev2.get_shared_secret = eap_ikev2_get_shared_secret; data->ikev2.cb_ctx = sm; diff -Nru wpa-1.0/src/eap_server/eap_server_md5.c wpa-2.1/src/eap_server/eap_server_md5.c --- wpa-1.0/src/eap_server/eap_server_md5.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/eap_server/eap_server_md5.c 2014-02-04 11:23:35.000000000 +0000 @@ -1,15 +1,9 @@ /* * hostapd / EAP-MD5 server - * Copyright (c) 2004-2007, Jouni Malinen + * Copyright (c) 2004-2012, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -125,8 +119,12 @@ wpa_hexdump(MSG_MSGDUMP, "EAP-MD5: Response", pos, CHAP_MD5_LEN); id = eap_get_id(respData); - chap_md5(id, sm->user->password, sm->user->password_len, - data->challenge, CHALLENGE_LEN, hash); + if (chap_md5(id, sm->user->password, sm->user->password_len, + data->challenge, CHALLENGE_LEN, hash)) { + wpa_printf(MSG_INFO, "EAP-MD5: CHAP MD5 operation failed"); + data->state = FAILURE; + return; + } if (os_memcmp(hash, pos, CHAP_MD5_LEN) == 0) { wpa_printf(MSG_DEBUG, "EAP-MD5: Done - Success"); diff -Nru wpa-1.0/src/eap_server/eap_server_methods.c wpa-2.1/src/eap_server/eap_server_methods.c --- wpa-1.0/src/eap_server/eap_server_methods.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/eap_server/eap_server_methods.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * EAP server method registration * Copyright (c) 2004-2009, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff -Nru wpa-1.0/src/eap_server/eap_server_mschapv2.c wpa-2.1/src/eap_server/eap_server_mschapv2.c --- wpa-1.0/src/eap_server/eap_server_mschapv2.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/eap_server/eap_server_mschapv2.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * hostapd / EAP-MSCHAPv2 (draft-kamath-pppext-eap-mschapv2-00.txt) server * Copyright (c) 2004-2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -106,7 +100,6 @@ { struct wpabuf *req; struct eap_mschapv2_hdr *ms; - char *name = "hostapd"; /* TODO: make this configurable */ size_t ms_len; if (!data->auth_challenge_from_tls && @@ -117,7 +110,7 @@ return NULL; } - ms_len = sizeof(*ms) + 1 + CHALLENGE_LEN + os_strlen(name); + ms_len = sizeof(*ms) + 1 + CHALLENGE_LEN + sm->server_id_len; req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, ms_len, EAP_CODE_REQUEST, id); if (req == NULL) { @@ -139,7 +132,7 @@ wpabuf_put(req, CHALLENGE_LEN); wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: Challenge", data->auth_challenge, CHALLENGE_LEN); - wpabuf_put_data(req, name, os_strlen(name)); + wpabuf_put_data(req, sm->server_id, sm->server_id_len); return req; } @@ -405,9 +398,12 @@ if (sm->user->password_hash) { pw_hash = sm->user->password; } else { - nt_password_hash(sm->user->password, - sm->user->password_len, - pw_hash_buf); + if (nt_password_hash(sm->user->password, + sm->user->password_len, + pw_hash_buf) < 0) { + data->state = FAILURE; + return; + } pw_hash = pw_hash_buf; } generate_authenticator_response_pwhash( diff -Nru wpa-1.0/src/eap_server/eap_server_pax.c wpa-2.1/src/eap_server/eap_server_pax.c --- wpa-1.0/src/eap_server/eap_server_pax.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/eap_server/eap_server_pax.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * hostapd / EAP-PAX (RFC 4746) server * Copyright (c) 2005-2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff -Nru wpa-1.0/src/eap_server/eap_server_peap.c wpa-2.1/src/eap_server/eap_server_peap.c --- wpa-1.0/src/eap_server/eap_server_peap.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/eap_server/eap_server_peap.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * hostapd / EAP-PEAP (draft-josefsson-pppext-eap-tls-eap-10.txt) * Copyright (c) 2004-2008, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -28,7 +22,6 @@ /* Maximum supported PEAP version * 0 = Microsoft's PEAP version 0; draft-kamath-pppext-peapv0-00.txt * 1 = draft-josefsson-ppext-eap-tls-eap-05.txt - * 2 = draft-josefsson-ppext-eap-tls-eap-10.txt */ #define EAP_PEAP_VERSION 1 @@ -105,33 +98,6 @@ } -static struct wpabuf * eap_peapv2_tlv_eap_payload(struct wpabuf *buf) -{ - struct wpabuf *e; - struct eap_tlv_hdr *tlv; - - if (buf == NULL) - return NULL; - - /* Encapsulate EAP packet in EAP-Payload TLV */ - wpa_printf(MSG_DEBUG, "EAP-PEAPv2: Add EAP-Payload TLV"); - e = wpabuf_alloc(sizeof(*tlv) + wpabuf_len(buf)); - if (e == NULL) { - wpa_printf(MSG_DEBUG, "EAP-PEAPv2: Failed to allocate memory " - "for TLV encapsulation"); - wpabuf_free(buf); - return NULL; - } - tlv = wpabuf_put(e, sizeof(*tlv)); - tlv->tlv_type = host_to_be16(EAP_TLV_TYPE_MANDATORY | - EAP_TLV_EAP_PAYLOAD_TLV); - tlv->length = host_to_be16(wpabuf_len(buf)); - wpabuf_put_buf(e, buf); - wpabuf_free(buf); - return e; -} - - static void eap_peap_req_success(struct eap_sm *sm, struct eap_peap_data *data) { @@ -245,8 +211,6 @@ return NULL; } buf = data->phase2_method->buildReq(sm, data->phase2_priv, id); - if (data->peap_version >= 2 && buf) - buf = eap_peapv2_tlv_eap_payload(buf); if (buf == NULL) return NULL; @@ -431,8 +395,6 @@ len[1] = 1; tlv_type = EAP_TLV_CRYPTO_BINDING_TLV; - if (data->peap_version >= 2) - tlv_type |= EAP_TLV_TYPE_MANDATORY; wpabuf_put_be16(buf, tlv_type); wpabuf_put_be16(buf, 56); @@ -511,8 +473,7 @@ return eap_peap_build_start(sm, data, id); case PHASE1: case PHASE1_ID2: - if (data->peap_version < 2 && - tls_connection_established(sm->ssl_ctx, data->ssl.conn)) { + if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) { wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase1 done, " "starting Phase2"); eap_peap_state(data, PHASE2_START); @@ -1064,8 +1025,6 @@ wpa_hexdump_buf_key(MSG_DEBUG, "EAP-PEAP: Decrypted Phase 2 EAP", in_decrypted); - hdr = wpabuf_head(in_decrypted); - if (data->peap_version == 0 && data->state != PHASE2_TLV) { const struct eap_hdr *resp; struct eap_hdr *nhdr; @@ -1087,47 +1046,6 @@ wpabuf_free(in_decrypted); in_decrypted = nbuf; - } else if (data->peap_version >= 2) { - struct eap_tlv_hdr *tlv; - struct wpabuf *nmsg; - - if (wpabuf_len(in_decrypted) < sizeof(*tlv) + sizeof(*hdr)) { - wpa_printf(MSG_INFO, "EAP-PEAPv2: Too short Phase 2 " - "EAP TLV"); - wpabuf_free(in_decrypted); - return; - } - tlv = wpabuf_mhead(in_decrypted); - if ((be_to_host16(tlv->tlv_type) & EAP_TLV_TYPE_MASK) != - EAP_TLV_EAP_PAYLOAD_TLV) { - wpa_printf(MSG_INFO, "EAP-PEAPv2: Not an EAP TLV"); - wpabuf_free(in_decrypted); - return; - } - if (sizeof(*tlv) + be_to_host16(tlv->length) > - wpabuf_len(in_decrypted)) { - wpa_printf(MSG_INFO, "EAP-PEAPv2: Invalid EAP TLV " - "length"); - wpabuf_free(in_decrypted); - return; - } - hdr = (struct eap_hdr *) (tlv + 1); - if (be_to_host16(hdr->length) > be_to_host16(tlv->length)) { - wpa_printf(MSG_INFO, "EAP-PEAPv2: No room for full " - "EAP packet in EAP TLV"); - wpabuf_free(in_decrypted); - return; - } - - nmsg = wpabuf_alloc(be_to_host16(hdr->length)); - if (nmsg == NULL) { - wpabuf_free(in_decrypted); - return; - } - - wpabuf_put_data(nmsg, hdr, be_to_host16(hdr->length)); - wpabuf_free(in_decrypted); - in_decrypted = nmsg; } hdr = wpabuf_head(in_decrypted); @@ -1176,53 +1094,6 @@ } -static int eap_peapv2_start_phase2(struct eap_sm *sm, - struct eap_peap_data *data) -{ - struct wpabuf *buf, *buf2; - - wpa_printf(MSG_DEBUG, "EAP-PEAPv2: Phase1 done, include first Phase2 " - "payload in the same message"); - eap_peap_state(data, PHASE1_ID2); - if (eap_peap_phase2_init(sm, data, EAP_TYPE_IDENTITY)) - return -1; - - /* TODO: which Id to use here? */ - buf = data->phase2_method->buildReq(sm, data->phase2_priv, 6); - if (buf == NULL) - return -1; - - buf2 = eap_peapv2_tlv_eap_payload(buf); - if (buf2 == NULL) - return -1; - - wpa_hexdump_buf(MSG_DEBUG, "EAP-PEAPv2: Identity Request", buf2); - - buf = tls_connection_encrypt(sm->ssl_ctx, data->ssl.conn, - buf2); - wpabuf_free(buf2); - - if (buf == NULL) { - wpa_printf(MSG_INFO, "EAP-PEAPv2: Failed to encrypt Phase 2 " - "data"); - return -1; - } - - wpa_hexdump_buf(MSG_DEBUG, "EAP-PEAPv2: Encrypted Identity Request", - buf); - - /* Append TLS data into the pending buffer after the Server Finished */ - if (wpabuf_resize(&data->ssl.tls_out, wpabuf_len(buf)) < 0) { - wpabuf_free(buf); - return -1; - } - wpabuf_put_buf(data->ssl.tls_out, buf); - wpabuf_free(buf); - - return 0; -} - - static int eap_peap_process_version(struct eap_sm *sm, void *priv, int peer_version) { @@ -1257,14 +1128,6 @@ eap_peap_state(data, FAILURE); break; } - - if (data->peap_version >= 2 && - tls_connection_established(sm->ssl_ctx, data->ssl.conn)) { - if (eap_peapv2_start_phase2(sm, data)) { - eap_peap_state(data, FAILURE); - break; - } - } break; case PHASE2_START: eap_peap_state(data, PHASE2_ID); diff -Nru wpa-1.0/src/eap_server/eap_server_psk.c wpa-2.1/src/eap_server/eap_server_psk.c --- wpa-1.0/src/eap_server/eap_server_psk.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/eap_server/eap_server_psk.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * hostapd / EAP-PSK (RFC 4764) server * Copyright (c) 2005-2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. * * Note: EAP-PSK is an EAP authentication method and as such, completely * different from WPA-PSK. This file is not needed for WPA-PSK functionality. @@ -28,8 +22,8 @@ enum { PSK_1, PSK_3, SUCCESS, FAILURE } state; u8 rand_s[EAP_PSK_RAND_LEN]; u8 rand_p[EAP_PSK_RAND_LEN]; - u8 *id_p, *id_s; - size_t id_p_len, id_s_len; + u8 *id_p; + size_t id_p_len; u8 ak[EAP_PSK_AK_LEN], kdk[EAP_PSK_KDK_LEN], tek[EAP_PSK_TEK_LEN]; u8 msk[EAP_MSK_LEN]; u8 emsk[EAP_EMSK_LEN]; @@ -44,8 +38,6 @@ if (data == NULL) return NULL; data->state = PSK_1; - data->id_s = (u8 *) "hostapd"; - data->id_s_len = 7; return data; } @@ -76,7 +68,7 @@ data->rand_s, EAP_PSK_RAND_LEN); req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PSK, - sizeof(*psk) + data->id_s_len, + sizeof(*psk) + sm->server_id_len, EAP_CODE_REQUEST, id); if (req == NULL) { wpa_printf(MSG_ERROR, "EAP-PSK: Failed to allocate memory " @@ -88,7 +80,7 @@ psk = wpabuf_put(req, sizeof(*psk)); psk->flags = EAP_PSK_FLAGS_SET_T(0); /* T=0 */ os_memcpy(psk->rand_s, data->rand_s, EAP_PSK_RAND_LEN); - wpabuf_put_data(req, data->id_s, data->id_s_len); + wpabuf_put_data(req, sm->server_id, sm->server_id_len); return req; } @@ -118,13 +110,13 @@ os_memcpy(psk->rand_s, data->rand_s, EAP_PSK_RAND_LEN); /* MAC_S = OMAC1-AES-128(AK, ID_S||RAND_P) */ - buflen = data->id_s_len + EAP_PSK_RAND_LEN; + buflen = sm->server_id_len + EAP_PSK_RAND_LEN; buf = os_malloc(buflen); if (buf == NULL) goto fail; - os_memcpy(buf, data->id_s, data->id_s_len); - os_memcpy(buf + data->id_s_len, data->rand_p, EAP_PSK_RAND_LEN); + os_memcpy(buf, sm->server_id, sm->server_id_len); + os_memcpy(buf + sm->server_id_len, data->rand_p, EAP_PSK_RAND_LEN); if (omac1_aes_128(data->ak, buf, buflen, psk->mac_s)) { os_free(buf); goto fail; @@ -302,7 +294,7 @@ os_memcpy(data->rand_p, resp->rand_p, EAP_PSK_RAND_LEN); /* MAC_P = OMAC1-AES-128(AK, ID_P||ID_S||RAND_S||RAND_P) */ - buflen = data->id_p_len + data->id_s_len + 2 * EAP_PSK_RAND_LEN; + buflen = data->id_p_len + sm->server_id_len + 2 * EAP_PSK_RAND_LEN; buf = os_malloc(buflen); if (buf == NULL) { data->state = FAILURE; @@ -310,8 +302,8 @@ } os_memcpy(buf, data->id_p, data->id_p_len); pos = buf + data->id_p_len; - os_memcpy(pos, data->id_s, data->id_s_len); - pos += data->id_s_len; + os_memcpy(pos, sm->server_id, sm->server_id_len); + pos += sm->server_id_len; os_memcpy(pos, data->rand_s, EAP_PSK_RAND_LEN); pos += EAP_PSK_RAND_LEN; os_memcpy(pos, data->rand_p, EAP_PSK_RAND_LEN); diff -Nru wpa-1.0/src/eap_server/eap_server_pwd.c wpa-2.1/src/eap_server/eap_server_pwd.c --- wpa-1.0/src/eap_server/eap_server_pwd.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/eap_server/eap_server_pwd.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,19 +2,14 @@ * hostapd / EAP-pwd (RFC 5931) server * Copyright (c) 2010, Dan Harkins * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the BSD license. - * - * Alternatively, this software may be distributed under the terms of the - * GNU General Public License version 2 as published by the Free Software - * Foundation. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" #include "common.h" +#include "crypto/sha256.h" #include "eap_server/eap_i.h" #include "eap_common/eap_pwd_common.h" @@ -33,6 +28,12 @@ u16 group_num; EAP_PWD_group *grp; + struct wpabuf *inbuf; + size_t in_frag_pos; + struct wpabuf *outbuf; + size_t out_frag_pos; + size_t mtu; + BIGNUM *k; BIGNUM *private_value; BIGNUM *peer_scalar; @@ -40,7 +41,7 @@ EC_POINT *my_element; EC_POINT *peer_element; - u8 my_confirm[SHA256_DIGEST_LENGTH]; + u8 my_confirm[SHA256_MAC_LEN]; u8 msk[EAP_MSK_LEN]; u8 emsk[EAP_EMSK_LEN]; @@ -120,6 +121,10 @@ return NULL; } + data->in_frag_pos = data->out_frag_pos = 0; + data->inbuf = data->outbuf = NULL; + data->mtu = 1020; /* default from RFC 5931, make it configurable! */ + return data; } @@ -149,44 +154,48 @@ } -static struct wpabuf * -eap_pwd_build_id_req(struct eap_sm *sm, struct eap_pwd_data *data, u8 id) +static void eap_pwd_build_id_req(struct eap_sm *sm, struct eap_pwd_data *data, + u8 id) { - struct wpabuf *req; - wpa_printf(MSG_DEBUG, "EAP-pwd: ID/Request"); - req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PWD, - sizeof(struct eap_pwd_hdr) + - sizeof(struct eap_pwd_id) + data->id_server_len, - EAP_CODE_REQUEST, id); - if (req == NULL) { + /* + * if we're fragmenting then we already have an id request, just return + */ + if (data->out_frag_pos) + return; + + data->outbuf = wpabuf_alloc(sizeof(struct eap_pwd_id) + + data->id_server_len); + if (data->outbuf == NULL) { eap_pwd_state(data, FAILURE); - return NULL; + return; } /* an lfsr is good enough to generate unpredictable tokens */ data->token = os_random(); - wpabuf_put_u8(req, EAP_PWD_OPCODE_ID_EXCH); - wpabuf_put_be16(req, data->group_num); - wpabuf_put_u8(req, EAP_PWD_DEFAULT_RAND_FUNC); - wpabuf_put_u8(req, EAP_PWD_DEFAULT_PRF); - wpabuf_put_data(req, &data->token, sizeof(data->token)); - wpabuf_put_u8(req, EAP_PWD_PREP_NONE); - wpabuf_put_data(req, data->id_server, data->id_server_len); - - return req; + wpabuf_put_be16(data->outbuf, data->group_num); + wpabuf_put_u8(data->outbuf, EAP_PWD_DEFAULT_RAND_FUNC); + wpabuf_put_u8(data->outbuf, EAP_PWD_DEFAULT_PRF); + wpabuf_put_data(data->outbuf, &data->token, sizeof(data->token)); + wpabuf_put_u8(data->outbuf, EAP_PWD_PREP_NONE); + wpabuf_put_data(data->outbuf, data->id_server, data->id_server_len); } -static struct wpabuf * -eap_pwd_build_commit_req(struct eap_sm *sm, struct eap_pwd_data *data, u8 id) +static void eap_pwd_build_commit_req(struct eap_sm *sm, + struct eap_pwd_data *data, u8 id) { - struct wpabuf *req = NULL; BIGNUM *mask = NULL, *x = NULL, *y = NULL; u8 *scalar = NULL, *element = NULL; u16 offset; wpa_printf(MSG_DEBUG, "EAP-pwd: Commit/Request"); + /* + * if we're fragmenting then we already have an commit request, just + * return + */ + if (data->out_frag_pos) + return; if (((data->private_value = BN_new()) == NULL) || ((data->my_element = EC_POINT_new(data->grp->group)) == NULL) || @@ -256,42 +265,42 @@ offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y); BN_bn2bin(y, element + BN_num_bytes(data->grp->prime) + offset); - req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PWD, - sizeof(struct eap_pwd_hdr) + - (2 * BN_num_bytes(data->grp->prime)) + - BN_num_bytes(data->grp->order), - EAP_CODE_REQUEST, id); - if (req == NULL) + data->outbuf = wpabuf_alloc(2 * BN_num_bytes(data->grp->prime) + + BN_num_bytes(data->grp->order)); + if (data->outbuf == NULL) goto fin; - wpabuf_put_u8(req, EAP_PWD_OPCODE_COMMIT_EXCH); /* We send the element as (x,y) followed by the scalar */ - wpabuf_put_data(req, element, (2 * BN_num_bytes(data->grp->prime))); - wpabuf_put_data(req, scalar, BN_num_bytes(data->grp->order)); + wpabuf_put_data(data->outbuf, element, + 2 * BN_num_bytes(data->grp->prime)); + wpabuf_put_data(data->outbuf, scalar, BN_num_bytes(data->grp->order)); fin: os_free(scalar); os_free(element); BN_free(x); BN_free(y); - if (req == NULL) + if (data->outbuf == NULL) eap_pwd_state(data, FAILURE); - - return req; } -static struct wpabuf * -eap_pwd_build_confirm_req(struct eap_sm *sm, struct eap_pwd_data *data, u8 id) +static void eap_pwd_build_confirm_req(struct eap_sm *sm, + struct eap_pwd_data *data, u8 id) { - struct wpabuf *req = NULL; BIGNUM *x = NULL, *y = NULL; - HMAC_CTX ctx; - u8 conf[SHA256_DIGEST_LENGTH], *cruft = NULL, *ptr; + struct crypto_hash *hash; + u8 conf[SHA256_MAC_LEN], *cruft = NULL, *ptr; u16 grp; int offset; wpa_printf(MSG_DEBUG, "EAP-pwd: Confirm/Request"); + /* + * if we're fragmenting then we already have an confirm request, just + * return + */ + if (data->out_frag_pos) + return; /* Each component of the cruft will be at most as big as the prime */ if (((cruft = os_malloc(BN_num_bytes(data->grp->prime))) == NULL) || @@ -305,7 +314,9 @@ * commit is H(k | server_element | server_scalar | peer_element | * peer_scalar | ciphersuite) */ - H_Init(&ctx); + hash = eap_pwd_h_init(); + if (hash == NULL) + goto fin; /* * Zero the memory each time because this is mod prime math and some @@ -316,7 +327,7 @@ os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(data->k); BN_bn2bin(data->k, cruft + offset); - H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime)); + eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); /* server element: x, y */ if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group, @@ -330,18 +341,18 @@ os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x); BN_bn2bin(x, cruft + offset); - H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime)); + eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y); BN_bn2bin(y, cruft + offset); - H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime)); + eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); /* server scalar */ os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); offset = BN_num_bytes(data->grp->order) - BN_num_bytes(data->my_scalar); BN_bn2bin(data->my_scalar, cruft + offset); - H_Update(&ctx, cruft, BN_num_bytes(data->grp->order)); + eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order)); /* peer element: x, y */ if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group, @@ -355,18 +366,18 @@ os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x); BN_bn2bin(x, cruft + offset); - H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime)); + eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y); BN_bn2bin(y, cruft + offset); - H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime)); + eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); /* peer scalar */ os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); offset = BN_num_bytes(data->grp->order) - BN_num_bytes(data->peer_scalar); BN_bn2bin(data->peer_scalar, cruft + offset); - H_Update(&ctx, cruft, BN_num_bytes(data->grp->order)); + eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order)); /* ciphersuite */ grp = htons(data->group_num); @@ -378,29 +389,24 @@ ptr += sizeof(u8); *ptr = EAP_PWD_DEFAULT_PRF; ptr += sizeof(u8); - H_Update(&ctx, cruft, ptr-cruft); + eap_pwd_h_update(hash, cruft, ptr - cruft); /* all done with the random function */ - H_Final(&ctx, conf); - os_memcpy(data->my_confirm, conf, SHA256_DIGEST_LENGTH); + eap_pwd_h_final(hash, conf); + os_memcpy(data->my_confirm, conf, SHA256_MAC_LEN); - req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PWD, - sizeof(struct eap_pwd_hdr) + SHA256_DIGEST_LENGTH, - EAP_CODE_REQUEST, id); - if (req == NULL) + data->outbuf = wpabuf_alloc(SHA256_MAC_LEN); + if (data->outbuf == NULL) goto fin; - wpabuf_put_u8(req, EAP_PWD_OPCODE_CONFIRM_EXCH); - wpabuf_put_data(req, conf, SHA256_DIGEST_LENGTH); + wpabuf_put_data(data->outbuf, conf, SHA256_MAC_LEN); fin: os_free(cruft); BN_free(x); BN_free(y); - if (req == NULL) + if (data->outbuf == NULL) eap_pwd_state(data, FAILURE); - - return req; } @@ -408,21 +414,119 @@ eap_pwd_build_req(struct eap_sm *sm, void *priv, u8 id) { struct eap_pwd_data *data = priv; + struct wpabuf *req; + u8 lm_exch; + const u8 *buf; + u16 totlen = 0; + size_t len; + /* + * if we're buffering response fragments then just ACK + */ + if (data->in_frag_pos) { + wpa_printf(MSG_DEBUG, "EAP-pwd: ACKing a fragment!!"); + req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PWD, + EAP_PWD_HDR_SIZE, EAP_CODE_REQUEST, id); + if (req == NULL) { + eap_pwd_state(data, FAILURE); + return NULL; + } + switch (data->state) { + case PWD_ID_Req: + wpabuf_put_u8(req, EAP_PWD_OPCODE_ID_EXCH); + break; + case PWD_Commit_Req: + wpabuf_put_u8(req, EAP_PWD_OPCODE_COMMIT_EXCH); + break; + case PWD_Confirm_Req: + wpabuf_put_u8(req, EAP_PWD_OPCODE_CONFIRM_EXCH); + break; + default: + eap_pwd_state(data, FAILURE); /* just to be sure */ + wpabuf_free(req); + return NULL; + } + return req; + } + + /* + * build the data portion of a request + */ switch (data->state) { - case PWD_ID_Req: - return eap_pwd_build_id_req(sm, data, id); - case PWD_Commit_Req: - return eap_pwd_build_commit_req(sm, data, id); - case PWD_Confirm_Req: - return eap_pwd_build_confirm_req(sm, data, id); - default: + case PWD_ID_Req: + eap_pwd_build_id_req(sm, data, id); + lm_exch = EAP_PWD_OPCODE_ID_EXCH; + break; + case PWD_Commit_Req: + eap_pwd_build_commit_req(sm, data, id); + lm_exch = EAP_PWD_OPCODE_COMMIT_EXCH; + break; + case PWD_Confirm_Req: + eap_pwd_build_confirm_req(sm, data, id); + lm_exch = EAP_PWD_OPCODE_CONFIRM_EXCH; + break; + default: wpa_printf(MSG_INFO, "EAP-pwd: Unknown state %d in build_req", data->state); + eap_pwd_state(data, FAILURE); + lm_exch = 0; /* hush now, sweet compiler */ break; } - return NULL; + if (data->state == FAILURE) + return NULL; + + /* + * determine whether that data needs to be fragmented + */ + len = wpabuf_len(data->outbuf) - data->out_frag_pos; + if ((len + EAP_PWD_HDR_SIZE) > data->mtu) { + len = data->mtu - EAP_PWD_HDR_SIZE; + EAP_PWD_SET_MORE_BIT(lm_exch); + /* + * if this is the first fragment, need to set the M bit + * and add the total length to the eap_pwd_hdr + */ + if (data->out_frag_pos == 0) { + EAP_PWD_SET_LENGTH_BIT(lm_exch); + totlen = wpabuf_len(data->outbuf) + + EAP_PWD_HDR_SIZE + sizeof(u16); + len -= sizeof(u16); + wpa_printf(MSG_DEBUG, "EAP-pwd: Fragmenting output, " + "total length = %d", totlen); + } + wpa_printf(MSG_DEBUG, "EAP-pwd: Send a %d byte fragment", + (int) len); + } + + /* + * alloc an eap request and populate it with the data + */ + req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PWD, + EAP_PWD_HDR_SIZE + len + + (totlen ? sizeof(u16) : 0), + EAP_CODE_REQUEST, id); + if (req == NULL) { + eap_pwd_state(data, FAILURE); + return NULL; + } + + wpabuf_put_u8(req, lm_exch); + if (EAP_PWD_GET_LENGTH_BIT(lm_exch)) + wpabuf_put_be16(req, totlen); + + buf = wpabuf_head_u8(data->outbuf); + wpabuf_put_data(req, buf + data->out_frag_pos, len); + data->out_frag_pos += len; + /* + * either not fragged or last fragment, either way free up the data + */ + if (data->out_frag_pos >= wpabuf_len(data->outbuf)) { + wpabuf_free(data->outbuf); + data->out_frag_pos = 0; + } + + return req; } @@ -439,17 +543,19 @@ return TRUE; } - wpa_printf(MSG_DEBUG, "EAP-pwd: Received frame: opcode=%d", *pos); + wpa_printf(MSG_DEBUG, "EAP-pwd: Received frame: exch = %d, len = %d", + EAP_PWD_GET_EXCHANGE(*pos), (int) len); - if (data->state == PWD_ID_Req && *pos == EAP_PWD_OPCODE_ID_EXCH) + if (data->state == PWD_ID_Req && + ((EAP_PWD_GET_EXCHANGE(*pos)) == EAP_PWD_OPCODE_ID_EXCH)) return FALSE; if (data->state == PWD_Commit_Req && - *pos == EAP_PWD_OPCODE_COMMIT_EXCH) + ((EAP_PWD_GET_EXCHANGE(*pos)) == EAP_PWD_OPCODE_COMMIT_EXCH)) return FALSE; if (data->state == PWD_Confirm_Req && - *pos == EAP_PWD_OPCODE_CONFIRM_EXCH) + ((EAP_PWD_GET_EXCHANGE(*pos)) == EAP_PWD_OPCODE_CONFIRM_EXCH)) return FALSE; wpa_printf(MSG_INFO, "EAP-pwd: Unexpected opcode=%d in state=%d", @@ -630,10 +736,10 @@ const u8 *payload, size_t payload_len) { BIGNUM *x = NULL, *y = NULL; - HMAC_CTX ctx; + struct crypto_hash *hash; u32 cs; u16 grp; - u8 conf[SHA256_DIGEST_LENGTH], *cruft = NULL, *ptr; + u8 conf[SHA256_MAC_LEN], *cruft = NULL, *ptr; int offset; /* build up the ciphersuite: group | random_function | prf */ @@ -656,13 +762,15 @@ * commit is H(k | peer_element | peer_scalar | server_element | * server_scalar | ciphersuite) */ - H_Init(&ctx); + hash = eap_pwd_h_init(); + if (hash == NULL) + goto fin; /* k */ os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(data->k); BN_bn2bin(data->k, cruft + offset); - H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime)); + eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); /* peer element: x, y */ if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group, @@ -675,18 +783,18 @@ os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x); BN_bn2bin(x, cruft + offset); - H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime)); + eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y); BN_bn2bin(y, cruft + offset); - H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime)); + eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); /* peer scalar */ os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); offset = BN_num_bytes(data->grp->order) - BN_num_bytes(data->peer_scalar); BN_bn2bin(data->peer_scalar, cruft + offset); - H_Update(&ctx, cruft, BN_num_bytes(data->grp->order)); + eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order)); /* server element: x, y */ if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group, @@ -700,28 +808,28 @@ os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x); BN_bn2bin(x, cruft + offset); - H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime)); + eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y); BN_bn2bin(y, cruft + offset); - H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime)); + eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); /* server scalar */ os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); offset = BN_num_bytes(data->grp->order) - BN_num_bytes(data->my_scalar); BN_bn2bin(data->my_scalar, cruft + offset); - H_Update(&ctx, cruft, BN_num_bytes(data->grp->order)); + eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order)); /* ciphersuite */ os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); - H_Update(&ctx, (u8 *)&cs, sizeof(u32)); + eap_pwd_h_update(hash, (u8 *) &cs, sizeof(u32)); /* all done */ - H_Final(&ctx, conf); + eap_pwd_h_final(hash, conf); ptr = (u8 *) payload; - if (os_memcmp(conf, ptr, SHA256_DIGEST_LENGTH)) { + if (os_memcmp(conf, ptr, SHA256_MAC_LEN)) { wpa_printf(MSG_INFO, "EAP-PWD (server): confirm did not " "verify"); goto fin; @@ -748,7 +856,8 @@ struct eap_pwd_data *data = priv; const u8 *pos; size_t len; - u8 exch; + u8 lm_exch; + u16 tot_len; pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PWD, respData, &len); if ((pos == NULL) || (len < 1)) { @@ -758,18 +867,90 @@ return; } - exch = *pos & 0x3f; - switch (exch) { + lm_exch = *pos; + pos++; /* skip over the bits and the exch */ + len--; + + /* + * if we're fragmenting then this should be an ACK with no data, + * just return and continue fragmenting in the "build" section above + */ + if (data->out_frag_pos) { + if (len > 1) + wpa_printf(MSG_INFO, "EAP-pwd: Bad response! " + "Fragmenting but not an ACK"); + else + wpa_printf(MSG_DEBUG, "EAP-pwd: received ACK from " + "peer"); + return; + } + /* + * if we're receiving fragmented packets then we need to buffer... + * + * the first fragment has a total length + */ + if (EAP_PWD_GET_LENGTH_BIT(lm_exch)) { + tot_len = WPA_GET_BE16(pos); + wpa_printf(MSG_DEBUG, "EAP-pwd: Incoming fragments, total " + "length = %d", tot_len); + data->inbuf = wpabuf_alloc(tot_len); + if (data->inbuf == NULL) { + wpa_printf(MSG_INFO, "EAP-pwd: Out of memory to " + "buffer fragments!"); + return; + } + pos += sizeof(u16); + len -= sizeof(u16); + } + /* + * the first and all intermediate fragments have the M bit set + */ + if (EAP_PWD_GET_MORE_BIT(lm_exch)) { + if ((data->in_frag_pos + len) > wpabuf_size(data->inbuf)) { + wpa_printf(MSG_DEBUG, "EAP-pwd: Buffer overflow " + "attack detected! (%d+%d > %d)", + (int) data->in_frag_pos, (int) len, + (int) wpabuf_size(data->inbuf)); + eap_pwd_state(data, FAILURE); + return; + } + wpabuf_put_data(data->inbuf, pos, len); + data->in_frag_pos += len; + wpa_printf(MSG_DEBUG, "EAP-pwd: Got a %d byte fragment", + (int) len); + return; + } + /* + * last fragment won't have the M bit set (but we're obviously + * buffering fragments so that's how we know it's the last) + */ + if (data->in_frag_pos) { + wpabuf_put_data(data->inbuf, pos, len); + data->in_frag_pos += len; + pos = wpabuf_head_u8(data->inbuf); + len = data->in_frag_pos; + wpa_printf(MSG_DEBUG, "EAP-pwd: Last fragment, %d bytes", + (int) len); + } + switch (EAP_PWD_GET_EXCHANGE(lm_exch)) { case EAP_PWD_OPCODE_ID_EXCH: - eap_pwd_process_id_resp(sm, data, pos + 1, len - 1); + eap_pwd_process_id_resp(sm, data, pos, len); break; case EAP_PWD_OPCODE_COMMIT_EXCH: - eap_pwd_process_commit_resp(sm, data, pos + 1, len - 1); + eap_pwd_process_commit_resp(sm, data, pos, len); break; - case EAP_PWD_OPCODE_CONFIRM_EXCH: - eap_pwd_process_confirm_resp(sm, data, pos + 1, len - 1); + case EAP_PWD_OPCODE_CONFIRM_EXCH: + eap_pwd_process_confirm_resp(sm, data, pos, len); break; } + /* + * if we had been buffering fragments, here's a great place + * to clean up + */ + if (data->in_frag_pos) { + wpabuf_free(data->inbuf); + data->in_frag_pos = 0; + } } diff -Nru wpa-1.0/src/eap_server/eap_server_sake.c wpa-2.1/src/eap_server/eap_server_sake.c --- wpa-1.0/src/eap_server/eap_server_sake.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/eap_server/eap_server_sake.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * hostapd / EAP-SAKE (RFC 4763) server * Copyright (c) 2006-2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -33,8 +27,6 @@ u8 session_id; u8 *peerid; size_t peerid_len; - u8 *serverid; - size_t serverid_len; }; @@ -83,11 +75,6 @@ wpa_printf(MSG_DEBUG, "EAP-SAKE: Initialized Session ID %d", data->session_id); - /* TODO: add support for configuring SERVERID */ - data->serverid = (u8 *) os_strdup("hostapd"); - if (data->serverid) - data->serverid_len = os_strlen((char *) data->serverid); - return data; } @@ -95,7 +82,6 @@ static void eap_sake_reset(struct eap_sm *sm, void *priv) { struct eap_sake_data *data = priv; - os_free(data->serverid); os_free(data->peerid); os_free(data); } @@ -137,8 +123,7 @@ wpa_printf(MSG_DEBUG, "EAP-SAKE: Request/Identity"); plen = 4; - if (data->serverid) - plen += 2 + data->serverid_len; + plen += 2 + sm->server_id_len; msg = eap_sake_build_msg(data, id, plen, EAP_SAKE_SUBTYPE_IDENTITY); if (msg == NULL) { data->state = FAILURE; @@ -148,11 +133,9 @@ wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_PERM_ID_REQ"); eap_sake_add_attr(msg, EAP_SAKE_AT_PERM_ID_REQ, NULL, 2); - if (data->serverid) { - wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_SERVERID"); - eap_sake_add_attr(msg, EAP_SAKE_AT_SERVERID, - data->serverid, data->serverid_len); - } + wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_SERVERID"); + eap_sake_add_attr(msg, EAP_SAKE_AT_SERVERID, + sm->server_id, sm->server_id_len); return msg; } @@ -175,9 +158,7 @@ wpa_hexdump(MSG_MSGDUMP, "EAP-SAKE: RAND_S (server rand)", data->rand_s, EAP_SAKE_RAND_LEN); - plen = 2 + EAP_SAKE_RAND_LEN; - if (data->serverid) - plen += 2 + data->serverid_len; + plen = 2 + EAP_SAKE_RAND_LEN + 2 + sm->server_id_len; msg = eap_sake_build_msg(data, id, plen, EAP_SAKE_SUBTYPE_CHALLENGE); if (msg == NULL) { data->state = FAILURE; @@ -188,11 +169,9 @@ eap_sake_add_attr(msg, EAP_SAKE_AT_RAND_S, data->rand_s, EAP_SAKE_RAND_LEN); - if (data->serverid) { - wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_SERVERID"); - eap_sake_add_attr(msg, EAP_SAKE_AT_SERVERID, - data->serverid, data->serverid_len); - } + wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_SERVERID"); + eap_sake_add_attr(msg, EAP_SAKE_AT_SERVERID, + sm->server_id, sm->server_id_len); return msg; } @@ -219,7 +198,7 @@ wpabuf_put_u8(msg, 2 + EAP_SAKE_MIC_LEN); mic = wpabuf_put(msg, EAP_SAKE_MIC_LEN); if (eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p, - data->serverid, data->serverid_len, + sm->server_id, sm->server_id_len, data->peerid, data->peerid_len, 0, wpabuf_head(msg), wpabuf_len(msg), mic, mic)) { @@ -368,7 +347,7 @@ (u8 *) &data->tek, data->msk, data->emsk); eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p, - data->serverid, data->serverid_len, + sm->server_id, sm->server_id_len, data->peerid, data->peerid_len, 1, wpabuf_head(respData), wpabuf_len(respData), attr.mic_p, mic_p); @@ -405,7 +384,7 @@ } eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p, - data->serverid, data->serverid_len, + sm->server_id, sm->server_id_len, data->peerid, data->peerid_len, 1, wpabuf_head(respData), wpabuf_len(respData), attr.mic_p, mic_p); diff -Nru wpa-1.0/src/eap_server/eap_server_sim.c wpa-2.1/src/eap_server/eap_server_sim.c --- wpa-1.0/src/eap_server/eap_server_sim.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/eap_server/eap_server_sim.c 2014-02-04 11:23:35.000000000 +0000 @@ -1,15 +1,9 @@ /* * hostapd / EAP-SIM (RFC 4186) - * Copyright (c) 2005-2008, Jouni Malinen + * Copyright (c) 2005-2012, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -42,6 +36,8 @@ struct eap_sim_reauth *reauth; u16 notification; int use_result_ind; + int start_round; + char permanent[20]; /* Permanent username */ }; @@ -111,17 +107,33 @@ wpa_printf(MSG_DEBUG, "EAP-SIM: Generating Start"); msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, EAP_TYPE_SIM, EAP_SIM_SUBTYPE_START); - if (eap_sim_db_identity_known(sm->eap_sim_db_priv, sm->identity, - sm->identity_len)) { - wpa_printf(MSG_DEBUG, " AT_PERMANENT_ID_REQ"); - eap_sim_msg_add(msg, EAP_SIM_AT_PERMANENT_ID_REQ, 0, NULL, 0); - } else { + data->start_round++; + if (data->start_round == 1) { /* * RFC 4186, Chap. 4.2.4 recommends that identity from EAP is * ignored and the SIM/Start is used to request the identity. */ wpa_printf(MSG_DEBUG, " AT_ANY_ID_REQ"); eap_sim_msg_add(msg, EAP_SIM_AT_ANY_ID_REQ, 0, NULL, 0); + } else if (data->start_round > 3) { + /* Cannot use more than three rounds of Start messages */ + eap_sim_msg_free(msg); + return NULL; + } else if (data->start_round == 0) { + /* + * This is a special case that is used to recover from + * AT_COUNTER_TOO_SMALL during re-authentication. Since we + * already know the identity of the peer, there is no need to + * request any identity in this case. + */ + } else if (sm->identity && sm->identity_len > 0 && + sm->identity[0] == EAP_SIM_REAUTH_ID_PREFIX) { + /* Reauth id may have expired - try fullauth */ + wpa_printf(MSG_DEBUG, " AT_FULLAUTH_ID_REQ"); + eap_sim_msg_add(msg, EAP_SIM_AT_FULLAUTH_ID_REQ, 0, NULL, 0); + } else { + wpa_printf(MSG_DEBUG, " AT_PERMANENT_ID_REQ"); + eap_sim_msg_add(msg, EAP_SIM_AT_PERMANENT_ID_REQ, 0, NULL, 0); } wpa_printf(MSG_DEBUG, " AT_VERSION_LIST"); ver[0] = 0; @@ -139,7 +151,8 @@ os_free(data->next_pseudonym); if (nonce_s == NULL) { data->next_pseudonym = - eap_sim_db_get_next_pseudonym(sm->eap_sim_db_priv, 0); + eap_sim_db_get_next_pseudonym(sm->eap_sim_db_priv, + EAP_SIM_DB_SIM); } else { /* Do not update pseudonym during re-authentication */ data->next_pseudonym = NULL; @@ -147,7 +160,8 @@ os_free(data->next_reauth_id); if (data->counter <= EAP_SIM_MAX_FAST_REAUTHS) { data->next_reauth_id = - eap_sim_db_get_next_reauth_id(sm->eap_sim_db_priv, 0); + eap_sim_db_get_next_reauth_id(sm->eap_sim_db_priv, + EAP_SIM_DB_SIM); } else { wpa_printf(MSG_DEBUG, "EAP-SIM: Max fast re-authentication " "count exceeded - force full authentication"); @@ -332,18 +346,22 @@ static Boolean eap_sim_check(struct eap_sm *sm, void *priv, struct wpabuf *respData) { - struct eap_sim_data *data = priv; const u8 *pos; size_t len; - u8 subtype; pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_SIM, respData, &len); if (pos == NULL || len < 3) { wpa_printf(MSG_INFO, "EAP-SIM: Invalid frame"); return TRUE; } - subtype = *pos; + return FALSE; +} + + +static Boolean eap_sim_unexpected_subtype(struct eap_sim_data *data, + u8 subtype) +{ if (subtype == EAP_SIM_SUBTYPE_CLIENT_ERROR) return FALSE; @@ -397,85 +415,113 @@ struct wpabuf *respData, struct eap_sim_attrs *attr) { - const u8 *identity; size_t identity_len; u8 ver_list[2]; + u8 *new_identity; + char *username; wpa_printf(MSG_DEBUG, "EAP-SIM: Receive start response"); - if (attr->identity) { - os_free(sm->identity); - sm->identity = os_malloc(attr->identity_len); - if (sm->identity) { - os_memcpy(sm->identity, attr->identity, - attr->identity_len); - sm->identity_len = attr->identity_len; - } + if (data->start_round == 0) { + /* + * Special case for AT_COUNTER_TOO_SMALL recovery - no identity + * was requested since we already know it. + */ + goto skip_id_update; } - identity = NULL; - identity_len = 0; + /* + * We always request identity in SIM/Start, so the peer is required to + * have replied with one. + */ + if (!attr->identity || attr->identity_len == 0) { + wpa_printf(MSG_DEBUG, "EAP-SIM: Peer did not provide any " + "identity"); + goto failed; + } + + new_identity = os_malloc(attr->identity_len); + if (new_identity == NULL) + goto failed; + os_free(sm->identity); + sm->identity = new_identity; + os_memcpy(sm->identity, attr->identity, attr->identity_len); + sm->identity_len = attr->identity_len; - if (sm->identity && sm->identity_len > 0 && - sm->identity[0] == EAP_SIM_PERMANENT_PREFIX) { - identity = sm->identity; - identity_len = sm->identity_len; - } else { - identity = eap_sim_db_get_permanent(sm->eap_sim_db_priv, - sm->identity, - sm->identity_len, - &identity_len); - if (identity == NULL) { - data->reauth = eap_sim_db_get_reauth_entry( - sm->eap_sim_db_priv, sm->identity, - sm->identity_len); - if (data->reauth) { - wpa_printf(MSG_DEBUG, "EAP-SIM: Using fast " - "re-authentication"); - identity = data->reauth->identity; - identity_len = data->reauth->identity_len; - data->counter = data->reauth->counter; - os_memcpy(data->mk, data->reauth->mk, - EAP_SIM_MK_LEN); - } + wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM: Identity", + sm->identity, sm->identity_len); + username = sim_get_username(sm->identity, sm->identity_len); + if (username == NULL) + goto failed; + + if (username[0] == EAP_SIM_REAUTH_ID_PREFIX) { + wpa_printf(MSG_DEBUG, "EAP-SIM: Reauth username '%s'", + username); + data->reauth = eap_sim_db_get_reauth_entry( + sm->eap_sim_db_priv, username); + os_free(username); + if (data->reauth == NULL) { + wpa_printf(MSG_DEBUG, "EAP-SIM: Unknown reauth " + "identity - request full auth identity"); + /* Remain in START state for another round */ + return; } - } - - if (identity == NULL) { - wpa_printf(MSG_DEBUG, "EAP-SIM: Could not get proper permanent" - " user name"); - eap_sim_state(data, FAILURE); + wpa_printf(MSG_DEBUG, "EAP-SIM: Using fast re-authentication"); + os_strlcpy(data->permanent, data->reauth->permanent, + sizeof(data->permanent)); + data->counter = data->reauth->counter; + os_memcpy(data->mk, data->reauth->mk, EAP_SIM_MK_LEN); + eap_sim_state(data, REAUTH); return; } - wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM: Identity", - identity, identity_len); - - if (data->reauth) { - eap_sim_state(data, REAUTH); - return; + if (username[0] == EAP_SIM_PSEUDONYM_PREFIX) { + const char *permanent; + wpa_printf(MSG_DEBUG, "EAP-SIM: Pseudonym username '%s'", + username); + permanent = eap_sim_db_get_permanent( + sm->eap_sim_db_priv, username); + os_free(username); + if (permanent == NULL) { + wpa_printf(MSG_DEBUG, "EAP-SIM: Unknown pseudonym " + "identity - request permanent identity"); + /* Remain in START state for another round */ + return; + } + os_strlcpy(data->permanent, permanent, + sizeof(data->permanent)); + } else if (username[0] == EAP_SIM_PERMANENT_PREFIX) { + wpa_printf(MSG_DEBUG, "EAP-SIM: Permanent username '%s'", + username); + os_strlcpy(data->permanent, username, sizeof(data->permanent)); + os_free(username); + } else { + wpa_printf(MSG_DEBUG, "EAP-SIM: Unrecognized username '%s'", + username); + os_free(username); + goto failed; } +skip_id_update: + /* Full authentication */ + if (attr->nonce_mt == NULL || attr->selected_version < 0) { wpa_printf(MSG_DEBUG, "EAP-SIM: Start/Response missing " "required attributes"); - eap_sim_state(data, FAILURE); - return; + goto failed; } if (!eap_sim_supported_ver(data, attr->selected_version)) { wpa_printf(MSG_DEBUG, "EAP-SIM: Peer selected unsupported " "version %d", attr->selected_version); - eap_sim_state(data, FAILURE); - return; + goto failed; } data->counter = 0; /* reset re-auth counter since this is full auth */ data->reauth = NULL; data->num_chal = eap_sim_db_get_gsm_triplets( - sm->eap_sim_db_priv, identity, identity_len, - EAP_SIM_MAX_CHAL, + sm->eap_sim_db_priv, data->permanent, EAP_SIM_MAX_CHAL, (u8 *) data->rand, (u8 *) data->kc, (u8 *) data->sres, sm); if (data->num_chal == EAP_SIM_DB_PENDING) { wpa_printf(MSG_DEBUG, "EAP-SIM: GSM authentication triplets " @@ -486,8 +532,7 @@ if (data->num_chal < 2) { wpa_printf(MSG_INFO, "EAP-SIM: Failed to get GSM " "authentication triplets for the peer"); - eap_sim_state(data, FAILURE); - return; + goto failed; } identity_len = sm->identity_len; @@ -508,6 +553,11 @@ data->emsk); eap_sim_state(data, CHALLENGE); + return; + +failed: + data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; + eap_sim_state(data, NOTIFICATION); } @@ -516,16 +566,14 @@ struct wpabuf *respData, struct eap_sim_attrs *attr) { - const u8 *identity; - size_t identity_len; - if (attr->mac == NULL || eap_sim_verify_mac(data->k_aut, respData, attr->mac, (u8 *) data->sres, data->num_chal * EAP_SIM_SRES_LEN)) { wpa_printf(MSG_WARNING, "EAP-SIM: Challenge message " "did not include valid AT_MAC"); - eap_sim_state(data, FAILURE); + data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; + eap_sim_state(data, NOTIFICATION); return; } @@ -538,22 +586,13 @@ } else eap_sim_state(data, SUCCESS); - identity = eap_sim_db_get_permanent(sm->eap_sim_db_priv, sm->identity, - sm->identity_len, &identity_len); - if (identity == NULL) { - identity = sm->identity; - identity_len = sm->identity_len; - } - if (data->next_pseudonym) { - eap_sim_db_add_pseudonym(sm->eap_sim_db_priv, identity, - identity_len, + eap_sim_db_add_pseudonym(sm->eap_sim_db_priv, data->permanent, data->next_pseudonym); data->next_pseudonym = NULL; } if (data->next_reauth_id) { - eap_sim_db_add_reauth(sm->eap_sim_db_priv, identity, - identity_len, + eap_sim_db_add_reauth(sm->eap_sim_db_priv, data->permanent, data->next_reauth_id, data->counter + 1, data->mk); data->next_reauth_id = NULL; @@ -568,8 +607,6 @@ { struct eap_sim_attrs eattr; u8 *decrypted = NULL; - const u8 *identity, *id2; - size_t identity_len, id2_len; if (attr->mac == NULL || eap_sim_verify_mac(data->k_aut, respData, attr->mac, data->nonce_s, @@ -605,6 +642,16 @@ wpa_printf(MSG_DEBUG, "EAP-SIM: Re-authentication response includes " "the correct AT_MAC"); + + if (eattr.counter_too_small) { + wpa_printf(MSG_DEBUG, "EAP-AKA: Re-authentication response " + "included AT_COUNTER_TOO_SMALL - starting full " + "authentication"); + data->start_round = -1; + eap_sim_state(data, START); + return; + } + if (sm->eap_sim_aka_result_ind && attr->result_ind) { data->use_result_ind = 1; data->notification = EAP_SIM_SUCCESS; @@ -612,24 +659,9 @@ } else eap_sim_state(data, SUCCESS); - if (data->reauth) { - identity = data->reauth->identity; - identity_len = data->reauth->identity_len; - } else { - identity = sm->identity; - identity_len = sm->identity_len; - } - - id2 = eap_sim_db_get_permanent(sm->eap_sim_db_priv, identity, - identity_len, &id2_len); - if (id2) { - identity = id2; - identity_len = id2_len; - } - if (data->next_reauth_id) { - eap_sim_db_add_reauth(sm->eap_sim_db_priv, identity, - identity_len, data->next_reauth_id, + eap_sim_db_add_reauth(sm->eap_sim_db_priv, data->permanent, + data->next_reauth_id, data->counter + 1, data->mk); data->next_reauth_id = NULL; } else { @@ -640,7 +672,8 @@ return; fail: - eap_sim_state(data, FAILURE); + data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; + eap_sim_state(data, NOTIFICATION); eap_sim_db_remove_reauth(sm->eap_sim_db_priv, data->reauth); data->reauth = NULL; os_free(decrypted); @@ -691,8 +724,24 @@ subtype = *pos; pos += 3; + if (eap_sim_unexpected_subtype(data, subtype)) { + wpa_printf(MSG_DEBUG, "EAP-SIM: Unrecognized or unexpected " + "EAP-SIM Subtype in EAP Response"); + data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; + eap_sim_state(data, NOTIFICATION); + return; + } + if (eap_sim_parse_attr(pos, end, &attr, 0, 0)) { wpa_printf(MSG_DEBUG, "EAP-SIM: Failed to parse attributes"); + if (subtype != EAP_SIM_SUBTYPE_CLIENT_ERROR && + (data->state == START || data->state == CHALLENGE || + data->state == REAUTH)) { + data->notification = + EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; + eap_sim_state(data, NOTIFICATION); + return; + } eap_sim_state(data, FAILURE); return; } diff -Nru wpa-1.0/src/eap_server/eap_server_tls.c wpa-2.1/src/eap_server/eap_server_tls.c --- wpa-1.0/src/eap_server/eap_server_tls.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/eap_server/eap_server_tls.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * hostapd / EAP-TLS (RFC 2716) * Copyright (c) 2004-2008, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -27,6 +21,7 @@ struct eap_ssl_data ssl; enum { START, CONTINUE, SUCCESS, FAILURE } state; int established; + u8 eap_type; }; @@ -71,10 +66,34 @@ return NULL; } + data->eap_type = EAP_TYPE_TLS; + return data; } +#ifdef EAP_SERVER_UNAUTH_TLS +static void * eap_unauth_tls_init(struct eap_sm *sm) +{ + struct eap_tls_data *data; + + data = os_zalloc(sizeof(*data)); + if (data == NULL) + return NULL; + data->state = START; + + if (eap_server_tls_ssl_init(sm, &data->ssl, 0)) { + wpa_printf(MSG_INFO, "EAP-TLS: Failed to initialize SSL."); + eap_tls_reset(sm, data); + return NULL; + } + + data->eap_type = EAP_UNAUTH_TLS_TYPE; + return data; +} +#endif /* EAP_SERVER_UNAUTH_TLS */ + + static void eap_tls_reset(struct eap_sm *sm, void *priv) { struct eap_tls_data *data = priv; @@ -90,8 +109,7 @@ { struct wpabuf *req; - req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TLS, 1, EAP_CODE_REQUEST, - id); + req = eap_tls_msg_alloc(data->eap_type, 1, EAP_CODE_REQUEST, id); if (req == NULL) { wpa_printf(MSG_ERROR, "EAP-TLS: Failed to allocate memory for " "request"); @@ -113,11 +131,11 @@ struct wpabuf *res; if (data->ssl.state == FRAG_ACK) { - return eap_server_tls_build_ack(id, EAP_TYPE_TLS, 0); + return eap_server_tls_build_ack(id, data->eap_type, 0); } if (data->ssl.state == WAIT_FRAG_ACK) { - res = eap_server_tls_build_msg(&data->ssl, EAP_TYPE_TLS, 0, + res = eap_server_tls_build_msg(&data->ssl, data->eap_type, 0, id); goto check_established; } @@ -135,7 +153,7 @@ return NULL; } - res = eap_server_tls_build_msg(&data->ssl, EAP_TYPE_TLS, 0, id); + res = eap_server_tls_build_msg(&data->ssl, data->eap_type, 0, id); check_established: if (data->established && data->ssl.state != WAIT_FRAG_ACK) { @@ -152,10 +170,17 @@ static Boolean eap_tls_check(struct eap_sm *sm, void *priv, struct wpabuf *respData) { + struct eap_tls_data *data = priv; const u8 *pos; size_t len; - pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_TLS, respData, &len); + if (data->eap_type == EAP_UNAUTH_TLS_TYPE) + pos = eap_hdr_validate(EAP_VENDOR_UNAUTH_TLS, + EAP_VENDOR_TYPE_UNAUTH_TLS, respData, + &len); + else + pos = eap_hdr_validate(EAP_VENDOR_IETF, data->eap_type, + respData, &len); if (pos == NULL || len < 1) { wpa_printf(MSG_INFO, "EAP-TLS: Invalid frame"); return TRUE; @@ -184,7 +209,7 @@ { struct eap_tls_data *data = priv; if (eap_server_tls_process(sm, &data->ssl, respData, data, - EAP_TYPE_TLS, NULL, eap_tls_process_msg) < + data->eap_type, NULL, eap_tls_process_msg) < 0) eap_tls_state(data, FAILURE); } @@ -284,3 +309,34 @@ eap_server_method_free(eap); return ret; } + + +#ifdef EAP_SERVER_UNAUTH_TLS +int eap_server_unauth_tls_register(void) +{ + struct eap_method *eap; + int ret; + + eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, + EAP_VENDOR_UNAUTH_TLS, + EAP_VENDOR_TYPE_UNAUTH_TLS, + "UNAUTH-TLS"); + if (eap == NULL) + return -1; + + eap->init = eap_unauth_tls_init; + eap->reset = eap_tls_reset; + eap->buildReq = eap_tls_buildReq; + eap->check = eap_tls_check; + eap->process = eap_tls_process; + eap->isDone = eap_tls_isDone; + eap->getKey = eap_tls_getKey; + eap->isSuccess = eap_tls_isSuccess; + eap->get_emsk = eap_tls_get_emsk; + + ret = eap_server_method_register(eap); + if (ret) + eap_server_method_free(eap); + return ret; +} +#endif /* EAP_SERVER_UNAUTH_TLS */ diff -Nru wpa-1.0/src/eap_server/eap_server_tls_common.c wpa-2.1/src/eap_server/eap_server_tls_common.c --- wpa-1.0/src/eap_server/eap_server_tls_common.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/eap_server/eap_server_tls_common.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * EAP-TLS/PEAP/TTLS/FAST server common functions * Copyright (c) 2004-2009, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -24,9 +18,26 @@ static void eap_server_tls_free_in_buf(struct eap_ssl_data *data); +struct wpabuf * eap_tls_msg_alloc(EapType type, size_t payload_len, + u8 code, u8 identifier) +{ + if (type == EAP_UNAUTH_TLS_TYPE) + return eap_msg_alloc(EAP_VENDOR_UNAUTH_TLS, + EAP_VENDOR_TYPE_UNAUTH_TLS, payload_len, + code, identifier); + return eap_msg_alloc(EAP_VENDOR_IETF, type, payload_len, code, + identifier); +} + + int eap_server_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data, int verify_peer) { + if (sm->ssl_ctx == NULL) { + wpa_printf(MSG_ERROR, "TLS context not initialized - cannot use TLS-based EAP method"); + return -1; + } + data->eap = sm; data->phase2 = sm->init_phase2; @@ -94,9 +105,9 @@ os_memcpy(rnd + keys.client_random_len, keys.server_random, keys.server_random_len); - if (tls_prf(keys.master_key, keys.master_key_len, - label, rnd, keys.client_random_len + - keys.server_random_len, out, len)) + if (tls_prf_sha1_md5(keys.master_key, keys.master_key_len, + label, rnd, keys.client_random_len + + keys.server_random_len, out, len)) goto fail; os_free(rnd); @@ -137,8 +148,7 @@ if (flags & EAP_TLS_FLAGS_LENGTH_INCLUDED) plen += 4; - req = eap_msg_alloc(EAP_VENDOR_IETF, eap_type, plen, - EAP_CODE_REQUEST, id); + req = eap_tls_msg_alloc(eap_type, plen, EAP_CODE_REQUEST, id); if (req == NULL) return NULL; @@ -174,8 +184,7 @@ { struct wpabuf *req; - req = eap_msg_alloc(EAP_VENDOR_IETF, eap_type, 1, EAP_CODE_REQUEST, - id); + req = eap_tls_msg_alloc(eap_type, 1, EAP_CODE_REQUEST, id); if (req == NULL) return NULL; wpa_printf(MSG_DEBUG, "SSL: Building ACK"); @@ -224,6 +233,14 @@ return -1; } + if (len > message_length) { + wpa_printf(MSG_INFO, "SSL: Too much data (%d bytes) in " + "first fragment of frame (TLS Message " + "Length %d bytes)", + (int) len, (int) message_length); + return -1; + } + data->tls_in = wpabuf_alloc(message_length); if (data->tls_in == NULL) { wpa_printf(MSG_DEBUG, "SSL: No memory for message"); @@ -285,6 +302,13 @@ tls_msg_len); *pos += 4; *left -= 4; + + if (*left > tls_msg_len) { + wpa_printf(MSG_INFO, "SSL: TLS Message Length (%d " + "bytes) smaller than this fragment (%d " + "bytes)", (int) tls_msg_len, (int) *left); + return -1; + } } wpa_printf(MSG_DEBUG, "SSL: Received packet: Flags 0x%x " @@ -365,7 +389,13 @@ size_t left; int ret, res = 0; - pos = eap_hdr_validate(EAP_VENDOR_IETF, eap_type, respData, &left); + if (eap_type == EAP_UNAUTH_TLS_TYPE) + pos = eap_hdr_validate(EAP_VENDOR_UNAUTH_TLS, + EAP_VENDOR_TYPE_UNAUTH_TLS, respData, + &left); + else + pos = eap_hdr_validate(EAP_VENDOR_IETF, eap_type, respData, + &left); if (pos == NULL || left < 1) return 0; /* Should not happen - frame already validated */ flags = *pos++; diff -Nru wpa-1.0/src/eap_server/eap_server_tnc.c wpa-2.1/src/eap_server/eap_server_tnc.c --- wpa-1.0/src/eap_server/eap_server_tnc.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/eap_server/eap_server_tnc.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,20 +2,13 @@ * EAP server method: EAP-TNC (Trusted Network Connect) * Copyright (c) 2007-2010, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" #include "common.h" -#include "base64.h" #include "eap_i.h" #include "tncs.h" diff -Nru wpa-1.0/src/eap_server/eap_server_ttls.c wpa-2.1/src/eap_server/eap_server_ttls.c --- wpa-1.0/src/eap_server/eap_server_ttls.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/eap_server/eap_server_ttls.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * hostapd / EAP-TTLS (RFC 5281) * Copyright (c) 2004-2011, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -680,6 +674,13 @@ return; } + if (sm->identity == NULL) { + wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: No user identity " + "known"); + eap_ttls_state(data, FAILURE); + return; + } + /* MSCHAPv2 does not include optional domain name in the * challenge-response calculation, so remove domain prefix * (if present). */ @@ -985,11 +986,12 @@ if (parse.user_name) { os_free(sm->identity); sm->identity = os_malloc(parse.user_name_len); - if (sm->identity) { - os_memcpy(sm->identity, parse.user_name, - parse.user_name_len); - sm->identity_len = parse.user_name_len; + if (sm->identity == NULL) { + eap_ttls_state(data, FAILURE); + goto done; } + os_memcpy(sm->identity, parse.user_name, parse.user_name_len); + sm->identity_len = parse.user_name_len; if (eap_user_get(sm, parse.user_name, parse.user_name_len, 1) != 0) { wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase2 Identity not " diff -Nru wpa-1.0/src/eap_server/eap_server_vendor_test.c wpa-2.1/src/eap_server/eap_server_vendor_test.c --- wpa-1.0/src/eap_server/eap_server_vendor_test.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/eap_server/eap_server_vendor_test.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * hostapd / Test method for vendor specific (expanded) EAP type * Copyright (c) 2005-2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -18,7 +12,7 @@ #include "eap_i.h" -#define EAP_VENDOR_ID 0xfffefd +#define EAP_VENDOR_ID EAP_VENDOR_HOSTAP #define EAP_VENDOR_TYPE 0xfcfbfaf9 diff -Nru wpa-1.0/src/eap_server/eap_server_wsc.c wpa-2.1/src/eap_server/eap_server_wsc.c --- wpa-1.0/src/eap_server/eap_server_wsc.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/eap_server/eap_server_wsc.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * EAP-WSC server for Wi-Fi Protected Setup * Copyright (c) 2007-2008, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff -Nru wpa-1.0/src/eap_server/eap_sim_db.c wpa-2.1/src/eap_server/eap_sim_db.c --- wpa-1.0/src/eap_server/eap_sim_db.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/eap_server/eap_sim_db.c 2014-02-04 11:23:35.000000000 +0000 @@ -1,15 +1,9 @@ /* * hostapd / EAP-SIM database/authenticator gateway - * Copyright (c) 2005-2007, Jouni Malinen + * Copyright (c) 2005-2010, 2012, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. * * This is an example implementation of the EAP-SIM/AKA database/authentication * gateway interface that is using an external program as an SS7 gateway to @@ -23,6 +17,9 @@ #include "includes.h" #include +#ifdef CONFIG_SQLITE +#include +#endif /* CONFIG_SQLITE */ #include "common.h" #include "crypto/random.h" @@ -32,18 +29,15 @@ struct eap_sim_pseudonym { struct eap_sim_pseudonym *next; - u8 *identity; - size_t identity_len; - char *pseudonym; + char *permanent; /* permanent username */ + char *pseudonym; /* pseudonym username */ }; struct eap_sim_db_pending { struct eap_sim_db_pending *next; - u8 imsi[20]; - size_t imsi_len; + char imsi[20]; enum { PENDING, SUCCESS, FAILURE } state; void *cb_session_ctx; - struct os_time timestamp; int aka; union { struct { @@ -72,19 +66,316 @@ struct eap_sim_pseudonym *pseudonyms; struct eap_sim_reauth *reauths; struct eap_sim_db_pending *pending; +#ifdef CONFIG_SQLITE + sqlite3 *sqlite_db; + char db_tmp_identity[100]; + char db_tmp_pseudonym_str[100]; + struct eap_sim_pseudonym db_tmp_pseudonym; + struct eap_sim_reauth db_tmp_reauth; +#endif /* CONFIG_SQLITE */ }; +#ifdef CONFIG_SQLITE + +static int db_table_exists(sqlite3 *db, const char *name) +{ + char cmd[128]; + os_snprintf(cmd, sizeof(cmd), "SELECT 1 FROM %s;", name); + return sqlite3_exec(db, cmd, NULL, NULL, NULL) == SQLITE_OK; +} + + +static int db_table_create_pseudonym(sqlite3 *db) +{ + char *err = NULL; + const char *sql = + "CREATE TABLE pseudonyms(" + " permanent CHAR(21) PRIMARY KEY," + " pseudonym CHAR(21) NOT NULL" + ");"; + + wpa_printf(MSG_DEBUG, "EAP-SIM DB: Adding database table for " + "pseudonym information"); + if (sqlite3_exec(db, sql, NULL, NULL, &err) != SQLITE_OK) { + wpa_printf(MSG_ERROR, "EAP-SIM DB: SQLite error: %s", err); + sqlite3_free(err); + return -1; + } + + return 0; +} + + +static int db_table_create_reauth(sqlite3 *db) +{ + char *err = NULL; + const char *sql = + "CREATE TABLE reauth(" + " permanent CHAR(21) PRIMARY KEY," + " reauth_id CHAR(21) NOT NULL," + " counter INTEGER," + " mk CHAR(40)," + " k_encr CHAR(32)," + " k_aut CHAR(64)," + " k_re CHAR(64)" + ");"; + + wpa_printf(MSG_DEBUG, "EAP-SIM DB: Adding database table for " + "reauth information"); + if (sqlite3_exec(db, sql, NULL, NULL, &err) != SQLITE_OK) { + wpa_printf(MSG_ERROR, "EAP-SIM DB: SQLite error: %s", err); + sqlite3_free(err); + return -1; + } + + return 0; +} + + +static sqlite3 * db_open(const char *db_file) +{ + sqlite3 *db; + + if (sqlite3_open(db_file, &db)) { + wpa_printf(MSG_ERROR, "EAP-SIM DB: Failed to open database " + "%s: %s", db_file, sqlite3_errmsg(db)); + sqlite3_close(db); + return NULL; + } + + if (!db_table_exists(db, "pseudonyms") && + db_table_create_pseudonym(db) < 0) { + sqlite3_close(db); + return NULL; + } + + if (!db_table_exists(db, "reauth") && + db_table_create_reauth(db) < 0) { + sqlite3_close(db); + return NULL; + } + + return db; +} + + +static int valid_db_string(const char *str) +{ + const char *pos = str; + while (*pos) { + if ((*pos < '0' || *pos > '9') && + (*pos < 'a' || *pos > 'f')) + return 0; + pos++; + } + return 1; +} + + +static int db_add_pseudonym(struct eap_sim_db_data *data, + const char *permanent, char *pseudonym) +{ + char cmd[128]; + char *err = NULL; + + if (!valid_db_string(permanent) || !valid_db_string(pseudonym)) { + os_free(pseudonym); + return -1; + } + + os_snprintf(cmd, sizeof(cmd), "INSERT OR REPLACE INTO pseudonyms " + "(permanent, pseudonym) VALUES ('%s', '%s');", + permanent, pseudonym); + os_free(pseudonym); + if (sqlite3_exec(data->sqlite_db, cmd, NULL, NULL, &err) != SQLITE_OK) + { + wpa_printf(MSG_ERROR, "EAP-SIM DB: SQLite error: %s", err); + sqlite3_free(err); + return -1; + } + + return 0; +} + + +static int get_pseudonym_cb(void *ctx, int argc, char *argv[], char *col[]) +{ + struct eap_sim_db_data *data = ctx; + int i; + + for (i = 0; i < argc; i++) { + if (os_strcmp(col[i], "permanent") == 0 && argv[i]) { + os_strlcpy(data->db_tmp_identity, argv[i], + sizeof(data->db_tmp_identity)); + } + } + + return 0; +} + + +static char * +db_get_pseudonym(struct eap_sim_db_data *data, const char *pseudonym) +{ + char cmd[128]; + + if (!valid_db_string(pseudonym)) + return NULL; + os_memset(&data->db_tmp_identity, 0, sizeof(data->db_tmp_identity)); + os_snprintf(cmd, sizeof(cmd), + "SELECT permanent FROM pseudonyms WHERE pseudonym='%s';", + pseudonym); + if (sqlite3_exec(data->sqlite_db, cmd, get_pseudonym_cb, data, NULL) != + SQLITE_OK) + return NULL; + if (data->db_tmp_identity[0] == '\0') + return NULL; + return data->db_tmp_identity; +} + + +static int db_add_reauth(struct eap_sim_db_data *data, const char *permanent, + char *reauth_id, u16 counter, const u8 *mk, + const u8 *k_encr, const u8 *k_aut, const u8 *k_re) +{ + char cmd[2000], *pos, *end; + char *err = NULL; + + if (!valid_db_string(permanent) || !valid_db_string(reauth_id)) { + os_free(reauth_id); + return -1; + } + + pos = cmd; + end = pos + sizeof(cmd); + pos += os_snprintf(pos, end - pos, "INSERT OR REPLACE INTO reauth " + "(permanent, reauth_id, counter%s%s%s%s) " + "VALUES ('%s', '%s', %u", + mk ? ", mk" : "", + k_encr ? ", k_encr" : "", + k_aut ? ", k_aut" : "", + k_re ? ", k_re" : "", + permanent, reauth_id, counter); + os_free(reauth_id); + + if (mk) { + pos += os_snprintf(pos, end - pos, ", '"); + pos += wpa_snprintf_hex(pos, end - pos, mk, EAP_SIM_MK_LEN); + pos += os_snprintf(pos, end - pos, "'"); + } + + if (k_encr) { + pos += os_snprintf(pos, end - pos, ", '"); + pos += wpa_snprintf_hex(pos, end - pos, k_encr, + EAP_SIM_K_ENCR_LEN); + pos += os_snprintf(pos, end - pos, "'"); + } + + if (k_aut) { + pos += os_snprintf(pos, end - pos, ", '"); + pos += wpa_snprintf_hex(pos, end - pos, k_aut, + EAP_AKA_PRIME_K_AUT_LEN); + pos += os_snprintf(pos, end - pos, "'"); + } + + if (k_re) { + pos += os_snprintf(pos, end - pos, ", '"); + pos += wpa_snprintf_hex(pos, end - pos, k_re, + EAP_AKA_PRIME_K_RE_LEN); + pos += os_snprintf(pos, end - pos, "'"); + } + + os_snprintf(pos, end - pos, ");"); + + if (sqlite3_exec(data->sqlite_db, cmd, NULL, NULL, &err) != SQLITE_OK) + { + wpa_printf(MSG_ERROR, "EAP-SIM DB: SQLite error: %s", err); + sqlite3_free(err); + return -1; + } + + return 0; +} + + +static int get_reauth_cb(void *ctx, int argc, char *argv[], char *col[]) +{ + struct eap_sim_db_data *data = ctx; + int i; + struct eap_sim_reauth *reauth = &data->db_tmp_reauth; + + for (i = 0; i < argc; i++) { + if (os_strcmp(col[i], "permanent") == 0 && argv[i]) { + os_strlcpy(data->db_tmp_identity, argv[i], + sizeof(data->db_tmp_identity)); + reauth->permanent = data->db_tmp_identity; + } else if (os_strcmp(col[i], "counter") == 0 && argv[i]) { + reauth->counter = atoi(argv[i]); + } else if (os_strcmp(col[i], "mk") == 0 && argv[i]) { + hexstr2bin(argv[i], reauth->mk, sizeof(reauth->mk)); + } else if (os_strcmp(col[i], "k_encr") == 0 && argv[i]) { + hexstr2bin(argv[i], reauth->k_encr, + sizeof(reauth->k_encr)); + } else if (os_strcmp(col[i], "k_aut") == 0 && argv[i]) { + hexstr2bin(argv[i], reauth->k_aut, + sizeof(reauth->k_aut)); + } else if (os_strcmp(col[i], "k_re") == 0 && argv[i]) { + hexstr2bin(argv[i], reauth->k_re, + sizeof(reauth->k_re)); + } + } + + return 0; +} + + +static struct eap_sim_reauth * +db_get_reauth(struct eap_sim_db_data *data, const char *reauth_id) +{ + char cmd[256]; + + if (!valid_db_string(reauth_id)) + return NULL; + os_memset(&data->db_tmp_reauth, 0, sizeof(data->db_tmp_reauth)); + os_strlcpy(data->db_tmp_pseudonym_str, reauth_id, + sizeof(data->db_tmp_pseudonym_str)); + data->db_tmp_reauth.reauth_id = data->db_tmp_pseudonym_str; + os_snprintf(cmd, sizeof(cmd), + "SELECT * FROM reauth WHERE reauth_id='%s';", reauth_id); + if (sqlite3_exec(data->sqlite_db, cmd, get_reauth_cb, data, NULL) != + SQLITE_OK) + return NULL; + if (data->db_tmp_reauth.permanent == NULL) + return NULL; + return &data->db_tmp_reauth; +} + + +static void db_remove_reauth(struct eap_sim_db_data *data, + struct eap_sim_reauth *reauth) +{ + char cmd[256]; + + if (!valid_db_string(reauth->permanent)) + return; + os_snprintf(cmd, sizeof(cmd), + "DELETE FROM reauth WHERE permanent='%s';", + reauth->permanent); + sqlite3_exec(data->sqlite_db, cmd, NULL, NULL, NULL); +} + +#endif /* CONFIG_SQLITE */ + + static struct eap_sim_db_pending * -eap_sim_db_get_pending(struct eap_sim_db_data *data, const u8 *imsi, - size_t imsi_len, int aka) +eap_sim_db_get_pending(struct eap_sim_db_data *data, const char *imsi, int aka) { struct eap_sim_db_pending *entry, *prev = NULL; entry = data->pending; while (entry) { - if (entry->aka == aka && entry->imsi_len == imsi_len && - os_memcmp(entry->imsi, imsi, imsi_len) == 0) { + if (entry->aka == aka && os_strcmp(entry->imsi, imsi) == 0) { if (prev) prev->next = entry->next; else @@ -119,7 +410,7 @@ * (IMSI = ASCII string, Kc/SRES/RAND = hex string) */ - entry = eap_sim_db_get_pending(data, (u8 *) imsi, os_strlen(imsi), 0); + entry = eap_sim_db_get_pending(data, imsi, 0); if (entry == NULL) { wpa_printf(MSG_DEBUG, "EAP-SIM DB: No pending entry for the " "received message found"); @@ -197,7 +488,7 @@ * (IMSI = ASCII string, RAND/AUTN/IK/CK/RES = hex string) */ - entry = eap_sim_db_get_pending(data, (u8 *) imsi, os_strlen(imsi), 1); + entry = eap_sim_db_get_pending(data, imsi, 1); if (entry == NULL) { wpa_printf(MSG_DEBUG, "EAP-SIM DB: No pending entry for the " "received message found"); @@ -338,7 +629,7 @@ data->sock = socket(PF_UNIX, SOCK_DGRAM, 0); if (data->sock < 0) { - perror("socket(eap_sim_db)"); + wpa_printf(MSG_INFO, "socket(eap_sim_db): %s", strerror(errno)); return -1; } @@ -346,9 +637,10 @@ addr.sun_family = AF_UNIX; os_snprintf(addr.sun_path, sizeof(addr.sun_path), "/tmp/eap_sim_db_%d-%d", getpid(), counter++); + os_free(data->local_sock); data->local_sock = os_strdup(addr.sun_path); if (bind(data->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - perror("bind(eap_sim_db)"); + wpa_printf(MSG_INFO, "bind(eap_sim_db): %s", strerror(errno)); close(data->sock); data->sock = -1; return -1; @@ -358,7 +650,8 @@ addr.sun_family = AF_UNIX; os_strlcpy(addr.sun_path, data->fname + 5, sizeof(addr.sun_path)); if (connect(data->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - perror("connect(eap_sim_db)"); + wpa_printf(MSG_INFO, "connect(eap_sim_db): %s", + strerror(errno)); wpa_hexdump_ascii(MSG_INFO, "HLR/AuC GW socket", (u8 *) addr.sun_path, os_strlen(addr.sun_path)); @@ -395,11 +688,13 @@ * @ctx: Context pointer for get_complete_cb * Returns: Pointer to a private data structure or %NULL on failure */ -void * eap_sim_db_init(const char *config, - void (*get_complete_cb)(void *ctx, void *session_ctx), - void *ctx) +struct eap_sim_db_data * +eap_sim_db_init(const char *config, + void (*get_complete_cb)(void *ctx, void *session_ctx), + void *ctx) { struct eap_sim_db_data *data; + char *pos; data = os_zalloc(sizeof(*data)); if (data == NULL) @@ -411,10 +706,23 @@ data->fname = os_strdup(config); if (data->fname == NULL) goto fail; + pos = os_strstr(data->fname, " db="); + if (pos) { + *pos = '\0'; +#ifdef CONFIG_SQLITE + pos += 4; + data->sqlite_db = db_open(pos); + if (data->sqlite_db == NULL) + goto fail; +#endif /* CONFIG_SQLITE */ + } if (os_strncmp(data->fname, "unix:", 5) == 0) { - if (eap_sim_db_open_socket(data)) - goto fail; + if (eap_sim_db_open_socket(data)) { + wpa_printf(MSG_DEBUG, "EAP-SIM DB: External database " + "connection not available - will retry " + "later"); + } } return data; @@ -429,7 +737,7 @@ static void eap_sim_db_free_pseudonym(struct eap_sim_pseudonym *p) { - os_free(p->identity); + os_free(p->permanent); os_free(p->pseudonym); os_free(p); } @@ -437,7 +745,7 @@ static void eap_sim_db_free_reauth(struct eap_sim_reauth *r) { - os_free(r->identity); + os_free(r->permanent); os_free(r->reauth_id); os_free(r); } @@ -454,6 +762,13 @@ struct eap_sim_reauth *r, *prevr; struct eap_sim_db_pending *pending, *prev_pending; +#ifdef CONFIG_SQLITE + if (data->sqlite_db) { + sqlite3_close(data->sqlite_db); + data->sqlite_db = NULL; + } +#endif /* CONFIG_SQLITE */ + eap_sim_db_close_socket(data); os_free(data->fname); @@ -489,7 +804,8 @@ if (send(data->sock, msg, len, 0) < 0) { _errno = errno; - perror("send[EAP-SIM DB UNIX]"); + wpa_printf(MSG_INFO, "send[EAP-SIM DB UNIX]: %s", + strerror(errno)); } if (_errno == ENOTCONN || _errno == EDESTADDRREQ || _errno == EINVAL || @@ -501,7 +817,8 @@ wpa_printf(MSG_DEBUG, "EAP-SIM DB: Reconnected to the " "external server"); if (send(data->sock, msg, len, 0) < 0) { - perror("send[EAP-SIM DB UNIX]"); + wpa_printf(MSG_INFO, "send[EAP-SIM DB UNIX]: %s", + strerror(errno)); return -1; } } @@ -520,9 +837,8 @@ /** * eap_sim_db_get_gsm_triplets - Get GSM triplets - * @priv: Private data pointer from eap_sim_db_init() - * @identity: User name identity - * @identity_len: Length of identity in bytes + * @data: Private data pointer from eap_sim_db_init() + * @username: Permanent username (prefix | IMSI) * @max_chal: Maximum number of triplets * @_rand: Buffer for RAND values * @kc: Buffer for Kc values @@ -534,9 +850,6 @@ * callback function registered with eap_sim_db_init() will be called once the * results become available. * - * In most cases, the user name is '1' | IMSI, i.e., 1 followed by the IMSI in - * ASCII format. - * * When using an external server for GSM triplets, this function can always * start a request and return EAP_SIM_DB_PENDING immediately if authentication * triplets are not available. Once the triplets are received, callback @@ -545,39 +858,28 @@ * function will then be called again and the newly received triplets will then * be given to the caller. */ -int eap_sim_db_get_gsm_triplets(void *priv, const u8 *identity, - size_t identity_len, int max_chal, +int eap_sim_db_get_gsm_triplets(struct eap_sim_db_data *data, + const char *username, int max_chal, u8 *_rand, u8 *kc, u8 *sres, void *cb_session_ctx) { - struct eap_sim_db_data *data = priv; struct eap_sim_db_pending *entry; int len, ret; - size_t i; char msg[40]; + const char *imsi; + size_t imsi_len; - if (identity_len < 2 || identity[0] != EAP_SIM_PERMANENT_PREFIX) { - wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity", - identity, identity_len); - return EAP_SIM_DB_FAILURE; - } - identity++; - identity_len--; - for (i = 0; i < identity_len; i++) { - if (identity[i] == '@') { - identity_len = i; - break; - } - } - if (identity_len + 1 > sizeof(entry->imsi)) { - wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity", - identity, identity_len); + if (username == NULL || username[0] != EAP_SIM_PERMANENT_PREFIX || + username[1] == '\0' || os_strlen(username) > sizeof(entry->imsi)) { + wpa_printf(MSG_DEBUG, "EAP-SIM DB: unexpected username '%s'", + username); return EAP_SIM_DB_FAILURE; } - wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: Get GSM triplets for IMSI", - identity, identity_len); + imsi = username + 1; + wpa_printf(MSG_DEBUG, "EAP-SIM DB: Get GSM triplets for IMSI '%s'", + imsi); - entry = eap_sim_db_get_pending(data, identity, identity_len, 0); + entry = eap_sim_db_get_pending(data, imsi, 0); if (entry) { int num_chal; if (entry->state == FAILURE) { @@ -612,18 +914,19 @@ return EAP_SIM_DB_FAILURE; } + imsi_len = os_strlen(imsi); len = os_snprintf(msg, sizeof(msg), "SIM-REQ-AUTH "); - if (len < 0 || len + identity_len >= sizeof(msg)) + if (len < 0 || len + imsi_len >= sizeof(msg)) return EAP_SIM_DB_FAILURE; - os_memcpy(msg + len, identity, identity_len); - len += identity_len; + os_memcpy(msg + len, imsi, imsi_len); + len += imsi_len; ret = os_snprintf(msg + len, sizeof(msg) - len, " %d", max_chal); if (ret < 0 || (size_t) ret >= sizeof(msg) - len) return EAP_SIM_DB_FAILURE; len += ret; - wpa_hexdump(MSG_DEBUG, "EAP-SIM DB: requesting SIM authentication " - "data for IMSI", identity, identity_len); + wpa_printf(MSG_DEBUG, "EAP-SIM DB: requesting SIM authentication " + "data for IMSI '%s'", imsi); if (eap_sim_db_send(data, msg, len) < 0) return EAP_SIM_DB_FAILURE; @@ -631,9 +934,7 @@ if (entry == NULL) return EAP_SIM_DB_FAILURE; - os_get_time(&entry->timestamp); - os_memcpy(entry->imsi, identity, identity_len); - entry->imsi_len = identity_len; + os_strlcpy(entry->imsi, imsi, sizeof(entry->imsi)); entry->cb_session_ctx = cb_session_ctx; entry->state = PENDING; eap_sim_db_add_pending(data, entry); @@ -643,189 +944,6 @@ } -static struct eap_sim_pseudonym * -eap_sim_db_get_pseudonym(struct eap_sim_db_data *data, const u8 *identity, - size_t identity_len) -{ - char *pseudonym; - size_t len; - struct eap_sim_pseudonym *p; - - if (identity_len == 0 || - (identity[0] != EAP_SIM_PSEUDONYM_PREFIX && - identity[0] != EAP_AKA_PSEUDONYM_PREFIX)) - return NULL; - - /* Remove possible realm from identity */ - len = 0; - while (len < identity_len) { - if (identity[len] == '@') - break; - len++; - } - - pseudonym = os_malloc(len + 1); - if (pseudonym == NULL) - return NULL; - os_memcpy(pseudonym, identity, len); - pseudonym[len] = '\0'; - - p = data->pseudonyms; - while (p) { - if (os_strcmp(p->pseudonym, pseudonym) == 0) - break; - p = p->next; - } - - os_free(pseudonym); - - return p; -} - - -static struct eap_sim_pseudonym * -eap_sim_db_get_pseudonym_id(struct eap_sim_db_data *data, const u8 *identity, - size_t identity_len) -{ - struct eap_sim_pseudonym *p; - - if (identity_len == 0 || - (identity[0] != EAP_SIM_PERMANENT_PREFIX && - identity[0] != EAP_AKA_PERMANENT_PREFIX)) - return NULL; - - p = data->pseudonyms; - while (p) { - if (identity_len == p->identity_len && - os_memcmp(p->identity, identity, identity_len) == 0) - break; - p = p->next; - } - - return p; -} - - -static struct eap_sim_reauth * -eap_sim_db_get_reauth(struct eap_sim_db_data *data, const u8 *identity, - size_t identity_len) -{ - char *reauth_id; - size_t len; - struct eap_sim_reauth *r; - - if (identity_len == 0 || - (identity[0] != EAP_SIM_REAUTH_ID_PREFIX && - identity[0] != EAP_AKA_REAUTH_ID_PREFIX)) - return NULL; - - /* Remove possible realm from identity */ - len = 0; - while (len < identity_len) { - if (identity[len] == '@') - break; - len++; - } - - reauth_id = os_malloc(len + 1); - if (reauth_id == NULL) - return NULL; - os_memcpy(reauth_id, identity, len); - reauth_id[len] = '\0'; - - r = data->reauths; - while (r) { - if (os_strcmp(r->reauth_id, reauth_id) == 0) - break; - r = r->next; - } - - os_free(reauth_id); - - return r; -} - - -static struct eap_sim_reauth * -eap_sim_db_get_reauth_id(struct eap_sim_db_data *data, const u8 *identity, - size_t identity_len) -{ - struct eap_sim_pseudonym *p; - struct eap_sim_reauth *r; - - if (identity_len == 0) - return NULL; - - p = eap_sim_db_get_pseudonym(data, identity, identity_len); - if (p == NULL) - p = eap_sim_db_get_pseudonym_id(data, identity, identity_len); - if (p) { - identity = p->identity; - identity_len = p->identity_len; - } - - r = data->reauths; - while (r) { - if (identity_len == r->identity_len && - os_memcmp(r->identity, identity, identity_len) == 0) - break; - r = r->next; - } - - return r; -} - - -/** - * eap_sim_db_identity_known - Verify whether the given identity is known - * @priv: Private data pointer from eap_sim_db_init() - * @identity: User name identity - * @identity_len: Length of identity in bytes - * Returns: 0 if the user is found or -1 on failure - * - * In most cases, the user name is ['0','1'] | IMSI, i.e., 1 followed by the - * IMSI in ASCII format, ['2','3'] | pseudonym, or ['4','5'] | reauth_id. - */ -int eap_sim_db_identity_known(void *priv, const u8 *identity, - size_t identity_len) -{ - struct eap_sim_db_data *data = priv; - - if (identity == NULL || identity_len < 2) - return -1; - - if (identity[0] == EAP_SIM_PSEUDONYM_PREFIX || - identity[0] == EAP_AKA_PSEUDONYM_PREFIX) { - struct eap_sim_pseudonym *p = - eap_sim_db_get_pseudonym(data, identity, identity_len); - return p ? 0 : -1; - } - - if (identity[0] == EAP_SIM_REAUTH_ID_PREFIX || - identity[0] == EAP_AKA_REAUTH_ID_PREFIX) { - struct eap_sim_reauth *r = - eap_sim_db_get_reauth(data, identity, identity_len); - return r ? 0 : -1; - } - - if (identity[0] != EAP_SIM_PERMANENT_PREFIX && - identity[0] != EAP_AKA_PERMANENT_PREFIX) { - /* Unknown identity prefix */ - return -1; - } - - /* TODO: Should consider asking HLR/AuC gateway whether this permanent - * identity is known. If it is, EAP-SIM/AKA can skip identity request. - * In case of EAP-AKA, this would reduce number of needed round-trips. - * Ideally, this would be done with one wait, i.e., just request - * authentication data and store it for the next use. This would then - * need to use similar pending-request functionality as the normal - * request for authentication data at later phase. - */ - return -1; -} - - static char * eap_sim_db_get_next(struct eap_sim_db_data *data, char prefix) { char *id, *pos, *end; @@ -848,8 +966,8 @@ /** * eap_sim_db_get_next_pseudonym - EAP-SIM DB: Get next pseudonym - * @priv: Private data pointer from eap_sim_db_init() - * @aka: Using EAP-AKA instead of EAP-SIM + * @data: Private data pointer from eap_sim_db_init() + * @method: EAP method (SIM/AKA/AKA') * Returns: Next pseudonym (allocated string) or %NULL on failure * * This function is used to generate a pseudonym for EAP-SIM. The returned @@ -857,18 +975,31 @@ * with eap_sim_db_add_pseudonym() once the authentication has been completed * successfully. Caller is responsible for freeing the returned buffer. */ -char * eap_sim_db_get_next_pseudonym(void *priv, int aka) +char * eap_sim_db_get_next_pseudonym(struct eap_sim_db_data *data, + enum eap_sim_db_method method) { - struct eap_sim_db_data *data = priv; - return eap_sim_db_get_next(data, aka ? EAP_AKA_PSEUDONYM_PREFIX : - EAP_SIM_PSEUDONYM_PREFIX); + char prefix = EAP_SIM_REAUTH_ID_PREFIX; + + switch (method) { + case EAP_SIM_DB_SIM: + prefix = EAP_SIM_PSEUDONYM_PREFIX; + break; + case EAP_SIM_DB_AKA: + prefix = EAP_AKA_PSEUDONYM_PREFIX; + break; + case EAP_SIM_DB_AKA_PRIME: + prefix = EAP_AKA_PRIME_PSEUDONYM_PREFIX; + break; + } + + return eap_sim_db_get_next(data, prefix); } /** * eap_sim_db_get_next_reauth_id - EAP-SIM DB: Get next reauth_id - * @priv: Private data pointer from eap_sim_db_init() - * @aka: Using EAP-AKA instead of EAP-SIM + * @data: Private data pointer from eap_sim_db_init() + * @method: EAP method (SIM/AKA/AKA') * Returns: Next reauth_id (allocated string) or %NULL on failure * * This function is used to generate a fast re-authentication identity for @@ -877,19 +1008,31 @@ * has been completed successfully. Caller is responsible for freeing the * returned buffer. */ -char * eap_sim_db_get_next_reauth_id(void *priv, int aka) +char * eap_sim_db_get_next_reauth_id(struct eap_sim_db_data *data, + enum eap_sim_db_method method) { - struct eap_sim_db_data *data = priv; - return eap_sim_db_get_next(data, aka ? EAP_AKA_REAUTH_ID_PREFIX : - EAP_SIM_REAUTH_ID_PREFIX); + char prefix = EAP_SIM_REAUTH_ID_PREFIX; + + switch (method) { + case EAP_SIM_DB_SIM: + prefix = EAP_SIM_REAUTH_ID_PREFIX; + break; + case EAP_SIM_DB_AKA: + prefix = EAP_AKA_REAUTH_ID_PREFIX; + break; + case EAP_SIM_DB_AKA_PRIME: + prefix = EAP_AKA_PRIME_REAUTH_ID_PREFIX; + break; + } + + return eap_sim_db_get_next(data, prefix); } /** * eap_sim_db_add_pseudonym - EAP-SIM DB: Add new pseudonym - * @priv: Private data pointer from eap_sim_db_init() - * @identity: Identity of the user (may be permanent identity or pseudonym) - * @identity_len: Length of identity + * @data: Private data pointer from eap_sim_db_init() + * @permanent: Permanent username * @pseudonym: Pseudonym for this user. This needs to be an allocated buffer, * e.g., return value from eap_sim_db_get_next_pseudonym(). Caller must not * free it. @@ -898,20 +1041,22 @@ * This function adds a new pseudonym for EAP-SIM user. EAP-SIM DB is * responsible of freeing pseudonym buffer once it is not needed anymore. */ -int eap_sim_db_add_pseudonym(void *priv, const u8 *identity, - size_t identity_len, char *pseudonym) +int eap_sim_db_add_pseudonym(struct eap_sim_db_data *data, + const char *permanent, char *pseudonym) { - struct eap_sim_db_data *data = priv; struct eap_sim_pseudonym *p; - wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: Add pseudonym for identity", - identity, identity_len); - wpa_printf(MSG_DEBUG, "EAP-SIM DB: Pseudonym: %s", pseudonym); + wpa_printf(MSG_DEBUG, "EAP-SIM DB: Add pseudonym '%s' for permanent " + "username '%s'", pseudonym, permanent); /* TODO: could store last two pseudonyms */ - p = eap_sim_db_get_pseudonym(data, identity, identity_len); - if (p == NULL) - p = eap_sim_db_get_pseudonym_id(data, identity, identity_len); - +#ifdef CONFIG_SQLITE + if (data->sqlite_db) + return db_add_pseudonym(data, permanent, pseudonym); +#endif /* CONFIG_SQLITE */ + for (p = data->pseudonyms; p; p = p->next) { + if (os_strcmp(permanent, p->permanent) == 0) + break; + } if (p) { wpa_printf(MSG_DEBUG, "EAP-SIM DB: Replacing previous " "pseudonym: %s", p->pseudonym); @@ -927,14 +1072,12 @@ } p->next = data->pseudonyms; - p->identity = os_malloc(identity_len); - if (p->identity == NULL) { + p->permanent = os_strdup(permanent); + if (p->permanent == NULL) { os_free(p); os_free(pseudonym); return -1; } - os_memcpy(p->identity, identity, identity_len); - p->identity_len = identity_len; p->pseudonym = pseudonym; data->pseudonyms = p; @@ -944,18 +1087,16 @@ static struct eap_sim_reauth * -eap_sim_db_add_reauth_data(struct eap_sim_db_data *data, const u8 *identity, - size_t identity_len, char *reauth_id, u16 counter) +eap_sim_db_add_reauth_data(struct eap_sim_db_data *data, + const char *permanent, + char *reauth_id, u16 counter) { struct eap_sim_reauth *r; - wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: Add reauth_id for identity", - identity, identity_len); - wpa_printf(MSG_DEBUG, "EAP-SIM DB: reauth_id: %s", reauth_id); - - r = eap_sim_db_get_reauth(data, identity, identity_len); - if (r == NULL) - r = eap_sim_db_get_reauth_id(data, identity, identity_len); + for (r = data->reauths; r; r = r->next) { + if (os_strcmp(r->permanent, permanent) == 0) + break; + } if (r) { wpa_printf(MSG_DEBUG, "EAP-SIM DB: Replacing previous " @@ -970,14 +1111,12 @@ } r->next = data->reauths; - r->identity = os_malloc(identity_len); - if (r->identity == NULL) { + r->permanent = os_strdup(permanent); + if (r->permanent == NULL) { os_free(r); os_free(reauth_id); return NULL; } - os_memcpy(r->identity, identity, identity_len); - r->identity_len = identity_len; r->reauth_id = reauth_id; data->reauths = r; wpa_printf(MSG_DEBUG, "EAP-SIM DB: Added new reauth entry"); @@ -992,7 +1131,7 @@ /** * eap_sim_db_add_reauth - EAP-SIM DB: Add new re-authentication entry * @priv: Private data pointer from eap_sim_db_init() - * @identity: Identity of the user (may be permanent identity or pseudonym) + * @permanent: Permanent username * @identity_len: Length of identity * @reauth_id: reauth_id for this user. This needs to be an allocated buffer, * e.g., return value from eap_sim_db_get_next_reauth_id(). Caller must not @@ -1005,20 +1144,24 @@ * EAP-SIM DB is responsible of freeing reauth_id buffer once it is not needed * anymore. */ -int eap_sim_db_add_reauth(void *priv, const u8 *identity, - size_t identity_len, char *reauth_id, u16 counter, - const u8 *mk) +int eap_sim_db_add_reauth(struct eap_sim_db_data *data, const char *permanent, + char *reauth_id, u16 counter, const u8 *mk) { - struct eap_sim_db_data *data = priv; struct eap_sim_reauth *r; - r = eap_sim_db_add_reauth_data(data, identity, identity_len, reauth_id, - counter); + wpa_printf(MSG_DEBUG, "EAP-SIM DB: Add reauth_id '%s' for permanent " + "identity '%s'", reauth_id, permanent); + +#ifdef CONFIG_SQLITE + if (data->sqlite_db) + return db_add_reauth(data, permanent, reauth_id, counter, mk, + NULL, NULL, NULL); +#endif /* CONFIG_SQLITE */ + r = eap_sim_db_add_reauth_data(data, permanent, reauth_id, counter); if (r == NULL) return -1; os_memcpy(r->mk, mk, EAP_SIM_MK_LEN); - r->aka_prime = 0; return 0; } @@ -1027,9 +1170,8 @@ #ifdef EAP_SERVER_AKA_PRIME /** * eap_sim_db_add_reauth_prime - EAP-AKA' DB: Add new re-authentication entry - * @priv: Private data pointer from eap_sim_db_init() - * @identity: Identity of the user (may be permanent identity or pseudonym) - * @identity_len: Length of identity + * @data: Private data pointer from eap_sim_db_init() + * @permanent: Permanent username * @reauth_id: reauth_id for this user. This needs to be an allocated buffer, * e.g., return value from eap_sim_db_get_next_reauth_id(). Caller must not * free it. @@ -1043,20 +1185,25 @@ * EAP-SIM DB is responsible of freeing reauth_id buffer once it is not needed * anymore. */ -int eap_sim_db_add_reauth_prime(void *priv, const u8 *identity, - size_t identity_len, char *reauth_id, - u16 counter, const u8 *k_encr, const u8 *k_aut, - const u8 *k_re) +int eap_sim_db_add_reauth_prime(struct eap_sim_db_data *data, + const char *permanent, char *reauth_id, + u16 counter, const u8 *k_encr, + const u8 *k_aut, const u8 *k_re) { - struct eap_sim_db_data *data = priv; struct eap_sim_reauth *r; - r = eap_sim_db_add_reauth_data(data, identity, identity_len, reauth_id, - counter); + wpa_printf(MSG_DEBUG, "EAP-SIM DB: Add reauth_id '%s' for permanent " + "identity '%s'", reauth_id, permanent); + +#ifdef CONFIG_SQLITE + if (data->sqlite_db) + return db_add_reauth(data, permanent, reauth_id, counter, NULL, + k_encr, k_aut, k_re); +#endif /* CONFIG_SQLITE */ + r = eap_sim_db_add_reauth_data(data, permanent, reauth_id, counter); if (r == NULL) return -1; - r->aka_prime = 1; os_memcpy(r->k_encr, k_encr, EAP_SIM_K_ENCR_LEN); os_memcpy(r->k_aut, k_aut, EAP_AKA_PRIME_K_AUT_LEN); os_memcpy(r->k_re, k_re, EAP_AKA_PRIME_K_RE_LEN); @@ -1068,66 +1215,75 @@ /** * eap_sim_db_get_permanent - EAP-SIM DB: Get permanent identity - * @priv: Private data pointer from eap_sim_db_init() - * @identity: Identity of the user (may be permanent identity or pseudonym) - * @identity_len: Length of identity - * @len: Buffer for length of the returned permanent identity - * Returns: Pointer to the permanent identity, or %NULL if not found + * @data: Private data pointer from eap_sim_db_init() + * @pseudonym: Pseudonym username + * Returns: Pointer to permanent username or %NULL if not found */ -const u8 * eap_sim_db_get_permanent(void *priv, const u8 *identity, - size_t identity_len, size_t *len) +const char * +eap_sim_db_get_permanent(struct eap_sim_db_data *data, const char *pseudonym) { - struct eap_sim_db_data *data = priv; struct eap_sim_pseudonym *p; - if (identity == NULL) - return NULL; +#ifdef CONFIG_SQLITE + if (data->sqlite_db) + return db_get_pseudonym(data, pseudonym); +#endif /* CONFIG_SQLITE */ - p = eap_sim_db_get_pseudonym(data, identity, identity_len); - if (p == NULL) - p = eap_sim_db_get_pseudonym_id(data, identity, identity_len); - if (p == NULL) - return NULL; + p = data->pseudonyms; + while (p) { + if (os_strcmp(p->pseudonym, pseudonym) == 0) + return p->permanent; + p = p->next; + } - *len = p->identity_len; - return p->identity; + return NULL; } /** * eap_sim_db_get_reauth_entry - EAP-SIM DB: Get re-authentication entry - * @priv: Private data pointer from eap_sim_db_init() - * @identity: Identity of the user (may be permanent identity, pseudonym, or - * reauth_id) - * @identity_len: Length of identity + * @data: Private data pointer from eap_sim_db_init() + * @reauth_id: Fast re-authentication username * Returns: Pointer to the re-auth entry, or %NULL if not found */ struct eap_sim_reauth * -eap_sim_db_get_reauth_entry(void *priv, const u8 *identity, - size_t identity_len) +eap_sim_db_get_reauth_entry(struct eap_sim_db_data *data, + const char *reauth_id) { - struct eap_sim_db_data *data = priv; struct eap_sim_reauth *r; - if (identity == NULL) - return NULL; - r = eap_sim_db_get_reauth(data, identity, identity_len); - if (r == NULL) - r = eap_sim_db_get_reauth_id(data, identity, identity_len); +#ifdef CONFIG_SQLITE + if (data->sqlite_db) + return db_get_reauth(data, reauth_id); +#endif /* CONFIG_SQLITE */ + + r = data->reauths; + while (r) { + if (os_strcmp(r->reauth_id, reauth_id) == 0) + break; + r = r->next; + } + return r; } /** * eap_sim_db_remove_reauth - EAP-SIM DB: Remove re-authentication entry - * @priv: Private data pointer from eap_sim_db_init() + * @data: Private data pointer from eap_sim_db_init() * @reauth: Pointer to re-authentication entry from * eap_sim_db_get_reauth_entry() */ -void eap_sim_db_remove_reauth(void *priv, struct eap_sim_reauth *reauth) +void eap_sim_db_remove_reauth(struct eap_sim_db_data *data, + struct eap_sim_reauth *reauth) { - struct eap_sim_db_data *data = priv; struct eap_sim_reauth *r, *prev = NULL; +#ifdef CONFIG_SQLITE + if (data->sqlite_db) { + db_remove_reauth(data, reauth); + return; + } +#endif /* CONFIG_SQLITE */ r = data->reauths; while (r) { if (r == reauth) { @@ -1146,9 +1302,8 @@ /** * eap_sim_db_get_aka_auth - Get AKA authentication values - * @priv: Private data pointer from eap_sim_db_init() - * @identity: User name identity - * @identity_len: Length of identity in bytes + * @data: Private data pointer from eap_sim_db_init() + * @username: Permanent username (prefix | IMSI) * @_rand: Buffer for RAND value * @autn: Buffer for AUTN value * @ik: Buffer for IK value @@ -1161,9 +1316,6 @@ * case, the callback function registered with eap_sim_db_init() will be * called once the results become available. * - * In most cases, the user name is '0' | IMSI, i.e., 0 followed by the IMSI in - * ASCII format. - * * When using an external server for AKA authentication, this function can * always start a request and return EAP_SIM_DB_PENDING immediately if * authentication triplets are not available. Once the authentication data are @@ -1172,40 +1324,29 @@ * eap_sim_db_get_aka_auth() function will then be called again and the newly * received triplets will then be given to the caller. */ -int eap_sim_db_get_aka_auth(void *priv, const u8 *identity, - size_t identity_len, u8 *_rand, u8 *autn, u8 *ik, - u8 *ck, u8 *res, size_t *res_len, - void *cb_session_ctx) +int eap_sim_db_get_aka_auth(struct eap_sim_db_data *data, const char *username, + u8 *_rand, u8 *autn, u8 *ik, u8 *ck, + u8 *res, size_t *res_len, void *cb_session_ctx) { - struct eap_sim_db_data *data = priv; struct eap_sim_db_pending *entry; int len; - size_t i; char msg[40]; + const char *imsi; + size_t imsi_len; - if (identity_len < 2 || identity == NULL || - identity[0] != EAP_AKA_PERMANENT_PREFIX) { - wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity", - identity, identity_len); - return EAP_SIM_DB_FAILURE; - } - identity++; - identity_len--; - for (i = 0; i < identity_len; i++) { - if (identity[i] == '@') { - identity_len = i; - break; - } - } - if (identity_len + 1 > sizeof(entry->imsi)) { - wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity", - identity, identity_len); + if (username == NULL || + (username[0] != EAP_AKA_PERMANENT_PREFIX && + username[0] != EAP_AKA_PRIME_PERMANENT_PREFIX) || + username[1] == '\0' || os_strlen(username) > sizeof(entry->imsi)) { + wpa_printf(MSG_DEBUG, "EAP-SIM DB: unexpected username '%s'", + username); return EAP_SIM_DB_FAILURE; } - wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: Get AKA auth for IMSI", - identity, identity_len); + imsi = username + 1; + wpa_printf(MSG_DEBUG, "EAP-SIM DB: Get AKA auth for IMSI '%s'", + imsi); - entry = eap_sim_db_get_pending(data, identity, identity_len, 1); + entry = eap_sim_db_get_pending(data, imsi, 1); if (entry) { if (entry->state == FAILURE) { os_free(entry); @@ -1236,14 +1377,15 @@ return EAP_SIM_DB_FAILURE; } + imsi_len = os_strlen(imsi); len = os_snprintf(msg, sizeof(msg), "AKA-REQ-AUTH "); - if (len < 0 || len + identity_len >= sizeof(msg)) + if (len < 0 || len + imsi_len >= sizeof(msg)) return EAP_SIM_DB_FAILURE; - os_memcpy(msg + len, identity, identity_len); - len += identity_len; + os_memcpy(msg + len, imsi, imsi_len); + len += imsi_len; - wpa_hexdump(MSG_DEBUG, "EAP-SIM DB: requesting AKA authentication " - "data for IMSI", identity, identity_len); + wpa_printf(MSG_DEBUG, "EAP-SIM DB: requesting AKA authentication " + "data for IMSI '%s'", imsi); if (eap_sim_db_send(data, msg, len) < 0) return EAP_SIM_DB_FAILURE; @@ -1251,10 +1393,8 @@ if (entry == NULL) return EAP_SIM_DB_FAILURE; - os_get_time(&entry->timestamp); entry->aka = 1; - os_memcpy(entry->imsi, identity, identity_len); - entry->imsi_len = identity_len; + os_strlcpy(entry->imsi, imsi, sizeof(entry->imsi)); entry->cb_session_ctx = cb_session_ctx; entry->state = PENDING; eap_sim_db_add_pending(data, entry); @@ -1266,9 +1406,8 @@ /** * eap_sim_db_resynchronize - Resynchronize AKA AUTN - * @priv: Private data pointer from eap_sim_db_init() - * @identity: User name identity - * @identity_len: Length of identity in bytes + * @data: Private data pointer from eap_sim_db_init() + * @username: Permanent username * @auts: AUTS value from the peer * @_rand: RAND value used in the rejected message * Returns: 0 on success, -1 on failure @@ -1279,42 +1418,35 @@ * eap_sim_db_get_aka_auth() will be called again to to fetch updated * RAND/AUTN values for the next challenge. */ -int eap_sim_db_resynchronize(void *priv, const u8 *identity, - size_t identity_len, const u8 *auts, - const u8 *_rand) +int eap_sim_db_resynchronize(struct eap_sim_db_data *data, + const char *username, + const u8 *auts, const u8 *_rand) { - struct eap_sim_db_data *data = priv; - size_t i; + const char *imsi; + size_t imsi_len; - if (identity_len < 2 || identity == NULL || - identity[0] != EAP_AKA_PERMANENT_PREFIX) { - wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity", - identity, identity_len); - return -1; - } - identity++; - identity_len--; - for (i = 0; i < identity_len; i++) { - if (identity[i] == '@') { - identity_len = i; - break; - } - } - if (identity_len > 20) { - wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity", - identity, identity_len); + if (username == NULL || + (username[0] != EAP_AKA_PERMANENT_PREFIX && + username[0] != EAP_AKA_PRIME_PERMANENT_PREFIX) || + username[1] == '\0' || os_strlen(username) > 20) { + wpa_printf(MSG_DEBUG, "EAP-SIM DB: unexpected username '%s'", + username); return -1; } + imsi = username + 1; + wpa_printf(MSG_DEBUG, "EAP-SIM DB: Get AKA auth for IMSI '%s'", + imsi); if (data->sock >= 0) { char msg[100]; int len, ret; + imsi_len = os_strlen(imsi); len = os_snprintf(msg, sizeof(msg), "AKA-AUTS "); - if (len < 0 || len + identity_len >= sizeof(msg)) + if (len < 0 || len + imsi_len >= sizeof(msg)) return -1; - os_memcpy(msg + len, identity, identity_len); - len += identity_len; + os_memcpy(msg + len, imsi, imsi_len); + len += imsi_len; ret = os_snprintf(msg + len, sizeof(msg) - len, " "); if (ret < 0 || (size_t) ret >= sizeof(msg) - len) @@ -1328,11 +1460,35 @@ len += ret; len += wpa_snprintf_hex(msg + len, sizeof(msg) - len, _rand, EAP_AKA_RAND_LEN); - wpa_hexdump(MSG_DEBUG, "EAP-SIM DB: reporting AKA AUTS for " - "IMSI", identity, identity_len); + wpa_printf(MSG_DEBUG, "EAP-SIM DB: reporting AKA AUTS for " + "IMSI '%s'", imsi); if (eap_sim_db_send(data, msg, len) < 0) return -1; } return 0; } + + +/** + * sim_get_username - Extract username from SIM identity + * @identity: Identity + * @identity_len: Identity length + * Returns: Allocated buffer with the username part of the identity + * + * Caller is responsible for freeing the returned buffer with os_free(). + */ +char * sim_get_username(const u8 *identity, size_t identity_len) +{ + size_t pos; + + if (identity == NULL) + return NULL; + + for (pos = 0; pos < identity_len; pos++) { + if (identity[pos] == '@' || identity[pos] == '\0') + break; + } + + return dup_binstr(identity, pos); +} diff -Nru wpa-1.0/src/eap_server/eap_sim_db.h wpa-2.1/src/eap_server/eap_sim_db.h --- wpa-1.0/src/eap_server/eap_sim_db.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/eap_server/eap_sim_db.h 2014-02-04 11:23:35.000000000 +0000 @@ -1,15 +1,9 @@ /* * hostapd / EAP-SIM database/authenticator gateway - * Copyright (c) 2005-2007, Jouni Malinen + * Copyright (c) 2005-2008, 2012, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef EAP_SIM_DB_H @@ -24,49 +18,57 @@ #define EAP_AKA_PERMANENT_PREFIX '0' #define EAP_AKA_PSEUDONYM_PREFIX '2' #define EAP_AKA_REAUTH_ID_PREFIX '4' +#define EAP_AKA_PRIME_PERMANENT_PREFIX '6' +#define EAP_AKA_PRIME_PSEUDONYM_PREFIX '7' +#define EAP_AKA_PRIME_REAUTH_ID_PREFIX '8' + +enum eap_sim_db_method { + EAP_SIM_DB_SIM, + EAP_SIM_DB_AKA, + EAP_SIM_DB_AKA_PRIME +}; -void * eap_sim_db_init(const char *config, - void (*get_complete_cb)(void *ctx, void *session_ctx), - void *ctx); +struct eap_sim_db_data; + +struct eap_sim_db_data * +eap_sim_db_init(const char *config, + void (*get_complete_cb)(void *ctx, void *session_ctx), + void *ctx); void eap_sim_db_deinit(void *priv); -int eap_sim_db_get_gsm_triplets(void *priv, const u8 *identity, - size_t identity_len, int max_chal, +int eap_sim_db_get_gsm_triplets(struct eap_sim_db_data *data, + const char *username, int max_chal, u8 *_rand, u8 *kc, u8 *sres, void *cb_session_ctx); #define EAP_SIM_DB_FAILURE -1 #define EAP_SIM_DB_PENDING -2 -int eap_sim_db_identity_known(void *priv, const u8 *identity, - size_t identity_len); +char * eap_sim_db_get_next_pseudonym(struct eap_sim_db_data *data, + enum eap_sim_db_method method); -char * eap_sim_db_get_next_pseudonym(void *priv, int aka); +char * eap_sim_db_get_next_reauth_id(struct eap_sim_db_data *data, + enum eap_sim_db_method method); -char * eap_sim_db_get_next_reauth_id(void *priv, int aka); +int eap_sim_db_add_pseudonym(struct eap_sim_db_data *data, + const char *permanent, char *pseudonym); -int eap_sim_db_add_pseudonym(void *priv, const u8 *identity, - size_t identity_len, char *pseudonym); - -int eap_sim_db_add_reauth(void *priv, const u8 *identity, - size_t identity_len, char *reauth_id, u16 counter, - const u8 *mk); -int eap_sim_db_add_reauth_prime(void *priv, const u8 *identity, - size_t identity_len, char *reauth_id, - u16 counter, const u8 *k_encr, const u8 *k_aut, - const u8 *k_re); +int eap_sim_db_add_reauth(struct eap_sim_db_data *data, const char *permanent, + char *reauth_id, u16 counter, const u8 *mk); +int eap_sim_db_add_reauth_prime(struct eap_sim_db_data *data, + const char *permanent, + char *reauth_id, u16 counter, const u8 *k_encr, + const u8 *k_aut, const u8 *k_re); -const u8 * eap_sim_db_get_permanent(void *priv, const u8 *identity, - size_t identity_len, size_t *len); +const char * eap_sim_db_get_permanent(struct eap_sim_db_data *data, + const char *pseudonym); struct eap_sim_reauth { struct eap_sim_reauth *next; - u8 *identity; - size_t identity_len; - char *reauth_id; + char *permanent; /* Permanent username */ + char *reauth_id; /* Fast re-authentication username */ u16 counter; - int aka_prime; u8 mk[EAP_SIM_MK_LEN]; u8 k_encr[EAP_SIM_K_ENCR_LEN]; u8 k_aut[EAP_AKA_PRIME_K_AUT_LEN]; @@ -74,18 +76,20 @@ }; struct eap_sim_reauth * -eap_sim_db_get_reauth_entry(void *priv, const u8 *identity, - size_t identity_len); +eap_sim_db_get_reauth_entry(struct eap_sim_db_data *data, + const char *reauth_id); -void eap_sim_db_remove_reauth(void *priv, struct eap_sim_reauth *reauth); +void eap_sim_db_remove_reauth(struct eap_sim_db_data *data, + struct eap_sim_reauth *reauth); -int eap_sim_db_get_aka_auth(void *priv, const u8 *identity, - size_t identity_len, u8 *_rand, u8 *autn, u8 *ik, - u8 *ck, u8 *res, size_t *res_len, - void *cb_session_ctx); +int eap_sim_db_get_aka_auth(struct eap_sim_db_data *data, const char *username, + u8 *_rand, u8 *autn, u8 *ik, u8 *ck, + u8 *res, size_t *res_len, void *cb_session_ctx); -int eap_sim_db_resynchronize(void *priv, const u8 *identity, - size_t identity_len, const u8 *auts, +int eap_sim_db_resynchronize(struct eap_sim_db_data *data, + const char *username, const u8 *auts, const u8 *_rand); +char * sim_get_username(const u8 *identity, size_t identity_len); + #endif /* EAP_SIM_DB_H */ diff -Nru wpa-1.0/src/eap_server/eap_tls_common.h wpa-2.1/src/eap_server/eap_tls_common.h --- wpa-1.0/src/eap_server/eap_tls_common.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/eap_server/eap_tls_common.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * EAP-TLS/PEAP/TTLS/FAST server common functions * Copyright (c) 2004-2009, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef EAP_TLS_COMMON_H @@ -68,7 +62,12 @@ /* could be up to 128 bytes, but only the first 64 bytes are used */ #define EAP_TLS_KEY_LEN 64 +/* dummy type used as a flag for UNAUTH-TLS */ +#define EAP_UNAUTH_TLS_TYPE 255 + +struct wpabuf * eap_tls_msg_alloc(EapType type, size_t payload_len, + u8 code, u8 identifier); int eap_server_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data, int verify_peer); void eap_server_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data); diff -Nru wpa-1.0/src/eap_server/ikev2.c wpa-2.1/src/eap_server/ikev2.c --- wpa-1.0/src/eap_server/ikev2.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/eap_server/ikev2.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * IKEv2 initiator (RFC 4306) for EAP-IKEV2 * Copyright (c) 2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -996,7 +990,7 @@ */ wpabuf_put(msg, data->dh->prime_len - wpabuf_len(pv)); wpabuf_put_buf(msg, pv); - os_free(pv); + wpabuf_free(pv); plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) phdr; WPA_PUT_BE16(phdr->payload_length, plen); diff -Nru wpa-1.0/src/eap_server/ikev2.h wpa-2.1/src/eap_server/ikev2.h --- wpa-1.0/src/eap_server/ikev2.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/eap_server/ikev2.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * IKEv2 initiator (RFC 4306) for EAP-IKEV2 * Copyright (c) 2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef IKEV2_H diff -Nru wpa-1.0/src/eap_server/Makefile wpa-2.1/src/eap_server/Makefile --- wpa-1.0/src/eap_server/Makefile 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/eap_server/Makefile 2014-02-04 11:23:35.000000000 +0000 @@ -2,7 +2,7 @@ @echo Nothing to be made. clean: - rm -f *~ *.o *.d + rm -f *~ *.o *.d *.gcno *.gcda *.gcov install: @echo Nothing to be made. diff -Nru wpa-1.0/src/eap_server/tncs.c wpa-2.1/src/eap_server/tncs.c --- wpa-1.0/src/eap_server/tncs.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/eap_server/tncs.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * EAP-TNC - TNCS (IF-IMV, IF-TNCCS, and IF-TNCCS-SOH) * Copyright (c) 2007-2008, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -857,12 +851,10 @@ unsigned char *decoded; size_t decoded_len; - buf = os_malloc(len + 1); + buf = dup_binstr(msg, len); if (buf == NULL) return TNCCS_PROCESS_ERROR; - os_memcpy(buf, msg, len); - buf[len] = '\0'; start = os_strstr(buf, ""); if (start == NULL || end == NULL || start > end) { diff -Nru wpa-1.0/src/eap_server/tncs.h wpa-2.1/src/eap_server/tncs.h --- wpa-1.0/src/eap_server/tncs.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/eap_server/tncs.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * EAP-TNC - TNCS (IF-IMV, IF-TNCCS, and IF-TNCCS-SOH) * Copyright (c) 2007-2008, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef TNCS_H diff -Nru wpa-1.0/src/l2_packet/l2_packet_freebsd.c wpa-2.1/src/l2_packet/l2_packet_freebsd.c --- wpa-1.0/src/l2_packet/l2_packet_freebsd.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/l2_packet/l2_packet_freebsd.c 2014-02-04 11:23:35.000000000 +0000 @@ -3,14 +3,8 @@ * Copyright (c) 2003-2005, Jouni Malinen * Copyright (c) 2005, Sam Leffler * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff -Nru wpa-1.0/src/l2_packet/l2_packet.h wpa-2.1/src/l2_packet/l2_packet.h --- wpa-1.0/src/l2_packet/l2_packet.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/l2_packet/l2_packet.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * WPA Supplicant - Layer2 packet interface definition * Copyright (c) 2003-2005, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. * * This file defines an interface for layer 2 (link layer) packet sending and * receiving. l2_packet_linux.c is one implementation for such a layer 2 diff -Nru wpa-1.0/src/l2_packet/l2_packet_linux.c wpa-2.1/src/l2_packet/l2_packet_linux.c --- wpa-1.0/src/l2_packet/l2_packet_linux.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/l2_packet/l2_packet_linux.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * WPA Supplicant - Layer2 packet handling with Linux packet sockets * Copyright (c) 2003-2005, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff -Nru wpa-1.0/src/l2_packet/l2_packet_ndis.c wpa-2.1/src/l2_packet/l2_packet_ndis.c --- wpa-1.0/src/l2_packet/l2_packet_ndis.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/l2_packet/l2_packet_ndis.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * WPA Supplicant - Layer2 packet handling with Microsoft NDISUIO * Copyright (c) 2003-2006, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. * * This implementation requires Windows specific event loop implementation, * i.e., eloop_win.c. In addition, the NDISUIO connection is shared with diff -Nru wpa-1.0/src/l2_packet/l2_packet_none.c wpa-2.1/src/l2_packet/l2_packet_none.c --- wpa-1.0/src/l2_packet/l2_packet_none.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/l2_packet/l2_packet_none.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * WPA Supplicant - Layer2 packet handling example with dummy functions * Copyright (c) 2003-2005, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. * * This file can be used as a starting point for layer2 packet implementation. */ diff -Nru wpa-1.0/src/l2_packet/l2_packet_pcap.c wpa-2.1/src/l2_packet/l2_packet_pcap.c --- wpa-1.0/src/l2_packet/l2_packet_pcap.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/l2_packet/l2_packet_pcap.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * WPA Supplicant - Layer2 packet handling with libpcap/libdnet and WinPcap * Copyright (c) 2003-2006, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff -Nru wpa-1.0/src/l2_packet/l2_packet_privsep.c wpa-2.1/src/l2_packet/l2_packet_privsep.c --- wpa-1.0/src/l2_packet/l2_packet_privsep.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/l2_packet/l2_packet_privsep.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * WPA Supplicant - Layer2 packet handling with privilege separation * Copyright (c) 2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -179,7 +173,7 @@ addr.sun_family = AF_UNIX; os_strlcpy(addr.sun_path, l2->own_socket_path, sizeof(addr.sun_path)); if (bind(l2->fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - perror("bind(PF_UNIX)"); + perror("l2-pkt-privsep: bind(PF_UNIX)"); goto fail; } diff -Nru wpa-1.0/src/l2_packet/l2_packet_winpcap.c wpa-2.1/src/l2_packet/l2_packet_winpcap.c --- wpa-1.0/src/l2_packet/l2_packet_winpcap.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/l2_packet/l2_packet_winpcap.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * WPA Supplicant - Layer2 packet handling with WinPcap RX thread * Copyright (c) 2003-2006, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. * * This l2_packet implementation is explicitly for WinPcap and Windows events. * l2_packet_pcap.c has support for WinPcap, but it requires polling to receive diff -Nru wpa-1.0/src/l2_packet/Makefile wpa-2.1/src/l2_packet/Makefile --- wpa-1.0/src/l2_packet/Makefile 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/l2_packet/Makefile 2014-02-04 11:23:35.000000000 +0000 @@ -2,7 +2,7 @@ @echo Nothing to be made. clean: - rm -f *~ *.o *.d + rm -f *~ *.o *.d *.gcno *.gcda *.gcov install: @echo Nothing to be made. diff -Nru wpa-1.0/src/p2p/Makefile wpa-2.1/src/p2p/Makefile --- wpa-1.0/src/p2p/Makefile 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/p2p/Makefile 2014-02-04 11:23:35.000000000 +0000 @@ -2,8 +2,7 @@ @echo Nothing to be made. clean: - for d in $(SUBDIRS); do make -C $$d clean; done - rm -f *~ *.o *.d + rm -f *~ *.o *.d *.gcno *.gcda *.gcov install: @echo Nothing to be made. diff -Nru wpa-1.0/src/p2p/p2p_build.c wpa-2.1/src/p2p/p2p_build.c --- wpa-1.0/src/p2p/p2p_build.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/p2p/p2p_build.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * P2P - IE builder * Copyright (c) 2009-2010, Atheros Communications * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -138,7 +132,8 @@ /* Update attribute length */ WPA_PUT_LE16(len, (u8 *) wpabuf_put(buf, 0) - len - 2); - wpa_printf(MSG_DEBUG, "P2P: * Channel List"); + wpa_hexdump(MSG_DEBUG, "P2P: * Channel List", + len + 2, (u8 *) wpabuf_put(buf, 0) - len - 2); } @@ -263,6 +258,7 @@ wpabuf_put_data(buf, ssid, ssid_len); wpa_printf(MSG_DEBUG, "P2P: * P2P Group ID " MACSTR, MAC2STR(dev_addr)); + wpa_hexdump_ascii(MSG_DEBUG, "P2P: P2P Group ID SSID", ssid, ssid_len); } @@ -332,13 +328,32 @@ } -static void p2p_add_wps_string(struct wpabuf *buf, enum wps_attribute attr, - const char *val) +void p2p_buf_add_oob_go_neg_channel(struct wpabuf *buf, const char *country, + u8 oper_class, u8 channel, + enum p2p_role_indication role) +{ + /* OOB Group Owner Negotiation Channel */ + wpabuf_put_u8(buf, P2P_ATTR_OOB_GO_NEG_CHANNEL); + wpabuf_put_le16(buf, 6); + wpabuf_put_data(buf, country, 3); + wpabuf_put_u8(buf, oper_class); /* Operating Class */ + wpabuf_put_u8(buf, channel); /* Channel Number */ + wpabuf_put_u8(buf, (u8) role); /* Role indication */ + wpa_printf(MSG_DEBUG, "P2P: * OOB GO Negotiation Channel: Operating " + "Class %u Channel %u Role %d", + oper_class, channel, role); +} + + +static int p2p_add_wps_string(struct wpabuf *buf, enum wps_attribute attr, + const char *val) { size_t len; - wpabuf_put_be16(buf, attr); len = val ? os_strlen(val) : 0; + if (wpabuf_tailroom(buf) < 4 + len) + return -1; + wpabuf_put_be16(buf, attr); #ifndef CONFIG_WPS_STRICT if (len == 0) { /* @@ -346,69 +361,96 @@ * attributes. As a workaround, send a space character if the * device attribute string is empty. */ + if (wpabuf_tailroom(buf) < 3) + return -1; wpabuf_put_be16(buf, 1); wpabuf_put_u8(buf, ' '); - return; + return 0; } #endif /* CONFIG_WPS_STRICT */ wpabuf_put_be16(buf, len); if (val) wpabuf_put_data(buf, val, len); + return 0; } -void p2p_build_wps_ie(struct p2p_data *p2p, struct wpabuf *buf, u16 pw_id, - int all_attr) +int p2p_build_wps_ie(struct p2p_data *p2p, struct wpabuf *buf, int pw_id, + int all_attr) { u8 *len; int i; + if (wpabuf_tailroom(buf) < 6) + return -1; wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC); len = wpabuf_put(buf, 1); wpabuf_put_be32(buf, WPS_DEV_OUI_WFA); - wps_build_version(buf); + if (wps_build_version(buf) < 0) + return -1; if (all_attr) { + if (wpabuf_tailroom(buf) < 5) + return -1; wpabuf_put_be16(buf, ATTR_WPS_STATE); wpabuf_put_be16(buf, 1); wpabuf_put_u8(buf, WPS_STATE_NOT_CONFIGURED); } - /* Device Password ID */ - wpabuf_put_be16(buf, ATTR_DEV_PASSWORD_ID); - wpabuf_put_be16(buf, 2); - wpa_printf(MSG_DEBUG, "P2P: WPS IE Device Password ID: %d", pw_id); - wpabuf_put_be16(buf, pw_id); + if (pw_id >= 0) { + if (wpabuf_tailroom(buf) < 6) + return -1; + /* Device Password ID */ + wpabuf_put_be16(buf, ATTR_DEV_PASSWORD_ID); + wpabuf_put_be16(buf, 2); + wpa_printf(MSG_DEBUG, "P2P: WPS IE Device Password ID: %d", + pw_id); + wpabuf_put_be16(buf, pw_id); + } if (all_attr) { + if (wpabuf_tailroom(buf) < 5) + return -1; wpabuf_put_be16(buf, ATTR_RESPONSE_TYPE); wpabuf_put_be16(buf, 1); wpabuf_put_u8(buf, WPS_RESP_ENROLLEE_INFO); - wps_build_uuid_e(buf, p2p->cfg->uuid); - p2p_add_wps_string(buf, ATTR_MANUFACTURER, - p2p->cfg->manufacturer); - p2p_add_wps_string(buf, ATTR_MODEL_NAME, p2p->cfg->model_name); - p2p_add_wps_string(buf, ATTR_MODEL_NUMBER, - p2p->cfg->model_number); - p2p_add_wps_string(buf, ATTR_SERIAL_NUMBER, - p2p->cfg->serial_number); + if (wps_build_uuid_e(buf, p2p->cfg->uuid) < 0 || + p2p_add_wps_string(buf, ATTR_MANUFACTURER, + p2p->cfg->manufacturer) < 0 || + p2p_add_wps_string(buf, ATTR_MODEL_NAME, + p2p->cfg->model_name) < 0 || + p2p_add_wps_string(buf, ATTR_MODEL_NUMBER, + p2p->cfg->model_number) < 0 || + p2p_add_wps_string(buf, ATTR_SERIAL_NUMBER, + p2p->cfg->serial_number) < 0) + return -1; + if (wpabuf_tailroom(buf) < 4 + WPS_DEV_TYPE_LEN) + return -1; wpabuf_put_be16(buf, ATTR_PRIMARY_DEV_TYPE); wpabuf_put_be16(buf, WPS_DEV_TYPE_LEN); wpabuf_put_data(buf, p2p->cfg->pri_dev_type, WPS_DEV_TYPE_LEN); - p2p_add_wps_string(buf, ATTR_DEV_NAME, p2p->cfg->dev_name); + if (p2p_add_wps_string(buf, ATTR_DEV_NAME, p2p->cfg->dev_name) + < 0) + return -1; + if (wpabuf_tailroom(buf) < 6) + return -1; wpabuf_put_be16(buf, ATTR_CONFIG_METHODS); wpabuf_put_be16(buf, 2); wpabuf_put_be16(buf, p2p->cfg->config_methods); } - wps_build_wfa_ext(buf, 0, NULL, 0); + if (wps_build_wfa_ext(buf, 0, NULL, 0) < 0) + return -1; if (all_attr && p2p->cfg->num_sec_dev_types) { + if (wpabuf_tailroom(buf) < + 4 + WPS_DEV_TYPE_LEN * p2p->cfg->num_sec_dev_types) + return -1; wpabuf_put_be16(buf, ATTR_SECONDARY_DEV_TYPE_LIST); wpabuf_put_be16(buf, WPS_DEV_TYPE_LEN * p2p->cfg->num_sec_dev_types); @@ -430,4 +472,6 @@ } p2p_buf_update_ie_hdr(buf, len); + + return 0; } diff -Nru wpa-1.0/src/p2p/p2p.c wpa-2.1/src/p2p/p2p.c --- wpa-1.0/src/p2p/p2p.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/p2p/p2p.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * Wi-Fi Direct - P2P module * Copyright (c) 2009-2010, Atheros Communications * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -18,7 +12,6 @@ #include "eloop.h" #include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" -#include "common/wpa_ctrl.h" #include "wps/wps_i.h" #include "p2p_i.h" #include "p2p.h" @@ -48,21 +41,32 @@ * P2P_PEER_EXPIRATION_AGE - Number of seconds after which inactive peer * entries will be removed */ -#define P2P_PEER_EXPIRATION_AGE 300 +#ifndef P2P_PEER_EXPIRATION_AGE +#define P2P_PEER_EXPIRATION_AGE 60 +#endif /* P2P_PEER_EXPIRATION_AGE */ #define P2P_PEER_EXPIRATION_INTERVAL (P2P_PEER_EXPIRATION_AGE / 2) static void p2p_expire_peers(struct p2p_data *p2p) { struct p2p_device *dev, *n; - struct os_time now; + struct os_reltime now; size_t i; - os_get_time(&now); + os_get_reltime(&now); dl_list_for_each_safe(dev, n, &p2p->devices, struct p2p_device, list) { if (dev->last_seen.sec + P2P_PEER_EXPIRATION_AGE >= now.sec) continue; + if (dev == p2p->go_neg_peer) { + /* + * GO Negotiation is in progress with the peer, so + * don't expire the peer entry until GO Negotiation + * fails or times out. + */ + continue; + } + if (p2p->cfg->go_connected && p2p->cfg->go_connected(p2p->cfg->cb_ctx, dev->info.p2p_device_addr)) { @@ -70,7 +74,7 @@ * We are connected as a client to a group in which the * peer is the GO, so do not expire the peer entry. */ - os_get_time(&dev->last_seen); + os_get_reltime(&dev->last_seen); continue; } @@ -84,12 +88,12 @@ * The peer is connected as a client in a group where * we are the GO, so do not expire the peer entry. */ - os_get_time(&dev->last_seen); + os_get_reltime(&dev->last_seen); continue; } - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Expiring old peer " - "entry " MACSTR, MAC2STR(dev->info.p2p_device_addr)); + p2p_dbg(p2p, "Expiring old peer entry " MACSTR, + MAC2STR(dev->info.p2p_device_addr)); dl_list_del(&dev->list); p2p_device_free(p2p, dev); } @@ -134,14 +138,18 @@ return "INVITE"; case P2P_INVITE_LISTEN: return "INVITE_LISTEN"; - case P2P_SEARCH_WHEN_READY: - return "SEARCH_WHEN_READY"; default: return "?"; } } +const char * p2p_get_state_txt(struct p2p_data *p2p) +{ + return p2p_state_txt(p2p->state); +} + + u16 p2p_get_provisioning_info(struct p2p_data *p2p, const u8 *addr) { struct p2p_device *dev = NULL; @@ -172,7 +180,7 @@ void p2p_set_state(struct p2p_data *p2p, int new_state) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: State %s -> %s", + p2p_dbg(p2p, "State %s -> %s", p2p_state_txt(p2p->state), p2p_state_txt(new_state)); p2p->state = new_state; } @@ -180,8 +188,7 @@ void p2p_set_timeout(struct p2p_data *p2p, unsigned int sec, unsigned int usec) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Set timeout (state=%s): %u.%06u sec", + p2p_dbg(p2p, "Set timeout (state=%s): %u.%06u sec", p2p_state_txt(p2p->state), sec, usec); eloop_cancel_timeout(p2p_state_timeout, p2p, NULL); eloop_register_timeout(sec, usec, p2p_state_timeout, p2p, NULL); @@ -190,8 +197,7 @@ void p2p_clear_timeout(struct p2p_data *p2p) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Clear timeout (state=%s)", - p2p_state_txt(p2p->state)); + p2p_dbg(p2p, "Clear timeout (state=%s)", p2p_state_txt(p2p->state)); eloop_cancel_timeout(p2p_state_timeout, p2p, NULL); } @@ -202,8 +208,11 @@ struct p2p_go_neg_results res; p2p_clear_timeout(p2p); p2p_set_state(p2p, P2P_IDLE); - if (p2p->go_neg_peer) + if (p2p->go_neg_peer) { + p2p->go_neg_peer->flags &= ~P2P_DEV_PEER_WAITING_RESPONSE; p2p->go_neg_peer->wps_method = WPS_NOT_READY; + p2p->go_neg_peer->oob_pw_id = 0; + } p2p->go_neg_peer = NULL; os_memset(&res, 0, sizeof(res)); @@ -218,27 +227,36 @@ } -static void p2p_listen_in_find(struct p2p_data *p2p) +static void p2p_listen_in_find(struct p2p_data *p2p, int dev_disc) { unsigned int r, tu; int freq; struct wpabuf *ies; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Starting short listen state (state=%s)", + p2p_dbg(p2p, "Starting short listen state (state=%s)", p2p_state_txt(p2p->state)); - freq = p2p_channel_to_freq(p2p->cfg->country, p2p->cfg->reg_class, - p2p->cfg->channel); + freq = p2p_channel_to_freq(p2p->cfg->reg_class, p2p->cfg->channel); if (freq < 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Unknown regulatory class/channel"); + p2p_dbg(p2p, "Unknown regulatory class/channel"); return; } os_get_random((u8 *) &r, sizeof(r)); tu = (r % ((p2p->max_disc_int - p2p->min_disc_int) + 1) + p2p->min_disc_int) * 100; + if (p2p->max_disc_tu >= 0 && tu > (unsigned int) p2p->max_disc_tu) + tu = p2p->max_disc_tu; + if (!dev_disc && tu < 100) + tu = 100; /* Need to wait in non-device discovery use cases */ + if (p2p->cfg->max_listen && 1024 * tu / 1000 > p2p->cfg->max_listen) + tu = p2p->cfg->max_listen * 1000 / 1024; + + if (tu == 0) { + p2p_dbg(p2p, "Skip listen state since duration was 0 TU"); + p2p_set_timeout(p2p, 0, 0); + return; + } p2p->pending_listen_freq = freq; p2p->pending_listen_sec = 0; @@ -250,8 +268,7 @@ if (p2p->cfg->start_listen(p2p->cfg->cb_ctx, freq, 1024 * tu / 1000, ies) < 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Failed to start listen mode"); + p2p_dbg(p2p, "Failed to start listen mode"); p2p->pending_listen_freq = 0; } wpabuf_free(ies); @@ -263,14 +280,11 @@ int freq; struct wpabuf *ies; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Going to listen(only) state"); + p2p_dbg(p2p, "Going to listen(only) state"); - freq = p2p_channel_to_freq(p2p->cfg->country, p2p->cfg->reg_class, - p2p->cfg->channel); + freq = p2p_channel_to_freq(p2p->cfg->reg_class, p2p->cfg->channel); if (freq < 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Unknown regulatory class/channel"); + p2p_dbg(p2p, "Unknown regulatory class/channel"); return -1; } @@ -279,14 +293,11 @@ p2p->pending_listen_usec = (timeout % 1000) * 1000; if (p2p->p2p_scan_running) { - if (p2p->start_after_scan == P2P_AFTER_SCAN_NOTHING) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: p2p_scan running - connect is already " - "pending - skip listen"); + if (p2p->start_after_scan == P2P_AFTER_SCAN_CONNECT) { + p2p_dbg(p2p, "p2p_scan running - connect is already pending - skip listen"); return 0; } - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: p2p_scan running - delay start of listen state"); + p2p_dbg(p2p, "p2p_scan running - delay start of listen state"); p2p->start_after_scan = P2P_AFTER_SCAN_LISTEN; return 0; } @@ -296,8 +307,7 @@ return -1; if (p2p->cfg->start_listen(p2p->cfg->cb_ctx, freq, timeout, ies) < 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Failed to start listen mode"); + p2p_dbg(p2p, "Failed to start listen mode"); p2p->pending_listen_freq = 0; wpabuf_free(ies); return -1; @@ -375,13 +385,11 @@ dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) { count++; if (oldest == NULL || - os_time_before(&dev->last_seen, &oldest->last_seen)) + os_reltime_before(&dev->last_seen, &oldest->last_seen)) oldest = dev; } if (count + 1 > p2p->cfg->max_peers && oldest) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Remove oldest peer entry to make room for a new " - "peer"); + p2p_dbg(p2p, "Remove oldest peer entry to make room for a new peer"); dl_list_del(&oldest->list); p2p_device_free(p2p, oldest); } @@ -443,13 +451,25 @@ continue; /* ignore our own entry */ dev = p2p_get_device(p2p, cli->p2p_device_addr); if (dev) { - /* - * Update information only if we have not received this - * directly from the client. - */ if (dev->flags & (P2P_DEV_GROUP_CLIENT_ONLY | - P2P_DEV_PROBE_REQ_ONLY)) + P2P_DEV_PROBE_REQ_ONLY)) { + /* + * Update information since we have not + * received this directly from the client. + */ p2p_copy_client_info(dev, cli); + } else { + /* + * Need to update P2P Client Discoverability + * flag since it is valid only in P2P Group + * Info attribute. + */ + dev->info.dev_capab &= + ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY; + dev->info.dev_capab |= + cli->dev_capab & + P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY; + } if (dev->flags & P2P_DEV_PROBE_REQ_ONLY) { dev->flags &= ~P2P_DEV_PROBE_REQ_ONLY; } @@ -468,7 +488,7 @@ os_memcpy(dev->interface_addr, cli->p2p_interface_addr, ETH_ALEN); - os_get_time(&dev->last_seen); + os_get_reltime(&dev->last_seen); os_memcpy(dev->member_in_go_dev, go_dev_addr, ETH_ALEN); os_memcpy(dev->member_in_go_iface, go_interface_addr, ETH_ALEN); @@ -478,8 +498,8 @@ } -static void p2p_copy_wps_info(struct p2p_device *dev, int probe_req, - const struct p2p_message *msg) +static void p2p_copy_wps_info(struct p2p_data *p2p, struct p2p_device *dev, + int probe_req, const struct p2p_message *msg) { os_memcpy(dev->info.device_name, msg->device_name, sizeof(dev->info.device_name)); @@ -532,7 +552,13 @@ } if (msg->capability) { - dev->info.dev_capab = msg->capability[0]; + /* + * P2P Client Discoverability bit is reserved in all frames + * that use this function, so do not change its value here. + */ + dev->info.dev_capab &= P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY; + dev->info.dev_capab |= msg->capability[0] & + ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY; dev->info.group_capab = msg->capability[1]; } @@ -543,21 +569,33 @@ } if (!probe_req) { - dev->info.config_methods = msg->config_methods ? + u16 new_config_methods; + new_config_methods = msg->config_methods ? msg->config_methods : msg->wps_config_methods; + if (new_config_methods && + dev->info.config_methods != new_config_methods) { + p2p_dbg(p2p, "Update peer " MACSTR + " config_methods 0x%x -> 0x%x", + MAC2STR(dev->info.p2p_device_addr), + dev->info.config_methods, + new_config_methods); + dev->info.config_methods = new_config_methods; + } } } /** - * p2p_add_device - Add peer entries based on scan results + * p2p_add_device - Add peer entries based on scan results or P2P frames * @p2p: P2P module context from p2p_init() * @addr: Source address of Beacon or Probe Response frame (may be either * P2P Device Address or P2P Interface Address) * @level: Signal level (signal strength of the received frame from the peer) * @freq: Frequency on which the Beacon or Probe Response frame was received + * @rx_time: Time when the result was received * @ies: IEs from the Beacon or Probe Response frame * @ies_len: Length of ies buffer in octets + * @scan_res: Whether this was based on scan results * Returns: 0 on success, -1 on failure * * If the scan result is for a GO, the clients in the group will also be added @@ -565,18 +603,19 @@ * like Provision Discovery Request that contains P2P Capability and P2P Device * Info attributes. */ -int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq, int level, - const u8 *ies, size_t ies_len) +int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq, + struct os_reltime *rx_time, int level, const u8 *ies, + size_t ies_len, int scan_res) { struct p2p_device *dev; struct p2p_message msg; const u8 *p2p_dev_addr; int i; + struct os_reltime time_now; os_memset(&msg, 0, sizeof(msg)); if (p2p_parse_ies(ies, ies_len, &msg)) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Failed to parse P2P IE for a device entry"); + p2p_dbg(p2p, "Failed to parse P2P IE for a device entry"); p2p_parse_free(&msg); return -1; } @@ -586,18 +625,16 @@ else if (msg.device_id) p2p_dev_addr = msg.device_id; else { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Ignore scan data without P2P Device Info or " - "P2P Device Id"); + p2p_dbg(p2p, "Ignore scan data without P2P Device Info or P2P Device Id"); p2p_parse_free(&msg); return -1; } if (!is_zero_ether_addr(p2p->peer_filter) && os_memcmp(p2p_dev_addr, p2p->peer_filter, ETH_ALEN) != 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Do not add peer " - "filter for " MACSTR " due to peer filter", - MAC2STR(p2p_dev_addr)); + p2p_dbg(p2p, "Do not add peer filter for " MACSTR + " due to peer filter", MAC2STR(p2p_dev_addr)); + p2p_parse_free(&msg); return 0; } @@ -606,7 +643,29 @@ p2p_parse_free(&msg); return -1; } - os_get_time(&dev->last_seen); + + if (rx_time == NULL) { + os_get_reltime(&time_now); + rx_time = &time_now; + } + + /* + * Update the device entry only if the new peer + * entry is newer than the one previously stored. + */ + if (dev->last_seen.sec > 0 && + os_reltime_before(rx_time, &dev->last_seen)) { + p2p_dbg(p2p, "Do not update peer entry based on old frame (rx_time=%u.%06u last_seen=%u.%06u)", + (unsigned int) rx_time->sec, + (unsigned int) rx_time->usec, + (unsigned int) dev->last_seen.sec, + (unsigned int) dev->last_seen.usec); + p2p_parse_free(&msg); + return -1; + } + + os_memcpy(&dev->last_seen, rx_time, sizeof(struct os_reltime)); + dev->flags &= ~(P2P_DEV_PROBE_REQ_ONLY | P2P_DEV_GROUP_CLIENT_ONLY); if (os_memcmp(addr, p2p_dev_addr, ETH_ALEN) != 0) @@ -627,27 +686,26 @@ else ds_freq = 2407 + *msg.ds_params * 5; if (freq != ds_freq) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Update Listen frequency based on DS " - "Parameter Set IE: %d -> %d MHz", + p2p_dbg(p2p, "Update Listen frequency based on DS Parameter Set IE: %d -> %d MHz", freq, ds_freq); freq = ds_freq; } } - if (dev->listen_freq && dev->listen_freq != freq) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Update Listen frequency based on scan " - "results (" MACSTR " %d -> %d MHz (DS param %d)", + if (dev->listen_freq && dev->listen_freq != freq && scan_res) { + p2p_dbg(p2p, "Update Listen frequency based on scan results (" + MACSTR " %d -> %d MHz (DS param %d)", MAC2STR(dev->info.p2p_device_addr), dev->listen_freq, freq, msg.ds_params ? *msg.ds_params : -1); } - dev->listen_freq = freq; - if (msg.group_info) - dev->oper_freq = freq; + if (scan_res) { + dev->listen_freq = freq; + if (msg.group_info) + dev->oper_freq = freq; + } dev->info.level = level; - p2p_copy_wps_info(dev, 0, &msg); + p2p_copy_wps_info(p2p, dev, 0, &msg); for (i = 0; i < P2P_MAX_WPS_VENDOR_EXT; i++) { wpabuf_free(dev->info.wps_vendor_ext[i]); @@ -663,8 +721,15 @@ break; } - p2p_add_group_clients(p2p, p2p_dev_addr, addr, freq, msg.group_info, - msg.group_info_len); + if (msg.wfd_subelems) { + wpabuf_free(dev->info.wfd_subelems); + dev->info.wfd_subelems = wpabuf_dup(msg.wfd_subelems); + } + + if (scan_res) { + p2p_add_group_clients(p2p, p2p_dev_addr, addr, freq, + msg.group_info, msg.group_info_len); + } p2p_parse_free(&msg); @@ -674,11 +739,32 @@ if (dev->flags & P2P_DEV_REPORTED) return 0; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Peer found with Listen frequency %d MHz", freq); + p2p_dbg(p2p, "Peer found with Listen frequency %d MHz (rx_time=%u.%06u)", + freq, (unsigned int) rx_time->sec, + (unsigned int) rx_time->usec); if (dev->flags & P2P_DEV_USER_REJECTED) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Do not report rejected device"); + p2p_dbg(p2p, "Do not report rejected device"); + return 0; + } + + if (dev->info.config_methods == 0 && + (freq == 2412 || freq == 2437 || freq == 2462)) { + /* + * If we have only seen a Beacon frame from a GO, we do not yet + * know what WPS config methods it supports. Since some + * applications use config_methods value from P2P-DEVICE-FOUND + * events, postpone reporting this peer until we've fully + * discovered its capabilities. + * + * At least for now, do this only if the peer was detected on + * one of the social channels since that peer can be easily be + * found again and there are no limitations of having to use + * passive scan on this channels, so this can be done through + * Probe Response frame that includes the config_methods + * information. + */ + p2p_dbg(p2p, "Do not report peer " MACSTR + " with unknown config methods", MAC2STR(addr)); return 0; } @@ -718,6 +804,8 @@ dev->info.wps_vendor_ext[i] = NULL; } + wpabuf_free(dev->info.wfd_subelems); + os_free(dev); } @@ -764,9 +852,8 @@ channel = c->reg_class[cl].channel[ch]; } - freq = p2p_channel_to_freq(p2p->cfg->country, reg_class, channel); - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Next progressive search " - "channel: reg_class %u channel %u -> %d MHz", + freq = p2p_channel_to_freq(reg_class, channel); + p2p_dbg(p2p, "Next progressive search channel: reg_class %u channel %u -> %d MHz", reg_class, channel, freq); p2p->last_prog_scan_class = reg_class; p2p->last_prog_scan_chan = channel; @@ -781,55 +868,32 @@ { int freq = 0; enum p2p_scan_type type; + u16 pw_id = DEV_PW_DEFAULT; + int res; if (p2p->drv_in_listen) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Driver is still " - "in Listen state - wait for it to end before " - "continuing"); + p2p_dbg(p2p, "Driver is still in Listen state - wait for it to end before continuing"); return; } p2p->cfg->stop_listen(p2p->cfg->cb_ctx); - if (p2p->go_neg_peer) { - /* - * Only scan the known listen frequency of the peer - * during GO Negotiation start. - */ - freq = p2p->go_neg_peer->listen_freq; - if (freq <= 0) - freq = p2p->go_neg_peer->oper_freq; - type = P2P_SCAN_SPECIFIC; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Starting search " - "for freq %u (GO Neg)", freq); - } else if (p2p->invite_peer) { - /* - * Only scan the known listen frequency of the peer - * during Invite start. - */ - freq = p2p->invite_peer->listen_freq; - if (freq <= 0) - freq = p2p->invite_peer->oper_freq; - type = P2P_SCAN_SPECIFIC; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Starting search " - "for freq %u (Invite)", freq); - } else if (p2p->find_type == P2P_FIND_PROGRESSIVE && - (freq = p2p_get_next_prog_freq(p2p)) > 0) { + if (p2p->find_type == P2P_FIND_PROGRESSIVE && + (freq = p2p_get_next_prog_freq(p2p)) > 0) { type = P2P_SCAN_SOCIAL_PLUS_ONE; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Starting search " - "(+ freq %u)", freq); + p2p_dbg(p2p, "Starting search (+ freq %u)", freq); } else { type = P2P_SCAN_SOCIAL; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Starting search"); + p2p_dbg(p2p, "Starting search"); } - if (p2p->cfg->p2p_scan(p2p->cfg->cb_ctx, type, freq, - p2p->num_req_dev_types, p2p->req_dev_types, - p2p->find_dev_id)) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Scan request failed"); + res = p2p->cfg->p2p_scan(p2p->cfg->cb_ctx, type, freq, + p2p->num_req_dev_types, p2p->req_dev_types, + p2p->find_dev_id, pw_id); + if (res < 0) { + p2p_dbg(p2p, "Scan request failed"); p2p_continue_find(p2p); } else { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Running p2p_scan"); + p2p_dbg(p2p, "Running p2p_scan"); p2p->p2p_scan_running = 1; eloop_cancel_timeout(p2p_scan_timeout, p2p, NULL); eloop_register_timeout(P2P_SCAN_TIMEOUT, 0, p2p_scan_timeout, @@ -841,7 +905,7 @@ static void p2p_find_timeout(void *eloop_ctx, void *timeout_ctx) { struct p2p_data *p2p = eloop_ctx; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Find timeout -> stop"); + p2p_dbg(p2p, "Find timeout -> stop"); p2p_stop_find(p2p); } @@ -852,10 +916,8 @@ enum p2p_after_scan op; if (p2p->after_scan_tx) { - /* TODO: schedule p2p_run_after_scan to be called from TX - * status callback(?) */ - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Send pending " - "Action frame at p2p_scan completion"); + p2p->after_scan_tx_in_progress = 1; + p2p_dbg(p2p, "Send pending Action frame at p2p_scan completion"); p2p->cfg->send_action(p2p->cfg->cb_ctx, p2p->after_scan_tx->freq, p2p->after_scan_tx->dst, @@ -875,19 +937,16 @@ case P2P_AFTER_SCAN_NOTHING: break; case P2P_AFTER_SCAN_LISTEN: - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Start previously " - "requested Listen state"); + p2p_dbg(p2p, "Start previously requested Listen state"); p2p_listen(p2p, p2p->pending_listen_sec * 1000 + p2p->pending_listen_usec / 1000); return 1; case P2P_AFTER_SCAN_CONNECT: - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Start previously " - "requested connect with " MACSTR, + p2p_dbg(p2p, "Start previously requested connect with " MACSTR, MAC2STR(p2p->after_scan_peer)); dev = p2p_get_device(p2p, p2p->after_scan_peer); if (dev == NULL) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Peer not " - "known anymore"); + p2p_dbg(p2p, "Peer not known anymore"); break; } p2p_connect_send(p2p, dev); @@ -902,8 +961,7 @@ { struct p2p_data *p2p = eloop_ctx; int running; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: p2p_scan timeout " - "(running=%d)", p2p->p2p_scan_running); + p2p_dbg(p2p, "p2p_scan timeout (running=%d)", p2p->p2p_scan_running); running = p2p->p2p_scan_running; /* Make sure we recover from missed scan results callback */ p2p->p2p_scan_running = 0; @@ -924,15 +982,14 @@ int p2p_find(struct p2p_data *p2p, unsigned int timeout, enum p2p_discovery_type type, unsigned int num_req_dev_types, const u8 *req_dev_types, - const u8 *dev_id) + const u8 *dev_id, unsigned int search_delay) { int res; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Starting find (type=%d)", - type); + p2p_dbg(p2p, "Starting find (type=%d)", type); + os_get_reltime(&p2p->find_start); if (p2p->p2p_scan_running) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: p2p_scan is " - "already running"); + p2p_dbg(p2p, "p2p_scan is already running"); } p2p_free_req_dev_types(p2p); @@ -958,6 +1015,8 @@ p2p->find_type = type; p2p_device_clear_reported(p2p); p2p_set_state(p2p, P2P_SEARCH); + p2p->search_delay = search_delay; + p2p->in_search_delay = 0; eloop_cancel_timeout(p2p_find_timeout, p2p, NULL); p2p->last_p2p_find_timeout = timeout; if (timeout) @@ -968,33 +1027,30 @@ case P2P_FIND_PROGRESSIVE: res = p2p->cfg->p2p_scan(p2p->cfg->cb_ctx, P2P_SCAN_FULL, 0, p2p->num_req_dev_types, - p2p->req_dev_types, dev_id); + p2p->req_dev_types, dev_id, + DEV_PW_DEFAULT); break; case P2P_FIND_ONLY_SOCIAL: res = p2p->cfg->p2p_scan(p2p->cfg->cb_ctx, P2P_SCAN_SOCIAL, 0, p2p->num_req_dev_types, - p2p->req_dev_types, dev_id); + p2p->req_dev_types, dev_id, + DEV_PW_DEFAULT); break; default: return -1; } if (res == 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Running p2p_scan"); + p2p_dbg(p2p, "Running p2p_scan"); p2p->p2p_scan_running = 1; eloop_cancel_timeout(p2p_scan_timeout, p2p, NULL); eloop_register_timeout(P2P_SCAN_TIMEOUT, 0, p2p_scan_timeout, p2p, NULL); - } else if (res == 1) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Could not start " - "p2p_scan at this point - will try again after " - "previous scan completes"); - res = 0; - p2p_set_state(p2p, P2P_SEARCH_WHEN_READY); - eloop_cancel_timeout(p2p_find_timeout, p2p, NULL); + } else if (p2p->p2p_scan_running) { + p2p_dbg(p2p, "Failed to start p2p_scan - another p2p_scan was already running"); + /* wait for the previous p2p_scan to complete */ } else { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Failed to start " - "p2p_scan"); + p2p_dbg(p2p, "Failed to start p2p_scan"); p2p_set_state(p2p, P2P_IDLE); eloop_cancel_timeout(p2p_find_timeout, p2p, NULL); } @@ -1003,30 +1059,18 @@ } -int p2p_other_scan_completed(struct p2p_data *p2p) -{ - if (p2p->state != P2P_SEARCH_WHEN_READY) - return 0; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Starting pending P2P find " - "now that previous scan was completed"); - if (p2p_find(p2p, p2p->last_p2p_find_timeout, p2p->find_type, - p2p->num_req_dev_types, p2p->req_dev_types, - p2p->find_dev_id) < 0) - return 0; - return 1; -} - - void p2p_stop_find_for_freq(struct p2p_data *p2p, int freq) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Stopping find"); + p2p_dbg(p2p, "Stopping find"); eloop_cancel_timeout(p2p_find_timeout, p2p, NULL); p2p_clear_timeout(p2p); if (p2p->state == P2P_SEARCH) - wpa_msg(p2p->cfg->msg_ctx, MSG_INFO, P2P_EVENT_FIND_STOPPED); + p2p->cfg->find_stopped(p2p->cfg->cb_ctx); p2p_set_state(p2p, P2P_IDLE); p2p_free_req_dev_types(p2p); p2p->start_after_scan = P2P_AFTER_SCAN_NOTHING; + if (p2p->go_neg_peer) + p2p->go_neg_peer->flags &= ~P2P_DEV_PEER_WAITING_RESPONSE; p2p->go_neg_peer = NULL; p2p->sd_peer = NULL; p2p->invite_peer = NULL; @@ -1037,8 +1081,7 @@ void p2p_stop_listen_for_freq(struct p2p_data *p2p, int freq) { if (freq > 0 && p2p->drv_in_listen == freq && p2p->in_listen) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Skip stop_listen " - "since we are on correct channel for response"); + p2p_dbg(p2p, "Skip stop_listen since we are on correct channel for response"); return; } if (p2p->in_listen) { @@ -1051,95 +1094,176 @@ * when the operation gets canceled, so clear the internal * variable that is tracking driver state. */ - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Clear " - "drv_in_listen (%d)", p2p->drv_in_listen); + p2p_dbg(p2p, "Clear drv_in_listen (%d)", p2p->drv_in_listen); p2p->drv_in_listen = 0; } p2p->cfg->stop_listen(p2p->cfg->cb_ctx); } +void p2p_stop_listen(struct p2p_data *p2p) +{ + if (p2p->state != P2P_LISTEN_ONLY) { + p2p_dbg(p2p, "Skip stop_listen since not in listen_only state."); + return; + } + + p2p_stop_listen_for_freq(p2p, 0); + p2p_set_state(p2p, P2P_IDLE); +} + + void p2p_stop_find(struct p2p_data *p2p) { p2p_stop_find_for_freq(p2p, 0); } -static int p2p_prepare_channel(struct p2p_data *p2p, unsigned int force_freq) -{ +static int p2p_prepare_channel_pref(struct p2p_data *p2p, + unsigned int force_freq, + unsigned int pref_freq, int go) +{ + u8 op_class, op_channel; + unsigned int freq = force_freq ? force_freq : pref_freq; + + p2p_dbg(p2p, "Prepare channel pref - force_freq=%u pref_freq=%u go=%d", + force_freq, pref_freq, go); + if (p2p_freq_to_channel(freq, &op_class, &op_channel) < 0) { + p2p_dbg(p2p, "Unsupported frequency %u MHz", freq); + return -1; + } + + if (!p2p_channels_includes(&p2p->cfg->channels, op_class, op_channel) && + (go || !p2p_channels_includes(&p2p->cfg->cli_channels, op_class, + op_channel))) { + p2p_dbg(p2p, "Frequency %u MHz (oper_class %u channel %u) not allowed for P2P", + freq, op_class, op_channel); + return -1; + } + + p2p->op_reg_class = op_class; + p2p->op_channel = op_channel; + if (force_freq) { - u8 op_reg_class, op_channel; - if (p2p_freq_to_channel(p2p->cfg->country, force_freq, - &op_reg_class, &op_channel) < 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Unsupported frequency %u MHz", - force_freq); - return -1; - } - if (!p2p_channels_includes(&p2p->cfg->channels, op_reg_class, - op_channel)) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Frequency %u MHz (oper_class %u " - "channel %u) not allowed for P2P", - force_freq, op_reg_class, op_channel); - return -1; - } - p2p->op_reg_class = op_reg_class; - p2p->op_channel = op_channel; p2p->channels.reg_classes = 1; p2p->channels.reg_class[0].channels = 1; p2p->channels.reg_class[0].reg_class = p2p->op_reg_class; p2p->channels.reg_class[0].channel[0] = p2p->op_channel; } else { - u8 op_reg_class, op_channel; - - if (!p2p->cfg->cfg_op_channel && p2p->best_freq_overall > 0 && - p2p_supported_freq(p2p, p2p->best_freq_overall) && - p2p_freq_to_channel(p2p->cfg->country, - p2p->best_freq_overall, - &op_reg_class, &op_channel) == 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Select best overall channel as " - "operating channel preference"); - p2p->op_reg_class = op_reg_class; - p2p->op_channel = op_channel; - } else if (!p2p->cfg->cfg_op_channel && p2p->best_freq_5 > 0 && - p2p_supported_freq(p2p, p2p->best_freq_5) && - p2p_freq_to_channel(p2p->cfg->country, - p2p->best_freq_5, - &op_reg_class, &op_channel) == - 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Select best 5 GHz channel as " - "operating channel preference"); - p2p->op_reg_class = op_reg_class; - p2p->op_channel = op_channel; - } else if (!p2p->cfg->cfg_op_channel && - p2p->best_freq_24 > 0 && - p2p_supported_freq(p2p, p2p->best_freq_24) && - p2p_freq_to_channel(p2p->cfg->country, - p2p->best_freq_24, - &op_reg_class, &op_channel) == - 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Select best 2.4 GHz channel as " - "operating channel preference"); - p2p->op_reg_class = op_reg_class; - p2p->op_channel = op_channel; - } else { - p2p->op_reg_class = p2p->cfg->op_reg_class; - p2p->op_channel = p2p->cfg->op_channel; - } - os_memcpy(&p2p->channels, &p2p->cfg->channels, sizeof(struct p2p_channels)); } - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Own preference for operation channel: " - "Operating Class %u Channel %u%s", + + return 0; +} + + +static void p2p_prepare_channel_best(struct p2p_data *p2p) +{ + u8 op_class, op_channel; + const int op_classes_5ghz[] = { 124, 115, 0 }; + const int op_classes_ht40[] = { 126, 127, 116, 117, 0 }; + const int op_classes_vht[] = { 128, 0 }; + + p2p_dbg(p2p, "Prepare channel best"); + + if (!p2p->cfg->cfg_op_channel && p2p->best_freq_overall > 0 && + p2p_supported_freq(p2p, p2p->best_freq_overall) && + p2p_freq_to_channel(p2p->best_freq_overall, &op_class, &op_channel) + == 0) { + p2p_dbg(p2p, "Select best overall channel as operating channel preference"); + p2p->op_reg_class = op_class; + p2p->op_channel = op_channel; + } else if (!p2p->cfg->cfg_op_channel && p2p->best_freq_5 > 0 && + p2p_supported_freq(p2p, p2p->best_freq_5) && + p2p_freq_to_channel(p2p->best_freq_5, &op_class, &op_channel) + == 0) { + p2p_dbg(p2p, "Select best 5 GHz channel as operating channel preference"); + p2p->op_reg_class = op_class; + p2p->op_channel = op_channel; + } else if (!p2p->cfg->cfg_op_channel && p2p->best_freq_24 > 0 && + p2p_supported_freq(p2p, p2p->best_freq_24) && + p2p_freq_to_channel(p2p->best_freq_24, &op_class, + &op_channel) == 0) { + p2p_dbg(p2p, "Select best 2.4 GHz channel as operating channel preference"); + p2p->op_reg_class = op_class; + p2p->op_channel = op_channel; + } else if (p2p->cfg->num_pref_chan > 0 && + p2p_channels_includes(&p2p->cfg->channels, + p2p->cfg->pref_chan[0].op_class, + p2p->cfg->pref_chan[0].chan)) { + p2p_dbg(p2p, "Select first pref_chan entry as operating channel preference"); + p2p->op_reg_class = p2p->cfg->pref_chan[0].op_class; + p2p->op_channel = p2p->cfg->pref_chan[0].chan; + } else if (p2p_channel_select(&p2p->cfg->channels, op_classes_vht, + &p2p->op_reg_class, &p2p->op_channel) == + 0) { + p2p_dbg(p2p, "Select possible VHT channel (op_class %u channel %u) as operating channel preference", + p2p->op_reg_class, p2p->op_channel); + } else if (p2p_channel_select(&p2p->cfg->channels, op_classes_ht40, + &p2p->op_reg_class, &p2p->op_channel) == + 0) { + p2p_dbg(p2p, "Select possible HT40 channel (op_class %u channel %u) as operating channel preference", + p2p->op_reg_class, p2p->op_channel); + } else if (p2p_channel_select(&p2p->cfg->channels, op_classes_5ghz, + &p2p->op_reg_class, &p2p->op_channel) == + 0) { + p2p_dbg(p2p, "Select possible 5 GHz channel (op_class %u channel %u) as operating channel preference", + p2p->op_reg_class, p2p->op_channel); + } else { + p2p_dbg(p2p, "Select pre-configured channel as operating channel preference"); + p2p->op_reg_class = p2p->cfg->op_reg_class; + p2p->op_channel = p2p->cfg->op_channel; + } + + os_memcpy(&p2p->channels, &p2p->cfg->channels, + sizeof(struct p2p_channels)); +} + + +/** + * p2p_prepare_channel - Select operating channel for GO Negotiation + * @p2p: P2P module context from p2p_init() + * @dev: Selected peer device + * @force_freq: Forced frequency in MHz or 0 if not forced + * @pref_freq: Preferred frequency in MHz or 0 if no preference + * @go: Whether the local end will be forced to be GO + * Returns: 0 on success, -1 on failure (channel not supported for P2P) + * + * This function is used to do initial operating channel selection for GO + * Negotiation prior to having received peer information. The selected channel + * may be further optimized in p2p_reselect_channel() once the peer information + * is available. + */ +int p2p_prepare_channel(struct p2p_data *p2p, struct p2p_device *dev, + unsigned int force_freq, unsigned int pref_freq, int go) +{ + p2p_dbg(p2p, "Prepare channel - force_freq=%u pref_freq=%u go=%d", + force_freq, pref_freq, go); + if (force_freq || pref_freq) { + if (p2p_prepare_channel_pref(p2p, force_freq, pref_freq, go) < + 0) + return -1; + } else { + p2p_prepare_channel_best(p2p); + } + p2p_channels_dump(p2p, "prepared channels", &p2p->channels); + if (go) + p2p_channels_remove_freqs(&p2p->channels, &p2p->no_go_freq); + else if (!force_freq) + p2p_channels_union(&p2p->channels, &p2p->cfg->cli_channels, + &p2p->channels); + p2p_channels_dump(p2p, "after go/cli filter/add", &p2p->channels); + + p2p_dbg(p2p, "Own preference for operation channel: Operating Class %u Channel %u%s", p2p->op_reg_class, p2p->op_channel, force_freq ? " (forced)" : ""); + if (force_freq) + dev->flags |= P2P_DEV_FORCE_FREQ; + else + dev->flags &= ~P2P_DEV_FORCE_FREQ; + return 0; } @@ -1167,41 +1291,40 @@ int p2p_connect(struct p2p_data *p2p, const u8 *peer_addr, enum p2p_wps_method wps_method, int go_intent, const u8 *own_interface_addr, - unsigned int force_freq, int persistent_group) + unsigned int force_freq, int persistent_group, + const u8 *force_ssid, size_t force_ssid_len, + int pd_before_go_neg, unsigned int pref_freq, u16 oob_pw_id) { struct p2p_device *dev; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Request to start group negotiation - peer=" MACSTR + p2p_dbg(p2p, "Request to start group negotiation - peer=" MACSTR " GO Intent=%d Intended Interface Address=" MACSTR - " wps_method=%d persistent_group=%d", + " wps_method=%d persistent_group=%d pd_before_go_neg=%d " + "oob_pw_id=%u", MAC2STR(peer_addr), go_intent, MAC2STR(own_interface_addr), - wps_method, persistent_group); + wps_method, persistent_group, pd_before_go_neg, oob_pw_id); - if (p2p_prepare_channel(p2p, force_freq) < 0) - return -1; - - p2p->ssid_set = 0; dev = p2p_get_device(p2p, peer_addr); if (dev == NULL || (dev->flags & P2P_DEV_PROBE_REQ_ONLY)) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Cannot connect to unknown P2P Device " MACSTR, + p2p_dbg(p2p, "Cannot connect to unknown P2P Device " MACSTR, MAC2STR(peer_addr)); return -1; } + if (p2p_prepare_channel(p2p, dev, force_freq, pref_freq, + go_intent == 15) < 0) + return -1; + if (dev->flags & P2P_DEV_GROUP_CLIENT_ONLY) { if (!(dev->info.dev_capab & P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY)) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Cannot connect to P2P Device " MACSTR + p2p_dbg(p2p, "Cannot connect to P2P Device " MACSTR " that is in a group and is not discoverable", MAC2STR(peer_addr)); return -1; } if (dev->oper_freq <= 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Cannot connect to P2P Device " MACSTR + p2p_dbg(p2p, "Cannot connect to P2P Device " MACSTR " with incomplete information", MAC2STR(peer_addr)); return -1; @@ -1214,10 +1337,33 @@ */ } + p2p->ssid_set = 0; + if (force_ssid) { + wpa_hexdump_ascii(MSG_DEBUG, "P2P: Forced SSID", + force_ssid, force_ssid_len); + os_memcpy(p2p->ssid, force_ssid, force_ssid_len); + p2p->ssid_len = force_ssid_len; + p2p->ssid_set = 1; + } + dev->flags &= ~P2P_DEV_NOT_YET_READY; dev->flags &= ~P2P_DEV_USER_REJECTED; dev->flags &= ~P2P_DEV_WAIT_GO_NEG_RESPONSE; dev->flags &= ~P2P_DEV_WAIT_GO_NEG_CONFIRM; + if (pd_before_go_neg) + dev->flags |= P2P_DEV_PD_BEFORE_GO_NEG; + else { + dev->flags &= ~P2P_DEV_PD_BEFORE_GO_NEG; + /* + * Assign dialog token and tie breaker here to use the same + * values in each retry within the same GO Negotiation exchange. + */ + dev->dialog_token++; + if (dev->dialog_token == 0) + dev->dialog_token = 1; + dev->tie_breaker = p2p->next_tie_breaker; + p2p->next_tie_breaker = !p2p->next_tie_breaker; + } dev->connect_reqs = 0; dev->go_neg_req_sent = 0; dev->go_state = UNKNOWN_GO; @@ -1234,24 +1380,17 @@ * new GO Negotiation, e.g., when the pending frame was from a * previous attempt at starting a GO Negotiation. */ - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Dropped " - "previous pending Action frame TX that was waiting " - "for p2p_scan completion"); + p2p_dbg(p2p, "Dropped previous pending Action frame TX that was waiting for p2p_scan completion"); os_free(p2p->after_scan_tx); p2p->after_scan_tx = NULL; } dev->wps_method = wps_method; + dev->oob_pw_id = oob_pw_id; dev->status = P2P_SC_SUCCESS; - if (force_freq) - dev->flags |= P2P_DEV_FORCE_FREQ; - else - dev->flags &= ~P2P_DEV_FORCE_FREQ; - if (p2p->p2p_scan_running) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: p2p_scan running - delay connect send"); + p2p_dbg(p2p, "p2p_scan running - delay connect send"); p2p->start_after_scan = P2P_AFTER_SCAN_CONNECT; os_memcpy(p2p->after_scan_peer, peer_addr, ETH_ALEN); return 0; @@ -1265,28 +1404,38 @@ int p2p_authorize(struct p2p_data *p2p, const u8 *peer_addr, enum p2p_wps_method wps_method, int go_intent, const u8 *own_interface_addr, - unsigned int force_freq, int persistent_group) + unsigned int force_freq, int persistent_group, + const u8 *force_ssid, size_t force_ssid_len, + unsigned int pref_freq, u16 oob_pw_id) { struct p2p_device *dev; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Request to authorize group negotiation - peer=" MACSTR + p2p_dbg(p2p, "Request to authorize group negotiation - peer=" MACSTR " GO Intent=%d Intended Interface Address=" MACSTR - " wps_method=%d persistent_group=%d", + " wps_method=%d persistent_group=%d oob_pw_id=%u", MAC2STR(peer_addr), go_intent, MAC2STR(own_interface_addr), - wps_method, persistent_group); - - if (p2p_prepare_channel(p2p, force_freq) < 0) - return -1; + wps_method, persistent_group, oob_pw_id); dev = p2p_get_device(p2p, peer_addr); if (dev == NULL) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Cannot authorize unknown P2P Device " MACSTR, + p2p_dbg(p2p, "Cannot authorize unknown P2P Device " MACSTR, MAC2STR(peer_addr)); return -1; } + if (p2p_prepare_channel(p2p, dev, force_freq, pref_freq, go_intent == + 15) < 0) + return -1; + + p2p->ssid_set = 0; + if (force_ssid) { + wpa_hexdump_ascii(MSG_DEBUG, "P2P: Forced SSID", + force_ssid, force_ssid_len); + os_memcpy(p2p->ssid, force_ssid, force_ssid_len); + p2p->ssid_len = force_ssid_len; + p2p->ssid_set = 1; + } + dev->flags &= ~P2P_DEV_NOT_YET_READY; dev->flags &= ~P2P_DEV_USER_REJECTED; dev->go_neg_req_sent = 0; @@ -1296,13 +1445,9 @@ os_memcpy(p2p->intended_addr, own_interface_addr, ETH_ALEN); dev->wps_method = wps_method; + dev->oob_pw_id = oob_pw_id; dev->status = P2P_SC_SUCCESS; - if (force_freq) - dev->flags |= P2P_DEV_FORCE_FREQ; - else - dev->flags &= ~P2P_DEV_FORCE_FREQ; - return 0; } @@ -1310,18 +1455,16 @@ void p2p_add_dev_info(struct p2p_data *p2p, const u8 *addr, struct p2p_device *dev, struct p2p_message *msg) { - os_get_time(&dev->last_seen); + os_get_reltime(&dev->last_seen); - p2p_copy_wps_info(dev, 0, msg); + p2p_copy_wps_info(p2p, dev, 0, msg); if (msg->listen_channel) { int freq; - freq = p2p_channel_to_freq((char *) msg->listen_channel, - msg->listen_channel[3], + freq = p2p_channel_to_freq(msg->listen_channel[3], msg->listen_channel[4]); if (freq < 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Unknown peer Listen channel: " + p2p_dbg(p2p, "Unknown peer Listen channel: " "country=%c%c(0x%02x) reg_class=%u channel=%u", msg->listen_channel[0], msg->listen_channel[1], @@ -1329,22 +1472,24 @@ msg->listen_channel[3], msg->listen_channel[4]); } else { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Update " - "peer " MACSTR " Listen channel: %u -> %u MHz", + p2p_dbg(p2p, "Update peer " MACSTR + " Listen channel: %u -> %u MHz", MAC2STR(dev->info.p2p_device_addr), dev->listen_freq, freq); dev->listen_freq = freq; } } + if (msg->wfd_subelems) { + wpabuf_free(dev->info.wfd_subelems); + dev->info.wfd_subelems = wpabuf_dup(msg->wfd_subelems); + } + if (dev->flags & P2P_DEV_PROBE_REQ_ONLY) { dev->flags &= ~P2P_DEV_PROBE_REQ_ONLY; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Completed device entry based on data from " - "GO Negotiation Request"); + p2p_dbg(p2p, "Completed device entry based on data from GO Negotiation Request"); } else { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Created device entry based on GO Neg Req: " + p2p_dbg(p2p, "Created device entry based on GO Neg Req: " MACSTR " dev_capab=0x%x group_capab=0x%x name='%s' " "listen_freq=%d", MAC2STR(dev->info.p2p_device_addr), @@ -1355,8 +1500,7 @@ dev->flags &= ~P2P_DEV_GROUP_CLIENT_ONLY; if (dev->flags & P2P_DEV_USER_REJECTED) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Do not report rejected device"); + p2p_dbg(p2p, "Do not report rejected device"); return; } @@ -1392,10 +1536,8 @@ int freqs; size_t i, j; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: GO Negotiation with " MACSTR " completed (%s will be " - "GO)", MAC2STR(peer->info.p2p_device_addr), - go ? "local end" : "peer"); + p2p_dbg(p2p, "GO Negotiation with " MACSTR " completed (%s will be GO)", + MAC2STR(peer->info.p2p_device_addr), go ? "local end" : "peer"); os_memset(&res, 0, sizeof(res)); res.role_go = go; @@ -1411,8 +1553,7 @@ if (go) { /* Setup AP mode for WPS provisioning */ - res.freq = p2p_channel_to_freq(p2p->cfg->country, - p2p->op_reg_class, + res.freq = p2p_channel_to_freq(p2p->op_reg_class, p2p->op_channel); os_memcpy(res.ssid, p2p->ssid, p2p->ssid_len); res.ssid_len = p2p->ssid_len; @@ -1425,8 +1566,15 @@ } } + p2p_channels_dump(p2p, "own channels", &p2p->channels); + p2p_channels_dump(p2p, "peer channels", &peer->channels); p2p_channels_intersect(&p2p->channels, &peer->channels, &intersection); + if (go) { + p2p_channels_remove_freqs(&intersection, &p2p->no_go_freq); + p2p_channels_dump(p2p, "intersection after no-GO removal", + &intersection); + } freqs = 0; for (i = 0; i < intersection.reg_classes; i++) { struct p2p_reg_class *c = &intersection.reg_class[i]; @@ -1436,8 +1584,7 @@ int freq; if (freqs + 1 == P2P_MAX_CHANNELS) break; - freq = p2p_channel_to_freq(peer->country, c->reg_class, - c->channel[j]); + freq = p2p_channel_to_freq(c->reg_class, c->channel[j]); if (freq < 0) continue; res.freq_list[freqs++] = freq; @@ -1450,6 +1597,7 @@ p2p->ssid_set = 0; peer->go_neg_req_sent = 0; peer->wps_method = WPS_NOT_READY; + peer->oob_pw_id = 0; p2p_set_state(p2p, P2P_PROVISIONING); p2p->cfg->go_neg_completed(p2p->cfg->cb_ctx, &res); @@ -1459,8 +1607,7 @@ static void p2p_rx_p2p_action(struct p2p_data *p2p, const u8 *sa, const u8 *data, size_t len, int rx_freq) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: RX P2P Public Action from " MACSTR, MAC2STR(sa)); + p2p_dbg(p2p, "RX P2P Public Action from " MACSTR, MAC2STR(sa)); wpa_hexdump(MSG_MSGDUMP, "P2P: P2P Public Action contents", data, len); if (len < 1) @@ -1481,6 +1628,7 @@ rx_freq); break; case P2P_INVITATION_RESP: + p2p->cfg->send_action_done(p2p->cfg->cb_ctx); p2p_process_invitation_resp(p2p, sa, data + 1, len - 1); break; case P2P_PROV_DISC_REQ: @@ -1496,17 +1644,16 @@ p2p_process_dev_disc_resp(p2p, sa, data + 1, len - 1); break; default: - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Unsupported P2P Public Action frame type %d", + p2p_dbg(p2p, "Unsupported P2P Public Action frame type %d", data[0]); break; } } -void p2p_rx_action_public(struct p2p_data *p2p, const u8 *da, const u8 *sa, - const u8 *bssid, const u8 *data, size_t len, - int freq) +static void p2p_rx_action_public(struct p2p_data *p2p, const u8 *da, + const u8 *sa, const u8 *bssid, const u8 *data, + size_t len, int freq) { if (len < 1) return; @@ -1572,16 +1719,14 @@ len--; /* P2P action frame */ - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: RX P2P Action from " MACSTR, MAC2STR(sa)); + p2p_dbg(p2p, "RX P2P Action from " MACSTR, MAC2STR(sa)); wpa_hexdump(MSG_MSGDUMP, "P2P: P2P Action contents", data, len); if (len < 1) return; switch (data[0]) { case P2P_NOA: - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Received P2P Action - Notice of Absence"); + p2p_dbg(p2p, "Received P2P Action - Notice of Absence"); /* TODO */ break; case P2P_PRESENCE_REQ: @@ -1594,8 +1739,7 @@ p2p_process_go_disc_req(p2p, da, sa, data + 1, len - 1, freq); break; default: - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Received P2P Action - unknown type %u", data[0]); + p2p_dbg(p2p, "Received P2P Action - unknown type %u", data[0]); break; } } @@ -1618,7 +1762,8 @@ if (p2p->invite_peer == NULL) return; p2p->cfg->stop_listen(p2p->cfg->cb_ctx); - p2p_invite_send(p2p, p2p->invite_peer, p2p->invite_go_dev_addr); + p2p_invite_send(p2p, p2p->invite_peer, p2p->invite_go_dev_addr, + p2p->invite_dev_pw_id); } @@ -1651,7 +1796,7 @@ if (dev) { if (dev->country[0] == 0 && msg.listen_channel) os_memcpy(dev->country, msg.listen_channel, 3); - os_get_time(&dev->last_seen); + os_get_reltime(&dev->last_seen); p2p_parse_free(&msg); return; /* already known */ } @@ -1662,22 +1807,25 @@ return; } - os_get_time(&dev->last_seen); + os_get_reltime(&dev->last_seen); dev->flags |= P2P_DEV_PROBE_REQ_ONLY; if (msg.listen_channel) { os_memcpy(dev->country, msg.listen_channel, 3); - dev->listen_freq = p2p_channel_to_freq(dev->country, - msg.listen_channel[3], + dev->listen_freq = p2p_channel_to_freq(msg.listen_channel[3], msg.listen_channel[4]); } - p2p_copy_wps_info(dev, 1, &msg); + p2p_copy_wps_info(p2p, dev, 1, &msg); + + if (msg.wfd_subelems) { + wpabuf_free(dev->info.wfd_subelems); + dev->info.wfd_subelems = wpabuf_dup(msg.wfd_subelems); + } p2p_parse_free(&msg); - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Created device entry based on Probe Req: " MACSTR + p2p_dbg(p2p, "Created device entry based on Probe Req: " MACSTR " dev_capab=0x%x group_capab=0x%x name='%s' listen_freq=%d", MAC2STR(dev->info.p2p_device_addr), dev->info.dev_capab, dev->info.group_capab, dev->info.device_name, @@ -1693,7 +1841,7 @@ dev = p2p_get_device(p2p, addr); if (dev) { - os_get_time(&dev->last_seen); + os_get_reltime(&dev->last_seen); return dev; /* already known */ } @@ -1771,16 +1919,38 @@ { struct wpabuf *buf; u8 *len; + int pw_id = -1; + size_t extra = 0; - buf = wpabuf_alloc(1000); +#ifdef CONFIG_WIFI_DISPLAY + if (p2p->wfd_ie_probe_resp) + extra = wpabuf_len(p2p->wfd_ie_probe_resp); +#endif /* CONFIG_WIFI_DISPLAY */ + + buf = wpabuf_alloc(1000 + extra); if (buf == NULL) return NULL; - p2p_build_wps_ie(p2p, buf, DEV_PW_DEFAULT, 1); + if (p2p->go_neg_peer) { + /* Advertise immediate availability of WPS credential */ + pw_id = p2p_wps_method_pw_id(p2p->go_neg_peer->wps_method); + } + + if (p2p_build_wps_ie(p2p, buf, pw_id, 1) < 0) { + p2p_dbg(p2p, "Failed to build WPS IE for Probe Response"); + wpabuf_free(buf); + return NULL; + } + +#ifdef CONFIG_WIFI_DISPLAY + if (p2p->wfd_ie_probe_resp) + wpabuf_put_buf(buf, p2p->wfd_ie_probe_resp); +#endif /* CONFIG_WIFI_DISPLAY */ /* P2P IE */ len = p2p_buf_add_ie_hdr(buf); - p2p_buf_add_capability(buf, p2p->dev_capab, 0); + p2p_buf_add_capability(buf, p2p->dev_capab & + ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY, 0); if (p2p->ext_listen_interval) p2p_buf_add_ext_listen_timing(buf, p2p->ext_listen_period, p2p->ext_listen_interval); @@ -1791,42 +1961,9 @@ } -static int is_11b(u8 rate) -{ - return rate == 0x02 || rate == 0x04 || rate == 0x0b || rate == 0x16; -} - - -static int supp_rates_11b_only(struct ieee802_11_elems *elems) -{ - int num_11b = 0, num_others = 0; - int i; - - if (elems->supp_rates == NULL && elems->ext_supp_rates == NULL) - return 0; - - for (i = 0; elems->supp_rates && i < elems->supp_rates_len; i++) { - if (is_11b(elems->supp_rates[i])) - num_11b++; - else - num_others++; - } - - for (i = 0; elems->ext_supp_rates && i < elems->ext_supp_rates_len; - i++) { - if (is_11b(elems->ext_supp_rates[i])) - num_11b++; - else - num_others++; - } - - return num_11b > 0 && num_others == 0; -} - - -static void p2p_reply_probe(struct p2p_data *p2p, const u8 *addr, - const u8 *dst, const u8 *bssid, const u8 *ie, - size_t ie_len) +static enum p2p_probe_req_status +p2p_reply_probe(struct p2p_data *p2p, const u8 *addr, const u8 *dst, + const u8 *bssid, const u8 *ie, size_t ie_len) { struct ieee802_11_elems elems; struct wpabuf *buf; @@ -1836,55 +1973,55 @@ if (!p2p->in_listen || !p2p->drv_in_listen) { /* not in Listen state - ignore Probe Request */ - return; + return P2P_PREQ_NOT_LISTEN; } if (ieee802_11_parse_elems((u8 *) ie, ie_len, &elems, 0) == ParseFailed) { /* Ignore invalid Probe Request frames */ - return; + return P2P_PREQ_MALFORMED; } if (elems.p2p == NULL) { /* not a P2P probe - ignore it */ - return; + return P2P_PREQ_NOT_P2P; } if (dst && !is_broadcast_ether_addr(dst) && os_memcmp(dst, p2p->cfg->dev_addr, ETH_ALEN) != 0) { /* Not sent to the broadcast address or our P2P Device Address */ - return; + return P2P_PREQ_NOT_PROCESSED; } if (bssid && !is_broadcast_ether_addr(bssid)) { /* Not sent to the Wildcard BSSID */ - return; + return P2P_PREQ_NOT_PROCESSED; } if (elems.ssid == NULL || elems.ssid_len != P2P_WILDCARD_SSID_LEN || os_memcmp(elems.ssid, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN) != 0) { /* not using P2P Wildcard SSID - ignore */ - return; + return P2P_PREQ_NOT_PROCESSED; } if (supp_rates_11b_only(&elems)) { /* Indicates support for 11b rates only */ - return; + return P2P_PREQ_NOT_P2P; } os_memset(&msg, 0, sizeof(msg)); if (p2p_parse_ies(ie, ie_len, &msg) < 0) { /* Could not parse P2P attributes */ - return; + return P2P_PREQ_NOT_P2P; } if (msg.device_id && - os_memcmp(msg.device_id, p2p->cfg->dev_addr, ETH_ALEN != 0)) { + os_memcmp(msg.device_id, p2p->cfg->dev_addr, ETH_ALEN) != 0) { /* Device ID did not match */ p2p_parse_free(&msg); - return; + return P2P_PREQ_NOT_PROCESSED; } /* Check Requested Device Type match */ @@ -1892,15 +2029,16 @@ !p2p_match_dev_type(p2p, msg.wps_attributes)) { /* No match with Requested Device Type */ p2p_parse_free(&msg); - return; + return P2P_PREQ_NOT_PROCESSED; } p2p_parse_free(&msg); - if (!p2p->cfg->send_probe_resp) - return; /* Response generated elsewhere */ + if (!p2p->cfg->send_probe_resp) { + /* Response generated elsewhere */ + return P2P_PREQ_NOT_PROCESSED; + } - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Reply to P2P Probe Request in Listen state"); + p2p_dbg(p2p, "Reply to P2P Probe Request in Listen state"); /* * We do not really have a specific BSS that this frame is advertising, @@ -1910,12 +2048,12 @@ */ ies = p2p_build_probe_resp_ies(p2p); if (ies == NULL) - return; + return P2P_PREQ_NOT_PROCESSED; buf = wpabuf_alloc(200 + wpabuf_len(ies)); if (buf == NULL) { wpabuf_free(ies); - return; + return P2P_PREQ_NOT_PROCESSED; } resp = NULL; @@ -1958,26 +2096,31 @@ p2p->cfg->send_probe_resp(p2p->cfg->cb_ctx, buf); wpabuf_free(buf); + + return P2P_PREQ_NOT_PROCESSED; } -int p2p_probe_req_rx(struct p2p_data *p2p, const u8 *addr, const u8 *dst, - const u8 *bssid, const u8 *ie, size_t ie_len) +enum p2p_probe_req_status +p2p_probe_req_rx(struct p2p_data *p2p, const u8 *addr, const u8 *dst, + const u8 *bssid, const u8 *ie, size_t ie_len) { + enum p2p_probe_req_status res; + p2p_add_dev_from_probe_req(p2p, addr, ie, ie_len); - p2p_reply_probe(p2p, addr, dst, bssid, ie, ie_len); + res = p2p_reply_probe(p2p, addr, dst, bssid, ie, ie_len); if ((p2p->state == P2P_CONNECT || p2p->state == P2P_CONNECT_LISTEN) && p2p->go_neg_peer && os_memcmp(addr, p2p->go_neg_peer->info.p2p_device_addr, ETH_ALEN) - == 0) { + == 0 && + !(p2p->go_neg_peer->flags & P2P_DEV_WAIT_GO_NEG_CONFIRM)) { /* Received a Probe Request from GO Negotiation peer */ - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Found GO Negotiation peer - try to start GO " - "negotiation from timeout"); + p2p_dbg(p2p, "Found GO Negotiation peer - try to start GO negotiation from timeout"); + eloop_cancel_timeout(p2p_go_neg_start, p2p, NULL); eloop_register_timeout(0, 0, p2p_go_neg_start, p2p, NULL); - return 1; + return P2P_PREQ_PROCESSED; } if ((p2p->state == P2P_INVITE || p2p->state == P2P_INVITE_LISTEN) && @@ -1985,14 +2128,12 @@ os_memcmp(addr, p2p->invite_peer->info.p2p_device_addr, ETH_ALEN) == 0) { /* Received a Probe Request from Invite peer */ - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Found Invite peer - try to start Invite from " - "timeout"); + p2p_dbg(p2p, "Found Invite peer - try to start Invite from timeout"); eloop_register_timeout(0, 0, p2p_invite_start, p2p, NULL); - return 1; + return P2P_PREQ_PROCESSED; } - return 0; + return res; } @@ -2054,20 +2195,31 @@ struct p2p_device *peer; size_t tmplen; int res; + size_t extra = 0; if (!p2p_group) return p2p_assoc_req_ie_wlan_ap(p2p, bssid, buf, len, p2p_ie); +#ifdef CONFIG_WIFI_DISPLAY + if (p2p->wfd_ie_assoc_req) + extra = wpabuf_len(p2p->wfd_ie_assoc_req); +#endif /* CONFIG_WIFI_DISPLAY */ + /* * (Re)Association Request - P2P IE * P2P Capability attribute (shall be present) * Extended Listen Timing (may be present) * P2P Device Info attribute (shall be present) */ - tmp = wpabuf_alloc(200); + tmp = wpabuf_alloc(200 + extra); if (tmp == NULL) return -1; +#ifdef CONFIG_WIFI_DISPLAY + if (p2p->wfd_ie_assoc_req) + wpabuf_put_buf(tmp, p2p->wfd_ie_assoc_req); +#endif /* CONFIG_WIFI_DISPLAY */ + peer = bssid ? p2p_get_device(p2p, bssid) : NULL; lpos = p2p_buf_add_ie_hdr(tmp); @@ -2106,30 +2258,35 @@ } -int p2p_parse_dev_addr(const u8 *ies, size_t ies_len, u8 *dev_addr) +int p2p_parse_dev_addr_in_p2p_ie(struct wpabuf *p2p_ie, u8 *dev_addr) { - struct wpabuf *p2p_ie; struct p2p_message msg; - int ret = -1; - p2p_ie = ieee802_11_vendor_ie_concat(ies, ies_len, - P2P_IE_VENDOR_TYPE); - if (p2p_ie == NULL) - return -1; os_memset(&msg, 0, sizeof(msg)); - if (p2p_parse_p2p_ie(p2p_ie, &msg)) { - wpabuf_free(p2p_ie); + if (p2p_parse_p2p_ie(p2p_ie, &msg)) return -1; - } if (msg.p2p_device_addr) { os_memcpy(dev_addr, msg.p2p_device_addr, ETH_ALEN); - ret = 0; + return 0; } else if (msg.device_id) { os_memcpy(dev_addr, msg.device_id, ETH_ALEN); - ret = 0; + return 0; } + return -1; +} + + +int p2p_parse_dev_addr(const u8 *ies, size_t ies_len, u8 *dev_addr) +{ + struct wpabuf *p2p_ie; + int ret; + p2p_ie = ieee802_11_vendor_ie_concat(ies, ies_len, + P2P_IE_VENDOR_TYPE); + if (p2p_ie == NULL) + return -1; + ret = p2p_parse_dev_addr_in_p2p_ie(p2p_ie, dev_addr); wpabuf_free(p2p_ie); return ret; } @@ -2146,24 +2303,20 @@ void p2p_wps_success_cb(struct p2p_data *p2p, const u8 *mac_addr) { if (p2p->go_neg_peer == NULL) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: No pending Group Formation - " - "ignore WPS registration success notification"); + p2p_dbg(p2p, "No pending Group Formation - ignore WPS registration success notification"); return; /* No pending Group Formation */ } if (os_memcmp(mac_addr, p2p->go_neg_peer->intended_addr, ETH_ALEN) != 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Ignore WPS registration success notification " - "for " MACSTR " (GO Negotiation peer " MACSTR ")", + p2p_dbg(p2p, "Ignore WPS registration success notification for " + MACSTR " (GO Negotiation peer " MACSTR ")", MAC2STR(mac_addr), MAC2STR(p2p->go_neg_peer->intended_addr)); return; /* Ignore unexpected peer address */ } - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Group Formation completed successfully with " MACSTR, + p2p_dbg(p2p, "Group Formation completed successfully with " MACSTR, MAC2STR(mac_addr)); p2p_clear_go_neg(p2p); @@ -2173,14 +2326,11 @@ void p2p_group_formation_failed(struct p2p_data *p2p) { if (p2p->go_neg_peer == NULL) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: No pending Group Formation - " - "ignore group formation failure notification"); + p2p_dbg(p2p, "No pending Group Formation - ignore group formation failure notification"); return; /* No pending Group Formation */ } - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Group Formation failed with " MACSTR, + p2p_dbg(p2p, "Group Formation failed with " MACSTR, MAC2STR(p2p->go_neg_peer->intended_addr)); p2p_clear_go_neg(p2p); @@ -2222,6 +2372,7 @@ p2p->min_disc_int = 1; p2p->max_disc_int = 3; + p2p->max_disc_tu = -1; os_get_random(&p2p->next_tie_breaker, 1); p2p->next_tie_breaker &= 0x01; @@ -2237,15 +2388,37 @@ eloop_register_timeout(P2P_PEER_EXPIRATION_INTERVAL, 0, p2p_expiration_timeout, p2p, NULL); + p2p->go_timeout = 100; + p2p->client_timeout = 20; + + p2p_dbg(p2p, "initialized"); + p2p_channels_dump(p2p, "channels", &p2p->cfg->channels); + p2p_channels_dump(p2p, "cli_channels", &p2p->cfg->cli_channels); + return p2p; } void p2p_deinit(struct p2p_data *p2p) { +#ifdef CONFIG_WIFI_DISPLAY + wpabuf_free(p2p->wfd_ie_beacon); + wpabuf_free(p2p->wfd_ie_probe_req); + wpabuf_free(p2p->wfd_ie_probe_resp); + wpabuf_free(p2p->wfd_ie_assoc_req); + wpabuf_free(p2p->wfd_ie_invitation); + wpabuf_free(p2p->wfd_ie_prov_disc_req); + wpabuf_free(p2p->wfd_ie_prov_disc_resp); + wpabuf_free(p2p->wfd_ie_go_neg); + wpabuf_free(p2p->wfd_dev_info); + wpabuf_free(p2p->wfd_assoc_bssid); + wpabuf_free(p2p->wfd_coupled_sink_info); +#endif /* CONFIG_WIFI_DISPLAY */ + eloop_cancel_timeout(p2p_expiration_timeout, p2p, NULL); eloop_cancel_timeout(p2p_ext_listen_timeout, p2p, NULL); eloop_cancel_timeout(p2p_scan_timeout, p2p, NULL); + eloop_cancel_timeout(p2p_go_neg_start, p2p, NULL); p2p_flush(p2p); p2p_free_req_dev_types(p2p); os_free(p2p->cfg->dev_name); @@ -2258,6 +2431,7 @@ wpabuf_free(p2p->sd_resp); os_free(p2p->after_scan_tx); p2p_remove_wps_vendor_extensions(p2p); + os_free(p2p->no_go_freq.range); os_free(p2p); } @@ -2285,13 +2459,13 @@ if (dev == NULL) return -1; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Unauthorizing " MACSTR, - MAC2STR(addr)); + p2p_dbg(p2p, "Unauthorizing " MACSTR, MAC2STR(addr)); if (p2p->go_neg_peer == dev) p2p->go_neg_peer = NULL; dev->wps_method = WPS_NOT_READY; + dev->oob_pw_id = 0; dev->flags &= ~P2P_DEV_WAIT_GO_NEG_RESPONSE; dev->flags &= ~P2P_DEV_WAIT_GO_NEG_CONFIRM; @@ -2458,8 +2632,7 @@ break; } else if (dev->req_config_methods && !(dev->flags & P2P_DEV_PD_FOR_JOIN)) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Send " - "pending Provisioning Discovery Request to " + p2p_dbg(p2p, "Send pending Provision Discovery Request to " MACSTR " (config methods 0x%x)", MAC2STR(dev->info.p2p_device_addr), dev->req_config_methods); @@ -2468,14 +2641,13 @@ } } - p2p_listen_in_find(p2p); + p2p_listen_in_find(p2p, 1); } static void p2p_sd_cb(struct p2p_data *p2p, int success) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Service Discovery Query TX callback: success=%d", + p2p_dbg(p2p, "Service Discovery Query TX callback: success=%d", success); p2p->pending_action_state = P2P_NO_PENDING_ACTION; @@ -2489,8 +2661,7 @@ } if (p2p->sd_peer == NULL) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: No SD peer entry known"); + p2p_dbg(p2p, "No SD peer entry known"); p2p_continue_find(p2p); return; } @@ -2505,7 +2676,7 @@ * p2p_retry_pd - Retry any pending provision disc requests in IDLE state * @p2p: P2P module context from p2p_init() */ -void p2p_retry_pd(struct p2p_data *p2p) +static void p2p_retry_pd(struct p2p_data *p2p) { struct p2p_device *dev; @@ -2514,8 +2685,7 @@ /* * Retry the prov disc req attempt only for the peer that the user had - * requested for and provided a join has not been initiated on it - * in the meantime. + * requested. */ dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) { @@ -2524,15 +2694,14 @@ continue; if (!dev->req_config_methods) continue; - if (dev->flags & P2P_DEV_PD_FOR_JOIN) - continue; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Send " - "pending Provisioning Discovery Request to " + p2p_dbg(p2p, "Send pending Provision Discovery Request to " MACSTR " (config methods 0x%x)", MAC2STR(dev->info.p2p_device_addr), dev->req_config_methods); - p2p_send_prov_disc_req(p2p, dev, 0, 0); + p2p_send_prov_disc_req(p2p, dev, + dev->flags & P2P_DEV_PD_FOR_JOIN, + p2p->pd_force_freq); return; } } @@ -2540,8 +2709,7 @@ static void p2p_prov_disc_cb(struct p2p_data *p2p, int success) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Provision Discovery Request TX callback: success=%d", + p2p_dbg(p2p, "Provision Discovery Request TX callback: success=%d", success); /* @@ -2557,7 +2725,13 @@ if (!success) { p2p->pending_action_state = P2P_NO_PENDING_ACTION; - if (p2p->state != P2P_IDLE) + if (p2p->user_initiated_pd && + (p2p->state == P2P_SEARCH || p2p->state == P2P_LISTEN_ONLY)) + { + /* Retry request from timeout to avoid busy loops */ + p2p->pending_action_state = P2P_PENDING_PD; + p2p_set_timeout(p2p, 0, 50000); + } else if (p2p->state != P2P_IDLE) p2p_continue_find(p2p); else if (p2p->user_initiated_pd) { p2p->pending_action_state = P2P_PENDING_PD; @@ -2583,20 +2757,26 @@ int p2p_scan_res_handler(struct p2p_data *p2p, const u8 *bssid, int freq, - int level, const u8 *ies, size_t ies_len) + struct os_reltime *rx_time, int level, const u8 *ies, + size_t ies_len) { - p2p_add_device(p2p, bssid, freq, level, ies, ies_len); - - if (p2p->go_neg_peer && p2p->state == P2P_SEARCH && - os_memcmp(p2p->go_neg_peer->info.p2p_device_addr, bssid, ETH_ALEN) - == 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Found GO Negotiation peer - try to start GO " - "negotiation"); - p2p_connect_send(p2p, p2p->go_neg_peer); - return 1; + if (os_reltime_before(rx_time, &p2p->find_start)) { + /* + * The driver may have cached (e.g., in cfg80211 BSS table) the + * scan results for relatively long time. To avoid reporting + * stale information, update P2P peers only based on results + * that have based on frames received after the last p2p_find + * operation was started. + */ + p2p_dbg(p2p, "Ignore old scan result for " MACSTR + " (rx_time=%u.%06u)", + MAC2STR(bssid), (unsigned int) rx_time->sec, + (unsigned int) rx_time->usec); + return 0; } + p2p_add_device(p2p, bssid, freq, rx_time, level, ies, ies_len, 1); + return 0; } @@ -2604,8 +2784,7 @@ void p2p_scan_res_handled(struct p2p_data *p2p) { if (!p2p->p2p_scan_running) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: p2p_scan was not " - "running, but scan results received"); + p2p_dbg(p2p, "p2p_scan was not running, but scan results received"); } p2p->p2p_scan_running = 0; eloop_cancel_timeout(p2p_scan_timeout, p2p, NULL); @@ -2619,8 +2798,16 @@ void p2p_scan_ie(struct p2p_data *p2p, struct wpabuf *ies, const u8 *dev_id) { - u8 *len = p2p_buf_add_ie_hdr(ies); - p2p_buf_add_capability(ies, p2p->dev_capab, 0); + u8 *len; + +#ifdef CONFIG_WIFI_DISPLAY + if (p2p->wfd_ie_probe_req) + wpabuf_put_buf(ies, p2p->wfd_ie_probe_req); +#endif /* CONFIG_WIFI_DISPLAY */ + + len = p2p_buf_add_ie_hdr(ies); + p2p_buf_add_capability(ies, p2p->dev_capab & + ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY, 0); if (dev_id) p2p_buf_add_device_id(ies, dev_id); if (p2p->cfg->reg_class && p2p->cfg->channel) @@ -2637,7 +2824,14 @@ size_t p2p_scan_ie_buf_len(struct p2p_data *p2p) { - return 100; + size_t len = 100; + +#ifdef CONFIG_WIFI_DISPLAY + if (p2p && p2p->wfd_ie_probe_req) + len += wpabuf_len(p2p->wfd_ie_probe_req); +#endif /* CONFIG_WIFI_DISPLAY */ + + return len; } @@ -2650,31 +2844,29 @@ static void p2p_go_neg_req_cb(struct p2p_data *p2p, int success) { struct p2p_device *dev = p2p->go_neg_peer; + int timeout; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: GO Negotiation Request TX callback: success=%d", - success); + p2p_dbg(p2p, "GO Negotiation Request TX callback: success=%d", success); if (dev == NULL) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: No pending GO Negotiation"); + p2p_dbg(p2p, "No pending GO Negotiation"); return; } if (success) { - dev->go_neg_req_sent++; if (dev->flags & P2P_DEV_USER_REJECTED) { p2p_set_state(p2p, P2P_IDLE); return; } + } else if (dev->go_neg_req_sent) { + /* Cancel the increment from p2p_connect_send() on failure */ + dev->go_neg_req_sent--; } if (!success && (dev->info.dev_capab & P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY) && !is_zero_ether_addr(dev->member_in_go_dev)) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Peer " MACSTR " did not acknowledge request - " - "try to use device discoverability through its GO", + p2p_dbg(p2p, "Peer " MACSTR " did not acknowledge request - try to use device discoverability through its GO", MAC2STR(dev->info.p2p_device_addr)); p2p->cfg->send_action_done(p2p->cfg->cb_ctx); p2p_send_dev_disc_req(p2p, dev); @@ -2686,34 +2878,49 @@ * channel. */ p2p_set_state(p2p, P2P_CONNECT); - p2p_set_timeout(p2p, 0, 100000); + timeout = success ? 500000 : 100000; + if (!success && p2p->go_neg_peer && + (p2p->go_neg_peer->flags & P2P_DEV_PEER_WAITING_RESPONSE)) { + unsigned int r; + /* + * Peer is expected to wait our response and we will skip the + * listen phase. Add some randomness to the wait time here to + * make it less likely to hit cases where we could end up in + * sync with peer not listening. + */ + os_get_random((u8 *) &r, sizeof(r)); + timeout += r % 100000; + } + p2p_set_timeout(p2p, 0, timeout); } static void p2p_go_neg_resp_cb(struct p2p_data *p2p, int success) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: GO Negotiation Response TX callback: success=%d", + p2p_dbg(p2p, "GO Negotiation Response TX callback: success=%d", success); if (!p2p->go_neg_peer && p2p->state == P2P_PROVISIONING) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Ignore TX callback event - GO Negotiation is " - "not running anymore"); + p2p_dbg(p2p, "Ignore TX callback event - GO Negotiation is not running anymore"); return; } p2p_set_state(p2p, P2P_CONNECT); - p2p_set_timeout(p2p, 0, 100000); + p2p_set_timeout(p2p, 0, 500000); } -static void p2p_go_neg_resp_failure_cb(struct p2p_data *p2p, int success) +static void p2p_go_neg_resp_failure_cb(struct p2p_data *p2p, int success, + const u8 *addr) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: GO Negotiation Response (failure) TX callback: " - "success=%d", success); + p2p_dbg(p2p, "GO Negotiation Response (failure) TX callback: success=%d", success); if (p2p->go_neg_peer && p2p->go_neg_peer->status != P2P_SC_SUCCESS) { p2p_go_neg_failed(p2p, p2p->go_neg_peer, p2p->go_neg_peer->status); + } else if (success) { + struct p2p_device *dev; + dev = p2p_get_device(p2p, addr); + if (dev && + dev->status == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE) + dev->flags |= P2P_DEV_PEER_WAITING_RESPONSE; } } @@ -2723,9 +2930,7 @@ { struct p2p_device *dev; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: GO Negotiation Confirm TX callback: result=%d", - result); + p2p_dbg(p2p, "GO Negotiation Confirm TX callback: result=%d", result); p2p->cfg->send_action_done(p2p->cfg->cb_ctx); if (result == P2P_SEND_ACTION_FAILED) { p2p_go_neg_failed(p2p, p2p->go_neg_peer, -1); @@ -2742,10 +2947,7 @@ * peer did indeed receive the frame, continue regardless of * the TX status. */ - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Assume GO Negotiation Confirm TX was actually " - "received by the peer even though Ack was not " - "reported"); + p2p_dbg(p2p, "Assume GO Negotiation Confirm TX was actually received by the peer even though Ack was not reported"); } dev = p2p->go_neg_peer; @@ -2763,8 +2965,7 @@ enum p2p_pending_action_state state; int success; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Action frame TX callback (state=%d freq=%u dst=" MACSTR + p2p_dbg(p2p, "Action frame TX callback (state=%d freq=%u dst=" MACSTR " src=" MACSTR " bssid=" MACSTR " result=%d", p2p->pending_action_state, freq, MAC2STR(dst), MAC2STR(src), MAC2STR(bssid), result); @@ -2773,6 +2974,16 @@ p2p->pending_action_state = P2P_NO_PENDING_ACTION; switch (state) { case P2P_NO_PENDING_ACTION: + if (p2p->after_scan_tx_in_progress) { + p2p->after_scan_tx_in_progress = 0; + if (p2p->start_after_scan != P2P_AFTER_SCAN_NOTHING && + p2p_run_after_scan(p2p)) + break; + if (p2p->state == P2P_SEARCH) { + p2p_dbg(p2p, "Continue find after after_scan_tx completion"); + p2p_continue_find(p2p); + } + } break; case P2P_PENDING_GO_NEG_REQUEST: p2p_go_neg_req_cb(p2p, success); @@ -2781,7 +2992,7 @@ p2p_go_neg_resp_cb(p2p, success); break; case P2P_PENDING_GO_NEG_RESPONSE_FAILURE: - p2p_go_neg_resp_failure_cb(p2p, success); + p2p_go_neg_resp_failure_cb(p2p, success, dst); break; case P2P_PENDING_GO_NEG_CONFIRM: p2p_go_neg_conf_cb(p2p, result); @@ -2808,6 +3019,8 @@ p2p_go_disc_req_cb(p2p, success); break; } + + p2p->after_scan_tx_in_progress = 0; } @@ -2815,23 +3028,18 @@ unsigned int duration) { if (freq == p2p->pending_client_disc_freq) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Client discoverability remain-awake completed"); + p2p_dbg(p2p, "Client discoverability remain-awake completed"); p2p->pending_client_disc_freq = 0; return; } if (freq != p2p->pending_listen_freq) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Unexpected listen callback for freq=%u " - "duration=%u (pending_listen_freq=%u)", + p2p_dbg(p2p, "Unexpected listen callback for freq=%u duration=%u (pending_listen_freq=%u)", freq, duration, p2p->pending_listen_freq); return; } - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Starting Listen timeout(%u,%u) on freq=%u based on " - "callback", + p2p_dbg(p2p, "Starting Listen timeout(%u,%u) on freq=%u based on callback", p2p->pending_listen_sec, p2p->pending_listen_usec, p2p->pending_listen_freq); p2p->in_listen = 1; @@ -2852,17 +3060,14 @@ int p2p_listen_end(struct p2p_data *p2p, unsigned int freq) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Driver ended Listen " - "state (freq=%u)", freq); + p2p_dbg(p2p, "Driver ended Listen state (freq=%u)", freq); p2p->drv_in_listen = 0; if (p2p->in_listen) return 0; /* Internal timeout will trigger the next step */ if (p2p->state == P2P_CONNECT_LISTEN && p2p->go_neg_peer) { if (p2p->go_neg_peer->connect_reqs >= 120) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Timeout on sending GO Negotiation " - "Request without getting response"); + p2p_dbg(p2p, "Timeout on sending GO Negotiation Request without getting response"); p2p_go_neg_failed(p2p, p2p->go_neg_peer, -1); return 0; } @@ -2880,9 +3085,24 @@ * operation while in p2p_find. Avoid an attempt to * restart a scan here. */ - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: p2p_scan " - "already in progress - do not try to start a " - "new one"); + p2p_dbg(p2p, "p2p_scan already in progress - do not try to start a new one"); + return 1; + } + if (p2p->pending_listen_freq) { + /* + * Better wait a bit if the driver is unable to start + * offchannel operation for some reason. p2p_search() + * will be started from internal timeout. + */ + p2p_dbg(p2p, "Listen operation did not seem to start - delay search phase to avoid busy loop"); + p2p_set_timeout(p2p, 0, 100000); + return 1; + } + if (p2p->search_delay) { + p2p_dbg(p2p, "Delay search operation by %u ms", + p2p->search_delay); + p2p_set_timeout(p2p, p2p->search_delay / 1000, + (p2p->search_delay % 1000) * 1000); return 1; } p2p_search(p2p); @@ -2896,25 +3116,40 @@ static void p2p_timeout_connect(struct p2p_data *p2p) { p2p->cfg->send_action_done(p2p->cfg->cb_ctx); - p2p_set_state(p2p, P2P_CONNECT_LISTEN); - p2p_listen_in_find(p2p); -} + if (p2p->go_neg_peer && + (p2p->go_neg_peer->flags & P2P_DEV_WAIT_GO_NEG_CONFIRM)) { + p2p_dbg(p2p, "Wait for GO Negotiation Confirm timed out - assume GO Negotiation failed"); + p2p_go_neg_failed(p2p, p2p->go_neg_peer, -1); + return; + } + if (p2p->go_neg_peer && + (p2p->go_neg_peer->flags & P2P_DEV_PEER_WAITING_RESPONSE) && + p2p->go_neg_peer->connect_reqs < 120) { + p2p_dbg(p2p, "Peer expected to wait our response - skip listen"); + p2p_connect_send(p2p, p2p->go_neg_peer); + return; + } + if (p2p->go_neg_peer && p2p->go_neg_peer->oob_go_neg_freq > 0) { + p2p_dbg(p2p, "Skip connect-listen since GO Neg channel known (OOB)"); + p2p_set_state(p2p, P2P_CONNECT_LISTEN); + p2p_set_timeout(p2p, 0, 30000); + return; + } + p2p_set_state(p2p, P2P_CONNECT_LISTEN); + p2p_listen_in_find(p2p, 0); +} static void p2p_timeout_connect_listen(struct p2p_data *p2p) { if (p2p->go_neg_peer) { if (p2p->drv_in_listen) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Driver is " - "still in Listen state; wait for it to " - "complete"); + p2p_dbg(p2p, "Driver is still in Listen state; wait for it to complete"); return; } if (p2p->go_neg_peer->connect_reqs >= 120) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Timeout on sending GO Negotiation " - "Request without getting response"); + p2p_dbg(p2p, "Timeout on sending GO Negotiation Request without getting response"); p2p_go_neg_failed(p2p, p2p->go_neg_peer, -1); return; } @@ -2928,13 +3163,13 @@ static void p2p_timeout_wait_peer_connect(struct p2p_data *p2p) { - /* - * TODO: could remain constantly in Listen state for some time if there - * are no other concurrent uses for the radio. For now, go to listen - * state once per second to give other uses a chance to use the radio. - */ p2p_set_state(p2p, P2P_WAIT_PEER_IDLE); - p2p_set_timeout(p2p, 0, 500000); + + if (p2p->cfg->is_concurrent_session_active && + p2p->cfg->is_concurrent_session_active(p2p->cfg->cb_ctx)) + p2p_set_timeout(p2p, 0, 500000); + else + p2p_set_timeout(p2p, 0, 200000); } @@ -2943,32 +3178,26 @@ struct p2p_device *dev = p2p->go_neg_peer; if (dev == NULL) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Unknown GO Neg peer - stop GO Neg wait"); + p2p_dbg(p2p, "Unknown GO Neg peer - stop GO Neg wait"); return; } dev->wait_count++; if (dev->wait_count >= 120) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Timeout on waiting peer to become ready for GO " - "Negotiation"); + p2p_dbg(p2p, "Timeout on waiting peer to become ready for GO Negotiation"); p2p_go_neg_failed(p2p, dev, -1); return; } - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Go to Listen state while waiting for the peer to become " - "ready for GO Negotiation"); + p2p_dbg(p2p, "Go to Listen state while waiting for the peer to become ready for GO Negotiation"); p2p_set_state(p2p, P2P_WAIT_PEER_CONNECT); - p2p_listen_in_find(p2p); + p2p_listen_in_find(p2p, 0); } static void p2p_timeout_sd_during_find(struct p2p_data *p2p) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Service Discovery Query timeout"); + p2p_dbg(p2p, "Service Discovery Query timeout"); if (p2p->sd_peer) { p2p->cfg->send_action_done(p2p->cfg->cb_ctx); p2p->sd_peer->flags &= ~P2P_DEV_SD_SCHEDULE; @@ -2980,8 +3209,7 @@ static void p2p_timeout_prov_disc_during_find(struct p2p_data *p2p) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Provision Discovery Request timeout"); + p2p_dbg(p2p, "Provision Discovery Request timeout"); p2p->cfg->send_action_done(p2p->cfg->cb_ctx); p2p_continue_find(p2p); } @@ -2999,16 +3227,29 @@ if (!p2p->user_initiated_pd) return; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: User initiated Provision Discovery Request timeout"); + p2p_dbg(p2p, "User initiated Provision Discovery Request timeout"); if (p2p->pd_retries) { p2p->pd_retries--; p2p_retry_pd(p2p); } else { + struct p2p_device *dev; + int for_join = 0; + + dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) { + if (os_memcmp(p2p->pending_pd_devaddr, + dev->info.p2p_device_addr, ETH_ALEN) != 0) + continue; + if (dev->req_config_methods && + (dev->flags & P2P_DEV_PD_FOR_JOIN)) + for_join = 1; + } + if (p2p->cfg->prov_disc_fail) p2p->cfg->prov_disc_fail(p2p->cfg->cb_ctx, p2p->pending_pd_devaddr, + for_join ? + P2P_PROV_DISC_TIMEOUT_JOIN : P2P_PROV_DISC_TIMEOUT); p2p_reset_pending_pd(p2p); } @@ -3024,12 +3265,11 @@ * Better remain on operating channel instead of listen channel * when running a group. */ - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Inviting in " - "active GO role - wait on operating channel"); + p2p_dbg(p2p, "Inviting in active GO role - wait on operating channel"); p2p_set_timeout(p2p, 0, 100000); return; } - p2p_listen_in_find(p2p); + p2p_listen_in_find(p2p, 0); } @@ -3038,14 +3278,15 @@ if (p2p->invite_peer && p2p->invite_peer->invitation_reqs < 100) { p2p_set_state(p2p, P2P_INVITE); p2p_invite_send(p2p, p2p->invite_peer, - p2p->invite_go_dev_addr); + p2p->invite_go_dev_addr, p2p->invite_dev_pw_id); } else { if (p2p->invite_peer) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Invitation Request retry limit reached"); + p2p_dbg(p2p, "Invitation Request retry limit reached"); if (p2p->cfg->invitation_result) p2p->cfg->invitation_result( - p2p->cfg->cb_ctx, -1, NULL); + p2p->cfg->cb_ctx, -1, NULL, NULL, + p2p->invite_peer->info.p2p_device_addr, + 0); } p2p_set_state(p2p, P2P_IDLE); } @@ -3056,8 +3297,7 @@ { struct p2p_data *p2p = eloop_ctx; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Timeout (state=%s)", - p2p_state_txt(p2p->state)); + p2p_dbg(p2p, "Timeout (state=%s)", p2p_state_txt(p2p->state)); p2p->in_listen = 0; @@ -3071,6 +3311,15 @@ /* Check if we timed out waiting for PD req */ if (p2p->pending_action_state == P2P_PENDING_PD) p2p_timeout_prov_disc_req(p2p); + if (p2p->search_delay && !p2p->in_search_delay) { + p2p_dbg(p2p, "Delay search operation by %u ms", + p2p->search_delay); + p2p->in_search_delay = 1; + p2p_set_timeout(p2p, p2p->search_delay / 1000, + (p2p->search_delay % 1000) * 1000); + break; + } + p2p->in_search_delay = 0; p2p_search(p2p); break; case P2P_CONNECT: @@ -3087,9 +3336,7 @@ p2p_timeout_prov_disc_req(p2p); if (p2p->ext_listen_only) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Extended Listen Timing - Listen State " - "completed"); + p2p_dbg(p2p, "Extended Listen Timing - Listen State completed"); p2p->ext_listen_only = 0; p2p_set_state(p2p, P2P_IDLE); } @@ -3114,8 +3361,6 @@ case P2P_INVITE_LISTEN: p2p_timeout_invite_listen(p2p); break; - case P2P_SEARCH_WHEN_READY: - break; } } @@ -3125,11 +3370,10 @@ struct p2p_device *dev; dev = p2p_get_device(p2p, peer_addr); - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Local request to reject " - "connection attempts by peer " MACSTR, MAC2STR(peer_addr)); + p2p_dbg(p2p, "Local request to reject connection attempts by peer " + MACSTR, MAC2STR(peer_addr)); if (dev == NULL) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Peer " MACSTR - " unknown", MAC2STR(peer_addr)); + p2p_dbg(p2p, "Peer " MACSTR " unknown", MAC2STR(peer_addr)); return -1; } dev->status = P2P_SC_FAIL_REJECTED_BY_USER; @@ -3149,6 +3393,8 @@ return "Keypad"; case WPS_PBC: return "PBC"; + case WPS_NFC: + return "NFC"; } return "??"; @@ -3199,7 +3445,7 @@ struct p2p_device *dev; int res; char *pos, *end; - struct os_time now; + struct os_reltime now; if (info == NULL) return -1; @@ -3210,7 +3456,7 @@ pos = buf; end = buf + buflen; - os_get_time(&now); + os_get_reltime(&now); res = os_snprintf(pos, end - pos, "age=%d\n" "listen_freq=%d\n" @@ -3299,6 +3545,24 @@ pos += res; } +#ifdef CONFIG_WIFI_DISPLAY + if (dev->info.wfd_subelems) { + res = os_snprintf(pos, end - pos, "wfd_subelems="); + if (res < 0 || res >= end - pos) + return pos - buf; + pos += res; + + pos += wpa_snprintf_hex(pos, end - pos, + wpabuf_head(dev->info.wfd_subelems), + wpabuf_len(dev->info.wfd_subelems)); + + res = os_snprintf(pos, end - pos, "\n"); + if (res < 0 || res >= end - pos) + return pos - buf; + pos += res; + } +#endif /* CONFIG_WIFI_DISPLAY */ + return pos - buf; } @@ -3312,12 +3576,10 @@ void p2p_set_client_discoverability(struct p2p_data *p2p, int enabled) { if (enabled) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Client " - "discoverability enabled"); + p2p_dbg(p2p, "Client discoverability enabled"); p2p->dev_capab |= P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY; } else { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Client " - "discoverability disabled"); + p2p_dbg(p2p, "Client discoverability disabled"); p2p->dev_capab &= ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY; } } @@ -3366,9 +3628,9 @@ { struct wpabuf *req; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Send Presence Request to " - "GO " MACSTR " (own interface " MACSTR ") freq=%u dur1=%u " - "int1=%u dur2=%u int2=%u", + p2p_dbg(p2p, "Send Presence Request to GO " MACSTR + " (own interface " MACSTR ") freq=%u dur1=%u int1=%u " + "dur2=%u int2=%u", MAC2STR(go_interface_addr), MAC2STR(own_interface_addr), freq, duration1, interval1, duration2, interval2); @@ -3381,8 +3643,7 @@ if (p2p_send_action(p2p, freq, go_interface_addr, own_interface_addr, go_interface_addr, wpabuf_head(req), wpabuf_len(req), 200) < 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Failed to send Action frame"); + p2p_dbg(p2p, "Failed to send Action frame"); } wpabuf_free(req); @@ -3428,8 +3689,7 @@ u8 noa[50]; int noa_len; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Received P2P Action - P2P Presence Request"); + p2p_dbg(p2p, "Received P2P Action - P2P Presence Request"); for (g = 0; g < p2p->num_groups; g++) { if (os_memcmp(da, p2p_group_get_interface_addr(p2p->groups[g]), @@ -3439,23 +3699,20 @@ } } if (group == NULL) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Ignore P2P Presence Request for unknown group " + p2p_dbg(p2p, "Ignore P2P Presence Request for unknown group " MACSTR, MAC2STR(da)); return; } if (p2p_parse(data, len, &msg) < 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Failed to parse P2P Presence Request"); + p2p_dbg(p2p, "Failed to parse P2P Presence Request"); status = P2P_SC_FAIL_INVALID_PARAMS; goto fail; } parsed = 1; if (msg.noa == NULL) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: No NoA attribute in P2P Presence Request"); + p2p_dbg(p2p, "No NoA attribute in P2P Presence Request"); status = P2P_SC_FAIL_INVALID_PARAMS; goto fail; } @@ -3479,8 +3736,7 @@ p2p->pending_action_state = P2P_NO_PENDING_ACTION; if (p2p_send_action(p2p, rx_freq, sa, da, da, wpabuf_head(resp), wpabuf_len(resp), 200) < 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Failed to send Action frame"); + p2p_dbg(p2p, "Failed to send Action frame"); } wpabuf_free(resp); } @@ -3491,33 +3747,32 @@ { struct p2p_message msg; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Received P2P Action - P2P Presence Response"); + p2p_dbg(p2p, "Received P2P Action - P2P Presence Response"); if (p2p_parse(data, len, &msg) < 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Failed to parse P2P Presence Response"); + p2p_dbg(p2p, "Failed to parse P2P Presence Response"); return; } if (msg.status == NULL || msg.noa == NULL) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: No Status or NoA attribute in P2P Presence " - "Response"); + p2p_dbg(p2p, "No Status or NoA attribute in P2P Presence Response"); p2p_parse_free(&msg); return; } + if (p2p->cfg->presence_resp) { + p2p->cfg->presence_resp(p2p->cfg->cb_ctx, sa, *msg.status, + msg.noa, msg.noa_len); + } + if (*msg.status) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: P2P Presence Request was rejected: status %u", + p2p_dbg(p2p, "P2P Presence Request was rejected: status %u", *msg.status); p2p_parse_free(&msg); return; } - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: P2P Presence Request was accepted"); + p2p_dbg(p2p, "P2P Presence Request was accepted"); wpa_hexdump(MSG_DEBUG, "P2P: P2P Presence Response - NoA", msg.noa, msg.noa_len); /* TODO: process NoA */ @@ -3543,25 +3798,20 @@ * running at an inconvenient time. As a workaround, allow new * Extended Listen operation to be started. */ - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Previous " - "Extended Listen operation had not been completed - " - "try again"); + p2p_dbg(p2p, "Previous Extended Listen operation had not been completed - try again"); p2p->ext_listen_only = 0; p2p_set_state(p2p, P2P_IDLE); } if (p2p->state != P2P_IDLE) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Skip Extended " - "Listen timeout in active state (%s)", - p2p_state_txt(p2p->state)); + p2p_dbg(p2p, "Skip Extended Listen timeout in active state (%s)", p2p_state_txt(p2p->state)); return; } - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Extended Listen timeout"); + p2p_dbg(p2p, "Extended Listen timeout"); p2p->ext_listen_only = 1; if (p2p_listen(p2p, p2p->ext_listen_period) < 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Failed to start " - "Listen state for Extended Listen Timing"); + p2p_dbg(p2p, "Failed to start Listen state for Extended Listen Timing"); p2p->ext_listen_only = 0; } } @@ -3572,25 +3822,22 @@ { if (period > 65535 || interval > 65535 || period > interval || (period == 0 && interval > 0) || (period > 0 && interval == 0)) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Invalid Extended Listen Timing request: " - "period=%u interval=%u", period, interval); + p2p_dbg(p2p, "Invalid Extended Listen Timing request: period=%u interval=%u", + period, interval); return -1; } eloop_cancel_timeout(p2p_ext_listen_timeout, p2p, NULL); if (interval == 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Disabling Extended Listen Timing"); + p2p_dbg(p2p, "Disabling Extended Listen Timing"); p2p->ext_listen_period = 0; p2p->ext_listen_interval = 0; return 0; } - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Enabling Extended Listen Timing: period %u msec, " - "interval %u msec", period, interval); + p2p_dbg(p2p, "Enabling Extended Listen Timing: period %u msec, interval %u msec", + period, interval); p2p->ext_listen_period = period; p2p->ext_listen_interval = interval; p2p->ext_listen_interval_sec = interval / 1000; @@ -3615,11 +3862,12 @@ os_memset(&msg, 0, sizeof(msg)); if (p2p_parse_ies(ie, ie_len, &msg)) return; - if (msg.minor_reason_code == NULL) + if (msg.minor_reason_code == NULL) { + p2p_parse_free(&msg); return; + } - wpa_msg(p2p->cfg->msg_ctx, MSG_INFO, - "P2P: Deauthentication notification BSSID " MACSTR + p2p_dbg(p2p, "Deauthentication notification BSSID " MACSTR " reason_code=%u minor_reason_code=%u", MAC2STR(bssid), reason_code, *msg.minor_reason_code); @@ -3638,11 +3886,12 @@ os_memset(&msg, 0, sizeof(msg)); if (p2p_parse_ies(ie, ie_len, &msg)) return; - if (msg.minor_reason_code == NULL) + if (msg.minor_reason_code == NULL) { + p2p_parse_free(&msg); return; + } - wpa_msg(p2p->cfg->msg_ctx, MSG_INFO, - "P2P: Disassociation notification BSSID " MACSTR + p2p_dbg(p2p, "Disassociation notification BSSID " MACSTR " reason_code=%u minor_reason_code=%u", MAC2STR(bssid), reason_code, *msg.minor_reason_code); @@ -3653,12 +3902,10 @@ void p2p_set_managed_oper(struct p2p_data *p2p, int enabled) { if (enabled) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Managed P2P " - "Device operations enabled"); + p2p_dbg(p2p, "Managed P2P Device operations enabled"); p2p->dev_capab |= P2P_DEV_CAPAB_INFRA_MANAGED; } else { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Managed P2P " - "Device operations disabled"); + p2p_dbg(p2p, "Managed P2P Device operations disabled"); p2p->dev_capab &= ~P2P_DEV_CAPAB_INFRA_MANAGED; } } @@ -3666,11 +3913,11 @@ int p2p_set_listen_channel(struct p2p_data *p2p, u8 reg_class, u8 channel) { - if (p2p_channel_to_freq(p2p->cfg->country, reg_class, channel) < 0) + if (p2p_channel_to_freq(reg_class, channel) < 0) return -1; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Set Listen channel: " - "reg_class %u channel %u", reg_class, channel); + p2p_dbg(p2p, "Set Listen channel: reg_class %u channel %u", + reg_class, channel); p2p->cfg->reg_class = reg_class; p2p->cfg->channel = channel; @@ -3680,7 +3927,7 @@ int p2p_set_ssid_postfix(struct p2p_data *p2p, const u8 *postfix, size_t len) { - wpa_hexdump_ascii(MSG_DEBUG, "P2P: New SSID postfix", postfix, len); + p2p_dbg(p2p, "New SSID postfix: %s", wpa_ssid_txt(postfix, len)); if (postfix == NULL) { p2p->cfg->ssid_postfix_len = 0; return 0; @@ -3696,12 +3943,11 @@ int p2p_set_oper_channel(struct p2p_data *p2p, u8 op_reg_class, u8 op_channel, int cfg_op_channel) { - if (p2p_channel_to_freq(p2p->cfg->country, op_reg_class, op_channel) - < 0) + if (p2p_channel_to_freq(op_reg_class, op_channel) < 0) return -1; - wpa_msg(p2p->cfg->msg_ctx, MSG_INFO, "P2P: Set Operating channel: " - "reg_class %u channel %u", op_reg_class, op_channel); + p2p_dbg(p2p, "Set Operating channel: reg_class %u channel %u", + op_reg_class, op_channel); p2p->cfg->op_reg_class = op_reg_class; p2p->cfg->op_channel = op_channel; p2p->cfg->cfg_op_channel = cfg_op_channel; @@ -3731,6 +3977,31 @@ } +int p2p_set_no_go_freq(struct p2p_data *p2p, + const struct wpa_freq_range_list *list) +{ + struct wpa_freq_range *tmp; + + if (list == NULL || list->num == 0) { + os_free(p2p->no_go_freq.range); + p2p->no_go_freq.range = NULL; + p2p->no_go_freq.num = 0; + return 0; + } + + tmp = os_calloc(list->num, sizeof(struct wpa_freq_range)); + if (tmp == NULL) + return -1; + os_memcpy(tmp, list->range, list->num * sizeof(struct wpa_freq_range)); + os_free(p2p->no_go_freq.range); + p2p->no_go_freq.range = tmp; + p2p->no_go_freq.num = list->num; + p2p_dbg(p2p, "Updated no GO chan list"); + + return 0; +} + + int p2p_get_interface_addr(struct p2p_data *p2p, const u8 *dev_addr, u8 *iface_addr) { @@ -3757,18 +4028,16 @@ { os_memcpy(p2p->peer_filter, addr, ETH_ALEN); if (is_zero_ether_addr(p2p->peer_filter)) - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Disable peer " - "filter"); + p2p_dbg(p2p, "Disable peer filter"); else - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Enable peer " - "filter for " MACSTR, MAC2STR(p2p->peer_filter)); + p2p_dbg(p2p, "Enable peer filter for " MACSTR, + MAC2STR(p2p->peer_filter)); } void p2p_set_cross_connect(struct p2p_data *p2p, int enabled) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Cross connection %s", - enabled ? "enabled" : "disabled"); + p2p_dbg(p2p, "Cross connection %s", enabled ? "enabled" : "disabled"); if (p2p->cross_connect == enabled) return; p2p->cross_connect = enabled; @@ -3789,16 +4058,22 @@ void p2p_set_intra_bss_dist(struct p2p_data *p2p, int enabled) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Intra BSS distribution %s", + p2p_dbg(p2p, "Intra BSS distribution %s", enabled ? "enabled" : "disabled"); p2p->cfg->p2p_intra_bss = enabled; } -void p2p_update_channel_list(struct p2p_data *p2p, struct p2p_channels *chan) +void p2p_update_channel_list(struct p2p_data *p2p, + const struct p2p_channels *chan, + const struct p2p_channels *cli_chan) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Update channel list"); + p2p_dbg(p2p, "Update channel list"); os_memcpy(&p2p->cfg->channels, chan, sizeof(struct p2p_channels)); + p2p_channels_dump(p2p, "channels", &p2p->cfg->channels); + os_memcpy(&p2p->cfg->cli_channels, cli_chan, + sizeof(struct p2p_channels)); + p2p_channels_dump(p2p, "cli_channels", &p2p->cfg->cli_channels); } @@ -3807,11 +4082,9 @@ size_t len, unsigned int wait_time) { if (p2p->p2p_scan_running) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Delay Action " - "frame TX until p2p_scan completes"); + p2p_dbg(p2p, "Delay Action frame TX until p2p_scan completes"); if (p2p->after_scan_tx) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Dropped " - "previous pending Action frame TX"); + p2p_dbg(p2p, "Dropped previous pending Action frame TX"); os_free(p2p->after_scan_tx); } p2p->after_scan_tx = os_malloc(sizeof(*p2p->after_scan_tx) + @@ -3836,14 +4109,21 @@ void p2p_set_best_channels(struct p2p_data *p2p, int freq_24, int freq_5, int freq_overall) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Best channel: 2.4 GHz: %d," - " 5 GHz: %d, overall: %d", freq_24, freq_5, freq_overall); + p2p_dbg(p2p, "Best channel: 2.4 GHz: %d, 5 GHz: %d, overall: %d", + freq_24, freq_5, freq_overall); p2p->best_freq_24 = freq_24; p2p->best_freq_5 = freq_5; p2p->best_freq_overall = freq_overall; } +void p2p_set_own_freq_preference(struct p2p_data *p2p, int freq) +{ + p2p_dbg(p2p, "Own frequency preference: %d MHz", freq); + p2p->own_freq_preference = freq; +} + + const u8 * p2p_get_go_neg_peer(struct p2p_data *p2p) { if (p2p == NULL || p2p->go_neg_peer == NULL) @@ -3897,5 +4177,422 @@ { if (p2p == NULL) return 0; + if (p2p->state == P2P_SEARCH) + return 2; return p2p->state != P2P_IDLE && p2p->state != P2P_PROVISIONING; } + + +void p2p_set_config_timeout(struct p2p_data *p2p, u8 go_timeout, + u8 client_timeout) +{ + if (p2p) { + p2p->go_timeout = go_timeout; + p2p->client_timeout = client_timeout; + } +} + + +#ifdef CONFIG_WIFI_DISPLAY + +static void p2p_update_wfd_ie_groups(struct p2p_data *p2p) +{ + size_t g; + struct p2p_group *group; + + for (g = 0; g < p2p->num_groups; g++) { + group = p2p->groups[g]; + p2p_group_force_beacon_update_ies(group); + } +} + + +int p2p_set_wfd_ie_beacon(struct p2p_data *p2p, struct wpabuf *ie) +{ + wpabuf_free(p2p->wfd_ie_beacon); + p2p->wfd_ie_beacon = ie; + p2p_update_wfd_ie_groups(p2p); + return 0; +} + + +int p2p_set_wfd_ie_probe_req(struct p2p_data *p2p, struct wpabuf *ie) +{ + wpabuf_free(p2p->wfd_ie_probe_req); + p2p->wfd_ie_probe_req = ie; + return 0; +} + + +int p2p_set_wfd_ie_probe_resp(struct p2p_data *p2p, struct wpabuf *ie) +{ + wpabuf_free(p2p->wfd_ie_probe_resp); + p2p->wfd_ie_probe_resp = ie; + p2p_update_wfd_ie_groups(p2p); + return 0; +} + + +int p2p_set_wfd_ie_assoc_req(struct p2p_data *p2p, struct wpabuf *ie) +{ + wpabuf_free(p2p->wfd_ie_assoc_req); + p2p->wfd_ie_assoc_req = ie; + return 0; +} + + +int p2p_set_wfd_ie_invitation(struct p2p_data *p2p, struct wpabuf *ie) +{ + wpabuf_free(p2p->wfd_ie_invitation); + p2p->wfd_ie_invitation = ie; + return 0; +} + + +int p2p_set_wfd_ie_prov_disc_req(struct p2p_data *p2p, struct wpabuf *ie) +{ + wpabuf_free(p2p->wfd_ie_prov_disc_req); + p2p->wfd_ie_prov_disc_req = ie; + return 0; +} + + +int p2p_set_wfd_ie_prov_disc_resp(struct p2p_data *p2p, struct wpabuf *ie) +{ + wpabuf_free(p2p->wfd_ie_prov_disc_resp); + p2p->wfd_ie_prov_disc_resp = ie; + return 0; +} + + +int p2p_set_wfd_ie_go_neg(struct p2p_data *p2p, struct wpabuf *ie) +{ + wpabuf_free(p2p->wfd_ie_go_neg); + p2p->wfd_ie_go_neg = ie; + return 0; +} + + +int p2p_set_wfd_dev_info(struct p2p_data *p2p, const struct wpabuf *elem) +{ + wpabuf_free(p2p->wfd_dev_info); + if (elem) { + p2p->wfd_dev_info = wpabuf_dup(elem); + if (p2p->wfd_dev_info == NULL) + return -1; + } else + p2p->wfd_dev_info = NULL; + + return 0; +} + + +int p2p_set_wfd_assoc_bssid(struct p2p_data *p2p, const struct wpabuf *elem) +{ + wpabuf_free(p2p->wfd_assoc_bssid); + if (elem) { + p2p->wfd_assoc_bssid = wpabuf_dup(elem); + if (p2p->wfd_assoc_bssid == NULL) + return -1; + } else + p2p->wfd_assoc_bssid = NULL; + + return 0; +} + + +int p2p_set_wfd_coupled_sink_info(struct p2p_data *p2p, + const struct wpabuf *elem) +{ + wpabuf_free(p2p->wfd_coupled_sink_info); + if (elem) { + p2p->wfd_coupled_sink_info = wpabuf_dup(elem); + if (p2p->wfd_coupled_sink_info == NULL) + return -1; + } else + p2p->wfd_coupled_sink_info = NULL; + + return 0; +} + +#endif /* CONFIG_WIFI_DISPLAY */ + + +int p2p_set_disc_int(struct p2p_data *p2p, int min_disc_int, int max_disc_int, + int max_disc_tu) +{ + if (min_disc_int > max_disc_int || min_disc_int < 0 || max_disc_int < 0) + return -1; + + p2p->min_disc_int = min_disc_int; + p2p->max_disc_int = max_disc_int; + p2p->max_disc_tu = max_disc_tu; + p2p_dbg(p2p, "Set discoverable interval: min=%d max=%d max_tu=%d", + min_disc_int, max_disc_int, max_disc_tu); + + return 0; +} + + +void p2p_dbg(struct p2p_data *p2p, const char *fmt, ...) +{ + va_list ap; + char buf[500]; + + if (!p2p->cfg->debug_print) + return; + + va_start(ap, fmt); + vsnprintf(buf, sizeof(buf), fmt, ap); + buf[sizeof(buf) - 1] = '\0'; + va_end(ap); + p2p->cfg->debug_print(p2p->cfg->cb_ctx, MSG_DEBUG, buf); +} + + +void p2p_info(struct p2p_data *p2p, const char *fmt, ...) +{ + va_list ap; + char buf[500]; + + if (!p2p->cfg->debug_print) + return; + + va_start(ap, fmt); + vsnprintf(buf, sizeof(buf), fmt, ap); + buf[sizeof(buf) - 1] = '\0'; + va_end(ap); + p2p->cfg->debug_print(p2p->cfg->cb_ctx, MSG_INFO, buf); +} + + +void p2p_err(struct p2p_data *p2p, const char *fmt, ...) +{ + va_list ap; + char buf[500]; + + if (!p2p->cfg->debug_print) + return; + + va_start(ap, fmt); + vsnprintf(buf, sizeof(buf), fmt, ap); + buf[sizeof(buf) - 1] = '\0'; + va_end(ap); + p2p->cfg->debug_print(p2p->cfg->cb_ctx, MSG_ERROR, buf); +} + + +#ifdef CONFIG_WPS_NFC + +static struct wpabuf * p2p_build_nfc_handover(struct p2p_data *p2p, + int client_freq, + const u8 *go_dev_addr, + const u8 *ssid, size_t ssid_len) +{ + struct wpabuf *buf; + u8 op_class, channel; + enum p2p_role_indication role = P2P_DEVICE_NOT_IN_GROUP; + + buf = wpabuf_alloc(1000); + if (buf == NULL) + return NULL; + + op_class = p2p->cfg->reg_class; + channel = p2p->cfg->channel; + + p2p_buf_add_capability(buf, p2p->dev_capab & + ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY, 0); + p2p_buf_add_device_info(buf, p2p, NULL); + + if (p2p->num_groups > 0) { + role = P2P_GO_IN_A_GROUP; + p2p_freq_to_channel(p2p_group_get_freq(p2p->groups[0]), + &op_class, &channel); + } else if (client_freq > 0) { + role = P2P_CLIENT_IN_A_GROUP; + p2p_freq_to_channel(client_freq, &op_class, &channel); + } + + p2p_buf_add_oob_go_neg_channel(buf, p2p->cfg->country, op_class, + channel, role); + + if (p2p->num_groups > 0) { + /* Limit number of clients to avoid very long message */ + p2p_buf_add_group_info(p2p->groups[0], buf, 5); + p2p_group_buf_add_id(p2p->groups[0], buf); + } else if (client_freq > 0 && + go_dev_addr && !is_zero_ether_addr(go_dev_addr) && + ssid && ssid_len > 0) { + /* + * Add the optional P2P Group ID to indicate in which group this + * device is a P2P Client. + */ + p2p_buf_add_group_id(buf, go_dev_addr, ssid, ssid_len); + } + + return buf; +} + + +struct wpabuf * p2p_build_nfc_handover_req(struct p2p_data *p2p, + int client_freq, + const u8 *go_dev_addr, + const u8 *ssid, size_t ssid_len) +{ + return p2p_build_nfc_handover(p2p, client_freq, go_dev_addr, ssid, + ssid_len); +} + + +struct wpabuf * p2p_build_nfc_handover_sel(struct p2p_data *p2p, + int client_freq, + const u8 *go_dev_addr, + const u8 *ssid, size_t ssid_len) +{ + return p2p_build_nfc_handover(p2p, client_freq, go_dev_addr, ssid, + ssid_len); +} + + +int p2p_process_nfc_connection_handover(struct p2p_data *p2p, + struct p2p_nfc_params *params) +{ + struct p2p_message msg; + struct p2p_device *dev; + const u8 *p2p_dev_addr; + int freq; + enum p2p_role_indication role; + + params->next_step = NO_ACTION; + + if (p2p_parse_ies_separate(params->wsc_attr, params->wsc_len, + params->p2p_attr, params->p2p_len, &msg)) { + p2p_dbg(p2p, "Failed to parse WSC/P2P attributes from NFC"); + p2p_parse_free(&msg); + return -1; + } + + if (msg.p2p_device_addr) + p2p_dev_addr = msg.p2p_device_addr; + else if (msg.device_id) + p2p_dev_addr = msg.device_id; + else { + p2p_dbg(p2p, "Ignore scan data without P2P Device Info or P2P Device Id"); + p2p_parse_free(&msg); + return -1; + } + + if (msg.oob_dev_password) { + os_memcpy(params->oob_dev_pw, msg.oob_dev_password, + msg.oob_dev_password_len); + params->oob_dev_pw_len = msg.oob_dev_password_len; + } + + dev = p2p_create_device(p2p, p2p_dev_addr); + if (dev == NULL) { + p2p_parse_free(&msg); + return -1; + } + + params->peer = &dev->info; + + os_get_reltime(&dev->last_seen); + dev->flags &= ~(P2P_DEV_PROBE_REQ_ONLY | P2P_DEV_GROUP_CLIENT_ONLY); + p2p_copy_wps_info(p2p, dev, 0, &msg); + + if (!msg.oob_go_neg_channel) { + p2p_dbg(p2p, "OOB GO Negotiation Channel attribute not included"); + return -1; + } + + if (msg.oob_go_neg_channel[3] == 0 && + msg.oob_go_neg_channel[4] == 0) + freq = 0; + else + freq = p2p_channel_to_freq(msg.oob_go_neg_channel[3], + msg.oob_go_neg_channel[4]); + if (freq < 0) { + p2p_dbg(p2p, "Unknown peer OOB GO Neg channel"); + return -1; + } + role = msg.oob_go_neg_channel[5]; + + if (role == P2P_GO_IN_A_GROUP) { + p2p_dbg(p2p, "Peer OOB GO operating channel: %u MHz", freq); + params->go_freq = freq; + } else if (role == P2P_CLIENT_IN_A_GROUP) { + p2p_dbg(p2p, "Peer (client) OOB GO operating channel: %u MHz", + freq); + params->go_freq = freq; + } else + p2p_dbg(p2p, "Peer OOB GO Neg channel: %u MHz", freq); + dev->oob_go_neg_freq = freq; + + if (!params->sel && role != P2P_GO_IN_A_GROUP) { + freq = p2p_channel_to_freq(p2p->cfg->reg_class, + p2p->cfg->channel); + if (freq < 0) { + p2p_dbg(p2p, "Own listen channel not known"); + return -1; + } + p2p_dbg(p2p, "Use own Listen channel as OOB GO Neg channel: %u MHz", freq); + dev->oob_go_neg_freq = freq; + } + + if (msg.group_id) { + os_memcpy(params->go_dev_addr, msg.group_id, ETH_ALEN); + params->go_ssid_len = msg.group_id_len - ETH_ALEN; + os_memcpy(params->go_ssid, msg.group_id + ETH_ALEN, + params->go_ssid_len); + } + + p2p_parse_free(&msg); + + if (dev->flags & P2P_DEV_USER_REJECTED) { + p2p_dbg(p2p, "Do not report rejected device"); + return 0; + } + + if (!(dev->flags & P2P_DEV_REPORTED)) { + p2p->cfg->dev_found(p2p->cfg->cb_ctx, p2p_dev_addr, &dev->info, + !(dev->flags & P2P_DEV_REPORTED_ONCE)); + dev->flags |= P2P_DEV_REPORTED | P2P_DEV_REPORTED_ONCE; + } + + if (role == P2P_GO_IN_A_GROUP && p2p->num_groups > 0) + params->next_step = BOTH_GO; + else if (role == P2P_GO_IN_A_GROUP) + params->next_step = JOIN_GROUP; + else if (role == P2P_CLIENT_IN_A_GROUP) { + dev->flags |= P2P_DEV_GROUP_CLIENT_ONLY; + params->next_step = PEER_CLIENT; + } else if (p2p->num_groups > 0) + params->next_step = AUTH_JOIN; + else if (params->sel) + params->next_step = INIT_GO_NEG; + else + params->next_step = RESP_GO_NEG; + + return 0; +} + + +void p2p_set_authorized_oob_dev_pw_id(struct p2p_data *p2p, u16 dev_pw_id, + int go_intent, + const u8 *own_interface_addr) +{ + + p2p->authorized_oob_dev_pw_id = dev_pw_id; + if (dev_pw_id == 0) { + p2p_dbg(p2p, "NFC OOB Password unauthorized for static handover"); + return; + } + + p2p_dbg(p2p, "NFC OOB Password (id=%u) authorized for static handover", + dev_pw_id); + + p2p->go_intent = go_intent; + os_memcpy(p2p->intended_addr, own_interface_addr, ETH_ALEN); +} + +#endif /* CONFIG_WPS_NFC */ diff -Nru wpa-1.0/src/p2p/p2p_dev_disc.c wpa-2.1/src/p2p/p2p_dev_disc.c --- wpa-1.0/src/p2p/p2p_dev_disc.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/p2p/p2p_dev_disc.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * Wi-Fi Direct - P2P Device Discoverability procedure * Copyright (c) 2010, Atheros Communications * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -48,8 +42,7 @@ void p2p_dev_disc_req_cb(struct p2p_data *p2p, int success) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Device Discoverability Request TX callback: success=%d", + p2p_dbg(p2p, "Device Discoverability Request TX callback: success=%d", success); if (!success) { @@ -62,9 +55,7 @@ return; } - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: GO acknowledged Device Discoverability Request - wait " - "for response"); + p2p_dbg(p2p, "GO acknowledged Device Discoverability Request - wait for response"); /* * TODO: is the remain-on-channel from Action frame TX long enough for * most cases or should we try to increase its duration and/or start @@ -80,9 +71,7 @@ go = p2p_get_device(p2p, dev->member_in_go_dev); if (go == NULL || dev->oper_freq <= 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Could not find peer entry for GO and frequency " - "to send Device Discoverability Request"); + p2p_dbg(p2p, "Could not find peer entry for GO and frequency to send Device Discoverability Request"); return -1; } @@ -90,8 +79,7 @@ if (req == NULL) return -1; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Sending Device Discoverability Request to GO " MACSTR + p2p_dbg(p2p, "Sending Device Discoverability Request to GO " MACSTR " for client " MACSTR, MAC2STR(go->info.p2p_device_addr), MAC2STR(dev->info.p2p_device_addr)); @@ -103,8 +91,7 @@ if (p2p_send_action(p2p, dev->oper_freq, go->info.p2p_device_addr, p2p->cfg->dev_addr, go->info.p2p_device_addr, wpabuf_head(req), wpabuf_len(req), 1000) < 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Failed to send Action frame"); + p2p_dbg(p2p, "Failed to send Action frame"); wpabuf_free(req); /* TODO: how to recover from failure? */ return -1; @@ -137,8 +124,7 @@ void p2p_dev_disc_resp_cb(struct p2p_data *p2p, int success) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Device Discoverability Response TX callback: success=%d", + p2p_dbg(p2p, "Device Discoverability Response TX callback: success=%d", success); p2p->cfg->send_action_done(p2p->cfg->cb_ctx); } @@ -153,8 +139,7 @@ if (resp == NULL) return; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Sending Device Discoverability Response to " MACSTR + p2p_dbg(p2p, "Sending Device Discoverability Response to " MACSTR " (status %u freq %d)", MAC2STR(addr), status, freq); @@ -162,8 +147,7 @@ if (p2p_send_action(p2p, freq, addr, p2p->cfg->dev_addr, p2p->cfg->dev_addr, wpabuf_head(resp), wpabuf_len(resp), 200) < 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Failed to send Action frame"); + p2p_dbg(p2p, "Failed to send Action frame"); } wpabuf_free(resp); @@ -176,17 +160,14 @@ struct p2p_message msg; size_t g; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Received Device Discoverability Request from " MACSTR + p2p_dbg(p2p, "Received Device Discoverability Request from " MACSTR " (freq=%d)", MAC2STR(sa), rx_freq); if (p2p_parse(data, len, &msg)) return; if (msg.dialog_token == 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Invalid Dialog Token 0 (must be nonzero) in " - "Device Discoverability Request"); + p2p_dbg(p2p, "Invalid Dialog Token 0 (must be nonzero) in Device Discoverability Request"); p2p_send_dev_disc_resp(p2p, msg.dialog_token, sa, rx_freq, P2P_SC_FAIL_INVALID_PARAMS); p2p_parse_free(&msg); @@ -194,9 +175,7 @@ } if (msg.device_id == NULL) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: P2P Device ID attribute missing from Device " - "Discoverability Request"); + p2p_dbg(p2p, "P2P Device ID attribute missing from Device Discoverability Request"); p2p_send_dev_disc_resp(p2p, msg.dialog_token, sa, rx_freq, P2P_SC_FAIL_INVALID_PARAMS); p2p_parse_free(&msg); @@ -206,9 +185,7 @@ for (g = 0; g < p2p->num_groups; g++) { if (p2p_group_go_discover(p2p->groups[g], msg.device_id, sa, rx_freq) == 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Scheduled " - "GO Discoverability Request for the target " - "device"); + p2p_dbg(p2p, "Scheduled GO Discoverability Request for the target device"); /* * P2P group code will use a callback to indicate TX * status, so that we can reply to the request once the @@ -223,9 +200,7 @@ } } - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Requested client " - "was not found in any group or did not support client " - "discoverability"); + p2p_dbg(p2p, "Requested client was not found in any group or did not support client discoverability"); p2p_send_dev_disc_resp(p2p, msg.dialog_token, sa, rx_freq, P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE); p2p_parse_free(&msg); @@ -239,15 +214,13 @@ struct p2p_device *go; u8 status; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Received Device Discoverability Response from " MACSTR, + p2p_dbg(p2p, "Received Device Discoverability Response from " MACSTR, MAC2STR(sa)); go = p2p->pending_client_disc_go; if (go == NULL || os_memcmp(sa, go->info.p2p_device_addr, ETH_ALEN) != 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Ignore unexpected " - "Device Discoverability Response"); + p2p_dbg(p2p, "Ignore unexpected Device Discoverability Response"); return; } @@ -260,9 +233,7 @@ } if (msg.dialog_token != go->dialog_token) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Ignore Device " - "Discoverability Response with unexpected dialog " - "token %u (expected %u)", + p2p_dbg(p2p, "Ignore Device Discoverability Response with unexpected dialog token %u (expected %u)", msg.dialog_token, go->dialog_token); p2p_parse_free(&msg); return; @@ -271,17 +242,14 @@ status = *msg.status; p2p_parse_free(&msg); - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Device Discoverability Response status %u", status); + p2p_dbg(p2p, "Device Discoverability Response status %u", status); if (p2p->go_neg_peer == NULL || os_memcmp(p2p->pending_client_disc_addr, p2p->go_neg_peer->info.p2p_device_addr, ETH_ALEN) != 0 || os_memcmp(p2p->go_neg_peer->member_in_go_dev, go->info.p2p_device_addr, ETH_ALEN) != 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: No pending " - "operation with the client discoverability peer " - "anymore"); + p2p_dbg(p2p, "No pending operation with the client discoverability peer anymore"); return; } @@ -290,8 +258,7 @@ * Peer is expected to be awake for at least 100 TU; try to * connect immediately. */ - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Client discoverability request succeeded"); + p2p_dbg(p2p, "Client discoverability request succeeded"); if (p2p->state == P2P_CONNECT) { /* * Change state to force the timeout to start in @@ -307,8 +274,7 @@ * Client discoverability request failed; try to connect from * timeout. */ - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Client discoverability request failed"); + p2p_dbg(p2p, "Client discoverability request failed"); p2p_set_timeout(p2p, 0, 500000); } @@ -317,14 +283,12 @@ void p2p_go_disc_req_cb(struct p2p_data *p2p, int success) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: GO Discoverability Request TX callback: success=%d", + p2p_dbg(p2p, "GO Discoverability Request TX callback: success=%d", success); p2p->cfg->send_action_done(p2p->cfg->cb_ctx); if (p2p->pending_dev_disc_dialog_token == 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: No pending Device " - "Discoverability Request"); + p2p_dbg(p2p, "No pending Device Discoverability Request"); return; } @@ -344,9 +308,7 @@ unsigned int tu; struct wpabuf *ies; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Received GO Discoverability Request - remain awake for " - "100 TU"); + p2p_dbg(p2p, "Received GO Discoverability Request - remain awake for 100 TU"); ies = p2p_build_probe_resp_ies(p2p); if (ies == NULL) @@ -357,9 +319,7 @@ tu = 100; if (p2p->cfg->start_listen(p2p->cfg->cb_ctx, rx_freq, 1024 * tu / 1000, ies) < 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Failed to start listen mode for client " - "discoverability"); + p2p_dbg(p2p, "Failed to start listen mode for client discoverability"); } wpabuf_free(ies); } diff -Nru wpa-1.0/src/p2p/p2p_go_neg.c wpa-2.1/src/p2p/p2p_go_neg.c --- wpa-1.0/src/p2p/p2p_go_neg.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/p2p/p2p_go_neg.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * Wi-Fi Direct - P2P Group Owner Negotiation * Copyright (c) 2009-2010, Atheros Communications * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -55,8 +49,7 @@ os_memcpy(dev->country, pos, 3); wpa_hexdump_ascii(MSG_DEBUG, "P2P: Peer country", pos, 3); if (pos[2] != 0x04 && os_memcmp(pos, p2p->cfg->country, 2) != 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_INFO, - "P2P: Mismatching country (ours=%c%c peer's=%c%c)", + p2p_info(p2p, "Mismatching country (ours=%c%c peer's=%c%c)", p2p->cfg->country[0], p2p->cfg->country[1], pos[0], pos[1]); return -1; @@ -67,8 +60,7 @@ struct p2p_reg_class *cl = &ch->reg_class[ch->reg_classes]; cl->reg_class = *pos++; if (pos + 1 + pos[0] > end) { - wpa_msg(p2p->cfg->msg_ctx, MSG_INFO, - "P2P: Invalid peer Channel List"); + p2p_info(p2p, "Invalid peer Channel List"); return -1; } channels = *pos++; @@ -82,14 +74,12 @@ } p2p_channels_intersect(own, &dev->channels, &intersection); - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Own reg_classes %d " - "peer reg_classes %d intersection reg_classes %d", + p2p_dbg(p2p, "Own reg_classes %d peer reg_classes %d intersection reg_classes %d", (int) own->reg_classes, (int) dev->channels.reg_classes, (int) intersection.reg_classes); if (intersection.reg_classes == 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_INFO, - "P2P: No common channels found"); + p2p_info(p2p, "No common channels found"); return -1; } return 0; @@ -104,7 +94,7 @@ } -static u16 p2p_wps_method_pw_id(enum p2p_wps_method wps_method) +u16 p2p_wps_method_pw_id(enum p2p_wps_method wps_method) { switch (wps_method) { case WPS_PIN_DISPLAY: @@ -113,6 +103,8 @@ return DEV_PW_USER_SPECIFIED; case WPS_PBC: return DEV_PW_PUSHBUTTON; + case WPS_NFC: + return DEV_PW_NFC_CONNECTION_HANDOVER; default: return DEV_PW_DEFAULT; } @@ -128,6 +120,8 @@ return "Keypad"; case WPS_PBC: return "PBC"; + case WPS_NFC: + return "NFC"; default: return "??"; } @@ -140,14 +134,18 @@ struct wpabuf *buf; u8 *len; u8 group_capab; + size_t extra = 0; + u16 pw_id; - buf = wpabuf_alloc(1000); +#ifdef CONFIG_WIFI_DISPLAY + if (p2p->wfd_ie_go_neg) + extra = wpabuf_len(p2p->wfd_ie_go_neg); +#endif /* CONFIG_WIFI_DISPLAY */ + + buf = wpabuf_alloc(1000 + extra); if (buf == NULL) return NULL; - peer->dialog_token++; - if (peer->dialog_token == 0) - peer->dialog_token = 1; p2p_buf_add_public_action_hdr(buf, P2P_GO_NEG_REQ, peer->dialog_token); len = p2p_buf_add_ie_hdr(buf); @@ -161,11 +159,11 @@ group_capab |= P2P_GROUP_CAPAB_CROSS_CONN; if (p2p->cfg->p2p_intra_bss) group_capab |= P2P_GROUP_CAPAB_INTRA_BSS_DIST; - p2p_buf_add_capability(buf, p2p->dev_capab, group_capab); - p2p_buf_add_go_intent(buf, (p2p->go_intent << 1) | - p2p->next_tie_breaker); - p2p->next_tie_breaker = !p2p->next_tie_breaker; - p2p_buf_add_config_timeout(buf, 100, 20); + p2p_buf_add_capability(buf, p2p->dev_capab & + ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY, + group_capab); + p2p_buf_add_go_intent(buf, (p2p->go_intent << 1) | peer->tie_breaker); + p2p_buf_add_config_timeout(buf, p2p->go_timeout, p2p->client_timeout); p2p_buf_add_listen_channel(buf, p2p->cfg->country, p2p->cfg->reg_class, p2p->cfg->channel); if (p2p->ext_listen_interval) @@ -179,7 +177,19 @@ p2p_buf_update_ie_hdr(buf, len); /* WPS IE with Device Password ID attribute */ - p2p_build_wps_ie(p2p, buf, p2p_wps_method_pw_id(peer->wps_method), 0); + pw_id = p2p_wps_method_pw_id(peer->wps_method); + if (peer->oob_pw_id) + pw_id = peer->oob_pw_id; + if (p2p_build_wps_ie(p2p, buf, pw_id, 0) < 0) { + p2p_dbg(p2p, "Failed to build WPS IE for GO Negotiation Request"); + wpabuf_free(buf); + return NULL; + } + +#ifdef CONFIG_WIFI_DISPLAY + if (p2p->wfd_ie_go_neg) + wpabuf_put_buf(buf, p2p->wfd_ie_go_neg); +#endif /* CONFIG_WIFI_DISPLAY */ return buf; } @@ -190,11 +200,28 @@ struct wpabuf *req; int freq; + if (dev->flags & P2P_DEV_PD_BEFORE_GO_NEG) { + u16 config_method; + p2p_dbg(p2p, "Use PD-before-GO-Neg workaround for " MACSTR, + MAC2STR(dev->info.p2p_device_addr)); + if (dev->wps_method == WPS_PIN_DISPLAY) + config_method = WPS_CONFIG_KEYPAD; + else if (dev->wps_method == WPS_PIN_KEYPAD) + config_method = WPS_CONFIG_DISPLAY; + else if (dev->wps_method == WPS_PBC) + config_method = WPS_CONFIG_PUSHBUTTON; + else + return -1; + return p2p_prov_disc_req(p2p, dev->info.p2p_device_addr, + config_method, 0, 0, 1); + } + freq = dev->listen_freq > 0 ? dev->listen_freq : dev->oper_freq; + if (dev->oob_go_neg_freq > 0) + freq = dev->oob_go_neg_freq; if (freq <= 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: No Listen/Operating frequency known for the " - "peer " MACSTR " to send GO Negotiation Request", + p2p_dbg(p2p, "No Listen/Operating frequency known for the peer " + MACSTR " to send GO Negotiation Request", MAC2STR(dev->info.p2p_device_addr)); return -1; } @@ -202,8 +229,7 @@ req = p2p_build_go_neg_req(p2p, dev); if (req == NULL) return -1; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Sending GO Negotiation Request"); + p2p_dbg(p2p, "Sending GO Negotiation Request"); p2p_set_state(p2p, P2P_CONNECT); p2p->pending_action_state = P2P_PENDING_GO_NEG_REQUEST; p2p->go_neg_peer = dev; @@ -211,12 +237,12 @@ dev->connect_reqs++; if (p2p_send_action(p2p, freq, dev->info.p2p_device_addr, p2p->cfg->dev_addr, dev->info.p2p_device_addr, - wpabuf_head(req), wpabuf_len(req), 200) < 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Failed to send Action frame"); + wpabuf_head(req), wpabuf_len(req), 500) < 0) { + p2p_dbg(p2p, "Failed to send Action frame"); /* Use P2P find to recover and retry */ p2p_set_timeout(p2p, 0, 0); - } + } else + dev->go_neg_req_sent++; wpabuf_free(req); @@ -232,10 +258,17 @@ struct wpabuf *buf; u8 *len; u8 group_capab; + size_t extra = 0; + u16 pw_id; + + p2p_dbg(p2p, "Building GO Negotiation Response"); + +#ifdef CONFIG_WIFI_DISPLAY + if (p2p->wfd_ie_go_neg) + extra = wpabuf_len(p2p->wfd_ie_go_neg); +#endif /* CONFIG_WIFI_DISPLAY */ - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Building GO Negotiation Response"); - buf = wpabuf_alloc(1000); + buf = wpabuf_alloc(1000 + extra); if (buf == NULL) return NULL; @@ -256,12 +289,13 @@ if (p2p->cfg->p2p_intra_bss) group_capab |= P2P_GROUP_CAPAB_INTRA_BSS_DIST; } - p2p_buf_add_capability(buf, p2p->dev_capab, group_capab); + p2p_buf_add_capability(buf, p2p->dev_capab & + ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY, + group_capab); p2p_buf_add_go_intent(buf, (p2p->go_intent << 1) | tie_breaker); - p2p_buf_add_config_timeout(buf, 100, 20); + p2p_buf_add_config_timeout(buf, p2p->go_timeout, p2p->client_timeout); if (peer && peer->go_state == REMOTE_GO) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Omit Operating " - "Channel attribute"); + p2p_dbg(p2p, "Omit Operating Channel attribute"); } else { p2p_buf_add_operating_channel(buf, p2p->cfg->country, p2p->op_reg_class, @@ -288,35 +322,78 @@ p2p_buf_update_ie_hdr(buf, len); /* WPS IE with Device Password ID attribute */ - p2p_build_wps_ie(p2p, buf, - p2p_wps_method_pw_id(peer ? peer->wps_method : - WPS_NOT_READY), 0); + pw_id = p2p_wps_method_pw_id(peer ? peer->wps_method : WPS_NOT_READY); + if (peer && peer->oob_pw_id) + pw_id = peer->oob_pw_id; + if (p2p_build_wps_ie(p2p, buf, pw_id, 0) < 0) { + p2p_dbg(p2p, "Failed to build WPS IE for GO Negotiation Response"); + wpabuf_free(buf); + return NULL; + } + +#ifdef CONFIG_WIFI_DISPLAY + if (p2p->wfd_ie_go_neg) + wpabuf_put_buf(buf, p2p->wfd_ie_go_neg); +#endif /* CONFIG_WIFI_DISPLAY */ + return buf; } -static void p2p_reselect_channel(struct p2p_data *p2p, - struct p2p_channels *intersection) +/** + * p2p_reselect_channel - Re-select operating channel based on peer information + * @p2p: P2P module context from p2p_init() + * @intersection: Support channel list intersection from local and peer + * + * This function is used to re-select the best channel after having received + * information from the peer to allow supported channel lists to be intersected. + * This can be used to improve initial channel selection done in + * p2p_prepare_channel() prior to the start of GO Negotiation. In addition, this + * can be used for Invitation case. + */ +void p2p_reselect_channel(struct p2p_data *p2p, + struct p2p_channels *intersection) { struct p2p_reg_class *cl; int freq; u8 op_reg_class, op_channel; unsigned int i; + const int op_classes_5ghz[] = { 124, 115, 0 }; + const int op_classes_ht40[] = { 126, 127, 116, 117, 0 }; + const int op_classes_vht[] = { 128, 0 }; + + if (p2p->own_freq_preference > 0 && + p2p_freq_to_channel(p2p->own_freq_preference, + &op_reg_class, &op_channel) == 0 && + p2p_channels_includes(intersection, op_reg_class, op_channel)) { + p2p_dbg(p2p, "Pick own channel preference (reg_class %u channel %u) from intersection", + op_reg_class, op_channel); + p2p->op_reg_class = op_reg_class; + p2p->op_channel = op_channel; + return; + } - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Selected operating " - "channel (reg_class %u channel %u) not acceptable to the " - "peer", p2p->op_reg_class, p2p->op_channel); + if (p2p->best_freq_overall > 0 && + p2p_freq_to_channel(p2p->best_freq_overall, + &op_reg_class, &op_channel) == 0 && + p2p_channels_includes(intersection, op_reg_class, op_channel)) { + p2p_dbg(p2p, "Pick best overall channel (reg_class %u channel %u) from intersection", + op_reg_class, op_channel); + p2p->op_reg_class = op_reg_class; + p2p->op_channel = op_channel; + return; + } /* First, try to pick the best channel from another band */ - freq = p2p_channel_to_freq(p2p->cfg->country, p2p->op_reg_class, - p2p->op_channel); + freq = p2p_channel_to_freq(p2p->op_reg_class, p2p->op_channel); if (freq >= 2400 && freq < 2500 && p2p->best_freq_5 > 0 && - p2p_freq_to_channel(p2p->cfg->country, p2p->best_freq_5, + !p2p_channels_includes(intersection, p2p->op_reg_class, + p2p->op_channel) && + p2p_freq_to_channel(p2p->best_freq_5, &op_reg_class, &op_channel) == 0 && p2p_channels_includes(intersection, op_reg_class, op_channel)) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Pick best 5 GHz " - "channel (reg_class %u channel %u) from intersection", + p2p_dbg(p2p, "Pick best 5 GHz channel (reg_class %u channel %u) from intersection", op_reg_class, op_channel); p2p->op_reg_class = op_reg_class; p2p->op_channel = op_channel; @@ -324,11 +401,12 @@ } if (freq >= 4900 && freq < 6000 && p2p->best_freq_24 > 0 && - p2p_freq_to_channel(p2p->cfg->country, p2p->best_freq_24, + !p2p_channels_includes(intersection, p2p->op_reg_class, + p2p->op_channel) && + p2p_freq_to_channel(p2p->best_freq_24, &op_reg_class, &op_channel) == 0 && p2p_channels_includes(intersection, op_reg_class, op_channel)) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Pick best 2.4 GHz " - "channel (reg_class %u channel %u) from intersection", + p2p_dbg(p2p, "Pick best 2.4 GHz channel (reg_class %u channel %u) from intersection", op_reg_class, op_channel); p2p->op_reg_class = op_reg_class; p2p->op_channel = op_channel; @@ -342,27 +420,108 @@ p2p->cfg->pref_chan[i].chan)) { p2p->op_reg_class = p2p->cfg->pref_chan[i].op_class; p2p->op_channel = p2p->cfg->pref_chan[i].chan; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Pick " - "highest preferred chnnel (op_class %u " - "channel %u) from intersection", + p2p_dbg(p2p, "Pick highest preferred channel (op_class %u channel %u) from intersection", p2p->op_reg_class, p2p->op_channel); return; } } + /* Try a channel where we might be able to use VHT */ + if (p2p_channel_select(intersection, op_classes_vht, + &p2p->op_reg_class, &p2p->op_channel) == 0) { + p2p_dbg(p2p, "Pick possible VHT channel (op_class %u channel %u) from intersection", + p2p->op_reg_class, p2p->op_channel); + return; + } + + /* Try a channel where we might be able to use HT40 */ + if (p2p_channel_select(intersection, op_classes_ht40, + &p2p->op_reg_class, &p2p->op_channel) == 0) { + p2p_dbg(p2p, "Pick possible HT40 channel (op_class %u channel %u) from intersection", + p2p->op_reg_class, p2p->op_channel); + return; + } + + /* Prefer a 5 GHz channel */ + if (p2p_channel_select(intersection, op_classes_5ghz, + &p2p->op_reg_class, &p2p->op_channel) == 0) { + p2p_dbg(p2p, "Pick possible 5 GHz channel (op_class %u channel %u) from intersection", + p2p->op_reg_class, p2p->op_channel); + return; + } + + /* + * Try to see if the original channel is in the intersection. If + * so, no need to change anything, as it already contains some + * randomness. + */ + if (p2p_channels_includes(intersection, p2p->op_reg_class, + p2p->op_channel)) { + p2p_dbg(p2p, "Using original operating class and channel (op_class %u channel %u) from intersection", + p2p->op_reg_class, p2p->op_channel); + return; + } + /* * Fall back to whatever is included in the channel intersection since * no better options seems to be available. */ cl = &intersection->reg_class[0]; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Pick another channel " - "(reg_class %u channel %u) from intersection", + p2p_dbg(p2p, "Pick another channel (reg_class %u channel %u) from intersection", cl->reg_class, cl->channel[0]); p2p->op_reg_class = cl->reg_class; p2p->op_channel = cl->channel[0]; } +static int p2p_go_select_channel(struct p2p_data *p2p, struct p2p_device *dev, + u8 *status) +{ + struct p2p_channels tmp, intersection; + + p2p_channels_dump(p2p, "own channels", &p2p->channels); + p2p_channels_dump(p2p, "peer channels", &dev->channels); + p2p_channels_intersect(&p2p->channels, &dev->channels, &tmp); + p2p_channels_dump(p2p, "intersection", &tmp); + p2p_channels_remove_freqs(&tmp, &p2p->no_go_freq); + p2p_channels_dump(p2p, "intersection after no-GO removal", &tmp); + p2p_channels_intersect(&tmp, &p2p->cfg->channels, &intersection); + p2p_channels_dump(p2p, "intersection with local channel list", + &intersection); + if (intersection.reg_classes == 0 || + intersection.reg_class[0].channels == 0) { + *status = P2P_SC_FAIL_NO_COMMON_CHANNELS; + p2p_dbg(p2p, "No common channels found"); + return -1; + } + + if (!p2p_channels_includes(&intersection, p2p->op_reg_class, + p2p->op_channel)) { + if (dev->flags & P2P_DEV_FORCE_FREQ) { + *status = P2P_SC_FAIL_NO_COMMON_CHANNELS; + p2p_dbg(p2p, "Peer does not support the forced channel"); + return -1; + } + + p2p_dbg(p2p, "Selected operating channel (op_class %u channel %u) not acceptable to the peer", + p2p->op_reg_class, p2p->op_channel); + p2p_reselect_channel(p2p, &intersection); + } else if (!(dev->flags & P2P_DEV_FORCE_FREQ) && + !p2p->cfg->cfg_op_channel) { + p2p_dbg(p2p, "Try to optimize channel selection with peer information received; previously selected op_class %u channel %u", + p2p->op_reg_class, p2p->op_channel); + p2p_reselect_channel(p2p, &intersection); + } + + if (!p2p->ssid_set) { + p2p_build_ssid(p2p, p2p->ssid, &p2p->ssid_len); + p2p->ssid_set = 1; + } + + return 0; +} + + void p2p_process_go_neg_req(struct p2p_data *p2p, const u8 *sa, const u8 *data, size_t len, int rx_freq) { @@ -373,17 +532,14 @@ int tie_breaker = 0; int freq; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Received GO Negotiation Request from " MACSTR - "(freq=%d)", MAC2STR(sa), rx_freq); + p2p_dbg(p2p, "Received GO Negotiation Request from " MACSTR "(freq=%d)", + MAC2STR(sa), rx_freq); if (p2p_parse(data, len, &msg)) return; if (!msg.capability) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Mandatory Capability attribute missing from GO " - "Negotiation Request"); + p2p_dbg(p2p, "Mandatory Capability attribute missing from GO Negotiation Request"); #ifdef CONFIG_P2P_STRICT goto fail; #endif /* CONFIG_P2P_STRICT */ @@ -392,53 +548,42 @@ if (msg.go_intent) tie_breaker = *msg.go_intent & 0x01; else { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Mandatory GO Intent attribute missing from GO " - "Negotiation Request"); + p2p_dbg(p2p, "Mandatory GO Intent attribute missing from GO Negotiation Request"); #ifdef CONFIG_P2P_STRICT goto fail; #endif /* CONFIG_P2P_STRICT */ } if (!msg.config_timeout) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Mandatory Configuration Timeout attribute " - "missing from GO Negotiation Request"); + p2p_dbg(p2p, "Mandatory Configuration Timeout attribute missing from GO Negotiation Request"); #ifdef CONFIG_P2P_STRICT goto fail; #endif /* CONFIG_P2P_STRICT */ } if (!msg.listen_channel) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: No Listen Channel attribute received"); + p2p_dbg(p2p, "No Listen Channel attribute received"); goto fail; } if (!msg.operating_channel) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: No Operating Channel attribute received"); + p2p_dbg(p2p, "No Operating Channel attribute received"); goto fail; } if (!msg.channel_list) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: No Channel List attribute received"); + p2p_dbg(p2p, "No Channel List attribute received"); goto fail; } if (!msg.intended_addr) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: No Intended P2P Interface Address attribute " - "received"); + p2p_dbg(p2p, "No Intended P2P Interface Address attribute received"); goto fail; } if (!msg.p2p_device_info) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: No P2P Device Info attribute received"); + p2p_dbg(p2p, "No P2P Device Info attribute received"); goto fail; } if (os_memcmp(msg.p2p_device_addr, sa, ETH_ALEN) != 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Unexpected GO Negotiation Request SA=" MACSTR + p2p_dbg(p2p, "Unexpected GO Negotiation Request SA=" MACSTR " != dev_addr=" MACSTR, MAC2STR(sa), MAC2STR(msg.p2p_device_addr)); goto fail; @@ -447,9 +592,8 @@ dev = p2p_get_device(p2p, sa); if (msg.status && *msg.status) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Unexpected Status attribute (%d) in GO " - "Negotiation Request", *msg.status); + p2p_dbg(p2p, "Unexpected Status attribute (%d) in GO Negotiation Request", + *msg.status); goto fail; } @@ -457,174 +601,155 @@ dev = p2p_add_dev_from_go_neg_req(p2p, sa, &msg); else if (dev->flags & P2P_DEV_PROBE_REQ_ONLY) p2p_add_dev_info(p2p, sa, dev, &msg); + else if (!dev->listen_freq && !dev->oper_freq) { + /* + * This may happen if the peer entry was added based on PD + * Request and no Probe Request/Response frame has been received + * from this peer (or that information has timed out). + */ + p2p_dbg(p2p, "Update peer " MACSTR + " based on GO Neg Req since listen/oper freq not known", + MAC2STR(dev->info.p2p_device_addr)); + p2p_add_dev_info(p2p, sa, dev, &msg); + } + if (dev && dev->flags & P2P_DEV_USER_REJECTED) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: User has rejected this peer"); + p2p_dbg(p2p, "User has rejected this peer"); status = P2P_SC_FAIL_REJECTED_BY_USER; - } else if (dev == NULL || dev->wps_method == WPS_NOT_READY) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Not ready for GO negotiation with " MACSTR, + } else if (dev == NULL || + (dev->wps_method == WPS_NOT_READY && + (p2p->authorized_oob_dev_pw_id == 0 || + p2p->authorized_oob_dev_pw_id != + msg.dev_password_id))) { + p2p_dbg(p2p, "Not ready for GO negotiation with " MACSTR, MAC2STR(sa)); status = P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE; - if (dev) - dev->flags |= P2P_DEV_PEER_WAITING_RESPONSE; p2p->cfg->go_neg_req_rx(p2p->cfg->cb_ctx, sa, msg.dev_password_id); } else if (p2p->go_neg_peer && p2p->go_neg_peer != dev) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Already in Group Formation with another peer"); + p2p_dbg(p2p, "Already in Group Formation with another peer"); status = P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE; } else { int go; if (!p2p->go_neg_peer) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Starting " - "GO Negotiation with previously authorized " - "peer"); + p2p_dbg(p2p, "Starting GO Negotiation with previously authorized peer"); if (!(dev->flags & P2P_DEV_FORCE_FREQ)) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Use default channel settings"); + p2p_dbg(p2p, "Use default channel settings"); p2p->op_reg_class = p2p->cfg->op_reg_class; p2p->op_channel = p2p->cfg->op_channel; os_memcpy(&p2p->channels, &p2p->cfg->channels, sizeof(struct p2p_channels)); } else { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Use previously configured " - "forced channel settings"); + p2p_dbg(p2p, "Use previously configured forced channel settings"); } } dev->flags &= ~P2P_DEV_NOT_YET_READY; if (!msg.go_intent) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: No GO Intent attribute received"); + p2p_dbg(p2p, "No GO Intent attribute received"); goto fail; } if ((*msg.go_intent >> 1) > P2P_MAX_GO_INTENT) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Invalid GO Intent value (%u) received", + p2p_dbg(p2p, "Invalid GO Intent value (%u) received", *msg.go_intent >> 1); goto fail; } if (dev->go_neg_req_sent && os_memcmp(sa, p2p->cfg->dev_addr, ETH_ALEN) > 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Do not reply since peer has higher " - "address and GO Neg Request already sent"); + p2p_dbg(p2p, "Do not reply since peer has higher address and GO Neg Request already sent"); p2p_parse_free(&msg); return; } go = p2p_go_det(p2p->go_intent, *msg.go_intent); if (go < 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Incompatible GO Intent"); + p2p_dbg(p2p, "Incompatible GO Intent"); status = P2P_SC_FAIL_BOTH_GO_INTENT_15; goto fail; } if (p2p_peer_channels(p2p, dev, msg.channel_list, msg.channel_list_len) < 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: No common channels found"); + p2p_dbg(p2p, "No common channels found"); status = P2P_SC_FAIL_NO_COMMON_CHANNELS; goto fail; } switch (msg.dev_password_id) { case DEV_PW_REGISTRAR_SPECIFIED: - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: PIN from peer Display"); + p2p_dbg(p2p, "PIN from peer Display"); if (dev->wps_method != WPS_PIN_KEYPAD) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: We have wps_method=%s -> " - "incompatible", + p2p_dbg(p2p, "We have wps_method=%s -> incompatible", p2p_wps_method_str(dev->wps_method)); status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD; goto fail; } break; case DEV_PW_USER_SPECIFIED: - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Peer entered PIN on Keypad"); + p2p_dbg(p2p, "Peer entered PIN on Keypad"); if (dev->wps_method != WPS_PIN_DISPLAY) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: We have wps_method=%s -> " - "incompatible", + p2p_dbg(p2p, "We have wps_method=%s -> incompatible", p2p_wps_method_str(dev->wps_method)); status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD; goto fail; } break; case DEV_PW_PUSHBUTTON: - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Peer using pushbutton"); + p2p_dbg(p2p, "Peer using pushbutton"); if (dev->wps_method != WPS_PBC) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: We have wps_method=%s -> " - "incompatible", + p2p_dbg(p2p, "We have wps_method=%s -> incompatible", p2p_wps_method_str(dev->wps_method)); status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD; goto fail; } break; default: - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Unsupported Device Password ID %d", + if (msg.dev_password_id && + msg.dev_password_id == dev->oob_pw_id) { + p2p_dbg(p2p, "Peer using NFC"); + if (dev->wps_method != WPS_NFC) { + p2p_dbg(p2p, "We have wps_method=%s -> incompatible", + p2p_wps_method_str( + dev->wps_method)); + status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD; + goto fail; + } + break; + } +#ifdef CONFIG_WPS_NFC + if (p2p->authorized_oob_dev_pw_id && + msg.dev_password_id == + p2p->authorized_oob_dev_pw_id) { + p2p_dbg(p2p, "Using static handover with our device password from NFC Tag"); + dev->wps_method = WPS_NFC; + dev->oob_pw_id = p2p->authorized_oob_dev_pw_id; + break; + } +#endif /* CONFIG_WPS_NFC */ + p2p_dbg(p2p, "Unsupported Device Password ID %d", msg.dev_password_id); status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD; goto fail; } - if (go) { - struct p2p_channels intersection; - size_t i; - p2p_channels_intersect(&p2p->channels, &dev->channels, - &intersection); - if (intersection.reg_classes == 0 || - intersection.reg_class[0].channels == 0) { - status = P2P_SC_FAIL_NO_COMMON_CHANNELS; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: No common channels found"); - goto fail; - } - for (i = 0; i < intersection.reg_classes; i++) { - struct p2p_reg_class *c; - c = &intersection.reg_class[i]; - wpa_printf(MSG_DEBUG, "P2P: reg_class %u", - c->reg_class); - wpa_hexdump(MSG_DEBUG, "P2P: channels", - c->channel, c->channels); - } - if (!p2p_channels_includes(&intersection, - p2p->op_reg_class, - p2p->op_channel)) - p2p_reselect_channel(p2p, &intersection); - - if (!p2p->ssid_set) { - p2p_build_ssid(p2p, p2p->ssid, &p2p->ssid_len); - p2p->ssid_set = 1; - } - } + if (go && p2p_go_select_channel(p2p, dev, &status) < 0) + goto fail; dev->go_state = go ? LOCAL_GO : REMOTE_GO; - dev->oper_freq = p2p_channel_to_freq((const char *) - msg.operating_channel, - msg.operating_channel[3], + dev->oper_freq = p2p_channel_to_freq(msg.operating_channel[3], msg.operating_channel[4]); - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Peer operating " - "channel preference: %d MHz", dev->oper_freq); + p2p_dbg(p2p, "Peer operating channel preference: %d MHz", + dev->oper_freq); if (msg.config_timeout) { dev->go_timeout = msg.config_timeout[0]; dev->client_timeout = msg.config_timeout[1]; } - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: GO Negotiation with " MACSTR, MAC2STR(sa)); + p2p_dbg(p2p, "GO Negotiation with " MACSTR, MAC2STR(sa)); if (p2p->state != P2P_IDLE) p2p_stop_find_for_freq(p2p, rx_freq); p2p_set_state(p2p, P2P_GO_NEG); @@ -643,31 +768,38 @@ p2p_parse_free(&msg); if (resp == NULL) return; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Sending GO Negotiation Response"); + p2p_dbg(p2p, "Sending GO Negotiation Response"); if (rx_freq > 0) freq = rx_freq; else - freq = p2p_channel_to_freq(p2p->cfg->country, - p2p->cfg->reg_class, + freq = p2p_channel_to_freq(p2p->cfg->reg_class, p2p->cfg->channel); if (freq < 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Unknown regulatory class/channel"); + p2p_dbg(p2p, "Unknown regulatory class/channel"); wpabuf_free(resp); return; } if (status == P2P_SC_SUCCESS) { p2p->pending_action_state = P2P_PENDING_GO_NEG_RESPONSE; dev->flags |= P2P_DEV_WAIT_GO_NEG_CONFIRM; + if (os_memcmp(sa, p2p->cfg->dev_addr, ETH_ALEN) < 0) { + /* + * Peer has smaller address, so the GO Negotiation + * Response from us is expected to complete + * negotiation. Ignore a GO Negotiation Response from + * the peer if it happens to be received after this + * point due to a race condition in GO Negotiation + * Request transmission and processing. + */ + dev->flags &= ~P2P_DEV_WAIT_GO_NEG_RESPONSE; + } } else p2p->pending_action_state = P2P_PENDING_GO_NEG_RESPONSE_FAILURE; if (p2p_send_action(p2p, freq, sa, p2p->cfg->dev_addr, p2p->cfg->dev_addr, - wpabuf_head(resp), wpabuf_len(resp), 200) < 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Failed to send Action frame"); + wpabuf_head(resp), wpabuf_len(resp), 500) < 0) { + p2p_dbg(p2p, "Failed to send Action frame"); } wpabuf_free(resp); @@ -683,10 +815,16 @@ u8 *len; struct p2p_channels res; u8 group_capab; + size_t extra = 0; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Building GO Negotiation Confirm"); - buf = wpabuf_alloc(1000); + p2p_dbg(p2p, "Building GO Negotiation Confirm"); + +#ifdef CONFIG_WIFI_DISPLAY + if (p2p->wfd_ie_go_neg) + extra = wpabuf_len(p2p->wfd_ie_go_neg); +#endif /* CONFIG_WIFI_DISPLAY */ + + buf = wpabuf_alloc(1000 + extra); if (buf == NULL) return NULL; @@ -707,7 +845,9 @@ if (p2p->cfg->p2p_intra_bss) group_capab |= P2P_GROUP_CAPAB_INTRA_BSS_DIST; } - p2p_buf_add_capability(buf, p2p->dev_capab, group_capab); + p2p_buf_add_capability(buf, p2p->dev_capab & + ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY, + group_capab); if (go || resp_chan == NULL) p2p_buf_add_operating_channel(buf, p2p->cfg->country, p2p->op_reg_class, @@ -723,6 +863,11 @@ } p2p_buf_update_ie_hdr(buf, len); +#ifdef CONFIG_WIFI_DISPLAY + if (p2p->wfd_ie_go_neg) + wpabuf_put_buf(buf, p2p->wfd_ie_go_neg); +#endif /* CONFIG_WIFI_DISPLAY */ + return buf; } @@ -737,14 +882,12 @@ u8 status = P2P_SC_SUCCESS; int freq; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Received GO Negotiation Response from " MACSTR + p2p_dbg(p2p, "Received GO Negotiation Response from " MACSTR " (freq=%d)", MAC2STR(sa), rx_freq); dev = p2p_get_device(p2p, sa); if (dev == NULL || dev->wps_method == WPS_NOT_READY || dev != p2p->go_neg_peer) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Not ready for GO negotiation with " MACSTR, + p2p_dbg(p2p, "Not ready for GO negotiation with " MACSTR, MAC2STR(sa)); return; } @@ -753,44 +896,35 @@ return; if (!(dev->flags & P2P_DEV_WAIT_GO_NEG_RESPONSE)) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Was not expecting GO Negotiation Response - " - "ignore"); + p2p_dbg(p2p, "Was not expecting GO Negotiation Response - ignore"); p2p_parse_free(&msg); return; } dev->flags &= ~P2P_DEV_WAIT_GO_NEG_RESPONSE; if (msg.dialog_token != dev->dialog_token) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Unexpected Dialog Token %u (expected %u)", + p2p_dbg(p2p, "Unexpected Dialog Token %u (expected %u)", msg.dialog_token, dev->dialog_token); p2p_parse_free(&msg); return; } if (!msg.status) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: No Status attribute received"); + p2p_dbg(p2p, "No Status attribute received"); status = P2P_SC_FAIL_INVALID_PARAMS; goto fail; } if (*msg.status) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: GO Negotiation rejected: status %d", - *msg.status); + p2p_dbg(p2p, "GO Negotiation rejected: status %d", *msg.status); dev->go_neg_req_sent = 0; if (*msg.status == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Wait for the peer to become ready for " - "GO Negotiation"); + p2p_dbg(p2p, "Wait for the peer to become ready for GO Negotiation"); dev->flags |= P2P_DEV_NOT_YET_READY; dev->wait_count = 0; p2p_set_state(p2p, P2P_WAIT_PEER_IDLE); p2p_set_timeout(p2p, 0, 0); } else { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Stop GO Negotiation attempt"); + p2p_dbg(p2p, "Stop GO Negotiation attempt"); p2p_go_neg_failed(p2p, dev, *msg.status); } p2p->cfg->send_action_done(p2p->cfg->cb_ctx); @@ -799,9 +933,7 @@ } if (!msg.capability) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Mandatory Capability attribute missing from GO " - "Negotiation Response"); + p2p_dbg(p2p, "Mandatory Capability attribute missing from GO Negotiation Response"); #ifdef CONFIG_P2P_STRICT status = P2P_SC_FAIL_INVALID_PARAMS; goto fail; @@ -809,9 +941,7 @@ } if (!msg.p2p_device_info) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Mandatory P2P Device Info attribute missing " - "from GO Negotiation Response"); + p2p_dbg(p2p, "Mandatory P2P Device Info attribute missing from GO Negotiation Response"); #ifdef CONFIG_P2P_STRICT status = P2P_SC_FAIL_INVALID_PARAMS; goto fail; @@ -819,22 +949,18 @@ } if (!msg.intended_addr) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: No Intended P2P Interface Address attribute " - "received"); + p2p_dbg(p2p, "No Intended P2P Interface Address attribute received"); status = P2P_SC_FAIL_INVALID_PARAMS; goto fail; } if (!msg.go_intent) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: No GO Intent attribute received"); + p2p_dbg(p2p, "No GO Intent attribute received"); status = P2P_SC_FAIL_INVALID_PARAMS; goto fail; } if ((*msg.go_intent >> 1) > P2P_MAX_GO_INTENT) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Invalid GO Intent value (%u) received", + p2p_dbg(p2p, "Invalid GO Intent value (%u) received", *msg.go_intent >> 1); status = P2P_SC_FAIL_INVALID_PARAMS; goto fail; @@ -842,8 +968,7 @@ go = p2p_go_det(p2p->go_intent, *msg.go_intent); if (go < 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Incompatible GO Intent"); + p2p_dbg(p2p, "Incompatible GO Intent"); status = P2P_SC_FAIL_INCOMPATIBLE_PARAMS; goto fail; } @@ -853,20 +978,14 @@ p2p->ssid_len = msg.group_id_len - ETH_ALEN; os_memcpy(p2p->ssid, msg.group_id + ETH_ALEN, p2p->ssid_len); } else if (!go) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Mandatory P2P Group ID attribute missing from " - "GO Negotiation Response"); + p2p_dbg(p2p, "Mandatory P2P Group ID attribute missing from GO Negotiation Response"); p2p->ssid_len = 0; -#ifdef CONFIG_P2P_STRICT status = P2P_SC_FAIL_INVALID_PARAMS; goto fail; -#endif /* CONFIG_P2P_STRICT */ } if (!msg.config_timeout) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Mandatory Configuration Timeout attribute " - "missing from GO Negotiation Response"); + p2p_dbg(p2p, "Mandatory Configuration Timeout attribute missing from GO Negotiation Response"); #ifdef CONFIG_P2P_STRICT status = P2P_SC_FAIL_INVALID_PARAMS; goto fail; @@ -881,116 +1000,84 @@ * Note: P2P Client may omit Operating Channel attribute to * indicate it does not have a preference. */ - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: No Operating Channel attribute received"); + p2p_dbg(p2p, "No Operating Channel attribute received"); status = P2P_SC_FAIL_INVALID_PARAMS; goto fail; } if (!msg.channel_list) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: No Channel List attribute received"); + p2p_dbg(p2p, "No Channel List attribute received"); status = P2P_SC_FAIL_INVALID_PARAMS; goto fail; } if (p2p_peer_channels(p2p, dev, msg.channel_list, msg.channel_list_len) < 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: No common channels found"); + p2p_dbg(p2p, "No common channels found"); status = P2P_SC_FAIL_NO_COMMON_CHANNELS; goto fail; } if (msg.operating_channel) { - dev->oper_freq = p2p_channel_to_freq((const char *) - msg.operating_channel, - msg.operating_channel[3], + dev->oper_freq = p2p_channel_to_freq(msg.operating_channel[3], msg.operating_channel[4]); - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Peer operating " - "channel preference: %d MHz", dev->oper_freq); + p2p_dbg(p2p, "Peer operating channel preference: %d MHz", + dev->oper_freq); } else dev->oper_freq = 0; switch (msg.dev_password_id) { case DEV_PW_REGISTRAR_SPECIFIED: - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: PIN from peer Display"); + p2p_dbg(p2p, "PIN from peer Display"); if (dev->wps_method != WPS_PIN_KEYPAD) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: We have wps_method=%s -> " - "incompatible", + p2p_dbg(p2p, "We have wps_method=%s -> incompatible", p2p_wps_method_str(dev->wps_method)); status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD; goto fail; } break; case DEV_PW_USER_SPECIFIED: - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Peer entered PIN on Keypad"); + p2p_dbg(p2p, "Peer entered PIN on Keypad"); if (dev->wps_method != WPS_PIN_DISPLAY) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: We have wps_method=%s -> " - "incompatible", + p2p_dbg(p2p, "We have wps_method=%s -> incompatible", p2p_wps_method_str(dev->wps_method)); status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD; goto fail; } break; case DEV_PW_PUSHBUTTON: - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Peer using pushbutton"); + p2p_dbg(p2p, "Peer using pushbutton"); if (dev->wps_method != WPS_PBC) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: We have wps_method=%s -> " - "incompatible", + p2p_dbg(p2p, "We have wps_method=%s -> incompatible", p2p_wps_method_str(dev->wps_method)); status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD; goto fail; } break; default: - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Unsupported Device Password ID %d", + if (msg.dev_password_id && + msg.dev_password_id == dev->oob_pw_id) { + p2p_dbg(p2p, "Peer using NFC"); + if (dev->wps_method != WPS_NFC) { + p2p_dbg(p2p, "We have wps_method=%s -> incompatible", + p2p_wps_method_str(dev->wps_method)); + status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD; + goto fail; + } + break; + } + p2p_dbg(p2p, "Unsupported Device Password ID %d", msg.dev_password_id); status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD; goto fail; } - if (go) { - struct p2p_channels intersection; - size_t i; - p2p_channels_intersect(&p2p->channels, &dev->channels, - &intersection); - if (intersection.reg_classes == 0 || - intersection.reg_class[0].channels == 0) { - status = P2P_SC_FAIL_NO_COMMON_CHANNELS; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: No common channels found"); - goto fail; - } - for (i = 0; i < intersection.reg_classes; i++) { - struct p2p_reg_class *c; - c = &intersection.reg_class[i]; - wpa_printf(MSG_DEBUG, "P2P: reg_class %u", - c->reg_class); - wpa_hexdump(MSG_DEBUG, "P2P: channels", - c->channel, c->channels); - } - if (!p2p_channels_includes(&intersection, p2p->op_reg_class, - p2p->op_channel)) - p2p_reselect_channel(p2p, &intersection); - - if (!p2p->ssid_set) { - p2p_build_ssid(p2p, p2p->ssid, &p2p->ssid_len); - p2p->ssid_set = 1; - } - } + if (go && p2p_go_select_channel(p2p, dev, &status) < 0) + goto fail; p2p_set_state(p2p, P2P_GO_NEG); p2p_clear_timeout(p2p); - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: GO Negotiation with " MACSTR, MAC2STR(sa)); + p2p_dbg(p2p, "GO Negotiation with " MACSTR, MAC2STR(sa)); os_memcpy(dev->intended_addr, msg.intended_addr, ETH_ALEN); fail: @@ -999,8 +1086,7 @@ p2p_parse_free(&msg); if (conf == NULL) return; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Sending GO Negotiation Confirm"); + p2p_dbg(p2p, "Sending GO Negotiation Confirm"); if (status == P2P_SC_SUCCESS) { p2p->pending_action_state = P2P_PENDING_GO_NEG_CONFIRM; dev->go_state = go ? LOCAL_GO : REMOTE_GO; @@ -1012,11 +1098,15 @@ freq = dev->listen_freq; if (p2p_send_action(p2p, freq, sa, p2p->cfg->dev_addr, sa, wpabuf_head(conf), wpabuf_len(conf), 200) < 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Failed to send Action frame"); + p2p_dbg(p2p, "Failed to send Action frame"); p2p_go_neg_failed(p2p, dev, -1); + p2p->cfg->send_action_done(p2p->cfg->cb_ctx); } wpabuf_free(conf); + if (status != P2P_SC_SUCCESS) { + p2p_dbg(p2p, "GO Negotiation failed"); + p2p_go_neg_failed(p2p, dev, status); + } } @@ -1026,22 +1116,18 @@ struct p2p_device *dev; struct p2p_message msg; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Received GO Negotiation Confirm from " MACSTR, + p2p_dbg(p2p, "Received GO Negotiation Confirm from " MACSTR, MAC2STR(sa)); dev = p2p_get_device(p2p, sa); if (dev == NULL || dev->wps_method == WPS_NOT_READY || dev != p2p->go_neg_peer) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Not ready for GO negotiation with " MACSTR, + p2p_dbg(p2p, "Not ready for GO negotiation with " MACSTR, MAC2STR(sa)); return; } if (p2p->pending_action_state == P2P_PENDING_GO_NEG_RESPONSE) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Stopped waiting " - "for TX status on GO Negotiation Response since we " - "already received Confirmation"); + p2p_dbg(p2p, "Stopped waiting for TX status on GO Negotiation Response since we already received Confirmation"); p2p->pending_action_state = P2P_NO_PENDING_ACTION; } @@ -1049,31 +1135,28 @@ return; if (!(dev->flags & P2P_DEV_WAIT_GO_NEG_CONFIRM)) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Was not expecting GO Negotiation Confirm - " - "ignore"); + p2p_dbg(p2p, "Was not expecting GO Negotiation Confirm - ignore"); + p2p_parse_free(&msg); return; } dev->flags &= ~P2P_DEV_WAIT_GO_NEG_CONFIRM; + p2p->cfg->send_action_done(p2p->cfg->cb_ctx); if (msg.dialog_token != dev->dialog_token) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Unexpected Dialog Token %u (expected %u)", + p2p_dbg(p2p, "Unexpected Dialog Token %u (expected %u)", msg.dialog_token, dev->dialog_token); p2p_parse_free(&msg); return; } if (!msg.status) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: No Status attribute received"); + p2p_dbg(p2p, "No Status attribute received"); p2p_parse_free(&msg); return; } if (*msg.status) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: GO Negotiation rejected: status %d", - *msg.status); + p2p_dbg(p2p, "GO Negotiation rejected: status %d", *msg.status); + p2p_go_neg_failed(p2p, dev, *msg.status); p2p_parse_free(&msg); return; } @@ -1083,30 +1166,31 @@ p2p->ssid_len = msg.group_id_len - ETH_ALEN; os_memcpy(p2p->ssid, msg.group_id + ETH_ALEN, p2p->ssid_len); } else if (dev->go_state == REMOTE_GO) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Mandatory P2P Group ID attribute missing from " - "GO Negotiation Confirmation"); + p2p_dbg(p2p, "Mandatory P2P Group ID attribute missing from GO Negotiation Confirmation"); p2p->ssid_len = 0; -#ifdef CONFIG_P2P_STRICT + p2p_go_neg_failed(p2p, dev, P2P_SC_FAIL_INVALID_PARAMS); p2p_parse_free(&msg); return; -#endif /* CONFIG_P2P_STRICT */ } if (!msg.operating_channel) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Mandatory Operating Channel attribute missing " - "from GO Negotiation Confirmation"); + p2p_dbg(p2p, "Mandatory Operating Channel attribute missing from GO Negotiation Confirmation"); #ifdef CONFIG_P2P_STRICT p2p_parse_free(&msg); return; #endif /* CONFIG_P2P_STRICT */ + } else if (dev->go_state == REMOTE_GO) { + int oper_freq = p2p_channel_to_freq(msg.operating_channel[3], + msg.operating_channel[4]); + if (oper_freq != dev->oper_freq) { + p2p_dbg(p2p, "Updated peer (GO) operating channel preference from %d MHz to %d MHz", + dev->oper_freq, oper_freq); + dev->oper_freq = oper_freq; + } } if (!msg.channel_list) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Mandatory Operating Channel attribute missing " - "from GO Negotiation Confirmation"); + p2p_dbg(p2p, "Mandatory Operating Channel attribute missing from GO Negotiation Confirmation"); #ifdef CONFIG_P2P_STRICT p2p_parse_free(&msg); return; @@ -1120,11 +1204,20 @@ * This should not happen since GO negotiation has already * been completed. */ - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Unexpected GO Neg state - do not know which end " - "becomes GO"); + p2p_dbg(p2p, "Unexpected GO Neg state - do not know which end becomes GO"); return; } + /* + * The peer could have missed our ctrl::ack frame for GO Negotiation + * Confirm and continue retransmitting the frame. To reduce the + * likelihood of the peer not getting successful TX status for the + * GO Negotiation Confirm frame, wait a short time here before starting + * the group so that we will remain on the current channel to + * acknowledge any possible retransmission from the peer. + */ + p2p_dbg(p2p, "20 ms wait on current channel before starting group"); + os_sleep(0, 20000); + p2p_go_complete(p2p, dev); } diff -Nru wpa-1.0/src/p2p/p2p_group.c wpa-2.1/src/p2p/p2p_group.c --- wpa-1.0/src/p2p/p2p_group.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/p2p/p2p_group.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * Wi-Fi Direct - P2P group operations * Copyright (c) 2009-2010, Atheros Communications * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -28,6 +22,7 @@ u8 addr[ETH_ALEN]; /* P2P Interface Address */ u8 dev_addr[ETH_ALEN]; /* P2P Device Address */ struct wpabuf *p2p_ie; + struct wpabuf *wfd_ie; struct wpabuf *client_info; u8 dev_capab; }; @@ -43,12 +38,10 @@ int group_formation; int beacon_update; struct wpabuf *noa; + struct wpabuf *wfd_ie; }; -static void p2p_group_update_ies(struct p2p_group *group); - - struct p2p_group * p2p_group_init(struct p2p_data *p2p, struct p2p_group_config *config) { @@ -58,8 +51,8 @@ if (group == NULL) return NULL; - groups = os_realloc(p2p->groups, (p2p->num_groups + 1) * - sizeof(struct p2p_group *)); + groups = os_realloc_array(p2p->groups, p2p->num_groups + 1, + sizeof(struct p2p_group *)); if (groups == NULL) { os_free(group); return NULL; @@ -80,6 +73,7 @@ static void p2p_group_free_member(struct p2p_group_member *m) { + wpabuf_free(m->wfd_ie); wpabuf_free(m->p2p_ie); wpabuf_free(m->client_info); os_free(m); @@ -124,6 +118,7 @@ p2p_group_free_members(group); os_free(group->cfg); wpabuf_free(group->noa); + wpabuf_free(group->wfd_ie); os_free(group); } @@ -141,11 +136,10 @@ static void p2p_group_add_common_ies(struct p2p_group *group, struct wpabuf *ie) { - u8 dev_capab = 0, group_capab = 0; + u8 dev_capab = group->p2p->dev_capab, group_capab = 0; /* P2P Capability */ - dev_capab |= P2P_DEV_CAPAB_SERVICE_DISCOVERY; - dev_capab |= P2P_DEV_CAPAB_INVITATION_PROCEDURE; + dev_capab &= ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY; group_capab |= P2P_GROUP_CAPAB_GROUP_OWNER; if (group->cfg->persistent_group) { group_capab |= P2P_GROUP_CAPAB_PERSISTENT_GROUP; @@ -160,6 +154,7 @@ group_capab |= P2P_GROUP_CAPAB_CROSS_CONN; if (group->num_members >= group->cfg->max_clients) group_capab |= P2P_GROUP_CAPAB_GROUP_LIMIT; + group_capab |= P2P_GROUP_CAPAB_IP_ADDR_ALLOCATION; p2p_buf_add_capability(ie, dev_capab, group_capab); } @@ -175,15 +170,59 @@ } +static struct wpabuf * p2p_group_encaps_probe_resp(struct wpabuf *subelems) +{ + struct wpabuf *ie; + const u8 *pos, *end; + size_t len; + + if (subelems == NULL) + return NULL; + + len = wpabuf_len(subelems) + 100; + + ie = wpabuf_alloc(len); + if (ie == NULL) + return NULL; + + pos = wpabuf_head(subelems); + end = pos + wpabuf_len(subelems); + + while (end > pos) { + size_t frag_len = end - pos; + if (frag_len > 251) + frag_len = 251; + wpabuf_put_u8(ie, WLAN_EID_VENDOR_SPECIFIC); + wpabuf_put_u8(ie, 4 + frag_len); + wpabuf_put_be32(ie, P2P_IE_VENDOR_TYPE); + wpabuf_put_data(ie, pos, frag_len); + pos += frag_len; + } + + return ie; +} + + static struct wpabuf * p2p_group_build_beacon_ie(struct p2p_group *group) { struct wpabuf *ie; u8 *len; + size_t extra = 0; + +#ifdef CONFIG_WIFI_DISPLAY + if (group->p2p->wfd_ie_beacon) + extra = wpabuf_len(group->p2p->wfd_ie_beacon); +#endif /* CONFIG_WIFI_DISPLAY */ - ie = wpabuf_alloc(257); + ie = wpabuf_alloc(257 + extra); if (ie == NULL) return NULL; +#ifdef CONFIG_WIFI_DISPLAY + if (group->p2p->wfd_ie_beacon) + wpabuf_put_buf(ie, group->p2p->wfd_ie_beacon); +#endif /* CONFIG_WIFI_DISPLAY */ + len = p2p_buf_add_ie_hdr(ie); p2p_group_add_common_ies(group, ie); p2p_buf_add_device_id(ie, group->p2p->cfg->dev_addr); @@ -194,44 +233,241 @@ } -static struct wpabuf * p2p_group_build_probe_resp_ie(struct p2p_group *group) +#ifdef CONFIG_WIFI_DISPLAY + +struct wpabuf * p2p_group_get_wfd_ie(struct p2p_group *g) +{ + return g->wfd_ie; +} + + +struct wpabuf * wifi_display_encaps(struct wpabuf *subelems) { - u8 *group_info; struct wpabuf *ie; + const u8 *pos, *end; + + if (subelems == NULL) + return NULL; + + ie = wpabuf_alloc(wpabuf_len(subelems) + 100); + if (ie == NULL) + return NULL; + + pos = wpabuf_head(subelems); + end = pos + wpabuf_len(subelems); + + while (end > pos) { + size_t frag_len = end - pos; + if (frag_len > 251) + frag_len = 251; + wpabuf_put_u8(ie, WLAN_EID_VENDOR_SPECIFIC); + wpabuf_put_u8(ie, 4 + frag_len); + wpabuf_put_be32(ie, WFD_IE_VENDOR_TYPE); + wpabuf_put_data(ie, pos, frag_len); + pos += frag_len; + } + + return ie; +} + + +static int wifi_display_add_dev_info_descr(struct wpabuf *buf, + struct p2p_group_member *m) +{ + const u8 *pos, *end; + const u8 *dev_info = NULL; + const u8 *assoc_bssid = NULL; + const u8 *coupled_sink = NULL; + u8 zero_addr[ETH_ALEN]; + + if (m->wfd_ie == NULL) + return 0; + + os_memset(zero_addr, 0, ETH_ALEN); + pos = wpabuf_head_u8(m->wfd_ie); + end = pos + wpabuf_len(m->wfd_ie); + while (pos + 1 < end) { + u8 id; + u16 len; + + id = *pos++; + len = WPA_GET_BE16(pos); + pos += 2; + if (pos + len > end) + break; + + switch (id) { + case WFD_SUBELEM_DEVICE_INFO: + if (len < 6) + break; + dev_info = pos; + break; + case WFD_SUBELEM_ASSOCIATED_BSSID: + if (len < ETH_ALEN) + break; + assoc_bssid = pos; + break; + case WFD_SUBELEM_COUPLED_SINK: + if (len < 1 + ETH_ALEN) + break; + coupled_sink = pos; + break; + } + + pos += len; + } + + if (dev_info == NULL) + return 0; + + wpabuf_put_u8(buf, 23); + wpabuf_put_data(buf, m->dev_addr, ETH_ALEN); + if (assoc_bssid) + wpabuf_put_data(buf, assoc_bssid, ETH_ALEN); + else + wpabuf_put_data(buf, zero_addr, ETH_ALEN); + wpabuf_put_data(buf, dev_info, 2); /* WFD Device Info */ + wpabuf_put_data(buf, dev_info + 4, 2); /* WFD Device Max Throughput */ + if (coupled_sink) { + wpabuf_put_data(buf, coupled_sink, 1 + ETH_ALEN); + } else { + wpabuf_put_u8(buf, 0); + wpabuf_put_data(buf, zero_addr, ETH_ALEN); + } + + return 1; +} + + +static struct wpabuf * +wifi_display_build_go_ie(struct p2p_group *group) +{ + struct wpabuf *wfd_subelems, *wfd_ie; struct p2p_group_member *m; u8 *len; + unsigned int count = 0; - ie = wpabuf_alloc(257); - if (ie == NULL) + if (!group->p2p->wfd_ie_probe_resp) return NULL; - len = p2p_buf_add_ie_hdr(ie); + wfd_subelems = wpabuf_alloc(wpabuf_len(group->p2p->wfd_ie_probe_resp) + + group->num_members * 24 + 100); + if (wfd_subelems == NULL) + return NULL; + if (group->p2p->wfd_dev_info) + wpabuf_put_buf(wfd_subelems, group->p2p->wfd_dev_info); + if (group->p2p->wfd_assoc_bssid) + wpabuf_put_buf(wfd_subelems, + group->p2p->wfd_assoc_bssid); + if (group->p2p->wfd_coupled_sink_info) + wpabuf_put_buf(wfd_subelems, + group->p2p->wfd_coupled_sink_info); + + /* Build WFD Session Info */ + wpabuf_put_u8(wfd_subelems, WFD_SUBELEM_SESSION_INFO); + len = wpabuf_put(wfd_subelems, 2); + m = group->members; + while (m) { + if (wifi_display_add_dev_info_descr(wfd_subelems, m)) + count++; + m = m->next; + } - p2p_group_add_common_ies(group, ie); - p2p_group_add_noa(ie, group->noa); + if (count == 0) { + /* No Wi-Fi Display clients - do not include subelement */ + wfd_subelems->used -= 3; + } else { + WPA_PUT_BE16(len, (u8 *) wpabuf_put(wfd_subelems, 0) - len - + 2); + p2p_dbg(group->p2p, "WFD: WFD Session Info: %u descriptors", + count); + } + + wfd_ie = wifi_display_encaps(wfd_subelems); + wpabuf_free(wfd_subelems); + + return wfd_ie; +} + +static void wifi_display_group_update(struct p2p_group *group) +{ + wpabuf_free(group->wfd_ie); + group->wfd_ie = wifi_display_build_go_ie(group); +} + +#endif /* CONFIG_WIFI_DISPLAY */ - /* P2P Device Info */ - p2p_buf_add_device_info(ie, group->p2p, NULL); - /* P2P Group Info */ - group_info = wpabuf_put(ie, 0); - wpabuf_put_u8(ie, P2P_ATTR_GROUP_INFO); - wpabuf_put_le16(ie, 0); /* Length to be filled */ - for (m = group->members; m; m = m->next) - p2p_client_info(ie, m); +void p2p_buf_add_group_info(struct p2p_group *group, struct wpabuf *buf, + int max_clients) +{ + u8 *group_info; + int count = 0; + struct p2p_group_member *m; + + p2p_dbg(group->p2p, "* P2P Group Info"); + group_info = wpabuf_put(buf, 0); + wpabuf_put_u8(buf, P2P_ATTR_GROUP_INFO); + wpabuf_put_le16(buf, 0); /* Length to be filled */ + for (m = group->members; m; m = m->next) { + p2p_client_info(buf, m); + count++; + if (max_clients >= 0 && count >= max_clients) + break; + } WPA_PUT_LE16(group_info + 1, - (u8 *) wpabuf_put(ie, 0) - group_info - 3); + (u8 *) wpabuf_put(buf, 0) - group_info - 3); +} + + +void p2p_group_buf_add_id(struct p2p_group *group, struct wpabuf *buf) +{ + p2p_buf_add_group_id(buf, group->p2p->cfg->dev_addr, group->cfg->ssid, + group->cfg->ssid_len); +} + + +static struct wpabuf * p2p_group_build_probe_resp_ie(struct p2p_group *group) +{ + struct wpabuf *p2p_subelems, *ie; + + p2p_subelems = wpabuf_alloc(500); + if (p2p_subelems == NULL) + return NULL; + + p2p_group_add_common_ies(group, p2p_subelems); + p2p_group_add_noa(p2p_subelems, group->noa); + + /* P2P Device Info */ + p2p_buf_add_device_info(p2p_subelems, group->p2p, NULL); + + /* P2P Group Info: Only when at least one P2P Client is connected */ + if (group->members) + p2p_buf_add_group_info(group, p2p_subelems, -1); + + ie = p2p_group_encaps_probe_resp(p2p_subelems); + wpabuf_free(p2p_subelems); + +#ifdef CONFIG_WIFI_DISPLAY + if (group->wfd_ie) { + struct wpabuf *wfd = wpabuf_dup(group->wfd_ie); + ie = wpabuf_concat(wfd, ie); + } +#endif /* CONFIG_WIFI_DISPLAY */ - p2p_buf_update_ie_hdr(ie, len); return ie; } -static void p2p_group_update_ies(struct p2p_group *group) +void p2p_group_update_ies(struct p2p_group *group) { struct wpabuf *beacon_ie; struct wpabuf *probe_resp_ie; +#ifdef CONFIG_WIFI_DISPLAY + wifi_display_group_update(group); +#endif /* CONFIG_WIFI_DISPLAY */ + probe_resp_ie = p2p_group_build_probe_resp_ie(group); if (probe_resp_ie == NULL) return; @@ -351,6 +587,8 @@ if (group == NULL) return -1; + p2p_add_device(group->p2p, addr, 0, NULL, 0, ie, len, 0); + m = os_zalloc(sizeof(*m)); if (m == NULL) return -1; @@ -361,15 +599,19 @@ &m->dev_capab, m->dev_addr); } +#ifdef CONFIG_WIFI_DISPLAY + m->wfd_ie = ieee802_11_vendor_ie_concat(ie, len, WFD_IE_VENDOR_TYPE); +#endif /* CONFIG_WIFI_DISPLAY */ p2p_group_remove_member(group, addr); m->next = group->members; group->members = m; group->num_members++; - wpa_msg(group->p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Add client " MACSTR - " to group (p2p=%d client_info=%d); num_members=%u/%u", - MAC2STR(addr), m->p2p_ie ? 1 : 0, m->client_info ? 1 : 0, + p2p_dbg(group->p2p, "Add client " MACSTR + " to group (p2p=%d wfd=%d client_info=%d); num_members=%u/%u", + MAC2STR(addr), m->p2p_ie ? 1 : 0, m->wfd_ie ? 1 : 0, + m->client_info ? 1 : 0, group->num_members, group->cfg->max_clients); if (group->num_members == group->cfg->max_clients) group->beacon_update = 1; @@ -385,6 +627,12 @@ { struct wpabuf *resp; u8 *rlen; + size_t extra = 0; + +#ifdef CONFIG_WIFI_DISPLAY + if (group->wfd_ie) + extra = wpabuf_len(group->wfd_ie); +#endif /* CONFIG_WIFI_DISPLAY */ /* * (Re)Association Response - P2P IE @@ -392,9 +640,15 @@ * denied) * Extended Listen Timing (may be present) */ - resp = wpabuf_alloc(20); + resp = wpabuf_alloc(20 + extra); if (resp == NULL) return NULL; + +#ifdef CONFIG_WIFI_DISPLAY + if (group->wfd_ie) + wpabuf_put_buf(resp, group->wfd_ie); +#endif /* CONFIG_WIFI_DISPLAY */ + rlen = p2p_buf_add_ie_hdr(resp); if (status != P2P_SC_SUCCESS) p2p_buf_add_status(resp, status); @@ -407,8 +661,8 @@ void p2p_group_notif_disassoc(struct p2p_group *group, const u8 *addr) { if (p2p_group_remove_member(group, addr)) { - wpa_msg(group->p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Remove " - "client " MACSTR " from group; num_members=%u/%u", + p2p_dbg(group->p2p, "Remove client " MACSTR + " from group; num_members=%u/%u", MAC2STR(addr), group->num_members, group->cfg->max_clients); if (group->num_members == group->cfg->max_clients - 1) @@ -620,20 +874,18 @@ m = p2p_group_get_client(group, dev_id); if (m == NULL || m->client_info == NULL) { - wpa_printf(MSG_DEBUG, "P2P: Requested client was not in this " - "group " MACSTR, - MAC2STR(group->cfg->interface_addr)); + p2p_dbg(group->p2p, "Requested client was not in this group " + MACSTR, MAC2STR(group->cfg->interface_addr)); return -1; } if (!(m->dev_capab & P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY)) { - wpa_printf(MSG_DEBUG, "P2P: Requested client does not support " - "client discoverability"); + p2p_dbg(group->p2p, "Requested client does not support client discoverability"); return -1; } - wpa_printf(MSG_DEBUG, "P2P: Schedule GO Discoverability Request to be " - "sent to " MACSTR, MAC2STR(dev_id)); + p2p_dbg(group->p2p, "Schedule GO Discoverability Request to be sent to " + MACSTR, MAC2STR(dev_id)); req = p2p_build_go_disc_req(); if (req == NULL) @@ -648,8 +900,7 @@ group->cfg->interface_addr, wpabuf_head(req), wpabuf_len(req), 200) < 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Failed to send Action frame"); + p2p_dbg(p2p, "Failed to send Action frame"); } wpabuf_free(req); @@ -674,7 +925,7 @@ m = p2p_group_get_client_iface(group, client_interface_addr); if (m == NULL || m->client_info == NULL) { - wpa_printf(MSG_DEBUG, "P2P: Client was not in this group"); + p2p_dbg(group->p2p, "Client was not in this group"); return P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE; } @@ -687,9 +938,9 @@ else curr_noa_len = -1; if (curr_noa_len < 0) - wpa_printf(MSG_DEBUG, "P2P: Failed to fetch current NoA"); + p2p_dbg(group->p2p, "Failed to fetch current NoA"); else if (curr_noa_len == 0) - wpa_printf(MSG_DEBUG, "P2P: No NoA being advertized"); + p2p_dbg(group->p2p, "No NoA being advertized"); else wpa_hexdump(MSG_DEBUG, "P2P: Current NoA", curr_noa, curr_noa_len); @@ -737,3 +988,28 @@ return 0; } + + +int p2p_group_is_group_id_match(struct p2p_group *group, const u8 *group_id, + size_t group_id_len) +{ + if (group_id_len != ETH_ALEN + group->cfg->ssid_len) + return 0; + if (os_memcmp(group_id, group->p2p->cfg->dev_addr, ETH_ALEN) != 0) + return 0; + return os_memcmp(group_id + ETH_ALEN, group->cfg->ssid, + group->cfg->ssid_len) == 0; +} + + +void p2p_group_force_beacon_update_ies(struct p2p_group *group) +{ + group->beacon_update = 1; + p2p_group_update_ies(group); +} + + +int p2p_group_get_freq(struct p2p_group *group) +{ + return group->cfg->freq; +} diff -Nru wpa-1.0/src/p2p/p2p.h wpa-2.1/src/p2p/p2p.h --- wpa-1.0/src/p2p/p2p.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/p2p/p2p.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,19 +2,15 @@ * Wi-Fi Direct - P2P module * Copyright (c) 2009-2010, Atheros Communications * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef P2P_H #define P2P_H +#include "wps/wps_defs.h" + /** * P2P_MAX_REG_CLASSES - Maximum number of regulatory classes */ @@ -56,7 +52,7 @@ }; enum p2p_wps_method { - WPS_NOT_READY, WPS_PIN_DISPLAY, WPS_PIN_KEYPAD, WPS_PBC + WPS_NOT_READY, WPS_PIN_DISPLAY, WPS_PIN_KEYPAD, WPS_PBC, WPS_NFC }; /** @@ -81,6 +77,10 @@ */ int freq; + int ht40; + + int vht; + /** * ssid - SSID of the group */ @@ -92,6 +92,16 @@ size_t ssid_len; /** + * psk - WPA pre-shared key (256 bits) (GO only) + */ + u8 psk[32]; + + /** + * psk_set - Whether PSK field is configured (GO only) + */ + int psk_set; + + /** * passphrase - WPA2-Personal passphrase for the group (GO only) */ char passphrase[64]; @@ -137,7 +147,6 @@ enum p2p_scan_type { P2P_SCAN_SOCIAL, P2P_SCAN_FULL, - P2P_SCAN_SPECIFIC, P2P_SCAN_SOCIAL_PLUS_ONE }; @@ -216,12 +225,18 @@ size_t wps_sec_dev_type_list_len; struct wpabuf *wps_vendor_ext[P2P_MAX_WPS_VENDOR_EXT]; + + /** + * wfd_subelems - Wi-Fi Display subelements from WFD IE(s) + */ + struct wpabuf *wfd_subelems; }; enum p2p_prov_disc_status { P2P_PROV_DISC_SUCCESS, P2P_PROV_DISC_TIMEOUT, P2P_PROV_DISC_REJECTED, + P2P_PROV_DISC_TIMEOUT_JOIN, }; struct p2p_channel { @@ -276,6 +291,20 @@ struct p2p_channels channels; /** + * cli_channels - Additional client channels + * + * This list of channels (if any) will be used when advertising local + * channels during GO Negotiation or Invitation for the cases where the + * local end may become the client. This may allow the peer to become a + * GO on additional channels if it supports these options. The main use + * case for this is to include passive-scan channels on devices that may + * not know their current location and have configured most channels to + * not allow initiation of radition (i.e., another device needs to take + * master responsibilities). + */ + struct p2p_channels cli_channels; + + /** * num_pref_chan - Number of pref_chan entries */ unsigned int num_pref_chan; @@ -355,15 +384,23 @@ size_t ssid_postfix_len; /** - * msg_ctx - Context to use with wpa_msg() calls + * max_listen - Maximum listen duration in ms */ - void *msg_ctx; + unsigned int max_listen; /** * cb_ctx - Context to use with callback functions */ void *cb_ctx; + /** + * debug_print - Debug print + * @ctx: Callback context from cb_ctx + * @level: Debug verbosity level (MSG_*) + * @msg: Debug message + */ + void (*debug_print)(void *ctx, int level, const char *msg); + /* Callbacks to request lower layer driver operations */ @@ -375,14 +412,14 @@ * @num_req_dev_types: Number of requested device types * @req_dev_types: Array containing requested device types * @dev_id: Device ID to search for or %NULL to find all devices + * @pw_id: Device Password ID * Returns: 0 on success, -1 on failure * * This callback function is used to request a P2P scan or search * operation to be completed. Type type argument specifies which type * of scan is to be done. @P2P_SCAN_SOCIAL indicates that only the * social channels (1, 6, 11) should be scanned. @P2P_SCAN_FULL - * indicates that all channels are to be scanned. @P2P_SCAN_SPECIFIC - * request a scan of a single channel specified by freq. + * indicates that all channels are to be scanned. * @P2P_SCAN_SOCIAL_PLUS_ONE request scan of all the social channels * plus one extra channel specified by freq. * @@ -398,7 +435,7 @@ */ int (*p2p_scan)(void *ctx, enum p2p_scan_type type, int freq, unsigned int num_req_dev_types, - const u8 *req_dev_types, const u8 *dev_id); + const u8 *req_dev_types, const u8 *dev_id, u16 pw_id); /** * send_probe_resp - Transmit a Probe Response frame @@ -529,6 +566,12 @@ void (*dev_lost)(void *ctx, const u8 *dev_addr); /** + * find_stopped - Notification of a p2p_find operation stopping + * @ctx: Callback context from cb_ctx + */ + void (*find_stopped)(void *ctx); + + /** * go_neg_req_rx - Notification of a receive GO Negotiation Request * @ctx: Callback context from cb_ctx * @src: Source address of the message triggering this notification @@ -664,6 +707,9 @@ * @persistent_group: Whether this is an invitation to reinvoke a * persistent group (instead of invitation to join an active * group) + * @channels: Available operating channels for the group + * @dev_pw_id: Device Password ID for NFC static handover or -1 if not + * used * Returns: Status code (P2P_SC_*) * * This optional callback can be used to implement persistent reconnect @@ -684,7 +730,9 @@ u8 (*invitation_process)(void *ctx, const u8 *sa, const u8 *bssid, const u8 *go_dev_addr, const u8 *ssid, size_t ssid_len, int *go, u8 *group_bssid, - int *force_freq, int persistent_group); + int *force_freq, int persistent_group, + const struct p2p_channels *channels, + int dev_pw_id); /** * invitation_received - Callback on Invitation Request RX @@ -713,6 +761,9 @@ * @ctx: Callback context from cb_ctx * @status: Negotiation result (Status Code) * @bssid: P2P Group BSSID or %NULL if not received + * @channels: Available operating channels for the group + * @addr: Peer address + * @freq: Frequency (in MHz) indicated during invitation or 0 * * This callback is used to indicate result of an Invitation procedure * started with a call to p2p_invite(). The indicated status code is @@ -720,7 +771,9 @@ * (P2P_SC_SUCCESS) indicating success or -1 to indicate a timeout or a * local failure in transmitting the Invitation Request. */ - void (*invitation_result)(void *ctx, int status, const u8 *bssid); + void (*invitation_result)(void *ctx, int status, const u8 *bssid, + const struct p2p_channels *channels, + const u8 *addr, int freq); /** * go_connected - Check whether we are connected to a GO @@ -730,6 +783,26 @@ * or 0 if not. */ int (*go_connected)(void *ctx, const u8 *dev_addr); + + /** + * presence_resp - Callback on Presence Response + * @ctx: Callback context from cb_ctx + * @src: Source address (GO's P2P Interface Address) + * @status: Result of the request (P2P_SC_*) + * @noa: Returned NoA value + * @noa_len: Length of the NoA buffer in octets + */ + void (*presence_resp)(void *ctx, const u8 *src, u8 status, + const u8 *noa, size_t noa_len); + + /** + * is_concurrent_session_active - Check whether concurrent session is + * active on other virtual interfaces + * @ctx: Callback context from cb_ctx + * Returns: 1 if concurrent session is active on other virtual interface + * or 0 if not. + */ + int (*is_concurrent_session_active)(void *ctx); }; @@ -835,12 +908,13 @@ * containing num_req_dev_types * WPS_DEV_TYPE_LEN bytes; %NULL if no * requested device types. * @dev_id: Device ID to search for or %NULL to find all devices + * @search_delay: Extra delay in milliseconds between search iterations * Returns: 0 on success, -1 on failure */ int p2p_find(struct p2p_data *p2p, unsigned int timeout, enum p2p_discovery_type type, unsigned int num_req_dev_types, const u8 *req_dev_types, - const u8 *dev_id); + const u8 *dev_id, unsigned int search_delay); /** * p2p_stop_find - Stop P2P Find (Device Discovery) @@ -872,6 +946,12 @@ int p2p_listen(struct p2p_data *p2p, unsigned int timeout); /** + * p2p_stop_listen - Stop P2P Listen + * @p2p: P2P module context from p2p_init() + */ +void p2p_stop_listen(struct p2p_data *p2p); + +/** * p2p_connect - Start P2P group formation (GO negotiation) * @p2p: P2P module context from p2p_init() * @peer_addr: MAC address of the peer P2P client @@ -882,12 +962,22 @@ * @persistent_group: Whether to create a persistent group (0 = no, 1 = * persistent group without persistent reconnect, 2 = persistent group with * persistent reconnect) + * @force_ssid: Forced SSID for the group if we become GO or %NULL to generate + * a new SSID + * @force_ssid_len: Length of $force_ssid buffer + * @pd_before_go_neg: Whether to send Provision Discovery prior to GO + * Negotiation as an interoperability workaround when initiating group + * formation + * @pref_freq: Preferred operating frequency in MHz or 0 (this is only used if + * force_freq == 0) * Returns: 0 on success, -1 on failure */ int p2p_connect(struct p2p_data *p2p, const u8 *peer_addr, enum p2p_wps_method wps_method, int go_intent, const u8 *own_interface_addr, - unsigned int force_freq, int persistent_group); + unsigned int force_freq, int persistent_group, + const u8 *force_ssid, size_t force_ssid_len, + int pd_before_go_neg, unsigned int pref_freq, u16 oob_pw_id); /** * p2p_authorize - Authorize P2P group formation (GO negotiation) @@ -900,6 +990,11 @@ * @persistent_group: Whether to create a persistent group (0 = no, 1 = * persistent group without persistent reconnect, 2 = persistent group with * persistent reconnect) + * @force_ssid: Forced SSID for the group if we become GO or %NULL to generate + * a new SSID + * @force_ssid_len: Length of $force_ssid buffer + * @pref_freq: Preferred operating frequency in MHz or 0 (this is only used if + * force_freq == 0) * Returns: 0 on success, -1 on failure * * This is like p2p_connect(), but the actual group negotiation is not @@ -908,7 +1003,9 @@ int p2p_authorize(struct p2p_data *p2p, const u8 *peer_addr, enum p2p_wps_method wps_method, int go_intent, const u8 *own_interface_addr, - unsigned int force_freq, int persistent_group); + unsigned int force_freq, int persistent_group, + const u8 *force_ssid, size_t force_ssid_len, + unsigned int pref_freq, u16 oob_pw_id); /** * p2p_reject - Reject peer device (explicitly block connection attempts) @@ -925,6 +1022,7 @@ * @config_methods: WPS Config Methods value (only one bit set) * @join: Whether this is used by a client joining an active group * @force_freq: Forced TX frequency for the frame (mainly for the join case) + * @user_initiated_pd: Flag to indicate if initiated by user or not * Returns: 0 on success, -1 on failure * * This function can be used to request a discovered P2P peer to display a PIN @@ -936,7 +1034,8 @@ * indicated with the p2p_config::prov_disc_resp() callback. */ int p2p_prov_disc_req(struct p2p_data *p2p, const u8 *peer_addr, - u16 config_methods, int join, int force_freq); + u16 config_methods, int join, int force_freq, + int user_initiated_pd); /** * p2p_sd_request - Schedule a service discovery query @@ -951,6 +1050,11 @@ void * p2p_sd_request(struct p2p_data *p2p, const u8 *dst, const struct wpabuf *tlvs); +#ifdef CONFIG_WIFI_DISPLAY +void * p2p_sd_request_wfd(struct p2p_data *p2p, const u8 *dst, + const struct wpabuf *tlvs); +#endif /* CONFIG_WIFI_DISPLAY */ + /** * p2p_sd_cancel_request - Cancel a pending service discovery query * @p2p: P2P module context from p2p_init() @@ -1001,12 +1105,16 @@ * @force_freq: The only allowed channel frequency in MHz or 0 * @go_dev_addr: Forced GO Device Address or %NULL if none * @persistent_group: Whether this is to reinvoke a persistent group + * @pref_freq: Preferred operating frequency in MHz or 0 (this is only used if + * force_freq == 0) + * @dev_pw_id: Device Password ID from OOB Device Password (NFC) static handover + * case or -1 if not used * Returns: 0 on success, -1 on failure */ int p2p_invite(struct p2p_data *p2p, const u8 *peer, enum p2p_invite_role role, const u8 *bssid, const u8 *ssid, size_t ssid_len, unsigned int force_freq, const u8 *go_dev_addr, - int persistent_group); + int persistent_group, unsigned int pref_freq, int dev_pw_id); /** * p2p_presence_req - Request GO presence @@ -1092,6 +1200,23 @@ /* Event notifications from lower layer driver operations */ /** + * enum p2p_probe_req_status + * + * @P2P_PREQ_MALFORMED: frame was not well-formed + * @P2P_PREQ_NOT_LISTEN: device isn't in listen state, frame ignored + * @P2P_PREQ_NOT_P2P: frame was not a P2P probe request + * @P2P_PREQ_P2P_NOT_PROCESSED: frame was P2P but wasn't processed + * @P2P_PREQ_P2P_PROCESSED: frame has been processed by P2P + */ +enum p2p_probe_req_status { + P2P_PREQ_MALFORMED, + P2P_PREQ_NOT_LISTEN, + P2P_PREQ_NOT_P2P, + P2P_PREQ_NOT_PROCESSED, + P2P_PREQ_PROCESSED +}; + +/** * p2p_probe_req_rx - Report reception of a Probe Request frame * @p2p: P2P module context from p2p_init() * @addr: Source MAC address @@ -1099,10 +1224,11 @@ * @bssid: BSSID if available or %NULL * @ie: Information elements from the Probe Request frame body * @ie_len: Length of ie buffer in octets - * Returns: 0 to indicate the frame was not processed or 1 if it was + * Returns: value indicating the type and status of the probe request */ -int p2p_probe_req_rx(struct p2p_data *p2p, const u8 *addr, const u8 *dst, - const u8 *bssid, const u8 *ie, size_t ie_len); +enum p2p_probe_req_status +p2p_probe_req_rx(struct p2p_data *p2p, const u8 *addr, const u8 *dst, + const u8 *bssid, const u8 *ie, size_t ie_len); /** * p2p_rx_action - Report received Action frame @@ -1124,6 +1250,7 @@ * @p2p: P2P module context from p2p_init() * @bssid: BSSID of the scan result * @freq: Frequency of the channel on which the device was found in MHz + * @rx_time: Time when the result was received * @level: Signal level (signal strength of the received Beacon/Probe Response * frame) * @ies: Pointer to IEs from the scan result @@ -1145,7 +1272,8 @@ * start of a pending operation, e.g., to start a pending GO negotiation. */ int p2p_scan_res_handler(struct p2p_data *p2p, const u8 *bssid, int freq, - int level, const u8 *ies, size_t ies_len); + struct os_reltime *rx_time, int level, const u8 *ies, + size_t ies_len); /** * p2p_scan_res_handled - Indicate end of scan results @@ -1241,6 +1369,21 @@ unsigned int max_clients; /** + * ssid - Group SSID + */ + u8 ssid[32]; + + /** + * ssid_len - Length of SSID + */ + size_t ssid_len; + + /** + * freq - Operating channel of the group + */ + int freq; + + /** * cb_ctx - Context to use with callback functions */ void *cb_ctx; @@ -1392,6 +1535,15 @@ int p2p_scan_result_text(const u8 *ies, size_t ies_len, char *buf, char *end); /** + * p2p_parse_dev_addr_in_p2p_ie - Parse P2P Device Address from a concatenated + * P2P IE + * @p2p_ie: P2P IE + * @dev_addr: Buffer for returning P2P Device Address + * Returns: 0 on success or -1 if P2P Device Address could not be parsed + */ +int p2p_parse_dev_addr_in_p2p_ie(struct wpabuf *p2p_ie, u8 *dev_addr); + +/** * p2p_parse_dev_addr - Parse P2P Device Address from P2P IE(s) * @ies: Information elements from scan results * @ies_len: ies buffer length in octets @@ -1534,6 +1686,9 @@ */ void p2p_set_intra_bss_dist(struct p2p_data *p2p, int enabled); +int p2p_channels_includes_freq(const struct p2p_channels *channels, + unsigned int freq); + /** * p2p_supported_freq - Check whether channel is supported for P2P * @p2p: P2P module context from p2p_init() @@ -1542,7 +1697,34 @@ */ int p2p_supported_freq(struct p2p_data *p2p, unsigned int freq); -void p2p_update_channel_list(struct p2p_data *p2p, struct p2p_channels *chan); +/** + * p2p_supported_freq_go - Check whether channel is supported for P2P GO operation + * @p2p: P2P module context from p2p_init() + * @freq: Channel frequency in MHz + * Returns: 0 if channel not usable for P2P, 1 if usable for P2P + */ +int p2p_supported_freq_go(struct p2p_data *p2p, unsigned int freq); + +/** + * p2p_supported_freq_cli - Check whether channel is supported for P2P client operation + * @p2p: P2P module context from p2p_init() + * @freq: Channel frequency in MHz + * Returns: 0 if channel not usable for P2P, 1 if usable for P2P + */ +int p2p_supported_freq_cli(struct p2p_data *p2p, unsigned int freq); + +/** + * p2p_get_pref_freq - Get channel from preferred channel list + * @p2p: P2P module context from p2p_init() + * @channels: List of channels + * Returns: Preferred channel + */ +unsigned int p2p_get_pref_freq(struct p2p_data *p2p, + const struct p2p_channels *channels); + +void p2p_update_channel_list(struct p2p_data *p2p, + const struct p2p_channels *chan, + const struct p2p_channels *cli_chan); /** * p2p_set_best_channels - Update best channel information @@ -1554,6 +1736,17 @@ void p2p_set_best_channels(struct p2p_data *p2p, int freq_24, int freq_5, int freq_overall); +/** + * p2p_set_own_freq_preference - Set own preference for channel + * @p2p: P2P module context from p2p_init() + * @freq: Frequency (MHz) of the preferred channel or 0 if no preference + * + * This function can be used to set a preference on the operating channel based + * on frequencies used on the other virtual interfaces that share the same + * radio. If non-zero, this is used to try to avoid multi-channel concurrency. + */ +void p2p_set_own_freq_preference(struct p2p_data *p2p, int freq); + const u8 * p2p_get_go_neg_peer(struct p2p_data *p2p); /** @@ -1639,19 +1832,113 @@ const struct p2p_channel *pref_chan); /** + * p2p_set_no_go_freq - Set no GO channel ranges + * @p2p: P2P module context from p2p_init() + * @list: Channel ranges or %NULL to remove restriction + * Returns: 0 on success, -1 on failure + */ +int p2p_set_no_go_freq(struct p2p_data *p2p, + const struct wpa_freq_range_list *list); + +/** * p2p_in_progress - Check whether a P2P operation is progress * @p2p: P2P module context from p2p_init() * Returns: 0 if P2P module is idle or 1 if an operation is in progress */ int p2p_in_progress(struct p2p_data *p2p); +const char * p2p_wps_method_text(enum p2p_wps_method method); + /** - * p2p_other_scan_completed - Notify completion of non-P2P scan + * p2p_set_config_timeout - Set local config timeouts * @p2p: P2P module context from p2p_init() - * Returns: 0 if P2P module is idle or 1 if an operation was started + * @go_timeout: Time in 10 ms units it takes to start the GO mode + * @client_timeout: Time in 10 ms units it takes to start the client mode */ -int p2p_other_scan_completed(struct p2p_data *p2p); +void p2p_set_config_timeout(struct p2p_data *p2p, u8 go_timeout, + u8 client_timeout); -const char * p2p_wps_method_text(enum p2p_wps_method method); +int p2p_set_wfd_ie_beacon(struct p2p_data *p2p, struct wpabuf *ie); +int p2p_set_wfd_ie_probe_req(struct p2p_data *p2p, struct wpabuf *ie); +int p2p_set_wfd_ie_probe_resp(struct p2p_data *p2p, struct wpabuf *ie); +int p2p_set_wfd_ie_assoc_req(struct p2p_data *p2p, struct wpabuf *ie); +int p2p_set_wfd_ie_invitation(struct p2p_data *p2p, struct wpabuf *ie); +int p2p_set_wfd_ie_prov_disc_req(struct p2p_data *p2p, struct wpabuf *ie); +int p2p_set_wfd_ie_prov_disc_resp(struct p2p_data *p2p, struct wpabuf *ie); +int p2p_set_wfd_ie_go_neg(struct p2p_data *p2p, struct wpabuf *ie); +int p2p_set_wfd_dev_info(struct p2p_data *p2p, const struct wpabuf *elem); +int p2p_set_wfd_assoc_bssid(struct p2p_data *p2p, const struct wpabuf *elem); +int p2p_set_wfd_coupled_sink_info(struct p2p_data *p2p, + const struct wpabuf *elem); +struct wpabuf * wifi_display_encaps(struct wpabuf *subelems); + +/** + * p2p_set_disc_int - Set min/max discoverable interval for p2p_find + * @p2p: P2P module context from p2p_init() + * @min_disc_int: minDiscoverableInterval (in units of 100 TU); default 1 + * @max_disc_int: maxDiscoverableInterval (in units of 100 TU); default 3 + * @max_disc_tu: Maximum number of TUs (1.024 ms) for discoverable interval; or + * -1 not to limit + * Returns: 0 on success, or -1 on failure + * + * This function can be used to configure minDiscoverableInterval and + * maxDiscoverableInterval parameters for the Listen state during device + * discovery (p2p_find). A random number of 100 TU units is picked for each + * Listen state iteration from [min_disc_int,max_disc_int] range. + * + * max_disc_tu can be used to futher limit the discoverable duration. However, + * it should be noted that use of this parameter is not recommended since it + * would not be compliant with the P2P specification. + */ +int p2p_set_disc_int(struct p2p_data *p2p, int min_disc_int, int max_disc_int, + int max_disc_tu); + +/** + * p2p_get_state_txt - Get current P2P state for debug purposes + * @p2p: P2P module context from p2p_init() + * Returns: Name of the current P2P module state + * + * It should be noted that the P2P module state names are internal information + * and subject to change at any point, i.e., this information should be used + * mainly for debugging purposes. + */ +const char * p2p_get_state_txt(struct p2p_data *p2p); + +struct wpabuf * p2p_build_nfc_handover_req(struct p2p_data *p2p, + int client_freq, + const u8 *go_dev_addr, + const u8 *ssid, size_t ssid_len); +struct wpabuf * p2p_build_nfc_handover_sel(struct p2p_data *p2p, + int client_freq, + const u8 *go_dev_addr, + const u8 *ssid, size_t ssid_len); + +struct p2p_nfc_params { + int sel; + const u8 *wsc_attr; + size_t wsc_len; + const u8 *p2p_attr; + size_t p2p_len; + + enum { + NO_ACTION, JOIN_GROUP, AUTH_JOIN, INIT_GO_NEG, RESP_GO_NEG, + BOTH_GO, PEER_CLIENT + } next_step; + struct p2p_peer_info *peer; + u8 oob_dev_pw[WPS_OOB_PUBKEY_HASH_LEN + 2 + + WPS_OOB_DEVICE_PASSWORD_LEN]; + size_t oob_dev_pw_len; + int go_freq; + u8 go_dev_addr[ETH_ALEN]; + u8 go_ssid[32]; + size_t go_ssid_len; +}; + +int p2p_process_nfc_connection_handover(struct p2p_data *p2p, + struct p2p_nfc_params *params); + +void p2p_set_authorized_oob_dev_pw_id(struct p2p_data *p2p, u16 dev_pw_id, + int go_intent, + const u8 *own_interface_addr); #endif /* P2P_H */ diff -Nru wpa-1.0/src/p2p/p2p_i.h wpa-2.1/src/p2p/p2p_i.h --- wpa-1.0/src/p2p/p2p_i.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/p2p/p2p_i.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * P2P - Internal definitions for P2P module * Copyright (c) 2009-2010, Atheros Communications * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef P2P_I_H @@ -18,6 +12,8 @@ #include "utils/list.h" #include "p2p.h" +enum p2p_role_indication; + enum p2p_go_state { UNKNOWN_GO, LOCAL_GO, @@ -29,9 +25,11 @@ */ struct p2p_device { struct dl_list list; - struct os_time last_seen; + struct os_reltime last_seen; int listen_freq; + int oob_go_neg_freq; enum p2p_wps_method wps_method; + u16 oob_pw_id; struct p2p_peer_info info; @@ -58,6 +56,7 @@ int go_neg_req_sent; enum p2p_go_state go_state; u8 dialog_token; + u8 tie_breaker; u8 intended_addr[ETH_ALEN]; char country[3]; @@ -67,7 +66,7 @@ size_t oper_ssid_len; /** - * req_config_methods - Pending provisioning discovery methods + * req_config_methods - Pending provision discovery methods */ u16 req_config_methods; @@ -96,6 +95,8 @@ #define P2P_DEV_PD_FOR_JOIN BIT(14) #define P2P_DEV_REPORTED_ONCE BIT(15) #define P2P_DEV_PREFER_PERSISTENT_RECONN BIT(16) +#define P2P_DEV_PD_BEFORE_GO_NEG BIT(17) +#define P2P_DEV_NO_PREF_CHAN BIT(18) unsigned int flags; int status; /* enum p2p_status_code */ @@ -114,6 +115,7 @@ struct p2p_sd_query *next; u8 peer[ETH_ALEN]; int for_all_peers; + int wsd; /* Wi-Fi Display Service Discovery Request */ struct wpabuf *tlvs; }; @@ -207,11 +209,6 @@ * P2P_INVITE_LISTEN - Listen during Invite */ P2P_INVITE_LISTEN, - - /** - * P2P_SEARCH_WHEN_READY - Waiting to start Search - */ - P2P_SEARCH_WHEN_READY, } state; /** @@ -225,6 +222,11 @@ int max_disc_int; /** + * max_disc_tu - Maximum number of TUs for discoverable interval + */ + int max_disc_tu; + + /** * devices - List of known P2P Device peers */ struct dl_list devices; @@ -241,6 +243,7 @@ const u8 *invite_go_dev_addr; u8 invite_go_dev_addr_buf[ETH_ALEN]; + int invite_dev_pw_id; /** * sd_peer - Pointer to Service Discovery peer @@ -308,6 +311,8 @@ */ struct p2p_channels channels; + struct wpa_freq_range_list no_go_freq; + enum p2p_pending_action_state { P2P_NO_PENDING_ACTION, P2P_PENDING_GO_NEG_REQUEST, @@ -377,6 +382,7 @@ } start_after_scan; u8 after_scan_peer[ETH_ALEN]; struct p2p_pending_action_tx *after_scan_tx; + unsigned int after_scan_tx_in_progress:1; /* Requested device types for find/search */ unsigned int num_req_dev_types; @@ -384,6 +390,8 @@ u8 *find_dev_id; u8 find_dev_id_buf[ETH_ALEN]; + struct os_reltime find_start; /* time of last p2p_find start */ + struct p2p_group **groups; size_t num_groups; @@ -407,6 +415,7 @@ int best_freq_24; int best_freq_5; int best_freq_overall; + int own_freq_preference; /** * wps_vendor_ext - WPS Vendor Extensions to add @@ -429,6 +438,37 @@ * in IDLE state. */ int pd_retries; + + /** + * pd_force_freq - Forced frequency for PD retries or 0 to auto-select + * + * This is is used during PD retries for join-a-group case to use the + * correct operating frequency determined from a BSS entry for the GO. + */ + int pd_force_freq; + + u8 go_timeout; + u8 client_timeout; + + /* Extra delay in milliseconds between search iterations */ + unsigned int search_delay; + int in_search_delay; + +#ifdef CONFIG_WIFI_DISPLAY + struct wpabuf *wfd_ie_beacon; + struct wpabuf *wfd_ie_probe_req; + struct wpabuf *wfd_ie_probe_resp; + struct wpabuf *wfd_ie_assoc_req; + struct wpabuf *wfd_ie_invitation; + struct wpabuf *wfd_ie_prov_disc_req; + struct wpabuf *wfd_ie_prov_disc_resp; + struct wpabuf *wfd_ie_go_neg; + struct wpabuf *wfd_dev_info; + struct wpabuf *wfd_assoc_bssid; + struct wpabuf *wfd_coupled_sink_info; +#endif /* CONFIG_WIFI_DISPLAY */ + + u16 authorized_oob_dev_pw_id; }; /** @@ -437,6 +477,7 @@ struct p2p_message { struct wpabuf *p2p_attributes; struct wpabuf *wps_attributes; + struct wpabuf *wfd_subelems; u8 dialog_token; @@ -469,6 +510,8 @@ const u8 *minor_reason_code; + const u8 *oob_go_neg_channel; + /* P2P Device Info */ const u8 *p2p_device_info; size_t p2p_device_info_len; @@ -480,6 +523,7 @@ /* WPS IE */ u16 dev_password_id; + int dev_password_id_present; u16 wps_config_methods; const u8 *wps_pri_dev_type; const u8 *wps_sec_dev_type_list; @@ -494,6 +538,8 @@ size_t model_number_len; const u8 *serial_number; size_t serial_number_len; + const u8 *oob_dev_password; + size_t oob_dev_password_len; /* DS Parameter Set IE */ const u8 *ds_params; @@ -523,19 +569,29 @@ /* p2p_utils.c */ int p2p_random(char *buf, size_t len); -int p2p_channel_to_freq(const char *country, int reg_class, int channel); -int p2p_freq_to_channel(const char *country, unsigned int freq, u8 *reg_class, - u8 *channel); +int p2p_channel_to_freq(int op_class, int channel); +int p2p_freq_to_channel(unsigned int freq, u8 *op_class, u8 *channel); void p2p_channels_intersect(const struct p2p_channels *a, const struct p2p_channels *b, struct p2p_channels *res); +void p2p_channels_union(const struct p2p_channels *a, + const struct p2p_channels *b, + struct p2p_channels *res); +void p2p_channels_remove_freqs(struct p2p_channels *chan, + const struct wpa_freq_range_list *list); int p2p_channels_includes(const struct p2p_channels *channels, u8 reg_class, u8 channel); +void p2p_channels_dump(struct p2p_data *p2p, const char *title, + const struct p2p_channels *chan); +int p2p_channel_select(struct p2p_channels *chans, const int *classes, + u8 *op_class, u8 *op_channel); /* p2p_parse.c */ int p2p_parse_p2p_ie(const struct wpabuf *buf, struct p2p_message *msg); int p2p_parse_ies(const u8 *data, size_t len, struct p2p_message *msg); int p2p_parse(const u8 *data, size_t len, struct p2p_message *msg); +int p2p_parse_ies_separate(const u8 *wsc, size_t wsc_len, const u8 *p2p, + size_t p2p_len, struct p2p_message *msg); void p2p_parse_free(struct p2p_message *msg); int p2p_attr_text(struct wpabuf *data, char *buf, char *end); int p2p_group_info_parse(const u8 *gi, size_t gi_len, @@ -555,6 +611,15 @@ u8 p2p_group_presence_req(struct p2p_group *group, const u8 *client_interface_addr, const u8 *noa, size_t noa_len); +int p2p_group_is_group_id_match(struct p2p_group *group, const u8 *group_id, + size_t group_id_len); +void p2p_group_update_ies(struct p2p_group *group); +void p2p_group_force_beacon_update_ies(struct p2p_group *group); +struct wpabuf * p2p_group_get_wfd_ie(struct p2p_group *g); +void p2p_buf_add_group_info(struct p2p_group *group, struct wpabuf *buf, + int max_clients); +void p2p_group_buf_add_id(struct p2p_group *group, struct wpabuf *buf); +int p2p_group_get_freq(struct p2p_group *group); void p2p_buf_add_action_hdr(struct wpabuf *buf, u8 subtype, u8 dialog_token); @@ -586,8 +651,11 @@ void p2p_buf_add_ext_listen_timing(struct wpabuf *buf, u16 period, u16 interval); void p2p_buf_add_p2p_interface(struct wpabuf *buf, struct p2p_data *p2p); -void p2p_build_wps_ie(struct p2p_data *p2p, struct wpabuf *buf, u16 pw_id, - int all_attr); +void p2p_buf_add_oob_go_neg_channel(struct wpabuf *buf, const char *country, + u8 oper_class, u8 channel, + enum p2p_role_indication role); +int p2p_build_wps_ie(struct p2p_data *p2p, struct wpabuf *buf, int pw_id, + int all_attr); /* p2p_sd.c */ struct p2p_sd_query * p2p_pending_sd_req(struct p2p_data *p2p, @@ -614,6 +682,9 @@ void p2p_process_go_neg_conf(struct p2p_data *p2p, const u8 *sa, const u8 *data, size_t len); int p2p_connect_send(struct p2p_data *p2p, struct p2p_device *dev); +u16 p2p_wps_method_pw_id(enum p2p_wps_method wps_method); +void p2p_reselect_channel(struct p2p_data *p2p, + struct p2p_channels *intersection); /* p2p_pd.c */ void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa, @@ -630,7 +701,7 @@ void p2p_process_invitation_resp(struct p2p_data *p2p, const u8 *sa, const u8 *data, size_t len); int p2p_invite_send(struct p2p_data *p2p, struct p2p_device *dev, - const u8 *go_dev_addr); + const u8 *go_dev_addr, int dev_pw_id); void p2p_invitation_req_cb(struct p2p_data *p2p, int success); void p2p_invitation_resp_cb(struct p2p_data *p2p, int success); @@ -657,8 +728,9 @@ struct p2p_message *msg); void p2p_add_dev_info(struct p2p_data *p2p, const u8 *addr, struct p2p_device *dev, struct p2p_message *msg); -int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq, int level, - const u8 *ies, size_t ies_len); +int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq, + struct os_reltime *rx_time, int level, const u8 *ies, + size_t ies_len, int scan_res); struct p2p_device * p2p_get_device(struct p2p_data *p2p, const u8 *addr); struct p2p_device * p2p_get_device_interface(struct p2p_data *p2p, const u8 *addr); @@ -674,5 +746,14 @@ const u8 *src, const u8 *bssid, const u8 *buf, size_t len, unsigned int wait_time); void p2p_stop_listen_for_freq(struct p2p_data *p2p, int freq); +int p2p_prepare_channel(struct p2p_data *p2p, struct p2p_device *dev, + unsigned int force_freq, unsigned int pref_freq, + int go); +void p2p_dbg(struct p2p_data *p2p, const char *fmt, ...) +PRINTF_FORMAT(2, 3); +void p2p_info(struct p2p_data *p2p, const char *fmt, ...) +PRINTF_FORMAT(2, 3); +void p2p_err(struct p2p_data *p2p, const char *fmt, ...) +PRINTF_FORMAT(2, 3); #endif /* P2P_I_H */ diff -Nru wpa-1.0/src/p2p/p2p_invitation.c wpa-2.1/src/p2p/p2p_invitation.c --- wpa-1.0/src/p2p/p2p_invitation.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/p2p/p2p_invitation.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * Wi-Fi Direct - P2P Invitation procedure * Copyright (c) 2010, Atheros Communications * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -22,13 +16,36 @@ static struct wpabuf * p2p_build_invitation_req(struct p2p_data *p2p, struct p2p_device *peer, - const u8 *go_dev_addr) + const u8 *go_dev_addr, + int dev_pw_id) { struct wpabuf *buf; u8 *len; const u8 *dev_addr; + size_t extra = 0; - buf = wpabuf_alloc(1000); +#ifdef CONFIG_WIFI_DISPLAY + struct wpabuf *wfd_ie = p2p->wfd_ie_invitation; + if (wfd_ie && p2p->inv_role == P2P_INVITE_ROLE_ACTIVE_GO) { + size_t i; + for (i = 0; i < p2p->num_groups; i++) { + struct p2p_group *g = p2p->groups[i]; + struct wpabuf *ie; + if (os_memcmp(p2p_group_get_interface_addr(g), + p2p->inv_bssid, ETH_ALEN) != 0) + continue; + ie = p2p_group_get_wfd_ie(g); + if (ie) { + wfd_ie = ie; + break; + } + } + } + if (wfd_ie) + extra = wpabuf_len(wfd_ie); +#endif /* CONFIG_WIFI_DISPLAY */ + + buf = wpabuf_alloc(1000 + extra); if (buf == NULL) return NULL; @@ -42,11 +59,15 @@ if (p2p->inv_role == P2P_INVITE_ROLE_ACTIVE_GO || !p2p->inv_persistent) p2p_buf_add_config_timeout(buf, 0, 0); else - p2p_buf_add_config_timeout(buf, 100, 20); + p2p_buf_add_config_timeout(buf, p2p->go_timeout, + p2p->client_timeout); p2p_buf_add_invitation_flags(buf, p2p->inv_persistent ? P2P_INVITATION_FLAGS_TYPE : 0); - p2p_buf_add_operating_channel(buf, p2p->cfg->country, - p2p->op_reg_class, p2p->op_channel); + if (p2p->inv_role != P2P_INVITE_ROLE_CLIENT || + !(peer->flags & P2P_DEV_NO_PREF_CHAN)) + p2p_buf_add_operating_channel(buf, p2p->cfg->country, + p2p->op_reg_class, + p2p->op_channel); if (p2p->inv_bssid_set) p2p_buf_add_group_bssid(buf, p2p->inv_bssid); p2p_buf_add_channel_list(buf, p2p->cfg->country, &p2p->channels); @@ -60,6 +81,16 @@ p2p_buf_add_device_info(buf, p2p, peer); p2p_buf_update_ie_hdr(buf, len); +#ifdef CONFIG_WIFI_DISPLAY + if (wfd_ie) + wpabuf_put_buf(buf, wfd_ie); +#endif /* CONFIG_WIFI_DISPLAY */ + + if (dev_pw_id >= 0) { + /* WSC IE in Invitation Request for NFC static handover */ + p2p_build_wps_ie(p2p, buf, dev_pw_id, 0); + } + return buf; } @@ -73,8 +104,30 @@ { struct wpabuf *buf; u8 *len; + size_t extra = 0; + +#ifdef CONFIG_WIFI_DISPLAY + struct wpabuf *wfd_ie = p2p->wfd_ie_invitation; + if (wfd_ie && group_bssid) { + size_t i; + for (i = 0; i < p2p->num_groups; i++) { + struct p2p_group *g = p2p->groups[i]; + struct wpabuf *ie; + if (os_memcmp(p2p_group_get_interface_addr(g), + group_bssid, ETH_ALEN) != 0) + continue; + ie = p2p_group_get_wfd_ie(g); + if (ie) { + wfd_ie = ie; + break; + } + } + } + if (wfd_ie) + extra = wpabuf_len(wfd_ie); +#endif /* CONFIG_WIFI_DISPLAY */ - buf = wpabuf_alloc(1000); + buf = wpabuf_alloc(1000 + extra); if (buf == NULL) return NULL; @@ -93,6 +146,11 @@ p2p_buf_add_channel_list(buf, p2p->cfg->country, channels); p2p_buf_update_ie_hdr(buf, len); +#ifdef CONFIG_WIFI_DISPLAY + if (wfd_ie) + wpabuf_put_buf(buf, wfd_ie); +#endif /* CONFIG_WIFI_DISPLAY */ + return buf; } @@ -114,8 +172,7 @@ os_memset(group_bssid, 0, sizeof(group_bssid)); - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Received Invitation Request from " MACSTR " (freq=%d)", + p2p_dbg(p2p, "Received Invitation Request from " MACSTR " (freq=%d)", MAC2STR(sa), rx_freq); if (p2p_parse(data, len, &msg)) @@ -123,13 +180,12 @@ dev = p2p_get_device(p2p, sa); if (dev == NULL || (dev->flags & P2P_DEV_PROBE_REQ_ONLY)) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Invitation Request from unknown peer " - MACSTR, MAC2STR(sa)); + p2p_dbg(p2p, "Invitation Request from unknown peer " MACSTR, + MAC2STR(sa)); - if (p2p_add_device(p2p, sa, rx_freq, 0, data + 1, len - 1)) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Invitation Request add device failed " + if (p2p_add_device(p2p, sa, rx_freq, NULL, 0, data + 1, len - 1, + 0)) { + p2p_dbg(p2p, "Invitation Request add device failed " MACSTR, MAC2STR(sa)); status = P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE; goto fail; @@ -137,18 +193,16 @@ dev = p2p_get_device(p2p, sa); if (dev == NULL) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Reject Invitation Request from unknown " - "peer " MACSTR, MAC2STR(sa)); + p2p_dbg(p2p, "Reject Invitation Request from unknown peer " + MACSTR, MAC2STR(sa)); status = P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE; goto fail; } } if (!msg.group_id || !msg.channel_list) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Mandatory attribute missing in Invitation " - "Request from " MACSTR, MAC2STR(sa)); + p2p_dbg(p2p, "Mandatory attribute missing in Invitation Request from " + MACSTR, MAC2STR(sa)); status = P2P_SC_FAIL_INVALID_PARAMS; goto fail; } @@ -161,44 +215,43 @@ * the request was for a persistent group if the attribute is * missing. */ - wpa_printf(MSG_DEBUG, "P2P: Mandatory Invitation Flags " - "attribute missing from Invitation Request"); + p2p_dbg(p2p, "Mandatory Invitation Flags attribute missing from Invitation Request"); persistent = 1; } if (p2p_peer_channels_check(p2p, &p2p->cfg->channels, dev, msg.channel_list, msg.channel_list_len) < 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: No common channels found"); + p2p_dbg(p2p, "No common channels found"); status = P2P_SC_FAIL_NO_COMMON_CHANNELS; goto fail; } + p2p_channels_intersect(&p2p->cfg->channels, &dev->channels, + &intersection); + if (p2p->cfg->invitation_process) { status = p2p->cfg->invitation_process( p2p->cfg->cb_ctx, sa, msg.group_bssid, msg.group_id, msg.group_id + ETH_ALEN, msg.group_id_len - ETH_ALEN, - &go, group_bssid, &op_freq, persistent); + &go, group_bssid, &op_freq, persistent, &intersection, + msg.dev_password_id_present ? msg.dev_password_id : -1); } if (op_freq) { - if (p2p_freq_to_channel(p2p->cfg->country, op_freq, - ®_class, &channel) < 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Unknown forced freq %d MHz from " - "invitation_process()", op_freq); + p2p_dbg(p2p, "Invitation processing forced frequency %d MHz", + op_freq); + if (p2p_freq_to_channel(op_freq, ®_class, &channel) < 0) { + p2p_dbg(p2p, "Unknown forced freq %d MHz from invitation_process()", + op_freq); status = P2P_SC_FAIL_NO_COMMON_CHANNELS; goto fail; } - p2p_channels_intersect(&p2p->cfg->channels, &dev->channels, - &intersection); if (!p2p_channels_includes(&intersection, reg_class, channel)) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: forced freq %d MHz not in the supported " - "channels interaction", op_freq); + p2p_dbg(p2p, "forced freq %d MHz not in the supported channels interaction", + op_freq); status = P2P_SC_FAIL_NO_COMMON_CHANNELS; goto fail; } @@ -206,24 +259,71 @@ if (status == P2P_SC_SUCCESS) channels = &intersection; } else { - op_freq = p2p_channel_to_freq(p2p->cfg->country, - p2p->cfg->op_reg_class, - p2p->cfg->op_channel); + p2p_dbg(p2p, "No forced channel from invitation processing - figure out best one to use"); + + /* Default to own configuration as a starting point */ + p2p->op_reg_class = p2p->cfg->op_reg_class; + p2p->op_channel = p2p->cfg->op_channel; + p2p_dbg(p2p, "Own default op_class %d channel %d", + p2p->op_reg_class, p2p->op_channel); + + /* Use peer preference if specified and compatible */ + if (msg.operating_channel) { + int req_freq; + req_freq = p2p_channel_to_freq( + msg.operating_channel[3], + msg.operating_channel[4]); + p2p_dbg(p2p, "Peer operating channel preference: %d MHz", + req_freq); + if (req_freq > 0 && + p2p_channels_includes(&intersection, + msg.operating_channel[3], + msg.operating_channel[4])) { + p2p->op_reg_class = msg.operating_channel[3]; + p2p->op_channel = msg.operating_channel[4]; + p2p_dbg(p2p, "Use peer preference op_class %d channel %d", + p2p->op_reg_class, p2p->op_channel); + } else { + p2p_dbg(p2p, "Cannot use peer channel preference"); + } + } + + if (!p2p_channels_includes(&intersection, p2p->op_reg_class, + p2p->op_channel)) { + p2p_dbg(p2p, "Initially selected channel (op_class %d channel %d) not in channel intersection - try to reselect", + p2p->op_reg_class, p2p->op_channel); + p2p_reselect_channel(p2p, &intersection); + p2p_dbg(p2p, "Re-selection result: op_class %d channel %d", + p2p->op_reg_class, p2p->op_channel); + if (!p2p_channels_includes(&intersection, + p2p->op_reg_class, + p2p->op_channel)) { + p2p_dbg(p2p, "Peer does not support selected operating channel (reg_class=%u channel=%u)", + p2p->op_reg_class, p2p->op_channel); + status = P2P_SC_FAIL_NO_COMMON_CHANNELS; + goto fail; + } + } else if (!(dev->flags & P2P_DEV_FORCE_FREQ) && + !p2p->cfg->cfg_op_channel) { + p2p_dbg(p2p, "Try to reselect channel selection with peer information received; previously selected op_class %u channel %u", + p2p->op_reg_class, p2p->op_channel); + p2p_reselect_channel(p2p, &intersection); + } + + op_freq = p2p_channel_to_freq(p2p->op_reg_class, + p2p->op_channel); if (op_freq < 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Unknown operational channel " - "(country=%c%c reg_class=%u channel=%u)", + p2p_dbg(p2p, "Unknown operational channel (country=%c%c reg_class=%u channel=%u)", p2p->cfg->country[0], p2p->cfg->country[1], - p2p->cfg->op_reg_class, p2p->cfg->op_channel); + p2p->op_reg_class, p2p->op_channel); status = P2P_SC_FAIL_NO_COMMON_CHANNELS; goto fail; } + p2p_dbg(p2p, "Selected operating channel - %d MHz", op_freq); - p2p_channels_intersect(&p2p->cfg->channels, &dev->channels, - &intersection); if (status == P2P_SC_SUCCESS) { - reg_class = p2p->cfg->op_reg_class; - channel = p2p->cfg->op_channel; + reg_class = p2p->op_reg_class; + channel = p2p->op_channel; channels = &intersection; } } @@ -242,12 +342,10 @@ if (rx_freq > 0) freq = rx_freq; else - freq = p2p_channel_to_freq(p2p->cfg->country, - p2p->cfg->reg_class, + freq = p2p_channel_to_freq(p2p->cfg->reg_class, p2p->cfg->channel); if (freq < 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Unknown regulatory class/channel"); + p2p_dbg(p2p, "Unknown regulatory class/channel"); goto out; } @@ -274,8 +372,7 @@ if (p2p_send_action(p2p, freq, sa, p2p->cfg->dev_addr, p2p->cfg->dev_addr, wpabuf_head(resp), wpabuf_len(resp), 200) < 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Failed to send Action frame"); + p2p_dbg(p2p, "Failed to send Action frame"); } out: @@ -289,22 +386,20 @@ { struct p2p_device *dev; struct p2p_message msg; + struct p2p_channels intersection, *channels = NULL; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Received Invitation Response from " MACSTR, + p2p_dbg(p2p, "Received Invitation Response from " MACSTR, MAC2STR(sa)); dev = p2p_get_device(p2p, sa); if (dev == NULL) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Ignore Invitation Response from unknown peer " + p2p_dbg(p2p, "Ignore Invitation Response from unknown peer " MACSTR, MAC2STR(sa)); return; } if (dev != p2p->invite_peer) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Ignore unexpected Invitation Response from peer " + p2p_dbg(p2p, "Ignore unexpected Invitation Response from peer " MACSTR, MAC2STR(sa)); return; } @@ -313,16 +408,45 @@ return; if (!msg.status) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Mandatory Status attribute missing in " - "Invitation Response from " MACSTR, MAC2STR(sa)); + p2p_dbg(p2p, "Mandatory Status attribute missing in Invitation Response from " + MACSTR, MAC2STR(sa)); + p2p_parse_free(&msg); + return; + } + + if (!msg.channel_list && *msg.status == P2P_SC_SUCCESS) { + p2p_dbg(p2p, "Mandatory Channel List attribute missing in Invitation Response from " + MACSTR, MAC2STR(sa)); +#ifdef CONFIG_P2P_STRICT + p2p_parse_free(&msg); + return; +#endif /* CONFIG_P2P_STRICT */ + /* Try to survive without peer channel list */ + channels = &p2p->channels; + } else if (!msg.channel_list) { + /* Non-success cases are not required to include Channel List */ + channels = &p2p->channels; + } else if (p2p_peer_channels_check(p2p, &p2p->channels, dev, + msg.channel_list, + msg.channel_list_len) < 0) { + p2p_dbg(p2p, "No common channels found"); p2p_parse_free(&msg); return; + } else { + p2p_channels_intersect(&p2p->channels, &dev->channels, + &intersection); + channels = &intersection; } - if (p2p->cfg->invitation_result) + if (p2p->cfg->invitation_result) { + int freq = p2p_channel_to_freq(p2p->op_reg_class, + p2p->op_channel); + if (freq < 0) + freq = 0; p2p->cfg->invitation_result(p2p->cfg->cb_ctx, *msg.status, - msg.group_bssid); + msg.group_bssid, channels, sa, + freq); + } p2p_parse_free(&msg); @@ -333,36 +457,35 @@ int p2p_invite_send(struct p2p_data *p2p, struct p2p_device *dev, - const u8 *go_dev_addr) + const u8 *go_dev_addr, int dev_pw_id) { struct wpabuf *req; int freq; freq = dev->listen_freq > 0 ? dev->listen_freq : dev->oper_freq; + if (freq <= 0) + freq = dev->oob_go_neg_freq; if (freq <= 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: No Listen/Operating frequency known for the " - "peer " MACSTR " to send Invitation Request", + p2p_dbg(p2p, "No Listen/Operating frequency known for the peer " + MACSTR " to send Invitation Request", MAC2STR(dev->info.p2p_device_addr)); return -1; } - req = p2p_build_invitation_req(p2p, dev, go_dev_addr); + req = p2p_build_invitation_req(p2p, dev, go_dev_addr, dev_pw_id); if (req == NULL) return -1; if (p2p->state != P2P_IDLE) p2p_stop_listen_for_freq(p2p, freq); - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Sending Invitation Request"); + p2p_dbg(p2p, "Sending Invitation Request"); p2p_set_state(p2p, P2P_INVITE); p2p->pending_action_state = P2P_PENDING_INVITATION_REQUEST; p2p->invite_peer = dev; dev->invitation_reqs++; if (p2p_send_action(p2p, freq, dev->info.p2p_device_addr, p2p->cfg->dev_addr, dev->info.p2p_device_addr, - wpabuf_head(req), wpabuf_len(req), 200) < 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Failed to send Action frame"); + wpabuf_head(req), wpabuf_len(req), 500) < 0) { + p2p_dbg(p2p, "Failed to send Action frame"); /* Use P2P find to recover and retry */ p2p_set_timeout(p2p, 0, 0); } @@ -375,12 +498,10 @@ void p2p_invitation_req_cb(struct p2p_data *p2p, int success) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Invitation Request TX callback: success=%d", success); + p2p_dbg(p2p, "Invitation Request TX callback: success=%d", success); if (p2p->invite_peer == NULL) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: No pending Invite"); + p2p_dbg(p2p, "No pending Invite"); return; } @@ -389,17 +510,19 @@ * channel. */ p2p_set_state(p2p, P2P_INVITE); - p2p_set_timeout(p2p, 0, 100000); + p2p_set_timeout(p2p, 0, success ? 500000 : 100000); } void p2p_invitation_resp_cb(struct p2p_data *p2p, int success) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Invitation Response TX callback: success=%d", success); + p2p_dbg(p2p, "Invitation Response TX callback: success=%d", success); p2p->cfg->send_action_done(p2p->cfg->cb_ctx); - if (success && p2p->cfg->invitation_received) { + if (!success) + p2p_dbg(p2p, "Assume Invitation Response was actually received by the peer even though Ack was not reported"); + + if (p2p->cfg->invitation_received) { p2p->cfg->invitation_received(p2p->cfg->cb_ctx, p2p->inv_sa, p2p->inv_group_bssid_ptr, @@ -414,41 +537,52 @@ int p2p_invite(struct p2p_data *p2p, const u8 *peer, enum p2p_invite_role role, const u8 *bssid, const u8 *ssid, size_t ssid_len, unsigned int force_freq, const u8 *go_dev_addr, - int persistent_group) + int persistent_group, unsigned int pref_freq, int dev_pw_id) { struct p2p_device *dev; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Request to invite peer " MACSTR " role=%d persistent=%d " + p2p_dbg(p2p, "Request to invite peer " MACSTR " role=%d persistent=%d " "force_freq=%u", MAC2STR(peer), role, persistent_group, force_freq); if (bssid) - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Invitation for BSSID " MACSTR, MAC2STR(bssid)); + p2p_dbg(p2p, "Invitation for BSSID " MACSTR, MAC2STR(bssid)); if (go_dev_addr) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Invitation for GO Device Address " MACSTR, + p2p_dbg(p2p, "Invitation for GO Device Address " MACSTR, MAC2STR(go_dev_addr)); os_memcpy(p2p->invite_go_dev_addr_buf, go_dev_addr, ETH_ALEN); p2p->invite_go_dev_addr = p2p->invite_go_dev_addr_buf; } else p2p->invite_go_dev_addr = NULL; - wpa_hexdump_ascii(MSG_DEBUG, "P2P: Invitation for SSID", + wpa_hexdump_ascii(MSG_DEBUG, "Invitation for SSID", ssid, ssid_len); + if (dev_pw_id >= 0) { + p2p_dbg(p2p, "Invitation to use Device Password ID %d", + dev_pw_id); + } + p2p->invite_dev_pw_id = dev_pw_id; dev = p2p_get_device(p2p, peer); - if (dev == NULL || (dev->listen_freq <= 0 && dev->oper_freq <= 0)) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Cannot invite unknown P2P Device " MACSTR, + if (dev == NULL || (dev->listen_freq <= 0 && dev->oper_freq <= 0 && + dev->oob_go_neg_freq <= 0)) { + p2p_dbg(p2p, "Cannot invite unknown P2P Device " MACSTR, MAC2STR(peer)); return -1; } + if (p2p_prepare_channel(p2p, dev, force_freq, pref_freq, + role != P2P_INVITE_ROLE_CLIENT) < 0) + return -1; + + if (persistent_group && role == P2P_INVITE_ROLE_CLIENT && !force_freq && + !pref_freq) + dev->flags |= P2P_DEV_NO_PREF_CHAN; + else + dev->flags &= ~P2P_DEV_NO_PREF_CHAN; + if (dev->flags & P2P_DEV_GROUP_CLIENT_ONLY) { if (!(dev->info.dev_capab & P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY)) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Cannot invite a P2P Device " MACSTR + p2p_dbg(p2p, "Cannot invite a P2P Device " MACSTR " that is in a group and is not discoverable", MAC2STR(peer)); } @@ -457,26 +591,6 @@ dev->invitation_reqs = 0; - if (force_freq) { - if (p2p_freq_to_channel(p2p->cfg->country, force_freq, - &p2p->op_reg_class, &p2p->op_channel) < - 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Unsupported frequency %u MHz", - force_freq); - return -1; - } - p2p->channels.reg_classes = 1; - p2p->channels.reg_class[0].channels = 1; - p2p->channels.reg_class[0].reg_class = p2p->op_reg_class; - p2p->channels.reg_class[0].channel[0] = p2p->op_channel; - } else { - p2p->op_reg_class = p2p->cfg->op_reg_class; - p2p->op_channel = p2p->cfg->op_channel; - os_memcpy(&p2p->channels, &p2p->cfg->channels, - sizeof(struct p2p_channels)); - } - if (p2p->state != P2P_IDLE) p2p_stop_find(p2p); @@ -487,5 +601,5 @@ os_memcpy(p2p->inv_ssid, ssid, ssid_len); p2p->inv_ssid_len = ssid_len; p2p->inv_persistent = persistent_group; - return p2p_invite_send(p2p, dev, go_dev_addr); + return p2p_invite_send(p2p, dev, go_dev_addr, dev_pw_id); } diff -Nru wpa-1.0/src/p2p/p2p_parse.c wpa-2.1/src/p2p/p2p_parse.c --- wpa-1.0/src/p2p/p2p_parse.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/p2p/p2p_parse.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * P2P - IE parser * Copyright (c) 2009-2010, Atheros Communications * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -274,6 +268,19 @@ wpa_printf(MSG_DEBUG, "P2P: * Minor Reason Code: %u", *msg->minor_reason_code); break; + case P2P_ATTR_OOB_GO_NEG_CHANNEL: + if (len < 6) { + wpa_printf(MSG_DEBUG, "P2P: Too short OOB GO Neg " + "Channel attribute (length %d)", len); + return -1; + } + msg->oob_go_neg_channel = data; + wpa_printf(MSG_DEBUG, "P2P: * OOB GO Neg Channel: " + "Country %c%c(0x%02x) Operating Class %d " + "Channel Number %d Role %d", + data[0], data[1], data[2], data[3], data[4], + data[5]); + break; default: wpa_printf(MSG_DEBUG, "P2P: Skipped unknown attribute %d " "(length %d)", id, len); @@ -346,6 +353,7 @@ msg->dev_password_id = WPA_GET_BE16(attr.dev_password_id); wpa_printf(MSG_DEBUG, "P2P: Device Password ID: %d", msg->dev_password_id); + msg->dev_password_id_present = 1; } if (attr.primary_dev_type) { char devtype[WPS_DEV_TYPE_BUFSIZE]; @@ -373,6 +381,9 @@ msg->serial_number = attr.serial_number; msg->serial_number_len = attr.serial_number_len; + msg->oob_dev_password = attr.oob_dev_password; + msg->oob_dev_password_len = attr.oob_dev_password_len; + return 0; } @@ -420,6 +431,13 @@ return -1; } +#ifdef CONFIG_WIFI_DISPLAY + if (elems.wfd) { + msg->wfd_subelems = ieee802_11_vendor_ie_concat( + data, len, WFD_IE_VENDOR_TYPE); + } +#endif /* CONFIG_WIFI_DISPLAY */ + return 0; } @@ -449,6 +467,33 @@ } +int p2p_parse_ies_separate(const u8 *wsc, size_t wsc_len, const u8 *p2p, + size_t p2p_len, struct p2p_message *msg) +{ + os_memset(msg, 0, sizeof(*msg)); + + msg->wps_attributes = wpabuf_alloc_copy(wsc, wsc_len); + if (msg->wps_attributes && + p2p_parse_wps_ie(msg->wps_attributes, msg)) { + p2p_parse_free(msg); + return -1; + } + + msg->p2p_attributes = wpabuf_alloc_copy(p2p, p2p_len); + if (msg->p2p_attributes && + p2p_parse_p2p_ie(msg->p2p_attributes, msg)) { + wpa_printf(MSG_DEBUG, "P2P: Failed to parse P2P IE data"); + if (msg->p2p_attributes) + wpa_hexdump_buf(MSG_MSGDUMP, "P2P: P2P IE data", + msg->p2p_attributes); + p2p_parse_free(msg); + return -1; + } + + return 0; +} + + /** * p2p_parse_free - Free temporary data from P2P parsing * @msg: Parsed attributes @@ -459,6 +504,10 @@ msg->p2p_attributes = NULL; wpabuf_free(msg->wps_attributes); msg->wps_attributes = NULL; +#ifdef CONFIG_WIFI_DISPLAY + wpabuf_free(msg->wfd_subelems); + msg->wfd_subelems = NULL; +#endif /* CONFIG_WIFI_DISPLAY */ } diff -Nru wpa-1.0/src/p2p/p2p_pd.c wpa-2.1/src/p2p/p2p_pd.c --- wpa-1.0/src/p2p/p2p_pd.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/p2p/p2p_pd.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * Wi-Fi Direct - P2P provision discovery * Copyright (c) 2009-2010, Atheros Communications * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -22,10 +16,10 @@ /* - * Number of retries to attempt for provision discovery requests during IDLE - * state in case the peer is not listening. + * Number of retries to attempt for provision discovery requests + * in case the peer is not listening. */ -#define MAX_PROV_DISC_REQ_RETRIES 10 +#define MAX_PROV_DISC_REQ_RETRIES 120 static void p2p_build_wps_ie_config_methods(struct wpabuf *buf, @@ -52,15 +46,22 @@ { struct wpabuf *buf; u8 *len; + size_t extra = 0; + +#ifdef CONFIG_WIFI_DISPLAY + if (p2p->wfd_ie_prov_disc_req) + extra = wpabuf_len(p2p->wfd_ie_prov_disc_req); +#endif /* CONFIG_WIFI_DISPLAY */ - buf = wpabuf_alloc(1000); + buf = wpabuf_alloc(1000 + extra); if (buf == NULL) return NULL; p2p_buf_add_public_action_hdr(buf, P2P_PROV_DISC_REQ, dialog_token); len = p2p_buf_add_ie_hdr(buf); - p2p_buf_add_capability(buf, p2p->dev_capab, 0); + p2p_buf_add_capability(buf, p2p->dev_capab & + ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY, 0); p2p_buf_add_device_info(buf, p2p, NULL); if (go) { p2p_buf_add_group_id(buf, go->info.p2p_device_addr, @@ -71,17 +72,46 @@ /* WPS IE with Config Methods attribute */ p2p_build_wps_ie_config_methods(buf, config_methods); +#ifdef CONFIG_WIFI_DISPLAY + if (p2p->wfd_ie_prov_disc_req) + wpabuf_put_buf(buf, p2p->wfd_ie_prov_disc_req); +#endif /* CONFIG_WIFI_DISPLAY */ + return buf; } static struct wpabuf * p2p_build_prov_disc_resp(struct p2p_data *p2p, u8 dialog_token, - u16 config_methods) + u16 config_methods, + const u8 *group_id, + size_t group_id_len) { struct wpabuf *buf; + size_t extra = 0; - buf = wpabuf_alloc(100); +#ifdef CONFIG_WIFI_DISPLAY + struct wpabuf *wfd_ie = p2p->wfd_ie_prov_disc_resp; + if (wfd_ie && group_id) { + size_t i; + for (i = 0; i < p2p->num_groups; i++) { + struct p2p_group *g = p2p->groups[i]; + struct wpabuf *ie; + if (!p2p_group_is_group_id_match(g, group_id, + group_id_len)) + continue; + ie = p2p_group_get_wfd_ie(g); + if (ie) { + wfd_ie = ie; + break; + } + } + } + if (wfd_ie) + extra = wpabuf_len(wfd_ie); +#endif /* CONFIG_WIFI_DISPLAY */ + + buf = wpabuf_alloc(100 + extra); if (buf == NULL) return NULL; @@ -90,6 +120,11 @@ /* WPS IE with Config Methods attribute */ p2p_build_wps_ie_config_methods(buf, config_methods); +#ifdef CONFIG_WIFI_DISPLAY + if (wfd_ie) + wpabuf_put_buf(buf, wfd_ie); +#endif /* CONFIG_WIFI_DISPLAY */ + return buf; } @@ -106,41 +141,56 @@ if (p2p_parse(data, len, &msg)) return; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Received Provision Discovery Request from " MACSTR + p2p_dbg(p2p, "Received Provision Discovery Request from " MACSTR " with config methods 0x%x (freq=%d)", MAC2STR(sa), msg.wps_config_methods, rx_freq); dev = p2p_get_device(p2p, sa); if (dev == NULL || (dev->flags & P2P_DEV_PROBE_REQ_ONLY)) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Provision Discovery Request from " - "unknown peer " MACSTR, MAC2STR(sa)); - if (p2p_add_device(p2p, sa, rx_freq, 0, data + 1, len - 1)) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Provision Discovery Request add device " - "failed " MACSTR, MAC2STR(sa)); + p2p_dbg(p2p, "Provision Discovery Request from unknown peer " + MACSTR, MAC2STR(sa)); + + if (p2p_add_device(p2p, sa, rx_freq, NULL, 0, data + 1, len - 1, + 0)) { + p2p_dbg(p2p, "Provision Discovery Request add device failed " + MACSTR, MAC2STR(sa)); } + } else if (msg.wfd_subelems) { + wpabuf_free(dev->info.wfd_subelems); + dev->info.wfd_subelems = wpabuf_dup(msg.wfd_subelems); } if (!(msg.wps_config_methods & (WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD | WPS_CONFIG_PUSHBUTTON))) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Unsupported " - "Config Methods in Provision Discovery Request"); + p2p_dbg(p2p, "Unsupported Config Methods in Provision Discovery Request"); goto out; } + if (msg.group_id) { + size_t i; + for (i = 0; i < p2p->num_groups; i++) { + if (p2p_group_is_group_id_match(p2p->groups[i], + msg.group_id, + msg.group_id_len)) + break; + } + if (i == p2p->num_groups) { + p2p_dbg(p2p, "PD request for unknown P2P Group ID - reject"); + goto out; + } + } + if (dev) dev->flags &= ~(P2P_DEV_PD_PEER_DISPLAY | P2P_DEV_PD_PEER_KEYPAD); if (msg.wps_config_methods & WPS_CONFIG_DISPLAY) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Peer " MACSTR + p2p_dbg(p2p, "Peer " MACSTR " requested us to show a PIN on display", MAC2STR(sa)); if (dev) dev->flags |= P2P_DEV_PD_PEER_KEYPAD; } else if (msg.wps_config_methods & WPS_CONFIG_KEYPAD) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Peer " MACSTR + p2p_dbg(p2p, "Peer " MACSTR " requested us to write its PIN using keypad", MAC2STR(sa)); if (dev) @@ -151,22 +201,20 @@ out: resp = p2p_build_prov_disc_resp(p2p, msg.dialog_token, - reject ? 0 : msg.wps_config_methods); + reject ? 0 : msg.wps_config_methods, + msg.group_id, msg.group_id_len); if (resp == NULL) { p2p_parse_free(&msg); return; } - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Sending Provision Discovery Response"); + p2p_dbg(p2p, "Sending Provision Discovery Response"); if (rx_freq > 0) freq = rx_freq; else - freq = p2p_channel_to_freq(p2p->cfg->country, - p2p->cfg->reg_class, + freq = p2p_channel_to_freq(p2p->cfg->reg_class, p2p->cfg->channel); if (freq < 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Unknown regulatory class/channel"); + p2p_dbg(p2p, "Unknown regulatory class/channel"); wpabuf_free(resp); p2p_parse_free(&msg); return; @@ -175,8 +223,7 @@ if (p2p_send_action(p2p, freq, sa, p2p->cfg->dev_addr, p2p->cfg->dev_addr, wpabuf_head(resp), wpabuf_len(resp), 200) < 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Failed to send Action frame"); + p2p_dbg(p2p, "Failed to send Action frame"); } wpabuf_free(resp); @@ -203,39 +250,42 @@ { struct p2p_message msg; struct p2p_device *dev; - u16 report_config_methods = 0; + u16 report_config_methods = 0, req_config_methods; + int success = 0; if (p2p_parse(data, len, &msg)) return; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Received Provisioning Discovery Response from " MACSTR + p2p_dbg(p2p, "Received Provision Discovery Response from " MACSTR " with config methods 0x%x", MAC2STR(sa), msg.wps_config_methods); dev = p2p_get_device(p2p, sa); if (dev == NULL || !dev->req_config_methods) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Ignore Provisioning Discovery Response from " - MACSTR " with no pending request", MAC2STR(sa)); + p2p_dbg(p2p, "Ignore Provision Discovery Response from " MACSTR + " with no pending request", MAC2STR(sa)); p2p_parse_free(&msg); return; } - if (p2p->pending_action_state == P2P_PENDING_PD) { - os_memset(p2p->pending_pd_devaddr, 0, ETH_ALEN); - p2p->pending_action_state = P2P_NO_PENDING_ACTION; - } - if (dev->dialog_token != msg.dialog_token) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Ignore Provisioning Discovery Response with " - "unexpected Dialog Token %u (expected %u)", + p2p_dbg(p2p, "Ignore Provision Discovery Response with unexpected Dialog Token %u (expected %u)", msg.dialog_token, dev->dialog_token); p2p_parse_free(&msg); return; } + if (p2p->pending_action_state == P2P_PENDING_PD) { + os_memset(p2p->pending_pd_devaddr, 0, ETH_ALEN); + p2p->pending_action_state = P2P_NO_PENDING_ACTION; + } + + /* + * Use a local copy of the requested config methods since + * p2p_reset_pending_pd() can clear this in the peer entry. + */ + req_config_methods = dev->req_config_methods; + /* * If the response is from the peer to whom a user initiated request * was sent earlier, we reset that state info here. @@ -244,9 +294,9 @@ os_memcmp(p2p->pending_pd_devaddr, sa, ETH_ALEN) == 0) p2p_reset_pending_pd(p2p); - if (msg.wps_config_methods != dev->req_config_methods) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Peer rejected " - "our Provisioning Discovery Request"); + if (msg.wps_config_methods != req_config_methods) { + p2p_dbg(p2p, "Peer rejected our Provision Discovery Request (received config_methods 0x%x expected 0x%x", + msg.wps_config_methods, req_config_methods); if (p2p->cfg->prov_disc_fail) p2p->cfg->prov_disc_fail(p2p->cfg->cb_ctx, sa, P2P_PROV_DISC_REJECTED); @@ -254,15 +304,15 @@ goto out; } - report_config_methods = dev->req_config_methods; + report_config_methods = req_config_methods; dev->flags &= ~(P2P_DEV_PD_PEER_DISPLAY | P2P_DEV_PD_PEER_KEYPAD); - if (dev->req_config_methods & WPS_CONFIG_DISPLAY) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Peer " MACSTR + if (req_config_methods & WPS_CONFIG_DISPLAY) { + p2p_dbg(p2p, "Peer " MACSTR " accepted to show a PIN on display", MAC2STR(sa)); dev->flags |= P2P_DEV_PD_PEER_DISPLAY; } else if (msg.wps_config_methods & WPS_CONFIG_KEYPAD) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Peer " MACSTR + p2p_dbg(p2p, "Peer " MACSTR " accepted to write our PIN using keypad", MAC2STR(sa)); dev->flags |= P2P_DEV_PD_PEER_KEYPAD; @@ -272,13 +322,26 @@ dev->wps_prov_info = msg.wps_config_methods; p2p_parse_free(&msg); + success = 1; out: dev->req_config_methods = 0; p2p->cfg->send_action_done(p2p->cfg->cb_ctx); - if (p2p->cfg->prov_disc_resp) + if (dev->flags & P2P_DEV_PD_BEFORE_GO_NEG) { + p2p_dbg(p2p, "Start GO Neg after the PD-before-GO-Neg workaround with " + MACSTR, MAC2STR(dev->info.p2p_device_addr)); + dev->flags &= ~P2P_DEV_PD_BEFORE_GO_NEG; + p2p_connect_send(p2p, dev); + return; + } + if (success && p2p->cfg->prov_disc_resp) p2p->cfg->prov_disc_resp(p2p->cfg->cb_ctx, sa, report_config_methods); + + if (p2p->state == P2P_PD_DURING_FIND) { + p2p_clear_timeout(p2p); + p2p_continue_find(p2p); + } } @@ -294,9 +357,8 @@ freq = dev->listen_freq > 0 ? dev->listen_freq : dev->oper_freq; if (freq <= 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: No Listen/Operating frequency known for the " - "peer " MACSTR " to send Provision Discovery Request", + p2p_dbg(p2p, "No Listen/Operating frequency known for the peer " + MACSTR " to send Provision Discovery Request", MAC2STR(dev->info.p2p_device_addr)); return -1; } @@ -304,8 +366,7 @@ if (dev->flags & P2P_DEV_GROUP_CLIENT_ONLY) { if (!(dev->info.dev_capab & P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY)) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Cannot use PD with P2P Device " MACSTR + p2p_dbg(p2p, "Cannot use PD with P2P Device " MACSTR " that is in a group and is not discoverable", MAC2STR(dev->info.p2p_device_addr)); return -1; @@ -313,9 +374,6 @@ /* TODO: use device discoverability request through GO */ } - dev->dialog_token++; - if (dev->dialog_token == 0) - dev->dialog_token = 1; req = p2p_build_prov_disc_req(p2p, dev->dialog_token, dev->req_config_methods, join ? dev : NULL); @@ -328,8 +386,7 @@ if (p2p_send_action(p2p, freq, dev->info.p2p_device_addr, p2p->cfg->dev_addr, dev->info.p2p_device_addr, wpabuf_head(req), wpabuf_len(req), 200) < 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Failed to send Action frame"); + p2p_dbg(p2p, "Failed to send Action frame"); wpabuf_free(req); return -1; } @@ -342,7 +399,8 @@ int p2p_prov_disc_req(struct p2p_data *p2p, const u8 *peer_addr, - u16 config_methods, int join, int force_freq) + u16 config_methods, int join, int force_freq, + int user_initiated_pd) { struct p2p_device *dev; @@ -350,14 +408,13 @@ if (dev == NULL) dev = p2p_get_device_interface(p2p, peer_addr); if (dev == NULL || (dev->flags & P2P_DEV_PROBE_REQ_ONLY)) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Provision " - "Discovery Request destination " MACSTR + p2p_dbg(p2p, "Provision Discovery Request destination " MACSTR " not yet known", MAC2STR(peer_addr)); return -1; } - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Provision Discovery " - "Request with " MACSTR " (config methods 0x%x)", + p2p_dbg(p2p, "Provision Discovery Request with " MACSTR + " (config methods 0x%x)", MAC2STR(peer_addr), config_methods); if (config_methods == 0) return -1; @@ -373,23 +430,26 @@ if (p2p->state != P2P_IDLE && p2p->state != P2P_SEARCH && p2p->state != P2P_LISTEN_ONLY) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Busy with other " - "operations; postpone Provision Discovery Request " - "with " MACSTR " (config methods 0x%x)", + p2p_dbg(p2p, "Busy with other operations; postpone Provision Discovery Request with " + MACSTR " (config methods 0x%x)", MAC2STR(peer_addr), config_methods); return 0; } - /* - * We use the join param as a cue to differentiate between user - * initiated PD request and one issued during finds (internal). - */ - p2p->user_initiated_pd = !join; + p2p->user_initiated_pd = user_initiated_pd; + p2p->pd_force_freq = force_freq; - /* Also set some retries to attempt in case of IDLE state */ - if (p2p->user_initiated_pd && p2p->state == P2P_IDLE) + if (p2p->user_initiated_pd) p2p->pd_retries = MAX_PROV_DISC_REQ_RETRIES; + /* + * Assign dialog token here to use the same value in each retry within + * the same PD exchange. + */ + dev->dialog_token++; + if (dev->dialog_token == 0) + dev->dialog_token = 1; + return p2p_send_prov_disc_req(p2p, dev, join, force_freq); } @@ -413,4 +473,5 @@ p2p->user_initiated_pd = 0; os_memset(p2p->pending_pd_devaddr, 0, ETH_ALEN); p2p->pd_retries = 0; + p2p->pd_force_freq = 0; } diff -Nru wpa-1.0/src/p2p/p2p_sd.c wpa-2.1/src/p2p/p2p_sd.c --- wpa-1.0/src/p2p/p2p_sd.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/p2p/p2p_sd.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * Wi-Fi Direct - P2P service discovery * Copyright (c) 2009, Atheros Communications * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -21,15 +15,55 @@ #include "p2p.h" +#ifdef CONFIG_WIFI_DISPLAY +static int wfd_wsd_supported(struct wpabuf *wfd) +{ + const u8 *pos, *end; + u8 subelem; + u16 len; + + if (wfd == NULL) + return 0; + + pos = wpabuf_head(wfd); + end = pos + wpabuf_len(wfd); + + while (pos + 3 <= end) { + subelem = *pos++; + len = WPA_GET_BE16(pos); + pos += 2; + if (pos + len > end) + break; + + if (subelem == WFD_SUBELEM_DEVICE_INFO && len >= 6) { + u16 info = WPA_GET_BE16(pos); + return !!(info & 0x0040); + } + + pos += len; + } + + return 0; +} +#endif /* CONFIG_WIFI_DISPLAY */ + struct p2p_sd_query * p2p_pending_sd_req(struct p2p_data *p2p, struct p2p_device *dev) { struct p2p_sd_query *q; + int wsd = 0; if (!(dev->info.dev_capab & P2P_DEV_CAPAB_SERVICE_DISCOVERY)) - return 0; /* peer does not support SD */ + return NULL; /* peer does not support SD */ +#ifdef CONFIG_WIFI_DISPLAY + if (wfd_wsd_supported(dev->info.wfd_subelems)) + wsd = 1; +#endif /* CONFIG_WIFI_DISPLAY */ for (q = p2p->sd_queries; q; q = q->next) { + /* Use WSD only if the peer indicates support or it */ + if (q->wsd && !wsd) + continue; if (q->for_all_peers && !(dev->flags & P2P_DEV_SD_INFO)) return q; if (!q->for_all_peers && @@ -123,8 +157,7 @@ p2p->pending_action_state = P2P_NO_PENDING_ACTION; if (p2p_send_action(p2p, freq, dst, p2p->cfg->dev_addr, dst, wpabuf_head(req), wpabuf_len(req), 200) < 0) - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Failed to send Action frame"); + p2p_dbg(p2p, "Failed to send Action frame"); wpabuf_free(req); } @@ -201,9 +234,8 @@ freq = dev->listen_freq > 0 ? dev->listen_freq : dev->oper_freq; if (freq <= 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: No Listen/Operating frequency known for the " - "peer " MACSTR " to send SD Request", + p2p_dbg(p2p, "No Listen/Operating frequency known for the peer " + MACSTR " to send SD Request", MAC2STR(dev->info.p2p_device_addr)); return -1; } @@ -212,8 +244,7 @@ if (query == NULL) return -1; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Start Service Discovery with " MACSTR, + p2p_dbg(p2p, "Start Service Discovery with " MACSTR, MAC2STR(dev->info.p2p_device_addr)); req = p2p_build_sd_query(p2p->srv_update_indic, query->tlvs); @@ -227,8 +258,7 @@ if (p2p_send_action(p2p, freq, dev->info.p2p_device_addr, p2p->cfg->dev_addr, dev->info.p2p_device_addr, wpabuf_head(req), wpabuf_len(req), 5000) < 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Failed to send Action frame"); + p2p_dbg(p2p, "Failed to send Action frame"); ret = -1; } @@ -256,8 +286,7 @@ if (rx_freq > 0) freq = rx_freq; else - freq = p2p_channel_to_freq(p2p->cfg->country, - p2p->cfg->reg_class, + freq = p2p_channel_to_freq(p2p->cfg->reg_class, p2p->cfg->channel); if (freq < 0) return; @@ -266,14 +295,12 @@ return; dialog_token = *pos++; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: GAS Initial Request from " MACSTR " (dialog token %u, " - "freq %d)", + p2p_dbg(p2p, "GAS Initial Request from " MACSTR + " (dialog token %u, freq %d)", MAC2STR(sa), dialog_token, rx_freq); if (*pos != WLAN_EID_ADV_PROTO) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Unexpected IE in GAS Initial Request: %u", *pos); + p2p_dbg(p2p, "Unexpected IE in GAS Initial Request: %u", *pos); return; } pos++; @@ -281,15 +308,13 @@ slen = *pos++; next = pos + slen; if (next > end || slen < 2) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Invalid IE in GAS Initial Request"); + p2p_dbg(p2p, "Invalid IE in GAS Initial Request"); return; } pos++; /* skip QueryRespLenLimit and PAME-BI */ if (*pos != ACCESS_NETWORK_QUERY_PROTOCOL) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Unsupported GAS advertisement protocol id %u", + p2p_dbg(p2p, "Unsupported GAS advertisement protocol id %u", *pos); return; } @@ -308,8 +333,7 @@ if (pos + 4 > end) return; if (WPA_GET_LE16(pos) != ANQP_VENDOR_SPECIFIC) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Unsupported ANQP Info ID %u", WPA_GET_LE16(pos)); + p2p_dbg(p2p, "Unsupported ANQP Info ID %u", WPA_GET_LE16(pos)); return; } pos += 2; @@ -317,21 +341,18 @@ slen = WPA_GET_LE16(pos); pos += 2; if (pos + slen > end || slen < 3 + 1) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Invalid ANQP Query Request length"); + p2p_dbg(p2p, "Invalid ANQP Query Request length"); return; } if (WPA_GET_BE24(pos) != OUI_WFA) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Unsupported ANQP OUI %06x", WPA_GET_BE24(pos)); + p2p_dbg(p2p, "Unsupported ANQP OUI %06x", WPA_GET_BE24(pos)); return; } pos += 3; if (*pos != P2P_OUI_TYPE) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Unsupported ANQP vendor type %u", *pos); + p2p_dbg(p2p, "Unsupported ANQP vendor type %u", *pos); return; } pos++; @@ -339,8 +360,7 @@ if (pos + 2 > end) return; update_indic = WPA_GET_LE16(pos); - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Service Update Indicator: %u", update_indic); + p2p_dbg(p2p, "Service Update Indicator: %u", update_indic); pos += 2; p2p->cfg->sd_request(p2p->cfg->cb_ctx, freq, sa, dialog_token, @@ -356,8 +376,7 @@ /* TODO: fix the length limit to match with the maximum frame length */ if (wpabuf_len(resp_tlvs) > 1400) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: SD response long " - "enough to require fragmentation"); + p2p_dbg(p2p, "SD response long enough to require fragmentation"); if (p2p->sd_resp) { /* * TODO: Could consider storing the fragmented response @@ -366,20 +385,22 @@ * Though, that would eat more memory, so there are * also benefits to just using a single buffer. */ - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Drop " - "previous SD response"); + p2p_dbg(p2p, "Drop previous SD response"); wpabuf_free(p2p->sd_resp); } + p2p->sd_resp = wpabuf_dup(resp_tlvs); + if (p2p->sd_resp == NULL) { + p2p_err(p2p, "Failed to allocate SD response fragmentation area"); + return; + } os_memcpy(p2p->sd_resp_addr, dst, ETH_ALEN); p2p->sd_resp_dialog_token = dialog_token; - p2p->sd_resp = wpabuf_dup(resp_tlvs); p2p->sd_resp_pos = 0; p2p->sd_frag_id = 0; resp = p2p_build_sd_response(dialog_token, WLAN_STATUS_SUCCESS, 1, p2p->srv_update_indic, NULL); } else { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: SD response fits " - "in initial response"); + p2p_dbg(p2p, "SD response fits in initial response"); resp = p2p_build_sd_response(dialog_token, WLAN_STATUS_SUCCESS, 0, p2p->srv_update_indic, resp_tlvs); @@ -391,8 +412,7 @@ if (p2p_send_action(p2p, freq, dst, p2p->cfg->dev_addr, p2p->cfg->dev_addr, wpabuf_head(resp), wpabuf_len(resp), 200) < 0) - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Failed to send Action frame"); + p2p_dbg(p2p, "Failed to send Action frame"); wpabuf_free(resp); } @@ -412,21 +432,18 @@ if (p2p->state != P2P_SD_DURING_FIND || p2p->sd_peer == NULL || os_memcmp(sa, p2p->sd_peer->info.p2p_device_addr, ETH_ALEN) != 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Ignore unexpected GAS Initial Response from " + p2p_dbg(p2p, "Ignore unexpected GAS Initial Response from " MACSTR, MAC2STR(sa)); return; } p2p->cfg->send_action_done(p2p->cfg->cb_ctx); p2p_clear_timeout(p2p); - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Received GAS Initial Response from " MACSTR " (len=%d)", + p2p_dbg(p2p, "Received GAS Initial Response from " MACSTR " (len=%d)", MAC2STR(sa), (int) len); if (len < 5 + 2) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Too short GAS Initial Response frame"); + p2p_dbg(p2p, "Too short GAS Initial Response frame"); return; } @@ -436,20 +453,16 @@ pos += 2; comeback_delay = WPA_GET_LE16(pos); pos += 2; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: dialog_token=%u status_code=%u comeback_delay=%u", + p2p_dbg(p2p, "dialog_token=%u status_code=%u comeback_delay=%u", dialog_token, status_code, comeback_delay); if (status_code) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Service Discovery failed: status code %u", + p2p_dbg(p2p, "Service Discovery failed: status code %u", status_code); return; } if (*pos != WLAN_EID_ADV_PROTO) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Unexpected IE in GAS Initial Response: %u", - *pos); + p2p_dbg(p2p, "Unexpected IE in GAS Initial Response: %u", *pos); return; } pos++; @@ -457,15 +470,13 @@ slen = *pos++; next = pos + slen; if (next > end || slen < 2) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Invalid IE in GAS Initial Response"); + p2p_dbg(p2p, "Invalid IE in GAS Initial Response"); return; } pos++; /* skip QueryRespLenLimit and PAME-BI */ if (*pos != ACCESS_NETWORK_QUERY_PROTOCOL) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Unsupported GAS advertisement protocol id %u", + p2p_dbg(p2p, "Unsupported GAS advertisement protocol id %u", *pos); return; } @@ -473,27 +484,22 @@ pos = next; /* Query Response */ if (pos + 2 > end) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Too short Query " - "Response"); + p2p_dbg(p2p, "Too short Query Response"); return; } slen = WPA_GET_LE16(pos); pos += 2; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Query Response Length: %d", - slen); + p2p_dbg(p2p, "Query Response Length: %d", slen); if (pos + slen > end) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Not enough Query " - "Response data"); + p2p_dbg(p2p, "Not enough Query Response data"); return; } end = pos + slen; if (comeback_delay) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Fragmented " - "response - request fragments"); + p2p_dbg(p2p, "Fragmented response - request fragments"); if (p2p->sd_rx_resp) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Drop " - "old SD reassembly buffer"); + p2p_dbg(p2p, "Drop old SD reassembly buffer"); wpabuf_free(p2p->sd_rx_resp); p2p->sd_rx_resp = NULL; } @@ -505,8 +511,7 @@ if (pos + 4 > end) return; if (WPA_GET_LE16(pos) != ANQP_VENDOR_SPECIFIC) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Unsupported ANQP Info ID %u", WPA_GET_LE16(pos)); + p2p_dbg(p2p, "Unsupported ANQP Info ID %u", WPA_GET_LE16(pos)); return; } pos += 2; @@ -514,21 +519,18 @@ slen = WPA_GET_LE16(pos); pos += 2; if (pos + slen > end || slen < 3 + 1) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Invalid ANQP Query Response length"); + p2p_dbg(p2p, "Invalid ANQP Query Response length"); return; } if (WPA_GET_BE24(pos) != OUI_WFA) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Unsupported ANQP OUI %06x", WPA_GET_BE24(pos)); + p2p_dbg(p2p, "Unsupported ANQP OUI %06x", WPA_GET_BE24(pos)); return; } pos += 3; if (*pos != P2P_OUI_TYPE) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Unsupported ANQP vendor type %u", *pos); + p2p_dbg(p2p, "Unsupported ANQP vendor type %u", *pos); return; } pos++; @@ -536,8 +538,7 @@ if (pos + 2 > end) return; update_indic = WPA_GET_LE16(pos); - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Service Update Indicator: %u", update_indic); + p2p_dbg(p2p, "Service Update Indicator: %u", update_indic); pos += 2; p2p->sd_peer->flags |= P2P_DEV_SD_INFO; @@ -547,8 +548,7 @@ if (p2p->sd_query) { if (!p2p->sd_query->for_all_peers) { struct p2p_sd_query *q; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Remove completed SD query %p", + p2p_dbg(p2p, "Remove completed SD query %p", p2p->sd_query); q = p2p->sd_query; p2p_unlink_sd_query(p2p, p2p->sd_query); @@ -576,22 +576,20 @@ if (len < 1) return; dialog_token = *data; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Dialog Token: %u", - dialog_token); + p2p_dbg(p2p, "Dialog Token: %u", dialog_token); if (dialog_token != p2p->sd_resp_dialog_token) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: No pending SD " - "response fragment for dialog token %u", dialog_token); + p2p_dbg(p2p, "No pending SD response fragment for dialog token %u", + dialog_token); return; } if (p2p->sd_resp == NULL) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: No pending SD " - "response fragment available"); + p2p_dbg(p2p, "No pending SD response fragment available"); return; } if (os_memcmp(sa, p2p->sd_resp_addr, ETH_ALEN) != 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: No pending SD " - "response fragment for " MACSTR, MAC2STR(sa)); + p2p_dbg(p2p, "No pending SD response fragment for " MACSTR, + MAC2STR(sa)); return; } @@ -608,19 +606,16 @@ wpabuf_len(p2p->sd_resp)); if (resp == NULL) return; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Send GAS Comeback " - "Response (frag_id %d more=%d frag_len=%d)", + p2p_dbg(p2p, "Send GAS Comeback Response (frag_id %d more=%d frag_len=%d)", p2p->sd_frag_id, more, (int) frag_len); p2p->sd_frag_id++; p2p->sd_resp_pos += frag_len; if (more) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: %d more bytes " - "remain to be sent", + p2p_dbg(p2p, "%d more bytes remain to be sent", (int) (wpabuf_len(p2p->sd_resp) - p2p->sd_resp_pos)); } else { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: All fragments of " - "SD response sent"); + p2p_dbg(p2p, "All fragments of SD response sent"); wpabuf_free(p2p->sd_resp); p2p->sd_resp = NULL; } @@ -629,8 +624,7 @@ if (p2p_send_action(p2p, rx_freq, sa, p2p->cfg->dev_addr, p2p->cfg->dev_addr, wpabuf_head(resp), wpabuf_len(resp), 200) < 0) - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Failed to send Action frame"); + p2p_dbg(p2p, "Failed to send Action frame"); wpabuf_free(resp); } @@ -653,21 +647,18 @@ if (p2p->state != P2P_SD_DURING_FIND || p2p->sd_peer == NULL || os_memcmp(sa, p2p->sd_peer->info.p2p_device_addr, ETH_ALEN) != 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Ignore unexpected GAS Comeback Response from " + p2p_dbg(p2p, "Ignore unexpected GAS Comeback Response from " MACSTR, MAC2STR(sa)); return; } p2p->cfg->send_action_done(p2p->cfg->cb_ctx); p2p_clear_timeout(p2p); - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Received GAS Comeback Response from " MACSTR " (len=%d)", + p2p_dbg(p2p, "Received GAS Comeback Response from " MACSTR " (len=%d)", MAC2STR(sa), (int) len); if (len < 6 + 2) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Too short GAS Comeback Response frame"); + p2p_dbg(p2p, "Too short GAS Comeback Response frame"); return; } @@ -680,22 +671,19 @@ pos++; comeback_delay = WPA_GET_LE16(pos); pos += 2; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: dialog_token=%u status_code=%u frag_id=%d more_frags=%d " + p2p_dbg(p2p, "dialog_token=%u status_code=%u frag_id=%d more_frags=%d " "comeback_delay=%u", dialog_token, status_code, frag_id, more_frags, comeback_delay); /* TODO: check frag_id match */ if (status_code) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Service Discovery failed: status code %u", + p2p_dbg(p2p, "Service Discovery failed: status code %u", status_code); return; } if (*pos != WLAN_EID_ADV_PROTO) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Unexpected IE in GAS Comeback Response: %u", + p2p_dbg(p2p, "Unexpected IE in GAS Comeback Response: %u", *pos); return; } @@ -704,15 +692,13 @@ slen = *pos++; next = pos + slen; if (next > end || slen < 2) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Invalid IE in GAS Comeback Response"); + p2p_dbg(p2p, "Invalid IE in GAS Comeback Response"); return; } pos++; /* skip QueryRespLenLimit and PAME-BI */ if (*pos != ACCESS_NETWORK_QUERY_PROTOCOL) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Unsupported GAS advertisement protocol id %u", + p2p_dbg(p2p, "Unsupported GAS advertisement protocol id %u", *pos); return; } @@ -720,22 +706,18 @@ pos = next; /* Query Response */ if (pos + 2 > end) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Too short Query " - "Response"); + p2p_dbg(p2p, "Too short Query Response"); return; } slen = WPA_GET_LE16(pos); pos += 2; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Query Response Length: %d", - slen); + p2p_dbg(p2p, "Query Response Length: %d", slen); if (pos + slen > end) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Not enough Query " - "Response data"); + p2p_dbg(p2p, "Not enough Query Response data"); return; } if (slen == 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: No Query Response " - "data"); + p2p_dbg(p2p, "No Query Response data"); return; } end = pos + slen; @@ -752,34 +734,29 @@ if (pos + 4 > end) return; if (WPA_GET_LE16(pos) != ANQP_VENDOR_SPECIFIC) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Unsupported ANQP Info ID %u", WPA_GET_LE16(pos)); + p2p_dbg(p2p, "Unsupported ANQP Info ID %u", WPA_GET_LE16(pos)); return; } pos += 2; slen = WPA_GET_LE16(pos); pos += 2; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: ANQP Query Response " - "length: %u", slen); + p2p_dbg(p2p, "ANQP Query Response length: %u", slen); if (slen < 3 + 1) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Invalid ANQP Query Response length"); + p2p_dbg(p2p, "Invalid ANQP Query Response length"); return; } if (pos + 4 > end) return; if (WPA_GET_BE24(pos) != OUI_WFA) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Unsupported ANQP OUI %06x", WPA_GET_BE24(pos)); + p2p_dbg(p2p, "Unsupported ANQP OUI %06x", WPA_GET_BE24(pos)); return; } pos += 3; if (*pos != P2P_OUI_TYPE) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Unsupported ANQP vendor type %u", *pos); + p2p_dbg(p2p, "Unsupported ANQP vendor type %u", *pos); return; } pos++; @@ -787,27 +764,23 @@ if (pos + 2 > end) return; p2p->sd_rx_update_indic = WPA_GET_LE16(pos); - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Service Update Indicator: %u", p2p->sd_rx_update_indic); + p2p_dbg(p2p, "Service Update Indicator: %u", p2p->sd_rx_update_indic); pos += 2; skip_nqp_header: if (wpabuf_resize(&p2p->sd_rx_resp, end - pos) < 0) return; wpabuf_put_data(p2p->sd_rx_resp, pos, end - pos); - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Current SD reassembly " - "buffer length: %u", + p2p_dbg(p2p, "Current SD reassembly buffer length: %u", (unsigned int) wpabuf_len(p2p->sd_rx_resp)); if (more_frags) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: More fragments " - "remains"); + p2p_dbg(p2p, "More fragments remains"); /* TODO: what would be a good size limit? */ if (wpabuf_len(p2p->sd_rx_resp) > 64000) { wpabuf_free(p2p->sd_rx_resp); p2p->sd_rx_resp = NULL; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Too long " - "SD response - drop it"); + p2p_dbg(p2p, "Too long SD response - drop it"); return; } p2p_send_gas_comeback_req(p2p, sa, dialog_token, rx_freq); @@ -821,8 +794,7 @@ if (p2p->sd_query) { if (!p2p->sd_query->for_all_peers) { struct p2p_sd_query *q; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Remove completed SD query %p", + p2p_dbg(p2p, "Remove completed SD query %p", p2p->sd_query); q = p2p->sd_query; p2p_unlink_sd_query(p2p, p2p->sd_query); @@ -865,12 +837,31 @@ q->next = p2p->sd_queries; p2p->sd_queries = q; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Added SD Query %p", q); + p2p_dbg(p2p, "Added SD Query %p", q); + + if (dst == NULL) { + struct p2p_device *dev; + dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) + dev->flags &= ~P2P_DEV_SD_INFO; + } return q; } +#ifdef CONFIG_WIFI_DISPLAY +void * p2p_sd_request_wfd(struct p2p_data *p2p, const u8 *dst, + const struct wpabuf *tlvs) +{ + struct p2p_sd_query *q; + q = p2p_sd_request(p2p, dst, tlvs); + if (q) + q->wsd = 1; + return q; +} +#endif /* CONFIG_WIFI_DISPLAY */ + + void p2p_sd_service_update(struct p2p_data *p2p) { p2p->srv_update_indic++; @@ -880,8 +871,7 @@ int p2p_sd_cancel_request(struct p2p_data *p2p, void *req) { if (p2p_unlink_sd_query(p2p, req)) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Cancel pending SD query %p", req); + p2p_dbg(p2p, "Cancel pending SD query %p", req); p2p_free_sd_query(req); return 0; } diff -Nru wpa-1.0/src/p2p/p2p_utils.c wpa-2.1/src/p2p/p2p_utils.c --- wpa-1.0/src/p2p/p2p_utils.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/p2p/p2p_utils.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * P2P - generic helper functions * Copyright (c) 2009, Atheros Communications * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -52,11 +46,17 @@ } -static int p2p_channel_to_freq_j4(int reg_class, int channel) +/** + * p2p_channel_to_freq - Convert channel info to frequency + * @op_class: Operating class + * @channel: Channel number + * Returns: Frequency in MHz or -1 if the specified channel is unknown + */ +int p2p_channel_to_freq(int op_class, int channel) { - /* Table J-4 in P802.11REVmb/D4.0 - Global operating classes */ - /* TODO: more regulatory classes */ - switch (reg_class) { + /* Table E-4 in IEEE Std 802.11-2012 - Global operating classes */ + /* TODO: more operating classes */ + switch (op_class) { case 81: /* channels 1..13 */ if (channel < 1 || channel > 13) @@ -94,81 +94,53 @@ if (channel < 149 || channel > 161) return -1; return 5000 + 5 * channel; - } - return -1; -} - - -/** - * p2p_channel_to_freq - Convert channel info to frequency - * @country: Country code - * @reg_class: Regulatory class - * @channel: Channel number - * Returns: Frequency in MHz or -1 if the specified channel is unknown - */ -int p2p_channel_to_freq(const char *country, int reg_class, int channel) -{ - if (country[2] == 0x04) - return p2p_channel_to_freq_j4(reg_class, channel); - - /* These are mainly for backwards compatibility; to be removed */ - switch (reg_class) { - case 1: /* US/1, EU/1, JP/1 = 5 GHz, channels 36,40,44,48 */ - if (channel < 36 || channel > 48) - return -1; - return 5000 + 5 * channel; - case 3: /* US/3 = 5 GHz, channels 149,153,157,161 */ - case 5: /* US/5 = 5 GHz, channels 149,153,157,161 */ - if (channel < 149 || channel > 161) + case 128: /* center freqs 42, 58, 106, 122, 138, 155; 80 MHz */ + if (channel < 36 || channel > 161) return -1; return 5000 + 5 * channel; - case 4: /* EU/4 = 2.407 GHz, channels 1..13 */ - case 12: /* US/12 = 2.407 GHz, channels 1..11 */ - case 30: /* JP/30 = 2.407 GHz, channels 1..13 */ - if (channel < 1 || channel > 13) - return -1; - return 2407 + 5 * channel; - case 31: /* JP/31 = 2.414 GHz, channel 14 */ - if (channel != 14) - return -1; - return 2414 + 5 * channel; } - return -1; } /** * p2p_freq_to_channel - Convert frequency into channel info - * @country: Country code - * @reg_class: Buffer for returning regulatory class + * @op_class: Buffer for returning operating class * @channel: Buffer for returning channel number * Returns: 0 on success, -1 if the specified frequency is unknown */ -int p2p_freq_to_channel(const char *country, unsigned int freq, u8 *reg_class, - u8 *channel) +int p2p_freq_to_channel(unsigned int freq, u8 *op_class, u8 *channel) { /* TODO: more operating classes */ if (freq >= 2412 && freq <= 2472) { - *reg_class = 81; /* 2.407 GHz, channels 1..13 */ + if ((freq - 2407) % 5) + return -1; + + *op_class = 81; /* 2.407 GHz, channels 1..13 */ *channel = (freq - 2407) / 5; return 0; } if (freq == 2484) { - *reg_class = 82; /* channel 14 */ + *op_class = 82; /* channel 14 */ *channel = 14; return 0; } if (freq >= 5180 && freq <= 5240) { - *reg_class = 115; /* 5 GHz, channels 36..48 */ + if ((freq - 5000) % 5) + return -1; + + *op_class = 115; /* 5 GHz, channels 36..48 */ *channel = (freq - 5000) / 5; return 0; } if (freq >= 5745 && freq <= 5805) { - *reg_class = 124; /* 5 GHz, channels 149..161 */ + if ((freq - 5000) % 5) + return -1; + + *op_class = 124; /* 5 GHz, channels 149..161 */ *channel = (freq - 5000) / 5; return 0; } @@ -236,6 +208,105 @@ } +static void p2p_op_class_union(struct p2p_reg_class *cl, + const struct p2p_reg_class *b_cl) +{ + size_t i, j; + + for (i = 0; i < b_cl->channels; i++) { + for (j = 0; j < cl->channels; j++) { + if (b_cl->channel[i] == cl->channel[j]) + break; + } + if (j == cl->channels) { + if (cl->channels == P2P_MAX_REG_CLASS_CHANNELS) + return; + cl->channel[cl->channels++] = b_cl->channel[i]; + } + } +} + + +/** + * p2p_channels_union - Union of channel lists + * @a: First set of channels + * @b: Second set of channels + * @res: Data structure for returning the union of channels + */ +void p2p_channels_union(const struct p2p_channels *a, + const struct p2p_channels *b, + struct p2p_channels *res) +{ + size_t i, j; + + if (a != res) + os_memcpy(res, a, sizeof(*res)); + + for (i = 0; i < res->reg_classes; i++) { + struct p2p_reg_class *cl = &res->reg_class[i]; + for (j = 0; j < b->reg_classes; j++) { + const struct p2p_reg_class *b_cl = &b->reg_class[j]; + if (cl->reg_class != b_cl->reg_class) + continue; + p2p_op_class_union(cl, b_cl); + } + } + + for (j = 0; j < b->reg_classes; j++) { + const struct p2p_reg_class *b_cl = &b->reg_class[j]; + + for (i = 0; i < res->reg_classes; i++) { + struct p2p_reg_class *cl = &res->reg_class[i]; + if (cl->reg_class == b_cl->reg_class) + break; + } + + if (i == res->reg_classes) { + if (res->reg_classes == P2P_MAX_REG_CLASSES) + return; + os_memcpy(&res->reg_class[res->reg_classes++], + b_cl, sizeof(struct p2p_reg_class)); + } + } +} + + +void p2p_channels_remove_freqs(struct p2p_channels *chan, + const struct wpa_freq_range_list *list) +{ + size_t o, c; + + if (list == NULL) + return; + + o = 0; + while (o < chan->reg_classes) { + struct p2p_reg_class *op = &chan->reg_class[o]; + + c = 0; + while (c < op->channels) { + int freq = p2p_channel_to_freq(op->reg_class, + op->channel[c]); + if (freq > 0 && freq_range_list_includes(list, freq)) { + op->channels--; + os_memmove(&op->channel[c], + &op->channel[c + 1], + op->channels - c); + } else + c++; + } + + if (op->channels == 0) { + chan->reg_classes--; + os_memmove(&chan->reg_class[o], &chan->reg_class[o + 1], + (chan->reg_classes - o) * + sizeof(struct p2p_reg_class)); + } else + o++; + } +} + + /** * p2p_channels_includes - Check whether a channel is included in the list * @channels: List of supported channels @@ -260,12 +331,141 @@ } +int p2p_channels_includes_freq(const struct p2p_channels *channels, + unsigned int freq) +{ + size_t i, j; + for (i = 0; i < channels->reg_classes; i++) { + const struct p2p_reg_class *reg = &channels->reg_class[i]; + for (j = 0; j < reg->channels; j++) { + if (p2p_channel_to_freq(reg->reg_class, + reg->channel[j]) == (int) freq) + return 1; + } + } + return 0; +} + + int p2p_supported_freq(struct p2p_data *p2p, unsigned int freq) { u8 op_reg_class, op_channel; - if (p2p_freq_to_channel(p2p->cfg->country, freq, - &op_reg_class, &op_channel) < 0) + if (p2p_freq_to_channel(freq, &op_reg_class, &op_channel) < 0) return 0; return p2p_channels_includes(&p2p->cfg->channels, op_reg_class, op_channel); } + + +int p2p_supported_freq_go(struct p2p_data *p2p, unsigned int freq) +{ + u8 op_reg_class, op_channel; + if (p2p_freq_to_channel(freq, &op_reg_class, &op_channel) < 0) + return 0; + return p2p_channels_includes(&p2p->cfg->channels, op_reg_class, + op_channel) && + !freq_range_list_includes(&p2p->no_go_freq, freq); +} + + +int p2p_supported_freq_cli(struct p2p_data *p2p, unsigned int freq) +{ + u8 op_reg_class, op_channel; + if (p2p_freq_to_channel(freq, &op_reg_class, &op_channel) < 0) + return 0; + return p2p_channels_includes(&p2p->cfg->channels, op_reg_class, + op_channel) || + p2p_channels_includes(&p2p->cfg->cli_channels, op_reg_class, + op_channel); +} + + +unsigned int p2p_get_pref_freq(struct p2p_data *p2p, + const struct p2p_channels *channels) +{ + unsigned int i; + int freq = 0; + + if (channels == NULL) { + if (p2p->cfg->num_pref_chan) { + freq = p2p_channel_to_freq( + p2p->cfg->pref_chan[0].op_class, + p2p->cfg->pref_chan[0].chan); + if (freq < 0) + freq = 0; + } + return freq; + } + + for (i = 0; p2p->cfg->pref_chan && i < p2p->cfg->num_pref_chan; i++) { + freq = p2p_channel_to_freq(p2p->cfg->pref_chan[i].op_class, + p2p->cfg->pref_chan[i].chan); + if (p2p_channels_includes_freq(channels, freq)) + return freq; + } + + return 0; +} + + +void p2p_channels_dump(struct p2p_data *p2p, const char *title, + const struct p2p_channels *chan) +{ + char buf[500], *pos, *end; + size_t i, j; + int ret; + + pos = buf; + end = pos + sizeof(buf); + + for (i = 0; i < chan->reg_classes; i++) { + const struct p2p_reg_class *c; + c = &chan->reg_class[i]; + ret = os_snprintf(pos, end - pos, " %u:", c->reg_class); + if (ret < 0 || ret >= end - pos) + break; + pos += ret; + + for (j = 0; j < c->channels; j++) { + ret = os_snprintf(pos, end - pos, "%s%u", + j == 0 ? "" : ",", + c->channel[j]); + if (ret < 0 || ret >= end - pos) + break; + pos += ret; + } + } + *pos = '\0'; + + p2p_dbg(p2p, "%s:%s", title, buf); +} + + +int p2p_channel_select(struct p2p_channels *chans, const int *classes, + u8 *op_class, u8 *op_channel) +{ + unsigned int i, j, r; + + for (j = 0; classes[j]; j++) { + for (i = 0; i < chans->reg_classes; i++) { + struct p2p_reg_class *c = &chans->reg_class[i]; + + if (c->channels == 0) + continue; + + if (c->reg_class == classes[j]) { + /* + * Pick one of the available channels in the + * operating class at random. + */ + os_get_random((u8 *) &r, sizeof(r)); + r %= c->channels; + *op_class = c->reg_class; + *op_channel = c->channel[r]; + return 0; + } + } + } + + return -1; +} diff -Nru wpa-1.0/src/radius/Makefile wpa-2.1/src/radius/Makefile --- wpa-1.0/src/radius/Makefile 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/radius/Makefile 2014-02-04 11:23:35.000000000 +0000 @@ -1,7 +1,7 @@ all: libradius.a clean: - rm -f *~ *.o *.d libradius.a + rm -f *~ *.o *.d *.gcno *.gcda *.gcov libradius.a install: @echo Nothing to be made. diff -Nru wpa-1.0/src/radius/radius.c wpa-2.1/src/radius/radius.c --- wpa-1.0/src/radius/radius.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/radius/radius.c 2014-02-04 11:23:35.000000000 +0000 @@ -1,15 +1,9 @@ /* * RADIUS message processing - * Copyright (c) 2002-2009, Jouni Malinen + * Copyright (c) 2002-2009, 2011-2012, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "utils/includes.h" @@ -84,8 +78,8 @@ static int radius_msg_initialize(struct radius_msg *msg) { - msg->attr_pos = - os_zalloc(RADIUS_DEFAULT_ATTR_COUNT * sizeof(*msg->attr_pos)); + msg->attr_pos = os_calloc(RADIUS_DEFAULT_ATTR_COUNT, + sizeof(*msg->attr_pos)); if (msg->attr_pos == NULL) return -1; @@ -153,6 +147,12 @@ case RADIUS_CODE_STATUS_SERVER: return "Status-Server"; case RADIUS_CODE_STATUS_CLIENT: return "Status-Client"; case RADIUS_CODE_RESERVED: return "Reserved"; + case RADIUS_CODE_DISCONNECT_REQUEST: return "Disconnect-Request"; + case RADIUS_CODE_DISCONNECT_ACK: return "Disconnect-ACK"; + case RADIUS_CODE_DISCONNECT_NAK: return "Disconnect-NAK"; + case RADIUS_CODE_COA_REQUEST: return "CoA-Request"; + case RADIUS_CODE_COA_ACK: return "CoA-ACK"; + case RADIUS_CODE_COA_NAK: return "CoA-NAK"; default: return "?Unknown?"; } } @@ -218,6 +218,8 @@ { RADIUS_ATTR_TUNNEL_TYPE, "Tunnel-Type", RADIUS_ATTR_HEXDUMP }, { RADIUS_ATTR_TUNNEL_MEDIUM_TYPE, "Tunnel-Medium-Type", RADIUS_ATTR_HEXDUMP }, + { RADIUS_ATTR_TUNNEL_PASSWORD, "Tunnel-Password", + RADIUS_ATTR_UNDIST }, { RADIUS_ATTR_CONNECT_INFO, "Connect-Info", RADIUS_ATTR_TEXT }, { RADIUS_ATTR_EAP_MESSAGE, "EAP-Message", RADIUS_ATTR_UNDIST }, { RADIUS_ATTR_MESSAGE_AUTHENTICATOR, "Message-Authenticator", @@ -226,11 +228,12 @@ RADIUS_ATTR_HEXDUMP }, { RADIUS_ATTR_ACCT_INTERIM_INTERVAL, "Acct-Interim-Interval", RADIUS_ATTR_INT32 }, - { RADIUS_ATTR_CHARGEABLE_USER_IDENTITY, "Chargable-User-Identity", + { RADIUS_ATTR_CHARGEABLE_USER_IDENTITY, "Chargeable-User-Identity", RADIUS_ATTR_TEXT }, { RADIUS_ATTR_NAS_IPV6_ADDRESS, "NAS-IPv6-Address", RADIUS_ATTR_IPV6 }, + { RADIUS_ATTR_ERROR_CAUSE, "Error-Cause", RADIUS_ATTR_INT32 } }; -#define RADIUS_ATTRS (sizeof(radius_attrs) / sizeof(radius_attrs[0])) +#define RADIUS_ATTRS ARRAY_SIZE(radius_attrs) static struct radius_attr_type *radius_get_attr_type(u8 type) @@ -266,7 +269,7 @@ printf(" Attribute %d (%s) length=%d\n", hdr->type, attr ? attr->name : "?Unknown?", hdr->length); - if (attr == NULL) + if (attr == NULL || hdr->length < sizeof(struct radius_attr_hdr)) return; len = hdr->length - sizeof(struct radius_attr_hdr); @@ -329,7 +332,7 @@ printf("RADIUS message: code=%d (%s) identifier=%d length=%d\n", msg->hdr->code, radius_code_string(msg->hdr->code), - msg->hdr->identifier, ntohs(msg->hdr->length)); + msg->hdr->identifier, be_to_host16(msg->hdr->length)); for (i = 0; i < msg->attr_used; i++) { struct radius_attr_hdr *attr = radius_get_attr_hdr(msg, i); @@ -354,11 +357,11 @@ "Message-Authenticator"); return -1; } - msg->hdr->length = htons(wpabuf_len(msg->buf)); + msg->hdr->length = host_to_be16(wpabuf_len(msg->buf)); hmac_md5(secret, secret_len, wpabuf_head(msg->buf), wpabuf_len(msg->buf), (u8 *) (attr + 1)); } else - msg->hdr->length = htons(wpabuf_len(msg->buf)); + msg->hdr->length = host_to_be16(wpabuf_len(msg->buf)); if (wpabuf_len(msg->buf) > 0xffff) { wpa_printf(MSG_WARNING, "RADIUS: Too long message (%lu)", @@ -384,7 +387,7 @@ printf("WARNING: Could not add Message-Authenticator\n"); return -1; } - msg->hdr->length = htons(wpabuf_len(msg->buf)); + msg->hdr->length = host_to_be16(wpabuf_len(msg->buf)); os_memcpy(msg->hdr->authenticator, req_authenticator, sizeof(msg->hdr->authenticator)); hmac_md5(secret, secret_len, wpabuf_head(msg->buf), @@ -410,13 +413,52 @@ } +int radius_msg_finish_das_resp(struct radius_msg *msg, const u8 *secret, + size_t secret_len, + const struct radius_hdr *req_hdr) +{ + const u8 *addr[2]; + size_t len[2]; + u8 auth[MD5_MAC_LEN]; + struct radius_attr_hdr *attr; + + os_memset(auth, 0, MD5_MAC_LEN); + attr = radius_msg_add_attr(msg, RADIUS_ATTR_MESSAGE_AUTHENTICATOR, + auth, MD5_MAC_LEN); + if (attr == NULL) { + wpa_printf(MSG_WARNING, "Could not add Message-Authenticator"); + return -1; + } + + msg->hdr->length = host_to_be16(wpabuf_len(msg->buf)); + os_memcpy(msg->hdr->authenticator, req_hdr->authenticator, 16); + hmac_md5(secret, secret_len, wpabuf_head(msg->buf), + wpabuf_len(msg->buf), (u8 *) (attr + 1)); + + /* ResponseAuth = MD5(Code+ID+Length+RequestAuth+Attributes+Secret) */ + addr[0] = wpabuf_head_u8(msg->buf); + len[0] = wpabuf_len(msg->buf); + addr[1] = secret; + len[1] = secret_len; + if (md5_vector(2, addr, len, msg->hdr->authenticator) < 0) + return -1; + + if (wpabuf_len(msg->buf) > 0xffff) { + wpa_printf(MSG_WARNING, "RADIUS: Too long message (%lu)", + (unsigned long) wpabuf_len(msg->buf)); + return -1; + } + return 0; +} + + void radius_msg_finish_acct(struct radius_msg *msg, const u8 *secret, size_t secret_len) { const u8 *addr[2]; size_t len[2]; - msg->hdr->length = htons(wpabuf_len(msg->buf)); + msg->hdr->length = host_to_be16(wpabuf_len(msg->buf)); os_memset(msg->hdr->authenticator, 0, MD5_MAC_LEN); addr[0] = wpabuf_head(msg->buf); len[0] = wpabuf_len(msg->buf); @@ -431,6 +473,88 @@ } +int radius_msg_verify_acct_req(struct radius_msg *msg, const u8 *secret, + size_t secret_len) +{ + const u8 *addr[4]; + size_t len[4]; + u8 zero[MD5_MAC_LEN]; + u8 hash[MD5_MAC_LEN]; + + os_memset(zero, 0, sizeof(zero)); + addr[0] = (u8 *) msg->hdr; + len[0] = sizeof(struct radius_hdr) - MD5_MAC_LEN; + addr[1] = zero; + len[1] = MD5_MAC_LEN; + addr[2] = (u8 *) (msg->hdr + 1); + len[2] = wpabuf_len(msg->buf) - sizeof(struct radius_hdr); + addr[3] = secret; + len[3] = secret_len; + md5_vector(4, addr, len, hash); + return os_memcmp(msg->hdr->authenticator, hash, MD5_MAC_LEN) != 0; +} + + +int radius_msg_verify_das_req(struct radius_msg *msg, const u8 *secret, + size_t secret_len) +{ + const u8 *addr[4]; + size_t len[4]; + u8 zero[MD5_MAC_LEN]; + u8 hash[MD5_MAC_LEN]; + u8 auth[MD5_MAC_LEN], orig[MD5_MAC_LEN]; + u8 orig_authenticator[16]; + + struct radius_attr_hdr *attr = NULL, *tmp; + size_t i; + + os_memset(zero, 0, sizeof(zero)); + addr[0] = (u8 *) msg->hdr; + len[0] = sizeof(struct radius_hdr) - MD5_MAC_LEN; + addr[1] = zero; + len[1] = MD5_MAC_LEN; + addr[2] = (u8 *) (msg->hdr + 1); + len[2] = wpabuf_len(msg->buf) - sizeof(struct radius_hdr); + addr[3] = secret; + len[3] = secret_len; + md5_vector(4, addr, len, hash); + if (os_memcmp(msg->hdr->authenticator, hash, MD5_MAC_LEN) != 0) + return 1; + + for (i = 0; i < msg->attr_used; i++) { + tmp = radius_get_attr_hdr(msg, i); + if (tmp->type == RADIUS_ATTR_MESSAGE_AUTHENTICATOR) { + if (attr != NULL) { + wpa_printf(MSG_WARNING, "Multiple " + "Message-Authenticator attributes " + "in RADIUS message"); + return 1; + } + attr = tmp; + } + } + + if (attr == NULL) { + /* Message-Authenticator is MAY; not required */ + return 0; + } + + os_memcpy(orig, attr + 1, MD5_MAC_LEN); + os_memset(attr + 1, 0, MD5_MAC_LEN); + os_memcpy(orig_authenticator, msg->hdr->authenticator, + sizeof(orig_authenticator)); + os_memset(msg->hdr->authenticator, 0, + sizeof(msg->hdr->authenticator)); + hmac_md5(secret, secret_len, wpabuf_head(msg->buf), + wpabuf_len(msg->buf), auth); + os_memcpy(attr + 1, orig, MD5_MAC_LEN); + os_memcpy(msg->hdr->authenticator, orig_authenticator, + sizeof(orig_authenticator)); + + return os_memcmp(orig, auth, MD5_MAC_LEN) != 0; +} + + static int radius_msg_add_attr_to_array(struct radius_msg *msg, struct radius_attr_hdr *attr) { @@ -438,8 +562,8 @@ size_t *nattr_pos; int nlen = msg->attr_size * 2; - nattr_pos = os_realloc(msg->attr_pos, - nlen * sizeof(*msg->attr_pos)); + nattr_pos = os_realloc_array(msg->attr_pos, nlen, + sizeof(*msg->attr_pos)); if (nattr_pos == NULL) return -1; @@ -509,7 +633,7 @@ hdr = (struct radius_hdr *) data; - msg_len = ntohs(hdr->length); + msg_len = be_to_host16(hdr->length); if (msg_len < sizeof(*hdr) || msg_len > len) { wpa_printf(MSG_INFO, "RADIUS: Invalid message length"); return NULL; @@ -583,9 +707,9 @@ } -u8 *radius_msg_get_eap(struct radius_msg *msg, size_t *eap_len) +struct wpabuf * radius_msg_get_eap(struct radius_msg *msg) { - u8 *eap, *pos; + struct wpabuf *eap; size_t len, i; struct radius_attr_hdr *attr; @@ -595,30 +719,27 @@ len = 0; for (i = 0; i < msg->attr_used; i++) { attr = radius_get_attr_hdr(msg, i); - if (attr->type == RADIUS_ATTR_EAP_MESSAGE) + if (attr->type == RADIUS_ATTR_EAP_MESSAGE && + attr->length > sizeof(struct radius_attr_hdr)) len += attr->length - sizeof(struct radius_attr_hdr); } if (len == 0) return NULL; - eap = os_malloc(len); + eap = wpabuf_alloc(len); if (eap == NULL) return NULL; - pos = eap; for (i = 0; i < msg->attr_used; i++) { attr = radius_get_attr_hdr(msg, i); - if (attr->type == RADIUS_ATTR_EAP_MESSAGE) { + if (attr->type == RADIUS_ATTR_EAP_MESSAGE && + attr->length > sizeof(struct radius_attr_hdr)) { int flen = attr->length - sizeof(*attr); - os_memcpy(pos, attr + 1, flen); - pos += flen; + wpabuf_put_data(eap, attr + 1, flen); } } - if (eap_len) - *eap_len = len; - return eap; } @@ -719,7 +840,7 @@ for (i = 0; i < src->attr_used; i++) { attr = radius_get_attr_hdr(src, i); - if (attr->type == type) { + if (attr->type == type && attr->length >= sizeof(*attr)) { if (!radius_msg_add_attr(dst, type, (u8 *) (attr + 1), attr->length - sizeof(*attr))) return -1; @@ -776,7 +897,8 @@ u32 vendor_id; struct radius_attr_vendor *vhdr; - if (attr->type != RADIUS_ATTR_VENDOR_SPECIFIC) + if (attr->type != RADIUS_ATTR_VENDOR_SPECIFIC || + attr->length < sizeof(*attr)) continue; left = attr->length - sizeof(*attr); @@ -1149,7 +1271,7 @@ } } - if (!attr) + if (!attr || attr->length < sizeof(*attr)) return -1; dlen = attr->length - sizeof(*attr); @@ -1174,7 +1296,7 @@ } } - if (!attr) + if (!attr || attr->length < sizeof(*attr)) return -1; *buf = (u8 *) (attr + 1); @@ -1225,6 +1347,8 @@ for (i = 0; i < msg->attr_used; i++) { attr = radius_get_attr_hdr(msg, i); + if (attr->length < sizeof(*attr)) + return -1; data = (const u8 *) (attr + 1); dlen = attr->length - sizeof(*attr); if (attr->length < 3) @@ -1275,6 +1399,123 @@ } +/** + * radius_msg_get_tunnel_password - Parse RADIUS attribute Tunnel-Password + * @msg: Received RADIUS message + * @keylen: Length of returned password + * @secret: RADIUS shared secret + * @secret_len: Length of secret + * @sent_msg: Sent RADIUS message + * @n: Number of password attribute to return (starting with 0) + * Returns: Pointer to n-th password (free with os_free) or %NULL + */ +char * radius_msg_get_tunnel_password(struct radius_msg *msg, int *keylen, + const u8 *secret, size_t secret_len, + struct radius_msg *sent_msg, size_t n) +{ + u8 *buf = NULL; + size_t buflen; + const u8 *salt; + u8 *str; + const u8 *addr[3]; + size_t len[3]; + u8 hash[16]; + u8 *pos; + size_t i, j = 0; + struct radius_attr_hdr *attr; + const u8 *data; + size_t dlen; + const u8 *fdata = NULL; /* points to found item */ + size_t fdlen = -1; + char *ret = NULL; + + /* find n-th valid Tunnel-Password attribute */ + for (i = 0; i < msg->attr_used; i++) { + attr = radius_get_attr_hdr(msg, i); + if (attr == NULL || + attr->type != RADIUS_ATTR_TUNNEL_PASSWORD) { + continue; + } + if (attr->length <= 5) + continue; + data = (const u8 *) (attr + 1); + dlen = attr->length - sizeof(*attr); + if (dlen <= 3 || dlen % 16 != 3) + continue; + j++; + if (j <= n) + continue; + + fdata = data; + fdlen = dlen; + break; + } + if (fdata == NULL) + goto out; + + /* alloc writable memory for decryption */ + buf = os_malloc(fdlen); + if (buf == NULL) + goto out; + os_memcpy(buf, fdata, fdlen); + buflen = fdlen; + + /* init pointers */ + salt = buf + 1; + str = buf + 3; + + /* decrypt blocks */ + pos = buf + buflen - 16; /* last block */ + while (pos >= str + 16) { /* all but the first block */ + addr[0] = secret; + len[0] = secret_len; + addr[1] = pos - 16; + len[1] = 16; + md5_vector(2, addr, len, hash); + + for (i = 0; i < 16; i++) + pos[i] ^= hash[i]; + + pos -= 16; + } + + /* decrypt first block */ + if (str != pos) + goto out; + addr[0] = secret; + len[0] = secret_len; + addr[1] = sent_msg->hdr->authenticator; + len[1] = 16; + addr[2] = salt; + len[2] = 2; + md5_vector(3, addr, len, hash); + + for (i = 0; i < 16; i++) + pos[i] ^= hash[i]; + + /* derive plaintext length from first subfield */ + *keylen = (unsigned char) str[0]; + if ((u8 *) (str + *keylen) >= (u8 *) (buf + buflen)) { + /* decryption error - invalid key length */ + goto out; + } + if (*keylen == 0) { + /* empty password */ + goto out; + } + + /* copy passphrase into new buffer */ + ret = os_malloc(*keylen); + if (ret) + os_memcpy(ret, str + 1, *keylen); + +out: + /* return new buffer */ + os_free(buf); + return ret; +} + + void radius_free_class(struct radius_class_data *c) { size_t i; @@ -1296,7 +1537,7 @@ if (src->attr == NULL) return 0; - dst->attr = os_zalloc(src->count * sizeof(struct radius_attr_data)); + dst->attr = os_calloc(src->count, sizeof(struct radius_attr_data)); if (dst->attr == NULL) return -1; @@ -1313,4 +1554,25 @@ } return 0; +} + + +u8 radius_msg_find_unlisted_attr(struct radius_msg *msg, u8 *attrs) +{ + size_t i, j; + struct radius_attr_hdr *attr; + + for (i = 0; i < msg->attr_used; i++) { + attr = radius_get_attr_hdr(msg, i); + + for (j = 0; attrs[j]; j++) { + if (attr->type == attrs[j]) + break; + } + + if (attrs[j] == 0) + return attr->type; /* unlisted attr */ + } + + return 0; } diff -Nru wpa-1.0/src/radius/radius_client.c wpa-2.1/src/radius/radius_client.c --- wpa-1.0/src/radius/radius_client.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/radius/radius_client.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * RADIUS client * Copyright (c) 2002-2009, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -128,7 +122,7 @@ /** * last_attempt - Time of the last transmission attempt */ - struct os_time last_attempt; + struct os_reltime last_attempt; /** * shared_secret - Shared secret with the target RADIUS server @@ -287,8 +281,8 @@ num = &radius->num_auth_handlers; } - newh = os_realloc(*handlers, - (*num + 1) * sizeof(struct radius_rx_handler)); + newh = os_realloc_array(*handlers, *num + 1, + sizeof(struct radius_rx_handler)); if (newh == NULL) return -1; @@ -306,7 +300,7 @@ { #ifndef CONFIG_NATIVE_WINDOWS int _errno = errno; - perror("send[RADIUS]"); + wpa_printf(MSG_INFO, "send[RADIUS]: %s", strerror(errno)); if (_errno == ENOTCONN || _errno == EDESTADDRREQ || _errno == EINVAL || _errno == EBADF) { hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, @@ -357,7 +351,7 @@ HOSTAPD_LEVEL_DEBUG, "Resending RADIUS message (id=%d)", radius_msg_get_hdr(entry->msg)->identifier); - os_get_time(&entry->last_attempt); + os_get_reltime(&entry->last_attempt); buf = radius_msg_get_buf(entry->msg); if (send(s, wpabuf_head(buf), wpabuf_len(buf), 0) < 0) radius_client_handle_send_error(radius, s, entry->msg_type); @@ -367,8 +361,7 @@ if (entry->next_wait > RADIUS_CLIENT_MAX_WAIT) entry->next_wait = RADIUS_CLIENT_MAX_WAIT; if (entry->attempts >= RADIUS_CLIENT_MAX_RETRIES) { - printf("Removing un-ACKed RADIUS message due to too many " - "failed retransmit attempts\n"); + wpa_printf(MSG_INFO, "RADIUS: Removing un-ACKed message due to too many failed retransmit attempts"); return 1; } @@ -380,7 +373,7 @@ { struct radius_client_data *radius = eloop_ctx; struct hostapd_radius_servers *conf = radius->conf; - struct os_time now; + struct os_reltime now; os_time_t first; struct radius_msg_list *entry, *prev, *tmp; int auth_failover = 0, acct_failover = 0; @@ -390,7 +383,7 @@ if (!entry) return; - os_get_time(&now); + os_get_reltime(&now); first = 0; prev = NULL; @@ -488,7 +481,7 @@ static void radius_client_update_timeout(struct radius_client_data *radius) { - struct os_time now; + struct os_reltime now; os_time_t first; struct radius_msg_list *entry; @@ -504,14 +497,14 @@ first = entry->next_try; } - os_get_time(&now); + os_get_reltime(&now); if (first < now.sec) first = now.sec; eloop_register_timeout(first - now.sec, 0, radius_client_timer, radius, NULL); hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, HOSTAPD_LEVEL_DEBUG, "Next RADIUS client retransmit in" - " %ld seconds\n", (long int) (first - now.sec)); + " %ld seconds", (long int) (first - now.sec)); } @@ -532,7 +525,7 @@ entry = os_zalloc(sizeof(*entry)); if (entry == NULL) { - printf("Failed to add RADIUS packet into retransmit list\n"); + wpa_printf(MSG_INFO, "RADIUS: Failed to add packet into retransmit list"); radius_msg_free(msg); return; } @@ -543,7 +536,7 @@ entry->msg_type = msg_type; entry->shared_secret = shared_secret; entry->shared_secret_len = shared_secret_len; - os_get_time(&entry->last_attempt); + os_get_reltime(&entry->last_attempt); entry->first_try = entry->last_attempt.sec; entry->next_try = entry->first_try + RADIUS_CLIENT_FIRST_WAIT; entry->attempts = 1; @@ -553,8 +546,7 @@ radius_client_update_timeout(radius); if (radius->num_msgs >= RADIUS_CLIENT_MAX_ENTRIES) { - printf("Removing the oldest un-ACKed RADIUS packet due to " - "retransmit list limits.\n"); + wpa_printf(MSG_INFO, "RADIUS: Removing the oldest un-ACKed packet due to retransmit list limits"); prev = NULL; while (entry->next) { prev = entry; @@ -700,7 +692,7 @@ struct radius_rx_handler *handlers; size_t num_handlers, i; struct radius_msg_list *req, *prev_req; - struct os_time now; + struct os_reltime now; struct hostapd_radius_server *rconf; int invalid_authenticator = 0; @@ -716,21 +708,20 @@ len = recv(sock, buf, sizeof(buf), MSG_DONTWAIT); if (len < 0) { - perror("recv[RADIUS]"); + wpa_printf(MSG_INFO, "recv[RADIUS]: %s", strerror(errno)); return; } hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, HOSTAPD_LEVEL_DEBUG, "Received %d bytes from RADIUS " "server", len); if (len == sizeof(buf)) { - printf("Possibly too long UDP frame for our buffer - " - "dropping it\n"); + wpa_printf(MSG_INFO, "RADIUS: Possibly too long UDP frame for our buffer - dropping it"); return; } msg = radius_msg_parse(buf, len); if (msg == NULL) { - printf("Parsing incoming RADIUS frame failed\n"); + wpa_printf(MSG_INFO, "RADIUS: Parsing incoming frame failed"); rconf->malformed_responses++; return; } @@ -781,7 +772,7 @@ goto fail; } - os_get_time(&now); + os_get_reltime(&now); roundtrip = (now.sec - req->last_attempt.sec) * 100 + (now.usec - req->last_attempt.usec) / 10000; hostapd_logger(radius->ctx, req->addr, HOSTAPD_MODULE_RADIUS, @@ -1047,13 +1038,14 @@ } if (bind(sel_sock, cl_addr, claddrlen) < 0) { - perror("bind[radius]"); + wpa_printf(MSG_INFO, "bind[radius]: %s", + strerror(errno)); return -1; } } if (connect(sel_sock, addr, addrlen) < 0) { - perror("connect[radius]"); + wpa_printf(MSG_INFO, "connect[radius]: %s", strerror(errno)); return -1; } @@ -1129,8 +1121,8 @@ r = setsockopt(s, IPPROTO_IP, IP_MTU_DISCOVER, &action, sizeof(action)); if (r == -1) - wpa_printf(MSG_ERROR, "Failed to set IP_MTU_DISCOVER: " - "%s", strerror(errno)); + wpa_printf(MSG_ERROR, "RADIUS: Failed to set IP_MTU_DISCOVER: %s", + strerror(errno)); #endif return r; } @@ -1143,7 +1135,8 @@ radius->auth_serv_sock = socket(PF_INET, SOCK_DGRAM, 0); if (radius->auth_serv_sock < 0) - perror("socket[PF_INET,SOCK_DGRAM]"); + wpa_printf(MSG_INFO, "RADIUS: socket[PF_INET,SOCK_DGRAM]: %s", + strerror(errno)); else { radius_client_disable_pmtu_discovery(radius->auth_serv_sock); ok++; @@ -1152,7 +1145,8 @@ #ifdef CONFIG_IPV6 radius->auth_serv_sock6 = socket(PF_INET6, SOCK_DGRAM, 0); if (radius->auth_serv_sock6 < 0) - perror("socket[PF_INET6,SOCK_DGRAM]"); + wpa_printf(MSG_INFO, "RADIUS: socket[PF_INET6,SOCK_DGRAM]: %s", + strerror(errno)); else ok++; #endif /* CONFIG_IPV6 */ @@ -1168,8 +1162,7 @@ eloop_register_read_sock(radius->auth_serv_sock, radius_client_receive, radius, (void *) RADIUS_AUTH)) { - printf("Could not register read socket for authentication " - "server\n"); + wpa_printf(MSG_INFO, "RADIUS: Could not register read socket for authentication server"); return -1; } @@ -1178,8 +1171,7 @@ eloop_register_read_sock(radius->auth_serv_sock6, radius_client_receive, radius, (void *) RADIUS_AUTH)) { - printf("Could not register read socket for authentication " - "server\n"); + wpa_printf(MSG_INFO, "RADIUS: Could not register read socket for authentication server"); return -1; } #endif /* CONFIG_IPV6 */ @@ -1195,7 +1187,8 @@ radius->acct_serv_sock = socket(PF_INET, SOCK_DGRAM, 0); if (radius->acct_serv_sock < 0) - perror("socket[PF_INET,SOCK_DGRAM]"); + wpa_printf(MSG_INFO, "RADIUS: socket[PF_INET,SOCK_DGRAM]: %s", + strerror(errno)); else { radius_client_disable_pmtu_discovery(radius->acct_serv_sock); ok++; @@ -1204,7 +1197,8 @@ #ifdef CONFIG_IPV6 radius->acct_serv_sock6 = socket(PF_INET6, SOCK_DGRAM, 0); if (radius->acct_serv_sock6 < 0) - perror("socket[PF_INET6,SOCK_DGRAM]"); + wpa_printf(MSG_INFO, "RADIUS: socket[PF_INET6,SOCK_DGRAM]: %s", + strerror(errno)); else ok++; #endif /* CONFIG_IPV6 */ @@ -1220,8 +1214,7 @@ eloop_register_read_sock(radius->acct_serv_sock, radius_client_receive, radius, (void *) RADIUS_ACCT)) { - printf("Could not register read socket for accounting " - "server\n"); + wpa_printf(MSG_INFO, "RADIUS: Could not register read socket for accounting server"); return -1; } @@ -1230,8 +1223,7 @@ eloop_register_read_sock(radius->acct_serv_sock6, radius_client_receive, radius, (void *) RADIUS_ACCT)) { - printf("Could not register read socket for accounting " - "server\n"); + wpa_printf(MSG_INFO, "RADIUS: Could not register read socket for accounting server"); return -1; } #endif /* CONFIG_IPV6 */ diff -Nru wpa-1.0/src/radius/radius_client.h wpa-2.1/src/radius/radius_client.h --- wpa-1.0/src/radius/radius_client.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/radius/radius_client.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * RADIUS client * Copyright (c) 2002-2009, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef RADIUS_CLIENT_H diff -Nru wpa-1.0/src/radius/radius_das.c wpa-2.1/src/radius/radius_das.c --- wpa-1.0/src/radius/radius_das.c 1970-01-01 00:00:00.000000000 +0000 +++ wpa-2.1/src/radius/radius_das.c 2014-02-04 11:23:35.000000000 +0000 @@ -0,0 +1,362 @@ +/* + * RADIUS Dynamic Authorization Server (DAS) (RFC 5176) + * Copyright (c) 2012-2013, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" +#include + +#include "utils/common.h" +#include "utils/eloop.h" +#include "utils/ip_addr.h" +#include "radius.h" +#include "radius_das.h" + + +struct radius_das_data { + int sock; + u8 *shared_secret; + size_t shared_secret_len; + struct hostapd_ip_addr client_addr; + unsigned int time_window; + int require_event_timestamp; + void *ctx; + enum radius_das_res (*disconnect)(void *ctx, + struct radius_das_attrs *attr); +}; + + +static struct radius_msg * radius_das_disconnect(struct radius_das_data *das, + struct radius_msg *msg, + const char *abuf, + int from_port) +{ + struct radius_hdr *hdr; + struct radius_msg *reply; + u8 allowed[] = { + RADIUS_ATTR_USER_NAME, + RADIUS_ATTR_CALLING_STATION_ID, + RADIUS_ATTR_ACCT_SESSION_ID, + RADIUS_ATTR_EVENT_TIMESTAMP, + RADIUS_ATTR_MESSAGE_AUTHENTICATOR, + RADIUS_ATTR_CHARGEABLE_USER_IDENTITY, + 0 + }; + int error = 405; + u8 attr; + enum radius_das_res res; + struct radius_das_attrs attrs; + u8 *buf; + size_t len; + char tmp[100]; + u8 sta_addr[ETH_ALEN]; + + hdr = radius_msg_get_hdr(msg); + + attr = radius_msg_find_unlisted_attr(msg, allowed); + if (attr) { + wpa_printf(MSG_INFO, "DAS: Unsupported attribute %u in " + "Disconnect-Request from %s:%d", attr, + abuf, from_port); + error = 401; + goto fail; + } + + os_memset(&attrs, 0, sizeof(attrs)); + + if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_CALLING_STATION_ID, + &buf, &len, NULL) == 0) { + if (len >= sizeof(tmp)) + len = sizeof(tmp) - 1; + os_memcpy(tmp, buf, len); + tmp[len] = '\0'; + if (hwaddr_aton2(tmp, sta_addr) < 0) { + wpa_printf(MSG_INFO, "DAS: Invalid Calling-Station-Id " + "'%s' from %s:%d", tmp, abuf, from_port); + error = 407; + goto fail; + } + attrs.sta_addr = sta_addr; + } + + if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_USER_NAME, + &buf, &len, NULL) == 0) { + attrs.user_name = buf; + attrs.user_name_len = len; + } + + if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_ACCT_SESSION_ID, + &buf, &len, NULL) == 0) { + attrs.acct_session_id = buf; + attrs.acct_session_id_len = len; + } + + if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_CHARGEABLE_USER_IDENTITY, + &buf, &len, NULL) == 0) { + attrs.cui = buf; + attrs.cui_len = len; + } + + res = das->disconnect(das->ctx, &attrs); + switch (res) { + case RADIUS_DAS_NAS_MISMATCH: + wpa_printf(MSG_INFO, "DAS: NAS mismatch from %s:%d", + abuf, from_port); + error = 403; + break; + case RADIUS_DAS_SESSION_NOT_FOUND: + wpa_printf(MSG_INFO, "DAS: Session not found for request from " + "%s:%d", abuf, from_port); + error = 503; + break; + case RADIUS_DAS_SUCCESS: + error = 0; + break; + } + +fail: + reply = radius_msg_new(error ? RADIUS_CODE_DISCONNECT_NAK : + RADIUS_CODE_DISCONNECT_ACK, hdr->identifier); + if (reply == NULL) + return NULL; + + if (error) { + if (!radius_msg_add_attr_int32(reply, RADIUS_ATTR_ERROR_CAUSE, + error)) { + radius_msg_free(reply); + return NULL; + } + } + + return reply; +} + + +static void radius_das_receive(int sock, void *eloop_ctx, void *sock_ctx) +{ + struct radius_das_data *das = eloop_ctx; + u8 buf[1500]; + union { + struct sockaddr_storage ss; + struct sockaddr_in sin; +#ifdef CONFIG_IPV6 + struct sockaddr_in6 sin6; +#endif /* CONFIG_IPV6 */ + } from; + char abuf[50]; + int from_port = 0; + socklen_t fromlen; + int len; + struct radius_msg *msg, *reply = NULL; + struct radius_hdr *hdr; + struct wpabuf *rbuf; + u32 val; + int res; + struct os_time now; + + fromlen = sizeof(from); + len = recvfrom(sock, buf, sizeof(buf), 0, + (struct sockaddr *) &from.ss, &fromlen); + if (len < 0) { + wpa_printf(MSG_ERROR, "DAS: recvfrom: %s", strerror(errno)); + return; + } + + os_strlcpy(abuf, inet_ntoa(from.sin.sin_addr), sizeof(abuf)); + from_port = ntohs(from.sin.sin_port); + + wpa_printf(MSG_DEBUG, "DAS: Received %d bytes from %s:%d", + len, abuf, from_port); + if (das->client_addr.u.v4.s_addr != from.sin.sin_addr.s_addr) { + wpa_printf(MSG_DEBUG, "DAS: Drop message from unknown client"); + return; + } + + msg = radius_msg_parse(buf, len); + if (msg == NULL) { + wpa_printf(MSG_DEBUG, "DAS: Parsing incoming RADIUS packet " + "from %s:%d failed", abuf, from_port); + return; + } + + if (wpa_debug_level <= MSG_MSGDUMP) + radius_msg_dump(msg); + + if (radius_msg_verify_das_req(msg, das->shared_secret, + das->shared_secret_len)) { + wpa_printf(MSG_DEBUG, "DAS: Invalid authenticator in packet " + "from %s:%d - drop", abuf, from_port); + goto fail; + } + + os_get_time(&now); + res = radius_msg_get_attr(msg, RADIUS_ATTR_EVENT_TIMESTAMP, + (u8 *) &val, 4); + if (res == 4) { + u32 timestamp = ntohl(val); + if ((unsigned int) abs(now.sec - timestamp) > + das->time_window) { + wpa_printf(MSG_DEBUG, "DAS: Unacceptable " + "Event-Timestamp (%u; local time %u) in " + "packet from %s:%d - drop", + timestamp, (unsigned int) now.sec, + abuf, from_port); + goto fail; + } + } else if (das->require_event_timestamp) { + wpa_printf(MSG_DEBUG, "DAS: Missing Event-Timestamp in packet " + "from %s:%d - drop", abuf, from_port); + goto fail; + } + + hdr = radius_msg_get_hdr(msg); + + switch (hdr->code) { + case RADIUS_CODE_DISCONNECT_REQUEST: + reply = radius_das_disconnect(das, msg, abuf, from_port); + break; + case RADIUS_CODE_COA_REQUEST: + /* TODO */ + reply = radius_msg_new(RADIUS_CODE_COA_NAK, + hdr->identifier); + if (reply == NULL) + break; + + /* Unsupported Service */ + if (!radius_msg_add_attr_int32(reply, RADIUS_ATTR_ERROR_CAUSE, + 405)) { + radius_msg_free(reply); + reply = NULL; + break; + } + break; + default: + wpa_printf(MSG_DEBUG, "DAS: Unexpected RADIUS code %u in " + "packet from %s:%d", + hdr->code, abuf, from_port); + } + + if (reply) { + wpa_printf(MSG_DEBUG, "DAS: Reply to %s:%d", abuf, from_port); + + if (!radius_msg_add_attr_int32(reply, + RADIUS_ATTR_EVENT_TIMESTAMP, + now.sec)) { + wpa_printf(MSG_DEBUG, "DAS: Failed to add " + "Event-Timestamp attribute"); + } + + if (radius_msg_finish_das_resp(reply, das->shared_secret, + das->shared_secret_len, hdr) < + 0) { + wpa_printf(MSG_DEBUG, "DAS: Failed to add " + "Message-Authenticator attribute"); + } + + if (wpa_debug_level <= MSG_MSGDUMP) + radius_msg_dump(reply); + + rbuf = radius_msg_get_buf(reply); + res = sendto(das->sock, wpabuf_head(rbuf), + wpabuf_len(rbuf), 0, + (struct sockaddr *) &from.ss, fromlen); + if (res < 0) { + wpa_printf(MSG_ERROR, "DAS: sendto(to %s:%d): %s", + abuf, from_port, strerror(errno)); + } + } + +fail: + radius_msg_free(msg); + radius_msg_free(reply); +} + + +static int radius_das_open_socket(int port) +{ + int s; + struct sockaddr_in addr; + + s = socket(PF_INET, SOCK_DGRAM, 0); + if (s < 0) { + wpa_printf(MSG_INFO, "RADIUS DAS: socket: %s", strerror(errno)); + return -1; + } + + os_memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(port); + if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + wpa_printf(MSG_INFO, "RADIUS DAS: bind: %s", strerror(errno)); + close(s); + return -1; + } + + return s; +} + + +struct radius_das_data * +radius_das_init(struct radius_das_conf *conf) +{ + struct radius_das_data *das; + + if (conf->port == 0 || conf->shared_secret == NULL || + conf->client_addr == NULL) + return NULL; + + das = os_zalloc(sizeof(*das)); + if (das == NULL) + return NULL; + + das->time_window = conf->time_window; + das->require_event_timestamp = conf->require_event_timestamp; + das->ctx = conf->ctx; + das->disconnect = conf->disconnect; + + os_memcpy(&das->client_addr, conf->client_addr, + sizeof(das->client_addr)); + + das->shared_secret = os_malloc(conf->shared_secret_len); + if (das->shared_secret == NULL) { + radius_das_deinit(das); + return NULL; + } + os_memcpy(das->shared_secret, conf->shared_secret, + conf->shared_secret_len); + das->shared_secret_len = conf->shared_secret_len; + + das->sock = radius_das_open_socket(conf->port); + if (das->sock < 0) { + wpa_printf(MSG_ERROR, "Failed to open UDP socket for RADIUS " + "DAS"); + radius_das_deinit(das); + return NULL; + } + + if (eloop_register_read_sock(das->sock, radius_das_receive, das, NULL)) + { + radius_das_deinit(das); + return NULL; + } + + return das; +} + + +void radius_das_deinit(struct radius_das_data *das) +{ + if (das == NULL) + return; + + if (das->sock >= 0) { + eloop_unregister_read_sock(das->sock); + close(das->sock); + } + + os_free(das->shared_secret); + os_free(das); +} diff -Nru wpa-1.0/src/radius/radius_das.h wpa-2.1/src/radius/radius_das.h --- wpa-1.0/src/radius/radius_das.h 1970-01-01 00:00:00.000000000 +0000 +++ wpa-2.1/src/radius/radius_das.h 2014-02-04 11:23:35.000000000 +0000 @@ -0,0 +1,47 @@ +/* + * RADIUS Dynamic Authorization Server (DAS) + * Copyright (c) 2012, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef RADIUS_DAS_H +#define RADIUS_DAS_H + +struct radius_das_data; + +enum radius_das_res { + RADIUS_DAS_SUCCESS, + RADIUS_DAS_NAS_MISMATCH, + RADIUS_DAS_SESSION_NOT_FOUND +}; + +struct radius_das_attrs { + const u8 *sta_addr; + const u8 *user_name; + size_t user_name_len; + const u8 *acct_session_id; + size_t acct_session_id_len; + const u8 *cui; + size_t cui_len; +}; + +struct radius_das_conf { + int port; + const u8 *shared_secret; + size_t shared_secret_len; + const struct hostapd_ip_addr *client_addr; + unsigned int time_window; + int require_event_timestamp; + void *ctx; + enum radius_das_res (*disconnect)(void *ctx, + struct radius_das_attrs *attr); +}; + +struct radius_das_data * +radius_das_init(struct radius_das_conf *conf); + +void radius_das_deinit(struct radius_das_data *data); + +#endif /* RADIUS_DAS_H */ diff -Nru wpa-1.0/src/radius/radius.h wpa-2.1/src/radius/radius.h --- wpa-1.0/src/radius/radius.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/radius/radius.h 2014-02-04 11:23:35.000000000 +0000 @@ -1,15 +1,9 @@ /* * RADIUS message processing - * Copyright (c) 2002-2009, Jouni Malinen + * Copyright (c) 2002-2009, 2012, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef RADIUS_H @@ -24,7 +18,7 @@ struct radius_hdr { u8 code; u8 identifier; - u16 length; /* including this header */ + be16 length; /* including this header */ u8 authenticator[16]; /* followed by length-20 octets of attributes */ } STRUCT_PACKED; @@ -37,6 +31,12 @@ RADIUS_CODE_ACCESS_CHALLENGE = 11, RADIUS_CODE_STATUS_SERVER = 12, RADIUS_CODE_STATUS_CLIENT = 13, + RADIUS_CODE_DISCONNECT_REQUEST = 40, + RADIUS_CODE_DISCONNECT_ACK = 41, + RADIUS_CODE_DISCONNECT_NAK = 42, + RADIUS_CODE_COA_REQUEST = 43, + RADIUS_CODE_COA_ACK = 44, + RADIUS_CODE_COA_NAK = 45, RADIUS_CODE_RESERVED = 255 }; @@ -82,13 +82,15 @@ RADIUS_ATTR_NAS_PORT_TYPE = 61, RADIUS_ATTR_TUNNEL_TYPE = 64, RADIUS_ATTR_TUNNEL_MEDIUM_TYPE = 65, + RADIUS_ATTR_TUNNEL_PASSWORD = 69, RADIUS_ATTR_CONNECT_INFO = 77, RADIUS_ATTR_EAP_MESSAGE = 79, RADIUS_ATTR_MESSAGE_AUTHENTICATOR = 80, RADIUS_ATTR_TUNNEL_PRIVATE_GROUP_ID = 81, RADIUS_ATTR_ACCT_INTERIM_INTERVAL = 85, RADIUS_ATTR_CHARGEABLE_USER_IDENTITY = 89, - RADIUS_ATTR_NAS_IPV6_ADDRESS = 95 + RADIUS_ATTR_NAS_IPV6_ADDRESS = 95, + RADIUS_ATTR_ERROR_CAUSE = 101 }; @@ -197,14 +199,21 @@ size_t secret_len); int radius_msg_finish_srv(struct radius_msg *msg, const u8 *secret, size_t secret_len, const u8 *req_authenticator); +int radius_msg_finish_das_resp(struct radius_msg *msg, const u8 *secret, + size_t secret_len, + const struct radius_hdr *req_hdr); void radius_msg_finish_acct(struct radius_msg *msg, const u8 *secret, size_t secret_len); +int radius_msg_verify_acct_req(struct radius_msg *msg, const u8 *secret, + size_t secret_len); +int radius_msg_verify_das_req(struct radius_msg *msg, const u8 *secret, + size_t secret_len); struct radius_attr_hdr * radius_msg_add_attr(struct radius_msg *msg, u8 type, const u8 *data, size_t data_len); struct radius_msg * radius_msg_parse(const u8 *data, size_t len); int radius_msg_add_eap(struct radius_msg *msg, const u8 *data, size_t data_len); -u8 *radius_msg_get_eap(struct radius_msg *msg, size_t *len); +struct wpabuf * radius_msg_get_eap(struct radius_msg *msg); int radius_msg_verify(struct radius_msg *msg, const u8 *secret, size_t secret_len, struct radius_msg *sent_msg, int auth); @@ -231,6 +240,9 @@ const u8 *secret, size_t secret_len); int radius_msg_get_attr(struct radius_msg *msg, u8 type, u8 *buf, size_t len); int radius_msg_get_vlanid(struct radius_msg *msg); +char * radius_msg_get_tunnel_password(struct radius_msg *msg, int *keylen, + const u8 *secret, size_t secret_len, + struct radius_msg *sent_msg, size_t n); static inline int radius_msg_add_attr_int32(struct radius_msg *msg, u8 type, u32 value) @@ -270,4 +282,6 @@ int radius_copy_class(struct radius_class_data *dst, const struct radius_class_data *src); +u8 radius_msg_find_unlisted_attr(struct radius_msg *msg, u8 *attrs); + #endif /* RADIUS_H */ diff -Nru wpa-1.0/src/radius/radius_server.c wpa-2.1/src/radius/radius_server.c --- wpa-1.0/src/radius/radius_server.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/radius/radius_server.c 2014-02-04 11:23:35.000000000 +0000 @@ -1,15 +1,9 @@ /* * RADIUS authentication server - * Copyright (c) 2005-2009, Jouni Malinen + * Copyright (c) 2005-2009, 2011, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -229,6 +223,11 @@ u16 pwd_group; /** + * server_id - Server identity + */ + const char *server_id; + + /** * wps - Wi-Fi Protected Setup context * * If WPS is used with an external RADIUS server (which is quite @@ -245,7 +244,7 @@ /** * start_time - Timestamp of server start */ - struct os_time start_time; + struct os_reltime start_time; /** * counters - Statistics counters for server operations @@ -292,10 +291,12 @@ * msg_ctx - Context data for wpa_msg() calls */ void *msg_ctx; -}; +#ifdef CONFIG_RADIUS_TEST + char *dump_msk_file; +#endif /* CONFIG_RADIUS_TEST */ +}; -extern int wpa_debug_level; #define RADIUS_DEBUG(args...) \ wpa_printf(MSG_DEBUG, "RADIUS SRV: " args) @@ -513,6 +514,8 @@ eap_conf.tnc = data->tnc; eap_conf.wps = data->wps; eap_conf.pwd_group = data->pwd_group; + eap_conf.server_id = (const u8 *) data->server_id; + eap_conf.server_id_len = os_strlen(data->server_id); sess->eap = eap_server_sm_init(sess, &radius_server_eapol_cb, &eap_conf); if (sess->eap == NULL) { @@ -574,6 +577,24 @@ if (code == RADIUS_CODE_ACCESS_ACCEPT && sess->eap_if->eapKeyData) { int len; +#ifdef CONFIG_RADIUS_TEST + if (data->dump_msk_file) { + FILE *f; + char buf[2 * 64 + 1]; + f = fopen(data->dump_msk_file, "a"); + if (f) { + len = sess->eap_if->eapKeyDataLen; + if (len > 64) + len = 64; + len = wpa_snprintf_hex( + buf, sizeof(buf), + sess->eap_if->eapKeyData, len); + buf[len] = '\0'; + fprintf(f, "%s\n", buf); + fclose(f); + } + } +#endif /* CONFIG_RADIUS_TEST */ if (sess->eap_if->eapKeyDataLen > 64) { len = 32; } else { @@ -656,7 +677,7 @@ buf = radius_msg_get_buf(msg); if (sendto(data->auth_sock, wpabuf_head(buf), wpabuf_len(buf), 0, (struct sockaddr *) from, sizeof(*from)) < 0) { - perror("sendto[RADIUS SRV]"); + wpa_printf(MSG_INFO, "sendto[RADIUS SRV]: %s", strerror(errno)); ret = -1; } @@ -673,8 +694,7 @@ const char *from_addr, int from_port, struct radius_session *force_sess) { - u8 *eap = NULL; - size_t eap_len; + struct wpabuf *eap = NULL; int res, state_included = 0; u8 statebuf[4]; unsigned int state; @@ -728,7 +748,8 @@ wpabuf_len(buf), 0, (struct sockaddr *) from, fromlen); if (res < 0) { - perror("sendto[RADIUS SRV]"); + wpa_printf(MSG_INFO, "sendto[RADIUS SRV]: %s", + strerror(errno)); } return 0; } @@ -738,7 +759,7 @@ return -1; } - eap = radius_msg_get_eap(msg, &eap_len); + eap = radius_msg_get_eap(msg); if (eap == NULL) { RADIUS_DEBUG("No EAP-Message in RADIUS packet from %s", from_addr); @@ -747,7 +768,7 @@ return -1; } - RADIUS_DUMP("Received EAP data", eap, eap_len); + RADIUS_DUMP("Received EAP data", wpabuf_head(eap), wpabuf_len(eap)); /* FIX: if Code is Request, Success, or Failure, send Access-Reject; * RFC3579 Sect. 2.6.2. @@ -757,10 +778,7 @@ * Or is this already done by the EAP state machine? */ wpabuf_free(sess->eap_if->eapRespData); - sess->eap_if->eapRespData = wpabuf_alloc_ext_data(eap, eap_len); - if (sess->eap_if->eapRespData == NULL) - os_free(eap); - eap = NULL; + sess->eap_if->eapRespData = eap; sess->eap_if->eapResp = TRUE; eap_server_sm_step(sess->eap); @@ -823,7 +841,8 @@ wpabuf_len(buf), 0, (struct sockaddr *) from, fromlen); if (res < 0) { - perror("sendto[RADIUS SRV]"); + wpa_printf(MSG_INFO, "sendto[RADIUS SRV]: %s", + strerror(errno)); } radius_msg_free(sess->last_reply); sess->last_reply = reply; @@ -878,7 +897,8 @@ len = recvfrom(sock, buf, RADIUS_MAX_MSG_LEN, 0, (struct sockaddr *) &from.ss, &fromlen); if (len < 0) { - perror("recvfrom[radius_server]"); + wpa_printf(MSG_INFO, "recvfrom[radius_server]: %s", + strerror(errno)); goto fail; } @@ -982,7 +1002,7 @@ s = socket(PF_INET, SOCK_DGRAM, 0); if (s < 0) { - perror("socket"); + wpa_printf(MSG_INFO, "RADIUS: socket: %s", strerror(errno)); return -1; } @@ -992,7 +1012,7 @@ addr.sin_family = AF_INET; addr.sin_port = htons(port); if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - perror("bind"); + wpa_printf(MSG_INFO, "RADIUS: bind: %s", strerror(errno)); close(s); return -1; } @@ -1009,7 +1029,8 @@ s = socket(PF_INET6, SOCK_DGRAM, 0); if (s < 0) { - perror("socket[IPv6]"); + wpa_printf(MSG_INFO, "RADIUS: socket[IPv6]: %s", + strerror(errno)); return -1; } @@ -1018,7 +1039,7 @@ os_memcpy(&addr.sin6_addr, &in6addr_any, sizeof(in6addr_any)); addr.sin6_port = htons(port); if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - perror("bind"); + wpa_printf(MSG_INFO, "RADIUS: bind: %s", strerror(errno)); close(s); return -1; } @@ -1229,8 +1250,7 @@ #ifndef CONFIG_IPV6 if (conf->ipv6) { - fprintf(stderr, "RADIUS server compiled without IPv6 " - "support.\n"); + wpa_printf(MSG_ERROR, "RADIUS server compiled without IPv6 support"); return NULL; } #endif /* CONFIG_IPV6 */ @@ -1239,7 +1259,7 @@ if (data == NULL) return NULL; - os_get_time(&data->start_time); + os_get_reltime(&data->start_time); data->conf_ctx = conf->conf_ctx; data->eap_sim_db_priv = conf->eap_sim_db_priv; data->ssl_ctx = conf->ssl_ctx; @@ -1268,6 +1288,7 @@ data->tnc = conf->tnc; data->wps = conf->wps; data->pwd_group = conf->pwd_group; + data->server_id = conf->server_id; if (conf->eap_req_id_text) { data->eap_req_id_text = os_malloc(conf->eap_req_id_text_len); if (data->eap_req_id_text) { @@ -1277,10 +1298,15 @@ } } +#ifdef CONFIG_RADIUS_TEST + if (conf->dump_msk_file) + data->dump_msk_file = os_strdup(conf->dump_msk_file); +#endif /* CONFIG_RADIUS_TEST */ + data->clients = radius_server_read_clients(conf->client_file, conf->ipv6); if (data->clients == NULL) { - printf("No RADIUS clients configured.\n"); + wpa_printf(MSG_ERROR, "No RADIUS clients configured"); radius_server_deinit(data); return NULL; } @@ -1292,8 +1318,7 @@ #endif /* CONFIG_IPV6 */ data->auth_sock = radius_server_open_socket(conf->auth_port); if (data->auth_sock < 0) { - printf("Failed to open UDP socket for RADIUS authentication " - "server\n"); + wpa_printf(MSG_ERROR, "Failed to open UDP socket for RADIUS authentication server"); radius_server_deinit(data); return NULL; } @@ -1328,6 +1353,9 @@ os_free(data->eap_fast_a_id); os_free(data->eap_fast_a_id_info); os_free(data->eap_req_id_text); +#ifdef CONFIG_RADIUS_TEST + os_free(data->dump_msk_file); +#endif /* CONFIG_RADIUS_TEST */ os_free(data); } @@ -1345,7 +1373,7 @@ int ret, uptime; unsigned int idx; char *end, *pos; - struct os_time now; + struct os_reltime now; struct radius_client *cli; /* RFC 2619 - RADIUS Authentication Server MIB */ @@ -1356,7 +1384,7 @@ pos = buf; end = buf + buflen; - os_get_time(&now); + os_get_reltime(&now); uptime = (now.sec - data->start_time.sec) * 100 + ((now.usec - data->start_time.usec) / 10000) % 100; ret = os_snprintf(pos, end - pos, diff -Nru wpa-1.0/src/radius/radius_server.h wpa-2.1/src/radius/radius_server.h --- wpa-1.0/src/radius/radius_server.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/radius/radius_server.h 2014-02-04 11:23:35.000000000 +0000 @@ -1,15 +1,9 @@ /* * RADIUS authentication server - * Copyright (c) 2005-2009, Jouni Malinen + * Copyright (c) 2005-2009, 2011, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef RADIUS_SERVER_H @@ -150,6 +144,11 @@ u16 pwd_group; /** + * server_id - Server identity + */ + const char *server_id; + + /** * wps - Wi-Fi Protected Setup context * * If WPS is used with an external RADIUS server (which is quite @@ -201,6 +200,10 @@ * msg_ctx - Context data for wpa_msg() calls */ void *msg_ctx; + +#ifdef CONFIG_RADIUS_TEST + const char *dump_msk_file; +#endif /* CONFIG_RADIUS_TEST */ }; diff -Nru wpa-1.0/src/rsn_supp/Makefile wpa-2.1/src/rsn_supp/Makefile --- wpa-1.0/src/rsn_supp/Makefile 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/rsn_supp/Makefile 2014-02-04 11:23:35.000000000 +0000 @@ -2,7 +2,7 @@ @echo Nothing to be made. clean: - rm -f *~ *.o *.d + rm -f *~ *.o *.d *.gcno *.gcda *.gcov install: @echo Nothing to be made. diff -Nru wpa-1.0/src/rsn_supp/peerkey.c wpa-2.1/src/rsn_supp/peerkey.c --- wpa-1.0/src/rsn_supp/peerkey.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/rsn_supp/peerkey.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * WPA Supplicant - PeerKey for Direct Link Setup (DLS) * Copyright (c) 2006-2008, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -223,20 +217,17 @@ return -1; } - cipher = ie.pairwise_cipher & sm->allowed_pairwise_cipher; - if (cipher & WPA_CIPHER_CCMP) { - wpa_printf(MSG_DEBUG, "RSN: Using CCMP for PeerKey"); - cipher = WPA_CIPHER_CCMP; - } else if (cipher & WPA_CIPHER_TKIP) { - wpa_printf(MSG_DEBUG, "RSN: Using TKIP for PeerKey"); - cipher = WPA_CIPHER_TKIP; - } else { + cipher = wpa_pick_pairwise_cipher(ie.pairwise_cipher & + sm->allowed_pairwise_cipher, 0); + if (cipher < 0) { wpa_printf(MSG_INFO, "RSN: No acceptable cipher in SMK M2"); wpa_supplicant_send_smk_error(sm, src_addr, kde.mac_addr, STK_MUI_SMK, STK_ERR_CPHR_NS, ver); return -1; } + wpa_printf(MSG_DEBUG, "RSN: Using %s for PeerKey", + wpa_cipher_txt(cipher)); /* TODO: find existing entry and if found, use that instead of adding * a new one; how to handle the case where both ends initiate at the @@ -273,10 +264,7 @@ /* Include only the selected cipher in pairwise cipher suite */ WPA_PUT_LE16(pos, 1); pos += 2; - if (cipher == WPA_CIPHER_CCMP) - RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP); - else if (cipher == WPA_CIPHER_TKIP) - RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_TKIP); + RSN_SELECTOR_PUT(pos, wpa_cipher_to_suite(WPA_PROTO_RSN, cipher)); pos += RSN_SELECTOR_LEN; hdr->len = (pos - peerkey->rsnie_p) - 2; @@ -350,7 +338,7 @@ msg->type = EAPOL_KEY_TYPE_RSN; - if (peerkey->cipher == WPA_CIPHER_CCMP) + if (peerkey->cipher != WPA_CIPHER_TKIP) ver = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES; else ver = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4; @@ -358,7 +346,7 @@ key_info = ver | WPA_KEY_INFO_KEY_TYPE | WPA_KEY_INFO_ACK; WPA_PUT_BE16(msg->key_info, key_info); - if (peerkey->cipher == WPA_CIPHER_CCMP) + if (peerkey->cipher != WPA_CIPHER_TKIP) WPA_PUT_BE16(msg->key_length, 16); else WPA_PUT_BE16(msg->key_length, 32); @@ -409,7 +397,7 @@ msg->type = EAPOL_KEY_TYPE_RSN; - if (peerkey->cipher == WPA_CIPHER_CCMP) + if (peerkey->cipher != WPA_CIPHER_TKIP) ver = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES; else ver = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4; @@ -418,7 +406,7 @@ WPA_KEY_INFO_MIC | WPA_KEY_INFO_SECURE; WPA_PUT_BE16(msg->key_info, key_info); - if (peerkey->cipher == WPA_CIPHER_CCMP) + if (peerkey->cipher != WPA_CIPHER_TKIP) WPA_PUT_BE16(msg->key_length, 16); else WPA_PUT_BE16(msg->key_length, 32); @@ -502,14 +490,9 @@ peerkey->rsnie_p_len = kde->rsn_ie_len; os_memcpy(peerkey->pnonce, kde->nonce, WPA_NONCE_LEN); - cipher = ie.pairwise_cipher & sm->allowed_pairwise_cipher; - if (cipher & WPA_CIPHER_CCMP) { - wpa_printf(MSG_DEBUG, "RSN: Using CCMP for PeerKey"); - peerkey->cipher = WPA_CIPHER_CCMP; - } else if (cipher & WPA_CIPHER_TKIP) { - wpa_printf(MSG_DEBUG, "RSN: Using TKIP for PeerKey"); - peerkey->cipher = WPA_CIPHER_TKIP; - } else { + cipher = wpa_pick_pairwise_cipher(ie.pairwise_cipher & + sm->allowed_pairwise_cipher, 0); + if (cipher < 0) { wpa_printf(MSG_INFO, "RSN: SMK Peer STA " MACSTR " selected " "unacceptable cipher", MAC2STR(kde->mac_addr)); wpa_supplicant_send_smk_error(sm, src_addr, kde->mac_addr, @@ -518,6 +501,9 @@ /* TODO: abort negotiation */ return -1; } + wpa_printf(MSG_DEBUG, "RSN: Using %s for PeerKey", + wpa_cipher_txt(cipher)); + peerkey->cipher = cipher; return 0; } @@ -530,7 +516,6 @@ struct wpa_peerkey *peerkey; struct wpa_eapol_ie_parse kde; u32 lifetime; - struct os_time now; if (!sm->peerkey_enabled || sm->proto != WPA_PROTO_RSN) { wpa_printf(MSG_DEBUG, "RSN: SMK handshake not allowed for " @@ -582,10 +567,8 @@ lifetime = WPA_GET_BE32(kde.lifetime); wpa_printf(MSG_DEBUG, "RSN: SMK lifetime %u seconds", lifetime); if (lifetime > 1000000000) - lifetime = 1000000000; /* avoid overflowing expiration time */ + lifetime = 1000000000; /* avoid overflowing eloop time */ peerkey->lifetime = lifetime; - os_get_time(&now); - peerkey->expiration = now.sec + lifetime; eloop_register_timeout(lifetime, 0, wpa_supplicant_smk_timeout, sm, peerkey); @@ -750,7 +733,6 @@ struct wpa_eapol_ie_parse *kde) { u32 lifetime; - struct os_time now; if (kde->lifetime == NULL || kde->lifetime_len < sizeof(lifetime)) return; @@ -769,8 +751,6 @@ lifetime, peerkey->lifetime); peerkey->lifetime = lifetime; - os_get_time(&now); - peerkey->expiration = now.sec + lifetime; eloop_cancel_timeout(wpa_supplicant_smk_timeout, sm, peerkey); eloop_register_timeout(lifetime, 0, wpa_supplicant_smk_timeout, sm, peerkey); @@ -1022,7 +1002,7 @@ return -1; } - if (sm->pairwise_cipher == WPA_CIPHER_CCMP) + if (sm->pairwise_cipher != WPA_CIPHER_TKIP) ver = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES; else ver = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4; @@ -1061,17 +1041,8 @@ count_pos = pos; pos += 2; - count = 0; - if (sm->allowed_pairwise_cipher & WPA_CIPHER_CCMP) { - RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP); - pos += RSN_SELECTOR_LEN; - count++; - } - if (sm->allowed_pairwise_cipher & WPA_CIPHER_TKIP) { - RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_TKIP); - pos += RSN_SELECTOR_LEN; - count++; - } + count = rsn_cipher_put_suites(pos, sm->allowed_pairwise_cipher); + pos += count * RSN_SELECTOR_LEN; WPA_PUT_LE16(count_pos, count); hdr->len = (pos - peerkey->rsnie_i) - 2; @@ -1139,7 +1110,7 @@ while (peerkey) { prev = peerkey; peerkey = peerkey->next; - os_free(prev); + wpa_supplicant_peerkey_free(sm, prev); } sm->peerkey = NULL; } diff -Nru wpa-1.0/src/rsn_supp/peerkey.h wpa-2.1/src/rsn_supp/peerkey.h --- wpa-1.0/src/rsn_supp/peerkey.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/rsn_supp/peerkey.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * WPA Supplicant - PeerKey for Direct Link Setup (DLS) * Copyright (c) 2006-2008, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef PEERKEY_H @@ -30,7 +24,6 @@ int smk_complete; u8 smkid[PMKID_LEN]; u32 lifetime; - os_time_t expiration; int cipher; /* Selected cipher (WPA_CIPHER_*) */ u8 replay_counter[WPA_REPLAY_COUNTER_LEN]; int replay_counter_set; diff -Nru wpa-1.0/src/rsn_supp/pmksa_cache.c wpa-2.1/src/rsn_supp/pmksa_cache.c --- wpa-1.0/src/rsn_supp/pmksa_cache.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/rsn_supp/pmksa_cache.c 2014-02-04 11:23:35.000000000 +0000 @@ -1,15 +1,9 @@ /* * WPA Supplicant - RSN PMKSA cache - * Copyright (c) 2004-2008, Jouni Malinen + * Copyright (c) 2004-2009, 2011-2012, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -21,7 +15,7 @@ #include "wpa_i.h" #include "pmksa_cache.h" -#if defined(IEEE8021X_EAPOL) && !defined(CONFIG_NO_WPA2) +#ifdef IEEE8021X_EAPOL static const int pmksa_cache_max_entries = 32; @@ -31,7 +25,7 @@ struct wpa_sm *sm; /* TODO: get rid of this reference(?) */ void (*free_cb)(struct rsn_pmksa_cache_entry *entry, void *ctx, - int replace); + enum pmksa_free_reason reason); void *ctx; }; @@ -47,11 +41,11 @@ static void pmksa_cache_free_entry(struct rsn_pmksa_cache *pmksa, struct rsn_pmksa_cache_entry *entry, - int replace) + enum pmksa_free_reason reason) { wpa_sm_remove_pmkid(pmksa->sm, entry->aa, entry->pmkid); pmksa->pmksa_count--; - pmksa->free_cb(entry, pmksa->ctx, replace); + pmksa->free_cb(entry, pmksa->ctx, reason); _pmksa_cache_free_entry(entry); } @@ -59,15 +53,15 @@ static void pmksa_cache_expire(void *eloop_ctx, void *timeout_ctx) { struct rsn_pmksa_cache *pmksa = eloop_ctx; - struct os_time now; + struct os_reltime now; - os_get_time(&now); + os_get_reltime(&now); while (pmksa->pmksa && pmksa->pmksa->expiration <= now.sec) { struct rsn_pmksa_cache_entry *entry = pmksa->pmksa; pmksa->pmksa = entry->next; wpa_printf(MSG_DEBUG, "RSN: expired PMKSA cache entry for " MACSTR, MAC2STR(entry->aa)); - pmksa_cache_free_entry(pmksa, entry, 0); + pmksa_cache_free_entry(pmksa, entry, PMKSA_EXPIRE); } pmksa_cache_set_expiration(pmksa); @@ -86,20 +80,20 @@ { int sec; struct rsn_pmksa_cache_entry *entry; - struct os_time now; + struct os_reltime now; eloop_cancel_timeout(pmksa_cache_expire, pmksa, NULL); eloop_cancel_timeout(pmksa_cache_reauth, pmksa, NULL); if (pmksa->pmksa == NULL) return; - os_get_time(&now); + os_get_reltime(&now); sec = pmksa->pmksa->expiration - now.sec; if (sec < 0) sec = 0; eloop_register_timeout(sec + 1, 0, pmksa_cache_expire, pmksa, NULL); entry = pmksa->sm->cur_pmksa ? pmksa->sm->cur_pmksa : - pmksa_cache_get(pmksa, pmksa->sm->bssid, NULL); + pmksa_cache_get(pmksa, pmksa->sm->bssid, NULL, NULL); if (entry) { sec = pmksa->pmksa->reauth_time - now.sec; if (sec < 0) @@ -131,7 +125,7 @@ const u8 *aa, const u8 *spa, void *network_ctx, int akmp) { struct rsn_pmksa_cache_entry *entry, *pos, *prev; - struct os_time now; + struct os_reltime now; if (pmk_len > PMK_LEN) return NULL; @@ -143,7 +137,7 @@ entry->pmk_len = pmk_len; rsn_pmkid(pmk, pmk_len, aa, spa, entry->pmkid, wpa_key_mgmt_sha256(akmp)); - os_get_time(&now); + os_get_reltime(&now); entry->expiration = now.sec + pmksa->sm->dot11RSNAConfigPMKLifetime; entry->reauth_time = now.sec + pmksa->sm->dot11RSNAConfigPMKLifetime * pmksa->sm->dot11RSNAConfigPMKReauthThreshold / 100; @@ -170,30 +164,23 @@ pmksa->pmksa = pos->next; else prev->next = pos->next; - if (pos == pmksa->sm->cur_pmksa) { - /* We are about to replace the current PMKSA - * cache entry. This happens when the PMKSA - * caching attempt fails, so we don't want to - * force pmksa_cache_free_entry() to disconnect - * at this point. Let's just make sure the old - * PMKSA cache entry will not be used in the - * future. - */ - wpa_printf(MSG_DEBUG, "RSN: replacing current " - "PMKSA entry"); - pmksa->sm->cur_pmksa = NULL; - } - wpa_printf(MSG_DEBUG, "RSN: Replace PMKSA entry for " - "the current AP"); - pmksa_cache_free_entry(pmksa, pos, 1); /* * If OKC is used, there may be other PMKSA cache * entries based on the same PMK. These needs to be * flushed so that a new entry can be created based on - * the new PMK. + * the new PMK. Only clear other entries if they have a + * matching PMK and this PMK has been used successfully + * with the current AP, i.e., if opportunistic flag has + * been cleared in wpa_supplicant_key_neg_complete(). */ - pmksa_cache_flush(pmksa, network_ctx); + wpa_printf(MSG_DEBUG, "RSN: Replace PMKSA entry for " + "the current AP and any PMKSA cache entry " + "that was based on the old PMK"); + if (!pos->opportunistic) + pmksa_cache_flush(pmksa, network_ctx, pos->pmk, + pos->pmk_len); + pmksa_cache_free_entry(pmksa, pos, PMKSA_REPLACE); break; } prev = pos; @@ -203,11 +190,25 @@ if (pmksa->pmksa_count >= pmksa_cache_max_entries && pmksa->pmksa) { /* Remove the oldest entry to make room for the new entry */ pos = pmksa->pmksa; - pmksa->pmksa = pos->next; - wpa_printf(MSG_DEBUG, "RSN: removed the oldest PMKSA cache " - "entry (for " MACSTR ") to make room for new one", - MAC2STR(pos->aa)); - pmksa_cache_free_entry(pmksa, pos, 0); + + if (pos == pmksa->sm->cur_pmksa) { + /* + * Never remove the current PMKSA cache entry, since + * it's in use, and removing it triggers a needless + * deauthentication. + */ + pos = pos->next; + pmksa->pmksa->next = pos ? pos->next : NULL; + } else + pmksa->pmksa = pos->next; + + if (pos) { + wpa_printf(MSG_DEBUG, "RSN: removed the oldest idle " + "PMKSA cache entry (for " MACSTR ") to " + "make room for new one", + MAC2STR(pos->aa)); + pmksa_cache_free_entry(pmksa, pos, PMKSA_FREE); + } } /* Add the new entry; order by expiration time */ @@ -228,8 +229,8 @@ prev->next = entry; } pmksa->pmksa_count++; - wpa_printf(MSG_DEBUG, "RSN: added PMKSA cache entry for " MACSTR, - MAC2STR(entry->aa)); + wpa_printf(MSG_DEBUG, "RSN: Added PMKSA cache entry for " MACSTR + " network_ctx=%p", MAC2STR(entry->aa), network_ctx); wpa_sm_add_pmkid(pmksa->sm, entry->aa, entry->pmkid); return entry; @@ -240,15 +241,22 @@ * pmksa_cache_flush - Flush PMKSA cache entries for a specific network * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init() * @network_ctx: Network configuration context or %NULL to flush all entries + * @pmk: PMK to match for or %NYLL to match all PMKs + * @pmk_len: PMK length */ -void pmksa_cache_flush(struct rsn_pmksa_cache *pmksa, void *network_ctx) +void pmksa_cache_flush(struct rsn_pmksa_cache *pmksa, void *network_ctx, + const u8 *pmk, size_t pmk_len) { struct rsn_pmksa_cache_entry *entry, *prev = NULL, *tmp; int removed = 0; entry = pmksa->pmksa; while (entry) { - if (entry->network_ctx == network_ctx || network_ctx == NULL) { + if ((entry->network_ctx == network_ctx || + network_ctx == NULL) && + (pmk == NULL || + (pmk_len == entry->pmk_len && + os_memcmp(pmk, entry->pmk, pmk_len) == 0))) { wpa_printf(MSG_DEBUG, "RSN: Flush PMKSA cache entry " "for " MACSTR, MAC2STR(entry->aa)); if (prev) @@ -257,7 +265,7 @@ pmksa->pmksa = entry->next; tmp = entry; entry = entry->next; - pmksa_cache_free_entry(pmksa, tmp, 0); + pmksa_cache_free_entry(pmksa, tmp, PMKSA_FREE); removed++; } else { prev = entry; @@ -297,16 +305,19 @@ * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init() * @aa: Authenticator address or %NULL to match any * @pmkid: PMKID or %NULL to match any + * @network_ctx: Network context or %NULL to match any * Returns: Pointer to PMKSA cache entry or %NULL if no match was found */ struct rsn_pmksa_cache_entry * pmksa_cache_get(struct rsn_pmksa_cache *pmksa, - const u8 *aa, const u8 *pmkid) + const u8 *aa, const u8 *pmkid, + const void *network_ctx) { struct rsn_pmksa_cache_entry *entry = pmksa->pmksa; while (entry) { if ((aa == NULL || os_memcmp(entry->aa, aa, ETH_ALEN) == 0) && (pmkid == NULL || - os_memcmp(entry->pmkid, pmkid, PMKID_LEN) == 0)) + os_memcmp(entry->pmkid, pmkid, PMKID_LEN) == 0) && + (network_ctx == NULL || network_ctx == entry->network_ctx)) return entry; entry = entry->next; } @@ -410,20 +421,32 @@ int try_opportunistic) { struct rsn_pmksa_cache *pmksa = sm->pmksa; + wpa_printf(MSG_DEBUG, "RSN: PMKSA cache search - network_ctx=%p " + "try_opportunistic=%d", network_ctx, try_opportunistic); + if (pmkid) + wpa_hexdump(MSG_DEBUG, "RSN: Search for PMKID", + pmkid, PMKID_LEN); + if (bssid) + wpa_printf(MSG_DEBUG, "RSN: Search for BSSID " MACSTR, + MAC2STR(bssid)); + sm->cur_pmksa = NULL; if (pmkid) - sm->cur_pmksa = pmksa_cache_get(pmksa, NULL, pmkid); + sm->cur_pmksa = pmksa_cache_get(pmksa, NULL, pmkid, + network_ctx); if (sm->cur_pmksa == NULL && bssid) - sm->cur_pmksa = pmksa_cache_get(pmksa, bssid, NULL); + sm->cur_pmksa = pmksa_cache_get(pmksa, bssid, NULL, + network_ctx); if (sm->cur_pmksa == NULL && try_opportunistic && bssid) sm->cur_pmksa = pmksa_cache_get_opportunistic(pmksa, network_ctx, bssid); if (sm->cur_pmksa) { - wpa_hexdump(MSG_DEBUG, "RSN: PMKID", + wpa_hexdump(MSG_DEBUG, "RSN: PMKSA cache entry found - PMKID", sm->cur_pmksa->pmkid, PMKID_LEN); return 0; } + wpa_printf(MSG_DEBUG, "RSN: No PMKSA cache entry found"); return -1; } @@ -443,9 +466,9 @@ int i, ret; char *pos = buf; struct rsn_pmksa_cache_entry *entry; - struct os_time now; + struct os_reltime now; - os_get_time(&now); + os_get_reltime(&now); ret = os_snprintf(pos, buf + len - pos, "Index / AA / PMKID / expiration (in seconds) / " "opportunistic\n"); @@ -484,7 +507,7 @@ */ struct rsn_pmksa_cache * pmksa_cache_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry, - void *ctx, int replace), + void *ctx, enum pmksa_free_reason reason), void *ctx, struct wpa_sm *sm) { struct rsn_pmksa_cache *pmksa; @@ -499,4 +522,4 @@ return pmksa; } -#endif /* IEEE8021X_EAPOL and !CONFIG_NO_WPA2 */ +#endif /* IEEE8021X_EAPOL */ diff -Nru wpa-1.0/src/rsn_supp/pmksa_cache.h wpa-2.1/src/rsn_supp/pmksa_cache.h --- wpa-1.0/src/rsn_supp/pmksa_cache.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/rsn_supp/pmksa_cache.h 2014-02-04 11:23:35.000000000 +0000 @@ -1,15 +1,9 @@ /* * wpa_supplicant - WPA2/RSN PMKSA cache functions - * Copyright (c) 2003-2008, Jouni Malinen + * Copyright (c) 2003-2009, 2011-2012, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef PMKSA_CACHE_H @@ -44,15 +38,22 @@ struct rsn_pmksa_cache; -#if defined(IEEE8021X_EAPOL) && !defined(CONFIG_NO_WPA2) +enum pmksa_free_reason { + PMKSA_FREE, + PMKSA_REPLACE, + PMKSA_EXPIRE, +}; + +#ifdef IEEE8021X_EAPOL struct rsn_pmksa_cache * pmksa_cache_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry, - void *ctx, int replace), + void *ctx, enum pmksa_free_reason reason), void *ctx, struct wpa_sm *sm); void pmksa_cache_deinit(struct rsn_pmksa_cache *pmksa); struct rsn_pmksa_cache_entry * pmksa_cache_get(struct rsn_pmksa_cache *pmksa, - const u8 *aa, const u8 *pmkid); + const u8 *aa, const u8 *pmkid, + const void *network_ctx); int pmksa_cache_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len); struct rsn_pmksa_cache_entry * pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len, @@ -65,13 +66,14 @@ struct rsn_pmksa_cache_entry * pmksa_cache_get_opportunistic(struct rsn_pmksa_cache *pmksa, void *network_ctx, const u8 *aa); -void pmksa_cache_flush(struct rsn_pmksa_cache *pmksa, void *network_ctx); +void pmksa_cache_flush(struct rsn_pmksa_cache *pmksa, void *network_ctx, + const u8 *pmk, size_t pmk_len); -#else /* IEEE8021X_EAPOL and !CONFIG_NO_WPA2 */ +#else /* IEEE8021X_EAPOL */ static inline struct rsn_pmksa_cache * pmksa_cache_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry, - void *ctx, int replace), + void *ctx, enum pmksa_free_reason reason), void *ctx, struct wpa_sm *sm) { return (void *) -1; @@ -82,7 +84,8 @@ } static inline struct rsn_pmksa_cache_entry * -pmksa_cache_get(struct rsn_pmksa_cache *pmksa, const u8 *aa, const u8 *pmkid) +pmksa_cache_get(struct rsn_pmksa_cache *pmksa, const u8 *aa, const u8 *pmkid, + const void *network_ctx) { return NULL; } @@ -119,10 +122,11 @@ } static inline void pmksa_cache_flush(struct rsn_pmksa_cache *pmksa, - void *network_ctx) + void *network_ctx, + const u8 *pmk, size_t pmk_len) { } -#endif /* IEEE8021X_EAPOL and !CONFIG_NO_WPA2 */ +#endif /* IEEE8021X_EAPOL */ #endif /* PMKSA_CACHE_H */ diff -Nru wpa-1.0/src/rsn_supp/preauth.c wpa-2.1/src/rsn_supp/preauth.c --- wpa-1.0/src/rsn_supp/preauth.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/rsn_supp/preauth.c 2014-02-04 11:23:35.000000000 +0000 @@ -1,15 +1,9 @@ /* * RSN pre-authentication (supplicant) - * Copyright (c) 2003-2010, Jouni Malinen + * Copyright (c) 2003-2012, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -22,10 +16,9 @@ #include "preauth.h" #include "pmksa_cache.h" #include "wpa_i.h" -#include "common/ieee802_11_defs.h" -#if defined(IEEE8021X_EAPOL) && !defined(CONFIG_NO_WPA2) +#ifdef IEEE8021X_EAPOL #define PMKID_CANDIDATE_PRIO_SCAN 1000 @@ -77,13 +70,14 @@ } -static void rsn_preauth_eapol_cb(struct eapol_sm *eapol, int success, +static void rsn_preauth_eapol_cb(struct eapol_sm *eapol, + enum eapol_supp_result result, void *ctx) { struct wpa_sm *sm = ctx; u8 pmk[PMK_LEN]; - if (success) { + if (result == EAPOL_SUPP_RESULT_SUCCESS) { int res, pmk_len; pmk_len = PMK_LEN; res = eapol_sm_get_key(eapol, pmk, PMK_LEN); @@ -107,13 +101,14 @@ wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "RSN: failed to get master session key from " "pre-auth EAPOL state machines"); - success = 0; + result = EAPOL_SUPP_RESULT_FAILURE; } } wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "RSN: pre-authentication with " MACSTR " %s", MAC2STR(sm->preauth_bssid), - success ? "completed successfully" : "failed"); + result == EAPOL_SUPP_RESULT_SUCCESS ? "completed successfully" : + "failed"); rsn_preauth_deinit(sm); rsn_preauth_candidate_process(sm); @@ -312,7 +307,7 @@ dl_list_for_each_safe(candidate, n, &sm->pmksa_candidates, struct rsn_pmksa_candidate, list) { struct rsn_pmksa_cache_entry *p = NULL; - p = pmksa_cache_get(sm->pmksa, candidate->bssid, NULL); + p = pmksa_cache_get(sm->pmksa, candidate->bssid, NULL, NULL); if (os_memcmp(sm->bssid, candidate->bssid, ETH_ALEN) != 0 && (p == NULL || p->opportunistic)) { wpa_msg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: PMKSA " @@ -459,7 +454,7 @@ if (wpa_parse_wpa_ie(rsn, 2 + rsn[1], &ie)) return; - pmksa = pmksa_cache_get(sm->pmksa, bssid, NULL); + pmksa = pmksa_cache_get(sm->pmksa, bssid, NULL, NULL); if (pmksa && (!pmksa->opportunistic || !(ie.capabilities & WPA_CAPABILITY_PREAUTH))) return; @@ -515,4 +510,4 @@ return sm->preauth_eapol != NULL; } -#endif /* IEEE8021X_EAPOL and !CONFIG_NO_WPA2 */ +#endif /* IEEE8021X_EAPOL */ diff -Nru wpa-1.0/src/rsn_supp/preauth.h wpa-2.1/src/rsn_supp/preauth.h --- wpa-1.0/src/rsn_supp/preauth.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/rsn_supp/preauth.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * wpa_supplicant - WPA2/RSN pre-authentication functions * Copyright (c) 2003-2009, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef PREAUTH_H @@ -17,7 +11,7 @@ struct wpa_scan_results; -#if defined(IEEE8021X_EAPOL) && !defined(CONFIG_NO_WPA2) +#ifdef IEEE8021X_EAPOL void pmksa_candidate_free(struct wpa_sm *sm); int rsn_preauth_init(struct wpa_sm *sm, const u8 *dst, @@ -33,7 +27,7 @@ int verbose); int rsn_preauth_in_progress(struct wpa_sm *sm); -#else /* IEEE8021X_EAPOL and !CONFIG_NO_WPA2 */ +#else /* IEEE8021X_EAPOL */ static inline void pmksa_candidate_free(struct wpa_sm *sm) { @@ -80,6 +74,6 @@ return 0; } -#endif /* IEEE8021X_EAPOL and !CONFIG_NO_WPA2 */ +#endif /* IEEE8021X_EAPOL */ #endif /* PREAUTH_H */ diff -Nru wpa-1.0/src/rsn_supp/tdls.c wpa-2.1/src/rsn_supp/tdls.c --- wpa-1.0/src/rsn_supp/tdls.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/rsn_supp/tdls.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * wpa_supplicant - TDLS * Copyright (c) 2010-2011, Atheros Communications * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "utils/includes.h" @@ -43,8 +37,10 @@ #endif /* CONFIG_TDLS_TESTING */ #define TPK_LIFETIME 43200 /* 12 hours */ -#define TPK_RETRY_COUNT 3 -#define TPK_TIMEOUT 5000 /* in milliseconds */ +#define TPK_M1_RETRY_COUNT 3 +#define TPK_M1_TIMEOUT 5000 /* in milliseconds */ +#define TPK_M2_RETRY_COUNT 10 +#define TPK_M2_TIMEOUT 500 /* in milliseconds */ #define TDLS_MIC_LEN 16 @@ -85,6 +81,8 @@ static u8 * wpa_add_tdls_timeoutie(u8 *pos, u8 *ie, size_t ie_len, u32 tsecs); static void wpa_tdls_tpk_retry_timeout(void *eloop_ctx, void *timeout_ctx); static void wpa_tdls_peer_free(struct wpa_sm *sm, struct wpa_tdls_peer *peer); +static void wpa_tdls_disable_peer_link(struct wpa_sm *sm, + struct wpa_tdls_peer *peer); #define TDLS_MAX_IE_LEN 80 @@ -92,6 +90,7 @@ struct wpa_tdls_peer { struct wpa_tdls_peer *next; + unsigned int reconfig_key:1; int initiator; /* whether this end was initiator for TDLS setup */ u8 addr[ETH_ALEN]; /* other end MAC address */ u8 inonce[WPA_NONCE_LEN]; /* Initiator Nonce */ @@ -110,6 +109,7 @@ } tpk; int tpk_set; int tpk_success; + int tpk_in_progress; struct tpk_timer { u8 dest[ETH_ALEN]; @@ -126,6 +126,22 @@ u8 supp_rates[IEEE80211_MAX_SUPP_RATES]; size_t supp_rates_len; + + struct ieee80211_ht_capabilities *ht_capabilities; + struct ieee80211_vht_capabilities *vht_capabilities; + + u8 qos_info; + + u16 aid; + + u8 *ext_capab; + size_t ext_capab_len; + + u8 *supp_channels; + size_t supp_channels_len; + + u8 *supp_oper_classes; + size_t supp_oper_classes_len; }; @@ -239,8 +255,13 @@ eloop_cancel_timeout(wpa_tdls_tpk_retry_timeout, sm, peer); - peer->sm_tmr.count = TPK_RETRY_COUNT; - peer->sm_tmr.timer = TPK_TIMEOUT; + if (action_code == WLAN_TDLS_SETUP_RESPONSE) { + peer->sm_tmr.count = TPK_M2_RETRY_COUNT; + peer->sm_tmr.timer = TPK_M2_TIMEOUT; + } else { + peer->sm_tmr.count = TPK_M1_RETRY_COUNT; + peer->sm_tmr.timer = TPK_M1_TIMEOUT; + } /* Copy message to resend on timeout */ os_memcpy(peer->sm_tmr.dest, dest, ETH_ALEN); @@ -256,28 +277,21 @@ wpa_printf(MSG_DEBUG, "TDLS: Retry timeout registered " "(action_code=%u)", action_code); - eloop_register_timeout(peer->sm_tmr.timer / 1000, 0, + eloop_register_timeout(peer->sm_tmr.timer / 1000, + (peer->sm_tmr.timer % 1000) * 1000, wpa_tdls_tpk_retry_timeout, sm, peer); return 0; } static int wpa_tdls_do_teardown(struct wpa_sm *sm, struct wpa_tdls_peer *peer, - u16 reason_code, int free_peer) + u16 reason_code) { int ret; - if (sm->tdls_external_setup) { - ret = wpa_tdls_send_teardown(sm, peer->addr, reason_code); - - /* disable the link after teardown was sent */ - wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, peer->addr); - } else { - ret = wpa_sm_tdls_oper(sm, TDLS_TEARDOWN, peer->addr); - } - - if (sm->tdls_external_setup || free_peer) - wpa_tdls_peer_free(sm, peer); + ret = wpa_tdls_send_teardown(sm, peer->addr, reason_code); + /* disable the link after teardown was sent */ + wpa_tdls_disable_peer_link(sm, peer); return ret; } @@ -291,7 +305,6 @@ if (peer->sm_tmr.count) { peer->sm_tmr.count--; - peer->sm_tmr.timer = TPK_TIMEOUT; wpa_printf(MSG_INFO, "TDLS: Retrying sending of message " "(action_code=%u)", @@ -318,14 +331,15 @@ } eloop_cancel_timeout(wpa_tdls_tpk_retry_timeout, sm, peer); - eloop_register_timeout(peer->sm_tmr.timer / 1000, 0, + eloop_register_timeout(peer->sm_tmr.timer / 1000, + (peer->sm_tmr.timer % 1000) * 1000, wpa_tdls_tpk_retry_timeout, sm, peer); } else { eloop_cancel_timeout(wpa_tdls_tpk_retry_timeout, sm, peer); wpa_printf(MSG_DEBUG, "TDLS: Sending Teardown Request"); wpa_tdls_do_teardown(sm, peer, - WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED, 1); + WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED); } } @@ -603,7 +617,7 @@ wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime expired for " MACSTR " - tear down", MAC2STR(peer->addr)); wpa_tdls_do_teardown(sm, peer, - WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED, 1); + WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED); } } @@ -614,9 +628,21 @@ MAC2STR(peer->addr)); eloop_cancel_timeout(wpa_tdls_tpk_timeout, sm, peer); eloop_cancel_timeout(wpa_tdls_tpk_retry_timeout, sm, peer); + peer->reconfig_key = 0; peer->initiator = 0; + peer->tpk_in_progress = 0; os_free(peer->sm_tmr.buf); peer->sm_tmr.buf = NULL; + os_free(peer->ht_capabilities); + peer->ht_capabilities = NULL; + os_free(peer->vht_capabilities); + peer->vht_capabilities = NULL; + os_free(peer->ext_capab); + peer->ext_capab = NULL; + os_free(peer->supp_channels); + peer->supp_channels = NULL; + os_free(peer->supp_oper_classes); + peer->supp_oper_classes = NULL; peer->rsnie_i_len = peer->rsnie_p_len = 0; peer->cipher = 0; peer->tpk_set = peer->tpk_success = 0; @@ -721,8 +747,7 @@ /* request driver to send Teardown using this FTIE */ wpa_tdls_tpk_send(sm, addr, WLAN_TDLS_TEARDOWN, 0, - WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED, rbuf, - pos - rbuf); + reason_code, rbuf, pos - rbuf); os_free(rbuf); /* clear the Peerkey statemachine */ @@ -756,7 +781,15 @@ return -1; } - return wpa_tdls_do_teardown(sm, peer, reason_code, 0); + return wpa_tdls_do_teardown(sm, peer, reason_code); +} + + +static void wpa_tdls_disable_peer_link(struct wpa_sm *sm, + struct wpa_tdls_peer *peer) +{ + wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, peer->addr); + wpa_tdls_peer_free(sm, peer); } @@ -769,10 +802,30 @@ break; } - if (peer) { - wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, addr); - wpa_tdls_peer_free(sm, peer); + if (peer) + wpa_tdls_disable_peer_link(sm, peer); +} + + +const char * wpa_tdls_get_link_status(struct wpa_sm *sm, const u8 *addr) +{ + struct wpa_tdls_peer *peer; + + if (sm->tdls_disabled || !sm->tdls_supported) + return "disabled"; + + for (peer = sm->tdls; peer; peer = peer->next) { + if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0) + break; } + + if (peer == NULL) + return "peer does not exist"; + + if (!peer->tpk_success) + return "peer not connected"; + + return "connected"; } @@ -845,11 +898,7 @@ * Request the driver to disable the direct link and clear associated * keys. */ - wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, src_addr); - - /* clear the Peerkey statemachine */ - wpa_tdls_peer_free(sm, peer); - + wpa_tdls_disable_peer_link(sm, peer); return 0; } @@ -874,10 +923,20 @@ static struct wpa_tdls_peer * -wpa_tdls_add_peer(struct wpa_sm *sm, const u8 *addr) +wpa_tdls_add_peer(struct wpa_sm *sm, const u8 *addr, int *existing) { struct wpa_tdls_peer *peer; + if (existing) + *existing = 0; + for (peer = sm->tdls; peer; peer = peer->next) { + if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0) { + if (existing) + *existing = 1; + return peer; /* re-use existing entry */ + } + } + wpa_printf(MSG_INFO, "TDLS: Creating peer entry for " MACSTR, MAC2STR(addr)); @@ -903,6 +962,7 @@ u8 *rbuf, *pos, *count_pos; u16 count; struct rsn_ie_hdr *hdr; + int status; if (!wpa_tdls_get_privacy(sm)) { wpa_printf(MSG_DEBUG, "TDLS: No security used on the link"); @@ -1063,11 +1123,11 @@ "Handshake Message 1 (peer " MACSTR ")", MAC2STR(peer->addr)); - wpa_tdls_tpk_send(sm, peer->addr, WLAN_TDLS_SETUP_REQUEST, 1, 0, - rbuf, pos - rbuf); + status = wpa_tdls_tpk_send(sm, peer->addr, WLAN_TDLS_SETUP_REQUEST, + 1, 0, rbuf, pos - rbuf); os_free(rbuf); - return 0; + return status; } @@ -1081,6 +1141,7 @@ u32 lifetime; struct wpa_tdls_timeoutie timeoutie; struct wpa_tdls_ftie *ftie; + int status; buf_len = 0; if (wpa_tdls_get_privacy(sm)) { @@ -1146,11 +1207,11 @@ (u8 *) &timeoutie, (u8 *) ftie, ftie->mic); skip_ies: - wpa_tdls_tpk_send(sm, src_addr, WLAN_TDLS_SETUP_RESPONSE, dtoken, 0, - rbuf, pos - rbuf); + status = wpa_tdls_tpk_send(sm, src_addr, WLAN_TDLS_SETUP_RESPONSE, + dtoken, 0, rbuf, pos - rbuf); os_free(rbuf); - return 0; + return status; } @@ -1164,6 +1225,7 @@ struct wpa_tdls_ftie *ftie; struct wpa_tdls_timeoutie timeoutie; u32 lifetime; + int status; buf_len = 0; if (wpa_tdls_get_privacy(sm)) { @@ -1227,11 +1289,11 @@ (u8 *) &timeoutie, (u8 *) ftie, ftie->mic); skip_ies: - wpa_tdls_tpk_send(sm, src_addr, WLAN_TDLS_SETUP_CONFIRM, dtoken, 0, - rbuf, pos - rbuf); + status = wpa_tdls_tpk_send(sm, src_addr, WLAN_TDLS_SETUP_CONFIRM, + dtoken, 0, rbuf, pos - rbuf); os_free(rbuf); - return 0; + return status; } @@ -1288,7 +1350,7 @@ return -1; } - peer = wpa_tdls_add_peer(sm, addr); + peer = wpa_tdls_add_peer(sm, addr, NULL); if (peer == NULL) return -1; @@ -1315,21 +1377,143 @@ wpa_printf(MSG_DEBUG, "TDLS: No supported rates received"); return -1; } + peer->supp_rates_len = merge_byte_arrays( + peer->supp_rates, sizeof(peer->supp_rates), + kde->supp_rates + 2, kde->supp_rates_len - 2, + kde->ext_supp_rates ? kde->ext_supp_rates + 2 : NULL, + kde->ext_supp_rates_len - 2); + return 0; +} + + +static int copy_peer_ht_capab(const struct wpa_eapol_ie_parse *kde, + struct wpa_tdls_peer *peer) +{ + if (!kde->ht_capabilities || + kde->ht_capabilities_len < + sizeof(struct ieee80211_ht_capabilities) ) { + wpa_printf(MSG_DEBUG, "TDLS: No supported ht capabilities " + "received"); + return 0; + } + + if (!peer->ht_capabilities) { + peer->ht_capabilities = + os_zalloc(sizeof(struct ieee80211_ht_capabilities)); + if (peer->ht_capabilities == NULL) + return -1; + } + + os_memcpy(peer->ht_capabilities, kde->ht_capabilities, + sizeof(struct ieee80211_ht_capabilities)); + wpa_hexdump(MSG_DEBUG, "TDLS: Peer HT capabilities", + (u8 *) peer->ht_capabilities, + sizeof(struct ieee80211_ht_capabilities)); + + return 0; +} + + +static int copy_peer_vht_capab(const struct wpa_eapol_ie_parse *kde, + struct wpa_tdls_peer *peer) +{ + if (!kde->vht_capabilities || + kde->vht_capabilities_len < + sizeof(struct ieee80211_vht_capabilities) ) { + wpa_printf(MSG_DEBUG, "TDLS: No supported vht capabilities " + "received"); + return 0; + } + + if (!peer->vht_capabilities) { + peer->vht_capabilities = + os_zalloc(sizeof(struct ieee80211_vht_capabilities)); + if (peer->vht_capabilities == NULL) + return -1; + } + + os_memcpy(peer->vht_capabilities, kde->vht_capabilities, + sizeof(struct ieee80211_vht_capabilities)); + wpa_hexdump(MSG_DEBUG, "TDLS: Peer VHT capabilities", + (u8 *) peer->vht_capabilities, + sizeof(struct ieee80211_vht_capabilities)); + + return 0; +} + + +static int copy_peer_ext_capab(const struct wpa_eapol_ie_parse *kde, + struct wpa_tdls_peer *peer) +{ + if (!kde->ext_capab) { + wpa_printf(MSG_DEBUG, "TDLS: No extended capabilities " + "received"); + return 0; + } - peer->supp_rates_len = kde->supp_rates_len - 2; - if (peer->supp_rates_len > IEEE80211_MAX_SUPP_RATES) - peer->supp_rates_len = IEEE80211_MAX_SUPP_RATES; - os_memcpy(peer->supp_rates, kde->supp_rates + 2, peer->supp_rates_len); - - if (kde->ext_supp_rates) { - int clen = kde->ext_supp_rates_len - 2; - if (peer->supp_rates_len + clen > IEEE80211_MAX_SUPP_RATES) - clen = IEEE80211_MAX_SUPP_RATES - peer->supp_rates_len; - os_memcpy(peer->supp_rates + peer->supp_rates_len, - kde->ext_supp_rates + 2, clen); - peer->supp_rates_len += clen; + if (!peer->ext_capab || peer->ext_capab_len < kde->ext_capab_len - 2) { + /* Need to allocate buffer to fit the new information */ + os_free(peer->ext_capab); + peer->ext_capab = os_zalloc(kde->ext_capab_len - 2); + if (peer->ext_capab == NULL) + return -1; + } + + peer->ext_capab_len = kde->ext_capab_len - 2; + os_memcpy(peer->ext_capab, kde->ext_capab + 2, peer->ext_capab_len); + + return 0; +} + + +static int copy_peer_supp_channels(const struct wpa_eapol_ie_parse *kde, + struct wpa_tdls_peer *peer) +{ + if (!kde->supp_channels) { + wpa_printf(MSG_DEBUG, "TDLS: No supported channels received"); + return 0; } + if (!peer->supp_channels || + peer->supp_channels_len < kde->supp_channels_len) { + os_free(peer->supp_channels); + peer->supp_channels = os_zalloc(kde->supp_channels_len); + if (peer->supp_channels == NULL) + return -1; + } + + peer->supp_channels_len = kde->supp_channels_len; + + os_memcpy(peer->supp_channels, kde->supp_channels, + peer->supp_channels_len); + wpa_hexdump(MSG_DEBUG, "TDLS: Peer Supported Channels", + (u8 *) peer->supp_channels, peer->supp_channels_len); + return 0; +} + + +static int copy_peer_supp_oper_classes(const struct wpa_eapol_ie_parse *kde, + struct wpa_tdls_peer *peer) +{ + if (!kde->supp_oper_classes) { + wpa_printf(MSG_DEBUG, "TDLS: No supported operating classes received"); + return 0; + } + + if (!peer->supp_oper_classes || + peer->supp_oper_classes_len < kde->supp_oper_classes_len) { + os_free(peer->supp_oper_classes); + peer->supp_oper_classes = os_zalloc(kde->supp_oper_classes_len); + if (peer->supp_oper_classes == NULL) + return -1; + } + + peer->supp_oper_classes_len = kde->supp_oper_classes_len; + os_memcpy(peer->supp_oper_classes, kde->supp_oper_classes, + peer->supp_oper_classes_len); + wpa_hexdump(MSG_DEBUG, "TDLS: Peer Supported Operating Classes", + (u8 *) peer->supp_oper_classes, + peer->supp_oper_classes_len); return 0; } @@ -1369,17 +1553,42 @@ wpa_printf(MSG_INFO, "TDLS: Dialog Token in TPK M1 %d", dtoken); - for (peer = sm->tdls; peer; peer = peer->next) { - if (os_memcmp(peer->addr, src_addr, ETH_ALEN) == 0) { - existing_peer = 1; - break; + peer = wpa_tdls_add_peer(sm, src_addr, &existing_peer); + if (peer == NULL) + goto error; + + /* If found, use existing entry instead of adding a new one; + * how to handle the case where both ends initiate at the + * same time? */ + if (existing_peer) { + if (peer->tpk_success) { + wpa_printf(MSG_DEBUG, "TDLS: TDLS Setup Request while " + "direct link is enabled - tear down the " + "old link first"); + wpa_tdls_disable_peer_link(sm, peer); } - } - if (peer == NULL) { - peer = wpa_tdls_add_peer(sm, src_addr); - if (peer == NULL) - goto error; + /* + * An entry is already present, so check if we already sent a + * TDLS Setup Request. If so, compare MAC addresses and let the + * STA with the lower MAC address continue as the initiator. + * The other negotiation is terminated. + */ + if (peer->initiator) { + if (os_memcmp(sm->own_addr, src_addr, ETH_ALEN) < 0) { + wpa_printf(MSG_DEBUG, "TDLS: Discard request " + "from peer with higher address " + MACSTR, MAC2STR(src_addr)); + return -1; + } else { + wpa_printf(MSG_DEBUG, "TDLS: Accept request " + "from peer with lower address " + MACSTR " (terminate previously " + "initiated negotiation", + MAC2STR(src_addr)); + wpa_tdls_disable_peer_link(sm, peer); + } + } } /* capability information */ @@ -1412,17 +1621,30 @@ if (copy_supp_rates(&kde, peer) < 0) goto error; + if (copy_peer_ht_capab(&kde, peer) < 0) + goto error; + + if (copy_peer_vht_capab(&kde, peer) < 0) + goto error; + + if (copy_peer_ext_capab(&kde, peer) < 0) + goto error; + + if (copy_peer_supp_channels(&kde, peer) < 0) + goto error; + + if (copy_peer_supp_oper_classes(&kde, peer) < 0) + goto error; + + peer->qos_info = kde.qosinfo; + + peer->aid = kde.aid; + #ifdef CONFIG_TDLS_TESTING if (tdls_testing & TDLS_TESTING_CONCURRENT_INIT) { - for (peer = sm->tdls; peer; peer = peer->next) { - if (os_memcmp(peer->addr, src_addr, ETH_ALEN) == 0) - break; - } - if (peer == NULL) { - peer = wpa_tdls_add_peer(sm, src_addr); - if (peer == NULL) - goto error; - } + peer = wpa_tdls_add_peer(sm, src_addr, NULL); + if (peer == NULL) + goto error; wpa_printf(MSG_DEBUG, "TDLS: Testing concurrent initiation of " "TDLS setup - send own request"); peer->initiator = 1; @@ -1508,52 +1730,6 @@ } skip_rsn: - /* If found, use existing entry instead of adding a new one; - * how to handle the case where both ends initiate at the - * same time? */ - if (existing_peer) { - if (peer->tpk_success) { - wpa_printf(MSG_DEBUG, "TDLS: TDLS Setup Request while " - "direct link is enabled - tear down the " - "old link first"); -#if 0 - /* TODO: Disabling the link would be more proper - * operation here, but it seems to trigger a race with - * some drivers handling the new request frame. */ - wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, src_addr); -#else - if (sm->tdls_external_setup) - wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, - src_addr); - else - wpa_tdls_del_key(sm, peer); -#endif - wpa_tdls_peer_free(sm, peer); - } - - /* - * An entry is already present, so check if we already sent a - * TDLS Setup Request. If so, compare MAC addresses and let the - * STA with the lower MAC address continue as the initiator. - * The other negotiation is terminated. - */ - if (peer->initiator) { - if (os_memcmp(sm->own_addr, src_addr, ETH_ALEN) < 0) { - wpa_printf(MSG_DEBUG, "TDLS: Discard request " - "from peer with higher address " - MACSTR, MAC2STR(src_addr)); - return -1; - } else { - wpa_printf(MSG_DEBUG, "TDLS: Accept request " - "from peer with lower address " - MACSTR " (terminate previously " - "initiated negotiation", - MAC2STR(src_addr)); - wpa_tdls_peer_free(sm, peer); - } - } - } - #ifdef CONFIG_TDLS_TESTING if (tdls_testing & TDLS_TESTING_CONCURRENT_INIT) { if (os_memcmp(sm->own_addr, peer->addr, ETH_ALEN) < 0) { @@ -1578,16 +1754,27 @@ } ftie = (struct wpa_tdls_ftie *) kde.ftie; - os_memcpy(peer->inonce, ftie->Snonce, WPA_NONCE_LEN); os_memcpy(peer->rsnie_i, kde.rsn_ie, kde.rsn_ie_len); peer->rsnie_i_len = kde.rsn_ie_len; peer->cipher = cipher; - if (os_get_random(peer->rnonce, WPA_NONCE_LEN)) { - wpa_msg(sm->ctx->ctx, MSG_WARNING, - "TDLS: Failed to get random data for responder nonce"); - wpa_tdls_peer_free(sm, peer); - goto error; + if (os_memcmp(peer->inonce, ftie->Snonce, WPA_NONCE_LEN) != 0) { + /* + * There is no point in updating the RNonce for every obtained + * TPK M1 frame (e.g., retransmission due to timeout) with the + * same INonce (SNonce in FTIE). However, if the TPK M1 is + * retransmitted with a different INonce, update the RNonce + * since this is for a new TDLS session. + */ + wpa_printf(MSG_DEBUG, + "TDLS: New TPK M1 INonce - generate new RNonce"); + os_memcpy(peer->inonce, ftie->Snonce, WPA_NONCE_LEN); + if (os_get_random(peer->rnonce, WPA_NONCE_LEN)) { + wpa_msg(sm->ctx->ctx, MSG_WARNING, + "TDLS: Failed to get random data for responder nonce"); + wpa_tdls_peer_free(sm, peer); + goto error; + } } #if 0 @@ -1641,11 +1828,13 @@ skip_rsn_check: /* add the peer to the driver as a "setup in progress" peer */ - wpa_sm_tdls_peer_addset(sm, peer->addr, 1, 0, NULL, 0); + wpa_sm_tdls_peer_addset(sm, peer->addr, 1, 0, 0, NULL, 0, NULL, NULL, 0, + NULL, 0, NULL, 0, NULL, 0); + peer->tpk_in_progress = 1; wpa_printf(MSG_DEBUG, "TDLS: Sending TDLS Setup Response / TPK M2"); if (wpa_tdls_send_tpk_m2(sm, src_addr, dtoken, lnkid, peer) < 0) { - wpa_tdls_disable_link(sm, peer->addr); + wpa_tdls_disable_peer_link(sm, peer); goto error; } @@ -1658,9 +1847,10 @@ } -static void wpa_tdls_enable_link(struct wpa_sm *sm, struct wpa_tdls_peer *peer) +static int wpa_tdls_enable_link(struct wpa_sm *sm, struct wpa_tdls_peer *peer) { peer->tpk_success = 1; + peer->tpk_in_progress = 0; eloop_cancel_timeout(wpa_tdls_tpk_timeout, sm, peer); if (wpa_tdls_get_privacy(sm)) { u32 lifetime = peer->lifetime; @@ -1681,11 +1871,28 @@ #endif /* CONFIG_TDLS_TESTING */ } - /* add supported rates and capabilities to the TDLS peer */ - wpa_sm_tdls_peer_addset(sm, peer->addr, 0, peer->capability, - peer->supp_rates, peer->supp_rates_len); + /* add supported rates, capabilities, and qos_info to the TDLS peer */ + if (wpa_sm_tdls_peer_addset(sm, peer->addr, 0, peer->aid, + peer->capability, + peer->supp_rates, peer->supp_rates_len, + peer->ht_capabilities, + peer->vht_capabilities, + peer->qos_info, peer->ext_capab, + peer->ext_capab_len, + peer->supp_channels, + peer->supp_channels_len, + peer->supp_oper_classes, + peer->supp_oper_classes_len) < 0) + return -1; + + if (peer->reconfig_key && wpa_tdls_set_key(sm, peer) < 0) { + wpa_printf(MSG_INFO, "TDLS: Could not configure key to the " + "driver"); + return -1; + } + peer->reconfig_key = 0; - wpa_sm_tdls_oper(sm, TDLS_ENABLE_LINK, peer->addr); + return wpa_sm_tdls_oper(sm, TDLS_ENABLE_LINK, peer->addr); } @@ -1704,6 +1911,7 @@ int ielen; u16 status; const u8 *pos; + int ret = 0; wpa_printf(MSG_DEBUG, "TDLS: Received TDLS Setup Response / TPK M2 " "(Peer " MACSTR ")", MAC2STR(src_addr)); @@ -1716,10 +1924,23 @@ "TPK M2: " MACSTR, MAC2STR(src_addr)); return -1; } + if (!peer->initiator) { + /* + * This may happen if both devices try to initiate TDLS at the + * same time and we accept the TPK M1 from the peer in + * wpa_tdls_process_tpk_m1() and clear our previous state. + */ + wpa_printf(MSG_INFO, "TDLS: We were not the initiator, so " + "ignore TPK M2 from " MACSTR, MAC2STR(src_addr)); + return -1; + } wpa_tdls_tpk_retry_timeout_cancel(sm, peer, WLAN_TDLS_SETUP_REQUEST); - if (len < 3 + 2 + 1) + if (len < 3 + 2 + 1) { + wpa_tdls_disable_peer_link(sm, peer); return -1; + } + pos = buf; pos += 1 /* pkt_type */ + 1 /* Category */ + 1 /* Action */; status = WPA_GET_LE16(pos); @@ -1728,8 +1949,7 @@ if (status != WLAN_STATUS_SUCCESS) { wpa_printf(MSG_INFO, "TDLS: Status code in TPK M2: %u", status); - if (sm->tdls_external_setup) - wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, src_addr); + wpa_tdls_disable_peer_link(sm, peer); return -1; } @@ -1740,8 +1960,10 @@ wpa_printf(MSG_DEBUG, "TDLS: Dialog Token in TPK M2 %d", dtoken); - if (len < 3 + 2 + 1 + 2) + if (len < 3 + 2 + 1 + 2) { + wpa_tdls_disable_peer_link(sm, peer); return -1; + } /* capability information */ peer->capability = WPA_GET_LE16(pos); @@ -1779,6 +2001,25 @@ if (copy_supp_rates(&kde, peer) < 0) goto error; + if (copy_peer_ht_capab(&kde, peer) < 0) + goto error; + + if (copy_peer_vht_capab(&kde, peer) < 0) + goto error; + + if (copy_peer_ext_capab(&kde, peer) < 0) + goto error; + + if (copy_peer_supp_channels(&kde, peer) < 0) + goto error; + + if (copy_peer_supp_oper_classes(&kde, peer) < 0) + goto error; + + peer->qos_info = kde.qosinfo; + + peer->aid = kde.aid; + if (!wpa_tdls_get_privacy(sm)) { peer->rsnie_p_len = 0; peer->cipher = WPA_CIPHER_NONE; @@ -1869,30 +2110,50 @@ (u8 *) timeoutie, ftie) < 0) { /* Discard the frame */ wpa_tdls_del_key(sm, peer); - wpa_tdls_peer_free(sm, peer); - if (sm->tdls_external_setup) - wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, src_addr); + wpa_tdls_disable_peer_link(sm, peer); return -1; } - wpa_tdls_set_key(sm, peer); + if (wpa_tdls_set_key(sm, peer) < 0) { + /* + * Some drivers may not be able to config the key prior to full + * STA entry having been configured. + */ + wpa_printf(MSG_DEBUG, "TDLS: Try to configure TPK again after " + "STA entry is complete"); + peer->reconfig_key = 1; + } skip_rsn: peer->dtoken = dtoken; wpa_printf(MSG_DEBUG, "TDLS: Sending TDLS Setup Confirm / " "TPK Handshake Message 3"); - wpa_tdls_send_tpk_m3(sm, src_addr, dtoken, lnkid, peer); - - wpa_tdls_enable_link(sm, peer); + if (wpa_tdls_send_tpk_m3(sm, src_addr, dtoken, lnkid, peer) < 0) { + wpa_tdls_disable_peer_link(sm, peer); + return -1; + } - return 0; + if (!peer->tpk_success) { + /* + * Enable Link only when tpk_success is 0, signifying that this + * processing of TPK M2 frame is not because of a retransmission + * during TDLS setup handshake. + */ + ret = wpa_tdls_enable_link(sm, peer); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "TDLS: Could not enable link"); + wpa_tdls_do_teardown( + sm, peer, + WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED); + } + } + return ret; error: wpa_tdls_send_error(sm, src_addr, WLAN_TDLS_SETUP_CONFIRM, dtoken, status); - if (sm->tdls_external_setup) - wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, src_addr); + wpa_tdls_disable_peer_link(sm, peer); return -1; } @@ -1909,6 +2170,7 @@ u16 status; const u8 *pos; u32 lifetime; + int ret = 0; wpa_printf(MSG_DEBUG, "TDLS: Received TDLS Setup Confirm / TPK M3 " "(Peer " MACSTR ")", MAC2STR(src_addr)); @@ -1924,7 +2186,7 @@ wpa_tdls_tpk_retry_timeout_cancel(sm, peer, WLAN_TDLS_SETUP_RESPONSE); if (len < 3 + 3) - return -1; + goto error; pos = buf; pos += 1 /* pkt_type */ + 1 /* Category */ + 1 /* Action */; @@ -1933,21 +2195,19 @@ if (status != 0) { wpa_printf(MSG_INFO, "TDLS: Status code in TPK M3: %u", status); - if (sm->tdls_external_setup) - wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, src_addr); - return -1; + goto error; } pos += 2 /* status code */ + 1 /* dialog token */; ielen = len - (pos - buf); /* start of IE in buf */ if (wpa_supplicant_parse_ies((const u8 *) pos, ielen, &kde) < 0) { wpa_printf(MSG_INFO, "TDLS: Failed to parse KDEs in TPK M3"); - return -1; + goto error; } if (kde.lnkid == NULL || kde.lnkid_len < 3 * ETH_ALEN) { wpa_printf(MSG_INFO, "TDLS: No Link Identifier IE in TPK M3"); - return -1; + goto error; } wpa_hexdump(MSG_DEBUG, "TDLS: Link ID Received from TPK M3", (u8 *) kde.lnkid, kde.lnkid_len); @@ -1955,7 +2215,7 @@ if (os_memcmp(sm->bssid, lnkid->bssid, ETH_ALEN) != 0) { wpa_printf(MSG_INFO, "TDLS: TPK M3 from diff BSS"); - return -1; + goto error; } if (!wpa_tdls_get_privacy(sm)) @@ -1963,7 +2223,7 @@ if (kde.ftie == NULL || kde.ftie_len < sizeof(*ftie)) { wpa_printf(MSG_INFO, "TDLS: No FTIE in TPK M3"); - return -1; + goto error; } wpa_hexdump(MSG_DEBUG, "TDLS: FTIE Received from TPK M3", kde.ftie, sizeof(*ftie)); @@ -1971,7 +2231,7 @@ if (kde.rsn_ie == NULL) { wpa_printf(MSG_INFO, "TDLS: No RSN IE in TPK M3"); - return -1; + goto error; } wpa_hexdump(MSG_DEBUG, "TDLS: RSN IE Received from TPK M3", kde.rsn_ie, kde.rsn_ie_len); @@ -1979,24 +2239,24 @@ os_memcmp(kde.rsn_ie, peer->rsnie_p, peer->rsnie_p_len) != 0) { wpa_printf(MSG_INFO, "TDLS: RSN IE in TPK M3 does not match " "with the one sent in TPK M2"); - return -1; + goto error; } if (!os_memcmp(peer->rnonce, ftie->Anonce, WPA_NONCE_LEN) == 0) { wpa_printf(MSG_INFO, "TDLS: FTIE ANonce in TPK M3 does " "not match with FTIE ANonce used in TPK M2"); - return -1; + goto error; } if (!os_memcmp(peer->inonce, ftie->Snonce, WPA_NONCE_LEN) == 0) { wpa_printf(MSG_INFO, "TDLS: FTIE SNonce in TPK M3 does not " "match with FTIE SNonce used in TPK M1"); - return -1; + goto error; } if (kde.key_lifetime == NULL) { wpa_printf(MSG_INFO, "TDLS: No Key Lifetime IE in TPK M3"); - return -1; + goto error; } timeoutie = (struct wpa_tdls_timeoutie *) kde.key_lifetime; wpa_hexdump(MSG_DEBUG, "TDLS: Timeout IE Received from TPK M3", @@ -2007,25 +2267,44 @@ if (lifetime != peer->lifetime) { wpa_printf(MSG_INFO, "TDLS: Unexpected TPK lifetime %u in " "TPK M3 (expected %u)", lifetime, peer->lifetime); - if (sm->tdls_external_setup) - wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, src_addr); - return -1; + goto error; } if (wpa_supplicant_verify_tdls_mic(3, peer, (u8 *) lnkid, (u8 *) timeoutie, ftie) < 0) { wpa_tdls_del_key(sm, peer); - wpa_tdls_peer_free(sm, peer); - return -1; + goto error; } - if (wpa_tdls_set_key(sm, peer) < 0) - return -1; + if (wpa_tdls_set_key(sm, peer) < 0) { + /* + * Some drivers may not be able to config the key prior to full + * STA entry having been configured. + */ + wpa_printf(MSG_DEBUG, "TDLS: Try to configure TPK again after " + "STA entry is complete"); + peer->reconfig_key = 1; + } skip_rsn: - wpa_tdls_enable_link(sm, peer); - - return 0; + if (!peer->tpk_success) { + /* + * Enable Link only when tpk_success is 0, signifying that this + * processing of TPK M3 frame is not because of a retransmission + * during TDLS setup handshake. + */ + ret = wpa_tdls_enable_link(sm, peer); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "TDLS: Could not enable link"); + wpa_tdls_do_teardown( + sm, peer, + WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED); + } + } + return ret; +error: + wpa_tdls_disable_peer_link(sm, peer); + return -1; } @@ -2075,26 +2354,25 @@ return -1; } - /* Find existing entry and if found, use that instead of adding - * a new one */ - for (peer = sm->tdls; peer; peer = peer->next) { - if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0) - break; - } + peer = wpa_tdls_add_peer(sm, addr, NULL); + if (peer == NULL) + return -1; - if (peer == NULL) { - peer = wpa_tdls_add_peer(sm, addr); - if (peer == NULL) - return -1; + if (peer->tpk_in_progress) { + wpa_printf(MSG_DEBUG, "TDLS: Setup is already in progress with the peer"); + return 0; } peer->initiator = 1; /* add the peer to the driver as a "setup in progress" peer */ - wpa_sm_tdls_peer_addset(sm, peer->addr, 1, 0, NULL, 0); + wpa_sm_tdls_peer_addset(sm, peer->addr, 1, 0, 0, NULL, 0, NULL, NULL, 0, + NULL, 0, NULL, 0, NULL, 0); + + peer->tpk_in_progress = 1; if (wpa_tdls_send_tpk_m1(sm, peer) < 0) { - wpa_tdls_disable_link(sm, peer->addr); + wpa_tdls_disable_peer_link(sm, peer); return -1; } @@ -2102,12 +2380,12 @@ } -int wpa_tdls_reneg(struct wpa_sm *sm, const u8 *addr) +void wpa_tdls_remove(struct wpa_sm *sm, const u8 *addr) { struct wpa_tdls_peer *peer; if (sm->tdls_disabled || !sm->tdls_supported) - return -1; + return; for (peer = sm->tdls; peer; peer = peer->next) { if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0) @@ -2115,17 +2393,15 @@ } if (peer == NULL || !peer->tpk_success) - return -1; + return; if (sm->tdls_external_setup) { /* * Disable previous link to allow renegotiation to be completed * on AP path. */ - wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, peer->addr); + wpa_tdls_disable_peer_link(sm, peer); } - - return wpa_tdls_start(sm, addr); } @@ -2208,7 +2484,9 @@ if (sm == NULL) return -1; - sm->l2_tdls = l2_packet_init(sm->ifname, sm->own_addr, + sm->l2_tdls = l2_packet_init(sm->bridge_ifname ? sm->bridge_ifname : + sm->ifname, + sm->own_addr, ETH_P_80211_ENCAP, wpa_supplicant_rx_tdls, sm, 0); if (sm->l2_tdls == NULL) { @@ -2236,6 +2514,28 @@ } +void wpa_tdls_teardown_peers(struct wpa_sm *sm) +{ + struct wpa_tdls_peer *peer; + + peer = sm->tdls; + + wpa_printf(MSG_DEBUG, "TDLS: Tear down peers"); + + while (peer) { + wpa_printf(MSG_DEBUG, "TDLS: Tear down peer " MACSTR, + MAC2STR(peer->addr)); + if (sm->tdls_external_setup) + wpa_tdls_send_teardown(sm, peer->addr, + WLAN_REASON_DEAUTH_LEAVING); + else + wpa_sm_tdls_oper(sm, TDLS_TEARDOWN, peer->addr); + + peer = peer->next; + } +} + + static void wpa_tdls_remove_peers(struct wpa_sm *sm) { struct wpa_tdls_peer *peer, *tmp; diff -Nru wpa-1.0/src/rsn_supp/wpa.c wpa-2.1/src/rsn_supp/wpa.c --- wpa-1.0/src/rsn_supp/wpa.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/rsn_supp/wpa.c 2014-02-04 11:23:35.000000000 +0000 @@ -1,15 +1,9 @@ /* * WPA Supplicant - WPA state machine and EAPOL-Key processing - * Copyright (c) 2003-2010, Jouni Malinen + * Copyright (c) 2003-2012, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -97,7 +91,7 @@ if (wpa_key_mgmt_ft(sm->key_mgmt) || wpa_key_mgmt_sha256(sm->key_mgmt)) ver = WPA_KEY_INFO_TYPE_AES_128_CMAC; - else if (sm->pairwise_cipher == WPA_CIPHER_CCMP) + else if (sm->pairwise_cipher != WPA_CIPHER_TKIP) ver = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES; else ver = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4; @@ -151,7 +145,8 @@ * not have enough time to get the association information * event before receiving this 1/4 message, so try to find a * matching PMKSA cache entry here. */ - sm->cur_pmksa = pmksa_cache_get(sm->pmksa, src_addr, pmkid); + sm->cur_pmksa = pmksa_cache_get(sm->pmksa, src_addr, pmkid, + NULL); if (sm->cur_pmksa) { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: found matching PMKID from PMKSA cache"); @@ -195,22 +190,29 @@ #endif /* CONFIG_IEEE80211R */ } if (res == 0) { + struct rsn_pmksa_cache_entry *sa = NULL; wpa_hexdump_key(MSG_DEBUG, "WPA: PMK from EAPOL state " "machines", sm->pmk, pmk_len); sm->pmk_len = pmk_len; if (sm->proto == WPA_PROTO_RSN && !wpa_key_mgmt_ft(sm->key_mgmt)) { - pmksa_cache_add(sm->pmksa, sm->pmk, pmk_len, - src_addr, sm->own_addr, - sm->network_ctx, sm->key_mgmt); + sa = pmksa_cache_add(sm->pmksa, + sm->pmk, pmk_len, + src_addr, sm->own_addr, + sm->network_ctx, + sm->key_mgmt); } if (!sm->cur_pmksa && pmkid && - pmksa_cache_get(sm->pmksa, src_addr, pmkid)) { + pmksa_cache_get(sm->pmksa, src_addr, pmkid, NULL)) + { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: the new PMK matches with the " "PMKID"); abort_cached = 0; } + + if (!sm->cur_pmksa) + sm->cur_pmksa = sa; } else { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: Failed to get master session key from " @@ -354,7 +356,7 @@ const struct wpa_eapol_key *key, struct wpa_ptk *ptk) { - size_t ptk_len = sm->pairwise_cipher == WPA_CIPHER_CCMP ? 48 : 64; + size_t ptk_len = sm->pairwise_cipher != WPA_CIPHER_TKIP ? 48 : 64; #ifdef CONFIG_IEEE80211R if (wpa_key_mgmt_ft(sm->key_mgmt)) return wpa_derive_ptk_ft(sm, src_addr, key, ptk, ptk_len); @@ -377,6 +379,8 @@ struct wpa_ptk *ptk; u8 buf[8]; int res; + u8 *kde, *kde_buf = NULL; + size_t kde_len; if (wpa_sm_get_network_ctx(sm) == NULL) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: No SSID info " @@ -390,7 +394,6 @@ os_memset(&ie, 0, sizeof(ie)); -#ifndef CONFIG_NO_WPA2 if (sm->proto == WPA_PROTO_RSN) { /* RSN: msg 1/4 should contain PMKID for the selected PMK */ const u8 *_buf = (const u8 *) (key + 1); @@ -403,7 +406,6 @@ "Authenticator", ie.pmkid, PMKID_LEN); } } -#endif /* CONFIG_NO_WPA2 */ res = wpa_supplicant_get_pmk(sm, src_addr, ie.pmkid); if (res == -2) { @@ -435,15 +437,39 @@ os_memcpy(ptk->u.auth.rx_mic_key, buf, 8); sm->tptk_set = 1; + kde = sm->assoc_wpa_ie; + kde_len = sm->assoc_wpa_ie_len; + +#ifdef CONFIG_P2P + if (sm->p2p) { + kde_buf = os_malloc(kde_len + 2 + RSN_SELECTOR_LEN + 1); + if (kde_buf) { + u8 *pos; + wpa_printf(MSG_DEBUG, "P2P: Add IP Address Request KDE " + "into EAPOL-Key 2/4"); + os_memcpy(kde_buf, kde, kde_len); + kde = kde_buf; + pos = kde + kde_len; + *pos++ = WLAN_EID_VENDOR_SPECIFIC; + *pos++ = RSN_SELECTOR_LEN + 1; + RSN_SELECTOR_PUT(pos, WFA_KEY_DATA_IP_ADDR_REQ); + pos += RSN_SELECTOR_LEN; + *pos++ = 0x01; + kde_len = pos - kde; + } + } +#endif /* CONFIG_P2P */ + if (wpa_supplicant_send_2_of_4(sm, sm->bssid, key, ver, sm->snonce, - sm->assoc_wpa_ie, sm->assoc_wpa_ie_len, - ptk)) + kde, kde_len, ptk)) goto failed; + os_free(kde_buf); os_memcpy(sm->anonce, key->key_nonce, WPA_NONCE_LEN); return; failed: + os_free(kde_buf); wpa_sm_deauthenticate(sm, WLAN_REASON_UNSPECIFIED); } @@ -518,28 +544,23 @@ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Installing PTK to the driver"); - switch (sm->pairwise_cipher) { - case WPA_CIPHER_CCMP: - alg = WPA_ALG_CCMP; - keylen = 16; - rsclen = 6; - break; - case WPA_CIPHER_TKIP: - alg = WPA_ALG_TKIP; - keylen = 32; - rsclen = 6; - break; - case WPA_CIPHER_NONE: + if (sm->pairwise_cipher == WPA_CIPHER_NONE) { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Pairwise Cipher " "Suite: NONE - do not use pairwise keys"); return 0; - default: + } + + if (!wpa_cipher_valid_pairwise(sm->pairwise_cipher)) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: Unsupported pairwise cipher %d", sm->pairwise_cipher); return -1; } + alg = wpa_cipher_to_alg(sm->pairwise_cipher); + keylen = wpa_cipher_key_len(sm->pairwise_cipher); + rsclen = wpa_cipher_rsc_len(sm->pairwise_cipher); + if (sm->proto == WPA_PROTO_RSN) { key_rsc = null_rsc; } else { @@ -572,55 +593,25 @@ int *key_rsc_len, enum wpa_alg *alg) { - int ret = 0; + int klen; - switch (group_cipher) { - case WPA_CIPHER_CCMP: - if (keylen != 16 || maxkeylen < 16) { - ret = -1; - break; - } - *key_rsc_len = 6; - *alg = WPA_ALG_CCMP; - break; - case WPA_CIPHER_TKIP: - if (keylen != 32 || maxkeylen < 32) { - ret = -1; - break; - } - *key_rsc_len = 6; - *alg = WPA_ALG_TKIP; - break; - case WPA_CIPHER_WEP104: - if (keylen != 13 || maxkeylen < 13) { - ret = -1; - break; - } - *key_rsc_len = 0; - *alg = WPA_ALG_WEP; - break; - case WPA_CIPHER_WEP40: - if (keylen != 5 || maxkeylen < 5) { - ret = -1; - break; - } - *key_rsc_len = 0; - *alg = WPA_ALG_WEP; - break; - default: + *alg = wpa_cipher_to_alg(group_cipher); + if (*alg == WPA_ALG_NONE) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: Unsupported Group Cipher %d", group_cipher); return -1; } + *key_rsc_len = wpa_cipher_rsc_len(group_cipher); - if (ret < 0 ) { + klen = wpa_cipher_key_len(group_cipher); + if (keylen != klen || maxkeylen < klen) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: Unsupported %s Group Cipher key length %d (%d)", wpa_cipher_txt(group_cipher), keylen, maxkeylen); + return -1; } - - return ret; + return 0; } @@ -697,7 +688,6 @@ const u8 *gtk, size_t gtk_len, int key_info) { -#ifndef CONFIG_NO_WPA2 struct wpa_gtk_data gd; /* @@ -724,10 +714,11 @@ os_memcpy(gd.gtk, gtk, gtk_len); gd.gtk_len = gtk_len; - if (wpa_supplicant_check_group_cipher(sm, sm->group_cipher, - gtk_len, gtk_len, - &gd.key_rsc_len, &gd.alg) || - wpa_supplicant_install_gtk(sm, &gd, key->key_rsc)) { + if (sm->group_cipher != WPA_CIPHER_GTK_NOT_USED && + (wpa_supplicant_check_group_cipher(sm, sm->group_cipher, + gtk_len, gtk_len, + &gd.key_rsc_len, &gd.alg) || + wpa_supplicant_install_gtk(sm, &gd, key->key_rsc))) { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: Failed to install GTK"); return -1; @@ -736,9 +727,6 @@ wpa_supplicant_key_neg_complete(sm, sm->bssid, key_info & WPA_KEY_INFO_SECURE); return 0; -#else /* CONFIG_NO_WPA2 */ - return -1; -#endif /* CONFIG_NO_WPA2 */ } @@ -816,7 +804,7 @@ rsn_ie, rsn_ie_len); } - wpa_sm_disassociate(sm, WLAN_REASON_IE_IN_4WAY_DIFFERS); + wpa_sm_deauthenticate(sm, WLAN_REASON_IE_IN_4WAY_DIFFERS); } @@ -1120,25 +1108,22 @@ } keylen = WPA_GET_BE16(key->key_length); - switch (sm->pairwise_cipher) { - case WPA_CIPHER_CCMP: - if (keylen != 16) { - wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, - "WPA: Invalid CCMP key length %d (src=" MACSTR - ")", keylen, MAC2STR(sm->bssid)); - goto failed; - } - break; - case WPA_CIPHER_TKIP: - if (keylen != 32) { - wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, - "WPA: Invalid TKIP key length %d (src=" MACSTR - ")", keylen, MAC2STR(sm->bssid)); - goto failed; - } - break; + if (keylen != wpa_cipher_key_len(sm->pairwise_cipher)) { + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: Invalid %s key length %d (src=" MACSTR + ")", wpa_cipher_txt(sm->pairwise_cipher), keylen, + MAC2STR(sm->bssid)); + goto failed; } +#ifdef CONFIG_P2P + if (ie.ip_addr_alloc) { + os_memcpy(sm->p2p_ip_addr, ie.ip_addr_alloc, 3 * 4); + wpa_hexdump(MSG_DEBUG, "P2P: IP address info", + sm->p2p_ip_addr, sizeof(sm->p2p_ip_addr)); + } +#endif /* CONFIG_P2P */ + if (wpa_supplicant_send_4_of_4(sm, sm->bssid, key, ver, key_info, NULL, 0, &sm->ptk)) { goto failed; @@ -1162,7 +1147,10 @@ } wpa_sm_set_state(sm, WPA_GROUP_HANDSHAKE); - if (ie.gtk && + if (sm->group_cipher == WPA_CIPHER_GTK_NOT_USED) { + wpa_supplicant_key_neg_complete(sm, sm->bssid, + key_info & WPA_KEY_INFO_SECURE); + } else if (ie.gtk && wpa_supplicant_pairwise_gtk(sm, key, ie.gtk, ie.gtk_len, key_info) < 0) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, @@ -1176,7 +1164,8 @@ goto failed; } - wpa_sm_set_rekey_offload(sm); + if (ie.gtk) + wpa_sm_set_rekey_offload(sm); return; @@ -1397,13 +1386,14 @@ MAC2STR(sm->bssid), wpa_cipher_txt(sm->group_cipher)); wpa_sm_cancel_auth_timeout(sm); wpa_sm_set_state(sm, WPA_COMPLETED); - - wpa_sm_set_rekey_offload(sm); } else { wpa_supplicant_key_neg_complete(sm, sm->bssid, key_info & WPA_KEY_INFO_SECURE); } + + wpa_sm_set_rekey_offload(sm); + return; failed: @@ -1716,6 +1706,13 @@ } else goto out; } + if (sm->pairwise_cipher == WPA_CIPHER_GCMP && + ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) { + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, + "WPA: GCMP is used, but EAPOL-Key " + "descriptor version (%d) is not 2", ver); + goto out; + } #ifdef CONFIG_PEERKEY for (peerkey = sm->peerkey; peerkey; peerkey = peerkey->next) { @@ -1850,23 +1847,6 @@ #ifdef CONFIG_CTRL_IFACE -static int wpa_cipher_bits(int cipher) -{ - switch (cipher) { - case WPA_CIPHER_CCMP: - return 128; - case WPA_CIPHER_TKIP: - return 256; - case WPA_CIPHER_WEP104: - return 104; - case WPA_CIPHER_WEP40: - return 40; - default: - return 0; - } -} - - static u32 wpa_key_mgmt_suite(struct wpa_sm *sm) { switch (sm->key_mgmt) { @@ -1890,6 +1870,10 @@ case WPA_KEY_MGMT_PSK_SHA256: return RSN_AUTH_KEY_MGMT_PSK_SHA256; #endif /* CONFIG_IEEE80211W */ + case WPA_KEY_MGMT_CCKM: + return (sm->proto == WPA_PROTO_RSN ? + RSN_AUTH_KEY_MGMT_CCKM: + WPA_AUTH_KEY_MGMT_CCKM); case WPA_KEY_MGMT_WPA_NONE: return WPA_AUTH_KEY_MGMT_NONE; default: @@ -1898,30 +1882,6 @@ } -static u32 wpa_cipher_suite(struct wpa_sm *sm, int cipher) -{ - switch (cipher) { - case WPA_CIPHER_CCMP: - return (sm->proto == WPA_PROTO_RSN ? - RSN_CIPHER_SUITE_CCMP : WPA_CIPHER_SUITE_CCMP); - case WPA_CIPHER_TKIP: - return (sm->proto == WPA_PROTO_RSN ? - RSN_CIPHER_SUITE_TKIP : WPA_CIPHER_SUITE_TKIP); - case WPA_CIPHER_WEP104: - return (sm->proto == WPA_PROTO_RSN ? - RSN_CIPHER_SUITE_WEP104 : WPA_CIPHER_SUITE_WEP104); - case WPA_CIPHER_WEP40: - return (sm->proto == WPA_PROTO_RSN ? - RSN_CIPHER_SUITE_WEP40 : WPA_CIPHER_SUITE_WEP40); - case WPA_CIPHER_NONE: - return (sm->proto == WPA_PROTO_RSN ? - RSN_CIPHER_SUITE_NONE : WPA_CIPHER_SUITE_NONE); - default: - return 0; - } -} - - #define RSN_SUITE "%02x-%02x-%02x-%d" #define RSN_SUITE_ARG(s) \ ((s) >> 24) & 0xff, ((s) >> 16) & 0xff, ((s) >> 8) & 0xff, (s) & 0xff @@ -1969,7 +1929,7 @@ rsna ? "TRUE" : "FALSE", rsna ? "TRUE" : "FALSE", RSN_VERSION, - wpa_cipher_bits(sm->group_cipher), + wpa_cipher_key_len(sm->group_cipher) * 8, sm->dot11RSNAConfigPMKLifetime, sm->dot11RSNAConfigPMKReauthThreshold, sm->dot11RSNAConfigSATimeout); @@ -1989,12 +1949,16 @@ "dot11RSNAConfigNumberOfGTKSAReplayCounters=0\n" "dot11RSNA4WayHandshakeFailures=%u\n", RSN_SUITE_ARG(wpa_key_mgmt_suite(sm)), - RSN_SUITE_ARG(wpa_cipher_suite(sm, sm->pairwise_cipher)), - RSN_SUITE_ARG(wpa_cipher_suite(sm, sm->group_cipher)), + RSN_SUITE_ARG(wpa_cipher_to_suite(sm->proto, + sm->pairwise_cipher)), + RSN_SUITE_ARG(wpa_cipher_to_suite(sm->proto, + sm->group_cipher)), pmkid_txt, RSN_SUITE_ARG(wpa_key_mgmt_suite(sm)), - RSN_SUITE_ARG(wpa_cipher_suite(sm, sm->pairwise_cipher)), - RSN_SUITE_ARG(wpa_cipher_suite(sm, sm->group_cipher)), + RSN_SUITE_ARG(wpa_cipher_to_suite(sm->proto, + sm->pairwise_cipher)), + RSN_SUITE_ARG(wpa_cipher_to_suite(sm->proto, + sm->group_cipher)), sm->dot11RSNA4WayHandshakeFailures); if (ret >= 0 && (size_t) ret < buflen) len += ret; @@ -2005,25 +1969,40 @@ static void wpa_sm_pmksa_free_cb(struct rsn_pmksa_cache_entry *entry, - void *ctx, int replace) + void *ctx, enum pmksa_free_reason reason) { struct wpa_sm *sm = ctx; + int deauth = 0; + + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: PMKSA cache entry free_cb: " + MACSTR " reason=%d", MAC2STR(entry->aa), reason); - if (sm->cur_pmksa == entry || + if (sm->cur_pmksa == entry) { + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "RSN: %s current PMKSA entry", + reason == PMKSA_REPLACE ? "replaced" : "removed"); + pmksa_cache_clear_current(sm); + + /* + * If an entry is simply being replaced, there's no need to + * deauthenticate because it will be immediately re-added. + * This happens when EAP authentication is completed again + * (reauth or failed PMKSA caching attempt). + */ + if (reason != PMKSA_REPLACE) + deauth = 1; + } + + if (reason == PMKSA_EXPIRE && (sm->pmk_len == entry->pmk_len && os_memcmp(sm->pmk, entry->pmk, sm->pmk_len) == 0)) { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, - "RSN: removed current PMKSA entry"); - sm->cur_pmksa = NULL; - - if (replace) { - /* A new entry is being added, so no need to - * deauthenticate in this case. This happens when EAP - * authentication is completed again (reauth or failed - * PMKSA caching attempt). */ - return; - } + "RSN: deauthenticating due to expired PMK"); + pmksa_cache_clear_current(sm); + deauth = 1; + } + if (deauth) { os_memset(sm->pmk, 0, sizeof(sm->pmk)); wpa_sm_deauthenticate(sm, WLAN_REASON_UNSPECIFIED); } @@ -2141,6 +2120,10 @@ #ifdef CONFIG_TDLS wpa_tdls_assoc(sm); #endif /* CONFIG_TDLS */ + +#ifdef CONFIG_P2P + os_memset(sm->p2p_ip_addr, 0, sizeof(sm->p2p_ip_addr)); +#endif /* CONFIG_P2P */ } @@ -2153,7 +2136,9 @@ */ void wpa_sm_notify_disassoc(struct wpa_sm *sm) { + peerkey_deinit(sm); rsn_preauth_deinit(sm); + pmksa_cache_clear_current(sm); if (wpa_sm_get_state(sm) == WPA_4WAY_HANDSHAKE) sm->dot11RSNA4WayHandshakeFailures++; #ifdef CONFIG_TDLS @@ -2262,6 +2247,7 @@ } else sm->ssid_len = 0; sm->wpa_ptk_rekey = config->wpa_ptk_rekey; + sm->p2p = config->p2p; } else { sm->network_ctx = NULL; sm->peerkey_enabled = 0; @@ -2271,6 +2257,7 @@ sm->eap_conf_ctx = NULL; sm->ssid_len = 0; sm->wpa_ptk_rekey = 0; + sm->p2p = 0; } } @@ -2446,10 +2433,41 @@ if (ret < 0 || ret >= end - pos) return pos - buf; pos += ret; + + if (sm->mfp != NO_MGMT_FRAME_PROTECTION && sm->ap_rsn_ie) { + struct wpa_ie_data rsn; + if (wpa_parse_wpa_ie_rsn(sm->ap_rsn_ie, sm->ap_rsn_ie_len, &rsn) + >= 0 && + rsn.capabilities & (WPA_CAPABILITY_MFPR | + WPA_CAPABILITY_MFPC)) { + ret = os_snprintf(pos, end - pos, "pmf=%d\n", + (rsn.capabilities & + WPA_CAPABILITY_MFPR) ? 2 : 1); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + } + } + return pos - buf; } +int wpa_sm_pmf_enabled(struct wpa_sm *sm) +{ + struct wpa_ie_data rsn; + + if (sm->mfp == NO_MGMT_FRAME_PROTECTION || !sm->ap_rsn_ie) + return 0; + + if (wpa_parse_wpa_ie_rsn(sm->ap_rsn_ie, sm->ap_rsn_ie_len, &rsn) >= 0 && + rsn.capabilities & (WPA_CAPABILITY_MFPR | WPA_CAPABILITY_MFPC)) + return 1; + + return 0; +} + + /** * wpa_sm_set_assoc_wpa_ie_default - Generate own WPA/RSN IE from configuration * @sm: Pointer to WPA state machine data from wpa_sm_init() @@ -2624,11 +2642,7 @@ int wpa_sm_pmksa_cache_list(struct wpa_sm *sm, char *buf, size_t len) { -#ifndef CONFIG_NO_WPA2 return pmksa_cache_list(sm->pmksa, buf, len); -#else /* CONFIG_NO_WPA2 */ - return -1; -#endif /* CONFIG_NO_WPA2 */ } @@ -2659,7 +2673,114 @@ void wpa_sm_pmksa_cache_flush(struct wpa_sm *sm, void *network_ctx) { -#ifndef CONFIG_NO_WPA2 - pmksa_cache_flush(sm->pmksa, network_ctx); -#endif /* CONFIG_NO_WPA2 */ + pmksa_cache_flush(sm->pmksa, network_ctx, NULL, 0); } + + +#ifdef CONFIG_WNM +int wpa_wnmsleep_install_key(struct wpa_sm *sm, u8 subelem_id, u8 *buf) +{ + struct wpa_gtk_data gd; +#ifdef CONFIG_IEEE80211W + struct wpa_igtk_kde igd; + u16 keyidx; +#endif /* CONFIG_IEEE80211W */ + u16 keyinfo; + u8 keylen; /* plaintext key len */ + u8 *key_rsc; + + os_memset(&gd, 0, sizeof(gd)); +#ifdef CONFIG_IEEE80211W + os_memset(&igd, 0, sizeof(igd)); +#endif /* CONFIG_IEEE80211W */ + + keylen = wpa_cipher_key_len(sm->group_cipher); + gd.key_rsc_len = wpa_cipher_rsc_len(sm->group_cipher); + gd.alg = wpa_cipher_to_alg(sm->group_cipher); + if (gd.alg == WPA_ALG_NONE) { + wpa_printf(MSG_DEBUG, "Unsupported group cipher suite"); + return -1; + } + + if (subelem_id == WNM_SLEEP_SUBELEM_GTK) { + key_rsc = buf + 5; + keyinfo = WPA_GET_LE16(buf + 2); + gd.gtk_len = keylen; + if (gd.gtk_len != buf[4]) { + wpa_printf(MSG_DEBUG, "GTK len mismatch len %d vs %d", + gd.gtk_len, buf[4]); + return -1; + } + gd.keyidx = keyinfo & 0x03; /* B0 - B1 */ + gd.tx = wpa_supplicant_gtk_tx_bit_workaround( + sm, !!(keyinfo & WPA_KEY_INFO_TXRX)); + + os_memcpy(gd.gtk, buf + 13, gd.gtk_len); + + wpa_hexdump_key(MSG_DEBUG, "Install GTK (WNM SLEEP)", + gd.gtk, gd.gtk_len); + if (wpa_supplicant_install_gtk(sm, &gd, key_rsc)) { + wpa_printf(MSG_DEBUG, "Failed to install the GTK in " + "WNM mode"); + return -1; + } +#ifdef CONFIG_IEEE80211W + } else if (subelem_id == WNM_SLEEP_SUBELEM_IGTK) { + os_memcpy(igd.keyid, buf + 2, 2); + os_memcpy(igd.pn, buf + 4, 6); + + keyidx = WPA_GET_LE16(igd.keyid); + os_memcpy(igd.igtk, buf + 10, WPA_IGTK_LEN); + + wpa_hexdump_key(MSG_DEBUG, "Install IGTK (WNM SLEEP)", + igd.igtk, WPA_IGTK_LEN); + if (wpa_sm_set_key(sm, WPA_ALG_IGTK, broadcast_ether_addr, + keyidx, 0, igd.pn, sizeof(igd.pn), + igd.igtk, WPA_IGTK_LEN) < 0) { + wpa_printf(MSG_DEBUG, "Failed to install the IGTK in " + "WNM mode"); + return -1; + } +#endif /* CONFIG_IEEE80211W */ + } else { + wpa_printf(MSG_DEBUG, "Unknown element id"); + return -1; + } + + return 0; +} +#endif /* CONFIG_WNM */ + + +#ifdef CONFIG_PEERKEY +int wpa_sm_rx_eapol_peerkey(struct wpa_sm *sm, const u8 *src_addr, + const u8 *buf, size_t len) +{ + struct wpa_peerkey *peerkey; + + for (peerkey = sm->peerkey; peerkey; peerkey = peerkey->next) { + if (os_memcmp(peerkey->addr, src_addr, ETH_ALEN) == 0) + break; + } + + if (!peerkey) + return 0; + + wpa_sm_rx_eapol(sm, src_addr, buf, len); + + return 1; +} +#endif /* CONFIG_PEERKEY */ + + +#ifdef CONFIG_P2P + +int wpa_sm_get_p2p_ip_addr(struct wpa_sm *sm, u8 *buf) +{ + if (sm == NULL || WPA_GET_BE32(sm->p2p_ip_addr) == 0) + return -1; + os_memcpy(buf, sm->p2p_ip_addr, 3 * 4); + return 0; +} + +#endif /* CONFIG_P2P */ diff -Nru wpa-1.0/src/rsn_supp/wpa_ft.c wpa-2.1/src/rsn_supp/wpa_ft.c --- wpa-1.0/src/rsn_supp/wpa_ft.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/rsn_supp/wpa_ft.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * WPA Supplicant - IEEE 802.11r - Fast BSS Transition * Copyright (c) 2006-2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -21,7 +15,6 @@ #include "common/ieee802_11_common.h" #include "wpa.h" #include "wpa_i.h" -#include "wpa_ie.h" #ifdef CONFIG_IEEE80211R @@ -178,16 +171,16 @@ pos = (u8 *) (rsnie + 1); /* Group Suite Selector */ - if (sm->group_cipher == WPA_CIPHER_CCMP) - RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP); - else if (sm->group_cipher == WPA_CIPHER_TKIP) - RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_TKIP); - else { + if (sm->group_cipher != WPA_CIPHER_CCMP && + sm->group_cipher != WPA_CIPHER_GCMP && + sm->group_cipher != WPA_CIPHER_TKIP) { wpa_printf(MSG_WARNING, "FT: Invalid group cipher (%d)", sm->group_cipher); os_free(buf); return NULL; } + RSN_SELECTOR_PUT(pos, wpa_cipher_to_suite(WPA_PROTO_RSN, + sm->group_cipher)); pos += RSN_SELECTOR_LEN; /* Pairwise Suite Count */ @@ -195,16 +188,14 @@ pos += 2; /* Pairwise Suite List */ - if (sm->pairwise_cipher == WPA_CIPHER_CCMP) - RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP); - else if (sm->pairwise_cipher == WPA_CIPHER_TKIP) - RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_TKIP); - else { + if (!wpa_cipher_valid_pairwise(sm->pairwise_cipher)) { wpa_printf(MSG_WARNING, "FT: Invalid pairwise cipher (%d)", sm->pairwise_cipher); os_free(buf); return NULL; } + RSN_SELECTOR_PUT(pos, wpa_cipher_to_suite(WPA_PROTO_RSN, + sm->pairwise_cipher)); pos += RSN_SELECTOR_LEN; /* Authenticated Key Management Suite Count */ @@ -216,6 +207,8 @@ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_802_1X); else if (sm->key_mgmt == WPA_KEY_MGMT_FT_PSK) RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_PSK); + else if (sm->key_mgmt == WPA_KEY_MGMT_FT_SAE) + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_SAE); else { wpa_printf(MSG_WARNING, "FT: Invalid key management type (%d)", sm->key_mgmt); @@ -330,21 +323,15 @@ wpa_printf(MSG_DEBUG, "FT: Installing PTK to the driver."); - switch (sm->pairwise_cipher) { - case WPA_CIPHER_CCMP: - alg = WPA_ALG_CCMP; - keylen = 16; - break; - case WPA_CIPHER_TKIP: - alg = WPA_ALG_TKIP; - keylen = 32; - break; - default: + if (!wpa_cipher_valid_pairwise(sm->pairwise_cipher)) { wpa_printf(MSG_WARNING, "FT: Unsupported pairwise cipher %d", sm->pairwise_cipher); return -1; } + alg = wpa_cipher_to_alg(sm->pairwise_cipher); + keylen = wpa_cipher_key_len(sm->pairwise_cipher); + if (wpa_sm_set_key(sm, alg, bssid, 0, 1, null_rsc, sizeof(null_rsc), (u8 *) sm->ptk.tk1, keylen) < 0) { wpa_printf(MSG_WARNING, "FT: Failed to set PTK to the driver"); @@ -415,8 +402,7 @@ } } - if (sm->key_mgmt != WPA_KEY_MGMT_FT_IEEE8021X && - sm->key_mgmt != WPA_KEY_MGMT_FT_PSK) { + if (!wpa_key_mgmt_ft(sm->key_mgmt)) { wpa_printf(MSG_DEBUG, "FT: Reject FT IEs since FT is not " "enabled for this connection"); return -1; @@ -490,7 +476,7 @@ sm->pmk_r1_name, WPA_PMK_NAME_LEN); bssid = target_ap; - ptk_len = sm->pairwise_cipher == WPA_CIPHER_CCMP ? 48 : 64; + ptk_len = sm->pairwise_cipher != WPA_CIPHER_TKIP ? 48 : 64; wpa_pmk_r1_to_ptk(sm->pmk_r1, sm->snonce, ftie->anonce, sm->own_addr, bssid, sm->pmk_r1_name, (u8 *) &sm->ptk, ptk_len, ptk_name); @@ -541,14 +527,20 @@ if (sm == NULL) return 0; - if (sm->key_mgmt != WPA_KEY_MGMT_FT_IEEE8021X && - sm->key_mgmt != WPA_KEY_MGMT_FT_PSK) + if (!wpa_key_mgmt_ft(sm->key_mgmt)) return 0; return sm->ft_completed; } +void wpa_reset_ft_completed(struct wpa_sm *sm) +{ + if (sm != NULL) + sm->ft_completed = 0; +} + + static int wpa_ft_process_gtk_subelem(struct wpa_sm *sm, const u8 *gtk_elem, size_t gtk_elem_len) { @@ -578,28 +570,10 @@ return -1; } - switch (sm->group_cipher) { - case WPA_CIPHER_CCMP: - keylen = 16; - rsc_len = 6; - alg = WPA_ALG_CCMP; - break; - case WPA_CIPHER_TKIP: - keylen = 32; - rsc_len = 6; - alg = WPA_ALG_TKIP; - break; - case WPA_CIPHER_WEP104: - keylen = 13; - rsc_len = 0; - alg = WPA_ALG_WEP; - break; - case WPA_CIPHER_WEP40: - keylen = 5; - rsc_len = 0; - alg = WPA_ALG_WEP; - break; - default: + keylen = wpa_cipher_key_len(sm->group_cipher); + rsc_len = wpa_cipher_rsc_len(sm->group_cipher); + alg = wpa_cipher_to_alg(sm->group_cipher); + if (alg == WPA_ALG_NONE) { wpa_printf(MSG_WARNING, "WPA: Unsupported Group Cipher %d", sm->group_cipher); return -1; @@ -622,6 +596,13 @@ } wpa_hexdump_key(MSG_DEBUG, "FT: GTK from Reassoc Resp", gtk, keylen); + if (sm->group_cipher == WPA_CIPHER_TKIP) { + /* Swap Tx/Rx keys for Michael MIC */ + u8 tmp[8]; + os_memcpy(tmp, gtk + 16, 8); + os_memcpy(gtk + 16, gtk + 24, 8); + os_memcpy(gtk + 24, tmp, 8); + } if (wpa_sm_set_key(sm, alg, broadcast_ether_addr, keyidx, 0, gtk_elem + 3, rsc_len, gtk, keylen) < 0) { wpa_printf(MSG_WARNING, "WPA: Failed to set GTK to the " @@ -697,8 +678,7 @@ wpa_hexdump(MSG_DEBUG, "FT: Response IEs", ies, ies_len); - if (sm->key_mgmt != WPA_KEY_MGMT_FT_IEEE8021X && - sm->key_mgmt != WPA_KEY_MGMT_FT_PSK) { + if (!wpa_key_mgmt_ft(sm->key_mgmt)) { wpa_printf(MSG_DEBUG, "FT: Reject FT IEs since FT is not " "enabled for this connection"); return -1; @@ -821,9 +801,12 @@ if (parse.ric) { wpa_hexdump(MSG_MSGDUMP, "FT: RIC Response", parse.ric, parse.ric_len); - /* TODO: parse response and inform driver about results */ + /* TODO: parse response and inform driver about results when + * using wpa_supplicant SME */ } + wpa_printf(MSG_DEBUG, "FT: Completed successfully"); + return 0; } diff -Nru wpa-1.0/src/rsn_supp/wpa.h wpa-2.1/src/rsn_supp/wpa.h --- wpa-1.0/src/rsn_supp/wpa.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/rsn_supp/wpa.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * wpa_supplicant - WPA definitions * Copyright (c) 2003-2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef WPA_H @@ -18,6 +12,7 @@ #include "common/defs.h" #include "common/eapol_common.h" #include "common/wpa_common.h" +#include "common/ieee802_11_defs.h" struct wpa_sm; struct eapol_sm; @@ -30,7 +25,6 @@ void (*set_state)(void *ctx, enum wpa_states state); enum wpa_states (*get_state)(void *ctx); void (*deauthenticate)(void * ctx, int reason_code); - void (*disassociate)(void *ctx, int reason_code); int (*set_key)(void *ctx, enum wpa_alg alg, const u8 *addr, int key_idx, int set_tx, const u8 *seq, size_t seq_len, @@ -62,9 +56,16 @@ u8 action_code, u8 dialog_token, u16 status_code, const u8 *buf, size_t len); int (*tdls_oper)(void *ctx, int oper, const u8 *peer); - int (*tdls_peer_addset)(void *ctx, const u8 *addr, int add, + int (*tdls_peer_addset)(void *ctx, const u8 *addr, int add, u16 aid, u16 capability, const u8 *supp_rates, - size_t supp_rates_len); + size_t supp_rates_len, + const struct ieee80211_ht_capabilities *ht_capab, + const struct ieee80211_vht_capabilities *vht_capab, + u8 qosinfo, const u8 *ext_capab, + size_t ext_capab_len, const u8 *supp_channels, + size_t supp_channels_len, + const u8 *supp_oper_classes, + size_t supp_oper_classes_len); #endif /* CONFIG_TDLS */ void (*set_rekey_offload)(void *ctx, const u8 *kek, const u8 *kck, const u8 *replay_ctr); @@ -94,6 +95,7 @@ const u8 *ssid; size_t ssid_len; int wpa_ptk_rekey; + int p2p; }; #ifndef CONFIG_NO_WPA @@ -125,6 +127,7 @@ int wpa_sm_get_status(struct wpa_sm *sm, char *buf, size_t buflen, int verbose); +int wpa_sm_pmf_enabled(struct wpa_sm *sm); void wpa_sm_key_request(struct wpa_sm *sm, int error, int pairwise); @@ -143,6 +146,8 @@ void wpa_sm_pmksa_cache_flush(struct wpa_sm *sm, void *network_ctx); +int wpa_sm_get_p2p_ip_addr(struct wpa_sm *sm, u8 *buf); + #else /* CONFIG_NO_WPA */ static inline struct wpa_sm * wpa_sm_init(struct wpa_sm_ctx *ctx) @@ -246,6 +251,11 @@ return 0; } +static inline int wpa_sm_pmf_enabled(struct wpa_sm *sm) +{ + return 0; +} + static inline void wpa_sm_key_request(struct wpa_sm *sm, int error, int pairwise) { @@ -302,11 +312,19 @@ #ifdef CONFIG_PEERKEY int wpa_sm_stkstart(struct wpa_sm *sm, const u8 *peer); +int wpa_sm_rx_eapol_peerkey(struct wpa_sm *sm, const u8 *src_addr, + const u8 *buf, size_t len); #else /* CONFIG_PEERKEY */ static inline int wpa_sm_stkstart(struct wpa_sm *sm, const u8 *peer) { return -1; } + +static inline int wpa_sm_rx_eapol_peerkey(struct wpa_sm *sm, const u8 *src_addr, + const u8 *buf, size_t len) +{ + return 0; +} #endif /* CONFIG_PEERKEY */ #ifdef CONFIG_IEEE80211R @@ -317,6 +335,7 @@ int ft_action, const u8 *target_ap, const u8 *ric_ies, size_t ric_ies_len); int wpa_ft_is_completed(struct wpa_sm *sm); +void wpa_reset_ft_completed(struct wpa_sm *sm); int wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies, size_t ies_len, const u8 *src_addr); int wpa_ft_start_over_ds(struct wpa_sm *sm, const u8 *target_ap, @@ -348,6 +367,10 @@ return 0; } +static inline void wpa_reset_ft_completed(struct wpa_sm *sm) +{ +} + static inline int wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies, size_t ies_len, const u8 *src_addr) @@ -362,14 +385,18 @@ void wpa_tdls_ap_ies(struct wpa_sm *sm, const u8 *ies, size_t len); void wpa_tdls_assoc_resp_ies(struct wpa_sm *sm, const u8 *ies, size_t len); int wpa_tdls_start(struct wpa_sm *sm, const u8 *addr); -int wpa_tdls_reneg(struct wpa_sm *sm, const u8 *addr); +void wpa_tdls_remove(struct wpa_sm *sm, const u8 *addr); int wpa_tdls_send_teardown(struct wpa_sm *sm, const u8 *addr, u16 reason_code); int wpa_tdls_teardown_link(struct wpa_sm *sm, const u8 *addr, u16 reason_code); int wpa_tdls_send_discovery_request(struct wpa_sm *sm, const u8 *addr); int wpa_tdls_init(struct wpa_sm *sm); +void wpa_tdls_teardown_peers(struct wpa_sm *sm); void wpa_tdls_deinit(struct wpa_sm *sm); void wpa_tdls_enable(struct wpa_sm *sm, int enabled); void wpa_tdls_disable_link(struct wpa_sm *sm, const u8 *addr); +const char * wpa_tdls_get_link_status(struct wpa_sm *sm, const u8 *addr); int wpa_tdls_is_external_setup(struct wpa_sm *sm); +int wpa_wnmsleep_install_key(struct wpa_sm *sm, u8 subelem_id, u8 *buf); + #endif /* WPA_H */ diff -Nru wpa-1.0/src/rsn_supp/wpa_ie.c wpa-2.1/src/rsn_supp/wpa_ie.c --- wpa-1.0/src/rsn_supp/wpa_ie.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/rsn_supp/wpa_ie.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * wpa_supplicant - WPA/RSN IE and KDE processing * Copyright (c) 2003-2008, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -47,6 +41,7 @@ { u8 *pos; struct wpa_ie_hdr *hdr; + u32 suite; if (wpa_ie_len < sizeof(*hdr) + WPA_SELECTOR_LEN + 2 + WPA_SELECTOR_LEN + 2 + WPA_SELECTOR_LEN) @@ -58,34 +53,26 @@ WPA_PUT_LE16(hdr->version, WPA_VERSION); pos = (u8 *) (hdr + 1); - if (group_cipher == WPA_CIPHER_CCMP) { - RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_CCMP); - } else if (group_cipher == WPA_CIPHER_TKIP) { - RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_TKIP); - } else if (group_cipher == WPA_CIPHER_WEP104) { - RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_WEP104); - } else if (group_cipher == WPA_CIPHER_WEP40) { - RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_WEP40); - } else { + suite = wpa_cipher_to_suite(WPA_PROTO_WPA, group_cipher); + if (suite == 0) { wpa_printf(MSG_WARNING, "Invalid group cipher (%d).", group_cipher); return -1; } + RSN_SELECTOR_PUT(pos, suite); pos += WPA_SELECTOR_LEN; *pos++ = 1; *pos++ = 0; - if (pairwise_cipher == WPA_CIPHER_CCMP) { - RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_CCMP); - } else if (pairwise_cipher == WPA_CIPHER_TKIP) { - RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_TKIP); - } else if (pairwise_cipher == WPA_CIPHER_NONE) { - RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_NONE); - } else { + suite = wpa_cipher_to_suite(WPA_PROTO_WPA, pairwise_cipher); + if (suite == 0 || + (!wpa_cipher_valid_pairwise(pairwise_cipher) && + pairwise_cipher != WPA_CIPHER_NONE)) { wpa_printf(MSG_WARNING, "Invalid pairwise cipher (%d).", pairwise_cipher); return -1; } + RSN_SELECTOR_PUT(pos, suite); pos += WPA_SELECTOR_LEN; *pos++ = 1; @@ -96,6 +83,8 @@ RSN_SELECTOR_PUT(pos, WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X); } else if (key_mgmt == WPA_KEY_MGMT_WPA_NONE) { RSN_SELECTOR_PUT(pos, WPA_AUTH_KEY_MGMT_NONE); + } else if (key_mgmt == WPA_KEY_MGMT_CCKM) { + RSN_SELECTOR_PUT(pos, WPA_AUTH_KEY_MGMT_CCKM); } else { wpa_printf(MSG_WARNING, "Invalid key management type (%d).", key_mgmt); @@ -118,10 +107,10 @@ int key_mgmt, int mgmt_group_cipher, struct wpa_sm *sm) { -#ifndef CONFIG_NO_WPA2 u8 *pos; struct rsn_ie_hdr *hdr; u16 capab; + u32 suite; if (rsn_ie_len < sizeof(*hdr) + RSN_SELECTOR_LEN + 2 + RSN_SELECTOR_LEN + 2 + RSN_SELECTOR_LEN + 2 + @@ -136,34 +125,26 @@ WPA_PUT_LE16(hdr->version, RSN_VERSION); pos = (u8 *) (hdr + 1); - if (group_cipher == WPA_CIPHER_CCMP) { - RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP); - } else if (group_cipher == WPA_CIPHER_TKIP) { - RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_TKIP); - } else if (group_cipher == WPA_CIPHER_WEP104) { - RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_WEP104); - } else if (group_cipher == WPA_CIPHER_WEP40) { - RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_WEP40); - } else { + suite = wpa_cipher_to_suite(WPA_PROTO_RSN, group_cipher); + if (suite == 0) { wpa_printf(MSG_WARNING, "Invalid group cipher (%d).", group_cipher); return -1; } + RSN_SELECTOR_PUT(pos, suite); pos += RSN_SELECTOR_LEN; *pos++ = 1; *pos++ = 0; - if (pairwise_cipher == WPA_CIPHER_CCMP) { - RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP); - } else if (pairwise_cipher == WPA_CIPHER_TKIP) { - RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_TKIP); - } else if (pairwise_cipher == WPA_CIPHER_NONE) { - RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_NONE); - } else { + suite = wpa_cipher_to_suite(WPA_PROTO_RSN, pairwise_cipher); + if (suite == 0 || + (!wpa_cipher_valid_pairwise(pairwise_cipher) && + pairwise_cipher != WPA_CIPHER_NONE)) { wpa_printf(MSG_WARNING, "Invalid pairwise cipher (%d).", pairwise_cipher); return -1; } + RSN_SELECTOR_PUT(pos, suite); pos += RSN_SELECTOR_LEN; *pos++ = 1; @@ -172,6 +153,8 @@ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_UNSPEC_802_1X); } else if (key_mgmt == WPA_KEY_MGMT_PSK) { RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X); + } else if (key_mgmt == WPA_KEY_MGMT_CCKM) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_CCKM); #ifdef CONFIG_IEEE80211R } else if (key_mgmt == WPA_KEY_MGMT_FT_IEEE8021X) { RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_802_1X); @@ -184,6 +167,12 @@ } else if (key_mgmt == WPA_KEY_MGMT_PSK_SHA256) { RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_PSK_SHA256); #endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_SAE + } else if (key_mgmt == WPA_KEY_MGMT_SAE) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_SAE); + } else if (key_mgmt == WPA_KEY_MGMT_FT_SAE) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_SAE); +#endif /* CONFIG_SAE */ } else { wpa_printf(MSG_WARNING, "Invalid key management type (%d).", key_mgmt); @@ -230,9 +219,6 @@ WPA_ASSERT((size_t) (pos - rsn_ie) <= rsn_ie_len); return pos - rsn_ie; -#else /* CONFIG_NO_WPA2 */ - return -1; -#endif /* CONFIG_NO_WPA2 */ } @@ -359,6 +345,25 @@ } #endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_P2P + if (pos[1] >= RSN_SELECTOR_LEN + 1 && + RSN_SELECTOR_GET(pos + 2) == WFA_KEY_DATA_IP_ADDR_REQ) { + ie->ip_addr_req = pos + 2 + RSN_SELECTOR_LEN; + wpa_hexdump(MSG_DEBUG, "WPA: IP Address Request in EAPOL-Key", + ie->ip_addr_req, pos[1] - RSN_SELECTOR_LEN); + return 0; + } + + if (pos[1] >= RSN_SELECTOR_LEN + 3 * 4 && + RSN_SELECTOR_GET(pos + 2) == WFA_KEY_DATA_IP_ADDR_ALLOC) { + ie->ip_addr_alloc = pos + 2 + RSN_SELECTOR_LEN; + wpa_hexdump(MSG_DEBUG, + "WPA: IP Address Allocation in EAPOL-Key", + ie->ip_addr_alloc, pos[1] - RSN_SELECTOR_LEN); + return 0; + } +#endif /* CONFIG_P2P */ + return 0; } @@ -437,6 +442,23 @@ } else if (*pos == WLAN_EID_EXT_SUPP_RATES) { ie->ext_supp_rates = pos; ie->ext_supp_rates_len = pos[1] + 2; + } else if (*pos == WLAN_EID_HT_CAP) { + ie->ht_capabilities = pos + 2; + ie->ht_capabilities_len = pos[1]; + } else if (*pos == WLAN_EID_VHT_AID) { + if (pos[1] >= 2) + ie->aid = WPA_GET_LE16(pos + 2); + } else if (*pos == WLAN_EID_VHT_CAP) { + ie->vht_capabilities = pos + 2; + ie->vht_capabilities_len = pos[1]; + } else if (*pos == WLAN_EID_QOS && pos[1] >= 1) { + ie->qosinfo = pos[2]; + } else if (*pos == WLAN_EID_SUPPORTED_CHANNELS) { + ie->supp_channels = pos + 2; + ie->supp_channels_len = pos[1]; + } else if (*pos == WLAN_EID_SUPPORTED_OPERATING_CLASSES) { + ie->supp_oper_classes = pos + 2; + ie->supp_oper_classes_len = pos[1]; } else if (*pos == WLAN_EID_VENDOR_SPECIFIC) { ret = wpa_parse_generic(pos, end, ie); if (ret < 0) diff -Nru wpa-1.0/src/rsn_supp/wpa_ie.h wpa-2.1/src/rsn_supp/wpa_ie.h --- wpa-1.0/src/rsn_supp/wpa_ie.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/rsn_supp/wpa_ie.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * wpa_supplicant - WPA/RSN IE and KDE definitions * Copyright (c) 2004-2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef WPA_IE_H @@ -55,6 +49,20 @@ size_t supp_rates_len; const u8 *ext_supp_rates; size_t ext_supp_rates_len; + const u8 *ht_capabilities; + size_t ht_capabilities_len; + const u8 *vht_capabilities; + size_t vht_capabilities_len; + const u8 *supp_channels; + size_t supp_channels_len; + const u8 *supp_oper_classes; + size_t supp_oper_classes_len; + u8 qosinfo; + u16 aid; +#ifdef CONFIG_P2P + const u8 *ip_addr_req; + const u8 *ip_addr_alloc; +#endif /* CONFIG_P2P */ }; int wpa_supplicant_parse_ies(const u8 *buf, size_t len, diff -Nru wpa-1.0/src/rsn_supp/wpa_i.h wpa-2.1/src/rsn_supp/wpa_i.h --- wpa-1.0/src/rsn_supp/wpa_i.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/rsn_supp/wpa_i.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * Internal WPA/RSN supplicant state machine definitions * Copyright (c) 2004-2010, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef WPA_I_H @@ -64,6 +58,7 @@ u8 ssid[32]; size_t ssid_len; int wpa_ptk_rekey; + int p2p; u8 own_addr[ETH_ALEN]; const char *ifname; @@ -128,6 +123,10 @@ u8 *assoc_resp_ies; /* MDIE and FTIE from (Re)Association Response */ size_t assoc_resp_ies_len; #endif /* CONFIG_IEEE80211R */ + +#ifdef CONFIG_P2P + u8 p2p_ip_addr[3 * 4]; +#endif /* CONFIG_P2P */ }; @@ -149,12 +148,6 @@ sm->ctx->deauthenticate(sm->ctx->ctx, reason_code); } -static inline void wpa_sm_disassociate(struct wpa_sm *sm, int reason_code) -{ - WPA_ASSERT(sm->ctx->disassociate); - sm->ctx->disassociate(sm->ctx->ctx, reason_code); -} - static inline int wpa_sm_set_key(struct wpa_sm *sm, enum wpa_alg alg, const u8 *addr, int key_idx, int set_tx, const u8 *seq, size_t seq_len, @@ -294,13 +287,25 @@ static inline int wpa_sm_tdls_peer_addset(struct wpa_sm *sm, const u8 *addr, int add, - u16 capability, const u8 *supp_rates, - size_t supp_rates_len) + u16 aid, u16 capability, const u8 *supp_rates, + size_t supp_rates_len, + const struct ieee80211_ht_capabilities *ht_capab, + const struct ieee80211_vht_capabilities *vht_capab, + u8 qosinfo, const u8 *ext_capab, size_t ext_capab_len, + const u8 *supp_channels, size_t supp_channels_len, + const u8 *supp_oper_classes, + size_t supp_oper_classes_len) { if (sm->ctx->tdls_peer_addset) return sm->ctx->tdls_peer_addset(sm->ctx->ctx, addr, add, - capability, supp_rates, - supp_rates_len); + aid, capability, supp_rates, + supp_rates_len, ht_capab, + vht_capab, qosinfo, + ext_capab, ext_capab_len, + supp_channels, + supp_channels_len, + supp_oper_classes, + supp_oper_classes_len); return -1; } #endif /* CONFIG_TDLS */ diff -Nru wpa-1.0/src/tls/asn1.c wpa-2.1/src/tls/asn1.c --- wpa-1.0/src/tls/asn1.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/tls/asn1.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * ASN.1 DER parsing * Copyright (c) 2006, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff -Nru wpa-1.0/src/tls/asn1.h wpa-2.1/src/tls/asn1.h --- wpa-1.0/src/tls/asn1.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/tls/asn1.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * ASN.1 DER parsing * Copyright (c) 2006, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef ASN1_H diff -Nru wpa-1.0/src/tls/bignum.c wpa-2.1/src/tls/bignum.c --- wpa-1.0/src/tls/bignum.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/tls/bignum.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * Big number math * Copyright (c) 2006, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff -Nru wpa-1.0/src/tls/bignum.h wpa-2.1/src/tls/bignum.h --- wpa-1.0/src/tls/bignum.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/tls/bignum.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * Big number math * Copyright (c) 2006, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef BIGNUM_H diff -Nru wpa-1.0/src/tls/libtommath.c wpa-2.1/src/tls/libtommath.c --- wpa-1.0/src/tls/libtommath.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/tls/libtommath.c 2014-02-04 11:23:35.000000000 +0000 @@ -42,6 +42,9 @@ /* Include faster sqr at the cost of about 0.5 kB in code */ #define BN_FAST_S_MP_SQR_C +/* About 0.25 kB of code, but ~1.7kB of stack space! */ +#define BN_FAST_S_MP_MUL_DIGS_C + #else /* LTM_FAST */ #define BN_MP_DIV_SMALL @@ -66,11 +69,19 @@ #define OPT_CAST(x) +#ifdef __x86_64__ +typedef unsigned long mp_digit; +typedef unsigned long mp_word __attribute__((mode(TI))); + +#define DIGIT_BIT 60 +#define MP_64BIT +#else typedef unsigned long mp_digit; typedef u64 mp_word; #define DIGIT_BIT 28 #define MP_28BIT +#endif #define XMALLOC os_malloc @@ -131,7 +142,9 @@ static int s_mp_sqr(mp_int * a, mp_int * b); static int s_mp_mul_high_digs(mp_int * a, mp_int * b, mp_int * c, int digs); +#ifdef BN_FAST_S_MP_MUL_DIGS_C static int fast_s_mp_mul_digs (mp_int * a, mp_int * b, mp_int * c, int digs); +#endif #ifdef BN_MP_INIT_MULTI_C static int mp_init_multi(mp_int *mp, ...); @@ -663,6 +676,9 @@ #ifdef BN_MP_EXPTMOD_FAST_C } #endif + if (dr == 0) { + /* avoid compiler warnings about possibly unused variable */ + } } @@ -2331,12 +2347,14 @@ mp_word r; mp_digit tmpx, *tmpt, *tmpy; +#ifdef BN_FAST_S_MP_MUL_DIGS_C /* can we use the fast multiplier? */ if (((digs) < MP_WARRAY) && MIN (a->used, b->used) < (1 << ((CHAR_BIT * sizeof (mp_word)) - (2 * DIGIT_BIT)))) { return fast_s_mp_mul_digs (a, b, c, digs); } +#endif if ((res = mp_init_size (&t, digs)) != MP_OKAY) { return res; @@ -2389,6 +2407,7 @@ } +#ifdef BN_FAST_S_MP_MUL_DIGS_C /* Fast (comba) multiplier * * This is the fast column-array [comba] multiplier. It is @@ -2474,6 +2493,7 @@ mp_clamp (c); return MP_OKAY; } +#endif /* BN_FAST_S_MP_MUL_DIGS_C */ /* init an mp_init for a given size */ @@ -2678,7 +2698,7 @@ * * Based on Algorithm 14.32 on pp.601 of HAC. */ -int fast_mp_montgomery_reduce (mp_int * x, mp_int * n, mp_digit rho) +static int fast_mp_montgomery_reduce (mp_int * x, mp_int * n, mp_digit rho) { int ix, res, olduse; mp_word W[MP_WARRAY]; diff -Nru wpa-1.0/src/tls/Makefile wpa-2.1/src/tls/Makefile --- wpa-1.0/src/tls/Makefile 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/tls/Makefile 2014-02-04 11:23:35.000000000 +0000 @@ -11,6 +11,8 @@ CFLAGS += -DCONFIG_INTERNAL_LIBTOMMATH CFLAGS += -DCONFIG_CRYPTO_INTERNAL +CFLAGS += -DCONFIG_TLSV11 +CFLAGS += -DCONFIG_TLSV12 LIB_OBJS= \ asn1.o \ diff -Nru wpa-1.0/src/tls/pkcs1.c wpa-2.1/src/tls/pkcs1.c --- wpa-1.0/src/tls/pkcs1.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/tls/pkcs1.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * PKCS #1 (RSA Encryption) * Copyright (c) 2006-2009, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff -Nru wpa-1.0/src/tls/pkcs1.h wpa-2.1/src/tls/pkcs1.h --- wpa-1.0/src/tls/pkcs1.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/tls/pkcs1.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * PKCS #1 (RSA Encryption) * Copyright (c) 2006-2009, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef PKCS1_H diff -Nru wpa-1.0/src/tls/pkcs5.c wpa-2.1/src/tls/pkcs5.c --- wpa-1.0/src/tls/pkcs5.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/tls/pkcs5.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * PKCS #5 (Password-based Encryption) * Copyright (c) 2009, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -32,7 +26,7 @@ }; -enum pkcs5_alg pkcs5_get_alg(struct asn1_oid *oid) +static enum pkcs5_alg pkcs5_get_alg(struct asn1_oid *oid) { if (oid->len == 7 && oid->oid[0] == 1 /* iso */ && diff -Nru wpa-1.0/src/tls/pkcs5.h wpa-2.1/src/tls/pkcs5.h --- wpa-1.0/src/tls/pkcs5.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/tls/pkcs5.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * PKCS #5 (Password-based Encryption) * Copyright (c) 2009, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef PKCS5_H diff -Nru wpa-1.0/src/tls/pkcs8.c wpa-2.1/src/tls/pkcs8.c --- wpa-1.0/src/tls/pkcs8.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/tls/pkcs8.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * PKCS #8 (Private-key information syntax) * Copyright (c) 2006-2009, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff -Nru wpa-1.0/src/tls/pkcs8.h wpa-2.1/src/tls/pkcs8.h --- wpa-1.0/src/tls/pkcs8.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/tls/pkcs8.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * PKCS #8 (Private-key information syntax) * Copyright (c) 2006-2009, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef PKCS8_H diff -Nru wpa-1.0/src/tls/rsa.c wpa-2.1/src/tls/rsa.c --- wpa-1.0/src/tls/rsa.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/tls/rsa.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * RSA * Copyright (c) 2006, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff -Nru wpa-1.0/src/tls/rsa.h wpa-2.1/src/tls/rsa.h --- wpa-1.0/src/tls/rsa.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/tls/rsa.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * RSA * Copyright (c) 2006, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef RSA_H diff -Nru wpa-1.0/src/tls/tlsv1_client.c wpa-2.1/src/tls/tlsv1_client.c --- wpa-1.0/src/tls/tlsv1_client.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/tls/tlsv1_client.c 2014-02-04 11:23:35.000000000 +0000 @@ -1,15 +1,9 @@ /* - * TLS v1.0 (RFC 2246) and v1.1 (RFC 4346) client + * TLS v1.0/v1.1/v1.2 client (RFC 2246, RFC 4346, RFC 5246) * Copyright (c) 2006-2011, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -67,7 +61,8 @@ os_memcpy(seed, conn->client_random, TLS_RANDOM_LEN); os_memcpy(seed + TLS_RANDOM_LEN, conn->server_random, TLS_RANDOM_LEN); - if (tls_prf(pre_master_secret, pre_master_secret_len, + if (tls_prf(conn->rl.tls_version, + pre_master_secret, pre_master_secret_len, "master secret", seed, 2 * TLS_RANDOM_LEN, conn->master_secret, TLS_MASTER_SECRET_LEN)) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive " @@ -83,7 +78,8 @@ key_block_len = 2 * (conn->rl.hash_size + conn->rl.key_material_len); if (conn->rl.tls_version == TLS_VERSION_1) key_block_len += 2 * conn->rl.iv_size; - if (tls_prf(conn->master_secret, TLS_MASTER_SECRET_LEN, + if (tls_prf(conn->rl.tls_version, + conn->master_secret, TLS_MASTER_SECRET_LEN, "key expansion", seed, 2 * TLS_RANDOM_LEN, key_block, key_block_len)) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive key_block"); @@ -136,17 +132,23 @@ * @out_len: Length of the output buffer. * @appl_data: Pointer to application data pointer, or %NULL if dropped * @appl_data_len: Pointer to variable that is set to appl_data length + * @need_more_data: Set to 1 if more data would be needed to complete + * processing * Returns: Pointer to output data, %NULL on failure */ u8 * tlsv1_client_handshake(struct tlsv1_client *conn, const u8 *in_data, size_t in_len, size_t *out_len, u8 **appl_data, - size_t *appl_data_len) + size_t *appl_data_len, int *need_more_data) { const u8 *pos, *end; - u8 *msg = NULL, *in_msg, *in_pos, *in_end, alert, ct; + u8 *msg = NULL, *in_msg = NULL, *in_pos, *in_end, alert, ct; size_t in_msg_len; int no_appl_data; + int used; + + if (need_more_data) + *need_more_data = 0; if (conn->state == CLIENT_HELLO) { if (in_len) @@ -154,6 +156,19 @@ return tls_send_client_hello(conn, out_len); } + if (conn->partial_input) { + if (wpabuf_resize(&conn->partial_input, in_len) < 0) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to allocate " + "memory for pending record"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + goto failed; + } + wpabuf_put_data(conn->partial_input, in_data, in_len); + in_data = wpabuf_head(conn->partial_input); + in_len = wpabuf_len(conn->partial_input); + } + if (in_data == NULL || in_len == 0) return NULL; @@ -166,13 +181,33 @@ /* Each received packet may include multiple records */ while (pos < end) { in_msg_len = in_len; - if (tlsv1_record_receive(&conn->rl, pos, end - pos, - in_msg, &in_msg_len, &alert)) { + used = tlsv1_record_receive(&conn->rl, pos, end - pos, + in_msg, &in_msg_len, &alert); + if (used < 0) { wpa_printf(MSG_DEBUG, "TLSv1: Processing received " "record failed"); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, alert); goto failed; } + if (used == 0) { + struct wpabuf *partial; + wpa_printf(MSG_DEBUG, "TLSv1: Need more data"); + partial = wpabuf_alloc_copy(pos, end - pos); + wpabuf_free(conn->partial_input); + conn->partial_input = partial; + if (conn->partial_input == NULL) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to " + "allocate memory for pending " + "record"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + goto failed; + } + os_free(in_msg); + if (need_more_data) + *need_more_data = 1; + return NULL; + } ct = pos[0]; in_pos = in_msg; @@ -190,7 +225,7 @@ in_pos += in_msg_len; } - pos += TLS_RECORD_HEADER_LEN + WPA_GET_BE16(pos + 3); + pos += used; } os_free(in_msg); @@ -202,6 +237,8 @@ failed: os_free(in_msg); if (conn->alert_level) { + wpabuf_free(conn->partial_input); + conn->partial_input = NULL; conn->state = FAILED; os_free(msg); msg = tlsv1_client_send_alert(conn, conn->alert_level, @@ -212,6 +249,11 @@ *out_len = 0; } + if (need_more_data == NULL || !(*need_more_data)) { + wpabuf_free(conn->partial_input); + conn->partial_input = NULL; + } + return msg; } @@ -254,58 +296,116 @@ * @conn: TLSv1 client connection data from tlsv1_client_init() * @in_data: Pointer to input buffer (encrypted TLS data) * @in_len: Input buffer length - * @out_data: Pointer to output buffer (decrypted data from TLS tunnel) - * @out_len: Maximum out_data length - * Returns: Number of bytes written to out_data, -1 on failure + * @need_more_data: Set to 1 if more data would be needed to complete + * processing + * Returns: Decrypted data or %NULL on failure * * This function is used after TLS handshake has been completed successfully to * receive data from the encrypted tunnel. */ -int tlsv1_client_decrypt(struct tlsv1_client *conn, - const u8 *in_data, size_t in_len, - u8 *out_data, size_t out_len) +struct wpabuf * tlsv1_client_decrypt(struct tlsv1_client *conn, + const u8 *in_data, size_t in_len, + int *need_more_data) { const u8 *in_end, *pos; - int res; - u8 alert, *out_end, *out_pos; + int used; + u8 alert, *out_pos, ct; size_t olen; + struct wpabuf *buf = NULL; + + if (need_more_data) + *need_more_data = 0; + + if (conn->partial_input) { + if (wpabuf_resize(&conn->partial_input, in_len) < 0) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to allocate " + "memory for pending record"); + alert = TLS_ALERT_INTERNAL_ERROR; + goto fail; + } + wpabuf_put_data(conn->partial_input, in_data, in_len); + in_data = wpabuf_head(conn->partial_input); + in_len = wpabuf_len(conn->partial_input); + } pos = in_data; in_end = in_data + in_len; - out_pos = out_data; - out_end = out_data + out_len; while (pos < in_end) { - if (pos[0] != TLS_CONTENT_TYPE_APPLICATION_DATA) { - wpa_printf(MSG_DEBUG, "TLSv1: Unexpected content type " - "0x%x", pos[0]); - tls_alert(conn, TLS_ALERT_LEVEL_FATAL, - TLS_ALERT_UNEXPECTED_MESSAGE); - return -1; + ct = pos[0]; + if (wpabuf_resize(&buf, in_end - pos) < 0) { + alert = TLS_ALERT_INTERNAL_ERROR; + goto fail; } - - olen = out_end - out_pos; - res = tlsv1_record_receive(&conn->rl, pos, in_end - pos, - out_pos, &olen, &alert); - if (res < 0) { + out_pos = wpabuf_put(buf, 0); + olen = wpabuf_tailroom(buf); + used = tlsv1_record_receive(&conn->rl, pos, in_end - pos, + out_pos, &olen, &alert); + if (used < 0) { wpa_printf(MSG_DEBUG, "TLSv1: Record layer processing " "failed"); - tls_alert(conn, TLS_ALERT_LEVEL_FATAL, alert); - return -1; + goto fail; } - out_pos += olen; - if (out_pos > out_end) { - wpa_printf(MSG_DEBUG, "TLSv1: Buffer not large enough " - "for processing the received record"); - tls_alert(conn, TLS_ALERT_LEVEL_FATAL, - TLS_ALERT_INTERNAL_ERROR); - return -1; + if (used == 0) { + struct wpabuf *partial; + wpa_printf(MSG_DEBUG, "TLSv1: Need more data"); + partial = wpabuf_alloc_copy(pos, in_end - pos); + wpabuf_free(conn->partial_input); + conn->partial_input = partial; + if (conn->partial_input == NULL) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to " + "allocate memory for pending " + "record"); + alert = TLS_ALERT_INTERNAL_ERROR; + goto fail; + } + if (need_more_data) + *need_more_data = 1; + return buf; } - pos += TLS_RECORD_HEADER_LEN + WPA_GET_BE16(pos + 3); + if (ct == TLS_CONTENT_TYPE_ALERT) { + if (olen < 2) { + wpa_printf(MSG_DEBUG, "TLSv1: Alert " + "underflow"); + alert = TLS_ALERT_DECODE_ERROR; + goto fail; + } + wpa_printf(MSG_DEBUG, "TLSv1: Received alert %d:%d", + out_pos[0], out_pos[1]); + if (out_pos[0] == TLS_ALERT_LEVEL_WARNING) { + /* Continue processing */ + pos += used; + continue; + } + + alert = out_pos[1]; + goto fail; + } + + if (ct != TLS_CONTENT_TYPE_APPLICATION_DATA) { + wpa_printf(MSG_DEBUG, "TLSv1: Unexpected content type " + "0x%x when decrypting application data", + pos[0]); + alert = TLS_ALERT_UNEXPECTED_MESSAGE; + goto fail; + } + + wpabuf_put(buf, olen); + + pos += used; } - return out_pos - out_data; + wpabuf_free(conn->partial_input); + conn->partial_input = NULL; + return buf; + +fail: + wpabuf_free(buf); + wpabuf_free(conn->partial_input); + conn->partial_input = NULL; + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, alert); + return NULL; } @@ -359,9 +459,9 @@ count = 0; suites = conn->cipher_suites; -#ifndef CONFIG_CRYPTO_INTERNAL + suites[count++] = TLS_RSA_WITH_AES_256_CBC_SHA256; suites[count++] = TLS_RSA_WITH_AES_256_CBC_SHA; -#endif /* CONFIG_CRYPTO_INTERNAL */ + suites[count++] = TLS_RSA_WITH_AES_128_CBC_SHA256; suites[count++] = TLS_RSA_WITH_AES_128_CBC_SHA; suites[count++] = TLS_RSA_WITH_3DES_EDE_CBC_SHA; suites[count++] = TLS_RSA_WITH_RC4_128_SHA; @@ -388,6 +488,7 @@ os_free(conn->client_hello_ext); tlsv1_client_free_dh(conn); tlsv1_cred_free(conn->cred); + wpabuf_free(conn->partial_input); os_free(conn); } @@ -431,7 +532,8 @@ TLS_RANDOM_LEN); } - return tls_prf(conn->master_secret, TLS_MASTER_SECRET_LEN, + return tls_prf(conn->rl.tls_version, + conn->master_secret, TLS_MASTER_SECRET_LEN, label, seed, 2 * TLS_RANDOM_LEN, out, out_len); } @@ -463,15 +565,24 @@ case TLS_RSA_WITH_3DES_EDE_CBC_SHA: cipher = "DES-CBC3-SHA"; break; + case TLS_DH_anon_WITH_AES_128_CBC_SHA256: + cipher = "ADH-AES-128-SHA256"; + break; case TLS_DH_anon_WITH_AES_128_CBC_SHA: cipher = "ADH-AES-128-SHA"; break; case TLS_RSA_WITH_AES_256_CBC_SHA: cipher = "AES-256-SHA"; break; + case TLS_RSA_WITH_AES_256_CBC_SHA256: + cipher = "AES-256-SHA256"; + break; case TLS_RSA_WITH_AES_128_CBC_SHA: cipher = "AES-128-SHA"; break; + case TLS_RSA_WITH_AES_128_CBC_SHA256: + cipher = "AES-128-SHA256"; + break; default: return -1; } @@ -622,9 +733,9 @@ if (ciphers[0] == TLS_CIPHER_ANON_DH_AES128_SHA) { count = 0; suites = conn->cipher_suites; -#ifndef CONFIG_CRYPTO_INTERNAL + suites[count++] = TLS_DH_anon_WITH_AES_256_CBC_SHA256; suites[count++] = TLS_DH_anon_WITH_AES_256_CBC_SHA; -#endif /* CONFIG_CRYPTO_INTERNAL */ + suites[count++] = TLS_DH_anon_WITH_AES_128_CBC_SHA256; suites[count++] = TLS_DH_anon_WITH_AES_128_CBC_SHA; suites[count++] = TLS_DH_anon_WITH_3DES_EDE_CBC_SHA; suites[count++] = TLS_DH_anon_WITH_RC4_128_MD5; diff -Nru wpa-1.0/src/tls/tlsv1_client.h wpa-2.1/src/tls/tlsv1_client.h --- wpa-1.0/src/tls/tlsv1_client.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/tls/tlsv1_client.h 2014-02-04 11:23:35.000000000 +0000 @@ -1,15 +1,9 @@ /* - * TLSv1 client (RFC 2246) + * TLS v1.0/v1.1/v1.2 client (RFC 2246, RFC 4346, RFC 5246) * Copyright (c) 2006-2011, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef TLSV1_CLIENT_H @@ -29,13 +23,13 @@ u8 * tlsv1_client_handshake(struct tlsv1_client *conn, const u8 *in_data, size_t in_len, size_t *out_len, u8 **appl_data, - size_t *appl_data_len); + size_t *appl_data_len, int *need_more_data); int tlsv1_client_encrypt(struct tlsv1_client *conn, const u8 *in_data, size_t in_len, u8 *out_data, size_t out_len); -int tlsv1_client_decrypt(struct tlsv1_client *conn, - const u8 *in_data, size_t in_len, - u8 *out_data, size_t out_len); +struct wpabuf * tlsv1_client_decrypt(struct tlsv1_client *conn, + const u8 *in_data, size_t in_len, + int *need_more_data); int tlsv1_client_get_cipher(struct tlsv1_client *conn, char *buf, size_t buflen); int tlsv1_client_shutdown(struct tlsv1_client *conn); diff -Nru wpa-1.0/src/tls/tlsv1_client_i.h wpa-2.1/src/tls/tlsv1_client_i.h --- wpa-1.0/src/tls/tlsv1_client_i.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/tls/tlsv1_client_i.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * TLSv1 client - internal structures * Copyright (c) 2006-2011, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef TLSV1_CLIENT_I_H @@ -68,6 +62,8 @@ tlsv1_client_session_ticket_cb session_ticket_cb; void *session_ticket_cb_ctx; + + struct wpabuf *partial_input; }; diff -Nru wpa-1.0/src/tls/tlsv1_client_read.c wpa-2.1/src/tls/tlsv1_client_read.c --- wpa-1.0/src/tls/tlsv1_client_read.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/tls/tlsv1_client_read.c 2014-02-04 11:23:35.000000000 +0000 @@ -1,15 +1,9 @@ /* - * TLS v1.0 (RFC 2246) and v1.1 (RFC 4346) client - read handshake message + * TLSv1 client - read handshake message * Copyright (c) 2006-2011, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -17,6 +11,7 @@ #include "common.h" #include "crypto/md5.h" #include "crypto/sha1.h" +#include "crypto/sha256.h" #include "crypto/tls.h" #include "x509v3.h" #include "tlsv1_common.h" @@ -81,9 +76,7 @@ if (end - pos < 2) goto decode_error; tls_version = WPA_GET_BE16(pos); - if (tls_version != TLS_VERSION_1 && - (tls_version != TLS_VERSION_1_1 || - TLS_VERSION == TLS_VERSION_1)) { + if (!tls_version_ok(tls_version)) { wpa_printf(MSG_DEBUG, "TLSv1: Unexpected protocol version in " "ServerHello %u.%u", pos[0], pos[1]); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, @@ -93,7 +86,7 @@ pos += 2; wpa_printf(MSG_DEBUG, "TLSv1: Using TLS v%s", - tls_version == TLS_VERSION_1_1 ? "1.1" : "1.0"); + tls_version_str(tls_version)); conn->rl.tls_version = tls_version; /* Random random */ @@ -824,6 +817,21 @@ wpa_hexdump(MSG_MSGDUMP, "TLSv1: verify_data in Finished", pos, TLS_VERIFY_DATA_LEN); +#ifdef CONFIG_TLSV12 + if (conn->rl.tls_version >= TLS_VERSION_1_2) { + hlen = SHA256_MAC_LEN; + if (conn->verify.sha256_server == NULL || + crypto_hash_finish(conn->verify.sha256_server, hash, &hlen) + < 0) { + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + conn->verify.sha256_server = NULL; + return -1; + } + conn->verify.sha256_server = NULL; + } else { +#endif /* CONFIG_TLSV12 */ + hlen = MD5_MAC_LEN; if (conn->verify.md5_server == NULL || crypto_hash_finish(conn->verify.md5_server, hash, &hlen) < 0) { @@ -845,9 +853,15 @@ return -1; } conn->verify.sha1_server = NULL; + hlen = MD5_MAC_LEN + SHA1_MAC_LEN; + +#ifdef CONFIG_TLSV12 + } +#endif /* CONFIG_TLSV12 */ - if (tls_prf(conn->master_secret, TLS_MASTER_SECRET_LEN, - "server finished", hash, MD5_MAC_LEN + SHA1_MAC_LEN, + if (tls_prf(conn->rl.tls_version, + conn->master_secret, TLS_MASTER_SECRET_LEN, + "server finished", hash, hlen, verify_data, TLS_VERIFY_DATA_LEN)) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive verify_data"); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, diff -Nru wpa-1.0/src/tls/tlsv1_client_write.c wpa-2.1/src/tls/tlsv1_client_write.c --- wpa-1.0/src/tls/tlsv1_client_write.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/tls/tlsv1_client_write.c 2014-02-04 11:23:35.000000000 +0000 @@ -1,15 +1,9 @@ /* - * TLS v1.0 (RFC 2246) and v1.1 (RFC 4346) client - write handshake message + * TLSv1 client - write handshake message * Copyright (c) 2006-2011, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -17,6 +11,7 @@ #include "common.h" #include "crypto/md5.h" #include "crypto/sha1.h" +#include "crypto/sha256.h" #include "crypto/tls.h" #include "crypto/random.h" #include "x509v3.h" @@ -436,7 +431,7 @@ { u8 *pos, *rhdr, *hs_start, *hs_length, *signed_start; size_t rlen, hlen, clen; - u8 hash[MD5_MAC_LEN + SHA1_MAC_LEN], *hpos; + u8 hash[100], *hpos; enum { SIGN_ALG_RSA, SIGN_ALG_DSA } alg = SIGN_ALG_RSA; pos = *msgpos; @@ -476,6 +471,40 @@ hpos = hash; +#ifdef CONFIG_TLSV12 + if (conn->rl.tls_version == TLS_VERSION_1_2) { + hlen = SHA256_MAC_LEN; + if (conn->verify.sha256_cert == NULL || + crypto_hash_finish(conn->verify.sha256_cert, hpos, &hlen) < + 0) { + conn->verify.sha256_cert = NULL; + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + conn->verify.sha256_cert = NULL; + + /* + * RFC 3447, A.2.4 RSASSA-PKCS1-v1_5 + * + * DigestInfo ::= SEQUENCE { + * digestAlgorithm DigestAlgorithm, + * digest OCTET STRING + * } + * + * SHA-256 OID: sha256WithRSAEncryption ::= {pkcs-1 11} + * + * DER encoded DigestInfo for SHA256 per RFC 3447: + * 30 31 30 0d 06 09 60 86 48 01 65 03 04 02 01 05 00 04 20 || + * H + */ + os_memmove(hash + 19, hash, hlen); + hlen += 19; + os_memcpy(hash, "\x30\x31\x30\x0d\x06\x09\x60\x86\x48\x01\x65" + "\x03\x04\x02\x01\x05\x00\x04\x20", 19); + } else { +#endif /* CONFIG_TLSV12 */ + if (alg == SIGN_ALG_RSA) { hlen = MD5_MAC_LEN; if (conn->verify.md5_cert == NULL || @@ -506,8 +535,29 @@ if (alg == SIGN_ALG_RSA) hlen += MD5_MAC_LEN; +#ifdef CONFIG_TLSV12 + } +#endif /* CONFIG_TLSV12 */ + wpa_hexdump(MSG_MSGDUMP, "TLSv1: CertificateVerify hash", hash, hlen); +#ifdef CONFIG_TLSV12 + if (conn->rl.tls_version >= TLS_VERSION_1_2) { + /* + * RFC 5246, 4.7: + * TLS v1.2 adds explicit indication of the used signature and + * hash algorithms. + * + * struct { + * HashAlgorithm hash; + * SignatureAlgorithm signature; + * } SignatureAndHashAlgorithm; + */ + *pos++ = TLS_HASH_ALG_SHA256; + *pos++ = TLS_SIGN_ALG_RSA; + } +#endif /* CONFIG_TLSV12 */ + /* * RFC 2246, 4.7: * In digital signing, one-way hash functions are used as input for a @@ -599,6 +649,21 @@ /* Encrypted Handshake Message: Finished */ +#ifdef CONFIG_TLSV12 + if (conn->rl.tls_version >= TLS_VERSION_1_2) { + hlen = SHA256_MAC_LEN; + if (conn->verify.sha256_client == NULL || + crypto_hash_finish(conn->verify.sha256_client, hash, &hlen) + < 0) { + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + conn->verify.sha256_client = NULL; + return -1; + } + conn->verify.sha256_client = NULL; + } else { +#endif /* CONFIG_TLSV12 */ + hlen = MD5_MAC_LEN; if (conn->verify.md5_client == NULL || crypto_hash_finish(conn->verify.md5_client, hash, &hlen) < 0) { @@ -620,9 +685,15 @@ return -1; } conn->verify.sha1_client = NULL; + hlen = MD5_MAC_LEN + SHA1_MAC_LEN; + +#ifdef CONFIG_TLSV12 + } +#endif /* CONFIG_TLSV12 */ - if (tls_prf(conn->master_secret, TLS_MASTER_SECRET_LEN, - "client finished", hash, MD5_MAC_LEN + SHA1_MAC_LEN, + if (tls_prf(conn->rl.tls_version, + conn->master_secret, TLS_MASTER_SECRET_LEN, + "client finished", hash, hlen, verify_data + 1 + 3, TLS_VERIFY_DATA_LEN)) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate verify_data"); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, diff -Nru wpa-1.0/src/tls/tlsv1_common.c wpa-2.1/src/tls/tlsv1_common.c --- wpa-1.0/src/tls/tlsv1_common.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/tls/tlsv1_common.c 2014-02-04 11:23:35.000000000 +0000 @@ -1,20 +1,16 @@ /* * TLSv1 common routines - * Copyright (c) 2006-2007, Jouni Malinen + * Copyright (c) 2006-2011, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" #include "common.h" +#include "crypto/sha1.h" +#include "crypto/sha256.h" #include "x509v3.h" #include "tlsv1_common.h" @@ -50,11 +46,18 @@ { TLS_RSA_WITH_AES_256_CBC_SHA, TLS_KEY_X_RSA, TLS_CIPHER_AES_256_CBC, TLS_HASH_SHA }, { TLS_DH_anon_WITH_AES_256_CBC_SHA, TLS_KEY_X_DH_anon, - TLS_CIPHER_AES_256_CBC, TLS_HASH_SHA } + TLS_CIPHER_AES_256_CBC, TLS_HASH_SHA }, + { TLS_RSA_WITH_AES_128_CBC_SHA256, TLS_KEY_X_RSA, + TLS_CIPHER_AES_128_CBC, TLS_HASH_SHA256 }, + { TLS_RSA_WITH_AES_256_CBC_SHA256, TLS_KEY_X_RSA, + TLS_CIPHER_AES_256_CBC, TLS_HASH_SHA256 }, + { TLS_DH_anon_WITH_AES_128_CBC_SHA256, TLS_KEY_X_DH_anon, + TLS_CIPHER_AES_128_CBC, TLS_HASH_SHA256 }, + { TLS_DH_anon_WITH_AES_256_CBC_SHA256, TLS_KEY_X_DH_anon, + TLS_CIPHER_AES_256_CBC, TLS_HASH_SHA256 } }; -#define NUM_ELEMS(a) (sizeof(a) / sizeof((a)[0])) -#define NUM_TLS_CIPHER_SUITES NUM_ELEMS(tls_cipher_suites) +#define NUM_TLS_CIPHER_SUITES ARRAY_SIZE(tls_cipher_suites) static const struct tls_cipher_data tls_ciphers[] = { @@ -80,7 +83,7 @@ CRYPTO_CIPHER_ALG_AES } }; -#define NUM_TLS_CIPHER_DATA NUM_ELEMS(tls_ciphers) +#define NUM_TLS_CIPHER_DATA ARRAY_SIZE(tls_ciphers) /** @@ -202,6 +205,19 @@ tls_verify_hash_free(verify); return -1; } +#ifdef CONFIG_TLSV12 + verify->sha256_client = crypto_hash_init(CRYPTO_HASH_ALG_SHA256, NULL, + 0); + verify->sha256_server = crypto_hash_init(CRYPTO_HASH_ALG_SHA256, NULL, + 0); + verify->sha256_cert = crypto_hash_init(CRYPTO_HASH_ALG_SHA256, NULL, + 0); + if (verify->sha256_client == NULL || verify->sha256_server == NULL || + verify->sha256_cert == NULL) { + tls_verify_hash_free(verify); + return -1; + } +#endif /* CONFIG_TLSV12 */ return 0; } @@ -221,6 +237,14 @@ crypto_hash_update(verify->md5_cert, buf, len); crypto_hash_update(verify->sha1_cert, buf, len); } +#ifdef CONFIG_TLSV12 + if (verify->sha256_client) + crypto_hash_update(verify->sha256_client, buf, len); + if (verify->sha256_server) + crypto_hash_update(verify->sha256_server, buf, len); + if (verify->sha256_cert) + crypto_hash_update(verify->sha256_cert, buf, len); +#endif /* CONFIG_TLSV12 */ } @@ -238,4 +262,60 @@ verify->sha1_client = NULL; verify->sha1_server = NULL; verify->sha1_cert = NULL; +#ifdef CONFIG_TLSV12 + crypto_hash_finish(verify->sha256_client, NULL, NULL); + crypto_hash_finish(verify->sha256_server, NULL, NULL); + crypto_hash_finish(verify->sha256_cert, NULL, NULL); + verify->sha256_client = NULL; + verify->sha256_server = NULL; + verify->sha256_cert = NULL; +#endif /* CONFIG_TLSV12 */ +} + + +int tls_version_ok(u16 ver) +{ + if (ver == TLS_VERSION_1) + return 1; +#ifdef CONFIG_TLSV11 + if (ver == TLS_VERSION_1_1) + return 1; +#endif /* CONFIG_TLSV11 */ +#ifdef CONFIG_TLSV12 + if (ver == TLS_VERSION_1_2) + return 1; +#endif /* CONFIG_TLSV12 */ + + return 0; +} + + +const char * tls_version_str(u16 ver) +{ + switch (ver) { + case TLS_VERSION_1: + return "1.0"; + case TLS_VERSION_1_1: + return "1.1"; + case TLS_VERSION_1_2: + return "1.2"; + } + + return "?"; +} + + +int tls_prf(u16 ver, const u8 *secret, size_t secret_len, const char *label, + const u8 *seed, size_t seed_len, u8 *out, size_t outlen) +{ +#ifdef CONFIG_TLSV12 + if (ver >= TLS_VERSION_1_2) { + tls_prf_sha256(secret, secret_len, label, seed, seed_len, + out, outlen); + return 0; + } +#endif /* CONFIG_TLSV12 */ + + return tls_prf_sha1_md5(secret, secret_len, label, seed, seed_len, out, + outlen); } diff -Nru wpa-1.0/src/tls/tlsv1_common.h wpa-2.1/src/tls/tlsv1_common.h --- wpa-1.0/src/tls/tlsv1_common.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/tls/tlsv1_common.h 2014-02-04 11:23:35.000000000 +0000 @@ -1,15 +1,9 @@ /* - * TLS v1.0 (RFC 2246) and v1.1 (RFC 4346) common definitions + * TLSv1 common definitions * Copyright (c) 2006-2011, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef TLSV1_COMMON_H @@ -19,11 +13,16 @@ #define TLS_VERSION_1 0x0301 /* TLSv1 */ #define TLS_VERSION_1_1 0x0302 /* TLSv1.1 */ +#define TLS_VERSION_1_2 0x0303 /* TLSv1.2 */ +#ifdef CONFIG_TLSV12 +#define TLS_VERSION TLS_VERSION_1_2 +#else /* CONFIG_TLSV12 */ #ifdef CONFIG_TLSV11 #define TLS_VERSION TLS_VERSION_1_1 #else /* CONFIG_TLSV11 */ #define TLS_VERSION TLS_VERSION_1 #endif /* CONFIG_TLSV11 */ +#endif /* CONFIG_TLSV12 */ #define TLS_RANDOM_LEN 32 #define TLS_PRE_MASTER_SECRET_LEN 48 #define TLS_MASTER_SECRET_LEN 48 @@ -88,10 +87,42 @@ #define TLS_DHE_DSS_WITH_AES_256_CBC_SHA 0x0038 /* RFC 3268 */ #define TLS_DHE_RSA_WITH_AES_256_CBC_SHA 0x0039 /* RFC 3268 */ #define TLS_DH_anon_WITH_AES_256_CBC_SHA 0x003A /* RFC 3268 */ +#define TLS_RSA_WITH_NULL_SHA256 0x003B /* RFC 5246 */ +#define TLS_RSA_WITH_AES_128_CBC_SHA256 0x003C /* RFC 5246 */ +#define TLS_RSA_WITH_AES_256_CBC_SHA256 0x003D /* RFC 5246 */ +#define TLS_DH_DSS_WITH_AES_128_CBC_SHA256 0x003E /* RFC 5246 */ +#define TLS_DH_RSA_WITH_AES_128_CBC_SHA256 0x003F /* RFC 5246 */ +#define TLS_DHE_DSS_WITH_AES_128_CBC_SHA256 0x0040 /* RFC 5246 */ +#define TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 0x0067 /* RFC 5246 */ +#define TLS_DH_DSS_WITH_AES_256_CBC_SHA256 0x0068 /* RFC 5246 */ +#define TLS_DH_RSA_WITH_AES_256_CBC_SHA256 0x0069 /* RFC 5246 */ +#define TLS_DHE_DSS_WITH_AES_256_CBC_SHA256 0x006A /* RFC 5246 */ +#define TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 0x006B /* RFC 5246 */ +#define TLS_DH_anon_WITH_AES_128_CBC_SHA256 0x006C /* RFC 5246 */ +#define TLS_DH_anon_WITH_AES_256_CBC_SHA256 0x006D /* RFC 5246 */ /* CompressionMethod */ #define TLS_COMPRESSION_NULL 0 +/* HashAlgorithm */ +enum { + TLS_HASH_ALG_NONE = 0, + TLS_HASH_ALG_MD5 = 1, + TLS_HASH_ALG_SHA1 = 2, + TLS_HASH_ALG_SHA224 = 3, + TLS_HASH_ALG_SHA256 = 4, + TLS_HASH_ALG_SHA384 = 5, + TLS_HASH_ALG_SHA512 = 6 +}; + +/* SignatureAlgorithm */ +enum { + TLS_SIGN_ALG_ANONYMOUS = 0, + TLS_SIGN_ALG_RSA = 1, + TLS_SIGN_ALG_DSA = 2, + TLS_SIGN_ALG_ECDSA = 3, +}; + /* AlertLevel */ #define TLS_ALERT_LEVEL_WARNING 1 #define TLS_ALERT_LEVEL_FATAL 2 @@ -175,7 +206,8 @@ typedef enum { TLS_HASH_NULL, TLS_HASH_MD5, - TLS_HASH_SHA + TLS_HASH_SHA, + TLS_HASH_SHA256 } tls_hash; struct tls_cipher_suite { @@ -203,10 +235,13 @@ struct tls_verify_hash { struct crypto_hash *md5_client; struct crypto_hash *sha1_client; + struct crypto_hash *sha256_client; struct crypto_hash *md5_server; struct crypto_hash *sha1_server; + struct crypto_hash *sha256_server; struct crypto_hash *md5_cert; struct crypto_hash *sha1_cert; + struct crypto_hash *sha256_cert; }; @@ -218,5 +253,9 @@ void tls_verify_hash_add(struct tls_verify_hash *verify, const u8 *buf, size_t len); void tls_verify_hash_free(struct tls_verify_hash *verify); +int tls_version_ok(u16 ver); +const char * tls_version_str(u16 ver); +int tls_prf(u16 ver, const u8 *secret, size_t secret_len, const char *label, + const u8 *seed, size_t seed_len, u8 *out, size_t outlen); #endif /* TLSV1_COMMON_H */ diff -Nru wpa-1.0/src/tls/tlsv1_cred.c wpa-2.1/src/tls/tlsv1_cred.c --- wpa-1.0/src/tls/tlsv1_cred.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/tls/tlsv1_cred.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * TLSv1 credentials * Copyright (c) 2006-2009, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -244,10 +238,17 @@ if (!end) return NULL; } else { + const u8 *pos2; pos += os_strlen(pem_key_begin); end = search_tag(pem_key_end, pos, key + len - pos); if (!end) return NULL; + pos2 = search_tag("Proc-Type: 4,ENCRYPTED", pos, end - pos); + if (pos2) { + wpa_printf(MSG_DEBUG, "TLSv1: Unsupported private key " + "format (Proc-Type/DEK-Info)"); + return NULL; + } } der = base64_decode(pos, end - pos, &der_len); diff -Nru wpa-1.0/src/tls/tlsv1_cred.h wpa-2.1/src/tls/tlsv1_cred.h --- wpa-1.0/src/tls/tlsv1_cred.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/tls/tlsv1_cred.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * TLSv1 credentials * Copyright (c) 2006-2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef TLSV1_CRED_H diff -Nru wpa-1.0/src/tls/tlsv1_record.c wpa-2.1/src/tls/tlsv1_record.c --- wpa-1.0/src/tls/tlsv1_record.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/tls/tlsv1_record.c 2014-02-04 11:23:35.000000000 +0000 @@ -1,15 +1,9 @@ /* - * TLS v1.0 (RFC 2246) and v1.1 (RFC 4346) Record Protocol + * TLSv1 Record Protocol * Copyright (c) 2006-2011, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -17,6 +11,7 @@ #include "common.h" #include "crypto/md5.h" #include "crypto/sha1.h" +#include "crypto/sha256.h" #include "tlsv1_common.h" #include "tlsv1_record.h" @@ -52,6 +47,9 @@ } else if (suite->hash == TLS_HASH_SHA) { rl->hash_alg = CRYPTO_HASH_ALG_HMAC_SHA1; rl->hash_size = SHA1_MAC_LEN; + } else if (suite->hash == TLS_HASH_SHA256) { + rl->hash_alg = CRYPTO_HASH_ALG_HMAC_SHA256; + rl->hash_size = SHA256_MAC_LEN; } data = tls_get_cipher_data(suite->cipher); @@ -175,7 +173,7 @@ cpayload = pos; explicit_iv = rl->write_cipher_suite != TLS_NULL_WITH_NULL_NULL && - rl->iv_size && rl->tls_version == TLS_VERSION_1_1; + rl->iv_size && rl->tls_version >= TLS_VERSION_1_1; if (explicit_iv) { /* opaque IV[Cipherspec.block_length] */ if (pos + rl->iv_size > buf + buf_size) @@ -271,7 +269,8 @@ * @out_len: Set to maximum out_data length by caller; used to return the * length of the used data * @alert: Buffer for returning an alert value on failure - * Returns: 0 on success, -1 on failure + * Returns: Number of bytes used from in_data on success, 0 if record was not + * complete (more data needed), or -1 on failure * * This function decrypts the received message, verifies HMAC and TLS record * layer header. @@ -285,30 +284,21 @@ struct crypto_hash *hmac; u8 len[2], hash[100]; int force_mac_error = 0; - - wpa_hexdump(MSG_MSGDUMP, "TLSv1: Record Layer - Received", - in_data, in_len); + u8 ct; if (in_len < TLS_RECORD_HEADER_LEN) { - wpa_printf(MSG_DEBUG, "TLSv1: Too short record (in_len=%lu)", + wpa_printf(MSG_DEBUG, "TLSv1: Too short record (in_len=%lu) - " + "need more data", (unsigned long) in_len); - *alert = TLS_ALERT_DECODE_ERROR; - return -1; + wpa_hexdump(MSG_MSGDUMP, "TLSv1: Record Layer - Received", + in_data, in_len); + return 0; } + ct = in_data[0]; + rlen = WPA_GET_BE16(in_data + 3); wpa_printf(MSG_DEBUG, "TLSv1: Received content type %d version %d.%d " - "length %d", in_data[0], in_data[1], in_data[2], - WPA_GET_BE16(in_data + 3)); - - if (in_data[0] != TLS_CONTENT_TYPE_HANDSHAKE && - in_data[0] != TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC && - in_data[0] != TLS_CONTENT_TYPE_ALERT && - in_data[0] != TLS_CONTENT_TYPE_APPLICATION_DATA) { - wpa_printf(MSG_DEBUG, "TLSv1: Unexpected content type 0x%x", - in_data[0]); - *alert = TLS_ALERT_UNEXPECTED_MESSAGE; - return -1; - } + "length %d", ct, in_data[1], in_data[2], (int) rlen); /* * TLS v1.0 and v1.1 RFCs were not exactly clear on the use of the @@ -322,8 +312,6 @@ return -1; } - rlen = WPA_GET_BE16(in_data + 3); - /* TLSCiphertext must not be more than 2^14+2048 bytes */ if (TLS_RECORD_HEADER_LEN + rlen > 18432) { wpa_printf(MSG_DEBUG, "TLSv1: Record overflow (len=%lu)", @@ -339,7 +327,19 @@ wpa_printf(MSG_DEBUG, "TLSv1: Not all record data included " "(rlen=%lu > in_len=%lu)", (unsigned long) rlen, (unsigned long) in_len); - *alert = TLS_ALERT_DECODE_ERROR; + return 0; + } + + wpa_hexdump(MSG_MSGDUMP, "TLSv1: Record Layer - Received", + in_data, rlen); + + if (ct != TLS_CONTENT_TYPE_HANDSHAKE && + ct != TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC && + ct != TLS_CONTENT_TYPE_ALERT && + ct != TLS_CONTENT_TYPE_APPLICATION_DATA) { + wpa_printf(MSG_DEBUG, "TLSv1: Ignore record with unknown " + "content type 0x%x", ct); + *alert = TLS_ALERT_UNEXPECTED_MESSAGE; return -1; } @@ -375,7 +375,7 @@ * attacks more difficult. */ - if (rl->tls_version == TLS_VERSION_1_1) { + if (rl->tls_version >= TLS_VERSION_1_1) { /* Remove opaque IV[Cipherspec.block_length] */ if (plen < rl->iv_size) { wpa_printf(MSG_DEBUG, "TLSv1.1: Not " @@ -417,13 +417,13 @@ } plen -= padlen + 1; + + wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: Record Layer - " + "Decrypted data with IV and padding " + "removed", out_data, plen); } check_mac: - wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: Record Layer - Decrypted " - "data with IV and padding removed", - out_data, plen); - if (plen < rl->hash_size) { wpa_printf(MSG_DEBUG, "TLSv1: Too short record; no " "hash value"); @@ -481,5 +481,5 @@ inc_byte_array(rl->read_seq_num, TLS_SEQ_NUM_LEN); - return 0; + return TLS_RECORD_HEADER_LEN + rlen; } diff -Nru wpa-1.0/src/tls/tlsv1_record.h wpa-2.1/src/tls/tlsv1_record.h --- wpa-1.0/src/tls/tlsv1_record.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/tls/tlsv1_record.h 2014-02-04 11:23:35.000000000 +0000 @@ -1,15 +1,9 @@ /* - * TLS v1.0 (RFC 2246) and v1.1 (RFC 4346) Record Protocol + * TLSv1 Record Protocol * Copyright (c) 2006-2011, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef TLSV1_RECORD_H @@ -17,7 +11,7 @@ #include "crypto/crypto.h" -#define TLS_MAX_WRITE_MAC_SECRET_LEN 20 +#define TLS_MAX_WRITE_MAC_SECRET_LEN 32 #define TLS_MAX_WRITE_KEY_LEN 32 #define TLS_MAX_IV_LEN 16 #define TLS_MAX_KEY_BLOCK_LEN (2 * (TLS_MAX_WRITE_MAC_SECRET_LEN + \ diff -Nru wpa-1.0/src/tls/tlsv1_server.c wpa-2.1/src/tls/tlsv1_server.c --- wpa-1.0/src/tls/tlsv1_server.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/tls/tlsv1_server.c 2014-02-04 11:23:35.000000000 +0000 @@ -1,15 +1,9 @@ /* - * TLS v1.0 (RFC 2246) and v1.1 (RFC 4346) server + * TLS v1.0/v1.1/v1.2 server (RFC 2246, RFC 4346, RFC 5246) * Copyright (c) 2006-2011, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -49,7 +43,8 @@ os_memcpy(seed, conn->client_random, TLS_RANDOM_LEN); os_memcpy(seed + TLS_RANDOM_LEN, conn->server_random, TLS_RANDOM_LEN); - if (tls_prf(pre_master_secret, pre_master_secret_len, + if (tls_prf(conn->rl.tls_version, + pre_master_secret, pre_master_secret_len, "master secret", seed, 2 * TLS_RANDOM_LEN, conn->master_secret, TLS_MASTER_SECRET_LEN)) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive " @@ -64,7 +59,8 @@ os_memcpy(seed + TLS_RANDOM_LEN, conn->client_random, TLS_RANDOM_LEN); key_block_len = 2 * (conn->rl.hash_size + conn->rl.key_material_len + conn->rl.iv_size); - if (tls_prf(conn->master_secret, TLS_MASTER_SECRET_LEN, + if (tls_prf(conn->rl.tls_version, + conn->master_secret, TLS_MASTER_SECRET_LEN, "key expansion", seed, 2 * TLS_RANDOM_LEN, key_block, key_block_len)) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive key_block"); @@ -115,6 +111,7 @@ const u8 *pos, *end; u8 *msg = NULL, *in_msg, *in_pos, *in_end, alert, ct; size_t in_msg_len; + int used; if (in_data == NULL || in_len == 0) { wpa_printf(MSG_DEBUG, "TLSv1: No input data to server"); @@ -130,13 +127,21 @@ /* Each received packet may include multiple records */ while (pos < end) { in_msg_len = in_len; - if (tlsv1_record_receive(&conn->rl, pos, end - pos, - in_msg, &in_msg_len, &alert)) { + used = tlsv1_record_receive(&conn->rl, pos, end - pos, + in_msg, &in_msg_len, &alert); + if (used < 0) { wpa_printf(MSG_DEBUG, "TLSv1: Processing received " "record failed"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, alert); goto failed; } + if (used == 0) { + /* need more data */ + wpa_printf(MSG_DEBUG, "TLSv1: Partial processing not " + "yet supported"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, alert); + goto failed; + } ct = pos[0]; in_pos = in_msg; @@ -152,7 +157,7 @@ in_pos += in_msg_len; } - pos += TLS_RECORD_HEADER_LEN + WPA_GET_BE16(pos + 3); + pos += used; } os_free(in_msg); @@ -230,8 +235,8 @@ u8 *out_data, size_t out_len) { const u8 *in_end, *pos; - int res; - u8 alert, *out_end, *out_pos; + int used; + u8 alert, *out_end, *out_pos, ct; size_t olen; pos = in_data; @@ -240,7 +245,46 @@ out_end = out_data + out_len; while (pos < in_end) { - if (pos[0] != TLS_CONTENT_TYPE_APPLICATION_DATA) { + ct = pos[0]; + olen = out_end - out_pos; + used = tlsv1_record_receive(&conn->rl, pos, in_end - pos, + out_pos, &olen, &alert); + if (used < 0) { + wpa_printf(MSG_DEBUG, "TLSv1: Record layer processing " + "failed"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, alert); + return -1; + } + if (used == 0) { + /* need more data */ + wpa_printf(MSG_DEBUG, "TLSv1: Partial processing not " + "yet supported"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, alert); + return -1; + } + + if (ct == TLS_CONTENT_TYPE_ALERT) { + if (olen < 2) { + wpa_printf(MSG_DEBUG, "TLSv1: Alert " + "underflow"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_DECODE_ERROR); + return -1; + } + wpa_printf(MSG_DEBUG, "TLSv1: Received alert %d:%d", + out_pos[0], out_pos[1]); + if (out_pos[0] == TLS_ALERT_LEVEL_WARNING) { + /* Continue processing */ + pos += used; + continue; + } + + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + out_pos[1]); + return -1; + } + + if (ct != TLS_CONTENT_TYPE_APPLICATION_DATA) { wpa_printf(MSG_DEBUG, "TLSv1: Unexpected content type " "0x%x", pos[0]); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, @@ -248,15 +292,6 @@ return -1; } - olen = out_end - out_pos; - res = tlsv1_record_receive(&conn->rl, pos, in_end - pos, - out_pos, &olen, &alert); - if (res < 0) { - wpa_printf(MSG_DEBUG, "TLSv1: Record layer processing " - "failed"); - tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, alert); - return -1; - } out_pos += olen; if (out_pos > out_end) { wpa_printf(MSG_DEBUG, "TLSv1: Buffer not large enough " @@ -266,7 +301,7 @@ return -1; } - pos += TLS_RECORD_HEADER_LEN + WPA_GET_BE16(pos + 3); + pos += used; } return out_pos - out_data; @@ -326,9 +361,7 @@ count = 0; suites = conn->cipher_suites; -#ifndef CONFIG_CRYPTO_INTERNAL suites[count++] = TLS_RSA_WITH_AES_256_CBC_SHA; -#endif /* CONFIG_CRYPTO_INTERNAL */ suites[count++] = TLS_RSA_WITH_AES_128_CBC_SHA; suites[count++] = TLS_RSA_WITH_3DES_EDE_CBC_SHA; suites[count++] = TLS_RSA_WITH_RC4_128_SHA; @@ -410,7 +443,8 @@ TLS_RANDOM_LEN); } - return tls_prf(conn->master_secret, TLS_MASTER_SECRET_LEN, + return tls_prf(conn->rl.tls_version, + conn->master_secret, TLS_MASTER_SECRET_LEN, label, seed, 2 * TLS_RANDOM_LEN, out, out_len); } @@ -551,16 +585,12 @@ if (ciphers[0] == TLS_CIPHER_ANON_DH_AES128_SHA) { count = 0; suites = conn->cipher_suites; -#ifndef CONFIG_CRYPTO_INTERNAL suites[count++] = TLS_RSA_WITH_AES_256_CBC_SHA; -#endif /* CONFIG_CRYPTO_INTERNAL */ suites[count++] = TLS_RSA_WITH_AES_128_CBC_SHA; suites[count++] = TLS_RSA_WITH_3DES_EDE_CBC_SHA; suites[count++] = TLS_RSA_WITH_RC4_128_SHA; suites[count++] = TLS_RSA_WITH_RC4_128_MD5; -#ifndef CONFIG_CRYPTO_INTERNAL suites[count++] = TLS_DH_anon_WITH_AES_256_CBC_SHA; -#endif /* CONFIG_CRYPTO_INTERNAL */ suites[count++] = TLS_DH_anon_WITH_AES_128_CBC_SHA; suites[count++] = TLS_DH_anon_WITH_3DES_EDE_CBC_SHA; suites[count++] = TLS_DH_anon_WITH_RC4_128_MD5; diff -Nru wpa-1.0/src/tls/tlsv1_server.h wpa-2.1/src/tls/tlsv1_server.h --- wpa-1.0/src/tls/tlsv1_server.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/tls/tlsv1_server.h 2014-02-04 11:23:35.000000000 +0000 @@ -1,15 +1,9 @@ /* - * TLSv1 server (RFC 2246) - * Copyright (c) 2006-2007, Jouni Malinen + * TLS v1.0/v1.1/v1.2 server (RFC 2246, RFC 4346, RFC 5246) + * Copyright (c) 2006-2011, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef TLSV1_SERVER_H diff -Nru wpa-1.0/src/tls/tlsv1_server_i.h wpa-2.1/src/tls/tlsv1_server_i.h --- wpa-1.0/src/tls/tlsv1_server_i.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/tls/tlsv1_server_i.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * TLSv1 server - internal structures * Copyright (c) 2006-2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef TLSV1_SERVER_I_H diff -Nru wpa-1.0/src/tls/tlsv1_server_read.c wpa-2.1/src/tls/tlsv1_server_read.c --- wpa-1.0/src/tls/tlsv1_server_read.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/tls/tlsv1_server_read.c 2014-02-04 11:23:35.000000000 +0000 @@ -1,15 +1,9 @@ /* * TLSv1 server - read handshake message - * Copyright (c) 2006-2007, Jouni Malinen + * Copyright (c) 2006-2011, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -17,6 +11,7 @@ #include "common.h" #include "crypto/md5.h" #include "crypto/sha1.h" +#include "crypto/sha256.h" #include "crypto/tls.h" #include "x509v3.h" #include "tlsv1_common.h" @@ -98,12 +93,16 @@ if (TLS_VERSION == TLS_VERSION_1) conn->rl.tls_version = TLS_VERSION_1; +#ifdef CONFIG_TLSV12 + else if (conn->client_version >= TLS_VERSION_1_2) + conn->rl.tls_version = TLS_VERSION_1_2; +#endif /* CONFIG_TLSV12 */ else if (conn->client_version > TLS_VERSION_1_1) conn->rl.tls_version = TLS_VERSION_1_1; else conn->rl.tls_version = conn->client_version; wpa_printf(MSG_DEBUG, "TLSv1: Using TLS v%s", - conn->rl.tls_version == TLS_VERSION_1_1 ? "1.1" : "1.0"); + tls_version_str(conn->rl.tls_version)); /* Random random */ if (end - pos < TLS_RANDOM_LEN) @@ -841,6 +840,47 @@ hpos = hash; +#ifdef CONFIG_TLSV12 + if (conn->rl.tls_version == TLS_VERSION_1_2) { + /* + * RFC 5246, 4.7: + * TLS v1.2 adds explicit indication of the used signature and + * hash algorithms. + * + * struct { + * HashAlgorithm hash; + * SignatureAlgorithm signature; + * } SignatureAndHashAlgorithm; + */ + if (end - pos < 2) { + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_DECODE_ERROR); + return -1; + } + if (pos[0] != TLS_HASH_ALG_SHA256 || + pos[1] != TLS_SIGN_ALG_RSA) { + wpa_printf(MSG_DEBUG, "TLSv1.2: Unsupported hash(%u)/" + "signature(%u) algorithm", + pos[0], pos[1]); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + pos += 2; + + hlen = SHA256_MAC_LEN; + if (conn->verify.sha256_cert == NULL || + crypto_hash_finish(conn->verify.sha256_cert, hpos, &hlen) < + 0) { + conn->verify.sha256_cert = NULL; + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + conn->verify.sha256_cert = NULL; + } else { +#endif /* CONFIG_TLSV12 */ + if (alg == SIGN_ALG_RSA) { hlen = MD5_MAC_LEN; if (conn->verify.md5_cert == NULL || @@ -871,6 +911,10 @@ if (alg == SIGN_ALG_RSA) hlen += MD5_MAC_LEN; +#ifdef CONFIG_TLSV12 + } +#endif /* CONFIG_TLSV12 */ + wpa_hexdump(MSG_MSGDUMP, "TLSv1: CertificateVerify hash", hash, hlen); if (end - pos < 2) { @@ -910,6 +954,41 @@ wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: Decrypted Signature", buf, buflen); +#ifdef CONFIG_TLSV12 + if (conn->rl.tls_version >= TLS_VERSION_1_2) { + /* + * RFC 3447, A.2.4 RSASSA-PKCS1-v1_5 + * + * DigestInfo ::= SEQUENCE { + * digestAlgorithm DigestAlgorithm, + * digest OCTET STRING + * } + * + * SHA-256 OID: sha256WithRSAEncryption ::= {pkcs-1 11} + * + * DER encoded DigestInfo for SHA256 per RFC 3447: + * 30 31 30 0d 06 09 60 86 48 01 65 03 04 02 01 05 00 04 20 || + * H + */ + if (buflen >= 19 + 32 && + os_memcmp(buf, "\x30\x31\x30\x0d\x06\x09\x60\x86\x48\x01" + "\x65\x03\x04\x02\x01\x05\x00\x04\x20", 19) == 0) + { + wpa_printf(MSG_DEBUG, "TLSv1.2: DigestAlgorithn = " + "SHA-256"); + os_memmove(buf, buf + 19, buflen - 19); + buflen -= 19; + } else { + wpa_printf(MSG_DEBUG, "TLSv1.2: Unrecognized " + "DigestInfo"); + os_free(buf); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_DECRYPT_ERROR); + return -1; + } + } +#endif /* CONFIG_TLSV12 */ + if (buflen != hlen || os_memcmp(buf, hash, buflen) != 0) { wpa_printf(MSG_DEBUG, "TLSv1: Invalid Signature in " "CertificateVerify - did not match with calculated " @@ -1041,6 +1120,21 @@ wpa_hexdump(MSG_MSGDUMP, "TLSv1: verify_data in Finished", pos, TLS_VERIFY_DATA_LEN); +#ifdef CONFIG_TLSV12 + if (conn->rl.tls_version >= TLS_VERSION_1_2) { + hlen = SHA256_MAC_LEN; + if (conn->verify.sha256_client == NULL || + crypto_hash_finish(conn->verify.sha256_client, hash, &hlen) + < 0) { + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + conn->verify.sha256_client = NULL; + return -1; + } + conn->verify.sha256_client = NULL; + } else { +#endif /* CONFIG_TLSV12 */ + hlen = MD5_MAC_LEN; if (conn->verify.md5_client == NULL || crypto_hash_finish(conn->verify.md5_client, hash, &hlen) < 0) { @@ -1062,9 +1156,15 @@ return -1; } conn->verify.sha1_client = NULL; + hlen = MD5_MAC_LEN + SHA1_MAC_LEN; + +#ifdef CONFIG_TLSV12 + } +#endif /* CONFIG_TLSV12 */ - if (tls_prf(conn->master_secret, TLS_MASTER_SECRET_LEN, - "client finished", hash, MD5_MAC_LEN + SHA1_MAC_LEN, + if (tls_prf(conn->rl.tls_version, + conn->master_secret, TLS_MASTER_SECRET_LEN, + "client finished", hash, hlen, verify_data, TLS_VERIFY_DATA_LEN)) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive verify_data"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, diff -Nru wpa-1.0/src/tls/tlsv1_server_write.c wpa-2.1/src/tls/tlsv1_server_write.c --- wpa-1.0/src/tls/tlsv1_server_write.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/tls/tlsv1_server_write.c 2014-02-04 11:23:35.000000000 +0000 @@ -1,15 +1,9 @@ /* - * TLS v1.0 (RFC 2246) and v1.1 (RFC 4346) server - write handshake message + * TLSv1 server - write handshake message * Copyright (c) 2006-2011, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -17,6 +11,7 @@ #include "common.h" #include "crypto/md5.h" #include "crypto/sha1.h" +#include "crypto/sha256.h" #include "crypto/tls.h" #include "crypto/random.h" #include "x509v3.h" @@ -587,6 +582,21 @@ /* Encrypted Handshake Message: Finished */ +#ifdef CONFIG_TLSV12 + if (conn->rl.tls_version >= TLS_VERSION_1_2) { + hlen = SHA256_MAC_LEN; + if (conn->verify.sha256_server == NULL || + crypto_hash_finish(conn->verify.sha256_server, hash, &hlen) + < 0) { + conn->verify.sha256_server = NULL; + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + conn->verify.sha256_server = NULL; + } else { +#endif /* CONFIG_TLSV12 */ + hlen = MD5_MAC_LEN; if (conn->verify.md5_server == NULL || crypto_hash_finish(conn->verify.md5_server, hash, &hlen) < 0) { @@ -608,9 +618,15 @@ return -1; } conn->verify.sha1_server = NULL; + hlen = MD5_MAC_LEN + SHA1_MAC_LEN; + +#ifdef CONFIG_TLSV12 + } +#endif /* CONFIG_TLSV12 */ - if (tls_prf(conn->master_secret, TLS_MASTER_SECRET_LEN, - "server finished", hash, MD5_MAC_LEN + SHA1_MAC_LEN, + if (tls_prf(conn->rl.tls_version, + conn->master_secret, TLS_MASTER_SECRET_LEN, + "server finished", hash, hlen, verify_data + 1 + 3, TLS_VERIFY_DATA_LEN)) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate verify_data"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, diff -Nru wpa-1.0/src/tls/x509v3.c wpa-2.1/src/tls/x509v3.c --- wpa-1.0/src/tls/x509v3.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/tls/x509v3.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * X.509v3 certificate parsing and processing (RFC 3280 profile) * Copyright (c) 2006-2011, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -449,17 +443,16 @@ return -1; } - val = os_malloc(hdr.length + 1); + val = dup_binstr(hdr.payload, hdr.length); if (val == NULL) { x509_free_name(name); return -1; } - os_memcpy(val, hdr.payload, hdr.length); - val[hdr.length] = '\0'; if (os_strlen(val) != hdr.length) { wpa_printf(MSG_INFO, "X509: Reject certificate with " "embedded NUL byte in a string (%s[NUL])", val); + os_free(val); x509_free_name(name); return -1; } diff -Nru wpa-1.0/src/tls/x509v3.h wpa-2.1/src/tls/x509v3.h --- wpa-1.0/src/tls/x509v3.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/tls/x509v3.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * X.509v3 certificate parsing and processing * Copyright (c) 2006-2011, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef X509V3_H diff -Nru wpa-1.0/src/utils/base64.c wpa-2.1/src/utils/base64.c --- wpa-1.0/src/utils/base64.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/utils/base64.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * Base64 encoding/decoding (RFC1341) * Copyright (c) 2005-2011, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff -Nru wpa-1.0/src/utils/base64.h wpa-2.1/src/utils/base64.h --- wpa-1.0/src/utils/base64.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/utils/base64.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * Base64 encoding/decoding (RFC1341) * Copyright (c) 2005, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef BASE64_H diff -Nru wpa-1.0/src/utils/bitfield.c wpa-2.1/src/utils/bitfield.c --- wpa-1.0/src/utils/bitfield.c 1970-01-01 00:00:00.000000000 +0000 +++ wpa-2.1/src/utils/bitfield.c 2014-02-04 11:23:35.000000000 +0000 @@ -0,0 +1,89 @@ +/* + * Bitfield + * Copyright (c) 2013, 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 "bitfield.h" + + +struct bitfield { + u8 *bits; + size_t max_bits; +}; + + +struct bitfield * bitfield_alloc(size_t max_bits) +{ + struct bitfield *bf; + + bf = os_zalloc(sizeof(*bf) + (max_bits + 7) / 8); + if (bf == NULL) + return NULL; + bf->bits = (u8 *) (bf + 1); + bf->max_bits = max_bits; + return bf; +} + + +void bitfield_free(struct bitfield *bf) +{ + os_free(bf); +} + + +void bitfield_set(struct bitfield *bf, size_t bit) +{ + if (bit >= bf->max_bits) + return; + bf->bits[bit / 8] |= BIT(bit % 8); +} + + +void bitfield_clear(struct bitfield *bf, size_t bit) +{ + if (bit >= bf->max_bits) + return; + bf->bits[bit / 8] &= ~BIT(bit % 8); +} + + +int bitfield_is_set(struct bitfield *bf, size_t bit) +{ + if (bit >= bf->max_bits) + return 0; + return !!(bf->bits[bit / 8] & BIT(bit % 8)); +} + + +static int first_zero(u8 val) +{ + int i; + for (i = 0; i < 8; i++) { + if (!(val & 0x01)) + return i; + val >>= 1; + } + return -1; +} + + +int bitfield_get_first_zero(struct bitfield *bf) +{ + size_t i; + for (i = 0; i <= (bf->max_bits + 7) / 8; i++) { + if (bf->bits[i] != 0xff) + break; + } + if (i > (bf->max_bits + 7) / 8) + return -1; + i = i * 8 + first_zero(bf->bits[i]); + if (i >= bf->max_bits) + return -1; + return i; +} diff -Nru wpa-1.0/src/utils/bitfield.h wpa-2.1/src/utils/bitfield.h --- wpa-1.0/src/utils/bitfield.h 1970-01-01 00:00:00.000000000 +0000 +++ wpa-2.1/src/utils/bitfield.h 2014-02-04 11:23:35.000000000 +0000 @@ -0,0 +1,21 @@ +/* + * Bitfield + * Copyright (c) 2013, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef BITFIELD_H +#define BITFIELD_H + +struct bitfield; + +struct bitfield * bitfield_alloc(size_t max_bits); +void bitfield_free(struct bitfield *bf); +void bitfield_set(struct bitfield *bf, size_t bit); +void bitfield_clear(struct bitfield *bf, size_t bit); +int bitfield_is_set(struct bitfield *bf, size_t bit); +int bitfield_get_first_zero(struct bitfield *bf); + +#endif /* BITFIELD_H */ diff -Nru wpa-1.0/src/utils/build_config.h wpa-2.1/src/utils/build_config.h --- wpa-1.0/src/utils/build_config.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/utils/build_config.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * wpa_supplicant/hostapd - Build time configuration defines * Copyright (c) 2005-2006, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. * * This header file can be used to define configuration defines that were * originally defined in Makefile. This is mainly meant for IDE use or for @@ -53,53 +47,4 @@ #endif /* USE_INTERNAL_CRYPTO */ #endif /* CONFIG_WIN32_DEFAULTS */ -#ifdef __SYMBIAN32__ -#define OS_NO_C_LIB_DEFINES -#define CONFIG_ANSI_C_EXTRA -#define CONFIG_NO_WPA_MSG -#define CONFIG_NO_HOSTAPD_LOGGER -#define CONFIG_NO_STDOUT_DEBUG -#define CONFIG_BACKEND_FILE -#define CONFIG_INTERNAL_LIBTOMMATH -#define CONFIG_CRYPTO_INTERNAL -#define IEEE8021X_EAPOL -#define PKCS12_FUNCS -#define EAP_MD5 -#define EAP_TLS -#define EAP_MSCHAPv2 -#define EAP_PEAP -#define EAP_TTLS -#define EAP_GTC -#define EAP_OTP -#define EAP_LEAP -#define EAP_FAST -#endif /* __SYMBIAN32__ */ - -#ifdef CONFIG_XCODE_DEFAULTS -#define CONFIG_DRIVER_OSX -#define CONFIG_BACKEND_FILE -#define IEEE8021X_EAPOL -#define PKCS12_FUNCS -#define CONFIG_CTRL_IFACE -#define CONFIG_CTRL_IFACE_UNIX -#define CONFIG_DEBUG_FILE -#define EAP_MD5 -#define EAP_TLS -#define EAP_MSCHAPv2 -#define EAP_PEAP -#define EAP_TTLS -#define EAP_GTC -#define EAP_OTP -#define EAP_LEAP -#define EAP_TNC -#define CONFIG_WPS -#define EAP_WSC - -#ifdef USE_INTERNAL_CRYPTO -#define CONFIG_TLS_INTERNAL_CLIENT -#define CONFIG_INTERNAL_LIBTOMMATH -#define CONFIG_CRYPTO_INTERNAL -#endif /* USE_INTERNAL_CRYPTO */ -#endif /* CONFIG_XCODE_DEFAULTS */ - #endif /* BUILD_CONFIG_H */ diff -Nru wpa-1.0/src/utils/common.c wpa-2.1/src/utils/common.c --- wpa-1.0/src/utils/common.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/utils/common.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * wpa_supplicant/hostapd / common helper functions, etc. * Copyright (c) 2002-2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -350,6 +344,137 @@ #endif /* CONFIG_NATIVE_WINDOWS */ +void printf_encode(char *txt, size_t maxlen, const u8 *data, size_t len) +{ + char *end = txt + maxlen; + size_t i; + + for (i = 0; i < len; i++) { + if (txt + 4 > end) + break; + + switch (data[i]) { + case '\"': + *txt++ = '\\'; + *txt++ = '\"'; + break; + case '\\': + *txt++ = '\\'; + *txt++ = '\\'; + break; + case '\e': + *txt++ = '\\'; + *txt++ = 'e'; + break; + case '\n': + *txt++ = '\\'; + *txt++ = 'n'; + break; + case '\r': + *txt++ = '\\'; + *txt++ = 'r'; + break; + case '\t': + *txt++ = '\\'; + *txt++ = 't'; + break; + default: + if (data[i] >= 32 && data[i] <= 127) { + *txt++ = data[i]; + } else { + txt += os_snprintf(txt, end - txt, "\\x%02x", + data[i]); + } + break; + } + } + + *txt = '\0'; +} + + +size_t printf_decode(u8 *buf, size_t maxlen, const char *str) +{ + const char *pos = str; + size_t len = 0; + int val; + + while (*pos) { + if (len + 1 >= maxlen) + break; + switch (*pos) { + case '\\': + pos++; + switch (*pos) { + case '\\': + buf[len++] = '\\'; + pos++; + break; + case '"': + buf[len++] = '"'; + pos++; + break; + case 'n': + buf[len++] = '\n'; + pos++; + break; + case 'r': + buf[len++] = '\r'; + pos++; + break; + case 't': + buf[len++] = '\t'; + pos++; + break; + case 'e': + buf[len++] = '\e'; + pos++; + break; + case 'x': + pos++; + val = hex2byte(pos); + if (val < 0) { + val = hex2num(*pos); + if (val < 0) + break; + buf[len++] = val; + pos++; + } else { + buf[len++] = val; + pos += 2; + } + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + val = *pos++ - '0'; + if (*pos >= '0' && *pos <= '7') + val = val * 8 + (*pos++ - '0'); + if (*pos >= '0' && *pos <= '7') + val = val * 8 + (*pos++ - '0'); + buf[len++] = val; + break; + default: + break; + } + break; + default: + buf[len++] = *pos++; + break; + } + } + if (maxlen > len) + buf[len] = '\0'; + + return len; +} + + /** * wpa_ssid_txt - Convert SSID to a printable string * @ssid: SSID (32-octet string) @@ -366,17 +491,14 @@ */ const char * wpa_ssid_txt(const u8 *ssid, size_t ssid_len) { - static char ssid_txt[33]; - char *pos; + static char ssid_txt[32 * 4 + 1]; - if (ssid_len > 32) - ssid_len = 32; - os_memcpy(ssid_txt, ssid, ssid_len); - ssid_txt[ssid_len] = '\0'; - for (pos = ssid_txt; *pos != '\0'; pos++) { - if ((u8) *pos < 32 || (u8) *pos >= 127) - *pos = '_'; + if (ssid == NULL) { + ssid_txt[0] = '\0'; + return ssid_txt; } + + printf_encode(ssid_txt, sizeof(ssid_txt), ssid, ssid_len); return ssid_txt; } @@ -385,3 +507,323 @@ { return foo; } + + +char * wpa_config_parse_string(const char *value, size_t *len) +{ + if (*value == '"') { + const char *pos; + char *str; + value++; + pos = os_strrchr(value, '"'); + if (pos == NULL || pos[1] != '\0') + return NULL; + *len = pos - value; + str = dup_binstr(value, *len); + if (str == NULL) + return NULL; + return str; + } else if (*value == 'P' && value[1] == '"') { + const char *pos; + char *tstr, *str; + size_t tlen; + value += 2; + pos = os_strrchr(value, '"'); + if (pos == NULL || pos[1] != '\0') + return NULL; + tlen = pos - value; + tstr = dup_binstr(value, tlen); + if (tstr == NULL) + return NULL; + + str = os_malloc(tlen + 1); + if (str == NULL) { + os_free(tstr); + return NULL; + } + + *len = printf_decode((u8 *) str, tlen + 1, tstr); + os_free(tstr); + + return str; + } else { + u8 *str; + size_t tlen, hlen = os_strlen(value); + if (hlen & 1) + return NULL; + tlen = hlen / 2; + str = os_malloc(tlen + 1); + if (str == NULL) + return NULL; + if (hexstr2bin(value, str, tlen)) { + os_free(str); + return NULL; + } + str[tlen] = '\0'; + *len = tlen; + return (char *) str; + } +} + + +int is_hex(const u8 *data, size_t len) +{ + size_t i; + + for (i = 0; i < len; i++) { + if (data[i] < 32 || data[i] >= 127) + return 1; + } + return 0; +} + + +int find_first_bit(u32 value) +{ + int pos = 0; + + while (value) { + if (value & 0x1) + return pos; + value >>= 1; + pos++; + } + + return -1; +} + + +size_t merge_byte_arrays(u8 *res, size_t res_len, + const u8 *src1, size_t src1_len, + const u8 *src2, size_t src2_len) +{ + size_t len = 0; + + os_memset(res, 0, res_len); + + if (src1) { + if (src1_len >= res_len) { + os_memcpy(res, src1, res_len); + return res_len; + } + + os_memcpy(res, src1, src1_len); + len += src1_len; + } + + if (src2) { + if (len + src2_len >= res_len) { + os_memcpy(res + len, src2, res_len - len); + return res_len; + } + + os_memcpy(res + len, src2, src2_len); + len += src2_len; + } + + return len; +} + + +char * dup_binstr(const void *src, size_t len) +{ + char *res; + + if (src == NULL) + return NULL; + res = os_malloc(len + 1); + if (res == NULL) + return NULL; + os_memcpy(res, src, len); + res[len] = '\0'; + + return res; +} + + +int freq_range_list_parse(struct wpa_freq_range_list *res, const char *value) +{ + struct wpa_freq_range *freq = NULL, *n; + unsigned int count = 0; + const char *pos, *pos2, *pos3; + + /* + * Comma separated list of frequency ranges. + * For example: 2412-2432,2462,5000-6000 + */ + pos = value; + while (pos && pos[0]) { + n = os_realloc_array(freq, count + 1, + sizeof(struct wpa_freq_range)); + if (n == NULL) { + os_free(freq); + return -1; + } + freq = n; + freq[count].min = atoi(pos); + pos2 = os_strchr(pos, '-'); + pos3 = os_strchr(pos, ','); + if (pos2 && (!pos3 || pos2 < pos3)) { + pos2++; + freq[count].max = atoi(pos2); + } else + freq[count].max = freq[count].min; + pos = pos3; + if (pos) + pos++; + count++; + } + + os_free(res->range); + res->range = freq; + res->num = count; + + return 0; +} + + +int freq_range_list_includes(const struct wpa_freq_range_list *list, + unsigned int freq) +{ + unsigned int i; + + if (list == NULL) + return 0; + + for (i = 0; i < list->num; i++) { + if (freq >= list->range[i].min && freq <= list->range[i].max) + return 1; + } + + return 0; +} + + +char * freq_range_list_str(const struct wpa_freq_range_list *list) +{ + char *buf, *pos, *end; + size_t maxlen; + unsigned int i; + int res; + + if (list->num == 0) + return NULL; + + maxlen = list->num * 30; + buf = os_malloc(maxlen); + if (buf == NULL) + return NULL; + pos = buf; + end = buf + maxlen; + + for (i = 0; i < list->num; i++) { + struct wpa_freq_range *range = &list->range[i]; + + if (range->min == range->max) + res = os_snprintf(pos, end - pos, "%s%u", + i == 0 ? "" : ",", range->min); + else + res = os_snprintf(pos, end - pos, "%s%u-%u", + i == 0 ? "" : ",", + range->min, range->max); + if (res < 0 || res > end - pos) { + os_free(buf); + return NULL; + } + pos += res; + } + + return buf; +} + + +int int_array_len(const int *a) +{ + int i; + for (i = 0; a && a[i]; i++) + ; + return i; +} + + +void int_array_concat(int **res, const int *a) +{ + int reslen, alen, i; + int *n; + + reslen = int_array_len(*res); + alen = int_array_len(a); + + n = os_realloc_array(*res, reslen + alen + 1, sizeof(int)); + if (n == NULL) { + os_free(*res); + *res = NULL; + return; + } + for (i = 0; i <= alen; i++) + n[reslen + i] = a[i]; + *res = n; +} + + +static int freq_cmp(const void *a, const void *b) +{ + int _a = *(int *) a; + int _b = *(int *) b; + + if (_a == 0) + return 1; + if (_b == 0) + return -1; + return _a - _b; +} + + +void int_array_sort_unique(int *a) +{ + int alen; + int i, j; + + if (a == NULL) + return; + + alen = int_array_len(a); + qsort(a, alen, sizeof(int), freq_cmp); + + i = 0; + j = 1; + while (a[i] && a[j]) { + if (a[i] == a[j]) { + j++; + continue; + } + a[++i] = a[j++]; + } + if (a[i]) + i++; + a[i] = 0; +} + + +void int_array_add_unique(int **res, int a) +{ + int reslen; + int *n; + + for (reslen = 0; *res && (*res)[reslen]; reslen++) { + if ((*res)[reslen] == a) + return; /* already in the list */ + } + + n = os_realloc_array(*res, reslen + 2, sizeof(int)); + if (n == NULL) { + os_free(*res); + *res = NULL; + return; + } + + n[reslen] = a; + n[reslen + 1] = 0; + + *res = n; +} diff -Nru wpa-1.0/src/utils/common.h wpa-2.1/src/utils/common.h --- wpa-1.0/src/utils/common.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/utils/common.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * wpa_supplicant/hostapd / common helper functions, etc. * Copyright (c) 2002-2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef COMMON_H @@ -69,12 +63,6 @@ #endif #endif /* CONFIG_TI_COMPILER */ -#ifdef __SYMBIAN32__ -#define __BIG_ENDIAN 4321 -#define __LITTLE_ENDIAN 1234 -#define __BYTE_ORDER __LITTLE_ENDIAN -#endif /* __SYMBIAN32__ */ - #ifdef CONFIG_NATIVE_WINDOWS #include @@ -138,16 +126,6 @@ #define WPA_TYPES_DEFINED #endif /* CONFIG_TI_COMPILER */ -#ifdef __SYMBIAN32__ -#define __REMOVE_PLATSEC_DIAGNOSTICS__ -#include -typedef TUint64 u64; -typedef TUint32 u32; -typedef TUint16 u16; -typedef TUint8 u8; -#define WPA_TYPES_DEFINED -#endif /* __SYMBIAN32__ */ - #ifndef WPA_TYPES_DEFINED #ifdef CONFIG_USE_INTTYPES_H #include @@ -227,6 +205,7 @@ #define be_to_host16(n) (n) #define host_to_be16(n) (n) #define le_to_host32(n) bswap_32(n) +#define host_to_le32(n) bswap_32(n) #define be_to_host32(n) (n) #define host_to_be32(n) (n) #define le_to_host64(n) bswap_64(n) @@ -246,69 +225,105 @@ /* Macros for handling unaligned memory accesses */ -#define WPA_GET_BE16(a) ((u16) (((a)[0] << 8) | (a)[1])) -#define WPA_PUT_BE16(a, val) \ - do { \ - (a)[0] = ((u16) (val)) >> 8; \ - (a)[1] = ((u16) (val)) & 0xff; \ - } while (0) - -#define WPA_GET_LE16(a) ((u16) (((a)[1] << 8) | (a)[0])) -#define WPA_PUT_LE16(a, val) \ - do { \ - (a)[1] = ((u16) (val)) >> 8; \ - (a)[0] = ((u16) (val)) & 0xff; \ - } while (0) - -#define WPA_GET_BE24(a) ((((u32) (a)[0]) << 16) | (((u32) (a)[1]) << 8) | \ - ((u32) (a)[2])) -#define WPA_PUT_BE24(a, val) \ - do { \ - (a)[0] = (u8) ((((u32) (val)) >> 16) & 0xff); \ - (a)[1] = (u8) ((((u32) (val)) >> 8) & 0xff); \ - (a)[2] = (u8) (((u32) (val)) & 0xff); \ - } while (0) - -#define WPA_GET_BE32(a) ((((u32) (a)[0]) << 24) | (((u32) (a)[1]) << 16) | \ - (((u32) (a)[2]) << 8) | ((u32) (a)[3])) -#define WPA_PUT_BE32(a, val) \ - do { \ - (a)[0] = (u8) ((((u32) (val)) >> 24) & 0xff); \ - (a)[1] = (u8) ((((u32) (val)) >> 16) & 0xff); \ - (a)[2] = (u8) ((((u32) (val)) >> 8) & 0xff); \ - (a)[3] = (u8) (((u32) (val)) & 0xff); \ - } while (0) - -#define WPA_GET_LE32(a) ((((u32) (a)[3]) << 24) | (((u32) (a)[2]) << 16) | \ - (((u32) (a)[1]) << 8) | ((u32) (a)[0])) -#define WPA_PUT_LE32(a, val) \ - do { \ - (a)[3] = (u8) ((((u32) (val)) >> 24) & 0xff); \ - (a)[2] = (u8) ((((u32) (val)) >> 16) & 0xff); \ - (a)[1] = (u8) ((((u32) (val)) >> 8) & 0xff); \ - (a)[0] = (u8) (((u32) (val)) & 0xff); \ - } while (0) - -#define WPA_GET_BE64(a) ((((u64) (a)[0]) << 56) | (((u64) (a)[1]) << 48) | \ - (((u64) (a)[2]) << 40) | (((u64) (a)[3]) << 32) | \ - (((u64) (a)[4]) << 24) | (((u64) (a)[5]) << 16) | \ - (((u64) (a)[6]) << 8) | ((u64) (a)[7])) -#define WPA_PUT_BE64(a, val) \ - do { \ - (a)[0] = (u8) (((u64) (val)) >> 56); \ - (a)[1] = (u8) (((u64) (val)) >> 48); \ - (a)[2] = (u8) (((u64) (val)) >> 40); \ - (a)[3] = (u8) (((u64) (val)) >> 32); \ - (a)[4] = (u8) (((u64) (val)) >> 24); \ - (a)[5] = (u8) (((u64) (val)) >> 16); \ - (a)[6] = (u8) (((u64) (val)) >> 8); \ - (a)[7] = (u8) (((u64) (val)) & 0xff); \ - } while (0) - -#define WPA_GET_LE64(a) ((((u64) (a)[7]) << 56) | (((u64) (a)[6]) << 48) | \ - (((u64) (a)[5]) << 40) | (((u64) (a)[4]) << 32) | \ - (((u64) (a)[3]) << 24) | (((u64) (a)[2]) << 16) | \ - (((u64) (a)[1]) << 8) | ((u64) (a)[0])) +static inline u16 WPA_GET_BE16(const u8 *a) +{ + return (a[0] << 8) | a[1]; +} + +static inline void WPA_PUT_BE16(u8 *a, u16 val) +{ + a[0] = val >> 8; + a[1] = val & 0xff; +} + +static inline u16 WPA_GET_LE16(const u8 *a) +{ + return (a[1] << 8) | a[0]; +} + +static inline void WPA_PUT_LE16(u8 *a, u16 val) +{ + a[1] = val >> 8; + a[0] = val & 0xff; +} + +static inline u32 WPA_GET_BE24(const u8 *a) +{ + return (a[0] << 16) | (a[1] << 8) | a[2]; +} + +static inline void WPA_PUT_BE24(u8 *a, u32 val) +{ + a[0] = (val >> 16) & 0xff; + a[1] = (val >> 8) & 0xff; + a[2] = val & 0xff; +} + +static inline u32 WPA_GET_BE32(const u8 *a) +{ + return (a[0] << 24) | (a[1] << 16) | (a[2] << 8) | a[3]; +} + +static inline void WPA_PUT_BE32(u8 *a, u32 val) +{ + a[0] = (val >> 24) & 0xff; + a[1] = (val >> 16) & 0xff; + a[2] = (val >> 8) & 0xff; + a[3] = val & 0xff; +} + +static inline u32 WPA_GET_LE32(const u8 *a) +{ + return (a[3] << 24) | (a[2] << 16) | (a[1] << 8) | a[0]; +} + +static inline void WPA_PUT_LE32(u8 *a, u32 val) +{ + a[3] = (val >> 24) & 0xff; + a[2] = (val >> 16) & 0xff; + a[1] = (val >> 8) & 0xff; + a[0] = val & 0xff; +} + +static inline u64 WPA_GET_BE64(const u8 *a) +{ + return (((u64) a[0]) << 56) | (((u64) a[1]) << 48) | + (((u64) a[2]) << 40) | (((u64) a[3]) << 32) | + (((u64) a[4]) << 24) | (((u64) a[5]) << 16) | + (((u64) a[6]) << 8) | ((u64) a[7]); +} + +static inline void WPA_PUT_BE64(u8 *a, u64 val) +{ + a[0] = val >> 56; + a[1] = val >> 48; + a[2] = val >> 40; + a[3] = val >> 32; + a[4] = val >> 24; + a[5] = val >> 16; + a[6] = val >> 8; + a[7] = val & 0xff; +} + +static inline u64 WPA_GET_LE64(const u8 *a) +{ + return (((u64) a[7]) << 56) | (((u64) a[6]) << 48) | + (((u64) a[5]) << 40) | (((u64) a[4]) << 32) | + (((u64) a[3]) << 24) | (((u64) a[2]) << 16) | + (((u64) a[1]) << 8) | ((u64) a[0]); +} + +static inline void WPA_PUT_LE64(u8 *a, u64 val) +{ + a[7] = val >> 56; + a[6] = val >> 48; + a[5] = val >> 40; + a[4] = val >> 32; + a[3] = val >> 24; + a[2] = val >> 16; + a[1] = val >> 8; + a[0] = val & 0xff; +} #ifndef ETH_ALEN @@ -444,6 +459,14 @@ #endif /* __GNUC__ */ #endif /* __must_check */ +#ifndef __maybe_unused +#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) +#define __maybe_unused __attribute__((unused)) +#else +#define __maybe_unused +#endif /* __GNUC__ */ +#endif /* __must_check */ + int hwaddr_aton(const char *txt, u8 *addr); int hwaddr_compact_aton(const char *txt, u8 *addr); int hwaddr_aton2(const char *txt, u8 *addr); @@ -463,8 +486,19 @@ #define wpa_strdup_tchar(s) strdup((s)) #endif /* CONFIG_NATIVE_WINDOWS */ +void printf_encode(char *txt, size_t maxlen, const u8 *data, size_t len); +size_t printf_decode(u8 *buf, size_t maxlen, const char *str); + const char * wpa_ssid_txt(const u8 *ssid, size_t ssid_len); +char * wpa_config_parse_string(const char *value, size_t *len); +int is_hex(const u8 *data, size_t len); +int find_first_bit(u32 value); +size_t merge_byte_arrays(u8 *res, size_t res_len, + const u8 *src1, size_t src1_len, + const u8 *src2, size_t src2_len); +char * dup_binstr(const void *src, size_t len); + static inline int is_zero_ether_addr(const u8 *a) { return !(a[0] | a[1] | a[2] | a[3] | a[4] | a[5]); @@ -480,6 +514,27 @@ #include "wpa_debug.h" +struct wpa_freq_range_list { + struct wpa_freq_range { + unsigned int min; + unsigned int max; + } *range; + unsigned int num; +}; + +int freq_range_list_parse(struct wpa_freq_range_list *res, const char *value); +int freq_range_list_includes(const struct wpa_freq_range_list *list, + unsigned int freq); +char * freq_range_list_str(const struct wpa_freq_range_list *list); + +int int_array_len(const int *a); +void int_array_concat(int **res, const int *a); +void int_array_sort_unique(int *a); +void int_array_add_unique(int **res, int a); + +#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) + + /* * gcc 4.4 ends up generating strict-aliasing warnings about some very common * networking socket uses that do not really result in a real problem and diff -Nru wpa-1.0/src/utils/edit.c wpa-2.1/src/utils/edit.c --- wpa-1.0/src/utils/edit.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/utils/edit.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * Command line editing and history * Copyright (c) 2010-2011, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -26,6 +20,7 @@ static int cmdbuf_len = 0; static char currbuf[CMD_BUF_LEN]; static int currbuf_valid = 0; +static const char *ps2 = NULL; #define HISTORY_MAX 100 @@ -53,7 +48,7 @@ { int i; putchar('\r'); - for (i = 0; i < cmdbuf_len + 2; i++) + for (i = 0; i < cmdbuf_len + 2 + (ps2 ? (int) os_strlen(ps2) : 0); i++) putchar(' '); } @@ -350,9 +345,9 @@ static void process_cmd(void) { - + currbuf_valid = 0; if (cmdbuf_len == 0) { - printf("\n> "); + printf("\n%s> ", ps2 ? ps2 : ""); fflush(stdout); return; } @@ -362,7 +357,7 @@ cmdbuf_pos = 0; cmdbuf_len = 0; edit_cmd_cb(edit_cb_ctx, cmdbuf); - printf("> "); + printf("%s> ", ps2 ? ps2 : ""); fflush(stdout); } @@ -1118,7 +1113,7 @@ int edit_init(void (*cmd_cb)(void *ctx, char *cmd), void (*eof_cb)(void *ctx), char ** (*completion_cb)(void *ctx, const char *cmd, int pos), - void *ctx, const char *history_file) + void *ctx, const char *history_file, const char *ps) { currbuf[0] = '\0'; dl_list_init(&history_list); @@ -1138,7 +1133,8 @@ eloop_register_read_sock(STDIN_FILENO, edit_read_char, NULL, NULL); - printf("> "); + ps2 = ps; + printf("%s> ", ps2 ? ps2 : ""); fflush(stdout); return 0; @@ -1167,11 +1163,11 @@ { char tmp; cmdbuf[cmdbuf_len] = '\0'; - printf("\r> %s", cmdbuf); + printf("\r%s> %s", ps2 ? ps2 : "", cmdbuf); if (cmdbuf_pos != cmdbuf_len) { tmp = cmdbuf[cmdbuf_pos]; cmdbuf[cmdbuf_pos] = '\0'; - printf("\r> %s", cmdbuf); + printf("\r%s> %s", ps2 ? ps2 : "", cmdbuf); cmdbuf[cmdbuf_pos] = tmp; } fflush(stdout); diff -Nru wpa-1.0/src/utils/edit.h wpa-2.1/src/utils/edit.h --- wpa-1.0/src/utils/edit.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/utils/edit.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * Command line editing and history * Copyright (c) 2010, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef EDIT_H @@ -18,7 +12,7 @@ int edit_init(void (*cmd_cb)(void *ctx, char *cmd), void (*eof_cb)(void *ctx), char ** (*completion_cb)(void *ctx, const char *cmd, int pos), - void *ctx, const char *history_file); + void *ctx, const char *history_file, const char *ps); void edit_deinit(const char *history_file, int (*filter_cb)(void *ctx, const char *cmd)); void edit_clear_line(void); diff -Nru wpa-1.0/src/utils/edit_readline.c wpa-2.1/src/utils/edit_readline.c --- wpa-1.0/src/utils/edit_readline.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/utils/edit_readline.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * Command line editing and history wrapper for readline * Copyright (c) 2010, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -118,7 +112,7 @@ int edit_init(void (*cmd_cb)(void *ctx, char *cmd), void (*eof_cb)(void *ctx), char ** (*completion_cb)(void *ctx, const char *cmd, int pos), - void *ctx, const char *history_file) + void *ctx, const char *history_file, const char *ps) { edit_cb_ctx = ctx; edit_cmd_cb = cmd_cb; @@ -133,6 +127,17 @@ eloop_register_read_sock(STDIN_FILENO, edit_read_char, NULL, NULL); + if (ps) { + size_t blen = os_strlen(ps) + 3; + char *ps2 = os_malloc(blen); + if (ps2) { + os_snprintf(ps2, blen, "%s> ", ps); + rl_callback_handler_install(ps2, readline_cmd_handler); + os_free(ps2); + return 0; + } + } + rl_callback_handler_install("> ", readline_cmd_handler); return 0; @@ -142,6 +147,9 @@ void edit_deinit(const char *history_file, int (*filter_cb)(void *ctx, const char *cmd)) { + rl_set_prompt(""); + rl_replace_line("", 0); + rl_redisplay(); rl_callback_handler_remove(); readline_free_completions(); @@ -159,9 +167,9 @@ if (filter_cb && filter_cb(edit_cb_ctx, p)) { h = remove_history(where_history()); if (h) { - os_free(h->line); + free(h->line); free(h->data); - os_free(h); + free(h); } else next_history(); } else diff -Nru wpa-1.0/src/utils/edit_simple.c wpa-2.1/src/utils/edit_simple.c --- wpa-1.0/src/utils/edit_simple.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/utils/edit_simple.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * Minimal command line editing * Copyright (c) 2010, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -22,6 +16,7 @@ #define CMD_BUF_LEN 256 static char cmdbuf[CMD_BUF_LEN]; static int cmdbuf_pos = 0; +static const char *ps2 = NULL; static void *edit_cb_ctx; static void (*edit_cmd_cb)(void *ctx, char *cmd); @@ -47,7 +42,7 @@ cmdbuf[cmdbuf_pos] = '\0'; cmdbuf_pos = 0; edit_cmd_cb(edit_cb_ctx, cmdbuf); - printf("> "); + printf("%s> ", ps2 ? ps2 : ""); fflush(stdout); return; } @@ -63,14 +58,15 @@ int edit_init(void (*cmd_cb)(void *ctx, char *cmd), void (*eof_cb)(void *ctx), char ** (*completion_cb)(void *ctx, const char *cmd, int pos), - void *ctx, const char *history_file) + void *ctx, const char *history_file, const char *ps) { edit_cb_ctx = ctx; edit_cmd_cb = cmd_cb; edit_eof_cb = eof_cb; eloop_register_read_sock(STDIN_FILENO, edit_read_char, NULL, NULL); + ps2 = ps; - printf("> "); + printf("%s> ", ps2 ? ps2 : ""); fflush(stdout); return 0; diff -Nru wpa-1.0/src/utils/eloop.c wpa-2.1/src/utils/eloop.c --- wpa-1.0/src/utils/eloop.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/utils/eloop.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * Event loop based on select() loop * Copyright (c) 2002-2009, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -19,6 +13,11 @@ #include "list.h" #include "eloop.h" +#ifdef CONFIG_ELOOP_POLL +#include +#include +#endif /* CONFIG_ELOOP_POLL */ + struct eloop_sock { int sock; @@ -32,7 +31,7 @@ struct eloop_timeout { struct dl_list list; - struct os_time time; + struct os_reltime time; void *eloop_data; void *user_data; eloop_timeout_handler handler; @@ -57,6 +56,13 @@ struct eloop_data { int max_sock; + int count; /* sum of all table counts */ +#ifdef CONFIG_ELOOP_POLL + int max_pollfd_map; /* number of pollfds_map currently allocated */ + int max_poll_fds; /* number of pollfds currently allocated */ + struct pollfd *pollfds; + struct pollfd **pollfds_map; +#endif /* CONFIG_ELOOP_POLL */ struct eloop_sock_table readers; struct eloop_sock_table writers; struct eloop_sock_table exceptions; @@ -134,14 +140,44 @@ void *eloop_data, void *user_data) { struct eloop_sock *tmp; + int new_max_sock; + + if (sock > eloop.max_sock) + new_max_sock = sock; + else + new_max_sock = eloop.max_sock; if (table == NULL) return -1; +#ifdef CONFIG_ELOOP_POLL + if (new_max_sock >= eloop.max_pollfd_map) { + struct pollfd **nmap; + nmap = os_realloc_array(eloop.pollfds_map, new_max_sock + 50, + sizeof(struct pollfd *)); + if (nmap == NULL) + return -1; + + eloop.max_pollfd_map = new_max_sock + 50; + eloop.pollfds_map = nmap; + } + + if (eloop.count + 1 > eloop.max_poll_fds) { + struct pollfd *n; + int nmax = eloop.count + 1 + 50; + n = os_realloc_array(eloop.pollfds, nmax, + sizeof(struct pollfd)); + if (n == NULL) + return -1; + + eloop.max_poll_fds = nmax; + eloop.pollfds = n; + } +#endif /* CONFIG_ELOOP_POLL */ + eloop_trace_sock_remove_ref(table); - tmp = (struct eloop_sock *) - os_realloc(table->table, - (table->count + 1) * sizeof(struct eloop_sock)); + tmp = os_realloc_array(table->table, table->count + 1, + sizeof(struct eloop_sock)); if (tmp == NULL) return -1; @@ -152,8 +188,8 @@ wpa_trace_record(&tmp[table->count]); table->count++; table->table = tmp; - if (sock > eloop.max_sock) - eloop.max_sock = sock; + eloop.max_sock = new_max_sock; + eloop.count++; table->changed = 1; eloop_trace_sock_add_ref(table); @@ -182,11 +218,152 @@ sizeof(struct eloop_sock)); } table->count--; + eloop.count--; table->changed = 1; eloop_trace_sock_add_ref(table); } +#ifdef CONFIG_ELOOP_POLL + +static struct pollfd * find_pollfd(struct pollfd **pollfds_map, int fd, int mx) +{ + if (fd < mx && fd >= 0) + return pollfds_map[fd]; + return NULL; +} + + +static int eloop_sock_table_set_fds(struct eloop_sock_table *readers, + struct eloop_sock_table *writers, + struct eloop_sock_table *exceptions, + struct pollfd *pollfds, + struct pollfd **pollfds_map, + int max_pollfd_map) +{ + int i; + int nxt = 0; + int fd; + struct pollfd *pfd; + + /* Clear pollfd lookup map. It will be re-populated below. */ + os_memset(pollfds_map, 0, sizeof(struct pollfd *) * max_pollfd_map); + + if (readers && readers->table) { + for (i = 0; i < readers->count; i++) { + fd = readers->table[i].sock; + assert(fd >= 0 && fd < max_pollfd_map); + pollfds[nxt].fd = fd; + pollfds[nxt].events = POLLIN; + pollfds[nxt].revents = 0; + pollfds_map[fd] = &(pollfds[nxt]); + nxt++; + } + } + + if (writers && writers->table) { + for (i = 0; i < writers->count; i++) { + /* + * See if we already added this descriptor, update it + * if so. + */ + fd = writers->table[i].sock; + assert(fd >= 0 && fd < max_pollfd_map); + pfd = pollfds_map[fd]; + if (!pfd) { + pfd = &(pollfds[nxt]); + pfd->events = 0; + pfd->fd = fd; + pollfds[i].revents = 0; + pollfds_map[fd] = pfd; + nxt++; + } + pfd->events |= POLLOUT; + } + } + + /* + * Exceptions are always checked when using poll, but I suppose it's + * possible that someone registered a socket *only* for exception + * handling. Set the POLLIN bit in this case. + */ + if (exceptions && exceptions->table) { + for (i = 0; i < exceptions->count; i++) { + /* + * See if we already added this descriptor, just use it + * if so. + */ + fd = exceptions->table[i].sock; + assert(fd >= 0 && fd < max_pollfd_map); + pfd = pollfds_map[fd]; + if (!pfd) { + pfd = &(pollfds[nxt]); + pfd->events = POLLIN; + pfd->fd = fd; + pollfds[i].revents = 0; + pollfds_map[fd] = pfd; + nxt++; + } + } + } + + return nxt; +} + + +static int eloop_sock_table_dispatch_table(struct eloop_sock_table *table, + struct pollfd **pollfds_map, + int max_pollfd_map, + short int revents) +{ + int i; + struct pollfd *pfd; + + if (!table || !table->table) + return 0; + + table->changed = 0; + for (i = 0; i < table->count; i++) { + pfd = find_pollfd(pollfds_map, table->table[i].sock, + max_pollfd_map); + if (!pfd) + continue; + + if (!(pfd->revents & revents)) + continue; + + table->table[i].handler(table->table[i].sock, + table->table[i].eloop_data, + table->table[i].user_data); + if (table->changed) + return 1; + } + + return 0; +} + + +static void eloop_sock_table_dispatch(struct eloop_sock_table *readers, + struct eloop_sock_table *writers, + struct eloop_sock_table *exceptions, + struct pollfd **pollfds_map, + int max_pollfd_map) +{ + if (eloop_sock_table_dispatch_table(readers, pollfds_map, + max_pollfd_map, POLLIN | POLLERR | + POLLHUP)) + return; /* pollfds may be invalid at this point */ + + if (eloop_sock_table_dispatch_table(writers, pollfds_map, + max_pollfd_map, POLLOUT)) + return; /* pollfds may be invalid at this point */ + + eloop_sock_table_dispatch_table(exceptions, pollfds_map, + max_pollfd_map, POLLERR | POLLHUP); +} + +#else /* CONFIG_ELOOP_POLL */ + static void eloop_sock_table_set_fds(struct eloop_sock_table *table, fd_set *fds) { @@ -222,6 +399,8 @@ } } +#endif /* CONFIG_ELOOP_POLL */ + static void eloop_sock_table_destroy(struct eloop_sock_table *table) { @@ -305,7 +484,7 @@ timeout = os_zalloc(sizeof(*timeout)); if (timeout == NULL) return -1; - if (os_get_time(&timeout->time) < 0) { + if (os_get_reltime(&timeout->time) < 0) { os_free(timeout); return -1; } @@ -335,7 +514,7 @@ /* Maintain timeouts in order of increasing time */ dl_list_for_each(tmp, &eloop.timeout, struct eloop_timeout, list) { - if (os_time_before(&timeout->time, &tmp->time)) { + if (os_reltime_before(&timeout->time, &tmp->time)) { dl_list_add(tmp->list.prev, &timeout->list); return 0; } @@ -377,6 +556,33 @@ } +int eloop_cancel_timeout_one(eloop_timeout_handler handler, + void *eloop_data, void *user_data, + struct os_reltime *remaining) +{ + struct eloop_timeout *timeout, *prev; + int removed = 0; + struct os_reltime now; + + os_get_reltime(&now); + remaining->sec = remaining->usec = 0; + + dl_list_for_each_safe(timeout, prev, &eloop.timeout, + struct eloop_timeout, list) { + if (timeout->handler == handler && + (timeout->eloop_data == eloop_data) && + (timeout->user_data == user_data)) { + removed = 1; + if (os_reltime_before(&now, &timeout->time)) + os_reltime_sub(&timeout->time, &now, remaining); + eloop_remove_timeout(timeout); + break; + } + } + return removed; +} + + int eloop_is_timeout_registered(eloop_timeout_handler handler, void *eloop_data, void *user_data) { @@ -393,6 +599,70 @@ } +int eloop_deplete_timeout(unsigned int req_secs, unsigned int req_usecs, + eloop_timeout_handler handler, void *eloop_data, + void *user_data) +{ + struct os_reltime now, requested, remaining; + struct eloop_timeout *tmp; + + dl_list_for_each(tmp, &eloop.timeout, struct eloop_timeout, list) { + if (tmp->handler == handler && + tmp->eloop_data == eloop_data && + tmp->user_data == user_data) { + requested.sec = req_secs; + requested.usec = req_usecs; + os_get_reltime(&now); + os_reltime_sub(&tmp->time, &now, &remaining); + if (os_reltime_before(&requested, &remaining)) { + eloop_cancel_timeout(handler, eloop_data, + user_data); + eloop_register_timeout(requested.sec, + requested.usec, + handler, eloop_data, + user_data); + return 1; + } + return 0; + } + } + + return -1; +} + + +int eloop_replenish_timeout(unsigned int req_secs, unsigned int req_usecs, + eloop_timeout_handler handler, void *eloop_data, + void *user_data) +{ + struct os_reltime now, requested, remaining; + struct eloop_timeout *tmp; + + dl_list_for_each(tmp, &eloop.timeout, struct eloop_timeout, list) { + if (tmp->handler == handler && + tmp->eloop_data == eloop_data && + tmp->user_data == user_data) { + requested.sec = req_secs; + requested.usec = req_usecs; + os_get_reltime(&now); + os_reltime_sub(&tmp->time, &now, &remaining); + if (os_reltime_before(&remaining, &requested)) { + eloop_cancel_timeout(handler, eloop_data, + user_data); + eloop_register_timeout(requested.sec, + requested.usec, + handler, eloop_data, + user_data); + return 1; + } + return 0; + } + } + + return -1; +} + + #ifndef CONFIG_NATIVE_WINDOWS static void eloop_handle_alarm(int sig) { @@ -460,10 +730,8 @@ { struct eloop_signal *tmp; - tmp = (struct eloop_signal *) - os_realloc(eloop.signals, - (eloop.signal_count + 1) * - sizeof(struct eloop_signal)); + tmp = os_realloc_array(eloop.signals, eloop.signal_count + 1, + sizeof(struct eloop_signal)); if (tmp == NULL) return -1; @@ -502,16 +770,23 @@ void eloop_run(void) { +#ifdef CONFIG_ELOOP_POLL + int num_poll_fds; + int timeout_ms = 0; +#else /* CONFIG_ELOOP_POLL */ fd_set *rfds, *wfds, *efds; - int res; struct timeval _tv; - struct os_time tv, now; +#endif /* CONFIG_ELOOP_POLL */ + int res; + struct os_reltime tv, now; +#ifndef CONFIG_ELOOP_POLL rfds = os_malloc(sizeof(*rfds)); wfds = os_malloc(sizeof(*wfds)); efds = os_malloc(sizeof(*efds)); if (rfds == NULL || wfds == NULL || efds == NULL) goto out; +#endif /* CONFIG_ELOOP_POLL */ while (!eloop.terminate && (!dl_list_empty(&eloop.timeout) || eloop.readers.count > 0 || @@ -520,32 +795,52 @@ timeout = dl_list_first(&eloop.timeout, struct eloop_timeout, list); if (timeout) { - os_get_time(&now); - if (os_time_before(&now, &timeout->time)) - os_time_sub(&timeout->time, &now, &tv); + os_get_reltime(&now); + if (os_reltime_before(&now, &timeout->time)) + os_reltime_sub(&timeout->time, &now, &tv); else tv.sec = tv.usec = 0; +#ifdef CONFIG_ELOOP_POLL + timeout_ms = tv.sec * 1000 + tv.usec / 1000; +#else /* CONFIG_ELOOP_POLL */ _tv.tv_sec = tv.sec; _tv.tv_usec = tv.usec; +#endif /* CONFIG_ELOOP_POLL */ } +#ifdef CONFIG_ELOOP_POLL + num_poll_fds = eloop_sock_table_set_fds( + &eloop.readers, &eloop.writers, &eloop.exceptions, + eloop.pollfds, eloop.pollfds_map, + eloop.max_pollfd_map); + res = poll(eloop.pollfds, num_poll_fds, + timeout ? timeout_ms : -1); + + if (res < 0 && errno != EINTR && errno != 0) { + wpa_printf(MSG_INFO, "eloop: poll: %s", + strerror(errno)); + goto out; + } +#else /* CONFIG_ELOOP_POLL */ eloop_sock_table_set_fds(&eloop.readers, rfds); eloop_sock_table_set_fds(&eloop.writers, wfds); eloop_sock_table_set_fds(&eloop.exceptions, efds); res = select(eloop.max_sock + 1, rfds, wfds, efds, timeout ? &_tv : NULL); if (res < 0 && errno != EINTR && errno != 0) { - perror("select"); + wpa_printf(MSG_INFO, "eloop: select: %s", + strerror(errno)); goto out; } +#endif /* CONFIG_ELOOP_POLL */ eloop_process_pending_signals(); /* check if some registered timeouts have occurred */ timeout = dl_list_first(&eloop.timeout, struct eloop_timeout, list); if (timeout) { - os_get_time(&now); - if (!os_time_before(&now, &timeout->time)) { + os_get_reltime(&now); + if (!os_reltime_before(&now, &timeout->time)) { void *eloop_data = timeout->eloop_data; void *user_data = timeout->user_data; eloop_timeout_handler handler = @@ -559,15 +854,25 @@ if (res <= 0) continue; +#ifdef CONFIG_ELOOP_POLL + eloop_sock_table_dispatch(&eloop.readers, &eloop.writers, + &eloop.exceptions, eloop.pollfds_map, + eloop.max_pollfd_map); +#else /* CONFIG_ELOOP_POLL */ eloop_sock_table_dispatch(&eloop.readers, rfds); eloop_sock_table_dispatch(&eloop.writers, wfds); eloop_sock_table_dispatch(&eloop.exceptions, efds); +#endif /* CONFIG_ELOOP_POLL */ } + eloop.terminate = 0; out: +#ifndef CONFIG_ELOOP_POLL os_free(rfds); os_free(wfds); os_free(efds); +#endif /* CONFIG_ELOOP_POLL */ + return; } @@ -580,9 +885,9 @@ void eloop_destroy(void) { struct eloop_timeout *timeout, *prev; - struct os_time now; + struct os_reltime now; - os_get_time(&now); + os_get_reltime(&now); dl_list_for_each_safe(timeout, prev, &eloop.timeout, struct eloop_timeout, list) { int sec, usec; @@ -605,6 +910,11 @@ eloop_sock_table_destroy(&eloop.writers); eloop_sock_table_destroy(&eloop.exceptions); os_free(eloop.signals); + +#ifdef CONFIG_ELOOP_POLL + os_free(eloop.pollfds); + os_free(eloop.pollfds_map); +#endif /* CONFIG_ELOOP_POLL */ } @@ -616,6 +926,18 @@ void eloop_wait_for_read_sock(int sock) { +#ifdef CONFIG_ELOOP_POLL + struct pollfd pfd; + + if (sock < 0) + return; + + os_memset(&pfd, 0, sizeof(pfd)); + pfd.fd = sock; + pfd.events = POLLIN; + + poll(&pfd, 1, -1); +#else /* CONFIG_ELOOP_POLL */ fd_set rfds; if (sock < 0) @@ -624,4 +946,5 @@ FD_ZERO(&rfds); FD_SET(sock, &rfds); select(sock + 1, &rfds, NULL, NULL, NULL); +#endif /* CONFIG_ELOOP_POLL */ } diff -Nru wpa-1.0/src/utils/eloop.h wpa-2.1/src/utils/eloop.h --- wpa-1.0/src/utils/eloop.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/utils/eloop.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * Event loop * Copyright (c) 2002-2006, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. * * This file defines an event loop interface that supports processing events * from registered timeouts (i.e., do something after N seconds), sockets @@ -201,6 +195,21 @@ void *eloop_data, void *user_data); /** + * eloop_cancel_timeout_one - Cancel a single timeout + * @handler: Matching callback function + * @eloop_data: Matching eloop_data + * @user_data: Matching user_data + * @remaining: Time left on the cancelled timer + * Returns: Number of cancelled timeouts + * + * Cancel matching timeout registered with + * eloop_register_timeout() and return the remaining time left. + */ +int eloop_cancel_timeout_one(eloop_timeout_handler handler, + void *eloop_data, void *user_data, + struct os_reltime *remaining); + +/** * eloop_is_timeout_registered - Check if a timeout is already registered * @handler: Matching callback function * @eloop_data: Matching eloop_data @@ -214,6 +223,40 @@ void *eloop_data, void *user_data); /** + * eloop_deplete_timeout - Deplete a timeout that is already registered + * @req_secs: Requested number of seconds to the timeout + * @req_usecs: Requested number of microseconds to the timeout + * @handler: Matching callback function + * @eloop_data: Matching eloop_data + * @user_data: Matching user_data + * Returns: 1 if the timeout is depleted, 0 if no change is made, -1 if no + * timeout matched + * + * Find a registered matching timeout. If found, + * deplete the timeout if remaining time is more than the requested time. + */ +int eloop_deplete_timeout(unsigned int req_secs, unsigned int req_usecs, + eloop_timeout_handler handler, void *eloop_data, + void *user_data); + +/** + * eloop_replenish_timeout - Replenish a timeout that is already registered + * @req_secs: Requested number of seconds to the timeout + * @req_usecs: Requested number of microseconds to the timeout + * @handler: Matching callback function + * @eloop_data: Matching eloop_data + * @user_data: Matching user_data + * Returns: 1 if the timeout is replenished, 0 if no change is made, -1 if no + * timeout matched + * + * Find a registered matching timeout. If found, + * replenish the timeout if remaining time is less than the requested time. + */ +int eloop_replenish_timeout(unsigned int req_secs, unsigned int req_usecs, + eloop_timeout_handler handler, void *eloop_data, + void *user_data); + +/** * eloop_register_signal - Register handler for signals * @sig: Signal number (e.g., SIGHUP) * @handler: Callback function to be called when the signal is received diff -Nru wpa-1.0/src/utils/eloop_none.c wpa-2.1/src/utils/eloop_none.c --- wpa-1.0/src/utils/eloop_none.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/utils/eloop_none.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,401 +0,0 @@ -/* - * Event loop - empty template (basic structure, but no OS specific operations) - * Copyright (c) 2002-2005, Jouni Malinen - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#include "includes.h" - -#include "common.h" -#include "eloop.h" - - -struct eloop_sock { - int sock; - void *eloop_data; - void *user_data; - void (*handler)(int sock, void *eloop_ctx, void *sock_ctx); -}; - -struct eloop_timeout { - struct os_time time; - void *eloop_data; - void *user_data; - void (*handler)(void *eloop_ctx, void *sock_ctx); - struct eloop_timeout *next; -}; - -struct eloop_signal { - int sig; - void *user_data; - void (*handler)(int sig, void *eloop_ctx, void *signal_ctx); - int signaled; -}; - -struct eloop_data { - int max_sock, reader_count; - struct eloop_sock *readers; - - struct eloop_timeout *timeout; - - int signal_count; - struct eloop_signal *signals; - int signaled; - int pending_terminate; - - int terminate; - int reader_table_changed; -}; - -static struct eloop_data eloop; - - -int eloop_init(void) -{ - memset(&eloop, 0, sizeof(eloop)); - return 0; -} - - -int eloop_register_read_sock(int sock, - void (*handler)(int sock, void *eloop_ctx, - void *sock_ctx), - void *eloop_data, void *user_data) -{ - struct eloop_sock *tmp; - - tmp = (struct eloop_sock *) - realloc(eloop.readers, - (eloop.reader_count + 1) * sizeof(struct eloop_sock)); - if (tmp == NULL) - return -1; - - tmp[eloop.reader_count].sock = sock; - tmp[eloop.reader_count].eloop_data = eloop_data; - tmp[eloop.reader_count].user_data = user_data; - tmp[eloop.reader_count].handler = handler; - eloop.reader_count++; - eloop.readers = tmp; - if (sock > eloop.max_sock) - eloop.max_sock = sock; - eloop.reader_table_changed = 1; - - return 0; -} - - -void eloop_unregister_read_sock(int sock) -{ - int i; - - if (eloop.readers == NULL || eloop.reader_count == 0) - return; - - for (i = 0; i < eloop.reader_count; i++) { - if (eloop.readers[i].sock == sock) - break; - } - if (i == eloop.reader_count) - return; - if (i != eloop.reader_count - 1) { - memmove(&eloop.readers[i], &eloop.readers[i + 1], - (eloop.reader_count - i - 1) * - sizeof(struct eloop_sock)); - } - eloop.reader_count--; - eloop.reader_table_changed = 1; -} - - -int eloop_register_timeout(unsigned int secs, unsigned int usecs, - void (*handler)(void *eloop_ctx, void *timeout_ctx), - void *eloop_data, void *user_data) -{ - struct eloop_timeout *timeout, *tmp, *prev; - - timeout = (struct eloop_timeout *) malloc(sizeof(*timeout)); - if (timeout == NULL) - return -1; - os_get_time(&timeout->time); - timeout->time.sec += secs; - timeout->time.usec += usecs; - while (timeout->time.usec >= 1000000) { - timeout->time.sec++; - timeout->time.usec -= 1000000; - } - timeout->eloop_data = eloop_data; - timeout->user_data = user_data; - timeout->handler = handler; - timeout->next = NULL; - - if (eloop.timeout == NULL) { - eloop.timeout = timeout; - return 0; - } - - prev = NULL; - tmp = eloop.timeout; - while (tmp != NULL) { - if (os_time_before(&timeout->time, &tmp->time)) - break; - prev = tmp; - tmp = tmp->next; - } - - if (prev == NULL) { - timeout->next = eloop.timeout; - eloop.timeout = timeout; - } else { - timeout->next = prev->next; - prev->next = timeout; - } - - return 0; -} - - -int eloop_cancel_timeout(void (*handler)(void *eloop_ctx, void *sock_ctx), - void *eloop_data, void *user_data) -{ - struct eloop_timeout *timeout, *prev, *next; - int removed = 0; - - prev = NULL; - timeout = eloop.timeout; - while (timeout != NULL) { - next = timeout->next; - - if (timeout->handler == handler && - (timeout->eloop_data == eloop_data || - eloop_data == ELOOP_ALL_CTX) && - (timeout->user_data == user_data || - user_data == ELOOP_ALL_CTX)) { - if (prev == NULL) - eloop.timeout = next; - else - prev->next = next; - free(timeout); - removed++; - } else - prev = timeout; - - timeout = next; - } - - return removed; -} - - -int eloop_is_timeout_registered(void (*handler)(void *eloop_ctx, - void *timeout_ctx), - void *eloop_data, void *user_data) -{ - struct eloop_timeout *tmp; - - tmp = eloop.timeout; - while (tmp != NULL) { - if (tmp->handler == handler && - tmp->eloop_data == eloop_data && - tmp->user_data == user_data) - return 1; - - tmp = tmp->next; - } - - return 0; -} - - -/* TODO: replace with suitable signal handler */ -#if 0 -static void eloop_handle_signal(int sig) -{ - int i; - - eloop.signaled++; - for (i = 0; i < eloop.signal_count; i++) { - if (eloop.signals[i].sig == sig) { - eloop.signals[i].signaled++; - break; - } - } -} -#endif - - -static void eloop_process_pending_signals(void) -{ - int i; - - if (eloop.signaled == 0) - return; - eloop.signaled = 0; - - if (eloop.pending_terminate) { - eloop.pending_terminate = 0; - } - - for (i = 0; i < eloop.signal_count; i++) { - if (eloop.signals[i].signaled) { - eloop.signals[i].signaled = 0; - eloop.signals[i].handler(eloop.signals[i].sig, - eloop.user_data, - eloop.signals[i].user_data); - } - } -} - - -int eloop_register_signal(int sig, - void (*handler)(int sig, void *eloop_ctx, - void *signal_ctx), - void *user_data) -{ - struct eloop_signal *tmp; - - tmp = (struct eloop_signal *) - realloc(eloop.signals, - (eloop.signal_count + 1) * - sizeof(struct eloop_signal)); - if (tmp == NULL) - return -1; - - tmp[eloop.signal_count].sig = sig; - tmp[eloop.signal_count].user_data = user_data; - tmp[eloop.signal_count].handler = handler; - tmp[eloop.signal_count].signaled = 0; - eloop.signal_count++; - eloop.signals = tmp; - - /* TODO: register signal handler */ - - return 0; -} - - -int eloop_register_signal_terminate(void (*handler)(int sig, void *eloop_ctx, - void *signal_ctx), - void *user_data) -{ -#if 0 - /* TODO: for example */ - int ret = eloop_register_signal(SIGINT, handler, user_data); - if (ret == 0) - ret = eloop_register_signal(SIGTERM, handler, user_data); - return ret; -#endif - return 0; -} - - -int eloop_register_signal_reconfig(void (*handler)(int sig, void *eloop_ctx, - void *signal_ctx), - void *user_data) -{ -#if 0 - /* TODO: for example */ - return eloop_register_signal(SIGHUP, handler, user_data); -#endif - return 0; -} - - -void eloop_run(void) -{ - int i; - struct os_time tv, now; - - while (!eloop.terminate && - (eloop.timeout || eloop.reader_count > 0)) { - if (eloop.timeout) { - os_get_time(&now); - if (os_time_before(&now, &eloop.timeout->time)) - os_time_sub(&eloop.timeout->time, &now, &tv); - else - tv.sec = tv.usec = 0; - } - - /* - * TODO: wait for any event (read socket ready, timeout (tv), - * signal - */ - os_sleep(1, 0); /* just a dummy wait for testing */ - - eloop_process_pending_signals(); - - /* check if some registered timeouts have occurred */ - if (eloop.timeout) { - struct eloop_timeout *tmp; - - os_get_time(&now); - if (!os_time_before(&now, &eloop.timeout->time)) { - tmp = eloop.timeout; - eloop.timeout = eloop.timeout->next; - tmp->handler(tmp->eloop_data, - tmp->user_data); - free(tmp); - } - - } - - eloop.reader_table_changed = 0; - for (i = 0; i < eloop.reader_count; i++) { - /* - * TODO: call each handler that has pending data to - * read - */ - if (0 /* TODO: eloop.readers[i].sock ready */) { - eloop.readers[i].handler( - eloop.readers[i].sock, - eloop.readers[i].eloop_data, - eloop.readers[i].user_data); - if (eloop.reader_table_changed) - break; - } - } - } -} - - -void eloop_terminate(void) -{ - eloop.terminate = 1; -} - - -void eloop_destroy(void) -{ - struct eloop_timeout *timeout, *prev; - - timeout = eloop.timeout; - while (timeout != NULL) { - prev = timeout; - timeout = timeout->next; - free(prev); - } - free(eloop.readers); - free(eloop.signals); -} - - -int eloop_terminated(void) -{ - return eloop.terminate; -} - - -void eloop_wait_for_read_sock(int sock) -{ - /* - * TODO: wait for the file descriptor to have something available for - * reading - */ -} diff -Nru wpa-1.0/src/utils/eloop_win.c wpa-2.1/src/utils/eloop_win.c --- wpa-1.0/src/utils/eloop_win.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/utils/eloop_win.c 2014-02-04 11:23:35.000000000 +0000 @@ -1,21 +1,16 @@ /* * Event loop based on Windows events and WaitForMultipleObjects - * Copyright (c) 2002-2006, Jouni Malinen + * Copyright (c) 2002-2009, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" #include #include "common.h" +#include "list.h" #include "eloop.h" @@ -35,11 +30,11 @@ }; struct eloop_timeout { - struct os_time time; + struct dl_list list; + struct os_reltime time; void *eloop_data; void *user_data; eloop_timeout_handler handler; - struct eloop_timeout *next; }; struct eloop_signal { @@ -57,7 +52,7 @@ size_t event_count; struct eloop_event *events; - struct eloop_timeout *timeout; + struct dl_list timeout; int signal_count; struct eloop_signal *signals; @@ -80,6 +75,7 @@ int eloop_init(void) { os_memset(&eloop, 0, sizeof(eloop)); + dl_list_init(&eloop.timeout); eloop.num_handles = 1; eloop.handles = os_malloc(eloop.num_handles * sizeof(eloop.handles[0])); @@ -104,8 +100,8 @@ if (eloop.num_handles > eloop.reader_count + eloop.event_count + 8) return 0; - n = os_realloc(eloop.handles, - eloop.num_handles * 2 * sizeof(eloop.handles[0])); + n = os_realloc_array(eloop.handles, eloop.num_handles * 2, + sizeof(eloop.handles[0])); if (n == NULL) return -1; eloop.handles = n; @@ -134,8 +130,8 @@ WSACloseEvent(event); return -1; } - tmp = os_realloc(eloop.readers, - (eloop.reader_count + 1) * sizeof(struct eloop_sock)); + tmp = os_realloc_array(eloop.readers, eloop.reader_count + 1, + sizeof(struct eloop_sock)); if (tmp == NULL) { WSAEventSelect(sock, event, 0); WSACloseEvent(event); @@ -197,8 +193,8 @@ if (eloop_prepare_handles()) return -1; - tmp = os_realloc(eloop.events, - (eloop.event_count + 1) * sizeof(struct eloop_event)); + tmp = os_realloc_array(eloop.events, eloop.event_count + 1, + sizeof(struct eloop_event)); if (tmp == NULL) return -1; @@ -242,13 +238,16 @@ eloop_timeout_handler handler, void *eloop_data, void *user_data) { - struct eloop_timeout *timeout, *tmp, *prev; + struct eloop_timeout *timeout, *tmp; os_time_t now_sec; - timeout = os_malloc(sizeof(*timeout)); + timeout = os_zalloc(sizeof(*timeout)); if (timeout == NULL) return -1; - os_get_time(&timeout->time); + if (os_get_reltime(&timeout->time) < 0) { + os_free(timeout); + return -1; + } now_sec = timeout->time.sec; timeout->time.sec += secs; if (timeout->time.sec < now_sec) { @@ -269,85 +268,156 @@ timeout->eloop_data = eloop_data; timeout->user_data = user_data; timeout->handler = handler; - timeout->next = NULL; - if (eloop.timeout == NULL) { - eloop.timeout = timeout; - return 0; + /* Maintain timeouts in order of increasing time */ + dl_list_for_each(tmp, &eloop.timeout, struct eloop_timeout, list) { + if (os_reltime_before(&timeout->time, &tmp->time)) { + dl_list_add(tmp->list.prev, &timeout->list); + return 0; + } } + dl_list_add_tail(&eloop.timeout, &timeout->list); - prev = NULL; - tmp = eloop.timeout; - while (tmp != NULL) { - if (os_time_before(&timeout->time, &tmp->time)) - break; - prev = tmp; - tmp = tmp->next; - } + return 0; +} - if (prev == NULL) { - timeout->next = eloop.timeout; - eloop.timeout = timeout; - } else { - timeout->next = prev->next; - prev->next = timeout; - } - return 0; +static void eloop_remove_timeout(struct eloop_timeout *timeout) +{ + dl_list_del(&timeout->list); + os_free(timeout); } int eloop_cancel_timeout(eloop_timeout_handler handler, void *eloop_data, void *user_data) { - struct eloop_timeout *timeout, *prev, *next; + struct eloop_timeout *timeout, *prev; int removed = 0; - prev = NULL; - timeout = eloop.timeout; - while (timeout != NULL) { - next = timeout->next; - + dl_list_for_each_safe(timeout, prev, &eloop.timeout, + struct eloop_timeout, list) { if (timeout->handler == handler && (timeout->eloop_data == eloop_data || eloop_data == ELOOP_ALL_CTX) && (timeout->user_data == user_data || user_data == ELOOP_ALL_CTX)) { - if (prev == NULL) - eloop.timeout = next; - else - prev->next = next; - os_free(timeout); + eloop_remove_timeout(timeout); removed++; - } else - prev = timeout; - - timeout = next; + } } return removed; } +int eloop_cancel_timeout_one(eloop_timeout_handler handler, + void *eloop_data, void *user_data, + struct os_reltime *remaining) +{ + struct eloop_timeout *timeout, *prev; + int removed = 0; + struct os_reltime now; + + os_get_reltime(&now); + remaining->sec = remaining->usec = 0; + + dl_list_for_each_safe(timeout, prev, &eloop.timeout, + struct eloop_timeout, list) { + if (timeout->handler == handler && + (timeout->eloop_data == eloop_data) && + (timeout->user_data == user_data)) { + removed = 1; + if (os_reltime_before(&now, &timeout->time)) + os_reltime_sub(&timeout->time, &now, remaining); + eloop_remove_timeout(timeout); + break; + } + } + return removed; +} + + int eloop_is_timeout_registered(eloop_timeout_handler handler, void *eloop_data, void *user_data) { struct eloop_timeout *tmp; - tmp = eloop.timeout; - while (tmp != NULL) { + dl_list_for_each(tmp, &eloop.timeout, struct eloop_timeout, list) { if (tmp->handler == handler && tmp->eloop_data == eloop_data && tmp->user_data == user_data) return 1; - - tmp = tmp->next; } return 0; } +int eloop_deplete_timeout(unsigned int req_secs, unsigned int req_usecs, + eloop_timeout_handler handler, void *eloop_data, + void *user_data) +{ + struct os_reltime now, requested, remaining; + struct eloop_timeout *tmp; + + dl_list_for_each(tmp, &eloop.timeout, struct eloop_timeout, list) { + if (tmp->handler == handler && + tmp->eloop_data == eloop_data && + tmp->user_data == user_data) { + requested.sec = req_secs; + requested.usec = req_usecs; + os_get_reltime(&now); + os_reltime_sub(&tmp->time, &now, &remaining); + if (os_reltime_before(&requested, &remaining)) { + eloop_cancel_timeout(handler, eloop_data, + user_data); + eloop_register_timeout(requested.sec, + requested.usec, + handler, eloop_data, + user_data); + return 1; + } + return 0; + } + } + + return -1; +} + + +int eloop_replenish_timeout(unsigned int req_secs, unsigned int req_usecs, + eloop_timeout_handler handler, void *eloop_data, + void *user_data) +{ + struct os_reltime now, requested, remaining; + struct eloop_timeout *tmp; + + dl_list_for_each(tmp, &eloop.timeout, struct eloop_timeout, list) { + if (tmp->handler == handler && + tmp->eloop_data == eloop_data && + tmp->user_data == user_data) { + requested.sec = req_secs; + requested.usec = req_usecs; + os_get_reltime(&now); + os_reltime_sub(&tmp->time, &now, &remaining); + if (os_reltime_before(&remaining, &requested)) { + eloop_cancel_timeout(handler, eloop_data, + user_data); + eloop_register_timeout(requested.sec, + requested.usec, + handler, eloop_data, + user_data); + return 1; + } + return 0; + } + } + + return -1; +} + + /* TODO: replace with suitable signal handler */ #if 0 static void eloop_handle_signal(int sig) @@ -398,9 +468,8 @@ { struct eloop_signal *tmp; - tmp = os_realloc(eloop.signals, - (eloop.signal_count + 1) * - sizeof(struct eloop_signal)); + tmp = os_realloc_array(eloop.signals, eloop.signal_count + 1, + sizeof(struct eloop_signal)); if (tmp == NULL) return -1; @@ -463,18 +532,21 @@ void eloop_run(void) { - struct os_time tv, now; - DWORD count, ret, timeout, err; + struct os_reltime tv, now; + DWORD count, ret, timeout_val, err; size_t i; while (!eloop.terminate && - (eloop.timeout || eloop.reader_count > 0 || + (!dl_list_empty(&eloop.timeout) || eloop.reader_count > 0 || eloop.event_count > 0)) { + struct eloop_timeout *timeout; tv.sec = tv.usec = 0; - if (eloop.timeout) { - os_get_time(&now); - if (os_time_before(&now, &eloop.timeout->time)) - os_time_sub(&eloop.timeout->time, &now, &tv); + timeout = dl_list_first(&eloop.timeout, struct eloop_timeout, + list); + if (timeout) { + os_get_reltime(&now); + if (os_reltime_before(&now, &timeout->time)) + os_reltime_sub(&timeout->time, &now, &tv); } count = 0; @@ -487,10 +559,10 @@ if (eloop.term_event) eloop.handles[count++] = eloop.term_event; - if (eloop.timeout) - timeout = tv.sec * 1000 + tv.usec / 1000; + if (timeout) + timeout_val = tv.sec * 1000 + tv.usec / 1000; else - timeout = INFINITE; + timeout_val = INFINITE; if (count > MAXIMUM_WAIT_OBJECTS) { printf("WaitForMultipleObjects: Too many events: " @@ -500,26 +572,27 @@ } #ifdef _WIN32_WCE ret = WaitForMultipleObjects(count, eloop.handles, FALSE, - timeout); + timeout_val); #else /* _WIN32_WCE */ ret = WaitForMultipleObjectsEx(count, eloop.handles, FALSE, - timeout, TRUE); + timeout_val, TRUE); #endif /* _WIN32_WCE */ err = GetLastError(); eloop_process_pending_signals(); /* check if some registered timeouts have occurred */ - if (eloop.timeout) { - struct eloop_timeout *tmp; - - os_get_time(&now); - if (!os_time_before(&now, &eloop.timeout->time)) { - tmp = eloop.timeout; - eloop.timeout = eloop.timeout->next; - tmp->handler(tmp->eloop_data, - tmp->user_data); - os_free(tmp); + timeout = dl_list_first(&eloop.timeout, struct eloop_timeout, + list); + if (timeout) { + os_get_reltime(&now); + if (!os_reltime_before(&now, &timeout->time)) { + void *eloop_data = timeout->eloop_data; + void *user_data = timeout->user_data; + eloop_timeout_handler handler = + timeout->handler; + eloop_remove_timeout(timeout); + handler(eloop_data, user_data); } } @@ -578,11 +651,9 @@ { struct eloop_timeout *timeout, *prev; - timeout = eloop.timeout; - while (timeout != NULL) { - prev = timeout; - timeout = timeout->next; - os_free(prev); + dl_list_for_each_safe(timeout, prev, &eloop.timeout, + struct eloop_timeout, list) { + eloop_remove_timeout(timeout); } os_free(eloop.readers); os_free(eloop.signals); diff -Nru wpa-1.0/src/utils/ext_password.c wpa-2.1/src/utils/ext_password.c --- wpa-1.0/src/utils/ext_password.c 1970-01-01 00:00:00.000000000 +0000 +++ wpa-2.1/src/utils/ext_password.c 2014-02-04 11:23:35.000000000 +0000 @@ -0,0 +1,116 @@ +/* + * External password backend + * Copyright (c) 2012, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#ifdef __linux__ +#include +#endif /* __linux__ */ + +#include "common.h" +#include "ext_password_i.h" + + +#ifdef CONFIG_EXT_PASSWORD_TEST +extern struct ext_password_backend ext_password_test; +#endif /* CONFIG_EXT_PASSWORD_TEST */ + +static const struct ext_password_backend *backends[] = { +#ifdef CONFIG_EXT_PASSWORD_TEST + &ext_password_test, +#endif /* CONFIG_EXT_PASSWORD_TEST */ + NULL +}; + +struct ext_password_data { + const struct ext_password_backend *backend; + void *priv; +}; + + +struct ext_password_data * ext_password_init(const char *backend, + const char *params) +{ + struct ext_password_data *data; + int i; + + data = os_zalloc(sizeof(*data)); + if (data == NULL) + return NULL; + + for (i = 0; backends[i]; i++) { + if (os_strcmp(backends[i]->name, backend) == 0) { + data->backend = backends[i]; + break; + } + } + + if (!data->backend) { + os_free(data); + return NULL; + } + + data->priv = data->backend->init(params); + if (data->priv == NULL) { + os_free(data); + return NULL; + } + + return data; +} + + +void ext_password_deinit(struct ext_password_data *data) +{ + if (data && data->backend && data->priv) + data->backend->deinit(data->priv); + os_free(data); +} + + +struct wpabuf * ext_password_get(struct ext_password_data *data, + const char *name) +{ + if (data == NULL) + return NULL; + return data->backend->get(data->priv, name); +} + + +struct wpabuf * ext_password_alloc(size_t len) +{ + struct wpabuf *buf; + + buf = wpabuf_alloc(len); + if (buf == NULL) + return NULL; + +#ifdef __linux__ + if (mlock(wpabuf_head(buf), wpabuf_len(buf)) < 0) { + wpa_printf(MSG_ERROR, "EXT PW: mlock failed: %s", + strerror(errno)); + } +#endif /* __linux__ */ + + return buf; +} + + +void ext_password_free(struct wpabuf *pw) +{ + if (pw == NULL) + return; + os_memset(wpabuf_mhead(pw), 0, wpabuf_len(pw)); +#ifdef __linux__ + if (munlock(wpabuf_head(pw), wpabuf_len(pw)) < 0) { + wpa_printf(MSG_ERROR, "EXT PW: munlock failed: %s", + strerror(errno)); + } +#endif /* __linux__ */ + wpabuf_free(pw); +} diff -Nru wpa-1.0/src/utils/ext_password.h wpa-2.1/src/utils/ext_password.h --- wpa-1.0/src/utils/ext_password.h 1970-01-01 00:00:00.000000000 +0000 +++ wpa-2.1/src/utils/ext_password.h 2014-02-04 11:23:35.000000000 +0000 @@ -0,0 +1,33 @@ +/* + * External password backend + * Copyright (c) 2012, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef EXT_PASSWORD_H +#define EXT_PASSWORD_H + +struct ext_password_data; + +#ifdef CONFIG_EXT_PASSWORD + +struct ext_password_data * ext_password_init(const char *backend, + const char *params); +void ext_password_deinit(struct ext_password_data *data); + +struct wpabuf * ext_password_get(struct ext_password_data *data, + const char *name); +void ext_password_free(struct wpabuf *pw); + +#else /* CONFIG_EXT_PASSWORD */ + +#define ext_password_init(b, p) ((void *) 1) +#define ext_password_deinit(d) do { } while (0) +#define ext_password_get(d, n) (NULL) +#define ext_password_free(p) do { } while (0) + +#endif /* CONFIG_EXT_PASSWORD */ + +#endif /* EXT_PASSWORD_H */ diff -Nru wpa-1.0/src/utils/ext_password_i.h wpa-2.1/src/utils/ext_password_i.h --- wpa-1.0/src/utils/ext_password_i.h 1970-01-01 00:00:00.000000000 +0000 +++ wpa-2.1/src/utils/ext_password_i.h 2014-02-04 11:23:35.000000000 +0000 @@ -0,0 +1,23 @@ +/* + * External password backend - internal definitions + * Copyright (c) 2012, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef EXT_PASSWORD_I_H +#define EXT_PASSWORD_I_H + +#include "ext_password.h" + +struct ext_password_backend { + const char *name; + void * (*init)(const char *params); + void (*deinit)(void *ctx); + struct wpabuf * (*get)(void *ctx, const char *name); +}; + +struct wpabuf * ext_password_alloc(size_t len); + +#endif /* EXT_PASSWORD_I_H */ diff -Nru wpa-1.0/src/utils/ext_password_test.c wpa-2.1/src/utils/ext_password_test.c --- wpa-1.0/src/utils/ext_password_test.c 1970-01-01 00:00:00.000000000 +0000 +++ wpa-2.1/src/utils/ext_password_test.c 2014-02-04 11:23:35.000000000 +0000 @@ -0,0 +1,90 @@ +/* + * External password backend + * Copyright (c) 2012, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "ext_password_i.h" + + +struct ext_password_test_data { + char *params; +}; + + +static void * ext_password_test_init(const char *params) +{ + struct ext_password_test_data *data; + + data = os_zalloc(sizeof(*data)); + if (data == NULL) + return NULL; + + if (params) + data->params = os_strdup(params); + + return data; +} + + +static void ext_password_test_deinit(void *ctx) +{ + struct ext_password_test_data *data = ctx; + + os_free(data->params); + os_free(data); +} + + +static struct wpabuf * ext_password_test_get(void *ctx, const char *name) +{ + struct ext_password_test_data *data = ctx; + char *pos, *pos2; + size_t nlen; + + wpa_printf(MSG_DEBUG, "EXT PW TEST: get(%s)", name); + + pos = data->params; + if (pos == NULL) + return NULL; + nlen = os_strlen(name); + + while (pos && *pos) { + if (os_strncmp(pos, name, nlen) == 0 && pos[nlen] == '=') { + struct wpabuf *buf; + pos += nlen + 1; + pos2 = pos; + while (*pos2 != '|' && *pos2 != '\0') + pos2++; + buf = ext_password_alloc(pos2 - pos); + if (buf == NULL) + return NULL; + wpabuf_put_data(buf, pos, pos2 - pos); + wpa_hexdump_ascii_key(MSG_DEBUG, "EXT PW TEST: value", + wpabuf_head(buf), + wpabuf_len(buf)); + return buf; + } + + pos = os_strchr(pos + 1, '|'); + if (pos) + pos++; + } + + wpa_printf(MSG_DEBUG, "EXT PW TEST: get(%s) - not found", name); + + return NULL; +} + + +const struct ext_password_backend ext_password_test = { + .name = "test", + .init = ext_password_test_init, + .deinit = ext_password_test_deinit, + .get = ext_password_test_get, +}; diff -Nru wpa-1.0/src/utils/includes.h wpa-2.1/src/utils/includes.h --- wpa-1.0/src/utils/includes.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/utils/includes.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * wpa_supplicant/hostapd - Default include files * Copyright (c) 2005-2006, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. * * This header file is included into all C files so that commonly used header * files can be selected with OS specific ifdef blocks in one place instead of @@ -47,9 +41,7 @@ #include #include #ifndef __vxworks -#ifndef __SYMBIAN32__ #include -#endif /* __SYMBIAN32__ */ #include #endif /* __vxworks */ #endif /* CONFIG_TI_COMPILER */ diff -Nru wpa-1.0/src/utils/ip_addr.c wpa-2.1/src/utils/ip_addr.c --- wpa-1.0/src/utils/ip_addr.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/utils/ip_addr.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * IP address processing * Copyright (c) 2003-2006, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff -Nru wpa-1.0/src/utils/ip_addr.h wpa-2.1/src/utils/ip_addr.h --- wpa-1.0/src/utils/ip_addr.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/utils/ip_addr.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * IP address processing * Copyright (c) 2003-2006, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef IP_ADDR_H diff -Nru wpa-1.0/src/utils/list.h wpa-2.1/src/utils/list.h --- wpa-1.0/src/utils/list.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/utils/list.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * Doubly-linked list * Copyright (c) 2009, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef LIST_H diff -Nru wpa-1.0/src/utils/Makefile wpa-2.1/src/utils/Makefile --- wpa-1.0/src/utils/Makefile 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/utils/Makefile 2014-02-04 11:23:35.000000000 +0000 @@ -1,7 +1,7 @@ all: libutils.a clean: - rm -f *~ *.o *.d libutils.a + rm -f *~ *.o *.d *.gcno *.gcda *.gcov libutils.a install: @echo Nothing to be made. @@ -11,9 +11,11 @@ #CFLAGS += -DWPA_TRACE CFLAGS += -DCONFIG_IPV6 +CFLAGS += -DCONFIG_DEBUG_FILE LIB_OBJS= \ base64.o \ + bitfield.o \ common.o \ ip_addr.o \ radiotap.o \ diff -Nru wpa-1.0/src/utils/os.h wpa-2.1/src/utils/os.h --- wpa-1.0/src/utils/os.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/utils/os.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * OS specific functions * Copyright (c) 2005-2009, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef OS_H @@ -29,6 +23,11 @@ os_time_t usec; }; +struct os_reltime { + os_time_t sec; + os_time_t usec; +}; + /** * os_get_time - Get current time (sec, usec) * @t: Pointer to buffer for the time @@ -36,21 +35,84 @@ */ int os_get_time(struct os_time *t); +/** + * os_get_reltime - Get relative time (sec, usec) + * @t: Pointer to buffer for the time + * Returns: 0 on success, -1 on failure + */ +int os_get_reltime(struct os_reltime *t); + -/* Helper macros for handling struct os_time */ +/* Helpers for handling struct os_time */ + +static inline int os_time_before(struct os_time *a, struct os_time *b) +{ + return (a->sec < b->sec) || + (a->sec == b->sec && a->usec < b->usec); +} + + +static inline void os_time_sub(struct os_time *a, struct os_time *b, + struct os_time *res) +{ + res->sec = a->sec - b->sec; + res->usec = a->usec - b->usec; + if (res->usec < 0) { + res->sec--; + res->usec += 1000000; + } +} + + +/* Helpers for handling struct os_reltime */ + +static inline int os_reltime_before(struct os_reltime *a, + struct os_reltime *b) +{ + return (a->sec < b->sec) || + (a->sec == b->sec && a->usec < b->usec); +} + + +static inline void os_reltime_sub(struct os_reltime *a, struct os_reltime *b, + struct os_reltime *res) +{ + res->sec = a->sec - b->sec; + res->usec = a->usec - b->usec; + if (res->usec < 0) { + res->sec--; + res->usec += 1000000; + } +} + + +static inline void os_reltime_age(struct os_reltime *start, + struct os_reltime *age) +{ + struct os_reltime now; + + os_get_reltime(&now); + os_reltime_sub(&now, start, age); +} + + +static inline int os_reltime_expired(struct os_reltime *now, + struct os_reltime *ts, + os_time_t timeout_secs) +{ + struct os_reltime age; + + os_reltime_sub(now, ts, &age); + return (age.sec > timeout_secs) || + (age.sec == timeout_secs && age.usec > 0); +} + + +static inline int os_reltime_initialized(struct os_reltime *t) +{ + return t->sec != 0 || t->usec != 0; +} -#define os_time_before(a, b) \ - ((a)->sec < (b)->sec || \ - ((a)->sec == (b)->sec && (a)->usec < (b)->usec)) - -#define os_time_sub(a, b, res) do { \ - (res)->sec = (a)->sec - (b)->sec; \ - (res)->usec = (a)->usec - (b)->usec; \ - if ((res)->usec < 0) { \ - (res)->sec--; \ - (res)->usec += 1000000; \ - } \ -} while (0) /** * os_mktime - Convert broken-down time into seconds since 1970-01-01 @@ -186,6 +248,25 @@ */ void * os_zalloc(size_t size); +/** + * os_calloc - Allocate and zero memory for an array + * @nmemb: Number of members in the array + * @size: Number of bytes in each member + * Returns: Pointer to allocated and zeroed memory or %NULL on failure + * + * This function can be used as a wrapper for os_zalloc(nmemb * size) when an + * allocation is used for an array. The main benefit over os_zalloc() is in + * having an extra check to catch integer overflows in multiplication. + * + * Caller is responsible for freeing the returned buffer with os_free(). + */ +static inline void * os_calloc(size_t nmemb, size_t size) +{ + if (size && nmemb > (~(size_t) 0) / size) + return NULL; + return os_zalloc(nmemb * size); +} + /* * The following functions are wrapper for standard ANSI C or POSIX functions. @@ -348,15 +429,6 @@ int os_strncmp(const char *s1, const char *s2, size_t n); /** - * os_strncpy - Copy a string - * @dest: Destination - * @src: Source - * @n: Maximum number of characters to copy - * Returns: dest - */ -char * os_strncpy(char *dest, const char *src, size_t n); - -/** * os_strstr - Locate a substring * @haystack: String (haystack) to search from * @needle: Needle to search from haystack @@ -452,9 +524,6 @@ #ifndef os_strncmp #define os_strncmp(s1, s2, n) strncmp((s1), (s2), (n)) #endif -#ifndef os_strncpy -#define os_strncpy(d, s, n) strncpy((d), (s), (n)) -#endif #ifndef os_strrchr #define os_strrchr(s, c) strrchr((s), (c)) #endif @@ -473,6 +542,14 @@ #endif /* OS_NO_C_LIB_DEFINES */ +static inline void * os_realloc_array(void *ptr, size_t nmemb, size_t size) +{ + if (size && nmemb > (~(size_t) 0) / size) + return NULL; + return os_realloc(ptr, nmemb * size); +} + + /** * os_strlcpy - Copy a string with size bound and NUL-termination * @dest: Destination diff -Nru wpa-1.0/src/utils/os_internal.c wpa-2.1/src/utils/os_internal.c --- wpa-1.0/src/utils/os_internal.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/utils/os_internal.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * wpa_supplicant/hostapd / Internal implementation of OS specific functions * Copyright (c) 2005-2006, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. * * This file is an example of operating system specific wrapper functions. * This version implements many of the functions internally, so it can be used @@ -40,6 +34,17 @@ { int res; struct timeval tv; + res = gettimeofday(&tv, NULL); + t->sec = tv.tv_sec; + t->usec = tv.tv_usec; + return res; +} + + +int os_get_reltime(struct os_reltime *t) +{ + int res; + struct timeval tv; res = gettimeofday(&tv, NULL); t->sec = tv.tv_sec; t->usec = tv.tv_usec; diff -Nru wpa-1.0/src/utils/os_none.c wpa-2.1/src/utils/os_none.c --- wpa-1.0/src/utils/os_none.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/utils/os_none.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * wpa_supplicant/hostapd / Empty OS specific functions * Copyright (c) 2005-2006, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. * * This file can be used as a starting point when adding a new OS target. The * functions here do not really work as-is since they are just empty or only @@ -30,6 +24,12 @@ { return -1; } + + +int os_get_reltime(struct os_reltime *t) +{ + return -1; +} int os_mktime(int year, int month, int day, int hour, int min, int sec, diff -Nru wpa-1.0/src/utils/os_unix.c wpa-2.1/src/utils/os_unix.c --- wpa-1.0/src/utils/os_unix.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/utils/os_unix.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * OS specific functions for UNIX/POSIX systems * Copyright (c) 2005-2009, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -17,19 +11,19 @@ #include #ifdef ANDROID -#include +#include #include #include #endif /* ANDROID */ #include "os.h" +#include "common.h" #ifdef WPA_TRACE -#include "common.h" -#include "list.h" #include "wpa_debug.h" #include "trace.h" +#include "list.h" static struct dl_list alloc_list; @@ -66,6 +60,43 @@ } +int os_get_reltime(struct os_reltime *t) +{ +#if defined(CLOCK_BOOTTIME) + static clockid_t clock_id = CLOCK_BOOTTIME; +#elif defined(CLOCK_MONOTONIC) + static clockid_t clock_id = CLOCK_MONOTONIC; +#else + static clockid_t clock_id = CLOCK_REALTIME; +#endif + struct timespec ts; + int res; + + while (1) { + res = clock_gettime(clock_id, &ts); + if (res == 0) { + t->sec = ts.tv_sec; + t->usec = ts.tv_nsec / 1000; + return 0; + } + switch (clock_id) { +#ifdef CLOCK_BOOTTIME + case CLOCK_BOOTTIME: + clock_id = CLOCK_MONOTONIC; + break; +#endif +#ifdef CLOCK_MONOTONIC + case CLOCK_MONOTONIC: + clock_id = CLOCK_REALTIME; + break; +#endif + case CLOCK_REALTIME: + return -1; + } + } +} + + int os_mktime(int year, int month, int day, int hour, int min, int sec, os_time_t *t) { @@ -219,6 +250,9 @@ size_t len = 128, cwd_len, rel_len, ret_len; int last_errno; + if (!rel_path) + return NULL; + if (rel_path[0] == '/') return os_strdup(rel_path); @@ -263,11 +297,15 @@ * We ignore errors here since errors are normal if we * are already running as non-root. */ +#ifdef ANDROID_SETGROUPS_OVERRIDE + gid_t groups[] = { ANDROID_SETGROUPS_OVERRIDE }; +#else /* ANDROID_SETGROUPS_OVERRIDE */ gid_t groups[] = { AID_INET, AID_WIFI, AID_KEYSTORE }; +#endif /* ANDROID_SETGROUPS_OVERRIDE */ struct __user_cap_header_struct header; struct __user_cap_data_struct cap; - setgroups(sizeof(groups)/sizeof(groups[0]), groups); + setgroups(ARRAY_SIZE(groups), groups); prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0); diff -Nru wpa-1.0/src/utils/os_win32.c wpa-2.1/src/utils/os_win32.c --- wpa-1.0/src/utils/os_win32.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/utils/os_win32.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * wpa_supplicant/hostapd / OS specific functions for Win32 systems * Copyright (c) 2005-2006, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -53,6 +47,17 @@ } +int os_get_reltime(struct os_reltime *t) +{ + /* consider using performance counters or so instead */ + struct os_time now; + int res = os_get_time(&now); + t->sec = now.sec; + t->usec = now.usec; + return res; +} + + int os_mktime(int year, int month, int day, int hour, int min, int sec, os_time_t *t) { diff -Nru wpa-1.0/src/utils/pcsc_funcs.c wpa-2.1/src/utils/pcsc_funcs.c --- wpa-1.0/src/utils/pcsc_funcs.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/utils/pcsc_funcs.c 2014-02-04 11:23:35.000000000 +0000 @@ -1,15 +1,9 @@ /* * WPA Supplicant / PC/SC smartcard interface for USIM, GSM SIM - * Copyright (c) 2004-2007, Jouni Malinen + * Copyright (c) 2004-2007, 2012, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. * * This file implements wrapper functions for accessing GSM SIM and 3GPP USIM * cards through PC/SC smartcard library. These functions are used to implement @@ -76,6 +70,9 @@ #define USIM_TLV_TOTAL_FILE_SIZE 0x81 #define USIM_TLV_PIN_STATUS_TEMPLATE 0xC6 #define USIM_TLV_SHORT_FILE_ID 0x88 +#define USIM_TLV_SECURITY_ATTR_8B 0x8B +#define USIM_TLV_SECURITY_ATTR_8C 0x8C +#define USIM_TLV_SECURITY_ATTR_AB 0xAB #define USIM_PS_DO_TAG 0x90 @@ -87,6 +84,27 @@ #define CK_LEN 16 +/* GSM files + * File type in first octet: + * 3F = Master File + * 7F = Dedicated File + * 2F = Elementary File under the Master File + * 6F = Elementary File under a Dedicated File + */ +#define SCARD_FILE_MF 0x3F00 +#define SCARD_FILE_GSM_DF 0x7F20 +#define SCARD_FILE_UMTS_DF 0x7F50 +#define SCARD_FILE_GSM_EF_IMSI 0x6F07 +#define SCARD_FILE_GSM_EF_AD 0x6FAD +#define SCARD_FILE_EF_DIR 0x2F00 +#define SCARD_FILE_EF_ICCID 0x2FE2 +#define SCARD_FILE_EF_CK 0x6FE1 +#define SCARD_FILE_EF_IK 0x6FE2 + +#define SCARD_CHV1_OFFSET 13 +#define SCARD_CHV1_FLAG 0x80 + + typedef enum { SCARD_GSM_SIM, SCARD_USIM } sim_types; struct scard_data { @@ -240,37 +258,60 @@ static int scard_parse_fsp_templ(unsigned char *buf, size_t buf_len, int *ps_do, int *file_len) { - unsigned char *pos, *end; + unsigned char *pos, *end; - if (ps_do) - *ps_do = -1; - if (file_len) - *file_len = -1; - - pos = buf; - end = pos + buf_len; - if (*pos != USIM_FSP_TEMPL_TAG) { - wpa_printf(MSG_DEBUG, "SCARD: file header did not " - "start with FSP template tag"); - return -1; - } - pos++; - if (pos >= end) - return -1; - if ((pos + pos[0]) < end) - end = pos + 1 + pos[0]; - pos++; - wpa_hexdump(MSG_DEBUG, "SCARD: file header FSP template", - pos, end - pos); - - while (pos + 1 < end) { - wpa_printf(MSG_MSGDUMP, "SCARD: file header TLV " - "0x%02x len=%d", pos[0], pos[1]); - if (pos + 2 + pos[1] > end) - break; + if (ps_do) + *ps_do = -1; + if (file_len) + *file_len = -1; + + pos = buf; + end = pos + buf_len; + if (*pos != USIM_FSP_TEMPL_TAG) { + wpa_printf(MSG_DEBUG, "SCARD: file header did not " + "start with FSP template tag"); + return -1; + } + pos++; + if (pos >= end) + return -1; + if ((pos + pos[0]) < end) + end = pos + 1 + pos[0]; + pos++; + wpa_hexdump(MSG_DEBUG, "SCARD: file header FSP template", + pos, end - pos); + + while (pos + 1 < end) { + wpa_printf(MSG_MSGDUMP, "SCARD: file header TLV 0x%02x len=%d", + pos[0], pos[1]); + if (pos + 2 + pos[1] > end) + break; - if (pos[0] == USIM_TLV_FILE_SIZE && - (pos[1] == 1 || pos[1] == 2) && file_len) { + switch (pos[0]) { + case USIM_TLV_FILE_DESC: + wpa_hexdump(MSG_MSGDUMP, "SCARD: File Descriptor TLV", + pos + 2, pos[1]); + break; + case USIM_TLV_FILE_ID: + wpa_hexdump(MSG_MSGDUMP, "SCARD: File Identifier TLV", + pos + 2, pos[1]); + break; + case USIM_TLV_DF_NAME: + wpa_hexdump(MSG_MSGDUMP, "SCARD: DF name (AID) TLV", + pos + 2, pos[1]); + break; + case USIM_TLV_PROPR_INFO: + wpa_hexdump(MSG_MSGDUMP, "SCARD: Proprietary " + "information TLV", pos + 2, pos[1]); + break; + case USIM_TLV_LIFE_CYCLE_STATUS: + wpa_hexdump(MSG_MSGDUMP, "SCARD: Life Cycle Status " + "Integer TLV", pos + 2, pos[1]); + break; + case USIM_TLV_FILE_SIZE: + wpa_hexdump(MSG_MSGDUMP, "SCARD: File size TLV", + pos + 2, pos[1]); + if ((pos[1] == 1 || pos[1] == 2) && file_len) { if (pos[1] == 1) *file_len = (int) pos[2]; else @@ -279,21 +320,43 @@ wpa_printf(MSG_DEBUG, "SCARD: file_size=%d", *file_len); } - - if (pos[0] == USIM_TLV_PIN_STATUS_TEMPLATE && - pos[1] >= 2 && pos[2] == USIM_PS_DO_TAG && + break; + case USIM_TLV_TOTAL_FILE_SIZE: + wpa_hexdump(MSG_MSGDUMP, "SCARD: Total file size TLV", + pos + 2, pos[1]); + break; + case USIM_TLV_PIN_STATUS_TEMPLATE: + wpa_hexdump(MSG_MSGDUMP, "SCARD: PIN Status Template " + "DO TLV", pos + 2, pos[1]); + if (pos[1] >= 2 && pos[2] == USIM_PS_DO_TAG && pos[3] >= 1 && ps_do) { wpa_printf(MSG_DEBUG, "SCARD: PS_DO=0x%02x", pos[4]); *ps_do = (int) pos[4]; } + break; + case USIM_TLV_SHORT_FILE_ID: + wpa_hexdump(MSG_MSGDUMP, "SCARD: Short File " + "Identifier (SFI) TLV", pos + 2, pos[1]); + break; + case USIM_TLV_SECURITY_ATTR_8B: + case USIM_TLV_SECURITY_ATTR_8C: + case USIM_TLV_SECURITY_ATTR_AB: + wpa_hexdump(MSG_MSGDUMP, "SCARD: Security attribute " + "TLV", pos + 2, pos[1]); + break; + default: + wpa_hexdump(MSG_MSGDUMP, "SCARD: Unrecognized TLV", + pos, 2 + pos[1]); + break; + } - pos += 2 + pos[1]; + pos += 2 + pos[1]; - if (pos == end) - return 0; - } - return -1; + if (pos == end) + return 0; + } + return -1; } @@ -334,7 +397,7 @@ unsigned char rid[5]; unsigned char appl_code[2]; /* 0x1002 for 3G USIM */ } *efdir; - unsigned char buf[100]; + unsigned char buf[127]; size_t blen; efdir = (struct efdir *) buf; @@ -422,19 +485,18 @@ /** * scard_init - Initialize SIM/USIM connection using PC/SC - * @sim_type: Allowed SIM types (SIM, USIM, or both) + * @reader: Reader name prefix to search for * Returns: Pointer to private data structure, or %NULL on failure * * This function is used to initialize SIM/USIM connection. PC/SC is used to - * open connection to the SIM/USIM card and the card is verified to support the - * selected sim_type. In addition, local flag is set if a PIN is needed to - * access some of the card functions. Once the connection is not needed - * anymore, scard_deinit() can be used to close it. + * open connection to the SIM/USIM card. In addition, local flag is set if a + * PIN is needed to access some of the card functions. Once the connection is + * not needed anymore, scard_deinit() can be used to close it. */ -struct scard_data * scard_init(scard_sim_type sim_type) +struct scard_data * scard_init(const char *reader) { long ret; - unsigned long len; + unsigned long len, pos; struct scard_data *scard; #ifdef CONFIG_NATIVE_WINDOWS TCHAR *readers = NULL; @@ -488,18 +550,41 @@ "available."); goto failed; } - /* readers is a list of available reader. Last entry is terminated with - * double NUL. - * TODO: add support for selecting the reader; now just use the first - * one.. */ + wpa_hexdump_ascii(MSG_DEBUG, "SCARD: Readers", (u8 *) readers, len); + /* + * readers is a list of available readers. The last entry is terminated + * with double null. + */ + pos = 0; #ifdef UNICODE - wpa_printf(MSG_DEBUG, "SCARD: Selected reader='%S'", readers); + /* TODO */ #else /* UNICODE */ - wpa_printf(MSG_DEBUG, "SCARD: Selected reader='%s'", readers); + while (pos < len) { + if (reader == NULL || + os_strncmp(&readers[pos], reader, os_strlen(reader)) == 0) + break; + while (pos < len && readers[pos]) + pos++; + pos++; /* skip separating null */ + if (pos < len && readers[pos] == '\0') + pos = len; /* double null terminates list */ + } #endif /* UNICODE */ + if (pos >= len) { + wpa_printf(MSG_WARNING, "SCARD: No reader with prefix '%s' " + "found", reader); + goto failed; + } - ret = SCardConnect(scard->ctx, readers, SCARD_SHARE_SHARED, - SCARD_PROTOCOL_T0, &scard->card, &scard->protocol); +#ifdef UNICODE + wpa_printf(MSG_DEBUG, "SCARD: Selected reader='%S'", &readers[pos]); +#else /* UNICODE */ + wpa_printf(MSG_DEBUG, "SCARD: Selected reader='%s'", &readers[pos]); +#endif /* UNICODE */ + + ret = SCardConnect(scard->ctx, &readers[pos], SCARD_SHARE_SHARED, + SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1, + &scard->card, &scard->protocol); if (ret != SCARD_S_SUCCESS) { if (ret == (long) SCARD_E_NO_SMARTCARD) wpa_printf(MSG_INFO, "No smart card inserted."); @@ -525,20 +610,14 @@ blen = sizeof(buf); - scard->sim_type = SCARD_GSM_SIM; - if (sim_type == SCARD_USIM_ONLY || sim_type == SCARD_TRY_BOTH) { - wpa_printf(MSG_DEBUG, "SCARD: verifying USIM support"); - if (_scard_select_file(scard, SCARD_FILE_MF, buf, &blen, - SCARD_USIM, NULL, 0)) { - wpa_printf(MSG_DEBUG, "SCARD: USIM is not supported"); - if (sim_type == SCARD_USIM_ONLY) - goto failed; - wpa_printf(MSG_DEBUG, "SCARD: Trying to use GSM SIM"); - scard->sim_type = SCARD_GSM_SIM; - } else { - wpa_printf(MSG_DEBUG, "SCARD: USIM is supported"); - scard->sim_type = SCARD_USIM; - } + wpa_printf(MSG_DEBUG, "SCARD: verifying USIM support"); + if (_scard_select_file(scard, SCARD_FILE_MF, buf, &blen, + SCARD_USIM, NULL, 0)) { + wpa_printf(MSG_DEBUG, "SCARD: USIM is not supported. Trying to use GSM SIM"); + scard->sim_type = SCARD_GSM_SIM; + } else { + wpa_printf(MSG_DEBUG, "SCARD: USIM is supported"); + scard->sim_type = SCARD_USIM; } if (scard->sim_type == SCARD_GSM_SIM) { @@ -588,7 +667,8 @@ } if (pin_needed) { scard->pin1_required = 1; - wpa_printf(MSG_DEBUG, "PIN1 needed for SIM access"); + wpa_printf(MSG_DEBUG, "PIN1 needed for SIM access (retry " + "counter=%d)", scard_get_pin_retry_counter(scard)); } ret = SCardEndTransaction(scard->card, SCARD_LEAVE_CARD); @@ -945,6 +1025,46 @@ } +int scard_get_pin_retry_counter(struct scard_data *scard) +{ + long ret; + unsigned char resp[3]; + unsigned char cmd[5] = { SIM_CMD_VERIFY_CHV1 }; + size_t len; + u16 val; + + wpa_printf(MSG_DEBUG, "SCARD: fetching PIN retry counter"); + + if (scard->sim_type == SCARD_USIM) + cmd[0] = USIM_CLA; + cmd[4] = 0; /* Empty data */ + + len = sizeof(resp); + ret = scard_transmit(scard, cmd, sizeof(cmd), resp, &len); + if (ret != SCARD_S_SUCCESS) + return -2; + + if (len != 2) { + wpa_printf(MSG_WARNING, "SCARD: failed to fetch PIN retry " + "counter"); + return -1; + } + + val = WPA_GET_BE16(resp); + if (val == 0x63c0 || val == 0x6983) { + wpa_printf(MSG_DEBUG, "SCARD: PIN has been blocked"); + return 0; + } + + if (val >= 0x63c0 && val <= 0x63cf) + return val & 0x000f; + + wpa_printf(MSG_DEBUG, "SCARD: Unexpected PIN retry counter response " + "value 0x%x", val); + return 0; +} + + /** * scard_get_imsi - Read IMSI from SIM/USIM card * @scard: Pointer to private data from scard_init() @@ -1024,6 +1144,61 @@ /** + * scard_get_mnc_len - Read length of MNC in the IMSI from SIM/USIM card + * @scard: Pointer to private data from scard_init() + * Returns: length (>0) on success, -1 if administrative data file cannot be + * selected, -2 if administrative data file selection returns invalid result + * code, -3 if parsing FSP template file fails (USIM only), -4 if length of + * the file is unexpected, -5 if reading file fails, -6 if MNC length is not + * in range (i.e. 2 or 3), -7 if MNC length is not available. + * + */ +int scard_get_mnc_len(struct scard_data *scard) +{ + unsigned char buf[100]; + size_t blen; + int file_size; + + wpa_printf(MSG_DEBUG, "SCARD: reading MNC len from (GSM) EF-AD"); + blen = sizeof(buf); + if (scard_select_file(scard, SCARD_FILE_GSM_EF_AD, buf, &blen)) + return -1; + if (blen < 4) { + wpa_printf(MSG_WARNING, "SCARD: too short (GSM) EF-AD " + "header (len=%ld)", (long) blen); + return -2; + } + + if (scard->sim_type == SCARD_GSM_SIM) { + file_size = (buf[2] << 8) | buf[3]; + } else { + if (scard_parse_fsp_templ(buf, blen, NULL, &file_size)) + return -3; + } + if (file_size == 3) { + wpa_printf(MSG_DEBUG, "SCARD: MNC length not available"); + return -7; + } + if (file_size < 4 || file_size > (int) sizeof(buf)) { + wpa_printf(MSG_DEBUG, "SCARD: invalid file length=%ld", + (long) file_size); + return -4; + } + + if (scard_read_file(scard, buf, file_size)) + return -5; + buf[3] = buf[3] & 0x0f; /* upper nibble reserved for future use */ + if (buf[3] < 2 || buf[3] > 3) { + wpa_printf(MSG_DEBUG, "SCARD: invalid MNC length=%ld", + (long) buf[3]); + return -6; + } + wpa_printf(MSG_DEBUG, "SCARD: MNC length=%ld", (long) buf[3]); + return buf[3]; +} + + +/** * scard_gsm_auth - Run GSM authentication command on SIM card * @scard: Pointer to private data from scard_init() * @_rand: 16-byte RAND value from HLR/AuC @@ -1236,3 +1411,9 @@ wpa_printf(MSG_DEBUG, "SCARD: Unrecognized response"); return -1; } + + +int scard_supports_umts(struct scard_data *scard) +{ + return scard->sim_type == SCARD_USIM; +} diff -Nru wpa-1.0/src/utils/pcsc_funcs.h wpa-2.1/src/utils/pcsc_funcs.h --- wpa-1.0/src/utils/pcsc_funcs.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/utils/pcsc_funcs.h 2014-02-04 11:23:35.000000000 +0000 @@ -1,67 +1,41 @@ /* * WPA Supplicant / PC/SC smartcard interface for USIM, GSM SIM - * Copyright (c) 2004-2006, Jouni Malinen + * Copyright (c) 2004-2006, 2012, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef PCSC_FUNCS_H #define PCSC_FUNCS_H -/* GSM files - * File type in first octet: - * 3F = Master File - * 7F = Dedicated File - * 2F = Elementary File under the Master File - * 6F = Elementary File under a Dedicated File - */ -#define SCARD_FILE_MF 0x3F00 -#define SCARD_FILE_GSM_DF 0x7F20 -#define SCARD_FILE_UMTS_DF 0x7F50 -#define SCARD_FILE_GSM_EF_IMSI 0x6F07 -#define SCARD_FILE_EF_DIR 0x2F00 -#define SCARD_FILE_EF_ICCID 0x2FE2 -#define SCARD_FILE_EF_CK 0x6FE1 -#define SCARD_FILE_EF_IK 0x6FE2 - -#define SCARD_CHV1_OFFSET 13 -#define SCARD_CHV1_FLAG 0x80 - -typedef enum { - SCARD_GSM_SIM_ONLY, - SCARD_USIM_ONLY, - SCARD_TRY_BOTH -} scard_sim_type; - - #ifdef PCSC_FUNCS -struct scard_data * scard_init(scard_sim_type sim_type); +struct scard_data * scard_init(const char *reader); void scard_deinit(struct scard_data *scard); int scard_set_pin(struct scard_data *scard, const char *pin); int scard_get_imsi(struct scard_data *scard, char *imsi, size_t *len); +int scard_get_mnc_len(struct scard_data *scard); int scard_gsm_auth(struct scard_data *scard, const unsigned char *_rand, unsigned char *sres, unsigned char *kc); int scard_umts_auth(struct scard_data *scard, const unsigned char *_rand, const unsigned char *autn, unsigned char *res, size_t *res_len, unsigned char *ik, unsigned char *ck, unsigned char *auts); +int scard_get_pin_retry_counter(struct scard_data *scard); +int scard_supports_umts(struct scard_data *scard); #else /* PCSC_FUNCS */ -#define scard_init(s) NULL +#define scard_init(r) NULL #define scard_deinit(s) do { } while (0) #define scard_set_pin(s, p) -1 #define scard_get_imsi(s, i, l) -1 +#define scard_get_mnc_len(s) -1 #define scard_gsm_auth(s, r, s2, k) -1 #define scard_umts_auth(s, r, a, r2, rl, i, c, a2) -1 +#define scard_get_pin_retry_counter(s) -1 +#define scard_supports_umts(s) 0 #endif /* PCSC_FUNCS */ diff -Nru wpa-1.0/src/utils/radiotap.h wpa-2.1/src/utils/radiotap.h --- wpa-1.0/src/utils/radiotap.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/utils/radiotap.h 2014-02-04 11:23:35.000000000 +0000 @@ -238,5 +238,6 @@ * retries */ #define IEEE80211_RADIOTAP_F_TX_CTS 0x0002 /* used cts 'protection' */ #define IEEE80211_RADIOTAP_F_TX_RTS 0x0004 /* used rts/cts handshake */ +#define IEEE80211_RADIOTAP_F_TX_NOACK 0x0008 /* don't expect an ACK */ #endif /* IEEE80211_RADIOTAP_H */ diff -Nru wpa-1.0/src/utils/state_machine.h wpa-2.1/src/utils/state_machine.h --- wpa-1.0/src/utils/state_machine.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/utils/state_machine.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * wpa_supplicant/hostapd - State machine definitions * Copyright (c) 2002-2005, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. * * This file includes a set of pre-processor macros that can be used to * implement a state machine. In addition to including this header file, each diff -Nru wpa-1.0/src/utils/trace.c wpa-2.1/src/utils/trace.c --- wpa-1.0/src/utils/trace.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/utils/trace.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * Backtrace debugging * Copyright (c) 2009, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff -Nru wpa-1.0/src/utils/trace.h wpa-2.1/src/utils/trace.h --- wpa-1.0/src/utils/trace.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/utils/trace.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * Backtrace debugging * Copyright (c) 2009, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef TRACE_H diff -Nru wpa-1.0/src/utils/uuid.c wpa-2.1/src/utils/uuid.c --- wpa-1.0/src/utils/uuid.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/utils/uuid.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * Universally Unique IDentifier (UUID) * Copyright (c) 2008, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff -Nru wpa-1.0/src/utils/uuid.h wpa-2.1/src/utils/uuid.h --- wpa-1.0/src/utils/uuid.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/utils/uuid.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * Universally Unique IDentifier (UUID) * Copyright (c) 2008, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef UUID_H diff -Nru wpa-1.0/src/utils/wpabuf.c wpa-2.1/src/utils/wpabuf.c --- wpa-1.0/src/utils/wpabuf.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/utils/wpabuf.c 2014-02-04 11:23:35.000000000 +0000 @@ -1,15 +1,9 @@ /* * Dynamic data buffer - * Copyright (c) 2007-2009, Jouni Malinen + * Copyright (c) 2007-2012, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -74,12 +68,12 @@ if (buf->used + add_len > buf->size) { unsigned char *nbuf; - if (buf->ext_data) { - nbuf = os_realloc(buf->ext_data, buf->used + add_len); + if (buf->flags & WPABUF_FLAG_EXT_DATA) { + nbuf = os_realloc(buf->buf, buf->used + add_len); if (nbuf == NULL) return -1; os_memset(nbuf + buf->used, 0, add_len); - buf->ext_data = nbuf; + buf->buf = nbuf; } else { #ifdef WPA_TRACE nbuf = os_realloc(trace, sizeof(struct wpabuf_trace) + @@ -101,6 +95,7 @@ os_memset(nbuf + sizeof(struct wpabuf) + buf->used, 0, add_len); #endif /* WPA_TRACE */ + buf->buf = (u8 *) (buf + 1); *_buf = buf; } buf->size = buf->used + add_len; @@ -132,6 +127,7 @@ #endif /* WPA_TRACE */ buf->size = len; + buf->buf = (u8 *) (buf + 1); return buf; } @@ -154,7 +150,8 @@ buf->size = len; buf->used = len; - buf->ext_data = data; + buf->buf = data; + buf->flags |= WPABUF_FLAG_EXT_DATA; return buf; } @@ -195,12 +192,14 @@ wpa_trace_show("wpabuf_free magic mismatch"); abort(); } - os_free(buf->ext_data); + if (buf->flags & WPABUF_FLAG_EXT_DATA) + os_free(buf->buf); os_free(trace); #else /* WPA_TRACE */ if (buf == NULL) return; - os_free(buf->ext_data); + if (buf->flags & WPABUF_FLAG_EXT_DATA) + os_free(buf->buf); os_free(buf); #endif /* WPA_TRACE */ } diff -Nru wpa-1.0/src/utils/wpabuf.h wpa-2.1/src/utils/wpabuf.h --- wpa-1.0/src/utils/wpabuf.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/utils/wpabuf.h 2014-02-04 11:23:35.000000000 +0000 @@ -1,20 +1,17 @@ /* * Dynamic data buffer - * Copyright (c) 2007-2009, Jouni Malinen + * Copyright (c) 2007-2012, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef WPABUF_H #define WPABUF_H +/* wpabuf::buf is a pointer to external data */ +#define WPABUF_FLAG_EXT_DATA BIT(0) + /* * Internal data structure for wpabuf. Please do not touch this directly from * elsewhere. This is only defined in header file to allow inline functions @@ -23,8 +20,8 @@ struct wpabuf { size_t size; /* total size of the allocated buffer */ size_t used; /* length of data in the buffer */ - u8 *ext_data; /* pointer to external data; NULL if data follows - * struct wpabuf */ + u8 *buf; /* pointer to the head of the buffer */ + unsigned int flags; /* optionally followed by the allocated buffer */ }; @@ -78,9 +75,7 @@ */ static inline const void * wpabuf_head(const struct wpabuf *buf) { - if (buf->ext_data) - return buf->ext_data; - return buf + 1; + return buf->buf; } static inline const u8 * wpabuf_head_u8(const struct wpabuf *buf) @@ -95,9 +90,7 @@ */ static inline void * wpabuf_mhead(struct wpabuf *buf) { - if (buf->ext_data) - return buf->ext_data; - return buf + 1; + return buf->buf; } static inline u8 * wpabuf_mhead_u8(struct wpabuf *buf) @@ -156,7 +149,8 @@ static inline void wpabuf_set(struct wpabuf *buf, const void *data, size_t len) { - buf->ext_data = (u8 *) data; + buf->buf = (u8 *) data; + buf->flags = WPABUF_FLAG_EXT_DATA; buf->size = buf->used = len; } diff -Nru wpa-1.0/src/utils/wpa_debug.c wpa-2.1/src/utils/wpa_debug.c --- wpa-1.0/src/utils/wpa_debug.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/utils/wpa_debug.c 2014-02-04 11:23:35.000000000 +0000 @@ -1,15 +1,9 @@ /* * wpa_supplicant/hostapd / Debug prints - * Copyright (c) 2002-2007, Jouni Malinen + * Copyright (c) 2002-2013, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -22,6 +16,18 @@ static int wpa_debug_syslog = 0; #endif /* CONFIG_DEBUG_SYSLOG */ +#ifdef CONFIG_DEBUG_LINUX_TRACING +#include +#include +#include +#include +#include + +static FILE *wpa_debug_tracing_file = NULL; + +#define WPAS_TRACE_PFX "wpas <%d>: " +#endif /* CONFIG_DEBUG_LINUX_TRACING */ + int wpa_debug_level = MSG_INFO; int wpa_debug_show_keys = 0; @@ -36,25 +42,18 @@ #define ANDROID_LOG_NAME "wpa_supplicant" #endif /* ANDROID_LOG_NAME */ -void android_printf(int level, char *format, ...) +static int wpa_to_android_level(int level) { - if (level >= wpa_debug_level) { - va_list ap; - if (level == MSG_ERROR) - level = ANDROID_LOG_ERROR; - else if (level == MSG_WARNING) - level = ANDROID_LOG_WARN; - else if (level == MSG_INFO) - level = ANDROID_LOG_INFO; - else - level = ANDROID_LOG_DEBUG; - va_start(ap, format); - __android_log_vprint(level, ANDROID_LOG_NAME, format, ap); - va_end(ap); - } + if (level == MSG_ERROR) + return ANDROID_LOG_ERROR; + if (level == MSG_WARNING) + return ANDROID_LOG_WARN; + if (level == MSG_INFO) + return ANDROID_LOG_INFO; + return ANDROID_LOG_DEBUG; } -#else /* CONFIG_ANDROID_LOG */ +#endif /* CONFIG_ANDROID_LOG */ #ifndef CONFIG_NO_STDOUT_DEBUG @@ -65,6 +64,7 @@ void wpa_debug_print_timestamp(void) { +#ifndef CONFIG_ANDROID_LOG struct os_time tv; if (!wpa_debug_timestamp) @@ -78,6 +78,7 @@ } else #endif /* CONFIG_DEBUG_FILE */ printf("%ld.%06u: ", (long) tv.sec, (unsigned int) tv.usec); +#endif /* CONFIG_ANDROID_LOG */ } @@ -118,6 +119,77 @@ #endif /* CONFIG_DEBUG_SYSLOG */ +#ifdef CONFIG_DEBUG_LINUX_TRACING + +int wpa_debug_open_linux_tracing(void) +{ + int mounts, trace_fd; + char buf[4096] = {}; + ssize_t buflen; + char *line, *tmp1, *path = NULL; + + mounts = open("/proc/mounts", O_RDONLY); + if (mounts < 0) { + printf("no /proc/mounts\n"); + return -1; + } + + buflen = read(mounts, buf, sizeof(buf) - 1); + close(mounts); + if (buflen < 0) { + printf("failed to read /proc/mounts\n"); + return -1; + } + + line = strtok_r(buf, "\n", &tmp1); + while (line) { + char *tmp2, *tmp_path, *fstype; + /* " ..." */ + strtok_r(line, " ", &tmp2); + tmp_path = strtok_r(NULL, " ", &tmp2); + fstype = strtok_r(NULL, " ", &tmp2); + if (strcmp(fstype, "debugfs") == 0) { + path = tmp_path; + break; + } + + line = strtok_r(NULL, "\n", &tmp1); + } + + if (path == NULL) { + printf("debugfs mountpoint not found\n"); + return -1; + } + + snprintf(buf, sizeof(buf) - 1, "%s/tracing/trace_marker", path); + + trace_fd = open(buf, O_WRONLY); + if (trace_fd < 0) { + printf("failed to open trace_marker file\n"); + return -1; + } + wpa_debug_tracing_file = fdopen(trace_fd, "w"); + if (wpa_debug_tracing_file == NULL) { + close(trace_fd); + printf("failed to fdopen()\n"); + return -1; + } + + return 0; +} + + +void wpa_debug_close_linux_tracing(void) +{ + if (wpa_debug_tracing_file == NULL) + return; + fclose(wpa_debug_tracing_file); + wpa_debug_tracing_file = NULL; +} + +#endif /* CONFIG_DEBUG_LINUX_TRACING */ + + /** * wpa_printf - conditional printf * @level: priority level (MSG_*) of the message @@ -135,6 +207,10 @@ va_start(ap, fmt); if (level >= wpa_debug_level) { +#ifdef CONFIG_ANDROID_LOG + __android_log_vprint(wpa_to_android_level(level), + ANDROID_LOG_NAME, fmt, ap); +#else /* CONFIG_ANDROID_LOG */ #ifdef CONFIG_DEBUG_SYSLOG if (wpa_debug_syslog) { vsyslog(syslog_priority(level), fmt, ap); @@ -155,8 +231,20 @@ #ifdef CONFIG_DEBUG_SYSLOG } #endif /* CONFIG_DEBUG_SYSLOG */ +#endif /* CONFIG_ANDROID_LOG */ } va_end(ap); + +#ifdef CONFIG_DEBUG_LINUX_TRACING + if (wpa_debug_tracing_file != NULL) { + va_start(ap, fmt); + fprintf(wpa_debug_tracing_file, WPAS_TRACE_PFX, level); + vfprintf(wpa_debug_tracing_file, fmt, ap); + fprintf(wpa_debug_tracing_file, "\n"); + fflush(wpa_debug_tracing_file); + va_end(ap); + } +#endif /* CONFIG_DEBUG_LINUX_TRACING */ } @@ -164,8 +252,65 @@ size_t len, int show) { size_t i; + +#ifdef CONFIG_DEBUG_LINUX_TRACING + if (wpa_debug_tracing_file != NULL) { + fprintf(wpa_debug_tracing_file, + WPAS_TRACE_PFX "%s - hexdump(len=%lu):", + level, title, (unsigned long) len); + if (buf == NULL) { + fprintf(wpa_debug_tracing_file, " [NULL]\n"); + } else if (!show) { + fprintf(wpa_debug_tracing_file, " [REMOVED]\n"); + } else { + for (i = 0; i < len; i++) + fprintf(wpa_debug_tracing_file, + " %02x", buf[i]); + } + fflush(wpa_debug_tracing_file); + } +#endif /* CONFIG_DEBUG_LINUX_TRACING */ + if (level < wpa_debug_level) return; +#ifdef CONFIG_ANDROID_LOG + { + const char *display; + char *strbuf = NULL; + size_t slen = len; + if (buf == NULL) { + display = " [NULL]"; + } else if (len == 0) { + display = ""; + } else if (show && len) { + /* Limit debug message length for Android log */ + if (slen > 32) + slen = 32; + strbuf = os_malloc(1 + 3 * slen); + if (strbuf == NULL) { + wpa_printf(MSG_ERROR, "wpa_hexdump: Failed to " + "allocate message buffer"); + return; + } + + for (i = 0; i < slen; i++) + os_snprintf(&strbuf[i * 3], 4, " %02x", + buf[i]); + + display = strbuf; + } else { + display = " [REMOVED]"; + } + + __android_log_print(wpa_to_android_level(level), + ANDROID_LOG_NAME, + "%s - hexdump(len=%lu):%s%s", + title, (long unsigned int) len, display, + len > slen ? " ..." : ""); + os_free(strbuf); + return; + } +#else /* CONFIG_ANDROID_LOG */ #ifdef CONFIG_DEBUG_SYSLOG if (wpa_debug_syslog) { const char *display; @@ -193,7 +338,7 @@ } syslog(syslog_priority(level), "%s - hexdump(len=%lu):%s", - title, len, display); + title, (unsigned long) len, display); os_free(strbuf); return; } @@ -227,29 +372,52 @@ #ifdef CONFIG_DEBUG_FILE } #endif /* CONFIG_DEBUG_FILE */ +#endif /* CONFIG_ANDROID_LOG */ } -void wpa_hexdump(int level, const char *title, const u8 *buf, size_t len) +void wpa_hexdump(int level, const char *title, const void *buf, size_t len) { _wpa_hexdump(level, title, buf, len, 1); } -void wpa_hexdump_key(int level, const char *title, const u8 *buf, size_t len) +void wpa_hexdump_key(int level, const char *title, const void *buf, size_t len) { _wpa_hexdump(level, title, buf, len, wpa_debug_show_keys); } -static void _wpa_hexdump_ascii(int level, const char *title, const u8 *buf, +static void _wpa_hexdump_ascii(int level, const char *title, const void *buf, size_t len, int show) { size_t i, llen; const u8 *pos = buf; const size_t line_len = 16; +#ifdef CONFIG_DEBUG_LINUX_TRACING + if (wpa_debug_tracing_file != NULL) { + fprintf(wpa_debug_tracing_file, + WPAS_TRACE_PFX "%s - hexdump_ascii(len=%lu):", + level, title, (unsigned long) len); + if (buf == NULL) { + fprintf(wpa_debug_tracing_file, " [NULL]\n"); + } else if (!show) { + fprintf(wpa_debug_tracing_file, " [REMOVED]\n"); + } else { + /* can do ascii processing in userspace */ + for (i = 0; i < len; i++) + fprintf(wpa_debug_tracing_file, + " %02x", pos[i]); + } + fflush(wpa_debug_tracing_file); + } +#endif /* CONFIG_DEBUG_LINUX_TRACING */ + if (level < wpa_debug_level) return; +#ifdef CONFIG_ANDROID_LOG + _wpa_hexdump(level, title, buf, len, show); +#else /* CONFIG_ANDROID_LOG */ wpa_debug_print_timestamp(); #ifdef CONFIG_DEBUG_FILE if (out_file) { @@ -323,16 +491,18 @@ #ifdef CONFIG_DEBUG_FILE } #endif /* CONFIG_DEBUG_FILE */ +#endif /* CONFIG_ANDROID_LOG */ } -void wpa_hexdump_ascii(int level, const char *title, const u8 *buf, size_t len) +void wpa_hexdump_ascii(int level, const char *title, const void *buf, + size_t len) { _wpa_hexdump_ascii(level, title, buf, len, 1); } -void wpa_hexdump_ascii_key(int level, const char *title, const u8 *buf, +void wpa_hexdump_ascii_key(int level, const char *title, const void *buf, size_t len) { _wpa_hexdump_ascii(level, title, buf, len, wpa_debug_show_keys); @@ -404,7 +574,6 @@ #endif /* CONFIG_NO_STDOUT_DEBUG */ -#endif /* CONFIG_ANDROID_LOG */ #ifndef CONFIG_NO_WPA_MSG static wpa_msg_cb_func wpa_msg_cb = NULL; @@ -452,7 +621,7 @@ va_end(ap); wpa_printf(level, "%s%s", prefix, buf); if (wpa_msg_cb) - wpa_msg_cb(ctx, level, buf, len); + wpa_msg_cb(ctx, level, 0, buf, len); os_free(buf); } @@ -476,9 +645,56 @@ va_start(ap, fmt); len = vsnprintf(buf, buflen, fmt, ap); va_end(ap); - wpa_msg_cb(ctx, level, buf, len); + wpa_msg_cb(ctx, level, 0, buf, len); os_free(buf); } + + +void wpa_msg_global(void *ctx, int level, const char *fmt, ...) +{ + va_list ap; + char *buf; + const int buflen = 2048; + int len; + + buf = os_malloc(buflen); + if (buf == NULL) { + wpa_printf(MSG_ERROR, "wpa_msg_global: Failed to allocate " + "message buffer"); + return; + } + va_start(ap, fmt); + len = vsnprintf(buf, buflen, fmt, ap); + va_end(ap); + wpa_printf(level, "%s", buf); + if (wpa_msg_cb) + wpa_msg_cb(ctx, level, 1, buf, len); + os_free(buf); +} + + +void wpa_msg_no_global(void *ctx, int level, const char *fmt, ...) +{ + va_list ap; + char *buf; + const int buflen = 2048; + int len; + + buf = os_malloc(buflen); + if (buf == NULL) { + wpa_printf(MSG_ERROR, "wpa_msg_no_global: Failed to allocate " + "message buffer"); + return; + } + va_start(ap, fmt); + len = vsnprintf(buf, buflen, fmt, ap); + va_end(ap); + wpa_printf(level, "%s", buf); + if (wpa_msg_cb) + wpa_msg_cb(ctx, level, 2, buf, len); + os_free(buf); +} + #endif /* CONFIG_NO_WPA_MSG */ diff -Nru wpa-1.0/src/utils/wpa_debug.h wpa-2.1/src/utils/wpa_debug.h --- wpa-1.0/src/utils/wpa_debug.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/utils/wpa_debug.h 2014-02-04 11:23:35.000000000 +0000 @@ -1,15 +1,9 @@ /* * wpa_supplicant/hostapd / Debug prints - * Copyright (c) 2002-2007, Jouni Malinen + * Copyright (c) 2002-2013, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef WPA_DEBUG_H @@ -17,6 +11,10 @@ #include "wpabuf.h" +extern int wpa_debug_level; +extern int wpa_debug_show_keys; +extern int wpa_debug_timestamp; + /* Debugging function - conditional printf and hex dump. Driver wrappers can * use these for debugging purposes. */ @@ -24,32 +22,6 @@ MSG_EXCESSIVE, MSG_MSGDUMP, MSG_DEBUG, MSG_INFO, MSG_WARNING, MSG_ERROR }; -#ifdef CONFIG_ANDROID_LOG - -#define wpa_debug_print_timestamp() do {} while (0) -#define wpa_hexdump(...) do {} while (0) -#define wpa_hexdump_key(...) do {} while (0) -#define wpa_hexdump_buf(l,t,b) do {} while (0) -#define wpa_hexdump_buf_key(l,t,b) do {} while (0) -#define wpa_hexdump_ascii(...) do {} while (0) -#define wpa_hexdump_ascii_key(...) do {} while (0) -#define wpa_debug_open_file(...) do {} while (0) -#define wpa_debug_close_file() do {} while (0) -#define wpa_dbg(...) do {} while (0) - -static inline int wpa_debug_reopen_file(void) -{ - return 0; -} - - -void android_printf(int level, char *format, ...) -PRINTF_FORMAT(2, 3); - -#define wpa_printf android_printf - -#else /* CONFIG_ANDROID_LOG */ - #ifdef CONFIG_NO_STDOUT_DEBUG #define wpa_debug_print_timestamp() do { } while (0) @@ -109,7 +81,7 @@ * output may be directed to stdout, stderr, and/or syslog based on * configuration. The contents of buf is printed out has hex dump. */ -void wpa_hexdump(int level, const char *title, const u8 *buf, size_t len); +void wpa_hexdump(int level, const char *title, const void *buf, size_t len); static inline void wpa_hexdump_buf(int level, const char *title, const struct wpabuf *buf) @@ -131,12 +103,12 @@ * like wpa_hexdump(), but by default, does not include secret keys (passwords, * etc.) in debug output. */ -void wpa_hexdump_key(int level, const char *title, const u8 *buf, size_t len); +void wpa_hexdump_key(int level, const char *title, const void *buf, size_t len); static inline void wpa_hexdump_buf_key(int level, const char *title, const struct wpabuf *buf) { - wpa_hexdump_key(level, title, buf ? wpabuf_head(buf) : 0, + wpa_hexdump_key(level, title, buf ? wpabuf_head(buf) : NULL, buf ? wpabuf_len(buf) : 0); } @@ -153,7 +125,7 @@ * the hex numbers and ASCII characters (for printable range) are shown. 16 * bytes per line will be shown. */ -void wpa_hexdump_ascii(int level, const char *title, const u8 *buf, +void wpa_hexdump_ascii(int level, const char *title, const void *buf, size_t len); /** @@ -170,7 +142,7 @@ * bytes per line will be shown. This works like wpa_hexdump_ascii(), but by * default, does not include secret keys (passwords, etc.) in debug output. */ -void wpa_hexdump_ascii_key(int level, const char *title, const u8 *buf, +void wpa_hexdump_ascii_key(int level, const char *title, const void *buf, size_t len); /* @@ -183,12 +155,12 @@ #endif /* CONFIG_NO_STDOUT_DEBUG */ -#endif /* CONFIG_ANDROID_LOG */ - #ifdef CONFIG_NO_WPA_MSG #define wpa_msg(args...) do { } while (0) #define wpa_msg_ctrl(args...) do { } while (0) +#define wpa_msg_global(args...) do { } while (0) +#define wpa_msg_no_global(args...) do { } while (0) #define wpa_msg_register_cb(f) do { } while (0) #define wpa_msg_register_ifname_cb(f) do { } while (0) #else /* CONFIG_NO_WPA_MSG */ @@ -223,8 +195,38 @@ void wpa_msg_ctrl(void *ctx, int level, const char *fmt, ...) PRINTF_FORMAT(3, 4); -typedef void (*wpa_msg_cb_func)(void *ctx, int level, const char *txt, - size_t len); +/** + * wpa_msg_global - Global printf for ctrl_iface monitors + * @ctx: Pointer to context data; this is the ctx variable registered + * with struct wpa_driver_ops::init() + * @level: priority level (MSG_*) of the message + * @fmt: printf format string, followed by optional arguments + * + * This function is used to print conditional debugging and error messages. + * This function is like wpa_msg(), but it sends the output as a global event, + * i.e., without being specific to an interface. For backwards compatibility, + * an old style event is also delivered on one of the interfaces (the one + * specified by the context data). + */ +void wpa_msg_global(void *ctx, int level, const char *fmt, ...) +PRINTF_FORMAT(3, 4); + +/** + * wpa_msg_no_global - Conditional printf for ctrl_iface monitors + * @ctx: Pointer to context data; this is the ctx variable registered + * with struct wpa_driver_ops::init() + * @level: priority level (MSG_*) of the message + * @fmt: printf format string, followed by optional arguments + * + * This function is used to print conditional debugging and error messages. + * This function is like wpa_msg(), but it does not send the output as a global + * event. + */ +void wpa_msg_no_global(void *ctx, int level, const char *fmt, ...) +PRINTF_FORMAT(3, 4); + +typedef void (*wpa_msg_cb_func)(void *ctx, int level, int global, + const char *txt, size_t len); /** * wpa_msg_register_cb - Register callback function for wpa_msg() messages @@ -289,6 +291,24 @@ #endif /* CONFIG_DEBUG_SYSLOG */ +#ifdef CONFIG_DEBUG_LINUX_TRACING + +int wpa_debug_open_linux_tracing(void); +void wpa_debug_close_linux_tracing(void); + +#else /* CONFIG_DEBUG_LINUX_TRACING */ + +static inline int wpa_debug_open_linux_tracing(void) +{ + return 0; +} + +static inline void wpa_debug_close_linux_tracing(void) +{ +} + +#endif /* CONFIG_DEBUG_LINUX_TRACING */ + #ifdef EAPOL_TEST #define WPA_ASSERT(a) \ diff -Nru wpa-1.0/src/wps/http_client.c wpa-2.1/src/wps/http_client.c --- wpa-1.0/src/wps/http_client.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/wps/http_client.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * http_client - HTTP client * Copyright (c) 2009, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -98,7 +92,7 @@ (unsigned long) wpabuf_len(c->req), (unsigned long) wpabuf_len(c->req) - c->req_pos); - res = send(c->sd, wpabuf_head(c->req) + c->req_pos, + res = send(c->sd, wpabuf_head_u8(c->req) + c->req_pos, wpabuf_len(c->req) - c->req_pos, 0); if (res < 0) { wpa_printf(MSG_DEBUG, "HTTP: Failed to send buffer: %s", diff -Nru wpa-1.0/src/wps/http_client.h wpa-2.1/src/wps/http_client.h --- wpa-1.0/src/wps/http_client.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/wps/http_client.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * http_client - HTTP client * Copyright (c) 2009, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef HTTP_CLIENT_H diff -Nru wpa-1.0/src/wps/httpread.c wpa-2.1/src/wps/httpread.c --- wpa-1.0/src/wps/httpread.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/wps/httpread.c 2014-02-04 11:23:35.000000000 +0000 @@ -3,14 +3,8 @@ * Author: Ted Merrill * Copyright 2008 Atheros Communications * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. * * The files are buffered via internal callbacks from eloop, then presented to * an application callback routine when completely read into memory. May also @@ -73,8 +67,6 @@ int timeout_seconds; /* 0 or total duration timeout period */ /* dynamically used information follows */ - int sd_registered; /* nonzero if we need to unregister socket */ - int to_registered; /* nonzero if we need to unregister timeout */ int got_hdr; /* nonzero when header is finalized */ char hdr[HTTPREAD_HEADER_MAX_SIZE+1]; /* headers stored here */ @@ -135,19 +127,6 @@ } -/* convert hex to binary - * Requires that c have been previously tested true with isxdigit(). - */ -static int hex_value(int c) -{ - if (isdigit(c)) - return c - '0'; - if (islower(c)) - return 10 + c - 'a'; - return 10 + c - 'A'; -} - - static void httpread_timeout_handler(void *eloop_data, void *user_ctx); /* httpread_destroy -- if h is non-NULL, clean up @@ -162,12 +141,8 @@ if (!h) return; - if (h->to_registered) - eloop_cancel_timeout(httpread_timeout_handler, NULL, h); - h->to_registered = 0; - if (h->sd_registered) - eloop_unregister_sock(h->sd, EVENT_TYPE_READ); - h->sd_registered = 0; + eloop_cancel_timeout(httpread_timeout_handler, NULL, h); + eloop_unregister_sock(h->sd, EVENT_TYPE_READ); os_free(h->body); os_free(h->uri); os_memset(h, 0, sizeof(*h)); /* aid debugging */ @@ -182,7 +157,6 @@ { struct httpread *h = user_ctx; wpa_printf(MSG_DEBUG, "httpread timeout (%p)", h); - h->to_registered = 0; /* is self-cancelling */ (*h->cb)(h, h->cookie, HTTPREAD_EVENT_TIMEOUT); } @@ -301,8 +275,7 @@ int c = *rawuri; if (c == '%' && isxdigit(rawuri[1]) && isxdigit(rawuri[2])) { - *uri++ = (hex_value(rawuri[1]) << 4) | - hex_value(rawuri[2]); + *uri++ = hex2byte(rawuri + 1); rawuri += 3; } else { *uri++ = c; @@ -709,15 +682,11 @@ * and just in case somehow we don't get destroyed right away, * unregister now. */ - if (h->sd_registered) - eloop_unregister_sock(h->sd, EVENT_TYPE_READ); - h->sd_registered = 0; + eloop_unregister_sock(h->sd, EVENT_TYPE_READ); /* The application can destroy us whenever they feel like... * cancel timeout. */ - if (h->to_registered) - eloop_cancel_timeout(httpread_timeout_handler, NULL, h); - h->to_registered = 0; + eloop_cancel_timeout(httpread_timeout_handler, NULL, h); (*h->cb)(h, h->cookie, HTTPREAD_EVENT_FILE_READY); } @@ -755,21 +724,17 @@ h->max_bytes = max_bytes; h->timeout_seconds = timeout_seconds; - if (timeout_seconds > 0) { - if (eloop_register_timeout(timeout_seconds, 0, - httpread_timeout_handler, - NULL, h)) { - /* No way to recover (from malloc failure) */ - goto fail; - } - h->to_registered = 1; + if (timeout_seconds > 0 && + eloop_register_timeout(timeout_seconds, 0, + httpread_timeout_handler, NULL, h)) { + /* No way to recover (from malloc failure) */ + goto fail; } if (eloop_register_sock(sd, EVENT_TYPE_READ, httpread_read_handler, NULL, h)) { /* No way to recover (from malloc failure) */ goto fail; } - h->sd_registered = 1; return h; fail: diff -Nru wpa-1.0/src/wps/httpread.h wpa-2.1/src/wps/httpread.h --- wpa-1.0/src/wps/httpread.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/wps/httpread.h 2014-02-04 11:23:35.000000000 +0000 @@ -3,14 +3,8 @@ * Author: Ted Merrill * Copyright 2008 Atheros Communications * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef HTTPREAD_H diff -Nru wpa-1.0/src/wps/http_server.c wpa-2.1/src/wps/http_server.c --- wpa-1.0/src/wps/http_server.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/wps/http_server.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * http_server - HTTP server * Copyright (c) 2009, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -238,6 +232,7 @@ { struct sockaddr_in sin; struct http_server *srv; + int on = 1; srv = os_zalloc(sizeof(*srv)); if (srv == NULL) @@ -248,6 +243,9 @@ srv->fd = socket(AF_INET, SOCK_STREAM, 0); if (srv->fd < 0) goto fail; + + setsockopt(srv->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); + if (fcntl(srv->fd, F_SETFL, O_NONBLOCK) < 0) goto fail; if (port < 0) diff -Nru wpa-1.0/src/wps/http_server.h wpa-2.1/src/wps/http_server.h --- wpa-1.0/src/wps/http_server.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/wps/http_server.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * http_server - HTTP server * Copyright (c) 2009, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef HTTP_SERVER_H diff -Nru wpa-1.0/src/wps/Makefile wpa-2.1/src/wps/Makefile --- wpa-1.0/src/wps/Makefile 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/wps/Makefile 2014-02-04 11:23:35.000000000 +0000 @@ -2,7 +2,7 @@ @echo Nothing to be made. clean: - rm -f *~ *.o *.d + rm -f *~ *.o *.d *.gcno *.gcda *.gcov install: @echo Nothing to be made. diff -Nru wpa-1.0/src/wps/ndef.c wpa-2.1/src/wps/ndef.c --- wpa-1.0/src/wps/ndef.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/wps/ndef.c 2014-02-04 11:23:35.000000000 +0000 @@ -1,34 +1,28 @@ /* * NDEF(NFC Data Exchange Format) routines for Wi-Fi Protected Setup * Reference is "NFCForum-TS-NDEF_1.0 2006-07-24". - * Copyright (c) 2009, Masashi Honma + * Copyright (c) 2009-2012, Masashi Honma * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" #include "common.h" #include "wps/wps.h" -#include "wps/wps_i.h" #define FLAG_MESSAGE_BEGIN (1 << 7) #define FLAG_MESSAGE_END (1 << 6) #define FLAG_CHUNK (1 << 5) #define FLAG_SHORT_RECORD (1 << 4) #define FLAG_ID_LENGTH_PRESENT (1 << 3) +#define FLAG_TNF_NFC_FORUM (0x01) #define FLAG_TNF_RFC2046 (0x02) struct ndef_record { - u8 *type; - u8 *id; - u8 *payload; + const u8 *type; + const u8 *id; + const u8 *payload; u8 type_length; u8 id_length; u32 payload_length; @@ -36,10 +30,12 @@ }; static char wifi_handover_type[] = "application/vnd.wfa.wsc"; +static char p2p_handover_type[] = "application/vnd.wfa.p2p"; -static int ndef_parse_record(u8 *data, u32 size, struct ndef_record *record) +static int ndef_parse_record(const u8 *data, u32 size, + struct ndef_record *record) { - u8 *pos = data + 1; + const u8 *pos = data + 1; if (size < 2) return -1; @@ -78,12 +74,12 @@ } -static struct wpabuf * ndef_parse_records(struct wpabuf *buf, +static struct wpabuf * ndef_parse_records(const struct wpabuf *buf, int (*filter)(struct ndef_record *)) { struct ndef_record record; int len = wpabuf_len(buf); - u8 *data = wpabuf_mhead(buf); + const u8 *data = wpabuf_head(buf); while (len > 0) { if (ndef_parse_record(data, len, &record) < 0) { @@ -103,13 +99,14 @@ static struct wpabuf * ndef_build_record(u8 flags, void *type, u8 type_length, void *id, - u8 id_length, void *payload, - u32 payload_length) + u8 id_length, + const struct wpabuf *payload) { struct wpabuf *record; size_t total_len; int short_record; u8 local_flag; + size_t payload_length = wpabuf_len(payload); short_record = payload_length < 256 ? 1 : 0; @@ -144,7 +141,7 @@ wpabuf_put_u8(record, id_length); wpabuf_put_data(record, type, type_length); wpabuf_put_data(record, id, id_length); - wpabuf_put_data(record, payload, payload_length); + wpabuf_put_buf(record, payload); return record; } @@ -160,16 +157,40 @@ } -struct wpabuf * ndef_parse_wifi(struct wpabuf *buf) +struct wpabuf * ndef_parse_wifi(const struct wpabuf *buf) { return ndef_parse_records(buf, wifi_filter); } -struct wpabuf * ndef_build_wifi(struct wpabuf *buf) +struct wpabuf * ndef_build_wifi(const struct wpabuf *buf) { return ndef_build_record(FLAG_MESSAGE_BEGIN | FLAG_MESSAGE_END | FLAG_TNF_RFC2046, wifi_handover_type, - os_strlen(wifi_handover_type), NULL, 0, - wpabuf_mhead(buf), wpabuf_len(buf)); + os_strlen(wifi_handover_type), NULL, 0, buf); +} + + +static int p2p_filter(struct ndef_record *record) +{ + if (record->type_length != os_strlen(p2p_handover_type)) + return 0; + if (os_memcmp(record->type, p2p_handover_type, + os_strlen(p2p_handover_type)) != 0) + return 0; + return 1; +} + + +struct wpabuf * ndef_parse_p2p(const struct wpabuf *buf) +{ + return ndef_parse_records(buf, p2p_filter); +} + + +struct wpabuf * ndef_build_p2p(const struct wpabuf *buf) +{ + return ndef_build_record(FLAG_MESSAGE_BEGIN | FLAG_MESSAGE_END | + FLAG_TNF_RFC2046, p2p_handover_type, + os_strlen(p2p_handover_type), NULL, 0, buf); } diff -Nru wpa-1.0/src/wps/wps_attr_build.c wpa-2.1/src/wps/wps_attr_build.c --- wpa-1.0/src/wps/wps_attr_build.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/wps/wps_attr_build.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * Wi-Fi Protected Setup - attribute building * Copyright (c) 2008, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -30,15 +24,46 @@ wpa_printf(MSG_DEBUG, "WPS: * Public Key"); wpabuf_free(wps->dh_privkey); - if (wps->dev_pw_id != DEV_PW_DEFAULT && wps->wps->dh_privkey) { + wps->dh_privkey = NULL; + if (wps->dev_pw_id != DEV_PW_DEFAULT && wps->wps->dh_privkey && + wps->wps->dh_ctx) { wpa_printf(MSG_DEBUG, "WPS: Using pre-configured DH keys"); + if (wps->wps->dh_pubkey == NULL) { + wpa_printf(MSG_DEBUG, + "WPS: wps->wps->dh_pubkey == NULL"); + return -1; + } wps->dh_privkey = wpabuf_dup(wps->wps->dh_privkey); wps->dh_ctx = wps->wps->dh_ctx; wps->wps->dh_ctx = NULL; pubkey = wpabuf_dup(wps->wps->dh_pubkey); +#ifdef CONFIG_WPS_NFC + } else if ((wps->dev_pw_id >= 0x10 || + wps->dev_pw_id == DEV_PW_NFC_CONNECTION_HANDOVER) && + (wps->wps->ap || + (wps->wps->ap_nfc_dh_pubkey && + wps->wps->ap_nfc_dev_pw_id == + DEV_PW_NFC_CONNECTION_HANDOVER && + wps->dev_pw_id == DEV_PW_NFC_CONNECTION_HANDOVER)) && + (wps->dev_pw_id == wps->wps->ap_nfc_dev_pw_id || + wps->wps->ap_nfc_dh_pubkey)) { + wpa_printf(MSG_DEBUG, "WPS: Using NFC password token DH keys"); + if (wps->wps->ap_nfc_dh_privkey == NULL) { + wpa_printf(MSG_DEBUG, + "WPS: wps->wps->ap_nfc_dh_privkey == NULL"); + return -1; + } + if (wps->wps->ap_nfc_dh_pubkey == NULL) { + wpa_printf(MSG_DEBUG, + "WPS: wps->wps->ap_nfc_dh_pubkey == NULL"); + return -1; + } + wps->dh_privkey = wpabuf_dup(wps->wps->ap_nfc_dh_privkey); + pubkey = wpabuf_dup(wps->wps->ap_nfc_dh_pubkey); + wps->dh_ctx = dh5_init_fixed(wps->dh_privkey, pubkey); +#endif /* CONFIG_WPS_NFC */ } else { wpa_printf(MSG_DEBUG, "WPS: Generate new DH keys"); - wps->dh_privkey = NULL; dh5_free(wps->dh_ctx); wps->dh_ctx = dh5_init(&wps->dh_privkey, &pubkey); pubkey = wpabuf_zeropad(pubkey, 192); @@ -100,6 +125,8 @@ int wps_build_uuid_e(struct wpabuf *msg, const u8 *uuid) { + if (wpabuf_tailroom(msg) < 4 + WPS_UUID_LEN) + return -1; wpa_printf(MSG_DEBUG, "WPS: * UUID-E"); wpabuf_put_be16(msg, ATTR_UUID_E); wpabuf_put_be16(msg, WPS_UUID_LEN); @@ -165,6 +192,8 @@ * backwards compatibility reasons. The real version negotiation is * done with Version2. */ + if (wpabuf_tailroom(msg) < 5) + return -1; wpa_printf(MSG_DEBUG, "WPS: * Version (hardcoded 0x10)"); wpabuf_put_be16(msg, ATTR_VERSION); wpabuf_put_be16(msg, 1); @@ -179,6 +208,15 @@ #ifdef CONFIG_WPS2 u8 *len; +#ifdef CONFIG_WPS_TESTING + if (WPS_VERSION == 0x10) + return 0; +#endif /* CONFIG_WPS_TESTING */ + + if (wpabuf_tailroom(msg) < + 7 + 3 + (req_to_enroll ? 3 : 0) + + (auth_macs ? 2 + auth_macs_count * ETH_ALEN : 0)) + return -1; wpabuf_put_be16(msg, ATTR_VENDOR_EXT); len = wpabuf_put(msg, 2); /* to be filled */ wpabuf_put_be24(msg, WPS_VENDOR_ID_WFA); @@ -212,6 +250,8 @@ #ifdef CONFIG_WPS_TESTING if (WPS_VERSION > 0x20) { + if (wpabuf_tailroom(msg) < 5) + return -1; wpa_printf(MSG_DEBUG, "WPS: * Extensibility Testing - extra " "attribute"); wpabuf_put_be16(msg, ATTR_EXTENSIBILITY_TEST); @@ -346,44 +386,39 @@ #ifdef CONFIG_WPS_OOB -int wps_build_oob_dev_password(struct wpabuf *msg, struct wps_context *wps) +int wps_build_oob_dev_pw(struct wpabuf *msg, u16 dev_pw_id, + const struct wpabuf *pubkey, const u8 *dev_pw, + size_t dev_pw_len) { size_t hash_len; const u8 *addr[1]; u8 pubkey_hash[WPS_HASH_LEN]; - u8 dev_password_bin[WPS_OOB_DEVICE_PASSWORD_LEN]; - - wpa_printf(MSG_DEBUG, "WPS: * OOB Device Password"); - addr[0] = wpabuf_head(wps->dh_pubkey); - hash_len = wpabuf_len(wps->dh_pubkey); + wpa_printf(MSG_DEBUG, "WPS: * OOB Device Password (dev_pw_id=%u)", + dev_pw_id); + addr[0] = wpabuf_head(pubkey); + hash_len = wpabuf_len(pubkey); sha256_vector(1, addr, &hash_len, pubkey_hash); - - if (os_get_random((u8 *) &wps->oob_dev_pw_id, sizeof(u16)) < 0) { - wpa_printf(MSG_ERROR, "WPS: device password id " - "generation error"); - return -1; - } - wps->oob_dev_pw_id |= 0x0010; - - if (random_get_bytes(dev_password_bin, WPS_OOB_DEVICE_PASSWORD_LEN) < - 0) { - wpa_printf(MSG_ERROR, "WPS: OOB device password " - "generation error"); - return -1; +#ifdef CONFIG_WPS_TESTING + if (wps_corrupt_pkhash) { + wpa_hexdump(MSG_DEBUG, "WPS: Real Public Key Hash", + pubkey_hash, WPS_OOB_PUBKEY_HASH_LEN); + wpa_printf(MSG_INFO, "WPS: Testing - corrupt public key hash"); + pubkey_hash[WPS_OOB_PUBKEY_HASH_LEN - 2]++; } +#endif /* CONFIG_WPS_TESTING */ wpabuf_put_be16(msg, ATTR_OOB_DEVICE_PASSWORD); - wpabuf_put_be16(msg, WPS_OOB_DEVICE_PASSWORD_ATTR_LEN); + wpabuf_put_be16(msg, WPS_OOB_PUBKEY_HASH_LEN + 2 + dev_pw_len); + wpa_hexdump(MSG_DEBUG, "WPS: Public Key Hash", + pubkey_hash, WPS_OOB_PUBKEY_HASH_LEN); wpabuf_put_data(msg, pubkey_hash, WPS_OOB_PUBKEY_HASH_LEN); - wpabuf_put_be16(msg, wps->oob_dev_pw_id); - wpabuf_put_data(msg, dev_password_bin, WPS_OOB_DEVICE_PASSWORD_LEN); - - wpa_snprintf_hex_uppercase( - wpabuf_put(wps->oob_conf.dev_password, - wpabuf_size(wps->oob_conf.dev_password)), - wpabuf_size(wps->oob_conf.dev_password), - dev_password_bin, WPS_OOB_DEVICE_PASSWORD_LEN); + wpabuf_put_be16(msg, dev_pw_id); + if (dev_pw) { + wpa_hexdump_key(MSG_DEBUG, "WPS: OOB Device Password", + dev_pw, dev_pw_len); + wpabuf_put_data(msg, dev_pw, dev_pw_len); + } return 0; } @@ -420,3 +455,34 @@ return ie; } + + +int wps_build_mac_addr(struct wpabuf *msg, const u8 *addr) +{ + wpa_printf(MSG_DEBUG, "WPS: * MAC Address (" MACSTR ")", + MAC2STR(addr)); + wpabuf_put_be16(msg, ATTR_MAC_ADDR); + wpabuf_put_be16(msg, ETH_ALEN); + wpabuf_put_data(msg, addr, ETH_ALEN); + return 0; +} + + +int wps_build_rf_bands_attr(struct wpabuf *msg, u8 rf_bands) +{ + wpa_printf(MSG_DEBUG, "WPS: * RF Bands (%x)", rf_bands); + wpabuf_put_be16(msg, ATTR_RF_BANDS); + wpabuf_put_be16(msg, 1); + wpabuf_put_u8(msg, rf_bands); + return 0; +} + + +int wps_build_ap_channel(struct wpabuf *msg, u16 ap_channel) +{ + wpa_printf(MSG_DEBUG, "WPS: * AP Channel (%u)", ap_channel); + wpabuf_put_be16(msg, ATTR_AP_CHANNEL); + wpabuf_put_be16(msg, 2); + wpabuf_put_be16(msg, ap_channel); + return 0; +} diff -Nru wpa-1.0/src/wps/wps_attr_parse.c wpa-2.1/src/wps/wps_attr_parse.c --- wpa-1.0/src/wps/wps_attr_parse.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/wps/wps_attr_parse.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,20 +2,15 @@ * Wi-Fi Protected Setup - attribute parsing * Copyright (c) 2008, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" #include "common.h" -#include "wps_i.h" +#include "wps_defs.h" +#include "wps_attr_parse.h" #ifndef CONFIG_WPS_STRICT #define WPS_WORKAROUNDS @@ -268,12 +263,19 @@ attr->dev_password_id = pos; break; case ATTR_OOB_DEVICE_PASSWORD: - if (len != WPS_OOB_DEVICE_PASSWORD_ATTR_LEN) { + if (len < WPS_OOB_PUBKEY_HASH_LEN + 2 || + len > WPS_OOB_PUBKEY_HASH_LEN + 2 + + WPS_OOB_DEVICE_PASSWORD_LEN || + (len < WPS_OOB_PUBKEY_HASH_LEN + 2 + + WPS_OOB_DEVICE_PASSWORD_MIN_LEN && + WPA_GET_BE16(pos + WPS_OOB_PUBKEY_HASH_LEN) != + DEV_PW_NFC_CONNECTION_HANDOVER)) { wpa_printf(MSG_DEBUG, "WPS: Invalid OOB Device " "Password length %u", len); return -1; } attr->oob_dev_password = pos; + attr->oob_dev_password_len = len; break; case ATTR_OS_VERSION: if (len != 4) { @@ -543,6 +545,14 @@ if (wps_parse_vendor_ext(attr, pos, len) < 0) return -1; break; + case ATTR_AP_CHANNEL: + if (len != 2) { + wpa_printf(MSG_DEBUG, "WPS: Invalid AP Channel " + "length %u", len); + return -1; + } + attr->ap_channel = pos; + break; default: wpa_printf(MSG_DEBUG, "WPS: Unsupported attribute type 0x%x " "len=%u", type, len); diff -Nru wpa-1.0/src/wps/wps_attr_parse.h wpa-2.1/src/wps/wps_attr_parse.h --- wpa-1.0/src/wps/wps_attr_parse.h 1970-01-01 00:00:00.000000000 +0000 +++ wpa-2.1/src/wps/wps_attr_parse.h 2014-02-04 11:23:35.000000000 +0000 @@ -0,0 +1,108 @@ +/* + * Wi-Fi Protected Setup - attribute parsing + * Copyright (c) 2008-2012, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef WPS_ATTR_PARSE_H +#define WPS_ATTR_PARSE_H + +#include "wps.h" + +struct wps_parse_attr { + /* fixed length fields */ + const u8 *version; /* 1 octet */ + const u8 *version2; /* 1 octet */ + const u8 *msg_type; /* 1 octet */ + const u8 *enrollee_nonce; /* WPS_NONCE_LEN (16) octets */ + const u8 *registrar_nonce; /* WPS_NONCE_LEN (16) octets */ + const u8 *uuid_r; /* WPS_UUID_LEN (16) octets */ + const u8 *uuid_e; /* WPS_UUID_LEN (16) octets */ + const u8 *auth_type_flags; /* 2 octets */ + const u8 *encr_type_flags; /* 2 octets */ + const u8 *conn_type_flags; /* 1 octet */ + const u8 *config_methods; /* 2 octets */ + const u8 *sel_reg_config_methods; /* 2 octets */ + const u8 *primary_dev_type; /* 8 octets */ + const u8 *rf_bands; /* 1 octet */ + const u8 *assoc_state; /* 2 octets */ + const u8 *config_error; /* 2 octets */ + const u8 *dev_password_id; /* 2 octets */ + const u8 *os_version; /* 4 octets */ + const u8 *wps_state; /* 1 octet */ + const u8 *authenticator; /* WPS_AUTHENTICATOR_LEN (8) octets */ + const u8 *r_hash1; /* WPS_HASH_LEN (32) octets */ + const u8 *r_hash2; /* WPS_HASH_LEN (32) octets */ + const u8 *e_hash1; /* WPS_HASH_LEN (32) octets */ + const u8 *e_hash2; /* WPS_HASH_LEN (32) octets */ + const u8 *r_snonce1; /* WPS_SECRET_NONCE_LEN (16) octets */ + const u8 *r_snonce2; /* WPS_SECRET_NONCE_LEN (16) octets */ + const u8 *e_snonce1; /* WPS_SECRET_NONCE_LEN (16) octets */ + const u8 *e_snonce2; /* WPS_SECRET_NONCE_LEN (16) octets */ + const u8 *key_wrap_auth; /* WPS_KWA_LEN (8) octets */ + const u8 *auth_type; /* 2 octets */ + const u8 *encr_type; /* 2 octets */ + const u8 *network_idx; /* 1 octet */ + const u8 *network_key_idx; /* 1 octet */ + const u8 *mac_addr; /* ETH_ALEN (6) octets */ + const u8 *key_prov_auto; /* 1 octet (Bool) */ + const u8 *dot1x_enabled; /* 1 octet (Bool) */ + const u8 *selected_registrar; /* 1 octet (Bool) */ + const u8 *request_type; /* 1 octet */ + const u8 *response_type; /* 1 octet */ + const u8 *ap_setup_locked; /* 1 octet */ + const u8 *settings_delay_time; /* 1 octet */ + const u8 *network_key_shareable; /* 1 octet (Bool) */ + const u8 *request_to_enroll; /* 1 octet (Bool) */ + const u8 *ap_channel; /* 2 octets */ + + /* variable length fields */ + const u8 *manufacturer; + size_t manufacturer_len; + const u8 *model_name; + size_t model_name_len; + const u8 *model_number; + size_t model_number_len; + const u8 *serial_number; + size_t serial_number_len; + const u8 *dev_name; + size_t dev_name_len; + const u8 *public_key; + size_t public_key_len; + const u8 *encr_settings; + size_t encr_settings_len; + const u8 *ssid; /* <= 32 octets */ + size_t ssid_len; + const u8 *network_key; /* <= 64 octets */ + size_t network_key_len; + const u8 *eap_type; /* <= 8 octets */ + size_t eap_type_len; + const u8 *eap_identity; /* <= 64 octets */ + size_t eap_identity_len; + const u8 *authorized_macs; /* <= 30 octets */ + size_t authorized_macs_len; + const u8 *sec_dev_type_list; /* <= 128 octets */ + size_t sec_dev_type_list_len; + const u8 *oob_dev_password; /* 38..54 octets */ + size_t oob_dev_password_len; + + /* attributes that can occur multiple times */ +#define MAX_CRED_COUNT 10 + const u8 *cred[MAX_CRED_COUNT]; + size_t cred_len[MAX_CRED_COUNT]; + size_t num_cred; + +#define MAX_REQ_DEV_TYPE_COUNT 10 + const u8 *req_dev_type[MAX_REQ_DEV_TYPE_COUNT]; + size_t num_req_dev_type; + + const u8 *vendor_ext[MAX_WPS_PARSE_VENDOR_EXT]; + size_t vendor_ext_len[MAX_WPS_PARSE_VENDOR_EXT]; + size_t num_vendor_ext; +}; + +int wps_parse_msg(const struct wpabuf *msg, struct wps_parse_attr *attr); + +#endif /* WPS_ATTR_PARSE_H */ diff -Nru wpa-1.0/src/wps/wps_attr_process.c wpa-2.1/src/wps/wps_attr_process.c --- wpa-1.0/src/wps/wps_attr_process.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/wps/wps_attr_process.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * Wi-Fi Protected Setup - attribute processing * Copyright (c) 2008, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -264,6 +258,19 @@ } +static int wps_process_cred_ap_channel(struct wps_credential *cred, + const u8 *ap_channel) +{ + if (ap_channel == NULL) + return 0; /* optional attribute */ + + cred->ap_channel = WPA_GET_BE16(ap_channel); + wpa_printf(MSG_DEBUG, "WPS: AP Channel: %u", cred->ap_channel); + + return 0; +} + + static int wps_workaround_cred_key(struct wps_credential *cred) { if (cred->auth_type & (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK) && @@ -309,7 +316,8 @@ wps_process_cred_eap_identity(cred, attr->eap_identity, attr->eap_identity_len) || wps_process_cred_key_prov_auto(cred, attr->key_prov_auto) || - wps_process_cred_802_1x_enabled(cred, attr->dot1x_enabled)) + wps_process_cred_802_1x_enabled(cred, attr->dot1x_enabled) || + wps_process_cred_ap_channel(cred, attr->ap_channel)) return -1; return wps_workaround_cred_key(cred); diff -Nru wpa-1.0/src/wps/wps.c wpa-2.1/src/wps/wps.c --- wpa-1.0/src/wps/wps.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/wps/wps.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * Wi-Fi Protected Setup * Copyright (c) 2007-2009, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -24,6 +18,7 @@ #ifdef CONFIG_WPS_TESTING int wps_version_number = 0x20; int wps_testing_dummy_cred = 0; +int wps_corrupt_pkhash = 0; #endif /* CONFIG_WPS_TESTING */ @@ -51,8 +46,7 @@ os_memcpy(data->uuid_e, cfg->wps->uuid, WPS_UUID_LEN); } if (cfg->pin) { - data->dev_pw_id = data->wps->oob_dev_pw_id == 0 ? - cfg->dev_pw_id : data->wps->oob_dev_pw_id; + data->dev_pw_id = cfg->dev_pw_id; data->dev_password = os_malloc(cfg->pin_len); if (data->dev_password == NULL) { os_free(data); @@ -60,7 +54,36 @@ } os_memcpy(data->dev_password, cfg->pin, cfg->pin_len); data->dev_password_len = cfg->pin_len; + wpa_hexdump_key(MSG_DEBUG, "WPS: AP PIN dev_password", + data->dev_password, data->dev_password_len); + } + +#ifdef CONFIG_WPS_NFC + if (cfg->pin == NULL && + cfg->dev_pw_id == DEV_PW_NFC_CONNECTION_HANDOVER) + data->dev_pw_id = cfg->dev_pw_id; + + if (cfg->wps->ap && !cfg->registrar && cfg->wps->ap_nfc_dev_pw_id) { + /* Keep AP PIN as alternative Device Password */ + data->alt_dev_pw_id = data->dev_pw_id; + data->alt_dev_password = data->dev_password; + data->alt_dev_password_len = data->dev_password_len; + + data->dev_pw_id = cfg->wps->ap_nfc_dev_pw_id; + data->dev_password = + os_malloc(wpabuf_len(cfg->wps->ap_nfc_dev_pw)); + if (data->dev_password == NULL) { + os_free(data); + return NULL; + } + os_memcpy(data->dev_password, + wpabuf_head(cfg->wps->ap_nfc_dev_pw), + wpabuf_len(cfg->wps->ap_nfc_dev_pw)); + data->dev_password_len = wpabuf_len(cfg->wps->ap_nfc_dev_pw); + wpa_hexdump_key(MSG_DEBUG, "WPS: NFC dev_password", + data->dev_password, data->dev_password_len); } +#endif /* CONFIG_WPS_NFC */ data->pbc = cfg->pbc; if (cfg->pbc) { @@ -99,6 +122,7 @@ data->new_ap_settings = os_malloc(sizeof(*data->new_ap_settings)); if (data->new_ap_settings == NULL) { + os_free(data->dev_password); os_free(data); return NULL; } @@ -114,6 +138,12 @@ data->use_psk_key = cfg->use_psk_key; data->pbc_in_m1 = cfg->pbc_in_m1; + if (cfg->peer_pubkey_hash) { + os_memcpy(data->peer_pubkey_hash, cfg->peer_pubkey_hash, + WPS_OOB_PUBKEY_HASH_LEN); + data->peer_pubkey_hash_set = 1; + } + return data; } @@ -124,6 +154,12 @@ */ void wps_deinit(struct wps_data *data) { +#ifdef CONFIG_WPS_NFC + if (data->registrar && data->nfc_pw_token) + wps_registrar_remove_nfc_pw_token(data->wps->registrar, + data->nfc_pw_token); +#endif /* CONFIG_WPS_NFC */ + if (data->wps_pin_revealed) { wpa_printf(MSG_DEBUG, "WPS: Full PIN information revealed and " "negotiation failed"); @@ -138,6 +174,7 @@ wpabuf_free(data->dh_pubkey_r); wpabuf_free(data->last_msg); os_free(data->dev_password); + os_free(data->alt_dev_password); os_free(data->new_psk); wps_device_data_free(&data->peer_dev); os_free(data->new_ap_settings); @@ -269,7 +306,8 @@ * @msg: WPS IE contents from Beacon or Probe Response frame * @addr: MAC address to search for * @ver1_compat: Whether to use version 1 compatibility mode - * Returns: 1 if address is authorized, 0 if not + * Returns: 2 if the specified address is explicit authorized, 1 if address is + * authorized (broadcast), 0 if not */ int wps_is_addr_authorized(const struct wpabuf *msg, const u8 *addr, int ver1_compat) @@ -295,8 +333,9 @@ pos = attr.authorized_macs; for (i = 0; i < attr.authorized_macs_len / ETH_ALEN; i++) { - if (os_memcmp(pos, addr, ETH_ALEN) == 0 || - os_memcmp(pos, bcast, ETH_ALEN) == 0) + if (os_memcmp(pos, addr, ETH_ALEN) == 0) + return 2; + if (os_memcmp(pos, bcast, ETH_ALEN) == 0) return 1; pos += ETH_ALEN; } @@ -437,7 +476,8 @@ /** * wps_build_probe_req_ie - Build WPS IE for Probe Request - * @pbc: Whether searching for PBC mode APs + * @pw_id: Password ID (DEV_PW_PUSHBUTTON for active PBC and DEV_PW_DEFAULT for + * most other use cases) * @dev: Device attributes * @uuid: Own UUID * @req_type: Value for Request Type attribute @@ -448,7 +488,7 @@ * * The caller is responsible for freeing the buffer. */ -struct wpabuf * wps_build_probe_req_ie(int pbc, struct wps_device_data *dev, +struct wpabuf * wps_build_probe_req_ie(u16 pw_id, struct wps_device_data *dev, const u8 *uuid, enum wps_request_type req_type, unsigned int num_req_dev_types, @@ -467,11 +507,10 @@ wps_build_config_methods(ie, dev->config_methods) || wps_build_uuid_e(ie, uuid) || wps_build_primary_dev_type(dev, ie) || - wps_build_rf_bands(dev, ie) || + wps_build_rf_bands(dev, ie, 0) || wps_build_assoc_state(NULL, ie) || wps_build_config_error(ie, WPS_CFG_NO_ERROR) || - wps_build_dev_password_id(ie, pbc ? DEV_PW_PUSHBUTTON : - DEV_PW_DEFAULT) || + wps_build_dev_password_id(ie, pw_id) || #ifdef CONFIG_WPS2 wps_build_manufacturer(dev, ie) || wps_build_model_name(dev, ie) || @@ -611,3 +650,20 @@ return pos - buf; } + + +const char * wps_ei_str(enum wps_error_indication ei) +{ + switch (ei) { + case WPS_EI_NO_ERROR: + return "No Error"; + case WPS_EI_SECURITY_TKIP_ONLY_PROHIBITED: + return "TKIP Only Prohibited"; + case WPS_EI_SECURITY_WEP_PROHIBITED: + return "WEP Prohibited"; + case WPS_EI_AUTH_FAILURE: + return "Authentication Failure"; + default: + return "Unknown"; + } +} diff -Nru wpa-1.0/src/wps/wps_common.c wpa-2.1/src/wps/wps_common.c --- wpa-1.0/src/wps/wps_common.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/wps/wps_common.c 2014-02-04 11:23:35.000000000 +0000 @@ -1,20 +1,16 @@ /* * Wi-Fi Protected Setup - common functionality - * Copyright (c) 2008-2009, Jouni Malinen + * Copyright (c) 2008-2012, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" #include "common.h" +#include "common/defs.h" +#include "common/ieee802_11_common.h" #include "crypto/aes_wrap.h" #include "crypto/crypto.h" #include "crypto/dh_group5.h" @@ -256,8 +252,24 @@ } +int wps_pin_str_valid(const char *pin) +{ + const char *p; + size_t len; + + p = pin; + while (*p >= '0' && *p <= '9') + p++; + if (*p != '\0') + return 0; + + len = p - pin; + return len == 4 || len == 8; +} + + void wps_fail_event(struct wps_context *wps, enum wps_msg_type msg, - u16 config_error, u16 error_indication) + u16 config_error, u16 error_indication, const u8 *mac_addr) { union wps_event_data data; @@ -268,20 +280,26 @@ data.fail.msg = msg; data.fail.config_error = config_error; data.fail.error_indication = error_indication; + os_memcpy(data.fail.peer_macaddr, mac_addr, ETH_ALEN); wps->event_cb(wps->cb_ctx, WPS_EV_FAIL, &data); } -void wps_success_event(struct wps_context *wps) +void wps_success_event(struct wps_context *wps, const u8 *mac_addr) { + union wps_event_data data; + if (wps->event_cb == NULL) return; - wps->event_cb(wps->cb_ctx, WPS_EV_SUCCESS, NULL); + os_memset(&data, 0, sizeof(data)); + os_memcpy(data.success.peer_macaddr, mac_addr, ETH_ALEN); + wps->event_cb(wps->cb_ctx, WPS_EV_SUCCESS, &data); } -void wps_pwd_auth_fail_event(struct wps_context *wps, int enrollee, int part) +void wps_pwd_auth_fail_event(struct wps_context *wps, int enrollee, int part, + const u8 *mac_addr) { union wps_event_data data; @@ -291,6 +309,7 @@ os_memset(&data, 0, sizeof(data)); data.pwd_auth_fail.enrollee = enrollee; data.pwd_auth_fail.part = part; + os_memcpy(data.pwd_auth_fail.peer_macaddr, mac_addr, ETH_ALEN); wps->event_cb(wps->cb_ctx, WPS_EV_PWD_AUTH_FAIL, &data); } @@ -313,9 +332,28 @@ } +void wps_pbc_active_event(struct wps_context *wps) +{ + if (wps->event_cb == NULL) + return; + + wps->event_cb(wps->cb_ctx, WPS_EV_PBC_ACTIVE, NULL); +} + + +void wps_pbc_disable_event(struct wps_context *wps) +{ + if (wps->event_cb == NULL) + return; + + wps->event_cb(wps->cb_ctx, WPS_EV_PBC_DISABLE, NULL); +} + + #ifdef CONFIG_WPS_OOB -static struct wpabuf * wps_get_oob_cred(struct wps_context *wps) +struct wpabuf * wps_get_oob_cred(struct wps_context *wps, int rf_band, + int channel) { struct wps_data data; struct wpabuf *plain; @@ -331,43 +369,60 @@ data.wps = wps; data.auth_type = wps->auth_types; data.encr_type = wps->encr_types; - if (wps_build_version(plain) || - wps_build_cred(&data, plain) || + if (wps_build_cred(&data, plain) || + (rf_band && wps_build_rf_bands_attr(plain, rf_band)) || + (channel && wps_build_ap_channel(plain, channel)) || + wps_build_mac_addr(plain, wps->dev.mac_addr) || wps_build_wfa_ext(plain, 0, NULL, 0)) { + os_free(data.new_psk); wpabuf_free(plain); return NULL; } + if (wps->wps_state == WPS_STATE_NOT_CONFIGURED && data.new_psk && + wps->ap) { + struct wps_credential cred; + + wpa_printf(MSG_DEBUG, "WPS: Moving to Configured state based " + "on credential token generation"); + + os_memset(&cred, 0, sizeof(cred)); + os_memcpy(cred.ssid, wps->ssid, wps->ssid_len); + cred.ssid_len = wps->ssid_len; + cred.auth_type = WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK; + cred.encr_type = WPS_ENCR_TKIP | WPS_ENCR_AES; + os_memcpy(cred.key, data.new_psk, data.new_psk_len); + cred.key_len = data.new_psk_len; + + wps->wps_state = WPS_STATE_CONFIGURED; + wpa_hexdump_ascii_key(MSG_DEBUG, + "WPS: Generated random passphrase", + data.new_psk, data.new_psk_len); + if (wps->cred_cb) + wps->cred_cb(wps->cb_ctx, &cred); + } + + os_free(data.new_psk); + return plain; } -static struct wpabuf * wps_get_oob_dev_pwd(struct wps_context *wps) +struct wpabuf * wps_build_nfc_pw_token(u16 dev_pw_id, + const struct wpabuf *pubkey, + const struct wpabuf *dev_pw) { struct wpabuf *data; - data = wpabuf_alloc(9 + WPS_OOB_DEVICE_PASSWORD_ATTR_LEN); - if (data == NULL) { - wpa_printf(MSG_ERROR, "WPS: Failed to allocate memory for OOB " - "device password attribute"); + data = wpabuf_alloc(200); + if (data == NULL) return NULL; - } - wpabuf_free(wps->oob_conf.dev_password); - wps->oob_conf.dev_password = - wpabuf_alloc(WPS_OOB_DEVICE_PASSWORD_LEN * 2 + 1); - if (wps->oob_conf.dev_password == NULL) { - wpa_printf(MSG_ERROR, "WPS: Failed to allocate memory for OOB " - "device password"); - wpabuf_free(data); - return NULL; - } - - if (wps_build_version(data) || - wps_build_oob_dev_password(data, wps) || + if (wps_build_oob_dev_pw(data, dev_pw_id, pubkey, + wpabuf_head(dev_pw), wpabuf_len(dev_pw)) || wps_build_wfa_ext(data, 0, NULL, 0)) { - wpa_printf(MSG_ERROR, "WPS: Build OOB device password " - "attribute error"); + wpa_printf(MSG_ERROR, "WPS: Failed to build NFC password " + "token"); wpabuf_free(data); return NULL; } @@ -376,66 +431,17 @@ } -static int wps_parse_oob_dev_pwd(struct wps_context *wps, - struct wpabuf *data) -{ - struct oob_conf_data *oob_conf = &wps->oob_conf; - struct wps_parse_attr attr; - const u8 *pos; - - if (wps_parse_msg(data, &attr) < 0 || - attr.oob_dev_password == NULL) { - wpa_printf(MSG_ERROR, "WPS: OOB device password not found"); - return -1; - } - - pos = attr.oob_dev_password; - - oob_conf->pubkey_hash = - wpabuf_alloc_copy(pos, WPS_OOB_PUBKEY_HASH_LEN); - if (oob_conf->pubkey_hash == NULL) { - wpa_printf(MSG_ERROR, "WPS: Failed to allocate memory for OOB " - "public key hash"); - return -1; - } - pos += WPS_OOB_PUBKEY_HASH_LEN; - - wps->oob_dev_pw_id = WPA_GET_BE16(pos); - pos += sizeof(wps->oob_dev_pw_id); - - oob_conf->dev_password = - wpabuf_alloc(WPS_OOB_DEVICE_PASSWORD_LEN * 2 + 1); - if (oob_conf->dev_password == NULL) { - wpa_printf(MSG_ERROR, "WPS: Failed to allocate memory for OOB " - "device password"); - return -1; - } - wpa_snprintf_hex_uppercase(wpabuf_put(oob_conf->dev_password, - wpabuf_size(oob_conf->dev_password)), - wpabuf_size(oob_conf->dev_password), pos, - WPS_OOB_DEVICE_PASSWORD_LEN); - - return 0; -} - - -static int wps_parse_oob_cred(struct wps_context *wps, struct wpabuf *data) +int wps_oob_use_cred(struct wps_context *wps, struct wps_parse_attr *attr) { struct wpabuf msg; - struct wps_parse_attr attr; size_t i; - if (wps_parse_msg(data, &attr) < 0 || attr.num_cred <= 0) { - wpa_printf(MSG_ERROR, "WPS: OOB credential not found"); - return -1; - } - - for (i = 0; i < attr.num_cred; i++) { + for (i = 0; i < attr->num_cred; i++) { struct wps_credential local_cred; struct wps_parse_attr cattr; os_memset(&local_cred, 0, sizeof(local_cred)); - wpabuf_set(&msg, attr.cred[i], attr.cred_len[i]); + wpabuf_set(&msg, attr->cred[i], attr->cred_len[i]); if (wps_parse_msg(&msg, &cattr) < 0 || wps_process_cred(&cattr, &local_cred)) { wpa_printf(MSG_ERROR, "WPS: Failed to parse OOB " @@ -449,94 +455,6 @@ } -int wps_process_oob(struct wps_context *wps, struct oob_device_data *oob_dev, - int registrar) -{ - struct wpabuf *data; - int ret, write_f, oob_method = wps->oob_conf.oob_method; - void *oob_priv; - - write_f = oob_method == OOB_METHOD_DEV_PWD_E ? !registrar : registrar; - - oob_priv = oob_dev->init_func(wps, oob_dev, registrar); - if (oob_priv == NULL) { - wpa_printf(MSG_ERROR, "WPS: Failed to initialize OOB device"); - return -1; - } - - if (write_f) { - if (oob_method == OOB_METHOD_CRED) - data = wps_get_oob_cred(wps); - else - data = wps_get_oob_dev_pwd(wps); - - ret = 0; - if (data == NULL || oob_dev->write_func(oob_priv, data) < 0) - ret = -1; - } else { - data = oob_dev->read_func(oob_priv); - if (data == NULL) - ret = -1; - else { - if (oob_method == OOB_METHOD_CRED) - ret = wps_parse_oob_cred(wps, data); - else - ret = wps_parse_oob_dev_pwd(wps, data); - } - } - wpabuf_free(data); - oob_dev->deinit_func(oob_priv); - - if (ret < 0) { - wpa_printf(MSG_ERROR, "WPS: Failed to process OOB data"); - return -1; - } - - return 0; -} - - -struct oob_device_data * wps_get_oob_device(char *device_type) -{ -#ifdef CONFIG_WPS_UFD - if (os_strstr(device_type, "ufd") != NULL) - return &oob_ufd_device_data; -#endif /* CONFIG_WPS_UFD */ -#ifdef CONFIG_WPS_NFC - if (os_strstr(device_type, "nfc") != NULL) - return &oob_nfc_device_data; -#endif /* CONFIG_WPS_NFC */ - - return NULL; -} - - -#ifdef CONFIG_WPS_NFC -struct oob_nfc_device_data * wps_get_oob_nfc_device(char *device_name) -{ - if (device_name == NULL) - return NULL; -#ifdef CONFIG_WPS_NFC_PN531 - if (os_strstr(device_name, "pn531") != NULL) - return &oob_nfc_pn531_device_data; -#endif /* CONFIG_WPS_NFC_PN531 */ - - return NULL; -} -#endif /* CONFIG_WPS_NFC */ - - -int wps_get_oob_method(char *method) -{ - if (os_strstr(method, "pin-e") != NULL) - return OOB_METHOD_DEV_PWD_E; - if (os_strstr(method, "pin-r") != NULL) - return OOB_METHOD_DEV_PWD_R; - if (os_strstr(method, "cred") != NULL) - return OOB_METHOD_CRED; - return OOB_METHOD_UNKNOWN; -} - #endif /* CONFIG_WPS_OOB */ @@ -616,15 +534,10 @@ #ifdef CONFIG_WPS2 methods |= WPS_CONFIG_VIRT_DISPLAY; #endif /* CONFIG_WPS2 */ -#ifdef CONFIG_WPS_UFD - methods |= WPS_CONFIG_USBA; -#endif /* CONFIG_WPS_UFD */ #ifdef CONFIG_WPS_NFC methods |= WPS_CONFIG_NFC_INTERFACE; #endif /* CONFIG_WPS_NFC */ } else { - if (os_strstr(str, "usba")) - methods |= WPS_CONFIG_USBA; if (os_strstr(str, "ethernet")) methods |= WPS_CONFIG_ETHERNET; if (os_strstr(str, "label")) @@ -702,3 +615,292 @@ return msg; } + + +#ifdef CONFIG_WPS_NFC + +struct wpabuf * wps_nfc_token_build(int ndef, int id, struct wpabuf *pubkey, + struct wpabuf *dev_pw) +{ + struct wpabuf *ret; + + if (pubkey == NULL || dev_pw == NULL) + return NULL; + + ret = wps_build_nfc_pw_token(id, pubkey, dev_pw); + if (ndef && ret) { + struct wpabuf *tmp; + tmp = ndef_build_wifi(ret); + wpabuf_free(ret); + if (tmp == NULL) + return NULL; + ret = tmp; + } + + return ret; +} + + +int wps_nfc_gen_dh(struct wpabuf **pubkey, struct wpabuf **privkey) +{ + struct wpabuf *priv = NULL, *pub = NULL; + void *dh_ctx; + + dh_ctx = dh5_init(&priv, &pub); + if (dh_ctx == NULL) + return -1; + pub = wpabuf_zeropad(pub, 192); + if (pub == NULL) { + wpabuf_free(priv); + return -1; + } + wpa_hexdump_buf(MSG_DEBUG, "WPS: Generated new DH pubkey", pub); + dh5_free(dh_ctx); + + wpabuf_free(*pubkey); + *pubkey = pub; + wpabuf_free(*privkey); + *privkey = priv; + + return 0; +} + + +struct wpabuf * wps_nfc_token_gen(int ndef, int *id, struct wpabuf **pubkey, + struct wpabuf **privkey, + struct wpabuf **dev_pw) +{ + struct wpabuf *pw; + u16 val; + + pw = wpabuf_alloc(WPS_OOB_DEVICE_PASSWORD_LEN); + if (pw == NULL) + return NULL; + + if (random_get_bytes(wpabuf_put(pw, WPS_OOB_DEVICE_PASSWORD_LEN), + WPS_OOB_DEVICE_PASSWORD_LEN) || + random_get_bytes((u8 *) &val, sizeof(val))) { + wpabuf_free(pw); + return NULL; + } + + if (wps_nfc_gen_dh(pubkey, privkey) < 0) { + wpabuf_free(pw); + return NULL; + } + + *id = 0x10 + val % 0xfff0; + wpabuf_free(*dev_pw); + *dev_pw = pw; + + return wps_nfc_token_build(ndef, *id, *pubkey, *dev_pw); +} + + +struct wpabuf * wps_build_nfc_handover_req(struct wps_context *ctx, + struct wpabuf *nfc_dh_pubkey) +{ + struct wpabuf *msg; + void *len; + + if (ctx == NULL) + return NULL; + + wpa_printf(MSG_DEBUG, "WPS: Building attributes for NFC connection " + "handover request"); + + if (nfc_dh_pubkey == NULL) { + wpa_printf(MSG_DEBUG, "WPS: No NFC OOB Device Password " + "configured"); + return NULL; + } + + msg = wpabuf_alloc(1000); + if (msg == NULL) + return msg; + len = wpabuf_put(msg, 2); + + if (wps_build_oob_dev_pw(msg, DEV_PW_NFC_CONNECTION_HANDOVER, + nfc_dh_pubkey, NULL, 0) || + wps_build_uuid_e(msg, ctx->uuid) || + wps_build_wfa_ext(msg, 0, NULL, 0)) { + wpabuf_free(msg); + return NULL; + } + + WPA_PUT_BE16(len, wpabuf_len(msg) - 2); + + return msg; +} + + +static int wps_build_ssid(struct wpabuf *msg, struct wps_context *wps) +{ + wpa_printf(MSG_DEBUG, "WPS: * SSID"); + wpa_hexdump_ascii(MSG_DEBUG, "WPS: SSID in Connection Handover Select", + wps->ssid, wps->ssid_len); + wpabuf_put_be16(msg, ATTR_SSID); + wpabuf_put_be16(msg, wps->ssid_len); + wpabuf_put_data(msg, wps->ssid, wps->ssid_len); + return 0; +} + + +static int wps_build_ap_freq(struct wpabuf *msg, int freq) +{ + enum hostapd_hw_mode mode; + u8 channel, rf_band; + u16 ap_channel; + + if (freq <= 0) + return 0; + + mode = ieee80211_freq_to_chan(freq, &channel); + if (mode == NUM_HOSTAPD_MODES) + return 0; /* Unknown channel */ + + if (mode == HOSTAPD_MODE_IEEE80211G || mode == HOSTAPD_MODE_IEEE80211B) + rf_band = WPS_RF_24GHZ; + else if (mode == HOSTAPD_MODE_IEEE80211A) + rf_band = WPS_RF_50GHZ; + else + return 0; /* Unknown band */ + ap_channel = channel; + + if (wps_build_rf_bands_attr(msg, rf_band) || + wps_build_ap_channel(msg, ap_channel)) + return -1; + + return 0; +} + + +struct wpabuf * wps_build_nfc_handover_sel(struct wps_context *ctx, + struct wpabuf *nfc_dh_pubkey, + const u8 *bssid, int freq) +{ + struct wpabuf *msg; + void *len; + + if (ctx == NULL) + return NULL; + + wpa_printf(MSG_DEBUG, "WPS: Building attributes for NFC connection " + "handover select"); + + if (nfc_dh_pubkey == NULL) { + wpa_printf(MSG_DEBUG, "WPS: No NFC OOB Device Password " + "configured"); + return NULL; + } + + msg = wpabuf_alloc(1000); + if (msg == NULL) + return msg; + len = wpabuf_put(msg, 2); + + if (wps_build_oob_dev_pw(msg, DEV_PW_NFC_CONNECTION_HANDOVER, + nfc_dh_pubkey, NULL, 0) || + wps_build_ssid(msg, ctx) || + wps_build_ap_freq(msg, freq) || + (bssid && wps_build_mac_addr(msg, bssid)) || + wps_build_wfa_ext(msg, 0, NULL, 0)) { + wpabuf_free(msg); + return NULL; + } + + WPA_PUT_BE16(len, wpabuf_len(msg) - 2); + + return msg; +} + + +struct wpabuf * wps_build_nfc_handover_req_p2p(struct wps_context *ctx, + struct wpabuf *nfc_dh_pubkey) +{ + struct wpabuf *msg; + + if (ctx == NULL) + return NULL; + + wpa_printf(MSG_DEBUG, "WPS: Building attributes for NFC connection " + "handover request (P2P)"); + + if (nfc_dh_pubkey == NULL) { + wpa_printf(MSG_DEBUG, "WPS: No NFC DH Public Key configured"); + return NULL; + } + + msg = wpabuf_alloc(1000); + if (msg == NULL) + return msg; + + if (wps_build_manufacturer(&ctx->dev, msg) || + wps_build_model_name(&ctx->dev, msg) || + wps_build_model_number(&ctx->dev, msg) || + wps_build_oob_dev_pw(msg, DEV_PW_NFC_CONNECTION_HANDOVER, + nfc_dh_pubkey, NULL, 0) || + wps_build_rf_bands(&ctx->dev, msg, 0) || + wps_build_serial_number(&ctx->dev, msg) || + wps_build_uuid_e(msg, ctx->uuid) || + wps_build_wfa_ext(msg, 0, NULL, 0)) { + wpabuf_free(msg); + return NULL; + } + + return msg; +} + + +struct wpabuf * wps_build_nfc_handover_sel_p2p(struct wps_context *ctx, + int nfc_dev_pw_id, + struct wpabuf *nfc_dh_pubkey, + struct wpabuf *nfc_dev_pw) +{ + struct wpabuf *msg; + const u8 *dev_pw; + size_t dev_pw_len; + + if (ctx == NULL) + return NULL; + + wpa_printf(MSG_DEBUG, "WPS: Building attributes for NFC connection " + "handover select (P2P)"); + + if (nfc_dh_pubkey == NULL || + (nfc_dev_pw_id != DEV_PW_NFC_CONNECTION_HANDOVER && + nfc_dev_pw == NULL)) { + wpa_printf(MSG_DEBUG, "WPS: No NFC OOB Device Password " + "configured"); + return NULL; + } + + msg = wpabuf_alloc(1000); + if (msg == NULL) + return msg; + + if (nfc_dev_pw) { + dev_pw = wpabuf_head(nfc_dev_pw); + dev_pw_len = wpabuf_len(nfc_dev_pw); + } else { + dev_pw = NULL; + dev_pw_len = 0; + } + + if (wps_build_manufacturer(&ctx->dev, msg) || + wps_build_model_name(&ctx->dev, msg) || + wps_build_model_number(&ctx->dev, msg) || + wps_build_oob_dev_pw(msg, nfc_dev_pw_id, nfc_dh_pubkey, + dev_pw, dev_pw_len) || + wps_build_rf_bands(&ctx->dev, msg, 0) || + wps_build_serial_number(&ctx->dev, msg) || + wps_build_uuid_e(msg, ctx->uuid) || + wps_build_wfa_ext(msg, 0, NULL, 0)) { + wpabuf_free(msg); + return NULL; + } + + return msg; +} + +#endif /* CONFIG_WPS_NFC */ diff -Nru wpa-1.0/src/wps/wps_defs.h wpa-2.1/src/wps/wps_defs.h --- wpa-1.0/src/wps/wps_defs.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/wps/wps_defs.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * Wi-Fi Protected Setup - message definitions * Copyright (c) 2008, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef WPS_DEFS_H @@ -19,6 +13,7 @@ extern int wps_version_number; extern int wps_testing_dummy_cred; +extern int wps_corrupt_pkhash; #define WPS_VERSION wps_version_number #else /* CONFIG_WPS_TESTING */ @@ -47,7 +42,7 @@ #define WPS_MGMTAUTHKEY_LEN 32 #define WPS_MGMTENCKEY_LEN 16 #define WPS_MGMT_KEY_ID_LEN 16 -#define WPS_OOB_DEVICE_PASSWORD_ATTR_LEN 54 +#define WPS_OOB_DEVICE_PASSWORD_MIN_LEN 16 #define WPS_OOB_DEVICE_PASSWORD_LEN 32 #define WPS_OOB_PUBKEY_HASH_LEN 20 @@ -161,7 +156,8 @@ DEV_PW_MACHINE_SPECIFIED = 0x0002, DEV_PW_REKEY = 0x0003, DEV_PW_PUSHBUTTON = 0x0004, - DEV_PW_REGISTRAR_SPECIFIED = 0x0005 + DEV_PW_REGISTRAR_SPECIFIED = 0x0005, + DEV_PW_NFC_CONNECTION_HANDOVER = 0x0007 }; /* Message Type */ @@ -221,7 +217,9 @@ WPS_CFG_SETUP_LOCKED = 15, WPS_CFG_MSG_TIMEOUT = 16, WPS_CFG_REG_SESS_TIMEOUT = 17, - WPS_CFG_DEV_PASSWORD_AUTH_FAILURE = 18 + WPS_CFG_DEV_PASSWORD_AUTH_FAILURE = 18, + WPS_CFG_60G_CHAN_NOT_SUPPORTED = 19, + WPS_CFG_PUBLIC_KEY_HASH_MISMATCH = 20 }; /* Vendor specific Error Indication for WPS event messages */ @@ -229,6 +227,7 @@ WPS_EI_NO_ERROR, WPS_EI_SECURITY_TKIP_ONLY_PROHIBITED, WPS_EI_SECURITY_WEP_PROHIBITED, + WPS_EI_AUTH_FAILURE, NUM_WPS_EI_VALUES }; diff -Nru wpa-1.0/src/wps/wps_dev_attr.c wpa-2.1/src/wps/wps_dev_attr.c --- wpa-1.0/src/wps/wps_dev_attr.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/wps/wps_dev_attr.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * Wi-Fi Protected Setup - device attributes * Copyright (c) 2008, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -91,8 +85,7 @@ } -static int wps_build_serial_number(struct wps_device_data *dev, - struct wpabuf *msg) +int wps_build_serial_number(struct wps_device_data *dev, struct wpabuf *msg) { size_t len; wpa_printf(MSG_DEBUG, "WPS: * Serial Number"); @@ -209,16 +202,27 @@ } -int wps_build_rf_bands(struct wps_device_data *dev, struct wpabuf *msg) +int wps_build_vendor_ext_m1(struct wps_device_data *dev, struct wpabuf *msg) { - wpa_printf(MSG_DEBUG, "WPS: * RF Bands (%x)", dev->rf_bands); - wpabuf_put_be16(msg, ATTR_RF_BANDS); - wpabuf_put_be16(msg, 1); - wpabuf_put_u8(msg, dev->rf_bands); + if (dev->vendor_ext_m1 != NULL) { + wpa_hexdump(MSG_DEBUG, "WPS: * Vendor Extension M1", + wpabuf_head_u8(dev->vendor_ext_m1), + wpabuf_len(dev->vendor_ext_m1)); + wpabuf_put_be16(msg, ATTR_VENDOR_EXT); + wpabuf_put_be16(msg, wpabuf_len(dev->vendor_ext_m1)); + wpabuf_put_buf(msg, dev->vendor_ext_m1); + } return 0; } +int wps_build_rf_bands(struct wps_device_data *dev, struct wpabuf *msg, + u8 rf_band) +{ + return wps_build_rf_bands_attr(msg, rf_band ? rf_band : dev->rf_bands); +} + + int wps_build_vendor_ext(struct wps_device_data *dev, struct wpabuf *msg) { int i; @@ -249,11 +253,9 @@ wpa_hexdump_ascii(MSG_DEBUG, "WPS: Manufacturer", str, str_len); os_free(dev->manufacturer); - dev->manufacturer = os_malloc(str_len + 1); + dev->manufacturer = dup_binstr(str, str_len); if (dev->manufacturer == NULL) return -1; - os_memcpy(dev->manufacturer, str, str_len); - dev->manufacturer[str_len] = '\0'; return 0; } @@ -270,11 +272,9 @@ wpa_hexdump_ascii(MSG_DEBUG, "WPS: Model Name", str, str_len); os_free(dev->model_name); - dev->model_name = os_malloc(str_len + 1); + dev->model_name = dup_binstr(str, str_len); if (dev->model_name == NULL) return -1; - os_memcpy(dev->model_name, str, str_len); - dev->model_name[str_len] = '\0'; return 0; } @@ -291,11 +291,9 @@ wpa_hexdump_ascii(MSG_DEBUG, "WPS: Model Number", str, str_len); os_free(dev->model_number); - dev->model_number = os_malloc(str_len + 1); + dev->model_number = dup_binstr(str, str_len); if (dev->model_number == NULL) return -1; - os_memcpy(dev->model_number, str, str_len); - dev->model_number[str_len] = '\0'; return 0; } @@ -312,11 +310,9 @@ wpa_hexdump_ascii(MSG_DEBUG, "WPS: Serial Number", str, str_len); os_free(dev->serial_number); - dev->serial_number = os_malloc(str_len + 1); + dev->serial_number = dup_binstr(str, str_len); if (dev->serial_number == NULL) return -1; - os_memcpy(dev->serial_number, str, str_len); - dev->serial_number[str_len] = '\0'; return 0; } @@ -333,11 +329,9 @@ wpa_hexdump_ascii(MSG_DEBUG, "WPS: Device Name", str, str_len); os_free(dev->device_name); - dev->device_name = os_malloc(str_len + 1); + dev->device_name = dup_binstr(str, str_len); if (dev->device_name == NULL) return -1; - os_memcpy(dev->device_name, str, str_len); - dev->device_name[str_len] = '\0'; return 0; } @@ -410,25 +404,6 @@ } -void wps_device_data_dup(struct wps_device_data *dst, - const struct wps_device_data *src) -{ - if (src->device_name) - dst->device_name = os_strdup(src->device_name); - if (src->manufacturer) - dst->manufacturer = os_strdup(src->manufacturer); - if (src->model_name) - dst->model_name = os_strdup(src->model_name); - if (src->model_number) - dst->model_number = os_strdup(src->model_number); - if (src->serial_number) - dst->serial_number = os_strdup(src->serial_number); - os_memcpy(dst->pri_dev_type, src->pri_dev_type, WPS_DEV_TYPE_LEN); - dst->os_version = src->os_version; - dst->rf_bands = src->rf_bands; -} - - void wps_device_data_free(struct wps_device_data *dev) { os_free(dev->device_name); diff -Nru wpa-1.0/src/wps/wps_dev_attr.h wpa-2.1/src/wps/wps_dev_attr.h --- wpa-1.0/src/wps/wps_dev_attr.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/wps/wps_dev_attr.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * Wi-Fi Protected Setup - device attributes * Copyright (c) 2008, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef WPS_DEV_ATTR_H @@ -20,10 +14,13 @@ int wps_build_manufacturer(struct wps_device_data *dev, struct wpabuf *msg); int wps_build_model_name(struct wps_device_data *dev, struct wpabuf *msg); int wps_build_model_number(struct wps_device_data *dev, struct wpabuf *msg); +int wps_build_serial_number(struct wps_device_data *dev, struct wpabuf *msg); int wps_build_dev_name(struct wps_device_data *dev, struct wpabuf *msg); int wps_build_device_attrs(struct wps_device_data *dev, struct wpabuf *msg); int wps_build_os_version(struct wps_device_data *dev, struct wpabuf *msg); -int wps_build_rf_bands(struct wps_device_data *dev, struct wpabuf *msg); +int wps_build_vendor_ext_m1(struct wps_device_data *dev, struct wpabuf *msg); +int wps_build_rf_bands(struct wps_device_data *dev, struct wpabuf *msg, + u8 rf_band); int wps_build_primary_dev_type(struct wps_device_data *dev, struct wpabuf *msg); int wps_build_secondary_dev_type(struct wps_device_data *dev, @@ -33,8 +30,6 @@ struct wps_parse_attr *attr); int wps_process_os_version(struct wps_device_data *dev, const u8 *ver); int wps_process_rf_bands(struct wps_device_data *dev, const u8 *bands); -void wps_device_data_dup(struct wps_device_data *dst, - const struct wps_device_data *src); void wps_device_data_free(struct wps_device_data *dev); int wps_build_vendor_ext(struct wps_device_data *dev, struct wpabuf *msg); int wps_build_req_dev_type(struct wps_device_data *dev, struct wpabuf *msg, diff -Nru wpa-1.0/src/wps/wps_enrollee.c wpa-2.1/src/wps/wps_enrollee.c --- wpa-1.0/src/wps/wps_enrollee.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/wps/wps_enrollee.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * Wi-Fi Protected Setup - Enrollee * Copyright (c) 2008, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -22,16 +16,6 @@ #include "wps_dev_attr.h" -static int wps_build_mac_addr(struct wps_data *wps, struct wpabuf *msg) -{ - wpa_printf(MSG_DEBUG, "WPS: * MAC Address"); - wpabuf_put_be16(msg, ATTR_MAC_ADDR); - wpabuf_put_be16(msg, ETH_ALEN); - wpabuf_put_data(msg, wps->mac_addr_e, ETH_ALEN); - return 0; -} - - static int wps_build_wps_state(struct wps_data *wps, struct wpabuf *msg) { u8 state; @@ -155,7 +139,7 @@ if (wps_build_version(msg) || wps_build_msg_type(msg, WPS_M1) || wps_build_uuid_e(msg, wps->uuid_e) || - wps_build_mac_addr(wps, msg) || + wps_build_mac_addr(msg, wps->mac_addr_e) || wps_build_enrollee_nonce(wps, msg) || wps_build_public_key(wps, msg) || wps_build_auth_type_flags(wps, msg) || @@ -164,12 +148,14 @@ wps_build_config_methods(msg, config_methods) || wps_build_wps_state(wps, msg) || wps_build_device_attrs(&wps->wps->dev, msg) || - wps_build_rf_bands(&wps->wps->dev, msg) || + wps_build_rf_bands(&wps->wps->dev, msg, + wps->wps->rf_band_cb(wps->wps->cb_ctx)) || wps_build_assoc_state(wps, msg) || wps_build_dev_password_id(msg, wps->dev_pw_id) || wps_build_config_error(msg, WPS_CFG_NO_ERROR) || wps_build_os_version(&wps->wps->dev, msg) || - wps_build_wfa_ext(msg, 0, NULL, 0)) { + wps_build_wfa_ext(msg, 0, NULL, 0) || + wps_build_vendor_ext_m1(&wps->wps->dev, msg)) { wpabuf_free(msg); return NULL; } @@ -407,7 +393,7 @@ if (wps->wps->ap) wps->state = RECV_ACK; else { - wps_success_event(wps->wps); + wps_success_event(wps->wps, wps->peer_dev.mac_addr); wps->state = WPS_FINISHED; } return msg; @@ -528,22 +514,23 @@ return -1; } -#ifdef CONFIG_WPS_OOB - if (wps->dev_pw_id != DEV_PW_DEFAULT && - wps->wps->oob_conf.pubkey_hash) { - const u8 *addr[1]; + if (wps->peer_pubkey_hash_set) { u8 hash[WPS_HASH_LEN]; - - addr[0] = pk; - sha256_vector(1, addr, &pk_len, hash); - if (os_memcmp(hash, - wpabuf_head(wps->wps->oob_conf.pubkey_hash), + sha256_vector(1, &pk, &pk_len, hash); + if (os_memcmp(hash, wps->peer_pubkey_hash, WPS_OOB_PUBKEY_HASH_LEN) != 0) { - wpa_printf(MSG_ERROR, "WPS: Public Key hash error"); + wpa_printf(MSG_ERROR, "WPS: Public Key hash mismatch"); + wpa_hexdump(MSG_DEBUG, "WPS: Received public key", + pk, pk_len); + wpa_hexdump(MSG_DEBUG, "WPS: Calculated public key " + "hash", hash, WPS_OOB_PUBKEY_HASH_LEN); + wpa_hexdump(MSG_DEBUG, "WPS: Expected public key hash", + wps->peer_pubkey_hash, + WPS_OOB_PUBKEY_HASH_LEN); + wps->config_error = WPS_CFG_PUBLIC_KEY_HASH_MISMATCH; return -1; } } -#endif /* CONFIG_WPS_OOB */ wpabuf_free(wps->dh_pubkey_r); wps->dh_pubkey_r = wpabuf_alloc_copy(pk, pk_len); @@ -614,7 +601,7 @@ wpa_printf(MSG_DEBUG, "WPS: R-Hash1 derived from R-S1 does " "not match with the pre-committed value"); wps->config_error = WPS_CFG_DEV_PASSWORD_AUTH_FAILURE; - wps_pwd_auth_fail_event(wps->wps, 1, 1); + wps_pwd_auth_fail_event(wps->wps, 1, 1, wps->peer_dev.mac_addr); return -1; } @@ -654,7 +641,7 @@ wpa_printf(MSG_DEBUG, "WPS: R-Hash2 derived from R-S2 does " "not match with the pre-committed value"); wps->config_error = WPS_CFG_DEV_PASSWORD_AUTH_FAILURE; - wps_pwd_auth_fail_event(wps->wps, 1, 2); + wps_pwd_auth_fail_event(wps->wps, 1, 2, wps->peer_dev.mac_addr); return -1; } @@ -670,6 +657,7 @@ { struct wps_parse_attr attr; struct wpabuf msg; + int ret = 0; wpa_printf(MSG_DEBUG, "WPS: Received Credential"); os_memset(&wps->cred, 0, sizeof(wps->cred)); @@ -719,12 +707,12 @@ if (wps->wps->cred_cb) { wps->cred.cred_attr = cred - 4; wps->cred.cred_attr_len = cred_len + 4; - wps->wps->cred_cb(wps->wps->cb_ctx, &wps->cred); + ret = wps->wps->cred_cb(wps->wps->cb_ctx, &wps->cred); wps->cred.cred_attr = NULL; wps->cred.cred_attr_len = 0; } - return 0; + return ret; } @@ -858,6 +846,63 @@ } +static int wps_process_dev_pw_id(struct wps_data *wps, const u8 *dev_pw_id) +{ + u16 id; + + if (dev_pw_id == NULL) { + wpa_printf(MSG_DEBUG, "WPS: Device Password ID"); + return -1; + } + + id = WPA_GET_BE16(dev_pw_id); + if (wps->dev_pw_id == id) { + wpa_printf(MSG_DEBUG, "WPS: Device Password ID %u", id); + return 0; + } + +#ifdef CONFIG_P2P + if ((id == DEV_PW_DEFAULT && + wps->dev_pw_id == DEV_PW_REGISTRAR_SPECIFIED) || + (id == DEV_PW_REGISTRAR_SPECIFIED && + wps->dev_pw_id == DEV_PW_DEFAULT)) { + /* + * Common P2P use cases indicate whether the PIN is from the + * client or GO using Device Password Id in M1/M2 in a way that + * does not look fully compliant with WSC specification. Anyway, + * this is deployed and needs to be allowed, so ignore changes + * between Registrar-Specified and Default PIN. + */ + wpa_printf(MSG_DEBUG, "WPS: Allow PIN Device Password ID " + "change"); + return 0; + } +#endif /* CONFIG_P2P */ + + wpa_printf(MSG_DEBUG, "WPS: Registrar trying to change Device Password " + "ID from %u to %u", wps->dev_pw_id, id); + + if (wps->dev_pw_id == DEV_PW_PUSHBUTTON && id == DEV_PW_DEFAULT) { + wpa_printf(MSG_DEBUG, + "WPS: Workaround - ignore PBC-to-PIN change"); + return 0; + } + + if (wps->alt_dev_password && wps->alt_dev_pw_id == id) { + wpa_printf(MSG_DEBUG, "WPS: Found a matching Device Password"); + os_free(wps->dev_password); + wps->dev_pw_id = wps->alt_dev_pw_id; + wps->dev_password = wps->alt_dev_password; + wps->dev_password_len = wps->alt_dev_password_len; + wps->alt_dev_password = NULL; + wps->alt_dev_password_len = 0; + return 0; + } + + return -1; +} + + static enum wps_process_res wps_process_m2(struct wps_data *wps, const struct wpabuf *msg, struct wps_parse_attr *attr) @@ -873,7 +918,8 @@ if (wps_process_registrar_nonce(wps, attr->registrar_nonce) || wps_process_enrollee_nonce(wps, attr->enrollee_nonce) || - wps_process_uuid_r(wps, attr->uuid_r)) { + wps_process_uuid_r(wps, attr->uuid_r) || + wps_process_dev_pw_id(wps, attr->dev_password_id)) { wps->state = SEND_WSC_NACK; return WPS_CONTINUE; } @@ -901,6 +947,38 @@ return WPS_CONTINUE; } +#ifdef CONFIG_WPS_NFC + if (wps->peer_pubkey_hash_set) { + struct wpabuf *decrypted; + struct wps_parse_attr eattr; + + decrypted = wps_decrypt_encr_settings(wps, attr->encr_settings, + attr->encr_settings_len); + if (decrypted == NULL) { + wpa_printf(MSG_DEBUG, "WPS: Failed to decrypt " + "Encrypted Settings attribute"); + wps->state = SEND_WSC_NACK; + return WPS_CONTINUE; + } + + wpa_printf(MSG_DEBUG, "WPS: Processing decrypted Encrypted " + "Settings attribute"); + if (wps_parse_msg(decrypted, &eattr) < 0 || + wps_process_key_wrap_auth(wps, decrypted, + eattr.key_wrap_auth) || + wps_process_creds(wps, eattr.cred, eattr.cred_len, + eattr.num_cred, attr->version2 != NULL)) { + wpabuf_free(decrypted); + wps->state = SEND_WSC_NACK; + return WPS_CONTINUE; + } + wpabuf_free(decrypted); + + wps->state = WPS_MSG_DONE; + return WPS_CONTINUE; + } +#endif /* CONFIG_WPS_NFC */ + wps->state = SEND_M3; return WPS_CONTINUE; } @@ -994,7 +1072,7 @@ return WPS_CONTINUE; } - if (wps_validate_m4_encr(decrypted, attr->version2 != 0) < 0) { + if (wps_validate_m4_encr(decrypted, attr->version2 != NULL) < 0) { wpabuf_free(decrypted); wps->state = SEND_WSC_NACK; return WPS_CONTINUE; @@ -1047,7 +1125,7 @@ return WPS_CONTINUE; } - if (wps_validate_m6_encr(decrypted, attr->version2 != 0) < 0) { + if (wps_validate_m6_encr(decrypted, attr->version2 != NULL) < 0) { wpabuf_free(decrypted); wps->state = SEND_WSC_NACK; return WPS_CONTINUE; @@ -1117,8 +1195,8 @@ return WPS_CONTINUE; } - if (wps_validate_m8_encr(decrypted, wps->wps->ap, attr->version2 != 0) - < 0) { + if (wps_validate_m8_encr(decrypted, wps->wps->ap, + attr->version2 != NULL) < 0) { wpabuf_free(decrypted); wps->state = SEND_WSC_NACK; return WPS_CONTINUE; @@ -1155,7 +1233,7 @@ return WPS_FAILURE; if (attr.enrollee_nonce == NULL || - os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN != 0)) { + os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN) != 0) { wpa_printf(MSG_DEBUG, "WPS: Mismatch in enrollee nonce"); return WPS_FAILURE; } @@ -1183,7 +1261,8 @@ ret = wps_process_m4(wps, msg, &attr); if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK) wps_fail_event(wps->wps, WPS_M4, wps->config_error, - wps->error_indication); + wps->error_indication, + wps->peer_dev.mac_addr); break; case WPS_M6: if (wps_validate_m6(msg) < 0) @@ -1191,7 +1270,8 @@ ret = wps_process_m6(wps, msg, &attr); if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK) wps_fail_event(wps->wps, WPS_M6, wps->config_error, - wps->error_indication); + wps->error_indication, + wps->peer_dev.mac_addr); break; case WPS_M8: if (wps_validate_m8(msg) < 0) @@ -1199,7 +1279,8 @@ ret = wps_process_m8(wps, msg, &attr); if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK) wps_fail_event(wps->wps, WPS_M8, wps->config_error, - wps->error_indication); + wps->error_indication, + wps->peer_dev.mac_addr); break; default: wpa_printf(MSG_DEBUG, "WPS: Unsupported Message Type %d", @@ -1247,14 +1328,14 @@ } if (attr.registrar_nonce == NULL || - os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN != 0)) + os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN) != 0) { wpa_printf(MSG_DEBUG, "WPS: Mismatch in registrar nonce"); return WPS_FAILURE; } if (attr.enrollee_nonce == NULL || - os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN != 0)) { + os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN) != 0) { wpa_printf(MSG_DEBUG, "WPS: Mismatch in enrollee nonce"); return WPS_FAILURE; } @@ -1262,7 +1343,7 @@ if (wps->state == RECV_ACK && wps->wps->ap) { wpa_printf(MSG_DEBUG, "WPS: External Registrar registration " "completed successfully"); - wps_success_event(wps->wps); + wps_success_event(wps->wps, wps->peer_dev.mac_addr); wps->state = WPS_FINISHED; return WPS_DONE; } @@ -1294,7 +1375,7 @@ } if (attr.registrar_nonce == NULL || - os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN != 0)) + os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN) != 0) { wpa_printf(MSG_DEBUG, "WPS: Mismatch in registrar nonce"); wpa_hexdump(MSG_DEBUG, "WPS: Received Registrar Nonce", @@ -1305,7 +1386,7 @@ } if (attr.enrollee_nonce == NULL || - os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN != 0)) { + os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN) != 0) { wpa_printf(MSG_DEBUG, "WPS: Mismatch in enrollee nonce"); wpa_hexdump(MSG_DEBUG, "WPS: Received Enrollee Nonce", attr.enrollee_nonce, WPS_NONCE_LEN); @@ -1327,15 +1408,15 @@ switch (wps->state) { case RECV_M4: wps_fail_event(wps->wps, WPS_M3, config_error, - wps->error_indication); + wps->error_indication, wps->peer_dev.mac_addr); break; case RECV_M6: wps_fail_event(wps->wps, WPS_M5, config_error, - wps->error_indication); + wps->error_indication, wps->peer_dev.mac_addr); break; case RECV_M8: wps_fail_event(wps->wps, WPS_M7, config_error, - wps->error_indication); + wps->error_indication, wps->peer_dev.mac_addr); break; default: break; diff -Nru wpa-1.0/src/wps/wps_er.c wpa-2.1/src/wps/wps_er.c --- wpa-1.0/src/wps/wps_er.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/wps/wps_er.c 2014-02-04 11:23:35.000000000 +0000 @@ -1,15 +1,9 @@ /* * Wi-Fi Protected Setup - External Registrar - * Copyright (c) 2009, Jouni Malinen + * Copyright (c) 2009-2013, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -103,13 +97,16 @@ static struct wps_er_ap * wps_er_ap_get(struct wps_er *er, - struct in_addr *addr, const u8 *uuid) + struct in_addr *addr, const u8 *uuid, + const u8 *mac_addr) { struct wps_er_ap *ap; dl_list_for_each(ap, &er->ap, struct wps_er_ap, list) { if ((addr == NULL || ap->addr.s_addr == addr->s_addr) && (uuid == NULL || - os_memcmp(uuid, ap->uuid, WPS_UUID_LEN) == 0)) + os_memcmp(uuid, ap->uuid, WPS_UUID_LEN) == 0) && + (mac_addr == NULL || + os_memcmp(mac_addr, ap->mac_addr, ETH_ALEN) == 0)) return ap; } return NULL; @@ -188,10 +185,8 @@ dl_list_del(&ap->list); wps_er_ap_free(ap); - if (er->deinitializing && dl_list_empty(&er->ap_unsubscribing)) { - eloop_cancel_timeout(wps_er_deinit_finish, er, NULL); + if (er->deinitializing && dl_list_empty(&er->ap_unsubscribing)) wps_er_deinit_finish(er, NULL); - } } @@ -296,7 +291,7 @@ struct wps_er_ap *ap; struct wps_er_ap_settings *settings; - ap = wps_er_ap_get(er, addr, NULL); + ap = wps_er_ap_get(er, addr, NULL, NULL); if (ap == NULL || ap->ap_settings == NULL) return -1; @@ -642,7 +637,7 @@ { struct wps_er_ap *ap; - ap = wps_er_ap_get(er, addr, uuid); + ap = wps_er_ap_get(er, addr, uuid, NULL); if (ap) { /* Update advertisement timeout */ eloop_cancel_timeout(wps_er_ap_timeout, er, ap); @@ -799,52 +794,31 @@ if (attr->manufacturer) { os_free(sta->manufacturer); - sta->manufacturer = os_malloc(attr->manufacturer_len + 1); - if (sta->manufacturer) { - os_memcpy(sta->manufacturer, attr->manufacturer, - attr->manufacturer_len); - sta->manufacturer[attr->manufacturer_len] = '\0'; - } + sta->manufacturer = dup_binstr(attr->manufacturer, + attr->manufacturer_len); } if (attr->model_name) { os_free(sta->model_name); - sta->model_name = os_malloc(attr->model_name_len + 1); - if (sta->model_name) { - os_memcpy(sta->model_name, attr->model_name, - attr->model_name_len); - sta->model_name[attr->model_name_len] = '\0'; - } + sta->model_name = dup_binstr(attr->model_name, + attr->model_name_len); } if (attr->model_number) { os_free(sta->model_number); - sta->model_number = os_malloc(attr->model_number_len + 1); - if (sta->model_number) { - os_memcpy(sta->model_number, attr->model_number, - attr->model_number_len); - sta->model_number[attr->model_number_len] = '\0'; - } + sta->model_number = dup_binstr(attr->model_number, + attr->model_number_len); } if (attr->serial_number) { os_free(sta->serial_number); - sta->serial_number = os_malloc(attr->serial_number_len + 1); - if (sta->serial_number) { - os_memcpy(sta->serial_number, attr->serial_number, - attr->serial_number_len); - sta->serial_number[attr->serial_number_len] = '\0'; - } + sta->serial_number = dup_binstr(attr->serial_number, + attr->serial_number_len); } if (attr->dev_name) { os_free(sta->dev_name); - sta->dev_name = os_malloc(attr->dev_name_len + 1); - if (sta->dev_name) { - os_memcpy(sta->dev_name, attr->dev_name, - attr->dev_name_len); - sta->dev_name[attr->dev_name_len] = '\0'; - } + sta->dev_name = dup_binstr(attr->dev_name, attr->dev_name_len); } eloop_cancel_timeout(wps_er_sta_timeout, sta, NULL); @@ -1295,6 +1269,22 @@ /* Limit event_id to < 32 bits to avoid issues with atoi() */ er->event_id &= 0x0fffffff; + if (filter && os_strncmp(filter, "ifname=", 7) == 0) { + const char *pos, *end; + pos = filter + 7; + end = os_strchr(pos, ' '); + if (end) { + size_t len = end - pos; + os_strlcpy(er->ifname, pos, len < sizeof(er->ifname) ? + len + 1 : sizeof(er->ifname)); + filter = end + 1; + } else { + os_strlcpy(er->ifname, pos, sizeof(er->ifname)); + filter = NULL; + } + er->forced_ifname = 1; + } + if (filter) { if (inet_aton(filter, &er->filter_addr) == 0) { wpa_printf(MSG_INFO, "WPS UPnP: Invalid filter " @@ -1305,10 +1295,10 @@ wpa_printf(MSG_DEBUG, "WPS UPnP: Only accepting connections " "with %s", filter); } - if (get_netif_info(ifname, &er->ip_addr, &er->ip_addr_text, + if (get_netif_info(er->ifname, &er->ip_addr, &er->ip_addr_text, er->mac_addr)) { wpa_printf(MSG_INFO, "WPS UPnP: Could not get IP/MAC address " - "for %s. Does it have IP address?", ifname); + "for %s. Does it have IP address?", er->ifname); wps_er_deinit(er, NULL, NULL); return NULL; } @@ -1355,9 +1345,19 @@ struct wps_er *er = eloop_data; void (*deinit_done_cb)(void *ctx); void *deinit_done_ctx; + struct wps_er_ap *ap, *tmp; wpa_printf(MSG_DEBUG, "WPS ER: Finishing deinit"); + dl_list_for_each_safe(ap, tmp, &er->ap_unsubscribing, struct wps_er_ap, + list) { + wpa_printf(MSG_DEBUG, "WPS ER: AP entry for %s (%s) still in ap_unsubscribing list - free it", + inet_ntoa(ap->addr), ap->location); + dl_list_del(&ap->list); + wps_er_ap_free(ap); + } + + eloop_cancel_timeout(wps_er_deinit_finish, er, NULL); deinit_done_cb = er->deinit_done_cb; deinit_done_ctx = er->deinit_done_ctx; os_free(er->ip_addr_text); @@ -1561,7 +1561,7 @@ } -int wps_er_pbc(struct wps_er *er, const u8 *uuid) +int wps_er_pbc(struct wps_er *er, const u8 *uuid, const u8 *addr) { int res; struct wps_er_ap *ap; @@ -1575,11 +1575,14 @@ return -2; } - ap = wps_er_ap_get(er, NULL, uuid); + if (uuid) + ap = wps_er_ap_get(er, NULL, uuid, NULL); + else + ap = NULL; if (ap == NULL) { struct wps_er_sta *sta = NULL; dl_list_for_each(ap, &er->ap, struct wps_er_ap, list) { - sta = wps_er_sta_get(ap, NULL, uuid); + sta = wps_er_sta_get(ap, addr, uuid); if (sta) { uuid = ap->uuid; break; @@ -1625,6 +1628,19 @@ } +const u8 * wps_er_get_sta_uuid(struct wps_er *er, const u8 *addr) +{ + struct wps_er_ap *ap; + dl_list_for_each(ap, &er->ap, struct wps_er_ap, list) { + struct wps_er_sta *sta; + sta = wps_er_sta_get(ap, addr, NULL); + if (sta) + return sta->uuid; + } + return NULL; +} + + static void wps_er_http_put_message_cb(void *ctx, struct http_client *c, enum http_client_event event) { @@ -1883,20 +1899,22 @@ } -int wps_er_learn(struct wps_er *er, const u8 *uuid, const u8 *pin, - size_t pin_len) +int wps_er_learn(struct wps_er *er, const u8 *uuid, const u8 *addr, + const u8 *pin, size_t pin_len) { struct wps_er_ap *ap; if (er == NULL) return -1; - ap = wps_er_ap_get(er, NULL, uuid); + ap = wps_er_ap_get(er, NULL, uuid, addr); if (ap == NULL) { wpa_printf(MSG_DEBUG, "WPS ER: AP not found for learn " "request"); return -1; } + if (uuid == NULL) + uuid = ap->uuid; if (ap->wps) { wpa_printf(MSG_DEBUG, "WPS ER: Pending operation ongoing " "with the AP - cannot start learn"); @@ -1914,7 +1932,7 @@ } -int wps_er_set_config(struct wps_er *er, const u8 *uuid, +int wps_er_set_config(struct wps_er *er, const u8 *uuid, const u8 *addr, const struct wps_credential *cred) { struct wps_er_ap *ap; @@ -1922,7 +1940,7 @@ if (er == NULL) return -1; - ap = wps_er_ap_get(er, NULL, uuid); + ap = wps_er_ap_get(er, NULL, uuid, addr); if (ap == NULL) { wpa_printf(MSG_DEBUG, "WPS ER: AP not found for set config " "request"); @@ -1966,20 +1984,23 @@ } -int wps_er_config(struct wps_er *er, const u8 *uuid, const u8 *pin, - size_t pin_len, const struct wps_credential *cred) +int wps_er_config(struct wps_er *er, const u8 *uuid, const u8 *addr, + const u8 *pin, size_t pin_len, + const struct wps_credential *cred) { struct wps_er_ap *ap; if (er == NULL) return -1; - ap = wps_er_ap_get(er, NULL, uuid); + ap = wps_er_ap_get(er, NULL, uuid, addr); if (ap == NULL) { wpa_printf(MSG_DEBUG, "WPS ER: AP not found for config " "request"); return -1; } + if (uuid == NULL) + uuid = ap->uuid; if (ap->wps) { wpa_printf(MSG_DEBUG, "WPS ER: Pending operation ongoing " "with the AP - cannot start config"); @@ -2002,3 +2023,76 @@ return 0; } + + +#ifdef CONFIG_WPS_NFC + +struct wpabuf * wps_er_config_token_from_cred(struct wps_context *wps, + struct wps_credential *cred) +{ + struct wpabuf *ret; + struct wps_data data; + + ret = wpabuf_alloc(500); + if (ret == NULL) + return NULL; + + os_memset(&data, 0, sizeof(data)); + data.wps = wps; + data.use_cred = cred; + if (wps_build_cred(&data, ret) || + wps_build_wfa_ext(ret, 0, NULL, 0)) { + wpabuf_free(ret); + return NULL; + } + + return ret; +} + + +struct wpabuf * wps_er_nfc_config_token(struct wps_er *er, const u8 *uuid, + const u8 *addr) +{ + struct wps_er_ap *ap; + + if (er == NULL) + return NULL; + + ap = wps_er_ap_get(er, NULL, uuid, addr); + if (ap == NULL) + return NULL; + if (ap->ap_settings == NULL) { + wpa_printf(MSG_DEBUG, "WPS ER: No settings known for the " + "selected AP"); + return NULL; + } + + return wps_er_config_token_from_cred(er->wps, ap->ap_settings); +} + + +struct wpabuf * wps_er_nfc_handover_sel(struct wps_er *er, + struct wps_context *wps, const u8 *uuid, + const u8 *addr, struct wpabuf *pubkey) +{ + struct wps_er_ap *ap; + + if (er == NULL) + return NULL; + + ap = wps_er_ap_get(er, NULL, uuid, addr); + if (ap == NULL) + return NULL; + if (ap->ap_settings == NULL) { + wpa_printf(MSG_DEBUG, "WPS ER: No settings known for the " + "selected AP"); + return NULL; + } + + os_memcpy(wps->ssid, ap->ap_settings->ssid, ap->ap_settings->ssid_len); + wps->ssid_len = ap->ap_settings->ssid_len; + + return wps_build_nfc_handover_sel(wps, pubkey, addr, 0); +} + +#endif /* CONFIG_WPS_NFC */ diff -Nru wpa-1.0/src/wps/wps_er.h wpa-2.1/src/wps/wps_er.h --- wpa-1.0/src/wps/wps_er.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/wps/wps_er.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * Wi-Fi Protected Setup - External Registrar * Copyright (c) 2009, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef WPS_ER_H @@ -82,6 +76,7 @@ struct wps_er { struct wps_context *wps; char ifname[17]; + int forced_ifname; u8 mac_addr[ETH_ALEN]; /* mac addr of network i.f. we use */ char *ip_addr_text; /* IP address of network i.f. we use */ unsigned ip_addr; /* IP address of network i.f. we use (host order) */ diff -Nru wpa-1.0/src/wps/wps_er_ssdp.c wpa-2.1/src/wps/wps_er_ssdp.c --- wpa-1.0/src/wps/wps_er_ssdp.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/wps/wps_er_ssdp.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * Wi-Fi Protected Setup - External Registrar (SSDP) * Copyright (c) 2009, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -172,7 +166,9 @@ return -1; } - er->multicast_sd = ssdp_open_multicast_sock(er->ip_addr); + er->multicast_sd = ssdp_open_multicast_sock(er->ip_addr, + er->forced_ifname ? + er->ifname : NULL); if (er->multicast_sd < 0) { wpa_printf(MSG_INFO, "WPS ER: Failed to open multicast socket " "for SSDP"); diff -Nru wpa-1.0/src/wps/wps.h wpa-2.1/src/wps/wps.h --- wpa-1.0/src/wps/wps.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/wps/wps.h 2014-02-04 11:23:35.000000000 +0000 @@ -1,15 +1,9 @@ /* * Wi-Fi Protected Setup - * Copyright (c) 2007-2009, Jouni Malinen + * Copyright (c) 2007-2013, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef WPS_H @@ -33,6 +27,7 @@ struct wps_registrar; struct upnp_wps_device_sm; struct wps_er; +struct wps_parse_attr; /** * struct wps_credential - WPS Credential @@ -47,6 +42,7 @@ * @cred_attr: Unparsed Credential attribute data (used only in cred_cb()); * this may be %NULL, if not used * @cred_attr_len: Length of cred_attr in octets + * @ap_channel: AP channel */ struct wps_credential { u8 ssid[32]; @@ -59,6 +55,7 @@ u8 mac_addr[ETH_ALEN]; const u8 *cred_attr; size_t cred_attr_len; + u16 ap_channel; }; #define WPS_DEV_TYPE_LEN 8 @@ -100,22 +97,12 @@ u32 os_version; u8 rf_bands; u16 config_methods; + struct wpabuf *vendor_ext_m1; struct wpabuf *vendor_ext[MAX_WPS_VENDOR_EXTENSIONS]; int p2p; }; -struct oob_conf_data { - enum { - OOB_METHOD_UNKNOWN = 0, - OOB_METHOD_DEV_PWD_E, - OOB_METHOD_DEV_PWD_R, - OOB_METHOD_CRED, - } oob_method; - struct wpabuf *dev_password; - struct wpabuf *pubkey_hash; -}; - /** * struct wps_config - WPS configuration for a single registration protocol run */ @@ -196,6 +183,11 @@ * PBC with the AP. */ int pbc_in_m1; + + /** + * peer_pubkey_hash - Peer public key hash or %NULL if not known + */ + const u8 *peer_pubkey_hash; }; struct wps_data * wps_init(const struct wps_config *cfg); @@ -244,7 +236,7 @@ struct wpabuf * wps_build_assoc_req_ie(enum wps_request_type req_type); struct wpabuf * wps_build_assoc_resp_ie(void); -struct wpabuf * wps_build_probe_req_ie(int pbc, struct wps_device_data *dev, +struct wpabuf * wps_build_probe_req_ie(u16 pw_id, struct wps_device_data *dev, const u8 *uuid, enum wps_request_type req_type, unsigned int num_req_dev_types, @@ -259,14 +251,15 @@ * new_psk_cb - Callback for new PSK * @ctx: Higher layer context data (cb_ctx) * @mac_addr: MAC address of the Enrollee + * @p2p_dev_addr: P2P Device Address of the Enrollee or all zeros if not * @psk: The new PSK * @psk_len: The length of psk in octets * Returns: 0 on success, -1 on failure * * This callback is called when a new per-device PSK is provisioned. */ - int (*new_psk_cb)(void *ctx, const u8 *mac_addr, const u8 *psk, - size_t psk_len); + int (*new_psk_cb)(void *ctx, const u8 *mac_addr, const u8 *p2p_dev_addr, + const u8 *psk, size_t psk_len); /** * set_ie_cb - Callback for WPS IE changes @@ -300,12 +293,15 @@ * @ctx: Higher layer context data (cb_ctx) * @mac_addr: MAC address of the Enrollee * @uuid_e: UUID-E of the Enrollee + * @dev_pw: Device Password (PIN) used during registration + * @dev_pw_len: Length of dev_pw in octets * * This callback is called whenever an Enrollee completes registration * successfully. */ void (*reg_success_cb)(void *ctx, const u8 *mac_addr, - const u8 *uuid_e); + const u8 *uuid_e, const u8 *dev_pw, + size_t dev_pw_len); /** * set_sel_reg_cb - Callback for reporting selected registrar changes @@ -392,6 +388,14 @@ * dualband - Whether this is a concurrent dualband AP */ int dualband; + + /** + * force_per_enrollee_psk - Force per-Enrollee random PSK + * + * This forces per-Enrollee random PSK to be generated even if a default + * PSK is set for a network. + */ + int force_per_enrollee_psk; }; @@ -430,6 +434,16 @@ WPS_EV_PBC_TIMEOUT, /** + * WPS_EV_PBC_ACTIVE - PBC mode was activated + */ + WPS_EV_PBC_ACTIVE, + + /** + * WPS_EV_PBC_DISABLE - PBC mode was disabled + */ + WPS_EV_PBC_DISABLE, + + /** * WPS_EV_ER_AP_ADD - ER: AP added */ WPS_EV_ER_AP_ADD, @@ -497,11 +511,17 @@ int msg; u16 config_error; u16 error_indication; + u8 peer_macaddr[ETH_ALEN]; } fail; + struct wps_event_success { + u8 peer_macaddr[ETH_ALEN]; + } success; + struct wps_event_pwd_auth_fail { int enrollee; int part; + u8 peer_macaddr[ETH_ALEN]; } pwd_auth_fail; struct wps_event_er_ap { @@ -618,16 +638,6 @@ struct wps_device_data dev; /** - * oob_conf - OOB Config data - */ - struct oob_conf_data oob_conf; - - /** - * oob_dev_pw_id - OOB Device password id - */ - u16 oob_dev_pw_id; - - /** * dh_ctx - Context data for Diffie-Hellman operation */ void *dh_ctx; @@ -750,6 +760,13 @@ union wps_event_data *data); /** + * rf_band_cb - Fetch currently used RF band + * @ctx: Higher layer context data (cb_ctx) + * Return: Current used RF band or 0 if not known + */ + int (*rf_band_cb)(void *ctx); + + /** * cb_ctx: Higher layer context data for callbacks */ void *cb_ctx; @@ -758,23 +775,11 @@ /* Pending messages from UPnP PutWLANResponse */ struct upnp_pending_message *upnp_msgs; -}; - -struct oob_device_data { - char *device_name; - char *device_path; - void * (*init_func)(struct wps_context *, struct oob_device_data *, - int); - struct wpabuf * (*read_func)(void *); - int (*write_func)(void *, struct wpabuf *); - void (*deinit_func)(void *); -}; -struct oob_nfc_device_data { - int (*init_func)(char *); - void * (*read_func)(size_t *); - int (*write_func)(void *, size_t); - void (*deinit_func)(void); + u16 ap_nfc_dev_pw_id; + struct wpabuf *ap_nfc_dh_pubkey; + struct wpabuf *ap_nfc_dh_privkey; + struct wpabuf *ap_nfc_dev_pw; }; struct wps_registrar * @@ -789,7 +794,8 @@ int wps_registrar_unlock_pin(struct wps_registrar *reg, const u8 *uuid); int wps_registrar_button_pushed(struct wps_registrar *reg, const u8 *p2p_dev_addr); -void wps_registrar_complete(struct wps_registrar *registrar, const u8 *uuid_e); +void wps_registrar_complete(struct wps_registrar *registrar, const u8 *uuid_e, + const u8 *dev_pw, size_t dev_pw_len); void wps_registrar_probe_req_rx(struct wps_registrar *reg, const u8 *addr, const struct wpabuf *wps_data, int p2p_wildcard); @@ -798,6 +804,13 @@ char *buf, size_t buflen); int wps_registrar_config_ap(struct wps_registrar *reg, struct wps_credential *cred); +int wps_registrar_add_nfc_pw_token(struct wps_registrar *reg, + const u8 *pubkey_hash, u16 pw_id, + const u8 *dev_pw, size_t dev_pw_len, + int pk_hash_provided_oob); +int wps_registrar_add_nfc_password_token(struct wps_registrar *reg, + const u8 *oob_dev_pw, + size_t oob_dev_pw_len); int wps_build_credential_wrap(struct wpabuf *msg, const struct wps_credential *cred); @@ -805,14 +818,14 @@ unsigned int wps_pin_checksum(unsigned int pin); unsigned int wps_pin_valid(unsigned int pin); unsigned int wps_generate_pin(void); +int wps_pin_str_valid(const char *pin); void wps_free_pending_msgs(struct upnp_pending_message *msgs); -struct oob_device_data * wps_get_oob_device(char *device_type); -struct oob_nfc_device_data * wps_get_oob_nfc_device(char *device_name); -int wps_get_oob_method(char *method); -int wps_process_oob(struct wps_context *wps, struct oob_device_data *oob_dev, - int registrar); +struct wpabuf * wps_get_oob_cred(struct wps_context *wps, int rf_band, + int channel); +int wps_oob_use_cred(struct wps_context *wps, struct wps_parse_attr *attr); int wps_attr_text(struct wpabuf *data, char *buf, char *end); +const char * wps_ei_str(enum wps_error_indication ei); struct wps_er * wps_er_init(struct wps_context *wps, const char *ifname, const char *filter); @@ -820,19 +833,54 @@ void wps_er_deinit(struct wps_er *er, void (*cb)(void *ctx), void *ctx); void wps_er_set_sel_reg(struct wps_er *er, int sel_reg, u16 dev_passwd_id, u16 sel_reg_config_methods); -int wps_er_pbc(struct wps_er *er, const u8 *uuid); -int wps_er_learn(struct wps_er *er, const u8 *uuid, const u8 *pin, - size_t pin_len); -int wps_er_set_config(struct wps_er *er, const u8 *uuid, +int wps_er_pbc(struct wps_er *er, const u8 *uuid, const u8 *addr); +const u8 * wps_er_get_sta_uuid(struct wps_er *er, const u8 *addr); +int wps_er_learn(struct wps_er *er, const u8 *uuid, const u8 *addr, + const u8 *pin, size_t pin_len); +int wps_er_set_config(struct wps_er *er, const u8 *uuid, const u8 *addr, const struct wps_credential *cred); -int wps_er_config(struct wps_er *er, const u8 *uuid, const u8 *pin, - size_t pin_len, const struct wps_credential *cred); +int wps_er_config(struct wps_er *er, const u8 *uuid, const u8 *addr, + const u8 *pin, size_t pin_len, + const struct wps_credential *cred); +struct wpabuf * wps_er_config_token_from_cred(struct wps_context *wps, + struct wps_credential *cred); +struct wpabuf * wps_er_nfc_config_token(struct wps_er *er, const u8 *uuid, + const u8 *addr); +struct wpabuf * wps_er_nfc_handover_sel(struct wps_er *er, + struct wps_context *wps, const u8 *uuid, + const u8 *addr, struct wpabuf *pubkey); int wps_dev_type_str2bin(const char *str, u8 dev_type[WPS_DEV_TYPE_LEN]); char * wps_dev_type_bin2str(const u8 dev_type[WPS_DEV_TYPE_LEN], char *buf, size_t buf_len); void uuid_gen_mac_addr(const u8 *mac_addr, u8 *uuid); u16 wps_config_methods_str2bin(const char *str); +struct wpabuf * wps_build_nfc_pw_token(u16 dev_pw_id, + const struct wpabuf *pubkey, + const struct wpabuf *dev_pw); +struct wpabuf * wps_nfc_token_build(int ndef, int id, struct wpabuf *pubkey, + struct wpabuf *dev_pw); +int wps_nfc_gen_dh(struct wpabuf **pubkey, struct wpabuf **privkey); +struct wpabuf * wps_nfc_token_gen(int ndef, int *id, struct wpabuf **pubkey, + struct wpabuf **privkey, + struct wpabuf **dev_pw); +struct wpabuf * wps_build_nfc_handover_req(struct wps_context *ctx, + struct wpabuf *nfc_dh_pubkey); +struct wpabuf * wps_build_nfc_handover_sel(struct wps_context *ctx, + struct wpabuf *nfc_dh_pubkey, + const u8 *bssid, int freq); +struct wpabuf * wps_build_nfc_handover_req_p2p(struct wps_context *ctx, + struct wpabuf *nfc_dh_pubkey); +struct wpabuf * wps_build_nfc_handover_sel_p2p(struct wps_context *ctx, + int nfc_dev_pw_id, + struct wpabuf *nfc_dh_pubkey, + struct wpabuf *nfc_dev_pw); + +/* ndef.c */ +struct wpabuf * ndef_parse_wifi(const struct wpabuf *buf); +struct wpabuf * ndef_build_wifi(const struct wpabuf *buf); +struct wpabuf * ndef_parse_p2p(const struct wpabuf *buf); +struct wpabuf * ndef_build_p2p(const struct wpabuf *buf); #ifdef CONFIG_WPS_STRICT int wps_validate_beacon(const struct wpabuf *wps_ie); diff -Nru wpa-1.0/src/wps/wps_i.h wpa-2.1/src/wps/wps_i.h --- wpa-1.0/src/wps/wps_i.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/wps/wps_i.h 2014-02-04 11:23:35.000000000 +0000 @@ -1,21 +1,18 @@ /* * Wi-Fi Protected Setup - internal definitions - * Copyright (c) 2008-2009, Jouni Malinen + * Copyright (c) 2008-2012, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef WPS_I_H #define WPS_I_H #include "wps.h" +#include "wps_attr_parse.h" + +struct wps_nfc_pw_token; /** * struct wps_data - WPS registration protocol data @@ -74,6 +71,12 @@ size_t dev_password_len; u16 dev_pw_id; int pbc; + u8 *alt_dev_password; + size_t alt_dev_password_len; + u16 alt_dev_pw_id; + + u8 peer_pubkey_hash[WPS_OOB_PUBKEY_HASH_LEN]; + int peer_pubkey_hash_set; /** * request_type - Request Type attribute from (Re)AssocReq @@ -120,100 +123,11 @@ u8 p2p_dev_addr[ETH_ALEN]; /* P2P Device Address of the client or * 00:00:00:00:00:00 if not a P2p client */ int pbc_in_m1; -}; - -struct wps_parse_attr { - /* fixed length fields */ - const u8 *version; /* 1 octet */ - const u8 *version2; /* 1 octet */ - const u8 *msg_type; /* 1 octet */ - const u8 *enrollee_nonce; /* WPS_NONCE_LEN (16) octets */ - const u8 *registrar_nonce; /* WPS_NONCE_LEN (16) octets */ - const u8 *uuid_r; /* WPS_UUID_LEN (16) octets */ - const u8 *uuid_e; /* WPS_UUID_LEN (16) octets */ - const u8 *auth_type_flags; /* 2 octets */ - const u8 *encr_type_flags; /* 2 octets */ - const u8 *conn_type_flags; /* 1 octet */ - const u8 *config_methods; /* 2 octets */ - const u8 *sel_reg_config_methods; /* 2 octets */ - const u8 *primary_dev_type; /* 8 octets */ - const u8 *rf_bands; /* 1 octet */ - const u8 *assoc_state; /* 2 octets */ - const u8 *config_error; /* 2 octets */ - const u8 *dev_password_id; /* 2 octets */ - const u8 *oob_dev_password; /* WPS_OOB_DEVICE_PASSWORD_ATTR_LEN (54) - * octets */ - const u8 *os_version; /* 4 octets */ - const u8 *wps_state; /* 1 octet */ - const u8 *authenticator; /* WPS_AUTHENTICATOR_LEN (8) octets */ - const u8 *r_hash1; /* WPS_HASH_LEN (32) octets */ - const u8 *r_hash2; /* WPS_HASH_LEN (32) octets */ - const u8 *e_hash1; /* WPS_HASH_LEN (32) octets */ - const u8 *e_hash2; /* WPS_HASH_LEN (32) octets */ - const u8 *r_snonce1; /* WPS_SECRET_NONCE_LEN (16) octets */ - const u8 *r_snonce2; /* WPS_SECRET_NONCE_LEN (16) octets */ - const u8 *e_snonce1; /* WPS_SECRET_NONCE_LEN (16) octets */ - const u8 *e_snonce2; /* WPS_SECRET_NONCE_LEN (16) octets */ - const u8 *key_wrap_auth; /* WPS_KWA_LEN (8) octets */ - const u8 *auth_type; /* 2 octets */ - const u8 *encr_type; /* 2 octets */ - const u8 *network_idx; /* 1 octet */ - const u8 *network_key_idx; /* 1 octet */ - const u8 *mac_addr; /* ETH_ALEN (6) octets */ - const u8 *key_prov_auto; /* 1 octet (Bool) */ - const u8 *dot1x_enabled; /* 1 octet (Bool) */ - const u8 *selected_registrar; /* 1 octet (Bool) */ - const u8 *request_type; /* 1 octet */ - const u8 *response_type; /* 1 octet */ - const u8 *ap_setup_locked; /* 1 octet */ - const u8 *settings_delay_time; /* 1 octet */ - const u8 *network_key_shareable; /* 1 octet (Bool) */ - const u8 *request_to_enroll; /* 1 octet (Bool) */ - - /* variable length fields */ - const u8 *manufacturer; - size_t manufacturer_len; - const u8 *model_name; - size_t model_name_len; - const u8 *model_number; - size_t model_number_len; - const u8 *serial_number; - size_t serial_number_len; - const u8 *dev_name; - size_t dev_name_len; - const u8 *public_key; - size_t public_key_len; - const u8 *encr_settings; - size_t encr_settings_len; - const u8 *ssid; /* <= 32 octets */ - size_t ssid_len; - const u8 *network_key; /* <= 64 octets */ - size_t network_key_len; - const u8 *eap_type; /* <= 8 octets */ - size_t eap_type_len; - const u8 *eap_identity; /* <= 64 octets */ - size_t eap_identity_len; - const u8 *authorized_macs; /* <= 30 octets */ - size_t authorized_macs_len; - const u8 *sec_dev_type_list; /* <= 128 octets */ - size_t sec_dev_type_list_len; - - /* attributes that can occur multiple times */ -#define MAX_CRED_COUNT 10 - const u8 *cred[MAX_CRED_COUNT]; - size_t cred_len[MAX_CRED_COUNT]; - size_t num_cred; - -#define MAX_REQ_DEV_TYPE_COUNT 10 - const u8 *req_dev_type[MAX_REQ_DEV_TYPE_COUNT]; - size_t num_req_dev_type; - - const u8 *vendor_ext[MAX_WPS_PARSE_VENDOR_EXT]; - size_t vendor_ext_len[MAX_WPS_PARSE_VENDOR_EXT]; - size_t num_vendor_ext; + struct wps_nfc_pw_token *nfc_pw_token; }; + /* wps_common.c */ void wps_kdf(const u8 *key, const u8 *label_prefix, size_t label_prefix_len, const char *label, u8 *res, size_t res_len); @@ -223,22 +137,18 @@ struct wpabuf * wps_decrypt_encr_settings(struct wps_data *wps, const u8 *encr, size_t encr_len); void wps_fail_event(struct wps_context *wps, enum wps_msg_type msg, - u16 config_error, u16 error_indication); -void wps_success_event(struct wps_context *wps); -void wps_pwd_auth_fail_event(struct wps_context *wps, int enrollee, int part); + u16 config_error, u16 error_indication, const u8 *mac_addr); +void wps_success_event(struct wps_context *wps, const u8 *mac_addr); +void wps_pwd_auth_fail_event(struct wps_context *wps, int enrollee, int part, + const u8 *mac_addr); void wps_pbc_overlap_event(struct wps_context *wps); void wps_pbc_timeout_event(struct wps_context *wps); - -extern struct oob_device_data oob_ufd_device_data; -extern struct oob_device_data oob_nfc_device_data; -extern struct oob_nfc_device_data oob_nfc_pn531_device_data; +void wps_pbc_active_event(struct wps_context *wps); +void wps_pbc_disable_event(struct wps_context *wps); struct wpabuf * wps_build_wsc_ack(struct wps_data *wps); struct wpabuf * wps_build_wsc_nack(struct wps_data *wps); -/* wps_attr_parse.c */ -int wps_parse_msg(const struct wpabuf *msg, struct wps_parse_attr *attr); - /* wps_attr_build.c */ int wps_build_public_key(struct wps_data *wps, struct wpabuf *msg); int wps_build_req_type(struct wpabuf *msg, enum wps_request_type type); @@ -261,8 +171,13 @@ int wps_build_encr_type_flags(struct wps_data *wps, struct wpabuf *msg); int wps_build_conn_type_flags(struct wps_data *wps, struct wpabuf *msg); int wps_build_assoc_state(struct wps_data *wps, struct wpabuf *msg); -int wps_build_oob_dev_password(struct wpabuf *msg, struct wps_context *wps); +int wps_build_oob_dev_pw(struct wpabuf *msg, u16 dev_pw_id, + const struct wpabuf *pubkey, const u8 *dev_pw, + size_t dev_pw_len); struct wpabuf * wps_ie_encapsulate(struct wpabuf *data); +int wps_build_mac_addr(struct wpabuf *msg, const u8 *addr); +int wps_build_rf_bands_attr(struct wpabuf *msg, u8 rf_bands); +int wps_build_ap_channel(struct wpabuf *msg, u16 ap_channel); /* wps_attr_process.c */ int wps_process_authenticator(struct wps_data *wps, const u8 *authenticator, @@ -290,13 +205,12 @@ int wps_build_cred(struct wps_data *wps, struct wpabuf *msg); int wps_device_store(struct wps_registrar *reg, struct wps_device_data *dev, const u8 *uuid); -void wps_registrar_selected_registrar_changed(struct wps_registrar *reg); +void wps_registrar_selected_registrar_changed(struct wps_registrar *reg, + u16 dev_pw_id); const u8 * wps_authorized_macs(struct wps_registrar *reg, size_t *count); int wps_registrar_pbc_overlap(struct wps_registrar *reg, const u8 *addr, const u8 *uuid_e); - -/* ndef.c */ -struct wpabuf * ndef_parse_wifi(struct wpabuf *buf); -struct wpabuf * ndef_build_wifi(struct wpabuf *buf); +void wps_registrar_remove_nfc_pw_token(struct wps_registrar *reg, + struct wps_nfc_pw_token *token); #endif /* WPS_I_H */ diff -Nru wpa-1.0/src/wps/wps_nfc.c wpa-2.1/src/wps/wps_nfc.c --- wpa-1.0/src/wps/wps_nfc.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/wps/wps_nfc.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,117 +0,0 @@ -/* - * NFC routines for Wi-Fi Protected Setup - * Copyright (c) 2009, Masashi Honma - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#include "includes.h" -#include "common.h" - -#include "wps/wps.h" -#include "wps_i.h" - - -struct wps_nfc_data { - struct oob_nfc_device_data *oob_nfc_dev; -}; - - -static void * init_nfc(struct wps_context *wps, - struct oob_device_data *oob_dev, int registrar) -{ - struct oob_nfc_device_data *oob_nfc_dev; - struct wps_nfc_data *data; - - oob_nfc_dev = wps_get_oob_nfc_device(oob_dev->device_name); - if (oob_nfc_dev == NULL) { - wpa_printf(MSG_ERROR, "WPS (NFC): Unknown NFC device (%s)", - oob_dev->device_name); - return NULL; - } - - if (oob_nfc_dev->init_func(oob_dev->device_path) < 0) - return NULL; - - data = os_zalloc(sizeof(*data)); - if (data == NULL) { - wpa_printf(MSG_ERROR, "WPS (NFC): Failed to allocate " - "nfc data area"); - return NULL; - } - data->oob_nfc_dev = oob_nfc_dev; - return data; -} - - -static struct wpabuf * read_nfc(void *priv) -{ - struct wps_nfc_data *data = priv; - struct wpabuf *wifi, *buf; - char *raw_data; - size_t len; - - raw_data = data->oob_nfc_dev->read_func(&len); - if (raw_data == NULL) - return NULL; - - wifi = wpabuf_alloc_copy(raw_data, len); - os_free(raw_data); - if (wifi == NULL) { - wpa_printf(MSG_ERROR, "WPS (NFC): Failed to allocate " - "nfc read area"); - return NULL; - } - - buf = ndef_parse_wifi(wifi); - wpabuf_free(wifi); - if (buf == NULL) - wpa_printf(MSG_ERROR, "WPS (NFC): Failed to unwrap"); - return buf; -} - - -static int write_nfc(void *priv, struct wpabuf *buf) -{ - struct wps_nfc_data *data = priv; - struct wpabuf *wifi; - int ret; - - wifi = ndef_build_wifi(buf); - if (wifi == NULL) { - wpa_printf(MSG_ERROR, "WPS (NFC): Failed to wrap"); - return -1; - } - - ret = data->oob_nfc_dev->write_func(wpabuf_mhead(wifi), - wpabuf_len(wifi)); - wpabuf_free(wifi); - return ret; -} - - -static void deinit_nfc(void *priv) -{ - struct wps_nfc_data *data = priv; - - data->oob_nfc_dev->deinit_func(); - - os_free(data); -} - - -struct oob_device_data oob_nfc_device_data = { - .device_name = NULL, - .device_path = NULL, - .init_func = init_nfc, - .read_func = read_nfc, - .write_func = write_nfc, - .deinit_func = deinit_nfc, -}; diff -Nru wpa-1.0/src/wps/wps_nfc_pn531.c wpa-2.1/src/wps/wps_nfc_pn531.c --- wpa-1.0/src/wps/wps_nfc_pn531.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/wps/wps_nfc_pn531.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,113 +0,0 @@ -/* - * NFC PN531 routines for Wi-Fi Protected Setup - * Copyright (c) 2009, Masashi Honma - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#include "includes.h" -#include "common.h" - -#include "wps/wps.h" -#include "wps_i.h" - -#include "WpsNfcType.h" -#include "WpsNfc.h" - - -static int init_nfc_pn531(char *path) -{ - u32 ret; - - ret = WpsNfcInit(); - if (ret != WPS_NFCLIB_ERR_SUCCESS) { - wpa_printf(MSG_ERROR, "WPS (PN531): Failed to initialize " - "NFC Library: 0x%08x", ret); - return -1; - } - - ret = WpsNfcOpenDevice((int8 *) path); - if (ret != WPS_NFCLIB_ERR_SUCCESS) { - wpa_printf(MSG_ERROR, "WPS (PN531): Failed to open " - "NFC Device(%s): 0x%08x", path, ret); - goto fail; - } - - ret = WpsNfcTokenDiscovery(); - if (ret != WPS_NFCLIB_ERR_SUCCESS) { - wpa_printf(MSG_ERROR, "WPS (PN531): Failed to discover " - "token: 0x%08x", ret); - WpsNfcCloseDevice(); - goto fail; - } - - return 0; - -fail: - WpsNfcDeinit(); - return -1; -} - - -static void * read_nfc_pn531(size_t *size) -{ - uint32 len; - u32 ret; - int8 *data; - - ret = WpsNfcRawReadToken(&data, &len); - if (ret != WPS_NFCLIB_ERR_SUCCESS) { - wpa_printf(MSG_ERROR, "WPS (PN531): Failed to read: 0x%08x", - ret); - return NULL; - } - - *size = len; - return data; -} - - -static int write_nfc_pn531(void *data, size_t len) -{ - u32 ret; - - ret = WpsNfcRawWriteToken(data, len); - if (ret != WPS_NFCLIB_ERR_SUCCESS) { - wpa_printf(MSG_ERROR, "WPS (PN531): Failed to write: 0x%08x", - ret); - return -1; - } - - return 0; -} - - -static void deinit_nfc_pn531(void) -{ - u32 ret; - - ret = WpsNfcCloseDevice(); - if (ret != WPS_NFCLIB_ERR_SUCCESS) - wpa_printf(MSG_ERROR, "WPS (PN531): Failed to close " - "NFC Device: 0x%08x", ret); - - ret = WpsNfcDeinit(); - if (ret != WPS_NFCLIB_ERR_SUCCESS) - wpa_printf(MSG_ERROR, "WPS (PN531): Failed to deinitialize " - "NFC Library: 0x%08x", ret); -} - - -struct oob_nfc_device_data oob_nfc_pn531_device_data = { - .init_func = init_nfc_pn531, - .read_func = read_nfc_pn531, - .write_func = write_nfc_pn531, - .deinit_func = deinit_nfc_pn531, -}; diff -Nru wpa-1.0/src/wps/wps_registrar.c wpa-2.1/src/wps/wps_registrar.c --- wpa-1.0/src/wps/wps_registrar.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/wps/wps_registrar.c 2014-02-04 11:23:35.000000000 +0000 @@ -1,15 +1,9 @@ /* * Wi-Fi Protected Setup - Registrar - * Copyright (c) 2008-2009, Jouni Malinen + * Copyright (c) 2008-2013, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "utils/includes.h" @@ -32,6 +26,55 @@ #define WPS_WORKAROUNDS #endif /* CONFIG_WPS_STRICT */ +#ifdef CONFIG_WPS_NFC + +struct wps_nfc_pw_token { + struct dl_list list; + u8 pubkey_hash[WPS_OOB_PUBKEY_HASH_LEN]; + unsigned int peer_pk_hash_known:1; + u16 pw_id; + u8 dev_pw[WPS_OOB_DEVICE_PASSWORD_LEN * 2 + 1]; + size_t dev_pw_len; + int pk_hash_provided_oob; /* whether own PK hash was provided OOB */ +}; + + +static void wps_remove_nfc_pw_token(struct wps_nfc_pw_token *token) +{ + dl_list_del(&token->list); + os_free(token); +} + + +static void wps_free_nfc_pw_tokens(struct dl_list *tokens, u16 pw_id) +{ + struct wps_nfc_pw_token *token, *prev; + dl_list_for_each_safe(token, prev, tokens, struct wps_nfc_pw_token, + list) { + if (pw_id == 0 || pw_id == token->pw_id) + wps_remove_nfc_pw_token(token); + } +} + + +static struct wps_nfc_pw_token * wps_get_nfc_pw_token(struct dl_list *tokens, + u16 pw_id) +{ + struct wps_nfc_pw_token *token; + dl_list_for_each(token, tokens, struct wps_nfc_pw_token, list) { + if (pw_id == token->pw_id) + return token; + } + return NULL; +} + +#else /* CONFIG_WPS_NFC */ + +#define wps_free_nfc_pw_tokens(t, p) do { } while (0) + +#endif /* CONFIG_WPS_NFC */ + + struct wps_uuid_pin { struct dl_list list; u8 uuid[WPS_UUID_LEN]; @@ -41,7 +84,7 @@ #define PIN_LOCKED BIT(0) #define PIN_EXPIRES BIT(1) int flags; - struct os_time expiration; + struct os_reltime expiration; u8 enrollee_addr[ETH_ALEN]; }; @@ -72,7 +115,7 @@ struct wps_pbc_session *next; u8 addr[ETH_ALEN]; u8 uuid_e[WPS_UUID_LEN]; - struct os_time timestamp; + struct os_reltime timestamp; }; @@ -101,14 +144,15 @@ int pbc; int selected_registrar; - int (*new_psk_cb)(void *ctx, const u8 *mac_addr, const u8 *psk, - size_t psk_len); + int (*new_psk_cb)(void *ctx, const u8 *mac_addr, const u8 *p2p_dev_addr, + const u8 *psk, size_t psk_len); int (*set_ie_cb)(void *ctx, struct wpabuf *beacon_ie, struct wpabuf *probe_resp_ie); void (*pin_needed_cb)(void *ctx, const u8 *uuid_e, const struct wps_device_data *dev); void (*reg_success_cb)(void *ctx, const u8 *mac_addr, - const u8 *uuid_e); + const u8 *uuid_e, const u8 *dev_pw, + size_t dev_pw_len); void (*set_sel_reg_cb)(void *ctx, int sel_reg, u16 dev_passwd_id, u16 sel_reg_config_methods); void (*enrollee_seen_cb)(void *ctx, const u8 *addr, const u8 *uuid_e, @@ -118,6 +162,7 @@ void *cb_ctx; struct dl_list pins; + struct dl_list nfc_pw_tokens; struct wps_pbc_session *pbc_sessions; int skip_cred_build; @@ -128,6 +173,7 @@ int sel_reg_config_methods_override; int static_wep_only; int dualband; + int force_per_enrollee_psk; struct wps_registrar_device *devices; @@ -137,6 +183,11 @@ u8 authorized_macs_union[WPS_MAX_AUTHORIZED_MACS][ETH_ALEN]; u8 p2p_dev_addr[ETH_ALEN]; + + u8 pbc_ignore_uuid[WPS_UUID_LEN]; +#ifdef WPS_WORKAROUNDS + struct os_reltime pbc_ignore_start; +#endif /* WPS_WORKAROUNDS */ }; @@ -144,6 +195,8 @@ static void wps_registrar_pbc_timeout(void *eloop_ctx, void *timeout_ctx); static void wps_registrar_set_selected_timeout(void *eloop_ctx, void *timeout_ctx); +static void wps_registrar_remove_pin(struct wps_registrar *reg, + struct wps_uuid_pin *pin); static void wps_registrar_add_authorized_mac(struct wps_registrar *reg, @@ -262,9 +315,9 @@ const u8 *addr, const u8 *uuid_e) { struct wps_pbc_session *pbc, *prev = NULL; - struct os_time now; + struct os_reltime now; - os_get_time(&now); + os_get_reltime(&now); pbc = reg->pbc_sessions; while (pbc) { @@ -298,7 +351,8 @@ pbc = pbc->next; while (pbc) { - if (now.sec > pbc->timestamp.sec + WPS_PBC_WALK_TIME) { + if (os_reltime_expired(&now, &pbc->timestamp, + WPS_PBC_WALK_TIME)) { prev->next = NULL; wps_free_pbc_sessions(pbc); break; @@ -346,9 +400,9 @@ int count = 0; struct wps_pbc_session *pbc; struct wps_pbc_session *first = NULL; - struct os_time now; + struct os_reltime now; - os_get_time(&now); + os_get_reltime(&now); wpa_printf(MSG_DEBUG, "WPS: Checking active PBC sessions for overlap"); @@ -364,9 +418,9 @@ MAC2STR(pbc->addr)); wpa_hexdump(MSG_DEBUG, "WPS: UUID-E", pbc->uuid_e, WPS_UUID_LEN); - if (now.sec > pbc->timestamp.sec + WPS_PBC_WALK_TIME) { - wpa_printf(MSG_DEBUG, "WPS: PBC walk time has " - "expired"); + if (os_reltime_expired(&now, &pbc->timestamp, + WPS_PBC_WALK_TIME)) { + wpa_printf(MSG_DEBUG, "WPS: PBC walk time has expired"); break; } if (first && @@ -485,12 +539,16 @@ { *methods |= WPS_CONFIG_PUSHBUTTON; #ifdef CONFIG_WPS2 - if (conf_methods & WPS_CONFIG_VIRT_PUSHBUTTON) + if ((conf_methods & WPS_CONFIG_VIRT_PUSHBUTTON) == + WPS_CONFIG_VIRT_PUSHBUTTON) *methods |= WPS_CONFIG_VIRT_PUSHBUTTON; - if (conf_methods & WPS_CONFIG_PHY_PUSHBUTTON) + if ((conf_methods & WPS_CONFIG_PHY_PUSHBUTTON) == + WPS_CONFIG_PHY_PUSHBUTTON) *methods |= WPS_CONFIG_PHY_PUSHBUTTON; - if (!(*methods & (WPS_CONFIG_VIRT_PUSHBUTTON | - WPS_CONFIG_PHY_PUSHBUTTON))) { + if ((*methods & WPS_CONFIG_VIRT_PUSHBUTTON) != + WPS_CONFIG_VIRT_PUSHBUTTON && + (*methods & WPS_CONFIG_PHY_PUSHBUTTON) != + WPS_CONFIG_PHY_PUSHBUTTON) { /* * Required to include virtual/physical flag, but we were not * configured with push button type, so have to default to one @@ -592,6 +650,7 @@ return NULL; dl_list_init(®->pins); + dl_list_init(®->nfc_pw_tokens); reg->wps = wps; reg->new_psk_cb = cfg->new_psk_cb; reg->set_ie_cb = cfg->set_ie_cb; @@ -614,6 +673,7 @@ reg->sel_reg_config_methods_override = -1; reg->static_wep_only = cfg->static_wep_only; reg->dualband = cfg->dualband; + reg->force_per_enrollee_psk = cfg->force_per_enrollee_psk; if (wps_set_ie(reg)) { wps_registrar_deinit(reg); @@ -635,6 +695,7 @@ eloop_cancel_timeout(wps_registrar_pbc_timeout, reg, NULL); eloop_cancel_timeout(wps_registrar_set_selected_timeout, reg, NULL); wps_free_pins(®->pins); + wps_free_nfc_pw_tokens(®->nfc_pw_tokens, 0); wps_free_pbc_sessions(reg->pbc_sessions); wpabuf_free(reg->extra_cred); wps_free_devices(reg->devices); @@ -642,6 +703,21 @@ } +static void wps_registrar_invalidate_unused(struct wps_registrar *reg) +{ + struct wps_uuid_pin *pin; + + dl_list_for_each(pin, ®->pins, struct wps_uuid_pin, list) { + if (pin->wildcard_uuid == 1 && !(pin->flags & PIN_LOCKED)) { + wpa_printf(MSG_DEBUG, "WPS: Invalidate previously " + "configured wildcard PIN"); + wps_registrar_remove_pin(reg, pin); + break; + } + } +} + + /** * wps_registrar_add_pin - Configure a new PIN for Registrar * @reg: Registrar data from wps_registrar_init() @@ -677,10 +753,13 @@ if (timeout) { p->flags |= PIN_EXPIRES; - os_get_time(&p->expiration); + os_get_reltime(&p->expiration); p->expiration.sec += timeout; } + if (p->wildcard_uuid) + wps_registrar_invalidate_unused(reg); + dl_list_add(®->pins, &p->list); wpa_printf(MSG_DEBUG, "WPS: A new PIN configured (timeout=%d)", @@ -694,7 +773,7 @@ else wps_registrar_add_authorized_mac( reg, (u8 *) "\xff\xff\xff\xff\xff\xff"); - wps_registrar_selected_registrar_changed(reg); + wps_registrar_selected_registrar_changed(reg, 0); eloop_cancel_timeout(wps_registrar_set_selected_timeout, reg, NULL); eloop_register_timeout(WPS_PBC_WALK_TIME, 0, wps_registrar_set_selected_timeout, @@ -716,20 +795,20 @@ addr = pin->enrollee_addr; wps_registrar_remove_authorized_mac(reg, addr); wps_remove_pin(pin); - wps_registrar_selected_registrar_changed(reg); + wps_registrar_selected_registrar_changed(reg, 0); } static void wps_registrar_expire_pins(struct wps_registrar *reg) { struct wps_uuid_pin *pin, *prev; - struct os_time now; + struct os_reltime now; - os_get_time(&now); + os_get_reltime(&now); dl_list_for_each_safe(pin, prev, ®->pins, struct wps_uuid_pin, list) { if ((pin->flags & PIN_EXPIRES) && - os_time_before(&pin->expiration, &now)) { + os_reltime_before(&pin->expiration, &now)) { wpa_hexdump(MSG_DEBUG, "WPS: Expired PIN for UUID", pin->uuid, WPS_UUID_LEN); wps_registrar_remove_pin(reg, pin); @@ -741,14 +820,22 @@ /** * wps_registrar_invalidate_wildcard_pin - Invalidate a wildcard PIN * @reg: Registrar data from wps_registrar_init() + * @dev_pw: PIN to search for or %NULL to match any + * @dev_pw_len: Length of dev_pw in octets * Returns: 0 on success, -1 if not wildcard PIN is enabled */ -static int wps_registrar_invalidate_wildcard_pin(struct wps_registrar *reg) +static int wps_registrar_invalidate_wildcard_pin(struct wps_registrar *reg, + const u8 *dev_pw, + size_t dev_pw_len) { struct wps_uuid_pin *pin, *prev; dl_list_for_each_safe(pin, prev, ®->pins, struct wps_uuid_pin, list) { + if (dev_pw && pin->pin && + (dev_pw_len != pin->pin_len || + os_memcmp(dev_pw, pin->pin, dev_pw_len) != 0)) + continue; /* different PIN */ if (pin->wildcard_uuid) { wpa_hexdump(MSG_DEBUG, "WPS: Invalidated PIN for UUID", pin->uuid, WPS_UUID_LEN); @@ -804,10 +891,11 @@ /* Check for wildcard UUIDs since none of the UUID-specific * PINs matched */ dl_list_for_each(pin, ®->pins, struct wps_uuid_pin, list) { - if (pin->wildcard_uuid == 1) { + if (pin->wildcard_uuid == 1 || + pin->wildcard_uuid == 2) { wpa_printf(MSG_DEBUG, "WPS: Found a wildcard " "PIN. Assigned it for this UUID-E"); - pin->wildcard_uuid = 2; + pin->wildcard_uuid++; os_memcpy(pin->uuid, uuid, WPS_UUID_LEN); found = pin; break; @@ -849,7 +937,7 @@ dl_list_for_each(pin, ®->pins, struct wps_uuid_pin, list) { if (os_memcmp(pin->uuid, uuid, WPS_UUID_LEN) == 0) { - if (pin->wildcard_uuid == 2) { + if (pin->wildcard_uuid == 3) { wpa_printf(MSG_DEBUG, "WPS: Invalidating used " "wildcard PIN"); return wps_registrar_invalidate_pin(reg, uuid); @@ -870,7 +958,7 @@ os_memset(reg->p2p_dev_addr, 0, ETH_ALEN); wps_registrar_remove_authorized_mac(reg, (u8 *) "\xff\xff\xff\xff\xff\xff"); - wps_registrar_selected_registrar_changed(reg); + wps_registrar_selected_registrar_changed(reg, 0); } @@ -918,8 +1006,9 @@ os_memset(reg->p2p_dev_addr, 0, ETH_ALEN); wps_registrar_add_authorized_mac(reg, (u8 *) "\xff\xff\xff\xff\xff\xff"); - wps_registrar_selected_registrar_changed(reg); + wps_registrar_selected_registrar_changed(reg, 0); + wps_pbc_active_event(reg->wps); eloop_cancel_timeout(wps_registrar_set_selected_timeout, reg, NULL); eloop_cancel_timeout(wps_registrar_pbc_timeout, reg, NULL); eloop_register_timeout(WPS_PBC_WALK_TIME, 0, wps_registrar_pbc_timeout, @@ -933,6 +1022,7 @@ wpa_printf(MSG_DEBUG, "WPS: PBC completed - stopping PBC mode"); eloop_cancel_timeout(wps_registrar_pbc_timeout, reg, NULL); wps_registrar_stop_pbc(reg); + wps_pbc_disable_event(reg->wps); } @@ -941,19 +1031,31 @@ wpa_printf(MSG_DEBUG, "WPS: PIN completed using internal Registrar"); eloop_cancel_timeout(wps_registrar_set_selected_timeout, reg, NULL); reg->selected_registrar = 0; - wps_registrar_selected_registrar_changed(reg); + wps_registrar_selected_registrar_changed(reg, 0); } -void wps_registrar_complete(struct wps_registrar *registrar, const u8 *uuid_e) +void wps_registrar_complete(struct wps_registrar *registrar, const u8 *uuid_e, + const u8 *dev_pw, size_t dev_pw_len) { if (registrar->pbc) { wps_registrar_remove_pbc_session(registrar, uuid_e, NULL); wps_registrar_pbc_completed(registrar); +#ifdef WPS_WORKAROUNDS + os_get_reltime(®istrar->pbc_ignore_start); +#endif /* WPS_WORKAROUNDS */ + os_memcpy(registrar->pbc_ignore_uuid, uuid_e, WPS_UUID_LEN); } else { wps_registrar_pin_completed(registrar); } + + if (dev_pw && + wps_registrar_invalidate_wildcard_pin(registrar, dev_pw, + dev_pw_len) == 0) { + wpa_hexdump_key(MSG_DEBUG, "WPS: Invalidated wildcard PIN", + dev_pw, dev_pw_len); + } } @@ -968,7 +1070,7 @@ /* PIN Method */ wpa_printf(MSG_DEBUG, "WPS: PIN is set - cancelling it"); wps_registrar_pin_completed(reg); - wps_registrar_invalidate_wildcard_pin(reg); + wps_registrar_invalidate_wildcard_pin(reg, NULL, 0); return 1; } return 0; @@ -990,6 +1092,7 @@ int p2p_wildcard) { struct wps_parse_attr attr; + int skip_add = 0; wpa_hexdump_buf(MSG_MSGDUMP, "WPS: Probe Request with WPS data received", @@ -1041,7 +1144,24 @@ wpa_hexdump(MSG_DEBUG, "WPS: UUID-E from Probe Request", attr.uuid_e, WPS_UUID_LEN); - wps_registrar_add_pbc_session(reg, addr, attr.uuid_e); +#ifdef WPS_WORKAROUNDS + if (reg->pbc_ignore_start.sec && + os_memcmp(attr.uuid_e, reg->pbc_ignore_uuid, WPS_UUID_LEN) == 0) { + struct os_reltime now, dur; + os_get_reltime(&now); + os_reltime_sub(&now, ®->pbc_ignore_start, &dur); + if (dur.sec >= 0 && dur.sec < 5) { + wpa_printf(MSG_DEBUG, "WPS: Ignore PBC activation " + "based on Probe Request from the Enrollee " + "that just completed PBC provisioning"); + skip_add = 1; + } else + reg->pbc_ignore_start.sec = 0; + } +#endif /* WPS_WORKAROUNDS */ + + if (!skip_add) + wps_registrar_add_pbc_session(reg, addr, attr.uuid_e); if (wps_registrar_pbc_overlap(reg, addr, attr.uuid_e)) { wpa_printf(MSG_DEBUG, "WPS: PBC session overlap detected"); reg->force_pbc_overlap = 1; @@ -1051,12 +1171,13 @@ static int wps_cb_new_psk(struct wps_registrar *reg, const u8 *mac_addr, - const u8 *psk, size_t psk_len) + const u8 *p2p_dev_addr, const u8 *psk, size_t psk_len) { if (reg->new_psk_cb == NULL) return 0; - return reg->new_psk_cb(reg->cb_ctx, mac_addr, psk, psk_len); + return reg->new_psk_cb(reg->cb_ctx, mac_addr, p2p_dev_addr, psk, + psk_len); } @@ -1071,12 +1192,13 @@ static void wps_cb_reg_success(struct wps_registrar *reg, const u8 *mac_addr, - const u8 *uuid_e) + const u8 *uuid_e, const u8 *dev_pw, + size_t dev_pw_len) { if (reg->reg_success_cb == NULL) return; - reg->reg_success_cb(reg->cb_ctx, mac_addr, uuid_e); + reg->reg_success_cb(reg->cb_ctx, mac_addr, uuid_e, dev_pw, dev_pw_len); } @@ -1153,7 +1275,7 @@ wps_build_sel_reg_dev_password_id(reg, beacon) || wps_build_sel_reg_config_methods(reg, beacon) || wps_build_sel_pbc_reg_uuid_e(reg, beacon) || - (reg->dualband && wps_build_rf_bands(®->wps->dev, beacon)) || + (reg->dualband && wps_build_rf_bands(®->wps->dev, beacon, 0)) || wps_build_wfa_ext(beacon, 0, auth_macs, count) || wps_build_vendor_ext(®->wps->dev, beacon)) { wpabuf_free(beacon); @@ -1183,7 +1305,7 @@ wps_build_uuid_e(probe, reg->wps->uuid) || wps_build_device_attrs(®->wps->dev, probe) || wps_build_probe_config_methods(reg, probe) || - wps_build_rf_bands(®->wps->dev, probe) || + (reg->dualband && wps_build_rf_bands(®->wps->dev, probe, 0)) || wps_build_wfa_ext(probe, 0, auth_macs, count) || wps_build_vendor_ext(®->wps->dev, probe)) { wpabuf_free(beacon); @@ -1238,13 +1360,42 @@ wpa_printf(MSG_DEBUG, "WPS: Use default PIN for PBC"); pin = (const u8 *) "00000000"; pin_len = 8; +#ifdef CONFIG_WPS_NFC + } else if (wps->nfc_pw_token) { + if (wps->nfc_pw_token->pw_id == DEV_PW_NFC_CONNECTION_HANDOVER) + { + wpa_printf(MSG_DEBUG, "WPS: Using NFC connection " + "handover and abbreviated WPS handshake " + "without Device Password"); + return 0; + } + wpa_printf(MSG_DEBUG, "WPS: Use OOB Device Password from NFC " + "Password Token"); + pin = wps->nfc_pw_token->dev_pw; + pin_len = wps->nfc_pw_token->dev_pw_len; + } else if (wps->dev_pw_id >= 0x10 && + wps->wps->ap_nfc_dev_pw_id == wps->dev_pw_id && + wps->wps->ap_nfc_dev_pw) { + wpa_printf(MSG_DEBUG, "WPS: Use OOB Device Password from own NFC Password Token"); + pin = wpabuf_head(wps->wps->ap_nfc_dev_pw); + pin_len = wpabuf_len(wps->wps->ap_nfc_dev_pw); +#endif /* CONFIG_WPS_NFC */ } else { pin = wps_registrar_get_pin(wps->wps->registrar, wps->uuid_e, &pin_len); + if (pin && wps->dev_pw_id >= 0x10) { + wpa_printf(MSG_DEBUG, "WPS: No match for OOB Device " + "Password ID, but PIN found"); + /* + * See whether Enrollee is willing to use PIN instead. + */ + wps->dev_pw_id = DEV_PW_DEFAULT; + } } if (pin == NULL) { wpa_printf(MSG_DEBUG, "WPS: No Device Password available for " - "the Enrollee"); + "the Enrollee (context %p registrar %p)", + wps->wps, wps->wps->registrar); wps_cb_pin_needed(wps->wps->registrar, wps->uuid_e, &wps->peer_dev); return -1; @@ -1401,18 +1552,6 @@ } -static int wps_build_cred_mac_addr(struct wpabuf *msg, - const struct wps_credential *cred) -{ - wpa_printf(MSG_DEBUG, "WPS: * MAC Address (" MACSTR ")", - MAC2STR(cred->mac_addr)); - wpabuf_put_be16(msg, ATTR_MAC_ADDR); - wpabuf_put_be16(msg, ETH_ALEN); - wpabuf_put_data(msg, cred->mac_addr, ETH_ALEN); - return 0; -} - - static int wps_build_credential(struct wpabuf *msg, const struct wps_credential *cred) { @@ -1421,7 +1560,7 @@ wps_build_cred_auth_type(msg, cred) || wps_build_cred_encr_type(msg, cred) || wps_build_cred_network_key(msg, cred) || - wps_build_cred_mac_addr(msg, cred)) + wps_build_mac_addr(msg, cred->mac_addr)) return -1; return 0; } @@ -1525,13 +1664,15 @@ wps->new_psk, wps->new_psk_len); os_memcpy(wps->cred.key, wps->new_psk, wps->new_psk_len); wps->cred.key_len = wps->new_psk_len; - } else if (wps->use_psk_key && wps->wps->psk_set) { + } else if (!wps->wps->registrar->force_per_enrollee_psk && + wps->use_psk_key && wps->wps->psk_set) { char hex[65]; wpa_printf(MSG_DEBUG, "WPS: Use PSK format for Network Key"); wpa_snprintf_hex(hex, sizeof(hex), wps->wps->psk, 32); os_memcpy(wps->cred.key, hex, 32 * 2); wps->cred.key_len = 32 * 2; - } else if (wps->wps->network_key) { + } else if (!wps->wps->registrar->force_per_enrollee_psk && + wps->wps->network_key) { os_memcpy(wps->cred.key, wps->wps->network_key, wps->wps->network_key_len); wps->cred.key_len = wps->wps->network_key_len; @@ -1651,6 +1792,7 @@ static struct wpabuf * wps_build_m2(struct wps_data *wps) { struct wpabuf *msg; + int config_in_m2 = 0; if (random_get_bytes(wps->nonce_r, WPS_NONCE_LEN) < 0) return NULL; @@ -1675,19 +1817,47 @@ wps_build_conn_type_flags(wps, msg) || wps_build_config_methods_r(wps->wps->registrar, msg) || wps_build_device_attrs(&wps->wps->dev, msg) || - wps_build_rf_bands(&wps->wps->dev, msg) || + wps_build_rf_bands(&wps->wps->dev, msg, + wps->wps->rf_band_cb(wps->wps->cb_ctx)) || wps_build_assoc_state(wps, msg) || wps_build_config_error(msg, WPS_CFG_NO_ERROR) || wps_build_dev_password_id(msg, wps->dev_pw_id) || wps_build_os_version(&wps->wps->dev, msg) || - wps_build_wfa_ext(msg, 0, NULL, 0) || - wps_build_authenticator(wps, msg)) { + wps_build_wfa_ext(msg, 0, NULL, 0)) { + wpabuf_free(msg); + return NULL; + } + +#ifdef CONFIG_WPS_NFC + if (wps->nfc_pw_token && wps->nfc_pw_token->pk_hash_provided_oob && + wps->nfc_pw_token->pw_id == DEV_PW_NFC_CONNECTION_HANDOVER) { + /* + * Use abbreviated handshake since public key hash allowed + * Enrollee to validate our public key similarly to how Enrollee + * public key was validated. There is no need to validate Device + * Password in this case. + */ + struct wpabuf *plain = wpabuf_alloc(500); + if (plain == NULL || + wps_build_cred(wps, plain) || + wps_build_key_wrap_auth(wps, plain) || + wps_build_encr_settings(wps, msg, plain)) { + wpabuf_free(msg); + wpabuf_free(plain); + return NULL; + } + wpabuf_free(plain); + config_in_m2 = 1; + } +#endif /* CONFIG_WPS_NFC */ + + if (wps_build_authenticator(wps, msg)) { wpabuf_free(msg); return NULL; } wps->int_reg = 1; - wps->state = RECV_M3; + wps->state = config_in_m2 ? RECV_DONE : RECV_M3; return msg; } @@ -1716,7 +1886,8 @@ wps_build_conn_type_flags(wps, msg) || wps_build_config_methods_r(wps->wps->registrar, msg) || wps_build_device_attrs(&wps->wps->dev, msg) || - wps_build_rf_bands(&wps->wps->dev, msg) || + wps_build_rf_bands(&wps->wps->dev, msg, + wps->wps->rf_band_cb(wps->wps->cb_ctx)) || wps_build_assoc_state(wps, msg) || wps_build_config_error(msg, err) || wps_build_os_version(&wps->wps->dev, msg) || @@ -2054,7 +2225,7 @@ wpa_printf(MSG_DEBUG, "WPS: E-Hash1 derived from E-S1 does " "not match with the pre-committed value"); wps->config_error = WPS_CFG_DEV_PASSWORD_AUTH_FAILURE; - wps_pwd_auth_fail_event(wps->wps, 0, 1); + wps_pwd_auth_fail_event(wps->wps, 0, 1, wps->mac_addr_e); return -1; } @@ -2095,7 +2266,7 @@ "not match with the pre-committed value"); wps_registrar_invalidate_pin(wps->wps->registrar, wps->uuid_e); wps->config_error = WPS_CFG_DEV_PASSWORD_AUTH_FAILURE; - wps_pwd_auth_fail_event(wps->wps, 0, 2); + wps_pwd_auth_fail_event(wps->wps, 0, 2, wps->mac_addr_e); return -1; } @@ -2104,6 +2275,13 @@ wps->wps_pin_revealed = 0; wps_registrar_unlock_pin(wps->wps->registrar, wps->uuid_e); + /* + * In case wildcard PIN is used and WPS handshake succeeds in the first + * attempt, wps_registrar_unlock_pin() would not free the PIN, so make + * sure the PIN gets invalidated here. + */ + wps_registrar_invalidate_pin(wps->wps->registrar, wps->uuid_e); + return 0; } @@ -2132,22 +2310,6 @@ return -1; } -#ifdef CONFIG_WPS_OOB - if (wps->wps->oob_conf.pubkey_hash != NULL) { - const u8 *addr[1]; - u8 hash[WPS_HASH_LEN]; - - addr[0] = pk; - sha256_vector(1, addr, &pk_len, hash); - if (os_memcmp(hash, - wpabuf_head(wps->wps->oob_conf.pubkey_hash), - WPS_OOB_PUBKEY_HASH_LEN) != 0) { - wpa_printf(MSG_ERROR, "WPS: Public Key hash error"); - return -1; - } - } -#endif /* CONFIG_WPS_OOB */ - wpabuf_free(wps->dh_pubkey_e); wps->dh_pubkey_e = wpabuf_alloc_copy(pk, pk_len); if (wps->dh_pubkey_e == NULL) @@ -2409,6 +2571,9 @@ wps->dev_pw_id != DEV_PW_USER_SPECIFIED && wps->dev_pw_id != DEV_PW_MACHINE_SPECIFIED && wps->dev_pw_id != DEV_PW_REGISTRAR_SPECIFIED && +#ifdef CONFIG_WPS_NFC + wps->dev_pw_id != DEV_PW_NFC_CONNECTION_HANDOVER && +#endif /* CONFIG_WPS_NFC */ (wps->dev_pw_id != DEV_PW_PUSHBUTTON || !wps->wps->registrar->pbc)) { wpa_printf(MSG_DEBUG, "WPS: Unsupported Device Password ID %d", @@ -2417,15 +2582,45 @@ return WPS_CONTINUE; } -#ifdef CONFIG_WPS_OOB - if (wps->dev_pw_id >= 0x10 && - wps->dev_pw_id != wps->wps->oob_dev_pw_id) { - wpa_printf(MSG_DEBUG, "WPS: OOB Device Password ID " - "%d mismatch", wps->dev_pw_id); - wps->state = SEND_M2D; - return WPS_CONTINUE; +#ifdef CONFIG_WPS_NFC + if (wps->dev_pw_id >= 0x10 || + wps->dev_pw_id == DEV_PW_NFC_CONNECTION_HANDOVER) { + struct wps_nfc_pw_token *token; + const u8 *addr[1]; + u8 hash[WPS_HASH_LEN]; + + wpa_printf(MSG_DEBUG, "WPS: Searching for NFC token match for id=%d (ctx %p registrar %p)", + wps->dev_pw_id, wps->wps, wps->wps->registrar); + token = wps_get_nfc_pw_token( + &wps->wps->registrar->nfc_pw_tokens, wps->dev_pw_id); + if (token && token->peer_pk_hash_known) { + wpa_printf(MSG_DEBUG, "WPS: Found matching NFC " + "Password Token"); + dl_list_del(&token->list); + wps->nfc_pw_token = token; + + addr[0] = attr->public_key; + sha256_vector(1, addr, &attr->public_key_len, hash); + if (os_memcmp(hash, wps->nfc_pw_token->pubkey_hash, + WPS_OOB_PUBKEY_HASH_LEN) != 0) { + wpa_printf(MSG_ERROR, "WPS: Public Key hash " + "mismatch"); + wps->state = SEND_M2D; + wps->config_error = + WPS_CFG_PUBLIC_KEY_HASH_MISMATCH; + return WPS_CONTINUE; + } + } else if (token) { + wpa_printf(MSG_DEBUG, "WPS: Found matching NFC " + "Password Token (no peer PK hash)"); + wps->nfc_pw_token = token; + } else if (wps->dev_pw_id >= 0x10 && + wps->wps->ap_nfc_dev_pw_id == wps->dev_pw_id && + wps->wps->ap_nfc_dev_pw) { + wpa_printf(MSG_DEBUG, "WPS: Found match with own NFC Password Token"); + } } -#endif /* CONFIG_WPS_OOB */ +#endif /* CONFIG_WPS_NFC */ if (wps->dev_pw_id == DEV_PW_PUSHBUTTON) { if ((wps->wps->registrar->force_pbc_overlap || @@ -2440,7 +2635,7 @@ wps_pbc_overlap_event(wps->wps); wps_fail_event(wps->wps, WPS_M1, WPS_CFG_MULTIPLE_PBC_DETECTED, - WPS_EI_NO_ERROR); + WPS_EI_NO_ERROR, wps->mac_addr_e); wps->wps->registrar->force_pbc_overlap = 1; return WPS_CONTINUE; } @@ -2742,7 +2937,7 @@ if (*attr.msg_type != WPS_M1 && (attr.registrar_nonce == NULL || os_memcmp(wps->nonce_r, attr.registrar_nonce, - WPS_NONCE_LEN != 0))) { + WPS_NONCE_LEN) != 0)) { wpa_printf(MSG_DEBUG, "WPS: Mismatch in registrar nonce"); return WPS_FAILURE; } @@ -2770,7 +2965,7 @@ ret = wps_process_m3(wps, msg, &attr); if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK) wps_fail_event(wps->wps, WPS_M3, wps->config_error, - wps->error_indication); + wps->error_indication, wps->mac_addr_e); break; case WPS_M5: if (wps_validate_m5(msg) < 0) @@ -2778,7 +2973,7 @@ ret = wps_process_m5(wps, msg, &attr); if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK) wps_fail_event(wps->wps, WPS_M5, wps->config_error, - wps->error_indication); + wps->error_indication, wps->mac_addr_e); break; case WPS_M7: if (wps_validate_m7(msg) < 0) @@ -2786,7 +2981,7 @@ ret = wps_process_m7(wps, msg, &attr); if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK) wps_fail_event(wps->wps, WPS_M7, wps->config_error, - wps->error_indication); + wps->error_indication, wps->mac_addr_e); break; default: wpa_printf(MSG_DEBUG, "WPS: Unsupported Message Type %d", @@ -2838,14 +3033,14 @@ #endif /* CONFIG_WPS_UPNP */ if (attr.registrar_nonce == NULL || - os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN != 0)) + os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN) != 0) { wpa_printf(MSG_DEBUG, "WPS: Mismatch in registrar nonce"); return WPS_FAILURE; } if (attr.enrollee_nonce == NULL || - os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN != 0)) { + os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN) != 0) { wpa_printf(MSG_DEBUG, "WPS: Mismatch in enrollee nonce"); return WPS_FAILURE; } @@ -2907,14 +3102,14 @@ #endif /* CONFIG_WPS_UPNP */ if (attr.registrar_nonce == NULL || - os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN != 0)) + os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN) != 0) { wpa_printf(MSG_DEBUG, "WPS: Mismatch in registrar nonce"); return WPS_FAILURE; } if (attr.enrollee_nonce == NULL || - os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN != 0)) { + os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN) != 0) { wpa_printf(MSG_DEBUG, "WPS: Mismatch in enrollee nonce"); return WPS_FAILURE; } @@ -2932,19 +3127,19 @@ switch (old_state) { case RECV_M3: wps_fail_event(wps->wps, WPS_M2, config_error, - wps->error_indication); + wps->error_indication, wps->mac_addr_e); break; case RECV_M5: wps_fail_event(wps->wps, WPS_M4, config_error, - wps->error_indication); + wps->error_indication, wps->mac_addr_e); break; case RECV_M7: wps_fail_event(wps->wps, WPS_M6, config_error, - wps->error_indication); + wps->error_indication, wps->mac_addr_e); break; case RECV_DONE: wps_fail_event(wps->wps, WPS_M8, config_error, - wps->error_indication); + wps->error_indication, wps->mac_addr_e); break; default: break; @@ -2993,14 +3188,14 @@ #endif /* CONFIG_WPS_UPNP */ if (attr.registrar_nonce == NULL || - os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN != 0)) + os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN) != 0) { wpa_printf(MSG_DEBUG, "WPS: Mismatch in registrar nonce"); return WPS_FAILURE; } if (attr.enrollee_nonce == NULL || - os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN != 0)) { + os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN) != 0) { wpa_printf(MSG_DEBUG, "WPS: Mismatch in enrollee nonce"); return WPS_FAILURE; } @@ -3040,7 +3235,8 @@ if (wps->new_psk) { if (wps_cb_new_psk(wps->wps->registrar, wps->mac_addr_e, - wps->new_psk, wps->new_psk_len)) { + wps->p2p_dev_addr, wps->new_psk, + wps->new_psk_len)) { wpa_printf(MSG_DEBUG, "WPS: Failed to configure the " "new PSK"); } @@ -3048,20 +3244,26 @@ wps->new_psk = NULL; } - wps_cb_reg_success(wps->wps->registrar, wps->mac_addr_e, wps->uuid_e); + wps_cb_reg_success(wps->wps->registrar, wps->mac_addr_e, wps->uuid_e, + wps->dev_password, wps->dev_password_len); if (wps->pbc) { wps_registrar_remove_pbc_session(wps->wps->registrar, wps->uuid_e, wps->p2p_dev_addr); wps_registrar_pbc_completed(wps->wps->registrar); +#ifdef WPS_WORKAROUNDS + os_get_reltime(&wps->wps->registrar->pbc_ignore_start); +#endif /* WPS_WORKAROUNDS */ + os_memcpy(wps->wps->registrar->pbc_ignore_uuid, wps->uuid_e, + WPS_UUID_LEN); } else { wps_registrar_pin_completed(wps->wps->registrar); } /* TODO: maintain AuthorizedMACs somewhere separately for each ER and * merge them into APs own list.. */ - wps_success_event(wps->wps); + wps_success_event(wps->wps, wps->mac_addr_e); return WPS_DONE; } @@ -3130,7 +3332,7 @@ wps->state = SEND_WSC_NACK; wps_fail_event(wps->wps, WPS_WSC_DONE, wps->config_error, - wps->error_indication); + wps->error_indication, wps->mac_addr_e); } return ret; default: @@ -3155,7 +3357,7 @@ "unselect internal Registrar"); reg->selected_registrar = 0; reg->pbc = 0; - wps_registrar_selected_registrar_changed(reg); + wps_registrar_selected_registrar_changed(reg, 0); } @@ -3227,7 +3429,8 @@ * This function is called when selected registrar state changes, e.g., when an * AP receives a SetSelectedRegistrar UPnP message. */ -void wps_registrar_selected_registrar_changed(struct wps_registrar *reg) +void wps_registrar_selected_registrar_changed(struct wps_registrar *reg, + u16 dev_pw_id) { wpa_printf(MSG_DEBUG, "WPS: Selected registrar information changed"); @@ -3251,7 +3454,8 @@ reg->sel_reg_dev_password_id_override = DEV_PW_PUSHBUTTON; wps_set_pushbutton(&methods, reg->wps->config_methods); - } + } else if (dev_pw_id) + reg->sel_reg_dev_password_id_override = dev_pw_id; wpa_printf(MSG_DEBUG, "WPS: Internal Registrar selected " "(pbc=%d)", reg->pbc); reg->sel_reg_config_methods_override = methods; @@ -3307,7 +3511,7 @@ struct wps_credential *cred) { #ifdef CONFIG_WPS2 - printf("encr_type=0x%x\n", cred->encr_type); + wpa_printf(MSG_DEBUG, "WPS: encr_type=0x%x", cred->encr_type); if (!(cred->encr_type & (WPS_ENCR_NONE | WPS_ENCR_TKIP | WPS_ENCR_AES))) { if (cred->encr_type & WPS_ENCR_WEP) { @@ -3341,3 +3545,110 @@ return -1; } + + +#ifdef CONFIG_WPS_NFC + +int wps_registrar_add_nfc_pw_token(struct wps_registrar *reg, + const u8 *pubkey_hash, u16 pw_id, + const u8 *dev_pw, size_t dev_pw_len, + int pk_hash_provided_oob) +{ + struct wps_nfc_pw_token *token; + + if (dev_pw_len > WPS_OOB_DEVICE_PASSWORD_LEN) + return -1; + + if (pw_id == DEV_PW_NFC_CONNECTION_HANDOVER && + (pubkey_hash == NULL || !pk_hash_provided_oob)) { + wpa_printf(MSG_DEBUG, "WPS: Unexpected NFC Password Token " + "addition - missing public key hash"); + return -1; + } + + wps_free_nfc_pw_tokens(®->nfc_pw_tokens, pw_id); + + token = os_zalloc(sizeof(*token)); + if (token == NULL) + return -1; + + token->peer_pk_hash_known = pubkey_hash != NULL; + if (pubkey_hash) + os_memcpy(token->pubkey_hash, pubkey_hash, + WPS_OOB_PUBKEY_HASH_LEN); + token->pw_id = pw_id; + token->pk_hash_provided_oob = pk_hash_provided_oob; + if (dev_pw) { + wpa_snprintf_hex_uppercase((char *) token->dev_pw, + sizeof(token->dev_pw), + dev_pw, dev_pw_len); + token->dev_pw_len = dev_pw_len * 2; + } + + dl_list_add(®->nfc_pw_tokens, &token->list); + + reg->selected_registrar = 1; + reg->pbc = 0; + wps_registrar_add_authorized_mac(reg, + (u8 *) "\xff\xff\xff\xff\xff\xff"); + wps_registrar_selected_registrar_changed(reg, pw_id); + eloop_cancel_timeout(wps_registrar_set_selected_timeout, reg, NULL); + eloop_register_timeout(WPS_PBC_WALK_TIME, 0, + wps_registrar_set_selected_timeout, + reg, NULL); + + wpa_printf(MSG_DEBUG, "WPS: Added NFC Device Password %u to Registrar", + pw_id); + + return 0; +} + + +int wps_registrar_add_nfc_password_token(struct wps_registrar *reg, + const u8 *oob_dev_pw, + size_t oob_dev_pw_len) +{ + const u8 *pos, *hash, *dev_pw; + u16 id; + size_t dev_pw_len; + + if (oob_dev_pw_len < WPS_OOB_PUBKEY_HASH_LEN + 2 || + oob_dev_pw_len > WPS_OOB_PUBKEY_HASH_LEN + 2 + + WPS_OOB_DEVICE_PASSWORD_LEN) + return -1; + + hash = oob_dev_pw; + pos = oob_dev_pw + WPS_OOB_PUBKEY_HASH_LEN; + id = WPA_GET_BE16(pos); + dev_pw = pos + 2; + dev_pw_len = oob_dev_pw + oob_dev_pw_len - dev_pw; + + wpa_printf(MSG_DEBUG, "WPS: Add NFC Password Token for Password ID %u", + id); + + wpa_hexdump(MSG_DEBUG, "WPS: Public Key Hash", + hash, WPS_OOB_PUBKEY_HASH_LEN); + wpa_hexdump_key(MSG_DEBUG, "WPS: Device Password", dev_pw, dev_pw_len); + + return wps_registrar_add_nfc_pw_token(reg, hash, id, dev_pw, + dev_pw_len, 0); +} + + +void wps_registrar_remove_nfc_pw_token(struct wps_registrar *reg, + struct wps_nfc_pw_token *token) +{ + wps_registrar_remove_authorized_mac(reg, + (u8 *) "\xff\xff\xff\xff\xff\xff"); + wps_registrar_selected_registrar_changed(reg, 0); + + /* + * Free the NFC password token if it was used only for a single protocol + * run. The static handover case uses the same password token multiple + * times, so do not free that case here. + */ + if (token->peer_pk_hash_known) + os_free(token); +} + +#endif /* CONFIG_WPS_NFC */ diff -Nru wpa-1.0/src/wps/wps_ufd.c wpa-2.1/src/wps/wps_ufd.c --- wpa-1.0/src/wps/wps_ufd.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/wps/wps_ufd.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,235 +0,0 @@ -/* - * UFD routines for Wi-Fi Protected Setup - * Copyright (c) 2009, Masashi Honma - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#include "includes.h" -#include "common.h" -#include -#include -#include -#include -#include - -#include "wps/wps.h" -#include "wps/wps_i.h" - -#ifdef CONFIG_NATIVE_WINDOWS -#define UFD_DIR1 "%s\\SMRTNTKY" -#define UFD_DIR2 UFD_DIR1 "\\WFAWSC" -#define UFD_FILE UFD_DIR2 "\\%s" -#else /* CONFIG_NATIVE_WINDOWS */ -#define UFD_DIR1 "%s/SMRTNTKY" -#define UFD_DIR2 UFD_DIR1 "/WFAWSC" -#define UFD_FILE UFD_DIR2 "/%s" -#endif /* CONFIG_NATIVE_WINDOWS */ - - -struct wps_ufd_data { - int ufd_fd; -}; - - -static int dev_pwd_e_file_filter(const struct dirent *entry) -{ - unsigned int prefix; - char ext[5]; - - if (sscanf(entry->d_name, "%8x.%4s", &prefix, ext) != 2) - return 0; - if (prefix == 0) - return 0; - if (os_strcasecmp(ext, "WFA") != 0) - return 0; - - return 1; -} - - -static int wps_get_dev_pwd_e_file_name(char *path, char *file_name) -{ - struct dirent **namelist; - int i, file_num; - - file_num = scandir(path, &namelist, &dev_pwd_e_file_filter, - alphasort); - if (file_num < 0) { - wpa_printf(MSG_ERROR, "WPS: OOB file not found: %d (%s)", - errno, strerror(errno)); - return -1; - } - if (file_num == 0) { - wpa_printf(MSG_ERROR, "WPS: OOB file not found"); - os_free(namelist); - return -1; - } - os_strlcpy(file_name, namelist[0]->d_name, 13); - for (i = 0; i < file_num; i++) - os_free(namelist[i]); - os_free(namelist); - return 0; -} - - -static int get_file_name(struct wps_context *wps, int registrar, - const char *path, char *file_name) -{ - switch (wps->oob_conf.oob_method) { - case OOB_METHOD_CRED: - os_snprintf(file_name, 13, "00000000.WSC"); - break; - case OOB_METHOD_DEV_PWD_E: - if (registrar) { - char temp[128]; - os_snprintf(temp, sizeof(temp), UFD_DIR2, path); - if (wps_get_dev_pwd_e_file_name(temp, file_name) < 0) - return -1; - } else { - u8 *mac_addr = wps->dev.mac_addr; - - os_snprintf(file_name, 13, "%02X%02X%02X%02X.WFA", - mac_addr[2], mac_addr[3], mac_addr[4], - mac_addr[5]); - } - break; - case OOB_METHOD_DEV_PWD_R: - os_snprintf(file_name, 13, "00000000.WFA"); - break; - default: - wpa_printf(MSG_ERROR, "WPS: Invalid USBA OOB method"); - return -1; - } - return 0; -} - - -static int ufd_mkdir(const char *path) -{ - if (mkdir(path, S_IRWXU) < 0 && errno != EEXIST) { - wpa_printf(MSG_ERROR, "WPS (UFD): Failed to create directory " - "'%s': %d (%s)", path, errno, strerror(errno)); - return -1; - } - return 0; -} - - -static void * init_ufd(struct wps_context *wps, - struct oob_device_data *oob_dev, int registrar) -{ - int write_f; - char temp[128]; - char *path = oob_dev->device_path; - char filename[13]; - struct wps_ufd_data *data; - int ufd_fd; - - if (path == NULL) - return NULL; - - write_f = wps->oob_conf.oob_method == OOB_METHOD_DEV_PWD_E ? - !registrar : registrar; - - if (get_file_name(wps, registrar, path, filename) < 0) { - wpa_printf(MSG_ERROR, "WPS (UFD): Failed to get file name"); - return NULL; - } - - if (write_f) { - os_snprintf(temp, sizeof(temp), UFD_DIR1, path); - if (ufd_mkdir(temp)) - return NULL; - os_snprintf(temp, sizeof(temp), UFD_DIR2, path); - if (ufd_mkdir(temp)) - return NULL; - } - - os_snprintf(temp, sizeof(temp), UFD_FILE, path, filename); - if (write_f) - ufd_fd = open(temp, O_WRONLY | O_CREAT | O_TRUNC, - S_IRUSR | S_IWUSR); - else - ufd_fd = open(temp, O_RDONLY); - if (ufd_fd < 0) { - wpa_printf(MSG_ERROR, "WPS (UFD): Failed to open %s: %s", - temp, strerror(errno)); - return NULL; - } - - data = os_zalloc(sizeof(*data)); - if (data == NULL) - return NULL; - data->ufd_fd = ufd_fd; - return data; -} - - -static struct wpabuf * read_ufd(void *priv) -{ - struct wps_ufd_data *data = priv; - struct wpabuf *buf; - struct stat s; - size_t file_size; - - if (fstat(data->ufd_fd, &s) < 0) { - wpa_printf(MSG_ERROR, "WPS (UFD): Failed to get file size"); - return NULL; - } - - file_size = s.st_size; - buf = wpabuf_alloc(file_size); - if (buf == NULL) { - wpa_printf(MSG_ERROR, "WPS (UFD): Failed to alloc read " - "buffer"); - return NULL; - } - - if (read(data->ufd_fd, wpabuf_mhead(buf), file_size) != - (int) file_size) { - wpabuf_free(buf); - wpa_printf(MSG_ERROR, "WPS (UFD): Failed to read"); - return NULL; - } - wpabuf_put(buf, file_size); - return buf; -} - - -static int write_ufd(void *priv, struct wpabuf *buf) -{ - struct wps_ufd_data *data = priv; - - if (write(data->ufd_fd, wpabuf_mhead(buf), wpabuf_len(buf)) != - (int) wpabuf_len(buf)) { - wpa_printf(MSG_ERROR, "WPS (UFD): Failed to write"); - return -1; - } - return 0; -} - - -static void deinit_ufd(void *priv) -{ - struct wps_ufd_data *data = priv; - close(data->ufd_fd); - os_free(data); -} - - -struct oob_device_data oob_ufd_device_data = { - .device_name = NULL, - .device_path = NULL, - .init_func = init_ufd, - .read_func = read_ufd, - .write_func = write_ufd, - .deinit_func = deinit_ufd, -}; diff -Nru wpa-1.0/src/wps/wps_upnp_ap.c wpa-2.1/src/wps/wps_upnp_ap.c --- wpa-1.0/src/wps/wps_upnp_ap.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/wps/wps_upnp_ap.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * Wi-Fi Protected Setup - UPnP AP functionality * Copyright (c) 2009, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -25,9 +19,10 @@ static void upnp_er_set_selected_timeout(void *eloop_ctx, void *timeout_ctx) { struct subscription *s = eloop_ctx; + struct wps_registrar *reg = timeout_ctx; wpa_printf(MSG_DEBUG, "WPS: SetSelectedRegistrar from ER timed out"); s->selected_registrar = 0; - wps_registrar_selected_registrar_changed(s->reg); + wps_registrar_selected_registrar_changed(reg, 0); } @@ -46,7 +41,7 @@ return -1; s->reg = reg; - eloop_cancel_timeout(upnp_er_set_selected_timeout, s, NULL); + eloop_cancel_timeout(upnp_er_set_selected_timeout, s, reg); os_memset(s->authorized_macs, 0, sizeof(s->authorized_macs)); if (attr.selected_registrar == NULL || *attr.selected_registrar == 0) { @@ -73,19 +68,20 @@ #endif /* CONFIG_WPS2 */ } eloop_register_timeout(WPS_PBC_WALK_TIME, 0, - upnp_er_set_selected_timeout, s, NULL); + upnp_er_set_selected_timeout, s, reg); } - wps_registrar_selected_registrar_changed(reg); + wps_registrar_selected_registrar_changed(reg, 0); return 0; } -void upnp_er_remove_notification(struct subscription *s) +void upnp_er_remove_notification(struct wps_registrar *reg, + struct subscription *s) { s->selected_registrar = 0; - eloop_cancel_timeout(upnp_er_set_selected_timeout, s, NULL); - if (s->reg) - wps_registrar_selected_registrar_changed(s->reg); + eloop_cancel_timeout(upnp_er_set_selected_timeout, s, reg); + if (reg) + wps_registrar_selected_registrar_changed(reg, 0); } diff -Nru wpa-1.0/src/wps/wps_upnp.c wpa-2.1/src/wps/wps_upnp.c --- wpa-1.0/src/wps/wps_upnp.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/wps/wps_upnp.c 2014-02-04 11:23:35.000000000 +0000 @@ -305,15 +305,15 @@ int alloc_len; char *scratch_mem = NULL; char *mem; - char *domain_and_port; + char *host; char *delim; char *path; - char *domain; int port = 80; /* port to send to (default is port 80) */ struct addrinfo hints; struct addrinfo *result = NULL; struct addrinfo *rp; int rerr; + size_t host_len, path_len; /* url MUST begin with http: */ if (url_len < 7 || os_strncasecmp(url, "http://", 7)) @@ -321,30 +321,22 @@ url += 7; url_len -= 7; - /* allocate memory for the extra stuff we need */ - alloc_len = 2 * (url_len + 1); - scratch_mem = os_zalloc(alloc_len); + /* Make a copy of the string to allow modification during parsing */ + scratch_mem = dup_binstr(url, url_len); if (scratch_mem == NULL) goto fail; - mem = scratch_mem; - os_strncpy(mem, url, url_len); - wpa_printf(MSG_DEBUG, "WPS UPnP: Adding URL '%s'", mem); - domain_and_port = mem; - mem += 1 + os_strlen(mem); - delim = os_strchr(domain_and_port, '/'); - if (delim) { - *delim++ = 0; /* null terminate domain and port */ - path = delim; - } else { - path = domain_and_port + os_strlen(domain_and_port); - } - domain = mem; - strcpy(domain, domain_and_port); - delim = os_strchr(domain, ':'); + wpa_printf(MSG_DEBUG, "WPS UPnP: Adding URL '%s'", scratch_mem); + host = scratch_mem; + path = os_strchr(host, '/'); + if (path) + *path++ = '\0'; /* null terminate host */ + + /* Process and remove optional port component */ + delim = os_strchr(host, ':'); if (delim) { - *delim++ = 0; /* null terminate domain */ - if (isdigit(*delim)) - port = atol(delim); + *delim = '\0'; /* null terminate host name for now */ + if (isdigit(delim[1])) + port = atol(delim + 1); } /* @@ -367,13 +359,21 @@ hints.ai_flags = 0; #endif hints.ai_protocol = 0; /* Any protocol? */ - rerr = getaddrinfo(domain, NULL /* fill in port ourselves */, + rerr = getaddrinfo(host, NULL /* fill in port ourselves */, &hints, &result); if (rerr) { wpa_printf(MSG_INFO, "WPS UPnP: Resolve error %d (%s) on: %s", - rerr, gai_strerror(rerr), domain); + rerr, gai_strerror(rerr), host); goto fail; } + + if (delim) + *delim = ':'; /* Restore port */ + + host_len = os_strlen(host); + path_len = path ? os_strlen(path) : 0; + alloc_len = host_len + 1 + 1 + path_len + 1; + for (rp = result; rp; rp = rp->ai_next) { struct subscr_addr *a; @@ -386,16 +386,16 @@ a = os_zalloc(sizeof(*a) + alloc_len); if (a == NULL) - continue; - mem = (void *) (a + 1); + break; + mem = (char *) (a + 1); a->domain_and_port = mem; - strcpy(mem, domain_and_port); - mem += 1 + strlen(mem); + os_memcpy(mem, host, host_len); + mem += host_len + 1; a->path = mem; - if (path[0] != '/') + if (path == NULL || path[0] != '/') *mem++ = '/'; - strcpy(mem, path); - mem += 1 + os_strlen(mem); + if (path) + os_memcpy(mem, path, path_len); os_memcpy(&a->saddr, rp->ai_addr, sizeof(a->saddr)); a->saddr.sin_port = htons(port); @@ -432,23 +432,6 @@ } -int send_wpabuf(int fd, struct wpabuf *buf) -{ - wpa_printf(MSG_DEBUG, "WPS UPnP: Send %lu byte message", - (unsigned long) wpabuf_len(buf)); - errno = 0; - if (write(fd, wpabuf_head(buf), wpabuf_len(buf)) != - (int) wpabuf_len(buf)) { - wpa_printf(MSG_ERROR, "WPS UPnP: Failed to send buffer: " - "errno=%d (%s)", - errno, strerror(errno)); - return -1; - } - - return 0; -} - - static void wpabuf_put_property(struct wpabuf *buf, const char *name, const char *value) { @@ -480,14 +463,14 @@ "\n" "\n"; const char *format_tail = "\n"; - struct os_time now; + struct os_reltime now; if (dl_list_empty(&sm->subscriptions)) { /* optimize */ return; } - if (os_get_time(&now) == 0) { + if (os_get_reltime(&now) == 0) { if (now.sec != sm->last_event_sec) { sm->last_event_sec = now.sec; sm->num_events_in_sec = 1; @@ -550,10 +533,13 @@ */ void subscription_destroy(struct subscription *s) { + struct upnp_wps_device_interface *iface; wpa_printf(MSG_DEBUG, "WPS UPnP: Destroy subscription %p", s); subscr_addr_free_all(s); event_delete_all(s); - upnp_er_remove_notification(s); + dl_list_for_each(iface, &s->sm->interfaces, + struct upnp_wps_device_interface, list) + upnp_er_remove_notification(iface->wps->registrar, s); os_free(s); } @@ -979,6 +965,7 @@ wpa_printf(MSG_DEBUG, "WPS UPnP: Stop device"); web_listener_stop(sm); + ssdp_listener_stop(sm); upnp_wps_free_msearchreply(&sm->msearch_replies); upnp_wps_free_subscriptions(&sm->subscriptions, NULL); @@ -992,7 +979,6 @@ if (sm->multicast_sd >= 0) close(sm->multicast_sd); sm->multicast_sd = -1; - ssdp_listener_stop(sm); sm->started = 0; } diff -Nru wpa-1.0/src/wps/wps_upnp_i.h wpa-2.1/src/wps/wps_upnp_i.h --- wpa-1.0/src/wps/wps_upnp_i.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/wps/wps_upnp_i.h 2014-02-04 11:23:35.000000000 +0000 @@ -158,7 +158,6 @@ struct subscription * subscription_find(struct upnp_wps_device_sm *sm, const u8 uuid[UUID_LEN]); void subscr_addr_delete(struct subscr_addr *a); -int send_wpabuf(int fd, struct wpabuf *buf); int get_netif_info(const char *net_if, unsigned *ip_addr, char **ip_addr_text, u8 mac[ETH_ALEN]); @@ -171,7 +170,7 @@ int ssdp_listener_start(struct upnp_wps_device_sm *sm); int ssdp_listener_open(void); int add_ssdp_network(const char *net_if); -int ssdp_open_multicast_sock(u32 ip_addr); +int ssdp_open_multicast_sock(u32 ip_addr, const char *forced_ifname); int ssdp_open_multicast(struct upnp_wps_device_sm *sm); /* wps_upnp_web.c */ @@ -188,6 +187,7 @@ int upnp_er_set_selected_registrar(struct wps_registrar *reg, struct subscription *s, const struct wpabuf *msg); -void upnp_er_remove_notification(struct subscription *s); +void upnp_er_remove_notification(struct wps_registrar *reg, + struct subscription *s); #endif /* WPS_UPNP_I_H */ diff -Nru wpa-1.0/src/wps/wps_upnp_ssdp.c wpa-2.1/src/wps/wps_upnp_ssdp.c --- wpa-1.0/src/wps/wps_upnp_ssdp.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/wps/wps_upnp_ssdp.c 2014-02-04 11:23:35.000000000 +0000 @@ -3,7 +3,7 @@ * Copyright (c) 2000-2003 Intel Corporation * Copyright (c) 2006-2007 Sony Corporation * Copyright (c) 2008-2009 Atheros Communications - * Copyright (c) 2009, Jouni Malinen + * Copyright (c) 2009-2013, Jouni Malinen * * See wps_upnp.c for more details on licensing and code history. */ @@ -13,6 +13,9 @@ #include #include #include +#ifdef __linux__ +#include +#endif /* __linux__ */ #include "common.h" #include "uuid.h" @@ -854,7 +857,7 @@ } -int ssdp_open_multicast_sock(u32 ip_addr) +int ssdp_open_multicast_sock(u32 ip_addr, const char *forced_ifname) { int sd; /* per UPnP-arch-DeviceArchitecture, 1. Discovery, keep IP packet @@ -865,21 +868,41 @@ if (sd < 0) return -1; + if (forced_ifname) { +#ifdef __linux__ + struct ifreq req; + os_memset(&req, 0, sizeof(req)); + os_strlcpy(req.ifr_name, forced_ifname, sizeof(req.ifr_name)); + if (setsockopt(sd, SOL_SOCKET, SO_BINDTODEVICE, &req, + sizeof(req)) < 0) { + wpa_printf(MSG_INFO, "WPS UPnP: Failed to bind " + "multicast socket to ifname %s: %s", + forced_ifname, strerror(errno)); + close(sd); + return -1; + } +#endif /* __linux__ */ + } + #if 0 /* maybe ok if we sometimes block on writes */ - if (fcntl(sd, F_SETFL, O_NONBLOCK) != 0) + if (fcntl(sd, F_SETFL, O_NONBLOCK) != 0) { + close(sd); return -1; + } #endif if (setsockopt(sd, IPPROTO_IP, IP_MULTICAST_IF, &ip_addr, sizeof(ip_addr))) { wpa_printf(MSG_DEBUG, "WPS: setsockopt(IP_MULTICAST_IF) %x: " "%d (%s)", ip_addr, errno, strerror(errno)); + close(sd); return -1; } if (setsockopt(sd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl))) { wpa_printf(MSG_DEBUG, "WPS: setsockopt(IP_MULTICAST_TTL): " "%d (%s)", errno, strerror(errno)); + close(sd); return -1; } @@ -898,6 +921,7 @@ "WPS UPnP: setsockopt " "IP_ADD_MEMBERSHIP errno %d (%s)", errno, strerror(errno)); + close(sd); return -1; } } @@ -919,7 +943,7 @@ */ int ssdp_open_multicast(struct upnp_wps_device_sm *sm) { - sm->multicast_sd = ssdp_open_multicast_sock(sm->ip_addr); + sm->multicast_sd = ssdp_open_multicast_sock(sm->ip_addr, NULL); if (sm->multicast_sd < 0) return -1; return 0; diff -Nru wpa-1.0/src/wps/wps_upnp_web.c wpa-2.1/src/wps/wps_upnp_web.c --- wpa-1.0/src/wps/wps_upnp_web.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/wps/wps_upnp_web.c 2014-02-04 11:23:35.000000000 +0000 @@ -996,13 +996,11 @@ h++; len = end - h; os_free(callback_urls); - callback_urls = os_malloc(len + 1); + callback_urls = dup_binstr(h, len); if (callback_urls == NULL) { ret = HTTP_INTERNAL_SERVER_ERROR; goto error; } - os_memcpy(callback_urls, h, len); - callback_urls[len] = 0; continue; } /* SID is only for renewal */ diff -Nru wpa-1.0/src/wps/wps_validate.c wpa-2.1/src/wps/wps_validate.c --- wpa-1.0/src/wps/wps_validate.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/src/wps/wps_validate.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * Wi-Fi Protected Setup - Strict protocol validation routines * Copyright (c) 2010, Atheros Communications, Inc. * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "utils/includes.h" @@ -273,7 +267,7 @@ return 0; } val = WPA_GET_BE16(config_error); - if (val > 18) { + if (val > 20) { wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Configuration Error " "attribute value 0x%04x", val); return -1; @@ -296,7 +290,7 @@ return 0; } val = WPA_GET_BE16(dev_password_id); - if (val >= 0x0006 && val <= 0x000f) { + if (val >= 0x0008 && val <= 0x000f) { wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Device Password ID " "attribute value 0x%04x", val); return -1; diff -Nru wpa-1.0/wpa_supplicant/android.config wpa-2.1/wpa_supplicant/android.config --- wpa-1.0/wpa_supplicant/android.config 1970-01-01 00:00:00.000000000 +0000 +++ wpa-2.1/wpa_supplicant/android.config 2014-02-04 11:23:35.000000000 +0000 @@ -0,0 +1,481 @@ +# Example wpa_supplicant build time configuration +# +# This file lists the configuration options that are used when building the +# hostapd binary. All lines starting with # are ignored. Configuration option +# lines must be commented out complete, if they are not to be included, i.e., +# just setting VARIABLE=n is not disabling that variable. +# +# This file is included in Makefile, so variables like CFLAGS and LIBS can also +# be modified from here. In most cases, these lines should use += in order not +# to override previous values of the variables. + + +# Uncomment following two lines and fix the paths if you have installed OpenSSL +# or GnuTLS in non-default location +#CFLAGS += -I/usr/local/openssl/include +#LIBS += -L/usr/local/openssl/lib + +# Some Red Hat versions seem to include kerberos header files from OpenSSL, but +# the kerberos files are not in the default include path. Following line can be +# used to fix build issues on such systems (krb5.h not found). +#CFLAGS += -I/usr/include/kerberos + +# Driver interface for generic Linux wireless extensions +# Note: WEXT is deprecated in the current Linux kernel version and no new +# functionality is added to it. nl80211-based interface is the new +# replacement for WEXT and its use allows wpa_supplicant to properly control +# the driver to improve existing functionality like roaming and to support new +# functionality. +#CONFIG_DRIVER_WEXT=y + +# Driver interface for Linux drivers using the nl80211 kernel interface +#CONFIG_DRIVER_NL80211=y +CONFIG_LIBNL20=y + +# Driver interface for FreeBSD net80211 layer (e.g., Atheros driver) +#CONFIG_DRIVER_BSD=y +#CFLAGS += -I/usr/local/include +#LIBS += -L/usr/local/lib +#LIBS_p += -L/usr/local/lib +#LIBS_c += -L/usr/local/lib + +# Driver interface for Windows NDIS +#CONFIG_DRIVER_NDIS=y +#CFLAGS += -I/usr/include/w32api/ddk +#LIBS += -L/usr/local/lib +# For native build using mingw +#CONFIG_NATIVE_WINDOWS=y +# Additional directories for cross-compilation on Linux host for mingw target +#CFLAGS += -I/opt/mingw/mingw32/include/ddk +#LIBS += -L/opt/mingw/mingw32/lib +#CC=mingw32-gcc +# By default, driver_ndis uses WinPcap for low-level operations. This can be +# replaced with the following option which replaces WinPcap calls with NDISUIO. +# However, this requires that WZC is disabled (net stop wzcsvc) before starting +# wpa_supplicant. +# CONFIG_USE_NDISUIO=y + +# Driver interface for development testing +#CONFIG_DRIVER_TEST=y + +# Driver interface for wired Ethernet drivers +#CONFIG_DRIVER_WIRED=y + +# Driver interface for the Broadcom RoboSwitch family +#CONFIG_DRIVER_ROBOSWITCH=y + +# Driver interface for no driver (e.g., WPS ER only) +#CONFIG_DRIVER_NONE=y + +# Solaris libraries +#LIBS += -lsocket -ldlpi -lnsl +#LIBS_c += -lsocket + +# Enable IEEE 802.1X Supplicant (automatically included if any EAP method is +# included) +CONFIG_IEEE8021X_EAPOL=y + +# EAP-MD5 +CONFIG_EAP_MD5=y + +# EAP-MSCHAPv2 +CONFIG_EAP_MSCHAPV2=y + +# EAP-TLS +CONFIG_EAP_TLS=y + +# EAL-PEAP +CONFIG_EAP_PEAP=y + +# EAP-TTLS +CONFIG_EAP_TTLS=y + +# EAP-FAST +# Note: Default OpenSSL package does not include support for all the +# functionality needed for EAP-FAST. If EAP-FAST is enabled with OpenSSL, +# the OpenSSL library must be patched (openssl-0.9.8d-tls-extensions.patch) +# to add the needed functions. +CONFIG_EAP_FAST=y + +# EAP-GTC +CONFIG_EAP_GTC=y + +# EAP-OTP +CONFIG_EAP_OTP=y + +# EAP-SIM (enable CONFIG_PCSC, if EAP-SIM is used) +CONFIG_EAP_SIM=y + +# EAP-PSK (experimental; this is _not_ needed for WPA-PSK) +#CONFIG_EAP_PSK=y + +# EAP-pwd (secure authentication using only a password) +CONFIG_EAP_PWD=y + +# EAP-PAX +#CONFIG_EAP_PAX=y + +# LEAP +CONFIG_EAP_LEAP=y + +# EAP-AKA (enable CONFIG_PCSC, if EAP-AKA is used) +CONFIG_EAP_AKA=y + +# EAP-AKA' (enable CONFIG_PCSC, if EAP-AKA' is used). +# This requires CONFIG_EAP_AKA to be enabled, too. +#CONFIG_EAP_AKA_PRIME=y + +# Enable USIM simulator (Milenage) for EAP-AKA +#CONFIG_USIM_SIMULATOR=y + +# EAP-SAKE +#CONFIG_EAP_SAKE=y + +# EAP-GPSK +#CONFIG_EAP_GPSK=y +# Include support for optional SHA256 cipher suite in EAP-GPSK +#CONFIG_EAP_GPSK_SHA256=y + +# EAP-TNC and related Trusted Network Connect support (experimental) +#CONFIG_EAP_TNC=y + +# Wi-Fi Protected Setup (WPS) +CONFIG_WPS=y +# Enable WSC 2.0 support +CONFIG_WPS2=y +# Enable WPS external registrar functionality +CONFIG_WPS_ER=y +# Disable credentials for an open network by default when acting as a WPS +# registrar. +#CONFIG_WPS_REG_DISABLE_OPEN=y +# Enable WPS support with NFC config method +CONFIG_WPS_NFC=y + +# EAP-IKEv2 +#CONFIG_EAP_IKEV2=y + +# PKCS#12 (PFX) support (used to read private key and certificate file from +# a file that usually has extension .p12 or .pfx) +CONFIG_PKCS12=y + +# Smartcard support (i.e., private key on a smartcard), e.g., with openssl +# engine. +CONFIG_SMARTCARD=y + +# PC/SC interface for smartcards (USIM, GSM SIM) +# Enable this if EAP-SIM or EAP-AKA is included +#CONFIG_PCSC=y + +# Support HT overrides (disable HT/HT40, mask MCS rates, etc.) +#CONFIG_HT_OVERRIDES=y + +# Support VHT overrides (disable VHT, mask MCS rates, etc.) +#CONFIG_VHT_OVERRIDES=y + +# Development testing +#CONFIG_EAPOL_TEST=y + +# Select control interface backend for external programs, e.g, wpa_cli: +# unix = UNIX domain sockets (default for Linux/*BSD) +# udp = UDP sockets using localhost (127.0.0.1) +# named_pipe = Windows Named Pipe (default for Windows) +# udp-remote = UDP sockets with remote access (only for tests systems/purpose) +# y = use default (backwards compatibility) +# If this option is commented out, control interface is not included in the +# build. +CONFIG_CTRL_IFACE=y + +# Include support for GNU Readline and History Libraries in wpa_cli. +# When building a wpa_cli binary for distribution, please note that these +# libraries are licensed under GPL and as such, BSD license may not apply for +# the resulting binary. +#CONFIG_READLINE=y + +# Include internal line edit mode in wpa_cli. This can be used as a replacement +# for GNU Readline to provide limited command line editing and history support. +CONFIG_WPA_CLI_EDIT=y + +# Remove debugging code that is printing out debug message to stdout. +# This can be used to reduce the size of the wpa_supplicant considerably +# if debugging code is not needed. The size reduction can be around 35% +# (e.g., 90 kB). +#CONFIG_NO_STDOUT_DEBUG=y + +# Remove WPA support, e.g., for wired-only IEEE 802.1X supplicant, to save +# 35-50 kB in code size. +#CONFIG_NO_WPA=y + +# Remove IEEE 802.11i/WPA-Personal ASCII passphrase support +# This option can be used to reduce code size by removing support for +# converting ASCII passphrases into PSK. If this functionality is removed, the +# PSK can only be configured as the 64-octet hexstring (e.g., from +# wpa_passphrase). This saves about 0.5 kB in code size. +#CONFIG_NO_WPA_PASSPHRASE=y + +# Disable scan result processing (ap_mode=1) to save code size by about 1 kB. +# This can be used if ap_scan=1 mode is never enabled. +#CONFIG_NO_SCAN_PROCESSING=y + +# Select configuration backend: +# file = text file (e.g., wpa_supplicant.conf; note: the configuration file +# path is given on command line, not here; this option is just used to +# select the backend that allows configuration files to be used) +# winreg = Windows registry (see win_example.reg for an example) +CONFIG_BACKEND=file + +# Remove configuration write functionality (i.e., to allow the configuration +# file to be updated based on runtime configuration changes). The runtime +# configuration can still be changed, the changes are just not going to be +# persistent over restarts. This option can be used to reduce code size by +# about 3.5 kB. +#CONFIG_NO_CONFIG_WRITE=y + +# Remove support for configuration blobs to reduce code size by about 1.5 kB. +#CONFIG_NO_CONFIG_BLOBS=y + +# Select program entry point implementation: +# main = UNIX/POSIX like main() function (default) +# main_winsvc = Windows service (read parameters from registry) +# main_none = Very basic example (development use only) +#CONFIG_MAIN=main + +# Select wrapper for operatins system and C library specific functions +# unix = UNIX/POSIX like systems (default) +# win32 = Windows systems +# none = Empty template +CONFIG_OS=unix + +# Select event loop implementation +# eloop = select() loop (default) +# eloop_win = Windows events and WaitForMultipleObject() loop +CONFIG_ELOOP=eloop + +# Should we use poll instead of select? Select is used by default. +#CONFIG_ELOOP_POLL=y + +# Select layer 2 packet implementation +# linux = Linux packet socket (default) +# pcap = libpcap/libdnet/WinPcap +# freebsd = FreeBSD libpcap +# winpcap = WinPcap with receive thread +# ndis = Windows NDISUIO (note: requires CONFIG_USE_NDISUIO=y) +# none = Empty template +CONFIG_L2_PACKET=linux + +# PeerKey handshake for Station to Station Link (IEEE 802.11e DLS) +CONFIG_PEERKEY=y + +# IEEE 802.11w (management frame protection), also known as PMF +# Driver support is also needed for IEEE 802.11w. +CONFIG_IEEE80211W=y + +# Select TLS implementation +# openssl = OpenSSL (default) +# gnutls = GnuTLS +# internal = Internal TLSv1 implementation (experimental) +# none = Empty template +#CONFIG_TLS=openssl + +# TLS-based EAP methods require at least TLS v1.0. Newer version of TLS (v1.1) +# can be enabled to get a stronger construction of messages when block ciphers +# are used. It should be noted that some existing TLS v1.0 -based +# implementation may not be compatible with TLS v1.1 message (ClientHello is +# sent prior to negotiating which version will be used) +#CONFIG_TLSV11=y + +# TLS-based EAP methods require at least TLS v1.0. Newer version of TLS (v1.2) +# can be enabled to enable use of stronger crypto algorithms. It should be +# noted that some existing TLS v1.0 -based implementation may not be compatible +# with TLS v1.2 message (ClientHello is sent prior to negotiating which version +# will be used) +#CONFIG_TLSV12=y + +# 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 +# and drawbacks of this option. +#CONFIG_INTERNAL_LIBTOMMATH=y +#ifndef CONFIG_INTERNAL_LIBTOMMATH +#LTM_PATH=/usr/src/libtommath-0.39 +#CFLAGS += -I$(LTM_PATH) +#LIBS += -L$(LTM_PATH) +#LIBS_p += -L$(LTM_PATH) +#endif +# At the cost of about 4 kB of additional binary size, the internal LibTomMath +# can be configured to include faster routines for exptmod, sqr, and div to +# speed up DH and RSA calculation considerably +#CONFIG_INTERNAL_LIBTOMMATH_FAST=y + +# Include NDIS event processing through WMI into wpa_supplicant/wpasvc. +# This is only for Windows builds and requires WMI-related header files and +# WbemUuid.Lib from Platform SDK even when building with MinGW. +#CONFIG_NDIS_EVENTS_INTEGRATED=y +#PLATFORMSDKLIB="/opt/Program Files/Microsoft Platform SDK/Lib" + +# Add support for old DBus control interface +# (fi.epitest.hostap.WPASupplicant) +#CONFIG_CTRL_IFACE_DBUS=y + +# Add support for new DBus control interface +# (fi.w1.hostap.wpa_supplicant1) +#CONFIG_CTRL_IFACE_DBUS_NEW=y + +# Add introspection support for new DBus control interface +#CONFIG_CTRL_IFACE_DBUS_INTRO=y + +# Add support for loading EAP methods dynamically as shared libraries. +# When this option is enabled, each EAP method can be either included +# statically (CONFIG_EAP_=y) or dynamically (CONFIG_EAP_=dyn). +# Dynamic EAP methods are build as shared objects (eap_*.so) and they need to +# be loaded in the beginning of the wpa_supplicant configuration file +# (see load_dynamic_eap parameter in the example file) before being used in +# the network blocks. +# +# Note that some shared parts of EAP methods are included in the main program +# and in order to be able to use dynamic EAP methods using these parts, the +# main program must have been build with the EAP method enabled (=y or =dyn). +# This means that EAP-TLS/PEAP/TTLS/FAST cannot be added as dynamic libraries +# unless at least one of them was included in the main build to force inclusion +# of the shared code. Similarly, at least one of EAP-SIM/AKA must be included +# in the main build to be able to load these methods dynamically. +# +# Please also note that using dynamic libraries will increase the total binary +# size. Thus, it may not be the best option for targets that have limited +# amount of memory/flash. +#CONFIG_DYNAMIC_EAP_METHODS=y + +# IEEE Std 802.11r-2008 (Fast BSS Transition) +CONFIG_IEEE80211R=y + +# Add support for writing debug log to a file (/tmp/wpa_supplicant-log-#.txt) +#CONFIG_DEBUG_FILE=y + +# Send debug messages to syslog instead of stdout +#CONFIG_DEBUG_SYSLOG=y +# Set syslog facility for debug messages +#CONFIG_DEBUG_SYSLOG_FACILITY=LOG_DAEMON + +# 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 +# same file, e.g., using trace-cmd. +#CONFIG_DEBUG_LINUX_TRACING=y + +# Add support for writing debug log to Android logcat instead of standard +# output +CONFIG_ANDROID_LOG=y + +# Enable privilege separation (see README 'Privilege separation' for details) +#CONFIG_PRIVSEP=y + +# Enable mitigation against certain attacks against TKIP by delaying Michael +# MIC error reports by a random amount of time between 0 and 60 seconds +#CONFIG_DELAYED_MIC_ERROR_REPORT=y + +# Enable tracing code for developer debugging +# This tracks use of memory allocations and other registrations and reports +# incorrect use with a backtrace of call (or allocation) location. +#CONFIG_WPA_TRACE=y +# For BSD, uncomment these. +#LIBS += -lexecinfo +#LIBS_p += -lexecinfo +#LIBS_c += -lexecinfo + +# Use libbfd to get more details for developer debugging +# This enables use of libbfd to get more detailed symbols for the backtraces +# generated by CONFIG_WPA_TRACE=y. +#CONFIG_WPA_TRACE_BFD=y +# For BSD, uncomment these. +#LIBS += -lbfd -liberty -lz +#LIBS_p += -lbfd -liberty -lz +#LIBS_c += -lbfd -liberty -lz + +# wpa_supplicant depends on strong random number generation being available +# from the operating system. os_get_random() function is used to fetch random +# data when needed, e.g., for key generation. On Linux and BSD systems, this +# works by reading /dev/urandom. It should be noted that the OS entropy pool +# needs to be properly initialized before wpa_supplicant is started. This is +# important especially on embedded devices that do not have a hardware random +# number generator and may by default start up with minimal entropy available +# for random number generation. +# +# As a safety net, wpa_supplicant is by default trying to internally collect +# additional entropy for generating random data to mix in with the data fetched +# from the OS. This by itself is not considered to be very strong, but it may +# help in cases where the system pool is not initialized properly. However, it +# is very strongly recommended that the system pool is initialized with enough +# entropy either by using hardware assisted random number generator or by +# storing state over device reboots. +# +# wpa_supplicant can be configured to maintain its own entropy store over +# restarts to enhance random number generation. This is not perfect, but it is +# much more secure than using the same sequence of random numbers after every +# reboot. This can be enabled with -e command line option. The +# specified file needs to be readable and writable by wpa_supplicant. +# +# If the os_get_random() is known to provide strong random data (e.g., on +# Linux/BSD, the board in question is known to have reliable source of random +# data from /dev/urandom), the internal wpa_supplicant random pool can be +# disabled. This will save some in binary size and CPU use. However, this +# should only be considered for builds that are known to be used on devices +# that meet the requirements described above. +#CONFIG_NO_RANDOM_POOL=y + +# IEEE 802.11n (High Throughput) support (mainly for AP mode) +CONFIG_IEEE80211N=y + +# Wireless Network Management (IEEE Std 802.11v-2011) +# Note: This is experimental and not complete implementation. +CONFIG_WNM=y + +# Interworking (IEEE 802.11u) +# This can be used to enable functionality to improve interworking with +# external networks (GAS/ANQP to learn more about the networks and network +# selection based on available credentials). +CONFIG_INTERWORKING=y + +# Hotspot 2.0 +CONFIG_HS20=y + +# Disable roaming in wpa_supplicant +CONFIG_NO_ROAMING=y + +# AP mode operations with wpa_supplicant +# This can be used for controlling AP mode operations with wpa_supplicant. It +# should be noted that this is mainly aimed at simple cases like +# WPA2-Personal while more complex configurations like WPA2-Enterprise with an +# external RADIUS server can be supported with hostapd. +CONFIG_AP=y + +# P2P (Wi-Fi Direct) +# This can be used to enable P2P support in wpa_supplicant. See README-P2P for +# more information on P2P operations. +CONFIG_P2P=y + +# Enable TDLS support +CONFIG_TDLS=y + +# Wi-Fi Direct +# This can be used to enable Wi-Fi Direct extensions for P2P using an external +# program to control the additional information exchanges in the messages. +CONFIG_WIFI_DISPLAY=y + +# Autoscan +# This can be used to enable automatic scan support in wpa_supplicant. +# See wpa_supplicant.conf for more information on autoscan usage. +# +# Enabling directly a module will enable autoscan support. +# For exponential module: +#CONFIG_AUTOSCAN_EXPONENTIAL=y +# For periodic module: +#CONFIG_AUTOSCAN_PERIODIC=y + +# Password (and passphrase, etc.) backend for external storage +# These optional mechanisms can be used to add support for storing passwords +# and other secrets in external (to wpa_supplicant) location. This allows, for +# example, operating system specific key storage to be used +# +# External password backend for testing purposes (developer use) +#CONFIG_EXT_PASSWORD_TEST=y + +include $(wildcard $(LOCAL_PATH)/android_config_*.inc) diff -Nru wpa-1.0/wpa_supplicant/Android.mk wpa-2.1/wpa_supplicant/Android.mk --- wpa-1.0/wpa_supplicant/Android.mk 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/Android.mk 2014-02-04 11:23:35.000000000 +0000 @@ -1,39 +1,47 @@ # # Copyright (C) 2008 The Android Open Source Project # -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# +# This software may be distributed under the terms of the BSD license. +# See README for more details. # LOCAL_PATH := $(call my-dir) PKG_CONFIG ?= pkg-config -WPA_BUILD_SUPPLICANT := false -ifneq ($(TARGET_SIMULATOR),true) - ifneq ($(BOARD_WPA_SUPPLICANT_DRIVER),) - WPA_BUILD_SUPPLICANT := true - CONFIG_DRIVER_$(BOARD_WPA_SUPPLICANT_DRIVER) := y - endif +ifneq ($(BOARD_WPA_SUPPLICANT_DRIVER),) + CONFIG_DRIVER_$(BOARD_WPA_SUPPLICANT_DRIVER) := y +else + CONFIG_DRIVER_TEST := y endif -include $(LOCAL_PATH)/.config +include $(LOCAL_PATH)/android.config # To ignore possible wrong network configurations L_CFLAGS = -DWPA_IGNORE_CONFIG_ERRORS +L_CFLAGS += -DVERSION_STR_POSTFIX=\"-$(PLATFORM_VERSION)\" + # Set Android log name L_CFLAGS += -DANDROID_LOG_NAME=\"wpa_supplicant\" +# Disable roaming in wpa_supplicant +ifdef CONFIG_NO_ROAMING +L_CFLAGS += -DCONFIG_NO_ROAMING +endif + +ifeq ($(BOARD_WLAN_DEVICE), bcmdhd) +L_CFLAGS += -DANDROID_P2P +L_CFLAGS += -DP2P_CONCURRENT_SEARCH_DELAY=0 +endif + +ifeq ($(BOARD_WLAN_DEVICE), qcwcn) +L_CFLAGS += -DANDROID_P2P +endif + +ifeq ($(BOARD_WLAN_DEVICE), mrvl) +L_CFLAGS += -DANDROID_P2P +endif + # Use Android specific directory for control interface sockets L_CFLAGS += -DCONFIG_CTRL_IFACE_CLIENT_DIR=\"/data/misc/wifi/sockets\" L_CFLAGS += -DCONFIG_CTRL_IFACE_DIR=\"/data/system/wpa_supplicant\" @@ -43,12 +51,6 @@ L_CFLAGS += -mabi=aapcs-linux endif -# To allow non-ASCII characters in SSID -L_CFLAGS += -DWPA_UNICODE_SSID - -# OpenSSL is configured without engines on Android -L_CFLAGS += -DOPENSSL_NO_ENGINE - INCLUDES = $(LOCAL_PATH) INCLUDES += $(LOCAL_PATH)/src INCLUDES += $(LOCAL_PATH)/src/common @@ -66,11 +68,16 @@ INCLUDES += $(LOCAL_PATH)/src/utils INCLUDES += $(LOCAL_PATH)/src/wps INCLUDES += external/openssl/include -INCLUDES += frameworks/base/cmds/keystore +INCLUDES += system/security/keystore/include ifdef CONFIG_DRIVER_NL80211 INCLUDES += external/libnl-headers endif +ifdef CONFIG_FIPS +CONFIG_NO_RANDOM_POOL= +CONFIG_OPENSSL_CMAC=y +endif + OBJS = config.c OBJS += notify.c OBJS += bss.c @@ -125,11 +132,22 @@ OBJS += src/utils/$(CONFIG_ELOOP).c OBJS_c += src/utils/$(CONFIG_ELOOP).c +ifdef CONFIG_ELOOP_POLL +L_CFLAGS += -DCONFIG_ELOOP_POLL +endif ifdef CONFIG_EAPOL_TEST L_CFLAGS += -Werror -DEAPOL_TEST endif +ifdef CONFIG_HT_OVERRIDES +L_CFLAGS += -DCONFIG_HT_OVERRIDES +endif + +ifdef CONFIG_VHT_OVERRIDES +L_CFLAGS += -DCONFIG_VHT_OVERRIDES +endif + ifndef CONFIG_BACKEND CONFIG_BACKEND=file endif @@ -176,6 +194,18 @@ NEED_AES_OMAC1=y endif +ifdef CONFIG_SAE +L_CFLAGS += -DCONFIG_SAE +OBJS += src/common/sae.c +NEED_ECC=y +NEED_DH_GROUPS=y +endif + +ifdef CONFIG_WNM +L_CFLAGS += -DCONFIG_WNM +OBJS += wnm_sta.c +endif + ifdef CONFIG_TDLS L_CFLAGS += -DCONFIG_TDLS OBJS += src/rsn_supp/tdls.c @@ -203,7 +233,7 @@ NEED_MD5=y NEED_RC4=y else -L_CFLAGS += -DCONFIG_NO_WPA -DCONFIG_NO_WPA2 +L_CFLAGS += -DCONFIG_NO_WPA endif ifdef CONFIG_IBSS_RSN @@ -225,6 +255,7 @@ OBJS += src/p2p/p2p_dev_disc.c OBJS += src/p2p/p2p_group.c OBJS += src/ap/p2p_hostapd.c +OBJS += src/utils/bitfield.c L_CFLAGS += -DCONFIG_P2P NEED_GAS=y NEED_OFFCHANNEL=y @@ -236,16 +267,23 @@ endif endif +ifdef CONFIG_WIFI_DISPLAY +L_CFLAGS += -DCONFIG_WIFI_DISPLAY +OBJS += wifi_display.c +endif + +ifdef CONFIG_HS20 +OBJS += hs20_supplicant.c +L_CFLAGS += -DCONFIG_HS20 +CONFIG_INTERWORKING=y +endif + ifdef CONFIG_INTERWORKING OBJS += interworking.c L_CFLAGS += -DCONFIG_INTERWORKING NEED_GAS=y endif -ifdef CONFIG_NO_WPA2 -L_CFLAGS += -DCONFIG_NO_WPA2 -endif - include $(LOCAL_PATH)/src/drivers/drivers.mk ifdef CONFIG_AP @@ -300,6 +338,17 @@ CONFIG_IEEE8021X_EAPOL=y endif +ifdef CONFIG_EAP_UNAUTH_TLS +# EAP-UNAUTH-TLS +L_CFLAGS += -DEAP_UNAUTH_TLS +ifndef CONFIG_EAP_UNAUTH_TLS +OBJS += src/eap_peer/eap_tls.c +OBJS_h += src/eap_server/eap_server_tls.c +TLS_FUNCS=y +endif +CONFIG_IEEE8021X_EAPOL=y +endif + ifdef CONFIG_EAP_PEAP # EAP-PEAP ifeq ($(CONFIG_EAP_PEAP), dyn) @@ -453,6 +502,13 @@ NEED_AES_CBC=y endif +ifdef CONFIG_EAP_PROXY +L_CFLAGS += -DCONFIG_EAP_PROXY +OBJS += src/eap_peer/eap_proxy_$(CONFIG_EAP_PROXY).c +include $(LOCAL_PATH)/eap_proxy_$(CONFIG_EAP_PROXY).mk +CONFIG_IEEE8021X_EAPOL=y +endif + ifdef CONFIG_EAP_AKA_PRIME # EAP-AKA' ifeq ($(CONFIG_EAP_AKA_PRIME), dyn) @@ -534,11 +590,27 @@ ifdef CONFIG_EAP_PWD L_CFLAGS += -DEAP_PWD OBJS += src/eap_peer/eap_pwd.c src/eap_common/eap_pwd_common.c -OBJS_h += src/eap_server/eap_pwd.c +OBJS_h += src/eap_server/eap_server_pwd.c CONFIG_IEEE8021X_EAPOL=y NEED_SHA256=y endif +ifdef CONFIG_EAP_EKE +# EAP-EKE +ifeq ($(CONFIG_EAP_EKE), dyn) +L_CFLAGS += -DEAP_EKE_DYNAMIC +EAPDYN += src/eap_peer/eap_eke.so +else +L_CFLAGS += -DEAP_EKE +OBJS += src/eap_peer/eap_eke.c src/eap_common/eap_eke_common.c +OBJS_h += src/eap_server/eap_server_eke.c +endif +CONFIG_IEEE8021X_EAPOL=y +NEED_DH_GROUPS=y +NEED_DH_GROUPS_ALL=y +NEED_SHA256=y +endif + ifdef CONFIG_WPS ifdef CONFIG_WPS2 L_CFLAGS += -DCONFIG_WPS2 @@ -566,25 +638,10 @@ NEED_AES_CBC=y NEED_MODEXP=y -ifdef CONFIG_WPS_UFD -L_CFLAGS += -DCONFIG_WPS_UFD -OBJS += src/wps/wps_ufd.c -NEED_WPS_OOB=y -endif - ifdef CONFIG_WPS_NFC L_CFLAGS += -DCONFIG_WPS_NFC OBJS += src/wps/ndef.c -OBJS += src/wps/wps_nfc.c NEED_WPS_OOB=y -ifdef CONFIG_WPS_NFC_PN531 -PN531_PATH ?= /usr/local/src/nfc -L_CFLAGS += -DCONFIG_WPS_NFC_PN531 -L_CFLAGS += -I${PN531_PATH}/inc -OBJS += src/wps/wps_nfc_pn531.c -LIBS += ${PN531_PATH}/lib/wpsnfc.dll -LIBS += ${PN531_PATH}/lib/libnfc_mapping_pn53x.dll -endif endif ifdef NEED_WPS_OOB @@ -710,8 +767,15 @@ OBJS += src/ap/drv_callbacks.c OBJS += src/ap/ap_drv_ops.c OBJS += src/ap/beacon.c +OBJS += src/ap/eap_user_db.c ifdef CONFIG_IEEE80211N OBJS += src/ap/ieee802_11_ht.c +ifdef CONFIG_IEEE80211AC +OBJS += src/ap/ieee802_11_vht.c +endif +endif +ifdef CONFIG_WNM +OBJS += src/ap/wnm_ap.c endif ifdef CONFIG_CTRL_IFACE OBJS += src/ap/ctrl_iface_ap.c @@ -724,6 +788,9 @@ ifdef CONFIG_IEEE80211N L_CFLAGS += -DCONFIG_IEEE80211N +ifdef CONFIG_IEEE80211AC +L_CFLAGS += -DCONFIG_IEEE80211AC +endif endif ifdef NEED_AP_MLME @@ -731,6 +798,7 @@ OBJS += src/ap/ap_list.c OBJS += src/ap/ieee802_11.c OBJS += src/ap/hw_features.c +OBJS += src/ap/dfs.c L_CFLAGS += -DNEED_AP_MLME endif ifdef CONFIG_WPS @@ -738,6 +806,12 @@ OBJS += src/ap/wps_hostapd.c OBJS += src/eap_server/eap_server_wsc.c endif +ifdef CONFIG_INTERWORKING +OBJS += src/ap/gas_serv.c +endif +ifdef CONFIG_HS20 +OBJS += src/ap/hs20.c +endif endif ifdef NEED_RSN_AUTHENTICATOR @@ -836,7 +910,11 @@ # Shared TLS functions (needed for EAP_TLS, EAP_PEAP, EAP_TTLS, and EAP_FAST) OBJS += src/eap_peer/eap_tls_common.c OBJS_h += src/eap_server/eap_server_tls_common.c +ifndef CONFIG_FIPS NEED_TLS_PRF=y +NEED_SHA1=y +NEED_MD5=y +endif endif ifndef CONFIG_TLS @@ -847,6 +925,11 @@ L_CFLAGS += -DCONFIG_TLSV11 endif +ifdef CONFIG_TLSV12 +L_CFLAGS += -DCONFIG_TLSV12 +NEED_SHA256=y +endif + ifeq ($(CONFIG_TLS), openssl) ifdef TLS_FUNCS L_CFLAGS += -DEAP_TLS_OPENSSL @@ -860,6 +943,10 @@ endif LIBS += -lcrypto LIBS_p += -lcrypto +ifdef CONFIG_TLS_ADD_DL +LIBS += -ldl +LIBS_p += -ldl +endif endif ifeq ($(CONFIG_TLS), gnutls) @@ -931,6 +1018,9 @@ NEED_SHA256=y NEED_BASE64=y NEED_TLS_PRF=y +ifdef CONFIG_TLSV12 +NEED_TLS_PRF_SHA256=y +endif NEED_MODEXP=y NEED_CIPHER=y L_CFLAGS += -DCONFIG_TLS_INTERNAL_CLIENT @@ -1036,8 +1126,12 @@ endif ifdef NEED_AES_OMAC1 NEED_AES_ENC=y +ifdef CONFIG_OPENSSL_CMAC +L_CFLAGS += -DCONFIG_OPENSSL_CMAC +else AESOBJS += src/crypto/aes-omac1.c endif +endif ifdef NEED_AES_WRAP NEED_AES_ENC=y AESOBJS += src/crypto/aes-wrap.c @@ -1057,7 +1151,10 @@ SHA1OBJS = ifdef NEED_SHA1 +ifneq ($(CONFIG_TLS), openssl) SHA1OBJS += src/crypto/sha1.c +endif +SHA1OBJS += src/crypto/sha1-prf.c ifdef CONFIG_INTERNAL_SHA1 SHA1OBJS += src/crypto/sha1-internal.c ifdef NEED_FIPS186_2_PRF @@ -1067,8 +1164,10 @@ ifdef CONFIG_NO_WPA_PASSPHRASE L_CFLAGS += -DCONFIG_NO_PBKDF2 else +ifneq ($(CONFIG_TLS), openssl) SHA1OBJS += src/crypto/sha1-pbkdf2.c endif +endif ifdef NEED_T_PRF SHA1OBJS += src/crypto/sha1-tprf.c endif @@ -1077,14 +1176,14 @@ endif endif -MD5OBJS = src/crypto/md5.c +MD5OBJS = +ifndef CONFIG_FIPS +MD5OBJS += src/crypto/md5.c +endif ifdef NEED_MD5 ifdef CONFIG_INTERNAL_MD5 MD5OBJS += src/crypto/md5-internal.c endif -ifdef CONFIG_FIPS -MD5OBJS += src/crypto/md5-non-fips.c -endif OBJS += $(MD5OBJS) OBJS_p += $(MD5OBJS) endif @@ -1111,10 +1210,16 @@ SHA256OBJS = # none by default ifdef NEED_SHA256 L_CFLAGS += -DCONFIG_SHA256 +ifneq ($(CONFIG_TLS), openssl) SHA256OBJS += src/crypto/sha256.c +endif +SHA256OBJS += src/crypto/sha256-prf.c ifdef CONFIG_INTERNAL_SHA256 SHA256OBJS += src/crypto/sha256-internal.c endif +ifdef NEED_TLS_PRF_SHA256 +SHA256OBJS += src/crypto/sha256-tlsprf.c +endif OBJS += $(SHA256OBJS) endif @@ -1130,6 +1235,10 @@ endif endif +ifdef NEED_ECC +L_CFLAGS += -DCONFIG_ECC +endif + ifdef CONFIG_NO_RANDOM_POOL L_CFLAGS += -DCONFIG_NO_RANDOM_POOL else @@ -1154,6 +1263,11 @@ ifeq ($(CONFIG_CTRL_IFACE), named_pipe) L_CFLAGS += -DCONFIG_CTRL_IFACE_NAMED_PIPE endif +ifeq ($(CONFIG_CTRL_IFACE), udp-remote) +CONFIG_CTRL_IFACE=udp +L_CFLAGS += -DCONFIG_CTRL_IFACE_UDP +L_CFLAGS += -DCONFIG_CTRL_IFACE_UDP_REMOTE +endif OBJS += ctrl_iface.c ctrl_iface_$(CONFIG_CTRL_IFACE).c endif @@ -1274,6 +1388,10 @@ endif endif +ifdef CONFIG_DEBUG_LINUX_TRACING +L_CFLAGS += -DCONFIG_DEBUG_LINUX_TRACING +endif + ifdef CONFIG_DEBUG_FILE L_CFLAGS += -DCONFIG_DEBUG_FILE endif @@ -1289,6 +1407,7 @@ OBJS += $(SHA1OBJS) $(DESOBJS) OBJS_p += $(SHA1OBJS) +OBJS_p += $(SHA256OBJS) ifdef CONFIG_BGSCAN_SIMPLE L_CFLAGS += -DCONFIG_BGSCAN_SIMPLE @@ -1307,8 +1426,36 @@ OBJS += bgscan.c endif +ifdef CONFIG_AUTOSCAN_EXPONENTIAL +L_CFLAGS += -DCONFIG_AUTOSCAN_EXPONENTIAL +OBJS += autoscan_exponential.c +NEED_AUTOSCAN=y +endif + +ifdef CONFIG_AUTOSCAN_PERIODIC +L_CFLAGS += -DCONFIG_AUTOSCAN_PERIODIC +OBJS += autoscan_periodic.c +NEED_AUTOSCAN=y +endif + +ifdef NEED_AUTOSCAN +L_CFLAGS += -DCONFIG_AUTOSCAN +OBJS += autoscan.c +endif + +ifdef CONFIG_EXT_PASSWORD_TEST +OBJS += src/utils/ext_password_test.c +L_CFLAGS += -DCONFIG_EXT_PASSWORD_TEST +NEED_EXT_PASSWORD=y +endif + +ifdef NEED_EXT_PASSWORD +OBJS += src/utils/ext_password.c +L_CFLAGS += -DCONFIG_EXT_PASSWORD +endif + ifdef NEED_GAS -OBJS += ../src/common/gas.c +OBJS += src/common/gas.c OBJS += gas_query.c L_CFLAGS += -DCONFIG_GAS NEED_OFFCHANNEL=y @@ -1346,6 +1493,9 @@ OBJS_priv += src/utils/wpa_debug.c OBJS_priv += src/utils/wpabuf.c OBJS_priv += wpa_priv.c +ifdef CONFIG_DRIVER_NL80211 +OBJS_priv += src/common/ieee802_11_common.c +endif ifdef CONFIG_DRIVER_TEST OBJS_priv += $(SHA1OBJS) OBJS_priv += $(MD5OBJS) @@ -1389,14 +1539,12 @@ LDO=$(CC) endif -ifeq ($(WPA_BUILD_SUPPLICANT),true) - ######################## include $(CLEAR_VARS) LOCAL_MODULE := wpa_cli LOCAL_MODULE_TAGS := debug -LOCAL_SHARED_LIBRARIES := libc libcutils +LOCAL_SHARED_LIBRARIES := libc libcutils liblog LOCAL_CFLAGS := $(L_CFLAGS) LOCAL_SRC_FILES := $(OBJS_c) LOCAL_C_INCLUDES := $(INCLUDES) @@ -1411,9 +1559,13 @@ ifneq ($(BOARD_WPA_SUPPLICANT_PRIVATE_LIB),) LOCAL_STATIC_LIBRARIES += $(BOARD_WPA_SUPPLICANT_PRIVATE_LIB) endif -LOCAL_SHARED_LIBRARIES := libc libcutils +LOCAL_SHARED_LIBRARIES := libc libcutils liblog +ifdef CONFIG_EAP_PROXY +LOCAL_STATIC_LIBRARIES += $(LIB_STATIC_EAP_PROXY) +LOCAL_SHARED_LIBRARIES += $(LIB_SHARED_EAP_PROXY) +endif ifeq ($(CONFIG_TLS), openssl) -LOCAL_SHARED_LIBRARIES += libcrypto libssl +LOCAL_SHARED_LIBRARIES += libcrypto libssl libkeystore_binder endif ifdef CONFIG_DRIVER_NL80211 LOCAL_STATIC_LIBRARIES += libnl_2 @@ -1442,7 +1594,6 @@ # #include $(CLEAR_VARS) #LOCAL_MODULE := wpa_supplicant.conf -#LOCAL_MODULE_TAGS := user #LOCAL_MODULE_CLASS := ETC #LOCAL_MODULE_PATH := $(local_target_dir) #LOCAL_SRC_FILES := $(LOCAL_MODULE) @@ -1450,14 +1601,12 @@ # ######################## -endif # ifeq ($(WPA_BUILD_SUPPLICANT),true) - include $(CLEAR_VARS) LOCAL_MODULE = libwpa_client LOCAL_CFLAGS = $(L_CFLAGS) LOCAL_SRC_FILES = src/common/wpa_ctrl.c src/utils/os_$(CONFIG_OS).c LOCAL_C_INCLUDES = $(INCLUDES) -LOCAL_SHARED_LIBRARIES := libcutils +LOCAL_SHARED_LIBRARIES := libcutils liblog LOCAL_COPY_HEADERS_TO := libwpa_client LOCAL_COPY_HEADERS := src/common/wpa_ctrl.h include $(BUILD_SHARED_LIBRARY) diff -Nru wpa-1.0/wpa_supplicant/ap.c wpa-2.1/wpa_supplicant/ap.c --- wpa-1.0/wpa_supplicant/ap.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/ap.c 2014-02-04 11:23:35.000000000 +0000 @@ -3,14 +3,8 @@ * Copyright (c) 2003-2009, Jouni Malinen * Copyright (c) 2009, Atheros Communications * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "utils/includes.h" @@ -20,6 +14,8 @@ #include "utils/uuid.h" #include "common/ieee802_11_defs.h" #include "common/wpa_ctrl.h" +#include "eapol_supp/eapol_supp_sm.h" +#include "crypto/dh_group5.h" #include "ap/hostapd.h" #include "ap/ap_config.h" #include "ap/ap_drv_ops.h" @@ -30,9 +26,6 @@ #include "ap/ieee802_1x.h" #include "ap/wps_hostapd.h" #include "ap/ctrl_iface_ap.h" -#include "eap_common/eap_defs.h" -#include "eap_server/eap_methods.h" -#include "eap_common/eap_wsc_common.h" #include "wps/wps.h" #include "common/ieee802_11_defs.h" #include "config_ssid.h" @@ -50,29 +43,46 @@ #endif /* CONFIG_WPS */ +#ifdef CONFIG_IEEE80211N +static void wpas_conf_ap_vht(struct wpa_supplicant *wpa_s, + struct hostapd_config *conf, + struct hostapd_hw_modes *mode) +{ + u8 center_chan = 0; + u8 channel = conf->channel; + + if (!conf->secondary_channel) + goto no_vht; + + center_chan = wpas_p2p_get_vht80_center(wpa_s, mode, channel); + if (!center_chan) + goto no_vht; + + /* Use 80 MHz channel */ + conf->vht_oper_chwidth = 1; + conf->vht_oper_centr_freq_seg0_idx = center_chan; + return; + +no_vht: + conf->vht_oper_centr_freq_seg0_idx = + channel + conf->secondary_channel * 2; +} +#endif /* CONFIG_IEEE80211N */ + + static int wpa_supplicant_conf_ap(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, struct hostapd_config *conf) { - struct hostapd_bss_config *bss = &conf->bss[0]; - int pairwise; + struct hostapd_bss_config *bss = conf->bss[0]; conf->driver = wpa_s->driver; os_strlcpy(bss->iface, wpa_s->ifname, sizeof(bss->iface)); - if (ssid->frequency == 0) { - /* default channel 11 */ - conf->hw_mode = HOSTAPD_MODE_IEEE80211G; - conf->channel = 11; - } else if (ssid->frequency >= 2412 && ssid->frequency <= 2472) { - conf->hw_mode = HOSTAPD_MODE_IEEE80211G; - conf->channel = (ssid->frequency - 2407) / 5; - } else if ((ssid->frequency >= 5180 && ssid->frequency <= 5240) || - (ssid->frequency >= 5745 && ssid->frequency <= 5825)) { - conf->hw_mode = HOSTAPD_MODE_IEEE80211A; - conf->channel = (ssid->frequency - 5000) / 5; - } else { + conf->hw_mode = ieee80211_freq_to_chan(ssid->frequency, + &conf->channel); + if (conf->hw_mode == NUM_HOSTAPD_MODES) { wpa_printf(MSG_ERROR, "Unsupported AP mode frequency: %d MHz", ssid->frequency); return -1; @@ -83,26 +93,68 @@ #ifdef CONFIG_IEEE80211N /* - * Enable HT20 if the driver supports it, by setting conf->ieee80211n. + * Enable HT20 if the driver supports it, by setting conf->ieee80211n + * and a mask of allowed capabilities within conf->ht_capab. * Using default config settings for: conf->ht_op_mode_fixed, - * conf->ht_capab, conf->secondary_channel, conf->require_ht + * conf->secondary_channel, conf->require_ht */ if (wpa_s->hw.modes) { struct hostapd_hw_modes *mode = NULL; - int i; + int i, no_ht = 0; for (i = 0; i < wpa_s->hw.num_modes; i++) { if (wpa_s->hw.modes[i].mode == conf->hw_mode) { mode = &wpa_s->hw.modes[i]; break; } } - if (mode && mode->ht_capab) + +#ifdef CONFIG_HT_OVERRIDES + if (ssid->disable_ht) { + conf->ieee80211n = 0; + conf->ht_capab = 0; + no_ht = 1; + } +#endif /* CONFIG_HT_OVERRIDES */ + + if (!no_ht && mode && mode->ht_capab) { conf->ieee80211n = 1; +#ifdef CONFIG_P2P + if (conf->hw_mode == HOSTAPD_MODE_IEEE80211A && + (mode->ht_capab & + HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET) && + ssid->ht40) + conf->secondary_channel = + wpas_p2p_get_ht40_mode(wpa_s, mode, + conf->channel); + if (conf->secondary_channel) + conf->ht_capab |= + HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET; +#endif /* CONFIG_P2P */ + + /* + * white-list capabilities that won't cause issues + * to connecting stations, while leaving the current + * capabilities intact (currently disabled SMPS). + */ + conf->ht_capab |= mode->ht_capab & + (HT_CAP_INFO_GREEN_FIELD | + HT_CAP_INFO_SHORT_GI20MHZ | + HT_CAP_INFO_SHORT_GI40MHZ | + HT_CAP_INFO_RX_STBC_MASK | + HT_CAP_INFO_MAX_AMSDU_SIZE); + + if (mode->vht_capab && ssid->vht) { + conf->ieee80211ac = 1; + wpas_conf_ap_vht(wpa_s, conf, mode); + } + } } #endif /* CONFIG_IEEE80211N */ #ifdef CONFIG_P2P - if (conf->hw_mode == HOSTAPD_MODE_IEEE80211G) { + if (conf->hw_mode == HOSTAPD_MODE_IEEE80211G && + (ssid->mode == WPAS_MODE_P2P_GO || + ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION)) { /* Remove 802.11b rates from supported and basic rate sets */ int *list = os_malloc(4 * sizeof(int)); if (list) { @@ -129,6 +181,17 @@ } bss->isolate = !wpa_s->conf->p2p_intra_bss; + bss->force_per_enrollee_psk = wpa_s->global->p2p_per_sta_psk; + + if (ssid->p2p_group) { + os_memcpy(bss->ip_addr_go, wpa_s->parent->conf->ip_addr_go, 4); + os_memcpy(bss->ip_addr_mask, wpa_s->parent->conf->ip_addr_mask, + 4); + os_memcpy(bss->ip_addr_start, + wpa_s->parent->conf->ip_addr_start, 4); + os_memcpy(bss->ip_addr_end, wpa_s->parent->conf->ip_addr_end, + 4); + } #endif /* CONFIG_P2P */ if (ssid->ssid_len == 0) { @@ -136,10 +199,11 @@ return -1; } os_memcpy(bss->ssid.ssid, ssid->ssid, ssid->ssid_len); - bss->ssid.ssid[ssid->ssid_len] = '\0'; bss->ssid.ssid_len = ssid->ssid_len; bss->ssid.ssid_set = 1; + bss->ignore_broadcast_ssid = ssid->ignore_broadcast_ssid; + if (ssid->auth_alg) bss->auth_algs = ssid->auth_alg; @@ -147,15 +211,15 @@ bss->wpa = ssid->proto; bss->wpa_key_mgmt = ssid->key_mgmt; bss->wpa_pairwise = ssid->pairwise_cipher; - if (ssid->passphrase) { - bss->ssid.wpa_passphrase = os_strdup(ssid->passphrase); - } else if (ssid->psk_set) { + if (ssid->psk_set) { os_free(bss->ssid.wpa_psk); bss->ssid.wpa_psk = os_zalloc(sizeof(struct hostapd_wpa_psk)); if (bss->ssid.wpa_psk == NULL) return -1; os_memcpy(bss->ssid.wpa_psk->psk, ssid->psk, PMK_LEN); bss->ssid.wpa_psk->group = 1; + } else if (ssid->passphrase) { + bss->ssid.wpa_passphrase = os_strdup(ssid->passphrase); } else if (ssid->wep_key_len[0] || ssid->wep_key_len[1] || ssid->wep_key_len[2] || ssid->wep_key_len[3]) { struct hostapd_wep_keys *wep = &bss->ssid.wep; @@ -174,19 +238,23 @@ wep->keys_set = 1; } - /* Select group cipher based on the enabled pairwise cipher suites */ - pairwise = 0; - if (bss->wpa & 1) - pairwise |= bss->wpa_pairwise; - if (bss->wpa & 2) { - if (bss->rsn_pairwise == 0) - bss->rsn_pairwise = bss->wpa_pairwise; - pairwise |= bss->rsn_pairwise; - } - if (pairwise & WPA_CIPHER_TKIP) - bss->wpa_group = WPA_CIPHER_TKIP; - else - bss->wpa_group = WPA_CIPHER_CCMP; + if (ssid->ap_max_inactivity) + bss->ap_max_inactivity = ssid->ap_max_inactivity; + + if (ssid->dtim_period) + bss->dtim_period = ssid->dtim_period; + else if (wpa_s->conf->dtim_period) + bss->dtim_period = wpa_s->conf->dtim_period; + + if (ssid->beacon_int) + conf->beacon_int = ssid->beacon_int; + else if (wpa_s->conf->beacon_int) + conf->beacon_int = wpa_s->conf->beacon_int; + + 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->wpa && bss->ieee802_1x) bss->ssid.security_policy = SECURITY_WPA; @@ -217,6 +285,18 @@ bss->rsn_pairwise = WPA_CIPHER_NONE; } + if (bss->wpa_group_rekey < 86400 && (bss->wpa & 2) && + (bss->wpa_group == WPA_CIPHER_CCMP || + bss->wpa_group == WPA_CIPHER_GCMP || + bss->wpa_group == WPA_CIPHER_CCMP_256 || + bss->wpa_group == WPA_CIPHER_GCMP_256)) { + /* + * Strong ciphers do not need frequent rekeying, so increase + * the default GTK rekeying period to 24 hours. + */ + bss->wpa_group_rekey = 86400; + } + #ifdef CONFIG_WPS /* * Enable WPS by default for open and WPA/WPA2-Personal network, but @@ -228,12 +308,15 @@ goto no_wps; #ifdef CONFIG_WPS2 if (bss->ssid.security_policy == SECURITY_WPA_PSK && - (!(pairwise & WPA_CIPHER_CCMP) || !(bss->wpa & 2))) + (!(bss->rsn_pairwise & WPA_CIPHER_CCMP) || !(bss->wpa & 2))) goto no_wps; /* WPS2 does not allow WPA/TKIP-only * configuration */ #endif /* CONFIG_WPS2 */ bss->eap_server = 1; - bss->wps_state = 2; + + if (!ssid->ignore_broadcast_ssid) + bss->wps_state = 2; + bss->ap_setup_locked = 2; if (wpa_s->conf->config_methods) bss->config_methods = os_strdup(wpa_s->conf->config_methods); @@ -256,6 +339,7 @@ else os_memcpy(bss->uuid, wpa_s->conf->uuid, WPS_UUID_LEN); os_memcpy(bss->os_version, wpa_s->conf->os_version, 4); + bss->pbc_in_m1 = wpa_s->conf->pbc_in_m1; no_wps: #endif /* CONFIG_WPS */ @@ -267,6 +351,11 @@ bss->disassoc_low_ack = wpa_s->conf->disassoc_low_ack; + if (wpa_s->conf->ap_vendor_elements) { + bss->vendor_elements = + wpabuf_dup(wpa_s->conf->ap_vendor_elements); + } + return 0; } @@ -282,6 +371,8 @@ hdr_len = (const u8 *) &mgmt->u.action.u.vs_public_action.action - buf; if (hdr_len > len) return; + if (mgmt->u.action.category != WLAN_ACTION_PUBLIC) + return; wpas_p2p_rx_action(wpa_s, mgmt->da, mgmt->sa, mgmt->bssid, mgmt->u.action.category, &mgmt->u.action.u.vs_public_action.action, @@ -323,6 +414,19 @@ } +#ifdef CONFIG_P2P +static void ap_new_psk_cb(void *ctx, const u8 *mac_addr, const u8 *p2p_dev_addr, + const u8 *psk, size_t psk_len) +{ + + struct wpa_supplicant *wpa_s = ctx; + if (wpa_s->ap_iface == NULL || wpa_s->current_ssid == NULL) + return; + wpas_p2p_new_psk_cb(wpa_s, mac_addr, p2p_dev_addr, psk, psk_len); +} +#endif /* CONFIG_P2P */ + + static int ap_vendor_action_rx(void *ctx, const u8 *buf, size_t len, int freq) { #ifdef CONFIG_P2P @@ -344,11 +448,13 @@ static int ap_probe_req_rx(void *ctx, const u8 *sa, const u8 *da, - const u8 *bssid, const u8 *ie, size_t ie_len) + const u8 *bssid, const u8 *ie, size_t ie_len, + int ssi_signal) { #ifdef CONFIG_P2P struct wpa_supplicant *wpa_s = ctx; - return wpas_p2p_probe_req_rx(wpa_s, sa, da, bssid, ie, ie_len); + return wpas_p2p_probe_req_rx(wpa_s, sa, da, bssid, ie, ie_len, + ssi_signal); #else /* CONFIG_P2P */ return 0; #endif /* CONFIG_P2P */ @@ -411,6 +517,8 @@ params.mode = IEEE80211_MODE_AP; break; } + if (ssid->frequency == 0) + ssid->frequency = 2462; /* default channel 11 */ params.freq = ssid->frequency; params.wpa_proto = ssid->proto; @@ -418,20 +526,16 @@ wpa_s->key_mgmt = WPA_KEY_MGMT_PSK; else wpa_s->key_mgmt = WPA_KEY_MGMT_NONE; - params.key_mgmt_suite = key_mgmt2driver(wpa_s->key_mgmt); + params.key_mgmt_suite = wpa_s->key_mgmt; - if (ssid->pairwise_cipher & WPA_CIPHER_CCMP) - wpa_s->pairwise_cipher = WPA_CIPHER_CCMP; - else if (ssid->pairwise_cipher & WPA_CIPHER_TKIP) - wpa_s->pairwise_cipher = WPA_CIPHER_TKIP; - else if (ssid->pairwise_cipher & WPA_CIPHER_NONE) - wpa_s->pairwise_cipher = WPA_CIPHER_NONE; - else { + wpa_s->pairwise_cipher = wpa_pick_pairwise_cipher(ssid->pairwise_cipher, + 1); + if (wpa_s->pairwise_cipher < 0) { wpa_printf(MSG_WARNING, "WPA: Failed to select pairwise " "cipher."); return -1; } - params.pairwise_suite = cipher_suite2driver(wpa_s->pairwise_cipher); + params.pairwise_suite = wpa_s->pairwise_cipher; params.group_suite = params.pairwise_suite; #ifdef CONFIG_P2P @@ -455,6 +559,10 @@ return -1; hapd_iface->owner = wpa_s; hapd_iface->drv_flags = wpa_s->drv_flags; + hapd_iface->probe_resp_offloads = wpa_s->probe_resp_offloads; + hapd_iface->extended_capa = wpa_s->extended_capa; + hapd_iface->extended_capa_mask = wpa_s->extended_capa_mask; + hapd_iface->extended_capa_len = wpa_s->extended_capa_len; wpa_s->ap_iface->conf = conf = hostapd_config_defaults(); if (conf == NULL) { @@ -462,9 +570,13 @@ return -1; } + os_memcpy(wpa_s->ap_iface->conf->wmm_ac_params, + wpa_s->conf->wmm_ac_params, + sizeof(wpa_s->conf->wmm_ac_params)); + if (params.uapsd > 0) { - conf->bss->wmm_enabled = 1; - conf->bss->wmm_uapsd = 1; + conf->bss[0]->wmm_enabled = 1; + conf->bss[0]->wmm_uapsd = 1; } if (wpa_supplicant_conf_ap(wpa_s, ssid, conf)) { @@ -475,14 +587,14 @@ #ifdef CONFIG_P2P if (ssid->mode == WPAS_MODE_P2P_GO) - conf->bss[0].p2p = P2P_ENABLED | P2P_GROUP_OWNER; + conf->bss[0]->p2p = P2P_ENABLED | P2P_GROUP_OWNER; else if (ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION) - conf->bss[0].p2p = P2P_ENABLED | P2P_GROUP_OWNER | + conf->bss[0]->p2p = P2P_ENABLED | P2P_GROUP_OWNER | P2P_GROUP_FORMATION; #endif /* CONFIG_P2P */ hapd_iface->num_bss = conf->num_bss; - hapd_iface->bss = os_zalloc(conf->num_bss * + hapd_iface->bss = os_calloc(conf->num_bss, sizeof(struct hostapd_data *)); if (hapd_iface->bss == NULL) { wpa_supplicant_ap_deinit(wpa_s); @@ -492,7 +604,7 @@ for (i = 0; i < conf->num_bss; i++) { hapd_iface->bss[i] = hostapd_alloc_bss_data(hapd_iface, conf, - &conf->bss[i]); + conf->bss[i]); if (hapd_iface->bss[i] == NULL) { wpa_supplicant_ap_deinit(wpa_s); return -1; @@ -513,10 +625,11 @@ hapd_iface->bss[i]->sta_authorized_cb = ap_sta_authorized_cb; hapd_iface->bss[i]->sta_authorized_cb_ctx = wpa_s; #ifdef CONFIG_P2P + hapd_iface->bss[i]->new_psk_cb = ap_new_psk_cb; + hapd_iface->bss[i]->new_psk_cb_ctx = wpa_s; hapd_iface->bss[i]->p2p = wpa_s->global->p2p; - hapd_iface->bss[i]->p2p_group = wpas_p2p_group_init( - wpa_s, ssid->p2p_persistent_group, - ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION); + hapd_iface->bss[i]->p2p_group = wpas_p2p_group_init(wpa_s, + ssid); #endif /* CONFIG_P2P */ hapd_iface->bss[i]->setup_complete_cb = wpas_ap_configured_cb; hapd_iface->bss[i]->setup_complete_cb_ctx = wpa_s; @@ -527,6 +640,7 @@ hapd_iface->bss[0]->drv_priv = wpa_s->drv_priv; wpa_s->current_ssid = ssid; + eapol_sm_notify_config(wpa_s->eapol, NULL, NULL); os_memcpy(wpa_s->bssid, wpa_s->own_addr, ETH_ALEN); wpa_s->assoc_freq = ssid->frequency; @@ -550,8 +664,8 @@ return; wpa_s->current_ssid = NULL; + eapol_sm_notify_config(wpa_s->eapol, NULL, NULL); wpa_s->assoc_freq = 0; - wpa_s->reassociated_connection = 0; #ifdef CONFIG_P2P if (wpa_s->ap_iface->bss) wpa_s->ap_iface->bss[0]->p2p_group = NULL; @@ -574,6 +688,18 @@ } +void ap_eapol_tx_status(void *ctx, const u8 *dst, + const u8 *data, size_t len, int ack) +{ +#ifdef NEED_AP_MLME + struct wpa_supplicant *wpa_s = ctx; + if (!wpa_s->ap_iface) + return; + hostapd_tx_status(wpa_s->ap_iface->bss[0], dst, data, len, ack); +#endif /* NEED_AP_MLME */ +} + + void ap_client_poll_ok(void *ctx, const u8 *addr) { #ifdef NEED_AP_MLME @@ -635,21 +761,6 @@ } -static int wpa_supplicant_ap_wps_sta_cancel(struct hostapd_data *hapd, - struct sta_info *sta, void *ctx) -{ - if (sta && (sta->flags & WLAN_STA_WPS)) { - ap_sta_deauthenticate(hapd, sta, - WLAN_REASON_PREV_AUTH_NOT_VALID); - wpa_printf(MSG_DEBUG, "WPS: %s: Deauth sta=" MACSTR, - __func__, MAC2STR(sta->addr)); - return 1; - } - - return 0; -} - - int wpa_supplicant_ap_wps_cancel(struct wpa_supplicant *wpa_s) { struct wps_registrar *reg; @@ -661,7 +772,7 @@ reg = wpa_s->ap_iface->bss[0]->wps->registrar; reg_sel = wps_registrar_wps_cancel(reg); wps_sta = ap_for_each_sta(wpa_s->ap_iface->bss[0], - wpa_supplicant_ap_wps_sta_cancel, NULL); + ap_sta_wps_cancel, NULL); if (!reg_sel && !wps_sta) { wpa_printf(MSG_DEBUG, "No WPS operation in progress at this " @@ -681,7 +792,8 @@ int wpa_supplicant_ap_wps_pin(struct wpa_supplicant *wpa_s, const u8 *bssid, - const char *pin, char *buf, size_t buflen) + const char *pin, char *buf, size_t buflen, + int timeout) { int ret, ret_len = 0; @@ -696,7 +808,7 @@ ret_len = os_snprintf(buf, buflen, "%s", pin); ret = hostapd_wps_add_pin(wpa_s->ap_iface->bss[0], bssid, "any", pin, - 0); + timeout); if (ret) return -1; return ret_len; @@ -819,6 +931,47 @@ hapd->conf->ap_pin = NULL; } + +#ifdef CONFIG_WPS_NFC + +struct wpabuf * wpas_ap_wps_nfc_config_token(struct wpa_supplicant *wpa_s, + int ndef) +{ + struct hostapd_data *hapd; + + if (wpa_s->ap_iface == NULL) + return NULL; + hapd = wpa_s->ap_iface->bss[0]; + return hostapd_wps_nfc_config_token(hapd, ndef); +} + + +struct wpabuf * wpas_ap_wps_nfc_handover_sel(struct wpa_supplicant *wpa_s, + int ndef) +{ + struct hostapd_data *hapd; + + if (wpa_s->ap_iface == NULL) + return NULL; + hapd = wpa_s->ap_iface->bss[0]; + return hostapd_wps_nfc_hs_cr(hapd, ndef); +} + + +int wpas_ap_wps_nfc_report_handover(struct wpa_supplicant *wpa_s, + const struct wpabuf *req, + const struct wpabuf *sel) +{ + struct hostapd_data *hapd; + + if (wpa_s->ap_iface == NULL) + return -1; + hapd = wpa_s->ap_iface->bss[0]; + return hostapd_wps_nfc_report_handover(hapd, req, sel); +} + +#endif /* CONFIG_WPS_NFC */ + #endif /* CONFIG_WPS */ @@ -854,6 +1007,26 @@ } +int ap_ctrl_iface_sta_disassociate(struct wpa_supplicant *wpa_s, + const char *txtaddr) +{ + if (wpa_s->ap_iface == NULL) + return -1; + return hostapd_ctrl_iface_disassociate(wpa_s->ap_iface->bss[0], + txtaddr); +} + + +int ap_ctrl_iface_sta_deauthenticate(struct wpa_supplicant *wpa_s, + const char *txtaddr) +{ + if (wpa_s->ap_iface == NULL) + return -1; + return hostapd_ctrl_iface_deauthenticate(wpa_s->ap_iface->bss[0], + txtaddr); +} + + int ap_ctrl_iface_wpa_get_status(struct wpa_supplicant *wpa_s, char *buf, size_t buflen, int verbose) { @@ -898,9 +1071,9 @@ #ifdef CONFIG_P2P if (ssid->mode == WPAS_MODE_P2P_GO) - iface->conf->bss[0].p2p = P2P_ENABLED | P2P_GROUP_OWNER; + iface->conf->bss[0]->p2p = P2P_ENABLED | P2P_GROUP_OWNER; else if (ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION) - iface->conf->bss[0].p2p = P2P_ENABLED | P2P_GROUP_OWNER | + iface->conf->bss[0]->p2p = P2P_ENABLED | P2P_GROUP_OWNER | P2P_GROUP_FORMATION; #endif /* CONFIG_P2P */ @@ -914,6 +1087,43 @@ } +int ap_switch_channel(struct wpa_supplicant *wpa_s, + struct csa_settings *settings) +{ +#ifdef NEED_AP_MLME + if (!wpa_s->ap_iface || !wpa_s->ap_iface->bss[0]) + return -1; + + return hostapd_switch_channel(wpa_s->ap_iface->bss[0], settings); +#else /* NEED_AP_MLME */ + return -1; +#endif /* NEED_AP_MLME */ +} + + +int ap_ctrl_iface_chanswitch(struct wpa_supplicant *wpa_s, const char *pos) +{ + struct csa_settings settings; + int ret = hostapd_parse_csa_settings(pos, &settings); + + if (ret) + return ret; + + return ap_switch_channel(wpa_s, &settings); +} + + +void wpas_ap_ch_switch(struct wpa_supplicant *wpa_s, int freq, int ht, + int offset, int width, int cf1, int cf2) +{ + if (!wpa_s->ap_iface) + return; + + wpa_s->assoc_freq = freq; + hostapd_event_ch_switch(wpa_s->ap_iface->bss[0], freq, ht, offset, width, cf1, cf1); +} + + int wpa_supplicant_ap_mac_addr_filter(struct wpa_supplicant *wpa_s, const u8 *addr) { @@ -953,3 +1163,48 @@ return 0; } + + +#ifdef CONFIG_WPS_NFC +int wpas_ap_wps_add_nfc_pw(struct wpa_supplicant *wpa_s, u16 pw_id, + const struct wpabuf *pw, const u8 *pubkey_hash) +{ + struct hostapd_data *hapd; + struct wps_context *wps; + + if (!wpa_s->ap_iface) + return -1; + hapd = wpa_s->ap_iface->bss[0]; + wps = hapd->wps; + + if (wpa_s->parent->conf->wps_nfc_dh_pubkey == NULL || + wpa_s->parent->conf->wps_nfc_dh_privkey == NULL) { + wpa_printf(MSG_DEBUG, "P2P: No NFC DH key known"); + return -1; + } + + dh5_free(wps->dh_ctx); + wpabuf_free(wps->dh_pubkey); + wpabuf_free(wps->dh_privkey); + wps->dh_privkey = wpabuf_dup( + wpa_s->parent->conf->wps_nfc_dh_privkey); + wps->dh_pubkey = wpabuf_dup( + wpa_s->parent->conf->wps_nfc_dh_pubkey); + if (wps->dh_privkey == NULL || wps->dh_pubkey == NULL) { + wps->dh_ctx = NULL; + wpabuf_free(wps->dh_pubkey); + wps->dh_pubkey = NULL; + wpabuf_free(wps->dh_privkey); + wps->dh_privkey = NULL; + return -1; + } + wps->dh_ctx = dh5_init_fixed(wps->dh_privkey, wps->dh_pubkey); + if (wps->dh_ctx == NULL) + return -1; + + return wps_registrar_add_nfc_pw_token(hapd->wps->registrar, pubkey_hash, + pw_id, + pw ? wpabuf_head(pw) : NULL, + pw ? wpabuf_len(pw) : 0, 1); +} +#endif /* CONFIG_WPS_NFC */ diff -Nru wpa-1.0/wpa_supplicant/ap.h wpa-2.1/wpa_supplicant/ap.h --- wpa-1.0/wpa_supplicant/ap.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/ap.h 2014-02-04 11:23:35.000000000 +0000 @@ -3,14 +3,8 @@ * Copyright (c) 2003-2009, Jouni Malinen * Copyright (c) 2009, Atheros Communications * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef AP_H @@ -24,7 +18,8 @@ int wpa_supplicant_ap_wps_pbc(struct wpa_supplicant *wpa_s, const u8 *bssid, const u8 *p2p_dev_addr); int wpa_supplicant_ap_wps_pin(struct wpa_supplicant *wpa_s, const u8 *bssid, - const char *pin, char *buf, size_t buflen); + const char *pin, char *buf, size_t buflen, + int timeout); int wpa_supplicant_ap_wps_cancel(struct wpa_supplicant *wpa_s); void wpas_wps_ap_pin_disable(struct wpa_supplicant *wpa_s); const char * wpas_wps_ap_pin_random(struct wpa_supplicant *wpa_s, int timeout); @@ -37,10 +32,16 @@ char *buf, size_t buflen); int ap_ctrl_iface_sta_next(struct wpa_supplicant *wpa_s, const char *txtaddr, char *buf, size_t buflen); +int ap_ctrl_iface_sta_deauthenticate(struct wpa_supplicant *wpa_s, + const char *txtaddr); +int ap_ctrl_iface_sta_disassociate(struct wpa_supplicant *wpa_s, + const char *txtaddr); int ap_ctrl_iface_wpa_get_status(struct wpa_supplicant *wpa_s, char *buf, size_t buflen, int verbose); void ap_tx_status(void *ctx, const u8 *addr, const u8 *buf, size_t len, int ack); +void ap_eapol_tx_status(void *ctx, const u8 *dst, + const u8 *data, size_t len, int ack); void ap_client_poll_ok(void *ctx, const u8 *addr); void ap_rx_from_unknown_sta(void *ctx, const u8 *addr, int wds); void ap_mgmt_rx(void *ctx, struct rx_mgmt *rx_mgmt); @@ -49,5 +50,29 @@ int wpa_supplicant_ap_mac_addr_filter(struct wpa_supplicant *wpa_s, const u8 *addr); void wpa_supplicant_ap_pwd_auth_fail(struct wpa_supplicant *wpa_s); +int ap_switch_channel(struct wpa_supplicant *wpa_s, + struct csa_settings *settings); +int ap_ctrl_iface_chanswitch(struct wpa_supplicant *wpa_s, const char *txtaddr); +void wpas_ap_ch_switch(struct wpa_supplicant *wpa_s, int freq, int ht, + int offset, int width, int cf1, int cf2); +struct wpabuf * wpas_ap_wps_nfc_config_token(struct wpa_supplicant *wpa_s, + int ndef); +#ifdef CONFIG_AP +struct wpabuf * wpas_ap_wps_nfc_handover_sel(struct wpa_supplicant *wpa_s, + int ndef); +#else /* CONFIG_AP */ +static inline struct wpabuf * +wpas_ap_wps_nfc_handover_sel(struct wpa_supplicant *wpa_s, + int ndef) +{ + return NULL; +} +#endif /* CONFIG_AP */ + +int wpas_ap_wps_nfc_report_handover(struct wpa_supplicant *wpa_s, + const struct wpabuf *req, + const struct wpabuf *sel); +int wpas_ap_wps_add_nfc_pw(struct wpa_supplicant *wpa_s, u16 pw_id, + const struct wpabuf *pw, const u8 *pubkey_hash); #endif /* AP_H */ diff -Nru wpa-1.0/wpa_supplicant/autoscan.c wpa-2.1/wpa_supplicant/autoscan.c --- wpa-1.0/wpa_supplicant/autoscan.c 1970-01-01 00:00:00.000000000 +0000 +++ wpa-2.1/wpa_supplicant/autoscan.c 2014-02-04 11:23:35.000000000 +0000 @@ -0,0 +1,143 @@ +/* + * WPA Supplicant - auto scan + * Copyright (c) 2012, Intel Corporation. All rights reserved. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "config.h" +#include "wpa_supplicant_i.h" +#include "bss.h" +#include "scan.h" +#include "autoscan.h" + +#ifdef CONFIG_AUTOSCAN_EXPONENTIAL +extern const struct autoscan_ops autoscan_exponential_ops; +#endif /* CONFIG_AUTOSCAN_EXPONENTIAL */ + +#ifdef CONFIG_AUTOSCAN_PERIODIC +extern const struct autoscan_ops autoscan_periodic_ops; +#endif /* CONFIG_AUTOSCAN_PERIODIC */ + +static const struct autoscan_ops * autoscan_modules[] = { +#ifdef CONFIG_AUTOSCAN_EXPONENTIAL + &autoscan_exponential_ops, +#endif /* CONFIG_AUTOSCAN_EXPONENTIAL */ +#ifdef CONFIG_AUTOSCAN_PERIODIC + &autoscan_periodic_ops, +#endif /* CONFIG_AUTOSCAN_PERIODIC */ + NULL +}; + + +static void request_scan(struct wpa_supplicant *wpa_s) +{ + wpa_s->scan_req = MANUAL_SCAN_REQ; + + if (wpa_supplicant_req_sched_scan(wpa_s)) + wpa_supplicant_req_scan(wpa_s, wpa_s->scan_interval, 0); +} + + +int autoscan_init(struct wpa_supplicant *wpa_s, int req_scan) +{ + const char *name = wpa_s->conf->autoscan; + const char *params; + size_t nlen; + int i; + const struct autoscan_ops *ops = NULL; + + if (wpa_s->autoscan && wpa_s->autoscan_priv) + return 0; + + if (name == NULL) + return 0; + + params = os_strchr(name, ':'); + if (params == NULL) { + params = ""; + nlen = os_strlen(name); + } else { + nlen = params - name; + params++; + } + + for (i = 0; autoscan_modules[i]; i++) { + if (os_strncmp(name, autoscan_modules[i]->name, nlen) == 0) { + ops = autoscan_modules[i]; + break; + } + } + + if (ops == NULL) { + wpa_printf(MSG_ERROR, "autoscan: Could not find module " + "matching the parameter '%s'", name); + return -1; + } + + wpa_s->autoscan_params = NULL; + + wpa_s->autoscan_priv = ops->init(wpa_s, params); + if (wpa_s->autoscan_priv == NULL) + return -1; + wpa_s->autoscan = ops; + + wpa_printf(MSG_DEBUG, "autoscan: Initialized module '%s' with " + "parameters '%s'", ops->name, params); + if (!req_scan) + return 0; + + /* + * Cancelling existing scan requests, if any. + */ + wpa_supplicant_cancel_sched_scan(wpa_s); + wpa_supplicant_cancel_scan(wpa_s); + + /* + * Firing first scan, which will lead to call autoscan_notify_scan. + */ + request_scan(wpa_s); + + return 0; +} + + +void autoscan_deinit(struct wpa_supplicant *wpa_s) +{ + if (wpa_s->autoscan && wpa_s->autoscan_priv) { + wpa_printf(MSG_DEBUG, "autoscan: Deinitializing module '%s'", + wpa_s->autoscan->name); + wpa_s->autoscan->deinit(wpa_s->autoscan_priv); + wpa_s->autoscan = NULL; + wpa_s->autoscan_priv = NULL; + + wpa_s->scan_interval = 5; + wpa_s->sched_scan_interval = 0; + } +} + + +int autoscan_notify_scan(struct wpa_supplicant *wpa_s, + struct wpa_scan_results *scan_res) +{ + int interval; + + if (wpa_s->autoscan && wpa_s->autoscan_priv) { + interval = wpa_s->autoscan->notify_scan(wpa_s->autoscan_priv, + scan_res); + + if (interval <= 0) + return -1; + + wpa_s->scan_interval = interval; + wpa_s->sched_scan_interval = interval; + + request_scan(wpa_s); + } + + return 0; +} diff -Nru wpa-1.0/wpa_supplicant/autoscan_exponential.c wpa-2.1/wpa_supplicant/autoscan_exponential.c --- wpa-1.0/wpa_supplicant/autoscan_exponential.c 1970-01-01 00:00:00.000000000 +0000 +++ wpa-2.1/wpa_supplicant/autoscan_exponential.c 2014-02-04 11:23:35.000000000 +0000 @@ -0,0 +1,104 @@ +/* + * WPA Supplicant - auto scan exponential module + * Copyright (c) 2012, Intel Corporation. All rights reserved. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "wpa_supplicant_i.h" +#include "autoscan.h" + +struct autoscan_exponential_data { + struct wpa_supplicant *wpa_s; + int base; + int limit; + int interval; +}; + + +static int +autoscan_exponential_get_params(struct autoscan_exponential_data *data, + const char *params) +{ + const char *pos; + + if (params == NULL) + return -1; + + data->base = atoi(params); + + pos = os_strchr(params, ':'); + if (pos == NULL) + return -1; + + pos++; + data->limit = atoi(pos); + + return 0; +} + + +static void * autoscan_exponential_init(struct wpa_supplicant *wpa_s, + const char *params) +{ + struct autoscan_exponential_data *data; + + data = os_zalloc(sizeof(struct autoscan_exponential_data)); + if (data == NULL) + return NULL; + + if (autoscan_exponential_get_params(data, params) < 0) { + os_free(data); + return NULL; + } + + wpa_printf(MSG_DEBUG, "autoscan exponential: base exponential is %d " + "and limit is %d", data->base, data->limit); + + data->wpa_s = wpa_s; + + return data; +} + + +static void autoscan_exponential_deinit(void *priv) +{ + struct autoscan_exponential_data *data = priv; + + os_free(data); +} + + +static int autoscan_exponential_notify_scan(void *priv, + struct wpa_scan_results *scan_res) +{ + struct autoscan_exponential_data *data = priv; + + wpa_printf(MSG_DEBUG, "autoscan exponential: scan result " + "notification"); + + if (data->interval >= data->limit) + return data->limit; + + if (data->interval <= 0) + data->interval = data->base; + else { + data->interval = data->interval * data->base; + if (data->interval > data->limit) + return data->limit; + } + + return data->interval; +} + + +const struct autoscan_ops autoscan_exponential_ops = { + .name = "exponential", + .init = autoscan_exponential_init, + .deinit = autoscan_exponential_deinit, + .notify_scan = autoscan_exponential_notify_scan, +}; diff -Nru wpa-1.0/wpa_supplicant/autoscan.h wpa-2.1/wpa_supplicant/autoscan.h --- wpa-1.0/wpa_supplicant/autoscan.h 1970-01-01 00:00:00.000000000 +0000 +++ wpa-2.1/wpa_supplicant/autoscan.h 2014-02-04 11:23:35.000000000 +0000 @@ -0,0 +1,49 @@ +/* + * WPA Supplicant - auto scan + * Copyright (c) 2012, Intel Corporation. All rights reserved. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef AUTOSCAN_H +#define AUTOSCAN_H + +struct wpa_supplicant; + +struct autoscan_ops { + const char *name; + + void * (*init)(struct wpa_supplicant *wpa_s, const char *params); + void (*deinit)(void *priv); + + int (*notify_scan)(void *priv, struct wpa_scan_results *scan_res); +}; + +#ifdef CONFIG_AUTOSCAN + +int autoscan_init(struct wpa_supplicant *wpa_s, int req_scan); +void autoscan_deinit(struct wpa_supplicant *wpa_s); +int autoscan_notify_scan(struct wpa_supplicant *wpa_s, + struct wpa_scan_results *scan_res); + +#else /* CONFIG_AUTOSCAN */ + +static inline int autoscan_init(struct wpa_supplicant *wpa_s, int req_scan) +{ + return 0; +} + +static inline void autoscan_deinit(struct wpa_supplicant *wpa_s) +{ +} + +static inline int autoscan_notify_scan(struct wpa_supplicant *wpa_s, + struct wpa_scan_results *scan_res) +{ + return 0; +} + +#endif /* CONFIG_AUTOSCAN */ + +#endif /* AUTOSCAN_H */ diff -Nru wpa-1.0/wpa_supplicant/autoscan_periodic.c wpa-2.1/wpa_supplicant/autoscan_periodic.c --- wpa-1.0/wpa_supplicant/autoscan_periodic.c 1970-01-01 00:00:00.000000000 +0000 +++ wpa-2.1/wpa_supplicant/autoscan_periodic.c 2014-02-04 11:23:35.000000000 +0000 @@ -0,0 +1,85 @@ +/* + * WPA Supplicant - auto scan periodic module + * Copyright (c) 2012, Intel Corporation. All rights reserved. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "wpa_supplicant_i.h" +#include "autoscan.h" + + +struct autoscan_periodic_data { + int periodic_interval; +}; + + +static int autoscan_periodic_get_params(struct autoscan_periodic_data *data, + const char *params) +{ + int interval; + + if (params == NULL) + return -1; + + interval = atoi(params); + + if (interval < 0) + return -1; + + data->periodic_interval = interval; + + return 0; +} + + +static void * autoscan_periodic_init(struct wpa_supplicant *wpa_s, + const char *params) +{ + struct autoscan_periodic_data *data; + + data = os_zalloc(sizeof(struct autoscan_periodic_data)); + if (data == NULL) + return NULL; + + if (autoscan_periodic_get_params(data, params) < 0) { + os_free(data); + return NULL; + } + + wpa_printf(MSG_DEBUG, "autoscan periodic: interval is %d", + data->periodic_interval); + + return data; +} + + +static void autoscan_periodic_deinit(void *priv) +{ + struct autoscan_periodic_data *data = priv; + + os_free(data); +} + + +static int autoscan_periodic_notify_scan(void *priv, + struct wpa_scan_results *scan_res) +{ + struct autoscan_periodic_data *data = priv; + + wpa_printf(MSG_DEBUG, "autoscan periodic: scan result notification"); + + return data->periodic_interval; +} + + +const struct autoscan_ops autoscan_periodic_ops = { + .name = "periodic", + .init = autoscan_periodic_init, + .deinit = autoscan_periodic_deinit, + .notify_scan = autoscan_periodic_notify_scan, +}; diff -Nru wpa-1.0/wpa_supplicant/bgscan.c wpa-2.1/wpa_supplicant/bgscan.c --- wpa-1.0/wpa_supplicant/bgscan.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/bgscan.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * WPA Supplicant - background scan and roaming interface * Copyright (c) 2009-2010, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -37,9 +31,9 @@ }; -int bgscan_init(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) +int bgscan_init(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, + const char *name) { - const char *name = ssid->bgscan; const char *params; size_t nlen; int i; @@ -47,7 +41,7 @@ bgscan_deinit(wpa_s); if (name == NULL) - return 0; + return -1; params = os_strchr(name, ':'); if (params == NULL) { diff -Nru wpa-1.0/wpa_supplicant/bgscan.h wpa-2.1/wpa_supplicant/bgscan.h --- wpa-1.0/wpa_supplicant/bgscan.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/bgscan.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * WPA Supplicant - background scan and roaming interface * Copyright (c) 2009-2010, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef BGSCAN_H @@ -35,7 +29,8 @@ #ifdef CONFIG_BGSCAN -int bgscan_init(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid); +int bgscan_init(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, + const char *name); void bgscan_deinit(struct wpa_supplicant *wpa_s); int bgscan_notify_scan(struct wpa_supplicant *wpa_s, struct wpa_scan_results *scan_res); @@ -47,7 +42,7 @@ #else /* CONFIG_BGSCAN */ static inline int bgscan_init(struct wpa_supplicant *wpa_s, - struct wpa_ssid *ssid) + struct wpa_ssid *ssid, const char name) { return 0; } diff -Nru wpa-1.0/wpa_supplicant/bgscan_learn.c wpa-2.1/wpa_supplicant/bgscan_learn.c --- wpa-1.0/wpa_supplicant/bgscan_learn.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/bgscan_learn.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * WPA Supplicant - background scan and roaming module: learn * Copyright (c) 2009-2010, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -40,7 +34,7 @@ int signal_threshold; int short_interval; /* use if signal < threshold */ int long_interval; /* use if signal > threshold */ - struct os_time last_bgscan; + struct os_reltime last_bgscan; char *fname; struct dl_list bss; int *supp_freqs; @@ -81,7 +75,7 @@ if (bssid_in_array(bss->neigh, bss->num_neigh, bssid)) return; - n = os_realloc(bss->neigh, (bss->num_neigh + 1) * ETH_ALEN); + n = os_realloc_array(bss->neigh, bss->num_neigh + 1, ETH_ALEN); if (n == NULL) return; @@ -225,7 +219,7 @@ dl_list_for_each(bss, &data->bss, struct bgscan_learn_bss, list) { if (in_array(freqs, bss->freq)) continue; - n = os_realloc(freqs, (*count + 2) * sizeof(int)); + n = os_realloc_array(freqs, *count + 2, sizeof(int)); if (n == NULL) return freqs; freqs = n; @@ -246,15 +240,15 @@ if (data->supp_freqs == NULL) return freqs; - idx = data->probe_idx + 1; - while (idx != data->probe_idx) { - if (data->supp_freqs[idx] == 0) - idx = 0; + idx = data->probe_idx; + do { if (!in_array(freqs, data->supp_freqs[idx])) { wpa_printf(MSG_DEBUG, "bgscan learn: Probe new freq " "%u", data->supp_freqs[idx]); - data->probe_idx = idx; - n = os_realloc(freqs, (count + 2) * sizeof(int)); + data->probe_idx = idx + 1; + if (data->supp_freqs[data->probe_idx] == 0) + data->probe_idx = 0; + n = os_realloc_array(freqs, count + 2, sizeof(int)); if (n == NULL) return freqs; freqs = n; @@ -265,7 +259,9 @@ } idx++; - } + if (data->supp_freqs[idx] == 0) + idx = 0; + } while (idx != data->probe_idx); return freqs; } @@ -314,7 +310,7 @@ eloop_register_timeout(data->scan_interval, 0, bgscan_learn_timeout, data, NULL); } else - os_get_time(&data->last_bgscan); + os_get_reltime(&data->last_bgscan); os_free(freqs); } @@ -366,7 +362,10 @@ for (j = 0; j < modes[i].num_channels; j++) { if (modes[i].channels[j].flag & HOSTAPD_CHAN_DISABLED) continue; - n = os_realloc(freqs, (count + 2) * sizeof(int)); + /* some hw modes (e.g. 11b & 11g) contain same freqs */ + if (in_array(freqs, modes[i].channels[j].freq)) + continue; + n = os_realloc_array(freqs, count + 2, sizeof(int)); if (n == NULL) continue; @@ -422,6 +421,14 @@ data->supp_freqs = bgscan_learn_get_supp_freqs(wpa_s); data->scan_interval = data->short_interval; + if (data->signal_threshold) { + /* Poll for signal info to set initial scan interval */ + struct wpa_signal_info siginfo; + if (wpa_drv_signal_poll(wpa_s, &siginfo) == 0 && + siginfo.current_signal >= data->signal_threshold) + data->scan_interval = data->long_interval; + } + eloop_register_timeout(data->scan_interval, 0, bgscan_learn_timeout, data, NULL); @@ -431,7 +438,7 @@ * us skip an immediate new scan in cases where the current signal * level is below the bgscan threshold. */ - os_get_time(&data->last_bgscan); + os_get_reltime(&data->last_bgscan); return data; } @@ -558,7 +565,7 @@ { struct bgscan_learn_data *data = priv; int scan = 0; - struct os_time now; + struct os_reltime now; if (data->short_interval == data->long_interval || data->signal_threshold == 0) @@ -572,7 +579,7 @@ wpa_printf(MSG_DEBUG, "bgscan learn: Start using short bgscan " "interval"); data->scan_interval = data->short_interval; - os_get_time(&now); + os_get_reltime(&now); if (now.sec > data->last_bgscan.sec + 1) scan = 1; } else if (data->scan_interval == data->short_interval && above) { @@ -587,7 +594,7 @@ * Signal dropped further 4 dB. Request a new scan if we have * not yet scanned in a while. */ - os_get_time(&now); + os_get_reltime(&now); if (now.sec > data->last_bgscan.sec + 10) scan = 1; } diff -Nru wpa-1.0/wpa_supplicant/bgscan_simple.c wpa-2.1/wpa_supplicant/bgscan_simple.c --- wpa-1.0/wpa_supplicant/bgscan_simple.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/bgscan_simple.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * WPA Supplicant - background scan and roaming module: simple * Copyright (c) 2009-2010, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -29,9 +23,10 @@ int scan_interval; int signal_threshold; int short_scan_count; /* counter for scans using short scan interval */ + int max_short_scans; /* maximum times we short-scan before back-off */ int short_interval; /* use if signal < threshold */ int long_interval; /* use if signal > threshold */ - struct os_time last_bgscan; + struct os_reltime last_bgscan; }; @@ -66,14 +61,21 @@ * scanning at the short scan interval. After that, * revert to the long scan interval. */ - if (data->short_scan_count > - data->long_interval / data->short_interval + 1) { + if (data->short_scan_count > data->max_short_scans) { data->scan_interval = data->long_interval; wpa_printf(MSG_DEBUG, "bgscan simple: Backing " "off to long scan interval"); } + } else if (data->short_scan_count > 0) { + /* + * If we lasted a long scan interval without any + * CQM triggers, decrease the short-scan count, + * which allows 1 more short-scan interval to + * occur in the future when CQM triggers. + */ + data->short_scan_count--; } - os_get_time(&data->last_bgscan); + os_get_reltime(&data->last_bgscan); } } @@ -138,6 +140,7 @@ } data->scan_interval = data->short_interval; + data->max_short_scans = data->long_interval / data->short_interval + 1; if (data->signal_threshold) { /* Poll for signal info to set initial scan interval */ struct wpa_signal_info siginfo; @@ -156,7 +159,7 @@ * us skip an immediate new scan in cases where the current signal * level is below the bgscan threshold. */ - os_get_time(&data->last_bgscan); + os_get_reltime(&data->last_bgscan); return data; } @@ -208,7 +211,7 @@ { struct bgscan_simple_data *data = priv; int scan = 0; - struct os_time now; + struct os_reltime now; if (data->short_interval == data->long_interval || data->signal_threshold == 0) @@ -222,9 +225,15 @@ wpa_printf(MSG_DEBUG, "bgscan simple: Start using short " "bgscan interval"); data->scan_interval = data->short_interval; - data->short_scan_count = 0; - os_get_time(&now); - if (now.sec > data->last_bgscan.sec + 1) + os_get_reltime(&now); + if (now.sec > data->last_bgscan.sec + 1 && + data->short_scan_count <= data->max_short_scans) + /* + * If we haven't just previously (<1 second ago) + * performed a scan, and we haven't depleted our + * budget for short-scans, perform a scan + * immediately. + */ scan = 1; else if (data->last_bgscan.sec + data->long_interval > now.sec + data->scan_interval) { @@ -250,7 +259,7 @@ * Signal dropped further 4 dB. Request a new scan if we have * not yet scanned in a while. */ - os_get_time(&now); + os_get_reltime(&now); if (now.sec > data->last_bgscan.sec + 10) scan = 1; } diff -Nru wpa-1.0/wpa_supplicant/blacklist.c wpa-2.1/wpa_supplicant/blacklist.c --- wpa-1.0/wpa_supplicant/blacklist.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/blacklist.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * wpa_supplicant - Temporary BSSID blacklist * Copyright (c) 2003-2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -29,6 +23,9 @@ { struct wpa_blacklist *e; + if (wpa_s == NULL || bssid == NULL) + return NULL; + e = wpa_s->blacklist; while (e) { if (os_memcmp(e->bssid, bssid, ETH_ALEN) == 0) @@ -60,6 +57,9 @@ { struct wpa_blacklist *e; + if (wpa_s == NULL || bssid == NULL) + return -1; + e = wpa_blacklist_get(wpa_s, bssid); if (e) { e->count++; @@ -93,6 +93,9 @@ { struct wpa_blacklist *e, *prev = NULL; + if (wpa_s == NULL || bssid == NULL) + return -1; + e = wpa_s->blacklist; while (e) { if (os_memcmp(e->bssid, bssid, ETH_ALEN) == 0) { @@ -120,14 +123,19 @@ void wpa_blacklist_clear(struct wpa_supplicant *wpa_s) { struct wpa_blacklist *e, *prev; + int max_count = 0; e = wpa_s->blacklist; wpa_s->blacklist = NULL; while (e) { + if (e->count > max_count) + max_count = e->count; prev = e; e = e->next; wpa_printf(MSG_DEBUG, "Removed BSSID " MACSTR " from " "blacklist (clear)", MAC2STR(prev->bssid)); os_free(prev); } + + wpa_s->extra_blacklist_count += max_count; } diff -Nru wpa-1.0/wpa_supplicant/blacklist.h wpa-2.1/wpa_supplicant/blacklist.h --- wpa-1.0/wpa_supplicant/blacklist.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/blacklist.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * wpa_supplicant - Temporary BSSID blacklist * Copyright (c) 2003-2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef BLACKLIST_H diff -Nru wpa-1.0/wpa_supplicant/bss.c wpa-2.1/wpa_supplicant/bss.c --- wpa-1.0/wpa_supplicant/bss.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/bss.c 2014-02-04 11:23:35.000000000 +0000 @@ -1,15 +1,9 @@ /* * BSS table - * Copyright (c) 2009-2010, Jouni Malinen + * Copyright (c) 2009-2012, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "utils/includes.h" @@ -41,32 +35,185 @@ #define WPA_BSS_IES_CHANGED_FLAG BIT(8) -static void wpa_bss_remove(struct wpa_supplicant *wpa_s, struct wpa_bss *bss) +static void wpa_bss_set_hessid(struct wpa_bss *bss) +{ +#ifdef CONFIG_INTERWORKING + const u8 *ie = wpa_bss_get_ie(bss, WLAN_EID_INTERWORKING); + if (ie == NULL || (ie[1] != 7 && ie[1] != 9)) { + os_memset(bss->hessid, 0, ETH_ALEN); + return; + } + if (ie[1] == 7) + os_memcpy(bss->hessid, ie + 3, ETH_ALEN); + else + os_memcpy(bss->hessid, ie + 5, ETH_ALEN); +#endif /* CONFIG_INTERWORKING */ +} + + +/** + * wpa_bss_anqp_alloc - Allocate ANQP data structure for a BSS entry + * Returns: Allocated ANQP data structure or %NULL on failure + * + * The allocated ANQP data structure has its users count set to 1. It may be + * shared by multiple BSS entries and each shared entry is freed with + * wpa_bss_anqp_free(). + */ +struct wpa_bss_anqp * wpa_bss_anqp_alloc(void) +{ + struct wpa_bss_anqp *anqp; + anqp = os_zalloc(sizeof(*anqp)); + if (anqp == NULL) + return NULL; + anqp->users = 1; + return anqp; +} + + +/** + * wpa_bss_anqp_clone - Clone an ANQP data structure + * @anqp: ANQP data structure from wpa_bss_anqp_alloc() + * Returns: Cloned ANQP data structure or %NULL on failure + */ +static struct wpa_bss_anqp * wpa_bss_anqp_clone(struct wpa_bss_anqp *anqp) +{ + struct wpa_bss_anqp *n; + + n = os_zalloc(sizeof(*n)); + if (n == NULL) + return NULL; + +#define ANQP_DUP(f) if (anqp->f) n->f = wpabuf_dup(anqp->f) +#ifdef CONFIG_INTERWORKING + ANQP_DUP(venue_name); + ANQP_DUP(network_auth_type); + ANQP_DUP(roaming_consortium); + ANQP_DUP(ip_addr_type_availability); + ANQP_DUP(nai_realm); + ANQP_DUP(anqp_3gpp); + ANQP_DUP(domain_name); +#endif /* CONFIG_INTERWORKING */ +#ifdef CONFIG_HS20 + ANQP_DUP(hs20_operator_friendly_name); + ANQP_DUP(hs20_wan_metrics); + ANQP_DUP(hs20_connection_capability); + ANQP_DUP(hs20_operating_class); +#endif /* CONFIG_HS20 */ +#undef ANQP_DUP + + return n; +} + + +/** + * wpa_bss_anqp_unshare_alloc - Unshare ANQP data (if shared) in a BSS entry + * @bss: BSS entry + * Returns: 0 on success, -1 on failure + * + * This function ensures the specific BSS entry has an ANQP data structure that + * is not shared with any other BSS entry. + */ +int wpa_bss_anqp_unshare_alloc(struct wpa_bss *bss) +{ + struct wpa_bss_anqp *anqp; + + if (bss->anqp && bss->anqp->users > 1) { + /* allocated, but shared - clone an unshared copy */ + anqp = wpa_bss_anqp_clone(bss->anqp); + if (anqp == NULL) + return -1; + anqp->users = 1; + bss->anqp->users--; + bss->anqp = anqp; + return 0; + } + + if (bss->anqp) + return 0; /* already allocated and not shared */ + + /* not allocated - allocate a new storage area */ + bss->anqp = wpa_bss_anqp_alloc(); + return bss->anqp ? 0 : -1; +} + + +/** + * wpa_bss_anqp_free - Free an ANQP data structure + * @anqp: ANQP data structure from wpa_bss_anqp_alloc() or wpa_bss_anqp_clone() + */ +static void wpa_bss_anqp_free(struct wpa_bss_anqp *anqp) { + if (anqp == NULL) + return; + + anqp->users--; + if (anqp->users > 0) { + /* Another BSS entry holds a pointer to this ANQP info */ + return; + } + +#ifdef CONFIG_INTERWORKING + wpabuf_free(anqp->venue_name); + wpabuf_free(anqp->network_auth_type); + wpabuf_free(anqp->roaming_consortium); + wpabuf_free(anqp->ip_addr_type_availability); + wpabuf_free(anqp->nai_realm); + wpabuf_free(anqp->anqp_3gpp); + wpabuf_free(anqp->domain_name); +#endif /* CONFIG_INTERWORKING */ +#ifdef CONFIG_HS20 + wpabuf_free(anqp->hs20_operator_friendly_name); + wpabuf_free(anqp->hs20_wan_metrics); + wpabuf_free(anqp->hs20_connection_capability); + wpabuf_free(anqp->hs20_operating_class); +#endif /* CONFIG_HS20 */ + + os_free(anqp); +} + + +static void wpa_bss_remove(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, + const char *reason) +{ + if (wpa_s->last_scan_res) { + unsigned int i; + for (i = 0; i < wpa_s->last_scan_res_used; i++) { + if (wpa_s->last_scan_res[i] == bss) { + os_memmove(&wpa_s->last_scan_res[i], + &wpa_s->last_scan_res[i + 1], + (wpa_s->last_scan_res_used - i - 1) + * sizeof(struct wpa_bss *)); + wpa_s->last_scan_res_used--; + break; + } + } + } dl_list_del(&bss->list); dl_list_del(&bss->list_id); wpa_s->num_bss--; wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Remove id %u BSSID " MACSTR - " SSID '%s'", bss->id, MAC2STR(bss->bssid), - wpa_ssid_txt(bss->ssid, bss->ssid_len)); + " SSID '%s' due to %s", bss->id, MAC2STR(bss->bssid), + wpa_ssid_txt(bss->ssid, bss->ssid_len), reason); wpas_notify_bss_removed(wpa_s, bss->bssid, bss->id); -#ifdef CONFIG_INTERWORKING - wpabuf_free(bss->anqp_venue_name); - wpabuf_free(bss->anqp_network_auth_type); - wpabuf_free(bss->anqp_roaming_consortium); - wpabuf_free(bss->anqp_ip_addr_type_availability); - wpabuf_free(bss->anqp_nai_realm); - wpabuf_free(bss->anqp_3gpp); - wpabuf_free(bss->anqp_domain_name); -#endif /* CONFIG_INTERWORKING */ + wpa_bss_anqp_free(bss->anqp); os_free(bss); } +/** + * wpa_bss_get - Fetch a BSS table entry based on BSSID and SSID + * @wpa_s: Pointer to wpa_supplicant data + * @bssid: BSSID + * @ssid: SSID + * @ssid_len: Length of @ssid + * Returns: Pointer to the BSS entry or %NULL if not found + */ struct wpa_bss * wpa_bss_get(struct wpa_supplicant *wpa_s, const u8 *bssid, const u8 *ssid, size_t ssid_len) { struct wpa_bss *bss; + if (!wpa_supplicant_filter_bssid_match(wpa_s, bssid)) + return NULL; dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) { if (os_memcmp(bss->bssid, bssid, ETH_ALEN) == 0 && bss->ssid_len == ssid_len && @@ -77,10 +224,27 @@ } -static void wpa_bss_copy_res(struct wpa_bss *dst, struct wpa_scan_res *src) +static void calculate_update_time(const struct os_reltime *fetch_time, + unsigned int age_ms, + struct os_reltime *update_time) { os_time_t usec; + update_time->sec = fetch_time->sec; + update_time->usec = fetch_time->usec; + update_time->sec -= age_ms / 1000; + usec = (age_ms % 1000) * 1000; + if (update_time->usec < usec) { + update_time->sec--; + update_time->usec += 1000000; + } + update_time->usec -= usec; +} + + +static void wpa_bss_copy_res(struct wpa_bss *dst, struct wpa_scan_res *src, + struct os_reltime *fetch_time) +{ dst->flags = src->flags; os_memcpy(dst->bssid, src->bssid, ETH_ALEN); dst->freq = src->freq; @@ -91,14 +255,7 @@ dst->level = src->level; dst->tsf = src->tsf; - os_get_time(&dst->last_update); - dst->last_update.sec -= src->age / 1000; - usec = (src->age % 1000) * 1000; - if (dst->last_update.usec < usec) { - dst->last_update.sec--; - dst->last_update.usec += 1000000; - } - dst->last_update.usec -= usec; + calculate_update_time(fetch_time, src->age, &dst->last_update); } @@ -118,13 +275,21 @@ } +static int wpa_bss_in_use(struct wpa_supplicant *wpa_s, struct wpa_bss *bss) +{ + return bss == wpa_s->current_bss || + os_memcmp(bss->bssid, wpa_s->bssid, ETH_ALEN) == 0 || + os_memcmp(bss->bssid, wpa_s->pending_bssid, ETH_ALEN) == 0; +} + + static int wpa_bss_remove_oldest_unknown(struct wpa_supplicant *wpa_s) { struct wpa_bss *bss; dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) { if (!wpa_bss_known(wpa_s, bss)) { - wpa_bss_remove(wpa_s, bss); + wpa_bss_remove(wpa_s, bss, __func__); return 0; } } @@ -133,41 +298,58 @@ } -static void wpa_bss_remove_oldest(struct wpa_supplicant *wpa_s) +static int wpa_bss_remove_oldest(struct wpa_supplicant *wpa_s) { + struct wpa_bss *bss; + /* * Remove the oldest entry that does not match with any configured * network. */ if (wpa_bss_remove_oldest_unknown(wpa_s) == 0) - return; + return 0; /* - * Remove the oldest entry since no better candidate for removal was - * found. + * Remove the oldest entry that isn't currently in use. */ - wpa_bss_remove(wpa_s, dl_list_first(&wpa_s->bss, - struct wpa_bss, list)); + dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) { + if (!wpa_bss_in_use(wpa_s, bss)) { + wpa_bss_remove(wpa_s, bss, __func__); + return 0; + } + } + + return -1; } -static void wpa_bss_add(struct wpa_supplicant *wpa_s, - const u8 *ssid, size_t ssid_len, - struct wpa_scan_res *res) +static struct wpa_bss * wpa_bss_add(struct wpa_supplicant *wpa_s, + const u8 *ssid, size_t ssid_len, + struct wpa_scan_res *res, + struct os_reltime *fetch_time) { struct wpa_bss *bss; bss = os_zalloc(sizeof(*bss) + res->ie_len + res->beacon_ie_len); if (bss == NULL) - return; + return NULL; bss->id = wpa_s->bss_next_id++; bss->last_update_idx = wpa_s->bss_update_idx; - wpa_bss_copy_res(bss, res); + wpa_bss_copy_res(bss, res, fetch_time); os_memcpy(bss->ssid, ssid, ssid_len); bss->ssid_len = ssid_len; bss->ie_len = res->ie_len; bss->beacon_ie_len = res->beacon_ie_len; os_memcpy(bss + 1, res + 1, res->ie_len + res->beacon_ie_len); + wpa_bss_set_hessid(bss); + + if (wpa_s->num_bss + 1 > wpa_s->conf->bss_max_count && + wpa_bss_remove_oldest(wpa_s) != 0) { + wpa_printf(MSG_ERROR, "Increasing the MAX BSS count to %d " + "because all BSSes are in use. We should normally " + "not get here!", (int) wpa_s->num_bss + 1); + wpa_s->conf->bss_max_count = wpa_s->num_bss + 1; + } dl_list_add_tail(&wpa_s->bss, &bss->list); dl_list_add_tail(&wpa_s->bss_id, &bss->list_id); @@ -176,8 +358,7 @@ " SSID '%s'", bss->id, MAC2STR(bss->bssid), wpa_ssid_txt(ssid, ssid_len)); wpas_notify_bss_added(wpa_s, bss->bssid, bss->id); - if (wpa_s->num_bss > wpa_s->conf->bss_max_count) - wpa_bss_remove_oldest(wpa_s); + return bss; } @@ -309,17 +490,34 @@ } -static void wpa_bss_update(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, - struct wpa_scan_res *res) +static struct wpa_bss * +wpa_bss_update(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, + struct wpa_scan_res *res, struct os_reltime *fetch_time) { u32 changes; changes = wpa_bss_compare_res(bss, res); bss->scan_miss_count = 0; bss->last_update_idx = wpa_s->bss_update_idx; - wpa_bss_copy_res(bss, res); + wpa_bss_copy_res(bss, res, fetch_time); /* Move the entry to the end of the list */ dl_list_del(&bss->list); +#ifdef CONFIG_P2P + if (wpa_bss_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE) && + !wpa_scan_get_vendor_ie(res, P2P_IE_VENDOR_TYPE)) { + /* + * This can happen when non-P2P station interface runs a scan + * without P2P IE in the Probe Request frame. P2P GO would reply + * to that with a Probe Response that does not include P2P IE. + * Do not update the IEs in this BSS entry to avoid such loss of + * information that may be needed for P2P operations to + * determine group information. + */ + wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Do not update scan IEs for " + MACSTR " since that would remove P2P IE information", + MAC2STR(bss->bssid)); + } else +#endif /* CONFIG_P2P */ if (bss->ie_len + bss->beacon_ie_len >= res->ie_len + res->beacon_ie_len) { os_memcpy(bss + 1, res + 1, res->ie_len + res->beacon_ie_len); @@ -332,6 +530,13 @@ nbss = os_realloc(bss, sizeof(*bss) + res->ie_len + res->beacon_ie_len); if (nbss) { + unsigned int i; + for (i = 0; i < wpa_s->last_scan_res_used; i++) { + if (wpa_s->last_scan_res[i] == bss) { + wpa_s->last_scan_res[i] = nbss; + break; + } + } if (wpa_s->current_bss == bss) wpa_s->current_bss = nbss; bss = nbss; @@ -342,34 +547,67 @@ } dl_list_add(prev, &bss->list_id); } + if (changes & WPA_BSS_IES_CHANGED_FLAG) + wpa_bss_set_hessid(bss); dl_list_add_tail(&wpa_s->bss, &bss->list); notify_bss_changes(wpa_s, changes, bss); -} - -static int wpa_bss_in_use(struct wpa_supplicant *wpa_s, struct wpa_bss *bss) -{ - return bss == wpa_s->current_bss || - os_memcmp(bss->bssid, wpa_s->bssid, ETH_ALEN) == 0 || - os_memcmp(bss->bssid, wpa_s->pending_bssid, ETH_ALEN) == 0; + return bss; } +/** + * wpa_bss_update_start - Start a BSS table update from scan results + * @wpa_s: Pointer to wpa_supplicant data + * + * This function is called at the start of each BSS table update round for new + * scan results. The actual scan result entries are indicated with calls to + * wpa_bss_update_scan_res() and the update round is finished with a call to + * wpa_bss_update_end(). + */ void wpa_bss_update_start(struct wpa_supplicant *wpa_s) { wpa_s->bss_update_idx++; wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Start scan result update %u", wpa_s->bss_update_idx); + wpa_s->last_scan_res_used = 0; } +/** + * wpa_bss_update_scan_res - Update a BSS table entry based on a scan result + * @wpa_s: Pointer to wpa_supplicant data + * @res: Scan result + * @fetch_time: Time when the result was fetched from the driver + * + * This function updates a BSS table entry (or adds one) based on a scan result. + * This is called separately for each scan result between the calls to + * wpa_bss_update_start() and wpa_bss_update_end(). + */ void wpa_bss_update_scan_res(struct wpa_supplicant *wpa_s, - struct wpa_scan_res *res) + struct wpa_scan_res *res, + struct os_reltime *fetch_time) { const u8 *ssid, *p2p; struct wpa_bss *bss; + if (wpa_s->conf->ignore_old_scan_res) { + struct os_reltime update; + calculate_update_time(fetch_time, res->age, &update); + if (os_reltime_before(&update, &wpa_s->scan_trigger_time)) { + struct os_reltime age; + os_reltime_sub(&wpa_s->scan_trigger_time, &update, + &age); + wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Ignore driver BSS " + "table entry that is %u.%06u seconds older " + "than our scan trigger", + (unsigned int) age.sec, + (unsigned int) age.usec); + return; + } + } + ssid = wpa_scan_get_ie(res, WLAN_EID_SSID); if (ssid == NULL) { wpa_dbg(wpa_s, MSG_DEBUG, "BSS: No SSID IE included for " @@ -383,6 +621,18 @@ } p2p = wpa_scan_get_vendor_ie(res, P2P_IE_VENDOR_TYPE); +#ifdef CONFIG_P2P + if (p2p == NULL && + wpa_s->p2p_group_interface != NOT_P2P_GROUP_INTERFACE) { + /* + * If it's a P2P specific interface, then don't update + * the scan result without a P2P IE. + */ + wpa_printf(MSG_DEBUG, "BSS: No P2P IE - skipping BSS " MACSTR + " update for P2P interface", MAC2STR(res->bssid)); + return; + } +#endif /* CONFIG_P2P */ if (p2p && ssid[1] == P2P_WILDCARD_SSID_LEN && os_memcmp(ssid + 2, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN) == 0) return; /* Skip P2P listen discovery results here */ @@ -391,9 +641,38 @@ * (to save memory) */ bss = wpa_bss_get(wpa_s, res->bssid, ssid + 2, ssid[1]); if (bss == NULL) - wpa_bss_add(wpa_s, ssid + 2, ssid[1], res); - else - wpa_bss_update(wpa_s, bss, res); + bss = wpa_bss_add(wpa_s, ssid + 2, ssid[1], res, fetch_time); + else { + bss = wpa_bss_update(wpa_s, bss, res, fetch_time); + if (wpa_s->last_scan_res) { + unsigned int i; + for (i = 0; i < wpa_s->last_scan_res_used; i++) { + if (bss == wpa_s->last_scan_res[i]) { + /* Already in the list */ + return; + } + } + } + } + + if (bss == NULL) + return; + if (wpa_s->last_scan_res_used >= wpa_s->last_scan_res_size) { + struct wpa_bss **n; + unsigned int siz; + if (wpa_s->last_scan_res_size == 0) + siz = 32; + else + siz = wpa_s->last_scan_res_size * 2; + n = os_realloc_array(wpa_s->last_scan_res, siz, + sizeof(struct wpa_bss *)); + if (n == NULL) + return; + wpa_s->last_scan_res = n; + wpa_s->last_scan_res_size = siz; + } + + wpa_s->last_scan_res[wpa_s->last_scan_res_used++] = bss; } @@ -438,11 +717,22 @@ } +/** + * wpa_bss_update_end - End a BSS table update from scan results + * @wpa_s: Pointer to wpa_supplicant data + * @info: Information about scan parameters + * @new_scan: Whether this update round was based on a new scan + * + * This function is called at the end of each BSS table update round for new + * scan results. The start of the update was indicated with a call to + * wpa_bss_update_start(). + */ void wpa_bss_update_end(struct wpa_supplicant *wpa_s, struct scan_info *info, int new_scan) { struct wpa_bss *bss, *n; + os_get_reltime(&wpa_s->last_scan); if (!new_scan) return; /* do not expire entries without new scan */ @@ -455,33 +745,39 @@ bss->scan_miss_count++; if (bss->scan_miss_count >= wpa_s->conf->bss_expiration_scan_count) { - wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Expire BSS %u due to " - "no match in scan", bss->id); - wpa_bss_remove(wpa_s, bss); + wpa_bss_remove(wpa_s, bss, "no match in scan"); } } + + wpa_printf(MSG_DEBUG, "BSS: last_scan_res_used=%u/%u", + wpa_s->last_scan_res_used, wpa_s->last_scan_res_size); } +/** + * wpa_bss_flush_by_age - Flush old BSS entries + * @wpa_s: Pointer to wpa_supplicant data + * @age: Maximum entry age in seconds + * + * Remove BSS entries that have not been updated during the last @age seconds. + */ void wpa_bss_flush_by_age(struct wpa_supplicant *wpa_s, int age) { struct wpa_bss *bss, *n; - struct os_time t; + struct os_reltime t; if (dl_list_empty(&wpa_s->bss)) return; - os_get_time(&t); + os_get_reltime(&t); t.sec -= age; dl_list_for_each_safe(bss, n, &wpa_s->bss, struct wpa_bss, list) { if (wpa_bss_in_use(wpa_s, bss)) continue; - if (os_time_before(&bss->last_update, &t)) { - wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Expire BSS %u due to " - "age", bss->id); - wpa_bss_remove(wpa_s, bss); + if (os_reltime_before(&bss->last_update, &t)) { + wpa_bss_remove(wpa_s, bss, __func__); } else break; } @@ -498,6 +794,14 @@ } +/** + * wpa_bss_init - Initialize BSS table + * @wpa_s: Pointer to wpa_supplicant data + * Returns: 0 on success, -1 on failure + * + * This prepares BSS table lists and timer for periodic updates. The BSS table + * is deinitialized with wpa_bss_deinit() once not needed anymore. + */ int wpa_bss_init(struct wpa_supplicant *wpa_s) { dl_list_init(&wpa_s->bss); @@ -508,21 +812,31 @@ } +/** + * wpa_bss_flush - Flush all unused BSS entries + * @wpa_s: Pointer to wpa_supplicant data + */ void wpa_bss_flush(struct wpa_supplicant *wpa_s) { struct wpa_bss *bss, *n; + wpa_s->clear_driver_scan_cache = 1; + if (wpa_s->bss.next == NULL) return; /* BSS table not yet initialized */ dl_list_for_each_safe(bss, n, &wpa_s->bss, struct wpa_bss, list) { if (wpa_bss_in_use(wpa_s, bss)) continue; - wpa_bss_remove(wpa_s, bss); + wpa_bss_remove(wpa_s, bss, __func__); } } +/** + * wpa_bss_deinit - Deinitialize BSS table + * @wpa_s: Pointer to wpa_supplicant data + */ void wpa_bss_deinit(struct wpa_supplicant *wpa_s) { eloop_cancel_timeout(wpa_bss_timeout, wpa_s, NULL); @@ -530,10 +844,18 @@ } +/** + * wpa_bss_get_bssid - Fetch a BSS table entry based on BSSID + * @wpa_s: Pointer to wpa_supplicant data + * @bssid: BSSID + * Returns: Pointer to the BSS entry or %NULL if not found + */ struct wpa_bss * wpa_bss_get_bssid(struct wpa_supplicant *wpa_s, const u8 *bssid) { struct wpa_bss *bss; + if (!wpa_supplicant_filter_bssid_match(wpa_s, bssid)) + return NULL; dl_list_for_each_reverse(bss, &wpa_s->bss, struct wpa_bss, list) { if (os_memcmp(bss->bssid, bssid, ETH_ALEN) == 0) return bss; @@ -542,7 +864,41 @@ } +/** + * wpa_bss_get_bssid_latest - Fetch the latest BSS table entry based on BSSID + * @wpa_s: Pointer to wpa_supplicant data + * @bssid: BSSID + * Returns: Pointer to the BSS entry or %NULL if not found + * + * This function is like wpa_bss_get_bssid(), but full BSS table is iterated to + * find the entry that has the most recent update. This can help in finding the + * correct entry in cases where the SSID of the AP may have changed recently + * (e.g., in WPS reconfiguration cases). + */ +struct wpa_bss * wpa_bss_get_bssid_latest(struct wpa_supplicant *wpa_s, + const u8 *bssid) +{ + struct wpa_bss *bss, *found = NULL; + if (!wpa_supplicant_filter_bssid_match(wpa_s, bssid)) + return NULL; + dl_list_for_each_reverse(bss, &wpa_s->bss, struct wpa_bss, list) { + if (os_memcmp(bss->bssid, bssid, ETH_ALEN) != 0) + continue; + if (found == NULL || + os_reltime_before(&found->last_update, &bss->last_update)) + found = bss; + } + return found; +} + + #ifdef CONFIG_P2P +/** + * wpa_bss_get_p2p_dev_addr - Fetch a BSS table entry based on P2P Device Addr + * @wpa_s: Pointer to wpa_supplicant data + * @dev_addr: P2P Device Address of the GO + * Returns: Pointer to the BSS entry or %NULL if not found + */ struct wpa_bss * wpa_bss_get_p2p_dev_addr(struct wpa_supplicant *wpa_s, const u8 *dev_addr) { @@ -559,6 +915,12 @@ #endif /* CONFIG_P2P */ +/** + * wpa_bss_get_id - Fetch a BSS table entry based on identifier + * @wpa_s: Pointer to wpa_supplicant data + * @id: Unique identifier (struct wpa_bss::id) assigned for the entry + * Returns: Pointer to the BSS entry or %NULL if not found + */ struct wpa_bss * wpa_bss_get_id(struct wpa_supplicant *wpa_s, unsigned int id) { struct wpa_bss *bss; @@ -570,6 +932,38 @@ } +/** + * wpa_bss_get_id_range - Fetch a BSS table entry based on identifier range + * @wpa_s: Pointer to wpa_supplicant data + * @idf: Smallest allowed identifier assigned for the entry + * @idf: Largest allowed identifier assigned for the entry + * Returns: Pointer to the BSS entry or %NULL if not found + * + * This function is similar to wpa_bss_get_id() but allows a BSS entry with the + * smallest id value to be fetched within the specified range without the + * caller having to know the exact id. + */ +struct wpa_bss * wpa_bss_get_id_range(struct wpa_supplicant *wpa_s, + unsigned int idf, unsigned int idl) +{ + struct wpa_bss *bss; + dl_list_for_each(bss, &wpa_s->bss_id, struct wpa_bss, list_id) { + if (bss->id >= idf && bss->id <= idl) + return bss; + } + return NULL; +} + + +/** + * wpa_bss_get_ie - Fetch a specified information element from a BSS entry + * @bss: BSS table entry + * @ie: Information element identitifier (WLAN_EID_*) + * Returns: Pointer to the information element (id field) or %NULL if not found + * + * This function returns the first matching information element in the BSS + * entry. + */ const u8 * wpa_bss_get_ie(const struct wpa_bss *bss, u8 ie) { const u8 *end, *pos; @@ -589,6 +983,15 @@ } +/** + * wpa_bss_get_vendor_ie - Fetch a vendor information element from a BSS entry + * @bss: BSS table entry + * @vendor_type: Vendor type (four octets starting the IE payload) + * Returns: Pointer to the information element (id field) or %NULL if not found + * + * This function returns the first matching information element in the BSS + * entry. + */ const u8 * wpa_bss_get_vendor_ie(const struct wpa_bss *bss, u32 vendor_type) { const u8 *end, *pos; @@ -609,6 +1012,53 @@ } +/** + * wpa_bss_get_vendor_ie_beacon - Fetch a vendor information from a BSS entry + * @bss: BSS table entry + * @vendor_type: Vendor type (four octets starting the IE payload) + * Returns: Pointer to the information element (id field) or %NULL if not found + * + * This function returns the first matching information element in the BSS + * entry. + * + * This function is like wpa_bss_get_vendor_ie(), but uses IE buffer only + * from Beacon frames instead of either Beacon or Probe Response frames. + */ +const u8 * wpa_bss_get_vendor_ie_beacon(const struct wpa_bss *bss, + u32 vendor_type) +{ + const u8 *end, *pos; + + if (bss->beacon_ie_len == 0) + return NULL; + + pos = (const u8 *) (bss + 1); + pos += bss->ie_len; + end = pos + bss->beacon_ie_len; + + while (pos + 1 < end) { + if (pos + 2 + pos[1] > end) + break; + if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 && + vendor_type == WPA_GET_BE32(&pos[2])) + return pos; + pos += 2 + pos[1]; + } + + return NULL; +} + + +/** + * wpa_bss_get_vendor_ie_multi - Fetch vendor IE data from a BSS entry + * @bss: BSS table entry + * @vendor_type: Vendor type (four octets starting the IE payload) + * Returns: Pointer to the information element payload or %NULL if not found + * + * This function returns concatenated payload of possibly fragmented vendor + * specific information elements in the BSS entry. The caller is responsible for + * freeing the returned buffer. + */ struct wpabuf * wpa_bss_get_vendor_ie_multi(const struct wpa_bss *bss, u32 vendor_type) { @@ -640,6 +1090,56 @@ } +/** + * wpa_bss_get_vendor_ie_multi_beacon - Fetch vendor IE data from a BSS entry + * @bss: BSS table entry + * @vendor_type: Vendor type (four octets starting the IE payload) + * Returns: Pointer to the information element payload or %NULL if not found + * + * This function returns concatenated payload of possibly fragmented vendor + * specific information elements in the BSS entry. The caller is responsible for + * freeing the returned buffer. + * + * This function is like wpa_bss_get_vendor_ie_multi(), but uses IE buffer only + * from Beacon frames instead of either Beacon or Probe Response frames. + */ +struct wpabuf * wpa_bss_get_vendor_ie_multi_beacon(const struct wpa_bss *bss, + u32 vendor_type) +{ + struct wpabuf *buf; + const u8 *end, *pos; + + buf = wpabuf_alloc(bss->beacon_ie_len); + if (buf == NULL) + return NULL; + + pos = (const u8 *) (bss + 1); + pos += bss->ie_len; + end = pos + bss->beacon_ie_len; + + while (pos + 1 < end) { + if (pos + 2 + pos[1] > end) + break; + if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 && + vendor_type == WPA_GET_BE32(&pos[2])) + wpabuf_put_data(buf, pos + 2 + 4, pos[1] - 4); + pos += 2 + pos[1]; + } + + if (wpabuf_len(buf) == 0) { + wpabuf_free(buf); + buf = NULL; + } + + return buf; +} + + +/** + * wpa_bss_get_max_rate - Get maximum legacy TX rate supported in a BSS + * @bss: BSS table entry + * Returns: Maximum legacy rate in units of 500 kbps + */ int wpa_bss_get_max_rate(const struct wpa_bss *bss) { int rate = 0; @@ -662,6 +1162,15 @@ } +/** + * wpa_bss_get_bit_rates - Get legacy TX rates supported in a BSS + * @bss: BSS table entry + * @rates: Buffer for returning a pointer to the rates list (units of 500 kbps) + * Returns: number of legacy TX rates or -1 on failure + * + * The caller is responsible for freeing the returned buffer with os_free() in + * case of success. + */ int wpa_bss_get_bit_rates(const struct wpa_bss *bss, u8 **rates) { const u8 *ie, *ie2; diff -Nru wpa-1.0/wpa_supplicant/bss.h wpa-2.1/wpa_supplicant/bss.h --- wpa-1.0/wpa_supplicant/bss.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/bss.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * BSS table * Copyright (c) 2009-2010, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef BSS_H @@ -26,56 +20,76 @@ #define WPA_BSS_ANQP_FETCH_TRIED BIT(6) /** + * struct wpa_bss_anqp - ANQP data for a BSS entry (struct wpa_bss) + */ +struct wpa_bss_anqp { + /** Number of BSS entries referring to this ANQP data instance */ + unsigned int users; +#ifdef CONFIG_INTERWORKING + struct wpabuf *venue_name; + struct wpabuf *network_auth_type; + struct wpabuf *roaming_consortium; + struct wpabuf *ip_addr_type_availability; + struct wpabuf *nai_realm; + struct wpabuf *anqp_3gpp; + struct wpabuf *domain_name; +#endif /* CONFIG_INTERWORKING */ +#ifdef CONFIG_HS20 + struct wpabuf *hs20_operator_friendly_name; + struct wpabuf *hs20_wan_metrics; + struct wpabuf *hs20_connection_capability; + struct wpabuf *hs20_operating_class; +#endif /* CONFIG_HS20 */ +}; + +/** * struct wpa_bss - BSS table - * @list: List entry for struct wpa_supplicant::bss - * @list_id: List entry for struct wpa_supplicant::bss_id - * @id: Unique identifier for this BSS entry - * @scan_miss_count: Number of counts without seeing this BSS - * @flags: information flags about the BSS/IBSS (WPA_BSS_*) - * @last_update_idx: Index of the last scan update - * @bssid: BSSID - * @freq: frequency of the channel in MHz (e.g., 2412 = channel 1) - * @beacon_int: beacon interval in TUs (host byte order) - * @caps: capability information field in host byte order - * @qual: signal quality - * @noise: noise level - * @level: signal level - * @tsf: Timestamp of last Beacon/Probe Response frame - * @last_update: Time of the last update (i.e., Beacon or Probe Response RX) - * @ie_len: length of the following IE field in octets (from Probe Response) - * @beacon_ie_len: length of the following Beacon IE field in octets * * This structure is used to store information about neighboring BSSes in * generic format. It is mainly updated based on scan results from the driver. */ struct wpa_bss { + /** List entry for struct wpa_supplicant::bss */ struct dl_list list; + /** List entry for struct wpa_supplicant::bss_id */ struct dl_list list_id; + /** Unique identifier for this BSS entry */ unsigned int id; + /** Number of counts without seeing this BSS */ unsigned int scan_miss_count; + /** Index of the last scan update */ unsigned int last_update_idx; + /** Information flags about the BSS/IBSS (WPA_BSS_*) */ unsigned int flags; + /** BSSID */ u8 bssid[ETH_ALEN]; + /** HESSID */ + u8 hessid[ETH_ALEN]; + /** SSID */ u8 ssid[32]; + /** Length of SSID */ size_t ssid_len; + /** Frequency of the channel in MHz (e.g., 2412 = channel 1) */ int freq; + /** Beacon interval in TUs (host byte order) */ u16 beacon_int; + /** Capability information field in host byte order */ u16 caps; + /** Signal quality */ int qual; + /** Noise level */ int noise; + /** Signal level */ int level; + /** Timestamp of last Beacon/Probe Response frame */ u64 tsf; - struct os_time last_update; -#ifdef CONFIG_INTERWORKING - struct wpabuf *anqp_venue_name; - struct wpabuf *anqp_network_auth_type; - struct wpabuf *anqp_roaming_consortium; - struct wpabuf *anqp_ip_addr_type_availability; - struct wpabuf *anqp_nai_realm; - struct wpabuf *anqp_3gpp; - struct wpabuf *anqp_domain_name; -#endif /* CONFIG_INTERWORKING */ + /** Time of the last update (i.e., Beacon or Probe Response RX) */ + struct os_reltime last_update; + /** ANQP data */ + struct wpa_bss_anqp *anqp; + /** Length of the following IE field in octets (from Probe Response) */ size_t ie_len; + /** Length of the following Beacon IE field in octets */ size_t beacon_ie_len; /* followed by ie_len octets of IEs */ /* followed by beacon_ie_len octets of IEs */ @@ -83,7 +97,8 @@ void wpa_bss_update_start(struct wpa_supplicant *wpa_s); void wpa_bss_update_scan_res(struct wpa_supplicant *wpa_s, - struct wpa_scan_res *res); + struct wpa_scan_res *res, + struct os_reltime *fetch_time); void wpa_bss_update_end(struct wpa_supplicant *wpa_s, struct scan_info *info, int new_scan); int wpa_bss_init(struct wpa_supplicant *wpa_s); @@ -94,14 +109,24 @@ const u8 *ssid, size_t ssid_len); struct wpa_bss * wpa_bss_get_bssid(struct wpa_supplicant *wpa_s, const u8 *bssid); +struct wpa_bss * wpa_bss_get_bssid_latest(struct wpa_supplicant *wpa_s, + const u8 *bssid); struct wpa_bss * wpa_bss_get_p2p_dev_addr(struct wpa_supplicant *wpa_s, const u8 *dev_addr); struct wpa_bss * wpa_bss_get_id(struct wpa_supplicant *wpa_s, unsigned int id); +struct wpa_bss * wpa_bss_get_id_range(struct wpa_supplicant *wpa_s, + unsigned int idf, unsigned int idl); const u8 * wpa_bss_get_ie(const struct wpa_bss *bss, u8 ie); const u8 * wpa_bss_get_vendor_ie(const struct wpa_bss *bss, u32 vendor_type); +const u8 * wpa_bss_get_vendor_ie_beacon(const struct wpa_bss *bss, + u32 vendor_type); struct wpabuf * wpa_bss_get_vendor_ie_multi(const struct wpa_bss *bss, u32 vendor_type); +struct wpabuf * wpa_bss_get_vendor_ie_multi_beacon(const struct wpa_bss *bss, + u32 vendor_type); int wpa_bss_get_max_rate(const struct wpa_bss *bss); int wpa_bss_get_bit_rates(const struct wpa_bss *bss, u8 **rates); +struct wpa_bss_anqp * wpa_bss_anqp_alloc(void); +int wpa_bss_anqp_unshare_alloc(struct wpa_bss *bss); #endif /* BSS_H */ diff -Nru wpa-1.0/wpa_supplicant/ChangeLog wpa-2.1/wpa_supplicant/ChangeLog --- wpa-1.0/wpa_supplicant/ChangeLog 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/ChangeLog 2014-02-04 11:23:35.000000000 +0000 @@ -1,6 +1,345 @@ ChangeLog for wpa_supplicant -2012-04-18 - v1.0 +2014-02-04 - v2.1 + * added support for simultaneous authentication of equals (SAE) for + stronger password-based authentication with WPA2-Personal + * improved P2P negotiation and group formation robustness + - avoid unnecessary Dialog Token value changes during retries + - avoid more concurrent scanning cases during full group formation + sequence + - do not use potentially obsolete scan result data from driver + cache for peer discovery/updates + - avoid undesired re-starting of GO negotiation based on Probe + Request frames + - increase GO Negotiation and Invitation timeouts to address busy + environments and peers that take long time to react to messages, + e.g., due to power saving + - P2P Device interface type + * improved P2P channel selection (use more peer information and allow + more local options) + * added support for optional per-device PSK assignment by P2P GO + (wpa_cli p2p_set per_sta_psk <0/1>) + * added P2P_REMOVE_CLIENT for removing a client from P2P groups + (including persistent groups); this can be used to securely remove + a client from a group if per-device PSKs are used + * added more configuration flexibility for allowed P2P GO/client + channels (p2p_no_go_freq list and p2p_add_cli_chan=0/1) + * added nl80211 functionality + - VHT configuration for nl80211 + - MFP (IEEE 802.11w) information for nl80211 command API + - support split wiphy dump + - FT (IEEE 802.11r) with driver-based SME + - use advertised number of supported concurrent channels + - QoS Mapping configuration + * improved TDLS negotiation robustness + * added more TDLS peer parameters to be configured to the driver + * optimized connection time by allowing recently received scan results + to be used instead of having to run through a new scan + * fixed ctrl_iface BSS command iteration with RANGE argument and no + exact matches; also fixed argument parsing for some cases with + multiple arguments + * added 'SCAN TYPE=ONLY' ctrl_iface command to request manual scan + without executing roaming/network re-selection on scan results + * added Session-Id derivation for EAP peer methods + * added fully automated regression testing with mac80211_hwsim + * changed configuration parser to reject invalid integer values + * allow AP/Enrollee to be specified with BSSID instead of UUID for + WPS ER operations + * disable network block temporarily on repeated connection failures + * changed the default driver interface from wext to nl80211 if both are + included in the build + * remove duplicate networks if WPS provisioning is run multiple times + * remove duplicate networks when Interworking network selection uses the + same network + * added global freq_list configuration to allow scan frequencies to be + limited for all cases instead of just for a specific network block + * added support for BSS Transition Management + * added option to use "IFNAME= " prefix to use the global + control interface connection to perform per-interface commands; + similarly, allow global control interface to be used as a monitor + interface to receive events from all interfaces + * fixed OKC-based PMKSA cache entry clearing + * fixed TKIP group key configuration with FT + * added support for using OCSP stapling to validate server certificate + (ocsp=1 as optional and ocsp=2 as mandatory) + * added EAP-EKE peer + * added peer restart detection for IBSS RSN + * added domain_suffix_match (and domain_suffix_match2 for Phase 2 + EAP-TLS) to specify additional constraint for the server certificate + domain name + * added support for external SIM/USIM processing in EAP-SIM, EAP-AKA, + and EAP-AKA' (CTRL-REQ-SIM and CTRL-RSP-SIM commands over control + interface) + * added global bgscan configuration option as a default for all network + blocks that do not specify their own bgscan parameters + * added D-Bus methods for TDLS + * added more control to scan requests + - "SCAN freq=" can be used to specify which channels are + scanned (comma-separated frequency ranges in MHz) + - "SCAN passive=1" can be used to request a passive scan (no Probe + Request frames are sent) + - "SCAN use_id" can be used to request a scan id to be returned and + included in event messages related to this specific scan operation + - "SCAN only_new=1" can be used to request the driver/cfg80211 to + report only BSS entries that have been updated during this scan + round + - these optional arguments to the SCAN command can be combined with + each other + * modified behavior on externally triggered scans + - avoid concurrent operations requiring full control of the radio when + an externally triggered scan is detected + - do not use results for internal roaming decision + * added a new cred block parameter 'temporary' to allow credential + blocks to be stored separately even if wpa_supplicant configuration + file is used to maintain other network information + * added "radio work" framework to schedule exclusive radio operations + for off-channel functionality + - reduce issues with concurrent operations that try to control which + channel is used + - allow external programs to request exclusive radio control in a way + that avoids conflicts with wpa_supplicant + * added support for using Protected Dual of Public Action frames for + GAS/ANQP exchanges when associated with PMF + * added support for WPS+NFC updates and P2P+NFC + - improved protocol for WPS + - P2P group formation/join based on NFC connection handover + - new IPv4 address assignment for P2P groups (ip_addr_* configuration + parameters on the GO) to replace DHCP + - option to fetch and report alternative carrier records for external + NFC operations + * various bug fixes + +2013-01-12 - v2.0 + * removed Qt3-based wpa_gui (obsoleted by wpa_qui-qt4) + * removed unmaintained driver wrappers broadcom, iphone, osx, ralink, + hostap, madwifi (hostap and madwifi remain available for hostapd; + their wpa_supplicant functionality is obsoleted by wext) + * improved debug logging (human readable event names, interface name + included in more entries) + * changed AP mode behavior to enable WPS only for open and + WPA/WPA2-Personal configuration + * improved P2P concurrency operations + - better coordination of concurrent scan and P2P search operations + - avoid concurrent remain-on-channel operation requests by canceling + previous operations prior to starting a new one + - reject operations that would require multi-channel concurrency if + the driver does not support it + - add parameter to select whether STA or P2P connection is preferred + if the driver cannot support both at the same time + - allow driver to indicate channel changes + - added optional delay= parameter for + p2p_find to avoid taking all radio resources + - use 500 ms p2p_find search delay by default during concurrent + operations + - allow all channels in GO Negotiation if the driver supports + multi-channel concurrency + * added number of small changes to make it easier for static analyzers + to understand the implementation + * fixed number of small bugs (see git logs for more details) + * nl80211: number of updates to use new cfg80211/nl80211 functionality + - replace monitor interface with nl80211 commands for AP mode + - additional information for driver-based AP SME + - STA entry authorization in RSN IBSS + * EAP-pwd: + - fixed KDF for group 21 and zero-padding + - added support for fragmentation + - increased maximum number of hunting-and-pecking iterations + * avoid excessive Probe Response retries for broadcast Probe Request + frames (only with drivers using wpa_supplicant AP mode SME/MLME) + * added "GET country" ctrl_iface command + * do not save an invalid network block in wpa_supplicant.conf to avoid + problems reading the file on next start + * send STA connected/disconnected ctrl_iface events to both the P2P + group and parent interfaces + * added preliminary support for using TLS v1.2 (CONFIG_TLSV12=y) + * added "SET pno <1/0>" ctrl_iface command to start/stop preferred + network offload with sched_scan driver command + * merged in number of changes from Android repository for P2P, nl80211, + and build parameters + * changed P2P GO mode configuration to use driver capabilities to + automatically enable HT operations when supported + * added "wpa_cli status wps" command to fetch WPA2-Personal passhrase + for WPS use cases in AP mode + * EAP-AKA: keep pseudonym identity across EAP exchanges to match EAP-SIM + behavior + * improved reassociation behavior in cases where association is rejected + or when an AP disconnects us to handle common load balancing + mechanisms + - try to avoid extra scans when the needed information is available + * added optional "join" argument for p2p_prov_disc ctrl_iface command + * added group ifname to P2P-PROV-DISC-* events + * added P2P Device Address to AP-STA-DISCONNECTED event and use + p2p_dev_addr parameter name with AP-STA-CONNECTED + * added workarounds for WPS PBC overlap detection for some P2P use cases + where deployed stations work incorrectly + * optimize WPS connection speed by disconnecting prior to WPS scan and + by using single channel scans when AP channel is known + * PCSC and SIM/USIM improvements: + - accept 0x67 (Wrong length) as a response to READ RECORD to fix + issues with some USIM cards + - try to read MNC length from SIM/USIM + - build realm according to 3GPP TS 23.003 with identity from the SIM + - allow T1 protocol to be enabled + * added more WPS and P2P information available through D-Bus + * improve P2P negotiation robustness + - extra waits to get ACK frames through + - longer timeouts for cases where deployed devices have been + identified have issues meeting the specification requirements + - more retries for some P2P frames + - handle race conditions in GO Negotiation start by both devices + - ignore unexpected GO Negotiation Response frame + * added support for libnl 3.2 and newer + * added P2P persistent group info to P2P_PEER data + * maintain a list of P2P Clients for persistent group on GO + * AP: increased initial group key handshake retransmit timeout to 500 ms + * added optional dev_id parameter for p2p_find + * added P2P-FIND-STOPPED ctrl_iface event + * fixed issues in WPA/RSN element validation when roaming with ap_scan=1 + and driver-based BSS selection + * do not expire P2P peer entries while connected with the peer in a + group + * fixed WSC element inclusion in cases where P2P is disabled + * AP: added a WPS workaround for mixed mode AP Settings with Windows 7 + * EAP-SIM: fixed AT_COUNTER_TOO_SMALL use + * EAP-SIM/AKA: append realm to pseudonym identity + * EAP-SIM/AKA: store pseudonym identity in network configuration to + allow it to persist over multiple EAP sessions and wpa_supplicant + restarts + * EAP-AKA': updated to RFC 5448 (username prefixes changed); note: this + breaks interoperability with older versions + * added support for WFA Hotspot 2.0 + - GAS/ANQP to fetch network information + - credential configuration and automatic network selections based on + credential match with ANQP information + * limited PMKSA cache entries to be used only with the network context + that was used to create them + * improved PMKSA cache expiration to avoid unnecessary disconnections + * adjusted bgscan_simple fast-scan backoff to avoid too frequent + background scans + * removed ctrl_iface event on P2P PD Response in join-group case + * added option to fetch BSS table entry based on P2P Device Address + ("BSS p2p_dev_addr=") + * added BSS entry age to ctrl_iface BSS command output + * added optional MASK=0xH option for ctrl_iface BSS command to select + which fields are included in the response + * added optional RANGE=ALL|N1-N2 option for ctrl_iface BSS command to + fetch information about several BSSes in one call + * simplified licensing terms by selecting the BSD license as the only + alternative + * added "P2P_SET disallow_freq " ctrl_iface command to + disable channels from P2P use + * added p2p_pref_chan configuration parameter to allow preferred P2P + channels to be specified + * added support for advertising immediate availability of a WPS + credential for P2P use cases + * optimized scan operations for P2P use cases (use single channel scan + for a specific SSID when possible) + * EAP-TTLS: fixed peer challenge generation for MSCHAPv2 + * SME: do not use reassociation after explicit disconnection request + (local or a notification from an AP) + * added support for sending debug info to Linux tracing (-T on command + line) + * added support for using Deauthentication reason code 3 as an + indication of P2P group termination + * added wps_vendor_ext_m1 configuration parameter to allow vendor + specific attributes to be added to WPS M1 + * started using separate TLS library context for tunneled TLS + (EAP-PEAP/TLS, EAP-TTLS/TLS, EAP-FAST/TLS) to support different CA + certificate configuration between Phase 1 and Phase 2 + * added optional "auto" parameter for p2p_connect to request automatic + GO Negotiation vs. join-a-group selection + * added disabled_scan_offload parameter to disable automatic scan + offloading (sched_scan) + * added optional persistent= parameter for p2p_connect to + allow forcing of a specific SSID/passphrase for GO Negotiation + * added support for OBSS scan requests and 20/40 BSS coexistence reports + * reject PD Request for unknown group + * removed scripts and notes related to Windows binary releases (which + have not been used starting from 1.x) + * added initial support for WNM operations + - Keep-alive based on BSS max idle period + - WNM-Sleep Mode + - minimal BSS Transition Management processing + * added autoscan module to control scanning behavior while not connected + - autoscan_periodic and autoscan_exponential modules + * added new WPS NFC ctrl_iface mechanism + - added initial support NFC connection handover + - removed obsoleted WPS_OOB command (including support for deprecated + UFD config_method) + * added optional framework for external password storage ("ext:") + * wpa_cli: added optional support for controlling wpa_supplicant + remotely over UDP (CONFIG_CTRL_IFACE=udp-remote) for testing purposes + * wpa_cli: extended tab completion to more commands + * changed SSID output to use printf-escaped strings instead of masking + of non-ASCII characters + - SSID can now be configured in the same format: ssid=P"abc\x00test" + * removed default ACM=1 from AC_VO and AC_VI + * added optional "ht40" argument for P2P ctrl_iface commands to allow + 40 MHz channels to be requested on the 5 GHz band + * added optional parameters for p2p_invite command to specify channel + when reinvoking a persistent group as the GO + * improved FIPS mode builds with OpenSSL + - "make fips" with CONFIG_FIPS=y to build wpa_supplicant with the + OpenSSL FIPS object module + - replace low level OpenSSL AES API calls to use EVP + - use OpenSSL keying material exporter when possible + - do not export TLS keys in FIPS mode + - remove MD5 from CONFIG_FIPS=y builds + - use OpenSSL function for PKBDF2 passphrase-to-PSK + - use OpenSSL HMAC implementation + - mix RAND_bytes() output into random_get_bytes() to force OpenSSL + DRBG to be used in FIPS mode + - use OpenSSL CMAC implementation + * added mechanism to disable TLS Session Ticket extension + - a workaround for servers that do not support TLS extensions that + was enabled by default in recent OpenSSL versions + - tls_disable_session_ticket=1 + - automatically disable TLS Session Ticket extension by default when + using EAP-TLS/PEAP/TTLS (i.e., only use it with EAP-FAST) + * changed VENDOR-TEST EAP method to use proper private enterprise number + (this will not interoperate with older versions) + * disable network block temporarily on authentication failures + * improved WPS AP selection during WPS PIN iteration + * added support for configuring GCMP cipher for IEEE 802.11ad + * added support for Wi-Fi Display extensions + - WFD_SUBELEMENT_SET ctrl_iface command to configure WFD subelements + - SET wifi_display <0/1> to disable/enable WFD support + - WFD service discovery + - an external program is needed to manage the audio/video streaming + and codecs + * optimized scan result use for network selection + - use the internal BSS table instead of raw scan results + - allow unnecessary scans to be skipped if fresh information is + available (e.g., after GAS/ANQP round for Interworking) + * added support for 256-bit AES with internal TLS implementation + * allow peer to propose channel in P2P invitation process for a + persistent group + * added disallow_aps parameter to allow BSSIDs/SSIDs to be disallowed + from network selection + * re-enable the networks disabled during WPS operations + * allow P2P functionality to be disabled per interface (p2p_disabled=1) + * added secondary device types into P2P_PEER output + * added an option to disable use of a separate P2P group interface + (p2p_no_group_iface=1) + * fixed P2P Bonjour SD to match entries with both compressed and not + compressed domain name format and support multiple Bonjour PTR matches + for the same key + * use deauthentication instead of disassociation for all disconnection + operations; this removes the now unused disassociate() wpa_driver_ops + callback + * optimized PSK generation on P2P GO by caching results to avoid + multiple PBKDF2 operations + * added okc=1 global configuration parameter to allow OKC to be enabled + by default for all network blocks + * added a workaround for WPS PBC session overlap detection to avoid + interop issues with deployed station implementations that do not + remove active PBC indication from Probe Request frames properly + * added basic support for 60 GHz band + * extend EAPOL frames processing workaround for roaming cases + (postpone processing of unexpected EAPOL frame until association + event to handle reordered events) + +2012-05-10 - v1.0 * bsd: Add support for setting HT values in IFM_MMASK. * Delay STA entry removal until Deauth/Disassoc TX status in AP mode. This allows the driver to use PS buffering of Deauthentication and diff -Nru wpa-1.0/wpa_supplicant/config.c wpa-2.1/wpa_supplicant/config.c --- wpa-1.0/wpa_supplicant/config.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/config.c 2014-02-04 11:23:35.000000000 +0000 @@ -1,21 +1,16 @@ /* * WPA Supplicant / Configuration parser and common functions - * Copyright (c) 2003-2008, Jouni Malinen + * Copyright (c) 2003-2012, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * 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/uuid.h" +#include "utils/ip_addr.h" #include "crypto/sha1.h" #include "rsn_supp/wpa.h" #include "eap_peer/eap.h" @@ -59,42 +54,6 @@ }; -static char * wpa_config_parse_string(const char *value, size_t *len) -{ - if (*value == '"') { - const char *pos; - char *str; - value++; - pos = os_strrchr(value, '"'); - if (pos == NULL || pos[1] != '\0') - return NULL; - *len = pos - value; - str = os_malloc(*len + 1); - if (str == NULL) - return NULL; - os_memcpy(str, value, *len); - str[*len] = '\0'; - return str; - } else { - u8 *str; - size_t tlen, hlen = os_strlen(value); - if (hlen & 1) - return NULL; - tlen = hlen / 2; - str = os_malloc(tlen + 1); - if (str == NULL) - return NULL; - if (hexstr2bin(value, str, tlen)) { - os_free(str); - return NULL; - } - str[tlen] = '\0'; - *len = tlen; - return (char *) str; - } -} - - static int wpa_config_parse_str(const struct parse_data *data, struct wpa_ssid *ssid, int line, const char *value) @@ -155,18 +114,6 @@ #ifndef NO_CONFIG_WRITE -static int is_hex(const u8 *data, size_t len) -{ - size_t i; - - for (i = 0; i < len; i++) { - if (data[i] < 32 || data[i] >= 127) - return 1; - } - return 0; -} - - static char * wpa_config_write_string_ascii(const u8 *value, size_t len) { char *buf; @@ -232,10 +179,17 @@ struct wpa_ssid *ssid, int line, const char *value) { - int *dst; + int val, *dst; + char *end; dst = (int *) (((u8 *) ssid) + (long) data->param1); - *dst = atoi(value); + val = strtol(value, &end, 0); + if (*end) { + wpa_printf(MSG_ERROR, "Line %d: invalid number \"%s\"", + line, value); + return -1; + } + *dst = val; wpa_printf(MSG_MSGDUMP, "%s=%d (0x%x)", data->name, *dst, *dst); if (data->param3 && *dst < (long) data->param3) { @@ -330,6 +284,21 @@ struct wpa_ssid *ssid, int line, const char *value) { +#ifdef CONFIG_EXT_PASSWORD + if (os_strncmp(value, "ext:", 4) == 0) { + os_free(ssid->passphrase); + ssid->passphrase = NULL; + ssid->psk_set = 0; + os_free(ssid->ext_psk); + ssid->ext_psk = os_strdup(value + 4); + if (ssid->ext_psk == NULL) + return -1; + wpa_printf(MSG_DEBUG, "PSK: External password '%s'", + ssid->ext_psk); + return 0; + } +#endif /* CONFIG_EXT_PASSWORD */ + if (*value == '"') { #ifndef CONFIG_NO_PBKDF2 const char *pos; @@ -354,11 +323,9 @@ return 0; ssid->psk_set = 0; os_free(ssid->passphrase); - ssid->passphrase = os_malloc(len + 1); + ssid->passphrase = dup_binstr(value, len); if (ssid->passphrase == NULL) return -1; - os_memcpy(ssid->passphrase, value, len); - ssid->passphrase[len] = '\0'; return 0; #else /* CONFIG_NO_PBKDF2 */ wpa_printf(MSG_ERROR, "Line %d: ASCII passphrase not " @@ -387,6 +354,17 @@ static char * wpa_config_write_psk(const struct parse_data *data, struct wpa_ssid *ssid) { +#ifdef CONFIG_EXT_PASSWORD + if (ssid->ext_psk) { + size_t len = 4 + os_strlen(ssid->ext_psk) + 1; + char *buf = os_malloc(len); + if (buf == NULL) + return NULL; + os_snprintf(buf, len, "ext:%s", ssid->ext_psk); + return buf; + } +#endif /* CONFIG_EXT_PASSWORD */ + if (ssid->passphrase) return wpa_config_write_string_ascii( (const u8 *) ssid->passphrase, @@ -532,6 +510,12 @@ else if (os_strcmp(start, "WPS") == 0) val |= WPA_KEY_MGMT_WPS; #endif /* CONFIG_WPS */ +#ifdef CONFIG_SAE + else if (os_strcmp(start, "SAE") == 0) + val |= WPA_KEY_MGMT_SAE; + else if (os_strcmp(start, "FT-SAE") == 0) + val |= WPA_KEY_MGMT_FT_SAE; +#endif /* CONFIG_SAE */ else { wpa_printf(MSG_ERROR, "Line %d: invalid key_mgmt '%s'", line, start); @@ -563,10 +547,10 @@ char *buf, *pos, *end; int ret; - pos = buf = os_zalloc(50); + pos = buf = os_zalloc(100); if (buf == NULL) return NULL; - end = buf + 50; + end = buf + 100; if (ssid->key_mgmt & WPA_KEY_MGMT_PSK) { ret = os_snprintf(pos, end - pos, "%sWPA-PSK", @@ -619,101 +603,8 @@ } #ifdef CONFIG_IEEE80211R - if (ssid->key_mgmt & WPA_KEY_MGMT_FT_PSK) - pos += os_snprintf(pos, end - pos, "%sFT-PSK", - pos == buf ? "" : " "); - - if (ssid->key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X) - pos += os_snprintf(pos, end - pos, "%sFT-EAP", - pos == buf ? "" : " "); -#endif /* CONFIG_IEEE80211R */ - -#ifdef CONFIG_IEEE80211W - if (ssid->key_mgmt & WPA_KEY_MGMT_PSK_SHA256) - pos += os_snprintf(pos, end - pos, "%sWPA-PSK-SHA256", - pos == buf ? "" : " "); - - if (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256) - pos += os_snprintf(pos, end - pos, "%sWPA-EAP-SHA256", - pos == buf ? "" : " "); -#endif /* CONFIG_IEEE80211W */ - -#ifdef CONFIG_WPS - if (ssid->key_mgmt & WPA_KEY_MGMT_WPS) - pos += os_snprintf(pos, end - pos, "%sWPS", - pos == buf ? "" : " "); -#endif /* CONFIG_WPS */ - - return buf; -} -#endif /* NO_CONFIG_WRITE */ - - -static int wpa_config_parse_cipher(int line, const char *value) -{ - int val = 0, last; - char *start, *end, *buf; - - buf = os_strdup(value); - if (buf == NULL) - return -1; - start = buf; - - while (*start != '\0') { - while (*start == ' ' || *start == '\t') - start++; - if (*start == '\0') - break; - end = start; - while (*end != ' ' && *end != '\t' && *end != '\0') - end++; - last = *end == '\0'; - *end = '\0'; - if (os_strcmp(start, "CCMP") == 0) - val |= WPA_CIPHER_CCMP; - else if (os_strcmp(start, "TKIP") == 0) - val |= WPA_CIPHER_TKIP; - else if (os_strcmp(start, "WEP104") == 0) - val |= WPA_CIPHER_WEP104; - else if (os_strcmp(start, "WEP40") == 0) - val |= WPA_CIPHER_WEP40; - else if (os_strcmp(start, "NONE") == 0) - val |= WPA_CIPHER_NONE; - else { - wpa_printf(MSG_ERROR, "Line %d: invalid cipher '%s'.", - line, start); - os_free(buf); - return -1; - } - - if (last) - break; - start = end + 1; - } - os_free(buf); - - if (val == 0) { - wpa_printf(MSG_ERROR, "Line %d: no cipher values configured.", - line); - return -1; - } - return val; -} - - -#ifndef NO_CONFIG_WRITE -static char * wpa_config_write_cipher(int cipher) -{ - char *buf, *pos, *end; - int ret; - - pos = buf = os_zalloc(50); - if (buf == NULL) - return NULL; - end = buf + 50; - - if (cipher & WPA_CIPHER_CCMP) { - ret = os_snprintf(pos, end - pos, "%sCCMP", + if (ssid->key_mgmt & WPA_KEY_MGMT_FT_PSK) { + ret = os_snprintf(pos, end - pos, "%sFT-PSK", pos == buf ? "" : " "); if (ret < 0 || ret >= end - pos) { end[-1] = '\0'; @@ -722,8 +613,8 @@ pos += ret; } - if (cipher & WPA_CIPHER_TKIP) { - ret = os_snprintf(pos, end - pos, "%sTKIP", + if (ssid->key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X) { + ret = os_snprintf(pos, end - pos, "%sFT-EAP", pos == buf ? "" : " "); if (ret < 0 || ret >= end - pos) { end[-1] = '\0'; @@ -731,9 +622,11 @@ } pos += ret; } +#endif /* CONFIG_IEEE80211R */ - if (cipher & WPA_CIPHER_WEP104) { - ret = os_snprintf(pos, end - pos, "%sWEP104", +#ifdef CONFIG_IEEE80211W + if (ssid->key_mgmt & WPA_KEY_MGMT_PSK_SHA256) { + ret = os_snprintf(pos, end - pos, "%sWPA-PSK-SHA256", pos == buf ? "" : " "); if (ret < 0 || ret >= end - pos) { end[-1] = '\0'; @@ -742,8 +635,8 @@ pos += ret; } - if (cipher & WPA_CIPHER_WEP40) { - ret = os_snprintf(pos, end - pos, "%sWEP40", + if (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256) { + ret = os_snprintf(pos, end - pos, "%sWPA-EAP-SHA256", pos == buf ? "" : " "); if (ret < 0 || ret >= end - pos) { end[-1] = '\0'; @@ -751,9 +644,11 @@ } pos += ret; } +#endif /* CONFIG_IEEE80211W */ - if (cipher & WPA_CIPHER_NONE) { - ret = os_snprintf(pos, end - pos, "%sNONE", +#ifdef CONFIG_WPS + if (ssid->key_mgmt & WPA_KEY_MGMT_WPS) { + ret = os_snprintf(pos, end - pos, "%sWPS", pos == buf ? "" : " "); if (ret < 0 || ret >= end - pos) { end[-1] = '\0'; @@ -761,6 +656,41 @@ } pos += ret; } +#endif /* CONFIG_WPS */ + + return buf; +} +#endif /* NO_CONFIG_WRITE */ + + +static int wpa_config_parse_cipher(int line, const char *value) +{ + int val = wpa_parse_cipher(value); + if (val < 0) { + wpa_printf(MSG_ERROR, "Line %d: invalid cipher '%s'.", + line, value); + return -1; + } + if (val == 0) { + wpa_printf(MSG_ERROR, "Line %d: no cipher values configured.", + line); + return -1; + } + return val; +} + + +#ifndef NO_CONFIG_WRITE +static char * wpa_config_write_cipher(int cipher) +{ + char *buf = os_zalloc(50); + if (buf == NULL) + return NULL; + + if (wpa_write_ciphers(buf, buf + 50, cipher, " ") < 0) { + os_free(buf); + return NULL; + } return buf; } @@ -775,7 +705,7 @@ val = wpa_config_parse_cipher(line, value); if (val == -1) return -1; - if (val & ~(WPA_CIPHER_CCMP | WPA_CIPHER_TKIP | WPA_CIPHER_NONE)) { + if (val & ~WPA_ALLOWED_PAIRWISE_CIPHERS) { wpa_printf(MSG_ERROR, "Line %d: not allowed pairwise cipher " "(0x%x).", line, val); return -1; @@ -804,8 +734,7 @@ val = wpa_config_parse_cipher(line, value); if (val == -1) return -1; - if (val & ~(WPA_CIPHER_CCMP | WPA_CIPHER_TKIP | WPA_CIPHER_WEP104 | - WPA_CIPHER_WEP40)) { + if (val & ~WPA_ALLOWED_GROUP_CIPHERS) { wpa_printf(MSG_ERROR, "Line %d: not allowed group cipher " "(0x%x).", line, val); return -1; @@ -925,9 +854,7 @@ #endif /* NO_CONFIG_WRITE */ -static int * wpa_config_parse_freqs(const struct parse_data *data, - struct wpa_ssid *ssid, int line, - const char *value) +static int * wpa_config_parse_int_array(const char *value) { int *freqs; size_t used, len; @@ -935,7 +862,7 @@ used = 0; len = 10; - freqs = os_zalloc((len + 1) * sizeof(int)); + freqs = os_calloc(len + 1, sizeof(int)); if (freqs == NULL) return NULL; @@ -946,7 +873,7 @@ if (used == len) { int *n; size_t i; - n = os_realloc(freqs, (len * 2 + 1) * sizeof(int)); + n = os_realloc_array(freqs, len * 2 + 1, sizeof(int)); if (n == NULL) { os_free(freqs); return NULL; @@ -974,9 +901,13 @@ { int *freqs; - freqs = wpa_config_parse_freqs(data, ssid, line, value); + freqs = wpa_config_parse_int_array(value); if (freqs == NULL) return -1; + if (freqs[0] == 0) { + os_free(freqs); + freqs = NULL; + } os_free(ssid->scan_freq); ssid->scan_freq = freqs; @@ -990,9 +921,13 @@ { int *freqs; - freqs = wpa_config_parse_freqs(data, ssid, line, value); + freqs = wpa_config_parse_int_array(value); if (freqs == NULL) return -1; + if (freqs[0] == 0) { + os_free(freqs); + freqs = NULL; + } os_free(ssid->freq_list); ssid->freq_list = freqs; @@ -1075,8 +1010,8 @@ last = *end == '\0'; *end = '\0'; tmp = methods; - methods = os_realloc(methods, - (num_methods + 1) * sizeof(*methods)); + methods = os_realloc_array(methods, num_methods + 1, + sizeof(*methods)); if (methods == NULL) { os_free(tmp); os_free(buf); @@ -1106,7 +1041,7 @@ os_free(buf); tmp = methods; - methods = os_realloc(methods, (num_methods + 1) * sizeof(*methods)); + methods = os_realloc_array(methods, num_methods + 1, sizeof(*methods)); if (methods == NULL) { os_free(tmp); return -1; @@ -1172,6 +1107,20 @@ return 0; } +#ifdef CONFIG_EXT_PASSWORD + if (os_strncmp(value, "ext:", 4) == 0) { + char *name = os_strdup(value + 4); + if (name == NULL) + return -1; + os_free(ssid->eap.password); + ssid->eap.password = (u8 *) name; + ssid->eap.password_len = os_strlen(name); + ssid->eap.flags &= ~EAP_CONFIG_FLAGS_PASSWORD_NTHASH; + ssid->eap.flags |= EAP_CONFIG_FLAGS_EXT_PASSWORD; + return 0; + } +#endif /* CONFIG_EXT_PASSWORD */ + if (os_strncmp(value, "hash:", 5) != 0) { char *tmp; size_t res_len; @@ -1189,6 +1138,7 @@ ssid->eap.password = (u8 *) tmp; ssid->eap.password_len = res_len; ssid->eap.flags &= ~EAP_CONFIG_FLAGS_PASSWORD_NTHASH; + ssid->eap.flags &= ~EAP_CONFIG_FLAGS_EXT_PASSWORD; return 0; } @@ -1217,6 +1167,7 @@ ssid->eap.password = hash; ssid->eap.password_len = 16; ssid->eap.flags |= EAP_CONFIG_FLAGS_PASSWORD_NTHASH; + ssid->eap.flags &= ~EAP_CONFIG_FLAGS_EXT_PASSWORD; return 0; } @@ -1230,6 +1181,17 @@ if (ssid->eap.password == NULL) return NULL; +#ifdef CONFIG_EXT_PASSWORD + if (ssid->eap.flags & EAP_CONFIG_FLAGS_EXT_PASSWORD) { + buf = os_zalloc(4 + ssid->eap.password_len + 1); + if (buf == NULL) + return NULL; + os_memcpy(buf, "ext:", 4); + os_memcpy(buf + 4, ssid->eap.password, ssid->eap.password_len); + return buf; + } +#endif /* CONFIG_EXT_PASSWORD */ + if (!(ssid->eap.flags & EAP_CONFIG_FLAGS_PASSWORD_NTHASH)) { return wpa_config_write_string( ssid->eap.password, ssid->eap.password_len); @@ -1265,6 +1227,11 @@ os_free(buf); return -1; } + if (*len && *len != 5 && *len != 13 && *len != 16) { + wpa_printf(MSG_ERROR, "Line %d: Invalid WEP key length %u - " + "this network block will be ignored", + line, (unsigned int) *len); + } os_memcpy(key, buf, *len); os_free(buf); res = os_snprintf(title, sizeof(title), "wep_key%d", idx); @@ -1355,6 +1322,52 @@ #ifdef CONFIG_P2P +static int wpa_config_parse_go_p2p_dev_addr(const struct parse_data *data, + struct wpa_ssid *ssid, int line, + const char *value) +{ + if (value[0] == '\0' || os_strcmp(value, "\"\"") == 0 || + os_strcmp(value, "any") == 0) { + os_memset(ssid->go_p2p_dev_addr, 0, ETH_ALEN); + wpa_printf(MSG_MSGDUMP, "GO P2P Device Address any"); + return 0; + } + if (hwaddr_aton(value, ssid->go_p2p_dev_addr)) { + wpa_printf(MSG_ERROR, "Line %d: Invalid GO P2P Device Address '%s'.", + line, value); + return -1; + } + ssid->bssid_set = 1; + wpa_printf(MSG_MSGDUMP, "GO P2P Device Address " MACSTR, + MAC2STR(ssid->go_p2p_dev_addr)); + return 0; +} + + +#ifndef NO_CONFIG_WRITE +static char * wpa_config_write_go_p2p_dev_addr(const struct parse_data *data, + struct wpa_ssid *ssid) +{ + char *value; + int res; + + if (is_zero_ether_addr(ssid->go_p2p_dev_addr)) + return NULL; + + value = os_malloc(20); + if (value == NULL) + return NULL; + res = os_snprintf(value, 20, MACSTR, MAC2STR(ssid->go_p2p_dev_addr)); + if (res < 0 || res >= 20) { + os_free(value); + return NULL; + } + value[20 - 1] = '\0'; + return value; +} +#endif /* NO_CONFIG_WRITE */ + + static int wpa_config_parse_p2p_client_list(const struct parse_data *data, struct wpa_ssid *ssid, int line, const char *value) @@ -1372,18 +1385,27 @@ pos++; if (hwaddr_aton(pos, addr)) { - wpa_printf(MSG_ERROR, "Line %d: Invalid " - "p2p_client_list address '%s'.", - line, value); - /* continue anyway */ + if (count == 0) { + wpa_printf(MSG_ERROR, "Line %d: Invalid " + "p2p_client_list address '%s'.", + line, value); + os_free(buf); + return -1; + } + /* continue anyway since this could have been from a + * truncated configuration file line */ + wpa_printf(MSG_INFO, "Line %d: Ignore likely " + "truncated p2p_client_list address '%s'", + line, pos); } else { - n = os_realloc(buf, (count + 1) * ETH_ALEN); + n = os_realloc_array(buf, count + 1, ETH_ALEN); if (n == NULL) { os_free(buf); return -1; } buf = n; - os_memcpy(buf + count * ETH_ALEN, addr, ETH_ALEN); + os_memmove(buf + ETH_ALEN, buf, count * ETH_ALEN); + os_memcpy(buf, addr, ETH_ALEN); count++; wpa_hexdump(MSG_MSGDUMP, "p2p_client_list", addr, ETH_ALEN); @@ -1417,10 +1439,10 @@ pos = value; end = value + 20 * ssid->num_p2p_clients; - for (i = 0; i < ssid->num_p2p_clients; i++) { + for (i = ssid->num_p2p_clients; i > 0; i--) { res = os_snprintf(pos, end - pos, MACSTR " ", MAC2STR(ssid->p2p_client_list + - i * ETH_ALEN)); + (i - 1) * ETH_ALEN)); if (res < 0 || res >= end - pos) { os_free(value); return NULL; @@ -1435,6 +1457,60 @@ } #endif /* NO_CONFIG_WRITE */ + +static int wpa_config_parse_psk_list(const struct parse_data *data, + struct wpa_ssid *ssid, int line, + const char *value) +{ + struct psk_list_entry *p; + const char *pos; + + p = os_zalloc(sizeof(*p)); + if (p == NULL) + return -1; + + pos = value; + if (os_strncmp(pos, "P2P-", 4) == 0) { + p->p2p = 1; + pos += 4; + } + + if (hwaddr_aton(pos, p->addr)) { + wpa_printf(MSG_ERROR, "Line %d: Invalid psk_list address '%s'", + line, pos); + os_free(p); + return -1; + } + pos += 17; + if (*pos != '-') { + wpa_printf(MSG_ERROR, "Line %d: Invalid psk_list '%s'", + line, pos); + os_free(p); + return -1; + } + pos++; + + if (hexstr2bin(pos, p->psk, PMK_LEN) || pos[PMK_LEN * 2] != '\0') { + wpa_printf(MSG_ERROR, "Line %d: Invalid psk_list PSK '%s'", + line, pos); + os_free(p); + return -1; + } + + dl_list_add(&ssid->psk_list, &p->list); + + return 0; +} + + +#ifndef NO_CONFIG_WRITE +static char * wpa_config_write_psk_list(const struct parse_data *data, + struct wpa_ssid *ssid) +{ + return NULL; +} +#endif /* NO_CONFIG_WRITE */ + #endif /* CONFIG_P2P */ /* Helper macros for network block parser */ @@ -1531,6 +1607,7 @@ { FUNC_KEY(psk) }, { FUNC(proto) }, { FUNC(key_mgmt) }, + { INT(bg_scan_period) }, { FUNC(pairwise) }, { FUNC(group) }, { FUNC(auth_alg) }, @@ -1549,6 +1626,7 @@ { STRe(dh_file) }, { STRe(subject_match) }, { STRe(altsubject_match) }, + { STRe(domain_suffix_match) }, { STRe(ca_cert2) }, { STRe(ca_path2) }, { STRe(client_cert2) }, @@ -1557,6 +1635,7 @@ { STRe(dh_file2) }, { STRe(subject_match2) }, { STRe(altsubject_match2) }, + { STRe(domain_suffix_match2) }, { STRe(phase1) }, { STRe(phase2) }, { STRe(pcsc) }, @@ -1584,6 +1663,7 @@ { INT(eap_workaround) }, { STRe(pac_file) }, { INTe(fragment_size) }, + { INTe(ocsp) }, #endif /* IEEE8021X_EAPOL */ { INT_RANGE(mode, 0, 4) }, { INT_RANGE(proactive_key_caching, 0, 1) }, @@ -1594,12 +1674,48 @@ #endif /* CONFIG_IEEE80211W */ { INT_RANGE(peerkey, 0, 1) }, { INT_RANGE(mixed_cell, 0, 1) }, - { INT_RANGE(frequency, 0, 10000) }, + { INT_RANGE(frequency, 0, 65000) }, { INT(wpa_ptk_rekey) }, { STR(bgscan) }, + { INT_RANGE(ignore_broadcast_ssid, 0, 2) }, #ifdef CONFIG_P2P + { FUNC(go_p2p_dev_addr) }, { FUNC(p2p_client_list) }, + { FUNC(psk_list) }, #endif /* CONFIG_P2P */ +#ifdef CONFIG_HT_OVERRIDES + { INT_RANGE(disable_ht, 0, 1) }, + { INT_RANGE(disable_ht40, -1, 1) }, + { INT_RANGE(disable_sgi, 0, 1) }, + { INT_RANGE(disable_max_amsdu, -1, 1) }, + { INT_RANGE(ampdu_factor, -1, 3) }, + { INT_RANGE(ampdu_density, -1, 7) }, + { STR(ht_mcs) }, +#endif /* CONFIG_HT_OVERRIDES */ +#ifdef CONFIG_VHT_OVERRIDES + { INT_RANGE(disable_vht, 0, 1) }, + { INT(vht_capa) }, + { INT(vht_capa_mask) }, + { INT_RANGE(vht_rx_mcs_nss_1, -1, 3) }, + { INT_RANGE(vht_rx_mcs_nss_2, -1, 3) }, + { INT_RANGE(vht_rx_mcs_nss_3, -1, 3) }, + { INT_RANGE(vht_rx_mcs_nss_4, -1, 3) }, + { INT_RANGE(vht_rx_mcs_nss_5, -1, 3) }, + { INT_RANGE(vht_rx_mcs_nss_6, -1, 3) }, + { INT_RANGE(vht_rx_mcs_nss_7, -1, 3) }, + { INT_RANGE(vht_rx_mcs_nss_8, -1, 3) }, + { INT_RANGE(vht_tx_mcs_nss_1, -1, 3) }, + { INT_RANGE(vht_tx_mcs_nss_2, -1, 3) }, + { INT_RANGE(vht_tx_mcs_nss_3, -1, 3) }, + { INT_RANGE(vht_tx_mcs_nss_4, -1, 3) }, + { INT_RANGE(vht_tx_mcs_nss_5, -1, 3) }, + { INT_RANGE(vht_tx_mcs_nss_6, -1, 3) }, + { INT_RANGE(vht_tx_mcs_nss_7, -1, 3) }, + { INT_RANGE(vht_tx_mcs_nss_8, -1, 3) }, +#endif /* CONFIG_VHT_OVERRIDES */ + { INT(ap_max_inactivity) }, + { INT(dtim_period) }, + { INT(beacon_int) }, }; #undef OFFSET @@ -1618,7 +1734,7 @@ #undef _FUNC #undef FUNC #undef FUNC_KEY -#define NUM_SSID_FIELDS (sizeof(ssid_fields) / sizeof(ssid_fields[0])) +#define NUM_SSID_FIELDS ARRAY_SIZE(ssid_fields) /** @@ -1653,19 +1769,20 @@ } /* First network for this priority - add a new priority list */ - nlist = os_realloc(config->pssid, - (config->num_prio + 1) * sizeof(struct wpa_ssid *)); + nlist = os_realloc_array(config->pssid, config->num_prio + 1, + sizeof(struct wpa_ssid *)); if (nlist == NULL) return -1; for (prio = 0; prio < config->num_prio; prio++) { - if (nlist[prio]->priority < ssid->priority) + if (nlist[prio]->priority < ssid->priority) { + os_memmove(&nlist[prio + 1], &nlist[prio], + (config->num_prio - prio) * + sizeof(struct wpa_ssid *)); break; + } } - os_memmove(&nlist[prio + 1], &nlist[prio], - (config->num_prio - prio) * sizeof(struct wpa_ssid *)); - nlist[prio] = ssid; config->num_prio++; config->pssid = nlist; @@ -1719,6 +1836,7 @@ os_free(eap->dh_file); os_free(eap->subject_match); os_free(eap->altsubject_match); + os_free(eap->domain_suffix_match); os_free(eap->ca_cert2); os_free(eap->ca_path2); os_free(eap->client_cert2); @@ -1727,6 +1845,7 @@ os_free(eap->dh_file2); os_free(eap->subject_match2); os_free(eap->altsubject_match2); + os_free(eap->domain_suffix_match2); os_free(eap->phase1); os_free(eap->phase2); os_free(eap->pcsc); @@ -1744,6 +1863,7 @@ os_free(eap->pending_req_otp); os_free(eap->pac_file); os_free(eap->new_password); + os_free(eap->external_sim_resp); } #endif /* IEEE8021X_EAPOL */ @@ -1757,8 +1877,11 @@ */ void wpa_config_free_ssid(struct wpa_ssid *ssid) { + struct psk_list_entry *psk; + os_free(ssid->ssid); os_free(ssid->passphrase); + os_free(ssid->ext_psk); #ifdef IEEE8021X_EAPOL eap_peer_config_free(&ssid->eap); #endif /* IEEE8021X_EAPOL */ @@ -1767,10 +1890,59 @@ os_free(ssid->freq_list); os_free(ssid->bgscan); os_free(ssid->p2p_client_list); +#ifdef CONFIG_HT_OVERRIDES + os_free(ssid->ht_mcs); +#endif /* CONFIG_HT_OVERRIDES */ + while ((psk = dl_list_first(&ssid->psk_list, struct psk_list_entry, + list))) { + dl_list_del(&psk->list); + os_free(psk); + } os_free(ssid); } +void wpa_config_free_cred(struct wpa_cred *cred) +{ + size_t i; + + os_free(cred->realm); + os_free(cred->username); + os_free(cred->password); + os_free(cred->ca_cert); + os_free(cred->client_cert); + os_free(cred->private_key); + os_free(cred->private_key_passwd); + os_free(cred->imsi); + os_free(cred->milenage); + for (i = 0; i < cred->num_domain; i++) + os_free(cred->domain[i]); + os_free(cred->domain); + os_free(cred->domain_suffix_match); + os_free(cred->eap_method); + os_free(cred->phase1); + os_free(cred->phase2); + os_free(cred->excluded_ssid); + os_free(cred); +} + + +void wpa_config_flush_blobs(struct wpa_config *config) +{ +#ifndef CONFIG_NO_CONFIG_BLOBS + struct wpa_config_blob *blob, *prev; + + blob = config->blobs; + config->blobs = NULL; + while (blob) { + prev = blob; + blob = blob->next; + wpa_config_free_blob(prev); + } +#endif /* CONFIG_NO_CONFIG_BLOBS */ +} + + /** * wpa_config_free - Free configuration data * @config: Configuration data from wpa_config_read() @@ -1780,10 +1952,8 @@ */ void wpa_config_free(struct wpa_config *config) { -#ifndef CONFIG_NO_CONFIG_BLOBS - struct wpa_config_blob *blob, *prevblob; -#endif /* CONFIG_NO_CONFIG_BLOBS */ struct wpa_ssid *ssid, *prev = NULL; + struct wpa_cred *cred, *cprev; ssid = config->ssid; while (ssid) { @@ -1792,21 +1962,23 @@ wpa_config_free_ssid(prev); } -#ifndef CONFIG_NO_CONFIG_BLOBS - blob = config->blobs; - prevblob = NULL; - while (blob) { - prevblob = blob; - blob = blob->next; - wpa_config_free_blob(prevblob); + cred = config->cred; + while (cred) { + cprev = cred; + cred = cred->next; + wpa_config_free_cred(cprev); } -#endif /* CONFIG_NO_CONFIG_BLOBS */ + wpa_config_flush_blobs(config); + + wpabuf_free(config->wps_vendor_ext_m1); os_free(config->ctrl_interface); os_free(config->ctrl_interface_group); os_free(config->opensc_engine_path); os_free(config->pkcs11_engine_path); os_free(config->pkcs11_module_path); + os_free(config->pcsc_reader); + os_free(config->pcsc_pin); os_free(config->driver_param); os_free(config->device_name); os_free(config->manufacturer); @@ -1816,13 +1988,16 @@ os_free(config->config_methods); os_free(config->p2p_ssid_postfix); os_free(config->pssid); - os_free(config->home_realm); - os_free(config->home_username); - os_free(config->home_password); - os_free(config->home_ca_cert); - os_free(config->home_imsi); - os_free(config->home_milenage); os_free(config->p2p_pref_chan); + os_free(config->p2p_no_go_freq.range); + os_free(config->autoscan); + os_free(config->freq_list); + wpabuf_free(config->wps_nfc_dh_pubkey); + wpabuf_free(config->wps_nfc_dh_privkey); + wpabuf_free(config->wps_nfc_dev_pw); + os_free(config->ext_password_backend); + os_free(config->sae_groups); + wpabuf_free(config->ap_vendor_elements); os_free(config); } @@ -1897,6 +2072,7 @@ if (ssid == NULL) return NULL; ssid->id = id; + dl_list_init(&ssid->psk_list); if (last) last->next = ssid; else @@ -1950,11 +2126,42 @@ ssid->pairwise_cipher = DEFAULT_PAIRWISE; ssid->group_cipher = DEFAULT_GROUP; ssid->key_mgmt = DEFAULT_KEY_MGMT; + ssid->bg_scan_period = DEFAULT_BG_SCAN_PERIOD; #ifdef IEEE8021X_EAPOL ssid->eapol_flags = DEFAULT_EAPOL_FLAGS; ssid->eap_workaround = DEFAULT_EAP_WORKAROUND; ssid->eap.fragment_size = DEFAULT_FRAGMENT_SIZE; #endif /* IEEE8021X_EAPOL */ +#ifdef CONFIG_HT_OVERRIDES + ssid->disable_ht = DEFAULT_DISABLE_HT; + ssid->disable_ht40 = DEFAULT_DISABLE_HT40; + ssid->disable_sgi = DEFAULT_DISABLE_SGI; + ssid->disable_max_amsdu = DEFAULT_DISABLE_MAX_AMSDU; + ssid->ampdu_factor = DEFAULT_AMPDU_FACTOR; + ssid->ampdu_density = DEFAULT_AMPDU_DENSITY; +#endif /* CONFIG_HT_OVERRIDES */ +#ifdef CONFIG_VHT_OVERRIDES + ssid->vht_rx_mcs_nss_1 = -1; + ssid->vht_rx_mcs_nss_2 = -1; + ssid->vht_rx_mcs_nss_3 = -1; + ssid->vht_rx_mcs_nss_4 = -1; + ssid->vht_rx_mcs_nss_5 = -1; + ssid->vht_rx_mcs_nss_6 = -1; + ssid->vht_rx_mcs_nss_7 = -1; + ssid->vht_rx_mcs_nss_8 = -1; + ssid->vht_tx_mcs_nss_1 = -1; + ssid->vht_tx_mcs_nss_2 = -1; + ssid->vht_tx_mcs_nss_3 = -1; + ssid->vht_tx_mcs_nss_4 = -1; + ssid->vht_tx_mcs_nss_5 = -1; + ssid->vht_tx_mcs_nss_6 = -1; + ssid->vht_tx_mcs_nss_7 = -1; + ssid->vht_tx_mcs_nss_8 = -1; +#endif /* CONFIG_VHT_OVERRIDES */ + ssid->proactive_key_caching = -1; +#ifdef CONFIG_IEEE80211W + ssid->ieee80211w = MGMT_FRAME_PROTECTION_DEFAULT; +#endif /* CONFIG_IEEE80211W */ } @@ -2049,7 +2256,7 @@ get_keys = get_keys && ssid->export_keys; - props = os_zalloc(sizeof(char *) * ((2 * NUM_SSID_FIELDS) + 1)); + props = os_calloc(2 * NUM_SSID_FIELDS + 1, sizeof(char *)); if (!props) return NULL; @@ -2177,8 +2384,7 @@ void wpa_config_update_psk(struct wpa_ssid *ssid) { #ifndef CONFIG_NO_PBKDF2 - pbkdf2_sha1(ssid->passphrase, - (char *) ssid->ssid, ssid->ssid_len, 4096, + pbkdf2_sha1(ssid->passphrase, ssid->ssid, ssid->ssid_len, 4096, ssid->psk, PMK_LEN); wpa_hexdump_key(MSG_MSGDUMP, "PSK (from passphrase)", ssid->psk, PMK_LEN); @@ -2187,8 +2393,284 @@ } -#ifndef CONFIG_NO_CONFIG_BLOBS -/** +int wpa_config_set_cred(struct wpa_cred *cred, const char *var, + const char *value, int line) +{ + char *val; + size_t len; + + if (os_strcmp(var, "temporary") == 0) { + cred->temporary = atoi(value); + return 0; + } + + if (os_strcmp(var, "priority") == 0) { + cred->priority = atoi(value); + return 0; + } + + if (os_strcmp(var, "pcsc") == 0) { + cred->pcsc = atoi(value); + return 0; + } + + if (os_strcmp(var, "eap") == 0) { + struct eap_method_type method; + method.method = eap_peer_get_type(value, &method.vendor); + if (method.vendor == EAP_VENDOR_IETF && + method.method == EAP_TYPE_NONE) { + wpa_printf(MSG_ERROR, "Line %d: unknown EAP type '%s' " + "for a credential", line, value); + return -1; + } + os_free(cred->eap_method); + cred->eap_method = os_malloc(sizeof(*cred->eap_method)); + if (cred->eap_method == NULL) + return -1; + os_memcpy(cred->eap_method, &method, sizeof(method)); + return 0; + } + + if (os_strcmp(var, "password") == 0 && + os_strncmp(value, "ext:", 4) == 0) { + os_free(cred->password); + cred->password = os_strdup(value); + cred->ext_password = 1; + return 0; + } + + val = wpa_config_parse_string(value, &len); + if (val == NULL) { + wpa_printf(MSG_ERROR, "Line %d: invalid field '%s' string " + "value '%s'.", line, var, value); + return -1; + } + + if (os_strcmp(var, "realm") == 0) { + os_free(cred->realm); + cred->realm = val; + return 0; + } + + if (os_strcmp(var, "username") == 0) { + os_free(cred->username); + cred->username = val; + return 0; + } + + if (os_strcmp(var, "password") == 0) { + os_free(cred->password); + cred->password = val; + cred->ext_password = 0; + return 0; + } + + if (os_strcmp(var, "ca_cert") == 0) { + os_free(cred->ca_cert); + cred->ca_cert = val; + return 0; + } + + if (os_strcmp(var, "client_cert") == 0) { + os_free(cred->client_cert); + cred->client_cert = val; + return 0; + } + + if (os_strcmp(var, "private_key") == 0) { + os_free(cred->private_key); + cred->private_key = val; + return 0; + } + + if (os_strcmp(var, "private_key_passwd") == 0) { + os_free(cred->private_key_passwd); + cred->private_key_passwd = val; + return 0; + } + + if (os_strcmp(var, "imsi") == 0) { + os_free(cred->imsi); + cred->imsi = val; + return 0; + } + + if (os_strcmp(var, "milenage") == 0) { + os_free(cred->milenage); + cred->milenage = val; + return 0; + } + + if (os_strcmp(var, "domain_suffix_match") == 0) { + os_free(cred->domain_suffix_match); + cred->domain_suffix_match = val; + return 0; + } + + if (os_strcmp(var, "domain") == 0) { + char **new_domain; + new_domain = os_realloc_array(cred->domain, + cred->num_domain + 1, + sizeof(char *)); + if (new_domain == NULL) { + os_free(val); + return -1; + } + new_domain[cred->num_domain++] = val; + cred->domain = new_domain; + return 0; + } + + if (os_strcmp(var, "phase1") == 0) { + os_free(cred->phase1); + cred->phase1 = val; + return 0; + } + + if (os_strcmp(var, "phase2") == 0) { + os_free(cred->phase2); + cred->phase2 = val; + return 0; + } + + if (os_strcmp(var, "roaming_consortium") == 0) { + if (len < 3 || len > sizeof(cred->roaming_consortium)) { + wpa_printf(MSG_ERROR, "Line %d: invalid " + "roaming_consortium length %d (3..15 " + "expected)", line, (int) len); + os_free(val); + return -1; + } + os_memcpy(cred->roaming_consortium, val, len); + cred->roaming_consortium_len = len; + os_free(val); + return 0; + } + + if (os_strcmp(var, "required_roaming_consortium") == 0) { + if (len < 3 || len > sizeof(cred->required_roaming_consortium)) + { + wpa_printf(MSG_ERROR, "Line %d: invalid " + "required_roaming_consortium length %d " + "(3..15 expected)", line, (int) len); + os_free(val); + return -1; + } + os_memcpy(cred->required_roaming_consortium, val, len); + cred->required_roaming_consortium_len = len; + os_free(val); + return 0; + } + + if (os_strcmp(var, "excluded_ssid") == 0) { + struct excluded_ssid *e; + + if (len > MAX_SSID_LEN) { + wpa_printf(MSG_ERROR, "Line %d: invalid " + "excluded_ssid length %d", line, (int) len); + os_free(val); + return -1; + } + + e = os_realloc_array(cred->excluded_ssid, + cred->num_excluded_ssid + 1, + sizeof(struct excluded_ssid)); + if (e == NULL) { + os_free(val); + return -1; + } + cred->excluded_ssid = e; + + e = &cred->excluded_ssid[cred->num_excluded_ssid++]; + os_memcpy(e->ssid, val, len); + e->ssid_len = len; + + os_free(val); + + return 0; + } + + if (line) { + wpa_printf(MSG_ERROR, "Line %d: unknown cred field '%s'.", + line, var); + } + + os_free(val); + + return -1; +} + + +struct wpa_cred * wpa_config_get_cred(struct wpa_config *config, int id) +{ + struct wpa_cred *cred; + + cred = config->cred; + while (cred) { + if (id == cred->id) + break; + cred = cred->next; + } + + return cred; +} + + +struct wpa_cred * wpa_config_add_cred(struct wpa_config *config) +{ + int id; + struct wpa_cred *cred, *last = NULL; + + id = -1; + cred = config->cred; + while (cred) { + if (cred->id > id) + id = cred->id; + last = cred; + cred = cred->next; + } + id++; + + cred = os_zalloc(sizeof(*cred)); + if (cred == NULL) + return NULL; + cred->id = id; + if (last) + last->next = cred; + else + config->cred = cred; + + return cred; +} + + +int wpa_config_remove_cred(struct wpa_config *config, int id) +{ + struct wpa_cred *cred, *prev = NULL; + + cred = config->cred; + while (cred) { + if (id == cred->id) + break; + prev = cred; + cred = cred->next; + } + + if (cred == NULL) + return -1; + + if (prev) + prev->next = cred->next; + else + config->cred = cred->next; + + wpa_config_free_cred(cred); + return 0; +} + + +#ifndef CONFIG_NO_CONFIG_BLOBS +/** * wpa_config_get_blob - Get a named configuration blob * @config: Configuration data from wpa_config_read() * @name: Name of the blob @@ -2278,6 +2760,15 @@ const char *driver_param) { struct wpa_config *config; + const int aCWmin = 4, aCWmax = 10; + const struct hostapd_wmm_ac_params ac_bk = + { aCWmin, aCWmax, 7, 0, 0 }; /* background traffic */ + const struct hostapd_wmm_ac_params ac_be = + { aCWmin, aCWmax, 3, 0, 0 }; /* best effort traffic */ + const struct hostapd_wmm_ac_params ac_vi = /* video traffic */ + { aCWmin - 1, aCWmin, 2, 3000 / 32, 0 }; + const struct hostapd_wmm_ac_params ac_vo = /* voice traffic */ + { aCWmin - 2, aCWmin - 1, 2, 1500 / 32, 0 }; config = os_zalloc(sizeof(*config)); if (config == NULL) @@ -2287,11 +2778,17 @@ config->fast_reauth = DEFAULT_FAST_REAUTH; config->p2p_go_intent = DEFAULT_P2P_GO_INTENT; config->p2p_intra_bss = DEFAULT_P2P_INTRA_BSS; + config->p2p_go_max_inactivity = DEFAULT_P2P_GO_MAX_INACTIVITY; config->bss_max_count = DEFAULT_BSS_MAX_COUNT; config->bss_expiration_age = DEFAULT_BSS_EXPIRATION_AGE; config->bss_expiration_scan_count = DEFAULT_BSS_EXPIRATION_SCAN_COUNT; config->max_num_sta = DEFAULT_MAX_NUM_STA; config->access_network_type = DEFAULT_ACCESS_NETWORK_TYPE; + config->scan_cur_freq = DEFAULT_SCAN_CUR_FREQ; + config->wmm_ac_params[0] = ac_be; + config->wmm_ac_params[1] = ac_bk; + config->wmm_ac_params[2] = ac_vi; + config->wmm_ac_params[3] = ac_vo; if (ctrl_interface) config->ctrl_interface = os_strdup(ctrl_interface); @@ -2340,9 +2837,18 @@ struct wpa_config *config, int line, const char *pos) { - int *dst; + int val, *dst; + char *end; + dst = (int *) (((u8 *) config) + (long) data->param1); - *dst = atoi(pos); + val = strtol(pos, &end, 0); + if (*end) { + wpa_printf(MSG_ERROR, "Line %d: invalid number \"%s\"", + line, pos); + return -1; + } + *dst = val; + wpa_printf(MSG_DEBUG, "%s=%d", data->name, *dst); if (data->param2 && *dst < (long) data->param2) { @@ -2400,6 +2906,98 @@ } +static int wpa_config_process_bgscan(const struct global_parse_data *data, + struct wpa_config *config, int line, + const char *pos) +{ + size_t len; + char *tmp; + int res; + + tmp = wpa_config_parse_string(pos, &len); + if (tmp == NULL) { + wpa_printf(MSG_ERROR, "Line %d: failed to parse %s", + line, data->name); + return -1; + } + + res = wpa_global_config_parse_str(data, config, line, tmp); + os_free(tmp); + return res; +} + + +static int wpa_global_config_parse_bin(const struct global_parse_data *data, + struct wpa_config *config, int line, + const char *pos) +{ + size_t len; + struct wpabuf **dst, *tmp; + + len = os_strlen(pos); + if (len & 0x01) + return -1; + + tmp = wpabuf_alloc(len / 2); + if (tmp == NULL) + return -1; + + if (hexstr2bin(pos, wpabuf_put(tmp, len / 2), len / 2)) { + wpabuf_free(tmp); + return -1; + } + + dst = (struct wpabuf **) (((u8 *) config) + (long) data->param1); + wpabuf_free(*dst); + *dst = tmp; + wpa_printf(MSG_DEBUG, "%s", data->name); + + return 0; +} + + +static int wpa_config_process_freq_list(const struct global_parse_data *data, + struct wpa_config *config, int line, + const char *value) +{ + int *freqs; + + freqs = wpa_config_parse_int_array(value); + if (freqs == NULL) + return -1; + if (freqs[0] == 0) { + os_free(freqs); + freqs = NULL; + } + os_free(config->freq_list); + config->freq_list = freqs; + return 0; +} + + +#ifdef CONFIG_P2P +static int wpa_global_config_parse_ipv4(const struct global_parse_data *data, + struct wpa_config *config, int line, + const char *pos) +{ + u32 *dst; + struct hostapd_ip_addr addr; + + if (hostapd_parse_ip_addr(pos, &addr) < 0) + return -1; + if (addr.af != AF_INET) + return -1; + + dst = (u32 *) (((u8 *) config) + (long) data->param1); + os_memcpy(dst, &addr.u.v4.s_addr, 4); + wpa_printf(MSG_DEBUG, "%s = 0x%x", data->name, + WPA_GET_BE32((u8 *) dst)); + + return 0; +} +#endif /* CONFIG_P2P */ + + static int wpa_config_process_country(const struct global_parse_data *data, struct wpa_config *config, int line, const char *pos) @@ -2474,6 +3072,43 @@ return 0; } + +static int wpa_config_process_wps_vendor_ext_m1( + const struct global_parse_data *data, + struct wpa_config *config, int line, const char *pos) +{ + struct wpabuf *tmp; + int len = os_strlen(pos) / 2; + u8 *p; + + if (!len) { + wpa_printf(MSG_ERROR, "Line %d: " + "invalid wps_vendor_ext_m1", line); + return -1; + } + + tmp = wpabuf_alloc(len); + if (tmp) { + p = wpabuf_put(tmp, len); + + if (hexstr2bin(pos, p, len)) { + wpa_printf(MSG_ERROR, "Line %d: " + "invalid wps_vendor_ext_m1", line); + wpabuf_free(tmp); + return -1; + } + + wpabuf_free(config->wps_vendor_ext_m1); + config->wps_vendor_ext_m1 = tmp; + } else { + wpa_printf(MSG_ERROR, "Can not allocate " + "memory for wps_vendor_ext_m1"); + return -1; + } + + return 0; +} + #endif /* CONFIG_WPS */ #ifdef CONFIG_P2P @@ -2518,7 +3153,8 @@ pos2++; chan = atoi(pos2); - n = os_realloc(pref, (num + 1) * sizeof(struct p2p_channel)); + n = os_realloc_array(pref, num + 1, + sizeof(struct p2p_channel)); if (n == NULL) goto fail; pref = n; @@ -2546,6 +3182,26 @@ wpa_printf(MSG_ERROR, "Line %d: Invalid p2p_pref_chan list", line); return -1; } + + +static int wpa_config_process_p2p_no_go_freq( + const struct global_parse_data *data, + struct wpa_config *config, int line, const char *pos) +{ + int ret; + + ret = freq_range_list_parse(&config->p2p_no_go_freq, pos); + if (ret < 0) { + wpa_printf(MSG_ERROR, "Line %d: Invalid p2p_no_go_freq", line); + return -1; + } + + wpa_printf(MSG_DEBUG, "P2P: p2p_no_go_freq with %u items", + config->p2p_no_go_freq.num); + + return 0; +} + #endif /* CONFIG_P2P */ @@ -2563,6 +3219,74 @@ } +static int wpa_config_process_sae_groups( + const struct global_parse_data *data, + struct wpa_config *config, int line, const char *pos) +{ + int *groups = wpa_config_parse_int_array(pos); + if (groups == NULL) { + wpa_printf(MSG_ERROR, "Line %d: Invalid sae_groups '%s'", + line, pos); + return -1; + } + + os_free(config->sae_groups); + config->sae_groups = groups; + + return 0; +} + + +static int wpa_config_process_ap_vendor_elements( + const struct global_parse_data *data, + struct wpa_config *config, int line, const char *pos) +{ + struct wpabuf *tmp; + int len = os_strlen(pos) / 2; + u8 *p; + + if (!len) { + wpa_printf(MSG_ERROR, "Line %d: invalid ap_vendor_elements", + line); + return -1; + } + + tmp = wpabuf_alloc(len); + if (tmp) { + p = wpabuf_put(tmp, len); + + if (hexstr2bin(pos, p, len)) { + wpa_printf(MSG_ERROR, "Line %d: invalid " + "ap_vendor_elements", line); + wpabuf_free(tmp); + return -1; + } + + wpabuf_free(config->ap_vendor_elements); + config->ap_vendor_elements = tmp; + } else { + wpa_printf(MSG_ERROR, "Cannot allocate memory for " + "ap_vendor_elements"); + return -1; + } + + return 0; +} + + +#ifdef CONFIG_CTRL_IFACE +static int wpa_config_process_no_ctrl_interface( + const struct global_parse_data *data, + struct wpa_config *config, int line, const char *pos) +{ + wpa_printf(MSG_DEBUG, "no_ctrl_interface -> ctrl_interface=NULL"); + os_free(config->ctrl_interface); + config->ctrl_interface = NULL; + return 0; +} +#endif /* CONFIG_CTRL_IFACE */ + + #ifdef OFFSET #undef OFFSET #endif /* OFFSET */ @@ -2577,18 +3301,26 @@ #define _STR(f) #f, wpa_global_config_parse_str, OFFSET(f) #define STR(f) _STR(f), NULL, NULL #define STR_RANGE(f, min, max) _STR(f), (void *) min, (void *) max +#define BIN(f) #f, wpa_global_config_parse_bin, OFFSET(f), NULL, NULL +#define IPV4(f) #f, wpa_global_config_parse_ipv4, OFFSET(f), NULL, NULL static const struct global_parse_data global_fields[] = { #ifdef CONFIG_CTRL_IFACE { STR(ctrl_interface), 0 }, + { FUNC_NO_VAR(no_ctrl_interface), 0 }, { STR(ctrl_interface_group), 0 } /* deprecated */, #endif /* CONFIG_CTRL_IFACE */ { INT_RANGE(eapol_version, 1, 2), 0 }, { INT(ap_scan), 0 }, + { FUNC(bgscan), 0 }, + { INT(disable_scan_offload), 0 }, { INT(fast_reauth), 0 }, { STR(opensc_engine_path), 0 }, { STR(pkcs11_engine_path), 0 }, { STR(pkcs11_module_path), 0 }, + { STR(pcsc_reader), 0 }, + { STR(pcsc_pin), 0 }, + { INT(external_sim), 0 }, { STR(driver_param), 0 }, { INT(dot11RSNAConfigPMKLifetime), 0 }, { INT(dot11RSNAConfigPMKReauthThreshold), 0 }, @@ -2608,36 +3340,67 @@ { FUNC(os_version), CFG_CHANGED_OS_VERSION }, { STR(config_methods), CFG_CHANGED_CONFIG_METHODS }, { INT_RANGE(wps_cred_processing, 0, 2), 0 }, + { FUNC(wps_vendor_ext_m1), CFG_CHANGED_VENDOR_EXTENSION }, #endif /* CONFIG_WPS */ #ifdef CONFIG_P2P { FUNC(sec_device_type), CFG_CHANGED_SEC_DEVICE_TYPE }, { INT(p2p_listen_reg_class), 0 }, { INT(p2p_listen_channel), 0 }, - { INT(p2p_oper_reg_class), 0 }, - { INT(p2p_oper_channel), 0 }, + { INT(p2p_oper_reg_class), CFG_CHANGED_P2P_OPER_CHANNEL }, + { INT(p2p_oper_channel), CFG_CHANGED_P2P_OPER_CHANNEL }, { INT_RANGE(p2p_go_intent, 0, 15), 0 }, { STR(p2p_ssid_postfix), CFG_CHANGED_P2P_SSID_POSTFIX }, { INT_RANGE(persistent_reconnect, 0, 1), 0 }, { INT_RANGE(p2p_intra_bss, 0, 1), CFG_CHANGED_P2P_INTRA_BSS }, { INT(p2p_group_idle), 0 }, { FUNC(p2p_pref_chan), CFG_CHANGED_P2P_PREF_CHAN }, + { FUNC(p2p_no_go_freq), CFG_CHANGED_P2P_PREF_CHAN }, + { INT_RANGE(p2p_add_cli_chan, 0, 1), 0 }, + { INT(p2p_go_ht40), 0 }, + { INT(p2p_go_vht), 0 }, + { INT(p2p_disabled), 0 }, + { INT(p2p_no_group_iface), 0 }, + { INT_RANGE(p2p_ignore_shared_freq, 0, 1), 0 }, + { IPV4(ip_addr_go), 0 }, + { IPV4(ip_addr_mask), 0 }, + { IPV4(ip_addr_start), 0 }, + { IPV4(ip_addr_end), 0 }, #endif /* CONFIG_P2P */ { FUNC(country), CFG_CHANGED_COUNTRY }, { INT(bss_max_count), 0 }, { INT(bss_expiration_age), 0 }, { INT(bss_expiration_scan_count), 0 }, { INT_RANGE(filter_ssids, 0, 1), 0 }, + { INT_RANGE(filter_rssi, -100, 0), 0 }, { INT(max_num_sta), 0 }, { INT_RANGE(disassoc_low_ack, 0, 1), 0 }, - { STR(home_realm), 0 }, - { STR(home_username), 0 }, - { STR(home_password), 0 }, - { STR(home_ca_cert), 0 }, - { STR(home_imsi), 0 }, - { STR(home_milenage), 0 }, +#ifdef CONFIG_HS20 + { INT_RANGE(hs20, 0, 1), 0 }, +#endif /* CONFIG_HS20 */ { INT_RANGE(interworking, 0, 1), 0 }, { FUNC(hessid), 0 }, - { INT_RANGE(access_network_type, 0, 15), 0 } + { INT_RANGE(access_network_type, 0, 15), 0 }, + { INT_RANGE(pbc_in_m1, 0, 1), 0 }, + { STR(autoscan), 0 }, + { INT_RANGE(wps_nfc_dev_pw_id, 0x10, 0xffff), + CFG_CHANGED_NFC_PASSWORD_TOKEN }, + { BIN(wps_nfc_dh_pubkey), CFG_CHANGED_NFC_PASSWORD_TOKEN }, + { BIN(wps_nfc_dh_privkey), CFG_CHANGED_NFC_PASSWORD_TOKEN }, + { BIN(wps_nfc_dev_pw), CFG_CHANGED_NFC_PASSWORD_TOKEN }, + { STR(ext_password_backend), CFG_CHANGED_EXT_PW_BACKEND }, + { INT(p2p_go_max_inactivity), 0 }, + { INT_RANGE(auto_interworking, 0, 1), 0 }, + { INT(okc), 0 }, + { INT(pmf), 0 }, + { FUNC(sae_groups), 0 }, + { INT(dtim_period), 0 }, + { INT(beacon_int), 0 }, + { FUNC(ap_vendor_elements), 0 }, + { INT_RANGE(ignore_old_scan_res, 0, 1), 0 }, + { FUNC(freq_list), 0 }, + { INT(scan_cur_freq), 0 }, + { INT(sched_scan_interval), 0 }, + { INT(tdls_external_control), 0}, }; #undef FUNC @@ -2647,7 +3410,9 @@ #undef _STR #undef STR #undef STR_RANGE -#define NUM_GLOBAL_FIELDS (sizeof(global_fields) / sizeof(global_fields[0])) +#undef BIN +#undef IPV4 +#define NUM_GLOBAL_FIELDS ARRAY_SIZE(global_fields) int wpa_config_process_global(struct wpa_config *config, char *pos, int line) @@ -2667,10 +3432,31 @@ "parse '%s'.", line, pos); ret = -1; } + if (field->changed_flag == CFG_CHANGED_NFC_PASSWORD_TOKEN) + config->wps_nfc_pw_from_config = 1; config->changed_parameters |= field->changed_flag; break; } if (i == NUM_GLOBAL_FIELDS) { +#ifdef CONFIG_AP + if (os_strncmp(pos, "wmm_ac_", 7) == 0) { + char *tmp = os_strchr(pos, '='); + if (tmp == NULL) { + if (line < 0) + return -1; + wpa_printf(MSG_ERROR, "Line %d: invalid line " + "'%s'", line, pos); + return -1; + } + *tmp++ = '\0'; + if (hostapd_config_wmm_ac(config->wmm_ac_params, pos, + tmp)) { + wpa_printf(MSG_ERROR, "Line %d: invalid WMM " + "AC item", line); + return -1; + } + } +#endif /* CONFIG_AP */ if (line < 0) return -1; wpa_printf(MSG_ERROR, "Line %d: unknown global field '%s'.", diff -Nru wpa-1.0/wpa_supplicant/config_file.c wpa-2.1/wpa_supplicant/config_file.c --- wpa-1.0/wpa_supplicant/config_file.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/config_file.c 2014-02-04 11:23:35.000000000 +0000 @@ -1,15 +1,9 @@ /* * WPA Supplicant / Configuration backend: text file - * Copyright (c) 2003-2008, Jouni Malinen + * Copyright (c) 2003-2012, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. * * This file implements a configuration backend for text files. All the * configuration information is stored in a text file that uses a format @@ -23,6 +17,31 @@ #include "base64.h" #include "uuid.h" #include "p2p/p2p.h" +#include "eap_peer/eap_methods.h" +#include "eap_peer/eap.h" + + +static int newline_terminated(const char *buf, size_t buflen) +{ + size_t len = os_strlen(buf); + if (len == 0) + return 0; + if (len == buflen - 1 && buf[buflen - 1] != '\r' && + buf[len - 1] != '\n') + return 0; + return 1; +} + + +static void skip_line_end(FILE *stream) +{ + char buf[100]; + while (fgets(buf, sizeof(buf), stream)) { + buf[sizeof(buf) - 1] = '\0'; + if (newline_terminated(buf, sizeof(buf))) + return; + } +} /** @@ -47,6 +66,15 @@ while (fgets(s, size, stream)) { (*line)++; s[size - 1] = '\0'; + if (!newline_terminated(s, size)) { + /* + * The line was truncated - skip rest of it to avoid + * confusing error messages. + */ + wpa_printf(MSG_INFO, "Long line in configuration file " + "truncated"); + skip_line_end(stream); + } pos = s; /* Skip white space from the beginning of line. */ @@ -105,14 +133,6 @@ wpa_config_update_psk(ssid); } - if ((ssid->key_mgmt & (WPA_KEY_MGMT_PSK | WPA_KEY_MGMT_FT_PSK | - WPA_KEY_MGMT_PSK_SHA256)) && - !ssid->psk_set) { - wpa_printf(MSG_ERROR, "Line %d: WPA-PSK accepted for key " - "management, but no PSK configured.", line); - errors++; - } - if ((ssid->group_cipher & WPA_CIPHER_CCMP) && !(ssid->pairwise_cipher & WPA_CIPHER_CCMP) && !(ssid->pairwise_cipher & WPA_CIPHER_NONE)) { @@ -131,13 +151,14 @@ { struct wpa_ssid *ssid; int errors = 0, end = 0; - char buf[256], *pos, *pos2; + char buf[2000], *pos, *pos2; wpa_printf(MSG_MSGDUMP, "Line: %d - start of a new network block", *line); ssid = os_zalloc(sizeof(*ssid)); if (ssid == NULL) return NULL; + dl_list_init(&ssid->psk_list); ssid->id = id; wpa_config_set_network_defaults(ssid); @@ -187,6 +208,61 @@ } +static struct wpa_cred * wpa_config_read_cred(FILE *f, int *line, int id) +{ + struct wpa_cred *cred; + int errors = 0, end = 0; + char buf[256], *pos, *pos2; + + wpa_printf(MSG_MSGDUMP, "Line: %d - start of a new cred block", *line); + cred = os_zalloc(sizeof(*cred)); + if (cred == NULL) + return NULL; + cred->id = id; + + while (wpa_config_get_line(buf, sizeof(buf), f, line, &pos)) { + if (os_strcmp(pos, "}") == 0) { + end = 1; + break; + } + + pos2 = os_strchr(pos, '='); + if (pos2 == NULL) { + wpa_printf(MSG_ERROR, "Line %d: Invalid cred line " + "'%s'.", *line, pos); + errors++; + continue; + } + + *pos2++ = '\0'; + if (*pos2 == '"') { + if (os_strchr(pos2 + 1, '"') == NULL) { + wpa_printf(MSG_ERROR, "Line %d: invalid " + "quotation '%s'.", *line, pos2); + errors++; + continue; + } + } + + if (wpa_config_set_cred(cred, pos, pos2, *line) < 0) + errors++; + } + + if (!end) { + wpa_printf(MSG_ERROR, "Line %d: cred block was not " + "terminated properly.", *line); + errors++; + } + + if (errors) { + wpa_config_free_cred(cred); + cred = NULL; + } + + return cred; +} + + #ifndef CONFIG_NO_CONFIG_BLOBS static struct wpa_config_blob * wpa_config_read_blob(FILE *f, int *line, const char *name) @@ -270,21 +346,36 @@ #endif /* CONFIG_NO_CONFIG_BLOBS */ -struct wpa_config * wpa_config_read(const char *name) +struct wpa_config * wpa_config_read(const char *name, struct wpa_config *cfgp) { FILE *f; - char buf[256], *pos; + char buf[512], *pos; int errors = 0, line = 0; struct wpa_ssid *ssid, *tail = NULL, *head = NULL; + struct wpa_cred *cred, *cred_tail = NULL, *cred_head = NULL; struct wpa_config *config; int id = 0; + int cred_id = 0; - config = wpa_config_alloc_empty(NULL, NULL); - if (config == NULL) + if (name == NULL) + return NULL; + if (cfgp) + config = cfgp; + else + config = wpa_config_alloc_empty(NULL, NULL); + if (config == NULL) { + wpa_printf(MSG_ERROR, "Failed to allocate config file " + "structure"); return NULL; + } + head = config->ssid; + cred_head = config->cred; + wpa_printf(MSG_DEBUG, "Reading configuration file '%s'", name); f = fopen(name, "r"); if (f == NULL) { + wpa_printf(MSG_ERROR, "Failed to open config file '%s', " + "error: %s", name, strerror(errno)); os_free(config); return NULL; } @@ -311,10 +402,26 @@ errors++; continue; } + } else if (os_strcmp(pos, "cred={") == 0) { + cred = wpa_config_read_cred(f, &line, cred_id++); + if (cred == NULL) { + wpa_printf(MSG_ERROR, "Line %d: failed to " + "parse cred block.", line); + errors++; + continue; + } + if (cred_head == NULL) { + cred_head = cred_tail = cred; + } else { + cred_tail->next = cred; + cred_tail = cred; + } #ifndef CONFIG_NO_CONFIG_BLOBS } else if (os_strncmp(pos, "blob-base64-", 12) == 0) { if (wpa_config_process_blob(config, f, &line, pos + 12) < 0) { + wpa_printf(MSG_ERROR, "Line %d: failed to " + "process blob.", line); errors++; continue; } @@ -331,6 +438,7 @@ config->ssid = head; wpa_config_debug_dump_networks(config); + config->cred = cred_head; #ifndef WPA_IGNORE_CONFIG_ERRORS if (errors) { @@ -497,6 +605,16 @@ #ifdef CONFIG_P2P + +static void write_go_p2p_dev_addr(FILE *f, struct wpa_ssid *ssid) +{ + char *value = wpa_config_get(ssid, "go_p2p_dev_addr"); + if (value == NULL) + return; + fprintf(f, "\tgo_p2p_dev_addr=%s\n", value); + os_free(value); +} + static void write_p2p_client_list(FILE *f, struct wpa_ssid *ssid) { char *value = wpa_config_get(ssid, "p2p_client_list"); @@ -505,6 +623,20 @@ fprintf(f, "\tp2p_client_list=%s\n", value); os_free(value); } + + +static void write_psk_list(FILE *f, struct wpa_ssid *ssid) +{ + struct psk_list_entry *psk; + char hex[32 * 2 + 1]; + + dl_list_for_each(psk, &ssid->psk_list, struct psk_list_entry, list) { + wpa_snprintf_hex(hex, sizeof(hex), psk->psk, sizeof(psk->psk)); + fprintf(f, "\tpsk_list=%s" MACSTR "-%s\n", + psk->p2p ? "P2P-" : "", MAC2STR(psk->addr), hex); + } +} + #endif /* CONFIG_P2P */ @@ -524,10 +656,13 @@ write_psk(f, ssid); write_proto(f, ssid); write_key_mgmt(f, ssid); + INT_DEF(bg_scan_period, DEFAULT_BG_SCAN_PERIOD); write_pairwise(f, ssid); write_group(f, ssid); write_auth_alg(f, ssid); STR(bgscan); + STR(autoscan); + STR(scan_freq); #ifdef IEEE8021X_EAPOL write_eap(f, ssid); STR(identity); @@ -541,6 +676,7 @@ STR(dh_file); STR(subject_match); STR(altsubject_match); + STR(domain_suffix_match); STR(ca_cert2); STR(ca_path2); STR(client_cert2); @@ -549,6 +685,7 @@ STR(dh_file2); STR(subject_match2); STR(altsubject_match2); + STR(domain_suffix_match2); STR(phase1); STR(phase2); STR(pcsc); @@ -576,16 +713,22 @@ INT_DEFe(fragment_size, DEFAULT_FRAGMENT_SIZE); #endif /* IEEE8021X_EAPOL */ INT(mode); - INT(proactive_key_caching); + INT(frequency); + write_int(f, "proactive_key_caching", ssid->proactive_key_caching, -1); INT(disabled); INT(peerkey); #ifdef CONFIG_IEEE80211W - INT(ieee80211w); + write_int(f, "ieee80211w", ssid->ieee80211w, + MGMT_FRAME_PROTECTION_DEFAULT); #endif /* CONFIG_IEEE80211W */ STR(id_str); #ifdef CONFIG_P2P + write_go_p2p_dev_addr(f, ssid); write_p2p_client_list(f, ssid); + write_psk_list(f, ssid); #endif /* CONFIG_P2P */ + INT(dtim_period); + INT(beacon_int); #undef STR #undef INT @@ -593,6 +736,69 @@ } +static void wpa_config_write_cred(FILE *f, struct wpa_cred *cred) +{ + size_t i; + + if (cred->priority) + fprintf(f, "\tpriority=%d\n", cred->priority); + if (cred->pcsc) + fprintf(f, "\tpcsc=%d\n", cred->pcsc); + if (cred->realm) + fprintf(f, "\trealm=\"%s\"\n", cred->realm); + if (cred->username) + fprintf(f, "\tusername=\"%s\"\n", cred->username); + if (cred->password && cred->ext_password) + fprintf(f, "\tpassword=ext:%s\n", cred->password); + else if (cred->password) + fprintf(f, "\tpassword=\"%s\"\n", cred->password); + if (cred->ca_cert) + fprintf(f, "\tca_cert=\"%s\"\n", cred->ca_cert); + if (cred->client_cert) + fprintf(f, "\tclient_cert=\"%s\"\n", cred->client_cert); + if (cred->private_key) + fprintf(f, "\tprivate_key=\"%s\"\n", cred->private_key); + if (cred->private_key_passwd) + fprintf(f, "\tprivate_key_passwd=\"%s\"\n", + cred->private_key_passwd); + if (cred->imsi) + fprintf(f, "\timsi=\"%s\"\n", cred->imsi); + if (cred->milenage) + fprintf(f, "\tmilenage=\"%s\"\n", cred->milenage); + for (i = 0; i < cred->num_domain; i++) + fprintf(f, "\tdomain=\"%s\"\n", cred->domain[i]); + if (cred->domain_suffix_match) + fprintf(f, "\tdomain_suffix_match=\"%s\"", + cred->domain_suffix_match); + if (cred->roaming_consortium_len) { + fprintf(f, "\troaming_consortium="); + for (i = 0; i < cred->roaming_consortium_len; i++) + fprintf(f, "%02x", cred->roaming_consortium[i]); + fprintf(f, "\n"); + } + if (cred->eap_method) { + const char *name; + name = eap_get_name(cred->eap_method[0].vendor, + cred->eap_method[0].method); + fprintf(f, "\teap=%s\n", name); + } + if (cred->phase1) + fprintf(f, "\tphase1=\"%s\"\n", cred->phase1); + if (cred->phase2) + fprintf(f, "\tphase2=\"%s\"\n", cred->phase2); + if (cred->excluded_ssid) { + size_t j; + for (i = 0; i < cred->num_excluded_ssid; i++) { + struct excluded_ssid *e = &cred->excluded_ssid[i]; + fprintf(f, "\texcluded_ssid="); + for (j = 0; j < e->ssid_len; j++) + fprintf(f, "%02x", e->ssid[j]); + fprintf(f, "\n"); + } + } +} + + #ifndef CONFIG_NO_CONFIG_BLOBS static int wpa_config_write_blob(FILE *f, struct wpa_config_blob *blob) { @@ -609,6 +815,23 @@ #endif /* CONFIG_NO_CONFIG_BLOBS */ +static void write_global_bin(FILE *f, const char *field, + const struct wpabuf *val) +{ + size_t i; + const u8 *pos; + + if (val == NULL) + return; + + fprintf(f, "%s=", field); + pos = wpabuf_head(val); + for (i = 0; i < wpabuf_len(val); i++) + fprintf(f, "%02X", *pos++); + fprintf(f, "\n"); +} + + static void wpa_config_write_global(FILE *f, struct wpa_config *config) { #ifdef CONFIG_CTRL_IFACE @@ -622,6 +845,9 @@ fprintf(f, "eapol_version=%d\n", config->eapol_version); if (config->ap_scan != DEFAULT_AP_SCAN) fprintf(f, "ap_scan=%d\n", config->ap_scan); + if (config->disable_scan_offload) + fprintf(f, "disable_scan_offload=%d\n", + config->disable_scan_offload); if (config->fast_reauth != DEFAULT_FAST_REAUTH) fprintf(f, "fast_reauth=%d\n", config->fast_reauth); if (config->opensc_engine_path) @@ -633,6 +859,10 @@ if (config->pkcs11_module_path) fprintf(f, "pkcs11_module_path=%s\n", config->pkcs11_module_path); + if (config->pcsc_reader) + fprintf(f, "pcsc_reader=%s\n", config->pcsc_reader); + if (config->pcsc_pin) + fprintf(f, "pcsc_pin=%s\n", config->pcsc_pin); if (config->driver_param) fprintf(f, "driver_param=%s\n", config->driver_param); if (config->dot11RSNAConfigPMKLifetime) @@ -677,6 +907,16 @@ if (config->wps_cred_processing) fprintf(f, "wps_cred_processing=%d\n", config->wps_cred_processing); + if (config->wps_vendor_ext_m1) { + int i, len = wpabuf_len(config->wps_vendor_ext_m1); + const u8 *p = wpabuf_head_u8(config->wps_vendor_ext_m1); + if (len > 0) { + fprintf(f, "wps_vendor_ext_m1="); + for (i = 0; i < len; i++) + fprintf(f, "%02x", *p++); + fprintf(f, "\n"); + } + } #endif /* CONFIG_WPS */ #ifdef CONFIG_P2P if (config->p2p_listen_reg_class) @@ -711,6 +951,27 @@ } fprintf(f, "\n"); } + if (config->p2p_no_go_freq.num) { + char *val = freq_range_list_str(&config->p2p_no_go_freq); + if (val) { + fprintf(f, "p2p_no_go_freq=%s\n", val); + os_free(val); + } + } + if (config->p2p_add_cli_chan) + fprintf(f, "p2p_add_cli_chan=%d\n", config->p2p_add_cli_chan); + if (config->p2p_go_ht40) + fprintf(f, "p2p_go_ht40=%u\n", config->p2p_go_ht40); + if (config->p2p_go_vht) + fprintf(f, "p2p_go_vht=%u\n", config->p2p_go_vht); + if (config->p2p_disabled) + fprintf(f, "p2p_disabled=%u\n", config->p2p_disabled); + if (config->p2p_no_group_iface) + fprintf(f, "p2p_no_group_iface=%u\n", + config->p2p_no_group_iface); + if (config->p2p_ignore_shared_freq) + fprintf(f, "p2p_ignore_shared_freq=%u\n", + config->p2p_ignore_shared_freq); #endif /* CONFIG_P2P */ if (config->country[0] && config->country[1]) { fprintf(f, "country=%c%c\n", @@ -731,19 +992,11 @@ fprintf(f, "max_num_sta=%u\n", config->max_num_sta); if (config->disassoc_low_ack) fprintf(f, "disassoc_low_ack=%u\n", config->disassoc_low_ack); +#ifdef CONFIG_HS20 + if (config->hs20) + fprintf(f, "hs20=1\n"); +#endif /* CONFIG_HS20 */ #ifdef CONFIG_INTERWORKING - if (config->home_realm) - fprintf(f, "home_realm=%s\n", config->home_realm); - if (config->home_username) - fprintf(f, "home_username=%s\n", config->home_username); - if (config->home_password) - fprintf(f, "home_password=%s\n", config->home_password); - if (config->home_ca_cert) - fprintf(f, "home_ca_cert=%s\n", config->home_ca_cert); - if (config->home_imsi) - fprintf(f, "home_imsi=%s\n", config->home_imsi); - if (config->home_milenage) - fprintf(f, "home_milenage=%s\n", config->home_milenage); if (config->interworking) fprintf(f, "interworking=%u\n", config->interworking); if (!is_zero_ether_addr(config->hessid)) @@ -752,6 +1005,84 @@ fprintf(f, "access_network_type=%d\n", config->access_network_type); #endif /* CONFIG_INTERWORKING */ + if (config->pbc_in_m1) + fprintf(f, "pbc_in_m1=%u\n", config->pbc_in_m1); + if (config->wps_nfc_pw_from_config) { + if (config->wps_nfc_dev_pw_id) + fprintf(f, "wps_nfc_dev_pw_id=%d\n", + config->wps_nfc_dev_pw_id); + write_global_bin(f, "wps_nfc_dh_pubkey", + config->wps_nfc_dh_pubkey); + write_global_bin(f, "wps_nfc_dh_privkey", + config->wps_nfc_dh_privkey); + write_global_bin(f, "wps_nfc_dev_pw", config->wps_nfc_dev_pw); + } + + if (config->ext_password_backend) + fprintf(f, "ext_password_backend=%s\n", + config->ext_password_backend); + if (config->p2p_go_max_inactivity != DEFAULT_P2P_GO_MAX_INACTIVITY) + fprintf(f, "p2p_go_max_inactivity=%d\n", + config->p2p_go_max_inactivity); + if (config->auto_interworking) + fprintf(f, "auto_interworking=%d\n", + config->auto_interworking); + if (config->okc) + fprintf(f, "okc=%d\n", config->okc); + if (config->pmf) + fprintf(f, "pmf=%d\n", config->pmf); + if (config->dtim_period) + fprintf(f, "dtim_period=%d\n", config->dtim_period); + if (config->beacon_int) + fprintf(f, "beacon_int=%d\n", config->beacon_int); + + if (config->sae_groups) { + int i; + fprintf(f, "sae_groups="); + for (i = 0; config->sae_groups[i] >= 0; i++) { + fprintf(f, "%s%d", i > 0 ? " " : "", + config->sae_groups[i]); + } + fprintf(f, "\n"); + } + + if (config->ap_vendor_elements) { + int i, len = wpabuf_len(config->ap_vendor_elements); + const u8 *p = wpabuf_head_u8(config->ap_vendor_elements); + if (len > 0) { + fprintf(f, "ap_vendor_elements="); + for (i = 0; i < len; i++) + fprintf(f, "%02x", *p++); + fprintf(f, "\n"); + } + } + + if (config->ignore_old_scan_res) + fprintf(f, "ignore_old_scan_res=%d\n", + config->ignore_old_scan_res); + + if (config->freq_list && config->freq_list[0]) { + int i; + fprintf(f, "freq_list="); + for (i = 0; config->freq_list[i]; i++) { + fprintf(f, "%s%u", i > 0 ? " " : "", + config->freq_list[i]); + } + fprintf(f, "\n"); + } + if (config->scan_cur_freq != DEFAULT_SCAN_CUR_FREQ) + fprintf(f, "scan_cur_freq=%d\n", config->scan_cur_freq); + + if (config->sched_scan_interval) + fprintf(f, "sched_scan_interval=%u\n", + config->sched_scan_interval); + + if (config->external_sim) + fprintf(f, "external_sim=%d\n", config->external_sim); + + if (config->tdls_external_control) + fprintf(f, "tdls_external_control=%d\n", + config->tdls_external_control); } #endif /* CONFIG_NO_CONFIG_WRITE */ @@ -762,6 +1093,7 @@ #ifndef CONFIG_NO_CONFIG_WRITE FILE *f; struct wpa_ssid *ssid; + struct wpa_cred *cred; #ifndef CONFIG_NO_CONFIG_BLOBS struct wpa_config_blob *blob; #endif /* CONFIG_NO_CONFIG_BLOBS */ @@ -777,6 +1109,14 @@ wpa_config_write_global(f, config); + for (cred = config->cred; cred; cred = cred->next) { + if (cred->temporary) + continue; + fprintf(f, "\ncred={\n"); + wpa_config_write_cred(f, cred); + fprintf(f, "}\n"); + } + for (ssid = config->ssid; ssid; ssid = ssid->next) { if (ssid->key_mgmt == WPA_KEY_MGMT_WPS || ssid->temporary) continue; /* do not save temporary networks */ diff -Nru wpa-1.0/wpa_supplicant/config.h wpa-2.1/wpa_supplicant/config.h --- wpa-1.0/wpa_supplicant/config.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/config.h 2014-02-04 11:23:35.000000000 +0000 @@ -1,15 +1,9 @@ /* * WPA Supplicant / Configuration file structures - * Copyright (c) 2003-2005, Jouni Malinen + * Copyright (c) 2003-2012, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef CONFIG_H @@ -24,14 +18,225 @@ #define DEFAULT_FAST_REAUTH 1 #define DEFAULT_P2P_GO_INTENT 7 #define DEFAULT_P2P_INTRA_BSS 1 +#define DEFAULT_P2P_GO_MAX_INACTIVITY (5 * 60) #define DEFAULT_BSS_MAX_COUNT 200 #define DEFAULT_BSS_EXPIRATION_AGE 180 #define DEFAULT_BSS_EXPIRATION_SCAN_COUNT 2 #define DEFAULT_MAX_NUM_STA 128 #define DEFAULT_ACCESS_NETWORK_TYPE 15 +#define DEFAULT_SCAN_CUR_FREQ 0 #include "config_ssid.h" #include "wps/wps.h" +#include "common/ieee802_11_common.h" + + +struct wpa_cred { + /** + * next - Next credential in the list + * + * This pointer can be used to iterate over all credentials. The head + * of this list is stored in the cred field of struct wpa_config. + */ + struct wpa_cred *next; + + /** + * id - Unique id for the credential + * + * This identifier is used as a unique identifier for each credential + * block when using the control interface. Each credential is allocated + * an id when it is being created, either when reading the + * configuration file or when a new credential is added through the + * control interface. + */ + int id; + + /** + * temporary - Whether this credential is temporary and not to be saved + */ + int temporary; + + /** + * priority - Priority group + * + * By default, all networks and credentials get the same priority group + * (0). This field can be used to give higher priority for credentials + * (and similarly in struct wpa_ssid for network blocks) to change the + * Interworking automatic networking selection behavior. The matching + * network (based on either an enabled network block or a credential) + * with the highest priority value will be selected. + */ + int priority; + + /** + * pcsc - Use PC/SC and SIM/USIM card + */ + int pcsc; + + /** + * realm - Home Realm for Interworking + */ + char *realm; + + /** + * username - Username for Interworking network selection + */ + char *username; + + /** + * password - Password for Interworking network selection + */ + char *password; + + /** + * ext_password - Whether password is a name for external storage + */ + int ext_password; + + /** + * ca_cert - CA certificate for Interworking network selection + */ + char *ca_cert; + + /** + * client_cert - File path to client certificate file (PEM/DER) + * + * This field is used with Interworking networking selection for a case + * where client certificate/private key is used for authentication + * (EAP-TLS). Full path to the file should be used since working + * directory may change when wpa_supplicant is run in the background. + * + * Alternatively, a named configuration blob can be used by setting + * this to blob://blob_name. + */ + char *client_cert; + + /** + * private_key - File path to client private key file (PEM/DER/PFX) + * + * When PKCS#12/PFX file (.p12/.pfx) is used, client_cert should be + * commented out. Both the private key and certificate will be read + * from the PKCS#12 file in this case. Full path to the file should be + * used since working directory may change when wpa_supplicant is run + * in the background. + * + * Windows certificate store can be used by leaving client_cert out and + * configuring private_key in one of the following formats: + * + * cert://substring_to_match + * + * hash://certificate_thumbprint_in_hex + * + * For example: private_key="hash://63093aa9c47f56ae88334c7b65a4" + * + * Note that when running wpa_supplicant as an application, the user + * certificate store (My user account) is used, whereas computer store + * (Computer account) is used when running wpasvc as a service. + * + * Alternatively, a named configuration blob can be used by setting + * this to blob://blob_name. + */ + char *private_key; + + /** + * private_key_passwd - Password for private key file + */ + char *private_key_passwd; + + /** + * imsi - IMSI in | | '-' | format + */ + char *imsi; + + /** + * milenage - Milenage parameters for SIM/USIM simulator in + * :: format + */ + char *milenage; + + /** + * domain_suffix_match - Constraint for server domain name + * + * If set, this FQDN is used as a suffix match requirement for the AAA + * server certificate in SubjectAltName dNSName element(s). If a + * matching dNSName is found, this constraint is met. If no dNSName + * values are present, this constraint is matched against SubjetName CN + * using same suffix match comparison. Suffix match here means that the + * host/domain name is compared one label at a time starting from the + * top-level domain and all the labels in @domain_suffix_match shall be + * included in the certificate. The certificate may include additional + * sub-level labels in addition to the required labels. + * + * For example, domain_suffix_match=example.com would match + * test.example.com but would not match test-example.com. + */ + char *domain_suffix_match; + + /** + * domain - Home service provider FQDN(s) + * + * This is used to compare against the Domain Name List to figure out + * whether the AP is operated by the Home SP. Multiple domain entries + * can be used to configure alternative FQDNs that will be considered + * home networks. + */ + char **domain; + + /** + * num_domain - Number of FQDNs in the domain array + */ + size_t num_domain; + + /** + * roaming_consortium - Roaming Consortium OI + * + * If roaming_consortium_len is non-zero, this field contains the + * Roaming Consortium OI that can be used to determine which access + * points support authentication with this credential. This is an + * alternative to the use of the realm parameter. When using Roaming + * Consortium to match the network, the EAP parameters need to be + * pre-configured with the credential since the NAI Realm information + * may not be available or fetched. + */ + u8 roaming_consortium[15]; + + /** + * roaming_consortium_len - Length of roaming_consortium + */ + size_t roaming_consortium_len; + + u8 required_roaming_consortium[15]; + size_t required_roaming_consortium_len; + + /** + * eap_method - EAP method to use + * + * Pre-configured EAP method to use with this credential or %NULL to + * indicate no EAP method is selected, i.e., the method will be + * selected automatically based on ANQP information. + */ + struct eap_method_type *eap_method; + + /** + * phase1 - Phase 1 (outer authentication) parameters + * + * Pre-configured EAP parameters or %NULL. + */ + char *phase1; + + /** + * phase2 - Phase 2 (inner authentication) parameters + * + * Pre-configured EAP parameters or %NULL. + */ + char *phase2; + + struct excluded_ssid { + u8 ssid[MAX_SSID_LEN]; + size_t ssid_len; + } *excluded_ssid; + size_t num_excluded_ssid; +}; #define CFG_CHANGED_DEVICE_NAME BIT(0) @@ -48,6 +253,8 @@ #define CFG_CHANGED_P2P_LISTEN_CHANNEL BIT(11) #define CFG_CHANGED_P2P_OPER_CHANNEL BIT(12) #define CFG_CHANGED_P2P_PREF_CHAN BIT(13) +#define CFG_CHANGED_EXT_PW_BACKEND BIT(14) +#define CFG_CHANGED_NFC_PASSWORD_TOKEN BIT(15) /** * struct wpa_config - wpa_supplicant configuration data @@ -79,6 +286,13 @@ int num_prio; /** + * cred - Head of the credential list + * + * This is the head for the list of all the configured credentials. + */ + struct wpa_cred *cred; + + /** * eapol_version - IEEE 802.1X/EAPOL version number * * wpa_supplicant is implemented based on IEEE Std 802.1X-2004 which @@ -120,6 +334,27 @@ int ap_scan; /** + * bgscan - Background scan and roaming parameters or %NULL if none + * + * This is an optional set of parameters for background scanning and + * roaming within a network (ESS). For more detailed information see + * ssid block documentation. + * + * The variable defines default bgscan behavior for all BSS station + * networks except for those which have their own bgscan configuration. + */ + char *bgscan; + + /** + * disable_scan_offload - Disable automatic offloading of scan requests + * + * By default, %wpa_supplicant tries to offload scanning if the driver + * indicates support for this (sched_scan). This configuration + * parameter can be used to disable this offloading mechanism. + */ + int disable_scan_offload; + + /** * ctrl_interface - Parameters for the control interface * * If this is specified, %wpa_supplicant will open a control interface @@ -218,6 +453,28 @@ char *pkcs11_module_path; /** + * pcsc_reader - PC/SC reader name prefix + * + * If not %NULL, PC/SC reader with a name that matches this prefix is + * initialized for SIM/USIM access. Empty string can be used to match + * the first available reader. + */ + char *pcsc_reader; + + /** + * pcsc_pin - PIN for USIM, GSM SIM, and smartcards + * + * This field is used to configure PIN for SIM/USIM for EAP-SIM and + * EAP-AKA. If left out, this will be asked through control interface. + */ + char *pcsc_pin; + + /** + * external_sim - Use external processing for SIM/USIM operations + */ + int external_sim; + + /** * driver_param - Driver interface parameters * * This text string is passed to the selected driver interface with the @@ -365,6 +622,11 @@ int p2p_intra_bss; unsigned int num_p2p_pref_chan; struct p2p_channel *p2p_pref_chan; + struct wpa_freq_range_list p2p_no_go_freq; + int p2p_add_cli_chan; + int p2p_ignore_shared_freq; + + struct wpabuf *wps_vendor_ext_m1; #define MAX_WPS_VENDOR_EXT 10 /** @@ -383,9 +645,12 @@ * state indefinitely until explicitly removed. As a P2P client, the * maximum idle time of P2P_MAX_CLIENT_IDLE seconds is enforced, i.e., * this parameter is mainly meant for GO use and for P2P client, it can - * only be used to reduce the default timeout to smaller value. + * only be used to reduce the default timeout to smaller value. A + * special value -1 can be used to configure immediate removal of the + * group for P2P client role on any disconnection after the data + * connection has been established. */ - unsigned int p2p_group_idle; + int p2p_group_idle; /** * bss_max_count - Maximum number of BSS entries to keep in memory @@ -420,11 +685,35 @@ int filter_ssids; /** + * filter_rssi - RSSI-based scan result filtering + * + * 0 = do not filter scan results + * -n = filter scan results below -n dBm + */ + int filter_rssi; + + /** * max_num_sta - Maximum number of STAs in an AP/P2P GO */ unsigned int max_num_sta; /** + * freq_list - Array of allowed scan frequencies or %NULL for all + * + * This is an optional zero-terminated array of frequencies in + * megahertz (MHz) to allow for narrowing scanning range. + */ + int *freq_list; + + /** + * scan_cur_freq - Whether to scan only the current channel + * + * If true, attempt to scan only the current channel if any other + * VIFs on this radio are already associated on a particular channel. + */ + int scan_cur_freq; + + /** * changed_parameters - Bitmap of changed parameters since last update */ unsigned int changed_parameters; @@ -458,35 +747,212 @@ u8 hessid[ETH_ALEN]; /** - * home_realm - Home Realm for Interworking + * hs20 - Hotspot 2.0 */ - char *home_realm; + int hs20; /** - * home_username - Username for Interworking network selection + * pbc_in_m1 - AP mode WPS probing workaround for PBC with Windows 7 + * + * Windows 7 uses incorrect way of figuring out AP's WPS capabilities + * by acting as a Registrar and using M1 from the AP. The config + * methods attribute in that message is supposed to indicate only the + * configuration method supported by the AP in Enrollee role, i.e., to + * add an external Registrar. For that case, PBC shall not be used and + * as such, the PushButton config method is removed from M1 by default. + * If pbc_in_m1=1 is included in the configuration file, the PushButton + * config method is left in M1 (if included in config_methods + * parameter) to allow Windows 7 to use PBC instead of PIN (e.g., from + * a label in the AP). */ - char *home_username; + int pbc_in_m1; /** - * home_password - Password for Interworking network selection + * autoscan - Automatic scan parameters or %NULL if none + * + * This is an optional set of parameters for automatic scanning + * within an interface in following format: + * : */ - char *home_password; + char *autoscan; /** - * home_ca_cert - CA certificate for Interworking network selection + * wps_nfc_pw_from_config - NFC Device Password was read from config + * + * This parameter can be determined whether the NFC Device Password was + * included in the configuration (1) or generated dynamically (0). Only + * the former case is re-written back to the configuration file. */ - char *home_ca_cert; + int wps_nfc_pw_from_config; /** - * home_imsi - IMSI in | | '-' | format + * wps_nfc_dev_pw_id - NFC Device Password ID for password token */ - char *home_imsi; + int wps_nfc_dev_pw_id; /** - * home_milenage - Milenage parameters for SIM/USIM simulator in - * :: format + * wps_nfc_dh_pubkey - NFC DH Public Key for password token + */ + struct wpabuf *wps_nfc_dh_pubkey; + + /** + * wps_nfc_dh_privkey - NFC DH Private Key for password token + */ + struct wpabuf *wps_nfc_dh_privkey; + + /** + * wps_nfc_dev_pw - NFC Device Password for password token */ - char *home_milenage; + struct wpabuf *wps_nfc_dev_pw; + + /** + * ext_password_backend - External password backend or %NULL if none + * + * format: [:] + */ + char *ext_password_backend; + + /* + * p2p_go_max_inactivity - Timeout in seconds to detect STA inactivity + * + * This timeout value is used in P2P GO mode to clean up + * inactive stations. + * By default: 300 seconds. + */ + int p2p_go_max_inactivity; + + struct hostapd_wmm_ac_params wmm_ac_params[4]; + + /** + * auto_interworking - Whether to use network selection automatically + * + * 0 = do not automatically go through Interworking network selection + * (i.e., require explicit interworking_select command for this) + * 1 = perform Interworking network selection if one or more + * credentials have been configured and scan did not find a + * matching network block + */ + int auto_interworking; + + /** + * p2p_go_ht40 - Default mode for HT40 enable when operating as GO. + * + * This will take effect for p2p_group_add, p2p_connect, and p2p_invite. + * Note that regulatory constraints and driver capabilities are + * consulted anyway, so setting it to 1 can't do real harm. + * By default: 0 (disabled) + */ + int p2p_go_ht40; + + /** + * p2p_go_vht - Default mode for VHT enable when operating as GO + * + * This will take effect for p2p_group_add, p2p_connect, and p2p_invite. + * Note that regulatory constraints and driver capabilities are + * consulted anyway, so setting it to 1 can't do real harm. + * By default: 0 (disabled) + */ + int p2p_go_vht; + + /** + * p2p_disabled - Whether P2P operations are disabled for this interface + */ + int p2p_disabled; + + /** + * p2p_no_group_iface - Whether group interfaces can be used + * + * By default, wpa_supplicant will create a separate interface for P2P + * group operations if the driver supports this. This functionality can + * be disabled by setting this parameter to 1. In that case, the same + * interface that was used for the P2P management operations is used + * also for the group operation. + */ + int p2p_no_group_iface; + + /** + * okc - Whether to enable opportunistic key caching by default + * + * By default, OKC is disabled unless enabled by the per-network + * proactive_key_caching=1 parameter. okc=1 can be used to change this + * default behavior. + */ + int okc; + + /** + * pmf - Whether to enable/require PMF by default + * + * By default, PMF is disabled unless enabled by the per-network + * ieee80211w=1 or ieee80211w=2 parameter. pmf=1/2 can be used to change + * this default behavior. + */ + enum mfp_options pmf; + + /** + * sae_groups - Preference list of enabled groups for SAE + * + * By default (if this parameter is not set), the mandatory group 19 + * (ECC group defined over a 256-bit prime order field) is preferred, + * but other groups are also enabled. If this parameter is set, the + * groups will be tried in the indicated order. + */ + int *sae_groups; + + /** + * dtim_period - Default DTIM period in Beacon intervals + * + * This parameter can be used to set the default value for network + * blocks that do not specify dtim_period. + */ + int dtim_period; + + /** + * beacon_int - Default Beacon interval in TU + * + * This parameter can be used to set the default value for network + * blocks that do not specify beacon_int. + */ + int beacon_int; + + /** + * ap_vendor_elements: Vendor specific elements for Beacon/ProbeResp + * + * This parameter can be used to define additional vendor specific + * elements for Beacon and Probe Response frames in AP/P2P GO mode. The + * format for these element(s) is a hexdump of the raw information + * elements (id+len+payload for one or more elements). + */ + struct wpabuf *ap_vendor_elements; + + /** + * ignore_old_scan_res - Ignore scan results older than request + * + * The driver may have a cache of scan results that makes it return + * information that is older than our scan trigger. This parameter can + * be used to configure such old information to be ignored instead of + * allowing it to update the internal BSS table. + */ + int ignore_old_scan_res; + + /** + * sched_scan_interval - schedule scan interval + */ + unsigned int sched_scan_interval; + + /** + * tdls_external_control - External control for TDLS setup requests + * + * Enable TDLS mode where external programs are given the control + * to specify the TDLS link to get established to the driver. The + * driver requests the TDLS setup to the supplicant only for the + * specified TDLS peers. + */ + int tdls_external_control; + + u8 ip_addr_go[4]; + u8 ip_addr_mask[4]; + u8 ip_addr_start[4]; + u8 ip_addr_end[4]; }; @@ -518,6 +984,14 @@ struct wpa_config_blob *blob); void wpa_config_free_blob(struct wpa_config_blob *blob); int wpa_config_remove_blob(struct wpa_config *config, const char *name); +void wpa_config_flush_blobs(struct wpa_config *config); + +struct wpa_cred * wpa_config_get_cred(struct wpa_config *config, int id); +struct wpa_cred * wpa_config_add_cred(struct wpa_config *config); +int wpa_config_remove_cred(struct wpa_config *config, int id); +void wpa_config_free_cred(struct wpa_cred *cred); +int wpa_config_set_cred(struct wpa_cred *cred, const char *var, + const char *value, int line); struct wpa_config * wpa_config_alloc_empty(const char *ctrl_interface, const char *driver_param); @@ -538,6 +1012,7 @@ * wpa_config_read - Read and parse configuration database * @name: Name of the configuration (e.g., path and file name for the * configuration file) + * @cfgp: Pointer to previously allocated configuration data or %NULL if none * Returns: Pointer to allocated configuration data or %NULL on failure * * This function reads configuration data, parses its contents, and allocates @@ -546,7 +1021,7 @@ * * Each configuration backend needs to implement this function. */ -struct wpa_config * wpa_config_read(const char *name); +struct wpa_config * wpa_config_read(const char *name, struct wpa_config *cfgp); /** * wpa_config_write - Write or update configuration data diff -Nru wpa-1.0/wpa_supplicant/config_none.c wpa-2.1/wpa_supplicant/config_none.c --- wpa-1.0/wpa_supplicant/config_none.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/config_none.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * WPA Supplicant / Configuration backend: empty starting point * Copyright (c) 2003-2005, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. * * This file implements dummy example of a configuration backend. None of the * functions are actually implemented so this can be used as a simple @@ -23,11 +17,16 @@ #include "base64.h" -struct wpa_config * wpa_config_read(const char *name) +struct wpa_config * wpa_config_read(const char *name, struct wpa_config *cfgp) { struct wpa_config *config; - config = wpa_config_alloc_empty(NULL, NULL); + if (name == NULL) + return NULL; + if (cfgp) + config = cfgp; + else + config = wpa_config_alloc_empty(NULL, NULL); if (config == NULL) return NULL; /* TODO: fill in configuration data */ diff -Nru wpa-1.0/wpa_supplicant/config_ssid.h wpa-2.1/wpa_supplicant/config_ssid.h --- wpa-1.0/wpa_supplicant/config_ssid.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/config_ssid.h 2014-02-04 11:23:35.000000000 +0000 @@ -1,21 +1,16 @@ /* * WPA Supplicant / Network configuration structures - * Copyright (c) 2003-2008, Jouni Malinen + * Copyright (c) 2003-2013, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef CONFIG_SSID_H #define CONFIG_SSID_H #include "common/defs.h" +#include "utils/list.h" #include "eap_peer/eap_config.h" #define MAX_SSID_LEN 32 @@ -31,6 +26,21 @@ WPA_CIPHER_WEP104 | WPA_CIPHER_WEP40) #define DEFAULT_FRAGMENT_SIZE 1398 +#define DEFAULT_BG_SCAN_PERIOD -1 +#define DEFAULT_DISABLE_HT 0 +#define DEFAULT_DISABLE_HT40 0 +#define DEFAULT_DISABLE_SGI 0 +#define DEFAULT_DISABLE_MAX_AMSDU -1 /* no change */ +#define DEFAULT_AMPDU_FACTOR -1 /* no change */ +#define DEFAULT_AMPDU_DENSITY -1 /* no change */ + +struct psk_list_entry { + struct dl_list list; + u8 addr[ETH_ALEN]; + u8 psk[32]; + u8 p2p; +}; + /** * struct wpa_ssid - Network configuration data * @@ -121,6 +131,11 @@ int bssid_set; /** + * go_p2p_dev_addr - GO's P2P Device Address or all zeros if not set + */ + u8 go_p2p_dev_addr[ETH_ALEN]; + + /** * psk - WPA pre-shared key (256 bits) */ u8 psk[32]; @@ -140,6 +155,14 @@ char *passphrase; /** + * ext_psk - PSK/passphrase name in external storage + * + * If this is set, PSK/passphrase will be fetched from external storage + * when requesting association with the network. + */ + char *ext_psk; + + /** * pairwise_cipher - Bitfield of allowed pairwise ciphers, WPA_CIPHER_* */ int pairwise_cipher; @@ -157,6 +180,12 @@ int key_mgmt; /** + * bg_scan_period - Background scan period in seconds, 0 to disable, or + * -1 to indicate no change to default driver configuration + */ + int bg_scan_period; + + /** * proto - Bitfield of allowed protocols, WPA_PROTO_* */ int proto; @@ -213,13 +242,18 @@ * * This field can be used to enable proactive key caching which is also * known as opportunistic PMKSA caching for WPA2. This is disabled (0) - * by default. Enable by setting this to 1. + * by default unless default value is changed with the global okc=1 + * parameter. Enable by setting this to 1. * * Proactive key caching is used to make supplicant assume that the APs * are using the same PMK and generate PMKSA cache entries without * doing RSN pre-authentication. This requires support from the AP side * and is normally used with wireless switches that co-locate the * authenticator. + * + * Internally, special value -1 is used to indicate that the parameter + * was not specified in the configuration (i.e., default behavior is + * followed). */ int proactive_key_caching; @@ -281,12 +315,13 @@ * 4 = P2P Group Formation (used internally; not in configuration * files) * - * Note: IBSS can only be used with key_mgmt NONE (plaintext and - * static WEP) and key_mgmt=WPA-NONE (fixed group key TKIP/CCMP). In - * addition, ap_scan has to be set to 2 for IBSS. WPA-None requires - * following network block options: proto=WPA, key_mgmt=WPA-NONE, - * pairwise=NONE, group=TKIP (or CCMP, but not both), and psk must also - * be set (either directly or using ASCII passphrase). + * Note: IBSS can only be used with key_mgmt NONE (plaintext and static + * WEP) and WPA-PSK (with proto=RSN). In addition, key_mgmt=WPA-NONE + * (fixed group key TKIP/CCMP) is available for backwards compatibility, + * but its use is deprecated. WPA-None requires following network block + * options: proto=WPA, key_mgmt=WPA-NONE, pairwise=NONE, group=TKIP (or + * CCMP, but not both), and psk must also be set (either directly or + * using ASCII passphrase). */ enum wpas_mode { WPAS_MODE_INFRA = 0, @@ -308,6 +343,14 @@ int disabled; /** + * disabled_for_connect - Whether this network was temporarily disabled + * + * This flag is used to reenable all the temporarily disabled networks + * after either the success or failure of a WPS connection. + */ + int disabled_for_connect; + + /** * peerkey - Whether PeerKey handshake for direct links is allowed * * This is only used when both RSN/WPA2 and IEEE 802.11e (QoS) are @@ -333,6 +376,12 @@ * * This value is used to configure policy for management frame * protection (IEEE 802.11w). 0 = disabled, 1 = optional, 2 = required. + * This is disabled by default unless the default value has been changed + * with the global pmf=1/2 parameter. + * + * Internally, special value 3 is used to indicate that the parameter + * was not specified in the configuration (i.e., default behavior is + * followed). */ enum mfp_options ieee80211w; #endif /* CONFIG_IEEE80211W */ @@ -349,6 +398,10 @@ */ int frequency; + int ht40; + + int vht; + /** * wpa_ptk_rekey - Maximum lifetime for PTK in seconds * @@ -377,6 +430,20 @@ char *bgscan; /** + * ignore_broadcast_ssid - Hide SSID in AP mode + * + * Send empty SSID in beacons and ignore probe request frames that do + * not specify full SSID, i.e., require stations to know SSID. + * default: disabled (0) + * 1 = send empty (length=0) SSID in beacon and ignore probe request + * for broadcast SSID + * 2 = clear SSID (ASCII 0), but keep the original length (this may be + * required with some clients that do not support empty SSID) and + * ignore probe requests for broadcast SSID + */ + int ignore_broadcast_ssid; + + /** * freq_list - Array of allowed frequencies or %NULL for all * * This is an optional zero-terminated array of frequencies in @@ -400,6 +467,15 @@ */ size_t num_p2p_clients; +#ifndef P2P_MAX_STORED_CLIENTS +#define P2P_MAX_STORED_CLIENTS 100 +#endif /* P2P_MAX_STORED_CLIENTS */ + + /** + * psk_list - Per-client PSKs (struct psk_list_entry) + */ + struct dl_list psk_list; + /** * p2p_group - Network generated as a P2P group (used internally) */ @@ -422,6 +498,129 @@ * WPS or similar so that they may be exported. */ int export_keys; + +#ifdef CONFIG_HT_OVERRIDES + /** + * disable_ht - Disable HT (IEEE 802.11n) for this network + * + * By default, use it if it is available, but this can be configured + * to 1 to have it disabled. + */ + int disable_ht; + + /** + * disable_ht40 - Disable HT40 for this network + * + * By default, use it if it is available, but this can be configured + * to 1 to have it disabled. + */ + int disable_ht40; + + /** + * disable_sgi - Disable SGI (Short Guard Interval) for this network + * + * By default, use it if it is available, but this can be configured + * to 1 to have it disabled. + */ + int disable_sgi; + + /** + * disable_max_amsdu - Disable MAX A-MSDU + * + * A-MDSU will be 3839 bytes when disabled, or 7935 + * when enabled (assuming it is otherwise supported) + * -1 (default) means do not apply any settings to the kernel. + */ + int disable_max_amsdu; + + /** + * ampdu_factor - Maximum A-MPDU Length Exponent + * + * Value: 0-3, see 7.3.2.56.3 in IEEE Std 802.11n-2009. + */ + int ampdu_factor; + + /** + * ampdu_density - Minimum A-MPDU Start Spacing + * + * Value: 0-7, see 7.3.2.56.3 in IEEE Std 802.11n-2009. + */ + int ampdu_density; + + /** + * ht_mcs - Allowed HT-MCS rates, in ASCII hex: ffff0000... + * + * By default (empty string): Use whatever the OS has configured. + */ + char *ht_mcs; +#endif /* CONFIG_HT_OVERRIDES */ + +#ifdef CONFIG_VHT_OVERRIDES + /** + * disable_vht - Disable VHT (IEEE 802.11ac) for this network + * + * By default, use it if it is available, but this can be configured + * to 1 to have it disabled. + */ + int disable_vht; + + /** + * vht_capa - VHT capabilities to use + */ + unsigned int vht_capa; + + /** + * vht_capa_mask - mask for VHT capabilities + */ + unsigned int vht_capa_mask; + + int vht_rx_mcs_nss_1, vht_rx_mcs_nss_2, + vht_rx_mcs_nss_3, vht_rx_mcs_nss_4, + vht_rx_mcs_nss_5, vht_rx_mcs_nss_6, + vht_rx_mcs_nss_7, vht_rx_mcs_nss_8; + int vht_tx_mcs_nss_1, vht_tx_mcs_nss_2, + vht_tx_mcs_nss_3, vht_tx_mcs_nss_4, + vht_tx_mcs_nss_5, vht_tx_mcs_nss_6, + vht_tx_mcs_nss_7, vht_tx_mcs_nss_8; +#endif /* CONFIG_VHT_OVERRIDES */ + + /** + * ap_max_inactivity - Timeout in seconds to detect STA's inactivity + * + * This timeout value is used in AP mode to clean up inactive stations. + * By default: 300 seconds. + */ + int ap_max_inactivity; + + /** + * dtim_period - DTIM period in Beacon intervals + * By default: 2 + */ + int dtim_period; + + /** + * beacon_int - Beacon interval (default: 100 TU) + */ + int beacon_int; + + /** + * auth_failures - Number of consecutive authentication failures + */ + unsigned int auth_failures; + + /** + * disabled_until - Network block disabled until this time if non-zero + */ + struct os_reltime disabled_until; + + /** + * parent_cred - Pointer to parent wpa_cred entry + * + * This pointer can be used to delete temporary networks when a wpa_cred + * that was used to create them is removed. This pointer should not be + * dereferences since it may not be updated in all cases. + */ + void *parent_cred; }; #endif /* CONFIG_SSID_H */ diff -Nru wpa-1.0/wpa_supplicant/config_winreg.c wpa-2.1/wpa_supplicant/config_winreg.c --- wpa-1.0/wpa_supplicant/config_winreg.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/config_winreg.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * WPA Supplicant / Configuration backend: Windows registry * Copyright (c) 2003-2008, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. * * This file implements a configuration backend for Windows registry. All the * configuration information is stored in the registry and the format for @@ -208,6 +202,7 @@ static int wpa_config_read_global(struct wpa_config *config, HKEY hk) { int errors = 0; + int val; wpa_config_read_reg_dword(hk, TEXT("ap_scan"), &config->ap_scan); wpa_config_read_reg_dword(hk, TEXT("fast_reauth"), @@ -277,6 +272,10 @@ wpa_config_read_reg_dword(hk, TEXT("disassoc_low_ack"), (int *) &config->disassoc_low_ack); + wpa_config_read_reg_dword(hk, TEXT("okc"), &config->okc); + wpa_config_read_reg_dword(hk, TEXT("pmf"), &val); + config->pmf = val; + return errors ? -1 : 0; } @@ -303,6 +302,7 @@ RegCloseKey(nhk); return NULL; } + dl_list_init(&ssid->psk_list); ssid->id = id; wpa_config_set_network_defaults(ssid); @@ -350,15 +350,6 @@ wpa_config_update_psk(ssid); } - if ((ssid->key_mgmt & (WPA_KEY_MGMT_PSK | WPA_KEY_MGMT_FT_PSK | - WPA_KEY_MGMT_PSK_SHA256)) && - !ssid->psk_set) { - wpa_printf(MSG_ERROR, "WPA-PSK accepted for key management, " - "but no PSK configured for network '" TSTR "'.", - netw); - errors++; - } - if ((ssid->group_cipher & WPA_CIPHER_CCMP) && !(ssid->pairwise_cipher & WPA_CIPHER_CCMP) && !(ssid->pairwise_cipher & WPA_CIPHER_NONE)) { @@ -444,7 +435,7 @@ } -struct wpa_config * wpa_config_read(const char *name) +struct wpa_config * wpa_config_read(const char *name, struct wpa_config *cfgp) { TCHAR buf[256]; int errors = 0; @@ -452,7 +443,12 @@ HKEY hk; LONG ret; - config = wpa_config_alloc_empty(NULL, NULL); + if (name == NULL) + return NULL; + if (cfgp) + config = cfgp; + else + config = wpa_config_alloc_empty(NULL, NULL); if (config == NULL) return NULL; wpa_printf(MSG_DEBUG, "Reading configuration profile '%s'", name); @@ -624,6 +620,12 @@ wpa_config_write_reg_dword(hk, TEXT("disassoc_low_ack"), config->disassoc_low_ack, 0); + wpa_config_write_reg_dword(hk, TEXT("okc"), config->okc, 0); + wpa_config_write_reg_dword(hk, TEXT("pmf"), config->pmf, 0); + + wpa_config_write_reg_dword(hk, TEXT("external_sim"), + config->external_sim, 0); + return 0; } @@ -919,11 +921,13 @@ INT_DEFe(fragment_size, DEFAULT_FRAGMENT_SIZE); #endif /* IEEE8021X_EAPOL */ INT(mode); - INT(proactive_key_caching); + write_int(netw, "proactive_key_caching", ssid->proactive_key_caching, + -1); INT(disabled); INT(peerkey); #ifdef CONFIG_IEEE80211W - INT(ieee80211w); + write_int(netw, "ieee80211w", ssid->ieee80211w, + MGMT_FRAME_PROTECTION_DEFAULT); #endif /* CONFIG_IEEE80211W */ STR(id_str); diff -Nru wpa-1.0/wpa_supplicant/ctrl_iface.c wpa-2.1/wpa_supplicant/ctrl_iface.c --- wpa-1.0/wpa_supplicant/ctrl_iface.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/ctrl_iface.c 2014-02-04 11:23:35.000000000 +0000 @@ -1,23 +1,19 @@ /* * WPA Supplicant / Control interface (shared code for all backends) - * Copyright (c) 2004-2010, Jouni Malinen + * Copyright (c) 2004-2014, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "utils/includes.h" #include "utils/common.h" #include "utils/eloop.h" +#include "utils/uuid.h" #include "common/version.h" #include "common/ieee802_11_defs.h" +#include "common/ieee802_11_common.h" #include "common/wpa_ctrl.h" #include "eap_peer/eap.h" #include "eapol_supp/eapol_supp_sm.h" @@ -34,15 +30,16 @@ #include "ap.h" #include "p2p_supplicant.h" #include "p2p/p2p.h" +#include "hs20_supplicant.h" +#include "wifi_display.h" #include "notify.h" #include "bss.h" #include "scan.h" #include "ctrl_iface.h" #include "interworking.h" #include "blacklist.h" -#include "wpas_glue.h" - -extern struct wpa_driver_ops *wpa_drivers[]; +#include "autoscan.h" +#include "wnm_sta.h" static int wpa_supplicant_global_iface_list(struct wpa_global *global, char *buf, int len); @@ -50,6 +47,266 @@ char *buf, int len); +static int pno_start(struct wpa_supplicant *wpa_s) +{ + int ret, interval; + size_t i, num_ssid; + struct wpa_ssid *ssid; + struct wpa_driver_scan_params params; + + if (wpa_s->pno || wpa_s->pno_sched_pending) + return 0; + + if ((wpa_s->wpa_state > WPA_SCANNING) && + (wpa_s->wpa_state <= WPA_COMPLETED)) { + wpa_printf(MSG_ERROR, "PNO: In assoc process"); + return -EAGAIN; + } + + if (wpa_s->wpa_state == WPA_SCANNING) { + wpa_supplicant_cancel_scan(wpa_s); + if (wpa_s->sched_scanning) { + wpa_printf(MSG_DEBUG, "Schedule PNO on completion of " + "ongoing sched scan"); + wpa_supplicant_cancel_sched_scan(wpa_s); + wpa_s->pno_sched_pending = 1; + return 0; + } + } + + os_memset(¶ms, 0, sizeof(params)); + + num_ssid = 0; + ssid = wpa_s->conf->ssid; + while (ssid) { + if (!wpas_network_disabled(wpa_s, ssid)) + num_ssid++; + ssid = ssid->next; + } + if (num_ssid > WPAS_MAX_SCAN_SSIDS) { + wpa_printf(MSG_DEBUG, "PNO: Use only the first %u SSIDs from " + "%u", WPAS_MAX_SCAN_SSIDS, (unsigned int) num_ssid); + num_ssid = WPAS_MAX_SCAN_SSIDS; + } + + if (num_ssid == 0) { + wpa_printf(MSG_DEBUG, "PNO: No configured SSIDs"); + return -1; + } + + params.filter_ssids = os_malloc(sizeof(struct wpa_driver_scan_filter) * + num_ssid); + if (params.filter_ssids == NULL) + return -1; + i = 0; + ssid = wpa_s->conf->ssid; + while (ssid) { + if (!wpas_network_disabled(wpa_s, ssid)) { + params.ssids[i].ssid = ssid->ssid; + params.ssids[i].ssid_len = ssid->ssid_len; + params.num_ssids++; + os_memcpy(params.filter_ssids[i].ssid, ssid->ssid, + ssid->ssid_len); + params.filter_ssids[i].ssid_len = ssid->ssid_len; + params.num_filter_ssids++; + i++; + if (i == num_ssid) + break; + } + ssid = ssid->next; + } + + if (wpa_s->conf->filter_rssi) + params.filter_rssi = wpa_s->conf->filter_rssi; + + interval = wpa_s->conf->sched_scan_interval ? + wpa_s->conf->sched_scan_interval : 10; + + ret = wpa_supplicant_start_sched_scan(wpa_s, ¶ms, interval); + os_free(params.filter_ssids); + if (ret == 0) + wpa_s->pno = 1; + return ret; +} + + +static int pno_stop(struct wpa_supplicant *wpa_s) +{ + int ret = 0; + + if (wpa_s->pno || wpa_s->sched_scanning) { + wpa_s->pno = 0; + ret = wpa_supplicant_stop_sched_scan(wpa_s); + } + + wpa_s->pno_sched_pending = 0; + + if (wpa_s->wpa_state == WPA_SCANNING) + wpa_supplicant_req_scan(wpa_s, 0, 0); + + return ret; +} + + +static int set_bssid_filter(struct wpa_supplicant *wpa_s, char *val) +{ + char *pos; + u8 addr[ETH_ALEN], *filter = NULL, *n; + size_t count = 0; + + pos = val; + while (pos) { + if (*pos == '\0') + break; + if (hwaddr_aton(pos, addr)) { + os_free(filter); + return -1; + } + n = os_realloc_array(filter, count + 1, ETH_ALEN); + if (n == NULL) { + os_free(filter); + return -1; + } + filter = n; + os_memcpy(filter + count * ETH_ALEN, addr, ETH_ALEN); + count++; + + pos = os_strchr(pos, ' '); + if (pos) + pos++; + } + + wpa_hexdump(MSG_DEBUG, "bssid_filter", filter, count * ETH_ALEN); + os_free(wpa_s->bssid_filter); + wpa_s->bssid_filter = filter; + wpa_s->bssid_filter_count = count; + + return 0; +} + + +static int set_disallow_aps(struct wpa_supplicant *wpa_s, char *val) +{ + char *pos; + u8 addr[ETH_ALEN], *bssid = NULL, *n; + struct wpa_ssid_value *ssid = NULL, *ns; + size_t count = 0, ssid_count = 0; + struct wpa_ssid *c; + + /* + * disallow_list ::= | | | "" + * SSID_SPEC ::= ssid + * BSSID_SPEC ::= bssid + */ + + pos = val; + while (pos) { + if (*pos == '\0') + break; + if (os_strncmp(pos, "bssid ", 6) == 0) { + int res; + pos += 6; + res = hwaddr_aton2(pos, addr); + if (res < 0) { + os_free(ssid); + os_free(bssid); + wpa_printf(MSG_DEBUG, "Invalid disallow_aps " + "BSSID value '%s'", pos); + return -1; + } + pos += res; + n = os_realloc_array(bssid, count + 1, ETH_ALEN); + if (n == NULL) { + os_free(ssid); + os_free(bssid); + return -1; + } + bssid = n; + os_memcpy(bssid + count * ETH_ALEN, addr, ETH_ALEN); + count++; + } else if (os_strncmp(pos, "ssid ", 5) == 0) { + char *end; + pos += 5; + + end = pos; + while (*end) { + if (*end == '\0' || *end == ' ') + break; + end++; + } + + ns = os_realloc_array(ssid, ssid_count + 1, + sizeof(struct wpa_ssid_value)); + if (ns == NULL) { + os_free(ssid); + os_free(bssid); + return -1; + } + ssid = ns; + + if ((end - pos) & 0x01 || end - pos > 2 * 32 || + hexstr2bin(pos, ssid[ssid_count].ssid, + (end - pos) / 2) < 0) { + os_free(ssid); + os_free(bssid); + wpa_printf(MSG_DEBUG, "Invalid disallow_aps " + "SSID value '%s'", pos); + return -1; + } + ssid[ssid_count].ssid_len = (end - pos) / 2; + wpa_hexdump_ascii(MSG_DEBUG, "disallow_aps SSID", + ssid[ssid_count].ssid, + ssid[ssid_count].ssid_len); + ssid_count++; + pos = end; + } else { + wpa_printf(MSG_DEBUG, "Unexpected disallow_aps value " + "'%s'", pos); + os_free(ssid); + os_free(bssid); + return -1; + } + + pos = os_strchr(pos, ' '); + if (pos) + pos++; + } + + wpa_hexdump(MSG_DEBUG, "disallow_aps_bssid", bssid, count * ETH_ALEN); + os_free(wpa_s->disallow_aps_bssid); + wpa_s->disallow_aps_bssid = bssid; + wpa_s->disallow_aps_bssid_count = count; + + wpa_printf(MSG_DEBUG, "disallow_aps_ssid_count %d", (int) ssid_count); + os_free(wpa_s->disallow_aps_ssid); + wpa_s->disallow_aps_ssid = ssid; + wpa_s->disallow_aps_ssid_count = ssid_count; + + if (!wpa_s->current_ssid || wpa_s->wpa_state < WPA_AUTHENTICATING) + return 0; + + c = wpa_s->current_ssid; + if (c->mode != WPAS_MODE_INFRA && c->mode != WPAS_MODE_IBSS) + return 0; + + if (!disallowed_bssid(wpa_s, wpa_s->bssid) && + !disallowed_ssid(wpa_s, c->ssid, c->ssid_len)) + return 0; + + wpa_printf(MSG_DEBUG, "Disconnect and try to find another network " + "because current AP was marked disallowed"); + +#ifdef CONFIG_SME + wpa_s->sme.prev_bssid_set = 0; +#endif /* CONFIG_SME */ + wpa_s->reassociate = 1; + wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_DEAUTH_LEAVING); + wpa_supplicant_req_scan(wpa_s, 0, 0); + + return 0; +} + + static int wpa_supplicant_ctrl_iface_set(struct wpa_supplicant *wpa_s, char *cmd) { @@ -107,17 +364,21 @@ wps_testing_dummy_cred = atoi(value); wpa_printf(MSG_DEBUG, "WPS: Testing - dummy_cred=%d", wps_testing_dummy_cred); + } else if (os_strcasecmp(cmd, "wps_corrupt_pkhash") == 0) { + wps_corrupt_pkhash = atoi(value); + wpa_printf(MSG_DEBUG, "WPS: Testing - wps_corrupt_pkhash=%d", + wps_corrupt_pkhash); #endif /* CONFIG_WPS_TESTING */ } else if (os_strcasecmp(cmd, "ampdu") == 0) { if (wpa_drv_ampdu(wpa_s, atoi(value)) < 0) ret = -1; +#ifdef CONFIG_TDLS #ifdef CONFIG_TDLS_TESTING } else if (os_strcasecmp(cmd, "tdls_testing") == 0) { extern unsigned int tdls_testing; tdls_testing = strtol(value, NULL, 0); wpa_printf(MSG_DEBUG, "TDLS: tdls_testing=0x%x", tdls_testing); #endif /* CONFIG_TDLS_TESTING */ -#ifdef CONFIG_TDLS } else if (os_strcasecmp(cmd, "tdls_disabled") == 0) { int disabled = atoi(value); wpa_printf(MSG_DEBUG, "TDLS: tdls_disabled=%d", disabled); @@ -128,6 +389,65 @@ ret = -1; wpa_tdls_enable(wpa_s->wpa, !disabled); #endif /* CONFIG_TDLS */ + } else if (os_strcasecmp(cmd, "pno") == 0) { + if (atoi(value)) + ret = pno_start(wpa_s); + else + ret = pno_stop(wpa_s); + } else if (os_strcasecmp(cmd, "radio_disabled") == 0) { + int disabled = atoi(value); + if (wpa_drv_radio_disable(wpa_s, disabled) < 0) + ret = -1; + else if (disabled) + wpa_supplicant_set_state(wpa_s, WPA_INACTIVE); + } else if (os_strcasecmp(cmd, "uapsd") == 0) { + if (os_strcmp(value, "disable") == 0) + wpa_s->set_sta_uapsd = 0; + else { + int be, bk, vi, vo; + char *pos; + /* format: BE,BK,VI,VO;max SP Length */ + be = atoi(value); + pos = os_strchr(value, ','); + if (pos == NULL) + return -1; + pos++; + bk = atoi(pos); + pos = os_strchr(pos, ','); + if (pos == NULL) + return -1; + pos++; + vi = atoi(pos); + pos = os_strchr(pos, ','); + if (pos == NULL) + return -1; + pos++; + vo = atoi(pos); + /* ignore max SP Length for now */ + + wpa_s->set_sta_uapsd = 1; + wpa_s->sta_uapsd = 0; + if (be) + wpa_s->sta_uapsd |= BIT(0); + if (bk) + wpa_s->sta_uapsd |= BIT(1); + if (vi) + wpa_s->sta_uapsd |= BIT(2); + if (vo) + wpa_s->sta_uapsd |= BIT(3); + } + } else if (os_strcasecmp(cmd, "ps") == 0) { + ret = wpa_drv_set_p2p_powersave(wpa_s, atoi(value), -1, -1); +#ifdef CONFIG_WIFI_DISPLAY + } else if (os_strcasecmp(cmd, "wifi_display") == 0) { + wifi_display_enable(wpa_s->global, !!atoi(value)); +#endif /* CONFIG_WIFI_DISPLAY */ + } else if (os_strcasecmp(cmd, "bssid_filter") == 0) { + ret = set_bssid_filter(wpa_s, value); + } else if (os_strcasecmp(cmd, "disallow_aps") == 0) { + ret = set_disallow_aps(wpa_s, value); + } else if (os_strcasecmp(cmd, "no_keep_alive") == 0) { + wpa_s->no_keep_alive = !!atoi(value); } else { value[-1] = '='; ret = wpa_config_process_global(wpa_s->conf, cmd, -1); @@ -142,18 +462,38 @@ static int wpa_supplicant_ctrl_iface_get(struct wpa_supplicant *wpa_s, char *cmd, char *buf, size_t buflen) { - int res; + int res = -1; wpa_printf(MSG_DEBUG, "CTRL_IFACE GET '%s'", cmd); if (os_strcmp(cmd, "version") == 0) { res = os_snprintf(buf, buflen, "%s", VERSION_STR); + } else if (os_strcasecmp(cmd, "country") == 0) { + if (wpa_s->conf->country[0] && wpa_s->conf->country[1]) + res = os_snprintf(buf, buflen, "%c%c", + wpa_s->conf->country[0], + wpa_s->conf->country[1]); +#ifdef CONFIG_WIFI_DISPLAY + } else if (os_strcasecmp(cmd, "wifi_display") == 0) { + res = os_snprintf(buf, buflen, "%d", + wpa_s->global->wifi_display); if (res < 0 || (unsigned int) res >= buflen) return -1; return res; +#endif /* CONFIG_WIFI_DISPLAY */ +#ifdef CONFIG_TESTING_GET_GTK + } else if (os_strcmp(cmd, "gtk") == 0) { + if (wpa_s->last_gtk_len == 0) + return -1; + res = wpa_snprintf_hex(buf, buflen, wpa_s->last_gtk, + wpa_s->last_gtk_len); + return res; +#endif /* CONFIG_TESTING_GET_GTK */ } - return -1; + if (res < 0 || (unsigned int) res >= buflen) + return -1; + return res; } @@ -242,13 +582,16 @@ wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_SETUP " MACSTR, MAC2STR(peer)); - ret = wpa_tdls_reneg(wpa_s->wpa, peer); - if (ret) { - if (wpa_tdls_is_external_setup(wpa_s->wpa)) - ret = wpa_tdls_start(wpa_s->wpa, peer); - else - ret = wpa_drv_tdls_oper(wpa_s, TDLS_SETUP, peer); - } + if ((wpa_s->conf->tdls_external_control) && + wpa_tdls_is_external_setup(wpa_s->wpa)) + return wpa_drv_tdls_oper(wpa_s, TDLS_SETUP, peer); + + wpa_tdls_remove(wpa_s->wpa, peer); + + if (wpa_tdls_is_external_setup(wpa_s->wpa)) + ret = wpa_tdls_start(wpa_s->wpa, peer); + else + ret = wpa_drv_tdls_oper(wpa_s, TDLS_SETUP, peer); return ret; } @@ -258,6 +601,7 @@ struct wpa_supplicant *wpa_s, char *addr) { u8 peer[ETH_ALEN]; + int ret; if (hwaddr_aton(addr, peer)) { wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_TEARDOWN: invalid " @@ -268,8 +612,18 @@ wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_TEARDOWN " MACSTR, MAC2STR(peer)); - return wpa_tdls_teardown_link(wpa_s->wpa, peer, - WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED); + if ((wpa_s->conf->tdls_external_control) && + wpa_tdls_is_external_setup(wpa_s->wpa)) + return wpa_drv_tdls_oper(wpa_s, TDLS_TEARDOWN, peer); + + if (wpa_tdls_is_external_setup(wpa_s->wpa)) + ret = wpa_tdls_teardown_link( + wpa_s->wpa, peer, + WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED); + else + ret = wpa_drv_tdls_oper(wpa_s, TDLS_TEARDOWN, peer); + + return ret; } #endif /* CONFIG_TDLS */ @@ -365,9 +719,21 @@ } #ifdef CONFIG_AP - if (wpa_s->ap_iface) + if (wpa_s->ap_iface) { + int timeout = 0; + char *pos; + + if (pin) { + pos = os_strchr(pin, ' '); + if (pos) { + *pos++ = '\0'; + timeout = atoi(pos); + } + } + return wpa_supplicant_ap_wps_pin(wpa_s, _bssid, pin, - buf, buflen); + buf, buflen, timeout); + } #endif /* CONFIG_AP */ if (pin) { @@ -439,99 +805,526 @@ } -#ifdef CONFIG_WPS_OOB -static int wpa_supplicant_ctrl_iface_wps_oob(struct wpa_supplicant *wpa_s, +#ifdef CONFIG_WPS_NFC + +static int wpa_supplicant_ctrl_iface_wps_nfc(struct wpa_supplicant *wpa_s, char *cmd) { - char *path, *method, *name; - - path = os_strchr(cmd, ' '); - if (path == NULL) - return -1; - *path++ = '\0'; + u8 bssid[ETH_ALEN], *_bssid = bssid; - method = os_strchr(path, ' '); - if (method == NULL) + if (cmd == NULL || cmd[0] == '\0') + _bssid = NULL; + else if (hwaddr_aton(cmd, bssid)) return -1; - *method++ = '\0'; - - name = os_strchr(method, ' '); - if (name != NULL) - *name++ = '\0'; - return wpas_wps_start_oob(wpa_s, cmd, path, method, name); + return wpas_wps_start_nfc(wpa_s, NULL, _bssid, NULL, 0, 0, NULL, NULL, + 0, 0); } -#endif /* CONFIG_WPS_OOB */ -static int wpa_supplicant_ctrl_iface_wps_reg(struct wpa_supplicant *wpa_s, - char *cmd) +static int wpa_supplicant_ctrl_iface_wps_nfc_config_token( + struct wpa_supplicant *wpa_s, char *cmd, char *reply, size_t max_len) { - u8 bssid[ETH_ALEN]; - char *pin; - char *new_ssid; - char *new_auth; - char *new_encr; - char *new_key; - struct wps_new_ap_settings ap; + int ndef; + struct wpabuf *buf; + int res; + char *pos; - pin = os_strchr(cmd, ' '); - if (pin == NULL) + pos = os_strchr(cmd, ' '); + if (pos) + *pos++ = '\0'; + if (os_strcmp(cmd, "WPS") == 0) + ndef = 0; + else if (os_strcmp(cmd, "NDEF") == 0) + ndef = 1; + else return -1; - *pin++ = '\0'; - if (hwaddr_aton(cmd, bssid)) { - wpa_printf(MSG_DEBUG, "CTRL_IFACE WPS_REG: invalid BSSID '%s'", - cmd); + buf = wpas_wps_nfc_config_token(wpa_s, ndef, pos); + if (buf == NULL) return -1; - } - new_ssid = os_strchr(pin, ' '); - if (new_ssid == NULL) - return wpas_wps_start_reg(wpa_s, bssid, pin, NULL); - *new_ssid++ = '\0'; + res = wpa_snprintf_hex_uppercase(reply, max_len, wpabuf_head(buf), + wpabuf_len(buf)); + reply[res++] = '\n'; + reply[res] = '\0'; - new_auth = os_strchr(new_ssid, ' '); - if (new_auth == NULL) - return -1; - *new_auth++ = '\0'; + wpabuf_free(buf); - new_encr = os_strchr(new_auth, ' '); - if (new_encr == NULL) + return res; +} + + +static int wpa_supplicant_ctrl_iface_wps_nfc_token( + struct wpa_supplicant *wpa_s, char *cmd, char *reply, size_t max_len) +{ + int ndef; + struct wpabuf *buf; + int res; + + if (os_strcmp(cmd, "WPS") == 0) + ndef = 0; + else if (os_strcmp(cmd, "NDEF") == 0) + ndef = 1; + else return -1; - *new_encr++ = '\0'; - new_key = os_strchr(new_encr, ' '); - if (new_key == NULL) + buf = wpas_wps_nfc_token(wpa_s, ndef); + if (buf == NULL) return -1; - *new_key++ = '\0'; - os_memset(&ap, 0, sizeof(ap)); - ap.ssid_hex = new_ssid; - ap.auth = new_auth; - ap.encr = new_encr; - ap.key_hex = new_key; - return wpas_wps_start_reg(wpa_s, bssid, pin, &ap); + res = wpa_snprintf_hex_uppercase(reply, max_len, wpabuf_head(buf), + wpabuf_len(buf)); + reply[res++] = '\n'; + reply[res] = '\0'; + + wpabuf_free(buf); + + return res; } -#ifdef CONFIG_AP -static int wpa_supplicant_ctrl_iface_wps_ap_pin(struct wpa_supplicant *wpa_s, - char *cmd, char *buf, - size_t buflen) +static int wpa_supplicant_ctrl_iface_wps_nfc_tag_read( + struct wpa_supplicant *wpa_s, char *pos) { - int timeout = 300; - char *pos; - const char *pin_txt; + size_t len; + struct wpabuf *buf; + int ret; + char *freq; + int forced_freq = 0; - if (!wpa_s->ap_iface) + freq = strstr(pos, " freq="); + if (freq) { + *freq = '\0'; + freq += 6; + forced_freq = atoi(freq); + } + + len = os_strlen(pos); + if (len & 0x01) return -1; + len /= 2; - pos = os_strchr(cmd, ' '); - if (pos) - *pos++ = '\0'; + buf = wpabuf_alloc(len); + if (buf == NULL) + return -1; + if (hexstr2bin(pos, wpabuf_put(buf, len), len) < 0) { + wpabuf_free(buf); + return -1; + } - if (os_strcmp(cmd, "disable") == 0) { + ret = wpas_wps_nfc_tag_read(wpa_s, buf, forced_freq); + wpabuf_free(buf); + + return ret; +} + + +static int wpas_ctrl_nfc_get_handover_req_wps(struct wpa_supplicant *wpa_s, + char *reply, size_t max_len, + int ndef) +{ + struct wpabuf *buf; + int res; + + buf = wpas_wps_nfc_handover_req(wpa_s, ndef); + if (buf == NULL) + return -1; + + res = wpa_snprintf_hex_uppercase(reply, max_len, wpabuf_head(buf), + wpabuf_len(buf)); + reply[res++] = '\n'; + reply[res] = '\0'; + + wpabuf_free(buf); + + return res; +} + + +static int wpas_ctrl_nfc_get_handover_req_p2p(struct wpa_supplicant *wpa_s, + char *reply, size_t max_len, + int ndef) +{ + struct wpabuf *buf; + int res; + + buf = wpas_p2p_nfc_handover_req(wpa_s, ndef); + if (buf == NULL) { + wpa_printf(MSG_DEBUG, "P2P: Could not generate NFC handover request"); + return -1; + } + + res = wpa_snprintf_hex_uppercase(reply, max_len, wpabuf_head(buf), + wpabuf_len(buf)); + reply[res++] = '\n'; + reply[res] = '\0'; + + wpabuf_free(buf); + + return res; +} + + +static int wpas_ctrl_nfc_get_handover_req(struct wpa_supplicant *wpa_s, + char *cmd, char *reply, + size_t max_len) +{ + char *pos; + int ndef; + + pos = os_strchr(cmd, ' '); + if (pos == NULL) + return -1; + *pos++ = '\0'; + + if (os_strcmp(cmd, "WPS") == 0) + ndef = 0; + else if (os_strcmp(cmd, "NDEF") == 0) + ndef = 1; + else + return -1; + + if (os_strcmp(pos, "WPS") == 0 || os_strcmp(pos, "WPS-CR") == 0) { + if (!ndef) + return -1; + return wpas_ctrl_nfc_get_handover_req_wps( + wpa_s, reply, max_len, ndef); + } + + if (os_strcmp(pos, "P2P-CR") == 0) { + return wpas_ctrl_nfc_get_handover_req_p2p( + wpa_s, reply, max_len, ndef); + } + + return -1; +} + + +static int wpas_ctrl_nfc_get_handover_sel_wps(struct wpa_supplicant *wpa_s, + char *reply, size_t max_len, + int ndef, int cr, char *uuid) +{ + struct wpabuf *buf; + int res; + + buf = wpas_wps_nfc_handover_sel(wpa_s, ndef, cr, uuid); + if (buf == NULL) + return -1; + + res = wpa_snprintf_hex_uppercase(reply, max_len, wpabuf_head(buf), + wpabuf_len(buf)); + reply[res++] = '\n'; + reply[res] = '\0'; + + wpabuf_free(buf); + + return res; +} + + +static int wpas_ctrl_nfc_get_handover_sel_p2p(struct wpa_supplicant *wpa_s, + char *reply, size_t max_len, + int ndef, int tag) +{ + struct wpabuf *buf; + int res; + + buf = wpas_p2p_nfc_handover_sel(wpa_s, ndef, tag); + if (buf == NULL) + return -1; + + res = wpa_snprintf_hex_uppercase(reply, max_len, wpabuf_head(buf), + wpabuf_len(buf)); + reply[res++] = '\n'; + reply[res] = '\0'; + + wpabuf_free(buf); + + return res; +} + + +static int wpas_ctrl_nfc_get_handover_sel(struct wpa_supplicant *wpa_s, + char *cmd, char *reply, + size_t max_len) +{ + char *pos, *pos2; + int ndef; + + pos = os_strchr(cmd, ' '); + if (pos == NULL) + return -1; + *pos++ = '\0'; + + if (os_strcmp(cmd, "WPS") == 0) + ndef = 0; + else if (os_strcmp(cmd, "NDEF") == 0) + ndef = 1; + else + return -1; + + pos2 = os_strchr(pos, ' '); + if (pos2) + *pos2++ = '\0'; + if (os_strcmp(pos, "WPS") == 0 || os_strcmp(pos, "WPS-CR") == 0) { + if (!ndef) + return -1; + return wpas_ctrl_nfc_get_handover_sel_wps( + wpa_s, reply, max_len, ndef, + os_strcmp(pos, "WPS-CR") == 0, pos2); + } + + if (os_strcmp(pos, "P2P-CR") == 0) { + return wpas_ctrl_nfc_get_handover_sel_p2p( + wpa_s, reply, max_len, ndef, 0); + } + + if (os_strcmp(pos, "P2P-CR-TAG") == 0) { + return wpas_ctrl_nfc_get_handover_sel_p2p( + wpa_s, reply, max_len, ndef, 1); + } + + return -1; +} + + +static int wpas_ctrl_nfc_rx_handover_req(struct wpa_supplicant *wpa_s, + char *cmd, char *reply, + size_t max_len) +{ + size_t len; + struct wpabuf *buf; + int ret; + + len = os_strlen(cmd); + if (len & 0x01) + return -1; + len /= 2; + + buf = wpabuf_alloc(len); + if (buf == NULL) + return -1; + if (hexstr2bin(cmd, wpabuf_put(buf, len), len) < 0) { + wpabuf_free(buf); + return -1; + } + + ret = wpas_wps_nfc_rx_handover_req(wpa_s, buf); + wpabuf_free(buf); + + return ret; +} + + +static int wpas_ctrl_nfc_rx_handover_sel(struct wpa_supplicant *wpa_s, + char *cmd) +{ + size_t len; + struct wpabuf *buf; + int ret; + + len = os_strlen(cmd); + if (len & 0x01) + return -1; + len /= 2; + + buf = wpabuf_alloc(len); + if (buf == NULL) + return -1; + if (hexstr2bin(cmd, wpabuf_put(buf, len), len) < 0) { + wpabuf_free(buf); + return -1; + } + + ret = wpas_wps_nfc_rx_handover_sel(wpa_s, buf); + wpabuf_free(buf); + + return ret; +} + + +static int wpas_ctrl_nfc_report_handover(struct wpa_supplicant *wpa_s, + char *cmd) +{ + size_t len; + struct wpabuf *req, *sel; + int ret; + char *pos, *role, *type, *pos2; + char *freq; + int forced_freq = 0; + + freq = strstr(cmd, " freq="); + if (freq) { + *freq = '\0'; + freq += 6; + forced_freq = atoi(freq); + } + + role = cmd; + pos = os_strchr(role, ' '); + if (pos == NULL) { + wpa_printf(MSG_DEBUG, "NFC: Missing type in handover report"); + return -1; + } + *pos++ = '\0'; + + type = pos; + pos = os_strchr(type, ' '); + if (pos == NULL) { + wpa_printf(MSG_DEBUG, "NFC: Missing request message in handover report"); + return -1; + } + *pos++ = '\0'; + + pos2 = os_strchr(pos, ' '); + if (pos2 == NULL) { + wpa_printf(MSG_DEBUG, "NFC: Missing select message in handover report"); + return -1; + } + *pos2++ = '\0'; + + len = os_strlen(pos); + if (len & 0x01) { + wpa_printf(MSG_DEBUG, "NFC: Invalid request message length in handover report"); + return -1; + } + len /= 2; + + req = wpabuf_alloc(len); + if (req == NULL) { + wpa_printf(MSG_DEBUG, "NFC: Failed to allocate memory for request message"); + return -1; + } + if (hexstr2bin(pos, wpabuf_put(req, len), len) < 0) { + wpa_printf(MSG_DEBUG, "NFC: Invalid request message hexdump in handover report"); + wpabuf_free(req); + return -1; + } + + len = os_strlen(pos2); + if (len & 0x01) { + wpa_printf(MSG_DEBUG, "NFC: Invalid select message length in handover report"); + wpabuf_free(req); + return -1; + } + len /= 2; + + sel = wpabuf_alloc(len); + if (sel == NULL) { + wpa_printf(MSG_DEBUG, "NFC: Failed to allocate memory for select message"); + wpabuf_free(req); + return -1; + } + if (hexstr2bin(pos2, wpabuf_put(sel, len), len) < 0) { + wpa_printf(MSG_DEBUG, "NFC: Invalid select message hexdump in handover report"); + wpabuf_free(req); + wpabuf_free(sel); + return -1; + } + + wpa_printf(MSG_DEBUG, "NFC: Connection handover reported - role=%s type=%s req_len=%d sel_len=%d", + role, type, (int) wpabuf_len(req), (int) wpabuf_len(sel)); + + if (os_strcmp(role, "INIT") == 0 && os_strcmp(type, "WPS") == 0) { + ret = wpas_wps_nfc_report_handover(wpa_s, req, sel); + } else if (os_strcmp(role, "RESP") == 0 && os_strcmp(type, "WPS") == 0) + { + ret = wpas_ap_wps_nfc_report_handover(wpa_s, req, sel); + if (ret < 0) + ret = wpas_er_wps_nfc_report_handover(wpa_s, req, sel); + } else if (os_strcmp(role, "INIT") == 0 && os_strcmp(type, "P2P") == 0) + { + ret = wpas_p2p_nfc_report_handover(wpa_s, 1, req, sel, 0); + } else if (os_strcmp(role, "RESP") == 0 && os_strcmp(type, "P2P") == 0) + { + ret = wpas_p2p_nfc_report_handover(wpa_s, 0, req, sel, + forced_freq); + } else { + wpa_printf(MSG_DEBUG, "NFC: Unsupported connection handover " + "reported: role=%s type=%s", role, type); + ret = -1; + } + wpabuf_free(req); + wpabuf_free(sel); + + if (ret) + wpa_printf(MSG_DEBUG, "NFC: Failed to process reported handover messages"); + + return ret; +} + +#endif /* CONFIG_WPS_NFC */ + + +static int wpa_supplicant_ctrl_iface_wps_reg(struct wpa_supplicant *wpa_s, + char *cmd) +{ + u8 bssid[ETH_ALEN]; + char *pin; + char *new_ssid; + char *new_auth; + char *new_encr; + char *new_key; + struct wps_new_ap_settings ap; + + pin = os_strchr(cmd, ' '); + if (pin == NULL) + return -1; + *pin++ = '\0'; + + if (hwaddr_aton(cmd, bssid)) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE WPS_REG: invalid BSSID '%s'", + cmd); + return -1; + } + + new_ssid = os_strchr(pin, ' '); + if (new_ssid == NULL) + return wpas_wps_start_reg(wpa_s, bssid, pin, NULL); + *new_ssid++ = '\0'; + + new_auth = os_strchr(new_ssid, ' '); + if (new_auth == NULL) + return -1; + *new_auth++ = '\0'; + + new_encr = os_strchr(new_auth, ' '); + if (new_encr == NULL) + return -1; + *new_encr++ = '\0'; + + new_key = os_strchr(new_encr, ' '); + if (new_key == NULL) + return -1; + *new_key++ = '\0'; + + os_memset(&ap, 0, sizeof(ap)); + ap.ssid_hex = new_ssid; + ap.auth = new_auth; + ap.encr = new_encr; + ap.key_hex = new_key; + return wpas_wps_start_reg(wpa_s, bssid, pin, &ap); +} + + +#ifdef CONFIG_AP +static int wpa_supplicant_ctrl_iface_wps_ap_pin(struct wpa_supplicant *wpa_s, + char *cmd, char *buf, + size_t buflen) +{ + int timeout = 300; + char *pos; + const char *pin_txt; + + if (!wpa_s->ap_iface) + return -1; + + pos = os_strchr(cmd, ' '); + if (pos) + *pos++ = '\0'; + + if (os_strcmp(cmd, "disable") == 0) { wpas_wps_ap_pin_disable(wpa_s); return os_snprintf(buf, buflen, "OK\n"); } @@ -660,14 +1453,51 @@ ap.key_hex = new_key; return wpas_wps_er_config(wpa_s, cmd, pin, &ap); } -#endif /* CONFIG_WPS_ER */ - -#endif /* CONFIG_WPS */ -#ifdef CONFIG_IBSS_RSN -static int wpa_supplicant_ctrl_iface_ibss_rsn( - struct wpa_supplicant *wpa_s, char *addr) +#ifdef CONFIG_WPS_NFC +static int wpa_supplicant_ctrl_iface_wps_er_nfc_config_token( + struct wpa_supplicant *wpa_s, char *cmd, char *reply, size_t max_len) +{ + int ndef; + struct wpabuf *buf; + int res; + char *uuid; + + uuid = os_strchr(cmd, ' '); + if (uuid == NULL) + return -1; + *uuid++ = '\0'; + + if (os_strcmp(cmd, "WPS") == 0) + ndef = 0; + else if (os_strcmp(cmd, "NDEF") == 0) + ndef = 1; + else + return -1; + + buf = wpas_wps_er_nfc_config_token(wpa_s, ndef, uuid); + if (buf == NULL) + return -1; + + res = wpa_snprintf_hex_uppercase(reply, max_len, wpabuf_head(buf), + wpabuf_len(buf)); + reply[res++] = '\n'; + reply[res] = '\0'; + + wpabuf_free(buf); + + return res; +} +#endif /* CONFIG_WPS_NFC */ +#endif /* CONFIG_WPS_ER */ + +#endif /* CONFIG_WPS */ + + +#ifdef CONFIG_IBSS_RSN +static int wpa_supplicant_ctrl_iface_ibss_rsn( + struct wpa_supplicant *wpa_s, char *addr) { u8 peer[ETH_ALEN]; @@ -685,78 +1515,6 @@ #endif /* CONFIG_IBSS_RSN */ -int wpa_supplicant_ctrl_iface_ctrl_rsp_handle(struct wpa_supplicant *wpa_s, - struct wpa_ssid *ssid, - const char *field, - const char *value) -{ -#ifdef IEEE8021X_EAPOL - struct eap_peer_config *eap = &ssid->eap; - - wpa_printf(MSG_DEBUG, "CTRL_IFACE: response handle field=%s", field); - wpa_hexdump_ascii_key(MSG_DEBUG, "CTRL_IFACE: response value", - (const u8 *) value, os_strlen(value)); - - switch (wpa_supplicant_ctrl_req_from_string(field)) { - case WPA_CTRL_REQ_EAP_IDENTITY: - os_free(eap->identity); - eap->identity = (u8 *) os_strdup(value); - eap->identity_len = os_strlen(value); - eap->pending_req_identity = 0; - if (ssid == wpa_s->current_ssid) - wpa_s->reassociate = 1; - break; - case WPA_CTRL_REQ_EAP_PASSWORD: - os_free(eap->password); - eap->password = (u8 *) os_strdup(value); - eap->password_len = os_strlen(value); - eap->pending_req_password = 0; - if (ssid == wpa_s->current_ssid) - wpa_s->reassociate = 1; - break; - case WPA_CTRL_REQ_EAP_NEW_PASSWORD: - os_free(eap->new_password); - eap->new_password = (u8 *) os_strdup(value); - eap->new_password_len = os_strlen(value); - eap->pending_req_new_password = 0; - if (ssid == wpa_s->current_ssid) - wpa_s->reassociate = 1; - break; - case WPA_CTRL_REQ_EAP_PIN: - os_free(eap->pin); - eap->pin = os_strdup(value); - eap->pending_req_pin = 0; - if (ssid == wpa_s->current_ssid) - wpa_s->reassociate = 1; - break; - case WPA_CTRL_REQ_EAP_OTP: - os_free(eap->otp); - eap->otp = (u8 *) os_strdup(value); - eap->otp_len = os_strlen(value); - os_free(eap->pending_req_otp); - eap->pending_req_otp = NULL; - eap->pending_req_otp_len = 0; - break; - case WPA_CTRL_REQ_EAP_PASSPHRASE: - os_free(eap->private_key_passwd); - eap->private_key_passwd = (u8 *) os_strdup(value); - eap->pending_req_passphrase = 0; - if (ssid == wpa_s->current_ssid) - wpa_s->reassociate = 1; - break; - default: - wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown field '%s'", field); - return -1; - } - - return 0; -#else /* IEEE8021X_EAPOL */ - wpa_printf(MSG_DEBUG, "CTRL_IFACE: IEEE 802.1X not included"); - return -1; -#endif /* IEEE8021X_EAPOL */ -} - - static int wpa_supplicant_ctrl_iface_ctrl_rsp(struct wpa_supplicant *wpa_s, char *rsp) { @@ -800,9 +1558,12 @@ char *buf, size_t buflen) { char *pos, *end, tmp[30]; - int res, verbose, ret; + int res, verbose, wps, ret; + if (os_strcmp(params, "-DRIVER") == 0) + return wpa_drv_status(wpa_s, buf, buflen); verbose = os_strcmp(params, "-VERBOSE") == 0; + wps = os_strcmp(params, "-WPS") == 0; pos = buf; end = buf + buflen; if (wpa_s->wpa_state >= WPA_ASSOCIATED) { @@ -831,6 +1592,17 @@ return pos - buf; pos += ret; + if (wps && ssid->passphrase && + wpa_key_mgmt_wpa_psk(ssid->key_mgmt) && + (ssid->mode == WPAS_MODE_AP || + ssid->mode == WPAS_MODE_P2P_GO)) { + ret = os_snprintf(pos, end - pos, + "passphrase=%s\n", + ssid->passphrase); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + } if (ssid->id_str) { ret = os_snprintf(pos, end - pos, "id_str=%s\n", @@ -880,6 +1652,19 @@ #endif /* CONFIG_AP */ pos += wpa_sm_get_status(wpa_s->wpa, pos, end - pos, verbose); } +#ifdef CONFIG_SAE + if (wpa_s->wpa_state >= WPA_ASSOCIATED && +#ifdef CONFIG_AP + !wpa_s->ap_iface && +#endif /* CONFIG_AP */ + wpa_s->sme.sae.state == SAE_ACCEPTED) { + ret = os_snprintf(pos, end - pos, "sae_group=%d\n", + wpa_s->sme.sae.group); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + } +#endif /* CONFIG_SAE */ ret = os_snprintf(pos, end - pos, "wpa_state=%s\n", wpa_supplicant_state_txt(wpa_s->wpa_state)); if (ret < 0 || ret >= end - pos) @@ -910,6 +1695,60 @@ return pos - buf; pos += ret; +#ifdef CONFIG_HS20 + if (wpa_s->current_bss && + wpa_bss_get_vendor_ie(wpa_s->current_bss, HS20_IE_VENDOR_TYPE) && + wpa_s->wpa_proto == WPA_PROTO_RSN && + wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt)) { + ret = os_snprintf(pos, end - pos, "hs20=1\n"); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + } + + if (wpa_s->current_ssid) { + struct wpa_cred *cred; + char *type; + + for (cred = wpa_s->conf->cred; cred; cred = cred->next) { + size_t i; + + if (wpa_s->current_ssid->parent_cred != cred) + continue; + + for (i = 0; cred->domain && i < cred->num_domain; i++) { + ret = os_snprintf(pos, end - pos, + "home_sp=%s\n", + cred->domain[i]); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + } + + if (wpa_s->current_bss == NULL || + wpa_s->current_bss->anqp == NULL) + res = -1; + else + res = interworking_home_sp_cred( + wpa_s, cred, + wpa_s->current_bss->anqp->domain_name); + if (res > 0) + type = "home"; + else if (res == 0) + type = "roaming"; + else + type = "unknown"; + + ret = os_snprintf(pos, end - pos, "sp_type=%s\n", type); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + + break; + } + } +#endif /* CONFIG_HS20 */ + if (wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt) || wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA) { res = eapol_sm_get_status(wpa_s->eapol, pos, end - pos, @@ -922,6 +1761,37 @@ if (res >= 0) pos += res; +#ifdef CONFIG_WPS + { + char uuid_str[100]; + uuid_bin2str(wpa_s->wps->uuid, uuid_str, sizeof(uuid_str)); + ret = os_snprintf(pos, end - pos, "uuid=%s\n", uuid_str); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + } +#endif /* CONFIG_WPS */ + +#ifdef ANDROID + wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_STATE_CHANGE + "id=%d state=%d BSSID=" MACSTR " SSID=%s", + wpa_s->current_ssid ? wpa_s->current_ssid->id : -1, + wpa_s->wpa_state, + MAC2STR(wpa_s->bssid), + wpa_s->current_ssid && wpa_s->current_ssid->ssid ? + wpa_ssid_txt(wpa_s->current_ssid->ssid, + wpa_s->current_ssid->ssid_len) : ""); + if (wpa_s->wpa_state == WPA_COMPLETED) { + struct wpa_ssid *ssid = wpa_s->current_ssid; + wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_CONNECTED + "- connection to " MACSTR + " completed %s [id=%d id_str=%s]", + MAC2STR(wpa_s->bssid), "(auth)", + ssid ? ssid->id : -1, + ssid && ssid->id_str ? ssid->id_str : ""); + } +#endif /* ANDROID */ + return pos - buf; } @@ -1013,9 +1883,6 @@ } -extern int wpa_debug_level; -extern int wpa_debug_timestamp; - static const char * debug_level_str(int level) { switch (level) { @@ -1138,10 +2005,12 @@ if (ret < 0 || ret >= end - pos) return pos - buf; pos += ret; - ret = os_snprintf(pos, end - pos, "\t%s%s%s", + ret = os_snprintf(pos, end - pos, "\t%s%s%s%s", ssid == wpa_s->current_ssid ? "[CURRENT]" : "", ssid->disabled ? "[DISABLED]" : "", + ssid->disabled_until.sec ? + "[TEMP-DISABLED]" : "", ssid->disabled == 2 ? "[P2P-PERSISTENT]" : ""); if (ret < 0 || ret >= end - pos) @@ -1161,47 +2030,15 @@ static char * wpa_supplicant_cipher_txt(char *pos, char *end, int cipher) { - int first = 1, ret; + int ret; ret = os_snprintf(pos, end - pos, "-"); if (ret < 0 || ret >= end - pos) return pos; pos += ret; - if (cipher & WPA_CIPHER_NONE) { - ret = os_snprintf(pos, end - pos, "%sNONE", first ? "" : "+"); - if (ret < 0 || ret >= end - pos) - return pos; - pos += ret; - first = 0; - } - if (cipher & WPA_CIPHER_WEP40) { - ret = os_snprintf(pos, end - pos, "%sWEP40", first ? "" : "+"); - if (ret < 0 || ret >= end - pos) - return pos; - pos += ret; - first = 0; - } - if (cipher & WPA_CIPHER_WEP104) { - ret = os_snprintf(pos, end - pos, "%sWEP104", - first ? "" : "+"); - if (ret < 0 || ret >= end - pos) - return pos; - pos += ret; - first = 0; - } - if (cipher & WPA_CIPHER_TKIP) { - ret = os_snprintf(pos, end - pos, "%sTKIP", first ? "" : "+"); - if (ret < 0 || ret >= end - pos) - return pos; - pos += ret; - first = 0; - } - if (cipher & WPA_CIPHER_CCMP) { - ret = os_snprintf(pos, end - pos, "%sCCMP", first ? "" : "+"); - if (ret < 0 || ret >= end - pos) - return pos; - pos += ret; - first = 0; - } + ret = wpa_write_ciphers(pos, end, cipher, "+"); + if (ret < 0) + return pos; + pos += ret; return pos; } @@ -1356,6 +2193,8 @@ const u8 *ie, *ie2, *p2p; p2p = wpa_bss_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE); + if (!p2p) + p2p = wpa_bss_get_vendor_ie_beacon(bss, P2P_IE_VENDOR_TYPE); if (p2p && bss->ssid_len == P2P_WILDCARD_SSID_LEN && os_memcmp(bss->ssid, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN) == 0) @@ -1400,6 +2239,14 @@ return -1; pos += ret; } +#ifdef CONFIG_HS20 + if (wpa_bss_get_vendor_ie(bss, HS20_IE_VENDOR_TYPE) && ie2) { + ret = os_snprintf(pos, end - pos, "[HS20]"); + if (ret < 0 || ret >= end - pos) + return -1; + pos += ret; + } +#endif /* CONFIG_HS20 */ ret = os_snprintf(pos, end - pos, "\t%s", wpa_ssid_txt(bss->ssid, bss->ssid_len)); @@ -1501,6 +2348,11 @@ "ENABLE_NETWORK with persistent P2P group"); return -1; } + + if (os_strstr(cmd, " no-connect")) { + ssid->disabled = 0; + return 0; + } } wpa_supplicant_enable_network(wpa_s, ssid); @@ -1570,18 +2422,14 @@ { int id; struct wpa_ssid *ssid; + int was_disabled; /* cmd: "" or "all" */ if (os_strcmp(cmd, "all") == 0) { wpa_printf(MSG_DEBUG, "CTRL_IFACE: REMOVE_NETWORK all"); - ssid = wpa_s->conf->ssid; - while (ssid) { - struct wpa_ssid *remove_ssid = ssid; - id = ssid->id; - ssid = ssid->next; - wpas_notify_network_removed(wpa_s, remove_ssid); - wpa_config_remove_network(wpa_s->conf, id); - } + if (wpa_s->sched_scanning) + wpa_supplicant_cancel_sched_scan(wpa_s); + eapol_sm_invalidate_cached_session(wpa_s->eapol); if (wpa_s->current_ssid) { #ifdef CONFIG_SME @@ -1589,8 +2437,16 @@ #endif /* CONFIG_SME */ wpa_sm_set_config(wpa_s->wpa, NULL); eapol_sm_notify_config(wpa_s->eapol, NULL, NULL); - wpa_supplicant_disassociate(wpa_s, - WLAN_REASON_DEAUTH_LEAVING); + wpa_supplicant_deauthenticate( + wpa_s, WLAN_REASON_DEAUTH_LEAVING); + } + ssid = wpa_s->conf->ssid; + while (ssid) { + struct wpa_ssid *remove_ssid = ssid; + id = ssid->id; + ssid = ssid->next; + wpas_notify_network_removed(wpa_s, remove_ssid); + wpa_config_remove_network(wpa_s->conf, id); } return 0; } @@ -1601,8 +2457,7 @@ ssid = wpa_config_get_network(wpa_s->conf, id); if (ssid) wpas_notify_network_removed(wpa_s, ssid); - if (ssid == NULL || - wpa_config_remove_network(wpa_s->conf, id) < 0) { + if (ssid == NULL) { wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find network " "id=%d", id); return -1; @@ -1623,7 +2478,23 @@ wpa_sm_set_config(wpa_s->wpa, NULL); eapol_sm_notify_config(wpa_s->eapol, NULL, NULL); - wpa_supplicant_disassociate(wpa_s, WLAN_REASON_DEAUTH_LEAVING); + wpa_supplicant_deauthenticate(wpa_s, + WLAN_REASON_DEAUTH_LEAVING); + } + + was_disabled = ssid->disabled; + + if (wpa_config_remove_network(wpa_s->conf, id) < 0) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE: Not able to remove the " + "network id=%d", id); + return -1; + } + + if (!was_disabled && wpa_s->sched_scanning) { + wpa_printf(MSG_DEBUG, "Stop ongoing sched_scan to remove " + "network from filters"); + wpa_supplicant_cancel_sched_scan(wpa_s); + wpa_supplicant_req_scan(wpa_s, 0, 0); } return 0; @@ -1667,7 +2538,9 @@ return -1; } - wpa_sm_pmksa_cache_flush(wpa_s->wpa, ssid); + if (os_strcmp(name, "bssid") != 0 && + os_strcmp(name, "priority") != 0) + wpa_sm_pmksa_cache_flush(wpa_s->wpa, ssid); if (wpa_s->current_ssid == ssid || wpa_s->current_ssid == NULL) { /* @@ -1732,6 +2605,174 @@ } +static int wpa_supplicant_ctrl_iface_list_creds(struct wpa_supplicant *wpa_s, + char *buf, size_t buflen) +{ + char *pos, *end; + struct wpa_cred *cred; + int ret; + + pos = buf; + end = buf + buflen; + ret = os_snprintf(pos, end - pos, + "cred id / realm / username / domain / imsi\n"); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + + cred = wpa_s->conf->cred; + while (cred) { + ret = os_snprintf(pos, end - pos, "%d\t%s\t%s\t%s\t%s\n", + cred->id, cred->realm ? cred->realm : "", + cred->username ? cred->username : "", + cred->domain ? cred->domain[0] : "", + cred->imsi ? cred->imsi : ""); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + + cred = cred->next; + } + + return pos - buf; +} + + +static int wpa_supplicant_ctrl_iface_add_cred(struct wpa_supplicant *wpa_s, + char *buf, size_t buflen) +{ + struct wpa_cred *cred; + int ret; + + wpa_printf(MSG_DEBUG, "CTRL_IFACE: ADD_CRED"); + + cred = wpa_config_add_cred(wpa_s->conf); + if (cred == NULL) + return -1; + + ret = os_snprintf(buf, buflen, "%d\n", cred->id); + if (ret < 0 || (size_t) ret >= buflen) + return -1; + return ret; +} + + +static int wpas_ctrl_remove_cred(struct wpa_supplicant *wpa_s, + struct wpa_cred *cred) +{ + struct wpa_ssid *ssid; + char str[20]; + + if (cred == NULL || wpa_config_remove_cred(wpa_s->conf, cred->id) < 0) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find cred"); + return -1; + } + + /* Remove any network entry created based on the removed credential */ + ssid = wpa_s->conf->ssid; + while (ssid) { + if (ssid->parent_cred == cred) { + wpa_printf(MSG_DEBUG, "Remove network id %d since it " + "used the removed credential", ssid->id); + os_snprintf(str, sizeof(str), "%d", ssid->id); + ssid = ssid->next; + wpa_supplicant_ctrl_iface_remove_network(wpa_s, str); + } else + ssid = ssid->next; + } + + return 0; +} + + +static int wpa_supplicant_ctrl_iface_remove_cred(struct wpa_supplicant *wpa_s, + char *cmd) +{ + int id; + struct wpa_cred *cred, *prev; + + /* cmd: "", "all", or "sp_fqdn=" */ + if (os_strcmp(cmd, "all") == 0) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE: REMOVE_CRED all"); + cred = wpa_s->conf->cred; + while (cred) { + prev = cred; + cred = cred->next; + wpas_ctrl_remove_cred(wpa_s, prev); + } + return 0; + } + + if (os_strncmp(cmd, "sp_fqdn=", 8) == 0) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE: REMOVE_CRED SP FQDN '%s'", + cmd + 8); + cred = wpa_s->conf->cred; + while (cred) { + prev = cred; + cred = cred->next; + if (prev->domain) { + size_t i; + for (i = 0; i < prev->num_domain; i++) { + if (os_strcmp(prev->domain[i], cmd + 8) + != 0) + continue; + wpas_ctrl_remove_cred(wpa_s, prev); + break; + } + } + } + return 0; + } + + id = atoi(cmd); + wpa_printf(MSG_DEBUG, "CTRL_IFACE: REMOVE_CRED id=%d", id); + + cred = wpa_config_get_cred(wpa_s->conf, id); + return wpas_ctrl_remove_cred(wpa_s, cred); +} + + +static int wpa_supplicant_ctrl_iface_set_cred(struct wpa_supplicant *wpa_s, + char *cmd) +{ + int id; + struct wpa_cred *cred; + char *name, *value; + + /* cmd: " " */ + name = os_strchr(cmd, ' '); + if (name == NULL) + return -1; + *name++ = '\0'; + + value = os_strchr(name, ' '); + if (value == NULL) + return -1; + *value++ = '\0'; + + id = atoi(cmd); + wpa_printf(MSG_DEBUG, "CTRL_IFACE: SET_CRED id=%d name='%s'", + id, name); + wpa_hexdump_ascii_key(MSG_DEBUG, "CTRL_IFACE: value", + (u8 *) value, os_strlen(value)); + + cred = wpa_config_get_cred(wpa_s->conf, id); + if (cred == NULL) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find cred id=%d", + id); + return -1; + } + + if (wpa_config_set_cred(cred, name, value, 0) < 0) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE: Failed to set cred " + "variable '%s'", name); + return -1; + } + + return 0; +} + + #ifndef CONFIG_NO_CONFIG_WRITE static int wpa_supplicant_ctrl_iface_save_config(struct wpa_supplicant *wpa_s) { @@ -1757,6 +2798,24 @@ #endif /* CONFIG_NO_CONFIG_WRITE */ +struct cipher_info { + unsigned int capa; + const char *name; + int group_only; +}; + +static const struct cipher_info ciphers[] = { + { WPA_DRIVER_CAPA_ENC_CCMP_256, "CCMP-256", 0 }, + { WPA_DRIVER_CAPA_ENC_GCMP_256, "GCMP-256", 0 }, + { WPA_DRIVER_CAPA_ENC_CCMP, "CCMP", 0 }, + { WPA_DRIVER_CAPA_ENC_GCMP, "GCMP", 0 }, + { WPA_DRIVER_CAPA_ENC_TKIP, "TKIP", 0 }, + { WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE, "NONE", 0 }, + { WPA_DRIVER_CAPA_ENC_WEP104, "WEP104", 1 }, + { WPA_DRIVER_CAPA_ENC_WEP40, "WEP40", 1 } +}; + + static int ctrl_iface_get_capability_pairwise(int res, char *strict, struct wpa_driver_capa *capa, char *buf, size_t buflen) @@ -1764,6 +2823,7 @@ int ret, first = 1; char *pos, *end; size_t len; + unsigned int i; pos = buf; end = pos + buflen; @@ -1777,28 +2837,15 @@ return len; } - if (capa->enc & WPA_DRIVER_CAPA_ENC_CCMP) { - ret = os_snprintf(pos, end - pos, "%sCCMP", first ? "" : " "); - if (ret < 0 || ret >= end - pos) - return pos - buf; - pos += ret; - first = 0; - } - - if (capa->enc & WPA_DRIVER_CAPA_ENC_TKIP) { - ret = os_snprintf(pos, end - pos, "%sTKIP", first ? "" : " "); - if (ret < 0 || ret >= end - pos) - return pos - buf; - pos += ret; - first = 0; - } - - if (capa->key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE) { - ret = os_snprintf(pos, end - pos, "%sNONE", first ? "" : " "); - if (ret < 0 || ret >= end - pos) - return pos - buf; - pos += ret; - first = 0; + for (i = 0; i < ARRAY_SIZE(ciphers); i++) { + if (!ciphers[i].group_only && capa->enc & ciphers[i].capa) { + ret = os_snprintf(pos, end - pos, "%s%s", + first ? "" : " ", ciphers[i].name); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + first = 0; + } } return pos - buf; @@ -1812,6 +2859,7 @@ int ret, first = 1; char *pos, *end; size_t len; + unsigned int i; pos = buf; end = pos + buflen; @@ -1825,37 +2873,15 @@ return len; } - if (capa->enc & WPA_DRIVER_CAPA_ENC_CCMP) { - ret = os_snprintf(pos, end - pos, "%sCCMP", first ? "" : " "); - if (ret < 0 || ret >= end - pos) - return pos - buf; - pos += ret; - first = 0; - } - - if (capa->enc & WPA_DRIVER_CAPA_ENC_TKIP) { - ret = os_snprintf(pos, end - pos, "%sTKIP", first ? "" : " "); - if (ret < 0 || ret >= end - pos) - return pos - buf; - pos += ret; - first = 0; - } - - if (capa->enc & WPA_DRIVER_CAPA_ENC_WEP104) { - ret = os_snprintf(pos, end - pos, "%sWEP104", - first ? "" : " "); - if (ret < 0 || ret >= end - pos) - return pos - buf; - pos += ret; - first = 0; - } - - if (capa->enc & WPA_DRIVER_CAPA_ENC_WEP40) { - ret = os_snprintf(pos, end - pos, "%sWEP40", first ? "" : " "); - if (ret < 0 || ret >= end - pos) - return pos - buf; - pos += ret; - first = 0; + for (i = 0; i < ARRAY_SIZE(ciphers); i++) { + if (capa->enc & ciphers[i].capa) { + ret = os_snprintf(pos, end - pos, "%s%s", + first ? "" : " ", ciphers[i].name); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + first = 0; + } } return pos - buf; @@ -2006,6 +3032,150 @@ } +static int ctrl_iface_get_capability_modes(int res, char *strict, + struct wpa_driver_capa *capa, + char *buf, size_t buflen) +{ + int ret, first = 1; + char *pos, *end; + size_t len; + + pos = buf; + end = pos + buflen; + + if (res < 0) { + if (strict) + return 0; + len = os_strlcpy(buf, "IBSS AP", buflen); + if (len >= buflen) + return -1; + return len; + } + + if (capa->flags & WPA_DRIVER_FLAGS_IBSS) { + ret = os_snprintf(pos, end - pos, "%sIBSS", first ? "" : " "); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + first = 0; + } + + if (capa->flags & WPA_DRIVER_FLAGS_AP) { + ret = os_snprintf(pos, end - pos, "%sAP", first ? "" : " "); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + first = 0; + } + + return pos - buf; +} + + +static int ctrl_iface_get_capability_channels(struct wpa_supplicant *wpa_s, + char *buf, size_t buflen) +{ + struct hostapd_channel_data *chnl; + int ret, i, j; + char *pos, *end, *hmode; + + pos = buf; + end = pos + buflen; + + for (j = 0; j < wpa_s->hw.num_modes; j++) { + switch (wpa_s->hw.modes[j].mode) { + case HOSTAPD_MODE_IEEE80211B: + hmode = "B"; + break; + case HOSTAPD_MODE_IEEE80211G: + hmode = "G"; + break; + case HOSTAPD_MODE_IEEE80211A: + hmode = "A"; + break; + case HOSTAPD_MODE_IEEE80211AD: + hmode = "AD"; + break; + default: + continue; + } + ret = os_snprintf(pos, end - pos, "Mode[%s] Channels:", hmode); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + chnl = wpa_s->hw.modes[j].channels; + for (i = 0; i < wpa_s->hw.modes[j].num_channels; i++) { + if (chnl[i].flag & HOSTAPD_CHAN_DISABLED) + continue; + ret = os_snprintf(pos, end - pos, " %d", chnl[i].chan); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + } + ret = os_snprintf(pos, end - pos, "\n"); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + } + + return pos - buf; +} + + +static int ctrl_iface_get_capability_freq(struct wpa_supplicant *wpa_s, + char *buf, size_t buflen) +{ + struct hostapd_channel_data *chnl; + int ret, i, j; + char *pos, *end, *hmode; + + pos = buf; + end = pos + buflen; + + for (j = 0; j < wpa_s->hw.num_modes; j++) { + switch (wpa_s->hw.modes[j].mode) { + case HOSTAPD_MODE_IEEE80211B: + hmode = "B"; + break; + case HOSTAPD_MODE_IEEE80211G: + hmode = "G"; + break; + case HOSTAPD_MODE_IEEE80211A: + hmode = "A"; + break; + case HOSTAPD_MODE_IEEE80211AD: + hmode = "AD"; + break; + default: + continue; + } + ret = os_snprintf(pos, end - pos, "Mode[%s] Channels:\n", + hmode); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + chnl = wpa_s->hw.modes[j].channels; + for (i = 0; i < wpa_s->hw.modes[j].num_channels; i++) { + if (chnl[i].flag & HOSTAPD_CHAN_DISABLED) + continue; + ret = os_snprintf(pos, end - pos, " %d = %d MHz%s\n", + chnl[i].chan, chnl[i].freq, + chnl[i].flag & HOSTAPD_CHAN_NO_IBSS ? + " (NO_IBSS)" : ""); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + } + ret = os_snprintf(pos, end - pos, "\n"); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + } + + return pos - buf; +} + + static int wpa_supplicant_ctrl_iface_get_capability( struct wpa_supplicant *wpa_s, const char *_field, char *buf, size_t buflen) @@ -2056,6 +3226,16 @@ return ctrl_iface_get_capability_auth_alg(res, strict, &capa, buf, buflen); + if (os_strcmp(field, "modes") == 0) + return ctrl_iface_get_capability_modes(res, strict, &capa, + buf, buflen); + + if (os_strcmp(field, "channels") == 0) + return ctrl_iface_get_capability_channels(wpa_s, buf, buflen); + + if (os_strcmp(field, "freq") == 0) + return ctrl_iface_get_capability_freq(wpa_s, buf, buflen); + wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown GET_CAPABILITY field '%s'", field); @@ -2098,174 +3278,395 @@ #endif /* CONFIG_INTERWORKING */ -static int wpa_supplicant_ctrl_iface_bss(struct wpa_supplicant *wpa_s, - const char *cmd, char *buf, - size_t buflen) +static int print_bss_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, + unsigned long mask, char *buf, size_t buflen) { - u8 bssid[ETH_ALEN]; size_t i; - struct wpa_bss *bss; int ret; char *pos, *end; const u8 *ie, *ie2; - if (os_strcmp(cmd, "FIRST") == 0) - bss = dl_list_first(&wpa_s->bss, struct wpa_bss, list); - else if (os_strncmp(cmd, "ID-", 3) == 0) { - i = atoi(cmd + 3); - bss = wpa_bss_get_id(wpa_s, i); - } else if (os_strncmp(cmd, "NEXT-", 5) == 0) { - i = atoi(cmd + 5); - bss = wpa_bss_get_id(wpa_s, i); - if (bss) { - struct dl_list *next = bss->list_id.next; - if (next == &wpa_s->bss_id) - bss = NULL; - else - bss = dl_list_entry(next, struct wpa_bss, - list_id); - } -#ifdef CONFIG_P2P - } else if (os_strncmp(cmd, "p2p_dev_addr=", 13) == 0) { - if (hwaddr_aton(cmd + 13, bssid) == 0) - bss = wpa_bss_get_p2p_dev_addr(wpa_s, bssid); - else - bss = NULL; -#endif /* CONFIG_P2P */ - } else if (hwaddr_aton(cmd, bssid) == 0) - bss = wpa_bss_get_bssid(wpa_s, bssid); - else { - struct wpa_bss *tmp; - i = atoi(cmd); - bss = NULL; - dl_list_for_each(tmp, &wpa_s->bss_id, struct wpa_bss, list_id) - { - if (i-- == 0) { - bss = tmp; - break; - } - } - } - - if (bss == NULL) - return 0; - pos = buf; end = buf + buflen; - ret = os_snprintf(pos, end - pos, - "id=%u\n" - "bssid=" MACSTR "\n" - "freq=%d\n" - "beacon_int=%d\n" - "capabilities=0x%04x\n" - "qual=%d\n" - "noise=%d\n" - "level=%d\n" - "tsf=%016llu\n" - "ie=", - bss->id, - MAC2STR(bss->bssid), bss->freq, bss->beacon_int, - bss->caps, bss->qual, bss->noise, bss->level, - (unsigned long long) bss->tsf); - if (ret < 0 || ret >= end - pos) - return pos - buf; - pos += ret; - ie = (const u8 *) (bss + 1); - for (i = 0; i < bss->ie_len; i++) { - ret = os_snprintf(pos, end - pos, "%02x", *ie++); + if (mask & WPA_BSS_MASK_ID) { + ret = os_snprintf(pos, end - pos, "id=%u\n", bss->id); if (ret < 0 || ret >= end - pos) - return pos - buf; + return 0; pos += ret; } - ret = os_snprintf(pos, end - pos, "\n"); - if (ret < 0 || ret >= end - pos) - return pos - buf; - pos += ret; - - ret = os_snprintf(pos, end - pos, "flags="); - if (ret < 0 || ret >= end - pos) - return pos - buf; - pos += ret; + if (mask & WPA_BSS_MASK_BSSID) { + ret = os_snprintf(pos, end - pos, "bssid=" MACSTR "\n", + MAC2STR(bss->bssid)); + if (ret < 0 || ret >= end - pos) + return 0; + pos += ret; + } - ie = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE); - if (ie) - pos = wpa_supplicant_ie_txt(pos, end, "WPA", ie, 2 + ie[1]); - ie2 = wpa_bss_get_ie(bss, WLAN_EID_RSN); - if (ie2) - pos = wpa_supplicant_ie_txt(pos, end, "WPA2", ie2, 2 + ie2[1]); - pos = wpa_supplicant_wps_ie_txt(wpa_s, pos, end, bss); - if (!ie && !ie2 && bss->caps & IEEE80211_CAP_PRIVACY) { - ret = os_snprintf(pos, end - pos, "[WEP]"); + if (mask & WPA_BSS_MASK_FREQ) { + ret = os_snprintf(pos, end - pos, "freq=%d\n", bss->freq); if (ret < 0 || ret >= end - pos) - return pos - buf; + return 0; pos += ret; } - if (bss->caps & IEEE80211_CAP_IBSS) { - ret = os_snprintf(pos, end - pos, "[IBSS]"); + + if (mask & WPA_BSS_MASK_BEACON_INT) { + ret = os_snprintf(pos, end - pos, "beacon_int=%d\n", + bss->beacon_int); if (ret < 0 || ret >= end - pos) - return pos - buf; + return 0; pos += ret; } - if (bss->caps & IEEE80211_CAP_ESS) { - ret = os_snprintf(pos, end - pos, "[ESS]"); + + if (mask & WPA_BSS_MASK_CAPABILITIES) { + ret = os_snprintf(pos, end - pos, "capabilities=0x%04x\n", + bss->caps); if (ret < 0 || ret >= end - pos) - return pos - buf; + return 0; pos += ret; } - if (wpa_bss_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE)) { - ret = os_snprintf(pos, end - pos, "[P2P]"); + + if (mask & WPA_BSS_MASK_QUAL) { + ret = os_snprintf(pos, end - pos, "qual=%d\n", bss->qual); if (ret < 0 || ret >= end - pos) - return pos - buf; + return 0; pos += ret; } - ret = os_snprintf(pos, end - pos, "\n"); - if (ret < 0 || ret >= end - pos) - return pos - buf; - pos += ret; + if (mask & WPA_BSS_MASK_NOISE) { + ret = os_snprintf(pos, end - pos, "noise=%d\n", bss->noise); + if (ret < 0 || ret >= end - pos) + return 0; + pos += ret; + } - ret = os_snprintf(pos, end - pos, "ssid=%s\n", - wpa_ssid_txt(bss->ssid, bss->ssid_len)); - if (ret < 0 || ret >= end - pos) - return pos - buf; - pos += ret; + if (mask & WPA_BSS_MASK_LEVEL) { + ret = os_snprintf(pos, end - pos, "level=%d\n", bss->level); + if (ret < 0 || ret >= end - pos) + return 0; + pos += ret; + } -#ifdef CONFIG_WPS - ie = (const u8 *) (bss + 1); - ret = wpas_wps_scan_result_text(ie, bss->ie_len, pos, end); - if (ret < 0 || ret >= end - pos) - return pos - buf; - pos += ret; -#endif /* CONFIG_WPS */ + if (mask & WPA_BSS_MASK_TSF) { + ret = os_snprintf(pos, end - pos, "tsf=%016llu\n", + (unsigned long long) bss->tsf); + if (ret < 0 || ret >= end - pos) + return 0; + pos += ret; + } -#ifdef CONFIG_P2P - ie = (const u8 *) (bss + 1); - ret = wpas_p2p_scan_result_text(ie, bss->ie_len, pos, end); - if (ret < 0 || ret >= end - pos) - return pos - buf; - pos += ret; -#endif /* CONFIG_P2P */ + if (mask & WPA_BSS_MASK_AGE) { + struct os_reltime now; -#ifdef CONFIG_INTERWORKING - pos = anqp_add_hex(pos, end, "anqp_venue_name", bss->anqp_venue_name); - pos = anqp_add_hex(pos, end, "anqp_network_auth_type", - bss->anqp_network_auth_type); - pos = anqp_add_hex(pos, end, "anqp_roaming_consortium", - bss->anqp_roaming_consortium); - pos = anqp_add_hex(pos, end, "anqp_ip_addr_type_availability", - bss->anqp_ip_addr_type_availability); - pos = anqp_add_hex(pos, end, "anqp_nai_realm", bss->anqp_nai_realm); - pos = anqp_add_hex(pos, end, "anqp_3gpp", bss->anqp_3gpp); - pos = anqp_add_hex(pos, end, "anqp_domain_name", - bss->anqp_domain_name); -#endif /* CONFIG_INTERWORKING */ + os_get_reltime(&now); + ret = os_snprintf(pos, end - pos, "age=%d\n", + (int) (now.sec - bss->last_update.sec)); + if (ret < 0 || ret >= end - pos) + return 0; + pos += ret; + } + + if (mask & WPA_BSS_MASK_IE) { + ret = os_snprintf(pos, end - pos, "ie="); + if (ret < 0 || ret >= end - pos) + return 0; + pos += ret; + + ie = (const u8 *) (bss + 1); + for (i = 0; i < bss->ie_len; i++) { + ret = os_snprintf(pos, end - pos, "%02x", *ie++); + if (ret < 0 || ret >= end - pos) + return 0; + pos += ret; + } + + ret = os_snprintf(pos, end - pos, "\n"); + if (ret < 0 || ret >= end - pos) + return 0; + pos += ret; + } + + if (mask & WPA_BSS_MASK_FLAGS) { + ret = os_snprintf(pos, end - pos, "flags="); + if (ret < 0 || ret >= end - pos) + return 0; + pos += ret; + + ie = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE); + if (ie) + pos = wpa_supplicant_ie_txt(pos, end, "WPA", ie, + 2 + ie[1]); + ie2 = wpa_bss_get_ie(bss, WLAN_EID_RSN); + if (ie2) + pos = wpa_supplicant_ie_txt(pos, end, "WPA2", ie2, + 2 + ie2[1]); + pos = wpa_supplicant_wps_ie_txt(wpa_s, pos, end, bss); + if (!ie && !ie2 && bss->caps & IEEE80211_CAP_PRIVACY) { + ret = os_snprintf(pos, end - pos, "[WEP]"); + if (ret < 0 || ret >= end - pos) + return 0; + pos += ret; + } + if (bss->caps & IEEE80211_CAP_IBSS) { + ret = os_snprintf(pos, end - pos, "[IBSS]"); + if (ret < 0 || ret >= end - pos) + return 0; + pos += ret; + } + if (bss->caps & IEEE80211_CAP_ESS) { + ret = os_snprintf(pos, end - pos, "[ESS]"); + if (ret < 0 || ret >= end - pos) + return 0; + pos += ret; + } + if (wpa_bss_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE) || + wpa_bss_get_vendor_ie_beacon(bss, P2P_IE_VENDOR_TYPE)) { + ret = os_snprintf(pos, end - pos, "[P2P]"); + if (ret < 0 || ret >= end - pos) + return 0; + pos += ret; + } +#ifdef CONFIG_HS20 + if (wpa_bss_get_vendor_ie(bss, HS20_IE_VENDOR_TYPE)) { + ret = os_snprintf(pos, end - pos, "[HS20]"); + if (ret < 0 || ret >= end - pos) + return 0; + pos += ret; + } +#endif /* CONFIG_HS20 */ + + ret = os_snprintf(pos, end - pos, "\n"); + if (ret < 0 || ret >= end - pos) + return 0; + pos += ret; + } + + if (mask & WPA_BSS_MASK_SSID) { + ret = os_snprintf(pos, end - pos, "ssid=%s\n", + wpa_ssid_txt(bss->ssid, bss->ssid_len)); + if (ret < 0 || ret >= end - pos) + return 0; + pos += ret; + } + +#ifdef CONFIG_WPS + if (mask & WPA_BSS_MASK_WPS_SCAN) { + ie = (const u8 *) (bss + 1); + ret = wpas_wps_scan_result_text(ie, bss->ie_len, pos, end); + if (ret < 0 || ret >= end - pos) + return 0; + pos += ret; + } +#endif /* CONFIG_WPS */ + +#ifdef CONFIG_P2P + if (mask & WPA_BSS_MASK_P2P_SCAN) { + ie = (const u8 *) (bss + 1); + ret = wpas_p2p_scan_result_text(ie, bss->ie_len, pos, end); + if (ret < 0 || ret >= end - pos) + return 0; + pos += ret; + } +#endif /* CONFIG_P2P */ + +#ifdef CONFIG_WIFI_DISPLAY + if (mask & WPA_BSS_MASK_WIFI_DISPLAY) { + struct wpabuf *wfd; + ie = (const u8 *) (bss + 1); + wfd = ieee802_11_vendor_ie_concat(ie, bss->ie_len, + WFD_IE_VENDOR_TYPE); + if (wfd) { + ret = os_snprintf(pos, end - pos, "wfd_subelems="); + if (ret < 0 || ret >= end - pos) + return 0; + pos += ret; + + pos += wpa_snprintf_hex(pos, end - pos, + wpabuf_head(wfd), + wpabuf_len(wfd)); + wpabuf_free(wfd); + + ret = os_snprintf(pos, end - pos, "\n"); + if (ret < 0 || ret >= end - pos) + return 0; + pos += ret; + } + } +#endif /* CONFIG_WIFI_DISPLAY */ + +#ifdef CONFIG_INTERWORKING + if ((mask & WPA_BSS_MASK_INTERNETW) && bss->anqp) { + struct wpa_bss_anqp *anqp = bss->anqp; + pos = anqp_add_hex(pos, end, "anqp_venue_name", + anqp->venue_name); + pos = anqp_add_hex(pos, end, "anqp_network_auth_type", + anqp->network_auth_type); + pos = anqp_add_hex(pos, end, "anqp_roaming_consortium", + anqp->roaming_consortium); + pos = anqp_add_hex(pos, end, "anqp_ip_addr_type_availability", + anqp->ip_addr_type_availability); + pos = anqp_add_hex(pos, end, "anqp_nai_realm", + anqp->nai_realm); + pos = anqp_add_hex(pos, end, "anqp_3gpp", anqp->anqp_3gpp); + pos = anqp_add_hex(pos, end, "anqp_domain_name", + anqp->domain_name); +#ifdef CONFIG_HS20 + pos = anqp_add_hex(pos, end, "hs20_operator_friendly_name", + anqp->hs20_operator_friendly_name); + pos = anqp_add_hex(pos, end, "hs20_wan_metrics", + anqp->hs20_wan_metrics); + pos = anqp_add_hex(pos, end, "hs20_connection_capability", + anqp->hs20_connection_capability); +#endif /* CONFIG_HS20 */ + } +#endif /* CONFIG_INTERWORKING */ + + if (mask & WPA_BSS_MASK_DELIM) { + ret = os_snprintf(pos, end - pos, "====\n"); + if (ret < 0 || ret >= end - pos) + return 0; + pos += ret; + } return pos - buf; } +static int wpa_supplicant_ctrl_iface_bss(struct wpa_supplicant *wpa_s, + const char *cmd, char *buf, + size_t buflen) +{ + u8 bssid[ETH_ALEN]; + size_t i; + struct wpa_bss *bss; + struct wpa_bss *bsslast = NULL; + struct dl_list *next; + int ret = 0; + int len; + char *ctmp; + unsigned long mask = WPA_BSS_MASK_ALL; + + if (os_strncmp(cmd, "RANGE=", 6) == 0) { + if (os_strncmp(cmd + 6, "ALL", 3) == 0) { + bss = dl_list_first(&wpa_s->bss_id, struct wpa_bss, + list_id); + bsslast = dl_list_last(&wpa_s->bss_id, struct wpa_bss, + list_id); + } else { /* N1-N2 */ + unsigned int id1, id2; + + if ((ctmp = os_strchr(cmd + 6, '-')) == NULL) { + wpa_printf(MSG_INFO, "Wrong BSS range " + "format"); + return 0; + } + + if (*(cmd + 6) == '-') + id1 = 0; + else + id1 = atoi(cmd + 6); + ctmp++; + if (*ctmp >= '0' && *ctmp <= '9') + id2 = atoi(ctmp); + else + id2 = (unsigned int) -1; + bss = wpa_bss_get_id_range(wpa_s, id1, id2); + if (id2 == (unsigned int) -1) + bsslast = dl_list_last(&wpa_s->bss_id, + struct wpa_bss, + list_id); + else { + bsslast = wpa_bss_get_id(wpa_s, id2); + if (bsslast == NULL && bss && id2 > id1) { + struct wpa_bss *tmp = bss; + for (;;) { + next = tmp->list_id.next; + if (next == &wpa_s->bss_id) + break; + tmp = dl_list_entry( + next, struct wpa_bss, + list_id); + if (tmp->id > id2) + break; + bsslast = tmp; + } + } + } + } + } else if (os_strncmp(cmd, "FIRST", 5) == 0) + bss = dl_list_first(&wpa_s->bss_id, struct wpa_bss, list_id); + else if (os_strncmp(cmd, "LAST", 4) == 0) + bss = dl_list_last(&wpa_s->bss_id, struct wpa_bss, list_id); + else if (os_strncmp(cmd, "ID-", 3) == 0) { + i = atoi(cmd + 3); + bss = wpa_bss_get_id(wpa_s, i); + } else if (os_strncmp(cmd, "NEXT-", 5) == 0) { + i = atoi(cmd + 5); + bss = wpa_bss_get_id(wpa_s, i); + if (bss) { + next = bss->list_id.next; + if (next == &wpa_s->bss_id) + bss = NULL; + else + bss = dl_list_entry(next, struct wpa_bss, + list_id); + } +#ifdef CONFIG_P2P + } else if (os_strncmp(cmd, "p2p_dev_addr=", 13) == 0) { + if (hwaddr_aton(cmd + 13, bssid) == 0) + bss = wpa_bss_get_p2p_dev_addr(wpa_s, bssid); + else + bss = NULL; +#endif /* CONFIG_P2P */ + } else if (hwaddr_aton(cmd, bssid) == 0) + bss = wpa_bss_get_bssid(wpa_s, bssid); + else { + struct wpa_bss *tmp; + i = atoi(cmd); + bss = NULL; + dl_list_for_each(tmp, &wpa_s->bss_id, struct wpa_bss, list_id) + { + if (i-- == 0) { + bss = tmp; + break; + } + } + } + + if ((ctmp = os_strstr(cmd, "MASK=")) != NULL) { + mask = strtoul(ctmp + 5, NULL, 0x10); + if (mask == 0) + mask = WPA_BSS_MASK_ALL; + } + + if (bss == NULL) + return 0; + + if (bsslast == NULL) + bsslast = bss; + do { + len = print_bss_info(wpa_s, bss, mask, buf, buflen); + ret += len; + buf += len; + buflen -= len; + if (bss == bsslast) { + if ((mask & WPA_BSS_MASK_DELIM) && len && + (bss == dl_list_last(&wpa_s->bss_id, + struct wpa_bss, list_id))) + os_snprintf(buf - 5, 5, "####\n"); + break; + } + next = bss->list_id.next; + if (next == &wpa_s->bss_id) + break; + bss = dl_list_entry(next, struct wpa_bss, list_id); + } while (bss && len); + + return ret; +} + + static int wpa_supplicant_ctrl_iface_ap_scan( struct wpa_supplicant *wpa_s, char *cmd) { @@ -2278,10 +3679,7 @@ struct wpa_supplicant *wpa_s, char *cmd) { int scan_int = atoi(cmd); - if (scan_int < 0) - return -1; - wpa_s->scan_interval = scan_int; - return 0; + return wpa_supplicant_set_scan_interval(wpa_s, scan_int); } @@ -2301,6 +3699,19 @@ } +static int wpa_supplicant_ctrl_iface_bss_flush( + struct wpa_supplicant *wpa_s, char *cmd) +{ + int flush_age = atoi(cmd); + + if (flush_age == 0) + wpa_bss_flush(wpa_s); + else + wpa_bss_flush_by_age(wpa_s, flush_age); + return 0; +} + + static void wpa_supplicant_ctrl_iface_drop_sa(struct wpa_supplicant *wpa_s) { wpa_printf(MSG_DEBUG, "Dropping SA without deauthentication"); @@ -2342,7 +3753,13 @@ wpa_printf(MSG_DEBUG, "CTRL_IFACE ROAM " MACSTR, MAC2STR(bssid)); - bss = wpa_bss_get_bssid(wpa_s, bssid); + if (!ssid) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE ROAM: No network " + "configuration known for the target AP"); + return -1; + } + + bss = wpa_bss_get(wpa_s, bssid, ssid->ssid, ssid->ssid_len); if (!bss) { wpa_printf(MSG_DEBUG, "CTRL_IFACE ROAM: Target AP not found " "from BSS table"); @@ -2354,12 +3771,6 @@ * allow roaming to other networks */ - if (!ssid) { - wpa_printf(MSG_DEBUG, "CTRL_IFACE ROAM: No network " - "configuration known for the target AP"); - return -1; - } - wpa_s->reassociate = 1; wpa_supplicant_connect(wpa_s, bss, ssid); @@ -2374,7 +3785,9 @@ unsigned int timeout = atoi(cmd); enum p2p_discovery_type type = P2P_FIND_START_WITH_FULL; u8 dev_id[ETH_ALEN], *_dev_id = NULL; + u8 dev_type[WPS_DEV_TYPE_LEN], *_dev_type = NULL; char *pos; + unsigned int search_delay; if (os_strstr(cmd, "type=social")) type = P2P_FIND_ONLY_SOCIAL; @@ -2389,7 +3802,23 @@ _dev_id = dev_id; } - return wpas_p2p_find(wpa_s, timeout, type, 0, NULL, _dev_id); + pos = os_strstr(cmd, "dev_type="); + if (pos) { + pos += 9; + if (wps_dev_type_str2bin(pos, dev_type) < 0) + return -1; + _dev_type = dev_type; + } + + pos = os_strstr(cmd, "delay="); + if (pos) { + pos += 6; + search_delay = atoi(pos); + } else + search_delay = wpas_p2p_search_delay(wpa_s); + + return wpas_p2p_find(wpa_s, timeout, type, _dev_type != NULL, _dev_type, + _dev_id, search_delay); } @@ -2402,14 +3831,19 @@ enum p2p_wps_method wps_method; int new_pin; int ret; - int persistent_group; + int persistent_group, persistent_id = -1; int join; int auth; + int automatic; int go_intent = -1; int freq = 0; + int pd; + int ht40, vht; - /* <"pbc" | "pin" | PIN> [label|display|keypad] [persistent] - * [join] [auth] [go_intent=<0..15>] [freq=] */ + /* <"pbc" | "pin" | PIN> [label|display|keypad] + * [persistent|persistent=] + * [join] [auth] [go_intent=<0..15>] [freq=] [provdisc] + * [ht40] [vht] */ if (hwaddr_aton(cmd, addr)) return -1; @@ -2420,8 +3854,26 @@ pos++; persistent_group = os_strstr(pos, " persistent") != NULL; + pos2 = os_strstr(pos, " persistent="); + if (pos2) { + struct wpa_ssid *ssid; + persistent_id = atoi(pos2 + 12); + ssid = wpa_config_get_network(wpa_s->conf, persistent_id); + if (ssid == NULL || ssid->disabled != 2 || + ssid->mode != WPAS_MODE_P2P_GO) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find " + "SSID id=%d for persistent P2P group (GO)", + persistent_id); + return -1; + } + } join = os_strstr(pos, " join") != NULL; auth = os_strstr(pos, " auth") != NULL; + automatic = os_strstr(pos, " auto") != NULL; + pd = os_strstr(pos, " provdisc") != NULL; + vht = (os_strstr(cmd, " vht") != NULL) || wpa_s->conf->p2p_go_vht; + ht40 = (os_strstr(cmd, " ht40") != NULL) || wpa_s->conf->p2p_go_ht40 || + vht; pos2 = os_strstr(pos, " go_intent="); if (pos2) { @@ -2453,11 +3905,16 @@ if (os_strncmp(pos, "display", 7) == 0) wps_method = WPS_PIN_DISPLAY; } + if (!wps_pin_str_valid(pin)) { + os_memcpy(buf, "FAIL-INVALID-PIN\n", 17); + return 17; + } } new_pin = wpas_p2p_connect(wpa_s, addr, pin, wps_method, - persistent_group, join, auth, go_intent, - freq); + persistent_group, automatic, join, + auth, go_intent, freq, persistent_id, pd, + ht40, vht); if (new_pin == -2) { os_memcpy(buf, "FAIL-CHANNEL-UNAVAILABLE\n", 25); return 25; @@ -2491,8 +3948,9 @@ { u8 addr[ETH_ALEN]; char *pos; + enum wpas_p2p_prov_disc_use use = WPAS_P2P_PD_FOR_GO_NEG; - /* [join] */ + /* [join|auto] */ if (hwaddr_aton(cmd, addr)) return -1; @@ -2502,8 +3960,12 @@ return -1; pos++; - return wpas_p2p_prov_disc(wpa_s, addr, pos, - os_strstr(pos, "join") != NULL); + if (os_strstr(pos, " join") != NULL) + use = WPAS_P2P_PD_FOR_JOIN; + else if (os_strstr(pos, " auto") != NULL) + use = WPAS_P2P_PD_AUTO; + + return wpas_p2p_prov_disc(wpa_s, addr, pos, use); } @@ -2551,8 +4013,11 @@ if (*pos != ' ') return -1; pos++; - ref = (unsigned long) wpas_p2p_sd_request_upnp(wpa_s, dst, - version, pos); + ref = wpas_p2p_sd_request_upnp(wpa_s, dst, version, pos); +#ifdef CONFIG_WIFI_DISPLAY + } else if (os_strncmp(pos, "wifi-display ", 13) == 0) { + ref = wpas_p2p_sd_request_wifi_display(wpa_s, dst, pos + 13); +#endif /* CONFIG_WIFI_DISPLAY */ } else { len = os_strlen(pos); if (len & 1) @@ -2566,9 +4031,11 @@ return -1; } - ref = (unsigned long) wpas_p2p_sd_request(wpa_s, dst, tlvs); + ref = wpas_p2p_sd_request(wpa_s, dst, tlvs); wpabuf_free(tlvs); } + if (ref == 0) + return -1; res = os_snprintf(buf, buflen, "%llx", (long long unsigned) ref); if (res < 0 || (unsigned) res >= buflen) return -1; @@ -2584,7 +4051,7 @@ if (sscanf(cmd, "%llx", &val) != 1) return -1; req = val; - return wpas_p2p_sd_cancel_request(wpa_s, (void *) (unsigned long) req); + return wpas_p2p_sd_cancel_request(wpa_s, req); } @@ -2809,7 +4276,9 @@ char *pos; int id; struct wpa_ssid *ssid; - u8 peer[ETH_ALEN]; + u8 *_peer = NULL, peer[ETH_ALEN]; + int freq = 0, pref_freq = 0; + int ht40, vht; id = atoi(cmd); pos = os_strstr(cmd, " peer="); @@ -2817,6 +4286,7 @@ pos += 6; if (hwaddr_aton(pos, peer)) return -1; + _peer = peer; } ssid = wpa_config_get_network(wpa_s->conf, id); if (ssid == NULL || ssid->disabled != 2) { @@ -2826,7 +4296,28 @@ return -1; } - return wpas_p2p_invite(wpa_s, pos ? peer : NULL, ssid, NULL); + pos = os_strstr(cmd, " freq="); + if (pos) { + pos += 6; + freq = atoi(pos); + if (freq <= 0) + return -1; + } + + pos = os_strstr(cmd, " pref="); + if (pos) { + pos += 6; + pref_freq = atoi(pos); + if (pref_freq <= 0) + return -1; + } + + vht = (os_strstr(cmd, " vht") != NULL) || wpa_s->conf->p2p_go_vht; + ht40 = (os_strstr(cmd, " ht40") != NULL) || wpa_s->conf->p2p_go_ht40 || + vht; + + return wpas_p2p_invite(wpa_s, _peer, ssid, NULL, freq, ht40, vht, + pref_freq); } @@ -2873,7 +4364,8 @@ static int p2p_ctrl_group_add_persistent(struct wpa_supplicant *wpa_s, - char *cmd, int freq) + char *cmd, int freq, int ht40, + int vht) { int id; struct wpa_ssid *ssid; @@ -2887,26 +4379,34 @@ return -1; } - return wpas_p2p_group_add_persistent(wpa_s, ssid, 0, freq); + return wpas_p2p_group_add_persistent(wpa_s, ssid, 0, freq, ht40, vht, + NULL, 0); } static int p2p_ctrl_group_add(struct wpa_supplicant *wpa_s, char *cmd) { - int freq = 0; + int freq = 0, ht40, vht; char *pos; pos = os_strstr(cmd, "freq="); if (pos) freq = atoi(pos + 5); + vht = (os_strstr(cmd, "vht") != NULL) || wpa_s->conf->p2p_go_vht; + ht40 = (os_strstr(cmd, "ht40") != NULL) || wpa_s->conf->p2p_go_ht40 || + vht; + if (os_strncmp(cmd, "persistent=", 11) == 0) - return p2p_ctrl_group_add_persistent(wpa_s, cmd + 11, freq); + return p2p_ctrl_group_add_persistent(wpa_s, cmd + 11, freq, + ht40, vht); if (os_strcmp(cmd, "persistent") == 0 || os_strncmp(cmd, "persistent ", 11) == 0) - return wpas_p2p_group_add(wpa_s, 1, freq); + return wpas_p2p_group_add(wpa_s, 1, freq, ht40, vht); if (os_strncmp(cmd, "freq=", 5) == 0) - return wpas_p2p_group_add(wpa_s, 0, freq); + return wpas_p2p_group_add(wpa_s, 0, freq, ht40, vht); + if (ht40) + return wpas_p2p_group_add(wpa_s, 0, freq, ht40, vht); wpa_printf(MSG_DEBUG, "CTRL: Invalid P2P_GROUP_ADD parameters '%s'", cmd); @@ -2923,6 +4423,7 @@ char *pos, *end; char devtype[WPS_DEV_TYPE_BUFSIZE]; struct wpa_ssid *ssid; + size_t i; if (!wpa_s->global->p2p) return -1; @@ -2976,6 +4477,18 @@ return pos - buf; pos += res; + for (i = 0; i < info->wps_sec_dev_type_list_len / WPS_DEV_TYPE_LEN; i++) + { + const u8 *t; + t = &info->wps_sec_dev_type_list[i * WPS_DEV_TYPE_LEN]; + res = os_snprintf(pos, end - pos, "sec_dev_type=%s\n", + wps_dev_type_bin2str(t, devtype, + sizeof(devtype))); + if (res < 0 || res >= end - pos) + return pos - buf; + pos += res; + } + ssid = wpas_p2p_get_persistent(wpa_s, info->p2p_device_addr, NULL, 0); if (ssid) { res = os_snprintf(pos, end - pos, "persistent=%d\n", ssid->id); @@ -2993,6 +4506,29 @@ } +static int p2p_ctrl_disallow_freq(struct wpa_supplicant *wpa_s, + const char *param) +{ + unsigned int i; + + if (wpa_s->global->p2p == NULL) + return -1; + + if (freq_range_list_parse(&wpa_s->global->p2p_disallow_freq, param) < 0) + return -1; + + for (i = 0; i < wpa_s->global->p2p_disallow_freq.num; i++) { + struct wpa_freq_range *freq; + freq = &wpa_s->global->p2p_disallow_freq.range[i]; + wpa_printf(MSG_DEBUG, "P2P: Disallowed frequency range %u-%u", + freq->min, freq->max); + } + + wpas_p2p_update_channel_list(wpa_s); + return 0; +} + + static int p2p_ctrl_set(struct wpa_supplicant *wpa_s, char *cmd) { char *param; @@ -3072,6 +4608,20 @@ return 0; } + if (os_strcmp(cmd, "conc_pref") == 0) { + if (os_strcmp(param, "sta") == 0) + wpa_s->global->conc_pref = WPA_CONC_PREF_STA; + else if (os_strcmp(param, "p2p") == 0) + wpa_s->global->conc_pref = WPA_CONC_PREF_P2P; + else { + wpa_printf(MSG_INFO, "Invalid conc_pref value"); + return -1; + } + wpa_printf(MSG_DEBUG, "Single channel concurrency preference: " + "%s", param); + return 0; + } + if (os_strcmp(cmd, "force_long_sd") == 0) { wpa_s->force_long_sd = atoi(param); return 0; @@ -3137,6 +4687,48 @@ return 0; } + if (os_strcmp(cmd, "disallow_freq") == 0) + return p2p_ctrl_disallow_freq(wpa_s, param); + + if (os_strcmp(cmd, "disc_int") == 0) { + int min_disc_int, max_disc_int, max_disc_tu; + char *pos; + + pos = param; + + min_disc_int = atoi(pos); + pos = os_strchr(pos, ' '); + if (pos == NULL) + return -1; + *pos++ = '\0'; + + max_disc_int = atoi(pos); + pos = os_strchr(pos, ' '); + if (pos == NULL) + return -1; + *pos++ = '\0'; + + max_disc_tu = atoi(pos); + + return p2p_set_disc_int(wpa_s->global->p2p, min_disc_int, + max_disc_int, max_disc_tu); + } + + if (os_strcmp(cmd, "per_sta_psk") == 0) { + wpa_s->global->p2p_per_sta_psk = !!atoi(param); + return 0; + } + +#ifdef CONFIG_WPS_NFC + if (os_strcmp(cmd, "nfc_tag") == 0) + return wpas_p2p_nfc_tag_enabled(wpa_s, !!atoi(param)); +#endif /* CONFIG_WPS_NFC */ + + if (os_strcmp(cmd, "disable_ip_addr_req") == 0) { + wpa_s->p2p_disable_ip_addr_req = !!atoi(param); + return 0; + } + wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown P2P_SET field value '%s'", cmd); @@ -3144,6 +4736,15 @@ } +static void p2p_ctrl_flush(struct wpa_supplicant *wpa_s) +{ + os_memset(wpa_s->p2p_auth_invite, 0, ETH_ALEN); + wpa_s->force_long_sd = 0; + if (wpa_s->global->p2p) + p2p_flush(wpa_s->global->p2p); +} + + static int p2p_ctrl_presence_req(struct wpa_supplicant *wpa_s, char *cmd) { char *pos, *pos2; @@ -3172,108 +4773,976 @@ int2 = atoi(pos); } - return wpas_p2p_presence_req(wpa_s, dur1, int1, dur2, int2); + return wpas_p2p_presence_req(wpa_s, dur1, int1, dur2, int2); +} + + +static int p2p_ctrl_ext_listen(struct wpa_supplicant *wpa_s, char *cmd) +{ + char *pos; + unsigned int period = 0, interval = 0; + + if (cmd[0]) { + pos = os_strchr(cmd, ' '); + if (pos == NULL) + return -1; + *pos++ = '\0'; + period = atoi(cmd); + interval = atoi(pos); + } + + return wpas_p2p_ext_listen(wpa_s, period, interval); +} + + +static int p2p_ctrl_remove_client(struct wpa_supplicant *wpa_s, const char *cmd) +{ + const char *pos; + u8 peer[ETH_ALEN]; + int iface_addr = 0; + + pos = cmd; + if (os_strncmp(pos, "iface=", 6) == 0) { + iface_addr = 1; + pos += 6; + } + if (hwaddr_aton(pos, peer)) + return -1; + + wpas_p2p_remove_client(wpa_s, peer, iface_addr); + return 0; +} + +#endif /* CONFIG_P2P */ + + +static int * freq_range_to_channel_list(struct wpa_supplicant *wpa_s, char *val) +{ + struct wpa_freq_range_list ranges; + int *freqs = NULL; + struct hostapd_hw_modes *mode; + u16 i; + + if (wpa_s->hw.modes == NULL) + return NULL; + + os_memset(&ranges, 0, sizeof(ranges)); + if (freq_range_list_parse(&ranges, val) < 0) + return NULL; + + for (i = 0; i < wpa_s->hw.num_modes; i++) { + int j; + + mode = &wpa_s->hw.modes[i]; + for (j = 0; j < mode->num_channels; j++) { + unsigned int freq; + + if (mode->channels[j].flag & HOSTAPD_CHAN_DISABLED) + continue; + + freq = mode->channels[j].freq; + if (!freq_range_list_includes(&ranges, freq)) + continue; + + int_array_add_unique(&freqs, freq); + } + } + + os_free(ranges.range); + return freqs; +} + + +#ifdef CONFIG_INTERWORKING + +static int ctrl_interworking_select(struct wpa_supplicant *wpa_s, char *param) +{ + int auto_sel = 0; + int *freqs = NULL; + + if (param) { + char *pos; + + auto_sel = os_strstr(param, "auto") != NULL; + + pos = os_strstr(param, "freq="); + if (pos) { + freqs = freq_range_to_channel_list(wpa_s, pos + 5); + if (freqs == NULL) + return -1; + } + + } + + return interworking_select(wpa_s, auto_sel, freqs); +} + + +static int ctrl_interworking_connect(struct wpa_supplicant *wpa_s, char *dst) +{ + u8 bssid[ETH_ALEN]; + struct wpa_bss *bss; + + if (hwaddr_aton(dst, bssid)) { + wpa_printf(MSG_DEBUG, "Invalid BSSID '%s'", dst); + return -1; + } + + bss = wpa_bss_get_bssid(wpa_s, bssid); + if (bss == NULL) { + wpa_printf(MSG_DEBUG, "Could not find BSS " MACSTR, + MAC2STR(bssid)); + return -1; + } + + return interworking_connect(wpa_s, bss); +} + + +static int get_anqp(struct wpa_supplicant *wpa_s, char *dst) +{ + u8 dst_addr[ETH_ALEN]; + int used; + char *pos; +#define MAX_ANQP_INFO_ID 100 + u16 id[MAX_ANQP_INFO_ID]; + size_t num_id = 0; + + used = hwaddr_aton2(dst, dst_addr); + if (used < 0) + return -1; + pos = dst + used; + while (num_id < MAX_ANQP_INFO_ID) { + id[num_id] = atoi(pos); + if (id[num_id]) + num_id++; + pos = os_strchr(pos + 1, ','); + if (pos == NULL) + break; + pos++; + } + + if (num_id == 0) + return -1; + + return anqp_send_req(wpa_s, dst_addr, id, num_id); +} + + +static int gas_request(struct wpa_supplicant *wpa_s, char *cmd) +{ + u8 dst_addr[ETH_ALEN]; + struct wpabuf *advproto, *query = NULL; + int used, ret = -1; + char *pos, *end; + size_t len; + + used = hwaddr_aton2(cmd, dst_addr); + if (used < 0) + return -1; + + pos = cmd + used; + while (*pos == ' ') + pos++; + + /* Advertisement Protocol ID */ + end = os_strchr(pos, ' '); + if (end) + len = end - pos; + else + len = os_strlen(pos); + if (len & 0x01) + return -1; + len /= 2; + if (len == 0) + return -1; + advproto = wpabuf_alloc(len); + if (advproto == NULL) + return -1; + if (hexstr2bin(pos, wpabuf_put(advproto, len), len) < 0) + goto fail; + + if (end) { + /* Optional Query Request */ + pos = end + 1; + while (*pos == ' ') + pos++; + + len = os_strlen(pos); + if (len) { + if (len & 0x01) + goto fail; + len /= 2; + if (len == 0) + goto fail; + query = wpabuf_alloc(len); + if (query == NULL) + goto fail; + if (hexstr2bin(pos, wpabuf_put(query, len), len) < 0) + goto fail; + } + } + + ret = gas_send_request(wpa_s, dst_addr, advproto, query); + +fail: + wpabuf_free(advproto); + wpabuf_free(query); + + return ret; +} + + +static int gas_response_get(struct wpa_supplicant *wpa_s, char *cmd, char *buf, + size_t buflen) +{ + u8 addr[ETH_ALEN]; + int dialog_token; + int used; + char *pos; + size_t resp_len, start, requested_len; + struct wpabuf *resp; + int ret; + + used = hwaddr_aton2(cmd, addr); + if (used < 0) + return -1; + + pos = cmd + used; + while (*pos == ' ') + pos++; + dialog_token = atoi(pos); + + if (wpa_s->last_gas_resp && + os_memcmp(addr, wpa_s->last_gas_addr, ETH_ALEN) == 0 && + dialog_token == wpa_s->last_gas_dialog_token) + resp = wpa_s->last_gas_resp; + else if (wpa_s->prev_gas_resp && + os_memcmp(addr, wpa_s->prev_gas_addr, ETH_ALEN) == 0 && + dialog_token == wpa_s->prev_gas_dialog_token) + resp = wpa_s->prev_gas_resp; + else + return -1; + + resp_len = wpabuf_len(resp); + start = 0; + requested_len = resp_len; + + pos = os_strchr(pos, ' '); + if (pos) { + start = atoi(pos); + if (start > resp_len) + return os_snprintf(buf, buflen, "FAIL-Invalid range"); + pos = os_strchr(pos, ','); + if (pos == NULL) + return -1; + pos++; + requested_len = atoi(pos); + if (start + requested_len > resp_len) + return os_snprintf(buf, buflen, "FAIL-Invalid range"); + } + + if (requested_len * 2 + 1 > buflen) + return os_snprintf(buf, buflen, "FAIL-Too long response"); + + ret = wpa_snprintf_hex(buf, buflen, wpabuf_head_u8(resp) + start, + requested_len); + + if (start + requested_len == resp_len) { + /* + * Free memory by dropping the response after it has been + * fetched. + */ + if (resp == wpa_s->prev_gas_resp) { + wpabuf_free(wpa_s->prev_gas_resp); + wpa_s->prev_gas_resp = NULL; + } else { + wpabuf_free(wpa_s->last_gas_resp); + wpa_s->last_gas_resp = NULL; + } + } + + return ret; +} +#endif /* CONFIG_INTERWORKING */ + + +#ifdef CONFIG_HS20 + +static int get_hs20_anqp(struct wpa_supplicant *wpa_s, char *dst) +{ + u8 dst_addr[ETH_ALEN]; + int used; + char *pos; + u32 subtypes = 0; + + used = hwaddr_aton2(dst, dst_addr); + if (used < 0) + return -1; + pos = dst + used; + for (;;) { + int num = atoi(pos); + if (num <= 0 || num > 31) + return -1; + subtypes |= BIT(num); + pos = os_strchr(pos + 1, ','); + if (pos == NULL) + break; + pos++; + } + + if (subtypes == 0) + return -1; + + return hs20_anqp_send_req(wpa_s, dst_addr, subtypes, NULL, 0); +} + + +static int hs20_nai_home_realm_list(struct wpa_supplicant *wpa_s, + const u8 *addr, const char *realm) +{ + u8 *buf; + size_t rlen, len; + int ret; + + rlen = os_strlen(realm); + len = 3 + rlen; + buf = os_malloc(len); + if (buf == NULL) + return -1; + buf[0] = 1; /* NAI Home Realm Count */ + buf[1] = 0; /* Formatted in accordance with RFC 4282 */ + buf[2] = rlen; + os_memcpy(buf + 3, realm, rlen); + + ret = hs20_anqp_send_req(wpa_s, addr, + BIT(HS20_STYPE_NAI_HOME_REALM_QUERY), + buf, len); + + os_free(buf); + + return ret; +} + + +static int hs20_get_nai_home_realm_list(struct wpa_supplicant *wpa_s, + char *dst) +{ + struct wpa_cred *cred = wpa_s->conf->cred; + u8 dst_addr[ETH_ALEN]; + int used; + u8 *buf; + size_t len; + int ret; + + used = hwaddr_aton2(dst, dst_addr); + if (used < 0) + return -1; + + while (dst[used] == ' ') + used++; + if (os_strncmp(dst + used, "realm=", 6) == 0) + return hs20_nai_home_realm_list(wpa_s, dst_addr, + dst + used + 6); + + len = os_strlen(dst + used); + + if (len == 0 && cred && cred->realm) + return hs20_nai_home_realm_list(wpa_s, dst_addr, cred->realm); + + if (len % 1) + return -1; + len /= 2; + buf = os_malloc(len); + if (buf == NULL) + return -1; + if (hexstr2bin(dst + used, buf, len) < 0) { + os_free(buf); + return -1; + } + + ret = hs20_anqp_send_req(wpa_s, dst_addr, + BIT(HS20_STYPE_NAI_HOME_REALM_QUERY), + buf, len); + os_free(buf); + + return ret; +} + +#endif /* CONFIG_HS20 */ + + +static int wpa_supplicant_ctrl_iface_sta_autoconnect( + struct wpa_supplicant *wpa_s, char *cmd) +{ + wpa_s->auto_reconnect_disabled = atoi(cmd) == 0 ? 1 : 0; + return 0; +} + + +#ifdef CONFIG_AUTOSCAN + +static int wpa_supplicant_ctrl_iface_autoscan(struct wpa_supplicant *wpa_s, + char *cmd) +{ + enum wpa_states state = wpa_s->wpa_state; + char *new_params = NULL; + + if (os_strlen(cmd) > 0) { + new_params = os_strdup(cmd); + if (new_params == NULL) + return -1; + } + + os_free(wpa_s->conf->autoscan); + wpa_s->conf->autoscan = new_params; + + if (wpa_s->conf->autoscan == NULL) + autoscan_deinit(wpa_s); + else if (state == WPA_DISCONNECTED || state == WPA_INACTIVE) + autoscan_init(wpa_s, 1); + else if (state == WPA_SCANNING) + wpa_supplicant_reinit_autoscan(wpa_s); + + return 0; +} + +#endif /* CONFIG_AUTOSCAN */ + + +#ifdef CONFIG_WNM + +static int wpas_ctrl_iface_wnm_sleep(struct wpa_supplicant *wpa_s, char *cmd) +{ + int enter; + int intval = 0; + char *pos; + int ret; + struct wpabuf *tfs_req = NULL; + + if (os_strncmp(cmd, "enter", 5) == 0) + enter = 1; + else if (os_strncmp(cmd, "exit", 4) == 0) + enter = 0; + else + return -1; + + pos = os_strstr(cmd, " interval="); + if (pos) + intval = atoi(pos + 10); + + pos = os_strstr(cmd, " tfs_req="); + if (pos) { + char *end; + size_t len; + pos += 9; + end = os_strchr(pos, ' '); + if (end) + len = end - pos; + else + len = os_strlen(pos); + if (len & 1) + return -1; + len /= 2; + tfs_req = wpabuf_alloc(len); + if (tfs_req == NULL) + return -1; + if (hexstr2bin(pos, wpabuf_put(tfs_req, len), len) < 0) { + wpabuf_free(tfs_req); + return -1; + } + } + + ret = ieee802_11_send_wnmsleep_req(wpa_s, enter ? WNM_SLEEP_MODE_ENTER : + WNM_SLEEP_MODE_EXIT, intval, + tfs_req); + wpabuf_free(tfs_req); + + return ret; +} + + +static int wpas_ctrl_iface_wnm_bss_query(struct wpa_supplicant *wpa_s, char *cmd) +{ + int query_reason; + + query_reason = atoi(cmd); + + wpa_printf(MSG_DEBUG, "CTRL_IFACE: WNM_BSS_QUERY query_reason=%d", + query_reason); + + return wnm_send_bss_transition_mgmt_query(wpa_s, query_reason); +} + +#endif /* CONFIG_WNM */ + + +/* Get string representation of channel width */ +static const char * channel_width_name(enum chan_width width) +{ + switch (width) { + case CHAN_WIDTH_20_NOHT: + return "20 MHz (no HT)"; + case CHAN_WIDTH_20: + return "20 MHz"; + case CHAN_WIDTH_40: + return "40 MHz"; + case CHAN_WIDTH_80: + return "80 MHz"; + case CHAN_WIDTH_80P80: + return "80+80 MHz"; + case CHAN_WIDTH_160: + return "160 MHz"; + default: + return "unknown"; + } +} + + +static int wpa_supplicant_signal_poll(struct wpa_supplicant *wpa_s, char *buf, + size_t buflen) +{ + struct wpa_signal_info si; + int ret; + char *pos, *end; + + ret = wpa_drv_signal_poll(wpa_s, &si); + if (ret) + return -1; + + pos = buf; + end = buf + buflen; + + ret = os_snprintf(pos, end - pos, "RSSI=%d\nLINKSPEED=%d\n" + "NOISE=%d\nFREQUENCY=%u\n", + si.current_signal, si.current_txrate / 1000, + si.current_noise, si.frequency); + if (ret < 0 || ret > end - pos) + return -1; + pos += ret; + + if (si.chanwidth != CHAN_WIDTH_UNKNOWN) { + ret = os_snprintf(pos, end - pos, "WIDTH=%s\n", + channel_width_name(si.chanwidth)); + if (ret < 0 || ret > end - pos) + return -1; + pos += ret; + } + + if (si.center_frq1 > 0 && si.center_frq2 > 0) { + ret = os_snprintf(pos, end - pos, + "CENTER_FRQ1=%d\nCENTER_FRQ2=%d\n", + si.center_frq1, si.center_frq2); + if (ret < 0 || ret > end - pos) + return -1; + pos += ret; + } + + if (si.avg_signal) { + ret = os_snprintf(pos, end - pos, + "AVG_RSSI=%d\n", si.avg_signal); + if (ret < 0 || ret >= end - pos) + return -1; + pos += ret; + } + + return pos - buf; +} + + +static int wpa_supplicant_pktcnt_poll(struct wpa_supplicant *wpa_s, char *buf, + size_t buflen) +{ + struct hostap_sta_driver_data sta; + int ret; + + ret = wpa_drv_pktcnt_poll(wpa_s, &sta); + if (ret) + return -1; + + ret = os_snprintf(buf, buflen, "TXGOOD=%lu\nTXBAD=%lu\nRXGOOD=%lu\n", + sta.tx_packets, sta.tx_retry_failed, sta.rx_packets); + if (ret < 0 || (size_t) ret > buflen) + return -1; + return ret; +} + + +#ifdef ANDROID +static int wpa_supplicant_driver_cmd(struct wpa_supplicant *wpa_s, char *cmd, + char *buf, size_t buflen) +{ + int ret; + + ret = wpa_drv_driver_cmd(wpa_s, cmd, buf, buflen); + if (ret == 0) { + if (os_strncasecmp(cmd, "COUNTRY", 7) == 0) { + struct p2p_data *p2p = wpa_s->global->p2p; + if (p2p) { + char country[3]; + country[0] = cmd[8]; + country[1] = cmd[9]; + country[2] = 0x04; + p2p_set_country(p2p, country); + } + } + ret = os_snprintf(buf, buflen, "%s\n", "OK"); + } + return ret; +} +#endif /* ANDROID */ + + +static void wpa_supplicant_ctrl_iface_flush(struct wpa_supplicant *wpa_s) +{ + wpa_dbg(wpa_s, MSG_DEBUG, "Flush all wpa_supplicant state"); + +#ifdef CONFIG_P2P + wpas_p2p_stop_find(wpa_s); + p2p_ctrl_flush(wpa_s); + wpas_p2p_group_remove(wpa_s, "*"); + wpas_p2p_service_flush(wpa_s); + wpa_s->global->p2p_disabled = 0; + wpa_s->global->p2p_per_sta_psk = 0; + wpa_s->conf->num_sec_device_types = 0; + wpa_s->p2p_disable_ip_addr_req = 0; +#endif /* CONFIG_P2P */ + +#ifdef CONFIG_WPS_TESTING + wps_version_number = 0x20; + wps_testing_dummy_cred = 0; + wps_corrupt_pkhash = 0; +#endif /* CONFIG_WPS_TESTING */ +#ifdef CONFIG_WPS + wpa_s->wps_fragment_size = 0; + wpas_wps_cancel(wpa_s); +#endif /* CONFIG_WPS */ + wpa_s->after_wps = 0; + wpa_s->known_wps_freq = 0; + +#ifdef CONFIG_TDLS +#ifdef CONFIG_TDLS_TESTING + extern unsigned int tdls_testing; + tdls_testing = 0; +#endif /* CONFIG_TDLS_TESTING */ + wpa_drv_tdls_oper(wpa_s, TDLS_ENABLE, NULL); + wpa_tdls_enable(wpa_s->wpa, 1); +#endif /* CONFIG_TDLS */ + + eloop_cancel_timeout(wpa_supplicant_stop_countermeasures, wpa_s, NULL); + wpa_supplicant_stop_countermeasures(wpa_s, NULL); + + wpa_s->no_keep_alive = 0; + + os_free(wpa_s->disallow_aps_bssid); + wpa_s->disallow_aps_bssid = NULL; + wpa_s->disallow_aps_bssid_count = 0; + os_free(wpa_s->disallow_aps_ssid); + wpa_s->disallow_aps_ssid = NULL; + wpa_s->disallow_aps_ssid_count = 0; + + wpa_s->set_sta_uapsd = 0; + wpa_s->sta_uapsd = 0; + + wpa_drv_radio_disable(wpa_s, 0); + + wpa_bss_flush(wpa_s); + wpa_blacklist_clear(wpa_s); + wpa_s->extra_blacklist_count = 0; + wpa_supplicant_ctrl_iface_remove_network(wpa_s, "all"); + wpa_supplicant_ctrl_iface_remove_cred(wpa_s, "all"); + wpa_config_flush_blobs(wpa_s->conf); + wpa_s->conf->auto_interworking = 0; + wpa_s->conf->okc = 0; + wpa_s->conf->pmf = 0; + + wpa_sm_set_param(wpa_s->wpa, RSNA_PMK_LIFETIME, 43200); + wpa_sm_set_param(wpa_s->wpa, RSNA_PMK_REAUTH_THRESHOLD, 70); + wpa_sm_set_param(wpa_s->wpa, RSNA_SA_TIMEOUT, 60); + eapol_sm_notify_logoff(wpa_s->eapol, FALSE); + + radio_remove_unstarted_work(wpa_s, NULL); +} + + +static int wpas_ctrl_radio_work_show(struct wpa_supplicant *wpa_s, + char *buf, size_t buflen) +{ + struct wpa_radio_work *work; + char *pos, *end; + struct os_reltime now, diff; + + pos = buf; + end = buf + buflen; + + os_get_reltime(&now); + + dl_list_for_each(work, &wpa_s->radio->work, struct wpa_radio_work, list) + { + int ret; + + os_reltime_sub(&now, &work->time, &diff); + ret = os_snprintf(pos, end - pos, "%s@%s:%u:%u:%ld.%06ld\n", + work->type, work->wpa_s->ifname, work->freq, + work->started, diff.sec, diff.usec); + if (ret < 0 || ret >= end - pos) + break; + pos += ret; + } + + return pos - buf; +} + + +static void wpas_ctrl_radio_work_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_radio_work *work = eloop_ctx; + struct wpa_external_work *ework = work->ctx; + + wpa_dbg(work->wpa_s, MSG_DEBUG, + "Timing out external radio work %u (%s)", + ework->id, work->type); + wpa_msg(work->wpa_s, MSG_INFO, EXT_RADIO_WORK_TIMEOUT "%u", ework->id); + os_free(ework); + radio_work_done(work); +} + + +static void wpas_ctrl_radio_work_cb(struct wpa_radio_work *work, int deinit) +{ + struct wpa_external_work *ework = work->ctx; + + if (deinit) { + os_free(ework); + return; + } + + wpa_dbg(work->wpa_s, MSG_DEBUG, "Starting external radio work %u (%s)", + ework->id, ework->type); + wpa_msg(work->wpa_s, MSG_INFO, EXT_RADIO_WORK_START "%u", ework->id); + if (!ework->timeout) + ework->timeout = 10; + eloop_register_timeout(ework->timeout, 0, wpas_ctrl_radio_work_timeout, + work, NULL); +} + + +static int wpas_ctrl_radio_work_add(struct wpa_supplicant *wpa_s, char *cmd, + char *buf, size_t buflen) +{ + struct wpa_external_work *ework; + char *pos, *pos2; + size_t type_len; + int ret; + unsigned int freq = 0; + + /* format: [freq=] [timeout=] */ + + ework = os_zalloc(sizeof(*ework)); + if (ework == NULL) + return -1; + + pos = os_strchr(cmd, ' '); + if (pos) { + type_len = pos - cmd; + pos++; + + pos2 = os_strstr(pos, "freq="); + if (pos2) + freq = atoi(pos2 + 5); + + pos2 = os_strstr(pos, "timeout="); + if (pos2) + ework->timeout = atoi(pos2 + 8); + } else { + type_len = os_strlen(cmd); + } + if (4 + type_len >= sizeof(ework->type)) + type_len = sizeof(ework->type) - 4 - 1; + os_strlcpy(ework->type, "ext:", sizeof(ework->type)); + os_memcpy(ework->type + 4, cmd, type_len); + ework->type[4 + type_len] = '\0'; + + wpa_s->ext_work_id++; + if (wpa_s->ext_work_id == 0) + wpa_s->ext_work_id++; + ework->id = wpa_s->ext_work_id; + + if (radio_add_work(wpa_s, freq, ework->type, 0, wpas_ctrl_radio_work_cb, + ework) < 0) { + os_free(ework); + return -1; + } + + ret = os_snprintf(buf, buflen, "%u", ework->id); + if (ret < 0 || (size_t) ret >= buflen) + return -1; + return ret; } -static int p2p_ctrl_ext_listen(struct wpa_supplicant *wpa_s, char *cmd) +static int wpas_ctrl_radio_work_done(struct wpa_supplicant *wpa_s, char *cmd) { - char *pos; - unsigned int period = 0, interval = 0; + struct wpa_radio_work *work; + unsigned int id = atoi(cmd); - if (cmd[0]) { - pos = os_strchr(cmd, ' '); - if (pos == NULL) - return -1; - *pos++ = '\0'; - period = atoi(cmd); - interval = atoi(pos); + dl_list_for_each(work, &wpa_s->radio->work, struct wpa_radio_work, list) + { + struct wpa_external_work *ework; + + if (os_strncmp(work->type, "ext:", 4) != 0) + continue; + ework = work->ctx; + if (id && ework->id != id) + continue; + wpa_dbg(wpa_s, MSG_DEBUG, + "Completed external radio work %u (%s)", + ework->id, ework->type); + eloop_cancel_timeout(wpas_ctrl_radio_work_timeout, work, NULL); + os_free(ework); + radio_work_done(work); + return 3; /* "OK\n" */ } - return wpas_p2p_ext_listen(wpa_s, period, interval); + return -1; } -#endif /* CONFIG_P2P */ +static int wpas_ctrl_radio_work(struct wpa_supplicant *wpa_s, char *cmd, + char *buf, size_t buflen) +{ + if (os_strcmp(cmd, "show") == 0) + return wpas_ctrl_radio_work_show(wpa_s, buf, buflen); + if (os_strncmp(cmd, "add ", 4) == 0) + return wpas_ctrl_radio_work_add(wpa_s, cmd + 4, buf, buflen); + if (os_strncmp(cmd, "done ", 5) == 0) + return wpas_ctrl_radio_work_done(wpa_s, cmd + 4); + return -1; +} -#ifdef CONFIG_INTERWORKING -static int ctrl_interworking_connect(struct wpa_supplicant *wpa_s, char *dst) + +void wpas_ctrl_radio_work_flush(struct wpa_supplicant *wpa_s) { - u8 bssid[ETH_ALEN]; - struct wpa_bss *bss; + struct wpa_radio_work *work, *tmp; - if (hwaddr_aton(dst, bssid)) { - wpa_printf(MSG_DEBUG, "Invalid BSSID '%s'", dst); - return -1; - } + if (!wpa_s || !wpa_s->radio) + return; - bss = wpa_bss_get_bssid(wpa_s, bssid); - if (bss == NULL) { - wpa_printf(MSG_DEBUG, "Could not find BSS " MACSTR, - MAC2STR(bssid)); - return -1; + dl_list_for_each_safe(work, tmp, &wpa_s->radio->work, + struct wpa_radio_work, list) { + struct wpa_external_work *ework; + + if (os_strncmp(work->type, "ext:", 4) != 0) + continue; + ework = work->ctx; + wpa_dbg(wpa_s, MSG_DEBUG, + "Flushing %sexternal radio work %u (%s)", + work->started ? " started" : "", ework->id, + ework->type); + if (work->started) + eloop_cancel_timeout(wpas_ctrl_radio_work_timeout, + work, NULL); + os_free(ework); + radio_work_done(work); } +} - return interworking_connect(wpa_s, bss); + +static void wpas_ctrl_eapol_response(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + eapol_sm_notify_ctrl_response(wpa_s->eapol); } -static int get_anqp(struct wpa_supplicant *wpa_s, char *dst) +static int set_scan_freqs(struct wpa_supplicant *wpa_s, char *val) { - u8 dst_addr[ETH_ALEN]; - int used; - char *pos; -#define MAX_ANQP_INFO_ID 100 - u16 id[MAX_ANQP_INFO_ID]; - size_t num_id = 0; + int *freqs = NULL; - used = hwaddr_aton2(dst, dst_addr); - if (used < 0) + freqs = freq_range_to_channel_list(wpa_s, val); + if (freqs == NULL) return -1; - pos = dst + used; - while (num_id < MAX_ANQP_INFO_ID) { - id[num_id] = atoi(pos); - if (id[num_id]) - num_id++; - pos = os_strchr(pos + 1, ','); - if (pos == NULL) - break; - pos++; - } - if (num_id == 0) - return -1; + os_free(wpa_s->manual_scan_freqs); + wpa_s->manual_scan_freqs = freqs; - return anqp_send_req(wpa_s, dst_addr, id, num_id); + return 0; } -#endif /* CONFIG_INTERWORKING */ -static int wpa_supplicant_ctrl_iface_sta_autoconnect( - struct wpa_supplicant *wpa_s, char *cmd) +static void wpas_ctrl_scan(struct wpa_supplicant *wpa_s, char *params, + char *reply, int reply_size, int *reply_len) { - wpa_s->auto_reconnect_disabled = atoi(cmd) == 0 ? 1 : 0; - return 0; -} + char *pos; + if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) { + *reply_len = -1; + return; + } + + wpa_s->manual_scan_passive = 0; + wpa_s->manual_scan_use_id = 0; + wpa_s->manual_scan_only_new = 0; + + if (params) { + if (os_strncasecmp(params, "TYPE=ONLY", 9) == 0) + wpa_s->scan_res_handler = scan_only_handler; + + pos = os_strstr(params, "freq="); + if (pos && set_scan_freqs(wpa_s, pos + 5) < 0) { + *reply_len = -1; + return; + } -static int wpa_supplicant_signal_poll(struct wpa_supplicant *wpa_s, char *buf, - size_t buflen) -{ - struct wpa_signal_info si; - int ret; + pos = os_strstr(params, "passive="); + if (pos) + wpa_s->manual_scan_passive = !!atoi(pos + 8); - ret = wpa_drv_signal_poll(wpa_s, &si); - if (ret) - return -1; + pos = os_strstr(params, "use_id="); + if (pos) + wpa_s->manual_scan_use_id = atoi(pos + 7); - ret = os_snprintf(buf, buflen, "RSSI=%d\nLINKSPEED=%d\n" - "NOISE=%d\nFREQUENCY=%u\n", - si.current_signal, si.current_txrate / 1000, - si.current_noise, si.frequency); - if (ret < 0 || (unsigned int) ret > buflen) - return -1; - return ret; + pos = os_strstr(params, "only_new=1"); + if (pos) + wpa_s->manual_scan_only_new = 1; + } else { + os_free(wpa_s->manual_scan_freqs); + wpa_s->manual_scan_freqs = NULL; + if (wpa_s->scan_res_handler == scan_only_handler) + wpa_s->scan_res_handler = NULL; + } + + if (!wpa_s->sched_scanning && !wpa_s->scanning && + ((wpa_s->wpa_state <= WPA_SCANNING) || + (wpa_s->wpa_state == WPA_COMPLETED))) { + wpa_s->normal_scans = 0; + wpa_s->scan_req = MANUAL_SCAN_REQ; + wpa_s->after_wps = 0; + wpa_s->known_wps_freq = 0; + wpa_supplicant_req_scan(wpa_s, 0, 0); + if (wpa_s->manual_scan_use_id) { + wpa_s->manual_scan_id++; + wpa_dbg(wpa_s, MSG_DEBUG, "Assigned scan id %u", + wpa_s->manual_scan_id); + *reply_len = os_snprintf(reply, reply_size, "%u\n", + wpa_s->manual_scan_id); + } + } else if (wpa_s->sched_scanning) { + wpa_printf(MSG_DEBUG, "Stop ongoing sched_scan to allow requested full scan to proceed"); + wpa_supplicant_cancel_sched_scan(wpa_s); + wpa_s->scan_req = MANUAL_SCAN_REQ; + wpa_supplicant_req_scan(wpa_s, 0, 0); + if (wpa_s->manual_scan_use_id) { + wpa_s->manual_scan_id++; + *reply_len = os_snprintf(reply, reply_size, "%u\n", + wpa_s->manual_scan_id); + wpa_dbg(wpa_s, MSG_DEBUG, "Assigned scan id %u", + wpa_s->manual_scan_id); + } + } else { + wpa_printf(MSG_DEBUG, "Ongoing scan action - reject new request"); + *reply_len = os_snprintf(reply, reply_size, "FAIL-BUSY\n"); + } } @@ -3282,19 +5751,29 @@ { char *reply; const int reply_size = 4096; - int ctrl_rsp = 0; int reply_len; if (os_strncmp(buf, WPA_CTRL_RSP, os_strlen(WPA_CTRL_RSP)) == 0 || os_strncmp(buf, "SET_NETWORK ", 12) == 0) { + if (wpa_debug_show_keys) + wpa_dbg(wpa_s, MSG_DEBUG, + "Control interface command '%s'", buf); + else + wpa_dbg(wpa_s, MSG_DEBUG, + "Control interface command '%s [REMOVED]'", + os_strncmp(buf, WPA_CTRL_RSP, + os_strlen(WPA_CTRL_RSP)) == 0 ? + WPA_CTRL_RSP : "SET_NETWORK"); + } else if (os_strncmp(buf, "WPS_NFC_TAG_READ", 16) == 0 || + os_strncmp(buf, "NFC_REPORT_HANDOVER", 19) == 0 || + os_strncmp(buf, "NFC_RX_HANDOVER_SEL", 19) == 0) { wpa_hexdump_ascii_key(MSG_DEBUG, "RX ctrl_iface", (const u8 *) buf, os_strlen(buf)); } else { int level = MSG_DEBUG; if (os_strcmp(buf, "PING") == 0) level = MSG_EXCESSIVE; - wpa_hexdump_ascii(level, "RX ctrl_iface", - (const u8 *) buf, os_strlen(buf)); + wpa_dbg(wpa_s, level, "Control interface command '%s'", buf); } reply = os_malloc(reply_size); @@ -3309,6 +5788,9 @@ if (os_strcmp(buf, "PING") == 0) { os_memcpy(reply, "PONG\n", 5); reply_len = 5; + } else if (os_strcmp(buf, "IFNAME") == 0) { + reply_len = os_strlen(wpa_s->ifname); + os_memcpy(reply, wpa_s->ifname, reply_len); } else if (os_strncmp(buf, "RELOG", 5) == 0) { if (wpa_debug_reopen_file() < 0) reply_len = -1; @@ -3344,19 +5826,13 @@ } else if (os_strcmp(buf, "REASSOCIATE") == 0) { if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) reply_len = -1; - else { - wpa_s->disconnected = 0; - wpa_s->reassociate = 1; - wpa_supplicant_req_scan(wpa_s, 0, 0); - } + else + wpas_request_connection(wpa_s); } else if (os_strcmp(buf, "RECONNECT") == 0) { if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) reply_len = -1; - else if (wpa_s->disconnected) { - wpa_s->disconnected = 0; - wpa_s->reassociate = 1; - wpa_supplicant_req_scan(wpa_s, 0, 0); - } + else if (wpa_s->disconnected) + wpas_request_connection(wpa_s); #ifdef IEEE8021X_EAPOL } else if (os_strncmp(buf, "PREAUTH ", 8) == 0) { if (wpa_supplicant_ctrl_iface_preauth(wpa_s, buf + 8)) @@ -3397,11 +5873,39 @@ } else if (os_strcmp(buf, "WPS_CANCEL") == 0) { if (wpas_wps_cancel(wpa_s)) reply_len = -1; -#ifdef CONFIG_WPS_OOB - } else if (os_strncmp(buf, "WPS_OOB ", 8) == 0) { - if (wpa_supplicant_ctrl_iface_wps_oob(wpa_s, buf + 8)) +#ifdef CONFIG_WPS_NFC + } else if (os_strcmp(buf, "WPS_NFC") == 0) { + if (wpa_supplicant_ctrl_iface_wps_nfc(wpa_s, NULL)) + reply_len = -1; + } else if (os_strncmp(buf, "WPS_NFC ", 8) == 0) { + if (wpa_supplicant_ctrl_iface_wps_nfc(wpa_s, buf + 8)) + reply_len = -1; + } else if (os_strncmp(buf, "WPS_NFC_CONFIG_TOKEN ", 21) == 0) { + reply_len = wpa_supplicant_ctrl_iface_wps_nfc_config_token( + wpa_s, buf + 21, reply, reply_size); + } else if (os_strncmp(buf, "WPS_NFC_TOKEN ", 14) == 0) { + reply_len = wpa_supplicant_ctrl_iface_wps_nfc_token( + wpa_s, buf + 14, reply, reply_size); + } else if (os_strncmp(buf, "WPS_NFC_TAG_READ ", 17) == 0) { + if (wpa_supplicant_ctrl_iface_wps_nfc_tag_read(wpa_s, + buf + 17)) + reply_len = -1; + } else if (os_strncmp(buf, "NFC_GET_HANDOVER_REQ ", 21) == 0) { + reply_len = wpas_ctrl_nfc_get_handover_req( + wpa_s, buf + 21, reply, reply_size); + } else if (os_strncmp(buf, "NFC_GET_HANDOVER_SEL ", 21) == 0) { + reply_len = wpas_ctrl_nfc_get_handover_sel( + wpa_s, buf + 21, reply, reply_size); + } else if (os_strncmp(buf, "NFC_RX_HANDOVER_REQ ", 20) == 0) { + reply_len = wpas_ctrl_nfc_rx_handover_req( + wpa_s, buf + 20, reply, reply_size); + } else if (os_strncmp(buf, "NFC_RX_HANDOVER_SEL ", 20) == 0) { + if (wpas_ctrl_nfc_rx_handover_sel(wpa_s, buf + 20)) reply_len = -1; -#endif /* CONFIG_WPS_OOB */ + } else if (os_strncmp(buf, "NFC_REPORT_HANDOVER ", 20) == 0) { + if (wpas_ctrl_nfc_report_handover(wpa_s, buf + 20)) + reply_len = -1; +#endif /* CONFIG_WPS_NFC */ } else if (os_strncmp(buf, "WPS_REG ", 8) == 0) { if (wpa_supplicant_ctrl_iface_wps_reg(wpa_s, buf + 8)) reply_len = -1; @@ -3446,6 +5950,11 @@ } else if (os_strncmp(buf, "WPS_ER_CONFIG ", 14) == 0) { if (wpa_supplicant_ctrl_iface_wps_er_config(wpa_s, buf + 14)) reply_len = -1; +#ifdef CONFIG_WPS_NFC + } else if (os_strncmp(buf, "WPS_ER_NFC_CONFIG_TOKEN ", 24) == 0) { + reply_len = wpa_supplicant_ctrl_iface_wps_er_nfc_config_token( + wpa_s, buf + 24, reply, reply_size); +#endif /* CONFIG_WPS_NFC */ #endif /* CONFIG_WPS_ER */ #endif /* CONFIG_WPS */ #ifdef CONFIG_IBSS_RSN @@ -3475,7 +5984,7 @@ if (wpas_p2p_group_remove(wpa_s, buf + 17)) reply_len = -1; } else if (os_strcmp(buf, "P2P_GROUP_ADD") == 0) { - if (wpas_p2p_group_add(wpa_s, 0, 0)) + if (wpas_p2p_group_add(wpa_s, 0, 0, 0, 0)) reply_len = -1; } else if (os_strncmp(buf, "P2P_GROUP_ADD ", 14) == 0) { if (p2p_ctrl_group_add(wpa_s, buf + 14)) @@ -3520,10 +6029,7 @@ if (p2p_ctrl_set(wpa_s, buf + 8) < 0) reply_len = -1; } else if (os_strcmp(buf, "P2P_FLUSH") == 0) { - os_memset(wpa_s->p2p_auth_invite, 0, ETH_ALEN); - wpa_s->force_long_sd = 0; - if (wpa_s->global->p2p) - p2p_flush(wpa_s->global->p2p); + p2p_ctrl_flush(wpa_s); } else if (os_strncmp(buf, "P2P_UNAUTHORIZE ", 16) == 0) { if (wpas_p2p_unauthorize(wpa_s, buf + 16) < 0) reply_len = -1; @@ -3542,16 +6048,29 @@ } else if (os_strcmp(buf, "P2P_EXT_LISTEN") == 0) { if (p2p_ctrl_ext_listen(wpa_s, "") < 0) reply_len = -1; + } else if (os_strncmp(buf, "P2P_REMOVE_CLIENT ", 18) == 0) { + if (p2p_ctrl_remove_client(wpa_s, buf + 18) < 0) + reply_len = -1; #endif /* CONFIG_P2P */ +#ifdef CONFIG_WIFI_DISPLAY + } else if (os_strncmp(buf, "WFD_SUBELEM_SET ", 16) == 0) { + if (wifi_display_subelem_set(wpa_s->global, buf + 16) < 0) + reply_len = -1; + } else if (os_strncmp(buf, "WFD_SUBELEM_GET ", 16) == 0) { + reply_len = wifi_display_subelem_get(wpa_s->global, buf + 16, + reply, reply_size); +#endif /* CONFIG_WIFI_DISPLAY */ #ifdef CONFIG_INTERWORKING } else if (os_strcmp(buf, "FETCH_ANQP") == 0) { if (interworking_fetch_anqp(wpa_s) < 0) reply_len = -1; } else if (os_strcmp(buf, "STOP_FETCH_ANQP") == 0) { interworking_stop_fetch_anqp(wpa_s); - } else if (os_strncmp(buf, "INTERWORKING_SELECT", 19) == 0) { - if (interworking_select(wpa_s, os_strstr(buf + 19, "auto") != - NULL) < 0) + } else if (os_strcmp(buf, "INTERWORKING_SELECT") == 0) { + if (ctrl_interworking_select(wpa_s, NULL) < 0) + reply_len = -1; + } else if (os_strncmp(buf, "INTERWORKING_SELECT ", 20) == 0) { + if (ctrl_interworking_select(wpa_s, buf + 20) < 0) reply_len = -1; } else if (os_strncmp(buf, "INTERWORKING_CONNECT ", 21) == 0) { if (ctrl_interworking_connect(wpa_s, buf + 21) < 0) @@ -3559,14 +6078,34 @@ } else if (os_strncmp(buf, "ANQP_GET ", 9) == 0) { if (get_anqp(wpa_s, buf + 9) < 0) reply_len = -1; + } else if (os_strncmp(buf, "GAS_REQUEST ", 12) == 0) { + if (gas_request(wpa_s, buf + 12) < 0) + reply_len = -1; + } else if (os_strncmp(buf, "GAS_RESPONSE_GET ", 17) == 0) { + reply_len = gas_response_get(wpa_s, buf + 17, reply, + reply_size); #endif /* CONFIG_INTERWORKING */ +#ifdef CONFIG_HS20 + } else if (os_strncmp(buf, "HS20_ANQP_GET ", 14) == 0) { + if (get_hs20_anqp(wpa_s, buf + 14) < 0) + reply_len = -1; + } else if (os_strncmp(buf, "HS20_GET_NAI_HOME_REALM_LIST ", 29) == 0) { + if (hs20_get_nai_home_realm_list(wpa_s, buf + 29) < 0) + reply_len = -1; +#endif /* CONFIG_HS20 */ } else if (os_strncmp(buf, WPA_CTRL_RSP, os_strlen(WPA_CTRL_RSP)) == 0) { if (wpa_supplicant_ctrl_iface_ctrl_rsp( wpa_s, buf + os_strlen(WPA_CTRL_RSP))) reply_len = -1; - else - ctrl_rsp = 1; + else { + /* + * Notify response from timeout to allow the control + * interface response to be sent first. + */ + eloop_register_timeout(0, 0, wpas_ctrl_eapol_response, + wpa_s, NULL); + } } else if (os_strcmp(buf, "RECONFIGURE") == 0) { if (wpa_supplicant_reload_configuration(wpa_s)) reply_len = -1; @@ -3590,24 +6129,14 @@ #endif /* CONFIG_SME */ wpa_s->reassociate = 0; wpa_s->disconnected = 1; + wpa_supplicant_cancel_sched_scan(wpa_s); + wpa_supplicant_cancel_scan(wpa_s); wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_DEAUTH_LEAVING); } else if (os_strcmp(buf, "SCAN") == 0) { - if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) - reply_len = -1; - else { - if (!wpa_s->scanning && - ((wpa_s->wpa_state <= WPA_SCANNING) || - (wpa_s->wpa_state == WPA_COMPLETED))) { - wpa_s->scan_req = 2; - wpa_supplicant_req_scan(wpa_s, 0, 0); - } else { - wpa_printf(MSG_DEBUG, "Ongoing scan action - " - "reject new request"); - reply_len = os_snprintf(reply, reply_size, - "FAIL-BUSY\n"); - } - } + wpas_ctrl_scan(wpa_s, NULL, reply, reply_size, &reply_len); + } else if (os_strncmp(buf, "SCAN ", 5) == 0) { + wpas_ctrl_scan(wpa_s, buf + 5, reply, reply_size, &reply_len); } else if (os_strcmp(buf, "SCAN_RESULTS") == 0) { reply_len = wpa_supplicant_ctrl_iface_scan_results( wpa_s, reply, reply_size); @@ -3632,6 +6161,18 @@ } else if (os_strncmp(buf, "GET_NETWORK ", 12) == 0) { reply_len = wpa_supplicant_ctrl_iface_get_network( wpa_s, buf + 12, reply, reply_size); + } else if (os_strcmp(buf, "LIST_CREDS") == 0) { + reply_len = wpa_supplicant_ctrl_iface_list_creds( + wpa_s, reply, reply_size); + } else if (os_strcmp(buf, "ADD_CRED") == 0) { + reply_len = wpa_supplicant_ctrl_iface_add_cred( + wpa_s, reply, reply_size); + } else if (os_strncmp(buf, "REMOVE_CRED ", 12) == 0) { + if (wpa_supplicant_ctrl_iface_remove_cred(wpa_s, buf + 12)) + reply_len = -1; + } else if (os_strncmp(buf, "SET_CRED ", 9) == 0) { + if (wpa_supplicant_ctrl_iface_set_cred(wpa_s, buf + 9)) + reply_len = -1; #ifndef CONFIG_NO_CONFIG_WRITE } else if (os_strcmp(buf, "SAVE_CONFIG") == 0) { if (wpa_supplicant_ctrl_iface_save_config(wpa_s)) @@ -3664,6 +6205,15 @@ } else if (os_strncmp(buf, "STA-NEXT ", 9) == 0) { reply_len = ap_ctrl_iface_sta_next(wpa_s, buf + 9, reply, reply_size); + } else if (os_strncmp(buf, "DEAUTHENTICATE ", 15) == 0) { + if (ap_ctrl_iface_sta_deauthenticate(wpa_s, buf + 15)) + reply_len = -1; + } else if (os_strncmp(buf, "DISASSOCIATE ", 13) == 0) { + if (ap_ctrl_iface_sta_disassociate(wpa_s, buf + 13)) + reply_len = -1; + } else if (os_strncmp(buf, "CHAN_SWITCH ", 12) == 0) { + if (ap_ctrl_iface_chanswitch(wpa_s, buf + 12)) + reply_len = -1; #endif /* CONFIG_AP */ } else if (os_strcmp(buf, "SUSPEND") == 0) { wpas_notify_suspend(wpa_s->global); @@ -3684,6 +6234,9 @@ if (wpa_supplicant_ctrl_iface_bss_expire_count(wpa_s, buf + 17)) reply_len = -1; + } else if (os_strncmp(buf, "BSS_FLUSH ", 10) == 0) { + if (wpa_supplicant_ctrl_iface_bss_flush(wpa_s, buf + 10)) + reply_len = -1; #ifdef CONFIG_TDLS } else if (os_strncmp(buf, "TDLS_DISCOVER ", 14) == 0) { if (wpa_supplicant_ctrl_iface_tdls_discover(wpa_s, buf + 14)) @@ -3698,6 +6251,35 @@ } else if (os_strncmp(buf, "SIGNAL_POLL", 11) == 0) { reply_len = wpa_supplicant_signal_poll(wpa_s, reply, reply_size); + } else if (os_strncmp(buf, "PKTCNT_POLL", 11) == 0) { + reply_len = wpa_supplicant_pktcnt_poll(wpa_s, reply, + reply_size); +#ifdef CONFIG_AUTOSCAN + } else if (os_strncmp(buf, "AUTOSCAN ", 9) == 0) { + if (wpa_supplicant_ctrl_iface_autoscan(wpa_s, buf + 9)) + reply_len = -1; +#endif /* CONFIG_AUTOSCAN */ +#ifdef ANDROID + } else if (os_strncmp(buf, "DRIVER ", 7) == 0) { + reply_len = wpa_supplicant_driver_cmd(wpa_s, buf + 7, reply, + reply_size); +#endif /* ANDROID */ + } else if (os_strcmp(buf, "REAUTHENTICATE") == 0) { + pmksa_cache_clear_current(wpa_s->wpa); + eapol_sm_request_reauth(wpa_s->eapol); +#ifdef CONFIG_WNM + } else if (os_strncmp(buf, "WNM_SLEEP ", 10) == 0) { + if (wpas_ctrl_iface_wnm_sleep(wpa_s, buf + 10)) + reply_len = -1; + } else if (os_strncmp(buf, "WNM_BSS_QUERY ", 10) == 0) { + if (wpas_ctrl_iface_wnm_bss_query(wpa_s, buf + 10)) + reply_len = -1; +#endif /* CONFIG_WNM */ + } else if (os_strcmp(buf, "FLUSH") == 0) { + wpa_supplicant_ctrl_iface_flush(wpa_s); + } else if (os_strncmp(buf, "RADIO_WORK ", 11) == 0) { + reply_len = wpas_ctrl_radio_work(wpa_s, buf + 11, reply, + reply_size); } else { os_memcpy(reply, "UNKNOWN COMMAND\n", 16); reply_len = 16; @@ -3708,9 +6290,6 @@ reply_len = 5; } - if (ctrl_rsp) - eapol_sm_notify_ctrl_response(wpa_s->eapol); - *resp_len = reply_len; return reply; } @@ -3803,7 +6382,7 @@ wpa_s = wpa_supplicant_get_iface(global, cmd); if (wpa_s == NULL) return -1; - return wpa_supplicant_remove_iface(global, wpa_s); + return wpa_supplicant_remove_iface(global, wpa_s, 0); } @@ -3888,6 +6467,231 @@ } +static char * wpas_global_ctrl_iface_ifname(struct wpa_global *global, + const char *ifname, + char *cmd, size_t *resp_len) +{ + struct wpa_supplicant *wpa_s; + + for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) { + if (os_strcmp(ifname, wpa_s->ifname) == 0) + break; + } + + if (wpa_s == NULL) { + char *resp = os_strdup("FAIL-NO-IFNAME-MATCH\n"); + if (resp) + *resp_len = os_strlen(resp); + else + *resp_len = 1; + return resp; + } + + return wpa_supplicant_ctrl_iface_process(wpa_s, cmd, resp_len); +} + + +static char * wpas_global_ctrl_iface_redir_p2p(struct wpa_global *global, + char *buf, size_t *resp_len) +{ +#ifdef CONFIG_P2P + static const char * cmd[] = { + "LIST_NETWORKS", + "SAVE_CONFIG", + "P2P_FIND", + "P2P_STOP_FIND", + "P2P_LISTEN", + "P2P_GROUP_ADD", + "P2P_GET_PASSPHRASE", + "P2P_SERVICE_UPDATE", + "P2P_SERVICE_FLUSH", + "P2P_FLUSH", + "P2P_CANCEL", + "P2P_PRESENCE_REQ", + "P2P_EXT_LISTEN", + NULL + }; + static const char * prefix[] = { +#ifdef ANDROID + "DRIVER ", +#endif /* ANDROID */ + "GET_NETWORK ", + "REMOVE_NETWORK ", + "SET ", + "P2P_FIND ", + "P2P_CONNECT ", + "P2P_LISTEN ", + "P2P_GROUP_REMOVE ", + "P2P_GROUP_ADD ", + "P2P_PROV_DISC ", + "P2P_SERV_DISC_REQ ", + "P2P_SERV_DISC_CANCEL_REQ ", + "P2P_SERV_DISC_RESP ", + "P2P_SERV_DISC_EXTERNAL ", + "P2P_SERVICE_ADD ", + "P2P_SERVICE_DEL ", + "P2P_REJECT ", + "P2P_INVITE ", + "P2P_PEER ", + "P2P_SET ", + "P2P_UNAUTHORIZE ", + "P2P_PRESENCE_REQ ", + "P2P_EXT_LISTEN ", + "P2P_REMOVE_CLIENT ", + NULL + }; + int found = 0; + int i; + + if (global->p2p_init_wpa_s == NULL) + return NULL; + + for (i = 0; !found && cmd[i]; i++) { + if (os_strcmp(buf, cmd[i]) == 0) + found = 1; + } + + for (i = 0; !found && prefix[i]; i++) { + if (os_strncmp(buf, prefix[i], os_strlen(prefix[i])) == 0) + found = 1; + } + + if (found) + return wpa_supplicant_ctrl_iface_process(global->p2p_init_wpa_s, + buf, resp_len); +#endif /* CONFIG_P2P */ + return NULL; +} + + +static char * wpas_global_ctrl_iface_redir_wfd(struct wpa_global *global, + char *buf, size_t *resp_len) +{ +#ifdef CONFIG_WIFI_DISPLAY + if (global->p2p_init_wpa_s == NULL) + return NULL; + if (os_strncmp(buf, "WFD_SUBELEM_SET ", 16) == 0 || + os_strncmp(buf, "WFD_SUBELEM_GET ", 16) == 0) + return wpa_supplicant_ctrl_iface_process(global->p2p_init_wpa_s, + buf, resp_len); +#endif /* CONFIG_WIFI_DISPLAY */ + return NULL; +} + + +static char * wpas_global_ctrl_iface_redir(struct wpa_global *global, + char *buf, size_t *resp_len) +{ + char *ret; + + ret = wpas_global_ctrl_iface_redir_p2p(global, buf, resp_len); + if (ret) + return ret; + + ret = wpas_global_ctrl_iface_redir_wfd(global, buf, resp_len); + if (ret) + return ret; + + return NULL; +} + + +static int wpas_global_ctrl_iface_set(struct wpa_global *global, char *cmd) +{ + char *value; + + value = os_strchr(cmd, ' '); + if (value == NULL) + return -1; + *value++ = '\0'; + + wpa_printf(MSG_DEBUG, "GLOBAL_CTRL_IFACE SET '%s'='%s'", cmd, value); + +#ifdef CONFIG_WIFI_DISPLAY + if (os_strcasecmp(cmd, "wifi_display") == 0) { + wifi_display_enable(global, !!atoi(value)); + return 0; + } +#endif /* CONFIG_WIFI_DISPLAY */ + + return -1; +} + + +#ifndef CONFIG_NO_CONFIG_WRITE +static int wpas_global_ctrl_iface_save_config(struct wpa_global *global) +{ + int ret = 0; + struct wpa_supplicant *wpa_s; + + for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) { + if (!wpa_s->conf->update_config) { + wpa_dbg(wpa_s, MSG_DEBUG, "CTRL_IFACE: SAVE_CONFIG - Not allowed to update configuration (update_config=0)"); + continue; + } + + if (wpa_config_write(wpa_s->confname, wpa_s->conf)) { + wpa_dbg(wpa_s, MSG_DEBUG, "CTRL_IFACE: SAVE_CONFIG - Failed to update configuration"); + ret = 1; + } else { + wpa_dbg(wpa_s, MSG_DEBUG, "CTRL_IFACE: SAVE_CONFIG - Configuration updated"); + } + } + + return ret; +} +#endif /* CONFIG_NO_CONFIG_WRITE */ + + +static int wpas_global_ctrl_iface_status(struct wpa_global *global, + char *buf, size_t buflen) +{ + char *pos, *end; + int ret; + struct wpa_supplicant *wpa_s; + + pos = buf; + end = buf + buflen; + +#ifdef CONFIG_P2P + if (global->p2p && !global->p2p_disabled) { + ret = os_snprintf(pos, end - pos, "p2p_device_address=" MACSTR + "\n" + "p2p_state=%s\n", + MAC2STR(global->p2p_dev_addr), + p2p_get_state_txt(global->p2p)); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + } else if (global->p2p) { + ret = os_snprintf(pos, end - pos, "p2p_state=DISABLED\n"); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + } +#endif /* CONFIG_P2P */ + +#ifdef CONFIG_WIFI_DISPLAY + ret = os_snprintf(pos, end - pos, "wifi_display=%d\n", + !!global->wifi_display); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; +#endif /* CONFIG_WIFI_DISPLAY */ + + for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) { + ret = os_snprintf(pos, end - pos, "ifname=%s\n" + "address=" MACSTR "\n", + wpa_s->ifname, MAC2STR(wpa_s->own_addr)); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + } + + return pos - buf; +} + + char * wpa_supplicant_global_ctrl_iface_process(struct wpa_global *global, char *buf, size_t *resp_len) { @@ -3896,6 +6700,20 @@ int reply_len; int level = MSG_DEBUG; + if (os_strncmp(buf, "IFNAME=", 7) == 0) { + char *pos = os_strchr(buf + 7, ' '); + if (pos) { + *pos++ = '\0'; + return wpas_global_ctrl_iface_ifname(global, + buf + 7, pos, + resp_len); + } + } + + reply = wpas_global_ctrl_iface_redir(global, buf, resp_len); + if (reply) + return reply; + if (os_strcmp(buf, "PING") == 0) level = MSG_EXCESSIVE; wpa_hexdump_ascii(level, "RX global ctrl_iface", @@ -3931,6 +6749,17 @@ wpas_notify_suspend(global); } else if (os_strcmp(buf, "RESUME") == 0) { wpas_notify_resume(global); + } else if (os_strncmp(buf, "SET ", 4) == 0) { + if (wpas_global_ctrl_iface_set(global, buf + 4)) + reply_len = -1; +#ifndef CONFIG_NO_CONFIG_WRITE + } else if (os_strcmp(buf, "SAVE_CONFIG") == 0) { + if (wpas_global_ctrl_iface_save_config(global)) + reply_len = -1; +#endif /* CONFIG_NO_CONFIG_WRITE */ + } else if (os_strcmp(buf, "STATUS") == 0) { + reply_len = wpas_global_ctrl_iface_status(global, reply, + reply_size); } else { os_memcpy(reply, "UNKNOWN COMMAND\n", 16); reply_len = 16; diff -Nru wpa-1.0/wpa_supplicant/ctrl_iface.h wpa-2.1/wpa_supplicant/ctrl_iface.h --- wpa-1.0/wpa_supplicant/ctrl_iface.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/ctrl_iface.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * WPA Supplicant / UNIX domain socket -based control interface * Copyright (c) 2004-2005, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef CTRL_IFACE_H @@ -95,21 +89,6 @@ void wpa_supplicant_ctrl_iface_wait(struct ctrl_iface_priv *priv); /** - * wpa_supplicant_ctrl_iface_ctrl_rsp_handle - Handle a control response - * @wpa_s: Pointer to wpa_supplicant data - * @ssid: Pointer to the network block the reply is for - * @field: field the response is a reply for - * @value: value (ie, password, etc) for @field - * Returns: 0 on success, non-zero on error - * - * Helper function to handle replies to control interface requests. - */ -int wpa_supplicant_ctrl_iface_ctrl_rsp_handle(struct wpa_supplicant *wpa_s, - struct wpa_ssid *ssid, - const char *field, - const char *value); - -/** * wpa_supplicant_global_ctrl_iface_init - Initialize global control interface * @global: Pointer to global data from wpa_supplicant_init() * Returns: Pointer to private data on success, %NULL on failure @@ -134,6 +113,8 @@ void wpa_supplicant_global_ctrl_iface_deinit( struct ctrl_iface_global_priv *priv); +void wpas_ctrl_radio_work_flush(struct wpa_supplicant *wpa_s); + #else /* CONFIG_CTRL_IFACE */ static inline struct ctrl_iface_priv * @@ -169,6 +150,10 @@ { } +static inline void wpas_ctrl_radio_work_flush(struct wpa_supplicant *wpa_s) +{ +} + #endif /* CONFIG_CTRL_IFACE */ #endif /* CTRL_IFACE_H */ diff -Nru wpa-1.0/wpa_supplicant/ctrl_iface_named_pipe.c wpa-2.1/wpa_supplicant/ctrl_iface_named_pipe.c --- wpa-1.0/wpa_supplicant/ctrl_iface_named_pipe.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/ctrl_iface_named_pipe.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * WPA Supplicant / Windows Named Pipe -based control interface * Copyright (c) 2004-2006, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -429,7 +423,7 @@ } -static void wpa_supplicant_ctrl_iface_msg_cb(void *ctx, int level, +static void wpa_supplicant_ctrl_iface_msg_cb(void *ctx, int level, int global, const char *txt, size_t len) { struct wpa_supplicant *wpa_s = ctx; diff -Nru wpa-1.0/wpa_supplicant/ctrl_iface_udp.c wpa-2.1/wpa_supplicant/ctrl_iface_udp.c --- wpa-1.0/wpa_supplicant/ctrl_iface_udp.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/ctrl_iface_udp.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * WPA Supplicant / UDP socket -based control interface * Copyright (c) 2004-2005, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -86,14 +80,14 @@ while (dst) { if (from->sin_addr.s_addr == dst->addr.sin_addr.s_addr && from->sin_port == dst->addr.sin_port) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor detached " + "%s:%d", inet_ntoa(from->sin_addr), + ntohs(from->sin_port)); if (prev == NULL) priv->ctrl_dst = dst->next; else prev->next = dst->next; os_free(dst); - wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor detached " - "%s:%d", inet_ntoa(from->sin_addr), - ntohs(from->sin_port)); return 0; } prev = dst; @@ -169,6 +163,8 @@ perror("recvfrom(ctrl_iface)"); return; } + +#ifndef CONFIG_CTRL_IFACE_UDP_REMOTE if (from.sin_addr.s_addr != htonl((127 << 24) | 1)) { /* * The OS networking stack is expected to drop this kind of @@ -180,6 +176,8 @@ "source %s", inet_ntoa(from.sin_addr)); return; } +#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */ + buf[res] = '\0'; if (os_strcmp(buf, "GET_COOKIE") == 0) { @@ -257,7 +255,7 @@ } -static void wpa_supplicant_ctrl_iface_msg_cb(void *ctx, int level, +static void wpa_supplicant_ctrl_iface_msg_cb(void *ctx, int level, int global, const char *txt, size_t len) { struct wpa_supplicant *wpa_s = ctx; @@ -272,6 +270,7 @@ { struct ctrl_iface_priv *priv; struct sockaddr_in addr; + int port = WPA_CTRL_IFACE_PORT; priv = os_zalloc(sizeof(*priv)); if (priv == NULL) @@ -291,13 +290,25 @@ os_memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; +#ifdef CONFIG_CTRL_IFACE_UDP_REMOTE + addr.sin_addr.s_addr = INADDR_ANY; +#else /* CONFIG_CTRL_IFACE_UDP_REMOTE */ addr.sin_addr.s_addr = htonl((127 << 24) | 1); - addr.sin_port = htons(WPA_CTRL_IFACE_PORT); +#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */ +try_again: + addr.sin_port = htons(port); if (bind(priv->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + port--; + if ((WPA_CTRL_IFACE_PORT - port) < WPA_CTRL_IFACE_PORT_LIMIT) + goto try_again; perror("bind(AF_INET)"); goto fail; } +#ifdef CONFIG_CTRL_IFACE_UDP_REMOTE + wpa_msg(wpa_s, MSG_DEBUG, "ctrl_iface_init UDP port: %d", port); +#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */ + eloop_register_read_sock(priv->sock, wpa_supplicant_ctrl_iface_receive, wpa_s, priv); wpa_msg_register_cb(wpa_supplicant_ctrl_iface_msg_cb); @@ -320,13 +331,13 @@ eloop_unregister_read_sock(priv->sock); if (priv->ctrl_dst) { /* - * Wait a second before closing the control socket if + * Wait before closing the control socket if * there are any attached monitors in order to allow * them to receive any pending messages. */ wpa_printf(MSG_DEBUG, "CTRL_IFACE wait for attached " "monitors to receive messages"); - os_sleep(1, 0); + os_sleep(0, 100000); } close(priv->sock); priv->sock = -1; @@ -448,6 +459,8 @@ perror("recvfrom(ctrl_iface)"); return; } + +#ifndef CONFIG_CTRL_IFACE_UDP_REMOTE if (from.sin_addr.s_addr != htonl((127 << 24) | 1)) { /* * The OS networking stack is expected to drop this kind of @@ -459,6 +472,8 @@ "source %s", inet_ntoa(from.sin_addr)); return; } +#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */ + buf[res] = '\0'; if (os_strcmp(buf, "GET_COOKIE") == 0) { @@ -508,6 +523,7 @@ { struct ctrl_iface_global_priv *priv; struct sockaddr_in addr; + int port = WPA_GLOBAL_CTRL_IFACE_PORT; priv = os_zalloc(sizeof(*priv)); if (priv == NULL) @@ -529,13 +545,26 @@ os_memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; +#ifdef CONFIG_CTRL_IFACE_UDP_REMOTE + addr.sin_addr.s_addr = INADDR_ANY; +#else /* CONFIG_CTRL_IFACE_UDP_REMOTE */ addr.sin_addr.s_addr = htonl((127 << 24) | 1); - addr.sin_port = htons(WPA_GLOBAL_CTRL_IFACE_PORT); +#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */ +try_again: + addr.sin_port = htons(port); if (bind(priv->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + port++; + if ((port - WPA_GLOBAL_CTRL_IFACE_PORT) < + WPA_GLOBAL_CTRL_IFACE_PORT_LIMIT) + goto try_again; perror("bind(AF_INET)"); goto fail; } +#ifdef CONFIG_CTRL_IFACE_UDP_REMOTE + wpa_printf(MSG_DEBUG, "global_ctrl_iface_init UDP port: %d", port); +#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */ + eloop_register_read_sock(priv->sock, wpa_supplicant_global_ctrl_iface_receive, global, priv); diff -Nru wpa-1.0/wpa_supplicant/ctrl_iface_unix.c wpa-2.1/wpa_supplicant/ctrl_iface_unix.c --- wpa-1.0/wpa_supplicant/ctrl_iface_unix.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/ctrl_iface_unix.c 2014-02-04 11:23:35.000000000 +0000 @@ -1,15 +1,9 @@ /* * WPA Supplicant / UNIX domain socket -based control interface - * Copyright (c) 2004-2009, Jouni Malinen + * Copyright (c) 2004-2014, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -17,6 +11,8 @@ #include #include #include +#include +#include #ifdef ANDROID #include #endif /* ANDROID */ @@ -54,16 +50,32 @@ }; -static void wpa_supplicant_ctrl_iface_send(struct ctrl_iface_priv *priv, +struct ctrl_iface_global_priv { + struct wpa_global *global; + int sock; + struct dl_list ctrl_dst; +}; + + +static void wpa_supplicant_ctrl_iface_send(struct wpa_supplicant *wpa_s, + const char *ifname, int sock, + struct dl_list *ctrl_dst, int level, const char *buf, - size_t len); + size_t len, + struct ctrl_iface_priv *priv, + struct ctrl_iface_global_priv *gp); +static int wpas_ctrl_iface_reinit(struct wpa_supplicant *wpa_s, + struct ctrl_iface_priv *priv); +static int wpas_ctrl_iface_global_reinit(struct wpa_global *global, + struct ctrl_iface_global_priv *priv); -static int wpa_supplicant_ctrl_iface_attach(struct ctrl_iface_priv *priv, +static int wpa_supplicant_ctrl_iface_attach(struct dl_list *ctrl_dst, struct sockaddr_un *from, socklen_t fromlen) { struct wpa_ctrl_dst *dst; + char addr_txt[200]; dst = os_zalloc(sizeof(*dst)); if (dst == NULL) @@ -71,31 +83,35 @@ os_memcpy(&dst->addr, from, sizeof(struct sockaddr_un)); dst->addrlen = fromlen; dst->debug_level = MSG_INFO; - dl_list_add(&priv->ctrl_dst, &dst->list); - wpa_hexdump(MSG_DEBUG, "CTRL_IFACE monitor attached", - (u8 *) from->sun_path, - fromlen - offsetof(struct sockaddr_un, sun_path)); + dl_list_add(ctrl_dst, &dst->list); + printf_encode(addr_txt, sizeof(addr_txt), + (u8 *) from->sun_path, + fromlen - offsetof(struct sockaddr_un, sun_path)); + wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor attached %s", addr_txt); return 0; } -static int wpa_supplicant_ctrl_iface_detach(struct ctrl_iface_priv *priv, +static int wpa_supplicant_ctrl_iface_detach(struct dl_list *ctrl_dst, struct sockaddr_un *from, socklen_t fromlen) { struct wpa_ctrl_dst *dst; - dl_list_for_each(dst, &priv->ctrl_dst, struct wpa_ctrl_dst, list) { + dl_list_for_each(dst, ctrl_dst, struct wpa_ctrl_dst, list) { if (fromlen == dst->addrlen && os_memcmp(from->sun_path, dst->addr.sun_path, fromlen - offsetof(struct sockaddr_un, sun_path)) == 0) { + char addr_txt[200]; + printf_encode(addr_txt, sizeof(addr_txt), + (u8 *) from->sun_path, + fromlen - + offsetof(struct sockaddr_un, sun_path)); + wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor detached %s", + addr_txt); dl_list_del(&dst->list); os_free(dst); - wpa_hexdump(MSG_DEBUG, "CTRL_IFACE monitor detached", - (u8 *) from->sun_path, - fromlen - - offsetof(struct sockaddr_un, sun_path)); return 0; } } @@ -117,11 +133,13 @@ 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)); + char addr_txt[200]; dst->debug_level = atoi(level); + printf_encode(addr_txt, sizeof(addr_txt), + (u8 *) from->sun_path, fromlen - + offsetof(struct sockaddr_un, sun_path)); + wpa_printf(MSG_DEBUG, "CTRL_IFACE changed monitor level to %d for %s", + dst->debug_level, addr_txt); return 0; } } @@ -139,27 +157,30 @@ int res; struct sockaddr_un from; socklen_t fromlen = sizeof(from); - char *reply = NULL; + char *reply = NULL, *reply_buf = NULL; size_t reply_len = 0; int new_attached = 0; res = recvfrom(sock, buf, sizeof(buf) - 1, 0, (struct sockaddr *) &from, &fromlen); if (res < 0) { - perror("recvfrom(ctrl_iface)"); + wpa_printf(MSG_ERROR, "recvfrom(ctrl_iface): %s", + strerror(errno)); return; } buf[res] = '\0'; if (os_strcmp(buf, "ATTACH") == 0) { - if (wpa_supplicant_ctrl_iface_attach(priv, &from, fromlen)) + if (wpa_supplicant_ctrl_iface_attach(&priv->ctrl_dst, &from, + fromlen)) reply_len = 1; else { new_attached = 1; reply_len = 2; } } else if (os_strcmp(buf, "DETACH") == 0) { - if (wpa_supplicant_ctrl_iface_detach(priv, &from, fromlen)) + if (wpa_supplicant_ctrl_iface_detach(&priv->ctrl_dst, &from, + fromlen)) reply_len = 1; else reply_len = 2; @@ -170,21 +191,49 @@ else reply_len = 2; } else { - reply = wpa_supplicant_ctrl_iface_process(wpa_s, buf, - &reply_len); + reply_buf = wpa_supplicant_ctrl_iface_process(wpa_s, buf, + &reply_len); + reply = reply_buf; + } + + if (!reply && reply_len == 1) { + reply = "FAIL\n"; + reply_len = 5; + } else if (!reply && reply_len == 2) { + reply = "OK\n"; + reply_len = 3; } if (reply) { - sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from, - fromlen); - os_free(reply); - } else if (reply_len == 1) { - sendto(sock, "FAIL\n", 5, 0, (struct sockaddr *) &from, - fromlen); - } else if (reply_len == 2) { - sendto(sock, "OK\n", 3, 0, (struct sockaddr *) &from, - fromlen); + if (sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from, + fromlen) < 0) { + int _errno = errno; + wpa_dbg(wpa_s, MSG_DEBUG, + "ctrl_iface sendto failed: %d - %s", + _errno, strerror(_errno)); + if (_errno == ENOBUFS || _errno == EAGAIN) { + /* + * The socket send buffer could be full. This + * may happen if client programs are not + * receiving their pending messages. Close and + * reopen the socket as a workaround to avoid + * getting stuck being unable to send any new + * responses. + */ + sock = wpas_ctrl_iface_reinit(wpa_s, priv); + if (sock < 0) { + wpa_dbg(wpa_s, MSG_DEBUG, "Failed to reinitialize ctrl_iface socket"); + } + } + if (new_attached) { + wpa_dbg(wpa_s, MSG_DEBUG, "Failed to send response to ATTACH - detaching"); + new_attached = 0; + wpa_supplicant_ctrl_iface_detach( + &priv->ctrl_dst, &from, fromlen); + } + } } + os_free(reply_buf); if (new_attached) eapol_sm_notify_ctrl_attached(wpa_s->eapol); @@ -244,20 +293,38 @@ } -static void wpa_supplicant_ctrl_iface_msg_cb(void *ctx, int level, +static void wpa_supplicant_ctrl_iface_msg_cb(void *ctx, int level, int global, const char *txt, size_t len) { struct wpa_supplicant *wpa_s = ctx; - if (wpa_s == NULL || wpa_s->ctrl_iface == NULL) + + if (wpa_s == NULL) return; - wpa_supplicant_ctrl_iface_send(wpa_s->ctrl_iface, level, txt, len); + + if (global != 2 && wpa_s->global->ctrl_iface) { + struct ctrl_iface_global_priv *priv = wpa_s->global->ctrl_iface; + if (!dl_list_empty(&priv->ctrl_dst)) { + wpa_supplicant_ctrl_iface_send(wpa_s, global ? NULL : + wpa_s->ifname, + priv->sock, + &priv->ctrl_dst, + level, txt, len, NULL, + priv); + } + } + + if (wpa_s->ctrl_iface == NULL) + return; + wpa_supplicant_ctrl_iface_send(wpa_s, NULL, wpa_s->ctrl_iface->sock, + &wpa_s->ctrl_iface->ctrl_dst, + level, txt, len, wpa_s->ctrl_iface, + NULL); } -struct ctrl_iface_priv * -wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s) +static int wpas_ctrl_iface_open_sock(struct wpa_supplicant *wpa_s, + struct ctrl_iface_priv *priv) { - struct ctrl_iface_priv *priv; struct sockaddr_un addr; char *fname = NULL; gid_t gid = 0; @@ -265,16 +332,7 @@ char *buf, *dir = NULL, *gid_str = NULL; struct group *grp; char *endp; - - priv = os_zalloc(sizeof(*priv)); - if (priv == NULL) - return NULL; - dl_list_init(&priv->ctrl_dst); - priv->wpa_s = wpa_s; - priv->sock = -1; - - if (wpa_s->conf->ctrl_interface == NULL) - return priv; + int flags; buf = os_strdup(wpa_s->conf->ctrl_interface); if (buf == NULL) @@ -303,11 +361,28 @@ wpa_printf(MSG_DEBUG, "Using existing control " "interface directory."); } else { - perror("mkdir[ctrl_interface]"); + wpa_printf(MSG_ERROR, "mkdir[ctrl_interface=%s]: %s", + dir, strerror(errno)); goto fail; } } +#ifdef ANDROID + /* + * wpa_supplicant is started from /init.*.rc on Android and that seems + * to be using umask 0077 which would leave the control interface + * directory without group access. This breaks things since Wi-Fi + * framework assumes that this directory can be accessed by other + * applications in the wifi group. Fix this by adding group access even + * if umask value would prevent this. + */ + if (chmod(dir, S_IRWXU | S_IRWXG) < 0) { + wpa_printf(MSG_ERROR, "CTRL: Could not chmod directory: %s", + strerror(errno)); + /* Try to continue anyway */ + } +#endif /* ANDROID */ + if (gid_str) { grp = getgrnam(gid_str); if (grp) { @@ -331,7 +406,8 @@ } if (gid_set && chown(dir, -1, gid) < 0) { - perror("chown[ctrl_interface]"); + wpa_printf(MSG_ERROR, "chown[ctrl_interface=%s,gid=%d]: %s", + dir, (int) gid, strerror(errno)); goto fail; } @@ -351,7 +427,7 @@ priv->sock = socket(PF_UNIX, SOCK_DGRAM, 0); if (priv->sock < 0) { - perror("socket(PF_UNIX)"); + wpa_printf(MSG_ERROR, "socket(PF_UNIX): %s", strerror(errno)); goto fail; } @@ -373,15 +449,15 @@ " allow connections - assuming it was left" "over from forced program termination"); if (unlink(fname) < 0) { - perror("unlink[ctrl_iface]"); - wpa_printf(MSG_ERROR, "Could not unlink " - "existing ctrl_iface socket '%s'", - fname); + wpa_printf(MSG_ERROR, + "Could not unlink existing ctrl_iface socket '%s': %s", + fname, strerror(errno)); goto fail; } if (bind(priv->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - perror("bind(PF_UNIX)"); + wpa_printf(MSG_ERROR, "supp-ctrl-iface-init: bind(PF_UNIX): %s", + strerror(errno)); goto fail; } wpa_printf(MSG_DEBUG, "Successfully replaced leftover " @@ -398,12 +474,14 @@ } if (gid_set && chown(fname, -1, gid) < 0) { - perror("chown[ctrl_interface/ifname]"); + wpa_printf(MSG_ERROR, "chown[ctrl_interface=%s,gid=%d]: %s", + fname, (int) gid, strerror(errno)); goto fail; } if (chmod(fname, S_IRWXU | S_IRWXG) < 0) { - perror("chmod[ctrl_interface/ifname]"); + wpa_printf(MSG_ERROR, "chmod[ctrl_interface=%s]: %s", + fname, strerror(errno)); goto fail; } os_free(fname); @@ -411,23 +489,81 @@ #ifdef ANDROID havesock: #endif /* ANDROID */ + + /* + * Make socket non-blocking so that we don't hang forever if + * target dies unexpectedly. + */ + flags = fcntl(priv->sock, F_GETFL); + if (flags >= 0) { + flags |= O_NONBLOCK; + if (fcntl(priv->sock, F_SETFL, flags) < 0) { + wpa_printf(MSG_INFO, "fcntl(ctrl, O_NONBLOCK): %s", + strerror(errno)); + /* Not fatal, continue on.*/ + } + } + eloop_register_read_sock(priv->sock, wpa_supplicant_ctrl_iface_receive, wpa_s, priv); wpa_msg_register_cb(wpa_supplicant_ctrl_iface_msg_cb); os_free(buf); - return priv; + return 0; fail: - if (priv->sock >= 0) + if (priv->sock >= 0) { close(priv->sock); - os_free(priv); + priv->sock = -1; + } if (fname) { unlink(fname); os_free(fname); } os_free(buf); - return NULL; + return -1; +} + + +struct ctrl_iface_priv * +wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s) +{ + struct ctrl_iface_priv *priv; + + priv = os_zalloc(sizeof(*priv)); + if (priv == NULL) + return NULL; + dl_list_init(&priv->ctrl_dst); + priv->wpa_s = wpa_s; + priv->sock = -1; + + if (wpa_s->conf->ctrl_interface == NULL) + return priv; + + if (wpas_ctrl_iface_open_sock(wpa_s, priv) < 0) { + os_free(priv); + return NULL; + } + + return priv; +} + + +static int wpas_ctrl_iface_reinit(struct wpa_supplicant *wpa_s, + struct ctrl_iface_priv *priv) +{ + int res; + + if (priv->sock <= 0) + return -1; + + eloop_unregister_read_sock(priv->sock); + close(priv->sock); + priv->sock = -1; + res = wpas_ctrl_iface_open_sock(wpa_s, priv); + if (res < 0) + return -1; + return priv->sock; } @@ -441,13 +577,13 @@ eloop_unregister_read_sock(priv->sock); if (!dl_list_empty(&priv->ctrl_dst)) { /* - * Wait a second before closing the control socket if + * Wait before closing the control socket if * there are any attached monitors in order to allow * them to receive any pending messages. */ wpa_printf(MSG_DEBUG, "CTRL_IFACE wait for attached " "monitors to receive messages"); - os_sleep(1, 0); + os_sleep(0, 100000); } close(priv->sock); priv->sock = -1; @@ -457,6 +593,8 @@ os_free(fname); } + if (priv->wpa_s->conf->ctrl_interface == NULL) + goto free_dst; buf = os_strdup(priv->wpa_s->conf->ctrl_interface); if (buf == NULL) goto free_dst; @@ -476,7 +614,9 @@ "directory not empty - leaving it " "behind"); } else { - perror("rmdir[ctrl_interface]"); + wpa_printf(MSG_ERROR, + "rmdir[ctrl_interface=%s]: %s", + dir, strerror(errno)); } } os_free(buf); @@ -492,63 +632,109 @@ /** * wpa_supplicant_ctrl_iface_send - Send a control interface packet to monitors - * @priv: Pointer to private data from wpa_supplicant_ctrl_iface_init() + * @ifname: Interface name for global control socket or %NULL + * @sock: Local socket fd + * @ctrl_dst: List of attached listeners * @level: Priority level of the message * @buf: Message data * @len: Message length * * Send a packet to all monitor programs attached to the control interface. */ -static void wpa_supplicant_ctrl_iface_send(struct ctrl_iface_priv *priv, +static void wpa_supplicant_ctrl_iface_send(struct wpa_supplicant *wpa_s, + const char *ifname, int sock, + struct dl_list *ctrl_dst, int level, const char *buf, - size_t len) + size_t len, + struct ctrl_iface_priv *priv, + struct ctrl_iface_global_priv *gp) { struct wpa_ctrl_dst *dst, *next; char levelstr[10]; int idx, res; struct msghdr msg; - struct iovec io[2]; + struct iovec io[5]; - if (priv->sock < 0 || dl_list_empty(&priv->ctrl_dst)) + if (sock < 0 || dl_list_empty(ctrl_dst)) return; res = os_snprintf(levelstr, sizeof(levelstr), "<%d>", level); if (res < 0 || (size_t) res >= sizeof(levelstr)) return; - io[0].iov_base = levelstr; - io[0].iov_len = os_strlen(levelstr); - io[1].iov_base = (char *) buf; - io[1].iov_len = len; + idx = 0; + if (ifname) { + io[idx].iov_base = "IFNAME="; + io[idx].iov_len = 7; + idx++; + io[idx].iov_base = (char *) ifname; + io[idx].iov_len = os_strlen(ifname); + idx++; + io[idx].iov_base = " "; + io[idx].iov_len = 1; + idx++; + } + io[idx].iov_base = levelstr; + io[idx].iov_len = os_strlen(levelstr); + idx++; + io[idx].iov_base = (char *) buf; + io[idx].iov_len = len; + idx++; os_memset(&msg, 0, sizeof(msg)); msg.msg_iov = io; - msg.msg_iovlen = 2; + msg.msg_iovlen = idx; - idx = 0; - dl_list_for_each_safe(dst, next, &priv->ctrl_dst, struct wpa_ctrl_dst, - list) { - 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)); - msg.msg_name = (void *) &dst->addr; - msg.msg_namelen = dst->addrlen; - if (sendmsg(priv->sock, &msg, 0) < 0) { - int _errno = errno; - wpa_printf(MSG_INFO, "CTRL_IFACE monitor[%d]: " - "%d - %s", - idx, errno, strerror(errno)); - dst->errors++; - if (dst->errors > 1000 || - (_errno != ENOBUFS && dst->errors > 10) || - _errno == ENOENT) { - wpa_supplicant_ctrl_iface_detach( - priv, &dst->addr, - dst->addrlen); - } - } else - dst->errors = 0; + dl_list_for_each_safe(dst, next, ctrl_dst, struct wpa_ctrl_dst, list) { + int _errno; + char addr_txt[200]; + + if (level < dst->debug_level) + continue; + + printf_encode(addr_txt, sizeof(addr_txt), + (u8 *) dst->addr.sun_path, dst->addrlen - + offsetof(struct sockaddr_un, sun_path)); + msg.msg_name = (void *) &dst->addr; + msg.msg_namelen = dst->addrlen; + if (sendmsg(sock, &msg, MSG_DONTWAIT) >= 0) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor sent successfully to %s", + addr_txt); + dst->errors = 0; + continue; + } + + _errno = errno; + wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor[%s]: %d - %s", + addr_txt, errno, strerror(errno)); + dst->errors++; + + if (dst->errors > 10 || _errno == ENOENT || _errno == EPERM) { + wpa_printf(MSG_INFO, "CTRL_IFACE: Detach monitor %s that cannot receive messages", + addr_txt); + wpa_supplicant_ctrl_iface_detach(ctrl_dst, &dst->addr, + dst->addrlen); + } + + if (_errno == ENOBUFS || _errno == EAGAIN) { + /* + * The socket send buffer could be full. This may happen + * if client programs are not receiving their pending + * messages. Close and reopen the socket as a workaround + * to avoid getting stuck being unable to send any new + * responses. + */ + if (priv) + sock = wpas_ctrl_iface_reinit(wpa_s, priv); + else if (gp) + sock = wpas_ctrl_iface_global_reinit( + wpa_s->global, gp); + else + break; + if (sock < 0) { + wpa_dbg(wpa_s, MSG_DEBUG, + "Failed to reinitialize ctrl_iface socket"); + break; + } } - idx++; } } @@ -568,27 +754,40 @@ res = recvfrom(priv->sock, buf, sizeof(buf) - 1, 0, (struct sockaddr *) &from, &fromlen); if (res < 0) { - perror("recvfrom(ctrl_iface)"); + wpa_printf(MSG_ERROR, "recvfrom(ctrl_iface): %s", + strerror(errno)); continue; } buf[res] = '\0'; if (os_strcmp(buf, "ATTACH") == 0) { /* handle ATTACH signal of first monitor interface */ - if (!wpa_supplicant_ctrl_iface_attach(priv, &from, - fromlen)) { - sendto(priv->sock, "OK\n", 3, 0, - (struct sockaddr *) &from, fromlen); + if (!wpa_supplicant_ctrl_iface_attach(&priv->ctrl_dst, + &from, fromlen)) { + if (sendto(priv->sock, "OK\n", 3, 0, + (struct sockaddr *) &from, fromlen) < + 0) { + wpa_printf(MSG_DEBUG, "ctrl_iface sendto failed: %s", + strerror(errno)); + } /* OK to continue */ return; } else { - sendto(priv->sock, "FAIL\n", 5, 0, - (struct sockaddr *) &from, fromlen); + if (sendto(priv->sock, "FAIL\n", 5, 0, + (struct sockaddr *) &from, fromlen) < + 0) { + wpa_printf(MSG_DEBUG, "ctrl_iface sendto failed: %s", + strerror(errno)); + } } } else { /* return FAIL for all other signals */ - sendto(priv->sock, "FAIL\n", 5, 0, - (struct sockaddr *) &from, fromlen); + if (sendto(priv->sock, "FAIL\n", 5, 0, + (struct sockaddr *) &from, fromlen) < 0) { + wpa_printf(MSG_DEBUG, + "ctrl_iface sendto failed: %s", + strerror(errno)); + } } } } @@ -596,72 +795,105 @@ /* Global ctrl_iface */ -struct ctrl_iface_global_priv { - struct wpa_global *global; - int sock; -}; - - static void wpa_supplicant_global_ctrl_iface_receive(int sock, void *eloop_ctx, void *sock_ctx) { struct wpa_global *global = eloop_ctx; - char buf[256]; + struct ctrl_iface_global_priv *priv = sock_ctx; + char buf[4096]; int res; struct sockaddr_un from; socklen_t fromlen = sizeof(from); - char *reply; + char *reply = NULL, *reply_buf = NULL; size_t reply_len; res = recvfrom(sock, buf, sizeof(buf) - 1, 0, (struct sockaddr *) &from, &fromlen); if (res < 0) { - perror("recvfrom(ctrl_iface)"); + wpa_printf(MSG_ERROR, "recvfrom(ctrl_iface): %s", + strerror(errno)); return; } buf[res] = '\0'; - reply = wpa_supplicant_global_ctrl_iface_process(global, buf, - &reply_len); + if (os_strcmp(buf, "ATTACH") == 0) { + if (wpa_supplicant_ctrl_iface_attach(&priv->ctrl_dst, &from, + fromlen)) + reply_len = 1; + else + reply_len = 2; + } else if (os_strcmp(buf, "DETACH") == 0) { + if (wpa_supplicant_ctrl_iface_detach(&priv->ctrl_dst, &from, + fromlen)) + reply_len = 1; + else + reply_len = 2; + } else { + reply_buf = wpa_supplicant_global_ctrl_iface_process( + global, buf, &reply_len); + reply = reply_buf; + } + + if (!reply && reply_len == 1) { + reply = "FAIL\n"; + reply_len = 5; + } else if (!reply && reply_len == 2) { + reply = "OK\n"; + reply_len = 3; + } if (reply) { - sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from, - fromlen); - os_free(reply); - } else if (reply_len) { - sendto(sock, "FAIL\n", 5, 0, (struct sockaddr *) &from, - fromlen); + if (sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from, + fromlen) < 0) { + wpa_printf(MSG_DEBUG, "ctrl_iface sendto failed: %s", + strerror(errno)); + } } + os_free(reply_buf); } -struct ctrl_iface_global_priv * -wpa_supplicant_global_ctrl_iface_init(struct wpa_global *global) +static int wpas_global_ctrl_iface_open_sock(struct wpa_global *global, + struct ctrl_iface_global_priv *priv) { - struct ctrl_iface_global_priv *priv; struct sockaddr_un addr; + const char *ctrl = global->params.ctrl_interface; + int flags; - priv = os_zalloc(sizeof(*priv)); - if (priv == NULL) - return NULL; - priv->global = global; - priv->sock = -1; - - if (global->params.ctrl_interface == NULL) - return priv; + wpa_printf(MSG_DEBUG, "Global control interface '%s'", ctrl); #ifdef ANDROID - priv->sock = android_get_control_socket(global->params.ctrl_interface); - if (priv->sock >= 0) + if (os_strncmp(ctrl, "@android:", 9) == 0) { + priv->sock = android_get_control_socket(ctrl + 9); + if (priv->sock < 0) { + wpa_printf(MSG_ERROR, "Failed to open Android control " + "socket '%s'", ctrl + 9); + goto fail; + } + wpa_printf(MSG_DEBUG, "Using Android control socket '%s'", + ctrl + 9); goto havesock; -#endif /* ANDROID */ + } - wpa_printf(MSG_DEBUG, "Global control interface '%s'", - global->params.ctrl_interface); + if (os_strncmp(ctrl, "@abstract:", 10) != 0) { + /* + * Backwards compatibility - try to open an Android control + * socket and if that fails, assume this was a UNIX domain + * socket instead. + */ + priv->sock = android_get_control_socket(ctrl); + if (priv->sock >= 0) { + wpa_printf(MSG_DEBUG, + "Using Android control socket '%s'", + ctrl); + goto havesock; + } + } +#endif /* ANDROID */ priv->sock = socket(PF_UNIX, SOCK_DGRAM, 0); if (priv->sock < 0) { - perror("socket(PF_UNIX)"); + wpa_printf(MSG_ERROR, "socket(PF_UNIX): %s", strerror(errno)); goto fail; } @@ -670,65 +902,187 @@ addr.sun_len = sizeof(addr); #endif /* __FreeBSD__ */ addr.sun_family = AF_UNIX; - os_strlcpy(addr.sun_path, global->params.ctrl_interface, - sizeof(addr.sun_path)); + + if (os_strncmp(ctrl, "@abstract:", 10) == 0) { + addr.sun_path[0] = '\0'; + os_strlcpy(addr.sun_path + 1, ctrl + 10, + sizeof(addr.sun_path) - 1); + if (bind(priv->sock, (struct sockaddr *) &addr, sizeof(addr)) < + 0) { + wpa_printf(MSG_ERROR, "supp-global-ctrl-iface-init: " + "bind(PF_UNIX;%s) failed: %s", + ctrl, strerror(errno)); + goto fail; + } + wpa_printf(MSG_DEBUG, "Using Abstract control socket '%s'", + ctrl + 10); + goto havesock; + } + + os_strlcpy(addr.sun_path, ctrl, sizeof(addr.sun_path)); if (bind(priv->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - perror("bind(PF_UNIX)"); + wpa_printf(MSG_INFO, "supp-global-ctrl-iface-init(%s) (will try fixup): bind(PF_UNIX): %s", + ctrl, strerror(errno)); if (connect(priv->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { wpa_printf(MSG_DEBUG, "ctrl_iface exists, but does not" " allow connections - assuming it was left" "over from forced program termination"); - if (unlink(global->params.ctrl_interface) < 0) { - perror("unlink[ctrl_iface]"); - wpa_printf(MSG_ERROR, "Could not unlink " - "existing ctrl_iface socket '%s'", - global->params.ctrl_interface); + if (unlink(ctrl) < 0) { + wpa_printf(MSG_ERROR, + "Could not unlink existing ctrl_iface socket '%s': %s", + ctrl, strerror(errno)); goto fail; } if (bind(priv->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - perror("bind(PF_UNIX)"); + wpa_printf(MSG_ERROR, "supp-glb-iface-init: bind(PF_UNIX;%s): %s", + ctrl, strerror(errno)); goto fail; } wpa_printf(MSG_DEBUG, "Successfully replaced leftover " "ctrl_iface socket '%s'", - global->params.ctrl_interface); + ctrl); } else { wpa_printf(MSG_INFO, "ctrl_iface exists and seems to " "be in use - cannot override it"); wpa_printf(MSG_INFO, "Delete '%s' manually if it is " "not used anymore", - global->params.ctrl_interface); + ctrl); goto fail; } } -#ifdef ANDROID + wpa_printf(MSG_DEBUG, "Using UNIX control socket '%s'", ctrl); + + if (global->params.ctrl_interface_group) { + char *gid_str = global->params.ctrl_interface_group; + gid_t gid = 0; + struct group *grp; + char *endp; + + grp = getgrnam(gid_str); + if (grp) { + gid = grp->gr_gid; + wpa_printf(MSG_DEBUG, "ctrl_interface_group=%d" + " (from group name '%s')", + (int) gid, gid_str); + } else { + /* Group name not found - try to parse this as gid */ + gid = strtol(gid_str, &endp, 10); + if (*gid_str == '\0' || *endp != '\0') { + wpa_printf(MSG_ERROR, "CTRL: Invalid group " + "'%s'", gid_str); + goto fail; + } + wpa_printf(MSG_DEBUG, "ctrl_interface_group=%d", + (int) gid); + } + if (chown(ctrl, -1, gid) < 0) { + wpa_printf(MSG_ERROR, + "chown[global_ctrl_interface=%s,gid=%d]: %s", + ctrl, (int) gid, strerror(errno)); + goto fail; + } + + if (chmod(ctrl, S_IRWXU | S_IRWXG) < 0) { + wpa_printf(MSG_ERROR, + "chmod[global_ctrl_interface=%s]: %s", + ctrl, strerror(errno)); + goto fail; + } + } else { + chmod(ctrl, S_IRWXU); + } + havesock: -#endif /* ANDROID */ + + /* + * Make socket non-blocking so that we don't hang forever if + * target dies unexpectedly. + */ + flags = fcntl(priv->sock, F_GETFL); + if (flags >= 0) { + flags |= O_NONBLOCK; + if (fcntl(priv->sock, F_SETFL, flags) < 0) { + wpa_printf(MSG_INFO, "fcntl(ctrl, O_NONBLOCK): %s", + strerror(errno)); + /* Not fatal, continue on.*/ + } + } + eloop_register_read_sock(priv->sock, wpa_supplicant_global_ctrl_iface_receive, - global, NULL); + global, priv); - return priv; + return 0; fail: - if (priv->sock >= 0) + if (priv->sock >= 0) { close(priv->sock); - os_free(priv); - return NULL; + priv->sock = -1; + } + return -1; +} + + +struct ctrl_iface_global_priv * +wpa_supplicant_global_ctrl_iface_init(struct wpa_global *global) +{ + struct ctrl_iface_global_priv *priv; + + priv = os_zalloc(sizeof(*priv)); + if (priv == NULL) + return NULL; + dl_list_init(&priv->ctrl_dst); + priv->global = global; + priv->sock = -1; + + if (global->params.ctrl_interface == NULL) + return priv; + + if (wpas_global_ctrl_iface_open_sock(global, priv) < 0) { + os_free(priv); + return NULL; + } + + wpa_msg_register_cb(wpa_supplicant_ctrl_iface_msg_cb); + + return priv; +} + + +static int wpas_ctrl_iface_global_reinit(struct wpa_global *global, + struct ctrl_iface_global_priv *priv) +{ + int res; + + if (priv->sock <= 0) + return -1; + + eloop_unregister_read_sock(priv->sock); + close(priv->sock); + priv->sock = -1; + res = wpas_global_ctrl_iface_open_sock(global, priv); + if (res < 0) + return -1; + return priv->sock; } void wpa_supplicant_global_ctrl_iface_deinit(struct ctrl_iface_global_priv *priv) { + struct wpa_ctrl_dst *dst, *prev; + if (priv->sock >= 0) { eloop_unregister_read_sock(priv->sock); close(priv->sock); } if (priv->global->params.ctrl_interface) unlink(priv->global->params.ctrl_interface); + dl_list_for_each_safe(dst, prev, &priv->ctrl_dst, struct wpa_ctrl_dst, + list) + os_free(dst); os_free(priv); } diff -Nru wpa-1.0/wpa_supplicant/dbus/dbus_common.c wpa-2.1/wpa_supplicant/dbus/dbus_common.c --- wpa-1.0/wpa_supplicant/dbus/dbus_common.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/dbus/dbus_common.c 2014-02-04 11:23:35.000000000 +0000 @@ -4,14 +4,8 @@ * Copyright (c) 2009, Witold Sowa * Copyright (c) 2009, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "utils/includes.h" @@ -23,6 +17,7 @@ #include "dbus_common_i.h" #include "dbus_new.h" #include "dbus_old.h" +#include "../wpa_supplicant_i.h" #ifndef SIGPOLL @@ -263,6 +258,22 @@ } +static DBusHandlerResult disconnect_filter(DBusConnection *conn, + DBusMessage *message, void *data) +{ + struct wpas_dbus_priv *priv = data; + + if (dbus_message_is_signal(message, DBUS_INTERFACE_LOCAL, + "Disconnected")) { + wpa_printf(MSG_DEBUG, "dbus: bus disconnected, terminating"); + dbus_connection_set_exit_on_disconnect(conn, FALSE); + wpa_supplicant_terminate_proc(priv->global); + return DBUS_HANDLER_RESULT_HANDLED; + } else + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + + static int wpas_dbus_init_common(struct wpas_dbus_priv *priv) { DBusError error; @@ -271,7 +282,10 @@ /* Get a reference to the system bus */ dbus_error_init(&error); priv->con = dbus_bus_get(DBUS_BUS_SYSTEM, &error); - if (!priv->con) { + if (priv->con) { + dbus_connection_add_filter(priv->con, disconnect_filter, priv, + NULL); + } else { wpa_printf(MSG_ERROR, "dbus: Could not acquire the system " "bus: %s - %s", error.name, error.message); ret = -1; @@ -310,6 +324,9 @@ NULL, NULL, NULL); dbus_connection_set_timeout_functions(priv->con, NULL, NULL, NULL, NULL, NULL); + dbus_connection_remove_filter(priv->con, disconnect_filter, + priv); + dbus_connection_unref(priv->con); } diff -Nru wpa-1.0/wpa_supplicant/dbus/dbus_common.h wpa-2.1/wpa_supplicant/dbus/dbus_common.h --- wpa-1.0/wpa_supplicant/dbus/dbus_common.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/dbus/dbus_common.h 2014-02-04 11:23:35.000000000 +0000 @@ -4,14 +4,8 @@ * Copyright (c) 2009, Witold Sowa * Copyright (c) 2009, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef DBUS_COMMON_H diff -Nru wpa-1.0/wpa_supplicant/dbus/dbus_common_i.h wpa-2.1/wpa_supplicant/dbus/dbus_common_i.h --- wpa-1.0/wpa_supplicant/dbus/dbus_common_i.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/dbus/dbus_common_i.h 2014-02-04 11:23:35.000000000 +0000 @@ -4,14 +4,8 @@ * Copyright (c) 2009, Witold Sowa * Copyright (c) 2009, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef DBUS_COMMON_I_H @@ -25,6 +19,10 @@ struct wpa_global *global; u32 next_objid; int dbus_new_initialized; + +#if defined(CONFIG_CTRL_IFACE_DBUS_NEW) && defined(CONFIG_AP) + int dbus_noc_refcnt; +#endif /* CONFIG_CTRL_IFACE_DBUS_NEW && CONFIG_AP */ }; #endif /* DBUS_COMMON_I_H */ diff -Nru wpa-1.0/wpa_supplicant/dbus/dbus_dict_helpers.c wpa-2.1/wpa_supplicant/dbus/dbus_dict_helpers.c --- wpa-1.0/wpa_supplicant/dbus/dbus_dict_helpers.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/dbus/dbus_dict_helpers.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * WPA Supplicant / dbus-based control interface * Copyright (c) 2006, Dan Williams and Red Hat, Inc. * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -739,12 +733,12 @@ { dbus_uint32_t count = 0; dbus_bool_t success = FALSE; - char *buffer, *nbuffer;; + char *buffer, *nbuffer; entry->bytearray_value = NULL; entry->array_type = DBUS_TYPE_BYTE; - buffer = os_zalloc(BYTE_ARRAY_ITEM_SIZE * BYTE_ARRAY_CHUNK_SIZE); + buffer = os_calloc(BYTE_ARRAY_CHUNK_SIZE, BYTE_ARRAY_ITEM_SIZE); if (!buffer) return FALSE; @@ -754,8 +748,9 @@ char byte; if ((count % BYTE_ARRAY_CHUNK_SIZE) == 0 && count != 0) { - nbuffer = os_realloc(buffer, BYTE_ARRAY_ITEM_SIZE * - (count + BYTE_ARRAY_CHUNK_SIZE)); + nbuffer = os_realloc_array( + buffer, count + BYTE_ARRAY_CHUNK_SIZE, + BYTE_ARRAY_ITEM_SIZE); if (nbuffer == NULL) { os_free(buffer); wpa_printf(MSG_ERROR, "dbus: _wpa_dbus_dict_" @@ -801,7 +796,7 @@ entry->strarray_value = NULL; entry->array_type = DBUS_TYPE_STRING; - buffer = os_zalloc(STR_ARRAY_ITEM_SIZE * STR_ARRAY_CHUNK_SIZE); + buffer = os_calloc(STR_ARRAY_CHUNK_SIZE, STR_ARRAY_ITEM_SIZE); if (buffer == NULL) return FALSE; @@ -812,8 +807,9 @@ char *str; if ((count % STR_ARRAY_CHUNK_SIZE) == 0 && count != 0) { - nbuffer = os_realloc(buffer, STR_ARRAY_ITEM_SIZE * - (count + STR_ARRAY_CHUNK_SIZE)); + nbuffer = os_realloc_array( + buffer, count + STR_ARRAY_CHUNK_SIZE, + STR_ARRAY_ITEM_SIZE); if (nbuffer == NULL) { os_free(buffer); wpa_printf(MSG_ERROR, "dbus: _wpa_dbus_dict_" @@ -877,8 +873,8 @@ buflen += BIN_ARRAY_CHUNK_SIZE; - newbuf = os_realloc(entry->binarray_value, - buflen * BIN_ARRAY_ITEM_SIZE); + newbuf = os_realloc_array(entry->binarray_value, + buflen, BIN_ARRAY_ITEM_SIZE); if (!newbuf) goto cleanup; entry->binarray_value = newbuf; @@ -1104,5 +1100,5 @@ break; } - memset(entry, 0, sizeof(struct wpa_dbus_dict_entry)); + os_memset(entry, 0, sizeof(struct wpa_dbus_dict_entry)); } diff -Nru wpa-1.0/wpa_supplicant/dbus/dbus_dict_helpers.h wpa-2.1/wpa_supplicant/dbus/dbus_dict_helpers.h --- wpa-1.0/wpa_supplicant/dbus/dbus_dict_helpers.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/dbus/dbus_dict_helpers.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * WPA Supplicant / dbus-based control interface * Copyright (c) 2006, Dan Williams and Red Hat, Inc. * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef DBUS_DICT_HELPERS_H diff -Nru wpa-1.0/wpa_supplicant/dbus/dbus_new.c wpa-2.1/wpa_supplicant/dbus/dbus_new.c --- wpa-1.0/wpa_supplicant/dbus/dbus_new.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/dbus/dbus_new.c 2014-02-04 11:23:35.000000000 +0000 @@ -4,14 +4,8 @@ * Copyright (c) 2009-2010, Witold Sowa * Copyright (c) 2009, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -27,11 +21,103 @@ #include "dbus_dict_helpers.h" #include "dbus_new.h" #include "dbus_new_handlers.h" -#include "dbus_common.h" #include "dbus_common_i.h" #include "dbus_new_handlers_p2p.h" #include "p2p/p2p.h" +#ifdef CONFIG_AP /* until needed by something else */ + +/* + * NameOwnerChanged handling + * + * Some services we provide allow an application to register for + * a signal that it needs. While it can also unregister, we must + * be prepared for the case where the application simply crashes + * and thus doesn't clean up properly. The way to handle this in + * DBus is to register for the NameOwnerChanged signal which will + * signal an owner change to NULL if the peer closes the socket + * for whatever reason. + * + * Handle this signal via a filter function whenever necessary. + * The code below also handles refcounting in case in the future + * there will be multiple instances of this subscription scheme. + */ +static const char wpas_dbus_noc_filter_str[] = + "interface=org.freedesktop.DBus,member=NameOwnerChanged"; + + +static DBusHandlerResult noc_filter(DBusConnection *conn, + DBusMessage *message, void *data) +{ + struct wpas_dbus_priv *priv = data; + + if (dbus_message_get_type(message) != DBUS_MESSAGE_TYPE_SIGNAL) + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + + if (dbus_message_is_signal(message, DBUS_INTERFACE_DBUS, + "NameOwnerChanged")) { + const char *name; + const char *prev_owner; + const char *new_owner; + DBusError derr; + struct wpa_supplicant *wpa_s; + + dbus_error_init(&derr); + + if (!dbus_message_get_args(message, &derr, + DBUS_TYPE_STRING, &name, + DBUS_TYPE_STRING, &prev_owner, + DBUS_TYPE_STRING, &new_owner, + DBUS_TYPE_INVALID)) { + /* Ignore this error */ + dbus_error_free(&derr); + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + + for (wpa_s = priv->global->ifaces; wpa_s; wpa_s = wpa_s->next) + { + if (wpa_s->preq_notify_peer != NULL && + os_strcmp(name, wpa_s->preq_notify_peer) == 0 && + (new_owner == NULL || os_strlen(new_owner) == 0)) { + /* probe request owner disconnected */ + os_free(wpa_s->preq_notify_peer); + wpa_s->preq_notify_peer = NULL; + wpas_dbus_unsubscribe_noc(priv); + } + } + } + + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + + +void wpas_dbus_subscribe_noc(struct wpas_dbus_priv *priv) +{ + priv->dbus_noc_refcnt++; + if (priv->dbus_noc_refcnt > 1) + return; + + if (!dbus_connection_add_filter(priv->con, noc_filter, priv, NULL)) { + wpa_printf(MSG_ERROR, "dbus: failed to add filter"); + return; + } + + dbus_bus_add_match(priv->con, wpas_dbus_noc_filter_str, NULL); +} + + +void wpas_dbus_unsubscribe_noc(struct wpas_dbus_priv *priv) +{ + priv->dbus_noc_refcnt--; + if (priv->dbus_noc_refcnt > 0) + return; + + dbus_bus_remove_match(priv->con, wpas_dbus_noc_filter_str, NULL); + dbus_connection_remove_filter(priv->con, noc_filter, priv); +} + +#endif /* CONFIG_AP */ + /** * wpas_dbus_signal_interface - Send a interface related event signal @@ -748,6 +834,111 @@ dbus_message_unref(msg); } + +void wpas_dbus_signal_eap_status(struct wpa_supplicant *wpa_s, + const char *status, const char *parameter) +{ + struct wpas_dbus_priv *iface; + DBusMessage *msg; + DBusMessageIter iter; + + iface = wpa_s->global->dbus; + + /* Do nothing if the control interface is not turned on */ + if (iface == NULL) + return; + + msg = dbus_message_new_signal(wpa_s->dbus_new_path, + WPAS_DBUS_NEW_IFACE_INTERFACE, + "EAP"); + if (msg == NULL) + return; + + dbus_message_iter_init_append(msg, &iter); + + if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &status) + || + !dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, + ¶meter)) + goto nomem; + + dbus_connection_send(iface->con, msg, NULL); + +nomem: + dbus_message_unref(msg); +} + + +/** + * wpas_dbus_signal_sta - Send a station related event signal + * @wpa_s: %wpa_supplicant network interface data + * @sta: station mac address + * @sig_name: signal name - StaAuthorized or StaDeauthorized + * + * Notify listeners about event related with station + */ +static void wpas_dbus_signal_sta(struct wpa_supplicant *wpa_s, + const u8 *sta, const char *sig_name) +{ + struct wpas_dbus_priv *iface; + DBusMessage *msg; + char sta_mac[WPAS_DBUS_OBJECT_PATH_MAX]; + char *dev_mac; + + os_snprintf(sta_mac, WPAS_DBUS_OBJECT_PATH_MAX, MACSTR, MAC2STR(sta)); + dev_mac = sta_mac; + + iface = wpa_s->global->dbus; + + /* Do nothing if the control interface is not turned on */ + if (iface == NULL) + return; + + msg = dbus_message_new_signal(wpa_s->dbus_new_path, + WPAS_DBUS_NEW_IFACE_INTERFACE, sig_name); + if (msg == NULL) + return; + + if (dbus_message_append_args(msg, DBUS_TYPE_STRING, &dev_mac, + DBUS_TYPE_INVALID)) + dbus_connection_send(iface->con, msg, NULL); + else + wpa_printf(MSG_ERROR, "dbus: Failed to construct signal"); + dbus_message_unref(msg); + + wpa_printf(MSG_DEBUG, "dbus: Station MAC address '%s' '%s'", + sta_mac, sig_name); +} + + +/** + * wpas_dbus_signal_sta_authorized - Send a STA authorized signal + * @wpa_s: %wpa_supplicant network interface data + * @sta: station mac address + * + * Notify listeners a new station has been authorized + */ +void wpas_dbus_signal_sta_authorized(struct wpa_supplicant *wpa_s, + const u8 *sta) +{ + wpas_dbus_signal_sta(wpa_s, sta, "StaAuthorized"); +} + + +/** + * wpas_dbus_signal_sta_deauthorized - Send a STA deauthorized signal + * @wpa_s: %wpa_supplicant network interface data + * @sta: station mac address + * + * Notify listeners a station has been deauthorized + */ +void wpas_dbus_signal_sta_deauthorized(struct wpa_supplicant *wpa_s, + const u8 *sta) +{ + wpas_dbus_signal_sta(wpa_s, sta, "StaDeauthorized"); +} + + #ifdef CONFIG_P2P /** @@ -954,7 +1145,7 @@ if (os_memcmp(ssid->ssid, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN)) return -1; - memcpy(group_name, ssid->ssid + P2P_WILDCARD_SSID_LEN, 2); + os_memcpy(group_name, ssid->ssid + P2P_WILDCARD_SSID_LEN, 2); group_name[2] = '\0'; os_snprintf(group_obj_path, WPAS_DBUS_OBJECT_PATH_MAX, @@ -983,7 +1174,6 @@ DBusMessage *msg; DBusMessageIter iter, dict_iter; struct wpas_dbus_priv *iface; - char net_obj_path[WPAS_DBUS_OBJECT_PATH_MAX]; char group_obj_path[WPAS_DBUS_OBJECT_PATH_MAX]; iface = wpa_s->parent->global->dbus; @@ -1021,14 +1211,8 @@ client ? "client" : "GO")) goto nomem; - os_snprintf(net_obj_path, WPAS_DBUS_OBJECT_PATH_MAX, - "%s/" WPAS_DBUS_NEW_NETWORKS_PART "/%u", - wpa_s->parent->dbus_new_path, network_id); - if (!wpa_dbus_dict_append_object_path(&dict_iter, "group_object", group_obj_path) || - !wpa_dbus_dict_append_object_path(&dict_iter, "network_object", - net_obj_path) || !wpa_dbus_dict_close_write(&iter, &dict_iter)) goto nomem; @@ -1178,7 +1362,7 @@ DBusMessageIter iter, dict_iter; struct wpas_dbus_priv *iface; - wpa_printf(MSG_INFO, "%s\n", __func__); + wpa_printf(MSG_DEBUG, "%s", __func__); iface = wpa_s->global->dbus; /* Do nothing if the control interface is not turned on */ @@ -1608,10 +1792,12 @@ enum wpas_dbus_prop property) { char *prop; + dbus_bool_t flush; if (wpa_s->dbus_new_path == NULL) return; /* Skip signal since D-Bus setup is not yet ready */ + flush = FALSE; switch (property) { case WPAS_DBUS_PROP_AP_SCAN: prop = "ApScan"; @@ -1634,6 +1820,10 @@ case WPAS_DBUS_PROP_CURRENT_AUTH_MODE: prop = "CurrentAuthMode"; break; + case WPAS_DBUS_PROP_DISCONNECT_REASON: + prop = "DisconnectReason"; + flush = TRUE; + break; default: wpa_printf(MSG_ERROR, "dbus: %s: Unknown Property value %d", __func__, property); @@ -1643,6 +1833,10 @@ wpa_dbus_mark_property_changed(wpa_s->global->dbus, wpa_s->dbus_new_path, WPAS_DBUS_NEW_IFACE_INTERFACE, prop); + if (flush) { + wpa_dbus_flush_object_changed_properties( + wpa_s->global->dbus->con, wpa_s->dbus_new_path); + } } @@ -1684,6 +1878,9 @@ case WPAS_DBUS_BSS_PROP_RSN: prop = "RSN"; break; + case WPAS_DBUS_BSS_PROP_WPS: + prop = "WPS"; + break; case WPAS_DBUS_BSS_PROP_IES: prop = "IEs"; break; @@ -1817,6 +2014,10 @@ wpas_dbus_getter_eap_methods, NULL }, + { "Capabilities", WPAS_DBUS_NEW_INTERFACE, "as", + wpas_dbus_getter_global_capabilities, + NULL + }, { NULL, NULL, NULL, NULL, NULL } }; @@ -2030,11 +2231,11 @@ struct wpas_dbus_priv *ctrl_iface; char net_obj_path[WPAS_DBUS_OBJECT_PATH_MAX]; int ret; +#ifdef CONFIG_P2P struct wpa_ssid *ssid; ssid = wpa_config_get_network(wpa_s->conf, nid); -#ifdef CONFIG_P2P /* If it is a persistent group unregister it as such */ if (ssid && network_is_persistent_group(ssid)) return wpas_dbus_unregister_persistent_group(wpa_s, nid); @@ -2099,6 +2300,10 @@ wpas_dbus_getter_bss_rsn, NULL }, + { "WPS", WPAS_DBUS_NEW_IFACE_BSS, "a{sv}", + wpas_dbus_getter_bss_wps, + NULL + }, { "IEs", WPAS_DBUS_NEW_IFACE_BSS, "ay", wpas_dbus_getter_bss_ies, NULL @@ -2251,6 +2456,12 @@ END_ARGS } }, + { "Reassociate", WPAS_DBUS_NEW_IFACE_INTERFACE, + (WPADBusMethodHandler) &wpas_dbus_handler_reassociate, + { + END_ARGS + } + }, { "RemoveNetwork", WPAS_DBUS_NEW_IFACE_INTERFACE, (WPADBusMethodHandler) &wpas_dbus_handler_remove_network, { @@ -2280,6 +2491,7 @@ END_ARGS } }, +#ifndef CONFIG_NO_CONFIG_BLOBS { "AddBlob", WPAS_DBUS_NEW_IFACE_INTERFACE, (WPADBusMethodHandler) &wpas_dbus_handler_add_blob, { @@ -2303,6 +2515,16 @@ END_ARGS } }, +#endif /* CONFIG_NO_CONFIG_BLOBS */ + { "SetPKCS11EngineAndModulePath", WPAS_DBUS_NEW_IFACE_INTERFACE, + (WPADBusMethodHandler) + &wpas_dbus_handler_set_pkcs11_engine_and_module_path, + { + { "pkcs11_engine_path", "s", ARG_IN }, + { "pkcs11_module_path", "s", ARG_IN }, + END_ARGS + } + }, #ifdef CONFIG_WPS { "Start", WPAS_DBUS_NEW_IFACE_WPS, (WPADBusMethodHandler) &wpas_dbus_handler_wps_start, @@ -2488,6 +2710,72 @@ END_ARGS } }, +#ifdef CONFIG_AP + { "SubscribeProbeReq", WPAS_DBUS_NEW_IFACE_INTERFACE, + (WPADBusMethodHandler) wpas_dbus_handler_subscribe_preq, + { + END_ARGS + } + }, + { "UnsubscribeProbeReq", WPAS_DBUS_NEW_IFACE_INTERFACE, + (WPADBusMethodHandler) wpas_dbus_handler_unsubscribe_preq, + { + END_ARGS + } + }, +#endif /* CONFIG_AP */ + { "EAPLogoff", WPAS_DBUS_NEW_IFACE_INTERFACE, + (WPADBusMethodHandler) &wpas_dbus_handler_eap_logoff, + { + END_ARGS + } + }, + { "EAPLogon", WPAS_DBUS_NEW_IFACE_INTERFACE, + (WPADBusMethodHandler) &wpas_dbus_handler_eap_logon, + { + END_ARGS + } + }, +#ifdef CONFIG_AUTOSCAN + { "AutoScan", WPAS_DBUS_NEW_IFACE_INTERFACE, + (WPADBusMethodHandler) &wpas_dbus_handler_autoscan, + { + { "arg", "s", ARG_IN }, + END_ARGS + } + }, +#endif /* CONFIG_AUTOSCAN */ +#ifdef CONFIG_TDLS + { "TDLSDiscover", WPAS_DBUS_NEW_IFACE_INTERFACE, + (WPADBusMethodHandler) wpas_dbus_handler_tdls_discover, + { + { "peer_address", "s", ARG_IN }, + END_ARGS + } + }, + { "TDLSSetup", WPAS_DBUS_NEW_IFACE_INTERFACE, + (WPADBusMethodHandler) wpas_dbus_handler_tdls_setup, + { + { "peer_address", "s", ARG_IN }, + END_ARGS + } + }, + { "TDLSStatus", WPAS_DBUS_NEW_IFACE_INTERFACE, + (WPADBusMethodHandler) wpas_dbus_handler_tdls_status, + { + { "peer_address", "s", ARG_IN }, + { "status", "s", ARG_OUT }, + END_ARGS + } + }, + { "TDLSTeardown", WPAS_DBUS_NEW_IFACE_INTERFACE, + (WPADBusMethodHandler) wpas_dbus_handler_tdls_teardown, + { + { "peer_address", "s", ARG_IN }, + END_ARGS + } + }, +#endif /* CONFIG_TDLS */ { NULL, NULL, NULL, { END_ARGS } } }; @@ -2560,6 +2848,18 @@ wpas_dbus_getter_fast_reauth, wpas_dbus_setter_fast_reauth }, + { "ScanInterval", WPAS_DBUS_NEW_IFACE_INTERFACE, "i", + wpas_dbus_getter_scan_interval, + wpas_dbus_setter_scan_interval + }, + { "PKCS11EnginePath", WPAS_DBUS_NEW_IFACE_INTERFACE, "s", + wpas_dbus_getter_pkcs11_engine_path, + NULL + }, + { "PKCS11ModulePath", WPAS_DBUS_NEW_IFACE_INTERFACE, "s", + wpas_dbus_getter_pkcs11_module_path, + NULL + }, #ifdef CONFIG_WPS { "ProcessCredentials", WPAS_DBUS_NEW_IFACE_WPS, "b", wpas_dbus_getter_process_credentials, @@ -2592,6 +2892,10 @@ NULL }, #endif /* CONFIG_P2P */ + { "DisconnectReason", WPAS_DBUS_NEW_IFACE_INTERFACE, "i", + wpas_dbus_getter_disconnect_reason, + NULL + }, { NULL, NULL, NULL, NULL, NULL } }; @@ -2810,12 +3114,39 @@ } }, #endif /* CONFIG_P2P */ +#ifdef CONFIG_AP + { "ProbeRequest", WPAS_DBUS_NEW_IFACE_INTERFACE, + { + { "args", "a{sv}", ARG_OUT }, + END_ARGS + } + }, +#endif /* CONFIG_AP */ { "Certification", WPAS_DBUS_NEW_IFACE_INTERFACE, { { "certification", "a{sv}", ARG_OUT }, END_ARGS } }, + { "EAP", WPAS_DBUS_NEW_IFACE_INTERFACE, + { + { "status", "s", ARG_OUT }, + { "parameter", "s", ARG_OUT }, + END_ARGS + } + }, + { "StaAuthorized", WPAS_DBUS_NEW_IFACE_INTERFACE, + { + { "name", "s", ARG_OUT }, + END_ARGS + } + }, + { "StaDeauthorized", WPAS_DBUS_NEW_IFACE_INTERFACE, + { + { "name", "s", ARG_OUT }, + END_ARGS + } + }, { NULL, NULL, { END_ARGS } } }; @@ -2883,6 +3214,15 @@ wpa_printf(MSG_DEBUG, "dbus: Unregister interface object '%s'", wpa_s->dbus_new_path); + +#ifdef CONFIG_AP + if (wpa_s->preq_notify_peer) { + wpas_dbus_unsubscribe_noc(ctrl_iface); + os_free(wpa_s->preq_notify_peer); + wpa_s->preq_notify_peer = NULL; + } +#endif /* CONFIG_AP */ + if (wpa_dbus_unregister_object_per_iface(ctrl_iface, wpa_s->dbus_new_path)) return -1; diff -Nru wpa-1.0/wpa_supplicant/dbus/dbus_new.h wpa-2.1/wpa_supplicant/dbus/dbus_new.h --- wpa-1.0/wpa_supplicant/dbus/dbus_new.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/dbus/dbus_new.h 2014-02-04 11:23:35.000000000 +0000 @@ -3,14 +3,8 @@ * Copyright (c) 2006, Dan Williams and Red Hat, Inc. * Copyright (c) 2009-2010, Witold Sowa * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef CTRL_IFACE_DBUS_NEW_H @@ -34,6 +28,7 @@ WPAS_DBUS_PROP_CURRENT_NETWORK, WPAS_DBUS_PROP_CURRENT_AUTH_MODE, WPAS_DBUS_PROP_BSSS, + WPAS_DBUS_PROP_DISCONNECT_REASON, }; enum wpas_dbus_bss_prop { @@ -44,6 +39,7 @@ WPAS_DBUS_BSS_PROP_RATES, WPAS_DBUS_BSS_PROP_WPA, WPAS_DBUS_BSS_PROP_RSN, + WPAS_DBUS_BSS_PROP_WPS, WPAS_DBUS_BSS_PROP_IES, }; @@ -115,6 +111,17 @@ #define WPAS_DBUS_ERROR_BLOB_UNKNOWN \ WPAS_DBUS_NEW_INTERFACE ".BlobUnknown" +#define WPAS_DBUS_ERROR_SUBSCRIPTION_IN_USE \ + WPAS_DBUS_NEW_INTERFACE ".SubscriptionInUse" +#define WPAS_DBUS_ERROR_NO_SUBSCRIPTION \ + WPAS_DBUS_NEW_INTERFACE ".NoSubscription" +#define WPAS_DBUS_ERROR_SUBSCRIPTION_EPERM \ + WPAS_DBUS_NEW_INTERFACE ".SubscriptionNotYou" + + +void wpas_dbus_subscribe_noc(struct wpas_dbus_priv *priv); +void wpas_dbus_unsubscribe_noc(struct wpas_dbus_priv *priv); + #ifdef CONFIG_CTRL_IFACE_DBUS_NEW @@ -210,6 +217,15 @@ int depth, const char *subject, const char *cert_hash, const struct wpabuf *cert); +void wpas_dbus_signal_preq(struct wpa_supplicant *wpa_s, + const u8 *addr, const u8 *dst, const u8 *bssid, + const u8 *ie, size_t ie_len, u32 ssi_signal); +void wpas_dbus_signal_eap_status(struct wpa_supplicant *wpa_s, + const char *status, const char *parameter); +void wpas_dbus_signal_sta_authorized(struct wpa_supplicant *wpa_s, + const u8 *sta); +void wpas_dbus_signal_sta_deauthorized(struct wpa_supplicant *wpa_s, + const u8 *sta); #else /* CONFIG_CTRL_IFACE_DBUS_NEW */ @@ -467,6 +483,32 @@ { } +static inline void wpas_dbus_signal_preq(struct wpa_supplicant *wpa_s, + const u8 *addr, const u8 *dst, + const u8 *bssid, + const u8 *ie, size_t ie_len, + u32 ssi_signal) +{ +} + +static inline void wpas_dbus_signal_eap_status(struct wpa_supplicant *wpa_s, + const char *status, + const char *parameter) +{ +} + +static inline +void wpas_dbus_signal_sta_authorized(struct wpa_supplicant *wpa_s, + const u8 *sta) +{ +} + +static inline +void wpas_dbus_signal_sta_deauthorized(struct wpa_supplicant *wpa_s, + const u8 *sta) +{ +} + #endif /* CONFIG_CTRL_IFACE_DBUS_NEW */ #endif /* CTRL_IFACE_DBUS_H_NEW */ diff -Nru wpa-1.0/wpa_supplicant/dbus/dbus_new_handlers.c wpa-2.1/wpa_supplicant/dbus/dbus_new_handlers.c --- wpa-1.0/wpa_supplicant/dbus/dbus_new_handlers.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/dbus/dbus_new_handlers.c 2014-02-04 11:23:35.000000000 +0000 @@ -4,14 +4,8 @@ * Copyright (c) 2009-2010, Witold Sowa * Copyright (c) 2009, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -25,18 +19,14 @@ #include "../wpa_supplicant_i.h" #include "../driver_i.h" #include "../notify.h" -#include "../wpas_glue.h" #include "../bss.h" #include "../scan.h" -#include "../ctrl_iface.h" +#include "../autoscan.h" #include "dbus_new_helpers.h" #include "dbus_new.h" #include "dbus_new_handlers.h" #include "dbus_dict_helpers.h" - -extern int wpa_debug_level; -extern int wpa_debug_show_keys; -extern int wpa_debug_timestamp; +#include "dbus_common_i.h" static const char *debug_strings[] = { "excessive", "msgdump", "debug", "info", "warning", "error", NULL @@ -129,7 +119,7 @@ static const char *dont_quote[] = { "key_mgmt", "proto", "pairwise", "auth_alg", "group", "eap", "opensc_engine_path", "pkcs11_engine_path", "pkcs11_module_path", - "bssid", NULL + "bssid", "scan_freq", "freq_list", NULL }; static dbus_bool_t should_quote_opt(const char *key) @@ -254,7 +244,7 @@ if ((os_strcmp(entry.key, "psk") == 0 && value[0] == '"' && ssid->ssid_len) || - (strcmp(entry.key, "ssid") == 0 && ssid->passphrase)) + (os_strcmp(entry.key, "ssid") == 0 && ssid->passphrase)) wpa_config_update_psk(ssid); else if (os_strcmp(entry.key, "priority") == 0) wpa_config_update_prio_list(wpa_s->conf); @@ -548,25 +538,25 @@ while (wpa_dbus_dict_has_dict_entry(&iter_dict)) { if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) goto error; - if (!strcmp(entry.key, "Driver") && + if (!os_strcmp(entry.key, "Driver") && (entry.type == DBUS_TYPE_STRING)) { driver = os_strdup(entry.str_value); wpa_dbus_dict_entry_clear(&entry); if (driver == NULL) goto error; - } else if (!strcmp(entry.key, "Ifname") && + } else if (!os_strcmp(entry.key, "Ifname") && (entry.type == DBUS_TYPE_STRING)) { ifname = os_strdup(entry.str_value); wpa_dbus_dict_entry_clear(&entry); if (ifname == NULL) goto error; - } else if (!strcmp(entry.key, "ConfigFile") && + } else if (!os_strcmp(entry.key, "ConfigFile") && (entry.type == DBUS_TYPE_STRING)) { confname = os_strdup(entry.str_value); wpa_dbus_dict_entry_clear(&entry); if (confname == NULL) goto error; - } else if (!strcmp(entry.key, "BridgeIfname") && + } else if (!os_strcmp(entry.key, "BridgeIfname") && (entry.type == DBUS_TYPE_STRING)) { bridge_ifname = os_strdup(entry.str_value); wpa_dbus_dict_entry_clear(&entry); @@ -614,6 +604,7 @@ out: os_free(driver); os_free(ifname); + os_free(confname); os_free(bridge_ifname); return reply; @@ -647,7 +638,7 @@ wpa_s = get_iface_by_dbus_path(global, path); if (wpa_s == NULL) reply = wpas_dbus_error_iface_unknown(message); - else if (wpa_supplicant_remove_iface(global, wpa_s)) { + else if (wpa_supplicant_remove_iface(global, wpa_s, 0)) { reply = wpas_dbus_error_unknown_error( message, "wpa_supplicant couldn't remove this " "interface."); @@ -875,7 +866,7 @@ for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) num++; - paths = os_zalloc(num * sizeof(char*)); + paths = os_calloc(num, sizeof(char *)); if (!paths) { dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory"); return FALSE; @@ -928,6 +919,44 @@ } +/** + * wpas_dbus_getter_global_capabilities - Request supported global capabilities + * @iter: Pointer to incoming dbus message iter + * @error: Location to store error on failure + * @user_data: Function specific data + * Returns: TRUE on success, FALSE on failure + * + * Getter for "Capabilities" property. Handles requests by dbus clients to + * return a list of strings with supported capabilities like AP, RSN IBSS, + * and P2P that are determined at compile time. + */ +dbus_bool_t wpas_dbus_getter_global_capabilities(DBusMessageIter *iter, + DBusError *error, + void *user_data) +{ + const char *capabilities[5] = { NULL, NULL, NULL, NULL, NULL }; + size_t num_items = 0; + +#ifdef CONFIG_AP + capabilities[num_items++] = "ap"; +#endif /* CONFIG_AP */ +#ifdef CONFIG_IBSS_RSN + capabilities[num_items++] = "ibss-rsn"; +#endif /* CONFIG_IBSS_RSN */ +#ifdef CONFIG_P2P + capabilities[num_items++] = "p2p"; +#endif /* CONFIG_P2P */ +#ifdef CONFIG_INTERWORKING + capabilities[num_items++] = "interworking"; +#endif /* CONFIG_INTERWORKING */ + + return wpas_dbus_simple_array_property_getter(iter, + DBUS_TYPE_STRING, + capabilities, + num_items, error); +} + + static int wpas_dbus_get_scan_type(DBusMessage *message, DBusMessageIter *var, char **type, DBusMessage **reply) { @@ -1163,8 +1192,9 @@ #define FREQS_ALLOC_CHUNK 32 if (freqs_num % FREQS_ALLOC_CHUNK == 0) { - nfreqs = os_realloc(freqs, sizeof(int) * - (freqs_num + FREQS_ALLOC_CHUNK)); + nfreqs = os_realloc_array( + freqs, freqs_num + FREQS_ALLOC_CHUNK, + sizeof(int)); if (nfreqs == NULL) os_free(freqs); freqs = nfreqs; @@ -1184,8 +1214,7 @@ dbus_message_iter_next(&array_iter); } - nfreqs = os_realloc(freqs, - sizeof(int) * (freqs_num + 1)); + nfreqs = os_realloc_array(freqs, freqs_num + 1, sizeof(int)); if (nfreqs == NULL) os_free(freqs); freqs = nfreqs; @@ -1203,6 +1232,23 @@ } +static int wpas_dbus_get_scan_allow_roam(DBusMessage *message, + DBusMessageIter *var, + dbus_bool_t *allow, + DBusMessage **reply) +{ + if (dbus_message_iter_get_arg_type(var) != DBUS_TYPE_BOOLEAN) { + wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: " + "Type must be a boolean"); + *reply = wpas_dbus_error_invalid_args( + message, "Wrong Type value type. Boolean required"); + return -1; + } + dbus_message_iter_get_basic(var, allow); + return 0; +} + + /** * wpas_dbus_handler_scan - Request a wireless scan on an interface * @message: Pointer to incoming dbus message @@ -1221,6 +1267,7 @@ char *key = NULL, *type = NULL; struct wpa_driver_scan_params params; size_t i; + dbus_bool_t allow_roam = 1; os_memset(¶ms, 0, sizeof(params)); @@ -1251,6 +1298,12 @@ if (wpas_dbus_get_scan_channels(message, &variant_iter, ¶ms, &reply) < 0) goto out; + } else if (os_strcmp(key, "AllowRoam") == 0) { + if (wpas_dbus_get_scan_allow_roam(message, + &variant_iter, + &allow_roam, + &reply) < 0) + goto out; } else { wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: " "Unknown argument %s", key); @@ -1279,7 +1332,7 @@ } else if (params.freqs && params.freqs[0]) { wpa_supplicant_trigger_scan(wpa_s, ¶ms); } else { - wpa_s->scan_req = 2; + wpa_s->scan_req = MANUAL_SCAN_REQ; wpa_supplicant_req_scan(wpa_s, 0, 0); } } else if (!os_strcmp(type, "active")) { @@ -1287,6 +1340,9 @@ /* Add wildcard ssid */ params.num_ssids++; } +#ifdef CONFIG_AUTOSCAN + autoscan_deinit(wpa_s); +#endif /* CONFIG_AUTOSCAN */ wpa_supplicant_trigger_scan(wpa_s, ¶ms); } else { wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: " @@ -1296,6 +1352,9 @@ goto out; } + if (!allow_roam) + wpa_s->scan_res_handler = scan_only_handler; + out: for (i = 0; i < WPAS_MAX_SCAN_SSIDS; i++) os_free((u8 *) params.ssids[i].ssid); @@ -1406,6 +1465,28 @@ /** + * wpas_dbus_handler_reassociate - Reassociate to current AP + * @message: Pointer to incoming dbus message + * @wpa_s: wpa_supplicant structure for a network interface + * Returns: NotConnected DBus error message if not connected + * or NULL otherwise. + * + * Handler function for "Reassociate" method call of network interface. + */ +DBusMessage * wpas_dbus_handler_reassociate(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + if (wpa_s->current_ssid != NULL) { + wpas_request_connection(wpa_s); + return NULL; + } + + return dbus_message_new_error(message, WPAS_DBUS_ERROR_NOT_CONNECTED, + "This interface is not connected"); +} + + +/** * wpas_dbus_handler_remove_network - Remove a configured network * @message: Pointer to incoming dbus message * @wpa_s: wpa_supplicant structure for a network interface @@ -1421,6 +1502,7 @@ char *iface = NULL, *net_id = NULL; int id; struct wpa_ssid *ssid; + int was_disabled; dbus_message_get_args(message, NULL, DBUS_TYPE_OBJECT_PATH, &op, DBUS_TYPE_INVALID); @@ -1428,13 +1510,15 @@ /* Extract the network ID and ensure the network */ /* is actually a child of this interface */ iface = wpas_dbus_new_decompose_object_path(op, 0, &net_id, NULL); - if (iface == NULL || os_strcmp(iface, wpa_s->dbus_new_path) != 0) { + if (iface == NULL || net_id == NULL || + os_strcmp(iface, wpa_s->dbus_new_path) != 0) { reply = wpas_dbus_error_invalid_args(message, op); goto out; } + errno = 0; id = strtoul(net_id, NULL, 10); - if (errno == EINVAL) { + if (errno != 0) { reply = wpas_dbus_error_invalid_args(message, op); goto out; } @@ -1445,6 +1529,8 @@ goto out; } + was_disabled = ssid->disabled; + wpas_notify_network_removed(wpa_s, ssid); if (wpa_config_remove_network(wpa_s->conf, id) < 0) { @@ -1460,6 +1546,13 @@ if (ssid == wpa_s->current_ssid) wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_DEAUTH_LEAVING); + else if (!was_disabled && wpa_s->sched_scanning) { + wpa_printf(MSG_DEBUG, "Stop ongoing sched_scan to remove " + "network from filters"); + wpa_supplicant_cancel_sched_scan(wpa_s); + wpa_supplicant_req_scan(wpa_s, 0, 0); + } + out: os_free(iface); @@ -1483,7 +1576,8 @@ } if (ssid == wpa_s->current_ssid) - wpa_supplicant_disassociate(wpa_s, WLAN_REASON_DEAUTH_LEAVING); + wpa_supplicant_deauthenticate(wpa_s, + WLAN_REASON_DEAUTH_LEAVING); } @@ -1498,6 +1592,9 @@ DBusMessage * wpas_dbus_handler_remove_all_networks( DBusMessage *message, struct wpa_supplicant *wpa_s) { + if (wpa_s->sched_scanning) + wpa_supplicant_cancel_sched_scan(wpa_s); + /* NB: could check for failure and return an error */ wpa_config_foreach_network(wpa_s->conf, remove_network, wpa_s); return NULL; @@ -1527,13 +1624,15 @@ /* Extract the network ID and ensure the network */ /* is actually a child of this interface */ iface = wpas_dbus_new_decompose_object_path(op, 0, &net_id, NULL); - if (iface == NULL || os_strcmp(iface, wpa_s->dbus_new_path) != 0) { + if (iface == NULL || net_id == NULL || + os_strcmp(iface, wpa_s->dbus_new_path) != 0) { reply = wpas_dbus_error_invalid_args(message, op); goto out; } + errno = 0; id = strtoul(net_id, NULL, 10); - if (errno == EINVAL) { + if (errno != 0) { reply = wpas_dbus_error_invalid_args(message, op); goto out; } @@ -1582,13 +1681,15 @@ /* Extract the network ID and ensure the network */ /* is actually a child of this interface */ iface = wpas_dbus_new_decompose_object_path(op, 0, &net_id, NULL); - if (iface == NULL || os_strcmp(iface, wpa_s->dbus_new_path) != 0) { + if (iface == NULL || net_id == NULL || + os_strcmp(iface, wpa_s->dbus_new_path) != 0) { reply = wpas_dbus_error_invalid_args(message, op); goto out; } + errno = 0; id = strtoul(net_id, NULL, 10); - if (errno == EINVAL) { + if (errno != 0) { reply = wpas_dbus_error_invalid_args(message, net_id); goto out; } @@ -1618,6 +1719,8 @@ } +#ifndef CONFIG_NO_CONFIG_BLOBS + /** * wpas_dbus_handler_add_blob - Store named binary blob (ie, for certificates) * @message: Pointer to incoming dbus message @@ -1782,6 +1885,9 @@ } +#endif /* CONFIG_NO_CONFIG_BLOBS */ + + /* * wpas_dbus_handler_flush_bss - Flush the BSS cache * @message: Pointer to incoming dbus message @@ -1807,6 +1913,307 @@ } +#ifdef CONFIG_AUTOSCAN +/** + * wpas_dbus_handler_autoscan - Set autoscan parameters for the interface + * @message: Pointer to incoming dbus message + * @wpa_s: wpa_supplicant structure for a network interface + * Returns: NULL + * + * Handler function for "AutoScan" method call of network interface. + */ +DBusMessage * wpas_dbus_handler_autoscan(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + DBusMessage *reply = NULL; + enum wpa_states state = wpa_s->wpa_state; + char *arg; + + dbus_message_get_args(message, NULL, DBUS_TYPE_STRING, &arg, + DBUS_TYPE_INVALID); + + if (arg != NULL && os_strlen(arg) > 0) { + char *tmp; + tmp = os_strdup(arg); + if (tmp == NULL) { + reply = dbus_message_new_error(message, + DBUS_ERROR_NO_MEMORY, + NULL); + } else { + os_free(wpa_s->conf->autoscan); + wpa_s->conf->autoscan = tmp; + if (state == WPA_DISCONNECTED || state == WPA_INACTIVE) + autoscan_init(wpa_s, 1); + else if (state == WPA_SCANNING) + wpa_supplicant_reinit_autoscan(wpa_s); + } + } else if (arg != NULL && os_strlen(arg) == 0) { + os_free(wpa_s->conf->autoscan); + wpa_s->conf->autoscan = NULL; + autoscan_deinit(wpa_s); + } else + reply = dbus_message_new_error(message, + DBUS_ERROR_INVALID_ARGS, + NULL); + + return reply; +} +#endif /* CONFIG_AUTOSCAN */ + + +/* + * wpas_dbus_handler_eap_logoff - IEEE 802.1X EAPOL state machine logoff + * @message: Pointer to incoming dbus message + * @wpa_s: wpa_supplicant structure for a network interface + * Returns: NULL + * + * Handler function for "EAPLogoff" method call of network interface. + */ +DBusMessage * wpas_dbus_handler_eap_logoff(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + eapol_sm_notify_logoff(wpa_s->eapol, TRUE); + return NULL; +} + + +/* + * wpas_dbus_handler_eap_logon - IEEE 802.1X EAPOL state machine logon + * @message: Pointer to incoming dbus message + * @wpa_s: wpa_supplicant structure for a network interface + * Returns: NULL + * + * Handler function for "EAPLogin" method call of network interface. + */ +DBusMessage * wpas_dbus_handler_eap_logon(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + eapol_sm_notify_logoff(wpa_s->eapol, FALSE); + return NULL; +} + + +#ifdef CONFIG_TDLS + +static DBusMessage * get_peer_hwaddr_helper(DBusMessage *message, + const char *func_name, + u8 *peer_address) +{ + const char *peer_string; + + if (!dbus_message_get_args(message, NULL, + DBUS_TYPE_STRING, &peer_string, + DBUS_TYPE_INVALID)) + return wpas_dbus_error_invalid_args(message, NULL); + + if (hwaddr_aton(peer_string, peer_address)) { + wpa_printf(MSG_DEBUG, "%s: invalid address '%s'", + func_name, peer_string); + return wpas_dbus_error_invalid_args( + message, "Invalid hardware address format"); + } + + return NULL; +} + + +/* + * wpas_dbus_handler_tdls_discover - Discover TDLS peer + * @message: Pointer to incoming dbus message + * @wpa_s: wpa_supplicant structure for a network interface + * Returns: NULL indicating success or DBus error message on failure + * + * Handler function for "TDLSDiscover" method call of network interface. + */ +DBusMessage * wpas_dbus_handler_tdls_discover(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + u8 peer[ETH_ALEN]; + DBusMessage *error_reply; + int ret; + + error_reply = get_peer_hwaddr_helper(message, __func__, peer); + if (error_reply) + return error_reply; + + wpa_printf(MSG_DEBUG, "DBUS TDLS_DISCOVER " MACSTR, MAC2STR(peer)); + + if (wpa_tdls_is_external_setup(wpa_s->wpa)) + ret = wpa_tdls_send_discovery_request(wpa_s->wpa, peer); + else + ret = wpa_drv_tdls_oper(wpa_s, TDLS_DISCOVERY_REQ, peer); + + if (ret) { + return wpas_dbus_error_unknown_error( + message, "error performing TDLS discovery"); + } + + return NULL; +} + + +/* + * wpas_dbus_handler_tdls_setup - Setup TDLS session + * @message: Pointer to incoming dbus message + * @wpa_s: wpa_supplicant structure for a network interface + * Returns: NULL indicating success or DBus error message on failure + * + * Handler function for "TDLSSetup" method call of network interface. + */ +DBusMessage * wpas_dbus_handler_tdls_setup(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + u8 peer[ETH_ALEN]; + DBusMessage *error_reply; + int ret; + + error_reply = get_peer_hwaddr_helper(message, __func__, peer); + if (error_reply) + return error_reply; + + wpa_printf(MSG_DEBUG, "DBUS TDLS_SETUP " MACSTR, MAC2STR(peer)); + + wpa_tdls_remove(wpa_s->wpa, peer); + if (wpa_tdls_is_external_setup(wpa_s->wpa)) + ret = wpa_tdls_start(wpa_s->wpa, peer); + else + ret = wpa_drv_tdls_oper(wpa_s, TDLS_SETUP, peer); + + if (ret) { + return wpas_dbus_error_unknown_error( + message, "error performing TDLS setup"); + } + + return NULL; +} + + +/* + * wpas_dbus_handler_tdls_status - Return TDLS session status + * @message: Pointer to incoming dbus message + * @wpa_s: wpa_supplicant structure for a network interface + * Returns: A string representing the state of the link to this TDLS peer + * + * Handler function for "TDLSStatus" method call of network interface. + */ +DBusMessage * wpas_dbus_handler_tdls_status(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + u8 peer[ETH_ALEN]; + DBusMessage *reply; + const char *tdls_status; + + reply = get_peer_hwaddr_helper(message, __func__, peer); + if (reply) + return reply; + + wpa_printf(MSG_DEBUG, "DBUS TDLS_STATUS " MACSTR, MAC2STR(peer)); + + tdls_status = wpa_tdls_get_link_status(wpa_s->wpa, peer); + + reply = dbus_message_new_method_return(message); + dbus_message_append_args(reply, DBUS_TYPE_STRING, + &tdls_status, DBUS_TYPE_INVALID); + return reply; +} + + +/* + * wpas_dbus_handler_tdls_teardown - Teardown TDLS session + * @message: Pointer to incoming dbus message + * @wpa_s: wpa_supplicant structure for a network interface + * Returns: NULL indicating success or DBus error message on failure + * + * Handler function for "TDLSTeardown" method call of network interface. + */ +DBusMessage * wpas_dbus_handler_tdls_teardown(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + u8 peer[ETH_ALEN]; + DBusMessage *error_reply; + int ret; + + error_reply = get_peer_hwaddr_helper(message, __func__, peer); + if (error_reply) + return error_reply; + + wpa_printf(MSG_DEBUG, "DBUS TDLS_TEARDOWN " MACSTR, MAC2STR(peer)); + + if (wpa_tdls_is_external_setup(wpa_s->wpa)) + ret = wpa_tdls_teardown_link( + wpa_s->wpa, peer, + WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED); + else + ret = wpa_drv_tdls_oper(wpa_s, TDLS_TEARDOWN, peer); + + if (ret) { + return wpas_dbus_error_unknown_error( + message, "error performing TDLS teardown"); + } + + return NULL; +} + +#endif /* CONFIG_TDLS */ + + +/** + * wpas_dbus_handler_set_pkcs11_engine_and_module_path - Set PKCS #11 engine and module path + * @message: Pointer to incoming dbus message + * @wpa_s: %wpa_supplicant data structure + * Returns: A dbus message containing an error on failure or NULL on success + * + * Sets the PKCS #11 engine and module path. + */ +DBusMessage * wpas_dbus_handler_set_pkcs11_engine_and_module_path( + DBusMessage *message, struct wpa_supplicant *wpa_s) +{ + DBusMessageIter iter; + char *value = NULL; + char *pkcs11_engine_path = NULL; + char *pkcs11_module_path = NULL; + + dbus_message_iter_init(message, &iter); + dbus_message_iter_get_basic(&iter, &value); + if (value == NULL) { + return dbus_message_new_error( + message, DBUS_ERROR_INVALID_ARGS, + "Invalid pkcs11_engine_path argument"); + } + /* Empty path defaults to NULL */ + if (os_strlen(value)) + pkcs11_engine_path = value; + + dbus_message_iter_next(&iter); + dbus_message_iter_get_basic(&iter, &value); + if (value == NULL) { + os_free(pkcs11_engine_path); + return dbus_message_new_error( + message, DBUS_ERROR_INVALID_ARGS, + "Invalid pkcs11_module_path argument"); + } + /* Empty path defaults to NULL */ + if (os_strlen(value)) + pkcs11_module_path = value; + + if (wpas_set_pkcs11_engine_and_module_path(wpa_s, pkcs11_engine_path, + pkcs11_module_path)) + return dbus_message_new_error( + message, DBUS_ERROR_FAILED, + "Reinit of the EAPOL state machine with the new PKCS " + "#11 engine and module path failed."); + + wpa_dbus_mark_property_changed( + wpa_s->global->dbus, wpa_s->dbus_new_path, + WPAS_DBUS_NEW_IFACE_INTERFACE, "PKCS11EnginePath"); + wpa_dbus_mark_property_changed( + wpa_s->global->dbus, wpa_s->dbus_new_path, + WPAS_DBUS_NEW_IFACE_INTERFACE, "PKCS11ModulePath"); + + return NULL; +} + + /** * wpas_dbus_getter_capabilities - Return interface capabilities * @iter: Pointer to incoming dbus message iter @@ -1840,7 +2247,7 @@ const char *args[] = {"ccmp", "tkip", "none"}; if (!wpa_dbus_dict_append_string_array( &iter_dict, "Pairwise", args, - sizeof(args) / sizeof(char*))) + ARRAY_SIZE(args))) goto nomem; } else { if (!wpa_dbus_dict_begin_string_array(&iter_dict, "Pairwise", @@ -1849,12 +2256,30 @@ &iter_array)) goto nomem; + if (capa.enc & WPA_DRIVER_CAPA_ENC_CCMP_256) { + if (!wpa_dbus_dict_string_array_add_element( + &iter_array, "ccmp-256")) + goto nomem; + } + + if (capa.enc & WPA_DRIVER_CAPA_ENC_GCMP_256) { + if (!wpa_dbus_dict_string_array_add_element( + &iter_array, "gcmp-256")) + goto nomem; + } + if (capa.enc & WPA_DRIVER_CAPA_ENC_CCMP) { if (!wpa_dbus_dict_string_array_add_element( &iter_array, "ccmp")) goto nomem; } + if (capa.enc & WPA_DRIVER_CAPA_ENC_GCMP) { + if (!wpa_dbus_dict_string_array_add_element( + &iter_array, "gcmp")) + goto nomem; + } + if (capa.enc & WPA_DRIVER_CAPA_ENC_TKIP) { if (!wpa_dbus_dict_string_array_add_element( &iter_array, "tkip")) @@ -1881,7 +2306,7 @@ }; if (!wpa_dbus_dict_append_string_array( &iter_dict, "Group", args, - sizeof(args) / sizeof(char*))) + ARRAY_SIZE(args))) goto nomem; } else { if (!wpa_dbus_dict_begin_string_array(&iter_dict, "Group", @@ -1890,12 +2315,30 @@ &iter_array)) goto nomem; + if (capa.enc & WPA_DRIVER_CAPA_ENC_CCMP_256) { + if (!wpa_dbus_dict_string_array_add_element( + &iter_array, "ccmp-256")) + goto nomem; + } + + if (capa.enc & WPA_DRIVER_CAPA_ENC_GCMP_256) { + if (!wpa_dbus_dict_string_array_add_element( + &iter_array, "gcmp-256")) + goto nomem; + } + if (capa.enc & WPA_DRIVER_CAPA_ENC_CCMP) { if (!wpa_dbus_dict_string_array_add_element( &iter_array, "ccmp")) goto nomem; } + if (capa.enc & WPA_DRIVER_CAPA_ENC_GCMP) { + if (!wpa_dbus_dict_string_array_add_element( + &iter_array, "gcmp")) + goto nomem; + } + if (capa.enc & WPA_DRIVER_CAPA_ENC_TKIP) { if (!wpa_dbus_dict_string_array_add_element( &iter_array, "tkip")) @@ -1932,7 +2375,7 @@ }; if (!wpa_dbus_dict_append_string_array( &iter_dict, "KeyMgmt", args, - sizeof(args) / sizeof(char*))) + ARRAY_SIZE(args))) goto nomem; } else { if (!wpa_dbus_dict_begin_string_array(&iter_dict, "KeyMgmt", @@ -2012,7 +2455,7 @@ const char *args[] = { "rsn", "wpa" }; if (!wpa_dbus_dict_append_string_array( &iter_dict, "Protocol", args, - sizeof(args) / sizeof(char*))) + ARRAY_SIZE(args))) goto nomem; } else { if (!wpa_dbus_dict_begin_string_array(&iter_dict, "Protocol", @@ -2047,7 +2490,7 @@ const char *args[] = { "open", "shared", "leap" }; if (!wpa_dbus_dict_append_string_array( &iter_dict, "AuthAlg", args, - sizeof(args) / sizeof(char*))) + ARRAY_SIZE(args))) goto nomem; } else { if (!wpa_dbus_dict_begin_string_array(&iter_dict, "AuthAlg", @@ -2083,7 +2526,7 @@ /***** Scan */ if (!wpa_dbus_dict_append_string_array(&iter_dict, "Scan", scans, - sizeof(scans) / sizeof(char *))) + ARRAY_SIZE(scans))) goto nomem; /***** Modes */ @@ -2300,6 +2743,27 @@ /** + * wpas_dbus_getter_disconnect_reason - Get most recent reason for disconnect + * @iter: Pointer to incoming dbus message iter + * @error: Location to store error on failure + * @user_data: Function specific data + * Returns: TRUE on success, FALSE on failure + * + * Getter for "DisconnectReason" property. The reason is negative if it is + * locally generated. + */ +dbus_bool_t wpas_dbus_getter_disconnect_reason(DBusMessageIter *iter, + DBusError *error, + void *user_data) +{ + struct wpa_supplicant *wpa_s = user_data; + dbus_int32_t reason = wpa_s->disconnect_reason; + return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_INT32, + &reason, error); +} + + +/** * wpas_dbus_getter_bss_expire_age - Get BSS entry expiration age * @iter: Pointer to incoming dbus message iter * @error: Location to store error on failure @@ -2363,7 +2827,7 @@ void *user_data) { struct wpa_supplicant *wpa_s = user_data; - dbus_uint32_t expire_count = wpa_s->conf->bss_expiration_age; + dbus_uint32_t expire_count = wpa_s->conf->bss_expiration_scan_count; return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_UINT32, &expire_count, error); @@ -2463,6 +2927,56 @@ /** + * wpas_dbus_getter_scan_interval - Get scan interval + * @iter: Pointer to incoming dbus message iter + * @error: Location to store error on failure + * @user_data: Function specific data + * Returns: TRUE on success, FALSE on failure + * + * Getter function for "ScanInterval" property. + */ +dbus_bool_t wpas_dbus_getter_scan_interval(DBusMessageIter *iter, + DBusError *error, + void *user_data) +{ + struct wpa_supplicant *wpa_s = user_data; + dbus_int32_t scan_interval = wpa_s->scan_interval; + + return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_INT32, + &scan_interval, error); +} + + +/** + * wpas_dbus_setter_scan_interval - Control scan interval + * @iter: Pointer to incoming dbus message iter + * @error: Location to store error on failure + * @user_data: Function specific data + * Returns: TRUE on success, FALSE on failure + * + * Setter function for "ScanInterval" property. + */ +dbus_bool_t wpas_dbus_setter_scan_interval(DBusMessageIter *iter, + DBusError *error, + void *user_data) +{ + struct wpa_supplicant *wpa_s = user_data; + dbus_int32_t scan_interval; + + if (!wpas_dbus_simple_property_setter(iter, error, DBUS_TYPE_INT32, + &scan_interval)) + return FALSE; + + if (wpa_supplicant_set_scan_interval(wpa_s, scan_interval)) { + dbus_set_error_const(error, DBUS_ERROR_FAILED, + "scan_interval must be >= 0"); + return FALSE; + } + return TRUE; +} + + +/** * wpas_dbus_getter_ifname - Get interface name * @iter: Pointer to incoming dbus message iter * @error: Location to store error on failure @@ -2618,9 +3132,7 @@ void *user_data) { struct wpa_supplicant *wpa_s = user_data; - const char *bridge_ifname; - - bridge_ifname = wpa_s->bridge_ifname ? wpa_s->bridge_ifname : ""; + const char *bridge_ifname = wpa_s->bridge_ifname; return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING, &bridge_ifname, error); } @@ -2644,7 +3156,7 @@ unsigned int i = 0; dbus_bool_t success = FALSE; - paths = os_zalloc(wpa_s->num_bss * sizeof(char *)); + paths = os_calloc(wpa_s->num_bss, sizeof(char *)); if (!paths) { dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory"); return FALSE; @@ -2707,7 +3219,7 @@ if (!network_is_persistent_group(ssid)) num++; - paths = os_zalloc(num * sizeof(char *)); + paths = os_calloc(num, sizeof(char *)); if (!paths) { dbus_set_error(error, DBUS_ERROR_NO_MEMORY, "no memory"); return FALSE; @@ -2742,6 +3254,76 @@ /** + * wpas_dbus_getter_pkcs11_engine_path - Get PKCS #11 engine path + * @iter: Pointer to incoming dbus message iter + * @error: Location to store error on failure + * @user_data: Function specific data + * Returns: A dbus message containing the PKCS #11 engine path + * + * Getter for "PKCS11EnginePath" property. + */ +dbus_bool_t wpas_dbus_getter_pkcs11_engine_path(DBusMessageIter *iter, + DBusError *error, + void *user_data) +{ + struct wpa_supplicant *wpa_s = user_data; + const char *pkcs11_engine_path; + + if (wpa_s->conf == NULL) { + wpa_printf(MSG_ERROR, + "wpas_dbus_getter_pkcs11_engine_path[dbus]: An " + "error occurred getting the PKCS #11 engine path."); + dbus_set_error_const( + error, DBUS_ERROR_FAILED, + "An error occured getting the PKCS #11 engine path."); + return FALSE; + } + + if (wpa_s->conf->pkcs11_engine_path == NULL) + pkcs11_engine_path = ""; + else + pkcs11_engine_path = wpa_s->conf->pkcs11_engine_path; + return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING, + &pkcs11_engine_path, error); +} + + +/** + * wpas_dbus_getter_pkcs11_module_path - Get PKCS #11 module path + * @iter: Pointer to incoming dbus message iter + * @error: Location to store error on failure + * @user_data: Function specific data + * Returns: A dbus message containing the PKCS #11 module path + * + * Getter for "PKCS11ModulePath" property. + */ +dbus_bool_t wpas_dbus_getter_pkcs11_module_path(DBusMessageIter *iter, + DBusError *error, + void *user_data) +{ + struct wpa_supplicant *wpa_s = user_data; + const char *pkcs11_module_path; + + if (wpa_s->conf == NULL) { + wpa_printf(MSG_ERROR, + "wpas_dbus_getter_pkcs11_module_path[dbus]: An " + "error occurred getting the PKCS #11 module path."); + dbus_set_error_const( + error, DBUS_ERROR_FAILED, + "An error occured getting the PKCS #11 module path."); + return FALSE; + } + + if (wpa_s->conf->pkcs11_module_path == NULL) + pkcs11_module_path = ""; + else + pkcs11_module_path = wpa_s->conf->pkcs11_module_path; + return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING, + &pkcs11_module_path, error); +} + + +/** * wpas_dbus_getter_blobs - Get all blobs defined for this interface * @iter: Pointer to incoming dbus message iter * @error: Location to store error on failure @@ -3039,7 +3621,7 @@ { DBusMessageIter iter_dict, variant_iter; const char *group; - const char *pairwise[2]; /* max 2 pairwise ciphers is supported */ + const char *pairwise[5]; /* max 5 pairwise ciphers is supported */ const char *key_mgmt[7]; /* max 7 key managements may be supported */ int n; @@ -3082,9 +3664,18 @@ case WPA_CIPHER_CCMP: group = "ccmp"; break; + case WPA_CIPHER_GCMP: + group = "gcmp"; + break; case WPA_CIPHER_WEP104: group = "wep104"; break; + case WPA_CIPHER_CCMP_256: + group = "ccmp-256"; + break; + case WPA_CIPHER_GCMP_256: + group = "gcmp-256"; + break; default: group = ""; break; @@ -3099,6 +3690,12 @@ pairwise[n++] = "tkip"; if (ie_data->pairwise_cipher & WPA_CIPHER_CCMP) pairwise[n++] = "ccmp"; + if (ie_data->pairwise_cipher & WPA_CIPHER_GCMP) + pairwise[n++] = "gcmp"; + if (ie_data->pairwise_cipher & WPA_CIPHER_CCMP_256) + pairwise[n++] = "ccmp-256"; + if (ie_data->pairwise_cipher & WPA_CIPHER_GCMP_256) + pairwise[n++] = "gcmp-256"; if (!wpa_dbus_dict_append_string_array(&iter_dict, "Pairwise", pairwise, n)) @@ -3206,6 +3803,63 @@ /** + * wpas_dbus_getter_bss_wps - Return the WPS options of a BSS + * @iter: Pointer to incoming dbus message iter + * @error: Location to store error on failure + * @user_data: Function specific data + * Returns: TRUE on success, FALSE on failure + * + * Getter for "WPS" property. + */ +dbus_bool_t wpas_dbus_getter_bss_wps(DBusMessageIter *iter, DBusError *error, + void *user_data) +{ + struct bss_handler_args *args = user_data; + struct wpa_bss *res; +#ifdef CONFIG_WPS + struct wpabuf *wps_ie; +#endif /* CONFIG_WPS */ + DBusMessageIter iter_dict, variant_iter; + const char *type = ""; + + res = get_bss_helper(args, error, __func__); + if (!res) + return FALSE; + + if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, + "a{sv}", &variant_iter)) + goto nomem; + + if (!wpa_dbus_dict_open_write(&variant_iter, &iter_dict)) + goto nomem; + +#ifdef CONFIG_WPS + wps_ie = wpa_bss_get_vendor_ie_multi(res, WPS_IE_VENDOR_TYPE); + if (wps_ie) { + if (wps_is_selected_pbc_registrar(wps_ie)) + type = "pbc"; + else if (wps_is_selected_pin_registrar(wps_ie)) + type = "pin"; + } +#endif /* CONFIG_WPS */ + + if (!wpa_dbus_dict_append_string(&iter_dict, "Type", type)) + goto nomem; + + if (!wpa_dbus_dict_close_write(&variant_iter, &iter_dict)) + goto nomem; + if (!dbus_message_iter_close_container(iter, &variant_iter)) + goto nomem; + + return TRUE; + +nomem: + dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory"); + return FALSE; +} + + +/** * wpas_dbus_getter_bss_ies - Return all IEs of a BSS * @iter: Pointer to incoming dbus message iter * @error: Location to store error on failure @@ -3365,3 +4019,139 @@ dbus_message_iter_recurse(iter, &variant_iter); return set_network_properties(net->wpa_s, ssid, &variant_iter, error); } + + +#ifdef CONFIG_AP + +DBusMessage * wpas_dbus_handler_subscribe_preq( + DBusMessage *message, struct wpa_supplicant *wpa_s) +{ + struct wpas_dbus_priv *priv = wpa_s->global->dbus; + char *name; + + if (wpa_s->preq_notify_peer != NULL) { + if (os_strcmp(dbus_message_get_sender(message), + wpa_s->preq_notify_peer) == 0) + return NULL; + + return dbus_message_new_error(message, + WPAS_DBUS_ERROR_SUBSCRIPTION_IN_USE, + "Another application is already subscribed"); + } + + name = os_strdup(dbus_message_get_sender(message)); + if (!name) + return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY, + "out of memory"); + + wpa_s->preq_notify_peer = name; + + /* Subscribe to clean up if application closes socket */ + wpas_dbus_subscribe_noc(priv); + + /* + * Double-check it's still alive to make sure that we didn't + * miss the NameOwnerChanged signal, e.g. while strdup'ing. + */ + if (!dbus_bus_name_has_owner(priv->con, name, NULL)) { + /* + * Application no longer exists, clean up. + * The return value is irrelevant now. + * + * Need to check if the NameOwnerChanged handling + * already cleaned up because we have processed + * DBus messages while checking if the name still + * has an owner. + */ + if (!wpa_s->preq_notify_peer) + return NULL; + os_free(wpa_s->preq_notify_peer); + wpa_s->preq_notify_peer = NULL; + wpas_dbus_unsubscribe_noc(priv); + } + + return NULL; +} + + +DBusMessage * wpas_dbus_handler_unsubscribe_preq( + DBusMessage *message, struct wpa_supplicant *wpa_s) +{ + struct wpas_dbus_priv *priv = wpa_s->global->dbus; + + if (!wpa_s->preq_notify_peer) + return dbus_message_new_error(message, + WPAS_DBUS_ERROR_NO_SUBSCRIPTION, + "Not subscribed"); + + if (os_strcmp(wpa_s->preq_notify_peer, + dbus_message_get_sender(message))) + return dbus_message_new_error(message, + WPAS_DBUS_ERROR_SUBSCRIPTION_EPERM, + "Can't unsubscribe others"); + + os_free(wpa_s->preq_notify_peer); + wpa_s->preq_notify_peer = NULL; + wpas_dbus_unsubscribe_noc(priv); + return NULL; +} + + +void wpas_dbus_signal_preq(struct wpa_supplicant *wpa_s, + const u8 *addr, const u8 *dst, const u8 *bssid, + const u8 *ie, size_t ie_len, u32 ssi_signal) +{ + DBusMessage *msg; + DBusMessageIter iter, dict_iter; + struct wpas_dbus_priv *priv = wpa_s->global->dbus; + + /* Do nothing if the control interface is not turned on */ + if (priv == NULL) + return; + + if (wpa_s->preq_notify_peer == NULL) + return; + + msg = dbus_message_new_signal(wpa_s->dbus_new_path, + WPAS_DBUS_NEW_IFACE_INTERFACE, + "ProbeRequest"); + if (msg == NULL) + return; + + dbus_message_set_destination(msg, wpa_s->preq_notify_peer); + + dbus_message_iter_init_append(msg, &iter); + + if (!wpa_dbus_dict_open_write(&iter, &dict_iter)) + goto fail; + if (addr && !wpa_dbus_dict_append_byte_array(&dict_iter, "addr", + (const char *) addr, + ETH_ALEN)) + goto fail; + if (dst && !wpa_dbus_dict_append_byte_array(&dict_iter, "dst", + (const char *) dst, + ETH_ALEN)) + goto fail; + if (bssid && !wpa_dbus_dict_append_byte_array(&dict_iter, "bssid", + (const char *) bssid, + ETH_ALEN)) + goto fail; + if (ie && ie_len && !wpa_dbus_dict_append_byte_array(&dict_iter, "ies", + (const char *) ie, + ie_len)) + goto fail; + if (ssi_signal && !wpa_dbus_dict_append_int32(&dict_iter, "signal", + ssi_signal)) + goto fail; + if (!wpa_dbus_dict_close_write(&iter, &dict_iter)) + goto fail; + + dbus_connection_send(priv->con, msg, NULL); + goto out; +fail: + wpa_printf(MSG_ERROR, "dbus: Failed to construct signal"); +out: + dbus_message_unref(msg); +} + +#endif /* CONFIG_AP */ diff -Nru wpa-1.0/wpa_supplicant/dbus/dbus_new_handlers.h wpa-2.1/wpa_supplicant/dbus/dbus_new_handlers.h --- wpa-1.0/wpa_supplicant/dbus/dbus_new_handlers.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/dbus/dbus_new_handlers.h 2014-02-04 11:23:35.000000000 +0000 @@ -3,14 +3,8 @@ * Copyright (c) 2006, Dan Williams and Red Hat, Inc. * Copyright (c) 2009-2010, Witold Sowa * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef CTRL_IFACE_DBUS_NEW_HANDLERS_H @@ -86,6 +80,10 @@ dbus_bool_t wpas_dbus_getter_eap_methods(DBusMessageIter *iter, DBusError *error, void *user_data); +dbus_bool_t wpas_dbus_getter_global_capabilities(DBusMessageIter *iter, + DBusError *error, + void *user_data); + DBusMessage * wpas_dbus_handler_scan(DBusMessage *message, struct wpa_supplicant *wpa_s); @@ -100,6 +98,9 @@ DBusMessage * wpas_dbus_handler_add_network(DBusMessage *message, struct wpa_supplicant *wpa_s); +DBusMessage * wpas_dbus_handler_reassociate(DBusMessage *message, + struct wpa_supplicant *wpa_s); + DBusMessage * wpas_dbus_handler_remove_network(DBusMessage *message, struct wpa_supplicant *wpa_s); @@ -121,9 +122,21 @@ DBusMessage * wpas_dbus_handler_remove_blob(DBusMessage *message, struct wpa_supplicant *wpa_s); +DBusMessage * wpas_dbus_handler_set_pkcs11_engine_and_module_path( + DBusMessage *message, struct wpa_supplicant *wpa_s); + DBusMessage * wpas_dbus_handler_flush_bss(DBusMessage *message, struct wpa_supplicant *wpa_s); +DBusMessage * wpas_dbus_handler_autoscan(DBusMessage *message, + struct wpa_supplicant *wpa_s); + +DBusMessage * wpas_dbus_handler_eap_logoff(DBusMessage *message, + struct wpa_supplicant *wpa_s); + +DBusMessage * wpas_dbus_handler_eap_logon(DBusMessage *message, + struct wpa_supplicant *wpa_s); + dbus_bool_t wpas_dbus_getter_capabilities(DBusMessageIter *iter, DBusError *error, void *user_data); @@ -147,6 +160,10 @@ DBusError *error, void *user_data); +dbus_bool_t wpas_dbus_getter_disconnect_reason(DBusMessageIter *iter, + DBusError *error, + void *user_data); + dbus_bool_t wpas_dbus_getter_bss_expire_age(DBusMessageIter *iter, DBusError *error, void *user_data); @@ -168,6 +185,14 @@ dbus_bool_t wpas_dbus_setter_country(DBusMessageIter *iter, DBusError *error, void *user_data); +dbus_bool_t wpas_dbus_getter_scan_interval(DBusMessageIter *iter, + DBusError *error, + void *user_data); + +dbus_bool_t wpas_dbus_setter_scan_interval(DBusMessageIter *iter, + DBusError *error, + void *user_data); + dbus_bool_t wpas_dbus_getter_ifname(DBusMessageIter *iter, DBusError *error, void *user_data); @@ -196,6 +221,14 @@ dbus_bool_t wpas_dbus_getter_networks(DBusMessageIter *iter, DBusError *error, void *user_data); +dbus_bool_t wpas_dbus_getter_pkcs11_engine_path(DBusMessageIter *iter, + DBusError *error, + void *user_data); + +dbus_bool_t wpas_dbus_getter_pkcs11_module_path(DBusMessageIter *iter, + DBusError *error, + void *user_data); + dbus_bool_t wpas_dbus_getter_blobs(DBusMessageIter *iter, DBusError *error, void *user_data); @@ -226,6 +259,9 @@ dbus_bool_t wpas_dbus_getter_bss_rsn(DBusMessageIter *iter, DBusError *error, void *user_data); +dbus_bool_t wpas_dbus_getter_bss_wps(DBusMessageIter *iter, DBusError *error, + void *user_data); + dbus_bool_t wpas_dbus_getter_bss_ies(DBusMessageIter *iter, DBusError *error, void *user_data); @@ -253,9 +289,23 @@ DBusError *error, void *user_data); +DBusMessage * wpas_dbus_handler_tdls_discover(DBusMessage *message, + struct wpa_supplicant *wpa_s); +DBusMessage * wpas_dbus_handler_tdls_setup(DBusMessage *message, + struct wpa_supplicant *wpa_s); +DBusMessage * wpas_dbus_handler_tdls_status(DBusMessage *message, + struct wpa_supplicant *wpa_s); +DBusMessage * wpas_dbus_handler_tdls_teardown(DBusMessage *message, + struct wpa_supplicant *wpa_s); + DBusMessage * wpas_dbus_error_invalid_args(DBusMessage *message, const char *arg); DBusMessage * wpas_dbus_error_unknown_error(DBusMessage *message, const char *arg); +DBusMessage * wpas_dbus_handler_subscribe_preq( + DBusMessage *message, struct wpa_supplicant *wpa_s); +DBusMessage * wpas_dbus_handler_unsubscribe_preq( + DBusMessage *message, struct wpa_supplicant *wpa_s); + #endif /* CTRL_IFACE_DBUS_HANDLERS_NEW_H */ diff -Nru wpa-1.0/wpa_supplicant/dbus/dbus_new_handlers_p2p.c wpa-2.1/wpa_supplicant/dbus/dbus_new_handlers_p2p.c --- wpa-1.0/wpa_supplicant/dbus/dbus_new_handlers_p2p.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/dbus/dbus_new_handlers_p2p.c 2014-02-04 11:23:35.000000000 +0000 @@ -1,14 +1,9 @@ /* * WPA Supplicant / dbus-based control interface (P2P) + * Copyright (c) 2011-2012, Intel Corporation * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -45,7 +40,7 @@ if (!peer_path) return -1; - p = strrchr(peer_path, '/'); + p = os_strrchr(peer_path, '/'); if (!p) return -1; p++; @@ -132,7 +127,7 @@ } wpas_p2p_find(wpa_s, timeout, type, num_req_dev_types, req_dev_types, - NULL); + NULL, 0); os_free(req_dev_types); return reply; @@ -351,13 +346,14 @@ if (ssid == NULL || ssid->disabled != 2) goto inv_args; - if (wpas_p2p_group_add_persistent(wpa_s, ssid, 0, freq)) { + if (wpas_p2p_group_add_persistent(wpa_s, ssid, 0, freq, 0, 0, + NULL, 0)) { reply = wpas_dbus_error_unknown_error( message, "Failed to reinvoke a persistent group"); goto out; } - } else if (wpas_p2p_group_add(wpa_s, persistent_group, freq)) + } else if (wpas_p2p_group_add(wpa_s, persistent_group, freq, 0, 0)) goto inv_args; out: @@ -508,8 +504,8 @@ goto inv_args; new_pin = wpas_p2p_connect(wpa_s, addr, pin, wps_method, - persistent_group, join, authorize_only, - go_intent, freq); + persistent_group, 0, join, authorize_only, + go_intent, freq, -1, 0, 0, 0); if (new_pin >= 0) { char npin[9]; @@ -635,7 +631,8 @@ if (ssid == NULL || ssid->disabled != 2) goto err; - if (wpas_p2p_invite(wpa_s, peer_addr, ssid, NULL) < 0) { + if (wpas_p2p_invite(wpa_s, peer_addr, ssid, NULL, 0, 0, 0, 0) < + 0) { reply = wpas_dbus_error_unknown_error( message, "Failed to reinvoke a persistent group"); @@ -692,7 +689,8 @@ os_strcmp(config_method, "pushbutton")) return wpas_dbus_error_invalid_args(message, NULL); - if (wpas_p2p_prov_disc(wpa_s, peer_addr, config_method, 0) < 0) + if (wpas_p2p_prov_disc(wpa_s, peer_addr, config_method, + WPAS_P2P_PD_FOR_GO_NEG) < 0) return wpas_dbus_error_unknown_error(message, "Failed to send provision discovery request"); @@ -1053,7 +1051,7 @@ * Now construct the peer object paths in a form suitable for * array_property_getter helper below. */ - peer_obj_paths = os_zalloc(num * sizeof(char *)); + peer_obj_paths = os_calloc(num, sizeof(char *)); if (!peer_obj_paths) { out_of_mem = 1; @@ -1513,7 +1511,7 @@ if (network_is_persistent_group(ssid)) num++; - paths = os_zalloc(num * sizeof(char *)); + paths = os_calloc(num, sizeof(char *)); if (!paths) { dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory"); return FALSE; @@ -1820,7 +1818,7 @@ num_members = p2p_get_group_num_members(wpa_s->p2p_group); - paths = os_zalloc(num_members * sizeof(char *)); + paths = os_calloc(num_members, sizeof(char *)); if (!paths) goto out_of_memory; @@ -2073,7 +2071,7 @@ if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL)) goto error; - if (wpa_dbus_dict_has_dict_entry(&iter_dict)) { + while (wpa_dbus_dict_has_dict_entry(&iter_dict)) { if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) goto error; @@ -2085,23 +2083,30 @@ bonjour = 1; else goto error_clear; - wpa_dbus_dict_entry_clear(&entry); + } else if (!os_strcmp(entry.key, "version") && + entry.type == DBUS_TYPE_INT32) { + version = entry.uint32_value; + } else if (!os_strcmp(entry.key, "service") && + (entry.type == DBUS_TYPE_STRING)) { + service = os_strdup(entry.str_value); + } else if (!os_strcmp(entry.key, "query")) { + if ((entry.type != DBUS_TYPE_ARRAY) || + (entry.array_type != DBUS_TYPE_BYTE)) + goto error_clear; + query = wpabuf_alloc_copy( + entry.bytearray_value, + entry.array_len); + } else if (!os_strcmp(entry.key, "response")) { + if ((entry.type != DBUS_TYPE_ARRAY) || + (entry.array_type != DBUS_TYPE_BYTE)) + goto error_clear; + resp = wpabuf_alloc_copy(entry.bytearray_value, + entry.array_len); } + wpa_dbus_dict_entry_clear(&entry); } if (upnp == 1) { - while (wpa_dbus_dict_has_dict_entry(&iter_dict)) { - if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) - goto error; - - if (!os_strcmp(entry.key, "version") && - entry.type == DBUS_TYPE_INT32) - version = entry.uint32_value; - else if (!os_strcmp(entry.key, "service") && - entry.type == DBUS_TYPE_STRING) - service = os_strdup(entry.str_value); - wpa_dbus_dict_entry_clear(&entry); - } if (version <= 0 || service == NULL) goto error; @@ -2109,37 +2114,15 @@ goto error; os_free(service); + service = NULL; } else if (bonjour == 1) { - while (wpa_dbus_dict_has_dict_entry(&iter_dict)) { - if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) - goto error; - - if (!os_strcmp(entry.key, "query")) { - if ((entry.type != DBUS_TYPE_ARRAY) || - (entry.array_type != DBUS_TYPE_BYTE)) - goto error_clear; - query = wpabuf_alloc_copy( - entry.bytearray_value, - entry.array_len); - } else if (!os_strcmp(entry.key, "response")) { - if ((entry.type != DBUS_TYPE_ARRAY) || - (entry.array_type != DBUS_TYPE_BYTE)) - goto error_clear; - resp = wpabuf_alloc_copy(entry.bytearray_value, - entry.array_len); - } - - wpa_dbus_dict_entry_clear(&entry); - } - if (query == NULL || resp == NULL) goto error; - if (wpas_p2p_service_add_bonjour(wpa_s, query, resp) < 0) { - wpabuf_free(query); - wpabuf_free(resp); + if (wpas_p2p_service_add_bonjour(wpa_s, query, resp) < 0) goto error; - } + query = NULL; + resp = NULL; } else goto error; @@ -2147,6 +2130,9 @@ error_clear: wpa_dbus_dict_entry_clear(&entry); error: + os_free(service); + wpabuf_free(query); + wpabuf_free(resp); return wpas_dbus_error_invalid_args(message, NULL); } @@ -2316,13 +2302,11 @@ if (version <= 0 || service == NULL) goto error; - ref = (unsigned long) wpas_p2p_sd_request_upnp(wpa_s, addr, - version, - service); + ref = wpas_p2p_sd_request_upnp(wpa_s, addr, version, service); } else { if (tlv == NULL) goto error; - ref = (unsigned long)wpas_p2p_sd_request(wpa_s, addr, tlv); + ref = wpas_p2p_sd_request(wpa_s, addr, tlv); wpabuf_free(tlv); } @@ -2423,7 +2407,7 @@ if (req == 0) goto error; - if (!wpas_p2p_sd_cancel_request(wpa_s, (void *)(unsigned long) req)) + if (!wpas_p2p_sd_cancel_request(wpa_s, req)) goto error; return NULL; diff -Nru wpa-1.0/wpa_supplicant/dbus/dbus_new_handlers_p2p.h wpa-2.1/wpa_supplicant/dbus/dbus_new_handlers_p2p.h --- wpa-1.0/wpa_supplicant/dbus/dbus_new_handlers_p2p.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/dbus/dbus_new_handlers_p2p.h 2014-02-04 11:23:35.000000000 +0000 @@ -1,15 +1,9 @@ - /* * WPA Supplicant / dbus-based control interface for p2p + * Copyright (c) 2011-2012, Intel Corporation * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef DBUS_NEW_HANDLERS_P2P_H diff -Nru wpa-1.0/wpa_supplicant/dbus/dbus_new_handlers_wps.c wpa-2.1/wpa_supplicant/dbus/dbus_new_handlers_wps.c --- wpa-1.0/wpa_supplicant/dbus/dbus_new_handlers_wps.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/dbus/dbus_new_handlers_wps.c 2014-02-04 11:23:35.000000000 +0000 @@ -3,14 +3,8 @@ * Copyright (c) 2006, Dan Williams and Red Hat, Inc. * Copyright (c) 2009, Witold Sowa * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -279,7 +273,7 @@ ret = wpa_supplicant_ap_wps_pin(wpa_s, params.bssid, params.pin, - npin, sizeof(npin)); + npin, sizeof(npin), 0); else #endif /* CONFIG_AP */ { diff -Nru wpa-1.0/wpa_supplicant/dbus/dbus_new_helpers.c wpa-2.1/wpa_supplicant/dbus/dbus_new_helpers.c --- wpa-1.0/wpa_supplicant/dbus/dbus_new_helpers.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/dbus/dbus_new_helpers.c 2014-02-04 11:23:35.000000000 +0000 @@ -3,14 +3,8 @@ * Copyright (c) 2006, Dan Williams and Red Hat, Inc. * Copyright (c) 2009, Witold Sowa * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "utils/includes.h" @@ -596,11 +590,11 @@ if (!obj_desc) { wpa_printf(MSG_ERROR, "dbus: %s: Could not obtain object's " "private data: %s", __func__, path); - } else { - eloop_cancel_timeout(flush_object_timeout_handler, con, - obj_desc); + return 0; } + eloop_cancel_timeout(flush_object_timeout_handler, con, obj_desc); + if (!dbus_connection_unregister_object_path(con, path)) return -1; diff -Nru wpa-1.0/wpa_supplicant/dbus/dbus_new_helpers.h wpa-2.1/wpa_supplicant/dbus/dbus_new_helpers.h --- wpa-1.0/wpa_supplicant/dbus/dbus_new_helpers.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/dbus/dbus_new_helpers.h 2014-02-04 11:23:35.000000000 +0000 @@ -3,14 +3,8 @@ * Copyright (c) 2006, Dan Williams and Red Hat, Inc. * Copyright (c) 2009, Witold Sowa * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef WPA_DBUS_CTRL_H diff -Nru wpa-1.0/wpa_supplicant/dbus/dbus_new_introspect.c wpa-2.1/wpa_supplicant/dbus/dbus_new_introspect.c --- wpa-1.0/wpa_supplicant/dbus/dbus_new_introspect.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/dbus/dbus_new_introspect.c 2014-02-04 11:23:35.000000000 +0000 @@ -4,14 +4,8 @@ * Copyright (c) 2009, Witold Sowa * Copyright (c) 2010, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "utils/includes.h" diff -Nru wpa-1.0/wpa_supplicant/dbus/dbus_old.c wpa-2.1/wpa_supplicant/dbus/dbus_old.c --- wpa-1.0/wpa_supplicant/dbus/dbus_old.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/dbus/dbus_old.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * WPA Supplicant / dbus-based control interface * Copyright (c) 2006, Dan Williams and Red Hat, Inc. * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -23,7 +17,6 @@ #include "../bss.h" #include "dbus_old.h" #include "dbus_old_handlers.h" -#include "dbus_common.h" #include "dbus_common_i.h" @@ -275,10 +268,12 @@ reply = wpas_dbus_iface_get_state(message, wpa_s); else if (!strcmp(method, "scanning")) reply = wpas_dbus_iface_get_scanning(message, wpa_s); +#ifndef CONFIG_NO_CONFIG_BLOBS else if (!strcmp(method, "setBlobs")) reply = wpas_dbus_iface_set_blobs(message, wpa_s); else if (!strcmp(method, "removeBlobs")) reply = wpas_dbus_iface_remove_blobs(message, wpa_s); +#endif /* CONFIG_NO_CONFIG_BLOBS */ #ifdef CONFIG_WPS else if (!os_strcmp(method, "wpsPbc")) reply = wpas_dbus_iface_wps_pbc(message, wpa_s); diff -Nru wpa-1.0/wpa_supplicant/dbus/dbus_old.h wpa-2.1/wpa_supplicant/dbus/dbus_old.h --- wpa-1.0/wpa_supplicant/dbus/dbus_old.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/dbus/dbus_old.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * WPA Supplicant / dbus-based control interface * Copyright (c) 2006, Dan Williams and Red Hat, Inc. * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef CTRL_IFACE_DBUS_H diff -Nru wpa-1.0/wpa_supplicant/dbus/dbus_old_handlers.c wpa-2.1/wpa_supplicant/dbus/dbus_old_handlers.c --- wpa-1.0/wpa_supplicant/dbus/dbus_old_handlers.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/dbus/dbus_old_handlers.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * WPA Supplicant / dbus-based control interface * Copyright (c) 2006, Dan Williams and Red Hat, Inc. * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -31,10 +25,6 @@ #include "dbus_old_handlers.h" #include "dbus_dict_helpers.h" -extern int wpa_debug_level; -extern int wpa_debug_show_keys; -extern int wpa_debug_timestamp; - /** * wpas_dbus_new_invalid_opts_error - Return a new invalid options error message * @message: Pointer to incoming dbus message this error refers to @@ -229,7 +219,7 @@ goto out; } - if (!wpa_supplicant_remove_iface(global, wpa_s)) { + if (!wpa_supplicant_remove_iface(global, wpa_s, 0)) { reply = wpas_dbus_new_success_reply(message); } else { reply = dbus_message_new_error(message, @@ -337,7 +327,7 @@ DBusMessage * wpas_dbus_iface_scan(DBusMessage *message, struct wpa_supplicant *wpa_s) { - wpa_s->scan_req = 2; + wpa_s->scan_req = MANUAL_SCAN_REQ; wpa_supplicant_req_scan(wpa_s, 0, 0); return wpas_dbus_new_success_reply(message); } @@ -545,7 +535,7 @@ const char *args[] = {"CCMP", "TKIP", "NONE"}; if (!wpa_dbus_dict_append_string_array( &iter_dict, "pairwise", args, - sizeof(args) / sizeof(char*))) + ARRAY_SIZE(args))) goto error; } } else { @@ -588,7 +578,7 @@ }; if (!wpa_dbus_dict_append_string_array( &iter_dict, "group", args, - sizeof(args) / sizeof(char*))) + ARRAY_SIZE(args))) goto error; } } else { @@ -638,7 +628,7 @@ }; if (!wpa_dbus_dict_append_string_array( &iter_dict, "key_mgmt", args, - sizeof(args) / sizeof(char*))) + ARRAY_SIZE(args))) goto error; } } else { @@ -689,7 +679,7 @@ const char *args[] = { "RSN", "WPA" }; if (!wpa_dbus_dict_append_string_array( &iter_dict, "proto", args, - sizeof(args) / sizeof(char*))) + ARRAY_SIZE(args))) goto error; } } else { @@ -726,7 +716,7 @@ const char *args[] = { "OPEN", "SHARED", "LEAP" }; if (!wpa_dbus_dict_append_string_array( &iter_dict, "auth_alg", args, - sizeof(args) / sizeof(char*))) + ARRAY_SIZE(args))) goto error; } } else { @@ -1306,6 +1296,8 @@ } +#ifndef CONFIG_NO_CONFIG_BLOBS + /** * wpas_dbus_iface_set_blobs - Store named binary blobs (ie, for certificates) * @message: Pointer to incoming dbus message @@ -1435,6 +1427,8 @@ return wpas_dbus_new_success_reply(message); } +#endif /* CONFIG_NO_CONFIG_BLOBS */ + /** * wpas_dbus_iface_flush - Clear BSS of old or all inactive entries diff -Nru wpa-1.0/wpa_supplicant/dbus/dbus_old_handlers.h wpa-2.1/wpa_supplicant/dbus/dbus_old_handlers.h --- wpa-1.0/wpa_supplicant/dbus/dbus_old_handlers.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/dbus/dbus_old_handlers.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * WPA Supplicant / dbus-based control interface * Copyright (c) 2006, Dan Williams and Red Hat, Inc. * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef CTRL_IFACE_DBUS_HANDLERS_H diff -Nru wpa-1.0/wpa_supplicant/dbus/dbus_old_handlers_wps.c wpa-2.1/wpa_supplicant/dbus/dbus_old_handlers_wps.c --- wpa-1.0/wpa_supplicant/dbus/dbus_old_handlers_wps.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/dbus/dbus_old_handlers_wps.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * WPA Supplicant / dbus-based control interface (WPS) * Copyright (c) 2006, Dan Williams and Red Hat, Inc. * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff -Nru wpa-1.0/wpa_supplicant/dbus/Makefile wpa-2.1/wpa_supplicant/dbus/Makefile --- wpa-1.0/wpa_supplicant/dbus/Makefile 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/dbus/Makefile 2014-02-04 11:23:35.000000000 +0000 @@ -1,7 +1,7 @@ all: libwpadbus.a clean: - rm -f *~ *.o *.d + rm -f *~ *.o *.d *.gcno *.gcda *.gcov rm -f libwpadbus.a install: diff -Nru wpa-1.0/wpa_supplicant/defconfig wpa-2.1/wpa_supplicant/defconfig --- wpa-1.0/wpa_supplicant/defconfig 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/defconfig 2014-02-04 11:23:35.000000000 +0000 @@ -20,63 +20,6 @@ # used to fix build issues on such systems (krb5.h not found). #CFLAGS += -I/usr/include/kerberos -# Example configuration for various cross-compilation platforms - -#### sveasoft (e.g., for Linksys WRT54G) ###################################### -#CC=mipsel-uclibc-gcc -#CC=/opt/brcm/hndtools-mipsel-uclibc/bin/mipsel-uclibc-gcc -#CFLAGS += -Os -#CPPFLAGS += -I../src/include -I../../src/router/openssl/include -#LIBS += -L/opt/brcm/hndtools-mipsel-uclibc-0.9.19/lib -lssl -############################################################################### - -#### openwrt (e.g., for Linksys WRT54G) ####################################### -#CC=mipsel-uclibc-gcc -#CC=/opt/brcm/hndtools-mipsel-uclibc/bin/mipsel-uclibc-gcc -#CFLAGS += -Os -#CPPFLAGS=-I../src/include -I../openssl-0.9.7d/include \ -# -I../WRT54GS/release/src/include -#LIBS = -lssl -############################################################################### - - -# Driver interface for Host AP driver -CONFIG_DRIVER_HOSTAP=y - -# Driver interface for Agere driver -#CONFIG_DRIVER_HERMES=y -# Change include directories to match with the local setup -#CFLAGS += -I../../hcf -I../../include -I../../include/hcf -#CFLAGS += -I../../include/wireless - -# Driver interface for madwifi driver -# Deprecated; use CONFIG_DRIVER_WEXT=y instead. -#CONFIG_DRIVER_MADWIFI=y -# Set include directory to the madwifi source tree -#CFLAGS += -I../../madwifi - -# Driver interface for ndiswrapper -# Deprecated; use CONFIG_DRIVER_WEXT=y instead. -#CONFIG_DRIVER_NDISWRAPPER=y - -# Driver interface for Atmel driver -CONFIG_DRIVER_ATMEL=y - -# Driver interface for old Broadcom driver -# Please note that the newer Broadcom driver ("hybrid Linux driver") supports -# Linux wireless extensions and does not need (or even work) with the old -# driver wrapper. Use CONFIG_DRIVER_WEXT=y with that driver. -#CONFIG_DRIVER_BROADCOM=y -# Example path for wlioctl.h; change to match your configuration -#CFLAGS += -I/opt/WRT54GS/release/src/include - -# Driver interface for Intel ipw2100/2200 driver -# Deprecated; use CONFIG_DRIVER_WEXT=y instead. -#CONFIG_DRIVER_IPW=y - -# Driver interface for Ralink driver -#CONFIG_DRIVER_RALINK=y - # Driver interface for generic Linux wireless extensions # Note: WEXT is deprecated in the current Linux kernel version and no new # functionality is added to it. nl80211-based interface is the new @@ -88,6 +31,19 @@ # Driver interface for Linux drivers using the nl80211 kernel interface CONFIG_DRIVER_NL80211=y +# driver_nl80211.c requires libnl. If you are compiling it yourself +# you may need to point hostapd to your version of libnl. +# +#CFLAGS += -I$ +#LIBS += -L$ + +# Use libnl v2.0 (or 3.0) libraries. +#CONFIG_LIBNL20=y + +# Use libnl 3.2 libraries (if this is selected, CONFIG_LIBNL20 is ignored) +#CONFIG_LIBNL32=y + + # Driver interface for FreeBSD net80211 layer (e.g., Atheros driver) #CONFIG_DRIVER_BSD=y #CFLAGS += -I/usr/local/include @@ -147,10 +103,9 @@ CONFIG_EAP_TTLS=y # EAP-FAST -# Note: Default OpenSSL package does not include support for all the -# functionality needed for EAP-FAST. If EAP-FAST is enabled with OpenSSL, -# the OpenSSL library must be patched (openssl-0.9.8d-tls-extensions.patch) -# to add the needed functions. +# Note: If OpenSSL is used as the TLS library, OpenSSL 1.0 or newer is needed +# for EAP-FAST support. Older OpenSSL releases would need to be patched, e.g., +# with openssl-0.9.8x-tls-extensions.patch, to add the needed functions. #CONFIG_EAP_FAST=y # EAP-GTC @@ -204,10 +159,15 @@ # Disable credentials for an open network by default when acting as a WPS # registrar. #CONFIG_WPS_REG_DISABLE_OPEN=y +# Enable WPS support with NFC config method +#CONFIG_WPS_NFC=y # EAP-IKEv2 #CONFIG_EAP_IKEV2=y +# EAP-EKE +#CONFIG_EAP_EKE=y + # PKCS#12 (PFX) support (used to read private key and certificate file from # a file that usually has extension .p12 or .pfx) CONFIG_PKCS12=y @@ -220,6 +180,12 @@ # Enable this if EAP-SIM or EAP-AKA is included #CONFIG_PCSC=y +# Support HT overrides (disable HT/HT40, mask MCS rates, etc.) +#CONFIG_HT_OVERRIDES=y + +# Support VHT overrides (disable VHT, mask MCS rates, etc.) +#CONFIG_VHT_OVERRIDES=y + # Development testing #CONFIG_EAPOL_TEST=y @@ -227,6 +193,7 @@ # unix = UNIX domain sockets (default for Linux/*BSD) # udp = UDP sockets using localhost (127.0.0.1) # named_pipe = Windows Named Pipe (default for Windows) +# udp-remote = UDP sockets with remote access (only for tests systems/purpose) # y = use default (backwards compatibility) # If this option is commented out, control interface is not included in the # build. @@ -252,11 +219,6 @@ # 35-50 kB in code size. #CONFIG_NO_WPA=y -# Remove WPA2 support. This allows WPA to be used, but removes WPA2 code to -# save about 1 kB in code size when building only WPA-Personal (no EAP support) -# or 6 kB if building for WPA-Enterprise. -#CONFIG_NO_WPA2=y - # Remove IEEE 802.11i/WPA-Personal ASCII passphrase support # This option can be used to reduce code size by removing support for # converting ASCII passphrases into PSK. If this functionality is removed, the @@ -300,9 +262,11 @@ # Select event loop implementation # eloop = select() loop (default) # eloop_win = Windows events and WaitForMultipleObject() loop -# eloop_none = Empty template #CONFIG_ELOOP=eloop +# Should we use poll instead of select? Select is used by default. +#CONFIG_ELOOP_POLL=y + # Select layer 2 packet implementation # linux = Linux packet socket (default) # pcap = libpcap/libdnet/WinPcap @@ -315,9 +279,7 @@ # PeerKey handshake for Station to Station Link (IEEE 802.11e DLS) CONFIG_PEERKEY=y -# IEEE 802.11w (management frame protection) -# This version is an experimental implementation based on IEEE 802.11w/D1.0 -# draft and is subject to change since the standard has not yet been finalized. +# IEEE 802.11w (management frame protection), also known as PMF # Driver support is also needed for IEEE 802.11w. #CONFIG_IEEE80211W=y @@ -335,6 +297,13 @@ # sent prior to negotiating which version will be used) #CONFIG_TLSV11=y +# TLS-based EAP methods require at least TLS v1.0. Newer version of TLS (v1.2) +# can be enabled to enable use of stronger crypto algorithms. It should be +# noted that some existing TLS v1.0 -based implementation may not be compatible +# with TLS v1.2 message (ClientHello is sent prior to negotiating which version +# will be used) +#CONFIG_TLSV12=y + # 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 @@ -400,6 +369,16 @@ # Set syslog facility for debug messages #CONFIG_DEBUG_SYSLOG_FACILITY=LOG_DAEMON +# 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 +# same file, e.g., using trace-cmd. +#CONFIG_DEBUG_LINUX_TRACING=y + +# Add support for writing debug log to Android logcat instead of standard +# output +#CONFIG_ANDROID_LOG=y + # Enable privilege separation (see README 'Privilege separation' for details) #CONFIG_PRIVSEP=y @@ -459,8 +438,60 @@ # IEEE 802.11n (High Throughput) support (mainly for AP mode) #CONFIG_IEEE80211N=y +# IEEE 802.11ac (Very High Throughput) support (mainly for AP mode) +# (depends on CONFIG_IEEE80211N) +#CONFIG_IEEE80211AC=y + +# Wireless Network Management (IEEE Std 802.11v-2011) +# Note: This is experimental and not complete implementation. +#CONFIG_WNM=y + # Interworking (IEEE 802.11u) # This can be used to enable functionality to improve interworking with # external networks (GAS/ANQP to learn more about the networks and network # selection based on available credentials). #CONFIG_INTERWORKING=y + +# Hotspot 2.0 +#CONFIG_HS20=y + +# Disable roaming in wpa_supplicant +#CONFIG_NO_ROAMING=y + +# AP mode operations with wpa_supplicant +# This can be used for controlling AP mode operations with wpa_supplicant. It +# should be noted that this is mainly aimed at simple cases like +# WPA2-Personal while more complex configurations like WPA2-Enterprise with an +# external RADIUS server can be supported with hostapd. +#CONFIG_AP=y + +# P2P (Wi-Fi Direct) +# This can be used to enable P2P support in wpa_supplicant. See README-P2P for +# more information on P2P operations. +#CONFIG_P2P=y + +# Enable TDLS support +#CONFIG_TDLS=y + +# Wi-Fi Direct +# This can be used to enable Wi-Fi Direct extensions for P2P using an external +# program to control the additional information exchanges in the messages. +#CONFIG_WIFI_DISPLAY=y + +# Autoscan +# This can be used to enable automatic scan support in wpa_supplicant. +# See wpa_supplicant.conf for more information on autoscan usage. +# +# Enabling directly a module will enable autoscan support. +# For exponential module: +#CONFIG_AUTOSCAN_EXPONENTIAL=y +# For periodic module: +#CONFIG_AUTOSCAN_PERIODIC=y + +# Password (and passphrase, etc.) backend for external storage +# These optional mechanisms can be used to add support for storing passwords +# and other secrets in external (to wpa_supplicant) location. This allows, for +# example, operating system specific key storage to be used +# +# External password backend for testing purposes (developer use) +#CONFIG_EXT_PASSWORD_TEST=y diff -Nru wpa-1.0/wpa_supplicant/doc/docbook/eapol_test.sgml wpa-2.1/wpa_supplicant/doc/docbook/eapol_test.sgml --- wpa-1.0/wpa_supplicant/doc/docbook/eapol_test.sgml 1970-01-01 00:00:00.000000000 +0000 +++ wpa-2.1/wpa_supplicant/doc/docbook/eapol_test.sgml 2014-02-04 11:23:35.000000000 +0000 @@ -0,0 +1,205 @@ + + + + + eapol_test + 8 + + + eapol_test + + EAP peer and RADIUS client testing + + + + + eapol_test + -nWS + -cconfig file + -aserver IP address + -Aclient IP address + -pUDP port + -sshared secret + -rre-authentications + -ttimeout + -CConnect-Info + -MMAC address + -ofile + -Nattr spec + + + eapol_test scard + + + eapol_test sim + PIN + num triplets + + + + + Overview + + eapol_test is a program that links together the same EAP + peer implementation that wpa_supplicant is using and the RADIUS + authentication client code from hostapd. In addition, it has + minimal glue code to combine these two components in similar + ways to IEEE 802.1X/EAPOL Authenticator state machines. In other + words, it integrates IEEE 802.1X Authenticator (normally, an + access point) and IEEE 802.1X Supplicant (normally, a wireless + client) together to generate a single program that can be used to + test EAP methods without having to setup an access point and a + wireless client. + + The main uses for eapol_test are in interoperability testing + of EAP methods against RADIUS servers and in development testing + for new EAP methods. It can be easily used to automate EAP testing + for interoperability and regression since the program can be run + from shell scripts without require additional test components apart + from a RADIUS server. For example, the automated EAP tests described + in eap_testing.txt are implemented with eapol_test. Similarly, + eapol_test could be used to implement an automated regression + test suite for a RADIUS authentication server. + + + As an example: + +
          +eapol_test -ctest.conf -a127.0.0.1 -p1812 -ssecret -r1 +
          + + tries to complete EAP authentication based on the network + configuration from test.conf against the RADIUS server running + on the local host. A re-authentication is triggered to test fast + re-authentication. The configuration file uses the same format for + network blocks as wpa_supplicant. + +
          + + Command Arguments + + + -c configuration file path + + A configuration to use. The configuration should + use the same format for network blocks as wpa_supplicant. + + + + + -a AS address + + IP address of the authentication server. The + default is '127.0.0.1'. + + + + -A client address + + IP address of the client. The default is to + select an address automatically. + + + + -p AS port + + UDP port of the authentication server. The + default is '1812'. + + + + -s AS secret + + Shared secret with the authentication server. + The default is 'radius'. + + + + -r count + + Number of reauthentications. + + + + -t timeout + + Timeout in seconds. The default is 30. + + + + -C info + + RADIUS Connect-Info. The default is + 'CONNECT 11Mbps 802.11b'. + + + + + -M mac address + + Client MAC address (Calling-Station-Id). The + default is '02:00:00:00:00:01'. + + + + -o file + + Location to write out server certificate. + + + + + -N attr spec + + Send arbitrary attribute specific by + attr_id:syntax:value, or attr_id alone. attr_id should be the numeric + ID of the attribute, and syntax should be one of 's' (string), + 'd' (integer), or 'x' (octet string). The value is the attribute value + to send. When attr_id is given alone, NULL is used as the attribute + value. Multiple attributes can be specified by using the option + several times. + + + + -n + + Indicates that no MPPE keys are expected. + + + + + -W + + Wait for a control interface monitor before starting. + + + + + -S + + Save configuration after authentication. + + + + + + + See Also + + + wpa_supplicant + 8 + + + + + Legal + wpa_supplicant is copyright (c) 2003-2014, + Jouni Malinen j@w1.fi and + contributors. + All Rights Reserved. + + This program is licensed under the BSD license (the one with + advertisement clause removed). + +
          diff -Nru wpa-1.0/wpa_supplicant/doc/docbook/Makefile wpa-2.1/wpa_supplicant/doc/docbook/Makefile --- wpa-1.0/wpa_supplicant/doc/docbook/Makefile 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/doc/docbook/Makefile 2014-02-04 11:23:35.000000000 +0000 @@ -7,6 +7,7 @@ FILES += wpa_priv FILES += wpa_supplicant.conf FILES += wpa_supplicant +FILES += eapol_test man: for i in $(FILES); do docbook2man $$i.sgml; done @@ -20,7 +21,7 @@ clean: - rm -f wpa_background.8 wpa_cli.8 wpa_gui.8 wpa_passphrase.8 wpa_priv.8 wpa_supplicant.8 + rm -f wpa_background.8 wpa_cli.8 wpa_gui.8 wpa_passphrase.8 wpa_priv.8 wpa_supplicant.8 eapol_test.8 rm -f wpa_supplicant.conf.5 rm -f manpage.links manpage.refs rm -f $(FILES:%=%.pdf) diff -Nru wpa-1.0/wpa_supplicant/doc/docbook/wpa_background.sgml wpa-2.1/wpa_supplicant/doc/docbook/wpa_background.sgml --- wpa-1.0/wpa_supplicant/doc/docbook/wpa_background.sgml 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/doc/docbook/wpa_background.sgml 2014-02-04 11:23:35.000000000 +0000 @@ -90,12 +90,12 @@ Legal - wpa_supplicant is copyright (c) 2003-2007, + wpa_supplicant is copyright (c) 2003-2014, Jouni Malinen j@w1.fi and contributors. All Rights Reserved. - This program is dual-licensed under both the GPL version 2 - and BSD license. Either license may be used at your option. + This program is licensed under the BSD license (the one with + advertisement clause removed). diff -Nru wpa-1.0/wpa_supplicant/doc/docbook/wpa_cli.sgml wpa-2.1/wpa_supplicant/doc/docbook/wpa_cli.sgml --- wpa-1.0/wpa_supplicant/doc/docbook/wpa_cli.sgml 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/doc/docbook/wpa_cli.sgml 2014-02-04 11:23:35.000000000 +0000 @@ -15,10 +15,12 @@ wpa_cli -p path to ctrl sockets + -g path to global ctrl_interface socket -i ifname -hvB -a action file -P pid file + -G ping interval command ... @@ -111,6 +113,14 @@ + -g control socket path + + Connect to the global control socket at the + indicated path rather than an interface-specific control + socket. + + + -i ifname Specify the interface that is being @@ -161,6 +171,13 @@ + -G ping interval + + Set the interval (in seconds) at which + wpa_cli pings the supplicant. + + + command Run a command. The available commands are @@ -328,12 +345,12 @@ Legal - wpa_supplicant is copyright (c) 2003-2007, + wpa_supplicant is copyright (c) 2003-2014, Jouni Malinen j@w1.fi and contributors. All Rights Reserved. - This program is dual-licensed under both the GPL version 2 - and BSD license. Either license may be used at your option. + This program is licensed under the BSD license (the one with + advertisement clause removed). diff -Nru wpa-1.0/wpa_supplicant/doc/docbook/wpa_gui.sgml wpa-2.1/wpa_supplicant/doc/docbook/wpa_gui.sgml --- wpa-1.0/wpa_supplicant/doc/docbook/wpa_gui.sgml 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/doc/docbook/wpa_gui.sgml 2014-02-04 11:23:35.000000000 +0000 @@ -74,12 +74,12 @@ Legal - wpa_supplicant is copyright (c) 2003-2007, + wpa_supplicant is copyright (c) 2003-2014, Jouni Malinen j@w1.fi and contributors. All Rights Reserved. - This program is dual-licensed under both the GPL version 2 - and BSD license. Either license may be used at your option. + This program is licensed under the BSD license (the one with + advertisement clause removed). diff -Nru wpa-1.0/wpa_supplicant/doc/docbook/wpa_passphrase.sgml wpa-2.1/wpa_supplicant/doc/docbook/wpa_passphrase.sgml --- wpa-1.0/wpa_supplicant/doc/docbook/wpa_passphrase.sgml 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/doc/docbook/wpa_passphrase.sgml 2014-02-04 11:23:35.000000000 +0000 @@ -62,12 +62,12 @@ Legal - wpa_supplicant is copyright (c) 2003-2007, + wpa_supplicant is copyright (c) 2003-2014, Jouni Malinen j@w1.fi and contributors. All Rights Reserved. - This program is dual-licensed under both the GPL version 2 - and BSD license. Either license may be used at your option. + This program is licensed under the BSD license (the one with + advertisement clause removed). diff -Nru wpa-1.0/wpa_supplicant/doc/docbook/wpa_priv.sgml wpa-2.1/wpa_supplicant/doc/docbook/wpa_priv.sgml --- wpa-1.0/wpa_supplicant/doc/docbook/wpa_priv.sgml 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/doc/docbook/wpa_priv.sgml 2014-02-04 11:23:35.000000000 +0000 @@ -137,12 +137,12 @@ Legal - wpa_supplicant is copyright (c) 2003-2007, + wpa_supplicant is copyright (c) 2003-2014, Jouni Malinen j@w1.fi and contributors. All Rights Reserved. - This program is dual-licensed under both the GPL version 2 - and BSD license. Either license may be used at your option. + This program is licensed under the BSD license (the one with + advertisement clause removed). diff -Nru wpa-1.0/wpa_supplicant/doc/docbook/wpa_supplicant.sgml wpa-2.1/wpa_supplicant/doc/docbook/wpa_supplicant.sgml --- wpa-1.0/wpa_supplicant/doc/docbook/wpa_supplicant.sgml 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/doc/docbook/wpa_supplicant.sgml 2014-02-04 11:23:35.000000000 +0000 @@ -12,7 +12,7 @@ wpa_supplicant - -BddfhKLqqtuvW + -BddfhKLqqsTtuvW -iifname -cconfig file -Ddriver @@ -246,28 +246,6 @@ - hostap - - (default) Host AP driver (Intersil Prism2/2.5/3). - (this can also be used with Linuxant DriverLoader). - - - - - hermes - - Agere Systems Inc. driver (Hermes-I/Hermes-II). - - - - - madwifi - - MADWIFI 802.11 support (Atheros, etc.). - - - - wext Linux wireless extensions (generic). @@ -275,13 +253,6 @@ - broadcom - - Broadcom wl.o driver. - - - - wired wpa_supplicant wired Ethernet driver @@ -373,9 +344,20 @@ + -e entropy file + + File for wpa_supplicant to use to + maintain its internal entropy store in over restarts. + + + + -f output file - Log output to specified file instead of stdout. + Log output to specified file instead of stdout. (This + is only available if wpa_supplicant was + built with the CONFIG_DEBUG_FILE + option.) @@ -411,7 +393,23 @@ -L - Show license (GPL and BSD). + Show license (BSD). + + + + + -o override driver + + Override the driver parameter for new + interfaces. + + + + + -O override ctrl_interface + + Override the ctrl_interface parameter for new + interfaces. @@ -438,10 +436,40 @@ + -s + + Log output to syslog instead of stdout. (This is only + available if wpa_supplicant was built + with the CONFIG_DEBUG_SYSLOG + option.) + + + + + -T + + Log output to Linux tracing in addition to any other + destinations. (This is only available + if wpa_supplicant was built with + the CONFIG_DEBUG_LINUX_TRACING + option.) + + + + + -t + + Include timestamp in debug messages. + + + + -u - Enabled DBus control interface. If enabled, interface - definitions may be omitted. + Enable DBus control interface. If enabled, interface + definitions may be omitted. (This is only available + if wpa_supplicant was built with + the CONFIG_DBUS option.)0 @@ -506,8 +534,8 @@
          wpa_supplicant \ - -c wpa1.conf -i wlan0 -D hostap -N \ - -c wpa2.conf -i ath0 -D madwifi + -c wpa1.conf -i wlan0 -D nl80211 -N \ + -c wpa2.conf -i ath0 -D wext
          @@ -537,86 +565,6 @@ Supported Drivers - Host AP driver for Prism2/2.5/3 (development - snapshot/v0.2.x) - - (http://hostap.epitest.fi/) Driver needs to be set in - Managed mode (iwconfig wlan0 mode managed). - Please note that station firmware version needs to be 1.7.0 or - newer to work in WPA mode. - - - - - Linuxant DriverLoader - - (http://www.linuxant.com/driverloader/) - with Windows NDIS driver for your wlan card supporting WPA. - - - - - Agere Systems Inc. Linux Driver - - (http://www.agere.com/support/drivers/) Please note - that the driver interface file (driver_hermes.c) and hardware - specific include files are not included in the wpa_supplicant - distribution. You will need to copy these from the source - package of the Agere driver. - - - - - madwifi driver for cards based on Atheros chip set (ar521x) - - (http://sourceforge.net/projects/madwifi/) Please - note that you will need to modify the wpa_supplicant .config - file to use the correct path for the madwifi driver root - directory (CFLAGS += -I../madwifi/wpa line in example - defconfig). - - - - - Linux ndiswrapper - - (http://ndiswrapper.sourceforge.net/) with Windows - NDIS driver. - - - - - Broadcom wl.o driver - - This is a generic Linux driver for Broadcom IEEE - 802.11a/g cards. However, it is proprietary driver that is - not publicly available except for couple of exceptions, mainly - Broadcom-based APs/wireless routers that use Linux. The driver - binary can be downloaded, e.g., from Linksys support site - (http://www.linksys.com/support/gpl.asp) for Linksys - WRT54G. The GPL tarball includes cross-compiler and the needed - header file, wlioctl.h, for compiling wpa_supplicant. This - driver support in wpa_supplicant is expected to work also with - other devices based on Broadcom driver (assuming the driver - includes client mode support). - - - - - Intel ipw2100 driver - - (http://sourceforge.net/projects/ipw2100/) - - - - - Intel ipw2200 driver - - (http://sourceforge.net/projects/ipw2200/) - - - - Linux wireless extensions In theory, any driver that supports Linux wireless @@ -788,12 +736,12 @@ Legal - wpa_supplicant is copyright (c) 2003-2007, + wpa_supplicant is copyright (c) 2003-2014, Jouni Malinen j@w1.fi and contributors. All Rights Reserved. - This program is dual-licensed under both the GPL version 2 - and BSD license. Either license may be used at your option. + This program is licensed under the BSD license (the one with + advertisement clause removed). diff -Nru wpa-1.0/wpa_supplicant/driver_i.h wpa-2.1/wpa_supplicant/driver_i.h --- wpa-1.0/wpa_supplicant/driver_i.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/driver_i.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * wpa_supplicant - Internal driver interface wrappers * Copyright (c) 2003-2009, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef DRIVER_I_H @@ -123,11 +117,16 @@ static inline int wpa_drv_set_key(struct wpa_supplicant *wpa_s, enum wpa_alg alg, const u8 *addr, int key_idx, int set_tx, - const u8 *seq, size_t seq_len, - const u8 *key, size_t key_len) + const u8 *seq, size_t seq_len, + const u8 *key, size_t key_len) { + if (alg != WPA_ALG_NONE) { + if (key_idx >= 0 && key_idx <= 6) + wpa_s->keys_cleared &= ~BIT(key_idx); + else + wpa_s->keys_cleared = 0; + } if (wpa_s->driver->set_key) { - wpa_s->keys_cleared = 0; return wpa_s->driver->set_key(wpa_s->ifname, wpa_s->drv_priv, alg, addr, key_idx, set_tx, seq, seq_len, key, key_len); @@ -135,22 +134,23 @@ return -1; } -static inline int wpa_drv_deauthenticate(struct wpa_supplicant *wpa_s, - const u8 *addr, int reason_code) +static inline int wpa_drv_sta_deauth(struct wpa_supplicant *wpa_s, + const u8 *addr, int reason_code) { - if (wpa_s->driver->deauthenticate) { - return wpa_s->driver->deauthenticate(wpa_s->drv_priv, addr, - reason_code); + if (wpa_s->driver->sta_deauth) { + return wpa_s->driver->sta_deauth(wpa_s->drv_priv, + wpa_s->own_addr, addr, + reason_code); } return -1; } -static inline int wpa_drv_disassociate(struct wpa_supplicant *wpa_s, - const u8 *addr, int reason_code) +static inline int wpa_drv_deauthenticate(struct wpa_supplicant *wpa_s, + const u8 *addr, int reason_code) { - if (wpa_s->driver->disassociate) { - return wpa_s->driver->disassociate(wpa_s->drv_priv, addr, - reason_code); + if (wpa_s->driver->deauthenticate) { + return wpa_s->driver->deauthenticate(wpa_s->drv_priv, addr, + reason_code); } return -1; } @@ -262,11 +262,11 @@ } static inline int wpa_drv_send_mlme(struct wpa_supplicant *wpa_s, - const u8 *data, size_t data_len) + const u8 *data, size_t data_len, int noack) { if (wpa_s->driver->send_mlme) return wpa_s->driver->send_mlme(wpa_s->drv_priv, - data, data_len); + data, data_len, noack); return -1; } @@ -385,7 +385,7 @@ if (wpa_s->driver->if_add) return wpa_s->driver->if_add(wpa_s->drv_priv, type, ifname, addr, bss_ctx, NULL, force_ifname, - if_addr, bridge); + if_addr, bridge, 0); return -1; } @@ -469,6 +469,15 @@ return -1; } +static inline int wpa_drv_pktcnt_poll(struct wpa_supplicant *wpa_s, + struct hostap_sta_driver_data *sta) +{ + if (wpa_s->driver->read_sta_data) + return wpa_s->driver->read_sta_data(wpa_s->drv_priv, sta, + wpa_s->bssid); + return -1; +} + static inline int wpa_drv_set_ap_wps_ie(struct wpa_supplicant *wpa_s, const struct wpabuf *beacon, const struct wpabuf *proberesp, @@ -512,162 +521,87 @@ return wpa_s->driver->ampdu(wpa_s->drv_priv, ampdu); } -static inline int wpa_drv_p2p_find(struct wpa_supplicant *wpa_s, - unsigned int timeout, int type) -{ - if (!wpa_s->driver->p2p_find) - return -1; - return wpa_s->driver->p2p_find(wpa_s->drv_priv, timeout, type); -} - -static inline int wpa_drv_p2p_stop_find(struct wpa_supplicant *wpa_s) -{ - if (!wpa_s->driver->p2p_stop_find) - return -1; - return wpa_s->driver->p2p_stop_find(wpa_s->drv_priv); -} - -static inline int wpa_drv_p2p_listen(struct wpa_supplicant *wpa_s, - unsigned int timeout) -{ - if (!wpa_s->driver->p2p_listen) - return -1; - return wpa_s->driver->p2p_listen(wpa_s->drv_priv, timeout); -} - -static inline int wpa_drv_p2p_connect(struct wpa_supplicant *wpa_s, - const u8 *peer_addr, int wps_method, - int go_intent, - const u8 *own_interface_addr, - unsigned int force_freq, - int persistent_group) -{ - if (!wpa_s->driver->p2p_connect) - return -1; - return wpa_s->driver->p2p_connect(wpa_s->drv_priv, peer_addr, - wps_method, go_intent, - own_interface_addr, force_freq, - persistent_group); -} - -static inline int wpa_drv_wps_success_cb(struct wpa_supplicant *wpa_s, - const u8 *peer_addr) -{ - if (!wpa_s->driver->wps_success_cb) - return -1; - return wpa_s->driver->wps_success_cb(wpa_s->drv_priv, peer_addr); -} - -static inline int -wpa_drv_p2p_group_formation_failed(struct wpa_supplicant *wpa_s) +static inline int wpa_drv_send_tdls_mgmt(struct wpa_supplicant *wpa_s, + const u8 *dst, u8 action_code, + u8 dialog_token, u16 status_code, + const u8 *buf, size_t len) { - if (!wpa_s->driver->p2p_group_formation_failed) - return -1; - return wpa_s->driver->p2p_group_formation_failed(wpa_s->drv_priv); + if (wpa_s->driver->send_tdls_mgmt) { + return wpa_s->driver->send_tdls_mgmt(wpa_s->drv_priv, dst, + action_code, dialog_token, + status_code, buf, len); + } + return -1; } -static inline int wpa_drv_p2p_set_params(struct wpa_supplicant *wpa_s, - const struct p2p_params *params) +static inline int wpa_drv_tdls_oper(struct wpa_supplicant *wpa_s, + enum tdls_oper oper, const u8 *peer) { - if (!wpa_s->driver->p2p_set_params) + if (!wpa_s->driver->tdls_oper) return -1; - return wpa_s->driver->p2p_set_params(wpa_s->drv_priv, params); + return wpa_s->driver->tdls_oper(wpa_s->drv_priv, oper, peer); } -static inline int wpa_drv_p2p_prov_disc_req(struct wpa_supplicant *wpa_s, - const u8 *peer_addr, - u16 config_methods, int join) +#ifdef ANDROID +static inline int wpa_drv_driver_cmd(struct wpa_supplicant *wpa_s, + char *cmd, char *buf, size_t buf_len) { - if (!wpa_s->driver->p2p_prov_disc_req) + if (!wpa_s->driver->driver_cmd) return -1; - return wpa_s->driver->p2p_prov_disc_req(wpa_s->drv_priv, peer_addr, - config_methods, join); + return wpa_s->driver->driver_cmd(wpa_s->drv_priv, cmd, buf, buf_len); } +#endif /* ANDROID */ -static inline u64 wpa_drv_p2p_sd_request(struct wpa_supplicant *wpa_s, - const u8 *dst, - const struct wpabuf *tlvs) -{ - if (!wpa_s->driver->p2p_sd_request) - return 0; - return wpa_s->driver->p2p_sd_request(wpa_s->drv_priv, dst, tlvs); -} - -static inline int wpa_drv_p2p_sd_cancel_request(struct wpa_supplicant *wpa_s, - u64 req) +static inline void wpa_drv_set_rekey_info(struct wpa_supplicant *wpa_s, + const u8 *kek, const u8 *kck, + const u8 *replay_ctr) { - if (!wpa_s->driver->p2p_sd_cancel_request) - return -1; - return wpa_s->driver->p2p_sd_cancel_request(wpa_s->drv_priv, req); + if (!wpa_s->driver->set_rekey_info) + return; + wpa_s->driver->set_rekey_info(wpa_s->drv_priv, kek, kck, replay_ctr); } -static inline int wpa_drv_p2p_sd_response(struct wpa_supplicant *wpa_s, - int freq, const u8 *dst, - u8 dialog_token, - const struct wpabuf *resp_tlvs) +static inline int wpa_drv_radio_disable(struct wpa_supplicant *wpa_s, + int disabled) { - if (!wpa_s->driver->p2p_sd_response) + if (!wpa_s->driver->radio_disable) return -1; - return wpa_s->driver->p2p_sd_response(wpa_s->drv_priv, freq, dst, - dialog_token, resp_tlvs); + return wpa_s->driver->radio_disable(wpa_s->drv_priv, disabled); } -static inline int wpa_drv_p2p_service_update(struct wpa_supplicant *wpa_s) +static inline int wpa_drv_switch_channel(struct wpa_supplicant *wpa_s, + struct csa_settings *settings) { - if (!wpa_s->driver->p2p_service_update) + if (!wpa_s->driver->switch_channel) return -1; - return wpa_s->driver->p2p_service_update(wpa_s->drv_priv); + return wpa_s->driver->switch_channel(wpa_s->drv_priv, settings); } -static inline int wpa_drv_p2p_reject(struct wpa_supplicant *wpa_s, - const u8 *addr) +static inline int wpa_drv_wnm_oper(struct wpa_supplicant *wpa_s, + enum wnm_oper oper, const u8 *peer, + u8 *buf, u16 *buf_len) { - if (!wpa_s->driver->p2p_reject) + if (!wpa_s->driver->wnm_oper) return -1; - return wpa_s->driver->p2p_reject(wpa_s->drv_priv, addr); + return wpa_s->driver->wnm_oper(wpa_s->drv_priv, oper, peer, buf, + buf_len); } -static inline int wpa_drv_p2p_invite(struct wpa_supplicant *wpa_s, - const u8 *peer, int role, const u8 *bssid, - const u8 *ssid, size_t ssid_len, - const u8 *go_dev_addr, - int persistent_group) +static inline int wpa_drv_status(struct wpa_supplicant *wpa_s, + char *buf, size_t buflen) { - if (!wpa_s->driver->p2p_invite) + if (!wpa_s->driver->status) return -1; - return wpa_s->driver->p2p_invite(wpa_s->drv_priv, peer, role, bssid, - ssid, ssid_len, go_dev_addr, - persistent_group); -} - -static inline int wpa_drv_send_tdls_mgmt(struct wpa_supplicant *wpa_s, - const u8 *dst, u8 action_code, - u8 dialog_token, u16 status_code, - const u8 *buf, size_t len) -{ - if (wpa_s->driver->send_tdls_mgmt) { - return wpa_s->driver->send_tdls_mgmt(wpa_s->drv_priv, dst, - action_code, dialog_token, - status_code, buf, len); - } - return -1; + return wpa_s->driver->status(wpa_s->drv_priv, buf, buflen); } -static inline int wpa_drv_tdls_oper(struct wpa_supplicant *wpa_s, - enum tdls_oper oper, const u8 *peer) +static inline int wpa_drv_set_qos_map(struct wpa_supplicant *wpa_s, + const u8 *qos_map_set, u8 qos_map_set_len) { - if (!wpa_s->driver->tdls_oper) + if (!wpa_s->driver->set_qos_map) return -1; - return wpa_s->driver->tdls_oper(wpa_s->drv_priv, oper, peer); -} - -static inline void wpa_drv_set_rekey_info(struct wpa_supplicant *wpa_s, - const u8 *kek, const u8 *kck, - const u8 *replay_ctr) -{ - if (!wpa_s->driver->set_rekey_info) - return; - wpa_s->driver->set_rekey_info(wpa_s->drv_priv, kek, kck, replay_ctr); + return wpa_s->driver->set_qos_map(wpa_s->drv_priv, qos_map_set, + qos_map_set_len); } #endif /* DRIVER_I_H */ diff -Nru wpa-1.0/wpa_supplicant/eapol_test.c wpa-2.1/wpa_supplicant/eapol_test.c --- wpa-1.0/wpa_supplicant/eapol_test.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/eapol_test.c 2014-02-04 11:23:35.000000000 +0000 @@ -1,15 +1,9 @@ /* * WPA Supplicant - test code - * Copyright (c) 2003-2011, Jouni Malinen + * Copyright (c) 2003-2013, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. * * IEEE 802.1X Supplicant test code (to be used in place of wpa_supplicant.c. * Not used in production version. @@ -19,6 +13,7 @@ #include #include "common.h" +#include "utils/ext_password.h" #include "config.h" #include "eapol_supp/eapol_supp_sm.h" #include "eap_peer/eap.h" @@ -26,18 +21,15 @@ #include "eloop.h" #include "utils/base64.h" #include "rsn_supp/wpa.h" -#include "eap_peer/eap_i.h" #include "wpa_supplicant_i.h" #include "radius/radius.h" #include "radius/radius_client.h" #include "common/wpa_ctrl.h" #include "ctrl_iface.h" #include "pcsc_funcs.h" +#include "wpas_glue.h" -extern int wpa_debug_level; -extern int wpa_debug_show_keys; - struct wpa_driver_ops *wpa_drivers[] = { NULL }; @@ -61,9 +53,8 @@ struct radius_client_data *radius; struct hostapd_radius_servers *radius_conf; - u8 *last_eap_radius; /* last received EAP Response from Authentication - * Server */ - size_t last_eap_radius_len; + /* last received EAP Response from Authentication Server */ + struct wpabuf *last_eap_radius; u8 authenticator_pmk[PMK_LEN]; size_t authenticator_pmk_len; @@ -104,7 +95,7 @@ size_t len; char *pos; u32 val; - char buf[128]; + char buf[RADIUS_MAX_ATTR_LEN + 1]; switch (attr->syntax) { case 's': @@ -120,7 +111,7 @@ if (pos[0] == '0' && pos[1] == 'x') pos += 2; len = os_strlen(pos); - if ((len & 1) || (len / 2) > sizeof(buf)) { + if ((len & 1) || (len / 2) > RADIUS_MAX_ATTR_LEN) { printf("Invalid extra attribute hexstring\n"); return -1; } @@ -177,7 +168,7 @@ const u8 *eap, size_t len) { struct radius_msg *msg; - char buf[128]; + char buf[RADIUS_MAX_ATTR_LEN + 1]; const struct eap_hdr *hdr; const u8 *pos; @@ -374,10 +365,11 @@ } -static void eapol_sm_cb(struct eapol_sm *eapol, int success, void *ctx) +static void eapol_sm_cb(struct eapol_sm *eapol, enum eapol_supp_result result, + void *ctx) { struct eapol_test_data *e = ctx; - printf("eapol_sm_cb: success=%d\n", success); + printf("eapol_sm_cb: result=%d\n", result); e->eapol_test_num_reauths--; if (e->eapol_test_num_reauths < 0) eloop_terminate(); @@ -402,6 +394,54 @@ } +#if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG) +static void eapol_test_eap_param_needed(void *ctx, enum wpa_ctrl_req_type field, + const char *default_txt) +{ + struct eapol_test_data *e = ctx; + struct wpa_supplicant *wpa_s = e->wpa_s; + struct wpa_ssid *ssid = wpa_s->current_ssid; + const char *field_name, *txt = NULL; + char *buf; + size_t buflen; + int len; + + if (ssid == NULL) + return; + + field_name = wpa_supplicant_ctrl_req_to_string(field, default_txt, + &txt); + if (field_name == NULL) { + wpa_printf(MSG_WARNING, "Unhandled EAP param %d needed", + field); + return; + } + + buflen = 100 + os_strlen(txt) + ssid->ssid_len; + buf = os_malloc(buflen); + if (buf == NULL) + return; + len = os_snprintf(buf, buflen, + WPA_CTRL_REQ "%s-%d:%s needed for SSID ", + field_name, ssid->id, txt); + if (len < 0 || (size_t) len >= buflen) { + os_free(buf); + return; + } + if (ssid->ssid && buflen > len + ssid->ssid_len) { + os_memcpy(buf + len, ssid->ssid, ssid->ssid_len); + len += ssid->ssid_len; + buf[len] = '\0'; + } + buf[buflen - 1] = '\0'; + wpa_msg(wpa_s, MSG_INFO, "%s", buf); + os_free(buf); +} +#else /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */ +#define eapol_test_eap_param_needed NULL +#endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */ + + static void eapol_test_cert_cb(void *ctx, int depth, const char *subject, const char *cert_hash, const struct wpabuf *cert) @@ -435,6 +475,37 @@ } +static void eapol_test_set_anon_id(void *ctx, const u8 *id, size_t len) +{ + struct eapol_test_data *e = ctx; + struct wpa_supplicant *wpa_s = e->wpa_s; + char *str; + int res; + + wpa_hexdump_ascii(MSG_DEBUG, "EAP method updated anonymous_identity", + id, len); + + if (wpa_s->current_ssid == NULL) + return; + + if (id == NULL) { + if (wpa_config_set(wpa_s->current_ssid, "anonymous_identity", + "NULL", 0) < 0) + return; + } else { + str = os_malloc(len * 2 + 1); + if (str == NULL) + return; + wpa_snprintf_hex(str, len * 2 + 1, id, len); + res = wpa_config_set(wpa_s->current_ssid, "anonymous_identity", + str, 0); + os_free(str); + if (res < 0) + return; + } +} + + static int test_eapol(struct eapol_test_data *e, struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) { @@ -460,8 +531,10 @@ ctx->opensc_engine_path = wpa_s->conf->opensc_engine_path; ctx->pkcs11_engine_path = wpa_s->conf->pkcs11_engine_path; ctx->pkcs11_module_path = wpa_s->conf->pkcs11_module_path; + ctx->eap_param_needed = eapol_test_eap_param_needed; ctx->cert_cb = eapol_test_cert_cb; ctx->cert_in_cb = 1; + ctx->set_anon_id = eapol_test_set_anon_id; wpa_s->eapol = eapol_sm_init(ctx); if (wpa_s->eapol == NULL) { @@ -476,6 +549,7 @@ eapol_conf.required_keys = 0; eapol_conf.fast_reauth = wpa_s->conf->fast_reauth; eapol_conf.workaround = ssid->eap_workaround; + eapol_conf.external_sim = wpa_s->conf->external_sim; eapol_sm_notify_config(wpa_s->eapol, &ssid->eap, &eapol_conf); eapol_sm_register_scard_ctx(wpa_s->eapol, wpa_s->scard); @@ -494,7 +568,7 @@ struct extra_radius_attr *p, *prev; radius_client_deinit(e->radius); - os_free(e->last_eap_radius); + wpabuf_free(e->last_eap_radius); radius_msg_free(e->last_recv_radius); e->last_recv_radius = NULL; os_free(e->eap_identity); @@ -512,6 +586,10 @@ wpa_supplicant_ctrl_iface_deinit(wpa_s->ctrl_iface); wpa_s->ctrl_iface = NULL; } + + ext_password_deinit(wpa_s->ext_pw); + wpa_s->ext_pw = NULL; + wpa_config_free(wpa_s->conf); p = e->extra_attrs; @@ -580,9 +658,8 @@ static void ieee802_1x_decapsulate_radius(struct eapol_test_data *e) { - u8 *eap; - size_t len; - struct eap_hdr *hdr; + struct wpabuf *eap; + const struct eap_hdr *hdr; int eap_type = -1; char buf[64]; struct radius_msg *msg; @@ -592,30 +669,29 @@ msg = e->last_recv_radius; - eap = radius_msg_get_eap(msg, &len); + eap = radius_msg_get_eap(msg); if (eap == NULL) { /* draft-aboba-radius-rfc2869bis-20.txt, Chap. 2.6.3: * RADIUS server SHOULD NOT send Access-Reject/no EAP-Message * attribute */ wpa_printf(MSG_DEBUG, "could not extract " "EAP-Message from RADIUS message"); - os_free(e->last_eap_radius); + wpabuf_free(e->last_eap_radius); e->last_eap_radius = NULL; - e->last_eap_radius_len = 0; return; } - if (len < sizeof(*hdr)) { + if (wpabuf_len(eap) < sizeof(*hdr)) { wpa_printf(MSG_DEBUG, "too short EAP packet " "received from authentication server"); - os_free(eap); + wpabuf_free(eap); return; } - if (len > sizeof(*hdr)) - eap_type = eap[sizeof(*hdr)]; + if (wpabuf_len(eap) > sizeof(*hdr)) + eap_type = (wpabuf_head_u8(eap))[sizeof(*hdr)]; - hdr = (struct eap_hdr *) eap; + hdr = wpabuf_head(eap); switch (hdr->code) { case EAP_CODE_REQUEST: os_snprintf(buf, sizeof(buf), "EAP-Request-%s (%d)", @@ -638,7 +714,7 @@ break; default: os_strlcpy(buf, "unknown EAP code", sizeof(buf)); - wpa_hexdump(MSG_DEBUG, "Decapsulated EAP packet", eap, len); + wpa_hexdump_buf(MSG_DEBUG, "Decapsulated EAP packet", eap); break; } wpa_printf(MSG_DEBUG, "decapsulated EAP packet (code=%d " @@ -647,20 +723,21 @@ /* sta->eapol_sm->be_auth.idFromServer = hdr->identifier; */ - os_free(e->last_eap_radius); + wpabuf_free(e->last_eap_radius); e->last_eap_radius = eap; - e->last_eap_radius_len = len; { struct ieee802_1x_hdr *dot1x; - dot1x = os_malloc(sizeof(*dot1x) + len); + dot1x = os_malloc(sizeof(*dot1x) + wpabuf_len(eap)); assert(dot1x != NULL); dot1x->version = EAPOL_VERSION; dot1x->type = IEEE802_1X_TYPE_EAP_PACKET; - dot1x->length = htons(len); - os_memcpy((u8 *) (dot1x + 1), eap, len); + dot1x->length = htons(wpabuf_len(eap)); + os_memcpy((u8 *) (dot1x + 1), wpabuf_head(eap), + wpabuf_len(eap)); eapol_sm_rx_eapol(e->wpa_s->eapol, e->wpa_s->bssid, - (u8 *) dot1x, sizeof(*dot1x) + len); + (u8 *) dot1x, + sizeof(*dot1x) + wpabuf_len(eap)); os_free(dot1x); } } @@ -864,7 +941,7 @@ unsigned char aka_ik[IK_LEN]; unsigned char aka_ck[CK_LEN]; - scard = scard_init(SCARD_TRY_BOTH); + scard = scard_init(NULL); if (scard == NULL) return -1; if (scard_set_pin(scard, "1234")) { @@ -879,6 +956,9 @@ wpa_hexdump_ascii(MSG_DEBUG, "SCARD: IMSI", (u8 *) imsi, len); /* NOTE: Permanent Username: 1 | IMSI */ + wpa_printf(MSG_DEBUG, "SCARD: MNC length %d", + scard_get_mnc_len(scard)); + os_memset(_rand, 0, sizeof(_rand)); if (scard_gsm_auth(scard, _rand, sres, kc)) goto failed; @@ -961,7 +1041,7 @@ wpa_debug_level = 99; } - scard = scard_init(SCARD_GSM_SIM_ONLY); + scard = scard_init(NULL); if (scard == NULL) { printf("Failed to open smartcard connection\n"); return -1; @@ -1063,6 +1143,7 @@ int main(int argc, char *argv[]) { + struct wpa_global global; struct wpa_supplicant wpa_s; int c, ret = 1, wait_for_monitor = 0, save_config = 0; char *as_addr = "127.0.0.1"; @@ -1141,7 +1222,7 @@ wait_for_monitor++; break; case 'N': - p1 = os_zalloc(sizeof(p1)); + p1 = os_zalloc(sizeof(*p1)); if (p1 == NULL) break; if (!p) @@ -1199,9 +1280,13 @@ return -1; } + os_memset(&global, 0, sizeof(global)); os_memset(&wpa_s, 0, sizeof(wpa_s)); + wpa_s.global = &global; eapol_test.wpa_s = &wpa_s; - wpa_s.conf = wpa_config_read(conf); + dl_list_init(&wpa_s.bss); + dl_list_init(&wpa_s.bss_id); + wpa_s.conf = wpa_config_read(conf, NULL); if (wpa_s.conf == NULL) { printf("Failed to parse configuration file '%s'.\n", conf); return -1; @@ -1231,6 +1316,9 @@ if (test_eapol(&eapol_test, &wpa_s, wpa_s.conf->ssid)) return -1; + if (wpas_init_ext_pw(&wpa_s) < 0) + return -1; + if (wait_for_monitor) wpa_supplicant_ctrl_iface_wait(wpa_s.ctrl_iface); diff -Nru wpa-1.0/wpa_supplicant/eap_register.c wpa-2.1/wpa_supplicant/eap_register.c --- wpa-1.0/wpa_supplicant/eap_register.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/eap_register.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * EAP method registration * Copyright (c) 2004-2009, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -17,6 +11,7 @@ #include "common.h" #include "eap_peer/eap_methods.h" #include "eap_server/eap_methods.h" +#include "wpa_supplicant_i.h" /** @@ -40,6 +35,11 @@ ret = eap_peer_tls_register(); #endif /* EAP_TLS */ +#ifdef EAP_UNAUTH_TLS + if (ret == 0) + ret = eap_peer_unauth_tls_register(); +#endif /* EAP_UNAUTH_TLS */ + #ifdef EAP_MSCHAPv2 if (ret == 0) ret = eap_peer_mschapv2_register(); @@ -135,6 +135,11 @@ ret = eap_peer_pwd_register(); #endif /* EAP_PWD */ +#ifdef EAP_EKE + if (ret == 0) + ret = eap_peer_eke_register(); +#endif /* EAP_EKE */ + #ifdef EAP_SERVER_IDENTITY if (ret == 0) ret = eap_server_identity_register(); @@ -150,6 +155,11 @@ ret = eap_server_tls_register(); #endif /* EAP_SERVER_TLS */ +#ifdef EAP_SERVER_UNAUTH_TLS + if (ret == 0) + ret = eap_server_unauth_tls_register(); +#endif /* EAP_SERVER_UNAUTH_TLS */ + #ifdef EAP_SERVER_MSCHAPV2 if (ret == 0) ret = eap_server_mschapv2_register(); diff -Nru wpa-1.0/wpa_supplicant/events.c wpa-2.1/wpa_supplicant/events.c --- wpa-1.0/wpa_supplicant/events.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/events.c 2014-02-04 11:23:35.000000000 +0000 @@ -1,15 +1,9 @@ /* * WPA Supplicant - Driver event processing - * Copyright (c) 2003-2011, Jouni Malinen + * Copyright (c) 2003-2014, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -29,6 +23,7 @@ #include "eap_peer/eap.h" #include "ap/hostapd.h" #include "p2p/p2p.h" +#include "wnm_sta.h" #include "notify.h" #include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" @@ -41,15 +36,58 @@ #include "gas_query.h" #include "p2p_supplicant.h" #include "bgscan.h" +#include "autoscan.h" #include "ap.h" #include "bss.h" #include "scan.h" #include "offchannel.h" +#include "interworking.h" + + +#ifndef CONFIG_NO_SCAN_PROCESSING +static int wpas_select_network_from_last_scan(struct wpa_supplicant *wpa_s, + int new_scan, int own_request); +#endif /* CONFIG_NO_SCAN_PROCESSING */ + + +static int wpas_temp_disabled(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid) +{ + struct os_reltime now; + + if (ssid == NULL || ssid->disabled_until.sec == 0) + return 0; + + os_get_reltime(&now); + if (ssid->disabled_until.sec > now.sec) + return ssid->disabled_until.sec - now.sec; + + wpas_clear_temp_disabled(wpa_s, ssid, 0); + + return 0; +} + + +static struct wpa_bss * wpa_supplicant_get_new_bss( + struct wpa_supplicant *wpa_s, const u8 *bssid) +{ + struct wpa_bss *bss = NULL; + struct wpa_ssid *ssid = wpa_s->current_ssid; + + if (ssid->ssid_len > 0) + bss = wpa_bss_get(wpa_s, bssid, ssid->ssid, ssid->ssid_len); + if (!bss) + bss = wpa_bss_get_bssid(wpa_s, bssid); + + return bss; +} static int wpa_supplicant_select_config(struct wpa_supplicant *wpa_s) { struct wpa_ssid *ssid, *old_ssid; + struct wpa_bss *bss; + int res; if (wpa_s->conf->ap_scan == 1 && wpa_s->current_ssid) return 0; @@ -63,22 +101,32 @@ return -1; } - if (ssid->disabled) { + if (wpas_network_disabled(wpa_s, ssid)) { wpa_dbg(wpa_s, MSG_DEBUG, "Selected network is disabled"); return -1; } + if (disallowed_bssid(wpa_s, wpa_s->bssid) || + disallowed_ssid(wpa_s, ssid->ssid, ssid->ssid_len)) { + wpa_dbg(wpa_s, MSG_DEBUG, "Selected BSS is disallowed"); + return -1; + } + + res = wpas_temp_disabled(wpa_s, ssid); + if (res > 0) { + wpa_dbg(wpa_s, MSG_DEBUG, "Selected network is temporarily " + "disabled for %d second(s)", res); + return -1; + } + wpa_dbg(wpa_s, MSG_DEBUG, "Network configuration found for the " "current AP"); - if (ssid->key_mgmt & (WPA_KEY_MGMT_PSK | WPA_KEY_MGMT_IEEE8021X | - WPA_KEY_MGMT_WPA_NONE | - WPA_KEY_MGMT_FT_PSK | WPA_KEY_MGMT_FT_IEEE8021X | - WPA_KEY_MGMT_PSK_SHA256 | - WPA_KEY_MGMT_IEEE8021X_SHA256)) { + if (wpa_key_mgmt_wpa_any(ssid->key_mgmt)) { u8 wpa_ie[80]; size_t wpa_ie_len = sizeof(wpa_ie); - wpa_supplicant_set_suites(wpa_s, NULL, ssid, - wpa_ie, &wpa_ie_len); + if (wpa_supplicant_set_suites(wpa_s, NULL, ssid, + wpa_ie, &wpa_ie_len) < 0) + wpa_dbg(wpa_s, MSG_DEBUG, "Could not set WPA suites"); } else { wpa_supplicant_set_non_wpa_policy(wpa_s, ssid); } @@ -87,6 +135,18 @@ eapol_sm_invalidate_cached_session(wpa_s->eapol); old_ssid = wpa_s->current_ssid; wpa_s->current_ssid = ssid; + + bss = wpa_supplicant_get_new_bss(wpa_s, wpa_s->bssid); + if (!bss) { + wpa_supplicant_update_scan_results(wpa_s); + + /* Get the BSS from the new scan results */ + bss = wpa_supplicant_get_new_bss(wpa_s, wpa_s->bssid); + } + + if (bss) + wpa_s->current_bss = bss; + wpa_supplicant_rsn_supp_set_config(wpa_s, wpa_s->current_ssid); wpa_supplicant_initiate_eapol(wpa_s); if (old_ssid != wpa_s->current_ssid) @@ -113,6 +173,8 @@ { int bssid_changed; + wnm_bss_keep_alive_deinit(wpa_s); + #ifdef CONFIG_IBSS_RSN ibss_rsn_deinit(wpa_s->ibss_rsn); wpa_s->ibss_rsn = NULL; @@ -129,6 +191,9 @@ bssid_changed = !is_zero_ether_addr(wpa_s->bssid); os_memset(wpa_s->bssid, 0, ETH_ALEN); os_memset(wpa_s->pending_bssid, 0, ETH_ALEN); +#ifdef CONFIG_SME + wpa_s->sme.prev_bssid_set = 0; +#endif /* CONFIG_SME */ #ifdef CONFIG_P2P os_memset(wpa_s->go_dev_addr, 0, ETH_ALEN); #endif /* CONFIG_P2P */ @@ -149,6 +214,9 @@ if (wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt)) eapol_sm_notify_eap_success(wpa_s->eapol, FALSE); wpa_s->ap_ies_from_associnfo = 0; + wpa_s->current_ssid = NULL; + eapol_sm_notify_config(wpa_s->eapol, NULL, NULL); + wpa_s->key_mgmt = 0; } @@ -233,9 +301,10 @@ { #ifdef IEEE8021X_EAPOL #ifdef PCSC_FUNCS - int aka = 0, sim = 0, type; + int aka = 0, sim = 0; - if (ssid->eap.pcsc == NULL || wpa_s->scard != NULL) + if (ssid->eap.pcsc == NULL || wpa_s->scard != NULL || + wpa_s->conf->external_sim) return 0; if (ssid->eap.eap_methods == NULL) { @@ -248,7 +317,8 @@ if (eap->vendor == EAP_VENDOR_IETF) { if (eap->method == EAP_TYPE_SIM) sim = 1; - else if (eap->method == EAP_TYPE_AKA) + else if (eap->method == EAP_TYPE_AKA || + eap->method == EAP_TYPE_AKA_PRIME) aka = 1; } eap++; @@ -257,7 +327,9 @@ if (eap_peer_get_eap_method(EAP_VENDOR_IETF, EAP_TYPE_SIM) == NULL) sim = 0; - if (eap_peer_get_eap_method(EAP_VENDOR_IETF, EAP_TYPE_AKA) == NULL) + if (eap_peer_get_eap_method(EAP_VENDOR_IETF, EAP_TYPE_AKA) == NULL && + eap_peer_get_eap_method(EAP_VENDOR_IETF, EAP_TYPE_AKA_PRIME) == + NULL) aka = 0; if (!sim && !aka) { @@ -269,14 +341,8 @@ wpa_dbg(wpa_s, MSG_DEBUG, "Selected network is configured to use SIM " "(sim=%d aka=%d) - initialize PCSC", sim, aka); - if (sim && aka) - type = SCARD_TRY_BOTH; - else if (aka) - type = SCARD_USIM_ONLY; - else - type = SCARD_GSM_SIM_ONLY; - wpa_s->scard = scard_init(type); + wpa_s->scard = scard_init(NULL); if (wpa_s->scard == NULL) { wpa_msg(wpa_s, MSG_WARNING, "Failed to initialize SIM " "(pcsc-lite)"); @@ -292,10 +358,24 @@ #ifndef CONFIG_NO_SCAN_PROCESSING -static int wpa_supplicant_match_privacy(struct wpa_scan_res *bss, + +static int has_wep_key(struct wpa_ssid *ssid) +{ + int i; + + for (i = 0; i < NUM_WEP_KEYS; i++) { + if (ssid->wep_key_len[i]) + return 1; + } + + return 0; +} + + +static int wpa_supplicant_match_privacy(struct wpa_bss *bss, struct wpa_ssid *ssid) { - int i, privacy = 0; + int privacy = 0; if (ssid->mixed_cell) return 1; @@ -305,12 +385,9 @@ return 1; #endif /* CONFIG_WPS */ - for (i = 0; i < NUM_WEP_KEYS; i++) { - if (ssid->wep_key_len[i]) { - privacy = 1; - break; - } - } + if (has_wep_key(ssid)) + privacy = 1; + #ifdef IEEE8021X_EAPOL if ((ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA) && ssid->eapol_flags & (EAPOL_FLAG_REQUIRE_KEY_UNICAST | @@ -329,7 +406,7 @@ static int wpa_supplicant_ssid_bss_match(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, - struct wpa_scan_res *bss) + struct wpa_bss *bss) { struct wpa_ie_data ie; int proto_match = 0; @@ -347,7 +424,7 @@ ssid->wep_key_len[ssid->wep_tx_keyidx] > 0) || (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA)); - rsn_ie = wpa_scan_get_ie(bss, WLAN_EID_RSN); + rsn_ie = wpa_bss_get_ie(bss, WLAN_EID_RSN); while ((ssid->proto & WPA_PROTO_RSN) && rsn_ie) { proto_match++; @@ -391,7 +468,9 @@ #ifdef CONFIG_IEEE80211W if (!(ie.capabilities & WPA_CAPABILITY_MFPC) && - ssid->ieee80211w == MGMT_FRAME_PROTECTION_REQUIRED) { + (ssid->ieee80211w == MGMT_FRAME_PROTECTION_DEFAULT ? + wpa_s->conf->pmf : ssid->ieee80211w) == + MGMT_FRAME_PROTECTION_REQUIRED) { wpa_dbg(wpa_s, MSG_DEBUG, " skip RSN IE - no mgmt " "frame protection"); break; @@ -402,7 +481,7 @@ return 1; } - wpa_ie = wpa_scan_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE); + wpa_ie = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE); while ((ssid->proto & WPA_PROTO_WPA) && wpa_ie) { proto_match++; @@ -448,6 +527,12 @@ return 1; } + if ((ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA) && !wpa_ie && + !rsn_ie) { + wpa_dbg(wpa_s, MSG_DEBUG, " allow for non-WPA IEEE 802.1X"); + return 1; + } + if ((ssid->proto & (WPA_PROTO_WPA | WPA_PROTO_RSN)) && wpa_key_mgmt_wpa(ssid->key_mgmt) && proto_match == 0) { wpa_dbg(wpa_s, MSG_DEBUG, " skip - no WPA/RSN proto match"); @@ -498,7 +583,25 @@ } -static int rate_match(struct wpa_supplicant *wpa_s, struct wpa_scan_res *bss) +static int vht_supported(const struct hostapd_hw_modes *mode) +{ + if (!(mode->flags & HOSTAPD_MODE_FLAG_VHT_INFO_KNOWN)) { + /* + * The driver did not indicate whether it supports VHT. Assume + * it does to avoid connection issues. + */ + return 1; + } + + /* + * A VHT non-AP STA shall support MCS 0-7 for one spatial stream. + * TODO: Verify if this complies with the standard + */ + return (mode->vht_mcs_set[0] & 0x3) != 3; +} + + +static int rate_match(struct wpa_supplicant *wpa_s, struct wpa_bss *bss) { const struct hostapd_hw_modes *mode = NULL, *modes; const u8 scan_ie[2] = { WLAN_EID_SUPP_RATES, WLAN_EID_EXT_SUPP_RATES }; @@ -536,7 +639,7 @@ return 0; for (i = 0; i < (int) sizeof(scan_ie); i++) { - rate_ie = wpa_scan_get_ie(bss, scan_ie[i]); + rate_ie = wpa_bss_get_ie(bss, scan_ie[i]); if (rate_ie == NULL) continue; @@ -563,6 +666,18 @@ continue; } + /* There's also a VHT selector for 802.11ac */ + if (flagged && ((rate_ie[j] & 0x7f) == + BSS_MEMBERSHIP_SELECTOR_VHT_PHY)) { + if (!vht_supported(mode)) { + wpa_dbg(wpa_s, MSG_DEBUG, + " hardware does not support " + "VHT PHY"); + return 0; + } + continue; + } + if (!flagged) continue; @@ -589,37 +704,57 @@ } +static int bss_is_dmg(struct wpa_bss *bss) +{ + return bss->freq > 45000; +} + + +/* + * Test whether BSS is in an ESS. + * This is done differently in DMG (60 GHz) and non-DMG bands + */ +static int bss_is_ess(struct wpa_bss *bss) +{ + if (bss_is_dmg(bss)) { + return (bss->caps & IEEE80211_CAP_DMG_MASK) == + IEEE80211_CAP_DMG_AP; + } + + return ((bss->caps & (IEEE80211_CAP_ESS | IEEE80211_CAP_IBSS)) == + IEEE80211_CAP_ESS); +} + + static struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s, - int i, struct wpa_scan_res *bss, + int i, struct wpa_bss *bss, struct wpa_ssid *group) { - const u8 *ssid_; - u8 wpa_ie_len, rsn_ie_len, ssid_len; + u8 wpa_ie_len, rsn_ie_len; int wpa; struct wpa_blacklist *e; const u8 *ie; struct wpa_ssid *ssid; - ie = wpa_scan_get_ie(bss, WLAN_EID_SSID); - ssid_ = ie ? ie + 2 : (u8 *) ""; - ssid_len = ie ? ie[1] : 0; - - ie = wpa_scan_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE); + ie = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE); wpa_ie_len = ie ? ie[1] : 0; - ie = wpa_scan_get_ie(bss, WLAN_EID_RSN); + ie = wpa_bss_get_ie(bss, WLAN_EID_RSN); rsn_ie_len = ie ? ie[1] : 0; wpa_dbg(wpa_s, MSG_DEBUG, "%d: " MACSTR " ssid='%s' " - "wpa_ie_len=%u rsn_ie_len=%u caps=0x%x level=%d%s", - i, MAC2STR(bss->bssid), wpa_ssid_txt(ssid_, ssid_len), + "wpa_ie_len=%u rsn_ie_len=%u caps=0x%x level=%d%s%s", + i, MAC2STR(bss->bssid), wpa_ssid_txt(bss->ssid, bss->ssid_len), wpa_ie_len, rsn_ie_len, bss->caps, bss->level, - wpa_scan_get_vendor_ie(bss, WPS_IE_VENDOR_TYPE) ? " wps" : ""); + wpa_bss_get_vendor_ie(bss, WPS_IE_VENDOR_TYPE) ? " wps" : "", + (wpa_bss_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE) || + wpa_bss_get_vendor_ie_beacon(bss, P2P_IE_VENDOR_TYPE)) ? + " p2p" : ""); e = wpa_blacklist_get(wpa_s, bss->bssid); if (e) { int limit = 1; - if (wpa_supplicant_enabled_networks(wpa_s->conf) == 1) { + if (wpa_supplicant_enabled_networks(wpa_s) == 1) { /* * When only a single network is enabled, we can * trigger blacklisting on the first failure. This @@ -637,21 +772,39 @@ } } - if (ssid_len == 0) { + if (bss->ssid_len == 0) { wpa_dbg(wpa_s, MSG_DEBUG, " skip - SSID not known"); return NULL; } + if (disallowed_bssid(wpa_s, bss->bssid)) { + wpa_dbg(wpa_s, MSG_DEBUG, " skip - BSSID disallowed"); + return NULL; + } + + if (disallowed_ssid(wpa_s, bss->ssid, bss->ssid_len)) { + wpa_dbg(wpa_s, MSG_DEBUG, " skip - SSID disallowed"); + return NULL; + } + wpa = wpa_ie_len > 0 || rsn_ie_len > 0; for (ssid = group; ssid; ssid = ssid->pnext) { int check_ssid = wpa ? 1 : (ssid->ssid_len != 0); + int res; - if (ssid->disabled) { + if (wpas_network_disabled(wpa_s, ssid)) { wpa_dbg(wpa_s, MSG_DEBUG, " skip - disabled"); continue; } + res = wpas_temp_disabled(wpa_s, ssid); + if (res > 0) { + wpa_dbg(wpa_s, MSG_DEBUG, " skip - disabled " + "temporarily for %d second(s)", res); + continue; + } + #ifdef CONFIG_WPS if ((ssid->key_mgmt & WPA_KEY_MGMT_WPS) && e && e->count > 0) { wpa_dbg(wpa_s, MSG_DEBUG, " skip - blacklisted " @@ -679,8 +832,8 @@ check_ssid = 0; if (check_ssid && - (ssid_len != ssid->ssid_len || - os_memcmp(ssid_, ssid->ssid, ssid_len) != 0)) { + (bss->ssid_len != ssid->ssid_len || + os_memcmp(bss->ssid, ssid->ssid, bss->ssid_len) != 0)) { wpa_dbg(wpa_s, MSG_DEBUG, " skip - SSID mismatch"); continue; } @@ -703,15 +856,20 @@ continue; } + if (wpa && !wpa_key_mgmt_wpa(ssid->key_mgmt) && + has_wep_key(ssid)) { + wpa_dbg(wpa_s, MSG_DEBUG, " skip - ignore WPA/WPA2 AP for WEP network block"); + continue; + } + if (!wpa_supplicant_match_privacy(bss, ssid)) { wpa_dbg(wpa_s, MSG_DEBUG, " skip - privacy " "mismatch"); continue; } - if (bss->caps & IEEE80211_CAP_IBSS) { - wpa_dbg(wpa_s, MSG_DEBUG, " skip - IBSS (adhoc) " - "network"); + if (!bss_is_ess(bss)) { + wpa_dbg(wpa_s, MSG_DEBUG, " skip - not ESS network"); continue; } @@ -728,6 +886,39 @@ } #ifdef CONFIG_P2P + if (ssid->p2p_group && + !wpa_bss_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE) && + !wpa_bss_get_vendor_ie_beacon(bss, P2P_IE_VENDOR_TYPE)) { + wpa_dbg(wpa_s, MSG_DEBUG, " skip - no P2P IE seen"); + continue; + } + + if (!is_zero_ether_addr(ssid->go_p2p_dev_addr)) { + struct wpabuf *p2p_ie; + u8 dev_addr[ETH_ALEN]; + + ie = wpa_bss_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE); + if (ie == NULL) { + wpa_dbg(wpa_s, MSG_DEBUG, " skip - no P2P element"); + continue; + } + p2p_ie = wpa_bss_get_vendor_ie_multi( + bss, P2P_IE_VENDOR_TYPE); + if (p2p_ie == NULL) { + wpa_dbg(wpa_s, MSG_DEBUG, " skip - could not fetch P2P element"); + continue; + } + + if (p2p_parse_dev_addr_in_p2p_ie(p2p_ie, dev_addr) < 0 + || os_memcmp(dev_addr, ssid->go_p2p_dev_addr, + ETH_ALEN) != 0) { + wpa_dbg(wpa_s, MSG_DEBUG, " skip - no matching GO P2P Device Address in P2P element"); + wpabuf_free(p2p_ie); + continue; + } + wpabuf_free(p2p_ie); + } + /* * TODO: skip the AP if its P2P IE has Group Formation * bit set in the P2P Group Capability Bitmap and we @@ -746,56 +937,51 @@ static struct wpa_bss * wpa_supplicant_select_bss(struct wpa_supplicant *wpa_s, - struct wpa_scan_results *scan_res, struct wpa_ssid *group, struct wpa_ssid **selected_ssid) { - size_t i; + unsigned int i; wpa_dbg(wpa_s, MSG_DEBUG, "Selecting BSS from priority group %d", group->priority); - for (i = 0; i < scan_res->num; i++) { - struct wpa_scan_res *bss = scan_res->res[i]; - const u8 *ie, *ssid; - u8 ssid_len; - + for (i = 0; i < wpa_s->last_scan_res_used; i++) { + struct wpa_bss *bss = wpa_s->last_scan_res[i]; *selected_ssid = wpa_scan_res_match(wpa_s, i, bss, group); if (!*selected_ssid) continue; - - ie = wpa_scan_get_ie(bss, WLAN_EID_SSID); - ssid = ie ? ie + 2 : (u8 *) ""; - ssid_len = ie ? ie[1] : 0; - wpa_dbg(wpa_s, MSG_DEBUG, " selected BSS " MACSTR " ssid='%s'", - MAC2STR(bss->bssid), wpa_ssid_txt(ssid, ssid_len)); - return wpa_bss_get(wpa_s, bss->bssid, ssid, ssid_len); + MAC2STR(bss->bssid), + wpa_ssid_txt(bss->ssid, bss->ssid_len)); + return bss; } return NULL; } -static struct wpa_bss * -wpa_supplicant_pick_network(struct wpa_supplicant *wpa_s, - struct wpa_scan_results *scan_res, - struct wpa_ssid **selected_ssid) +struct wpa_bss * wpa_supplicant_pick_network(struct wpa_supplicant *wpa_s, + struct wpa_ssid **selected_ssid) { struct wpa_bss *selected = NULL; int prio; + if (wpa_s->last_scan_res == NULL || + wpa_s->last_scan_res_used == 0) + return NULL; /* no scan results from last update */ + while (selected == NULL) { for (prio = 0; prio < wpa_s->conf->num_prio; prio++) { selected = wpa_supplicant_select_bss( - wpa_s, scan_res, wpa_s->conf->pssid[prio], + wpa_s, wpa_s->conf->pssid[prio], selected_ssid); if (selected) break; } - if (selected == NULL && wpa_s->blacklist) { + if (selected == NULL && wpa_s->blacklist && + !wpa_s->countermeasures) { wpa_dbg(wpa_s, MSG_DEBUG, "No APs found - clear " "blacklist and try again"); wpa_blacklist_clear(wpa_s); @@ -811,15 +997,19 @@ static void wpa_supplicant_req_new_scan(struct wpa_supplicant *wpa_s, int timeout_sec, int timeout_usec) { - if (!wpa_supplicant_enabled_networks(wpa_s->conf)) { + if (!wpa_supplicant_enabled_networks(wpa_s)) { /* * No networks are enabled; short-circuit request so * we don't wait timeout seconds before transitioning * to INACTIVE state. */ + wpa_dbg(wpa_s, MSG_DEBUG, "Short-circuit new scan request " + "since there are no enabled networks"); wpa_supplicant_set_state(wpa_s, WPA_INACTIVE); return; } + + wpa_s->scan_for_connection = 1; wpa_supplicant_req_scan(wpa_s, timeout_sec, timeout_usec); } @@ -842,6 +1032,15 @@ return -1; } + wpa_msg(wpa_s, MSG_DEBUG, + "Considering connect request: reassociate: %d selected: " + MACSTR " bssid: " MACSTR " pending: " MACSTR + " wpa_state: %s ssid=%p current_ssid=%p", + wpa_s->reassociate, MAC2STR(selected->bssid), + MAC2STR(wpa_s->bssid), MAC2STR(wpa_s->pending_bssid), + wpa_supplicant_state_txt(wpa_s->wpa_state), + ssid, wpa_s->current_ssid); + /* * Do not trigger new association unless the BSSID has changed or if * reassociation is requested. If we are in process of associating with @@ -851,22 +1050,21 @@ (os_memcmp(selected->bssid, wpa_s->bssid, ETH_ALEN) != 0 && ((wpa_s->wpa_state != WPA_ASSOCIATING && wpa_s->wpa_state != WPA_AUTHENTICATING) || - os_memcmp(selected->bssid, wpa_s->pending_bssid, ETH_ALEN) != - 0))) { + (!is_zero_ether_addr(wpa_s->pending_bssid) && + os_memcmp(selected->bssid, wpa_s->pending_bssid, ETH_ALEN) != + 0) || + (is_zero_ether_addr(wpa_s->pending_bssid) && + ssid != wpa_s->current_ssid)))) { if (wpa_supplicant_scard_init(wpa_s, ssid)) { wpa_supplicant_req_new_scan(wpa_s, 10, 0); return 0; } - wpa_msg(wpa_s, MSG_DEBUG, "Request association: " - "reassociate: %d selected: "MACSTR " bssid: " MACSTR - " pending: " MACSTR " wpa_state: %s", - wpa_s->reassociate, MAC2STR(selected->bssid), - MAC2STR(wpa_s->bssid), MAC2STR(wpa_s->pending_bssid), - wpa_supplicant_state_txt(wpa_s->wpa_state)); + wpa_msg(wpa_s, MSG_DEBUG, "Request association with " MACSTR, + MAC2STR(selected->bssid)); wpa_supplicant_associate(wpa_s, selected, ssid); } else { - wpa_dbg(wpa_s, MSG_DEBUG, "Already associated with the " - "selected AP"); + wpa_dbg(wpa_s, MSG_DEBUG, "Already associated or trying to " + "connect with the selected AP"); } return 0; @@ -882,7 +1080,7 @@ for (prio = 0; prio < wpa_s->conf->num_prio; prio++) { for (ssid = wpa_s->conf->pssid[prio]; ssid; ssid = ssid->pnext) { - if (ssid->disabled) + if (wpas_network_disabled(wpa_s, ssid)) continue; if (ssid->mode == IEEE80211_MODE_IBSS || ssid->mode == IEEE80211_MODE_AP) @@ -922,11 +1120,9 @@ static int wpa_supplicant_need_to_roam(struct wpa_supplicant *wpa_s, struct wpa_bss *selected, - struct wpa_ssid *ssid, - struct wpa_scan_results *scan_res) + struct wpa_ssid *ssid) { - size_t i; - struct wpa_scan_res *current_bss = NULL; + struct wpa_bss *current_bss = NULL; int min_diff; if (wpa_s->reassociate) @@ -941,25 +1137,23 @@ if (wpas_driver_bss_selection(wpa_s)) return 0; /* Driver-based roaming */ - for (i = 0; i < scan_res->num; i++) { - struct wpa_scan_res *res = scan_res->res[i]; - const u8 *ie; - if (os_memcmp(res->bssid, wpa_s->bssid, ETH_ALEN) != 0) - continue; - - ie = wpa_scan_get_ie(res, WLAN_EID_SSID); - if (ie == NULL) - continue; - if (ie[1] != wpa_s->current_ssid->ssid_len || - os_memcmp(ie + 2, wpa_s->current_ssid->ssid, ie[1]) != 0) - continue; - current_bss = res; - break; - } + if (wpa_s->current_ssid->ssid) + current_bss = wpa_bss_get(wpa_s, wpa_s->bssid, + wpa_s->current_ssid->ssid, + wpa_s->current_ssid->ssid_len); + if (!current_bss) + current_bss = wpa_bss_get_bssid(wpa_s, wpa_s->bssid); if (!current_bss) return 1; /* current BSS not seen in scan results */ + if (current_bss == selected) + return 0; + + if (selected->last_update_idx > current_bss->last_update_idx) + return 1; /* current BSS not seen in the last scan */ + +#ifndef CONFIG_NO_ROAMING wpa_dbg(wpa_s, MSG_DEBUG, "Considering within-ESS reassociation"); wpa_dbg(wpa_s, MSG_DEBUG, "Current BSS: " MACSTR " level=%d", MAC2STR(current_bss->bssid), current_bss->level); @@ -974,6 +1168,12 @@ return 1; } + if (current_bss->level < 0 && current_bss->level > selected->level) { + wpa_dbg(wpa_s, MSG_DEBUG, "Skip roam - Current BSS has better " + "signal level"); + return 0; + } + min_diff = 2; if (current_bss->level < 0) { if (current_bss->level < -85) @@ -994,16 +1194,24 @@ } return 1; +#else /* CONFIG_NO_ROAMING */ + return 0; +#endif /* CONFIG_NO_ROAMING */ } -/* Return < 0 if no scan results could be fetched. */ + +/* Return != 0 if no scan results could be fetched or if scan results should not + * be shared with other virtual interfaces. */ static int _wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s, - union wpa_event_data *data) + union wpa_event_data *data, + int own_request) { - struct wpa_bss *selected; - struct wpa_ssid *ssid = NULL; - struct wpa_scan_results *scan_res; + struct wpa_scan_results *scan_res = NULL; + int ret = 0; int ap = 0; +#ifndef CONFIG_NO_RANDOM_POOL + size_t i, num; +#endif /* CONFIG_NO_RANDOM_POOL */ #ifdef CONFIG_AP if (wpa_s->ap_iface) @@ -1012,32 +1220,23 @@ wpa_supplicant_notify_scanning(wpa_s, 0); -#ifdef CONFIG_P2P - if (wpa_s->p2p_cb_on_scan_complete && !wpa_s->global->p2p_disabled && - wpa_s->global->p2p != NULL) { - wpa_s->p2p_cb_on_scan_complete = 0; - if (p2p_other_scan_completed(wpa_s->global->p2p) == 1) { - wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Pending P2P operation " - "stopped scan processing"); - return -1; - } - } -#endif /* CONFIG_P2P */ - scan_res = wpa_supplicant_get_scan_results(wpa_s, data ? &data->scan_info : NULL, 1); if (scan_res == NULL) { - if (wpa_s->conf->ap_scan == 2 || ap) + if (wpa_s->conf->ap_scan == 2 || ap || + wpa_s->scan_res_handler == scan_only_handler) + return -1; + if (!own_request) return -1; wpa_dbg(wpa_s, MSG_DEBUG, "Failed to get scan results - try " "scanning again"); wpa_supplicant_req_new_scan(wpa_s, 1, 0); - return -1; + ret = -1; + goto scan_work_done; } #ifndef CONFIG_NO_RANDOM_POOL - size_t i, num; num = scan_res->num; if (num > 10) num = 10; @@ -1053,16 +1252,16 @@ } #endif /* CONFIG_NO_RANDOM_POOL */ - if (wpa_s->scan_res_handler) { + if (own_request && wpa_s->scan_res_handler && + (wpa_s->own_scan_running || !wpa_s->external_scan_running)) { void (*scan_res_handler)(struct wpa_supplicant *wpa_s, struct wpa_scan_results *scan_res); scan_res_handler = wpa_s->scan_res_handler; wpa_s->scan_res_handler = NULL; scan_res_handler(wpa_s, scan_res); - - wpa_scan_results_free(scan_res); - return 0; + ret = -2; + goto scan_work_done; } if (ap) { @@ -1071,42 +1270,84 @@ if (wpa_s->ap_iface->scan_cb) wpa_s->ap_iface->scan_cb(wpa_s->ap_iface); #endif /* CONFIG_AP */ - wpa_scan_results_free(scan_res); - return 0; + goto scan_work_done; } - wpa_dbg(wpa_s, MSG_DEBUG, "New scan results available"); - wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_SCAN_RESULTS); + wpa_dbg(wpa_s, MSG_DEBUG, "New scan results available (own=%u ext=%u)", + wpa_s->own_scan_running, wpa_s->external_scan_running); + if (wpa_s->last_scan_req == MANUAL_SCAN_REQ && + wpa_s->manual_scan_use_id && wpa_s->own_scan_running) { + wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_SCAN_RESULTS "id=%u", + wpa_s->manual_scan_id); + wpa_s->manual_scan_use_id = 0; + } else { + wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_SCAN_RESULTS); + } wpas_notify_scan_results(wpa_s); wpas_notify_scan_done(wpa_s, 1); - if ((wpa_s->conf->ap_scan == 2 && !wpas_wps_searching(wpa_s))) { + if (!wpa_s->own_scan_running && wpa_s->external_scan_running) { + wpa_dbg(wpa_s, MSG_DEBUG, "Do not use results from externally requested scan operation for network selection"); wpa_scan_results_free(scan_res); return 0; } + if (sme_proc_obss_scan(wpa_s) > 0) + goto scan_work_done; + + if ((wpa_s->conf->ap_scan == 2 && !wpas_wps_searching(wpa_s))) + goto scan_work_done; + + if (autoscan_notify_scan(wpa_s, scan_res)) + goto scan_work_done; + if (wpa_s->disconnected) { wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED); - wpa_scan_results_free(scan_res); - return 0; + goto scan_work_done; } if (!wpas_driver_bss_selection(wpa_s) && - bgscan_notify_scan(wpa_s, scan_res) == 1) { - wpa_scan_results_free(scan_res); - return 0; + bgscan_notify_scan(wpa_s, scan_res) == 1) + goto scan_work_done; + + wpas_wps_update_ap_info(wpa_s, scan_res); + + wpa_scan_results_free(scan_res); + + if (wpa_s->scan_work) { + struct wpa_radio_work *work = wpa_s->scan_work; + wpa_s->scan_work = NULL; + radio_work_done(work); + } + + return wpas_select_network_from_last_scan(wpa_s, 1, own_request); + +scan_work_done: + wpa_scan_results_free(scan_res); + if (wpa_s->scan_work) { + struct wpa_radio_work *work = wpa_s->scan_work; + wpa_s->scan_work = NULL; + radio_work_done(work); } + return ret; +} + + +static int wpas_select_network_from_last_scan(struct wpa_supplicant *wpa_s, + int new_scan, int own_request) +{ + struct wpa_bss *selected; + struct wpa_ssid *ssid = NULL; - selected = wpa_supplicant_pick_network(wpa_s, scan_res, &ssid); + selected = wpa_supplicant_pick_network(wpa_s, &ssid); if (selected) { int skip; - skip = !wpa_supplicant_need_to_roam(wpa_s, selected, ssid, - scan_res); - wpa_scan_results_free(scan_res); + skip = !wpa_supplicant_need_to_roam(wpa_s, selected, ssid); if (skip) { - wpa_supplicant_rsn_preauth_scan_results(wpa_s); + if (new_scan) + wpa_supplicant_rsn_preauth_scan_results(wpa_s); return 0; } @@ -1114,23 +1355,39 @@ wpa_dbg(wpa_s, MSG_DEBUG, "Connect failed"); return -1; } - wpa_supplicant_rsn_preauth_scan_results(wpa_s); + if (new_scan) + wpa_supplicant_rsn_preauth_scan_results(wpa_s); + /* + * Do not notify other virtual radios of scan results since we do not + * want them to start other associations at the same time. + */ + return 1; } else { - wpa_scan_results_free(scan_res); wpa_dbg(wpa_s, MSG_DEBUG, "No suitable network found"); ssid = wpa_supplicant_pick_new_network(wpa_s); if (ssid) { wpa_dbg(wpa_s, MSG_DEBUG, "Setup a new network"); wpa_supplicant_associate(wpa_s, NULL, ssid); - wpa_supplicant_rsn_preauth_scan_results(wpa_s); - } else { + if (new_scan) + wpa_supplicant_rsn_preauth_scan_results(wpa_s); + } else if (own_request) { + /* + * No SSID found. If SCAN results are as a result of + * own scan request and not due to a scan request on + * another shared interface, try another scan. + */ int timeout_sec = wpa_s->scan_interval; int timeout_usec = 0; #ifdef CONFIG_P2P - if (wpa_s->p2p_in_provisioning) { + if (wpas_p2p_scan_no_go_seen(wpa_s) == 1) + return 0; + + if (wpa_s->p2p_in_provisioning || + wpa_s->show_group_started) { /* * Use shorter wait during P2P Provisioning - * state to speed up group formation. + * state and during P2P join-a-group operation + * to speed up group formation. */ timeout_sec = 0; timeout_usec = 250000; @@ -1139,6 +1396,29 @@ return 0; } #endif /* CONFIG_P2P */ +#ifdef CONFIG_INTERWORKING + if (wpa_s->conf->auto_interworking && + wpa_s->conf->interworking && + wpa_s->conf->cred) { + wpa_dbg(wpa_s, MSG_DEBUG, "Interworking: " + "start ANQP fetch since no matching " + "networks found"); + wpa_s->network_select = 1; + wpa_s->auto_network_select = 1; + interworking_start_fetch_anqp(wpa_s); + return 1; + } +#endif /* CONFIG_INTERWORKING */ +#ifdef CONFIG_WPS + if (wpa_s->after_wps > 0 || wpas_wps_searching(wpa_s)) { + wpa_dbg(wpa_s, MSG_DEBUG, "Use shorter wait during WPS processing"); + timeout_sec = 0; + timeout_usec = 500000; + wpa_supplicant_req_new_scan(wpa_s, timeout_sec, + timeout_usec); + return 0; + } +#endif /* CONFIG_WPS */ if (wpa_supplicant_req_sched_scan(wpa_s)) wpa_supplicant_req_new_scan(wpa_s, timeout_sec, timeout_usec); @@ -1151,41 +1431,29 @@ static void wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s, union wpa_event_data *data) { - const char *rn, *rn2; struct wpa_supplicant *ifs; - if (_wpa_supplicant_event_scan_results(wpa_s, data) < 0) { + if (_wpa_supplicant_event_scan_results(wpa_s, data, 1) != 0) { /* * If no scan results could be fetched, then no need to * notify those interfaces that did not actually request - * this scan. + * this scan. Similarly, if scan results started a new operation on this + * interface, do not notify other interfaces to avoid concurrent + * operations during a connection attempt. */ return; } /* - * Check other interfaces to see if they have the same radio-name. If + * Check other interfaces to see if they share the same radio. If * so, they get updated with this same scan info. */ - if (!wpa_s->driver->get_radio_name) - return; - - rn = wpa_s->driver->get_radio_name(wpa_s->drv_priv); - if (rn == NULL || rn[0] == '\0') - return; - - wpa_dbg(wpa_s, MSG_DEBUG, "Checking for other virtual interfaces " - "sharing same radio (%s) in event_scan_results", rn); - - for (ifs = wpa_s->global->ifaces; ifs; ifs = ifs->next) { - if (ifs == wpa_s || !ifs->driver->get_radio_name) - continue; - - rn2 = ifs->driver->get_radio_name(ifs->drv_priv); - if (rn2 && os_strcmp(rn, rn2) == 0) { + dl_list_for_each(ifs, &wpa_s->radio->ifaces, struct wpa_supplicant, + radio_list) { + if (ifs != wpa_s) { wpa_printf(MSG_DEBUG, "%s: Updating scan results from " "sibling", ifs->ifname); - _wpa_supplicant_event_scan_results(ifs, data); + _wpa_supplicant_event_scan_results(ifs, data, 0); } } } @@ -1193,49 +1461,197 @@ #endif /* CONFIG_NO_SCAN_PROCESSING */ -static int wpa_supplicant_event_associnfo(struct wpa_supplicant *wpa_s, - union wpa_event_data *data) +int wpa_supplicant_fast_associate(struct wpa_supplicant *wpa_s) { - int l, len, found = 0, wpa_found, rsn_found; - const u8 *p; +#ifdef CONFIG_NO_SCAN_PROCESSING + return -1; +#else /* CONFIG_NO_SCAN_PROCESSING */ + struct os_reltime now; - wpa_dbg(wpa_s, MSG_DEBUG, "Association info event"); - if (data->assoc_info.req_ies) - wpa_hexdump(MSG_DEBUG, "req_ies", data->assoc_info.req_ies, - data->assoc_info.req_ies_len); - if (data->assoc_info.resp_ies) { - wpa_hexdump(MSG_DEBUG, "resp_ies", data->assoc_info.resp_ies, - data->assoc_info.resp_ies_len); -#ifdef CONFIG_TDLS - wpa_tdls_assoc_resp_ies(wpa_s->wpa, data->assoc_info.resp_ies, - data->assoc_info.resp_ies_len); -#endif /* CONFIG_TDLS */ + if (wpa_s->last_scan_res_used <= 0) + return -1; + + os_get_reltime(&now); + if (os_reltime_expired(&now, &wpa_s->last_scan, 5)) { + wpa_printf(MSG_DEBUG, "Fast associate: Old scan results"); + return -1; } - if (data->assoc_info.beacon_ies) - wpa_hexdump(MSG_DEBUG, "beacon_ies", - data->assoc_info.beacon_ies, - data->assoc_info.beacon_ies_len); - if (data->assoc_info.freq) - wpa_dbg(wpa_s, MSG_DEBUG, "freq=%u MHz", - data->assoc_info.freq); - p = data->assoc_info.req_ies; - l = data->assoc_info.req_ies_len; + return wpas_select_network_from_last_scan(wpa_s, 0, 1); +#endif /* CONFIG_NO_SCAN_PROCESSING */ +} - /* Go through the IEs and make a copy of the WPA/RSN IE, if present. */ - while (p && l >= 2) { - len = p[1] + 2; - if (len > l) { - wpa_hexdump(MSG_DEBUG, "Truncated IE in assoc_info", - p, l); - break; - } - if ((p[0] == WLAN_EID_VENDOR_SPECIFIC && p[1] >= 6 && - (os_memcmp(&p[2], "\x00\x50\xF2\x01\x01\x00", 6) == 0)) || - (p[0] == WLAN_EID_RSN && p[1] >= 2)) { - if (wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, p, len)) - break; - found = 1; +#ifdef CONFIG_WNM + +static void wnm_bss_keep_alive(void *eloop_ctx, void *sock_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + + if (wpa_s->wpa_state < WPA_ASSOCIATED) + return; + + if (!wpa_s->no_keep_alive) { + wpa_printf(MSG_DEBUG, "WNM: Send keep-alive to AP " MACSTR, + MAC2STR(wpa_s->bssid)); + /* TODO: could skip this if normal data traffic has been sent */ + /* TODO: Consider using some more appropriate data frame for + * this */ + if (wpa_s->l2) + l2_packet_send(wpa_s->l2, wpa_s->bssid, 0x0800, + (u8 *) "", 0); + } + +#ifdef CONFIG_SME + if (wpa_s->sme.bss_max_idle_period) { + unsigned int msec; + msec = wpa_s->sme.bss_max_idle_period * 1024; /* times 1000 */ + if (msec > 100) + msec -= 100; + eloop_register_timeout(msec / 1000, msec % 1000 * 1000, + wnm_bss_keep_alive, wpa_s, NULL); + } +#endif /* CONFIG_SME */ +} + + +static void wnm_process_assoc_resp(struct wpa_supplicant *wpa_s, + const u8 *ies, size_t ies_len) +{ + struct ieee802_11_elems elems; + + if (ies == NULL) + return; + + if (ieee802_11_parse_elems(ies, ies_len, &elems, 1) == ParseFailed) + return; + +#ifdef CONFIG_SME + if (elems.bss_max_idle_period) { + unsigned int msec; + wpa_s->sme.bss_max_idle_period = + WPA_GET_LE16(elems.bss_max_idle_period); + wpa_printf(MSG_DEBUG, "WNM: BSS Max Idle Period: %u (* 1000 " + "TU)%s", wpa_s->sme.bss_max_idle_period, + (elems.bss_max_idle_period[2] & 0x01) ? + " (protected keep-live required)" : ""); + if (wpa_s->sme.bss_max_idle_period == 0) + wpa_s->sme.bss_max_idle_period = 1; + if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) { + eloop_cancel_timeout(wnm_bss_keep_alive, wpa_s, NULL); + /* msec times 1000 */ + msec = wpa_s->sme.bss_max_idle_period * 1024; + if (msec > 100) + msec -= 100; + eloop_register_timeout(msec / 1000, msec % 1000 * 1000, + wnm_bss_keep_alive, wpa_s, + NULL); + } + } +#endif /* CONFIG_SME */ +} + +#endif /* CONFIG_WNM */ + + +void wnm_bss_keep_alive_deinit(struct wpa_supplicant *wpa_s) +{ +#ifdef CONFIG_WNM + eloop_cancel_timeout(wnm_bss_keep_alive, wpa_s, NULL); +#endif /* CONFIG_WNM */ +} + + +#ifdef CONFIG_INTERWORKING + +static int wpas_qos_map_set(struct wpa_supplicant *wpa_s, const u8 *qos_map, + size_t len) +{ + int res; + + wpa_hexdump(MSG_DEBUG, "Interworking: QoS Map Set", qos_map, len); + res = wpa_drv_set_qos_map(wpa_s, qos_map, len); + if (res) { + wpa_printf(MSG_DEBUG, "Interworking: Failed to configure QoS Map Set to the driver"); + } + + return res; +} + + +static void interworking_process_assoc_resp(struct wpa_supplicant *wpa_s, + const u8 *ies, size_t ies_len) +{ + struct ieee802_11_elems elems; + + if (ies == NULL) + return; + + if (ieee802_11_parse_elems(ies, ies_len, &elems, 1) == ParseFailed) + return; + + if (elems.qos_map_set) { + wpas_qos_map_set(wpa_s, elems.qos_map_set, + elems.qos_map_set_len); + } +} + +#endif /* CONFIG_INTERWORKING */ + + +static int wpa_supplicant_event_associnfo(struct wpa_supplicant *wpa_s, + union wpa_event_data *data) +{ + int l, len, found = 0, wpa_found, rsn_found; + const u8 *p; +#ifdef CONFIG_IEEE80211R + u8 bssid[ETH_ALEN]; +#endif /* CONFIG_IEEE80211R */ + + wpa_dbg(wpa_s, MSG_DEBUG, "Association info event"); + if (data->assoc_info.req_ies) + wpa_hexdump(MSG_DEBUG, "req_ies", data->assoc_info.req_ies, + data->assoc_info.req_ies_len); + if (data->assoc_info.resp_ies) { + wpa_hexdump(MSG_DEBUG, "resp_ies", data->assoc_info.resp_ies, + data->assoc_info.resp_ies_len); +#ifdef CONFIG_TDLS + wpa_tdls_assoc_resp_ies(wpa_s->wpa, data->assoc_info.resp_ies, + data->assoc_info.resp_ies_len); +#endif /* CONFIG_TDLS */ +#ifdef CONFIG_WNM + wnm_process_assoc_resp(wpa_s, data->assoc_info.resp_ies, + data->assoc_info.resp_ies_len); +#endif /* CONFIG_WNM */ +#ifdef CONFIG_INTERWORKING + interworking_process_assoc_resp(wpa_s, data->assoc_info.resp_ies, + data->assoc_info.resp_ies_len); +#endif /* CONFIG_INTERWORKING */ + } + if (data->assoc_info.beacon_ies) + wpa_hexdump(MSG_DEBUG, "beacon_ies", + data->assoc_info.beacon_ies, + data->assoc_info.beacon_ies_len); + if (data->assoc_info.freq) + wpa_dbg(wpa_s, MSG_DEBUG, "freq=%u MHz", + data->assoc_info.freq); + + p = data->assoc_info.req_ies; + l = data->assoc_info.req_ies_len; + + /* Go through the IEs and make a copy of the WPA/RSN IE, if present. */ + while (p && l >= 2) { + len = p[1] + 2; + if (len > l) { + wpa_hexdump(MSG_DEBUG, "Truncated IE in assoc_info", + p, l); + break; + } + if ((p[0] == WLAN_EID_VENDOR_SPECIFIC && p[1] >= 6 && + (os_memcmp(&p[2], "\x00\x50\xF2\x01\x01\x00", 6) == 0)) || + (p[0] == WLAN_EID_RSN && p[1] >= 2)) { + if (wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, p, len)) + break; + found = 1; wpa_find_assoc_pmkid(wpa_s); break; } @@ -1248,7 +1664,6 @@ #ifdef CONFIG_IEEE80211R #ifdef CONFIG_SME if (wpa_s->sme.auth_alg == WPA_AUTH_ALG_FT) { - u8 bssid[ETH_ALEN]; if (wpa_drv_get_bssid(wpa_s, bssid) < 0 || wpa_ft_validate_reassoc_resp(wpa_s->wpa, data->assoc_info.resp_ies, @@ -1306,6 +1721,23 @@ } #endif /* CONFIG_SME */ + /* Process FT when SME is in the driver */ + if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) && + wpa_ft_is_completed(wpa_s->wpa)) { + if (wpa_drv_get_bssid(wpa_s, bssid) < 0 || + wpa_ft_validate_reassoc_resp(wpa_s->wpa, + data->assoc_info.resp_ies, + data->assoc_info.resp_ies_len, + bssid) < 0) { + wpa_dbg(wpa_s, MSG_DEBUG, "FT: Validation of " + "Reassociation Response failed"); + wpa_supplicant_deauthenticate( + wpa_s, WLAN_REASON_INVALID_IE); + return -1; + } + wpa_dbg(wpa_s, MSG_DEBUG, "FT: Reassociation Response done"); + } + wpa_sm_set_ft_params(wpa_s->wpa, data->assoc_info.resp_ies, data->assoc_info.resp_ies_len); #endif /* CONFIG_IEEE80211R */ @@ -1362,13 +1794,35 @@ } +static int wpa_supplicant_assoc_update_ie(struct wpa_supplicant *wpa_s) +{ + const u8 *bss_wpa = NULL, *bss_rsn = NULL; + + if (!wpa_s->current_bss || !wpa_s->current_ssid) + return -1; + + if (!wpa_key_mgmt_wpa_any(wpa_s->current_ssid->key_mgmt)) + return 0; + + bss_wpa = wpa_bss_get_vendor_ie(wpa_s->current_bss, + WPA_IE_VENDOR_TYPE); + bss_rsn = wpa_bss_get_ie(wpa_s->current_bss, WLAN_EID_RSN); + + if (wpa_sm_set_ap_wpa_ie(wpa_s->wpa, bss_wpa, + bss_wpa ? 2 + bss_wpa[1] : 0) || + wpa_sm_set_ap_rsn_ie(wpa_s->wpa, bss_rsn, + bss_rsn ? 2 + bss_rsn[1] : 0)) + return -1; + + return 0; +} + + static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s, union wpa_event_data *data) { u8 bssid[ETH_ALEN]; int ft_completed; - int bssid_changed; - struct wpa_driver_capa capa; #ifdef CONFIG_AP if (wpa_s->ap_iface) { @@ -1385,36 +1839,36 @@ if (data && wpa_supplicant_event_associnfo(wpa_s, data) < 0) return; + if (wpa_drv_get_bssid(wpa_s, bssid) < 0) { + wpa_dbg(wpa_s, MSG_ERROR, "Failed to get BSSID"); + wpa_supplicant_deauthenticate( + wpa_s, WLAN_REASON_DEAUTH_LEAVING); + return; + } + wpa_supplicant_set_state(wpa_s, WPA_ASSOCIATED); - if (wpa_drv_get_bssid(wpa_s, bssid) >= 0 && - os_memcmp(bssid, wpa_s->bssid, ETH_ALEN) != 0) { + if (os_memcmp(bssid, wpa_s->bssid, ETH_ALEN) != 0) { wpa_dbg(wpa_s, MSG_DEBUG, "Associated to a new BSS: BSSID=" MACSTR, MAC2STR(bssid)); random_add_randomness(bssid, ETH_ALEN); - bssid_changed = os_memcmp(wpa_s->bssid, bssid, ETH_ALEN); os_memcpy(wpa_s->bssid, bssid, ETH_ALEN); os_memset(wpa_s->pending_bssid, 0, ETH_ALEN); - if (bssid_changed) - wpas_notify_bssid_changed(wpa_s); + wpas_notify_bssid_changed(wpa_s); if (wpa_supplicant_dynamic_keys(wpa_s) && !ft_completed) { wpa_clear_keys(wpa_s, bssid); } if (wpa_supplicant_select_config(wpa_s) < 0) { - wpa_supplicant_disassociate( + wpa_supplicant_deauthenticate( wpa_s, WLAN_REASON_DEAUTH_LEAVING); return; } - if (wpa_s->current_ssid) { - struct wpa_bss *bss = NULL; - struct wpa_ssid *ssid = wpa_s->current_ssid; - if (ssid->ssid_len > 0) - bss = wpa_bss_get(wpa_s, bssid, - ssid->ssid, ssid->ssid_len); - if (!bss) - bss = wpa_bss_get_bssid(wpa_s, bssid); - if (bss) - wpa_s->current_bss = bss; + + if (wpa_s->conf->ap_scan == 1 && + wpa_s->drv_flags & WPA_DRIVER_FLAGS_BSS_SELECTION) { + if (wpa_supplicant_assoc_update_ie(wpa_s) < 0) + wpa_msg(wpa_s, MSG_WARNING, + "WPA/RSN IEs not updated"); } } @@ -1456,6 +1910,16 @@ wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE || (wpa_s->current_ssid && wpa_s->current_ssid->mode == IEEE80211_MODE_IBSS)) { + if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE && + (wpa_s->drv_flags & + WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC_DONE)) { + /* + * Set the key after having received joined-IBSS event + * from the driver. + */ + wpa_supplicant_set_wpa_none_key(wpa_s, + wpa_s->current_ssid); + } wpa_supplicant_cancel_auth_timeout(wpa_s); wpa_supplicant_set_state(wpa_s, WPA_COMPLETED); } else if (!ft_completed) { @@ -1493,10 +1957,12 @@ eapol_sm_notify_eap_success(wpa_s->eapol, TRUE); } + wpa_s->last_eapol_matches_bssid = 0; + if (wpa_s->pending_eapol_rx) { - struct os_time now, age; - os_get_time(&now); - os_time_sub(&now, &wpa_s->pending_eapol_rx_time, &age); + struct os_reltime now, age; + os_get_reltime(&now); + os_reltime_sub(&now, &wpa_s->pending_eapol_rx_time, &age); if (age.sec == 0 && age.usec < 100000 && os_memcmp(wpa_s->pending_eapol_rx_src, bssid, ETH_ALEN) == 0) { @@ -1514,8 +1980,8 @@ if ((wpa_s->key_mgmt == WPA_KEY_MGMT_NONE || wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA) && - wpa_s->current_ssid && wpa_drv_get_capa(wpa_s, &capa) == 0 && - capa.flags & WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC_DONE) { + wpa_s->current_ssid && + (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC_DONE)) { /* Set static WEP keys again */ wpa_set_wep_keys(wpa_s, wpa_s->current_ssid); } @@ -1537,15 +2003,82 @@ ibss_rsn_set_psk(wpa_s->ibss_rsn, wpa_s->current_ssid->psk); } #endif /* CONFIG_IBSS_RSN */ + + wpas_wps_notify_assoc(wpa_s, bssid); +} + + +static int disconnect_reason_recoverable(u16 reason_code) +{ + return reason_code == WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY || + reason_code == WLAN_REASON_CLASS2_FRAME_FROM_NONAUTH_STA || + reason_code == WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA; } static void wpa_supplicant_event_disassoc(struct wpa_supplicant *wpa_s, - u16 reason_code) + u16 reason_code, + int locally_generated) +{ + const u8 *bssid; + + if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE) { + /* + * At least Host AP driver and a Prism3 card seemed to be + * generating streams of disconnected events when configuring + * IBSS for WPA-None. Ignore them for now. + */ + return; + } + + bssid = wpa_s->bssid; + if (is_zero_ether_addr(bssid)) + bssid = wpa_s->pending_bssid; + + if (!is_zero_ether_addr(bssid) || + wpa_s->wpa_state >= WPA_AUTHENTICATING) { + wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_DISCONNECTED "bssid=" MACSTR + " reason=%d%s", + MAC2STR(bssid), reason_code, + locally_generated ? " locally_generated=1" : ""); + } +} + + +static int could_be_psk_mismatch(struct wpa_supplicant *wpa_s, u16 reason_code, + int locally_generated) +{ + if (wpa_s->wpa_state != WPA_4WAY_HANDSHAKE || + !wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt)) + return 0; /* Not in 4-way handshake with PSK */ + + /* + * It looks like connection was lost while trying to go through PSK + * 4-way handshake. Filter out known disconnection cases that are caused + * by something else than PSK mismatch to avoid confusing reports. + */ + + if (locally_generated) { + if (reason_code == WLAN_REASON_IE_IN_4WAY_DIFFERS) + return 0; + } + + return 1; +} + + +static void wpa_supplicant_event_disassoc_finish(struct wpa_supplicant *wpa_s, + u16 reason_code, + int locally_generated) { const u8 *bssid; int authenticating; u8 prev_pending_bssid[ETH_ALEN]; + struct wpa_bss *fast_reconnect = NULL; +#ifndef CONFIG_NO_SCAN_PROCESSING + struct wpa_ssid *fast_reconnect_ssid = NULL; +#endif /* CONFIG_NO_SCAN_PROCESSING */ + struct wpa_ssid *last_ssid; authenticating = wpa_s->wpa_state == WPA_AUTHENTICATING; os_memcpy(prev_pending_bssid, wpa_s->pending_bssid, ETH_ALEN); @@ -1561,20 +2094,42 @@ return; } - if (wpa_s->wpa_state == WPA_4WAY_HANDSHAKE && - wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt)) { + if (could_be_psk_mismatch(wpa_s, reason_code, locally_generated)) { wpa_msg(wpa_s, MSG_INFO, "WPA: 4-Way Handshake failed - " "pre-shared key may be incorrect"); - } - if (!wpa_s->auto_reconnect_disabled || - wpa_s->key_mgmt == WPA_KEY_MGMT_WPS) { - wpa_dbg(wpa_s, MSG_DEBUG, "WPA: Auto connect enabled: try to " - "reconnect (wps=%d)", - wpa_s->key_mgmt == WPA_KEY_MGMT_WPS); - if (wpa_s->wpa_state >= WPA_ASSOCIATING) + if (wpas_p2p_4way_hs_failed(wpa_s) > 0) + return; /* P2P group removed */ + wpas_auth_failed(wpa_s); + } + if (!wpa_s->disconnected && + (!wpa_s->auto_reconnect_disabled || + wpa_s->key_mgmt == WPA_KEY_MGMT_WPS)) { + wpa_dbg(wpa_s, MSG_DEBUG, "Auto connect enabled: try to " + "reconnect (wps=%d wpa_state=%d)", + wpa_s->key_mgmt == WPA_KEY_MGMT_WPS, + wpa_s->wpa_state); + if (wpa_s->wpa_state == WPA_COMPLETED && + wpa_s->current_ssid && + wpa_s->current_ssid->mode == WPAS_MODE_INFRA && + !locally_generated && + disconnect_reason_recoverable(reason_code)) { + /* + * It looks like the AP has dropped association with + * us, but could allow us to get back in. Try to + * reconnect to the same BSS without full scan to save + * time for some common cases. + */ + fast_reconnect = wpa_s->current_bss; +#ifndef CONFIG_NO_SCAN_PROCESSING + fast_reconnect_ssid = wpa_s->current_ssid; +#endif /* CONFIG_NO_SCAN_PROCESSING */ + } else if (wpa_s->wpa_state >= WPA_ASSOCIATING) wpa_supplicant_req_scan(wpa_s, 0, 100000); + else + wpa_dbg(wpa_s, MSG_DEBUG, "Do not request new " + "immediate scan"); } else { - wpa_dbg(wpa_s, MSG_DEBUG, "WPA: Auto connect disabled: do not " + wpa_dbg(wpa_s, MSG_DEBUG, "Auto connect disabled: do not " "try to re-connect"); wpa_s->reassociate = 0; wpa_s->disconnected = 1; @@ -1583,20 +2138,36 @@ bssid = wpa_s->bssid; if (is_zero_ether_addr(bssid)) bssid = wpa_s->pending_bssid; - wpas_connection_failed(wpa_s, bssid); + if (wpa_s->wpa_state >= WPA_AUTHENTICATING) + wpas_connection_failed(wpa_s, bssid); wpa_sm_notify_disassoc(wpa_s->wpa); - wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_DISCONNECTED "bssid=" MACSTR - " reason=%d", - MAC2STR(bssid), reason_code); + if (locally_generated) + wpa_s->disconnect_reason = -reason_code; + else + wpa_s->disconnect_reason = reason_code; + wpas_notify_disconnect_reason(wpa_s); if (wpa_supplicant_dynamic_keys(wpa_s)) { wpa_dbg(wpa_s, MSG_DEBUG, "Disconnect event - remove keys"); - wpa_s->keys_cleared = 0; wpa_clear_keys(wpa_s, wpa_s->bssid); } + last_ssid = wpa_s->current_ssid; wpa_supplicant_mark_disassoc(wpa_s); - if (authenticating && (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME)) + if (authenticating && (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME)) { sme_disassoc_while_authenticating(wpa_s, prev_pending_bssid); + wpa_s->current_ssid = last_ssid; + } + + if (fast_reconnect) { +#ifndef CONFIG_NO_SCAN_PROCESSING + wpa_dbg(wpa_s, MSG_DEBUG, "Try to reconnect to the same BSS"); + if (wpa_supplicant_connect(wpa_s, fast_reconnect, + fast_reconnect_ssid) < 0) { + /* Recover through full scan */ + wpa_supplicant_req_scan(wpa_s, 0, 100000); + } +#endif /* CONFIG_NO_SCAN_PROCESSING */ + } } @@ -1620,13 +2191,13 @@ union wpa_event_data *data) { int pairwise; - struct os_time t; + struct os_reltime t; wpa_msg(wpa_s, MSG_WARNING, "Michael MIC failure detected"); pairwise = (data && data->michael_mic_failure.unicast); - os_get_time(&t); - if ((wpa_s->last_michael_mic_error && - t.sec - wpa_s->last_michael_mic_error <= 60) || + os_get_reltime(&t); + if ((wpa_s->last_michael_mic_error.sec && + !os_reltime_expired(&t, &wpa_s->last_michael_mic_error, 60)) || wpa_s->pending_mic_error_report) { if (wpa_s->pending_mic_error_report) { /* @@ -1645,6 +2216,9 @@ /* initialize countermeasures */ wpa_s->countermeasures = 1; + + wpa_blacklist_add(wpa_s, wpa_s->bssid); + wpa_msg(wpa_s, MSG_WARNING, "TKIP countermeasures started"); /* @@ -1701,7 +2275,7 @@ wpa_sm_key_request(wpa_s->wpa, 1, pairwise); #endif /* CONFIG_DELAYED_MIC_ERROR_REPORT */ } - wpa_s->last_michael_mic_error = t.sec; + wpa_s->last_michael_mic_error = t; wpa_s->mic_errors_seen++; } @@ -1736,7 +2310,6 @@ wpa_msg(wpa_s, MSG_INFO, "Failed to initialize the " "driver after interface was added"); } - wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED); break; case EVENT_INTERFACE_REMOVED: wpa_dbg(wpa_s, MSG_DEBUG, "Configured interface was removed"); @@ -1779,17 +2352,44 @@ return; switch (data->tdls.oper) { case TDLS_REQUEST_SETUP: - wpa_tdls_start(wpa_s->wpa, data->tdls.peer); + wpa_tdls_remove(wpa_s->wpa, data->tdls.peer); + if (wpa_tdls_is_external_setup(wpa_s->wpa)) + wpa_tdls_start(wpa_s->wpa, data->tdls.peer); + else + wpa_drv_tdls_oper(wpa_s, TDLS_SETUP, data->tdls.peer); break; case TDLS_REQUEST_TEARDOWN: - wpa_tdls_send_teardown(wpa_s->wpa, data->tdls.peer, - data->tdls.reason_code); + if (wpa_tdls_is_external_setup(wpa_s->wpa)) + wpa_tdls_teardown_link(wpa_s->wpa, data->tdls.peer, + data->tdls.reason_code); + else + wpa_drv_tdls_oper(wpa_s, TDLS_TEARDOWN, + data->tdls.peer); break; } } #endif /* CONFIG_TDLS */ +#ifdef CONFIG_WNM +static void wpa_supplicant_event_wnm(struct wpa_supplicant *wpa_s, + union wpa_event_data *data) +{ + if (data == NULL) + return; + switch (data->wnm.oper) { + case WNM_OPER_SLEEP: + wpa_printf(MSG_DEBUG, "Start sending WNM-Sleep Request " + "(action=%d, intval=%d)", + data->wnm.sleep_action, data->wnm.sleep_intval); + ieee802_11_send_wnmsleep_req(wpa_s, data->wnm.sleep_action, + data->wnm.sleep_intval, NULL); + break; + } +} +#endif /* CONFIG_WNM */ + + #ifdef CONFIG_IEEE80211R static void wpa_supplicant_event_ft_response(struct wpa_supplicant *wpa_s, @@ -1827,6 +2427,23 @@ ibss_rsn_start(wpa_s->ibss_rsn, data->ibss_rsn_start.peer); } + + +static void wpa_supplicant_event_ibss_auth(struct wpa_supplicant *wpa_s, + union wpa_event_data *data) +{ + struct wpa_ssid *ssid = wpa_s->current_ssid; + + if (ssid == NULL) + return; + + /* check if the ssid is correctly configured as IBSS/RSN */ + if (ssid->mode != WPAS_MODE_IBSS || !wpa_key_mgmt_wpa(ssid->key_mgmt)) + return; + + ibss_rsn_handle_auth(wpa_s->ibss_rsn, data->rx_mgmt.frame, + data->rx_mgmt.frame_len); +} #endif /* CONFIG_IBSS_RSN */ @@ -1910,47 +2527,302 @@ } -static void wnm_action_rx(struct wpa_supplicant *wpa_s, struct rx_action *rx) +static void wpas_event_disconnect(struct wpa_supplicant *wpa_s, const u8 *addr, + u16 reason_code, int locally_generated, + const u8 *ie, size_t ie_len, int deauth) { - u8 action, mode; - const u8 *pos, *end; +#ifdef CONFIG_AP + if (wpa_s->ap_iface && addr) { + hostapd_notif_disassoc(wpa_s->ap_iface->bss[0], addr); + return; + } - if (rx->data == NULL || rx->len == 0) + if (wpa_s->ap_iface) { + wpa_dbg(wpa_s, MSG_DEBUG, "Ignore deauth event in AP mode"); return; + } +#endif /* CONFIG_AP */ - pos = rx->data; - end = pos + rx->len; - action = *pos++; - - wpa_printf(MSG_DEBUG, "WNM: RX action %u from " MACSTR, - action, MAC2STR(rx->sa)); - switch (action) { - case WNM_BSS_TRANS_MGMT_REQ: - if (pos + 5 > end) - break; - wpa_printf(MSG_DEBUG, "WNM: BSS Transition Management " - "Request: dialog_token=%u request_mode=0x%x " - "disassoc_timer=%u validity_interval=%u", - pos[0], pos[1], WPA_GET_LE16(pos + 2), pos[4]); - mode = pos[1]; - pos += 5; - if (mode & 0x08) - pos += 12; /* BSS Termination Duration */ - if (mode & 0x10) { - char url[256]; - if (pos + 1 > end || pos + 1 + pos[0] > end) { - wpa_printf(MSG_DEBUG, "WNM: Invalid BSS " - "Transition Management Request " - "(URL)"); - break; - } - os_memcpy(url, pos + 1, pos[0]); - url[pos[0]] = '\0'; - wpa_msg(wpa_s, MSG_INFO, "WNM: ESS Disassociation " - "Imminent - session_info_url=%s", url); + wpa_supplicant_event_disassoc(wpa_s, reason_code, locally_generated); + + if (((reason_code == WLAN_REASON_IEEE_802_1X_AUTH_FAILED || + ((wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt) || + (wpa_s->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA)) && + eapol_sm_failed(wpa_s->eapol))) && + !wpa_s->eap_expected_failure)) + wpas_auth_failed(wpa_s); + +#ifdef CONFIG_P2P + if (deauth && reason_code > 0) { + if (wpas_p2p_deauth_notif(wpa_s, addr, reason_code, ie, ie_len, + locally_generated) > 0) { + /* + * The interface was removed, so cannot continue + * processing any additional operations after this. + */ + return; } - break; } +#endif /* CONFIG_P2P */ + + wpa_supplicant_event_disassoc_finish(wpa_s, reason_code, + locally_generated); +} + + +static void wpas_event_disassoc(struct wpa_supplicant *wpa_s, + struct disassoc_info *info) +{ + u16 reason_code = 0; + int locally_generated = 0; + const u8 *addr = NULL; + const u8 *ie = NULL; + size_t ie_len = 0; + + wpa_dbg(wpa_s, MSG_DEBUG, "Disassociation notification"); + + if (info) { + addr = info->addr; + ie = info->ie; + ie_len = info->ie_len; + reason_code = info->reason_code; + locally_generated = info->locally_generated; + wpa_dbg(wpa_s, MSG_DEBUG, " * reason %u%s", reason_code, + locally_generated ? " (locally generated)" : ""); + if (addr) + wpa_dbg(wpa_s, MSG_DEBUG, " * address " MACSTR, + MAC2STR(addr)); + wpa_hexdump(MSG_DEBUG, "Disassociation frame IE(s)", + ie, ie_len); + } + +#ifdef CONFIG_AP + if (wpa_s->ap_iface && info && info->addr) { + hostapd_notif_disassoc(wpa_s->ap_iface->bss[0], info->addr); + return; + } + + if (wpa_s->ap_iface) { + wpa_dbg(wpa_s, MSG_DEBUG, "Ignore disassoc event in AP mode"); + return; + } +#endif /* CONFIG_AP */ + +#ifdef CONFIG_P2P + if (info) { + wpas_p2p_disassoc_notif( + wpa_s, info->addr, reason_code, info->ie, info->ie_len, + locally_generated); + } +#endif /* CONFIG_P2P */ + + if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) + sme_event_disassoc(wpa_s, info); + + wpas_event_disconnect(wpa_s, addr, reason_code, locally_generated, + ie, ie_len, 0); +} + + +static void wpas_event_deauth(struct wpa_supplicant *wpa_s, + struct deauth_info *info) +{ + u16 reason_code = 0; + int locally_generated = 0; + const u8 *addr = NULL; + const u8 *ie = NULL; + size_t ie_len = 0; + + wpa_dbg(wpa_s, MSG_DEBUG, "Deauthentication notification"); + + if (info) { + addr = info->addr; + ie = info->ie; + ie_len = info->ie_len; + reason_code = info->reason_code; + locally_generated = info->locally_generated; + wpa_dbg(wpa_s, MSG_DEBUG, " * reason %u%s", + reason_code, + locally_generated ? " (locally generated)" : ""); + if (addr) { + wpa_dbg(wpa_s, MSG_DEBUG, " * address " MACSTR, + MAC2STR(addr)); + } + wpa_hexdump(MSG_DEBUG, "Deauthentication frame IE(s)", + ie, ie_len); + } + + wpa_reset_ft_completed(wpa_s->wpa); + + wpas_event_disconnect(wpa_s, addr, reason_code, + locally_generated, ie, ie_len, 1); +} + + +static void wpa_supplicant_update_channel_list(struct wpa_supplicant *wpa_s) +{ + struct wpa_supplicant *ifs; + + if (wpa_s->drv_priv == NULL) + return; /* Ignore event during drv initialization */ + + free_hw_features(wpa_s); + wpa_s->hw.modes = wpa_drv_get_hw_feature_data( + wpa_s, &wpa_s->hw.num_modes, &wpa_s->hw.flags); + +#ifdef CONFIG_P2P + wpas_p2p_update_channel_list(wpa_s); +#endif /* CONFIG_P2P */ + + /* + * Check other interfaces to see if they share the same radio. If + * so, they get updated with this same hw mode info. + */ + dl_list_for_each(ifs, &wpa_s->radio->ifaces, struct wpa_supplicant, + radio_list) { + if (ifs != wpa_s) { + wpa_printf(MSG_DEBUG, "%s: Updating hw mode", + ifs->ifname); + free_hw_features(ifs); + ifs->hw.modes = wpa_drv_get_hw_feature_data( + ifs, &ifs->hw.num_modes, &ifs->hw.flags); + } + } +} + + +static void wpas_event_rx_mgmt_action(struct wpa_supplicant *wpa_s, + const struct ieee80211_mgmt *mgmt, + size_t len, int freq) +{ + const u8 *payload; + size_t plen; + u8 category; + + if (len < IEEE80211_HDRLEN + 2) + return; + + payload = &mgmt->u.action.category; + category = *payload++; + plen = (((const u8 *) mgmt) + len) - payload; + + wpa_dbg(wpa_s, MSG_DEBUG, "Received Action frame: SA=" MACSTR + " Category=%u DataLen=%d freq=%d MHz", + MAC2STR(mgmt->sa), category, (int) plen, freq); + +#ifdef CONFIG_IEEE80211R + if (category == WLAN_ACTION_FT) { + ft_rx_action(wpa_s, payload, plen); + return; + } +#endif /* CONFIG_IEEE80211R */ + +#ifdef CONFIG_IEEE80211W +#ifdef CONFIG_SME + if (category == WLAN_ACTION_SA_QUERY) { + sme_sa_query_rx(wpa_s, mgmt->sa, payload, plen); + return; + } +#endif /* CONFIG_SME */ +#endif /* CONFIG_IEEE80211W */ + +#ifdef CONFIG_WNM + if (mgmt->u.action.category == WLAN_ACTION_WNM) { + ieee802_11_rx_wnm_action(wpa_s, mgmt, len); + return; + } +#endif /* CONFIG_WNM */ + +#ifdef CONFIG_GAS + if ((mgmt->u.action.category == WLAN_ACTION_PUBLIC || + mgmt->u.action.category == WLAN_ACTION_PROTECTED_DUAL) && + gas_query_rx(wpa_s->gas, mgmt->da, mgmt->sa, mgmt->bssid, + mgmt->u.action.category, + payload, plen, freq) == 0) + return; +#endif /* CONFIG_GAS */ + +#ifdef CONFIG_TDLS + if (category == WLAN_ACTION_PUBLIC && plen >= 4 && + payload[0] == WLAN_TDLS_DISCOVERY_RESPONSE) { + wpa_dbg(wpa_s, MSG_DEBUG, + "TDLS: Received Discovery Response from " MACSTR, + MAC2STR(mgmt->sa)); + return; + } +#endif /* CONFIG_TDLS */ + +#ifdef CONFIG_INTERWORKING + if (category == WLAN_ACTION_QOS && plen >= 1 && + payload[0] == QOS_QOS_MAP_CONFIG) { + const u8 *pos = payload + 1; + size_t qlen = plen - 1; + wpa_dbg(wpa_s, MSG_DEBUG, "Interworking: Received QoS Map Configure frame from " + MACSTR, MAC2STR(mgmt->sa)); + if (os_memcmp(mgmt->sa, wpa_s->bssid, ETH_ALEN) == 0 && + qlen > 2 && pos[0] == WLAN_EID_QOS_MAP_SET && + pos[1] <= qlen - 2 && pos[1] >= 16) + wpas_qos_map_set(wpa_s, pos + 2, pos[1]); + return; + } +#endif /* CONFIG_INTERWORKING */ + +#ifdef CONFIG_P2P + wpas_p2p_rx_action(wpa_s, mgmt->da, mgmt->sa, mgmt->bssid, + category, payload, plen, freq); +#endif /* CONFIG_P2P */ +} + + +static void wpa_supplicant_notify_avoid_freq(struct wpa_supplicant *wpa_s, + union wpa_event_data *event) +{ +#ifdef CONFIG_P2P + struct wpa_supplicant *ifs; +#endif /* CONFIG_P2P */ + struct wpa_freq_range_list *list; + char *str = NULL; + + list = &event->freq_range; + + if (list->num) + str = freq_range_list_str(list); + wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_AVOID_FREQ "ranges=%s", + str ? str : ""); + +#ifdef CONFIG_P2P + if (freq_range_list_parse(&wpa_s->global->p2p_go_avoid_freq, str)) { + wpa_dbg(wpa_s, MSG_ERROR, "%s: Failed to parse freq range", + __func__); + } else { + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Update channel list based on frequency avoid event"); + wpas_p2p_update_channel_list(wpa_s); + } + + for (ifs = wpa_s->global->ifaces; ifs; ifs = ifs->next) { + int freq; + if (!ifs->current_ssid || + !ifs->current_ssid->p2p_group || + (ifs->current_ssid->mode != WPAS_MODE_P2P_GO && + ifs->current_ssid->mode != WPAS_MODE_P2P_GROUP_FORMATION)) + continue; + + freq = ifs->current_ssid->frequency; + if (!freq_range_list_includes(list, freq)) { + wpa_dbg(ifs, MSG_DEBUG, "P2P GO operating frequency %d MHz in safe range", + freq); + continue; + } + + wpa_dbg(ifs, MSG_DEBUG, "P2P GO operating in unsafe frequency %d MHz", + freq); + /* TODO: Consider using CSA or removing the group within + * wpa_supplicant */ + wpa_msg(ifs, MSG_INFO, P2P_EVENT_REMOVE_AND_REFORM_GROUP); + } +#endif /* CONFIG_P2P */ + + os_free(str); } @@ -1958,19 +2830,35 @@ union wpa_event_data *data) { struct wpa_supplicant *wpa_s = ctx; - u16 reason_code = 0; if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED && event != EVENT_INTERFACE_ENABLED && - event != EVENT_INTERFACE_STATUS) { + event != EVENT_INTERFACE_STATUS && + event != EVENT_SCHED_SCAN_STOPPED) { wpa_dbg(wpa_s, MSG_DEBUG, "Ignore event %s (%d) while interface is disabled", event_to_string(event), event); return; } - wpa_dbg(wpa_s, MSG_DEBUG, "Event %s (%d) received", +#ifndef CONFIG_NO_STDOUT_DEBUG +{ + int level = MSG_DEBUG; + + if (event == EVENT_RX_MGMT && data->rx_mgmt.frame_len >= 24) { + const struct ieee80211_hdr *hdr; + u16 fc; + hdr = (const struct ieee80211_hdr *) data->rx_mgmt.frame; + fc = le_to_host16(hdr->frame_control); + if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT && + WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_BEACON) + level = MSG_EXCESSIVE; + } + + wpa_dbg(wpa_s, level, "Event %s (%d) received", event_to_string(event), event); +} +#endif /* CONFIG_NO_STDOUT_DEBUG */ switch (event) { case EVENT_AUTH: @@ -1980,88 +2868,55 @@ wpa_supplicant_event_assoc(wpa_s, data); break; case EVENT_DISASSOC: - wpa_dbg(wpa_s, MSG_DEBUG, "Disassociation notification"); - if (data) { - wpa_dbg(wpa_s, MSG_DEBUG, " * reason %u", - data->disassoc_info.reason_code); - if (data->disassoc_info.addr) - wpa_dbg(wpa_s, MSG_DEBUG, " * address " MACSTR, - MAC2STR(data->disassoc_info.addr)); - } -#ifdef CONFIG_AP - if (wpa_s->ap_iface && data && data->disassoc_info.addr) { - hostapd_notif_disassoc(wpa_s->ap_iface->bss[0], - data->disassoc_info.addr); - break; - } - if (wpa_s->ap_iface) { - wpa_dbg(wpa_s, MSG_DEBUG, "Ignore disassoc event in " - "AP mode"); - break; - } -#endif /* CONFIG_AP */ - if (data) { - reason_code = data->disassoc_info.reason_code; - wpa_hexdump(MSG_DEBUG, "Disassociation frame IE(s)", - data->disassoc_info.ie, - data->disassoc_info.ie_len); -#ifdef CONFIG_P2P - wpas_p2p_disassoc_notif( - wpa_s, data->disassoc_info.addr, reason_code, - data->disassoc_info.ie, - data->disassoc_info.ie_len); -#endif /* CONFIG_P2P */ - } - if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) - sme_event_disassoc(wpa_s, data); - /* fall through */ + wpas_event_disassoc(wpa_s, + data ? &data->disassoc_info : NULL); + break; case EVENT_DEAUTH: - if (event == EVENT_DEAUTH) { - wpa_dbg(wpa_s, MSG_DEBUG, - "Deauthentication notification"); - if (data) { - reason_code = data->deauth_info.reason_code; - wpa_dbg(wpa_s, MSG_DEBUG, " * reason %u", - data->deauth_info.reason_code); - if (data->deauth_info.addr) { - wpa_dbg(wpa_s, MSG_DEBUG, " * address " - MACSTR, - MAC2STR(data->deauth_info. - addr)); - } - wpa_hexdump(MSG_DEBUG, - "Deauthentication frame IE(s)", - data->deauth_info.ie, - data->deauth_info.ie_len); -#ifdef CONFIG_P2P - wpas_p2p_deauth_notif( - wpa_s, data->deauth_info.addr, - reason_code, - data->deauth_info.ie, - data->deauth_info.ie_len); -#endif /* CONFIG_P2P */ - } - } -#ifdef CONFIG_AP - if (wpa_s->ap_iface && data && data->deauth_info.addr) { - hostapd_notif_disassoc(wpa_s->ap_iface->bss[0], - data->deauth_info.addr); - break; - } - if (wpa_s->ap_iface) { - wpa_dbg(wpa_s, MSG_DEBUG, "Ignore deauth event in " - "AP mode"); - break; - } -#endif /* CONFIG_AP */ - wpa_supplicant_event_disassoc(wpa_s, reason_code); + wpas_event_deauth(wpa_s, + data ? &data->deauth_info : NULL); break; case EVENT_MICHAEL_MIC_FAILURE: wpa_supplicant_event_michael_mic_failure(wpa_s, data); break; #ifndef CONFIG_NO_SCAN_PROCESSING + case EVENT_SCAN_STARTED: + os_get_reltime(&wpa_s->scan_start_time); + if (wpa_s->own_scan_requested) { + struct os_reltime diff; + + os_reltime_sub(&wpa_s->scan_start_time, + &wpa_s->scan_trigger_time, &diff); + wpa_dbg(wpa_s, MSG_DEBUG, "Own scan request started a scan in %ld.%06ld seconds", + diff.sec, diff.usec); + wpa_s->own_scan_requested = 0; + wpa_s->own_scan_running = 1; + if (wpa_s->last_scan_req == MANUAL_SCAN_REQ && + wpa_s->manual_scan_use_id) { + wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_SCAN_STARTED "id=%u", + wpa_s->manual_scan_id); + } else { + wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_SCAN_STARTED); + } + } else { + wpa_dbg(wpa_s, MSG_DEBUG, "External program started a scan"); + wpa_s->external_scan_running = 1; + wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_SCAN_STARTED); + } + break; case EVENT_SCAN_RESULTS: + if (os_reltime_initialized(&wpa_s->scan_start_time)) { + struct os_reltime now, diff; + os_get_reltime(&now); + os_reltime_sub(&now, &wpa_s->scan_start_time, &diff); + wpa_s->scan_start_time.sec = 0; + wpa_s->scan_start_time.usec = 0; + wpa_dbg(wpa_s, MSG_DEBUG, "Scan completed in %ld.%06ld seconds", + diff.sec, diff.usec); + } wpa_supplicant_event_scan_results(wpa_s, data); + wpa_s->own_scan_running = 0; + wpa_s->external_scan_running = 0; + radio_work_check_next(wpa_s); break; #endif /* CONFIG_NO_SCAN_PROCESSING */ case EVENT_ASSOCINFO: @@ -2083,6 +2938,11 @@ wpa_supplicant_event_tdls(wpa_s, data); break; #endif /* CONFIG_TDLS */ +#ifdef CONFIG_WNM + case EVENT_WNM: + wpa_supplicant_event_wnm(wpa_s, data); + break; +#endif /* CONFIG_WNM */ #ifdef CONFIG_IEEE80211R case EVENT_FT_RESPONSE: wpa_supplicant_event_ft_response(wpa_s, data); @@ -2105,6 +2965,13 @@ data->assoc_reject.status_code); if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) sme_event_assoc_reject(wpa_s, data); + else { + const u8 *bssid = data->assoc_reject.bssid; + if (bssid == NULL || is_zero_ether_addr(bssid)) + bssid = wpa_s->pending_bssid; + wpas_connection_failed(wpa_s, bssid); + wpa_supplicant_mark_disassoc(wpa_s); + } break; case EVENT_AUTH_TIMED_OUT: if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) @@ -2174,6 +3041,12 @@ #endif /* CONFIG_AP */ break; #ifdef CONFIG_AP + case EVENT_EAPOL_TX_STATUS: + ap_eapol_tx_status(wpa_s, data->eapol_tx_status.dst, + data->eapol_tx_status.data, + data->eapol_tx_status.data_len, + data->eapol_tx_status.ack); + break; case EVENT_DRIVER_CLIENT_POLL_OK: ap_client_poll_ok(wpa_s, data->client_poll.addr); break; @@ -2183,15 +3056,36 @@ ap_rx_from_unknown_sta(wpa_s, data->rx_from_unknown.addr, data->rx_from_unknown.wds); break; - case EVENT_RX_MGMT: + case EVENT_CH_SWITCH: + if (!data) + break; + if (!wpa_s->ap_iface) { + wpa_dbg(wpa_s, MSG_DEBUG, "AP: Ignore channel switch " + "event in non-AP mode"); + break; + } + + wpas_ap_ch_switch(wpa_s, data->ch_switch.freq, + data->ch_switch.ht_enabled, + data->ch_switch.ch_offset, + data->ch_switch.ch_width, + data->ch_switch.cf1, + data->ch_switch.cf2); + break; +#endif /* CONFIG_AP */ + case EVENT_RX_MGMT: { + u16 fc, stype; + const struct ieee80211_mgmt *mgmt; + + mgmt = (const struct ieee80211_mgmt *) + data->rx_mgmt.frame; + fc = le_to_host16(mgmt->frame_control); + stype = WLAN_FC_GET_STYPE(fc); + +#ifdef CONFIG_AP if (wpa_s->ap_iface == NULL) { +#endif /* CONFIG_AP */ #ifdef CONFIG_P2P - u16 fc, stype; - const struct ieee80211_mgmt *mgmt; - mgmt = (const struct ieee80211_mgmt *) - data->rx_mgmt.frame; - fc = le_to_host16(mgmt->frame_control); - stype = WLAN_FC_GET_STYPE(fc); if (stype == WLAN_FC_STYPE_PROBE_REQ && data->rx_mgmt.frame_len > 24) { const u8 *src = mgmt->sa; @@ -2199,72 +3093,50 @@ size_t ie_len = data->rx_mgmt.frame_len - (mgmt->u.probe_req.variable - data->rx_mgmt.frame); - wpas_p2p_probe_req_rx(wpa_s, src, mgmt->da, - mgmt->bssid, ie, ie_len); + wpas_p2p_probe_req_rx( + wpa_s, src, mgmt->da, + mgmt->bssid, ie, ie_len, + data->rx_mgmt.ssi_signal); break; } #endif /* CONFIG_P2P */ +#ifdef CONFIG_IBSS_RSN + if (stype == WLAN_FC_STYPE_AUTH && + data->rx_mgmt.frame_len >= 30) { + wpa_supplicant_event_ibss_auth(wpa_s, data); + break; + } +#endif /* CONFIG_IBSS_RSN */ + + if (stype == WLAN_FC_STYPE_ACTION) { + wpas_event_rx_mgmt_action( + wpa_s, mgmt, data->rx_mgmt.frame_len, + data->rx_mgmt.freq); + break; + } + wpa_dbg(wpa_s, MSG_DEBUG, "AP: ignore received " "management frame in non-AP mode"); break; +#ifdef CONFIG_AP + } + + if (stype == WLAN_FC_STYPE_PROBE_REQ && + data->rx_mgmt.frame_len > 24) { + const u8 *ie = mgmt->u.probe_req.variable; + size_t ie_len = data->rx_mgmt.frame_len - + (mgmt->u.probe_req.variable - + data->rx_mgmt.frame); + + wpas_notify_preq(wpa_s, mgmt->sa, mgmt->da, + mgmt->bssid, ie, ie_len, + data->rx_mgmt.ssi_signal); } + ap_mgmt_rx(wpa_s, &data->rx_mgmt); - break; #endif /* CONFIG_AP */ - case EVENT_RX_ACTION: - wpa_dbg(wpa_s, MSG_DEBUG, "Received Action frame: SA=" MACSTR - " Category=%u DataLen=%d freq=%d MHz", - MAC2STR(data->rx_action.sa), - data->rx_action.category, (int) data->rx_action.len, - data->rx_action.freq); -#ifdef CONFIG_IEEE80211R - if (data->rx_action.category == WLAN_ACTION_FT) { - ft_rx_action(wpa_s, data->rx_action.data, - data->rx_action.len); - break; - } -#endif /* CONFIG_IEEE80211R */ -#ifdef CONFIG_IEEE80211W -#ifdef CONFIG_SME - if (data->rx_action.category == WLAN_ACTION_SA_QUERY) { - sme_sa_query_rx(wpa_s, data->rx_action.sa, - data->rx_action.data, - data->rx_action.len); - break; - } -#endif /* CONFIG_SME */ -#endif /* CONFIG_IEEE80211W */ -#ifdef CONFIG_GAS - if (data->rx_action.category == WLAN_ACTION_PUBLIC && - gas_query_rx(wpa_s->gas, data->rx_action.da, - data->rx_action.sa, data->rx_action.bssid, - data->rx_action.data, data->rx_action.len, - data->rx_action.freq) == 0) - break; -#endif /* CONFIG_GAS */ - if (data->rx_action.category == WLAN_ACTION_WNM) { - wnm_action_rx(wpa_s, &data->rx_action); - break; - } -#ifdef CONFIG_TDLS - if (data->rx_action.category == WLAN_ACTION_PUBLIC && - data->rx_action.len >= 4 && - data->rx_action.data[0] == WLAN_TDLS_DISCOVERY_RESPONSE) { - wpa_dbg(wpa_s, MSG_DEBUG, "TDLS: Received Discovery " - "Response from " MACSTR, - MAC2STR(data->rx_action.sa)); - break; - } -#endif /* CONFIG_TDLS */ -#ifdef CONFIG_P2P - wpas_p2p_rx_action(wpa_s, data->rx_action.da, - data->rx_action.sa, - data->rx_action.bssid, - data->rx_action.category, - data->rx_action.data, - data->rx_action.len, data->rx_action.freq); -#endif /* CONFIG_P2P */ break; + } case EVENT_RX_PROBE_REQ: if (data->rx_probe_req.sa == NULL || data->rx_probe_req.ie == NULL) @@ -2276,7 +3148,8 @@ data->rx_probe_req.da, data->rx_probe_req.bssid, data->rx_probe_req.ie, - data->rx_probe_req.ie_len); + data->rx_probe_req.ie_len, + data->rx_probe_req.ssi_signal); break; } #endif /* CONFIG_AP */ @@ -2285,7 +3158,8 @@ data->rx_probe_req.da, data->rx_probe_req.bssid, data->rx_probe_req.ie, - data->rx_probe_req.ie_len); + data->rx_probe_req.ie_len, + data->rx_probe_req.ssi_signal); #endif /* CONFIG_P2P */ break; case EVENT_REMAIN_ON_CHANNEL: @@ -2310,71 +3184,6 @@ wpa_s, data->remain_on_channel.freq); #endif /* CONFIG_P2P */ break; -#ifdef CONFIG_P2P - case EVENT_P2P_DEV_FOUND: { - struct p2p_peer_info peer_info; - - os_memset(&peer_info, 0, sizeof(peer_info)); - if (data->p2p_dev_found.dev_addr) - os_memcpy(peer_info.p2p_device_addr, - data->p2p_dev_found.dev_addr, ETH_ALEN); - if (data->p2p_dev_found.pri_dev_type) - os_memcpy(peer_info.pri_dev_type, - data->p2p_dev_found.pri_dev_type, - sizeof(peer_info.pri_dev_type)); - if (data->p2p_dev_found.dev_name) - os_strlcpy(peer_info.device_name, - data->p2p_dev_found.dev_name, - sizeof(peer_info.device_name)); - peer_info.config_methods = data->p2p_dev_found.config_methods; - peer_info.dev_capab = data->p2p_dev_found.dev_capab; - peer_info.group_capab = data->p2p_dev_found.group_capab; - - /* - * FIX: new_device=1 is not necessarily correct. We should - * maintain a P2P peer database in wpa_supplicant and update - * this information based on whether the peer is truly new. - */ - wpas_dev_found(wpa_s, data->p2p_dev_found.addr, &peer_info, 1); - break; - } - case EVENT_P2P_GO_NEG_REQ_RX: - wpas_go_neg_req_rx(wpa_s, data->p2p_go_neg_req_rx.src, - data->p2p_go_neg_req_rx.dev_passwd_id); - break; - case EVENT_P2P_GO_NEG_COMPLETED: - wpas_go_neg_completed(wpa_s, data->p2p_go_neg_completed.res); - break; - case EVENT_P2P_PROV_DISC_REQUEST: - wpas_prov_disc_req(wpa_s, data->p2p_prov_disc_req.peer, - data->p2p_prov_disc_req.config_methods, - data->p2p_prov_disc_req.dev_addr, - data->p2p_prov_disc_req.pri_dev_type, - data->p2p_prov_disc_req.dev_name, - data->p2p_prov_disc_req.supp_config_methods, - data->p2p_prov_disc_req.dev_capab, - data->p2p_prov_disc_req.group_capab, - NULL, 0); - break; - case EVENT_P2P_PROV_DISC_RESPONSE: - wpas_prov_disc_resp(wpa_s, data->p2p_prov_disc_resp.peer, - data->p2p_prov_disc_resp.config_methods); - break; - case EVENT_P2P_SD_REQUEST: - wpas_sd_request(wpa_s, data->p2p_sd_req.freq, - data->p2p_sd_req.sa, - data->p2p_sd_req.dialog_token, - data->p2p_sd_req.update_indic, - data->p2p_sd_req.tlvs, - data->p2p_sd_req.tlvs_len); - break; - case EVENT_P2P_SD_RESPONSE: - wpas_sd_response(wpa_s, data->p2p_sd_resp.sa, - data->p2p_sd_resp.update_indic, - data->p2p_sd_resp.tlvs, - data->p2p_sd_resp.tlvs_len); - break; -#endif /* CONFIG_P2P */ case EVENT_EAPOL_RX: wpa_supplicant_rx_eapol(wpa_s, data->eapol_rx.src, data->eapol_rx.data, @@ -2407,20 +3216,25 @@ break; case EVENT_INTERFACE_DISABLED: wpa_dbg(wpa_s, MSG_DEBUG, "Interface was disabled"); +#ifdef CONFIG_P2P + if (wpa_s->p2p_group_interface == P2P_GROUP_INTERFACE_GO || + (wpa_s->current_ssid && wpa_s->current_ssid->p2p_group && + wpa_s->current_ssid->mode == WPAS_MODE_P2P_GO)) { + /* + * The interface was externally disabled. Remove + * it assuming an external entity will start a + * new session if needed. + */ + wpas_p2p_disconnect(wpa_s); + break; + } +#endif /* CONFIG_P2P */ + wpa_supplicant_mark_disassoc(wpa_s); wpa_supplicant_set_state(wpa_s, WPA_INTERFACE_DISABLED); break; case EVENT_CHANNEL_LIST_CHANGED: - if (wpa_s->drv_priv == NULL) - break; /* Ignore event during drv initialization */ - - free_hw_features(wpa_s); - wpa_s->hw.modes = wpa_drv_get_hw_feature_data( - wpa_s, &wpa_s->hw.num_modes, &wpa_s->hw.flags); - -#ifdef CONFIG_P2P - wpas_p2p_update_channel_list(wpa_s); -#endif /* CONFIG_P2P */ + wpa_supplicant_update_channel_list(wpa_s); break; case EVENT_INTERFACE_UNAVAILABLE: #ifdef CONFIG_P2P @@ -2475,21 +3289,47 @@ data->driver_gtk_rekey.replay_ctr); break; case EVENT_SCHED_SCAN_STOPPED: + wpa_s->pno = 0; wpa_s->sched_scanning = 0; wpa_supplicant_notify_scanning(wpa_s, 0); + if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) + break; + /* - * If we timed out, start a new sched scan to continue - * searching for more SSIDs. + * Start a new sched scan to continue searching for more SSIDs + * either if timed out or PNO schedule scan is pending. */ - if (wpa_s->sched_scan_timed_out) - wpa_supplicant_req_sched_scan(wpa_s); + if (wpa_s->sched_scan_timed_out || wpa_s->pno_sched_pending) { + + if (wpa_supplicant_req_sched_scan(wpa_s) < 0 && + wpa_s->pno_sched_pending) { + wpa_msg(wpa_s, MSG_ERROR, "Failed to schedule PNO"); + } else if (wpa_s->pno_sched_pending) { + wpa_s->pno_sched_pending = 0; + wpa_s->pno = 1; + } + } + break; case EVENT_WPS_BUTTON_PUSHED: #ifdef CONFIG_WPS wpas_wps_start_pbc(wpa_s, NULL, 0); #endif /* CONFIG_WPS */ break; + case EVENT_AVOID_FREQUENCIES: + wpa_supplicant_notify_avoid_freq(wpa_s, data); + break; + case EVENT_CONNECT_FAILED_REASON: +#ifdef CONFIG_AP + if (!wpa_s->ap_iface || !data) + break; + hostapd_event_connect_failed_reason( + wpa_s->ap_iface->bss[0], + data->connect_failed_reason.addr, + data->connect_failed_reason.code); +#endif /* CONFIG_AP */ + break; default: wpa_msg(wpa_s, MSG_INFO, "Unknown event %d", event); break; diff -Nru wpa-1.0/wpa_supplicant/examples/dbus-listen-preq.py wpa-2.1/wpa_supplicant/examples/dbus-listen-preq.py --- wpa-1.0/wpa_supplicant/examples/dbus-listen-preq.py 1970-01-01 00:00:00.000000000 +0000 +++ wpa-2.1/wpa_supplicant/examples/dbus-listen-preq.py 2014-02-04 11:23:35.000000000 +0000 @@ -0,0 +1,62 @@ +#!/usr/bin/python + +import dbus +import sys +import time +import gobject +from dbus.mainloop.glib import DBusGMainLoop + +WPAS_DBUS_SERVICE = "fi.w1.wpa_supplicant1" +WPAS_DBUS_INTERFACE = "fi.w1.wpa_supplicant1" +WPAS_DBUS_OPATH = "/fi/w1/wpa_supplicant1" +WPAS_DBUS_INTERFACES_INTERFACE = "fi.w1.wpa_supplicant1.Interface" + +def usage(): + print "Usage: %s " % sys.argv[0] + print "Press Ctrl-C to stop" + +def ProbeRequest(args): + if 'addr' in args: + print '%.2x:%.2x:%.2x:%.2x:%.2x:%.2x' % tuple(args['addr']), + if 'dst' in args: + print '-> %.2x:%.2x:%.2x:%.2x:%.2x:%.2x' % tuple(args['dst']), + if 'bssid' in args: + print '(bssid %.2x:%.2x:%.2x:%.2x:%.2x:%.2x)' % tuple(args['dst']), + if 'signal' in args: + print 'signal:%d' % args['signal'], + if 'ies' in args: + print 'have IEs (%d bytes)' % len(args['ies']), + print '' + +if __name__ == "__main__": + global bus + global wpas_obj + global if_obj + global p2p_iface + + dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) + + bus = dbus.SystemBus() + wpas_obj = bus.get_object(WPAS_DBUS_SERVICE, WPAS_DBUS_OPATH) + + # Print list of i/f if no one is specified + if (len(sys.argv) < 2) : + usage() + sys.exit(0) + + wpas = dbus.Interface(wpas_obj, WPAS_DBUS_INTERFACE) + + ifname = sys.argv[1] + + path = wpas.GetInterface(ifname) + + if_obj = bus.get_object(WPAS_DBUS_SERVICE, path) + iface = dbus.Interface(if_obj, WPAS_DBUS_INTERFACES_INTERFACE) + + bus.add_signal_receiver(ProbeRequest, + dbus_interface=WPAS_DBUS_INTERFACES_INTERFACE, + signal_name="ProbeRequest") + + iface.SubscribeProbeReq() + + gobject.MainLoop().run() diff -Nru wpa-1.0/wpa_supplicant/examples/p2p/p2p_connect.py wpa-2.1/wpa_supplicant/examples/p2p/p2p_connect.py --- wpa-1.0/wpa_supplicant/examples/p2p/p2p_connect.py 1970-01-01 00:00:00.000000000 +0000 +++ wpa-2.1/wpa_supplicant/examples/p2p/p2p_connect.py 2014-02-04 11:23:35.000000000 +0000 @@ -0,0 +1,299 @@ +#!/usr/bin/python +# Tests p2p_connect +# Will try to connect to another peer +# and form a group +######### MAY NEED TO RUN AS SUDO ############# + +import dbus +import sys, os +import time +import gobject +import getopt +from dbus.mainloop.glib import DBusGMainLoop + + +def usage(): + print "Usage:" + print " %s -i -m \ " \ + % sys.argv[0] + print " -a [-p ] [-g ] \ " + print " [-w ]" + print "Options:" + print " -i = interface name" + print " -m = wps method" + print " -a = peer address" + print " -p = pin number (8 digits)" + print " -g = group owner intent" + print " -w = wpas dbus interface = fi.w1.wpa_supplicant1" + print "Example:" + print " %s -i wlan0 -a 0015008352c0 -m display -p 12345670" % sys.argv[0] + + +# Required Signals +def GONegotiationSuccess(status): + print "Go Negotiation Success" + +def GONegotiationFailure(status): + print 'Go Negotiation Failed. Status:' + print format(status) + os._exit(0) + +def GroupStarted(properties): + if properties.has_key("group_object"): + print 'Group Formation Complete %s' \ + % properties["group_object"] + os._exit(0) + +def WpsFailure(status, etc): + print "WPS Authentication Failure".format(status) + print etc + os._exit(0) + +class P2P_Connect(): + # Needed Variables + global bus + global wpas_object + global interface_object + global p2p_interface + global ifname + global wpas + global wpas_dbus_interface + global timeout + global path + global wps_method + global go_intent + global addr + global pin + + # Dbus Paths + global wpas_dbus_opath + global wpas_dbus_interfaces_opath + global wpas_dbus_interfaces_interface + global wpas_dbus_interfaces_p2pdevice + + # Dictionary of Arguements + global p2p_connect_arguements + + # Constructor + def __init__(self,ifname,wpas_dbus_interface,addr, + pin,wps_method,go_intent): + # Initializes variables and threads + self.ifname = ifname + self.wpas_dbus_interface = wpas_dbus_interface + self.wps_method = wps_method + self.go_intent = go_intent + self.addr = addr + self.pin = pin + + # Generating interface/object paths + self.wpas_dbus_opath = \ + "/" + self.wpas_dbus_interface.replace(".","/") + self.wpas_wpas_dbus_interfaces_opath = \ + self.wpas_dbus_opath + "/Interfaces" + self.wpas_dbus_interfaces_interface = \ + self.wpas_dbus_interface + ".Interface" + self.wpas_dbus_interfaces_p2pdevice = \ + self.wpas_dbus_interfaces_interface + ".P2PDevice" + + # Getting interfaces and objects + DBusGMainLoop(set_as_default=True) + self.bus = dbus.SystemBus() + self.wpas_object = self.bus.get_object( + self.wpas_dbus_interface, + self.wpas_dbus_opath) + self.wpas = dbus.Interface( + self.wpas_object, self.wpas_dbus_interface) + + # See if wpa_supplicant already knows about this interface + self.path = None + try: + self.path = self.wpas.GetInterface(ifname) + except: + if not str(exc).startswith( + self.wpas_dbus_interface + \ + ".InterfaceUnknown:"): + raise exc + try: + path = self.wpas.CreateInterface( + {'Ifname': ifname, 'Driver': 'test'}) + time.sleep(1) + + except dbus.DBusException, exc: + if not str(exc).startswith( + self.wpas_dbus_interface + \ + ".InterfaceExists:"): + raise exc + + # Get Interface and objects + self.interface_object = self.bus.get_object( + self.wpas_dbus_interface,self.path) + self.p2p_interface = dbus.Interface( + self.interface_object, + self.wpas_dbus_interfaces_p2pdevice) + + # Add signals + self.bus.add_signal_receiver(GONegotiationSuccess, + dbus_interface=self.wpas_dbus_interfaces_p2pdevice, + signal_name="GONegotiationSuccess") + self.bus.add_signal_receiver(GONegotiationFailure, + dbus_interface=self.wpas_dbus_interfaces_p2pdevice, + signal_name="GONegotiationFailure") + self.bus.add_signal_receiver(GroupStarted, + dbus_interface=self.wpas_dbus_interfaces_p2pdevice, + signal_name="GroupStarted") + self.bus.add_signal_receiver(WpsFailure, + dbus_interface=self.wpas_dbus_interfaces_p2pdevice, + signal_name="WpsFailed") + + + #Constructing all the arguements needed to connect + def constructArguements(self): + # Adding required arguements + self.p2p_connect_arguements = {'wps_method':self.wps_method, + 'peer':dbus.ObjectPath(self.path+'/Peers/'+self.addr)} + + # Display requires a pin, and a go intent of 15 + if (self.wps_method == 'display'): + if (self.pin != None): + self.p2p_connect_arguements.update({'pin':self.pin}) + else: + print "Error:\n Pin required for wps_method=display" + usage() + quit() + + if (self.go_intent != None and int(self.go_intent) != 15): + print "go_intent overwritten to 15" + + self.go_intent = '15' + + # Keypad requires a pin, and a go intent of less than 15 + elif (self.wps_method == 'keypad'): + if (self.pin != None): + self.p2p_connect_arguements.update({'pin':self.pin}) + else: + print "Error:\n Pin required for wps_method=keypad" + usage() + quit() + + if (self.go_intent != None and int(self.go_intent) == 15): + error = "Error :\n Group Owner intent cannot be" + \ + " 15 for wps_method=keypad" + print error + usage() + quit() + + # Doesn't require pin + # for ./wpa_cli, p2p_connect [mac] [pin#], wps_method=keypad + elif (self.wps_method == 'pin'): + if (self.pin != None): + print "pin ignored" + + # No pin is required for pbc so it is ignored + elif (self.wps_method == 'pbc'): + if (self.pin != None): + print "pin ignored" + + else: + print "Error:\n wps_method not supported or does not exist" + usage() + quit() + + # Go_intent is optional for all arguements + if (self.go_intent != None): + self.p2p_connect_arguements.update( + {'go_intent':dbus.Int32(self.go_intent)}) + + # Running p2p_connect + def run(self): + try: + result_pin = self.p2p_interface.Connect( + self.p2p_connect_arguements) + + except dbus.DBusException, exc: + raise exc + + if (self.wps_method == 'pin' and \ + not self.p2p_connect_arguements.has_key('pin') ): + print "Connect return with pin value of %d " % int(result_pin) + gobject.MainLoop().run() + +if __name__ == "__main__": + + # Required + interface_name = None + wps_method = None + addr = None + + # Conditionally optional + pin = None + + # Optional + wpas_dbus_interface = 'fi.w1.wpa_supplicant1' + go_intent = None + + # Using getopts to handle options + try: + options, args = getopt.getopt(sys.argv[1:],"hi:m:a:p:g:w:") + + except getopt.GetoptError: + usage() + quit() + + # If theres a switch, override default option + for key, value in options: + # Help + if (key == "-h"): + usage() + quit() + # Interface Name + elif (key == "-i"): + interface_name = value + # WPS Method + elif (key == "-m"): + wps_method = value + # Address + elif (key == "-a"): + addr = value + # Pin + elif (key == "-p"): + pin = value + # Group Owner Intent + elif (key == "-g"): + go_intent = value + # Dbus interface + elif (key == "-w"): + wpas_dbus_interface = value + else: + assert False, "unhandled option" + + # Required Arguements check + if (interface_name == None or wps_method == None or addr == None): + print "Error:\n Required arguements not specified" + usage() + quit() + + # Group Owner Intent Check + if (go_intent != None and (int(go_intent) > 15 or int(go_intent) < 0) ): + print "Error:\n Group Owner Intent must be between 0 and 15 inclusive" + usage() + quit() + + # Pin Check + if (pin != None and len(pin) != 8): + print "Error:\n Pin is not 8 digits" + usage() + quit() + + try: + p2p_connect_test = P2P_Connect(interface_name,wpas_dbus_interface, + addr,pin,wps_method,go_intent) + + except: + print "Error:\n Invalid Arguements" + usage() + quit() + + p2p_connect_test.constructArguements() + p2p_connect_test.run() + + os._exit(0) diff -Nru wpa-1.0/wpa_supplicant/examples/p2p/p2p_disconnect.py wpa-2.1/wpa_supplicant/examples/p2p/p2p_disconnect.py --- wpa-1.0/wpa_supplicant/examples/p2p/p2p_disconnect.py 1970-01-01 00:00:00.000000000 +0000 +++ wpa-2.1/wpa_supplicant/examples/p2p/p2p_disconnect.py 2014-02-04 11:23:35.000000000 +0000 @@ -0,0 +1,169 @@ +#!/usr/bin/python +# Tests P2P_Disconnect +# Will perform disconnect on interface_name +######### MAY NEED TO RUN AS SUDO ############# + +import dbus +import sys, os +import time +import gobject +import threading +import getopt +from dbus.mainloop.glib import DBusGMainLoop + +def usage(): + print "Usage:" + print " %s -i \ " \ + % sys.argv[0] + print " [-w ]" + print "Options:" + print " -i = interface name" + print " -w = wpas dbus interface = fi.w1.wpa_supplicant1" + print "Example:" + print " %s -i p2p-wlan0-0" % sys.argv[0] + +# Required Signals +def GroupFinished(status, etc): + print "Disconnected" + os._exit(0) + +class P2P_Disconnect (threading.Thread): + # Needed Variables + global bus + global wpas_object + global interface_object + global p2p_interface + global interface_name + global wpas + global wpas_dbus_interface + global path + global timeout + + # Dbus Paths + global wpas_dbus_opath + global wpas_dbus_interfaces_opath + global wpas_dbus_interfaces_interface + global wpas_dbus_interfaces_p2pdevice + + # Constructor + def __init__(self,interface_name,wpas_dbus_interface,timeout): + # Initializes variables and threads + self.interface_name = interface_name + self.wpas_dbus_interface = wpas_dbus_interface + self.timeout = timeout + + # Initializes thread and daemon allows for ctrl-c kill + threading.Thread.__init__(self) + self.daemon = True + + # Generating interface/object paths + self.wpas_dbus_opath = "/" + \ + self.wpas_dbus_interface.replace(".","/") + self.wpas_wpas_dbus_interfaces_opath = self.wpas_dbus_opath + \ + "/Interfaces" + self.wpas_dbus_interfaces_interface = \ + self.wpas_dbus_interface + ".Interface" + self.wpas_dbus_interfaces_p2pdevice = \ + self.wpas_dbus_interfaces_interface \ + + ".P2PDevice" + + # Getting interfaces and objects + DBusGMainLoop(set_as_default=True) + self.bus = dbus.SystemBus() + self.wpas_object = self.bus.get_object( + self.wpas_dbus_interface, + self.wpas_dbus_opath) + self.wpas = dbus.Interface(self.wpas_object, + self.wpas_dbus_interface) + + # Try to see if supplicant knows about interface + # If not, throw an exception + try: + self.path = self.wpas.GetInterface( + self.interface_name) + except dbus.DBusException, exc: + error = 'Error:\n Interface ' + self.interface_name \ + + ' was not found' + print error + usage() + os._exit(0) + + self.interface_object = self.bus.get_object( + self.wpas_dbus_interface, self.path) + self.p2p_interface = dbus.Interface(self.interface_object, + self.wpas_dbus_interfaces_p2pdevice) + + # Signals + self.bus.add_signal_receiver(GroupFinished, + dbus_interface=self.wpas_dbus_interfaces_p2pdevice, + signal_name="GroupFinished") + + # Runs p2p_disconnect + def run(self): + # Allows other threads to keep working while MainLoop runs + # Required for timeout implementation + gobject.MainLoop().get_context().iteration(True) + gobject.threads_init() + self.p2p_interface.Disconnect() + gobject.MainLoop().run() + + +if __name__ == "__main__": + + timeout = 5 + # Defaults for optional inputs + wpas_dbus_interface = 'fi.w1.wpa_supplicant1' + + # interface_name is required + interface_name = None + + # Using getopts to handle options + try: + options, args = getopt.getopt(sys.argv[1:],"hi:w:") + + except getopt.GetoptError: + usage() + quit() + + # If theres a switch, override default option + for key, value in options: + # Help + if (key == "-h"): + usage() + quit() + # Interface Name + elif (key == "-i"): + interface_name = value + # Dbus interface + elif (key == "-w"): + wpas_dbus_interface = value + else: + assert False, "unhandled option" + + # Interface name is required and was not given + if (interface_name == None): + print "Error:\n interface_name is required" + usage() + quit() + + # Constructor + try: + p2p_disconnect_test = P2P_Disconnect(interface_name, + wpas_dbus_interface,timeout) + + except: + print "Error:\n Invalid wpas_dbus_interface" + usage() + quit() + + # Start P2P_Disconnect + p2p_disconnect_test.start() + + try: + time.sleep(int(p2p_disconnect_test.timeout)) + + except: + pass + + print "Disconnect timed out" + quit() diff -Nru wpa-1.0/wpa_supplicant/examples/p2p/p2p_find.py wpa-2.1/wpa_supplicant/examples/p2p/p2p_find.py --- wpa-1.0/wpa_supplicant/examples/p2p/p2p_find.py 1970-01-01 00:00:00.000000000 +0000 +++ wpa-2.1/wpa_supplicant/examples/p2p/p2p_find.py 2014-02-04 11:23:35.000000000 +0000 @@ -0,0 +1,192 @@ +#!/usr/bin/python +# Tests p2p_find +# Will list all devices found/lost within a time frame (timeout) +# Then Program will exit +######### MAY NEED TO RUN AS SUDO ############# + +import dbus +import sys, os +import time +import gobject +import threading +import getopt +from dbus.mainloop.glib import DBusGMainLoop + +def usage(): + print "Usage:" + print " %s -i [-t ] \ " \ + % sys.argv[0] + print " [-w ]" + print "Options:" + print " -i = interface name" + print " -t = timeout = 0s (infinite)" + print " -w = wpas dbus interface = fi.w1.wpa_supplicant1" + print "Example:" + print " %s -i wlan0 -t 10" % sys.argv[0] + +# Required Signals +def deviceFound(devicepath): + print "Device found: %s" % (devicepath) + +def deviceLost(devicepath): + print "Device lost: %s" % (devicepath) + +class P2P_Find (threading.Thread): + # Needed Variables + global bus + global wpas_object + global interface_object + global p2p_interface + global interface_name + global wpas + global wpas_dbus_interface + global timeout + global path + + # Dbus Paths + global wpas_dbus_opath + global wpas_dbus_interfaces_opath + global wpas_dbus_interfaces_interface + global wpas_dbus_interfaces_p2pdevice + + # Constructor + def __init__(self,interface_name,wpas_dbus_interface,timeout): + # Initializes variables and threads + self.timeout = int(timeout) + self.interface_name = interface_name + self.wpas_dbus_interface = wpas_dbus_interface + + # Initializes thread and daemon allows for ctrl-c kill + threading.Thread.__init__(self) + self.daemon = True + + # Generating interface/object paths + self.wpas_dbus_opath = "/" + \ + self.wpas_dbus_interface.replace(".","/") + self.wpas_wpas_dbus_interfaces_opath = self.wpas_dbus_opath + \ + "/Interfaces" + self.wpas_dbus_interfaces_interface = \ + self.wpas_dbus_interface + ".Interface" + self.wpas_dbus_interfaces_p2pdevice = \ + self.wpas_dbus_interfaces_interface \ + + ".P2PDevice" + + # Getting interfaces and objects + DBusGMainLoop(set_as_default=True) + self.bus = dbus.SystemBus() + self.wpas_object = self.bus.get_object( + self.wpas_dbus_interface, + self.wpas_dbus_opath) + self.wpas = dbus.Interface(self.wpas_object, + self.wpas_dbus_interface) + + # Try to see if supplicant knows about interface + # If not, throw an exception + try: + self.path = self.wpas.GetInterface( + self.interface_name) + except dbus.DBusException, exc: + error = 'Error:\n Interface ' + self.interface_name \ + + ' was not found' + print error + usage() + os._exit(0) + + self.interface_object = self.bus.get_object( + self.wpas_dbus_interface, self.path) + self.p2p_interface = dbus.Interface(self.interface_object, + self.wpas_dbus_interfaces_p2pdevice) + + #Adds listeners for find and lost + self.bus.add_signal_receiver(deviceFound, + dbus_interface=self.wpas_dbus_interfaces_p2pdevice, + signal_name="DeviceFound") + self.bus.add_signal_receiver(deviceLost, + dbus_interface=self.wpas_dbus_interfaces_p2pdevice, + signal_name="DeviceLost") + + + # Sets up p2p_find + P2PFindDict = dbus.Dictionary( + {'Timeout':int(self.timeout)}) + self.p2p_interface.Find(P2PFindDict) + + # Run p2p_find + def run(self): + # Allows other threads to keep working while MainLoop runs + # Required for timeout implementation + gobject.MainLoop().get_context().iteration(True) + gobject.threads_init() + gobject.MainLoop().run() + +if __name__ == "__main__": + + # Defaults for optional inputs + timeout = 0 + wpas_dbus_interface = 'fi.w1.wpa_supplicant1' + + # interface_name is required + interface_name = None + + # Using getopts to handle options + try: + options, args = getopt.getopt(sys.argv[1:],"hi:t:w:") + + except getopt.GetoptError: + usage() + quit() + + # If theres a switch, override default option + for key, value in options: + # Help + if (key == "-h"): + usage() + quit() + # Interface Name + elif (key == "-i"): + interface_name = value + # Timeout + elif (key == "-t"): + if ( int(value) >= 0): + timeout = value + else: + print "Error:\n Timeout cannot be negative" + usage() + quit() + # Dbus interface + elif (key == "-w"): + wpas_dbus_interface = value + else: + assert False, "unhandled option" + + # Interface name is required and was not given + if (interface_name == None): + print "Error:\n interface_name is required" + usage() + quit() + + # Constructor + try: + p2p_find_test = P2P_Find(interface_name, wpas_dbus_interface, timeout) + + except: + print "Error:\n Invalid wpas_dbus_interface" + usage() + quit() + + # Start P2P_Find + p2p_find_test.start() + + try: + # If timeout is 0, then run forever + if (timeout == 0): + while(True): + pass + # Else sleep for (timeout) + else: + time.sleep(p2p_find_test.timeout) + + except: + pass + + quit() diff -Nru wpa-1.0/wpa_supplicant/examples/p2p/p2p_flush.py wpa-2.1/wpa_supplicant/examples/p2p/p2p_flush.py --- wpa-1.0/wpa_supplicant/examples/p2p/p2p_flush.py 1970-01-01 00:00:00.000000000 +0000 +++ wpa-2.1/wpa_supplicant/examples/p2p/p2p_flush.py 2014-02-04 11:23:35.000000000 +0000 @@ -0,0 +1,168 @@ +#!/usr/bin/python +# Tests P2P_Flush +# Will flush the p2p interface +# Then Program will exit +######### MAY NEED TO RUN AS SUDO ############# + +import dbus +import sys, os +import time +import gobject +import threading +import getopt +from dbus.mainloop.glib import DBusGMainLoop + +def usage(): + print "Usage:" + print " %s -i \ " \ + % sys.argv[0] + print " [-w ]" + print "Options:" + print " -i = interface name" + print " -w = wpas dbus interface = fi.w1.wpa_supplicant1" + print "Example:" + print " %s -i wlan0" % sys.argv[0] + +# Required Signals\ +def deviceLost(devicepath): + print "Device lost: %s" % (devicepath) + +class P2P_Flush (threading.Thread): + # Needed Variables + global bus + global wpas_object + global interface_object + global p2p_interface + global interface_name + global wpas + global wpas_dbus_interface + global path + global timeout + + # Dbus Paths + global wpas_dbus_opath + global wpas_dbus_interfaces_opath + global wpas_dbus_interfaces_interface + global wpas_dbus_interfaces_p2pdevice + + # Constructor + def __init__(self,interface_name,wpas_dbus_interface,timeout): + # Initializes variables and threads + self.interface_name = interface_name + self.wpas_dbus_interface = wpas_dbus_interface + self.timeout = timeout + + # Initializes thread and daemon allows for ctrl-c kill + threading.Thread.__init__(self) + self.daemon = True + + # Generating interface/object paths + self.wpas_dbus_opath = "/" + \ + self.wpas_dbus_interface.replace(".","/") + self.wpas_wpas_dbus_interfaces_opath = self.wpas_dbus_opath + \ + "/Interfaces" + self.wpas_dbus_interfaces_interface = \ + self.wpas_dbus_interface + ".Interface" + self.wpas_dbus_interfaces_p2pdevice = \ + self.wpas_dbus_interfaces_interface \ + + ".P2PDevice" + + # Getting interfaces and objects + DBusGMainLoop(set_as_default=True) + self.bus = dbus.SystemBus() + self.wpas_object = self.bus.get_object( + self.wpas_dbus_interface, + self.wpas_dbus_opath) + self.wpas = dbus.Interface(self.wpas_object, + self.wpas_dbus_interface) + + # Try to see if supplicant knows about interface + # If not, throw an exception + try: + self.path = self.wpas.GetInterface( + self.interface_name) + except dbus.DBusException, exc: + error = 'Error:\n Interface ' + self.interface_name \ + + ' was not found' + print error + usage() + os._exit(0) + + self.interface_object = self.bus.get_object( + self.wpas_dbus_interface, self.path) + self.p2p_interface = dbus.Interface(self.interface_object, + self.wpas_dbus_interfaces_p2pdevice) + + # Signals + self.bus.add_signal_receiver(deviceLost, + dbus_interface=self.wpas_dbus_interfaces_p2pdevice, + signal_name="DeviceLost") + + # Runs p2p_flush + def run(self): + # Allows other threads to keep working while MainLoop runs + # Required for timeout implementation + gobject.MainLoop().get_context().iteration(True) + gobject.threads_init() + self.p2p_interface.Flush() + gobject.MainLoop().run() + + +if __name__ == "__main__": + # Needed to show which devices were lost + timeout = 5 + # Defaults for optional inputs + wpas_dbus_interface = 'fi.w1.wpa_supplicant1' + + # interface_name is required + interface_name = None + + # Using getopts to handle options + try: + options, args = getopt.getopt(sys.argv[1:],"hi:w:") + + except getopt.GetoptError: + usage() + quit() + + # If theres a switch, override default option + for key, value in options: + # Help + if (key == "-h"): + usage() + quit() + # Interface Name + elif (key == "-i"): + interface_name = value + # Dbus interface + elif (key == "-w"): + wpas_dbus_interface = value + else: + assert False, "unhandled option" + + # Interface name is required and was not given + if (interface_name == None): + print "Error:\n interface_name is required" + usage() + quit() + + # Constructor + try: + p2p_flush_test = P2P_Flush(interface_name, wpas_dbus_interface,timeout) + + except: + print "Error:\n Invalid wpas_dbus_interface" + usage() + quit() + + # Start P2P_Find + p2p_flush_test.start() + + try: + time.sleep(int(p2p_flush_test.timeout)) + + except: + pass + + print "p2p_flush complete" + quit() diff -Nru wpa-1.0/wpa_supplicant/examples/p2p/p2p_group_add.py wpa-2.1/wpa_supplicant/examples/p2p/p2p_group_add.py --- wpa-1.0/wpa_supplicant/examples/p2p/p2p_group_add.py 1970-01-01 00:00:00.000000000 +0000 +++ wpa-2.1/wpa_supplicant/examples/p2p/p2p_group_add.py 2014-02-04 11:23:35.000000000 +0000 @@ -0,0 +1,222 @@ +#!/usr/bin/python +# Tests p2p_group_add +######### MAY NEED TO RUN AS SUDO ############# + +import dbus +import sys, os +import time +import gobject +import getopt +import threading +from dbus.mainloop.glib import DBusGMainLoop + +def usage(): + print "Usage:" + print " %s -i [-p ] \ " \ + % sys.argv[0] + print " [-f ] [-o ] \ " + print " [-w ]" + print "Options:" + print " -i = interface name" + print " -p = persistant group = 0 (0=false, 1=true)" + print " -f = frequency" + print " -o = persistent group object path" + print " -w = wpas dbus interface = fi.w1.wpa_supplicant1" + print "Example:" + print " %s -i wlan0" % sys.argv[0] + +# Required Signals +def GroupStarted(properties): + if properties.has_key("group_object"): + print 'Group Formation Complete %s' \ + % properties["group_object"] + os._exit(0) + +def WpsFailure(status, etc): + print "WPS Authentication Failure".format(status) + print etc + os._exit(0) + +class P2P_Group_Add (threading.Thread): + # Needed Variables + global bus + global wpas_object + global interface_object + global p2p_interface + global interface_name + global wpas + global wpas_dbus_interface + global path + global persistent + global frequency + global persistent_group_object + + # Dbus Paths + global wpas_dbus_opath + global wpas_dbus_interfaces_opath + global wpas_dbus_interfaces_interface + global wpas_dbus_interfaces_p2pdevice + + # Arguements + global P2PDictionary + + # Constructor + def __init__(self,interface_name,wpas_dbus_interface,persistent,frequency, + persistent_group_object): + # Initializes variables and threads + self.interface_name = interface_name + self.wpas_dbus_interface = wpas_dbus_interface + self.persistent = persistent + self.frequency = frequency + self.persistent_group_object = persistent_group_object + + # Initializes thread and daemon allows for ctrl-c kill + threading.Thread.__init__(self) + self.daemon = True + + # Generating interface/object paths + self.wpas_dbus_opath = "/" + \ + self.wpas_dbus_interface.replace(".","/") + self.wpas_wpas_dbus_interfaces_opath = self.wpas_dbus_opath + \ + "/Interfaces" + self.wpas_dbus_interfaces_interface = \ + self.wpas_dbus_interface + ".Interface" + self.wpas_dbus_interfaces_p2pdevice = \ + self.wpas_dbus_interfaces_interface \ + + ".P2PDevice" + + # Getting interfaces and objects + DBusGMainLoop(set_as_default=True) + self.bus = dbus.SystemBus() + self.wpas_object = self.bus.get_object( + self.wpas_dbus_interface, + self.wpas_dbus_opath) + self.wpas = dbus.Interface(self.wpas_object, + self.wpas_dbus_interface) + + # Try to see if supplicant knows about interface + # If not, throw an exception + try: + self.path = self.wpas.GetInterface( + self.interface_name) + except dbus.DBusException, exc: + error = 'Error:\n Interface ' + self.interface_name \ + + ' was not found' + print error + usage() + os._exit(0) + + self.interface_object = self.bus.get_object( + self.wpas_dbus_interface, self.path) + self.p2p_interface = dbus.Interface(self.interface_object, + self.wpas_dbus_interfaces_p2pdevice) + + #Adds listeners + self.bus.add_signal_receiver(GroupStarted, + dbus_interface=self.wpas_dbus_interfaces_p2pdevice, + signal_name="GroupStarted") + self.bus.add_signal_receiver(WpsFailure, + dbus_interface=self.wpas_dbus_interfaces_p2pdevice, + signal_name="WpsFailed") + + # Sets up p2p_group_add dictionary + def constructArguements(self): + self.P2PDictionary = {'persistent':self.persistent} + + if (self.frequency != None): + if (int(self.frequency) > 0): + self.P2PDictionary.update({'frequency':int(self.frequency)}) + else: + print "Error:\n Frequency must be greater than 0" + usage() + os._exit(0) + + if (self.persistent_group_object != None): + self.P2PDictionary.update({'persistent_group_object': + self.persistent_group_object}) + + # Run p2p_group_remove + def run(self): + try: + self.p2p_interface.GroupAdd(self.P2PDictionary) + + except: + print "Error:\n Could not preform group add" + usage() + os._exit(0) + + # Allows other threads to keep working while MainLoop runs + # Required for timeout implementation + gobject.MainLoop().get_context().iteration(True) + gobject.threads_init() + gobject.MainLoop().run() + + +if __name__ == "__main__": + + # Defaults for optional inputs + # 0 = false, 1 = true + persistent = False + frequency = None + persistent_group_object = None + wpas_dbus_interface = 'fi.w1.wpa_supplicant1' + + # interface_name is required + interface_name = None + + # Using getopts to handle options + try: + options, args = getopt.getopt(sys.argv[1:],"hi:p:f:o:w:") + + except getopt.GetoptError: + usage() + quit() + + # If theres a switch, override default option + for key, value in options: + # Help + if (key == "-h"): + usage() + quit() + # Interface Name + elif (key == "-i"): + interface_name = value + # Timeout + elif (key == "-p"): + if (value == '0'): + persistent = False + elif (value == '1'): + persistent = True + else: + print "Error:\n Persistent can only be 1 or 0" + usage() + os._exit(0) + # Frequency + elif (key == "-f"): + frequency = value + # Persistent group object path + elif (key == "-o"): + persistent_group_object = value + # Dbus interface + elif (key == "-w"): + wpas_dbus_interface = value + else: + assert False, "unhandled option" + + # Interface name is required and was not given + if (interface_name == None): + print "Error:\n interface_name is required" + usage() + quit() + + try: + p2p_group_add_test = P2P_Group_Add(interface_name,wpas_dbus_interface, + persistent,frequency,persistent_group_object) + except: + print "Error:\n Invalid Arguements" + + p2p_group_add_test.constructArguements() + p2p_group_add_test.start() + time.sleep(5) + print "Error:\n Group formation timed out" + os._exit(0) diff -Nru wpa-1.0/wpa_supplicant/examples/p2p/p2p_invite.py wpa-2.1/wpa_supplicant/examples/p2p/p2p_invite.py --- wpa-1.0/wpa_supplicant/examples/p2p/p2p_invite.py 1970-01-01 00:00:00.000000000 +0000 +++ wpa-2.1/wpa_supplicant/examples/p2p/p2p_invite.py 2014-02-04 11:23:35.000000000 +0000 @@ -0,0 +1,201 @@ +#!/usr/bin/python +# Tests p2p_invite +######### MAY NEED TO RUN AS SUDO ############# + +import dbus +import sys, os +import time +import gobject +import getopt +import threading +from dbus.mainloop.glib import DBusGMainLoop + +def usage(): + print "Usage:" + print " %s -i -a \ " \ + % sys.argv[0] + print " [-o ] [-w ]" + print "Options:" + print " -i = interface name" + print " -a = address of peer" + print " -o = persistent group object path" + print " -w = wpas dbus interface = fi.w1.wpa_supplicant1" + print "Example:" + print " %s -i p2p-wlan0-0 -a 00150083523c" % sys.argv[0] + +# Required Signals +def InvitationResult(invite_result): + print "Inviation Result signal :" + status = invite_result['status'] + print "status = ", status + if invite_result.has_key('BSSID'): + bssid = invite_result['BSSID'] + print "BSSID = ", hex(bssid[0]) , ":" , \ + hex(bssid[1]) , ":" , hex(bssid[2]) , ":", \ + hex(bssid[3]) , ":" , hex(bssid[4]) , ":" , \ + hex(bssid[5]) + os._exit(0) + +class P2P_Invite (threading.Thread): + # Needed Variables + global bus + global wpas_object + global interface_object + global p2p_interface + global interface_name + global wpas + global wpas_dbus_interface + global path + global addr + global persistent_group_object + + # Dbus Paths + global wpas_dbus_opath + global wpas_dbus_interfaces_opath + global wpas_dbus_interfaces_interface + global wpas_dbus_interfaces_p2pdevice + + # Arguements + global P2PDictionary + + # Constructor + def __init__(self,interface_name,wpas_dbus_interface,addr, + persistent_group_object): + # Initializes variables and threads + self.interface_name = interface_name + self.wpas_dbus_interface = wpas_dbus_interface + self.addr = addr + self.persistent_group_object = persistent_group_object + + # Initializes thread and daemon allows for ctrl-c kill + threading.Thread.__init__(self) + self.daemon = True + + # Generating interface/object paths + self.wpas_dbus_opath = "/" + \ + self.wpas_dbus_interface.replace(".","/") + self.wpas_wpas_dbus_interfaces_opath = self.wpas_dbus_opath + \ + "/Interfaces" + self.wpas_dbus_interfaces_interface = \ + self.wpas_dbus_interface + ".Interface" + self.wpas_dbus_interfaces_p2pdevice = \ + self.wpas_dbus_interfaces_interface \ + + ".P2PDevice" + + # Getting interfaces and objects + DBusGMainLoop(set_as_default=True) + self.bus = dbus.SystemBus() + self.wpas_object = self.bus.get_object( + self.wpas_dbus_interface, + self.wpas_dbus_opath) + self.wpas = dbus.Interface(self.wpas_object, + self.wpas_dbus_interface) + + # Try to see if supplicant knows about interface + # If not, throw an exception + try: + self.path = self.wpas.GetInterface( + self.interface_name) + except dbus.DBusException, exc: + error = 'Error:\n Interface ' + self.interface_name \ + + ' was not found' + print error + usage() + os._exit(0) + + self.interface_object = self.bus.get_object( + self.wpas_dbus_interface, self.path) + self.p2p_interface = dbus.Interface(self.interface_object, + self.wpas_dbus_interfaces_p2pdevice) + + #Adds listeners + self.bus.add_signal_receiver(InvitationResult, + dbus_interface=self.wpas_dbus_interfaces_p2pdevice, + signal_name="InvitationResult") + + # Sets up p2p_invite dictionary + def constructArguements(self): + self.P2PDictionary = \ + {'peer':dbus.ObjectPath(self.path+'/Peers/'+self.addr)} + if (self.persistent_group_object != None): + self.P2PDictionary.update({"persistent_group_object": + self.persistent_group_object}) + + # Run p2p_invite + def run(self): + try: + self.p2p_interface.Invite(self.P2PDictionary) + + except: + print "Error:\n Invalid Arguements" + usage() + os._exit(0) + + # Allows other threads to keep working while MainLoop runs + # Required for timeout implementation + gobject.MainLoop().get_context().iteration(True) + gobject.threads_init() + gobject.MainLoop().run() + +if __name__ == "__main__": + # Defaults for optional inputs + addr = None + persistent_group_object = None + wpas_dbus_interface = 'fi.w1.wpa_supplicant1' + + # interface_name is required + interface_name = None + + # Using getopts to handle options + try: + options, args = getopt.getopt(sys.argv[1:],"hi:o:w:a:") + + except getopt.GetoptError: + usage() + quit() + + # If theres a switch, override default option + for key, value in options: + # Help + if (key == "-h"): + usage() + quit() + # Interface Name + elif (key == "-i"): + interface_name = value + elif (key == "-a"): + addr = value + # Persistent group object path + elif (key == "-o"): + persistent_group_object = value + # Dbus interface + elif (key == "-w"): + wpas_dbus_interface = value + else: + assert False, "unhandled option" + + # Interface name is required and was not given + if (interface_name == None): + print "Error:\n interface_name is required" + usage() + quit() + + if (addr == None): + print "Error:\n peer address is required" + usage() + quit() + + try: + p2p_invite_test = \ + P2P_Invite(interface_name,wpas_dbus_interface, + addr,persistent_group_object) + except: + print "Error:\n Invalid Arguements" + usage() + os._exit(1) + + p2p_invite_test.constructArguements() + p2p_invite_test.start() + time.sleep(10) + print "Error:\n p2p_invite timed out" + os._exit(0) diff -Nru wpa-1.0/wpa_supplicant/examples/p2p/p2p_listen.py wpa-2.1/wpa_supplicant/examples/p2p/p2p_listen.py --- wpa-1.0/wpa_supplicant/examples/p2p/p2p_listen.py 1970-01-01 00:00:00.000000000 +0000 +++ wpa-2.1/wpa_supplicant/examples/p2p/p2p_listen.py 2014-02-04 11:23:35.000000000 +0000 @@ -0,0 +1,182 @@ +#!/usr/bin/python +# Tests P2P_Find +# Will listen +# Then Program will exit +######### MAY NEED TO RUN AS SUDO ############# + +import dbus +import sys, os +import time +import gobject +import threading +import getopt +from dbus.mainloop.glib import DBusGMainLoop + +def usage(): + print "Usage:" + print " %s -i [-t ] \ " \ + % sys.argv[0] + print " [-w ]" + print "Options:" + print " -i = interface name" + print " -t = timeout = 0s (infinite)" + print " -w = wpas dbus interface = fi.w1.wpa_supplicant1" + print "Example:" + print " %s -i wlan0 -t 5" % sys.argv[0] + +# Required Signals +def p2pStateChange(status): + print status + +class P2P_Listen(threading.Thread): + # Needed Variables + global bus + global wpas_object + global interface_object + global p2p_interface + global interface_name + global wpas + global wpas_dbus_interface + global path + global timeout + + # Dbus Paths + global wpas_dbus_opath + global wpas_dbus_interfaces_opath + global wpas_dbus_interfaces_interface + global wpas_dbus_interfaces_p2pdevice + + # Constructor + def __init__(self,interface_name,wpas_dbus_interface,timeout): + # Initializes variables and threads + self.timeout = int(timeout) + self.interface_name = interface_name + self.wpas_dbus_interface = wpas_dbus_interface + + # Initializes thread and daemon allows for ctrl-c kill + threading.Thread.__init__(self) + self.daemon = True + + # Generating interface/object paths + self.wpas_dbus_opath = "/" + \ + self.wpas_dbus_interface.replace(".","/") + self.wpas_wpas_dbus_interfaces_opath = self.wpas_dbus_opath + \ + "/Interfaces" + self.wpas_dbus_interfaces_interface = \ + self.wpas_dbus_interface + ".Interface" + self.wpas_dbus_interfaces_p2pdevice = \ + self.wpas_dbus_interfaces_interface \ + + ".P2PDevice" + + # Getting interfaces and objects + DBusGMainLoop(set_as_default=True) + self.bus = dbus.SystemBus() + self.wpas_object = self.bus.get_object( + self.wpas_dbus_interface, + self.wpas_dbus_opath) + self.wpas = dbus.Interface(self.wpas_object, + self.wpas_dbus_interface) + + # Try to see if supplicant knows about interface + # If not, throw an exception + try: + self.path = self.wpas.GetInterface( + self.interface_name) + except dbus.DBusException, exc: + error = 'Error:\n Interface ' + self.interface_name \ + + ' was not found' + print error + usage() + os._exit(0) + + self.interface_object = self.bus.get_object( + self.wpas_dbus_interface, self.path) + self.p2p_interface = dbus.Interface(self.interface_object, + self.wpas_dbus_interfaces_p2pdevice) + + self.bus.add_signal_receiver(p2pStateChange, + dbus_interface=self.wpas_dbus_interfaces_p2pdevice, + signal_name="P2PStateChanged") + + # Run p2p_find + def run(self): + # Sets up p2p_listen + self.p2p_interface.Listen(int(self.timeout)) + + # Allows other threads to keep working while MainLoop runs + # Required for timeout implementation + gobject.MainLoop().get_context().iteration(True) + gobject.threads_init() + gobject.MainLoop().run() + +if __name__ == "__main__": + + # Defaults for optional inputs + timeout = 0 + wpas_dbus_interface = 'fi.w1.wpa_supplicant1' + + # interface_name is required + interface_name = None + + # Using getopts to handle options + try: + options, args = getopt.getopt(sys.argv[1:],"hi:t:w:") + + except getopt.GetoptError: + usage() + quit() + + # If theres a switch, override default option + for key, value in options: + # Help + if (key == "-h"): + usage() + quit() + # Interface Name + elif (key == "-i"): + interface_name = value + # Timeout + elif (key == "-t"): + if ( int(value) >= 0): + timeout = value + else: + print "Error:\n Timeout cannot be negative" + usage() + quit() + # Dbus interface + elif (key == "-w"): + wpas_dbus_interface = value + else: + assert False, "unhandled option" + + # Interface name is required and was not given + if (interface_name == None): + print "Error:\n interface_name is required" + usage() + quit() + + # Constructor + try: + p2p_listen_test = P2P_Listen(interface_name, wpas_dbus_interface, timeout) + + except: + print "Error:\n Invalid wpas_dbus_interface" + usage() + quit() + + # Start P2P_Find + p2p_listen_test.start() + + try: + # If timeout is 0, then run forever + if (int(p2p_listen_test.timeout) == 0): + while(True): + pass + # Else sleep for (timeout) + else: + time.sleep(int(p2p_listen_test.timeout)) + + except: + pass + + quit() diff -Nru wpa-1.0/wpa_supplicant/examples/p2p/p2p_stop_find.py wpa-2.1/wpa_supplicant/examples/p2p/p2p_stop_find.py --- wpa-1.0/wpa_supplicant/examples/p2p/p2p_stop_find.py 1970-01-01 00:00:00.000000000 +0000 +++ wpa-2.1/wpa_supplicant/examples/p2p/p2p_stop_find.py 2014-02-04 11:23:35.000000000 +0000 @@ -0,0 +1,174 @@ +#!/usr/bin/python +# Tests p2p_stop_find +######### MAY NEED TO RUN AS SUDO ############# + +import dbus +import sys, os +import time +import gobject +import threading +import getopt +from dbus.mainloop.glib import DBusGMainLoop + +def usage(): + print "Usage:" + print " %s -i \ " \ + % sys.argv[0] + print " [-w ]" + print "Options:" + print " -i = interface name" + print " -w = wpas dbus interface = fi.w1.wpa_supplicant1" + print "Example:" + print " %s -i wlan0" % sys.argv[0] + +# Required Signals +def deviceLost(devicepath): + print "Device lost: %s" % (devicepath) + +def p2pStateChange(status): + print status + os._exit(0) + +class P2P_Stop_Find (threading.Thread): + # Needed Variables + global bus + global wpas_object + global interface_object + global p2p_interface + global interface_name + global wpas + global wpas_dbus_interface + global path + global timeout + + # Dbus Paths + global wpas_dbus_opath + global wpas_dbus_interfaces_opath + global wpas_dbus_interfaces_interface + global wpas_dbus_interfaces_p2pdevice + + # Constructor + def __init__(self,interface_name,wpas_dbus_interface,timeout): + # Initializes variables and threads + self.interface_name = interface_name + self.wpas_dbus_interface = wpas_dbus_interface + self.timeout = timeout + + # Initializes thread and daemon allows for ctrl-c kill + threading.Thread.__init__(self) + self.daemon = True + + # Generating interface/object paths + self.wpas_dbus_opath = "/" + \ + self.wpas_dbus_interface.replace(".","/") + self.wpas_wpas_dbus_interfaces_opath = self.wpas_dbus_opath + \ + "/Interfaces" + self.wpas_dbus_interfaces_interface = \ + self.wpas_dbus_interface + ".Interface" + self.wpas_dbus_interfaces_p2pdevice = \ + self.wpas_dbus_interfaces_interface \ + + ".P2PDevice" + + # Getting interfaces and objects + DBusGMainLoop(set_as_default=True) + self.bus = dbus.SystemBus() + self.wpas_object = self.bus.get_object( + self.wpas_dbus_interface, + self.wpas_dbus_opath) + self.wpas = dbus.Interface(self.wpas_object, + self.wpas_dbus_interface) + + # Try to see if supplicant knows about interface + # If not, throw an exception + try: + self.path = self.wpas.GetInterface( + self.interface_name) + except dbus.DBusException, exc: + error = 'Error:\n Interface ' + self.interface_name \ + + ' was not found' + print error + usage() + os._exit(0) + + self.interface_object = self.bus.get_object( + self.wpas_dbus_interface, self.path) + self.p2p_interface = dbus.Interface(self.interface_object, + self.wpas_dbus_interfaces_p2pdevice) + + # Signals + self.bus.add_signal_receiver(deviceLost, + dbus_interface=self.wpas_dbus_interfaces_p2pdevice, + signal_name="DeviceLost") + self.bus.add_signal_receiver(p2pStateChange, + dbus_interface=self.wpas_dbus_interfaces_p2pdevice, + signal_name="P2PStateChanged") + + # Runs p2p_stop_find + def run(self): + # Allows other threads to keep working while MainLoop runs + # Required for timeout implementation + gobject.MainLoop().get_context().iteration(True) + gobject.threads_init() + self.p2p_interface.StopFind() + gobject.MainLoop().run() + + +if __name__ == "__main__": + # Needed because P2PStateChanged signal is not caught + timeout = 5 + # Defaults for optional inputs + wpas_dbus_interface = 'fi.w1.wpa_supplicant1' + + # interface_name is required + interface_name = None + + # Using getopts to handle options + try: + options, args = getopt.getopt(sys.argv[1:],"ht:i:w:") + + except getopt.GetoptError: + usage() + quit() + + # If theres a switch, override default option + for key, value in options: + # Help + if (key == "-h"): + usage() + quit() + # Interface Name + elif (key == "-i"): + interface_name = value + # Dbus interface + elif (key == "-w"): + wpas_dbus_interface = value + else: + assert False, "unhandled option" + + # Interface name is required and was not given + if (interface_name == None): + print "Error:\n interface_name is required" + usage() + quit() + + # Constructor + try: + p2p_stop_find_test = P2P_Stop_Find(interface_name, + wpas_dbus_interface,timeout) + + except: + print "Error:\n Invalid wpas_dbus_interface" + usage() + quit() + + # Start P2P_Find + p2p_stop_find_test.start() + + try: + time.sleep(int(p2p_stop_find_test.timeout)) + + except: + pass + + print "p2p find stopped" + quit() diff -Nru wpa-1.0/wpa_supplicant/examples/p2p-action.sh wpa-2.1/wpa_supplicant/examples/p2p-action.sh --- wpa-1.0/wpa_supplicant/examples/p2p-action.sh 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/examples/p2p-action.sh 2014-02-04 11:23:35.000000000 +0000 @@ -34,13 +34,26 @@ # start with -z to avoid that dnsmasq -x /var/run/dnsmasq.pid-$GIFNAME \ -i $GIFNAME \ - -F192.168.42.11,192.168.42.99 --listen-address 192.168.42.1 -z + -F192.168.42.11,192.168.42.99 --listen-address 192.168.42.1 -z -p 0 fi fi if [ "$4" = "client" ]; then kill_daemon dhclient /var/run/dhclient-$GIFNAME.pid rm /var/run/dhclient.leases-$GIFNAME kill_daemon dnsmasq /var/run/dnsmasq.pid-$GIFNAME + ipaddr=`echo "$*" | sed 's/.* ip_addr=\([^ ]*\).*/\1/'` + ipmask=`echo "$*" | sed 's/.* ip_mask=\([^ ]*\).*/\1/'` + goipaddr=`echo "$*" | sed 's/.* go_ip_addr=\([^ ]*\).*/\1/'` + if echo "$ipaddr$ipmask$goipaddr" | grep -q ' '; then + ipaddr="" + ipmask="" + goipaddr="" + fi + if [ -n "$ipaddr" ]; then + sudo ifconfig $GIFNAME "$ipaddr" netmask "$ipmask" + sudo ip ro re default via "$goipaddr" + exit 0 + fi dhclient -pf /var/run/dhclient-$GIFNAME.pid \ -lf /var/run/dhclient.leases-$GIFNAME \ -nw \ diff -Nru wpa-1.0/wpa_supplicant/examples/p2p-nfc.py wpa-2.1/wpa_supplicant/examples/p2p-nfc.py --- wpa-1.0/wpa_supplicant/examples/p2p-nfc.py 1970-01-01 00:00:00.000000000 +0000 +++ wpa-2.1/wpa_supplicant/examples/p2p-nfc.py 2014-02-04 11:23:35.000000000 +0000 @@ -0,0 +1,581 @@ +#!/usr/bin/python +# +# Example nfcpy to wpa_supplicant wrapper for P2P NFC operations +# Copyright (c) 2012-2013, Jouni Malinen +# +# This software may be distributed under the terms of the BSD license. +# See README for more details. + +import os +import sys +import time +import random +import threading +import argparse + +import nfc +import nfc.ndef +import nfc.llcp +import nfc.handover + +import logging + +import wpaspy + +wpas_ctrl = '/var/run/wpa_supplicant' +ifname = None +init_on_touch = False +in_raw_mode = False +prev_tcgetattr = 0 +include_wps_req = True +include_p2p_req = True +no_input = False +srv = None +continue_loop = True +terminate_now = False + +def wpas_connect(): + ifaces = [] + 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 wpa_supplicant: ", error + return None + + if len(ifaces) < 1: + print "No wpa_supplicant control interface found" + return None + + for ctrl in ifaces: + if ifname: + if ifname not in ctrl: + continue + try: + print "Trying to use control interface " + ctrl + wpas = wpaspy.Ctrl(ctrl) + return wpas + except Exception, e: + pass + return None + + +def wpas_tag_read(message): + wpas = wpas_connect() + if (wpas == None): + return + cmd = "WPS_NFC_TAG_READ " + str(message).encode("hex") + global force_freq + if force_freq: + cmd = cmd + " freq=" + force_freq + if "FAIL" in wpas.request(cmd): + return False + return True + + +def wpas_get_handover_req(): + wpas = wpas_connect() + if (wpas == None): + return None + res = wpas.request("NFC_GET_HANDOVER_REQ NDEF P2P-CR").rstrip().decode("hex") + if "FAIL" in res: + return None + return res + +def wpas_get_handover_req_wps(): + wpas = wpas_connect() + if (wpas == None): + return None + return wpas.request("NFC_GET_HANDOVER_REQ NDEF WPS-CR").rstrip().decode("hex") + + +def wpas_get_handover_sel(tag=False): + wpas = wpas_connect() + if (wpas == None): + return None + if tag: + return wpas.request("NFC_GET_HANDOVER_SEL NDEF P2P-CR-TAG").rstrip().decode("hex") + return wpas.request("NFC_GET_HANDOVER_SEL NDEF P2P-CR").rstrip().decode("hex") + + +def wpas_get_handover_sel_wps(): + wpas = wpas_connect() + if (wpas == None): + return None + res = wpas.request("NFC_GET_HANDOVER_SEL NDEF WPS-CR"); + if "FAIL" in res: + return None + return res.rstrip().decode("hex") + + +def wpas_report_handover(req, sel, type): + wpas = wpas_connect() + if (wpas == None): + return None + cmd = "NFC_REPORT_HANDOVER " + type + " P2P " + str(req).encode("hex") + " " + str(sel).encode("hex") + global force_freq + if force_freq: + cmd = cmd + " freq=" + force_freq + return wpas.request(cmd) + + +def wpas_report_handover_wsc(req, sel, type): + wpas = wpas_connect() + if (wpas == None): + return None + cmd = "NFC_REPORT_HANDOVER " + type + " WPS " + str(req).encode("hex") + " " + str(sel).encode("hex") + if force_freq: + cmd = cmd + " freq=" + force_freq + return wpas.request(cmd) + + +def p2p_handover_client(llc): + message = nfc.ndef.HandoverRequestMessage(version="1.2") + message.nonce = random.randint(0, 0xffff) + + global include_p2p_req + if include_p2p_req: + data = wpas_get_handover_req() + if (data == None): + print "Could not get handover request carrier record from wpa_supplicant" + return + print "Handover request carrier record from wpa_supplicant: " + data.encode("hex") + datamsg = nfc.ndef.Message(data) + message.add_carrier(datamsg[0], "active", datamsg[1:]) + + global include_wps_req + if include_wps_req: + print "Handover request (pre-WPS):" + try: + print message.pretty() + except Exception, e: + print e + + data = wpas_get_handover_req_wps() + if data: + print "Add WPS request in addition to P2P" + datamsg = nfc.ndef.Message(data) + message.add_carrier(datamsg[0], "active", datamsg[1:]) + + print "Handover request:" + try: + print message.pretty() + except Exception, e: + print e + print str(message).encode("hex") + + client = nfc.handover.HandoverClient(llc) + try: + print "Trying handover"; + client.connect() + print "Connected for handover" + except nfc.llcp.ConnectRefused: + print "Handover connection refused" + client.close() + return + except Exception, e: + print "Other exception: " + str(e) + client.close() + return + + print "Sending handover request" + + if not client.send(message): + print "Failed to send handover request" + + print "Receiving handover response" + message = client._recv() + if message is None: + print "No response received" + client.close() + return + if message.type != "urn:nfc:wkt:Hs": + print "Response was not Hs - received: " + message.type + client.close() + return + + print "Received message" + try: + print message.pretty() + except Exception, e: + print e + print str(message).encode("hex") + message = nfc.ndef.HandoverSelectMessage(message) + print "Handover select received" + try: + print message.pretty() + except Exception, e: + print e + + for carrier in message.carriers: + print "Remote carrier type: " + carrier.type + if carrier.type == "application/vnd.wfa.p2p": + print "P2P carrier type match - send to wpa_supplicant" + wpas_report_handover(data, carrier.record, "INIT") + break + + print "Remove peer" + client.close() + print "Done with handover" + global only_one + if only_one: + print "only_one -> stop loop" + global continue_loop + continue_loop = False + + global no_wait + if no_wait: + print "Trying to exit.." + global terminate_now + terminate_now = True + + +class HandoverServer(nfc.handover.HandoverServer): + def __init__(self, llc): + super(HandoverServer, self).__init__(llc) + self.sent_carrier = None + self.ho_server_processing = False + self.success = False + + def process_request(self, request): + self.ho_server_processing = True + clear_raw_mode() + print "HandoverServer - request received" + try: + print "Parsed handover request: " + request.pretty() + except Exception, e: + print e + + sel = nfc.ndef.HandoverSelectMessage(version="1.2") + + found = False + + for carrier in request.carriers: + print "Remote carrier type: " + carrier.type + if carrier.type == "application/vnd.wfa.p2p": + print "P2P carrier type match - add P2P carrier record" + found = True + self.received_carrier = carrier.record + print "Carrier record:" + try: + print carrier.record.pretty() + except Exception, e: + print e + data = wpas_get_handover_sel() + if data is None: + print "Could not get handover select carrier record from wpa_supplicant" + continue + print "Handover select carrier record from wpa_supplicant:" + print data.encode("hex") + self.sent_carrier = data + wpas_report_handover(self.received_carrier, self.sent_carrier, + "RESP") + + message = nfc.ndef.Message(data); + sel.add_carrier(message[0], "active", message[1:]) + break + + for carrier in request.carriers: + if found: + break + print "Remote carrier type: " + carrier.type + if carrier.type == "application/vnd.wfa.wsc": + print "WSC carrier type match - add WSC carrier record" + found = True + self.received_carrier = carrier.record + print "Carrier record:" + try: + print carrier.record.pretty() + except Exception, e: + print e + data = wpas_get_handover_sel_wps() + if data is None: + print "Could not get handover select carrier record from wpa_supplicant" + continue + print "Handover select carrier record from wpa_supplicant:" + print data.encode("hex") + self.sent_carrier = data + wpas_report_handover_wsc(self.received_carrier, + self.sent_carrier, "RESP") + + message = nfc.ndef.Message(data); + sel.add_carrier(message[0], "active", message[1:]) + found = True + break + + print "Handover select:" + try: + print sel.pretty() + except Exception, e: + print e + print str(sel).encode("hex") + + print "Sending handover select" + self.success = True + return sel + + +def clear_raw_mode(): + import sys, tty, termios + global prev_tcgetattr, in_raw_mode + if not in_raw_mode: + return + fd = sys.stdin.fileno() + termios.tcsetattr(fd, termios.TCSADRAIN, prev_tcgetattr) + in_raw_mode = False + + +def getch(): + import sys, tty, termios, select + global prev_tcgetattr, in_raw_mode + fd = sys.stdin.fileno() + prev_tcgetattr = termios.tcgetattr(fd) + ch = None + try: + tty.setraw(fd) + in_raw_mode = True + [i, o, e] = select.select([fd], [], [], 0.05) + if i: + ch = sys.stdin.read(1) + finally: + termios.tcsetattr(fd, termios.TCSADRAIN, prev_tcgetattr) + in_raw_mode = False + return ch + + +def p2p_tag_read(tag): + success = False + if len(tag.ndef.message): + for record in tag.ndef.message: + print "record type " + record.type + if record.type == "application/vnd.wfa.wsc": + print "WPS tag - send to wpa_supplicant" + success = wpas_tag_read(tag.ndef.message) + break + if record.type == "application/vnd.wfa.p2p": + print "P2P tag - send to wpa_supplicant" + success = wpas_tag_read(tag.ndef.message) + break + else: + print "Empty tag" + + return success + + +def rdwr_connected_p2p_write(tag): + print "Tag found - writing" + global p2p_sel_data + tag.ndef.message = str(p2p_sel_data) + print "Done - remove tag" + global only_one + if only_one: + global continue_loop + continue_loop = False + global p2p_sel_wait_remove + return p2p_sel_wait_remove + +def wps_write_p2p_handover_sel(clf, wait_remove=True): + print "Write P2P handover select" + data = wpas_get_handover_sel(tag=True) + if (data == None): + print "Could not get P2P handover select from wpa_supplicant" + return + + global p2p_sel_wait_remove + p2p_sel_wait_remove = wait_remove + global p2p_sel_data + p2p_sel_data = nfc.ndef.HandoverSelectMessage(version="1.2") + message = nfc.ndef.Message(data); + p2p_sel_data.add_carrier(message[0], "active", message[1:]) + print "Handover select:" + try: + print p2p_sel_data.pretty() + except Exception, e: + print e + print str(p2p_sel_data).encode("hex") + + print "Touch an NFC tag" + clf.connect(rdwr={'on-connect': rdwr_connected_p2p_write}) + + +def rdwr_connected(tag): + global only_one, no_wait + print "Tag connected: " + str(tag) + + if tag.ndef: + print "NDEF tag: " + tag.type + try: + print tag.ndef.message.pretty() + except Exception, e: + print e + success = p2p_tag_read(tag) + if only_one and success: + global continue_loop + continue_loop = False + else: + print "Not an NDEF tag - remove tag" + + return not no_wait + + +def llcp_worker(llc): + global init_on_touch + if init_on_touch: + print "Starting handover client" + p2p_handover_client(llc) + return + + global no_input + if no_input: + print "Wait for handover to complete" + else: + print "Wait for handover to complete - press 'i' to initiate ('w' for WPS only, 'p' for P2P only)" + global srv + global wait_connection + while not wait_connection and srv.sent_carrier is None: + if srv.ho_server_processing: + time.sleep(0.025) + elif no_input: + time.sleep(0.5) + else: + global include_wps_req, include_p2p_req + res = getch() + if res == 'i': + include_wps_req = True + include_p2p_req = True + elif res == 'p': + include_wps_req = False + include_p2p_req = True + elif res == 'w': + include_wps_req = True + include_p2p_req = False + else: + continue + clear_raw_mode() + print "Starting handover client" + p2p_handover_client(llc) + return + + clear_raw_mode() + print "Exiting llcp_worker thread" + +def llcp_startup(clf, llc): + print "Start LLCP server" + global srv + srv = HandoverServer(llc) + return llc + +def llcp_connected(llc): + print "P2P LLCP connected" + global wait_connection + wait_connection = False + global init_on_touch + if not init_on_touch: + global srv + srv.start() + if init_on_touch or not no_input: + threading.Thread(target=llcp_worker, args=(llc,)).start() + return True + +def terminate_loop(): + global terminate_now + return terminate_now + +def main(): + clf = nfc.ContactlessFrontend() + + parser = argparse.ArgumentParser(description='nfcpy to wpa_supplicant integration for P2P and WPS NFC operations') + parser.add_argument('-d', const=logging.DEBUG, default=logging.INFO, + action='store_const', dest='loglevel', + help='verbose debug output') + parser.add_argument('-q', const=logging.WARNING, action='store_const', + dest='loglevel', help='be quiet') + parser.add_argument('--only-one', '-1', action='store_true', + help='run only one operation and exit') + parser.add_argument('--init-on-touch', '-I', action='store_true', + help='initiate handover on touch') + parser.add_argument('--no-wait', action='store_true', + help='do not wait for tag to be removed before exiting') + parser.add_argument('--ifname', '-i', + help='network interface name') + parser.add_argument('--no-wps-req', '-N', action='store_true', + help='do not include WPS carrier record in request') + parser.add_argument('--no-input', '-a', action='store_true', + help='do not use stdout input to initiate handover') + parser.add_argument('--tag-read-only', '-t', action='store_true', + help='tag read only (do not allow connection handover)') + parser.add_argument('--freq', '-f', + help='forced frequency of operating channel in MHz') + parser.add_argument('command', choices=['write-p2p-sel'], + nargs='?') + args = parser.parse_args() + + global only_one + only_one = args.only_one + + global no_wait + no_wait = args.no_wait + + global force_freq + force_freq = args.freq + + logging.basicConfig(level=args.loglevel) + + global init_on_touch + init_on_touch = args.init_on_touch + + if args.ifname: + global ifname + ifname = args.ifname + print "Selected ifname " + ifname + + if args.no_wps_req: + global include_wps_req + include_wps_req = False + + if args.no_input: + global no_input + no_input = True + + clf = nfc.ContactlessFrontend() + global wait_connection + + try: + if not clf.open("usb"): + print "Could not open connection with an NFC device" + raise SystemExit + + if args.command == "write-p2p-sel": + wps_write_p2p_handover_sel(clf, wait_remove=not args.no_wait) + raise SystemExit + + global continue_loop + while continue_loop: + print "Waiting for a tag or peer to be touched" + wait_connection = True + try: + if args.tag_read_only: + if not clf.connect(rdwr={'on-connect': rdwr_connected}): + break + else: + if not clf.connect(rdwr={'on-connect': rdwr_connected}, + llcp={'on-startup': llcp_startup, + 'on-connect': llcp_connected}, + terminate=terminate_loop): + break + except Exception, e: + print "clf.connect failed" + + global srv + if only_one and srv and srv.success: + raise SystemExit + + except KeyboardInterrupt: + raise SystemExit + finally: + clf.close() + + raise SystemExit + +if __name__ == '__main__': + main() diff -Nru wpa-1.0/wpa_supplicant/examples/wps-ap-cli wpa-2.1/wpa_supplicant/examples/wps-ap-cli --- wpa-1.0/wpa_supplicant/examples/wps-ap-cli 1970-01-01 00:00:00.000000000 +0000 +++ wpa-2.1/wpa_supplicant/examples/wps-ap-cli 2014-02-04 11:23:35.000000000 +0000 @@ -0,0 +1,78 @@ +#!/bin/sh + +CLI=wpa_cli + +pbc() +{ + echo "Starting PBC mode" + echo "Push button on the station within two minutes" + if ! $CLI wps_pbc | grep -q OK; then + echo "Failed to enable PBC mode" + fi +} + +enter_pin() +{ + echo "Enter a PIN from a station to be enrolled to the network." + read -p "Enrollee PIN: " pin + cpin=`$CLI wps_check_pin "$pin" | tail -1` + if [ "$cpin" = "FAIL-CHECKSUM" ]; then + echo "Checksum digit is not valid" + read -p "Do you want to use this PIN (y/n)? " resp + case "$resp" in + y*) + cpin=`echo "$pin" | sed "s/[^1234567890]//g"` + ;; + *) + return 1 + ;; + esac + fi + if [ "$cpin" = "FAIL" ]; then + echo "Invalid PIN: $pin" + return 1 + fi + echo "Enabling Enrollee PIN: $cpin" + $CLI wps_pin any "$cpin" +} + +show_config() +{ + $CLI status wps +} + +main_menu() +{ + echo "WPS AP" + echo "------" + echo "1: Push button (activate PBC)" + echo "2: Enter Enrollee PIN" + echo "3: Show current configuration" + echo "0: Exit wps-ap-cli" + + read -p "Command: " cmd + + case "$cmd" in + 1) + pbc + ;; + 2) + enter_pin + ;; + 3) + show_config + ;; + 0) + exit 0 + ;; + *) + echo "Unknown command: $cmd" + ;; + esac + + echo + main_menu +} + + +main_menu diff -Nru wpa-1.0/wpa_supplicant/examples/wps-nfc.py wpa-2.1/wpa_supplicant/examples/wps-nfc.py --- wpa-1.0/wpa_supplicant/examples/wps-nfc.py 1970-01-01 00:00:00.000000000 +0000 +++ wpa-2.1/wpa_supplicant/examples/wps-nfc.py 2014-02-04 11:23:35.000000000 +0000 @@ -0,0 +1,460 @@ +#!/usr/bin/python +# +# Example nfcpy to wpa_supplicant wrapper for WPS NFC operations +# Copyright (c) 2012-2013, Jouni Malinen +# +# This software may be distributed under the terms of the BSD license. +# See README for more details. + +import os +import sys +import time +import random +import threading +import argparse + +import nfc +import nfc.ndef +import nfc.llcp +import nfc.handover + +import logging + +import wpaspy + +wpas_ctrl = '/var/run/wpa_supplicant' +srv = None +continue_loop = True +terminate_now = False + +def wpas_connect(): + ifaces = [] + 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 wpa_supplicant: ", error + return None + + if len(ifaces) < 1: + print "No wpa_supplicant control interface found" + return None + + for ctrl in ifaces: + try: + wpas = wpaspy.Ctrl(ctrl) + return wpas + except Exception, e: + pass + return None + + +def wpas_tag_read(message): + wpas = wpas_connect() + if (wpas == None): + return False + if "FAIL" in wpas.request("WPS_NFC_TAG_READ " + str(message).encode("hex")): + return False + return True + +def wpas_get_config_token(id=None): + wpas = wpas_connect() + if (wpas == None): + return None + if id: + ret = wpas.request("WPS_NFC_CONFIG_TOKEN NDEF " + id) + else: + ret = wpas.request("WPS_NFC_CONFIG_TOKEN NDEF") + if "FAIL" in ret: + return None + return ret.rstrip().decode("hex") + + +def wpas_get_er_config_token(uuid): + wpas = wpas_connect() + if (wpas == None): + return None + ret = wpas.request("WPS_ER_NFC_CONFIG_TOKEN NDEF " + uuid) + if "FAIL" in ret: + return None + return ret.rstrip().decode("hex") + + +def wpas_get_password_token(): + wpas = wpas_connect() + if (wpas == None): + return None + return wpas.request("WPS_NFC_TOKEN NDEF").rstrip().decode("hex") + + +def wpas_get_handover_req(): + wpas = wpas_connect() + if (wpas == None): + return None + return wpas.request("NFC_GET_HANDOVER_REQ NDEF WPS-CR").rstrip().decode("hex") + + +def wpas_get_handover_sel(uuid): + wpas = wpas_connect() + if (wpas == None): + return None + if uuid is None: + res = wpas.request("NFC_GET_HANDOVER_SEL NDEF WPS-CR").rstrip() + else: + res = wpas.request("NFC_GET_HANDOVER_SEL NDEF WPS-CR " + uuid).rstrip() + if "FAIL" in res: + return None + return res.decode("hex") + + +def wpas_report_handover(req, sel, type): + wpas = wpas_connect() + if (wpas == None): + return None + return wpas.request("NFC_REPORT_HANDOVER " + type + " WPS " + + str(req).encode("hex") + " " + + str(sel).encode("hex")) + + +class HandoverServer(nfc.handover.HandoverServer): + def __init__(self, llc): + super(HandoverServer, self).__init__(llc) + self.sent_carrier = None + self.ho_server_processing = False + self.success = False + + def process_request(self, request): + self.ho_server_processing = True + print "HandoverServer - request received" + try: + print "Parsed handover request: " + request.pretty() + except Exception, e: + print e + + sel = nfc.ndef.HandoverSelectMessage(version="1.2") + + for carrier in request.carriers: + print "Remote carrier type: " + carrier.type + if carrier.type == "application/vnd.wfa.wsc": + print "WPS carrier type match - add WPS carrier record" + data = wpas_get_handover_sel(self.uuid) + if data is None: + print "Could not get handover select carrier record from wpa_supplicant" + continue + print "Handover select carrier record from wpa_supplicant:" + print data.encode("hex") + self.sent_carrier = data + wpas_report_handover(carrier.record, self.sent_carrier, "RESP") + + message = nfc.ndef.Message(data); + sel.add_carrier(message[0], "active", message[1:]) + + print "Handover select:" + try: + print sel.pretty() + except Exception, e: + print e + print str(sel).encode("hex") + + print "Sending handover select" + self.success = True + return sel + + +def wps_handover_init(llc): + print "Trying to initiate WPS handover" + + data = wpas_get_handover_req() + if (data == None): + print "Could not get handover request carrier record from wpa_supplicant" + return + print "Handover request carrier record from wpa_supplicant: " + data.encode("hex") + + message = nfc.ndef.HandoverRequestMessage(version="1.2") + message.nonce = random.randint(0, 0xffff) + datamsg = nfc.ndef.Message(data) + message.add_carrier(datamsg[0], "active", datamsg[1:]) + + print "Handover request:" + try: + print message.pretty() + except Exception, e: + print e + print str(message).encode("hex") + + client = nfc.handover.HandoverClient(llc) + try: + print "Trying handover"; + client.connect() + print "Connected for handover" + except nfc.llcp.ConnectRefused: + print "Handover connection refused" + client.close() + return + + print "Sending handover request" + + if not client.send(message): + print "Failed to send handover request" + + print "Receiving handover response" + message = client._recv() + if message is None: + print "No response received" + client.close() + return + if message.type != "urn:nfc:wkt:Hs": + print "Response was not Hs - received: " + message.type + client.close() + return + + print "Received message" + try: + print message.pretty() + except Exception, e: + print e + print str(message).encode("hex") + message = nfc.ndef.HandoverSelectMessage(message) + print "Handover select received" + try: + print message.pretty() + except Exception, e: + print e + + for carrier in message.carriers: + print "Remote carrier type: " + carrier.type + if carrier.type == "application/vnd.wfa.wsc": + print "WPS carrier type match - send to wpa_supplicant" + wpas_report_handover(data, carrier.record, "INIT") + # nfcpy does not support the new format.. + #wifi = nfc.ndef.WifiConfigRecord(carrier.record) + #print wifi.pretty() + + print "Remove peer" + client.close() + print "Done with handover" + global only_one + if only_one: + global continue_loop + continue_loop = False + + global no_wait + if no_wait: + print "Trying to exit.." + global terminate_now + terminate_now = True + +def wps_tag_read(tag, wait_remove=True): + success = False + if len(tag.ndef.message): + for record in tag.ndef.message: + print "record type " + record.type + if record.type == "application/vnd.wfa.wsc": + print "WPS tag - send to wpa_supplicant" + success = wpas_tag_read(tag.ndef.message) + break + else: + print "Empty tag" + + if wait_remove: + print "Remove tag" + while tag.is_present: + time.sleep(0.1) + + return success + + +def rdwr_connected_write(tag): + print "Tag found - writing" + global write_data + tag.ndef.message = str(write_data) + print "Done - remove tag" + global only_one + if only_one: + global continue_loop + continue_loop = False + global write_wait_remove + while write_wait_remove and tag.is_present: + time.sleep(0.1) + +def wps_write_config_tag(clf, id=None, wait_remove=True): + print "Write WPS config token" + global write_data, write_wait_remove + write_wait_remove = wait_remove + write_data = wpas_get_config_token(id) + if write_data == None: + print "Could not get WPS config token from wpa_supplicant" + sys.exit(1) + return + print "Touch an NFC tag" + clf.connect(rdwr={'on-connect': rdwr_connected_write}) + + +def wps_write_er_config_tag(clf, uuid, wait_remove=True): + print "Write WPS ER config token" + global write_data, write_wait_remove + write_wait_remove = wait_remove + write_data = wpas_get_er_config_token(uuid) + if write_data == None: + print "Could not get WPS config token from wpa_supplicant" + return + + print "Touch an NFC tag" + clf.connect(rdwr={'on-connect': rdwr_connected_write}) + + +def wps_write_password_tag(clf, wait_remove=True): + print "Write WPS password token" + global write_data, write_wait_remove + write_wait_remove = wait_remove + write_data = wpas_get_password_token() + if write_data == None: + print "Could not get WPS password token from wpa_supplicant" + return + + print "Touch an NFC tag" + clf.connect(rdwr={'on-connect': rdwr_connected_write}) + + +def rdwr_connected(tag): + global only_one, no_wait + print "Tag connected: " + str(tag) + + if tag.ndef: + print "NDEF tag: " + tag.type + try: + print tag.ndef.message.pretty() + except Exception, e: + print e + success = wps_tag_read(tag, not only_one) + if only_one and success: + global continue_loop + continue_loop = False + else: + print "Not an NDEF tag - remove tag" + + return not no_wait + + +def llcp_worker(llc): + global arg_uuid + if arg_uuid is None: + wps_handover_init(llc) + print "Exiting llcp_worker thread" + return + + global srv + global wait_connection + while not wait_connection and srv.sent_carrier is None: + if srv.ho_server_processing: + time.sleep(0.025) + +def llcp_startup(clf, llc): + global arg_uuid + if arg_uuid: + print "Start LLCP server" + global srv + srv = HandoverServer(llc) + if arg_uuid is "ap": + print "Trying to handle WPS handover" + srv.uuid = None + else: + print "Trying to handle WPS handover with AP " + arg_uuid + srv.uuid = arg_uuid + return llc + +def llcp_connected(llc): + print "P2P LLCP connected" + global wait_connection + wait_connection = False + global arg_uuid + if arg_uuid: + global srv + srv.start() + else: + threading.Thread(target=llcp_worker, args=(llc,)).start() + print "llcp_connected returning" + return True + + +def terminate_loop(): + global terminate_now + return terminate_now + +def main(): + clf = nfc.ContactlessFrontend() + + parser = argparse.ArgumentParser(description='nfcpy to wpa_supplicant integration for WPS NFC operations') + parser.add_argument('-d', const=logging.DEBUG, default=logging.INFO, + action='store_const', dest='loglevel', + help='verbose debug output') + parser.add_argument('-q', const=logging.WARNING, action='store_const', + dest='loglevel', help='be quiet') + parser.add_argument('--only-one', '-1', action='store_true', + help='run only one operation and exit') + parser.add_argument('--no-wait', action='store_true', + help='do not wait for tag to be removed before exiting') + parser.add_argument('--uuid', + help='UUID of an AP (used for WPS ER operations)') + parser.add_argument('--id', + help='network id (used for WPS ER operations)') + parser.add_argument('command', choices=['write-config', + 'write-er-config', + 'write-password'], + nargs='?') + args = parser.parse_args() + + global arg_uuid + arg_uuid = args.uuid + + global only_one + only_one = args.only_one + + global no_wait + no_wait = args.no_wait + + logging.basicConfig(level=args.loglevel) + + try: + if not clf.open("usb"): + print "Could not open connection with an NFC device" + raise SystemExit + + if args.command == "write-config": + wps_write_config_tag(clf, id=args.id, wait_remove=not args.no_wait) + raise SystemExit + + if args.command == "write-er-config": + wps_write_er_config_tag(clf, args.uuid, wait_remove=not args.no_wait) + raise SystemExit + + if args.command == "write-password": + wps_write_password_tag(clf, wait_remove=not args.no_wait) + raise SystemExit + + global continue_loop + while continue_loop: + print "Waiting for a tag or peer to be touched" + wait_connection = True + try: + if not clf.connect(rdwr={'on-connect': rdwr_connected}, + llcp={'on-startup': llcp_startup, + 'on-connect': llcp_connected}, + terminate=terminate_loop): + break + except Exception, e: + print "clf.connect failed" + + global srv + if only_one and srv and srv.success: + raise SystemExit + + except KeyboardInterrupt: + raise SystemExit + finally: + clf.close() + + raise SystemExit + +if __name__ == '__main__': + main() diff -Nru wpa-1.0/wpa_supplicant/gas_query.c wpa-2.1/wpa_supplicant/gas_query.c --- wpa-1.0/wpa_supplicant/gas_query.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/gas_query.c 2014-02-04 11:23:35.000000000 +0000 @@ -1,16 +1,11 @@ /* * Generic advertisement service (GAS) query * Copyright (c) 2009, Atheros Communications - * Copyright (c) 2011, Qualcomm Atheros + * Copyright (c) 2011-2014, Qualcomm Atheros, Inc. + * Copyright (c) 2011-2014, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -19,17 +14,24 @@ #include "utils/eloop.h" #include "common/ieee802_11_defs.h" #include "common/gas.h" +#include "common/wpa_ctrl.h" +#include "rsn_supp/wpa.h" #include "wpa_supplicant_i.h" #include "driver_i.h" #include "offchannel.h" #include "gas_query.h" -#define GAS_QUERY_TIMEOUT 5 +/** GAS query timeout in seconds */ +#define GAS_QUERY_TIMEOUT_PERIOD 2 +/** + * struct gas_query_pending - Pending GAS query + */ struct gas_query_pending { struct dl_list list; + struct gas_query *gas; u8 addr[ETH_ALEN]; u8 dialog_token; u8 next_frag_id; @@ -37,6 +39,7 @@ unsigned int offchannel_tx_started:1; int freq; u16 status_code; + struct wpabuf *req; struct wpabuf *adv_proto; struct wpabuf *resp; void (*cb)(void *ctx, const u8 *dst, u8 dialog_token, @@ -46,9 +49,14 @@ void *ctx; }; +/** + * struct gas_query - Internal GAS query data + */ struct gas_query { struct wpa_supplicant *wpa_s; struct dl_list pending; /* struct gas_query_pending */ + struct gas_query_pending *current; + struct wpa_radio_work *work; }; @@ -56,6 +64,11 @@ static void gas_query_timeout(void *eloop_data, void *user_ctx); +/** + * gas_query_init - Initialize GAS query component + * @wpa_s: Pointer to wpa_supplicant data + * Returns: Pointer to GAS query data or %NULL on failure + */ struct gas_query * gas_query_init(struct wpa_supplicant *wpa_s) { struct gas_query *gas; @@ -71,10 +84,58 @@ } +static const char * gas_result_txt(enum gas_query_result result) +{ + switch (result) { + case GAS_QUERY_SUCCESS: + return "SUCCESS"; + case GAS_QUERY_FAILURE: + return "FAILURE"; + case GAS_QUERY_TIMEOUT: + return "TIMEOUT"; + case GAS_QUERY_PEER_ERROR: + return "PEER_ERROR"; + case GAS_QUERY_INTERNAL_ERROR: + return "INTERNAL_ERROR"; + case GAS_QUERY_CANCELLED: + return "CANCELLED"; + case GAS_QUERY_DELETED_AT_DEINIT: + return "DELETED_AT_DEINIT"; + } + + return "N/A"; +} + + +static void gas_query_free(struct gas_query_pending *query, int del_list) +{ + struct gas_query *gas = query->gas; + + if (del_list) + dl_list_del(&query->list); + + if (gas->work && gas->work->ctx == query) { + radio_work_done(gas->work); + gas->work = NULL; + } + + wpabuf_free(query->req); + wpabuf_free(query->adv_proto); + wpabuf_free(query->resp); + os_free(query); +} + + static void gas_query_done(struct gas_query *gas, struct gas_query_pending *query, enum gas_query_result result) { + wpa_msg(gas->wpa_s, 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; if (query->offchannel_tx_started) offchannel_send_action_done(gas->wpa_s); eloop_cancel_timeout(gas_query_tx_comeback_timeout, gas, query); @@ -82,12 +143,14 @@ dl_list_del(&query->list); query->cb(query->ctx, query->addr, query->dialog_token, result, query->adv_proto, query->resp, query->status_code); - wpabuf_free(query->adv_proto); - wpabuf_free(query->resp); - os_free(query); + gas_query_free(query, 0); } +/** + * gas_query_deinit - Deinitialize GAS query component + * @gas: GAS query data from gas_query_init() + */ void gas_query_deinit(struct gas_query *gas) { struct gas_query_pending *query, *next; @@ -128,17 +191,70 @@ } +static void gas_query_tx_status(struct wpa_supplicant *wpa_s, + unsigned int freq, const u8 *dst, + const u8 *src, const u8 *bssid, + const u8 *data, size_t data_len, + enum offchannel_send_action_result result) +{ + struct gas_query_pending *query; + struct gas_query *gas = wpa_s->gas; + + if (gas->current == NULL) { + wpa_printf(MSG_DEBUG, "GAS: Unexpected TX status: freq=%u dst=" + MACSTR " result=%d - no query in progress", + freq, MAC2STR(dst), result); + return; + } + + query = gas->current; + + wpa_printf(MSG_DEBUG, "GAS: TX status: freq=%u dst=" MACSTR + " result=%d query=%p dialog_token=%u", + freq, MAC2STR(dst), result, query, query->dialog_token); + if (os_memcmp(dst, query->addr, ETH_ALEN) != 0) { + wpa_printf(MSG_DEBUG, "GAS: TX status for unexpected destination"); + return; + } + + if (result == OFFCHANNEL_SEND_ACTION_SUCCESS) { + eloop_cancel_timeout(gas_query_timeout, gas, query); + eloop_register_timeout(GAS_QUERY_TIMEOUT_PERIOD, 0, + gas_query_timeout, gas, query); + } + if (result == OFFCHANNEL_SEND_ACTION_FAILED) { + eloop_cancel_timeout(gas_query_timeout, gas, query); + eloop_register_timeout(0, 0, gas_query_timeout, gas, query); + } +} + + +static int pmf_in_use(struct wpa_supplicant *wpa_s, const u8 *addr) +{ + if (wpa_s->current_ssid == NULL || + wpa_s->wpa_state < WPA_4WAY_HANDSHAKE || + os_memcmp(addr, wpa_s->bssid, ETH_ALEN) != 0) + return 0; + return wpa_sm_pmf_enabled(wpa_s->wpa); +} + + static int gas_query_tx(struct gas_query *gas, struct gas_query_pending *query, struct wpabuf *req) { - int res; + int res, prot = pmf_in_use(gas->wpa_s, query->addr); + wpa_printf(MSG_DEBUG, "GAS: Send action frame to " MACSTR " len=%u " - "freq=%d", MAC2STR(query->addr), - (unsigned int) wpabuf_len(req), query->freq); + "freq=%d prot=%d", MAC2STR(query->addr), + (unsigned int) wpabuf_len(req), query->freq, prot); + if (prot) { + u8 *categ = wpabuf_mhead_u8(req); + *categ = WLAN_ACTION_PROTECTED_DUAL; + } res = offchannel_send_action(gas->wpa_s, query->freq, query->addr, gas->wpa_s->own_addr, query->addr, wpabuf_head(req), wpabuf_len(req), 1000, - NULL, 0); + gas_query_tx_status, 0); if (res == 0) query->offchannel_tx_started = 1; return res; @@ -261,6 +377,11 @@ 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_PEER_ERROR); return; } @@ -280,17 +401,42 @@ } +/** + * gas_query_rx - Indicate reception of a Public Action or Protected Dual frame + * @gas: GAS query data from gas_query_init() + * @da: Destination MAC address of the Action frame + * @sa: Source MAC address of the Action frame + * @bssid: BSSID 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_rx(struct gas_query *gas, const u8 *da, const u8 *sa, - const u8 *bssid, const u8 *data, size_t len, int freq) + const u8 *bssid, 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; if (gas == NULL || len < 4) return -1; + prot = categ == WLAN_ACTION_PROTECTED_DUAL; + pmf = pmf_in_use(gas->wpa_s, bssid); + 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; + } + pos = data; action = *pos++; dialog_token = *pos++; @@ -400,8 +546,9 @@ struct gas_query *gas = eloop_data; struct gas_query_pending *query = user_ctx; - wpa_printf(MSG_DEBUG, "GAS: No response received for query to " MACSTR, - MAC2STR(query->addr)); + 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_TIMEOUT); } @@ -420,6 +567,45 @@ } +static void gas_query_start_cb(struct wpa_radio_work *work, int deinit) +{ + struct gas_query_pending *query = work->ctx; + struct gas_query *gas = query->gas; + + if (deinit) { + gas_query_free(query, 1); + return; + } + + gas->work = work; + + if (gas_query_tx(gas, query, query->req) < 0) { + wpa_printf(MSG_DEBUG, "GAS: Failed to send Action frame to " + MACSTR, MAC2STR(query->addr)); + gas_query_free(query, 1); + 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); + +} + + +/** + * gas_query_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_req(struct gas_query *gas, const u8 *dst, int freq, struct wpabuf *req, void (*cb)(void *ctx, const u8 *dst, u8 dialog_token, @@ -430,46 +616,56 @@ { struct gas_query_pending *query; int dialog_token; + static int next_start = 0; if (wpabuf_len(req) < 3) return -1; for (dialog_token = 0; dialog_token < 256; dialog_token++) { - if (gas_query_dialog_token_available(gas, dst, 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; 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_printf(MSG_DEBUG, "GAS: Starting request for " MACSTR - " dialog_token %u", MAC2STR(dst), dialog_token); - if (gas_query_tx(gas, query, req) < 0) { - wpa_printf(MSG_DEBUG, "GAS: Failed to send Action frame to " - MACSTR, MAC2STR(query->addr)); - os_free(query); + wpa_msg(gas->wpa_s, MSG_INFO, GAS_QUERY_START "addr=" MACSTR + " dialog_token=%u freq=%d", + MAC2STR(query->addr), query->dialog_token, query->freq); + + if (radio_add_work(gas->wpa_s, freq, "gas-query", 0, gas_query_start_cb, + query) < 0) { + gas_query_free(query, 1); return -1; } - eloop_register_timeout(GAS_QUERY_TIMEOUT, 0, gas_query_timeout, - gas, query); - return dialog_token; } +/** + * gas_query_cancel - Cancel a pending GAS query + * @gas: GAS query data from gas_query_init() + * @dst: Destination MAC address for the query + * @dialog_token: Dialog token from gas_query_req() + */ void gas_query_cancel(struct gas_query *gas, const u8 *dst, u8 dialog_token) { struct gas_query_pending *query; diff -Nru wpa-1.0/wpa_supplicant/gas_query.h wpa-2.1/wpa_supplicant/gas_query.h --- wpa-1.0/wpa_supplicant/gas_query.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/gas_query.h 2014-02-04 11:23:35.000000000 +0000 @@ -3,14 +3,8 @@ * Copyright (c) 2009, Atheros Communications * Copyright (c) 2011, Qualcomm Atheros * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef GAS_QUERY_H @@ -23,8 +17,12 @@ struct gas_query * gas_query_init(struct wpa_supplicant *wpa_s); void gas_query_deinit(struct gas_query *gas); int gas_query_rx(struct gas_query *gas, const u8 *da, const u8 *sa, - const u8 *bssid, const u8 *data, size_t len, int freq); + const u8 *bssid, u8 categ, const u8 *data, size_t len, + int freq); +/** + * enum gas_query_result - GAS query result + */ enum gas_query_result { GAS_QUERY_SUCCESS, GAS_QUERY_FAILURE, diff -Nru wpa-1.0/wpa_supplicant/hs20_supplicant.c wpa-2.1/wpa_supplicant/hs20_supplicant.c --- wpa-1.0/wpa_supplicant/hs20_supplicant.c 1970-01-01 00:00:00.000000000 +0000 +++ wpa-2.1/wpa_supplicant/hs20_supplicant.c 2014-02-04 11:23:35.000000000 +0000 @@ -0,0 +1,214 @@ +/* + * Copyright (c) 2009, Atheros Communications, Inc. + * Copyright (c) 2011-2012, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "eloop.h" +#include "common/ieee802_11_common.h" +#include "common/ieee802_11_defs.h" +#include "common/gas.h" +#include "common/wpa_ctrl.h" +#include "wpa_supplicant_i.h" +#include "driver_i.h" +#include "config.h" +#include "bss.h" +#include "gas_query.h" +#include "interworking.h" +#include "hs20_supplicant.h" + + +void wpas_hs20_add_indication(struct wpabuf *buf) +{ + wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC); + wpabuf_put_u8(buf, 5); + wpabuf_put_be24(buf, OUI_WFA); + wpabuf_put_u8(buf, HS20_INDICATION_OUI_TYPE); + wpabuf_put_u8(buf, 0x00); /* Hotspot Configuration */ +} + + +int is_hs20_network(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, + struct wpa_bss *bss) +{ + if (!wpa_s->conf->hs20 || !ssid) + return 0; + + if (ssid->parent_cred) + return 1; + + if (bss && !wpa_bss_get_vendor_ie(bss, HS20_IE_VENDOR_TYPE)) + return 0; + + /* + * This may catch some non-Hotspot 2.0 cases, but it is safer to do that + * than cause Hotspot 2.0 connections without indication element getting + * added. Non-Hotspot 2.0 APs should ignore the unknown vendor element. + */ + + if (!(ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X)) + return 0; + if (!(ssid->pairwise_cipher & WPA_CIPHER_CCMP)) + return 0; + if (ssid->proto != WPA_PROTO_RSN) + return 0; + + return 1; +} + + +struct wpabuf * hs20_build_anqp_req(u32 stypes, const u8 *payload, + size_t payload_len) +{ + struct wpabuf *buf; + u8 *len_pos; + + buf = gas_anqp_build_initial_req(0, 100 + payload_len); + if (buf == NULL) + return NULL; + + len_pos = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC); + wpabuf_put_be24(buf, OUI_WFA); + wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE); + if (stypes == BIT(HS20_STYPE_NAI_HOME_REALM_QUERY)) { + wpabuf_put_u8(buf, HS20_STYPE_NAI_HOME_REALM_QUERY); + wpabuf_put_u8(buf, 0); /* Reserved */ + if (payload) + wpabuf_put_data(buf, payload, payload_len); + } else { + u8 i; + wpabuf_put_u8(buf, HS20_STYPE_QUERY_LIST); + wpabuf_put_u8(buf, 0); /* Reserved */ + for (i = 0; i < 32; i++) { + if (stypes & BIT(i)) + wpabuf_put_u8(buf, i); + } + } + gas_anqp_set_element_len(buf, len_pos); + + gas_anqp_set_len(buf); + + return buf; +} + + +int hs20_anqp_send_req(struct wpa_supplicant *wpa_s, const u8 *dst, u32 stypes, + const u8 *payload, size_t payload_len) +{ + struct wpabuf *buf; + int ret = 0; + int freq; + struct wpa_bss *bss; + int res; + + freq = wpa_s->assoc_freq; + bss = wpa_bss_get_bssid(wpa_s, dst); + if (bss) { + wpa_bss_anqp_unshare_alloc(bss); + freq = bss->freq; + } + if (freq <= 0) + return -1; + + wpa_printf(MSG_DEBUG, "HS20: ANQP Query Request to " MACSTR " for " + "subtypes 0x%x", MAC2STR(dst), stypes); + + buf = hs20_build_anqp_req(stypes, payload, payload_len); + if (buf == NULL) + return -1; + + res = gas_query_req(wpa_s->gas, dst, freq, buf, anqp_resp_cb, wpa_s); + if (res < 0) { + wpa_printf(MSG_DEBUG, "ANQP: Failed to send Query Request"); + wpabuf_free(buf); + ret = -1; + } else + wpa_printf(MSG_DEBUG, "ANQP: Query started with dialog token " + "%u", res); + + return ret; +} + + +void hs20_parse_rx_hs20_anqp_resp(struct wpa_supplicant *wpa_s, + const u8 *sa, const u8 *data, size_t slen) +{ + const u8 *pos = data; + u8 subtype; + struct wpa_bss *bss = wpa_bss_get_bssid(wpa_s, sa); + struct wpa_bss_anqp *anqp = NULL; + + if (slen < 2) + return; + + if (bss) + anqp = bss->anqp; + + subtype = *pos++; + slen--; + + pos++; /* Reserved */ + slen--; + + switch (subtype) { + case HS20_STYPE_CAPABILITY_LIST: + wpa_msg(wpa_s, MSG_INFO, "RX-HS20-ANQP " MACSTR + " HS Capability List", MAC2STR(sa)); + wpa_hexdump_ascii(MSG_DEBUG, "HS Capability List", pos, slen); + break; + case HS20_STYPE_OPERATOR_FRIENDLY_NAME: + wpa_msg(wpa_s, MSG_INFO, "RX-HS20-ANQP " MACSTR + " Operator Friendly Name", MAC2STR(sa)); + wpa_hexdump_ascii(MSG_DEBUG, "oper friendly name", pos, slen); + if (anqp) { + wpabuf_free(anqp->hs20_operator_friendly_name); + anqp->hs20_operator_friendly_name = + wpabuf_alloc_copy(pos, slen); + } + break; + case HS20_STYPE_WAN_METRICS: + wpa_hexdump(MSG_DEBUG, "WAN Metrics", pos, slen); + if (slen < 13) { + wpa_dbg(wpa_s, MSG_DEBUG, "HS 2.0: Too short WAN " + "Metrics value from " MACSTR, MAC2STR(sa)); + break; + } + wpa_msg(wpa_s, MSG_INFO, "RX-HS20-ANQP " MACSTR + " WAN Metrics %02x:%u:%u:%u:%u:%u", MAC2STR(sa), + pos[0], WPA_GET_LE32(pos + 1), WPA_GET_LE32(pos + 5), + pos[9], pos[10], WPA_GET_LE16(pos + 11)); + if (anqp) { + wpabuf_free(anqp->hs20_wan_metrics); + anqp->hs20_wan_metrics = wpabuf_alloc_copy(pos, slen); + } + break; + case HS20_STYPE_CONNECTION_CAPABILITY: + wpa_msg(wpa_s, MSG_INFO, "RX-HS20-ANQP " MACSTR + " Connection Capability", MAC2STR(sa)); + wpa_hexdump_ascii(MSG_DEBUG, "conn capability", pos, slen); + if (anqp) { + wpabuf_free(anqp->hs20_connection_capability); + anqp->hs20_connection_capability = + wpabuf_alloc_copy(pos, slen); + } + break; + case HS20_STYPE_OPERATING_CLASS: + wpa_msg(wpa_s, MSG_INFO, "RX-HS20-ANQP " MACSTR + " Operating Class", MAC2STR(sa)); + wpa_hexdump_ascii(MSG_DEBUG, "Operating Class", pos, slen); + if (anqp) { + wpabuf_free(anqp->hs20_operating_class); + anqp->hs20_operating_class = + wpabuf_alloc_copy(pos, slen); + } + break; + default: + wpa_printf(MSG_DEBUG, "HS20: Unsupported subtype %u", subtype); + break; + } +} diff -Nru wpa-1.0/wpa_supplicant/hs20_supplicant.h wpa-2.1/wpa_supplicant/hs20_supplicant.h --- wpa-1.0/wpa_supplicant/hs20_supplicant.h 1970-01-01 00:00:00.000000000 +0000 +++ wpa-2.1/wpa_supplicant/hs20_supplicant.h 2014-02-04 11:23:35.000000000 +0000 @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2011-2012, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef HS20_SUPPLICANT_H +#define HS20_SUPPLICANT_H + +void wpas_hs20_add_indication(struct wpabuf *buf); + +int hs20_anqp_send_req(struct wpa_supplicant *wpa_s, const u8 *dst, u32 stypes, + const u8 *payload, size_t payload_len); +struct wpabuf * hs20_build_anqp_req(u32 stypes, const u8 *payload, + size_t payload_len); +void hs20_parse_rx_hs20_anqp_resp(struct wpa_supplicant *wpa_s, + const u8 *sa, const u8 *data, size_t slen); +int is_hs20_network(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, + struct wpa_bss *bss); + +#endif /* HS20_SUPPLICANT_H */ diff -Nru wpa-1.0/wpa_supplicant/ibss_rsn.c wpa-2.1/wpa_supplicant/ibss_rsn.c --- wpa-1.0/wpa_supplicant/ibss_rsn.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/ibss_rsn.c 2014-02-04 11:23:35.000000000 +0000 @@ -1,31 +1,44 @@ /* * wpa_supplicant - IBSS RSN - * Copyright (c) 2009, Jouni Malinen + * Copyright (c) 2009-2013, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" #include "common.h" +#include "common/wpa_ctrl.h" +#include "utils/eloop.h" #include "l2_packet/l2_packet.h" #include "rsn_supp/wpa.h" #include "rsn_supp/wpa_ie.h" #include "ap/wpa_auth.h" #include "wpa_supplicant_i.h" #include "driver_i.h" +#include "common/ieee802_11_defs.h" #include "ibss_rsn.h" +static void ibss_rsn_auth_timeout(void *eloop_ctx, void *timeout_ctx); + + +static struct ibss_rsn_peer * ibss_rsn_get_peer(struct ibss_rsn *ibss_rsn, + const u8 *addr) +{ + struct ibss_rsn_peer *peer; + + for (peer = ibss_rsn->peers; peer; peer = peer->next) + if (os_memcmp(addr, peer->addr, ETH_ALEN) == 0) + break; + return peer; +} + + static void ibss_rsn_free(struct ibss_rsn_peer *peer) { + eloop_cancel_timeout(ibss_rsn_auth_timeout, peer, NULL); wpa_auth_sta_deinit(peer->auth); wpa_sm_deinit(peer->supp); os_free(peer); @@ -107,6 +120,22 @@ } +static void ibss_check_rsn_completed(struct ibss_rsn_peer *peer) +{ + struct wpa_supplicant *wpa_s = peer->ibss_rsn->wpa_s; + + if ((peer->authentication_status & + (IBSS_RSN_SET_PTK_SUPP | IBSS_RSN_SET_PTK_AUTH)) != + (IBSS_RSN_SET_PTK_SUPP | IBSS_RSN_SET_PTK_AUTH)) + return; + if (peer->authentication_status & IBSS_RSN_REPORTED_PTK) + return; + peer->authentication_status |= IBSS_RSN_REPORTED_PTK; + wpa_msg(wpa_s, MSG_INFO, IBSS_RSN_COMPLETED MACSTR, + MAC2STR(peer->addr)); +} + + static int supp_set_key(void *ctx, enum wpa_alg alg, const u8 *addr, int key_idx, int set_tx, const u8 *seq, size_t seq_len, @@ -121,6 +150,8 @@ wpa_hexdump_key(MSG_DEBUG, "SUPP: set_key - key", key, key_len); if (key_idx == 0) { + peer->authentication_status |= IBSS_RSN_SET_PTK_SUPP; + ibss_check_rsn_completed(peer); /* * In IBSS RSN, the pairwise key from the 4-way handshake * initiated by the peer with highest MAC address is used. @@ -168,8 +199,8 @@ } -int ibss_rsn_supp_init(struct ibss_rsn_peer *peer, const u8 *own_addr, - const u8 *psk) +static int ibss_rsn_supp_init(struct ibss_rsn_peer *peer, const u8 *own_addr, + const u8 *psk) { struct wpa_sm_ctx *ctx = os_zalloc(sizeof(*ctx)); if (ctx == NULL) @@ -226,7 +257,8 @@ } -static const u8 * auth_get_psk(void *ctx, const u8 *addr, const u8 *prev_psk) +static const u8 * auth_get_psk(void *ctx, const u8 *addr, + const u8 *p2p_dev_addr, const u8 *prev_psk) { struct ibss_rsn *ibss_rsn = ctx; wpa_printf(MSG_DEBUG, "AUTH: %s (addr=" MACSTR " prev_psk=%p)", @@ -274,6 +306,15 @@ wpa_hexdump_key(MSG_DEBUG, "AUTH: set_key - key", key, key_len); if (idx == 0) { + if (addr) { + struct ibss_rsn_peer *peer; + peer = ibss_rsn_get_peer(ibss_rsn, addr); + if (peer) { + peer->authentication_status |= + IBSS_RSN_SET_PTK_AUTH; + ibss_check_rsn_completed(peer); + } + } /* * In IBSS RSN, the pairwise key from the 4-way handshake * initiated by the peer with highest MAC address is used. @@ -290,6 +331,13 @@ } +static void ibss_rsn_disconnect(void *ctx, const u8 *addr, u16 reason) +{ + struct ibss_rsn *ibss_rsn = ctx; + wpa_drv_sta_deauth(ibss_rsn->wpa_s, addr, reason); +} + + static int auth_for_each_sta(void *ctx, int (*cb)(struct wpa_state_machine *sm, void *ctx), void *cb_ctx) @@ -308,6 +356,53 @@ } +static void ibss_set_sta_authorized(struct ibss_rsn *ibss_rsn, + struct ibss_rsn_peer *peer, int authorized) +{ + int res; + + if (authorized) { + res = wpa_drv_sta_set_flags(ibss_rsn->wpa_s, peer->addr, + WPA_STA_AUTHORIZED, + WPA_STA_AUTHORIZED, ~0); + wpa_printf(MSG_DEBUG, "AUTH: " MACSTR " authorizing port", + MAC2STR(peer->addr)); + } else { + res = wpa_drv_sta_set_flags(ibss_rsn->wpa_s, peer->addr, + 0, 0, ~WPA_STA_AUTHORIZED); + wpa_printf(MSG_DEBUG, "AUTH: " MACSTR " unauthorizing port", + MAC2STR(peer->addr)); + } + + if (res && errno != ENOENT) { + wpa_printf(MSG_DEBUG, "Could not set station " MACSTR " flags " + "for kernel driver (errno=%d)", + MAC2STR(peer->addr), errno); + } +} + + +static void auth_set_eapol(void *ctx, const u8 *addr, + wpa_eapol_variable var, int value) +{ + struct ibss_rsn *ibss_rsn = ctx; + struct ibss_rsn_peer *peer = ibss_rsn_get_peer(ibss_rsn, addr); + + if (peer == NULL) + return; + + switch (var) { + case WPA_EAPOL_authorized: + ibss_set_sta_authorized(ibss_rsn, peer, value); + break; + default: + /* do not handle any other event */ + wpa_printf(MSG_DEBUG, "AUTH: eapol event not handled %d", var); + break; + } +} + + static int ibss_rsn_auth_init_group(struct ibss_rsn *ibss_rsn, const u8 *own_addr) { @@ -328,10 +423,12 @@ os_memset(&cb, 0, sizeof(cb)); cb.ctx = ibss_rsn; cb.logger = auth_logger; + cb.set_eapol = auth_set_eapol; cb.send_eapol = auth_send_eapol; cb.get_psk = auth_get_psk; cb.set_key = auth_set_key; cb.for_each_sta = auth_for_each_sta; + cb.disconnect = ibss_rsn_disconnect; ibss_rsn->auth_group = wpa_init(own_addr, &conf, &cb); if (ibss_rsn->auth_group == NULL) { @@ -348,7 +445,7 @@ static int ibss_rsn_auth_init(struct ibss_rsn *ibss_rsn, struct ibss_rsn_peer *peer) { - peer->auth = wpa_auth_sta_init(ibss_rsn->auth_group, peer->addr); + peer->auth = wpa_auth_sta_init(ibss_rsn->auth_group, peer->addr, NULL); if (peer->auth == NULL) { wpa_printf(MSG_DEBUG, "AUTH: wpa_auth_sta_init() failed"); return -1; @@ -376,47 +473,152 @@ } -int ibss_rsn_start(struct ibss_rsn *ibss_rsn, const u8 *addr) +static int ibss_rsn_send_auth(struct ibss_rsn *ibss_rsn, const u8 *da, int seq) { - struct ibss_rsn_peer *peer; + struct ieee80211_mgmt auth; + const size_t auth_length = IEEE80211_HDRLEN + sizeof(auth.u.auth); + struct wpa_supplicant *wpa_s = ibss_rsn->wpa_s; - if (ibss_rsn == NULL) + if (wpa_s->driver->send_frame == NULL) return -1; - for (peer = ibss_rsn->peers; peer; peer = peer->next) { - if (os_memcmp(addr, peer->addr, ETH_ALEN) == 0) { - wpa_printf(MSG_DEBUG, "RSN: IBSS Authenticator and " - "Supplicant for peer " MACSTR " already " - "running", MAC2STR(addr)); - return 0; - } + os_memset(&auth, 0, sizeof(auth)); + + auth.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, + WLAN_FC_STYPE_AUTH); + os_memcpy(auth.da, da, ETH_ALEN); + os_memcpy(auth.sa, wpa_s->own_addr, ETH_ALEN); + os_memcpy(auth.bssid, wpa_s->bssid, ETH_ALEN); + + auth.u.auth.auth_alg = host_to_le16(WLAN_AUTH_OPEN); + auth.u.auth.auth_transaction = host_to_le16(seq); + auth.u.auth.status_code = host_to_le16(WLAN_STATUS_SUCCESS); + + wpa_printf(MSG_DEBUG, "RSN: IBSS TX Auth frame (SEQ %d) to " MACSTR, + seq, MAC2STR(da)); + + return wpa_s->driver->send_frame(wpa_s->drv_priv, (u8 *) &auth, + auth_length, 0); +} + + +static int ibss_rsn_is_auth_started(struct ibss_rsn_peer * peer) +{ + return peer->authentication_status & + (IBSS_RSN_AUTH_BY_US | IBSS_RSN_AUTH_EAPOL_BY_US); +} + + +static struct ibss_rsn_peer * +ibss_rsn_peer_init(struct ibss_rsn *ibss_rsn, const u8 *addr) +{ + struct ibss_rsn_peer *peer; + if (ibss_rsn == NULL) + return NULL; + + peer = ibss_rsn_get_peer(ibss_rsn, addr); + if (peer) { + wpa_printf(MSG_DEBUG, "RSN: IBSS Supplicant for peer "MACSTR + " already running", MAC2STR(addr)); + return peer; } - wpa_printf(MSG_DEBUG, "RSN: Starting IBSS Authenticator and " - "Supplicant for peer " MACSTR, MAC2STR(addr)); + wpa_printf(MSG_DEBUG, "RSN: Starting IBSS Supplicant for peer "MACSTR, + MAC2STR(addr)); peer = os_zalloc(sizeof(*peer)); - if (peer == NULL) - return -1; + if (peer == NULL) { + wpa_printf(MSG_DEBUG, "RSN: Could not allocate memory."); + return NULL; + } peer->ibss_rsn = ibss_rsn; os_memcpy(peer->addr, addr, ETH_ALEN); + peer->authentication_status = IBSS_RSN_AUTH_NOT_AUTHENTICATED; - if (ibss_rsn_supp_init(peer, ibss_rsn->wpa_s->own_addr, ibss_rsn->psk) - < 0) { + if (ibss_rsn_supp_init(peer, ibss_rsn->wpa_s->own_addr, + ibss_rsn->psk) < 0) { ibss_rsn_free(peer); + return NULL; + } + + peer->next = ibss_rsn->peers; + ibss_rsn->peers = peer; + + return peer; +} + + +static void ibss_rsn_auth_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct ibss_rsn_peer *peer = eloop_ctx; + + /* + * Assume peer does not support Authentication exchange or the frame was + * lost somewhere - start EAPOL Authenticator. + */ + wpa_printf(MSG_DEBUG, + "RSN: Timeout on waiting Authentication frame response from " + MACSTR " - start authenticator", MAC2STR(peer->addr)); + + peer->authentication_status |= IBSS_RSN_AUTH_BY_US; + ibss_rsn_auth_init(peer->ibss_rsn, peer); +} + + +int ibss_rsn_start(struct ibss_rsn *ibss_rsn, const u8 *addr) +{ + struct ibss_rsn_peer *peer; + int res; + + /* if the peer already exists, exit immediately */ + peer = ibss_rsn_get_peer(ibss_rsn, addr); + if (peer) + return 0; + + peer = ibss_rsn_peer_init(ibss_rsn, addr); + if (peer == NULL) return -1; + + /* Open Authentication: send first Authentication frame */ + res = ibss_rsn_send_auth(ibss_rsn, addr, 1); + if (res) { + /* + * The driver may not support Authentication frame exchange in + * IBSS. Ignore authentication and go through EAPOL exchange. + */ + peer->authentication_status |= IBSS_RSN_AUTH_BY_US; + return ibss_rsn_auth_init(ibss_rsn, peer); + } else { + os_get_reltime(&peer->own_auth_tx); + eloop_register_timeout(1, 0, ibss_rsn_auth_timeout, peer, NULL); } - if (ibss_rsn_auth_init(ibss_rsn, peer) < 0) { - ibss_rsn_free(peer); + return 0; +} + + +static int ibss_rsn_peer_authenticated(struct ibss_rsn *ibss_rsn, + struct ibss_rsn_peer *peer, int reason) +{ + int already_started; + + if (ibss_rsn == NULL || peer == NULL) return -1; + + already_started = ibss_rsn_is_auth_started(peer); + peer->authentication_status |= reason; + + if (already_started) { + wpa_printf(MSG_DEBUG, "RSN: IBSS Authenticator already " + "started for peer " MACSTR, MAC2STR(peer->addr)); + return 0; } - peer->next = ibss_rsn->peers; - ibss_rsn->peers = peer; + wpa_printf(MSG_DEBUG, "RSN: Starting IBSS Authenticator " + "for now-authenticated peer " MACSTR, MAC2STR(peer->addr)); - return 0; + return ibss_rsn_auth_init(ibss_rsn, peer); } @@ -557,10 +759,21 @@ return -1; os_memcpy(tmp, buf, len); if (supp) { - wpa_printf(MSG_DEBUG, "RSN: IBSS RX EAPOL for Supplicant"); + peer->authentication_status |= IBSS_RSN_AUTH_EAPOL_BY_PEER; + wpa_printf(MSG_DEBUG, "RSN: IBSS RX EAPOL for Supplicant from " + MACSTR, MAC2STR(peer->addr)); wpa_sm_rx_eapol(peer->supp, peer->addr, tmp, len); } else { - wpa_printf(MSG_DEBUG, "RSN: IBSS RX EAPOL for Authenticator"); + if (ibss_rsn_is_auth_started(peer) == 0) { + wpa_printf(MSG_DEBUG, "RSN: IBSS EAPOL for " + "Authenticator dropped as " MACSTR " is not " + "authenticated", MAC2STR(peer->addr)); + os_free(tmp); + return -1; + } + + wpa_printf(MSG_DEBUG, "RSN: IBSS RX EAPOL for Authenticator " + "from "MACSTR, MAC2STR(peer->addr)); wpa_receive(ibss_rsn->auth_group, peer->auth, tmp, len); } os_free(tmp); @@ -577,19 +790,25 @@ if (ibss_rsn == NULL) return -1; - for (peer = ibss_rsn->peers; peer; peer = peer->next) { - if (os_memcmp(src_addr, peer->addr, ETH_ALEN) == 0) - return ibss_rsn_process_rx_eapol(ibss_rsn, peer, - buf, len); - } + peer = ibss_rsn_get_peer(ibss_rsn, src_addr); + if (peer) + return ibss_rsn_process_rx_eapol(ibss_rsn, peer, buf, len); if (ibss_rsn_eapol_dst_supp(buf, len) > 0) { /* * Create new IBSS peer based on an EAPOL message from the peer * Authenticator. */ - if (ibss_rsn_start(ibss_rsn, src_addr) < 0) + peer = ibss_rsn_peer_init(ibss_rsn, src_addr); + if (peer == NULL) return -1; + + /* assume the peer is authenticated already */ + wpa_printf(MSG_DEBUG, "RSN: IBSS Not using IBSS Auth for peer " + MACSTR, MAC2STR(src_addr)); + ibss_rsn_peer_authenticated(ibss_rsn, peer, + IBSS_RSN_AUTH_EAPOL_BY_US); + return ibss_rsn_process_rx_eapol(ibss_rsn, ibss_rsn->peers, buf, len); } @@ -597,10 +816,101 @@ return 0; } - void ibss_rsn_set_psk(struct ibss_rsn *ibss_rsn, const u8 *psk) { if (ibss_rsn == NULL) return; os_memcpy(ibss_rsn->psk, psk, PMK_LEN); } + + +static void ibss_rsn_handle_auth_1_of_2(struct ibss_rsn *ibss_rsn, + struct ibss_rsn_peer *peer, + const u8* addr) +{ + wpa_printf(MSG_DEBUG, "RSN: IBSS RX Auth frame (SEQ 1) from " MACSTR, + MAC2STR(addr)); + + if (peer && + peer->authentication_status & IBSS_RSN_AUTH_EAPOL_BY_PEER) { + if (peer->own_auth_tx.sec) { + struct os_reltime now, diff; + os_get_reltime(&now); + os_reltime_sub(&now, &peer->own_auth_tx, &diff); + if (diff.sec == 0 && diff.usec < 500000) { + wpa_printf(MSG_DEBUG, "RSN: Skip IBSS reinit since only %u usec from own Auth frame TX", + (int) diff.usec); + goto skip_reinit; + } + } + /* + * A peer sent us an Authentication frame even though it already + * started an EAPOL session. We should reinit state machines + * here, but it's much more complicated than just deleting and + * recreating the state machine + */ + wpa_printf(MSG_DEBUG, "RSN: IBSS Reinitializing station " + MACSTR, MAC2STR(addr)); + + ibss_rsn_stop(ibss_rsn, addr); + peer = NULL; + } + + if (!peer) { + peer = ibss_rsn_peer_init(ibss_rsn, addr); + if (!peer) + return; + + wpa_printf(MSG_DEBUG, "RSN: IBSS Auth started by peer " MACSTR, + MAC2STR(addr)); + } + +skip_reinit: + /* reply with an Authentication frame now, before sending an EAPOL */ + ibss_rsn_send_auth(ibss_rsn, addr, 2); + /* no need to start another AUTH challenge in the other way.. */ + ibss_rsn_peer_authenticated(ibss_rsn, peer, IBSS_RSN_AUTH_EAPOL_BY_US); +} + + +void ibss_rsn_handle_auth(struct ibss_rsn *ibss_rsn, const u8 *auth_frame, + size_t len) +{ + const struct ieee80211_mgmt *header; + struct ibss_rsn_peer *peer; + size_t auth_length; + + header = (const struct ieee80211_mgmt *) auth_frame; + auth_length = IEEE80211_HDRLEN + sizeof(header->u.auth); + + if (ibss_rsn == NULL || len < auth_length) + return; + + if (le_to_host16(header->u.auth.auth_alg) != WLAN_AUTH_OPEN || + le_to_host16(header->u.auth.status_code) != WLAN_STATUS_SUCCESS) + return; + + peer = ibss_rsn_get_peer(ibss_rsn, header->sa); + + switch (le_to_host16(header->u.auth.auth_transaction)) { + case 1: + ibss_rsn_handle_auth_1_of_2(ibss_rsn, peer, header->sa); + break; + case 2: + wpa_printf(MSG_DEBUG, "RSN: IBSS RX Auth frame (SEQ 2) from " + MACSTR, MAC2STR(header->sa)); + if (!peer) { + wpa_printf(MSG_DEBUG, "RSN: Received Auth seq 2 from " + "unknown STA " MACSTR, MAC2STR(header->sa)); + break; + } + + /* authentication has been completed */ + eloop_cancel_timeout(ibss_rsn_auth_timeout, peer, NULL); + wpa_printf(MSG_DEBUG, "RSN: IBSS Auth completed with " MACSTR, + MAC2STR(header->sa)); + ibss_rsn_peer_authenticated(ibss_rsn, peer, + IBSS_RSN_AUTH_BY_US); + break; + } +} diff -Nru wpa-1.0/wpa_supplicant/ibss_rsn.h wpa-2.1/wpa_supplicant/ibss_rsn.h --- wpa-1.0/wpa_supplicant/ibss_rsn.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/ibss_rsn.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * wpa_supplicant - IBSS RSN * Copyright (c) 2009, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef IBSS_RSN_H @@ -17,6 +11,21 @@ struct ibss_rsn; +/* not authenticated */ +#define IBSS_RSN_AUTH_NOT_AUTHENTICATED 0x00 +/* remote peer sent an EAPOL message */ +#define IBSS_RSN_AUTH_EAPOL_BY_PEER 0x01 +/* we sent an AUTH message with seq 1 */ +#define IBSS_RSN_AUTH_BY_US 0x02 +/* we sent an EAPOL message */ +#define IBSS_RSN_AUTH_EAPOL_BY_US 0x04 +/* PTK derived as supplicant */ +#define IBSS_RSN_SET_PTK_SUPP 0x08 +/* PTK derived as authenticator */ +#define IBSS_RSN_SET_PTK_AUTH 0x10 +/* PTK completion reported */ +#define IBSS_RSN_REPORTED_PTK 0x20 + struct ibss_rsn_peer { struct ibss_rsn_peer *next; struct ibss_rsn *ibss_rsn; @@ -29,6 +38,9 @@ size_t supp_ie_len; struct wpa_state_machine *auth; + int authentication_status; + + struct os_reltime own_auth_tx; }; struct ibss_rsn { @@ -46,5 +58,7 @@ int ibss_rsn_rx_eapol(struct ibss_rsn *ibss_rsn, const u8 *src_addr, const u8 *buf, size_t len); void ibss_rsn_set_psk(struct ibss_rsn *ibss_rsn, const u8 *psk); +void ibss_rsn_handle_auth(struct ibss_rsn *ibss_rsn, const u8 *auth_frame, + size_t len); #endif /* IBSS_RSN_H */ diff -Nru wpa-1.0/wpa_supplicant/interworking.c wpa-2.1/wpa_supplicant/interworking.c --- wpa-1.0/wpa_supplicant/interworking.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/interworking.c 2014-02-04 11:23:35.000000000 +0000 @@ -1,15 +1,10 @@ /* * Interworking (IEEE 802.11u) - * Copyright (c) 2011, Qualcomm Atheros + * Copyright (c) 2011-2013, Qualcomm Atheros, Inc. + * Copyright (c) 2011-2014, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -18,15 +13,22 @@ #include "common/ieee802_11_defs.h" #include "common/gas.h" #include "common/wpa_ctrl.h" +#include "utils/pcsc_funcs.h" +#include "utils/eloop.h" #include "drivers/driver.h" #include "eap_common/eap_defs.h" +#include "eap_peer/eap.h" #include "eap_peer/eap_methods.h" +#include "eapol_supp/eapol_supp_sm.h" +#include "rsn_supp/wpa.h" #include "wpa_supplicant_i.h" #include "config.h" +#include "config_ssid.h" #include "bss.h" #include "scan.h" #include "notify.h" #include "gas_query.h" +#include "hs20_supplicant.h" #include "interworking.h" @@ -43,6 +45,27 @@ #endif static void interworking_next_anqp_fetch(struct wpa_supplicant *wpa_s); +static struct wpa_cred * interworking_credentials_available_realm( + struct wpa_supplicant *wpa_s, struct wpa_bss *bss); +static struct wpa_cred * interworking_credentials_available_3gpp( + struct wpa_supplicant *wpa_s, struct wpa_bss *bss); + + +static void interworking_reconnect(struct wpa_supplicant *wpa_s) +{ + if (wpa_s->wpa_state >= WPA_AUTHENTICATING) { + wpa_supplicant_cancel_sched_scan(wpa_s); + wpa_supplicant_deauthenticate(wpa_s, + WLAN_REASON_DEAUTH_LEAVING); + } + wpa_s->disconnected = 0; + wpa_s->reassociate = 1; + + if (wpa_supplicant_fast_associate(wpa_s) >= 0) + return; + + wpa_supplicant_req_scan(wpa_s, 0, 0); +} static struct wpabuf * anqp_build_req(u16 info_ids[], size_t num_ids, @@ -85,29 +108,137 @@ } +static int cred_with_roaming_consortium(struct wpa_supplicant *wpa_s) +{ + struct wpa_cred *cred; + + for (cred = wpa_s->conf->cred; cred; cred = cred->next) { + if (cred->roaming_consortium_len) + return 1; + if (cred->required_roaming_consortium_len) + return 1; + } + return 0; +} + + +static int cred_with_3gpp(struct wpa_supplicant *wpa_s) +{ + struct wpa_cred *cred; + + for (cred = wpa_s->conf->cred; cred; cred = cred->next) { + if (cred->pcsc || cred->imsi) + return 1; + } + return 0; +} + + +static int cred_with_nai_realm(struct wpa_supplicant *wpa_s) +{ + struct wpa_cred *cred; + + for (cred = wpa_s->conf->cred; cred; cred = cred->next) { + if (cred->pcsc || cred->imsi) + continue; + if (!cred->eap_method) + return 1; + if (cred->realm && cred->roaming_consortium_len == 0) + return 1; + } + return 0; +} + + +static int cred_with_domain(struct wpa_supplicant *wpa_s) +{ + struct wpa_cred *cred; + + for (cred = wpa_s->conf->cred; cred; cred = cred->next) { + if (cred->domain || cred->pcsc || cred->imsi) + return 1; + } + return 0; +} + + +static int additional_roaming_consortiums(struct wpa_bss *bss) +{ + const u8 *ie; + ie = wpa_bss_get_ie(bss, WLAN_EID_ROAMING_CONSORTIUM); + if (ie == NULL || ie[1] == 0) + return 0; + return ie[2]; /* Number of ANQP OIs */ +} + + +static void interworking_continue_anqp(void *eloop_ctx, void *sock_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + interworking_next_anqp_fetch(wpa_s); +} + + static int interworking_anqp_send_req(struct wpa_supplicant *wpa_s, struct wpa_bss *bss) { struct wpabuf *buf; int ret = 0; int res; - u16 info_ids[] = { - ANQP_CAPABILITY_LIST, - ANQP_VENUE_NAME, - ANQP_NETWORK_AUTH_TYPE, - ANQP_ROAMING_CONSORTIUM, - ANQP_IP_ADDR_TYPE_AVAILABILITY, - ANQP_NAI_REALM, - ANQP_3GPP_CELLULAR_NETWORK, - ANQP_DOMAIN_NAME - }; + u16 info_ids[8]; + size_t num_info_ids = 0; struct wpabuf *extra = NULL; + int all = wpa_s->fetch_all_anqp; wpa_printf(MSG_DEBUG, "Interworking: ANQP Query Request to " MACSTR, MAC2STR(bss->bssid)); + wpa_s->interworking_gas_bss = bss; + + info_ids[num_info_ids++] = ANQP_CAPABILITY_LIST; + if (all) { + info_ids[num_info_ids++] = ANQP_VENUE_NAME; + info_ids[num_info_ids++] = ANQP_NETWORK_AUTH_TYPE; + } + if (all || (cred_with_roaming_consortium(wpa_s) && + additional_roaming_consortiums(bss))) + info_ids[num_info_ids++] = ANQP_ROAMING_CONSORTIUM; + if (all) + info_ids[num_info_ids++] = ANQP_IP_ADDR_TYPE_AVAILABILITY; + if (all || cred_with_nai_realm(wpa_s)) + info_ids[num_info_ids++] = ANQP_NAI_REALM; + if (all || cred_with_3gpp(wpa_s)) + info_ids[num_info_ids++] = ANQP_3GPP_CELLULAR_NETWORK; + if (all || cred_with_domain(wpa_s)) + info_ids[num_info_ids++] = ANQP_DOMAIN_NAME; + wpa_hexdump(MSG_DEBUG, "Interworking: ANQP Query info", + (u8 *) info_ids, num_info_ids * 2); + +#ifdef CONFIG_HS20 + if (wpa_bss_get_vendor_ie(bss, HS20_IE_VENDOR_TYPE)) { + u8 *len_pos; + + extra = wpabuf_alloc(100); + if (!extra) + return -1; + + len_pos = gas_anqp_add_element(extra, ANQP_VENDOR_SPECIFIC); + wpabuf_put_be24(extra, OUI_WFA); + wpabuf_put_u8(extra, HS20_ANQP_OUI_TYPE); + wpabuf_put_u8(extra, HS20_STYPE_QUERY_LIST); + wpabuf_put_u8(extra, 0); /* Reserved */ + wpabuf_put_u8(extra, HS20_STYPE_CAPABILITY_LIST); + if (all) { + wpabuf_put_u8(extra, + HS20_STYPE_OPERATOR_FRIENDLY_NAME); + wpabuf_put_u8(extra, HS20_STYPE_WAN_METRICS); + wpabuf_put_u8(extra, HS20_STYPE_CONNECTION_CAPABILITY); + wpabuf_put_u8(extra, HS20_STYPE_OPERATING_CLASS); + } + gas_anqp_set_element_len(extra, len_pos); + } +#endif /* CONFIG_HS20 */ - buf = anqp_build_req(info_ids, sizeof(info_ids) / sizeof(info_ids[0]), - extra); + buf = anqp_build_req(info_ids, num_info_ids, extra); wpabuf_free(extra); if (buf == NULL) return -1; @@ -116,12 +247,14 @@ interworking_anqp_resp_cb, wpa_s); if (res < 0) { wpa_printf(MSG_DEBUG, "ANQP: Failed to send Query Request"); + wpabuf_free(buf); ret = -1; + eloop_register_timeout(0, 0, interworking_continue_anqp, wpa_s, + NULL); } else wpa_printf(MSG_DEBUG, "ANQP: Query started with dialog token " "%u", res); - wpabuf_free(buf); return ret; } @@ -280,11 +413,9 @@ return NULL; } wpa_hexdump_ascii(MSG_DEBUG, "NAI Realm", pos, realm_len); - r->realm = os_malloc(realm_len + 1); + r->realm = dup_binstr(pos, realm_len); if (r->realm == NULL) return NULL; - os_memcpy(r->realm, pos, realm_len); - r->realm[realm_len] = '\0'; pos += realm_len; if (pos + 1 > f_end) { @@ -297,7 +428,7 @@ wpa_printf(MSG_DEBUG, "No room for EAP Methods"); return NULL; } - r->eap = os_zalloc(r->eap_count * sizeof(struct nai_realm_eap)); + r->eap = os_calloc(r->eap_count, sizeof(struct nai_realm_eap)); if (r->eap == NULL) return NULL; @@ -333,7 +464,7 @@ return NULL; } - realm = os_zalloc(num * sizeof(struct nai_realm)); + realm = os_calloc(num, sizeof(struct nai_realm)); if (realm == NULL) return NULL; @@ -390,18 +521,24 @@ if (eap_get_name(EAP_VENDOR_IETF, eap->method) == NULL) return 0; /* method not supported */ - if (eap->method != EAP_TYPE_TTLS && eap->method != EAP_TYPE_PEAP) { + if (eap->method != EAP_TYPE_TTLS && eap->method != EAP_TYPE_PEAP && + eap->method != EAP_TYPE_FAST) { /* Only tunneled methods with username/password supported */ return 0; } - if (eap->method == EAP_TYPE_PEAP && - eap_get_name(EAP_VENDOR_IETF, eap->inner_method) == NULL) - return 0; + if (eap->method == EAP_TYPE_PEAP || eap->method == EAP_TYPE_FAST) { + if (eap->inner_method && + eap_get_name(EAP_VENDOR_IETF, eap->inner_method) == NULL) + return 0; + if (!eap->inner_method && + eap_get_name(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2) == NULL) + return 0; + } if (eap->method == EAP_TYPE_TTLS) { if (eap->inner_method == 0 && eap->inner_non_eap == 0) - return 0; + return 1; /* Assume TTLS/MSCHAPv2 is used */ if (eap->inner_method && eap_get_name(EAP_VENDOR_IETF, eap->inner_method) == NULL) return 0; @@ -422,20 +559,41 @@ } -struct nai_realm_eap * nai_realm_find_eap(struct wpa_supplicant *wpa_s, - struct nai_realm *realm) +static int nai_realm_cred_cert(struct nai_realm_eap *eap) +{ + if (eap_get_name(EAP_VENDOR_IETF, eap->method) == NULL) + return 0; /* method not supported */ + + if (eap->method != EAP_TYPE_TLS) { + /* Only EAP-TLS supported for credential authentication */ + return 0; + } + + return 1; +} + + +static struct nai_realm_eap * nai_realm_find_eap(struct wpa_cred *cred, + struct nai_realm *realm) { u8 e; - if (wpa_s->conf->home_username == NULL || - wpa_s->conf->home_username[0] == '\0' || - wpa_s->conf->home_password == NULL || - wpa_s->conf->home_password[0] == '\0') + if (cred == NULL || + cred->username == NULL || + cred->username[0] == '\0' || + ((cred->password == NULL || + cred->password[0] == '\0') && + (cred->private_key == NULL || + cred->private_key[0] == '\0'))) return NULL; for (e = 0; e < realm->eap_count; e++) { struct nai_realm_eap *eap = &realm->eap[e]; - if (nai_realm_cred_username(eap)) + if (cred->password && cred->password[0] && + nai_realm_cred_username(eap)) + return eap; + if (cred->private_key && cred->private_key[0] && + nai_realm_cred_cert(eap)) return eap; } @@ -445,25 +603,31 @@ #ifdef INTERWORKING_3GPP -static int plmn_id_match(struct wpabuf *anqp, const char *imsi) +static int plmn_id_match(struct wpabuf *anqp, const char *imsi, int mnc_len) { - const char *sep; - u8 plmn[3]; + u8 plmn[3], plmn2[3]; const u8 *pos, *end; u8 udhl; - sep = os_strchr(imsi, '-'); - if (sep == NULL || (sep - imsi != 5 && sep - imsi != 6)) - return 0; - - /* See Annex A of 3GPP TS 24.234 v8.1.0 for description */ + /* + * See Annex A of 3GPP TS 24.234 v8.1.0 for description. The network + * operator is allowed to include only two digits of the MNC, so allow + * matches based on both two and three digit MNC assumptions. Since some + * SIM/USIM cards may not expose MNC length conveniently, we may be + * provided the default MNC length 3 here and as such, checking with MNC + * length 2 is justifiable even though 3GPP TS 24.234 does not mention + * that case. Anyway, MCC/MNC pair where both 2 and 3 digit MNC is used + * with otherwise matching values would not be good idea in general, so + * this should not result in selecting incorrect networks. + */ + /* Match with 3 digit MNC */ plmn[0] = (imsi[0] - '0') | ((imsi[1] - '0') << 4); - plmn[1] = imsi[2] - '0'; - if (sep - imsi == 6) - plmn[1] |= (imsi[5] - '0') << 4; - else - plmn[1] |= 0xf0; + plmn[1] = (imsi[2] - '0') | ((imsi[5] - '0') << 4); plmn[2] = (imsi[3] - '0') | ((imsi[4] - '0') << 4); + /* Match with 2 digit MNC */ + plmn2[0] = (imsi[0] - '0') | ((imsi[1] - '0') << 4); + plmn2[1] = (imsi[2] - '0') | 0xf0; + plmn2[2] = (imsi[3] - '0') | ((imsi[4] - '0') << 4); if (anqp == NULL) return 0; @@ -483,6 +647,10 @@ } end = pos + udhl; + wpa_printf(MSG_DEBUG, "Interworking: Matching against MCC/MNC alternatives: %02x:%02x:%02x or %02x:%02x:%02x (IMSI %s, MNC length %d)", + plmn[0], plmn[1], plmn[2], plmn2[0], plmn2[1], plmn2[2], + imsi, mnc_len); + while (pos + 2 <= end) { u8 iei, len; const u8 *l_end; @@ -495,13 +663,20 @@ if (iei == 0 && len > 0) { /* PLMN List */ u8 num, i; + wpa_hexdump(MSG_DEBUG, "Interworking: PLMN List information element", + pos, len); num = *pos++; for (i = 0; i < num; i++) { - if (pos + 3 > end) + if (pos + 3 > l_end) break; - if (os_memcmp(pos, plmn, 3) == 0) + if (os_memcmp(pos, plmn, 3) == 0 || + os_memcmp(pos, plmn2, 3) == 0) return 1; /* Found matching PLMN */ + pos += 3; } + } else { + wpa_hexdump(MSG_DEBUG, "Interworking: Unrecognized 3GPP information element", + pos, len); } pos = l_end; @@ -511,10 +686,11 @@ } -static int set_root_nai(struct wpa_ssid *ssid, const char *imsi, char prefix) +static int build_root_nai(char *nai, size_t nai_len, const char *imsi, + size_t mnc_len, char prefix) { const char *sep, *msin; - char nai[100], *end, *pos; + char *end, *pos; size_t msin_len, plmn_len; /* @@ -529,17 +705,22 @@ return -1; } sep = os_strchr(imsi, '-'); - if (sep == NULL) + if (sep) { + plmn_len = sep - imsi; + msin = sep + 1; + } else if (mnc_len && os_strlen(imsi) >= 3 + mnc_len) { + plmn_len = 3 + mnc_len; + msin = imsi + plmn_len; + } else return -1; - plmn_len = sep - imsi; if (plmn_len != 5 && plmn_len != 6) return -1; - msin = sep + 1; msin_len = os_strlen(msin); pos = nai; - end = pos + sizeof(nai); - *pos++ = prefix; + end = nai + nai_len; + if (prefix) + *pos++ = prefix; os_memcpy(pos, imsi, plmn_len); pos += plmn_len; os_memcpy(pos, msin, msin_len); @@ -557,299 +738,1101 @@ pos += os_snprintf(pos, end - pos, ".mcc%c%c%c.3gppnetwork.org", imsi[0], imsi[1], imsi[2]); + return 0; +} + + +static int set_root_nai(struct wpa_ssid *ssid, const char *imsi, char prefix) +{ + char nai[100]; + if (build_root_nai(nai, sizeof(nai), imsi, 0, prefix) < 0) + return -1; return wpa_config_set_quoted(ssid, "identity", nai); } #endif /* INTERWORKING_3GPP */ -static int interworking_connect_3gpp(struct wpa_supplicant *wpa_s, - struct wpa_bss *bss) +static int already_connected(struct wpa_supplicant *wpa_s, + struct wpa_cred *cred, struct wpa_bss *bss) { -#ifdef INTERWORKING_3GPP struct wpa_ssid *ssid; - const u8 *ie; - ie = wpa_bss_get_ie(bss, WLAN_EID_SSID); - if (ie == NULL) - return -1; - wpa_printf(MSG_DEBUG, "Interworking: Connect with " MACSTR " (3GPP)", - MAC2STR(bss->bssid)); + if (wpa_s->wpa_state < WPA_ASSOCIATED || wpa_s->current_ssid == NULL) + return 0; - ssid = wpa_config_add_network(wpa_s->conf); - if (ssid == NULL) - return -1; + ssid = wpa_s->current_ssid; + if (ssid->parent_cred != cred) + return 0; - wpas_notify_network_added(wpa_s, ssid); - wpa_config_set_network_defaults(ssid); - ssid->temporary = 1; - ssid->ssid = os_zalloc(ie[1] + 1); - if (ssid->ssid == NULL) - goto fail; - os_memcpy(ssid->ssid, ie + 2, ie[1]); - ssid->ssid_len = ie[1]; + if (ssid->ssid_len != bss->ssid_len || + os_memcmp(ssid->ssid, bss->ssid, bss->ssid_len) != 0) + return 0; - /* TODO: figure out whether to use EAP-SIM, EAP-AKA, or EAP-AKA' */ - if (wpa_config_set(ssid, "eap", "SIM", 0) < 0) { - wpa_printf(MSG_DEBUG, "EAP-SIM not supported"); - goto fail; - } - if (set_root_nai(ssid, wpa_s->conf->home_imsi, '1') < 0) { - wpa_printf(MSG_DEBUG, "Failed to set Root NAI"); - goto fail; - } + return 1; +} - if (wpa_s->conf->home_milenage && wpa_s->conf->home_milenage[0]) { - if (wpa_config_set_quoted(ssid, "password", - wpa_s->conf->home_milenage) < 0) - goto fail; - } else { - /* TODO: PIN */ - if (wpa_config_set_quoted(ssid, "pcsc", "") < 0) - goto fail; + +static void remove_duplicate_network(struct wpa_supplicant *wpa_s, + struct wpa_cred *cred, + struct wpa_bss *bss) +{ + struct wpa_ssid *ssid; + + for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) { + if (ssid->parent_cred != cred) + continue; + if (ssid->ssid_len != bss->ssid_len || + os_memcmp(ssid->ssid, bss->ssid, bss->ssid_len) != 0) + continue; + + break; } - if (wpa_s->conf->home_password && wpa_s->conf->home_password[0] && - wpa_config_set_quoted(ssid, "password", wpa_s->conf->home_password) - < 0) - goto fail; + if (ssid == NULL) + return; - wpa_supplicant_select_network(wpa_s, ssid); + wpa_printf(MSG_DEBUG, "Interworking: Remove duplicate network entry for the same credential"); - return 0; + if (ssid == wpa_s->current_ssid) { + wpa_sm_set_config(wpa_s->wpa, NULL); + eapol_sm_notify_config(wpa_s->eapol, NULL, NULL); + wpa_supplicant_deauthenticate(wpa_s, + WLAN_REASON_DEAUTH_LEAVING); + } -fail: wpas_notify_network_removed(wpa_s, ssid); wpa_config_remove_network(wpa_s->conf, ssid->id); -#endif /* INTERWORKING_3GPP */ - return -1; } -int interworking_connect(struct wpa_supplicant *wpa_s, struct wpa_bss *bss) +static int interworking_set_hs20_params(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid) { - struct wpa_ssid *ssid; - struct nai_realm *realm; - struct nai_realm_eap *eap = NULL; - u16 count, i; - char buf[100]; - const u8 *ie; - - if (bss == NULL) + if (wpa_config_set(ssid, "key_mgmt", + wpa_s->conf->pmf != NO_MGMT_FRAME_PROTECTION ? + "WPA-EAP WPA-EAP-SHA256" : "WPA-EAP", 0) < 0) return -1; - ie = wpa_bss_get_ie(bss, WLAN_EID_SSID); - if (ie == NULL || ie[1] == 0) { - wpa_printf(MSG_DEBUG, "Interworking: No SSID known for " - MACSTR, MAC2STR(bss->bssid)); + if (wpa_config_set(ssid, "proto", "RSN", 0) < 0) return -1; - } - - realm = nai_realm_parse(bss->anqp_nai_realm, &count); - if (realm == NULL) { - wpa_printf(MSG_DEBUG, "Interworking: Could not parse NAI " - "Realm list from " MACSTR, MAC2STR(bss->bssid)); - count = 0; - } + if (wpa_config_set(ssid, "pairwise", "CCMP", 0) < 0) + return -1; + return 0; +} - for (i = 0; i < count; i++) { - if (!nai_realm_match(&realm[i], wpa_s->conf->home_realm)) - continue; - eap = nai_realm_find_eap(wpa_s, &realm[i]); - if (eap) - break; - } - if (!eap) { - if (interworking_connect_3gpp(wpa_s, bss) == 0) { - if (realm) - nai_realm_free(realm, count); - return 0; - } +static int interworking_connect_3gpp(struct wpa_supplicant *wpa_s, + struct wpa_cred *cred, + struct wpa_bss *bss) +{ +#ifdef INTERWORKING_3GPP + struct wpa_ssid *ssid; + int eap_type; + int res; + char prefix; - wpa_printf(MSG_DEBUG, "Interworking: No matching credentials " - "and EAP method found for " MACSTR, - MAC2STR(bss->bssid)); - nai_realm_free(realm, count); + if (bss->anqp == NULL || bss->anqp->anqp_3gpp == NULL) return -1; - } - wpa_printf(MSG_DEBUG, "Interworking: Connect with " MACSTR, + wpa_printf(MSG_DEBUG, "Interworking: Connect with " MACSTR " (3GPP)", MAC2STR(bss->bssid)); + if (already_connected(wpa_s, cred, bss)) { + wpa_msg(wpa_s, MSG_INFO, INTERWORKING_ALREADY_CONNECTED MACSTR, + MAC2STR(bss->bssid)); + return 0; + } + + remove_duplicate_network(wpa_s, cred, bss); + ssid = wpa_config_add_network(wpa_s->conf); - if (ssid == NULL) { - nai_realm_free(realm, count); + if (ssid == NULL) return -1; - } + ssid->parent_cred = cred; + wpas_notify_network_added(wpa_s, ssid); wpa_config_set_network_defaults(ssid); + ssid->priority = cred->priority; ssid->temporary = 1; - ssid->ssid = os_zalloc(ie[1] + 1); + ssid->ssid = os_zalloc(bss->ssid_len + 1); if (ssid->ssid == NULL) goto fail; - os_memcpy(ssid->ssid, ie + 2, ie[1]); - ssid->ssid_len = ie[1]; + os_memcpy(ssid->ssid, bss->ssid, bss->ssid_len); + ssid->ssid_len = bss->ssid_len; - if (wpa_config_set(ssid, "eap", eap_get_name(EAP_VENDOR_IETF, - eap->method), 0) < 0) + if (interworking_set_hs20_params(wpa_s, ssid) < 0) goto fail; - if (wpa_s->conf->home_username && wpa_s->conf->home_username[0] && - wpa_config_set_quoted(ssid, "identity", - wpa_s->conf->home_username) < 0) + eap_type = EAP_TYPE_SIM; + if (cred->pcsc && wpa_s->scard && scard_supports_umts(wpa_s->scard)) + eap_type = EAP_TYPE_AKA; + if (cred->eap_method && cred->eap_method[0].vendor == EAP_VENDOR_IETF) { + if (cred->eap_method[0].method == EAP_TYPE_SIM || + cred->eap_method[0].method == EAP_TYPE_AKA || + cred->eap_method[0].method == EAP_TYPE_AKA_PRIME) + eap_type = cred->eap_method[0].method; + } + + switch (eap_type) { + case EAP_TYPE_SIM: + prefix = '1'; + res = wpa_config_set(ssid, "eap", "SIM", 0); + break; + case EAP_TYPE_AKA: + prefix = '0'; + res = wpa_config_set(ssid, "eap", "AKA", 0); + break; + case EAP_TYPE_AKA_PRIME: + prefix = '6'; + res = wpa_config_set(ssid, "eap", "AKA'", 0); + break; + default: + res = -1; + break; + } + if (res < 0) { + wpa_printf(MSG_DEBUG, "Selected EAP method (%d) not supported", + eap_type); goto fail; + } - if (wpa_s->conf->home_password && wpa_s->conf->home_password[0] && - wpa_config_set_quoted(ssid, "password", wpa_s->conf->home_password) - < 0) + if (!cred->pcsc && set_root_nai(ssid, cred->imsi, prefix) < 0) { + wpa_printf(MSG_DEBUG, "Failed to set Root NAI"); goto fail; + } - switch (eap->method) { - case EAP_TYPE_TTLS: - if (eap->inner_method) { - os_snprintf(buf, sizeof(buf), "\"autheap=%s\"", - eap_get_name(EAP_VENDOR_IETF, - eap->inner_method)); - if (wpa_config_set(ssid, "phase2", buf, 0) < 0) - goto fail; - break; - } - switch (eap->inner_non_eap) { - case NAI_REALM_INNER_NON_EAP_PAP: - if (wpa_config_set(ssid, "phase2", "\"auth=PAP\"", 0) < - 0) - goto fail; - break; - case NAI_REALM_INNER_NON_EAP_CHAP: - if (wpa_config_set(ssid, "phase2", "\"auth=CHAP\"", 0) - < 0) - goto fail; - break; - case NAI_REALM_INNER_NON_EAP_MSCHAP: - if (wpa_config_set(ssid, "phase2", "\"auth=MSCHAP\"", - 0) < 0) - goto fail; - break; - case NAI_REALM_INNER_NON_EAP_MSCHAPV2: - if (wpa_config_set(ssid, "phase2", "\"auth=MSCHAPV2\"", - 0) < 0) - goto fail; - break; - } - break; - case EAP_TYPE_PEAP: - os_snprintf(buf, sizeof(buf), "\"auth=%s\"", - eap_get_name(EAP_VENDOR_IETF, eap->inner_method)); - if (wpa_config_set(ssid, "phase2", buf, 0) < 0) + if (cred->milenage && cred->milenage[0]) { + if (wpa_config_set_quoted(ssid, "password", + cred->milenage) < 0) + goto fail; + } else if (cred->pcsc) { + if (wpa_config_set_quoted(ssid, "pcsc", "") < 0) + goto fail; + if (wpa_s->conf->pcsc_pin && + wpa_config_set_quoted(ssid, "pin", wpa_s->conf->pcsc_pin) + < 0) goto fail; - break; } - if (wpa_s->conf->home_ca_cert && wpa_s->conf->home_ca_cert[0] && - wpa_config_set_quoted(ssid, "ca_cert", wpa_s->conf->home_ca_cert) < - 0) + if (cred->password && cred->password[0] && + wpa_config_set_quoted(ssid, "password", cred->password) < 0) goto fail; - nai_realm_free(realm, count); - - wpa_supplicant_select_network(wpa_s, ssid); + wpa_config_update_prio_list(wpa_s->conf); + interworking_reconnect(wpa_s); return 0; fail: wpas_notify_network_removed(wpa_s, ssid); wpa_config_remove_network(wpa_s->conf, ssid->id); - nai_realm_free(realm, count); +#endif /* INTERWORKING_3GPP */ return -1; } -static int interworking_credentials_available_3gpp( - struct wpa_supplicant *wpa_s, struct wpa_bss *bss) +static int roaming_consortium_element_match(const u8 *ie, const u8 *rc_id, + size_t rc_len) { - int ret = 0; + const u8 *pos, *end; + u8 lens; -#ifdef INTERWORKING_3GPP - if (bss->anqp_3gpp == NULL) - return ret; + if (ie == NULL) + return 0; - if (wpa_s->conf->home_imsi == NULL || !wpa_s->conf->home_imsi[0] || - wpa_s->conf->home_milenage == NULL || - !wpa_s->conf->home_milenage[0]) - return ret; + pos = ie + 2; + end = ie + 2 + ie[1]; - wpa_printf(MSG_DEBUG, "Interworking: Parsing 3GPP info from " MACSTR, - MAC2STR(bss->bssid)); - ret = plmn_id_match(bss->anqp_3gpp, wpa_s->conf->home_imsi); - wpa_printf(MSG_DEBUG, "PLMN match %sfound", ret ? "" : "not "); -#endif /* INTERWORKING_3GPP */ - return ret; + /* Roaming Consortium element: + * Number of ANQP OIs + * OI #1 and #2 lengths + * OI #1, [OI #2], [OI #3] + */ + + if (pos + 2 > end) + return 0; + + pos++; /* skip Number of ANQP OIs */ + lens = *pos++; + if (pos + (lens & 0x0f) + (lens >> 4) > end) + return 0; + + if ((lens & 0x0f) == rc_len && os_memcmp(pos, rc_id, rc_len) == 0) + return 1; + pos += lens & 0x0f; + + if ((lens >> 4) == rc_len && os_memcmp(pos, rc_id, rc_len) == 0) + return 1; + pos += lens >> 4; + + if (pos < end && (size_t) (end - pos) == rc_len && + os_memcmp(pos, rc_id, rc_len) == 0) + return 1; + + return 0; } -static int interworking_credentials_available_realm( +static int roaming_consortium_anqp_match(const struct wpabuf *anqp, + const u8 *rc_id, size_t rc_len) +{ + const u8 *pos, *end; + u8 len; + + if (anqp == NULL) + return 0; + + pos = wpabuf_head(anqp); + end = pos + wpabuf_len(anqp); + + /* Set of duples */ + while (pos < end) { + len = *pos++; + if (pos + len > end) + break; + if (len == rc_len && os_memcmp(pos, rc_id, rc_len) == 0) + return 1; + pos += len; + } + + return 0; +} + + +static int roaming_consortium_match(const u8 *ie, const struct wpabuf *anqp, + const u8 *rc_id, size_t rc_len) +{ + return roaming_consortium_element_match(ie, rc_id, rc_len) || + roaming_consortium_anqp_match(anqp, rc_id, rc_len); +} + + +static int cred_no_required_oi_match(struct wpa_cred *cred, struct wpa_bss *bss) +{ + const u8 *ie; + + if (cred->required_roaming_consortium_len == 0) + return 0; + + ie = wpa_bss_get_ie(bss, WLAN_EID_ROAMING_CONSORTIUM); + + if (ie == NULL && + (bss->anqp == NULL || bss->anqp->roaming_consortium == NULL)) + return 1; + + return !roaming_consortium_match(ie, + bss->anqp ? + bss->anqp->roaming_consortium : NULL, + cred->required_roaming_consortium, + cred->required_roaming_consortium_len); +} + + +static int cred_excluded_ssid(struct wpa_cred *cred, struct wpa_bss *bss) +{ + size_t i; + + if (!cred->excluded_ssid) + return 0; + + for (i = 0; i < cred->num_excluded_ssid; i++) { + struct excluded_ssid *e = &cred->excluded_ssid[i]; + if (bss->ssid_len == e->ssid_len && + os_memcmp(bss->ssid, e->ssid, e->ssid_len) == 0) + return 1; + } + + return 0; +} + + +static struct wpa_cred * interworking_credentials_available_roaming_consortium( struct wpa_supplicant *wpa_s, struct wpa_bss *bss) { + struct wpa_cred *cred, *selected = NULL; + const u8 *ie; + + ie = wpa_bss_get_ie(bss, WLAN_EID_ROAMING_CONSORTIUM); + + if (ie == NULL && + (bss->anqp == NULL || bss->anqp->roaming_consortium == NULL)) + return NULL; + + if (wpa_s->conf->cred == NULL) + return NULL; + + for (cred = wpa_s->conf->cred; cred; cred = cred->next) { + if (cred->roaming_consortium_len == 0) + continue; + + if (!roaming_consortium_match(ie, + bss->anqp ? + bss->anqp->roaming_consortium : + NULL, + cred->roaming_consortium, + cred->roaming_consortium_len)) + continue; + + if (cred_excluded_ssid(cred, bss)) + continue; + if (cred_no_required_oi_match(cred, bss)) + continue; + + if (selected == NULL || + selected->priority < cred->priority) + selected = cred; + } + + return selected; +} + + +static int interworking_set_eap_params(struct wpa_ssid *ssid, + struct wpa_cred *cred, int ttls) +{ + if (cred->eap_method) { + ttls = cred->eap_method->vendor == EAP_VENDOR_IETF && + cred->eap_method->method == EAP_TYPE_TTLS; + + os_free(ssid->eap.eap_methods); + ssid->eap.eap_methods = + os_malloc(sizeof(struct eap_method_type) * 2); + if (ssid->eap.eap_methods == NULL) + return -1; + os_memcpy(ssid->eap.eap_methods, cred->eap_method, + sizeof(*cred->eap_method)); + ssid->eap.eap_methods[1].vendor = EAP_VENDOR_IETF; + ssid->eap.eap_methods[1].method = EAP_TYPE_NONE; + } + + if (ttls && cred->username && cred->username[0]) { + const char *pos; + char *anon; + /* Use anonymous NAI in Phase 1 */ + pos = os_strchr(cred->username, '@'); + if (pos) { + size_t buflen = 9 + os_strlen(pos) + 1; + anon = os_malloc(buflen); + if (anon == NULL) + return -1; + os_snprintf(anon, buflen, "anonymous%s", pos); + } else if (cred->realm) { + size_t buflen = 10 + os_strlen(cred->realm) + 1; + anon = os_malloc(buflen); + if (anon == NULL) + return -1; + os_snprintf(anon, buflen, "anonymous@%s", cred->realm); + } else { + anon = os_strdup("anonymous"); + if (anon == NULL) + return -1; + } + if (wpa_config_set_quoted(ssid, "anonymous_identity", anon) < + 0) { + os_free(anon); + return -1; + } + os_free(anon); + } + + if (cred->username && cred->username[0] && + wpa_config_set_quoted(ssid, "identity", cred->username) < 0) + return -1; + + if (cred->password && cred->password[0]) { + if (cred->ext_password && + wpa_config_set(ssid, "password", cred->password, 0) < 0) + return -1; + if (!cred->ext_password && + wpa_config_set_quoted(ssid, "password", cred->password) < + 0) + return -1; + } + + if (cred->client_cert && cred->client_cert[0] && + wpa_config_set_quoted(ssid, "client_cert", cred->client_cert) < 0) + return -1; + +#ifdef ANDROID + if (cred->private_key && + os_strncmp(cred->private_key, "keystore://", 11) == 0) { + /* Use OpenSSL engine configuration for Android keystore */ + if (wpa_config_set_quoted(ssid, "engine_id", "keystore") < 0 || + wpa_config_set_quoted(ssid, "key_id", + cred->private_key + 11) < 0 || + wpa_config_set(ssid, "engine", "1", 0) < 0) + return -1; + } else +#endif /* ANDROID */ + if (cred->private_key && cred->private_key[0] && + wpa_config_set_quoted(ssid, "private_key", cred->private_key) < 0) + return -1; + + if (cred->private_key_passwd && cred->private_key_passwd[0] && + wpa_config_set_quoted(ssid, "private_key_passwd", + cred->private_key_passwd) < 0) + return -1; + + if (cred->phase1) { + os_free(ssid->eap.phase1); + ssid->eap.phase1 = os_strdup(cred->phase1); + } + if (cred->phase2) { + os_free(ssid->eap.phase2); + ssid->eap.phase2 = os_strdup(cred->phase2); + } + + if (cred->ca_cert && cred->ca_cert[0] && + wpa_config_set_quoted(ssid, "ca_cert", cred->ca_cert) < 0) + return -1; + + if (cred->domain_suffix_match && cred->domain_suffix_match[0] && + wpa_config_set_quoted(ssid, "domain_suffix_match", + cred->domain_suffix_match) < 0) + return -1; + + return 0; +} + + +static int interworking_connect_roaming_consortium( + struct wpa_supplicant *wpa_s, struct wpa_cred *cred, + struct wpa_bss *bss) +{ + struct wpa_ssid *ssid; + + wpa_printf(MSG_DEBUG, "Interworking: Connect with " MACSTR " based on " + "roaming consortium match", MAC2STR(bss->bssid)); + + if (already_connected(wpa_s, cred, bss)) { + wpa_msg(wpa_s, MSG_INFO, INTERWORKING_ALREADY_CONNECTED MACSTR, + MAC2STR(bss->bssid)); + return 0; + } + + remove_duplicate_network(wpa_s, cred, bss); + + ssid = wpa_config_add_network(wpa_s->conf); + if (ssid == NULL) + return -1; + ssid->parent_cred = cred; + wpas_notify_network_added(wpa_s, ssid); + wpa_config_set_network_defaults(ssid); + ssid->priority = cred->priority; + ssid->temporary = 1; + ssid->ssid = os_zalloc(bss->ssid_len + 1); + if (ssid->ssid == NULL) + goto fail; + os_memcpy(ssid->ssid, bss->ssid, bss->ssid_len); + ssid->ssid_len = bss->ssid_len; + + if (interworking_set_hs20_params(wpa_s, ssid) < 0) + goto fail; + + if (cred->eap_method == NULL) { + wpa_printf(MSG_DEBUG, "Interworking: No EAP method set for " + "credential using roaming consortium"); + goto fail; + } + + if (interworking_set_eap_params( + ssid, cred, + cred->eap_method->vendor == EAP_VENDOR_IETF && + cred->eap_method->method == EAP_TYPE_TTLS) < 0) + goto fail; + + wpa_config_update_prio_list(wpa_s->conf); + interworking_reconnect(wpa_s); + + return 0; + +fail: + wpas_notify_network_removed(wpa_s, ssid); + wpa_config_remove_network(wpa_s->conf, ssid->id); + return -1; +} + + +int interworking_connect(struct wpa_supplicant *wpa_s, struct wpa_bss *bss) +{ + struct wpa_cred *cred, *cred_rc, *cred_3gpp; + struct wpa_ssid *ssid; struct nai_realm *realm; + struct nai_realm_eap *eap = NULL; u16 count, i; - int found = 0; + char buf[100]; - if (bss->anqp_nai_realm == NULL) - return 0; + if (wpa_s->conf->cred == NULL || bss == NULL) + return -1; + if (disallowed_bssid(wpa_s, bss->bssid) || + disallowed_ssid(wpa_s, bss->ssid, bss->ssid_len)) { + wpa_printf(MSG_DEBUG, "Interworking: Reject connection to disallowed BSS " + MACSTR, MAC2STR(bss->bssid)); + return -1; + } + + if (!wpa_bss_get_ie(bss, WLAN_EID_RSN)) { + /* + * We currently support only HS 2.0 networks and those are + * required to use WPA2-Enterprise. + */ + wpa_printf(MSG_DEBUG, "Interworking: Network does not use " + "RSN"); + return -1; + } + + cred_rc = interworking_credentials_available_roaming_consortium(wpa_s, + bss); + if (cred_rc) { + wpa_printf(MSG_DEBUG, "Interworking: Highest roaming " + "consortium matching credential priority %d", + cred_rc->priority); + } + + cred = interworking_credentials_available_realm(wpa_s, bss); + if (cred) { + wpa_printf(MSG_DEBUG, "Interworking: Highest NAI Realm list " + "matching credential priority %d", + cred->priority); + } + + cred_3gpp = interworking_credentials_available_3gpp(wpa_s, bss); + if (cred_3gpp) { + wpa_printf(MSG_DEBUG, "Interworking: Highest 3GPP matching " + "credential priority %d", cred_3gpp->priority); + } + + if (cred_rc && + (cred == NULL || cred_rc->priority >= cred->priority) && + (cred_3gpp == NULL || cred_rc->priority >= cred_3gpp->priority)) + return interworking_connect_roaming_consortium(wpa_s, cred_rc, + bss); + + if (cred_3gpp && + (cred == NULL || cred_3gpp->priority >= cred->priority)) { + return interworking_connect_3gpp(wpa_s, cred_3gpp, bss); + } + + if (cred == NULL) { + wpa_printf(MSG_DEBUG, "Interworking: No matching credentials " + "found for " MACSTR, MAC2STR(bss->bssid)); + return -1; + } + + realm = nai_realm_parse(bss->anqp ? bss->anqp->nai_realm : NULL, + &count); + if (realm == NULL) { + wpa_printf(MSG_DEBUG, "Interworking: Could not parse NAI " + "Realm list from " MACSTR, MAC2STR(bss->bssid)); + return -1; + } + + for (i = 0; i < count; i++) { + if (!nai_realm_match(&realm[i], cred->realm)) + continue; + eap = nai_realm_find_eap(cred, &realm[i]); + if (eap) + break; + } + + if (!eap) { + wpa_printf(MSG_DEBUG, "Interworking: No matching credentials " + "and EAP method found for " MACSTR, + MAC2STR(bss->bssid)); + nai_realm_free(realm, count); + return -1; + } - if (wpa_s->conf->home_realm == NULL) + wpa_printf(MSG_DEBUG, "Interworking: Connect with " MACSTR, + MAC2STR(bss->bssid)); + + if (already_connected(wpa_s, cred, bss)) { + wpa_msg(wpa_s, MSG_INFO, INTERWORKING_ALREADY_CONNECTED MACSTR, + MAC2STR(bss->bssid)); + nai_realm_free(realm, count); return 0; + } + + remove_duplicate_network(wpa_s, cred, bss); + + ssid = wpa_config_add_network(wpa_s->conf); + if (ssid == NULL) { + nai_realm_free(realm, count); + return -1; + } + ssid->parent_cred = cred; + wpas_notify_network_added(wpa_s, ssid); + wpa_config_set_network_defaults(ssid); + ssid->priority = cred->priority; + ssid->temporary = 1; + ssid->ssid = os_zalloc(bss->ssid_len + 1); + if (ssid->ssid == NULL) + goto fail; + os_memcpy(ssid->ssid, bss->ssid, bss->ssid_len); + ssid->ssid_len = bss->ssid_len; + + if (interworking_set_hs20_params(wpa_s, ssid) < 0) + goto fail; + + if (wpa_config_set(ssid, "eap", eap_get_name(EAP_VENDOR_IETF, + eap->method), 0) < 0) + goto fail; + + switch (eap->method) { + case EAP_TYPE_TTLS: + if (eap->inner_method) { + os_snprintf(buf, sizeof(buf), "\"autheap=%s\"", + eap_get_name(EAP_VENDOR_IETF, + eap->inner_method)); + if (wpa_config_set(ssid, "phase2", buf, 0) < 0) + goto fail; + break; + } + switch (eap->inner_non_eap) { + case NAI_REALM_INNER_NON_EAP_PAP: + if (wpa_config_set(ssid, "phase2", "\"auth=PAP\"", 0) < + 0) + goto fail; + break; + case NAI_REALM_INNER_NON_EAP_CHAP: + if (wpa_config_set(ssid, "phase2", "\"auth=CHAP\"", 0) + < 0) + goto fail; + break; + case NAI_REALM_INNER_NON_EAP_MSCHAP: + if (wpa_config_set(ssid, "phase2", "\"auth=MSCHAP\"", + 0) < 0) + goto fail; + break; + case NAI_REALM_INNER_NON_EAP_MSCHAPV2: + if (wpa_config_set(ssid, "phase2", "\"auth=MSCHAPV2\"", + 0) < 0) + goto fail; + break; + default: + /* EAP params were not set - assume TTLS/MSCHAPv2 */ + if (wpa_config_set(ssid, "phase2", "\"auth=MSCHAPV2\"", + 0) < 0) + goto fail; + break; + } + break; + case EAP_TYPE_PEAP: + case EAP_TYPE_FAST: + if (wpa_config_set(ssid, "phase1", "\"fast_provisioning=2\"", + 0) < 0) + goto fail; + if (wpa_config_set(ssid, "pac_file", + "\"blob://pac_interworking\"", 0) < 0) + goto fail; + os_snprintf(buf, sizeof(buf), "\"auth=%s\"", + eap_get_name(EAP_VENDOR_IETF, + eap->inner_method ? + eap->inner_method : + EAP_TYPE_MSCHAPV2)); + if (wpa_config_set(ssid, "phase2", buf, 0) < 0) + goto fail; + break; + case EAP_TYPE_TLS: + break; + } + + if (interworking_set_eap_params(ssid, cred, + eap->method == EAP_TYPE_TTLS) < 0) + goto fail; + + nai_realm_free(realm, count); + + wpa_config_update_prio_list(wpa_s->conf); + interworking_reconnect(wpa_s); + + return 0; + +fail: + wpas_notify_network_removed(wpa_s, ssid); + wpa_config_remove_network(wpa_s->conf, ssid->id); + nai_realm_free(realm, count); + return -1; +} + + +static struct wpa_cred * interworking_credentials_available_3gpp( + struct wpa_supplicant *wpa_s, struct wpa_bss *bss) +{ + struct wpa_cred *selected = NULL; +#ifdef INTERWORKING_3GPP + struct wpa_cred *cred; + int ret; + + if (bss->anqp == NULL || bss->anqp->anqp_3gpp == NULL) + return NULL; + +#ifdef CONFIG_EAP_PROXY + if (!wpa_s->imsi[0]) { + size_t len; + wpa_printf(MSG_DEBUG, "Interworking: IMSI not available - try to read again through eap_proxy"); + wpa_s->mnc_len = eapol_sm_get_eap_proxy_imsi(wpa_s->eapol, + wpa_s->imsi, + &len); + if (wpa_s->mnc_len > 0) { + wpa_s->imsi[len] = '\0'; + wpa_printf(MSG_DEBUG, "eap_proxy: IMSI %s (MNC length %d)", + wpa_s->imsi, wpa_s->mnc_len); + } else { + wpa_printf(MSG_DEBUG, "eap_proxy: IMSI not available"); + } + } +#endif /* CONFIG_EAP_PROXY */ + + for (cred = wpa_s->conf->cred; cred; cred = cred->next) { + char *sep; + const char *imsi; + int mnc_len; + char imsi_buf[16]; + size_t msin_len; + +#ifdef PCSC_FUNCS + if (cred->pcsc && wpa_s->conf->pcsc_reader && wpa_s->scard && + wpa_s->imsi[0]) { + imsi = wpa_s->imsi; + mnc_len = wpa_s->mnc_len; + goto compare; + } +#endif /* PCSC_FUNCS */ +#ifdef CONFIG_EAP_PROXY + if (cred->pcsc && wpa_s->mnc_len > 0 && wpa_s->imsi[0]) { + imsi = wpa_s->imsi; + mnc_len = wpa_s->mnc_len; + goto compare; + } +#endif /* CONFIG_EAP_PROXY */ + + if (cred->imsi == NULL || !cred->imsi[0] || + (!wpa_s->conf->external_sim && + (cred->milenage == NULL || !cred->milenage[0]))) + continue; + + sep = os_strchr(cred->imsi, '-'); + if (sep == NULL || + (sep - cred->imsi != 5 && sep - cred->imsi != 6)) + continue; + mnc_len = sep - cred->imsi - 3; + os_memcpy(imsi_buf, cred->imsi, 3 + mnc_len); + sep++; + msin_len = os_strlen(cred->imsi); + if (3 + mnc_len + msin_len >= sizeof(imsi_buf) - 1) + msin_len = sizeof(imsi_buf) - 3 - mnc_len - 1; + os_memcpy(&imsi_buf[3 + mnc_len], sep, msin_len); + imsi_buf[3 + mnc_len + msin_len] = '\0'; + imsi = imsi_buf; + +#if defined(PCSC_FUNCS) || defined(CONFIG_EAP_PROXY) + compare: +#endif /* PCSC_FUNCS || CONFIG_EAP_PROXY */ + wpa_printf(MSG_DEBUG, "Interworking: Parsing 3GPP info from " + MACSTR, MAC2STR(bss->bssid)); + ret = plmn_id_match(bss->anqp->anqp_3gpp, imsi, mnc_len); + wpa_printf(MSG_DEBUG, "PLMN match %sfound", ret ? "" : "not "); + if (ret) { + if (cred_excluded_ssid(cred, bss)) + continue; + if (cred_no_required_oi_match(cred, bss)) + continue; + if (selected == NULL || + selected->priority < cred->priority) + selected = cred; + } + } +#endif /* INTERWORKING_3GPP */ + return selected; +} + + +static struct wpa_cred * interworking_credentials_available_realm( + struct wpa_supplicant *wpa_s, struct wpa_bss *bss) +{ + struct wpa_cred *cred, *selected = NULL; + struct nai_realm *realm; + u16 count, i; + + if (bss->anqp == NULL || bss->anqp->nai_realm == NULL) + return NULL; + + if (wpa_s->conf->cred == NULL) + return NULL; wpa_printf(MSG_DEBUG, "Interworking: Parsing NAI Realm list from " MACSTR, MAC2STR(bss->bssid)); - realm = nai_realm_parse(bss->anqp_nai_realm, &count); + realm = nai_realm_parse(bss->anqp->nai_realm, &count); if (realm == NULL) { wpa_printf(MSG_DEBUG, "Interworking: Could not parse NAI " "Realm list from " MACSTR, MAC2STR(bss->bssid)); - return 0; + return NULL; } - for (i = 0; i < count; i++) { - if (!nai_realm_match(&realm[i], wpa_s->conf->home_realm)) + for (cred = wpa_s->conf->cred; cred; cred = cred->next) { + if (cred->realm == NULL) continue; - if (nai_realm_find_eap(wpa_s, &realm[i])) { - found++; - break; + + for (i = 0; i < count; i++) { + if (!nai_realm_match(&realm[i], cred->realm)) + continue; + if (nai_realm_find_eap(cred, &realm[i])) { + if (cred_excluded_ssid(cred, bss)) + continue; + if (cred_no_required_oi_match(cred, bss)) + continue; + if (selected == NULL || + selected->priority < cred->priority) + selected = cred; + break; + } } } nai_realm_free(realm, count); - return found; + return selected; +} + + +static struct wpa_cred * interworking_credentials_available( + struct wpa_supplicant *wpa_s, struct wpa_bss *bss) +{ + struct wpa_cred *cred, *cred2; + + if (disallowed_bssid(wpa_s, bss->bssid) || + disallowed_ssid(wpa_s, bss->ssid, bss->ssid_len)) { + wpa_printf(MSG_DEBUG, "Interworking: Ignore disallowed BSS " + MACSTR, MAC2STR(bss->bssid)); + return NULL; + } + + cred = interworking_credentials_available_realm(wpa_s, bss); + cred2 = interworking_credentials_available_3gpp(wpa_s, bss); + if (cred && cred2 && cred2->priority >= cred->priority) + cred = cred2; + if (!cred) + cred = cred2; + + cred2 = interworking_credentials_available_roaming_consortium(wpa_s, + bss); + if (cred && cred2 && cred2->priority >= cred->priority) + cred = cred2; + if (!cred) + cred = cred2; + + return cred; } -static int interworking_credentials_available(struct wpa_supplicant *wpa_s, - struct wpa_bss *bss) +static int domain_name_list_contains(struct wpabuf *domain_names, + const char *domain) { - return interworking_credentials_available_realm(wpa_s, bss) || - interworking_credentials_available_3gpp(wpa_s, bss); + const u8 *pos, *end; + size_t len; + + len = os_strlen(domain); + pos = wpabuf_head(domain_names); + end = pos + wpabuf_len(domain_names); + + while (pos + 1 < end) { + if (pos + 1 + pos[0] > end) + break; + + wpa_hexdump_ascii(MSG_DEBUG, "Interworking: AP domain name", + pos + 1, pos[0]); + if (pos[0] == len && + os_strncasecmp(domain, (const char *) (pos + 1), len) == 0) + return 1; + + pos += 1 + pos[0]; + } + + return 0; +} + + +int interworking_home_sp_cred(struct wpa_supplicant *wpa_s, + struct wpa_cred *cred, + struct wpabuf *domain_names) +{ + size_t i; + int ret = -1; +#ifdef INTERWORKING_3GPP + char nai[100], *realm; + + char *imsi = NULL; + int mnc_len = 0; + if (cred->imsi) + imsi = cred->imsi; +#ifdef CONFIG_PCSC + else if (cred->pcsc && wpa_s->conf->pcsc_reader && + wpa_s->scard && wpa_s->imsi[0]) { + imsi = wpa_s->imsi; + mnc_len = wpa_s->mnc_len; + } +#endif /* CONFIG_PCSC */ +#ifdef CONFIG_EAP_PROXY + else if (cred->pcsc && wpa_s->mnc_len > 0 && wpa_s->imsi[0]) { + imsi = wpa_s->imsi; + mnc_len = wpa_s->mnc_len; + } +#endif /* CONFIG_EAP_PROXY */ + if (domain_names && + imsi && build_root_nai(nai, sizeof(nai), imsi, mnc_len, 0) == 0) { + realm = os_strchr(nai, '@'); + if (realm) + realm++; + wpa_printf(MSG_DEBUG, "Interworking: Search for match " + "with SIM/USIM domain %s", realm); + if (realm && + domain_name_list_contains(domain_names, realm)) + return 1; + if (realm) + ret = 0; + } +#endif /* INTERWORKING_3GPP */ + + if (domain_names == NULL || cred->domain == NULL) + return ret; + + for (i = 0; i < cred->num_domain; i++) { + wpa_printf(MSG_DEBUG, "Interworking: Search for match with " + "home SP FQDN %s", cred->domain[i]); + if (domain_name_list_contains(domain_names, cred->domain[i])) + return 1; + } + + return 0; +} + + +static int interworking_home_sp(struct wpa_supplicant *wpa_s, + struct wpabuf *domain_names) +{ + struct wpa_cred *cred; + + if (domain_names == NULL || wpa_s->conf->cred == NULL) + return -1; + + for (cred = wpa_s->conf->cred; cred; cred = cred->next) { + int res = interworking_home_sp_cred(wpa_s, cred, domain_names); + if (res) + return res; + } + + return 0; +} + + +static int interworking_find_network_match(struct wpa_supplicant *wpa_s) +{ + struct wpa_bss *bss; + struct wpa_ssid *ssid; + + dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) { + for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) { + if (wpas_network_disabled(wpa_s, ssid) || + ssid->mode != WPAS_MODE_INFRA) + continue; + if (ssid->ssid_len != bss->ssid_len || + os_memcmp(ssid->ssid, bss->ssid, ssid->ssid_len) != + 0) + continue; + /* + * TODO: Consider more accurate matching of security + * configuration similarly to what is done in events.c + */ + return 1; + } + } + + return 0; } static void interworking_select_network(struct wpa_supplicant *wpa_s) { - struct wpa_bss *bss, *selected = NULL; + struct wpa_bss *bss, *selected = NULL, *selected_home = NULL; + int selected_prio = -999999, selected_home_prio = -999999; unsigned int count = 0; + const char *type; + int res; + struct wpa_cred *cred; wpa_s->network_select = 0; dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) { - if (!interworking_credentials_available(wpa_s, bss)) + cred = interworking_credentials_available(wpa_s, bss); + if (!cred) continue; + if (!wpa_bss_get_ie(bss, WLAN_EID_RSN)) { + /* + * We currently support only HS 2.0 networks and those + * are required to use WPA2-Enterprise. + */ + wpa_printf(MSG_DEBUG, "Interworking: Credential match " + "with " MACSTR " but network does not use " + "RSN", MAC2STR(bss->bssid)); + continue; + } count++; - wpa_msg(wpa_s, MSG_INFO, INTERWORKING_AP MACSTR, - MAC2STR(bss->bssid)); - if (selected == NULL && wpa_s->auto_select) - selected = bss; + res = interworking_home_sp(wpa_s, bss->anqp ? + bss->anqp->domain_name : NULL); + if (res > 0) + type = "home"; + else if (res == 0) + type = "roaming"; + else + type = "unknown"; + wpa_msg(wpa_s, MSG_INFO, INTERWORKING_AP MACSTR " type=%s", + MAC2STR(bss->bssid), type); + if (wpa_s->auto_select || + (wpa_s->conf->auto_interworking && + wpa_s->auto_network_select)) { + if (selected == NULL || + cred->priority > selected_prio) { + selected = bss; + selected_prio = cred->priority; + } + if (res > 0 && + (selected_home == NULL || + cred->priority > selected_home_prio)) { + selected_home = bss; + selected_home_prio = cred->priority; + } + } + } + + if (selected_home && selected_home != selected && + selected_home_prio >= selected_prio) { + /* Prefer network operated by the Home SP */ + selected = selected_home; } if (count == 0) { + /* + * No matching network was found based on configured + * credentials. Check whether any of the enabled network blocks + * have matching APs. + */ + if (interworking_find_network_match(wpa_s)) { + wpa_printf(MSG_DEBUG, "Interworking: Possible BSS " + "match for enabled network configurations"); + if (wpa_s->auto_select) + interworking_reconnect(wpa_s); + return; + } + + if (wpa_s->auto_network_select) { + wpa_printf(MSG_DEBUG, "Interworking: Continue " + "scanning after ANQP fetch"); + wpa_supplicant_req_scan(wpa_s, wpa_s->scan_interval, + 0); + return; + } + wpa_msg(wpa_s, MSG_INFO, INTERWORKING_NO_MATCH "No network " "with matching credentials found"); } @@ -859,13 +1842,50 @@ } +static struct wpa_bss_anqp * +interworking_match_anqp_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss) +{ + struct wpa_bss *other; + + if (is_zero_ether_addr(bss->hessid)) + return NULL; /* Cannot be in the same homegenous ESS */ + + dl_list_for_each(other, &wpa_s->bss, struct wpa_bss, list) { + if (other == bss) + continue; + if (other->anqp == NULL) + continue; + if (other->anqp->roaming_consortium == NULL && + other->anqp->nai_realm == NULL && + other->anqp->anqp_3gpp == NULL && + other->anqp->domain_name == NULL) + continue; + if (!(other->flags & WPA_BSS_ANQP_FETCH_TRIED)) + continue; + if (os_memcmp(bss->hessid, other->hessid, ETH_ALEN) != 0) + continue; + if (bss->ssid_len != other->ssid_len || + os_memcmp(bss->ssid, other->ssid, bss->ssid_len) != 0) + continue; + + wpa_printf(MSG_DEBUG, "Interworking: Share ANQP data with " + "already fetched BSSID " MACSTR " and " MACSTR, + MAC2STR(other->bssid), MAC2STR(bss->bssid)); + other->anqp->users++; + return other->anqp; + } + + return NULL; +} + + static void interworking_next_anqp_fetch(struct wpa_supplicant *wpa_s) { struct wpa_bss *bss; int found = 0; const u8 *ie; - if (!wpa_s->fetch_anqp_in_progress) + if (eloop_terminated() || !wpa_s->fetch_anqp_in_progress) return; dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) { @@ -874,8 +1894,22 @@ ie = wpa_bss_get_ie(bss, WLAN_EID_EXT_CAPAB); if (ie == NULL || ie[1] < 4 || !(ie[5] & 0x80)) continue; /* AP does not support Interworking */ + if (disallowed_bssid(wpa_s, bss->bssid) || + disallowed_ssid(wpa_s, bss->ssid, bss->ssid_len)) + continue; /* Disallowed BSS */ if (!(bss->flags & WPA_BSS_ANQP_FETCH_TRIED)) { + if (bss->anqp == NULL) { + bss->anqp = interworking_match_anqp_info(wpa_s, + bss); + if (bss->anqp) { + /* Shared data already fetched */ + continue; + } + bss->anqp = wpa_bss_anqp_alloc(); + if (bss->anqp == NULL) + break; + } found++; bss->flags |= WPA_BSS_ANQP_FETCH_TRIED; wpa_msg(wpa_s, MSG_INFO, "Starting ANQP fetch for " @@ -894,7 +1928,7 @@ } -static void interworking_start_fetch_anqp(struct wpa_supplicant *wpa_s) +void interworking_start_fetch_anqp(struct wpa_supplicant *wpa_s) { struct wpa_bss *bss; @@ -912,6 +1946,7 @@ return 0; wpa_s->network_select = 0; + wpa_s->fetch_all_anqp = 1; interworking_start_fetch_anqp(wpa_s); @@ -939,8 +1974,10 @@ freq = wpa_s->assoc_freq; bss = wpa_bss_get_bssid(wpa_s, dst); - if (bss) + if (bss) { + wpa_bss_anqp_unshare_alloc(bss); freq = bss->freq; + } if (freq <= 0) return -1; @@ -954,22 +1991,29 @@ res = gas_query_req(wpa_s->gas, dst, freq, buf, anqp_resp_cb, wpa_s); if (res < 0) { wpa_printf(MSG_DEBUG, "ANQP: Failed to send Query Request"); + wpabuf_free(buf); ret = -1; } else wpa_printf(MSG_DEBUG, "ANQP: Query started with dialog token " "%u", res); - wpabuf_free(buf); return ret; } static void interworking_parse_rx_anqp_resp(struct wpa_supplicant *wpa_s, - const u8 *sa, u16 info_id, + struct wpa_bss *bss, const u8 *sa, + u16 info_id, const u8 *data, size_t slen) { const u8 *pos = data; - struct wpa_bss *bss = wpa_bss_get_bssid(wpa_s, sa); + struct wpa_bss_anqp *anqp = NULL; +#ifdef CONFIG_HS20 + u8 type; +#endif /* CONFIG_HS20 */ + + if (bss) + anqp = bss->anqp; switch (info_id) { case ANQP_CAPABILITY_LIST: @@ -980,9 +2024,9 @@ wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR " Venue Name", MAC2STR(sa)); wpa_hexdump_ascii(MSG_DEBUG, "ANQP: Venue Name", pos, slen); - if (bss) { - wpabuf_free(bss->anqp_venue_name); - bss->anqp_venue_name = wpabuf_alloc_copy(pos, slen); + if (anqp) { + wpabuf_free(anqp->venue_name); + anqp->venue_name = wpabuf_alloc_copy(pos, slen); } break; case ANQP_NETWORK_AUTH_TYPE: @@ -991,10 +2035,9 @@ MAC2STR(sa)); wpa_hexdump_ascii(MSG_DEBUG, "ANQP: Network Authentication " "Type", pos, slen); - if (bss) { - wpabuf_free(bss->anqp_network_auth_type); - bss->anqp_network_auth_type = - wpabuf_alloc_copy(pos, slen); + if (anqp) { + wpabuf_free(anqp->network_auth_type); + anqp->network_auth_type = wpabuf_alloc_copy(pos, slen); } break; case ANQP_ROAMING_CONSORTIUM: @@ -1002,10 +2045,9 @@ " Roaming Consortium list", MAC2STR(sa)); wpa_hexdump_ascii(MSG_DEBUG, "ANQP: Roaming Consortium", pos, slen); - if (bss) { - wpabuf_free(bss->anqp_roaming_consortium); - bss->anqp_roaming_consortium = - wpabuf_alloc_copy(pos, slen); + if (anqp) { + wpabuf_free(anqp->roaming_consortium); + anqp->roaming_consortium = wpabuf_alloc_copy(pos, slen); } break; case ANQP_IP_ADDR_TYPE_AVAILABILITY: @@ -1014,9 +2056,9 @@ MAC2STR(sa)); wpa_hexdump(MSG_MSGDUMP, "ANQP: IP Address Availability", pos, slen); - if (bss) { - wpabuf_free(bss->anqp_ip_addr_type_availability); - bss->anqp_ip_addr_type_availability = + if (anqp) { + wpabuf_free(anqp->ip_addr_type_availability); + anqp->ip_addr_type_availability = wpabuf_alloc_copy(pos, slen); } break; @@ -1024,9 +2066,9 @@ wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR " NAI Realm list", MAC2STR(sa)); wpa_hexdump_ascii(MSG_DEBUG, "ANQP: NAI Realm", pos, slen); - if (bss) { - wpabuf_free(bss->anqp_nai_realm); - bss->anqp_nai_realm = wpabuf_alloc_copy(pos, slen); + if (anqp) { + wpabuf_free(anqp->nai_realm); + anqp->nai_realm = wpabuf_alloc_copy(pos, slen); } break; case ANQP_3GPP_CELLULAR_NETWORK: @@ -1034,18 +2076,18 @@ " 3GPP Cellular Network information", MAC2STR(sa)); wpa_hexdump_ascii(MSG_DEBUG, "ANQP: 3GPP Cellular Network", pos, slen); - if (bss) { - wpabuf_free(bss->anqp_3gpp); - bss->anqp_3gpp = wpabuf_alloc_copy(pos, slen); + if (anqp) { + wpabuf_free(anqp->anqp_3gpp); + anqp->anqp_3gpp = wpabuf_alloc_copy(pos, slen); } break; case ANQP_DOMAIN_NAME: wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR " Domain Name list", MAC2STR(sa)); wpa_hexdump_ascii(MSG_MSGDUMP, "ANQP: Domain Name", pos, slen); - if (bss) { - wpabuf_free(bss->anqp_domain_name); - bss->anqp_domain_name = wpabuf_alloc_copy(pos, slen); + if (anqp) { + wpabuf_free(anqp->domain_name); + anqp->domain_name = wpabuf_alloc_copy(pos, slen); } break; case ANQP_VENDOR_SPECIFIC: @@ -1053,6 +2095,28 @@ return; switch (WPA_GET_BE24(pos)) { +#ifdef CONFIG_HS20 + case OUI_WFA: + pos += 3; + slen -= 3; + + if (slen < 1) + return; + type = *pos++; + slen--; + + switch (type) { + case HS20_ANQP_OUI_TYPE: + hs20_parse_rx_hs20_anqp_resp(wpa_s, sa, pos, + slen); + break; + default: + wpa_printf(MSG_DEBUG, "HS20: Unsupported ANQP " + "vendor type %u", type); + break; + } + break; +#endif /* CONFIG_HS20 */ default: wpa_printf(MSG_DEBUG, "Interworking: Unsupported " "vendor-specific ANQP OUI %06x", @@ -1078,6 +2142,7 @@ const u8 *end; u16 info_id; u16 slen; + struct wpa_bss *bss = NULL, *tmp; if (result != GAS_QUERY_SUCCESS) return; @@ -1090,6 +2155,21 @@ return; } + /* + * If possible, select the BSS entry based on which BSS entry was used + * for the request. This can help in cases where multiple BSS entries + * may exist for the same AP. + */ + dl_list_for_each_reverse(tmp, &wpa_s->bss, struct wpa_bss, list) { + if (tmp == wpa_s->interworking_gas_bss && + os_memcmp(tmp->bssid, dst, ETH_ALEN) == 0) { + bss = tmp; + break; + } + } + if (bss == NULL) + bss = wpa_bss_get_bssid(wpa_s, dst); + pos = wpabuf_head(resp); end = pos + wpabuf_len(resp); @@ -1107,7 +2187,7 @@ "for Info ID %u", info_id); break; } - interworking_parse_rx_anqp_resp(wpa_s, dst, info_id, pos, + interworking_parse_rx_anqp_resp(wpa_s, bss, dst, info_id, pos, slen); pos += slen; } @@ -1123,16 +2203,110 @@ } -int interworking_select(struct wpa_supplicant *wpa_s, int auto_select) +int interworking_select(struct wpa_supplicant *wpa_s, int auto_select, + int *freqs) { interworking_stop_fetch_anqp(wpa_s); wpa_s->network_select = 1; + wpa_s->auto_network_select = 0; wpa_s->auto_select = !!auto_select; + wpa_s->fetch_all_anqp = 0; wpa_printf(MSG_DEBUG, "Interworking: Start scan for network " "selection"); wpa_s->scan_res_handler = interworking_scan_res_handler; - wpa_s->scan_req = 2; + wpa_s->normal_scans = 0; + wpa_s->scan_req = MANUAL_SCAN_REQ; + os_free(wpa_s->manual_scan_freqs); + wpa_s->manual_scan_freqs = freqs; + wpa_s->after_wps = 0; + wpa_s->known_wps_freq = 0; wpa_supplicant_req_scan(wpa_s, 0, 0); return 0; } + + +static void gas_resp_cb(void *ctx, const u8 *addr, u8 dialog_token, + enum gas_query_result result, + const struct wpabuf *adv_proto, + const struct wpabuf *resp, u16 status_code) +{ + struct wpa_supplicant *wpa_s = ctx; + struct wpabuf *n; + + wpa_msg(wpa_s, MSG_INFO, GAS_RESPONSE_INFO "addr=" MACSTR + " dialog_token=%d status_code=%d resp_len=%d", + MAC2STR(addr), dialog_token, status_code, + resp ? (int) wpabuf_len(resp) : -1); + if (!resp) + return; + + n = wpabuf_dup(resp); + if (n == NULL) + return; + wpabuf_free(wpa_s->prev_gas_resp); + wpa_s->prev_gas_resp = wpa_s->last_gas_resp; + os_memcpy(wpa_s->prev_gas_addr, wpa_s->last_gas_addr, ETH_ALEN); + wpa_s->prev_gas_dialog_token = wpa_s->last_gas_dialog_token; + wpa_s->last_gas_resp = n; + os_memcpy(wpa_s->last_gas_addr, addr, ETH_ALEN); + wpa_s->last_gas_dialog_token = dialog_token; +} + + +int gas_send_request(struct wpa_supplicant *wpa_s, const u8 *dst, + const struct wpabuf *adv_proto, + const struct wpabuf *query) +{ + struct wpabuf *buf; + int ret = 0; + int freq; + struct wpa_bss *bss; + int res; + size_t len; + u8 query_resp_len_limit = 0, pame_bi = 0; + + freq = wpa_s->assoc_freq; + bss = wpa_bss_get_bssid(wpa_s, dst); + if (bss) + freq = bss->freq; + if (freq <= 0) + return -1; + + wpa_printf(MSG_DEBUG, "GAS request to " MACSTR " (freq %d MHz)", + MAC2STR(dst), freq); + wpa_hexdump_buf(MSG_DEBUG, "Advertisement Protocol ID", adv_proto); + wpa_hexdump_buf(MSG_DEBUG, "GAS Query", query); + + len = 3 + wpabuf_len(adv_proto) + 2; + if (query) + len += wpabuf_len(query); + buf = gas_build_initial_req(0, len); + if (buf == NULL) + return -1; + + /* Advertisement Protocol IE */ + wpabuf_put_u8(buf, WLAN_EID_ADV_PROTO); + wpabuf_put_u8(buf, 1 + wpabuf_len(adv_proto)); /* Length */ + wpabuf_put_u8(buf, (query_resp_len_limit & 0x7f) | + (pame_bi ? 0x80 : 0)); + wpabuf_put_buf(buf, adv_proto); + + /* GAS Query */ + if (query) { + wpabuf_put_le16(buf, wpabuf_len(query)); + wpabuf_put_buf(buf, query); + } else + wpabuf_put_le16(buf, 0); + + res = gas_query_req(wpa_s->gas, dst, freq, buf, gas_resp_cb, wpa_s); + if (res < 0) { + wpa_printf(MSG_DEBUG, "GAS: Failed to send Query Request"); + wpabuf_free(buf); + ret = -1; + } else + wpa_printf(MSG_DEBUG, "GAS: Query started with dialog token " + "%u", res); + + return ret; +} diff -Nru wpa-1.0/wpa_supplicant/interworking.h wpa-2.1/wpa_supplicant/interworking.h --- wpa-1.0/wpa_supplicant/interworking.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/interworking.h 2014-02-04 11:23:35.000000000 +0000 @@ -1,15 +1,9 @@ /* * Interworking (IEEE 802.11u) - * Copyright (c) 2011, Qualcomm Atheros + * Copyright (c) 2011-2012, Qualcomm Atheros * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef INTERWORKING_H @@ -23,9 +17,17 @@ enum gas_query_result result, const struct wpabuf *adv_proto, const struct wpabuf *resp, u16 status_code); +int gas_send_request(struct wpa_supplicant *wpa_s, const u8 *dst, + const struct wpabuf *adv_proto, + const struct wpabuf *query); int interworking_fetch_anqp(struct wpa_supplicant *wpa_s); void interworking_stop_fetch_anqp(struct wpa_supplicant *wpa_s); -int interworking_select(struct wpa_supplicant *wpa_s, int auto_select); +int interworking_select(struct wpa_supplicant *wpa_s, int auto_select, + int *freqs); int interworking_connect(struct wpa_supplicant *wpa_s, struct wpa_bss *bss); +void interworking_start_fetch_anqp(struct wpa_supplicant *wpa_s); +int interworking_home_sp_cred(struct wpa_supplicant *wpa_s, + struct wpa_cred *cred, + struct wpabuf *domain_names); #endif /* INTERWORKING_H */ diff -Nru wpa-1.0/wpa_supplicant/main.c wpa-2.1/wpa_supplicant/main.c --- wpa-1.0/wpa_supplicant/main.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/main.c 2014-02-04 11:23:35.000000000 +0000 @@ -1,15 +1,9 @@ /* * WPA Supplicant / main() function for UNIX like OSes and MinGW - * Copyright (c) 2003-2007, Jouni Malinen + * Copyright (c) 2003-2013, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -20,8 +14,7 @@ #include "common.h" #include "wpa_supplicant_i.h" #include "driver_i.h" - -extern struct wpa_driver_ops *wpa_drivers[]; +#include "p2p_supplicant.h" static void usage(void) @@ -29,16 +22,29 @@ int i; printf("%s\n\n%s\n" "usage:\n" - " wpa_supplicant [-BddhKLqqstuvW] [-P] " + " wpa_supplicant [-BddhKLqq" +#ifdef CONFIG_DEBUG_SYSLOG + "s" +#endif /* CONFIG_DEBUG_SYSLOG */ + "t" +#ifdef CONFIG_DBUS + "u" +#endif /* CONFIG_DBUS */ + "vW] [-P] " "[-g] \\\n" + " [-G] \\\n" " -i -c [-C] [-D] " "[-p] \\\n" - " [-b] [-f] [-e] " - "\\\n" + " [-b] [-e]" +#ifdef CONFIG_DEBUG_FILE + " [-f]" +#endif /* CONFIG_DEBUG_FILE */ + " \\\n" " [-o] [-O] \\\n" " [-N -i -c [-C] " "[-D] \\\n" - " [-p] [-b] ...]\n" + " [-p] [-b] [-I] " + "...]\n" "\n" "drivers:\n", wpa_supplicant_version, wpa_supplicant_license); @@ -56,6 +62,7 @@ " -c = Configuration file\n" " -C = ctrl_interface parameter (only used if -c is not)\n" " -i = interface name\n" + " -I = additional configuration file\n" " -d = increase debugging verbosity (-dd even more)\n" " -D = driver name (can be multiple drivers: nl80211,wext)\n" " -e = entropy file\n"); @@ -63,13 +70,18 @@ printf(" -f = log output to debug file instead of stdout\n"); #endif /* CONFIG_DEBUG_FILE */ printf(" -g = global ctrl_interface\n" + " -G = global ctrl_interface group\n" " -K = include keys (passwords, etc.) in debug output\n"); #ifdef CONFIG_DEBUG_SYSLOG printf(" -s = log output to syslog instead of stdout\n"); #endif /* CONFIG_DEBUG_SYSLOG */ +#ifdef CONFIG_DEBUG_LINUX_TRACING + printf(" -T = record to Linux tracing in addition to logging\n"); + printf(" (records all messages regardless of debug verbosity)\n"); +#endif /* CONFIG_DEBUG_LINUX_TRACING */ printf(" -t = include timestamp in debug messages\n" " -h = show this help text\n" - " -L = show license (GPL and BSD)\n" + " -L = show license (BSD)\n" " -o = override driver parameter for new interfaces\n" " -O = override ctrl_interface parameter for new interfaces\n" " -p = driver parameters\n" @@ -84,7 +96,7 @@ printf("example:\n" " wpa_supplicant -D%s -iwlan0 -c/etc/wpa_supplicant.conf\n", - wpa_drivers[i] ? wpa_drivers[i]->name : "wext"); + wpa_drivers[0] ? wpa_drivers[0]->name : "nl80211"); #endif /* CONFIG_NO_STDOUT_DEBUG */ } @@ -103,20 +115,31 @@ } -static void wpa_supplicant_fd_workaround(void) +static void wpa_supplicant_fd_workaround(int start) { #ifdef __linux__ - int s, i; + static int fd[3] = { -1, -1, -1 }; + int i; /* When started from pcmcia-cs scripts, wpa_supplicant might start with * fd 0, 1, and 2 closed. This will cause some issues because many * places in wpa_supplicant are still printing out to stdout. As a * workaround, make sure that fd's 0, 1, and 2 are not used for other * sockets. */ - for (i = 0; i < 3; i++) { - s = open("/dev/null", O_RDWR); - if (s > 2) { - close(s); - break; + if (start) { + for (i = 0; i < 3; i++) { + fd[i] = open("/dev/null", O_RDWR); + if (fd[i] > 2) { + close(fd[i]); + fd[i] = -1; + break; + } + } + } else { + for (i = 0; i < 3; i++) { + if (fd[i] >= 0) { + close(fd[i]); + fd[i] = -1; + } } } #endif /* __linux__ */ @@ -142,10 +165,11 @@ return -1; iface_count = 1; - wpa_supplicant_fd_workaround(); + wpa_supplicant_fd_workaround(1); for (;;) { - c = getopt(argc, argv, "b:Bc:C:D:de:f:g:hi:KLNo:O:p:P:qstuvW"); + c = getopt(argc, argv, + "b:Bc:C:D:de:f:g:G:hi:I:KLNo:O:p:P:qsTtuvW"); if (c < 0) break; switch (c) { @@ -185,6 +209,9 @@ case 'g': params.ctrl_interface = optarg; break; + case 'G': + params.ctrl_interface_group = optarg; + break; case 'h': usage(); exitcode = 0; @@ -192,6 +219,9 @@ case 'i': iface->ifname = optarg; break; + case 'I': + iface->confanother = optarg; + break; case 'K': params.wpa_debug_show_keys++; break; @@ -220,6 +250,11 @@ params.wpa_debug_syslog++; break; #endif /* CONFIG_DEBUG_SYSLOG */ +#ifdef CONFIG_DEBUG_LINUX_TRACING + case 'T': + params.wpa_debug_tracing++; + break; +#endif /* CONFIG_DEBUG_LINUX_TRACING */ case 't': params.wpa_debug_timestamp++; break; @@ -237,8 +272,8 @@ break; case 'N': iface_count++; - iface = os_realloc(ifaces, iface_count * - sizeof(struct wpa_interface)); + iface = os_realloc_array(ifaces, iface_count, + sizeof(struct wpa_interface)); if (iface == NULL) goto out; ifaces = iface; @@ -258,9 +293,14 @@ wpa_printf(MSG_ERROR, "Failed to initialize wpa_supplicant"); exitcode = -1; goto out; + } else { + wpa_printf(MSG_INFO, "Successfully initialized " + "wpa_supplicant"); } for (i = 0; exitcode == 0 && i < iface_count; i++) { + struct wpa_supplicant *wpa_s; + if ((ifaces[i].confname == NULL && ifaces[i].ctrl_interface == NULL) || ifaces[i].ifname == NULL) { @@ -271,8 +311,18 @@ exitcode = -1; break; } - if (wpa_supplicant_add_iface(global, &ifaces[i]) == NULL) + wpa_s = wpa_supplicant_add_iface(global, &ifaces[i]); + if (wpa_s == NULL) { + exitcode = -1; + break; + } +#ifdef CONFIG_P2P + if (wpa_s->global->p2p == NULL && + (wpa_s->drv_flags & + WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE) && + wpas_p2p_add_p2pdev_interface(wpa_s) < 0) exitcode = -1; +#endif /* CONFIG_P2P */ } if (exitcode == 0) @@ -281,6 +331,7 @@ wpa_supplicant_deinit(global); out: + wpa_supplicant_fd_workaround(0); os_free(ifaces); os_free(params.pid_file); diff -Nru wpa-1.0/wpa_supplicant/main_none.c wpa-2.1/wpa_supplicant/main_none.c --- wpa-1.0/wpa_supplicant/main_none.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/main_none.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * WPA Supplicant / Example program entrypoint * Copyright (c) 2003-2005, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff -Nru wpa-1.0/wpa_supplicant/main_symbian.cpp wpa-2.1/wpa_supplicant/main_symbian.cpp --- wpa-1.0/wpa_supplicant/main_symbian.cpp 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/main_symbian.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,48 +0,0 @@ -/* - * WPA Supplicant / Program entrypoint for Symbian - * Copyright (c) 2003-2007, Jouni Malinen - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#include "includes.h" - -extern "C" { -#include "common.h" -#include "wpa_supplicant_i.h" -} - -GLDEF_C TInt E32Main(void) -{ - struct wpa_interface iface; - int exitcode = 0; - struct wpa_params params; - struct wpa_global *global; - - memset(¶ms, 0, sizeof(params)); - params.wpa_debug_level = MSG_INFO; - - global = wpa_supplicant_init(¶ms); - if (global == NULL) - return -1; - - memset(&iface, 0, sizeof(iface)); - /* TODO: set interface parameters */ - - if (wpa_supplicant_add_iface(global, &iface) == NULL) - exitcode = -1; - - if (exitcode == 0) - exitcode = wpa_supplicant_run(global); - - wpa_supplicant_deinit(global); - - return exitcode; -} diff -Nru wpa-1.0/wpa_supplicant/main_winmain.c wpa-2.1/wpa_supplicant/main_winmain.c --- wpa-1.0/wpa_supplicant/main_winmain.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/main_winmain.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * WPA Supplicant / WinMain() function for Windows-based applications * Copyright (c) 2006, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff -Nru wpa-1.0/wpa_supplicant/main_winsvc.c wpa-2.1/wpa_supplicant/main_winsvc.c --- wpa-1.0/wpa_supplicant/main_winsvc.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/main_winsvc.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * WPA Supplicant / main() function for Win32 service * Copyright (c) 2003-2006, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. * * The root of wpa_supplicant configuration in registry is * HKEY_LOCAL_MACHINE\\SOFTWARE\\%wpa_supplicant. This level includes global diff -Nru wpa-1.0/wpa_supplicant/Makefile wpa-2.1/wpa_supplicant/Makefile --- wpa-1.0/wpa_supplicant/Makefile 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/Makefile 2014-02-04 11:23:35.000000000 +0000 @@ -10,11 +10,17 @@ export BINDIR ?= /usr/local/sbin/ PKG_CONFIG ?= pkg-config -CFLAGS += -I../src -CFLAGS += -I../src/utils +CFLAGS += -I$(abspath ../src) +CFLAGS += -I$(abspath ../src/utils) -include .config +ifdef CONFIG_TESTING_OPTIONS +CFLAGS += -DCONFIG_TESTING_OPTIONS +CONFIG_WPS_TESTING=y +CONFIG_TDLS_TESTING=y +endif + BINALL=wpa_supplicant wpa_cli ifndef CONFIG_NO_WPA_PASSPHRASE @@ -55,6 +61,11 @@ install: $(addprefix $(DESTDIR)$(BINDIR)/,$(BINALL)) $(MAKE) -C ../src install +ifdef CONFIG_FIPS +CONFIG_NO_RANDOM_POOL= +CONFIG_OPENSSL_CMAC=y +endif + OBJS = config.o OBJS += notify.o OBJS += bss.o @@ -108,11 +119,37 @@ OBJS += ../src/utils/$(CONFIG_ELOOP).o OBJS_c += ../src/utils/$(CONFIG_ELOOP).o +ifeq ($(CONFIG_ELOOP), eloop) +# Using glibc < 2.17 requires -lrt for clock_gettime() +LIBS += -lrt +LIBS_c += -lrt +LIBS_p += -lrt +endif + +ifdef CONFIG_ELOOP_POLL +CFLAGS += -DCONFIG_ELOOP_POLL +endif + ifdef CONFIG_EAPOL_TEST CFLAGS += -Werror -DEAPOL_TEST endif +ifdef CONFIG_CODE_COVERAGE +CFLAGS += -O0 -fprofile-arcs -ftest-coverage +LIBS += -lgcov +LIBS_c += -lgcov +LIBS_p += -lgcov +endif + +ifdef CONFIG_HT_OVERRIDES +CFLAGS += -DCONFIG_HT_OVERRIDES +endif + +ifdef CONFIG_VHT_OVERRIDES +CFLAGS += -DCONFIG_VHT_OVERRIDES +endif + ifndef CONFIG_BACKEND CONFIG_BACKEND=file endif @@ -159,6 +196,18 @@ NEED_AES_OMAC1=y endif +ifdef CONFIG_SAE +CFLAGS += -DCONFIG_SAE +OBJS += ../src/common/sae.o +NEED_ECC=y +NEED_DH_GROUPS=y +endif + +ifdef CONFIG_WNM +CFLAGS += -DCONFIG_WNM +OBJS += wnm_sta.o +endif + ifdef CONFIG_TDLS CFLAGS += -DCONFIG_TDLS OBJS += ../src/rsn_supp/tdls.o @@ -186,7 +235,7 @@ NEED_MD5=y NEED_RC4=y else -CFLAGS += -DCONFIG_NO_WPA -DCONFIG_NO_WPA2 +CFLAGS += -DCONFIG_NO_WPA endif ifdef CONFIG_IBSS_RSN @@ -208,6 +257,7 @@ OBJS += ../src/p2p/p2p_dev_disc.o OBJS += ../src/p2p/p2p_group.o OBJS += ../src/ap/p2p_hostapd.o +OBJS += ../src/utils/bitfield.o CFLAGS += -DCONFIG_P2P NEED_GAS=y NEED_OFFCHANNEL=y @@ -219,16 +269,23 @@ endif endif +ifdef CONFIG_WIFI_DISPLAY +CFLAGS += -DCONFIG_WIFI_DISPLAY +OBJS += wifi_display.o +endif + +ifdef CONFIG_HS20 +OBJS += hs20_supplicant.o +CFLAGS += -DCONFIG_HS20 +CONFIG_INTERWORKING=y +endif + ifdef CONFIG_INTERWORKING OBJS += interworking.o CFLAGS += -DCONFIG_INTERWORKING NEED_GAS=y endif -ifdef CONFIG_NO_WPA2 -CFLAGS += -DCONFIG_NO_WPA2 -endif - include ../src/drivers/drivers.mak ifdef CONFIG_AP OBJS_d += $(DRV_BOTH_OBJS) @@ -282,6 +339,17 @@ CONFIG_IEEE8021X_EAPOL=y endif +ifdef CONFIG_EAP_UNAUTH_TLS +# EAP-UNAUTH-TLS +CFLAGS += -DEAP_UNAUTH_TLS +ifndef CONFIG_EAP_UNAUTH_TLS +OBJS += ../src/eap_peer/eap_tls.o +OBJS_h += ../src/eap_server/eap_server_tls.o +TLS_FUNCS=y +endif +CONFIG_IEEE8021X_EAPOL=y +endif + ifdef CONFIG_EAP_PEAP # EAP-PEAP ifeq ($(CONFIG_EAP_PEAP), dyn) @@ -435,6 +503,13 @@ NEED_AES_CBC=y endif +ifdef CONFIG_EAP_PROXY +CFLAGS += -DCONFIG_EAP_PROXY +OBJS += ../src/eap_peer/eap_proxy_$(CONFIG_EAP_PROXY).o +include eap_proxy_$(CONFIG_EAP_PROXY).mk +CONFIG_IEEE8021X_EAPOL=y +endif + ifdef CONFIG_EAP_AKA_PRIME # EAP-AKA' ifeq ($(CONFIG_EAP_AKA_PRIME), dyn) @@ -516,8 +591,24 @@ ifdef CONFIG_EAP_PWD CFLAGS += -DEAP_PWD OBJS += ../src/eap_peer/eap_pwd.o ../src/eap_common/eap_pwd_common.o -OBJS_h += ../src/eap_server/eap_pwd.o +OBJS_h += ../src/eap_server/eap_server_pwd.o +CONFIG_IEEE8021X_EAPOL=y +NEED_SHA256=y +endif + +ifdef CONFIG_EAP_EKE +# EAP-EKE +ifeq ($(CONFIG_EAP_EKE), dyn) +CFLAGS += -DEAP_EKE_DYNAMIC +EAPDYN += ../src/eap_peer/eap_eke.so +else +CFLAGS += -DEAP_EKE +OBJS += ../src/eap_peer/eap_eke.o ../src/eap_common/eap_eke_common.o +OBJS_h += ../src/eap_server/eap_server_eke.o +endif CONFIG_IEEE8021X_EAPOL=y +NEED_DH_GROUPS=y +NEED_DH_GROUPS_ALL=y NEED_SHA256=y endif @@ -548,25 +639,10 @@ NEED_AES_CBC=y NEED_MODEXP=y -ifdef CONFIG_WPS_UFD -CFLAGS += -DCONFIG_WPS_UFD -OBJS += ../src/wps/wps_ufd.o -NEED_WPS_OOB=y -endif - ifdef CONFIG_WPS_NFC CFLAGS += -DCONFIG_WPS_NFC OBJS += ../src/wps/ndef.o -OBJS += ../src/wps/wps_nfc.o NEED_WPS_OOB=y -ifdef CONFIG_WPS_NFC_PN531 -PN531_PATH ?= /usr/local/src/nfc -CFLAGS += -DCONFIG_WPS_NFC_PN531 -CFLAGS += -I${PN531_PATH}/inc -OBJS += ../src/wps/wps_nfc_pn531.o -LIBS += ${PN531_PATH}/lib/wpsnfc.dll -LIBS += ${PN531_PATH}/lib/libnfc_mapping_pn53x.dll -endif endif ifdef NEED_WPS_OOB @@ -692,8 +768,15 @@ OBJS += ../src/ap/drv_callbacks.o OBJS += ../src/ap/ap_drv_ops.o OBJS += ../src/ap/beacon.o +OBJS += ../src/ap/eap_user_db.o ifdef CONFIG_IEEE80211N OBJS += ../src/ap/ieee802_11_ht.o +ifdef CONFIG_IEEE80211AC +OBJS += ../src/ap/ieee802_11_vht.o +endif +endif +ifdef CONFIG_WNM +OBJS += ../src/ap/wnm_ap.o endif ifdef CONFIG_CTRL_IFACE OBJS += ../src/ap/ctrl_iface_ap.o @@ -706,6 +789,9 @@ ifdef CONFIG_IEEE80211N CFLAGS += -DCONFIG_IEEE80211N +ifdef CONFIG_IEEE80211AC +CFLAGS += -DCONFIG_IEEE80211AC +endif endif ifdef NEED_AP_MLME @@ -713,6 +799,7 @@ OBJS += ../src/ap/ap_list.o OBJS += ../src/ap/ieee802_11.o OBJS += ../src/ap/hw_features.o +OBJS += ../src/ap/dfs.o CFLAGS += -DNEED_AP_MLME endif ifdef CONFIG_WPS @@ -720,6 +807,12 @@ OBJS += ../src/ap/wps_hostapd.o OBJS += ../src/eap_server/eap_server_wsc.o endif +ifdef CONFIG_INTERWORKING +OBJS += ../src/ap/gas_serv.o +endif +ifdef CONFIG_HS20 +OBJS += ../src/ap/hs20.o +endif endif ifdef NEED_RSN_AUTHENTICATOR @@ -818,7 +911,11 @@ # Shared TLS functions (needed for EAP_TLS, EAP_PEAP, EAP_TTLS, and EAP_FAST) OBJS += ../src/eap_peer/eap_tls_common.o OBJS_h += ../src/eap_server/eap_server_tls_common.o +ifndef CONFIG_FIPS NEED_TLS_PRF=y +NEED_SHA1=y +NEED_MD5=y +endif endif ifndef CONFIG_TLS @@ -829,6 +926,11 @@ CFLAGS += -DCONFIG_TLSV11 endif +ifdef CONFIG_TLSV12 +CFLAGS += -DCONFIG_TLSV12 +NEED_SHA256=y +endif + ifeq ($(CONFIG_TLS), openssl) ifdef TLS_FUNCS CFLAGS += -DEAP_TLS_OPENSSL @@ -842,6 +944,10 @@ endif LIBS += -lcrypto LIBS_p += -lcrypto +ifdef CONFIG_TLS_ADD_DL +LIBS += -ldl +LIBS_p += -ldl +endif endif ifeq ($(CONFIG_TLS), gnutls) @@ -913,6 +1019,9 @@ NEED_SHA256=y NEED_BASE64=y NEED_TLS_PRF=y +ifdef CONFIG_TLSV12 +NEED_TLS_PRF_SHA256=y +endif NEED_MODEXP=y NEED_CIPHER=y CFLAGS += -DCONFIG_TLS_INTERNAL_CLIENT @@ -1018,8 +1127,12 @@ endif ifdef NEED_AES_OMAC1 NEED_AES_ENC=y +ifdef CONFIG_OPENSSL_CMAC +CFLAGS += -DCONFIG_OPENSSL_CMAC +else AESOBJS += ../src/crypto/aes-omac1.o endif +endif ifdef NEED_AES_WRAP NEED_AES_ENC=y AESOBJS += ../src/crypto/aes-wrap.o @@ -1038,7 +1151,10 @@ endif ifdef NEED_SHA1 +ifneq ($(CONFIG_TLS), openssl) SHA1OBJS += ../src/crypto/sha1.o +endif +SHA1OBJS += ../src/crypto/sha1-prf.o ifdef CONFIG_INTERNAL_SHA1 SHA1OBJS += ../src/crypto/sha1-internal.o ifdef NEED_FIPS186_2_PRF @@ -1048,8 +1164,10 @@ ifdef CONFIG_NO_WPA_PASSPHRASE CFLAGS += -DCONFIG_NO_PBKDF2 else +ifneq ($(CONFIG_TLS), openssl) SHA1OBJS += ../src/crypto/sha1-pbkdf2.o endif +endif ifdef NEED_T_PRF SHA1OBJS += ../src/crypto/sha1-tprf.o endif @@ -1058,14 +1176,13 @@ endif endif -MD5OBJS = ../src/crypto/md5.o +ifndef CONFIG_FIPS +MD5OBJS += ../src/crypto/md5.o +endif ifdef NEED_MD5 ifdef CONFIG_INTERNAL_MD5 MD5OBJS += ../src/crypto/md5-internal.o endif -ifdef CONFIG_FIPS -MD5OBJS += ../src/crypto/md5-non-fips.o -endif OBJS += $(MD5OBJS) OBJS_p += $(MD5OBJS) endif @@ -1092,10 +1209,16 @@ SHA256OBJS = # none by default ifdef NEED_SHA256 CFLAGS += -DCONFIG_SHA256 +ifneq ($(CONFIG_TLS), openssl) SHA256OBJS += ../src/crypto/sha256.o +endif +SHA256OBJS += ../src/crypto/sha256-prf.o ifdef CONFIG_INTERNAL_SHA256 SHA256OBJS += ../src/crypto/sha256-internal.o endif +ifdef NEED_TLS_PRF_SHA256 +SHA256OBJS += ../src/crypto/sha256-tlsprf.o +endif OBJS += $(SHA256OBJS) endif @@ -1111,6 +1234,10 @@ endif endif +ifdef NEED_ECC +CFLAGS += -DCONFIG_ECC +endif + ifdef CONFIG_NO_RANDOM_POOL CFLAGS += -DCONFIG_NO_RANDOM_POOL else @@ -1135,6 +1262,11 @@ ifeq ($(CONFIG_CTRL_IFACE), named_pipe) CFLAGS += -DCONFIG_CTRL_IFACE_NAMED_PIPE endif +ifeq ($(CONFIG_CTRL_IFACE), udp-remote) +CONFIG_CTRL_IFACE=udp +CFLAGS += -DCONFIG_CTRL_IFACE_UDP +CFLAGS += -DCONFIG_CTRL_IFACE_UDP_REMOTE +endif OBJS += ctrl_iface.o ctrl_iface_$(CONFIG_CTRL_IFACE).o endif @@ -1251,6 +1383,10 @@ endif endif +ifdef CONFIG_DEBUG_LINUX_TRACING +CFLAGS += -DCONFIG_DEBUG_LINUX_TRACING +endif + ifdef CONFIG_DEBUG_FILE CFLAGS += -DCONFIG_DEBUG_FILE endif @@ -1261,11 +1397,15 @@ ifdef CONFIG_FIPS CFLAGS += -DCONFIG_FIPS +ifneq ($(CONFIG_TLS), openssl) +$(error CONFIG_FIPS=y requires CONFIG_TLS=openssl) +endif endif OBJS += $(SHA1OBJS) $(DESOBJS) OBJS_p += $(SHA1OBJS) +OBJS_p += $(SHA256OBJS) ifdef CONFIG_BGSCAN_SIMPLE CFLAGS += -DCONFIG_BGSCAN_SIMPLE @@ -1284,6 +1424,34 @@ OBJS += bgscan.o endif +ifdef CONFIG_AUTOSCAN_EXPONENTIAL +CFLAGS += -DCONFIG_AUTOSCAN_EXPONENTIAL +OBJS += autoscan_exponential.o +NEED_AUTOSCAN=y +endif + +ifdef CONFIG_AUTOSCAN_PERIODIC +CFLAGS += -DCONFIG_AUTOSCAN_PERIODIC +OBJS += autoscan_periodic.o +NEED_AUTOSCAN=y +endif + +ifdef NEED_AUTOSCAN +CFLAGS += -DCONFIG_AUTOSCAN +OBJS += autoscan.o +endif + +ifdef CONFIG_EXT_PASSWORD_TEST +OBJS += ../src/utils/ext_password_test.o +CFLAGS += -DCONFIG_EXT_PASSWORD_TEST +NEED_EXT_PASSWORD=y +endif + +ifdef NEED_EXT_PASSWORD +OBJS += ../src/utils/ext_password.o +CFLAGS += -DCONFIG_EXT_PASSWORD +endif + ifdef NEED_GAS OBJS += ../src/common/gas.o OBJS += gas_query.o @@ -1297,6 +1465,7 @@ endif OBJS += ../src/drivers/driver_common.o +OBJS_priv += ../src/drivers/driver_common.o OBJS_wpa_rm := ctrl_iface.o ctrl_iface_unix.o OBJS_wpa := $(filter-out $(OBJS_wpa_rm),$(OBJS)) $(OBJS_h) tests/test_wpa.o @@ -1312,6 +1481,10 @@ OBJS_t += ../src/utils/ip_addr.o endif OBJS_t2 := $(OBJS) $(OBJS_l2) preauth_test.o + +OBJS_nfc := $(OBJS) $(OBJS_l2) nfc_pw_token.o +OBJS_nfc += $(OBJS_d) ../src/drivers/drivers.o + OBJS += $(CONFIG_MAIN).o ifdef CONFIG_PRIVSEP @@ -1390,15 +1563,17 @@ $(Q)$(LDO) $(LDFLAGS) -o wpa_priv $(OBJS_priv) $(LIBS) @$(E) " LD " $@ -wpa_supplicant: .config $(BCHECK) $(OBJS) $(EXTRA_progs) +$(OBJS_c) $(OBJS_t) $(OBJS_t2) $(OBJS) $(BCHECK) $(EXTRA_progs): .config + +wpa_supplicant: $(BCHECK) $(OBJS) $(EXTRA_progs) $(Q)$(LDO) $(LDFLAGS) -o wpa_supplicant $(OBJS) $(LIBS) $(EXTRALIBS) @$(E) " LD " $@ -eapol_test: .config $(OBJS_t) +eapol_test: $(OBJS_t) $(Q)$(LDO) $(LDFLAGS) -o eapol_test $(OBJS_t) $(LIBS) @$(E) " LD " $@ -preauth_test: .config $(OBJS_t2) +preauth_test: $(OBJS_t2) $(Q)$(LDO) $(LDFLAGS) -o preauth_test $(OBJS_t2) $(LIBS) @$(E) " LD " $@ @@ -1418,6 +1593,10 @@ $(Q)$(LDO) $(LDFLAGS) -o test_wpa $(OBJS_wpa) $(LIBS) @$(E) " LD " $@ +nfc_pw_token: $(OBJS_nfc) + $(Q)$(LDO) $(LDFLAGS) -o nfc_pw_token $(OBJS_nfc) $(LIBS) + @$(E) " LD " $@ + win_if_list: win_if_list.c $(Q)$(LDO) $(LDFLAGS) -o $@ win_if_list.c $(CFLAGS) $(LIBS_w) @$(E) " LD " $@ @@ -1442,17 +1621,30 @@ $(CC) $(LDFLAGS) -o $@ $(CFLAGS) -shared -rdynamic -fPIC $^ \ -Deap_peer_ikev2_register=eap_peer_method_dynamic_init +eap_eke.so: ../src/eap_peer/eap_eke.c ../src/eap_common/eap_eke_common.c + $(CC) $(LDFLAGS) -o $@ $(CFLAGS) -shared -rdynamic -fPIC $^ \ + -Deap_peer_eke_register=eap_peer_method_dynamic_init + %.so: %.c $(CC) $(LDFLAGS) -o $@ $(CFLAGS) -shared -rdynamic -fPIC $< \ -D$(*F:eap_%=eap_peer_%)_register=eap_peer_method_dynamic_init +ifdef CONFIG_CODE_COVERAGE +%.o: %.c + @$(E) " CC " $< + $(Q)cd $(dir $@); $(CC) -c -o $(notdir $@) $(CFLAGS) $(notdir $<) +else %.o: %.c $(Q)$(CC) -c -o $@ $(CFLAGS) $< @$(E) " CC " $< +endif %.service: %.service.in sed -e 's|\@BINDIR\@|$(BINDIR)|g' $< >$@ +%@.service: %.service.arg.in + sed -e 's|\@BINDIR\@|$(BINDIR)|g' $< >$@ + wpa_supplicant.exe: wpa_supplicant mv -f $< $@ wpa_cli.exe: wpa_cli @@ -1469,11 +1661,8 @@ windows-bin: $(WINALL) $(STRIP) $(WINALL) -wpa_gui/Makefile: - qmake -o wpa_gui/Makefile wpa_gui/wpa_gui.pro - -wpa_gui: wpa_gui/Makefile - $(MAKE) -C wpa_gui +wpa_gui: + @echo "wpa_gui has been removed - see wpa_gui-qt4 for replacement" wpa_gui-qt4/Makefile: qmake -o wpa_gui-qt4/Makefile wpa_gui-qt4/wpa_gui.pro @@ -1495,10 +1684,23 @@ tests: test-eap_sim_common +FIPSDIR=/usr/local/ssl/fips-2.0 +FIPSLD=$(FIPSDIR)/bin/fipsld +fips: + $(MAKE) CC=$(FIPSLD) FIPSLD_CC="$(CC)" + +lcov-html: wpa_supplicant.gcda + lcov -c -d .. > lcov.info + genhtml lcov.info --output-directory lcov-html + clean: $(MAKE) -C ../src clean $(MAKE) -C dbus clean - rm -f core *~ *.o *.d eap_*.so $(ALL) $(WINALL) eapol_test preauth_test + rm -f core *~ *.o *.d *.gcno *.gcda *.gcov + rm -f eap_*.so $(ALL) $(WINALL) eapol_test preauth_test rm -f wpa_priv + rm -f nfc_pw_token + rm -f lcov.info + rm -rf lcov-html -include $(OBJS:%.o=%.d) diff -Nru wpa-1.0/wpa_supplicant/nfc_pw_token.c wpa-2.1/wpa_supplicant/nfc_pw_token.c --- wpa-1.0/wpa_supplicant/nfc_pw_token.c 1970-01-01 00:00:00.000000000 +0000 +++ wpa-2.1/wpa_supplicant/nfc_pw_token.c 2014-02-04 11:23:35.000000000 +0000 @@ -0,0 +1,83 @@ +/* + * nfc_pw_token - Tool for building NFC password tokens for WPS + * Copyright (c) 2012, 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 "crypto/random.h" +#include "wpa_supplicant_i.h" +#include "config.h" +#include "wps_supplicant.h" + + +static void print_bin(const char *title, const struct wpabuf *buf) +{ + size_t i, len; + const u8 *pos; + + if (buf == NULL) + return; + + printf("%s=", title); + + pos = wpabuf_head(buf); + len = wpabuf_len(buf); + for (i = 0; i < len; i++) + printf("%02X", *pos++); + + printf("\n"); +} + + +int main(int argc, char *argv[]) +{ + struct wpa_supplicant wpa_s; + int ret = -1; + struct wpabuf *buf = NULL, *ndef = NULL; + char txt[1000]; + + if (os_program_init()) + return -1; + random_init(NULL); + + os_memset(&wpa_s, 0, sizeof(wpa_s)); + wpa_s.conf = os_zalloc(sizeof(*wpa_s.conf)); + if (wpa_s.conf == NULL) + goto fail; + + buf = wpas_wps_nfc_token(&wpa_s, 0); + if (buf == NULL) + goto fail; + + ndef = ndef_build_wifi(buf); + if (ndef == NULL) + goto fail; + + wpa_snprintf_hex_uppercase(txt, sizeof(txt), wpabuf_head(buf), + wpabuf_len(buf)); + printf("#WPS=%s\n", txt); + + wpa_snprintf_hex_uppercase(txt, sizeof(txt), wpabuf_head(ndef), + wpabuf_len(ndef)); + printf("#NDEF=%s\n", txt); + + printf("wps_nfc_dev_pw_id=%d\n", wpa_s.conf->wps_nfc_dev_pw_id); + print_bin("wps_nfc_dh_pubkey", wpa_s.conf->wps_nfc_dh_pubkey); + print_bin("wps_nfc_dh_privkey", wpa_s.conf->wps_nfc_dh_privkey); + print_bin("wps_nfc_dev_pw", wpa_s.conf->wps_nfc_dev_pw); + + ret = 0; +fail: + wpabuf_free(ndef); + wpabuf_free(buf); + wpa_config_free(wpa_s.conf); + random_deinit(); + os_program_deinit(); + + return ret; +} diff -Nru wpa-1.0/wpa_supplicant/notify.c wpa-2.1/wpa_supplicant/notify.c --- wpa-1.0/wpa_supplicant/notify.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/notify.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * wpa_supplicant - Event notifications * Copyright (c) 2009-2010, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "utils/includes.h" @@ -96,13 +90,23 @@ #ifdef ANDROID wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_STATE_CHANGE - "id=%d state=%d BSSID=" MACSTR, + "id=%d state=%d BSSID=" MACSTR " SSID=%s", wpa_s->current_ssid ? wpa_s->current_ssid->id : -1, - new_state, MAC2STR(wpa_s->pending_bssid)); + new_state, + MAC2STR(wpa_s->bssid), + wpa_s->current_ssid && wpa_s->current_ssid->ssid ? + wpa_ssid_txt(wpa_s->current_ssid->ssid, + wpa_s->current_ssid->ssid_len) : ""); #endif /* ANDROID */ } +void wpas_notify_disconnect_reason(struct wpa_supplicant *wpa_s) +{ + wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_DISCONNECT_REASON); +} + + void wpas_notify_network_changed(struct wpa_supplicant *wpa_s) { wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_CURRENT_NETWORK); @@ -222,7 +226,7 @@ * applications since these network objects won't behave like * regular ones. */ - if (wpa_s->global->p2p_group_formation != wpa_s) + if (!ssid->p2p_group && wpa_s->global->p2p_group_formation != wpa_s) wpas_dbus_register_network(wpa_s, ssid); } @@ -250,7 +254,7 @@ { if (wpa_s->wpa) wpa_sm_pmksa_cache_flush(wpa_s->wpa, ssid); - if (wpa_s->global->p2p_group_formation != wpa_s) + if (!ssid->p2p_group && wpa_s->global->p2p_group_formation != wpa_s) wpas_dbus_unregister_network(wpa_s, ssid->id); #ifdef CONFIG_P2P wpas_p2p_network_removed(wpa_s, ssid); @@ -323,6 +327,9 @@ void wpas_notify_bss_wps_changed(struct wpa_supplicant *wpa_s, unsigned int id) { +#ifdef CONFIG_WPS + wpas_dbus_bss_signal_prop_changed(wpa_s, WPAS_DBUS_BSS_PROP_WPS, id); +#endif /* CONFIG_WPS */ } @@ -544,6 +551,9 @@ */ wpas_dbus_signal_p2p_peer_joined(wpa_s, sta); #endif /* CONFIG_P2P */ + + /* Notify listeners a new station has been authorized */ + wpas_dbus_signal_sta_authorized(wpa_s, sta); } @@ -563,6 +573,9 @@ */ wpas_dbus_signal_p2p_peer_disconnected(wpa_s, sta); #endif /* CONFIG_P2P */ + + /* Notify listeners a station has been deauthorized */ + wpas_dbus_signal_sta_deauthorized(wpa_s, sta); } @@ -608,3 +621,23 @@ /* notify the new DBus API */ wpas_dbus_signal_certification(wpa_s, depth, subject, cert_hash, cert); } + + +void wpas_notify_preq(struct wpa_supplicant *wpa_s, + const u8 *addr, const u8 *dst, const u8 *bssid, + const u8 *ie, size_t ie_len, u32 ssi_signal) +{ +#ifdef CONFIG_AP + wpas_dbus_signal_preq(wpa_s, addr, dst, bssid, ie, ie_len, ssi_signal); +#endif /* CONFIG_AP */ +} + + +void wpas_notify_eap_status(struct wpa_supplicant *wpa_s, const char *status, + const char *parameter) +{ + wpas_dbus_signal_eap_status(wpa_s, status, parameter); + wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_EAP_STATUS + "status='%s' parameter='%s'", + status, parameter); +} diff -Nru wpa-1.0/wpa_supplicant/notify.h wpa-2.1/wpa_supplicant/notify.h --- wpa-1.0/wpa_supplicant/notify.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/notify.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * wpa_supplicant - Event notifications * Copyright (c) 2009-2010, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef NOTIFY_H @@ -28,6 +22,7 @@ void wpas_notify_state_changed(struct wpa_supplicant *wpa_s, enum wpa_states new_state, enum wpa_states old_state); +void wpas_notify_disconnect_reason(struct wpa_supplicant *wpa_s); void wpas_notify_network_changed(struct wpa_supplicant *wpa_s); void wpas_notify_ap_scan_changed(struct wpa_supplicant *wpa_s); void wpas_notify_bssid_changed(struct wpa_supplicant *wpa_s); @@ -127,5 +122,10 @@ void wpas_notify_certification(struct wpa_supplicant *wpa_s, int depth, const char *subject, const char *cert_hash, const struct wpabuf *cert); +void wpas_notify_preq(struct wpa_supplicant *wpa_s, + const u8 *addr, const u8 *dst, const u8 *bssid, + const u8 *ie, size_t ie_len, u32 ssi_signal); +void wpas_notify_eap_status(struct wpa_supplicant *wpa_s, const char *status, + const char *parameter); #endif /* NOTIFY_H */ diff -Nru wpa-1.0/wpa_supplicant/offchannel.c wpa-2.1/wpa_supplicant/offchannel.c --- wpa-1.0/wpa_supplicant/offchannel.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/offchannel.c 2014-02-04 11:23:35.000000000 +0000 @@ -3,14 +3,8 @@ * Copyright (c) 2009-2010, Atheros Communications * Copyright (c) 2011, Qualcomm Atheros * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -138,6 +132,17 @@ } +/** + * offchannel_send_action_tx_status - TX status callback + * @wpa_s: Pointer to wpa_supplicant data + * @dst: Destination MAC address of the transmitted Action frame + * @data: Transmitted frame payload + * @data_len: Length of @data in bytes + * @result: TX status + * + * This function is called whenever the driver indicates a TX status event for + * a frame sent by offchannel_send_action() using wpa_drv_send_action(). + */ void offchannel_send_action_tx_status( struct wpa_supplicant *wpa_s, const u8 *dst, const u8 *data, size_t data_len, enum offchannel_send_action_result result) @@ -154,6 +159,21 @@ return; } + /* Accept report only if the contents of the frame matches */ + if (data_len - wpabuf_len(wpa_s->pending_action_tx) != 24 || + os_memcmp(data + 24, wpabuf_head(wpa_s->pending_action_tx), + wpabuf_len(wpa_s->pending_action_tx)) != 0) { + wpa_printf(MSG_DEBUG, "Off-channel: Ignore Action TX status - " + "mismatching contents with pending frame"); + wpa_hexdump(MSG_MSGDUMP, "TX status frame data", + data, data_len); + wpa_hexdump_buf(MSG_MSGDUMP, "Pending TX frame", + wpa_s->pending_action_tx); + return; + } + + wpa_printf(MSG_DEBUG, "Off-channel: Delete matching pending action frame"); + wpabuf_free(wpa_s->pending_action_tx); wpa_s->pending_action_tx = NULL; @@ -170,6 +190,27 @@ } +/** + * offchannel_send_action - Request off-channel Action frame TX + * @wpa_s: Pointer to wpa_supplicant data + * @freq: The frequency in MHz indicating the channel on which the frame is to + * transmitted or 0 for the current channel (only if associated) + * @dst: Action frame destination MAC address + * @src: Action frame source MAC address + * @bssid: Action frame BSSID + * @buf: Frame to transmit starting from the Category field + * @len: Length of @buf in bytes + * @wait_time: Wait time for response in milliseconds + * @tx_cb: Callback function for indicating TX status or %NULL for now callback + * @no_cck: Whether CCK rates are to be disallowed for TX rate selection + * Returns: 0 on success or -1 on failure + * + * This function is used to request an Action frame to be transmitted on the + * current operating channel or on another channel (off-channel). The actual + * frame transmission will be delayed until the driver is ready on the specified + * channel. The @wait_time parameter can be used to request the driver to remain + * awake on the channel to wait for a response. + */ int offchannel_send_action(struct wpa_supplicant *wpa_s, unsigned int freq, const u8 *dst, const u8 *src, const u8 *bssid, const u8 *buf, size_t len, unsigned int wait_time, @@ -259,6 +300,8 @@ "channel"); if (wait_time > wpa_s->max_remain_on_chan) wait_time = wpa_s->max_remain_on_chan; + else if (wait_time == 0) + wait_time = 20; if (wpa_drv_remain_on_channel(wpa_s, freq, wait_time) < 0) { wpa_printf(MSG_DEBUG, "Off-channel: Failed to request driver " "to remain on channel (%u MHz) for Action " @@ -272,6 +315,13 @@ } +/** + * offchannel_send_send_action_done - Notify completion of Action frame sequence + * @wpa_s: Pointer to wpa_supplicant data + * + * This function can be used to cancel a wait for additional response frames on + * the channel that was used with offchannel_send_action(). + */ void offchannel_send_action_done(struct wpa_supplicant *wpa_s) { wpa_printf(MSG_DEBUG, "Off-channel: Action frame sequence done " @@ -290,6 +340,15 @@ } +/** + * offchannel_remain_on_channel_cb - Remain-on-channel callback function + * @wpa_s: Pointer to wpa_supplicant data + * @freq: Frequency (in MHz) of the selected channel + * @duration: Duration of the remain-on-channel operation in milliseconds + * + * This function is called whenever the driver notifies beginning of a + * remain-on-channel operation. + */ void offchannel_remain_on_channel_cb(struct wpa_supplicant *wpa_s, unsigned int freq, unsigned int duration) { @@ -299,6 +358,14 @@ } +/** + * offchannel_cancel_remain_on_channel_cb - Remain-on-channel stopped callback + * @wpa_s: Pointer to wpa_supplicant data + * @freq: Frequency (in MHz) of the selected channel + * + * This function is called whenever the driver notifies termination of a + * remain-on-channel operation. + */ void offchannel_cancel_remain_on_channel_cb(struct wpa_supplicant *wpa_s, unsigned int freq) { @@ -306,9 +373,42 @@ } -void offchannel_deinit(struct wpa_supplicant *wpa_s) +/** + * offchannel_pending_action_tx - Check whether there is a pending Action TX + * @wpa_s: Pointer to wpa_supplicant data + * Returns: Pointer to pending frame or %NULL if no pending operation + * + * This function can be used to check whether there is a pending Action frame TX + * operation. The returned pointer should be used only for checking whether it + * is %NULL (no pending frame) or to print the pointer value in debug + * information (i.e., the pointer should not be dereferenced). + */ +const void * offchannel_pending_action_tx(struct wpa_supplicant *wpa_s) +{ + return wpa_s->pending_action_tx; +} + + +/** + * offchannel_clear_pending_action_tx - Clear pending Action frame TX + * @wpa_s: Pointer to wpa_supplicant data + */ +void offchannel_clear_pending_action_tx(struct wpa_supplicant *wpa_s) { wpabuf_free(wpa_s->pending_action_tx); wpa_s->pending_action_tx = NULL; +} + + +/** + * offchannel_deinit - Deinit off-channel operations + * @wpa_s: Pointer to wpa_supplicant data + * + * This function is used to free up any allocated resources for off-channel + * operations. + */ +void offchannel_deinit(struct wpa_supplicant *wpa_s) +{ + offchannel_clear_pending_action_tx(wpa_s); eloop_cancel_timeout(wpas_send_action_cb, wpa_s, NULL); } diff -Nru wpa-1.0/wpa_supplicant/offchannel.h wpa-2.1/wpa_supplicant/offchannel.h --- wpa-1.0/wpa_supplicant/offchannel.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/offchannel.h 2014-02-04 11:23:35.000000000 +0000 @@ -3,14 +3,8 @@ * Copyright (c) 2009-2010, Atheros Communications * Copyright (c) 2011, Qualcomm Atheros * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef OFFCHANNEL_H @@ -35,5 +29,7 @@ void offchannel_send_action_tx_status( struct wpa_supplicant *wpa_s, const u8 *dst, const u8 *data, size_t data_len, enum offchannel_send_action_result result); +const void * offchannel_pending_action_tx(struct wpa_supplicant *wpa_s); +void offchannel_clear_pending_action_tx(struct wpa_supplicant *wpa_s); #endif /* OFFCHANNEL_H */ diff -Nru wpa-1.0/wpa_supplicant/p2p_supplicant.c wpa-2.1/wpa_supplicant/p2p_supplicant.c --- wpa-1.0/wpa_supplicant/p2p_supplicant.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/p2p_supplicant.c 2014-02-04 11:23:35.000000000 +0000 @@ -1,15 +1,10 @@ /* * wpa_supplicant - P2P * Copyright (c) 2009-2010, Atheros Communications + * Copyright (c) 2010-2014, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -23,6 +18,9 @@ #include "p2p/p2p.h" #include "ap/hostapd.h" #include "ap/ap_config.h" +#include "ap/sta_info.h" +#include "ap/ap_drv_ops.h" +#include "ap/wps_hostapd.h" #include "ap/p2p_hostapd.h" #include "eapol_supp/eapol_supp_sm.h" #include "rsn_supp/wpa.h" @@ -37,6 +35,7 @@ #include "offchannel.h" #include "wps_supplicant.h" #include "p2p_supplicant.h" +#include "wifi_display.h" /* @@ -45,6 +44,8 @@ */ #define P2P_MAX_JOIN_SCAN_ATTEMPTS 10 +#define P2P_AUTO_PD_SCAN_ATTEMPTS 5 + #ifndef P2P_MAX_CLIENT_IDLE /* * How many seconds to try to reconnect to the GO when connection in P2P client @@ -53,21 +54,146 @@ #define P2P_MAX_CLIENT_IDLE 10 #endif /* P2P_MAX_CLIENT_IDLE */ +#ifndef P2P_MAX_INITIAL_CONN_WAIT +/* + * How many seconds to wait for initial 4-way handshake to get completed after + * WPS provisioning step. + */ +#define P2P_MAX_INITIAL_CONN_WAIT 10 +#endif /* P2P_MAX_INITIAL_CONN_WAIT */ + +#ifndef P2P_MAX_INITIAL_CONN_WAIT_GO +/* + * How many seconds to wait for initial 4-way handshake to get completed after + * WPS provisioning step on the GO. This controls the extra time the P2P + * operation is considered to be in progress (e.g., to delay other scans) after + * WPS provisioning has been completed on the GO during group formation. + */ +#define P2P_MAX_INITIAL_CONN_WAIT_GO 10 +#endif /* P2P_MAX_INITIAL_CONN_WAIT_GO */ + +#ifndef P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE +/* + * How many seconds to wait for initial 4-way handshake to get completed after + * re-invocation of a persistent group on the GO when the client is expected + * to connect automatically (no user interaction). + */ +#define P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE 15 +#endif /* P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE */ + +#ifndef P2P_CONCURRENT_SEARCH_DELAY +#define P2P_CONCURRENT_SEARCH_DELAY 500 +#endif /* P2P_CONCURRENT_SEARCH_DELAY */ + +#define P2P_MGMT_DEVICE_PREFIX "p2p-dev-" + +enum p2p_group_removal_reason { + P2P_GROUP_REMOVAL_UNKNOWN, + P2P_GROUP_REMOVAL_SILENT, + P2P_GROUP_REMOVAL_FORMATION_FAILED, + P2P_GROUP_REMOVAL_REQUESTED, + P2P_GROUP_REMOVAL_IDLE_TIMEOUT, + P2P_GROUP_REMOVAL_UNAVAILABLE, + P2P_GROUP_REMOVAL_GO_ENDING_SESSION, + P2P_GROUP_REMOVAL_PSK_FAILURE, + P2P_GROUP_REMOVAL_FREQ_CONFLICT +}; + static void wpas_p2p_long_listen_timeout(void *eloop_ctx, void *timeout_ctx); static struct wpa_supplicant * wpas_p2p_get_group_iface(struct wpa_supplicant *wpa_s, int addr_allocated, int go); -static int wpas_p2p_join_start(struct wpa_supplicant *wpa_s); +static int wpas_p2p_join_start(struct wpa_supplicant *wpa_s, int freq, + const u8 *ssid, size_t ssid_len); +static void wpas_p2p_join_scan_req(struct wpa_supplicant *wpa_s, int freq, + const u8 *ssid, size_t ssid_len); static void wpas_p2p_join_scan(void *eloop_ctx, void *timeout_ctx); static int wpas_p2p_join(struct wpa_supplicant *wpa_s, const u8 *iface_addr, - const u8 *dev_addr, enum p2p_wps_method wps_method); -static void wpas_p2p_pd_before_join_timeout(void *eloop_ctx, - void *timeout_ctx); + const u8 *dev_addr, enum p2p_wps_method wps_method, + int auto_join, int freq, + const u8 *ssid, size_t ssid_len); static int wpas_p2p_create_iface(struct wpa_supplicant *wpa_s); static void wpas_p2p_cross_connect_setup(struct wpa_supplicant *wpa_s); static void wpas_p2p_group_idle_timeout(void *eloop_ctx, void *timeout_ctx); static void wpas_p2p_set_group_idle_timeout(struct wpa_supplicant *wpa_s); +static void wpas_p2p_group_formation_timeout(void *eloop_ctx, + void *timeout_ctx); +static void wpas_p2p_group_freq_conflict(void *eloop_ctx, void *timeout_ctx); +static void wpas_p2p_fallback_to_go_neg(struct wpa_supplicant *wpa_s, + int group_added); +static int wpas_p2p_stop_find_oper(struct wpa_supplicant *wpa_s); + + +/* + * Get the number of concurrent channels that the HW can operate, but that are + * currently not in use by any of the wpa_supplicant interfaces. + */ +static int wpas_p2p_num_unused_channels(struct wpa_supplicant *wpa_s) +{ + int *freqs; + int num, unused; + + freqs = os_calloc(wpa_s->num_multichan_concurrent, sizeof(int)); + if (!freqs) + return -1; + + num = get_shared_radio_freqs(wpa_s, freqs, + wpa_s->num_multichan_concurrent); + os_free(freqs); + + unused = wpa_s->num_multichan_concurrent - num; + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: num_unused_channels: %d", unused); + return unused; +} + + +/* + * Get the frequencies that are currently in use by one or more of the virtual + * interfaces, and that are also valid for P2P operation. + */ +static int wpas_p2p_valid_oper_freqs(struct wpa_supplicant *wpa_s, + int *p2p_freqs, unsigned int len) +{ + int *freqs; + unsigned int num, i, j; + + freqs = os_calloc(wpa_s->num_multichan_concurrent, sizeof(int)); + if (!freqs) + return -1; + + num = get_shared_radio_freqs(wpa_s, freqs, + wpa_s->num_multichan_concurrent); + + os_memset(p2p_freqs, 0, sizeof(int) * len); + + for (i = 0, j = 0; i < num && j < len; i++) { + if (p2p_supported_freq(wpa_s->global->p2p, freqs[i])) + p2p_freqs[j++] = freqs[i]; + } + + os_free(freqs); + + dump_freq_array(wpa_s, "valid for P2P", p2p_freqs, j); + + return j; +} + + +static void wpas_p2p_set_own_freq_preference(struct wpa_supplicant *wpa_s, + int freq) +{ + if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) + return; + if (wpa_s->parent->conf->p2p_ignore_shared_freq && + freq > 0 && wpa_s->num_multichan_concurrent > 1 && + wpas_p2p_num_unused_channels(wpa_s) > 0) { + wpa_printf(MSG_DEBUG, "P2P: Ignore own channel preference %d MHz due to p2p_ignore_shared_freq=1 configuration", + freq); + freq = 0; + } + p2p_set_own_freq_preference(wpa_s->global->p2p, freq); +} static void wpas_p2p_scan_res_handler(struct wpa_supplicant *wpa_s, @@ -75,6 +201,12 @@ { size_t i; + if (wpa_s->p2p_scan_work) { + struct wpa_radio_work *work = wpa_s->p2p_scan_work; + wpa_s->p2p_scan_work = NULL; + radio_work_done(work); + } + if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) return; @@ -83,10 +215,29 @@ for (i = 0; i < scan_res->num; i++) { struct wpa_scan_res *bss = scan_res->res[i]; + struct os_reltime time_tmp_age, entry_ts; + const u8 *ies; + size_t ies_len; + + time_tmp_age.sec = bss->age / 1000; + time_tmp_age.usec = (bss->age % 1000) * 1000; + os_reltime_sub(&scan_res->fetch_time, &time_tmp_age, &entry_ts); + + ies = (const u8 *) (bss + 1); + ies_len = bss->ie_len; + if (bss->beacon_ie_len > 0 && + !wpa_scan_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE) && + wpa_scan_get_vendor_ie_beacon(bss, P2P_IE_VENDOR_TYPE)) { + wpa_printf(MSG_DEBUG, "P2P: Use P2P IE(s) from Beacon frame since no P2P IE(s) in Probe Response frames received for " + MACSTR, MAC2STR(bss->bssid)); + ies = ies + ies_len; + ies_len = bss->beacon_ie_len; + } + + if (p2p_scan_res_handler(wpa_s->global->p2p, bss->bssid, - bss->freq, bss->level, - (const u8 *) (bss + 1), - bss->ie_len) > 0) + bss->freq, &entry_ts, bss->level, + ies, ies_len) > 0) break; } @@ -94,82 +245,125 @@ } +static void wpas_p2p_trigger_scan_cb(struct wpa_radio_work *work, int deinit) +{ + struct wpa_supplicant *wpa_s = work->wpa_s; + struct wpa_driver_scan_params *params = work->ctx; + int ret; + + if (deinit) { + wpa_scan_free_params(params); + return; + } + + ret = wpa_drv_scan(wpa_s, params); + wpa_scan_free_params(params); + work->ctx = NULL; + if (ret) { + radio_work_done(work); + return; + } + + os_get_reltime(&wpa_s->scan_trigger_time); + wpa_s->scan_res_handler = wpas_p2p_scan_res_handler; + wpa_s->own_scan_requested = 1; + wpa_s->p2p_scan_work = work; +} + + static int wpas_p2p_scan(void *ctx, enum p2p_scan_type type, int freq, unsigned int num_req_dev_types, - const u8 *req_dev_types, const u8 *dev_id) + const u8 *req_dev_types, const u8 *dev_id, u16 pw_id) { struct wpa_supplicant *wpa_s = ctx; - struct wpa_driver_scan_params params; - int ret; + struct wpa_driver_scan_params *params = NULL; struct wpabuf *wps_ie, *ies; - int social_channels[] = { 2412, 2437, 2462, 0, 0 }; size_t ielen; - int was_in_p2p_scan; + u8 *n; if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) return -1; - os_memset(¶ms, 0, sizeof(params)); + if (wpa_s->p2p_scan_work) { + wpa_dbg(wpa_s, MSG_INFO, "P2P: Reject scan trigger since one is already pending"); + return -1; + } + + params = os_zalloc(sizeof(*params)); + if (params == NULL) + return -1; /* P2P Wildcard SSID */ - params.num_ssids = 1; - params.ssids[0].ssid = (u8 *) P2P_WILDCARD_SSID; - params.ssids[0].ssid_len = P2P_WILDCARD_SSID_LEN; + params->num_ssids = 1; + n = os_malloc(P2P_WILDCARD_SSID_LEN); + if (n == NULL) + goto fail; + os_memcpy(n, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN); + params->ssids[0].ssid = n; + params->ssids[0].ssid_len = P2P_WILDCARD_SSID_LEN; wpa_s->wps->dev.p2p = 1; - wps_ie = wps_build_probe_req_ie(0, &wpa_s->wps->dev, wpa_s->wps->uuid, - WPS_REQ_ENROLLEE, + wps_ie = wps_build_probe_req_ie(pw_id, &wpa_s->wps->dev, + wpa_s->wps->uuid, WPS_REQ_ENROLLEE, num_req_dev_types, req_dev_types); if (wps_ie == NULL) - return -1; + goto fail; ielen = p2p_scan_ie_buf_len(wpa_s->global->p2p); ies = wpabuf_alloc(wpabuf_len(wps_ie) + ielen); if (ies == NULL) { wpabuf_free(wps_ie); - return -1; + goto fail; } wpabuf_put_buf(ies, wps_ie); wpabuf_free(wps_ie); p2p_scan_ie(wpa_s->global->p2p, ies, dev_id); - params.p2p_probe = 1; - params.extra_ies = wpabuf_head(ies); - params.extra_ies_len = wpabuf_len(ies); + params->p2p_probe = 1; + n = os_malloc(wpabuf_len(ies)); + if (n == NULL) { + wpabuf_free(ies); + goto fail; + } + os_memcpy(n, wpabuf_head(ies), wpabuf_len(ies)); + params->extra_ies = n; + params->extra_ies_len = wpabuf_len(ies); + wpabuf_free(ies); switch (type) { case P2P_SCAN_SOCIAL: - params.freqs = social_channels; + params->freqs = os_malloc(4 * sizeof(int)); + if (params->freqs == NULL) + goto fail; + params->freqs[0] = 2412; + params->freqs[1] = 2437; + params->freqs[2] = 2462; + params->freqs[3] = 0; break; case P2P_SCAN_FULL: break; - case P2P_SCAN_SPECIFIC: - social_channels[0] = freq; - social_channels[1] = 0; - params.freqs = social_channels; - break; case P2P_SCAN_SOCIAL_PLUS_ONE: - social_channels[3] = freq; - params.freqs = social_channels; + params->freqs = os_malloc(5 * sizeof(int)); + if (params->freqs == NULL) + goto fail; + params->freqs[0] = 2412; + params->freqs[1] = 2437; + params->freqs[2] = 2462; + params->freqs[3] = freq; + params->freqs[4] = 0; break; } - was_in_p2p_scan = wpa_s->scan_res_handler == wpas_p2p_scan_res_handler; - wpa_s->scan_res_handler = wpas_p2p_scan_res_handler; - ret = wpa_drv_scan(wpa_s, ¶ms); - - wpabuf_free(ies); - - if (ret) { - wpa_s->scan_res_handler = NULL; - if (wpa_s->scanning || was_in_p2p_scan) { - wpa_s->p2p_cb_on_scan_complete = 1; - ret = 1; - } - } + radio_remove_unstarted_work(wpa_s, "p2p-scan"); + if (radio_add_work(wpa_s, 0, "p2p-scan", 0, wpas_p2p_trigger_scan_cb, + params) < 0) + goto fail; + return 0; - return ret; +fail: + wpa_scan_free_params(params); + return -1; } @@ -213,7 +407,8 @@ } -static void wpas_p2p_group_delete(struct wpa_supplicant *wpa_s) +static int wpas_p2p_group_delete(struct wpa_supplicant *wpa_s, + enum p2p_group_removal_reason removal_reason) { struct wpa_ssid *ssid; char *gtype; @@ -223,16 +418,22 @@ if (ssid == NULL) { /* * The current SSID was not known, but there may still be a - * pending P2P group interface waiting for provisioning. + * pending P2P group interface waiting for provisioning or a + * P2P group that is trying to reconnect. */ ssid = wpa_s->conf->ssid; while (ssid) { - if (ssid->p2p_group && - (ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION || - (ssid->key_mgmt & WPA_KEY_MGMT_WPS))) + if (ssid->p2p_group && ssid->disabled != 2) break; ssid = ssid->next; } + if (ssid == NULL && + wpa_s->p2p_group_interface == NOT_P2P_GROUP_INTERFACE) + { + wpa_printf(MSG_ERROR, "P2P: P2P group interface " + "not found"); + return -1; + } } if (wpa_s->p2p_group_interface == P2P_GROUP_INTERFACE_GO) gtype = "GO"; @@ -247,30 +448,60 @@ gtype = "GO"; if (wpa_s->cross_connect_in_use) { wpa_s->cross_connect_in_use = 0; - wpa_msg(wpa_s->parent, MSG_INFO, - P2P_EVENT_CROSS_CONNECT_DISABLE "%s %s", - wpa_s->ifname, wpa_s->cross_connect_uplink); + wpa_msg_global(wpa_s->parent, MSG_INFO, + P2P_EVENT_CROSS_CONNECT_DISABLE "%s %s", + wpa_s->ifname, wpa_s->cross_connect_uplink); } - switch (wpa_s->removal_reason) { + switch (removal_reason) { case P2P_GROUP_REMOVAL_REQUESTED: reason = " reason=REQUESTED"; break; + case P2P_GROUP_REMOVAL_FORMATION_FAILED: + reason = " reason=FORMATION_FAILED"; + break; case P2P_GROUP_REMOVAL_IDLE_TIMEOUT: reason = " reason=IDLE"; break; case P2P_GROUP_REMOVAL_UNAVAILABLE: reason = " reason=UNAVAILABLE"; break; + case P2P_GROUP_REMOVAL_GO_ENDING_SESSION: + reason = " reason=GO_ENDING_SESSION"; + break; + case P2P_GROUP_REMOVAL_PSK_FAILURE: + reason = " reason=PSK_FAILURE"; + break; + case P2P_GROUP_REMOVAL_FREQ_CONFLICT: + reason = " reason=FREQ_CONFLICT"; + break; default: reason = ""; break; } - wpa_msg(wpa_s->parent, MSG_INFO, P2P_EVENT_GROUP_REMOVED "%s %s%s", - wpa_s->ifname, gtype, reason); + if (removal_reason != P2P_GROUP_REMOVAL_SILENT) { + wpa_msg_global(wpa_s->parent, MSG_INFO, + P2P_EVENT_GROUP_REMOVED "%s %s%s", + wpa_s->ifname, gtype, reason); + } - eloop_cancel_timeout(wpas_p2p_group_idle_timeout, wpa_s, NULL); + if (eloop_cancel_timeout(wpas_p2p_group_freq_conflict, wpa_s, NULL) > 0) + wpa_printf(MSG_DEBUG, "P2P: Cancelled P2P group freq_conflict timeout"); + if (eloop_cancel_timeout(wpas_p2p_group_idle_timeout, wpa_s, NULL) > 0) + wpa_printf(MSG_DEBUG, "P2P: Cancelled P2P group idle timeout"); + if (eloop_cancel_timeout(wpas_p2p_group_formation_timeout, + wpa_s->parent, NULL) > 0) { + wpa_printf(MSG_DEBUG, "P2P: Cancelled P2P group formation " + "timeout"); + wpa_s->p2p_in_provisioning = 0; + } - if (ssid) + /* + * Make sure wait for the first client does not remain active after the + * group has been removed. + */ + wpa_s->global->p2p_go_wait_client.sec = 0; + + if (removal_reason != P2P_GROUP_REMOVAL_SILENT && ssid) wpas_notify_p2p_group_removed(wpa_s, ssid, gtype); if (wpa_s->p2p_group_interface != NOT_P2P_GROUP_INTERFACE) { @@ -282,14 +513,25 @@ global = wpa_s->global; ifname = os_strdup(wpa_s->ifname); type = wpas_p2p_if_type(wpa_s->p2p_group_interface); - wpa_supplicant_remove_iface(wpa_s->global, wpa_s); + wpa_supplicant_remove_iface(wpa_s->global, wpa_s, 0); wpa_s = global->ifaces; if (wpa_s && ifname) wpa_drv_if_remove(wpa_s, type, ifname); os_free(ifname); - return; + return 1; + } + + if (!wpa_s->p2p_go_group_formation_completed) { + wpa_s->global->p2p_group_formation = NULL; + wpa_s->p2p_in_provisioning = 0; } + wpa_s->show_group_started = 0; + os_free(wpa_s->go_params); + wpa_s->go_params = NULL; + + wpa_s->waiting_presence_resp = 0; + wpa_printf(MSG_DEBUG, "P2P: Remove temporary group network"); if (ssid && (ssid->p2p_group || ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION || @@ -320,6 +562,8 @@ wpa_supplicant_ap_deinit(wpa_s); else wpa_drv_deinit_p2p_cli(wpa_s); + + return 0; } @@ -339,6 +583,10 @@ bssid = wpa_s->bssid; bss = wpa_bss_get(wpa_s, bssid, ssid, ssid_len); + if (bss == NULL && wpa_s->go_params && + !is_zero_ether_addr(wpa_s->go_params->peer_device_addr)) + bss = wpa_bss_get_p2p_dev_addr( + wpa_s, wpa_s->go_params->peer_device_addr); if (bss == NULL) { u8 iface_addr[ETH_ALEN]; if (p2p_get_interface_addr(wpa_s->global->p2p, bssid, @@ -353,6 +601,9 @@ } p2p = wpa_bss_get_vendor_ie_multi(bss, P2P_IE_VENDOR_TYPE); + if (p2p == NULL) + p2p = wpa_bss_get_vendor_ie_multi_beacon(bss, + P2P_IE_VENDOR_TYPE); if (p2p == NULL) { wpa_printf(MSG_DEBUG, "P2P: Could not figure out whether " "group is persistent - BSS " MACSTR @@ -459,6 +710,11 @@ s->ssid_len = ssid->ssid_len; os_memcpy(s->ssid, ssid->ssid, s->ssid_len); } + if (ssid->mode == WPAS_MODE_P2P_GO && wpa_s->global->add_psk) { + dl_list_add(&s->psk_list, &wpa_s->global->add_psk->list); + wpa_s->global->add_psk = NULL; + changed = 1; + } #ifndef CONFIG_NO_CONFIG_WRITE if (changed && wpa_s->conf->update_config && @@ -477,6 +733,7 @@ struct wpa_ssid *ssid, *s; u8 *n; size_t i; + int found = 0; ssid = wpa_s->current_ssid; if (ssid == NULL || ssid->mode != WPAS_MODE_P2P_GO || @@ -497,17 +754,40 @@ for (i = 0; s->p2p_client_list && i < s->num_p2p_clients; i++) { if (os_memcmp(s->p2p_client_list + i * ETH_ALEN, addr, - ETH_ALEN) == 0) - return; /* already in list */ + ETH_ALEN) != 0) + continue; + + if (i == s->num_p2p_clients - 1) + return; /* already the most recent entry */ + + /* move the entry to mark it most recent */ + os_memmove(s->p2p_client_list + i * ETH_ALEN, + s->p2p_client_list + (i + 1) * ETH_ALEN, + (s->num_p2p_clients - i - 1) * ETH_ALEN); + os_memcpy(s->p2p_client_list + + (s->num_p2p_clients - 1) * ETH_ALEN, addr, ETH_ALEN); + found = 1; + break; } - n = os_realloc(s->p2p_client_list, - (s->num_p2p_clients + 1) * ETH_ALEN); - if (n == NULL) - return; - os_memcpy(n + s->num_p2p_clients * ETH_ALEN, addr, ETH_ALEN); - s->p2p_client_list = n; - s->num_p2p_clients++; + if (!found && s->num_p2p_clients < P2P_MAX_STORED_CLIENTS) { + n = os_realloc_array(s->p2p_client_list, + s->num_p2p_clients + 1, ETH_ALEN); + if (n == NULL) + return; + os_memcpy(n + s->num_p2p_clients * ETH_ALEN, addr, ETH_ALEN); + s->p2p_client_list = n; + s->num_p2p_clients++; + } else if (!found) { + /* Not enough room for an additional entry - drop the oldest + * entry */ + os_memmove(s->p2p_client_list, + s->p2p_client_list + ETH_ALEN, + (s->num_p2p_clients - 1) * ETH_ALEN); + os_memcpy(s->p2p_client_list + + (s->num_p2p_clients - 1) * ETH_ALEN, + addr, ETH_ALEN); + } #ifndef CONFIG_NO_CONFIG_WRITE if (wpa_s->parent->conf->update_config && @@ -534,17 +814,21 @@ */ if (wpa_s->global->p2p_group_formation) wpa_s = wpa_s->global->p2p_group_formation; - wpa_s->global->p2p_group_formation = NULL; - wpa_s->p2p_in_provisioning = 0; + if (wpa_s->p2p_go_group_formation_completed) { + wpa_s->global->p2p_group_formation = NULL; + wpa_s->p2p_in_provisioning = 0; + } if (!success) { - wpa_msg(wpa_s->parent, MSG_INFO, - P2P_EVENT_GROUP_FORMATION_FAILURE); - wpas_p2p_group_delete(wpa_s); + wpa_msg_global(wpa_s->parent, MSG_INFO, + P2P_EVENT_GROUP_FORMATION_FAILURE); + wpas_p2p_group_delete(wpa_s, + P2P_GROUP_REMOVAL_FORMATION_FAILED); return; } - wpa_msg(wpa_s->parent, MSG_INFO, P2P_EVENT_GROUP_FORMATION_SUCCESS); + wpa_msg_global(wpa_s->parent, MSG_INFO, + P2P_EVENT_GROUP_FORMATION_SUCCESS); ssid = wpa_s->current_ssid; if (ssid && ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION) { @@ -584,22 +868,23 @@ } else if (ssid && ssid->passphrase == NULL && ssid->psk_set) { char psk[65]; wpa_snprintf_hex(psk, sizeof(psk), ssid->psk, 32); - wpa_msg(wpa_s->parent, MSG_INFO, P2P_EVENT_GROUP_STARTED - "%s GO ssid=\"%s\" freq=%d psk=%s go_dev_addr=" MACSTR - "%s", - wpa_s->ifname, ssid_txt, ssid->frequency, psk, - MAC2STR(go_dev_addr), - persistent ? " [PERSISTENT]" : ""); + wpa_msg_global(wpa_s->parent, MSG_INFO, P2P_EVENT_GROUP_STARTED + "%s GO ssid=\"%s\" freq=%d psk=%s go_dev_addr=" + MACSTR "%s", + wpa_s->ifname, ssid_txt, ssid->frequency, psk, + MAC2STR(go_dev_addr), + persistent ? " [PERSISTENT]" : ""); wpas_p2p_cross_connect_setup(wpa_s); wpas_p2p_set_group_idle_timeout(wpa_s); } else { - wpa_msg(wpa_s->parent, MSG_INFO, P2P_EVENT_GROUP_STARTED - "%s GO ssid=\"%s\" freq=%d passphrase=\"%s\" " - "go_dev_addr=" MACSTR "%s", - wpa_s->ifname, ssid_txt, ssid ? ssid->frequency : 0, - ssid && ssid->passphrase ? ssid->passphrase : "", - MAC2STR(go_dev_addr), - persistent ? " [PERSISTENT]" : ""); + wpa_msg_global(wpa_s->parent, MSG_INFO, P2P_EVENT_GROUP_STARTED + "%s GO ssid=\"%s\" freq=%d passphrase=\"%s\" " + "go_dev_addr=" MACSTR "%s", + wpa_s->ifname, ssid_txt, + ssid ? ssid->frequency : 0, + ssid && ssid->passphrase ? ssid->passphrase : "", + MAC2STR(go_dev_addr), + persistent ? " [PERSISTENT]" : ""); wpas_p2p_cross_connect_setup(wpa_s); wpas_p2p_set_group_idle_timeout(wpa_s); } @@ -607,10 +892,42 @@ if (persistent) network_id = wpas_p2p_store_persistent_group(wpa_s->parent, ssid, go_dev_addr); + else { + os_free(wpa_s->global->add_psk); + wpa_s->global->add_psk = NULL; + } if (network_id < 0 && ssid) network_id = ssid->id; - if (!client) + if (!client) { wpas_notify_p2p_group_started(wpa_s, ssid, network_id, 0); + os_get_reltime(&wpa_s->global->p2p_go_wait_client); + } +} + + +struct send_action_work { + unsigned int freq; + u8 dst[ETH_ALEN]; + u8 src[ETH_ALEN]; + u8 bssid[ETH_ALEN]; + size_t len; + unsigned int wait_time; + u8 buf[0]; +}; + + +static void wpas_p2p_send_action_work_timeout(void *eloop_ctx, + void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + + if (!wpa_s->p2p_send_action_work) + return; + + wpa_printf(MSG_DEBUG, "P2P: Send Action frame radio work timed out"); + os_free(wpa_s->p2p_send_action_work->ctx); + radio_work_done(wpa_s->p2p_send_action_work); + wpa_s->p2p_send_action_work = NULL; } @@ -624,10 +941,31 @@ { enum p2p_send_action_result res = P2P_SEND_ACTION_SUCCESS; + if (wpa_s->p2p_send_action_work) { + struct send_action_work *awork; + awork = wpa_s->p2p_send_action_work->ctx; + if (awork->wait_time == 0) { + os_free(awork); + radio_work_done(wpa_s->p2p_send_action_work); + wpa_s->p2p_send_action_work = NULL; + } else { + /* + * In theory, this should not be needed, but number of + * places in the P2P code is still using non-zero wait + * time for the last Action frame in the sequence and + * some of these do not call send_action_done(). + */ + eloop_cancel_timeout(wpas_p2p_send_action_work_timeout, + wpa_s, NULL); + eloop_register_timeout( + 0, awork->wait_time * 1000, + wpas_p2p_send_action_work_timeout, + wpa_s, NULL); + } + } + if (wpa_s->global->p2p == NULL || wpa_s->global->p2p_disabled) return; - if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT) - return; switch (result) { case OFFCHANNEL_SEND_ACTION_SUCCESS: @@ -646,13 +984,70 @@ if (result != OFFCHANNEL_SEND_ACTION_SUCCESS && wpa_s->pending_pd_before_join && (os_memcmp(dst, wpa_s->pending_join_dev_addr, ETH_ALEN) == 0 || - os_memcmp(dst, wpa_s->pending_join_iface_addr, ETH_ALEN) == 0)) { + os_memcmp(dst, wpa_s->pending_join_iface_addr, ETH_ALEN) == 0) && + wpa_s->p2p_fallback_to_go_neg) { wpa_s->pending_pd_before_join = 0; - wpa_printf(MSG_DEBUG, "P2P: Starting pending " - "join-existing-group operation (no ACK for PD " - "Req)"); - wpas_p2p_join_start(wpa_s); + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: No ACK for PD Req " + "during p2p_connect-auto"); + wpas_p2p_fallback_to_go_neg(wpa_s, 0); + return; + } +} + + +static void wpas_send_action_cb(struct wpa_radio_work *work, int deinit) +{ + struct wpa_supplicant *wpa_s = work->wpa_s; + struct send_action_work *awork = work->ctx; + + if (deinit) { + os_free(awork); + return; + } + + if (offchannel_send_action(wpa_s, awork->freq, awork->dst, awork->src, + awork->bssid, awork->buf, awork->len, + awork->wait_time, + wpas_p2p_send_action_tx_status, 1) < 0) { + os_free(awork); + radio_work_done(work); + return; + } + wpa_s->p2p_send_action_work = work; +} + + +static int wpas_send_action_work(struct wpa_supplicant *wpa_s, + unsigned int freq, const u8 *dst, + const u8 *src, const u8 *bssid, const u8 *buf, + size_t len, unsigned int wait_time) +{ + struct send_action_work *awork; + + if (wpa_s->p2p_send_action_work) { + wpa_printf(MSG_DEBUG, "P2P: Cannot schedule new p2p-send-action work since one is already pending"); + return -1; + } + + awork = os_zalloc(sizeof(*awork) + len); + if (awork == NULL) + return -1; + + awork->freq = freq; + os_memcpy(awork->dst, dst, ETH_ALEN); + os_memcpy(awork->src, src, ETH_ALEN); + os_memcpy(awork->bssid, bssid, ETH_ALEN); + awork->len = len; + awork->wait_time = wait_time; + os_memcpy(awork->buf, buf, len); + + if (radio_add_work(wpa_s, freq, "p2p-send-action", 0, + wpas_send_action_cb, awork) < 0) { + os_free(awork); + return -1; } + + return 0; } @@ -661,6 +1056,20 @@ size_t len, unsigned int wait_time) { struct wpa_supplicant *wpa_s = ctx; + int listen_freq = -1, send_freq = -1; + + if (wpa_s->p2p_listen_work) + listen_freq = wpa_s->p2p_listen_work->freq; + if (wpa_s->p2p_send_action_work) + send_freq = wpa_s->p2p_send_action_work->freq; + if (listen_freq != (int) freq && send_freq != (int) freq) { + wpa_printf(MSG_DEBUG, "P2P: Schedule new radio work for Action frame TX (listen_freq=%d send_freq=%d)", + listen_freq, send_freq); + return wpas_send_action_work(wpa_s, freq, dst, src, bssid, buf, + len, wait_time); + } + + wpa_printf(MSG_DEBUG, "P2P: Use ongoing radio work for Action frame TX"); return offchannel_send_action(wpa_s, freq, dst, src, bssid, buf, len, wait_time, wpas_p2p_send_action_tx_status, 1); @@ -670,6 +1079,15 @@ static void wpas_send_action_done(void *ctx) { struct wpa_supplicant *wpa_s = ctx; + + if (wpa_s->p2p_send_action_work) { + eloop_cancel_timeout(wpas_p2p_send_action_work_timeout, + wpa_s, NULL); + os_free(wpa_s->p2p_send_action_work->ctx); + radio_work_done(wpa_s->p2p_send_action_work); + wpa_s->p2p_send_action_work = NULL; + } + offchannel_send_action_done(wpa_s); } @@ -690,15 +1108,29 @@ static void wpas_start_wps_enrollee(struct wpa_supplicant *wpa_s, struct p2p_go_neg_results *res) { - wpa_printf(MSG_DEBUG, "P2P: Start WPS Enrollee for peer " MACSTR, - MAC2STR(res->peer_interface_addr)); + wpa_printf(MSG_DEBUG, "P2P: Start WPS Enrollee for peer " MACSTR + " dev_addr " MACSTR " wps_method %d", + MAC2STR(res->peer_interface_addr), + MAC2STR(res->peer_device_addr), res->wps_method); wpa_hexdump_ascii(MSG_DEBUG, "P2P: Start WPS Enrollee for SSID", res->ssid, res->ssid_len); wpa_supplicant_ap_deinit(wpa_s); wpas_copy_go_neg_results(wpa_s, res); - if (res->wps_method == WPS_PBC) + if (res->wps_method == WPS_PBC) { wpas_wps_start_pbc(wpa_s, res->peer_interface_addr, 1); - else { +#ifdef CONFIG_WPS_NFC + } else if (res->wps_method == WPS_NFC) { + wpas_wps_start_nfc(wpa_s, res->peer_device_addr, + res->peer_interface_addr, + wpa_s->parent->p2p_oob_dev_pw, + wpa_s->parent->p2p_oob_dev_pw_id, 1, + wpa_s->parent->p2p_oob_dev_pw_id == + DEV_PW_NFC_CONNECTION_HANDOVER ? + wpa_s->parent->p2p_peer_oob_pubkey_hash : + NULL, + NULL, 0, 0); +#endif /* CONFIG_WPS_NFC */ + } else { u16 dev_pw_id = DEV_PW_DEFAULT; if (wpa_s->p2p_wps_method == WPS_PIN_KEYPAD) dev_pw_id = DEV_PW_REGISTRAR_SPECIFIED; @@ -708,6 +1140,44 @@ } +static void wpas_p2p_add_psk_list(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid) +{ + struct wpa_ssid *persistent; + struct psk_list_entry *psk; + struct hostapd_data *hapd; + + if (!wpa_s->ap_iface) + return; + + persistent = wpas_p2p_get_persistent(wpa_s->parent, NULL, ssid->ssid, + ssid->ssid_len); + if (persistent == NULL) + return; + + hapd = wpa_s->ap_iface->bss[0]; + + dl_list_for_each(psk, &persistent->psk_list, struct psk_list_entry, + list) { + struct hostapd_wpa_psk *hpsk; + + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Add persistent group PSK entry for " + MACSTR " psk=%d", + MAC2STR(psk->addr), psk->p2p); + hpsk = os_zalloc(sizeof(*hpsk)); + if (hpsk == NULL) + break; + os_memcpy(hpsk->psk, psk->psk, PMK_LEN); + if (psk->p2p) + os_memcpy(hpsk->p2p_dev_addr, psk->addr, ETH_ALEN); + else + os_memcpy(hpsk->addr, psk->addr, ETH_ALEN); + hpsk->next = hapd->conf->ssid.wpa_psk; + hapd->conf->ssid.wpa_psk = hpsk; + } +} + + static void p2p_go_configured(void *ctx, void *data) { struct wpa_supplicant *wpa_s = ctx; @@ -720,25 +1190,60 @@ wpa_printf(MSG_DEBUG, "P2P: Group setup without provisioning"); if (wpa_s->global->p2p_group_formation == wpa_s) wpa_s->global->p2p_group_formation = NULL; - wpa_msg(wpa_s->parent, MSG_INFO, P2P_EVENT_GROUP_STARTED - "%s GO ssid=\"%s\" freq=%d passphrase=\"%s\" " - "go_dev_addr=" MACSTR "%s", - wpa_s->ifname, - wpa_ssid_txt(ssid->ssid, ssid->ssid_len), - ssid->frequency, - params->passphrase ? params->passphrase : "", - MAC2STR(wpa_s->global->p2p_dev_addr), - params->persistent_group ? " [PERSISTENT]" : ""); + if (os_strlen(params->passphrase) > 0) { + wpa_msg_global(wpa_s->parent, MSG_INFO, + P2P_EVENT_GROUP_STARTED + "%s GO ssid=\"%s\" freq=%d " + "passphrase=\"%s\" go_dev_addr=" MACSTR + "%s", wpa_s->ifname, + wpa_ssid_txt(ssid->ssid, ssid->ssid_len), + ssid->frequency, params->passphrase, + MAC2STR(wpa_s->global->p2p_dev_addr), + params->persistent_group ? + " [PERSISTENT]" : ""); + } else { + char psk[65]; + wpa_snprintf_hex(psk, sizeof(psk), params->psk, + sizeof(params->psk)); + wpa_msg_global(wpa_s->parent, MSG_INFO, + P2P_EVENT_GROUP_STARTED + "%s GO ssid=\"%s\" freq=%d psk=%s " + "go_dev_addr=" MACSTR "%s", + wpa_s->ifname, + wpa_ssid_txt(ssid->ssid, ssid->ssid_len), + ssid->frequency, psk, + MAC2STR(wpa_s->global->p2p_dev_addr), + params->persistent_group ? + " [PERSISTENT]" : ""); + } - if (params->persistent_group) + os_get_reltime(&wpa_s->global->p2p_go_wait_client); + if (params->persistent_group) { network_id = wpas_p2p_store_persistent_group( wpa_s->parent, ssid, wpa_s->global->p2p_dev_addr); + wpas_p2p_add_psk_list(wpa_s, ssid); + } if (network_id < 0) network_id = ssid->id; wpas_notify_p2p_group_started(wpa_s, ssid, network_id, 0); wpas_p2p_cross_connect_setup(wpa_s); wpas_p2p_set_group_idle_timeout(wpa_s); + + if (wpa_s->p2p_first_connection_timeout) { + wpa_dbg(wpa_s, MSG_DEBUG, + "P2P: Start group formation timeout of %d seconds until first data connection on GO", + wpa_s->p2p_first_connection_timeout); + wpa_s->p2p_go_group_formation_completed = 0; + wpa_s->global->p2p_group_formation = wpa_s; + eloop_cancel_timeout(wpas_p2p_group_formation_timeout, + wpa_s->parent, NULL); + eloop_register_timeout( + wpa_s->p2p_first_connection_timeout, 0, + wpas_p2p_group_formation_timeout, + wpa_s->parent, NULL); + } + return; } @@ -749,12 +1254,26 @@ "filtering"); return; } - if (params->wps_method == WPS_PBC) + if (params->wps_method == WPS_PBC) { wpa_supplicant_ap_wps_pbc(wpa_s, params->peer_interface_addr, params->peer_device_addr); - else if (wpa_s->p2p_pin[0]) +#ifdef CONFIG_WPS_NFC + } else if (params->wps_method == WPS_NFC) { + if (wpa_s->parent->p2p_oob_dev_pw_id != + DEV_PW_NFC_CONNECTION_HANDOVER && + !wpa_s->parent->p2p_oob_dev_pw) { + wpa_printf(MSG_DEBUG, "P2P: No NFC Dev Pw known"); + return; + } + wpas_ap_wps_add_nfc_pw( + wpa_s, wpa_s->parent->p2p_oob_dev_pw_id, + wpa_s->parent->p2p_oob_dev_pw, + wpa_s->parent->p2p_peer_oob_pk_hash_known ? + wpa_s->parent->p2p_peer_oob_pubkey_hash : NULL); +#endif /* CONFIG_WPS_NFC */ + } else if (wpa_s->p2p_pin[0]) wpa_supplicant_ap_wps_pin(wpa_s, params->peer_interface_addr, - wpa_s->p2p_pin, NULL, 0); + wpa_s->p2p_pin, NULL, 0, 0); os_free(wpa_s->go_params); wpa_s->go_params = NULL; } @@ -766,12 +1285,18 @@ { struct wpa_ssid *ssid; - if (wpas_copy_go_neg_results(wpa_s, params) < 0) + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Starting GO"); + if (wpas_copy_go_neg_results(wpa_s, params) < 0) { + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Could not copy GO Negotiation " + "results"); return; + } ssid = wpa_config_add_network(wpa_s->conf); - if (ssid == NULL) + if (ssid == NULL) { + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Could not add network for GO"); return; + } wpa_s->show_group_started = 0; @@ -782,6 +1307,8 @@ ssid->mode = group_formation ? WPAS_MODE_P2P_GROUP_FORMATION : WPAS_MODE_P2P_GO; ssid->frequency = params->freq; + ssid->ht40 = params->ht40; + ssid->vht = params->vht; ssid->ssid = os_zalloc(params->ssid_len + 1); if (ssid->ssid) { os_memcpy(ssid->ssid, params->ssid, params->ssid_len); @@ -791,7 +1318,22 @@ ssid->key_mgmt = WPA_KEY_MGMT_PSK; ssid->proto = WPA_PROTO_RSN; ssid->pairwise_cipher = WPA_CIPHER_CCMP; - ssid->passphrase = os_strdup(params->passphrase); + if (os_strlen(params->passphrase) > 0) { + ssid->passphrase = os_strdup(params->passphrase); + if (ssid->passphrase == NULL) { + wpa_msg_global(wpa_s, MSG_ERROR, + "P2P: Failed to copy passphrase for GO"); + wpa_config_remove_network(wpa_s->conf, ssid->id); + return; + } + } else + ssid->passphrase = NULL; + ssid->psk_set = params->psk_set; + if (ssid->psk_set) + os_memcpy(ssid->psk, params->psk, sizeof(ssid->psk)); + else if (ssid->passphrase) + wpa_config_update_psk(ssid); + ssid->ap_max_inactivity = wpa_s->parent->conf->p2p_go_max_inactivity; wpa_s->ap_configured_cb = p2p_go_configured; wpa_s->ap_configured_cb_ctx = wpa_s; @@ -799,6 +1341,8 @@ wpa_s->connect_without_scan = ssid; wpa_s->reassociate = 1; wpa_s->disconnected = 0; + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Request scan (that will be skipped) to " + "start GO)"); wpa_supplicant_req_scan(wpa_s, 0, 0); } @@ -829,6 +1373,37 @@ d->p2p_group_idle = s->p2p_group_idle; d->p2p_intra_bss = s->p2p_intra_bss; d->persistent_reconnect = s->persistent_reconnect; + d->max_num_sta = s->max_num_sta; + d->pbc_in_m1 = s->pbc_in_m1; + d->ignore_old_scan_res = s->ignore_old_scan_res; + d->beacon_int = s->beacon_int; + d->dtim_period = s->dtim_period; + d->disassoc_low_ack = s->disassoc_low_ack; + d->disable_scan_offload = s->disable_scan_offload; + + if (s->wps_nfc_dh_privkey && s->wps_nfc_dh_pubkey) { + d->wps_nfc_dh_privkey = wpabuf_dup(s->wps_nfc_dh_privkey); + d->wps_nfc_dh_pubkey = wpabuf_dup(s->wps_nfc_dh_pubkey); + } +} + + +static void wpas_p2p_get_group_ifname(struct wpa_supplicant *wpa_s, + char *ifname, size_t len) +{ + char *ifname_ptr = wpa_s->ifname; + + if (os_strncmp(wpa_s->ifname, P2P_MGMT_DEVICE_PREFIX, + os_strlen(P2P_MGMT_DEVICE_PREFIX)) == 0) { + ifname_ptr = os_strrchr(wpa_s->ifname, '-') + 1; + } + + os_snprintf(ifname, len, "p2p-%s-%d", ifname_ptr, wpa_s->p2p_group_idx); + if (os_strlen(ifname) >= IFNAMSIZ && + os_strlen(wpa_s->ifname) < IFNAMSIZ) { + /* Try to avoid going over the IFNAMSIZ length limit */ + os_snprintf(ifname, len, "p2p-%d", wpa_s->p2p_group_idx); + } } @@ -849,14 +1424,7 @@ return 0; } - os_snprintf(ifname, sizeof(ifname), "p2p-%s-%d", wpa_s->ifname, - wpa_s->p2p_group_idx); - if (os_strlen(ifname) >= IFNAMSIZ && - os_strlen(wpa_s->ifname) < IFNAMSIZ) { - /* Try to avoid going over the IFNAMSIZ length limit */ - os_snprintf(ifname, sizeof(ifname), "p2p-%d", - wpa_s->p2p_group_idx); - } + wpas_p2p_get_group_ifname(wpa_s, ifname, sizeof(ifname)); force_ifname[0] = '\0'; wpa_printf(MSG_DEBUG, "P2P: Create a new interface %s for the group", @@ -926,7 +1494,13 @@ os_memset(&iface, 0, sizeof(iface)); iface.ifname = wpa_s->pending_interface_name; iface.driver = wpa_s->driver->name; - iface.ctrl_interface = wpa_s->conf->ctrl_interface; + if (wpa_s->conf->ctrl_interface == NULL && + wpa_s->parent != wpa_s && + wpa_s->p2p_mgmt && + (wpa_s->drv_flags & WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE)) + iface.ctrl_interface = wpa_s->parent->conf->ctrl_interface; + else + iface.ctrl_interface = wpa_s->conf->ctrl_interface; iface.driver_param = wpa_s->conf->driver_param; group_wpa_s = wpa_supplicant_add_iface(wpa_s->global, &iface); if (group_wpa_s == NULL) { @@ -951,35 +1525,90 @@ { struct wpa_supplicant *wpa_s = eloop_ctx; wpa_printf(MSG_DEBUG, "P2P: Group Formation timed out"); + wpas_p2p_group_formation_failed(wpa_s); +} + + +void wpas_p2p_group_formation_failed(struct wpa_supplicant *wpa_s) +{ + eloop_cancel_timeout(wpas_p2p_group_formation_timeout, + wpa_s->parent, NULL); if (wpa_s->global->p2p) p2p_group_formation_failed(wpa_s->global->p2p); - else if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT) - wpa_drv_p2p_group_formation_failed(wpa_s); wpas_group_formation_completed(wpa_s, 0); } -void wpas_go_neg_completed(void *ctx, struct p2p_go_neg_results *res) +static void wpas_p2p_grpform_fail_after_wps(struct wpa_supplicant *wpa_s) { - struct wpa_supplicant *wpa_s = ctx; + wpa_printf(MSG_DEBUG, "P2P: Reject group formation due to WPS provisioning failure"); + eloop_cancel_timeout(wpas_p2p_group_formation_timeout, + wpa_s->parent, NULL); + eloop_register_timeout(0, 0, wpas_p2p_group_formation_timeout, + wpa_s->parent, NULL); + wpa_s->global->p2p_fail_on_wps_complete = 0; +} - if (wpa_s->off_channel_freq || wpa_s->roc_waiting_drv_freq) { - wpa_drv_cancel_remain_on_channel(wpa_s); - wpa_s->off_channel_freq = 0; + +void wpas_p2p_ap_setup_failed(struct wpa_supplicant *wpa_s) +{ + if (wpa_s->global->p2p_group_formation != wpa_s) + return; + /* Speed up group formation timeout since this cannot succeed */ + eloop_cancel_timeout(wpas_p2p_group_formation_timeout, + wpa_s->parent, NULL); + eloop_register_timeout(0, 0, wpas_p2p_group_formation_timeout, + wpa_s->parent, NULL); +} + + +static void wpas_go_neg_completed(void *ctx, struct p2p_go_neg_results *res) +{ + struct wpa_supplicant *wpa_s = ctx; + + if (wpa_s->off_channel_freq || wpa_s->roc_waiting_drv_freq) { + wpa_drv_cancel_remain_on_channel(wpa_s); + wpa_s->off_channel_freq = 0; wpa_s->roc_waiting_drv_freq = 0; } if (res->status) { - wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_GO_NEG_FAILURE "status=%d", - res->status); + wpa_msg_global(wpa_s, MSG_INFO, + P2P_EVENT_GO_NEG_FAILURE "status=%d", + res->status); wpas_notify_p2p_go_neg_completed(wpa_s, res); wpas_p2p_remove_pending_group_interface(wpa_s); return; } - wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_GO_NEG_SUCCESS); + if (wpa_s->p2p_go_ht40) + res->ht40 = 1; + if (wpa_s->p2p_go_vht) + res->vht = 1; + + wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_GO_NEG_SUCCESS "role=%s " + "freq=%d ht40=%d peer_dev=" MACSTR " peer_iface=" MACSTR + " wps_method=%s", + res->role_go ? "GO" : "client", res->freq, res->ht40, + MAC2STR(res->peer_device_addr), + MAC2STR(res->peer_interface_addr), + p2p_wps_method_text(res->wps_method)); wpas_notify_p2p_go_neg_completed(wpa_s, res); + if (res->role_go && wpa_s->p2p_persistent_id >= 0) { + struct wpa_ssid *ssid; + ssid = wpa_config_get_network(wpa_s->conf, + wpa_s->p2p_persistent_id); + if (ssid && ssid->disabled == 2 && + ssid->mode == WPAS_MODE_P2P_GO && ssid->passphrase) { + size_t len = os_strlen(ssid->passphrase); + wpa_printf(MSG_DEBUG, "P2P: Override passphrase based " + "on requested persistent group"); + os_memcpy(res->passphrase, ssid->passphrase, len); + res->passphrase[len] = '\0'; + } + } + if (wpa_s->create_p2p_iface) { struct wpa_supplicant *group_wpa_s = wpas_p2p_init_group_interface(wpa_s, res->role_go); @@ -1020,32 +1649,44 @@ } -void wpas_go_neg_req_rx(void *ctx, const u8 *src, u16 dev_passwd_id) +static void wpas_go_neg_req_rx(void *ctx, const u8 *src, u16 dev_passwd_id) { struct wpa_supplicant *wpa_s = ctx; - wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_GO_NEG_REQUEST MACSTR - " dev_passwd_id=%u", MAC2STR(src), dev_passwd_id); + wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_GO_NEG_REQUEST MACSTR + " dev_passwd_id=%u", MAC2STR(src), dev_passwd_id); wpas_notify_p2p_go_neg_req(wpa_s, src, dev_passwd_id); } -void wpas_dev_found(void *ctx, const u8 *addr, - const struct p2p_peer_info *info, - int new_device) +static void wpas_dev_found(void *ctx, const u8 *addr, + const struct p2p_peer_info *info, + int new_device) { +#ifndef CONFIG_NO_STDOUT_DEBUG struct wpa_supplicant *wpa_s = ctx; char devtype[WPS_DEV_TYPE_BUFSIZE]; + char *wfd_dev_info_hex = NULL; + +#ifdef CONFIG_WIFI_DISPLAY + wfd_dev_info_hex = wifi_display_subelem_hex(info->wfd_subelems, + WFD_SUBELEM_DEVICE_INFO); +#endif /* CONFIG_WIFI_DISPLAY */ + + wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_DEVICE_FOUND MACSTR + " p2p_dev_addr=" MACSTR + " pri_dev_type=%s name='%s' config_methods=0x%x " + "dev_capab=0x%x group_capab=0x%x%s%s", + MAC2STR(addr), MAC2STR(info->p2p_device_addr), + wps_dev_type_bin2str(info->pri_dev_type, devtype, + sizeof(devtype)), + info->device_name, info->config_methods, + info->dev_capab, info->group_capab, + wfd_dev_info_hex ? " wfd_dev_info=0x" : "", + wfd_dev_info_hex ? wfd_dev_info_hex : ""); - wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_DEVICE_FOUND MACSTR - " p2p_dev_addr=" MACSTR - " pri_dev_type=%s name='%s' config_methods=0x%x " - "dev_capab=0x%x group_capab=0x%x", - MAC2STR(addr), MAC2STR(info->p2p_device_addr), - wps_dev_type_bin2str(info->pri_dev_type, devtype, - sizeof(devtype)), - info->device_name, info->config_methods, - info->dev_capab, info->group_capab); + os_free(wfd_dev_info_hex); +#endif /* CONFIG_NO_STDOUT_DEBUG */ wpas_notify_p2p_device_found(ctx, info->p2p_device_addr, new_device); } @@ -1055,39 +1696,118 @@ { struct wpa_supplicant *wpa_s = ctx; - wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_DEVICE_LOST - "p2p_dev_addr=" MACSTR, MAC2STR(dev_addr)); + wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_DEVICE_LOST + "p2p_dev_addr=" MACSTR, MAC2STR(dev_addr)); wpas_notify_p2p_device_lost(wpa_s, dev_addr); } -static int wpas_start_listen(void *ctx, unsigned int freq, - unsigned int duration, - const struct wpabuf *probe_resp_ie) +static void wpas_find_stopped(void *ctx) { struct wpa_supplicant *wpa_s = ctx; + wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_FIND_STOPPED); +} + + +struct wpas_p2p_listen_work { + unsigned int freq; + unsigned int duration; + struct wpabuf *probe_resp_ie; +}; + + +static void wpas_p2p_listen_work_free(struct wpas_p2p_listen_work *lwork) +{ + if (lwork == NULL) + return; + wpabuf_free(lwork->probe_resp_ie); + os_free(lwork); +} + + +static void wpas_p2p_listen_work_done(struct wpa_supplicant *wpa_s) +{ + struct wpas_p2p_listen_work *lwork; + + if (!wpa_s->p2p_listen_work) + return; + + lwork = wpa_s->p2p_listen_work->ctx; + wpas_p2p_listen_work_free(lwork); + radio_work_done(wpa_s->p2p_listen_work); + wpa_s->p2p_listen_work = NULL; +} + + +static void wpas_start_listen_cb(struct wpa_radio_work *work, int deinit) +{ + struct wpa_supplicant *wpa_s = work->wpa_s; + struct wpas_p2p_listen_work *lwork = work->ctx; - wpa_drv_set_ap_wps_ie(wpa_s, NULL, probe_resp_ie, NULL); + if (deinit) { + wpas_p2p_listen_work_free(lwork); + return; + } + + wpa_s->p2p_listen_work = work; + + wpa_drv_set_ap_wps_ie(wpa_s, NULL, lwork->probe_resp_ie, NULL); if (wpa_drv_probe_req_report(wpa_s, 1) < 0) { wpa_printf(MSG_DEBUG, "P2P: Failed to request the driver to " "report received Probe Request frames"); - return -1; + wpas_p2p_listen_work_done(wpa_s); + return; } - wpa_s->pending_listen_freq = freq; - wpa_s->pending_listen_duration = duration; + wpa_s->pending_listen_freq = lwork->freq; + wpa_s->pending_listen_duration = lwork->duration; - if (wpa_drv_remain_on_channel(wpa_s, freq, duration) < 0) { + if (wpa_drv_remain_on_channel(wpa_s, lwork->freq, lwork->duration) < 0) + { wpa_printf(MSG_DEBUG, "P2P: Failed to request the driver " "to remain on channel (%u MHz) for Listen " - "state", freq); + "state", lwork->freq); + wpas_p2p_listen_work_done(wpa_s); wpa_s->pending_listen_freq = 0; - return -1; + return; } wpa_s->off_channel_freq = 0; - wpa_s->roc_waiting_drv_freq = freq; + wpa_s->roc_waiting_drv_freq = lwork->freq; +} + + +static int wpas_start_listen(void *ctx, unsigned int freq, + unsigned int duration, + const struct wpabuf *probe_resp_ie) +{ + struct wpa_supplicant *wpa_s = ctx; + struct wpas_p2p_listen_work *lwork; + + if (wpa_s->p2p_listen_work) { + wpa_printf(MSG_DEBUG, "P2P: Reject start_listen since p2p_listen_work already exists"); + return -1; + } + + lwork = os_zalloc(sizeof(*lwork)); + if (lwork == NULL) + return -1; + lwork->freq = freq; + lwork->duration = duration; + if (probe_resp_ie) { + lwork->probe_resp_ie = wpabuf_dup(probe_resp_ie); + if (lwork->probe_resp_ie == NULL) { + wpas_p2p_listen_work_free(lwork); + return -1; + } + } + + if (radio_add_work(wpa_s, freq, "p2p-listen", 0, wpas_start_listen_cb, + lwork) < 0) { + wpas_p2p_listen_work_free(lwork); + return -1; + } return 0; } @@ -1103,13 +1823,143 @@ } wpa_drv_set_ap_wps_ie(wpa_s, NULL, NULL, NULL); wpa_drv_probe_req_report(wpa_s, 0); + wpas_p2p_listen_work_done(wpa_s); } static int wpas_send_probe_resp(void *ctx, const struct wpabuf *buf) { struct wpa_supplicant *wpa_s = ctx; - return wpa_drv_send_mlme(wpa_s, wpabuf_head(buf), wpabuf_len(buf)); + return wpa_drv_send_mlme(wpa_s, wpabuf_head(buf), wpabuf_len(buf), 1); +} + + +/* + * DNS Header section is used only to calculate compression pointers, so the + * contents of this data does not matter, but the length needs to be reserved + * in the virtual packet. + */ +#define DNS_HEADER_LEN 12 + +/* + * 27-octet in-memory packet from P2P specification containing two implied + * queries for _tcp.lcoal. PTR IN and _udp.local. PTR IN + */ +#define P2P_SD_IN_MEMORY_LEN 27 + +static int p2p_sd_dns_uncompress_label(char **upos, char *uend, u8 *start, + u8 **spos, const u8 *end) +{ + while (*spos < end) { + u8 val = ((*spos)[0] & 0xc0) >> 6; + int len; + + if (val == 1 || val == 2) { + /* These are reserved values in RFC 1035 */ + wpa_printf(MSG_DEBUG, "P2P: Invalid domain name " + "sequence starting with 0x%x", val); + return -1; + } + + if (val == 3) { + u16 offset; + u8 *spos_tmp; + + /* Offset */ + if (*spos + 2 > end) { + wpa_printf(MSG_DEBUG, "P2P: No room for full " + "DNS offset field"); + return -1; + } + + offset = (((*spos)[0] & 0x3f) << 8) | (*spos)[1]; + if (offset >= *spos - start) { + wpa_printf(MSG_DEBUG, "P2P: Invalid DNS " + "pointer offset %u", offset); + return -1; + } + + (*spos) += 2; + spos_tmp = start + offset; + return p2p_sd_dns_uncompress_label(upos, uend, start, + &spos_tmp, + *spos - 2); + } + + /* Label */ + len = (*spos)[0] & 0x3f; + if (len == 0) + return 0; + + (*spos)++; + if (*spos + len > end) { + wpa_printf(MSG_DEBUG, "P2P: Invalid domain name " + "sequence - no room for label with length " + "%u", len); + return -1; + } + + if (*upos + len + 2 > uend) + return -2; + + os_memcpy(*upos, *spos, len); + *spos += len; + *upos += len; + (*upos)[0] = '.'; + (*upos)++; + (*upos)[0] = '\0'; + } + + return 0; +} + + +/* Uncompress domain names per RFC 1035 using the P2P SD in-memory packet. + * Returns -1 on parsing error (invalid input sequence), -2 if output buffer is + * not large enough */ +static int p2p_sd_dns_uncompress(char *buf, size_t buf_len, const u8 *msg, + size_t msg_len, size_t offset) +{ + /* 27-octet in-memory packet from P2P specification */ + const char *prefix = "\x04_tcp\x05local\x00\x00\x0C\x00\x01" + "\x04_udp\xC0\x11\x00\x0C\x00\x01"; + u8 *tmp, *end, *spos; + char *upos, *uend; + int ret = 0; + + if (buf_len < 2) + return -1; + if (offset > msg_len) + return -1; + + tmp = os_malloc(DNS_HEADER_LEN + P2P_SD_IN_MEMORY_LEN + msg_len); + if (tmp == NULL) + return -1; + spos = tmp + DNS_HEADER_LEN + P2P_SD_IN_MEMORY_LEN; + end = spos + msg_len; + spos += offset; + + os_memset(tmp, 0, DNS_HEADER_LEN); + os_memcpy(tmp + DNS_HEADER_LEN, prefix, P2P_SD_IN_MEMORY_LEN); + os_memcpy(tmp + DNS_HEADER_LEN + P2P_SD_IN_MEMORY_LEN, msg, msg_len); + + upos = buf; + uend = buf + buf_len; + + ret = p2p_sd_dns_uncompress_label(&upos, uend, tmp, &spos, end); + if (ret) { + os_free(tmp); + return ret; + } + + if (upos == buf) { + upos[0] = '.'; + upos[1] = '\0'; + } else if (upos[-1] == '.') + upos[-1] = '\0'; + + os_free(tmp); + return 0; } @@ -1203,13 +2053,40 @@ } +static int match_bonjour_query(struct p2p_srv_bonjour *bsrv, const u8 *query, + size_t query_len) +{ + char str_rx[256], str_srv[256]; + + if (query_len < 3 || wpabuf_len(bsrv->query) < 3) + return 0; /* Too short to include DNS Type and Version */ + if (os_memcmp(query + query_len - 3, + wpabuf_head_u8(bsrv->query) + wpabuf_len(bsrv->query) - 3, + 3) != 0) + return 0; /* Mismatch in DNS Type or Version */ + if (query_len == wpabuf_len(bsrv->query) && + os_memcmp(query, wpabuf_head(bsrv->query), query_len - 3) == 0) + return 1; /* Binary match */ + + if (p2p_sd_dns_uncompress(str_rx, sizeof(str_rx), query, query_len - 3, + 0)) + return 0; /* Failed to uncompress query */ + if (p2p_sd_dns_uncompress(str_srv, sizeof(str_srv), + wpabuf_head(bsrv->query), + wpabuf_len(bsrv->query) - 3, 0)) + return 0; /* Failed to uncompress service */ + + return os_strcmp(str_rx, str_srv) == 0; +} + + static void wpas_sd_req_bonjour(struct wpa_supplicant *wpa_s, struct wpabuf *resp, u8 srv_trans_id, const u8 *query, size_t query_len) { struct p2p_srv_bonjour *bsrv; - struct wpabuf buf; u8 *len_pos; + int matches = 0; wpa_hexdump_ascii(MSG_DEBUG, "P2P: SD Request for Bonjour", query, query_len); @@ -1225,39 +2102,52 @@ return; } - if (wpabuf_tailroom(resp) < 5) - return; - /* Length (to be filled) */ - len_pos = wpabuf_put(resp, 2); - wpabuf_put_u8(resp, P2P_SERV_BONJOUR); - wpabuf_put_u8(resp, srv_trans_id); + dl_list_for_each(bsrv, &wpa_s->global->p2p_srv_bonjour, + struct p2p_srv_bonjour, list) { + if (!match_bonjour_query(bsrv, query, query_len)) + continue; + + if (wpabuf_tailroom(resp) < + 5 + query_len + wpabuf_len(bsrv->resp)) + return; + + matches++; + + /* Length (to be filled) */ + len_pos = wpabuf_put(resp, 2); + wpabuf_put_u8(resp, P2P_SERV_BONJOUR); + wpabuf_put_u8(resp, srv_trans_id); + + /* Status Code */ + wpabuf_put_u8(resp, P2P_SD_SUCCESS); + wpa_hexdump_ascii(MSG_DEBUG, "P2P: Matching Bonjour service", + wpabuf_head(bsrv->resp), + wpabuf_len(bsrv->resp)); + + /* Response Data */ + wpabuf_put_data(resp, query, query_len); /* Key */ + wpabuf_put_buf(resp, bsrv->resp); /* Value */ + + WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos - 2); + } - wpabuf_set(&buf, query, query_len); - bsrv = wpas_p2p_service_get_bonjour(wpa_s, &buf); - if (bsrv == NULL) { + if (matches == 0) { wpa_printf(MSG_DEBUG, "P2P: Requested Bonjour service not " "available"); + if (wpabuf_tailroom(resp) < 5) + return; + + /* Length (to be filled) */ + len_pos = wpabuf_put(resp, 2); + wpabuf_put_u8(resp, P2P_SERV_BONJOUR); + wpabuf_put_u8(resp, srv_trans_id); /* Status Code */ wpabuf_put_u8(resp, P2P_SD_REQUESTED_INFO_NOT_AVAILABLE); /* Response Data: empty */ WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos - 2); - return; - } - - /* Status Code */ - wpabuf_put_u8(resp, P2P_SD_SUCCESS); - wpa_hexdump_ascii(MSG_DEBUG, "P2P: Matching Bonjour service", - wpabuf_head(bsrv->resp), wpabuf_len(bsrv->resp)); - - if (wpabuf_tailroom(resp) >= - wpabuf_len(bsrv->query) + wpabuf_len(bsrv->resp)) { - /* Response Data */ - wpabuf_put_buf(resp, bsrv->query); /* Key */ - wpabuf_put_buf(resp, bsrv->resp); /* Value */ } - WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos - 2); } @@ -1378,8 +2268,64 @@ } -void wpas_sd_request(void *ctx, int freq, const u8 *sa, u8 dialog_token, - u16 update_indic, const u8 *tlvs, size_t tlvs_len) +#ifdef CONFIG_WIFI_DISPLAY +static void wpas_sd_req_wfd(struct wpa_supplicant *wpa_s, + struct wpabuf *resp, u8 srv_trans_id, + const u8 *query, size_t query_len) +{ + const u8 *pos; + u8 role; + u8 *len_pos; + + wpa_hexdump(MSG_DEBUG, "P2P: SD Request for WFD", query, query_len); + + if (!wpa_s->global->wifi_display) { + wpa_printf(MSG_DEBUG, "P2P: WFD protocol not available"); + wpas_sd_add_proto_not_avail(resp, P2P_SERV_WIFI_DISPLAY, + srv_trans_id); + return; + } + + if (query_len < 1) { + wpa_printf(MSG_DEBUG, "P2P: Missing WFD Requested Device " + "Role"); + return; + } + + if (wpabuf_tailroom(resp) < 5) + return; + + pos = query; + role = *pos++; + wpa_printf(MSG_DEBUG, "P2P: WSD for device role 0x%x", role); + + /* TODO: role specific handling */ + + /* Length (to be filled) */ + len_pos = wpabuf_put(resp, 2); + wpabuf_put_u8(resp, P2P_SERV_WIFI_DISPLAY); + wpabuf_put_u8(resp, srv_trans_id); + wpabuf_put_u8(resp, P2P_SD_SUCCESS); /* Status Code */ + + while (pos < query + query_len) { + if (*pos < MAX_WFD_SUBELEMS && + wpa_s->global->wfd_subelem[*pos] && + wpabuf_tailroom(resp) >= + wpabuf_len(wpa_s->global->wfd_subelem[*pos])) { + wpa_printf(MSG_DEBUG, "P2P: Add WSD response " + "subelement %u", *pos); + wpabuf_put_buf(resp, wpa_s->global->wfd_subelem[*pos]); + } + pos++; + } + + WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos - 2); +} +#endif /* CONFIG_WIFI_DISPLAY */ + + +static void wpas_sd_request(void *ctx, int freq, const u8 *sa, u8 dialog_token, + u16 update_indic, const u8 *tlvs, size_t tlvs_len) { struct wpa_supplicant *wpa_s = ctx; const u8 *pos = tlvs; @@ -1469,6 +2415,12 @@ wpas_sd_req_upnp(wpa_s, resp, srv_trans_id, pos, tlv_end - pos); break; +#ifdef CONFIG_WIFI_DISPLAY + case P2P_SERV_WIFI_DISPLAY: + wpas_sd_req_wfd(wpa_s, resp, srv_trans_id, + pos, tlv_end - pos); + break; +#endif /* CONFIG_WIFI_DISPLAY */ default: wpa_printf(MSG_DEBUG, "P2P: Unavailable service " "protocol %u", srv_proto); @@ -1490,8 +2442,8 @@ } -void wpas_sd_response(void *ctx, const u8 *sa, u16 update_indic, - const u8 *tlvs, size_t tlvs_len) +static void wpas_sd_response(void *ctx, const u8 *sa, u16 update_indic, + const u8 *tlvs, size_t tlvs_len) { struct wpa_supplicant *wpa_s = ctx; const u8 *pos = tlvs; @@ -1555,26 +2507,24 @@ } -void * wpas_p2p_sd_request(struct wpa_supplicant *wpa_s, const u8 *dst, - const struct wpabuf *tlvs) +u64 wpas_p2p_sd_request(struct wpa_supplicant *wpa_s, const u8 *dst, + const struct wpabuf *tlvs) { - if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT) - return (void *) wpa_drv_p2p_sd_request(wpa_s, dst, tlvs); if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) - return NULL; - return p2p_sd_request(wpa_s->global->p2p, dst, tlvs); + return 0; + return (uintptr_t) p2p_sd_request(wpa_s->global->p2p, dst, tlvs); } -void * wpas_p2p_sd_request_upnp(struct wpa_supplicant *wpa_s, const u8 *dst, - u8 version, const char *query) +u64 wpas_p2p_sd_request_upnp(struct wpa_supplicant *wpa_s, const u8 *dst, + u8 version, const char *query) { struct wpabuf *tlvs; - void *ret; + u64 ret; tlvs = wpabuf_alloc(2 + 1 + 1 + 1 + os_strlen(query)); if (tlvs == NULL) - return NULL; + return 0; wpabuf_put_le16(tlvs, 1 + 1 + 1 + os_strlen(query)); wpabuf_put_u8(tlvs, P2P_SERV_UPNP); /* Service Protocol Type */ wpabuf_put_u8(tlvs, 1); /* Service Transaction ID */ @@ -1586,13 +2536,92 @@ } -int wpas_p2p_sd_cancel_request(struct wpa_supplicant *wpa_s, void *req) +#ifdef CONFIG_WIFI_DISPLAY + +static u64 wpas_p2p_sd_request_wfd(struct wpa_supplicant *wpa_s, const u8 *dst, + const struct wpabuf *tlvs) +{ + if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) + return 0; + return (uintptr_t) p2p_sd_request_wfd(wpa_s->global->p2p, dst, tlvs); +} + + +#define MAX_WFD_SD_SUBELEMS 20 + +static void wfd_add_sd_req_role(struct wpabuf *tlvs, u8 id, u8 role, + const char *subelems) +{ + u8 *len; + const char *pos; + int val; + int count = 0; + + len = wpabuf_put(tlvs, 2); + wpabuf_put_u8(tlvs, P2P_SERV_WIFI_DISPLAY); /* Service Protocol Type */ + wpabuf_put_u8(tlvs, id); /* Service Transaction ID */ + + wpabuf_put_u8(tlvs, role); + + pos = subelems; + while (*pos) { + val = atoi(pos); + if (val >= 0 && val < 256) { + wpabuf_put_u8(tlvs, val); + count++; + if (count == MAX_WFD_SD_SUBELEMS) + break; + } + pos = os_strchr(pos + 1, ','); + if (pos == NULL) + break; + pos++; + } + + WPA_PUT_LE16(len, (u8 *) wpabuf_put(tlvs, 0) - len - 2); +} + + +u64 wpas_p2p_sd_request_wifi_display(struct wpa_supplicant *wpa_s, + const u8 *dst, const char *role) +{ + struct wpabuf *tlvs; + u64 ret; + const char *subelems; + u8 id = 1; + + subelems = os_strchr(role, ' '); + if (subelems == NULL) + return 0; + subelems++; + + tlvs = wpabuf_alloc(4 * (2 + 1 + 1 + 1 + MAX_WFD_SD_SUBELEMS)); + if (tlvs == NULL) + return 0; + + if (os_strstr(role, "[source]")) + wfd_add_sd_req_role(tlvs, id++, 0x00, subelems); + if (os_strstr(role, "[pri-sink]")) + wfd_add_sd_req_role(tlvs, id++, 0x01, subelems); + if (os_strstr(role, "[sec-sink]")) + wfd_add_sd_req_role(tlvs, id++, 0x02, subelems); + if (os_strstr(role, "[source+sink]")) + wfd_add_sd_req_role(tlvs, id++, 0x03, subelems); + + ret = wpas_p2p_sd_request_wfd(wpa_s, dst, tlvs); + wpabuf_free(tlvs); + return ret; +} + +#endif /* CONFIG_WIFI_DISPLAY */ + + +int wpas_p2p_sd_cancel_request(struct wpa_supplicant *wpa_s, u64 req) { - if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT) - return wpa_drv_p2p_sd_cancel_request(wpa_s, (u64) req); if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) return -1; - return p2p_sd_cancel_request(wpa_s->global->p2p, req); + return p2p_sd_cancel_request(wpa_s->global->p2p, + (void *) (uintptr_t) req); } @@ -1600,11 +2629,6 @@ const u8 *dst, u8 dialog_token, const struct wpabuf *resp_tlvs) { - if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT) { - wpa_drv_p2p_sd_response(wpa_s, freq, dst, dialog_token, - resp_tlvs); - return; - } if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) return; p2p_sd_response(wpa_s->global->p2p, freq, dst, dialog_token, @@ -1614,10 +2638,6 @@ void wpas_p2p_sd_service_update(struct wpa_supplicant *wpa_s) { - if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT) { - wpa_drv_p2p_service_update(wpa_s); - return; - } if (wpa_s->global->p2p) p2p_sd_service_update(wpa_s->global->p2p); } @@ -1662,14 +2682,6 @@ { struct p2p_srv_bonjour *bsrv; - bsrv = wpas_p2p_service_get_bonjour(wpa_s, query); - if (bsrv) { - wpabuf_free(query); - wpabuf_free(bsrv->resp); - bsrv->resp = resp; - return 0; - } - bsrv = os_zalloc(sizeof(*bsrv)); if (bsrv == NULL) return -1; @@ -1737,24 +2749,24 @@ const u8 *peer, const char *params, unsigned int generated_pin) { - wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_PROV_DISC_SHOW_PIN MACSTR " %08d%s", - MAC2STR(peer), generated_pin, params); + wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_PROV_DISC_SHOW_PIN MACSTR + " %08d%s", MAC2STR(peer), generated_pin, params); } static void wpas_prov_disc_local_keypad(struct wpa_supplicant *wpa_s, const u8 *peer, const char *params) { - wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_PROV_DISC_ENTER_PIN MACSTR "%s", - MAC2STR(peer), params); + wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_PROV_DISC_ENTER_PIN MACSTR + "%s", MAC2STR(peer), params); } -void wpas_prov_disc_req(void *ctx, const u8 *peer, u16 config_methods, - const u8 *dev_addr, const u8 *pri_dev_type, - const char *dev_name, u16 supp_config_methods, - u8 dev_capab, u8 group_capab, const u8 *group_id, - size_t group_id_len) +static void wpas_prov_disc_req(void *ctx, const u8 *peer, u16 config_methods, + const u8 *dev_addr, const u8 *pri_dev_type, + const char *dev_name, u16 supp_config_methods, + u8 dev_capab, u8 group_capab, const u8 *group_id, + size_t group_id_len) { struct wpa_supplicant *wpa_s = ctx; char devtype[WPS_DEV_TYPE_BUFSIZE]; @@ -1798,8 +2810,8 @@ } else if (config_methods & WPS_CONFIG_KEYPAD) wpas_prov_disc_local_keypad(wpa_s, peer, params); else if (config_methods & WPS_CONFIG_PUSHBUTTON) - wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_PROV_DISC_PBC_REQ MACSTR - "%s", MAC2STR(peer), params); + wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_PROV_DISC_PBC_REQ + MACSTR "%s", MAC2STR(peer), params); wpas_notify_p2p_provision_discovery(wpa_s, peer, 1 /* request */, P2P_PROV_DISC_SUCCESS, @@ -1807,10 +2819,11 @@ } -void wpas_prov_disc_resp(void *ctx, const u8 *peer, u16 config_methods) +static void wpas_prov_disc_resp(void *ctx, const u8 *peer, u16 config_methods) { struct wpa_supplicant *wpa_s = ctx; unsigned int generated_pin = 0; + char params[20]; if (wpa_s->pending_pd_before_join && (os_memcmp(peer, wpa_s->pending_join_dev_addr, ETH_ALEN) == 0 || @@ -1818,18 +2831,26 @@ wpa_s->pending_pd_before_join = 0; wpa_printf(MSG_DEBUG, "P2P: Starting pending " "join-existing-group operation"); - wpas_p2p_join_start(wpa_s); + wpas_p2p_join_start(wpa_s, 0, NULL, 0); return; } + if (wpa_s->pending_pd_use == AUTO_PD_JOIN || + wpa_s->pending_pd_use == AUTO_PD_GO_NEG) + os_snprintf(params, sizeof(params), " peer_go=%d", + wpa_s->pending_pd_use == AUTO_PD_JOIN); + else + params[0] = '\0'; + if (config_methods & WPS_CONFIG_DISPLAY) - wpas_prov_disc_local_keypad(wpa_s, peer, ""); + wpas_prov_disc_local_keypad(wpa_s, peer, params); else if (config_methods & WPS_CONFIG_KEYPAD) { generated_pin = wps_generate_pin(); - wpas_prov_disc_local_display(wpa_s, peer, "", generated_pin); + wpas_prov_disc_local_display(wpa_s, peer, params, + generated_pin); } else if (config_methods & WPS_CONFIG_PUSHBUTTON) - wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_PROV_DISC_PBC_RESP MACSTR, - MAC2STR(peer)); + wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_PROV_DISC_PBC_RESP + MACSTR "%s", MAC2STR(peer), params); wpas_notify_p2p_provision_discovery(wpa_s, peer, 0 /* response */, P2P_PROV_DISC_SUCCESS, @@ -1837,30 +2858,61 @@ } -void wpas_prov_disc_fail(void *ctx, const u8 *peer, - enum p2p_prov_disc_status status) +static void wpas_prov_disc_fail(void *ctx, const u8 *peer, + enum p2p_prov_disc_status status) { struct wpa_supplicant *wpa_s = ctx; + if (wpa_s->p2p_fallback_to_go_neg) { + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: PD for p2p_connect-auto " + "failed - fall back to GO Negotiation"); + wpas_p2p_fallback_to_go_neg(wpa_s, 0); + return; + } + + if (status == P2P_PROV_DISC_TIMEOUT_JOIN) { + wpa_s->pending_pd_before_join = 0; + wpa_printf(MSG_DEBUG, "P2P: Starting pending " + "join-existing-group operation (no ACK for PD " + "Req attempts)"); + wpas_p2p_join_start(wpa_s, 0, NULL, 0); + return; + } + + wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_PROV_DISC_FAILURE + " p2p_dev_addr=" MACSTR " status=%d", + MAC2STR(peer), status); + wpas_notify_p2p_provision_discovery(wpa_s, peer, 0 /* response */, status, 0, 0); } +static int freq_included(const struct p2p_channels *channels, unsigned int freq) +{ + if (channels == NULL) + return 1; /* Assume no restrictions */ + return p2p_channels_includes_freq(channels, freq); + +} + + static u8 wpas_invitation_process(void *ctx, const u8 *sa, const u8 *bssid, const u8 *go_dev_addr, const u8 *ssid, size_t ssid_len, int *go, u8 *group_bssid, - int *force_freq, int persistent_group) + int *force_freq, int persistent_group, + const struct p2p_channels *channels, + int dev_pw_id) { struct wpa_supplicant *wpa_s = ctx; struct wpa_ssid *s; - u8 cur_bssid[ETH_ALEN]; int res; struct wpa_supplicant *grp; if (!persistent_group) { wpa_printf(MSG_DEBUG, "P2P: Invitation from " MACSTR - " to join an active group", MAC2STR(sa)); + " to join an active group (SSID: %s)", + MAC2STR(sa), wpa_ssid_txt(ssid, ssid_len)); if (!is_zero_ether_addr(wpa_s->p2p_auth_invite) && (os_memcmp(go_dev_addr, wpa_s->p2p_auth_invite, ETH_ALEN) == 0 || @@ -1869,6 +2921,21 @@ "authorized invitation"); goto accept_inv; } + +#ifdef CONFIG_WPS_NFC + if (dev_pw_id >= 0 && wpa_s->parent->p2p_nfc_tag_enabled && + dev_pw_id == wpa_s->parent->p2p_oob_dev_pw_id) { + wpa_printf(MSG_DEBUG, "P2P: Accept invitation based on local enabled NFC Tag"); + wpa_s->parent->p2p_wps_method = WPS_NFC; + wpa_s->parent->pending_join_wps_method = WPS_NFC; + os_memcpy(wpa_s->parent->pending_join_dev_addr, + go_dev_addr, ETH_ALEN); + os_memcpy(wpa_s->parent->pending_join_iface_addr, + bssid, ETH_ALEN); + goto accept_inv; + } +#endif /* CONFIG_WPS_NFC */ + /* * Do not accept the invitation automatically; notify user and * request approval. @@ -1885,7 +2952,11 @@ goto accept_inv; } - if (!wpa_s->conf->persistent_reconnect) + if (!is_zero_ether_addr(wpa_s->p2p_auth_invite) && + os_memcmp(sa, wpa_s->p2p_auth_invite, ETH_ALEN) == 0) { + wpa_printf(MSG_DEBUG, "P2P: Accept previously initiated " + "invitation to re-invoke a persistent group"); + } else if (!wpa_s->conf->persistent_reconnect) return P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE; for (s = wpa_s->conf->ssid; s; s = s->next) { @@ -1925,19 +2996,37 @@ } accept_inv: - if (wpa_s->current_ssid && wpa_drv_get_bssid(wpa_s, cur_bssid) == 0 && - wpa_s->assoc_freq) { - wpa_printf(MSG_DEBUG, "P2P: Trying to force channel to match " - "the channel we are already using"); - *force_freq = wpa_s->assoc_freq; + wpas_p2p_set_own_freq_preference(wpa_s, 0); + + /* Get one of the frequencies currently in use */ + if (wpas_p2p_valid_oper_freqs(wpa_s, &res, 1) > 0) { + wpa_printf(MSG_DEBUG, "P2P: Trying to prefer a channel already used by one of the interfaces"); + wpas_p2p_set_own_freq_preference(wpa_s, res); + + if (wpa_s->num_multichan_concurrent < 2 || + wpas_p2p_num_unused_channels(wpa_s) < 1) { + wpa_printf(MSG_DEBUG, "P2P: No extra channels available - trying to force channel to match a channel already used by one of the interfaces"); + *force_freq = res; + } } - res = wpa_drv_shared_freq(wpa_s); - if (res > 0) { - wpa_printf(MSG_DEBUG, "P2P: Trying to force channel to match " - "with the channel we are already using on a " - "shared interface"); - *force_freq = res; + if (*force_freq > 0 && wpa_s->num_multichan_concurrent > 1 && + wpas_p2p_num_unused_channels(wpa_s) > 0) { + if (*go == 0) { + /* We are the client */ + wpa_printf(MSG_DEBUG, "P2P: Peer was found to be " + "running a GO but we are capable of MCC, " + "figure out the best channel to use"); + *force_freq = 0; + } else if (!freq_included(channels, *force_freq)) { + /* We are the GO, and *force_freq is not in the + * intersection */ + wpa_printf(MSG_DEBUG, "P2P: Forced GO freq %d MHz not " + "in intersection but we are capable of MCC, " + "figure out the best channel to use", + *force_freq); + *force_freq = 0; + } } return P2P_SC_SUCCESS; @@ -1961,14 +3050,18 @@ if (status == P2P_SC_SUCCESS) { wpa_printf(MSG_DEBUG, "P2P: Invitation from peer " MACSTR - " was accepted; op_freq=%d MHz", - MAC2STR(sa), op_freq); + " was accepted; op_freq=%d MHz, SSID=%s", + MAC2STR(sa), op_freq, wpa_ssid_txt(ssid, ssid_len)); if (s) { + int go = s->mode == WPAS_MODE_P2P_GO; wpas_p2p_group_add_persistent( - wpa_s, s, s->mode == WPAS_MODE_P2P_GO, 0); + wpa_s, s, go, go ? op_freq : 0, 0, 0, NULL, + go ? P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE : 0); } else if (bssid) { + wpa_s->user_initiated_pd = 0; wpas_p2p_join(wpa_s, bssid, go_dev_addr, - wpa_s->p2p_wps_method); + wpa_s->p2p_wps_method, 0, op_freq, + ssid, ssid_len); } return; } @@ -1981,44 +3074,132 @@ if (!s) { if (bssid) { - wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_INVITATION_RECEIVED - "sa=" MACSTR " go_dev_addr=" MACSTR - " bssid=" MACSTR " unknown-network", - MAC2STR(sa), MAC2STR(go_dev_addr), - MAC2STR(bssid)); + wpa_msg_global(wpa_s, MSG_INFO, + P2P_EVENT_INVITATION_RECEIVED + "sa=" MACSTR " go_dev_addr=" MACSTR + " bssid=" MACSTR " unknown-network", + MAC2STR(sa), MAC2STR(go_dev_addr), + MAC2STR(bssid)); } else { - wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_INVITATION_RECEIVED - "sa=" MACSTR " go_dev_addr=" MACSTR - " unknown-network", - MAC2STR(sa), MAC2STR(go_dev_addr)); + wpa_msg_global(wpa_s, MSG_INFO, + P2P_EVENT_INVITATION_RECEIVED + "sa=" MACSTR " go_dev_addr=" MACSTR + " unknown-network", + MAC2STR(sa), MAC2STR(go_dev_addr)); } return; } - wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_INVITATION_RECEIVED "sa=" MACSTR - " persistent=%d", MAC2STR(sa), s->id); + if (s->mode == WPAS_MODE_P2P_GO && op_freq) { + wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_INVITATION_RECEIVED + "sa=" MACSTR " persistent=%d freq=%d", + MAC2STR(sa), s->id, op_freq); + } else { + wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_INVITATION_RECEIVED + "sa=" MACSTR " persistent=%d", + MAC2STR(sa), s->id); + } +} + + +static void wpas_remove_persistent_peer(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid, + const u8 *peer, int inv) +{ + size_t i; + + if (ssid == NULL) + return; + + for (i = 0; ssid->p2p_client_list && i < ssid->num_p2p_clients; i++) { + if (os_memcmp(ssid->p2p_client_list + i * ETH_ALEN, peer, + ETH_ALEN) == 0) + break; + } + if (i >= ssid->num_p2p_clients) { + if (ssid->mode != WPAS_MODE_P2P_GO && + os_memcmp(ssid->bssid, peer, ETH_ALEN) == 0) { + wpa_printf(MSG_DEBUG, "P2P: Remove persistent group %d " + "due to invitation result", ssid->id); + wpas_notify_network_removed(wpa_s, ssid); + wpa_config_remove_network(wpa_s->conf, ssid->id); + return; + } + return; /* Peer not found in client list */ + } + + wpa_printf(MSG_DEBUG, "P2P: Remove peer " MACSTR " from persistent " + "group %d client list%s", + MAC2STR(peer), ssid->id, + inv ? " due to invitation result" : ""); + os_memmove(ssid->p2p_client_list + i * ETH_ALEN, + ssid->p2p_client_list + (i + 1) * ETH_ALEN, + (ssid->num_p2p_clients - i - 1) * ETH_ALEN); + ssid->num_p2p_clients--; +#ifndef CONFIG_NO_CONFIG_WRITE + if (wpa_s->parent->conf->update_config && + wpa_config_write(wpa_s->parent->confname, wpa_s->parent->conf)) + wpa_printf(MSG_DEBUG, "P2P: Failed to update configuration"); +#endif /* CONFIG_NO_CONFIG_WRITE */ +} + + +static void wpas_remove_persistent_client(struct wpa_supplicant *wpa_s, + const u8 *peer) +{ + struct wpa_ssid *ssid; + + wpa_s = wpa_s->global->p2p_invite_group; + if (wpa_s == NULL) + return; /* No known invitation group */ + ssid = wpa_s->current_ssid; + if (ssid == NULL || ssid->mode != WPAS_MODE_P2P_GO || + !ssid->p2p_persistent_group) + return; /* Not operating as a GO in persistent group */ + ssid = wpas_p2p_get_persistent(wpa_s->parent, peer, + ssid->ssid, ssid->ssid_len); + wpas_remove_persistent_peer(wpa_s, ssid, peer, 1); } -static void wpas_invitation_result(void *ctx, int status, const u8 *bssid) +static void wpas_invitation_result(void *ctx, int status, const u8 *bssid, + const struct p2p_channels *channels, + const u8 *peer, int neg_freq) { struct wpa_supplicant *wpa_s = ctx; struct wpa_ssid *ssid; + int freq; if (bssid) { - wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_INVITATION_RESULT - "status=%d " MACSTR, - status, MAC2STR(bssid)); + wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_INVITATION_RESULT + "status=%d " MACSTR, + status, MAC2STR(bssid)); } else { - wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_INVITATION_RESULT - "status=%d ", status); + wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_INVITATION_RESULT + "status=%d ", status); } wpas_notify_p2p_invitation_result(wpa_s, status, bssid); - if (wpa_s->pending_invite_ssid_id == -1) + wpa_printf(MSG_DEBUG, "P2P: Invitation result - status=%d peer=" MACSTR, + status, MAC2STR(peer)); + if (wpa_s->pending_invite_ssid_id == -1) { + if (status == P2P_SC_FAIL_UNKNOWN_GROUP) + wpas_remove_persistent_client(wpa_s, peer); return; /* Invitation to active group */ + } + + if (status == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE) { + wpa_printf(MSG_DEBUG, "P2P: Waiting for peer to start another " + "invitation exchange to indicate readiness for " + "re-invocation"); + } if (status != P2P_SC_SUCCESS) { + if (status == P2P_SC_FAIL_UNKNOWN_GROUP) { + ssid = wpa_config_get_network( + wpa_s->conf, wpa_s->pending_invite_ssid_id); + wpas_remove_persistent_peer(wpa_s, ssid, peer, 1); + } wpas_p2p_remove_pending_group_interface(wpa_s); return; } @@ -2031,49 +3212,107 @@ return; } + /* + * The peer could have missed our ctrl::ack frame for Invitation + * Response and continue retransmitting the frame. To reduce the + * likelihood of the peer not getting successful TX status for the + * Invitation Response frame, wait a short time here before starting + * the persistent group so that we will remain on the current channel to + * acknowledge any possible retransmission from the peer. + */ + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: 50 ms wait on current channel before " + "starting persistent group"); + os_sleep(0, 50000); + + freq = wpa_s->p2p_persistent_go_freq; + if (neg_freq > 0 && ssid->mode == WPAS_MODE_P2P_GO && + freq_included(channels, neg_freq)) { + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Use frequence %d MHz from invitation for GO mode", + neg_freq); + freq = neg_freq; + } + wpas_p2p_group_add_persistent(wpa_s, ssid, - ssid->mode == WPAS_MODE_P2P_GO, 0); + ssid->mode == WPAS_MODE_P2P_GO, + freq, + wpa_s->p2p_go_ht40, wpa_s->p2p_go_vht, + channels, + ssid->mode == WPAS_MODE_P2P_GO ? + P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE : + 0); +} + + +static int wpas_p2p_disallowed_freq(struct wpa_global *global, + unsigned int freq) +{ + if (freq_range_list_includes(&global->p2p_go_avoid_freq, freq)) + return 1; + return freq_range_list_includes(&global->p2p_disallow_freq, freq); +} + + +static void wpas_p2p_add_chan(struct p2p_reg_class *reg, u8 chan) +{ + reg->channel[reg->channels] = chan; + reg->channels++; } static int wpas_p2p_default_channels(struct wpa_supplicant *wpa_s, - struct p2p_channels *chan) + struct p2p_channels *chan, + struct p2p_channels *cli_chan) { int i, cla = 0; + os_memset(cli_chan, 0, sizeof(*cli_chan)); + wpa_printf(MSG_DEBUG, "P2P: Enable operating classes for 2.4 GHz " "band"); /* Operating class 81 - 2.4 GHz band channels 1..13 */ chan->reg_class[cla].reg_class = 81; - chan->reg_class[cla].channels = 11; - for (i = 0; i < 11; i++) - chan->reg_class[cla].channel[i] = i + 1; - cla++; + chan->reg_class[cla].channels = 0; + for (i = 0; i < 11; i++) { + if (!wpas_p2p_disallowed_freq(wpa_s->global, 2412 + i * 5)) + wpas_p2p_add_chan(&chan->reg_class[cla], i + 1); + } + if (chan->reg_class[cla].channels) + cla++; wpa_printf(MSG_DEBUG, "P2P: Enable operating classes for lower 5 GHz " "band"); /* Operating class 115 - 5 GHz, channels 36-48 */ chan->reg_class[cla].reg_class = 115; - chan->reg_class[cla].channels = 4; - chan->reg_class[cla].channel[0] = 36; - chan->reg_class[cla].channel[1] = 40; - chan->reg_class[cla].channel[2] = 44; - chan->reg_class[cla].channel[3] = 48; - cla++; + chan->reg_class[cla].channels = 0; + if (!wpas_p2p_disallowed_freq(wpa_s->global, 5000 + 36 * 5)) + wpas_p2p_add_chan(&chan->reg_class[cla], 36); + if (!wpas_p2p_disallowed_freq(wpa_s->global, 5000 + 40 * 5)) + wpas_p2p_add_chan(&chan->reg_class[cla], 40); + if (!wpas_p2p_disallowed_freq(wpa_s->global, 5000 + 44 * 5)) + wpas_p2p_add_chan(&chan->reg_class[cla], 44); + if (!wpas_p2p_disallowed_freq(wpa_s->global, 5000 + 48 * 5)) + wpas_p2p_add_chan(&chan->reg_class[cla], 48); + if (chan->reg_class[cla].channels) + cla++; wpa_printf(MSG_DEBUG, "P2P: Enable operating classes for higher 5 GHz " "band"); /* Operating class 124 - 5 GHz, channels 149,153,157,161 */ chan->reg_class[cla].reg_class = 124; - chan->reg_class[cla].channels = 4; - chan->reg_class[cla].channel[0] = 149; - chan->reg_class[cla].channel[1] = 153; - chan->reg_class[cla].channel[2] = 157; - chan->reg_class[cla].channel[3] = 161; - cla++; + chan->reg_class[cla].channels = 0; + if (!wpas_p2p_disallowed_freq(wpa_s->global, 5000 + 149 * 5)) + wpas_p2p_add_chan(&chan->reg_class[cla], 149); + if (!wpas_p2p_disallowed_freq(wpa_s->global, 5000 + 153 * 5)) + wpas_p2p_add_chan(&chan->reg_class[cla], 153); + if (!wpas_p2p_disallowed_freq(wpa_s->global, 5000 + 156 * 5)) + wpas_p2p_add_chan(&chan->reg_class[cla], 157); + if (!wpas_p2p_disallowed_freq(wpa_s->global, 5000 + 161 * 5)) + wpas_p2p_add_chan(&chan->reg_class[cla], 161); + if (chan->reg_class[cla].channels) + cla++; chan->reg_classes = cla; return 0; @@ -2095,23 +3334,38 @@ } -static int has_channel(struct hostapd_hw_modes *mode, u8 chan, int *flags) +enum chan_allowed { + NOT_ALLOWED, PASSIVE_ONLY, ALLOWED +}; + +static int has_channel(struct wpa_global *global, + struct hostapd_hw_modes *mode, u8 chan, int *flags) { int i; + unsigned int freq; + + freq = (mode->mode == HOSTAPD_MODE_IEEE80211A ? 5000 : 2407) + + chan * 5; + if (wpas_p2p_disallowed_freq(global, freq)) + return NOT_ALLOWED; for (i = 0; i < mode->num_channels; i++) { if (mode->channels[i].chan == chan) { if (flags) *flags = mode->channels[i].flag; - return !(mode->channels[i].flag & - (HOSTAPD_CHAN_DISABLED | - HOSTAPD_CHAN_PASSIVE_SCAN | - HOSTAPD_CHAN_NO_IBSS | - HOSTAPD_CHAN_RADAR)); + if (mode->channels[i].flag & + (HOSTAPD_CHAN_DISABLED | + HOSTAPD_CHAN_RADAR)) + return NOT_ALLOWED; + if (mode->channels[i].flag & + (HOSTAPD_CHAN_PASSIVE_SCAN | + HOSTAPD_CHAN_NO_IBSS)) + return PASSIVE_ONLY; + return ALLOWED; } } - return 0; + return NOT_ALLOWED; } @@ -2121,81 +3375,224 @@ u8 min_chan; u8 max_chan; u8 inc; - enum { BW20, BW40PLUS, BW40MINUS } bw; + enum { BW20, BW40PLUS, BW40MINUS, BW80 } bw; }; +static struct p2p_oper_class_map op_class[] = { + { HOSTAPD_MODE_IEEE80211G, 81, 1, 13, 1, BW20 }, +#if 0 /* Do not enable HT40 on 2 GHz for now */ + { HOSTAPD_MODE_IEEE80211G, 83, 1, 9, 1, BW40PLUS }, + { HOSTAPD_MODE_IEEE80211G, 84, 5, 13, 1, BW40MINUS }, +#endif + { HOSTAPD_MODE_IEEE80211A, 115, 36, 48, 4, BW20 }, + { HOSTAPD_MODE_IEEE80211A, 124, 149, 161, 4, BW20 }, + { HOSTAPD_MODE_IEEE80211A, 116, 36, 44, 8, BW40PLUS }, + { HOSTAPD_MODE_IEEE80211A, 117, 40, 48, 8, BW40MINUS }, + { HOSTAPD_MODE_IEEE80211A, 126, 149, 157, 8, BW40PLUS }, + { HOSTAPD_MODE_IEEE80211A, 127, 153, 161, 8, BW40MINUS }, + + /* + * IEEE P802.11ac/D7.0 Table E-4 actually talks about channel center + * frequency index 42, 58, 106, 122, 138, 155 with channel spacing of + * 80 MHz, but currently use the following definition for simplicity + * (these center frequencies are not actual channels, which makes + * has_channel() fail). wpas_p2p_verify_80mhz() should take care of + * removing invalid channels. + */ + { HOSTAPD_MODE_IEEE80211A, 128, 36, 161, 4, BW80 }, + { -1, 0, 0, 0, 0, BW20 } +}; + + +static int wpas_p2p_get_center_80mhz(struct wpa_supplicant *wpa_s, + struct hostapd_hw_modes *mode, + u8 channel) +{ + u8 center_channels[] = { 42, 58, 106, 122, 138, 155 }; + unsigned int i; + + if (mode->mode != HOSTAPD_MODE_IEEE80211A) + return 0; + + for (i = 0; i < ARRAY_SIZE(center_channels); i++) + /* + * In 80 MHz, the bandwidth "spans" 12 channels (e.g., 36-48), + * so the center channel is 6 channels away from the start/end. + */ + if (channel >= center_channels[i] - 6 && + channel <= center_channels[i] + 6) + return center_channels[i]; + + return 0; +} + + +static enum chan_allowed wpas_p2p_verify_80mhz(struct wpa_supplicant *wpa_s, + struct hostapd_hw_modes *mode, + u8 channel, u8 bw) +{ + u8 center_chan; + int i, flags; + enum chan_allowed res, ret = ALLOWED; + + center_chan = wpas_p2p_get_center_80mhz(wpa_s, mode, channel); + if (!center_chan) + return NOT_ALLOWED; + if (center_chan >= 58 && center_chan <= 138) + return NOT_ALLOWED; /* Do not allow DFS channels for P2P */ + + /* check all the channels are available */ + for (i = 0; i < 4; i++) { + int adj_chan = center_chan - 6 + i * 4; + + res = has_channel(wpa_s->global, mode, adj_chan, &flags); + if (res == NOT_ALLOWED) + return NOT_ALLOWED; + if (res == PASSIVE_ONLY) + ret = PASSIVE_ONLY; + + if (i == 0 && !(flags & HOSTAPD_CHAN_VHT_10_70)) + return NOT_ALLOWED; + if (i == 1 && !(flags & HOSTAPD_CHAN_VHT_30_50)) + return NOT_ALLOWED; + if (i == 2 && !(flags & HOSTAPD_CHAN_VHT_50_30)) + return NOT_ALLOWED; + if (i == 3 && !(flags & HOSTAPD_CHAN_VHT_70_10)) + return NOT_ALLOWED; + } + + return ret; +} + + +static enum chan_allowed wpas_p2p_verify_channel(struct wpa_supplicant *wpa_s, + struct hostapd_hw_modes *mode, + u8 channel, u8 bw) +{ + int flag; + enum chan_allowed res, res2; + + res2 = res = has_channel(wpa_s->global, mode, channel, &flag); + if (bw == BW40MINUS) { + if (!(flag & HOSTAPD_CHAN_HT40MINUS)) + return NOT_ALLOWED; + res2 = has_channel(wpa_s->global, mode, channel - 4, NULL); + } else if (bw == BW40PLUS) { + if (!(flag & HOSTAPD_CHAN_HT40PLUS)) + return NOT_ALLOWED; + res2 = has_channel(wpa_s->global, mode, channel + 4, NULL); + } else if (bw == BW80) { + res2 = wpas_p2p_verify_80mhz(wpa_s, mode, channel, bw); + } + + if (res == NOT_ALLOWED || res2 == NOT_ALLOWED) + return NOT_ALLOWED; + if (res == PASSIVE_ONLY || res2 == PASSIVE_ONLY) + return PASSIVE_ONLY; + return res; +} + + static int wpas_p2p_setup_channels(struct wpa_supplicant *wpa_s, - struct p2p_channels *chan) + struct p2p_channels *chan, + struct p2p_channels *cli_chan) { struct hostapd_hw_modes *mode; - int cla, op; - struct p2p_oper_class_map op_class[] = { - { HOSTAPD_MODE_IEEE80211G, 81, 1, 13, 1, BW20 }, - { HOSTAPD_MODE_IEEE80211G, 82, 14, 14, 1, BW20 }, -#if 0 /* Do not enable HT40 on 2 GHz for now */ - { HOSTAPD_MODE_IEEE80211G, 83, 1, 9, 1, BW40PLUS }, - { HOSTAPD_MODE_IEEE80211G, 84, 5, 13, 1, BW40MINUS }, -#endif - { HOSTAPD_MODE_IEEE80211A, 115, 36, 48, 4, BW20 }, - { HOSTAPD_MODE_IEEE80211A, 124, 149, 161, 4, BW20 }, - { HOSTAPD_MODE_IEEE80211A, 116, 36, 44, 8, BW40PLUS }, - { HOSTAPD_MODE_IEEE80211A, 117, 40, 48, 8, BW40MINUS }, - { HOSTAPD_MODE_IEEE80211A, 126, 149, 157, 8, BW40PLUS }, - { HOSTAPD_MODE_IEEE80211A, 127, 153, 161, 8, BW40MINUS }, - { -1, 0, 0, 0, 0, BW20 } - }; + int cla, op, cli_cla; if (wpa_s->hw.modes == NULL) { wpa_printf(MSG_DEBUG, "P2P: Driver did not support fetching " "of all supported channels; assume dualband " "support"); - return wpas_p2p_default_channels(wpa_s, chan); + return wpas_p2p_default_channels(wpa_s, chan, cli_chan); } - cla = 0; + cla = cli_cla = 0; for (op = 0; op_class[op].op_class; op++) { struct p2p_oper_class_map *o = &op_class[op]; u8 ch; - struct p2p_reg_class *reg = NULL; + struct p2p_reg_class *reg = NULL, *cli_reg = NULL; mode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes, o->mode); if (mode == NULL) continue; for (ch = o->min_chan; ch <= o->max_chan; ch += o->inc) { - int flag; - if (!has_channel(mode, ch, &flag)) - continue; - if (o->bw == BW40MINUS && - (!(flag & HOSTAPD_CHAN_HT40MINUS) || - !has_channel(mode, ch - 4, NULL))) - continue; - if (o->bw == BW40PLUS && - (!(flag & HOSTAPD_CHAN_HT40PLUS) || - !has_channel(mode, ch + 4, NULL))) - continue; - if (reg == NULL) { - wpa_printf(MSG_DEBUG, "P2P: Add operating " - "class %u", o->op_class); - reg = &chan->reg_class[cla]; - cla++; - reg->reg_class = o->op_class; + enum chan_allowed res; + res = wpas_p2p_verify_channel(wpa_s, mode, ch, o->bw); + if (res == ALLOWED) { + if (reg == NULL) { + wpa_printf(MSG_DEBUG, "P2P: Add operating class %u", + o->op_class); + reg = &chan->reg_class[cla]; + cla++; + reg->reg_class = o->op_class; + } + reg->channel[reg->channels] = ch; + reg->channels++; + } else if (res == PASSIVE_ONLY && + wpa_s->conf->p2p_add_cli_chan) { + if (cli_reg == NULL) { + wpa_printf(MSG_DEBUG, "P2P: Add operating class %u (client only)", + o->op_class); + cli_reg = &cli_chan->reg_class[cli_cla]; + cli_cla++; + cli_reg->reg_class = o->op_class; + } + cli_reg->channel[cli_reg->channels] = ch; + cli_reg->channels++; } - reg->channel[reg->channels] = ch; - reg->channels++; } if (reg) { wpa_hexdump(MSG_DEBUG, "P2P: Channels", reg->channel, reg->channels); } + if (cli_reg) { + wpa_hexdump(MSG_DEBUG, "P2P: Channels (client only)", + cli_reg->channel, cli_reg->channels); + } } chan->reg_classes = cla; + cli_chan->reg_classes = cli_cla; + + return 0; +} + + +int wpas_p2p_get_ht40_mode(struct wpa_supplicant *wpa_s, + struct hostapd_hw_modes *mode, u8 channel) +{ + int op; + enum chan_allowed ret; + + for (op = 0; op_class[op].op_class; op++) { + struct p2p_oper_class_map *o = &op_class[op]; + u8 ch; + for (ch = o->min_chan; ch <= o->max_chan; ch += o->inc) { + if (o->mode != HOSTAPD_MODE_IEEE80211A || + o->bw == BW20 || ch != channel) + continue; + ret = wpas_p2p_verify_channel(wpa_s, mode, ch, o->bw); + if (ret == ALLOWED) + return (o->bw == BW40MINUS) ? -1 : 1; + } + } return 0; } +int wpas_p2p_get_vht80_center(struct wpa_supplicant *wpa_s, + struct hostapd_hw_modes *mode, u8 channel) +{ + if (!wpas_p2p_verify_channel(wpa_s, mode, channel, BW80)) + return 0; + + return wpas_p2p_get_center_80mhz(wpa_s, mode, channel); +} + + static int wpas_get_noa(void *ctx, const u8 *interface_addr, u8 *buf, size_t buf_len) { @@ -2233,6 +3630,89 @@ } +static int wpas_is_concurrent_session_active(void *ctx) +{ + struct wpa_supplicant *wpa_s = ctx; + struct wpa_supplicant *ifs; + + for (ifs = wpa_s->global->ifaces; ifs; ifs = ifs->next) { + if (ifs == wpa_s) + continue; + if (ifs->wpa_state > WPA_ASSOCIATED) + return 1; + } + return 0; +} + + +static void wpas_p2p_debug_print(void *ctx, int level, const char *msg) +{ + struct wpa_supplicant *wpa_s = ctx; + wpa_msg_global(wpa_s, level, "P2P: %s", msg); +} + + +int wpas_p2p_add_p2pdev_interface(struct wpa_supplicant *wpa_s) +{ + struct wpa_interface iface; + struct wpa_supplicant *p2pdev_wpa_s; + char ifname[100]; + char force_name[100]; + int ret; + + os_snprintf(ifname, sizeof(ifname), P2P_MGMT_DEVICE_PREFIX "%s", + wpa_s->ifname); + force_name[0] = '\0'; + wpa_s->pending_interface_type = WPA_IF_P2P_DEVICE; + ret = wpa_drv_if_add(wpa_s, WPA_IF_P2P_DEVICE, ifname, NULL, NULL, + force_name, wpa_s->pending_interface_addr, NULL); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "P2P: Failed to create P2P Device interface"); + return ret; + } + os_strlcpy(wpa_s->pending_interface_name, ifname, + sizeof(wpa_s->pending_interface_name)); + + os_memset(&iface, 0, sizeof(iface)); + iface.p2p_mgmt = 1; + iface.ifname = wpa_s->pending_interface_name; + iface.driver = wpa_s->driver->name; + iface.driver_param = wpa_s->conf->driver_param; + iface.confname = wpa_s->confname; + p2pdev_wpa_s = wpa_supplicant_add_iface(wpa_s->global, &iface); + if (!p2pdev_wpa_s) { + wpa_printf(MSG_DEBUG, "P2P: Failed to add P2P Device interface"); + return -1; + } + p2pdev_wpa_s->parent = wpa_s; + + wpa_s->pending_interface_name[0] = '\0'; + return 0; +} + + +static void wpas_presence_resp(void *ctx, const u8 *src, u8 status, + const u8 *noa, size_t noa_len) +{ + struct wpa_supplicant *wpa_s, *intf = ctx; + char hex[100]; + + for (wpa_s = intf->global->ifaces; wpa_s; wpa_s = wpa_s->next) { + if (wpa_s->waiting_presence_resp) + break; + } + if (!wpa_s) { + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: No group interface was waiting for presence response"); + return; + } + wpa_s->waiting_presence_resp = 0; + + wpa_snprintf_hex(hex, sizeof(hex), noa, noa_len); + wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_PRESENCE_RESPONSE "src=" MACSTR + " status=%u noa=%s", MAC2STR(src), status, hex); +} + + /** * wpas_p2p_init - Initialize P2P module for %wpa_supplicant * @global: Pointer to global data from wpa_supplicant_init() @@ -2245,34 +3725,18 @@ unsigned int r; int i; - if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_CAPABLE)) + if (wpa_s->conf->p2p_disabled) return 0; - if (global->p2p) + if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_CAPABLE)) return 0; - if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT) { - struct p2p_params params; - - wpa_printf(MSG_DEBUG, "P2P: Use driver-based P2P management"); - os_memset(¶ms, 0, sizeof(params)); - params.dev_name = wpa_s->conf->device_name; - os_memcpy(params.pri_dev_type, wpa_s->conf->device_type, - WPS_DEV_TYPE_LEN); - params.num_sec_dev_types = wpa_s->conf->num_sec_device_types; - os_memcpy(params.sec_dev_type, - wpa_s->conf->sec_device_type, - params.num_sec_dev_types * WPS_DEV_TYPE_LEN); - - if (wpa_drv_p2p_set_params(wpa_s, ¶ms) < 0) - return -1; - + if (global->p2p) return 0; - } os_memset(&p2p, 0, sizeof(p2p)); - p2p.msg_ctx = wpa_s; p2p.cb_ctx = wpa_s; + p2p.debug_print = wpas_p2p_debug_print; p2p.p2p_scan = wpas_p2p_scan; p2p.send_action = wpas_send_action; p2p.send_action_done = wpas_send_action_done; @@ -2280,6 +3744,7 @@ p2p.go_neg_req_rx = wpas_go_neg_req_rx; p2p.dev_found = wpas_dev_found; p2p.dev_lost = wpas_dev_lost; + p2p.find_stopped = wpas_find_stopped; p2p.start_listen = wpas_start_listen; p2p.stop_listen = wpas_stop_listen; p2p.send_probe_resp = wpas_send_probe_resp; @@ -2293,6 +3758,8 @@ p2p.invitation_result = wpas_invitation_result; p2p.get_noa = wpas_get_noa; p2p.go_connected = wpas_go_connected; + p2p.presence_resp = wpas_presence_resp; + p2p.is_concurrent_session_active = wpas_is_concurrent_session_active; os_memcpy(wpa_s->global->p2p_dev_addr, wpa_s->own_addr, ETH_ALEN); os_memcpy(p2p.dev_addr, wpa_s->global->p2p_dev_addr, ETH_ALEN); @@ -2341,13 +3808,19 @@ wpa_printf(MSG_DEBUG, "P2P: Random operating channel: " "%d:%d", p2p.op_reg_class, p2p.op_channel); } + + if (wpa_s->conf->p2p_pref_chan && wpa_s->conf->num_p2p_pref_chan) { + p2p.pref_chan = wpa_s->conf->p2p_pref_chan; + p2p.num_pref_chan = wpa_s->conf->num_p2p_pref_chan; + } + if (wpa_s->conf->country[0] && wpa_s->conf->country[1]) { os_memcpy(p2p.country, wpa_s->conf->country, 2); p2p.country[2] = 0x04; } else os_memcpy(p2p.country, "XX\x04", 3); - if (wpas_p2p_setup_channels(wpa_s, &p2p.channels)) { + if (wpas_p2p_setup_channels(wpa_s, &p2p.channels, &p2p.cli_channels)) { wpa_printf(MSG_ERROR, "P2P: Failed to configure supported " "channel list"); return -1; @@ -2376,9 +3849,12 @@ p2p.p2p_intra_bss = wpa_s->conf->p2p_intra_bss; + p2p.max_listen = wpa_s->max_remain_on_chan; + global->p2p = p2p_init(&p2p); if (global->p2p == NULL) return -1; + global->p2p_init_wpa_s = wpa_s; for (i = 0; i < MAX_WPS_VENDOR_EXT; i++) { if (wpa_s->conf->wps_vendor_ext[i] == NULL) @@ -2387,6 +3863,8 @@ global->p2p, wpa_s->conf->wps_vendor_ext[i]); } + p2p_set_no_go_freq(global->p2p, &wpa_s->conf->p2p_no_go_freq); + return 0; } @@ -2413,11 +3891,21 @@ wpa_s->go_params = NULL; eloop_cancel_timeout(wpas_p2p_group_formation_timeout, wpa_s, NULL); eloop_cancel_timeout(wpas_p2p_join_scan, wpa_s, NULL); - eloop_cancel_timeout(wpas_p2p_pd_before_join_timeout, wpa_s, NULL); wpa_s->p2p_long_listen = 0; eloop_cancel_timeout(wpas_p2p_long_listen_timeout, wpa_s, NULL); eloop_cancel_timeout(wpas_p2p_group_idle_timeout, wpa_s, NULL); wpas_p2p_remove_pending_group_interface(wpa_s); + eloop_cancel_timeout(wpas_p2p_group_freq_conflict, wpa_s, NULL); + wpas_p2p_listen_work_done(wpa_s); + if (wpa_s->p2p_send_action_work) { + os_free(wpa_s->p2p_send_action_work->ctx); + radio_work_done(wpa_s->p2p_send_action_work); + wpa_s->p2p_send_action_work = NULL; + } + eloop_cancel_timeout(wpas_p2p_send_action_work_timeout, wpa_s, NULL); + + wpabuf_free(wpa_s->p2p_oob_dev_pw); + wpa_s->p2p_oob_dev_pw = NULL; /* TODO: remove group interface from the driver if this wpa_s instance * is on top of a P2P group interface */ @@ -2434,17 +3922,17 @@ { struct wpa_supplicant *wpa_s, *tmp; + wpa_s = global->ifaces; + if (wpa_s) + wpas_p2p_service_flush(wpa_s); + if (global->p2p == NULL) return; /* Remove remaining P2P group interfaces */ - wpa_s = global->ifaces; - if (wpa_s) - wpas_p2p_service_flush(wpa_s); while (wpa_s && wpa_s->p2p_group_interface != NOT_P2P_GROUP_INTERFACE) wpa_s = wpa_s->next; while (wpa_s) { - enum wpa_driver_if_type type; tmp = global->ifaces; while (tmp && (tmp == wpa_s || @@ -2453,7 +3941,6 @@ } if (tmp == NULL) break; - type = wpas_p2p_if_type(tmp->p2p_group_interface); /* Disconnect from the P2P group and deinit the interface */ wpas_p2p_disconnect(tmp); } @@ -2469,11 +3956,15 @@ p2p_deinit(global->p2p); global->p2p = NULL; + global->p2p_init_wpa_s = NULL; } static int wpas_p2p_create_iface(struct wpa_supplicant *wpa_s) { + if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE) && + wpa_s->conf->p2p_no_group_iface) + return 0; /* separate interface disabled per configuration */ if (wpa_s->drv_flags & (WPA_DRIVER_FLAGS_P2P_DEDICATED_INTERFACE | WPA_DRIVER_FLAGS_P2P_MGMT_AND_NON_P2P)) @@ -2493,20 +3984,26 @@ const u8 *peer_addr, enum p2p_wps_method wps_method, int go_intent, const u8 *own_interface_addr, - unsigned int force_freq, int persistent_group) + unsigned int force_freq, int persistent_group, + struct wpa_ssid *ssid, unsigned int pref_freq) { if (persistent_group && wpa_s->conf->persistent_reconnect) persistent_group = 2; - if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT) { - return wpa_drv_p2p_connect(wpa_s, peer_addr, wps_method, - go_intent, own_interface_addr, - force_freq, persistent_group); - } + /* + * Increase GO config timeout if HT40 is used since it takes some time + * to scan channels for coex purposes before the BSS can be started. + */ + p2p_set_config_timeout(wpa_s->global->p2p, + wpa_s->p2p_go_ht40 ? 255 : 100, 20); return p2p_connect(wpa_s->global->p2p, peer_addr, wps_method, go_intent, own_interface_addr, force_freq, - persistent_group); + persistent_group, ssid ? ssid->ssid : NULL, + ssid ? ssid->ssid_len : 0, + wpa_s->p2p_pd_before_go_neg, pref_freq, + wps_method == WPS_NFC ? wpa_s->p2p_oob_dev_pw_id : + 0); } @@ -2514,17 +4011,18 @@ const u8 *peer_addr, enum p2p_wps_method wps_method, int go_intent, const u8 *own_interface_addr, - unsigned int force_freq, int persistent_group) + unsigned int force_freq, int persistent_group, + struct wpa_ssid *ssid, unsigned int pref_freq) { if (persistent_group && wpa_s->conf->persistent_reconnect) persistent_group = 2; - if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT) - return -1; - return p2p_authorize(wpa_s->global->p2p, peer_addr, wps_method, go_intent, own_interface_addr, force_freq, - persistent_group); + persistent_group, ssid ? ssid->ssid : NULL, + ssid ? ssid->ssid_len : 0, pref_freq, + wps_method == WPS_NFC ? wpa_s->p2p_oob_dev_pw_id : + 0); } @@ -2538,31 +4036,87 @@ " for join operationg - stop join attempt", MAC2STR(wpa_s->pending_join_iface_addr)); eloop_cancel_timeout(wpas_p2p_join_scan, wpa_s, NULL); - wpa_msg(wpa_s->parent, MSG_INFO, - P2P_EVENT_GROUP_FORMATION_FAILURE); + if (wpa_s->p2p_auto_pd) { + wpa_s->p2p_auto_pd = 0; + wpa_msg_global(wpa_s, MSG_INFO, + P2P_EVENT_PROV_DISC_FAILURE + " p2p_dev_addr=" MACSTR " status=N/A", + MAC2STR(wpa_s->pending_join_dev_addr)); + return; + } + wpa_msg_global(wpa_s->parent, MSG_INFO, + P2P_EVENT_GROUP_FORMATION_FAILURE); } } -static void wpas_p2p_pd_before_join_timeout(void *eloop_ctx, void *timeout_ctx) +static int wpas_check_freq_conflict(struct wpa_supplicant *wpa_s, int freq) { - struct wpa_supplicant *wpa_s = eloop_ctx; - if (!wpa_s->pending_pd_before_join) - return; - /* - * Provision Discovery Response may have been lost - try to connect - * anyway since we do not need any information from this PD. - */ - wpa_printf(MSG_DEBUG, "P2P: PD timeout for join-existing-group - " - "try to connect anyway"); - wpas_p2p_join_start(wpa_s); + int *freqs, res, num, i; + + if (wpas_p2p_num_unused_channels(wpa_s) > 0) { + /* Multiple channels are supported and not all are in use */ + return 0; + } + + freqs = os_calloc(wpa_s->num_multichan_concurrent, sizeof(int)); + if (!freqs) + return 1; + + num = wpas_p2p_valid_oper_freqs(wpa_s, freqs, + wpa_s->num_multichan_concurrent); + if (num < 0) { + res = 1; + goto exit_free; + } + + for (i = 0; i < num; i++) { + if (freqs[i] == freq) { + wpa_printf(MSG_DEBUG, "P2P: Frequency %d MHz in use by another virtual interface and can be used", + freq); + res = 0; + goto exit_free; + } + } + + res = 1; + +exit_free: + os_free(freqs); + return res; +} + + +static int wpas_p2p_peer_go(struct wpa_supplicant *wpa_s, + const u8 *peer_dev_addr) +{ + struct wpa_bss *bss; + int updated; + + bss = wpa_bss_get_p2p_dev_addr(wpa_s, peer_dev_addr); + if (bss == NULL) + return -1; + if (bss->last_update_idx < wpa_s->bss_update_idx) { + wpa_printf(MSG_DEBUG, "P2P: Peer BSS entry not updated in the " + "last scan"); + return 0; + } + + updated = os_reltime_before(&wpa_s->p2p_auto_started, + &bss->last_update); + wpa_printf(MSG_DEBUG, "P2P: Current BSS entry for peer updated at " + "%ld.%06ld (%supdated in last scan)", + bss->last_update.sec, bss->last_update.usec, + updated ? "": "not "); + + return updated; } static void wpas_p2p_scan_res_join(struct wpa_supplicant *wpa_s, struct wpa_scan_results *scan_res) { - struct wpa_bss *bss; + struct wpa_bss *bss = NULL; int freq; u8 iface_addr[ETH_ALEN]; @@ -2571,12 +4125,79 @@ if (wpa_s->global->p2p_disabled) return; - wpa_printf(MSG_DEBUG, "P2P: Scan results received (%d BSS) for join", - scan_res ? (int) scan_res->num : -1); + wpa_printf(MSG_DEBUG, "P2P: Scan results received (%d BSS) for %sjoin", + scan_res ? (int) scan_res->num : -1, + wpa_s->p2p_auto_join ? "auto_" : ""); if (scan_res) wpas_p2p_scan_res_handler(wpa_s, scan_res); + if (wpa_s->p2p_auto_pd) { + int join = wpas_p2p_peer_go(wpa_s, + wpa_s->pending_join_dev_addr); + if (join == 0 && + wpa_s->auto_pd_scan_retry < P2P_AUTO_PD_SCAN_ATTEMPTS) { + wpa_s->auto_pd_scan_retry++; + bss = wpa_bss_get_bssid_latest( + wpa_s, wpa_s->pending_join_dev_addr); + if (bss) { + freq = bss->freq; + wpa_printf(MSG_DEBUG, "P2P: Scan retry %d for " + "the peer " MACSTR " at %d MHz", + wpa_s->auto_pd_scan_retry, + MAC2STR(wpa_s-> + pending_join_dev_addr), + freq); + wpas_p2p_join_scan_req(wpa_s, freq, NULL, 0); + return; + } + } + + if (join < 0) + join = 0; + + wpa_s->p2p_auto_pd = 0; + wpa_s->pending_pd_use = join ? AUTO_PD_JOIN : AUTO_PD_GO_NEG; + wpa_printf(MSG_DEBUG, "P2P: Auto PD with " MACSTR " join=%d", + MAC2STR(wpa_s->pending_join_dev_addr), join); + if (p2p_prov_disc_req(wpa_s->global->p2p, + wpa_s->pending_join_dev_addr, + wpa_s->pending_pd_config_methods, join, + 0, wpa_s->user_initiated_pd) < 0) { + wpa_s->p2p_auto_pd = 0; + wpa_msg_global(wpa_s, MSG_INFO, + P2P_EVENT_PROV_DISC_FAILURE + " p2p_dev_addr=" MACSTR " status=N/A", + MAC2STR(wpa_s->pending_join_dev_addr)); + } + return; + } + + if (wpa_s->p2p_auto_join) { + int join = wpas_p2p_peer_go(wpa_s, + wpa_s->pending_join_dev_addr); + if (join < 0) { + wpa_printf(MSG_DEBUG, "P2P: Peer was not found to be " + "running a GO -> use GO Negotiation"); + wpas_p2p_connect(wpa_s, wpa_s->pending_join_dev_addr, + wpa_s->p2p_pin, wpa_s->p2p_wps_method, + wpa_s->p2p_persistent_group, 0, 0, 0, + wpa_s->p2p_go_intent, + wpa_s->p2p_connect_freq, + wpa_s->p2p_persistent_id, + wpa_s->p2p_pd_before_go_neg, + wpa_s->p2p_go_ht40, + wpa_s->p2p_go_vht); + return; + } + + wpa_printf(MSG_DEBUG, "P2P: Peer was found running GO%s -> " + "try to join the group", join ? "" : + " in older scan"); + if (!join) + wpa_s->p2p_fallback_to_go_neg = 1; + } + freq = p2p_get_oper_freq(wpa_s->global->p2p, wpa_s->pending_join_iface_addr); if (freq < 0 && @@ -2600,15 +4221,38 @@ wpa_printf(MSG_DEBUG, "P2P: Target GO operating frequency " "from P2P peer table: %d MHz", freq); } - bss = wpa_bss_get_bssid(wpa_s, wpa_s->pending_join_iface_addr); + if (wpa_s->p2p_join_ssid_len) { + wpa_printf(MSG_DEBUG, "P2P: Trying to find target GO BSS entry based on BSSID " + MACSTR " and SSID %s", + MAC2STR(wpa_s->pending_join_iface_addr), + wpa_ssid_txt(wpa_s->p2p_join_ssid, + wpa_s->p2p_join_ssid_len)); + bss = wpa_bss_get(wpa_s, wpa_s->pending_join_iface_addr, + wpa_s->p2p_join_ssid, + wpa_s->p2p_join_ssid_len); + } + if (!bss) { + wpa_printf(MSG_DEBUG, "P2P: Trying to find target GO BSS entry based on BSSID " + MACSTR, MAC2STR(wpa_s->pending_join_iface_addr)); + bss = wpa_bss_get_bssid_latest(wpa_s, + wpa_s->pending_join_iface_addr); + } if (bss) { freq = bss->freq; wpa_printf(MSG_DEBUG, "P2P: Target GO operating frequency " - "from BSS table: %d MHz", freq); + "from BSS table: %d MHz (SSID %s)", freq, + wpa_ssid_txt(bss->ssid, bss->ssid_len)); } if (freq > 0) { u16 method; + if (wpas_check_freq_conflict(wpa_s, freq) > 0) { + wpa_msg_global(wpa_s->parent, MSG_INFO, + P2P_EVENT_GROUP_FORMATION_FAILURE + "reason=FREQ_CONFLICT"); + return; + } + wpa_printf(MSG_DEBUG, "P2P: Send Provision Discovery Request " "prior to joining an existing group (GO " MACSTR " freq=%u MHz)", @@ -2637,7 +4281,7 @@ * We have already performed provision discovery for * joining the group. Proceed directly to join * operation without duplicated provision discovery. */ - wpa_printf(MSG_DEBUG, "P2P: Provisioning discovery " + wpa_printf(MSG_DEBUG, "P2P: Provision discovery " "with " MACSTR " already done - proceed to " "join", MAC2STR(wpa_s->pending_join_dev_addr)); @@ -2647,25 +4291,13 @@ if (p2p_prov_disc_req(wpa_s->global->p2p, wpa_s->pending_join_dev_addr, method, 1, - freq) < 0) { + freq, wpa_s->user_initiated_pd) < 0) { wpa_printf(MSG_DEBUG, "P2P: Failed to send Provision " "Discovery Request before joining an " "existing group"); wpa_s->pending_pd_before_join = 0; goto start; } - - /* - * Actual join operation will be started from the Action frame - * TX status callback (if no ACK is received) or when the - * Provision Discovery Response is received. Use a short - * timeout as a backup mechanism should the Provision Discovery - * Response be lost for any reason. - */ - eloop_cancel_timeout(wpas_p2p_pd_before_join_timeout, wpa_s, - NULL); - eloop_register_timeout(2, 0, wpas_p2p_pd_before_join_timeout, - wpa_s, NULL); return; } @@ -2677,28 +4309,38 @@ start: /* Start join operation immediately */ - wpas_p2p_join_start(wpa_s); + wpas_p2p_join_start(wpa_s, 0, NULL, 0); } -static void wpas_p2p_join_scan(void *eloop_ctx, void *timeout_ctx) +static void wpas_p2p_join_scan_req(struct wpa_supplicant *wpa_s, int freq, + const u8 *ssid, size_t ssid_len) { - struct wpa_supplicant *wpa_s = eloop_ctx; int ret; struct wpa_driver_scan_params params; struct wpabuf *wps_ie, *ies; size_t ielen; + int freqs[2] = { 0, 0 }; os_memset(¶ms, 0, sizeof(params)); /* P2P Wildcard SSID */ params.num_ssids = 1; - params.ssids[0].ssid = (u8 *) P2P_WILDCARD_SSID; - params.ssids[0].ssid_len = P2P_WILDCARD_SSID_LEN; + if (ssid && ssid_len) { + params.ssids[0].ssid = ssid; + params.ssids[0].ssid_len = ssid_len; + os_memcpy(wpa_s->p2p_join_ssid, ssid, ssid_len); + wpa_s->p2p_join_ssid_len = ssid_len; + } else { + params.ssids[0].ssid = (u8 *) P2P_WILDCARD_SSID; + params.ssids[0].ssid_len = P2P_WILDCARD_SSID_LEN; + wpa_s->p2p_join_ssid_len = 0; + } wpa_s->wps->dev.p2p = 1; - wps_ie = wps_build_probe_req_ie(0, &wpa_s->wps->dev, wpa_s->wps->uuid, - WPS_REQ_ENROLLEE, 0, NULL); + wps_ie = wps_build_probe_req_ie(DEV_PW_DEFAULT, &wpa_s->wps->dev, + wpa_s->wps->uuid, WPS_REQ_ENROLLEE, 0, + NULL); if (wps_ie == NULL) { wpas_p2p_scan_res_join(wpa_s, NULL); return; @@ -2720,12 +4362,32 @@ params.extra_ies = wpabuf_head(ies); params.extra_ies_len = wpabuf_len(ies); + if (!freq) { + int oper_freq; + /* + * If freq is not provided, check the operating freq of the GO + * and use a single channel scan on if possible. + */ + oper_freq = p2p_get_oper_freq(wpa_s->global->p2p, + wpa_s->pending_join_iface_addr); + if (oper_freq > 0) + freq = oper_freq; + } + if (freq > 0) { + freqs[0] = freq; + params.freqs = freqs; + } + /* * Run a scan to update BSS table and start Provision Discovery once * the new scan results become available. */ - wpa_s->scan_res_handler = wpas_p2p_scan_res_join; ret = wpa_drv_scan(wpa_s, ¶ms); + if (!ret) { + os_get_reltime(&wpa_s->scan_trigger_time); + wpa_s->scan_res_handler = wpas_p2p_scan_res_join; + wpa_s->own_scan_requested = 1; + } wpabuf_free(ies); @@ -2739,13 +4401,29 @@ } +static void wpas_p2p_join_scan(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + wpas_p2p_join_scan_req(wpa_s, 0, NULL, 0); +} + + static int wpas_p2p_join(struct wpa_supplicant *wpa_s, const u8 *iface_addr, - const u8 *dev_addr, enum p2p_wps_method wps_method) + const u8 *dev_addr, enum p2p_wps_method wps_method, + int auto_join, int op_freq, + const u8 *ssid, size_t ssid_len) { wpa_printf(MSG_DEBUG, "P2P: Request to join existing group (iface " - MACSTR " dev " MACSTR ")", - MAC2STR(iface_addr), MAC2STR(dev_addr)); + MACSTR " dev " MACSTR " op_freq=%d)%s", + MAC2STR(iface_addr), MAC2STR(dev_addr), op_freq, + auto_join ? " (auto_join)" : ""); + if (ssid && ssid_len) { + wpa_printf(MSG_DEBUG, "P2P: Group SSID specified: %s", + wpa_ssid_txt(ssid, ssid_len)); + } + wpa_s->p2p_auto_pd = 0; + wpa_s->p2p_auto_join = !!auto_join; os_memcpy(wpa_s->pending_join_iface_addr, iface_addr, ETH_ALEN); os_memcpy(wpa_s->pending_join_dev_addr, dev_addr, ETH_ALEN); wpa_s->pending_join_wps_method = wps_method; @@ -2754,17 +4432,18 @@ wpas_p2p_stop_find(wpa_s); wpa_s->p2p_join_scan_count = 0; - wpas_p2p_join_scan(wpa_s, NULL); + wpas_p2p_join_scan_req(wpa_s, op_freq, ssid, ssid_len); return 0; } -static int wpas_p2p_join_start(struct wpa_supplicant *wpa_s) +static int wpas_p2p_join_start(struct wpa_supplicant *wpa_s, int freq, + const u8 *ssid, size_t ssid_len) { struct wpa_supplicant *group; struct p2p_go_neg_results res; + struct wpa_bss *bss; - eloop_cancel_timeout(wpas_p2p_pd_before_join_timeout, wpa_s, NULL); group = wpas_p2p_get_group_iface(wpa_s, 0, 0); if (group == NULL) return -1; @@ -2772,14 +4451,42 @@ os_memcpy(group->p2p_pin, wpa_s->p2p_pin, sizeof(group->p2p_pin)); group->p2p_wps_method = wpa_s->p2p_wps_method; + } else { + /* + * Need to mark the current interface for p2p_group_formation + * when a separate group interface is not used. This is needed + * to allow p2p_cancel stop a pending p2p_connect-join. + * wpas_p2p_init_group_interface() addresses this for the case + * where a separate group interface is used. + */ + wpa_s->global->p2p_group_formation = wpa_s; } group->p2p_in_provisioning = 1; + group->p2p_fallback_to_go_neg = wpa_s->p2p_fallback_to_go_neg; os_memset(&res, 0, sizeof(res)); + os_memcpy(res.peer_device_addr, wpa_s->pending_join_dev_addr, ETH_ALEN); os_memcpy(res.peer_interface_addr, wpa_s->pending_join_iface_addr, ETH_ALEN); res.wps_method = wpa_s->pending_join_wps_method; + if (freq && ssid && ssid_len) { + res.freq = freq; + res.ssid_len = ssid_len; + os_memcpy(res.ssid, ssid, ssid_len); + } else { + bss = wpa_bss_get_bssid_latest(wpa_s, + wpa_s->pending_join_iface_addr); + if (bss) { + res.freq = bss->freq; + res.ssid_len = bss->ssid_len; + os_memcpy(res.ssid, bss->ssid, bss->ssid_len); + wpa_printf(MSG_DEBUG, "P2P: Join target GO operating frequency from BSS table: %d MHz (SSID %s)", + bss->freq, + wpa_ssid_txt(bss->ssid, bss->ssid_len)); + } + } + if (wpa_s->off_channel_freq || wpa_s->roc_waiting_drv_freq) { wpa_printf(MSG_DEBUG, "P2P: Cancel remain-on-channel prior to " "starting client"); @@ -2802,35 +4509,141 @@ } +static int wpas_p2p_setup_freqs(struct wpa_supplicant *wpa_s, int freq, + int *force_freq, int *pref_freq, int go) +{ + int *freqs, res; + unsigned int freq_in_use = 0, num, i; + + freqs = os_calloc(wpa_s->num_multichan_concurrent, sizeof(int)); + if (!freqs) + return -1; + + num = get_shared_radio_freqs(wpa_s, freqs, + wpa_s->num_multichan_concurrent); + wpa_printf(MSG_DEBUG, + "P2P: Setup freqs: freq=%d num_MCC=%d shared_freqs=%u", + freq, wpa_s->num_multichan_concurrent, num); + + if (freq > 0) { + int ret; + if (go) + ret = p2p_supported_freq(wpa_s->global->p2p, freq); + else + ret = p2p_supported_freq_cli(wpa_s->global->p2p, freq); + if (!ret) { + wpa_printf(MSG_DEBUG, "P2P: The forced channel " + "(%u MHz) is not supported for P2P uses", + freq); + res = -3; + goto exit_free; + } + + for (i = 0; i < num; i++) { + if (freqs[i] == freq) + freq_in_use = 1; + } + + if (num == wpa_s->num_multichan_concurrent && !freq_in_use) { + wpa_printf(MSG_DEBUG, "P2P: Cannot start P2P group on %u MHz as there are no available channels", + freq); + res = -2; + goto exit_free; + } + wpa_printf(MSG_DEBUG, "P2P: Trying to force us to use the " + "requested channel (%u MHz)", freq); + *force_freq = freq; + goto exit_ok; + } + + for (i = 0; i < num; i++) { + if (!p2p_supported_freq(wpa_s->global->p2p, freqs[i])) + continue; + + if (*pref_freq == 0 && num < wpa_s->num_multichan_concurrent) { + wpa_printf(MSG_DEBUG, "P2P: Try to prefer a frequency (%u MHz) we are already using", + freqs[i]); + *pref_freq = freqs[i]; + } else { + wpa_printf(MSG_DEBUG, "P2P: Try to force us to use frequency (%u MHz) which is already in use", + freqs[i]); + *force_freq = freqs[i]; + } + break; + } + + if (i == num) { + if (num < wpa_s->num_multichan_concurrent && num > 0) { + wpa_printf(MSG_DEBUG, "P2P: Current operating channels are not available for P2P. Try to use another channel"); + *force_freq = 0; + } else if (num < wpa_s->num_multichan_concurrent) { + wpa_printf(MSG_DEBUG, "P2P: No current operating channels - try to use a new channel"); + *force_freq = 0; + } else { + wpa_printf(MSG_DEBUG, "P2P: All channels are in use and none of them are P2P enabled. Cannot start P2P group"); + res = -2; + goto exit_free; + } + } + +exit_ok: + res = 0; +exit_free: + os_free(freqs); + return res; +} + + /** * wpas_p2p_connect - Request P2P Group Formation to be started * @wpa_s: Pointer to wpa_supplicant data from wpa_supplicant_add_iface() * @peer_addr: Address of the peer P2P Device * @pin: PIN to use during provisioning or %NULL to indicate PBC mode * @persistent_group: Whether to create a persistent group + * @auto_join: Whether to select join vs. GO Negotiation automatically * @join: Whether to join an existing group (as a client) instead of starting * Group Owner negotiation; @peer_addr is BSSID in that case * @auth: Whether to only authorize the connection instead of doing that and * initiating Group Owner negotiation * @go_intent: GO Intent or -1 to use default * @freq: Frequency for the group or 0 for auto-selection + * @persistent_id: Persistent group credentials to use for forcing GO + * parameters or -1 to generate new values (SSID/passphrase) + * @pd: Whether to send Provision Discovery prior to GO Negotiation as an + * interoperability workaround when initiating group formation + * @ht40: Start GO with 40 MHz channel width + * @vht: Start GO with VHT support * Returns: 0 or new PIN (if pin was %NULL) on success, -1 on unspecified * failure, -2 on failure due to channel not currently available, * -3 if forced channel is not supported */ int wpas_p2p_connect(struct wpa_supplicant *wpa_s, const u8 *peer_addr, const char *pin, enum p2p_wps_method wps_method, - int persistent_group, int join, int auth, int go_intent, - int freq) + int persistent_group, int auto_join, int join, int auth, + int go_intent, int freq, int persistent_id, int pd, + int ht40, int vht) { - int force_freq = 0, oper_freq = 0; - u8 bssid[ETH_ALEN]; - int ret = 0; + int force_freq = 0, pref_freq = 0; + int ret = 0, res; enum wpa_driver_if_type iftype; + const u8 *if_addr; + struct wpa_ssid *ssid = NULL; if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) return -1; + if (persistent_id >= 0) { + ssid = wpa_config_get_network(wpa_s->conf, persistent_id); + if (ssid == NULL || ssid->disabled != 2 || + ssid->mode != WPAS_MODE_P2P_GO) + return -1; + } + + os_free(wpa_s->global->add_psk); + wpa_s->global->add_psk = NULL; + + wpa_s->global->p2p_fail_on_wps_complete = 0; + if (go_intent < 0) go_intent = wpa_s->conf->p2p_go_intent; @@ -2838,6 +4651,14 @@ wpa_s->p2p_long_listen = 0; wpa_s->p2p_wps_method = wps_method; + wpa_s->p2p_persistent_group = !!persistent_group; + wpa_s->p2p_persistent_id = persistent_id; + wpa_s->p2p_go_intent = go_intent; + wpa_s->p2p_connect_freq = freq; + wpa_s->p2p_fallback_to_go_neg = 0; + wpa_s->p2p_pd_before_go_neg = !!pd; + wpa_s->p2p_go_ht40 = !!ht40; + wpa_s->p2p_go_vht = !!vht; if (pin) os_strlcpy(wpa_s->p2p_pin, pin, sizeof(wpa_s->p2p_pin)); @@ -2850,7 +4671,7 @@ } else wpa_s->p2p_pin[0] = '\0'; - if (join) { + if (join || auto_join) { u8 iface_addr[ETH_ALEN], dev_addr[ETH_ALEN]; if (auth) { wpa_printf(MSG_DEBUG, "P2P: Authorize invitation to " @@ -2866,100 +4687,58 @@ p2p_get_dev_addr(wpa_s->global->p2p, peer_addr, dev_addr); } - if (wpas_p2p_join(wpa_s, iface_addr, dev_addr, wps_method) < - 0) + if (auto_join) { + os_get_reltime(&wpa_s->p2p_auto_started); + wpa_printf(MSG_DEBUG, "P2P: Auto join started at " + "%ld.%06ld", + wpa_s->p2p_auto_started.sec, + wpa_s->p2p_auto_started.usec); + } + wpa_s->user_initiated_pd = 1; + if (wpas_p2p_join(wpa_s, iface_addr, dev_addr, wps_method, + auto_join, freq, NULL, 0) < 0) return -1; return ret; } - if (wpa_s->current_ssid && wpa_drv_get_bssid(wpa_s, bssid) == 0 && - wpa_s->assoc_freq) - oper_freq = wpa_s->assoc_freq; - else { - oper_freq = wpa_drv_shared_freq(wpa_s); - if (oper_freq < 0) - oper_freq = 0; - } - - if (freq > 0) { - if (!p2p_supported_freq(wpa_s->global->p2p, freq)) { - wpa_printf(MSG_DEBUG, "P2P: The forced channel " - "(%u MHz) is not supported for P2P uses", - freq); - return -3; - } - - if (oper_freq > 0 && freq != oper_freq && - !(wpa_s->drv_flags & - WPA_DRIVER_FLAGS_MULTI_CHANNEL_CONCURRENT)) { - wpa_printf(MSG_DEBUG, "P2P: Cannot start P2P group " - "on %u MHz while connected on another " - "channel (%u MHz)", freq, oper_freq); - return -2; - } - wpa_printf(MSG_DEBUG, "P2P: Trying to force us to use the " - "requested channel (%u MHz)", freq); - force_freq = freq; - } else if (oper_freq > 0 && - !p2p_supported_freq(wpa_s->global->p2p, oper_freq)) { - if (!(wpa_s->drv_flags & - WPA_DRIVER_FLAGS_MULTI_CHANNEL_CONCURRENT)) { - wpa_printf(MSG_DEBUG, "P2P: Cannot start P2P group " - "while connected on non-P2P supported " - "channel (%u MHz)", oper_freq); - return -2; - } - wpa_printf(MSG_DEBUG, "P2P: Current operating channel " - "(%u MHz) not available for P2P - try to use " - "another channel", oper_freq); - force_freq = 0; - } else if (oper_freq > 0) { - wpa_printf(MSG_DEBUG, "P2P: Trying to force us to use the " - "channel we are already using (%u MHz) on another " - "interface", oper_freq); - force_freq = oper_freq; - } + res = wpas_p2p_setup_freqs(wpa_s, freq, &force_freq, &pref_freq, + go_intent == 15); + if (res) + return res; + wpas_p2p_set_own_freq_preference(wpa_s, + force_freq ? force_freq : pref_freq); wpa_s->create_p2p_iface = wpas_p2p_create_iface(wpa_s); - if (!wpa_s->create_p2p_iface) { - if (auth) { - if (wpas_p2p_auth_go_neg(wpa_s, peer_addr, wps_method, - go_intent, wpa_s->own_addr, - force_freq, persistent_group) - < 0) - return -1; - return ret; - } - if (wpas_p2p_start_go_neg(wpa_s, peer_addr, wps_method, - go_intent, wpa_s->own_addr, - force_freq, persistent_group) < 0) + if (wpa_s->create_p2p_iface) { + /* Prepare to add a new interface for the group */ + iftype = WPA_IF_P2P_GROUP; + if (go_intent == 15) + iftype = WPA_IF_P2P_GO; + if (wpas_p2p_add_group_interface(wpa_s, iftype) < 0) { + wpa_printf(MSG_ERROR, "P2P: Failed to allocate a new " + "interface for the group"); return -1; - return ret; - } + } - /* Prepare to add a new interface for the group */ - iftype = WPA_IF_P2P_GROUP; - if (go_intent == 15) - iftype = WPA_IF_P2P_GO; - if (wpas_p2p_add_group_interface(wpa_s, iftype) < 0) { - wpa_printf(MSG_ERROR, "P2P: Failed to allocate a new " - "interface for the group"); - return -1; - } + if_addr = wpa_s->pending_interface_addr; + } else + if_addr = wpa_s->own_addr; if (auth) { if (wpas_p2p_auth_go_neg(wpa_s, peer_addr, wps_method, - go_intent, - wpa_s->pending_interface_addr, - force_freq, persistent_group) < 0) + go_intent, if_addr, + force_freq, persistent_group, ssid, + pref_freq) < 0) return -1; return ret; } - if (wpas_p2p_start_go_neg(wpa_s, peer_addr, wps_method, go_intent, - wpa_s->pending_interface_addr, - force_freq, persistent_group) < 0) { - wpas_p2p_remove_pending_group_interface(wpa_s); + + if (wpas_p2p_start_go_neg(wpa_s, peer_addr, wps_method, + go_intent, if_addr, force_freq, + persistent_group, ssid, pref_freq) < 0) { + if (wpa_s->create_p2p_iface) + wpas_p2p_remove_pending_group_interface(wpa_s); return -1; } return ret; @@ -2995,9 +4774,6 @@ if (timeout > wpa_s->max_remain_on_chan) timeout = wpa_s->max_remain_on_chan; - if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT) - return wpa_drv_p2p_listen(wpa_s, timeout); - return p2p_listen(wpa_s->global->p2p, timeout); } @@ -3016,18 +4792,25 @@ { wpa_printf(MSG_DEBUG, "P2P: Cancel remain-on-channel callback " "(p2p_long_listen=%d ms pending_action_tx=%p)", - wpa_s->p2p_long_listen, wpa_s->pending_action_tx); + wpa_s->p2p_long_listen, offchannel_pending_action_tx(wpa_s)); + wpas_p2p_listen_work_done(wpa_s); if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) return; if (p2p_listen_end(wpa_s->global->p2p, freq) > 0) return; /* P2P module started a new operation */ - if (wpa_s->pending_action_tx) + if (offchannel_pending_action_tx(wpa_s)) return; if (wpa_s->p2p_long_listen > 0) wpa_s->p2p_long_listen -= wpa_s->max_remain_on_chan; if (wpa_s->p2p_long_listen > 0) { wpa_printf(MSG_DEBUG, "P2P: Continuing long Listen state"); wpas_p2p_listen_start(wpa_s, wpa_s->p2p_long_listen); + } else { + /* + * When listen duration is over, stop listen & update p2p_state + * to IDLE. + */ + p2p_stop_listen(wpa_s->global->p2p); } } @@ -3055,7 +4838,11 @@ while (wpa_s) { prev = wpa_s; wpa_s = wpa_s->next; - wpas_p2p_disconnect(prev); + if (prev->p2p_group_interface != + NOT_P2P_GROUP_INTERFACE || + (prev->current_ssid && + prev->current_ssid->p2p_group)) + wpas_p2p_disconnect(prev); } return 0; } @@ -3069,78 +4856,187 @@ } +static int wpas_p2p_select_go_freq(struct wpa_supplicant *wpa_s, int freq) +{ + unsigned int r; + + if (freq == 2) { + wpa_printf(MSG_DEBUG, "P2P: Request to start GO on 2.4 GHz " + "band"); + if (wpa_s->best_24_freq > 0 && + p2p_supported_freq_go(wpa_s->global->p2p, + wpa_s->best_24_freq)) { + freq = wpa_s->best_24_freq; + wpa_printf(MSG_DEBUG, "P2P: Use best 2.4 GHz band " + "channel: %d MHz", freq); + } else { + os_get_random((u8 *) &r, sizeof(r)); + freq = 2412 + (r % 3) * 25; + wpa_printf(MSG_DEBUG, "P2P: Use random 2.4 GHz band " + "channel: %d MHz", freq); + } + } + + if (freq == 5) { + wpa_printf(MSG_DEBUG, "P2P: Request to start GO on 5 GHz " + "band"); + if (wpa_s->best_5_freq > 0 && + p2p_supported_freq_go(wpa_s->global->p2p, + wpa_s->best_5_freq)) { + freq = wpa_s->best_5_freq; + wpa_printf(MSG_DEBUG, "P2P: Use best 5 GHz band " + "channel: %d MHz", freq); + } else { + os_get_random((u8 *) &r, sizeof(r)); + freq = 5180 + (r % 4) * 20; + if (!p2p_supported_freq_go(wpa_s->global->p2p, freq)) { + wpa_printf(MSG_DEBUG, "P2P: Could not select " + "5 GHz channel for P2P group"); + return -1; + } + wpa_printf(MSG_DEBUG, "P2P: Use random 5 GHz band " + "channel: %d MHz", freq); + } + } + + if (freq > 0 && !p2p_supported_freq_go(wpa_s->global->p2p, freq)) { + wpa_printf(MSG_DEBUG, "P2P: The forced channel for GO " + "(%u MHz) is not supported for P2P uses", + freq); + return -1; + } + + return freq; +} + + static int wpas_p2p_init_go_params(struct wpa_supplicant *wpa_s, struct p2p_go_neg_results *params, - int freq) + int freq, int ht40, int vht, + const struct p2p_channels *channels) { - u8 bssid[ETH_ALEN]; - int res; + int res, *freqs; + unsigned int pref_freq; + unsigned int num, i; os_memset(params, 0, sizeof(*params)); params->role_go = 1; + params->ht40 = ht40; + params->vht = vht; if (freq) { + if (!freq_included(channels, freq)) { + wpa_printf(MSG_DEBUG, "P2P: Forced GO freq %d MHz not " + "accepted", freq); + return -1; + } wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on forced " "frequency %d MHz", freq); params->freq = freq; } else if (wpa_s->conf->p2p_oper_reg_class == 81 && wpa_s->conf->p2p_oper_channel >= 1 && - wpa_s->conf->p2p_oper_channel <= 11) { + wpa_s->conf->p2p_oper_channel <= 11 && + freq_included(channels, + 2407 + 5 * wpa_s->conf->p2p_oper_channel)) { params->freq = 2407 + 5 * wpa_s->conf->p2p_oper_channel; wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on configured " "frequency %d MHz", params->freq); - } else if (wpa_s->conf->p2p_oper_reg_class == 115 || - wpa_s->conf->p2p_oper_reg_class == 124) { + } else if ((wpa_s->conf->p2p_oper_reg_class == 115 || + wpa_s->conf->p2p_oper_reg_class == 116 || + wpa_s->conf->p2p_oper_reg_class == 117 || + wpa_s->conf->p2p_oper_reg_class == 124 || + wpa_s->conf->p2p_oper_reg_class == 126 || + wpa_s->conf->p2p_oper_reg_class == 127) && + freq_included(channels, + 5000 + 5 * wpa_s->conf->p2p_oper_channel)) { params->freq = 5000 + 5 * wpa_s->conf->p2p_oper_channel; wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on configured " "frequency %d MHz", params->freq); } else if (wpa_s->conf->p2p_oper_channel == 0 && wpa_s->best_overall_freq > 0 && - p2p_supported_freq(wpa_s->global->p2p, - wpa_s->best_overall_freq)) { + p2p_supported_freq_go(wpa_s->global->p2p, + wpa_s->best_overall_freq) && + freq_included(channels, wpa_s->best_overall_freq)) { params->freq = wpa_s->best_overall_freq; wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on best overall " "channel %d MHz", params->freq); } else if (wpa_s->conf->p2p_oper_channel == 0 && wpa_s->best_24_freq > 0 && - p2p_supported_freq(wpa_s->global->p2p, - wpa_s->best_24_freq)) { + p2p_supported_freq_go(wpa_s->global->p2p, + wpa_s->best_24_freq) && + freq_included(channels, wpa_s->best_24_freq)) { params->freq = wpa_s->best_24_freq; wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on best 2.4 GHz " "channel %d MHz", params->freq); } else if (wpa_s->conf->p2p_oper_channel == 0 && wpa_s->best_5_freq > 0 && - p2p_supported_freq(wpa_s->global->p2p, - wpa_s->best_5_freq)) { + p2p_supported_freq_go(wpa_s->global->p2p, + wpa_s->best_5_freq) && + freq_included(channels, wpa_s->best_5_freq)) { params->freq = wpa_s->best_5_freq; wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on best 5 GHz " "channel %d MHz", params->freq); + } else if ((pref_freq = p2p_get_pref_freq(wpa_s->global->p2p, + channels))) { + params->freq = pref_freq; + wpa_printf(MSG_DEBUG, "P2P: Set GO freq %d MHz from preferred " + "channels", params->freq); } else { - params->freq = 2412; + int chan; + for (chan = 0; chan < 11; chan++) { + params->freq = 2412 + chan * 5; + if (!wpas_p2p_disallowed_freq(wpa_s->global, + params->freq) && + freq_included(channels, params->freq)) + break; + } + if (chan == 11) { + wpa_printf(MSG_DEBUG, "P2P: No 2.4 GHz channel " + "allowed"); + return -1; + } wpa_printf(MSG_DEBUG, "P2P: Set GO freq %d MHz (no preference " "known)", params->freq); } - if (wpa_s->current_ssid && wpa_drv_get_bssid(wpa_s, bssid) == 0 && - wpa_s->assoc_freq && !freq) { - wpa_printf(MSG_DEBUG, "P2P: Force GO on the channel we are " - "already using"); - params->freq = wpa_s->assoc_freq; - } - - res = wpa_drv_shared_freq(wpa_s); - if (res > 0 && !freq) { - wpa_printf(MSG_DEBUG, "P2P: Force GO on the channel we are " - "already using on a shared interface"); - params->freq = res; - } else if (res > 0 && freq != res && - !(wpa_s->drv_flags & - WPA_DRIVER_FLAGS_MULTI_CHANNEL_CONCURRENT)) { - wpa_printf(MSG_DEBUG, "P2P: Cannot start P2P group on %u MHz " - "while connected on another channel (%u MHz)", - freq, res); + freqs = os_calloc(wpa_s->num_multichan_concurrent, sizeof(int)); + if (!freqs) return -1; + + res = wpas_p2p_valid_oper_freqs(wpa_s, freqs, + wpa_s->num_multichan_concurrent); + if (res < 0) { + os_free(freqs); + return -1; + } + num = res; + + for (i = 0; i < num; i++) { + if (freq && freqs[i] == freq) + break; + if (!freq && freq_included(channels, freqs[i])) { + wpa_printf(MSG_DEBUG, "P2P: Force GO on a channel we are already using (%u MHz)", + freqs[i]); + params->freq = freqs[i]; + break; + } + } + + if (i == num) { + if (wpas_p2p_num_unused_channels(wpa_s) <= 0) { + if (freq) + wpa_printf(MSG_DEBUG, "P2P: Cannot force GO on freq (%u MHz) as all the channels are in use", freq); + else + wpa_printf(MSG_DEBUG, "P2P: Cannot force GO on any of the channels we are already using"); + os_free(freqs); + return -1; + } else if (num == 0) { + wpa_printf(MSG_DEBUG, "P2P: Use one of the free channels"); + } else { + wpa_printf(MSG_DEBUG, "P2P: Cannot force GO on any of the channels we are already using. Use one of the free channels"); + } } + os_free(freqs); return 0; } @@ -3151,18 +5047,30 @@ { struct wpa_supplicant *group_wpa_s; - if (!wpas_p2p_create_iface(wpa_s)) + if (!wpas_p2p_create_iface(wpa_s)) { + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Use same interface for group " + "operations"); + wpa_s->p2p_first_connection_timeout = 0; return wpa_s; + } if (wpas_p2p_add_group_interface(wpa_s, go ? WPA_IF_P2P_GO : - WPA_IF_P2P_CLIENT) < 0) + WPA_IF_P2P_CLIENT) < 0) { + wpa_msg_global(wpa_s, MSG_ERROR, + "P2P: Failed to add group interface"); return NULL; + } group_wpa_s = wpas_p2p_init_group_interface(wpa_s, go); if (group_wpa_s == NULL) { + wpa_msg_global(wpa_s, MSG_ERROR, + "P2P: Failed to initialize group interface"); wpas_p2p_remove_pending_group_interface(wpa_s); return NULL; } + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Use separate group interface %s", + group_wpa_s->ifname); + group_wpa_s->p2p_first_connection_timeout = 0; return group_wpa_s; } @@ -3172,74 +5080,36 @@ * @wpa_s: Pointer to wpa_supplicant data from wpa_supplicant_add_iface() * @persistent_group: Whether to create a persistent group * @freq: Frequency for the group or 0 to indicate no hardcoding + * @ht40: Start GO with 40 MHz channel width + * @vht: Start GO with VHT support * Returns: 0 on success, -1 on failure * * This function creates a new P2P group with the local end as the Group Owner, * i.e., without using Group Owner Negotiation. */ int wpas_p2p_group_add(struct wpa_supplicant *wpa_s, int persistent_group, - int freq) + int freq, int ht40, int vht) { struct p2p_go_neg_results params; - unsigned int r; if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) return -1; + os_free(wpa_s->global->add_psk); + wpa_s->global->add_psk = NULL; + /* Make sure we are not running find during connection establishment */ wpa_printf(MSG_DEBUG, "P2P: Stop any on-going P2P FIND"); - wpas_p2p_stop_find(wpa_s); - - if (freq == 2) { - wpa_printf(MSG_DEBUG, "P2P: Request to start GO on 2.4 GHz " - "band"); - if (wpa_s->best_24_freq > 0 && - p2p_supported_freq(wpa_s->global->p2p, - wpa_s->best_24_freq)) { - freq = wpa_s->best_24_freq; - wpa_printf(MSG_DEBUG, "P2P: Use best 2.4 GHz band " - "channel: %d MHz", freq); - } else { - os_get_random((u8 *) &r, sizeof(r)); - freq = 2412 + (r % 3) * 25; - wpa_printf(MSG_DEBUG, "P2P: Use random 2.4 GHz band " - "channel: %d MHz", freq); - } - } - - if (freq == 5) { - wpa_printf(MSG_DEBUG, "P2P: Request to start GO on 5 GHz " - "band"); - if (wpa_s->best_5_freq > 0 && - p2p_supported_freq(wpa_s->global->p2p, - wpa_s->best_5_freq)) { - freq = wpa_s->best_5_freq; - wpa_printf(MSG_DEBUG, "P2P: Use best 5 GHz band " - "channel: %d MHz", freq); - } else { - os_get_random((u8 *) &r, sizeof(r)); - freq = 5180 + (r % 4) * 20; - if (!p2p_supported_freq(wpa_s->global->p2p, freq)) { - wpa_printf(MSG_DEBUG, "P2P: Could not select " - "5 GHz channel for P2P group"); - return -1; - } - wpa_printf(MSG_DEBUG, "P2P: Use random 5 GHz band " - "channel: %d MHz", freq); - } - } + wpas_p2p_stop_find_oper(wpa_s); - if (freq > 0 && !p2p_supported_freq(wpa_s->global->p2p, freq)) { - wpa_printf(MSG_DEBUG, "P2P: The forced channel for GO " - "(%u MHz) is not supported for P2P uses", - freq); + freq = wpas_p2p_select_go_freq(wpa_s, freq); + if (freq < 0) return -1; - } - if (wpas_p2p_init_go_params(wpa_s, ¶ms, freq)) + if (wpas_p2p_init_go_params(wpa_s, ¶ms, freq, ht40, vht, NULL)) return -1; if (params.freq && - !p2p_supported_freq(wpa_s->global->p2p, params.freq)) { + !p2p_supported_freq_go(wpa_s->global->p2p, params.freq)) { wpa_printf(MSG_DEBUG, "P2P: The selected channel for GO " "(%u MHz) is not supported for P2P uses", params.freq); @@ -3265,6 +5135,7 @@ wpa_s = wpas_p2p_get_group_iface(wpa_s, addr_allocated, 0); if (wpa_s == NULL) return -1; + wpa_s->p2p_last_4way_hs_fail = NULL; wpa_supplicant_ap_deinit(wpa_s); @@ -3293,17 +5164,19 @@ if (params->passphrase) ssid->passphrase = os_strdup(params->passphrase); - wpa_supplicant_select_network(wpa_s, ssid); - wpa_s->show_group_started = 1; + wpa_supplicant_select_network(wpa_s, ssid); + return 0; } int wpas_p2p_group_add_persistent(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, int addr_allocated, - int freq) + int freq, int ht40, int vht, + const struct p2p_channels *channels, + int connection_timeout) { struct p2p_go_neg_results params; int go = 0; @@ -3318,8 +5191,13 @@ return 0; } + os_free(wpa_s->global->add_psk); + wpa_s->global->add_psk = NULL; + /* Make sure we are not running find during connection establishment */ - wpas_p2p_stop_find(wpa_s); + wpas_p2p_stop_find_oper(wpa_s); + + wpa_s->p2p_fallback_to_go_neg = 0; if (ssid->mode == WPAS_MODE_INFRA) return wpas_start_p2p_client(wpa_s, ssid, addr_allocated); @@ -3327,18 +5205,26 @@ if (ssid->mode != WPAS_MODE_P2P_GO) return -1; - if (wpas_p2p_init_go_params(wpa_s, ¶ms, freq)) + freq = wpas_p2p_select_go_freq(wpa_s, freq); + if (freq < 0) return -1; - params.role_go = 1; - if (ssid->passphrase == NULL || - os_strlen(ssid->passphrase) >= sizeof(params.passphrase)) { - wpa_printf(MSG_DEBUG, "P2P: Invalid passphrase in persistent " - "group"); + if (wpas_p2p_init_go_params(wpa_s, ¶ms, freq, ht40, vht, channels)) return -1; + + params.role_go = 1; + params.psk_set = ssid->psk_set; + if (params.psk_set) + os_memcpy(params.psk, ssid->psk, sizeof(params.psk)); + if (ssid->passphrase) { + if (os_strlen(ssid->passphrase) >= sizeof(params.passphrase)) { + wpa_printf(MSG_ERROR, "P2P: Invalid passphrase in " + "persistent group"); + return -1; + } + os_strlcpy(params.passphrase, ssid->passphrase, + sizeof(params.passphrase)); } - os_strlcpy(params.passphrase, ssid->passphrase, - sizeof(params.passphrase)); os_memcpy(params.ssid, ssid->ssid, ssid->ssid_len); params.ssid_len = ssid->ssid_len; params.persistent_group = 1; @@ -3347,6 +5233,7 @@ if (wpa_s == NULL) return -1; + wpa_s->p2p_first_connection_timeout = connection_timeout; wpas_start_wps_go(wpa_s, ¶ms, 0); return 0; @@ -3384,22 +5271,24 @@ if (!wpa_s->ap_iface) return; wpa_printf(MSG_DEBUG, "P2P: GO - group %sidle", idle ? "" : "not "); - if (idle) + if (idle) { + if (wpa_s->global->p2p_fail_on_wps_complete && + wpa_s->p2p_in_provisioning) { + wpas_p2p_grpform_fail_after_wps(wpa_s); + return; + } wpas_p2p_set_group_idle_timeout(wpa_s); - else + } else eloop_cancel_timeout(wpas_p2p_group_idle_timeout, wpa_s, NULL); } struct p2p_group * wpas_p2p_group_init(struct wpa_supplicant *wpa_s, - int persistent_group, - int group_formation) + struct wpa_ssid *ssid) { struct p2p_group *group; struct p2p_group_config *cfg; - if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT) - return NULL; if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) return NULL; @@ -3407,9 +5296,9 @@ if (cfg == NULL) return NULL; - if (persistent_group && wpa_s->conf->persistent_reconnect) + if (ssid->p2p_persistent_group && wpa_s->conf->persistent_reconnect) cfg->persistent_group = 2; - else if (persistent_group) + else if (ssid->p2p_persistent_group) cfg->persistent_group = 1; os_memcpy(cfg->interface_addr, wpa_s->own_addr, ETH_ALEN); if (wpa_s->max_stations && @@ -3417,6 +5306,9 @@ cfg->max_clients = wpa_s->max_stations; else cfg->max_clients = wpa_s->conf->max_num_sta; + os_memcpy(cfg->ssid, ssid->ssid, ssid->ssid_len); + cfg->ssid_len = ssid->ssid_len; + cfg->freq = ssid->frequency; cfg->cb_ctx = wpa_s; cfg->ie_update = wpas_p2p_ie_update; cfg->idle_update = wpas_p2p_idle_update; @@ -3424,7 +5316,7 @@ group = p2p_group_init(wpa_s->global->p2p, cfg); if (group == NULL) os_free(cfg); - if (!group_formation) + if (ssid->mode != WPAS_MODE_P2P_GROUP_FORMATION) p2p_group_notif_formation_done(group); wpa_s->p2p_group = group; return group; @@ -3453,10 +5345,39 @@ eloop_cancel_timeout(wpas_p2p_group_formation_timeout, wpa_s->parent, NULL); + wpa_s->p2p_go_group_formation_completed = 1; + if (ssid && ssid->mode == WPAS_MODE_INFRA) { + /* + * Use a separate timeout for initial data connection to + * complete to allow the group to be removed automatically if + * something goes wrong in this step before the P2P group idle + * timeout mechanism is taken into use. + */ + wpa_dbg(wpa_s, MSG_DEBUG, + "P2P: Re-start group formation timeout (%d seconds) as client for initial connection", + P2P_MAX_INITIAL_CONN_WAIT); + eloop_register_timeout(P2P_MAX_INITIAL_CONN_WAIT, 0, + wpas_p2p_group_formation_timeout, + wpa_s->parent, NULL); + } else if (ssid) { + /* + * Use a separate timeout for initial data connection to + * complete to allow the group to be removed automatically if + * the client does not complete data connection successfully. + */ + wpa_dbg(wpa_s, MSG_DEBUG, + "P2P: Re-start group formation timeout (%d seconds) as GO for initial connection", + P2P_MAX_INITIAL_CONN_WAIT_GO); + eloop_register_timeout(P2P_MAX_INITIAL_CONN_WAIT_GO, 0, + wpas_p2p_group_formation_timeout, + wpa_s->parent, NULL); + /* + * Complete group formation on first successful data connection + */ + wpa_s->p2p_go_group_formation_completed = 0; + } if (wpa_s->global->p2p) p2p_wps_success_cb(wpa_s->global->p2p, peer_addr); - else if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT) - wpa_drv_wps_success_cb(wpa_s, peer_addr); wpas_group_formation_completed(wpa_s, 1); } @@ -3477,14 +5398,42 @@ } wpas_notify_p2p_wps_failed(wpa_s, fail); + + if (wpa_s == wpa_s->global->p2p_group_formation) { + /* + * Allow some time for the failed WPS negotiation exchange to + * complete, but remove the group since group formation cannot + * succeed after provisioning failure. + */ + wpa_printf(MSG_DEBUG, "P2P: WPS step failed during group formation - reject connection from timeout"); + wpa_s->global->p2p_fail_on_wps_complete = 1; + eloop_deplete_timeout(0, 50000, + wpas_p2p_group_formation_timeout, + wpa_s->parent, NULL); + } +} + + +int wpas_p2p_wps_eapol_cb(struct wpa_supplicant *wpa_s) +{ + if (!wpa_s->global->p2p_fail_on_wps_complete || + !wpa_s->p2p_in_provisioning) + return 0; + + wpas_p2p_grpform_fail_after_wps(wpa_s); + + return 1; } int wpas_p2p_prov_disc(struct wpa_supplicant *wpa_s, const u8 *peer_addr, - const char *config_method, int join) + const char *config_method, + enum wpas_p2p_prov_disc_use use) { u16 config_methods; + wpa_s->p2p_fallback_to_go_neg = 0; + wpa_s->pending_pd_use = NORMAL_PD; if (os_strncmp(config_method, "display", 7) == 0) config_methods = WPS_CONFIG_DISPLAY; else if (os_strncmp(config_method, "keypad", 6) == 0) @@ -3497,16 +5446,29 @@ return -1; } - if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT) { - return wpa_drv_p2p_prov_disc_req(wpa_s, peer_addr, - config_methods, join); + if (use == WPAS_P2P_PD_AUTO) { + os_memcpy(wpa_s->pending_join_dev_addr, peer_addr, ETH_ALEN); + wpa_s->pending_pd_config_methods = config_methods; + wpa_s->p2p_auto_pd = 1; + wpa_s->p2p_auto_join = 0; + wpa_s->pending_pd_before_join = 0; + wpa_s->auto_pd_scan_retry = 0; + wpas_p2p_stop_find(wpa_s); + wpa_s->p2p_join_scan_count = 0; + os_get_reltime(&wpa_s->p2p_auto_started); + wpa_printf(MSG_DEBUG, "P2P: Auto PD started at %ld.%06ld", + wpa_s->p2p_auto_started.sec, + wpa_s->p2p_auto_started.usec); + wpas_p2p_join_scan(wpa_s, NULL); + return 0; } if (wpa_s->global->p2p == NULL || wpa_s->global->p2p_disabled) return -1; return p2p_prov_disc_req(wpa_s->global->p2p, peer_addr, - config_methods, join, 0); + config_methods, use == WPAS_P2P_PD_FOR_JOIN, + 0, 1); } @@ -3519,53 +5481,53 @@ static void wpas_p2p_clear_pending_action_tx(struct wpa_supplicant *wpa_s) { - if (!wpa_s->pending_action_tx) + if (!offchannel_pending_action_tx(wpa_s)) return; wpa_printf(MSG_DEBUG, "P2P: Drop pending Action TX due to new " "operation request"); - wpabuf_free(wpa_s->pending_action_tx); - wpa_s->pending_action_tx = NULL; + offchannel_clear_pending_action_tx(wpa_s); } int wpas_p2p_find(struct wpa_supplicant *wpa_s, unsigned int timeout, enum p2p_discovery_type type, unsigned int num_req_dev_types, const u8 *req_dev_types, - const u8 *dev_id) + const u8 *dev_id, unsigned int search_delay) { wpas_p2p_clear_pending_action_tx(wpa_s); wpa_s->p2p_long_listen = 0; - if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT) - return wpa_drv_p2p_find(wpa_s, timeout, type); - - if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) + if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL || + wpa_s->p2p_in_provisioning) return -1; wpa_supplicant_cancel_sched_scan(wpa_s); return p2p_find(wpa_s->global->p2p, timeout, type, - num_req_dev_types, req_dev_types, dev_id); + num_req_dev_types, req_dev_types, dev_id, + search_delay); } -void wpas_p2p_stop_find(struct wpa_supplicant *wpa_s) +static int wpas_p2p_stop_find_oper(struct wpa_supplicant *wpa_s) { wpas_p2p_clear_pending_action_tx(wpa_s); wpa_s->p2p_long_listen = 0; eloop_cancel_timeout(wpas_p2p_long_listen_timeout, wpa_s, NULL); eloop_cancel_timeout(wpas_p2p_join_scan, wpa_s, NULL); - wpa_s->p2p_cb_on_scan_complete = 0; - - if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT) { - wpa_drv_p2p_stop_find(wpa_s); - return; - } if (wpa_s->global->p2p) p2p_stop_find(wpa_s->global->p2p); + return 0; +} + + +void wpas_p2p_stop_find(struct wpa_supplicant *wpa_s) +{ + if (wpas_p2p_stop_find_oper(wpa_s) > 0) + return; wpas_p2p_remove_pending_group_interface(wpa_s); } @@ -3642,15 +5604,27 @@ int wpas_p2p_probe_req_rx(struct wpa_supplicant *wpa_s, const u8 *addr, const u8 *dst, const u8 *bssid, - const u8 *ie, size_t ie_len) + const u8 *ie, size_t ie_len, int ssi_signal) { if (wpa_s->global->p2p_disabled) return 0; if (wpa_s->global->p2p == NULL) return 0; - return p2p_probe_req_rx(wpa_s->global->p2p, addr, dst, bssid, - ie, ie_len); + switch (p2p_probe_req_rx(wpa_s->global->p2p, addr, dst, bssid, + ie, ie_len)) { + case P2P_PREQ_NOT_P2P: + wpas_notify_preq(wpa_s, addr, dst, bssid, ie, ie_len, + ssi_signal); + /* fall through */ + case P2P_PREQ_MALFORMED: + case P2P_PREQ_NOT_LISTEN: + case P2P_PREQ_NOT_PROCESSED: + default: /* make gcc happy */ + return 0; + case P2P_PREQ_PROCESSED: + return 1; + } } @@ -3695,9 +5669,6 @@ { wpa_s->p2p_long_listen = 0; - if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT) - return wpa_drv_p2p_reject(wpa_s, addr); - if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) return -1; @@ -3707,11 +5678,23 @@ /* Invite to reinvoke a persistent group */ int wpas_p2p_invite(struct wpa_supplicant *wpa_s, const u8 *peer_addr, - struct wpa_ssid *ssid, const u8 *go_dev_addr) + struct wpa_ssid *ssid, const u8 *go_dev_addr, int freq, + int ht40, int vht, int pref_freq) { enum p2p_invite_role role; u8 *bssid = NULL; + int force_freq = 0; + int res; + int no_pref_freq_given = pref_freq == 0; + + wpa_s->global->p2p_invite_group = NULL; + if (peer_addr) + os_memcpy(wpa_s->p2p_auth_invite, peer_addr, ETH_ALEN); + else + os_memset(wpa_s->p2p_auth_invite, 0, ETH_ALEN); + wpa_s->p2p_persistent_go_freq = freq; + wpa_s->p2p_go_ht40 = !!ht40; if (ssid->mode == WPAS_MODE_P2P_GO) { role = P2P_INVITE_ROLE_GO; if (peer_addr == NULL) { @@ -3736,16 +5719,26 @@ } wpa_s->pending_invite_ssid_id = ssid->id; - if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT) - return wpa_drv_p2p_invite(wpa_s, peer_addr, role, bssid, - ssid->ssid, ssid->ssid_len, - go_dev_addr, 1); + res = wpas_p2p_setup_freqs(wpa_s, freq, &force_freq, &pref_freq, + role == P2P_INVITE_ROLE_GO); + if (res) + return res; if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) return -1; + if (wpa_s->parent->conf->p2p_ignore_shared_freq && + no_pref_freq_given && pref_freq > 0 && + wpa_s->num_multichan_concurrent > 1 && + wpas_p2p_num_unused_channels(wpa_s) > 0) { + wpa_printf(MSG_DEBUG, "P2P: Ignore own channel preference %d MHz for invitation due to p2p_ignore_shared_freq=1 configuration", + pref_freq); + pref_freq = 0; + } + return p2p_invite(wpa_s->global->p2p, peer_addr, role, bssid, - ssid->ssid, ssid->ssid_len, 0, go_dev_addr, 1); + ssid->ssid, ssid->ssid_len, force_freq, go_dev_addr, + 1, pref_freq, -1); } @@ -3758,6 +5751,12 @@ u8 *bssid = NULL; struct wpa_ssid *ssid; int persistent; + int freq = 0, force_freq = 0, pref_freq = 0; + int res; + + wpa_s->p2p_persistent_go_freq = 0; + wpa_s->p2p_go_ht40 = 0; + wpa_s->p2p_go_vht = 0; for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) { if (os_strcmp(wpa_s->ifname, ifname) == 0) @@ -3775,6 +5774,7 @@ return -1; } + wpa_s->global->p2p_invite_group = wpa_s; persistent = ssid->p2p_persistent_group && wpas_p2p_get_persistent(wpa_s->parent, peer_addr, ssid->ssid, ssid->ssid_len); @@ -3784,6 +5784,7 @@ bssid = wpa_s->own_addr; if (go_dev_addr == NULL) go_dev_addr = wpa_s->global->p2p_dev_addr; + freq = ssid->frequency; } else { role = P2P_INVITE_ROLE_CLIENT; if (wpa_s->wpa_state < WPA_ASSOCIATED) { @@ -3795,20 +5796,23 @@ if (go_dev_addr == NULL && !is_zero_ether_addr(wpa_s->go_dev_addr)) go_dev_addr = wpa_s->go_dev_addr; + freq = wpa_s->current_bss ? wpa_s->current_bss->freq : + (int) wpa_s->assoc_freq; } wpa_s->parent->pending_invite_ssid_id = -1; - if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT) - return wpa_drv_p2p_invite(wpa_s, peer_addr, role, bssid, - ssid->ssid, ssid->ssid_len, - go_dev_addr, persistent); - if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) return -1; + res = wpas_p2p_setup_freqs(wpa_s, freq, &force_freq, &pref_freq, + role == P2P_INVITE_ROLE_ACTIVE_GO); + if (res) + return res; + wpas_p2p_set_own_freq_preference(wpa_s, force_freq); + return p2p_invite(wpa_s->global->p2p, peer_addr, role, bssid, - ssid->ssid, ssid->ssid_len, wpa_s->assoc_freq, - go_dev_addr, persistent); + ssid->ssid, ssid->ssid_len, force_freq, + go_dev_addr, persistent, pref_freq, -1); } @@ -3820,6 +5824,13 @@ int network_id = -1; int persistent; int freq; + u8 ip[3 * 4]; + char ip_addr[100]; + + if (ssid == NULL || ssid->mode != WPAS_MODE_P2P_GROUP_FORMATION) { + eloop_cancel_timeout(wpas_p2p_group_formation_timeout, + wpa_s->parent, NULL); + } if (!wpa_s->show_group_started || !ssid) return; @@ -3839,23 +5850,33 @@ freq = wpa_s->current_bss ? wpa_s->current_bss->freq : (int) wpa_s->assoc_freq; + + ip_addr[0] = '\0'; + if (wpa_sm_get_p2p_ip_addr(wpa_s->wpa, ip) == 0) { + os_snprintf(ip_addr, sizeof(ip_addr), " ip_addr=%u.%u.%u.%u " + "ip_mask=%u.%u.%u.%u go_ip_addr=%u.%u.%u.%u", + ip[0], ip[1], ip[2], ip[3], + ip[4], ip[5], ip[6], ip[7], + ip[8], ip[9], ip[10], ip[11]); + } + if (ssid->passphrase == NULL && ssid->psk_set) { char psk[65]; wpa_snprintf_hex(psk, sizeof(psk), ssid->psk, 32); - wpa_msg(wpa_s->parent, MSG_INFO, P2P_EVENT_GROUP_STARTED - "%s client ssid=\"%s\" freq=%d psk=%s go_dev_addr=" - MACSTR "%s", - wpa_s->ifname, ssid_txt, freq, psk, - MAC2STR(go_dev_addr), - persistent ? " [PERSISTENT]" : ""); + wpa_msg_global(wpa_s->parent, MSG_INFO, P2P_EVENT_GROUP_STARTED + "%s client ssid=\"%s\" freq=%d psk=%s " + "go_dev_addr=" MACSTR "%s%s", + wpa_s->ifname, ssid_txt, freq, psk, + MAC2STR(go_dev_addr), + persistent ? " [PERSISTENT]" : "", ip_addr); } else { - wpa_msg(wpa_s->parent, MSG_INFO, P2P_EVENT_GROUP_STARTED - "%s client ssid=\"%s\" freq=%d passphrase=\"%s\" " - "go_dev_addr=" MACSTR "%s", - wpa_s->ifname, ssid_txt, freq, - ssid->passphrase ? ssid->passphrase : "", - MAC2STR(go_dev_addr), - persistent ? " [PERSISTENT]" : ""); + wpa_msg_global(wpa_s->parent, MSG_INFO, P2P_EVENT_GROUP_STARTED + "%s client ssid=\"%s\" freq=%d " + "passphrase=\"%s\" go_dev_addr=" MACSTR "%s%s", + wpa_s->ifname, ssid_txt, freq, + ssid->passphrase ? ssid->passphrase : "", + MAC2STR(go_dev_addr), + persistent ? " [PERSISTENT]" : "", ip_addr); } if (persistent) @@ -3870,8 +5891,8 @@ int wpas_p2p_presence_req(struct wpa_supplicant *wpa_s, u32 duration1, u32 interval1, u32 duration2, u32 interval2) { - if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT) - return -1; + int ret; + if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) return -1; @@ -3880,18 +5901,19 @@ wpa_s->current_ssid->mode != WPAS_MODE_INFRA) return -1; - return p2p_presence_req(wpa_s->global->p2p, wpa_s->bssid, - wpa_s->own_addr, wpa_s->assoc_freq, - duration1, interval1, duration2, interval2); + ret = p2p_presence_req(wpa_s->global->p2p, wpa_s->bssid, + wpa_s->own_addr, wpa_s->assoc_freq, + duration1, interval1, duration2, interval2); + if (ret == 0) + wpa_s->waiting_presence_resp = 1; + + return ret; } int wpas_p2p_ext_listen(struct wpa_supplicant *wpa_s, unsigned int period, unsigned int interval) { - if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT) - return -1; - if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) return -1; @@ -3901,8 +5923,15 @@ static int wpas_p2p_is_client(struct wpa_supplicant *wpa_s) { - return wpa_s->current_ssid != NULL && - wpa_s->current_ssid->p2p_group && + if (wpa_s->current_ssid == NULL) { + /* + * current_ssid can be cleared when P2P client interface gets + * disconnected, so assume this interface was used as P2P + * client. + */ + return 1; + } + return wpa_s->current_ssid->p2p_group && wpa_s->current_ssid->mode == WPAS_MODE_INFRA; } @@ -3919,16 +5948,17 @@ wpa_printf(MSG_DEBUG, "P2P: Group idle timeout reached - terminate " "group"); - wpa_s->removal_reason = P2P_GROUP_REMOVAL_IDLE_TIMEOUT; - wpas_p2p_group_delete(wpa_s); + wpas_p2p_group_delete(wpa_s, P2P_GROUP_REMOVAL_IDLE_TIMEOUT); } static void wpas_p2p_set_group_idle_timeout(struct wpa_supplicant *wpa_s) { - unsigned int timeout; + int timeout; + + if (eloop_cancel_timeout(wpas_p2p_group_idle_timeout, wpa_s, NULL) > 0) + wpa_printf(MSG_DEBUG, "P2P: Cancelled P2P group idle timeout"); - eloop_cancel_timeout(wpas_p2p_group_idle_timeout, wpa_s, NULL); if (wpa_s->current_ssid == NULL || !wpa_s->current_ssid->p2p_group) return; @@ -3940,6 +5970,13 @@ if (timeout == 0) return; + if (timeout < 0) { + if (wpa_s->current_ssid->mode == WPAS_MODE_INFRA) + timeout = 0; /* special client mode no-timeout */ + else + return; + } + if (wpa_s->p2p_in_provisioning) { /* * Use the normal group formation timeout during the @@ -3951,6 +5988,19 @@ return; } + if (wpa_s->show_group_started) { + /* + * Use the normal group formation timeout between the end of + * the provisioning phase and completion of 4-way handshake to + * avoid terminating this process too early due to group idle + * timeout. + */ + wpa_printf(MSG_DEBUG, "P2P: Do not use P2P group idle timeout " + "while waiting for initial 4-way handshake to " + "complete"); + return; + } + wpa_printf(MSG_DEBUG, "P2P: Set P2P group idle timeout to %u seconds", timeout); eloop_register_timeout(timeout, 0, wpas_p2p_group_idle_timeout, @@ -3958,27 +6008,44 @@ } -void wpas_p2p_deauth_notif(struct wpa_supplicant *wpa_s, const u8 *bssid, - u16 reason_code, const u8 *ie, size_t ie_len) +/* Returns 1 if the interface was removed */ +int wpas_p2p_deauth_notif(struct wpa_supplicant *wpa_s, const u8 *bssid, + u16 reason_code, const u8 *ie, size_t ie_len, + int locally_generated) { if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) - return; - if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT) - return; + return 0; + + if (!locally_generated) + p2p_deauth_notif(wpa_s->global->p2p, bssid, reason_code, ie, + ie_len); + + if (reason_code == WLAN_REASON_DEAUTH_LEAVING && !locally_generated && + wpa_s->current_ssid && + wpa_s->current_ssid->p2p_group && + wpa_s->current_ssid->mode == WPAS_MODE_INFRA) { + wpa_printf(MSG_DEBUG, "P2P: GO indicated that the P2P Group " + "session is ending"); + if (wpas_p2p_group_delete(wpa_s, + P2P_GROUP_REMOVAL_GO_ENDING_SESSION) + > 0) + return 1; + } - p2p_deauth_notif(wpa_s->global->p2p, bssid, reason_code, ie, ie_len); + return 0; } void wpas_p2p_disassoc_notif(struct wpa_supplicant *wpa_s, const u8 *bssid, - u16 reason_code, const u8 *ie, size_t ie_len) + u16 reason_code, const u8 *ie, size_t ie_len, + int locally_generated) { if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) return; - if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT) - return; - p2p_disassoc_notif(wpa_s->global->p2p, bssid, reason_code, ie, ie_len); + if (!locally_generated) + p2p_disassoc_notif(wpa_s->global->p2p, bssid, reason_code, ie, + ie_len); } @@ -4101,6 +6168,11 @@ wpa_printf(MSG_ERROR, "P2P: Preferred channel list " "update failed"); } + + if (p2p_set_no_go_freq(p2p, &wpa_s->conf->p2p_no_go_freq) < 0) { + wpa_printf(MSG_ERROR, "P2P: No GO channel list " + "update failed"); + } } } @@ -4119,8 +6191,6 @@ { if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) return -1; - if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT) - return -1; wpa_s->global->cross_connection = enabled; p2p_set_cross_connect(wpa_s->global->p2p, enabled); @@ -4135,9 +6205,10 @@ iface->cross_connect_enabled = 0; iface->cross_connect_in_use = 0; - wpa_msg(iface->parent, MSG_INFO, - P2P_EVENT_CROSS_CONNECT_DISABLE "%s %s", - iface->ifname, iface->cross_connect_uplink); + wpa_msg_global(iface->parent, MSG_INFO, + P2P_EVENT_CROSS_CONNECT_DISABLE "%s %s", + iface->ifname, + iface->cross_connect_uplink); } } @@ -4164,9 +6235,9 @@ continue; iface->cross_connect_in_use = 1; - wpa_msg(iface->parent, MSG_INFO, - P2P_EVENT_CROSS_CONNECT_ENABLE "%s %s", - iface->ifname, iface->cross_connect_uplink); + wpa_msg_global(iface->parent, MSG_INFO, + P2P_EVENT_CROSS_CONNECT_ENABLE "%s %s", + iface->ifname, iface->cross_connect_uplink); } } @@ -4184,9 +6255,9 @@ if (!iface->cross_connect_in_use) continue; - wpa_msg(iface->parent, MSG_INFO, - P2P_EVENT_CROSS_CONNECT_DISABLE "%s %s", - iface->ifname, iface->cross_connect_uplink); + wpa_msg_global(iface->parent, MSG_INFO, + P2P_EVENT_CROSS_CONNECT_DISABLE "%s %s", + iface->ifname, iface->cross_connect_uplink); iface->cross_connect_in_use = 0; } } @@ -4200,8 +6271,9 @@ wpas_p2p_disable_cross_connect(wpa_s); else wpas_p2p_enable_cross_connect(wpa_s); - if (!wpa_s->ap_iface) - eloop_cancel_timeout(wpas_p2p_group_idle_timeout, wpa_s, NULL); + if (!wpa_s->ap_iface && + eloop_cancel_timeout(wpas_p2p_group_idle_timeout, wpa_s, NULL) > 0) + wpa_printf(MSG_DEBUG, "P2P: Cancelled P2P group idle timeout"); } @@ -4245,9 +6317,9 @@ break; wpa_s->cross_connect_in_use = 1; - wpa_msg(wpa_s->parent, MSG_INFO, - P2P_EVENT_CROSS_CONNECT_ENABLE "%s %s", - wpa_s->ifname, wpa_s->cross_connect_uplink); + wpa_msg_global(wpa_s->parent, MSG_INFO, + P2P_EVENT_CROSS_CONNECT_ENABLE "%s %s", + wpa_s->ifname, wpa_s->cross_connect_uplink); break; } } @@ -4263,33 +6335,34 @@ "session overlap"); if (wpa_s != wpa_s->parent) wpa_msg_ctrl(wpa_s->parent, MSG_INFO, WPS_EVENT_OVERLAP); - - if (wpa_s->global->p2p) - p2p_group_formation_failed(wpa_s->global->p2p); - - eloop_cancel_timeout(wpas_p2p_group_formation_timeout, - wpa_s->parent, NULL); - - wpas_group_formation_completed(wpa_s, 0); + wpas_p2p_group_formation_failed(wpa_s); return 1; } void wpas_p2p_update_channel_list(struct wpa_supplicant *wpa_s) { - struct p2p_channels chan; + struct p2p_channels chan, cli_chan; if (wpa_s->global == NULL || wpa_s->global->p2p == NULL) return; os_memset(&chan, 0, sizeof(chan)); - if (wpas_p2p_setup_channels(wpa_s, &chan)) { + os_memset(&cli_chan, 0, sizeof(cli_chan)); + if (wpas_p2p_setup_channels(wpa_s, &chan, &cli_chan)) { wpa_printf(MSG_ERROR, "P2P: Failed to update supported " "channel list"); return; } - p2p_update_channel_list(wpa_s->global->p2p, &chan); + p2p_update_channel_list(wpa_s->global->p2p, &chan, &cli_chan); +} + + +static void wpas_p2p_scan_res_ignore(struct wpa_supplicant *wpa_s, + struct wpa_scan_results *scan_res) +{ + wpa_printf(MSG_DEBUG, "P2P: Ignore scan results"); } @@ -4316,6 +6389,18 @@ found = 1; } + if (wpa_s->scan_res_handler == wpas_p2p_scan_res_join) { + wpa_printf(MSG_DEBUG, "P2P: Stop pending scan for join"); + wpa_s->scan_res_handler = wpas_p2p_scan_res_ignore; + found = 1; + } + + if (wpa_s->pending_pd_before_join) { + wpa_printf(MSG_DEBUG, "P2P: Stop pending PD before join"); + wpa_s->pending_pd_before_join = 0; + found = 1; + } + wpas_p2p_stop_find(wpa_s); for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) { @@ -4329,7 +6414,12 @@ found = 1; eloop_cancel_timeout(wpas_p2p_group_formation_timeout, wpa_s->parent, NULL); - wpas_p2p_group_delete(wpa_s); + if (wpa_s->p2p_in_provisioning) { + wpas_group_formation_completed(wpa_s, 0); + break; + } + wpas_p2p_group_delete(wpa_s, + P2P_GROUP_REMOVAL_REQUESTED); break; } } @@ -4350,8 +6440,7 @@ wpa_printf(MSG_DEBUG, "P2P: Remove group due to driver resource not " "being available anymore"); - wpa_s->removal_reason = P2P_GROUP_REMOVAL_UNAVAILABLE; - wpas_p2p_group_delete(wpa_s); + wpas_p2p_group_delete(wpa_s, P2P_GROUP_REMOVAL_UNAVAILABLE); } @@ -4359,7 +6448,7 @@ int freq_24, int freq_5, int freq_overall) { struct p2p_data *p2p = wpa_s->global->p2p; - if (p2p == NULL || (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)) + if (p2p == NULL) return; p2p_set_best_channels(p2p, freq_24, freq_5, freq_overall); } @@ -4370,7 +6459,7 @@ u8 peer[ETH_ALEN]; struct p2p_data *p2p = wpa_s->global->p2p; - if (p2p == NULL || (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)) + if (p2p == NULL) return -1; if (hwaddr_aton(addr, peer)) @@ -4397,19 +6486,49 @@ if (wpa_s == NULL) return -1; - wpa_s->removal_reason = P2P_GROUP_REMOVAL_REQUESTED; - wpas_p2p_group_delete(wpa_s); - - return 0; + return wpas_p2p_group_delete(wpa_s, P2P_GROUP_REMOVAL_REQUESTED) < 0 ? + -1 : 0; } int wpas_p2p_in_progress(struct wpa_supplicant *wpa_s) { + int ret; + if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) return 0; - return p2p_in_progress(wpa_s->global->p2p); + ret = p2p_in_progress(wpa_s->global->p2p); + if (ret == 0) { + /* + * Check whether there is an ongoing WPS provisioning step (or + * other parts of group formation) on another interface since + * p2p_in_progress() does not report this to avoid issues for + * scans during such provisioning step. + */ + if (wpa_s->global->p2p_group_formation && + wpa_s->global->p2p_group_formation != wpa_s) { + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Another interface (%s) " + "in group formation", + wpa_s->global->p2p_group_formation->ifname); + ret = 1; + } + } + + if (!ret && wpa_s->global->p2p_go_wait_client.sec) { + struct os_reltime now; + os_get_reltime(&now); + if (os_reltime_expired(&now, &wpa_s->global->p2p_go_wait_client, + P2P_MAX_INITIAL_CONN_WAIT_GO)) { + /* Wait for the first client has expired */ + wpa_s->global->p2p_go_wait_client.sec = 0; + } else { + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Waiting for initial client connection during group formation"); + ret = 1; + } + } + + return ret; } @@ -4449,6 +6568,11 @@ (ssid_len != s->ssid_len || os_memcmp(ssid, s->ssid, ssid_len) != 0)) continue; + if (addr == NULL) { + if (s->mode == WPAS_MODE_P2P_GO) + return s; + continue; + } if (os_memcmp(s->bssid, addr, ETH_ALEN) == 0) return s; /* peer is GO in the persistent group */ if (s->mode != WPAS_MODE_P2P_GO || s->p2p_client_list == NULL) @@ -4468,7 +6592,986 @@ void wpas_p2p_notify_ap_sta_authorized(struct wpa_supplicant *wpa_s, const u8 *addr) { + if (eloop_cancel_timeout(wpas_p2p_group_formation_timeout, + wpa_s->parent, NULL) > 0) { + /* + * This can happen if WPS provisioning step is not terminated + * cleanly (e.g., P2P Client does not send WSC_Done). Since the + * peer was able to connect, there is no need to time out group + * formation after this, though. In addition, this is used with + * the initial connection wait on the GO as a separate formation + * timeout and as such, expected to be hit after the initial WPS + * provisioning step. + */ + wpa_printf(MSG_DEBUG, "P2P: Canceled P2P group formation timeout on data connection"); + } + if (!wpa_s->p2p_go_group_formation_completed) { + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Marking group formation completed on GO on first data connection"); + wpa_s->p2p_go_group_formation_completed = 1; + wpa_s->global->p2p_group_formation = NULL; + wpa_s->p2p_in_provisioning = 0; + } + wpa_s->global->p2p_go_wait_client.sec = 0; if (addr == NULL) return; wpas_p2p_add_persistent_group_client(wpa_s, addr); } + + +static void wpas_p2p_fallback_to_go_neg(struct wpa_supplicant *wpa_s, + int group_added) +{ + struct wpa_supplicant *group = wpa_s; + if (wpa_s->global->p2p_group_formation) + group = wpa_s->global->p2p_group_formation; + wpa_s = wpa_s->parent; + offchannel_send_action_done(wpa_s); + if (group_added) + wpas_p2p_group_delete(group, P2P_GROUP_REMOVAL_SILENT); + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Fall back to GO Negotiation"); + wpas_p2p_connect(wpa_s, wpa_s->pending_join_dev_addr, wpa_s->p2p_pin, + wpa_s->p2p_wps_method, wpa_s->p2p_persistent_group, 0, + 0, 0, wpa_s->p2p_go_intent, wpa_s->p2p_connect_freq, + wpa_s->p2p_persistent_id, + wpa_s->p2p_pd_before_go_neg, + wpa_s->p2p_go_ht40, + wpa_s->p2p_go_vht); +} + + +int wpas_p2p_scan_no_go_seen(struct wpa_supplicant *wpa_s) +{ + if (!wpa_s->p2p_fallback_to_go_neg || + wpa_s->p2p_in_provisioning <= 5) + return 0; + + if (wpas_p2p_peer_go(wpa_s, wpa_s->pending_join_dev_addr) > 0) + return 0; /* peer operating as a GO */ + + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: GO not found for p2p_connect-auto - " + "fallback to GO Negotiation"); + wpas_p2p_fallback_to_go_neg(wpa_s, 1); + + return 1; +} + + +unsigned int wpas_p2p_search_delay(struct wpa_supplicant *wpa_s) +{ + struct wpa_supplicant *ifs; + + if (wpa_s->wpa_state > WPA_SCANNING) { + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Use %u ms search delay due to " + "concurrent operation", + P2P_CONCURRENT_SEARCH_DELAY); + return P2P_CONCURRENT_SEARCH_DELAY; + } + + dl_list_for_each(ifs, &wpa_s->radio->ifaces, struct wpa_supplicant, + radio_list) { + if (ifs != wpa_s && ifs->wpa_state > WPA_SCANNING) { + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Use %u ms search " + "delay due to concurrent operation on " + "interface %s", + P2P_CONCURRENT_SEARCH_DELAY, ifs->ifname); + return P2P_CONCURRENT_SEARCH_DELAY; + } + } + + return 0; +} + + +static int wpas_p2p_remove_psk_entry(struct wpa_supplicant *wpa_s, + struct wpa_ssid *s, const u8 *addr, + int iface_addr) +{ + struct psk_list_entry *psk, *tmp; + int changed = 0; + + dl_list_for_each_safe(psk, tmp, &s->psk_list, struct psk_list_entry, + list) { + if ((iface_addr && !psk->p2p && + os_memcmp(addr, psk->addr, ETH_ALEN) == 0) || + (!iface_addr && psk->p2p && + os_memcmp(addr, psk->addr, ETH_ALEN) == 0)) { + wpa_dbg(wpa_s, MSG_DEBUG, + "P2P: Remove persistent group PSK list entry for " + MACSTR " p2p=%u", + MAC2STR(psk->addr), psk->p2p); + dl_list_del(&psk->list); + os_free(psk); + changed++; + } + } + + return changed; +} + + +void wpas_p2p_new_psk_cb(struct wpa_supplicant *wpa_s, const u8 *mac_addr, + const u8 *p2p_dev_addr, + const u8 *psk, size_t psk_len) +{ + struct wpa_ssid *ssid = wpa_s->current_ssid; + struct wpa_ssid *persistent; + struct psk_list_entry *p; + + if (psk_len != sizeof(p->psk)) + return; + + if (p2p_dev_addr) { + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: New PSK for addr=" MACSTR + " p2p_dev_addr=" MACSTR, + MAC2STR(mac_addr), MAC2STR(p2p_dev_addr)); + if (is_zero_ether_addr(p2p_dev_addr)) + p2p_dev_addr = NULL; + } else { + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: New PSK for addr=" MACSTR, + MAC2STR(mac_addr)); + } + + if (ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION) { + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: new_psk_cb during group formation"); + /* To be added to persistent group once created */ + if (wpa_s->global->add_psk == NULL) { + wpa_s->global->add_psk = os_zalloc(sizeof(*p)); + if (wpa_s->global->add_psk == NULL) + return; + } + p = wpa_s->global->add_psk; + if (p2p_dev_addr) { + p->p2p = 1; + os_memcpy(p->addr, p2p_dev_addr, ETH_ALEN); + } else { + p->p2p = 0; + os_memcpy(p->addr, mac_addr, ETH_ALEN); + } + os_memcpy(p->psk, psk, psk_len); + return; + } + + if (ssid->mode != WPAS_MODE_P2P_GO || !ssid->p2p_persistent_group) { + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Ignore new_psk_cb on not-persistent GO"); + return; + } + + persistent = wpas_p2p_get_persistent(wpa_s->parent, NULL, ssid->ssid, + ssid->ssid_len); + if (!persistent) { + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Could not find persistent group information to store the new PSK"); + return; + } + + p = os_zalloc(sizeof(*p)); + if (p == NULL) + return; + if (p2p_dev_addr) { + p->p2p = 1; + os_memcpy(p->addr, p2p_dev_addr, ETH_ALEN); + } else { + p->p2p = 0; + os_memcpy(p->addr, mac_addr, ETH_ALEN); + } + os_memcpy(p->psk, psk, psk_len); + + if (dl_list_len(&persistent->psk_list) > P2P_MAX_STORED_CLIENTS) { + struct psk_list_entry *last; + last = dl_list_last(&persistent->psk_list, + struct psk_list_entry, list); + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Remove oldest PSK entry for " + MACSTR " (p2p=%u) to make room for a new one", + MAC2STR(last->addr), last->p2p); + dl_list_del(&last->list); + os_free(last); + } + + wpas_p2p_remove_psk_entry(wpa_s->parent, persistent, + p2p_dev_addr ? p2p_dev_addr : mac_addr, + p2p_dev_addr == NULL); + if (p2p_dev_addr) { + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Add new PSK for p2p_dev_addr=" + MACSTR, MAC2STR(p2p_dev_addr)); + } else { + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Add new PSK for addr=" MACSTR, + MAC2STR(mac_addr)); + } + dl_list_add(&persistent->psk_list, &p->list); + +#ifndef CONFIG_NO_CONFIG_WRITE + if (wpa_s->parent->conf->update_config && + wpa_config_write(wpa_s->parent->confname, wpa_s->parent->conf)) + wpa_printf(MSG_DEBUG, "P2P: Failed to update configuration"); +#endif /* CONFIG_NO_CONFIG_WRITE */ +} + + +static void wpas_p2p_remove_psk(struct wpa_supplicant *wpa_s, + struct wpa_ssid *s, const u8 *addr, + int iface_addr) +{ + int res; + + res = wpas_p2p_remove_psk_entry(wpa_s, s, addr, iface_addr); + if (res > 0) { +#ifndef CONFIG_NO_CONFIG_WRITE + if (wpa_s->conf->update_config && + wpa_config_write(wpa_s->confname, wpa_s->conf)) + wpa_dbg(wpa_s, MSG_DEBUG, + "P2P: Failed to update configuration"); +#endif /* CONFIG_NO_CONFIG_WRITE */ + } +} + + +static void wpas_p2p_remove_client_go(struct wpa_supplicant *wpa_s, + const u8 *peer, int iface_addr) +{ + struct hostapd_data *hapd; + struct hostapd_wpa_psk *psk, *prev, *rem; + struct sta_info *sta; + + if (wpa_s->ap_iface == NULL || wpa_s->current_ssid == NULL || + wpa_s->current_ssid->mode != WPAS_MODE_P2P_GO) + return; + + /* Remove per-station PSK entry */ + hapd = wpa_s->ap_iface->bss[0]; + prev = NULL; + psk = hapd->conf->ssid.wpa_psk; + while (psk) { + if ((iface_addr && os_memcmp(peer, psk->addr, ETH_ALEN) == 0) || + (!iface_addr && + os_memcmp(peer, psk->p2p_dev_addr, ETH_ALEN) == 0)) { + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Remove operating group PSK entry for " + MACSTR " iface_addr=%d", + MAC2STR(peer), iface_addr); + if (prev) + prev->next = psk->next; + else + hapd->conf->ssid.wpa_psk = psk->next; + rem = psk; + psk = psk->next; + os_free(rem); + } else { + prev = psk; + psk = psk->next; + } + } + + /* Disconnect from group */ + if (iface_addr) + sta = ap_get_sta(hapd, peer); + else + sta = ap_get_sta_p2p(hapd, peer); + if (sta) { + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Disconnect peer " MACSTR + " (iface_addr=%d) from group", + MAC2STR(peer), iface_addr); + hostapd_drv_sta_deauth(hapd, sta->addr, + WLAN_REASON_DEAUTH_LEAVING); + ap_sta_deauthenticate(hapd, sta, WLAN_REASON_DEAUTH_LEAVING); + } +} + + +void wpas_p2p_remove_client(struct wpa_supplicant *wpa_s, const u8 *peer, + int iface_addr) +{ + struct wpa_ssid *s; + struct wpa_supplicant *w; + + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Remove client " MACSTR, MAC2STR(peer)); + + /* Remove from any persistent group */ + for (s = wpa_s->parent->conf->ssid; s; s = s->next) { + if (s->disabled != 2 || s->mode != WPAS_MODE_P2P_GO) + continue; + if (!iface_addr) + wpas_remove_persistent_peer(wpa_s, s, peer, 0); + wpas_p2p_remove_psk(wpa_s->parent, s, peer, iface_addr); + } + + /* Remove from any operating group */ + for (w = wpa_s->global->ifaces; w; w = w->next) + wpas_p2p_remove_client_go(w, peer, iface_addr); +} + + +static void wpas_p2p_psk_failure_removal(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + wpas_p2p_group_delete(wpa_s, P2P_GROUP_REMOVAL_PSK_FAILURE); +} + + +static void wpas_p2p_group_freq_conflict(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + + wpa_printf(MSG_DEBUG, "P2P: Frequency conflict - terminate group"); + wpas_p2p_group_delete(wpa_s, P2P_GROUP_REMOVAL_FREQ_CONFLICT); +} + + +int wpas_p2p_handle_frequency_conflicts(struct wpa_supplicant *wpa_s, int freq, + struct wpa_ssid *ssid) +{ + struct wpa_supplicant *iface; + + for (iface = wpa_s->global->ifaces; iface; iface = iface->next) { + if (!iface->current_ssid || + iface->current_ssid->frequency == freq || + (iface->p2p_group_interface == NOT_P2P_GROUP_INTERFACE && + !iface->current_ssid->p2p_group)) + continue; + + /* Remove the connection with least priority */ + if (!wpas_is_p2p_prioritized(iface)) { + /* STA connection has priority over existing + * P2P connection, so remove the interface. */ + wpa_printf(MSG_DEBUG, "P2P: Removing P2P connection due to single channel concurrent mode frequency conflict"); + eloop_register_timeout(0, 0, + wpas_p2p_group_freq_conflict, + iface, NULL); + /* If connection in progress is P2P connection, do not + * proceed for the connection. */ + if (wpa_s == iface) + return -1; + else + return 0; + } else { + /* P2P connection has priority, disable the STA network + */ + wpa_supplicant_disable_network(wpa_s->global->ifaces, + ssid); + wpa_msg(wpa_s->global->ifaces, MSG_INFO, + WPA_EVENT_FREQ_CONFLICT " id=%d", ssid->id); + os_memset(wpa_s->global->ifaces->pending_bssid, 0, + ETH_ALEN); + /* If P2P connection is in progress, continue + * connecting...*/ + if (wpa_s == iface) + return 0; + else + return -1; + } + } + + return 0; +} + + +int wpas_p2p_4way_hs_failed(struct wpa_supplicant *wpa_s) +{ + struct wpa_ssid *ssid = wpa_s->current_ssid; + + if (ssid == NULL || !ssid->p2p_group) + return 0; + + if (wpa_s->p2p_last_4way_hs_fail && + wpa_s->p2p_last_4way_hs_fail == ssid) { + u8 go_dev_addr[ETH_ALEN]; + struct wpa_ssid *persistent; + + if (wpas_p2p_persistent_group(wpa_s, go_dev_addr, + ssid->ssid, + ssid->ssid_len) <= 0) { + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Could not determine whether 4-way handshake failures were for a persistent group"); + goto disconnect; + } + + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Two 4-way handshake failures for a P2P group - go_dev_addr=" + MACSTR, MAC2STR(go_dev_addr)); + persistent = wpas_p2p_get_persistent(wpa_s->parent, go_dev_addr, + ssid->ssid, + ssid->ssid_len); + if (persistent == NULL || persistent->mode != WPAS_MODE_INFRA) { + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: No matching persistent group stored"); + goto disconnect; + } + wpa_msg_global(wpa_s->parent, MSG_INFO, + P2P_EVENT_PERSISTENT_PSK_FAIL "%d", + persistent->id); + disconnect: + wpa_s->p2p_last_4way_hs_fail = NULL; + /* + * Remove the group from a timeout to avoid issues with caller + * continuing to use the interface if this is on a P2P group + * interface. + */ + eloop_register_timeout(0, 0, wpas_p2p_psk_failure_removal, + wpa_s, NULL); + return 1; + } + + wpa_s->p2p_last_4way_hs_fail = ssid; + return 0; +} + + +#ifdef CONFIG_WPS_NFC + +static struct wpabuf * wpas_p2p_nfc_handover(int ndef, struct wpabuf *wsc, + struct wpabuf *p2p) +{ + struct wpabuf *ret; + size_t wsc_len; + + if (p2p == NULL) { + wpabuf_free(wsc); + wpa_printf(MSG_DEBUG, "P2P: No p2p buffer for handover"); + return NULL; + } + + wsc_len = wsc ? wpabuf_len(wsc) : 0; + ret = wpabuf_alloc(2 + wsc_len + 2 + wpabuf_len(p2p)); + if (ret == NULL) { + wpabuf_free(wsc); + wpabuf_free(p2p); + return NULL; + } + + wpabuf_put_be16(ret, wsc_len); + if (wsc) + wpabuf_put_buf(ret, wsc); + wpabuf_put_be16(ret, wpabuf_len(p2p)); + wpabuf_put_buf(ret, p2p); + + wpabuf_free(wsc); + wpabuf_free(p2p); + wpa_hexdump_buf(MSG_DEBUG, + "P2P: Generated NFC connection handover message", ret); + + if (ndef && ret) { + struct wpabuf *tmp; + tmp = ndef_build_p2p(ret); + wpabuf_free(ret); + if (tmp == NULL) { + wpa_printf(MSG_DEBUG, "P2P: Failed to NDEF encapsulate handover request"); + return NULL; + } + ret = tmp; + } + + return ret; +} + + +static int wpas_p2p_cli_freq(struct wpa_supplicant *wpa_s, + struct wpa_ssid **ssid, u8 *go_dev_addr) +{ + struct wpa_supplicant *iface; + + if (go_dev_addr) + os_memset(go_dev_addr, 0, ETH_ALEN); + if (ssid) + *ssid = NULL; + for (iface = wpa_s->global->ifaces; iface; iface = iface->next) { + if (iface->wpa_state < WPA_ASSOCIATING || + iface->current_ssid == NULL || iface->assoc_freq == 0 || + !iface->current_ssid->p2p_group || + iface->current_ssid->mode != WPAS_MODE_INFRA) + continue; + if (ssid) + *ssid = iface->current_ssid; + if (go_dev_addr) + os_memcpy(go_dev_addr, iface->go_dev_addr, ETH_ALEN); + return iface->assoc_freq; + } + return 0; +} + + +struct wpabuf * wpas_p2p_nfc_handover_req(struct wpa_supplicant *wpa_s, + int ndef) +{ + struct wpabuf *wsc, *p2p; + struct wpa_ssid *ssid; + u8 go_dev_addr[ETH_ALEN]; + int cli_freq = wpas_p2p_cli_freq(wpa_s, &ssid, go_dev_addr); + + if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) { + wpa_printf(MSG_DEBUG, "P2P: P2P disabled - cannot build handover request"); + return NULL; + } + + if (wpa_s->conf->wps_nfc_dh_pubkey == NULL && + wps_nfc_gen_dh(&wpa_s->conf->wps_nfc_dh_pubkey, + &wpa_s->conf->wps_nfc_dh_privkey) < 0) { + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: No DH key available for handover request"); + return NULL; + } + + if (cli_freq == 0) { + wsc = wps_build_nfc_handover_req_p2p( + wpa_s->parent->wps, wpa_s->conf->wps_nfc_dh_pubkey); + } else + wsc = NULL; + p2p = p2p_build_nfc_handover_req(wpa_s->global->p2p, cli_freq, + go_dev_addr, ssid ? ssid->ssid : NULL, + ssid ? ssid->ssid_len : 0); + + return wpas_p2p_nfc_handover(ndef, wsc, p2p); +} + + +struct wpabuf * wpas_p2p_nfc_handover_sel(struct wpa_supplicant *wpa_s, + int ndef, int tag) +{ + struct wpabuf *wsc, *p2p; + struct wpa_ssid *ssid; + u8 go_dev_addr[ETH_ALEN]; + int cli_freq = wpas_p2p_cli_freq(wpa_s, &ssid, go_dev_addr); + + if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) + return NULL; + + if (!tag && wpa_s->conf->wps_nfc_dh_pubkey == NULL && + wps_nfc_gen_dh(&wpa_s->conf->wps_nfc_dh_pubkey, + &wpa_s->conf->wps_nfc_dh_privkey) < 0) + return NULL; + + if (cli_freq == 0) { + wsc = wps_build_nfc_handover_sel_p2p( + wpa_s->parent->wps, + tag ? wpa_s->conf->wps_nfc_dev_pw_id : + DEV_PW_NFC_CONNECTION_HANDOVER, + wpa_s->conf->wps_nfc_dh_pubkey, + tag ? wpa_s->conf->wps_nfc_dev_pw : NULL); + } else + wsc = NULL; + p2p = p2p_build_nfc_handover_sel(wpa_s->global->p2p, cli_freq, + go_dev_addr, ssid ? ssid->ssid : NULL, + ssid ? ssid->ssid_len : 0); + + return wpas_p2p_nfc_handover(ndef, wsc, p2p); +} + + +static int wpas_p2p_nfc_join_group(struct wpa_supplicant *wpa_s, + struct p2p_nfc_params *params) +{ + wpa_printf(MSG_DEBUG, "P2P: Initiate join-group based on NFC " + "connection handover (freq=%d)", + params->go_freq); + + if (params->go_freq && params->go_ssid_len) { + wpa_s->p2p_wps_method = WPS_NFC; + wpa_s->pending_join_wps_method = WPS_NFC; + os_memset(wpa_s->pending_join_iface_addr, 0, ETH_ALEN); + os_memcpy(wpa_s->pending_join_dev_addr, params->go_dev_addr, + ETH_ALEN); + return wpas_p2p_join_start(wpa_s, params->go_freq, + params->go_ssid, + params->go_ssid_len); + } + + return wpas_p2p_connect(wpa_s, params->peer->p2p_device_addr, NULL, + WPS_NFC, 0, 0, 1, 0, wpa_s->conf->p2p_go_intent, + params->go_freq, -1, 0, 1, 1); +} + + +static int wpas_p2p_nfc_auth_join(struct wpa_supplicant *wpa_s, + struct p2p_nfc_params *params, int tag) +{ + int res, persistent; + struct wpa_ssid *ssid; + + wpa_printf(MSG_DEBUG, "P2P: Authorize join-group based on NFC " + "connection handover"); + for (wpa_s = wpa_s->global->ifaces; wpa_s; wpa_s = wpa_s->next) { + ssid = wpa_s->current_ssid; + if (ssid == NULL) + continue; + if (ssid->mode != WPAS_MODE_P2P_GO) + continue; + if (wpa_s->ap_iface == NULL) + continue; + break; + } + if (wpa_s == NULL) { + wpa_printf(MSG_DEBUG, "P2P: Could not find GO interface"); + return -1; + } + + if (wpa_s->parent->p2p_oob_dev_pw_id != + DEV_PW_NFC_CONNECTION_HANDOVER && + !wpa_s->parent->p2p_oob_dev_pw) { + wpa_printf(MSG_DEBUG, "P2P: No NFC Dev Pw known"); + return -1; + } + res = wpas_ap_wps_add_nfc_pw( + wpa_s, wpa_s->parent->p2p_oob_dev_pw_id, + wpa_s->parent->p2p_oob_dev_pw, + wpa_s->parent->p2p_peer_oob_pk_hash_known ? + wpa_s->parent->p2p_peer_oob_pubkey_hash : NULL); + if (res) + return res; + + if (!tag) { + wpa_printf(MSG_DEBUG, "P2P: Negotiated handover - wait for peer to join without invitation"); + return 0; + } + + if (!params->peer || + !(params->peer->dev_capab & P2P_DEV_CAPAB_INVITATION_PROCEDURE)) + return 0; + + wpa_printf(MSG_DEBUG, "P2P: Static handover - invite peer " MACSTR + " to join", MAC2STR(params->peer->p2p_device_addr)); + + wpa_s->global->p2p_invite_group = wpa_s; + persistent = ssid->p2p_persistent_group && + wpas_p2p_get_persistent(wpa_s->parent, + params->peer->p2p_device_addr, + ssid->ssid, ssid->ssid_len); + wpa_s->parent->pending_invite_ssid_id = -1; + + return p2p_invite(wpa_s->global->p2p, params->peer->p2p_device_addr, + P2P_INVITE_ROLE_ACTIVE_GO, wpa_s->own_addr, + ssid->ssid, ssid->ssid_len, ssid->frequency, + wpa_s->global->p2p_dev_addr, persistent, 0, + wpa_s->parent->p2p_oob_dev_pw_id); +} + + +static int wpas_p2p_nfc_init_go_neg(struct wpa_supplicant *wpa_s, + struct p2p_nfc_params *params, + int forced_freq) +{ + wpa_printf(MSG_DEBUG, "P2P: Initiate GO Negotiation based on NFC " + "connection handover"); + return wpas_p2p_connect(wpa_s, params->peer->p2p_device_addr, NULL, + WPS_NFC, 0, 0, 0, 0, wpa_s->conf->p2p_go_intent, + forced_freq, -1, 0, 1, 1); +} + + +static int wpas_p2p_nfc_resp_go_neg(struct wpa_supplicant *wpa_s, + struct p2p_nfc_params *params, + int forced_freq) +{ + int res; + + wpa_printf(MSG_DEBUG, "P2P: Authorize GO Negotiation based on NFC " + "connection handover"); + res = wpas_p2p_connect(wpa_s, params->peer->p2p_device_addr, NULL, + WPS_NFC, 0, 0, 0, 1, wpa_s->conf->p2p_go_intent, + forced_freq, -1, 0, 1, 1); + if (res) + return res; + + res = wpas_p2p_listen(wpa_s, 60); + if (res) { + p2p_unauthorize(wpa_s->global->p2p, + params->peer->p2p_device_addr); + } + + return res; +} + + +static int wpas_p2p_nfc_connection_handover(struct wpa_supplicant *wpa_s, + const struct wpabuf *data, + int sel, int tag, int forced_freq) +{ + const u8 *pos, *end; + u16 len, id; + struct p2p_nfc_params params; + int res; + + os_memset(¶ms, 0, sizeof(params)); + params.sel = sel; + + wpa_hexdump_buf(MSG_DEBUG, "P2P: Received NFC tag payload", data); + + pos = wpabuf_head(data); + end = pos + wpabuf_len(data); + + if (end - pos < 2) { + wpa_printf(MSG_DEBUG, "P2P: Not enough data for Length of WSC " + "attributes"); + return -1; + } + len = WPA_GET_BE16(pos); + pos += 2; + if (pos + len > end) { + wpa_printf(MSG_DEBUG, "P2P: Not enough data for WSC " + "attributes"); + return -1; + } + params.wsc_attr = pos; + params.wsc_len = len; + pos += len; + + if (end - pos < 2) { + wpa_printf(MSG_DEBUG, "P2P: Not enough data for Length of P2P " + "attributes"); + return -1; + } + len = WPA_GET_BE16(pos); + pos += 2; + if (pos + len > end) { + wpa_printf(MSG_DEBUG, "P2P: Not enough data for P2P " + "attributes"); + return -1; + } + params.p2p_attr = pos; + params.p2p_len = len; + pos += len; + + wpa_hexdump(MSG_DEBUG, "P2P: WSC attributes", + params.wsc_attr, params.wsc_len); + wpa_hexdump(MSG_DEBUG, "P2P: P2P attributes", + params.p2p_attr, params.p2p_len); + if (pos < end) { + wpa_hexdump(MSG_DEBUG, + "P2P: Ignored extra data after P2P attributes", + pos, end - pos); + } + + res = p2p_process_nfc_connection_handover(wpa_s->global->p2p, ¶ms); + if (res) + return res; + + if (params.next_step == NO_ACTION) + return 0; + + if (params.next_step == BOTH_GO) { + wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_NFC_BOTH_GO "peer=" MACSTR, + MAC2STR(params.peer->p2p_device_addr)); + return 0; + } + + if (params.next_step == PEER_CLIENT) { + if (!is_zero_ether_addr(params.go_dev_addr)) { + wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_NFC_PEER_CLIENT + "peer=" MACSTR " freq=%d go_dev_addr=" MACSTR + " ssid=\"%s\"", + MAC2STR(params.peer->p2p_device_addr), + params.go_freq, + MAC2STR(params.go_dev_addr), + wpa_ssid_txt(params.go_ssid, + params.go_ssid_len)); + } else { + wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_NFC_PEER_CLIENT + "peer=" MACSTR " freq=%d", + MAC2STR(params.peer->p2p_device_addr), + params.go_freq); + } + return 0; + } + + if (wpas_p2p_cli_freq(wpa_s, NULL, NULL)) { + wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_NFC_WHILE_CLIENT "peer=" + MACSTR, MAC2STR(params.peer->p2p_device_addr)); + return 0; + } + + wpabuf_free(wpa_s->p2p_oob_dev_pw); + wpa_s->p2p_oob_dev_pw = NULL; + + if (params.oob_dev_pw_len < WPS_OOB_PUBKEY_HASH_LEN + 2) { + wpa_printf(MSG_DEBUG, "P2P: No peer OOB Dev Pw " + "received"); + return -1; + } + + id = WPA_GET_BE16(params.oob_dev_pw + WPS_OOB_PUBKEY_HASH_LEN); + wpa_printf(MSG_DEBUG, "P2P: Peer OOB Dev Pw %u", id); + wpa_hexdump(MSG_DEBUG, "P2P: Peer OOB Public Key hash", + params.oob_dev_pw, WPS_OOB_PUBKEY_HASH_LEN); + os_memcpy(wpa_s->p2p_peer_oob_pubkey_hash, + params.oob_dev_pw, WPS_OOB_PUBKEY_HASH_LEN); + wpa_s->p2p_peer_oob_pk_hash_known = 1; + + if (tag) { + if (id < 0x10) { + wpa_printf(MSG_DEBUG, "P2P: Static handover - invalid " + "peer OOB Device Password Id %u", id); + return -1; + } + wpa_printf(MSG_DEBUG, "P2P: Static handover - use peer OOB " + "Device Password Id %u", id); + wpa_hexdump_key(MSG_DEBUG, "P2P: Peer OOB Device Password", + params.oob_dev_pw + WPS_OOB_PUBKEY_HASH_LEN + 2, + params.oob_dev_pw_len - + WPS_OOB_PUBKEY_HASH_LEN - 2); + wpa_s->p2p_oob_dev_pw_id = id; + wpa_s->p2p_oob_dev_pw = wpabuf_alloc_copy( + params.oob_dev_pw + WPS_OOB_PUBKEY_HASH_LEN + 2, + params.oob_dev_pw_len - + WPS_OOB_PUBKEY_HASH_LEN - 2); + if (wpa_s->p2p_oob_dev_pw == NULL) + return -1; + + if (wpa_s->conf->wps_nfc_dh_pubkey == NULL && + wps_nfc_gen_dh(&wpa_s->conf->wps_nfc_dh_pubkey, + &wpa_s->conf->wps_nfc_dh_privkey) < 0) + return -1; + } else { + wpa_printf(MSG_DEBUG, "P2P: Using abbreviated WPS handshake " + "without Device Password"); + wpa_s->p2p_oob_dev_pw_id = DEV_PW_NFC_CONNECTION_HANDOVER; + } + + switch (params.next_step) { + case NO_ACTION: + case BOTH_GO: + case PEER_CLIENT: + /* already covered above */ + return 0; + case JOIN_GROUP: + return wpas_p2p_nfc_join_group(wpa_s, ¶ms); + case AUTH_JOIN: + return wpas_p2p_nfc_auth_join(wpa_s, ¶ms, tag); + case INIT_GO_NEG: + return wpas_p2p_nfc_init_go_neg(wpa_s, ¶ms, forced_freq); + case RESP_GO_NEG: + /* TODO: use own OOB Dev Pw */ + return wpas_p2p_nfc_resp_go_neg(wpa_s, ¶ms, forced_freq); + } + + return -1; +} + + +int wpas_p2p_nfc_tag_process(struct wpa_supplicant *wpa_s, + const struct wpabuf *data, int forced_freq) +{ + if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) + return -1; + + return wpas_p2p_nfc_connection_handover(wpa_s, data, 1, 1, forced_freq); +} + + +int wpas_p2p_nfc_report_handover(struct wpa_supplicant *wpa_s, int init, + const struct wpabuf *req, + const struct wpabuf *sel, int forced_freq) +{ + struct wpabuf *tmp; + int ret; + + if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) + return -1; + + wpa_printf(MSG_DEBUG, "NFC: P2P connection handover reported"); + + wpa_hexdump_ascii(MSG_DEBUG, "NFC: Req", + wpabuf_head(req), wpabuf_len(req)); + wpa_hexdump_ascii(MSG_DEBUG, "NFC: Sel", + wpabuf_head(sel), wpabuf_len(sel)); + if (forced_freq) + wpa_printf(MSG_DEBUG, "NFC: Forced freq %d", forced_freq); + tmp = ndef_parse_p2p(init ? sel : req); + if (tmp == NULL) { + wpa_printf(MSG_DEBUG, "P2P: Could not parse NDEF"); + return -1; + } + + ret = wpas_p2p_nfc_connection_handover(wpa_s, tmp, init, 0, + forced_freq); + wpabuf_free(tmp); + + return ret; +} + + +int wpas_p2p_nfc_tag_enabled(struct wpa_supplicant *wpa_s, int enabled) +{ + const u8 *if_addr; + int go_intent = wpa_s->conf->p2p_go_intent; + struct wpa_supplicant *iface; + + if (wpa_s->global->p2p == NULL) + return -1; + + if (!enabled) { + wpa_printf(MSG_DEBUG, "P2P: Disable use of own NFC Tag"); + for (iface = wpa_s->global->ifaces; iface; iface = iface->next) + { + if (!iface->ap_iface) + continue; + hostapd_wps_nfc_token_disable(iface->ap_iface->bss[0]); + } + p2p_set_authorized_oob_dev_pw_id(wpa_s->global->p2p, 0, + 0, NULL); + if (wpa_s->p2p_nfc_tag_enabled) + wpas_p2p_remove_pending_group_interface(wpa_s); + wpa_s->p2p_nfc_tag_enabled = 0; + return 0; + } + + if (wpa_s->global->p2p_disabled) + return -1; + + if (wpa_s->conf->wps_nfc_dh_pubkey == NULL || + wpa_s->conf->wps_nfc_dh_privkey == NULL || + wpa_s->conf->wps_nfc_dev_pw == NULL || + wpa_s->conf->wps_nfc_dev_pw_id < 0x10) { + wpa_printf(MSG_DEBUG, "P2P: NFC password token not configured " + "to allow static handover cases"); + return -1; + } + + wpa_printf(MSG_DEBUG, "P2P: Enable use of own NFC Tag"); + + wpa_s->p2p_oob_dev_pw_id = wpa_s->conf->wps_nfc_dev_pw_id; + wpabuf_free(wpa_s->p2p_oob_dev_pw); + wpa_s->p2p_oob_dev_pw = wpabuf_dup(wpa_s->conf->wps_nfc_dev_pw); + if (wpa_s->p2p_oob_dev_pw == NULL) + return -1; + wpa_s->p2p_peer_oob_pk_hash_known = 0; + + wpa_s->create_p2p_iface = wpas_p2p_create_iface(wpa_s); + + if (wpa_s->create_p2p_iface) { + enum wpa_driver_if_type iftype; + /* Prepare to add a new interface for the group */ + iftype = WPA_IF_P2P_GROUP; + if (go_intent == 15) + iftype = WPA_IF_P2P_GO; + if (wpas_p2p_add_group_interface(wpa_s, iftype) < 0) { + wpa_printf(MSG_ERROR, "P2P: Failed to allocate a new " + "interface for the group"); + return -1; + } + + if_addr = wpa_s->pending_interface_addr; + } else + if_addr = wpa_s->own_addr; + + wpa_s->p2p_nfc_tag_enabled = enabled; + + for (iface = wpa_s->global->ifaces; iface; iface = iface->next) { + struct hostapd_data *hapd; + if (iface->ap_iface == NULL) + continue; + hapd = iface->ap_iface->bss[0]; + wpabuf_free(hapd->conf->wps_nfc_dh_pubkey); + hapd->conf->wps_nfc_dh_pubkey = + wpabuf_dup(wpa_s->conf->wps_nfc_dh_pubkey); + wpabuf_free(hapd->conf->wps_nfc_dh_privkey); + hapd->conf->wps_nfc_dh_privkey = + wpabuf_dup(wpa_s->conf->wps_nfc_dh_privkey); + wpabuf_free(hapd->conf->wps_nfc_dev_pw); + hapd->conf->wps_nfc_dev_pw = + wpabuf_dup(wpa_s->conf->wps_nfc_dev_pw); + hapd->conf->wps_nfc_dev_pw_id = wpa_s->conf->wps_nfc_dev_pw_id; + + if (hostapd_wps_nfc_token_enable(iface->ap_iface->bss[0]) < 0) { + wpa_dbg(iface, MSG_DEBUG, + "P2P: Failed to enable NFC Tag for GO"); + } + } + p2p_set_authorized_oob_dev_pw_id( + wpa_s->global->p2p, wpa_s->conf->wps_nfc_dev_pw_id, go_intent, + if_addr); + + return 0; +} + +#endif /* CONFIG_WPS_NFC */ diff -Nru wpa-1.0/wpa_supplicant/p2p_supplicant.h wpa-2.1/wpa_supplicant/p2p_supplicant.h --- wpa-1.0/wpa_supplicant/p2p_supplicant.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/p2p_supplicant.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * wpa_supplicant - P2P * Copyright (c) 2009-2010, Atheros Communications * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef P2P_SUPPLICANT_H @@ -19,31 +13,44 @@ struct p2p_go_neg_results; enum p2p_send_action_result; struct p2p_peer_info; +struct p2p_channels; +struct wps_event_fail; int wpas_p2p_init(struct wpa_global *global, struct wpa_supplicant *wpa_s); void wpas_p2p_deinit(struct wpa_supplicant *wpa_s); void wpas_p2p_deinit_global(struct wpa_global *global); +int wpas_p2p_add_p2pdev_interface(struct wpa_supplicant *wpa_s); int wpas_p2p_connect(struct wpa_supplicant *wpa_s, const u8 *peer_addr, const char *pin, enum p2p_wps_method wps_method, - int persistent_group, int join, int auth, int go_intent, - int freq); + int persistent_group, int auto_join, int join, + int auth, int go_intent, int freq, int persistent_id, + int pd, int ht40, int vht); void wpas_p2p_remain_on_channel_cb(struct wpa_supplicant *wpa_s, unsigned int freq, unsigned int duration); void wpas_p2p_cancel_remain_on_channel_cb(struct wpa_supplicant *wpa_s, unsigned int freq); +int wpas_p2p_handle_frequency_conflicts(struct wpa_supplicant *wpa_s, + int freq, struct wpa_ssid *ssid); int wpas_p2p_group_remove(struct wpa_supplicant *wpa_s, const char *ifname); int wpas_p2p_group_add(struct wpa_supplicant *wpa_s, int persistent_group, - int freq); + int freq, int ht40, int vht); int wpas_p2p_group_add_persistent(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, int addr_allocated, - int freq); + int freq, int ht40, int vht, + const struct p2p_channels *channels, + int connection_timeout); struct p2p_group * wpas_p2p_group_init(struct wpa_supplicant *wpa_s, - int persistent_group, - int group_formation); + struct wpa_ssid *ssid); void wpas_p2p_wps_success(struct wpa_supplicant *wpa_s, const u8 *peer_addr, int registrar); +enum wpas_p2p_prov_disc_use { + WPAS_P2P_PD_FOR_GO_NEG, + WPAS_P2P_PD_FOR_JOIN, + WPAS_P2P_PD_AUTO +}; int wpas_p2p_prov_disc(struct wpa_supplicant *wpa_s, const u8 *peer_addr, - const char *config_method, int join); + const char *config_method, + enum wpas_p2p_prov_disc_use use); void wpas_send_action_tx_status(struct wpa_supplicant *wpa_s, const u8 *dst, const u8 *data, size_t data_len, enum p2p_send_action_result result); @@ -53,39 +60,28 @@ int wpas_p2p_find(struct wpa_supplicant *wpa_s, unsigned int timeout, enum p2p_discovery_type type, unsigned int num_req_dev_types, const u8 *req_dev_types, - const u8 *dev_id); + const u8 *dev_id, unsigned int search_delay); void wpas_p2p_stop_find(struct wpa_supplicant *wpa_s); int wpas_p2p_listen(struct wpa_supplicant *wpa_s, unsigned int timeout); int wpas_p2p_assoc_req_ie(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, u8 *buf, size_t len, int p2p_group); int wpas_p2p_probe_req_rx(struct wpa_supplicant *wpa_s, const u8 *addr, const u8 *dst, const u8 *bssid, - const u8 *ie, size_t ie_len); + const u8 *ie, size_t ie_len, + int ssi_signal); void wpas_p2p_rx_action(struct wpa_supplicant *wpa_s, const u8 *da, const u8 *sa, const u8 *bssid, u8 category, const u8 *data, size_t len, int freq); void wpas_p2p_scan_ie(struct wpa_supplicant *wpa_s, struct wpabuf *ies); void wpas_p2p_group_deinit(struct wpa_supplicant *wpa_s); -void wpas_dev_found(void *ctx, const u8 *addr, - const struct p2p_peer_info *info, - int new_device); -void wpas_go_neg_completed(void *ctx, struct p2p_go_neg_results *res); -void wpas_go_neg_req_rx(void *ctx, const u8 *src, u16 dev_passwd_id); -void wpas_prov_disc_req(void *ctx, const u8 *peer, u16 config_methods, - const u8 *dev_addr, const u8 *pri_dev_type, - const char *dev_name, u16 supp_config_methods, - u8 dev_capab, u8 group_capab, const u8 *group_id, - size_t group_id_len); -void wpas_prov_disc_resp(void *ctx, const u8 *peer, u16 config_methods); -void wpas_sd_request(void *ctx, int freq, const u8 *sa, u8 dialog_token, - u16 update_indic, const u8 *tlvs, size_t tlvs_len); -void wpas_sd_response(void *ctx, const u8 *sa, u16 update_indic, - const u8 *tlvs, size_t tlvs_len); -void * wpas_p2p_sd_request(struct wpa_supplicant *wpa_s, const u8 *dst, - const struct wpabuf *tlvs); -void * wpas_p2p_sd_request_upnp(struct wpa_supplicant *wpa_s, const u8 *dst, - u8 version, const char *query); -int wpas_p2p_sd_cancel_request(struct wpa_supplicant *wpa_s, void *req); +void wpas_p2p_group_formation_failed(struct wpa_supplicant *wpa_s); +u64 wpas_p2p_sd_request(struct wpa_supplicant *wpa_s, const u8 *dst, + const struct wpabuf *tlvs); +u64 wpas_p2p_sd_request_upnp(struct wpa_supplicant *wpa_s, const u8 *dst, + u8 version, const char *query); +u64 wpas_p2p_sd_request_wifi_display(struct wpa_supplicant *wpa_s, + const u8 *dst, const char *role); +int wpas_p2p_sd_cancel_request(struct wpa_supplicant *wpa_s, u64 req); void wpas_p2p_sd_response(struct wpa_supplicant *wpa_s, int freq, const u8 *dst, u8 dialog_token, const struct wpabuf *resp_tlvs); @@ -101,7 +97,8 @@ const char *service); int wpas_p2p_reject(struct wpa_supplicant *wpa_s, const u8 *addr); int wpas_p2p_invite(struct wpa_supplicant *wpa_s, const u8 *peer_addr, - struct wpa_ssid *ssid, const u8 *go_dev_addr); + struct wpa_ssid *ssid, const u8 *go_dev_addr, int freq, + int ht40, int vht, int pref_freq); int wpas_p2p_invite_group(struct wpa_supplicant *wpa_s, const char *ifname, const u8 *peer_addr, const u8 *go_dev_addr); void wpas_p2p_completed(struct wpa_supplicant *wpa_s); @@ -109,10 +106,12 @@ u32 interval1, u32 duration2, u32 interval2); int wpas_p2p_ext_listen(struct wpa_supplicant *wpa_s, unsigned int period, unsigned int interval); -void wpas_p2p_deauth_notif(struct wpa_supplicant *wpa_s, const u8 *bssid, - u16 reason_code, const u8 *ie, size_t ie_len); +int wpas_p2p_deauth_notif(struct wpa_supplicant *wpa_s, const u8 *bssid, + u16 reason_code, const u8 *ie, size_t ie_len, + int locally_generated); void wpas_p2p_disassoc_notif(struct wpa_supplicant *wpa_s, const u8 *bssid, - u16 reason_code, const u8 *ie, size_t ie_len); + u16 reason_code, const u8 *ie, size_t ie_len, + int locally_generated); void wpas_p2p_update_config(struct wpa_supplicant *wpa_s); int wpas_p2p_set_noa(struct wpa_supplicant *wpa_s, u8 count, int start, int duration); @@ -129,6 +128,7 @@ int wpas_p2p_disconnect(struct wpa_supplicant *wpa_s); void wpas_p2p_wps_failed(struct wpa_supplicant *wpa_s, struct wps_event_fail *fail); +int wpas_p2p_wps_eapol_cb(struct wpa_supplicant *wpa_s); int wpas_p2p_in_progress(struct wpa_supplicant *wpa_s); void wpas_p2p_network_removed(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid); @@ -137,5 +137,40 @@ size_t ssid_len); void wpas_p2p_notify_ap_sta_authorized(struct wpa_supplicant *wpa_s, const u8 *addr); +int wpas_p2p_scan_no_go_seen(struct wpa_supplicant *wpa_s); +int wpas_p2p_get_ht40_mode(struct wpa_supplicant *wpa_s, + struct hostapd_hw_modes *mode, u8 channel); +int wpas_p2p_get_vht80_center(struct wpa_supplicant *wpa_s, + struct hostapd_hw_modes *mode, u8 channel); +unsigned int wpas_p2p_search_delay(struct wpa_supplicant *wpa_s); +void wpas_p2p_new_psk_cb(struct wpa_supplicant *wpa_s, const u8 *mac_addr, + const u8 *p2p_dev_addr, + const u8 *psk, size_t psk_len); +void wpas_p2p_remove_client(struct wpa_supplicant *wpa_s, const u8 *peer, + int iface_addr); +struct wpabuf * wpas_p2p_nfc_handover_req(struct wpa_supplicant *wpa_s, + int ndef); +struct wpabuf * wpas_p2p_nfc_handover_sel(struct wpa_supplicant *wpa_s, + int ndef, int tag); +int wpas_p2p_nfc_tag_process(struct wpa_supplicant *wpa_s, + const struct wpabuf *data, int forced_freq); +int wpas_p2p_nfc_report_handover(struct wpa_supplicant *wpa_s, int init, + const struct wpabuf *req, + const struct wpabuf *sel, int forced_freq); +int wpas_p2p_nfc_tag_enabled(struct wpa_supplicant *wpa_s, int enabled); + +#ifdef CONFIG_P2P +int wpas_p2p_4way_hs_failed(struct wpa_supplicant *wpa_s); +void wpas_p2p_ap_setup_failed(struct wpa_supplicant *wpa_s); +#else /* CONFIG_P2P */ +static inline int wpas_p2p_4way_hs_failed(struct wpa_supplicant *wpa_s) +{ + return 0; +} + +static inline void wpas_p2p_ap_setup_failed(struct wpa_supplicant *wpa_s) +{ +} +#endif /* CONFIG_P2P */ #endif /* P2P_SUPPLICANT_H */ diff -Nru wpa-1.0/wpa_supplicant/preauth_test.c wpa-2.1/wpa_supplicant/preauth_test.c --- wpa-1.0/wpa_supplicant/preauth_test.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/preauth_test.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * WPA Supplicant - test code for pre-authentication * Copyright (c) 2003-2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. * * IEEE 802.1X Supplicant test code (to be used in place of wpa_supplicant.c. * Not used in production version. @@ -33,9 +27,6 @@ #include "drivers/driver.h" -extern int wpa_debug_level; -extern int wpa_debug_show_keys; - struct wpa_driver_ops *wpa_drivers[] = { NULL }; @@ -44,12 +35,6 @@ }; -static void _wpa_supplicant_disassociate(void *wpa_s, int reason_code) -{ - wpa_supplicant_disassociate(wpa_s, reason_code); -} - - static void _wpa_supplicant_deauthenticate(void *wpa_s, int reason_code) { wpa_supplicant_deauthenticate(wpa_s, reason_code); @@ -244,7 +229,6 @@ ctx->set_state = _wpa_supplicant_set_state; ctx->get_state = _wpa_supplicant_get_state; ctx->deauthenticate = _wpa_supplicant_deauthenticate; - ctx->disassociate = _wpa_supplicant_disassociate; ctx->set_key = wpa_supplicant_set_key; ctx->get_network_ctx = wpa_supplicant_get_network_ctx; ctx->get_bssid = wpa_supplicant_get_bssid; @@ -322,7 +306,7 @@ } os_memset(&wpa_s, 0, sizeof(wpa_s)); - wpa_s.conf = wpa_config_read(argv[1]); + wpa_s.conf = wpa_config_read(argv[1], NULL); if (wpa_s.conf == NULL) { printf("Failed to parse configuration file '%s'.\n", argv[1]); return -1; diff -Nru wpa-1.0/wpa_supplicant/README wpa-2.1/wpa_supplicant/README --- wpa-1.0/wpa_supplicant/README 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/README 2014-02-04 11:23:35.000000000 +0000 @@ -1,37 +1,22 @@ WPA Supplicant ============== -Copyright (c) 2003-2012, Jouni Malinen and contributors +Copyright (c) 2003-2014, Jouni Malinen and contributors All Rights Reserved. -This program is dual-licensed under both the GPL version 2 and BSD -license. Either license may be used at your option. +This program is licensed under the BSD license (the one with +advertisement clause removed). + +If you are submitting changes to the project, please see CONTRIBUTIONS +file for more instructions. License ------- -GPL v2: - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License version 2 as -published by the Free Software Foundation. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - -(this copy of the license is in COPYING file) - - -Alternatively, this software may be distributed, used, and modified -under the terms of BSD license: +This software may be distributed, used, and modified under the terms of +BSD license: Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -130,46 +115,15 @@ - NetBSD-current - Microsoft Windows with WinPcap (at least WinXP, may work with other versions) - drivers: - Linux drivers that support WPA/WPA2 configuration with the generic - Linux wireless extensions (WE-18 or newer). Even though there are + Linux drivers that support cfg80211/nl80211. Even though there are number of driver specific interface included in wpa_supplicant, please - note that Linux drivers are moving to use generic wireless extensions - and driver_wext (-Dwext on wpa_supplicant command line) should be the - default option to start with before falling back to driver specific - interface. - - Host AP driver for Prism2/2.5/3 (development snapshot/v0.2.x) - (http://hostap.epitest.fi/) - Driver need to be set in Managed mode ('iwconfig wlan0 mode managed'). - Please note that station firmware version needs to be 1.7.0 or newer - to work in WPA mode. - - Linuxant DriverLoader (http://www.linuxant.com/driverloader/) - with Windows NDIS driver for your wlan card supporting WPA. - - madwifi driver for cards based on Atheros chip set (ar521x) - (http://sourceforge.net/projects/madwifi/) - Please note that you will need to modify the wpa_supplicant .config - file to use the correct path for the madwifi driver root directory - (CFLAGS += -I../madwifi/wpa line in example defconfig). - - Linux ndiswrapper (http://ndiswrapper.sourceforge.net/) with - Windows NDIS driver. - - Broadcom wl.o driver (old version only) - This is a generic Linux driver for Broadcom IEEE 802.11a/g cards. - However, it is proprietary driver that is not publicly available - except for couple of exceptions, mainly Broadcom-based APs/wireless - routers that use Linux. The driver binary can be downloaded, e.g., - from Linksys support site (http://www.linksys.com/support/gpl.asp) - for Linksys WRT54G. The GPL tarball includes cross-compiler and - the needed header file, wlioctl.h, for compiling wpa_supplicant. - This driver support in wpa_supplicant is expected to work also with - other devices based on Broadcom driver (assuming the driver includes - client mode support). Please note that the newer Broadcom driver - ("hybrid Linux driver") supports Linux wireless extensions and does - not need (or even work) with the specific driver wrapper. Use -Dwext - with that driver. + note that Linux drivers are moving to use generic wireless configuration + interface driver_nl80211 (-Dnl80211 on wpa_supplicant command line) + should be the default option to start with before falling back to driver + specific interface. + + Linux drivers that support WPA/WPA2 configuration with the generic + Linux wireless extensions (WE-18 or newer). Obsoleted by nl80211. In theory, any driver that supports Linux wireless extensions can be used with IEEE 802.1X (i.e., not WPA) when using ap_scan=0 option in @@ -347,7 +301,7 @@ The build time configuration can be used to select only the needed features and limit the binary size and requirements for external libraries. The main configuration parts are the selection of which -driver interfaces (e.g., hostap, madwifi, ..) and which authentication +driver interfaces (e.g., nl80211, wext, ..) and which authentication methods (e.g., EAP-TLS, EAP-PEAP, ..) are included. Following build time configuration options are used to control IEEE @@ -382,21 +336,16 @@ Following options can be added to .config to select which driver interfaces are included. -CONFIG_DRIVER_HOSTAP=y -CONFIG_DRIVER_MADWIFI=y +CONFIG_DRIVER_NL80211=y CONFIG_DRIVER_WEXT=y -CONFIG_DRIVER_RALINK=y -CONFIG_DRIVER_BROADCOM=y CONFIG_DRIVER_BSD=y CONFIG_DRIVER_NDIS=y -Following example includes all features and driver interfaces that are -included in the wpa_supplicant package: +Following example includes some more features and driver interfaces that +are included in the wpa_supplicant package: -CONFIG_DRIVER_HOSTAP=y -CONFIG_DRIVER_MADWIFI=y +CONFIG_DRIVER_NL80211=y CONFIG_DRIVER_WEXT=y -CONFIG_DRIVER_BROADCOM=y CONFIG_DRIVER_BSD=y CONFIG_DRIVER_NDIS=y CONFIG_IEEE8021X_EAPOL=y @@ -461,6 +410,7 @@ usage: wpa_supplicant [-BddfhKLqqtuvwW] [-P] [-g] \ + [-G] \ -i -c [-C] [-D] [-p] \ [-b [-N -i -c [-C] [-D] \ [-p] [-b] ...] @@ -475,10 +425,11 @@ -D = driver name (can be multiple drivers: nl80211,wext) -f = Log output to default log location (normally /tmp) -g = global ctrl_interface + -G = global ctrl_interface group -K = include keys (passwords, etc.) in debug output -t = include timestamp in debug messages -h = show this help text - -L = show license (GPL and BSD) + -L = show license (BSD) -p = driver parameters -P = PID file -q = decrease debugging verbosity (-qq even less) @@ -489,12 +440,8 @@ -N = start describing new interface drivers: - hostap = Host AP driver (Intersil Prism2/2.5/3) [default] - (this can also be used with Linuxant DriverLoader) - madwifi = MADWIFI 802.11 support (Atheros, etc.) (deprecated; use wext) + nl80211 = Linux nl80211/cfg80211 wext = Linux wireless extensions (generic) - ralink = Ralink Client driver - broadcom = Broadcom wl.o driver wired = wpa_supplicant wired Ethernet driver roboswitch = wpa_supplicant Broadcom switch driver bsd = BSD 802.11 support (Atheros, etc.) @@ -527,15 +474,15 @@ start wpa_supplicant for two interfaces: wpa_supplicant \ - -c wpa1.conf -i wlan0 -D hostap -N \ - -c wpa2.conf -i ath0 -D madwifi + -c wpa1.conf -i wlan0 -D nl80211 -N \ + -c wpa2.conf -i wlan1 -D wext If the interface is added in a Linux bridge (e.g., br0), the bridge interface needs to be configured to wpa_supplicant in addition to the main interface: -wpa_supplicant -cw.conf -Dmadwifi -iath0 -bbr0 +wpa_supplicant -cw.conf -Dnl80211 -iwlan0 -bbr0 Configuration file @@ -927,10 +874,10 @@ # Start wpa_supplicant in the background wpa_supplicant -g/var/run/wpa_supplicant-global -B -# Add a new interface (wlan0, no configuration file, driver=wext, and +# Add a new interface (wlan0, no configuration file, driver=nl80211, and # enable control interface) wpa_cli -g/var/run/wpa_supplicant-global interface_add wlan0 \ - "" wext /var/run/wpa_supplicant + "" nl80211 /var/run/wpa_supplicant # Configure a network using the newly added network interface: wpa_cli -iwlan0 add_network @@ -991,7 +938,7 @@ chmod 0750 /var/run/wpa_priv - start wpa_priv as root (e.g., from system startup scripts) with the enabled interfaces configured on the command line: - wpa_priv -B -P /var/run/wpa_priv.pid wext:ath0 + wpa_priv -B -P /var/run/wpa_priv.pid nl80211:wlan0 - run wpa_supplicant as non-root with a user that is in wpapriv group: wpa_supplicant -i ath0 -c wpa_supplicant.conf @@ -1002,3 +949,105 @@ wpa_priv can control multiple interface with one process, but it is also possible to run multiple wpa_priv processes at the same time, if desired. + + +Linux capabilities instead of privileged process +------------------------------------------------ + +wpa_supplicant performs operations that need special permissions, e.g., +to control the network connection. Traditionally this has been achieved +by running wpa_supplicant as a privileged process with effective user id +0 (root). Linux capabilities can be used to provide restricted set of +capabilities to match the functions needed by wpa_supplicant. The +minimum set of capabilities needed for the operations is CAP_NET_ADMIN +and CAP_NET_RAW. + +setcap(8) can be used to set file capabilities. For example: + +sudo setcap cap_net_raw,cap_net_admin+ep wpa_supplicant + +Please note that this would give anyone being able to run that +wpa_supplicant binary access to the additional capabilities. This can +further be limited by file owner/group and mode bits. For example: + +sudo chown wpas wpa_supplicant +sudo chmod 0100 wpa_supplicant + +This combination of setcap, chown, and chmod commands would allow wpas +user to execute wpa_supplicant with additional network admin/raw +capabilities. + +Common way style of creating a control interface socket in +/var/run/wpa_supplicant could not be done by this user, but this +directory could be created before starting the wpa_supplicant and set to +suitable mode to allow wpa_supplicant to create sockets +there. Alternatively, other directory or abstract socket namespace could +be used for the control interface. + + +External requests for radio control +----------------------------------- + +External programs can request wpa_supplicant to not start offchannel +operations during other tasks that may need exclusive control of the +radio. The RADIO_WORK control interface command can be used for this. + +"RADIO_WORK add [freq=] [timeout=]" command can be +used to reserve a slot for radio access. If freq is specified, other +radio work items on the same channel may be completed in +parallel. Otherwise, all other radio work items are blocked during +execution. Timeout is set to 10 seconds by default to avoid blocking +wpa_supplicant operations for excessive time. If a longer (or shorter) +safety timeout is needed, that can be specified with the optional +timeout parameter. This command returns an identifier for the radio work +item. + +Once the radio work item has been started, "EXT-RADIO-WORK-START " +event message is indicated that the external processing can start. Once +the operation has been completed, "RADIO_WORK done " is used to +indicate that to wpa_supplicant. This allows other radio works to be +performed. If this command is forgotten (e.g., due to the external +program terminating), wpa_supplicant will time out the radio owrk item +and send "EXT-RADIO-WORK-TIMEOUT " event ot indicate that this has +happened. "RADIO_WORK done " can also be used to cancel items that +have not yet been started. + +For example, in wpa_cli interactive mode: + +> radio_work add test +1 +<3>EXT-RADIO-WORK-START 1 +> radio_work show +ext:test@wlan0:0:1:2.487797 +> radio_work done 1 +OK +> radio_work show + + +> radio_work done 3 +OK +> radio_work show +ext:test freq=2412 timeout=30@wlan0:2412:1:28.583483 +<3>EXT-RADIO-WORK-TIMEOUT 2 + + +> radio_work add test2 freq=2412 timeout=60 +5 +<3>EXT-RADIO-WORK-START 5 +> radio_work add test3 +6 +> radio_work add test4 +7 +> radio_work show +ext:test2 freq=2412 timeout=60@wlan0:2412:1:9.751844 +ext:test3@wlan0:0:0:5.071812 +ext:test4@wlan0:0:0:3.143870 +> radio_work done 6 +OK +> radio_work show +ext:test2 freq=2412 timeout=60@wlan0:2412:1:16.287869 +ext:test4@wlan0:0:0:9.679895 +> radio_work done 5 +OK +<3>EXT-RADIO-WORK-START 7 +<3>EXT-RADIO-WORK-TIMEOUT 7 diff -Nru wpa-1.0/wpa_supplicant/README-HS20 wpa-2.1/wpa_supplicant/README-HS20 --- wpa-1.0/wpa_supplicant/README-HS20 1970-01-01 00:00:00.000000000 +0000 +++ wpa-2.1/wpa_supplicant/README-HS20 2014-02-04 11:23:35.000000000 +0000 @@ -0,0 +1,507 @@ +wpa_supplicant and Hotspot 2.0 +============================== + +This document describe how the IEEE 802.11u Interworking and Wi-Fi +Hotspot 2.0 (Release 1) implementation in wpa_supplicant can be +configured and how an external component on the client e.g., management +GUI or Wi-Fi framework) is used to manage this functionality. + + +Introduction to Wi-Fi Hotspot 2.0 +--------------------------------- + +Hotspot 2.0 is the name of the Wi-Fi Alliance specification that is used +in the Wi-Fi CERTIFIED Passpoint program. More information about +this is available in this white paper: + +http://www.wi-fi.org/knowledge-center/white-papers/wi-fi-certified-passpoint%E2%84%A2-new-program-wi-fi-alliance%C2%AE-enable-seamless + +The Hotspot 2.0 specification is also available from WFA: +https://www.wi-fi.org/knowledge-center/published-specifications + +The core Interworking functionality (network selection, GAS/ANQP) were +standardized in IEEE Std 802.11u-2011 which is now part of the IEEE Std +802.11-2012. + + +wpa_supplicant network selection +-------------------------------- + +Interworking support added option for configuring credentials that can +work with multiple networks as an alternative to configuration of +network blocks (e.g., per-SSID parameters). When requested to perform +network selection, wpa_supplicant picks the highest priority enabled +network block or credential. If a credential is picked (based on ANQP +information from APs), a temporary network block is created +automatically for the matching network. This temporary network block is +used similarly to the network blocks that can be configured by the user, +but it is not stored into the configuration file and is meant to be used +only for temporary period of time since a new one can be created +whenever needed based on ANQP information and the credential. + +By default, wpa_supplicant is not using automatic network selection +unless requested explicitly with the interworking_select command. This +can be changed with the auto_interworking=1 parameter to perform network +selection automatically whenever trying to find a network for connection +and none of the enabled network blocks match with the scan results. This +case works similarly to "interworking_select auto", i.e., wpa_supplicant +will internally determine which network or credential is going to be +used based on configured priorities, scan results, and ANQP information. + + +wpa_supplicant configuration +---------------------------- + +Interworking and Hotspot 2.0 functionality are optional components that +need to be enabled in the wpa_supplicant build configuration +(.config). This is done by adding following parameters into that file: + +CONFIG_INTERWORKING=y +CONFIG_HS20=y + +It should be noted that this functionality requires a driver that +supports GAS/ANQP operations. This uses the same design as P2P, i.e., +Action frame processing and building in user space within +wpa_supplicant. The Linux nl80211 driver interface provides the needed +functionality for this. + + +There are number of run-time configuration parameters (e.g., in +wpa_supplicant.conf when using the configuration file) that can be used +to control Hotspot 2.0 operations. + +# Enable Interworking +interworking=1 + +# Enable Hotspot 2.0 +hs20=1 + +# Parameters for controlling scanning + +# Homogenous ESS identifier +# If this is set, scans will be used to request response only from BSSes +# belonging to the specified Homogeneous ESS. This is used only if interworking +# is enabled. +#hessid=00:11:22:33:44:55 + +# Access Network Type +# When Interworking is enabled, scans can be limited to APs that advertise the +# specified Access Network Type (0..15; with 15 indicating wildcard match). +# This value controls the Access Network Type value in Probe Request frames. +#access_network_type=15 + +# Automatic network selection behavior +# 0 = do not automatically go through Interworking network selection +# (i.e., require explicit interworking_select command for this; default) +# 1 = perform Interworking network selection if one or more +# credentials have been configured and scan did not find a +# matching network block +#auto_interworking=0 + + +Credentials can be pre-configured for automatic network selection: + +# credential block +# +# Each credential used for automatic network selection is configured as a set +# of parameters that are compared to the information advertised by the APs when +# interworking_select and interworking_connect commands are used. +# +# credential fields: +# +# temporary: Whether this credential is temporary and not to be saved +# +# priority: Priority group +# By default, all networks and credentials get the same priority group +# (0). This field can be used to give higher priority for credentials +# (and similarly in struct wpa_ssid for network blocks) to change the +# Interworking automatic networking selection behavior. The matching +# network (based on either an enabled network block or a credential) +# with the highest priority value will be selected. +# +# pcsc: Use PC/SC and SIM/USIM card +# +# realm: Home Realm for Interworking +# +# username: Username for Interworking network selection +# +# password: Password for Interworking network selection +# +# ca_cert: CA certificate for Interworking network selection +# +# client_cert: File path to client certificate file (PEM/DER) +# This field is used with Interworking networking selection for a case +# where client certificate/private key is used for authentication +# (EAP-TLS). Full path to the file should be used since working +# directory may change when wpa_supplicant is run in the background. +# +# Alternatively, a named configuration blob can be used by setting +# this to blob://blob_name. +# +# private_key: File path to client private key file (PEM/DER/PFX) +# When PKCS#12/PFX file (.p12/.pfx) is used, client_cert should be +# commented out. Both the private key and certificate will be read +# from the PKCS#12 file in this case. Full path to the file should be +# used since working directory may change when wpa_supplicant is run +# in the background. +# +# Windows certificate store can be used by leaving client_cert out and +# configuring private_key in one of the following formats: +# +# cert://substring_to_match +# +# hash://certificate_thumbprint_in_hex +# +# For example: private_key="hash://63093aa9c47f56ae88334c7b65a4" +# +# Note that when running wpa_supplicant as an application, the user +# certificate store (My user account) is used, whereas computer store +# (Computer account) is used when running wpasvc as a service. +# +# Alternatively, a named configuration blob can be used by setting +# this to blob://blob_name. +# +# private_key_passwd: Password for private key file +# +# imsi: IMSI in | | '-' | format +# +# milenage: Milenage parameters for SIM/USIM simulator in :: +# format +# +# domain_suffix_match: Constraint for server domain name +# If set, this FQDN is used as a suffix match requirement for the AAA +# server certificate in SubjectAltName dNSName element(s). If a +# matching dNSName is found, this constraint is met. If no dNSName +# values are present, this constraint is matched against SubjetName CN +# using same suffix match comparison. Suffix match here means that the +# host/domain name is compared one label at a time starting from the +# top-level domain and all the labels in @domain_suffix_match shall be +# included in the certificate. The certificate may include additional +# sub-level labels in addition to the required labels. +# +# For example, domain_suffix_match=example.com would match +# test.example.com but would not match test-example.com. +# +# domain: Home service provider FQDN(s) +# This is used to compare against the Domain Name List to figure out +# whether the AP is operated by the Home SP. Multiple domain entries can +# be used to configure alternative FQDNs that will be considered home +# networks. +# +# roaming_consortium: Roaming Consortium OI +# If roaming_consortium_len is non-zero, this field contains the +# Roaming Consortium OI that can be used to determine which access +# points support authentication with this credential. This is an +# alternative to the use of the realm parameter. When using Roaming +# Consortium to match the network, the EAP parameters need to be +# pre-configured with the credential since the NAI Realm information +# may not be available or fetched. +# +# eap: Pre-configured EAP method +# This optional field can be used to specify which EAP method will be +# used with this credential. If not set, the EAP method is selected +# automatically based on ANQP information (e.g., NAI Realm). +# +# phase1: Pre-configure Phase 1 (outer authentication) parameters +# This optional field is used with like the 'eap' parameter. +# +# phase2: Pre-configure Phase 2 (inner authentication) parameters +# This optional field is used with like the 'eap' parameter. +# +# excluded_ssid: Excluded SSID +# This optional field can be used to excluded specific SSID(s) from +# matching with the network. Multiple entries can be used to specify more +# than one SSID. +# +# for example: +# +#cred={ +# realm="example.com" +# username="user@example.com" +# password="password" +# ca_cert="/etc/wpa_supplicant/ca.pem" +# domain="example.com" +# domain_suffix_match="example.com" +#} +# +#cred={ +# imsi="310026-000000000" +# milenage="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82" +#} +# +#cred={ +# realm="example.com" +# username="user" +# password="password" +# ca_cert="/etc/wpa_supplicant/ca.pem" +# domain="example.com" +# roaming_consortium=223344 +# eap=TTLS +# phase2="auth=MSCHAPV2" +#} + + +Control interface +----------------- + +wpa_supplicant provides a control interface that can be used from +external programs to manage various operations. The included command +line tool, wpa_cli, can be used for manual testing with this interface. + +Following wpa_cli interactive mode commands show some examples of manual +operations related to Hotspot 2.0: + +Remove configured networks and credentials: + +> remove_network all +OK +> remove_cred all +OK + + +Add a username/password credential: + +> add_cred +0 +> set_cred 0 realm "mail.example.com" +OK +> set_cred 0 username "username" +OK +> set_cred 0 password "password" +OK +> set_cred 0 priority 1 +OK +> set_cred 0 temporary 1 +OK + +Add a SIM credential using a simulated SIM/USIM card for testing: + +> add_cred +1 +> set_cred 1 imsi "23456-0000000000" +OK +> set_cred 1 milenage "90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581:000000000123" +OK +> set_cred 1 priority 1 +OK + +Note: the return value of add_cred is used as the first argument to +the following set_cred commands. + +Add a SIM credential using a external SIM/USIM processing: + +> set external_sim 1 +OK +> add_cred +1 +> set_cred 1 imsi "23456-0000000000" +OK +> set_cred 1 eap SIM +OK + + +Add a WPA2-Enterprise network: + +> add_network +0 +> set_network 0 key_mgmt WPA-EAP +OK +> set_network 0 ssid "enterprise" +OK +> set_network 0 eap TTLS +OK +> set_network 0 anonymous_identity "anonymous" +OK +> set_network 0 identity "user" +OK +> set_network 0 password "password" +OK +> set_network 0 priority 0 +OK +> enable_network 0 no-connect +OK + + +Add an open network: + +> add_network +3 +> set_network 3 key_mgmt NONE +OK +> set_network 3 ssid "coffee-shop" +OK +> select_network 3 +OK + +Note: the return value of add_network is used as the first argument to +the following set_network commands. + +The preferred credentials/networks can be indicated with the priority +parameter (1 is higher priority than 0). + + +Interworking network selection can be started with interworking_select +command. This instructs wpa_supplicant to run a network scan and iterate +through the discovered APs to request ANQP information from the APs that +advertise support for Interworking/Hotspot 2.0: + +> interworking_select +OK +<3>Starting ANQP fetch for 02:00:00:00:01:00 +<3>RX-ANQP 02:00:00:00:01:00 ANQP Capability list +<3>RX-ANQP 02:00:00:00:01:00 Roaming Consortium list +<3>RX-HS20-ANQP 02:00:00:00:01:00 HS Capability List +<3>ANQP fetch completed +<3>INTERWORKING-AP 02:00:00:00:01:00 type=unknown + + +INTERWORKING-AP event messages indicate the APs that support network +selection and for which there is a matching +credential. interworking_connect command can be used to select a network +to connect with: + + +> interworking_connect 02:00:00:00:01:00 +OK +<3>CTRL-EVENT-SCAN-RESULTS +<3>SME: Trying to authenticate with 02:00:00:00:01:00 (SSID='Example Network' freq=2412 MHz) +<3>Trying to associate with 02:00:00:00:01:00 (SSID='Example Network' freq=2412 MHz) +<3>Associated with 02:00:00:00:01:00 +<3>CTRL-EVENT-EAP-STARTED EAP authentication started +<3>CTRL-EVENT-EAP-PROPOSED-METHOD vendor=0 method=21 +<3>CTRL-EVENT-EAP-METHOD EAP vendor 0 method 21 (TTLS) selected +<3>CTRL-EVENT-EAP-SUCCESS EAP authentication completed successfully +<3>WPA: Key negotiation completed with 02:00:00:00:01:00 [PTK=CCMP GTK=CCMP] +<3>CTRL-EVENT-CONNECTED - Connection to 02:00:00:00:01:00 completed (auth) [id=0 id_str=] + + +wpa_supplicant creates a temporary network block for the selected +network based on the configured credential and ANQP information from the +AP: + +> list_networks +network id / ssid / bssid / flags +0 Example Network any [CURRENT] +> get_network 0 key_mgmt +WPA-EAP +> get_network 0 eap +TTLS + + +Alternatively to using an external program to select the network, +"interworking_select auto" command can be used to request wpa_supplicant +to select which network to use based on configured priorities: + + +> remove_network all +OK +<3>CTRL-EVENT-DISCONNECTED bssid=02:00:00:00:01:00 reason=1 locally_generated=1 +> interworking_select auto +OK +<3>Starting ANQP fetch for 02:00:00:00:01:00 +<3>RX-ANQP 02:00:00:00:01:00 ANQP Capability list +<3>RX-ANQP 02:00:00:00:01:00 Roaming Consortium list +<3>RX-HS20-ANQP 02:00:00:00:01:00 HS Capability List +<3>ANQP fetch completed +<3>INTERWORKING-AP 02:00:00:00:01:00 type=unknown +<3>CTRL-EVENT-SCAN-RESULTS +<3>SME: Trying to authenticate with 02:00:00:00:01:00 (SSID='Example Network' freq=2412 MHz) +<3>Trying to associate with 02:00:00:00:01:00 (SSID='Example Network' freq=2412 MHz) +<3>Associated with 02:00:00:00:01:00 +<3>CTRL-EVENT-EAP-STARTED EAP authentication started +<3>CTRL-EVENT-EAP-PROPOSED-METHOD vendor=0 method=21 +<3>CTRL-EVENT-EAP-METHOD EAP vendor 0 method 21 (TTLS) selected +<3>CTRL-EVENT-EAP-SUCCESS EAP authentication completed successfully +<3>WPA: Key negotiation completed with 02:00:00:00:01:00 [PTK=CCMP GTK=CCMP] +<3>CTRL-EVENT-CONNECTED - Connection to 02:00:00:00:01:00 completed (reauth) [id=0 id_str=] + + +The connection status can be shown with the status command: + +> status +bssid=02:00:00:00:01:00 +ssid=Example Network +id=0 +mode=station +pairwise_cipher=CCMP <--- link layer security indication +group_cipher=CCMP +key_mgmt=WPA2/IEEE 802.1X/EAP +wpa_state=COMPLETED +p2p_device_address=02:00:00:00:00:00 +address=02:00:00:00:00:00 +hs20=1 <--- HS 2.0 indication +Supplicant PAE state=AUTHENTICATED +suppPortStatus=Authorized +EAP state=SUCCESS +selectedMethod=21 (EAP-TTLS) +EAP TLS cipher=AES-128-SHA +EAP-TTLSv0 Phase2 method=PAP + + +> status +bssid=02:00:00:00:02:00 +ssid=coffee-shop +id=3 +mode=station +pairwise_cipher=NONE +group_cipher=NONE +key_mgmt=NONE +wpa_state=COMPLETED +p2p_device_address=02:00:00:00:00:00 +address=02:00:00:00:00:00 + + +Note: The Hotspot 2.0 indication is shown as "hs20=1" in the status +command output. Link layer security is indicated with the +pairwise_cipher (CCMP = secure, NONE = no encryption used). + + +Also the scan results include the Hotspot 2.0 indication: + +> scan_results +bssid / frequency / signal level / flags / ssid +02:00:00:00:01:00 2412 -30 [WPA2-EAP-CCMP][ESS][HS20] Example Network + + +ANQP information for the BSS can be fetched using the BSS command: + +> bss 02:00:00:00:01:00 +id=1 +bssid=02:00:00:00:01:00 +freq=2412 +beacon_int=100 +capabilities=0x0411 +qual=0 +noise=-92 +level=-30 +tsf=1345573286517276 +age=105 +ie=000f4578616d706c65204e6574776f726b010882848b960c1218240301012a010432043048606c30140100000fac040100000fac040100000fac0100007f04000000806b091e07010203040506076c027f006f1001531122331020304050010203040506dd05506f9a1000 +flags=[WPA2-EAP-CCMP][ESS][HS20] +ssid=Example Network +anqp_roaming_consortium=031122330510203040500601020304050603fedcba + + +ANQP queries can also be requested with the anqp_get and hs20_anqp_get +commands: + +> anqp_get 02:00:00:00:01:00 261 +OK +<3>RX-ANQP 02:00:00:00:01:00 Roaming Consortium list +> hs20_anqp_get 02:00:00:00:01:00 2 +OK +<3>RX-HS20-ANQP 02:00:00:00:01:00 HS Capability List + +In addition, fetch_anqp command can be used to request similar set of +ANQP queries to be done as is run as part of interworking_select: + +> scan +OK +<3>CTRL-EVENT-SCAN-RESULTS +> fetch_anqp +OK +<3>Starting ANQP fetch for 02:00:00:00:01:00 +<3>RX-ANQP 02:00:00:00:01:00 ANQP Capability list +<3>RX-ANQP 02:00:00:00:01:00 Roaming Consortium list +<3>RX-HS20-ANQP 02:00:00:00:01:00 HS Capability List +<3>ANQP fetch completed diff -Nru wpa-1.0/wpa_supplicant/README-P2P wpa-2.1/wpa_supplicant/README-P2P --- wpa-1.0/wpa_supplicant/README-P2P 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/README-P2P 2014-02-04 11:23:35.000000000 +0000 @@ -71,7 +71,9 @@ Device Discovery -p2p_find [timeout in seconds] [type=] +p2p_find [timeout in seconds] [type=] \ + [dev_id=] [dev_type=] \ + [delay=] The default behavior is to run a single full scan in the beginning and then scan only social channels. type=social will scan only social @@ -81,6 +83,15 @@ will help in finding new groups or groups missed during the initial full scan. +The optional dev_id option can be used to specify a single P2P peer to +search for. The optional delay parameter can be used to request an extra +delay to be used between search iterations (e.g., to free up radio +resources for concurrent operations). + +The optional dev_type option can be used to specify a single device type +(primary or secondary) to search for, e.g., +"p2p_find dev_type=1-0050F204-1". + p2p_listen [timeout in seconds] Start Listen-only state (become discoverable without searching for @@ -101,7 +112,7 @@ Group Formation -p2p_prov_disc [join] +p2p_prov_disc [join|auto] Send P2P provision discovery request to the specified peer. The parameters for this command are the P2P device address of the peer and @@ -112,10 +123,14 @@ The optional "join" parameter can be used to indicate that this command is requesting an already running GO to prepare for a new client. This is -mainly used with "display" to request it to display a PIN. +mainly used with "display" to request it to display a PIN. The "auto" +parameter can be used to request wpa_supplicant to automatically figure +out whether the peer device is operating as a GO and if so, use +join-a-group style PD instead of GO Negotiation style PD. p2p_connect [display|keypad] - [persistent] [join|auth] [go_intent=<0..15>] [freq=] + [persistent|persistent=] [join|auth] + [go_intent=<0..15>] [freq=] [ht40] [vht] [provdisc] Start P2P group formation with a discovered P2P peer. This includes optional group owner negotiation, group interface setup, provisioning, @@ -128,7 +143,12 @@ used (e.g., 12345670). [display|keypad] is used with PIN method to specify which PIN is used (display=dynamically generated random PIN from local display, keypad=PIN entered from peer display). "persistent" -parameter can be used to request a persistent group to be formed. +parameter can be used to request a persistent group to be formed. The +"persistent=" alternative can be used to pre-populate +SSID/passphrase configuration based on a previously used persistent +group where this device was the GO. The previously used parameters will +then be used if the local end becomes the GO in GO Negotiation (which +can be forced with go_intent=15). "join" indicates that this is a command to join an existing group as a client. It skips the GO Negotiation part. This will send a Provision @@ -146,7 +166,13 @@ "freq" can be used to set a forced operating channel (e.g., freq=2412 to select 2.4 GHz channel 1). +"provdisc" can be used to request a Provision Discovery exchange to be +used prior to starting GO Negotiation as a workaround with some deployed +P2P implementations that require this to allow the user to accept the +connection. + p2p_group_add [persistent|persistent=] [freq=] + [ht40] [vht] Set up a P2P group owner manually (i.e., without group owner negotiation with a specific peer). This is also known as autonomous @@ -171,7 +197,21 @@ p2p_cancel -Cancel an ongoing P2P group formation related operation. +Cancel an ongoing P2P group formation and joining-a-group related +operation. This operations unauthorizes the specific peer device (if any +had been authorized to start group formation), stops P2P find (if in +progress), stops pending operations for join-a-group, and removes the +P2P group interface (if one was used) that is in the WPS provisioning +step. If the WPS provisioning step has been completed, the group is not +terminated. + +p2p_remove_client > + +This command can be used to remove the specified client from all groups +(operating and persistent) from the local GO. Note that the peer device +can rejoin the group if it is in possession of a valid key. See p2p_set +per_sta_psk command below for more details on how the peer can be +removed securely. Service Discovery @@ -199,6 +239,19 @@ will be automatically removed when the specified peer has replied to it. +Service Query TLV has following format: +Length (2 octets, little endian) - length of following data +Service Protocol Type (1 octet) - see the table below +Service Transaction ID (1 octet) - nonzero identifier for the TLV +Query Data (Length - 2 octets of data) - service protocol specific data + +Service Protocol Types: +0 = All service protocols +1 = Bonjour +2 = UPnP +3 = WS-Discovery +4 = Wi-Fi Display + For UPnP, an alternative command format can be used to specify a single query TLV (i.e., a service discovery for a specific UPnP service): @@ -236,6 +289,14 @@ p2p_serv_disc_req 00:00:00:00:00:00 upnp 10 uuid:6859dede-8574-59ab-9332-123456789012 p2p_serv_disc_req 00:00:00:00:00:00 upnp 10 urn:schemas-upnp-org:device:InternetGatewayDevice:1 +# Wi-Fi Display examples +# format: wifi-display +p2p_serv_disc_req 00:00:00:00:00:00 wifi-display [source] 2,3,4,5 +p2p_serv_disc_req 02:01:02:03:04:05 wifi-display [pri-sink] 3 +p2p_serv_disc_req 00:00:00:00:00:00 wifi-display [sec-source] 2 +p2p_serv_disc_req 00:00:00:00:00:00 wifi-display [source+sink] 2,3,4,5 +p2p_serv_disc_req 00:00:00:00:00:00 wifi-display [source][pri-sink] 2,3,4,5 + p2p_serv_disc_cancel_req Cancel a pending P2P service discovery request. This command takes a @@ -318,15 +379,21 @@ Invitation p2p_invite [persistent=|group=] [peer=address] - [go_dev_addr=address] + [go_dev_addr=address] [freq=] [ht40] [vht] + [pref=] Invite a peer to join a group (e.g., group=wlan1) or to reinvoke a persistent group (e.g., persistent=4). If the peer device is the GO of -the persisten group, the peer parameter is not needed. Otherwise it is +the persistent group, the peer parameter is not needed. Otherwise it is used to specify which device to invite. go_dev_addr parameter can be used to override the GO device address for Invitation Request should it be not known for some reason (this should not be needed in most -cases). +cases). When reinvoking a persistent group, the GO device can specify +the frequency for the group with the freq parameter. When reinvoking a +persistent group, the P2P client device can use freq parameter to force +a specific operating channel (or invitation failure if GO rejects that) +or pref parameter to request a specific channel (while allowing GO to +select to use another channel, if needed). Group Operations @@ -356,9 +423,11 @@ Send a P2P Presence Request to the GO (this is only available when acting as a P2P client). If no duration/interval pairs are given, the request indicates that this client has no special needs for GO -presence. the first parameter pair gives the preferred duration and +presence. The first parameter pair gives the preferred duration and interval values in microseconds. If the second pair is included, that -indicates which value would be acceptable. +indicates which value would be acceptable. This command returns OK +immediately and the response from the GO is indicated in a +P2P-PRESENCE-RESPONSE event message. Parameters @@ -404,6 +473,20 @@ (DIRECT-). For example, postfix of "-testing" could result in the SSID becoming DIRECT-ab-testing. +p2p_set per_sta_psk <0/1> + +Disabled(default)/enables use of per-client PSK in the P2P groups. This +can be used to request GO to assign a unique PSK for each client during +WPS provisioning. When enabled, this allow clients to be removed from +the group securily with p2p_remove_client command since that client's +PSK is removed at the same time to prevent it from connecting back using +the old PSK. When per-client PSK is not used, the client can still be +disconnected, but it will be able to re-join the group since the PSK it +learned previously is still valid. It should be noted that the default +passphrase on the GO that is normally used to allow legacy stations to +connect through manual configuration does not change here, so if that is +shared, devices with knowledge of that passphrase can still connect. + set Set global configuration parameters which may also affect P2P diff -Nru wpa-1.0/wpa_supplicant/README-Windows.txt wpa-2.1/wpa_supplicant/README-Windows.txt --- wpa-1.0/wpa_supplicant/README-Windows.txt 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/README-Windows.txt 2014-02-04 11:23:35.000000000 +0000 @@ -4,13 +4,8 @@ Copyright (c) 2003-2009, Jouni Malinen and contributors All Rights Reserved. -This program is dual-licensed under both the GPL version 2 and BSD -license. Either license may be used at your option. - -This product includes software developed by the OpenSSL Project -for use in the OpenSSL Toolkit (http://www.openssl.org/). This -product includes cryptographic software written by Eric Young -(eay@cryptsoft.com). +This program is licensed under the BSD license (the one with +advertisement clause removed). wpa_supplicant has support for being used as a WPA/WPA2/IEEE 802.1X @@ -35,20 +30,6 @@ - WPA2-EAP, TKIP, CCMP, TKIP+CCMP -Binary version --------------- - -Compiled binary version of the wpa_supplicant and additional tools is -available from http://w1.fi/wpa_supplicant/. These binaries can be -used after installing WinPcap. - -wpa_gui uses Qt 4 framework and may need additional dynamic libraries -(DLLs). These libraries are available from -http://w1.fi/wpa_supplicant/qt4/wpa_gui-qt433-windows-dll.zip -You can copy the DLL files from this ZIP package into the same directory -with wpa_gui.exe to allow wpa_gui to be started. - - Building wpa_supplicant with mingw ---------------------------------- @@ -316,135 +297,3 @@ See win_example.reg for an example on how to setup wpasvc.exe parameters in registry. It can also be imported to registry as a starting point for the configuration. - - - -License information for third party software used in this product: - - OpenSSL License - --------------- - -/* ==================================================================== - * Copyright (c) 1998-2004 The OpenSSL Project. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * 3. All advertising materials mentioning features or use of this - * software must display the following acknowledgment: - * "This product includes software developed by the OpenSSL Project - * for use in the OpenSSL Toolkit. (http://www.openssl.org/)" - * - * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to - * endorse or promote products derived from this software without - * prior written permission. For written permission, please contact - * openssl-core@openssl.org. - * - * 5. Products derived from this software may not be called "OpenSSL" - * nor may "OpenSSL" appear in their names without prior written - * permission of the OpenSSL Project. - * - * 6. Redistributions of any form whatsoever must retain the following - * acknowledgment: - * "This product includes software developed by the OpenSSL Project - * for use in the OpenSSL Toolkit (http://www.openssl.org/)" - * - * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY - * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR - * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED - * OF THE POSSIBILITY OF SUCH DAMAGE. - * ==================================================================== - * - * This product includes cryptographic software written by Eric Young - * (eay@cryptsoft.com). This product includes software written by Tim - * Hudson (tjh@cryptsoft.com). - * - */ - - Original SSLeay License - ----------------------- - -/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) - * All rights reserved. - * - * This package is an SSL implementation written - * by Eric Young (eay@cryptsoft.com). - * The implementation was written so as to conform with Netscapes SSL. - * - * This library is free for commercial and non-commercial use as long as - * the following conditions are aheared to. The following conditions - * apply to all code found in this distribution, be it the RC4, RSA, - * lhash, DES, etc., code; not just the SSL code. The SSL documentation - * included with this distribution is covered by the same copyright terms - * except that the holder is Tim Hudson (tjh@cryptsoft.com). - * - * Copyright remains Eric Young's, and as such any Copyright notices in - * the code are not to be removed. - * If this package is used in a product, Eric Young should be given attribution - * as the author of the parts of the library used. - * This can be in the form of a textual message at program startup or - * in documentation (online or textual) provided with the package. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * "This product includes cryptographic software written by - * Eric Young (eay@cryptsoft.com)" - * The word 'cryptographic' can be left out if the rouines from the library - * being used are not cryptographic related :-). - * 4. If you include any Windows specific code (or a derivative thereof) from - * the apps directory (application code) you must include an acknowledgement: - * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" - * - * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * The licence and distribution terms for any publically available version or - * derivative of this code cannot be changed. i.e. this code cannot simply be - * copied and put under another distribution licence - * [including the GNU Public Licence.] - */ - - - - Qt Open Source Edition - ---------------------- - -The Qt GUI Toolkit is Copyright (C) 1994-2007 Trolltech ASA. -Qt Open Source Edition is licensed under GPL version 2. - -Source code for the library is available at -http://w1.fi/wpa_supplicant/qt4/qt-win-opensource-src-4.3.3.zip diff -Nru wpa-1.0/wpa_supplicant/README-WPS wpa-2.1/wpa_supplicant/README-WPS --- wpa-1.0/wpa_supplicant/README-WPS 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/README-WPS 2014-02-04 11:23:35.000000000 +0000 @@ -67,6 +67,10 @@ CONFIG_WPS_ER=y +Following parameter can be used to enable support for NFC config method: + +CONFIG_WPS_NFC=y + WPS needs the Universally Unique IDentifier (UUID; see RFC 4122) for the device. This is configured in the runtime configuration for @@ -126,6 +130,12 @@ This starts the WPS negotiation in the same way as above with the generated PIN. +When the wps_pin command is issued for an AP (including P2P GO) mode +interface, an optional timeout parameter can be used to specify +expiration timeout for the PIN in seconds. For example: + +wpa_cli wps_pin any 12345670 300 + If a random PIN is needed for a user interface, "wpa_cli wps_pin get" can be used to generate a new PIN without starting WPS negotiation. @@ -249,16 +259,16 @@ wps_er_stop - stop WPS ER functionality -wps_er_learn +wps_er_learn - learn AP configuration -wps_er_set_config +wps_er_set_config - use AP configuration from a locally configured network (e.g., from wps_reg command); this does not change the AP's configuration, but only prepares a configuration to be used when enrolling a new device to the AP -wps_er_config +wps_er_config - examples: wps_er_config 87654321-9abc-def0-1234-56789abc0002 12345670 testing WPA2PSK CCMP 12345678 wpa_er_config 87654321-9abc-def0-1234-56789abc0002 12345670 clear OPEN NONE "" @@ -267,10 +277,10 @@ must be one of the following: NONE WEP TKIP CCMP -wps_er_pbc +wps_er_pbc - accept an Enrollee PBC using External Registrar -wps_er_pin [Enrollee MAC address] +wps_er_pin [Enrollee MAC address] - add an Enrollee PIN to External Registrar - if Enrollee UUID is not known, "any" can be used to add a wildcard PIN - if the MAC address of the enrollee is known, it should be configured @@ -303,3 +313,99 @@ - WPS ER learned AP settings WPS-ER-AP-SETTINGS uuid=fd91b4ec-e3fa-5891-a57d-8c59efeed1d2 ssid=test-wps auth_type=0x0020 encr_type=0x0008 key=12345678 + + +WPS with NFC +------------ + +WPS can be used with NFC-based configuration method. An NFC tag +containing a password token from the Enrollee can be used to +authenticate the connection instead of the PIN. In addition, an NFC tag +with a configuration token can be used to transfer AP settings without +going through the WPS protocol. + +When the station acts as an Enrollee, a local NFC tag with a password +token can be used by touching the NFC interface of a Registrar. + +"wps_nfc [BSSID]" command starts WPS protocol run with the local end as +the Enrollee using the NFC password token that is either pre-configured +in the configuration file (wps_nfc_dev_pw_id, wps_nfc_dh_pubkey, +wps_nfc_dh_privkey, wps_nfc_dev_pw) or generated dynamically with +"wps_nfc_token " command. The included nfc_pw_token tool +(build with "make nfc_pw_token") can be used to generate NFC password +tokens during manufacturing (each station needs to have its own random +keys). + +The "wps_nfc_config_token " command can be used to build an +NFC configuration token when wpa_supplicant is controlling an AP +interface (AP or P2P GO). The output value from this command is a +hexdump of the current AP configuration (WPS parameter requests this to +include only the WPS attributes; NDEF parameter requests additional NDEF +encapsulation to be included). This data needs to be written to an NFC +tag with an external program. Once written, the NFC configuration token +can be used to touch an NFC interface on a station to provision the +credentials needed to access the network. + +The "wps_nfc_config_token " command can be used +to build an NFC configuration token based on a locally configured +network. + +If the station includes NFC interface and reads an NFC tag with a MIME +media type "application/vnd.wfa.wsc", the NDEF message payload (with or +without NDEF encapsulation) can be delivered to wpa_supplicant using the +following wpa_cli command: + +wps_nfc_tag_read + +If the NFC tag contains a configuration token, the network is added to +wpa_supplicant configuration. If the NFC tag contains a password token, +the token is added to the WPS Registrar component. This information can +then be used with wps_reg command (when the NFC password token was from +an AP) using a special value "nfc-pw" in place of the PIN parameter. If +the ER functionality has been started (wps_er_start), the NFC password +token is used to enable enrollment of a new station (that was the source +of the NFC password token). + +"nfc_get_handover_req " command can be used to build the +WPS carrier record for a Handover Request Message for connection +handover. The first argument selects the format of the output data and +the second argument selects which type of connection handover is +requested (WPS-CR = Wi-Fi handover as specified in WSC 2.0). + +"nfc_get_handover_sel [UUID|BSSID]" command can be used to +build the contents of a Handover Select Message for connection handover +when this does not depend on the contents of the Handover Request +Message. The first argument selects the format of the output data and +the second argument selects which type of connection handover is +requested (WPS = Wi-Fi handover as specified in WSC 2.0). If the options +UUID|BSSID argument is included, this is a request to build the handover +message for the specified AP when wpa_supplicant is operating as a WPS +ER. + +"nfc_rx_handover_req " is used to indicate receipt +of NFC connection handover request. The payload may include multiple +carriers the the applicable ones are matched based on the media +type. The reply data is contents for the Handover Select Message +(hexdump). + +"nfc_rx_handover_sel " is used to indicate receipt +of NFC connection handover select. The payload may include multiple +carriers the the applicable ones are matched based on the media +type. + +"nfc_report_handover WPS +" can be used as an alternative way for +reporting completed NFC connection handover. The first parameter +indicates whether the local device initiated or responded to the +connection handover and the carrier records are the selected carrier +from the handover request and select messages as a hexdump. + +The "wps_er_nfc_config_token " command can be +used to build an NFC configuration token for the specified AP when +wpa_supplicant is operating as a WPS ER. The output value from this +command is a hexdump of the selected AP configuration (WPS parameter +requests this to include only the WPS attributes; NDEF parameter +requests additional NDEF encapsulation to be included). This data needs +to be written to an NFC tag with an external program. Once written, the +NFC configuration token can be used to touch an NFC interface on a +station to provision the credentials needed to access the network. diff -Nru wpa-1.0/wpa_supplicant/scan.c wpa-2.1/wpa_supplicant/scan.c --- wpa-1.0/wpa_supplicant/scan.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/scan.c 2014-02-04 11:23:35.000000000 +0000 @@ -1,15 +1,9 @@ /* * WPA Supplicant - Scanning - * Copyright (c) 2003-2010, Jouni Malinen + * Copyright (c) 2003-2014, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "utils/includes.h" @@ -17,12 +11,14 @@ #include "utils/common.h" #include "utils/eloop.h" #include "common/ieee802_11_defs.h" +#include "common/wpa_ctrl.h" #include "config.h" #include "wpa_supplicant_i.h" #include "driver_i.h" #include "wps_supplicant.h" #include "p2p_supplicant.h" #include "p2p/p2p.h" +#include "hs20_supplicant.h" #include "notify.h" #include "bss.h" #include "scan.h" @@ -71,7 +67,8 @@ } #ifdef CONFIG_P2P - if (!wpa_s->global->p2p_disabled && wpa_s->global->p2p) { + if (!wpa_s->global->p2p_disabled && wpa_s->global->p2p && + !wpa_s->conf->p2p_disabled) { wpa_s->wps->dev.p2p = 1; if (!wps) { wps = 1; @@ -85,15 +82,33 @@ #endif /* CONFIG_WPS */ -int wpa_supplicant_enabled_networks(struct wpa_config *conf) +/** + * wpa_supplicant_enabled_networks - Check whether there are enabled networks + * @wpa_s: Pointer to wpa_supplicant data + * Returns: 0 if no networks are enabled, >0 if networks are enabled + * + * This function is used to figure out whether any networks (or Interworking + * with enabled credentials and auto_interworking) are present in the current + * configuration. + */ +int wpa_supplicant_enabled_networks(struct wpa_supplicant *wpa_s) { - struct wpa_ssid *ssid = conf->ssid; - int count = 0; + struct wpa_ssid *ssid = wpa_s->conf->ssid; + int count = 0, disabled = 0; while (ssid) { - if (!ssid->disabled) + if (!wpas_network_disabled(wpa_s, ssid)) count++; + else + disabled++; ssid = ssid->next; } + if (wpa_s->conf->cred && wpa_s->conf->interworking && + wpa_s->conf->auto_interworking) + count++; + if (count == 0 && disabled > 0) { + wpa_dbg(wpa_s, MSG_DEBUG, "No enabled networks (%d disabled " + "networks)", disabled); + } return count; } @@ -102,7 +117,7 @@ struct wpa_ssid *ssid) { while (ssid) { - if (!ssid->disabled) + if (!wpas_network_disabled(wpa_s, ssid)) break; ssid = ssid->next; } @@ -126,89 +141,67 @@ } -static int int_array_len(const int *a) -{ - int i; - for (i = 0; a && a[i]; i++) - ; - return i; -} - - -static void int_array_concat(int **res, const int *a) +static void wpas_trigger_scan_cb(struct wpa_radio_work *work, int deinit) { - int reslen, alen, i; - int *n; - - reslen = int_array_len(*res); - alen = int_array_len(a); + struct wpa_supplicant *wpa_s = work->wpa_s; + struct wpa_driver_scan_params *params = work->ctx; + int ret; - n = os_realloc(*res, (reslen + alen + 1) * sizeof(int)); - if (n == NULL) { - os_free(*res); - *res = NULL; + if (deinit) { + wpa_scan_free_params(params); return; } - for (i = 0; i <= alen; i++) - n[reslen + i] = a[i]; - *res = n; -} - - -static int freq_cmp(const void *a, const void *b) -{ - int _a = *(int *) a; - int _b = *(int *) b; - - if (_a == 0) - return 1; - if (_b == 0) - return -1; - return _a - _b; -} + wpa_supplicant_notify_scanning(wpa_s, 1); -static void int_array_sort_unique(int *a) -{ - int alen; - int i, j; - - if (a == NULL) + if (wpa_s->clear_driver_scan_cache) + params->only_new_results = 1; + ret = wpa_drv_scan(wpa_s, params); + wpa_scan_free_params(params); + work->ctx = NULL; + if (ret) { + wpa_supplicant_notify_scanning(wpa_s, 0); + wpas_notify_scan_done(wpa_s, 0); + radio_work_done(work); return; - - alen = int_array_len(a); - qsort(a, alen, sizeof(int), freq_cmp); - - i = 0; - j = 1; - while (a[i] && a[j]) { - if (a[i] == a[j]) { - j++; - continue; - } - a[++i] = a[j++]; } - if (a[i]) - i++; - a[i] = 0; + + os_get_reltime(&wpa_s->scan_trigger_time); + wpa_s->scan_runs++; + wpa_s->normal_scans++; + wpa_s->own_scan_requested = 1; + wpa_s->clear_driver_scan_cache = 0; + wpa_s->scan_work = work; } +/** + * wpa_supplicant_trigger_scan - Request driver to start a scan + * @wpa_s: Pointer to wpa_supplicant data + * @params: Scan parameters + * Returns: 0 on success, -1 on failure + */ int wpa_supplicant_trigger_scan(struct wpa_supplicant *wpa_s, struct wpa_driver_scan_params *params) { - int ret; + struct wpa_driver_scan_params *ctx; - wpa_supplicant_notify_scanning(wpa_s, 1); + if (wpa_s->scan_work) { + wpa_dbg(wpa_s, MSG_INFO, "Reject scan trigger since one is already pending"); + return -1; + } - ret = wpa_drv_scan(wpa_s, params); - if (ret) { - wpa_supplicant_notify_scanning(wpa_s, 0); - wpas_notify_scan_done(wpa_s, 0); - } else - wpa_s->scan_runs++; + ctx = wpa_scan_clone_params(params); + if (ctx == NULL) + return -1; - return ret; + if (radio_add_work(wpa_s, 0, "scan", 0, wpas_trigger_scan_cb, ctx) < 0) + { + wpa_scan_free_params(ctx); + return -1; + } + + return 0; } @@ -236,10 +229,9 @@ } -static int -wpa_supplicant_start_sched_scan(struct wpa_supplicant *wpa_s, - struct wpa_driver_scan_params *params, - int interval) +int wpa_supplicant_start_sched_scan(struct wpa_supplicant *wpa_s, + struct wpa_driver_scan_params *params, + int interval) { int ret; @@ -254,7 +246,7 @@ } -static int wpa_supplicant_stop_sched_scan(struct wpa_supplicant *wpa_s) +int wpa_supplicant_stop_sched_scan(struct wpa_supplicant *wpa_s) { int ret; @@ -342,8 +334,19 @@ if (params->freqs) params->freqs[0] = wpa_s->wps_freq; wpa_s->after_wps--; - } + } else if (wpa_s->after_wps) + wpa_s->after_wps--; + if (params->freqs == NULL && wpa_s->known_wps_freq && wpa_s->wps_freq) + { + /* Optimize provisioning scan based on already known channel */ + wpa_dbg(wpa_s, MSG_DEBUG, "WPS: Scan only frequency %u MHz", + wpa_s->wps_freq); + params->freqs = os_zalloc(2 * sizeof(int)); + if (params->freqs) + params->freqs[0] = wpa_s->wps_freq; + wpa_s->known_wps_freq = 0; /* only do this once */ + } #endif /* CONFIG_WPS */ } @@ -373,9 +376,7 @@ #endif /* CONFIG_INTERWORKING */ -static struct wpabuf * -wpa_supplicant_extra_ies(struct wpa_supplicant *wpa_s, - struct wpa_driver_scan_params *params) +static struct wpabuf * wpa_supplicant_extra_ies(struct wpa_supplicant *wpa_s) { struct wpabuf *extra_ie = NULL; #ifdef CONFIG_WPS @@ -394,7 +395,9 @@ if (wps) { struct wpabuf *wps_ie; - wps_ie = wps_build_probe_req_ie(wps == 2, &wpa_s->wps->dev, + wps_ie = wps_build_probe_req_ie(wps == 2 ? DEV_PW_PUSHBUTTON : + DEV_PW_DEFAULT, + &wpa_s->wps->dev, wpa_s->wps->uuid, req_type, 0, NULL); if (wps_ie) { @@ -414,32 +417,138 @@ #endif /* CONFIG_WPS */ +#ifdef CONFIG_HS20 + if (wpa_s->conf->hs20 && wpabuf_resize(&extra_ie, 7) == 0) + wpas_hs20_add_indication(extra_ie); +#endif /* CONFIG_HS20 */ + return extra_ie; } +#ifdef CONFIG_P2P + +/* + * Check whether there are any enabled networks or credentials that could be + * used for a non-P2P connection. + */ +static int non_p2p_network_enabled(struct wpa_supplicant *wpa_s) +{ + struct wpa_ssid *ssid; + + for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) { + if (wpas_network_disabled(wpa_s, ssid)) + continue; + if (!ssid->p2p_group) + return 1; + } + + if (wpa_s->conf->cred && wpa_s->conf->interworking && + wpa_s->conf->auto_interworking) + return 1; + + return 0; +} + +#endif /* CONFIG_P2P */ + + +static struct hostapd_hw_modes * get_mode(struct hostapd_hw_modes *modes, + u16 num_modes, + enum hostapd_hw_mode mode) +{ + u16 i; + + for (i = 0; i < num_modes; i++) { + if (modes[i].mode == mode) + return &modes[i]; + } + + return NULL; +} + + +static void wpa_setband_scan_freqs_list(struct wpa_supplicant *wpa_s, + enum hostapd_hw_mode band, + struct wpa_driver_scan_params *params) +{ + /* Include only supported channels for the specified band */ + struct hostapd_hw_modes *mode; + int count, i; + + mode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes, band); + if (mode == NULL) { + /* No channels supported in this band - use empty list */ + params->freqs = os_zalloc(sizeof(int)); + return; + } + + params->freqs = os_zalloc((mode->num_channels + 1) * sizeof(int)); + if (params->freqs == NULL) + return; + for (count = 0, i = 0; i < mode->num_channels; i++) { + if (mode->channels[i].flag & HOSTAPD_CHAN_DISABLED) + continue; + params->freqs[count++] = mode->channels[i].freq; + } +} + + +static void wpa_setband_scan_freqs(struct wpa_supplicant *wpa_s, + struct wpa_driver_scan_params *params) +{ + if (wpa_s->hw.modes == NULL) + return; /* unknown what channels the driver supports */ + if (params->freqs) + return; /* already using a limited channel set */ + if (wpa_s->setband == WPA_SETBAND_5G) + wpa_setband_scan_freqs_list(wpa_s, HOSTAPD_MODE_IEEE80211A, + params); + else if (wpa_s->setband == WPA_SETBAND_2G) + wpa_setband_scan_freqs_list(wpa_s, HOSTAPD_MODE_IEEE80211G, + params); +} + + static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx) { struct wpa_supplicant *wpa_s = eloop_ctx; struct wpa_ssid *ssid; - int scan_req = 0, ret; - struct wpabuf *extra_ie; + int ret; + struct wpabuf *extra_ie = NULL; struct wpa_driver_scan_params params; + struct wpa_driver_scan_params *scan_params; size_t max_ssids; enum wpa_states prev_state; + if (wpa_s->pno || wpa_s->pno_sched_pending) { + wpa_dbg(wpa_s, MSG_DEBUG, "Skip scan - PNO is in progress"); + return; + } + if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) { wpa_dbg(wpa_s, MSG_DEBUG, "Skip scan - interface disabled"); return; } - if (wpa_s->disconnected && !wpa_s->scan_req) { + if (wpa_s->disconnected && wpa_s->scan_req == NORMAL_SCAN_REQ) { + wpa_dbg(wpa_s, MSG_DEBUG, "Disconnected - do not scan"); wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED); return; } - if (!wpa_supplicant_enabled_networks(wpa_s->conf) && - !wpa_s->scan_req) { + if (wpa_s->scanning) { + /* + * If we are already in scanning state, we shall reschedule the + * the incoming scan request. + */ + wpa_dbg(wpa_s, MSG_DEBUG, "Already scanning - Reschedule the incoming scan req"); + wpa_supplicant_req_scan(wpa_s, 1, 0); + return; + } + + if (!wpa_supplicant_enabled_networks(wpa_s) && + wpa_s->scan_req == NORMAL_SCAN_REQ) { wpa_dbg(wpa_s, MSG_DEBUG, "No enabled networks - do not scan"); wpa_supplicant_set_state(wpa_s, WPA_INACTIVE); return; @@ -460,14 +569,8 @@ #ifdef CONFIG_P2P if (wpas_p2p_in_progress(wpa_s)) { - if (wpa_s->wpa_state == WPA_SCANNING) { - wpa_dbg(wpa_s, MSG_DEBUG, "Delay station mode scan " - "while P2P operation is in progress"); - wpa_supplicant_req_scan(wpa_s, 5, 0); - } else { - wpa_dbg(wpa_s, MSG_DEBUG, "Do not request scan while " - "P2P operation is in progress"); - } + wpa_dbg(wpa_s, MSG_DEBUG, "Delay station mode scan while P2P operation is in progress"); + wpa_supplicant_req_scan(wpa_s, 5, 0); return; } #endif /* CONFIG_P2P */ @@ -480,8 +583,8 @@ max_ssids = WPAS_MAX_SCAN_SSIDS; } - scan_req = wpa_s->scan_req; - wpa_s->scan_req = 0; + wpa_s->last_scan_req = wpa_s->scan_req; + wpa_s->scan_req = NORMAL_SCAN_REQ; os_memset(¶ms, 0, sizeof(params)); @@ -490,7 +593,16 @@ wpa_s->wpa_state == WPA_INACTIVE) wpa_supplicant_set_state(wpa_s, WPA_SCANNING); - if (scan_req != 2 && wpa_s->connect_without_scan) { + /* + * If autoscan has set its own scanning parameters + */ + if (wpa_s->autoscan_params != NULL) { + scan_params = wpa_s->autoscan_params; + goto scan; + } + + if (wpa_s->last_scan_req != MANUAL_SCAN_REQ && + wpa_s->connect_without_scan) { for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) { if (ssid == wpa_s->connect_without_scan) break; @@ -504,6 +616,19 @@ } } +#ifdef CONFIG_P2P + if ((wpa_s->p2p_in_provisioning || wpa_s->show_group_started) && + wpa_s->go_params) { + wpa_printf(MSG_DEBUG, "P2P: Use specific SSID for scan during P2P group formation (p2p_in_provisioning=%d show_group_started=%d)", + wpa_s->p2p_in_provisioning, + wpa_s->show_group_started); + params.ssids[0].ssid = wpa_s->go_params->ssid; + params.ssids[0].ssid_len = wpa_s->go_params->ssid_len; + params.num_ssids = 1; + goto ssid_list_set; + } +#endif /* CONFIG_P2P */ + /* Find the starting point from which to continue scanning */ ssid = wpa_s->conf->ssid; if (wpa_s->prev_scan_ssid != WILDCARD_SSID_SCAN) { @@ -516,8 +641,10 @@ } } - if (scan_req != 2 && wpa_s->conf->ap_scan == 2) { + if (wpa_s->last_scan_req != MANUAL_SCAN_REQ && + wpa_s->conf->ap_scan == 2) { wpa_s->connect_without_scan = NULL; + wpa_s->prev_scan_wildcard = 0; wpa_supplicant_assoc_try(wpa_s, ssid); return; } else if (wpa_s->conf->ap_scan == 2) { @@ -532,7 +659,8 @@ if (ssid == NULL && max_ssids > 1) ssid = wpa_s->conf->ssid; while (ssid) { - if (!ssid->disabled && ssid->scan_ssid) { + if (!wpas_network_disabled(wpa_s, ssid) && + ssid->scan_ssid) { wpa_hexdump_ascii(MSG_DEBUG, "Scan SSID", ssid->ssid, ssid->ssid_len); params.ssids[params.num_ssids].ssid = @@ -552,7 +680,7 @@ } for (tssid = wpa_s->conf->ssid; tssid; tssid = tssid->next) { - if (tssid->disabled) + if (wpas_network_disabled(wpa_s, tssid)) continue; if ((params.freqs || !freqs_set) && tssid->scan_freq) { int_array_concat(¶ms.freqs, @@ -566,24 +694,58 @@ int_array_sort_unique(params.freqs); } - if (ssid) { - wpa_s->prev_scan_ssid = ssid; - if (max_ssids > 1) { - wpa_dbg(wpa_s, MSG_DEBUG, "Include wildcard SSID in " - "the scan request"); - params.num_ssids++; + if (ssid && max_ssids == 1) { + /* + * If the driver is limited to 1 SSID at a time interleave + * wildcard SSID scans with specific SSID scans to avoid + * waiting a long time for a wildcard scan. + */ + if (!wpa_s->prev_scan_wildcard) { + params.ssids[0].ssid = NULL; + params.ssids[0].ssid_len = 0; + wpa_s->prev_scan_wildcard = 1; + wpa_dbg(wpa_s, MSG_DEBUG, "Starting AP scan for " + "wildcard SSID (Interleave with specific)"); + } else { + wpa_s->prev_scan_ssid = ssid; + wpa_s->prev_scan_wildcard = 0; + wpa_dbg(wpa_s, MSG_DEBUG, + "Starting AP scan for specific SSID: %s", + wpa_ssid_txt(ssid->ssid, ssid->ssid_len)); } - wpa_dbg(wpa_s, MSG_DEBUG, "Starting AP scan for specific " - "SSID(s)"); + } else if (ssid) { + /* max_ssids > 1 */ + + wpa_s->prev_scan_ssid = ssid; + wpa_dbg(wpa_s, MSG_DEBUG, "Include wildcard SSID in " + "the scan request"); + params.num_ssids++; + } else if (wpa_s->last_scan_req == MANUAL_SCAN_REQ && + wpa_s->manual_scan_passive && params.num_ssids == 0) { + wpa_dbg(wpa_s, MSG_DEBUG, "Use passive scan based on manual request"); } else { wpa_s->prev_scan_ssid = WILDCARD_SSID_SCAN; params.num_ssids++; wpa_dbg(wpa_s, MSG_DEBUG, "Starting AP scan for wildcard " "SSID"); } +#ifdef CONFIG_P2P +ssid_list_set: +#endif /* CONFIG_P2P */ wpa_supplicant_optimize_freqs(wpa_s, ¶ms); - extra_ie = wpa_supplicant_extra_ies(wpa_s, ¶ms); + extra_ie = wpa_supplicant_extra_ies(wpa_s); + + if (wpa_s->last_scan_req == MANUAL_SCAN_REQ && + wpa_s->manual_scan_only_new) + params.only_new_results = 1; + + if (wpa_s->last_scan_req == MANUAL_SCAN_REQ && params.freqs == NULL && + wpa_s->manual_scan_freqs) { + wpa_dbg(wpa_s, MSG_DEBUG, "Limit manual scan to specified channels"); + params.freqs = wpa_s->manual_scan_freqs; + wpa_s->manual_scan_freqs = NULL; + } if (params.freqs == NULL && wpa_s->next_scan_freqs) { wpa_dbg(wpa_s, MSG_DEBUG, "Optimize scan based on previously " @@ -592,6 +754,32 @@ } else os_free(wpa_s->next_scan_freqs); wpa_s->next_scan_freqs = NULL; + wpa_setband_scan_freqs(wpa_s, ¶ms); + + /* See if user specified frequencies. If so, scan only those. */ + if (wpa_s->conf->freq_list && !params.freqs) { + wpa_dbg(wpa_s, MSG_DEBUG, + "Optimize scan based on conf->freq_list"); + int_array_concat(¶ms.freqs, wpa_s->conf->freq_list); + } + + /* Use current associated channel? */ + if (wpa_s->conf->scan_cur_freq && !params.freqs) { + unsigned int num = wpa_s->num_multichan_concurrent; + + params.freqs = os_calloc(num + 1, sizeof(int)); + if (params.freqs) { + num = get_shared_radio_freqs(wpa_s, params.freqs, num); + if (num > 0) { + wpa_dbg(wpa_s, MSG_DEBUG, "Scan only the " + "current operating channels since " + "scan_cur_freq is enabled"); + } else { + os_free(params.freqs); + params.freqs = NULL; + } + } + } params.filter_ssids = wpa_supplicant_build_filter_ssids( wpa_s->conf, ¶ms.num_filter_ssids); @@ -601,7 +789,8 @@ } #ifdef CONFIG_P2P - if (wpa_s->p2p_in_provisioning) { + if (wpa_s->p2p_in_provisioning || + (wpa_s->show_group_started && wpa_s->go_params)) { /* * The interface may not yet be in P2P mode, so we have to * explicitly request P2P probe to disable CCK rates. @@ -610,7 +799,48 @@ } #endif /* CONFIG_P2P */ - ret = wpa_supplicant_trigger_scan(wpa_s, ¶ms); + scan_params = ¶ms; + +scan: +#ifdef CONFIG_P2P + /* + * If the driver does not support multi-channel concurrency and a + * virtual interface that shares the same radio with the wpa_s interface + * is operating there may not be need to scan other channels apart from + * the current operating channel on the other virtual interface. Filter + * out other channels in case we are trying to find a connection for a + * station interface when we are not configured to prefer station + * connection and a concurrent operation is already in process. + */ + if (wpa_s->scan_for_connection && + wpa_s->last_scan_req == NORMAL_SCAN_REQ && + !scan_params->freqs && !params.freqs && + wpas_is_p2p_prioritized(wpa_s) && + wpa_s->p2p_group_interface == NOT_P2P_GROUP_INTERFACE && + non_p2p_network_enabled(wpa_s)) { + unsigned int num = wpa_s->num_multichan_concurrent; + + params.freqs = os_calloc(num + 1, sizeof(int)); + if (params.freqs) { + num = get_shared_radio_freqs(wpa_s, params.freqs, num); + if (num > 0 && num == wpa_s->num_multichan_concurrent) { + wpa_dbg(wpa_s, MSG_DEBUG, "Scan only the current operating channels since all channels are already used"); + } else { + os_free(params.freqs); + params.freqs = NULL; + } + } + } +#endif /* CONFIG_P2P */ + + ret = wpa_supplicant_trigger_scan(wpa_s, scan_params); + + if (ret && wpa_s->last_scan_req == MANUAL_SCAN_REQ && params.freqs && + !wpa_s->manual_scan_freqs) { + /* Restore manual_scan_freqs for the next attempt */ + wpa_s->manual_scan_freqs = params.freqs; + params.freqs = NULL; + } wpabuf_free(extra_ie); os_free(params.freqs); @@ -620,11 +850,38 @@ wpa_msg(wpa_s, MSG_WARNING, "Failed to initiate AP scan"); if (prev_state != wpa_s->wpa_state) wpa_supplicant_set_state(wpa_s, prev_state); + /* Restore scan_req since we will try to scan again */ + wpa_s->scan_req = wpa_s->last_scan_req; wpa_supplicant_req_scan(wpa_s, 1, 0); + } else { + wpa_s->scan_for_connection = 0; } } +void wpa_supplicant_update_scan_int(struct wpa_supplicant *wpa_s, int sec) +{ + struct os_reltime remaining, new_int; + int cancelled; + + cancelled = eloop_cancel_timeout_one(wpa_supplicant_scan, wpa_s, NULL, + &remaining); + + new_int.sec = sec; + new_int.usec = 0; + if (cancelled && os_reltime_before(&remaining, &new_int)) { + new_int.sec = remaining.sec; + new_int.usec = remaining.usec; + } + + if (cancelled) { + eloop_register_timeout(new_int.sec, new_int.usec, + wpa_supplicant_scan, wpa_s, NULL); + } + wpa_s->scan_interval = sec; +} + + /** * wpa_supplicant_req_scan - Schedule a scan for neighboring access points * @wpa_s: Pointer to wpa_supplicant data @@ -636,32 +893,19 @@ */ void wpa_supplicant_req_scan(struct wpa_supplicant *wpa_s, int sec, int usec) { - /* If there's at least one network that should be specifically scanned - * then don't cancel the scan and reschedule. Some drivers do - * background scanning which generates frequent scan results, and that - * causes the specific SSID scan to get continually pushed back and - * never happen, which causes hidden APs to never get probe-scanned. - */ - if (eloop_is_timeout_registered(wpa_supplicant_scan, wpa_s, NULL) && - wpa_s->conf->ap_scan == 1) { - struct wpa_ssid *ssid = wpa_s->conf->ssid; - - while (ssid) { - if (!ssid->disabled && ssid->scan_ssid) - break; - ssid = ssid->next; - } - if (ssid) { - wpa_dbg(wpa_s, MSG_DEBUG, "Not rescheduling scan to " - "ensure that specific SSID scans occur"); - return; - } + int res = eloop_deplete_timeout(sec, usec, wpa_supplicant_scan, wpa_s, + NULL); + if (res == 1) { + wpa_dbg(wpa_s, MSG_DEBUG, "Rescheduling scan request: %d.%06d sec", + sec, usec); + } else if (res == 0) { + wpa_dbg(wpa_s, MSG_DEBUG, "Ignore new scan request for %d.%06d sec since an earlier request is scheduled to trigger sooner", + sec, usec); + } else { + wpa_dbg(wpa_s, MSG_DEBUG, "Setting scan request: %d.%06d sec", + sec, usec); + eloop_register_timeout(sec, usec, wpa_supplicant_scan, wpa_s, NULL); } - - wpa_dbg(wpa_s, MSG_DEBUG, "Setting scan request: %d sec %d usec", - sec, usec); - eloop_cancel_timeout(wpa_supplicant_scan, wpa_s, NULL); - eloop_register_timeout(sec, usec, wpa_supplicant_scan, wpa_s, NULL); } @@ -670,6 +914,7 @@ * @wpa_s: Pointer to wpa_supplicant data * @sec: Number of seconds after which to scan * @usec: Number of microseconds after which to scan + * Returns: 0 on success or -1 otherwise * * This function is used to schedule periodic scans for neighboring * access points after the specified time. @@ -691,6 +936,7 @@ /** * wpa_supplicant_req_sched_scan - Start a periodic scheduled scan * @wpa_s: Pointer to wpa_supplicant data + * Returns: 0 is sched_scan was started or -1 otherwise * * This function is used to schedule periodic scans for neighboring * access points repeating the scan continuously. @@ -698,11 +944,14 @@ int wpa_supplicant_req_sched_scan(struct wpa_supplicant *wpa_s) { struct wpa_driver_scan_params params; + struct wpa_driver_scan_params *scan_params; enum wpa_states prev_state; - struct wpa_ssid *ssid; - struct wpabuf *wps_ie = NULL; + struct wpa_ssid *ssid = NULL; + struct wpabuf *extra_ie = NULL; int ret; unsigned int max_sched_scan_ssids; + int wildcard = 0; + int need_ssids; if (!wpa_s->sched_scan_supported) return -1; @@ -711,9 +960,56 @@ max_sched_scan_ssids = WPAS_MAX_SCAN_SSIDS; else max_sched_scan_ssids = wpa_s->max_sched_scan_ssids; + if (max_sched_scan_ssids < 1 || wpa_s->conf->disable_scan_offload) + return -1; - if (wpa_s->sched_scanning) + if (wpa_s->sched_scanning) { + wpa_dbg(wpa_s, MSG_DEBUG, "Already sched scanning"); return 0; + } + + need_ssids = 0; + for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) { + if (!wpas_network_disabled(wpa_s, ssid) && !ssid->scan_ssid) { + /* Use wildcard SSID to find this network */ + wildcard = 1; + } else if (!wpas_network_disabled(wpa_s, ssid) && + ssid->ssid_len) + need_ssids++; + +#ifdef CONFIG_WPS + if (!wpas_network_disabled(wpa_s, ssid) && + ssid->key_mgmt == WPA_KEY_MGMT_WPS) { + /* + * Normal scan is more reliable and faster for WPS + * operations and since these are for short periods of + * time, the benefit of trying to use sched_scan would + * be limited. + */ + wpa_dbg(wpa_s, MSG_DEBUG, "Use normal scan instead of " + "sched_scan for WPS"); + return -1; + } +#endif /* CONFIG_WPS */ + } + if (wildcard) + need_ssids++; + + if (wpa_s->normal_scans < 3 && + (need_ssids <= wpa_s->max_scan_ssids || + wpa_s->max_scan_ssids >= (int) max_sched_scan_ssids)) { + /* + * When normal scan can speed up operations, use that for the + * first operations before starting the sched_scan to allow + * user space sleep more. We do this only if the normal scan + * has functionality that is suitable for this or if the + * sched_scan does not have better support for multiple SSIDs. + */ + wpa_dbg(wpa_s, MSG_DEBUG, "Use normal scan instead of " + "sched_scan for initial scans (normal_scans=%d)", + wpa_s->normal_scans); + return -1; + } os_memset(¶ms, 0, sizeof(params)); @@ -726,6 +1022,11 @@ wpa_s->wpa_state == WPA_INACTIVE) wpa_supplicant_set_state(wpa_s, WPA_SCANNING); + if (wpa_s->autoscan_params != NULL) { + scan_params = wpa_s->autoscan_params; + goto scan; + } + /* Find the starting point from which to continue scanning */ ssid = wpa_s->conf->ssid; if (wpa_s->prev_sched_ssid) { @@ -740,30 +1041,50 @@ if (!ssid || !wpa_s->prev_sched_ssid) { wpa_dbg(wpa_s, MSG_DEBUG, "Beginning of SSID list"); - - wpa_s->sched_scan_interval = 2; + if (wpa_s->conf->sched_scan_interval) + wpa_s->sched_scan_interval = + wpa_s->conf->sched_scan_interval; + if (wpa_s->sched_scan_interval == 0) + wpa_s->sched_scan_interval = 10; wpa_s->sched_scan_timeout = max_sched_scan_ssids * 2; wpa_s->first_sched_scan = 1; ssid = wpa_s->conf->ssid; wpa_s->prev_sched_ssid = ssid; } + if (wildcard) { + wpa_dbg(wpa_s, MSG_DEBUG, "Add wildcard SSID to sched_scan"); + params.num_ssids++; + } + while (ssid) { - if (ssid->disabled) { - wpa_s->prev_sched_ssid = ssid; - ssid = ssid->next; - continue; - } + if (wpas_network_disabled(wpa_s, ssid)) + goto next; - if (params.filter_ssids && ssid->ssid && ssid->ssid_len) { + if (params.num_filter_ssids < wpa_s->max_match_sets && + params.filter_ssids && ssid->ssid && ssid->ssid_len) { + wpa_dbg(wpa_s, MSG_DEBUG, "add to filter ssid: %s", + wpa_ssid_txt(ssid->ssid, ssid->ssid_len)); os_memcpy(params.filter_ssids[params.num_filter_ssids].ssid, ssid->ssid, ssid->ssid_len); params.filter_ssids[params.num_filter_ssids].ssid_len = ssid->ssid_len; params.num_filter_ssids++; + } else if (params.filter_ssids && ssid->ssid && ssid->ssid_len) + { + wpa_dbg(wpa_s, MSG_DEBUG, "Not enough room for SSID " + "filter for sched_scan - drop filter"); + os_free(params.filter_ssids); + params.filter_ssids = NULL; + params.num_filter_ssids = 0; } - if (ssid->scan_ssid) { + if (ssid->scan_ssid && ssid->ssid && ssid->ssid_len) { + if (params.num_ssids == max_sched_scan_ssids) + break; /* only room for broadcast SSID */ + wpa_dbg(wpa_s, MSG_DEBUG, + "add to active scan ssid: %s", + wpa_ssid_txt(ssid->ssid, ssid->ssid_len)); params.ssids[params.num_ssids].ssid = ssid->ssid; params.ssids[params.num_ssids].ssid_len = @@ -771,31 +1092,52 @@ params.num_ssids++; if (params.num_ssids >= max_sched_scan_ssids) { wpa_s->prev_sched_ssid = ssid; + do { + ssid = ssid->next; + } while (ssid && + (wpas_network_disabled(wpa_s, ssid) || + !ssid->scan_ssid)); break; } } - if (params.num_filter_ssids >= wpa_s->max_match_sets) - break; + next: wpa_s->prev_sched_ssid = ssid; ssid = ssid->next; } - if (!params.num_ssids) { + if (params.num_filter_ssids == 0) { os_free(params.filter_ssids); - return 0; + params.filter_ssids = NULL; } - if (wpa_s->wps) - wps_ie = wpa_supplicant_extra_ies(wpa_s, ¶ms); + extra_ie = wpa_supplicant_extra_ies(wpa_s); + if (extra_ie) { + params.extra_ies = wpabuf_head(extra_ie); + params.extra_ies_len = wpabuf_len(extra_ie); + } + + if (wpa_s->conf->filter_rssi) + params.filter_rssi = wpa_s->conf->filter_rssi; + + scan_params = ¶ms; + +scan: + if (ssid || !wpa_s->first_sched_scan) { + wpa_dbg(wpa_s, MSG_DEBUG, + "Starting sched scan: interval %d timeout %d", + wpa_s->sched_scan_interval, wpa_s->sched_scan_timeout); + } else { + wpa_dbg(wpa_s, MSG_DEBUG, + "Starting sched scan: interval %d (no timeout)", + wpa_s->sched_scan_interval); + } - wpa_dbg(wpa_s, MSG_DEBUG, - "Starting sched scan: interval %d timeout %d", - wpa_s->sched_scan_interval, wpa_s->sched_scan_timeout); + wpa_setband_scan_freqs(wpa_s, scan_params); - ret = wpa_supplicant_start_sched_scan(wpa_s, ¶ms, + ret = wpa_supplicant_start_sched_scan(wpa_s, scan_params, wpa_s->sched_scan_interval); - wpabuf_free(wps_ie); + wpabuf_free(extra_ie); os_free(params.filter_ssids); if (ret) { wpa_msg(wpa_s, MSG_WARNING, "Failed to initiate sched scan"); @@ -813,8 +1155,16 @@ wpa_s->first_sched_scan = 0; wpa_s->sched_scan_timeout /= 2; wpa_s->sched_scan_interval *= 2; + if (wpa_s->sched_scan_timeout < wpa_s->sched_scan_interval) { + wpa_s->sched_scan_interval = 10; + wpa_s->sched_scan_timeout = max_sched_scan_ssids * 2; + } } + /* If there is no more ssids, start next time from the beginning */ + if (!ssid) + wpa_s->prev_sched_ssid = NULL; + return 0; } @@ -834,6 +1184,23 @@ /** + * wpa_supplicant_cancel_delayed_sched_scan - Stop a delayed scheduled scan + * @wpa_s: Pointer to wpa_supplicant data + * + * This function is used to stop a delayed scheduled scan. + */ +void wpa_supplicant_cancel_delayed_sched_scan(struct wpa_supplicant *wpa_s) +{ + if (!wpa_s->sched_scan_supported) + return; + + wpa_dbg(wpa_s, MSG_DEBUG, "Cancelling delayed sched scan"); + eloop_cancel_timeout(wpa_supplicant_delayed_sched_scan_timeout, + wpa_s, NULL); +} + + +/** * wpa_supplicant_cancel_sched_scan - Stop running scheduled scans * @wpa_s: Pointer to wpa_supplicant data * @@ -850,6 +1217,16 @@ } +/** + * wpa_supplicant_notify_scanning - Indicate possible scan state change + * @wpa_s: Pointer to wpa_supplicant data + * @scanning: Whether scanning is currently in progress + * + * This function is to generate scanning notifycations. It is called whenever + * there may have been a change in scanning (scan started, completed, stopped). + * wpas_notify_scanning() is called whenever the scanning state changed from the + * previously notified state. + */ void wpa_supplicant_notify_scanning(struct wpa_supplicant *wpa_s, int scanning) { @@ -882,6 +1259,15 @@ } +/** + * wpa_scan_get_ie - Fetch a specified information element from a scan result + * @res: Scan result entry + * @ie: Information element identitifier (WLAN_EID_*) + * Returns: Pointer to the information element (id field) or %NULL if not found + * + * This function returns the first matching information element in the scan + * result. + */ const u8 * wpa_scan_get_ie(const struct wpa_scan_res *res, u8 ie) { const u8 *end, *pos; @@ -901,6 +1287,15 @@ } +/** + * wpa_scan_get_vendor_ie - Fetch vendor information element from a scan result + * @res: Scan result entry + * @vendor_type: Vendor type (four octets starting the IE payload) + * Returns: Pointer to the information element (id field) or %NULL if not found + * + * This function returns the first matching information element in the scan + * result. + */ const u8 * wpa_scan_get_vendor_ie(const struct wpa_scan_res *res, u32 vendor_type) { @@ -922,52 +1317,65 @@ } -struct wpabuf * wpa_scan_get_vendor_ie_multi(const struct wpa_scan_res *res, - u32 vendor_type) +/** + * wpa_scan_get_vendor_ie_beacon - Fetch vendor information from a scan result + * @res: Scan result entry + * @vendor_type: Vendor type (four octets starting the IE payload) + * Returns: Pointer to the information element (id field) or %NULL if not found + * + * This function returns the first matching information element in the scan + * result. + * + * This function is like wpa_scan_get_vendor_ie(), but uses IE buffer only + * from Beacon frames instead of either Beacon or Probe Response frames. + */ +const u8 * wpa_scan_get_vendor_ie_beacon(const struct wpa_scan_res *res, + u32 vendor_type) { - struct wpabuf *buf; const u8 *end, *pos; - buf = wpabuf_alloc(res->ie_len); - if (buf == NULL) + if (res->beacon_ie_len == 0) return NULL; pos = (const u8 *) (res + 1); - end = pos + res->ie_len; + pos += res->ie_len; + end = pos + res->beacon_ie_len; while (pos + 1 < end) { if (pos + 2 + pos[1] > end) break; if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 && vendor_type == WPA_GET_BE32(&pos[2])) - wpabuf_put_data(buf, pos + 2 + 4, pos[1] - 4); + return pos; pos += 2 + pos[1]; } - if (wpabuf_len(buf) == 0) { - wpabuf_free(buf); - buf = NULL; - } - - return buf; + return NULL; } -struct wpabuf * wpa_scan_get_vendor_ie_multi_beacon( - const struct wpa_scan_res *res, u32 vendor_type) +/** + * wpa_scan_get_vendor_ie_multi - Fetch vendor IE data from a scan result + * @res: Scan result entry + * @vendor_type: Vendor type (four octets starting the IE payload) + * Returns: Pointer to the information element payload or %NULL if not found + * + * This function returns concatenated payload of possibly fragmented vendor + * specific information elements in the scan result. The caller is responsible + * for freeing the returned buffer. + */ +struct wpabuf * wpa_scan_get_vendor_ie_multi(const struct wpa_scan_res *res, + u32 vendor_type) { struct wpabuf *buf; const u8 *end, *pos; - if (res->beacon_ie_len == 0) - return NULL; - buf = wpabuf_alloc(res->beacon_ie_len); + buf = wpabuf_alloc(res->ie_len); if (buf == NULL) return NULL; pos = (const u8 *) (res + 1); - pos += res->ie_len; - end = pos + res->beacon_ie_len; + end = pos + res->ie_len; while (pos + 1 < end) { if (pos + 2 + pos[1] > end) @@ -1113,6 +1521,7 @@ static void dump_scan_res(struct wpa_scan_results *scan_res) { +#ifndef CONFIG_NO_STDOUT_DEBUG size_t i; if (scan_res->res == NULL || scan_res->num == 0) @@ -1122,21 +1531,85 @@ for (i = 0; i < scan_res->num; i++) { struct wpa_scan_res *r = scan_res->res[i]; + u8 *pos; if ((r->flags & (WPA_SCAN_LEVEL_DBM | WPA_SCAN_NOISE_INVALID)) == WPA_SCAN_LEVEL_DBM) { int snr = r->level - r->noise; wpa_printf(MSG_EXCESSIVE, MACSTR " freq=%d qual=%d " - "noise=%d level=%d snr=%d%s flags=0x%x", + "noise=%d level=%d snr=%d%s flags=0x%x " + "age=%u", MAC2STR(r->bssid), r->freq, r->qual, r->noise, r->level, snr, - snr >= GREAT_SNR ? "*" : "", r->flags); + snr >= GREAT_SNR ? "*" : "", r->flags, + r->age); } else { wpa_printf(MSG_EXCESSIVE, MACSTR " freq=%d qual=%d " - "noise=%d level=%d flags=0x%x", + "noise=%d level=%d flags=0x%x age=%u", MAC2STR(r->bssid), r->freq, r->qual, - r->noise, r->level, r->flags); + r->noise, r->level, r->flags, r->age); + } + pos = (u8 *) (r + 1); + if (r->ie_len) + wpa_hexdump(MSG_EXCESSIVE, "IEs", pos, r->ie_len); + pos += r->ie_len; + if (r->beacon_ie_len) + wpa_hexdump(MSG_EXCESSIVE, "Beacon IEs", + pos, r->beacon_ie_len); + } +#endif /* CONFIG_NO_STDOUT_DEBUG */ +} + + +/** + * wpa_supplicant_filter_bssid_match - Is the specified BSSID allowed + * @wpa_s: Pointer to wpa_supplicant data + * @bssid: BSSID to check + * Returns: 0 if the BSSID is filtered or 1 if not + * + * This function is used to filter out specific BSSIDs from scan reslts mainly + * for testing purposes (SET bssid_filter ctrl_iface command). + */ +int wpa_supplicant_filter_bssid_match(struct wpa_supplicant *wpa_s, + const u8 *bssid) +{ + size_t i; + + if (wpa_s->bssid_filter == NULL) + return 1; + + for (i = 0; i < wpa_s->bssid_filter_count; i++) { + if (os_memcmp(wpa_s->bssid_filter + i * ETH_ALEN, bssid, + ETH_ALEN) == 0) + return 1; + } + + return 0; +} + + +static void filter_scan_res(struct wpa_supplicant *wpa_s, + struct wpa_scan_results *res) +{ + size_t i, j; + + if (wpa_s->bssid_filter == NULL) + return; + + for (i = 0, j = 0; i < res->num; i++) { + if (wpa_supplicant_filter_bssid_match(wpa_s, + res->res[i]->bssid)) { + res->res[j++] = res->res[i]; + } else { + os_free(res->res[i]); + res->res[i] = NULL; } } + + if (res->num != j) { + wpa_printf(MSG_DEBUG, "Filtered out %d scan results", + (int) (res->num - j)); + res->num = j; + } } @@ -1164,9 +1637,17 @@ wpa_dbg(wpa_s, MSG_DEBUG, "Failed to get scan results"); return NULL; } + if (scan_res->fetch_time.sec == 0) { + /* + * Make sure we have a valid timestamp if the driver wrapper + * does not set this. + */ + os_get_reltime(&scan_res->fetch_time); + } + filter_scan_res(wpa_s, scan_res); #ifdef CONFIG_WPS - if (wpas_wps_in_progress(wpa_s)) { + if (wpas_wps_searching(wpa_s)) { wpa_dbg(wpa_s, MSG_DEBUG, "WPS: Order scan results with WPS " "provisioning rules"); compar = wpa_scan_result_wps_compar; @@ -1179,13 +1660,26 @@ wpa_bss_update_start(wpa_s); for (i = 0; i < scan_res->num; i++) - wpa_bss_update_scan_res(wpa_s, scan_res->res[i]); + wpa_bss_update_scan_res(wpa_s, scan_res->res[i], + &scan_res->fetch_time); wpa_bss_update_end(wpa_s, info, new_scan); return scan_res; } +/** + * wpa_supplicant_update_scan_results - Update scan results from the driver + * @wpa_s: Pointer to wpa_supplicant data + * Returns: 0 on success, -1 on failure + * + * This function updates the BSS table within wpa_supplicant based on the + * currently available scan results from the driver without requesting a new + * scan. This is used in cases where the driver indicates an association + * (including roaming within ESS) and wpa_supplicant does not yet have the + * needed information to complete the connection (e.g., to perform validation + * steps in 4-way handshake). + */ int wpa_supplicant_update_scan_results(struct wpa_supplicant *wpa_s) { struct wpa_scan_results *scan_res; @@ -1196,3 +1690,114 @@ return 0; } + + +/** + * scan_only_handler - Reports scan results + */ +void scan_only_handler(struct wpa_supplicant *wpa_s, + struct wpa_scan_results *scan_res) +{ + wpa_dbg(wpa_s, MSG_DEBUG, "Scan-only results received"); + if (wpa_s->last_scan_req == MANUAL_SCAN_REQ && + wpa_s->manual_scan_use_id && wpa_s->own_scan_running) { + wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_SCAN_RESULTS "id=%u", + wpa_s->manual_scan_id); + wpa_s->manual_scan_use_id = 0; + } else { + wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_SCAN_RESULTS); + } + wpas_notify_scan_results(wpa_s); + wpas_notify_scan_done(wpa_s, 1); + if (wpa_s->scan_work) { + struct wpa_radio_work *work = wpa_s->scan_work; + wpa_s->scan_work = NULL; + radio_work_done(work); + } +} + + +int wpas_scan_scheduled(struct wpa_supplicant *wpa_s) +{ + return eloop_is_timeout_registered(wpa_supplicant_scan, wpa_s, NULL); +} + + +struct wpa_driver_scan_params * +wpa_scan_clone_params(const struct wpa_driver_scan_params *src) +{ + struct wpa_driver_scan_params *params; + size_t i; + u8 *n; + + params = os_zalloc(sizeof(*params)); + if (params == NULL) + return NULL; + + for (i = 0; i < src->num_ssids; i++) { + if (src->ssids[i].ssid) { + n = os_malloc(src->ssids[i].ssid_len); + if (n == NULL) + goto failed; + os_memcpy(n, src->ssids[i].ssid, + src->ssids[i].ssid_len); + params->ssids[i].ssid = n; + params->ssids[i].ssid_len = src->ssids[i].ssid_len; + } + } + params->num_ssids = src->num_ssids; + + if (src->extra_ies) { + n = os_malloc(src->extra_ies_len); + if (n == NULL) + goto failed; + os_memcpy(n, src->extra_ies, src->extra_ies_len); + params->extra_ies = n; + params->extra_ies_len = src->extra_ies_len; + } + + if (src->freqs) { + int len = int_array_len(src->freqs); + params->freqs = os_malloc((len + 1) * sizeof(int)); + if (params->freqs == NULL) + goto failed; + os_memcpy(params->freqs, src->freqs, (len + 1) * sizeof(int)); + } + + if (src->filter_ssids) { + params->filter_ssids = os_malloc(sizeof(*params->filter_ssids) * + src->num_filter_ssids); + if (params->filter_ssids == NULL) + goto failed; + os_memcpy(params->filter_ssids, src->filter_ssids, + sizeof(*params->filter_ssids) * + src->num_filter_ssids); + params->num_filter_ssids = src->num_filter_ssids; + } + + params->filter_rssi = src->filter_rssi; + params->p2p_probe = src->p2p_probe; + params->only_new_results = src->only_new_results; + + return params; + +failed: + wpa_scan_free_params(params); + return NULL; +} + + +void wpa_scan_free_params(struct wpa_driver_scan_params *params) +{ + size_t i; + + if (params == NULL) + return; + + for (i = 0; i < params->num_ssids; i++) + os_free((u8 *) params->ssids[i].ssid); + os_free((u8 *) params->extra_ies); + os_free(params->freqs); + os_free(params->filter_ssids); + os_free(params); +} diff -Nru wpa-1.0/wpa_supplicant/scan.h wpa-2.1/wpa_supplicant/scan.h --- wpa-1.0/wpa_supplicant/scan.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/scan.h 2014-02-04 11:23:35.000000000 +0000 @@ -1,26 +1,21 @@ /* * WPA Supplicant - Scanning - * Copyright (c) 2003-2010, Jouni Malinen + * Copyright (c) 2003-2014, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef SCAN_H #define SCAN_H -int wpa_supplicant_enabled_networks(struct wpa_config *conf); +int wpa_supplicant_enabled_networks(struct wpa_supplicant *wpa_s); void wpa_supplicant_req_scan(struct wpa_supplicant *wpa_s, int sec, int usec); int wpa_supplicant_delayed_sched_scan(struct wpa_supplicant *wpa_s, int sec, int usec); int wpa_supplicant_req_sched_scan(struct wpa_supplicant *wpa_s); void wpa_supplicant_cancel_scan(struct wpa_supplicant *wpa_s); +void wpa_supplicant_cancel_delayed_sched_scan(struct wpa_supplicant *wpa_s); void wpa_supplicant_cancel_sched_scan(struct wpa_supplicant *wpa_s); void wpa_supplicant_notify_scanning(struct wpa_supplicant *wpa_s, int scanning); @@ -34,9 +29,22 @@ const u8 * wpa_scan_get_ie(const struct wpa_scan_res *res, u8 ie); const u8 * wpa_scan_get_vendor_ie(const struct wpa_scan_res *res, u32 vendor_type); +const u8 * wpa_scan_get_vendor_ie_beacon(const struct wpa_scan_res *res, + u32 vendor_type); struct wpabuf * wpa_scan_get_vendor_ie_multi(const struct wpa_scan_res *res, u32 vendor_type); -struct wpabuf * wpa_scan_get_vendor_ie_multi_beacon( - const struct wpa_scan_res *res, u32 vendor_type); +int wpa_supplicant_filter_bssid_match(struct wpa_supplicant *wpa_s, + const u8 *bssid); +void wpa_supplicant_update_scan_int(struct wpa_supplicant *wpa_s, int sec); +void scan_only_handler(struct wpa_supplicant *wpa_s, + struct wpa_scan_results *scan_res); +int wpas_scan_scheduled(struct wpa_supplicant *wpa_s); +int wpa_supplicant_start_sched_scan(struct wpa_supplicant *wpa_s, + struct wpa_driver_scan_params *params, + int interval); +int wpa_supplicant_stop_sched_scan(struct wpa_supplicant *wpa_s); +struct wpa_driver_scan_params * +wpa_scan_clone_params(const struct wpa_driver_scan_params *src); +void wpa_scan_free_params(struct wpa_driver_scan_params *params); #endif /* SCAN_H */ diff -Nru wpa-1.0/wpa_supplicant/sme.c wpa-2.1/wpa_supplicant/sme.c --- wpa-1.0/wpa_supplicant/sme.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/sme.c 2014-02-04 11:23:35.000000000 +0000 @@ -1,15 +1,9 @@ /* * wpa_supplicant - SME - * Copyright (c) 2009-2010, Jouni Malinen + * Copyright (c) 2009-2014, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -20,6 +14,7 @@ #include "common/ieee802_11_common.h" #include "eapol_supp/eapol_supp_sm.h" #include "common/wpa_common.h" +#include "common/sae.h" #include "rsn_supp/wpa.h" #include "rsn_supp/pmksa_cache.h" #include "config.h" @@ -29,23 +24,122 @@ #include "wps_supplicant.h" #include "p2p_supplicant.h" #include "notify.h" -#include "blacklist.h" #include "bss.h" #include "scan.h" #include "sme.h" +#include "hs20_supplicant.h" #define SME_AUTH_TIMEOUT 5 #define SME_ASSOC_TIMEOUT 5 static void sme_auth_timer(void *eloop_ctx, void *timeout_ctx); static void sme_assoc_timer(void *eloop_ctx, void *timeout_ctx); +static void sme_obss_scan_timeout(void *eloop_ctx, void *timeout_ctx); #ifdef CONFIG_IEEE80211W static void sme_stop_sa_query(struct wpa_supplicant *wpa_s); #endif /* CONFIG_IEEE80211W */ -void sme_authenticate(struct wpa_supplicant *wpa_s, - struct wpa_bss *bss, struct wpa_ssid *ssid) +#ifdef CONFIG_SAE + +static int index_within_array(const int *array, int idx) +{ + int i; + for (i = 0; i < idx; i++) { + if (array[i] <= 0) + return 0; + } + return 1; +} + + +static int sme_set_sae_group(struct wpa_supplicant *wpa_s) +{ + int *groups = wpa_s->conf->sae_groups; + int default_groups[] = { 19, 20, 21, 25, 26, 0 }; + + if (!groups || groups[0] <= 0) + groups = default_groups; + + /* Configuration may have changed, so validate current index */ + if (!index_within_array(groups, wpa_s->sme.sae_group_index)) + return -1; + + for (;;) { + int group = groups[wpa_s->sme.sae_group_index]; + if (group < 0) + break; + if (sae_set_group(&wpa_s->sme.sae, group) == 0) { + wpa_dbg(wpa_s, MSG_DEBUG, "SME: Selected SAE group %d", + wpa_s->sme.sae.group); + return 0; + } + wpa_s->sme.sae_group_index++; + } + + return -1; +} + + +static struct wpabuf * sme_auth_build_sae_commit(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid, + const u8 *bssid) +{ + struct wpabuf *buf; + size_t len; + + if (ssid->passphrase == NULL) { + wpa_printf(MSG_DEBUG, "SAE: No password available"); + return NULL; + } + + if (sme_set_sae_group(wpa_s) < 0) { + wpa_printf(MSG_DEBUG, "SAE: Failed to select group"); + return NULL; + } + + if (sae_prepare_commit(wpa_s->own_addr, bssid, + (u8 *) ssid->passphrase, + os_strlen(ssid->passphrase), + &wpa_s->sme.sae) < 0) { + wpa_printf(MSG_DEBUG, "SAE: Could not pick PWE"); + return NULL; + } + + len = wpa_s->sme.sae_token ? wpabuf_len(wpa_s->sme.sae_token) : 0; + buf = wpabuf_alloc(4 + SAE_COMMIT_MAX_LEN + len); + if (buf == NULL) + return NULL; + + wpabuf_put_le16(buf, 1); /* Transaction seq# */ + wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS); + sae_write_commit(&wpa_s->sme.sae, buf, wpa_s->sme.sae_token); + + return buf; +} + + +static struct wpabuf * sme_auth_build_sae_confirm(struct wpa_supplicant *wpa_s) +{ + struct wpabuf *buf; + + buf = wpabuf_alloc(4 + SAE_CONFIRM_MAX_LEN); + if (buf == NULL) + return NULL; + + wpabuf_put_le16(buf, 2); /* Transaction seq# */ + wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS); + sae_write_confirm(&wpa_s->sme.sae, buf); + + return buf; +} + +#endif /* CONFIG_SAE */ + + +static void sme_send_authentication(struct wpa_supplicant *wpa_s, + struct wpa_bss *bss, struct wpa_ssid *ssid, + int start) { struct wpa_driver_auth_params params; struct wpa_ssid *old_ssid; @@ -56,10 +150,14 @@ const u8 *md = NULL; #endif /* CONFIG_IEEE80211R */ int i, bssid_changed; + struct wpabuf *resp = NULL; + u8 ext_capab[10]; + int ext_capab_len; if (bss == NULL) { wpa_msg(wpa_s, MSG_ERROR, "SME: No scan result available for " "the network"); + wpas_connect_work_done(wpa_s); return; } @@ -100,6 +198,21 @@ wpa_dbg(wpa_s, MSG_DEBUG, "Overriding auth_alg selection: " "0x%x", params.auth_alg); } +#ifdef CONFIG_SAE + if (wpa_key_mgmt_sae(ssid->key_mgmt)) { + const u8 *rsn; + struct wpa_ie_data ied; + + rsn = wpa_bss_get_ie(bss, WLAN_EID_RSN); + if (rsn && + wpa_parse_wpa_ie(rsn, 2 + rsn[1], &ied) == 0) { + if (wpa_key_mgmt_sae(ied.key_mgmt)) { + wpa_dbg(wpa_s, MSG_DEBUG, "Using SAE auth_alg"); + params.auth_alg = WPA_AUTH_ALG_SAE; + } + } + } +#endif /* CONFIG_SAE */ for (i = 0; i < NUM_WEP_KEYS; i++) { if (ssid->wep_key_len[i]) @@ -116,13 +229,11 @@ if ((wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE) || wpa_bss_get_ie(bss, WLAN_EID_RSN)) && - (ssid->key_mgmt & (WPA_KEY_MGMT_IEEE8021X | WPA_KEY_MGMT_PSK | - WPA_KEY_MGMT_FT_IEEE8021X | - WPA_KEY_MGMT_FT_PSK | - WPA_KEY_MGMT_IEEE8021X_SHA256 | - WPA_KEY_MGMT_PSK_SHA256))) { + wpa_key_mgmt_wpa(ssid->key_mgmt)) { int try_opportunistic; - try_opportunistic = ssid->proactive_key_caching && + try_opportunistic = (ssid->proactive_key_caching < 0 ? + wpa_s->conf->okc : + ssid->proactive_key_caching) && (ssid->proto & WPA_PROTO_RSN); if (pmksa_cache_set_current(wpa_s->wpa, NULL, bss->bssid, wpa_s->current_ssid, @@ -134,13 +245,19 @@ &wpa_s->sme.assoc_req_ie_len)) { wpa_msg(wpa_s, MSG_WARNING, "SME: Failed to set WPA " "key management and encryption suites"); + wpas_connect_work_done(wpa_s); return; } - } else if (ssid->key_mgmt & - (WPA_KEY_MGMT_PSK | WPA_KEY_MGMT_IEEE8021X | - WPA_KEY_MGMT_WPA_NONE | WPA_KEY_MGMT_FT_PSK | - WPA_KEY_MGMT_FT_IEEE8021X | WPA_KEY_MGMT_PSK_SHA256 | - WPA_KEY_MGMT_IEEE8021X_SHA256)) { + } else if ((ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA) && + wpa_key_mgmt_wpa_ieee8021x(ssid->key_mgmt)) { + /* + * Both WPA and non-WPA IEEE 802.1X enabled in configuration - + * use non-WPA since the scan results did not indicate that the + * AP is using WPA or WPA2. + */ + wpa_supplicant_set_non_wpa_policy(wpa_s, ssid); + wpa_s->sme.assoc_req_ie_len = 0; + } else if (wpa_key_mgmt_wpa_any(ssid->key_mgmt)) { wpa_s->sme.assoc_req_ie_len = sizeof(wpa_s->sme.assoc_req_ie); if (wpa_supplicant_set_suites(wpa_s, NULL, ssid, wpa_s->sme.assoc_req_ie, @@ -148,6 +265,7 @@ wpa_msg(wpa_s, MSG_WARNING, "SME: Failed to set WPA " "key management and encryption suites (no " "scan results)"); + wpas_connect_work_done(wpa_s); return; } #ifdef CONFIG_WPS @@ -179,8 +297,7 @@ wpa_ft_prepare_auth_request(wpa_s->wpa, ie); } - if (md && ssid->key_mgmt & (WPA_KEY_MGMT_FT_PSK | - WPA_KEY_MGMT_FT_IEEE8021X)) { + if (md && wpa_key_mgmt_ft(ssid->key_mgmt)) { if (wpa_s->sme.assoc_req_ie_len + 5 < sizeof(wpa_s->sme.assoc_req_ie)) { struct rsn_mdie *mdie; @@ -208,8 +325,9 @@ #endif /* CONFIG_IEEE80211R */ #ifdef CONFIG_IEEE80211W - wpa_s->sme.mfp = ssid->ieee80211w; - if (ssid->ieee80211w != NO_MGMT_FRAME_PROTECTION) { + wpa_s->sme.mfp = ssid->ieee80211w == MGMT_FRAME_PROTECTION_DEFAULT ? + wpa_s->conf->pmf : ssid->ieee80211w; + if (wpa_s->sme.mfp != NO_MGMT_FRAME_PROTECTION) { const u8 *rsn = wpa_bss_get_ie(bss, WLAN_EID_RSN); struct wpa_ie_data _ie; if (rsn && wpa_parse_wpa_ie(rsn, 2 + rsn[1], &_ie) == 0 && @@ -237,23 +355,49 @@ } #endif /* CONFIG_P2P */ -#ifdef CONFIG_INTERWORKING - if (wpa_s->conf->interworking) { +#ifdef CONFIG_HS20 + if (is_hs20_network(wpa_s, ssid, bss)) { + struct wpabuf *hs20; + hs20 = wpabuf_alloc(20); + if (hs20) { + wpas_hs20_add_indication(hs20); + os_memcpy(wpa_s->sme.assoc_req_ie + + wpa_s->sme.assoc_req_ie_len, + wpabuf_head(hs20), wpabuf_len(hs20)); + wpa_s->sme.assoc_req_ie_len += wpabuf_len(hs20); + wpabuf_free(hs20); + } + } +#endif /* CONFIG_HS20 */ + + ext_capab_len = wpas_build_ext_capab(wpa_s, ext_capab); + if (ext_capab_len > 0) { u8 *pos = wpa_s->sme.assoc_req_ie; if (wpa_s->sme.assoc_req_ie_len > 0 && pos[0] == WLAN_EID_RSN) pos += 2 + pos[1]; - os_memmove(pos + 6, pos, + os_memmove(pos + ext_capab_len, pos, wpa_s->sme.assoc_req_ie_len - (pos - wpa_s->sme.assoc_req_ie)); - wpa_s->sme.assoc_req_ie_len += 6; - *pos++ = WLAN_EID_EXT_CAPAB; - *pos++ = 4; - *pos++ = 0x00; - *pos++ = 0x00; - *pos++ = 0x00; - *pos++ = 0x80; /* Bit 31 - Interworking */ + wpa_s->sme.assoc_req_ie_len += ext_capab_len; + os_memcpy(pos, ext_capab, ext_capab_len); + } + +#ifdef CONFIG_SAE + if (params.auth_alg == WPA_AUTH_ALG_SAE) { + if (start) + resp = sme_auth_build_sae_commit(wpa_s, ssid, + bss->bssid); + else + resp = sme_auth_build_sae_confirm(wpa_s); + if (resp == NULL) { + wpas_connect_work_done(wpa_s); + return; + } + params.sae_data = wpabuf_head(resp); + params.sae_data_len = wpabuf_len(resp); + wpa_s->sme.sae.state = start ? SAE_COMMITTED : SAE_CONFIRMED; } -#endif /* CONFIG_INTERWORKING */ +#endif /* CONFIG_SAE */ wpa_supplicant_cancel_sched_scan(wpa_s); wpa_supplicant_cancel_scan(wpa_s); @@ -277,6 +421,8 @@ "driver failed"); wpas_connection_failed(wpa_s, bss->bssid); wpa_supplicant_mark_disassoc(wpa_s); + wpabuf_free(resp); + wpas_connect_work_done(wpa_s); return; } @@ -287,9 +433,144 @@ * Association will be started based on the authentication event from * the driver. */ + + wpabuf_free(resp); +} + + +static void sme_auth_start_cb(struct wpa_radio_work *work, int deinit) +{ + struct wpa_connect_work *cwork = work->ctx; + struct wpa_supplicant *wpa_s = work->wpa_s; + + if (deinit) { + wpas_connect_work_free(cwork); + return; + } + + wpa_s->connect_work = work; + + if (!wpas_valid_bss_ssid(wpa_s, cwork->bss, cwork->ssid)) { + wpa_dbg(wpa_s, MSG_DEBUG, "SME: BSS/SSID entry for authentication not valid anymore - drop connection attempt"); + wpas_connect_work_done(wpa_s); + return; + } + + sme_send_authentication(wpa_s, cwork->bss, cwork->ssid, 1); +} + + +void sme_authenticate(struct wpa_supplicant *wpa_s, + struct wpa_bss *bss, struct wpa_ssid *ssid) +{ + struct wpa_connect_work *cwork; + + if (bss == NULL || ssid == NULL) + return; + if (wpa_s->connect_work) { + wpa_dbg(wpa_s, MSG_DEBUG, "SME: Reject sme_authenticate() call since connect_work exist"); + return; + } + + cwork = os_zalloc(sizeof(*cwork)); + if (cwork == NULL) + return; + cwork->bss = bss; + cwork->ssid = ssid; + cwork->sme = 1; + +#ifdef CONFIG_SAE + wpa_s->sme.sae.state = SAE_NOTHING; + wpa_s->sme.sae.send_confirm = 0; + wpa_s->sme.sae_group_index = 0; +#endif /* CONFIG_SAE */ + + if (radio_add_work(wpa_s, bss->freq, "sme-connect", 1, + sme_auth_start_cb, cwork) < 0) + wpas_connect_work_free(cwork); } +#ifdef CONFIG_SAE + +static int sme_sae_auth(struct wpa_supplicant *wpa_s, u16 auth_transaction, + u16 status_code, const u8 *data, size_t len) +{ + wpa_dbg(wpa_s, MSG_DEBUG, "SME: SAE authentication transaction %u " + "status code %u", auth_transaction, status_code); + + if (auth_transaction == 1 && + status_code == WLAN_STATUS_ANTI_CLOGGING_TOKEN_REQ && + wpa_s->sme.sae.state == SAE_COMMITTED && + wpa_s->current_bss && wpa_s->current_ssid) { + wpa_dbg(wpa_s, MSG_DEBUG, "SME: SAE anti-clogging token " + "requested"); + wpabuf_free(wpa_s->sme.sae_token); + wpa_s->sme.sae_token = wpabuf_alloc_copy(data, len); + sme_send_authentication(wpa_s, wpa_s->current_bss, + wpa_s->current_ssid, 1); + return 0; + } + + if (auth_transaction == 1 && + status_code == WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED && + wpa_s->sme.sae.state == SAE_COMMITTED && + wpa_s->current_bss && wpa_s->current_ssid) { + wpa_dbg(wpa_s, MSG_DEBUG, "SME: SAE group not supported"); + wpa_s->sme.sae_group_index++; + if (sme_set_sae_group(wpa_s) < 0) + return -1; /* no other groups enabled */ + wpa_dbg(wpa_s, MSG_DEBUG, "SME: Try next enabled SAE group"); + sme_send_authentication(wpa_s, wpa_s->current_bss, + wpa_s->current_ssid, 1); + return 0; + } + + if (status_code != WLAN_STATUS_SUCCESS) + return -1; + + if (auth_transaction == 1) { + int *groups = wpa_s->conf->sae_groups; + + wpa_dbg(wpa_s, MSG_DEBUG, "SME SAE commit"); + if (wpa_s->current_bss == NULL || + wpa_s->current_ssid == NULL) + return -1; + if (wpa_s->sme.sae.state != SAE_COMMITTED) + return -1; + if (groups && groups[0] <= 0) + groups = NULL; + if (sae_parse_commit(&wpa_s->sme.sae, data, len, NULL, NULL, + groups) != WLAN_STATUS_SUCCESS) + return -1; + + if (sae_process_commit(&wpa_s->sme.sae) < 0) { + wpa_printf(MSG_DEBUG, "SAE: Failed to process peer " + "commit"); + return -1; + } + + wpabuf_free(wpa_s->sme.sae_token); + wpa_s->sme.sae_token = NULL; + sme_send_authentication(wpa_s, wpa_s->current_bss, + wpa_s->current_ssid, 0); + return 0; + } else if (auth_transaction == 2) { + wpa_dbg(wpa_s, MSG_DEBUG, "SME SAE confirm"); + if (wpa_s->sme.sae.state != SAE_CONFIRMED) + return -1; + if (sae_check_confirm(&wpa_s->sme.sae, data, len) < 0) + return -1; + wpa_s->sme.sae.state = SAE_ACCEPTED; + sae_clear_temp_data(&wpa_s->sme.sae); + return 1; + } + + return -1; +} +#endif /* CONFIG_SAE */ + + void sme_event_auth(struct wpa_supplicant *wpa_s, union wpa_event_data *data) { struct wpa_ssid *ssid = wpa_s->current_ssid; @@ -314,14 +595,34 @@ } wpa_dbg(wpa_s, MSG_DEBUG, "SME: Authentication response: peer=" MACSTR - " auth_type=%d status_code=%d", + " auth_type=%d auth_transaction=%d status_code=%d", MAC2STR(data->auth.peer), data->auth.auth_type, - data->auth.status_code); + data->auth.auth_transaction, data->auth.status_code); wpa_hexdump(MSG_MSGDUMP, "SME: Authentication response IEs", data->auth.ies, data->auth.ies_len); eloop_cancel_timeout(sme_auth_timer, wpa_s, NULL); +#ifdef CONFIG_SAE + if (data->auth.auth_type == WLAN_AUTH_SAE) { + int res; + res = sme_sae_auth(wpa_s, data->auth.auth_transaction, + data->auth.status_code, data->auth.ies, + data->auth.ies_len); + if (res < 0) { + wpas_connection_failed(wpa_s, wpa_s->pending_bssid); + wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED); + + } + if (res != 1) + return; + + wpa_printf(MSG_DEBUG, "SME: SAE completed - setting PMK for " + "4-way handshake"); + wpa_sm_set_pmk(wpa_s->wpa, wpa_s->sme.sae.pmk, PMK_LEN); + } +#endif /* CONFIG_SAE */ + if (data->auth.status_code != WLAN_STATUS_SUCCESS) { wpa_dbg(wpa_s, MSG_DEBUG, "SME: Authentication failed (status " "code %d)", data->auth.status_code); @@ -331,9 +632,12 @@ wpa_s->sme.auth_alg == data->auth.auth_type || wpa_s->current_ssid->auth_alg == WPA_AUTH_ALG_LEAP) { wpas_connection_failed(wpa_s, wpa_s->pending_bssid); + wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED); return; } + wpas_connect_work_done(wpa_s); + switch (data->auth.auth_type) { case WLAN_AUTH_OPEN: wpa_s->current_ssid->auth_alg = WPA_AUTH_ALG_SHARED; @@ -377,17 +681,41 @@ { struct wpa_driver_associate_params params; struct ieee802_11_elems elems; +#ifdef CONFIG_HT_OVERRIDES + struct ieee80211_ht_capabilities htcaps; + struct ieee80211_ht_capabilities htcaps_mask; +#endif /* CONFIG_HT_OVERRIDES */ +#ifdef CONFIG_VHT_OVERRIDES + struct ieee80211_vht_capabilities vhtcaps; + struct ieee80211_vht_capabilities vhtcaps_mask; +#endif /* CONFIG_VHT_OVERRIDES */ os_memset(¶ms, 0, sizeof(params)); params.bssid = bssid; params.ssid = wpa_s->sme.ssid; params.ssid_len = wpa_s->sme.ssid_len; params.freq = wpa_s->sme.freq; + params.bg_scan_period = wpa_s->current_ssid ? + wpa_s->current_ssid->bg_scan_period : -1; params.wpa_ie = wpa_s->sme.assoc_req_ie_len ? wpa_s->sme.assoc_req_ie : NULL; params.wpa_ie_len = wpa_s->sme.assoc_req_ie_len; - params.pairwise_suite = cipher_suite2driver(wpa_s->pairwise_cipher); - params.group_suite = cipher_suite2driver(wpa_s->group_cipher); + params.pairwise_suite = wpa_s->pairwise_cipher; + params.group_suite = wpa_s->group_cipher; +#ifdef CONFIG_HT_OVERRIDES + os_memset(&htcaps, 0, sizeof(htcaps)); + os_memset(&htcaps_mask, 0, sizeof(htcaps_mask)); + params.htcaps = (u8 *) &htcaps; + params.htcaps_mask = (u8 *) &htcaps_mask; + wpa_supplicant_apply_ht_overrides(wpa_s, wpa_s->current_ssid, ¶ms); +#endif /* CONFIG_HT_OVERRIDES */ +#ifdef CONFIG_VHT_OVERRIDES + os_memset(&vhtcaps, 0, sizeof(vhtcaps)); + os_memset(&vhtcaps_mask, 0, sizeof(vhtcaps_mask)); + params.vhtcaps = &vhtcaps; + params.vhtcaps_mask = &vhtcaps_mask; + wpa_supplicant_apply_vht_overrides(wpa_s, wpa_s->current_ssid, ¶ms); +#endif /* CONFIG_VHT_OVERRIDES */ #ifdef CONFIG_IEEE80211R if (auth_type == WLAN_AUTH_FT && wpa_s->sme.ft_ies) { params.wpa_ie = wpa_s->sme.ft_ies; @@ -434,6 +762,7 @@ wpa_msg(wpa_s, MSG_INFO, "SME: Association request to the " "driver failed"); wpas_connection_failed(wpa_s, wpa_s->pending_bssid); + wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED); os_memset(wpa_s->pending_bssid, 0, ETH_ALEN); return; } @@ -528,7 +857,7 @@ void sme_event_disassoc(struct wpa_supplicant *wpa_s, - union wpa_event_data *data) + struct disassoc_info *info) { wpa_dbg(wpa_s, MSG_DEBUG, "SME: Disassociation event received"); if (wpa_s->sme.prev_bssid_set) { @@ -606,9 +935,259 @@ #ifdef CONFIG_IEEE80211W sme_stop_sa_query(wpa_s); #endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_SAE + wpabuf_free(wpa_s->sme.sae_token); + wpa_s->sme.sae_token = NULL; + sae_clear_data(&wpa_s->sme.sae); +#endif /* CONFIG_SAE */ eloop_cancel_timeout(sme_assoc_timer, wpa_s, NULL); eloop_cancel_timeout(sme_auth_timer, wpa_s, NULL); + eloop_cancel_timeout(sme_obss_scan_timeout, wpa_s, NULL); +} + + +static void sme_send_2040_bss_coex(struct wpa_supplicant *wpa_s, + const u8 *chan_list, u8 num_channels, + u8 num_intol) +{ + struct ieee80211_2040_bss_coex_ie *bc_ie; + struct ieee80211_2040_intol_chan_report *ic_report; + struct wpabuf *buf; + + wpa_printf(MSG_DEBUG, "SME: Send 20/40 BSS Coexistence to " MACSTR, + MAC2STR(wpa_s->bssid)); + + buf = wpabuf_alloc(2 + /* action.category + action_code */ + sizeof(struct ieee80211_2040_bss_coex_ie) + + sizeof(struct ieee80211_2040_intol_chan_report) + + num_channels); + if (buf == NULL) + return; + + wpabuf_put_u8(buf, WLAN_ACTION_PUBLIC); + wpabuf_put_u8(buf, WLAN_PA_20_40_BSS_COEX); + + bc_ie = wpabuf_put(buf, sizeof(*bc_ie)); + bc_ie->element_id = WLAN_EID_20_40_BSS_COEXISTENCE; + bc_ie->length = 1; + if (num_intol) + bc_ie->coex_param |= WLAN_20_40_BSS_COEX_20MHZ_WIDTH_REQ; + + if (num_channels > 0) { + ic_report = wpabuf_put(buf, sizeof(*ic_report)); + ic_report->element_id = WLAN_EID_20_40_BSS_INTOLERANT; + ic_report->length = num_channels + 1; + ic_report->op_class = 0; + os_memcpy(wpabuf_put(buf, num_channels), chan_list, + num_channels); + } + + if (wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid, + wpa_s->own_addr, wpa_s->bssid, + wpabuf_head(buf), wpabuf_len(buf), 0) < 0) { + wpa_msg(wpa_s, MSG_INFO, + "SME: Failed to send 20/40 BSS Coexistence frame"); + } + + wpabuf_free(buf); +} + + +int sme_proc_obss_scan(struct wpa_supplicant *wpa_s) +{ + struct wpa_bss *bss; + const u8 *ie; + u16 ht_cap; + u8 chan_list[P2P_MAX_CHANNELS], channel; + u8 num_channels = 0, num_intol = 0, i; + + if (!wpa_s->sme.sched_obss_scan) + return 0; + + wpa_s->sme.sched_obss_scan = 0; + if (!wpa_s->current_bss || wpa_s->wpa_state != WPA_COMPLETED) + return 1; + + /* + * Check whether AP uses regulatory triplet or channel triplet in + * country info. Right now the operating class of the BSS channel + * width trigger event is "unknown" (IEEE Std 802.11-2012 10.15.12), + * based on the assumption that operating class triplet is not used in + * beacon frame. If the First Channel Number/Operating Extension + * Identifier octet has a positive integer value of 201 or greater, + * then its operating class triplet. + * + * TODO: If Supported Operating Classes element is present in beacon + * frame, have to lookup operating class in Annex E and fill them in + * 2040 coex frame. + */ + ie = wpa_bss_get_ie(wpa_s->current_bss, WLAN_EID_COUNTRY); + if (ie && (ie[1] >= 6) && (ie[5] >= 201)) + return 1; + + os_memset(chan_list, 0, sizeof(chan_list)); + + dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) { + /* Skip other band bss */ + enum hostapd_hw_mode mode; + mode = ieee80211_freq_to_chan(bss->freq, &channel); + if (mode != HOSTAPD_MODE_IEEE80211G && + mode != HOSTAPD_MODE_IEEE80211B) + continue; + + ie = wpa_bss_get_ie(bss, WLAN_EID_HT_CAP); + ht_cap = (ie && (ie[1] == 26)) ? WPA_GET_LE16(ie + 2) : 0; + + if (!ht_cap || (ht_cap & HT_CAP_INFO_40MHZ_INTOLERANT)) { + /* Check whether the channel is already considered */ + for (i = 0; i < num_channels; i++) { + if (channel == chan_list[i]) + break; + } + if (i != num_channels) + continue; + + if (ht_cap & HT_CAP_INFO_40MHZ_INTOLERANT) + num_intol++; + + chan_list[num_channels++] = channel; + } + } + + sme_send_2040_bss_coex(wpa_s, chan_list, num_channels, num_intol); + return 1; +} + + +static struct hostapd_hw_modes * get_mode(struct hostapd_hw_modes *modes, + u16 num_modes, + enum hostapd_hw_mode mode) +{ + u16 i; + + for (i = 0; i < num_modes; i++) { + if (modes[i].mode == mode) + return &modes[i]; + } + + return NULL; +} + + +static void wpa_setband_scan_freqs_list(struct wpa_supplicant *wpa_s, + enum hostapd_hw_mode band, + struct wpa_driver_scan_params *params) +{ + /* Include only supported channels for the specified band */ + struct hostapd_hw_modes *mode; + int count, i; + + mode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes, band); + if (mode == NULL) { + /* No channels supported in this band - use empty list */ + params->freqs = os_zalloc(sizeof(int)); + return; + } + + params->freqs = os_calloc(mode->num_channels + 1, sizeof(int)); + if (params->freqs == NULL) + return; + for (count = 0, i = 0; i < mode->num_channels; i++) { + if (mode->channels[i].flag & HOSTAPD_CHAN_DISABLED) + continue; + params->freqs[count++] = mode->channels[i].freq; + } +} + + +static void sme_obss_scan_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + struct wpa_driver_scan_params params; + + if (!wpa_s->current_bss) { + wpa_printf(MSG_DEBUG, "SME OBSS: Ignore scan request"); + return; + } + + os_memset(¶ms, 0, sizeof(params)); + wpa_setband_scan_freqs_list(wpa_s, HOSTAPD_MODE_IEEE80211G, ¶ms); + wpa_printf(MSG_DEBUG, "SME OBSS: Request an OBSS scan"); + + if (wpa_supplicant_trigger_scan(wpa_s, ¶ms)) + wpa_printf(MSG_DEBUG, "SME OBSS: Failed to trigger scan"); + else + wpa_s->sme.sched_obss_scan = 1; + os_free(params.freqs); + + eloop_register_timeout(wpa_s->sme.obss_scan_int, 0, + sme_obss_scan_timeout, wpa_s, NULL); +} + + +void sme_sched_obss_scan(struct wpa_supplicant *wpa_s, int enable) +{ + const u8 *ie; + struct wpa_bss *bss = wpa_s->current_bss; + struct wpa_ssid *ssid = wpa_s->current_ssid; + struct hostapd_hw_modes *hw_mode = NULL; + int i; + + eloop_cancel_timeout(sme_obss_scan_timeout, wpa_s, NULL); + wpa_s->sme.sched_obss_scan = 0; + if (!enable) + return; + + /* + * Schedule OBSS scan if driver is using station SME in wpa_supplicant + * or it expects OBSS scan to be performed by wpa_supplicant. + */ + if (!((wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) || + (wpa_s->drv_flags & WPA_DRIVER_FLAGS_OBSS_SCAN)) || + ssid == NULL || ssid->mode != IEEE80211_MODE_INFRA) + return; + + if (!wpa_s->hw.modes) + return; + + /* only HT caps in 11g mode are relevant */ + for (i = 0; i < wpa_s->hw.num_modes; i++) { + hw_mode = &wpa_s->hw.modes[i]; + if (hw_mode->mode == HOSTAPD_MODE_IEEE80211G) + break; + } + + /* Driver does not support HT40 for 11g or doesn't have 11g. */ + if (i == wpa_s->hw.num_modes || !hw_mode || + !(hw_mode->ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)) + return; + + if (bss == NULL || bss->freq < 2400 || bss->freq > 2500) + return; /* Not associated on 2.4 GHz band */ + + /* Check whether AP supports HT40 */ + ie = wpa_bss_get_ie(wpa_s->current_bss, WLAN_EID_HT_CAP); + if (!ie || ie[1] < 2 || + !(WPA_GET_LE16(ie + 2) & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)) + return; /* AP does not support HT40 */ + + ie = wpa_bss_get_ie(wpa_s->current_bss, + WLAN_EID_OVERLAPPING_BSS_SCAN_PARAMS); + if (!ie || ie[1] < 14) + return; /* AP does not request OBSS scans */ + + wpa_s->sme.obss_scan_int = WPA_GET_LE16(ie + 6); + if (wpa_s->sme.obss_scan_int < 10) { + wpa_printf(MSG_DEBUG, "SME: Invalid OBSS Scan Interval %u " + "replaced with the minimum 10 sec", + wpa_s->sme.obss_scan_int); + wpa_s->sme.obss_scan_int = 10; + } + wpa_printf(MSG_DEBUG, "SME: OBSS Scan Interval %u sec", + wpa_s->sme.obss_scan_int); + eloop_register_timeout(wpa_s->sme.obss_scan_int, 0, + sme_obss_scan_timeout, wpa_s, NULL); } @@ -620,9 +1199,9 @@ static int sme_check_sa_query_timeout(struct wpa_supplicant *wpa_s) { u32 tu; - struct os_time now, passed; - os_get_time(&now); - os_time_sub(&now, &wpa_s->sme.sa_query_start, &passed); + struct os_reltime now, passed; + os_get_reltime(&now); + os_reltime_sub(&now, &wpa_s->sme.sa_query_start, &passed); tu = (passed.sec * 1000000 + passed.usec) / 1024; if (sa_query_max_timeout < tu) { wpa_dbg(wpa_s, MSG_DEBUG, "SME: SA Query timed out"); @@ -665,14 +1244,14 @@ sme_check_sa_query_timeout(wpa_s)) return; - nbuf = os_realloc(wpa_s->sme.sa_query_trans_id, - (wpa_s->sme.sa_query_count + 1) * - WLAN_SA_QUERY_TR_ID_LEN); + nbuf = os_realloc_array(wpa_s->sme.sa_query_trans_id, + wpa_s->sme.sa_query_count + 1, + WLAN_SA_QUERY_TR_ID_LEN); if (nbuf == NULL) return; if (wpa_s->sme.sa_query_count == 0) { /* Starting a new SA Query procedure */ - os_get_time(&wpa_s->sme.sa_query_start); + os_get_reltime(&wpa_s->sme.sa_query_start); } trans_id = nbuf + wpa_s->sme.sa_query_count * WLAN_SA_QUERY_TR_ID_LEN; wpa_s->sme.sa_query_trans_id = nbuf; @@ -698,7 +1277,7 @@ } -void sme_stop_sa_query(struct wpa_supplicant *wpa_s) +static void sme_stop_sa_query(struct wpa_supplicant *wpa_s) { eloop_cancel_timeout(sme_sa_query_timer, wpa_s, NULL); os_free(wpa_s->sme.sa_query_trans_id); @@ -712,12 +1291,12 @@ { struct wpa_ssid *ssid; - if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME)) - return; if (wpa_s->wpa_state != WPA_COMPLETED) return; ssid = wpa_s->current_ssid; - if (ssid == NULL || ssid->ieee80211w == 0) + if (ssid == NULL || + (ssid->ieee80211w == MGMT_FRAME_PROTECTION_DEFAULT ? + wpa_s->conf->pmf : ssid->ieee80211w) == NO_MGMT_FRAME_PROTECTION) return; if (os_memcmp(sa, wpa_s->bssid, ETH_ALEN) != 0) return; diff -Nru wpa-1.0/wpa_supplicant/sme.h wpa-2.1/wpa_supplicant/sme.h --- wpa-1.0/wpa_supplicant/sme.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/sme.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * wpa_supplicant - SME * Copyright (c) 2009-2010, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef SME_H @@ -31,7 +25,7 @@ void sme_event_assoc_timed_out(struct wpa_supplicant *wpa_s, union wpa_event_data *data); void sme_event_disassoc(struct wpa_supplicant *wpa_s, - union wpa_event_data *data); + struct disassoc_info *info); void sme_event_unprot_disconnect(struct wpa_supplicant *wpa_s, const u8 *sa, const u8 *da, u16 reason_code); void sme_sa_query_rx(struct wpa_supplicant *wpa_s, const u8 *sa, @@ -41,6 +35,9 @@ const u8 *prev_pending_bssid); void sme_deinit(struct wpa_supplicant *wpa_s); +int sme_proc_obss_scan(struct wpa_supplicant *wpa_s); +void sme_sched_obss_scan(struct wpa_supplicant *wpa_s, int enable); + #else /* CONFIG_SME */ static inline void sme_authenticate(struct wpa_supplicant *wpa_s, @@ -77,7 +74,7 @@ } static inline void sme_event_disassoc(struct wpa_supplicant *wpa_s, - union wpa_event_data *data) + struct disassoc_info *info) { } @@ -101,6 +98,16 @@ { } +static inline int sme_proc_obss_scan(struct wpa_supplicant *wpa_s) +{ + return 0; +} + +static inline void sme_sched_obss_scan(struct wpa_supplicant *wpa_s, + int enable) +{ +} + #endif /* CONFIG_SME */ #endif /* SME_H */ diff -Nru wpa-1.0/wpa_supplicant/symbian/bld.inf wpa-2.1/wpa_supplicant/symbian/bld.inf --- wpa-1.0/wpa_supplicant/symbian/bld.inf 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/symbian/bld.inf 1970-01-01 00:00:00.000000000 +0000 @@ -1,8 +0,0 @@ -PRJ_PLATFORMS -WINSCW GCCE - -PRJ_EXPORTS - -PRJ_MMPFILES - -wpa_supplicant.mmp diff -Nru wpa-1.0/wpa_supplicant/symbian/README.symbian wpa-2.1/wpa_supplicant/symbian/README.symbian --- wpa-1.0/wpa_supplicant/symbian/README.symbian 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/symbian/README.symbian 1970-01-01 00:00:00.000000000 +0000 @@ -1,24 +0,0 @@ -wpa_supplicant for Symbian -========================== - -Copyright (c) 2003-2007, Jouni Malinen and -contributors -All Rights Reserved. - -This program is dual-licensed under both the GPL version 2 and BSD -license. Either license may be used at your option. - - -This directory includes project files for testing experimental Symbian -(e.g., Nokia S60 3rd Ed) builds. The Symbian port is not really -complete or expected to work, but these files can be used to verify -that the build itself can be completed successfully. - -These files have been successfully tested with Nokia S60 3rd Edition -MR SDK. - -Build files can be created and a phone release build can be run with -following commands: - -bldmake bldfiles -abld build gcce urel diff -Nru wpa-1.0/wpa_supplicant/symbian/wpa_supplicant.mmp wpa-2.1/wpa_supplicant/symbian/wpa_supplicant.mmp --- wpa-1.0/wpa_supplicant/symbian/wpa_supplicant.mmp 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/symbian/wpa_supplicant.mmp 1970-01-01 00:00:00.000000000 +0000 @@ -1,38 +0,0 @@ -TARGET wpa_supplicant.exe -UID 0x0 0x0 -VENDORID 0 -TARGETTYPE exe - -SYSTEMINCLUDE \epoc32\include \epoc32\include\variant \epoc32\include\ecom \epoc32\include\libc - -USERINCLUDE .. ..\..\src ..\..\src\utils - -SOURCEPATH .. -SOURCE main_symbian.cpp -SOURCE config.c config_file.c -SOURCE eapol_sm.c -SOURCE wpa_supplicant.c events.c -SOURCEPATH ..\..\src\rsn_supp -SOURCE wpa.c preauth.c pmksa_cache.c peerkey.c wpa_ie.c -SOURCEPATH ..\..\src\drivers -SOURCE drivers.c driver_common.c -SOURCEPATH ..\..\src\common -SOURCE wpa_common.c -SOURCEPATH ..\..\src\utils -SOURCE os_none.c common.c wpa_debug.c eloop_none.c base64.c -SOURCEPATH ..\..\src\crypto -SOURCE sha1.c md5.c rc4.c des.c aes-cbc.c aes-ctr.c aes-eax.c aes-encblock.c aes-omac1.c aes-unwrap.c aes-wrap.c aes.c ms_funcs.c -SOURCE tls_internal.c crypto_internal.c -SOURCEPATH ..\..\src\tls -SOURCE asn1.c bignum.c rsa.c x509v3.c tlsv1_client.c tlsv1_common.c -SOURCEPATH ..\..\src\l2_packet -SOURCE l2_packet_none.c -SOURCEPATH ..\..\src\eap_peer -SOURCE eap.c eap_methods.c -SOURCE eap_md5.c eap_tls.c eap_mschapv2.c eap_peap.c eap_gtc.c -SOURCE eap_ttls.c eap_otp.c eap_leap.c eap_tls_common.c eap_tlv.c -SOURCE eap_fast.c eap_fast_pac.c -SOURCEPATH ..\..\src\eap_common -SOURCE eap_common.c - -LIBRARY euser.lib estlib.lib diff -Nru wpa-1.0/wpa_supplicant/systemd/wpa_supplicant-nl80211.service.arg.in wpa-2.1/wpa_supplicant/systemd/wpa_supplicant-nl80211.service.arg.in --- wpa-1.0/wpa_supplicant/systemd/wpa_supplicant-nl80211.service.arg.in 1970-01-01 00:00:00.000000000 +0000 +++ wpa-2.1/wpa_supplicant/systemd/wpa_supplicant-nl80211.service.arg.in 2014-02-04 11:23:35.000000000 +0000 @@ -0,0 +1,13 @@ +[Unit] +Description=WPA supplicant daemon (interface- and nl80211 driver-specific version) +Requires=sys-subsystem-net-devices-%i.device +After=sys-subsystem-net-devices-%i.device + +# NetworkManager users will probably want the dbus version instead. + +[Service] +Type=simple +ExecStart=@BINDIR@/wpa_supplicant -c/etc/wpa_supplicant/wpa_supplicant-nl80211-%I.conf -Dnl80211 -i%I + +[Install] +Alias=multi-user.target.wants/wpa_supplicant-nl80211@%i.service diff -Nru wpa-1.0/wpa_supplicant/systemd/wpa_supplicant-nl80211@.service.in wpa-2.1/wpa_supplicant/systemd/wpa_supplicant-nl80211@.service.in --- wpa-1.0/wpa_supplicant/systemd/wpa_supplicant-nl80211@.service.in 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/systemd/wpa_supplicant-nl80211@.service.in 1970-01-01 00:00:00.000000000 +0000 @@ -1,13 +0,0 @@ -[Unit] -Description=WPA supplicant daemon (interface- and nl80211 driver-specific version) -Requires=sys-subsystem-net-devices-%i.device -After=sys-subsystem-net-devices-%i.device - -# NetworkManager users will probably want the dbus version instead. - -[Service] -Type=simple -ExecStart=@BINDIR@/wpa_supplicant -c/etc/wpa_supplicant/wpa_supplicant-nl80211-%I.conf -Dnl80211 -i%I - -[Install] -Alias=multi-user.target.wants/wpa_supplicant-nl80211@wlan0.service diff -Nru wpa-1.0/wpa_supplicant/systemd/wpa_supplicant.service.arg.in wpa-2.1/wpa_supplicant/systemd/wpa_supplicant.service.arg.in --- wpa-1.0/wpa_supplicant/systemd/wpa_supplicant.service.arg.in 1970-01-01 00:00:00.000000000 +0000 +++ wpa-2.1/wpa_supplicant/systemd/wpa_supplicant.service.arg.in 2014-02-04 11:23:35.000000000 +0000 @@ -0,0 +1,13 @@ +[Unit] +Description=WPA supplicant daemon (interface-specific version) +Requires=sys-subsystem-net-devices-%i.device +After=sys-subsystem-net-devices-%i.device + +# NetworkManager users will probably want the dbus version instead. + +[Service] +Type=simple +ExecStart=@BINDIR@/wpa_supplicant -c/etc/wpa_supplicant/wpa_supplicant-%I.conf -i%I + +[Install] +Alias=multi-user.target.wants/wpa_supplicant@%i.service diff -Nru wpa-1.0/wpa_supplicant/systemd/wpa_supplicant@.service.in wpa-2.1/wpa_supplicant/systemd/wpa_supplicant@.service.in --- wpa-1.0/wpa_supplicant/systemd/wpa_supplicant@.service.in 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/systemd/wpa_supplicant@.service.in 1970-01-01 00:00:00.000000000 +0000 @@ -1,13 +0,0 @@ -[Unit] -Description=WPA supplicant daemon (interface-specific version) -Requires=sys-subsystem-net-devices-%i.device -After=sys-subsystem-net-devices-%i.device - -# NetworkManager users will probably want the dbus version instead. - -[Service] -Type=simple -ExecStart=@BINDIR@/wpa_supplicant -c/etc/wpa_supplicant/wpa_supplicant-%I.conf -i%I - -[Install] -Alias=multi-user.target.wants/wpa_supplicant@wlan0.service diff -Nru wpa-1.0/wpa_supplicant/systemd/wpa_supplicant-wired.service.arg.in wpa-2.1/wpa_supplicant/systemd/wpa_supplicant-wired.service.arg.in --- wpa-1.0/wpa_supplicant/systemd/wpa_supplicant-wired.service.arg.in 1970-01-01 00:00:00.000000000 +0000 +++ wpa-2.1/wpa_supplicant/systemd/wpa_supplicant-wired.service.arg.in 2014-02-04 11:23:35.000000000 +0000 @@ -0,0 +1,13 @@ +[Unit] +Description=WPA supplicant daemon (interface- and wired driver-specific version) +Requires=sys-subsystem-net-devices-%i.device +After=sys-subsystem-net-devices-%i.device + +# NetworkManager users will probably want the dbus version instead. + +[Service] +Type=simple +ExecStart=@BINDIR@/wpa_supplicant -c/etc/wpa_supplicant/wpa_supplicant-wired-%I.conf -Dwired -i%I + +[Install] +Alias=multi-user.target.wants/wpa_supplicant-wired@%i.service diff -Nru wpa-1.0/wpa_supplicant/systemd/wpa_supplicant-wired@.service.in wpa-2.1/wpa_supplicant/systemd/wpa_supplicant-wired@.service.in --- wpa-1.0/wpa_supplicant/systemd/wpa_supplicant-wired@.service.in 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/systemd/wpa_supplicant-wired@.service.in 1970-01-01 00:00:00.000000000 +0000 @@ -1,13 +0,0 @@ -[Unit] -Description=WPA supplicant daemon (interface- and wired driver-specific version) -Requires=sys-subsystem-net-devices-%i.device -After=sys-subsystem-net-devices-%i.device - -# NetworkManager users will probably want the dbus version instead. - -[Service] -Type=simple -ExecStart=@BINDIR@/wpa_supplicant -c/etc/wpa_supplicant/wpa_supplicant-wired-%I.conf -Dwired -i%I - -[Install] -Alias=multi-user.target.wants/wpa_supplicant-wired@wlan0.service diff -Nru wpa-1.0/wpa_supplicant/tests/test_eap_sim_common.c wpa-2.1/wpa_supplicant/tests/test_eap_sim_common.c --- wpa-1.0/wpa_supplicant/tests/test_eap_sim_common.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/tests/test_eap_sim_common.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * Test program for EAP-SIM PRF * Copyright (c) 2004-2006, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "eap_common/eap_sim_common.c" @@ -34,7 +28,7 @@ printf("Testing EAP-SIM PRF (FIPS 186-2 + change notice 1)\n"); eap_sim_prf(xkey, buf, sizeof(buf)); - if (memcmp(w, buf, sizeof(w) != 0)) { + if (memcmp(w, buf, sizeof(w)) != 0) { printf("eap_sim_prf failed\n"); return 1; } diff -Nru wpa-1.0/wpa_supplicant/tests/test_wpa.c wpa-2.1/wpa_supplicant/tests/test_wpa.c --- wpa-1.0/wpa_supplicant/tests/test_wpa.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/tests/test_wpa.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * Test program for combined WPA authenticator/supplicant * Copyright (c) 2006-2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -20,11 +14,7 @@ #include "../config.h" #include "rsn_supp/wpa.h" #include "rsn_supp/wpa_ie.h" -#include "../hostapd/wpa.h" - - -extern int wpa_debug_level; -extern int wpa_debug_show_keys; +#include "ap/wpa_auth.h" struct wpa { @@ -304,7 +294,7 @@ static int auth_init(struct wpa *wpa) { - wpa->auth = wpa_auth_sta_init(wpa->auth_group, wpa->supp_addr); + wpa->auth = wpa_auth_sta_init(wpa->auth_group, wpa->supp_addr, NULL); if (wpa->auth == NULL) { wpa_printf(MSG_DEBUG, "AUTH: wpa_auth_sta_init() failed"); return -1; diff -Nru wpa-1.0/wpa_supplicant/utils/log2pcap.py wpa-2.1/wpa_supplicant/utils/log2pcap.py --- wpa-1.0/wpa_supplicant/utils/log2pcap.py 1970-01-01 00:00:00.000000000 +0000 +++ wpa-2.1/wpa_supplicant/utils/log2pcap.py 2014-02-04 11:23:35.000000000 +0000 @@ -0,0 +1,54 @@ +#!/usr/bin/env python +# +# Copyright (c) 2012, Intel Corporation +# +# Author: Johannes Berg +# +# This software may be distributed under the terms of the BSD license. +# See README for more details. + +import sys, struct, re + +def write_pcap_header(pcap_file): + pcap_file.write( + struct.pack(' " % sys.argv[0] + sys.exit(2) + + input_file = open(input, 'r') + pcap_file = open(pcap, 'w') + frame_re = re.compile(r'(([0-9]+.[0-9]{6}):\s*)?nl80211: MLME event frame - hexdump\(len=[0-9]*\):((\s*[0-9a-fA-F]{2})*)') + + write_pcap_header(pcap_file) + + for line in input_file: + m = frame_re.match(line) + if m is None: + continue + if m.group(2): + ts = float(m.group(2)) + else: + ts = 0 + hexdata = m.group(3) + hexdata = hexdata.split() + data = ''.join([chr(int(x, 16)) for x in hexdata]) + pcap_addpacket(pcap_file, ts, data) + + input_file.close() + pcap_file.close() diff -Nru wpa-1.0/wpa_supplicant/vs2005/eapol_test/eapol_test.vcproj wpa-2.1/wpa_supplicant/vs2005/eapol_test/eapol_test.vcproj --- wpa-1.0/wpa_supplicant/vs2005/eapol_test/eapol_test.vcproj 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/vs2005/eapol_test/eapol_test.vcproj 2014-02-04 11:23:35.000000000 +0000 @@ -411,6 +411,10 @@ > + + diff -Nru wpa-1.0/wpa_supplicant/vs2005/wpa_passphrase/wpa_passphrase.vcproj wpa-2.1/wpa_supplicant/vs2005/wpa_passphrase/wpa_passphrase.vcproj --- wpa-1.0/wpa_supplicant/vs2005/wpa_passphrase/wpa_passphrase.vcproj 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/vs2005/wpa_passphrase/wpa_passphrase.vcproj 2014-02-04 11:23:35.000000000 +0000 @@ -206,6 +206,10 @@ > + + diff -Nru wpa-1.0/wpa_supplicant/vs2005/wpa_supplicant/wpa_supplicant.vcproj wpa-2.1/wpa_supplicant/vs2005/wpa_supplicant/wpa_supplicant.vcproj --- wpa-1.0/wpa_supplicant/vs2005/wpa_supplicant/wpa_supplicant.vcproj 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/vs2005/wpa_supplicant/wpa_supplicant.vcproj 2014-02-04 11:23:35.000000000 +0000 @@ -399,6 +399,10 @@ > + + diff -Nru wpa-1.0/wpa_supplicant/vs2005/wpasvc/wpasvc.vcproj wpa-2.1/wpa_supplicant/vs2005/wpasvc/wpasvc.vcproj --- wpa-1.0/wpa_supplicant/vs2005/wpasvc/wpasvc.vcproj 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/vs2005/wpasvc/wpasvc.vcproj 2014-02-04 11:23:35.000000000 +0000 @@ -399,6 +399,10 @@ > + + diff -Nru wpa-1.0/wpa_supplicant/wifi_display.c wpa-2.1/wpa_supplicant/wifi_display.c --- wpa-1.0/wpa_supplicant/wifi_display.c 1970-01-01 00:00:00.000000000 +0000 +++ wpa-2.1/wpa_supplicant/wifi_display.c 2014-02-04 11:23:35.000000000 +0000 @@ -0,0 +1,292 @@ +/* + * wpa_supplicant - Wi-Fi Display + * Copyright (c) 2011, Atheros Communications, Inc. + * Copyright (c) 2011-2012, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "p2p/p2p.h" +#include "common/ieee802_11_defs.h" +#include "wpa_supplicant_i.h" +#include "wifi_display.h" + + +#define WIFI_DISPLAY_SUBELEM_HEADER_LEN 3 + + +int wifi_display_init(struct wpa_global *global) +{ + global->wifi_display = 1; + return 0; +} + + +void wifi_display_deinit(struct wpa_global *global) +{ + int i; + for (i = 0; i < MAX_WFD_SUBELEMS; i++) { + wpabuf_free(global->wfd_subelem[i]); + global->wfd_subelem[i] = NULL; + } +} + + +static int wifi_display_update_wfd_ie(struct wpa_global *global) +{ + struct wpabuf *ie, *buf; + size_t len, plen; + + wpa_printf(MSG_DEBUG, "WFD: Update WFD IE"); + + if (!global->wifi_display) { + wpa_printf(MSG_DEBUG, "WFD: Wi-Fi Display disabled - do not " + "include WFD IE"); + p2p_set_wfd_ie_beacon(global->p2p, NULL); + p2p_set_wfd_ie_probe_req(global->p2p, NULL); + p2p_set_wfd_ie_probe_resp(global->p2p, NULL); + p2p_set_wfd_ie_assoc_req(global->p2p, NULL); + p2p_set_wfd_ie_invitation(global->p2p, NULL); + p2p_set_wfd_ie_prov_disc_req(global->p2p, NULL); + p2p_set_wfd_ie_prov_disc_resp(global->p2p, NULL); + p2p_set_wfd_ie_go_neg(global->p2p, NULL); + p2p_set_wfd_dev_info(global->p2p, NULL); + p2p_set_wfd_assoc_bssid(global->p2p, NULL); + p2p_set_wfd_coupled_sink_info(global->p2p, NULL); + return 0; + } + + p2p_set_wfd_dev_info(global->p2p, + global->wfd_subelem[WFD_SUBELEM_DEVICE_INFO]); + p2p_set_wfd_assoc_bssid( + global->p2p, + global->wfd_subelem[WFD_SUBELEM_ASSOCIATED_BSSID]); + p2p_set_wfd_coupled_sink_info( + global->p2p, global->wfd_subelem[WFD_SUBELEM_COUPLED_SINK]); + + /* + * WFD IE is included in number of management frames. Two different + * sets of subelements are included depending on the frame: + * + * Beacon, (Re)Association Request, GO Negotiation Req/Resp/Conf, + * Provision Discovery Req: + * WFD Device Info + * [Associated BSSID] + * [Coupled Sink Info] + * + * Probe Request: + * WFD Device Info + * [Associated BSSID] + * [Coupled Sink Info] + * [WFD Extended Capability] + * + * Probe Response: + * WFD Device Info + * [Associated BSSID] + * [Coupled Sink Info] + * [WFD Extended Capability] + * [WFD Session Info] + * + * (Re)Association Response, P2P Invitation Req/Resp, + * Provision Discovery Resp: + * WFD Device Info + * [Associated BSSID] + * [Coupled Sink Info] + * [WFD Session Info] + */ + len = 0; + if (global->wfd_subelem[WFD_SUBELEM_DEVICE_INFO]) + len += wpabuf_len(global->wfd_subelem[ + WFD_SUBELEM_DEVICE_INFO]); + if (global->wfd_subelem[WFD_SUBELEM_ASSOCIATED_BSSID]) + len += wpabuf_len(global->wfd_subelem[ + WFD_SUBELEM_ASSOCIATED_BSSID]); + if (global->wfd_subelem[WFD_SUBELEM_COUPLED_SINK]) + len += wpabuf_len(global->wfd_subelem[ + WFD_SUBELEM_COUPLED_SINK]); + if (global->wfd_subelem[WFD_SUBELEM_SESSION_INFO]) + len += wpabuf_len(global->wfd_subelem[ + WFD_SUBELEM_SESSION_INFO]); + if (global->wfd_subelem[WFD_SUBELEM_EXT_CAPAB]) + len += wpabuf_len(global->wfd_subelem[WFD_SUBELEM_EXT_CAPAB]); + buf = wpabuf_alloc(len); + if (buf == NULL) + return -1; + + if (global->wfd_subelem[WFD_SUBELEM_DEVICE_INFO]) + wpabuf_put_buf(buf, + global->wfd_subelem[WFD_SUBELEM_DEVICE_INFO]); + if (global->wfd_subelem[WFD_SUBELEM_ASSOCIATED_BSSID]) + wpabuf_put_buf(buf, global->wfd_subelem[ + WFD_SUBELEM_ASSOCIATED_BSSID]); + if (global->wfd_subelem[WFD_SUBELEM_COUPLED_SINK]) + wpabuf_put_buf(buf, + global->wfd_subelem[WFD_SUBELEM_COUPLED_SINK]); + + ie = wifi_display_encaps(buf); + wpa_hexdump_buf(MSG_DEBUG, "WFD: WFD IE for Beacon", ie); + p2p_set_wfd_ie_beacon(global->p2p, ie); + + ie = wifi_display_encaps(buf); + wpa_hexdump_buf(MSG_DEBUG, "WFD: WFD IE for (Re)Association Request", + ie); + p2p_set_wfd_ie_assoc_req(global->p2p, ie); + + ie = wifi_display_encaps(buf); + wpa_hexdump_buf(MSG_DEBUG, "WFD: WFD IE for GO Negotiation", ie); + p2p_set_wfd_ie_go_neg(global->p2p, ie); + + ie = wifi_display_encaps(buf); + wpa_hexdump_buf(MSG_DEBUG, "WFD: WFD IE for Provision Discovery " + "Request", ie); + p2p_set_wfd_ie_prov_disc_req(global->p2p, ie); + + plen = buf->used; + if (global->wfd_subelem[WFD_SUBELEM_EXT_CAPAB]) + wpabuf_put_buf(buf, + global->wfd_subelem[WFD_SUBELEM_EXT_CAPAB]); + + ie = wifi_display_encaps(buf); + wpa_hexdump_buf(MSG_DEBUG, "WFD: WFD IE for Probe Request", ie); + p2p_set_wfd_ie_probe_req(global->p2p, ie); + + if (global->wfd_subelem[WFD_SUBELEM_SESSION_INFO]) + wpabuf_put_buf(buf, + global->wfd_subelem[WFD_SUBELEM_SESSION_INFO]); + ie = wifi_display_encaps(buf); + wpa_hexdump_buf(MSG_DEBUG, "WFD: WFD IE for Probe Response", ie); + p2p_set_wfd_ie_probe_resp(global->p2p, ie); + + /* Remove WFD Extended Capability from buffer */ + buf->used = plen; + if (global->wfd_subelem[WFD_SUBELEM_SESSION_INFO]) + wpabuf_put_buf(buf, + global->wfd_subelem[WFD_SUBELEM_SESSION_INFO]); + + ie = wifi_display_encaps(buf); + wpa_hexdump_buf(MSG_DEBUG, "WFD: WFD IE for P2P Invitation", ie); + p2p_set_wfd_ie_invitation(global->p2p, ie); + + ie = wifi_display_encaps(buf); + wpa_hexdump_buf(MSG_DEBUG, "WFD: WFD IE for Provision Discovery " + "Response", ie); + p2p_set_wfd_ie_prov_disc_resp(global->p2p, ie); + + wpabuf_free(buf); + + return 0; +} + + +void wifi_display_enable(struct wpa_global *global, int enabled) +{ + wpa_printf(MSG_DEBUG, "WFD: Wi-Fi Display %s", + enabled ? "enabled" : "disabled"); + global->wifi_display = enabled; + wifi_display_update_wfd_ie(global); +} + + +int wifi_display_subelem_set(struct wpa_global *global, char *cmd) +{ + char *pos; + int subelem; + size_t len; + struct wpabuf *e; + + pos = os_strchr(cmd, ' '); + if (pos == NULL) + return -1; + *pos++ = '\0'; + subelem = atoi(cmd); + if (subelem < 0 || subelem >= MAX_WFD_SUBELEMS) + return -1; + + len = os_strlen(pos); + if (len & 1) + return -1; + len /= 2; + + if (len == 0) { + /* Clear subelement */ + e = NULL; + wpa_printf(MSG_DEBUG, "WFD: Clear subelement %d", subelem); + } else { + e = wpabuf_alloc(1 + len); + if (e == NULL) + return -1; + wpabuf_put_u8(e, subelem); + if (hexstr2bin(pos, wpabuf_put(e, len), len) < 0) { + wpabuf_free(e); + return -1; + } + wpa_printf(MSG_DEBUG, "WFD: Set subelement %d", subelem); + } + + wpabuf_free(global->wfd_subelem[subelem]); + global->wfd_subelem[subelem] = e; + wifi_display_update_wfd_ie(global); + + return 0; +} + + +int wifi_display_subelem_get(struct wpa_global *global, char *cmd, + char *buf, size_t buflen) +{ + int subelem; + + subelem = atoi(cmd); + if (subelem < 0 || subelem >= MAX_WFD_SUBELEMS) + return -1; + + if (global->wfd_subelem[subelem] == NULL) + return 0; + + return wpa_snprintf_hex(buf, buflen, + wpabuf_head_u8(global->wfd_subelem[subelem]) + + 1, + wpabuf_len(global->wfd_subelem[subelem]) - 1); +} + + +char * wifi_display_subelem_hex(const struct wpabuf *wfd_subelems, u8 id) +{ + char *subelem = NULL; + const u8 *buf; + size_t buflen; + size_t i = 0; + u16 elen; + + if (!wfd_subelems) + return NULL; + + buf = wpabuf_head_u8(wfd_subelems); + if (!buf) + return NULL; + + buflen = wpabuf_len(wfd_subelems); + + while (i + WIFI_DISPLAY_SUBELEM_HEADER_LEN < buflen) { + elen = WPA_GET_BE16(buf + i + 1); + + if (buf[i] == id) { + subelem = os_zalloc(2 * elen + 1); + if (!subelem) + return NULL; + wpa_snprintf_hex(subelem, 2 * elen + 1, + buf + i + + WIFI_DISPLAY_SUBELEM_HEADER_LEN, + elen); + break; + } + + i += elen + WIFI_DISPLAY_SUBELEM_HEADER_LEN; + } + + return subelem; +} diff -Nru wpa-1.0/wpa_supplicant/wifi_display.h wpa-2.1/wpa_supplicant/wifi_display.h --- wpa-1.0/wpa_supplicant/wifi_display.h 1970-01-01 00:00:00.000000000 +0000 +++ wpa-2.1/wpa_supplicant/wifi_display.h 2014-02-04 11:23:35.000000000 +0000 @@ -0,0 +1,21 @@ +/* + * wpa_supplicant - Wi-Fi Display + * Copyright (c) 2011, Atheros Communications, Inc. + * Copyright (c) 2011-2012, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef WIFI_DISPLAY_H +#define WIFI_DISPLAY_H + +int wifi_display_init(struct wpa_global *global); +void wifi_display_deinit(struct wpa_global *global); +void wifi_display_enable(struct wpa_global *global, int enabled); +int wifi_display_subelem_set(struct wpa_global *global, char *cmd); +int wifi_display_subelem_get(struct wpa_global *global, char *cmd, + char *buf, size_t buflen); +char * wifi_display_subelem_hex(const struct wpabuf *wfd_subelems, u8 id); + +#endif /* WIFI_DISPLAY_H */ diff -Nru wpa-1.0/wpa_supplicant/win_if_list.c wpa-2.1/wpa_supplicant/win_if_list.c --- wpa-1.0/wpa_supplicant/win_if_list.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/win_if_list.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * win_if_list - Display network interfaces with description (for Windows) * Copyright (c) 2004-2006, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. * * This small tool is for the Windows build to provide an easy way of fetching * a list of available network interfaces. diff -Nru wpa-1.0/wpa_supplicant/wnm_sta.c wpa-2.1/wpa_supplicant/wnm_sta.c --- wpa-1.0/wpa_supplicant/wnm_sta.c 1970-01-01 00:00:00.000000000 +0000 +++ wpa-2.1/wpa_supplicant/wnm_sta.c 2014-02-04 11:23:35.000000000 +0000 @@ -0,0 +1,789 @@ +/* + * wpa_supplicant - WNM + * Copyright (c) 2011-2013, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" + +#include "utils/common.h" +#include "common/ieee802_11_defs.h" +#include "common/wpa_ctrl.h" +#include "rsn_supp/wpa.h" +#include "wpa_supplicant_i.h" +#include "driver_i.h" +#include "scan.h" +#include "ctrl_iface.h" +#include "bss.h" +#include "wnm_sta.h" + +#define MAX_TFS_IE_LEN 1024 +#define WNM_MAX_NEIGHBOR_REPORT 10 + + +/* get the TFS IE from driver */ +static int ieee80211_11_get_tfs_ie(struct wpa_supplicant *wpa_s, u8 *buf, + u16 *buf_len, enum wnm_oper oper) +{ + wpa_printf(MSG_DEBUG, "%s: TFS get operation %d", __func__, oper); + + return wpa_drv_wnm_oper(wpa_s, oper, wpa_s->bssid, buf, buf_len); +} + + +/* set the TFS IE to driver */ +static int ieee80211_11_set_tfs_ie(struct wpa_supplicant *wpa_s, + const u8 *addr, u8 *buf, u16 *buf_len, + enum wnm_oper oper) +{ + wpa_printf(MSG_DEBUG, "%s: TFS set operation %d", __func__, oper); + + return wpa_drv_wnm_oper(wpa_s, oper, addr, buf, buf_len); +} + + +/* MLME-SLEEPMODE.request */ +int ieee802_11_send_wnmsleep_req(struct wpa_supplicant *wpa_s, + u8 action, u16 intval, struct wpabuf *tfs_req) +{ + struct ieee80211_mgmt *mgmt; + int res; + size_t len; + struct wnm_sleep_element *wnmsleep_ie; + u8 *wnmtfs_ie; + u8 wnmsleep_ie_len; + u16 wnmtfs_ie_len; /* possibly multiple IE(s) */ + enum wnm_oper tfs_oper = action == 0 ? WNM_SLEEP_TFS_REQ_IE_ADD : + WNM_SLEEP_TFS_REQ_IE_NONE; + + wpa_printf(MSG_DEBUG, "WNM: Request to send WNM-Sleep Mode Request " + "action=%s to " MACSTR, + action == 0 ? "enter" : "exit", + MAC2STR(wpa_s->bssid)); + + /* WNM-Sleep Mode IE */ + wnmsleep_ie_len = sizeof(struct wnm_sleep_element); + wnmsleep_ie = os_zalloc(sizeof(struct wnm_sleep_element)); + if (wnmsleep_ie == NULL) + return -1; + wnmsleep_ie->eid = WLAN_EID_WNMSLEEP; + wnmsleep_ie->len = wnmsleep_ie_len - 2; + wnmsleep_ie->action_type = action; + wnmsleep_ie->status = WNM_STATUS_SLEEP_ACCEPT; + wnmsleep_ie->intval = host_to_le16(intval); + wpa_hexdump(MSG_DEBUG, "WNM: WNM-Sleep Mode element", + (u8 *) wnmsleep_ie, wnmsleep_ie_len); + + /* TFS IE(s) */ + if (tfs_req) { + wnmtfs_ie_len = wpabuf_len(tfs_req); + wnmtfs_ie = os_malloc(wnmtfs_ie_len); + if (wnmtfs_ie == NULL) { + os_free(wnmsleep_ie); + return -1; + } + os_memcpy(wnmtfs_ie, wpabuf_head(tfs_req), wnmtfs_ie_len); + } else { + wnmtfs_ie = os_zalloc(MAX_TFS_IE_LEN); + if (wnmtfs_ie == NULL) { + os_free(wnmsleep_ie); + return -1; + } + if (ieee80211_11_get_tfs_ie(wpa_s, wnmtfs_ie, &wnmtfs_ie_len, + tfs_oper)) { + wnmtfs_ie_len = 0; + os_free(wnmtfs_ie); + wnmtfs_ie = NULL; + } + } + wpa_hexdump(MSG_DEBUG, "WNM: TFS Request element", + (u8 *) wnmtfs_ie, wnmtfs_ie_len); + + mgmt = os_zalloc(sizeof(*mgmt) + wnmsleep_ie_len + wnmtfs_ie_len); + if (mgmt == NULL) { + wpa_printf(MSG_DEBUG, "MLME: Failed to allocate buffer for " + "WNM-Sleep Request action frame"); + os_free(wnmsleep_ie); + os_free(wnmtfs_ie); + return -1; + } + + os_memcpy(mgmt->da, wpa_s->bssid, ETH_ALEN); + os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN); + os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN); + mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, + WLAN_FC_STYPE_ACTION); + mgmt->u.action.category = WLAN_ACTION_WNM; + mgmt->u.action.u.wnm_sleep_req.action = WNM_SLEEP_MODE_REQ; + mgmt->u.action.u.wnm_sleep_req.dialogtoken = 1; + os_memcpy(mgmt->u.action.u.wnm_sleep_req.variable, wnmsleep_ie, + wnmsleep_ie_len); + /* copy TFS IE here */ + if (wnmtfs_ie_len > 0) { + os_memcpy(mgmt->u.action.u.wnm_sleep_req.variable + + wnmsleep_ie_len, wnmtfs_ie, wnmtfs_ie_len); + } + + len = 1 + sizeof(mgmt->u.action.u.wnm_sleep_req) + wnmsleep_ie_len + + wnmtfs_ie_len; + + res = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid, + wpa_s->own_addr, wpa_s->bssid, + &mgmt->u.action.category, len, 0); + if (res < 0) + wpa_printf(MSG_DEBUG, "Failed to send WNM-Sleep Request " + "(action=%d, intval=%d)", action, intval); + + os_free(wnmsleep_ie); + os_free(wnmtfs_ie); + os_free(mgmt); + + return res; +} + + +static void wnm_sleep_mode_enter_success(struct wpa_supplicant *wpa_s, + u8 *tfsresp_ie_start, + u8 *tfsresp_ie_end) +{ + wpa_drv_wnm_oper(wpa_s, WNM_SLEEP_ENTER_CONFIRM, + wpa_s->bssid, NULL, NULL); + /* remove GTK/IGTK ?? */ + + /* set the TFS Resp IE(s) */ + if (tfsresp_ie_start && tfsresp_ie_end && + tfsresp_ie_end - tfsresp_ie_start >= 0) { + u16 tfsresp_ie_len; + tfsresp_ie_len = (tfsresp_ie_end + tfsresp_ie_end[1] + 2) - + tfsresp_ie_start; + wpa_printf(MSG_DEBUG, "TFS Resp IE(s) found"); + /* pass the TFS Resp IE(s) to driver for processing */ + if (ieee80211_11_set_tfs_ie(wpa_s, wpa_s->bssid, + tfsresp_ie_start, + &tfsresp_ie_len, + WNM_SLEEP_TFS_RESP_IE_SET)) + wpa_printf(MSG_DEBUG, "WNM: Fail to set TFS Resp IE"); + } +} + + +static void wnm_sleep_mode_exit_success(struct wpa_supplicant *wpa_s, + const u8 *frm, u16 key_len_total) +{ + u8 *ptr, *end; + u8 gtk_len; + + wpa_drv_wnm_oper(wpa_s, WNM_SLEEP_EXIT_CONFIRM, wpa_s->bssid, + NULL, NULL); + + /* Install GTK/IGTK */ + + /* point to key data field */ + ptr = (u8 *) frm + 1 + 2; + end = ptr + key_len_total; + wpa_hexdump_key(MSG_DEBUG, "WNM: Key Data", ptr, key_len_total); + + while (ptr + 1 < end) { + if (ptr + 2 + ptr[1] > end) { + wpa_printf(MSG_DEBUG, "WNM: Invalid Key Data element " + "length"); + if (end > ptr) { + wpa_hexdump(MSG_DEBUG, "WNM: Remaining data", + ptr, end - ptr); + } + break; + } + if (*ptr == WNM_SLEEP_SUBELEM_GTK) { + if (ptr[1] < 11 + 5) { + wpa_printf(MSG_DEBUG, "WNM: Too short GTK " + "subelem"); + break; + } + gtk_len = *(ptr + 4); + if (ptr[1] < 11 + gtk_len || + gtk_len < 5 || gtk_len > 32) { + wpa_printf(MSG_DEBUG, "WNM: Invalid GTK " + "subelem"); + break; + } + wpa_wnmsleep_install_key( + wpa_s->wpa, + WNM_SLEEP_SUBELEM_GTK, + ptr); + ptr += 13 + gtk_len; +#ifdef CONFIG_IEEE80211W + } else if (*ptr == WNM_SLEEP_SUBELEM_IGTK) { + if (ptr[1] < 2 + 6 + WPA_IGTK_LEN) { + wpa_printf(MSG_DEBUG, "WNM: Too short IGTK " + "subelem"); + break; + } + wpa_wnmsleep_install_key(wpa_s->wpa, + WNM_SLEEP_SUBELEM_IGTK, ptr); + ptr += 10 + WPA_IGTK_LEN; +#endif /* CONFIG_IEEE80211W */ + } else + break; /* skip the loop */ + } +} + + +static void ieee802_11_rx_wnmsleep_resp(struct wpa_supplicant *wpa_s, + const u8 *frm, int len) +{ + /* + * Action [1] | Diaglog Token [1] | Key Data Len [2] | Key Data | + * WNM-Sleep Mode IE | TFS Response IE + */ + u8 *pos = (u8 *) frm; /* point to payload after the action field */ + u16 key_len_total = le_to_host16(*((u16 *)(frm+2))); + struct wnm_sleep_element *wnmsleep_ie = NULL; + /* multiple TFS Resp IE (assuming consecutive) */ + u8 *tfsresp_ie_start = NULL; + u8 *tfsresp_ie_end = NULL; + + wpa_printf(MSG_DEBUG, "WNM-Sleep Mode Response token=%u key_len_total=%d", + frm[0], key_len_total); + pos += 3 + key_len_total; + if (pos > frm + len) { + wpa_printf(MSG_INFO, "WNM: Too short frame for Key Data field"); + return; + } + while (pos - frm < len) { + u8 ie_len = *(pos + 1); + if (pos + 2 + ie_len > frm + len) { + wpa_printf(MSG_INFO, "WNM: Invalid IE len %u", ie_len); + break; + } + wpa_hexdump(MSG_DEBUG, "WNM: Element", pos, 2 + ie_len); + if (*pos == WLAN_EID_WNMSLEEP) + wnmsleep_ie = (struct wnm_sleep_element *) pos; + else if (*pos == WLAN_EID_TFS_RESP) { + if (!tfsresp_ie_start) + tfsresp_ie_start = pos; + tfsresp_ie_end = pos; + } else + wpa_printf(MSG_DEBUG, "EID %d not recognized", *pos); + pos += ie_len + 2; + } + + if (!wnmsleep_ie) { + wpa_printf(MSG_DEBUG, "No WNM-Sleep IE found"); + return; + } + + if (wnmsleep_ie->status == WNM_STATUS_SLEEP_ACCEPT || + wnmsleep_ie->status == WNM_STATUS_SLEEP_EXIT_ACCEPT_GTK_UPDATE) { + wpa_printf(MSG_DEBUG, "Successfully recv WNM-Sleep Response " + "frame (action=%d, intval=%d)", + wnmsleep_ie->action_type, wnmsleep_ie->intval); + if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_ENTER) { + wnm_sleep_mode_enter_success(wpa_s, tfsresp_ie_start, + tfsresp_ie_end); + } else if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_EXIT) { + wnm_sleep_mode_exit_success(wpa_s, frm, key_len_total); + } + } else { + wpa_printf(MSG_DEBUG, "Reject recv WNM-Sleep Response frame " + "(action=%d, intval=%d)", + wnmsleep_ie->action_type, wnmsleep_ie->intval); + if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_ENTER) + wpa_drv_wnm_oper(wpa_s, WNM_SLEEP_ENTER_FAIL, + wpa_s->bssid, NULL, NULL); + else if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_EXIT) + wpa_drv_wnm_oper(wpa_s, WNM_SLEEP_EXIT_FAIL, + wpa_s->bssid, NULL, NULL); + } +} + + +void wnm_deallocate_memory(struct wpa_supplicant *wpa_s) +{ + int i; + + for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) { + os_free(wpa_s->wnm_neighbor_report_elements[i].tsf_info); + os_free(wpa_s->wnm_neighbor_report_elements[i].con_coun_str); + os_free(wpa_s->wnm_neighbor_report_elements[i].bss_tran_can); + os_free(wpa_s->wnm_neighbor_report_elements[i].bss_term_dur); + os_free(wpa_s->wnm_neighbor_report_elements[i].bearing); + os_free(wpa_s->wnm_neighbor_report_elements[i].meas_pilot); + os_free(wpa_s->wnm_neighbor_report_elements[i].rrm_cap); + os_free(wpa_s->wnm_neighbor_report_elements[i].mul_bssid); + } + + os_free(wpa_s->wnm_neighbor_report_elements); + wpa_s->wnm_neighbor_report_elements = NULL; +} + + +static void wnm_parse_neighbor_report_elem(struct neighbor_report *rep, + u8 id, u8 elen, const u8 *pos) +{ + switch (id) { + case WNM_NEIGHBOR_TSF: + if (elen < 2 + 2) { + wpa_printf(MSG_DEBUG, "WNM: Too short TSF"); + break; + } + rep->tsf_info = os_zalloc(sizeof(struct tsf_info)); + if (rep->tsf_info == NULL) + break; + rep->tsf_info->present = 1; + os_memcpy(rep->tsf_info->tsf_offset, pos, 2); + os_memcpy(rep->tsf_info->beacon_interval, pos + 2, 2); + break; + case WNM_NEIGHBOR_CONDENSED_COUNTRY_STRING: + if (elen < 2) { + wpa_printf(MSG_DEBUG, "WNM: Too short condensed " + "country string"); + break; + } + rep->con_coun_str = + os_zalloc(sizeof(struct condensed_country_string)); + if (rep->con_coun_str == NULL) + break; + rep->con_coun_str->present = 1; + os_memcpy(rep->con_coun_str->country_string, pos, 2); + break; + case WNM_NEIGHBOR_BSS_TRANSITION_CANDIDATE: + if (elen < 1) { + wpa_printf(MSG_DEBUG, "WNM: Too short BSS transition " + "candidate"); + break; + } + rep->bss_tran_can = + os_zalloc(sizeof(struct bss_transition_candidate)); + if (rep->bss_tran_can == NULL) + break; + rep->bss_tran_can->present = 1; + rep->bss_tran_can->preference = pos[0]; + break; + case WNM_NEIGHBOR_BSS_TERMINATION_DURATION: + if (elen < 12) { + wpa_printf(MSG_DEBUG, "WNM: Too short BSS termination " + "duration"); + break; + } + rep->bss_term_dur = + os_zalloc(sizeof(struct bss_termination_duration)); + if (rep->bss_term_dur == NULL) + break; + rep->bss_term_dur->present = 1; + os_memcpy(rep->bss_term_dur->duration, pos, 12); + break; + case WNM_NEIGHBOR_BEARING: + if (elen < 8) { + wpa_printf(MSG_DEBUG, "WNM: Too short neighbor " + "bearing"); + break; + } + rep->bearing = os_zalloc(sizeof(struct bearing)); + if (rep->bearing == NULL) + break; + rep->bearing->present = 1; + os_memcpy(rep->bearing->bearing, pos, 8); + break; + case WNM_NEIGHBOR_MEASUREMENT_PILOT: + if (elen < 2) { + wpa_printf(MSG_DEBUG, "WNM: Too short measurement " + "pilot"); + break; + } + rep->meas_pilot = os_zalloc(sizeof(struct measurement_pilot)); + if (rep->meas_pilot == NULL) + break; + rep->meas_pilot->present = 1; + rep->meas_pilot->measurement_pilot = pos[0]; + rep->meas_pilot->num_vendor_specific = pos[1]; + os_memcpy(rep->meas_pilot->vendor_specific, pos + 2, elen - 2); + break; + case WNM_NEIGHBOR_RRM_ENABLED_CAPABILITIES: + if (elen < 4) { + wpa_printf(MSG_DEBUG, "WNM: Too short RRM enabled " + "capabilities"); + break; + } + rep->rrm_cap = + os_zalloc(sizeof(struct rrm_enabled_capabilities)); + if (rep->rrm_cap == NULL) + break; + rep->rrm_cap->present = 1; + os_memcpy(rep->rrm_cap->capabilities, pos, 4); + break; + case WNM_NEIGHBOR_MULTIPLE_BSSID: + if (elen < 2) { + wpa_printf(MSG_DEBUG, "WNM: Too short multiple BSSID"); + break; + } + rep->mul_bssid = os_zalloc(sizeof(struct multiple_bssid)); + if (rep->mul_bssid == NULL) + break; + rep->mul_bssid->present = 1; + rep->mul_bssid->max_bssid_indicator = pos[0]; + rep->mul_bssid->num_vendor_specific = pos[1]; + os_memcpy(rep->mul_bssid->vendor_specific, pos + 2, elen - 2); + break; + } +} + + +static void wnm_parse_neighbor_report(struct wpa_supplicant *wpa_s, + const u8 *pos, u8 len, + struct neighbor_report *rep) +{ + u8 left = len; + + if (left < 13) { + wpa_printf(MSG_DEBUG, "WNM: Too short neighbor report"); + return; + } + + os_memcpy(rep->bssid, pos, ETH_ALEN); + os_memcpy(rep->bssid_information, pos + ETH_ALEN, 4); + rep->regulatory_class = *(pos + 10); + rep->channel_number = *(pos + 11); + rep->phy_type = *(pos + 12); + + pos += 13; + left -= 13; + + while (left >= 2) { + u8 id, elen; + + id = *pos++; + elen = *pos++; + wnm_parse_neighbor_report_elem(rep, id, elen, pos); + left -= 2 + elen; + pos += elen; + } +} + + +static int compare_scan_neighbor_results(struct wpa_supplicant *wpa_s, + struct wpa_scan_results *scan_res, + struct neighbor_report *neigh_rep, + u8 num_neigh_rep, u8 *bssid_to_connect) +{ + + u8 i, j; + + if (scan_res == NULL || num_neigh_rep == 0) + return 0; + + wpa_printf(MSG_DEBUG, "WNM: Current BSS " MACSTR " RSSI %d", + MAC2STR(wpa_s->bssid), + wpa_s->current_bss ? wpa_s->current_bss->level : 0); + + for (i = 0; i < num_neigh_rep; i++) { + for (j = 0; j < scan_res->num; j++) { + /* Check for a better RSSI AP */ + if (os_memcmp(scan_res->res[j]->bssid, + neigh_rep[i].bssid, ETH_ALEN) == 0 && + scan_res->res[j]->level > + wpa_s->current_bss->level) { + /* Got a BSSID with better RSSI value */ + os_memcpy(bssid_to_connect, neigh_rep[i].bssid, + ETH_ALEN); + wpa_printf(MSG_DEBUG, "Found a BSS " MACSTR + " with better scan RSSI %d", + MAC2STR(scan_res->res[j]->bssid), + scan_res->res[j]->level); + return 1; + } + wpa_printf(MSG_DEBUG, "scan_res[%d] " MACSTR + " RSSI %d", j, + MAC2STR(scan_res->res[j]->bssid), + scan_res->res[j]->level); + } + } + + return 0; +} + + +static void wnm_send_bss_transition_mgmt_resp( + struct wpa_supplicant *wpa_s, u8 dialog_token, + enum bss_trans_mgmt_status_code status, u8 delay, + const u8 *target_bssid) +{ + u8 buf[1000], *pos; + struct ieee80211_mgmt *mgmt; + size_t len; + + wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Response " + "to " MACSTR " dialog_token=%u status=%u delay=%d", + MAC2STR(wpa_s->bssid), dialog_token, status, delay); + + mgmt = (struct ieee80211_mgmt *) buf; + os_memset(&buf, 0, sizeof(buf)); + os_memcpy(mgmt->da, wpa_s->bssid, ETH_ALEN); + os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN); + os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN); + mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, + WLAN_FC_STYPE_ACTION); + mgmt->u.action.category = WLAN_ACTION_WNM; + mgmt->u.action.u.bss_tm_resp.action = WNM_BSS_TRANS_MGMT_RESP; + mgmt->u.action.u.bss_tm_resp.dialog_token = dialog_token; + mgmt->u.action.u.bss_tm_resp.status_code = status; + mgmt->u.action.u.bss_tm_resp.bss_termination_delay = delay; + pos = mgmt->u.action.u.bss_tm_resp.variable; + if (target_bssid) { + os_memcpy(pos, target_bssid, ETH_ALEN); + pos += ETH_ALEN; + } else if (status == WNM_BSS_TM_ACCEPT) { + /* + * P802.11-REVmc clarifies that the Target BSSID field is always + * present when status code is zero, so use a fake value here if + * no BSSID is yet known. + */ + os_memset(pos, 0, ETH_ALEN); + pos += ETH_ALEN; + } + + len = pos - (u8 *) &mgmt->u.action.category; + + wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid, + wpa_s->own_addr, wpa_s->bssid, + &mgmt->u.action.category, len, 0); +} + + +void wnm_scan_response(struct wpa_supplicant *wpa_s, + struct wpa_scan_results *scan_res) +{ + u8 bssid[ETH_ALEN]; + + if (scan_res == NULL) { + wpa_printf(MSG_ERROR, "Scan result is NULL"); + goto send_bss_resp_fail; + } + + /* Compare the Neighbor Report and scan results */ + if (compare_scan_neighbor_results(wpa_s, scan_res, + wpa_s->wnm_neighbor_report_elements, + wpa_s->wnm_num_neighbor_report, + bssid) == 1) { + /* Associate to the network */ + struct wpa_bss *bss; + struct wpa_ssid *ssid = wpa_s->current_ssid; + + bss = wpa_bss_get_bssid(wpa_s, bssid); + if (!bss) { + wpa_printf(MSG_DEBUG, "WNM: Target AP not found from " + "BSS table"); + goto send_bss_resp_fail; + } + + /* Send the BSS Management Response - Accept */ + if (wpa_s->wnm_reply) { + wnm_send_bss_transition_mgmt_resp(wpa_s, + wpa_s->wnm_dialog_token, + WNM_BSS_TM_ACCEPT, + 0, bssid); + } + + wpa_s->reassociate = 1; + wpa_supplicant_connect(wpa_s, bss, ssid); + wnm_deallocate_memory(wpa_s); + return; + } + + /* Send reject response for all the failures */ +send_bss_resp_fail: + wnm_deallocate_memory(wpa_s); + if (wpa_s->wnm_reply) { + wnm_send_bss_transition_mgmt_resp(wpa_s, + wpa_s->wnm_dialog_token, + WNM_BSS_TM_REJECT_UNSPECIFIED, + 0, NULL); + } + return; +} + + +static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s, + const u8 *pos, const u8 *end, + int reply) +{ + if (pos + 5 > end) + return; + + wpa_s->wnm_dialog_token = pos[0]; + wpa_s->wnm_mode = pos[1]; + wpa_s->wnm_dissoc_timer = WPA_GET_LE16(pos + 2); + wpa_s->wnm_validity_interval = pos[4]; + wpa_s->wnm_reply = reply; + + wpa_printf(MSG_DEBUG, "WNM: BSS Transition Management Request: " + "dialog_token=%u request_mode=0x%x " + "disassoc_timer=%u validity_interval=%u", + wpa_s->wnm_dialog_token, wpa_s->wnm_mode, + wpa_s->wnm_dissoc_timer, wpa_s->wnm_validity_interval); + + pos += 5; + + if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED) { + if (pos + 12 > end) { + wpa_printf(MSG_DEBUG, "WNM: Too short BSS TM Request"); + return; + } + os_memcpy(wpa_s->wnm_bss_termination_duration, pos, 12); + pos += 12; /* BSS Termination Duration */ + } + + if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT) { + char url[256]; + unsigned int beacon_int; + + if (pos + 1 > end || pos + 1 + pos[0] > end) { + wpa_printf(MSG_DEBUG, "WNM: Invalid BSS Transition " + "Management Request (URL)"); + return; + } + os_memcpy(url, pos + 1, pos[0]); + url[pos[0]] = '\0'; + pos += 1 + pos[0]; + + if (wpa_s->current_bss) + beacon_int = wpa_s->current_bss->beacon_int; + else + beacon_int = 100; /* best guess */ + + wpa_msg(wpa_s, MSG_INFO, ESS_DISASSOC_IMMINENT "%d %u %s", + wpa_sm_pmf_enabled(wpa_s->wpa), + wpa_s->wnm_dissoc_timer * beacon_int * 128 / 125, url); + } + + if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_DISASSOC_IMMINENT) { + wpa_msg(wpa_s, MSG_INFO, "WNM: Disassociation Imminent - " + "Disassociation Timer %u", wpa_s->wnm_dissoc_timer); + if (wpa_s->wnm_dissoc_timer && !wpa_s->scanning) { + /* TODO: mark current BSS less preferred for + * selection */ + wpa_printf(MSG_DEBUG, "Trying to find another BSS"); + wpa_supplicant_req_scan(wpa_s, 0, 0); + } + } + + if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_PREF_CAND_LIST_INCLUDED) { + wpa_msg(wpa_s, MSG_INFO, "WNM: Preferred List Available"); + wpa_s->wnm_num_neighbor_report = 0; + os_free(wpa_s->wnm_neighbor_report_elements); + wpa_s->wnm_neighbor_report_elements = os_zalloc( + WNM_MAX_NEIGHBOR_REPORT * + sizeof(struct neighbor_report)); + if (wpa_s->wnm_neighbor_report_elements == NULL) + return; + + while (pos + 2 <= end && + wpa_s->wnm_num_neighbor_report < WNM_MAX_NEIGHBOR_REPORT) + { + u8 tag = *pos++; + u8 len = *pos++; + + wpa_printf(MSG_DEBUG, "WNM: Neighbor report tag %u", + tag); + if (pos + len > end) { + wpa_printf(MSG_DEBUG, "WNM: Truncated request"); + return; + } + wnm_parse_neighbor_report( + wpa_s, pos, len, + &wpa_s->wnm_neighbor_report_elements[ + wpa_s->wnm_num_neighbor_report]); + + pos += len; + wpa_s->wnm_num_neighbor_report++; + } + + wpa_s->scan_res_handler = wnm_scan_response; + wpa_supplicant_req_scan(wpa_s, 0, 0); + } else if (reply) { + enum bss_trans_mgmt_status_code status; + if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT) + status = WNM_BSS_TM_ACCEPT; + else { + wpa_msg(wpa_s, MSG_INFO, "WNM: BSS Transition Management Request did not include candidates"); + status = WNM_BSS_TM_REJECT_UNSPECIFIED; + } + wnm_send_bss_transition_mgmt_resp(wpa_s, + wpa_s->wnm_dialog_token, + status, 0, NULL); + } +} + + +int wnm_send_bss_transition_mgmt_query(struct wpa_supplicant *wpa_s, + u8 query_reason) +{ + u8 buf[1000], *pos; + struct ieee80211_mgmt *mgmt; + size_t len; + int ret; + + wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Query to " + MACSTR " query_reason=%u", + MAC2STR(wpa_s->bssid), query_reason); + + mgmt = (struct ieee80211_mgmt *) buf; + os_memset(&buf, 0, sizeof(buf)); + os_memcpy(mgmt->da, wpa_s->bssid, ETH_ALEN); + os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN); + os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN); + mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, + WLAN_FC_STYPE_ACTION); + mgmt->u.action.category = WLAN_ACTION_WNM; + mgmt->u.action.u.bss_tm_query.action = WNM_BSS_TRANS_MGMT_QUERY; + mgmt->u.action.u.bss_tm_query.dialog_token = 1; + mgmt->u.action.u.bss_tm_query.query_reason = query_reason; + pos = mgmt->u.action.u.bss_tm_query.variable; + + len = pos - (u8 *) &mgmt->u.action.category; + + ret = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid, + wpa_s->own_addr, wpa_s->bssid, + &mgmt->u.action.category, len, 0); + + return ret; +} + + +void ieee802_11_rx_wnm_action(struct wpa_supplicant *wpa_s, + const struct ieee80211_mgmt *mgmt, size_t len) +{ + const u8 *pos, *end; + u8 act; + + if (len < IEEE80211_HDRLEN + 2) + return; + + pos = &mgmt->u.action.category; + pos++; + act = *pos++; + end = ((const u8 *) mgmt) + len; + + wpa_printf(MSG_DEBUG, "WNM: RX action %u from " MACSTR, + act, MAC2STR(mgmt->sa)); + if (wpa_s->wpa_state < WPA_ASSOCIATED || + os_memcmp(mgmt->sa, wpa_s->bssid, ETH_ALEN) != 0) { + wpa_printf(MSG_DEBUG, "WNM: Ignore unexpected WNM Action " + "frame"); + return; + } + + switch (act) { + case WNM_BSS_TRANS_MGMT_REQ: + ieee802_11_rx_bss_trans_mgmt_req(wpa_s, pos, end, + !(mgmt->da[0] & 0x01)); + break; + case WNM_SLEEP_MODE_RESP: + ieee802_11_rx_wnmsleep_resp(wpa_s, pos, end - pos); + break; + default: + wpa_printf(MSG_ERROR, "WNM: Unknown request"); + break; + } +} diff -Nru wpa-1.0/wpa_supplicant/wnm_sta.h wpa-2.1/wpa_supplicant/wnm_sta.h --- wpa-1.0/wpa_supplicant/wnm_sta.h 1970-01-01 00:00:00.000000000 +0000 +++ wpa-2.1/wpa_supplicant/wnm_sta.h 2014-02-04 11:23:35.000000000 +0000 @@ -0,0 +1,87 @@ +/* + * IEEE 802.11v WNM related functions and structures + * Copyright (c) 2011-2012, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef WNM_STA_H +#define WNM_STA_H + +struct tsf_info { + u8 present; + u8 tsf_offset[2]; + u8 beacon_interval[2]; +}; + +struct condensed_country_string { + u8 present; + u8 country_string[2]; +}; + +struct bss_transition_candidate { + u8 present; + u8 preference; +}; + +struct bss_termination_duration { + u8 present; + u8 duration[12]; +}; + +struct bearing { + u8 present; + u8 bearing[8]; +}; + +struct measurement_pilot { + u8 present; + u8 measurement_pilot; + u8 num_vendor_specific; + u8 vendor_specific[255]; +}; + +struct rrm_enabled_capabilities { + u8 present; + u8 capabilities[4]; +}; + +struct multiple_bssid { + u8 present; + u8 max_bssid_indicator; + u8 num_vendor_specific; + u8 vendor_specific[255]; +}; + +struct neighbor_report { + u8 bssid[ETH_ALEN]; + u8 bssid_information[4]; + u8 regulatory_class; + u8 channel_number; + u8 phy_type; + struct tsf_info *tsf_info; + struct condensed_country_string *con_coun_str; + struct bss_transition_candidate *bss_tran_can; + struct bss_termination_duration *bss_term_dur; + struct bearing *bearing; + struct measurement_pilot *meas_pilot; + struct rrm_enabled_capabilities *rrm_cap; + struct multiple_bssid *mul_bssid; +}; + + +int ieee802_11_send_wnmsleep_req(struct wpa_supplicant *wpa_s, + u8 action, u16 intval, struct wpabuf *tfs_req); + +void ieee802_11_rx_wnm_action(struct wpa_supplicant *wpa_s, + const struct ieee80211_mgmt *mgmt, size_t len); + +void wnm_scan_response(struct wpa_supplicant *wpa_s, + struct wpa_scan_results *scan_res); + +int wnm_send_bss_transition_mgmt_query(struct wpa_supplicant *wpa_s, + u8 query_reason); +void wnm_deallocate_memory(struct wpa_supplicant *wpa_s); + +#endif /* WNM_STA_H */ diff -Nru wpa-1.0/wpa_supplicant/wpa_cli.c wpa-2.1/wpa_supplicant/wpa_cli.c --- wpa-1.0/wpa_supplicant/wpa_cli.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/wpa_cli.c 2014-02-04 11:23:35.000000000 +0000 @@ -1,15 +1,9 @@ /* * WPA Supplicant - command line interface for wpa_supplicant daemon - * Copyright (c) 2004-2011, Jouni Malinen + * Copyright (c) 2004-2013, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -26,6 +20,7 @@ #include "utils/edit.h" #include "utils/list.h" #include "common/version.h" +#include "common/ieee802_11_defs.h" #ifdef ANDROID #include #endif /* ANDROID */ @@ -33,32 +28,15 @@ static const char *wpa_cli_version = "wpa_cli v" VERSION_STR "\n" -"Copyright (c) 2004-2012, Jouni Malinen and contributors"; +"Copyright (c) 2004-2014, Jouni Malinen and contributors"; static const char *wpa_cli_license = -"This program is free software. You can distribute it and/or modify it\n" -"under the terms of the GNU General Public License version 2.\n" -"\n" -"Alternatively, this software may be distributed under the terms of the\n" -"BSD license. See README and COPYING for more details.\n"; +"This software may be distributed under the terms of the BSD license.\n" +"See README for more details.\n"; static const char *wpa_cli_full_license = -"This program is free software; you can redistribute it and/or modify\n" -"it under the terms of the GNU General Public License version 2 as\n" -"published by the Free Software Foundation.\n" -"\n" -"This program is distributed in the hope that it will be useful,\n" -"but WITHOUT ANY WARRANTY; without even the implied warranty of\n" -"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" -"GNU General Public License for more details.\n" -"\n" -"You should have received a copy of the GNU General Public License\n" -"along with this program; if not, write to the Free Software\n" -"Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA\n" -"\n" -"Alternatively, this software may be distributed under the terms of the\n" -"BSD license.\n" +"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" @@ -92,7 +70,7 @@ static struct wpa_ctrl *mon_conn; static int wpa_cli_quit = 0; static int wpa_cli_attached = 0; -static int wpa_cli_connected = 0; +static int wpa_cli_connected = -1; static int wpa_cli_last_id = 0; #ifndef CONFIG_CTRL_IFACE_DIR #define CONFIG_CTRL_IFACE_DIR "/var/run/wpa_supplicant" @@ -103,6 +81,7 @@ static const char *action_file = NULL; static int ping_interval = 5; static int interactive = 0; +static char *ifname_prefix = NULL; struct cli_txt_entry { struct dl_list list; @@ -112,10 +91,14 @@ static DEFINE_DL_LIST(bsses); /* struct cli_txt_entry */ static DEFINE_DL_LIST(p2p_peers); /* struct cli_txt_entry */ static DEFINE_DL_LIST(p2p_groups); /* struct cli_txt_entry */ +static DEFINE_DL_LIST(ifnames); /* struct cli_txt_entry */ -static void print_help(void); +static void print_help(const char *cmd); static void wpa_cli_mon_receive(int sock, void *eloop_ctx, void *sock_ctx); +static void wpa_cli_close_connection(void); +static char * wpa_cli_get_default_ifname(void); +static char ** wpa_list_cmd_list(void); static void usage(void) @@ -132,7 +115,7 @@ " -B = run a daemon in the background\n" " default path: " CONFIG_CTRL_IFACE_DIR "\n" " default interface: first interface found in socket path\n"); - print_help(); + print_help(NULL); } @@ -192,11 +175,9 @@ end = os_strchr(txt, ' '); if (end == NULL) end = txt + os_strlen(txt); - buf = os_malloc(end - txt + 1); + buf = dup_binstr(txt, end - txt); if (buf == NULL) return; - os_memcpy(buf, txt, end - txt); - buf[end - txt] = '\0'; cli_txt_list_del(txt_list, buf); os_free(buf); } @@ -242,11 +223,9 @@ end = os_strchr(txt, ' '); if (end == NULL) end = txt + os_strlen(txt); - buf = os_malloc(end - txt + 1); + buf = dup_binstr(txt, end - txt); if (buf == NULL) return -1; - os_memcpy(buf, txt, end - txt); - buf[end - txt] = '\0'; ret = cli_txt_list_add(txt_list, buf); os_free(buf); return ret; @@ -260,7 +239,7 @@ char **res; struct cli_txt_entry *e; - res = os_zalloc((count + 1) * sizeof(char *)); + res = os_calloc(count + 1, sizeof(char *)); if (res == NULL) return NULL; @@ -383,6 +362,7 @@ } else { printf("Warning: Failed to attach to " "wpa_supplicant.\n"); + wpa_cli_close_connection(); return -1; } } @@ -418,7 +398,7 @@ static int _wpa_ctrl_command(struct wpa_ctrl *ctrl, char *cmd, int print) { - char buf[2048]; + char buf[4096]; size_t len; int ret; @@ -426,6 +406,12 @@ printf("Not connected to wpa_supplicant - command dropped.\n"); return -1; } + if (ifname_prefix) { + os_snprintf(buf, sizeof(buf), "IFNAME=%s %s", + ifname_prefix, cmd); + buf[sizeof(buf) - 1] = '\0'; + cmd = buf; + } len = sizeof(buf) - 1; ret = wpa_ctrl_request(ctrl, cmd, os_strlen(cmd), buf, &len, wpa_cli_msg_cb); @@ -452,10 +438,67 @@ } +static 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 (res < 0 || res >= end - pos) + goto fail; + pos += res; + + for (i = 0; i < argc; i++) { + res = os_snprintf(pos, end - pos, " %s", argv[i]); + if (res < 0 || res >= end - pos) + goto fail; + pos += res; + } + + buf[buflen - 1] = '\0'; + return 0; + +fail: + printf("Too long command\n"); + return -1; +} + + +static int wpa_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 wpa_cli_cmd_ifname(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + return wpa_ctrl_command(ctrl, "IFNAME"); +} + + static int wpa_cli_cmd_status(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - int verbose = argc > 0 && os_strcmp(argv[0], "verbose") == 0; - return wpa_ctrl_command(ctrl, verbose ? "STATUS-VERBOSE" : "STATUS"); + if (argc > 0 && os_strcmp(argv[0], "verbose") == 0) + return wpa_ctrl_command(ctrl, "STATUS-VERBOSE"); + if (argc > 0 && os_strcmp(argv[0], "wps") == 0) + return wpa_ctrl_command(ctrl, "STATUS-WPS"); + if (argc > 0 && os_strcmp(argv[0], "driver") == 0) + return wpa_ctrl_command(ctrl, "STATUS-DRIVER"); + return wpa_ctrl_command(ctrl, "STATUS"); } @@ -473,14 +516,7 @@ static int wpa_cli_cmd_note(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[256]; - int ret; - if (argc == 0) - return -1; - ret = os_snprintf(cmd, sizeof(cmd), "NOTE %s", argv[0]); - if (ret < 0 || (size_t) ret >= sizeof(cmd)) - return -1; - return wpa_ctrl_command(ctrl, cmd); + return wpa_cli_cmd(ctrl, "NOTE", 1, argc, argv); } @@ -498,11 +534,26 @@ static int wpa_cli_cmd_help(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - print_help(); + print_help(argc > 0 ? argv[0] : NULL); return 0; } +static char ** wpa_cli_complete_help(const char *str, int pos) +{ + int arg = get_cmd_arg_num(str, pos); + char **res = NULL; + + switch (arg) { + case 1: + res = wpa_list_cmd_list(); + break; + } + + return res; +} + + static int wpa_cli_cmd_license(struct wpa_ctrl *ctrl, int argc, char *argv[]) { printf("%s\n\n%s\n", wpa_cli_version, wpa_cli_full_license); @@ -519,72 +570,90 @@ } -static void wpa_cli_show_variables(void) -{ - printf("set variables:\n" - " EAPOL::heldPeriod (EAPOL state machine held period, " - "in seconds)\n" - " EAPOL::authPeriod (EAPOL state machine authentication " - "period, in seconds)\n" - " EAPOL::startPeriod (EAPOL state machine start period, in " - "seconds)\n" - " EAPOL::maxStart (EAPOL state machine maximum start " - "attempts)\n"); - printf(" dot11RSNAConfigPMKLifetime (WPA/WPA2 PMK lifetime in " - "seconds)\n" - " dot11RSNAConfigPMKReauthThreshold (WPA/WPA2 reauthentication" - " threshold\n\tpercentage)\n" - " dot11RSNAConfigSATimeout (WPA/WPA2 timeout for completing " - "security\n\tassociation in seconds)\n"); -} - - static int wpa_cli_cmd_set(struct wpa_ctrl *ctrl, int argc, char *argv[]) { char cmd[256]; int res; - if (argc == 0) { - wpa_cli_show_variables(); - return 0; + if (argc == 1) { + res = os_snprintf(cmd, sizeof(cmd), "SET %s ", argv[0]); + if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { + printf("Too long SET command.\n"); + return -1; + } + return wpa_ctrl_command(ctrl, cmd); } - if (argc != 1 && argc != 2) { - printf("Invalid SET command: needs two arguments (variable " - "name and value)\n"); - return -1; - } + return wpa_cli_cmd(ctrl, "SET", 2, argc, argv); +} - if (argc == 1) - res = os_snprintf(cmd, sizeof(cmd), "SET %s ", argv[0]); - else - res = os_snprintf(cmd, sizeof(cmd), "SET %s %s", - argv[0], argv[1]); - if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { - printf("Too long SET command.\n"); - return -1; + +static char ** wpa_cli_complete_set(const char *str, int pos) +{ + int arg = get_cmd_arg_num(str, pos); + const char *fields[] = { + /* runtime values */ + "EAPOL::heldPeriod", "EAPOL::authPeriod", "EAPOL::startPeriod", + "EAPOL::maxStart", "dot11RSNAConfigPMKLifetime", + "dot11RSNAConfigPMKReauthThreshold", "dot11RSNAConfigSATimeout", + "wps_fragment_size", "wps_version_number", "ampdu", + "tdls_testing", "tdls_disabled", "pno", "radio_disabled", + "uapsd", "ps", "wifi_display", "bssid_filter", "disallow_aps", + "no_keep_alive", + /* global configuration parameters */ + "eapol_version", "ap_scan", "disable_scan_offload", + "fast_reauth", "opensc_engine_path", "pkcs11_engine_path", + "pkcs11_module_path", "pcsc_reader", "pcsc_pin", + "driver_param", "dot11RSNAConfigPMKLifetime", + "dot11RSNAConfigPMKReauthThreshold", + "dot11RSNAConfigSATimeout", + "update_config", "load_dynamic_eap", "uuid", "device_name", + "manufacturer", "model_name", "model_number", "serial_number", + "device_type", "os_version", "config_methods", + "wps_cred_processing", "wps_vendor_ext_m1", "sec_device_type", + "p2p_listen_reg_class", "p2p_listen_channel", + "p2p_oper_reg_class", "p2p_oper_channel", + "p2p_go_intent", "p2p_ssid_postfix", "persistent_reconnect", + "p2p_intra_bss", "p2p_group_idle", "p2p_pref_chan", + "p2p_no_go_freq", + "p2p_go_ht40", "p2p_disabled", "p2p_no_group_iface", + "p2p_go_vht", + "p2p_ignore_shared_freq", "country", "bss_max_count", + "bss_expiration_age", "bss_expiration_scan_count", + "filter_ssids", "filter_rssi", "max_num_sta", + "disassoc_low_ack", "hs20", "interworking", "hessid", + "access_network_type", "pbc_in_m1", "autoscan", + "wps_nfc_dev_pw_id", "wps_nfc_dh_pubkey", "wps_nfc_dh_privkey", + "wps_nfc_dev_pw", "ext_password_backend", + "p2p_go_max_inactivity", "auto_interworking", "okc", "pmf", + "sae_groups", "dtim_period", "beacon_int", "ap_vendor_elements", + "ignore_old_scan_res", "freq_list", "external_sim", + "tdls_external_control" + }; + int i, num_fields = ARRAY_SIZE(fields); + + if (arg == 1) { + char **res = os_calloc(num_fields + 1, sizeof(char *)); + if (res == NULL) + return NULL; + for (i = 0; i < num_fields; i++) { + res[i] = os_strdup(fields[i]); + if (res[i] == NULL) + return res; + } + return res; } - return wpa_ctrl_command(ctrl, cmd); + + if (arg > 1 && os_strncasecmp(str, "set bssid_filter ", 17) == 0) + return cli_txt_list_array(&bsses); + + return NULL; } static int wpa_cli_cmd_get(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[256]; - int res; - - if (argc != 1) { - printf("Invalid GET command: need one argument (variable " - "name)\n"); - return -1; - } - - res = os_snprintf(cmd, sizeof(cmd), "GET %s", argv[0]); - if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { - printf("Too long GET command.\n"); - return -1; - } - return wpa_ctrl_command(ctrl, cmd); + return wpa_cli_cmd(ctrl, "GET", 1, argc, argv); } @@ -610,97 +679,48 @@ static int wpa_cli_cmd_preauthenticate(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[256]; - int res; - - if (argc != 1) { - printf("Invalid PREAUTH command: needs one argument " - "(BSSID)\n"); - return -1; - } - - res = os_snprintf(cmd, sizeof(cmd), "PREAUTH %s", argv[0]); - if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { - printf("Too long PREAUTH command.\n"); - return -1; - } - return wpa_ctrl_command(ctrl, cmd); + return wpa_cli_cmd(ctrl, "PREAUTH", 1, argc, argv); } static int wpa_cli_cmd_ap_scan(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[256]; - int res; - - if (argc != 1) { - printf("Invalid AP_SCAN command: needs one argument (ap_scan " - "value)\n"); - return -1; - } - res = os_snprintf(cmd, sizeof(cmd), "AP_SCAN %s", argv[0]); - if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { - printf("Too long AP_SCAN command.\n"); - return -1; - } - return wpa_ctrl_command(ctrl, cmd); + return wpa_cli_cmd(ctrl, "AP_SCAN", 1, argc, argv); } static int wpa_cli_cmd_scan_interval(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[256]; - int res; - - if (argc != 1) { - printf("Invalid SCAN_INTERVAL command: needs one argument " - "scan_interval value)\n"); - return -1; - } - res = os_snprintf(cmd, sizeof(cmd), "SCAN_INTERVAL %s", argv[0]); - if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { - printf("Too long SCAN_INTERVAL command.\n"); - return -1; - } - return wpa_ctrl_command(ctrl, cmd); + return wpa_cli_cmd(ctrl, "SCAN_INTERVAL", 1, argc, argv); } static int wpa_cli_cmd_bss_expire_age(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[256]; - int res; - - if (argc != 1) { - printf("Invalid BSS_EXPIRE_AGE command: needs one argument " - "(bss_expire_age value)\n"); - return -1; - } - res = os_snprintf(cmd, sizeof(cmd), "BSS_EXPIRE_AGE %s", argv[0]); - if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { - printf("Too long BSS_EXPIRE_AGE command.\n"); - return -1; - } - return wpa_ctrl_command(ctrl, cmd); + return wpa_cli_cmd(ctrl, "BSS_EXPIRE_AGE", 1, argc, argv); } static int wpa_cli_cmd_bss_expire_count(struct wpa_ctrl *ctrl, int argc, char *argv[]) { + return wpa_cli_cmd(ctrl, "BSS_EXPIRE_COUNT", 1, argc, argv); +} + + +static int wpa_cli_cmd_bss_flush(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ char cmd[256]; int res; - if (argc != 1) { - printf("Invalid BSS_EXPIRE_COUNT command: needs one argument " - "(bss_expire_count value)\n"); - return -1; - } - res = os_snprintf(cmd, sizeof(cmd), "BSS_EXPIRE_COUNT %s", argv[0]); + if (argc < 1) + res = os_snprintf(cmd, sizeof(cmd), "BSS_FLUSH 0"); + else + res = os_snprintf(cmd, sizeof(cmd), "BSS_FLUSH %s", argv[0]); if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { - printf("Too long BSS_EXPIRE_COUNT command.\n"); + printf("Too long BSS_FLUSH command.\n"); return -1; } return wpa_ctrl_command(ctrl, cmd); @@ -710,69 +730,24 @@ static int wpa_cli_cmd_stkstart(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[256]; - int res; - - if (argc != 1) { - printf("Invalid STKSTART command: needs one argument " - "(Peer STA MAC address)\n"); - return -1; - } - - res = os_snprintf(cmd, sizeof(cmd), "STKSTART %s", argv[0]); - if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { - printf("Too long STKSTART command.\n"); - return -1; - } - return wpa_ctrl_command(ctrl, cmd); + return wpa_cli_cmd(ctrl, "STKSTART", 1, argc, argv); } static int wpa_cli_cmd_ft_ds(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[256]; - int res; - - if (argc != 1) { - printf("Invalid FT_DS command: needs one argument " - "(Target AP MAC address)\n"); - return -1; - } - - res = os_snprintf(cmd, sizeof(cmd), "FT_DS %s", argv[0]); - if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { - printf("Too long FT_DS command.\n"); - return -1; - } - return wpa_ctrl_command(ctrl, cmd); + return wpa_cli_cmd(ctrl, "FT_DS", 1, argc, argv); } static int wpa_cli_cmd_wps_pbc(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[256]; - int res; - - if (argc == 0) { - /* Any BSSID */ - return wpa_ctrl_command(ctrl, "WPS_PBC"); - } - - /* Specific BSSID */ - res = os_snprintf(cmd, sizeof(cmd), "WPS_PBC %s", argv[0]); - if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { - printf("Too long WPS_PBC command.\n"); - return -1; - } - return wpa_ctrl_command(ctrl, cmd); + return wpa_cli_cmd(ctrl, "WPS_PBC", 0, argc, argv); } static int wpa_cli_cmd_wps_pin(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[256]; - int res; - if (argc == 0) { printf("Invalid WPS_PIN command: need one or two arguments:\n" "- BSSID: use 'any' to select any\n" @@ -781,49 +756,14 @@ return -1; } - if (argc == 1) { - /* Use dynamically generated PIN (returned as reply) */ - res = os_snprintf(cmd, sizeof(cmd), "WPS_PIN %s", argv[0]); - if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { - printf("Too long WPS_PIN command.\n"); - return -1; - } - return wpa_ctrl_command(ctrl, cmd); - } - - /* Use hardcoded PIN from a label */ - res = os_snprintf(cmd, sizeof(cmd), "WPS_PIN %s %s", argv[0], argv[1]); - if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { - printf("Too long WPS_PIN command.\n"); - return -1; - } - return wpa_ctrl_command(ctrl, cmd); + return wpa_cli_cmd(ctrl, "WPS_PIN", 1, argc, argv); } static int wpa_cli_cmd_wps_check_pin(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[256]; - int res; - - if (argc != 1 && argc != 2) { - printf("Invalid WPS_CHECK_PIN command: needs one argument:\n" - "- PIN to be verified\n"); - return -1; - } - - if (argc == 2) - res = os_snprintf(cmd, sizeof(cmd), "WPS_CHECK_PIN %s %s", - argv[0], argv[1]); - else - res = os_snprintf(cmd, sizeof(cmd), "WPS_CHECK_PIN %s", - argv[0]); - if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { - printf("Too long WPS_CHECK_PIN command.\n"); - return -1; - } - return wpa_ctrl_command(ctrl, cmd); + return wpa_cli_cmd(ctrl, "WPS_CHECK_PIN", 1, argc, argv); } @@ -834,60 +774,150 @@ } -#ifdef CONFIG_WPS_OOB -static int wpa_cli_cmd_wps_oob(struct wpa_ctrl *ctrl, int argc, char *argv[]) +#ifdef CONFIG_WPS_NFC + +static int wpa_cli_cmd_wps_nfc(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[256]; - int res; + return wpa_cli_cmd(ctrl, "WPS_NFC", 0, argc, argv); +} - if (argc != 3 && argc != 4) { - printf("Invalid WPS_OOB command: need three or four " - "arguments:\n" - "- DEV_TYPE: use 'ufd' or 'nfc'\n" - "- PATH: path of OOB device like '/mnt'\n" - "- METHOD: OOB method 'pin-e' or 'pin-r', " - "'cred'\n" - "- DEV_NAME: (only for NFC) device name like " - "'pn531'\n"); + +static int wpa_cli_cmd_wps_nfc_config_token(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "WPS_NFC_CONFIG_TOKEN", 1, argc, argv); +} + + +static int wpa_cli_cmd_wps_nfc_token(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "WPS_NFC_TOKEN", 1, argc, argv); +} + + +static int wpa_cli_cmd_wps_nfc_tag_read(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + int ret; + char *buf; + size_t buflen; + + if (argc != 1) { + printf("Invalid 'wps_nfc_tag_read' command - one argument " + "is required.\n"); return -1; } - if (argc == 3) - res = os_snprintf(cmd, sizeof(cmd), "WPS_OOB %s %s %s", - argv[0], argv[1], argv[2]); - else - res = os_snprintf(cmd, sizeof(cmd), "WPS_OOB %s %s %s %s", - argv[0], argv[1], argv[2], argv[3]); - if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { - printf("Too long WPS_OOB command.\n"); + buflen = 18 + os_strlen(argv[0]); + buf = os_malloc(buflen); + if (buf == NULL) return -1; - } - return wpa_ctrl_command(ctrl, cmd); + os_snprintf(buf, buflen, "WPS_NFC_TAG_READ %s", argv[0]); + + ret = wpa_ctrl_command(ctrl, buf); + os_free(buf); + + return ret; } -#endif /* CONFIG_WPS_OOB */ -static int wpa_cli_cmd_wps_reg(struct wpa_ctrl *ctrl, int argc, char *argv[]) +static int wpa_cli_cmd_nfc_get_handover_req(struct wpa_ctrl *ctrl, int argc, + char *argv[]) { - char cmd[256]; - int res; - - if (argc == 2) - res = os_snprintf(cmd, sizeof(cmd), "WPS_REG %s %s", - argv[0], argv[1]); - else if (argc == 5 || argc == 6) { - char ssid_hex[2 * 32 + 1]; - char key_hex[2 * 64 + 1]; - int i; + return wpa_cli_cmd(ctrl, "NFC_GET_HANDOVER_REQ", 2, argc, argv); +} - ssid_hex[0] = '\0'; - for (i = 0; i < 32; i++) { - if (argv[2][i] == '\0') - break; - os_snprintf(&ssid_hex[i * 2], 3, "%02x", argv[2][i]); - } - key_hex[0] = '\0'; +static int wpa_cli_cmd_nfc_get_handover_sel(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "NFC_GET_HANDOVER_SEL", 2, argc, argv); +} + + +static int wpa_cli_cmd_nfc_rx_handover_req(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + int ret; + char *buf; + size_t buflen; + + if (argc != 1) { + printf("Invalid 'nfc_rx_handover_req' command - one argument " + "is required.\n"); + return -1; + } + + buflen = 21 + os_strlen(argv[0]); + buf = os_malloc(buflen); + if (buf == NULL) + return -1; + os_snprintf(buf, buflen, "NFC_RX_HANDOVER_REQ %s", argv[0]); + + ret = wpa_ctrl_command(ctrl, buf); + os_free(buf); + + return ret; +} + + +static int wpa_cli_cmd_nfc_rx_handover_sel(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + int ret; + char *buf; + size_t buflen; + + if (argc != 1) { + printf("Invalid 'nfc_rx_handover_sel' command - one argument " + "is required.\n"); + return -1; + } + + buflen = 21 + os_strlen(argv[0]); + buf = os_malloc(buflen); + if (buf == NULL) + return -1; + os_snprintf(buf, buflen, "NFC_RX_HANDOVER_SEL %s", argv[0]); + + ret = wpa_ctrl_command(ctrl, buf); + os_free(buf); + + return ret; +} + + +static int wpa_cli_cmd_nfc_report_handover(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "NFC_REPORT_HANDOVER", 4, argc, argv); +} + +#endif /* CONFIG_WPS_NFC */ + + +static int wpa_cli_cmd_wps_reg(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + char cmd[256]; + int res; + + if (argc == 2) + res = os_snprintf(cmd, sizeof(cmd), "WPS_REG %s %s", + argv[0], argv[1]); + else if (argc == 5 || argc == 6) { + char ssid_hex[2 * 32 + 1]; + char key_hex[2 * 64 + 1]; + int i; + + ssid_hex[0] = '\0'; + for (i = 0; i < 32; i++) { + if (argv[2][i] == '\0') + break; + os_snprintf(&ssid_hex[i * 2], 3, "%02x", argv[2][i]); + } + + key_hex[0] = '\0'; if (argc == 6) { for (i = 0; i < 64; i++) { if (argv[5][i] == '\0') @@ -927,41 +957,14 @@ static int wpa_cli_cmd_wps_ap_pin(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[256]; - int res; - - if (argc < 1) { - printf("Invalid WPS_AP_PIN command: needs at least one " - "argument\n"); - return -1; - } - - if (argc > 2) - res = os_snprintf(cmd, sizeof(cmd), "WPS_AP_PIN %s %s %s", - argv[0], argv[1], argv[2]); - else if (argc > 1) - res = os_snprintf(cmd, sizeof(cmd), "WPS_AP_PIN %s %s", - argv[0], argv[1]); - else - res = os_snprintf(cmd, sizeof(cmd), "WPS_AP_PIN %s", - argv[0]); - if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { - printf("Too long WPS_AP_PIN command.\n"); - return -1; - } - return wpa_ctrl_command(ctrl, cmd); + return wpa_cli_cmd(ctrl, "WPS_AP_PIN", 1, argc, argv); } static int wpa_cli_cmd_wps_er_start(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[100]; - if (argc > 0) { - os_snprintf(cmd, sizeof(cmd), "WPS_ER_START %s", argv[0]); - return wpa_ctrl_command(ctrl, cmd); - } - return wpa_ctrl_command(ctrl, "WPS_ER_START"); + return wpa_cli_cmd(ctrl, "WPS_ER_START", 0, argc, argv); } @@ -976,9 +979,6 @@ static int wpa_cli_cmd_wps_er_pin(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[256]; - int res; - if (argc < 2) { printf("Invalid WPS_ER_PIN command: need at least two " "arguments:\n" @@ -988,48 +988,20 @@ return -1; } - if (argc > 2) - res = os_snprintf(cmd, sizeof(cmd), "WPS_ER_PIN %s %s %s", - argv[0], argv[1], argv[2]); - else - res = os_snprintf(cmd, sizeof(cmd), "WPS_ER_PIN %s %s", - argv[0], argv[1]); - if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { - printf("Too long WPS_ER_PIN command.\n"); - return -1; - } - return wpa_ctrl_command(ctrl, cmd); + return wpa_cli_cmd(ctrl, "WPS_ER_PIN", 2, argc, argv); } static int wpa_cli_cmd_wps_er_pbc(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[256]; - int res; - - if (argc != 1) { - printf("Invalid WPS_ER_PBC command: need one argument:\n" - "- UUID: Specify the Enrollee\n"); - return -1; - } - - res = os_snprintf(cmd, sizeof(cmd), "WPS_ER_PBC %s", - argv[0]); - if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { - printf("Too long WPS_ER_PBC command.\n"); - return -1; - } - return wpa_ctrl_command(ctrl, cmd); + return wpa_cli_cmd(ctrl, "WPS_ER_PBC", 1, argc, argv); } static int wpa_cli_cmd_wps_er_learn(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[256]; - int res; - if (argc != 2) { printf("Invalid WPS_ER_LEARN command: need two arguments:\n" "- UUID: specify which AP to use\n" @@ -1037,22 +1009,13 @@ return -1; } - res = os_snprintf(cmd, sizeof(cmd), "WPS_ER_LEARN %s %s", - argv[0], argv[1]); - if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { - printf("Too long WPS_ER_LEARN command.\n"); - return -1; - } - return wpa_ctrl_command(ctrl, cmd); + return wpa_cli_cmd(ctrl, "WPS_ER_LEARN", 2, argc, argv); } static int wpa_cli_cmd_wps_er_set_config(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[256]; - int res; - if (argc != 2) { printf("Invalid WPS_ER_SET_CONFIG command: need two " "arguments:\n" @@ -1061,13 +1024,7 @@ return -1; } - res = os_snprintf(cmd, sizeof(cmd), "WPS_ER_SET_CONFIG %s %s", - argv[0], argv[1]); - if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { - printf("Too long WPS_ER_SET_CONFIG command.\n"); - return -1; - } - return wpa_ctrl_command(ctrl, cmd); + return wpa_cli_cmd(ctrl, "WPS_ER_SET_CONFIG", 2, argc, argv); } @@ -1122,42 +1079,32 @@ } -static int wpa_cli_cmd_ibss_rsn(struct wpa_ctrl *ctrl, int argc, char *argv[]) +#ifdef CONFIG_WPS_NFC +static int wpa_cli_cmd_wps_er_nfc_config_token(struct wpa_ctrl *ctrl, int argc, + char *argv[]) { - char cmd[256]; - int res; - - if (argc != 1) { - printf("Invalid IBSS_RSN command: needs one argument " - "(Peer STA MAC address)\n"); + if (argc != 2) { + printf("Invalid WPS_ER_NFC_CONFIG_TOKEN command: need two " + "arguments:\n" + "- WPS/NDEF: token format\n" + "- UUID: specify which AP to use\n"); return -1; } - res = os_snprintf(cmd, sizeof(cmd), "IBSS_RSN %s", argv[0]); - if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { - printf("Too long IBSS_RSN command.\n"); - return -1; - } - return wpa_ctrl_command(ctrl, cmd); + return wpa_cli_cmd(ctrl, "WPS_ER_NFC_CONFIG_TOKEN", 2, argc, argv); } +#endif /* CONFIG_WPS_NFC */ -static int wpa_cli_cmd_level(struct wpa_ctrl *ctrl, int argc, char *argv[]) +static int wpa_cli_cmd_ibss_rsn(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[256]; - int res; + return wpa_cli_cmd(ctrl, "IBSS_RSN", 1, argc, argv); +} - if (argc != 1) { - printf("Invalid LEVEL command: needs one argument (debug " - "level)\n"); - return -1; - } - res = os_snprintf(cmd, sizeof(cmd), "LEVEL %s", argv[0]); - if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { - printf("Too long LEVEL command.\n"); - return -1; - } - return wpa_ctrl_command(ctrl, cmd); + +static int wpa_cli_cmd_level(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + return wpa_cli_cmd(ctrl, "LEVEL", 1, argc, argv); } @@ -1326,63 +1273,63 @@ } -static int wpa_cli_cmd_passphrase(struct wpa_ctrl *ctrl, int argc, - char *argv[]) +static int wpa_cli_cmd_sim(struct wpa_ctrl *ctrl, int argc, char *argv[]) { char cmd[256], *pos, *end; int i, ret; if (argc < 2) { - printf("Invalid PASSPHRASE command: needs two arguments " - "(network id and passphrase)\n"); + printf("Invalid SIM command: needs two arguments " + "(network id and SIM operation response)\n"); return -1; } end = cmd + sizeof(cmd); pos = cmd; - ret = os_snprintf(pos, end - pos, WPA_CTRL_RSP "PASSPHRASE-%s:%s", + ret = os_snprintf(pos, end - pos, WPA_CTRL_RSP "SIM-%s:%s", argv[0], argv[1]); if (ret < 0 || ret >= end - pos) { - printf("Too long PASSPHRASE command.\n"); + printf("Too long SIM command.\n"); return -1; } pos += ret; for (i = 2; i < argc; i++) { ret = os_snprintf(pos, end - pos, " %s", argv[i]); if (ret < 0 || ret >= end - pos) { - printf("Too long PASSPHRASE command.\n"); + printf("Too long SIM command.\n"); return -1; } pos += ret; } - return wpa_ctrl_command(ctrl, cmd); } -static int wpa_cli_cmd_bssid(struct wpa_ctrl *ctrl, int argc, char *argv[]) +static int wpa_cli_cmd_passphrase(struct wpa_ctrl *ctrl, int argc, + char *argv[]) { char cmd[256], *pos, *end; int i, ret; if (argc < 2) { - printf("Invalid BSSID command: needs two arguments (network " - "id and BSSID)\n"); + printf("Invalid PASSPHRASE command: needs two arguments " + "(network id and passphrase)\n"); return -1; } end = cmd + sizeof(cmd); pos = cmd; - ret = os_snprintf(pos, end - pos, "BSSID"); + ret = os_snprintf(pos, end - pos, WPA_CTRL_RSP "PASSPHRASE-%s:%s", + argv[0], argv[1]); if (ret < 0 || ret >= end - pos) { - printf("Too long BSSID command.\n"); + printf("Too long PASSPHRASE command.\n"); return -1; } pos += ret; - for (i = 0; i < argc; i++) { + for (i = 2; i < argc; i++) { ret = os_snprintf(pos, end - pos, " %s", argv[i]); if (ret < 0 || ret >= end - pos) { - printf("Too long BSSID command.\n"); + printf("Too long PASSPHRASE command.\n"); return -1; } pos += ret; @@ -1392,55 +1339,27 @@ } -static int wpa_cli_cmd_blacklist(struct wpa_ctrl *ctrl, int argc, char *argv[]) +static int wpa_cli_cmd_bssid(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[256], *pos, *end; - int i, ret; - - end = cmd + sizeof(cmd); - pos = cmd; - ret = os_snprintf(pos, end - pos, "BLACKLIST"); - if (ret < 0 || ret >= end - pos) { - printf("Too long BLACKLIST command.\n"); + if (argc < 2) { + printf("Invalid BSSID command: needs two arguments (network " + "id and BSSID)\n"); return -1; } - pos += ret; - for (i = 0; i < argc; i++) { - ret = os_snprintf(pos, end - pos, " %s", argv[i]); - if (ret < 0 || ret >= end - pos) { - printf("Too long BLACKLIST command.\n"); - return -1; - } - pos += ret; - } - return wpa_ctrl_command(ctrl, cmd); + return wpa_cli_cmd(ctrl, "BSSID", 2, argc, argv); } -static int wpa_cli_cmd_log_level(struct wpa_ctrl *ctrl, int argc, char *argv[]) +static int wpa_cli_cmd_blacklist(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[256], *pos, *end; - int i, ret; + return wpa_cli_cmd(ctrl, "BLACKLIST", 0, argc, argv); +} - end = cmd + sizeof(cmd); - pos = cmd; - ret = os_snprintf(pos, end - pos, "LOG_LEVEL"); - if (ret < 0 || ret >= end - pos) { - printf("Too long LOG_LEVEL command.\n"); - return -1; - } - pos += ret; - for (i = 0; i < argc; i++) { - ret = os_snprintf(pos, end - pos, " %s", argv[i]); - if (ret < 0 || ret >= end - pos) { - printf("Too long LOG_LEVEL command.\n"); - return -1; - } - pos += ret; - } - return wpa_ctrl_command(ctrl, cmd); +static int wpa_cli_cmd_log_level(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + return wpa_cli_cmd(ctrl, "LOG_LEVEL", 0, argc, argv); } @@ -1454,63 +1373,21 @@ static int wpa_cli_cmd_select_network(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[32]; - int res; - - if (argc < 1) { - printf("Invalid SELECT_NETWORK command: needs one argument " - "(network id)\n"); - return -1; - } - - res = os_snprintf(cmd, sizeof(cmd), "SELECT_NETWORK %s", argv[0]); - if (res < 0 || (size_t) res >= sizeof(cmd)) - return -1; - cmd[sizeof(cmd) - 1] = '\0'; - - return wpa_ctrl_command(ctrl, cmd); + return wpa_cli_cmd(ctrl, "SELECT_NETWORK", 1, argc, argv); } static int wpa_cli_cmd_enable_network(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[32]; - int res; - - if (argc < 1) { - printf("Invalid ENABLE_NETWORK command: needs one argument " - "(network id)\n"); - return -1; - } - - res = os_snprintf(cmd, sizeof(cmd), "ENABLE_NETWORK %s", argv[0]); - if (res < 0 || (size_t) res >= sizeof(cmd)) - return -1; - cmd[sizeof(cmd) - 1] = '\0'; - - return wpa_ctrl_command(ctrl, cmd); + return wpa_cli_cmd(ctrl, "ENABLE_NETWORK", 1, argc, argv); } static int wpa_cli_cmd_disable_network(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[32]; - int res; - - if (argc < 1) { - printf("Invalid DISABLE_NETWORK command: needs one argument " - "(network id)\n"); - return -1; - } - - res = os_snprintf(cmd, sizeof(cmd), "DISABLE_NETWORK %s", argv[0]); - if (res < 0 || (size_t) res >= sizeof(cmd)) - return -1; - cmd[sizeof(cmd) - 1] = '\0'; - - return wpa_ctrl_command(ctrl, cmd); + return wpa_cli_cmd(ctrl, "DISABLE_NETWORK", 1, argc, argv); } @@ -1524,21 +1401,7 @@ static int wpa_cli_cmd_remove_network(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[32]; - int res; - - if (argc < 1) { - printf("Invalid REMOVE_NETWORK command: needs one argument " - "(network id)\n"); - return -1; - } - - res = os_snprintf(cmd, sizeof(cmd), "REMOVE_NETWORK %s", argv[0]); - if (res < 0 || (size_t) res >= sizeof(cmd)) - return -1; - cmd[sizeof(cmd) - 1] = '\0'; - - return wpa_ctrl_command(ctrl, cmd); + return wpa_cli_cmd(ctrl, "REMOVE_NETWORK", 1, argc, argv); } @@ -1566,36 +1429,24 @@ static int wpa_cli_cmd_set_network(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[256]; - int res; - if (argc == 0) { wpa_cli_show_network_variables(); return 0; } - if (argc != 3) { + if (argc < 3) { printf("Invalid SET_NETWORK command: needs three arguments\n" "(network id, variable name, and value)\n"); return -1; } - res = os_snprintf(cmd, sizeof(cmd), "SET_NETWORK %s %s %s", - argv[0], argv[1], argv[2]); - if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { - printf("Too long SET_NETWORK command.\n"); - return -1; - } - return wpa_ctrl_command(ctrl, cmd); + return wpa_cli_cmd(ctrl, "SET_NETWORK", 3, argc, argv); } static int wpa_cli_cmd_get_network(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[256]; - int res; - if (argc == 0) { wpa_cli_show_network_variables(); return 0; @@ -1607,13 +1458,39 @@ return -1; } - res = os_snprintf(cmd, sizeof(cmd), "GET_NETWORK %s %s", - argv[0], argv[1]); - if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { - printf("Too long GET_NETWORK command.\n"); + return wpa_cli_cmd(ctrl, "GET_NETWORK", 2, argc, argv); +} + + +static int wpa_cli_cmd_list_creds(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "LIST_CREDS"); +} + + +static int wpa_cli_cmd_add_cred(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + return wpa_ctrl_command(ctrl, "ADD_CRED"); +} + + +static int wpa_cli_cmd_remove_cred(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "REMOVE_CRED", 1, argc, argv); +} + + +static int wpa_cli_cmd_set_cred(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + if (argc != 3) { + printf("Invalid SET_CRED command: needs three arguments\n" + "(cred id, variable name, and value)\n"); return -1; } - return wpa_ctrl_command(ctrl, cmd); + + return wpa_cli_cmd(ctrl, "SET_CRED", 3, argc, argv); } @@ -1640,7 +1517,7 @@ static int wpa_cli_cmd_scan(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - return wpa_ctrl_command(ctrl, "SCAN"); + return wpa_cli_cmd(ctrl, "SCAN", 0, argc, argv); } @@ -1653,21 +1530,7 @@ static int wpa_cli_cmd_bss(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[64]; - int res; - - if (argc != 1) { - printf("Invalid BSS command: need one argument (index or " - "BSSID)\n"); - return -1; - } - - res = os_snprintf(cmd, sizeof(cmd), "BSS %s", argv[0]); - if (res < 0 || (size_t) res >= sizeof(cmd)) - return -1; - cmd[sizeof(cmd) - 1] = '\0'; - - return wpa_ctrl_command(ctrl, cmd); + return wpa_cli_cmd(ctrl, "BSS", 1, argc, argv); } @@ -1689,9 +1552,6 @@ static int wpa_cli_cmd_get_capability(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[64]; - int res; - if (argc < 1 || argc > 2) { printf("Invalid GET_CAPABILITY command: need either one or " "two arguments\n"); @@ -1704,13 +1564,7 @@ return -1; } - res = os_snprintf(cmd, sizeof(cmd), "GET_CAPABILITY %s%s", argv[0], - (argc == 2) ? " strict" : ""); - if (res < 0 || (size_t) res >= sizeof(cmd)) - return -1; - cmd[sizeof(cmd) - 1] = '\0'; - - return wpa_ctrl_command(ctrl, cmd); + return wpa_cli_cmd(ctrl, "GET_CAPABILITY", 1, argc, argv); } @@ -1790,20 +1644,7 @@ static int wpa_cli_cmd_interface_remove(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[128]; - int res; - - if (argc != 1) { - printf("Invalid INTERFACE_REMOVE command: needs one argument " - "(interface name)\n"); - return -1; - } - - res = os_snprintf(cmd, sizeof(cmd), "INTERFACE_REMOVE %s", argv[0]); - if (res < 0 || (size_t) res >= sizeof(cmd)) - return -1; - cmd[sizeof(cmd) - 1] = '\0'; - return wpa_ctrl_command(ctrl, cmd); + return wpa_cli_cmd(ctrl, "INTERFACE_REMOVE", 1, argc, argv); } @@ -1817,14 +1658,7 @@ #ifdef CONFIG_AP static int wpa_cli_cmd_sta(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char buf[64]; - if (argc != 1) { - printf("Invalid 'sta' command - exactly one argument, STA " - "address, is required.\n"); - return -1; - } - os_snprintf(buf, sizeof(buf), "STA %s", argv[0]); - return wpa_ctrl_command(ctrl, buf); + return wpa_cli_cmd(ctrl, "STA", 1, argc, argv); } @@ -1840,7 +1674,7 @@ return -1; } len = sizeof(buf) - 1; - ret = wpa_ctrl_request(ctrl, cmd, strlen(cmd), buf, &len, + ret = wpa_ctrl_request(ctrl, cmd, os_strlen(cmd), buf, &len, wpa_cli_msg_cb); if (ret == -2) { printf("'%s' command timed out.\n", cmd); @@ -1851,7 +1685,7 @@ } buf[len] = '\0'; - if (memcmp(buf, "FAIL", 4) == 0) + if (os_memcmp(buf, "FAIL", 4) == 0) return -1; printf("%s", buf); @@ -1876,6 +1710,27 @@ return -1; } + + +static int wpa_cli_cmd_deauthenticate(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "DEAUTHENTICATE", 1, argc, argv); +} + + +static int wpa_cli_cmd_disassociate(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "DISASSOCIATE", 1, argc, argv); +} + +static int wpa_cli_cmd_chanswitch(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "CHAN_SWITCH", 2, argc, argv); +} + #endif /* CONFIG_AP */ @@ -1899,21 +1754,7 @@ static int wpa_cli_cmd_roam(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[128]; - int res; - - if (argc != 1) { - printf("Invalid ROAM command: needs one argument " - "(target AP's BSSID)\n"); - return -1; - } - - res = os_snprintf(cmd, sizeof(cmd), "ROAM %s", argv[0]); - if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { - printf("Too long ROAM command.\n"); - return -1; - } - return wpa_ctrl_command(ctrl, cmd); + return wpa_cli_cmd(ctrl, "ROAM", 1, argc, argv); } @@ -1921,24 +1762,36 @@ static int wpa_cli_cmd_p2p_find(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[128]; - int res; + return wpa_cli_cmd(ctrl, "P2P_FIND", 0, argc, argv); +} - if (argc == 0) - return wpa_ctrl_command(ctrl, "P2P_FIND"); - if (argc > 2) - res = os_snprintf(cmd, sizeof(cmd), "P2P_FIND %s %s %s", - argv[0], argv[1], argv[2]); - else if (argc > 1) - res = os_snprintf(cmd, sizeof(cmd), "P2P_FIND %s %s", - argv[0], argv[1]); - else - res = os_snprintf(cmd, sizeof(cmd), "P2P_FIND %s", argv[0]); - if (res < 0 || (size_t) res >= sizeof(cmd)) - return -1; - cmd[sizeof(cmd) - 1] = '\0'; - return wpa_ctrl_command(ctrl, cmd); +static char ** wpa_cli_complete_p2p_find(const char *str, int pos) +{ + char **res = NULL; + int arg = get_cmd_arg_num(str, pos); + + res = os_calloc(6, sizeof(char *)); + if (res == NULL) + return NULL; + res[0] = os_strdup("type=social"); + if (res[0] == NULL) { + os_free(res); + return NULL; + } + res[1] = os_strdup("type=progressive"); + if (res[1] == NULL) + return res; + res[2] = os_strdup("delay="); + if (res[2] == NULL) + return res; + res[3] = os_strdup("dev_id="); + if (res[3] == NULL) + return res; + if (arg == 1) + res[4] = os_strdup("[timeout]"); + + return res; } @@ -1952,33 +1805,7 @@ static int wpa_cli_cmd_p2p_connect(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[128]; - int res; - - if (argc < 2) { - printf("Invalid P2P_CONNECT command: needs at least two " - "arguments (address and pbc/PIN)\n"); - return -1; - } - - if (argc > 4) - res = os_snprintf(cmd, sizeof(cmd), - "P2P_CONNECT %s %s %s %s %s", - argv[0], argv[1], argv[2], argv[3], - argv[4]); - else if (argc > 3) - res = os_snprintf(cmd, sizeof(cmd), "P2P_CONNECT %s %s %s %s", - argv[0], argv[1], argv[2], argv[3]); - else if (argc > 2) - res = os_snprintf(cmd, sizeof(cmd), "P2P_CONNECT %s %s %s", - argv[0], argv[1], argv[2]); - else - res = os_snprintf(cmd, sizeof(cmd), "P2P_CONNECT %s %s", - argv[0], argv[1]); - if (res < 0 || (size_t) res >= sizeof(cmd)) - return -1; - cmd[sizeof(cmd) - 1] = '\0'; - return wpa_ctrl_command(ctrl, cmd); + return wpa_cli_cmd(ctrl, "P2P_CONNECT", 2, argc, argv); } @@ -2000,37 +1827,14 @@ static int wpa_cli_cmd_p2p_listen(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[128]; - int res; - - if (argc == 0) - return wpa_ctrl_command(ctrl, "P2P_LISTEN"); - - res = os_snprintf(cmd, sizeof(cmd), "P2P_LISTEN %s", argv[0]); - if (res < 0 || (size_t) res >= sizeof(cmd)) - return -1; - cmd[sizeof(cmd) - 1] = '\0'; - return wpa_ctrl_command(ctrl, cmd); + return wpa_cli_cmd(ctrl, "P2P_LISTEN", 0, argc, argv); } static int wpa_cli_cmd_p2p_group_remove(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[128]; - int res; - - if (argc != 1) { - printf("Invalid P2P_GROUP_REMOVE command: needs one argument " - "(interface name)\n"); - return -1; - } - - res = os_snprintf(cmd, sizeof(cmd), "P2P_GROUP_REMOVE %s", argv[0]); - if (res < 0 || (size_t) res >= sizeof(cmd)) - return -1; - cmd[sizeof(cmd) - 1] = '\0'; - return wpa_ctrl_command(ctrl, cmd); + return wpa_cli_cmd(ctrl, "P2P_GROUP_REMOVE", 1, argc, argv); } @@ -2052,31 +1856,13 @@ static int wpa_cli_cmd_p2p_group_add(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[128]; - int res; - - if (argc == 0) - return wpa_ctrl_command(ctrl, "P2P_GROUP_ADD"); - - if (argc > 1) - res = os_snprintf(cmd, sizeof(cmd), "P2P_GROUP_ADD %s %s", - argv[0], argv[1]); - else - res = os_snprintf(cmd, sizeof(cmd), "P2P_GROUP_ADD %s", - argv[0]); - if (res < 0 || (size_t) res >= sizeof(cmd)) - return -1; - cmd[sizeof(cmd) - 1] = '\0'; - return wpa_ctrl_command(ctrl, cmd); + return wpa_cli_cmd(ctrl, "P2P_GROUP_ADD", 0, argc, argv); } static int wpa_cli_cmd_p2p_prov_disc(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[128]; - int res; - if (argc != 2 && argc != 3) { printf("Invalid P2P_PROV_DISC command: needs at least " "two arguments, address and config method\n" @@ -2084,16 +1870,7 @@ return -1; } - if (argc == 3) - res = os_snprintf(cmd, sizeof(cmd), "P2P_PROV_DISC %s %s %s", - argv[0], argv[1], argv[2]); - else - res = os_snprintf(cmd, sizeof(cmd), "P2P_PROV_DISC %s %s", - argv[0], argv[1]); - if (res < 0 || (size_t) res >= sizeof(cmd)) - return -1; - cmd[sizeof(cmd) - 1] = '\0'; - return wpa_ctrl_command(ctrl, cmd); + return wpa_cli_cmd(ctrl, "P2P_PROV_DISC", 2, argc, argv); } @@ -2108,7 +1885,6 @@ char *argv[]) { char cmd[4096]; - int res; if (argc != 2 && argc != 4) { printf("Invalid P2P_SERV_DISC_REQ command: needs two " @@ -2118,16 +1894,8 @@ return -1; } - if (argc == 4) - res = os_snprintf(cmd, sizeof(cmd), - "P2P_SERV_DISC_REQ %s %s %s %s", - argv[0], argv[1], argv[2], argv[3]); - else - res = os_snprintf(cmd, sizeof(cmd), "P2P_SERV_DISC_REQ %s %s", - argv[0], argv[1]); - if (res < 0 || (size_t) res >= sizeof(cmd)) + if (write_cmd(cmd, sizeof(cmd), "P2P_SERV_DISC_REQ", argc, argv) < 0) return -1; - cmd[sizeof(cmd) - 1] = '\0'; return wpa_ctrl_command(ctrl, cmd); } @@ -2135,21 +1903,7 @@ static int wpa_cli_cmd_p2p_serv_disc_cancel_req(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[128]; - int res; - - if (argc != 1) { - printf("Invalid P2P_SERV_DISC_CANCEL_REQ command: needs one " - "argument (pending request identifier)\n"); - return -1; - } - - res = os_snprintf(cmd, sizeof(cmd), "P2P_SERV_DISC_CANCEL_REQ %s", - argv[0]); - if (res < 0 || (size_t) res >= sizeof(cmd)) - return -1; - cmd[sizeof(cmd) - 1] = '\0'; - return wpa_ctrl_command(ctrl, cmd); + return wpa_cli_cmd(ctrl, "P2P_SERV_DISC_CANCEL_REQ", 1, argc, argv); } @@ -2184,21 +1938,7 @@ static int wpa_cli_cmd_p2p_serv_disc_external(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[128]; - int res; - - if (argc != 1) { - printf("Invalid P2P_SERV_DISC_EXTERNAL command: needs one " - "argument (external processing: 0/1)\n"); - return -1; - } - - res = os_snprintf(cmd, sizeof(cmd), "P2P_SERV_DISC_EXTERNAL %s", - argv[0]); - if (res < 0 || (size_t) res >= sizeof(cmd)) - return -1; - cmd[sizeof(cmd) - 1] = '\0'; - return wpa_ctrl_command(ctrl, cmd); + return wpa_cli_cmd(ctrl, "P2P_SERV_DISC_EXTERNAL", 1, argc, argv); } @@ -2266,60 +2006,20 @@ static int wpa_cli_cmd_p2p_reject(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[128]; - int res; - - if (argc != 1) { - printf("Invalid P2P_REJECT command: needs one argument " - "(peer address)\n"); - return -1; - } - - res = os_snprintf(cmd, sizeof(cmd), "P2P_REJECT %s", argv[0]); - if (res < 0 || (size_t) res >= sizeof(cmd)) - return -1; - cmd[sizeof(cmd) - 1] = '\0'; - return wpa_ctrl_command(ctrl, cmd); + return wpa_cli_cmd(ctrl, "P2P_REJECT", 1, argc, argv); } static int wpa_cli_cmd_p2p_invite(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[128]; - int res; - - if (argc < 1) { - printf("Invalid P2P_INVITE command: needs at least one " - "argument\n"); - return -1; - } - - if (argc > 2) - res = os_snprintf(cmd, sizeof(cmd), "P2P_INVITE %s %s %s", - argv[0], argv[1], argv[2]); - else if (argc > 1) - res = os_snprintf(cmd, sizeof(cmd), "P2P_INVITE %s %s", - argv[0], argv[1]); - else - res = os_snprintf(cmd, sizeof(cmd), "P2P_INVITE %s", argv[0]); - if (res < 0 || (size_t) res >= sizeof(cmd)) - return -1; - cmd[sizeof(cmd) - 1] = '\0'; - return wpa_ctrl_command(ctrl, cmd); + return wpa_cli_cmd(ctrl, "P2P_INVITE", 1, argc, argv); } static int wpa_cli_cmd_p2p_peer(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char buf[64]; - if (argc != 1) { - printf("Invalid 'p2p_peer' command - exactly one argument, " - "P2P peer device address, is required.\n"); - return -1; - } - os_snprintf(buf, sizeof(buf), "P2P_PEER %s", argv[0]); - return wpa_ctrl_command(ctrl, buf); + return wpa_cli_cmd(ctrl, "P2P_PEER", 1, argc, argv); } @@ -2349,7 +2049,7 @@ if (ctrl_conn == NULL) return -1; len = sizeof(buf) - 1; - ret = wpa_ctrl_request(ctrl, cmd, strlen(cmd), buf, &len, + ret = wpa_ctrl_request(ctrl, cmd, os_strlen(cmd), buf, &len, wpa_cli_msg_cb); if (ret == -2) { printf("'%s' command timed out.\n", cmd); @@ -2360,7 +2060,7 @@ } buf[len] = '\0'; - if (memcmp(buf, "FAIL", 4) == 0) + if (os_memcmp(buf, "FAIL", 4) == 0) return -1; pos = buf; @@ -2395,20 +2095,51 @@ static int wpa_cli_cmd_p2p_set(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[100]; - int res; + return wpa_cli_cmd(ctrl, "P2P_SET", 2, argc, argv); +} - if (argc != 2) { - printf("Invalid P2P_SET command: needs two arguments (field, " - "value)\n"); - return -1; + +static char ** wpa_cli_complete_p2p_set(const char *str, int pos) +{ + int arg = get_cmd_arg_num(str, pos); + const char *fields[] = { + "discoverability", + "managed", + "listen_channel", + "ssid_postfix", + "noa", + "ps", + "oppps", + "ctwindow", + "disabled", + "conc_pref", + "force_long_sd", + "peer_filter", + "cross_connect", + "go_apsd", + "client_apsd", + "disallow_freq", + "disc_int", + "per_sta_psk", + }; + int i, num_fields = ARRAY_SIZE(fields); + + if (arg == 1) { + char **res = os_calloc(num_fields + 1, sizeof(char *)); + if (res == NULL) + return NULL; + for (i = 0; i < num_fields; i++) { + res[i] = os_strdup(fields[i]); + if (res[i] == NULL) + return res; + } + return res; } - res = os_snprintf(cmd, sizeof(cmd), "P2P_SET %s %s", argv[0], argv[1]); - if (res < 0 || (size_t) res >= sizeof(cmd)) - return -1; - cmd[sizeof(cmd) - 1] = '\0'; - return wpa_ctrl_command(ctrl, cmd); + if (arg == 2 && os_strncasecmp(str, "p2p_set peer_filter ", 20) == 0) + return cli_txt_list_array(&p2p_peers); + + return NULL; } @@ -2428,48 +2159,65 @@ static int wpa_cli_cmd_p2p_unauthorize(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[100]; - int res; + return wpa_cli_cmd(ctrl, "P2P_UNAUTHORIZE", 1, argc, argv); +} - if (argc != 1) { - printf("Invalid P2P_UNAUTHORIZE command: needs one argument " - "(peer address)\n"); + +static int wpa_cli_cmd_p2p_presence_req(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + if (argc != 0 && argc != 2 && argc != 4) { + printf("Invalid P2P_PRESENCE_REQ command: needs two arguments " + "(preferred duration, interval; in microsecods).\n" + "Optional second pair can be used to provide " + "acceptable values.\n"); return -1; } - res = os_snprintf(cmd, sizeof(cmd), "P2P_UNAUTHORIZE %s", argv[0]); + return wpa_cli_cmd(ctrl, "P2P_PRESENCE_REQ", 0, argc, argv); +} - if (res < 0 || (size_t) res >= sizeof(cmd)) + +static int wpa_cli_cmd_p2p_ext_listen(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + if (argc != 0 && argc != 2) { + printf("Invalid P2P_EXT_LISTEN command: needs two arguments " + "(availability period, availability interval; in " + "millisecods).\n" + "Extended Listen Timing can be cancelled with this " + "command when used without parameters.\n"); return -1; + } - cmd[sizeof(cmd) - 1] = '\0'; - return wpa_ctrl_command(ctrl, cmd); + return wpa_cli_cmd(ctrl, "P2P_EXT_LISTEN", 0, argc, argv); } -static int wpa_cli_cmd_p2p_presence_req(struct wpa_ctrl *ctrl, int argc, - char *argv[]) +static int wpa_cli_cmd_p2p_remove_client(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "P2P_REMOVE_CLIENT", 1, argc, argv); +} + +#endif /* CONFIG_P2P */ + +#ifdef CONFIG_WIFI_DISPLAY + +static int wpa_cli_cmd_wfd_subelem_set(struct wpa_ctrl *ctrl, int argc, + char *argv[]) { char cmd[100]; int res; - if (argc != 0 && argc != 2 && argc != 4) { - printf("Invalid P2P_PRESENCE_REQ command: needs two arguments " - "(preferred duration, interval; in microsecods).\n" - "Optional second pair can be used to provide " - "acceptable values.\n"); + if (argc != 1 && argc != 2) { + printf("Invalid WFD_SUBELEM_SET command: needs one or two " + "arguments (subelem, hexdump)\n"); return -1; } - if (argc == 4) - res = os_snprintf(cmd, sizeof(cmd), - "P2P_PRESENCE_REQ %s %s %s %s", - argv[0], argv[1], argv[2], argv[3]); - else if (argc == 2) - res = os_snprintf(cmd, sizeof(cmd), "P2P_PRESENCE_REQ %s %s", - argv[0], argv[1]); - else - res = os_snprintf(cmd, sizeof(cmd), "P2P_PRESENCE_REQ"); + res = os_snprintf(cmd, sizeof(cmd), "WFD_SUBELEM_SET %s %s", + argv[0], argc > 1 ? argv[1] : ""); if (res < 0 || (size_t) res >= sizeof(cmd)) return -1; cmd[sizeof(cmd) - 1] = '\0'; @@ -2477,33 +2225,26 @@ } -static int wpa_cli_cmd_p2p_ext_listen(struct wpa_ctrl *ctrl, int argc, - char *argv[]) +static int wpa_cli_cmd_wfd_subelem_get(struct wpa_ctrl *ctrl, int argc, + char *argv[]) { char cmd[100]; int res; - if (argc != 0 && argc != 2) { - printf("Invalid P2P_EXT_LISTEN command: needs two arguments " - "(availability period, availability interval; in " - "millisecods).\n" - "Extended Listen Timing can be cancelled with this " - "command when used without parameters.\n"); + if (argc != 1) { + printf("Invalid WFD_SUBELEM_GET command: needs one " + "argument (subelem)\n"); return -1; } - if (argc == 2) - res = os_snprintf(cmd, sizeof(cmd), "P2P_EXT_LISTEN %s %s", - argv[0], argv[1]); - else - res = os_snprintf(cmd, sizeof(cmd), "P2P_EXT_LISTEN"); + res = os_snprintf(cmd, sizeof(cmd), "WFD_SUBELEM_GET %s", + argv[0]); if (res < 0 || (size_t) res >= sizeof(cmd)) return -1; cmd[sizeof(cmd) - 1] = '\0'; return wpa_ctrl_command(ctrl, cmd); } - -#endif /* CONFIG_P2P */ +#endif /* CONFIG_WIFI_DISPLAY */ #ifdef CONFIG_INTERWORKING @@ -2524,149 +2265,171 @@ static int wpa_cli_cmd_interworking_select(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[100]; - int res; - - if (argc == 0) - return wpa_ctrl_command(ctrl, "INTERWORKING_SELECT"); - - res = os_snprintf(cmd, sizeof(cmd), "INTERWORKING_SELECT %s", argv[0]); - if (res < 0 || (size_t) res >= sizeof(cmd)) - return -1; - cmd[sizeof(cmd) - 1] = '\0'; - return wpa_ctrl_command(ctrl, cmd); + return wpa_cli_cmd(ctrl, "INTERWORKING_SELECT", 0, argc, argv); } static int wpa_cli_cmd_interworking_connect(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[100]; - int res; + return wpa_cli_cmd(ctrl, "INTERWORKING_CONNECT", 1, argc, argv); +} - if (argc != 1) { - printf("Invalid INTERWORKING_CONNECT commands: needs one " - "argument (BSSID)\n"); + +static int wpa_cli_cmd_anqp_get(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + return wpa_cli_cmd(ctrl, "ANQP_GET", 2, argc, argv); +} + + +static int wpa_cli_cmd_gas_request(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "GAS_REQUEST", 2, argc, argv); +} + + +static int wpa_cli_cmd_gas_response_get(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "GAS_RESPONSE_GET", 2, argc, argv); +} +#endif /* CONFIG_INTERWORKING */ + + +#ifdef CONFIG_HS20 + +static int wpa_cli_cmd_hs20_anqp_get(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "HS20_ANQP_GET", 2, argc, argv); +} + + +static int wpa_cli_cmd_get_nai_home_realm_list(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char cmd[512]; + + if (argc == 0) { + printf("Command needs one or two arguments (dst mac addr and " + "optional home realm)\n"); return -1; } - res = os_snprintf(cmd, sizeof(cmd), "INTERWORKING_CONNECT %s", - argv[0]); - if (res < 0 || (size_t) res >= sizeof(cmd)) + if (write_cmd(cmd, sizeof(cmd), "HS20_GET_NAI_HOME_REALM_LIST", + argc, argv) < 0) return -1; - cmd[sizeof(cmd) - 1] = '\0'; + return wpa_ctrl_command(ctrl, cmd); } +#endif /* CONFIG_HS20 */ + + +static int wpa_cli_cmd_sta_autoconnect(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "STA_AUTOCONNECT", 1, argc, argv); +} + + +static int wpa_cli_cmd_tdls_discover(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "TDLS_DISCOVER", 1, argc, argv); +} + + +static int wpa_cli_cmd_tdls_setup(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "TDLS_SETUP", 1, argc, argv); +} + + +static int wpa_cli_cmd_tdls_teardown(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "TDLS_TEARDOWN", 1, argc, argv); +} + + +static int wpa_cli_cmd_signal_poll(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "SIGNAL_POLL"); +} + -static int wpa_cli_cmd_anqp_get(struct wpa_ctrl *ctrl, int argc, char *argv[]) +static int wpa_cli_cmd_pktcnt_poll(struct wpa_ctrl *ctrl, int argc, + char *argv[]) { - char cmd[100]; - int res; + return wpa_ctrl_command(ctrl, "PKTCNT_POLL"); +} - if (argc != 2) { - printf("Invalid ANQP_GET command: needs two arguments " - "(addr and info id list)\n"); - return -1; - } - res = os_snprintf(cmd, sizeof(cmd), "ANQP_GET %s %s", - argv[0], argv[1]); - if (res < 0 || (size_t) res >= sizeof(cmd)) - return -1; - cmd[sizeof(cmd) - 1] = '\0'; - return wpa_ctrl_command(ctrl, cmd); +static int wpa_cli_cmd_reauthenticate(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "REAUTHENTICATE"); } -#endif /* CONFIG_INTERWORKING */ -static int wpa_cli_cmd_sta_autoconnect(struct wpa_ctrl *ctrl, int argc, - char *argv[]) +#ifdef CONFIG_AUTOSCAN + +static int wpa_cli_cmd_autoscan(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[256]; - int res; + if (argc == 0) + return wpa_ctrl_command(ctrl, "AUTOSCAN "); - if (argc != 1) { - printf("Invalid STA_AUTOCONNECT command: needs one argument " - "(0/1 = disable/enable automatic reconnection)\n"); - return -1; - } - res = os_snprintf(cmd, sizeof(cmd), "STA_AUTOCONNECT %s", argv[0]); - if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { - printf("Too long STA_AUTOCONNECT command.\n"); - return -1; - } - return wpa_ctrl_command(ctrl, cmd); + return wpa_cli_cmd(ctrl, "AUTOSCAN", 0, argc, argv); } +#endif /* CONFIG_AUTOSCAN */ -static int wpa_cli_cmd_tdls_discover(struct wpa_ctrl *ctrl, int argc, - char *argv[]) -{ - char cmd[256]; - int res; - if (argc != 1) { - printf("Invalid TDLS_DISCOVER command: needs one argument " - "(Peer STA MAC address)\n"); - return -1; - } +#ifdef CONFIG_WNM - res = os_snprintf(cmd, sizeof(cmd), "TDLS_DISCOVER %s", argv[0]); - if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { - printf("Too long TDLS_DISCOVER command.\n"); - return -1; - } - return wpa_ctrl_command(ctrl, cmd); +static int wpa_cli_cmd_wnm_sleep(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + return wpa_cli_cmd(ctrl, "WNM_SLEEP", 0, argc, argv); } -static int wpa_cli_cmd_tdls_setup(struct wpa_ctrl *ctrl, int argc, - char *argv[]) +static int wpa_cli_cmd_wnm_bss_query(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[256]; - int res; + return wpa_cli_cmd(ctrl, "WNM_BSS_QUERY", 1, argc, argv); +} - if (argc != 1) { - printf("Invalid TDLS_SETUP command: needs one argument " - "(Peer STA MAC address)\n"); - return -1; - } +#endif /* CONFIG_WNM */ - res = os_snprintf(cmd, sizeof(cmd), "TDLS_SETUP %s", argv[0]); - if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { - printf("Too long TDLS_SETUP command.\n"); + +static int wpa_cli_cmd_raw(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + if (argc == 0) return -1; - } - return wpa_ctrl_command(ctrl, cmd); + return wpa_cli_cmd(ctrl, argv[0], 0, argc - 1, &argv[1]); } -static int wpa_cli_cmd_tdls_teardown(struct wpa_ctrl *ctrl, int argc, - char *argv[]) +#ifdef ANDROID +static int wpa_cli_cmd_driver(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[256]; - int res; + return wpa_cli_cmd(ctrl, "DRIVER", 1, argc, argv); +} +#endif /* ANDROID */ - if (argc != 1) { - printf("Invalid TDLS_TEARDOWN command: needs one argument " - "(Peer STA MAC address)\n"); - return -1; - } - res = os_snprintf(cmd, sizeof(cmd), "TDLS_TEARDOWN %s", argv[0]); - if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { - printf("Too long TDLS_TEARDOWN command.\n"); - return -1; - } - return wpa_ctrl_command(ctrl, cmd); +static int wpa_cli_cmd_flush(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + return wpa_ctrl_command(ctrl, "FLUSH"); } -static int wpa_cli_cmd_signal_poll(struct wpa_ctrl *ctrl, int argc, - char *argv[]) +static int wpa_cli_cmd_radio_work(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - return wpa_ctrl_command(ctrl, "SIGNAL_POLL"); + return wpa_cli_cmd(ctrl, "RADIO_WORK", 1, argc, argv); } @@ -2678,341 +2441,468 @@ struct wpa_cli_cmd { const char *cmd; int (*handler)(struct wpa_ctrl *ctrl, int argc, char *argv[]); + char ** (*completion)(const char *str, int pos); enum wpa_cli_cmd_flags flags; const char *usage; }; static struct wpa_cli_cmd wpa_cli_commands[] = { - { "status", wpa_cli_cmd_status, + { "status", wpa_cli_cmd_status, NULL, cli_cmd_flag_none, "[verbose] = get current WPA/EAPOL/EAP status" }, - { "ping", wpa_cli_cmd_ping, + { "ifname", wpa_cli_cmd_ifname, NULL, + cli_cmd_flag_none, + "= get current interface name" }, + { "ping", wpa_cli_cmd_ping, NULL, cli_cmd_flag_none, "= pings wpa_supplicant" }, - { "relog", wpa_cli_cmd_relog, + { "relog", wpa_cli_cmd_relog, NULL, cli_cmd_flag_none, "= re-open log-file (allow rolling logs)" }, - { "note", wpa_cli_cmd_note, + { "note", wpa_cli_cmd_note, NULL, cli_cmd_flag_none, " = add a note to wpa_supplicant debug log" }, - { "mib", wpa_cli_cmd_mib, + { "mib", wpa_cli_cmd_mib, NULL, cli_cmd_flag_none, "= get MIB variables (dot1x, dot11)" }, - { "help", wpa_cli_cmd_help, + { "help", wpa_cli_cmd_help, wpa_cli_complete_help, cli_cmd_flag_none, - "= show this usage help" }, - { "interface", wpa_cli_cmd_interface, + "[command] = show usage help" }, + { "interface", wpa_cli_cmd_interface, NULL, cli_cmd_flag_none, "[ifname] = show interfaces/select interface" }, - { "level", wpa_cli_cmd_level, + { "level", wpa_cli_cmd_level, NULL, cli_cmd_flag_none, " = change debug level" }, - { "license", wpa_cli_cmd_license, + { "license", wpa_cli_cmd_license, NULL, cli_cmd_flag_none, "= show full wpa_cli license" }, - { "quit", wpa_cli_cmd_quit, + { "quit", wpa_cli_cmd_quit, NULL, cli_cmd_flag_none, "= exit wpa_cli" }, - { "set", wpa_cli_cmd_set, + { "set", wpa_cli_cmd_set, wpa_cli_complete_set, cli_cmd_flag_none, "= set variables (shows list of variables when run without " "arguments)" }, - { "get", wpa_cli_cmd_get, + { "get", wpa_cli_cmd_get, NULL, cli_cmd_flag_none, " = get information" }, - { "logon", wpa_cli_cmd_logon, + { "logon", wpa_cli_cmd_logon, NULL, cli_cmd_flag_none, "= IEEE 802.1X EAPOL state machine logon" }, - { "logoff", wpa_cli_cmd_logoff, + { "logoff", wpa_cli_cmd_logoff, NULL, cli_cmd_flag_none, "= IEEE 802.1X EAPOL state machine logoff" }, - { "pmksa", wpa_cli_cmd_pmksa, + { "pmksa", wpa_cli_cmd_pmksa, NULL, cli_cmd_flag_none, "= show PMKSA cache" }, - { "reassociate", wpa_cli_cmd_reassociate, + { "reassociate", wpa_cli_cmd_reassociate, NULL, cli_cmd_flag_none, "= force reassociation" }, - { "preauthenticate", wpa_cli_cmd_preauthenticate, + { "preauthenticate", wpa_cli_cmd_preauthenticate, wpa_cli_complete_bss, cli_cmd_flag_none, " = force preauthentication" }, - { "identity", wpa_cli_cmd_identity, + { "identity", wpa_cli_cmd_identity, NULL, cli_cmd_flag_none, " = configure identity for an SSID" }, - { "password", wpa_cli_cmd_password, + { "password", wpa_cli_cmd_password, NULL, cli_cmd_flag_sensitive, " = configure password for an SSID" }, - { "new_password", wpa_cli_cmd_new_password, + { "new_password", wpa_cli_cmd_new_password, NULL, cli_cmd_flag_sensitive, " = change password for an SSID" }, - { "pin", wpa_cli_cmd_pin, + { "pin", wpa_cli_cmd_pin, NULL, cli_cmd_flag_sensitive, " = configure pin for an SSID" }, - { "otp", wpa_cli_cmd_otp, + { "otp", wpa_cli_cmd_otp, NULL, cli_cmd_flag_sensitive, " = configure one-time-password for an SSID" }, - { "passphrase", wpa_cli_cmd_passphrase, + { "passphrase", wpa_cli_cmd_passphrase, NULL, cli_cmd_flag_sensitive, " = configure private key passphrase\n" " for an SSID" }, - { "bssid", wpa_cli_cmd_bssid, + { "sim", wpa_cli_cmd_sim, NULL, + cli_cmd_flag_sensitive, + " = report SIM operation result" }, + { "bssid", wpa_cli_cmd_bssid, NULL, cli_cmd_flag_none, " = set preferred BSSID for an SSID" }, - { "blacklist", wpa_cli_cmd_blacklist, + { "blacklist", wpa_cli_cmd_blacklist, wpa_cli_complete_bss, cli_cmd_flag_none, " = add a BSSID to the blacklist\n" "blacklist clear = clear the blacklist\n" "blacklist = display the blacklist" }, - { "log_level", wpa_cli_cmd_log_level, + { "log_level", wpa_cli_cmd_log_level, NULL, cli_cmd_flag_none, " [] = update the log level/timestamp\n" "log_level = display the current log level and log options" }, - { "list_networks", wpa_cli_cmd_list_networks, + { "list_networks", wpa_cli_cmd_list_networks, NULL, cli_cmd_flag_none, "= list configured networks" }, - { "select_network", wpa_cli_cmd_select_network, + { "select_network", wpa_cli_cmd_select_network, NULL, cli_cmd_flag_none, " = select a network (disable others)" }, - { "enable_network", wpa_cli_cmd_enable_network, + { "enable_network", wpa_cli_cmd_enable_network, NULL, cli_cmd_flag_none, " = enable a network" }, - { "disable_network", wpa_cli_cmd_disable_network, + { "disable_network", wpa_cli_cmd_disable_network, NULL, cli_cmd_flag_none, " = disable a network" }, - { "add_network", wpa_cli_cmd_add_network, + { "add_network", wpa_cli_cmd_add_network, NULL, cli_cmd_flag_none, "= add a network" }, - { "remove_network", wpa_cli_cmd_remove_network, + { "remove_network", wpa_cli_cmd_remove_network, NULL, cli_cmd_flag_none, " = remove a network" }, - { "set_network", wpa_cli_cmd_set_network, + { "set_network", wpa_cli_cmd_set_network, NULL, cli_cmd_flag_sensitive, " = set network variables (shows\n" " list of variables when run without arguments)" }, - { "get_network", wpa_cli_cmd_get_network, + { "get_network", wpa_cli_cmd_get_network, NULL, cli_cmd_flag_none, " = get network variables" }, - { "save_config", wpa_cli_cmd_save_config, + { "list_creds", wpa_cli_cmd_list_creds, NULL, + cli_cmd_flag_none, + "= list configured credentials" }, + { "add_cred", wpa_cli_cmd_add_cred, NULL, + cli_cmd_flag_none, + "= add a credential" }, + { "remove_cred", wpa_cli_cmd_remove_cred, NULL, + cli_cmd_flag_none, + " = remove a credential" }, + { "set_cred", wpa_cli_cmd_set_cred, NULL, + cli_cmd_flag_sensitive, + " = set credential variables" }, + { "save_config", wpa_cli_cmd_save_config, NULL, cli_cmd_flag_none, "= save the current configuration" }, - { "disconnect", wpa_cli_cmd_disconnect, + { "disconnect", wpa_cli_cmd_disconnect, NULL, cli_cmd_flag_none, "= disconnect and wait for reassociate/reconnect command before\n" " connecting" }, - { "reconnect", wpa_cli_cmd_reconnect, + { "reconnect", wpa_cli_cmd_reconnect, NULL, cli_cmd_flag_none, "= like reassociate, but only takes effect if already disconnected" }, - { "scan", wpa_cli_cmd_scan, + { "scan", wpa_cli_cmd_scan, NULL, cli_cmd_flag_none, "= request new BSS scan" }, - { "scan_results", wpa_cli_cmd_scan_results, + { "scan_results", wpa_cli_cmd_scan_results, NULL, cli_cmd_flag_none, "= get latest scan results" }, - { "bss", wpa_cli_cmd_bss, + { "bss", wpa_cli_cmd_bss, wpa_cli_complete_bss, cli_cmd_flag_none, "< | > = get detailed scan result info" }, - { "get_capability", wpa_cli_cmd_get_capability, + { "get_capability", wpa_cli_cmd_get_capability, NULL, cli_cmd_flag_none, - " = get capabilies" }, - { "reconfigure", wpa_cli_cmd_reconfigure, + " " + "= get capabilies" }, + { "reconfigure", wpa_cli_cmd_reconfigure, NULL, cli_cmd_flag_none, "= force wpa_supplicant to re-read its configuration file" }, - { "terminate", wpa_cli_cmd_terminate, + { "terminate", wpa_cli_cmd_terminate, NULL, cli_cmd_flag_none, "= terminate wpa_supplicant" }, - { "interface_add", wpa_cli_cmd_interface_add, + { "interface_add", wpa_cli_cmd_interface_add, NULL, cli_cmd_flag_none, " \n" " = adds new interface, all parameters but \n" " are optional" }, - { "interface_remove", wpa_cli_cmd_interface_remove, + { "interface_remove", wpa_cli_cmd_interface_remove, NULL, cli_cmd_flag_none, " = removes the interface" }, - { "interface_list", wpa_cli_cmd_interface_list, + { "interface_list", wpa_cli_cmd_interface_list, NULL, cli_cmd_flag_none, "= list available interfaces" }, - { "ap_scan", wpa_cli_cmd_ap_scan, + { "ap_scan", wpa_cli_cmd_ap_scan, NULL, cli_cmd_flag_none, " = set ap_scan parameter" }, - { "scan_interval", wpa_cli_cmd_scan_interval, + { "scan_interval", wpa_cli_cmd_scan_interval, NULL, cli_cmd_flag_none, " = set scan_interval parameter (in seconds)" }, - { "bss_expire_age", wpa_cli_cmd_bss_expire_age, + { "bss_expire_age", wpa_cli_cmd_bss_expire_age, NULL, cli_cmd_flag_none, " = set BSS expiration age parameter" }, - { "bss_expire_count", wpa_cli_cmd_bss_expire_count, + { "bss_expire_count", wpa_cli_cmd_bss_expire_count, NULL, cli_cmd_flag_none, " = set BSS expiration scan count parameter" }, - { "stkstart", wpa_cli_cmd_stkstart, + { "bss_flush", wpa_cli_cmd_bss_flush, NULL, + cli_cmd_flag_none, + " = set BSS flush age (0 by default)" }, + { "stkstart", wpa_cli_cmd_stkstart, NULL, cli_cmd_flag_none, " = request STK negotiation with " }, - { "ft_ds", wpa_cli_cmd_ft_ds, + { "ft_ds", wpa_cli_cmd_ft_ds, wpa_cli_complete_bss, cli_cmd_flag_none, " = request over-the-DS FT with " }, - { "wps_pbc", wpa_cli_cmd_wps_pbc, + { "wps_pbc", wpa_cli_cmd_wps_pbc, wpa_cli_complete_bss, cli_cmd_flag_none, "[BSSID] = start Wi-Fi Protected Setup: Push Button Configuration" }, - { "wps_pin", wpa_cli_cmd_wps_pin, + { "wps_pin", wpa_cli_cmd_wps_pin, wpa_cli_complete_bss, cli_cmd_flag_sensitive, " [PIN] = start WPS PIN method (returns PIN, if not " "hardcoded)" }, - { "wps_check_pin", wpa_cli_cmd_wps_check_pin, + { "wps_check_pin", wpa_cli_cmd_wps_check_pin, NULL, cli_cmd_flag_sensitive, " = verify PIN checksum" }, - { "wps_cancel", wpa_cli_cmd_wps_cancel, cli_cmd_flag_none, + { "wps_cancel", wpa_cli_cmd_wps_cancel, NULL, cli_cmd_flag_none, "Cancels the pending WPS operation" }, -#ifdef CONFIG_WPS_OOB - { "wps_oob", wpa_cli_cmd_wps_oob, +#ifdef CONFIG_WPS_NFC + { "wps_nfc", wpa_cli_cmd_wps_nfc, wpa_cli_complete_bss, + cli_cmd_flag_none, + "[BSSID] = start Wi-Fi Protected Setup: NFC" }, + { "wps_nfc_config_token", wpa_cli_cmd_wps_nfc_config_token, NULL, + cli_cmd_flag_none, + " = build configuration token" }, + { "wps_nfc_token", wpa_cli_cmd_wps_nfc_token, NULL, + cli_cmd_flag_none, + " = create password token" }, + { "wps_nfc_tag_read", wpa_cli_cmd_wps_nfc_tag_read, NULL, cli_cmd_flag_sensitive, - " [DEV_NAME] = start WPS OOB" }, -#endif /* CONFIG_WPS_OOB */ - { "wps_reg", wpa_cli_cmd_wps_reg, + " = report read NFC tag with WPS data" }, + { "nfc_get_handover_req", wpa_cli_cmd_nfc_get_handover_req, NULL, + cli_cmd_flag_none, + " = create NFC handover request" }, + { "nfc_get_handover_sel", wpa_cli_cmd_nfc_get_handover_sel, NULL, + cli_cmd_flag_none, + " = create NFC handover select" }, + { "nfc_rx_handover_req", wpa_cli_cmd_nfc_rx_handover_req, NULL, + cli_cmd_flag_none, + " = report received NFC handover request" }, + { "nfc_rx_handover_sel", wpa_cli_cmd_nfc_rx_handover_sel, NULL, + cli_cmd_flag_none, + " = report received NFC handover select" }, + { "nfc_report_handover", wpa_cli_cmd_nfc_report_handover, NULL, + cli_cmd_flag_none, + " = report completed " + "NFC handover" }, +#endif /* CONFIG_WPS_NFC */ + { "wps_reg", wpa_cli_cmd_wps_reg, wpa_cli_complete_bss, cli_cmd_flag_sensitive, " = start WPS Registrar to configure an AP" }, - { "wps_ap_pin", wpa_cli_cmd_wps_ap_pin, + { "wps_ap_pin", wpa_cli_cmd_wps_ap_pin, NULL, cli_cmd_flag_sensitive, "[params..] = enable/disable AP PIN" }, - { "wps_er_start", wpa_cli_cmd_wps_er_start, + { "wps_er_start", wpa_cli_cmd_wps_er_start, NULL, cli_cmd_flag_none, "[IP address] = start Wi-Fi Protected Setup External Registrar" }, - { "wps_er_stop", wpa_cli_cmd_wps_er_stop, + { "wps_er_stop", wpa_cli_cmd_wps_er_stop, NULL, cli_cmd_flag_none, "= stop Wi-Fi Protected Setup External Registrar" }, - { "wps_er_pin", wpa_cli_cmd_wps_er_pin, + { "wps_er_pin", wpa_cli_cmd_wps_er_pin, NULL, cli_cmd_flag_sensitive, " = add an Enrollee PIN to External Registrar" }, - { "wps_er_pbc", wpa_cli_cmd_wps_er_pbc, + { "wps_er_pbc", wpa_cli_cmd_wps_er_pbc, NULL, cli_cmd_flag_none, " = accept an Enrollee PBC using External Registrar" }, - { "wps_er_learn", wpa_cli_cmd_wps_er_learn, + { "wps_er_learn", wpa_cli_cmd_wps_er_learn, NULL, cli_cmd_flag_sensitive, " = learn AP configuration" }, - { "wps_er_set_config", wpa_cli_cmd_wps_er_set_config, + { "wps_er_set_config", wpa_cli_cmd_wps_er_set_config, NULL, cli_cmd_flag_none, " = set AP configuration for enrolling" }, - { "wps_er_config", wpa_cli_cmd_wps_er_config, + { "wps_er_config", wpa_cli_cmd_wps_er_config, NULL, cli_cmd_flag_sensitive, " = configure AP" }, - { "ibss_rsn", wpa_cli_cmd_ibss_rsn, +#ifdef CONFIG_WPS_NFC + { "wps_er_nfc_config_token", wpa_cli_cmd_wps_er_nfc_config_token, NULL, + cli_cmd_flag_none, + " = build NFC configuration token" }, +#endif /* CONFIG_WPS_NFC */ + { "ibss_rsn", wpa_cli_cmd_ibss_rsn, NULL, cli_cmd_flag_none, " = request RSN authentication with in IBSS" }, #ifdef CONFIG_AP - { "sta", wpa_cli_cmd_sta, + { "sta", wpa_cli_cmd_sta, NULL, cli_cmd_flag_none, " = get information about an associated station (AP)" }, - { "all_sta", wpa_cli_cmd_all_sta, + { "all_sta", wpa_cli_cmd_all_sta, NULL, cli_cmd_flag_none, "= get information about all associated stations (AP)" }, + { "deauthenticate", wpa_cli_cmd_deauthenticate, NULL, + cli_cmd_flag_none, + " = deauthenticate a station" }, + { "disassociate", wpa_cli_cmd_disassociate, NULL, + cli_cmd_flag_none, + " = disassociate a station" }, + { "chan_switch", wpa_cli_cmd_chanswitch, NULL, + cli_cmd_flag_none, + " [sec_channel_offset=] [center_freq1=]" + " [center_freq2=] [bandwidth=] [blocktx] [ht|vht]" + " = CSA parameters" }, #endif /* CONFIG_AP */ - { "suspend", wpa_cli_cmd_suspend, cli_cmd_flag_none, + { "suspend", wpa_cli_cmd_suspend, NULL, cli_cmd_flag_none, "= notification of suspend/hibernate" }, - { "resume", wpa_cli_cmd_resume, cli_cmd_flag_none, + { "resume", wpa_cli_cmd_resume, NULL, cli_cmd_flag_none, "= notification of resume/thaw" }, - { "drop_sa", wpa_cli_cmd_drop_sa, cli_cmd_flag_none, + { "drop_sa", wpa_cli_cmd_drop_sa, NULL, cli_cmd_flag_none, "= drop SA without deauth/disassoc (test command)" }, - { "roam", wpa_cli_cmd_roam, + { "roam", wpa_cli_cmd_roam, wpa_cli_complete_bss, cli_cmd_flag_none, " = roam to the specified BSS" }, #ifdef CONFIG_P2P - { "p2p_find", wpa_cli_cmd_p2p_find, cli_cmd_flag_none, + { "p2p_find", wpa_cli_cmd_p2p_find, wpa_cli_complete_p2p_find, + cli_cmd_flag_none, "[timeout] [type=*] = find P2P Devices for up-to timeout seconds" }, - { "p2p_stop_find", wpa_cli_cmd_p2p_stop_find, cli_cmd_flag_none, + { "p2p_stop_find", wpa_cli_cmd_p2p_stop_find, NULL, cli_cmd_flag_none, "= stop P2P Devices search" }, - { "p2p_connect", wpa_cli_cmd_p2p_connect, cli_cmd_flag_none, - " <\"pbc\"|PIN> = connect to a P2P Devices" }, - { "p2p_listen", wpa_cli_cmd_p2p_listen, cli_cmd_flag_none, + { "p2p_connect", wpa_cli_cmd_p2p_connect, wpa_cli_complete_p2p_connect, + cli_cmd_flag_none, + " <\"pbc\"|PIN> [ht40] = connect to a P2P Device" }, + { "p2p_listen", wpa_cli_cmd_p2p_listen, NULL, cli_cmd_flag_none, "[timeout] = listen for P2P Devices for up-to timeout seconds" }, - { "p2p_group_remove", wpa_cli_cmd_p2p_group_remove, cli_cmd_flag_none, + { "p2p_group_remove", wpa_cli_cmd_p2p_group_remove, + wpa_cli_complete_p2p_group_remove, cli_cmd_flag_none, " = remove P2P group interface (terminate group if GO)" }, - { "p2p_group_add", wpa_cli_cmd_p2p_group_add, cli_cmd_flag_none, - "= add a new P2P group (local end as GO)" }, - { "p2p_prov_disc", wpa_cli_cmd_p2p_prov_disc, cli_cmd_flag_none, + { "p2p_group_add", wpa_cli_cmd_p2p_group_add, NULL, cli_cmd_flag_none, + "[ht40] = add a new P2P group (local end as GO)" }, + { "p2p_prov_disc", wpa_cli_cmd_p2p_prov_disc, + wpa_cli_complete_p2p_peer, cli_cmd_flag_none, " = request provisioning discovery" }, - { "p2p_get_passphrase", wpa_cli_cmd_p2p_get_passphrase, + { "p2p_get_passphrase", wpa_cli_cmd_p2p_get_passphrase, NULL, cli_cmd_flag_none, "= get the passphrase for a group (GO only)" }, { "p2p_serv_disc_req", wpa_cli_cmd_p2p_serv_disc_req, - cli_cmd_flag_none, + wpa_cli_complete_p2p_peer, cli_cmd_flag_none, " = schedule service discovery request" }, { "p2p_serv_disc_cancel_req", wpa_cli_cmd_p2p_serv_disc_cancel_req, - cli_cmd_flag_none, + NULL, cli_cmd_flag_none, " = cancel pending service discovery request" }, - { "p2p_serv_disc_resp", wpa_cli_cmd_p2p_serv_disc_resp, + { "p2p_serv_disc_resp", wpa_cli_cmd_p2p_serv_disc_resp, NULL, cli_cmd_flag_none, " = service discovery response" }, - { "p2p_service_update", wpa_cli_cmd_p2p_service_update, + { "p2p_service_update", wpa_cli_cmd_p2p_service_update, NULL, cli_cmd_flag_none, "= indicate change in local services" }, - { "p2p_serv_disc_external", wpa_cli_cmd_p2p_serv_disc_external, + { "p2p_serv_disc_external", wpa_cli_cmd_p2p_serv_disc_external, NULL, cli_cmd_flag_none, " = set external processing of service discovery" }, - { "p2p_service_flush", wpa_cli_cmd_p2p_service_flush, + { "p2p_service_flush", wpa_cli_cmd_p2p_service_flush, NULL, cli_cmd_flag_none, "= remove all stored service entries" }, - { "p2p_service_add", wpa_cli_cmd_p2p_service_add, + { "p2p_service_add", wpa_cli_cmd_p2p_service_add, NULL, cli_cmd_flag_none, " = add a local " "service" }, - { "p2p_service_del", wpa_cli_cmd_p2p_service_del, + { "p2p_service_del", wpa_cli_cmd_p2p_service_del, NULL, cli_cmd_flag_none, " [|service] = remove a local " "service" }, - { "p2p_reject", wpa_cli_cmd_p2p_reject, + { "p2p_reject", wpa_cli_cmd_p2p_reject, wpa_cli_complete_p2p_peer, cli_cmd_flag_none, " = reject connection attempts from a specific peer" }, - { "p2p_invite", wpa_cli_cmd_p2p_invite, + { "p2p_invite", wpa_cli_cmd_p2p_invite, NULL, cli_cmd_flag_none, " [peer=addr] = invite peer" }, - { "p2p_peers", wpa_cli_cmd_p2p_peers, cli_cmd_flag_none, + { "p2p_peers", wpa_cli_cmd_p2p_peers, NULL, cli_cmd_flag_none, "[discovered] = list known (optionally, only fully discovered) P2P " "peers" }, - { "p2p_peer", wpa_cli_cmd_p2p_peer, cli_cmd_flag_none, + { "p2p_peer", wpa_cli_cmd_p2p_peer, wpa_cli_complete_p2p_peer, + cli_cmd_flag_none, "
          = show information about known P2P peer" }, - { "p2p_set", wpa_cli_cmd_p2p_set, cli_cmd_flag_none, + { "p2p_set", wpa_cli_cmd_p2p_set, wpa_cli_complete_p2p_set, + cli_cmd_flag_none, " = set a P2P parameter" }, - { "p2p_flush", wpa_cli_cmd_p2p_flush, cli_cmd_flag_none, + { "p2p_flush", wpa_cli_cmd_p2p_flush, NULL, cli_cmd_flag_none, "= flush P2P state" }, - { "p2p_cancel", wpa_cli_cmd_p2p_cancel, cli_cmd_flag_none, + { "p2p_cancel", wpa_cli_cmd_p2p_cancel, NULL, cli_cmd_flag_none, "= cancel P2P group formation" }, - { "p2p_unauthorize", wpa_cli_cmd_p2p_unauthorize, cli_cmd_flag_none, + { "p2p_unauthorize", wpa_cli_cmd_p2p_unauthorize, + wpa_cli_complete_p2p_peer, cli_cmd_flag_none, "
          = unauthorize a peer" }, - { "p2p_presence_req", wpa_cli_cmd_p2p_presence_req, cli_cmd_flag_none, + { "p2p_presence_req", wpa_cli_cmd_p2p_presence_req, NULL, + cli_cmd_flag_none, "[ ] [ ] = request GO " "presence" }, - { "p2p_ext_listen", wpa_cli_cmd_p2p_ext_listen, cli_cmd_flag_none, + { "p2p_ext_listen", wpa_cli_cmd_p2p_ext_listen, NULL, + cli_cmd_flag_none, "[ ] = set extended listen timing" }, + { "p2p_remove_client", wpa_cli_cmd_p2p_remove_client, + wpa_cli_complete_p2p_peer, cli_cmd_flag_none, + " = remove a peer from all groups" }, #endif /* CONFIG_P2P */ - +#ifdef CONFIG_WIFI_DISPLAY + { "wfd_subelem_set", wpa_cli_cmd_wfd_subelem_set, NULL, + cli_cmd_flag_none, + " [contents] = set Wi-Fi Display subelement" }, + { "wfd_subelem_get", wpa_cli_cmd_wfd_subelem_get, NULL, + cli_cmd_flag_none, + " = get Wi-Fi Display subelement" }, +#endif /* CONFIG_WIFI_DISPLAY */ #ifdef CONFIG_INTERWORKING - { "fetch_anqp", wpa_cli_cmd_fetch_anqp, cli_cmd_flag_none, + { "fetch_anqp", wpa_cli_cmd_fetch_anqp, NULL, cli_cmd_flag_none, "= fetch ANQP information for all APs" }, - { "stop_fetch_anqp", wpa_cli_cmd_stop_fetch_anqp, cli_cmd_flag_none, + { "stop_fetch_anqp", wpa_cli_cmd_stop_fetch_anqp, NULL, + cli_cmd_flag_none, "= stop fetch_anqp operation" }, - { "interworking_select", wpa_cli_cmd_interworking_select, + { "interworking_select", wpa_cli_cmd_interworking_select, NULL, cli_cmd_flag_none, "[auto] = perform Interworking network selection" }, { "interworking_connect", wpa_cli_cmd_interworking_connect, - cli_cmd_flag_none, + wpa_cli_complete_bss, cli_cmd_flag_none, " = connect using Interworking credentials" }, - { "anqp_get", wpa_cli_cmd_anqp_get, cli_cmd_flag_none, + { "anqp_get", wpa_cli_cmd_anqp_get, wpa_cli_complete_bss, + cli_cmd_flag_none, " [,]... = request ANQP information" }, + { "gas_request", wpa_cli_cmd_gas_request, wpa_cli_complete_bss, + cli_cmd_flag_none, + " [QueryReq] = GAS request" }, + { "gas_response_get", wpa_cli_cmd_gas_response_get, + wpa_cli_complete_bss, cli_cmd_flag_none, + " [start,len] = Fetch last GAS response" }, #endif /* CONFIG_INTERWORKING */ - { "sta_autoconnect", wpa_cli_cmd_sta_autoconnect, cli_cmd_flag_none, +#ifdef CONFIG_HS20 + { "hs20_anqp_get", wpa_cli_cmd_hs20_anqp_get, wpa_cli_complete_bss, + cli_cmd_flag_none, + " [,]... = request HS 2.0 ANQP information" + }, + { "nai_home_realm_list", wpa_cli_cmd_get_nai_home_realm_list, + wpa_cli_complete_bss, cli_cmd_flag_none, + " = get HS20 nai home realm list" }, +#endif /* CONFIG_HS20 */ + { "sta_autoconnect", wpa_cli_cmd_sta_autoconnect, NULL, + cli_cmd_flag_none, "<0/1> = disable/enable automatic reconnection" }, - { "tdls_discover", wpa_cli_cmd_tdls_discover, + { "tdls_discover", wpa_cli_cmd_tdls_discover, NULL, cli_cmd_flag_none, " = request TDLS discovery with " }, - { "tdls_setup", wpa_cli_cmd_tdls_setup, + { "tdls_setup", wpa_cli_cmd_tdls_setup, NULL, cli_cmd_flag_none, " = request TDLS setup with " }, - { "tdls_teardown", wpa_cli_cmd_tdls_teardown, + { "tdls_teardown", wpa_cli_cmd_tdls_teardown, NULL, cli_cmd_flag_none, " = tear down TDLS with " }, - { "signal_poll", wpa_cli_cmd_signal_poll, + { "signal_poll", wpa_cli_cmd_signal_poll, NULL, cli_cmd_flag_none, "= get signal parameters" }, - { NULL, NULL, cli_cmd_flag_none, NULL } + { "pktcnt_poll", wpa_cli_cmd_pktcnt_poll, NULL, + cli_cmd_flag_none, + "= get TX/RX packet counters" }, + { "reauthenticate", wpa_cli_cmd_reauthenticate, NULL, + cli_cmd_flag_none, + "= trigger IEEE 802.1X/EAPOL reauthentication" }, +#ifdef CONFIG_AUTOSCAN + { "autoscan", wpa_cli_cmd_autoscan, NULL, cli_cmd_flag_none, + "[params] = Set or unset (if none) autoscan parameters" }, +#endif /* CONFIG_AUTOSCAN */ +#ifdef CONFIG_WNM + { "wnm_sleep", wpa_cli_cmd_wnm_sleep, NULL, cli_cmd_flag_none, + " [interval=#] = enter/exit WNM-Sleep mode" }, + { "wnm_bss_query", wpa_cli_cmd_wnm_bss_query, NULL, cli_cmd_flag_none, + " = Send BSS Transition Management Query" }, +#endif /* CONFIG_WNM */ + { "raw", wpa_cli_cmd_raw, NULL, cli_cmd_flag_sensitive, + " = Sent unprocessed command" }, + { "flush", wpa_cli_cmd_flush, NULL, cli_cmd_flag_none, + "= flush wpa_supplicant state" }, +#ifdef ANDROID + { "driver", wpa_cli_cmd_driver, NULL, cli_cmd_flag_none, + " = driver private commands" }, +#endif /* ANDROID */ + { "radio_work", wpa_cli_cmd_radio_work, NULL, cli_cmd_flag_none, + "= radio_work " }, + { NULL, NULL, NULL, cli_cmd_flag_none, NULL } }; @@ -3034,12 +2924,14 @@ } -static void print_help(void) +static void print_help(const char *cmd) { int n; printf("commands:\n"); - for (n = 0; wpa_cli_commands[n].cmd; n++) - print_cmd_help(&wpa_cli_commands[n], " "); + for (n = 0; wpa_cli_commands[n].cmd; n++) { + if (cmd == NULL || str_starts(wpa_cli_commands[n].cmd, cmd)) + print_cmd_help(&wpa_cli_commands[n], " "); + } } @@ -3068,9 +2960,12 @@ { char **res; int i, count; + struct cli_txt_entry *e; - count = sizeof(wpa_cli_commands) / sizeof(wpa_cli_commands[0]); - res = os_zalloc(count * sizeof(char *)); + count = ARRAY_SIZE(wpa_cli_commands); + count += dl_list_len(&p2p_groups); + count += dl_list_len(&ifnames); + res = os_calloc(count + 1, sizeof(char *)); if (res == NULL) return NULL; @@ -3080,6 +2975,22 @@ break; } + dl_list_for_each(e, &p2p_groups, struct cli_txt_entry, list) { + size_t len = 8 + os_strlen(e->txt); + res[i] = os_malloc(len); + if (res[i] == NULL) + break; + os_snprintf(res[i], len, "ifname=%s", e->txt); + i++; + } + + dl_list_for_each(e, &ifnames, struct cli_txt_entry, list) { + res[i] = os_strdup(e->txt); + if (res[i] == NULL) + break; + i++; + } + return res; } @@ -3089,19 +3000,11 @@ { int i; - if (os_strcasecmp(cmd, "bss") == 0) - return wpa_cli_complete_bss(str, pos); -#ifdef CONFIG_P2P - if (os_strcasecmp(cmd, "p2p_connect") == 0) - return wpa_cli_complete_p2p_connect(str, pos); - if (os_strcasecmp(cmd, "p2p_peer") == 0) - return wpa_cli_complete_p2p_peer(str, pos); - if (os_strcasecmp(cmd, "p2p_group_remove") == 0) - return wpa_cli_complete_p2p_group_remove(str, pos); -#endif /* CONFIG_P2P */ - for (i = 0; wpa_cli_commands[i].cmd; i++) { if (os_strcasecmp(wpa_cli_commands[i].cmd, cmd) == 0) { + if (wpa_cli_commands[i].completion) + return wpa_cli_commands[i].completion(str, + pos); edit_clear_line(); printf("\r%s\n", wpa_cli_commands[i].usage); edit_redraw(); @@ -3119,6 +3022,14 @@ const char *end; char *cmd; + if (pos > 7 && os_strncasecmp(str, "IFNAME=", 7) == 0) { + end = os_strchr(str, ' '); + if (end && pos > end - str) { + pos -= end - str + 1; + str = end + 1; + } + } + end = os_strchr(str, ' '); if (end == NULL || str + pos < end) return wpa_list_cmd_list(); @@ -3140,6 +3051,16 @@ int count; int ret = 0; + if (argc > 1 && os_strncasecmp(argv[0], "IFNAME=", 7) == 0) { + ifname_prefix = argv[0] + 7; + argv = &argv[1]; + argc--; + } else + ifname_prefix = NULL; + + if (argc == 0) + return -1; + count = 0; cmd = wpa_cli_commands; while (cmd->cmd) { @@ -3259,7 +3180,7 @@ os_setenv("WPA_CTRL_DIR", ctrl_iface_dir, 1); - if (!wpa_cli_connected || new_id != wpa_cli_last_id) { + if (wpa_cli_connected <= 0 || new_id != wpa_cli_last_id) { wpa_cli_connected = 1; wpa_cli_last_id = new_id; wpa_cli_exec(action_file, ctrl_ifname, "CONNECTED"); @@ -3277,10 +3198,18 @@ wpa_cli_exec(action_file, ctrl_ifname, pos); } else if (str_match(pos, P2P_EVENT_CROSS_CONNECT_DISABLE)) { wpa_cli_exec(action_file, ctrl_ifname, pos); + } else if (str_match(pos, P2P_EVENT_GO_NEG_FAILURE)) { + wpa_cli_exec(action_file, ctrl_ifname, pos); } else if (str_match(pos, WPS_EVENT_SUCCESS)) { wpa_cli_exec(action_file, ctrl_ifname, pos); } else if (str_match(pos, WPS_EVENT_FAIL)) { wpa_cli_exec(action_file, ctrl_ifname, pos); + } else if (str_match(pos, AP_STA_CONNECTED)) { + wpa_cli_exec(action_file, ctrl_ifname, pos); + } else if (str_match(pos, AP_STA_DISCONNECTED)) { + wpa_cli_exec(action_file, ctrl_ifname, pos); + } else if (str_match(pos, ESS_DISASSOC_IMMINENT)) { + wpa_cli_exec(action_file, ctrl_ifname, pos); } else if (str_match(pos, WPA_EVENT_TERMINATING)) { printf("wpa_supplicant is terminating - stop monitoring\n"); wpa_cli_quit = 1; @@ -3299,7 +3228,14 @@ static void wpa_cli_reconnect(void) { wpa_cli_close_connection(); - wpa_cli_open_connection(ctrl_ifname, 1); + if (wpa_cli_open_connection(ctrl_ifname, 1) < 0) + return; + + if (interactive) { + edit_clear_line(); + printf("\rConnection to wpa_supplicant re-established\n"); + edit_redraw(); + } } @@ -3371,6 +3307,33 @@ } +static int check_terminating(const char *msg) +{ + const char *pos = msg; + + if (*pos == '<') { + /* skip priority */ + pos = os_strchr(pos, '>'); + if (pos) + pos++; + else + pos = msg; + } + + if (str_match(pos, WPA_EVENT_TERMINATING) && ctrl_conn) { + edit_clear_line(); + printf("\rConnection to wpa_supplicant lost - trying to " + "reconnect\n"); + edit_redraw(); + wpa_cli_attached = 0; + wpa_cli_close_connection(); + return 1; + } + + return 0; +} + + static void wpa_cli_recv_pending(struct wpa_ctrl *ctrl, int action_monitor) { if (ctrl_conn == NULL) { @@ -3391,6 +3354,9 @@ printf("\r%s\n", buf); edit_redraw(); } + + if (interactive && check_terminating(buf) > 0) + return; } } else { printf("Could not read pending message.\n"); @@ -3450,12 +3416,6 @@ } -static void wpa_cli_eloop_terminate(int sig, void *signal_ctx) -{ - eloop_terminate(); -} - - static void wpa_cli_mon_receive(int sock, void *eloop_ctx, void *sock_ctx) { wpa_cli_recv_pending(mon_conn, 0); @@ -3478,11 +3438,18 @@ } -static void wpa_cli_interactive(void) -{ - char *home, *hfile = NULL; - - printf("\nInteractive mode\n\n"); +static int warning_displayed = 0; +static char *hfile = NULL; +static int edit_started = 0; + +static void start_edit(void) +{ + char *home; + char *ps = NULL; + +#ifdef CONFIG_CTRL_IFACE_UDP_REMOTE + ps = wpa_ctrl_get_remote_ifname(ctrl_conn); +#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */ home = getenv("HOME"); if (home) { @@ -3493,17 +3460,122 @@ os_snprintf(hfile, hfile_len, "%s/%s", home, fname); } - eloop_register_signal_terminate(wpa_cli_eloop_terminate, NULL); - edit_init(wpa_cli_edit_cmd_cb, wpa_cli_edit_eof_cb, - wpa_cli_edit_completion_cb, NULL, hfile); + if (edit_init(wpa_cli_edit_cmd_cb, wpa_cli_edit_eof_cb, + wpa_cli_edit_completion_cb, NULL, hfile, ps) < 0) { + eloop_terminate(); + return; + } + + edit_started = 1; eloop_register_timeout(ping_interval, 0, wpa_cli_ping, NULL, NULL); +} + + +static void update_bssid_list(struct wpa_ctrl *ctrl) +{ + char buf[4096]; + size_t len = sizeof(buf); + int ret; + char *cmd = "BSS RANGE=ALL MASK=0x2"; + char *pos, *end; + + if (ctrl == NULL) + return; + ret = wpa_ctrl_request(ctrl, cmd, os_strlen(cmd), buf, &len, NULL); + if (ret < 0) + return; + buf[len] = '\0'; + pos = buf; + while (pos) { + pos = os_strstr(pos, "bssid="); + if (pos == NULL) + break; + pos += 6; + end = os_strchr(pos, '\n'); + if (end == NULL) + break; + *end = '\0'; + cli_txt_list_add(&bsses, pos); + pos = end + 1; + } +} + + +static void update_ifnames(struct wpa_ctrl *ctrl) +{ + char buf[4096]; + size_t len = sizeof(buf); + int ret; + char *cmd = "INTERFACES"; + char *pos, *end; + char txt[200]; + + cli_txt_list_flush(&ifnames); + + if (ctrl == NULL) + return; + ret = wpa_ctrl_request(ctrl, cmd, os_strlen(cmd), buf, &len, NULL); + if (ret < 0) + return; + buf[len] = '\0'; + + pos = buf; + while (pos) { + end = os_strchr(pos, '\n'); + if (end == NULL) + break; + *end = '\0'; + ret = os_snprintf(txt, sizeof(txt), "ifname=%s", pos); + if (ret > 0 && ret < (int) sizeof(txt)) + cli_txt_list_add(&ifnames, txt); + pos = end + 1; + } +} + + +static void try_connection(void *eloop_ctx, void *timeout_ctx) +{ + if (ctrl_conn) + goto done; + + if (ctrl_ifname == NULL) + ctrl_ifname = wpa_cli_get_default_ifname(); + + if (!wpa_cli_open_connection(ctrl_ifname, 1) == 0) { + if (!warning_displayed) { + printf("Could not connect to wpa_supplicant: " + "%s - re-trying\n", ctrl_ifname); + warning_displayed = 1; + } + eloop_register_timeout(1, 0, try_connection, NULL, NULL); + return; + } + + update_bssid_list(ctrl_conn); + + if (warning_displayed) + printf("Connection established.\n"); + +done: + start_edit(); +} + + +static void wpa_cli_interactive(void) +{ + printf("\nInteractive mode\n\n"); + + eloop_register_timeout(0, 0, try_connection, NULL, NULL); eloop_run(); + eloop_cancel_timeout(try_connection, NULL, NULL); cli_txt_list_flush(&p2p_peers); cli_txt_list_flush(&p2p_groups); cli_txt_list_flush(&bsses); - edit_deinit(hfile, wpa_cli_edit_filter_history_cb); + cli_txt_list_flush(&ifnames); + if (edit_started) + edit_deinit(hfile, wpa_cli_edit_filter_history_cb); os_free(hfile); eloop_cancel_timeout(wpa_cli_ping, NULL, NULL); wpa_cli_close_connection(); @@ -3562,10 +3634,10 @@ os_program_deinit(); } -static void wpa_cli_terminate(int sig) + +static void wpa_cli_terminate(int sig, void *ctx) { - wpa_cli_cleanup(); - exit(0); + eloop_terminate(); } @@ -3608,7 +3680,7 @@ #endif /* CONFIG_CTRL_IFACE_UNIX */ #ifdef CONFIG_CTRL_IFACE_NAMED_PIPE - char buf[2048], *pos; + char buf[4096], *pos; size_t len; struct wpa_ctrl *ctrl; int ret; @@ -3635,7 +3707,6 @@ int main(int argc, char *argv[]) { - int warning_displayed = 0; int c; int daemonize = 0; int ret = 0; @@ -3698,41 +3769,44 @@ ctrl_conn = wpa_ctrl_open(global); #endif /* CONFIG_CTRL_IFACE_NAMED_PIPE */ if (ctrl_conn == NULL) { - perror("Failed to connect to wpa_supplicant - " - "wpa_ctrl_open"); + fprintf(stderr, "Failed to connect to wpa_supplicant " + "global interface: %s error: %s\n", + global, strerror(errno)); return -1; } + + if (interactive) { + update_ifnames(ctrl_conn); + mon_conn = wpa_ctrl_open(global); + if (mon_conn) { + if (wpa_ctrl_attach(mon_conn) == 0) { + wpa_cli_attached = 1; + eloop_register_read_sock( + wpa_ctrl_get_fd(mon_conn), + wpa_cli_mon_receive, + NULL, NULL); + } else { + printf("Failed to open monitor " + "connection through global " + "control interface\n"); + } + } + } } -#ifndef _WIN32_WCE - signal(SIGINT, wpa_cli_terminate); - signal(SIGTERM, wpa_cli_terminate); -#endif /* _WIN32_WCE */ + eloop_register_signal_terminate(wpa_cli_terminate, NULL); if (ctrl_ifname == NULL) ctrl_ifname = wpa_cli_get_default_ifname(); if (interactive) { - for (; !global;) { - if (wpa_cli_open_connection(ctrl_ifname, 1) == 0) { - if (warning_displayed) - printf("Connection established.\n"); - break; - } - - if (!warning_displayed) { - printf("Could not connect to wpa_supplicant - " - "re-trying\n"); - warning_displayed = 1; - } - os_sleep(1, 0); - continue; - } + wpa_cli_interactive(); } else { if (!global && wpa_cli_open_connection(ctrl_ifname, 0) < 0) { - perror("Failed to connect to wpa_supplicant - " - "wpa_ctrl_open"); + fprintf(stderr, "Failed to connect to non-global " + "ctrl_ifname: %s error: %s\n", + ctrl_ifname, strerror(errno)); return -1; } @@ -3745,17 +3819,16 @@ return -1; } } - } - if (daemonize && os_daemonize(pid_file)) - return -1; + if (daemonize && os_daemonize(pid_file)) + return -1; - if (interactive) - wpa_cli_interactive(); - else if (action_file) - wpa_cli_action(ctrl_conn); - else - ret = wpa_request(ctrl_conn, argc - optind, &argv[optind]); + if (action_file) + wpa_cli_action(ctrl_conn); + else + ret = wpa_request(ctrl_conn, argc - optind, + &argv[optind]); + } os_free(ctrl_ifname); eloop_destroy(); diff -Nru wpa-1.0/wpa_supplicant/wpa_gui/eventhistory.ui wpa-2.1/wpa_supplicant/wpa_gui/eventhistory.ui --- wpa-1.0/wpa_supplicant/wpa_gui/eventhistory.ui 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/wpa_gui/eventhistory.ui 1970-01-01 00:00:00.000000000 +0000 @@ -1,125 +0,0 @@ - -EventHistory - - - EventHistory - - - - 0 - 0 - 533 - 285 - - - - Event history - - - - unnamed - - - - - Timestamp - - - true - - - true - - - - - Message - - - true - - - true - - - - eventListView - - - - 7 - 7 - 0 - 0 - - - - Manual - - - NoSelection - - - LastColumn - - - - - layout30 - - - - unnamed - - - - spacer3 - - - Horizontal - - - Expanding - - - - 20 - 20 - - - - - - closeButton - - - Close - - - - - - - - - closeButton - clicked() - EventHistory - close() - - - - wpamsg.h - eventhistory.ui.h - - - addEvents( WpaMsgList msgs ) - addEvent( WpaMsg msg ) - - - init() - destroy() - - - - diff -Nru wpa-1.0/wpa_supplicant/wpa_gui/eventhistory.ui.h wpa-2.1/wpa_supplicant/wpa_gui/eventhistory.ui.h --- wpa-1.0/wpa_supplicant/wpa_gui/eventhistory.ui.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/wpa_gui/eventhistory.ui.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,41 +0,0 @@ -/**************************************************************************** -** ui.h extension file, included from the uic-generated form implementation. -** -** If you want to add, delete, or rename functions or slots, use -** Qt Designer to update this file, preserving your code. -** -** You should not define a constructor or destructor in this file. -** Instead, write your code in functions called init() and destroy(). -** These will automatically be called by the form's constructor and -** destructor. -*****************************************************************************/ - -void EventHistory::init() -{ -} - - -void EventHistory::destroy() -{ -} - - -void EventHistory::addEvents(WpaMsgList msgs) -{ - WpaMsgList::iterator it; - for (it = msgs.begin(); it != msgs.end(); it++) { - addEvent(*it); - } -} - - -void EventHistory::addEvent(WpaMsg msg) -{ - Q3ListViewItem *item; - item = new Q3ListViewItem(eventListView, - msg.getTimestamp().toString("yyyy-MM-dd hh:mm:ss.zzz"), - msg.getMsg()); - if (item == NULL) - return; - eventListView->setSelected(item, false); -} diff -Nru wpa-1.0/wpa_supplicant/wpa_gui/.gitignore wpa-2.1/wpa_supplicant/wpa_gui/.gitignore --- wpa-1.0/wpa_supplicant/wpa_gui/.gitignore 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/wpa_gui/.gitignore 1970-01-01 00:00:00.000000000 +0000 @@ -1,3 +0,0 @@ -.moc -.obj -.ui diff -Nru wpa-1.0/wpa_supplicant/wpa_gui/main.cpp wpa-2.1/wpa_supplicant/wpa_gui/main.cpp --- wpa-1.0/wpa_supplicant/wpa_gui/main.cpp 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/wpa_gui/main.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,30 +0,0 @@ -#ifdef CONFIG_NATIVE_WINDOWS -#include -#endif /* CONFIG_NATIVE_WINDOWS */ -#include -#include "wpagui.h" - -int main( int argc, char ** argv ) -{ - QApplication a( argc, argv ); - WpaGui w; - int ret; - -#ifdef CONFIG_NATIVE_WINDOWS - WSADATA wsaData; - if (WSAStartup(MAKEWORD(2, 0), &wsaData)) { - printf("Could not find a usable WinSock.dll\n"); - return -1; - } -#endif /* CONFIG_NATIVE_WINDOWS */ - - w.show(); - a.connect( &a, SIGNAL( lastWindowClosed() ), &a, SLOT( quit() ) ); - ret = a.exec(); - -#ifdef CONFIG_NATIVE_WINDOWS - WSACleanup(); -#endif /* CONFIG_NATIVE_WINDOWS */ - - return ret; -} diff -Nru wpa-1.0/wpa_supplicant/wpa_gui/networkconfig.ui wpa-2.1/wpa_supplicant/wpa_gui/networkconfig.ui --- wpa-1.0/wpa_supplicant/wpa_gui/networkconfig.ui 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/wpa_gui/networkconfig.ui 1970-01-01 00:00:00.000000000 +0000 @@ -1,475 +0,0 @@ - -NetworkConfig - - - NetworkConfig - - - - 0 - 0 - 380 - 430 - - - - NetworkConfig - - - - unnamed - - - - cancelButton - - - Cancel - - - - - frame9 - - - StyledPanel - - - Raised - - - - unnamed - - - - textLabel0 - - - SSID - - - - - ssidEdit - - - - - - Network name (Service Set IDentifier) - - - - - textLabel1 - - - Network ID - - - - - idstrEdit - - - - - - Network Identification String - - - - - textLabel2 - - - Authentication - - - - - - Plaintext or static WEP - - - - - IEEE 802.1X - - - - - WPA-Personal (PSK) - - - - - WPA-Enterprise (EAP) - - - - - WPA2-Personal (PSK) - - - - - WPA2-Enterprise (EAP) - - - - authSelect - - - - - textLabel3 - - - Encryption - - - - - - None - - - - - WEP - - - - - TKIP - - - - - CCMP - - - - encrSelect - - - - - textLabel4 - - - PSK - - - - - pskEdit - - - false - - - Password - - - WPA/WPA2 pre-shared key or passphrase - - - - - - - - textLabel5 - - - EAP method - - - - - eapSelect - - - false - - - - - textLabel6 - - - Identity - - - - - identityEdit - - - false - - - Username/Identity for EAP methods - - - - - textLabel7 - - - Password - - - - - passwordEdit - - - false - - - Password - - - Password for EAP methods - - - - - textLabel1_2 - - - CA certificate - - - - - cacertEdit - - - false - - - - - buttonGroup1 - - - true - - - WEP keys - - - - unnamed - - - - wep0Radio - - - false - - - key 0 - - - - - wep1Radio - - - false - - - key 1 - - - - - wep3Radio - - - false - - - key 3 - - - - - wep2Radio - - - false - - - key 2 - - - - - wep0Edit - - - false - - - - - wep1Edit - - - false - - - - - wep2Edit - - - false - - - - - wep3Edit - - - false - - - - - - - - - spacer5 - - - Horizontal - - - Expanding - - - - 130 - 20 - - - - - - addButton - - - Add - - - - - removeButton - - - false - - - Remove - - - - - - - authSelect - activated(int) - NetworkConfig - authChanged(int) - - - cancelButton - clicked() - NetworkConfig - close() - - - addButton - clicked() - NetworkConfig - addNetwork() - - - encrSelect - activated(const QString&) - NetworkConfig - encrChanged(const QString&) - - - removeButton - clicked() - NetworkConfig - removeNetwork() - - - - ssidEdit - idstrEdit - authSelect - encrSelect - pskEdit - eapSelect - identityEdit - passwordEdit - cacertEdit - wep0Radio - wep1Radio - wep2Radio - wep3Radio - wep0Edit - wep1Edit - wep2Edit - wep3Edit - addButton - removeButton - cancelButton - - - qlistview.h - qmessagebox.h - wpagui.h - networkconfig.ui.h - - - class WpaGui; - - - WpaGui *wpagui; - int edit_network_id; - bool new_network; - - - authChanged( int sel ) - addNetwork() - encrChanged( const QString & sel ) - writeWepKey( int network_id, QLineEdit * edit, int id ) - removeNetwork() - - - init() - paramsFromScanResults( QListViewItem * sel ) - setWpaGui( WpaGui * _wpagui ) - setNetworkParam( int id, const char * field, const char * value, bool quote ) - wepEnabled( bool enabled ) - paramsFromConfig( int network_id ) - newNetwork() - getEapCapa() - - - - diff -Nru wpa-1.0/wpa_supplicant/wpa_gui/networkconfig.ui.h wpa-2.1/wpa_supplicant/wpa_gui/networkconfig.ui.h --- wpa-1.0/wpa_supplicant/wpa_gui/networkconfig.ui.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/wpa_gui/networkconfig.ui.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,552 +0,0 @@ -/**************************************************************************** -** ui.h extension file, included from the uic-generated form implementation. -** -** If you want to add, delete, or rename functions or slots, use -** Qt Designer to update this file, preserving your code. -** -** You should not define a constructor or destructor in this file. -** Instead, write your code in functions called init() and destroy(). -** These will automatically be called by the form's constructor and -** destructor. -*****************************************************************************/ - -#include - -enum { - AUTH_NONE = 0, - AUTH_IEEE8021X = 1, - AUTH_WPA_PSK = 2, - AUTH_WPA_EAP = 3, - AUTH_WPA2_PSK = 4, - AUTH_WPA2_EAP = 5 -}; - -#define WPA_GUI_KEY_DATA "[key is configured]" - -void NetworkConfig::init() -{ - wpagui = NULL; - new_network = false; -} - -void NetworkConfig::paramsFromScanResults(Q3ListViewItem *sel) -{ - new_network = true; - - /* SSID BSSID frequency signal flags */ - setCaption(sel->text(0)); - ssidEdit->setText(sel->text(0)); - - QString flags = sel->text(4); - int auth, encr = 0; - if (flags.find("[WPA2-EAP") >= 0) - auth = AUTH_WPA2_EAP; - else if (flags.find("[WPA-EAP") >= 0) - auth = AUTH_WPA_EAP; - else if (flags.find("[WPA2-PSK") >= 0) - auth = AUTH_WPA2_PSK; - else if (flags.find("[WPA-PSK") >= 0) - auth = AUTH_WPA_PSK; - else - auth = AUTH_NONE; - - if (flags.find("-CCMP") >= 0) - encr = 1; - else if (flags.find("-TKIP") >= 0) - encr = 0; - else if (flags.find("WEP") >= 0) - encr = 1; - else - encr = 0; - - authSelect->setCurrentItem(auth); - authChanged(auth); - encrSelect->setCurrentItem(encr); - - getEapCapa(); -} - - -void NetworkConfig::authChanged(int sel) -{ - pskEdit->setEnabled(sel == AUTH_WPA_PSK || sel == AUTH_WPA2_PSK); - bool eap = sel == AUTH_IEEE8021X || sel == AUTH_WPA_EAP || - sel == AUTH_WPA2_EAP; - eapSelect->setEnabled(eap); - identityEdit->setEnabled(eap); - passwordEdit->setEnabled(eap); - cacertEdit->setEnabled(eap); - - while (encrSelect->count()) - encrSelect->removeItem(0); - - if (sel == AUTH_NONE || sel == AUTH_IEEE8021X) { - encrSelect->insertItem("None"); - encrSelect->insertItem("WEP"); - encrSelect->setCurrentItem(sel == AUTH_NONE ? 0 : 1); - } else { - encrSelect->insertItem("TKIP"); - encrSelect->insertItem("CCMP"); - encrSelect->setCurrentItem((sel == AUTH_WPA2_PSK || - sel == AUTH_WPA2_EAP) ? 1 : 0); - } - - wepEnabled(sel == AUTH_IEEE8021X); -} - - -void NetworkConfig::addNetwork() -{ - char reply[10], cmd[256]; - size_t reply_len; - int id; - int psklen = pskEdit->text().length(); - int auth = authSelect->currentItem(); - - if (auth == AUTH_WPA_PSK || auth == AUTH_WPA2_PSK) { - if (psklen < 8 || psklen > 64) { - QMessageBox::warning(this, "wpa_gui", "WPA-PSK requires a passphrase " - "of 8 to 63 characters\n" - "or 64 hex digit PSK"); - return; - } - } - - if (wpagui == NULL) - return; - - memset(reply, 0, sizeof(reply)); - reply_len = sizeof(reply) - 1; - - if (new_network) { - wpagui->ctrlRequest("ADD_NETWORK", reply, &reply_len); - if (reply[0] == 'F') { - QMessageBox::warning(this, "wpa_gui", "Failed to add network to wpa_supplicant\n" - "configuration."); - return; - } - id = atoi(reply); - } else { - id = edit_network_id; - } - - setNetworkParam(id, "ssid", ssidEdit->text().ascii(), true); - - if (idstrEdit->isEnabled()) - setNetworkParam(id, "id_str", idstrEdit->text().ascii(), true); - - const char *key_mgmt = NULL, *proto = NULL, *pairwise = NULL; - switch (auth) { - case AUTH_NONE: - key_mgmt = "NONE"; - break; - case AUTH_IEEE8021X: - key_mgmt = "IEEE8021X"; - break; - case AUTH_WPA_PSK: - key_mgmt = "WPA-PSK"; - proto = "WPA"; - break; - case AUTH_WPA_EAP: - key_mgmt = "WPA-EAP"; - proto = "WPA"; - break; - case AUTH_WPA2_PSK: - key_mgmt = "WPA-PSK"; - proto = "WPA2"; - break; - case AUTH_WPA2_EAP: - key_mgmt = "WPA-EAP"; - proto = "WPA2"; - break; - } - - if (auth == AUTH_WPA_PSK || auth == AUTH_WPA_EAP || - auth == AUTH_WPA2_PSK || auth == AUTH_WPA2_EAP) { - int encr = encrSelect->currentItem(); - if (encr == 0) - pairwise = "TKIP"; - else - pairwise = "CCMP"; - } - - if (proto) - setNetworkParam(id, "proto", proto, false); - if (key_mgmt) - setNetworkParam(id, "key_mgmt", key_mgmt, false); - if (pairwise) { - setNetworkParam(id, "pairwise", pairwise, false); - setNetworkParam(id, "group", "TKIP CCMP WEP104 WEP40", false); - } - if (pskEdit->isEnabled() && - strcmp(pskEdit->text().ascii(), WPA_GUI_KEY_DATA) != 0) - setNetworkParam(id, "psk", pskEdit->text().ascii(), psklen != 64); - if (eapSelect->isEnabled()) - setNetworkParam(id, "eap", eapSelect->currentText().ascii(), false); - if (identityEdit->isEnabled()) - setNetworkParam(id, "identity", identityEdit->text().ascii(), true); - if (passwordEdit->isEnabled() && - strcmp(passwordEdit->text().ascii(), WPA_GUI_KEY_DATA) != 0) - setNetworkParam(id, "password", passwordEdit->text().ascii(), true); - if (cacertEdit->isEnabled()) - setNetworkParam(id, "ca_cert", cacertEdit->text().ascii(), true); - writeWepKey(id, wep0Edit, 0); - writeWepKey(id, wep1Edit, 1); - writeWepKey(id, wep2Edit, 2); - writeWepKey(id, wep3Edit, 3); - - if (wep0Radio->isEnabled() && wep0Radio->isChecked()) - setNetworkParam(id, "wep_tx_keyidx", "0", false); - else if (wep1Radio->isEnabled() && wep1Radio->isChecked()) - setNetworkParam(id, "wep_tx_keyidx", "1", false); - else if (wep2Radio->isEnabled() && wep2Radio->isChecked()) - setNetworkParam(id, "wep_tx_keyidx", "2", false); - else if (wep3Radio->isEnabled() && wep3Radio->isChecked()) - setNetworkParam(id, "wep_tx_keyidx", "3", false); - - snprintf(cmd, sizeof(cmd), "ENABLE_NETWORK %d", id); - reply_len = sizeof(reply); - wpagui->ctrlRequest(cmd, reply, &reply_len); - if (strncmp(reply, "OK", 2) != 0) { - QMessageBox::warning(this, "wpa_gui", "Failed to enable network in wpa_supplicant\n" - "configuration."); - /* Network was added, so continue anyway */ - } - wpagui->triggerUpdate(); - wpagui->ctrlRequest("SAVE_CONFIG", reply, &reply_len); - - close(); -} - - -void NetworkConfig::setWpaGui( WpaGui *_wpagui ) -{ - wpagui = _wpagui; -} - - -int NetworkConfig::setNetworkParam(int id, const char *field, const char *value, bool quote) -{ - char reply[10], cmd[256]; - size_t reply_len; - snprintf(cmd, sizeof(cmd), "SET_NETWORK %d %s %s%s%s", - id, field, quote ? "\"" : "", value, quote ? "\"" : ""); - reply_len = sizeof(reply); - wpagui->ctrlRequest(cmd, reply, &reply_len); - return strncmp(reply, "OK", 2) == 0 ? 0 : -1; -} - - -void NetworkConfig::encrChanged( const QString &sel ) -{ - wepEnabled(sel.find("WEP") == 0); -} - - -void NetworkConfig::wepEnabled( bool enabled ) -{ - wep0Edit->setEnabled(enabled); - wep1Edit->setEnabled(enabled); - wep2Edit->setEnabled(enabled); - wep3Edit->setEnabled(enabled); - wep0Radio->setEnabled(enabled); - wep1Radio->setEnabled(enabled); - wep2Radio->setEnabled(enabled); - wep3Radio->setEnabled(enabled); -} - - -void NetworkConfig::writeWepKey( int network_id, QLineEdit *edit, int id ) -{ - char buf[10]; - bool hex; - const char *txt, *pos; - size_t len; - - if (!edit->isEnabled() || edit->text().isEmpty()) - return; - - /* - * Assume hex key if only hex characters are present and length matches - * with 40, 104, or 128-bit key - */ - txt = edit->text().ascii(); - if (strcmp(txt, WPA_GUI_KEY_DATA) == 0) - return; - len = strlen(txt); - if (len == 0) - return; - pos = txt; - hex = true; - while (*pos) { - if (!((*pos >= '0' && *pos <= '9') || (*pos >= 'a' && *pos <= 'f') || - (*pos >= 'A' && *pos <= 'F'))) { - hex = false; - break; - } - pos++; - } - if (hex && len != 10 && len != 26 && len != 32) - hex = false; - snprintf(buf, sizeof(buf), "wep_key%d", id); - setNetworkParam(network_id, buf, txt, !hex); -} - - -static int key_value_isset(const char *reply, size_t reply_len) -{ - return reply_len > 0 && (reply_len < 4 || memcmp(reply, "FAIL", 4) != 0); -} - - -void NetworkConfig::paramsFromConfig( int network_id ) -{ - int i, res; - - edit_network_id = network_id; - getEapCapa(); - - char reply[1024], cmd[256], *pos; - size_t reply_len; - - snprintf(cmd, sizeof(cmd), "GET_NETWORK %d ssid", network_id); - reply_len = sizeof(reply) - 1; - if (wpagui->ctrlRequest(cmd, reply, &reply_len) >= 0 && reply_len >= 2 && - reply[0] == '"') { - reply[reply_len] = '\0'; - pos = strchr(reply + 1, '"'); - if (pos) - *pos = '\0'; - ssidEdit->setText(reply + 1); - } - - snprintf(cmd, sizeof(cmd), "GET_NETWORK %d id_str", network_id); - reply_len = sizeof(reply) - 1; - if (wpagui->ctrlRequest(cmd, reply, &reply_len) >= 0 && reply_len >= 2 && - reply[0] == '"') { - reply[reply_len] = '\0'; - pos = strchr(reply + 1, '"'); - if (pos) - *pos = '\0'; - idstrEdit->setText(reply + 1); - } - - snprintf(cmd, sizeof(cmd), "GET_NETWORK %d proto", network_id); - reply_len = sizeof(reply) - 1; - int wpa = 0; - if (wpagui->ctrlRequest(cmd, reply, &reply_len) >= 0) { - reply[reply_len] = '\0'; - if (strstr(reply, "RSN") || strstr(reply, "WPA2")) - wpa = 2; - else if (strstr(reply, "WPA")) - wpa = 1; - } - - int auth = AUTH_NONE, encr = 0; - snprintf(cmd, sizeof(cmd), "GET_NETWORK %d key_mgmt", network_id); - reply_len = sizeof(reply) - 1; - if (wpagui->ctrlRequest(cmd, reply, &reply_len) >= 0) { - reply[reply_len] = '\0'; - if (strstr(reply, "WPA-EAP")) - auth = wpa & 2 ? AUTH_WPA2_EAP : AUTH_WPA_EAP; - else if (strstr(reply, "WPA-PSK")) - auth = wpa & 2 ? AUTH_WPA2_PSK : AUTH_WPA_PSK; - else if (strstr(reply, "IEEE8021X")) { - auth = AUTH_IEEE8021X; - encr = 1; - } - } - - snprintf(cmd, sizeof(cmd), "GET_NETWORK %d pairwise", network_id); - reply_len = sizeof(reply) - 1; - if (wpagui->ctrlRequest(cmd, reply, &reply_len) >= 0) { - reply[reply_len] = '\0'; - if (strstr(reply, "CCMP") && auth != AUTH_NONE) - encr = 1; - else if (strstr(reply, "TKIP")) - encr = 0; - else if (strstr(reply, "WEP")) - encr = 1; - else - encr = 0; - } - - snprintf(cmd, sizeof(cmd), "GET_NETWORK %d psk", network_id); - reply_len = sizeof(reply) - 1; - res = wpagui->ctrlRequest(cmd, reply, &reply_len); - if (res >= 0 && reply_len >= 2 && reply[0] == '"') { - reply[reply_len] = '\0'; - pos = strchr(reply + 1, '"'); - if (pos) - *pos = '\0'; - pskEdit->setText(reply + 1); - } else if (res >= 0 && key_value_isset(reply, reply_len)) { - pskEdit->setText(WPA_GUI_KEY_DATA); - } - - snprintf(cmd, sizeof(cmd), "GET_NETWORK %d identity", network_id); - reply_len = sizeof(reply) - 1; - if (wpagui->ctrlRequest(cmd, reply, &reply_len) >= 0 && reply_len >= 2 && - reply[0] == '"') { - reply[reply_len] = '\0'; - pos = strchr(reply + 1, '"'); - if (pos) - *pos = '\0'; - identityEdit->setText(reply + 1); - } - - snprintf(cmd, sizeof(cmd), "GET_NETWORK %d password", network_id); - reply_len = sizeof(reply) - 1; - res = wpagui->ctrlRequest(cmd, reply, &reply_len); - if (res >= 0 && reply_len >= 2 && - reply[0] == '"') { - reply[reply_len] = '\0'; - pos = strchr(reply + 1, '"'); - if (pos) - *pos = '\0'; - passwordEdit->setText(reply + 1); - } else if (res >= 0 && key_value_isset(reply, reply_len)) { - passwordEdit->setText(WPA_GUI_KEY_DATA); - } - - snprintf(cmd, sizeof(cmd), "GET_NETWORK %d ca_cert", network_id); - reply_len = sizeof(reply) - 1; - if (wpagui->ctrlRequest(cmd, reply, &reply_len) >= 0 && reply_len >= 2 && - reply[0] == '"') { - reply[reply_len] = '\0'; - pos = strchr(reply + 1, '"'); - if (pos) - *pos = '\0'; - cacertEdit->setText(reply + 1); - } - - snprintf(cmd, sizeof(cmd), "GET_NETWORK %d eap", network_id); - reply_len = sizeof(reply) - 1; - if (wpagui->ctrlRequest(cmd, reply, &reply_len) >= 0 && reply_len >= 1) { - reply[reply_len] = '\0'; - for (i = 0; i < eapSelect->count(); i++) { - if (eapSelect->text(i).compare(reply) == 0) { - eapSelect->setCurrentItem(i); - break; - } - } - } - - for (i = 0; i < 4; i++) { - QLineEdit *wepEdit; - switch (i) { - default: - case 0: - wepEdit = wep0Edit; - break; - case 1: - wepEdit = wep1Edit; - break; - case 2: - wepEdit = wep2Edit; - break; - case 3: - wepEdit = wep3Edit; - break; - } - snprintf(cmd, sizeof(cmd), "GET_NETWORK %d wep_key%d", network_id, i); - reply_len = sizeof(reply) - 1; - res = wpagui->ctrlRequest(cmd, reply, &reply_len); - if (res >= 0 && reply_len >= 2 && reply[0] == '"') { - reply[reply_len] = '\0'; - pos = strchr(reply + 1, '"'); - if (pos) - *pos = '\0'; - if (auth == AUTH_NONE || auth == AUTH_IEEE8021X) - encr = 1; - - wepEdit->setText(reply + 1); - } else if (res >= 0 && key_value_isset(reply, reply_len)) { - if (auth == AUTH_NONE || auth == AUTH_IEEE8021X) - encr = 1; - wepEdit->setText(WPA_GUI_KEY_DATA); - } - } - - snprintf(cmd, sizeof(cmd), "GET_NETWORK %d wep_tx_keyidx", network_id); - reply_len = sizeof(reply) - 1; - if (wpagui->ctrlRequest(cmd, reply, &reply_len) >= 0 && reply_len >= 1) { - reply[reply_len] = '\0'; - switch (atoi(reply)) { - case 0: - wep0Radio->setChecked(true); - break; - case 1: - wep1Radio->setChecked(true); - break; - case 2: - wep2Radio->setChecked(true); - break; - case 3: - wep3Radio->setChecked(true); - break; - } - } - - authSelect->setCurrentItem(auth); - authChanged(auth); - encrSelect->setCurrentItem(encr); - if (auth == AUTH_NONE || auth == AUTH_IEEE8021X) - wepEnabled(encr == 1); - - removeButton->setEnabled(true); - addButton->setText("Save"); -} - - -void NetworkConfig::removeNetwork() -{ - char reply[10], cmd[256]; - size_t reply_len; - - if (QMessageBox::information(this, "wpa_gui", - "This will permanently remove the network\n" - "from the configuration. Do you really want\n" - "to remove this network?", "Yes", "No") != 0) - return; - - snprintf(cmd, sizeof(cmd), "REMOVE_NETWORK %d", edit_network_id); - reply_len = sizeof(reply); - wpagui->ctrlRequest(cmd, reply, &reply_len); - if (strncmp(reply, "OK", 2) != 0) { - QMessageBox::warning(this, "wpa_gui", - "Failed to remove network from wpa_supplicant\n" - "configuration."); - } else { - wpagui->triggerUpdate(); - wpagui->ctrlRequest("SAVE_CONFIG", reply, &reply_len); - } - - close(); -} - - -void NetworkConfig::newNetwork() -{ - new_network = true; - getEapCapa(); -} - - -void NetworkConfig::getEapCapa() -{ - char reply[256]; - size_t reply_len; - - if (wpagui == NULL) - return; - - reply_len = sizeof(reply) - 1; - if (wpagui->ctrlRequest("GET_CAPABILITY eap", reply, &reply_len) < 0) - return; - reply[reply_len] = '\0'; - - QString res(reply); - QStringList types = QStringList::split(QChar(' '), res); - eapSelect->insertStringList(types); -} diff -Nru wpa-1.0/wpa_supplicant/wpa_gui/scanresults.ui wpa-2.1/wpa_supplicant/wpa_gui/scanresults.ui --- wpa-1.0/wpa_supplicant/wpa_gui/scanresults.ui 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/wpa_gui/scanresults.ui 1970-01-01 00:00:00.000000000 +0000 @@ -1,179 +0,0 @@ - -ScanResults - - - ScanResults - - - - 0 - 0 - 452 - 225 - - - - Scan results - - - - unnamed - - - - - SSID - - - true - - - true - - - - - BSSID - - - true - - - true - - - - - frequency - - - true - - - true - - - - - signal - - - true - - - true - - - - - flags - - - true - - - true - - - - scanResultsView - - - StyledPanel - - - Sunken - - - - - layout24 - - - - unnamed - - - - spacer6 - - - Horizontal - - - Expanding - - - - 50 - 20 - - - - - - scanButton - - - Scan - - - - - closeButton - - - Close - - - - - - - - - closeButton - clicked() - ScanResults - close() - - - scanButton - clicked() - ScanResults - scanRequest() - - - scanResultsView - doubleClicked(QListViewItem*) - ScanResults - bssSelected(QListViewItem*) - - - - common/wpa_ctrl.h - wpagui.h - networkconfig.h - scanresults.ui.h - - - class WpaGui; - - - WpaGui *wpagui; - QTimer *timer; - - - setWpaGui( WpaGui * _wpagui ) - updateResults() - scanRequest() - getResults() - bssSelected( QListViewItem * sel ) - - - init() - destroy() - - - - diff -Nru wpa-1.0/wpa_supplicant/wpa_gui/scanresults.ui.h wpa-2.1/wpa_supplicant/wpa_gui/scanresults.ui.h --- wpa-1.0/wpa_supplicant/wpa_gui/scanresults.ui.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/wpa_gui/scanresults.ui.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,101 +0,0 @@ -/**************************************************************************** -** ui.h extension file, included from the uic-generated form implementation. -** -** If you want to add, delete, or rename functions or slots, use -** Qt Designer to update this file, preserving your code. -** -** You should not define a constructor or destructor in this file. -** Instead, write your code in functions called init() and destroy(). -** These will automatically be called by the form's constructor and -** destructor. -*****************************************************************************/ - -void ScanResults::init() -{ - wpagui = NULL; -} - - -void ScanResults::destroy() -{ - delete timer; -} - - -void ScanResults::setWpaGui(WpaGui *_wpagui) -{ - wpagui = _wpagui; - updateResults(); - - timer = new QTimer(this); - connect(timer, SIGNAL(timeout()), SLOT(getResults())); - timer->start(10000, FALSE); -} - - -void ScanResults::updateResults() -{ - char reply[8192]; - size_t reply_len; - - if (wpagui == NULL) - return; - - reply_len = sizeof(reply) - 1; - if (wpagui->ctrlRequest("SCAN_RESULTS", reply, &reply_len) < 0) - return; - reply[reply_len] = '\0'; - - scanResultsView->clear(); - - QString res(reply); - QStringList lines = QStringList::split(QChar('\n'), res); - bool first = true; - for (QStringList::Iterator it = lines.begin(); it != lines.end(); it++) { - if (first) { - first = false; - continue; - } - - QStringList cols = QStringList::split(QChar('\t'), *it, true); - QString ssid, bssid, freq, signal, flags; - bssid = cols.count() > 0 ? cols[0] : ""; - freq = cols.count() > 1 ? cols[1] : ""; - signal = cols.count() > 2 ? cols[2] : ""; - flags = cols.count() > 3 ? cols[3] : ""; - ssid = cols.count() > 4 ? cols[4] : ""; - new Q3ListViewItem(scanResultsView, ssid, bssid, freq, signal, flags); - } -} - - -void ScanResults::scanRequest() -{ - char reply[10]; - size_t reply_len = sizeof(reply); - - if (wpagui == NULL) - return; - - wpagui->ctrlRequest("SCAN", reply, &reply_len); -} - - -void ScanResults::getResults() -{ - updateResults(); -} - - - - -void ScanResults::bssSelected( Q3ListViewItem * sel ) -{ - NetworkConfig *nc = new NetworkConfig(); - if (nc == NULL) - return; - nc->setWpaGui(wpagui); - nc->paramsFromScanResults(sel); - nc->show(); - nc->exec(); -} diff -Nru wpa-1.0/wpa_supplicant/wpa_gui/setup-mingw-cross-compiling wpa-2.1/wpa_supplicant/wpa_gui/setup-mingw-cross-compiling --- wpa-1.0/wpa_supplicant/wpa_gui/setup-mingw-cross-compiling 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/wpa_gui/setup-mingw-cross-compiling 1970-01-01 00:00:00.000000000 +0000 @@ -1,11 +0,0 @@ -#!/bin/sh - -# qmake seems to be forcing include and lib paths from the original build -# and I have no idea how to change these. For now, just override the -# directories in the Makefile.Release file after qmake run. - -qmake -spec /q/jm/qt4-win/4.0.0/mkspecs/win32-g++ wpa_gui.pro -o Makefile -cat Makefile.Release | - sed s%qt4/lib%qt4-win/4.0.0/lib%g | - sed s%qt4/include%qt4-win/4.0.0/include%g > tmp.Makefile.Release && -mv -f tmp.Makefile.Release Makefile.Release diff -Nru wpa-1.0/wpa_supplicant/wpa_gui/userdatarequest.ui wpa-2.1/wpa_supplicant/wpa_gui/userdatarequest.ui --- wpa-1.0/wpa_supplicant/wpa_gui/userdatarequest.ui 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/wpa_gui/userdatarequest.ui 1970-01-01 00:00:00.000000000 +0000 @@ -1,163 +0,0 @@ - -UserDataRequest - - - UserDataRequest - - - - 0 - 0 - 216 - 103 - - - - Authentication credentials required - - - true - - - - unnamed - - - - queryInfo - - - - - - - - layout28 - - - - unnamed - - - - queryField - - - - - - - - queryEdit - - - true - - - Password - - - - - - - layout27 - - - - unnamed - - - - spacer4 - - - Horizontal - - - Expanding - - - - 20 - 20 - - - - - - buttonOk - - - &OK - - - - - - true - - - true - - - - - buttonCancel - - - &Cancel - - - - - - true - - - - - - - - - buttonOk - clicked() - UserDataRequest - sendReply() - - - buttonCancel - clicked() - UserDataRequest - reject() - - - queryEdit - returnPressed() - UserDataRequest - sendReply() - - - - common/wpa_ctrl.h - wpagui.h - userdatarequest.ui.h - - - class WpaGui; - - - WpaGui *wpagui; - int networkid; - QString field; - - - sendReply() - - - setParams( WpaGui * _wpagui, const char * reqMsg ) - - - - diff -Nru wpa-1.0/wpa_supplicant/wpa_gui/userdatarequest.ui.h wpa-2.1/wpa_supplicant/wpa_gui/userdatarequest.ui.h --- wpa-1.0/wpa_supplicant/wpa_gui/userdatarequest.ui.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/wpa_gui/userdatarequest.ui.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,72 +0,0 @@ -/**************************************************************************** -** ui.h extension file, included from the uic-generated form implementation. -** -** If you want to add, delete, or rename functions or slots, use -** Qt Designer to update this file, preserving your code. -** -** You should not define a constructor or destructor in this file. -** Instead, write your code in functions called init() and destroy(). -** These will automatically be called by the form's constructor and -** destructor. -*****************************************************************************/ - -#include - -int UserDataRequest::setParams(WpaGui *_wpagui, const char *reqMsg) -{ - char *tmp, *pos, *pos2; - wpagui = _wpagui; - tmp = strdup(reqMsg); - if (tmp == NULL) - return -1; - pos = strchr(tmp, '-'); - if (pos == NULL) { - free(tmp); - return -1; - } - *pos++ = '\0'; - field = tmp; - pos2 = strchr(pos, ':'); - if (pos2 == NULL) { - free(tmp); - return -1; - } - *pos2++ = '\0'; - - networkid = atoi(pos); - queryInfo->setText(pos2); - if (strcmp(tmp, "PASSWORD") == 0) { - queryField->setText("Password: "); - queryEdit->setEchoMode(QLineEdit::Password); - } else if (strcmp(tmp, "NEW_PASSWORD") == 0) { - queryField->setText("New password: "); - queryEdit->setEchoMode(QLineEdit::Password); - } else if (strcmp(tmp, "IDENTITY") == 0) - queryField->setText("Identity: "); - else if (strcmp(tmp, "PASSPHRASE") == 0) { - queryField->setText("Private key passphrase: "); - queryEdit->setEchoMode(QLineEdit::Password); - } else - queryField->setText(field + ":"); - free(tmp); - - return 0; -} - - -void UserDataRequest::sendReply() -{ - char reply[10]; - size_t reply_len = sizeof(reply); - - if (wpagui == NULL) { - reject(); - return; - } - - QString cmd = QString(WPA_CTRL_RSP) + field + '-' + - QString::number(networkid) + ':' + - queryEdit->text(); - wpagui->ctrlRequest(cmd.ascii(), reply, &reply_len); - accept(); -} diff -Nru wpa-1.0/wpa_supplicant/wpa_gui/wpa_gui.pro wpa-2.1/wpa_supplicant/wpa_gui/wpa_gui.pro --- wpa-1.0/wpa_supplicant/wpa_gui/wpa_gui.pro 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/wpa_gui/wpa_gui.pro 1970-01-01 00:00:00.000000000 +0000 @@ -1,50 +0,0 @@ -TEMPLATE = app -LANGUAGE = C++ - -CONFIG += qt warn_on release - -DEFINES += CONFIG_CTRL_IFACE - -win32 { - LIBS += -lws2_32 -static - DEFINES += CONFIG_NATIVE_WINDOWS CONFIG_CTRL_IFACE_NAMED_PIPE - SOURCES += ../../src/utils/os_win32.c -} else:win32-g++ { - # cross compilation to win32 - LIBS += -lws2_32 -static - DEFINES += CONFIG_NATIVE_WINDOWS CONFIG_CTRL_IFACE_NAMED_PIPE - SOURCES += ../../src/utils/os_win32.c -} else { - DEFINES += CONFIG_CTRL_IFACE_UNIX - SOURCES += ../../src/utils/os_unix.c -} - -INCLUDEPATH += . .. ../../src ../../src/utils - -HEADERS += wpamsg.h - -SOURCES += main.cpp \ - ../../src/common/wpa_ctrl.c - -FORMS = wpagui.ui \ - eventhistory.ui \ - scanresults.ui \ - userdatarequest.ui \ - networkconfig.ui - - -unix { - UI_DIR = .ui - MOC_DIR = .moc - OBJECTS_DIR = .obj -} - -qtver = $$[QT_VERSION] -isEmpty( qtver ) { - message(Compiling for Qt 3.x) - DEFINES += Q3ListViewItem=QListViewItem -} else { - message(Compiling for Qt $$qtver) - QT += qt3support - CONFIG += uic3 -} diff -Nru wpa-1.0/wpa_supplicant/wpa_gui/wpagui.ui wpa-2.1/wpa_supplicant/wpa_gui/wpagui.ui --- wpa-1.0/wpa_supplicant/wpa_gui/wpagui.ui 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/wpa_gui/wpagui.ui 1970-01-01 00:00:00.000000000 +0000 @@ -1,471 +0,0 @@ - -WpaGui - - - WpaGui - - - - 0 - 0 - 279 - 308 - - - - wpa_gui - - - - unnamed - - - - textLabel16 - - - Adapter: - - - - - adapterSelect - - - - - textLabel8 - - - Network: - - - - - networkSelect - - - - - frame3 - - - StyledPanel - - - Raised - - - - unnamed - - - - textLabel1 - - - Status: - - - - - textLabel2 - - - Last message: - - - - - textLabel3 - - - Authentication: - - - - - textLabel4 - - - Encryption: - - - - - textLabel5 - - - SSID: - - - - - textLabel6 - - - BSSID: - - - - - textLabel7 - - - IP address: - - - - - textStatus - - - - - - - - textLastMessage - - - - - - - - textAuthentication - - - - - - - - textEncryption - - - - - - - - textSsid - - - - - - - - textBssid - - - - - - - - textIpAddress - - - - - - - - - - spacer7 - - - Horizontal - - - Expanding - - - - 16 - 16 - - - - - - connectButton - - - Connect - - - - - disconnectButton - - - Disconnect - - - - - scanButton - - - Scan - - - - - - - MenuBar - - - - - - - - - - - - - - - - - - - - - - fileExitAction - - - Exit - - - E&xit - - - Ctrl+Q - - - - - helpContentsAction - - - false - - - Contents - - - &Contents... - - - - - - - - helpIndexAction - - - false - - - Index - - - &Index... - - - - - - - - helpAboutAction - - - About - - - &About - - - - - - - - fileEventHistoryAction - - - Event History - - - Event &History - - - - - fileAdd_NetworkAction - - - Add Network - - - &Add Network - - - - - fileEdit_networkAction - - - Edit Network - - - &Edit Network - - - - - - helpIndexAction - activated() - WpaGui - helpIndex() - - - helpContentsAction - activated() - WpaGui - helpContents() - - - helpAboutAction - activated() - WpaGui - helpAbout() - - - fileExitAction - activated() - WpaGui - close() - - - disconnectButton - clicked() - WpaGui - disconnect() - - - scanButton - clicked() - WpaGui - scan() - - - connectButton - clicked() - WpaGui - connectB() - - - fileEventHistoryAction - activated() - WpaGui - eventHistory() - - - networkSelect - activated(const QString&) - WpaGui - selectNetwork(const QString&) - - - fileEdit_networkAction - activated() - WpaGui - editNetwork() - - - fileAdd_NetworkAction - activated() - WpaGui - addNetwork() - - - adapterSelect - activated(const QString&) - WpaGui - selectAdapter(const QString&) - - - - qtimer.h - qsocketnotifier.h - wpamsg.h - eventhistory.h - scanresults.h - common/wpa_ctrl.h - dirent.h - qmessagebox.h - qapplication.h - userdatarequest.h - networkconfig.h - wpagui.ui.h - - - class UserDataRequest; - - - ScanResults *scanres; - bool networkMayHaveChanged; - char *ctrl_iface; - EventHistory *eh; - struct wpa_ctrl *ctrl_conn; - QSocketNotifier *msgNotifier; - QTimer *timer; - int pingsToStatusUpdate; - WpaMsgList msgs; - char *ctrl_iface_dir; - struct wpa_ctrl *monitor_conn; - UserDataRequest *udr; - - - parse_argv() - updateStatus() - updateNetworks() - helpIndex() - helpContents() - helpAbout() - disconnect() - scan() - eventHistory() - ping() - processMsg( char * msg ) - processCtrlReq( const char * req ) - receiveMsgs() - connectB() - selectNetwork( const QString & sel ) - editNetwork() - addNetwork() - selectAdapter( const QString & sel ) - - - init() - destroy() - openCtrlConnection( const char * ifname ) - ctrlRequest( const char * cmd, char * buf, size_t * buflen ) - triggerUpdate() - - - - diff -Nru wpa-1.0/wpa_supplicant/wpa_gui/wpagui.ui.h wpa-2.1/wpa_supplicant/wpa_gui/wpagui.ui.h --- wpa-1.0/wpa_supplicant/wpa_gui/wpagui.ui.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/wpa_gui/wpagui.ui.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,730 +0,0 @@ -/**************************************************************************** -** ui.h extension file, included from the uic-generated form implementation. -** -** If you want to add, delete, or rename functions or slots, use -** Qt Designer to update this file, preserving your code. -** -** You should not define a constructor or destructor in this file. -** Instead, write your code in functions called init() and destroy(). -** These will automatically be called by the form's constructor and -** destructor. -*****************************************************************************/ - - -#ifdef __MINGW32__ -/* Need to get getopt() */ -#include -#endif - -#include - -void WpaGui::init() -{ - eh = NULL; - scanres = NULL; - udr = NULL; - ctrl_iface = NULL; - ctrl_conn = NULL; - monitor_conn = NULL; - msgNotifier = NULL; - ctrl_iface_dir = strdup("/var/run/wpa_supplicant"); - - parse_argv(); - - textStatus->setText("connecting to wpa_supplicant"); - timer = new QTimer(this); - connect(timer, SIGNAL(timeout()), SLOT(ping())); - timer->start(1000, FALSE); - - if (openCtrlConnection(ctrl_iface) < 0) { - printf("Failed to open control connection to wpa_supplicant.\n"); - } - - updateStatus(); - networkMayHaveChanged = true; - updateNetworks(); -} - - -void WpaGui::destroy() -{ - delete msgNotifier; - - if (monitor_conn) { - wpa_ctrl_detach(monitor_conn); - wpa_ctrl_close(monitor_conn); - monitor_conn = NULL; - } - if (ctrl_conn) { - wpa_ctrl_close(ctrl_conn); - ctrl_conn = NULL; - } - - if (eh) { - eh->close(); - delete eh; - eh = NULL; - } - - if (scanres) { - scanres->close(); - delete scanres; - scanres = NULL; - } - - if (udr) { - udr->close(); - delete udr; - udr = NULL; - } - - free(ctrl_iface); - ctrl_iface = NULL; - - free(ctrl_iface_dir); - ctrl_iface_dir = NULL; -} - - -void WpaGui::parse_argv() -{ - int c; - for (;;) { - c = getopt(qApp->argc(), qApp->argv(), "i:p:"); - if (c < 0) - break; - switch (c) { - case 'i': - free(ctrl_iface); - ctrl_iface = strdup(optarg); - break; - case 'p': - free(ctrl_iface_dir); - ctrl_iface_dir = strdup(optarg); - break; - } - } -} - - -int WpaGui::openCtrlConnection(const char *ifname) -{ - char *cfile; - int flen; - char buf[2048], *pos, *pos2; - size_t len; - - if (ifname) { - if (ifname != ctrl_iface) { - free(ctrl_iface); - ctrl_iface = strdup(ifname); - } - } else { -#ifdef CONFIG_CTRL_IFACE_UDP - free(ctrl_iface); - ctrl_iface = strdup("udp"); -#endif /* CONFIG_CTRL_IFACE_UDP */ -#ifdef CONFIG_CTRL_IFACE_UNIX - struct dirent *dent; - DIR *dir = opendir(ctrl_iface_dir); - free(ctrl_iface); - ctrl_iface = NULL; - if (dir) { - while ((dent = readdir(dir))) { -#ifdef _DIRENT_HAVE_D_TYPE - /* Skip the file if it is not a socket. - * Also accept DT_UNKNOWN (0) in case - * the C library or underlying file - * system does not support d_type. */ - if (dent->d_type != DT_SOCK && - dent->d_type != DT_UNKNOWN) - continue; -#endif /* _DIRENT_HAVE_D_TYPE */ - - if (strcmp(dent->d_name, ".") == 0 || - strcmp(dent->d_name, "..") == 0) - continue; - printf("Selected interface '%s'\n", dent->d_name); - ctrl_iface = strdup(dent->d_name); - break; - } - closedir(dir); - } -#endif /* CONFIG_CTRL_IFACE_UNIX */ -#ifdef CONFIG_CTRL_IFACE_NAMED_PIPE - struct wpa_ctrl *ctrl; - int ret; - - free(ctrl_iface); - ctrl_iface = NULL; - - ctrl = wpa_ctrl_open(NULL); - if (ctrl) { - len = sizeof(buf) - 1; - ret = wpa_ctrl_request(ctrl, "INTERFACES", 10, buf, &len, NULL); - if (ret >= 0) { - buf[len] = '\0'; - pos = strchr(buf, '\n'); - if (pos) - *pos = '\0'; - ctrl_iface = strdup(buf); - } - wpa_ctrl_close(ctrl); - } -#endif /* CONFIG_CTRL_IFACE_NAMED_PIPE */ - } - - if (ctrl_iface == NULL) - return -1; - -#ifdef CONFIG_CTRL_IFACE_UNIX - flen = strlen(ctrl_iface_dir) + strlen(ctrl_iface) + 2; - cfile = (char *) malloc(flen); - if (cfile == NULL) - return -1; - snprintf(cfile, flen, "%s/%s", ctrl_iface_dir, ctrl_iface); -#else /* CONFIG_CTRL_IFACE_UNIX */ - flen = strlen(ctrl_iface) + 1; - cfile = (char *) malloc(flen); - if (cfile == NULL) - return -1; - snprintf(cfile, flen, "%s", ctrl_iface); -#endif /* CONFIG_CTRL_IFACE_UNIX */ - - if (ctrl_conn) { - wpa_ctrl_close(ctrl_conn); - ctrl_conn = NULL; - } - - if (monitor_conn) { - delete msgNotifier; - msgNotifier = NULL; - wpa_ctrl_detach(monitor_conn); - wpa_ctrl_close(monitor_conn); - monitor_conn = NULL; - } - - printf("Trying to connect to '%s'\n", cfile); - ctrl_conn = wpa_ctrl_open(cfile); - if (ctrl_conn == NULL) { - free(cfile); - return -1; - } - monitor_conn = wpa_ctrl_open(cfile); - free(cfile); - if (monitor_conn == NULL) { - wpa_ctrl_close(ctrl_conn); - return -1; - } - if (wpa_ctrl_attach(monitor_conn)) { - printf("Failed to attach to wpa_supplicant\n"); - wpa_ctrl_close(monitor_conn); - monitor_conn = NULL; - wpa_ctrl_close(ctrl_conn); - ctrl_conn = NULL; - return -1; - } - -#if defined(CONFIG_CTRL_IFACE_UNIX) || defined(CONFIG_CTRL_IFACE_UDP) - msgNotifier = new QSocketNotifier(wpa_ctrl_get_fd(monitor_conn), - QSocketNotifier::Read, this); - connect(msgNotifier, SIGNAL(activated(int)), SLOT(receiveMsgs())); -#endif - - adapterSelect->clear(); - adapterSelect->insertItem(ctrl_iface); - adapterSelect->setCurrentItem(0); - - len = sizeof(buf) - 1; - if (wpa_ctrl_request(ctrl_conn, "INTERFACES", 10, buf, &len, NULL) >= 0) { - buf[len] = '\0'; - pos = buf; - while (*pos) { - pos2 = strchr(pos, '\n'); - if (pos2) - *pos2 = '\0'; - if (strcmp(pos, ctrl_iface) != 0) - adapterSelect->insertItem(pos); - if (pos2) - pos = pos2 + 1; - else - break; - } - } - - return 0; -} - - -static void wpa_gui_msg_cb(char *msg, size_t) -{ - /* This should not happen anymore since two control connections are used. */ - printf("missed message: %s\n", msg); -} - - -int WpaGui::ctrlRequest(const char *cmd, char *buf, size_t *buflen) -{ - int ret; - - if (ctrl_conn == NULL) - return -3; - ret = wpa_ctrl_request(ctrl_conn, cmd, strlen(cmd), buf, buflen, - wpa_gui_msg_cb); - if (ret == -2) { - printf("'%s' command timed out.\n", cmd); - } else if (ret < 0) { - printf("'%s' command failed.\n", cmd); - } - - return ret; -} - - -void WpaGui::updateStatus() -{ - char buf[2048], *start, *end, *pos; - size_t len; - - pingsToStatusUpdate = 10; - - len = sizeof(buf) - 1; - if (ctrl_conn == NULL || ctrlRequest("STATUS", buf, &len) < 0) { - textStatus->setText("Could not get status from wpa_supplicant"); - textAuthentication->clear(); - textEncryption->clear(); - textSsid->clear(); - textBssid->clear(); - textIpAddress->clear(); - return; - } - - buf[len] = '\0'; - - bool auth_updated = false, ssid_updated = false; - bool bssid_updated = false, ipaddr_updated = false; - bool status_updated = false; - char *pairwise_cipher = NULL, *group_cipher = NULL; - - start = buf; - while (*start) { - bool last = false; - end = strchr(start, '\n'); - if (end == NULL) { - last = true; - end = start; - while (end[0] && end[1]) - end++; - } - *end = '\0'; - - pos = strchr(start, '='); - if (pos) { - *pos++ = '\0'; - if (strcmp(start, "bssid") == 0) { - bssid_updated = true; - textBssid->setText(pos); - } else if (strcmp(start, "ssid") == 0) { - ssid_updated = true; - textSsid->setText(pos); - } else if (strcmp(start, "ip_address") == 0) { - ipaddr_updated = true; - textIpAddress->setText(pos); - } else if (strcmp(start, "wpa_state") == 0) { - status_updated = true; - textStatus->setText(pos); - } else if (strcmp(start, "key_mgmt") == 0) { - auth_updated = true; - textAuthentication->setText(pos); - /* TODO: could add EAP status to this */ - } else if (strcmp(start, "pairwise_cipher") == 0) { - pairwise_cipher = pos; - } else if (strcmp(start, "group_cipher") == 0) { - group_cipher = pos; - } - } - - if (last) - break; - start = end + 1; - } - - if (pairwise_cipher || group_cipher) { - QString encr; - if (pairwise_cipher && group_cipher && - strcmp(pairwise_cipher, group_cipher) != 0) { - encr.append(pairwise_cipher); - encr.append(" + "); - encr.append(group_cipher); - } else if (pairwise_cipher) { - encr.append(pairwise_cipher); - } else { - encr.append(group_cipher); - encr.append(" [group key only]"); - } - textEncryption->setText(encr); - } else - textEncryption->clear(); - - if (!status_updated) - textStatus->clear(); - if (!auth_updated) - textAuthentication->clear(); - if (!ssid_updated) - textSsid->clear(); - if (!bssid_updated) - textBssid->clear(); - if (!ipaddr_updated) - textIpAddress->clear(); -} - - -void WpaGui::updateNetworks() -{ - char buf[2048], *start, *end, *id, *ssid, *bssid, *flags; - size_t len; - int first_active = -1; - bool selected = false; - - if (!networkMayHaveChanged) - return; - - networkSelect->clear(); - - if (ctrl_conn == NULL) - return; - - len = sizeof(buf) - 1; - if (ctrlRequest("LIST_NETWORKS", buf, &len) < 0) - return; - - buf[len] = '\0'; - start = strchr(buf, '\n'); - if (start == NULL) - return; - start++; - - while (*start) { - bool last = false; - end = strchr(start, '\n'); - if (end == NULL) { - last = true; - end = start; - while (end[0] && end[1]) - end++; - } - *end = '\0'; - - id = start; - ssid = strchr(id, '\t'); - if (ssid == NULL) - break; - *ssid++ = '\0'; - bssid = strchr(ssid, '\t'); - if (bssid == NULL) - break; - *bssid++ = '\0'; - flags = strchr(bssid, '\t'); - if (flags == NULL) - break; - *flags++ = '\0'; - - QString network(id); - network.append(": "); - network.append(ssid); - networkSelect->insertItem(network); - - if (strstr(flags, "[CURRENT]")) { - networkSelect->setCurrentItem(networkSelect->count() - 1); - selected = true; - } else if (first_active < 0 && strstr(flags, "[DISABLED]") == NULL) - first_active = networkSelect->count() - 1; - - if (last) - break; - start = end + 1; - } - - if (!selected && first_active >= 0) - networkSelect->setCurrentItem(first_active); - - networkMayHaveChanged = false; -} - - -void WpaGui::helpIndex() -{ - printf("helpIndex\n"); -} - - -void WpaGui::helpContents() -{ - printf("helpContents\n"); -} - - -void WpaGui::helpAbout() -{ - QMessageBox::about(this, "wpa_gui for wpa_supplicant", - "Copyright (c) 2003-2008,\n" - "Jouni Malinen \n" - "and contributors.\n" - "\n" - "This program is free software. You can\n" - "distribute it and/or modify it under the terms of\n" - "the GNU General Public License version 2.\n" - "\n" - "Alternatively, this software may be distributed\n" - "under the terms of the BSD license.\n" - "\n" - "This product includes software developed\n" - "by the OpenSSL Project for use in the\n" - "OpenSSL Toolkit (http://www.openssl.org/)\n"); -} - - -void WpaGui::disconnect() -{ - char reply[10]; - size_t reply_len = sizeof(reply); - ctrlRequest("DISCONNECT", reply, &reply_len); -} - - -void WpaGui::scan() -{ - if (scanres) { - scanres->close(); - delete scanres; - } - - scanres = new ScanResults(); - if (scanres == NULL) - return; - scanres->setWpaGui(this); - scanres->show(); - scanres->exec(); -} - - -void WpaGui::eventHistory() -{ - if (eh) { - eh->close(); - delete eh; - } - - eh = new EventHistory(); - if (eh == NULL) - return; - eh->addEvents(msgs); - eh->show(); - eh->exec(); -} - - -void WpaGui::ping() -{ - char buf[10]; - size_t len; - -#ifdef CONFIG_CTRL_IFACE_NAMED_PIPE - /* - * QSocketNotifier cannot be used with Windows named pipes, so use a timer - * to check for received messages for now. This could be optimized be doing - * something specific to named pipes or Windows events, but it is not clear - * what would be the best way of doing that in Qt. - */ - receiveMsgs(); -#endif /* CONFIG_CTRL_IFACE_NAMED_PIPE */ - - if (scanres && !scanres->isVisible()) { - delete scanres; - scanres = NULL; - } - - if (eh && !eh->isVisible()) { - delete eh; - eh = NULL; - } - - if (udr && !udr->isVisible()) { - delete udr; - udr = NULL; - } - - len = sizeof(buf) - 1; - if (ctrlRequest("PING", buf, &len) < 0) { - printf("PING failed - trying to reconnect\n"); - if (openCtrlConnection(ctrl_iface) >= 0) { - printf("Reconnected successfully\n"); - pingsToStatusUpdate = 0; - } - } - - pingsToStatusUpdate--; - if (pingsToStatusUpdate <= 0) { - updateStatus(); - updateNetworks(); - } -} - - -static int str_match(const char *a, const char *b) -{ - return strncmp(a, b, strlen(b)) == 0; -} - - -void WpaGui::processMsg(char *msg) -{ - char *pos = msg, *pos2; - int priority = 2; - - if (*pos == '<') { - /* skip priority */ - pos++; - priority = atoi(pos); - pos = strchr(pos, '>'); - if (pos) - pos++; - else - pos = msg; - } - - WpaMsg wm(pos, priority); - if (eh) - eh->addEvent(wm); - msgs.append(wm); - while (msgs.count() > 100) - msgs.pop_front(); - - /* Update last message with truncated version of the event */ - if (strncmp(pos, "CTRL-", 5) == 0) { - pos2 = strchr(pos, str_match(pos, WPA_CTRL_REQ) ? ':' : ' '); - if (pos2) - pos2++; - else - pos2 = pos; - } else - pos2 = pos; - QString lastmsg = pos2; - lastmsg.truncate(40); - textLastMessage->setText(lastmsg); - - pingsToStatusUpdate = 0; - networkMayHaveChanged = true; - - if (str_match(pos, WPA_CTRL_REQ)) - processCtrlReq(pos + strlen(WPA_CTRL_REQ)); -} - - -void WpaGui::processCtrlReq(const char *req) -{ - if (udr) { - udr->close(); - delete udr; - } - udr = new UserDataRequest(); - if (udr == NULL) - return; - if (udr->setParams(this, req) < 0) { - delete udr; - udr = NULL; - return; - } - udr->show(); - udr->exec(); -} - - -void WpaGui::receiveMsgs() -{ - char buf[256]; - size_t len; - - while (monitor_conn && wpa_ctrl_pending(monitor_conn) > 0) { - len = sizeof(buf) - 1; - if (wpa_ctrl_recv(monitor_conn, buf, &len) == 0) { - buf[len] = '\0'; - processMsg(buf); - } - } -} - - -void WpaGui::connectB() -{ - char reply[10]; - size_t reply_len = sizeof(reply); - ctrlRequest("REASSOCIATE", reply, &reply_len); -} - - -void WpaGui::selectNetwork( const QString &sel ) -{ - QString cmd(sel); - char reply[10]; - size_t reply_len = sizeof(reply); - - int pos = cmd.find(':'); - if (pos < 0) { - printf("Invalid selectNetwork '%s'\n", cmd.ascii()); - return; - } - cmd.truncate(pos); - cmd.prepend("SELECT_NETWORK "); - ctrlRequest(cmd.ascii(), reply, &reply_len); -} - - -void WpaGui::editNetwork() -{ - QString sel(networkSelect->currentText()); - int pos = sel.find(':'); - if (pos < 0) { - printf("Invalid selectNetwork '%s'\n", sel.ascii()); - return; - } - sel.truncate(pos); - - NetworkConfig *nc = new NetworkConfig(); - if (nc == NULL) - return; - nc->setWpaGui(this); - - nc->paramsFromConfig(sel.toInt()); - nc->show(); - nc->exec(); -} - - -void WpaGui::triggerUpdate() -{ - updateStatus(); - networkMayHaveChanged = true; - updateNetworks(); -} - - -void WpaGui::addNetwork() -{ - NetworkConfig *nc = new NetworkConfig(); - if (nc == NULL) - return; - nc->setWpaGui(this); - nc->newNetwork(); - nc->show(); - nc->exec(); -} - - -void WpaGui::selectAdapter( const QString & sel ) -{ - if (openCtrlConnection(sel.ascii()) < 0) - printf("Failed to open control connection to wpa_supplicant.\n"); - updateStatus(); - updateNetworks(); -} diff -Nru wpa-1.0/wpa_supplicant/wpa_gui/wpamsg.h wpa-2.1/wpa_supplicant/wpa_gui/wpamsg.h --- wpa-1.0/wpa_supplicant/wpa_gui/wpamsg.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/wpa_gui/wpamsg.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,34 +0,0 @@ -#ifndef WPAMSG_H -#define WPAMSG_H - -class WpaMsg; - -#if QT_VERSION >= 0x040000 -#include -#include -typedef QLinkedList WpaMsgList; -#else -#include -typedef QValueList WpaMsgList; -#endif - -class WpaMsg { -public: - WpaMsg() {} - WpaMsg(const QString &_msg, int _priority = 2) - : msg(_msg), priority(_priority) - { - timestamp = QDateTime::currentDateTime(); - } - - QString getMsg() const { return msg; } - int getPriority() const { return priority; } - QDateTime getTimestamp() const { return timestamp; } - -private: - QString msg; - int priority; - QDateTime timestamp; -}; - -#endif /* WPAMSG_H */ diff -Nru wpa-1.0/wpa_supplicant/wpa_gui-qt4/addinterface.cpp wpa-2.1/wpa_supplicant/wpa_gui-qt4/addinterface.cpp --- wpa-1.0/wpa_supplicant/wpa_gui-qt4/addinterface.cpp 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/wpa_gui-qt4/addinterface.cpp 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * wpa_gui - AddInterface class * Copyright (c) 2008, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include diff -Nru wpa-1.0/wpa_supplicant/wpa_gui-qt4/addinterface.h wpa-2.1/wpa_supplicant/wpa_gui-qt4/addinterface.h --- wpa-1.0/wpa_supplicant/wpa_gui-qt4/addinterface.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/wpa_gui-qt4/addinterface.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * wpa_gui - AddInterface class * Copyright (c) 2008, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef ADDINTERFACE_H diff -Nru wpa-1.0/wpa_supplicant/wpa_gui-qt4/eventhistory.cpp wpa-2.1/wpa_supplicant/wpa_gui-qt4/eventhistory.cpp --- wpa-1.0/wpa_supplicant/wpa_gui-qt4/eventhistory.cpp 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/wpa_gui-qt4/eventhistory.cpp 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * wpa_gui - EventHistory class * Copyright (c) 2005-2006, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include diff -Nru wpa-1.0/wpa_supplicant/wpa_gui-qt4/eventhistory.h wpa-2.1/wpa_supplicant/wpa_gui-qt4/eventhistory.h --- wpa-1.0/wpa_supplicant/wpa_gui-qt4/eventhistory.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/wpa_gui-qt4/eventhistory.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * wpa_gui - EventHistory class * Copyright (c) 2005-2006, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef EVENTHISTORY_H diff -Nru wpa-1.0/wpa_supplicant/wpa_gui-qt4/main.cpp wpa-2.1/wpa_supplicant/wpa_gui-qt4/main.cpp --- wpa-1.0/wpa_supplicant/wpa_gui-qt4/main.cpp 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/wpa_gui-qt4/main.cpp 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * wpa_gui - Application startup * Copyright (c) 2005-2006, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifdef CONFIG_NATIVE_WINDOWS diff -Nru wpa-1.0/wpa_supplicant/wpa_gui-qt4/networkconfig.cpp wpa-2.1/wpa_supplicant/wpa_gui-qt4/networkconfig.cpp --- wpa-1.0/wpa_supplicant/wpa_gui-qt4/networkconfig.cpp 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/wpa_gui-qt4/networkconfig.cpp 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * wpa_gui - NetworkConfig class * Copyright (c) 2005-2006, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include diff -Nru wpa-1.0/wpa_supplicant/wpa_gui-qt4/networkconfig.h wpa-2.1/wpa_supplicant/wpa_gui-qt4/networkconfig.h --- wpa-1.0/wpa_supplicant/wpa_gui-qt4/networkconfig.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/wpa_gui-qt4/networkconfig.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * wpa_gui - NetworkConfig class * Copyright (c) 2005-2006, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef NETWORKCONFIG_H diff -Nru wpa-1.0/wpa_supplicant/wpa_gui-qt4/peers.cpp wpa-2.1/wpa_supplicant/wpa_gui-qt4/peers.cpp --- wpa-1.0/wpa_supplicant/wpa_gui-qt4/peers.cpp 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/wpa_gui-qt4/peers.cpp 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * wpa_gui - Peers class * Copyright (c) 2009-2010, Atheros Communications * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include diff -Nru wpa-1.0/wpa_supplicant/wpa_gui-qt4/peers.h wpa-2.1/wpa_supplicant/wpa_gui-qt4/peers.h --- wpa-1.0/wpa_supplicant/wpa_gui-qt4/peers.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/wpa_gui-qt4/peers.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * wpa_gui - Peers class * Copyright (c) 2009-2010, Atheros Communications * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef PEERS_H diff -Nru wpa-1.0/wpa_supplicant/wpa_gui-qt4/scanresults.cpp wpa-2.1/wpa_supplicant/wpa_gui-qt4/scanresults.cpp --- wpa-1.0/wpa_supplicant/wpa_gui-qt4/scanresults.cpp 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/wpa_gui-qt4/scanresults.cpp 2014-02-04 11:23:35.000000000 +0000 @@ -2,19 +2,14 @@ * wpa_gui - ScanResults class * Copyright (c) 2005-2006, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include #include "scanresults.h" +#include "signalbar.h" #include "wpagui.h" #include "networkconfig.h" @@ -33,6 +28,7 @@ wpagui = NULL; scanResultsWidget->setItemsExpandable(FALSE); scanResultsWidget->setRootIsDecorated(FALSE); + scanResultsWidget->setItemDelegate(new SignalBar(scanResultsWidget)); } @@ -91,7 +87,7 @@ bssid = (*it).mid(pos); else if ((*it).startsWith("freq=")) freq = (*it).mid(pos); - else if ((*it).startsWith("qual=")) + else if ((*it).startsWith("level=")) signal = (*it).mid(pos); else if ((*it).startsWith("flags=")) flags = (*it).mid(pos); diff -Nru wpa-1.0/wpa_supplicant/wpa_gui-qt4/scanresults.h wpa-2.1/wpa_supplicant/wpa_gui-qt4/scanresults.h --- wpa-1.0/wpa_supplicant/wpa_gui-qt4/scanresults.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/wpa_gui-qt4/scanresults.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * wpa_gui - ScanResults class * Copyright (c) 2005-2006, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef SCANRESULTS_H diff -Nru wpa-1.0/wpa_supplicant/wpa_gui-qt4/signalbar.cpp wpa-2.1/wpa_supplicant/wpa_gui-qt4/signalbar.cpp --- wpa-1.0/wpa_supplicant/wpa_gui-qt4/signalbar.cpp 1970-01-01 00:00:00.000000000 +0000 +++ wpa-2.1/wpa_supplicant/wpa_gui-qt4/signalbar.cpp 2014-02-04 11:23:35.000000000 +0000 @@ -0,0 +1,58 @@ +/* + * wpa_gui - SignalBar class + * Copyright (c) 2011, Kel Modderman + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include +#include + +#include "signalbar.h" + + +SignalBar::SignalBar(QObject *parent) + : QStyledItemDelegate(parent) +{ +} + + +SignalBar::~SignalBar() +{ +} + + +void SignalBar::paint(QPainter *painter, + const QStyleOptionViewItem &option, + const QModelIndex &index) const +{ + QStyleOptionProgressBar opts; + int signal; + + if (index.column() != 3) { + QStyledItemDelegate::paint(painter, option, index); + return; + } + + if (index.data().toInt() > 0) + signal = 0 - (256 - index.data().toInt()); + else + signal = index.data().toInt(); + + opts.minimum = -95; + opts.maximum = -35; + if (signal < opts.minimum) + opts.progress = opts.minimum; + else if (signal > opts.maximum) + opts.progress = opts.maximum; + else + opts.progress = signal; + + opts.text = QString::number(signal) + " dBm"; + opts.textVisible = true; + opts.rect = option.rect; + + QApplication::style()->drawControl(QStyle::CE_ProgressBar, + &opts, painter); +} diff -Nru wpa-1.0/wpa_supplicant/wpa_gui-qt4/signalbar.h wpa-2.1/wpa_supplicant/wpa_gui-qt4/signalbar.h --- wpa-1.0/wpa_supplicant/wpa_gui-qt4/signalbar.h 1970-01-01 00:00:00.000000000 +0000 +++ wpa-2.1/wpa_supplicant/wpa_gui-qt4/signalbar.h 2014-02-04 11:23:35.000000000 +0000 @@ -0,0 +1,28 @@ +/* + * wpa_gui - SignalBar class + * Copyright (c) 2011, Kel Modderman + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef SIGNALBAR_H +#define SIGNALBAR_H + +#include +#include + +class SignalBar : public QStyledItemDelegate +{ + Q_OBJECT + +public: + SignalBar(QObject *parent = 0); + ~SignalBar(); + + virtual void paint(QPainter *painter, + const QStyleOptionViewItem &option, + const QModelIndex &index) const ; +}; + +#endif /* SIGNALBAR_H */ diff -Nru wpa-1.0/wpa_supplicant/wpa_gui-qt4/stringquery.cpp wpa-2.1/wpa_supplicant/wpa_gui-qt4/stringquery.cpp --- wpa-1.0/wpa_supplicant/wpa_gui-qt4/stringquery.cpp 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/wpa_gui-qt4/stringquery.cpp 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * wpa_gui - StringQuery class * Copyright (c) 2009, Atheros Communications * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include diff -Nru wpa-1.0/wpa_supplicant/wpa_gui-qt4/stringquery.h wpa-2.1/wpa_supplicant/wpa_gui-qt4/stringquery.h --- wpa-1.0/wpa_supplicant/wpa_gui-qt4/stringquery.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/wpa_gui-qt4/stringquery.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * wpa_gui - StringQuery class * Copyright (c) 2009, Atheros Communications * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef STRINGQUERY_H diff -Nru wpa-1.0/wpa_supplicant/wpa_gui-qt4/userdatarequest.cpp wpa-2.1/wpa_supplicant/wpa_gui-qt4/userdatarequest.cpp --- wpa-1.0/wpa_supplicant/wpa_gui-qt4/userdatarequest.cpp 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/wpa_gui-qt4/userdatarequest.cpp 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * wpa_gui - UserDataRequest class * Copyright (c) 2005-2006, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "userdatarequest.h" diff -Nru wpa-1.0/wpa_supplicant/wpa_gui-qt4/userdatarequest.h wpa-2.1/wpa_supplicant/wpa_gui-qt4/userdatarequest.h --- wpa-1.0/wpa_supplicant/wpa_gui-qt4/userdatarequest.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/wpa_gui-qt4/userdatarequest.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * wpa_gui - UserDataRequest class * Copyright (c) 2005-2006, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef USERDATAREQUEST_H diff -Nru wpa-1.0/wpa_supplicant/wpa_gui-qt4/wpagui.cpp wpa-2.1/wpa_supplicant/wpa_gui-qt4/wpagui.cpp --- wpa-1.0/wpa_supplicant/wpa_gui-qt4/wpagui.cpp 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/wpa_gui-qt4/wpagui.cpp 2014-02-04 11:23:35.000000000 +0000 @@ -2,26 +2,16 @@ * wpa_gui - WpaGui class * Copyright (c) 2005-2011, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ -#ifdef __MINGW32__ -/* Need to get getopt() */ -#include -#endif - #ifdef CONFIG_NATIVE_WINDOWS #include #endif /* CONFIG_NATIVE_WINDOWS */ #include +#include #include #include #include @@ -719,17 +709,13 @@ void WpaGui::helpAbout() { QMessageBox::about(this, "wpa_gui for wpa_supplicant", - "Copyright (c) 2003-2011,\n" + "Copyright (c) 2003-2013,\n" "Jouni Malinen \n" "and contributors.\n" "\n" - "This program is free software. You can\n" - "distribute it and/or modify it under the terms " - "of\n" - "the GNU General Public License version 2.\n" - "\n" - "Alternatively, this software may be distributed\n" - "under the terms of the BSD license.\n" + "This software may be distributed under\n" + "the terms of the BSD license.\n" + "See README for more details.\n" "\n" "This product includes software developed\n" "by the OpenSSL Project for use in the\n" diff -Nru wpa-1.0/wpa_supplicant/wpa_gui-qt4/wpagui.h wpa-2.1/wpa_supplicant/wpa_gui-qt4/wpagui.h --- wpa-1.0/wpa_supplicant/wpa_gui-qt4/wpagui.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/wpa_gui-qt4/wpagui.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * wpa_gui - WpaGui class * Copyright (c) 2005-2006, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef WPAGUI_H diff -Nru wpa-1.0/wpa_supplicant/wpa_gui-qt4/wpa_gui.pro wpa-2.1/wpa_supplicant/wpa_gui-qt4/wpa_gui.pro --- wpa-1.0/wpa_supplicant/wpa_gui-qt4/wpa_gui.pro 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/wpa_gui-qt4/wpa_gui.pro 2014-02-04 11:23:35.000000000 +0000 @@ -34,6 +34,7 @@ wpagui.h \ eventhistory.h \ scanresults.h \ + signalbar.h \ userdatarequest.h \ networkconfig.h \ addinterface.h \ @@ -44,6 +45,7 @@ wpagui.cpp \ eventhistory.cpp \ scanresults.cpp \ + signalbar.cpp \ userdatarequest.cpp \ networkconfig.cpp \ addinterface.cpp \ diff -Nru wpa-1.0/wpa_supplicant/wpa_gui-qt4/wpamsg.h wpa-2.1/wpa_supplicant/wpa_gui-qt4/wpamsg.h --- wpa-1.0/wpa_supplicant/wpa_gui-qt4/wpamsg.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/wpa_gui-qt4/wpamsg.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * wpa_gui - WpaMsg class for storing event messages * Copyright (c) 2005-2006, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef WPAMSG_H diff -Nru wpa-1.0/wpa_supplicant/wpa_passphrase.c wpa-2.1/wpa_supplicant/wpa_passphrase.c --- wpa-1.0/wpa_supplicant/wpa_passphrase.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/wpa_passphrase.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * WPA Supplicant - ASCII passphrase to WPA PSK tool * Copyright (c) 2003-2005, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -58,7 +52,7 @@ return 1; } - pbkdf2_sha1(passphrase, ssid, os_strlen(ssid), 4096, psk, 32); + pbkdf2_sha1(passphrase, (u8 *) ssid, os_strlen(ssid), 4096, psk, 32); printf("network={\n"); printf("\tssid=\"%s\"\n", ssid); diff -Nru wpa-1.0/wpa_supplicant/wpa_priv.c wpa-2.1/wpa_supplicant/wpa_priv.c --- wpa-1.0/wpa_supplicant/wpa_priv.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/wpa_priv.c 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * WPA Supplicant / privileged helper program * Copyright (c) 2007-2009, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -558,8 +552,6 @@ } -extern struct wpa_driver_ops *wpa_drivers[]; - static struct wpa_priv_interface * wpa_priv_interface_init(const char *dir, const char *params) { @@ -579,13 +571,11 @@ iface->fd = -1; len = pos - params; - iface->driver_name = os_malloc(len + 1); + iface->driver_name = dup_binstr(params, len); if (iface->driver_name == NULL) { wpa_priv_interface_deinit(iface); return NULL; } - os_memcpy(iface->driver_name, params, len); - iface->driver_name[len] = '\0'; for (i = 0; wpa_drivers[i]; i++) { if (os_strcmp(iface->driver_name, @@ -649,7 +639,7 @@ } if (bind(iface->fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - perror("bind(PF_UNIX)"); + perror("wpa-priv-iface-init: bind(PF_UNIX)"); goto fail; } wpa_printf(MSG_DEBUG, "Successfully replaced leftover " @@ -954,8 +944,6 @@ } -extern int wpa_debug_level; - int main(int argc, char *argv[]) { int c, i; diff -Nru wpa-1.0/wpa_supplicant/wpas_glue.c wpa-2.1/wpa_supplicant/wpas_glue.c --- wpa-1.0/wpa_supplicant/wpas_glue.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/wpas_glue.c 2014-02-04 11:23:35.000000000 +0000 @@ -1,15 +1,9 @@ /* * WPA Supplicant - Glue code to setup EAPOL and RSN modules - * Copyright (c) 2003-2008, Jouni Malinen + * Copyright (c) 2003-2012, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -222,20 +216,38 @@ } -static void wpa_supplicant_eapol_cb(struct eapol_sm *eapol, int success, +static const char * result_str(enum eapol_supp_result result) +{ + switch (result) { + case EAPOL_SUPP_RESULT_FAILURE: + return "FAILURE"; + case EAPOL_SUPP_RESULT_SUCCESS: + return "SUCCESS"; + case EAPOL_SUPP_RESULT_EXPECTED_FAILURE: + return "EXPECTED_FAILURE"; + } + return "?"; +} + + +static void wpa_supplicant_eapol_cb(struct eapol_sm *eapol, + enum eapol_supp_result result, void *ctx) { struct wpa_supplicant *wpa_s = ctx; int res, pmk_len; u8 pmk[PMK_LEN]; - wpa_printf(MSG_DEBUG, "EAPOL authentication completed %ssuccessfully", - success ? "" : "un"); + wpa_printf(MSG_DEBUG, "EAPOL authentication completed - result=%s", + result_str(result)); if (wpas_wps_eapol_cb(wpa_s) > 0) return; - if (!success) { + wpa_s->eap_expected_failure = result == + EAPOL_SUPP_RESULT_EXPECTED_FAILURE; + + if (result != EAPOL_SUPP_RESULT_SUCCESS) { /* * Make sure we do not get stuck here waiting for long EAPOL * timeout if the AP does not disconnect in case of @@ -244,7 +256,8 @@ wpa_supplicant_req_auth_timeout(wpa_s, 2, 0); } - if (!success || !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE)) + if (result != EAPOL_SUPP_RESULT_SUCCESS || + !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE)) return; if (!wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt)) @@ -412,14 +425,6 @@ } -static void _wpa_supplicant_disassociate(void *wpa_s, int reason_code) -{ - wpa_supplicant_disassociate(wpa_s, reason_code); - /* Schedule a scan to make sure we continue looking for networks */ - wpa_supplicant_req_scan(wpa_s, 5, 0); -} - - static void _wpa_supplicant_deauthenticate(void *wpa_s, int reason_code) { wpa_supplicant_deauthenticate(wpa_s, reason_code); @@ -451,6 +456,13 @@ /* Clear the MIC error counter when setting a new PTK. */ wpa_s->mic_errors_seen = 0; } +#ifdef CONFIG_TESTING_GET_GTK + if (key_idx > 0 && addr && is_broadcast_ether_addr(addr) && + alg != WPA_ALG_NONE && key_len <= sizeof(wpa_s->last_gtk)) { + os_memcpy(wpa_s->last_gtk, key, key_len); + wpa_s->last_gtk_len = key_len; + } +#endif /* CONFIG_TESTING_GET_GTK */ return wpa_drv_set_key(wpa_s, alg, addr, key_idx, set_tx, seq, seq_len, key, key_len); } @@ -520,8 +532,6 @@ } #endif /* CONFIG_IEEE80211R */ -#endif /* CONFIG_NO_WPA */ - #ifdef CONFIG_TDLS @@ -565,27 +575,52 @@ static int wpa_supplicant_tdls_peer_addset( - void *ctx, const u8 *peer, int add, u16 capability, - const u8 *supp_rates, size_t supp_rates_len) + void *ctx, const u8 *peer, int add, u16 aid, u16 capability, + const u8 *supp_rates, size_t supp_rates_len, + const struct ieee80211_ht_capabilities *ht_capab, + const struct ieee80211_vht_capabilities *vht_capab, + u8 qosinfo, const u8 *ext_capab, size_t ext_capab_len, + const u8 *supp_channels, size_t supp_channels_len, + const u8 *supp_oper_classes, size_t supp_oper_classes_len) { struct wpa_supplicant *wpa_s = ctx; struct hostapd_sta_add_params params; + os_memset(¶ms, 0, sizeof(params)); + params.addr = peer; - params.aid = 1; + params.aid = aid; params.capability = capability; params.flags = WPA_STA_TDLS_PEER | WPA_STA_AUTHORIZED; - params.ht_capabilities = NULL; + + /* + * TDLS Setup frames do not contain WMM IEs, hence need to depend on + * qosinfo to check if the peer is WMM capable. + */ + if (qosinfo) + params.flags |= WPA_STA_WMM; + + params.ht_capabilities = ht_capab; + params.vht_capabilities = vht_capab; + params.qosinfo = qosinfo; params.listen_interval = 0; params.supp_rates = supp_rates; params.supp_rates_len = supp_rates_len; params.set = !add; + params.ext_capab = ext_capab; + params.ext_capab_len = ext_capab_len; + params.supp_channels = supp_channels; + params.supp_channels_len = supp_channels_len; + params.supp_oper_classes = supp_oper_classes; + params.supp_oper_classes_len = supp_oper_classes_len; return wpa_drv_sta_add(wpa_s, ¶ms); } #endif /* CONFIG_TDLS */ +#endif /* CONFIG_NO_WPA */ + enum wpa_ctrl_req_type wpa_supplicant_ctrl_req_from_string(const char *field) { @@ -601,6 +636,8 @@ return WPA_CTRL_REQ_EAP_OTP; else if (os_strcmp(field, "PASSPHRASE") == 0) return WPA_CTRL_REQ_EAP_PASSPHRASE; + else if (os_strcmp(field, "SIM") == 0) + return WPA_CTRL_REQ_SIM; return WPA_CTRL_REQ_UNKNOWN; } @@ -637,6 +674,9 @@ *txt = "Private key passphrase"; ret = "PASSPHRASE"; break; + case WPA_CTRL_REQ_SIM: + ret = "SIM"; + break; default: break; } @@ -676,6 +716,8 @@ return; } + wpas_notify_eap_status(wpa_s, "eap parameter needed", field_name); + buflen = 100 + os_strlen(txt) + ssid->ssid_len; buf = os_malloc(buflen); if (buf == NULL) @@ -726,6 +768,53 @@ wpas_notify_certification(wpa_s, depth, subject, cert_hash, cert); } + + +static void wpa_supplicant_status_cb(void *ctx, const char *status, + const char *parameter) +{ + struct wpa_supplicant *wpa_s = ctx; + + wpas_notify_eap_status(wpa_s, status, parameter); +} + + +static void wpa_supplicant_set_anon_id(void *ctx, const u8 *id, size_t len) +{ + struct wpa_supplicant *wpa_s = ctx; + char *str; + int res; + + wpa_hexdump_ascii(MSG_DEBUG, "EAP method updated anonymous_identity", + id, len); + + if (wpa_s->current_ssid == NULL) + return; + + if (id == NULL) { + if (wpa_config_set(wpa_s->current_ssid, "anonymous_identity", + "NULL", 0) < 0) + return; + } else { + str = os_malloc(len * 2 + 1); + if (str == NULL) + return; + wpa_snprintf_hex(str, len * 2 + 1, id, len); + res = wpa_config_set(wpa_s->current_ssid, "anonymous_identity", + str, 0); + os_free(str); + if (res < 0) + return; + } + + if (wpa_s->conf->update_config) { + res = wpa_config_write(wpa_s->confname, wpa_s->conf); + if (res) { + wpa_printf(MSG_DEBUG, "Failed to update config after " + "anonymous_id update"); + } + } +} #endif /* IEEE8021X_EAPOL */ @@ -746,8 +835,10 @@ ctx->eapol_done_cb = wpa_supplicant_notify_eapol_done; ctx->eapol_send = wpa_supplicant_eapol_send; ctx->set_wep_key = wpa_eapol_set_wep_key; +#ifndef CONFIG_NO_CONFIG_BLOBS ctx->set_config_blob = wpa_supplicant_set_config_blob; ctx->get_config_blob = wpa_supplicant_get_config_blob; +#endif /* CONFIG_NO_CONFIG_BLOBS */ ctx->aborted_cached = wpa_supplicant_aborted_cached; ctx->opensc_engine_path = wpa_s->conf->opensc_engine_path; ctx->pkcs11_engine_path = wpa_s->conf->pkcs11_engine_path; @@ -757,6 +848,8 @@ ctx->port_cb = wpa_supplicant_port_cb; ctx->cb = wpa_supplicant_eapol_cb; ctx->cert_cb = wpa_supplicant_cert_cb; + ctx->status_cb = wpa_supplicant_status_cb; + ctx->set_anon_id = wpa_supplicant_set_anon_id; ctx->cb_ctx = wpa_s; wpa_s->eapol = eapol_sm_init(ctx); if (wpa_s->eapol == NULL) { @@ -771,6 +864,7 @@ } +#ifndef CONFIG_NO_WPA static void wpa_supplicant_set_rekey_offload(void *ctx, const u8 *kek, const u8 *kck, const u8 *replay_ctr) @@ -779,6 +873,7 @@ wpa_drv_set_rekey_info(wpa_s, kek, kck, replay_ctr); } +#endif /* CONFIG_NO_WPA */ int wpa_supplicant_init_wpa(struct wpa_supplicant *wpa_s) @@ -796,7 +891,6 @@ ctx->set_state = _wpa_supplicant_set_state; ctx->get_state = _wpa_supplicant_get_state; ctx->deauthenticate = _wpa_supplicant_deauthenticate; - ctx->disassociate = _wpa_supplicant_disassociate; ctx->set_key = wpa_supplicant_set_key; ctx->get_network_ctx = wpa_supplicant_get_network_ctx; ctx->get_bssid = wpa_supplicant_get_bssid; @@ -846,13 +940,30 @@ conf.peerkey_enabled = ssid->peerkey; conf.allowed_pairwise_cipher = ssid->pairwise_cipher; #ifdef IEEE8021X_EAPOL - conf.proactive_key_caching = ssid->proactive_key_caching; + conf.proactive_key_caching = ssid->proactive_key_caching < 0 ? + wpa_s->conf->okc : ssid->proactive_key_caching; conf.eap_workaround = ssid->eap_workaround; conf.eap_conf_ctx = &ssid->eap; #endif /* IEEE8021X_EAPOL */ conf.ssid = ssid->ssid; conf.ssid_len = ssid->ssid_len; conf.wpa_ptk_rekey = ssid->wpa_ptk_rekey; +#ifdef CONFIG_P2P + if (ssid->p2p_group && wpa_s->current_bss && + !wpa_s->p2p_disable_ip_addr_req) { + struct wpabuf *p2p; + p2p = wpa_bss_get_vendor_ie_multi(wpa_s->current_bss, + P2P_IE_VENDOR_TYPE); + if (p2p) { + u8 group_capab; + group_capab = p2p_get_group_capab(p2p); + if (group_capab & + P2P_GROUP_CAPAB_IP_ADDR_ALLOCATION) + conf.p2p = 1; + wpabuf_free(p2p); + } + } +#endif /* CONFIG_P2P */ } wpa_sm_set_config(wpa_s->wpa, ssid ? &conf : NULL); } diff -Nru wpa-1.0/wpa_supplicant/wpas_glue.h wpa-2.1/wpa_supplicant/wpas_glue.h --- wpa-1.0/wpa_supplicant/wpas_glue.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/wpas_glue.h 2014-02-04 11:23:35.000000000 +0000 @@ -2,14 +2,8 @@ * WPA Supplicant - Glue code to setup EAPOL and RSN modules * Copyright (c) 2003-2008, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef WPAS_GLUE_H diff -Nru wpa-1.0/wpa_supplicant/wpa_supplicant.c wpa-2.1/wpa_supplicant/wpa_supplicant.c --- wpa-1.0/wpa_supplicant/wpa_supplicant.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/wpa_supplicant.c 2014-02-04 11:23:35.000000000 +0000 @@ -1,15 +1,9 @@ /* * WPA Supplicant - * Copyright (c) 2003-2012, Jouni Malinen + * Copyright (c) 2003-2014, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. * * This file implements functions for registering and unregistering * %wpa_supplicant interfaces. In addition, this file contains number of @@ -23,10 +17,12 @@ #include "crypto/sha1.h" #include "eapol_supp/eapol_supp_sm.h" #include "eap_peer/eap.h" +#include "eap_peer/eap_proxy.h" #include "eap_server/eap_methods.h" #include "rsn_supp/wpa.h" #include "eloop.h" #include "config.h" +#include "utils/ext_password.h" #include "l2_packet/l2_packet.h" #include "wpa_supplicant_i.h" #include "driver_i.h" @@ -46,22 +42,23 @@ #include "gas_query.h" #include "ap.h" #include "p2p_supplicant.h" +#include "wifi_display.h" #include "notify.h" #include "bgscan.h" +#include "autoscan.h" #include "bss.h" #include "scan.h" #include "offchannel.h" +#include "hs20_supplicant.h" +#include "wnm_sta.h" const char *wpa_supplicant_version = "wpa_supplicant v" VERSION_STR "\n" -"Copyright (c) 2003-2012, Jouni Malinen and contributors"; +"Copyright (c) 2003-2014, Jouni Malinen and contributors"; const char *wpa_supplicant_license = -"This program is free software. You can distribute it and/or modify it\n" -"under the terms of the GNU General Public License version 2.\n" -"\n" -"Alternatively, this software may be distributed under the terms of the\n" -"BSD license. See README and COPYING for more details.\n" +"This software may be distributed under the terms of the BSD license.\n" +"See README for more details.\n" #ifdef EAP_TLS_OPENSSL "\nThis product includes software developed by the OpenSSL Project\n" "for use in the OpenSSL Toolkit (http://www.openssl.org/)\n" @@ -71,22 +68,9 @@ #ifndef CONFIG_NO_STDOUT_DEBUG /* Long text divided into parts in order to fit in C89 strings size limits. */ const char *wpa_supplicant_full_license1 = -"This program is free software; you can redistribute it and/or modify\n" -"it under the terms of the GNU General Public License version 2 as\n" -"published by the Free Software Foundation.\n" -"\n" -"This program is distributed in the hope that it will be useful,\n" -"but WITHOUT ANY WARRANTY; without even the implied warranty of\n" -"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" -"GNU General Public License for more details.\n" -"\n"; +""; const char *wpa_supplicant_full_license2 = -"You should have received a copy of the GNU General Public License\n" -"along with this program; if not, write to the Free Software\n" -"Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA\n" -"\n" -"Alternatively, this software may be distributed under the terms of the\n" -"BSD license.\n" +"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" @@ -120,11 +104,6 @@ "\n"; #endif /* CONFIG_NO_STDOUT_DEBUG */ -extern int wpa_debug_level; -extern int wpa_debug_show_keys; -extern int wpa_debug_timestamp; -extern struct wpa_driver_ops *wpa_drivers[]; - /* Configure default/group WEP keys for static WEP */ int wpa_set_wep_keys(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) { @@ -144,8 +123,8 @@ } -static int wpa_supplicant_set_wpa_none_key(struct wpa_supplicant *wpa_s, - struct wpa_ssid *ssid) +int wpa_supplicant_set_wpa_none_key(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid) { u8 key[32]; size_t keylen; @@ -173,6 +152,11 @@ keylen = 16; alg = WPA_ALG_CCMP; break; + case WPA_CIPHER_GCMP: + os_memcpy(key, ssid->psk, 16); + keylen = 16; + alg = WPA_ALG_GCMP; + break; case WPA_CIPHER_TKIP: /* WPA-None uses the same Michael MIC key for both TX and RX */ os_memcpy(key, ssid->psk, 16 + 8); @@ -203,7 +187,7 @@ MAC2STR(bssid)); wpa_blacklist_add(wpa_s, bssid); wpa_sm_notify_disassoc(wpa_s->wpa); - wpa_supplicant_disassociate(wpa_s, WLAN_REASON_DEAUTH_LEAVING); + wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_DEAUTH_LEAVING); wpa_s->reassociate = 1; /* @@ -226,7 +210,7 @@ void wpa_supplicant_req_auth_timeout(struct wpa_supplicant *wpa_s, int sec, int usec) { - if (wpa_s->conf && wpa_s->conf->ap_scan == 0 && + if (wpa_s->conf->ap_scan == 0 && (wpa_s->drv_flags & WPA_DRIVER_FLAGS_WIRED)) return; @@ -302,16 +286,16 @@ EAPOL_REQUIRE_KEY_BROADCAST; } - if (wpa_s->conf && (wpa_s->drv_flags & WPA_DRIVER_FLAGS_WIRED)) + if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_WIRED) eapol_conf.required_keys = 0; } - if (wpa_s->conf) - eapol_conf.fast_reauth = wpa_s->conf->fast_reauth; + eapol_conf.fast_reauth = wpa_s->conf->fast_reauth; eapol_conf.workaround = ssid->eap_workaround; eapol_conf.eap_disabled = !wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt) && wpa_s->key_mgmt != WPA_KEY_MGMT_IEEE8021X_NO_WPA && wpa_s->key_mgmt != WPA_KEY_MGMT_WPS; + eapol_conf.external_sim = wpa_s->conf->external_sim; eapol_sm_notify_config(wpa_s->eapol, &ssid->eap, &eapol_conf); #endif /* IEEE8021X_EAPOL */ } @@ -389,6 +373,7 @@ static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s) { bgscan_deinit(wpa_s); + autoscan_deinit(wpa_s); scard_deinit(wpa_s->scard); wpa_s->scard = NULL; wpa_sm_set_scard_ctx(wpa_s->wpa, NULL); @@ -400,21 +385,18 @@ wpa_s->l2_br = NULL; } - if (wpa_s->ctrl_iface) { - wpa_supplicant_ctrl_iface_deinit(wpa_s->ctrl_iface); - wpa_s->ctrl_iface = NULL; - } if (wpa_s->conf != NULL) { struct wpa_ssid *ssid; for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) wpas_notify_network_removed(wpa_s, ssid); - wpa_config_free(wpa_s->conf); - wpa_s->conf = NULL; } os_free(wpa_s->confname); wpa_s->confname = NULL; + os_free(wpa_s->confanother); + wpa_s->confanother = NULL; + wpa_sm_set_eapol(wpa_s->wpa, NULL); eapol_sm_deinit(wpa_s->eapol); wpa_s->eapol = NULL; @@ -432,6 +414,7 @@ wpa_bss_deinit(wpa_s); + wpa_supplicant_cancel_delayed_sched_scan(wpa_s); wpa_supplicant_cancel_scan(wpa_s); wpa_supplicant_cancel_auth_timeout(wpa_s); eloop_cancel_timeout(wpa_supplicant_stop_countermeasures, wpa_s, NULL); @@ -469,10 +452,37 @@ os_free(wpa_s->next_scan_freqs); wpa_s->next_scan_freqs = NULL; + os_free(wpa_s->manual_scan_freqs); + wpa_s->manual_scan_freqs = NULL; + gas_query_deinit(wpa_s->gas); wpa_s->gas = NULL; free_hw_features(wpa_s); + + os_free(wpa_s->bssid_filter); + wpa_s->bssid_filter = NULL; + + os_free(wpa_s->disallow_aps_bssid); + wpa_s->disallow_aps_bssid = NULL; + os_free(wpa_s->disallow_aps_ssid); + wpa_s->disallow_aps_ssid = NULL; + + wnm_bss_keep_alive_deinit(wpa_s); +#ifdef CONFIG_WNM + wnm_deallocate_memory(wpa_s); +#endif /* CONFIG_WNM */ + + ext_password_deinit(wpa_s->ext_pw); + wpa_s->ext_pw = NULL; + + wpabuf_free(wpa_s->last_gas_resp); + wpa_s->last_gas_resp = NULL; + wpabuf_free(wpa_s->prev_gas_resp); + wpa_s->prev_gas_resp = NULL; + + os_free(wpa_s->last_scan_res); + wpa_s->last_scan_res = NULL; } @@ -486,29 +496,23 @@ */ void wpa_clear_keys(struct wpa_supplicant *wpa_s, const u8 *addr) { - if (wpa_s->keys_cleared) { - /* Some drivers (e.g., ndiswrapper & NDIS drivers) seem to have - * timing issues with keys being cleared just before new keys - * are set or just after association or something similar. This - * shows up in group key handshake failing often because of the - * client not receiving the first encrypted packets correctly. - * Skipping some of the extra key clearing steps seems to help - * in completing group key handshake more reliably. */ - wpa_dbg(wpa_s, MSG_DEBUG, "No keys have been configured - " - "skip key clearing"); - return; - } + int i, max; - /* MLME-DELETEKEYS.request */ - wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 0, 0, NULL, 0, NULL, 0); - wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 1, 0, NULL, 0, NULL, 0); - wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 2, 0, NULL, 0, NULL, 0); - wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 3, 0, NULL, 0, NULL, 0); #ifdef CONFIG_IEEE80211W - wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 4, 0, NULL, 0, NULL, 0); - wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 5, 0, NULL, 0, NULL, 0); + max = 6; +#else /* CONFIG_IEEE80211W */ + max = 4; #endif /* CONFIG_IEEE80211W */ - if (addr) { + + /* MLME-DELETEKEYS.request */ + for (i = 0; i < max; i++) { + if (wpa_s->keys_cleared & BIT(i)) + continue; + wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, i, 0, NULL, 0, + NULL, 0); + } + if (!(wpa_s->keys_cleared & BIT(0)) && addr && + !is_zero_ether_addr(addr)) { wpa_drv_set_key(wpa_s, WPA_ALG_NONE, addr, 0, 0, NULL, 0, NULL, 0); /* MLME-SETPROTECTION.request(None) */ @@ -517,7 +521,7 @@ MLME_SETPROTECTION_PROTECT_TYPE_NONE, MLME_SETPROTECTION_KEY_TYPE_PAIRWISE); } - wpa_s->keys_cleared = 1; + wpa_s->keys_cleared = (u32) -1; } @@ -559,14 +563,22 @@ static void wpa_supplicant_start_bgscan(struct wpa_supplicant *wpa_s) { + const char *name; + + if (wpa_s->current_ssid && wpa_s->current_ssid->bgscan) + name = wpa_s->current_ssid->bgscan; + else + name = wpa_s->conf->bgscan; + if (name == NULL) + return; if (wpas_driver_bss_selection(wpa_s)) return; if (wpa_s->current_ssid == wpa_s->bgscan_ssid) return; bgscan_deinit(wpa_s); - if (wpa_s->current_ssid && wpa_s->current_ssid->bgscan) { - if (bgscan_init(wpa_s, wpa_s->current_ssid)) { + if (wpa_s->current_ssid) { + if (bgscan_init(wpa_s, wpa_s->current_ssid, name)) { wpa_dbg(wpa_s, MSG_DEBUG, "Failed to initialize " "bgscan"); /* @@ -574,8 +586,16 @@ * optimization, so the initial connection is not * affected. */ - } else + } else { + struct wpa_scan_results *scan_res; wpa_s->bgscan_ssid = wpa_s->current_ssid; + scan_res = wpa_supplicant_get_scan_results(wpa_s, NULL, + 0); + if (scan_res) { + bgscan_notify_scan(wpa_s, scan_res); + wpa_scan_results_free(scan_res); + } + } } else wpa_s->bgscan_ssid = NULL; } @@ -592,6 +612,29 @@ #endif /* CONFIG_BGSCAN */ +static void wpa_supplicant_start_autoscan(struct wpa_supplicant *wpa_s) +{ + if (autoscan_init(wpa_s, 0)) + wpa_dbg(wpa_s, MSG_DEBUG, "Failed to initialize autoscan"); +} + + +static void wpa_supplicant_stop_autoscan(struct wpa_supplicant *wpa_s) +{ + autoscan_deinit(wpa_s); +} + + +void wpa_supplicant_reinit_autoscan(struct wpa_supplicant *wpa_s) +{ + if (wpa_s->wpa_state == WPA_DISCONNECTED || + wpa_s->wpa_state == WPA_SCANNING) { + autoscan_deinit(wpa_s); + wpa_supplicant_start_autoscan(wpa_s); + } +} + + /** * wpa_supplicant_set_state - Set current connection state * @wpa_s: Pointer to wpa_supplicant data @@ -609,29 +652,40 @@ wpa_supplicant_state_txt(wpa_s->wpa_state), wpa_supplicant_state_txt(state)); + if (state == WPA_INTERFACE_DISABLED) { + /* Assure normal scan when interface is restored */ + wpa_s->normal_scans = 0; + } + + if (state == WPA_COMPLETED) + wpas_connect_work_done(wpa_s); + if (state != WPA_SCANNING) wpa_supplicant_notify_scanning(wpa_s, 0); if (state == WPA_COMPLETED && wpa_s->new_connection) { -#if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG) struct wpa_ssid *ssid = wpa_s->current_ssid; +#if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG) wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_CONNECTED "- Connection to " - MACSTR " completed %s [id=%d id_str=%s]", - MAC2STR(wpa_s->bssid), wpa_s->reassociated_connection ? - "(reauth)" : "(auth)", + MACSTR " completed [id=%d id_str=%s]", + MAC2STR(wpa_s->bssid), ssid ? ssid->id : -1, ssid && ssid->id_str ? ssid->id_str : ""); #endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */ + wpas_clear_temp_disabled(wpa_s, ssid, 1); + wpa_s->extra_blacklist_count = 0; wpa_s->new_connection = 0; - wpa_s->reassociated_connection = 1; wpa_drv_set_operstate(wpa_s, 1); #ifndef IEEE8021X_EAPOL wpa_drv_set_supp_port(wpa_s, 1); #endif /* IEEE8021X_EAPOL */ wpa_s->after_wps = 0; + wpa_s->known_wps_freq = 0; #ifdef CONFIG_P2P wpas_p2p_completed(wpa_s); #endif /* CONFIG_P2P */ + + sme_sched_obss_scan(wpa_s, 1); } else if (state == WPA_DISCONNECTED || state == WPA_ASSOCIATING || state == WPA_ASSOCIATED) { wpa_s->new_connection = 1; @@ -639,16 +693,23 @@ #ifndef IEEE8021X_EAPOL wpa_drv_set_supp_port(wpa_s, 0); #endif /* IEEE8021X_EAPOL */ + sme_sched_obss_scan(wpa_s, 0); } wpa_s->wpa_state = state; #ifdef CONFIG_BGSCAN if (state == WPA_COMPLETED) wpa_supplicant_start_bgscan(wpa_s); - else + else if (state < WPA_ASSOCIATED) wpa_supplicant_stop_bgscan(wpa_s); #endif /* CONFIG_BGSCAN */ + if (state == WPA_AUTHENTICATING) + wpa_supplicant_stop_autoscan(wpa_s); + + if (state == WPA_DISCONNECTED || state == WPA_INACTIVE) + wpa_supplicant_start_autoscan(wpa_s); + if (wpa_s->wpa_state != old_state) { wpas_notify_state_changed(wpa_s, wpa_s->wpa_state, old_state); @@ -665,9 +726,15 @@ #ifdef CONFIG_WPS struct wpa_supplicant *wpa_s = global->ifaces; while (wpa_s) { + struct wpa_supplicant *next = wpa_s->next; +#ifdef CONFIG_P2P + if (wpa_s->p2p_group_interface != NOT_P2P_GROUP_INTERFACE || + (wpa_s->current_ssid && wpa_s->current_ssid->p2p_group)) + wpas_p2p_disconnect(wpa_s); +#endif /* CONFIG_P2P */ if (wpas_wps_terminate_pending(wpa_s) == 1) pending = 1; - wpa_s = wpa_s->next; + wpa_s = next; } #endif /* CONFIG_WPS */ if (pending) @@ -679,11 +746,6 @@ static void wpa_supplicant_terminate(int sig, void *signal_ctx) { struct wpa_global *global = signal_ctx; - struct wpa_supplicant *wpa_s; - for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) { - wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_TERMINATING "- signal %d " - "received", sig); - } wpa_supplicant_terminate_proc(global); } @@ -697,7 +759,7 @@ wpa_s->mgmt_group_cipher = 0; wpa_s->key_mgmt = 0; if (wpa_s->wpa_state != WPA_INTERFACE_DISABLED) - wpa_s->wpa_state = WPA_DISCONNECTED; + wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED); if (wpa_s->wpa_state != old_state) wpas_notify_state_changed(wpa_s, wpa_s->wpa_state, old_state); @@ -723,12 +785,14 @@ if (wpa_s->confname == NULL) return -1; - conf = wpa_config_read(wpa_s->confname); + conf = wpa_config_read(wpa_s->confname, NULL); if (conf == NULL) { wpa_msg(wpa_s, MSG_ERROR, "Failed to parse the configuration " "file '%s' - exiting", wpa_s->confname); return -1; } + wpa_config_read(wpa_s->confanother, conf); + conf->changed_parameters = (unsigned int) -1; reconf_ctrl = !!conf->ctrl_interface != !!wpa_s->conf->ctrl_interface @@ -776,7 +840,7 @@ wpa_supplicant_update_config(wpa_s); wpa_supplicant_clear_status(wpa_s); - if (wpa_supplicant_enabled_networks(wpa_s->conf)) { + if (wpa_supplicant_enabled_networks(wpa_s)) { wpa_s->reassociate = 1; wpa_supplicant_req_scan(wpa_s, 0, 0); } @@ -799,52 +863,6 @@ } -enum wpa_cipher cipher_suite2driver(int cipher) -{ - switch (cipher) { - case WPA_CIPHER_NONE: - return CIPHER_NONE; - case WPA_CIPHER_WEP40: - return CIPHER_WEP40; - case WPA_CIPHER_WEP104: - return CIPHER_WEP104; - case WPA_CIPHER_CCMP: - return CIPHER_CCMP; - case WPA_CIPHER_TKIP: - default: - return CIPHER_TKIP; - } -} - - -enum wpa_key_mgmt key_mgmt2driver(int key_mgmt) -{ - switch (key_mgmt) { - case WPA_KEY_MGMT_NONE: - return KEY_MGMT_NONE; - case WPA_KEY_MGMT_IEEE8021X_NO_WPA: - return KEY_MGMT_802_1X_NO_WPA; - case WPA_KEY_MGMT_IEEE8021X: - return KEY_MGMT_802_1X; - case WPA_KEY_MGMT_WPA_NONE: - return KEY_MGMT_WPA_NONE; - case WPA_KEY_MGMT_FT_IEEE8021X: - return KEY_MGMT_FT_802_1X; - case WPA_KEY_MGMT_FT_PSK: - return KEY_MGMT_FT_PSK; - case WPA_KEY_MGMT_IEEE8021X_SHA256: - return KEY_MGMT_802_1X_SHA256; - case WPA_KEY_MGMT_PSK_SHA256: - return KEY_MGMT_PSK_SHA256; - case WPA_KEY_MGMT_WPS: - return KEY_MGMT_WPS; - case WPA_KEY_MGMT_PSK: - default: - return KEY_MGMT_PSK; - } -} - - static int wpa_supplicant_suites_from_ai(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, struct wpa_ie_data *ie) @@ -881,7 +899,9 @@ #ifdef CONFIG_IEEE80211W if (!(ie->capabilities & WPA_CAPABILITY_MFPC) && - ssid->ieee80211w == MGMT_FRAME_PROTECTION_REQUIRED) { + (ssid->ieee80211w == MGMT_FRAME_PROTECTION_DEFAULT ? + wpa_s->conf->pmf : ssid->ieee80211w) == + MGMT_FRAME_PROTECTION_REQUIRED) { wpa_msg(wpa_s, MSG_INFO, "WPA: Driver associated with an AP " "that does not support management frame protection - " "reject"); @@ -983,41 +1003,30 @@ } sel = ie.group_cipher & ssid->group_cipher; - if (sel & WPA_CIPHER_CCMP) { - wpa_s->group_cipher = WPA_CIPHER_CCMP; - wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using GTK CCMP"); - } else if (sel & WPA_CIPHER_TKIP) { - wpa_s->group_cipher = WPA_CIPHER_TKIP; - wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using GTK TKIP"); - } else if (sel & WPA_CIPHER_WEP104) { - wpa_s->group_cipher = WPA_CIPHER_WEP104; - wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using GTK WEP104"); - } else if (sel & WPA_CIPHER_WEP40) { - wpa_s->group_cipher = WPA_CIPHER_WEP40; - wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using GTK WEP40"); - } else { + wpa_s->group_cipher = wpa_pick_group_cipher(sel); + if (wpa_s->group_cipher < 0) { wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to select group " "cipher"); return -1; } + wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using GTK %s", + wpa_cipher_txt(wpa_s->group_cipher)); sel = ie.pairwise_cipher & ssid->pairwise_cipher; - if (sel & WPA_CIPHER_CCMP) { - wpa_s->pairwise_cipher = WPA_CIPHER_CCMP; - wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using PTK CCMP"); - } else if (sel & WPA_CIPHER_TKIP) { - wpa_s->pairwise_cipher = WPA_CIPHER_TKIP; - wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using PTK TKIP"); - } else if (sel & WPA_CIPHER_NONE) { - wpa_s->pairwise_cipher = WPA_CIPHER_NONE; - wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using PTK NONE"); - } else { + wpa_s->pairwise_cipher = wpa_pick_pairwise_cipher(sel, 1); + if (wpa_s->pairwise_cipher < 0) { wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to select pairwise " "cipher"); return -1; } + wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using PTK %s", + wpa_cipher_txt(wpa_s->pairwise_cipher)); sel = ie.key_mgmt & ssid->key_mgmt; +#ifdef CONFIG_SAE + if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SAE)) + sel &= ~(WPA_KEY_MGMT_SAE | WPA_KEY_MGMT_FT_SAE); +#endif /* CONFIG_SAE */ if (0) { #ifdef CONFIG_IEEE80211R } else if (sel & WPA_KEY_MGMT_FT_IEEE8021X) { @@ -1027,6 +1036,14 @@ wpa_s->key_mgmt = WPA_KEY_MGMT_FT_PSK; wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT FT/PSK"); #endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_SAE + } else if (sel & WPA_KEY_MGMT_SAE) { + wpa_s->key_mgmt = WPA_KEY_MGMT_SAE; + wpa_dbg(wpa_s, MSG_DEBUG, "RSN: using KEY_MGMT SAE"); + } else if (sel & WPA_KEY_MGMT_FT_SAE) { + wpa_s->key_mgmt = WPA_KEY_MGMT_FT_SAE; + wpa_dbg(wpa_s, MSG_DEBUG, "RSN: using KEY_MGMT FT/SAE"); +#endif /* CONFIG_SAE */ #ifdef CONFIG_IEEE80211W } else if (sel & WPA_KEY_MGMT_IEEE8021X_SHA256) { wpa_s->key_mgmt = WPA_KEY_MGMT_IEEE8021X_SHA256; @@ -1059,7 +1076,8 @@ #ifdef CONFIG_IEEE80211W sel = ie.mgmt_group_cipher; - if (ssid->ieee80211w == NO_MGMT_FRAME_PROTECTION || + if ((ssid->ieee80211w == MGMT_FRAME_PROTECTION_DEFAULT ? + wpa_s->conf->pmf : ssid->ieee80211w) == NO_MGMT_FRAME_PROTECTION || !(ie.capabilities & WPA_CAPABILITY_MFPC)) sel = 0; if (sel & WPA_CIPHER_AES_128_CMAC) { @@ -1072,7 +1090,9 @@ } wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_MGMT_GROUP, wpa_s->mgmt_group_cipher); - wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_MFP, ssid->ieee80211w); + wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_MFP, + (ssid->ieee80211w == MGMT_FRAME_PROTECTION_DEFAULT ? + wpa_s->conf->pmf : ssid->ieee80211w)); #endif /* CONFIG_IEEE80211W */ if (wpa_sm_set_assoc_wpa_ie_default(wpa_s->wpa, wpa_ie, wpa_ie_len)) { @@ -1080,21 +1100,76 @@ return -1; } - if (ssid->key_mgmt & - (WPA_KEY_MGMT_PSK | WPA_KEY_MGMT_FT_PSK | WPA_KEY_MGMT_PSK_SHA256)) - { + if (wpa_key_mgmt_wpa_psk(ssid->key_mgmt)) { wpa_sm_set_pmk(wpa_s->wpa, ssid->psk, PMK_LEN); #ifndef CONFIG_NO_PBKDF2 if (bss && ssid->bssid_set && ssid->ssid_len == 0 && ssid->passphrase) { u8 psk[PMK_LEN]; - pbkdf2_sha1(ssid->passphrase, (char *) bss->ssid, - bss->ssid_len, 4096, psk, PMK_LEN); + pbkdf2_sha1(ssid->passphrase, bss->ssid, bss->ssid_len, + 4096, psk, PMK_LEN); wpa_hexdump_key(MSG_MSGDUMP, "PSK (from passphrase)", psk, PMK_LEN); wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN); } #endif /* CONFIG_NO_PBKDF2 */ +#ifdef CONFIG_EXT_PASSWORD + if (ssid->ext_psk) { + struct wpabuf *pw = ext_password_get(wpa_s->ext_pw, + ssid->ext_psk); + char pw_str[64 + 1]; + u8 psk[PMK_LEN]; + + if (pw == NULL) { + wpa_msg(wpa_s, MSG_INFO, "EXT PW: No PSK " + "found from external storage"); + return -1; + } + + if (wpabuf_len(pw) < 8 || wpabuf_len(pw) > 64) { + wpa_msg(wpa_s, MSG_INFO, "EXT PW: Unexpected " + "PSK length %d in external storage", + (int) wpabuf_len(pw)); + ext_password_free(pw); + return -1; + } + + os_memcpy(pw_str, wpabuf_head(pw), wpabuf_len(pw)); + pw_str[wpabuf_len(pw)] = '\0'; + +#ifndef CONFIG_NO_PBKDF2 + if (wpabuf_len(pw) >= 8 && wpabuf_len(pw) < 64 && bss) + { + pbkdf2_sha1(pw_str, bss->ssid, bss->ssid_len, + 4096, psk, PMK_LEN); + os_memset(pw_str, 0, sizeof(pw_str)); + wpa_hexdump_key(MSG_MSGDUMP, "PSK (from " + "external passphrase)", + psk, PMK_LEN); + wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN); + } else +#endif /* CONFIG_NO_PBKDF2 */ + if (wpabuf_len(pw) == 2 * PMK_LEN) { + if (hexstr2bin(pw_str, psk, PMK_LEN) < 0) { + wpa_msg(wpa_s, MSG_INFO, "EXT PW: " + "Invalid PSK hex string"); + os_memset(pw_str, 0, sizeof(pw_str)); + ext_password_free(pw); + return -1; + } + wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN); + } else { + wpa_msg(wpa_s, MSG_INFO, "EXT PW: No suitable " + "PSK available"); + os_memset(pw_str, 0, sizeof(pw_str)); + ext_password_free(pw); + return -1; + } + + os_memset(pw_str, 0, sizeof(pw_str)); + ext_password_free(pw); + } +#endif /* CONFIG_EXT_PASSWORD */ } else wpa_sm_set_pmk_from_pmksa(wpa_s->wpa); @@ -1102,6 +1177,138 @@ } +static void wpas_ext_capab_byte(struct wpa_supplicant *wpa_s, u8 *pos, int idx) +{ + *pos = 0x00; + + switch (idx) { + case 0: /* Bits 0-7 */ + break; + case 1: /* Bits 8-15 */ + break; + case 2: /* Bits 16-23 */ +#ifdef CONFIG_WNM + *pos |= 0x02; /* Bit 17 - WNM-Sleep Mode */ + *pos |= 0x08; /* Bit 19 - BSS Transition */ +#endif /* CONFIG_WNM */ + break; + case 3: /* Bits 24-31 */ +#ifdef CONFIG_WNM + *pos |= 0x02; /* Bit 25 - SSID List */ +#endif /* CONFIG_WNM */ +#ifdef CONFIG_INTERWORKING + if (wpa_s->conf->interworking) + *pos |= 0x80; /* Bit 31 - Interworking */ +#endif /* CONFIG_INTERWORKING */ + break; + case 4: /* Bits 32-39 */ +#ifdef CONFIG_INTERWORKING + if (wpa_s->drv_flags / WPA_DRIVER_FLAGS_QOS_MAPPING) + *pos |= 0x01; /* Bit 32 - QoS Map */ +#endif /* CONFIG_INTERWORKING */ + break; + case 5: /* Bits 40-47 */ + break; + case 6: /* Bits 48-55 */ + break; + } +} + + +int wpas_build_ext_capab(struct wpa_supplicant *wpa_s, u8 *buf) +{ + u8 *pos = buf; + u8 len = 4, i; + + if (len < wpa_s->extended_capa_len) + len = wpa_s->extended_capa_len; + + *pos++ = WLAN_EID_EXT_CAPAB; + *pos++ = len; + for (i = 0; i < len; i++, pos++) { + wpas_ext_capab_byte(wpa_s, pos, i); + + if (i < wpa_s->extended_capa_len) { + *pos &= ~wpa_s->extended_capa_mask[i]; + *pos |= wpa_s->extended_capa[i]; + } + } + + while (len > 0 && buf[1 + len] == 0) { + len--; + buf[1] = len; + } + if (len == 0) + return 0; + + return 2 + len; +} + + +static int wpas_valid_bss(struct wpa_supplicant *wpa_s, + struct wpa_bss *test_bss) +{ + struct wpa_bss *bss; + + dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) { + if (bss == test_bss) + return 1; + } + + return 0; +} + + +static int wpas_valid_ssid(struct wpa_supplicant *wpa_s, + struct wpa_ssid *test_ssid) +{ + struct wpa_ssid *ssid; + + for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) { + if (ssid == test_ssid) + return 1; + } + + return 0; +} + + +int wpas_valid_bss_ssid(struct wpa_supplicant *wpa_s, struct wpa_bss *test_bss, + struct wpa_ssid *test_ssid) +{ + if (test_bss && !wpas_valid_bss(wpa_s, test_bss)) + return 0; + + return test_ssid == NULL || wpas_valid_ssid(wpa_s, test_ssid); +} + + +void wpas_connect_work_free(struct wpa_connect_work *cwork) +{ + if (cwork == NULL) + return; + os_free(cwork); +} + + +void wpas_connect_work_done(struct wpa_supplicant *wpa_s) +{ + struct wpa_connect_work *cwork; + struct wpa_radio_work *work = wpa_s->connect_work; + + if (!work) + return; + + wpa_s->connect_work = NULL; + cwork = work->ctx; + work->ctx = NULL; + wpas_connect_work_free(cwork); + radio_work_done(work); +} + + +static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit); + /** * wpa_supplicant_associate - Request association * @wpa_s: Pointer to wpa_supplicant data @@ -1113,16 +1320,7 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, struct wpa_ssid *ssid) { - u8 wpa_ie[200]; - size_t wpa_ie_len; - int use_crypt, ret, i, bssid_changed; - int algs = WPA_AUTH_ALG_OPEN; - enum wpa_cipher cipher_pairwise, cipher_group; - struct wpa_driver_associate_params params; - int wep_keys_set = 0; - struct wpa_driver_capa capa; - int assoc_failed = 0; - struct wpa_ssid *old_ssid; + struct wpa_connect_work *cwork; #ifdef CONFIG_IBSS_RSN ibss_rsn_deinit(wpa_s->ibss_rsn); @@ -1137,7 +1335,12 @@ "mode"); return; } - wpa_supplicant_create_ap(wpa_s, ssid); + if (wpa_supplicant_create_ap(wpa_s, ssid) < 0) { + wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED); + if (ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION) + wpas_p2p_ap_setup_failed(wpa_s); + return; + } wpa_s->current_bss = bss; #else /* CONFIG_AP */ wpa_msg(wpa_s, MSG_ERROR, "AP mode support not included in " @@ -1158,8 +1361,61 @@ return; } + if (wpa_s->connect_work) { + wpa_dbg(wpa_s, MSG_DEBUG, "Reject wpa_supplicant_associate() call since connect_work exist"); + return; + } + + cwork = os_zalloc(sizeof(*cwork)); + if (cwork == NULL) + return; + + cwork->bss = bss; + cwork->ssid = ssid; + + if (radio_add_work(wpa_s, bss ? bss->freq : 0, "connect", 1, + wpas_start_assoc_cb, cwork) < 0) { + os_free(cwork); + } +} + + +static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit) +{ + struct wpa_connect_work *cwork = work->ctx; + struct wpa_bss *bss = cwork->bss; + struct wpa_ssid *ssid = cwork->ssid; + struct wpa_supplicant *wpa_s = work->wpa_s; + u8 wpa_ie[200]; + size_t wpa_ie_len; + int use_crypt, ret, i, bssid_changed; + int algs = WPA_AUTH_ALG_OPEN; + unsigned int cipher_pairwise, cipher_group; + struct wpa_driver_associate_params params; + int wep_keys_set = 0; + int assoc_failed = 0; + struct wpa_ssid *old_ssid; +#ifdef CONFIG_HT_OVERRIDES + struct ieee80211_ht_capabilities htcaps; + struct ieee80211_ht_capabilities htcaps_mask; +#endif /* CONFIG_HT_OVERRIDES */ + + if (deinit) { + wpas_connect_work_free(cwork); + return; + } + + wpa_s->connect_work = work; + + if (!wpas_valid_bss_ssid(wpa_s, bss, ssid)) { + wpa_dbg(wpa_s, MSG_DEBUG, "BSS/SSID entry for association not valid anymore - drop connection attempt"); + wpas_connect_work_done(wpa_s); + return; + } + os_memset(¶ms, 0, sizeof(params)); wpa_s->reassociate = 0; + wpa_s->eap_expected_failure = 0; if (bss && !wpas_driver_bss_selection(wpa_s)) { #ifdef CONFIG_IEEE80211R const u8 *ie, *md = NULL; @@ -1188,7 +1444,7 @@ (ssid->key_mgmt & WPA_KEY_MGMT_WPS)) { /* Use ap_scan==1 style network selection to find the network */ - wpa_s->scan_req = 2; + wpa_s->scan_req = MANUAL_SCAN_REQ; wpa_s->reassociate = 1; wpa_supplicant_req_scan(wpa_s, 0, 0); return; @@ -1224,17 +1480,14 @@ if (bss && (wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE) || wpa_bss_get_ie(bss, WLAN_EID_RSN)) && - (ssid->key_mgmt & (WPA_KEY_MGMT_IEEE8021X | WPA_KEY_MGMT_PSK | - WPA_KEY_MGMT_FT_IEEE8021X | - WPA_KEY_MGMT_FT_PSK | - WPA_KEY_MGMT_IEEE8021X_SHA256 | - WPA_KEY_MGMT_PSK_SHA256))) { + wpa_key_mgmt_wpa(ssid->key_mgmt)) { int try_opportunistic; - try_opportunistic = ssid->proactive_key_caching && + try_opportunistic = (ssid->proactive_key_caching < 0 ? + wpa_s->conf->okc : + ssid->proactive_key_caching) && (ssid->proto & WPA_PROTO_RSN); if (pmksa_cache_set_current(wpa_s->wpa, NULL, bss->bssid, - wpa_s->current_ssid, - try_opportunistic) == 0) + ssid, try_opportunistic) == 0) eapol_sm_notify_pmkid_attempt(wpa_s->eapol, 1); wpa_ie_len = sizeof(wpa_ie); if (wpa_supplicant_set_suites(wpa_s, bss, ssid, @@ -1243,11 +1496,17 @@ "key management and encryption suites"); return; } - } else if (ssid->key_mgmt & - (WPA_KEY_MGMT_PSK | WPA_KEY_MGMT_IEEE8021X | - WPA_KEY_MGMT_WPA_NONE | WPA_KEY_MGMT_FT_PSK | - WPA_KEY_MGMT_FT_IEEE8021X | WPA_KEY_MGMT_PSK_SHA256 | - WPA_KEY_MGMT_IEEE8021X_SHA256)) { + } else if ((ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA) && bss && + wpa_key_mgmt_wpa_ieee8021x(ssid->key_mgmt)) { + /* + * Both WPA and non-WPA IEEE 802.1X enabled in configuration - + * use non-WPA since the scan results did not indicate that the + * AP is using WPA or WPA2. + */ + wpa_supplicant_set_non_wpa_policy(wpa_s, ssid); + wpa_ie_len = 0; + wpa_s->wpa_proto = 0; + } else if (wpa_key_mgmt_wpa_any(ssid->key_mgmt)) { wpa_ie_len = sizeof(wpa_ie); if (wpa_supplicant_set_suites(wpa_s, NULL, ssid, wpa_ie, &wpa_ie_len)) { @@ -1306,28 +1565,51 @@ "disallows" : "allows"); } } + + os_memset(wpa_s->p2p_ip_addr_info, 0, sizeof(wpa_s->p2p_ip_addr_info)); #endif /* CONFIG_P2P */ -#ifdef CONFIG_INTERWORKING - if (wpa_s->conf->interworking) { - u8 *pos = wpa_ie; - if (wpa_ie_len > 0 && pos[0] == WLAN_EID_RSN) - pos += 2 + pos[1]; - os_memmove(pos + 6, pos, wpa_ie_len - (pos - wpa_ie)); - wpa_ie_len += 6; - *pos++ = WLAN_EID_EXT_CAPAB; - *pos++ = 4; - *pos++ = 0x00; - *pos++ = 0x00; - *pos++ = 0x00; - *pos++ = 0x80; /* Bit 31 - Interworking */ +#ifdef CONFIG_HS20 + if (is_hs20_network(wpa_s, ssid, bss)) { + struct wpabuf *hs20; + hs20 = wpabuf_alloc(20); + if (hs20) { + wpas_hs20_add_indication(hs20); + os_memcpy(wpa_ie + wpa_ie_len, wpabuf_head(hs20), + wpabuf_len(hs20)); + wpa_ie_len += wpabuf_len(hs20); + wpabuf_free(hs20); + } + } +#endif /* CONFIG_HS20 */ + + /* + * Workaround: Add Extended Capabilities element only if the AP + * included this element in Beacon/Probe Response frames. Some older + * APs seem to have interoperability issues if this element is + * included, so while the standard may require us to include the + * element in all cases, it is justifiable to skip it to avoid + * interoperability issues. + */ + if (!bss || wpa_bss_get_ie(bss, WLAN_EID_EXT_CAPAB)) { + u8 ext_capab[10]; + int ext_capab_len; + ext_capab_len = wpas_build_ext_capab(wpa_s, ext_capab); + if (ext_capab_len > 0) { + u8 *pos = wpa_ie; + if (wpa_ie_len > 0 && pos[0] == WLAN_EID_RSN) + pos += 2 + pos[1]; + os_memmove(pos + ext_capab_len, pos, + wpa_ie_len - (pos - wpa_ie)); + wpa_ie_len += ext_capab_len; + os_memcpy(pos, ext_capab, ext_capab_len); + } } -#endif /* CONFIG_INTERWORKING */ wpa_clear_keys(wpa_s, bss ? bss->bssid : NULL); use_crypt = 1; - cipher_pairwise = cipher_suite2driver(wpa_s->pairwise_cipher); - cipher_group = cipher_suite2driver(wpa_s->group_cipher); + cipher_pairwise = wpa_s->pairwise_cipher; + cipher_group = wpa_s->group_cipher; if (wpa_s->key_mgmt == WPA_KEY_MGMT_NONE || wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA) { if (wpa_s->key_mgmt == WPA_KEY_MGMT_NONE) @@ -1351,7 +1633,7 @@ /* Assume that dynamic WEP-104 keys will be used and * set cipher suites in order for drivers to expect * encryption. */ - cipher_pairwise = cipher_group = CIPHER_WEP104; + cipher_pairwise = cipher_group = WPA_CIPHER_WEP104; } } #endif /* IEEE8021X_EAPOL */ @@ -1365,7 +1647,12 @@ if (bss) { params.ssid = bss->ssid; params.ssid_len = bss->ssid_len; - if (!wpas_driver_bss_selection(wpa_s)) { + if (!wpas_driver_bss_selection(wpa_s) || ssid->bssid_set) { + wpa_printf(MSG_DEBUG, "Limit connection to BSSID " + MACSTR " freq=%u MHz based on scan results " + "(bssid_set=%d)", + MAC2STR(bss->bssid), bss->freq, + ssid->bssid_set); params.bssid = bss->bssid; params.freq = bss->freq; } @@ -1373,6 +1660,13 @@ params.ssid = ssid->ssid; params.ssid_len = ssid->ssid_len; } + + if (ssid->mode == WPAS_MODE_IBSS && ssid->bssid_set && + wpa_s->conf->ap_scan == 2) { + params.bssid = ssid->bssid; + params.fixed_bssid = 1; + } + if (ssid->mode == WPAS_MODE_IBSS && ssid->frequency > 0 && params.freq == 0) params.freq = ssid->frequency; /* Initial channel for IBSS */ @@ -1380,10 +1674,11 @@ params.wpa_ie_len = wpa_ie_len; params.pairwise_suite = cipher_pairwise; params.group_suite = cipher_group; - params.key_mgmt_suite = key_mgmt2driver(wpa_s->key_mgmt); + params.key_mgmt_suite = wpa_s->key_mgmt; params.wpa_proto = wpa_s->wpa_proto; params.auth_alg = algs; params.mode = ssid->mode; + params.bg_scan_period = ssid->bg_scan_period; for (i = 0; i < NUM_WEP_KEYS; i++) { if (ssid->wep_key_len[i]) params.wep_key[i] = ssid->wep_key[i]; @@ -1392,8 +1687,8 @@ params.wep_tx_keyidx = ssid->wep_tx_keyidx; if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE) && - (params.key_mgmt_suite == KEY_MGMT_PSK || - params.key_mgmt_suite == KEY_MGMT_FT_PSK)) { + (params.key_mgmt_suite == WPA_KEY_MGMT_PSK || + params.key_mgmt_suite == WPA_KEY_MGMT_FT_PSK)) { params.passphrase = ssid->passphrase; if (ssid->psk_set) params.psk = ssid->psk; @@ -1402,8 +1697,10 @@ params.drop_unencrypted = use_crypt; #ifdef CONFIG_IEEE80211W - params.mgmt_frame_protection = ssid->ieee80211w; - if (ssid->ieee80211w != NO_MGMT_FRAME_PROTECTION && bss) { + params.mgmt_frame_protection = + ssid->ieee80211w == MGMT_FRAME_PROTECTION_DEFAULT ? + wpa_s->conf->pmf : ssid->ieee80211w; + if (params.mgmt_frame_protection != NO_MGMT_FRAME_PROTECTION && bss) { const u8 *rsn = wpa_bss_get_ie(bss, WLAN_EID_RSN); struct wpa_ie_data ie; if (rsn && wpa_parse_wpa_ie(rsn, 2 + rsn[1], &ie) == 0 && @@ -1424,6 +1721,33 @@ else params.uapsd = -1; +#ifdef CONFIG_HT_OVERRIDES + os_memset(&htcaps, 0, sizeof(htcaps)); + os_memset(&htcaps_mask, 0, sizeof(htcaps_mask)); + params.htcaps = (u8 *) &htcaps; + params.htcaps_mask = (u8 *) &htcaps_mask; + wpa_supplicant_apply_ht_overrides(wpa_s, ssid, ¶ms); +#endif /* CONFIG_HT_OVERRIDES */ + +#ifdef CONFIG_P2P + /* + * If multi-channel concurrency is not supported, check for any + * frequency conflict. In case of any frequency conflict, remove the + * least prioritized connection. + */ + if (wpa_s->num_multichan_concurrent < 2) { + int freq = wpa_drv_shared_freq(wpa_s); + if (freq > 0 && freq != params.freq) { + wpa_printf(MSG_DEBUG, "Shared interface with conflicting frequency found (%d != %d)", + freq, params.freq); + if (wpas_p2p_handle_frequency_conflicts(wpa_s, + params.freq, + ssid) < 0) + return; + } + } +#endif /* CONFIG_P2P */ + ret = wpa_drv_associate(wpa_s, ¶ms); if (ret < 0) { wpa_msg(wpa_s, MSG_INFO, "Association request to the driver " @@ -1435,6 +1759,7 @@ * succeed. */ wpas_connection_failed(wpa_s, wpa_s->pending_bssid); + wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED); os_memset(wpa_s->pending_bssid, 0, ETH_ALEN); return; } @@ -1475,8 +1800,8 @@ wpa_supplicant_req_auth_timeout(wpa_s, timeout, 0); } - if (wep_keys_set && wpa_drv_get_capa(wpa_s, &capa) == 0 && - capa.flags & WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC) { + if (wep_keys_set && + (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC)) { /* Set static WEP keys again */ wpa_set_wep_keys(wpa_s, ssid); } @@ -1504,10 +1829,8 @@ struct wpa_ssid *old_ssid; wpa_clear_keys(wpa_s, addr); - wpa_supplicant_mark_disassoc(wpa_s); old_ssid = wpa_s->current_ssid; - wpa_s->current_ssid = NULL; - wpa_s->current_bss = NULL; + wpa_supplicant_mark_disassoc(wpa_s); wpa_sm_set_config(wpa_s->wpa, NULL); eapol_sm_notify_config(wpa_s->eapol, NULL, NULL); if (old_ssid != wpa_s->current_ssid) @@ -1517,28 +1840,6 @@ /** - * wpa_supplicant_disassociate - Disassociate the current connection - * @wpa_s: Pointer to wpa_supplicant data - * @reason_code: IEEE 802.11 reason code for the disassociate frame - * - * This function is used to request %wpa_supplicant to disassociate with the - * current AP. - */ -void wpa_supplicant_disassociate(struct wpa_supplicant *wpa_s, - int reason_code) -{ - u8 *addr = NULL; - - if (!is_zero_ether_addr(wpa_s->bssid)) { - wpa_drv_disassociate(wpa_s, wpa_s->bssid, reason_code); - addr = wpa_s->bssid; - } - - wpa_supplicant_clear_connection(wpa_s, addr); -} - - -/** * wpa_supplicant_deauthenticate - Deauthenticate the current connection * @wpa_s: Pointer to wpa_supplicant data * @reason_code: IEEE 802.11 reason code for the deauthenticate frame @@ -1550,18 +1851,69 @@ int reason_code) { u8 *addr = NULL; + union wpa_event_data event; + int zero_addr = 0; - if (!is_zero_ether_addr(wpa_s->bssid)) { - wpa_drv_deauthenticate(wpa_s, wpa_s->bssid, reason_code); + wpa_dbg(wpa_s, MSG_DEBUG, "Request to deauthenticate - bssid=" MACSTR + " pending_bssid=" MACSTR " reason=%d state=%s", + MAC2STR(wpa_s->bssid), MAC2STR(wpa_s->pending_bssid), + reason_code, wpa_supplicant_state_txt(wpa_s->wpa_state)); + + if (!is_zero_ether_addr(wpa_s->bssid)) + addr = wpa_s->bssid; + else if (!is_zero_ether_addr(wpa_s->pending_bssid) && + (wpa_s->wpa_state == WPA_AUTHENTICATING || + wpa_s->wpa_state == WPA_ASSOCIATING)) + addr = wpa_s->pending_bssid; + else if (wpa_s->wpa_state == WPA_ASSOCIATING) { + /* + * When using driver-based BSS selection, we may not know the + * BSSID with which we are currently trying to associate. We + * need to notify the driver of this disconnection even in such + * a case, so use the all zeros address here. + */ addr = wpa_s->bssid; + zero_addr = 1; + } + +#ifdef CONFIG_TDLS + wpa_tdls_teardown_peers(wpa_s->wpa); +#endif /* CONFIG_TDLS */ + + if (addr) { + wpa_drv_deauthenticate(wpa_s, addr, reason_code); + os_memset(&event, 0, sizeof(event)); + event.deauth_info.reason_code = (u16) reason_code; + event.deauth_info.locally_generated = 1; + wpa_supplicant_event(wpa_s, EVENT_DEAUTH, &event); + if (zero_addr) + addr = NULL; } wpa_supplicant_clear_connection(wpa_s, addr); } +static void wpa_supplicant_enable_one_network(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid) +{ + if (!ssid || !ssid->disabled || ssid->disabled == 2) + return; -/** - * wpa_supplicant_enable_network - Mark a configured network as enabled + ssid->disabled = 0; + wpas_clear_temp_disabled(wpa_s, ssid, 1); + wpas_notify_network_enabled_changed(wpa_s, ssid); + + /* + * Try to reassociate since there is no current configuration and a new + * network was made available. + */ + if (!wpa_s->current_ssid && !wpa_s->disconnected) + wpa_s->reassociate = 1; +} + + +/** + * wpa_supplicant_enable_network - Mark a configured network as enabled * @wpa_s: wpa_supplicant structure for a network interface * @ssid: wpa_ssid structure for a configured network or %NULL * @@ -1570,45 +1922,21 @@ void wpa_supplicant_enable_network(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) { - struct wpa_ssid *other_ssid; - int was_disabled; - if (ssid == NULL) { - for (other_ssid = wpa_s->conf->ssid; other_ssid; - other_ssid = other_ssid->next) { - if (other_ssid->disabled == 2) - continue; /* do not change persistent P2P group - * data */ - if (other_ssid == wpa_s->current_ssid && - other_ssid->disabled) - wpa_s->reassociate = 1; - - was_disabled = other_ssid->disabled; - - other_ssid->disabled = 0; + for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) + wpa_supplicant_enable_one_network(wpa_s, ssid); + } else + wpa_supplicant_enable_one_network(wpa_s, ssid); - if (was_disabled != other_ssid->disabled) - wpas_notify_network_enabled_changed( - wpa_s, other_ssid); - } - if (wpa_s->reassociate) - wpa_supplicant_req_scan(wpa_s, 0, 0); - } else if (ssid->disabled && ssid->disabled != 2) { - if (wpa_s->current_ssid == NULL) { - /* - * Try to reassociate since there is no current - * configuration and a new network was made available. - */ - wpa_s->reassociate = 1; - wpa_supplicant_req_scan(wpa_s, 0, 0); + if (wpa_s->reassociate && !wpa_s->disconnected) { + if (wpa_s->sched_scanning) { + wpa_printf(MSG_DEBUG, "Stop ongoing sched_scan to add " + "new network to scan filters"); + wpa_supplicant_cancel_sched_scan(wpa_s); } - was_disabled = ssid->disabled; - - ssid->disabled = 0; - - if (was_disabled != ssid->disabled) - wpas_notify_network_enabled_changed(wpa_s, ssid); + if (wpa_supplicant_fast_associate(wpa_s) != 1) + wpa_supplicant_req_scan(wpa_s, 0, 0); } } @@ -1627,6 +1955,9 @@ int was_disabled; if (ssid == NULL) { + if (wpa_s->sched_scanning) + wpa_supplicant_cancel_sched_scan(wpa_s); + for (other_ssid = wpa_s->conf->ssid; other_ssid; other_ssid = other_ssid->next) { was_disabled = other_ssid->disabled; @@ -1641,19 +1972,26 @@ wpa_s, other_ssid); } if (wpa_s->current_ssid) - wpa_supplicant_disassociate( + wpa_supplicant_deauthenticate( wpa_s, WLAN_REASON_DEAUTH_LEAVING); } else if (ssid->disabled != 2) { if (ssid == wpa_s->current_ssid) - wpa_supplicant_disassociate( + wpa_supplicant_deauthenticate( wpa_s, WLAN_REASON_DEAUTH_LEAVING); was_disabled = ssid->disabled; ssid->disabled = 1; - if (was_disabled != ssid->disabled) + if (was_disabled != ssid->disabled) { wpas_notify_network_enabled_changed(wpa_s, ssid); + if (wpa_s->sched_scanning) { + wpa_printf(MSG_DEBUG, "Stop ongoing sched_scan " + "to remove network from filters"); + wpa_supplicant_cancel_sched_scan(wpa_s); + wpa_supplicant_req_scan(wpa_s, 0, 0); + } + } } } @@ -1671,11 +2009,14 @@ int disconnected = 0; if (ssid && ssid != wpa_s->current_ssid && wpa_s->current_ssid) { - wpa_supplicant_disassociate( + wpa_supplicant_deauthenticate( wpa_s, WLAN_REASON_DEAUTH_LEAVING); disconnected = 1; } + if (ssid) + wpas_clear_temp_disabled(wpa_s, ssid, 1); + /* * Mark all other networks disabled or mark all networks enabled if no * network specified. @@ -1687,6 +2028,8 @@ continue; /* do not change persistent P2P group data */ other_ssid->disabled = ssid ? (ssid->id != other_ssid->id) : 0; + if (was_disabled && !other_ssid->disabled) + wpas_clear_temp_disabled(wpa_s, other_ssid, 0); if (was_disabled != other_ssid->disabled) wpas_notify_network_enabled_changed(wpa_s, other_ssid); @@ -1699,10 +2042,16 @@ return; } + if (ssid) { + wpa_s->current_ssid = ssid; + eapol_sm_notify_config(wpa_s->eapol, NULL, NULL); + } wpa_s->connect_without_scan = NULL; wpa_s->disconnected = 0; wpa_s->reassociate = 1; - wpa_supplicant_req_scan(wpa_s, 0, disconnected ? 100000 : 0); + + if (wpa_supplicant_fast_associate(wpa_s) != 1) + wpa_supplicant_req_scan(wpa_s, 0, disconnected ? 100000 : 0); if (ssid) wpas_notify_network_selected(wpa_s, ssid); @@ -1710,6 +2059,59 @@ /** + * wpas_set_pkcs11_engine_and_module_path - Set PKCS #11 engine and module path + * @wpa_s: wpa_supplicant structure for a network interface + * @pkcs11_engine_path: PKCS #11 engine path or NULL + * @pkcs11_module_path: PKCS #11 module path or NULL + * Returns: 0 on success; -1 on failure + * + * Sets the PKCS #11 engine and module path. Both have to be NULL or a valid + * path. If resetting the EAPOL state machine with the new PKCS #11 engine and + * module path fails the paths will be reset to the default value (NULL). + */ +int wpas_set_pkcs11_engine_and_module_path(struct wpa_supplicant *wpa_s, + const char *pkcs11_engine_path, + const char *pkcs11_module_path) +{ + char *pkcs11_engine_path_copy = NULL; + char *pkcs11_module_path_copy = NULL; + + if (pkcs11_engine_path != NULL) { + pkcs11_engine_path_copy = os_strdup(pkcs11_engine_path); + if (pkcs11_engine_path_copy == NULL) + return -1; + } + if (pkcs11_module_path != NULL) { + pkcs11_module_path_copy = os_strdup(pkcs11_module_path); + if (pkcs11_module_path_copy == NULL) { + os_free(pkcs11_engine_path_copy); + return -1; + } + } + + os_free(wpa_s->conf->pkcs11_engine_path); + os_free(wpa_s->conf->pkcs11_module_path); + wpa_s->conf->pkcs11_engine_path = pkcs11_engine_path_copy; + wpa_s->conf->pkcs11_module_path = pkcs11_module_path_copy; + + wpa_sm_set_eapol(wpa_s->wpa, NULL); + eapol_sm_deinit(wpa_s->eapol); + wpa_s->eapol = NULL; + if (wpa_supplicant_init_eapol(wpa_s)) { + /* Error -> Reset paths to the default value (NULL) once. */ + if (pkcs11_engine_path != NULL && pkcs11_module_path != NULL) + wpas_set_pkcs11_engine_and_module_path(wpa_s, NULL, + NULL); + + return -1; + } + wpa_sm_set_eapol(wpa_s->wpa, wpa_s->eapol); + + return 0; +} + + +/** * wpa_supplicant_set_ap_scan - Set AP scan mode for interface * @wpa_s: wpa_supplicant structure for a network interface * @ap_scan: AP scan mode @@ -1791,6 +2193,29 @@ /** + * wpa_supplicant_set_scan_interval - Set scan interval + * @wpa_s: wpa_supplicant structure for a network interface + * @scan_interval: scan interval in seconds + * Returns: 0 if succeed or -1 if scan_interval has an invalid value + * + */ +int wpa_supplicant_set_scan_interval(struct wpa_supplicant *wpa_s, + int scan_interval) +{ + if (scan_interval < 0) { + wpa_msg(wpa_s, MSG_ERROR, "Invalid scan interval %d", + scan_interval); + return -1; + } + wpa_msg(wpa_s, MSG_DEBUG, "Setting scan interval: %d sec", + scan_interval); + wpa_supplicant_update_scan_int(wpa_s, scan_interval); + + return 0; +} + + +/** * wpa_supplicant_set_debug_params - Set global debug params * @global: wpa_global structure * @debug_level: debug level @@ -1865,14 +2290,14 @@ entry = wpa_s->conf->ssid; while (entry) { - if (!entry->disabled && + if (!wpas_network_disabled(wpa_s, entry) && ((ssid_len == entry->ssid_len && os_memcmp(ssid, entry->ssid, ssid_len) == 0) || wired) && (!entry->bssid_set || os_memcmp(bssid, entry->bssid, ETH_ALEN) == 0)) return entry; #ifdef CONFIG_WPS - if (!entry->disabled && + if (!wpas_network_disabled(wpa_s, entry) && (entry->key_mgmt & WPA_KEY_MGMT_WPS) && (entry->ssid == NULL || entry->ssid_len == 0) && (!entry->bssid_set || @@ -1880,7 +2305,7 @@ return entry; #endif /* CONFIG_WPS */ - if (!entry->disabled && entry->bssid_set && + if (!wpas_network_disabled(wpa_s, entry) && entry->bssid_set && entry->ssid_len == 0 && os_memcmp(bssid, entry->bssid, ETH_ALEN) == 0) return entry; @@ -1980,27 +2405,51 @@ wpa_dbg(wpa_s, MSG_DEBUG, "RX EAPOL from " MACSTR, MAC2STR(src_addr)); wpa_hexdump(MSG_MSGDUMP, "RX EAPOL", buf, len); - if (wpa_s->wpa_state < WPA_ASSOCIATED) { +#ifdef CONFIG_PEERKEY + if (wpa_s->wpa_state > WPA_ASSOCIATED && wpa_s->current_ssid && + wpa_s->current_ssid->peerkey && + !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE) && + wpa_sm_rx_eapol_peerkey(wpa_s->wpa, src_addr, buf, len) == 1) { + wpa_dbg(wpa_s, MSG_DEBUG, "RSN: Processed PeerKey EAPOL-Key"); + return; + } +#endif /* CONFIG_PEERKEY */ + + if (wpa_s->wpa_state < WPA_ASSOCIATED || + (wpa_s->last_eapol_matches_bssid && +#ifdef CONFIG_AP + !wpa_s->ap_iface && +#endif /* CONFIG_AP */ + os_memcmp(src_addr, wpa_s->bssid, ETH_ALEN) != 0)) { /* * There is possible race condition between receiving the * association event and the EAPOL frame since they are coming * through different paths from the driver. In order to avoid * issues in trying to process the EAPOL frame before receiving * association information, lets queue it for processing until - * the association event is received. + * the association event is received. This may also be needed in + * driver-based roaming case, so also use src_addr != BSSID as a + * trigger if we have previously confirmed that the + * Authenticator uses BSSID as the src_addr (which is not the + * case with wired IEEE 802.1X). */ wpa_dbg(wpa_s, MSG_DEBUG, "Not associated - Delay processing " - "of received EAPOL frame"); + "of received EAPOL frame (state=%s bssid=" MACSTR ")", + wpa_supplicant_state_txt(wpa_s->wpa_state), + MAC2STR(wpa_s->bssid)); wpabuf_free(wpa_s->pending_eapol_rx); wpa_s->pending_eapol_rx = wpabuf_alloc_copy(buf, len); if (wpa_s->pending_eapol_rx) { - os_get_time(&wpa_s->pending_eapol_rx_time); + os_get_reltime(&wpa_s->pending_eapol_rx_time); os_memcpy(wpa_s->pending_eapol_rx_src, src_addr, ETH_ALEN); } return; } + wpa_s->last_eapol_matches_bssid = + os_memcmp(src_addr, wpa_s->bssid, ETH_ALEN) == 0; + #ifdef CONFIG_AP if (wpa_s->ap_iface) { wpa_supplicant_ap_rx_eapol(wpa_s, src_addr, buf, len); @@ -2075,7 +2524,10 @@ const u8 *addr = wpa_drv_get_mac_addr(wpa_s); if (addr) os_memcpy(wpa_s->own_addr, addr, ETH_ALEN); - } else if (!(wpa_s->drv_flags & + } else if ((!wpa_s->p2p_mgmt || + !(wpa_s->drv_flags & + WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE)) && + !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_DEDICATED_INTERFACE)) { l2_packet_deinit(wpa_s->l2); wpa_s->l2 = l2_packet_init(wpa_s->ifname, @@ -2095,14 +2547,35 @@ return -1; } - wpa_dbg(wpa_s, MSG_DEBUG, "Own MAC address: " MACSTR, - MAC2STR(wpa_s->own_addr)); - wpa_sm_set_own_addr(wpa_s->wpa, wpa_s->own_addr); - return 0; } +static void wpa_supplicant_rx_eapol_bridge(void *ctx, const u8 *src_addr, + const u8 *buf, size_t len) +{ + struct wpa_supplicant *wpa_s = ctx; + const struct l2_ethhdr *eth; + + if (len < sizeof(*eth)) + return; + eth = (const struct l2_ethhdr *) buf; + + if (os_memcmp(eth->h_dest, wpa_s->own_addr, ETH_ALEN) != 0 && + !(eth->h_dest[0] & 0x01)) { + wpa_dbg(wpa_s, MSG_DEBUG, "RX EAPOL from " MACSTR " to " MACSTR + " (bridge - not for this interface - ignore)", + MAC2STR(src_addr), MAC2STR(eth->h_dest)); + return; + } + + wpa_dbg(wpa_s, MSG_DEBUG, "RX EAPOL from " MACSTR " to " MACSTR + " (bridge)", MAC2STR(src_addr), MAC2STR(eth->h_dest)); + wpa_supplicant_rx_eapol(wpa_s, src_addr, buf + sizeof(*eth), + len - sizeof(*eth)); +} + + /** * wpa_supplicant_driver_init - Initialize driver interface parameters * @wpa_s: Pointer to wpa_supplicant data @@ -2119,14 +2592,18 @@ if (wpa_supplicant_update_mac_addr(wpa_s) < 0) return -1; + wpa_dbg(wpa_s, MSG_DEBUG, "Own MAC address: " MACSTR, + MAC2STR(wpa_s->own_addr)); + wpa_sm_set_own_addr(wpa_s->wpa, wpa_s->own_addr); + if (wpa_s->bridge_ifname[0]) { wpa_dbg(wpa_s, MSG_DEBUG, "Receiving packets from bridge " "interface '%s'", wpa_s->bridge_ifname); wpa_s->l2_br = l2_packet_init(wpa_s->bridge_ifname, wpa_s->own_addr, ETH_P_EAPOL, - wpa_supplicant_rx_eapol, wpa_s, - 0); + wpa_supplicant_rx_eapol_bridge, + wpa_s, 1); if (wpa_s->l2_br == NULL) { wpa_msg(wpa_s, MSG_ERROR, "Failed to open l2_packet " "connection for the bridge interface '%s'", @@ -2145,7 +2622,13 @@ wpa_drv_flush_pmkid(wpa_s); wpa_s->prev_scan_ssid = WILDCARD_SSID_SCAN; - if (wpa_supplicant_enabled_networks(wpa_s->conf)) { + wpa_s->prev_scan_wildcard = 0; + + if (wpa_supplicant_enabled_networks(wpa_s)) { + if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) { + wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED); + interface_count = 0; + } if (wpa_supplicant_delayed_sched_scan(wpa_s, interface_count, 100000)) wpa_supplicant_req_scan(wpa_s, interface_count, @@ -2165,27 +2648,626 @@ } -static struct wpa_supplicant * wpa_supplicant_alloc(void) -{ - struct wpa_supplicant *wpa_s; +static struct wpa_supplicant * wpa_supplicant_alloc(void) +{ + struct wpa_supplicant *wpa_s; + + wpa_s = os_zalloc(sizeof(*wpa_s)); + if (wpa_s == NULL) + return NULL; + wpa_s->scan_req = INITIAL_SCAN_REQ; + wpa_s->scan_interval = 5; + wpa_s->new_connection = 1; + wpa_s->parent = wpa_s; + wpa_s->sched_scanning = 0; + + return wpa_s; +} + + +#ifdef CONFIG_HT_OVERRIDES + +static int wpa_set_htcap_mcs(struct wpa_supplicant *wpa_s, + struct ieee80211_ht_capabilities *htcaps, + struct ieee80211_ht_capabilities *htcaps_mask, + const char *ht_mcs) +{ + /* parse ht_mcs into hex array */ + int i; + const char *tmp = ht_mcs; + char *end = NULL; + + /* If ht_mcs is null, do not set anything */ + if (!ht_mcs) + return 0; + + /* This is what we are setting in the kernel */ + os_memset(&htcaps->supported_mcs_set, 0, IEEE80211_HT_MCS_MASK_LEN); + + wpa_msg(wpa_s, MSG_DEBUG, "set_htcap, ht_mcs -:%s:-", ht_mcs); + + for (i = 0; i < IEEE80211_HT_MCS_MASK_LEN; i++) { + errno = 0; + long v = strtol(tmp, &end, 16); + if (errno == 0) { + wpa_msg(wpa_s, MSG_DEBUG, + "htcap value[%i]: %ld end: %p tmp: %p", + i, v, end, tmp); + if (end == tmp) + break; + + htcaps->supported_mcs_set[i] = v; + tmp = end; + } else { + wpa_msg(wpa_s, MSG_ERROR, + "Failed to parse ht-mcs: %s, error: %s\n", + ht_mcs, strerror(errno)); + return -1; + } + } + + /* + * If we were able to parse any values, then set mask for the MCS set. + */ + if (i) { + os_memset(&htcaps_mask->supported_mcs_set, 0xff, + IEEE80211_HT_MCS_MASK_LEN - 1); + /* skip the 3 reserved bits */ + htcaps_mask->supported_mcs_set[IEEE80211_HT_MCS_MASK_LEN - 1] = + 0x1f; + } + + return 0; +} + + +static int wpa_disable_max_amsdu(struct wpa_supplicant *wpa_s, + struct ieee80211_ht_capabilities *htcaps, + struct ieee80211_ht_capabilities *htcaps_mask, + int disabled) +{ + u16 msk; + + wpa_msg(wpa_s, MSG_DEBUG, "set_disable_max_amsdu: %d", disabled); + + if (disabled == -1) + return 0; + + msk = host_to_le16(HT_CAP_INFO_MAX_AMSDU_SIZE); + htcaps_mask->ht_capabilities_info |= msk; + if (disabled) + htcaps->ht_capabilities_info &= msk; + else + htcaps->ht_capabilities_info |= msk; + + return 0; +} + + +static int wpa_set_ampdu_factor(struct wpa_supplicant *wpa_s, + struct ieee80211_ht_capabilities *htcaps, + struct ieee80211_ht_capabilities *htcaps_mask, + int factor) +{ + wpa_msg(wpa_s, MSG_DEBUG, "set_ampdu_factor: %d", factor); + + if (factor == -1) + return 0; + + if (factor < 0 || factor > 3) { + wpa_msg(wpa_s, MSG_ERROR, "ampdu_factor: %d out of range. " + "Must be 0-3 or -1", factor); + return -EINVAL; + } + + htcaps_mask->a_mpdu_params |= 0x3; /* 2 bits for factor */ + htcaps->a_mpdu_params &= ~0x3; + htcaps->a_mpdu_params |= factor & 0x3; + + return 0; +} + + +static int wpa_set_ampdu_density(struct wpa_supplicant *wpa_s, + struct ieee80211_ht_capabilities *htcaps, + struct ieee80211_ht_capabilities *htcaps_mask, + int density) +{ + wpa_msg(wpa_s, MSG_DEBUG, "set_ampdu_density: %d", density); + + if (density == -1) + return 0; + + if (density < 0 || density > 7) { + wpa_msg(wpa_s, MSG_ERROR, + "ampdu_density: %d out of range. Must be 0-7 or -1.", + density); + return -EINVAL; + } + + htcaps_mask->a_mpdu_params |= 0x1C; + htcaps->a_mpdu_params &= ~(0x1C); + htcaps->a_mpdu_params |= (density << 2) & 0x1C; + + return 0; +} + + +static int wpa_set_disable_ht40(struct wpa_supplicant *wpa_s, + struct ieee80211_ht_capabilities *htcaps, + struct ieee80211_ht_capabilities *htcaps_mask, + int disabled) +{ + /* Masking these out disables HT40 */ + u16 msk = host_to_le16(HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET | + HT_CAP_INFO_SHORT_GI40MHZ); + + wpa_msg(wpa_s, MSG_DEBUG, "set_disable_ht40: %d", disabled); + + if (disabled) + htcaps->ht_capabilities_info &= ~msk; + else + htcaps->ht_capabilities_info |= msk; + + htcaps_mask->ht_capabilities_info |= msk; + + return 0; +} + + +static int wpa_set_disable_sgi(struct wpa_supplicant *wpa_s, + struct ieee80211_ht_capabilities *htcaps, + struct ieee80211_ht_capabilities *htcaps_mask, + int disabled) +{ + /* Masking these out disables SGI */ + u16 msk = host_to_le16(HT_CAP_INFO_SHORT_GI20MHZ | + HT_CAP_INFO_SHORT_GI40MHZ); + + wpa_msg(wpa_s, MSG_DEBUG, "set_disable_sgi: %d", disabled); + + if (disabled) + htcaps->ht_capabilities_info &= ~msk; + else + htcaps->ht_capabilities_info |= msk; + + htcaps_mask->ht_capabilities_info |= msk; + + return 0; +} + + +void wpa_supplicant_apply_ht_overrides( + struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, + struct wpa_driver_associate_params *params) +{ + struct ieee80211_ht_capabilities *htcaps; + struct ieee80211_ht_capabilities *htcaps_mask; + + if (!ssid) + return; + + params->disable_ht = ssid->disable_ht; + if (!params->htcaps || !params->htcaps_mask) + return; + + htcaps = (struct ieee80211_ht_capabilities *) params->htcaps; + htcaps_mask = (struct ieee80211_ht_capabilities *) params->htcaps_mask; + wpa_set_htcap_mcs(wpa_s, htcaps, htcaps_mask, ssid->ht_mcs); + wpa_disable_max_amsdu(wpa_s, htcaps, htcaps_mask, + ssid->disable_max_amsdu); + wpa_set_ampdu_factor(wpa_s, htcaps, htcaps_mask, ssid->ampdu_factor); + wpa_set_ampdu_density(wpa_s, htcaps, htcaps_mask, ssid->ampdu_density); + wpa_set_disable_ht40(wpa_s, htcaps, htcaps_mask, ssid->disable_ht40); + wpa_set_disable_sgi(wpa_s, htcaps, htcaps_mask, ssid->disable_sgi); +} + +#endif /* CONFIG_HT_OVERRIDES */ + + +#ifdef CONFIG_VHT_OVERRIDES +void wpa_supplicant_apply_vht_overrides( + struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, + struct wpa_driver_associate_params *params) +{ + struct ieee80211_vht_capabilities *vhtcaps; + struct ieee80211_vht_capabilities *vhtcaps_mask; + + if (!ssid) + return; + + params->disable_vht = ssid->disable_vht; + + vhtcaps = (void *) params->vhtcaps; + vhtcaps_mask = (void *) params->vhtcaps_mask; + + if (!vhtcaps || !vhtcaps_mask) + return; + + vhtcaps->vht_capabilities_info = ssid->vht_capa; + vhtcaps_mask->vht_capabilities_info = ssid->vht_capa_mask; + +#define OVERRIDE_MCS(i) \ + if (ssid->vht_tx_mcs_nss_ ##i >= 0) { \ + vhtcaps_mask->vht_supported_mcs_set.tx_map |= \ + 3 << 2 * (i - 1); \ + vhtcaps->vht_supported_mcs_set.tx_map |= \ + ssid->vht_tx_mcs_nss_ ##i << 2 * (i - 1); \ + } \ + if (ssid->vht_rx_mcs_nss_ ##i >= 0) { \ + vhtcaps_mask->vht_supported_mcs_set.rx_map |= \ + 3 << 2 * (i - 1); \ + vhtcaps->vht_supported_mcs_set.rx_map |= \ + ssid->vht_rx_mcs_nss_ ##i << 2 * (i - 1); \ + } + + OVERRIDE_MCS(1); + OVERRIDE_MCS(2); + OVERRIDE_MCS(3); + OVERRIDE_MCS(4); + OVERRIDE_MCS(5); + OVERRIDE_MCS(6); + OVERRIDE_MCS(7); + OVERRIDE_MCS(8); +} +#endif /* CONFIG_VHT_OVERRIDES */ + + +static int pcsc_reader_init(struct wpa_supplicant *wpa_s) +{ +#ifdef PCSC_FUNCS + size_t len; + + if (!wpa_s->conf->pcsc_reader) + return 0; + + wpa_s->scard = scard_init(wpa_s->conf->pcsc_reader); + if (!wpa_s->scard) + return 1; + + if (wpa_s->conf->pcsc_pin && + scard_set_pin(wpa_s->scard, wpa_s->conf->pcsc_pin) < 0) { + scard_deinit(wpa_s->scard); + wpa_s->scard = NULL; + wpa_msg(wpa_s, MSG_ERROR, "PC/SC PIN validation failed"); + return -1; + } + + len = sizeof(wpa_s->imsi) - 1; + if (scard_get_imsi(wpa_s->scard, wpa_s->imsi, &len)) { + scard_deinit(wpa_s->scard); + wpa_s->scard = NULL; + wpa_msg(wpa_s, MSG_ERROR, "Could not read IMSI"); + return -1; + } + wpa_s->imsi[len] = '\0'; + + wpa_s->mnc_len = scard_get_mnc_len(wpa_s->scard); + + wpa_printf(MSG_DEBUG, "SCARD: IMSI %s (MNC length %d)", + wpa_s->imsi, wpa_s->mnc_len); + + wpa_sm_set_scard_ctx(wpa_s->wpa, wpa_s->scard); + eapol_sm_register_scard_ctx(wpa_s->eapol, wpa_s->scard); +#endif /* PCSC_FUNCS */ + + return 0; +} + + +int wpas_init_ext_pw(struct wpa_supplicant *wpa_s) +{ + char *val, *pos; + + ext_password_deinit(wpa_s->ext_pw); + wpa_s->ext_pw = NULL; + eapol_sm_set_ext_pw_ctx(wpa_s->eapol, NULL); + + if (!wpa_s->conf->ext_password_backend) + return 0; + + val = os_strdup(wpa_s->conf->ext_password_backend); + if (val == NULL) + return -1; + pos = os_strchr(val, ':'); + if (pos) + *pos++ = '\0'; + + wpa_printf(MSG_DEBUG, "EXT PW: Initialize backend '%s'", val); + + wpa_s->ext_pw = ext_password_init(val, pos); + os_free(val); + if (wpa_s->ext_pw == NULL) { + wpa_printf(MSG_DEBUG, "EXT PW: Failed to initialize backend"); + return -1; + } + eapol_sm_set_ext_pw_ctx(wpa_s->eapol, wpa_s->ext_pw); + + return 0; +} + + +static struct wpa_radio * radio_add_interface(struct wpa_supplicant *wpa_s, + const char *rn) +{ + struct wpa_supplicant *iface = wpa_s->global->ifaces; + struct wpa_radio *radio; + + while (rn && iface) { + radio = iface->radio; + if (radio && os_strcmp(rn, radio->name) == 0) { + wpa_printf(MSG_DEBUG, "Add interface %s to existing radio %s", + wpa_s->ifname, rn); + dl_list_add(&radio->ifaces, &wpa_s->radio_list); + return radio; + } + + iface = iface->next; + } + + wpa_printf(MSG_DEBUG, "Add interface %s to a new radio %s", + wpa_s->ifname, rn ? rn : "N/A"); + radio = os_zalloc(sizeof(*radio)); + if (radio == NULL) + return NULL; + + if (rn) + os_strlcpy(radio->name, rn, sizeof(radio->name)); + dl_list_init(&radio->ifaces); + dl_list_init(&radio->work); + dl_list_add(&radio->ifaces, &wpa_s->radio_list); + + return radio; +} + + +static void radio_work_free(struct wpa_radio_work *work) +{ + if (work->wpa_s->scan_work == work) { + /* This should not really happen. */ + wpa_dbg(work->wpa_s, MSG_INFO, "Freeing radio work '%s'@%p (started=%d) that is marked as scan_work", + work->type, work, work->started); + work->wpa_s->scan_work = NULL; + } + +#ifdef CONFIG_P2P + if (work->wpa_s->p2p_scan_work == work) { + /* This should not really happen. */ + wpa_dbg(work->wpa_s, MSG_INFO, "Freeing radio work '%s'@%p (started=%d) that is marked as p2p_scan_work", + work->type, work, work->started); + work->wpa_s->p2p_scan_work = NULL; + } +#endif /* CONFIG_P2P */ + + dl_list_del(&work->list); + os_free(work); +} + + +static void radio_start_next_work(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_radio *radio = eloop_ctx; + struct wpa_radio_work *work; + struct os_reltime now, diff; + struct wpa_supplicant *wpa_s; + + work = dl_list_first(&radio->work, struct wpa_radio_work, list); + if (work == NULL) + return; + + if (work->started) + return; /* already started and still in progress */ + + wpa_s = dl_list_first(&radio->ifaces, struct wpa_supplicant, + radio_list); + if (wpa_s && wpa_s->external_scan_running) { + wpa_printf(MSG_DEBUG, "Delay radio work start until externally triggered scan completes"); + return; + } + + os_get_reltime(&now); + os_reltime_sub(&now, &work->time, &diff); + wpa_dbg(work->wpa_s, MSG_DEBUG, "Starting radio work '%s'@%p after %ld.%06ld second wait", + work->type, work, diff.sec, diff.usec); + work->started = 1; + work->time = now; + work->cb(work, 0); +} + + +void radio_remove_unstarted_work(struct wpa_supplicant *wpa_s, const char *type) +{ + struct wpa_radio_work *work, *tmp; + struct wpa_radio *radio = wpa_s->radio; + + dl_list_for_each_safe(work, tmp, &radio->work, struct wpa_radio_work, + list) { + if (type && (work->started || os_strcmp(type, work->type) != 0)) + continue; + if (work->started) { + wpa_dbg(wpa_s, MSG_DEBUG, "Leaving started radio work '%s'@%p in the list", + work->type, work); + continue; + } + wpa_dbg(wpa_s, MSG_DEBUG, "Remove unstarted radio work '%s'@%p", + work->type, work); + work->cb(work, 1); + radio_work_free(work); + } +} + + +static void radio_remove_interface(struct wpa_supplicant *wpa_s) +{ + struct wpa_radio *radio = wpa_s->radio; + + if (!radio) + return; + + wpa_printf(MSG_DEBUG, "Remove interface %s from radio %s", + wpa_s->ifname, radio->name); + dl_list_del(&wpa_s->radio_list); + if (!dl_list_empty(&radio->ifaces)) { + wpa_s->radio = NULL; + return; /* Interfaces remain for this radio */ + } + + wpa_printf(MSG_DEBUG, "Remove radio %s", radio->name); + radio_remove_unstarted_work(wpa_s, NULL); + eloop_cancel_timeout(radio_start_next_work, radio, NULL); + wpa_s->radio = NULL; + os_free(radio); +} + + +void radio_work_check_next(struct wpa_supplicant *wpa_s) +{ + struct wpa_radio *radio = wpa_s->radio; + + if (dl_list_empty(&radio->work)) + return; + eloop_cancel_timeout(radio_start_next_work, radio, NULL); + eloop_register_timeout(0, 0, radio_start_next_work, radio, NULL); +} + + +/** + * radio_add_work - Add a radio work item + * @wpa_s: Pointer to wpa_supplicant data + * @freq: Frequency of the offchannel operation in MHz or 0 + * @type: Unique identifier for each type of work + * @next: Force as the next work to be executed + * @cb: Callback function for indicating when radio is available + * @ctx: Context pointer for the work (work->ctx in cb()) + * Returns: 0 on success, -1 on failure + * + * This function is used to request time for an operation that requires + * exclusive radio control. Once the radio is available, the registered callback + * function will be called. radio_work_done() must be called once the exclusive + * radio operation has been completed, so that the radio is freed for other + * operations. The special case of deinit=1 is used to free the context data + * during interface removal. That does not allow the callback function to start + * the radio operation, i.e., it must free any resources allocated for the radio + * work and return. + * + * The @freq parameter can be used to indicate a single channel on which the + * offchannel operation will occur. This may allow multiple radio work + * operations to be performed in parallel if they apply for the same channel. + * Setting this to 0 indicates that the work item may use multiple channels or + * requires exclusive control of the radio. + */ +int radio_add_work(struct wpa_supplicant *wpa_s, unsigned int freq, + const char *type, int next, + void (*cb)(struct wpa_radio_work *work, int deinit), + void *ctx) +{ + struct wpa_radio_work *work; + int was_empty; + + work = os_zalloc(sizeof(*work)); + if (work == NULL) + return -1; + wpa_dbg(wpa_s, MSG_DEBUG, "Add radio work '%s'@%p", type, work); + os_get_reltime(&work->time); + work->freq = freq; + work->type = type; + work->wpa_s = wpa_s; + work->cb = cb; + work->ctx = ctx; + + was_empty = dl_list_empty(&wpa_s->radio->work); + if (next) + dl_list_add(&wpa_s->radio->work, &work->list); + else + dl_list_add_tail(&wpa_s->radio->work, &work->list); + if (was_empty) { + wpa_dbg(wpa_s, MSG_DEBUG, "First radio work item in the queue - schedule start immediately"); + radio_work_check_next(wpa_s); + } + + return 0; +} + + +/** + * radio_work_done - Indicate that a radio work item has been completed + * @work: Completed work + * + * This function is called once the callback function registered with + * radio_add_work() has completed its work. + */ +void radio_work_done(struct wpa_radio_work *work) +{ + struct wpa_supplicant *wpa_s = work->wpa_s; + struct os_reltime now, diff; + unsigned int started = work->started; + + os_get_reltime(&now); + os_reltime_sub(&now, &work->time, &diff); + wpa_dbg(wpa_s, MSG_DEBUG, "Radio work '%s'@%p %s in %ld.%06ld seconds", + work->type, work, started ? "done" : "canceled", + diff.sec, diff.usec); + radio_work_free(work); + if (started) + radio_work_check_next(wpa_s); +} + + +static int wpas_init_driver(struct wpa_supplicant *wpa_s, + struct wpa_interface *iface) +{ + const char *ifname, *driver, *rn; + + driver = iface->driver; +next_driver: + if (wpa_supplicant_set_driver(wpa_s, driver) < 0) + return -1; + + wpa_s->drv_priv = wpa_drv_init(wpa_s, wpa_s->ifname); + if (wpa_s->drv_priv == NULL) { + const char *pos; + pos = driver ? os_strchr(driver, ',') : NULL; + if (pos) { + wpa_dbg(wpa_s, MSG_DEBUG, "Failed to initialize " + "driver interface - try next driver wrapper"); + driver = pos + 1; + goto next_driver; + } + wpa_msg(wpa_s, MSG_ERROR, "Failed to initialize driver " + "interface"); + return -1; + } + if (wpa_drv_set_param(wpa_s, wpa_s->conf->driver_param) < 0) { + wpa_msg(wpa_s, MSG_ERROR, "Driver interface rejected " + "driver_param '%s'", wpa_s->conf->driver_param); + return -1; + } + + ifname = wpa_drv_get_ifname(wpa_s); + if (ifname && os_strcmp(ifname, wpa_s->ifname) != 0) { + wpa_dbg(wpa_s, MSG_DEBUG, "Driver interface replaced " + "interface name with '%s'", ifname); + os_strlcpy(wpa_s->ifname, ifname, sizeof(wpa_s->ifname)); + } + + if (wpa_s->driver->get_radio_name) + rn = wpa_s->driver->get_radio_name(wpa_s->drv_priv); + else + rn = NULL; + if (rn && rn[0] == '\0') + rn = NULL; - wpa_s = os_zalloc(sizeof(*wpa_s)); - if (wpa_s == NULL) - return NULL; - wpa_s->scan_req = 1; - wpa_s->scan_interval = 5; - wpa_s->new_connection = 1; - wpa_s->parent = wpa_s; - wpa_s->sched_scanning = 0; + wpa_s->radio = radio_add_interface(wpa_s, rn); + if (wpa_s->radio == NULL) + return -1; - return wpa_s; + return 0; } static int wpa_supplicant_init_iface(struct wpa_supplicant *wpa_s, struct wpa_interface *iface) { - const char *ifname, *driver; struct wpa_driver_capa capa; wpa_printf(MSG_DEBUG, "Initializing interface '%s' conf '%s' driver " @@ -2209,12 +3291,14 @@ #else /* CONFIG_BACKEND_FILE */ wpa_s->confname = os_strdup(iface->confname); #endif /* CONFIG_BACKEND_FILE */ - wpa_s->conf = wpa_config_read(wpa_s->confname); + wpa_s->conf = wpa_config_read(wpa_s->confname, NULL); if (wpa_s->conf == NULL) { wpa_printf(MSG_ERROR, "Failed to read or parse " "configuration '%s'.", wpa_s->confname); return -1; } + wpa_s->confanother = os_rel2abs_path(iface->confanother); + wpa_config_read(wpa_s->confanother, wpa_s->conf); /* * Override ctrl_interface and driver_param if set on command @@ -2231,6 +3315,11 @@ wpa_s->conf->driver_param = os_strdup(iface->driver_param); } + + if (iface->p2p_mgmt && !iface->ctrl_interface) { + os_free(wpa_s->conf->ctrl_interface); + wpa_s->conf->ctrl_interface = NULL; + } } else wpa_s->conf = wpa_config_alloc_empty(iface->ctrl_interface, iface->driver_param); @@ -2270,37 +3359,8 @@ * L2 receive handler so that association events are processed before * EAPOL-Key packets if both become available for the same select() * call. */ - driver = iface->driver; -next_driver: - if (wpa_supplicant_set_driver(wpa_s, driver) < 0) - return -1; - - wpa_s->drv_priv = wpa_drv_init(wpa_s, wpa_s->ifname); - if (wpa_s->drv_priv == NULL) { - const char *pos; - pos = driver ? os_strchr(driver, ',') : NULL; - if (pos) { - wpa_dbg(wpa_s, MSG_DEBUG, "Failed to initialize " - "driver interface - try next driver wrapper"); - driver = pos + 1; - goto next_driver; - } - wpa_msg(wpa_s, MSG_ERROR, "Failed to initialize driver " - "interface"); - return -1; - } - if (wpa_drv_set_param(wpa_s, wpa_s->conf->driver_param) < 0) { - wpa_msg(wpa_s, MSG_ERROR, "Driver interface rejected " - "driver_param '%s'", wpa_s->conf->driver_param); + if (wpas_init_driver(wpa_s, iface) < 0) return -1; - } - - ifname = wpa_drv_get_ifname(wpa_s); - if (ifname && os_strcmp(ifname, wpa_s->ifname) != 0) { - wpa_dbg(wpa_s, MSG_DEBUG, "Driver interface replaced " - "interface name with '%s'", ifname); - os_strlcpy(wpa_s->ifname, ifname, sizeof(wpa_s->ifname)); - } if (wpa_supplicant_init_wpa(wpa_s) < 0) return -1; @@ -2341,21 +3401,44 @@ if (wpa_drv_get_capa(wpa_s, &capa) == 0) { wpa_s->drv_capa_known = 1; wpa_s->drv_flags = capa.flags; + wpa_s->drv_enc = capa.enc; + wpa_s->probe_resp_offloads = capa.probe_resp_offloads; wpa_s->max_scan_ssids = capa.max_scan_ssids; wpa_s->max_sched_scan_ssids = capa.max_sched_scan_ssids; wpa_s->sched_scan_supported = capa.sched_scan_supported; wpa_s->max_match_sets = capa.max_match_sets; wpa_s->max_remain_on_chan = capa.max_remain_on_chan; wpa_s->max_stations = capa.max_stations; + wpa_s->extended_capa = capa.extended_capa; + wpa_s->extended_capa_mask = capa.extended_capa_mask; + wpa_s->extended_capa_len = capa.extended_capa_len; + wpa_s->num_multichan_concurrent = + capa.num_multichan_concurrent; } if (wpa_s->max_remain_on_chan == 0) wpa_s->max_remain_on_chan = 1000; + /* + * Only take p2p_mgmt parameters when P2P Device is supported. + * Doing it here as it determines whether l2_packet_init() will be done + * during wpa_supplicant_driver_init(). + */ + if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE) + wpa_s->p2p_mgmt = iface->p2p_mgmt; + else + iface->p2p_mgmt = 1; + + if (wpa_s->num_multichan_concurrent == 0) + wpa_s->num_multichan_concurrent = 1; + if (wpa_supplicant_driver_init(wpa_s) < 0) return -1; #ifdef CONFIG_TDLS - if (wpa_tdls_init(wpa_s->wpa)) + if ((!iface->p2p_mgmt || + !(wpa_s->drv_flags & + WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE)) && + wpa_tdls_init(wpa_s->wpa)) return -1; #endif /* CONFIG_TDLS */ @@ -2393,7 +3476,7 @@ } #ifdef CONFIG_P2P - if (wpas_p2p_init(wpa_s->global, wpa_s) < 0) { + if (iface->p2p_mgmt && wpas_p2p_init(wpa_s->global, wpa_s) < 0) { wpa_msg(wpa_s, MSG_ERROR, "Failed to init P2P"); return -1; } @@ -2402,13 +3485,35 @@ if (wpa_bss_init(wpa_s) < 0) return -1; +#ifdef CONFIG_EAP_PROXY +{ + size_t len; + wpa_s->mnc_len = eapol_sm_get_eap_proxy_imsi(wpa_s->eapol, wpa_s->imsi, + &len); + if (wpa_s->mnc_len > 0) { + wpa_s->imsi[len] = '\0'; + wpa_printf(MSG_DEBUG, "eap_proxy: IMSI %s (MNC length %d)", + wpa_s->imsi, wpa_s->mnc_len); + } else { + wpa_printf(MSG_DEBUG, "eap_proxy: IMSI not available"); + } +} +#endif /* CONFIG_EAP_PROXY */ + + if (pcsc_reader_init(wpa_s) < 0) + return -1; + + if (wpas_init_ext_pw(wpa_s) < 0) + return -1; + return 0; } static void wpa_supplicant_deinit_iface(struct wpa_supplicant *wpa_s, - int notify) + int notify, int terminate) { + wpa_s->disconnected = 1; if (wpa_s->drv_priv) { wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_DEAUTH_LEAVING); @@ -2419,11 +3524,37 @@ wpa_supplicant_cleanup(wpa_s); - if (notify) - wpas_notify_iface_removed(wpa_s); +#ifdef CONFIG_P2P + if (wpa_s == wpa_s->global->p2p_init_wpa_s && wpa_s->global->p2p) { + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Disable P2P since removing " + "the management interface is being removed"); + wpas_p2p_deinit_global(wpa_s->global); + } +#endif /* CONFIG_P2P */ + + wpas_ctrl_radio_work_flush(wpa_s); + radio_remove_interface(wpa_s); if (wpa_s->drv_priv) wpa_drv_deinit(wpa_s); + + if (notify) + wpas_notify_iface_removed(wpa_s); + + if (terminate) + wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_TERMINATING); + + if (wpa_s->ctrl_iface) { + wpa_supplicant_ctrl_iface_deinit(wpa_s->ctrl_iface); + wpa_s->ctrl_iface = NULL; + } + + if (wpa_s->conf != NULL) { + wpa_config_free(wpa_s->conf); + wpa_s->conf = NULL; + } + + os_free(wpa_s); } @@ -2473,15 +3604,13 @@ if (wpa_supplicant_init_iface(wpa_s, &t_iface)) { wpa_printf(MSG_DEBUG, "Failed to add interface %s", iface->ifname); - wpa_supplicant_deinit_iface(wpa_s, 0); - os_free(wpa_s); + wpa_supplicant_deinit_iface(wpa_s, 0, 0); return NULL; } /* Notify the control interfaces about new iface */ if (wpas_notify_iface_added(wpa_s)) { - wpa_supplicant_deinit_iface(wpa_s, 1); - os_free(wpa_s); + wpa_supplicant_deinit_iface(wpa_s, 1, 0); return NULL; } @@ -2492,6 +3621,7 @@ global->ifaces = wpa_s; wpa_dbg(wpa_s, MSG_DEBUG, "Added interface %s", wpa_s->ifname); + wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED); return wpa_s; } @@ -2509,7 +3639,8 @@ * %wpa_supplicant is terminated. */ int wpa_supplicant_remove_iface(struct wpa_global *global, - struct wpa_supplicant *wpa_s) + struct wpa_supplicant *wpa_s, + int terminate) { struct wpa_supplicant *prev; @@ -2529,8 +3660,9 @@ if (global->p2p_group_formation == wpa_s) global->p2p_group_formation = NULL; - wpa_supplicant_deinit_iface(wpa_s, 1); - os_free(wpa_s); + if (global->p2p_invite_group == wpa_s) + global->p2p_invite_group = NULL; + wpa_supplicant_deinit_iface(wpa_s, 1, terminate); return 0; } @@ -2619,6 +3751,14 @@ wpa_debug_open_file(params->wpa_debug_file_path); if (params->wpa_debug_syslog) wpa_debug_open_syslog(); + if (params->wpa_debug_tracing) { + ret = wpa_debug_open_linux_tracing(); + if (ret) { + wpa_printf(MSG_ERROR, + "Failed to enable trace logging"); + return NULL; + } + } ret = eap_register_methods(); if (ret) { @@ -2642,6 +3782,9 @@ if (params->ctrl_interface) global->params.ctrl_interface = os_strdup(params->ctrl_interface); + if (params->ctrl_interface_group) + global->params.ctrl_interface_group = + os_strdup(params->ctrl_interface_group); if (params->override_driver) global->params.override_driver = os_strdup(params->override_driver); @@ -2689,6 +3832,14 @@ return NULL; } +#ifdef CONFIG_WIFI_DISPLAY + if (wifi_display_init(global) < 0) { + wpa_printf(MSG_ERROR, "Failed to initialize Wi-Fi Display"); + wpa_supplicant_deinit(global); + return NULL; + } +#endif /* CONFIG_WIFI_DISPLAY */ + return global; } @@ -2740,12 +3891,12 @@ if (global == NULL) return; -#ifdef CONFIG_P2P - wpas_p2p_deinit_global(global); -#endif /* CONFIG_P2P */ +#ifdef CONFIG_WIFI_DISPLAY + wifi_display_deinit(global); +#endif /* CONFIG_WIFI_DISPLAY */ while (global->ifaces) - wpa_supplicant_remove_iface(global, global->ifaces); + wpa_supplicant_remove_iface(global, global->ifaces, 1); if (global->ctrl_iface) wpa_supplicant_global_ctrl_iface_deinit(global->ctrl_iface); @@ -2773,12 +3924,18 @@ os_free(global->params.pid_file); } os_free(global->params.ctrl_interface); + os_free(global->params.ctrl_interface_group); os_free(global->params.override_driver); os_free(global->params.override_ctrl_interface); + os_free(global->p2p_disallow_freq.range); + os_free(global->p2p_go_avoid_freq.range); + os_free(global->add_psk); + os_free(global); wpa_debug_close_syslog(); wpa_debug_close_file(); + wpa_debug_close_linux_tracing(); } @@ -2796,6 +3953,9 @@ } } + if (wpa_s->conf->changed_parameters & CFG_CHANGED_EXT_PW_BACKEND) + wpas_init_ext_pw(wpa_s); + #ifdef CONFIG_WPS wpas_wps_update_config(wpa_s); #endif /* CONFIG_WPS */ @@ -2862,11 +4022,24 @@ int count; int *freqs = NULL; + wpas_connect_work_done(wpa_s); + /* * Remove possible authentication timeout since the connection failed. */ eloop_cancel_timeout(wpa_supplicant_timeout, wpa_s, NULL); + if (wpa_s->disconnected) { + /* + * There is no point in blacklisting the AP if this event is + * generated based on local request to disconnect. + */ + wpa_dbg(wpa_s, MSG_DEBUG, "Ignore connection failure " + "indication since interface has been put into " + "disconnected state"); + return; + } + /* * Add the failed BSSID into the blacklist and speed up next scan * attempt if there could be other APs that could accept association. @@ -2898,6 +4071,18 @@ } } + /* + * Add previous failure count in case the temporary blacklist was + * cleared due to no other BSSes being available. + */ + count += wpa_s->extra_blacklist_count; + + if (count > 3 && wpa_s->current_ssid) { + wpa_printf(MSG_DEBUG, "Continuous association failures - " + "consider temporary network disabling"); + wpas_auth_failed(wpa_s); + } + switch (count) { case 1: timeout = 100; @@ -2908,10 +4093,17 @@ case 3: timeout = 1000; break; - default: + case 4: timeout = 5000; + break; + default: + timeout = 10000; + break; } + wpa_dbg(wpa_s, MSG_DEBUG, "Blacklist count %d --> request scan in %d " + "ms", count, timeout); + /* * TODO: if more than one possible AP is available in scan results, * could try the other ones before requesting a new scan. @@ -2926,3 +4118,334 @@ return wpa_s->conf->ap_scan == 2 || (wpa_s->drv_flags & WPA_DRIVER_FLAGS_BSS_SELECTION); } + + +#if defined(CONFIG_CTRL_IFACE) || defined(CONFIG_CTRL_IFACE_DBUS_NEW) +int wpa_supplicant_ctrl_iface_ctrl_rsp_handle(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid, + const char *field, + const char *value) +{ +#ifdef IEEE8021X_EAPOL + struct eap_peer_config *eap = &ssid->eap; + + wpa_printf(MSG_DEBUG, "CTRL_IFACE: response handle field=%s", field); + wpa_hexdump_ascii_key(MSG_DEBUG, "CTRL_IFACE: response value", + (const u8 *) value, os_strlen(value)); + + switch (wpa_supplicant_ctrl_req_from_string(field)) { + case WPA_CTRL_REQ_EAP_IDENTITY: + os_free(eap->identity); + eap->identity = (u8 *) os_strdup(value); + eap->identity_len = os_strlen(value); + eap->pending_req_identity = 0; + if (ssid == wpa_s->current_ssid) + wpa_s->reassociate = 1; + break; + case WPA_CTRL_REQ_EAP_PASSWORD: + os_free(eap->password); + eap->password = (u8 *) os_strdup(value); + eap->password_len = os_strlen(value); + eap->pending_req_password = 0; + if (ssid == wpa_s->current_ssid) + wpa_s->reassociate = 1; + break; + case WPA_CTRL_REQ_EAP_NEW_PASSWORD: + os_free(eap->new_password); + eap->new_password = (u8 *) os_strdup(value); + eap->new_password_len = os_strlen(value); + eap->pending_req_new_password = 0; + if (ssid == wpa_s->current_ssid) + wpa_s->reassociate = 1; + break; + case WPA_CTRL_REQ_EAP_PIN: + os_free(eap->pin); + eap->pin = os_strdup(value); + eap->pending_req_pin = 0; + if (ssid == wpa_s->current_ssid) + wpa_s->reassociate = 1; + break; + case WPA_CTRL_REQ_EAP_OTP: + os_free(eap->otp); + eap->otp = (u8 *) os_strdup(value); + eap->otp_len = os_strlen(value); + os_free(eap->pending_req_otp); + eap->pending_req_otp = NULL; + eap->pending_req_otp_len = 0; + break; + case WPA_CTRL_REQ_EAP_PASSPHRASE: + os_free(eap->private_key_passwd); + eap->private_key_passwd = (u8 *) os_strdup(value); + eap->pending_req_passphrase = 0; + if (ssid == wpa_s->current_ssid) + wpa_s->reassociate = 1; + break; + case WPA_CTRL_REQ_SIM: + os_free(eap->external_sim_resp); + eap->external_sim_resp = os_strdup(value); + break; + default: + wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown field '%s'", field); + return -1; + } + + return 0; +#else /* IEEE8021X_EAPOL */ + wpa_printf(MSG_DEBUG, "CTRL_IFACE: IEEE 802.1X not included"); + return -1; +#endif /* IEEE8021X_EAPOL */ +} +#endif /* CONFIG_CTRL_IFACE || CONFIG_CTRL_IFACE_DBUS_NEW */ + + +int wpas_network_disabled(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) +{ + int i; + unsigned int drv_enc; + + if (ssid == NULL) + return 1; + + if (ssid->disabled) + return 1; + + if (wpa_s && wpa_s->drv_capa_known) + drv_enc = wpa_s->drv_enc; + else + drv_enc = (unsigned int) -1; + + for (i = 0; i < NUM_WEP_KEYS; i++) { + size_t len = ssid->wep_key_len[i]; + if (len == 0) + continue; + if (len == 5 && (drv_enc & WPA_DRIVER_CAPA_ENC_WEP40)) + continue; + if (len == 13 && (drv_enc & WPA_DRIVER_CAPA_ENC_WEP104)) + continue; + if (len == 16 && (drv_enc & WPA_DRIVER_CAPA_ENC_WEP128)) + continue; + return 1; /* invalid WEP key */ + } + + if (wpa_key_mgmt_wpa_psk(ssid->key_mgmt) && !ssid->psk_set && + !ssid->ext_psk) + return 1; + + return 0; +} + + +int wpas_is_p2p_prioritized(struct wpa_supplicant *wpa_s) +{ + if (wpa_s->global->conc_pref == WPA_CONC_PREF_P2P) + return 1; + if (wpa_s->global->conc_pref == WPA_CONC_PREF_STA) + return 0; + return -1; +} + + +void wpas_auth_failed(struct wpa_supplicant *wpa_s) +{ + struct wpa_ssid *ssid = wpa_s->current_ssid; + int dur; + struct os_reltime now; + + if (ssid == NULL) { + wpa_printf(MSG_DEBUG, "Authentication failure but no known " + "SSID block"); + return; + } + + if (ssid->key_mgmt == WPA_KEY_MGMT_WPS) + return; + + ssid->auth_failures++; + +#ifdef CONFIG_P2P + if (ssid->p2p_group && + (wpa_s->p2p_in_provisioning || wpa_s->show_group_started)) { + /* + * Skip the wait time since there is a short timeout on the + * connection to a P2P group. + */ + return; + } +#endif /* CONFIG_P2P */ + + if (ssid->auth_failures > 50) + dur = 300; + else if (ssid->auth_failures > 20) + dur = 120; + else if (ssid->auth_failures > 10) + dur = 60; + else if (ssid->auth_failures > 5) + dur = 30; + else if (ssid->auth_failures > 1) + dur = 20; + else + dur = 10; + + os_get_reltime(&now); + if (now.sec + dur <= ssid->disabled_until.sec) + return; + + ssid->disabled_until.sec = now.sec + dur; + + wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_TEMP_DISABLED + "id=%d ssid=\"%s\" auth_failures=%u duration=%d", + ssid->id, wpa_ssid_txt(ssid->ssid, ssid->ssid_len), + ssid->auth_failures, dur); +} + + +void wpas_clear_temp_disabled(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid, int clear_failures) +{ + if (ssid == NULL) + return; + + if (ssid->disabled_until.sec) { + wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_REENABLED + "id=%d ssid=\"%s\"", + ssid->id, wpa_ssid_txt(ssid->ssid, ssid->ssid_len)); + } + ssid->disabled_until.sec = 0; + ssid->disabled_until.usec = 0; + if (clear_failures) + ssid->auth_failures = 0; +} + + +int disallowed_bssid(struct wpa_supplicant *wpa_s, const u8 *bssid) +{ + size_t i; + + if (wpa_s->disallow_aps_bssid == NULL) + return 0; + + for (i = 0; i < wpa_s->disallow_aps_bssid_count; i++) { + if (os_memcmp(wpa_s->disallow_aps_bssid + i * ETH_ALEN, + bssid, ETH_ALEN) == 0) + return 1; + } + + return 0; +} + + +int disallowed_ssid(struct wpa_supplicant *wpa_s, const u8 *ssid, + size_t ssid_len) +{ + size_t i; + + if (wpa_s->disallow_aps_ssid == NULL || ssid == NULL) + return 0; + + for (i = 0; i < wpa_s->disallow_aps_ssid_count; i++) { + struct wpa_ssid_value *s = &wpa_s->disallow_aps_ssid[i]; + if (ssid_len == s->ssid_len && + os_memcmp(ssid, s->ssid, ssid_len) == 0) + return 1; + } + + return 0; +} + + +/** + * wpas_request_connection - Request a new connection + * @wpa_s: Pointer to the network interface + * + * This function is used to request a new connection to be found. It will mark + * the interface to allow reassociation and request a new scan to find a + * suitable network to connect to. + */ +void wpas_request_connection(struct wpa_supplicant *wpa_s) +{ + wpa_s->normal_scans = 0; + wpa_supplicant_reinit_autoscan(wpa_s); + wpa_s->extra_blacklist_count = 0; + wpa_s->disconnected = 0; + wpa_s->reassociate = 1; + + if (wpa_supplicant_fast_associate(wpa_s) != 1) + wpa_supplicant_req_scan(wpa_s, 0, 0); +} + + +void dump_freq_array(struct wpa_supplicant *wpa_s, const char *title, + int *freq_array, unsigned int len) +{ + unsigned int i; + + wpa_dbg(wpa_s, MSG_DEBUG, "Shared frequencies (len=%u): %s", + len, title); + for (i = 0; i < len; i++) + wpa_dbg(wpa_s, MSG_DEBUG, "freq[%u]: %d", i, freq_array[i]); +} + + +/* + * Find the operating frequencies of any of the virtual interfaces that + * are using the same radio as the current interface. + */ +int get_shared_radio_freqs(struct wpa_supplicant *wpa_s, + int *freq_array, unsigned int len) +{ + struct wpa_supplicant *ifs; + u8 bssid[ETH_ALEN]; + int freq; + unsigned int idx = 0, i; + + wpa_dbg(wpa_s, MSG_DEBUG, + "Determining shared radio frequencies (max len %u)", len); + os_memset(freq_array, 0, sizeof(int) * len); + + /* First add the frequency of the local interface */ + if (wpa_s->current_ssid != NULL && wpa_s->assoc_freq != 0) { + if (wpa_s->current_ssid->mode == WPAS_MODE_AP || + wpa_s->current_ssid->mode == WPAS_MODE_P2P_GO) + freq_array[idx++] = wpa_s->current_ssid->frequency; + else if (wpa_drv_get_bssid(wpa_s, bssid) == 0) + freq_array[idx++] = wpa_s->assoc_freq; + } + + /* If get_radio_name is not supported, use only the local freq */ + if (!wpa_s->driver->get_radio_name) { + freq = wpa_drv_shared_freq(wpa_s); + if (freq > 0 && idx < len && + (idx == 0 || freq_array[0] != freq)) + freq_array[idx++] = freq; + dump_freq_array(wpa_s, "No get_radio_name", freq_array, idx); + return idx; + } + + dl_list_for_each(ifs, &wpa_s->radio->ifaces, struct wpa_supplicant, + radio_list) { + if (wpa_s == ifs) + continue; + + if (ifs->current_ssid == NULL || ifs->assoc_freq == 0) + continue; + + if (ifs->current_ssid->mode == WPAS_MODE_AP || + ifs->current_ssid->mode == WPAS_MODE_P2P_GO) + freq = ifs->current_ssid->frequency; + else if (wpa_drv_get_bssid(ifs, bssid) == 0) + freq = ifs->assoc_freq; + else + continue; + + /* Hold only distinct freqs */ + for (i = 0; i < idx; i++) + if (freq_array[i] == freq) + break; + + if (i == idx) + freq_array[idx++] = freq; + } + + dump_freq_array(wpa_s, "completed iteration", freq_array, idx); + return idx; +} diff -Nru wpa-1.0/wpa_supplicant/wpa_supplicant.conf wpa-2.1/wpa_supplicant/wpa_supplicant.conf --- wpa-1.0/wpa_supplicant/wpa_supplicant.conf 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/wpa_supplicant.conf 2014-02-04 11:23:35.000000000 +0000 @@ -214,6 +214,22 @@ # to external program(s) #wps_cred_processing=0 +# Vendor attribute in WPS M1, e.g., Windows 7 Vertical Pairing +# The vendor attribute contents to be added in M1 (hex string) +#wps_vendor_ext_m1=000137100100020001 + +# NFC password token for WPS +# These parameters can be used to configure a fixed NFC password token for the +# station. This can be generated, e.g., with nfc_pw_token. When these +# parameters are used, the station is assumed to be deployed with a NFC tag +# that includes the matching NFC password token (e.g., written based on the +# NDEF record from nfc_pw_token). +# +#wps_nfc_dev_pw_id: Device Password ID (16..65535) +#wps_nfc_dh_pubkey: Hexdump of DH Public Key +#wps_nfc_dh_privkey: Hexdump of DH Private Key +#wps_nfc_dev_pw: Hexdump of Device Password + # Maximum number of BSS entries to keep in memory # Default: 200 # This can be used to limit memory use on the BSS entries (cached scan @@ -221,12 +237,83 @@ # of APs when using ap_scan=1 mode. #bss_max_count=200 +# Automatic scan +# This is an optional set of parameters for automatic scanning +# within an interface in following format: +#autoscan=: +# autoscan is like bgscan but on disconnected or inactive state. +# For instance, on exponential module parameters would be : +#autoscan=exponential:3:300 +# Which means a delay between scans on a base exponential of 3, +# up to the limit of 300 seconds (3, 9, 27 ... 300) +# For periodic module, parameters would be +#autoscan=periodic:30 +# So a delay of 30 seconds will be applied between each scan # filter_ssids - SSID-based scan result filtering # 0 = do not filter scan results (default) # 1 = only include configured SSIDs in scan results/BSS table #filter_ssids=0 +# Password (and passphrase, etc.) backend for external storage +# format: [:] +#ext_password_backend=test:pw1=password|pw2=testing + +# Timeout in seconds to detect STA inactivity (default: 300 seconds) +# +# This timeout value is used in P2P GO mode to clean up +# inactive stations. +#p2p_go_max_inactivity=300 + +# Opportunistic Key Caching (also known as Proactive Key Caching) default +# This parameter can be used to set the default behavior for the +# proactive_key_caching parameter. By default, OKC is disabled unless enabled +# with the global okc=1 parameter or with the per-network +# proactive_key_caching=1 parameter. With okc=1, OKC is enabled by default, but +# can be disabled with per-network proactive_key_caching=0 parameter. +#okc=0 + +# Protected Management Frames default +# This parameter can be used to set the default behavior for the ieee80211w +# parameter. By default, PMF is disabled unless enabled with the global pmf=1/2 +# parameter or with the per-network ieee80211w=1/2 parameter. With pmf=1/2, PMF +# is enabled/required by default, but can be disabled with the per-network +# ieee80211w parameter. +#pmf=0 + +# Enabled SAE finite cyclic groups in preference order +# By default (if this parameter is not set), the mandatory group 19 (ECC group +# defined over a 256-bit prime order field) is preferred, but other groups are +# also enabled. If this parameter is set, the groups will be tried in the +# indicated order. The group values are listed in the IANA registry: +# http://www.iana.org/assignments/ipsec-registry/ipsec-registry.xml#ipsec-registry-9 +#sae_groups=21 20 19 26 25 + +# Default value for DTIM period (if not overridden in network block) +#dtim_period=2 + +# Default value for Beacon interval (if not overridden in network block) +#beacon_int=100 + +# 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 +# one or more elements). This is used in AP and P2P GO modes. +#ap_vendor_elements=dd0411223301 + +# Ignore scan results older than request +# +# The driver may have a cache of scan results that makes it return +# information that is older than our scan trigger. This parameter can +# be used to configure such old information to be ignored instead of +# allowing it to update the internal BSS table. +#ignore_old_scan_res=0 + +# scan_cur_freq: Whether to scan only the current frequency +# 0: Scan all available frequencies. (Default) +# 1: Scan current operating frequency if another VIF on the same radio +# is already associated. # Interworking (IEEE 802.11u) @@ -239,23 +326,140 @@ # is enabled. # hessid=00:11:22:33:44:55 -# Home Realm for Interworking -#home_realm=example.com - -# Username for Interworking network selection -#home_username=user - -# Password for Interworking network selection -#home_password=secret - -# CA certificate for Interworking network selection -#home_ca_cert=/etc/cert/ca.pem +# Automatic network selection behavior +# 0 = do not automatically go through Interworking network selection +# (i.e., require explicit interworking_select command for this; default) +# 1 = perform Interworking network selection if one or more +# credentials have been configured and scan did not find a +# matching network block +#auto_interworking=0 -# IMSI in | | '-' | format -#home_imsi=232010000000000 +# credential block +# +# Each credential used for automatic network selection is configured as a set +# of parameters that are compared to the information advertised by the APs when +# interworking_select and interworking_connect commands are used. +# +# credential fields: +# +# temporary: Whether this credential is temporary and not to be saved +# +# priority: Priority group +# By default, all networks and credentials get the same priority group +# (0). This field can be used to give higher priority for credentials +# (and similarly in struct wpa_ssid for network blocks) to change the +# Interworking automatic networking selection behavior. The matching +# network (based on either an enabled network block or a credential) +# with the highest priority value will be selected. +# +# pcsc: Use PC/SC and SIM/USIM card +# +# realm: Home Realm for Interworking +# +# username: Username for Interworking network selection +# +# password: Password for Interworking network selection +# +# ca_cert: CA certificate for Interworking network selection +# +# client_cert: File path to client certificate file (PEM/DER) +# This field is used with Interworking networking selection for a case +# where client certificate/private key is used for authentication +# (EAP-TLS). Full path to the file should be used since working +# directory may change when wpa_supplicant is run in the background. +# +# Alternatively, a named configuration blob can be used by setting +# this to blob://blob_name. +# +# private_key: File path to client private key file (PEM/DER/PFX) +# When PKCS#12/PFX file (.p12/.pfx) is used, client_cert should be +# commented out. Both the private key and certificate will be read +# from the PKCS#12 file in this case. Full path to the file should be +# used since working directory may change when wpa_supplicant is run +# in the background. +# +# Windows certificate store can be used by leaving client_cert out and +# configuring private_key in one of the following formats: +# +# cert://substring_to_match +# +# hash://certificate_thumbprint_in_hex +# +# For example: private_key="hash://63093aa9c47f56ae88334c7b65a4" +# +# Note that when running wpa_supplicant as an application, the user +# certificate store (My user account) is used, whereas computer store +# (Computer account) is used when running wpasvc as a service. +# +# Alternatively, a named configuration blob can be used by setting +# this to blob://blob_name. +# +# private_key_passwd: Password for private key file +# +# imsi: IMSI in | | '-' | format +# +# milenage: Milenage parameters for SIM/USIM simulator in :: +# format +# +# domain: Home service provider FQDN(s) +# This is used to compare against the Domain Name List to figure out +# whether the AP is operated by the Home SP. Multiple domain entries can +# be used to configure alternative FQDNs that will be considered home +# networks. +# +# roaming_consortium: Roaming Consortium OI +# If roaming_consortium_len is non-zero, this field contains the +# Roaming Consortium OI that can be used to determine which access +# points support authentication with this credential. This is an +# alternative to the use of the realm parameter. When using Roaming +# Consortium to match the network, the EAP parameters need to be +# pre-configured with the credential since the NAI Realm information +# may not be available or fetched. +# +# eap: Pre-configured EAP method +# This optional field can be used to specify which EAP method will be +# used with this credential. If not set, the EAP method is selected +# automatically based on ANQP information (e.g., NAI Realm). +# +# phase1: Pre-configure Phase 1 (outer authentication) parameters +# This optional field is used with like the 'eap' parameter. +# +# phase2: Pre-configure Phase 2 (inner authentication) parameters +# This optional field is used with like the 'eap' parameter. +# +# excluded_ssid: Excluded SSID +# This optional field can be used to excluded specific SSID(s) from +# matching with the network. Multiple entries can be used to specify more +# than one SSID. +# +# for example: +# +#cred={ +# realm="example.com" +# username="user@example.com" +# password="password" +# ca_cert="/etc/wpa_supplicant/ca.pem" +# domain="example.com" +#} +# +#cred={ +# imsi="310026-000000000" +# milenage="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82" +#} +# +#cred={ +# realm="example.com" +# username="user" +# password="password" +# ca_cert="/etc/wpa_supplicant/ca.pem" +# domain="example.com" +# roaming_consortium=223344 +# eap=TTLS +# phase2="auth=MSCHAPV2" +#} -# Milenage parameters for SIM/USIM simulator in :: format -#home_milenage=90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581:000000000123 +# Hotspot 2.0 +# hs20=1 # network block # @@ -274,8 +478,10 @@ # to external action script through wpa_cli as WPA_ID_STR environment # variable to make it easier to do network specific configuration. # -# ssid: SSID (mandatory); either as an ASCII string with double quotation or -# as hex string; network name +# ssid: SSID (mandatory); network name in one of the optional formats: +# - an ASCII string with double quotation +# - a hex string (two characters per octet of SSID) +# - a printf-escaped ASCII string P"" # # scan_ssid: # 0 = do not scan this SSID with specific Probe Request frames (default) @@ -302,9 +508,10 @@ # 0 = infrastructure (Managed) mode, i.e., associate with an AP (default) # 1 = IBSS (ad-hoc, peer-to-peer) # 2 = AP (access point) -# Note: IBSS can only be used with key_mgmt NONE (plaintext and static WEP) -# and key_mgmt=WPA-NONE (fixed group key TKIP/CCMP). WPA-None requires -# following network block options: +# Note: IBSS can only be used with key_mgmt NONE (plaintext and static WEP) and +# WPA-PSK (with proto=RSN). In addition, key_mgmt=WPA-NONE (fixed group key +# TKIP/CCMP) is available for backwards compatibility, but its use is +# deprecated. WPA-None requires following network block options: # proto=WPA, key_mgmt=WPA-NONE, pairwise=NONE, group=TKIP (or CCMP, but not # both), and psk must also be set. # @@ -326,6 +533,30 @@ # set, scan results that do not match any of the specified frequencies are not # considered when selecting a BSS. # +# This can also be set on the outside of the network block. In this case, +# it limits the frequencies that will be scanned. +# +# bgscan: Background scanning +# wpa_supplicant behavior for background scanning can be specified by +# configuring a bgscan module. These modules are responsible for requesting +# background scans for the purpose of roaming within an ESS (i.e., within a +# single network block with all the APs using the same SSID). The bgscan +# parameter uses following format: ":" +# Following bgscan modules are available: +# simple - Periodic background scans based on signal strength +# bgscan="simple::: +# " +# bgscan="simple:30:-45:300" +# learn - Learn channels used by the network and try to avoid bgscans on other +# channels (experimental) +# bgscan="learn::: +# [:]" +# bgscan="learn:30:-45:300:/etc/wpa_supplicant/network1.bgscan" +# +# This option can also be set outside of all network blocks for the bgscan +# parameter to apply for all the networks that have no specific bgscan +# parameter. +# # proto: list of accepted protocols # WPA = WPA/IEEE 802.11i/D3.0 # RSN = WPA2/IEEE 802.11i (also WPA2 can be used as an alias for RSN) @@ -341,6 +572,16 @@ # WPA-EAP-SHA256 = Like WPA-EAP but using stronger SHA256-based algorithms # If not set, this defaults to: WPA-PSK WPA-EAP # +# ieee80211w: whether management frame protection is enabled +# 0 = disabled (default unless changed with the global pmf parameter) +# 1 = optional +# 2 = required +# The most common configuration options for this based on the PMF (protected +# management frames) certification program are: +# PMF enabled: ieee80211w=1 and key_mgmt=WPA-EAP WPA-EAP-SHA256 +# PMF required: ieee80211w=2 and key_mgmt=WPA-EAP-SHA256 +# (and similarly for WPA-PSK and WPA-WPSK-SHA256 if WPA2-Personal is used) +# # auth_alg: list of allowed IEEE 802.11 authentication algorithms # OPEN = Open System authentication (required for WPA/WPA2) # SHARED = Shared Key authentication (requires static WEP keys) @@ -366,7 +607,8 @@ # The key used in WPA-PSK mode can be entered either as 64 hex-digits, i.e., # 32 bytes or as an ASCII passphrase (in which case, the real PSK will be # generated using the passphrase and SSID). ASCII passphrase must be between -# 8 and 63 characters (inclusive). +# 8 and 63 characters (inclusive). ext: format can +# be used to indicate that the PSK/passphrase is stored in external storage. # This field is not needed, if WPA-EAP is used. # Note: Separate tool, wpa_passphrase, can be used to generate 256-bit keys # from ASCII passphrase. This process uses lot of CPU and wpa_supplicant @@ -389,7 +631,7 @@ # # proactive_key_caching: # Enable/disable opportunistic PMKSA caching for WPA2. -# 0 = disabled (default) +# 0 = disabled (default unless changed with the global okc parameter) # 1 = enabled # # wep_key0..3: Static WEP key (ASCII in double quotation, e.g. "abcde" or @@ -427,7 +669,8 @@ # EAP-PSK/PAX/SAKE/GPSK. # anonymous_identity: Anonymous identity string for EAP (to be used as the # unencrypted identity with EAP types that support different tunnelled -# identity, e.g., EAP-TTLS) +# identity, e.g., EAP-TTLS). This field can also be used with +# EAP-SIM/AKA/AKA' to store the pseudonym identity. # password: Password string for EAP. This field can include either the # plaintext password (using ASCII or hex string) or a NtPasswordHash # (16-byte MD4 hash of password) in hash:<32 hex digits> format. @@ -435,7 +678,8 @@ # MSCHAP (EAP-MSCHAPv2, EAP-TTLS/MSCHAPv2, EAP-TTLS/MSCHAP, LEAP). # EAP-PSK (128-bit PSK), EAP-PAX (128-bit PSK), and EAP-SAKE (256-bit # PSK) is also configured using this field. For EAP-GPSK, this is a -# variable length PSK. +# variable length PSK. ext: format can +# be used to indicate that the password is stored in external storage. # ca_cert: File path to CA certificate file (PEM/DER). This file can have one # or more trusted CA certificates. If ca_cert and ca_path are not # included, server certificate will not be verified. This is insecure and @@ -538,6 +782,25 @@ # phase2: Phase2 (inner authentication with TLS tunnel) parameters # (string with field-value pairs, e.g., "auth=MSCHAPV2" for EAP-PEAP or # "autheap=MSCHAPV2 autheap=MD5" for EAP-TTLS) +# +# TLS-based methods can use the following parameters to control TLS behavior +# (these are normally in the phase1 parameter, but can be used also in the +# phase2 parameter when EAP-TLS is used within the inner tunnel): +# tls_allow_md5=1 - allow MD5-based certificate signatures (depending on the +# TLS library, these may be disabled by default to enforce stronger +# security) +# tls_disable_time_checks=1 - 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) +# tls_disable_session_ticket=1 - disable TLS Session Ticket extension +# tls_disable_session_ticket=0 - allow TLS Session Ticket extension to be used +# Note: If not set, this is automatically set to 1 for EAP-TLS/PEAP/TTLS +# as a workaround for broken authentication server implementations unless +# EAP workarounds are disabled with eap_workarounds=0. +# For EAP-FAST, this must be set to 0 (or left unconfigured for the +# default value to be used automatically). +# # Following certificate/private key fields are used in inner Phase2 # authentication when using EAP-TTLS or EAP-PEAP. # ca_cert2: File path to CA certificate file. This file can have one or more @@ -561,6 +824,11 @@ # interface used for EAPOL. The default value is suitable for most # cases. # +# ocsp: Whether to use/require OCSP to check server certificate +# 0 = do not use OCSP stapling (TLS certificate status extension) +# 1 = try to use OCSP stapling, but not require response +# 2 = require valid OCSP stapling response +# # EAP-FAST variables: # pac_file: File path for the PAC entries. wpa_supplicant will need to be able # to create this file and write updates to it when PAC is being @@ -587,6 +855,71 @@ # number of authentication servers. Strict EAP conformance mode can be # configured by disabling workarounds with eap_workaround=0. +# Station inactivity limit +# +# If a station does not send anything in ap_max_inactivity seconds, an +# empty data frame is sent to it in order to verify whether it is +# still in range. If this frame is not ACKed, the station will be +# disassociated and then deauthenticated. This feature is used to +# clear station table of old entries when the STAs move out of the +# range. +# +# The station can associate again with the AP if it is still in range; +# this inactivity poll is just used as a nicer way of verifying +# inactivity; i.e., client will not report broken connection because +# disassociation frame is not sent immediately without first polling +# the STA with a data frame. +# default: 300 (i.e., 5 minutes) +#ap_max_inactivity=300 + +# DTIM period in Beacon intervals for AP mode (default: 2) +#dtim_period=2 + +# Beacon interval (default: 100 TU) +#beacon_int=100 + +# disable_ht: Whether HT (802.11n) should be disabled. +# 0 = HT enabled (if AP supports it) +# 1 = HT disabled +# +# disable_ht40: Whether HT-40 (802.11n) should be disabled. +# 0 = HT-40 enabled (if AP supports it) +# 1 = HT-40 disabled +# +# disable_sgi: Whether SGI (short guard interval) should be disabled. +# 0 = SGI enabled (if AP supports it) +# 1 = SGI disabled +# +# ht_mcs: Configure allowed MCS rates. +# Parsed as an array of bytes, in base-16 (ascii-hex) +# ht_mcs="" // Use all available (default) +# ht_mcs="0xff 00 00 00 00 00 00 00 00 00 " // Use MCS 0-7 only +# ht_mcs="0xff ff 00 00 00 00 00 00 00 00 " // Use MCS 0-15 only +# +# disable_max_amsdu: Whether MAX_AMSDU should be disabled. +# -1 = Do not make any changes. +# 0 = Enable MAX-AMSDU if hardware supports it. +# 1 = Disable AMSDU +# +# ampdu_density: Allow overriding AMPDU density configuration. +# Treated as hint by the kernel. +# -1 = Do not make any changes. +# 0-3 = Set AMPDU density (aka factor) to specified value. + +# disable_vht: Whether VHT should be disabled. +# 0 = VHT enabled (if AP supports it) +# 1 = VHT disabled +# +# vht_capa: VHT capabilities to set in the override +# vht_capa_mask: mask of VHT capabilities +# +# vht_rx_mcs_nss_1/2/3/4/5/6/7/8: override the MCS set for RX NSS 1-8 +# vht_tx_mcs_nss_1/2/3/4/5/6/7/8: override the MCS set for TX NSS 1-8 +# 0: MCS 0-7 +# 1: MCS 0-8 +# 2: MCS 0-9 +# 3: not supported + # Example blocks: # Simple case: WPA-PSK, PSK as an ASCII passphrase, allow all valid ciphers @@ -833,7 +1166,19 @@ } -# IBSS/ad-hoc network with WPA-None/TKIP. +# IBSS/ad-hoc network with RSN +network={ + ssid="ibss-rsn" + key_mgmt=WPA-PSK + proto=RSN + psk="12345678" + mode=1 + frequency=2412 + pairwise=CCMP + group=CCMP +} + +# IBSS/ad-hoc network with WPA-None/TKIP (deprecated) network={ ssid="test adhoc" mode=1 @@ -919,3 +1264,10 @@ network={ key_mgmt=NONE } + + +# Example config file that will only scan on channel 36. +freq_list=5180 +network={ + key_mgmt=NONE +} diff -Nru wpa-1.0/wpa_supplicant/wpa_supplicant_conf.mk wpa-2.1/wpa_supplicant/wpa_supplicant_conf.mk --- wpa-1.0/wpa_supplicant/wpa_supplicant_conf.mk 1970-01-01 00:00:00.000000000 +0000 +++ wpa-2.1/wpa_supplicant/wpa_supplicant_conf.mk 2014-02-04 11:23:35.000000000 +0000 @@ -0,0 +1,34 @@ +# +# Copyright (C) 2010 The Android Open Source Project +# +# This software may be distributed under the terms of the BSD license. +# See README for more details. +# + +# Include this makefile to generate your hardware specific wpa_supplicant.conf +# Requires: WIFI_DRIVER_SOCKET_IFACE + +LOCAL_PATH := $(call my-dir) + +######################## +include $(CLEAR_VARS) + +LOCAL_MODULE := wpa_supplicant.conf +LOCAL_MODULE_CLASS := ETC +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/wifi + +include $(BUILD_SYSTEM)/base_rules.mk + +WPA_SUPPLICANT_CONF_TEMPLATE := $(LOCAL_PATH)/wpa_supplicant_template.conf +WPA_SUPPLICANT_CONF_SCRIPT := $(LOCAL_PATH)/wpa_supplicant_conf.sh +$(LOCAL_BUILT_MODULE): PRIVATE_WIFI_DRIVER_SOCKET_IFACE := $(WIFI_DRIVER_SOCKET_IFACE) +$(LOCAL_BUILT_MODULE): PRIVATE_WPA_SUPPLICANT_CONF_TEMPLATE := $(WPA_SUPPLICANT_CONF_TEMPLATE) +$(LOCAL_BUILT_MODULE): PRIVATE_WPA_SUPPLICANT_CONF_SCRIPT := $(WPA_SUPPLICANT_CONF_SCRIPT) +$(LOCAL_BUILT_MODULE) : $(WPA_SUPPLICANT_CONF_TEMPLATE) $(WPA_SUPPLICANT_CONF_SCRIPT) + @echo Target wpa_supplicant.conf: $@ + @mkdir -p $(dir $@) + $(hide) WIFI_DRIVER_SOCKET_IFACE="$(PRIVATE_WIFI_DRIVER_SOCKET_IFACE)" \ + bash $(PRIVATE_WPA_SUPPLICANT_CONF_SCRIPT) $(PRIVATE_WPA_SUPPLICANT_CONF_TEMPLATE) > $@ + +######################## diff -Nru wpa-1.0/wpa_supplicant/wpa_supplicant_conf.sh wpa-2.1/wpa_supplicant/wpa_supplicant_conf.sh --- wpa-1.0/wpa_supplicant/wpa_supplicant_conf.sh 1970-01-01 00:00:00.000000000 +0000 +++ wpa-2.1/wpa_supplicant/wpa_supplicant_conf.sh 2014-02-04 11:23:35.000000000 +0000 @@ -0,0 +1,16 @@ +#!/bin/bash +# +# Copyright (C) 2010 The Android Open Source Project +# +# This software may be distributed under the terms of the BSD license. +# See README for more details. +# + +# Generate a wpa_supplicant.conf from the template. +# $1: the template file name +if [ -n "$WIFI_DRIVER_SOCKET_IFACE" ] +then + sed -e 's/#.*$//' -e 's/[ \t]*$//' -e '/^$/d' < $1 | sed -e "s/wlan0/$WIFI_DRIVER_SOCKET_IFACE/" +else + sed -e 's/#.*$//' -e 's/[ \t]*$//' -e '/^$/d' < $1 +fi diff -Nru wpa-1.0/wpa_supplicant/wpa_supplicant_i.h wpa-2.1/wpa_supplicant/wpa_supplicant_i.h --- wpa-1.0/wpa_supplicant/wpa_supplicant_i.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/wpa_supplicant_i.h 2014-02-04 11:23:35.000000000 +0000 @@ -1,15 +1,9 @@ /* * wpa_supplicant - Internal definitions - * Copyright (c) 2003-2010, Jouni Malinen + * Copyright (c) 2003-2014, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef WPA_SUPPLICANT_I_H @@ -17,6 +11,8 @@ #include "utils/list.h" #include "common/defs.h" +#include "common/sae.h" +#include "wps/wps_defs.h" #include "config_ssid.h" extern const char *wpa_supplicant_version; @@ -36,6 +32,7 @@ struct wpa_bss; struct wpa_scan_results; struct hostapd_hw_modes; +struct wpa_driver_associate_params; /* * Forward declarations of private structures used within the ctrl_iface @@ -60,6 +57,14 @@ const char *confname; /** + * confanother - Additional configuration name (file or profile) name + * + * This can also be %NULL when the additional configuration file is not + * used. + */ + const char *confanother; + + /** * ctrl_interface - Control interface parameter * * If a configuration file is not used, this variable can be used to @@ -100,6 +105,15 @@ * receiving of EAPOL frames from an additional interface. */ const char *bridge_ifname; + + /** + * p2p_mgmt - Interface used for P2P management (P2P Device operations) + * + * Indicates whether wpas_p2p_init() must be called for this interface. + * This is used only when the driver supports a dedicated P2P Device + * interface that is not a network interface. + */ + int p2p_mgmt; }; /** @@ -151,6 +165,11 @@ char *ctrl_interface; /** + * ctrl_interface_group - Global ctrl_iface group + */ + char *ctrl_interface_group; + + /** * dbus_ctrl_interface - Enable the DBus control interface */ int dbus_ctrl_interface; @@ -166,6 +185,11 @@ int wpa_debug_syslog; /** + * wpa_debug_tracing - Enable log output through Linux tracing + */ + int wpa_debug_tracing; + + /** * override_driver - Optional driver parameter override * * This parameter can be used to override the driver parameter in @@ -219,20 +243,114 @@ size_t drv_count; struct os_time suspend_time; struct p2p_data *p2p; + struct wpa_supplicant *p2p_init_wpa_s; struct wpa_supplicant *p2p_group_formation; + struct wpa_supplicant *p2p_invite_group; u8 p2p_dev_addr[ETH_ALEN]; + struct os_reltime p2p_go_wait_client; struct dl_list p2p_srv_bonjour; /* struct p2p_srv_bonjour */ struct dl_list p2p_srv_upnp; /* struct p2p_srv_upnp */ int p2p_disabled; int cross_connection; + struct wpa_freq_range_list p2p_disallow_freq; + struct wpa_freq_range_list p2p_go_avoid_freq; + enum wpa_conc_pref { + WPA_CONC_PREF_NOT_SET, + WPA_CONC_PREF_STA, + WPA_CONC_PREF_P2P + } conc_pref; + unsigned int p2p_per_sta_psk:1; + unsigned int p2p_fail_on_wps_complete:1; + +#ifdef CONFIG_WIFI_DISPLAY + int wifi_display; +#define MAX_WFD_SUBELEMS 10 + struct wpabuf *wfd_subelem[MAX_WFD_SUBELEMS]; +#endif /* CONFIG_WIFI_DISPLAY */ + + struct psk_list_entry *add_psk; /* From group formation */ }; +/** + * struct wpa_radio - Internal data for per-radio information + * + * This structure is used to share data about configured interfaces + * (struct wpa_supplicant) that share the same physical radio, e.g., to allow + * better coordination of offchannel operations. + */ +struct wpa_radio { + char name[16]; /* from driver_ops get_radio_name() or empty if not + * available */ + struct dl_list ifaces; /* struct wpa_supplicant::radio_list entries */ + struct dl_list work; /* struct wpa_radio_work::list entries */ +}; + +/** + * struct wpa_radio_work - Radio work item + */ +struct wpa_radio_work { + struct dl_list list; + unsigned int freq; /* known frequency (MHz) or 0 for multiple/unknown */ + const char *type; + struct wpa_supplicant *wpa_s; + void (*cb)(struct wpa_radio_work *work, int deinit); + void *ctx; + unsigned int started:1; + struct os_reltime time; +}; + +int radio_add_work(struct wpa_supplicant *wpa_s, unsigned int freq, + const char *type, int next, + void (*cb)(struct wpa_radio_work *work, int deinit), + void *ctx); +void radio_work_done(struct wpa_radio_work *work); +void radio_remove_unstarted_work(struct wpa_supplicant *wpa_s, + const char *type); +void radio_work_check_next(struct wpa_supplicant *wpa_s); + +struct wpa_connect_work { + unsigned int sme:1; + struct wpa_bss *bss; + struct wpa_ssid *ssid; +}; + +int wpas_valid_bss_ssid(struct wpa_supplicant *wpa_s, struct wpa_bss *test_bss, + struct wpa_ssid *test_ssid); +void wpas_connect_work_free(struct wpa_connect_work *cwork); +void wpas_connect_work_done(struct wpa_supplicant *wpa_s); + +struct wpa_external_work { + unsigned int id; + char type[100]; + unsigned int timeout; +}; + +/** + * offchannel_send_action_result - Result of offchannel send Action frame + */ enum offchannel_send_action_result { - OFFCHANNEL_SEND_ACTION_SUCCESS /* Frame was send and acknowledged */, - OFFCHANNEL_SEND_ACTION_NO_ACK /* Frame was sent, but not acknowledged + OFFCHANNEL_SEND_ACTION_SUCCESS /**< Frame was send and acknowledged */, + OFFCHANNEL_SEND_ACTION_NO_ACK /**< Frame was sent, but not acknowledged */, - OFFCHANNEL_SEND_ACTION_FAILED /* Frame was not sent due to a failure */ + OFFCHANNEL_SEND_ACTION_FAILED /**< Frame was not sent due to a failure + */ +}; + +struct wps_ap_info { + u8 bssid[ETH_ALEN]; + enum wps_ap_info_type { + WPS_AP_NOT_SEL_REG, + WPS_AP_SEL_REG, + WPS_AP_SEL_REG_OUR + } type; + unsigned int tries; + struct os_reltime last_attempt; +}; + +struct wpa_ssid_value { + u8 ssid[32]; + size_t ssid_len; }; /** @@ -245,6 +363,8 @@ */ struct wpa_supplicant { struct wpa_global *global; + struct wpa_radio *radio; /* shared radio context */ + struct dl_list radio_list; /* list head: struct wpa_radio::ifaces */ struct wpa_supplicant *parent; struct wpa_supplicant *next; struct l2_packet_data *l2; @@ -257,13 +377,17 @@ #ifdef CONFIG_CTRL_IFACE_DBUS_NEW char *dbus_new_path; char *dbus_groupobj_path; +#ifdef CONFIG_AP + char *preq_notify_peer; +#endif /* CONFIG_AP */ #endif /* CONFIG_CTRL_IFACE_DBUS_NEW */ char bridge_ifname[16]; char *confname; + char *confanother; struct wpa_config *conf; int countermeasures; - os_time_t last_michael_mic_error; + struct os_reltime last_michael_mic_error; u8 bssid[ETH_ALEN]; u8 pending_bssid[ETH_ALEN]; /* If wpa_state == WPA_ASSOCIATING, this * field contains the target BSSID. */ @@ -285,6 +409,19 @@ void *drv_priv; /* private data used by driver_ops */ void *global_drv_priv; + u8 *bssid_filter; + size_t bssid_filter_count; + + u8 *disallow_aps_bssid; + size_t disallow_aps_bssid_count; + struct wpa_ssid_value *disallow_aps_ssid; + size_t disallow_aps_ssid_count; + + enum { WPA_SETBAND_AUTO, WPA_SETBAND_5G, WPA_SETBAND_2G } setband; + + /* previous scan was wildcard when interleaving between + * wildcard scans and specific SSID scan when max_ssids=1 */ + int prev_scan_wildcard; struct wpa_ssid *prev_scan_ssid; /* previously scanned SSID; * NULL = not yet initialized (start * with wildcard SSID) @@ -307,6 +444,15 @@ unsigned int bss_update_idx; unsigned int bss_next_id; + /* + * Pointers to BSS entries in the order they were in the last scan + * results. + */ + struct wpa_bss **last_scan_res; + unsigned int last_scan_res_used; + unsigned int last_scan_res_size; + struct os_reltime last_scan; + struct wpa_driver_ops *driver; int interface_removed; /* whether the network interface has been * removed */ @@ -316,29 +462,97 @@ struct ctrl_iface_priv *ctrl_iface; enum wpa_states wpa_state; + struct wpa_radio_work *scan_work; int scanning; int sched_scanning; int new_connection; - int reassociated_connection; int eapol_received; /* number of EAPOL packets received after the * previous association event */ struct scard_data *scard; + char imsi[20]; + int mnc_len; unsigned char last_eapol_src[ETH_ALEN]; - int keys_cleared; + unsigned int keys_cleared; /* bitfield of key indexes that the driver is + * known not to be configured with a key */ struct wpa_blacklist *blacklist; - int scan_req; /* manual scan request; this forces a scan even if there - * are no enabled networks in the configuration */ + /** + * extra_blacklist_count - Sum of blacklist counts after last connection + * + * This variable is used to maintain a count of temporary blacklisting + * failures (maximum number for any BSS) over blacklist clear + * operations. This is needed for figuring out whether there has been + * failures prior to the last blacklist clear operation which happens + * whenever no other not-blacklisted BSS candidates are available. This + * gets cleared whenever a connection has been established successfully. + */ + int extra_blacklist_count; + + /** + * scan_req - Type of the scan request + */ + enum scan_req_type { + /** + * NORMAL_SCAN_REQ - Normal scan request + * + * This is used for scans initiated by wpa_supplicant to find an + * AP for a connection. + */ + NORMAL_SCAN_REQ, + + /** + * INITIAL_SCAN_REQ - Initial scan request + * + * This is used for the first scan on an interface to force at + * least one scan to be run even if the configuration does not + * include any enabled networks. + */ + INITIAL_SCAN_REQ, + + /** + * MANUAL_SCAN_REQ - Manual scan request + * + * This is used for scans where the user request a scan or + * a specific wpa_supplicant operation (e.g., WPS) requires scan + * to be run. + */ + MANUAL_SCAN_REQ + } scan_req, last_scan_req; + struct os_reltime scan_trigger_time, scan_start_time; int scan_runs; /* number of scan runs since WPS was started */ int *next_scan_freqs; + int *manual_scan_freqs; + unsigned int manual_scan_passive:1; + unsigned int manual_scan_use_id:1; + unsigned int manual_scan_only_new:1; + unsigned int own_scan_requested:1; + unsigned int own_scan_running:1; + unsigned int external_scan_running:1; + unsigned int clear_driver_scan_cache:1; + unsigned int manual_scan_id; int scan_interval; /* time in sec between scans to find suitable AP */ + int normal_scans; /* normal scans run before sched_scan */ + int scan_for_connection; /* whether the scan request was triggered for + * finding a connection */ unsigned int drv_flags; + unsigned int drv_enc; + + /* + * A bitmap of supported protocols for probe response offload. See + * struct wpa_driver_capa in driver.h + */ + unsigned int probe_resp_offloads; + + /* extended capabilities supported by the driver */ + const u8 *extended_capa, *extended_capa_mask; + unsigned int extended_capa_len; + int max_scan_ssids; int max_sched_scan_ssids; int sched_scan_supported; @@ -356,8 +570,10 @@ int blacklist_cleared; struct wpabuf *pending_eapol_rx; - struct os_time pending_eapol_rx_time; + struct os_reltime pending_eapol_rx_time; u8 pending_eapol_rx_src[ETH_ALEN]; + unsigned int last_eapol_matches_bssid:1; + unsigned int eap_expected_failure:1; struct ibss_rsn *ibss_rsn; @@ -389,7 +605,15 @@ u8 *sa_query_trans_id; /* buffer of WLAN_SA_QUERY_TR_ID_LEN * * sa_query_count octets of pending * SA Query transaction identifiers */ - struct os_time sa_query_start; + struct os_reltime sa_query_start; + u8 sched_obss_scan; + u16 obss_scan_int; + u16 bss_max_idle_period; +#ifdef CONFIG_SAE + struct sae_data sae; + struct wpabuf *sae_token; + int sae_group_index; +#endif /* CONFIG_SAE */ } sme; #endif /* CONFIG_SME */ @@ -417,6 +641,8 @@ unsigned int roc_waiting_drv_freq; int action_tx_wait_time; + int p2p_mgmt; + #ifdef CONFIG_P2P struct p2p_go_neg_results *go_params; int create_p2p_iface; @@ -446,8 +672,15 @@ u8 pending_join_iface_addr[ETH_ALEN]; u8 pending_join_dev_addr[ETH_ALEN]; int pending_join_wps_method; + u8 p2p_join_ssid[32]; + size_t p2p_join_ssid_len; int p2p_join_scan_count; + int auto_pd_scan_retry; int force_long_sd; + u16 pending_pd_config_methods; + enum { + NORMAL_PD, AUTO_PD_GO_NEG, AUTO_PD_JOIN + } pending_pd_use; /* * Whether cross connection is disallowed by the AP to which this @@ -470,24 +703,55 @@ */ char cross_connect_uplink[100]; - enum { - P2P_GROUP_REMOVAL_UNKNOWN, - P2P_GROUP_REMOVAL_REQUESTED, - P2P_GROUP_REMOVAL_IDLE_TIMEOUT, - P2P_GROUP_REMOVAL_UNAVAILABLE - } removal_reason; - - unsigned int p2p_cb_on_scan_complete:1; + unsigned int p2p_auto_join:1; + unsigned int p2p_auto_pd:1; + unsigned int p2p_persistent_group:1; + unsigned int p2p_fallback_to_go_neg:1; + unsigned int p2p_pd_before_go_neg:1; + unsigned int p2p_go_ht40:1; + unsigned int p2p_go_vht:1; + unsigned int user_initiated_pd:1; + unsigned int p2p_go_group_formation_completed:1; + unsigned int waiting_presence_resp; + int p2p_first_connection_timeout; + unsigned int p2p_nfc_tag_enabled:1; + unsigned int p2p_peer_oob_pk_hash_known:1; + unsigned int p2p_disable_ip_addr_req:1; + int p2p_persistent_go_freq; + int p2p_persistent_id; + int p2p_go_intent; + int p2p_connect_freq; + struct os_reltime p2p_auto_started; + struct wpa_ssid *p2p_last_4way_hs_fail; + struct wpa_radio_work *p2p_scan_work; + struct wpa_radio_work *p2p_listen_work; + struct wpa_radio_work *p2p_send_action_work; + + u16 p2p_oob_dev_pw_id; /* OOB Device Password Id for group formation */ + struct wpabuf *p2p_oob_dev_pw; /* OOB Device Password for group + * formation */ + u8 p2p_peer_oob_pubkey_hash[WPS_OOB_PUBKEY_HASH_LEN]; + u8 p2p_ip_addr_info[3 * 4]; #endif /* CONFIG_P2P */ struct wpa_ssid *bgscan_ssid; const struct bgscan_ops *bgscan; void *bgscan_priv; + const struct autoscan_ops *autoscan; + struct wpa_driver_scan_params *autoscan_params; + void *autoscan_priv; + struct wpa_ssid *connect_without_scan; + struct wps_ap_info *wps_ap; + size_t num_wps_ap; + int wps_ap_iter; + int after_wps; + int known_wps_freq; unsigned int wps_freq; + u16 wps_ap_channel; int wps_fragment_size; int auto_reconnect_disabled; @@ -502,6 +766,9 @@ unsigned int fetch_anqp_in_progress:1; unsigned int network_select:1; unsigned int auto_select:1; + unsigned int auto_network_select:1; + unsigned int fetch_all_anqp:1; + struct wpa_bss *interworking_gas_bss; #endif /* CONFIG_INTERWORKING */ unsigned int drv_capa_known; @@ -510,11 +777,55 @@ u16 num_modes; u16 flags; } hw; + + int pno; + int pno_sched_pending; + + /* WLAN_REASON_* reason codes. Negative if locally generated. */ + int disconnect_reason; + + struct ext_password_data *ext_pw; + + struct wpabuf *last_gas_resp, *prev_gas_resp; + u8 last_gas_addr[ETH_ALEN], prev_gas_addr[ETH_ALEN]; + u8 last_gas_dialog_token, prev_gas_dialog_token; + + unsigned int no_keep_alive:1; + +#ifdef CONFIG_WNM + u8 wnm_dialog_token; + u8 wnm_reply; + u8 wnm_num_neighbor_report; + u8 wnm_mode; + u16 wnm_dissoc_timer; + u8 wnm_validity_interval; + u8 wnm_bss_termination_duration[12]; + struct neighbor_report *wnm_neighbor_report_elements; +#endif /* CONFIG_WNM */ + +#ifdef CONFIG_TESTING_GET_GTK + u8 last_gtk[32]; + size_t last_gtk_len; +#endif /* CONFIG_TESTING_GET_GTK */ + + unsigned int num_multichan_concurrent; + struct wpa_radio_work *connect_work; + + unsigned int ext_work_id; }; /* wpa_supplicant.c */ +void wpa_supplicant_apply_ht_overrides( + struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, + struct wpa_driver_associate_params *params); +void wpa_supplicant_apply_vht_overrides( + struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, + struct wpa_driver_associate_params *params); + int wpa_set_wep_keys(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid); +int wpa_supplicant_set_wpa_none_key(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid); int wpa_supplicant_reload_configuration(struct wpa_supplicant *wpa_s); @@ -533,6 +844,7 @@ void wpa_clear_keys(struct wpa_supplicant *wpa_s, const u8 *addr); void wpa_supplicant_req_auth_timeout(struct wpa_supplicant *wpa_s, int sec, int usec); +void wpa_supplicant_reinit_autoscan(struct wpa_supplicant *wpa_s); void wpa_supplicant_set_state(struct wpa_supplicant *wpa_s, enum wpa_states state); struct wpa_ssid * wpa_supplicant_get_ssid(struct wpa_supplicant *wpa_s); @@ -540,8 +852,6 @@ void wpa_supplicant_cancel_auth_timeout(struct wpa_supplicant *wpa_s); void wpa_supplicant_deauthenticate(struct wpa_supplicant *wpa_s, int reason_code); -void wpa_supplicant_disassociate(struct wpa_supplicant *wpa_s, - int reason_code); void wpa_supplicant_enable_network(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid); @@ -549,12 +859,17 @@ struct wpa_ssid *ssid); void wpa_supplicant_select_network(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid); +int wpas_set_pkcs11_engine_and_module_path(struct wpa_supplicant *wpa_s, + const char *pkcs11_engine_path, + const char *pkcs11_module_path); int wpa_supplicant_set_ap_scan(struct wpa_supplicant *wpa_s, int ap_scan); int wpa_supplicant_set_bss_expiration_age(struct wpa_supplicant *wpa_s, unsigned int expire_age); int wpa_supplicant_set_bss_expiration_count(struct wpa_supplicant *wpa_s, unsigned int expire_count); +int wpa_supplicant_set_scan_interval(struct wpa_supplicant *wpa_s, + int scan_interval); int wpa_supplicant_set_debug_params(struct wpa_global *global, int debug_level, int debug_timestamp, int debug_show_keys); @@ -565,7 +880,8 @@ struct wpa_supplicant * wpa_supplicant_add_iface(struct wpa_global *global, struct wpa_interface *iface); int wpa_supplicant_remove_iface(struct wpa_global *global, - struct wpa_supplicant *wpa_s); + struct wpa_supplicant *wpa_s, + int terminate); struct wpa_supplicant * wpa_supplicant_get_iface(struct wpa_global *global, const char *ifname); struct wpa_global * wpa_supplicant_init(struct wpa_params *params); @@ -577,12 +893,34 @@ void wpa_supplicant_terminate_proc(struct wpa_global *global); void wpa_supplicant_rx_eapol(void *ctx, const u8 *src_addr, const u8 *buf, size_t len); -enum wpa_key_mgmt key_mgmt2driver(int key_mgmt); -enum wpa_cipher cipher_suite2driver(int cipher); void wpa_supplicant_update_config(struct wpa_supplicant *wpa_s); void wpa_supplicant_clear_status(struct wpa_supplicant *wpa_s); void wpas_connection_failed(struct wpa_supplicant *wpa_s, const u8 *bssid); int wpas_driver_bss_selection(struct wpa_supplicant *wpa_s); +int wpas_is_p2p_prioritized(struct wpa_supplicant *wpa_s); +void wpas_auth_failed(struct wpa_supplicant *wpa_s); +void wpas_clear_temp_disabled(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid, int clear_failures); +int disallowed_bssid(struct wpa_supplicant *wpa_s, const u8 *bssid); +int disallowed_ssid(struct wpa_supplicant *wpa_s, const u8 *ssid, + size_t ssid_len); +void wpas_request_connection(struct wpa_supplicant *wpa_s); +int wpas_build_ext_capab(struct wpa_supplicant *wpa_s, u8 *buf); + +/** + * wpa_supplicant_ctrl_iface_ctrl_rsp_handle - Handle a control response + * @wpa_s: Pointer to wpa_supplicant data + * @ssid: Pointer to the network block the reply is for + * @field: field the response is a reply for + * @value: value (ie, password, etc) for @field + * Returns: 0 on success, non-zero on error + * + * Helper function to handle replies to control interface requests. + */ +int wpa_supplicant_ctrl_iface_ctrl_rsp_handle(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid, + const char *field, + const char *value); /* events.c */ void wpa_supplicant_mark_disassoc(struct wpa_supplicant *wpa_s); @@ -591,6 +929,10 @@ struct wpa_ssid *ssid); void wpa_supplicant_stop_countermeasures(void *eloop_ctx, void *sock_ctx); void wpa_supplicant_delayed_mic_error_report(void *eloop_ctx, void *sock_ctx); +void wnm_bss_keep_alive_deinit(struct wpa_supplicant *wpa_s); +int wpa_supplicant_fast_associate(struct wpa_supplicant *wpa_s); +struct wpa_bss * wpa_supplicant_pick_network(struct wpa_supplicant *wpa_s, + struct wpa_ssid **selected_ssid); /* eap_register.c */ int eap_register_methods(void); @@ -605,4 +947,13 @@ return ((ssid->disabled == 2) || ssid->p2p_persistent_group); } +int wpas_network_disabled(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid); + +int wpas_init_ext_pw(struct wpa_supplicant *wpa_s); + +void dump_freq_array(struct wpa_supplicant *wpa_s, const char *title, + int *freq_array, unsigned int len); +int get_shared_radio_freqs(struct wpa_supplicant *wpa_s, + int *freq_array, unsigned int len); + #endif /* WPA_SUPPLICANT_I_H */ diff -Nru wpa-1.0/wpa_supplicant/wpa_supplicant.nsi wpa-2.1/wpa_supplicant/wpa_supplicant.nsi --- wpa-1.0/wpa_supplicant/wpa_supplicant.nsi 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/wpa_supplicant.nsi 1970-01-01 00:00:00.000000000 +0000 @@ -1,112 +0,0 @@ -!define PRODUCT_NAME "wpa_supplicant" -!define PRODUCT_VERSION "@WPAVER@" -!define PRODUCT_PUBLISHER "Jouni Malinen" - -Name "${PRODUCT_NAME} ${PRODUCT_VERSION}" -outfile "../wpa_supplicant-@WPAVER@.exe" - -installDir "$PROGRAMFILES\wpa_supplicant" - -Page Directory -Page InstFiles - -section -Prerequisites - SetOutPath $INSTDIR\Prerequisites - MessageBox MB_YESNO "Install WinPcap?" /SD IDYES IDNO endWinPcap - File "/opt/Qt-Win/files/WinPcap_4_1_2.exe" - ExecWait "$INSTDIR\Prerequisites\WinPcap_4_1_2.exe" - Goto endWinPcap - endWinPcap: -sectionEnd - - -section - setOutPath $INSTDIR - - File wpa_gui.exe - File wpa_gui_de.qm - File wpa_cli.exe - File COPYING - File README - File README-Windows.txt - File win_example.reg - File win_if_list.exe - File wpa_passphrase.exe - File wpa_supplicant.conf - File wpa_supplicant.exe - File wpasvc.exe - - File /opt/Qt-Win/files/mingwm10.dll - File /opt/Qt-Win/files/libgcc_s_dw2-1.dll - File /opt/Qt-Win/files/QtCore4.dll - File /opt/Qt-Win/files/QtGui4.dll - - WriteRegDWORD HKLM "Software\wpa_supplicant" "debug_level" 0 - WriteRegDWORD HKLM "Software\wpa_supplicant" "debug_show_keys" 0 - WriteRegDWORD HKLM "Software\wpa_supplicant" "debug_timestamp" 0 - WriteRegDWORD HKLM "Software\wpa_supplicant" "debug_use_file" 0 - - WriteRegDWORD HKLM "Software\wpa_supplicant\configs\default" "ap_scan" 2 - WriteRegDWORD HKLM "Software\wpa_supplicant\configs\default" "update_config" 1 - WriteRegDWORD HKLM "Software\wpa_supplicant\configs\default\networks" "dummy" 1 - DeleteRegValue HKLM "Software\wpa_supplicant\configs\default\networks" "dummy" - - WriteRegDWORD HKLM "Software\wpa_supplicant\interfaces" "dummy" 1 - DeleteRegValue HKLM "Software\wpa_supplicant\interfaces" "dummy" - - writeUninstaller "$INSTDIR\uninstall.exe" - - WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\wpa_supplicant" \ - "DisplayName" "wpa_supplicant" -WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\wpa_supplicant" \ - "UninstallString" "$INSTDIR\uninstall.exe" - - CreateDirectory "$SMPROGRAMS\wpa_supplicant" - CreateShortCut "$SMPROGRAMS\wpa_supplicant\wpa_gui.lnk" "$INSTDIR\wpa_gui.exe" - CreateShortCut "$SMPROGRAMS\wpa_supplicant\Uninstall.lnk" "$INSTDIR\uninstall.exe" - - ExecWait "$INSTDIR\wpasvc.exe reg" -sectionEnd - - -Function un.onInit - MessageBox MB_YESNO "This will uninstall wpa_supplicant. Continue?" IDYES NoAbort - Abort - NoAbort: -FunctionEnd - -section "uninstall" - DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\wpa_supplicant" - delete "$INSTDIR\uninstall.exe" - - ExecWait "$INSTDIR\wpasvc.exe unreg" - - DeleteRegKey HKLM "Software\wpa_supplicant" - - delete "$INSTDIR\wpa_gui.exe" - delete "$INSTDIR\wpa_gui_de.qm" - delete "$INSTDIR\wpa_cli.exe" - delete "$INSTDIR\COPYING" - delete "$INSTDIR\README" - delete "$INSTDIR\README-Windows.txt" - delete "$INSTDIR\win_example.reg" - delete "$INSTDIR\win_if_list.exe" - delete "$INSTDIR\wpa_passphrase.exe" - delete "$INSTDIR\wpa_supplicant.conf" - delete "$INSTDIR\wpa_supplicant.exe" - delete "$INSTDIR\wpasvc.exe" - - delete "$INSTDIR\mingwm10.dll" - delete "$INSTDIR\libgcc_s_dw2-1.dll" - delete "$INSTDIR\QtCore4.dll" - delete "$INSTDIR\QtGui4.dll" - - delete "$INSTDIR\Prerequisites\WinPcap_4_1_2.exe" - rmdir "$INSTDIR\Prerequisites" - - rmdir "$INSTDIR" - - delete "$SMPROGRAMS\wpa_supplicant\wpa_gui.lnk" - delete "$SMPROGRAMS\wpa_supplicant\Uninstall.lnk" - rmdir "$SMPROGRAMS\wpa_supplicant" -sectionEnd diff -Nru wpa-1.0/wpa_supplicant/wpa_supplicant_template.conf wpa-2.1/wpa_supplicant/wpa_supplicant_template.conf --- wpa-1.0/wpa_supplicant/wpa_supplicant_template.conf 1970-01-01 00:00:00.000000000 +0000 +++ wpa-2.1/wpa_supplicant/wpa_supplicant_template.conf 2014-02-04 11:23:35.000000000 +0000 @@ -0,0 +1,6 @@ +##### wpa_supplicant configuration file template ##### +update_config=1 +ctrl_interface=wlan0 +eapol_version=1 +ap_scan=1 +fast_reauth=1 diff -Nru wpa-1.0/wpa_supplicant/wps_supplicant.c wpa-2.1/wpa_supplicant/wps_supplicant.c --- wpa-1.0/wpa_supplicant/wps_supplicant.c 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/wps_supplicant.c 2014-02-04 11:23:35.000000000 +0000 @@ -1,15 +1,9 @@ /* * wpa_supplicant / WPS integration - * Copyright (c) 2008-2010, Jouni Malinen + * Copyright (c) 2008-2014, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -17,6 +11,7 @@ #include "common.h" #include "eloop.h" #include "uuid.h" +#include "crypto/random.h" #include "crypto/dh_group5.h" #include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" @@ -26,6 +21,7 @@ #include "eap_peer/eap.h" #include "eapol_supp/eapol_supp_sm.h" #include "rsn_supp/wpa.h" +#include "wps/wps_attr_parse.h" #include "config.h" #include "wpa_supplicant_i.h" #include "driver_i.h" @@ -47,8 +43,22 @@ static void wpas_clear_wps(struct wpa_supplicant *wpa_s); +static void wpas_wps_clear_ap_info(struct wpa_supplicant *wpa_s) +{ + os_free(wpa_s->wps_ap); + wpa_s->wps_ap = NULL; + wpa_s->num_wps_ap = 0; + wpa_s->wps_ap_iter = 0; +} + + int wpas_wps_eapol_cb(struct wpa_supplicant *wpa_s) { +#ifdef CONFIG_P2P + if (wpas_p2p_wps_eapol_cb(wpa_s) > 0) + return 1; +#endif /* CONFIG_P2P */ + if (!wpa_s->wps_success && wpa_s->current_ssid && eap_is_wps_pin_enrollee(&wpa_s->current_ssid->eap)) { @@ -70,6 +80,7 @@ return 1; } + wpas_wps_clear_ap_info(wpa_s); eloop_cancel_timeout(wpas_wps_timeout, wpa_s, NULL); if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPS && !wpa_s->wps_success) wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_FAIL); @@ -78,6 +89,10 @@ !(wpa_s->current_ssid->key_mgmt & WPA_KEY_MGMT_WPS)) { int disabled = wpa_s->current_ssid->disabled; unsigned int freq = wpa_s->assoc_freq; + struct wpa_bss *bss; + struct wpa_ssid *ssid = NULL; + int use_fast_assoc = 0; + wpa_printf(MSG_DEBUG, "WPS: Network configuration replaced - " "try to associate with the received credential " "(freq=%u)", freq); @@ -90,8 +105,28 @@ } wpa_s->after_wps = 5; wpa_s->wps_freq = freq; + wpa_s->normal_scans = 0; wpa_s->reassociate = 1; - wpa_supplicant_req_scan(wpa_s, 0, 0); + + wpa_printf(MSG_DEBUG, "WPS: Checking whether fast association " + "without a new scan can be used"); + bss = wpa_supplicant_pick_network(wpa_s, &ssid); + if (bss) { + struct wpabuf *wps; + struct wps_parse_attr attr; + + wps = wpa_bss_get_vendor_ie_multi(bss, + WPS_IE_VENDOR_TYPE); + if (wps && wps_parse_msg(wps, &attr) == 0 && + attr.wps_state && + *attr.wps_state == WPS_STATE_CONFIGURED) + use_fast_assoc = 1; + wpabuf_free(wps); + } + + if (!use_fast_assoc || + wpa_supplicant_fast_associate(wpa_s) != 1) + wpa_supplicant_req_scan(wpa_s, 0, 0); return 1; } @@ -189,6 +224,55 @@ } +static void wpas_wps_remove_dup_network(struct wpa_supplicant *wpa_s, + struct wpa_ssid *new_ssid) +{ + struct wpa_ssid *ssid, *next; + + for (ssid = wpa_s->conf->ssid, next = ssid ? ssid->next : NULL; ssid; + ssid = next, next = ssid ? ssid->next : NULL) { + /* + * new_ssid has already been added to the list in + * wpas_wps_add_network(), so skip it. + */ + if (ssid == new_ssid) + continue; + + if (ssid->bssid_set || new_ssid->bssid_set) { + if (ssid->bssid_set != new_ssid->bssid_set) + continue; + if (os_memcmp(ssid->bssid, new_ssid->bssid, ETH_ALEN) != + 0) + continue; + } + + /* compare SSID */ + if (ssid->ssid_len == 0 || ssid->ssid_len != new_ssid->ssid_len) + continue; + + if (ssid->ssid && new_ssid->ssid) { + if (os_memcmp(ssid->ssid, new_ssid->ssid, + ssid->ssid_len) != 0) + continue; + } else if (ssid->ssid || new_ssid->ssid) + continue; + + /* compare security parameters */ + if (ssid->auth_alg != new_ssid->auth_alg || + ssid->key_mgmt != new_ssid->key_mgmt || + ssid->proto != new_ssid->proto || + ssid->pairwise_cipher != new_ssid->pairwise_cipher || + ssid->group_cipher != new_ssid->group_cipher) + continue; + + /* Remove the duplicated older network entry. */ + wpa_printf(MSG_DEBUG, "Remove duplicate network %d", ssid->id); + wpas_notify_network_removed(wpa_s, ssid); + wpa_config_remove_network(wpa_s->conf, ssid->id); + } +} + + static int wpa_supplicant_wps_cred(void *ctx, const struct wps_credential *cred) { @@ -249,6 +333,15 @@ return 0; } + if (auth_type == WPS_AUTH_WPAPSK || auth_type == WPS_AUTH_WPA2PSK) { + if (cred->key_len < 8 || cred->key_len > 2 * PMK_LEN) { + wpa_printf(MSG_ERROR, "WPS: Reject PSK credential with " + "invalid Network Key length %lu", + (unsigned long) cred->key_len); + return -1; + } + } + if (ssid && (ssid->key_mgmt & WPA_KEY_MGMT_WPS)) { wpa_printf(MSG_DEBUG, "WPS: Replace WPS network block based " "on the received credential"); @@ -266,8 +359,13 @@ ssid->eap.phase1 = NULL; os_free(ssid->eap.eap_methods); ssid->eap.eap_methods = NULL; - if (!ssid->p2p_group) + if (!ssid->p2p_group) { ssid->temporary = 0; + ssid->bssid_set = 0; + } + ssid->disabled_until.sec = 0; + ssid->disabled_until.usec = 0; + ssid->auth_failures = 0; } else { wpa_printf(MSG_DEBUG, "WPS: Create a new network based on the " "received credential"); @@ -355,16 +453,6 @@ ssid->key_mgmt = WPA_KEY_MGMT_PSK; ssid->proto = WPA_PROTO_WPA; break; - case WPS_AUTH_WPA: - ssid->auth_alg = WPA_AUTH_ALG_OPEN; - ssid->key_mgmt = WPA_KEY_MGMT_IEEE8021X; - ssid->proto = WPA_PROTO_WPA; - break; - case WPS_AUTH_WPA2: - ssid->auth_alg = WPA_AUTH_ALG_OPEN; - ssid->key_mgmt = WPA_KEY_MGMT_IEEE8021X; - ssid->proto = WPA_PROTO_RSN; - break; case WPS_AUTH_WPA2PSK: ssid->auth_alg = WPA_AUTH_ALG_OPEN; ssid->key_mgmt = WPA_KEY_MGMT_PSK; @@ -401,6 +489,11 @@ wpas_wps_security_workaround(wpa_s, ssid, cred); + if (cred->ap_channel) + wpa_s->wps_ap_channel = cred->ap_channel; + + wpas_wps_remove_dup_network(wpa_s, ssid); + #ifndef CONFIG_NO_CONFIG_WRITE if (wpa_s->conf->update_config && wpa_config_write(wpa_s->confname, wpa_s->conf)) { @@ -409,6 +502,13 @@ } #endif /* CONFIG_NO_CONFIG_WRITE */ + /* + * Optimize the post-WPS scan based on the channel used during + * the provisioning in case EAP-Failure is not received. + */ + wpa_s->after_wps = 5; + wpa_s->wps_freq = wpa_s->assoc_freq; + return 0; } @@ -447,11 +547,13 @@ } -static const char * wps_event_fail_reason[NUM_WPS_EI_VALUES] = { - "No Error", /* WPS_EI_NO_ERROR */ - "TKIP Only Prohibited", /* WPS_EI_SECURITY_TKIP_ONLY_PROHIBITED */ - "WEP Prohibited" /* WPS_EI_SECURITY_WEP_PROHIBITED */ -}; +static void wpas_wps_clear_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + wpa_printf(MSG_DEBUG, "WPS: Clear WPS network from timeout"); + wpas_clear_wps(wpa_s); +} + static void wpa_supplicant_wps_event_fail(struct wpa_supplicant *wpa_s, struct wps_event_fail *fail) @@ -461,13 +563,13 @@ wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_FAIL "msg=%d config_error=%d reason=%d (%s)", fail->msg, fail->config_error, fail->error_indication, - wps_event_fail_reason[fail->error_indication]); + wps_ei_str(fail->error_indication)); if (wpa_s->parent && wpa_s->parent != wpa_s) wpa_msg(wpa_s->parent, MSG_INFO, WPS_EVENT_FAIL "msg=%d config_error=%d reason=%d (%s)", fail->msg, fail->config_error, fail->error_indication, - wps_event_fail_reason[fail->error_indication]); + wps_ei_str(fail->error_indication)); } else { wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_FAIL "msg=%d config_error=%d", @@ -477,7 +579,14 @@ "msg=%d config_error=%d", fail->msg, fail->config_error); } - wpas_clear_wps(wpa_s); + + /* + * Need to allow WPS processing to complete, e.g., by sending WSC_NACK. + */ + wpa_printf(MSG_DEBUG, "WPS: Register timeout to clear WPS network"); + eloop_cancel_timeout(wpas_wps_clear_timeout, wpa_s, NULL); + eloop_register_timeout(0, 100000, wpas_wps_clear_timeout, wpa_s, NULL); + wpas_notify_wps_event_fail(wpa_s, fail); #ifdef CONFIG_P2P wpas_p2p_wps_failed(wpa_s, fail); @@ -485,11 +594,61 @@ } +static void wpas_wps_reenable_networks_cb(void *eloop_ctx, void *timeout_ctx); + +static void wpas_wps_reenable_networks(struct wpa_supplicant *wpa_s) +{ + struct wpa_ssid *ssid; + int changed = 0; + + eloop_cancel_timeout(wpas_wps_reenable_networks_cb, wpa_s, NULL); + + for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) { + if (ssid->disabled_for_connect && ssid->disabled) { + ssid->disabled_for_connect = 0; + ssid->disabled = 0; + wpas_notify_network_enabled_changed(wpa_s, ssid); + changed++; + } + } + + if (changed) { +#ifndef CONFIG_NO_CONFIG_WRITE + if (wpa_s->conf->update_config && + wpa_config_write(wpa_s->confname, wpa_s->conf)) { + wpa_printf(MSG_DEBUG, "WPS: Failed to update " + "configuration"); + } +#endif /* CONFIG_NO_CONFIG_WRITE */ + } +} + + +static void wpas_wps_reenable_networks_cb(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + /* Enable the networks disabled during wpas_wps_reassoc */ + wpas_wps_reenable_networks(wpa_s); +} + + static void wpa_supplicant_wps_event_success(struct wpa_supplicant *wpa_s) { wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_SUCCESS); wpa_s->wps_success = 1; wpas_notify_wps_event_success(wpa_s); + if (wpa_s->current_ssid) + wpas_clear_temp_disabled(wpa_s, wpa_s->current_ssid, 1); + wpa_s->extra_blacklist_count = 0; + + /* + * Enable the networks disabled during wpas_wps_reassoc after 10 + * seconds. The 10 seconds timer is to allow the data connection to be + * formed before allowing other networks to be selected. + */ + eloop_register_timeout(10, 0, wpas_wps_reenable_networks_cb, wpa_s, + NULL); + #ifdef CONFIG_P2P wpas_p2p_wps_success(wpa_s, wpa_s->bssid, 0); #endif /* CONFIG_P2P */ @@ -643,6 +802,12 @@ break; case WPS_EV_PBC_TIMEOUT: break; + case WPS_EV_PBC_ACTIVE: + wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_ACTIVE); + break; + case WPS_EV_PBC_DISABLE: + wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_DISABLE); + break; case WPS_EV_ER_AP_ADD: wpa_supplicant_wps_event_er_ap_add(wpa_s, &data->ap); break; @@ -671,6 +836,17 @@ } +static int wpa_supplicant_wps_rf_band(void *ctx) +{ + struct wpa_supplicant *wpa_s = ctx; + + if (!wpa_s->current_ssid || !wpa_s->assoc_freq) + return 0; + + return (wpa_s->assoc_freq > 2484) ? WPS_RF_50GHZ : WPS_RF_24GHZ; +} + + enum wps_request_type wpas_wps_get_req_type(struct wpa_ssid *ssid) { if (eap_is_wps_pbc_enrollee(&ssid->eap) || @@ -686,18 +862,24 @@ int id; struct wpa_ssid *ssid, *remove_ssid = NULL, *prev_current; + wpa_s->after_wps = 0; + wpa_s->known_wps_freq = 0; + prev_current = wpa_s->current_ssid; + /* Enable the networks disabled during wpas_wps_reassoc */ + wpas_wps_reenable_networks(wpa_s); + eloop_cancel_timeout(wpas_wps_timeout, wpa_s, NULL); + eloop_cancel_timeout(wpas_wps_clear_timeout, wpa_s, NULL); /* Remove any existing WPS network from configuration */ ssid = wpa_s->conf->ssid; while (ssid) { if (ssid->key_mgmt & WPA_KEY_MGMT_WPS) { if (ssid == wpa_s->current_ssid) { - wpa_s->current_ssid = NULL; - if (ssid != NULL) - wpas_notify_network_changed(wpa_s); + wpa_supplicant_deauthenticate( + wpa_s, WLAN_REASON_DEAUTH_LEAVING); } id = ssid->id; remove_ssid = ssid; @@ -714,6 +896,8 @@ wpa_config_remove_network(wpa_s->conf, id); } } + + wpas_wps_clear_ap_info(wpa_s); } @@ -727,7 +911,8 @@ static struct wpa_ssid * wpas_wps_add_network(struct wpa_supplicant *wpa_s, - int registrar, const u8 *bssid) + int registrar, const u8 *dev_addr, + const u8 *bssid) { struct wpa_ssid *ssid; @@ -747,6 +932,11 @@ return NULL; } +#ifdef CONFIG_P2P + if (dev_addr) + os_memcpy(ssid->go_p2p_dev_addr, dev_addr, ETH_ALEN); +#endif /* CONFIG_P2P */ + if (bssid) { #ifndef CONFIG_P2P struct wpa_bss *bss; @@ -792,8 +982,8 @@ } -static void wpas_wps_reassoc(struct wpa_supplicant *wpa_s, - struct wpa_ssid *selected) +static void wpas_wps_temp_disable(struct wpa_supplicant *wpa_s, + struct wpa_ssid *selected) { struct wpa_ssid *ssid; @@ -805,6 +995,7 @@ ssid = wpa_s->conf->ssid; while (ssid) { int was_disabled = ssid->disabled; + ssid->disabled_for_connect = 0; /* * In case the network object corresponds to a persistent group * then do not send out network disabled signal. In addition, @@ -813,17 +1004,47 @@ */ if (was_disabled != 2) { ssid->disabled = ssid != selected; - if (was_disabled != ssid->disabled) + if (was_disabled != ssid->disabled) { + if (ssid->disabled) + ssid->disabled_for_connect = 1; wpas_notify_network_enabled_changed(wpa_s, ssid); + } } ssid = ssid->next; } +} + + +static void wpas_wps_reassoc(struct wpa_supplicant *wpa_s, + struct wpa_ssid *selected, const u8 *bssid, + int freq) +{ + struct wpa_bss *bss; + + wpa_s->after_wps = 0; + wpa_s->known_wps_freq = 0; + if (freq) { + wpa_s->after_wps = 5; + wpa_s->wps_freq = freq; + } else if (bssid) { + bss = wpa_bss_get_bssid_latest(wpa_s, bssid); + if (bss && bss->freq > 0) { + wpa_s->known_wps_freq = 1; + wpa_s->wps_freq = bss->freq; + } + } + + wpas_wps_temp_disable(wpa_s, selected); + wpa_s->disconnected = 0; wpa_s->reassociate = 1; wpa_s->scan_runs = 0; + wpa_s->normal_scans = 0; wpa_s->wps_success = 0; wpa_s->blacklist_cleared = 0; + + wpa_supplicant_cancel_sched_scan(wpa_s); wpa_supplicant_req_scan(wpa_s, 0, 0); } @@ -833,7 +1054,7 @@ { struct wpa_ssid *ssid; wpas_clear_wps(wpa_s); - ssid = wpas_wps_add_network(wpa_s, 0, bssid); + ssid = wpas_wps_add_network(wpa_s, 0, NULL, bssid); if (ssid == NULL) return -1; ssid->temporary = 1; @@ -850,29 +1071,53 @@ } } #endif /* CONFIG_P2P */ - wpa_config_set(ssid, "phase1", "\"pbc=1\"", 0); + if (wpa_config_set(ssid, "phase1", "\"pbc=1\"", 0) < 0) + return -1; if (wpa_s->wps_fragment_size) ssid->eap.fragment_size = wpa_s->wps_fragment_size; eloop_register_timeout(WPS_PBC_WALK_TIME, 0, wpas_wps_timeout, wpa_s, NULL); - wpas_wps_reassoc(wpa_s, ssid); + wpas_wps_reassoc(wpa_s, ssid, bssid, 0); return 0; } -int wpas_wps_start_pin(struct wpa_supplicant *wpa_s, const u8 *bssid, - const char *pin, int p2p_group, u16 dev_pw_id) +static int wpas_wps_start_dev_pw(struct wpa_supplicant *wpa_s, + const u8 *dev_addr, const u8 *bssid, + const char *pin, int p2p_group, u16 dev_pw_id, + const u8 *peer_pubkey_hash, + const u8 *ssid_val, size_t ssid_len, int freq) { struct wpa_ssid *ssid; - char val[128]; + char val[128 + 2 * WPS_OOB_PUBKEY_HASH_LEN]; unsigned int rpin = 0; + char hash[2 * WPS_OOB_PUBKEY_HASH_LEN + 10]; wpas_clear_wps(wpa_s); - ssid = wpas_wps_add_network(wpa_s, 0, bssid); - if (ssid == NULL) + if (bssid && is_zero_ether_addr(bssid)) + bssid = NULL; + ssid = wpas_wps_add_network(wpa_s, 0, dev_addr, bssid); + if (ssid == NULL) { + wpa_printf(MSG_DEBUG, "WPS: Could not add network"); return -1; + } ssid->temporary = 1; ssid->p2p_group = p2p_group; + if (ssid_val) { + ssid->ssid = os_malloc(ssid_len); + if (ssid->ssid) { + os_memcpy(ssid->ssid, ssid_val, ssid_len); + ssid->ssid_len = ssid_len; + } + } + if (peer_pubkey_hash) { + os_memcpy(hash, " pkhash=", 8); + wpa_snprintf_hex_uppercase(hash + 8, sizeof(hash) - 8, + peer_pubkey_hash, + WPS_OOB_PUBKEY_HASH_LEN); + } else { + hash[0] = '\0'; + } #ifdef CONFIG_P2P if (p2p_group && wpa_s->go_params && wpa_s->go_params->ssid_len) { ssid->ssid = os_zalloc(wpa_s->go_params->ssid_len + 1); @@ -886,23 +1131,38 @@ } #endif /* CONFIG_P2P */ if (pin) - os_snprintf(val, sizeof(val), "\"pin=%s dev_pw_id=%u\"", - pin, dev_pw_id); - else { + os_snprintf(val, sizeof(val), "\"pin=%s dev_pw_id=%u%s\"", + pin, dev_pw_id, hash); + else if (pin == NULL && dev_pw_id == DEV_PW_NFC_CONNECTION_HANDOVER) { + os_snprintf(val, sizeof(val), "\"dev_pw_id=%u%s\"", + dev_pw_id, hash); + } else { rpin = wps_generate_pin(); - os_snprintf(val, sizeof(val), "\"pin=%08d dev_pw_id=%u\"", - rpin, dev_pw_id); + os_snprintf(val, sizeof(val), "\"pin=%08d dev_pw_id=%u%s\"", + rpin, dev_pw_id, hash); + } + if (wpa_config_set(ssid, "phase1", val, 0) < 0) { + wpa_printf(MSG_DEBUG, "WPS: Failed to set phase1 '%s'", val); + return -1; } - wpa_config_set(ssid, "phase1", val, 0); if (wpa_s->wps_fragment_size) ssid->eap.fragment_size = wpa_s->wps_fragment_size; eloop_register_timeout(WPS_PBC_WALK_TIME, 0, wpas_wps_timeout, wpa_s, NULL); - wpas_wps_reassoc(wpa_s, ssid); + wpa_s->wps_ap_iter = 1; + wpas_wps_reassoc(wpa_s, ssid, bssid, freq); return rpin; } +int wpas_wps_start_pin(struct wpa_supplicant *wpa_s, const u8 *bssid, + const char *pin, int p2p_group, u16 dev_pw_id) +{ + return wpas_wps_start_dev_pw(wpa_s, NULL, bssid, pin, p2p_group, + dev_pw_id, NULL, NULL, 0, 0); +} + + /* Cancel the wps pbc/pin requests */ int wpas_wps_cancel(struct wpa_supplicant *wpa_s) { @@ -913,7 +1173,8 @@ } #endif /* CONFIG_AP */ - if (wpa_s->wpa_state == WPA_SCANNING) { + if (wpa_s->wpa_state == WPA_SCANNING || + wpa_s->wpa_state == WPA_DISCONNECTED) { wpa_printf(MSG_DEBUG, "WPS: Cancel operation - cancel scan"); wpa_supplicant_cancel_scan(wpa_s); wpas_clear_wps(wpa_s); @@ -923,61 +1184,18 @@ wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_DEAUTH_LEAVING); wpas_clear_wps(wpa_s); + } else { + wpas_wps_reenable_networks(wpa_s); + wpas_wps_clear_ap_info(wpa_s); + if (eloop_cancel_timeout(wpas_wps_clear_timeout, wpa_s, NULL) > + 0) + wpas_clear_wps(wpa_s); } - return 0; -} - - -#ifdef CONFIG_WPS_OOB -int wpas_wps_start_oob(struct wpa_supplicant *wpa_s, char *device_type, - char *path, char *method, char *name) -{ - struct wps_context *wps = wpa_s->wps; - struct oob_device_data *oob_dev; - - oob_dev = wps_get_oob_device(device_type); - if (oob_dev == NULL) - return -1; - oob_dev->device_path = path; - oob_dev->device_name = name; - wps->oob_conf.oob_method = wps_get_oob_method(method); - - if (wps->oob_conf.oob_method == OOB_METHOD_DEV_PWD_E) { - /* - * Use pre-configured DH keys in order to be able to write the - * key hash into the OOB file. - */ - wpabuf_free(wps->dh_pubkey); - wpabuf_free(wps->dh_privkey); - wps->dh_privkey = NULL; - wps->dh_pubkey = NULL; - dh5_free(wps->dh_ctx); - wps->dh_ctx = dh5_init(&wps->dh_privkey, &wps->dh_pubkey); - wps->dh_pubkey = wpabuf_zeropad(wps->dh_pubkey, 192); - if (wps->dh_ctx == NULL || wps->dh_pubkey == NULL) { - wpa_printf(MSG_ERROR, "WPS: Failed to initialize " - "Diffie-Hellman handshake"); - return -1; - } - } - - if (wps->oob_conf.oob_method == OOB_METHOD_CRED) - wpas_clear_wps(wpa_s); - - if (wps_process_oob(wps, oob_dev, 0) < 0) - return -1; - - if ((wps->oob_conf.oob_method == OOB_METHOD_DEV_PWD_E || - wps->oob_conf.oob_method == OOB_METHOD_DEV_PWD_R) && - wpas_wps_start_pin(wpa_s, NULL, - wpabuf_head(wps->oob_conf.dev_password), 0, - DEV_PW_DEFAULT) < 0) - return -1; + wpa_s->after_wps = 0; return 0; } -#endif /* CONFIG_WPS_OOB */ int wpas_wps_start_reg(struct wpa_supplicant *wpa_s, const u8 *bssid, @@ -991,7 +1209,7 @@ if (!pin) return -1; wpas_clear_wps(wpa_s); - ssid = wpas_wps_add_network(wpa_s, 1, bssid); + ssid = wpas_wps_add_network(wpa_s, 1, NULL, bssid); if (ssid == NULL) return -1; ssid->temporary = 1; @@ -1013,21 +1231,31 @@ res = os_snprintf(pos, end - pos, "\""); if (res < 0 || res >= end - pos) return -1; - wpa_config_set(ssid, "phase1", val, 0); + if (wpa_config_set(ssid, "phase1", val, 0) < 0) + return -1; if (wpa_s->wps_fragment_size) ssid->eap.fragment_size = wpa_s->wps_fragment_size; eloop_register_timeout(WPS_PBC_WALK_TIME, 0, wpas_wps_timeout, wpa_s, NULL); - wpas_wps_reassoc(wpa_s, ssid); + wpas_wps_reassoc(wpa_s, ssid, bssid, 0); return 0; } -static int wpas_wps_new_psk_cb(void *ctx, const u8 *mac_addr, const u8 *psk, +static int wpas_wps_new_psk_cb(void *ctx, const u8 *mac_addr, + const u8 *p2p_dev_addr, const u8 *psk, size_t psk_len) { - wpa_printf(MSG_DEBUG, "WPS: Received new WPA/WPA2-PSK from WPS for " - "STA " MACSTR, MAC2STR(mac_addr)); + if (is_zero_ether_addr(p2p_dev_addr)) { + wpa_printf(MSG_DEBUG, + "Received new WPA/WPA2-PSK from WPS for STA " MACSTR, + MAC2STR(mac_addr)); + } else { + wpa_printf(MSG_DEBUG, + "Received new WPA/WPA2-PSK from WPS for STA " MACSTR + " P2P Device Addr " MACSTR, + MAC2STR(mac_addr), MAC2STR(p2p_dev_addr)); + } wpa_hexdump_key(MSG_DEBUG, "Per-device PSK", psk, psk_len); /* TODO */ @@ -1100,26 +1328,47 @@ static void wpas_wps_set_uuid(struct wpa_supplicant *wpa_s, struct wps_context *wps) { - wpa_printf(MSG_DEBUG, "WPS: Set UUID for interface %s", wpa_s->ifname); + char buf[50]; + const char *src; + if (is_nil_uuid(wpa_s->conf->uuid)) { struct wpa_supplicant *first; first = wpa_s->global->ifaces; while (first && first->next) first = first->next; if (first && first != wpa_s) { - os_memcpy(wps->uuid, wpa_s->global->ifaces->wps->uuid, - WPS_UUID_LEN); - wpa_hexdump(MSG_DEBUG, "WPS: UUID from the first " - "interface", wps->uuid, WPS_UUID_LEN); + if (wps != wpa_s->global->ifaces->wps) + os_memcpy(wps->uuid, + wpa_s->global->ifaces->wps->uuid, + WPS_UUID_LEN); + src = "from the first interface"; } else { uuid_gen_mac_addr(wpa_s->own_addr, wps->uuid); - wpa_hexdump(MSG_DEBUG, "WPS: UUID based on MAC " - "address", wps->uuid, WPS_UUID_LEN); + src = "based on MAC address"; } } else { os_memcpy(wps->uuid, wpa_s->conf->uuid, WPS_UUID_LEN); - wpa_hexdump(MSG_DEBUG, "WPS: UUID based on configuration", - wps->uuid, WPS_UUID_LEN); + src = "based on configuration"; + } + + uuid_bin2str(wps->uuid, buf, sizeof(buf)); + wpa_dbg(wpa_s, MSG_DEBUG, "WPS: UUID %s: %s", src, buf); +} + + +static void wpas_wps_set_vendor_ext_m1(struct wpa_supplicant *wpa_s, + struct wps_context *wps) +{ + wpabuf_free(wps->dev.vendor_ext_m1); + wps->dev.vendor_ext_m1 = NULL; + + if (wpa_s->conf->wps_vendor_ext_m1) { + wps->dev.vendor_ext_m1 = + wpabuf_dup(wpa_s->conf->wps_vendor_ext_m1); + if (!wps->dev.vendor_ext_m1) { + wpa_printf(MSG_ERROR, "WPS: Cannot " + "allocate memory for vendor_ext_m1"); + } } } @@ -1137,6 +1386,7 @@ wps->cred_cb = wpa_supplicant_wps_cred; wps->event_cb = wpa_supplicant_wps_event; + wps->rf_band_cb = wpa_supplicant_wps_rf_band; wps->cb_ctx = wpa_s; wps->dev.device_name = wpa_s->conf->device_name; @@ -1162,6 +1412,8 @@ os_memcpy(wps->dev.sec_dev_type, wpa_s->conf->sec_device_type, WPS_DEV_TYPE_LEN * wps->dev.num_sec_dev_types); + wpas_wps_set_vendor_ext_m1(wpa_s, wps); + wps->dev.os_version = WPA_GET_BE32(wpa_s->conf->os_version); modes = wpa_s->hw.modes; if (modes) { @@ -1205,9 +1457,26 @@ } +#ifdef CONFIG_WPS_ER +static void wpas_wps_nfc_clear(struct wps_context *wps) +{ + wps->ap_nfc_dev_pw_id = 0; + wpabuf_free(wps->ap_nfc_dh_pubkey); + wps->ap_nfc_dh_pubkey = NULL; + wpabuf_free(wps->ap_nfc_dh_privkey); + wps->ap_nfc_dh_privkey = NULL; + wpabuf_free(wps->ap_nfc_dev_pw); + wps->ap_nfc_dev_pw = NULL; +} +#endif /* CONFIG_WPS_ER */ + + void wpas_wps_deinit(struct wpa_supplicant *wpa_s) { eloop_cancel_timeout(wpas_wps_timeout, wpa_s, NULL); + eloop_cancel_timeout(wpas_wps_clear_timeout, wpa_s, NULL); + eloop_cancel_timeout(wpas_wps_reenable_networks_cb, wpa_s, NULL); + wpas_wps_clear_ap_info(wpa_s); if (wpa_s->wps == NULL) return; @@ -1215,13 +1484,13 @@ #ifdef CONFIG_WPS_ER wps_er_deinit(wpa_s->wps_er, NULL, NULL); wpa_s->wps_er = NULL; + wpas_wps_nfc_clear(wpa_s->wps); #endif /* CONFIG_WPS_ER */ wps_registrar_deinit(wpa_s->wps->registrar); wpabuf_free(wpa_s->wps->dh_pubkey); wpabuf_free(wpa_s->wps->dh_privkey); - wpabuf_free(wpa_s->wps->oob_conf.pubkey_hash); - wpabuf_free(wpa_s->wps->oob_conf.dev_password); + wpabuf_free(wpa_s->wps->dev.vendor_ext_m1); os_free(wpa_s->wps->network_key); os_free(wpa_s->wps); wpa_s->wps = NULL; @@ -1229,14 +1498,14 @@ int wpas_wps_ssid_bss_match(struct wpa_supplicant *wpa_s, - struct wpa_ssid *ssid, struct wpa_scan_res *bss) + struct wpa_ssid *ssid, struct wpa_bss *bss) { struct wpabuf *wps_ie; if (!(ssid->key_mgmt & WPA_KEY_MGMT_WPS)) return -1; - wps_ie = wpa_scan_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE); + wps_ie = wpa_bss_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE); if (eap_is_wps_pbc_enrollee(&ssid->eap)) { if (!wps_ie) { wpa_printf(MSG_DEBUG, " skip - non-WPS AP"); @@ -1298,19 +1567,19 @@ int wpas_wps_ssid_wildcard_ok(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, - struct wpa_scan_res *bss) + struct wpa_bss *bss) { struct wpabuf *wps_ie = NULL; int ret = 0; if (eap_is_wps_pbc_enrollee(&ssid->eap)) { - wps_ie = wpa_scan_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE); + wps_ie = wpa_bss_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE); if (wps_ie && wps_is_selected_pbc_registrar(wps_ie)) { /* allow wildcard SSID for WPS PBC */ ret = 1; } } else if (eap_is_wps_pin_enrollee(&ssid->eap)) { - wps_ie = wpa_scan_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE); + wps_ie = wpa_bss_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE); if (wps_ie && (wps_is_addr_authorized(wps_ie, wpa_s->own_addr, 1) || wpa_s->scan_runs >= WPS_PIN_SCAN_IGNORE_SEL_REG)) { @@ -1332,7 +1601,7 @@ ret = 0; if (bss->beacon_ie_len) { struct wpabuf *bcn_wps; - bcn_wps = wpa_scan_get_vendor_ie_multi_beacon( + bcn_wps = wpa_bss_get_vendor_ie_multi_beacon( bss, WPS_IE_VENDOR_TYPE); if (bcn_wps == NULL) { wpa_printf(MSG_DEBUG, "WPS: Mandatory WPS IE " @@ -1514,91 +1783,128 @@ const char *uuid, const char *pin) { u8 u[UUID_LEN]; - int any = 0; + const u8 *use_uuid = NULL; + u8 addr_buf[ETH_ALEN]; - if (os_strcmp(uuid, "any") == 0) - any = 1; - else if (uuid_str2bin(uuid, u)) + if (os_strcmp(uuid, "any") == 0) { + } else if (uuid_str2bin(uuid, u) == 0) { + use_uuid = u; + } else if (hwaddr_aton(uuid, addr_buf) == 0) { + use_uuid = wps_er_get_sta_uuid(wpa_s->wps_er, addr_buf); + if (use_uuid == NULL) + return -1; + } else return -1; return wps_registrar_add_pin(wpa_s->wps->registrar, addr, - any ? NULL : u, + use_uuid, (const u8 *) pin, os_strlen(pin), 300); } int wpas_wps_er_pbc(struct wpa_supplicant *wpa_s, const char *uuid) { - u8 u[UUID_LEN]; + u8 u[UUID_LEN], *use_uuid = NULL; + u8 addr[ETH_ALEN], *use_addr = NULL; - if (uuid_str2bin(uuid, u)) + if (uuid_str2bin(uuid, u) == 0) + use_uuid = u; + else if (hwaddr_aton(uuid, addr) == 0) + use_addr = addr; + else return -1; - return wps_er_pbc(wpa_s->wps_er, u); + return wps_er_pbc(wpa_s->wps_er, use_uuid, use_addr); } int wpas_wps_er_learn(struct wpa_supplicant *wpa_s, const char *uuid, const char *pin) { - u8 u[UUID_LEN]; + u8 u[UUID_LEN], *use_uuid = NULL; + u8 addr[ETH_ALEN], *use_addr = NULL; - if (uuid_str2bin(uuid, u)) + if (uuid_str2bin(uuid, u) == 0) + use_uuid = u; + else if (hwaddr_aton(uuid, addr) == 0) + use_addr = addr; + else return -1; - return wps_er_learn(wpa_s->wps_er, u, (const u8 *) pin, + + return wps_er_learn(wpa_s->wps_er, use_uuid, use_addr, (const u8 *) pin, os_strlen(pin)); } -int wpas_wps_er_set_config(struct wpa_supplicant *wpa_s, const char *uuid, - int id) +static int wpas_wps_network_to_cred(struct wpa_ssid *ssid, + struct wps_credential *cred) { - u8 u[UUID_LEN]; - struct wpa_ssid *ssid; - struct wps_credential cred; - - if (uuid_str2bin(uuid, u)) - return -1; - ssid = wpa_config_get_network(wpa_s->conf, id); - if (ssid == NULL || ssid->ssid == NULL) - return -1; - - os_memset(&cred, 0, sizeof(cred)); + os_memset(cred, 0, sizeof(*cred)); if (ssid->ssid_len > 32) return -1; - os_memcpy(cred.ssid, ssid->ssid, ssid->ssid_len); - cred.ssid_len = ssid->ssid_len; + os_memcpy(cred->ssid, ssid->ssid, ssid->ssid_len); + cred->ssid_len = ssid->ssid_len; if (ssid->key_mgmt & WPA_KEY_MGMT_PSK) { - cred.auth_type = (ssid->proto & WPA_PROTO_RSN) ? + cred->auth_type = (ssid->proto & WPA_PROTO_RSN) ? WPS_AUTH_WPA2PSK : WPS_AUTH_WPAPSK; if (ssid->pairwise_cipher & WPA_CIPHER_CCMP) - cred.encr_type = WPS_ENCR_AES; + cred->encr_type = WPS_ENCR_AES; else - cred.encr_type = WPS_ENCR_TKIP; + cred->encr_type = WPS_ENCR_TKIP; if (ssid->passphrase) { - cred.key_len = os_strlen(ssid->passphrase); - if (cred.key_len >= 64) + cred->key_len = os_strlen(ssid->passphrase); + if (cred->key_len >= 64) return -1; - os_memcpy(cred.key, ssid->passphrase, cred.key_len); + os_memcpy(cred->key, ssid->passphrase, cred->key_len); } else if (ssid->psk_set) { - cred.key_len = 32; - os_memcpy(cred.key, ssid->psk, 32); + cred->key_len = 32; + os_memcpy(cred->key, ssid->psk, 32); } else return -1; } else { - cred.auth_type = WPS_AUTH_OPEN; - cred.encr_type = WPS_ENCR_NONE; + cred->auth_type = WPS_AUTH_OPEN; + cred->encr_type = WPS_ENCR_NONE; } - return wps_er_set_config(wpa_s->wps_er, u, &cred); + + return 0; +} + + +int wpas_wps_er_set_config(struct wpa_supplicant *wpa_s, const char *uuid, + int id) +{ + u8 u[UUID_LEN], *use_uuid = NULL; + u8 addr[ETH_ALEN], *use_addr = NULL; + struct wpa_ssid *ssid; + struct wps_credential cred; + + if (uuid_str2bin(uuid, u) == 0) + use_uuid = u; + else if (hwaddr_aton(uuid, addr) == 0) + use_addr = addr; + else + return -1; + ssid = wpa_config_get_network(wpa_s->conf, id); + if (ssid == NULL || ssid->ssid == NULL) + return -1; + + if (wpas_wps_network_to_cred(ssid, &cred) < 0) + return -1; + return wps_er_set_config(wpa_s->wps_er, use_uuid, use_addr, &cred); } int wpas_wps_er_config(struct wpa_supplicant *wpa_s, const char *uuid, const char *pin, struct wps_new_ap_settings *settings) { - u8 u[UUID_LEN]; + u8 u[UUID_LEN], *use_uuid = NULL; + u8 addr[ETH_ALEN], *use_addr = NULL; struct wps_credential cred; size_t len; - if (uuid_str2bin(uuid, u)) + if (uuid_str2bin(uuid, u) == 0) + use_uuid = u; + else if (hwaddr_aton(uuid, addr) == 0) + use_addr = addr; + else return -1; if (settings->ssid_hex == NULL || settings->auth == NULL || settings->encr == NULL || settings->key_hex == NULL) @@ -1637,9 +1943,42 @@ else return -1; - return wps_er_config(wpa_s->wps_er, u, (const u8 *) pin, - os_strlen(pin), &cred); + return wps_er_config(wpa_s->wps_er, use_uuid, use_addr, + (const u8 *) pin, os_strlen(pin), &cred); +} + + +#ifdef CONFIG_WPS_NFC +struct wpabuf * wpas_wps_er_nfc_config_token(struct wpa_supplicant *wpa_s, + int ndef, const char *uuid) +{ + struct wpabuf *ret; + u8 u[UUID_LEN], *use_uuid = NULL; + u8 addr[ETH_ALEN], *use_addr = NULL; + + if (!wpa_s->wps_er) + return NULL; + + if (uuid_str2bin(uuid, u) == 0) + use_uuid = u; + else if (hwaddr_aton(uuid, addr) == 0) + use_addr = addr; + else + return NULL; + + ret = wps_er_nfc_config_token(wpa_s->wps_er, use_uuid, use_addr); + if (ndef && ret) { + struct wpabuf *tmp; + tmp = ndef_build_wifi(ret); + wpabuf_free(ret); + if (tmp == NULL) + return NULL; + ret = tmp; + } + + return ret; } +#endif /* CONFIG_WPS_NFC */ static int callbacks_pending = 0; @@ -1667,19 +2006,6 @@ } -int wpas_wps_in_progress(struct wpa_supplicant *wpa_s) -{ - struct wpa_ssid *ssid; - - for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) { - if (!ssid->disabled && ssid->key_mgmt == WPA_KEY_MGMT_WPS) - return 1; - } - - return 0; -} - - void wpas_wps_update_config(struct wpa_supplicant *wpa_s) { struct wps_context *wps = wpa_s->wps; @@ -1700,6 +2026,7 @@ } } wps->config_methods = wps_fix_config_methods(wps->config_methods); + wps->dev.config_methods = wps->config_methods; if (wpa_s->conf->changed_parameters & CFG_CHANGED_DEVICE_TYPE) os_memcpy(wps->dev.pri_dev_type, wpa_s->conf->device_type, @@ -1711,6 +2038,9 @@ wps->dev.num_sec_dev_types * WPS_DEV_TYPE_LEN); } + if (wpa_s->conf->changed_parameters & CFG_CHANGED_VENDOR_EXTENSION) + wpas_wps_set_vendor_ext_m1(wpa_s, wps); + if (wpa_s->conf->changed_parameters & CFG_CHANGED_OS_VERSION) wps->dev.os_version = WPA_GET_BE32(wpa_s->conf->os_version); @@ -1727,3 +2057,730 @@ wps->dev.serial_number = wpa_s->conf->serial_number; } } + + +#ifdef CONFIG_WPS_NFC + +#ifdef CONFIG_WPS_ER +static struct wpabuf * +wpas_wps_network_config_token(struct wpa_supplicant *wpa_s, int ndef, + struct wpa_ssid *ssid) +{ + struct wpabuf *ret; + struct wps_credential cred; + + if (wpas_wps_network_to_cred(ssid, &cred) < 0) + return NULL; + + ret = wps_er_config_token_from_cred(wpa_s->wps, &cred); + + if (ndef && ret) { + struct wpabuf *tmp; + tmp = ndef_build_wifi(ret); + wpabuf_free(ret); + if (tmp == NULL) + return NULL; + ret = tmp; + } + + return ret; +} +#endif /* CONFIG_WPS_ER */ + + +struct wpabuf * wpas_wps_nfc_config_token(struct wpa_supplicant *wpa_s, + int ndef, const char *id_str) +{ +#ifdef CONFIG_WPS_ER + if (id_str) { + int id; + char *end = NULL; + struct wpa_ssid *ssid; + + id = strtol(id_str, &end, 10); + if (end && *end) + return NULL; + + ssid = wpa_config_get_network(wpa_s->conf, id); + if (ssid == NULL) + return NULL; + return wpas_wps_network_config_token(wpa_s, ndef, ssid); + } +#endif /* CONFIG_WPS_ER */ +#ifdef CONFIG_AP + if (wpa_s->ap_iface) + return wpas_ap_wps_nfc_config_token(wpa_s, ndef); +#endif /* CONFIG_AP */ + return NULL; +} + + +struct wpabuf * wpas_wps_nfc_token(struct wpa_supplicant *wpa_s, int ndef) +{ + if (wpa_s->conf->wps_nfc_pw_from_config) { + return wps_nfc_token_build(ndef, + wpa_s->conf->wps_nfc_dev_pw_id, + wpa_s->conf->wps_nfc_dh_pubkey, + wpa_s->conf->wps_nfc_dev_pw); + } + + return wps_nfc_token_gen(ndef, &wpa_s->conf->wps_nfc_dev_pw_id, + &wpa_s->conf->wps_nfc_dh_pubkey, + &wpa_s->conf->wps_nfc_dh_privkey, + &wpa_s->conf->wps_nfc_dev_pw); +} + + +int wpas_wps_start_nfc(struct wpa_supplicant *wpa_s, const u8 *go_dev_addr, + const u8 *bssid, + const struct wpabuf *dev_pw, u16 dev_pw_id, + int p2p_group, const u8 *peer_pubkey_hash, + const u8 *ssid, size_t ssid_len, int freq) +{ + struct wps_context *wps = wpa_s->wps; + char pw[32 * 2 + 1]; + + if (dev_pw_id != DEV_PW_NFC_CONNECTION_HANDOVER && dev_pw == NULL) { + dev_pw = wpa_s->conf->wps_nfc_dev_pw; + dev_pw_id = wpa_s->conf->wps_nfc_dev_pw_id; + } + + if (wpa_s->conf->wps_nfc_dh_pubkey == NULL || + wpa_s->conf->wps_nfc_dh_privkey == NULL) { + wpa_printf(MSG_DEBUG, "WPS: Missing DH params - " + "cannot start NFC-triggered connection"); + return -1; + } + + if (dev_pw_id != DEV_PW_NFC_CONNECTION_HANDOVER && dev_pw == NULL) { + wpa_printf(MSG_DEBUG, "WPS: Missing Device Password (id=%u) - " + "cannot start NFC-triggered connection", dev_pw_id); + return -1; + } + + dh5_free(wps->dh_ctx); + wpabuf_free(wps->dh_pubkey); + wpabuf_free(wps->dh_privkey); + wps->dh_privkey = wpabuf_dup(wpa_s->conf->wps_nfc_dh_privkey); + wps->dh_pubkey = wpabuf_dup(wpa_s->conf->wps_nfc_dh_pubkey); + if (wps->dh_privkey == NULL || wps->dh_pubkey == NULL) { + wps->dh_ctx = NULL; + wpabuf_free(wps->dh_pubkey); + wps->dh_pubkey = NULL; + wpabuf_free(wps->dh_privkey); + wps->dh_privkey = NULL; + wpa_printf(MSG_DEBUG, "WPS: Failed to get DH priv/pub key"); + return -1; + } + wps->dh_ctx = dh5_init_fixed(wps->dh_privkey, wps->dh_pubkey); + if (wps->dh_ctx == NULL) { + wpabuf_free(wps->dh_pubkey); + wps->dh_pubkey = NULL; + wpabuf_free(wps->dh_privkey); + wps->dh_privkey = NULL; + wpa_printf(MSG_DEBUG, "WPS: Failed to initialize DH context"); + return -1; + } + + if (dev_pw) { + wpa_snprintf_hex_uppercase(pw, sizeof(pw), + wpabuf_head(dev_pw), + wpabuf_len(dev_pw)); + } + return wpas_wps_start_dev_pw(wpa_s, go_dev_addr, bssid, + dev_pw ? pw : NULL, + p2p_group, dev_pw_id, peer_pubkey_hash, + ssid, ssid_len, freq); +} + + +static int wpas_wps_use_cred(struct wpa_supplicant *wpa_s, + struct wps_parse_attr *attr) +{ + wpa_s->wps_ap_channel = 0; + + /* + * Disable existing networks temporarily to allow the newly learned + * credential to be preferred. Enable the temporarily disabled networks + * after 10 seconds. + */ + wpas_wps_temp_disable(wpa_s, NULL); + eloop_register_timeout(10, 0, wpas_wps_reenable_networks_cb, wpa_s, + NULL); + + if (wps_oob_use_cred(wpa_s->wps, attr) < 0) + return -1; + + if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) + return 0; + + if (!wpa_s->wps_ap_channel && attr->ap_channel) { + wpa_s->wps_ap_channel = WPA_GET_BE16(attr->ap_channel); + wpa_printf(MSG_DEBUG, "WPS: Credential container indicated AP Channel %d", + wpa_s->wps_ap_channel); + } + + wpa_printf(MSG_DEBUG, "WPS: Request reconnection with new network " + "based on the received credential added"); + wpa_s->normal_scans = 0; + wpa_supplicant_reinit_autoscan(wpa_s); + if (wpa_s->wps_ap_channel) { + u16 chan = wpa_s->wps_ap_channel; + int freq = 0; + + if (chan >= 1 && chan <= 13) + freq = 2407 + 5 * chan; + else if (chan == 14) + freq = 2484; + else if (chan >= 30) + freq = 5000 + 5 * chan; + + if (freq) { + wpa_printf(MSG_DEBUG, "WPS: Credential indicated " + "AP channel %u -> %u MHz", chan, freq); + wpa_s->after_wps = 5; + wpa_s->wps_freq = freq; + } + } + wpa_s->disconnected = 0; + wpa_s->reassociate = 1; + + wpa_supplicant_cancel_sched_scan(wpa_s); + wpa_supplicant_req_scan(wpa_s, 0, 0); + + return 0; +} + + +#ifdef CONFIG_WPS_ER +static int wpas_wps_add_nfc_password_token(struct wpa_supplicant *wpa_s, + struct wps_parse_attr *attr) +{ + return wps_registrar_add_nfc_password_token( + wpa_s->wps->registrar, attr->oob_dev_password, + attr->oob_dev_password_len); +} +#endif /* CONFIG_WPS_ER */ + + +static int wpas_wps_nfc_tag_process(struct wpa_supplicant *wpa_s, + const struct wpabuf *wps) +{ + struct wps_parse_attr attr; + + wpa_hexdump_buf(MSG_DEBUG, "WPS: Received NFC tag payload", wps); + + if (wps_parse_msg(wps, &attr)) { + wpa_printf(MSG_DEBUG, "WPS: Ignore invalid data from NFC tag"); + return -1; + } + + if (attr.num_cred) + return wpas_wps_use_cred(wpa_s, &attr); + +#ifdef CONFIG_WPS_ER + if (attr.oob_dev_password) + return wpas_wps_add_nfc_password_token(wpa_s, &attr); +#endif /* CONFIG_WPS_ER */ + + wpa_printf(MSG_DEBUG, "WPS: Ignore unrecognized NFC tag"); + return -1; +} + + +int wpas_wps_nfc_tag_read(struct wpa_supplicant *wpa_s, + const struct wpabuf *data, int forced_freq) +{ + const struct wpabuf *wps = data; + struct wpabuf *tmp = NULL; + int ret; + + if (wpabuf_len(data) < 4) + return -1; + + if (*wpabuf_head_u8(data) != 0x10) { + /* Assume this contains full NDEF record */ + tmp = ndef_parse_wifi(data); + if (tmp == NULL) { +#ifdef CONFIG_P2P + tmp = ndef_parse_p2p(data); + if (tmp) { + ret = wpas_p2p_nfc_tag_process(wpa_s, tmp, + forced_freq); + wpabuf_free(tmp); + return ret; + } +#endif /* CONFIG_P2P */ + wpa_printf(MSG_DEBUG, "WPS: Could not parse NDEF"); + return -1; + } + wps = tmp; + } + + ret = wpas_wps_nfc_tag_process(wpa_s, wps); + wpabuf_free(tmp); + return ret; +} + + +struct wpabuf * wpas_wps_nfc_handover_req(struct wpa_supplicant *wpa_s, + int ndef) +{ + struct wpabuf *ret; + + if (wpa_s->conf->wps_nfc_dh_pubkey == NULL && + wps_nfc_gen_dh(&wpa_s->conf->wps_nfc_dh_pubkey, + &wpa_s->conf->wps_nfc_dh_privkey) < 0) + return NULL; + + ret = wps_build_nfc_handover_req(wpa_s->wps, + wpa_s->conf->wps_nfc_dh_pubkey); + + if (ndef && ret) { + struct wpabuf *tmp; + tmp = ndef_build_wifi(ret); + wpabuf_free(ret); + if (tmp == NULL) + return NULL; + ret = tmp; + } + + return ret; +} + + +#ifdef CONFIG_WPS_NFC + +static struct wpabuf * +wpas_wps_er_nfc_handover_sel(struct wpa_supplicant *wpa_s, int ndef, + const char *uuid) +{ +#ifdef CONFIG_WPS_ER + struct wpabuf *ret; + u8 u[UUID_LEN], *use_uuid = NULL; + u8 addr[ETH_ALEN], *use_addr = NULL; + struct wps_context *wps = wpa_s->wps; + + if (wps == NULL) + return NULL; + + if (uuid == NULL) + return NULL; + if (uuid_str2bin(uuid, u) == 0) + use_uuid = u; + else if (hwaddr_aton(uuid, addr) == 0) + use_addr = addr; + else + return NULL; + + if (wpa_s->conf->wps_nfc_dh_pubkey == NULL) { + if (wps_nfc_gen_dh(&wpa_s->conf->wps_nfc_dh_pubkey, + &wpa_s->conf->wps_nfc_dh_privkey) < 0) + return NULL; + } + + wpas_wps_nfc_clear(wps); + wps->ap_nfc_dev_pw_id = DEV_PW_NFC_CONNECTION_HANDOVER; + wps->ap_nfc_dh_pubkey = wpabuf_dup(wpa_s->conf->wps_nfc_dh_pubkey); + wps->ap_nfc_dh_privkey = wpabuf_dup(wpa_s->conf->wps_nfc_dh_privkey); + if (!wps->ap_nfc_dh_pubkey || !wps->ap_nfc_dh_privkey) { + wpas_wps_nfc_clear(wps); + return NULL; + } + + ret = wps_er_nfc_handover_sel(wpa_s->wps_er, wpa_s->wps, use_uuid, + use_addr, wpa_s->conf->wps_nfc_dh_pubkey); + if (ndef && ret) { + struct wpabuf *tmp; + tmp = ndef_build_wifi(ret); + wpabuf_free(ret); + if (tmp == NULL) + return NULL; + ret = tmp; + } + + return ret; +#else /* CONFIG_WPS_ER */ + return NULL; +#endif /* CONFIG_WPS_ER */ +} +#endif /* CONFIG_WPS_NFC */ + + +struct wpabuf * wpas_wps_nfc_handover_sel(struct wpa_supplicant *wpa_s, + int ndef, int cr, const char *uuid) +{ + struct wpabuf *ret; + if (!cr) + return NULL; + ret = wpas_ap_wps_nfc_handover_sel(wpa_s, ndef); + if (ret) + return ret; + return wpas_wps_er_nfc_handover_sel(wpa_s, ndef, uuid); +} + + +int wpas_wps_nfc_rx_handover_req(struct wpa_supplicant *wpa_s, + const struct wpabuf *data) +{ + /* TODO */ + return -1; +} + + +int wpas_wps_nfc_rx_handover_sel(struct wpa_supplicant *wpa_s, + const struct wpabuf *data) +{ + struct wpabuf *wps; + int ret = -1; + u16 wsc_len; + const u8 *pos; + struct wpabuf msg; + struct wps_parse_attr attr; + u16 dev_pw_id; + const u8 *bssid = NULL; + int freq = 0; + + wps = ndef_parse_wifi(data); + if (wps == NULL) + return -1; + wpa_printf(MSG_DEBUG, "WPS: Received application/vnd.wfa.wsc " + "payload from NFC connection handover"); + wpa_hexdump_buf(MSG_DEBUG, "WPS: NFC payload", wps); + if (wpabuf_len(wps) < 2) { + wpa_printf(MSG_DEBUG, "WPS: Too short Wi-Fi Handover Select " + "Message"); + goto out; + } + pos = wpabuf_head(wps); + wsc_len = WPA_GET_BE16(pos); + if (wsc_len > wpabuf_len(wps) - 2) { + wpa_printf(MSG_DEBUG, "WPS: Invalid WSC attribute length (%u) " + "in Wi-Fi Handover Select Message", wsc_len); + goto out; + } + pos += 2; + + wpa_hexdump(MSG_DEBUG, + "WPS: WSC attributes in Wi-Fi Handover Select Message", + pos, wsc_len); + if (wsc_len < wpabuf_len(wps) - 2) { + wpa_hexdump(MSG_DEBUG, + "WPS: Ignore extra data after WSC attributes", + pos + wsc_len, wpabuf_len(wps) - 2 - wsc_len); + } + + wpabuf_set(&msg, pos, wsc_len); + ret = wps_parse_msg(&msg, &attr); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "WPS: Could not parse WSC attributes in " + "Wi-Fi Handover Select Message"); + goto out; + } + + if (attr.oob_dev_password == NULL || + attr.oob_dev_password_len < WPS_OOB_PUBKEY_HASH_LEN + 2) { + wpa_printf(MSG_DEBUG, "WPS: No Out-of-Band Device Password " + "included in Wi-Fi Handover Select Message"); + ret = -1; + goto out; + } + + if (attr.ssid == NULL) { + wpa_printf(MSG_DEBUG, "WPS: No SSID included in Wi-Fi Handover " + "Select Message"); + ret = -1; + goto out; + } + + wpa_hexdump_ascii(MSG_DEBUG, "WPS: SSID", attr.ssid, attr.ssid_len); + + if (attr.mac_addr) { + bssid = attr.mac_addr; + wpa_printf(MSG_DEBUG, "WPS: MAC Address (BSSID): " MACSTR, + MAC2STR(bssid)); + } + + if (attr.rf_bands) + wpa_printf(MSG_DEBUG, "WPS: RF Bands: %d", *attr.rf_bands); + + if (attr.ap_channel) { + u16 chan = WPA_GET_BE16(attr.ap_channel); + + wpa_printf(MSG_DEBUG, "WPS: AP Channel: %d", chan); + + if (chan >= 1 && chan <= 13 && + (attr.rf_bands == NULL || *attr.rf_bands & WPS_RF_24GHZ)) + freq = 2407 + 5 * chan; + else if (chan == 14 && + (attr.rf_bands == NULL || + *attr.rf_bands & WPS_RF_24GHZ)) + freq = 2484; + else if (chan >= 30 && + (attr.rf_bands == NULL || + *attr.rf_bands & WPS_RF_50GHZ)) + freq = 5000 + 5 * chan; + + if (freq) { + wpa_printf(MSG_DEBUG, + "WPS: AP indicated channel %u -> %u MHz", + chan, freq); + } + } + + wpa_hexdump(MSG_DEBUG, "WPS: Out-of-Band Device Password", + attr.oob_dev_password, attr.oob_dev_password_len); + dev_pw_id = WPA_GET_BE16(attr.oob_dev_password + + WPS_OOB_PUBKEY_HASH_LEN); + if (dev_pw_id != DEV_PW_NFC_CONNECTION_HANDOVER) { + wpa_printf(MSG_DEBUG, "WPS: Unexpected OOB Device Password ID " + "%u in Wi-Fi Handover Select Message", dev_pw_id); + ret = -1; + goto out; + } + wpa_hexdump(MSG_DEBUG, "WPS: AP Public Key hash", + attr.oob_dev_password, WPS_OOB_PUBKEY_HASH_LEN); + + ret = wpas_wps_start_nfc(wpa_s, NULL, bssid, NULL, dev_pw_id, 0, + attr.oob_dev_password, + attr.ssid, attr.ssid_len, freq); + +out: + wpabuf_free(wps); + return ret; +} + + +int wpas_wps_nfc_report_handover(struct wpa_supplicant *wpa_s, + const struct wpabuf *req, + const struct wpabuf *sel) +{ + wpa_printf(MSG_DEBUG, "NFC: WPS connection handover reported"); + wpa_hexdump_buf_key(MSG_DEBUG, "WPS: Carrier record in request", req); + wpa_hexdump_buf_key(MSG_DEBUG, "WPS: Carrier record in select", sel); + return wpas_wps_nfc_rx_handover_sel(wpa_s, sel); +} + + +int wpas_er_wps_nfc_report_handover(struct wpa_supplicant *wpa_s, + const struct wpabuf *req, + const struct wpabuf *sel) +{ + struct wpabuf *wps; + int ret = -1; + u16 wsc_len; + const u8 *pos; + struct wpabuf msg; + struct wps_parse_attr attr; + u16 dev_pw_id; + + /* + * Enrollee/station is always initiator of the NFC connection handover, + * so use the request message here to find Enrollee public key hash. + */ + wps = ndef_parse_wifi(req); + if (wps == NULL) + return -1; + wpa_printf(MSG_DEBUG, "WPS: Received application/vnd.wfa.wsc " + "payload from NFC connection handover"); + wpa_hexdump_buf(MSG_DEBUG, "WPS: NFC payload", wps); + if (wpabuf_len(wps) < 2) { + wpa_printf(MSG_DEBUG, "WPS: Too short Wi-Fi Handover Request " + "Message"); + goto out; + } + pos = wpabuf_head(wps); + wsc_len = WPA_GET_BE16(pos); + if (wsc_len > wpabuf_len(wps) - 2) { + wpa_printf(MSG_DEBUG, "WPS: Invalid WSC attribute length (%u) " + "in rt Wi-Fi Handover Request Message", wsc_len); + goto out; + } + pos += 2; + + wpa_hexdump(MSG_DEBUG, + "WPS: WSC attributes in Wi-Fi Handover Request Message", + pos, wsc_len); + if (wsc_len < wpabuf_len(wps) - 2) { + wpa_hexdump(MSG_DEBUG, + "WPS: Ignore extra data after WSC attributes", + pos + wsc_len, wpabuf_len(wps) - 2 - wsc_len); + } + + wpabuf_set(&msg, pos, wsc_len); + ret = wps_parse_msg(&msg, &attr); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "WPS: Could not parse WSC attributes in " + "Wi-Fi Handover Request Message"); + goto out; + } + + if (attr.oob_dev_password == NULL || + attr.oob_dev_password_len < WPS_OOB_PUBKEY_HASH_LEN + 2) { + wpa_printf(MSG_DEBUG, "WPS: No Out-of-Band Device Password " + "included in Wi-Fi Handover Request Message"); + ret = -1; + goto out; + } + + if (attr.uuid_e == NULL) { + wpa_printf(MSG_DEBUG, "WPS: No UUID-E included in Wi-Fi " + "Handover Request Message"); + ret = -1; + goto out; + } + + wpa_hexdump(MSG_DEBUG, "WPS: UUID-E", attr.uuid_e, WPS_UUID_LEN); + + wpa_hexdump(MSG_DEBUG, "WPS: Out-of-Band Device Password", + attr.oob_dev_password, attr.oob_dev_password_len); + dev_pw_id = WPA_GET_BE16(attr.oob_dev_password + + WPS_OOB_PUBKEY_HASH_LEN); + if (dev_pw_id != DEV_PW_NFC_CONNECTION_HANDOVER) { + wpa_printf(MSG_DEBUG, "WPS: Unexpected OOB Device Password ID " + "%u in Wi-Fi Handover Request Message", dev_pw_id); + ret = -1; + goto out; + } + wpa_hexdump(MSG_DEBUG, "WPS: Enrollee Public Key hash", + attr.oob_dev_password, WPS_OOB_PUBKEY_HASH_LEN); + + ret = wps_registrar_add_nfc_pw_token(wpa_s->wps->registrar, + attr.oob_dev_password, + DEV_PW_NFC_CONNECTION_HANDOVER, + NULL, 0, 1); + +out: + wpabuf_free(wps); + return ret; +} + +#endif /* CONFIG_WPS_NFC */ + + +static void wpas_wps_dump_ap_info(struct wpa_supplicant *wpa_s) +{ + size_t i; + struct os_reltime now; + + if (wpa_debug_level > MSG_DEBUG) + return; + + if (wpa_s->wps_ap == NULL) + return; + + os_get_reltime(&now); + + for (i = 0; i < wpa_s->num_wps_ap; i++) { + struct wps_ap_info *ap = &wpa_s->wps_ap[i]; + struct wpa_blacklist *e = wpa_blacklist_get(wpa_s, ap->bssid); + + wpa_printf(MSG_DEBUG, "WPS: AP[%d] " MACSTR " type=%d " + "tries=%d last_attempt=%d sec ago blacklist=%d", + (int) i, MAC2STR(ap->bssid), ap->type, ap->tries, + ap->last_attempt.sec > 0 ? + (int) now.sec - (int) ap->last_attempt.sec : -1, + e ? e->count : 0); + } +} + + +static struct wps_ap_info * wpas_wps_get_ap_info(struct wpa_supplicant *wpa_s, + const u8 *bssid) +{ + size_t i; + + if (wpa_s->wps_ap == NULL) + return NULL; + + for (i = 0; i < wpa_s->num_wps_ap; i++) { + struct wps_ap_info *ap = &wpa_s->wps_ap[i]; + if (os_memcmp(ap->bssid, bssid, ETH_ALEN) == 0) + return ap; + } + + return NULL; +} + + +static void wpas_wps_update_ap_info_bss(struct wpa_supplicant *wpa_s, + struct wpa_scan_res *res) +{ + struct wpabuf *wps; + enum wps_ap_info_type type; + struct wps_ap_info *ap; + int r; + + if (wpa_scan_get_vendor_ie(res, WPS_IE_VENDOR_TYPE) == NULL) + return; + + wps = wpa_scan_get_vendor_ie_multi(res, WPS_IE_VENDOR_TYPE); + if (wps == NULL) + return; + + r = wps_is_addr_authorized(wps, wpa_s->own_addr, 1); + if (r == 2) + type = WPS_AP_SEL_REG_OUR; + else if (r == 1) + type = WPS_AP_SEL_REG; + else + type = WPS_AP_NOT_SEL_REG; + + wpabuf_free(wps); + + ap = wpas_wps_get_ap_info(wpa_s, res->bssid); + if (ap) { + if (ap->type != type) { + wpa_printf(MSG_DEBUG, "WPS: AP " MACSTR + " changed type %d -> %d", + MAC2STR(res->bssid), ap->type, type); + ap->type = type; + if (type != WPS_AP_NOT_SEL_REG) + wpa_blacklist_del(wpa_s, ap->bssid); + } + return; + } + + ap = os_realloc_array(wpa_s->wps_ap, wpa_s->num_wps_ap + 1, + sizeof(struct wps_ap_info)); + if (ap == NULL) + return; + + wpa_s->wps_ap = ap; + ap = &wpa_s->wps_ap[wpa_s->num_wps_ap]; + wpa_s->num_wps_ap++; + + os_memset(ap, 0, sizeof(*ap)); + os_memcpy(ap->bssid, res->bssid, ETH_ALEN); + ap->type = type; + wpa_printf(MSG_DEBUG, "WPS: AP " MACSTR " type %d added", + MAC2STR(ap->bssid), ap->type); +} + + +void wpas_wps_update_ap_info(struct wpa_supplicant *wpa_s, + struct wpa_scan_results *scan_res) +{ + size_t i; + + for (i = 0; i < scan_res->num; i++) + wpas_wps_update_ap_info_bss(wpa_s, scan_res->res[i]); + + wpas_wps_dump_ap_info(wpa_s); +} + + +void wpas_wps_notify_assoc(struct wpa_supplicant *wpa_s, const u8 *bssid) +{ + struct wps_ap_info *ap; + + wpa_s->after_wps = 0; + + if (!wpa_s->wps_ap_iter) + return; + ap = wpas_wps_get_ap_info(wpa_s, bssid); + if (ap == NULL) + return; + ap->tries++; + os_get_reltime(&ap->last_attempt); +} diff -Nru wpa-1.0/wpa_supplicant/wps_supplicant.h wpa-2.1/wpa_supplicant/wps_supplicant.h --- wpa-1.0/wpa_supplicant/wps_supplicant.h 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/wps_supplicant.h 2014-02-04 11:23:35.000000000 +0000 @@ -1,21 +1,15 @@ /* * wpa_supplicant / WPS integration - * Copyright (c) 2008-2009, Jouni Malinen + * Copyright (c) 2008-2012, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef WPS_SUPPLICANT_H #define WPS_SUPPLICANT_H -struct wpa_scan_res; +struct wpa_scan_results; #ifdef CONFIG_WPS @@ -40,14 +34,12 @@ int wpas_wps_start_pin(struct wpa_supplicant *wpa_s, const u8 *bssid, const char *pin, int p2p_group, u16 dev_pw_id); int wpas_wps_cancel(struct wpa_supplicant *wpa_s); -int wpas_wps_start_oob(struct wpa_supplicant *wpa_s, char *device_type, - char *path, char *method, char *name); int wpas_wps_start_reg(struct wpa_supplicant *wpa_s, const u8 *bssid, const char *pin, struct wps_new_ap_settings *settings); int wpas_wps_ssid_bss_match(struct wpa_supplicant *wpa_s, - struct wpa_ssid *ssid, struct wpa_scan_res *bss); + struct wpa_ssid *ssid, struct wpa_bss *bss); int wpas_wps_ssid_wildcard_ok(struct wpa_supplicant *wpa_s, - struct wpa_ssid *ssid, struct wpa_scan_res *bss); + struct wpa_ssid *ssid, struct wpa_bss *bss); int wpas_wps_scan_pbc_overlap(struct wpa_supplicant *wpa_s, struct wpa_bss *selected, struct wpa_ssid *ssid); void wpas_wps_notify_scan_results(struct wpa_supplicant *wpa_s); @@ -65,9 +57,37 @@ int id); int wpas_wps_er_config(struct wpa_supplicant *wpa_s, const char *uuid, const char *pin, struct wps_new_ap_settings *settings); +struct wpabuf * wpas_wps_er_nfc_config_token(struct wpa_supplicant *wpa_s, + int ndef, const char *uuid); int wpas_wps_terminate_pending(struct wpa_supplicant *wpa_s); -int wpas_wps_in_progress(struct wpa_supplicant *wpa_s); void wpas_wps_update_config(struct wpa_supplicant *wpa_s); +struct wpabuf * wpas_wps_nfc_config_token(struct wpa_supplicant *wpa_s, + int ndef, const char *id_str); +struct wpabuf * wpas_wps_nfc_token(struct wpa_supplicant *wpa_s, int ndef); +int wpas_wps_start_nfc(struct wpa_supplicant *wpa_s, const u8 *dev_addr, + const u8 *bssid, + const struct wpabuf *dev_pw, u16 dev_pw_id, + int p2p_group, const u8 *peer_pubkey_hash, + const u8 *ssid, size_t ssid_len, int freq); +int wpas_wps_nfc_tag_read(struct wpa_supplicant *wpa_s, + const struct wpabuf *data, int forced_freq); +struct wpabuf * wpas_wps_nfc_handover_req(struct wpa_supplicant *wpa_s, + int ndef); +struct wpabuf * wpas_wps_nfc_handover_sel(struct wpa_supplicant *wpa_s, + int ndef, int cr, const char *uuid); +int wpas_wps_nfc_rx_handover_req(struct wpa_supplicant *wpa_s, + const struct wpabuf *data); +int wpas_wps_nfc_rx_handover_sel(struct wpa_supplicant *wpa_s, + const struct wpabuf *data); +int wpas_wps_nfc_report_handover(struct wpa_supplicant *wpa_s, + const struct wpabuf *req, + const struct wpabuf *sel); +int wpas_er_wps_nfc_report_handover(struct wpa_supplicant *wpa_s, + const struct wpabuf *req, + const struct wpabuf *sel); +void wpas_wps_update_ap_info(struct wpa_supplicant *wpa_s, + struct wpa_scan_results *scan_res); +void wpas_wps_notify_assoc(struct wpa_supplicant *wpa_s, const u8 *bssid); #else /* CONFIG_WPS */ @@ -92,14 +112,14 @@ static inline int wpas_wps_ssid_bss_match(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, - struct wpa_scan_res *bss) + struct wpa_bss *bss) { return -1; } static inline int wpas_wps_ssid_wildcard_ok(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, - struct wpa_scan_res *bss) + struct wpa_bss *bss) { return 0; } @@ -120,6 +140,16 @@ return 0; } +static inline void wpas_wps_update_ap_info(struct wpa_supplicant *wpa_s, + struct wpa_scan_results *scan_res) +{ +} + +static inline void wpas_wps_notify_assoc(struct wpa_supplicant *wpa_s, + const u8 *bssid) +{ +} + #endif /* CONFIG_WPS */ #endif /* WPS_SUPPLICANT_H */ diff -Nru wpa-1.0/wpa_supplicant/xcode/wpa_supplicant.xcodeproj/project.pbxproj wpa-2.1/wpa_supplicant/xcode/wpa_supplicant.xcodeproj/project.pbxproj --- wpa-1.0/wpa_supplicant/xcode/wpa_supplicant.xcodeproj/project.pbxproj 2012-05-09 21:56:09.000000000 +0000 +++ wpa-2.1/wpa_supplicant/xcode/wpa_supplicant.xcodeproj/project.pbxproj 1970-01-01 00:00:00.000000000 +0000 @@ -1,513 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 45; - objects = { - -/* Begin PBXBuildFile section */ - 881EED0F10DC14EF009E449F /* eap_register.c in Sources */ = {isa = PBXBuildFile; fileRef = 881EED0E10DC14EF009E449F /* eap_register.c */; }; - 8853CB17109F385C00358CEF /* libpcap.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 8853CB16109F385C00358CEF /* libpcap.dylib */; }; - 8853CB1B109F389800358CEF /* libcrypto.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 8853CB1A109F389800358CEF /* libcrypto.dylib */; }; - 8853CB1F109F38BD00358CEF /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8853CB1E109F38BD00358CEF /* CoreFoundation.framework */; }; - 8853CB2E109F3A3900358CEF /* scan_helpers.c in Sources */ = {isa = PBXBuildFile; fileRef = 8853CB2D109F3A3900358CEF /* scan_helpers.c */; }; - 8853CB32109F3A9400358CEF /* wpa_common.c in Sources */ = {isa = PBXBuildFile; fileRef = 8853CB31109F3A9400358CEF /* wpa_common.c */; }; - 8853CB36109F3AC700358CEF /* md5.c in Sources */ = {isa = PBXBuildFile; fileRef = 8853CB35109F3AC700358CEF /* md5.c */; }; - 8853CB3C109F3B5800358CEF /* Apple80211.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8853CB3B109F3B5800358CEF /* Apple80211.framework */; }; - 8853CBFB109F4C6E00358CEF /* eap_gtc.c in Sources */ = {isa = PBXBuildFile; fileRef = 8853CBEC109F4C6E00358CEF /* eap_gtc.c */; }; - 8853CBFC109F4C6E00358CEF /* eap_leap.c in Sources */ = {isa = PBXBuildFile; fileRef = 8853CBED109F4C6E00358CEF /* eap_leap.c */; }; - 8853CBFD109F4C6E00358CEF /* eap_md5.c in Sources */ = {isa = PBXBuildFile; fileRef = 8853CBEE109F4C6E00358CEF /* eap_md5.c */; }; - 8853CBFE109F4C6E00358CEF /* eap_methods.c in Sources */ = {isa = PBXBuildFile; fileRef = 8853CBEF109F4C6E00358CEF /* eap_methods.c */; }; - 8853CBFF109F4C6E00358CEF /* eap_mschapv2.c in Sources */ = {isa = PBXBuildFile; fileRef = 8853CBF0109F4C6E00358CEF /* eap_mschapv2.c */; }; - 8853CC00109F4C6E00358CEF /* eap_otp.c in Sources */ = {isa = PBXBuildFile; fileRef = 8853CBF1109F4C6E00358CEF /* eap_otp.c */; }; - 8853CC01109F4C6E00358CEF /* eap_peap.c in Sources */ = {isa = PBXBuildFile; fileRef = 8853CBF2109F4C6E00358CEF /* eap_peap.c */; }; - 8853CC02109F4C6E00358CEF /* eap_tls_common.c in Sources */ = {isa = PBXBuildFile; fileRef = 8853CBF3109F4C6E00358CEF /* eap_tls_common.c */; }; - 8853CC03109F4C6E00358CEF /* eap_tls.c in Sources */ = {isa = PBXBuildFile; fileRef = 8853CBF4109F4C6E00358CEF /* eap_tls.c */; }; - 8853CC04109F4C6E00358CEF /* eap_tnc.c in Sources */ = {isa = PBXBuildFile; fileRef = 8853CBF5109F4C6E00358CEF /* eap_tnc.c */; }; - 8853CC05109F4C6E00358CEF /* eap_ttls.c in Sources */ = {isa = PBXBuildFile; fileRef = 8853CBF6109F4C6E00358CEF /* eap_ttls.c */; }; - 8853CC06109F4C6E00358CEF /* eap_wsc.c in Sources */ = {isa = PBXBuildFile; fileRef = 8853CBF7109F4C6E00358CEF /* eap_wsc.c */; }; - 8853CC07109F4C6E00358CEF /* eap.c in Sources */ = {isa = PBXBuildFile; fileRef = 8853CBF8109F4C6E00358CEF /* eap.c */; }; - 8853CC08109F4C6E00358CEF /* mschapv2.c in Sources */ = {isa = PBXBuildFile; fileRef = 8853CBF9109F4C6E00358CEF /* mschapv2.c */; }; - 8853CC09109F4C6E00358CEF /* tncc.c in Sources */ = {isa = PBXBuildFile; fileRef = 8853CBFA109F4C6E00358CEF /* tncc.c */; }; - 8853CC0E109F4CA100358CEF /* ctrl_iface_unix.c in Sources */ = {isa = PBXBuildFile; fileRef = 8853CC0C109F4CA100358CEF /* ctrl_iface_unix.c */; }; - 8853CC0F109F4CA100358CEF /* ctrl_iface.c in Sources */ = {isa = PBXBuildFile; fileRef = 8853CC0D109F4CA100358CEF /* ctrl_iface.c */; }; - 8853CC11109F4CC800358CEF /* eapol_supp_sm.c in Sources */ = {isa = PBXBuildFile; fileRef = 8853CC10109F4CC800358CEF /* eapol_supp_sm.c */; }; - 8853CC18109F4D0800358CEF /* chap.c in Sources */ = {isa = PBXBuildFile; fileRef = 8853CC14109F4D0800358CEF /* chap.c */; }; - 8853CC19109F4D0800358CEF /* eap_common.c in Sources */ = {isa = PBXBuildFile; fileRef = 8853CC15109F4D0800358CEF /* eap_common.c */; }; - 8853CC1A109F4D0800358CEF /* eap_peap_common.c in Sources */ = {isa = PBXBuildFile; fileRef = 8853CC16109F4D0800358CEF /* eap_peap_common.c */; }; - 8853CC1B109F4D0800358CEF /* eap_wsc_common.c in Sources */ = {isa = PBXBuildFile; fileRef = 8853CC17109F4D0800358CEF /* eap_wsc_common.c */; }; - 8853CC26109F4D3500358CEF /* wps_attr_build.c in Sources */ = {isa = PBXBuildFile; fileRef = 8853CC1E109F4D3500358CEF /* wps_attr_build.c */; }; - 8853CC27109F4D3500358CEF /* wps_attr_parse.c in Sources */ = {isa = PBXBuildFile; fileRef = 8853CC1F109F4D3500358CEF /* wps_attr_parse.c */; }; - 8853CC28109F4D3500358CEF /* wps_attr_process.c in Sources */ = {isa = PBXBuildFile; fileRef = 8853CC20109F4D3500358CEF /* wps_attr_process.c */; }; - 8853CC29109F4D3500358CEF /* wps_common.c in Sources */ = {isa = PBXBuildFile; fileRef = 8853CC21109F4D3500358CEF /* wps_common.c */; }; - 8853CC2A109F4D3500358CEF /* wps_dev_attr.c in Sources */ = {isa = PBXBuildFile; fileRef = 8853CC22109F4D3500358CEF /* wps_dev_attr.c */; }; - 8853CC2B109F4D3500358CEF /* wps_enrollee.c in Sources */ = {isa = PBXBuildFile; fileRef = 8853CC23109F4D3500358CEF /* wps_enrollee.c */; }; - 8853CC2C109F4D3500358CEF /* wps_registrar.c in Sources */ = {isa = PBXBuildFile; fileRef = 8853CC24109F4D3500358CEF /* wps_registrar.c */; }; - 8853CC2D109F4D3500358CEF /* wps.c in Sources */ = {isa = PBXBuildFile; fileRef = 8853CC25109F4D3500358CEF /* wps.c */; }; - 8853CC34109F4DE200358CEF /* ms_funcs.c in Sources */ = {isa = PBXBuildFile; fileRef = 8853CC32109F4DE200358CEF /* ms_funcs.c */; }; - 8853CC35109F4DE200358CEF /* tls_openssl.c in Sources */ = {isa = PBXBuildFile; fileRef = 8853CC33109F4DE200358CEF /* tls_openssl.c */; }; - 8853CC3C109F4E1D00358CEF /* libssl.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 8853CC3B109F4E1D00358CEF /* libssl.dylib */; }; - 8853CC40109F4E3A00358CEF /* wps_supplicant.c in Sources */ = {isa = PBXBuildFile; fileRef = 8853CC3F109F4E3A00358CEF /* wps_supplicant.c */; }; - 8853CC44109F4E6200358CEF /* uuid.c in Sources */ = {isa = PBXBuildFile; fileRef = 8853CC43109F4E6200358CEF /* uuid.c */; }; - 8853CC48109F4E8700358CEF /* ieee802_11_common.c in Sources */ = {isa = PBXBuildFile; fileRef = 8853CC47109F4E8700358CEF /* ieee802_11_common.c */; }; - 8853CC4E109F4ED500358CEF /* sha256.c in Sources */ = {isa = PBXBuildFile; fileRef = 8853CC4C109F4ED500358CEF /* sha256.c */; }; - 8853CC53109F4F3500358CEF /* aes-cbc.c in Sources */ = {isa = PBXBuildFile; fileRef = 8853CC51109F4F3500358CEF /* aes-cbc.c */; }; - 8853CC54109F4F3500358CEF /* sha1-tlsprf.c in Sources */ = {isa = PBXBuildFile; fileRef = 8853CC52109F4F3500358CEF /* sha1-tlsprf.c */; }; - 88950831109F2FAB004FB35D /* blacklist.c in Sources */ = {isa = PBXBuildFile; fileRef = 88950828109F2FAB004FB35D /* blacklist.c */; }; - 88950832109F2FAB004FB35D /* config_file.c in Sources */ = {isa = PBXBuildFile; fileRef = 88950829109F2FAB004FB35D /* config_file.c */; }; - 88950833109F2FAB004FB35D /* config.c in Sources */ = {isa = PBXBuildFile; fileRef = 8895082A109F2FAB004FB35D /* config.c */; }; - 88950834109F2FAB004FB35D /* events.c in Sources */ = {isa = PBXBuildFile; fileRef = 8895082B109F2FAB004FB35D /* events.c */; }; - 88950835109F2FAB004FB35D /* main.c in Sources */ = {isa = PBXBuildFile; fileRef = 8895082C109F2FAB004FB35D /* main.c */; }; - 88950836109F2FAB004FB35D /* notify.c in Sources */ = {isa = PBXBuildFile; fileRef = 8895082D109F2FAB004FB35D /* notify.c */; }; - 88950837109F2FAB004FB35D /* scan.c in Sources */ = {isa = PBXBuildFile; fileRef = 8895082E109F2FAB004FB35D /* scan.c */; }; - 88950838109F2FAB004FB35D /* wpa_supplicant.c in Sources */ = {isa = PBXBuildFile; fileRef = 8895082F109F2FAB004FB35D /* wpa_supplicant.c */; }; - 88950839109F2FAB004FB35D /* wpas_glue.c in Sources */ = {isa = PBXBuildFile; fileRef = 88950830109F2FAB004FB35D /* wpas_glue.c */; }; - 88950840109F301A004FB35D /* base64.c in Sources */ = {isa = PBXBuildFile; fileRef = 8895083A109F301A004FB35D /* base64.c */; }; - 88950841109F301A004FB35D /* common.c in Sources */ = {isa = PBXBuildFile; fileRef = 8895083B109F301A004FB35D /* common.c */; }; - 88950842109F301A004FB35D /* eloop.c in Sources */ = {isa = PBXBuildFile; fileRef = 8895083C109F301A004FB35D /* eloop.c */; }; - 88950843109F301A004FB35D /* os_unix.c in Sources */ = {isa = PBXBuildFile; fileRef = 8895083D109F301A004FB35D /* os_unix.c */; }; - 88950844109F301A004FB35D /* wpa_debug.c in Sources */ = {isa = PBXBuildFile; fileRef = 8895083E109F301A004FB35D /* wpa_debug.c */; }; - 88950845109F301A004FB35D /* wpabuf.c in Sources */ = {isa = PBXBuildFile; fileRef = 8895083F109F301A004FB35D /* wpabuf.c */; }; - 88950864109F32D1004FB35D /* peerkey.c in Sources */ = {isa = PBXBuildFile; fileRef = 8895085F109F32D1004FB35D /* peerkey.c */; }; - 88950865109F32D1004FB35D /* pmksa_cache.c in Sources */ = {isa = PBXBuildFile; fileRef = 88950860109F32D1004FB35D /* pmksa_cache.c */; }; - 88950866109F32D1004FB35D /* preauth.c in Sources */ = {isa = PBXBuildFile; fileRef = 88950861109F32D1004FB35D /* preauth.c */; }; - 88950867109F32D1004FB35D /* wpa_ie.c in Sources */ = {isa = PBXBuildFile; fileRef = 88950862109F32D1004FB35D /* wpa_ie.c */; }; - 88950868109F32D1004FB35D /* wpa.c in Sources */ = {isa = PBXBuildFile; fileRef = 88950863109F32D1004FB35D /* wpa.c */; }; - 8895086C109F3316004FB35D /* l2_packet_freebsd.c in Sources */ = {isa = PBXBuildFile; fileRef = 8895086B109F3316004FB35D /* l2_packet_freebsd.c */; }; - 88950871109F3367004FB35D /* aes-unwrap.c in Sources */ = {isa = PBXBuildFile; fileRef = 8895086D109F3367004FB35D /* aes-unwrap.c */; }; - 88950872109F3367004FB35D /* crypto_openssl.c in Sources */ = {isa = PBXBuildFile; fileRef = 8895086E109F3367004FB35D /* crypto_openssl.c */; }; - 88950873109F3367004FB35D /* sha1-pbkdf2.c in Sources */ = {isa = PBXBuildFile; fileRef = 8895086F109F3367004FB35D /* sha1-pbkdf2.c */; }; - 88950874109F3367004FB35D /* sha1.c in Sources */ = {isa = PBXBuildFile; fileRef = 88950870109F3367004FB35D /* sha1.c */; }; - 88950885109F3538004FB35D /* driver_osx.m in Sources */ = {isa = PBXBuildFile; fileRef = 88950883109F3538004FB35D /* driver_osx.m */; }; - 88950886109F3538004FB35D /* drivers.c in Sources */ = {isa = PBXBuildFile; fileRef = 88950884109F3538004FB35D /* drivers.c */; }; -/* End PBXBuildFile section */ - -/* Begin PBXCopyFilesBuildPhase section */ - 8DD76FAF0486AB0100D96B5E /* CopyFiles */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 8; - dstPath = /usr/share/man/man1/; - dstSubfolderSpec = 0; - files = ( - ); - runOnlyForDeploymentPostprocessing = 1; - }; -/* End PBXCopyFilesBuildPhase section */ - -/* Begin PBXFileReference section */ - 881EED0E10DC14EF009E449F /* eap_register.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = eap_register.c; path = ../eap_register.c; sourceTree = SOURCE_ROOT; }; - 8853CB16109F385C00358CEF /* libpcap.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libpcap.dylib; path = usr/lib/libpcap.dylib; sourceTree = SDKROOT; }; - 8853CB1A109F389800358CEF /* libcrypto.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libcrypto.dylib; path = usr/lib/libcrypto.dylib; sourceTree = SDKROOT; }; - 8853CB1E109F38BD00358CEF /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = System/Library/Frameworks/CoreFoundation.framework; sourceTree = SDKROOT; }; - 8853CB2D109F3A3900358CEF /* scan_helpers.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = scan_helpers.c; path = ../../src/drivers/scan_helpers.c; sourceTree = SOURCE_ROOT; }; - 8853CB31109F3A9400358CEF /* wpa_common.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = wpa_common.c; path = ../../src/common/wpa_common.c; sourceTree = SOURCE_ROOT; }; - 8853CB35109F3AC700358CEF /* md5.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = md5.c; path = ../../src/crypto/md5.c; sourceTree = SOURCE_ROOT; }; - 8853CB3B109F3B5800358CEF /* Apple80211.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Apple80211.framework; path = /System/Library/PrivateFrameworks/Apple80211.framework; sourceTree = ""; }; - 8853CBEC109F4C6E00358CEF /* eap_gtc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = eap_gtc.c; path = ../../src/eap_peer/eap_gtc.c; sourceTree = SOURCE_ROOT; }; - 8853CBED109F4C6E00358CEF /* eap_leap.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = eap_leap.c; path = ../../src/eap_peer/eap_leap.c; sourceTree = SOURCE_ROOT; }; - 8853CBEE109F4C6E00358CEF /* eap_md5.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = eap_md5.c; path = ../../src/eap_peer/eap_md5.c; sourceTree = SOURCE_ROOT; }; - 8853CBEF109F4C6E00358CEF /* eap_methods.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = eap_methods.c; path = ../../src/eap_peer/eap_methods.c; sourceTree = SOURCE_ROOT; }; - 8853CBF0109F4C6E00358CEF /* eap_mschapv2.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = eap_mschapv2.c; path = ../../src/eap_peer/eap_mschapv2.c; sourceTree = SOURCE_ROOT; }; - 8853CBF1109F4C6E00358CEF /* eap_otp.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = eap_otp.c; path = ../../src/eap_peer/eap_otp.c; sourceTree = SOURCE_ROOT; }; - 8853CBF2109F4C6E00358CEF /* eap_peap.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = eap_peap.c; path = ../../src/eap_peer/eap_peap.c; sourceTree = SOURCE_ROOT; }; - 8853CBF3109F4C6E00358CEF /* eap_tls_common.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = eap_tls_common.c; path = ../../src/eap_peer/eap_tls_common.c; sourceTree = SOURCE_ROOT; }; - 8853CBF4109F4C6E00358CEF /* eap_tls.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = eap_tls.c; path = ../../src/eap_peer/eap_tls.c; sourceTree = SOURCE_ROOT; }; - 8853CBF5109F4C6E00358CEF /* eap_tnc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = eap_tnc.c; path = ../../src/eap_peer/eap_tnc.c; sourceTree = SOURCE_ROOT; }; - 8853CBF6109F4C6E00358CEF /* eap_ttls.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = eap_ttls.c; path = ../../src/eap_peer/eap_ttls.c; sourceTree = SOURCE_ROOT; }; - 8853CBF7109F4C6E00358CEF /* eap_wsc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = eap_wsc.c; path = ../../src/eap_peer/eap_wsc.c; sourceTree = SOURCE_ROOT; }; - 8853CBF8109F4C6E00358CEF /* eap.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = eap.c; path = ../../src/eap_peer/eap.c; sourceTree = SOURCE_ROOT; }; - 8853CBF9109F4C6E00358CEF /* mschapv2.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = mschapv2.c; path = ../../src/eap_peer/mschapv2.c; sourceTree = SOURCE_ROOT; }; - 8853CBFA109F4C6E00358CEF /* tncc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = tncc.c; path = ../../src/eap_peer/tncc.c; sourceTree = SOURCE_ROOT; }; - 8853CC0C109F4CA100358CEF /* ctrl_iface_unix.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ctrl_iface_unix.c; path = ../ctrl_iface_unix.c; sourceTree = SOURCE_ROOT; }; - 8853CC0D109F4CA100358CEF /* ctrl_iface.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ctrl_iface.c; path = ../ctrl_iface.c; sourceTree = SOURCE_ROOT; }; - 8853CC10109F4CC800358CEF /* eapol_supp_sm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = eapol_supp_sm.c; path = ../../src/eapol_supp/eapol_supp_sm.c; sourceTree = SOURCE_ROOT; }; - 8853CC14109F4D0800358CEF /* chap.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = chap.c; path = ../../src/eap_common/chap.c; sourceTree = SOURCE_ROOT; }; - 8853CC15109F4D0800358CEF /* eap_common.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = eap_common.c; path = ../../src/eap_common/eap_common.c; sourceTree = SOURCE_ROOT; }; - 8853CC16109F4D0800358CEF /* eap_peap_common.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = eap_peap_common.c; path = ../../src/eap_common/eap_peap_common.c; sourceTree = SOURCE_ROOT; }; - 8853CC17109F4D0800358CEF /* eap_wsc_common.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = eap_wsc_common.c; path = ../../src/eap_common/eap_wsc_common.c; sourceTree = SOURCE_ROOT; }; - 8853CC1E109F4D3500358CEF /* wps_attr_build.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = wps_attr_build.c; path = ../../src/wps/wps_attr_build.c; sourceTree = SOURCE_ROOT; }; - 8853CC1F109F4D3500358CEF /* wps_attr_parse.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = wps_attr_parse.c; path = ../../src/wps/wps_attr_parse.c; sourceTree = SOURCE_ROOT; }; - 8853CC20109F4D3500358CEF /* wps_attr_process.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = wps_attr_process.c; path = ../../src/wps/wps_attr_process.c; sourceTree = SOURCE_ROOT; }; - 8853CC21109F4D3500358CEF /* wps_common.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = wps_common.c; path = ../../src/wps/wps_common.c; sourceTree = SOURCE_ROOT; }; - 8853CC22109F4D3500358CEF /* wps_dev_attr.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = wps_dev_attr.c; path = ../../src/wps/wps_dev_attr.c; sourceTree = SOURCE_ROOT; }; - 8853CC23109F4D3500358CEF /* wps_enrollee.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = wps_enrollee.c; path = ../../src/wps/wps_enrollee.c; sourceTree = SOURCE_ROOT; }; - 8853CC24109F4D3500358CEF /* wps_registrar.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = wps_registrar.c; path = ../../src/wps/wps_registrar.c; sourceTree = SOURCE_ROOT; }; - 8853CC25109F4D3500358CEF /* wps.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = wps.c; path = ../../src/wps/wps.c; sourceTree = SOURCE_ROOT; }; - 8853CC32109F4DE200358CEF /* ms_funcs.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ms_funcs.c; path = ../../src/crypto/ms_funcs.c; sourceTree = SOURCE_ROOT; }; - 8853CC33109F4DE200358CEF /* tls_openssl.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = tls_openssl.c; path = ../../src/crypto/tls_openssl.c; sourceTree = SOURCE_ROOT; }; - 8853CC3B109F4E1D00358CEF /* libssl.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libssl.dylib; path = usr/lib/libssl.dylib; sourceTree = SDKROOT; }; - 8853CC3F109F4E3A00358CEF /* wps_supplicant.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = wps_supplicant.c; path = ../wps_supplicant.c; sourceTree = SOURCE_ROOT; }; - 8853CC43109F4E6200358CEF /* uuid.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = uuid.c; path = ../../src/utils/uuid.c; sourceTree = SOURCE_ROOT; }; - 8853CC47109F4E8700358CEF /* ieee802_11_common.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ieee802_11_common.c; path = ../../src/common/ieee802_11_common.c; sourceTree = SOURCE_ROOT; }; - 8853CC4C109F4ED500358CEF /* sha256.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = sha256.c; path = ../../src/crypto/sha256.c; sourceTree = SOURCE_ROOT; }; - 8853CC51109F4F3500358CEF /* aes-cbc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = "aes-cbc.c"; path = "../../src/crypto/aes-cbc.c"; sourceTree = SOURCE_ROOT; }; - 8853CC52109F4F3500358CEF /* sha1-tlsprf.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = "sha1-tlsprf.c"; path = "../../src/crypto/sha1-tlsprf.c"; sourceTree = SOURCE_ROOT; }; - 88950828109F2FAB004FB35D /* blacklist.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = blacklist.c; path = ../../wpa_supplicant/blacklist.c; sourceTree = SOURCE_ROOT; }; - 88950829109F2FAB004FB35D /* config_file.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = config_file.c; path = ../../wpa_supplicant/config_file.c; sourceTree = SOURCE_ROOT; }; - 8895082A109F2FAB004FB35D /* config.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = config.c; path = ../../wpa_supplicant/config.c; sourceTree = SOURCE_ROOT; }; - 8895082B109F2FAB004FB35D /* events.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = events.c; path = ../../wpa_supplicant/events.c; sourceTree = SOURCE_ROOT; }; - 8895082C109F2FAB004FB35D /* main.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = main.c; path = ../../wpa_supplicant/main.c; sourceTree = SOURCE_ROOT; }; - 8895082D109F2FAB004FB35D /* notify.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = notify.c; path = ../../wpa_supplicant/notify.c; sourceTree = SOURCE_ROOT; }; - 8895082E109F2FAB004FB35D /* scan.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = scan.c; path = ../../wpa_supplicant/scan.c; sourceTree = SOURCE_ROOT; }; - 8895082F109F2FAB004FB35D /* wpa_supplicant.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = wpa_supplicant.c; path = ../../wpa_supplicant/wpa_supplicant.c; sourceTree = SOURCE_ROOT; }; - 88950830109F2FAB004FB35D /* wpas_glue.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = wpas_glue.c; path = ../../wpa_supplicant/wpas_glue.c; sourceTree = SOURCE_ROOT; }; - 8895083A109F301A004FB35D /* base64.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = base64.c; path = ../../src/utils/base64.c; sourceTree = SOURCE_ROOT; }; - 8895083B109F301A004FB35D /* common.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = common.c; path = ../../src/utils/common.c; sourceTree = SOURCE_ROOT; }; - 8895083C109F301A004FB35D /* eloop.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = eloop.c; path = ../../src/utils/eloop.c; sourceTree = SOURCE_ROOT; }; - 8895083D109F301A004FB35D /* os_unix.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = os_unix.c; path = ../../src/utils/os_unix.c; sourceTree = SOURCE_ROOT; }; - 8895083E109F301A004FB35D /* wpa_debug.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = wpa_debug.c; path = ../../src/utils/wpa_debug.c; sourceTree = SOURCE_ROOT; }; - 8895083F109F301A004FB35D /* wpabuf.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = wpabuf.c; path = ../../src/utils/wpabuf.c; sourceTree = SOURCE_ROOT; }; - 8895085F109F32D1004FB35D /* peerkey.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = peerkey.c; path = ../../src/rsn_supp/peerkey.c; sourceTree = SOURCE_ROOT; }; - 88950860109F32D1004FB35D /* pmksa_cache.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = pmksa_cache.c; path = ../../src/rsn_supp/pmksa_cache.c; sourceTree = SOURCE_ROOT; }; - 88950861109F32D1004FB35D /* preauth.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = preauth.c; path = ../../src/rsn_supp/preauth.c; sourceTree = SOURCE_ROOT; }; - 88950862109F32D1004FB35D /* wpa_ie.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = wpa_ie.c; path = ../../src/rsn_supp/wpa_ie.c; sourceTree = SOURCE_ROOT; }; - 88950863109F32D1004FB35D /* wpa.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = wpa.c; path = ../../src/rsn_supp/wpa.c; sourceTree = SOURCE_ROOT; }; - 8895086B109F3316004FB35D /* l2_packet_freebsd.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = l2_packet_freebsd.c; path = ../../src/l2_packet/l2_packet_freebsd.c; sourceTree = SOURCE_ROOT; }; - 8895086D109F3367004FB35D /* aes-unwrap.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = "aes-unwrap.c"; path = "../../src/crypto/aes-unwrap.c"; sourceTree = SOURCE_ROOT; }; - 8895086E109F3367004FB35D /* crypto_openssl.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = crypto_openssl.c; path = ../../src/crypto/crypto_openssl.c; sourceTree = SOURCE_ROOT; }; - 8895086F109F3367004FB35D /* sha1-pbkdf2.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = "sha1-pbkdf2.c"; path = "../../src/crypto/sha1-pbkdf2.c"; sourceTree = SOURCE_ROOT; }; - 88950870109F3367004FB35D /* sha1.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = sha1.c; path = ../../src/crypto/sha1.c; sourceTree = SOURCE_ROOT; }; - 88950883109F3538004FB35D /* driver_osx.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = driver_osx.m; path = ../../src/drivers/driver_osx.m; sourceTree = SOURCE_ROOT; }; - 88950884109F3538004FB35D /* drivers.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = drivers.c; path = ../../src/drivers/drivers.c; sourceTree = SOURCE_ROOT; }; - 8DD76FB20486AB0100D96B5E /* wpa_supplicant */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = wpa_supplicant; sourceTree = BUILT_PRODUCTS_DIR; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - 8DD76FAD0486AB0100D96B5E /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 8853CB17109F385C00358CEF /* libpcap.dylib in Frameworks */, - 8853CB1B109F389800358CEF /* libcrypto.dylib in Frameworks */, - 8853CB1F109F38BD00358CEF /* CoreFoundation.framework in Frameworks */, - 8853CB3C109F3B5800358CEF /* Apple80211.framework in Frameworks */, - 8853CC3C109F4E1D00358CEF /* libssl.dylib in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 08FB7794FE84155DC02AAC07 /* wpa_supplicant */ = { - isa = PBXGroup; - children = ( - 08FB7795FE84155DC02AAC07 /* Source */, - C6A0FF2B0290797F04C91782 /* Documentation */, - 1AB674ADFE9D54B511CA2CBB /* Products */, - 8853CB16109F385C00358CEF /* libpcap.dylib */, - 8853CB1A109F389800358CEF /* libcrypto.dylib */, - 8853CB1E109F38BD00358CEF /* CoreFoundation.framework */, - 8853CB3B109F3B5800358CEF /* Apple80211.framework */, - 8853CC3B109F4E1D00358CEF /* libssl.dylib */, - ); - name = wpa_supplicant; - sourceTree = ""; - }; - 08FB7795FE84155DC02AAC07 /* Source */ = { - isa = PBXGroup; - children = ( - 881EED0E10DC14EF009E449F /* eap_register.c */, - 8853CC51109F4F3500358CEF /* aes-cbc.c */, - 8853CC52109F4F3500358CEF /* sha1-tlsprf.c */, - 8853CC4C109F4ED500358CEF /* sha256.c */, - 8853CC47109F4E8700358CEF /* ieee802_11_common.c */, - 8853CC43109F4E6200358CEF /* uuid.c */, - 8853CC3F109F4E3A00358CEF /* wps_supplicant.c */, - 8853CC32109F4DE200358CEF /* ms_funcs.c */, - 8853CC33109F4DE200358CEF /* tls_openssl.c */, - 8853CC1E109F4D3500358CEF /* wps_attr_build.c */, - 8853CC1F109F4D3500358CEF /* wps_attr_parse.c */, - 8853CC20109F4D3500358CEF /* wps_attr_process.c */, - 8853CC21109F4D3500358CEF /* wps_common.c */, - 8853CC22109F4D3500358CEF /* wps_dev_attr.c */, - 8853CC23109F4D3500358CEF /* wps_enrollee.c */, - 8853CC24109F4D3500358CEF /* wps_registrar.c */, - 8853CC25109F4D3500358CEF /* wps.c */, - 8853CC14109F4D0800358CEF /* chap.c */, - 8853CC15109F4D0800358CEF /* eap_common.c */, - 8853CC16109F4D0800358CEF /* eap_peap_common.c */, - 8853CC17109F4D0800358CEF /* eap_wsc_common.c */, - 8853CC10109F4CC800358CEF /* eapol_supp_sm.c */, - 8853CC0C109F4CA100358CEF /* ctrl_iface_unix.c */, - 8853CC0D109F4CA100358CEF /* ctrl_iface.c */, - 8853CBEC109F4C6E00358CEF /* eap_gtc.c */, - 8853CBED109F4C6E00358CEF /* eap_leap.c */, - 8853CBEE109F4C6E00358CEF /* eap_md5.c */, - 8853CBEF109F4C6E00358CEF /* eap_methods.c */, - 8853CBF0109F4C6E00358CEF /* eap_mschapv2.c */, - 8853CBF1109F4C6E00358CEF /* eap_otp.c */, - 8853CBF2109F4C6E00358CEF /* eap_peap.c */, - 8853CBF3109F4C6E00358CEF /* eap_tls_common.c */, - 8853CBF4109F4C6E00358CEF /* eap_tls.c */, - 8853CBF5109F4C6E00358CEF /* eap_tnc.c */, - 8853CBF6109F4C6E00358CEF /* eap_ttls.c */, - 8853CBF7109F4C6E00358CEF /* eap_wsc.c */, - 8853CBF8109F4C6E00358CEF /* eap.c */, - 8853CBF9109F4C6E00358CEF /* mschapv2.c */, - 8853CBFA109F4C6E00358CEF /* tncc.c */, - 8853CB35109F3AC700358CEF /* md5.c */, - 8853CB31109F3A9400358CEF /* wpa_common.c */, - 8853CB2D109F3A3900358CEF /* scan_helpers.c */, - 88950883109F3538004FB35D /* driver_osx.m */, - 88950884109F3538004FB35D /* drivers.c */, - 8895086D109F3367004FB35D /* aes-unwrap.c */, - 8895086E109F3367004FB35D /* crypto_openssl.c */, - 8895086F109F3367004FB35D /* sha1-pbkdf2.c */, - 88950870109F3367004FB35D /* sha1.c */, - 8895086B109F3316004FB35D /* l2_packet_freebsd.c */, - 8895085F109F32D1004FB35D /* peerkey.c */, - 88950860109F32D1004FB35D /* pmksa_cache.c */, - 88950861109F32D1004FB35D /* preauth.c */, - 88950862109F32D1004FB35D /* wpa_ie.c */, - 88950863109F32D1004FB35D /* wpa.c */, - 8895083A109F301A004FB35D /* base64.c */, - 8895083B109F301A004FB35D /* common.c */, - 8895083C109F301A004FB35D /* eloop.c */, - 8895083D109F301A004FB35D /* os_unix.c */, - 8895083E109F301A004FB35D /* wpa_debug.c */, - 8895083F109F301A004FB35D /* wpabuf.c */, - 88950828109F2FAB004FB35D /* blacklist.c */, - 88950829109F2FAB004FB35D /* config_file.c */, - 8895082A109F2FAB004FB35D /* config.c */, - 8895082B109F2FAB004FB35D /* events.c */, - 8895082C109F2FAB004FB35D /* main.c */, - 8895082D109F2FAB004FB35D /* notify.c */, - 8895082E109F2FAB004FB35D /* scan.c */, - 8895082F109F2FAB004FB35D /* wpa_supplicant.c */, - 88950830109F2FAB004FB35D /* wpas_glue.c */, - ); - name = Source; - sourceTree = ""; - }; - 1AB674ADFE9D54B511CA2CBB /* Products */ = { - isa = PBXGroup; - children = ( - 8DD76FB20486AB0100D96B5E /* wpa_supplicant */, - ); - name = Products; - sourceTree = ""; - }; - C6A0FF2B0290797F04C91782 /* Documentation */ = { - isa = PBXGroup; - children = ( - ); - name = Documentation; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXNativeTarget section */ - 8DD76FA90486AB0100D96B5E /* wpa_supplicant */ = { - isa = PBXNativeTarget; - buildConfigurationList = 1DEB928508733DD80010E9CD /* Build configuration list for PBXNativeTarget "wpa_supplicant" */; - buildPhases = ( - 8DD76FAB0486AB0100D96B5E /* Sources */, - 8DD76FAD0486AB0100D96B5E /* Frameworks */, - 8DD76FAF0486AB0100D96B5E /* CopyFiles */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = wpa_supplicant; - productInstallPath = "$(HOME)/bin"; - productName = wpa_supplicant; - productReference = 8DD76FB20486AB0100D96B5E /* wpa_supplicant */; - productType = "com.apple.product-type.tool"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - 08FB7793FE84155DC02AAC07 /* Project object */ = { - isa = PBXProject; - buildConfigurationList = 1DEB928908733DD80010E9CD /* Build configuration list for PBXProject "wpa_supplicant" */; - compatibilityVersion = "Xcode 3.1"; - hasScannedForEncodings = 1; - mainGroup = 08FB7794FE84155DC02AAC07 /* wpa_supplicant */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - 8DD76FA90486AB0100D96B5E /* wpa_supplicant */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXSourcesBuildPhase section */ - 8DD76FAB0486AB0100D96B5E /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 88950831109F2FAB004FB35D /* blacklist.c in Sources */, - 88950832109F2FAB004FB35D /* config_file.c in Sources */, - 88950833109F2FAB004FB35D /* config.c in Sources */, - 88950834109F2FAB004FB35D /* events.c in Sources */, - 88950835109F2FAB004FB35D /* main.c in Sources */, - 88950836109F2FAB004FB35D /* notify.c in Sources */, - 88950837109F2FAB004FB35D /* scan.c in Sources */, - 88950838109F2FAB004FB35D /* wpa_supplicant.c in Sources */, - 88950839109F2FAB004FB35D /* wpas_glue.c in Sources */, - 88950840109F301A004FB35D /* base64.c in Sources */, - 88950841109F301A004FB35D /* common.c in Sources */, - 88950842109F301A004FB35D /* eloop.c in Sources */, - 88950843109F301A004FB35D /* os_unix.c in Sources */, - 88950844109F301A004FB35D /* wpa_debug.c in Sources */, - 88950845109F301A004FB35D /* wpabuf.c in Sources */, - 88950864109F32D1004FB35D /* peerkey.c in Sources */, - 88950865109F32D1004FB35D /* pmksa_cache.c in Sources */, - 88950866109F32D1004FB35D /* preauth.c in Sources */, - 88950867109F32D1004FB35D /* wpa_ie.c in Sources */, - 88950868109F32D1004FB35D /* wpa.c in Sources */, - 8895086C109F3316004FB35D /* l2_packet_freebsd.c in Sources */, - 88950871109F3367004FB35D /* aes-unwrap.c in Sources */, - 88950872109F3367004FB35D /* crypto_openssl.c in Sources */, - 88950873109F3367004FB35D /* sha1-pbkdf2.c in Sources */, - 88950874109F3367004FB35D /* sha1.c in Sources */, - 88950885109F3538004FB35D /* driver_osx.m in Sources */, - 88950886109F3538004FB35D /* drivers.c in Sources */, - 8853CB2E109F3A3900358CEF /* scan_helpers.c in Sources */, - 8853CB32109F3A9400358CEF /* wpa_common.c in Sources */, - 8853CB36109F3AC700358CEF /* md5.c in Sources */, - 8853CBFB109F4C6E00358CEF /* eap_gtc.c in Sources */, - 8853CBFC109F4C6E00358CEF /* eap_leap.c in Sources */, - 8853CBFD109F4C6E00358CEF /* eap_md5.c in Sources */, - 8853CBFE109F4C6E00358CEF /* eap_methods.c in Sources */, - 8853CBFF109F4C6E00358CEF /* eap_mschapv2.c in Sources */, - 8853CC00109F4C6E00358CEF /* eap_otp.c in Sources */, - 8853CC01109F4C6E00358CEF /* eap_peap.c in Sources */, - 8853CC02109F4C6E00358CEF /* eap_tls_common.c in Sources */, - 8853CC03109F4C6E00358CEF /* eap_tls.c in Sources */, - 8853CC04109F4C6E00358CEF /* eap_tnc.c in Sources */, - 8853CC05109F4C6E00358CEF /* eap_ttls.c in Sources */, - 8853CC06109F4C6E00358CEF /* eap_wsc.c in Sources */, - 8853CC07109F4C6E00358CEF /* eap.c in Sources */, - 8853CC08109F4C6E00358CEF /* mschapv2.c in Sources */, - 8853CC09109F4C6E00358CEF /* tncc.c in Sources */, - 8853CC0E109F4CA100358CEF /* ctrl_iface_unix.c in Sources */, - 8853CC0F109F4CA100358CEF /* ctrl_iface.c in Sources */, - 8853CC11109F4CC800358CEF /* eapol_supp_sm.c in Sources */, - 8853CC18109F4D0800358CEF /* chap.c in Sources */, - 8853CC19109F4D0800358CEF /* eap_common.c in Sources */, - 8853CC1A109F4D0800358CEF /* eap_peap_common.c in Sources */, - 8853CC1B109F4D0800358CEF /* eap_wsc_common.c in Sources */, - 8853CC26109F4D3500358CEF /* wps_attr_build.c in Sources */, - 8853CC27109F4D3500358CEF /* wps_attr_parse.c in Sources */, - 8853CC28109F4D3500358CEF /* wps_attr_process.c in Sources */, - 8853CC29109F4D3500358CEF /* wps_common.c in Sources */, - 8853CC2A109F4D3500358CEF /* wps_dev_attr.c in Sources */, - 8853CC2B109F4D3500358CEF /* wps_enrollee.c in Sources */, - 8853CC2C109F4D3500358CEF /* wps_registrar.c in Sources */, - 8853CC2D109F4D3500358CEF /* wps.c in Sources */, - 8853CC34109F4DE200358CEF /* ms_funcs.c in Sources */, - 8853CC35109F4DE200358CEF /* tls_openssl.c in Sources */, - 8853CC40109F4E3A00358CEF /* wps_supplicant.c in Sources */, - 8853CC44109F4E6200358CEF /* uuid.c in Sources */, - 8853CC48109F4E8700358CEF /* ieee802_11_common.c in Sources */, - 8853CC4E109F4ED500358CEF /* sha256.c in Sources */, - 8853CC53109F4F3500358CEF /* aes-cbc.c in Sources */, - 8853CC54109F4F3500358CEF /* sha1-tlsprf.c in Sources */, - 881EED0F10DC14EF009E449F /* eap_register.c in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin XCBuildConfiguration section */ - 1DEB928608733DD80010E9CD /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - COPY_PHASE_STRIP = NO; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "\"$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks\"", - ); - GCC_DYNAMIC_NO_PIC = NO; - GCC_ENABLE_FIX_AND_CONTINUE = YES; - GCC_MODEL_TUNING = G5; - GCC_OPTIMIZATION_LEVEL = 0; - INSTALL_PATH = /usr/local/bin; - PRODUCT_NAME = wpa_supplicant; - }; - name = Debug; - }; - 1DEB928708733DD80010E9CD /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "\"$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks\"", - ); - GCC_MODEL_TUNING = G5; - INSTALL_PATH = /usr/local/bin; - PRODUCT_NAME = wpa_supplicant; - }; - name = Release; - }; - 1DEB928A08733DD80010E9CD /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ARCHS = "$(ARCHS_STANDARD_32_64_BIT)"; - FRAMEWORK_SEARCH_PATHS = /System/Library/PrivateFrameworks; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_WARN_ABOUT_RETURN_TYPE = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - HEADER_SEARCH_PATHS = ( - ../../src, - ../../src/utils, - ); - ONLY_ACTIVE_ARCH = YES; - OTHER_CFLAGS = "-DCONFIG_XCODE_DEFAULTS"; - PREBINDING = NO; - PRELINK_LIBS = ""; - RUN_CLANG_STATIC_ANALYZER = YES; - SDKROOT = macosx10.6; - }; - name = Debug; - }; - 1DEB928B08733DD80010E9CD /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ARCHS = "$(ARCHS_STANDARD_32_64_BIT)"; - FRAMEWORK_SEARCH_PATHS = /System/Library/PrivateFrameworks; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_WARN_ABOUT_RETURN_TYPE = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - HEADER_SEARCH_PATHS = ( - ../../src, - ../../src/utils, - ); - OTHER_CFLAGS = "-DCONFIG_XCODE_DEFAULTS"; - PREBINDING = NO; - SDKROOT = macosx10.6; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 1DEB928508733DD80010E9CD /* Build configuration list for PBXNativeTarget "wpa_supplicant" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 1DEB928608733DD80010E9CD /* Debug */, - 1DEB928708733DD80010E9CD /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 1DEB928908733DD80010E9CD /* Build configuration list for PBXProject "wpa_supplicant" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 1DEB928A08733DD80010E9CD /* Debug */, - 1DEB928B08733DD80010E9CD /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = 08FB7793FE84155DC02AAC07 /* Project object */; -}