aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSam Leffler <sam@FreeBSD.org>2005-06-05 20:52:14 +0000
committerSam Leffler <sam@FreeBSD.org>2005-06-05 20:52:14 +0000
commitf1fb6907abb279cfabe81d79e2ffac54c4e7aa98 (patch)
tree98be326632e2ea3857ee0d9f831c91ea0823bb0d
downloadsrc-f1fb6907abb279cfabe81d79e2ffac54c4e7aa98.tar.gz
src-f1fb6907abb279cfabe81d79e2ffac54c4e7aa98.zip
Stripped down import of wpa_supplicant v0.3.8vendor/wpa_supplicant/0.3.8
Notes
Notes: svn path=/vendor/wpa_supplicant/dist/; revision=147013 svn path=/vendor/wpa_supplicant/0.3.8/; revision=147015; tag=vendor/wpa_supplicant/0.3.8
-rw-r--r--contrib/wpa_supplicant/COPYING340
-rw-r--r--contrib/wpa_supplicant/ChangeLog395
-rw-r--r--contrib/wpa_supplicant/Makefile385
-rw-r--r--contrib/wpa_supplicant/README860
-rw-r--r--contrib/wpa_supplicant/aes.c1132
-rw-r--r--contrib/wpa_supplicant/aes_wrap.c642
-rw-r--r--contrib/wpa_supplicant/aes_wrap.h21
-rw-r--r--contrib/wpa_supplicant/common.c335
-rw-r--r--contrib/wpa_supplicant/common.h222
-rw-r--r--contrib/wpa_supplicant/config.c1051
-rw-r--r--contrib/wpa_supplicant/config.h33
-rw-r--r--contrib/wpa_supplicant/config_ssid.h109
-rw-r--r--contrib/wpa_supplicant/crypto.c71
-rw-r--r--contrib/wpa_supplicant/crypto.h8
-rw-r--r--contrib/wpa_supplicant/ctrl_iface.c728
-rw-r--r--contrib/wpa_supplicant/ctrl_iface.h31
-rw-r--r--contrib/wpa_supplicant/defconfig154
-rw-r--r--contrib/wpa_supplicant/defs.h14
-rw-r--r--contrib/wpa_supplicant/developer.txt458
-rw-r--r--contrib/wpa_supplicant/doc/wpa_supplicant.fig221
-rw-r--r--contrib/wpa_supplicant/driver.h436
-rw-r--r--contrib/wpa_supplicant/drivers.c96
-rw-r--r--contrib/wpa_supplicant/eap.c1243
-rw-r--r--contrib/wpa_supplicant/eap.h70
-rw-r--r--contrib/wpa_supplicant/eap_aka.c915
-rw-r--r--contrib/wpa_supplicant/eap_defs.h41
-rw-r--r--contrib/wpa_supplicant/eap_fast.c1906
-rw-r--r--contrib/wpa_supplicant/eap_gtc.c164
-rw-r--r--contrib/wpa_supplicant/eap_i.h106
-rw-r--r--contrib/wpa_supplicant/eap_leap.c379
-rw-r--r--contrib/wpa_supplicant/eap_md5.c112
-rw-r--r--contrib/wpa_supplicant/eap_mschapv2.c564
-rw-r--r--contrib/wpa_supplicant/eap_otp.c113
-rw-r--r--contrib/wpa_supplicant/eap_peap.c820
-rw-r--r--contrib/wpa_supplicant/eap_psk.c563
-rw-r--r--contrib/wpa_supplicant/eap_sim.c1004
-rw-r--r--contrib/wpa_supplicant/eap_sim_common.c788
-rw-r--r--contrib/wpa_supplicant/eap_sim_common.h101
-rw-r--r--contrib/wpa_supplicant/eap_testing.txt349
-rw-r--r--contrib/wpa_supplicant/eap_tls.c245
-rw-r--r--contrib/wpa_supplicant/eap_tls_common.c338
-rw-r--r--contrib/wpa_supplicant/eap_tls_common.h51
-rw-r--r--contrib/wpa_supplicant/eap_tlv.c176
-rw-r--r--contrib/wpa_supplicant/eap_tlv.h73
-rw-r--r--contrib/wpa_supplicant/eap_ttls.c1384
-rw-r--r--contrib/wpa_supplicant/eap_ttls.h57
-rw-r--r--contrib/wpa_supplicant/eapol_sm.c1421
-rw-r--r--contrib/wpa_supplicant/eapol_sm.h138
-rw-r--r--contrib/wpa_supplicant/eapol_test.c1012
-rw-r--r--contrib/wpa_supplicant/eloop.c380
-rw-r--r--contrib/wpa_supplicant/eloop.h53
-rw-r--r--contrib/wpa_supplicant/hostap_common.h557
-rw-r--r--contrib/wpa_supplicant/l2_packet.h34
-rw-r--r--contrib/wpa_supplicant/md5.c355
-rw-r--r--contrib/wpa_supplicant/md5.h43
-rw-r--r--contrib/wpa_supplicant/ms_funcs.c333
-rw-r--r--contrib/wpa_supplicant/ms_funcs.h25
-rw-r--r--contrib/wpa_supplicant/openssl-tls-extensions.patch166
-rw-r--r--contrib/wpa_supplicant/pcsc_funcs.c776
-rw-r--r--contrib/wpa_supplicant/pcsc_funcs.h50
-rw-r--r--contrib/wpa_supplicant/preauth_test.c396
-rw-r--r--contrib/wpa_supplicant/radius_client.c674
-rw-r--r--contrib/wpa_supplicant/radius_client.h81
-rw-r--r--contrib/wpa_supplicant/rc4.c63
-rw-r--r--contrib/wpa_supplicant/rc4.h7
-rw-r--r--contrib/wpa_supplicant/sha1.c910
-rw-r--r--contrib/wpa_supplicant/sha1.h50
-rw-r--r--contrib/wpa_supplicant/tls.h312
-rw-r--r--contrib/wpa_supplicant/tls_none.c22
-rw-r--r--contrib/wpa_supplicant/tls_openssl.c876
-rw-r--r--contrib/wpa_supplicant/todo.txt54
-rw-r--r--contrib/wpa_supplicant/version.h6
-rw-r--r--contrib/wpa_supplicant/wpa.c2445
-rw-r--r--contrib/wpa_supplicant/wpa.h112
-rw-r--r--contrib/wpa_supplicant/wpa_cli.c850
-rw-r--r--contrib/wpa_supplicant/wpa_ctrl.c230
-rw-r--r--contrib/wpa_supplicant/wpa_ctrl.h15
-rw-r--r--contrib/wpa_supplicant/wpa_passphrase.c53
-rw-r--r--contrib/wpa_supplicant/wpa_supplicant.c2439
-rw-r--r--contrib/wpa_supplicant/wpa_supplicant.conf505
-rw-r--r--contrib/wpa_supplicant/wpa_supplicant.h81
-rw-r--r--contrib/wpa_supplicant/wpa_supplicant_i.h469
82 files changed, 35297 insertions, 0 deletions
diff --git a/contrib/wpa_supplicant/COPYING b/contrib/wpa_supplicant/COPYING
new file mode 100644
index 000000000000..60549be514af
--- /dev/null
+++ b/contrib/wpa_supplicant/COPYING
@@ -0,0 +1,340 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ 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.
+
+ 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.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) 19yy <name of author>
+
+ 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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.
+
+ <signature of Ty Coon>, 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.
diff --git a/contrib/wpa_supplicant/ChangeLog b/contrib/wpa_supplicant/ChangeLog
new file mode 100644
index 000000000000..57098581aee3
--- /dev/null
+++ b/contrib/wpa_supplicant/ChangeLog
@@ -0,0 +1,395 @@
+ChangeLog for wpa_supplicant
+
+2005-02-13 - v0.3.8
+ * fixed EAPOL-Key validation to drop packets with invalid Key Data
+ Length; such frames could have crashed wpa_supplicant due to buffer
+ overflow
+
+2005-02-12 - v0.3.7 (beginning of 0.3.x stable releases)
+ * added new phase1 option parameter, include_tls_length=1, to force
+ wpa_supplicant to add TLS Message Length field to all TLS messages
+ even if the packet is not fragmented; this may be needed with some
+ authentication servers
+ * fixed WPA/RSN IE verification in message 3 of 4-Way Handshake when
+ using drivers that take care of AP selection (e.g., when using
+ ap_scan=2)
+ * fixed reprocessing of pending request after ctrl_iface requests for
+ identity/password/otp
+ * fixed ctrl_iface requests for identity/password/otp in Phase 2 of
+ EAP-PEAP and EAP-TTLS
+ * all drivers using driver_wext: set interface up and select Managed
+ mode when starting wpa_supplicant; set interface down when exiting
+ * renamed driver_ipw2100.c to driver_ipw.c since it now supports both
+ ipw2100 and ipw2200; please note that this also changed the
+ configuration variable in .config to CONFIG_DRIVER_IPW
+
+2005-01-24 - v0.3.6
+ * fixed a busy loop introduced in v0.3.5 for scan result processing
+ when no matching AP is found
+
+2005-01-23 - v0.3.5
+ * added a workaround for an interoperability issue with a Cisco AP
+ when using WPA2-PSK
+ * fixed non-WPA IEEE 802.1X to use the same authentication timeout as
+ WPA with IEEE 802.1X (i.e., timeout 10 -> 70 sec to allow
+ retransmission of dropped frames)
+ * fixed issues with 64-bit CPUs and SHA1 cleanup in previous version
+ (e.g., segfault when processing EAPOL-Key frames)
+ * fixed EAP workaround and fast reauthentication configuration for
+ RSN pre-authentication; previously these were disabled and
+ pre-authentication would fail if the used authentication server
+ requires EAP workarounds
+ * added support for blacklisting APs that fail or timeout
+ authentication in ap_scan=1 mode so that all APs are tried in cases
+ where the ones with strongest signal level are failing authentication
+ * fixed CA certificate loading after a failed EAP-TLS/PEAP/TTLS
+ authentication attempt
+ * allow EAP-PEAP/TTLS fast reauthentication only if Phase 2 succeeded
+ in the previous authentication (previously, only Phase 1 success was
+ verified)
+
+2005-01-09 - v0.3.4
+ * added preliminary support for IBSS (ad-hoc) mode configuration
+ (mode=1 in network block); this included a new key_mgmt mode
+ WPA-NONE, i.e., TKIP or CCMP with a fixed key (based on psk) and no
+ key management; see wpa_supplicant.conf for more details and an
+ example on how to configure this (note: this is currently implemented
+ only for driver_hostapd.c, but the changes should be trivial to add
+ in associate() handler for other drivers, too (assuming the driver
+ supports WPA-None)
+ * added preliminary port for native Windows (i.e., no cygwin) using
+ mingw
+
+2005-01-02 - v0.3.3
+ * added optional support for GNU Readline and History Libraries for
+ wpa_cli (CONFIG_READLINE)
+ * cleaned up EAP state machine <-> method interface and number of
+ small problems with error case processing not terminating on
+ EAP-Failure but waiting for timeout
+ * added couple of workarounds for interoperability issues with a
+ Cisco AP when using WPA2
+ * added support for EAP-FAST (draft-cam-winget-eap-fast-00.txt);
+ Note: This requires a patch for openssl to add support for TLS
+ extensions and number of workarounds for operations without
+ certificates. Proof of concept type of experimental patch is
+ included in openssl-tls-extensions.patch.
+
+2004-12-19 - v0.3.2
+ * fixed private key loading for cases where passphrase is not set
+ * fixed Windows/cygwin L2 packet handler freeing; previous version
+ could cause a segfault when RSN pre-authentication was completed
+ * added support for PMKSA caching with drivers that generate RSN IEs
+ (e.g., NDIS); currently, this is only implemented in driver_ndis.c,
+ but similar code can be easily added to driver_ndiswrapper.c once
+ ndiswrapper gets full support for RSN PMKSA caching
+ * improved recovery from PMKID mismatches by requesting full EAP
+ authentication in case of failed PMKSA caching attempt
+ * driver_ndis: added support for NDIS NdisMIncidateStatus() events
+ (this requires that ndis_events is ran while wpa_supplicant is
+ running)
+ * driver_ndis: use ADD_WEP/REMOVE_WEP when configuring WEP keys
+ * added support for driver interfaces to replace the interface name
+ based on driver/OS specific mapping, e.g., in case of driver_ndis,
+ this allows the beginning of the adapter description to be used as
+ the interface name
+ * added support for CR+LF (Windows-style) line ends in configuration
+ file
+ * driver_ndis: enable radio before starting scanning, disable radio
+ when exiting
+ * modified association event handler to set portEnabled = FALSE before
+ clearing port Valid in order to reset EAP state machine and avoid
+ problems with new authentication getting ignored because of state
+ machines ending up in AUTHENTICATED/SUCCESS state based on old
+ information
+ * added support for driver events to add PMKID candidates in order to
+ allow drivers to give priority to most likely roaming candidates
+ * driver_hostap: moved PrivacyInvoked configuration to associate()
+ function so that this will not be set for plaintext connections
+ * added KEY_MGMT_802_1X_NO_WPA as a new key_mgmt type so that driver
+ interface can distinguish plaintext and IEEE 802.1X (no WPA)
+ authentication
+ * fixed static WEP key configuration to use broadcast/default type for
+ all keys (previously, the default TX key was configured as pairwise/
+ unicast key)
+ * driver_ndis: added legacy WPA capability detection for non-WPA2
+ drivers
+ * added support for setting static WEP keys for IEEE 802.1X without
+ dynamic WEP keying (eapol_flags=0)
+
+2004-12-12 - v0.3.1
+ * added support for reading PKCS#12 (PFX) files (as a replacement for
+ PEM/DER) to get certificate and private key (CONFIG_PKCS12)
+ * fixed compilation with CONFIG_PCSC=y
+ * added new ap_scan mode, ap_scan=2, for drivers that take care of
+ association, but need to be configured with security policy and SSID,
+ e.g., ndiswrapper and NDIS driver; this mode should allow such
+ drivers to work with hidden SSIDs and optimized roaming; when
+ ap_scan=2 is used, only the first network block in the configuration
+ file is used and this configuration should have explicit security
+ policy (i.e., only one option in the lists) for key_mgmt, pairwise,
+ group, proto variables
+ * added experimental port of wpa_supplicant for Windows
+ - driver_ndis.c driver interface (NDIS OIDs)
+ - currently, this requires cygwin and WinPcap
+ - small utility, win_if_list, can be used to get interface name
+ * control interface can now be removed at build time; add
+ CONFIG_CTRL_IFACE=y to .config to maintain old functionality
+ * optional Xsupplicant interface can now be removed at build time;
+ (CONFIG_XSUPPLICANT_IFACE=y in .config to bring it back)
+ * added auth_alg to driver interface associate() parameters to make it
+ easier for drivers to configure authentication algorithm as part of
+ the association
+
+2004-12-05 - v0.3.0 (beginning of 0.3.x development releases)
+ * driver_broadcom: added new driver interface for Broadcom wl.o driver
+ (a generic driver for Broadcom IEEE 802.11a/g cards)
+ * wpa_cli: fixed parsing of -p <path> command line argument
+ * PEAPv1: fixed tunneled EAP-Success reply handling to reply with TLS
+ ACK, not tunneled EAP-Success (of which only the first byte was
+ actually send due to a bug in previous code); this seems to
+ interoperate with most RADIUS servers that implements PEAPv1
+ * PEAPv1: added support for terminating PEAP authentication on tunneled
+ EAP-Success message; this can be configured by adding
+ peap_outer_success=0 on phase1 parameters in wpa_supplicant.conf
+ (some RADIUS servers require this whereas others require a tunneled
+ reply
+ * PEAPv1: changed phase1 option peaplabel to use default to 0, i.e., to
+ the old label for key derivation; previously, the default was 1,
+ but it looks like most existing PEAPv1 implementations use the old
+ label which is thus more suitable default option
+ * added support for EAP-PSK (draft-bersani-eap-psk-03.txt)
+ * fixed parsing of wep_tx_keyidx
+ * added support for configuring list of allowed Phase 2 EAP types
+ (for both EAP-PEAP and EAP-TTLS) instead of only one type
+ * added support for configuring IEEE 802.11 authentication algorithm
+ (auth_alg; mainly for using Shared Key authentication with static
+ WEP keys)
+ * added support for EAP-AKA (with UMTS SIM)
+ * fixed couple of errors in PCSC handling that could have caused
+ random-looking errors for EAP-SIM
+ * added support for EAP-SIM pseudonyms and fast re-authentication
+ * added support for EAP-TLS/PEAP/TTLS fast re-authentication (TLS
+ session resumption)
+ * added support for EAP-SIM with two challanges
+ (phase1="sim_min_num_chal=3" can be used to require three challenges)
+ * added support for configuring DH/DSA parameters for an ephemeral DH
+ key exchange (EAP-TLS/PEAP/TTLS) using new configuration parameters
+ dh_file and dh_file2 (phase 2); this adds support for using DSA keys
+ and optional DH key exchange to achieve forward secracy with RSA keys
+ * added support for matching subject of the authentication server
+ certificate with a substring when using EAP-TLS/PEAP/TTLS; new
+ configuration variables subject_match and subject_match2
+ * changed SSID configuration in driver_wext.c (used by many driver
+ interfaces) to use ssid_len+1 as the length for SSID since some Linux
+ drivers expect this
+ * fixed couple of unaligned reads in scan result parsing to fix WPA
+ connection on some platforms (e.g., ARM)
+ * added driver interface for Intel ipw2100 driver
+ * added support for LEAP with WPA
+ * added support for larger scan results report (old limit was 4 kB of
+ data, i.e., about 35 or so APs) when using Linux wireless extensions
+ v17 or newer
+ * fixed a bug in PMKSA cache processing: skip sending of EAPOL-Start
+ only if there is a PMKSA cache entry for the current AP
+ * fixed error handling for case where reading of scan results fails:
+ must schedule a new scan or wpa_supplicant will remain waiting
+ forever
+ * changed debug output to remove shared password/key material by
+ default; all key information can be included with -K command line
+ argument to match the previous behavior
+ * added support for timestamping debug log messages (disabled by
+ default, can be enabled with -t command line argument)
+ * set pairwise/group cipher suite for non-WPA IEEE 802.1X to WEP-104
+ if keys are not configured to be used; this fixes IEEE 802.1X mode
+ with drivers that use this information to configure whether Privacy
+ bit can be in Beacon frames (e.g., ndiswrapper)
+ * avoid clearing driver keys if no keys have been configured since last
+ key clear request; this seems to improve reliability of group key
+ handshake for ndiswrapper & NDIS driver which seems to be suffering
+ of some kind of timing issue when the keys are cleared again after
+ association
+ * changed driver interface API:
+ - WPA_SUPPLICANT_DRIVER_VERSION define can be used to determine which
+ version is being used (now, this is set to 2; previously, it was
+ not defined)
+ - pass pointer to private data structure to all calls
+ - the new API is not backwards compatible; all in-tree driver
+ interfaces has been converted to the new API
+ * added support for controlling multiple interfaces (radios) per
+ wpa_supplicant process; each interface needs to be listed on the
+ command line (-c, -i, -D arguments) with -N as a separator
+ (-cwpa1.conf -iwlan0 -Dhostap -N -cwpa2.conf -iath0 -Dmadwifi)
+ * added a workaround for EAP servers that incorrectly use same Id for
+ sequential EAP packets
+ * changed libpcap/libdnet configuration to use .config variable,
+ CONFIG_DNET_PCAP, instead of requiring Makefile modification
+ * improved downgrade attack detection in IE verification of msg 3/4:
+ verify both WPA and RSN IEs, if present, not only the selected one;
+ reject the AP if an RSN IE is found in msg 3/4, but not in Beacon or
+ Probe Response frame, and RSN is enabled in wpa_supplicant
+ configuration
+ * fixed WPA msg 3/4 processing to allow Key Data field contain other
+ IEs than just one WPA IE
+ * added support for FreeBSD and driver interface for the BSD net80211
+ layer (CONFIG_DRIVER_BSD=y in .config); please note that some of the
+ required kernel mods have not yet been committed
+ * made EAP workarounds configurable; enabled by default, can be
+ disabled with network block option eap_workaround=0
+
+2004-07-17 - v0.2.4 (beginning of 0.2.x stable releases)
+ * resolved couple of interoperability issues with EAP-PEAPv1 and
+ Phase 2 (inner EAP) fragment reassembly
+ * driver_madwifi: fixed WEP key configuration for IEEE 802.1X when the
+ AP is using non-zero key index for the unicast key and key index zero
+ for the broadcast key
+ * driver_hostap: fixed IEEE 802.1X WEP key updates and
+ re-authentication by allowing unencrypted EAPOL frames when not using
+ WPA
+ * added a new driver interface, 'wext', which uses only standard,
+ driver independent functionality in Linux wireless extensions;
+ currently, this can be used only for non-WPA IEEE 802.1X mode, but
+ eventually, this is to be extended to support full WPA/WPA2 once
+ Linux wireless extensions get support for this
+ * added support for mode in which the driver is responsible for AP
+ scanning and selection; this is disabled by default and can be
+ enabled with global ap_scan=0 variable in wpa_supplicant.conf;
+ this mode can be used, e.g., with generic 'wext' driver interface to
+ use wpa_supplicant as IEEE 802.1X Supplicant with any Linux driver
+ supporting wireless extensions.
+ * driver_madwifi: fixed WPA2 configuration and scan_ssid=1 (e.g.,
+ operation with an AP that does not include SSID in the Beacon frames)
+ * added support for new EAP authentication methods:
+ EAP-TTLS/EAP-OTP, EAP-PEAPv0/OTP, EAP-PEAPv1/OTP, EAP-OTP
+ * added support for asking one-time-passwords from frontends (e.g.,
+ wpa_cli); this 'otp' command works otherwise like 'password' command,
+ but the password is used only once and the frontend will be asked for
+ a new password whenever a request from authenticator requires a
+ password; this can be used with both EAP-OTP and EAP-GTC
+ * changed wpa_cli to automatically re-establish connection so that it
+ does not need to be re-started when wpa_supplicant is terminated and
+ started again
+ * improved user data (identity/password/otp) requests through
+ frontends: process pending EAPOL packets after getting new
+ information so that full authentication does not need to be
+ restarted; in addition, send pending requests again whenever a new
+ frontend is attached
+ * changed control frontends to use a new directory for socket files to
+ make it easier for wpa_cli to automatically select between interfaces
+ and to provide access control for the control interface;
+ wpa_supplicant.conf: ctrl_interface is now a path
+ (/var/run/wpa_supplicant is the recommended path) and
+ ctrl_interface_group can be used to select which group gets access to
+ the control interface;
+ wpa_cli: by default, try to connect to the first interface available
+ in /var/run/wpa_supplicant; this path can be overriden with -p option
+ and an interface can be selected with -i option (i.e., in most common
+ cases, wpa_cli does not need to get any arguments)
+ * added support for LEAP
+ * added driver interface for Linux ndiswrapper
+ * added priority option for network blocks in the configuration file;
+ this allows networks to be grouped based on priority (the scan
+ results are searched for matches with network blocks in this order)
+
+2004-06-20 - v0.2.3
+ * sort scan results to improve AP selection
+ * fixed control interface socket removal for some error cases
+ * improved scan requesting and authentication timeout
+ * small improvements/bug fixes for EAP-MSCHAPv2, EAP-PEAP, and
+ TLS processing
+ * PEAP version can now be forced with phase1="peapver=<ver>"
+ (mostly for testing; by default, the highest version supported by
+ both the Supplicant and Authentication Server is selected
+ automatically)
+ * added support for madwifi driver (Atheros ar521x)
+ * added a workaround for cases where AP sets Install Tx/Rx bit for
+ WPA Group Key messages when pairwise keys are used (without this,
+ the Group Key would be used for Tx and the AP would drop frames
+ from the station)
+ * added GSM SIM/USIM interface for GSM authentication algorithm for
+ EAP-SIM; this requires pcsc-lite
+ * added support for ATMEL AT76C5XXx driver
+ * fixed IEEE 802.1X WEP key derivation in the case where Authenticator
+ does not include key data in the EAPOL-Key frame (i.e., part of
+ EAP keying material is used as data encryption key)
+ * added support for using plaintext and static WEP networks
+ (key_mgmt=NONE)
+
+2004-05-31 - v0.2.2
+ * added support for new EAP authentication methods:
+ EAP-TTLS/EAP-MD5-Challenge
+ EAP-TTLS/EAP-GTC
+ EAP-TTLS/EAP-MSCHAPv2
+ EAP-TTLS/EAP-TLS
+ EAP-TTLS/MSCHAPv2
+ EAP-TTLS/MSCHAP
+ EAP-TTLS/PAP
+ EAP-TTLS/CHAP
+ EAP-PEAP/TLS
+ EAP-PEAP/GTC
+ EAP-PEAP/MD5-Challenge
+ EAP-GTC
+ EAP-SIM (not yet complete; needs GSM/SIM authentication interface)
+ * added support for anonymous identity (to be used when identity is
+ sent in plaintext; real identity will be used within TLS protected
+ tunnel (e.g., with EAP-TTLS)
+ * added event messages from wpa_supplicant to frontends, e.g., wpa_cli
+ * added support for requesting identity and password information using
+ control interface; in other words, the password for EAP-PEAP or
+ EAP-TTLS does not need to be included in the configuration file since
+ a frontand (e.g., wpa_cli) can ask it from the user
+ * improved RSN pre-authentication to use a candidate list and process
+ all candidates from each scan; not only one per scan
+ * fixed RSN IE and WPA IE capabilities field parsing
+ * ignore Tx bit in GTK IE when Pairwise keys are used
+ * avoid making new scan requests during IEEE 802.1X negotiation
+ * use openssl/libcrypto for MD5 and SHA-1 when compiling wpa_supplicant
+ with TLS support (this replaces the included implementation with
+ library code to save about 8 kB since the library code is needed
+ anyway for TLS)
+ * fixed WPA-PSK only mode when compiled without IEEE 802.1X support
+ (i.e., without CONFIG_IEEE8021X_EAPOL=y in .config)
+
+2004-05-06 - v0.2.1
+ * added support for internal IEEE 802.1X (actually, IEEE 802.1aa/D6.1)
+ Supplicant
+ - EAPOL state machines for Supplicant [IEEE 802.1aa/D6.1]
+ - EAP peer state machine [draft-ietf-eap-statemachine-02.pdf]
+ - EAP-MD5 (cannot be used with WPA-RADIUS)
+ [draft-ietf-eap-rfc2284bis-09.txt]
+ - EAP-TLS [RFC 2716]
+ - EAP-MSCHAPv2 (currently used only with EAP-PEAP)
+ - EAP-PEAP/MSCHAPv2 [draft-josefsson-pppext-eap-tls-eap-07.txt]
+ [draft-kamath-pppext-eap-mschapv2-00.txt]
+ (PEAP version 0, 1, and parts of 2; only 0 and 1 are enabled by
+ default; tested with FreeRADIUS, Microsoft IAS, and Funk Odyssey)
+ - new configuration file options: eap, identity, password, ca_cert,
+ client_cert, privatekey, private_key_passwd
+ - Xsupplicant is not required anymore, but it can be used by
+ disabling the internal IEEE 802.1X Supplicant with -e command line
+ option
+ - this code is not included in the default build; Makefile need to
+ be edited for this (uncomment lines for selected functionality)
+ - EAP-TLS and EAP-PEAP require openssl libraries
+ * use module prefix in debug messages (WPA, EAP, EAP-TLS, ..)
+ * added support for non-WPA IEEE 802.1X mode with dynamic WEP keys
+ (i.e., complete IEEE 802.1X/EAP authentication and use IEEE 802.1X
+ EAPOL-Key frames instead of WPA key handshakes)
+ * added support for IEEE 802.11i/RSN (WPA2)
+ - improved PTK Key Handshake
+ - PMKSA caching, pre-authentication
+ * fixed wpa_supplicant to ignore possible extra data after WPA
+ EAPOL-Key packets (this fixes 'Invalid EAPOL-Key MIC when using
+ TPTK' error from message 3 of 4-Way Handshake in case the AP
+ includes extra data after the EAPOL-Key)
+ * added interface for external programs (frontends) to control
+ wpa_supplicant
+ - CLI example (wpa_cli) with interactive mode and command line
+ mode
+ - replaced SIGUSR1 status/statistics with the new control interface
+ * made some feature compile time configurable
+ - .config file for make
+ - driver interfaces (hostap, hermes, ..)
+ - EAPOL/EAP functions
+
+2004-02-15 - v0.2.0
+ * Initial version of wpa_supplicant
diff --git a/contrib/wpa_supplicant/Makefile b/contrib/wpa_supplicant/Makefile
new file mode 100644
index 000000000000..fa912436c86e
--- /dev/null
+++ b/contrib/wpa_supplicant/Makefile
@@ -0,0 +1,385 @@
+ifndef CC
+CC=gcc
+endif
+
+ifndef CFLAGS
+CFLAGS = -MMD -O2 -Wall -g
+endif
+
+# Include directories for CVS version
+CFLAGS += -I../driver/modules -I../utils -I../hostapd
+
+ALL=wpa_supplicant wpa_passphrase wpa_cli
+
+all: verify_config $(ALL)
+
+verify_config:
+ @if [ ! -r .config ]; then \
+ echo 'Building wpa_supplicant requires a configuration file'; \
+ echo '(.config). See README for more instructions. You can'; \
+ echo 'run "cp defconfig .config" to create an example'; \
+ echo 'configuration.'; \
+ exit 1; \
+ fi
+
+mkconfig:
+ @if [ -e .config ]; then \
+ echo '.config exists - did not replace it'; \
+ exit 1; \
+ fi
+ echo CONFIG_DRIVER_HOSTAP=y >> .config
+ echo CONFIG_DRIVER_WEXT=y >> .config
+ echo CONFIG_WIRELESS_EXTENSION=y >> .config
+
+install: all
+ mkdir -p $(DESTDIR)/usr/local/sbin/
+ for i in $(ALL); do cp $$i $(DESTDIR)/usr/local/sbin/$$i; done
+
+OBJS = config.o \
+ eloop.o common.o md5.o \
+ rc4.o sha1.o aes_wrap.o
+OBJS_p = wpa_passphrase.o sha1.o md5.o
+OBJS_c = wpa_cli.o wpa_ctrl.o
+
+-include .config
+
+ifdef CONFIG_EAPOL_TEST
+CFLAGS += -Werror -DEAPOL_TEST
+endif
+
+ifdef CONFIG_DRIVER_HOSTAP
+CFLAGS += -DCONFIG_DRIVER_HOSTAP
+OBJS += driver_hostap.o
+CONFIG_WIRELESS_EXTENSION=y
+endif
+
+ifdef CONFIG_DRIVER_WEXT
+CFLAGS += -DCONFIG_DRIVER_WEXT
+CONFIG_WIRELESS_EXTENSION=y
+endif
+
+ifdef CONFIG_DRIVER_PRISM54
+CFLAGS += -DCONFIG_DRIVER_PRISM54
+OBJS += driver_prism54.o
+CONFIG_WIRELESS_EXTENSION=y
+endif
+
+ifdef CONFIG_DRIVER_HERMES
+CFLAGS += -DCONFIG_DRIVER_HERMES
+OBJS += driver_hermes.o
+CONFIG_WIRELESS_EXTENSION=y
+endif
+
+ifdef CONFIG_DRIVER_MADWIFI
+CFLAGS += -DCONFIG_DRIVER_MADWIFI
+OBJS += driver_madwifi.o
+CONFIG_WIRELESS_EXTENSION=y
+endif
+
+ifdef CONFIG_DRIVER_ATMEL
+CFLAGS += -DCONFIG_DRIVER_ATMEL
+OBJS += driver_atmel.o
+CONFIG_WIRELESS_EXTENSION=y
+endif
+
+ifdef CONFIG_DRIVER_NDISWRAPPER
+CFLAGS += -DCONFIG_DRIVER_NDISWRAPPER
+OBJS += driver_ndiswrapper.o
+CONFIG_WIRELESS_EXTENSION=y
+endif
+
+ifdef CONFIG_DRIVER_BROADCOM
+CFLAGS += -DCONFIG_DRIVER_BROADCOM
+OBJS += driver_broadcom.o
+endif
+
+ifdef CONFIG_DRIVER_IPW
+CFLAGS += -DCONFIG_DRIVER_IPW
+OBJS += driver_ipw.o
+CONFIG_WIRELESS_EXTENSION=y
+endif
+
+ifdef CONFIG_DRIVER_BSD
+CFLAGS += -DCONFIG_DRIVER_BSD
+OBJS += driver_bsd.o
+CONFIG_DNET_PCAP=y
+endif
+
+ifdef CONFIG_DRIVER_NDIS
+CFLAGS += -DCONFIG_DRIVER_NDIS
+OBJS += driver_ndis.o driver_ndis_.o
+CONFIG_DNET_PCAP=y
+CONFIG_WINPCAP=y
+endif
+
+ifdef CONFIG_DRIVER_TEST
+CFLAGS += -DCONFIG_DRIVER_TEST
+OBJS += driver_test.o
+endif
+
+ifdef CONFIG_DNET_PCAP
+CFLAGS += -DUSE_DNET_PCAP
+ifdef CONFIG_WINPCAP
+CFLAGS += -DCONFIG_WINPCAP
+LIBS += -lwpcap -lpacket
+LIBS_w += -lwpcap
+else
+LIBS += -ldnet -lpcap
+endif
+endif
+
+ifdef CONFIG_EAP_TLS
+# EAP-TLS
+CFLAGS += -DEAP_TLS
+OBJS += eap_tls.o
+TLS_FUNCS=y
+CONFIG_IEEE8021X_EAPOL=y
+endif
+
+ifdef CONFIG_EAP_PEAP
+# EAP-PEAP
+CFLAGS += -DEAP_PEAP
+OBJS += eap_peap.o
+TLS_FUNCS=y
+CONFIG_EAP_MSCHAPV2=y
+CONFIG_IEEE8021X_EAPOL=y
+CONFIG_EAP_TLV=y
+endif
+
+ifdef CONFIG_EAP_TTLS
+# EAP-TTLS
+CFLAGS += -DEAP_TTLS
+OBJS += eap_ttls.o
+MS_FUNCS=y
+TLS_FUNCS=y
+CONFIG_EAP_MD5=y
+CONFIG_IEEE8021X_EAPOL=y
+endif
+
+ifdef CONFIG_EAP_MD5
+# EAP-MD5 (also used by EAP-TTLS)
+CFLAGS += -DEAP_MD5
+OBJS += eap_md5.o
+CONFIG_IEEE8021X_EAPOL=y
+endif
+
+# backwards compatibility for old spelling
+ifdef CONFIG_MSCHAPV2
+CONFIG_EAP_MSCHAPV2=y
+endif
+
+ifdef CONFIG_EAP_MSCHAPV2
+# EAP-MSCHAPv2 (also used by EAP-PEAP)
+CFLAGS += -DEAP_MSCHAPv2
+OBJS += eap_mschapv2.o
+MS_FUNCS=y
+CONFIG_IEEE8021X_EAPOL=y
+endif
+
+ifdef CONFIG_EAP_GTC
+# EAP-GTC (also used by EAP-PEAP)
+CFLAGS += -DEAP_GTC
+OBJS += eap_gtc.o
+CONFIG_IEEE8021X_EAPOL=y
+endif
+
+ifdef CONFIG_EAP_OTP
+# EAP-OTP
+CFLAGS += -DEAP_OTP
+OBJS += eap_otp.o
+CONFIG_IEEE8021X_EAPOL=y
+endif
+
+ifdef CONFIG_EAP_SIM
+# EAP-SIM
+CFLAGS += -DEAP_SIM
+OBJS += eap_sim.o
+CONFIG_IEEE8021X_EAPOL=y
+CONFIG_EAP_SIM_COMMON=y
+endif
+
+ifdef CONFIG_EAP_LEAP
+# EAP-LEAP
+CFLAGS += -DEAP_LEAP
+OBJS += eap_leap.o
+MS_FUNCS=y
+CONFIG_IEEE8021X_EAPOL=y
+endif
+
+ifdef CONFIG_EAP_PSK
+# EAP-PSK
+CFLAGS += -DEAP_PSK
+OBJS += eap_psk.o
+CONFIG_IEEE8021X_EAPOL=y
+endif
+
+ifdef CONFIG_EAP_AKA
+# EAP-AKA
+CFLAGS += -DEAP_AKA
+OBJS += eap_aka.o
+CONFIG_IEEE8021X_EAPOL=y
+CONFIG_EAP_SIM_COMMON=y
+endif
+
+ifdef CONFIG_EAP_SIM_COMMON
+OBJS += eap_sim_common.o
+endif
+
+ifdef CONFIG_EAP_TLV
+# EAP-TLV
+CFLAGS += -DEAP_TLV
+OBJS += eap_tlv.o
+endif
+
+ifdef CONFIG_EAP_FAST
+# EAP-FAST
+CFLAGS += -DEAP_FAST
+OBJS += eap_fast.o
+TLS_FUNCS=y
+endif
+
+ifdef CONFIG_IEEE8021X_EAPOL
+# IEEE 802.1X/EAPOL state machines (e.g., for RADIUS authentication)
+CFLAGS += -DIEEE8021X_EAPOL
+OBJS += eapol_sm.o eap.o
+endif
+
+ifdef CONFIG_PCSC
+# PC/SC interface for smartcards (USIM, GSM SIM)
+CFLAGS += -DPCSC_FUNCS -I/usr/include/PCSC
+OBJS += pcsc_funcs.o
+# -lpthread may not be needed depending on how pcsc-lite was configured
+LIBS += -lpcsclite -lpthread
+endif
+
+ifdef TLS_FUNCS
+# Shared TLS functions (needed for EAP_TLS, EAP_PEAP, and EAP_TTLS)
+CFLAGS += -DEAP_TLS_FUNCS
+OBJS += eap_tls_common.o tls_openssl.o
+LIBS += -lssl -lcrypto
+LIBS_p += -lcrypto
+else
+OBJS += tls_none.o
+endif
+
+ifdef CONFIG_PKCS12
+CFLAGS += -DPKCS12_FUNCS
+endif
+
+ifdef MS_FUNCS
+ifndef TLS_FUNCS
+LIBS += -lcrypto
+endif
+OBJS += ms_funcs.o crypto.o
+endif
+
+ifdef CONFIG_WIRELESS_EXTENSION
+CFLAGS += -DCONFIG_WIRELESS_EXTENSION
+OBJS += driver_wext.o
+endif
+
+ifdef CONFIG_CTRL_IFACE
+CFLAGS += -DCONFIG_CTRL_IFACE
+OBJS += ctrl_iface.o
+endif
+
+ifdef CONFIG_XSUPPLICANT_IFACE
+CFLAGS += -DCONFIG_XSUPPLICANT_IFACE
+endif
+
+ifdef CONFIG_READLINE
+CFLAGS += -DCONFIG_READLINE
+LIBS_c += -lncurses -lreadline
+endif
+
+ifdef CONFIG_NATIVE_WINDOWS
+CFLAGS += -DCONFIG_NATIVE_WINDOWS -DCONFIG_CTRL_IFACE_UDP
+LIBS += -lws2_32 -lgdi32
+LIBS_c += -lws2_32
+endif
+
+OBJS_t := $(OBJS) eapol_test.o radius.o radius_client.o
+OBJS_t2 := $(OBJS) preauth_test.o l2_packet.o
+OBJS += wpa_supplicant.o wpa.o l2_packet.o drivers.o
+
+wpa_supplicant: .config $(OBJS)
+ $(CC) -o wpa_supplicant $(OBJS) $(LIBS)
+
+eapol_test: .config $(OBJS_t)
+ $(CC) -o eapol_test $(OBJS_t) $(LIBS)
+
+preauth_test: .config $(OBJS_t2)
+ $(CC) -o preauth_test $(OBJS_t2) $(LIBS)
+
+wpa_passphrase: $(OBJS_p)
+ $(CC) -o wpa_passphrase $(OBJS_p) $(LIBS_p)
+
+wpa_cli: $(OBJS_c)
+ $(CC) -o wpa_cli $(OBJS_c) $(LIBS_c)
+
+win_if_list: win_if_list.c
+ $(CC) -o $@ win_if_list.c $(CFLAGS) $(LIBS_w)
+
+# parameters for Microsoft Visual C++ Toolkit 2003 compiler
+CL=cl
+CLDIR=C:\Program Files\Microsoft Visual C++ Toolkit 2003
+PSDKDIR=C:\Program Files\Microsoft Platform SDK for Windows XP SP2
+CLFLAGS=-O
+CLLIBS=wbemuuid.lib libcmt.lib kernel32.lib uuid.lib ole32.lib oleaut32.lib \
+ ws2_32.lib
+
+ndis_events: ndis_events.cpp
+ INCLUDE="$(CLDIR)\include;$(PSDKDIR)\Include" \
+ LIB="$(CLDIR)\lib;$(PSDKDIR)\Lib" \
+ $(CL) $(CLFLAGS) -o ndis_events.exe ndis_events.cpp \
+ /link -nodefaultlib $(CLLIBS)
+
+wpa_supplicant.exe: wpa_supplicant
+ mv -f $< $@
+wpa_cli.exe: wpa_cli
+ mv -f $< $@
+wpa_passphrase.exe: wpa_passphrase
+ mv -f $< $@
+win_if_list.exe: win_if_list
+ mv -f $< $@
+
+WINALL=wpa_supplicant.exe wpa_cli.exe wpa_passphrase.exe win_if_list.exe
+
+windows-bin: $(WINALL)
+ $(STRIP) $(WINALL)
+
+TEST_SRC_MS_FUNCS = ms_funcs.c crypto.c sha1.c md5.c
+test-ms_funcs: $(TEST_SRC_MS_FUNCS)
+ $(CC) -o test-ms_funcs -Wall -Werror $(TEST_SRC_MS_FUNCS) \
+ -DTEST_MAIN_MS_FUNCS -lcrypto -I../hostapd
+ ./test-ms_funcs
+ rm test-ms_funcs
+
+TEST_SRC_SHA1 = sha1.c
+test-sha1: $(TEST_SRC_SHA1)
+ $(CC) -o test-sha1 -Wall -Werror $(TEST_SRC_SHA1) \
+ -DTEST_MAIN -I../hostad
+ ./test-sha1
+ rm test-sha1
+
+TEST_SRC_AES_WRAP = aes_wrap.c
+test-aes_wrap: $(TEST_SRC_AES_WRAP)
+ $(CC) -o test-aes_wrap -Wall -Werror $(TEST_SRC_AES_WRAP) \
+ -DTEST_MAIN -I../hostad
+ ./test-aes_wrap
+ rm test-aes_wrap
+
+TEST_SRC_EAP_SIM_COMMON = eap_sim_common.c sha1.c md5.c \
+ aes_wrap.c common.c
+test-eap_sim_common: $(TEST_SRC_EAP_SIM_COMMON)
+ $(CC) -o test-eap_sim_common -Wall -Werror $(TEST_SRC_EAP_SIM_COMMON) \
+ -DTEST_MAIN_EAP_SIM_COMMON -I../hostapd
+ ./test-eap_sim_common
+ rm test-eap_sim_common
+
+tests: test-ms_funcs test-sha1 test-aes_wrap test-eap_sim_common
+
+clean:
+ rm -f core *~ *.o *.d $(ALL) $(WINALL)
+
+-include $(OBJS:%.o=%.d)
diff --git a/contrib/wpa_supplicant/README b/contrib/wpa_supplicant/README
new file mode 100644
index 000000000000..7b5f5476a160
--- /dev/null
+++ b/contrib/wpa_supplicant/README
@@ -0,0 +1,860 @@
+WPA Supplicant
+==============
+
+Copyright (c) 2003-2005, Jouni Malinen <jkmaline@cc.hut.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. Please note that
+some of the driver interface implementations (driver_*.c) may be
+licensed under a different license.
+
+
+
+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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+(this copy of the license is in COPYING file)
+
+
+Alternatively, this software may be distributed 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
+met:
+
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+3. Neither the name(s) of the above-listed copyright holder(s) nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER 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.
+
+
+
+Features
+--------
+
+Supported WPA/IEEE 802.11i features:
+- WPA-PSK ("WPA-Personal")
+- WPA with EAP (e.g., with RADIUS authentication server) ("WPA-Enterprise")
+ Following authentication methods are supported with an integrate IEEE 802.1X
+ Supplicant:
+ * EAP-TLS
+ * EAP-PEAP/MSCHAPv2 (both PEAPv0 and PEAPv1)
+ * EAP-PEAP/TLS (both PEAPv0 and PEAPv1)
+ * EAP-PEAP/GTC (both PEAPv0 and PEAPv1)
+ * EAP-PEAP/OTP (both PEAPv0 and PEAPv1)
+ * EAP-PEAP/MD5-Challenge (both PEAPv0 and PEAPv1)
+ * EAP-TTLS/EAP-MD5-Challenge
+ * EAP-TTLS/EAP-GTC
+ * EAP-TTLS/EAP-OTP
+ * EAP-TTLS/EAP-MSCHAPv2
+ * EAP-TTLS/EAP-TLS
+ * EAP-TTLS/MSCHAPv2
+ * EAP-TTLS/MSCHAP
+ * EAP-TTLS/PAP
+ * EAP-TTLS/CHAP
+ * EAP-SIM
+ * EAP-AKA
+ * EAP-PSK
+ * LEAP (note: requires special support from the driver for IEEE 802.11
+ authentication)
+ (following methods are supported, but since they do not generate keying
+ material, they cannot be used with WPA or IEEE 802.1X WEP keying)
+ * EAP-MD5-Challenge
+ * EAP-MSCHAPv2
+ * EAP-GTC
+ * EAP-OTP
+ Alternatively, an external program, e.g., Xsupplicant, can be used for EAP
+ authentication.
+- key management for CCMP, TKIP, WEP104, WEP40
+- RSN/WPA2 (IEEE 802.11i)
+ * pre-authentication
+ * PMKSA caching
+
+
+
+Requirements
+------------
+
+Current hardware/software requirements:
+- Linux kernel 2.4.x or 2.6.x with Linux Wireless Extensions v15 or newer
+- FreeBSD 6-CURRENT
+- Microsoft Windows with WinPcap (at least WinXP, may work with other versions)
+- drivers:
+ 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.
+
+ 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).
+
+ ATMEL AT76C5XXx driver for USB and PCMCIA cards
+ (http://atmelwlandriver.sourceforge.net/).
+
+ 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/)
+
+ 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
+ configuration file.
+
+ BSD net80211 layer (e.g., Atheros driver)
+ At the moment, this is for FreeBSD 6-CURRENT branch.
+
+ Windows NDIS
+ The current Windows port requires WinPcap (http://winpcap.polito.it/).
+ See README-Windows.txt for more information.
+
+wpa_supplicant was designed to be portable for different drivers and
+operating systems. Hopefully, support for more wlan cards and OSes will be
+added in the future. See developer.txt for more information about the
+design of wpa_supplicant and porting to other drivers. One main goal
+is to add full WPA/WPA2 support to Linux wireless extensions to allow
+new drivers to be supported without having to implement new
+driver-specific interface code in wpa_supplicant.
+
+Optional libraries for layer2 packet processing:
+- libpcap (tested with 0.7.2, most relatively recent versions assumed to work,
+ this is likely to be available with most distributions,
+ http://tcpdump.org/)
+- libdnet (tested with v1.4, most versions assumed to work,
+ http://libdnet.sourceforge.net/)
+
+These libraries are _not_ used in the default Linux build. Instead,
+internal Linux specific implementation is used. libpcap/libdnet are
+more portable and they can be used by adding CONFIG_DNET_PCAP=y into
+.config. They may also be selected automatically for other operating
+systems.
+
+
+Optional libraries for EAP-TLS, EAP-PEAP, and EAP-TTLS:
+- openssl (tested with 0.9.7c and 0.9.7d, assumed to work with most
+ relatively recent versions; this is likely to be available with most
+ distributions, http://www.openssl.org/)
+
+This library is only needed when EAP-TLS, EAP-PEAP, or EAP-TTLS
+support is enabled. WPA-PSK mode does not require this or EAPOL/EAP
+implementation. A configuration file, .config, for compilation is
+needed to enable IEEE 802.1X/EAPOL and EAP methods. Note that EAP-MD5,
+EAP-GTC, EAP-OTP, and EAP-MSCHAPV2 cannot be used alone with WPA, so
+they should only be enabled if testing the EAPOL/EAP state
+machines. However, there can be used as inner authentication
+algorithms with EAP-PEAP and EAP-TTLS.
+
+See Building and installing section below for more detailed
+information about the wpa_supplicant build time configuration.
+
+
+
+WPA
+---
+
+The original security mechanism of IEEE 802.11 standard was not
+designed to be strong and has proven to be insufficient for most
+networks that require some kind of security. Task group I (Security)
+of IEEE 802.11 working group (http://www.ieee802.org/11/) has worked
+to address the flaws of the base standard and has in practice
+completed its work in May 2004. The IEEE 802.11i amendment to the IEEE
+802.11 standard was approved in June 2004 and this amendment is likely
+to be published in July 2004.
+
+Wi-Fi Alliance (http://www.wi-fi.org/) used a draft version of the
+IEEE 802.11i work (draft 3.0) to define a subset of the security
+enhancements that can be implemented with existing wlan hardware. This
+is called Wi-Fi Protected Access<TM> (WPA). This has now become a
+mandatory component of interoperability testing and certification done
+by Wi-Fi Alliance. Wi-Fi provides information about WPA at its web
+site (http://www.wi-fi.org/OpenSection/protected_access.asp).
+
+IEEE 802.11 standard defined wired equivalent privacy (WEP) algorithm
+for protecting wireless networks. WEP uses RC4 with 40-bit keys,
+24-bit initialization vector (IV), and CRC32 to protect against packet
+forgery. All these choices have proven to be insufficient: key space is
+too small against current attacks, RC4 key scheduling is insufficient
+(beginning of the pseudorandom stream should be skipped), IV space is
+too small and IV reuse makes attacks easier, there is no replay
+protection, and non-keyed authentication does not protect against bit
+flipping packet data.
+
+WPA is an intermediate solution for the security issues. It uses
+Temporal Key Integrity Protocol (TKIP) to replace WEP. TKIP is a
+compromise on strong security and possibility to use existing
+hardware. It still uses RC4 for the encryption like WEP, but with
+per-packet RC4 keys. In addition, it implements replay protection,
+keyed packet authentication mechanism (Michael MIC).
+
+Keys can be managed using two different mechanisms. WPA can either use
+an external authentication server (e.g., RADIUS) and EAP just like
+IEEE 802.1X is using or pre-shared keys without need for additional
+servers. Wi-Fi calls these "WPA-Enterprise" and "WPA-Personal",
+respectively. Both mechanisms will generate a master session key for
+the Authenticator (AP) and Supplicant (client station).
+
+WPA implements a new key handshake (4-Way Handshake and Group Key
+Handshake) for generating and exchanging data encryption keys between
+the Authenticator and Supplicant. This handshake is also used to
+verify that both Authenticator and Supplicant know the master session
+key. These handshakes are identical regardless of the selected key
+management mechanism (only the method for generating master session
+key changes).
+
+
+
+IEEE 802.11i / WPA2
+-------------------
+
+The design for parts of IEEE 802.11i that were not included in WPA has
+finished (May 2004) and this amendment to IEEE 802.11 was approved in
+June 2004. Wi-Fi Alliance is using the final IEEE 802.11i as a new
+version of WPA called WPA2. This includes, e.g., support for more
+robust encryption algorithm (CCMP: AES in Counter mode with CBC-MAC)
+to replace TKIP and optimizations for handoff (reduced number of
+messages in initial key handshake, pre-authentication, and PMKSA caching).
+
+Some wireless LAN vendors are already providing support for CCMP in
+their WPA products. There is no "official" interoperability
+certification for CCMP and/or mixed modes using both TKIP and CCMP, so
+some interoperability issues can be expected even though many
+combinations seem to be working with equipment from different vendors.
+Certification for WPA2 is likely to start during the second half of
+2004.
+
+
+
+wpa_supplicant
+--------------
+
+wpa_supplicant is an implementation of the WPA Supplicant component,
+i.e., the part that runs in the client stations. It implements WPA key
+negotiation with a WPA Authenticator and EAP authentication with
+Authentication Server. In addition, it controls the roaming and IEEE
+802.11 authentication/association of the wlan driver.
+
+wpa_supplicant is designed to be a "daemon" program that runs in the
+background and acts as the backend component controlling the wireless
+connection. wpa_supplicant supports separate frontend programs and an
+example text-based frontend, wpa_cli, is included with wpa_supplicant.
+
+Following steps are used when associating with an AP using WPA:
+
+- wpa_supplicant requests the kernel driver to scan neighboring BSSes
+- wpa_supplicant selects a BSS based on its configuration
+- wpa_supplicant requests the kernel driver to associate with the chosen
+ BSS
+- If WPA-EAP: integrated IEEE 802.1X Supplicant or external Xsupplicant
+ completes EAP authentication with the authentication server (proxied
+ by the Authenticator in the AP)
+- If WPA-EAP: master key is received from the IEEE 802.1X Supplicant
+- If WPA-PSK: wpa_supplicant uses PSK as the master session key
+- wpa_supplicant completes WPA 4-Way Handshake and Group Key Handshake
+ with the Authenticator (AP)
+- wpa_supplicant configures encryption keys for unicast and broadcast
+- normal data packets can be transmitted and received
+
+
+
+Building and installing
+-----------------------
+
+In order to be able to build wpa_supplicant, you will first need to
+select which parts of it will be included. This is done by creating a
+build time configuration file, .config, in the wpa_supplicant root
+directory. Configuration options are text lines using following
+format: CONFIG_<option>=y. Lines starting with # are considered
+comments and are ignored. See defconfig file for example configuration
+and list of available option.
+
+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
+methods (e.g., EAP-TLS, EAP-PEAP, ..) are included.
+
+Following build time configuration options are used to control IEEE
+802.1X/EAPOL and EAP state machines and all EAP methods. Including
+TLS, PEAP, or TTLS will require linking wpa_supplicant with openssl
+library for TLS implementation.
+
+CONFIG_IEEE8021X_EAPOL=y
+CONFIG_EAP_MD5=y
+CONFIG_EAP_MSCHAPV2=y
+CONFIG_EAP_TLS=y
+CONFIG_EAP_PEAP=y
+CONFIG_EAP_TTLS=y
+CONFIG_EAP_GTC=y
+CONFIG_EAP_OTP=y
+CONFIG_EAP_SIM=y
+CONFIG_EAP_AKA=y
+CONFIG_EAP_PSK=y
+CONFIG_EAP_LEAP=y
+
+Following option can be used to include GSM SIM/USIM interface for GSM/UMTS
+authentication algorithm (for EAP-SIM/EAP-AKA). This requires pcsc-lite
+(http://www.linuxnet.com/) for smart card access.
+
+CONFIG_PCSC=y
+
+Following option can be used to replace the native Linux packet socket
+interface with libpcap/libdnet.
+
+CONFIG_DNET_PCAP=y
+
+Following options can be added to .config to select which driver
+interfaces are included. Prism54.org driver is not yet complete and
+Hermes driver interface needs to be downloaded from Agere (see above).
+Most Linux driver need to include CONFIG_WIRELESS_EXTENSION.
+
+CONFIG_WIRELESS_EXTENSION=y
+CONFIG_DRIVER_HOSTAP=y
+CONFIG_DRIVER_PRISM54=y
+CONFIG_DRIVER_HERMES=y
+CONFIG_DRIVER_MADWIFI=y
+CONFIG_DRIVER_ATMEL=y
+CONFIG_DRIVER_WEXT=y
+CONFIG_DRIVER_NDISWRAPPER=y
+CONFIG_DRIVER_BROADCOM=y
+CONFIG_DRIVER_IPW=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:
+
+CONFIG_DRIVER_HOSTAP=y
+CONFIG_DRIVER_PRISM54=y
+CONFIG_DRIVER_HERMES=y
+CONFIG_DRIVER_MADWIFI=y
+CONFIG_DRIVER_ATMEL=y
+CONFIG_DRIVER_WEXT=y
+CONFIG_DRIVER_NDISWRAPPER=y
+CONFIG_DRIVER_BROADCOM=y
+CONFIG_DRIVER_IPW=y
+CONFIG_DRIVER_BSD=y
+CONFIG_DRIVER_NDIS=y
+CONFIG_WIRELESS_EXTENSION=y
+CONFIG_IEEE8021X_EAPOL=y
+CONFIG_EAP_MD5=y
+CONFIG_EAP_MSCHAPV2=y
+CONFIG_EAP_TLS=y
+CONFIG_EAP_PEAP=y
+CONFIG_EAP_TTLS=y
+CONFIG_EAP_GTC=y
+CONFIG_EAP_OTP=y
+CONFIG_EAP_SIM=y
+CONFIG_EAP_AKA=y
+CONFIG_EAP_PSK=y
+CONFIG_EAP_LEAP=y
+CONFIG_PCSC=y
+
+EAP-PEAP and EAP-TTLS will automatically include configured EAP
+methods (MD5, OTP, GTC, MSCHAPV2) for inner authentication selection.
+
+
+After you have created a configuration file, you can build
+wpa_supplicant and wpa_cli with 'make' command. You may then install
+the binaries to a suitable system directory, e.g., /usr/local/bin.
+
+Example commands:
+
+# build wpa_supplicant and wpa_cli
+make
+# install binaries (this may need root privileges)
+cp wpa_cli wpa_supplicant /usr/local/bin
+
+
+You will need to make a configuration file, e.g.,
+/etc/wpa_supplicant.conf, with network configuration for the networks
+you are going to use. Configuration file section below includes
+explanation fo the configuration file format and includes various
+examples. Once the configuration is ready, you can test whether the
+configuration work by first running wpa_supplicant with following
+command to start it on foreground with debugging enabled:
+
+wpa_supplicant -iwlan0 -c/etc/wpa_supplicant.conf -d
+
+Assuming everything goes fine, you can start using following command
+to start wpa_supplicant on background without debugging:
+
+wpa_supplicant -iwlan0 -c/etc/wpa_supplicant.conf -B
+
+Please note that if you included more than one driver interface in the
+build time configuration (.config), you may need to specify which
+interface to use by including -D<driver name> option on the command
+line. See following section for more details on command line options
+for wpa_supplicant.
+
+
+
+Command line options
+--------------------
+
+usage:
+ wpa_supplicant [-BddehLqqvw] -i<ifname> -c<config file> [-D<driver>] \
+ [-N -i<ifname> -c<conf> [-D<driver>] ...]
+
+options:
+ -B = run daemon in the background
+ -d = increase debugging verbosity (-dd even more)
+ -K = include keys (passwords, etc.) in debug output
+ -t = include timestamp in debug messages
+ -e = use external IEEE 802.1X Supplicant (e.g., xsupplicant)
+ (this disables the internal Supplicant)
+ -h = show this help text
+ -L = show license (GPL and BSD)
+ -q = decrease debugging verbosity (-qq even less)
+ -v = show version
+ -w = wait for interface to be added, if needed
+ -N = start describing new interface
+
+drivers:
+ hostap = Host AP driver (Intersil Prism2/2.5/3) [default]
+ (this can also be used with Linuxant DriverLoader)
+ prism54 = Prism54.org driver (Intersil Prism GT/Duette/Indigo)
+ not yet fully implemented
+ hermes = Agere Systems Inc. driver (Hermes-I/Hermes-II)
+ madwifi = MADWIFI 802.11 support (Atheros, etc.)
+ atmel = ATMEL AT76C5XXx (USB, PCMCIA)
+ wext = Linux wireless extensions (generic)
+ ndiswrapper = Linux ndiswrapper
+ broadcom = Broadcom wl.o driver
+ ipw = Intel ipw2100/2200 driver
+ bsd = BSD 802.11 support (Atheros, etc.)
+ ndis = Windows NDIS driver
+
+In most common cases, wpa_supplicant is started with
+
+wpa_supplicant -Bw -c/etc/wpa_supplicant.conf -iwlan0
+
+This makes the process fork into background and wait for the wlan0
+interface if it is not available at startup time.
+
+The easiest way to debug problems, and to get debug log for bug
+reports, is to start wpa_supplicant on foreground with debugging
+enabled:
+
+wpa_supplicant -c/etc/wpa_supplicant.conf -iwlan0 -d
+
+
+wpa_supplicant can control multiple interfaces (radios) either by
+running one process for each interface separately or by running just
+one process and list of options at command line. Each interface is
+separated with -N argument. As an example, following command would
+start wpa_supplicant for two interfaces:
+
+wpa_supplicant \
+ -c wpa1.conf -i wlan0 -D hostap -N \
+ -c wpa2.conf -i ath0 -D madwifi
+
+
+Configuration file
+------------------
+
+wpa_supplicant is configured using a text file that lists all accepted
+networks and security policies, including pre-shared keys. See
+example configuration file, wpa_supplicant.conf, for detailed
+information about the configuration format and supported fields.
+
+Changes to configuration file can be reloaded be sending SIGHUP signal
+to wpa_supplicant ('killall -HUP wpa_supplicant'). Similarly,
+reloading can be triggered with 'wpa_cli reconfigure' command.
+
+Configuration file can include one or more network blocks, e.g., one
+for each used SSID. wpa_supplicant will automatically select the best
+betwork based on the order of network blocks in the configuration
+file, network security level (WPA/WPA2 is prefered), and signal
+strength.
+
+Example configuration files for some common configurations:
+
+1) WPA-Personal (PSK) as home network and WPA-Enterprise with EAP-TLS as work
+ network
+
+# allow frontend (e.g., wpa_cli) to be used by all users in 'wheel' group
+ctrl_interface=/var/run/wpa_supplicant
+ctrl_interface_group=wheel
+#
+# home network; allow all valid ciphers
+network={
+ ssid="home"
+ scan_ssid=1
+ key_mgmt=WPA-PSK
+ psk="very secret passphrase"
+}
+#
+# work network; use EAP-TLS with WPA; allow only CCMP and TKIP ciphers
+network={
+ ssid="work"
+ scan_ssid=1
+ key_mgmt=WPA-EAP
+ pairwise=CCMP TKIP
+ group=CCMP TKIP
+ eap=TLS
+ identity="user@example.com"
+ ca_cert="/etc/cert/ca.pem"
+ client_cert="/etc/cert/user.pem"
+ private_key="/etc/cert/user.prv"
+ private_key_passwd="password"
+}
+
+
+2) WPA-RADIUS/EAP-PEAP/MSCHAPv2 with RADIUS servers that use old peaplabel
+ (e.g., Funk Odyssey and SBR, Meetinghouse Aegis, Interlink RAD-Series)
+
+ctrl_interface=/var/run/wpa_supplicant
+ctrl_interface_group=wheel
+network={
+ ssid="example"
+ scan_ssid=1
+ key_mgmt=WPA-EAP
+ eap=PEAP
+ identity="user@example.com"
+ password="foobar"
+ ca_cert="/etc/cert/ca.pem"
+ phase1="peaplabel=0"
+ phase2="auth=MSCHAPV2"
+}
+
+
+3) EAP-TTLS/EAP-MD5-Challenge configuration with anonymous identity for the
+ unencrypted use. Real identity is sent only within an encrypted TLS tunnel.
+
+ctrl_interface=/var/run/wpa_supplicant
+ctrl_interface_group=wheel
+network={
+ ssid="example"
+ scan_ssid=1
+ key_mgmt=WPA-EAP
+ eap=TTLS
+ identity="user@example.com"
+ anonymous_identity="anonymous@example.com"
+ password="foobar"
+ ca_cert="/etc/cert/ca.pem"
+ phase2="auth=MD5"
+}
+
+
+4) IEEE 802.1X (i.e., no WPA) with dynamic WEP keys (require both unicast and
+ broadcast); use EAP-TLS for authentication
+
+ctrl_interface=/var/run/wpa_supplicant
+ctrl_interface_group=wheel
+network={
+ ssid="1x-test"
+ scan_ssid=1
+ key_mgmt=IEEE8021X
+ eap=TLS
+ identity="user@example.com"
+ ca_cert="/etc/cert/ca.pem"
+ client_cert="/etc/cert/user.pem"
+ private_key="/etc/cert/user.prv"
+ private_key_passwd="password"
+ eapol_flags=3
+}
+
+
+5) Catch all example that allows more or less all configuration modes. The
+ configuration options are used based on what security policy is used in the
+ selected SSID. This is mostly for testing and is not recommended for normal
+ use.
+
+ctrl_interface=/var/run/wpa_supplicant
+ctrl_interface_group=wheel
+network={
+ ssid="example"
+ scan_ssid=1
+ key_mgmt=WPA-EAP WPA-PSK IEEE8021X NONE
+ pairwise=CCMP TKIP
+ group=CCMP TKIP WEP104 WEP40
+ psk="very secret passphrase"
+ eap=TTLS PEAP TLS
+ identity="user@example.com"
+ password="foobar"
+ ca_cert="/etc/cert/ca.pem"
+ client_cert="/etc/cert/user.pem"
+ private_key="/etc/cert/user.prv"
+ private_key_passwd="password"
+ phase1="peaplabel=0"
+ ca_cert2="/etc/cert/ca2.pem"
+ client_cert2="/etc/cer/user.pem"
+ private_key2="/etc/cer/user.prv"
+ private_key2_passwd="password"
+}
+
+
+
+Certificates
+------------
+
+Some EAP authentication methods require use of certificates. EAP-TLS
+uses both server side and client certificates whereas EAP-PEAP and
+EAP-TTLS only require the server side certificate. When client
+certificate is used, a matching private key file has to also be
+included in configuration. If the private key uses a passphrase, this
+has to be configured in wpa_supplicant.conf ("private_key_passwd").
+
+wpa_supplicant supports X.509 certificates in PEM and DER
+formats. User certificate and private key can be included in the same
+file.
+
+If the user certificate and private key is received in PKCS#12/PFX
+format, they need to be converted to suitable PEM/DER format for
+wpa_supplicant. This can be done, e.g., with following commands:
+
+# convert client certificate and private key to PEM format
+openssl pkcs12 -in example.pfx -out user.pem -clcerts
+# convert CA certificate (if included in PFX file) to PEM format
+openssl pkcs12 -in example.pfx -out ca.pem -cacerts -nokeys
+
+
+
+wpa_cli
+-------
+
+wpa_cli is a text-based frontend program for interacting with
+wpa_supplicant. It is used to query current status, change
+configuration, trigger events, and request interactive user input.
+
+wpa_cli can show the current authentication status, selected security
+mode, dot11 and dot1x MIBs, etc. In addition, it can configuring some
+variables like EAPOL state machine parameters and trigger events like
+reassociation and IEEE 802.1X logoff/logon. wpa_cli provides a user
+interface to request authentication information, like username and
+password, if these are not included in the configuration. This can be
+used to implement, e.g., one-time-passwords or generic token card
+authentication where the authentication is based on a
+challenge-response that uses an external device for generating the
+response.
+
+The control interface of wpa_supplicant can be configured to allow
+non-root user access (ctrl_interface_group in the configuration
+file). This makes it possible to run wpa_cli with a normal user
+account.
+
+wpa_cli supports two modes: interactive and command line. Both modes
+share the same command set and the main difference is in interactive
+mode providing access to unsolicited messages (event messages,
+username/password requests).
+
+Interactive mode is started when wpa_cli is executed without including
+the command as a command line parameter. Commands are then entered on
+the wpa_cli prompt. In command line mode, the same commands are
+entered as command line arguments for wpa_cli.
+
+
+Interactive authentication parameters request
+
+When wpa_supplicant need authentication parameters, like username and
+password, which are not present in the configuration file, it sends a
+request message to all attached frontend programs, e.g., wpa_cli in
+interactive mode. wpa_cli shows these requests with
+"CTRL-REQ-<type>-<id>:<text>" prefix. <type> is IDENTITY, PASSWORD, or
+OTP (one-time-password). <id> is a unique identifier for the current
+network. <text> is description of the request. In case of OTP request,
+it includes the challenge from the authentication server.
+
+The reply to these requests can be given with 'identity', 'password',
+and 'otp' commands. <id> needs to be copied from the the matching
+request. 'password' and 'otp' commands can be used regardless of
+whether the request was for PASSWORD or OTP. The main difference
+between these two commands is that values given with 'password' are
+remembered as long as wpa_supplicant is running whereas values given
+with 'otp' are used only once and then forgotten, i.e., wpa_supplicant
+will ask frontend for a new value for every use. This can be used to
+implement one-time-password lists and generic token card -based
+authentication.
+
+Example request for password and a matching reply:
+
+CTRL-REQ-PASSWORD-1:Password needed for SSID foobar
+> password 1 mysecretpassword
+
+Example request for generic token card challenge-response:
+
+CTRL-REQ-OTP-2:Challenge 1235663 needed for SSID foobar
+> otp 2 9876
+
+
+wpa_cli commands
+
+ status = get current WPA/EAPOL/EAP status
+ mib = get MIB variables (dot1x, dot11)
+ help = show this usage help
+ interface [ifname] = show interfaces/select interface
+ level <debug level> = change debug level
+ license = show full wpa_cli license
+ logoff = IEEE 802.1X EAPOL state machine logoff
+ logon = IEEE 802.1X EAPOL state machine logon
+ set = set variables (shows list of variables when run without arguments)
+ pmksa = show PMKSA cache
+ reassociate = force reassociation
+ reconfigure = force wpa_supplicant to re-read its configuration file
+ preauthenticate <BSSID> = force preauthentication
+ identity <network id> <identity> = configure identity for an SSID
+ password <network id> <password> = configure password for an SSID
+ otp <network id> <password> = configure one-time-password for an SSID
+ terminate = terminate wpa_supplicant
+ quit = exit wpa_cli
+
+
+
+Integrating with pcmcia-cs/cardmgr scripts
+------------------------------------------
+
+wpa_supplicant needs to be running when using a wireless network with
+WPA. It can be started either from system startup scripts or from
+pcmcia-cs/cardmgr scripts (when using PC Cards). WPA handshake must be
+completed before data frames can be exchanged, so wpa_supplicant
+should be started before DHCP client.
+
+Command line option '-w' can be used if wpa_supplicant is started
+before the wireless LAN interface is present (e.g., before inserting
+the PC Card) or is not yet up.
+
+For example, following small changes to pcmcia-cs scripts can be used
+to enable WPA support:
+
+Add MODE="Managed" and WPA="y" to the network scheme in
+/etc/pcmcia/wireless.opts.
+
+Add the following block to the end of 'start' action handler in
+/etc/pcmcia/wireless:
+
+ if [ "$WPA" = "y" -a -x /usr/local/bin/wpa_supplicant ]; then
+ /usr/local/bin/wpa_supplicant -Bw -c/etc/wpa_supplicant.conf \
+ -i$DEVICE
+ fi
+
+Add the following block to the end of 'stop' action handler (may need
+to be separated from other actions) in /etc/pcmcia/wireless:
+
+ if [ "$WPA" = "y" -a -x /usr/local/bin/wpa_supplicant ]; then
+ killall wpa_supplicant
+ fi
+
+This will make cardmgr start wpa_supplicant when the card is plugged
+in. wpa_supplicant will wait until the interface is set up--either
+when a static IP address is configured or when DHCP client is
+started--and will then negotiate keys with the AP.
+
+
+
+Optional integration with Xsupplicant
+-------------------------------------
+
+wpa_supplicant has an integrated IEEE 802.1X Supplicant that supports
+most commonly used EAP methods. In addition, wpa_supplicant has an
+experimental interface for integrating it with Xsupplicant
+(http://www.open1x.org/) for the WPA with EAP authentication.
+
+When using WPA-EAP, both wpa_supplicant and Xsupplicant must be
+configured with the network security policy. See Xsupplicant documents
+for information about its configuration. Please also note, that a new
+command line option -W (enable WPA) must be used when starting
+xsupplicant.
+
+Example configuration for xsupplicant:
+
+network_list = all
+default_netname = jkm
+
+jkm
+{
+ type = wireless
+ allow_types = eap_peap
+ identity = <BEGIN_ID>jkm<END_ID>
+ eap-peap {
+ random_file = /dev/urandom
+ root_cert = /home/jkm/CA.pem
+ chunk_size = 1398
+ allow_types = eap_mschapv2
+ eap-mschapv2 {
+ username = <BEGIN_UNAME>jkm<END_UNAME>
+ password = <BEGIN_PASS>jkm<END_PASS>
+ }
+ }
+}
+
+
+Example configuration for wpa_supplicant:
+
+network={
+ ssid="jkm"
+ key_mgmt=WPA-EAP
+}
+
+
+Both wpa_supplicant and xsupplicant need to be started. Please remember
+to add '-W' option for xsupplicant in order to provide keying material
+for wpa_supplicant and '-e' option for wpa_supplicant to disable internal
+IEEE 802.1X implementation.
+
+wpa_supplicant -iwlan0 -cwpa_supplicant.conf -e
+xsupplicant -iwlan0 -cxsupplicant.conf -W
diff --git a/contrib/wpa_supplicant/aes.c b/contrib/wpa_supplicant/aes.c
new file mode 100644
index 000000000000..eabebd074653
--- /dev/null
+++ b/contrib/wpa_supplicant/aes.c
@@ -0,0 +1,1132 @@
+/*
+ * Modifications to public domain implementation:
+ * - support only 128-bit keys
+ * - cleanup
+ * Copyright (c) 2003-2004, Jouni Malinen <jkmaline@cc.hut.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+/**
+ * rijndael-alg-fst.c
+ *
+ * @version 3.0 (December 2000)
+ *
+ * Optimised ANSI C code for the Rijndael cipher (now AES)
+ *
+ * @author Vincent Rijmen <vincent.rijmen@esat.kuleuven.ac.be>
+ * @author Antoon Bosselaers <antoon.bosselaers@esat.kuleuven.ac.be>
+ * @author Paulo Barreto <paulo.barreto@terra.com.br>
+ *
+ * This code is hereby placed in the public domain.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''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 AUTHORS 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.
+ */
+
+#define FULL_UNROLL
+
+
+/*
+Te0[x] = S [x].[02, 01, 01, 03];
+Te1[x] = S [x].[03, 02, 01, 01];
+Te2[x] = S [x].[01, 03, 02, 01];
+Te3[x] = S [x].[01, 01, 03, 02];
+Te4[x] = S [x].[01, 01, 01, 01];
+
+Td0[x] = Si[x].[0e, 09, 0d, 0b];
+Td1[x] = Si[x].[0b, 0e, 09, 0d];
+Td2[x] = Si[x].[0d, 0b, 0e, 09];
+Td3[x] = Si[x].[09, 0d, 0b, 0e];
+Td4[x] = Si[x].[01, 01, 01, 01];
+*/
+
+static const u32 Te0[256] = {
+ 0xc66363a5U, 0xf87c7c84U, 0xee777799U, 0xf67b7b8dU,
+ 0xfff2f20dU, 0xd66b6bbdU, 0xde6f6fb1U, 0x91c5c554U,
+ 0x60303050U, 0x02010103U, 0xce6767a9U, 0x562b2b7dU,
+ 0xe7fefe19U, 0xb5d7d762U, 0x4dababe6U, 0xec76769aU,
+ 0x8fcaca45U, 0x1f82829dU, 0x89c9c940U, 0xfa7d7d87U,
+ 0xeffafa15U, 0xb25959ebU, 0x8e4747c9U, 0xfbf0f00bU,
+ 0x41adadecU, 0xb3d4d467U, 0x5fa2a2fdU, 0x45afafeaU,
+ 0x239c9cbfU, 0x53a4a4f7U, 0xe4727296U, 0x9bc0c05bU,
+ 0x75b7b7c2U, 0xe1fdfd1cU, 0x3d9393aeU, 0x4c26266aU,
+ 0x6c36365aU, 0x7e3f3f41U, 0xf5f7f702U, 0x83cccc4fU,
+ 0x6834345cU, 0x51a5a5f4U, 0xd1e5e534U, 0xf9f1f108U,
+ 0xe2717193U, 0xabd8d873U, 0x62313153U, 0x2a15153fU,
+ 0x0804040cU, 0x95c7c752U, 0x46232365U, 0x9dc3c35eU,
+ 0x30181828U, 0x379696a1U, 0x0a05050fU, 0x2f9a9ab5U,
+ 0x0e070709U, 0x24121236U, 0x1b80809bU, 0xdfe2e23dU,
+ 0xcdebeb26U, 0x4e272769U, 0x7fb2b2cdU, 0xea75759fU,
+ 0x1209091bU, 0x1d83839eU, 0x582c2c74U, 0x341a1a2eU,
+ 0x361b1b2dU, 0xdc6e6eb2U, 0xb45a5aeeU, 0x5ba0a0fbU,
+ 0xa45252f6U, 0x763b3b4dU, 0xb7d6d661U, 0x7db3b3ceU,
+ 0x5229297bU, 0xdde3e33eU, 0x5e2f2f71U, 0x13848497U,
+ 0xa65353f5U, 0xb9d1d168U, 0x00000000U, 0xc1eded2cU,
+ 0x40202060U, 0xe3fcfc1fU, 0x79b1b1c8U, 0xb65b5bedU,
+ 0xd46a6abeU, 0x8dcbcb46U, 0x67bebed9U, 0x7239394bU,
+ 0x944a4adeU, 0x984c4cd4U, 0xb05858e8U, 0x85cfcf4aU,
+ 0xbbd0d06bU, 0xc5efef2aU, 0x4faaaae5U, 0xedfbfb16U,
+ 0x864343c5U, 0x9a4d4dd7U, 0x66333355U, 0x11858594U,
+ 0x8a4545cfU, 0xe9f9f910U, 0x04020206U, 0xfe7f7f81U,
+ 0xa05050f0U, 0x783c3c44U, 0x259f9fbaU, 0x4ba8a8e3U,
+ 0xa25151f3U, 0x5da3a3feU, 0x804040c0U, 0x058f8f8aU,
+ 0x3f9292adU, 0x219d9dbcU, 0x70383848U, 0xf1f5f504U,
+ 0x63bcbcdfU, 0x77b6b6c1U, 0xafdada75U, 0x42212163U,
+ 0x20101030U, 0xe5ffff1aU, 0xfdf3f30eU, 0xbfd2d26dU,
+ 0x81cdcd4cU, 0x180c0c14U, 0x26131335U, 0xc3ecec2fU,
+ 0xbe5f5fe1U, 0x359797a2U, 0x884444ccU, 0x2e171739U,
+ 0x93c4c457U, 0x55a7a7f2U, 0xfc7e7e82U, 0x7a3d3d47U,
+ 0xc86464acU, 0xba5d5de7U, 0x3219192bU, 0xe6737395U,
+ 0xc06060a0U, 0x19818198U, 0x9e4f4fd1U, 0xa3dcdc7fU,
+ 0x44222266U, 0x542a2a7eU, 0x3b9090abU, 0x0b888883U,
+ 0x8c4646caU, 0xc7eeee29U, 0x6bb8b8d3U, 0x2814143cU,
+ 0xa7dede79U, 0xbc5e5ee2U, 0x160b0b1dU, 0xaddbdb76U,
+ 0xdbe0e03bU, 0x64323256U, 0x743a3a4eU, 0x140a0a1eU,
+ 0x924949dbU, 0x0c06060aU, 0x4824246cU, 0xb85c5ce4U,
+ 0x9fc2c25dU, 0xbdd3d36eU, 0x43acacefU, 0xc46262a6U,
+ 0x399191a8U, 0x319595a4U, 0xd3e4e437U, 0xf279798bU,
+ 0xd5e7e732U, 0x8bc8c843U, 0x6e373759U, 0xda6d6db7U,
+ 0x018d8d8cU, 0xb1d5d564U, 0x9c4e4ed2U, 0x49a9a9e0U,
+ 0xd86c6cb4U, 0xac5656faU, 0xf3f4f407U, 0xcfeaea25U,
+ 0xca6565afU, 0xf47a7a8eU, 0x47aeaee9U, 0x10080818U,
+ 0x6fbabad5U, 0xf0787888U, 0x4a25256fU, 0x5c2e2e72U,
+ 0x381c1c24U, 0x57a6a6f1U, 0x73b4b4c7U, 0x97c6c651U,
+ 0xcbe8e823U, 0xa1dddd7cU, 0xe874749cU, 0x3e1f1f21U,
+ 0x964b4bddU, 0x61bdbddcU, 0x0d8b8b86U, 0x0f8a8a85U,
+ 0xe0707090U, 0x7c3e3e42U, 0x71b5b5c4U, 0xcc6666aaU,
+ 0x904848d8U, 0x06030305U, 0xf7f6f601U, 0x1c0e0e12U,
+ 0xc26161a3U, 0x6a35355fU, 0xae5757f9U, 0x69b9b9d0U,
+ 0x17868691U, 0x99c1c158U, 0x3a1d1d27U, 0x279e9eb9U,
+ 0xd9e1e138U, 0xebf8f813U, 0x2b9898b3U, 0x22111133U,
+ 0xd26969bbU, 0xa9d9d970U, 0x078e8e89U, 0x339494a7U,
+ 0x2d9b9bb6U, 0x3c1e1e22U, 0x15878792U, 0xc9e9e920U,
+ 0x87cece49U, 0xaa5555ffU, 0x50282878U, 0xa5dfdf7aU,
+ 0x038c8c8fU, 0x59a1a1f8U, 0x09898980U, 0x1a0d0d17U,
+ 0x65bfbfdaU, 0xd7e6e631U, 0x844242c6U, 0xd06868b8U,
+ 0x824141c3U, 0x299999b0U, 0x5a2d2d77U, 0x1e0f0f11U,
+ 0x7bb0b0cbU, 0xa85454fcU, 0x6dbbbbd6U, 0x2c16163aU,
+};
+static const u32 Te1[256] = {
+ 0xa5c66363U, 0x84f87c7cU, 0x99ee7777U, 0x8df67b7bU,
+ 0x0dfff2f2U, 0xbdd66b6bU, 0xb1de6f6fU, 0x5491c5c5U,
+ 0x50603030U, 0x03020101U, 0xa9ce6767U, 0x7d562b2bU,
+ 0x19e7fefeU, 0x62b5d7d7U, 0xe64dababU, 0x9aec7676U,
+ 0x458fcacaU, 0x9d1f8282U, 0x4089c9c9U, 0x87fa7d7dU,
+ 0x15effafaU, 0xebb25959U, 0xc98e4747U, 0x0bfbf0f0U,
+ 0xec41adadU, 0x67b3d4d4U, 0xfd5fa2a2U, 0xea45afafU,
+ 0xbf239c9cU, 0xf753a4a4U, 0x96e47272U, 0x5b9bc0c0U,
+ 0xc275b7b7U, 0x1ce1fdfdU, 0xae3d9393U, 0x6a4c2626U,
+ 0x5a6c3636U, 0x417e3f3fU, 0x02f5f7f7U, 0x4f83ccccU,
+ 0x5c683434U, 0xf451a5a5U, 0x34d1e5e5U, 0x08f9f1f1U,
+ 0x93e27171U, 0x73abd8d8U, 0x53623131U, 0x3f2a1515U,
+ 0x0c080404U, 0x5295c7c7U, 0x65462323U, 0x5e9dc3c3U,
+ 0x28301818U, 0xa1379696U, 0x0f0a0505U, 0xb52f9a9aU,
+ 0x090e0707U, 0x36241212U, 0x9b1b8080U, 0x3ddfe2e2U,
+ 0x26cdebebU, 0x694e2727U, 0xcd7fb2b2U, 0x9fea7575U,
+ 0x1b120909U, 0x9e1d8383U, 0x74582c2cU, 0x2e341a1aU,
+ 0x2d361b1bU, 0xb2dc6e6eU, 0xeeb45a5aU, 0xfb5ba0a0U,
+ 0xf6a45252U, 0x4d763b3bU, 0x61b7d6d6U, 0xce7db3b3U,
+ 0x7b522929U, 0x3edde3e3U, 0x715e2f2fU, 0x97138484U,
+ 0xf5a65353U, 0x68b9d1d1U, 0x00000000U, 0x2cc1ededU,
+ 0x60402020U, 0x1fe3fcfcU, 0xc879b1b1U, 0xedb65b5bU,
+ 0xbed46a6aU, 0x468dcbcbU, 0xd967bebeU, 0x4b723939U,
+ 0xde944a4aU, 0xd4984c4cU, 0xe8b05858U, 0x4a85cfcfU,
+ 0x6bbbd0d0U, 0x2ac5efefU, 0xe54faaaaU, 0x16edfbfbU,
+ 0xc5864343U, 0xd79a4d4dU, 0x55663333U, 0x94118585U,
+ 0xcf8a4545U, 0x10e9f9f9U, 0x06040202U, 0x81fe7f7fU,
+ 0xf0a05050U, 0x44783c3cU, 0xba259f9fU, 0xe34ba8a8U,
+ 0xf3a25151U, 0xfe5da3a3U, 0xc0804040U, 0x8a058f8fU,
+ 0xad3f9292U, 0xbc219d9dU, 0x48703838U, 0x04f1f5f5U,
+ 0xdf63bcbcU, 0xc177b6b6U, 0x75afdadaU, 0x63422121U,
+ 0x30201010U, 0x1ae5ffffU, 0x0efdf3f3U, 0x6dbfd2d2U,
+ 0x4c81cdcdU, 0x14180c0cU, 0x35261313U, 0x2fc3ececU,
+ 0xe1be5f5fU, 0xa2359797U, 0xcc884444U, 0x392e1717U,
+ 0x5793c4c4U, 0xf255a7a7U, 0x82fc7e7eU, 0x477a3d3dU,
+ 0xacc86464U, 0xe7ba5d5dU, 0x2b321919U, 0x95e67373U,
+ 0xa0c06060U, 0x98198181U, 0xd19e4f4fU, 0x7fa3dcdcU,
+ 0x66442222U, 0x7e542a2aU, 0xab3b9090U, 0x830b8888U,
+ 0xca8c4646U, 0x29c7eeeeU, 0xd36bb8b8U, 0x3c281414U,
+ 0x79a7dedeU, 0xe2bc5e5eU, 0x1d160b0bU, 0x76addbdbU,
+ 0x3bdbe0e0U, 0x56643232U, 0x4e743a3aU, 0x1e140a0aU,
+ 0xdb924949U, 0x0a0c0606U, 0x6c482424U, 0xe4b85c5cU,
+ 0x5d9fc2c2U, 0x6ebdd3d3U, 0xef43acacU, 0xa6c46262U,
+ 0xa8399191U, 0xa4319595U, 0x37d3e4e4U, 0x8bf27979U,
+ 0x32d5e7e7U, 0x438bc8c8U, 0x596e3737U, 0xb7da6d6dU,
+ 0x8c018d8dU, 0x64b1d5d5U, 0xd29c4e4eU, 0xe049a9a9U,
+ 0xb4d86c6cU, 0xfaac5656U, 0x07f3f4f4U, 0x25cfeaeaU,
+ 0xafca6565U, 0x8ef47a7aU, 0xe947aeaeU, 0x18100808U,
+ 0xd56fbabaU, 0x88f07878U, 0x6f4a2525U, 0x725c2e2eU,
+ 0x24381c1cU, 0xf157a6a6U, 0xc773b4b4U, 0x5197c6c6U,
+ 0x23cbe8e8U, 0x7ca1ddddU, 0x9ce87474U, 0x213e1f1fU,
+ 0xdd964b4bU, 0xdc61bdbdU, 0x860d8b8bU, 0x850f8a8aU,
+ 0x90e07070U, 0x427c3e3eU, 0xc471b5b5U, 0xaacc6666U,
+ 0xd8904848U, 0x05060303U, 0x01f7f6f6U, 0x121c0e0eU,
+ 0xa3c26161U, 0x5f6a3535U, 0xf9ae5757U, 0xd069b9b9U,
+ 0x91178686U, 0x5899c1c1U, 0x273a1d1dU, 0xb9279e9eU,
+ 0x38d9e1e1U, 0x13ebf8f8U, 0xb32b9898U, 0x33221111U,
+ 0xbbd26969U, 0x70a9d9d9U, 0x89078e8eU, 0xa7339494U,
+ 0xb62d9b9bU, 0x223c1e1eU, 0x92158787U, 0x20c9e9e9U,
+ 0x4987ceceU, 0xffaa5555U, 0x78502828U, 0x7aa5dfdfU,
+ 0x8f038c8cU, 0xf859a1a1U, 0x80098989U, 0x171a0d0dU,
+ 0xda65bfbfU, 0x31d7e6e6U, 0xc6844242U, 0xb8d06868U,
+ 0xc3824141U, 0xb0299999U, 0x775a2d2dU, 0x111e0f0fU,
+ 0xcb7bb0b0U, 0xfca85454U, 0xd66dbbbbU, 0x3a2c1616U,
+};
+static const u32 Te2[256] = {
+ 0x63a5c663U, 0x7c84f87cU, 0x7799ee77U, 0x7b8df67bU,
+ 0xf20dfff2U, 0x6bbdd66bU, 0x6fb1de6fU, 0xc55491c5U,
+ 0x30506030U, 0x01030201U, 0x67a9ce67U, 0x2b7d562bU,
+ 0xfe19e7feU, 0xd762b5d7U, 0xabe64dabU, 0x769aec76U,
+ 0xca458fcaU, 0x829d1f82U, 0xc94089c9U, 0x7d87fa7dU,
+ 0xfa15effaU, 0x59ebb259U, 0x47c98e47U, 0xf00bfbf0U,
+ 0xadec41adU, 0xd467b3d4U, 0xa2fd5fa2U, 0xafea45afU,
+ 0x9cbf239cU, 0xa4f753a4U, 0x7296e472U, 0xc05b9bc0U,
+ 0xb7c275b7U, 0xfd1ce1fdU, 0x93ae3d93U, 0x266a4c26U,
+ 0x365a6c36U, 0x3f417e3fU, 0xf702f5f7U, 0xcc4f83ccU,
+ 0x345c6834U, 0xa5f451a5U, 0xe534d1e5U, 0xf108f9f1U,
+ 0x7193e271U, 0xd873abd8U, 0x31536231U, 0x153f2a15U,
+ 0x040c0804U, 0xc75295c7U, 0x23654623U, 0xc35e9dc3U,
+ 0x18283018U, 0x96a13796U, 0x050f0a05U, 0x9ab52f9aU,
+ 0x07090e07U, 0x12362412U, 0x809b1b80U, 0xe23ddfe2U,
+ 0xeb26cdebU, 0x27694e27U, 0xb2cd7fb2U, 0x759fea75U,
+ 0x091b1209U, 0x839e1d83U, 0x2c74582cU, 0x1a2e341aU,
+ 0x1b2d361bU, 0x6eb2dc6eU, 0x5aeeb45aU, 0xa0fb5ba0U,
+ 0x52f6a452U, 0x3b4d763bU, 0xd661b7d6U, 0xb3ce7db3U,
+ 0x297b5229U, 0xe33edde3U, 0x2f715e2fU, 0x84971384U,
+ 0x53f5a653U, 0xd168b9d1U, 0x00000000U, 0xed2cc1edU,
+ 0x20604020U, 0xfc1fe3fcU, 0xb1c879b1U, 0x5bedb65bU,
+ 0x6abed46aU, 0xcb468dcbU, 0xbed967beU, 0x394b7239U,
+ 0x4ade944aU, 0x4cd4984cU, 0x58e8b058U, 0xcf4a85cfU,
+ 0xd06bbbd0U, 0xef2ac5efU, 0xaae54faaU, 0xfb16edfbU,
+ 0x43c58643U, 0x4dd79a4dU, 0x33556633U, 0x85941185U,
+ 0x45cf8a45U, 0xf910e9f9U, 0x02060402U, 0x7f81fe7fU,
+ 0x50f0a050U, 0x3c44783cU, 0x9fba259fU, 0xa8e34ba8U,
+ 0x51f3a251U, 0xa3fe5da3U, 0x40c08040U, 0x8f8a058fU,
+ 0x92ad3f92U, 0x9dbc219dU, 0x38487038U, 0xf504f1f5U,
+ 0xbcdf63bcU, 0xb6c177b6U, 0xda75afdaU, 0x21634221U,
+ 0x10302010U, 0xff1ae5ffU, 0xf30efdf3U, 0xd26dbfd2U,
+ 0xcd4c81cdU, 0x0c14180cU, 0x13352613U, 0xec2fc3ecU,
+ 0x5fe1be5fU, 0x97a23597U, 0x44cc8844U, 0x17392e17U,
+ 0xc45793c4U, 0xa7f255a7U, 0x7e82fc7eU, 0x3d477a3dU,
+ 0x64acc864U, 0x5de7ba5dU, 0x192b3219U, 0x7395e673U,
+ 0x60a0c060U, 0x81981981U, 0x4fd19e4fU, 0xdc7fa3dcU,
+ 0x22664422U, 0x2a7e542aU, 0x90ab3b90U, 0x88830b88U,
+ 0x46ca8c46U, 0xee29c7eeU, 0xb8d36bb8U, 0x143c2814U,
+ 0xde79a7deU, 0x5ee2bc5eU, 0x0b1d160bU, 0xdb76addbU,
+ 0xe03bdbe0U, 0x32566432U, 0x3a4e743aU, 0x0a1e140aU,
+ 0x49db9249U, 0x060a0c06U, 0x246c4824U, 0x5ce4b85cU,
+ 0xc25d9fc2U, 0xd36ebdd3U, 0xacef43acU, 0x62a6c462U,
+ 0x91a83991U, 0x95a43195U, 0xe437d3e4U, 0x798bf279U,
+ 0xe732d5e7U, 0xc8438bc8U, 0x37596e37U, 0x6db7da6dU,
+ 0x8d8c018dU, 0xd564b1d5U, 0x4ed29c4eU, 0xa9e049a9U,
+ 0x6cb4d86cU, 0x56faac56U, 0xf407f3f4U, 0xea25cfeaU,
+ 0x65afca65U, 0x7a8ef47aU, 0xaee947aeU, 0x08181008U,
+ 0xbad56fbaU, 0x7888f078U, 0x256f4a25U, 0x2e725c2eU,
+ 0x1c24381cU, 0xa6f157a6U, 0xb4c773b4U, 0xc65197c6U,
+ 0xe823cbe8U, 0xdd7ca1ddU, 0x749ce874U, 0x1f213e1fU,
+ 0x4bdd964bU, 0xbddc61bdU, 0x8b860d8bU, 0x8a850f8aU,
+ 0x7090e070U, 0x3e427c3eU, 0xb5c471b5U, 0x66aacc66U,
+ 0x48d89048U, 0x03050603U, 0xf601f7f6U, 0x0e121c0eU,
+ 0x61a3c261U, 0x355f6a35U, 0x57f9ae57U, 0xb9d069b9U,
+ 0x86911786U, 0xc15899c1U, 0x1d273a1dU, 0x9eb9279eU,
+ 0xe138d9e1U, 0xf813ebf8U, 0x98b32b98U, 0x11332211U,
+ 0x69bbd269U, 0xd970a9d9U, 0x8e89078eU, 0x94a73394U,
+ 0x9bb62d9bU, 0x1e223c1eU, 0x87921587U, 0xe920c9e9U,
+ 0xce4987ceU, 0x55ffaa55U, 0x28785028U, 0xdf7aa5dfU,
+ 0x8c8f038cU, 0xa1f859a1U, 0x89800989U, 0x0d171a0dU,
+ 0xbfda65bfU, 0xe631d7e6U, 0x42c68442U, 0x68b8d068U,
+ 0x41c38241U, 0x99b02999U, 0x2d775a2dU, 0x0f111e0fU,
+ 0xb0cb7bb0U, 0x54fca854U, 0xbbd66dbbU, 0x163a2c16U,
+};
+static const u32 Te3[256] = {
+
+ 0x6363a5c6U, 0x7c7c84f8U, 0x777799eeU, 0x7b7b8df6U,
+ 0xf2f20dffU, 0x6b6bbdd6U, 0x6f6fb1deU, 0xc5c55491U,
+ 0x30305060U, 0x01010302U, 0x6767a9ceU, 0x2b2b7d56U,
+ 0xfefe19e7U, 0xd7d762b5U, 0xababe64dU, 0x76769aecU,
+ 0xcaca458fU, 0x82829d1fU, 0xc9c94089U, 0x7d7d87faU,
+ 0xfafa15efU, 0x5959ebb2U, 0x4747c98eU, 0xf0f00bfbU,
+ 0xadadec41U, 0xd4d467b3U, 0xa2a2fd5fU, 0xafafea45U,
+ 0x9c9cbf23U, 0xa4a4f753U, 0x727296e4U, 0xc0c05b9bU,
+ 0xb7b7c275U, 0xfdfd1ce1U, 0x9393ae3dU, 0x26266a4cU,
+ 0x36365a6cU, 0x3f3f417eU, 0xf7f702f5U, 0xcccc4f83U,
+ 0x34345c68U, 0xa5a5f451U, 0xe5e534d1U, 0xf1f108f9U,
+ 0x717193e2U, 0xd8d873abU, 0x31315362U, 0x15153f2aU,
+ 0x04040c08U, 0xc7c75295U, 0x23236546U, 0xc3c35e9dU,
+ 0x18182830U, 0x9696a137U, 0x05050f0aU, 0x9a9ab52fU,
+ 0x0707090eU, 0x12123624U, 0x80809b1bU, 0xe2e23ddfU,
+ 0xebeb26cdU, 0x2727694eU, 0xb2b2cd7fU, 0x75759feaU,
+ 0x09091b12U, 0x83839e1dU, 0x2c2c7458U, 0x1a1a2e34U,
+ 0x1b1b2d36U, 0x6e6eb2dcU, 0x5a5aeeb4U, 0xa0a0fb5bU,
+ 0x5252f6a4U, 0x3b3b4d76U, 0xd6d661b7U, 0xb3b3ce7dU,
+ 0x29297b52U, 0xe3e33eddU, 0x2f2f715eU, 0x84849713U,
+ 0x5353f5a6U, 0xd1d168b9U, 0x00000000U, 0xeded2cc1U,
+ 0x20206040U, 0xfcfc1fe3U, 0xb1b1c879U, 0x5b5bedb6U,
+ 0x6a6abed4U, 0xcbcb468dU, 0xbebed967U, 0x39394b72U,
+ 0x4a4ade94U, 0x4c4cd498U, 0x5858e8b0U, 0xcfcf4a85U,
+ 0xd0d06bbbU, 0xefef2ac5U, 0xaaaae54fU, 0xfbfb16edU,
+ 0x4343c586U, 0x4d4dd79aU, 0x33335566U, 0x85859411U,
+ 0x4545cf8aU, 0xf9f910e9U, 0x02020604U, 0x7f7f81feU,
+ 0x5050f0a0U, 0x3c3c4478U, 0x9f9fba25U, 0xa8a8e34bU,
+ 0x5151f3a2U, 0xa3a3fe5dU, 0x4040c080U, 0x8f8f8a05U,
+ 0x9292ad3fU, 0x9d9dbc21U, 0x38384870U, 0xf5f504f1U,
+ 0xbcbcdf63U, 0xb6b6c177U, 0xdada75afU, 0x21216342U,
+ 0x10103020U, 0xffff1ae5U, 0xf3f30efdU, 0xd2d26dbfU,
+ 0xcdcd4c81U, 0x0c0c1418U, 0x13133526U, 0xecec2fc3U,
+ 0x5f5fe1beU, 0x9797a235U, 0x4444cc88U, 0x1717392eU,
+ 0xc4c45793U, 0xa7a7f255U, 0x7e7e82fcU, 0x3d3d477aU,
+ 0x6464acc8U, 0x5d5de7baU, 0x19192b32U, 0x737395e6U,
+ 0x6060a0c0U, 0x81819819U, 0x4f4fd19eU, 0xdcdc7fa3U,
+ 0x22226644U, 0x2a2a7e54U, 0x9090ab3bU, 0x8888830bU,
+ 0x4646ca8cU, 0xeeee29c7U, 0xb8b8d36bU, 0x14143c28U,
+ 0xdede79a7U, 0x5e5ee2bcU, 0x0b0b1d16U, 0xdbdb76adU,
+ 0xe0e03bdbU, 0x32325664U, 0x3a3a4e74U, 0x0a0a1e14U,
+ 0x4949db92U, 0x06060a0cU, 0x24246c48U, 0x5c5ce4b8U,
+ 0xc2c25d9fU, 0xd3d36ebdU, 0xacacef43U, 0x6262a6c4U,
+ 0x9191a839U, 0x9595a431U, 0xe4e437d3U, 0x79798bf2U,
+ 0xe7e732d5U, 0xc8c8438bU, 0x3737596eU, 0x6d6db7daU,
+ 0x8d8d8c01U, 0xd5d564b1U, 0x4e4ed29cU, 0xa9a9e049U,
+ 0x6c6cb4d8U, 0x5656faacU, 0xf4f407f3U, 0xeaea25cfU,
+ 0x6565afcaU, 0x7a7a8ef4U, 0xaeaee947U, 0x08081810U,
+ 0xbabad56fU, 0x787888f0U, 0x25256f4aU, 0x2e2e725cU,
+ 0x1c1c2438U, 0xa6a6f157U, 0xb4b4c773U, 0xc6c65197U,
+ 0xe8e823cbU, 0xdddd7ca1U, 0x74749ce8U, 0x1f1f213eU,
+ 0x4b4bdd96U, 0xbdbddc61U, 0x8b8b860dU, 0x8a8a850fU,
+ 0x707090e0U, 0x3e3e427cU, 0xb5b5c471U, 0x6666aaccU,
+ 0x4848d890U, 0x03030506U, 0xf6f601f7U, 0x0e0e121cU,
+ 0x6161a3c2U, 0x35355f6aU, 0x5757f9aeU, 0xb9b9d069U,
+ 0x86869117U, 0xc1c15899U, 0x1d1d273aU, 0x9e9eb927U,
+ 0xe1e138d9U, 0xf8f813ebU, 0x9898b32bU, 0x11113322U,
+ 0x6969bbd2U, 0xd9d970a9U, 0x8e8e8907U, 0x9494a733U,
+ 0x9b9bb62dU, 0x1e1e223cU, 0x87879215U, 0xe9e920c9U,
+ 0xcece4987U, 0x5555ffaaU, 0x28287850U, 0xdfdf7aa5U,
+ 0x8c8c8f03U, 0xa1a1f859U, 0x89898009U, 0x0d0d171aU,
+ 0xbfbfda65U, 0xe6e631d7U, 0x4242c684U, 0x6868b8d0U,
+ 0x4141c382U, 0x9999b029U, 0x2d2d775aU, 0x0f0f111eU,
+ 0xb0b0cb7bU, 0x5454fca8U, 0xbbbbd66dU, 0x16163a2cU,
+};
+static const u32 Te4[256] = {
+ 0x63636363U, 0x7c7c7c7cU, 0x77777777U, 0x7b7b7b7bU,
+ 0xf2f2f2f2U, 0x6b6b6b6bU, 0x6f6f6f6fU, 0xc5c5c5c5U,
+ 0x30303030U, 0x01010101U, 0x67676767U, 0x2b2b2b2bU,
+ 0xfefefefeU, 0xd7d7d7d7U, 0xababababU, 0x76767676U,
+ 0xcacacacaU, 0x82828282U, 0xc9c9c9c9U, 0x7d7d7d7dU,
+ 0xfafafafaU, 0x59595959U, 0x47474747U, 0xf0f0f0f0U,
+ 0xadadadadU, 0xd4d4d4d4U, 0xa2a2a2a2U, 0xafafafafU,
+ 0x9c9c9c9cU, 0xa4a4a4a4U, 0x72727272U, 0xc0c0c0c0U,
+ 0xb7b7b7b7U, 0xfdfdfdfdU, 0x93939393U, 0x26262626U,
+ 0x36363636U, 0x3f3f3f3fU, 0xf7f7f7f7U, 0xccccccccU,
+ 0x34343434U, 0xa5a5a5a5U, 0xe5e5e5e5U, 0xf1f1f1f1U,
+ 0x71717171U, 0xd8d8d8d8U, 0x31313131U, 0x15151515U,
+ 0x04040404U, 0xc7c7c7c7U, 0x23232323U, 0xc3c3c3c3U,
+ 0x18181818U, 0x96969696U, 0x05050505U, 0x9a9a9a9aU,
+ 0x07070707U, 0x12121212U, 0x80808080U, 0xe2e2e2e2U,
+ 0xebebebebU, 0x27272727U, 0xb2b2b2b2U, 0x75757575U,
+ 0x09090909U, 0x83838383U, 0x2c2c2c2cU, 0x1a1a1a1aU,
+ 0x1b1b1b1bU, 0x6e6e6e6eU, 0x5a5a5a5aU, 0xa0a0a0a0U,
+ 0x52525252U, 0x3b3b3b3bU, 0xd6d6d6d6U, 0xb3b3b3b3U,
+ 0x29292929U, 0xe3e3e3e3U, 0x2f2f2f2fU, 0x84848484U,
+ 0x53535353U, 0xd1d1d1d1U, 0x00000000U, 0xededededU,
+ 0x20202020U, 0xfcfcfcfcU, 0xb1b1b1b1U, 0x5b5b5b5bU,
+ 0x6a6a6a6aU, 0xcbcbcbcbU, 0xbebebebeU, 0x39393939U,
+ 0x4a4a4a4aU, 0x4c4c4c4cU, 0x58585858U, 0xcfcfcfcfU,
+ 0xd0d0d0d0U, 0xefefefefU, 0xaaaaaaaaU, 0xfbfbfbfbU,
+ 0x43434343U, 0x4d4d4d4dU, 0x33333333U, 0x85858585U,
+ 0x45454545U, 0xf9f9f9f9U, 0x02020202U, 0x7f7f7f7fU,
+ 0x50505050U, 0x3c3c3c3cU, 0x9f9f9f9fU, 0xa8a8a8a8U,
+ 0x51515151U, 0xa3a3a3a3U, 0x40404040U, 0x8f8f8f8fU,
+ 0x92929292U, 0x9d9d9d9dU, 0x38383838U, 0xf5f5f5f5U,
+ 0xbcbcbcbcU, 0xb6b6b6b6U, 0xdadadadaU, 0x21212121U,
+ 0x10101010U, 0xffffffffU, 0xf3f3f3f3U, 0xd2d2d2d2U,
+ 0xcdcdcdcdU, 0x0c0c0c0cU, 0x13131313U, 0xececececU,
+ 0x5f5f5f5fU, 0x97979797U, 0x44444444U, 0x17171717U,
+ 0xc4c4c4c4U, 0xa7a7a7a7U, 0x7e7e7e7eU, 0x3d3d3d3dU,
+ 0x64646464U, 0x5d5d5d5dU, 0x19191919U, 0x73737373U,
+ 0x60606060U, 0x81818181U, 0x4f4f4f4fU, 0xdcdcdcdcU,
+ 0x22222222U, 0x2a2a2a2aU, 0x90909090U, 0x88888888U,
+ 0x46464646U, 0xeeeeeeeeU, 0xb8b8b8b8U, 0x14141414U,
+ 0xdedededeU, 0x5e5e5e5eU, 0x0b0b0b0bU, 0xdbdbdbdbU,
+ 0xe0e0e0e0U, 0x32323232U, 0x3a3a3a3aU, 0x0a0a0a0aU,
+ 0x49494949U, 0x06060606U, 0x24242424U, 0x5c5c5c5cU,
+ 0xc2c2c2c2U, 0xd3d3d3d3U, 0xacacacacU, 0x62626262U,
+ 0x91919191U, 0x95959595U, 0xe4e4e4e4U, 0x79797979U,
+ 0xe7e7e7e7U, 0xc8c8c8c8U, 0x37373737U, 0x6d6d6d6dU,
+ 0x8d8d8d8dU, 0xd5d5d5d5U, 0x4e4e4e4eU, 0xa9a9a9a9U,
+ 0x6c6c6c6cU, 0x56565656U, 0xf4f4f4f4U, 0xeaeaeaeaU,
+ 0x65656565U, 0x7a7a7a7aU, 0xaeaeaeaeU, 0x08080808U,
+ 0xbabababaU, 0x78787878U, 0x25252525U, 0x2e2e2e2eU,
+ 0x1c1c1c1cU, 0xa6a6a6a6U, 0xb4b4b4b4U, 0xc6c6c6c6U,
+ 0xe8e8e8e8U, 0xddddddddU, 0x74747474U, 0x1f1f1f1fU,
+ 0x4b4b4b4bU, 0xbdbdbdbdU, 0x8b8b8b8bU, 0x8a8a8a8aU,
+ 0x70707070U, 0x3e3e3e3eU, 0xb5b5b5b5U, 0x66666666U,
+ 0x48484848U, 0x03030303U, 0xf6f6f6f6U, 0x0e0e0e0eU,
+ 0x61616161U, 0x35353535U, 0x57575757U, 0xb9b9b9b9U,
+ 0x86868686U, 0xc1c1c1c1U, 0x1d1d1d1dU, 0x9e9e9e9eU,
+ 0xe1e1e1e1U, 0xf8f8f8f8U, 0x98989898U, 0x11111111U,
+ 0x69696969U, 0xd9d9d9d9U, 0x8e8e8e8eU, 0x94949494U,
+ 0x9b9b9b9bU, 0x1e1e1e1eU, 0x87878787U, 0xe9e9e9e9U,
+ 0xcecececeU, 0x55555555U, 0x28282828U, 0xdfdfdfdfU,
+ 0x8c8c8c8cU, 0xa1a1a1a1U, 0x89898989U, 0x0d0d0d0dU,
+ 0xbfbfbfbfU, 0xe6e6e6e6U, 0x42424242U, 0x68686868U,
+ 0x41414141U, 0x99999999U, 0x2d2d2d2dU, 0x0f0f0f0fU,
+ 0xb0b0b0b0U, 0x54545454U, 0xbbbbbbbbU, 0x16161616U,
+};
+static const u32 Td0[256] = {
+ 0x51f4a750U, 0x7e416553U, 0x1a17a4c3U, 0x3a275e96U,
+ 0x3bab6bcbU, 0x1f9d45f1U, 0xacfa58abU, 0x4be30393U,
+ 0x2030fa55U, 0xad766df6U, 0x88cc7691U, 0xf5024c25U,
+ 0x4fe5d7fcU, 0xc52acbd7U, 0x26354480U, 0xb562a38fU,
+ 0xdeb15a49U, 0x25ba1b67U, 0x45ea0e98U, 0x5dfec0e1U,
+ 0xc32f7502U, 0x814cf012U, 0x8d4697a3U, 0x6bd3f9c6U,
+ 0x038f5fe7U, 0x15929c95U, 0xbf6d7aebU, 0x955259daU,
+ 0xd4be832dU, 0x587421d3U, 0x49e06929U, 0x8ec9c844U,
+ 0x75c2896aU, 0xf48e7978U, 0x99583e6bU, 0x27b971ddU,
+ 0xbee14fb6U, 0xf088ad17U, 0xc920ac66U, 0x7dce3ab4U,
+ 0x63df4a18U, 0xe51a3182U, 0x97513360U, 0x62537f45U,
+ 0xb16477e0U, 0xbb6bae84U, 0xfe81a01cU, 0xf9082b94U,
+ 0x70486858U, 0x8f45fd19U, 0x94de6c87U, 0x527bf8b7U,
+ 0xab73d323U, 0x724b02e2U, 0xe31f8f57U, 0x6655ab2aU,
+ 0xb2eb2807U, 0x2fb5c203U, 0x86c57b9aU, 0xd33708a5U,
+ 0x302887f2U, 0x23bfa5b2U, 0x02036abaU, 0xed16825cU,
+ 0x8acf1c2bU, 0xa779b492U, 0xf307f2f0U, 0x4e69e2a1U,
+ 0x65daf4cdU, 0x0605bed5U, 0xd134621fU, 0xc4a6fe8aU,
+ 0x342e539dU, 0xa2f355a0U, 0x058ae132U, 0xa4f6eb75U,
+ 0x0b83ec39U, 0x4060efaaU, 0x5e719f06U, 0xbd6e1051U,
+ 0x3e218af9U, 0x96dd063dU, 0xdd3e05aeU, 0x4de6bd46U,
+ 0x91548db5U, 0x71c45d05U, 0x0406d46fU, 0x605015ffU,
+ 0x1998fb24U, 0xd6bde997U, 0x894043ccU, 0x67d99e77U,
+ 0xb0e842bdU, 0x07898b88U, 0xe7195b38U, 0x79c8eedbU,
+ 0xa17c0a47U, 0x7c420fe9U, 0xf8841ec9U, 0x00000000U,
+ 0x09808683U, 0x322bed48U, 0x1e1170acU, 0x6c5a724eU,
+ 0xfd0efffbU, 0x0f853856U, 0x3daed51eU, 0x362d3927U,
+ 0x0a0fd964U, 0x685ca621U, 0x9b5b54d1U, 0x24362e3aU,
+ 0x0c0a67b1U, 0x9357e70fU, 0xb4ee96d2U, 0x1b9b919eU,
+ 0x80c0c54fU, 0x61dc20a2U, 0x5a774b69U, 0x1c121a16U,
+ 0xe293ba0aU, 0xc0a02ae5U, 0x3c22e043U, 0x121b171dU,
+ 0x0e090d0bU, 0xf28bc7adU, 0x2db6a8b9U, 0x141ea9c8U,
+ 0x57f11985U, 0xaf75074cU, 0xee99ddbbU, 0xa37f60fdU,
+ 0xf701269fU, 0x5c72f5bcU, 0x44663bc5U, 0x5bfb7e34U,
+ 0x8b432976U, 0xcb23c6dcU, 0xb6edfc68U, 0xb8e4f163U,
+ 0xd731dccaU, 0x42638510U, 0x13972240U, 0x84c61120U,
+ 0x854a247dU, 0xd2bb3df8U, 0xaef93211U, 0xc729a16dU,
+ 0x1d9e2f4bU, 0xdcb230f3U, 0x0d8652ecU, 0x77c1e3d0U,
+ 0x2bb3166cU, 0xa970b999U, 0x119448faU, 0x47e96422U,
+ 0xa8fc8cc4U, 0xa0f03f1aU, 0x567d2cd8U, 0x223390efU,
+ 0x87494ec7U, 0xd938d1c1U, 0x8ccaa2feU, 0x98d40b36U,
+ 0xa6f581cfU, 0xa57ade28U, 0xdab78e26U, 0x3fadbfa4U,
+ 0x2c3a9de4U, 0x5078920dU, 0x6a5fcc9bU, 0x547e4662U,
+ 0xf68d13c2U, 0x90d8b8e8U, 0x2e39f75eU, 0x82c3aff5U,
+ 0x9f5d80beU, 0x69d0937cU, 0x6fd52da9U, 0xcf2512b3U,
+ 0xc8ac993bU, 0x10187da7U, 0xe89c636eU, 0xdb3bbb7bU,
+ 0xcd267809U, 0x6e5918f4U, 0xec9ab701U, 0x834f9aa8U,
+ 0xe6956e65U, 0xaaffe67eU, 0x21bccf08U, 0xef15e8e6U,
+ 0xbae79bd9U, 0x4a6f36ceU, 0xea9f09d4U, 0x29b07cd6U,
+ 0x31a4b2afU, 0x2a3f2331U, 0xc6a59430U, 0x35a266c0U,
+ 0x744ebc37U, 0xfc82caa6U, 0xe090d0b0U, 0x33a7d815U,
+ 0xf104984aU, 0x41ecdaf7U, 0x7fcd500eU, 0x1791f62fU,
+ 0x764dd68dU, 0x43efb04dU, 0xccaa4d54U, 0xe49604dfU,
+ 0x9ed1b5e3U, 0x4c6a881bU, 0xc12c1fb8U, 0x4665517fU,
+ 0x9d5eea04U, 0x018c355dU, 0xfa877473U, 0xfb0b412eU,
+ 0xb3671d5aU, 0x92dbd252U, 0xe9105633U, 0x6dd64713U,
+ 0x9ad7618cU, 0x37a10c7aU, 0x59f8148eU, 0xeb133c89U,
+ 0xcea927eeU, 0xb761c935U, 0xe11ce5edU, 0x7a47b13cU,
+ 0x9cd2df59U, 0x55f2733fU, 0x1814ce79U, 0x73c737bfU,
+ 0x53f7cdeaU, 0x5ffdaa5bU, 0xdf3d6f14U, 0x7844db86U,
+ 0xcaaff381U, 0xb968c43eU, 0x3824342cU, 0xc2a3405fU,
+ 0x161dc372U, 0xbce2250cU, 0x283c498bU, 0xff0d9541U,
+ 0x39a80171U, 0x080cb3deU, 0xd8b4e49cU, 0x6456c190U,
+ 0x7bcb8461U, 0xd532b670U, 0x486c5c74U, 0xd0b85742U,
+};
+static const u32 Td1[256] = {
+ 0x5051f4a7U, 0x537e4165U, 0xc31a17a4U, 0x963a275eU,
+ 0xcb3bab6bU, 0xf11f9d45U, 0xabacfa58U, 0x934be303U,
+ 0x552030faU, 0xf6ad766dU, 0x9188cc76U, 0x25f5024cU,
+ 0xfc4fe5d7U, 0xd7c52acbU, 0x80263544U, 0x8fb562a3U,
+ 0x49deb15aU, 0x6725ba1bU, 0x9845ea0eU, 0xe15dfec0U,
+ 0x02c32f75U, 0x12814cf0U, 0xa38d4697U, 0xc66bd3f9U,
+ 0xe7038f5fU, 0x9515929cU, 0xebbf6d7aU, 0xda955259U,
+ 0x2dd4be83U, 0xd3587421U, 0x2949e069U, 0x448ec9c8U,
+ 0x6a75c289U, 0x78f48e79U, 0x6b99583eU, 0xdd27b971U,
+ 0xb6bee14fU, 0x17f088adU, 0x66c920acU, 0xb47dce3aU,
+ 0x1863df4aU, 0x82e51a31U, 0x60975133U, 0x4562537fU,
+ 0xe0b16477U, 0x84bb6baeU, 0x1cfe81a0U, 0x94f9082bU,
+ 0x58704868U, 0x198f45fdU, 0x8794de6cU, 0xb7527bf8U,
+ 0x23ab73d3U, 0xe2724b02U, 0x57e31f8fU, 0x2a6655abU,
+ 0x07b2eb28U, 0x032fb5c2U, 0x9a86c57bU, 0xa5d33708U,
+ 0xf2302887U, 0xb223bfa5U, 0xba02036aU, 0x5ced1682U,
+ 0x2b8acf1cU, 0x92a779b4U, 0xf0f307f2U, 0xa14e69e2U,
+ 0xcd65daf4U, 0xd50605beU, 0x1fd13462U, 0x8ac4a6feU,
+ 0x9d342e53U, 0xa0a2f355U, 0x32058ae1U, 0x75a4f6ebU,
+ 0x390b83ecU, 0xaa4060efU, 0x065e719fU, 0x51bd6e10U,
+ 0xf93e218aU, 0x3d96dd06U, 0xaedd3e05U, 0x464de6bdU,
+ 0xb591548dU, 0x0571c45dU, 0x6f0406d4U, 0xff605015U,
+ 0x241998fbU, 0x97d6bde9U, 0xcc894043U, 0x7767d99eU,
+ 0xbdb0e842U, 0x8807898bU, 0x38e7195bU, 0xdb79c8eeU,
+ 0x47a17c0aU, 0xe97c420fU, 0xc9f8841eU, 0x00000000U,
+ 0x83098086U, 0x48322bedU, 0xac1e1170U, 0x4e6c5a72U,
+ 0xfbfd0effU, 0x560f8538U, 0x1e3daed5U, 0x27362d39U,
+ 0x640a0fd9U, 0x21685ca6U, 0xd19b5b54U, 0x3a24362eU,
+ 0xb10c0a67U, 0x0f9357e7U, 0xd2b4ee96U, 0x9e1b9b91U,
+ 0x4f80c0c5U, 0xa261dc20U, 0x695a774bU, 0x161c121aU,
+ 0x0ae293baU, 0xe5c0a02aU, 0x433c22e0U, 0x1d121b17U,
+ 0x0b0e090dU, 0xadf28bc7U, 0xb92db6a8U, 0xc8141ea9U,
+ 0x8557f119U, 0x4caf7507U, 0xbbee99ddU, 0xfda37f60U,
+ 0x9ff70126U, 0xbc5c72f5U, 0xc544663bU, 0x345bfb7eU,
+ 0x768b4329U, 0xdccb23c6U, 0x68b6edfcU, 0x63b8e4f1U,
+ 0xcad731dcU, 0x10426385U, 0x40139722U, 0x2084c611U,
+ 0x7d854a24U, 0xf8d2bb3dU, 0x11aef932U, 0x6dc729a1U,
+ 0x4b1d9e2fU, 0xf3dcb230U, 0xec0d8652U, 0xd077c1e3U,
+ 0x6c2bb316U, 0x99a970b9U, 0xfa119448U, 0x2247e964U,
+ 0xc4a8fc8cU, 0x1aa0f03fU, 0xd8567d2cU, 0xef223390U,
+ 0xc787494eU, 0xc1d938d1U, 0xfe8ccaa2U, 0x3698d40bU,
+ 0xcfa6f581U, 0x28a57adeU, 0x26dab78eU, 0xa43fadbfU,
+ 0xe42c3a9dU, 0x0d507892U, 0x9b6a5fccU, 0x62547e46U,
+ 0xc2f68d13U, 0xe890d8b8U, 0x5e2e39f7U, 0xf582c3afU,
+ 0xbe9f5d80U, 0x7c69d093U, 0xa96fd52dU, 0xb3cf2512U,
+ 0x3bc8ac99U, 0xa710187dU, 0x6ee89c63U, 0x7bdb3bbbU,
+ 0x09cd2678U, 0xf46e5918U, 0x01ec9ab7U, 0xa8834f9aU,
+ 0x65e6956eU, 0x7eaaffe6U, 0x0821bccfU, 0xe6ef15e8U,
+ 0xd9bae79bU, 0xce4a6f36U, 0xd4ea9f09U, 0xd629b07cU,
+ 0xaf31a4b2U, 0x312a3f23U, 0x30c6a594U, 0xc035a266U,
+ 0x37744ebcU, 0xa6fc82caU, 0xb0e090d0U, 0x1533a7d8U,
+ 0x4af10498U, 0xf741ecdaU, 0x0e7fcd50U, 0x2f1791f6U,
+ 0x8d764dd6U, 0x4d43efb0U, 0x54ccaa4dU, 0xdfe49604U,
+ 0xe39ed1b5U, 0x1b4c6a88U, 0xb8c12c1fU, 0x7f466551U,
+ 0x049d5eeaU, 0x5d018c35U, 0x73fa8774U, 0x2efb0b41U,
+ 0x5ab3671dU, 0x5292dbd2U, 0x33e91056U, 0x136dd647U,
+ 0x8c9ad761U, 0x7a37a10cU, 0x8e59f814U, 0x89eb133cU,
+ 0xeecea927U, 0x35b761c9U, 0xede11ce5U, 0x3c7a47b1U,
+ 0x599cd2dfU, 0x3f55f273U, 0x791814ceU, 0xbf73c737U,
+ 0xea53f7cdU, 0x5b5ffdaaU, 0x14df3d6fU, 0x867844dbU,
+ 0x81caaff3U, 0x3eb968c4U, 0x2c382434U, 0x5fc2a340U,
+ 0x72161dc3U, 0x0cbce225U, 0x8b283c49U, 0x41ff0d95U,
+ 0x7139a801U, 0xde080cb3U, 0x9cd8b4e4U, 0x906456c1U,
+ 0x617bcb84U, 0x70d532b6U, 0x74486c5cU, 0x42d0b857U,
+};
+static const u32 Td2[256] = {
+ 0xa75051f4U, 0x65537e41U, 0xa4c31a17U, 0x5e963a27U,
+ 0x6bcb3babU, 0x45f11f9dU, 0x58abacfaU, 0x03934be3U,
+ 0xfa552030U, 0x6df6ad76U, 0x769188ccU, 0x4c25f502U,
+ 0xd7fc4fe5U, 0xcbd7c52aU, 0x44802635U, 0xa38fb562U,
+ 0x5a49deb1U, 0x1b6725baU, 0x0e9845eaU, 0xc0e15dfeU,
+ 0x7502c32fU, 0xf012814cU, 0x97a38d46U, 0xf9c66bd3U,
+ 0x5fe7038fU, 0x9c951592U, 0x7aebbf6dU, 0x59da9552U,
+ 0x832dd4beU, 0x21d35874U, 0x692949e0U, 0xc8448ec9U,
+ 0x896a75c2U, 0x7978f48eU, 0x3e6b9958U, 0x71dd27b9U,
+ 0x4fb6bee1U, 0xad17f088U, 0xac66c920U, 0x3ab47dceU,
+ 0x4a1863dfU, 0x3182e51aU, 0x33609751U, 0x7f456253U,
+ 0x77e0b164U, 0xae84bb6bU, 0xa01cfe81U, 0x2b94f908U,
+ 0x68587048U, 0xfd198f45U, 0x6c8794deU, 0xf8b7527bU,
+ 0xd323ab73U, 0x02e2724bU, 0x8f57e31fU, 0xab2a6655U,
+ 0x2807b2ebU, 0xc2032fb5U, 0x7b9a86c5U, 0x08a5d337U,
+ 0x87f23028U, 0xa5b223bfU, 0x6aba0203U, 0x825ced16U,
+ 0x1c2b8acfU, 0xb492a779U, 0xf2f0f307U, 0xe2a14e69U,
+ 0xf4cd65daU, 0xbed50605U, 0x621fd134U, 0xfe8ac4a6U,
+ 0x539d342eU, 0x55a0a2f3U, 0xe132058aU, 0xeb75a4f6U,
+ 0xec390b83U, 0xefaa4060U, 0x9f065e71U, 0x1051bd6eU,
+
+ 0x8af93e21U, 0x063d96ddU, 0x05aedd3eU, 0xbd464de6U,
+ 0x8db59154U, 0x5d0571c4U, 0xd46f0406U, 0x15ff6050U,
+ 0xfb241998U, 0xe997d6bdU, 0x43cc8940U, 0x9e7767d9U,
+ 0x42bdb0e8U, 0x8b880789U, 0x5b38e719U, 0xeedb79c8U,
+ 0x0a47a17cU, 0x0fe97c42U, 0x1ec9f884U, 0x00000000U,
+ 0x86830980U, 0xed48322bU, 0x70ac1e11U, 0x724e6c5aU,
+ 0xfffbfd0eU, 0x38560f85U, 0xd51e3daeU, 0x3927362dU,
+ 0xd9640a0fU, 0xa621685cU, 0x54d19b5bU, 0x2e3a2436U,
+ 0x67b10c0aU, 0xe70f9357U, 0x96d2b4eeU, 0x919e1b9bU,
+ 0xc54f80c0U, 0x20a261dcU, 0x4b695a77U, 0x1a161c12U,
+ 0xba0ae293U, 0x2ae5c0a0U, 0xe0433c22U, 0x171d121bU,
+ 0x0d0b0e09U, 0xc7adf28bU, 0xa8b92db6U, 0xa9c8141eU,
+ 0x198557f1U, 0x074caf75U, 0xddbbee99U, 0x60fda37fU,
+ 0x269ff701U, 0xf5bc5c72U, 0x3bc54466U, 0x7e345bfbU,
+ 0x29768b43U, 0xc6dccb23U, 0xfc68b6edU, 0xf163b8e4U,
+ 0xdccad731U, 0x85104263U, 0x22401397U, 0x112084c6U,
+ 0x247d854aU, 0x3df8d2bbU, 0x3211aef9U, 0xa16dc729U,
+ 0x2f4b1d9eU, 0x30f3dcb2U, 0x52ec0d86U, 0xe3d077c1U,
+ 0x166c2bb3U, 0xb999a970U, 0x48fa1194U, 0x642247e9U,
+ 0x8cc4a8fcU, 0x3f1aa0f0U, 0x2cd8567dU, 0x90ef2233U,
+ 0x4ec78749U, 0xd1c1d938U, 0xa2fe8ccaU, 0x0b3698d4U,
+ 0x81cfa6f5U, 0xde28a57aU, 0x8e26dab7U, 0xbfa43fadU,
+ 0x9de42c3aU, 0x920d5078U, 0xcc9b6a5fU, 0x4662547eU,
+ 0x13c2f68dU, 0xb8e890d8U, 0xf75e2e39U, 0xaff582c3U,
+ 0x80be9f5dU, 0x937c69d0U, 0x2da96fd5U, 0x12b3cf25U,
+ 0x993bc8acU, 0x7da71018U, 0x636ee89cU, 0xbb7bdb3bU,
+ 0x7809cd26U, 0x18f46e59U, 0xb701ec9aU, 0x9aa8834fU,
+ 0x6e65e695U, 0xe67eaaffU, 0xcf0821bcU, 0xe8e6ef15U,
+ 0x9bd9bae7U, 0x36ce4a6fU, 0x09d4ea9fU, 0x7cd629b0U,
+ 0xb2af31a4U, 0x23312a3fU, 0x9430c6a5U, 0x66c035a2U,
+ 0xbc37744eU, 0xcaa6fc82U, 0xd0b0e090U, 0xd81533a7U,
+ 0x984af104U, 0xdaf741ecU, 0x500e7fcdU, 0xf62f1791U,
+ 0xd68d764dU, 0xb04d43efU, 0x4d54ccaaU, 0x04dfe496U,
+ 0xb5e39ed1U, 0x881b4c6aU, 0x1fb8c12cU, 0x517f4665U,
+ 0xea049d5eU, 0x355d018cU, 0x7473fa87U, 0x412efb0bU,
+ 0x1d5ab367U, 0xd25292dbU, 0x5633e910U, 0x47136dd6U,
+ 0x618c9ad7U, 0x0c7a37a1U, 0x148e59f8U, 0x3c89eb13U,
+ 0x27eecea9U, 0xc935b761U, 0xe5ede11cU, 0xb13c7a47U,
+ 0xdf599cd2U, 0x733f55f2U, 0xce791814U, 0x37bf73c7U,
+ 0xcdea53f7U, 0xaa5b5ffdU, 0x6f14df3dU, 0xdb867844U,
+ 0xf381caafU, 0xc43eb968U, 0x342c3824U, 0x405fc2a3U,
+ 0xc372161dU, 0x250cbce2U, 0x498b283cU, 0x9541ff0dU,
+ 0x017139a8U, 0xb3de080cU, 0xe49cd8b4U, 0xc1906456U,
+ 0x84617bcbU, 0xb670d532U, 0x5c74486cU, 0x5742d0b8U,
+};
+static const u32 Td3[256] = {
+ 0xf4a75051U, 0x4165537eU, 0x17a4c31aU, 0x275e963aU,
+ 0xab6bcb3bU, 0x9d45f11fU, 0xfa58abacU, 0xe303934bU,
+ 0x30fa5520U, 0x766df6adU, 0xcc769188U, 0x024c25f5U,
+ 0xe5d7fc4fU, 0x2acbd7c5U, 0x35448026U, 0x62a38fb5U,
+ 0xb15a49deU, 0xba1b6725U, 0xea0e9845U, 0xfec0e15dU,
+ 0x2f7502c3U, 0x4cf01281U, 0x4697a38dU, 0xd3f9c66bU,
+ 0x8f5fe703U, 0x929c9515U, 0x6d7aebbfU, 0x5259da95U,
+ 0xbe832dd4U, 0x7421d358U, 0xe0692949U, 0xc9c8448eU,
+ 0xc2896a75U, 0x8e7978f4U, 0x583e6b99U, 0xb971dd27U,
+ 0xe14fb6beU, 0x88ad17f0U, 0x20ac66c9U, 0xce3ab47dU,
+ 0xdf4a1863U, 0x1a3182e5U, 0x51336097U, 0x537f4562U,
+ 0x6477e0b1U, 0x6bae84bbU, 0x81a01cfeU, 0x082b94f9U,
+ 0x48685870U, 0x45fd198fU, 0xde6c8794U, 0x7bf8b752U,
+ 0x73d323abU, 0x4b02e272U, 0x1f8f57e3U, 0x55ab2a66U,
+ 0xeb2807b2U, 0xb5c2032fU, 0xc57b9a86U, 0x3708a5d3U,
+ 0x2887f230U, 0xbfa5b223U, 0x036aba02U, 0x16825cedU,
+ 0xcf1c2b8aU, 0x79b492a7U, 0x07f2f0f3U, 0x69e2a14eU,
+ 0xdaf4cd65U, 0x05bed506U, 0x34621fd1U, 0xa6fe8ac4U,
+ 0x2e539d34U, 0xf355a0a2U, 0x8ae13205U, 0xf6eb75a4U,
+ 0x83ec390bU, 0x60efaa40U, 0x719f065eU, 0x6e1051bdU,
+ 0x218af93eU, 0xdd063d96U, 0x3e05aeddU, 0xe6bd464dU,
+ 0x548db591U, 0xc45d0571U, 0x06d46f04U, 0x5015ff60U,
+ 0x98fb2419U, 0xbde997d6U, 0x4043cc89U, 0xd99e7767U,
+ 0xe842bdb0U, 0x898b8807U, 0x195b38e7U, 0xc8eedb79U,
+ 0x7c0a47a1U, 0x420fe97cU, 0x841ec9f8U, 0x00000000U,
+ 0x80868309U, 0x2bed4832U, 0x1170ac1eU, 0x5a724e6cU,
+ 0x0efffbfdU, 0x8538560fU, 0xaed51e3dU, 0x2d392736U,
+ 0x0fd9640aU, 0x5ca62168U, 0x5b54d19bU, 0x362e3a24U,
+ 0x0a67b10cU, 0x57e70f93U, 0xee96d2b4U, 0x9b919e1bU,
+ 0xc0c54f80U, 0xdc20a261U, 0x774b695aU, 0x121a161cU,
+ 0x93ba0ae2U, 0xa02ae5c0U, 0x22e0433cU, 0x1b171d12U,
+ 0x090d0b0eU, 0x8bc7adf2U, 0xb6a8b92dU, 0x1ea9c814U,
+ 0xf1198557U, 0x75074cafU, 0x99ddbbeeU, 0x7f60fda3U,
+ 0x01269ff7U, 0x72f5bc5cU, 0x663bc544U, 0xfb7e345bU,
+ 0x4329768bU, 0x23c6dccbU, 0xedfc68b6U, 0xe4f163b8U,
+ 0x31dccad7U, 0x63851042U, 0x97224013U, 0xc6112084U,
+ 0x4a247d85U, 0xbb3df8d2U, 0xf93211aeU, 0x29a16dc7U,
+ 0x9e2f4b1dU, 0xb230f3dcU, 0x8652ec0dU, 0xc1e3d077U,
+ 0xb3166c2bU, 0x70b999a9U, 0x9448fa11U, 0xe9642247U,
+ 0xfc8cc4a8U, 0xf03f1aa0U, 0x7d2cd856U, 0x3390ef22U,
+ 0x494ec787U, 0x38d1c1d9U, 0xcaa2fe8cU, 0xd40b3698U,
+ 0xf581cfa6U, 0x7ade28a5U, 0xb78e26daU, 0xadbfa43fU,
+ 0x3a9de42cU, 0x78920d50U, 0x5fcc9b6aU, 0x7e466254U,
+ 0x8d13c2f6U, 0xd8b8e890U, 0x39f75e2eU, 0xc3aff582U,
+ 0x5d80be9fU, 0xd0937c69U, 0xd52da96fU, 0x2512b3cfU,
+ 0xac993bc8U, 0x187da710U, 0x9c636ee8U, 0x3bbb7bdbU,
+ 0x267809cdU, 0x5918f46eU, 0x9ab701ecU, 0x4f9aa883U,
+ 0x956e65e6U, 0xffe67eaaU, 0xbccf0821U, 0x15e8e6efU,
+ 0xe79bd9baU, 0x6f36ce4aU, 0x9f09d4eaU, 0xb07cd629U,
+ 0xa4b2af31U, 0x3f23312aU, 0xa59430c6U, 0xa266c035U,
+ 0x4ebc3774U, 0x82caa6fcU, 0x90d0b0e0U, 0xa7d81533U,
+ 0x04984af1U, 0xecdaf741U, 0xcd500e7fU, 0x91f62f17U,
+ 0x4dd68d76U, 0xefb04d43U, 0xaa4d54ccU, 0x9604dfe4U,
+ 0xd1b5e39eU, 0x6a881b4cU, 0x2c1fb8c1U, 0x65517f46U,
+ 0x5eea049dU, 0x8c355d01U, 0x877473faU, 0x0b412efbU,
+ 0x671d5ab3U, 0xdbd25292U, 0x105633e9U, 0xd647136dU,
+ 0xd7618c9aU, 0xa10c7a37U, 0xf8148e59U, 0x133c89ebU,
+ 0xa927eeceU, 0x61c935b7U, 0x1ce5ede1U, 0x47b13c7aU,
+ 0xd2df599cU, 0xf2733f55U, 0x14ce7918U, 0xc737bf73U,
+ 0xf7cdea53U, 0xfdaa5b5fU, 0x3d6f14dfU, 0x44db8678U,
+ 0xaff381caU, 0x68c43eb9U, 0x24342c38U, 0xa3405fc2U,
+ 0x1dc37216U, 0xe2250cbcU, 0x3c498b28U, 0x0d9541ffU,
+ 0xa8017139U, 0x0cb3de08U, 0xb4e49cd8U, 0x56c19064U,
+ 0xcb84617bU, 0x32b670d5U, 0x6c5c7448U, 0xb85742d0U,
+};
+static const u32 Td4[256] = {
+ 0x52525252U, 0x09090909U, 0x6a6a6a6aU, 0xd5d5d5d5U,
+ 0x30303030U, 0x36363636U, 0xa5a5a5a5U, 0x38383838U,
+ 0xbfbfbfbfU, 0x40404040U, 0xa3a3a3a3U, 0x9e9e9e9eU,
+ 0x81818181U, 0xf3f3f3f3U, 0xd7d7d7d7U, 0xfbfbfbfbU,
+ 0x7c7c7c7cU, 0xe3e3e3e3U, 0x39393939U, 0x82828282U,
+ 0x9b9b9b9bU, 0x2f2f2f2fU, 0xffffffffU, 0x87878787U,
+ 0x34343434U, 0x8e8e8e8eU, 0x43434343U, 0x44444444U,
+ 0xc4c4c4c4U, 0xdedededeU, 0xe9e9e9e9U, 0xcbcbcbcbU,
+ 0x54545454U, 0x7b7b7b7bU, 0x94949494U, 0x32323232U,
+ 0xa6a6a6a6U, 0xc2c2c2c2U, 0x23232323U, 0x3d3d3d3dU,
+ 0xeeeeeeeeU, 0x4c4c4c4cU, 0x95959595U, 0x0b0b0b0bU,
+ 0x42424242U, 0xfafafafaU, 0xc3c3c3c3U, 0x4e4e4e4eU,
+ 0x08080808U, 0x2e2e2e2eU, 0xa1a1a1a1U, 0x66666666U,
+ 0x28282828U, 0xd9d9d9d9U, 0x24242424U, 0xb2b2b2b2U,
+ 0x76767676U, 0x5b5b5b5bU, 0xa2a2a2a2U, 0x49494949U,
+ 0x6d6d6d6dU, 0x8b8b8b8bU, 0xd1d1d1d1U, 0x25252525U,
+ 0x72727272U, 0xf8f8f8f8U, 0xf6f6f6f6U, 0x64646464U,
+ 0x86868686U, 0x68686868U, 0x98989898U, 0x16161616U,
+ 0xd4d4d4d4U, 0xa4a4a4a4U, 0x5c5c5c5cU, 0xccccccccU,
+ 0x5d5d5d5dU, 0x65656565U, 0xb6b6b6b6U, 0x92929292U,
+ 0x6c6c6c6cU, 0x70707070U, 0x48484848U, 0x50505050U,
+ 0xfdfdfdfdU, 0xededededU, 0xb9b9b9b9U, 0xdadadadaU,
+ 0x5e5e5e5eU, 0x15151515U, 0x46464646U, 0x57575757U,
+ 0xa7a7a7a7U, 0x8d8d8d8dU, 0x9d9d9d9dU, 0x84848484U,
+ 0x90909090U, 0xd8d8d8d8U, 0xababababU, 0x00000000U,
+ 0x8c8c8c8cU, 0xbcbcbcbcU, 0xd3d3d3d3U, 0x0a0a0a0aU,
+ 0xf7f7f7f7U, 0xe4e4e4e4U, 0x58585858U, 0x05050505U,
+ 0xb8b8b8b8U, 0xb3b3b3b3U, 0x45454545U, 0x06060606U,
+ 0xd0d0d0d0U, 0x2c2c2c2cU, 0x1e1e1e1eU, 0x8f8f8f8fU,
+ 0xcacacacaU, 0x3f3f3f3fU, 0x0f0f0f0fU, 0x02020202U,
+ 0xc1c1c1c1U, 0xafafafafU, 0xbdbdbdbdU, 0x03030303U,
+ 0x01010101U, 0x13131313U, 0x8a8a8a8aU, 0x6b6b6b6bU,
+ 0x3a3a3a3aU, 0x91919191U, 0x11111111U, 0x41414141U,
+ 0x4f4f4f4fU, 0x67676767U, 0xdcdcdcdcU, 0xeaeaeaeaU,
+ 0x97979797U, 0xf2f2f2f2U, 0xcfcfcfcfU, 0xcecececeU,
+ 0xf0f0f0f0U, 0xb4b4b4b4U, 0xe6e6e6e6U, 0x73737373U,
+ 0x96969696U, 0xacacacacU, 0x74747474U, 0x22222222U,
+ 0xe7e7e7e7U, 0xadadadadU, 0x35353535U, 0x85858585U,
+ 0xe2e2e2e2U, 0xf9f9f9f9U, 0x37373737U, 0xe8e8e8e8U,
+ 0x1c1c1c1cU, 0x75757575U, 0xdfdfdfdfU, 0x6e6e6e6eU,
+ 0x47474747U, 0xf1f1f1f1U, 0x1a1a1a1aU, 0x71717171U,
+ 0x1d1d1d1dU, 0x29292929U, 0xc5c5c5c5U, 0x89898989U,
+ 0x6f6f6f6fU, 0xb7b7b7b7U, 0x62626262U, 0x0e0e0e0eU,
+ 0xaaaaaaaaU, 0x18181818U, 0xbebebebeU, 0x1b1b1b1bU,
+ 0xfcfcfcfcU, 0x56565656U, 0x3e3e3e3eU, 0x4b4b4b4bU,
+ 0xc6c6c6c6U, 0xd2d2d2d2U, 0x79797979U, 0x20202020U,
+ 0x9a9a9a9aU, 0xdbdbdbdbU, 0xc0c0c0c0U, 0xfefefefeU,
+ 0x78787878U, 0xcdcdcdcdU, 0x5a5a5a5aU, 0xf4f4f4f4U,
+ 0x1f1f1f1fU, 0xddddddddU, 0xa8a8a8a8U, 0x33333333U,
+ 0x88888888U, 0x07070707U, 0xc7c7c7c7U, 0x31313131U,
+ 0xb1b1b1b1U, 0x12121212U, 0x10101010U, 0x59595959U,
+ 0x27272727U, 0x80808080U, 0xececececU, 0x5f5f5f5fU,
+ 0x60606060U, 0x51515151U, 0x7f7f7f7fU, 0xa9a9a9a9U,
+ 0x19191919U, 0xb5b5b5b5U, 0x4a4a4a4aU, 0x0d0d0d0dU,
+ 0x2d2d2d2dU, 0xe5e5e5e5U, 0x7a7a7a7aU, 0x9f9f9f9fU,
+ 0x93939393U, 0xc9c9c9c9U, 0x9c9c9c9cU, 0xefefefefU,
+ 0xa0a0a0a0U, 0xe0e0e0e0U, 0x3b3b3b3bU, 0x4d4d4d4dU,
+ 0xaeaeaeaeU, 0x2a2a2a2aU, 0xf5f5f5f5U, 0xb0b0b0b0U,
+ 0xc8c8c8c8U, 0xebebebebU, 0xbbbbbbbbU, 0x3c3c3c3cU,
+ 0x83838383U, 0x53535353U, 0x99999999U, 0x61616161U,
+ 0x17171717U, 0x2b2b2b2bU, 0x04040404U, 0x7e7e7e7eU,
+ 0xbabababaU, 0x77777777U, 0xd6d6d6d6U, 0x26262626U,
+ 0xe1e1e1e1U, 0x69696969U, 0x14141414U, 0x63636363U,
+ 0x55555555U, 0x21212121U, 0x0c0c0c0cU, 0x7d7d7d7dU,
+};
+static const u32 rcon[] = {
+ 0x01000000, 0x02000000, 0x04000000, 0x08000000,
+ 0x10000000, 0x20000000, 0x40000000, 0x80000000,
+ 0x1B000000, 0x36000000, /* for 128-bit blocks, Rijndael never uses more than 10 rcon values */
+};
+
+#define SWAP(x) (_lrotl(x, 8) & 0x00ff00ff | _lrotr(x, 8) & 0xff00ff00)
+
+#ifdef _MSC_VER
+#define GETU32(p) SWAP(*((u32 *)(p)))
+#define PUTU32(ct, st) { *((u32 *)(ct)) = SWAP((st)); }
+#else
+#define GETU32(pt) (((u32)(pt)[0] << 24) ^ ((u32)(pt)[1] << 16) ^ \
+((u32)(pt)[2] << 8) ^ ((u32)(pt)[3]))
+#define PUTU32(ct, st) { \
+(ct)[0] = (u8)((st) >> 24); (ct)[1] = (u8)((st) >> 16); \
+(ct)[2] = (u8)((st) >> 8); (ct)[3] = (u8)(st); }
+#endif
+
+/**
+ * Expand the cipher key into the encryption key schedule.
+ *
+ * @return the number of rounds for the given cipher key size.
+ */
+void rijndaelKeySetupEnc(u32 rk[/*44*/], const u8 cipherKey[])
+{
+ int i;
+ u32 temp;
+
+ rk[0] = GETU32(cipherKey );
+ 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] ^
+ (Te4[(temp >> 16) & 0xff] & 0xff000000) ^
+ (Te4[(temp >> 8) & 0xff] & 0x00ff0000) ^
+ (Te4[(temp ) & 0xff] & 0x0000ff00) ^
+ (Te4[(temp >> 24) ] & 0x000000ff) ^
+ rcon[i];
+ rk[5] = rk[1] ^ rk[4];
+ rk[6] = rk[2] ^ rk[5];
+ rk[7] = rk[3] ^ rk[6];
+ rk += 4;
+ }
+}
+
+/**
+ * Expand the cipher key into the decryption key schedule.
+ *
+ * @return the number of rounds for the given cipher key size.
+ */
+void rijndaelKeySetupDec(u32 rk[/*44*/], const u8 cipherKey[])
+{
+ int Nr = 10, i, j;
+ u32 temp;
+
+ /* expand the cipher key: */
+ rijndaelKeySetupEnc(rk, cipherKey);
+ /* 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;
+ temp = rk[i + 1]; rk[i + 1] = rk[j + 1]; rk[j + 1] = temp;
+ temp = rk[i + 2]; rk[i + 2] = rk[j + 2]; rk[j + 2] = temp;
+ temp = rk[i + 3]; rk[i + 3] = rk[j + 3]; rk[j + 3] = temp;
+ }
+ /* apply the inverse MixColumn transform to all round keys but the
+ * first and the last: */
+ for (i = 1; i < Nr; i++) {
+ rk += 4;
+ rk[0] =
+ Td0[Te4[(rk[0] >> 24) ] & 0xff] ^
+ Td1[Te4[(rk[0] >> 16) & 0xff] & 0xff] ^
+ Td2[Te4[(rk[0] >> 8) & 0xff] & 0xff] ^
+ Td3[Te4[(rk[0] ) & 0xff] & 0xff];
+ rk[1] =
+ Td0[Te4[(rk[1] >> 24) ] & 0xff] ^
+ Td1[Te4[(rk[1] >> 16) & 0xff] & 0xff] ^
+ Td2[Te4[(rk[1] >> 8) & 0xff] & 0xff] ^
+ Td3[Te4[(rk[1] ) & 0xff] & 0xff];
+ rk[2] =
+ Td0[Te4[(rk[2] >> 24) ] & 0xff] ^
+ Td1[Te4[(rk[2] >> 16) & 0xff] & 0xff] ^
+ Td2[Te4[(rk[2] >> 8) & 0xff] & 0xff] ^
+ Td3[Te4[(rk[2] ) & 0xff] & 0xff];
+ rk[3] =
+ Td0[Te4[(rk[3] >> 24) ] & 0xff] ^
+ Td1[Te4[(rk[3] >> 16) & 0xff] & 0xff] ^
+ Td2[Te4[(rk[3] >> 8) & 0xff] & 0xff] ^
+ Td3[Te4[(rk[3] ) & 0xff] & 0xff];
+ }
+}
+
+void rijndaelEncrypt(const u32 rk[/*44*/], const u8 pt[16], u8 ct[16])
+{
+ u32 s0, s1, s2, s3, t0, t1, t2, t3;
+ int Nr = 10;
+#ifndef FULL_UNROLL
+ int r;
+#endif /* ?FULL_UNROLL */
+
+ /*
+ * map byte array block to cipher state
+ * and add initial round key:
+ */
+ s0 = GETU32(pt ) ^ rk[0];
+ s1 = GETU32(pt + 4) ^ rk[1];
+ s2 = GETU32(pt + 8) ^ rk[2];
+ s3 = GETU32(pt + 12) ^ rk[3];
+#ifdef FULL_UNROLL
+ /* round 1: */
+ t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[ 4];
+ t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[ 5];
+ t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[ 6];
+ t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[ 7];
+ /* round 2: */
+ s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[ 8];
+ s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[ 9];
+ s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[10];
+ s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[11];
+ /* round 3: */
+ t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[12];
+ t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[13];
+ t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[14];
+ t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[15];
+ /* round 4: */
+ s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[16];
+ s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[17];
+ s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[18];
+ s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[19];
+ /* round 5: */
+ t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[20];
+ t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[21];
+ t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[22];
+ t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[23];
+ /* round 6: */
+ s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[24];
+ s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[25];
+ s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[26];
+ s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[27];
+ /* round 7: */
+ t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[28];
+ t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[29];
+ t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[30];
+ t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[31];
+ /* round 8: */
+ s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[32];
+ s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[33];
+ s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[34];
+ s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[35];
+ /* round 9: */
+ t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[36];
+ t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[37];
+ t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[38];
+ t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[39];
+ rk += Nr << 2;
+#else /* !FULL_UNROLL */
+ /*
+ * Nr - 1 full rounds:
+ */
+ r = Nr >> 1;
+ for (;;) {
+ t0 =
+ Te0[(s0 >> 24) ] ^
+ Te1[(s1 >> 16) & 0xff] ^
+ Te2[(s2 >> 8) & 0xff] ^
+ Te3[(s3 ) & 0xff] ^
+ rk[4];
+ t1 =
+ Te0[(s1 >> 24) ] ^
+ Te1[(s2 >> 16) & 0xff] ^
+ Te2[(s3 >> 8) & 0xff] ^
+ Te3[(s0 ) & 0xff] ^
+ rk[5];
+ t2 =
+ Te0[(s2 >> 24) ] ^
+ Te1[(s3 >> 16) & 0xff] ^
+ Te2[(s0 >> 8) & 0xff] ^
+ Te3[(s1 ) & 0xff] ^
+ rk[6];
+ t3 =
+ Te0[(s3 >> 24) ] ^
+ Te1[(s0 >> 16) & 0xff] ^
+ Te2[(s1 >> 8) & 0xff] ^
+ Te3[(s2 ) & 0xff] ^
+ rk[7];
+
+ rk += 8;
+ if (--r == 0) {
+ break;
+ }
+
+ s0 =
+ Te0[(t0 >> 24) ] ^
+ Te1[(t1 >> 16) & 0xff] ^
+ Te2[(t2 >> 8) & 0xff] ^
+ Te3[(t3 ) & 0xff] ^
+ rk[0];
+ s1 =
+ Te0[(t1 >> 24) ] ^
+ Te1[(t2 >> 16) & 0xff] ^
+ Te2[(t3 >> 8) & 0xff] ^
+ Te3[(t0 ) & 0xff] ^
+ rk[1];
+ s2 =
+ Te0[(t2 >> 24) ] ^
+ Te1[(t3 >> 16) & 0xff] ^
+ Te2[(t0 >> 8) & 0xff] ^
+ Te3[(t1 ) & 0xff] ^
+ rk[2];
+ s3 =
+ Te0[(t3 >> 24) ] ^
+ Te1[(t0 >> 16) & 0xff] ^
+ Te2[(t1 >> 8) & 0xff] ^
+ Te3[(t2 ) & 0xff] ^
+ rk[3];
+ }
+#endif /* ?FULL_UNROLL */
+ /*
+ * apply last round and
+ * map cipher state to byte array block:
+ */
+ s0 =
+ (Te4[(t0 >> 24) ] & 0xff000000) ^
+ (Te4[(t1 >> 16) & 0xff] & 0x00ff0000) ^
+ (Te4[(t2 >> 8) & 0xff] & 0x0000ff00) ^
+ (Te4[(t3 ) & 0xff] & 0x000000ff) ^
+ rk[0];
+ PUTU32(ct , s0);
+ s1 =
+ (Te4[(t1 >> 24) ] & 0xff000000) ^
+ (Te4[(t2 >> 16) & 0xff] & 0x00ff0000) ^
+ (Te4[(t3 >> 8) & 0xff] & 0x0000ff00) ^
+ (Te4[(t0 ) & 0xff] & 0x000000ff) ^
+ rk[1];
+ PUTU32(ct + 4, s1);
+ s2 =
+ (Te4[(t2 >> 24) ] & 0xff000000) ^
+ (Te4[(t3 >> 16) & 0xff] & 0x00ff0000) ^
+ (Te4[(t0 >> 8) & 0xff] & 0x0000ff00) ^
+ (Te4[(t1 ) & 0xff] & 0x000000ff) ^
+ rk[2];
+ PUTU32(ct + 8, s2);
+ s3 =
+ (Te4[(t3 >> 24) ] & 0xff000000) ^
+ (Te4[(t0 >> 16) & 0xff] & 0x00ff0000) ^
+ (Te4[(t1 >> 8) & 0xff] & 0x0000ff00) ^
+ (Te4[(t2 ) & 0xff] & 0x000000ff) ^
+ rk[3];
+ PUTU32(ct + 12, s3);
+}
+
+void rijndaelDecrypt(const u32 rk[/*44*/], const u8 ct[16], u8 pt[16])
+{
+ u32 s0, s1, s2, s3, t0, t1, t2, t3;
+ int Nr = 10;
+#ifndef FULL_UNROLL
+ int r;
+#endif /* ?FULL_UNROLL */
+
+ /*
+ * map byte array block to cipher state
+ * and add initial round key:
+ */
+ s0 = GETU32(ct ) ^ rk[0];
+ s1 = GETU32(ct + 4) ^ rk[1];
+ s2 = GETU32(ct + 8) ^ rk[2];
+ s3 = GETU32(ct + 12) ^ rk[3];
+#ifdef FULL_UNROLL
+ /* round 1: */
+ t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[ 4];
+ t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[ 5];
+ t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[ 6];
+ t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[ 7];
+ /* round 2: */
+ s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[ 8];
+ s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[ 9];
+ s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[10];
+ s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[11];
+ /* round 3: */
+ t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[12];
+ t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[13];
+ t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[14];
+ t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[15];
+ /* round 4: */
+ s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[16];
+ s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[17];
+ s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[18];
+ s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[19];
+ /* round 5: */
+ t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[20];
+ t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[21];
+ t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[22];
+ t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[23];
+ /* round 6: */
+ s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[24];
+ s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[25];
+ s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[26];
+ s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[27];
+ /* round 7: */
+ t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[28];
+ t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[29];
+ t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[30];
+ t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[31];
+ /* round 8: */
+ s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[32];
+ s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[33];
+ s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[34];
+ s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[35];
+ /* round 9: */
+ t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[36];
+ t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[37];
+ t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[38];
+ t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[39];
+ rk += Nr << 2;
+#else /* !FULL_UNROLL */
+ /*
+ * Nr - 1 full rounds:
+ */
+ r = Nr >> 1;
+ for (;;) {
+ t0 =
+ Td0[(s0 >> 24) ] ^
+ Td1[(s3 >> 16) & 0xff] ^
+ Td2[(s2 >> 8) & 0xff] ^
+ Td3[(s1 ) & 0xff] ^
+ rk[4];
+ t1 =
+ Td0[(s1 >> 24) ] ^
+ Td1[(s0 >> 16) & 0xff] ^
+ Td2[(s3 >> 8) & 0xff] ^
+ Td3[(s2 ) & 0xff] ^
+ rk[5];
+ t2 =
+ Td0[(s2 >> 24) ] ^
+ Td1[(s1 >> 16) & 0xff] ^
+ Td2[(s0 >> 8) & 0xff] ^
+ Td3[(s3 ) & 0xff] ^
+ rk[6];
+ t3 =
+ Td0[(s3 >> 24) ] ^
+ Td1[(s2 >> 16) & 0xff] ^
+ Td2[(s1 >> 8) & 0xff] ^
+ Td3[(s0 ) & 0xff] ^
+ rk[7];
+
+ rk += 8;
+ if (--r == 0) {
+ break;
+ }
+
+ s0 =
+ Td0[(t0 >> 24) ] ^
+ Td1[(t3 >> 16) & 0xff] ^
+ Td2[(t2 >> 8) & 0xff] ^
+ Td3[(t1 ) & 0xff] ^
+ rk[0];
+ s1 =
+ Td0[(t1 >> 24) ] ^
+ Td1[(t0 >> 16) & 0xff] ^
+ Td2[(t3 >> 8) & 0xff] ^
+ Td3[(t2 ) & 0xff] ^
+ rk[1];
+ s2 =
+ Td0[(t2 >> 24) ] ^
+ Td1[(t1 >> 16) & 0xff] ^
+ Td2[(t0 >> 8) & 0xff] ^
+ Td3[(t3 ) & 0xff] ^
+ rk[2];
+ s3 =
+ Td0[(t3 >> 24) ] ^
+ Td1[(t2 >> 16) & 0xff] ^
+ Td2[(t1 >> 8) & 0xff] ^
+ Td3[(t0 ) & 0xff] ^
+ rk[3];
+ }
+#endif /* ?FULL_UNROLL */
+ /*
+ * apply last round and
+ * map cipher state to byte array block:
+ */
+ s0 =
+ (Td4[(t0 >> 24) ] & 0xff000000) ^
+ (Td4[(t3 >> 16) & 0xff] & 0x00ff0000) ^
+ (Td4[(t2 >> 8) & 0xff] & 0x0000ff00) ^
+ (Td4[(t1 ) & 0xff] & 0x000000ff) ^
+ rk[0];
+ PUTU32(pt , s0);
+ s1 =
+ (Td4[(t1 >> 24) ] & 0xff000000) ^
+ (Td4[(t0 >> 16) & 0xff] & 0x00ff0000) ^
+ (Td4[(t3 >> 8) & 0xff] & 0x0000ff00) ^
+ (Td4[(t2 ) & 0xff] & 0x000000ff) ^
+ rk[1];
+ PUTU32(pt + 4, s1);
+ s2 =
+ (Td4[(t2 >> 24) ] & 0xff000000) ^
+ (Td4[(t1 >> 16) & 0xff] & 0x00ff0000) ^
+ (Td4[(t0 >> 8) & 0xff] & 0x0000ff00) ^
+ (Td4[(t3 ) & 0xff] & 0x000000ff) ^
+ rk[2];
+ PUTU32(pt + 8, s2);
+ s3 =
+ (Td4[(t3 >> 24) ] & 0xff000000) ^
+ (Td4[(t2 >> 16) & 0xff] & 0x00ff0000) ^
+ (Td4[(t1 >> 8) & 0xff] & 0x0000ff00) ^
+ (Td4[(t0 ) & 0xff] & 0x000000ff) ^
+ rk[3];
+ PUTU32(pt + 12, s3);
+}
diff --git a/contrib/wpa_supplicant/aes_wrap.c b/contrib/wpa_supplicant/aes_wrap.c
new file mode 100644
index 000000000000..dbcc136517e7
--- /dev/null
+++ b/contrib/wpa_supplicant/aes_wrap.c
@@ -0,0 +1,642 @@
+/*
+ * AES Key Wrap Algorithm (128-bit KEK) (RFC3394)
+ * One-Key CBC MAC (OMAC1) hash with AES-128
+ * AES-128 CTR mode encryption
+ * AES-128 EAX mode encryption/decryption
+ * AES-128 CBC
+ * Copyright (c) 2003-2004, Jouni Malinen <jkmaline@cc.hut.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published 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 <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "common.h"
+#include "aes_wrap.h"
+
+#ifdef EAP_TLS_FUNCS
+
+#include <openssl/aes.h>
+
+#else /* EAP_TLS_FUNCS */
+
+#include "aes.c"
+
+struct aes_key_st {
+ u32 rk[44];
+};
+typedef struct aes_key_st AES_KEY;
+
+#define AES_set_encrypt_key(userKey, bits, key) \
+ rijndaelKeySetupEnc((key)->rk, (userKey))
+#define AES_set_decrypt_key(userKey, bits, key) \
+ rijndaelKeySetupDec((key)->rk, (userKey))
+#define AES_encrypt(in, out, key) \
+ rijndaelEncrypt((key)->rk, in, out)
+#define AES_decrypt(in, out, key) \
+ rijndaelDecrypt((key)->rk, in, out)
+
+#endif /* EAP_TLS_FUNCS */
+
+
+/*
+ * @kek: key encryption key (KEK)
+ * @n: length of the wrapped key in 64-bit units; e.g., 2 = 128-bit = 16 bytes
+ * @plain: plaintext key to be wrapped, n * 64 bit
+ * @cipher: wrapped key, (n + 1) * 64 bit
+ */
+void aes_wrap(u8 *kek, int n, u8 *plain, u8 *cipher)
+{
+ u8 *a, *r, b[16];
+ int i, j;
+ AES_KEY key;
+
+ a = cipher;
+ r = cipher + 8;
+
+ /* 1) Initialize variables. */
+ memset(a, 0xa6, 8);
+ memcpy(r, plain, 8 * n);
+
+ AES_set_encrypt_key(kek, 128, &key);
+
+ /* 2) Calculate intermediate values.
+ * For j = 0 to 5
+ * For i=1 to n
+ * B = AES(K, A | R[i])
+ * A = MSB(64, B) ^ t where t = (n*j)+i
+ * R[i] = LSB(64, B)
+ */
+ for (j = 0; j <= 5; j++) {
+ r = cipher + 8;
+ for (i = 1; i <= n; i++) {
+ memcpy(b, a, 8);
+ memcpy(b + 8, r, 8);
+ AES_encrypt(b, b, &key);
+ memcpy(a, b, 8);
+ a[7] ^= n * j + i;
+ memcpy(r, b + 8, 8);
+ r += 8;
+ }
+ }
+
+ /* 3) Output the results.
+ *
+ * These are already in @cipher due to the location of temporary
+ * variables.
+ */
+}
+
+
+/*
+ * @kek: key encryption key (KEK)
+ * @n: length of the wrapped key in 64-bit units; e.g., 2 = 128-bit = 16 bytes
+ * @cipher: wrapped key to be unwrapped, (n + 1) * 64 bit
+ * @plain: plaintext key, n * 64 bit
+ */
+int aes_unwrap(u8 *kek, int n, u8 *cipher, u8 *plain)
+{
+ u8 a[8], *r, b[16];
+ int i, j;
+ AES_KEY key;
+
+ /* 1) Initialize variables. */
+ memcpy(a, cipher, 8);
+ r = plain;
+ memcpy(r, cipher + 8, 8 * n);
+
+ AES_set_decrypt_key(kek, 128, &key);
+
+ /* 2) Compute intermediate values.
+ * For j = 5 to 0
+ * For i = n to 1
+ * B = AES-1(K, (A ^ t) | R[i]) where t = n*j+i
+ * A = MSB(64, B)
+ * R[i] = LSB(64, B)
+ */
+ for (j = 5; j >= 0; j--) {
+ r = plain + (n - 1) * 8;
+ for (i = n; i >= 1; i--) {
+ memcpy(b, a, 8);
+ b[7] ^= n * j + i;
+
+ memcpy(b + 8, r, 8);
+ AES_decrypt(b, b, &key);
+ memcpy(a, b, 8);
+ memcpy(r, b + 8, 8);
+ r -= 8;
+ }
+ }
+
+ /* 3) Output results.
+ *
+ * These are already in @plain due to the location of temporary
+ * variables. Just verify that the IV matches with the expected value.
+ */
+ for (i = 0; i < 8; i++) {
+ if (a[i] != 0xa6)
+ return -1;
+ }
+
+ return 0;
+}
+
+
+#define BLOCK_SIZE 16
+
+static void gf_mulx(u8 *pad)
+{
+ int i, carry;
+
+ carry = pad[0] & 0x80;
+ for (i = 0; i < BLOCK_SIZE - 1; i++)
+ pad[i] = (pad[i] << 1) | (pad[i + 1] >> 7);
+ pad[BLOCK_SIZE - 1] <<= 1;
+ if (carry)
+ pad[BLOCK_SIZE - 1] ^= 0x87;
+}
+
+
+void omac1_aes_128(const u8 *key, const u8 *data, size_t data_len, u8 *mac)
+{
+ AES_KEY akey;
+ u8 cbc[BLOCK_SIZE], pad[BLOCK_SIZE];
+ const u8 *pos = data;
+ int i;
+ size_t left = data_len;
+
+ AES_set_encrypt_key(key, 128, &akey);
+ memset(cbc, 0, BLOCK_SIZE);
+
+ while (left >= BLOCK_SIZE) {
+ for (i = 0; i < BLOCK_SIZE; i++)
+ cbc[i] ^= *pos++;
+ if (left > BLOCK_SIZE)
+ AES_encrypt(cbc, cbc, &akey);
+ left -= BLOCK_SIZE;
+ }
+
+ memset(pad, 0, BLOCK_SIZE);
+ AES_encrypt(pad, pad, &akey);
+ gf_mulx(pad);
+
+ if (left || data_len == 0) {
+ for (i = 0; i < left; i++)
+ cbc[i] ^= *pos++;
+ cbc[left] ^= 0x80;
+ gf_mulx(pad);
+ }
+
+ for (i = 0; i < BLOCK_SIZE; i++)
+ pad[i] ^= cbc[i];
+ AES_encrypt(pad, mac, &akey);
+}
+
+
+void aes_128_encrypt_block(const u8 *key, const u8 *in, u8 *out)
+{
+ AES_KEY akey;
+ AES_set_encrypt_key(key, 128, &akey);
+ AES_encrypt(in, out, &akey);
+}
+
+
+void aes_128_ctr_encrypt(const u8 *key, const u8 *nonce,
+ u8 *data, size_t data_len)
+{
+ AES_KEY akey;
+ size_t len, left = data_len;
+ int i;
+ u8 *pos = data;
+ u8 counter[BLOCK_SIZE], buf[BLOCK_SIZE];
+
+ AES_set_encrypt_key(key, 128, &akey);
+ memcpy(counter, nonce, BLOCK_SIZE);
+
+ while (left > 0) {
+ AES_encrypt(counter, buf, &akey);
+
+ len = (left < BLOCK_SIZE) ? left : BLOCK_SIZE;
+ for (i = 0; i < len; i++)
+ pos[i] ^= buf[i];
+ pos += len;
+ left -= len;
+
+ for (i = BLOCK_SIZE - 1; i >= 0; i--) {
+ counter[i]++;
+ if (counter[i])
+ break;
+ }
+ }
+}
+
+
+int aes_128_eax_encrypt(const u8 *key, const u8 *nonce, size_t nonce_len,
+ const u8 *hdr, size_t hdr_len,
+ u8 *data, size_t data_len, u8 *tag)
+{
+ u8 *buf;
+ size_t buf_len;
+ u8 nonce_mac[BLOCK_SIZE], hdr_mac[BLOCK_SIZE], data_mac[BLOCK_SIZE];
+ int i;
+
+ if (nonce_len > data_len)
+ buf_len = nonce_len;
+ else
+ buf_len = data_len;
+ if (hdr_len > buf_len)
+ buf_len = hdr_len;
+ buf_len += 16;
+
+ buf = malloc(buf_len);
+ if (buf == NULL)
+ return -1;
+
+ memset(buf, 0, 15);
+
+ buf[15] = 0;
+ memcpy(buf + 16, nonce, nonce_len);
+ omac1_aes_128(key, buf, 16 + nonce_len, nonce_mac);
+
+ buf[15] = 1;
+ memcpy(buf + 16, hdr, hdr_len);
+ omac1_aes_128(key, buf, 16 + hdr_len, hdr_mac);
+
+ aes_128_ctr_encrypt(key, nonce_mac, data, data_len);
+ buf[15] = 2;
+ memcpy(buf + 16, data, data_len);
+ omac1_aes_128(key, buf, 16 + data_len, data_mac);
+
+ free(buf);
+
+ for (i = 0; i < BLOCK_SIZE; i++)
+ tag[i] = nonce_mac[i] ^ data_mac[i] ^ hdr_mac[i];
+
+ return 0;
+}
+
+
+int aes_128_eax_decrypt(const u8 *key, const u8 *nonce, size_t nonce_len,
+ const u8 *hdr, size_t hdr_len,
+ u8 *data, size_t data_len, const u8 *tag)
+{
+ u8 *buf;
+ size_t buf_len;
+ u8 nonce_mac[BLOCK_SIZE], hdr_mac[BLOCK_SIZE], data_mac[BLOCK_SIZE];
+ int i;
+
+ if (nonce_len > data_len)
+ buf_len = nonce_len;
+ else
+ buf_len = data_len;
+ if (hdr_len > buf_len)
+ buf_len = hdr_len;
+ buf_len += 16;
+
+ buf = malloc(buf_len);
+ if (buf == NULL)
+ return -1;
+
+ memset(buf, 0, 15);
+
+ buf[15] = 0;
+ memcpy(buf + 16, nonce, nonce_len);
+ omac1_aes_128(key, buf, 16 + nonce_len, nonce_mac);
+
+ buf[15] = 1;
+ memcpy(buf + 16, hdr, hdr_len);
+ omac1_aes_128(key, buf, 16 + hdr_len, hdr_mac);
+
+ buf[15] = 2;
+ memcpy(buf + 16, data, data_len);
+ omac1_aes_128(key, buf, 16 + data_len, data_mac);
+
+ free(buf);
+
+ for (i = 0; i < BLOCK_SIZE; i++) {
+ if (tag[i] != (nonce_mac[i] ^ data_mac[i] ^ hdr_mac[i]))
+ return -2;
+ }
+
+ aes_128_ctr_encrypt(key, nonce_mac, data, data_len);
+
+ return 0;
+}
+
+
+void aes_128_cbc_encrypt(const u8 *key, const u8 *iv, u8 *data,
+ size_t data_len)
+{
+ AES_KEY akey;
+ u8 cbc[BLOCK_SIZE];
+ u8 *pos = data;
+ int i, j, blocks;
+
+ AES_set_encrypt_key(key, 128, &akey);
+ memcpy(cbc, iv, BLOCK_SIZE);
+
+ blocks = data_len / BLOCK_SIZE;
+ for (i = 0; i < blocks; i++) {
+ for (j = 0; j < BLOCK_SIZE; j++)
+ cbc[j] ^= pos[j];
+ AES_encrypt(cbc, cbc, &akey);
+ memcpy(pos, cbc, BLOCK_SIZE);
+ pos += BLOCK_SIZE;
+ }
+}
+
+
+void aes_128_cbc_decrypt(const u8 *key, const u8 *iv, u8 *data,
+ size_t data_len)
+{
+ AES_KEY akey;
+ u8 cbc[BLOCK_SIZE], tmp[BLOCK_SIZE];
+ u8 *pos = data;
+ int i, j, blocks;
+
+ AES_set_decrypt_key(key, 128, &akey);
+ memcpy(cbc, iv, BLOCK_SIZE);
+
+ blocks = data_len / BLOCK_SIZE;
+ for (i = 0; i < blocks; i++) {
+ memcpy(tmp, pos, BLOCK_SIZE);
+ AES_decrypt(pos, pos, &akey);
+ for (j = 0; j < BLOCK_SIZE; j++)
+ pos[j] ^= cbc[j];
+ memcpy(cbc, tmp, BLOCK_SIZE);
+ pos += BLOCK_SIZE;
+ }
+}
+
+
+#ifdef TEST_MAIN
+
+#ifdef __i386__
+#define rdtscll(val) \
+ __asm__ __volatile__("rdtsc" : "=A" (val))
+
+static void test_aes_perf(void)
+{
+ const int num_iters = 10;
+ int i;
+ unsigned int start, end;
+ AES_KEY akey;
+ u8 key[16], pt[16], ct[16];
+
+ printf("keySetupEnc:");
+ for (i = 0; i < num_iters; i++) {
+ rdtscll(start);
+ AES_set_encrypt_key(key, 128, &akey);
+ rdtscll(end);
+ printf(" %d", end - start);
+ }
+ printf("\n");
+
+ printf("Encrypt:");
+ for (i = 0; i < num_iters; i++) {
+ rdtscll(start);
+ AES_encrypt(pt, ct, &akey);
+ rdtscll(end);
+ printf(" %d", end - start);
+ }
+ printf("\n");
+}
+#endif /* __i386__ */
+
+
+static int test_eax(void)
+{
+ u8 msg[] = { 0xF7, 0xFB };
+ u8 key[] = { 0x91, 0x94, 0x5D, 0x3F, 0x4D, 0xCB, 0xEE, 0x0B,
+ 0xF4, 0x5E, 0xF5, 0x22, 0x55, 0xF0, 0x95, 0xA4 };
+ u8 nonce[] = { 0xBE, 0xCA, 0xF0, 0x43, 0xB0, 0xA2, 0x3D, 0x84,
+ 0x31, 0x94, 0xBA, 0x97, 0x2C, 0x66, 0xDE, 0xBD };
+ u8 hdr[] = { 0xFA, 0x3B, 0xFD, 0x48, 0x06, 0xEB, 0x53, 0xFA };
+ u8 cipher[] = { 0x19, 0xDD, 0x5C, 0x4C, 0x93, 0x31, 0x04, 0x9D,
+ 0x0B, 0xDA, 0xB0, 0x27, 0x74, 0x08, 0xF6, 0x79,
+ 0x67, 0xE5 };
+ u8 data[sizeof(msg)], tag[BLOCK_SIZE];
+
+ memcpy(data, msg, sizeof(msg));
+ if (aes_128_eax_encrypt(key, nonce, sizeof(nonce), hdr, sizeof(hdr),
+ data, sizeof(data), tag)) {
+ printf("AES-128 EAX mode encryption failed\n");
+ return 1;
+ }
+ if (memcmp(data, cipher, sizeof(data)) != 0) {
+ printf("AES-128 EAX mode encryption returned invalid cipher "
+ "text\n");
+ return 1;
+ }
+ if (memcmp(tag, cipher + sizeof(data), BLOCK_SIZE) != 0) {
+ printf("AES-128 EAX mode encryption returned invalid tag\n");
+ return 1;
+ }
+
+ if (aes_128_eax_decrypt(key, nonce, sizeof(nonce), hdr, sizeof(hdr),
+ data, sizeof(data), tag)) {
+ printf("AES-128 EAX mode decryption failed\n");
+ return 1;
+ }
+ if (memcmp(data, msg, sizeof(data)) != 0) {
+ printf("AES-128 EAX mode decryption returned invalid plain "
+ "text\n");
+ return 1;
+ }
+
+ return 0;
+}
+
+
+static int test_cbc(void)
+{
+ struct cbc_test_vector {
+ u8 key[16];
+ u8 iv[16];
+ u8 plain[32];
+ u8 cipher[32];
+ size_t len;
+ } vectors[] = {
+ {
+ { 0x06, 0xa9, 0x21, 0x40, 0x36, 0xb8, 0xa1, 0x5b,
+ 0x51, 0x2e, 0x03, 0xd5, 0x34, 0x12, 0x00, 0x06 },
+ { 0x3d, 0xaf, 0xba, 0x42, 0x9d, 0x9e, 0xb4, 0x30,
+ 0xb4, 0x22, 0xda, 0x80, 0x2c, 0x9f, 0xac, 0x41 },
+ "Single block msg",
+ { 0xe3, 0x53, 0x77, 0x9c, 0x10, 0x79, 0xae, 0xb8,
+ 0x27, 0x08, 0x94, 0x2d, 0xbe, 0x77, 0x18, 0x1a },
+ 16
+ },
+ {
+ { 0xc2, 0x86, 0x69, 0x6d, 0x88, 0x7c, 0x9a, 0xa0,
+ 0x61, 0x1b, 0xbb, 0x3e, 0x20, 0x25, 0xa4, 0x5a },
+ { 0x56, 0x2e, 0x17, 0x99, 0x6d, 0x09, 0x3d, 0x28,
+ 0xdd, 0xb3, 0xba, 0x69, 0x5a, 0x2e, 0x6f, 0x58 },
+ { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f },
+ { 0xd2, 0x96, 0xcd, 0x94, 0xc2, 0xcc, 0xcf, 0x8a,
+ 0x3a, 0x86, 0x30, 0x28, 0xb5, 0xe1, 0xdc, 0x0a,
+ 0x75, 0x86, 0x60, 0x2d, 0x25, 0x3c, 0xff, 0xf9,
+ 0x1b, 0x82, 0x66, 0xbe, 0xa6, 0xd6, 0x1a, 0xb1 },
+ 32
+ }
+ };
+ int i, ret = 0;
+ u8 *buf;
+
+ for (i = 0; i < sizeof(vectors) / sizeof(vectors[0]); i++) {
+ struct cbc_test_vector *tv = &vectors[i];
+ buf = malloc(tv->len);
+ if (buf == NULL) {
+ ret++;
+ break;
+ }
+ memcpy(buf, tv->plain, tv->len);
+ aes_128_cbc_encrypt(tv->key, tv->iv, buf, tv->len);
+ if (memcmp(buf, tv->cipher, tv->len) != 0) {
+ printf("AES-CBC encrypt %d failed\n", i);
+ ret++;
+ }
+ memcpy(buf, tv->cipher, tv->len);
+ aes_128_cbc_decrypt(tv->key, tv->iv, buf, tv->len);
+ if (memcmp(buf, tv->plain, tv->len) != 0) {
+ printf("AES-CBC decrypt %d failed\n", i);
+ ret++;
+ }
+ free(buf);
+ }
+
+ return ret;
+}
+
+
+/* OMAC1 AES-128 test vectors from
+ * http://csrc.nist.gov/CryptoToolkit/modes/proposedmodes/omac/omac-ad.pdf
+ */
+
+struct omac1_test_vector {
+ u8 k[16];
+ u8 msg[64];
+ int msg_len;
+ u8 tag[16];
+};
+
+static struct omac1_test_vector test_vectors[] =
+{
+ {
+ { 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6,
+ 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c },
+ { },
+ 0,
+ { 0xbb, 0x1d, 0x69, 0x29, 0xe9, 0x59, 0x37, 0x28,
+ 0x7f, 0xa3, 0x7d, 0x12, 0x9b, 0x75, 0x67, 0x46 }
+ },
+ {
+ { 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6,
+ 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c },
+ { 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96,
+ 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a},
+ 16,
+ { 0x07, 0x0a, 0x16, 0xb4, 0x6b, 0x4d, 0x41, 0x44,
+ 0xf7, 0x9b, 0xdd, 0x9d, 0xd0, 0x4a, 0x28, 0x7c }
+ },
+ {
+ { 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6,
+ 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c },
+ { 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96,
+ 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
+ 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c,
+ 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51,
+ 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11 },
+ 40,
+ { 0xdf, 0xa6, 0x67, 0x47, 0xde, 0x9a, 0xe6, 0x30,
+ 0x30, 0xca, 0x32, 0x61, 0x14, 0x97, 0xc8, 0x27 }
+ },
+ {
+ { 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6,
+ 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c },
+ { 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96,
+ 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
+ 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c,
+ 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51,
+ 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11,
+ 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef,
+ 0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17,
+ 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10 },
+ 64,
+ { 0x51, 0xf0, 0xbe, 0xbf, 0x7e, 0x3b, 0x9d, 0x92,
+ 0xfc, 0x49, 0x74, 0x17, 0x79, 0x36, 0x3c, 0xfe }
+ },
+};
+
+
+int main(int argc, char *argv[])
+{
+ u8 kek[] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f
+ };
+ u8 plain[] = {
+ 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
+ 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff
+ };
+ u8 crypt[] = {
+ 0x1F, 0xA6, 0x8B, 0x0A, 0x81, 0x12, 0xB4, 0x47,
+ 0xAE, 0xF3, 0x4B, 0xD8, 0xFB, 0x5A, 0x7B, 0x82,
+ 0x9D, 0x3E, 0x86, 0x23, 0x71, 0xD2, 0xCF, 0xE5
+ };
+ u8 result[24];
+ int ret = 0, i;
+ struct omac1_test_vector *tv;
+
+ aes_wrap(kek, 2, plain, result);
+ if (memcmp(result, crypt, 24) != 0) {
+ printf("AES-WRAP-128-128 failed\n");
+ ret++;
+ }
+ if (aes_unwrap(kek, 2, crypt, result)) {
+ printf("AES-UNWRAP-128-128 reported failure\n");
+ ret++;
+ }
+ if (memcmp(result, plain, 16) != 0) {
+ int i;
+ printf("AES-UNWRAP-128-128 failed\n");
+ ret++;
+ for (i = 0; i < 16; i++)
+ printf(" %02x", result[i]);
+ printf("\n");
+ }
+
+#ifdef __i386__
+ test_aes_perf();
+#endif /* __i386__ */
+
+ for (i = 0; i < sizeof(test_vectors) / sizeof(test_vectors[0]); i++) {
+ tv = &test_vectors[i];
+ omac1_aes_128(tv->k, tv->msg, tv->msg_len, result);
+ if (memcmp(result, tv->tag, 16) != 0) {
+ printf("OMAC1-AES-128 test vector %d failed\n", i);
+ ret++;
+ }
+ }
+
+ ret += test_eax();
+
+ ret += test_cbc();
+
+ if (ret)
+ printf("FAILED!\n");
+
+ return ret;
+}
+#endif /* TEST_MAIN */
diff --git a/contrib/wpa_supplicant/aes_wrap.h b/contrib/wpa_supplicant/aes_wrap.h
new file mode 100644
index 000000000000..70e83ea09d73
--- /dev/null
+++ b/contrib/wpa_supplicant/aes_wrap.h
@@ -0,0 +1,21 @@
+#ifndef AES_WRAP_H
+#define AES_WRAP_H
+
+void aes_wrap(u8 *kek, int n, u8 *plain, u8 *cipher);
+int aes_unwrap(u8 *kek, int n, u8 *cipher, u8 *plain);
+void omac1_aes_128(const u8 *key, const u8 *data, size_t data_len, u8 *mac);
+void aes_128_encrypt_block(const u8 *key, const u8 *in, u8 *out);
+void aes_128_ctr_encrypt(const u8 *key, const u8 *nonce,
+ u8 *data, size_t data_len);
+int aes_128_eax_encrypt(const u8 *key, const u8 *nonce, size_t nonce_len,
+ const u8 *hdr, size_t hdr_len,
+ u8 *data, size_t data_len, u8 *tag);
+int aes_128_eax_decrypt(const u8 *key, const u8 *nonce, size_t nonce_len,
+ const u8 *hdr, size_t hdr_len,
+ u8 *data, size_t data_len, const u8 *tag);
+void aes_128_cbc_encrypt(const u8 *key, const u8 *iv, u8 *data,
+ size_t data_len);
+void aes_128_cbc_decrypt(const u8 *key, const u8 *iv, u8 *data,
+ size_t data_len);
+
+#endif /* AES_WRAP_H */
diff --git a/contrib/wpa_supplicant/common.c b/contrib/wpa_supplicant/common.c
new file mode 100644
index 000000000000..071ffe87c1d1
--- /dev/null
+++ b/contrib/wpa_supplicant/common.c
@@ -0,0 +1,335 @@
+/*
+ * Host AP (software wireless LAN access point) user space daemon for
+ * Host AP kernel driver / common helper functions, etc.
+ * Copyright (c) 2002-2005, Jouni Malinen <jkmaline@cc.hut.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published 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 <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <ctype.h>
+#include <time.h>
+#include <sys/time.h>
+
+#include "common.h"
+
+
+int wpa_debug_level = MSG_INFO;
+int wpa_debug_show_keys = 0;
+int wpa_debug_timestamp = 0;
+
+
+int hostapd_get_rand(u8 *buf, size_t len)
+{
+#ifdef CONFIG_NATIVE_WINDOWS
+ int i;
+ /* FIX: use more secure pseudo random number generator */
+ for (i = 0; i < len; i++) {
+ buf[i] = rand();
+ }
+ return 0;
+#else /* CONFIG_NATIVE_WINDOWS */
+ FILE *f;
+ size_t rc;
+
+ f = fopen("/dev/urandom", "r");
+ if (f == NULL) {
+ printf("Could not open /dev/urandom.\n");
+ return -1;
+ }
+
+ rc = fread(buf, 1, len, f);
+ fclose(f);
+
+ return rc != len ? -1 : 0;
+#endif /* CONFIG_NATIVE_WINDOWS */
+}
+
+
+void hostapd_hexdump(const char *title, const u8 *buf, size_t len)
+{
+ size_t i;
+ printf("%s - hexdump(len=%lu):", title, (unsigned long) len);
+ for (i = 0; i < len; i++)
+ printf(" %02x", buf[i]);
+ printf("\n");
+}
+
+
+static int hex2num(char c)
+{
+ if (c >= '0' && c <= '9')
+ return c - '0';
+ if (c >= 'a' && c <= 'f')
+ return c - 'a' + 10;
+ if (c >= 'A' && c <= 'F')
+ return c - 'A' + 10;
+ return -1;
+}
+
+
+static int hex2byte(const char *hex)
+{
+ int a, b;
+ a = hex2num(*hex++);
+ if (a < 0)
+ return -1;
+ b = hex2num(*hex++);
+ if (b < 0)
+ return -1;
+ return (a << 4) | b;
+}
+
+
+int hwaddr_aton(const char *txt, u8 *addr)
+{
+ int i;
+
+ for (i = 0; i < 6; i++) {
+ int a, b;
+
+ a = hex2num(*txt++);
+ if (a < 0)
+ return -1;
+ b = hex2num(*txt++);
+ if (b < 0)
+ return -1;
+ *addr++ = (a << 4) | b;
+ if (i < 5 && *txt++ != ':')
+ return -1;
+ }
+
+ return 0;
+}
+
+
+int hexstr2bin(const char *hex, u8 *buf, size_t len)
+{
+ int i, a;
+ const char *ipos = hex;
+ u8 *opos = buf;
+
+ for (i = 0; i < len; i++) {
+ a = hex2byte(ipos);
+ if (a < 0)
+ return -1;
+ *opos++ = a;
+ ipos += 2;
+ }
+ return 0;
+}
+
+
+char * rel2abs_path(const char *rel_path)
+{
+ char *buf = NULL, *cwd, *ret;
+ size_t len = 128, cwd_len, rel_len, ret_len;
+
+ if (rel_path[0] == '/')
+ return strdup(rel_path);
+
+ for (;;) {
+ buf = malloc(len);
+ if (buf == NULL)
+ return NULL;
+ cwd = getcwd(buf, len);
+ if (cwd == NULL) {
+ free(buf);
+ if (errno != ERANGE) {
+ return NULL;
+ }
+ len *= 2;
+ } else {
+ break;
+ }
+ }
+
+ cwd_len = strlen(cwd);
+ rel_len = strlen(rel_path);
+ ret_len = cwd_len + 1 + rel_len + 1;
+ ret = malloc(ret_len);
+ if (ret) {
+ memcpy(ret, cwd, cwd_len);
+ ret[cwd_len] = '/';
+ memcpy(ret + cwd_len + 1, rel_path, rel_len);
+ ret[ret_len - 1] = '\0';
+ }
+ free(buf);
+ return ret;
+}
+
+
+void inc_byte_array(u8 *counter, size_t len)
+{
+ int pos = len - 1;
+ while (pos >= 0) {
+ counter[pos]++;
+ if (counter[pos] != 0)
+ break;
+ pos--;
+ }
+}
+
+
+void print_char(char c)
+{
+ if (c >= 32 && c < 127)
+ printf("%c", c);
+ else
+ printf("<%02x>", c);
+}
+
+
+void fprint_char(FILE *f, char c)
+{
+ if (c >= 32 && c < 127)
+ fprintf(f, "%c", c);
+ else
+ fprintf(f, "<%02x>", c);
+}
+
+
+static void wpa_debug_print_timestamp(void)
+{
+ struct timeval tv;
+ char buf[16];
+
+ if (!wpa_debug_timestamp)
+ return;
+
+ gettimeofday(&tv, NULL);
+ if (strftime(buf, sizeof(buf), "%b %d %H:%M:%S",
+ localtime((const time_t *) &tv.tv_sec)) <= 0) {
+ snprintf(buf, sizeof(buf), "%u", (int) tv.tv_sec);
+ }
+ printf("%s.%06u: ", buf, (unsigned int) tv.tv_usec);
+}
+
+
+void wpa_printf(int level, char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ if (level >= wpa_debug_level) {
+ wpa_debug_print_timestamp();
+ vprintf(fmt, ap);
+ printf("\n");
+ }
+ va_end(ap);
+}
+
+
+static void _wpa_hexdump(int level, const char *title, const u8 *buf,
+ size_t len, int show)
+{
+ size_t i;
+ if (level < wpa_debug_level)
+ return;
+ wpa_debug_print_timestamp();
+ printf("%s - hexdump(len=%lu):", title, (unsigned long) len);
+ if (show) {
+ for (i = 0; i < len; i++)
+ printf(" %02x", buf[i]);
+ } else {
+ printf(" [REMOVED]");
+ }
+ printf("\n");
+}
+
+void wpa_hexdump(int level, const char *title, const u8 *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)
+{
+ _wpa_hexdump(level, title, buf, len, wpa_debug_show_keys);
+}
+
+
+static void _wpa_hexdump_ascii(int level, const char *title, const u8 *buf,
+ size_t len, int show)
+{
+ int i, llen;
+ const u8 *pos = buf;
+ const int line_len = 16;
+
+ if (level < wpa_debug_level)
+ return;
+ wpa_debug_print_timestamp();
+ if (!show) {
+ printf("%s - hexdump_ascii(len=%lu): [REMOVED]\n",
+ title, (unsigned long) len);
+ return;
+ }
+ printf("%s - hexdump_ascii(len=%lu):\n", title, (unsigned long) len);
+ while (len) {
+ llen = len > line_len ? line_len : len;
+ printf(" ");
+ for (i = 0; i < llen; i++)
+ printf(" %02x", pos[i]);
+ for (i = llen; i < line_len; i++)
+ printf(" ");
+ printf(" ");
+ for (i = 0; i < llen; i++) {
+ if (isprint(pos[i]))
+ printf("%c", pos[i]);
+ else
+ printf("_");
+ }
+ for (i = llen; i < line_len; i++)
+ printf(" ");
+ printf("\n");
+ pos += llen;
+ len -= llen;
+ }
+}
+
+
+void wpa_hexdump_ascii(int level, const char *title, const u8 *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,
+ size_t len)
+{
+ _wpa_hexdump_ascii(level, title, buf, len, wpa_debug_show_keys);
+}
+
+
+#ifdef CONFIG_NATIVE_WINDOWS
+
+#define EPOCHFILETIME (116444736000000000ULL)
+
+int gettimeofday(struct timeval *tv, struct timezone *tz)
+{
+ FILETIME ft;
+ LARGE_INTEGER li;
+ ULONGLONG t;
+
+ GetSystemTimeAsFileTime(&ft);
+ li.LowPart = ft.dwLowDateTime;
+ li.HighPart = ft.dwHighDateTime;
+ t = (li.QuadPart - EPOCHFILETIME) / 10;
+ tv->tv_sec = (long) (t / 1000000);
+ tv->tv_usec = (long) (t % 1000000);
+
+ return 0;
+}
+#endif /* CONFIG_NATIVE_WINDOWS */
diff --git a/contrib/wpa_supplicant/common.h b/contrib/wpa_supplicant/common.h
new file mode 100644
index 000000000000..aa6429c76b96
--- /dev/null
+++ b/contrib/wpa_supplicant/common.h
@@ -0,0 +1,222 @@
+#ifndef COMMON_H
+#define COMMON_H
+
+#ifdef __linux__
+#include <endian.h>
+#include <byteswap.h>
+#endif
+#ifdef __FreeBSD__
+#include <sys/types.h>
+#include <sys/endian.h>
+#define bswap_16 bswap16
+#define bswap_32 bswap32
+#endif
+
+#ifdef CONFIG_NATIVE_WINDOWS
+#include <winsock.h>
+#include <winsock2.h>
+
+static inline int daemon(int nochdir, int noclose)
+{
+ printf("Windows - daemon() not supported yet\n");
+ return -1;
+}
+
+static inline void sleep(int seconds)
+{
+ Sleep(seconds * 1000);
+}
+
+static inline void usleep(unsigned long usec)
+{
+ Sleep(usec / 1000);
+}
+
+#ifndef timersub
+#define timersub(a, b, res) do { \
+ (res)->tv_sec = (a)->tv_sec - (b)->tv_sec; \
+ (res)->tv_usec = (a)->tv_usec - (b)->tv_usec; \
+ if ((res)->tv_usec < 0) { \
+ (res)->tv_sec--; \
+ (res)->tv_usec += 1000000; \
+ } \
+} while (0)
+#endif
+
+struct timezone {
+ int tz_minuteswest;
+ int tz_dsttime;
+};
+
+int gettimeofday(struct timeval *tv, struct timezone *tz);
+
+#endif /* CONFIG_NATIVE_WINDOWS */
+
+#if defined(__CYGWIN__) || defined(CONFIG_NATIVE_WINDOWS)
+
+static inline unsigned short wpa_swap_16(unsigned short v)
+{
+ return ((v & 0xff) << 8) | (v >> 8);
+}
+
+static inline unsigned int wpa_swap_32(unsigned int v)
+{
+ return ((v & 0xff) << 24) | ((v & 0xff00) << 8) |
+ ((v & 0xff0000) >> 8) | (v >> 24);
+}
+
+#define le_to_host16(n) (n)
+#define host_to_le16(n) (n)
+#define be_to_host16(n) wpa_swap_16(n)
+#define host_to_be16(n) wpa_swap_16(n)
+#define le_to_host32(n) (n)
+#define be_to_host32(n) wpa_swap_32(n)
+#define host_to_be32(n) wpa_swap_32(n)
+
+#else /* __CYGWIN__ */
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+#define le_to_host16(n) (n)
+#define host_to_le16(n) (n)
+#define be_to_host16(n) bswap_16(n)
+#define host_to_be16(n) bswap_16(n)
+#define le_to_host32(n) (n)
+#define be_to_host32(n) bswap_32(n)
+#define host_to_be32(n) bswap_32(n)
+#elif __BYTE_ORDER == __BIG_ENDIAN
+#define le_to_host16(n) bswap_16(n)
+#define host_to_le16(n) bswap_16(n)
+#define be_to_host16(n) (n)
+#define host_to_be16(n) (n)
+#define le_to_host32(n) bswap_32(n)
+#define be_to_host32(n) (n)
+#define host_to_be32(n) (n)
+#ifndef WORDS_BIGENDIAN
+#define WORDS_BIGENDIAN
+#endif
+#else
+#error Could not determine CPU byte order
+#endif
+
+#endif /* __CYGWIN__ */
+
+
+#ifndef ETH_ALEN
+#define ETH_ALEN 6
+#endif
+
+#include <stdint.h>
+typedef uint64_t u64;
+typedef uint32_t u32;
+typedef uint16_t u16;
+typedef uint8_t u8;
+typedef int64_t s64;
+typedef int32_t s32;
+typedef int16_t s16;
+typedef int8_t s8;
+
+int hostapd_get_rand(u8 *buf, size_t len);
+void hostapd_hexdump(const char *title, const u8 *buf, size_t len);
+int hwaddr_aton(const char *txt, u8 *addr);
+int hexstr2bin(const char *hex, u8 *buf, size_t len);
+char * rel2abs_path(const char *rel_path);
+void inc_byte_array(u8 *counter, size_t len);
+void print_char(char c);
+void fprint_char(FILE *f, char c);
+
+
+/* Debugging function - conditional printf and hex dump. Driver wrappers can
+ * use these for debugging purposes. */
+
+enum { MSG_MSGDUMP, MSG_DEBUG, MSG_INFO, MSG_WARNING, MSG_ERROR };
+
+/**
+ * wpa_printf - conditional printf
+ * @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. The
+ * output may be directed to stdout, stderr, and/or syslog based on
+ * configuration.
+ *
+ * Note: New line '\n' is added to the end of the text when printing to stdout.
+ */
+void wpa_printf(int level, char *fmt, ...)
+__attribute__ ((format (printf, 2, 3)));
+
+/**
+ * wpa_hexdump - conditional hex dump
+ * @level: priority level (MSG_*) of the message
+ * @title: title of for the message
+ * @buf: data buffer to be dumped
+ * @len: length of the @buf
+ *
+ * This function is used to print conditional debugging and error messages. The
+ * 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);
+
+/**
+ * wpa_hexdump_key - conditional hex dump, hide keys
+ * @level: priority level (MSG_*) of the message
+ * @title: title of for the message
+ * @buf: data buffer to be dumped
+ * @len: length of the @buf
+ *
+ * This function is used to print conditional debugging and error messages. The
+ * output may be directed to stdout, stderr, and/or syslog based on
+ * configuration. The contents of @buf is printed out has hex dump. This works
+ * 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);
+
+/**
+ * wpa_hexdump_ascii - conditional hex dump
+ * @level: priority level (MSG_*) of the message
+ * @title: title of for the message
+ * @buf: data buffer to be dumped
+ * @len: length of the @buf
+ *
+ * This function is used to print conditional debugging and error messages. The
+ * output may be directed to stdout, stderr, and/or syslog based on
+ * configuration. The contents of @buf is printed out has hex dump with both
+ * 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,
+ size_t len);
+
+/**
+ * wpa_hexdump_ascii_key - conditional hex dump, hide keys
+ * @level: priority level (MSG_*) of the message
+ * @title: title of for the message
+ * @buf: data buffer to be dumped
+ * @len: length of the @buf
+ *
+ * This function is used to print conditional debugging and error messages. The
+ * output may be directed to stdout, stderr, and/or syslog based on
+ * configuration. The contents of @buf is printed out has hex dump with both
+ * the hex numbers and ASCII characters (for printable range) are shown. 16
+ * 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,
+ size_t len);
+
+#ifdef EAPOL_TEST
+#define WPA_ASSERT(a) \
+ do { \
+ if (!(a)) { \
+ printf("WPA_ASSERT FAILED '" #a "' " \
+ "%s %s:%d\n", \
+ __FUNCTION__, __FILE__, __LINE__); \
+ exit(1); \
+ } \
+ } while (0)
+#else
+#define WPA_ASSERT(a) do { } while (0)
+#endif
+
+#endif /* COMMON_H */
diff --git a/contrib/wpa_supplicant/config.c b/contrib/wpa_supplicant/config.c
new file mode 100644
index 000000000000..241c75565f88
--- /dev/null
+++ b/contrib/wpa_supplicant/config.c
@@ -0,0 +1,1051 @@
+/*
+ * WPA Supplicant / Configuration file parser
+ * Copyright (c) 2003-2005, Jouni Malinen <jkmaline@cc.hut.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published 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 <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "common.h"
+#include "wpa.h"
+#include "config.h"
+#include "sha1.h"
+#include "wpa_supplicant.h"
+#include "eapol_sm.h"
+#include "eap.h"
+#include "config.h"
+
+
+struct parse_data {
+ char *name;
+ int (*parser)(struct parse_data *data, int line, const char *value);
+ void *param1, *param2, *param3, *param4;
+ struct wpa_ssid *ssid;
+ int key_data;
+};
+
+
+static char * wpa_config_get_line(char *s, int size, FILE *stream, int *line)
+{
+ char *pos, *end, *sstart;
+
+ while (fgets(s, size, stream)) {
+ (*line)++;
+ s[size - 1] = '\0';
+ pos = s;
+
+ while (*pos == ' ' || *pos == '\t' || *pos == '\r')
+ pos++;
+ if (*pos == '#' || *pos == '\n' || *pos == '\0' ||
+ *pos == '\r')
+ continue;
+
+ /* Remove # comments unless they are within a double quoted
+ * string. Remove trailing white space. */
+ sstart = strchr(pos, '"');
+ if (sstart)
+ sstart = strchr(sstart + 1, '"');
+ if (!sstart)
+ sstart = pos;
+ end = strchr(sstart, '#');
+ if (end)
+ *end-- = '\0';
+ else
+ end = pos + strlen(pos) - 1;
+ while (end > pos &&
+ (*end == '\n' || *end == ' ' || *end == '\t' ||
+ *end == '\r')) {
+ *end-- = '\0';
+ }
+ if (*pos == '\0')
+ continue;
+
+ return pos;
+ }
+
+ return NULL;
+}
+
+
+static char * wpa_config_parse_string(const char *value, size_t *len)
+{
+ if (*value == '"') {
+ char *pos;
+ value++;
+ pos = strchr(value, '"');
+ if (pos == NULL || pos[1] != '\0')
+ return NULL;
+ *pos = '\0';
+ *len = strlen(value);
+ return strdup(value);
+ } else {
+ u8 *str;
+ int hlen = strlen(value);
+ if (hlen % 1)
+ return NULL;
+ *len = hlen / 2;
+ str = malloc(*len);
+ if (str == NULL)
+ return NULL;
+ if (hexstr2bin(value, str, *len)) {
+ free(str);
+ return NULL;
+ }
+ return (char *) str;
+ }
+}
+
+
+static int wpa_config_parse_str(struct parse_data *data,
+ int line, const char *value)
+{
+ size_t res_len, *dst_len;
+ char **dst;
+
+ dst = (char **) (((u8 *) data->ssid) + (long) data->param1);
+ dst_len = (size_t *) (((u8 *) data->ssid) + (long) data->param2);
+
+ free(*dst);
+ *dst = wpa_config_parse_string(value, &res_len);
+ if (*dst == NULL) {
+ wpa_printf(MSG_ERROR, "Line %d: failed to parse %s '%s'.",
+ line, data->name, value);
+ return -1;
+ }
+ if (data->param2)
+ *dst_len = res_len;
+
+ if (data->key_data) {
+ wpa_hexdump_ascii_key(MSG_MSGDUMP, data->name,
+ (u8 *) *dst, res_len);
+ } else {
+ wpa_hexdump_ascii(MSG_MSGDUMP, data->name,
+ (u8 *) *dst, res_len);
+ }
+
+ if (data->param3 && res_len < (size_t) data->param3) {
+ wpa_printf(MSG_ERROR, "Line %d: too short %s (len=%lu "
+ "min_len=%ld)", line, data->name,
+ (unsigned long) res_len, (long) data->param3);
+ free(*dst);
+ *dst = NULL;
+ return -1;
+ }
+
+ if (data->param4 && res_len > (size_t) data->param4) {
+ wpa_printf(MSG_ERROR, "Line %d: too long %s (len=%lu "
+ "max_len=%ld)", line, data->name,
+ (unsigned long) res_len, (long) data->param4);
+ free(*dst);
+ *dst = NULL;
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int wpa_config_parse_int(struct parse_data *data,
+ int line, const char *value)
+{
+ int *dst;
+
+ dst = (int *) (((u8 *) data->ssid) + (long) data->param1);
+ *dst = atoi(value);
+ wpa_printf(MSG_MSGDUMP, "%s=%d (0x%x)", data->name, *dst, *dst);
+
+ if (data->param3 && *dst < (long) data->param3) {
+ wpa_printf(MSG_ERROR, "Line %d: too small %s (value=%d "
+ "min_value=%ld)", line, data->name, *dst,
+ (long) data->param3);
+ *dst = (long) data->param3;
+ return -1;
+ }
+
+ if (data->param4 && *dst > (long) data->param4) {
+ wpa_printf(MSG_ERROR, "Line %d: too large %s (value=%d "
+ "max_value=%ld)", line, data->name, *dst,
+ (long) data->param4);
+ *dst = (long) data->param4;
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int wpa_config_parse_bssid(struct parse_data *data, int line,
+ const char *value)
+{
+ if (hwaddr_aton(value, data->ssid->bssid)) {
+ wpa_printf(MSG_ERROR, "Line %d: Invalid BSSID '%s'.",
+ line, value);
+ return -1;
+ }
+ data->ssid->bssid_set = 1;
+ wpa_hexdump(MSG_MSGDUMP, "BSSID", data->ssid->bssid, ETH_ALEN);
+ return 0;
+}
+
+
+static int wpa_config_parse_psk(struct parse_data *data, int line,
+ const char *value)
+{
+ if (*value == '"') {
+ char *pos;
+ int len;
+
+ value++;
+ pos = strrchr(value, '"');
+ if (pos)
+ *pos = '\0';
+ len = strlen(value);
+ if (len < 8 || len > 63) {
+ wpa_printf(MSG_ERROR, "Line %d: Invalid passphrase "
+ "length %d (expected: 8..63) '%s'.",
+ line, len, value);
+ return -1;
+ }
+ wpa_hexdump_ascii_key(MSG_MSGDUMP, "PSK (ASCII passphrase)",
+ (u8 *) value, len);
+ data->ssid->passphrase = strdup(value);
+ return data->ssid->passphrase == NULL ? -1 : 0;
+ }
+
+ if (hexstr2bin(value, data->ssid->psk, PMK_LEN) ||
+ value[PMK_LEN * 2] != '\0') {
+ wpa_printf(MSG_ERROR, "Line %d: Invalid PSK '%s'.",
+ line, value);
+ return -1;
+ }
+ data->ssid->psk_set = 1;
+ wpa_hexdump_key(MSG_MSGDUMP, "PSK", data->ssid->psk, PMK_LEN);
+ return 0;
+}
+
+
+static int wpa_config_parse_proto(struct parse_data *data, int line,
+ const char *value)
+{
+ int val = 0, last, errors = 0;
+ char *start, *end, *buf;
+
+ buf = 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 (strcmp(start, "WPA") == 0)
+ val |= WPA_PROTO_WPA;
+ else if (strcmp(start, "RSN") == 0 ||
+ strcmp(start, "WPA2") == 0)
+ val |= WPA_PROTO_RSN;
+ else {
+ wpa_printf(MSG_ERROR, "Line %d: invalid proto '%s'",
+ line, start);
+ errors++;
+ }
+
+ if (last)
+ break;
+ start = end + 1;
+ }
+ free(buf);
+
+ if (val == 0) {
+ wpa_printf(MSG_ERROR,
+ "Line %d: no proto values configured.", line);
+ errors++;
+ }
+
+ wpa_printf(MSG_MSGDUMP, "proto: 0x%x", val);
+ data->ssid->proto = val;
+ return errors ? -1 : 0;
+}
+
+
+static int wpa_config_parse_key_mgmt(struct parse_data *data, int line,
+ const char *value)
+{
+ int val = 0, last, errors = 0;
+ char *start, *end, *buf;
+
+ buf = 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 (strcmp(start, "WPA-PSK") == 0)
+ val |= WPA_KEY_MGMT_PSK;
+ else if (strcmp(start, "WPA-EAP") == 0)
+ val |= WPA_KEY_MGMT_IEEE8021X;
+ else if (strcmp(start, "IEEE8021X") == 0)
+ val |= WPA_KEY_MGMT_IEEE8021X_NO_WPA;
+ else if (strcmp(start, "NONE") == 0)
+ val |= WPA_KEY_MGMT_NONE;
+ else if (strcmp(start, "WPA-NONE") == 0)
+ val |= WPA_KEY_MGMT_WPA_NONE;
+ else {
+ wpa_printf(MSG_ERROR, "Line %d: invalid key_mgmt '%s'",
+ line, start);
+ errors++;
+ }
+
+ if (last)
+ break;
+ start = end + 1;
+ }
+ free(buf);
+
+ if (val == 0) {
+ wpa_printf(MSG_ERROR,
+ "Line %d: no key_mgmt values configured.", line);
+ errors++;
+ }
+
+ wpa_printf(MSG_MSGDUMP, "key_mgmt: 0x%x", val);
+ data->ssid->key_mgmt = val;
+ return errors ? -1 : 0;
+}
+
+
+static int wpa_config_parse_cipher(int line, const char *value)
+{
+ int val = 0, last;
+ char *start, *end, *buf;
+
+ buf = 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 (strcmp(start, "CCMP") == 0)
+ val |= WPA_CIPHER_CCMP;
+ else if (strcmp(start, "TKIP") == 0)
+ val |= WPA_CIPHER_TKIP;
+ else if (strcmp(start, "WEP104") == 0)
+ val |= WPA_CIPHER_WEP104;
+ else if (strcmp(start, "WEP40") == 0)
+ val |= WPA_CIPHER_WEP40;
+ else if (strcmp(start, "NONE") == 0)
+ val |= WPA_CIPHER_NONE;
+ else {
+ wpa_printf(MSG_ERROR, "Line %d: invalid cipher '%s'.",
+ line, start);
+ free(buf);
+ return -1;
+ }
+
+ if (last)
+ break;
+ start = end + 1;
+ }
+ free(buf);
+
+ if (val == 0) {
+ wpa_printf(MSG_ERROR, "Line %d: no cipher values configured.",
+ line);
+ return -1;
+ }
+ return val;
+}
+
+
+static int wpa_config_parse_pairwise(struct parse_data *data, int line,
+ const char *value)
+{
+ int val;
+ val = wpa_config_parse_cipher(line, value);
+ if (val == -1)
+ return -1;
+ if (val & ~(WPA_CIPHER_CCMP | WPA_CIPHER_TKIP | WPA_CIPHER_NONE)) {
+ wpa_printf(MSG_ERROR, "Line %d: not allowed pairwise cipher "
+ "(0x%x).", line, val);
+ return -1;
+ }
+
+ wpa_printf(MSG_MSGDUMP, "pairwise: 0x%x", val);
+ data->ssid->pairwise_cipher = val;
+ return 0;
+}
+
+
+static int wpa_config_parse_group(struct parse_data *data, int line,
+ const char *value)
+{
+ int val;
+ 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)) {
+ wpa_printf(MSG_ERROR, "Line %d: not allowed group cipher "
+ "(0x%x).", line, val);
+ return -1;
+ }
+
+ wpa_printf(MSG_MSGDUMP, "group: 0x%x", val);
+ data->ssid->group_cipher = val;
+ return 0;
+}
+
+
+static int wpa_config_parse_auth_alg(struct parse_data *data, int line,
+ const char *value)
+{
+ int val = 0, last, errors = 0;
+ char *start, *end, *buf;
+
+ buf = 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 (strcmp(start, "OPEN") == 0)
+ val |= WPA_AUTH_ALG_OPEN;
+ else if (strcmp(start, "SHARED") == 0)
+ val |= WPA_AUTH_ALG_SHARED;
+ else if (strcmp(start, "LEAP") == 0)
+ val |= WPA_AUTH_ALG_LEAP;
+ else {
+ wpa_printf(MSG_ERROR, "Line %d: invalid auth_alg '%s'",
+ line, start);
+ errors++;
+ }
+
+ if (last)
+ break;
+ start = end + 1;
+ }
+ free(buf);
+
+ if (val == 0) {
+ wpa_printf(MSG_ERROR,
+ "Line %d: no auth_alg values configured.", line);
+ errors++;
+ }
+
+ wpa_printf(MSG_MSGDUMP, "auth_alg: 0x%x", val);
+ data->ssid->auth_alg = val;
+ return errors ? -1 : 0;
+}
+
+
+static int wpa_config_parse_eap(struct parse_data *data, int line,
+ const char *value)
+{
+ int last, errors = 0;
+ char *start, *end, *buf;
+ u8 *methods = NULL, *tmp;
+ size_t num_methods = 0;
+
+ buf = 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';
+ tmp = methods;
+ methods = realloc(methods, num_methods + 1);
+ if (methods == NULL) {
+ free(tmp);
+ return -1;
+ }
+ methods[num_methods] = eap_get_type(start);
+ if (methods[num_methods] == EAP_TYPE_NONE) {
+ wpa_printf(MSG_ERROR, "Line %d: unknown EAP method "
+ "'%s'", line, start);
+ wpa_printf(MSG_ERROR, "You may need to add support for"
+ " this EAP method during wpa_supplicant\n"
+ "build time configuration.\n"
+ "See README for more information.");
+ errors++;
+ } else if (methods[num_methods] == EAP_TYPE_LEAP)
+ data->ssid->leap++;
+ else
+ data->ssid->non_leap++;
+ num_methods++;
+ if (last)
+ break;
+ start = end + 1;
+ }
+ free(buf);
+
+ tmp = methods;
+ methods = realloc(methods, num_methods + 1);
+ if (methods == NULL) {
+ free(tmp);
+ return -1;
+ }
+ methods[num_methods] = EAP_TYPE_NONE;
+ num_methods++;
+
+ wpa_hexdump(MSG_MSGDUMP, "eap methods", methods, num_methods);
+ data->ssid->eap_methods = methods;
+ return errors ? -1 : 0;
+}
+
+
+static int wpa_config_parse_wep_key(u8 *key, size_t *len, int line,
+ const char *value, int idx)
+{
+ char *buf, title[20];
+
+ buf = wpa_config_parse_string(value, len);
+ if (buf == NULL) {
+ wpa_printf(MSG_ERROR, "Line %d: Invalid WEP key %d '%s'.",
+ line, idx, value);
+ return -1;
+ }
+ if (*len > MAX_WEP_KEY_LEN) {
+ wpa_printf(MSG_ERROR, "Line %d: Too long WEP key %d '%s'.",
+ line, idx, value);
+ free(buf);
+ return -1;
+ }
+ memcpy(key, buf, *len);
+ free(buf);
+ snprintf(title, sizeof(title), "wep_key%d", idx);
+ wpa_hexdump_key(MSG_MSGDUMP, title, key, *len);
+ return 0;
+}
+
+
+static int wpa_config_parse_wep_key0(struct parse_data *data, int line,
+ const char *value)
+{
+ return wpa_config_parse_wep_key(data->ssid->wep_key[0],
+ &data->ssid->wep_key_len[0], line,
+ value, 0);
+}
+
+
+static int wpa_config_parse_wep_key1(struct parse_data *data, int line,
+ const char *value)
+{
+ return wpa_config_parse_wep_key(data->ssid->wep_key[1],
+ &data->ssid->wep_key_len[1], line,
+ value, 1);
+}
+
+
+static int wpa_config_parse_wep_key2(struct parse_data *data, int line,
+ const char *value)
+{
+ return wpa_config_parse_wep_key(data->ssid->wep_key[2],
+ &data->ssid->wep_key_len[2], line,
+ value, 2);
+}
+
+
+static int wpa_config_parse_wep_key3(struct parse_data *data, int line,
+ const char *value)
+{
+ return wpa_config_parse_wep_key(data->ssid->wep_key[3],
+ &data->ssid->wep_key_len[3], line,
+ value, 3);
+}
+
+
+#define OFFSET(v) ((void *) &((struct wpa_ssid *) 0)->v)
+#define STR(f) .name = #f, .parser = wpa_config_parse_str, .param1 = OFFSET(f)
+#define STR_LEN(f) STR(f), .param2 = OFFSET(f ## _len)
+#define STR_RANGE(f, min, max) STR_LEN(f), .param3 = (void *) (min), \
+ .param4 = (void *) (max)
+#define INT(f) .name = #f, .parser = wpa_config_parse_int, \
+ .param1 = OFFSET(f), .param2 = (void *) 0
+#define INT_RANGE(f, min, max) INT(f), .param3 = (void *) (min), \
+ .param4 = (void *) (max)
+#define FUNC(f) .name = #f, .parser = wpa_config_parse_ ## f
+
+static struct parse_data ssid_fields[] = {
+ { STR_RANGE(ssid, 0, MAX_SSID_LEN) },
+ { INT_RANGE(scan_ssid, 0, 1) },
+ { FUNC(bssid) },
+ { FUNC(psk), .key_data = 1 },
+ { FUNC(proto) },
+ { FUNC(key_mgmt) },
+ { FUNC(pairwise) },
+ { FUNC(group) },
+ { FUNC(auth_alg) },
+ { FUNC(eap) },
+ { STR_LEN(identity) },
+ { STR_LEN(anonymous_identity) },
+ { STR_RANGE(eappsk, EAP_PSK_LEN, EAP_PSK_LEN), .key_data = 1 },
+ { STR_LEN(nai) },
+ { STR_LEN(server_nai) },
+ { STR_LEN(password), .key_data = 1 },
+ { STR(ca_cert) },
+ { STR(client_cert) },
+ { STR(private_key) },
+ { STR(private_key_passwd), .key_data = 1 },
+ { STR(dh_file) },
+ { STR(subject_match) },
+ { STR(ca_cert2) },
+ { STR(client_cert2) },
+ { STR(private_key2) },
+ { STR(private_key2_passwd), .key_data = 1 },
+ { STR(dh_file2) },
+ { STR(subject_match2) },
+ { STR(phase1) },
+ { STR(phase2) },
+ { STR(pcsc) },
+ { STR(pin), .key_data = 1 },
+ { INT(eapol_flags) },
+ { FUNC(wep_key0), .key_data = 1 },
+ { FUNC(wep_key1), .key_data = 1 },
+ { FUNC(wep_key2), .key_data = 1 },
+ { FUNC(wep_key3), .key_data = 1 },
+ { INT(wep_tx_keyidx) },
+ { INT(priority) },
+ { INT(eap_workaround) },
+ { STR(pac_file) },
+ { INT_RANGE(mode, 0, 1) },
+};
+
+#undef OFFSET
+#undef STR
+#undef STR_LEN
+#undef STR_RANGE
+#undef INT
+#undef INT_RANGE
+#undef FUNC
+#define NUM_SSID_FIELDS (sizeof(ssid_fields) / sizeof(ssid_fields[0]))
+
+
+static struct wpa_ssid * wpa_config_read_network(FILE *f, int *line, int id)
+{
+ struct wpa_ssid *ssid;
+ int errors = 0, i, end = 0;
+ char buf[256], *pos, *pos2;
+
+ wpa_printf(MSG_MSGDUMP, "Line: %d - start of a new network block",
+ *line);
+ ssid = (struct wpa_ssid *) malloc(sizeof(*ssid));
+ if (ssid == NULL)
+ return NULL;
+ memset(ssid, 0, sizeof(*ssid));
+ ssid->id = id;
+
+ ssid->proto = WPA_PROTO_WPA | WPA_PROTO_RSN;
+ ssid->pairwise_cipher = WPA_CIPHER_CCMP | WPA_CIPHER_TKIP;
+ ssid->group_cipher = WPA_CIPHER_CCMP | WPA_CIPHER_TKIP |
+ WPA_CIPHER_WEP104 | WPA_CIPHER_WEP40;
+ ssid->key_mgmt = WPA_KEY_MGMT_PSK | WPA_KEY_MGMT_IEEE8021X;
+ ssid->eapol_flags = EAPOL_FLAG_REQUIRE_KEY_UNICAST |
+ EAPOL_FLAG_REQUIRE_KEY_BROADCAST;
+ ssid->eap_workaround = (unsigned int) -1;
+
+ while ((pos = wpa_config_get_line(buf, sizeof(buf), f, line))) {
+ if (strcmp(pos, "}") == 0) {
+ end = 1;
+ break;
+ }
+
+ pos2 = strchr(pos, '=');
+ if (pos2 == NULL) {
+ wpa_printf(MSG_ERROR, "Line %d: Invalid SSID line "
+ "'%s'.", *line, pos);
+ errors++;
+ continue;
+ }
+
+ *pos2++ = '\0';
+ if (*pos2 == '"') {
+ if (strchr(pos2 + 1, '"') == NULL) {
+ wpa_printf(MSG_ERROR, "Line %d: invalid "
+ "quotation '%s'.", *line, pos2);
+ errors++;
+ continue;
+ }
+ }
+
+ for (i = 0; i < NUM_SSID_FIELDS; i++) {
+ struct parse_data *field = &ssid_fields[i];
+ if (strcmp(pos, field->name) != 0)
+ continue;
+
+ field->ssid = ssid;
+ if (field->parser(field, *line, pos2)) {
+ wpa_printf(MSG_ERROR, "Line %d: failed to "
+ "parse %s '%s'.", *line, pos, pos2);
+ errors++;
+ }
+ break;
+ }
+ if (i == NUM_SSID_FIELDS) {
+ wpa_printf(MSG_ERROR, "Line %d: unknown network field "
+ "'%s'.", *line, pos);
+ errors++;
+ }
+ }
+
+ if (!end) {
+ wpa_printf(MSG_ERROR, "Line %d: network block was not "
+ "terminated properly.", *line);
+ errors++;
+ }
+
+ if (ssid->passphrase) {
+ if (ssid->psk_set) {
+ wpa_printf(MSG_ERROR, "Line %d: both PSK and "
+ "passphrase configured.", *line);
+ errors++;
+ }
+ pbkdf2_sha1(ssid->passphrase,
+ (char *) ssid->ssid, ssid->ssid_len, 4096,
+ ssid->psk, PMK_LEN);
+ wpa_hexdump_key(MSG_MSGDUMP, "PSK (from passphrase)",
+ ssid->psk, PMK_LEN);
+ ssid->psk_set = 1;
+ }
+
+ if ((ssid->key_mgmt & WPA_KEY_MGMT_PSK) && !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)) {
+ /* Group cipher cannot be stronger than the pairwise cipher. */
+ wpa_printf(MSG_DEBUG, "Line %d: removed CCMP from group cipher"
+ " list since it was not allowed for pairwise "
+ "cipher", *line);
+ ssid->group_cipher &= ~WPA_CIPHER_CCMP;
+ }
+
+ if (errors) {
+ free(ssid);
+ ssid = NULL;
+ }
+
+ return ssid;
+}
+
+
+static int wpa_config_add_prio_network(struct wpa_config *config,
+ struct wpa_ssid *ssid)
+{
+ int prio;
+ struct wpa_ssid *prev, **nlist;
+
+ for (prio = 0; prio < config->num_prio; prio++) {
+ prev = config->pssid[prio];
+ if (prev->priority == ssid->priority) {
+ while (prev->pnext)
+ prev = prev->pnext;
+ prev->pnext = ssid;
+ return 0;
+ }
+ }
+
+ /* First network for this priority - add new priority list */
+ nlist = realloc(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)
+ break;
+ }
+
+ memmove(&nlist[prio + 1], &nlist[prio],
+ (config->num_prio - prio) * sizeof(struct wpa_ssid *));
+
+ nlist[prio] = ssid;
+ config->num_prio++;
+ config->pssid = nlist;
+
+ return 0;
+}
+
+
+struct wpa_config * wpa_config_read(const char *config_file)
+{
+ FILE *f;
+ char buf[256], *pos;
+ int errors = 0, line = 0;
+ struct wpa_ssid *ssid, *tail = NULL, *head = NULL;
+ struct wpa_config *config;
+ int id = 0, prio;
+
+ config = malloc(sizeof(*config));
+ if (config == NULL)
+ return NULL;
+ memset(config, 0, sizeof(*config));
+ config->eapol_version = 1;
+ config->ap_scan = 1;
+ config->fast_reauth = 1;
+ wpa_printf(MSG_DEBUG, "Reading configuration file '%s'",
+ config_file);
+ f = fopen(config_file, "r");
+ if (f == NULL) {
+ free(config);
+ return NULL;
+ }
+
+ while ((pos = wpa_config_get_line(buf, sizeof(buf), f, &line))) {
+ if (strcmp(pos, "network={") == 0) {
+ ssid = wpa_config_read_network(f, &line, id++);
+ if (ssid == NULL) {
+ wpa_printf(MSG_ERROR, "Line %d: failed to "
+ "parse network block.", line);
+ errors++;
+ continue;
+ }
+ if (head == NULL) {
+ head = tail = ssid;
+ } else {
+ tail->next = ssid;
+ tail = ssid;
+ }
+ if (wpa_config_add_prio_network(config, ssid)) {
+ wpa_printf(MSG_ERROR, "Line %d: failed to add "
+ "network block to priority list.",
+ line);
+ errors++;
+ continue;
+ }
+#ifdef CONFIG_CTRL_IFACE
+ } else if (strncmp(pos, "ctrl_interface=", 15) == 0) {
+ free(config->ctrl_interface);
+ config->ctrl_interface = strdup(pos + 15);
+ wpa_printf(MSG_DEBUG, "ctrl_interface='%s'",
+ config->ctrl_interface);
+#ifndef CONFIG_CTRL_IFACE_UDP
+ } else if (strncmp(pos, "ctrl_interface_group=", 21) == 0) {
+ struct group *grp;
+ char *endp;
+ const char *group = pos + 21;
+
+ grp = getgrnam(group);
+ if (grp) {
+ config->ctrl_interface_gid = grp->gr_gid;
+ config->ctrl_interface_gid_set = 1;
+ wpa_printf(MSG_DEBUG, "ctrl_interface_group=%d"
+ " (from group name '%s')",
+ (int) config->ctrl_interface_gid,
+ group);
+ continue;
+ }
+
+ /* Group name not found - try to parse this as gid */
+ config->ctrl_interface_gid = strtol(group, &endp, 10);
+ if (*group == '\0' || *endp != '\0') {
+ wpa_printf(MSG_DEBUG, "Line %d: Invalid group "
+ "'%s'", line, group);
+ errors++;
+ continue;
+ }
+ wpa_printf(MSG_DEBUG, "ctrl_interface_group=%d",
+ (int) config->ctrl_interface_gid);
+#endif /* CONFIG_CTRL_IFACE_UDP */
+#endif /* CONFIG_CTRL_IFACE */
+ } else if (strncmp(pos, "eapol_version=", 14) == 0) {
+ config->eapol_version = atoi(pos + 14);
+ if (config->eapol_version < 1 ||
+ config->eapol_version > 2) {
+ wpa_printf(MSG_ERROR, "Line %d: Invalid EAPOL "
+ "version (%d): '%s'.",
+ line, config->eapol_version, pos);
+ errors++;
+ continue;
+ }
+ wpa_printf(MSG_DEBUG, "eapol_version=%d",
+ config->eapol_version);
+ } else if (strncmp(pos, "ap_scan=", 8) == 0) {
+ config->ap_scan = atoi(pos + 8);
+ wpa_printf(MSG_DEBUG, "ap_scan=%d", config->ap_scan);
+ } else if (strncmp(pos, "fast_reauth=", 12) == 0) {
+ config->fast_reauth = atoi(pos + 12);
+ wpa_printf(MSG_DEBUG, "fast_reauth=%d",
+ config->fast_reauth);
+ } else {
+ wpa_printf(MSG_ERROR, "Line %d: Invalid configuration "
+ "line '%s'.", line, pos);
+ errors++;
+ continue;
+ }
+ }
+
+ fclose(f);
+
+ config->ssid = head;
+ for (prio = 0; prio < config->num_prio; prio++) {
+ ssid = config->pssid[prio];
+ wpa_printf(MSG_DEBUG, "Priority group %d",
+ ssid->priority);
+ while (ssid) {
+ wpa_printf(MSG_DEBUG, " id=%d ssid='%s'",
+ ssid->id,
+ wpa_ssid_txt(ssid->ssid, ssid->ssid_len));
+ ssid = ssid->pnext;
+ }
+ }
+ if (errors) {
+ wpa_config_free(config);
+ config = NULL;
+ head = NULL;
+ }
+
+ return config;
+}
+
+
+void wpa_config_free(struct wpa_config *config)
+{
+ struct wpa_ssid *ssid, *prev = NULL;
+ ssid = config->ssid;
+ while (ssid) {
+ prev = ssid;
+ ssid = ssid->next;
+ free(prev->ssid);
+ free(prev->passphrase);
+ free(prev->eap_methods);
+ free(prev->identity);
+ free(prev->anonymous_identity);
+ free(prev->eappsk);
+ free(prev->nai);
+ free(prev->server_nai);
+ free(prev->password);
+ free(prev->ca_cert);
+ free(prev->client_cert);
+ free(prev->private_key);
+ free(prev->private_key_passwd);
+ free(prev->dh_file);
+ free(prev->subject_match);
+ free(prev->ca_cert2);
+ free(prev->client_cert2);
+ free(prev->private_key2);
+ free(prev->private_key2_passwd);
+ free(prev->dh_file2);
+ free(prev->subject_match2);
+ free(prev->phase1);
+ free(prev->phase2);
+ free(prev->pcsc);
+ free(prev->pin);
+ free(prev->otp);
+ free(prev->pending_req_otp);
+ free(prev->pac_file);
+ free(prev);
+ }
+ free(config->ctrl_interface);
+ free(config->pssid);
+ free(config);
+}
+
+
+int wpa_config_allowed_eap_method(struct wpa_ssid *ssid, int method)
+{
+ u8 *pos;
+
+ if (ssid == NULL || ssid->eap_methods == NULL)
+ return 1;
+
+ pos = ssid->eap_methods;
+ while (*pos != EAP_TYPE_NONE) {
+ if (*pos == method)
+ return 1;
+ pos++;
+ }
+ return 0;
+}
+
+
+const char * wpa_cipher_txt(int cipher)
+{
+ switch (cipher) {
+ case WPA_CIPHER_NONE:
+ return "NONE";
+ case WPA_CIPHER_WEP40:
+ return "WEP-40";
+ case WPA_CIPHER_WEP104:
+ return "WEP-104";
+ case WPA_CIPHER_TKIP:
+ return "TKIP";
+ case WPA_CIPHER_CCMP:
+ return "CCMP";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+
+const char * wpa_key_mgmt_txt(int key_mgmt, int proto)
+{
+ switch (key_mgmt) {
+ case WPA_KEY_MGMT_IEEE8021X:
+ return proto == WPA_PROTO_RSN ?
+ "WPA2/IEEE 802.1X/EAP" : "WPA/IEEE 802.1X/EAP";
+ case WPA_KEY_MGMT_PSK:
+ return proto == WPA_PROTO_RSN ?
+ "WPA2-PSK" : "WPA-PSK";
+ case WPA_KEY_MGMT_NONE:
+ return "NONE";
+ case WPA_KEY_MGMT_IEEE8021X_NO_WPA:
+ return "IEEE 802.1X (no WPA)";
+ default:
+ return "UNKNOWN";
+ }
+}
diff --git a/contrib/wpa_supplicant/config.h b/contrib/wpa_supplicant/config.h
new file mode 100644
index 000000000000..13deb3e3cb58
--- /dev/null
+++ b/contrib/wpa_supplicant/config.h
@@ -0,0 +1,33 @@
+#ifndef CONFIG_H
+#define CONFIG_H
+
+#ifdef CONFIG_CTRL_IFACE
+#ifndef CONFIG_CTRL_IFACE_UDP
+#include <grp.h>
+#endif /* CONFIG_CTRL_IFACE_UDP */
+#endif /* CONFIG_CTRL_IFACE */
+
+#include "config_ssid.h"
+
+struct wpa_config {
+ struct wpa_ssid *ssid; /* global network list */
+ struct wpa_ssid **pssid; /* per priority network lists (in priority
+ * order) */
+ int num_prio; /* number of different priorities */
+ int eapol_version;
+ int ap_scan;
+ char *ctrl_interface; /* directory for UNIX domain sockets */
+#ifdef CONFIG_CTRL_IFACE
+#ifndef CONFIG_CTRL_IFACE_UDP
+ gid_t ctrl_interface_gid;
+#endif /* CONFIG_CTRL_IFACE_UDP */
+ int ctrl_interface_gid_set;
+#endif /* CONFIG_CTRL_IFACE */
+ int fast_reauth;
+};
+
+
+struct wpa_config * wpa_config_read(const char *config_file);
+void wpa_config_free(struct wpa_config *ssid);
+
+#endif /* CONFIG_H */
diff --git a/contrib/wpa_supplicant/config_ssid.h b/contrib/wpa_supplicant/config_ssid.h
new file mode 100644
index 000000000000..44bc98947ced
--- /dev/null
+++ b/contrib/wpa_supplicant/config_ssid.h
@@ -0,0 +1,109 @@
+#ifndef CONFIG_SSID_H
+#define CONFIG_SSID_H
+
+#define WPA_CIPHER_NONE BIT(0)
+#define WPA_CIPHER_WEP40 BIT(1)
+#define WPA_CIPHER_WEP104 BIT(2)
+#define WPA_CIPHER_TKIP BIT(3)
+#define WPA_CIPHER_CCMP BIT(4)
+
+#define WPA_KEY_MGMT_IEEE8021X BIT(0)
+#define WPA_KEY_MGMT_PSK BIT(1)
+#define WPA_KEY_MGMT_NONE BIT(2)
+#define WPA_KEY_MGMT_IEEE8021X_NO_WPA BIT(3)
+#define WPA_KEY_MGMT_WPA_NONE BIT(4)
+
+#define WPA_PROTO_WPA BIT(0)
+#define WPA_PROTO_RSN BIT(1)
+
+#define WPA_AUTH_ALG_OPEN BIT(0)
+#define WPA_AUTH_ALG_SHARED BIT(1)
+#define WPA_AUTH_ALG_LEAP BIT(2)
+
+#define MAX_SSID_LEN 32
+#define PMK_LEN 32
+#define EAP_PSK_LEN 16
+
+struct wpa_ssid {
+ struct wpa_ssid *next; /* next network in global list */
+ struct wpa_ssid *pnext; /* next network in per-priority list */
+ int id; /* unique id for ctrl_iface */
+ int priority;
+ u8 *ssid;
+ size_t ssid_len;
+ u8 bssid[ETH_ALEN];
+ int bssid_set;
+ u8 psk[PMK_LEN];
+ int psk_set;
+ char *passphrase;
+ /* Bitfields of allowed Pairwise/Group Ciphers, WPA_CIPHER_* */
+ int pairwise_cipher;
+ int group_cipher;
+ int key_mgmt;
+ int proto; /* Bitfield of allowed protocols (WPA_PROTO_*) */
+ int auth_alg; /* Bitfield of allow authentication algorithms
+ * (WPA_AUTH_ALG_*) */
+ int scan_ssid; /* scan this SSID with Probe Requests */
+ u8 *identity; /* EAP Identity */
+ size_t identity_len;
+ u8 *anonymous_identity; /* Anonymous EAP Identity (for unencrypted use
+ * with EAP types that support different
+ * tunnelled identity, e.g., EAP-TTLS) */
+ size_t anonymous_identity_len;
+ u8 *eappsk;
+ size_t eappsk_len;
+ u8 *nai;
+ size_t nai_len;
+ u8 *server_nai;
+ size_t server_nai_len;
+ u8 *password;
+ size_t password_len;
+ u8 *ca_cert;
+ u8 *client_cert;
+ u8 *private_key;
+ u8 *private_key_passwd;
+ u8 *dh_file;
+ u8 *subject_match;
+ u8 *ca_cert2;
+ u8 *client_cert2;
+ u8 *private_key2;
+ u8 *private_key2_passwd;
+ u8 *dh_file2;
+ u8 *subject_match2;
+ u8 *eap_methods; /* zero (EAP_TYPE_NONE) terminated list of allowed
+ * EAP methods or NULL = any */
+ char *phase1;
+ char *phase2;
+ char *pcsc;
+ char *pin;
+
+#define EAPOL_FLAG_REQUIRE_KEY_UNICAST BIT(0)
+#define EAPOL_FLAG_REQUIRE_KEY_BROADCAST BIT(1)
+ int eapol_flags; /* bit field of IEEE 802.1X/EAPOL options */
+
+#define NUM_WEP_KEYS 4
+#define MAX_WEP_KEY_LEN 16
+ u8 wep_key[NUM_WEP_KEYS][MAX_WEP_KEY_LEN];
+ size_t wep_key_len[NUM_WEP_KEYS];
+ int wep_tx_keyidx;
+
+ /* Per SSID variables that are not read from the configuration file */
+ u8 *otp;
+ size_t otp_len;
+ int pending_req_identity, pending_req_password;
+ char *pending_req_otp;
+ size_t pending_req_otp_len;
+ int leap, non_leap;
+
+ unsigned int eap_workaround;
+
+ char *pac_file;
+
+ int mode;
+};
+
+int wpa_config_allowed_eap_method(struct wpa_ssid *ssid, int method);
+const char * wpa_cipher_txt(int cipher);
+const char * wpa_key_mgmt_txt(int key_mgmt, int proto);
+
+#endif /* CONFIG_SSID_H */
diff --git a/contrib/wpa_supplicant/crypto.c b/contrib/wpa_supplicant/crypto.c
new file mode 100644
index 000000000000..cd278e0fae59
--- /dev/null
+++ b/contrib/wpa_supplicant/crypto.c
@@ -0,0 +1,71 @@
+/*
+ * WPA Supplicant / wrapper functions for libcrypto
+ * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published 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 <openssl/md4.h>
+#include <openssl/des.h>
+
+#include "common.h"
+
+
+#if OPENSSL_VERSION_NUMBER < 0x00907000
+#define DES_key_schedule des_key_schedule
+#define DES_cblock des_cblock
+#define DES_set_key(key, schedule) des_set_key((key), *(schedule))
+#define DES_ecb_encrypt(input, output, ks, enc) \
+ des_ecb_encrypt((input), (output), *(ks), (enc))
+#endif /* openssl < 0.9.7 */
+
+
+void md4_vector(size_t num_elem, const u8 *addr[], size_t *len, u8 *mac)
+{
+ MD4_CTX ctx;
+ int i;
+
+ MD4_Init(&ctx);
+ for (i = 0; i < num_elem; i++)
+ MD4_Update(&ctx, addr[i], len[i]);
+ MD4_Final(mac, &ctx);
+}
+
+
+void md4(const u8 *addr, size_t len, u8 *mac)
+{
+ md4_vector(1, &addr, &len, mac);
+}
+
+
+/**
+ * @clear: 8 octets (in)
+ * @key: 7 octets (in) (no parity bits included)
+ * @cypher: 8 octets (out)
+ */
+void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher)
+{
+ u8 pkey[8], next, tmp;
+ int i;
+ DES_key_schedule ks;
+
+ /* Add parity bits to the key */
+ next = 0;
+ for (i = 0; i < 7; i++) {
+ tmp = key[i];
+ pkey[i] = (tmp >> i) | next | 1;
+ next = tmp << (7 - i);
+ }
+ pkey[i] = next | 1;
+
+ DES_set_key(&pkey, &ks);
+ DES_ecb_encrypt((DES_cblock *) clear, (DES_cblock *) cypher, &ks,
+ DES_ENCRYPT);
+}
diff --git a/contrib/wpa_supplicant/crypto.h b/contrib/wpa_supplicant/crypto.h
new file mode 100644
index 000000000000..3e1a0e5cd3f8
--- /dev/null
+++ b/contrib/wpa_supplicant/crypto.h
@@ -0,0 +1,8 @@
+#ifndef CRYPTO_H
+#define CRYPTO_H
+
+void md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac);
+void md4(const u8 *addr, size_t len, u8 *mac);
+void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher);
+
+#endif /* CRYPTO_H */
diff --git a/contrib/wpa_supplicant/ctrl_iface.c b/contrib/wpa_supplicant/ctrl_iface.c
new file mode 100644
index 000000000000..e41881104ede
--- /dev/null
+++ b/contrib/wpa_supplicant/ctrl_iface.c
@@ -0,0 +1,728 @@
+/*
+ * WPA Supplicant / UNIX domain socket -based control interface
+ * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published 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 <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#ifndef CONFIG_NATIVE_WINDOWS
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/uio.h>
+#endif /* CONFIG_NATIVE_WINDOWS */
+
+#include "common.h"
+#include "eloop.h"
+#include "wpa.h"
+#include "wpa_supplicant.h"
+#include "config.h"
+#include "eapol_sm.h"
+#include "wpa_supplicant_i.h"
+#include "ctrl_iface.h"
+#include "l2_packet.h"
+
+
+#ifdef CONFIG_NATIVE_WINDOWS
+typedef int socklen_t;
+#endif /* CONFIG_NATIVE_WINDOWS */
+
+#ifdef CONFIG_CTRL_IFACE_UDP
+#define CTRL_IFACE_SOCK struct sockaddr_in
+#else /* CONFIG_CTRL_IFACE_UDP */
+#define CTRL_IFACE_SOCK struct sockaddr_un
+#endif /* CONFIG_CTRL_IFACE_UDP */
+
+
+struct wpa_ctrl_dst {
+ struct wpa_ctrl_dst *next;
+ CTRL_IFACE_SOCK addr;
+ socklen_t addrlen;
+ int debug_level;
+ int errors;
+};
+
+
+static const char * wpa_state_txt(int state)
+{
+ switch (state) {
+ case WPA_DISCONNECTED:
+ return "DISCONNECTED";
+ case WPA_SCANNING:
+ return "SCANNING";
+ case WPA_ASSOCIATING:
+ return "ASSOCIATING";
+ case WPA_ASSOCIATED:
+ return "ASSOCIATED";
+ case WPA_4WAY_HANDSHAKE:
+ return "4WAY_HANDSHAKE";
+ case WPA_GROUP_HANDSHAKE:
+ return "GROUP_HANDSHAKE";
+ case WPA_COMPLETED:
+ return "COMPLETED";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+
+static int wpa_supplicant_ctrl_iface_set(struct wpa_supplicant *wpa_s,
+ char *cmd)
+{
+ char *value;
+
+ value = strchr(cmd, ' ');
+ if (value == NULL)
+ return -1;
+ *value++ = '\0';
+
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE SET '%s'='%s'", cmd, value);
+ if (strcasecmp(cmd, "EAPOL::heldPeriod") == 0) {
+ eapol_sm_configure(wpa_s->eapol,
+ atoi(value), -1, -1, -1);
+ } else if (strcasecmp(cmd, "EAPOL::authPeriod") == 0) {
+ eapol_sm_configure(wpa_s->eapol,
+ -1, atoi(value), -1, -1);
+ } else if (strcasecmp(cmd, "EAPOL::startPeriod") == 0) {
+ eapol_sm_configure(wpa_s->eapol,
+ -1, -1, atoi(value), -1);
+ } else if (strcasecmp(cmd, "EAPOL::maxStart") == 0) {
+ eapol_sm_configure(wpa_s->eapol,
+ -1, -1, -1, atoi(value));
+ } else
+ return -1;
+ return 0;
+}
+
+
+static int wpa_supplicant_ctrl_iface_preauth(struct wpa_supplicant *wpa_s,
+ char *addr)
+{
+ u8 bssid[ETH_ALEN];
+
+ if (hwaddr_aton(addr, bssid)) {
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE PREAUTH: invalid address "
+ "'%s'", addr);
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE PREAUTH " MACSTR, MAC2STR(bssid));
+ rsn_preauth_deinit(wpa_s);
+ if (rsn_preauth_init(wpa_s, bssid))
+ return -1;
+
+ return 0;
+}
+
+
+static int wpa_supplicant_ctrl_iface_attach(struct wpa_supplicant *wpa_s,
+ CTRL_IFACE_SOCK *from,
+ socklen_t fromlen)
+{
+ struct wpa_ctrl_dst *dst;
+
+ dst = malloc(sizeof(*dst));
+ if (dst == NULL)
+ return -1;
+ memset(dst, 0, sizeof(*dst));
+ memcpy(&dst->addr, from, sizeof(CTRL_IFACE_SOCK));
+ dst->addrlen = fromlen;
+ dst->debug_level = MSG_INFO;
+ dst->next = wpa_s->ctrl_dst;
+ wpa_s->ctrl_dst = dst;
+#ifdef CONFIG_CTRL_IFACE_UDP
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor attached %s:%d",
+ inet_ntoa(from->sin_addr), ntohs(from->sin_port));
+#else /* CONFIG_CTRL_IFACE_UDP */
+ wpa_hexdump(MSG_DEBUG, "CTRL_IFACE monitor attached",
+ (u8 *) from->sun_path, fromlen);
+#endif /* CONFIG_CTRL_IFACE_UDP */
+ return 0;
+}
+
+
+static int wpa_supplicant_ctrl_iface_detach(struct wpa_supplicant *wpa_s,
+ CTRL_IFACE_SOCK *from,
+ socklen_t fromlen)
+{
+ struct wpa_ctrl_dst *dst, *prev = NULL;
+
+ dst = wpa_s->ctrl_dst;
+ while (dst) {
+#ifdef CONFIG_CTRL_IFACE_UDP
+ if (from->sin_addr.s_addr == dst->addr.sin_addr.s_addr &&
+ from->sin_port == dst->addr.sin_port) {
+ if (prev == NULL)
+ wpa_s->ctrl_dst = dst->next;
+ else
+ prev->next = dst->next;
+ free(dst);
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor detached "
+ "%s:%d", inet_ntoa(from->sin_addr),
+ ntohs(from->sin_port));
+ return 0;
+ }
+#else /* CONFIG_CTRL_IFACE_UDP */
+ if (fromlen == dst->addrlen &&
+ memcmp(from->sun_path, dst->addr.sun_path, fromlen) == 0) {
+ if (prev == NULL)
+ wpa_s->ctrl_dst = dst->next;
+ else
+ prev->next = dst->next;
+ free(dst);
+ wpa_hexdump(MSG_DEBUG, "CTRL_IFACE monitor detached",
+ (u8 *) from->sun_path, fromlen);
+ return 0;
+ }
+#endif /* CONFIG_CTRL_IFACE_UDP */
+ prev = dst;
+ dst = dst->next;
+ }
+ return -1;
+}
+
+
+static int wpa_supplicant_ctrl_iface_level(struct wpa_supplicant *wpa_s,
+ CTRL_IFACE_SOCK *from,
+ socklen_t fromlen,
+ char *level)
+{
+ struct wpa_ctrl_dst *dst;
+
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE LEVEL %s", level);
+
+ dst = wpa_s->ctrl_dst;
+ while (dst) {
+#ifdef CONFIG_CTRL_IFACE_UDP
+ 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 changed monitor "
+ "level %s:%d", inet_ntoa(from->sin_addr),
+ ntohs(from->sin_port));
+ dst->debug_level = atoi(level);
+ return 0;
+ }
+#else /* CONFIG_CTRL_IFACE_UDP */
+ if (fromlen == dst->addrlen &&
+ memcmp(from->sun_path, dst->addr.sun_path, fromlen) == 0) {
+ wpa_hexdump(MSG_DEBUG, "CTRL_IFACE changed monitor "
+ "level", (u8 *) from->sun_path, fromlen);
+ dst->debug_level = atoi(level);
+ return 0;
+ }
+#endif /* CONFIG_CTRL_IFACE_UDP */
+ dst = dst->next;
+ }
+
+ return -1;
+}
+
+
+static int wpa_supplicant_ctrl_iface_ctrl_rsp(struct wpa_supplicant *wpa_s,
+ char *rsp)
+{
+ char *pos, *id_pos;
+ int id;
+ struct wpa_ssid *ssid;
+
+ pos = strchr(rsp, '-');
+ if (pos == NULL)
+ return -1;
+ *pos++ = '\0';
+ id_pos = pos;
+ pos = strchr(pos, ':');
+ if (pos == NULL)
+ return -1;
+ *pos++ = '\0';
+ id = atoi(id_pos);
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE: field=%s id=%d value='%s'",
+ rsp, id, pos);
+
+ ssid = wpa_s->conf->ssid;
+ while (ssid) {
+ if (id == ssid->id)
+ break;
+ ssid = ssid->next;
+ }
+
+ if (ssid == NULL) {
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find SSID id=%d "
+ "to update", id);
+ return -1;
+ }
+
+ if (strcmp(rsp, "IDENTITY") == 0) {
+ free(ssid->identity);
+ ssid->identity = (u8 *) strdup(pos);
+ ssid->identity_len = strlen(pos);
+ ssid->pending_req_identity = 0;
+ if (ssid == wpa_s->current_ssid)
+ wpa_s->reassociate = 1;
+ } else if (strcmp(rsp, "PASSWORD") == 0) {
+ free(ssid->password);
+ ssid->password = (u8 *) strdup(pos);
+ ssid->password_len = strlen(pos);
+ ssid->pending_req_password = 0;
+ if (ssid == wpa_s->current_ssid)
+ wpa_s->reassociate = 1;
+ } else if (strcmp(rsp, "OTP") == 0) {
+ free(ssid->otp);
+ ssid->otp = (u8 *) strdup(pos);
+ ssid->otp_len = strlen(pos);
+ free(ssid->pending_req_otp);
+ ssid->pending_req_otp = NULL;
+ ssid->pending_req_otp_len = 0;
+ } else {
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown field '%s'", rsp);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int wpa_supplicant_ctrl_iface_status(struct wpa_supplicant *wpa_s,
+ const char *params,
+ char *buf, size_t buflen)
+{
+ char *pos, *end;
+ int res, verbose;
+
+ verbose = strcmp(params, "-VERBOSE") == 0;
+ pos = buf;
+ end = buf + buflen;
+ pos += snprintf(pos, end - pos, "bssid=" MACSTR "\n",
+ MAC2STR(wpa_s->bssid));
+ if (wpa_s->current_ssid) {
+ pos += snprintf(pos, end - pos, "ssid=%s\n",
+ wpa_ssid_txt(wpa_s->current_ssid->ssid,
+ wpa_s->current_ssid->ssid_len));
+ }
+ pos += snprintf(pos, end - pos,
+ "pairwise_cipher=%s\n"
+ "group_cipher=%s\n"
+ "key_mgmt=%s\n"
+ "wpa_state=%s\n",
+ wpa_cipher_txt(wpa_s->pairwise_cipher),
+ wpa_cipher_txt(wpa_s->group_cipher),
+ wpa_key_mgmt_txt(wpa_s->key_mgmt, wpa_s->proto),
+ wpa_state_txt(wpa_s->wpa_state));
+
+ res = eapol_sm_get_status(wpa_s->eapol, pos, end - pos, verbose);
+ if (res >= 0)
+ pos += res;
+
+ if (wpa_s->preauth_eapol) {
+ pos += snprintf(pos, end - pos, "Pre-authentication "
+ "EAPOL state machines:\n");
+ res = eapol_sm_get_status(wpa_s->preauth_eapol,
+ pos, end - pos, verbose);
+ if (res >= 0)
+ pos += res;
+ }
+
+ return pos - buf;
+}
+
+
+static void wpa_supplicant_ctrl_iface_receive(int sock, void *eloop_ctx,
+ void *sock_ctx)
+{
+ struct wpa_supplicant *wpa_s = eloop_ctx;
+ char buf[256];
+ int res;
+ CTRL_IFACE_SOCK from;
+ socklen_t fromlen = sizeof(from);
+ char *reply;
+ const int reply_size = 2048;
+ int reply_len;
+ int new_attached = 0, ctrl_rsp = 0;
+
+ res = recvfrom(sock, buf, sizeof(buf) - 1, 0,
+ (struct sockaddr *) &from, &fromlen);
+ if (res < 0) {
+ perror("recvfrom(ctrl_iface)");
+ return;
+ }
+ buf[res] = '\0';
+ if (strncmp(buf, "CTRL-RSP-", 9) == 0) {
+ wpa_hexdump_ascii_key(MSG_DEBUG, "RX ctrl_iface",
+ (u8 *) buf, res);
+ } else {
+ wpa_hexdump_ascii(MSG_DEBUG, "RX ctrl_iface", (u8 *) buf, res);
+ }
+
+ reply = malloc(reply_size);
+ if (reply == NULL) {
+ sendto(sock, "FAIL\n", 5, 0, (struct sockaddr *) &from,
+ fromlen);
+ return;
+ }
+
+ memcpy(reply, "OK\n", 3);
+ reply_len = 3;
+
+ if (strcmp(buf, "PING") == 0) {
+ memcpy(reply, "PONG\n", 5);
+ reply_len = 5;
+ } else if (strcmp(buf, "MIB") == 0) {
+ reply_len = wpa_get_mib(wpa_s, reply, reply_size);
+ if (reply_len >= 0) {
+ res = eapol_sm_get_mib(wpa_s->eapol, reply + reply_len,
+ reply_size - reply_len);
+ if (res < 0)
+ reply_len = -1;
+ else
+ reply_len += res;
+ }
+ } else if (strncmp(buf, "STATUS", 6) == 0) {
+ reply_len = wpa_supplicant_ctrl_iface_status(
+ wpa_s, buf + 6, reply, reply_size);
+ } else if (strcmp(buf, "PMKSA") == 0) {
+ reply_len = pmksa_cache_list(wpa_s, reply, reply_size);
+ } else if (strncmp(buf, "SET ", 4) == 0) {
+ if (wpa_supplicant_ctrl_iface_set(wpa_s, buf + 4))
+ reply_len = -1;
+ } else if (strcmp(buf, "LOGON") == 0) {
+ eapol_sm_notify_logoff(wpa_s->eapol, FALSE);
+ } else if (strcmp(buf, "LOGOFF") == 0) {
+ eapol_sm_notify_logoff(wpa_s->eapol, TRUE);
+ } else if (strcmp(buf, "REASSOCIATE") == 0) {
+ wpa_s->reassociate = 1;
+ wpa_supplicant_req_scan(wpa_s, 0, 0);
+ } else if (strncmp(buf, "PREAUTH ", 8) == 0) {
+ if (wpa_supplicant_ctrl_iface_preauth(wpa_s, buf + 8))
+ reply_len = -1;
+ } else if (strcmp(buf, "ATTACH") == 0) {
+ if (wpa_supplicant_ctrl_iface_attach(wpa_s, &from, fromlen))
+ reply_len = -1;
+ else
+ new_attached = 1;
+ } else if (strcmp(buf, "DETACH") == 0) {
+ if (wpa_supplicant_ctrl_iface_detach(wpa_s, &from, fromlen))
+ reply_len = -1;
+ } else if (strncmp(buf, "LEVEL ", 6) == 0) {
+ if (wpa_supplicant_ctrl_iface_level(wpa_s, &from, fromlen,
+ buf + 6))
+ reply_len = -1;
+ } else if (strncmp(buf, "CTRL-RSP-", 9) == 0) {
+ if (wpa_supplicant_ctrl_iface_ctrl_rsp(wpa_s, buf + 9))
+ reply_len = -1;
+ else
+ ctrl_rsp = 1;
+ } else if (strcmp(buf, "RECONFIGURE") == 0) {
+ if (wpa_supplicant_reload_configuration(wpa_s))
+ reply_len = -1;
+ } else if (strcmp(buf, "TERMINATE") == 0) {
+ eloop_terminate();
+ } else {
+ memcpy(reply, "UNKNOWN COMMAND\n", 16);
+ reply_len = 16;
+ }
+
+ if (reply_len < 0) {
+ memcpy(reply, "FAIL\n", 5);
+ reply_len = 5;
+ }
+ sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from, fromlen);
+ free(reply);
+
+ if (new_attached)
+ eapol_sm_notify_ctrl_attached(wpa_s->eapol);
+ if (ctrl_rsp)
+ eapol_sm_notify_ctrl_response(wpa_s->eapol);
+}
+
+
+static char * wpa_supplicant_ctrl_iface_path(struct wpa_supplicant *wpa_s)
+{
+ char *buf;
+ size_t len;
+
+ if (wpa_s->conf->ctrl_interface == NULL)
+ return NULL;
+
+ len = strlen(wpa_s->conf->ctrl_interface) + strlen(wpa_s->ifname) + 2;
+ buf = malloc(len);
+ if (buf == NULL)
+ return NULL;
+
+ snprintf(buf, len, "%s/%s",
+ wpa_s->conf->ctrl_interface, wpa_s->ifname);
+#ifdef __CYGWIN__
+ {
+ /* Windows/WinPcap uses interface names that are not suitable
+ * as a file name - convert invalid chars to underscores */
+ char *pos = buf;
+ while (*pos) {
+ if (*pos == '\\')
+ *pos = '_';
+ pos++;
+ }
+ }
+#endif /* __CYGWIN__ */
+ return buf;
+}
+
+
+int wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s)
+{
+ CTRL_IFACE_SOCK addr;
+ int s = -1;
+ char *fname = NULL;
+
+ wpa_s->ctrl_sock = -1;
+
+ if (wpa_s->conf->ctrl_interface == NULL)
+ return 0;
+
+#ifdef CONFIG_CTRL_IFACE_UDP
+ s = socket(PF_INET, SOCK_DGRAM, 0);
+ if (s < 0) {
+ perror("socket(PF_INET)");
+ goto fail;
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = htonl((127 << 24) | 1);
+ addr.sin_port = htons(9877);
+ if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ perror("bind(AF_UNIX)");
+ goto fail;
+ }
+#else /* CONFIG_CTRL_IFACE_UDP */
+ if (mkdir(wpa_s->conf->ctrl_interface, S_IRWXU | S_IRWXG) < 0) {
+ if (errno == EEXIST) {
+ wpa_printf(MSG_DEBUG, "Using existing control "
+ "interface directory.");
+ } else {
+ perror("mkdir[ctrl_interface]");
+ goto fail;
+ }
+ }
+
+ if (wpa_s->conf->ctrl_interface_gid_set &&
+ chown(wpa_s->conf->ctrl_interface, 0,
+ wpa_s->conf->ctrl_interface_gid) < 0) {
+ perror("chown[ctrl_interface]");
+ return -1;
+ }
+
+ if (strlen(wpa_s->conf->ctrl_interface) + 1 + strlen(wpa_s->ifname) >=
+ sizeof(addr.sun_path))
+ goto fail;
+
+ s = socket(PF_UNIX, SOCK_DGRAM, 0);
+ if (s < 0) {
+ perror("socket(PF_UNIX)");
+ goto fail;
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sun_family = AF_UNIX;
+ fname = wpa_supplicant_ctrl_iface_path(wpa_s);
+ if (fname == NULL)
+ goto fail;
+ strncpy(addr.sun_path, fname, sizeof(addr.sun_path));
+ if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ perror("bind(PF_UNIX)");
+ 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);
+ free(fname);
+ fname = NULL;
+ goto fail;
+ }
+ }
+
+ if (wpa_s->conf->ctrl_interface_gid_set &&
+ chown(fname, 0, wpa_s->conf->ctrl_interface_gid) < 0) {
+ perror("chown[ctrl_interface/ifname]");
+ goto fail;
+ }
+
+ if (chmod(fname, S_IRWXU | S_IRWXG) < 0) {
+ perror("chmod[ctrl_interface/ifname]");
+ goto fail;
+ }
+ free(fname);
+#endif /* CONFIG_CTRL_IFACE_UDP */
+
+ wpa_s->ctrl_sock = s;
+ eloop_register_read_sock(s, wpa_supplicant_ctrl_iface_receive, wpa_s,
+ NULL);
+
+ return 0;
+
+fail:
+ if (s >= 0)
+ close(s);
+ if (fname) {
+ unlink(fname);
+ free(fname);
+ }
+ return -1;
+}
+
+
+void wpa_supplicant_ctrl_iface_deinit(struct wpa_supplicant *wpa_s)
+{
+ struct wpa_ctrl_dst *dst, *prev;
+
+ if (wpa_s->ctrl_sock > -1) {
+ char *fname;
+ eloop_unregister_read_sock(wpa_s->ctrl_sock);
+ close(wpa_s->ctrl_sock);
+ wpa_s->ctrl_sock = -1;
+ fname = wpa_supplicant_ctrl_iface_path(wpa_s);
+ if (fname)
+ unlink(fname);
+ free(fname);
+
+ if (rmdir(wpa_s->conf->ctrl_interface) < 0) {
+ if (errno == ENOTEMPTY) {
+ wpa_printf(MSG_DEBUG, "Control interface "
+ "directory not empty - leaving it "
+ "behind");
+ } else {
+ perror("rmdir[ctrl_interface]");
+ }
+ }
+ }
+
+ dst = wpa_s->ctrl_dst;
+ while (dst) {
+ prev = dst;
+ dst = dst->next;
+ free(prev);
+ }
+}
+
+
+void wpa_supplicant_ctrl_iface_send(struct wpa_supplicant *wpa_s, int level,
+ char *buf, size_t len)
+{
+ struct wpa_ctrl_dst *dst, *next;
+ char levelstr[10];
+ int idx;
+#ifdef CONFIG_CTRL_IFACE_UDP
+ char *sbuf;
+ int llen;
+
+ dst = wpa_s->ctrl_dst;
+ if (wpa_s->ctrl_sock < 0 || dst == NULL)
+ return;
+
+ snprintf(levelstr, sizeof(levelstr), "<%d>", level);
+
+ llen = strlen(levelstr);
+ sbuf = malloc(llen + len);
+ if (sbuf == NULL)
+ return;
+
+ memcpy(sbuf, levelstr, llen);
+ memcpy(sbuf + llen, buf, len);
+
+ idx = 0;
+ while (dst) {
+ next = dst->next;
+ if (level >= dst->debug_level) {
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor send %s:%d",
+ inet_ntoa(dst->addr.sin_addr),
+ ntohs(dst->addr.sin_port));
+ if (sendto(wpa_s->ctrl_sock, sbuf, llen + len, 0,
+ (struct sockaddr *) &dst->addr,
+ sizeof(dst->addr)) < 0) {
+ fprintf(stderr, "CTRL_IFACE monitor[%d]: ",
+ idx);
+ perror("sendto");
+ dst->errors++;
+ if (dst->errors > 10) {
+ wpa_supplicant_ctrl_iface_detach(
+ wpa_s, &dst->addr,
+ dst->addrlen);
+ }
+ } else
+ dst->errors = 0;
+ }
+ idx++;
+ dst = next;
+ }
+ free(sbuf);
+#else /* CONFIG_CTRL_IFACE_UDP */
+ struct msghdr msg;
+ struct iovec io[2];
+
+ dst = wpa_s->ctrl_dst;
+ if (wpa_s->ctrl_sock < 0 || dst == NULL)
+ return;
+
+ snprintf(levelstr, sizeof(levelstr), "<%d>", level);
+ io[0].iov_base = levelstr;
+ io[0].iov_len = strlen(levelstr);
+ io[1].iov_base = buf;
+ io[1].iov_len = len;
+ memset(&msg, 0, sizeof(msg));
+ msg.msg_iov = io;
+ msg.msg_iovlen = 2;
+
+ idx = 0;
+ while (dst) {
+ next = dst->next;
+ if (level >= dst->debug_level) {
+ wpa_hexdump(MSG_DEBUG, "CTRL_IFACE monitor send",
+ (u8 *) dst->addr.sun_path, dst->addrlen);
+ msg.msg_name = &dst->addr;
+ msg.msg_namelen = dst->addrlen;
+ if (sendmsg(wpa_s->ctrl_sock, &msg, 0) < 0) {
+ fprintf(stderr, "CTRL_IFACE monitor[%d]: ",
+ idx);
+ perror("sendmsg");
+ dst->errors++;
+ if (dst->errors > 10) {
+ wpa_supplicant_ctrl_iface_detach(
+ wpa_s, &dst->addr,
+ dst->addrlen);
+ }
+ } else
+ dst->errors = 0;
+ }
+ idx++;
+ dst = next;
+ }
+#endif /* CONFIG_CTRL_IFACE_UDP */
+}
diff --git a/contrib/wpa_supplicant/ctrl_iface.h b/contrib/wpa_supplicant/ctrl_iface.h
new file mode 100644
index 000000000000..d7ad6dfe670a
--- /dev/null
+++ b/contrib/wpa_supplicant/ctrl_iface.h
@@ -0,0 +1,31 @@
+#ifndef CTRL_IFACE_H
+#define CTRL_IFACE_H
+
+#ifdef CONFIG_CTRL_IFACE
+
+int wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s);
+void wpa_supplicant_ctrl_iface_deinit(struct wpa_supplicant *wpa_s);
+void wpa_supplicant_ctrl_iface_send(struct wpa_supplicant *wpa_s, int level,
+ char *buf, size_t len);
+
+#else /* CONFIG_CTRL_IFACE */
+
+static inline int wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s)
+{
+ return 0;
+}
+
+static inline void
+wpa_supplicant_ctrl_iface_deinit(struct wpa_supplicant *wpa_s)
+{
+}
+
+static inline void
+wpa_supplicant_ctrl_iface_send(struct wpa_supplicant *wpa_s, int level,
+ char *buf, size_t len)
+{
+}
+
+#endif /* CONFIG_CTRL_IFACE */
+
+#endif /* CTRL_IFACE_H */
diff --git a/contrib/wpa_supplicant/defconfig b/contrib/wpa_supplicant/defconfig
new file mode 100644
index 000000000000..7e8e3811d06f
--- /dev/null
+++ b/contrib/wpa_supplicant/defconfig
@@ -0,0 +1,154 @@
+# 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 cass, 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
+# in non-default location
+#CFLAGS += -I/usr/local/openssl/include
+#LIBS += -L/usr/local/openssl/lib
+
+# 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
+#CONFIG_DRIVER_MADWIFI=y
+# Change include directories to match with the local setup
+#CFLAGS += -I../madwifi/wpa
+
+# Driver interface for Prism54 driver
+CONFIG_DRIVER_PRISM54=y
+
+# Driver interface for ndiswrapper
+#CONFIG_DRIVER_NDISWRAPPER=y
+
+# Driver interface for Atmel driver
+CONFIG_DRIVER_ATMEL=y
+
+# Driver interface for Broadcom 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
+#CONFIG_DRIVER_IPW=y
+
+# Driver interface for generic Linux wireless extensions
+CONFIG_DRIVER_WEXT=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
+
+# 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
+
+# Driver interface for development testing
+#CONFIG_DRIVER_TEST=y
+
+# Enable IEEE 802.1X Supplicant (automatically included if any EAP method is
+# included)
+CONFIG_IEEE8021X_EAPOL=y
+
+# EAP-MD5 (automatically included if EAP-TTLS is enabled)
+CONFIG_EAP_MD5=y
+
+# EAP-MSCHAPv2 (automatically included if EAP-PEAP is enabled)
+CONFIG_EAP_MSCHAPV2=y
+
+# EAP-TLS
+CONFIG_EAP_TLS=y
+
+# EAL-PEAP
+CONFIG_EAP_PEAP=y
+
+# EAP-TTLS
+CONFIG_EAP_TTLS=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
+
+# LEAP
+CONFIG_EAP_LEAP=y
+
+# EAP-AKA (enable CONFIG_PCSC, if EAP-AKA is used)
+#CONFIG_EAP_AKA=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
+
+# PC/SC interface for smartcards (USIM, GSM SIM)
+# Enable this if EAP-SIM or EAP-AKA is included
+#CONFIG_PCSC=y
+
+# Development testing
+#CONFIG_EAPOL_TEST=y
+
+# Replace native Linux implementation of packet sockets with libdnet/libpcap.
+# This will be automatically set for non-Linux OS.
+#CONFIG_DNET_PCAP=y
+
+# Include control interface for external programs, e.g, wpa_cli
+CONFIG_CTRL_IFACE=y
+
+# Include interface for using external supplicant (Xsupplicant) for EAP
+# authentication
+#CONFIG_XSUPPLICANT_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
diff --git a/contrib/wpa_supplicant/defs.h b/contrib/wpa_supplicant/defs.h
new file mode 100644
index 000000000000..a5a515c552f5
--- /dev/null
+++ b/contrib/wpa_supplicant/defs.h
@@ -0,0 +1,14 @@
+#ifndef DEFS_H
+#define DEFS_H
+
+#ifdef CONFIG_NATIVE_WINDOWS
+#ifdef FALSE
+#undef FALSE
+#endif
+#ifdef TRUE
+#undef TRUE
+#endif
+#endif /* CONFIG_NATIVE_WINDOWS */
+typedef enum { FALSE = 0, TRUE = 1 } Boolean;
+
+#endif /* DEFS_H */
diff --git a/contrib/wpa_supplicant/developer.txt b/contrib/wpa_supplicant/developer.txt
new file mode 100644
index 000000000000..bc5b34645d1a
--- /dev/null
+++ b/contrib/wpa_supplicant/developer.txt
@@ -0,0 +1,458 @@
+Developer notes for wpa_supplicant
+==================================
+
+The design goal for wpa_supplicant was to use hardware, driver, and OS
+independent, portable C code for all WPA functionality. All
+hardware/driver specific functionality is in separate files that
+implement a well-defined driver API.
+
+The goal of this file and the comments in the header files is to give
+enough information for other developers to be able to port the example
+code. If any information is missing, feel free to contact Jouni Malinen
+<jkmaline@cc.hut.fi> for more information. Contributions as patch files
+are also very welcome at the same address.
+
+Structure of the source code
+----------------------------
+
+Program initialization, main control loop and event handling is
+implemented in wpa_supplicant.c. WPA state machines and 4-Way/Group
+Key Handshake processing in in wpa.c. IEEE 802.1X/EAPOL processing and
+state machines are in eapol_sm.c. EAP state machine is in eap.c. EAP
+methods for the internal EAP peer are in eap_*.c. Parser for the
+configuration file is implemented in config.c.
+
+Driver interface API is defined in driver.h and all hardware/driver
+dependent functionality is implemented in driver_*.c (see below).
+
+
+Generic helper functions
+------------------------
+
+wpa_supplicant uses generic helper functions some of which are shared
+with with hostapd. The following C files are currently used:
+
+eloop.[ch]
+ event loop (select() loop with registerable timeouts, socket read
+ callbacks, and signal callbacks)
+
+common.[ch]
+ common helper functions
+
+defs.h
+ definitions shared by multiple files
+
+l2_packet.[ch]
+ Layer 2 (link) access wrapper (includes native Linux implementation
+ and wrappers for libdnet/libpcap)
+
+pcsc_funcs.[ch]
+ Wrapper for PC/SC lite SIM and smart card readers
+
+
+Cryptographic functions
+-----------------------
+
+md5.c
+ MD5 (replaced with openssl/crypto if TLS support is included)
+ HMAC-MD5 (keyed checksum for message authenticity validation)
+
+rc4.c
+ RC4 (broadcast/default key encryption)
+
+sha1.c
+ SHA-1 (replaced with openssl/crypto if TLS support is included)
+ HMAC-SHA-1 (keyed checksum for message authenticity validation)
+ PRF-SHA-1 (pseudorandom (key/nonce generation) function)
+ PBKDF2-SHA-1 (ASCII passphrase to shared secret)
+ T-PRF (for EAP-FAST)
+ TLS-PRF (RFC 2246)
+
+aes_wrap.[ch], aes.c
+ AES
+ AES Key Wrap Algorithm with 128-bit KEK, RFC3394 (broadcast/default
+ key encryption)
+ One-Key CBC MAC (OMAC1) hash with AES-128
+ AES-128 CTR mode encryption
+ AES-128 EAX mode encryption/decryption
+ AES-128 CBC
+
+crypto.[ch]
+ Wrapper functions for libcrypto (MD4 and DES)
+
+ms_funcs.[ch]
+ Helper functions for MSCHAPV2 and LEAP
+
+tls.h
+ Definition of TLS library wrapper
+
+tls_none.c
+ Dummy implementation of TLS library wrapper for cases where TLS
+ functionality is not included.
+
+tls_openssl.c
+ TLS library wrapper for openssl
+
+
+Configuration
+-------------
+
+config_ssid.h
+ Definition of per network configuration items
+
+config.h
+ Definition of the wpa_supplicant configuration
+
+config.c
+ Configuration file parser
+
+
+Control interface
+-----------------
+
+wpa_supplicant has a control interface that can be used to get status
+information and manage operations from external programs. An example,
+command line interface, wpa_cli, for this interface is included in the
+wpa_supplicant distribution.
+
+ctrl_iface.[ch]
+ wpa_supplicant-side of the control interface
+
+wpa_ctrl.[ch]
+ Library functions for external programs to provide access to the
+ wpa_supplicant control interface
+
+wpa_cli.c
+ Example program for using wpa_supplicant control interface
+
+
+EAP peer
+--------
+
+eap.[ch]
+ EAP state machine
+
+eap_defs.h
+ Common EAP definitions
+
+eap_i.h
+ Internal definitions for EAP state machine and EAP methods
+
+eap_sim_common.[ch]
+ Common code for EAP-SIM and EAP-AKA
+
+eap_tls_common.[ch]
+ Common code for EAP-PEAP, EAP-TTLS, and EAP-FAST
+
+eap_tlv.[ch]
+ EAP-TLV code for EAP-PEAP and EAP-FAST
+
+eap_{aka,fast,gtc,leap,md5,mschapv2,otp,peap,psk,sim,tls,ttls}.c
+ EAP method implementations
+
+
+EAPOL supplicant
+----------------
+
+eapol_sm.[ch]
+ EAPOL supplicant state machine and IEEE 802.1X processing
+
+
+Windows port
+------------
+
+ndis_events.cpp
+ External program for receiving NdisMIndicateStatus() events and
+ delivering them to wpa_supplicant in more easier to use form
+
+win_if_list.c
+ External program for listing current network interface
+
+
+Test programs
+-------------
+
+radius_client.[ch]
+ RADIUS authentication client implementation for eapol_test
+
+eapol_test.c
+ Standalone EAP testing tool with integrated RADIUS authentication
+ client
+
+preauth_test.c
+ Standalone RSN pre-authentication tool
+
+
+wpa_supplicant.c
+----------------
+
+main()
+- parse command line
+- call config file parser
+- initialize Supplicant data structures
+- call functions to initialize WPA support in the driver
+- initialize event loop
+- cleanup when exiting
+
+wpa_supplicant_dot1x_receive()
+- receive master session key update from Xsupplicant (optional)
+
+wpa_supplicant_get_beacon_ie()
+
+wpa_supplicant_deauthenticate()
+
+wpa_supplicant_disassociate()
+
+wpa_supplicant_scan()
+
+wpa_supplicant_reconfig()
+- SIGHUP signal processing
+
+wpa_supplicant_terminate()
+- SIGINT and SIGTERM processing
+
+wpa_supplicant_reload_configuration()
+
+wpa_supplicant_event()
+- receive driver events (through driver wrapper functions)
+ * wpa_supplicant_scan_results(): process scan result event, BSS selection
+ * wpa_supplicant_associnfo(): process association information event
+
+wpa_supplicant_associate()
+- control association (select cipher and key management suites, initiate
+ association)
+
+wpa_supplicant_req_auth_timeout()
+
+wpa_supplicant_cancel_auth_timeout()
+
+wpa_supplicant_req_scan()
+
+wpa_supplicant_cancel_scan()
+
+wpa_supplicant_notify_eapol_done()
+
+wpa_eapol_send()
+- send EAPOL frames
+
+wpa_eapol_send_preauth()
+- send RSN preauthentication frames
+
+wpa_msg()
+- event/debug function
+
+
+wpa_supplicant.h
+----------------
+
+- driver event definition
+- common function definition (e.g., wpa_msg)
+
+
+wpa_supplicant_i.h
+------------------
+- internal definitions for wpa_supplicant; must not be included into
+ common code, EAP methods, driver interface implementations
+
+
+wpa.[ch]
+--------
+- WPA supplicant state machine and 4-Way/Group Key Handshake processing
+- PMKSA cache and RSN pre-authentication
+
+pmksa_cache_free()
+
+pmksa_cache_get()
+
+pmksa_cache_list()
+
+pmksa_candidate_free()
+
+wpa_parse_wpa_ie()
+- WPA/RSN IE parsing
+
+wpa_gen_wpa_ei()
+- WPA/RSN IE generation
+
+wpa_supplicant_get_ssid()
+
+wpa_supplicant_key_request()
+- trigger function to start key requests
+
+wpa_sm_rx_eapol()
+- WPA processing for received EAPOL-Key frames
+ * wpa_supplicant_process_1_of_4() (message 1 of 4-Way Handshake)
+ * wpa_supplicant_process_3_of_4() (message 3 of 4-Way Handshake)
+ * wpa_supplicant_process_1_of_2() (message 1 of Group Key Handshake)
+
+wpa_supplicant_rx_eapol()
+- l2_packet RX callback for EAPOL frames; sends the frames to WPA and EAPOL
+ state machines for further processing
+
+wpa_get_mib()
+
+rsn_preauth_receive()
+- l2_packet RX callback for preauthentication frames
+
+rsn_preauth_eapol_cb()
+- callback function to be called when EAPOL authentication has been completed
+ (either successfully or unsuccessfully) for RSN pre-authentication
+
+rsn_preauth_init()
+rsn_preauth_deinit()
+
+pmksa_candidate_add()
+- add a BSSID to PMKSA candidate list
+
+rsn_preauth_scan_results()
+- update RSN pre-authentication candidate list based on scan results
+
+
+Driver wrapper implementation (driver.h, drivers.c)
+---------------------------------------------------
+
+All hardware and driver dependent functionality is implemented in as a
+separate C file(s) implementing defined wrapper functions. Other parts
+of the wpa_supplicant are designed to be hardware, driver, and operating
+system independent.
+
+Driver wrappers need to implement whatever calls are used in the
+target operating system/driver for controlling wireless LAN
+devices. As an example, in case of Linux, these are mostly some glue
+code and ioctl() calls and netlink message parsing for Linux Wireless
+Extensions. Since all features required for WPA are not yet included
+in Wireless Extensions, some driver specific code is used in the
+example implementation for Host AP driver. These driver dependent parts
+are to be replaced with generic code once the needed changes are
+included in the Wireless Extensions. After that, all Linux drivers, at
+least in theory, could use the same driver wrapper code.
+
+A driver wrapper needs to implement some or all of the functions
+defined in driver.h (see that file for detailed documentation of the
+functions). Hardware independent parts of wpa_supplicant will call
+these functions to control the driver/wlan card. In addition, support
+for driver events is required. The event callback function,
+wpa_supplicant_event(), and its parameters are documented in
+wpa_supplicant.h. In addition, pointer to the 'struct wpa_driver_ops'
+needs to be registered in drivers.c file.
+
+When porting to other operating systems, driver wrapper should be
+modified to use the native interface of the target OS. It is possible
+that some extra requirements for the interface between the driver
+wrapper and generic wpa_supplicant code are discovered during porting
+to a new operating system. These will be addresses on case by case
+basic by modifying the interface and updating the other driver
+wrappers for this. The goal is to avoid changing this interface
+without very good reasons in order to limit the number of changes
+needed to other wrappers and hardware independent parts of
+wpa_supplicant.
+
+Generic Linux Wireless Extensions functions are implemented in
+driver_wext.c. All Linux driver wrappers can use these when the kernel
+driver supports the generic ioctl()s and wireless events. Driver
+specific functions are implemented in separate C files, e.g.,
+driver_hostap.c. These files need to define struct wpa_driver_ops
+entry that will be used in wpa_supplicant.c when calling driver
+functions. These entries need to be added to the lists in
+wpa_supplicant_set_driver() and usage() functions in wpa_supplicant.c.
+
+In general, it is likely to be useful to first take a look at couple
+of the driver interfaces before starting on implementing a new
+one. driver_hostap.c and driver_wext.c include a complete
+implementation for Linux drivers that use wpa_supplicant-based control
+of WPA IE and roaming. driver_ndis.c (with help from driver_ndis_.c)
+is an example of a complete interface for Windows NDIS interface for
+drivers that generate WPA IE themselves and decide when to roam. These
+example implementations include full support for all security modes.
+
+
+Driver requirements for WPA
+---------------------------
+
+WPA introduces new requirements for the device driver. At least some
+of these need to be implemented in order to provide enough support for
+wpa_supplicant.
+
+TKIP/CCMP
+
+WPA requires that the pairwise cipher suite (encryption algorithm for
+unicast data packets) is TKIP or CCMP. These are new encryption
+protocols and thus, the driver will need to be modified to support
+them. Depending on the used wlan hardware, some parts of these may be
+implemented by the hardware/firmware.
+
+Specification for both TKIP and CCMP is available from IEEE (IEEE
+802.11i draft version 3.0). Fully functional, hardware independent
+implementation of both encryption protocols is also available in Host
+AP driver (driver/modules/hostap_{tkip,ccmp}.c).
+
+The driver will also need to provide configuration mechanism to allow
+user space programs to configure TKIP and CCMP. Current Linux Wireless
+Extensions (v16) does not yet support these algorithms or
+individual/non-default keys. Host AP driver has an example of private
+ioctl()s for this. Eventually, this should be replaced with modified
+Linux Wireless Extensions.
+
+Roaming control and scanning support
+
+wpa_supplicant controls AP selections based on the information
+received from Beacon and/or Probe Response frames. This means that the
+driver should support external control for scan process. In case of
+Linux, use of new Wireless Extensions scan support (i.e., 'iwlist
+wlan0 scan') is recommended. The current driver wrapper (driver_wext.c)
+uses this for scan results.
+
+Scan results must also include WPA information element. This is not
+yet defined in Linux Wireless Extensions and Host AP driver uses a
+custom event to provide the full WPA IE (including element id and
+length) as a hex string that is included in the scan results.
+Eventually, this should be defined as a Wireless Extensions ioctl
+that can be used both with scan results and with configuration of WPA IE
+for association request (and Beacon/Probe Response in case of an
+AP/IBSS).
+
+wpa_supplicant needs to also be able to request the driver to
+associate with a specific BSS. Current Host AP driver and matching
+driver_hostap.c wrapper uses following sequence for this
+request. Similar/identical mechanism should be usable also with other
+drivers.
+
+- set WPA IE for AssocReq with private ioctl
+- set SSID with SIOCSIWESSID
+- set channel/frequency with SIOCSIWFREQ
+- set BSSID with SIOCSIWAP
+ (this last ioctl will trigger the driver to request association)
+
+WPA IE generation
+
+wpa_supplicant selects which cipher suites and key management suites
+are used. Based on this information, it generates a WPA IE. This is
+provided to the driver interface in the associate call. This does not
+match with Windows NDIS drivers which generate the WPA IE
+themselves.
+
+wpa_supplicant allows Windows NDIS-like behavior by providing the
+selected cipher and key management suites in the associate call. If
+the driver generates its own WPA IE and that differs from the one
+generated by wpa_supplicant, the driver has to inform wpa_supplicant
+about the used WPA IE (i.e., the one it used in (Re)Associate
+Request). This notification is done using EVENT_ASSOCINFO event (see
+wpa_supplicant.h).
+
+Driver events
+
+wpa_supplicant needs to receive event callbacks when certain events
+occur (association, disassociation, Michael MIC failure, scan results
+available, PMKSA caching candidate). These events and the callback
+details are defined in wpa_supplicant.h.
+
+On Linux, association and disassociation can use existing Wireless
+Extensions event that is reporting new AP with SIOCGIWAP
+event. Similarly, completion of scan can be reported with SIOCGIWSCAN
+event.
+
+Michael MIC failure event is not yet included in Wireless Extensions,
+so this needs a custom event. Host AP driver uses custom event with
+following contents: MLME-MICHAELMICFAILURE.indication(keyid=#
+broadcast/unicast addr=addr2). This is the recommended format until
+the event is added to Linux Wireless Extensions.
diff --git a/contrib/wpa_supplicant/doc/wpa_supplicant.fig b/contrib/wpa_supplicant/doc/wpa_supplicant.fig
new file mode 100644
index 000000000000..dc1d9dfcf738
--- /dev/null
+++ b/contrib/wpa_supplicant/doc/wpa_supplicant.fig
@@ -0,0 +1,221 @@
+#FIG 3.2
+Landscape
+Center
+Inches
+Letter
+100.00
+Single
+-2
+1200 2
+6 1875 4050 2925 4350
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+ 1875 4050 2925 4050 2925 4350 1875 4350 1875 4050
+4 0 0 50 -1 0 12 0.0000 4 180 735 2025 4275 l2_packet\001
+-6
+6 3450 1200 4275 1500
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+ 3450 1200 4275 1200 4275 1500 3450 1500 3450 1200
+4 0 0 50 -1 0 12 0.0000 4 180 585 3600 1425 wpa_cli\001
+-6
+6 4725 1200 5925 1500
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+ 4725 1200 5925 1200 5925 1500 4725 1500 4725 1200
+4 0 0 50 -1 0 12 0.0000 4 135 1005 4800 1425 GUI frontend\001
+-6
+6 6000 2700 7200 3225
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+ 6000 2700 7200 2700 7200 3225 6000 3225 6000 2700
+4 0 0 50 -1 0 12 0.0000 4 135 975 6075 2925 WPA/WPA2\001
+4 0 0 50 -1 0 12 0.0000 4 135 1065 6075 3150 state machine\001
+-6
+6 6000 4950 7200 5475
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+ 6000 4950 7200 4950 7200 5475 6000 5475 6000 4950
+4 0 0 50 -1 0 12 0.0000 4 135 360 6075 5175 EAP\001
+4 0 0 50 -1 0 12 0.0000 4 135 1065 6075 5400 state machine\001
+-6
+6 8700 3000 9375 3300
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+ 8700 3000 9375 3000 9375 3300 8700 3300 8700 3000
+4 0 0 50 -1 0 12 0.0000 4 150 480 8775 3225 crypto\001
+-6
+6 4350 3900 5025 4425
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+ 4350 3900 5025 3900 5025 4425 4350 4425 4350 3900
+4 0 0 50 -1 0 12 0.0000 4 105 420 4500 4125 event\001
+4 0 0 50 -1 0 12 0.0000 4 180 315 4500 4350 loop\001
+-6
+6 4275 2550 5100 2850
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+ 4275 2550 5100 2550 5100 2850 4275 2850 4275 2550
+4 0 0 50 -1 0 12 0.0000 4 135 450 4425 2775 ctrl i/f\001
+-6
+6 6000 3900 7200 4425
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+ 6000 3900 7200 3900 7200 4425 6000 4425 6000 3900
+4 0 0 50 -1 0 12 0.0000 4 135 600 6075 4125 EAPOL\001
+4 0 0 50 -1 0 12 0.0000 4 135 1065 6075 4350 state machine\001
+-6
+6 1800 6000 7800 8100
+6 1800 6000 7800 7200
+6 1800 6900 2700 7200
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+ 1800 6900 2700 6900 2700 7200 1800 7200 1800 6900
+4 0 0 50 -1 0 12 0.0000 4 105 375 1875 7125 wext\001
+-6
+6 4725 6900 5625 7200
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+ 4725 6900 5625 6900 5625 7200 4725 7200 4725 6900
+4 0 0 50 -1 0 12 0.0000 4 135 555 4800 7125 hermes\001
+-6
+6 6675 6900 7800 7200
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+ 6675 6900 7800 6900 7800 7200 6675 7200 6675 6900
+4 0 0 50 -1 0 12 0.0000 4 180 930 6750 7125 ndiswrapper\001
+-6
+6 5700 6900 6600 7200
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+ 5700 6900 6600 6900 6600 7200 5700 7200 5700 6900
+4 0 0 50 -1 0 12 0.0000 4 135 420 5775 7125 atmel\001
+-6
+6 4275 6000 5100 6300
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+ 4275 6000 5100 6000 5100 6300 4275 6300 4275 6000
+4 0 0 50 -1 0 12 0.0000 4 135 630 4350 6225 driver i/f\001
+-6
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+ 2775 6900 3675 6900 3675 7200 2775 7200 2775 6900
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+ 3750 6900 4650 6900 4650 7200 3750 7200 3750 6900
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 4
+ 2250 6900 2250 6600 7200 6600 7200 6900
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+ 3225 6900 3225 6600
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+ 4200 6900 4200 6600
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+ 5175 6900 5175 6600
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+ 6150 6900 6150 6600
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+ 4650 6600 4650 6300
+4 0 0 50 -1 0 12 0.0000 4 180 510 2850 7125 hostap\001
+4 0 0 50 -1 0 12 0.0000 4 135 600 3825 7125 madwifi\001
+-6
+6 3525 7800 5775 8100
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+ 3525 7800 5775 7800 5775 8100 3525 8100 3525 7800
+4 0 0 50 -1 0 12 0.0000 4 135 2145 3600 8025 kernel network device driver\001
+-6
+2 1 1 1 0 7 50 -1 -1 4.000 0 0 -1 0 0 2
+ 2250 7200 4200 7800
+2 1 1 1 0 7 50 -1 -1 4.000 0 0 -1 0 0 2
+ 7200 7200 5100 7800
+-6
+6 9600 3000 10275 3300
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+ 9600 3000 10275 3000 10275 3300 9600 3300 9600 3000
+4 0 0 50 -1 0 12 0.0000 4 135 315 9750 3225 TLS\001
+-6
+6 8100 4425 10425 6975
+6 8175 4725 9225 5025
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+ 8175 4725 9225 4725 9225 5025 8175 5025 8175 4725
+4 0 0 50 -1 0 12 0.0000 4 135 735 8250 4950 EAP-TLS\001
+-6
+6 9300 4725 10350 5025
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+ 9300 4725 10350 4725 10350 5025 9300 5025 9300 4725
+4 0 0 50 -1 0 12 0.0000 4 135 810 9375 4950 EAP-MD5\001
+-6
+6 8175 5100 9225 5400
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+ 8175 5100 9225 5100 9225 5400 8175 5400 8175 5100
+4 0 0 50 -1 0 12 0.0000 4 135 885 8250 5325 EAP-PEAP\001
+-6
+6 9300 5100 10350 5400
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+ 9300 5100 10350 5100 10350 5400 9300 5400 9300 5100
+4 0 0 50 -1 0 12 0.0000 4 135 840 9375 5325 EAP-TTLS\001
+-6
+6 8175 5475 9225 5775
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+ 8175 5475 9225 5475 9225 5775 8175 5775 8175 5475
+4 0 0 50 -1 0 12 0.0000 4 135 780 8250 5700 EAP-GTC\001
+-6
+6 9300 5475 10350 5775
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+ 9300 5475 10350 5475 10350 5775 9300 5775 9300 5475
+4 0 0 50 -1 0 12 0.0000 4 135 765 9375 5700 EAP-OTP\001
+-6
+6 8175 5850 9225 6150
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+ 8175 5850 9225 5850 9225 6150 8175 6150 8175 5850
+4 0 0 50 -1 0 12 0.0000 4 135 750 8250 6075 EAP-SIM\001
+-6
+6 8175 6600 9675 6900
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+ 8175 6600 9675 6600 9675 6900 8175 6900 8175 6600
+4 0 0 50 -1 0 12 0.0000 4 135 1365 8250 6825 EAP-MSCHAPv2\001
+-6
+6 9300 6225 10350 6525
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+ 9300 6225 10350 6225 10350 6525 9300 6525 9300 6225
+4 0 0 50 -1 0 12 0.0000 4 135 465 9375 6450 LEAP\001
+-6
+6 8175 6225 9225 6525
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+ 8175 6225 9225 6225 9225 6525 8175 6525 8175 6225
+4 0 0 50 -1 0 12 0.0000 4 135 765 8250 6450 EAP-PSK\001
+-6
+6 9300 5850 10350 6150
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+ 9300 5850 10350 5850 10350 6150 9300 6150 9300 5850
+4 0 0 50 -1 0 12 0.0000 4 135 825 9375 6075 EAP-AKA\001
+-6
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+ 8100 6975 10425 6975 10425 4425 8100 4425 8100 6975
+4 0 0 50 -1 0 12 0.0000 4 135 1050 8700 4650 EAP methods\001
+-6
+2 1 1 1 0 7 50 -1 -1 3.000 0 0 -1 0 0 2
+ 1275 4200 1875 4200
+2 1 1 1 0 7 50 -1 -1 4.000 0 0 -1 0 0 2
+ 4500 2550 3900 1500
+2 1 1 1 0 7 50 -1 -1 4.000 0 0 -1 0 0 2
+ 4800 2550 5400 1500
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+ 2925 4200 4350 4200
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+ 5025 3900 6000 3000
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+ 5025 4200 6000 4200
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+ 4650 6000 4650 4425
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+ 6600 4425 6600 4950
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+ 6600 3225 6600 3900
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+ 7200 5250 8100 5250
+2 1 0 1 2 7 50 -1 -1 0.000 0 0 -1 0 0 2
+ 9075 4425 9075 3300
+2 1 0 1 2 7 50 -1 -1 0.000 0 0 -1 0 0 2
+ 7200 3000 8700 3150
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+ 4650 3900 4650 2850
+2 1 0 1 2 7 50 -1 -1 0.000 0 0 -1 0 0 2
+ 7200 4125 8700 3300
+2 1 0 1 2 7 50 -1 -1 0.000 0 0 -1 0 0 2
+ 6000 4350 5025 6000
+2 1 0 1 2 7 50 -1 -1 0.000 0 0 -1 0 0 2
+ 6000 3150 4875 6000
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+ 1500 2100 10800 2100 10800 7500 1500 7500 1500 2100
+2 1 0 1 2 7 50 -1 -1 0.000 0 0 -1 0 0 2
+ 9900 4425 9900 3300
+4 0 0 50 -1 0 12 0.0000 4 135 915 375 3975 EAPOL and\001
+4 0 0 50 -1 0 12 0.0000 4 180 630 375 4200 pre-auth\001
+4 0 0 50 -1 0 12 0.0000 4 180 810 375 4425 ethertypes\001
+4 0 0 50 -1 0 12 0.0000 4 135 1050 375 4650 from/to kernel\001
+4 0 0 50 -1 0 12 0.0000 4 135 1920 3675 1875 frontend control interface\001
+4 0 0 50 -1 2 14 0.0000 4 195 1425 1637 2371 wpa_supplicant\001
diff --git a/contrib/wpa_supplicant/driver.h b/contrib/wpa_supplicant/driver.h
new file mode 100644
index 000000000000..da28014c9270
--- /dev/null
+++ b/contrib/wpa_supplicant/driver.h
@@ -0,0 +1,436 @@
+#ifndef DRIVER_H
+#define DRIVER_H
+
+#define WPA_SUPPLICANT_DRIVER_VERSION 2
+
+typedef enum { WPA_ALG_NONE, WPA_ALG_WEP, WPA_ALG_TKIP, WPA_ALG_CCMP } wpa_alg;
+typedef enum { CIPHER_NONE, CIPHER_WEP40, CIPHER_TKIP, CIPHER_CCMP,
+ CIPHER_WEP104 } wpa_cipher;
+typedef enum { KEY_MGMT_802_1X, KEY_MGMT_PSK, KEY_MGMT_NONE,
+ KEY_MGMT_802_1X_NO_WPA, KEY_MGMT_WPA_NONE } wpa_key_mgmt;
+
+#define AUTH_ALG_OPEN_SYSTEM 0x01
+#define AUTH_ALG_SHARED_KEY 0x02
+#define AUTH_ALG_LEAP 0x04
+
+#define IEEE80211_MODE_INFRA 0
+#define IEEE80211_MODE_IBSS 1
+
+#define SSID_MAX_WPA_IE_LEN 40
+struct wpa_scan_result {
+ u8 bssid[ETH_ALEN];
+ u8 ssid[32];
+ size_t ssid_len;
+ u8 wpa_ie[SSID_MAX_WPA_IE_LEN];
+ size_t wpa_ie_len;
+ u8 rsn_ie[SSID_MAX_WPA_IE_LEN];
+ size_t rsn_ie_len;
+ int freq; /* MHz */
+ int caps; /* e.g. privacy */
+ int qual; /* signal quality */
+ int noise;
+ int level;
+ int maxrate;
+};
+
+/* Parameters for associate driver_ops. */
+struct wpa_driver_associate_params {
+ /* BSSID of the selected AP */
+ const u8 *bssid;
+
+ /* The selected SSID and its length in bytes */
+ const u8 *ssid;
+ size_t ssid_len;
+
+ /* frequency that the selected AP is using (in MHz as
+ * reported in the scan results) */
+ int freq;
+
+ /* WPA information element to be included in (Re)Association
+ * Request (including information element id and length). Use
+ * of this WPA IE is optional. If the driver generates the WPA
+ * IE, it can use @pairwise_suite, @group_suite, and
+ * @key_mgmt_suite to select proper algorithms. In this case,
+ * the driver has to notify wpa_supplicant about the used WPA
+ * IE by generating an event that the interface code will
+ * convert into EVENT_ASSOCINFO data (see wpa_supplicant.h).
+ * When using WPA2/IEEE 802.11i, @wpa_ie is used for RSN IE
+ * instead. The driver can determine which version is used by
+ * looking at the first byte of the IE (0xdd for WPA, 0x30 for
+ * WPA2/RSN). @wpa_ie_len: length of the @wpa_ie */
+ const u8 *wpa_ie;
+ size_t wpa_ie_len;
+
+ /* The selected pairwise/group cipher and key management
+ * suites. These are usually ignored if @wpa_ie is used. */
+ wpa_cipher pairwise_suite;
+ wpa_cipher group_suite;
+ wpa_key_mgmt key_mgmt_suite;
+
+ int auth_alg; /* bit field of AUTH_ALG_* */
+ int mode; /* IEEE80211_MODE_* */
+};
+
+struct wpa_driver_capa {
+#define WPA_DRIVER_CAPA_KEY_MGMT_WPA 0x00000001
+#define WPA_DRIVER_CAPA_KEY_MGMT_WPA2 0x00000002
+#define WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK 0x00000004
+#define WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK 0x00000008
+#define WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE 0x00000010
+ 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
+ unsigned int enc;
+
+#define WPA_DRIVER_AUTH_OPEN 0x00000001
+#define WPA_DRIVER_AUTH_SHARED 0x00000002
+#define WPA_DRIVER_AUTH_LEAP 0x00000004
+ unsigned int auth;
+
+/* Driver generated WPA/RSN IE */
+#define WPA_DRIVER_FLAGS_DRIVER_IE 0x00000001
+#define WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC 0x00000002
+ unsigned int flags;
+};
+
+
+struct wpa_driver_ops {
+ /* name of the driver interface */
+ const char *name;
+ /* one line description of the driver interface */
+ const char *desc;
+
+ /**
+ * get_bssid - get the current BSSID
+ * @priv: private driver interface data
+ * @bssid: buffer for BSSID (ETH_ALEN = 6 bytes)
+ *
+ * Returns: 0 on success, -1 on failure
+ *
+ * Query kernel driver for the current BSSID and copy it to @bssid.
+ * Setting @bssid to 00:00:00:00:00:00 is recommended if the STA is not
+ * associated.
+ */
+ int (*get_bssid)(void *priv, u8 *bssid);
+
+ /**
+ * get_ssid - get the current SSID
+ * @priv: private driver interface data
+ * @ssid: buffer for SSID (at least 32 bytes)
+ *
+ * Returns: length of the SSID on success, -1 on failure
+ *
+ * Query kernel driver for the current SSID and copy it to @ssid.
+ * Returning zero is recommended if the STA is not associated.
+ *
+ * Note: SSID is an array of octets, i.e., it is not nul terminated and
+ * can, at least in theory, contain control characters (including nul)
+ * and as such, should be processed as binary data, not a printable
+ * string.
+ */
+ int (*get_ssid)(void *priv, u8 *ssid);
+
+ /**
+ * set_wpa - enable/disable WPA support
+ * @priv: private driver interface data
+ * @enabled: 1 = enable, 0 = disable
+ *
+ * Returns: 0 on success, -1 on failure
+ *
+ * Configure the kernel driver to enable/disable WPA support. This may
+ * be empty function, if WPA support is always enabled. Common
+ * configuration items are WPA IE (clearing it when WPA support is
+ * disabled), Privacy flag for capability field, roaming mode (need to
+ * allow wpa_supplicant to control roaming).
+ */
+ int (*set_wpa)(void *priv, int enabled);
+
+ /**
+ * set_key - configure encryption key
+ * @priv: private driver interface data
+ * @alg: encryption algorithm (%WPA_ALG_NONE, %WPA_ALG_WEP,
+ * %WPA_ALG_TKIP, %WPA_ALG_CCMP); %WPA_ALG_NONE clears the key.
+ * @addr: address of the peer STA or ff:ff:ff:ff:ff:ff for
+ * broadcast/default keys
+ * @key_idx: key index (0..3), always 0 for unicast keys
+ * @set_tx: configure this key as the default Tx key (only used when
+ * driver does not support separate unicast/individual key
+ * @seq: sequence number/packet number, @seq_len octets, the next
+ * packet number to be used for in replay protection; configured
+ * for Rx keys (in most cases, this is only used with broadcast
+ * keys and set to zero for unicast keys)
+ * @seq_len: length of the @seq, depends on the algorithm:
+ * TKIP: 6 octets, CCMP: 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)
+ *
+ * Returns: 0 on success, -1 on failure
+ *
+ * Configure the given key for the kernel driver. If the driver
+ * supports separate individual keys (4 default keys + 1 individual),
+ * @addr can be used to determine whether the key is default or
+ * individual. If only 4 keys are supported, the default key with key
+ * index 0 is used as the individual key. STA must be configured to use
+ * it as the default Tx key (@set_tx is set) and accept Rx for all the
+ * key indexes. In most cases, WPA uses only key indexes 1 and 2 for
+ * broadcast keys, so key index 0 is available for this kind of
+ * configuration.
+ */
+ int (*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);
+
+ /**
+ * init - initialize driver interface
+ * @ctx: context to be used when calling wpa_supplicant functions,
+ * e.g., wpa_supplicant_event()
+ * @ifname: interface name, e.g., wlan0
+ *
+ * Return: pointer to private data, %NULL on failure
+ *
+ * Initialize driver interface, including event processing for kernel
+ * driver events (e.g., associated, scan results, Michael MIC failure).
+ * This function can allocate a private configuration data area for
+ * @ctx, file descriptor, interface name, etc. information that may be
+ * needed in future driver operations. If this is not used, non-NULL
+ * value will need to be returned because %NULL is used to indicate
+ * failure. The returned value will be used as 'void *priv' data for
+ * all other driver_ops functions.
+ *
+ * The main event loop (eloop.c) of wpa_supplicant can be used to
+ * register callback for read sockets (eloop_register_read_sock()).
+ *
+ * See wpa_supplicant.h for more information about events and
+ * wpa_supplicant_event() function.
+ */
+ void * (*init)(void *ctx, const char *ifname);
+
+ /**
+ * deinit - deinitialize driver interface
+ * @priv: pointer to private data (from matching
+ * wpa_driver_events_init())
+ *
+ * Shut down driver interface and processing of driver events. Free
+ * private data buffer if one was allocated in init() handler.
+ */
+ void (*deinit)(void *priv);
+
+ /**
+ * set_countermeasures - enable/disable TKIP countermeasures
+ * @priv: private driver interface data
+ * @enabled: 1 = countermeasures enabled, 0 = disabled
+ *
+ * Return: 0 on success, -1 on failure
+ *
+ * Configure TKIP countermeasures. When these are enabled, the driver
+ * should drop all received and queued frames that are using TKIP.
+ */
+ int (*set_countermeasures)(void *priv, int enabled);
+
+ /**
+ * set_drop_unencrypted - enable/disable unencrypted frame filtering
+ * @priv: private driver interface data
+ * @enabled: 1 = unencrypted Tx/Rx frames will be dropped, 0 = disabled
+ *
+ * Return: 0 on success, -1 on failure
+ *
+ * Configure the driver to drop all non-EAPOL frames (both receive and
+ * transmit paths). Unencrypted EAPOL frames (ethertype 0x888e) must
+ * still be allowed for key negotiation.
+ */
+ int (*set_drop_unencrypted)(void *priv, int enabled);
+
+ /**
+ * scan - request the driver to initiate scan
+ * @priv: private driver interface data
+ * @ssid: specific SSID to scan for (ProbeReq) or %NULL to scan for
+ * all SSIDs (either active scan with broadcast SSID or passive
+ * scan
+ * @ssid_len: length of the SSID
+ *
+ * Return: 0 on success, -1 on failure
+ *
+ * Once the scan results are ready, the driver should report scan
+ * results event for wpa_supplicant which will eventually request the
+ * results with wpa_driver_get_scan_results().
+ */
+ int (*scan)(void *priv, const u8 *ssid, size_t ssid_len);
+
+ /**
+ * get_scan_results - fetch the latest scan results
+ * @priv: private driver interface data
+ * @results: pointer to buffer for scan results
+ * @max_size: maximum number of entries (buffer size)
+ *
+ * Return: number of scan result entries used on success, -1 on failure
+ *
+ * If scan results include more than @max_size BSSes, @max_size will be
+ * returned and the remaining entries will not be included in the
+ * buffer.
+ */
+ int (*get_scan_results)(void *priv,
+ struct wpa_scan_result *results,
+ size_t max_size);
+
+ /**
+ * deauthenticate - request driver to deauthenticate
+ * @priv: private driver interface data
+ * @addr: peer address (BSSID of the AP)
+ * @reason_code: 16-bit reason code to be sent in the deauthentication
+ * frame
+ *
+ * Return: 0 on success, -1 on failure
+ */
+ 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
+ *
+ * Return: 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
+ *
+ * Return: 0 on success, -1 on failure
+ */
+ int (*associate)(void *priv,
+ struct wpa_driver_associate_params *params);
+
+ /**
+ * set_auth_alg - set IEEE 802.11 authentication algorithm
+ * @priv: private driver interface data
+ * @auth_alg: bit field of AUTH_ALG_*
+ *
+ * If the driver supports more than one authentication algorithm at the
+ * same time, it should configure all supported algorithms. If not, one
+ * algorithm needs to be selected arbitrarily. Open System
+ * authentication should be ok for most cases and it is recommended to
+ * be used if other options are not supported. Static WEP configuration
+ * may also use Shared Key authentication and LEAP requires its own
+ * algorithm number. For LEAP, user can make sure that only one
+ * algorithm is used at a time by configuring LEAP as the only
+ * supported EAP method. This information is also available in
+ * associate() params, so set_auth_alg may not be needed in case of
+ * most drivers.
+ *
+ * Return: 0 on success, -1 on failure
+ */
+ int (*set_auth_alg)(void *priv, int auth_alg);
+
+ /**
+ * add_pmkid - add PMKSA cache entry to the driver
+ * @priv: private driver interface data
+ * @bssid: BSSID for the PMKSA cache entry
+ * @pmkid: PMKID for the PMKSA cache entry
+ *
+ * Return: 0 on success, -1 on failure
+ *
+ * This function is called when a new PMK is received, as a result of
+ * either normal authentication or RSN pre-authentication.
+ *
+ * If the driver generates RSN IE, i.e., it does not use @wpa_ie in
+ * associate(), add_pmkid() can be used to add new PMKSA cache entries
+ * in the driver. If the driver uses @wpa_ie from wpa_supplicant, this
+ * driver_ops function does not need to be implemented. Likewise, if
+ * the driver does not support WPA, this function is not needed.
+ */
+ int (*add_pmkid)(void *priv, const u8 *bssid, const u8 *pmkid);
+
+ /**
+ * remove_pmkid - remove PMKSA cache entry to the driver
+ * @priv: private driver interface data
+ * @bssid: BSSID for the PMKSA cache entry
+ * @pmkid: PMKID for the PMKSA cache entry
+ *
+ * Return: 0 on success, -1 on failure
+ *
+ * This function is called when the supplicant drops a PMKSA cache
+ * entry for any reason.
+ *
+ * If the driver generates RSN IE, i.e., it does not use @wpa_ie in
+ * associate(), remove_pmkid() can be used to synchronize PMKSA caches
+ * between the driver and wpa_supplicant. If the driver uses @wpa_ie
+ * from wpa_supplicant, this driver_ops function does not need to be
+ * implemented. Likewise, if the driver does not support WPA, this
+ * function is not needed.
+ */
+ int (*remove_pmkid)(void *priv, const u8 *bssid, const u8 *pmkid);
+
+ /**
+ * flush_pmkid - flush PMKSA cache
+ * @priv: private driver interface data
+ *
+ * Return: 0 on success, -1 on failure
+ *
+ * This function is called when the supplicant drops all PMKSA cache
+ * entries for any reason.
+ *
+ * If the driver generates RSN IE, i.e., it does not use @wpa_ie in
+ * associate(), remove_pmkid() can be used to synchronize PMKSA caches
+ * between the driver and wpa_supplicant. If the driver uses @wpa_ie
+ * from wpa_supplicant, this driver_ops function does not need to be
+ * implemented. Likewise, if the driver does not support WPA, this
+ * function is not needed.
+ */
+ int (*flush_pmkid)(void *priv);
+
+ /**
+ * flush_pmkid - flush PMKSA cache
+ * @priv: private driver interface data
+ *
+ * Return: 0 on success, -1 on failure
+ *
+ * Get driver/firmware/hardware capabilities.
+ */
+ int (*get_capa)(void *priv, struct wpa_driver_capa *capa);
+
+ /**
+ * poll - poll driver for association information
+ * @priv: private driver interface data
+ *
+ * This is an option callback that can be used when the driver does not
+ * provide event mechanism for association events. This is called when
+ * receiving WPA EAPOL-Key messages that require association
+ * information. The driver interface is supposed to generate associnfo
+ * event before returning from this callback function. In addition, the
+ * driver interface should generate an association event after having
+ * sent out associnfo.
+ */
+ void (*poll)(void *priv);
+
+ /**
+ * get_ifname - get interface name
+ *
+ * This optional function can be used to allow the driver interface to
+ * replace the interface name with something else, e.g., based on an
+ * interface mapping from a more descriptive name.
+ *
+ * Returns a pointer to the interface name. This can differ from the
+ * interface name used in init() call.
+ */
+ const char * (*get_ifname)(void *priv);
+
+ /**
+ * get_mac_addr - get own MAC address
+ *
+ * This optional function can be used to get the own MAC address of the
+ * device from the driver interface code. This is only needed if the
+ * l2_packet implementation for the OS does not provide easy access to
+ * a MAC address. */
+ const u8 * (*get_mac_addr)(void *priv);
+};
+
+#endif /* DRIVER_H */
diff --git a/contrib/wpa_supplicant/drivers.c b/contrib/wpa_supplicant/drivers.c
new file mode 100644
index 000000000000..cb016ba803e5
--- /dev/null
+++ b/contrib/wpa_supplicant/drivers.c
@@ -0,0 +1,96 @@
+/*
+ * WPA Supplicant / driver interface list
+ * Copyright (c) 2004, Jouni Malinen <jkmaline@cc.hut.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published 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 <stdlib.h>
+
+
+#ifdef CONFIG_DRIVER_HOSTAP
+extern struct wpa_driver_ops wpa_driver_hostap_ops; /* driver_hostap.c */
+#endif /* CONFIG_DRIVER_HOSTAP */
+#ifdef CONFIG_DRIVER_PRISM54
+extern struct wpa_driver_ops wpa_driver_prism54_ops; /* driver_prism54.c */
+#endif /* CONFIG_DRIVER_PRISM54 */
+#ifdef CONFIG_DRIVER_HERMES
+extern struct wpa_driver_ops wpa_driver_hermes_ops; /* driver_hermes.c */
+#endif /* CONFIG_DRIVER_HERMES */
+#ifdef CONFIG_DRIVER_MADWIFI
+extern struct wpa_driver_ops wpa_driver_madwifi_ops; /* driver_madwifi.c */
+#endif /* CONFIG_DRIVER_MADWIFI */
+#ifdef CONFIG_DRIVER_ATMEL
+extern struct wpa_driver_ops wpa_driver_atmel_ops; /* driver_atmel.c */
+#endif /* CONFIG_DRIVER_ATMEL */
+#ifdef CONFIG_DRIVER_WEXT
+extern struct wpa_driver_ops wpa_driver_wext_ops; /* driver_wext.c */
+#endif /* CONFIG_DRIVER_WEXT */
+#ifdef CONFIG_DRIVER_NDISWRAPPER
+/* driver_ndiswrapper.c */
+extern struct wpa_driver_ops wpa_driver_ndiswrapper_ops;
+#endif /* CONFIG_DRIVER_NDISWRAPPER */
+#ifdef CONFIG_DRIVER_BROADCOM
+extern struct wpa_driver_ops wpa_driver_broadcom_ops; /* driver_broadcom.c */
+#endif /* CONFIG_DRIVER_BROADCOM */
+#ifdef CONFIG_DRIVER_IPW
+extern struct wpa_driver_ops wpa_driver_ipw_ops; /* driver_ipw.c */
+#endif /* CONFIG_DRIVER_IPW */
+#ifdef CONFIG_DRIVER_BSD
+extern struct wpa_driver_ops wpa_driver_bsd_ops; /* driver_bsd.c */
+#endif /* CONFIG_DRIVER_BSD */
+#ifdef CONFIG_DRIVER_NDIS
+extern struct wpa_driver_ops wpa_driver_ndis_ops; /* driver_ndis.c */
+#endif /* CONFIG_DRIVER_NDIS */
+#ifdef CONFIG_DRIVER_TEST
+extern struct wpa_driver_ops wpa_driver_test_ops; /* driver_test.c */
+#endif /* CONFIG_DRIVER_TEST */
+
+
+struct wpa_driver_ops *wpa_supplicant_drivers[] =
+{
+#ifdef CONFIG_DRIVER_HOSTAP
+ &wpa_driver_hostap_ops,
+#endif /* CONFIG_DRIVER_HOSTAP */
+#ifdef CONFIG_DRIVER_PRISM54
+ &wpa_driver_prism54_ops,
+#endif /* CONFIG_DRIVER_PRISM54 */
+#ifdef CONFIG_DRIVER_HERMES
+ &wpa_driver_hermes_ops,
+#endif /* CONFIG_DRIVER_HERMES */
+#ifdef CONFIG_DRIVER_MADWIFI
+ &wpa_driver_madwifi_ops,
+#endif /* CONFIG_DRIVER_MADWIFI */
+#ifdef CONFIG_DRIVER_ATMEL
+ &wpa_driver_atmel_ops,
+#endif /* CONFIG_DRIVER_ATMEL */
+#ifdef CONFIG_DRIVER_WEXT
+ &wpa_driver_wext_ops,
+#endif /* CONFIG_DRIVER_WEXT */
+#ifdef CONFIG_DRIVER_NDISWRAPPER
+ &wpa_driver_ndiswrapper_ops,
+#endif /* CONFIG_DRIVER_NDISWRAPPER */
+#ifdef CONFIG_DRIVER_BROADCOM
+ &wpa_driver_broadcom_ops,
+#endif /* CONFIG_DRIVER_BROADCOM */
+#ifdef CONFIG_DRIVER_IPW
+ &wpa_driver_ipw_ops,
+#endif /* CONFIG_DRIVER_IPW */
+#ifdef CONFIG_DRIVER_BSD
+ &wpa_driver_bsd_ops,
+#endif /* CONFIG_DRIVER_BSD */
+#ifdef CONFIG_DRIVER_NDIS
+ &wpa_driver_ndis_ops,
+#endif /* CONFIG_DRIVER_NDIS */
+#ifdef CONFIG_DRIVER_TEST
+ &wpa_driver_test_ops,
+#endif /* CONFIG_DRIVER_TEST */
+ NULL
+};
diff --git a/contrib/wpa_supplicant/eap.c b/contrib/wpa_supplicant/eap.c
new file mode 100644
index 000000000000..a76b942464e8
--- /dev/null
+++ b/contrib/wpa_supplicant/eap.c
@@ -0,0 +1,1243 @@
+/*
+ * WPA Supplicant / EAP state machines
+ * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published 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 <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "common.h"
+#include "eap_i.h"
+#include "wpa_supplicant.h"
+#include "config_ssid.h"
+#include "tls.h"
+#include "md5.h"
+
+
+#define EAP_MAX_AUTH_ROUNDS 50
+
+
+#ifdef EAP_MD5
+extern const struct eap_method eap_method_md5;
+#endif
+#ifdef EAP_TLS
+extern const struct eap_method eap_method_tls;
+#endif
+#ifdef EAP_MSCHAPv2
+extern const struct eap_method eap_method_mschapv2;
+#endif
+#ifdef EAP_PEAP
+extern const struct eap_method eap_method_peap;
+#endif
+#ifdef EAP_TTLS
+extern const struct eap_method eap_method_ttls;
+#endif
+#ifdef EAP_GTC
+extern const struct eap_method eap_method_gtc;
+#endif
+#ifdef EAP_OTP
+extern const struct eap_method eap_method_otp;
+#endif
+#ifdef EAP_SIM
+extern const struct eap_method eap_method_sim;
+#endif
+#ifdef EAP_LEAP
+extern const struct eap_method eap_method_leap;
+#endif
+#ifdef EAP_PSK
+extern const struct eap_method eap_method_psk;
+#endif
+#ifdef EAP_AKA
+extern const struct eap_method eap_method_aka;
+#endif
+#ifdef EAP_FAST
+extern const struct eap_method eap_method_fast;
+#endif
+
+static const struct eap_method *eap_methods[] =
+{
+#ifdef EAP_MD5
+ &eap_method_md5,
+#endif
+#ifdef EAP_TLS
+ &eap_method_tls,
+#endif
+#ifdef EAP_MSCHAPv2
+ &eap_method_mschapv2,
+#endif
+#ifdef EAP_PEAP
+ &eap_method_peap,
+#endif
+#ifdef EAP_TTLS
+ &eap_method_ttls,
+#endif
+#ifdef EAP_GTC
+ &eap_method_gtc,
+#endif
+#ifdef EAP_OTP
+ &eap_method_otp,
+#endif
+#ifdef EAP_SIM
+ &eap_method_sim,
+#endif
+#ifdef EAP_LEAP
+ &eap_method_leap,
+#endif
+#ifdef EAP_PSK
+ &eap_method_psk,
+#endif
+#ifdef EAP_AKA
+ &eap_method_aka,
+#endif
+#ifdef EAP_FAST
+ &eap_method_fast,
+#endif
+};
+#define NUM_EAP_METHODS (sizeof(eap_methods) / sizeof(eap_methods[0]))
+
+
+const struct eap_method * eap_sm_get_eap_methods(int method)
+{
+ int i;
+ for (i = 0; i < NUM_EAP_METHODS; i++) {
+ if (eap_methods[i]->method == method)
+ return eap_methods[i];
+ }
+ return NULL;
+}
+
+
+static Boolean eap_sm_allowMethod(struct eap_sm *sm, EapType method);
+static u8 * eap_sm_buildNak(struct eap_sm *sm, int id, size_t *len);
+static void eap_sm_processIdentity(struct eap_sm *sm, u8 *req, size_t len);
+static void eap_sm_processNotify(struct eap_sm *sm, u8 *req, size_t len);
+static u8 * eap_sm_buildNotify(struct eap_sm *sm, int id, size_t *len);
+static void eap_sm_parseEapReq(struct eap_sm *sm, u8 *req, size_t len);
+static const char * eap_sm_method_state_txt(int state);
+static const char * eap_sm_decision_txt(int decision);
+
+
+/* Definitions for clarifying state machine implementation */
+#define SM_STATE(machine, state) \
+static void sm_ ## machine ## _ ## state ## _Enter(struct eap_sm *sm, \
+ int global)
+
+#define SM_ENTRY(machine, state) \
+if (!global || sm->machine ## _state != machine ## _ ## state) { \
+ sm->changed = TRUE; \
+ wpa_printf(MSG_DEBUG, "EAP: " #machine " entering state " #state); \
+} \
+sm->machine ## _state = machine ## _ ## state;
+
+#define SM_ENTER(machine, state) \
+sm_ ## machine ## _ ## state ## _Enter(sm, 0)
+#define SM_ENTER_GLOBAL(machine, state) \
+sm_ ## machine ## _ ## state ## _Enter(sm, 1)
+
+#define SM_STEP(machine) \
+static void sm_ ## machine ## _Step(struct eap_sm *sm)
+
+#define SM_STEP_RUN(machine) sm_ ## machine ## _Step(sm)
+
+
+static Boolean eapol_get_bool(struct eap_sm *sm, enum eapol_bool_var var)
+{
+ return sm->eapol_cb->get_bool(sm->eapol_ctx, var);
+}
+
+
+static void eapol_set_bool(struct eap_sm *sm, enum eapol_bool_var var,
+ Boolean value)
+{
+ sm->eapol_cb->set_bool(sm->eapol_ctx, var, value);
+}
+
+
+static unsigned int eapol_get_int(struct eap_sm *sm, enum eapol_int_var var)
+{
+ return sm->eapol_cb->get_int(sm->eapol_ctx, var);
+}
+
+
+static void eapol_set_int(struct eap_sm *sm, enum eapol_int_var var,
+ unsigned int value)
+{
+ sm->eapol_cb->set_int(sm->eapol_ctx, var, value);
+}
+
+
+static u8 * eapol_get_eapReqData(struct eap_sm *sm, size_t *len)
+{
+ return sm->eapol_cb->get_eapReqData(sm->eapol_ctx, len);
+}
+
+
+static void eap_deinit_prev_method(struct eap_sm *sm, const char *txt)
+{
+ if (sm->m == NULL || sm->eap_method_priv == NULL)
+ return;
+
+ wpa_printf(MSG_DEBUG, "EAP: deinitialize previously used EAP method "
+ "(%d, %s) at %s", sm->selectedMethod, sm->m->name, txt);
+ sm->m->deinit(sm, sm->eap_method_priv);
+ sm->eap_method_priv = NULL;
+ sm->m = NULL;
+}
+
+
+SM_STATE(EAP, INITIALIZE)
+{
+ SM_ENTRY(EAP, INITIALIZE);
+ if (sm->fast_reauth && sm->m && sm->m->has_reauth_data &&
+ sm->m->has_reauth_data(sm, sm->eap_method_priv)) {
+ wpa_printf(MSG_DEBUG, "EAP: maintaining EAP method data for "
+ "fast reauthentication");
+ sm->m->deinit_for_reauth(sm, sm->eap_method_priv);
+ } else {
+ eap_deinit_prev_method(sm, "INITIALIZE");
+ }
+ sm->selectedMethod = EAP_TYPE_NONE;
+ sm->methodState = METHOD_NONE;
+ sm->allowNotifications = TRUE;
+ sm->decision = DECISION_FAIL;
+ eapol_set_int(sm, EAPOL_idleWhile, sm->ClientTimeout);
+ eapol_set_bool(sm, EAPOL_eapSuccess, FALSE);
+ eapol_set_bool(sm, EAPOL_eapFail, FALSE);
+ free(sm->eapKeyData);
+ sm->eapKeyData = NULL;
+ sm->eapKeyAvailable = FALSE;
+ eapol_set_bool(sm, EAPOL_eapRestart, FALSE);
+ sm->lastId = -1; /* new session - make sure this does not match with
+ * the first EAP-Packet */
+ /* draft-ietf-eap-statemachine-02.pdf does not reset eapResp and
+ * eapNoResp here. However, this seemed to be able to trigger cases
+ * where both were set and if EAPOL state machine uses eapNoResp first,
+ * it may end up not sending a real reply correctly. This occurred
+ * when the workaround in FAIL state set eapNoResp = TRUE.. Maybe that
+ * workaround needs to be fixed to do something else(?) */
+ eapol_set_bool(sm, EAPOL_eapResp, FALSE);
+ eapol_set_bool(sm, EAPOL_eapNoResp, FALSE);
+ sm->num_rounds = 0;
+}
+
+
+SM_STATE(EAP, DISABLED)
+{
+ SM_ENTRY(EAP, DISABLED);
+ sm->num_rounds = 0;
+}
+
+
+SM_STATE(EAP, IDLE)
+{
+ SM_ENTRY(EAP, IDLE);
+}
+
+
+SM_STATE(EAP, RECEIVED)
+{
+ u8 *eapReqData;
+ size_t eapReqDataLen;
+
+ SM_ENTRY(EAP, RECEIVED);
+ eapReqData = eapol_get_eapReqData(sm, &eapReqDataLen);
+ /* parse rxReq, rxSuccess, rxFailure, reqId, reqMethod */
+ eap_sm_parseEapReq(sm, eapReqData, eapReqDataLen);
+ sm->num_rounds++;
+}
+
+
+SM_STATE(EAP, GET_METHOD)
+{
+ SM_ENTRY(EAP, GET_METHOD);
+ if (eap_sm_allowMethod(sm, sm->reqMethod)) {
+ int reinit = 0;
+ if (sm->fast_reauth &&
+ sm->m && sm->m->method == sm->reqMethod &&
+ sm->m->has_reauth_data &&
+ sm->m->has_reauth_data(sm, sm->eap_method_priv)) {
+ wpa_printf(MSG_DEBUG, "EAP: using previous method data"
+ " for fast re-authentication");
+ reinit = 1;
+ } else
+ eap_deinit_prev_method(sm, "GET_METHOD");
+ sm->selectedMethod = sm->reqMethod;
+ if (sm->m == NULL)
+ sm->m = eap_sm_get_eap_methods(sm->selectedMethod);
+ if (sm->m) {
+ wpa_printf(MSG_DEBUG, "EAP: initialize selected EAP "
+ "method (%d, %s)",
+ sm->selectedMethod, sm->m->name);
+ if (reinit)
+ sm->eap_method_priv = sm->m->init_for_reauth(
+ sm, sm->eap_method_priv);
+ else
+ sm->eap_method_priv = sm->m->init(sm);
+ if (sm->eap_method_priv == NULL) {
+ wpa_printf(MSG_DEBUG, "EAP: Failed to "
+ "initialize EAP method %d",
+ sm->selectedMethod);
+ sm->m = NULL;
+ sm->methodState = METHOD_NONE;
+ sm->selectedMethod = EAP_TYPE_NONE;
+ } else {
+ sm->methodState = METHOD_INIT;
+ return;
+ }
+ }
+ }
+
+ free(sm->eapRespData);
+ sm->eapRespData = eap_sm_buildNak(sm, sm->reqId, &sm->eapRespDataLen);
+}
+
+
+SM_STATE(EAP, METHOD)
+{
+ u8 *eapReqData;
+ size_t eapReqDataLen;
+ struct eap_method_ret ret;
+
+ SM_ENTRY(EAP, METHOD);
+ if (sm->m == NULL) {
+ wpa_printf(MSG_WARNING, "EAP::METHOD - method not selected");
+ return;
+ }
+
+ eapReqData = eapol_get_eapReqData(sm, &eapReqDataLen);
+
+ /* Get ignore, methodState, decision, allowNotifications, and
+ * eapRespData. */
+ memset(&ret, 0, sizeof(ret));
+ ret.ignore = sm->ignore;
+ ret.methodState = sm->methodState;
+ ret.decision = sm->decision;
+ ret.allowNotifications = sm->allowNotifications;
+ free(sm->eapRespData);
+ sm->eapRespData = sm->m->process(sm, sm->eap_method_priv, &ret,
+ eapReqData, eapReqDataLen,
+ &sm->eapRespDataLen);
+ wpa_printf(MSG_DEBUG, "EAP: method process -> ignore=%s "
+ "methodState=%s decision=%s",
+ ret.ignore ? "TRUE" : "FALSE",
+ eap_sm_method_state_txt(ret.methodState),
+ eap_sm_decision_txt(ret.decision));
+
+ sm->ignore = ret.ignore;
+ if (sm->ignore)
+ return;
+ sm->methodState = ret.methodState;
+ sm->decision = ret.decision;
+ sm->allowNotifications = ret.allowNotifications;
+
+ if (sm->m->isKeyAvailable && sm->m->getKey &&
+ sm->m->isKeyAvailable(sm, sm->eap_method_priv)) {
+ free(sm->eapKeyData);
+ sm->eapKeyData = sm->m->getKey(sm, sm->eap_method_priv,
+ &sm->eapKeyDataLen);
+ }
+}
+
+
+SM_STATE(EAP, SEND_RESPONSE)
+{
+ SM_ENTRY(EAP, SEND_RESPONSE);
+ free(sm->lastRespData);
+ if (sm->eapRespData) {
+ if (sm->workaround)
+ memcpy(sm->last_md5, sm->req_md5, 16);
+ sm->lastId = sm->reqId;
+ sm->lastRespData = malloc(sm->eapRespDataLen);
+ if (sm->lastRespData) {
+ memcpy(sm->lastRespData, sm->eapRespData,
+ sm->eapRespDataLen);
+ sm->lastRespDataLen = sm->eapRespDataLen;
+ }
+ eapol_set_bool(sm, EAPOL_eapResp, TRUE);
+ } else
+ sm->lastRespData = NULL;
+ eapol_set_bool(sm, EAPOL_eapReq, FALSE);
+ eapol_set_int(sm, EAPOL_idleWhile, sm->ClientTimeout);
+}
+
+
+SM_STATE(EAP, DISCARD)
+{
+ SM_ENTRY(EAP, DISCARD);
+ eapol_set_bool(sm, EAPOL_eapReq, FALSE);
+ eapol_set_bool(sm, EAPOL_eapNoResp, TRUE);
+}
+
+
+SM_STATE(EAP, IDENTITY)
+{
+ u8 *eapReqData;
+ size_t eapReqDataLen;
+
+ SM_ENTRY(EAP, IDENTITY);
+ eapReqData = eapol_get_eapReqData(sm, &eapReqDataLen);
+ eap_sm_processIdentity(sm, eapReqData, eapReqDataLen);
+ free(sm->eapRespData);
+ sm->eapRespData = eap_sm_buildIdentity(sm, sm->reqId,
+ &sm->eapRespDataLen, 0);
+}
+
+
+SM_STATE(EAP, NOTIFICATION)
+{
+ u8 *eapReqData;
+ size_t eapReqDataLen;
+
+ SM_ENTRY(EAP, NOTIFICATION);
+ eapReqData = eapol_get_eapReqData(sm, &eapReqDataLen);
+ eap_sm_processNotify(sm, eapReqData, eapReqDataLen);
+ free(sm->eapRespData);
+ sm->eapRespData = eap_sm_buildNotify(sm, sm->reqId,
+ &sm->eapRespDataLen);
+}
+
+
+SM_STATE(EAP, RETRANSMIT)
+{
+ SM_ENTRY(EAP, RETRANSMIT);
+ free(sm->eapRespData);
+ if (sm->lastRespData) {
+ sm->eapRespData = malloc(sm->lastRespDataLen);
+ if (sm->eapRespData) {
+ memcpy(sm->eapRespData, sm->lastRespData,
+ sm->lastRespDataLen);
+ sm->eapRespDataLen = sm->lastRespDataLen;
+ }
+ } else
+ sm->eapRespData = NULL;
+}
+
+
+SM_STATE(EAP, SUCCESS)
+{
+ SM_ENTRY(EAP, SUCCESS);
+ if (sm->eapKeyData != NULL)
+ sm->eapKeyAvailable = TRUE;
+ eapol_set_bool(sm, EAPOL_eapSuccess, TRUE);
+ /* draft-ietf-eap-statemachine-02.pdf does not clear eapReq here, but
+ * this seems to be required to avoid processing the same request
+ * twice when state machine is initialized. */
+ eapol_set_bool(sm, EAPOL_eapReq, FALSE);
+ /* draft-ietf-eap-statemachine-02.pdf does not set eapNoResp here, but
+ * this seems to be required to get EAPOL Supplicant backend state
+ * machine into SUCCESS state. In addition, either eapResp or eapNoResp
+ * is required to be set after processing the received EAP frame. */
+ eapol_set_bool(sm, EAPOL_eapNoResp, TRUE);
+}
+
+
+SM_STATE(EAP, FAILURE)
+{
+ SM_ENTRY(EAP, FAILURE);
+ eapol_set_bool(sm, EAPOL_eapFail, TRUE);
+ /* draft-ietf-eap-statemachine-02.pdf does not clear eapReq here, but
+ * this seems to be required to avoid processing the same request
+ * twice when state machine is initialized. */
+ eapol_set_bool(sm, EAPOL_eapReq, FALSE);
+ /* draft-ietf-eap-statemachine-02.pdf does not set eapNoResp here.
+ * However, either eapResp or eapNoResp is required to be set after
+ * processing the received EAP frame. */
+ eapol_set_bool(sm, EAPOL_eapNoResp, TRUE);
+}
+
+
+static int eap_success_workaround(struct eap_sm *sm, int reqId, int lastId)
+{
+ /* At least Microsoft IAS and Meetinghouse Aegis seem to be sending
+ * EAP-Success/Failure with lastId + 1 even though RFC 3748 and
+ * draft-ietf-eap-statemachine-05.pdf require that reqId == lastId.
+ * Accept this kind of Id if EAP workarounds are enabled. These are
+ * unauthenticated plaintext messages, so this should have minimal
+ * security implications (bit easier to fake EAP-Success/Failure). */
+ if (sm->workaround && reqId == ((lastId + 1) & 0xff)) {
+ wpa_printf(MSG_DEBUG, "EAP: Workaround for unexpected "
+ "identifier field in EAP Success: "
+ "reqId=%d lastId=%d (these are supposed to be "
+ "same)", reqId, lastId);
+ return 1;
+ }
+ return 0;
+}
+
+
+SM_STEP(EAP)
+{
+ int duplicate;
+
+ if (eapol_get_bool(sm, EAPOL_eapRestart) &&
+ eapol_get_bool(sm, EAPOL_portEnabled))
+ SM_ENTER_GLOBAL(EAP, INITIALIZE);
+ else if (!eapol_get_bool(sm, EAPOL_portEnabled))
+ SM_ENTER_GLOBAL(EAP, DISABLED);
+ else if (sm->num_rounds > EAP_MAX_AUTH_ROUNDS) {
+ if (sm->num_rounds == EAP_MAX_AUTH_ROUNDS + 1) {
+ wpa_printf(MSG_DEBUG, "EAP: more than %d "
+ "authentication rounds - abort",
+ EAP_MAX_AUTH_ROUNDS);
+ sm->num_rounds++;
+ SM_ENTER_GLOBAL(EAP, FAILURE);
+ }
+ } else switch (sm->EAP_state) {
+ case EAP_INITIALIZE:
+ SM_ENTER(EAP, IDLE);
+ break;
+ case EAP_DISABLED:
+ if (eapol_get_bool(sm, EAPOL_portEnabled))
+ SM_ENTER(EAP, INITIALIZE);
+ break;
+ case EAP_IDLE:
+ if (eapol_get_bool(sm, EAPOL_eapReq))
+ SM_ENTER(EAP, RECEIVED);
+ else if ((eapol_get_bool(sm, EAPOL_altAccept) &&
+ sm->decision != DECISION_FAIL) ||
+ (eapol_get_int(sm, EAPOL_idleWhile) == 0 &&
+ sm->decision == DECISION_UNCOND_SUCC))
+ SM_ENTER(EAP, SUCCESS);
+ else if (eapol_get_bool(sm, EAPOL_altReject) ||
+ (eapol_get_int(sm, EAPOL_idleWhile) == 0 &&
+ sm->decision != DECISION_UNCOND_SUCC) ||
+ (eapol_get_bool(sm, EAPOL_altAccept) &&
+ sm->methodState != METHOD_CONT &&
+ sm->decision == DECISION_FAIL))
+ SM_ENTER(EAP, FAILURE);
+ else if (sm->selectedMethod == EAP_TYPE_LEAP &&
+ sm->leap_done && sm->decision != DECISION_FAIL &&
+ sm->methodState == METHOD_DONE)
+ SM_ENTER(EAP, SUCCESS);
+ else if (sm->selectedMethod == EAP_TYPE_PEAP &&
+ sm->peap_done && sm->decision != DECISION_FAIL &&
+ sm->methodState == METHOD_DONE)
+ SM_ENTER(EAP, SUCCESS);
+ break;
+ case EAP_RECEIVED:
+ duplicate = sm->reqId == sm->lastId;
+ if (sm->workaround && duplicate &&
+ memcmp(sm->req_md5, sm->last_md5, 16) != 0) {
+ /* draft-ietf-eap-statemachine-05.txt uses
+ * (reqId == lastId) as the only verification for
+ * duplicate EAP requests. However, this misses cases
+ * where the AS is incorrectly using the same id again;
+ * and unfortunately, such implementations exist. Use
+ * MD5 hash as an extra verification for the packets
+ * being duplicate to workaround these issues. */
+ wpa_printf(MSG_DEBUG, "EAP: AS used the same Id again,"
+ " but EAP packets were not identical");
+ wpa_printf(MSG_DEBUG, "EAP: workaround - assume this "
+ "is not a duplicate packet");
+ duplicate = 0;
+ }
+
+ if (sm->rxSuccess &&
+ (sm->reqId == sm->lastId ||
+ eap_success_workaround(sm, sm->reqId, sm->lastId)) &&
+ sm->decision != DECISION_FAIL)
+ SM_ENTER(EAP, SUCCESS);
+ else if (sm->methodState != METHOD_CONT &&
+ ((sm->rxFailure &&
+ sm->decision != DECISION_UNCOND_SUCC) ||
+ (sm->rxSuccess && sm->decision == DECISION_FAIL)) &&
+ (sm->reqId == sm->lastId ||
+ eap_success_workaround(sm, sm->reqId, sm->lastId)))
+ SM_ENTER(EAP, FAILURE);
+ else if (sm->rxReq && duplicate)
+ SM_ENTER(EAP, RETRANSMIT);
+ else if (sm->rxReq && !duplicate &&
+ sm->reqMethod == EAP_TYPE_NOTIFICATION &&
+ sm->allowNotifications)
+ SM_ENTER(EAP, NOTIFICATION);
+ else if (sm->rxReq && !duplicate &&
+ sm->selectedMethod == EAP_TYPE_NONE &&
+ sm->reqMethod == EAP_TYPE_IDENTITY)
+ SM_ENTER(EAP, IDENTITY);
+ else if (sm->rxReq && !duplicate &&
+ sm->selectedMethod == EAP_TYPE_NONE &&
+ sm->reqMethod != EAP_TYPE_IDENTITY &&
+ sm->reqMethod != EAP_TYPE_NOTIFICATION)
+ SM_ENTER(EAP, GET_METHOD);
+ else if (sm->rxReq && !duplicate &&
+ sm->reqMethod == sm->selectedMethod &&
+ sm->methodState != METHOD_DONE)
+ SM_ENTER(EAP, METHOD);
+ else if (sm->selectedMethod == EAP_TYPE_LEAP &&
+ (sm->rxSuccess || sm->rxResp))
+ SM_ENTER(EAP, METHOD);
+ else
+ SM_ENTER(EAP, DISCARD);
+ break;
+ case EAP_GET_METHOD:
+ if (sm->selectedMethod == sm->reqMethod)
+ SM_ENTER(EAP, METHOD);
+ else
+ SM_ENTER(EAP, SEND_RESPONSE);
+ break;
+ case EAP_METHOD:
+ if (sm->ignore)
+ SM_ENTER(EAP, DISCARD);
+ else
+ SM_ENTER(EAP, SEND_RESPONSE);
+ break;
+ case EAP_SEND_RESPONSE:
+ SM_ENTER(EAP, IDLE);
+ break;
+ case EAP_DISCARD:
+ SM_ENTER(EAP, IDLE);
+ break;
+ case EAP_IDENTITY:
+ SM_ENTER(EAP, SEND_RESPONSE);
+ break;
+ case EAP_NOTIFICATION:
+ SM_ENTER(EAP, SEND_RESPONSE);
+ break;
+ case EAP_RETRANSMIT:
+ SM_ENTER(EAP, SEND_RESPONSE);
+ break;
+ case EAP_SUCCESS:
+ break;
+ case EAP_FAILURE:
+ break;
+ }
+}
+
+
+static Boolean eap_sm_allowMethod(struct eap_sm *sm, EapType method)
+{
+ struct wpa_ssid *config = eap_get_config(sm);
+ int i;
+
+ if (!wpa_config_allowed_eap_method(config, method))
+ return FALSE;
+ for (i = 0; i < NUM_EAP_METHODS; i++) {
+ if (eap_methods[i]->method == method)
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+static u8 *eap_sm_buildNak(struct eap_sm *sm, int id, size_t *len)
+{
+ struct wpa_ssid *config = eap_get_config(sm);
+ struct eap_hdr *resp;
+ u8 *pos;
+ int i, found = 0;
+
+ wpa_printf(MSG_DEBUG, "EAP: Building EAP-Nak (requested type %d not "
+ "allowed)", sm->reqMethod);
+ *len = sizeof(struct eap_hdr) + 1;
+ resp = malloc(*len + NUM_EAP_METHODS);
+ if (resp == NULL)
+ return NULL;
+
+ resp->code = EAP_CODE_RESPONSE;
+ resp->identifier = id;
+ pos = (u8 *) (resp + 1);
+ *pos++ = EAP_TYPE_NAK;
+
+ for (i = 0; i < NUM_EAP_METHODS; i++) {
+ if (wpa_config_allowed_eap_method(config,
+ eap_methods[i]->method)) {
+ *pos++ = eap_methods[i]->method;
+ (*len)++;
+ found++;
+ }
+ }
+ if (!found) {
+ *pos = EAP_TYPE_NONE;
+ (*len)++;
+ }
+ wpa_hexdump(MSG_DEBUG, "EAP: allowed methods",
+ ((u8 *) (resp + 1)) + 1, found);
+
+ resp->length = host_to_be16(*len);
+
+ return (u8 *) resp;
+}
+
+
+static void eap_sm_processIdentity(struct eap_sm *sm, u8 *req, size_t len)
+{
+ struct eap_hdr *hdr = (struct eap_hdr *) req;
+ u8 *pos = (u8 *) (hdr + 1);
+ pos++;
+ /* 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);
+}
+
+
+u8 *eap_sm_buildIdentity(struct eap_sm *sm, int id, size_t *len,
+ int encrypted)
+{
+ struct wpa_ssid *config = eap_get_config(sm);
+ struct eap_hdr *resp;
+ u8 *pos;
+ const u8 *identity;
+ size_t identity_len;
+
+ if (config == NULL) {
+ wpa_printf(MSG_WARNING, "EAP: buildIdentity: configuration "
+ "was not available");
+ return NULL;
+ }
+
+ if (sm->m && sm->m->get_identity &&
+ (identity = sm->m->get_identity(sm, sm->eap_method_priv,
+ &identity_len)) != NULL) {
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP: using method re-auth "
+ "identity", identity, identity_len);
+ } else if (!encrypted && config->anonymous_identity) {
+ identity = config->anonymous_identity;
+ identity_len = config->anonymous_identity_len;
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP: using anonymous identity",
+ identity, identity_len);
+ } else {
+ identity = config->identity;
+ identity_len = config->identity_len;
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP: using real identity",
+ identity, identity_len);
+ }
+
+ if (identity == NULL) {
+ wpa_printf(MSG_WARNING, "EAP: buildIdentity: identity "
+ "configuration was not available");
+ eap_sm_request_identity(sm, config);
+ return NULL;
+ }
+
+
+ *len = sizeof(struct eap_hdr) + 1 + identity_len;
+ resp = malloc(*len);
+ if (resp == NULL)
+ return NULL;
+
+ resp->code = EAP_CODE_RESPONSE;
+ resp->identifier = id;
+ resp->length = host_to_be16(*len);
+ pos = (u8 *) (resp + 1);
+ *pos++ = EAP_TYPE_IDENTITY;
+ memcpy(pos, identity, identity_len);
+
+ return (u8 *) resp;
+}
+
+
+static void eap_sm_processNotify(struct eap_sm *sm, u8 *req, size_t len)
+{
+ struct eap_hdr *hdr = (struct eap_hdr *) req;
+ u8 *pos = (u8 *) (hdr + 1);
+ pos++;
+ /* TODO: log the Notification Request and make it available for UI */
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP: EAP-Request Notification data",
+ pos, be_to_host16(hdr->length) - 5);
+}
+
+
+static u8 *eap_sm_buildNotify(struct eap_sm *sm, int id, size_t *len)
+{
+ struct eap_hdr *resp;
+ u8 *pos;
+
+ wpa_printf(MSG_DEBUG, "EAP: Generating EAP-Response Notification");
+ *len = sizeof(struct eap_hdr) + 1;
+ resp = malloc(*len);
+ if (resp == NULL)
+ return NULL;
+
+ resp->code = EAP_CODE_RESPONSE;
+ resp->identifier = id;
+ resp->length = host_to_be16(*len);
+ pos = (u8 *) (resp + 1);
+ *pos = EAP_TYPE_NOTIFICATION;
+
+ return (u8 *) resp;
+}
+
+
+static void eap_sm_parseEapReq(struct eap_sm *sm, u8 *req, size_t len)
+{
+ struct eap_hdr *hdr;
+ size_t plen;
+ MD5_CTX context;
+
+ sm->rxReq = sm->rxSuccess = sm->rxFailure = FALSE;
+ sm->reqId = 0;
+ sm->reqMethod = EAP_TYPE_NONE;
+
+ if (req == NULL || len < sizeof(*hdr))
+ return;
+
+ hdr = (struct eap_hdr *) req;
+ plen = be_to_host16(hdr->length);
+ if (plen > len) {
+ wpa_printf(MSG_DEBUG, "EAP: Ignored truncated EAP-Packet "
+ "(len=%lu plen=%lu)",
+ (unsigned long) len, (unsigned long) plen);
+ return;
+ }
+
+ sm->reqId = hdr->identifier;
+
+ if (sm->workaround) {
+ MD5Init(&context);
+ MD5Update(&context, req, len);
+ MD5Final(sm->req_md5, &context);
+ }
+
+ switch (hdr->code) {
+ case EAP_CODE_REQUEST:
+ sm->rxReq = TRUE;
+ if (plen > sizeof(*hdr))
+ sm->reqMethod = *((u8 *) (hdr + 1));
+ wpa_printf(MSG_DEBUG, "EAP: Received EAP-Request method=%d "
+ "id=%d", sm->reqMethod, sm->reqId);
+ break;
+ case EAP_CODE_RESPONSE:
+ if (sm->selectedMethod == EAP_TYPE_LEAP) {
+ sm->rxResp = TRUE;
+ if (plen > sizeof(*hdr))
+ sm->reqMethod = *((u8 *) (hdr + 1));
+ wpa_printf(MSG_DEBUG, "EAP: Received EAP-Response for "
+ "LEAP method=%d id=%d",
+ sm->reqMethod, sm->reqId);
+ break;
+ }
+ wpa_printf(MSG_DEBUG, "EAP: Ignored EAP-Response");
+ break;
+ case EAP_CODE_SUCCESS:
+ wpa_printf(MSG_DEBUG, "EAP: Received EAP-Success");
+ sm->rxSuccess = TRUE;
+ break;
+ case EAP_CODE_FAILURE:
+ wpa_printf(MSG_DEBUG, "EAP: Received EAP-Failure");
+ sm->rxFailure = TRUE;
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "EAP: Ignored EAP-Packet with unknown "
+ "code %d", hdr->code);
+ break;
+ }
+}
+
+
+struct eap_sm *eap_sm_init(void *eapol_ctx, struct eapol_callbacks *eapol_cb,
+ void *msg_ctx)
+{
+ struct eap_sm *sm;
+
+ sm = malloc(sizeof(*sm));
+ if (sm == NULL)
+ return NULL;
+ memset(sm, 0, sizeof(*sm));
+ sm->eapol_ctx = eapol_ctx;
+ sm->eapol_cb = eapol_cb;
+ sm->msg_ctx = msg_ctx;
+ sm->ClientTimeout = 60;
+
+ sm->ssl_ctx = tls_init();
+ if (sm->ssl_ctx == NULL) {
+ wpa_printf(MSG_WARNING, "SSL: Failed to initialize TLS "
+ "context.");
+ free(sm);
+ return NULL;
+ }
+
+ return sm;
+}
+
+
+void eap_sm_deinit(struct eap_sm *sm)
+{
+ if (sm == NULL)
+ return;
+ eap_deinit_prev_method(sm, "EAP deinit");
+ free(sm->lastRespData);
+ free(sm->eapRespData);
+ free(sm->eapKeyData);
+ tls_deinit(sm->ssl_ctx);
+ free(sm);
+}
+
+
+int eap_sm_step(struct eap_sm *sm)
+{
+ int res = 0;
+ do {
+ sm->changed = FALSE;
+ SM_STEP_RUN(EAP);
+ if (sm->changed)
+ res = 1;
+ } while (sm->changed);
+ return res;
+}
+
+
+void eap_sm_abort(struct eap_sm *sm)
+{
+ /* release system resources that may have been allocated for the
+ * authentication session */
+ free(sm->eapRespData);
+ sm->eapRespData = NULL;
+ free(sm->eapKeyData);
+ sm->eapKeyData = NULL;
+}
+
+
+static const char * eap_sm_state_txt(int state)
+{
+ switch (state) {
+ case EAP_INITIALIZE:
+ return "INITIALIZE";
+ case EAP_DISABLED:
+ return "DISABLED";
+ case EAP_IDLE:
+ return "IDLE";
+ case EAP_RECEIVED:
+ return "RECEIVED";
+ case EAP_GET_METHOD:
+ return "GET_METHOD";
+ case EAP_METHOD:
+ return "METHOD";
+ case EAP_SEND_RESPONSE:
+ return "SEND_RESPONSE";
+ case EAP_DISCARD:
+ return "DISCARD";
+ case EAP_IDENTITY:
+ return "IDENTITY";
+ case EAP_NOTIFICATION:
+ return "NOTIFICATION";
+ case EAP_RETRANSMIT:
+ return "RETRANSMIT";
+ case EAP_SUCCESS:
+ return "SUCCESS";
+ case EAP_FAILURE:
+ return "FAILURE";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+
+static const char * eap_sm_method_state_txt(int state)
+{
+ switch (state) {
+ case METHOD_NONE:
+ return "NONE";
+ case METHOD_INIT:
+ return "INIT";
+ case METHOD_CONT:
+ return "CONT";
+ case METHOD_MAY_CONT:
+ return "MAY_CONT";
+ case METHOD_DONE:
+ return "DONE";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+
+static const char * eap_sm_decision_txt(int decision)
+{
+ switch (decision) {
+ case DECISION_FAIL:
+ return "FAIL";
+ case DECISION_COND_SUCC:
+ return "COND_SUCC";
+ case DECISION_UNCOND_SUCC:
+ return "UNCOND_SUCC";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+
+int eap_sm_get_status(struct eap_sm *sm, char *buf, size_t buflen, int verbose)
+{
+ int len;
+
+ if (sm == NULL)
+ return 0;
+
+ len = snprintf(buf, buflen,
+ "EAP state=%s\n",
+ eap_sm_state_txt(sm->EAP_state));
+
+ if (sm->selectedMethod != EAP_TYPE_NONE) {
+ const char *name;
+ if (sm->m) {
+ name = sm->m->name;
+ } else {
+ const struct eap_method *m =
+ eap_sm_get_eap_methods(sm->selectedMethod);
+ if (m)
+ name = m->name;
+ else
+ name = "?";
+ }
+ len += snprintf(buf + len, buflen - len,
+ "selectedMethod=%d (EAP-%s)\n",
+ sm->selectedMethod, name);
+
+ if (sm->m && sm->m->get_status) {
+ len += sm->m->get_status(sm, sm->eap_method_priv,
+ buf + len, buflen - len,
+ verbose);
+ }
+ }
+
+ if (verbose) {
+ len += snprintf(buf + len, buflen - len,
+ "reqMethod=%d\n"
+ "methodState=%s\n"
+ "decision=%s\n"
+ "ClientTimeout=%d\n",
+ sm->reqMethod,
+ eap_sm_method_state_txt(sm->methodState),
+ eap_sm_decision_txt(sm->decision),
+ sm->ClientTimeout);
+ }
+
+ return len;
+}
+
+
+typedef enum { TYPE_IDENTITY, TYPE_PASSWORD, TYPE_OTP } eap_ctrl_req_type;
+
+static void eap_sm_request(struct eap_sm *sm, struct wpa_ssid *config,
+ eap_ctrl_req_type type, char *msg, size_t msglen)
+{
+ char *buf;
+ size_t buflen;
+ int len;
+ char *field;
+ char *txt, *tmp;
+
+ if (config == NULL || sm == NULL)
+ return;
+
+ switch (type) {
+ case TYPE_IDENTITY:
+ field = "IDENTITY";
+ txt = "Identity";
+ config->pending_req_identity++;
+ break;
+ case TYPE_PASSWORD:
+ field = "PASSWORD";
+ txt = "Password";
+ config->pending_req_password++;
+ break;
+ case TYPE_OTP:
+ field = "OTP";
+ if (msg) {
+ tmp = malloc(msglen + 3);
+ if (tmp == NULL)
+ return;
+ tmp[0] = '[';
+ memcpy(tmp + 1, msg, msglen);
+ tmp[msglen + 1] = ']';
+ tmp[msglen + 2] = '\0';
+ txt = tmp;
+ free(config->pending_req_otp);
+ config->pending_req_otp = tmp;
+ config->pending_req_otp_len = msglen + 3;
+ } else {
+ if (config->pending_req_otp == NULL)
+ return;
+ txt = config->pending_req_otp;
+ }
+ break;
+ default:
+ return;
+ }
+
+ buflen = 100 + strlen(txt) + config->ssid_len;
+ buf = malloc(buflen);
+ if (buf == NULL)
+ return;
+ len = snprintf(buf, buflen, "CTRL-REQ-%s-%d:%s needed for SSID ",
+ field, config->id, txt);
+ if (config->ssid && buflen > len + config->ssid_len) {
+ memcpy(buf + len, config->ssid, config->ssid_len);
+ len += config->ssid_len;
+ buf[len] = '\0';
+ }
+ wpa_msg(sm->msg_ctx, MSG_INFO, buf);
+ free(buf);
+}
+
+
+void eap_sm_request_identity(struct eap_sm *sm, struct wpa_ssid *config)
+{
+ eap_sm_request(sm, config, TYPE_IDENTITY, NULL, 0);
+}
+
+
+void eap_sm_request_password(struct eap_sm *sm, struct wpa_ssid *config)
+{
+ eap_sm_request(sm, config, TYPE_PASSWORD, NULL, 0);
+}
+
+
+void eap_sm_request_otp(struct eap_sm *sm, struct wpa_ssid *config,
+ char *msg, size_t msg_len)
+{
+ eap_sm_request(sm, config, TYPE_OTP, msg, msg_len);
+}
+
+
+void eap_sm_notify_ctrl_attached(struct eap_sm *sm)
+{
+ struct wpa_ssid *config = eap_get_config(sm);
+
+ if (config == NULL)
+ return;
+
+ /* Re-send any pending requests for user data since a new control
+ * interface was added. This handles cases where the EAP authentication
+ * starts immediately after system startup when the user interface is
+ * not yet running. */
+ if (config->pending_req_identity)
+ eap_sm_request_identity(sm, config);
+ if (config->pending_req_password)
+ eap_sm_request_password(sm, config);
+ if (config->pending_req_otp)
+ eap_sm_request_otp(sm, config, NULL, 0);
+}
+
+
+u8 eap_get_type(const char *name)
+{
+ int i;
+ for (i = 0; i < NUM_EAP_METHODS; i++) {
+ if (strcmp(eap_methods[i]->name, name) == 0)
+ return eap_methods[i]->method;
+ }
+ return EAP_TYPE_NONE;
+}
+
+
+static int eap_allowed_phase2_type(int type)
+{
+ return type != EAP_TYPE_PEAP && type != EAP_TYPE_TTLS &&
+ type != EAP_TYPE_FAST;
+}
+
+
+u8 eap_get_phase2_type(const char *name)
+{
+ u8 type = eap_get_type(name);
+ if (eap_allowed_phase2_type(type))
+ return type;
+ return EAP_TYPE_NONE;
+}
+
+
+u8 *eap_get_phase2_types(struct wpa_ssid *config, size_t *count)
+{
+ u8 *buf, method;
+ int i;
+
+ *count = 0;
+ buf = malloc(NUM_EAP_METHODS);
+ if (buf == NULL)
+ return NULL;
+
+ for (i = 0; i < NUM_EAP_METHODS; i++) {
+ method = eap_methods[i]->method;
+ if (eap_allowed_phase2_type(method)) {
+ if (method == EAP_TYPE_TLS && config &&
+ config->private_key2 == NULL)
+ continue;
+ buf[*count] = method;
+ (*count)++;
+ }
+ }
+
+ return buf;
+}
+
+
+void eap_set_fast_reauth(struct eap_sm *sm, int enabled)
+{
+ sm->fast_reauth = enabled;
+}
+
+
+void eap_set_workaround(struct eap_sm *sm, unsigned int workaround)
+{
+ sm->workaround = workaround;
+}
+
+
+struct wpa_ssid * eap_get_config(struct eap_sm *sm)
+{
+ return sm->eapol_cb->get_config(sm->eapol_ctx);
+}
+
+
+int eap_key_available(struct eap_sm *sm)
+{
+ return sm ? sm->eapKeyAvailable : 0;
+}
+
+
+void eap_notify_success(struct eap_sm *sm)
+{
+ if (sm) {
+ sm->decision = DECISION_COND_SUCC;
+ sm->EAP_state = EAP_SUCCESS;
+ }
+}
+
+
+u8 * eap_get_eapKeyData(struct eap_sm *sm, size_t *len)
+{
+ if (sm == NULL || sm->eapKeyData == NULL) {
+ *len = 0;
+ return NULL;
+ }
+
+ *len = sm->eapKeyDataLen;
+ return sm->eapKeyData;
+}
+
+
+u8 * eap_get_eapRespData(struct eap_sm *sm, size_t *len)
+{
+ u8 *resp;
+
+ if (sm == NULL || sm->eapRespData == NULL) {
+ *len = 0;
+ return NULL;
+ }
+
+ resp = sm->eapRespData;
+ *len = sm->eapRespDataLen;
+ sm->eapRespData = NULL;
+ sm->eapRespDataLen = 0;
+
+ return resp;
+}
+
+
+void eap_register_scard_ctx(struct eap_sm *sm, void *ctx)
+{
+ if (sm)
+ sm->scard_ctx = ctx;
+}
diff --git a/contrib/wpa_supplicant/eap.h b/contrib/wpa_supplicant/eap.h
new file mode 100644
index 000000000000..3d7cc7210d7a
--- /dev/null
+++ b/contrib/wpa_supplicant/eap.h
@@ -0,0 +1,70 @@
+#ifndef EAP_H
+#define EAP_H
+
+#include "defs.h"
+#include "eap_defs.h"
+
+struct eap_sm;
+struct wpa_ssid;
+
+
+#ifdef IEEE8021X_EAPOL
+
+enum eapol_bool_var {
+ EAPOL_eapSuccess, EAPOL_eapRestart, EAPOL_eapFail, EAPOL_eapResp,
+ EAPOL_eapNoResp, EAPOL_eapReq, EAPOL_portEnabled, EAPOL_altAccept,
+ EAPOL_altReject
+};
+
+enum eapol_int_var {
+ EAPOL_idleWhile
+};
+
+struct eapol_callbacks {
+ struct wpa_ssid * (*get_config)(void *ctx);
+ Boolean (*get_bool)(void *ctx, enum eapol_bool_var variable);
+ void (*set_bool)(void *ctx, enum eapol_bool_var variable,
+ Boolean value);
+ unsigned int (*get_int)(void *ctx, enum eapol_int_var variable);
+ void (*set_int)(void *ctx, enum eapol_int_var variable,
+ unsigned int value);
+ u8 * (*get_eapReqData)(void *ctx, size_t *len);
+};
+
+struct eap_sm *eap_sm_init(void *eapol_ctx, struct eapol_callbacks *eapol_cb,
+ void *msg_ctx);
+void eap_sm_deinit(struct eap_sm *sm);
+int eap_sm_step(struct eap_sm *sm);
+void eap_sm_abort(struct eap_sm *sm);
+int eap_sm_get_status(struct eap_sm *sm, char *buf, size_t buflen,
+ int verbose);
+u8 *eap_sm_buildIdentity(struct eap_sm *sm, int id, size_t *len,
+ int encrypted);
+const struct eap_method * eap_sm_get_eap_methods(int method);
+void eap_sm_request_identity(struct eap_sm *sm, struct wpa_ssid *config);
+void eap_sm_request_password(struct eap_sm *sm, struct wpa_ssid *config);
+void eap_sm_request_otp(struct eap_sm *sm, struct wpa_ssid *config,
+ char *msg, size_t msg_len);
+void eap_sm_notify_ctrl_attached(struct eap_sm *sm);
+u8 eap_get_type(const char *name);
+u8 eap_get_phase2_type(const char *name);
+u8 *eap_get_phase2_types(struct wpa_ssid *config, size_t *count);
+void eap_set_fast_reauth(struct eap_sm *sm, int enabled);
+void eap_set_workaround(struct eap_sm *sm, unsigned int workaround);
+struct wpa_ssid * eap_get_config(struct eap_sm *sm);
+int eap_key_available(struct eap_sm *sm);
+void eap_notify_success(struct eap_sm *sm);
+u8 * eap_get_eapKeyData(struct eap_sm *sm, size_t *len);
+u8 * eap_get_eapRespData(struct eap_sm *sm, size_t *len);
+void eap_register_scard_ctx(struct eap_sm *sm, void *ctx);
+
+#else /* IEEE8021X_EAPOL */
+
+static inline u8 eap_get_type(const char *name)
+{
+ return EAP_TYPE_NONE;
+}
+
+#endif /* IEEE8021X_EAPOL */
+
+#endif /* EAP_H */
diff --git a/contrib/wpa_supplicant/eap_aka.c b/contrib/wpa_supplicant/eap_aka.c
new file mode 100644
index 000000000000..7fe6449ab896
--- /dev/null
+++ b/contrib/wpa_supplicant/eap_aka.c
@@ -0,0 +1,915 @@
+/*
+ * WPA Supplicant / EAP-AKA (draft-arkko-pppext-eap-aka-12.txt)
+ * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published 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 <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "common.h"
+#include "eap_i.h"
+#include "wpa_supplicant.h"
+#include "config_ssid.h"
+#include "sha1.h"
+#include "pcsc_funcs.h"
+#include "eap_sim_common.h"
+
+/* EAP-AKA Subtypes */
+#define EAP_AKA_SUBTYPE_CHALLENGE 1
+#define EAP_AKA_SUBTYPE_AUTHENTICATION_REJECT 2
+#define EAP_AKA_SUBTYPE_SYNCHRONIZATION_FAILURE 4
+#define EAP_AKA_SUBTYPE_IDENTITY 5
+#define EAP_AKA_SUBTYPE_NOTIFICATION 12
+#define EAP_AKA_SUBTYPE_REAUTHENTICATION 13
+#define EAP_AKA_SUBTYPE_CLIENT_ERROR 14
+
+/* AT_CLIENT_ERROR_CODE error codes */
+#define EAP_AKA_UNABLE_TO_PROCESS_PACKET 0
+
+#define AKA_AUTS_LEN 14
+#define RES_MAX_LEN 16
+#define IK_LEN 16
+#define CK_LEN 16
+#define EAP_AKA_MAX_FAST_REAUTHS 1000
+
+struct eap_aka_data {
+ u8 ik[IK_LEN], ck[CK_LEN], res[RES_MAX_LEN];
+ size_t res_len;
+ u8 nonce_s[EAP_SIM_NONCE_S_LEN];
+ u8 mk[EAP_SIM_MK_LEN];
+ u8 k_aut[EAP_SIM_K_AUT_LEN];
+ u8 k_encr[EAP_SIM_K_ENCR_LEN];
+ u8 msk[EAP_SIM_KEYING_DATA_LEN];
+ u8 rand[AKA_RAND_LEN], autn[AKA_AUTN_LEN];
+ u8 auts[AKA_AUTS_LEN];
+
+ int num_id_req, num_notification;
+ u8 *pseudonym;
+ size_t pseudonym_len;
+ u8 *reauth_id;
+ size_t reauth_id_len;
+ int reauth;
+ unsigned int counter, counter_too_small;
+ u8 *last_eap_identity;
+ size_t last_eap_identity_len;
+ enum { CONTINUE, SUCCESS, FAILURE } state;
+};
+
+
+static void * eap_aka_init(struct eap_sm *sm)
+{
+ struct eap_aka_data *data;
+ data = malloc(sizeof(*data));
+ if (data == NULL)
+ return NULL;
+ memset(data, 0, sizeof(*data));
+
+ data->state = CONTINUE;
+
+ return data;
+}
+
+
+static void eap_aka_deinit(struct eap_sm *sm, void *priv)
+{
+ struct eap_aka_data *data = priv;
+ if (data) {
+ free(data->pseudonym);
+ free(data->reauth_id);
+ free(data->last_eap_identity);
+ free(data);
+ }
+}
+
+
+static int eap_aka_umts_auth(struct eap_sm *sm, struct eap_aka_data *data)
+{
+ wpa_printf(MSG_DEBUG, "EAP-AKA: UMTS authentication algorithm");
+#ifdef PCSC_FUNCS
+ return scard_umts_auth(sm->scard_ctx, data->rand,
+ data->autn, data->res, &data->res_len,
+ data->ik, data->ck, data->auts);
+#else /* PCSC_FUNCS */
+ /* These hardcoded Kc and SRES values are used for testing.
+ * Could consider making them configurable. */
+ memset(data->res, '2', RES_MAX_LEN);
+ data->res_len = 16;
+ memset(data->ik, '3', IK_LEN);
+ memset(data->ck, '4', CK_LEN);
+ {
+ u8 autn[AKA_AUTN_LEN];
+ memset(autn, '1', AKA_AUTN_LEN);
+ if (memcmp(autn, data->autn, AKA_AUTN_LEN) != 0) {
+ wpa_printf(MSG_WARNING, "EAP-AKA: AUTN did not match "
+ "with expected value");
+ return -1;
+ }
+ }
+ return 0;
+#endif /* PCSC_FUNCS */
+}
+
+
+static void eap_aka_derive_mk(struct eap_aka_data *data,
+ const u8 *identity, size_t identity_len)
+{
+ const u8 *addr[3];
+ size_t len[3];
+
+ addr[0] = identity;
+ len[0] = identity_len;
+ addr[1] = data->ik;
+ len[1] = IK_LEN;
+ addr[2] = data->ck;
+ len[2] = CK_LEN;
+
+ /* MK = SHA1(Identity|IK|CK) */
+ sha1_vector(3, addr, len, data->mk);
+ wpa_hexdump_key(MSG_DEBUG, "EAP-AKA: IK", data->ik, IK_LEN);
+ wpa_hexdump_key(MSG_DEBUG, "EAP-AKA: CK", data->ck, CK_LEN);
+ wpa_hexdump_key(MSG_DEBUG, "EAP-AKA: MK", data->mk, EAP_SIM_MK_LEN);
+}
+
+
+#define CLEAR_PSEUDONYM 0x01
+#define CLEAR_REAUTH_ID 0x02
+#define CLEAR_EAP_ID 0x04
+
+static void eap_aka_clear_identities(struct eap_aka_data *data, int id)
+{
+ wpa_printf(MSG_DEBUG, "EAP-AKA: forgetting old%s%s%s",
+ id & CLEAR_PSEUDONYM ? " pseudonym" : "",
+ id & CLEAR_REAUTH_ID ? " reauth_id" : "",
+ id & CLEAR_EAP_ID ? " eap_id" : "");
+ if (id & CLEAR_PSEUDONYM) {
+ free(data->pseudonym);
+ data->pseudonym = NULL;
+ data->pseudonym_len = 0;
+ }
+ if (id & CLEAR_REAUTH_ID) {
+ free(data->reauth_id);
+ data->reauth_id = NULL;
+ data->reauth_id_len = 0;
+ }
+ if (id & CLEAR_EAP_ID) {
+ free(data->last_eap_identity);
+ data->last_eap_identity = NULL;
+ data->last_eap_identity_len = 0;
+ }
+}
+
+
+static int eap_aka_learn_ids(struct eap_aka_data *data,
+ struct eap_sim_attrs *attr)
+{
+ if (attr->next_pseudonym) {
+ free(data->pseudonym);
+ data->pseudonym = malloc(attr->next_pseudonym_len);
+ if (data->pseudonym == NULL) {
+ wpa_printf(MSG_INFO, "EAP-AKA: (encr) No memory for "
+ "next pseudonym");
+ return -1;
+ }
+ memcpy(data->pseudonym, attr->next_pseudonym,
+ attr->next_pseudonym_len);
+ data->pseudonym_len = attr->next_pseudonym_len;
+ wpa_hexdump_ascii(MSG_DEBUG,
+ "EAP-AKA: (encr) AT_NEXT_PSEUDONYM",
+ data->pseudonym,
+ data->pseudonym_len);
+ }
+
+ if (attr->next_reauth_id) {
+ free(data->reauth_id);
+ data->reauth_id = malloc(attr->next_reauth_id_len);
+ if (data->reauth_id == NULL) {
+ wpa_printf(MSG_INFO, "EAP-AKA: (encr) No memory for "
+ "next reauth_id");
+ return -1;
+ }
+ memcpy(data->reauth_id, attr->next_reauth_id,
+ attr->next_reauth_id_len);
+ data->reauth_id_len = attr->next_reauth_id_len;
+ wpa_hexdump_ascii(MSG_DEBUG,
+ "EAP-AKA: (encr) AT_NEXT_REAUTH_ID",
+ data->reauth_id,
+ data->reauth_id_len);
+ }
+
+ return 0;
+}
+
+
+static u8 * eap_aka_client_error(struct eap_sm *sm, struct eap_aka_data *data,
+ struct eap_hdr *req,
+ size_t *respDataLen, int err)
+{
+ struct eap_sim_msg *msg;
+
+ data->state = FAILURE;
+ data->num_id_req = 0;
+ data->num_notification = 0;
+
+ msg = eap_sim_msg_init(EAP_CODE_RESPONSE, req->identifier,
+ EAP_TYPE_AKA, EAP_AKA_SUBTYPE_CLIENT_ERROR);
+ eap_sim_msg_add(msg, EAP_SIM_AT_CLIENT_ERROR_CODE, err, NULL, 0);
+ return eap_sim_msg_finish(msg, respDataLen, NULL, NULL, 0);
+}
+
+
+static u8 * eap_aka_authentication_reject(struct eap_sm *sm,
+ struct eap_aka_data *data,
+ struct eap_hdr *req,
+ size_t *respDataLen)
+{
+ struct eap_sim_msg *msg;
+
+ data->state = FAILURE;
+ data->num_id_req = 0;
+ data->num_notification = 0;
+
+ wpa_printf(MSG_DEBUG, "Generating EAP-AKA Authentication-Reject "
+ "(id=%d)", req->identifier);
+ msg = eap_sim_msg_init(EAP_CODE_RESPONSE, req->identifier,
+ EAP_TYPE_AKA,
+ EAP_AKA_SUBTYPE_AUTHENTICATION_REJECT);
+ return eap_sim_msg_finish(msg, respDataLen, NULL, NULL, 0);
+}
+
+
+static u8 * eap_aka_synchronization_failure(struct eap_sm *sm,
+ struct eap_aka_data *data,
+ struct eap_hdr *req,
+ size_t *respDataLen)
+{
+ struct eap_sim_msg *msg;
+
+ data->state = FAILURE;
+ data->num_id_req = 0;
+ data->num_notification = 0;
+
+ wpa_printf(MSG_DEBUG, "Generating EAP-AKA Synchronization-Failure "
+ "(id=%d)", req->identifier);
+ msg = eap_sim_msg_init(EAP_CODE_RESPONSE, req->identifier,
+ EAP_TYPE_AKA,
+ EAP_AKA_SUBTYPE_SYNCHRONIZATION_FAILURE);
+ wpa_printf(MSG_DEBUG, " AT_AUTS");
+ eap_sim_msg_add_full(msg, EAP_SIM_AT_AUTS, data->auts, AKA_AUTS_LEN);
+ return eap_sim_msg_finish(msg, respDataLen, NULL, NULL, 0);
+}
+
+
+static u8 * eap_aka_response_identity(struct eap_sm *sm,
+ struct eap_aka_data *data,
+ struct eap_hdr *req,
+ size_t *respDataLen,
+ enum eap_sim_id_req id_req)
+{
+ struct wpa_ssid *config = eap_get_config(sm);
+ u8 *identity = NULL;
+ size_t identity_len = 0;
+ struct eap_sim_msg *msg;
+
+ data->reauth = 0;
+ if (id_req == ANY_ID && data->reauth_id) {
+ identity = data->reauth_id;
+ identity_len = data->reauth_id_len;
+ data->reauth = 1;
+ } else if ((id_req == ANY_ID || id_req == FULLAUTH_ID) &&
+ data->pseudonym) {
+ identity = data->pseudonym;
+ identity_len = data->pseudonym_len;
+ eap_aka_clear_identities(data, CLEAR_REAUTH_ID);
+ } else if (id_req != NO_ID_REQ && config && config->identity) {
+ identity = config->identity;
+ identity_len = config->identity_len;
+ eap_aka_clear_identities(data,
+ CLEAR_PSEUDONYM | CLEAR_REAUTH_ID);
+ }
+ if (id_req != NO_ID_REQ)
+ eap_aka_clear_identities(data, CLEAR_EAP_ID);
+
+ wpa_printf(MSG_DEBUG, "Generating EAP-AKA Identity (id=%d)",
+ req->identifier);
+ msg = eap_sim_msg_init(EAP_CODE_RESPONSE, req->identifier,
+ EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY);
+
+ if (identity) {
+ wpa_hexdump_ascii(MSG_DEBUG, " AT_IDENTITY",
+ identity, identity_len);
+ eap_sim_msg_add(msg, EAP_SIM_AT_IDENTITY, identity_len,
+ identity, identity_len);
+ }
+
+ return eap_sim_msg_finish(msg, respDataLen, NULL, NULL, 0);
+}
+
+
+static u8 * eap_aka_response_challenge(struct eap_sm *sm,
+ struct eap_aka_data *data,
+ struct eap_hdr *req,
+ size_t *respDataLen)
+{
+ struct eap_sim_msg *msg;
+
+ wpa_printf(MSG_DEBUG, "Generating EAP-AKA Challenge (id=%d)",
+ req->identifier);
+ msg = eap_sim_msg_init(EAP_CODE_RESPONSE, req->identifier,
+ EAP_TYPE_AKA, EAP_AKA_SUBTYPE_CHALLENGE);
+ wpa_printf(MSG_DEBUG, " AT_RES");
+ eap_sim_msg_add(msg, EAP_SIM_AT_RES, data->res_len,
+ data->res, data->res_len);
+ wpa_printf(MSG_DEBUG, " AT_MAC");
+ eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC);
+ return eap_sim_msg_finish(msg, respDataLen, data->k_aut, (u8 *) "", 0);
+}
+
+
+static u8 * eap_aka_response_reauth(struct eap_sm *sm,
+ struct eap_aka_data *data,
+ struct eap_hdr *req,
+ size_t *respDataLen, int counter_too_small)
+{
+ struct eap_sim_msg *msg;
+ unsigned int counter;
+
+ wpa_printf(MSG_DEBUG, "Generating EAP-AKA Reauthentication (id=%d)",
+ req->identifier);
+ msg = eap_sim_msg_init(EAP_CODE_RESPONSE, req->identifier,
+ EAP_TYPE_AKA,
+ EAP_AKA_SUBTYPE_REAUTHENTICATION);
+ wpa_printf(MSG_DEBUG, " AT_IV");
+ wpa_printf(MSG_DEBUG, " AT_ENCR_DATA");
+ eap_sim_msg_add_encr_start(msg, EAP_SIM_AT_IV, EAP_SIM_AT_ENCR_DATA);
+
+ if (counter_too_small) {
+ wpa_printf(MSG_DEBUG, " *AT_COUNTER_TOO_SMALL");
+ eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER_TOO_SMALL, 0, NULL, 0);
+ counter = data->counter_too_small;
+ } else
+ counter = data->counter;
+
+ wpa_printf(MSG_DEBUG, " *AT_COUNTER %d", counter);
+ eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER, counter, NULL, 0);
+
+ if (eap_sim_msg_add_encr_end(msg, data->k_encr, EAP_SIM_AT_PADDING)) {
+ wpa_printf(MSG_WARNING, "EAP-AKA: Failed to encrypt "
+ "AT_ENCR_DATA");
+ eap_sim_msg_free(msg);
+ return NULL;
+ }
+ wpa_printf(MSG_DEBUG, " AT_MAC");
+ eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC);
+ return eap_sim_msg_finish(msg, respDataLen, data->k_aut, data->nonce_s,
+ EAP_SIM_NONCE_S_LEN);
+}
+
+
+static u8 * eap_aka_response_notification(struct eap_sm *sm,
+ struct eap_aka_data *data,
+ struct eap_hdr *req,
+ size_t *respDataLen,
+ u16 notification)
+{
+ struct eap_sim_msg *msg;
+ u8 *k_aut = (notification & 0x4000) == 0 ? data->k_aut : NULL;
+
+ wpa_printf(MSG_DEBUG, "Generating EAP-AKA Notification (id=%d)",
+ req->identifier);
+ msg = eap_sim_msg_init(EAP_CODE_RESPONSE, req->identifier,
+ EAP_TYPE_AKA, EAP_AKA_SUBTYPE_NOTIFICATION);
+ wpa_printf(MSG_DEBUG, " AT_NOTIFICATION");
+ eap_sim_msg_add(msg, EAP_SIM_AT_NOTIFICATION, notification, NULL, 0);
+ if (k_aut && data->reauth) {
+ wpa_printf(MSG_DEBUG, " AT_IV");
+ wpa_printf(MSG_DEBUG, " AT_ENCR_DATA");
+ eap_sim_msg_add_encr_start(msg, EAP_SIM_AT_IV,
+ EAP_SIM_AT_ENCR_DATA);
+ wpa_printf(MSG_DEBUG, " *AT_COUNTER %d", data->counter);
+ eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER, data->counter,
+ NULL, 0);
+ if (eap_sim_msg_add_encr_end(msg, data->k_encr,
+ EAP_SIM_AT_PADDING)) {
+ wpa_printf(MSG_WARNING, "EAP-AKA: Failed to encrypt "
+ "AT_ENCR_DATA");
+ eap_sim_msg_free(msg);
+ return NULL;
+ }
+ }
+ if (k_aut) {
+ wpa_printf(MSG_DEBUG, " AT_MAC");
+ eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC);
+ }
+ return eap_sim_msg_finish(msg, respDataLen, k_aut, (u8 *) "", 0);
+}
+
+
+static u8 * eap_aka_process_identity(struct eap_sm *sm,
+ struct eap_aka_data *data,
+ struct eap_hdr *req, size_t reqDataLen,
+ size_t *respDataLen,
+ struct eap_sim_attrs *attr)
+{
+ int id_error;
+
+ wpa_printf(MSG_DEBUG, "EAP-AKA: subtype Identity");
+
+ id_error = 0;
+ switch (attr->id_req) {
+ case NO_ID_REQ:
+ break;
+ case ANY_ID:
+ if (data->num_id_req > 0)
+ id_error++;
+ data->num_id_req++;
+ break;
+ case FULLAUTH_ID:
+ if (data->num_id_req > 1)
+ id_error++;
+ data->num_id_req++;
+ break;
+ case PERMANENT_ID:
+ if (data->num_id_req > 2)
+ id_error++;
+ data->num_id_req++;
+ break;
+ }
+ if (id_error) {
+ wpa_printf(MSG_INFO, "EAP-AKA: Too many ID requests "
+ "used within one authentication");
+ return eap_aka_client_error(sm, data, req, respDataLen,
+ EAP_AKA_UNABLE_TO_PROCESS_PACKET);
+ }
+
+ return eap_aka_response_identity(sm, data, req, respDataLen,
+ attr->id_req);
+}
+
+
+static u8 * eap_aka_process_challenge(struct eap_sm *sm,
+ struct eap_aka_data *data,
+ struct eap_hdr *req, size_t reqDataLen,
+ size_t *respDataLen,
+ struct eap_sim_attrs *attr)
+{
+ struct wpa_ssid *config = eap_get_config(sm);
+ u8 *identity;
+ size_t identity_len;
+ int res;
+ struct eap_sim_attrs eattr;
+
+ wpa_printf(MSG_DEBUG, "EAP-AKA: subtype Challenge");
+ data->reauth = 0;
+ if (!attr->mac || !attr->rand || !attr->autn) {
+ wpa_printf(MSG_WARNING, "EAP-AKA: Challenge message "
+ "did not include%s%s%s",
+ !attr->mac ? " AT_MAC" : "",
+ !attr->rand ? " AT_RAND" : "",
+ !attr->autn ? " AT_AUTN" : "");
+ return eap_aka_client_error(sm, data, req, respDataLen,
+ EAP_AKA_UNABLE_TO_PROCESS_PACKET);
+ }
+ memcpy(data->rand, attr->rand, AKA_RAND_LEN);
+ memcpy(data->autn, attr->autn, AKA_AUTN_LEN);
+
+ res = eap_aka_umts_auth(sm, data);
+ if (res == -1) {
+ wpa_printf(MSG_WARNING, "EAP-AKA: UMTS authentication "
+ "failed (AUTN)");
+ return eap_aka_authentication_reject(sm, data, req,
+ respDataLen);
+ } else if (res == -2) {
+ wpa_printf(MSG_WARNING, "EAP-AKA: UMTS authentication "
+ "failed (AUTN seq# -> AUTS)");
+ return eap_aka_synchronization_failure(sm, data, req,
+ respDataLen);
+ } else if (res) {
+ wpa_printf(MSG_WARNING, "EAP-AKA: UMTS authentication failed");
+ return eap_aka_client_error(sm, data, req, respDataLen,
+ EAP_AKA_UNABLE_TO_PROCESS_PACKET);
+ }
+ if (data->last_eap_identity) {
+ identity = data->last_eap_identity;
+ identity_len = data->last_eap_identity_len;
+ } else if (data->pseudonym) {
+ identity = data->pseudonym;
+ identity_len = data->pseudonym_len;
+ } else {
+ identity = config->identity;
+ identity_len = config->identity_len;
+ }
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP-AKA: Selected identity for MK "
+ "derivation", identity, identity_len);
+ eap_aka_derive_mk(data, identity, identity_len);
+ eap_sim_derive_keys(data->mk, data->k_encr, data->k_aut, data->msk);
+ if (eap_sim_verify_mac(data->k_aut, (u8 *) req, reqDataLen, attr->mac,
+ (u8 *) "", 0)) {
+ wpa_printf(MSG_WARNING, "EAP-AKA: Challenge message "
+ "used invalid AT_MAC");
+ return eap_aka_client_error(sm, data, req, respDataLen,
+ EAP_AKA_UNABLE_TO_PROCESS_PACKET);
+ }
+
+ /* Old reauthentication and pseudonym identities must not be used
+ * anymore. In other words, if no new identities are received, full
+ * authentication will be used on next reauthentication. */
+ eap_aka_clear_identities(data, CLEAR_PSEUDONYM | CLEAR_REAUTH_ID |
+ CLEAR_EAP_ID);
+
+ if (attr->encr_data) {
+ if (eap_sim_parse_encr(data->k_encr, attr->encr_data,
+ attr->encr_data_len, attr->iv, &eattr,
+ 0)) {
+ return eap_aka_client_error(
+ sm, data, req, respDataLen,
+ EAP_AKA_UNABLE_TO_PROCESS_PACKET);
+ }
+ eap_aka_learn_ids(data, &eattr);
+ }
+
+ if (data->state != FAILURE)
+ data->state = SUCCESS;
+
+ data->num_id_req = 0;
+ data->num_notification = 0;
+ /* draft-arkko-pppext-eap-aka-12.txt specifies that counter
+ * is initialized to one after fullauth, but initializing it to
+ * zero makes it easier to implement reauth verification. */
+ data->counter = 0;
+ return eap_aka_response_challenge(sm, data, req, respDataLen);
+}
+
+
+static int eap_aka_process_notification_reauth(struct eap_aka_data *data,
+ struct eap_hdr *req,
+ size_t reqDataLen,
+ struct eap_sim_attrs *attr)
+{
+ struct eap_sim_attrs eattr;
+
+ if (attr->encr_data == NULL || attr->iv == NULL) {
+ wpa_printf(MSG_WARNING, "EAP-AKA: Notification message after "
+ "reauth did not include encrypted data");
+ return -1;
+ }
+
+ if (eap_sim_parse_encr(data->k_encr, attr->encr_data,
+ attr->encr_data_len, attr->iv, &eattr, 0)) {
+ wpa_printf(MSG_WARNING, "EAP-AKA: Failed to parse encrypted "
+ "data from notification message");
+ return -1;
+ }
+
+ if (eattr.counter != data->counter) {
+ wpa_printf(MSG_WARNING, "EAP-AKA: Counter in notification "
+ "message does not match with counter in reauth "
+ "message");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int eap_aka_process_notification_auth(struct eap_aka_data *data,
+ struct eap_hdr *req,
+ size_t reqDataLen,
+ struct eap_sim_attrs *attr)
+{
+ if (attr->mac == NULL) {
+ wpa_printf(MSG_INFO, "EAP-AKA: no AT_MAC in after_auth "
+ "Notification message");
+ return -1;
+ }
+
+ if (eap_sim_verify_mac(data->k_aut, (u8 *) req, reqDataLen, attr->mac,
+ (u8 *) "", 0)) {
+ wpa_printf(MSG_WARNING, "EAP-AKA: Notification message "
+ "used invalid AT_MAC");
+ return -1;
+ }
+
+ if (data->reauth &&
+ eap_aka_process_notification_reauth(data, req, reqDataLen, attr)) {
+ wpa_printf(MSG_WARNING, "EAP-AKA: Invalid notification "
+ "message after reauth");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static u8 * eap_aka_process_notification(struct eap_sm *sm,
+ struct eap_aka_data *data,
+ struct eap_hdr *req,
+ size_t reqDataLen,
+ size_t *respDataLen,
+ struct eap_sim_attrs *attr)
+{
+ wpa_printf(MSG_DEBUG, "EAP-AKA: subtype Notification");
+ if (data->num_notification > 0) {
+ wpa_printf(MSG_INFO, "EAP-AKA: too many notification "
+ "rounds (only one allowed)");
+ return eap_aka_client_error(sm, data, req, respDataLen,
+ EAP_AKA_UNABLE_TO_PROCESS_PACKET);
+ }
+ data->num_notification++;
+ if (attr->notification == -1) {
+ wpa_printf(MSG_INFO, "EAP-AKA: no AT_NOTIFICATION in "
+ "Notification message");
+ return eap_aka_client_error(sm, data, req, respDataLen,
+ EAP_AKA_UNABLE_TO_PROCESS_PACKET);
+ }
+
+ if ((attr->notification & 0x4000) == 0 &&
+ eap_aka_process_notification_auth(data, req, reqDataLen, attr)) {
+ return eap_aka_client_error(sm, data, req, respDataLen,
+ EAP_AKA_UNABLE_TO_PROCESS_PACKET);
+ }
+
+ eap_sim_report_notification(sm->msg_ctx, attr->notification, 1);
+ if (attr->notification >= 0 && attr->notification < 32768) {
+ data->state = FAILURE;
+ }
+ return eap_aka_response_notification(sm, data, req, respDataLen,
+ attr->notification);
+}
+
+
+static u8 * eap_aka_process_reauthentication(struct eap_sm *sm,
+ struct eap_aka_data *data,
+ struct eap_hdr *req,
+ size_t reqDataLen,
+ size_t *respDataLen,
+ struct eap_sim_attrs *attr)
+{
+ struct eap_sim_attrs eattr;
+
+ wpa_printf(MSG_DEBUG, "EAP-AKA: subtype Reauthentication");
+
+ if (data->reauth_id == NULL) {
+ wpa_printf(MSG_WARNING, "EAP-AKA: Server is trying "
+ "reauthentication, but no reauth_id available");
+ return eap_aka_client_error(sm, data, req, respDataLen,
+ EAP_AKA_UNABLE_TO_PROCESS_PACKET);
+ }
+
+ data->reauth = 1;
+ if (eap_sim_verify_mac(data->k_aut, (u8 *) req, reqDataLen,
+ attr->mac, (u8 *) "", 0)) {
+ wpa_printf(MSG_WARNING, "EAP-AKA: Reauthentication "
+ "did not have valid AT_MAC");
+ return eap_aka_client_error(sm, data, req, respDataLen,
+ EAP_AKA_UNABLE_TO_PROCESS_PACKET);
+ }
+
+ if (attr->encr_data == NULL || attr->iv == NULL) {
+ wpa_printf(MSG_WARNING, "EAP-AKA: Reauthentication "
+ "message did not include encrypted data");
+ return eap_aka_client_error(sm, data, req, respDataLen,
+ EAP_AKA_UNABLE_TO_PROCESS_PACKET);
+ }
+
+ if (eap_sim_parse_encr(data->k_encr, attr->encr_data,
+ attr->encr_data_len, attr->iv, &eattr, 0)) {
+ wpa_printf(MSG_WARNING, "EAP-AKA: Failed to parse encrypted "
+ "data from reauthentication message");
+ return eap_aka_client_error(sm, data, req, respDataLen,
+ EAP_AKA_UNABLE_TO_PROCESS_PACKET);
+ }
+
+ if (eattr.nonce_s == NULL || eattr.counter < 0) {
+ wpa_printf(MSG_INFO, "EAP-AKA: (encr) No%s%s in reauth packet",
+ !eattr.nonce_s ? " AT_NONCE_S" : "",
+ eattr.counter < 0 ? " AT_COUNTER" : "");
+ return eap_aka_client_error(sm, data, req, respDataLen,
+ EAP_AKA_UNABLE_TO_PROCESS_PACKET);
+ }
+
+ if (eattr.counter <= data->counter) {
+ wpa_printf(MSG_INFO, "EAP-AKA: (encr) Invalid counter "
+ "(%d <= %d)", eattr.counter, data->counter);
+ data->counter_too_small = eattr.counter;
+ /* Reply using Re-auth w/ AT_COUNTER_TOO_SMALL. The current
+ * reauth_id must not be used to start a new reauthentication.
+ * However, since it was used in the last EAP-Response-Identity
+ * packet, it has to saved for the following fullauth to be
+ * used in MK derivation. */
+ free(data->last_eap_identity);
+ data->last_eap_identity = data->reauth_id;
+ data->last_eap_identity_len = data->reauth_id_len;
+ data->reauth_id = NULL;
+ data->reauth_id_len = 0;
+ return eap_aka_response_reauth(sm, data, req, respDataLen, 1);
+ }
+ data->counter = eattr.counter;
+
+ memcpy(data->nonce_s, eattr.nonce_s, EAP_SIM_NONCE_S_LEN);
+ wpa_hexdump(MSG_DEBUG, "EAP-AKA: (encr) AT_NONCE_S",
+ data->nonce_s, EAP_SIM_NONCE_S_LEN);
+
+ eap_sim_derive_keys_reauth(data->counter,
+ data->reauth_id, data->reauth_id_len,
+ data->nonce_s, data->mk, data->msk);
+ eap_aka_clear_identities(data, CLEAR_REAUTH_ID | CLEAR_EAP_ID);
+ eap_aka_learn_ids(data, &eattr);
+
+ if (data->state != FAILURE)
+ data->state = SUCCESS;
+
+ data->num_id_req = 0;
+ data->num_notification = 0;
+ 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);
+ }
+ return eap_aka_response_reauth(sm, data, req, respDataLen, 0);
+}
+
+
+static u8 * eap_aka_process(struct eap_sm *sm, void *priv,
+ struct eap_method_ret *ret,
+ u8 *reqData, size_t reqDataLen,
+ size_t *respDataLen)
+{
+ struct eap_aka_data *data = priv;
+ struct wpa_ssid *config = eap_get_config(sm);
+ struct eap_hdr *req;
+ u8 *pos, subtype, *res;
+ struct eap_sim_attrs attr;
+ size_t len;
+
+ wpa_hexdump(MSG_DEBUG, "EAP-AKA: EAP data", reqData, reqDataLen);
+ if (config == NULL || config->identity == NULL) {
+ wpa_printf(MSG_INFO, "EAP-AKA: Identity not configured");
+ eap_sm_request_identity(sm, config);
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ req = (struct eap_hdr *) reqData;
+ pos = (u8 *) (req + 1);
+ if (reqDataLen < sizeof(*req) + 4 || *pos != EAP_TYPE_AKA ||
+ (len = be_to_host16(req->length)) > reqDataLen) {
+ wpa_printf(MSG_INFO, "EAP-AKA: Invalid frame");
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ ret->ignore = FALSE;
+ ret->methodState = METHOD_CONT;
+ ret->decision = DECISION_FAIL;
+ ret->allowNotifications = TRUE;
+
+ pos++;
+ subtype = *pos++;
+ wpa_printf(MSG_DEBUG, "EAP-AKA: Subtype=%d", subtype);
+ pos += 2; /* Reserved */
+
+ if (eap_sim_parse_attr(pos, reqData + len, &attr, 1, 0)) {
+ res = eap_aka_client_error(sm, data, req, respDataLen,
+ EAP_AKA_UNABLE_TO_PROCESS_PACKET);
+ goto done;
+ }
+
+ switch (subtype) {
+ case EAP_AKA_SUBTYPE_IDENTITY:
+ res = eap_aka_process_identity(sm, data, req, len,
+ respDataLen, &attr);
+ break;
+ case EAP_AKA_SUBTYPE_CHALLENGE:
+ res = eap_aka_process_challenge(sm, data, req, len,
+ respDataLen, &attr);
+ break;
+ case EAP_AKA_SUBTYPE_NOTIFICATION:
+ res = eap_aka_process_notification(sm, data, req, len,
+ respDataLen, &attr);
+ break;
+ case EAP_AKA_SUBTYPE_REAUTHENTICATION:
+ res = eap_aka_process_reauthentication(sm, data, req, len,
+ respDataLen, &attr);
+ break;
+ case EAP_AKA_SUBTYPE_CLIENT_ERROR:
+ wpa_printf(MSG_DEBUG, "EAP-AKA: subtype Client-Error");
+ res = eap_aka_client_error(sm, data, req, respDataLen,
+ EAP_AKA_UNABLE_TO_PROCESS_PACKET);
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "EAP-AKA: Unknown subtype=%d", subtype);
+ res = eap_aka_client_error(sm, data, req, respDataLen,
+ EAP_AKA_UNABLE_TO_PROCESS_PACKET);
+ break;
+ }
+
+done:
+ if (data->state == FAILURE) {
+ ret->decision = DECISION_FAIL;
+ ret->methodState = METHOD_DONE;
+ } else if (data->state == SUCCESS) {
+ ret->decision = DECISION_UNCOND_SUCC;
+ ret->methodState = METHOD_DONE;
+ }
+
+ if (ret->methodState == METHOD_DONE) {
+ ret->allowNotifications = FALSE;
+ }
+
+ return res;
+}
+
+
+static Boolean eap_aka_has_reauth_data(struct eap_sm *sm, void *priv)
+{
+ struct eap_aka_data *data = priv;
+ return data->pseudonym || data->reauth_id;
+}
+
+
+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);
+}
+
+
+static void * eap_aka_init_for_reauth(struct eap_sm *sm, void *priv)
+{
+ struct eap_aka_data *data = priv;
+ data->num_id_req = 0;
+ data->num_notification = 0;
+ data->state = CONTINUE;
+ return priv;
+}
+
+
+static const u8 * eap_aka_get_identity(struct eap_sm *sm, void *priv,
+ size_t *len)
+{
+ struct eap_aka_data *data = priv;
+
+ if (data->reauth_id) {
+ *len = data->reauth_id_len;
+ return data->reauth_id;
+ }
+
+ if (data->pseudonym) {
+ *len = data->pseudonym_len;
+ return data->pseudonym;
+ }
+
+ return NULL;
+}
+
+
+static Boolean eap_aka_isKeyAvailable(struct eap_sm *sm, void *priv)
+{
+ struct eap_aka_data *data = priv;
+ return data->state == SUCCESS;
+}
+
+
+static u8 * eap_aka_getKey(struct eap_sm *sm, void *priv, size_t *len)
+{
+ struct eap_aka_data *data = priv;
+ u8 *key;
+
+ if (data->state != SUCCESS)
+ return NULL;
+
+ key = malloc(EAP_SIM_KEYING_DATA_LEN);
+ if (key == NULL)
+ return NULL;
+
+ *len = EAP_SIM_KEYING_DATA_LEN;
+ memcpy(key, data->msk, EAP_SIM_KEYING_DATA_LEN);
+
+ return key;
+}
+
+
+const struct eap_method eap_method_aka =
+{
+ .method = EAP_TYPE_AKA,
+ .name = "AKA",
+ .init = eap_aka_init,
+ .deinit = eap_aka_deinit,
+ .process = eap_aka_process,
+ .isKeyAvailable = eap_aka_isKeyAvailable,
+ .getKey = eap_aka_getKey,
+ .has_reauth_data = eap_aka_has_reauth_data,
+ .deinit_for_reauth = eap_aka_deinit_for_reauth,
+ .init_for_reauth = eap_aka_init_for_reauth,
+ .get_identity = eap_aka_get_identity,
+};
diff --git a/contrib/wpa_supplicant/eap_defs.h b/contrib/wpa_supplicant/eap_defs.h
new file mode 100644
index 000000000000..effe665f0919
--- /dev/null
+++ b/contrib/wpa_supplicant/eap_defs.h
@@ -0,0 +1,41 @@
+#ifndef EAP_DEFS_H
+#define EAP_DEFS_H
+
+/* RFC 3748 - Extensible Authentication Protocol (EAP) */
+
+struct eap_hdr {
+ u8 code;
+ u8 identifier;
+ u16 length; /* including code and identifier */
+ /* followed by length-4 octets of data */
+} __attribute__ ((packed));
+
+enum { EAP_CODE_REQUEST = 1, EAP_CODE_RESPONSE = 2, EAP_CODE_SUCCESS = 3,
+ EAP_CODE_FAILURE = 4 };
+
+/* EAP Request and Response data begins with one octet Type. Success and
+ * Failure do not have additional data. */
+
+typedef enum {
+ EAP_TYPE_NONE = 0,
+ EAP_TYPE_IDENTITY = 1,
+ EAP_TYPE_NOTIFICATION = 2,
+ EAP_TYPE_NAK = 3 /* Response only */,
+ EAP_TYPE_MD5 = 4,
+ EAP_TYPE_OTP = 5 /* RFC 2284 */,
+ EAP_TYPE_GTC = 6, /* RFC 2284 */
+ EAP_TYPE_TLS = 13 /* RFC 2716 */,
+ EAP_TYPE_LEAP = 17 /* Cisco proprietary */,
+ EAP_TYPE_SIM = 18 /* draft-haverinen-pppext-eap-sim-12.txt */,
+ EAP_TYPE_TTLS = 21 /* draft-ietf-pppext-eap-ttls-02.txt */,
+ EAP_TYPE_AKA = 23 /* draft-arkko-pppext-eap-aka-12.txt */,
+ EAP_TYPE_PEAP = 25 /* draft-josefsson-pppext-eap-tls-eap-06.txt */,
+ EAP_TYPE_MSCHAPV2 = 26 /* draft-kamath-pppext-eap-mschapv2-00.txt */,
+ EAP_TYPE_TLV = 33 /* draft-josefsson-pppext-eap-tls-eap-07.txt */,
+ EAP_TYPE_FAST = 43 /* draft-cam-winget-eap-fast-00.txt */,
+ EAP_TYPE_EXPANDED_NAK = 254 /* RFC 3748 */,
+ EAP_TYPE_PSK = 255 /* EXPERIMENTAL - type not yet allocated
+ * draft-bersani-eap-psk-03 */
+} EapType;
+
+#endif /* EAP_DEFS_H */
diff --git a/contrib/wpa_supplicant/eap_fast.c b/contrib/wpa_supplicant/eap_fast.c
new file mode 100644
index 000000000000..1c9c3ff2370f
--- /dev/null
+++ b/contrib/wpa_supplicant/eap_fast.c
@@ -0,0 +1,1906 @@
+/*
+ * WPA Supplicant / EAP-FAST (draft-cam-winget-eap-fast-00.txt)
+ * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published 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 <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "common.h"
+#include "eap_i.h"
+#include "eap_tls_common.h"
+#include "wpa_supplicant.h"
+#include "config_ssid.h"
+#include "tls.h"
+#include "eap_tlv.h"
+#include "sha1.h"
+
+/* TODO:
+ * - encrypt PAC-Key in the PAC file
+ * - test session resumption and enable it if it interoperates
+ */
+
+#define EAP_FAST_VERSION 1
+#define EAP_FAST_KEY_LEN 64
+#define EAP_FAST_PAC_KEY_LEN 32
+
+#define TLS_EXT_PAC_OPAQUE 35
+
+static char *pac_file_hdr = "wpa_supplicant EAP-FAST PAC file - version 1";
+
+
+static void eap_fast_deinit(struct eap_sm *sm, void *priv);
+
+
+#define PAC_TYPE_PAC_KEY 1
+#define PAC_TYPE_PAC_OPAQUE 2
+#define PAC_TYPE_CRED_LIFETIME 3
+#define PAC_TYPE_A_ID 4
+#define PAC_TYPE_I_ID 5
+#define PAC_TYPE_SERVER_PROTECTED_DATA 6
+#define PAC_TYPE_A_ID_INFO 7
+#define PAC_TYPE_PAC_ACKNOWLEDGEMENT 8
+#define PAC_TYPE_PAC_INFO 9
+
+struct pac_tlv_hdr {
+ u16 type;
+ u16 len;
+};
+
+
+/* draft-cam-winget-eap-fast-00.txt, 6.2 EAP-FAST Authentication Phase 1 */
+struct eap_fast_key_block_auth {
+ /* RC4-128-SHA */
+ u8 client_write_MAC_secret[20];
+ u8 server_write_MAC_secret[20];
+ u8 client_write_key[16];
+ u8 server_write_key[16];
+ /* u8 client_write_IV[0]; */
+ /* u8 server_write_IV[0]; */
+ u8 session_key_seed[40];
+};
+
+
+/* draft-cam-winget-eap-fast-00.txt, 7.3 EAP-FAST Provisioning Exchange */
+struct eap_fast_key_block_provisioning {
+ /* AES128-SHA */
+ u8 client_write_MAC_secret[20];
+ u8 server_write_MAC_secret[20];
+ u8 client_write_key[16];
+ u8 server_write_key[16];
+ u8 client_write_IV[16];
+ u8 server_write_IV[16];
+ u8 session_key_seed[40];
+ u8 server_challenge[16];
+ u8 client_challenge[16];
+};
+
+
+struct eap_fast_pac {
+ struct eap_fast_pac *next;
+
+ u8 pac_key[EAP_FAST_PAC_KEY_LEN];
+ u8 *pac_opaque;
+ size_t pac_opaque_len;
+ u8 *pac_info;
+ size_t pac_info_len;
+ u8 *a_id;
+ size_t a_id_len;
+ u8 *i_id;
+ size_t i_id_len;
+ u8 *a_id_info;
+ size_t a_id_info_len;
+};
+
+
+struct eap_fast_data {
+ struct eap_ssl_data ssl;
+
+ int fast_version;
+
+ const struct eap_method *phase2_method;
+ void *phase2_priv;
+ int phase2_success;
+
+ u8 phase2_type;
+ u8 *phase2_types;
+ size_t num_phase2_types;
+ int resuming; /* starting a resumed session */
+ struct eap_fast_key_block_auth *key_block_a;
+ struct eap_fast_key_block_provisioning *key_block_p;
+ int provisioning_allowed; /* is PAC provisioning allowed */
+ int provisioning; /* doing PAC provisioning (not the normal auth) */
+
+ u8 key_data[EAP_FAST_KEY_LEN];
+ int success;
+
+ struct eap_fast_pac *pac;
+ struct eap_fast_pac *current_pac;
+
+ int tls_master_secret_set;
+};
+
+
+static void eap_fast_free_pac(struct eap_fast_pac *pac)
+{
+ free(pac->pac_opaque);
+ free(pac->pac_info);
+ free(pac->a_id);
+ free(pac->i_id);
+ free(pac->a_id_info);
+ free(pac);
+}
+
+
+static struct eap_fast_pac * eap_fast_get_pac(struct eap_fast_data *data,
+ const u8 *a_id, size_t a_id_len)
+{
+ struct eap_fast_pac *pac = data->pac;
+
+ while (pac) {
+ if (pac->a_id_len == a_id_len &&
+ memcmp(pac->a_id, a_id, a_id_len) == 0) {
+ return pac;
+ }
+ pac = pac->next;
+ }
+ return NULL;
+}
+
+
+static int eap_fast_add_pac(struct eap_fast_data *data,
+ struct eap_fast_pac *entry)
+{
+ struct eap_fast_pac *pac, *prev;
+
+ if (entry == NULL || entry->a_id == NULL)
+ return -1;
+
+ /* Remove a possible old entry for the matching A-ID. */
+ pac = data->pac;
+ prev = NULL;
+ while (pac) {
+ if (pac->a_id_len == entry->a_id_len &&
+ memcmp(pac->a_id, entry->a_id, pac->a_id_len) == 0) {
+ if (prev == NULL) {
+ data->pac = pac->next;
+ } else {
+ prev->next = pac->next;
+ }
+ if (data->current_pac == pac)
+ data->current_pac = NULL;
+ eap_fast_free_pac(pac);
+ break;
+ }
+ prev = pac;
+ pac = pac->next;
+ }
+
+ /* Allocate a new entry and add it to the list of PACs. */
+ pac = malloc(sizeof(*pac));
+ if (pac == NULL)
+ return -1;
+
+ memset(pac, 0, sizeof(*pac));
+ memcpy(pac->pac_key, entry->pac_key, EAP_FAST_PAC_KEY_LEN);
+ if (entry->pac_opaque) {
+ pac->pac_opaque = malloc(entry->pac_opaque_len);
+ if (pac->pac_opaque == NULL) {
+ eap_fast_free_pac(pac);
+ return -1;
+ }
+ memcpy(pac->pac_opaque, entry->pac_opaque,
+ entry->pac_opaque_len);
+ pac->pac_opaque_len = entry->pac_opaque_len;
+ }
+ if (entry->pac_info) {
+ pac->pac_info = malloc(entry->pac_info_len);
+ if (pac->pac_info == NULL) {
+ eap_fast_free_pac(pac);
+ return -1;
+ }
+ memcpy(pac->pac_info, entry->pac_info,
+ entry->pac_info_len);
+ pac->pac_info_len = entry->pac_info_len;
+ }
+ if (entry->a_id) {
+ pac->a_id = malloc(entry->a_id_len);
+ if (pac->a_id == NULL) {
+ eap_fast_free_pac(pac);
+ return -1;
+ }
+ memcpy(pac->a_id, entry->a_id,
+ entry->a_id_len);
+ pac->a_id_len = entry->a_id_len;
+ }
+ if (entry->i_id) {
+ pac->i_id = malloc(entry->i_id_len);
+ if (pac->i_id == NULL) {
+ eap_fast_free_pac(pac);
+ return -1;
+ }
+ memcpy(pac->i_id, entry->i_id,
+ entry->i_id_len);
+ pac->i_id_len = entry->i_id_len;
+ }
+ if (entry->a_id_info) {
+ pac->a_id_info = malloc(entry->a_id_info_len);
+ if (pac->a_id_info == NULL) {
+ eap_fast_free_pac(pac);
+ return -1;
+ }
+ memcpy(pac->a_id_info, entry->a_id_info,
+ entry->a_id_info_len);
+ pac->a_id_info_len = entry->a_id_info_len;
+ }
+ pac->next = data->pac;
+ data->pac = pac;
+ return 0;
+}
+
+
+static int eap_fast_read_line(FILE *f, char *buf, size_t buf_len)
+{
+ char *pos;
+
+ if (fgets(buf, buf_len, f) == NULL) {
+ return -1;
+ }
+
+ buf[buf_len - 1] = '\0';
+ pos = buf;
+ while (*pos != '\0') {
+ if (*pos == '\n' || *pos == '\r') {
+ *pos = '\0';
+ break;
+ }
+ pos++;
+ }
+
+ return 0;
+}
+
+
+static u8 * eap_fast_parse_hex(const char *value, size_t *len)
+{
+ int hlen;
+ u8 *buf;
+
+ if (value == NULL)
+ return NULL;
+ hlen = strlen(value);
+ if (hlen & 1)
+ return NULL;
+ *len = hlen / 2;
+ buf = malloc(*len);
+ if (buf == NULL)
+ return NULL;
+ if (hexstr2bin(value, buf, *len)) {
+ free(buf);
+ return NULL;
+ }
+ return buf;
+}
+
+
+static int eap_fast_load_pac(struct eap_fast_data *data, const char *pac_file)
+{
+ FILE *f;
+ struct eap_fast_pac *pac = NULL;
+ int count = 0;
+ char *buf, *pos;
+ const int buf_len = 2048;
+ int ret = 0, line = 0;
+
+ if (pac_file == NULL)
+ return -1;
+
+ f = fopen(pac_file, "r");
+ if (f == NULL) {
+ wpa_printf(MSG_INFO, "EAP-FAST: No PAC file '%s' - assume no "
+ "PAC entries have been provisioned", pac_file);
+ return 0;
+ }
+
+ buf = malloc(buf_len);
+ if (buf == NULL) {
+ return -1;
+ }
+
+ line++;
+ if (eap_fast_read_line(f, buf, buf_len) < 0 ||
+ strcmp(pac_file_hdr, buf) != 0) {
+ wpa_printf(MSG_INFO, "EAP-FAST: Unrecognized header line in "
+ "PAC file '%s'", pac_file);
+ free(buf);
+ fclose(f);
+ return -1;
+ }
+
+ while (eap_fast_read_line(f, buf, buf_len) == 0) {
+ line++;
+ pos = strchr(buf, '=');
+ if (pos) {
+ *pos++ = '\0';
+ }
+
+ if (strcmp(buf, "START") == 0) {
+ if (pac) {
+ wpa_printf(MSG_INFO, "EAP-FAST: START line "
+ "without END in '%s:%d'",
+ pac_file, line);
+ ret = -1;
+ break;
+ }
+ pac = malloc(sizeof(*pac));
+ if (pac == NULL) {
+ wpa_printf(MSG_INFO, "EAP-FAST: No memory for "
+ "PAC entry");
+ ret = -1;
+ break;
+ }
+ memset(pac, 0, sizeof(*pac));
+ } else if (strcmp(buf, "END") == 0) {
+ if (pac == NULL) {
+ wpa_printf(MSG_INFO, "EAP-FAST: END line "
+ "without START in '%s:%d'",
+ pac_file, line);
+ ret = -1;
+ break;
+ }
+ pac->next = data->pac;
+ data->pac = pac;
+ pac = NULL;
+ count++;
+ } else if (pac && strcmp(buf, "PAC-Key") == 0) {
+ u8 *key;
+ size_t key_len;
+ key = eap_fast_parse_hex(pos, &key_len);
+ if (key == NULL || key_len != EAP_FAST_PAC_KEY_LEN) {
+ wpa_printf(MSG_INFO, "EAP-FAST: Invalid "
+ "PAC-Key '%s:%d'", pac_file, line);
+ ret = -1;
+ free(key);
+ break;
+ }
+
+ memcpy(pac->pac_key, key, EAP_FAST_PAC_KEY_LEN);
+ free(key);
+ } else if (pac && strcmp(buf, "PAC-Opaque") == 0) {
+ pac->pac_opaque =
+ eap_fast_parse_hex(pos, &pac->pac_opaque_len);
+ if (pac->pac_opaque == NULL) {
+ wpa_printf(MSG_INFO, "EAP-FAST: Invalid "
+ "PAC-Opaque '%s:%d'",
+ pac_file, line);
+ ret = -1;
+ break;
+ }
+ } else if (pac && strcmp(buf, "A-ID") == 0) {
+ pac->a_id = eap_fast_parse_hex(pos, &pac->a_id_len);
+ if (pac->a_id == NULL) {
+ wpa_printf(MSG_INFO, "EAP-FAST: Invalid "
+ "A-ID '%s:%d'", pac_file, line);
+ ret = -1;
+ break;
+ }
+ } else if (pac && strcmp(buf, "I-ID") == 0) {
+ pac->i_id = eap_fast_parse_hex(pos, &pac->i_id_len);
+ if (pac->i_id == NULL) {
+ wpa_printf(MSG_INFO, "EAP-FAST: Invalid "
+ "I-ID '%s:%d'", pac_file, line);
+ ret = -1;
+ break;
+ }
+ } else if (pac && strcmp(buf, "A-ID-Info") == 0) {
+ pac->a_id_info =
+ eap_fast_parse_hex(pos, &pac->a_id_info_len);
+ if (pac->a_id_info == NULL) {
+ wpa_printf(MSG_INFO, "EAP-FAST: Invalid "
+ "A-ID-Info '%s:%d'",
+ pac_file, line);
+ ret = -1;
+ break;
+ }
+ }
+ }
+
+ if (pac) {
+ wpa_printf(MSG_INFO, "EAP-FAST: PAC block not terminated with "
+ "END in '%s'", pac_file);
+ eap_fast_free_pac(pac);
+ ret = -1;
+ }
+
+ free(buf);
+ fclose(f);
+
+ if (ret == 0) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: read %d PAC entries from "
+ "'%s'", count, pac_file);
+ }
+
+ return ret;
+}
+
+
+static void eap_fast_write(FILE *f, const char *field, const u8 *data,
+ size_t len, int txt)
+{
+ int i;
+
+ if (data == NULL)
+ return;
+
+ fprintf(f, "%s=", field);
+ for (i = 0; i < len; i++) {
+ fprintf(f, "%02x", data[i]);
+ }
+ fprintf(f, "\n");
+
+ if (txt) {
+ fprintf(f, "%s-txt=", field);
+ for (i = 0; i < len; i++) {
+ fprintf(f, "%c", data[i]);
+ }
+ fprintf(f, "\n");
+ }
+}
+
+
+static int eap_fast_save_pac(struct eap_fast_data *data, const char *pac_file)
+{
+ FILE *f;
+ struct eap_fast_pac *pac;
+ int count = 0;
+
+ if (pac_file == NULL)
+ return -1;
+
+ f = fopen(pac_file, "w");
+ if (f == NULL) {
+ wpa_printf(MSG_INFO, "EAP-FAST: Failed to open PAC file '%s' "
+ "for writing", pac_file);
+ return -1;
+ }
+
+ fprintf(f, "%s\n", pac_file_hdr);
+
+ pac = data->pac;
+ while (pac) {
+ fprintf(f, "START\n");
+ eap_fast_write(f, "PAC-Key", pac->pac_key,
+ EAP_FAST_PAC_KEY_LEN, 0);
+ eap_fast_write(f, "PAC-Opaque", pac->pac_opaque,
+ pac->pac_opaque_len, 0);
+ eap_fast_write(f, "PAC-Info", pac->pac_info,
+ pac->pac_info_len, 0);
+ eap_fast_write(f, "A-ID", pac->a_id, pac->a_id_len, 0);
+ eap_fast_write(f, "I-ID", pac->i_id, pac->i_id_len, 1);
+ eap_fast_write(f, "A-ID-Info", pac->a_id_info,
+ pac->a_id_info_len, 1);
+ fprintf(f, "END\n");
+ count++;
+ pac = pac->next;
+ }
+
+ fclose(f);
+
+ wpa_printf(MSG_DEBUG, "EAP-FAST: wrote %d PAC entries into '%s'",
+ count, pac_file);
+
+ return 0;
+}
+
+
+static void * eap_fast_init(struct eap_sm *sm)
+{
+ struct eap_fast_data *data;
+ struct wpa_ssid *config = eap_get_config(sm);
+
+ data = malloc(sizeof(*data));
+ if (data == NULL)
+ return NULL;
+ memset(data, 0, sizeof(*data));
+ data->fast_version = EAP_FAST_VERSION;
+
+ if (config && config->phase1) {
+ if (strstr(config->phase1, "fast_provisioning=1")) {
+ data->provisioning_allowed = 1;
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Automatic PAC "
+ "provisioning is allowed");
+ }
+ }
+
+ if (config && config->phase2) {
+ char *start, *pos, *buf;
+ u8 method, *methods = NULL, *_methods;
+ size_t num_methods = 0;
+ start = buf = strdup(config->phase2);
+ if (buf == NULL) {
+ eap_fast_deinit(sm, data);
+ return NULL;
+ }
+ while (start && *start != '\0') {
+ pos = strstr(start, "auth=");
+ if (pos == NULL)
+ break;
+ if (start != pos && *(pos - 1) != ' ') {
+ start = pos + 5;
+ continue;
+ }
+
+ start = pos + 5;
+ pos = strchr(start, ' ');
+ if (pos)
+ *pos++ = '\0';
+ method = eap_get_phase2_type(start);
+ if (method == EAP_TYPE_NONE) {
+ wpa_printf(MSG_ERROR, "EAP-FAST: Unsupported "
+ "Phase2 method '%s'", start);
+ } else {
+ num_methods++;
+ _methods = realloc(methods, num_methods);
+ if (_methods == NULL) {
+ free(methods);
+ eap_fast_deinit(sm, data);
+ return NULL;
+ }
+ methods = _methods;
+ methods[num_methods - 1] = method;
+ }
+
+ start = pos;
+ }
+ free(buf);
+ data->phase2_types = methods;
+ data->num_phase2_types = num_methods;
+ }
+ if (data->phase2_types == NULL) {
+ data->phase2_types =
+ eap_get_phase2_types(config, &data->num_phase2_types);
+ }
+ if (data->phase2_types == NULL) {
+ wpa_printf(MSG_ERROR, "EAP-FAST: No Phase2 method available");
+ eap_fast_deinit(sm, data);
+ return NULL;
+ }
+ wpa_hexdump(MSG_DEBUG, "EAP-FAST: Phase2 EAP types",
+ data->phase2_types, data->num_phase2_types);
+ data->phase2_type = EAP_TYPE_NONE;
+
+ if (eap_tls_ssl_init(sm, &data->ssl, config)) {
+ wpa_printf(MSG_INFO, "EAP-FAST: Failed to initialize SSL.");
+ eap_fast_deinit(sm, data);
+ return NULL;
+ }
+
+ /* The local RADIUS server in a Cisco AP does not seem to like empty
+ * fragments before data, so disable that workaround for CBC.
+ * TODO: consider making this configurable */
+ tls_connection_enable_workaround(sm->ssl_ctx, data->ssl.conn);
+
+ if (eap_fast_load_pac(data, config->pac_file) < 0) {
+ eap_fast_deinit(sm, data);
+ return NULL;
+ }
+
+ if (data->pac == NULL && !data->provisioning_allowed) {
+ wpa_printf(MSG_INFO, "EAP-FAST: No PAC configured and "
+ "provisioning disabled");
+ eap_fast_deinit(sm, data);
+ return NULL;
+ }
+
+ return data;
+}
+
+
+static void eap_fast_deinit(struct eap_sm *sm, void *priv)
+{
+ struct eap_fast_data *data = priv;
+ struct eap_fast_pac *pac, *prev;
+
+ if (data == NULL)
+ return;
+ if (data->phase2_priv && data->phase2_method)
+ data->phase2_method->deinit(sm, data->phase2_priv);
+ free(data->phase2_types);
+ free(data->key_block_a);
+ free(data->key_block_p);
+ eap_tls_ssl_deinit(sm, &data->ssl);
+
+ pac = data->pac;
+ prev = NULL;
+ while (pac) {
+ prev = pac;
+ pac = pac->next;
+ eap_fast_free_pac(prev);
+ }
+ free(data);
+}
+
+
+static int eap_fast_encrypt(struct eap_sm *sm, struct eap_fast_data *data,
+ int id, u8 *plain, size_t plain_len,
+ u8 **out_data, size_t *out_len)
+{
+ int res;
+ u8 *pos;
+ struct eap_hdr *resp;
+
+ /* TODO: add support for fragmentation, if needed. This will need to
+ * add TLS Message Length field, if the frame is fragmented. */
+ resp = malloc(sizeof(struct eap_hdr) + 2 + data->ssl.tls_out_limit);
+ if (resp == NULL)
+ return 0;
+
+ resp->code = EAP_CODE_RESPONSE;
+ resp->identifier = id;
+
+ pos = (u8 *) (resp + 1);
+ *pos++ = EAP_TYPE_FAST;
+ *pos++ = data->fast_version;
+
+ res = tls_connection_encrypt(sm->ssl_ctx, data->ssl.conn,
+ plain, plain_len,
+ pos, data->ssl.tls_out_limit);
+ if (res < 0) {
+ wpa_printf(MSG_INFO, "EAP-FAST: Failed to encrypt Phase 2 "
+ "data");
+ free(resp);
+ return 0;
+ }
+
+ *out_len = sizeof(struct eap_hdr) + 2 + res;
+ resp->length = host_to_be16(*out_len);
+ *out_data = (u8 *) resp;
+ return 0;
+}
+
+
+static int eap_fast_phase2_nak(struct eap_sm *sm,
+ struct eap_fast_data *data,
+ struct eap_hdr *hdr,
+ u8 **resp, size_t *resp_len)
+{
+ struct eap_hdr *resp_hdr;
+ u8 *pos = (u8 *) (hdr + 1);
+
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Phase 2 Request: Nak type=%d", *pos);
+ wpa_hexdump(MSG_DEBUG, "EAP-FAST: Allowed Phase2 EAP types",
+ data->phase2_types, data->num_phase2_types);
+ *resp_len = sizeof(struct eap_hdr) + 1 + data->num_phase2_types;
+ *resp = malloc(*resp_len);
+ if (*resp == NULL)
+ return -1;
+
+ resp_hdr = (struct eap_hdr *) (*resp);
+ resp_hdr->code = EAP_CODE_RESPONSE;
+ resp_hdr->identifier = hdr->identifier;
+ resp_hdr->length = host_to_be16(*resp_len);
+ pos = (u8 *) (resp_hdr + 1);
+ *pos++ = EAP_TYPE_NAK;
+ memcpy(pos, data->phase2_types, data->num_phase2_types);
+
+ return 0;
+}
+
+
+static int eap_fast_derive_msk(struct eap_sm *sm, struct eap_fast_data *data)
+{
+ u8 isk[32];
+ u8 imck[60];
+
+ if (data->key_block_a == NULL)
+ return -1;
+
+ memset(isk, 0, sizeof(isk));
+ sha1_t_prf(data->key_block_a->session_key_seed,
+ sizeof(data->key_block_a->session_key_seed),
+ "Inner Methods Compound Keys",
+ isk, sizeof(isk), imck, sizeof(imck));
+ sha1_t_prf(imck, 40, "Session Key Generating Function", (u8 *) "", 0,
+ data->key_data, EAP_FAST_KEY_LEN);
+
+ wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: Derived key (MSK)",
+ data->key_data, EAP_FAST_KEY_LEN);
+
+ data->success = 1;
+
+ return 0;
+}
+
+
+static int eap_fast_set_tls_master_secret(struct eap_sm *sm,
+ struct eap_fast_data *data,
+ const u8 *tls, size_t tls_len)
+{
+ struct tls_keys keys;
+ u8 master_secret[48], *seed;
+ const u8 *server_random;
+ size_t seed_len, server_random_len;
+
+ if (data->tls_master_secret_set || !data->current_pac ||
+ tls_connection_get_keys(sm->ssl_ctx, data->ssl.conn, &keys)) {
+ return 0;
+ }
+
+ wpa_hexdump(MSG_DEBUG, "EAP-FAST: client_random",
+ keys.client_random, keys.client_random_len);
+
+ /* TLS master secret is needed before TLS library has processed this
+ * message which includes both ServerHello and an encrypted handshake
+ * message, so we need to parse server_random from this message before
+ * passing it to TLS library.
+ *
+ * Example TLS packet header:
+ * (16 03 01 00 2a 02 00 00 26 03 01 <32 bytes server_random>)
+ * Content Type: Handshake: 0x16
+ * Version: TLS 1.0 (0x0301)
+ * Lenghth: 42 (0x002a)
+ * Handshake Type: Server Hello: 0x02
+ * Length: 38 (0x000026)
+ * Version TLS 1.0 (0x0301)
+ * Random: 32 bytes
+ */
+ if (tls_len < 43 || tls[0] != 0x16 ||
+ tls[1] != 0x03 || tls[2] != 0x01 ||
+ tls[5] != 0x02 || tls[9] != 0x03 || tls[10] != 0x01) {
+ wpa_hexdump(MSG_DEBUG, "EAP-FAST: unrecognized TLS "
+ "ServerHello", tls, tls_len);
+ return -1;
+ }
+ server_random = tls + 11;
+ server_random_len = 32;
+ wpa_hexdump(MSG_DEBUG, "EAP-FAST: server_random",
+ server_random, server_random_len);
+
+
+ seed_len = keys.client_random_len + server_random_len;
+ seed = malloc(seed_len);
+ if (seed == NULL)
+ return -1;
+ memcpy(seed, server_random, server_random_len);
+ memcpy(seed + server_random_len,
+ keys.client_random, keys.client_random_len);
+
+ wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: T-PRF seed", seed, seed_len);
+ wpa_hexdump_key(MSG_MSGDUMP, "EAP-FAST: PAC-Key",
+ data->current_pac->pac_key, EAP_FAST_PAC_KEY_LEN);
+ /* master_secret = T-PRF(PAC-Key, "PAC to master secret label hash",
+ * server_random + client_random, 48) */
+ sha1_t_prf(data->current_pac->pac_key, EAP_FAST_PAC_KEY_LEN,
+ "PAC to master secret label hash",
+ seed, seed_len, master_secret, sizeof(master_secret));
+ free(seed);
+ wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: TLS pre-master-secret",
+ master_secret, sizeof(master_secret));
+
+ data->tls_master_secret_set = 1;
+
+ return tls_connection_set_master_key(sm->ssl_ctx, data->ssl.conn,
+ master_secret,
+ sizeof(master_secret));
+}
+
+
+static u8 * eap_fast_derive_key(struct eap_sm *sm, struct eap_ssl_data *data,
+ char *label, size_t len)
+{
+ struct tls_keys keys;
+ u8 *random;
+ u8 *out;
+
+ if (tls_connection_get_keys(sm->ssl_ctx, data->conn, &keys))
+ return NULL;
+ out = malloc(len);
+ random = malloc(keys.client_random_len + keys.server_random_len);
+ if (out == NULL || random == NULL) {
+ free(out);
+ free(random);
+ return NULL;
+ }
+ memcpy(random, keys.server_random, keys.server_random_len);
+ memcpy(random + keys.server_random_len, keys.client_random,
+ keys.client_random_len);
+
+ 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, random, keys.client_random_len +
+ keys.server_random_len, out, len)) {
+ free(random);
+ free(out);
+ return NULL;
+ }
+ free(random);
+ return out;
+}
+
+
+static void eap_fast_derive_key_auth(struct eap_sm *sm,
+ struct eap_fast_data *data)
+{
+ free(data->key_block_a);
+ data->key_block_a = (struct eap_fast_key_block_auth *)
+ eap_fast_derive_key(sm, &data->ssl, "key expansion",
+ sizeof(*data->key_block_a));
+ wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: session_key_seed",
+ data->key_block_a->session_key_seed,
+ sizeof(data->key_block_a->session_key_seed));
+}
+
+
+static void eap_fast_derive_key_provisioning(struct eap_sm *sm,
+ struct eap_fast_data *data)
+{
+ free(data->key_block_p);
+ data->key_block_p = (struct eap_fast_key_block_provisioning *)
+ eap_fast_derive_key(sm, &data->ssl, "key expansion",
+ sizeof(*data->key_block_p));
+ if (data->key_block_p == NULL) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to derive key block");
+ return;
+ }
+ wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: session_key_seed",
+ data->key_block_p->session_key_seed,
+ sizeof(data->key_block_p->session_key_seed));
+ wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: server_challenge",
+ data->key_block_p->server_challenge,
+ sizeof(data->key_block_p->server_challenge));
+ wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: client_challenge",
+ data->key_block_p->client_challenge,
+ sizeof(data->key_block_p->client_challenge));
+}
+
+
+static void eap_fast_derive_keys(struct eap_sm *sm, struct eap_fast_data *data)
+{
+ if (data->current_pac) {
+ eap_fast_derive_key_auth(sm, data);
+ } else {
+ eap_fast_derive_key_provisioning(sm, data);
+ }
+}
+
+
+static int eap_fast_phase2_request(struct eap_sm *sm,
+ struct eap_fast_data *data,
+ struct eap_method_ret *ret,
+ struct eap_hdr *req,
+ struct eap_hdr *hdr,
+ u8 **resp, size_t *resp_len)
+{
+ size_t len = be_to_host16(hdr->length);
+ u8 *pos;
+ struct eap_method_ret iret;
+
+ if (len <= sizeof(struct eap_hdr)) {
+ wpa_printf(MSG_INFO, "EAP-FAST: too short "
+ "Phase 2 request (len=%lu)", (unsigned long) len);
+ return -1;
+ }
+ pos = (u8 *) (hdr + 1);
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Phase 2 Request: type=%d", *pos);
+ switch (*pos) {
+ case EAP_TYPE_IDENTITY:
+ *resp = eap_sm_buildIdentity(sm, req->identifier, resp_len, 1);
+ break;
+ default:
+ if (data->phase2_type == EAP_TYPE_NONE) {
+ int i;
+ for (i = 0; i < data->num_phase2_types; i++) {
+ if (data->phase2_types[i] != *pos)
+ continue;
+
+ data->phase2_type = *pos;
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Selected "
+ "Phase 2 EAP method %d",
+ data->phase2_type);
+ break;
+ }
+ }
+ if (*pos != data->phase2_type || *pos == EAP_TYPE_NONE) {
+ if (eap_fast_phase2_nak(sm, data, hdr, resp, resp_len))
+ return -1;
+ return 0;
+ }
+
+ if (data->phase2_priv == NULL) {
+ data->phase2_method = eap_sm_get_eap_methods(*pos);
+ if (data->phase2_method) {
+ if (data->key_block_p) {
+ sm->auth_challenge =
+ data->key_block_p->
+ server_challenge;
+ sm->peer_challenge =
+ data->key_block_p->
+ client_challenge;
+ }
+ sm->init_phase2 = 1;
+ data->phase2_priv =
+ data->phase2_method->init(sm);
+ sm->init_phase2 = 0;
+ sm->auth_challenge = NULL;
+ sm->peer_challenge = NULL;
+ }
+ }
+ if (data->phase2_priv == NULL || data->phase2_method == NULL) {
+ wpa_printf(MSG_INFO, "EAP-FAST: failed to initialize "
+ "Phase 2 EAP method %d", *pos);
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_FAIL;
+ return -1;
+ }
+ memset(&iret, 0, sizeof(iret));
+ *resp = data->phase2_method->process(sm, data->phase2_priv,
+ &iret, (u8 *) hdr, len,
+ resp_len);
+ if (*resp == NULL ||
+ (iret.methodState == METHOD_DONE &&
+ iret.decision == DECISION_FAIL)) {
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_FAIL;
+ } else if ((iret.methodState == METHOD_DONE ||
+ iret.methodState == METHOD_MAY_CONT) &&
+ (iret.decision == DECISION_UNCOND_SUCC ||
+ iret.decision == DECISION_COND_SUCC)) {
+ data->phase2_success = 1;
+ }
+ if (*resp == NULL)
+ return -1;
+ break;
+ }
+ return 0;
+}
+
+
+static u8 * eap_fast_tlv_nak(int vendor_id, int tlv_type, size_t *len)
+{
+ struct eap_tlv_nak_tlv *nak;
+ *len = sizeof(*nak);
+ nak = malloc(*len);
+ if (nak == NULL)
+ return NULL;
+ nak->tlv_type = host_to_be16(EAP_TLV_TYPE_MANDATORY | EAP_TLV_NAK_TLV);
+ nak->length = host_to_be16(6);
+ nak->vendor_id = host_to_be32(vendor_id);
+ nak->nak_type = host_to_be16(tlv_type);
+ return (u8 *) nak;
+}
+
+
+static u8 * eap_fast_tlv_result(int status, int intermediate, size_t *len)
+{
+ struct eap_tlv_intermediate_result_tlv *result;
+ *len = sizeof(*result);
+ result = malloc(*len);
+ if (result == NULL)
+ return NULL;
+ result->tlv_type = host_to_be16(EAP_TLV_TYPE_MANDATORY |
+ (intermediate ?
+ EAP_TLV_INTERMEDIATE_RESULT_TLV :
+ EAP_TLV_RESULT_TLV));
+ result->length = host_to_be16(2);
+ result->status = host_to_be16(status);
+ return (u8 *) result;
+}
+
+
+static u8 * eap_fast_tlv_pac_ack(size_t *len)
+{
+ struct eap_tlv_result_tlv *res;
+ struct eap_tlv_pac_ack_tlv *ack;
+
+ *len = sizeof(*res) + sizeof(*ack);
+ res = malloc(*len);
+ if (res == NULL)
+ return NULL;
+
+ memset(res, 0, *len);
+ res->tlv_type = host_to_be16(EAP_TLV_RESULT_TLV |
+ EAP_TLV_TYPE_MANDATORY);
+ res->length = host_to_be16(sizeof(*res) - sizeof(struct eap_tlv_hdr));
+ res->status = host_to_be16(EAP_TLV_RESULT_SUCCESS);
+
+ ack = (struct eap_tlv_pac_ack_tlv *) (res + 1);
+ ack->tlv_type = host_to_be16(EAP_TLV_PAC_TLV |
+ EAP_TLV_TYPE_MANDATORY);
+ ack->length = host_to_be16(sizeof(*ack) - sizeof(struct eap_tlv_hdr));
+ ack->pac_type = host_to_be16(PAC_TYPE_PAC_ACKNOWLEDGEMENT);
+ ack->pac_len = host_to_be16(2);
+ ack->result = host_to_be16(EAP_TLV_RESULT_SUCCESS);
+
+ return (u8 *) res;
+}
+
+
+static u8 * eap_fast_tlv_eap_payload(u8 *buf, size_t *len)
+{
+ struct eap_tlv_hdr *tlv;
+
+ /* Encapsulate EAP packet in EAP Payload TLV */
+ tlv = malloc(sizeof(*tlv) + *len);
+ if (tlv == NULL) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to "
+ "allocate memory for TLV "
+ "encapsulation");
+ free(buf);
+ return NULL;
+ }
+ tlv->tlv_type = host_to_be16(EAP_TLV_TYPE_MANDATORY |
+ EAP_TLV_EAP_PAYLOAD_TLV);
+ tlv->length = host_to_be16(*len);
+ memcpy(tlv + 1, buf, *len);
+ free(buf);
+ *len += sizeof(*tlv);
+ return (u8 *) tlv;
+}
+
+
+static u8 * eap_fast_process_crypto_binding(
+ struct eap_sm *sm, struct eap_fast_data *data,
+ struct eap_method_ret *ret,
+ struct eap_tlv_crypto_binding__tlv *bind, size_t bind_len,
+ size_t *resp_len, int final)
+{
+ u8 *resp, *sks = NULL;
+ struct eap_tlv_intermediate_result_tlv *rresult;
+ struct eap_tlv_crypto_binding__tlv *rbind;
+ u8 isk[32], imck[60], *cmk, cmac[20], *key;
+ size_t key_len;
+ int res;
+
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Crypto-Binding TLV: Version %d "
+ "Received Version %d SubType %d",
+ bind->version, bind->received_version, bind->subtype);
+ wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: NONCE",
+ bind->nonce, sizeof(bind->nonce));
+ wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Compound MAC",
+ bind->compound_mac, sizeof(bind->compound_mac));
+
+ if (bind->version != EAP_FAST_VERSION ||
+ bind->received_version != EAP_FAST_VERSION ||
+ bind->subtype != EAP_TLV_CRYPTO_BINDING_SUBTYPE_REQUEST) {
+ wpa_printf(MSG_INFO, "EAP-FAST: Invalid version/subtype in "
+ "Crypto-Binding TLV: Version %d "
+ "Received Version %d SubType %d",
+ bind->version, bind->received_version,
+ bind->subtype);
+ resp = eap_fast_tlv_result(EAP_TLV_RESULT_FAILURE, 1,
+ resp_len);
+ return resp;
+ }
+
+
+ if (data->provisioning) {
+ if (data->key_block_p) {
+ sks = data->key_block_p->session_key_seed;
+ }
+ } else {
+ if (data->key_block_a) {
+ sks = data->key_block_a->session_key_seed;
+ }
+ }
+ if (sks == NULL) {
+ wpa_printf(MSG_INFO, "EAP-FAST: No Session Key Seed available "
+ "for processing Crypto-Binding TLV");
+ return NULL;
+ }
+
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Determining CMK for Compound MIC "
+ "calculation");
+ wpa_hexdump_key(MSG_MSGDUMP, "EAP-FAST: S-IMCK[0] = SKS", sks, 40);
+
+ memset(isk, 0, sizeof(isk));
+ if (data->phase2_method == NULL || data->phase2_priv == NULL) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Phase 2 method not "
+ "available");
+ return NULL;
+ }
+ if (data->phase2_method->isKeyAvailable && data->phase2_method->getKey)
+ {
+ if (!data->phase2_method->isKeyAvailable(sm, data->phase2_priv)
+ ||
+ (key = data->phase2_method->getKey(sm, data->phase2_priv,
+ &key_len)) == NULL) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Could not get key "
+ "material from Phase 2");
+ return NULL;
+ }
+ if (key_len > sizeof(isk))
+ key_len = sizeof(isk);
+ /* FIX: which end is being padded? */
+#if 0
+ memcpy(isk + (sizeof(isk) - key_len), key, key_len);
+#else
+ memcpy(isk, key, key_len);
+#endif
+ free(key);
+ }
+ wpa_hexdump_key(MSG_MSGDUMP, "EAP-FAST: ISK[0]", isk, sizeof(isk));
+ sha1_t_prf(sks, 40, "Inner Methods Compound Keys",
+ isk, sizeof(isk), imck, sizeof(imck));
+ /* S-IMCK[1] = imkc[0..39] */
+ wpa_hexdump_key(MSG_MSGDUMP, "EAP-FAST: S-IMCK[1]", imck, 40);
+ cmk = imck + 40;
+ wpa_hexdump_key(MSG_MSGDUMP, "EAP-FAST: CMK", cmk, 20);
+
+ memcpy(cmac, bind->compound_mac, sizeof(cmac));
+ memset(bind->compound_mac, 0, sizeof(cmac));
+ wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Crypto-Binding TLV for Compound "
+ "MAC calculation", (u8 *) bind, bind_len);
+ hmac_sha1(cmk, 20, (u8 *) bind, bind_len, bind->compound_mac);
+ res = memcmp(cmac, bind->compound_mac, sizeof(cmac));
+ wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Received Compound MAC",
+ cmac, sizeof(cmac));
+ wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Calculated Compound MAC",
+ bind->compound_mac, sizeof(cmac));
+ if (res != 0) {
+ wpa_printf(MSG_INFO, "EAP-FAST: Compound MAC did not match");
+ resp = eap_fast_tlv_result(EAP_TLV_RESULT_FAILURE, 1,
+ resp_len);
+ memcpy(bind->compound_mac, cmac, sizeof(cmac));
+ return resp;
+ }
+
+ *resp_len = sizeof(*rresult) + sizeof(*rbind);
+ resp = malloc(*resp_len);
+ if (resp == NULL)
+ return NULL;
+ memset(resp, 0, *resp_len);
+
+ /* Both intermediate and final Result TLVs are identical, so ok to use
+ * the same structure definition for them. */
+ rresult = (struct eap_tlv_intermediate_result_tlv *) resp;
+ rresult->tlv_type = host_to_be16(EAP_TLV_TYPE_MANDATORY |
+ (final ? EAP_TLV_RESULT_TLV :
+ EAP_TLV_INTERMEDIATE_RESULT_TLV));
+ rresult->length = host_to_be16(2);
+ rresult->status = host_to_be16(EAP_TLV_RESULT_SUCCESS);
+
+ if (!data->provisioning && data->phase2_success &&
+ eap_fast_derive_msk(sm, data) < 0) {
+ wpa_printf(MSG_INFO, "EAP-FAST: Failed to generate MSK");
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_FAIL;
+ rresult->status = host_to_be16(EAP_TLV_RESULT_FAILURE);
+ data->phase2_success = 0;
+ }
+
+ rbind = (struct eap_tlv_crypto_binding__tlv *) (rresult + 1);
+ rbind->tlv_type = host_to_be16(EAP_TLV_TYPE_MANDATORY |
+ EAP_TLV_CRYPTO_BINDING_TLV_);
+ rbind->length = host_to_be16(sizeof(*rbind) -
+ sizeof(struct eap_tlv_hdr));
+ rbind->version = EAP_FAST_VERSION;
+ rbind->received_version = bind->version;
+ rbind->subtype = EAP_TLV_CRYPTO_BINDING_SUBTYPE_RESPONSE;
+ memcpy(rbind->nonce, bind->nonce, sizeof(bind->nonce));
+ inc_byte_array(rbind->nonce, sizeof(bind->nonce));
+ hmac_sha1(cmk, 20, (u8 *) rbind, sizeof(*rbind), rbind->compound_mac);
+
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Reply Crypto-Binding TLV: Version %d "
+ "Received Version %d SubType %d",
+ rbind->version, rbind->received_version, rbind->subtype);
+ wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: NONCE",
+ rbind->nonce, sizeof(rbind->nonce));
+ wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Compound MAC",
+ rbind->compound_mac, sizeof(rbind->compound_mac));
+
+ if (final && data->phase2_success) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Authentication completed "
+ "successfully.");
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_UNCOND_SUCC;
+ }
+
+ return resp;
+}
+
+
+static u8 * eap_fast_process_pac(struct eap_sm *sm, struct eap_fast_data *data,
+ struct eap_method_ret *ret,
+ u8 *pac, size_t pac_len, size_t *resp_len)
+{
+ struct wpa_ssid *config = eap_get_config(sm);
+ struct pac_tlv_hdr *hdr;
+ u8 *pos;
+ size_t left, len;
+ int type, pac_key_found = 0;
+ struct eap_fast_pac entry;
+
+ memset(&entry, 0, sizeof(entry));
+ pos = pac;
+ left = pac_len;
+ while (left > sizeof(*hdr)) {
+ hdr = (struct pac_tlv_hdr *) pos;
+ type = be_to_host16(hdr->type);
+ len = be_to_host16(hdr->len);
+ pos += sizeof(*hdr);
+ left -= sizeof(*hdr);
+ if (len > left) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: PAC TLV overrun "
+ "(type=%d len=%lu left=%lu)",
+ type, (unsigned long) len,
+ (unsigned long) left);
+ return eap_fast_tlv_result(EAP_TLV_RESULT_FAILURE, 0,
+ resp_len);
+ }
+ switch (type) {
+ case PAC_TYPE_PAC_KEY:
+ wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: PAC-Key",
+ pos, len);
+ if (len != EAP_FAST_PAC_KEY_LEN) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Invalid "
+ "PAC-Key length %lu",
+ (unsigned long) len);
+ break;
+ }
+ pac_key_found = 1;
+ memcpy(entry.pac_key, pos, len);
+ break;
+ case PAC_TYPE_PAC_OPAQUE:
+ wpa_hexdump(MSG_DEBUG, "EAP-FAST: PAC-Opaque",
+ pos, len);
+ entry.pac_opaque = pos;
+ entry.pac_opaque_len = len;
+ break;
+ case PAC_TYPE_PAC_INFO:
+ wpa_hexdump(MSG_DEBUG, "EAP-FAST: PAC-Info",
+ pos, len);
+ entry.pac_info = pos;
+ entry.pac_info_len = len;
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Ignored unknown PAC "
+ "type %d", type);
+ break;
+ }
+
+ pos += len;
+ left -= len;
+ }
+
+ if (!pac_key_found || !entry.pac_opaque || !entry.pac_info) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: PAC TLV does not include "
+ "all the required fields");
+ return eap_fast_tlv_result(EAP_TLV_RESULT_FAILURE, 0,
+ resp_len);
+ }
+
+ pos = entry.pac_info;
+ left = entry.pac_info_len;
+ while (left > sizeof(*hdr)) {
+ hdr = (struct pac_tlv_hdr *) pos;
+ type = be_to_host16(hdr->type);
+ len = be_to_host16(hdr->len);
+ pos += sizeof(*hdr);
+ left -= sizeof(*hdr);
+ if (len > left) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: PAC-Info overrun "
+ "(type=%d len=%lu left=%lu)",
+ type, (unsigned long) len,
+ (unsigned long) left);
+ return eap_fast_tlv_result(EAP_TLV_RESULT_FAILURE, 0,
+ resp_len);
+ }
+ switch (type) {
+ case PAC_TYPE_A_ID:
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP-FAST: PAC-Info - "
+ "A-ID", pos, len);
+ entry.a_id = pos;
+ entry.a_id_len = len;
+ break;
+ case PAC_TYPE_I_ID:
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP-FAST: PAC-Info - "
+ "I-ID", pos, len);
+ entry.i_id = pos;
+ entry.i_id_len = len;
+ break;
+ case PAC_TYPE_A_ID_INFO:
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP-FAST: PAC-Info - "
+ "A-ID-Info", pos, len);
+ entry.a_id_info = pos;
+ entry.a_id_info_len = len;
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Ignored unknown "
+ "PAC-Info type %d", type);
+ break;
+ }
+
+ pos += len;
+ left -= len;
+ }
+
+ if (entry.a_id == NULL || entry.a_id_info == NULL) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: PAC-Info does not include "
+ "all the required fields");
+ return eap_fast_tlv_result(EAP_TLV_RESULT_FAILURE, 0,
+ resp_len);
+ }
+
+ eap_fast_add_pac(data, &entry);
+ eap_fast_save_pac(data, config->pac_file);
+
+ if (data->provisioning) {
+ /* EAP-FAST provisioning does not provide keying material and
+ * must end with an EAP-Failure. Authentication will be done
+ * separately after this. */
+ data->success = 0;
+ ret->decision = DECISION_FAIL;
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Send PAC-Acknowledgement TLV "
+ "- Provisioning completed successfully");
+ } else {
+ /* This is PAC refreshing, i.e., normal authentication that is
+ * expected to be completed with an EAP-Success. */
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Send PAC-Acknowledgement TLV "
+ "- PAC refreshing completed successfully");
+ ret->decision = DECISION_UNCOND_SUCC;
+ }
+ ret->methodState = METHOD_DONE;
+ return eap_fast_tlv_pac_ack(resp_len);
+}
+
+
+static int eap_fast_decrypt(struct eap_sm *sm, struct eap_fast_data *data,
+ struct eap_method_ret *ret, struct eap_hdr *req,
+ u8 *in_data, size_t in_len,
+ u8 **out_data, size_t *out_len)
+{
+ u8 *in_decrypted, *pos, *end;
+ int buf_len, len_decrypted, len, res;
+ struct eap_hdr *hdr;
+ u8 *resp = NULL;
+ size_t resp_len;
+ int mandatory, tlv_type;
+ u8 *eap_payload_tlv = NULL, *pac = NULL;
+ size_t eap_payload_tlv_len = 0, pac_len = 0;
+ int iresult = 0, result = 0;
+ struct eap_tlv_crypto_binding__tlv *crypto_binding = NULL;
+ size_t crypto_binding_len = 0;
+
+ wpa_printf(MSG_DEBUG, "EAP-FAST: received %lu bytes encrypted data for"
+ " Phase 2", (unsigned long) in_len);
+
+ res = eap_tls_data_reassemble(sm, &data->ssl, &in_data, &in_len);
+ if (res < 0 || res == 1)
+ return res;
+
+ buf_len = in_len;
+ if (data->ssl.tls_in_total > buf_len)
+ buf_len = data->ssl.tls_in_total;
+ in_decrypted = malloc(buf_len);
+ if (in_decrypted == NULL) {
+ free(data->ssl.tls_in);
+ data->ssl.tls_in = NULL;
+ data->ssl.tls_in_len = 0;
+ wpa_printf(MSG_WARNING, "EAP-FAST: failed to allocate memory "
+ "for decryption");
+ return -1;
+ }
+
+ len_decrypted = tls_connection_decrypt(sm->ssl_ctx, data->ssl.conn,
+ in_data, in_len,
+ in_decrypted, buf_len);
+ free(data->ssl.tls_in);
+ data->ssl.tls_in = NULL;
+ data->ssl.tls_in_len = 0;
+ if (len_decrypted < 0) {
+ wpa_printf(MSG_INFO, "EAP-FAST: Failed to decrypt Phase 2 "
+ "data");
+ free(in_decrypted);
+ return -1;
+ }
+
+ wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Decrypted Phase 2 TLV(s)",
+ in_decrypted, len_decrypted);
+
+ if (len_decrypted < 4) {
+ free(in_decrypted);
+ wpa_printf(MSG_INFO, "EAP-FAST: Too short Phase 2 "
+ "TLV frame (len=%d)", len_decrypted);
+ return -1;
+ }
+
+ pos = in_decrypted;
+ end = in_decrypted + len_decrypted;
+ while (pos < end) {
+ mandatory = pos[0] & 0x80;
+ tlv_type = ((pos[0] & 0x3f) << 8) | pos[1];
+ len = (pos[2] << 8) | pos[3];
+ pos += 4;
+ if (pos + len > end) {
+ free(in_decrypted);
+ wpa_printf(MSG_INFO, "EAP-FAST: TLV overflow");
+ return 0;
+ }
+ wpa_printf(MSG_DEBUG, "EAP-FAST: received Phase 2: "
+ "TLV type %d length %d%s",
+ tlv_type, len, mandatory ? " (mandatory)" : "");
+
+ switch (tlv_type) {
+ case EAP_TLV_EAP_PAYLOAD_TLV:
+ wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: EAP Payload TLV",
+ pos, len);
+ eap_payload_tlv = pos;
+ eap_payload_tlv_len = len;
+ break;
+ case EAP_TLV_RESULT_TLV:
+ wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Result TLV",
+ pos, len);
+ if (len < 2) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Too short "
+ "Result TLV");
+ result = EAP_TLV_RESULT_FAILURE;
+ break;
+ }
+ result = (pos[0] << 16) | pos[1];
+ if (result != EAP_TLV_RESULT_SUCCESS &&
+ result != EAP_TLV_RESULT_FAILURE) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Unknown "
+ "Result %d", result);
+ result = EAP_TLV_RESULT_FAILURE;
+ }
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Result: %s",
+ result == EAP_TLV_RESULT_SUCCESS ?
+ "Success" : "Failure");
+ break;
+ case EAP_TLV_INTERMEDIATE_RESULT_TLV:
+ wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Intermediate "
+ "Result TLV", pos, len);
+ if (len < 2) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Too short "
+ "Intermediate Result TLV");
+ iresult = EAP_TLV_RESULT_FAILURE;
+ break;
+ }
+ iresult = (pos[0] << 16) | pos[1];
+ if (iresult != EAP_TLV_RESULT_SUCCESS &&
+ iresult != EAP_TLV_RESULT_FAILURE) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Unknown "
+ "Intermediate Result %d", iresult);
+ iresult = EAP_TLV_RESULT_FAILURE;
+ }
+ wpa_printf(MSG_DEBUG,
+ "EAP-FAST: Intermediate Result: %s",
+ iresult == EAP_TLV_RESULT_SUCCESS ?
+ "Success" : "Failure");
+ break;
+ case EAP_TLV_CRYPTO_BINDING_TLV_:
+ wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Crypto-Binding "
+ "TLV", pos, len);
+ crypto_binding_len = sizeof(struct eap_tlv_hdr) + len;
+ if (crypto_binding_len < sizeof(*crypto_binding)) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Too short "
+ "Crypto-Binding TLV");
+ iresult = EAP_TLV_RESULT_FAILURE;
+ pos = end;
+ break;
+ }
+ crypto_binding =
+ (struct eap_tlv_crypto_binding__tlv *)
+ (pos - sizeof(struct eap_tlv_hdr));
+ break;
+ case EAP_TLV_PAC_TLV:
+ wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: PAC TLV",
+ pos, len);
+ pac = pos;
+ pac_len = len;
+ break;
+ default:
+ if (mandatory) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Nak unknown "
+ "mandatory TLV type %d", tlv_type);
+ resp = eap_fast_tlv_nak(0, tlv_type,
+ &resp_len);
+ pos = end;
+ } else {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: ignored "
+ "unknown optional TLV type %d",
+ tlv_type);
+ }
+ break;
+ }
+
+ pos += len;
+ }
+
+ if (!resp && result == EAP_TLV_RESULT_FAILURE) {
+ resp = eap_fast_tlv_result(EAP_TLV_RESULT_FAILURE, 0,
+ &resp_len);
+ if (!resp) {
+ free(in_decrypted);
+ return 0;
+ }
+ }
+
+ if (!resp && iresult == EAP_TLV_RESULT_FAILURE) {
+ resp = eap_fast_tlv_result(EAP_TLV_RESULT_FAILURE, 1,
+ &resp_len);
+ if (!resp) {
+ free(in_decrypted);
+ return 0;
+ }
+ }
+
+ if (!resp && eap_payload_tlv) {
+ if (eap_payload_tlv_len < sizeof(*hdr)) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: too short EAP "
+ "Payload TLV (len=%lu)",
+ (unsigned long) eap_payload_tlv_len);
+ free(in_decrypted);
+ return 0;
+ }
+ hdr = (struct eap_hdr *) eap_payload_tlv;
+ if (be_to_host16(hdr->length) > eap_payload_tlv_len) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: EAP packet overflow "
+ "in EAP Payload TLV");
+ }
+ if (hdr->code == EAP_CODE_REQUEST) {
+ if (eap_fast_phase2_request(sm, data, ret, req, hdr,
+ &resp, &resp_len)) {
+ free(in_decrypted);
+ wpa_printf(MSG_INFO, "EAP-FAST: Phase2 "
+ "Request processing failed");
+ return 0;
+ }
+ resp = eap_fast_tlv_eap_payload(resp, &resp_len);
+ if (resp == NULL) {
+ free(in_decrypted);
+ return 0;
+ }
+ } else {
+ wpa_printf(MSG_INFO, "EAP-FAST: Unexpected code=%d in "
+ "Phase 2 EAP header", hdr->code);
+ free(in_decrypted);
+ return 0;
+ }
+ }
+
+ if (!resp && crypto_binding) {
+ int final = result == EAP_TLV_RESULT_SUCCESS;
+ resp = eap_fast_process_crypto_binding(sm, data, ret,
+ crypto_binding,
+ crypto_binding_len,
+ &resp_len, final);
+ if (!resp) {
+ free(in_decrypted);
+ return 0;
+ }
+ }
+
+ if (!resp && pac && result != EAP_TLV_RESULT_SUCCESS) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: PAC TLV without Result TLV "
+ "acnowledging success");
+ resp = eap_fast_tlv_result(EAP_TLV_RESULT_FAILURE, 0,
+ &resp_len);
+ if (!resp) {
+ free(in_decrypted);
+ return 0;
+ }
+ }
+
+ if (!resp && pac && result == EAP_TLV_RESULT_SUCCESS) {
+ resp = eap_fast_process_pac(sm, data, ret, pac, pac_len,
+ &resp_len);
+ if (!resp) {
+ free(in_decrypted);
+ return 0;
+ }
+ }
+
+ free(in_decrypted);
+
+ if (resp == NULL) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: No recognized TLVs - send "
+ "empty response packet");
+ resp = malloc(0);
+ if (resp == NULL)
+ return 0;
+ resp_len = 0;
+ }
+
+ wpa_hexdump(MSG_DEBUG, "EAP-FAST: Encrypting Phase 2 data",
+ resp, resp_len);
+ if (eap_fast_encrypt(sm, data, req->identifier, resp, resp_len,
+ out_data, out_len)) {
+ wpa_printf(MSG_INFO, "EAP-FAST: Failed to encrypt a Phase 2 "
+ "frame");
+ }
+ free(resp);
+
+ return 0;
+}
+
+
+static u8 * eap_fast_process(struct eap_sm *sm, void *priv,
+ struct eap_method_ret *ret,
+ u8 *reqData, size_t reqDataLen,
+ size_t *respDataLen)
+{
+ struct eap_hdr *req;
+ int left, res;
+ unsigned int tls_msg_len;
+ u8 flags, *pos, *resp, id;
+ struct eap_fast_data *data = priv;
+
+ if (tls_get_errors(sm->ssl_ctx)) {
+ wpa_printf(MSG_INFO, "EAP-FAST: TLS errors detected");
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ req = (struct eap_hdr *) reqData;
+ pos = (u8 *) (req + 1);
+ if (reqDataLen < sizeof(*req) + 2 || *pos != EAP_TYPE_FAST ||
+ (left = host_to_be16(req->length)) > reqDataLen) {
+ wpa_printf(MSG_INFO, "EAP-FAST: Invalid frame");
+ ret->ignore = TRUE;
+ return NULL;
+ }
+ left -= sizeof(struct eap_hdr);
+ id = req->identifier;
+ pos++;
+ flags = *pos++;
+ left -= 2;
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Received packet(len=%lu) - "
+ "Flags 0x%02x", (unsigned long) reqDataLen, flags);
+ if (flags & EAP_TLS_FLAGS_LENGTH_INCLUDED) {
+ if (left < 4) {
+ wpa_printf(MSG_INFO, "EAP-FAST: Short frame with TLS "
+ "length");
+ ret->ignore = TRUE;
+ return NULL;
+ }
+ tls_msg_len = (pos[0] << 24) | (pos[1] << 16) | (pos[2] << 8) |
+ pos[3];
+ wpa_printf(MSG_DEBUG, "EAP-FAST: TLS Message Length: %d",
+ tls_msg_len);
+ if (data->ssl.tls_in_left == 0) {
+ data->ssl.tls_in_total = tls_msg_len;
+ data->ssl.tls_in_left = tls_msg_len;
+ free(data->ssl.tls_in);
+ data->ssl.tls_in = NULL;
+ data->ssl.tls_in_len = 0;
+ }
+ pos += 4;
+ left -= 4;
+ }
+
+ ret->ignore = FALSE;
+ ret->methodState = METHOD_CONT;
+ ret->decision = DECISION_FAIL;
+ ret->allowNotifications = TRUE;
+
+ if (flags & EAP_TLS_FLAGS_START) {
+ u8 *a_id;
+ size_t a_id_len;
+ struct pac_tlv_hdr *hdr;
+
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Start (server ver=%d, own "
+ "ver=%d)", flags & EAP_PEAP_VERSION_MASK,
+ data->fast_version);
+ if ((flags & EAP_PEAP_VERSION_MASK) < data->fast_version)
+ data->fast_version = flags & EAP_PEAP_VERSION_MASK;
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Using FAST version %d",
+ data->fast_version);
+
+ a_id = pos;
+ a_id_len = left;
+ if (left > sizeof(*hdr)) {
+ int tlen;
+ hdr = (struct pac_tlv_hdr *) pos;
+ tlen = be_to_host16(hdr->len);
+ if (be_to_host16(hdr->type) == PAC_TYPE_A_ID &&
+ sizeof(*hdr) + tlen <= left) {
+ a_id = (u8 *) (hdr + 1);
+ a_id_len = tlen;
+ }
+ }
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP-FAST: A-ID", a_id, a_id_len);
+
+ data->current_pac = eap_fast_get_pac(data, a_id, a_id_len);
+ if (data->current_pac) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: PAC found for this "
+ "A-ID");
+ wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-FAST: A-ID-Info",
+ data->current_pac->a_id_info,
+ data->current_pac->a_id_info_len);
+ }
+
+ if (data->resuming && data->current_pac) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Trying to resume "
+ "session - do not add PAC-Opaque to TLS "
+ "ClientHello");
+ if (tls_connection_client_hello_ext(
+ sm->ssl_ctx, data->ssl.conn,
+ TLS_EXT_PAC_OPAQUE, NULL, 0) < 0) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to "
+ "remove PAC-Opaque TLS extension");
+ return NULL;
+ }
+
+ } else if (data->current_pac) {
+ u8 *tlv;
+ size_t tlv_len, olen;
+ struct eap_tlv_hdr *hdr;
+ olen = data->current_pac->pac_opaque_len;
+ tlv_len = sizeof(*hdr) + olen;
+ tlv = malloc(tlv_len);
+ if (tlv) {
+ hdr = (struct eap_tlv_hdr *) tlv;
+ hdr->tlv_type =
+ host_to_be16(PAC_TYPE_PAC_OPAQUE);
+ hdr->length = host_to_be16(olen);
+ memcpy(hdr + 1, data->current_pac->pac_opaque,
+ olen);
+ }
+ if (tlv == NULL ||
+ tls_connection_client_hello_ext(
+ sm->ssl_ctx, data->ssl.conn,
+ TLS_EXT_PAC_OPAQUE, tlv, tlv_len) < 0) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to "
+ "add PAC-Opaque TLS extension");
+ free(tlv);
+ return NULL;
+ }
+ free(tlv);
+ } else {
+ if (!data->provisioning_allowed) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: No PAC found "
+ "and provisioning disabled");
+ return NULL;
+ }
+ wpa_printf(MSG_DEBUG, "EAP-FAST: No PAC found - "
+ "starting provisioning");
+ if (tls_connection_set_anon_dh(sm->ssl_ctx,
+ data->ssl.conn)) {
+ wpa_printf(MSG_INFO, "EAP-FAST: Could not "
+ "configure anonymous DH for TLS "
+ "connection");
+ return NULL;
+ }
+ if (tls_connection_client_hello_ext(
+ sm->ssl_ctx, data->ssl.conn,
+ TLS_EXT_PAC_OPAQUE, NULL, 0) < 0) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to "
+ "remove PAC-Opaque TLS extension");
+ return NULL;
+ }
+ data->provisioning = 1;
+ }
+
+ left = 0; /* A-ID is not used in further packet processing */
+ }
+
+ resp = NULL;
+ if (tls_connection_established(sm->ssl_ctx, data->ssl.conn) &&
+ !data->resuming) {
+ res = eap_fast_decrypt(sm, data, ret, req, pos, left,
+ &resp, respDataLen);
+ if (res < 0) {
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_FAIL;
+ /* Ack possible Alert that may have caused failure in
+ * decryption */
+ res = 1;
+ }
+ } else {
+ if (eap_fast_set_tls_master_secret(sm, data, pos, left) < 0) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to configure "
+ "TLS master secret");
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_FAIL;
+ return NULL;
+ }
+
+ res = eap_tls_process_helper(sm, &data->ssl, EAP_TYPE_FAST,
+ data->fast_version, id, pos, left,
+ &resp, respDataLen);
+
+ if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) {
+ wpa_printf(MSG_DEBUG,
+ "EAP-FAST: TLS done, proceed to Phase 2");
+ data->resuming = 0;
+ eap_fast_derive_keys(sm, data);
+ }
+ }
+
+ if (res == 1)
+ return eap_tls_build_ack(&data->ssl, respDataLen, id,
+ EAP_TYPE_FAST, data->fast_version);
+ return resp;
+}
+
+
+#if 0 /* FIX */
+static Boolean eap_fast_has_reauth_data(struct eap_sm *sm, void *priv)
+{
+ struct eap_fast_data *data = priv;
+ return tls_connection_established(sm->ssl_ctx, data->ssl.conn);
+}
+
+
+static void eap_fast_deinit_for_reauth(struct eap_sm *sm, void *priv)
+{
+}
+
+
+static void * eap_fast_init_for_reauth(struct eap_sm *sm, void *priv)
+{
+ struct eap_fast_data *data = priv;
+ if (eap_tls_reauth_init(sm, &data->ssl)) {
+ free(data);
+ return NULL;
+ }
+ data->phase2_success = 0;
+ data->resuming = 1;
+ data->provisioning = 0;
+ return priv;
+}
+#endif
+
+
+static int eap_fast_get_status(struct eap_sm *sm, void *priv, char *buf,
+ size_t buflen, int verbose)
+{
+ struct eap_fast_data *data = priv;
+ return eap_tls_status(sm, &data->ssl, buf, buflen, verbose);
+}
+
+
+static Boolean eap_fast_isKeyAvailable(struct eap_sm *sm, void *priv)
+{
+ struct eap_fast_data *data = priv;
+ return data->success;
+}
+
+
+static u8 * eap_fast_getKey(struct eap_sm *sm, void *priv, size_t *len)
+{
+ struct eap_fast_data *data = priv;
+ u8 *key;
+
+ if (!data->success)
+ return NULL;
+
+ key = malloc(EAP_FAST_KEY_LEN);
+ if (key == NULL)
+ return NULL;
+
+ *len = EAP_FAST_KEY_LEN;
+ memcpy(key, data->key_data, EAP_FAST_KEY_LEN);
+
+ return key;
+}
+
+
+const struct eap_method eap_method_fast =
+{
+ .method = EAP_TYPE_FAST,
+ .name = "FAST",
+ .init = eap_fast_init,
+ .deinit = eap_fast_deinit,
+ .process = eap_fast_process,
+ .isKeyAvailable = eap_fast_isKeyAvailable,
+ .getKey = eap_fast_getKey,
+ .get_status = eap_fast_get_status,
+#if 0
+ .has_reauth_data = eap_fast_has_reauth_data,
+ .deinit_for_reauth = eap_fast_deinit_for_reauth,
+ .init_for_reauth = eap_fast_init_for_reauth,
+#endif
+};
diff --git a/contrib/wpa_supplicant/eap_gtc.c b/contrib/wpa_supplicant/eap_gtc.c
new file mode 100644
index 000000000000..42a7a49acaf1
--- /dev/null
+++ b/contrib/wpa_supplicant/eap_gtc.c
@@ -0,0 +1,164 @@
+/*
+ * WPA Supplicant / EAP-GTC (RFC 2284)
+ * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published 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 <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "common.h"
+#include "eap_i.h"
+#include "wpa_supplicant.h"
+#include "config_ssid.h"
+
+
+struct eap_gtc_data {
+ int prefix;
+};
+
+
+static void * eap_gtc_init(struct eap_sm *sm)
+{
+ struct eap_gtc_data *data;
+ data = malloc(sizeof(*data));
+ if (data == NULL)
+ return NULL;
+ memset(data, 0, sizeof(*data));
+
+ if (sm->m && sm->m->method == EAP_TYPE_FAST) {
+ wpa_printf(MSG_DEBUG, "EAP-GTC: EAP-FAST tunnel - use prefix "
+ "with challenge/response");
+ data->prefix = 1;
+ }
+ return data;
+}
+
+
+static void eap_gtc_deinit(struct eap_sm *sm, void *priv)
+{
+ struct eap_gtc_data *data = priv;
+ free(data);
+}
+
+
+static u8 * eap_gtc_process(struct eap_sm *sm, void *priv,
+ struct eap_method_ret *ret,
+ u8 *reqData, size_t reqDataLen,
+ size_t *respDataLen)
+{
+ struct eap_gtc_data *data = priv;
+ struct wpa_ssid *config = eap_get_config(sm);
+ struct eap_hdr *req, *resp;
+ u8 *pos, *password;
+ size_t password_len, len, msg_len;
+
+ req = (struct eap_hdr *) reqData;
+ pos = (u8 *) (req + 1);
+ if (reqDataLen < sizeof(*req) + 1 || *pos != EAP_TYPE_GTC ||
+ (len = be_to_host16(req->length)) > reqDataLen) {
+ wpa_printf(MSG_INFO, "EAP-GTC: Invalid frame");
+ ret->ignore = TRUE;
+ return NULL;
+ }
+ pos++;
+ msg_len = len - sizeof(*req) - 1;
+ wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-GTC: Request message",
+ pos, msg_len);
+ if (data->prefix &&
+ (msg_len < 10 || memcmp(pos, "CHALLENGE=", 10) != 0)) {
+ wpa_printf(MSG_DEBUG, "EAP-GTC: Challenge did not start with "
+ "expected prefix");
+
+ /* Send an empty response in order to allow tunneled
+ * acknowledgement of the failure. This will also cover the
+ * error case which seems to use EAP-MSCHAPv2 like error
+ * reporting with EAP-GTC inside EAP-FAST tunnel. */
+ *respDataLen = sizeof(struct eap_hdr) + 1;
+ resp = malloc(*respDataLen);
+ if (resp == NULL)
+ return NULL;
+ resp->code = EAP_CODE_RESPONSE;
+ resp->identifier = req->identifier;
+ resp->length = host_to_be16(*respDataLen);
+ pos = (u8 *) (resp + 1);
+ *pos++ = EAP_TYPE_GTC;
+ return (u8 *) resp;
+ }
+
+ if (config == NULL ||
+ (config->password == NULL && config->otp == NULL)) {
+ wpa_printf(MSG_INFO, "EAP-GTC: Password not configured");
+ eap_sm_request_otp(sm, config, (char *) pos,
+ len - sizeof(*req) - 1);
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ if (config->otp) {
+ password = config->otp;
+ password_len = config->otp_len;
+ } else {
+ password = config->password;
+ password_len = config->password_len;
+ }
+
+ ret->ignore = FALSE;
+
+ ret->methodState = data->prefix ? METHOD_MAY_CONT : METHOD_DONE;
+ ret->decision = DECISION_COND_SUCC;
+ ret->allowNotifications = FALSE;
+
+ *respDataLen = sizeof(struct eap_hdr) + 1 + password_len;
+ if (data->prefix) {
+ *respDataLen += 9 + config->identity_len + 1;
+ }
+ resp = malloc(*respDataLen);
+ if (resp == NULL)
+ return NULL;
+ resp->code = EAP_CODE_RESPONSE;
+ resp->identifier = req->identifier;
+ resp->length = host_to_be16(*respDataLen);
+ pos = (u8 *) (resp + 1);
+ *pos++ = EAP_TYPE_GTC;
+ if (data->prefix) {
+ memcpy(pos, "RESPONSE=", 9);
+ pos += 9;
+ memcpy(pos, config->identity, config->identity_len);
+ pos += config->identity_len;
+ *pos++ = '\0';
+ }
+ memcpy(pos, password, password_len);
+ wpa_hexdump_ascii_key(MSG_MSGDUMP, "EAP-GTC: Response",
+ (u8 *) (resp + 1) + 1,
+ *respDataLen - sizeof(struct eap_hdr) - 1);
+
+ if (config->otp) {
+ wpa_printf(MSG_DEBUG, "EAP-GTC: Forgetting used password");
+ memset(config->otp, 0, config->otp_len);
+ free(config->otp);
+ config->otp = NULL;
+ config->otp_len = 0;
+ }
+
+ return (u8 *) resp;
+}
+
+
+const struct eap_method eap_method_gtc =
+{
+ .method = EAP_TYPE_GTC,
+ .name = "GTC",
+ .init = eap_gtc_init,
+ .deinit = eap_gtc_deinit,
+ .process = eap_gtc_process,
+};
diff --git a/contrib/wpa_supplicant/eap_i.h b/contrib/wpa_supplicant/eap_i.h
new file mode 100644
index 000000000000..c71981255783
--- /dev/null
+++ b/contrib/wpa_supplicant/eap_i.h
@@ -0,0 +1,106 @@
+#ifndef EAP_I_H
+#define EAP_I_H
+
+#include "eap.h"
+
+/* draft-ietf-eap-statemachine-05.pdf - Peer state machine */
+
+typedef enum {
+ DECISION_FAIL, DECISION_COND_SUCC, DECISION_UNCOND_SUCC
+} EapDecision;
+
+typedef enum {
+ METHOD_NONE, METHOD_INIT, METHOD_CONT, METHOD_MAY_CONT, METHOD_DONE
+} EapMethodState;
+
+struct eap_method_ret {
+ Boolean ignore;
+ EapMethodState methodState;
+ EapDecision decision;
+ Boolean allowNotifications;
+};
+
+
+struct eap_method {
+ EapType method;
+ const char *name;
+
+ void * (*init)(struct eap_sm *sm);
+ void (*deinit)(struct eap_sm *sm, void *priv);
+ u8 * (*process)(struct eap_sm *sm, void *priv,
+ struct eap_method_ret *ret,
+ u8 *reqData, size_t reqDataLen,
+ size_t *respDataLen);
+ Boolean (*isKeyAvailable)(struct eap_sm *sm, void *priv);
+ u8 * (*getKey)(struct eap_sm *sm, void *priv, size_t *len);
+ int (*get_status)(struct eap_sm *sm, void *priv, char *buf,
+ size_t buflen, int verbose);
+
+ /* Optional handlers for fast re-authentication */
+ Boolean (*has_reauth_data)(struct eap_sm *sm, void *priv);
+ void (*deinit_for_reauth)(struct eap_sm *sm, void *priv);
+ void * (*init_for_reauth)(struct eap_sm *sm, void *priv);
+ const u8 * (*get_identity)(struct eap_sm *sm, void *priv, size_t *len);
+};
+
+
+struct eap_sm {
+ enum {
+ EAP_INITIALIZE, EAP_DISABLED, EAP_IDLE, EAP_RECEIVED,
+ EAP_GET_METHOD, EAP_METHOD, EAP_SEND_RESPONSE, EAP_DISCARD,
+ EAP_IDENTITY, EAP_NOTIFICATION, EAP_RETRANSMIT, EAP_SUCCESS,
+ EAP_FAILURE
+ } EAP_state;
+ /* Long-term local variables */
+ EapType selectedMethod;
+ EapMethodState methodState;
+ int lastId;
+ u8 *lastRespData;
+ size_t lastRespDataLen;
+ EapDecision decision;
+ /* Short-term local variables */
+ Boolean rxReq;
+ Boolean rxSuccess;
+ Boolean rxFailure;
+ int reqId;
+ EapType reqMethod;
+ Boolean ignore;
+ /* Constants */
+ int ClientTimeout;
+
+ /* Miscellaneous variables */
+ Boolean allowNotifications; /* peer state machine <-> methods */
+ u8 *eapRespData; /* peer to lower layer */
+ size_t eapRespDataLen; /* peer to lower layer */
+ Boolean eapKeyAvailable; /* peer to lower layer */
+ u8 *eapKeyData; /* peer to lower layer */
+ size_t eapKeyDataLen; /* peer to lower layer */
+ const struct eap_method *m; /* selected EAP method */
+ /* not defined in draft-ietf-eap-statemachine-02 */
+ Boolean changed;
+ void *eapol_ctx;
+ struct eapol_callbacks *eapol_cb;
+ void *eap_method_priv;
+ int init_phase2;
+ int fast_reauth;
+
+ Boolean rxResp /* LEAP only */;
+ Boolean leap_done;
+ Boolean peap_done;
+ u8 req_md5[16]; /* MD5() of the current EAP packet */
+ u8 last_md5[16]; /* MD5() of the previously received EAP packet; used
+ * in duplicate request detection. */
+
+ void *msg_ctx;
+ void *scard_ctx;
+ void *ssl_ctx;
+
+ unsigned int workaround;
+
+ /* Optional challenges generated in Phase 1 (EAP-FAST) */
+ u8 *peer_challenge, *auth_challenge;
+
+ int num_rounds;
+};
+
+#endif /* EAP_I_H */
diff --git a/contrib/wpa_supplicant/eap_leap.c b/contrib/wpa_supplicant/eap_leap.c
new file mode 100644
index 000000000000..6409a6855f92
--- /dev/null
+++ b/contrib/wpa_supplicant/eap_leap.c
@@ -0,0 +1,379 @@
+/*
+ * WPA Supplicant / EAP-LEAP
+ * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published 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 <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "common.h"
+#include "eap_i.h"
+#include "wpa_supplicant.h"
+#include "config_ssid.h"
+#include "ms_funcs.h"
+#include "md5.h"
+
+#define LEAP_VERSION 1
+#define LEAP_CHALLENGE_LEN 8
+#define LEAP_RESPONSE_LEN 24
+#define LEAP_KEY_LEN 16
+
+
+struct eap_leap_data {
+ enum {
+ LEAP_WAIT_CHALLENGE,
+ LEAP_WAIT_SUCCESS,
+ LEAP_WAIT_RESPONSE,
+ LEAP_DONE
+ } state;
+
+ u8 peer_challenge[LEAP_CHALLENGE_LEN];
+ u8 peer_response[LEAP_RESPONSE_LEN];
+
+ u8 ap_challenge[LEAP_CHALLENGE_LEN];
+ u8 ap_response[LEAP_RESPONSE_LEN];
+};
+
+
+static void * eap_leap_init(struct eap_sm *sm)
+{
+ struct eap_leap_data *data;
+
+ data = malloc(sizeof(*data));
+ if (data == NULL)
+ return NULL;
+ memset(data, 0, sizeof(*data));
+ data->state = LEAP_WAIT_CHALLENGE;
+
+ sm->leap_done = FALSE;
+ return data;
+}
+
+
+static void eap_leap_deinit(struct eap_sm *sm, void *priv)
+{
+ free(priv);
+}
+
+
+static u8 * eap_leap_process_request(struct eap_sm *sm, void *priv,
+ struct eap_method_ret *ret,
+ u8 *reqData, size_t reqDataLen,
+ size_t *respDataLen)
+{
+ struct eap_leap_data *data = priv;
+ struct wpa_ssid *config = eap_get_config(sm);
+ struct eap_hdr *req, *resp;
+ u8 *pos, *challenge, challenge_len;
+
+ wpa_printf(MSG_DEBUG, "EAP-LEAP: Processing EAP-Request");
+
+ req = (struct eap_hdr *) reqData;
+ pos = (u8 *) (req + 1);
+ if (reqDataLen < sizeof(*req) + 4 || *pos != EAP_TYPE_LEAP) {
+ wpa_printf(MSG_INFO, "EAP-LEAP: Invalid EAP-Request frame");
+ ret->ignore = TRUE;
+ return NULL;
+ }
+ pos++;
+
+ if (*pos != LEAP_VERSION) {
+ wpa_printf(MSG_WARNING, "EAP-LEAP: Unsupported LEAP version "
+ "%d", *pos);
+ ret->ignore = TRUE;
+ return NULL;
+ }
+ pos++;
+
+ pos++; /* skip unused byte */
+
+ challenge_len = *pos++;
+ if (challenge_len != LEAP_CHALLENGE_LEN ||
+ challenge_len > reqDataLen - sizeof(*req) - 4) {
+ wpa_printf(MSG_INFO, "EAP-LEAP: Invalid challenge "
+ "(challenge_len=%d reqDataLen=%lu",
+ challenge_len, (unsigned long) reqDataLen);
+ ret->ignore = TRUE;
+ return NULL;
+ }
+ challenge = pos;
+ memcpy(data->peer_challenge, challenge, LEAP_CHALLENGE_LEN);
+ wpa_hexdump(MSG_MSGDUMP, "EAP-LEAP: Challenge from AP",
+ challenge, LEAP_CHALLENGE_LEN);
+
+ wpa_printf(MSG_DEBUG, "EAP-LEAP: Generating Challenge Response");
+
+ *respDataLen = sizeof(struct eap_hdr) + 1 + 3 + LEAP_RESPONSE_LEN +
+ config->identity_len;
+ resp = malloc(*respDataLen);
+ if (resp == NULL)
+ return NULL;
+ resp->code = EAP_CODE_RESPONSE;
+ resp->identifier = req->identifier;
+ resp->length = host_to_be16(*respDataLen);
+ pos = (u8 *) (resp + 1);
+ *pos++ = EAP_TYPE_LEAP;
+ *pos++ = LEAP_VERSION;
+ *pos++ = 0; /* unused */
+ *pos++ = LEAP_RESPONSE_LEN;
+ nt_challenge_response(challenge,
+ config->password, config->password_len, pos);
+ memcpy(data->peer_response, pos, LEAP_RESPONSE_LEN);
+ wpa_hexdump(MSG_MSGDUMP, "EAP-LEAP: Response", pos, LEAP_RESPONSE_LEN);
+ pos += LEAP_RESPONSE_LEN;
+ memcpy(pos, config->identity, config->identity_len);
+
+ data->state = LEAP_WAIT_SUCCESS;
+
+ return (u8 *) resp;
+}
+
+
+static u8 * eap_leap_process_success(struct eap_sm *sm, void *priv,
+ struct eap_method_ret *ret,
+ u8 *reqData, size_t reqDataLen,
+ size_t *respDataLen)
+{
+ struct eap_leap_data *data = priv;
+ struct wpa_ssid *config = eap_get_config(sm);
+ struct eap_hdr *req, *resp;
+ u8 *pos;
+
+ wpa_printf(MSG_DEBUG, "EAP-LEAP: Processing EAP-Success");
+
+ if (data->state != LEAP_WAIT_SUCCESS) {
+ wpa_printf(MSG_INFO, "EAP-LEAP: EAP-Success received in "
+ "unexpected state (%d) - ignored", data->state);
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ req = (struct eap_hdr *) reqData;
+
+ *respDataLen = sizeof(struct eap_hdr) + 1 + 3 + LEAP_CHALLENGE_LEN +
+ config->identity_len;
+ resp = malloc(*respDataLen);
+ if (resp == NULL)
+ return NULL;
+ resp->code = EAP_CODE_REQUEST;
+ resp->identifier = req->identifier;
+ resp->length = host_to_be16(*respDataLen);
+ pos = (u8 *) (resp + 1);
+ *pos++ = EAP_TYPE_LEAP;
+ *pos++ = LEAP_VERSION;
+ *pos++ = 0; /* unused */
+ *pos++ = LEAP_CHALLENGE_LEN;
+ if (hostapd_get_rand(pos, LEAP_CHALLENGE_LEN)) {
+ wpa_printf(MSG_WARNING, "EAP-LEAP: Failed to read random data "
+ "for challenge");
+ free(resp);
+ ret->ignore = TRUE;
+ return NULL;
+ }
+ memcpy(data->ap_challenge, pos, LEAP_CHALLENGE_LEN);
+ wpa_hexdump(MSG_MSGDUMP, "EAP-LEAP: Challenge to AP/AS", pos,
+ LEAP_CHALLENGE_LEN);
+ pos += LEAP_CHALLENGE_LEN;
+ memcpy(pos, config->identity, config->identity_len);
+
+ data->state = LEAP_WAIT_RESPONSE;
+
+ return (u8 *) resp;
+}
+
+
+static u8 * eap_leap_process_response(struct eap_sm *sm, void *priv,
+ struct eap_method_ret *ret,
+ u8 *reqData, size_t reqDataLen,
+ size_t *respDataLen)
+{
+ struct eap_leap_data *data = priv;
+ struct wpa_ssid *config = eap_get_config(sm);
+ struct eap_hdr *resp;
+ u8 *pos, response_len, pw_hash[16], pw_hash_hash[16],
+ expected[LEAP_RESPONSE_LEN];
+
+ wpa_printf(MSG_DEBUG, "EAP-LEAP: Processing EAP-Response");
+
+ resp = (struct eap_hdr *) reqData;
+ pos = (u8 *) (resp + 1);
+ if (reqDataLen < sizeof(*resp) + 4 || *pos != EAP_TYPE_LEAP) {
+ wpa_printf(MSG_INFO, "EAP-LEAP: Invalid EAP-Response frame");
+ ret->ignore = TRUE;
+ return NULL;
+ }
+ pos++;
+
+ if (*pos != LEAP_VERSION) {
+ wpa_printf(MSG_WARNING, "EAP-LEAP: Unsupported LEAP version "
+ "%d", *pos);
+ ret->ignore = TRUE;
+ return NULL;
+ }
+ pos++;
+
+ pos++; /* skip unused byte */
+
+ response_len = *pos++;
+ if (response_len != LEAP_RESPONSE_LEN ||
+ response_len > reqDataLen - sizeof(*resp) - 4) {
+ wpa_printf(MSG_INFO, "EAP-LEAP: Invalid response "
+ "(response_len=%d reqDataLen=%lu",
+ response_len, (unsigned long) reqDataLen);
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ wpa_hexdump(MSG_DEBUG, "EAP-LEAP: Response from AP",
+ pos, LEAP_RESPONSE_LEN);
+ memcpy(data->ap_response, pos, LEAP_RESPONSE_LEN);
+
+ nt_password_hash(config->password, config->password_len, pw_hash);
+ hash_nt_password_hash(pw_hash, pw_hash_hash);
+ challenge_response(data->ap_challenge, pw_hash_hash, expected);
+
+ ret->methodState = METHOD_DONE;
+ ret->allowNotifications = FALSE;
+
+ if (memcmp(pos, expected, LEAP_RESPONSE_LEN) != 0) {
+ wpa_printf(MSG_WARNING, "EAP-LEAP: AP sent an invalid "
+ "response - authentication failed");
+ wpa_hexdump(MSG_DEBUG, "EAP-LEAP: Expected response from AP",
+ expected, LEAP_RESPONSE_LEN);
+ ret->decision = DECISION_FAIL;
+ return NULL;
+ }
+
+ ret->decision = DECISION_UNCOND_SUCC;
+
+ /* LEAP is somewhat odd method since it sends EAP-Success in the middle
+ * of the authentication. Use special variable to transit EAP state
+ * machine to SUCCESS state. */
+ sm->leap_done = TRUE;
+ data->state = LEAP_DONE;
+
+ /* No more authentication messages expected; AP will send EAPOL-Key
+ * frames if encryption is enabled. */
+ return NULL;
+}
+
+
+static u8 * eap_leap_process(struct eap_sm *sm, void *priv,
+ struct eap_method_ret *ret,
+ u8 *reqData, size_t reqDataLen,
+ size_t *respDataLen)
+{
+ struct wpa_ssid *config = eap_get_config(sm);
+ struct eap_hdr *eap;
+ size_t len;
+
+ if (config == NULL || config->password == NULL) {
+ wpa_printf(MSG_INFO, "EAP-LEAP: Password not configured");
+ eap_sm_request_password(sm, config);
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ eap = (struct eap_hdr *) reqData;
+
+ if (reqDataLen < sizeof(*eap) ||
+ (len = be_to_host16(eap->length)) > reqDataLen) {
+ wpa_printf(MSG_INFO, "EAP-LEAP: Invalid frame");
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ ret->ignore = FALSE;
+ ret->allowNotifications = TRUE;
+ ret->methodState = METHOD_CONT;
+ ret->decision = DECISION_FAIL;
+
+ sm->leap_done = FALSE;
+
+ switch (eap->code) {
+ case EAP_CODE_REQUEST:
+ return eap_leap_process_request(sm, priv, ret, reqData, len,
+ respDataLen);
+ case EAP_CODE_SUCCESS:
+ return eap_leap_process_success(sm, priv, ret, reqData, len,
+ respDataLen);
+ case EAP_CODE_RESPONSE:
+ return eap_leap_process_response(sm, priv, ret, reqData, len,
+ respDataLen);
+ default:
+ wpa_printf(MSG_INFO, "EAP-LEAP: Unexpected EAP code (%d) - "
+ "ignored", eap->code);
+ ret->ignore = TRUE;
+ return NULL;
+ }
+}
+
+
+static Boolean eap_leap_isKeyAvailable(struct eap_sm *sm, void *priv)
+{
+ struct eap_leap_data *data = priv;
+ return data->state == LEAP_DONE;
+}
+
+
+static u8 * eap_leap_getKey(struct eap_sm *sm, void *priv, size_t *len)
+{
+ struct eap_leap_data *data = priv;
+ struct wpa_ssid *config = eap_get_config(sm);
+ u8 *key, pw_hash_hash[16], pw_hash[16];
+ MD5_CTX context;
+
+ if (data->state != LEAP_DONE)
+ return NULL;
+
+ key = malloc(LEAP_KEY_LEN);
+ if (key == NULL)
+ return NULL;
+
+ nt_password_hash(config->password, config->password_len, pw_hash);
+ hash_nt_password_hash(pw_hash, pw_hash_hash);
+ wpa_hexdump_key(MSG_DEBUG, "EAP-LEAP: pw_hash_hash",
+ pw_hash_hash, 16);
+ wpa_hexdump(MSG_DEBUG, "EAP-LEAP: peer_challenge",
+ data->peer_challenge, LEAP_CHALLENGE_LEN);
+ wpa_hexdump(MSG_DEBUG, "EAP-LEAP: peer_response",
+ data->peer_response, LEAP_RESPONSE_LEN);
+ wpa_hexdump(MSG_DEBUG, "EAP-LEAP: ap_challenge",
+ data->ap_challenge, LEAP_CHALLENGE_LEN);
+ wpa_hexdump(MSG_DEBUG, "EAP-LEAP: ap_response",
+ data->ap_response, LEAP_RESPONSE_LEN);
+
+ MD5Init(&context);
+ MD5Update(&context, pw_hash_hash, 16);
+ MD5Update(&context, data->ap_challenge, LEAP_CHALLENGE_LEN);
+ MD5Update(&context, data->ap_response, LEAP_RESPONSE_LEN);
+ MD5Update(&context, data->peer_challenge, LEAP_CHALLENGE_LEN);
+ MD5Update(&context, data->peer_response, LEAP_RESPONSE_LEN);
+ MD5Final(key, &context);
+ wpa_hexdump_key(MSG_DEBUG, "EAP-LEAP: master key", key, LEAP_KEY_LEN);
+ *len = LEAP_KEY_LEN;
+
+ return key;
+}
+
+
+const struct eap_method eap_method_leap =
+{
+ .method = EAP_TYPE_LEAP,
+ .name = "LEAP",
+ .init = eap_leap_init,
+ .deinit = eap_leap_deinit,
+ .process = eap_leap_process,
+ .isKeyAvailable = eap_leap_isKeyAvailable,
+ .getKey = eap_leap_getKey,
+};
diff --git a/contrib/wpa_supplicant/eap_md5.c b/contrib/wpa_supplicant/eap_md5.c
new file mode 100644
index 000000000000..bcb5558be97e
--- /dev/null
+++ b/contrib/wpa_supplicant/eap_md5.c
@@ -0,0 +1,112 @@
+/*
+ * WPA Supplicant / EAP-MD5
+ * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published 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 <stdlib.h>
+#include <stdio.h>
+
+#include "common.h"
+#include "eap_i.h"
+#include "wpa_supplicant.h"
+#include "config_ssid.h"
+#include "md5.h"
+
+
+static void * eap_md5_init(struct eap_sm *sm)
+{
+ return (void *) 1;
+}
+
+
+static void eap_md5_deinit(struct eap_sm *sm, void *priv)
+{
+}
+
+
+static u8 * eap_md5_process(struct eap_sm *sm, void *priv,
+ struct eap_method_ret *ret,
+ u8 *reqData, size_t reqDataLen,
+ size_t *respDataLen)
+{
+ struct wpa_ssid *config = eap_get_config(sm);
+ struct eap_hdr *req, *resp;
+ u8 *pos, *challenge;
+ int challenge_len;
+ MD5_CTX context;
+ size_t len;
+
+ if (config == NULL || config->password == NULL) {
+ wpa_printf(MSG_INFO, "EAP-MD5: Password not configured");
+ eap_sm_request_password(sm, config);
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ req = (struct eap_hdr *) reqData;
+ pos = (u8 *) (req + 1);
+ if (reqDataLen < sizeof(*req) + 2 || *pos != EAP_TYPE_MD5 ||
+ (len = be_to_host16(req->length)) > reqDataLen) {
+ wpa_printf(MSG_INFO, "EAP-MD5: Invalid frame");
+ ret->ignore = TRUE;
+ return NULL;
+ }
+ pos++;
+ challenge_len = *pos++;
+ if (challenge_len == 0 ||
+ challenge_len > len - sizeof(*req) - 2) {
+ wpa_printf(MSG_INFO, "EAP-MD5: Invalid challenge "
+ "(challenge_len=%d len=%lu",
+ challenge_len, (unsigned long) len);
+ ret->ignore = TRUE;
+ return NULL;
+ }
+ ret->ignore = FALSE;
+ challenge = pos;
+ wpa_hexdump(MSG_MSGDUMP, "EAP-MD5: Challenge",
+ challenge, challenge_len);
+
+ wpa_printf(MSG_DEBUG, "EAP-MD5: generating Challenge Response");
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_UNCOND_SUCC;
+ ret->allowNotifications = TRUE;
+
+ *respDataLen = sizeof(struct eap_hdr) + 1 + 1 + MD5_MAC_LEN;
+ resp = malloc(*respDataLen);
+ if (resp == NULL)
+ return NULL;
+ resp->code = EAP_CODE_RESPONSE;
+ resp->identifier = req->identifier;
+ resp->length = host_to_be16(*respDataLen);
+ pos = (u8 *) (resp + 1);
+ *pos++ = EAP_TYPE_MD5;
+ *pos++ = MD5_MAC_LEN; /* Value-Size */
+
+ MD5Init(&context);
+ MD5Update(&context, &resp->identifier, 1);
+ MD5Update(&context, config->password, config->password_len);
+ MD5Update(&context, challenge, challenge_len);
+ MD5Final(pos, &context);
+ wpa_hexdump(MSG_MSGDUMP, "EAP-MD5: Response", pos, MD5_MAC_LEN);
+
+ return (u8 *) resp;
+}
+
+
+const struct eap_method eap_method_md5 =
+{
+ .method = EAP_TYPE_MD5,
+ .name = "MD5",
+ .init = eap_md5_init,
+ .deinit = eap_md5_deinit,
+ .process = eap_md5_process,
+};
diff --git a/contrib/wpa_supplicant/eap_mschapv2.c b/contrib/wpa_supplicant/eap_mschapv2.c
new file mode 100644
index 000000000000..a39a5d330a78
--- /dev/null
+++ b/contrib/wpa_supplicant/eap_mschapv2.c
@@ -0,0 +1,564 @@
+/*
+ * WPA Supplicant / EAP-MSCHAPV2 (draft-kamath-pppext-eap-mschapv2-00.txt)
+ * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published 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 <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "common.h"
+#include "eap_i.h"
+#include "wpa_supplicant.h"
+#include "config_ssid.h"
+#include "ms_funcs.h"
+
+
+struct eap_mschapv2_hdr {
+ u8 code;
+ u8 identifier;
+ u16 length; /* including code, identifier, and length */
+ u8 type; /* EAP_TYPE_MSCHAPV2 */
+ u8 op_code; /* MSCHAPV2_OP_* */
+ u8 mschapv2_id; /* usually same as identifier */
+ u8 ms_length[2]; /* Note: misaligned; length - 5 */
+ /* followed by data */
+} __attribute__ ((packed));
+
+#define MSCHAPV2_OP_CHALLENGE 1
+#define MSCHAPV2_OP_RESPONSE 2
+#define MSCHAPV2_OP_SUCCESS 3
+#define MSCHAPV2_OP_FAILURE 4
+#define MSCHAPV2_OP_CHANGE_PASSWORD 7
+
+#define MSCHAPV2_RESP_LEN 49
+
+#define ERROR_RESTRICTED_LOGON_HOURS 646
+#define ERROR_ACCT_DISABLED 647
+#define ERROR_PASSWD_EXPIRED 648
+#define ERROR_NO_DIALIN_PERMISSION 649
+#define ERROR_AUTHENTICATION_FAILURE 691
+#define ERROR_CHANGING_PASSWORD 709
+
+#define PASSWD_CHANGE_CHAL_LEN 16
+#define MSCHAPV2_KEY_LEN 16
+
+
+struct eap_mschapv2_data {
+ u8 auth_response[20];
+ int auth_response_valid;
+
+ int prev_error;
+ u8 passwd_change_challenge[PASSWD_CHANGE_CHAL_LEN];
+ int passwd_change_challenge_valid;
+ int passwd_change_version;
+
+ /* Optional challenge values generated in EAP-FAST Phase 1 negotiation
+ */
+ u8 *peer_challenge;
+ u8 *auth_challenge;
+
+ int phase2;
+ u8 master_key[16];
+ int master_key_valid;
+ int success;
+};
+
+
+static void eap_mschapv2_deinit(struct eap_sm *sm, void *priv);
+
+
+static void * eap_mschapv2_init(struct eap_sm *sm)
+{
+ struct eap_mschapv2_data *data;
+ data = malloc(sizeof(*data));
+ if (data == NULL)
+ return NULL;
+ memset(data, 0, sizeof(*data));
+
+ if (sm->peer_challenge) {
+ data->peer_challenge = malloc(16);
+ if (data->peer_challenge == NULL) {
+ eap_mschapv2_deinit(sm, data);
+ return NULL;
+ }
+ memcpy(data->peer_challenge, sm->peer_challenge, 16);
+ }
+
+ if (sm->auth_challenge) {
+ data->auth_challenge = malloc(16);
+ if (data->auth_challenge == NULL) {
+ eap_mschapv2_deinit(sm, data);
+ return NULL;
+ }
+ memcpy(data->auth_challenge, sm->auth_challenge, 16);
+ }
+
+ data->phase2 = sm->init_phase2;
+
+ return data;
+}
+
+
+static void eap_mschapv2_deinit(struct eap_sm *sm, void *priv)
+{
+ struct eap_mschapv2_data *data = priv;
+ free(data->peer_challenge);
+ free(data->auth_challenge);
+ free(data);
+}
+
+
+static u8 * eap_mschapv2_challenge(struct eap_sm *sm,
+ struct eap_mschapv2_data *data,
+ struct eap_method_ret *ret,
+ struct eap_mschapv2_hdr *req,
+ size_t *respDataLen)
+{
+ struct wpa_ssid *config = eap_get_config(sm);
+ u8 *challenge, *peer_challenge, *username, *pos;
+ int challenge_len, i, ms_len;
+ size_t len, username_len;
+ struct eap_mschapv2_hdr *resp;
+ u8 password_hash[16], password_hash_hash[16];
+
+ /* MSCHAPv2 does not include optional domain name in the
+ * challenge-response calculation, so remove domain prefix
+ * (if present). */
+ username = config->identity;
+ username_len = config->identity_len;
+ for (i = 0; i < username_len; i++) {
+ if (username[i] == '\\') {
+ username_len -= i + 1;
+ username += i + 1;
+ break;
+ }
+ }
+
+ wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received challenge");
+ len = be_to_host16(req->length);
+ pos = (u8 *) (req + 1);
+ challenge_len = *pos++;
+ if (challenge_len != 16) {
+ wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Invalid challenge length "
+ "%d", challenge_len);
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ if (len - challenge_len - 10 < 0) {
+ wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Too short challenge"
+ " packet: len=%lu challenge_len=%d",
+ (unsigned long) len, challenge_len);
+ }
+
+ challenge = pos;
+ pos += challenge_len;
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Authentication Servername",
+ pos, len - challenge_len - 10);
+
+ ret->ignore = FALSE;
+ ret->methodState = METHOD_CONT;
+ ret->decision = DECISION_FAIL;
+ ret->allowNotifications = TRUE;
+
+ wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Generating Challenge Response");
+
+ *respDataLen = sizeof(*resp) + 1 + MSCHAPV2_RESP_LEN +
+ config->identity_len;
+ resp = malloc(*respDataLen);
+ if (resp == NULL)
+ return NULL;
+ memset(resp, 0, *respDataLen);
+ resp->code = EAP_CODE_RESPONSE;
+ resp->identifier = req->identifier;
+ resp->length = host_to_be16(*respDataLen);
+ resp->type = EAP_TYPE_MSCHAPV2;
+ resp->op_code = MSCHAPV2_OP_RESPONSE;
+ resp->mschapv2_id = req->mschapv2_id;
+ ms_len = *respDataLen - 5;
+ resp->ms_length[0] = ms_len >> 8;
+ resp->ms_length[1] = ms_len & 0xff;
+ pos = (u8 *) (resp + 1);
+ *pos++ = MSCHAPV2_RESP_LEN; /* Value-Size */
+
+ /* Response */
+ peer_challenge = pos;
+ if (data->peer_challenge) {
+ wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: peer_challenge generated "
+ "in Phase 1");
+ peer_challenge = data->peer_challenge;
+ } else if (hostapd_get_rand(peer_challenge, 16)) {
+ free(resp);
+ return NULL;
+ }
+ pos += 16;
+ pos += 8; /* Reserved, must be zero */
+ if (data->auth_challenge) {
+ wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: auth_challenge generated "
+ "in Phase 1");
+ challenge = data->auth_challenge;
+ }
+ wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: auth_challenge", challenge, 16);
+ wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: peer_challenge",
+ peer_challenge, 16);
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: username",
+ username, username_len);
+ wpa_hexdump_ascii_key(MSG_DEBUG, "EAP-MSCHAPV2: password",
+ config->password, config->password_len);
+ generate_nt_response(challenge, peer_challenge,
+ username, username_len,
+ config->password, config->password_len,
+ pos);
+ wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: response", pos, 24);
+ /* Authenticator response is not really needed yet, but calculate it
+ * here so that challenges need not be saved. */
+ generate_authenticator_response(config->password, config->password_len,
+ peer_challenge, challenge,
+ username, username_len, pos,
+ data->auth_response);
+ data->auth_response_valid = 1;
+
+ /* Likewise, generate master_key here since we have the needed data
+ * available. */
+ nt_password_hash(config->password, config->password_len,
+ password_hash);
+ hash_nt_password_hash(password_hash, password_hash_hash);
+ get_master_key(password_hash_hash, pos /* nt_response */,
+ data->master_key);
+ data->master_key_valid = 1;
+
+ pos += 24;
+ pos++; /* Flag / reserved, must be zero */
+
+ memcpy(pos, config->identity, config->identity_len);
+ return (u8 *) resp;
+}
+
+
+static u8 * eap_mschapv2_success(struct eap_sm *sm,
+ struct eap_mschapv2_data *data,
+ struct eap_method_ret *ret,
+ struct eap_mschapv2_hdr *req,
+ size_t *respDataLen)
+{
+ struct eap_mschapv2_hdr *resp;
+ u8 *pos, recv_response[20];
+ int len, left;
+
+ wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received success");
+ len = be_to_host16(req->length);
+ pos = (u8 *) (req + 1);
+ if (!data->auth_response_valid || len < sizeof(*req) + 42 ||
+ pos[0] != 'S' || pos[1] != '=' ||
+ hexstr2bin((char *) (pos + 2), recv_response, 20) ||
+ memcmp(data->auth_response, recv_response, 20) != 0) {
+ wpa_printf(MSG_WARNING, "EAP-MSCHAPV2: Invalid authenticator "
+ "response in success request");
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_FAIL;
+ return NULL;
+ }
+ pos += 42;
+ left = len - sizeof(*req) - 42;
+ while (left > 0 && *pos == ' ') {
+ pos++;
+ left--;
+ }
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Success message",
+ pos, left);
+ wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Authentication succeeded");
+ *respDataLen = 6;
+ resp = malloc(6);
+ if (resp == NULL) {
+ wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Failed to allocate "
+ "buffer for success response");
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ resp->code = EAP_CODE_RESPONSE;
+ resp->identifier = req->identifier;
+ resp->length = host_to_be16(6);
+ resp->type = EAP_TYPE_MSCHAPV2;
+ resp->op_code = MSCHAPV2_OP_SUCCESS;
+
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_UNCOND_SUCC;
+ ret->allowNotifications = FALSE;
+ data->success = 1;
+
+ return (u8 *) resp;
+}
+
+
+static int eap_mschapv2_failure_txt(struct eap_sm *sm,
+ struct eap_mschapv2_data *data, char *txt)
+{
+ char *pos, *msg = "";
+ int retry = 1;
+ struct wpa_ssid *config = eap_get_config(sm);
+
+ /* For example:
+ * E=691 R=1 C=<32 octets hex challenge> V=3 M=Authentication Failure
+ */
+
+ pos = txt;
+
+ if (pos && strncmp(pos, "E=", 2) == 0) {
+ pos += 2;
+ data->prev_error = atoi(pos);
+ wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: error %d",
+ data->prev_error);
+ pos = strchr(pos, ' ');
+ if (pos)
+ pos++;
+ }
+
+ if (pos && strncmp(pos, "R=", 2) == 0) {
+ pos += 2;
+ retry = atoi(pos);
+ wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: retry is %sallowed",
+ retry == 1 ? "" : "not ");
+ pos = strchr(pos, ' ');
+ if (pos)
+ pos++;
+ }
+
+ if (pos && strncmp(pos, "C=", 2) == 0) {
+ int hex_len;
+ pos += 2;
+ hex_len = strchr(pos, ' ') - (char *) pos;
+ if (hex_len == PASSWD_CHANGE_CHAL_LEN * 2) {
+ if (hexstr2bin(pos, data->passwd_change_challenge,
+ PASSWD_CHANGE_CHAL_LEN)) {
+ wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: invalid "
+ "failure challenge");
+ } else {
+ wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: failure "
+ "challenge",
+ data->passwd_change_challenge,
+ PASSWD_CHANGE_CHAL_LEN);
+ data->passwd_change_challenge_valid = 1;
+ }
+ } else {
+ wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: invalid failure "
+ "challenge len %d", hex_len);
+ }
+ pos = strchr(pos, ' ');
+ if (pos)
+ pos++;
+ } else {
+ wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: required challenge field "
+ "was not present in failure message");
+ }
+
+ if (pos && strncmp(pos, "V=", 2) == 0) {
+ pos += 2;
+ data->passwd_change_version = atoi(pos);
+ wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: password changing "
+ "protocol version %d", data->passwd_change_version);
+ pos = strchr(pos, ' ');
+ if (pos)
+ pos++;
+ }
+
+ if (pos && strncmp(pos, "M=", 2) == 0) {
+ pos += 2;
+ msg = pos;
+ }
+ wpa_msg(sm->msg_ctx, MSG_WARNING,
+ "EAP-MSCHAPV2: failure message: '%s' (retry %sallowed, error "
+ "%d)",
+ msg, retry == 1 ? "" : "not ", data->prev_error);
+ if (retry == 1 && config) {
+ /* TODO: could prevent the current password from being used
+ * again at least for some period of time */
+ eap_sm_request_identity(sm, config);
+ eap_sm_request_password(sm, config);
+ } else if (config) {
+ /* TODO: prevent retries using same username/password */
+ }
+
+ return retry == 1;
+}
+
+
+static u8 * eap_mschapv2_failure(struct eap_sm *sm,
+ struct eap_mschapv2_data *data,
+ struct eap_method_ret *ret,
+ struct eap_mschapv2_hdr *req,
+ size_t *respDataLen)
+{
+ struct eap_mschapv2_hdr *resp;
+ u8 *msdata = (u8 *) (req + 1);
+ char *buf;
+ int len = be_to_host16(req->length) - sizeof(*req);
+ int retry = 0;
+
+ wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received failure");
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Failure data",
+ msdata, len);
+ buf = malloc(len + 1);
+ if (buf) {
+ memcpy(buf, msdata, len);
+ buf[len] = '\0';
+ retry = eap_mschapv2_failure_txt(sm, data, buf);
+ free(buf);
+ }
+
+ ret->ignore = FALSE;
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_FAIL;
+ ret->allowNotifications = FALSE;
+
+ if (retry) {
+ /* TODO: could try to retry authentication, e.g, after having
+ * changed the username/password. In this case, EAP MS-CHAP-v2
+ * Failure Response would not be sent here. */
+ }
+
+ *respDataLen = 6;
+ resp = malloc(6);
+ if (resp == NULL) {
+ return NULL;
+ }
+
+ resp->code = EAP_CODE_RESPONSE;
+ resp->identifier = req->identifier;
+ resp->length = host_to_be16(6);
+ resp->type = EAP_TYPE_MSCHAPV2;
+ resp->op_code = MSCHAPV2_OP_FAILURE;
+
+ return (u8 *) resp;
+}
+
+
+static u8 * eap_mschapv2_process(struct eap_sm *sm, void *priv,
+ struct eap_method_ret *ret,
+ u8 *reqData, size_t reqDataLen,
+ size_t *respDataLen)
+{
+ struct eap_mschapv2_data *data = priv;
+ struct wpa_ssid *config = eap_get_config(sm);
+ struct eap_mschapv2_hdr *req;
+ int ms_len, len;
+
+ if (config == NULL || config->identity == NULL) {
+ wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Identity not configured");
+ eap_sm_request_identity(sm, config);
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ if (config->password == NULL) {
+ wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Password not configured");
+ eap_sm_request_password(sm, config);
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ req = (struct eap_mschapv2_hdr *) reqData;
+ len = be_to_host16(req->length);
+ if (len < sizeof(*req) + 2 || req->type != EAP_TYPE_MSCHAPV2) {
+ wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Invalid frame");
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ ms_len = ((int) req->ms_length[0] << 8) | req->ms_length[1];
+ if (ms_len != len - 5) {
+ wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Invalid header: len=%d "
+ "ms_len=%d", len, ms_len);
+ if (sm->workaround) {
+ /* Some authentication servers use invalid ms_len,
+ * ignore it for interoperability. */
+ wpa_printf(MSG_INFO, "EAP-MSCHAPV2: workaround, ignore"
+ " invalid ms_len");
+ } else {
+ ret->ignore = TRUE;
+ return NULL;
+ }
+ }
+
+ switch (req->op_code) {
+ case MSCHAPV2_OP_CHALLENGE:
+ return eap_mschapv2_challenge(sm, data, ret, req, respDataLen);
+ case MSCHAPV2_OP_SUCCESS:
+ return eap_mschapv2_success(sm, data, ret, req, respDataLen);
+ case MSCHAPV2_OP_FAILURE:
+ return eap_mschapv2_failure(sm, data, ret, req, respDataLen);
+ default:
+ wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Unknown op %d - ignored",
+ req->op_code);
+ ret->ignore = TRUE;
+ return NULL;
+ }
+}
+
+
+static Boolean eap_mschapv2_isKeyAvailable(struct eap_sm *sm, void *priv)
+{
+ struct eap_mschapv2_data *data = priv;
+ return data->success && data->master_key_valid;
+}
+
+
+static u8 * eap_mschapv2_getKey(struct eap_sm *sm, void *priv, size_t *len)
+{
+ struct eap_mschapv2_data *data = priv;
+ u8 *key;
+ int key_len;
+
+ if (!data->master_key_valid || !data->success)
+ return NULL;
+
+ if (data->peer_challenge) {
+ /* EAP-FAST needs both send and receive keys */
+ key_len = 2 * MSCHAPV2_KEY_LEN;
+ } else {
+ key_len = MSCHAPV2_KEY_LEN;
+ }
+
+ key = malloc(key_len);
+ if (key == NULL)
+ return NULL;
+
+ if (data->peer_challenge) {
+ get_asymetric_start_key(data->master_key, key,
+ MSCHAPV2_KEY_LEN, 0, 0);
+ get_asymetric_start_key(data->master_key,
+ key + MSCHAPV2_KEY_LEN,
+ MSCHAPV2_KEY_LEN, 1, 0);
+ } else {
+ get_asymetric_start_key(data->master_key, key,
+ MSCHAPV2_KEY_LEN, 1, 0);
+ }
+
+ wpa_hexdump_key(MSG_DEBUG, "EAP-MSCHAPV2: Derived key",
+ key, key_len);
+
+ *len = key_len;
+ return key;
+}
+
+
+const struct eap_method eap_method_mschapv2 =
+{
+ .method = EAP_TYPE_MSCHAPV2,
+ .name = "MSCHAPV2",
+ .init = eap_mschapv2_init,
+ .deinit = eap_mschapv2_deinit,
+ .process = eap_mschapv2_process,
+ .isKeyAvailable = eap_mschapv2_isKeyAvailable,
+ .getKey = eap_mschapv2_getKey,
+};
diff --git a/contrib/wpa_supplicant/eap_otp.c b/contrib/wpa_supplicant/eap_otp.c
new file mode 100644
index 000000000000..e50de636accd
--- /dev/null
+++ b/contrib/wpa_supplicant/eap_otp.c
@@ -0,0 +1,113 @@
+/*
+ * WPA Supplicant / EAP-OTP (RFC 3748)
+ * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published 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 <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "common.h"
+#include "eap_i.h"
+#include "wpa_supplicant.h"
+#include "config_ssid.h"
+
+
+static void * eap_otp_init(struct eap_sm *sm)
+{
+ return (void *) 1;
+}
+
+
+static void eap_otp_deinit(struct eap_sm *sm, void *priv)
+{
+}
+
+
+static u8 * eap_otp_process(struct eap_sm *sm, void *priv,
+ struct eap_method_ret *ret,
+ u8 *reqData, size_t reqDataLen,
+ size_t *respDataLen)
+{
+ struct wpa_ssid *config = eap_get_config(sm);
+ struct eap_hdr *req, *resp;
+ u8 *pos, *password;
+ size_t password_len, len;
+
+ req = (struct eap_hdr *) reqData;
+ pos = (u8 *) (req + 1);
+ if (reqDataLen < sizeof(*req) + 1 || *pos != EAP_TYPE_OTP ||
+ (len = be_to_host16(req->length)) > reqDataLen) {
+ wpa_printf(MSG_INFO, "EAP-OTP: Invalid frame");
+ ret->ignore = TRUE;
+ return NULL;
+ }
+ pos++;
+ wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-OTP: Request message",
+ pos, len - sizeof(*req) - 1);
+
+ if (config == NULL ||
+ (config->password == NULL && config->otp == NULL)) {
+ wpa_printf(MSG_INFO, "EAP-OTP: Password not configured");
+ eap_sm_request_otp(sm, config, (char *) pos,
+ len - sizeof(*req) - 1);
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ if (config->otp) {
+ password = config->otp;
+ password_len = config->otp_len;
+ } else {
+ password = config->password;
+ password_len = config->password_len;
+ }
+
+ ret->ignore = FALSE;
+
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_COND_SUCC;
+ ret->allowNotifications = FALSE;
+
+ *respDataLen = sizeof(struct eap_hdr) + 1 + password_len;
+ resp = malloc(*respDataLen);
+ if (resp == NULL)
+ return NULL;
+ resp->code = EAP_CODE_RESPONSE;
+ resp->identifier = req->identifier;
+ resp->length = host_to_be16(*respDataLen);
+ pos = (u8 *) (resp + 1);
+ *pos++ = EAP_TYPE_OTP;
+ memcpy(pos, password, password_len);
+ wpa_hexdump_ascii_key(MSG_MSGDUMP, "EAP-OTP: Response",
+ password, password_len);
+
+ if (config->otp) {
+ wpa_printf(MSG_DEBUG, "EAP-OTP: Forgetting used password");
+ memset(config->otp, 0, config->otp_len);
+ free(config->otp);
+ config->otp = NULL;
+ config->otp_len = 0;
+ }
+
+ return (u8 *) resp;
+}
+
+
+const struct eap_method eap_method_otp =
+{
+ .method = EAP_TYPE_OTP,
+ .name = "OTP",
+ .init = eap_otp_init,
+ .deinit = eap_otp_deinit,
+ .process = eap_otp_process,
+};
diff --git a/contrib/wpa_supplicant/eap_peap.c b/contrib/wpa_supplicant/eap_peap.c
new file mode 100644
index 000000000000..27f0793cb8c1
--- /dev/null
+++ b/contrib/wpa_supplicant/eap_peap.c
@@ -0,0 +1,820 @@
+/*
+ * WPA Supplicant / EAP-PEAP (draft-josefsson-pppext-eap-tls-eap-07.txt)
+ * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published 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 <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "common.h"
+#include "eap_i.h"
+#include "eap_tls_common.h"
+#include "wpa_supplicant.h"
+#include "config_ssid.h"
+#include "tls.h"
+#include "eap_tlv.h"
+
+
+/* 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-07.txt
+ */
+#define EAP_PEAP_VERSION 1
+
+
+static void eap_peap_deinit(struct eap_sm *sm, void *priv);
+
+
+struct eap_peap_data {
+ struct eap_ssl_data ssl;
+
+ int peap_version, force_peap_version, force_new_label;
+
+ const struct eap_method *phase2_method;
+ void *phase2_priv;
+ int phase2_success;
+
+ u8 phase2_type;
+ u8 *phase2_types;
+ size_t num_phase2_types;
+
+ int peap_outer_success; /* 0 = PEAP terminated on Phase 2 inner
+ * EAP-Success
+ * 1 = reply with tunneled EAP-Success to inner
+ * EAP-Success and expect AS to send outer
+ * (unencrypted) EAP-Success after this
+ * 2 = reply with PEAP/TLS ACK to inner
+ * EAP-Success and expect AS to send outer
+ * (unencrypted) EAP-Success after this */
+ int resuming; /* starting a resumed session */
+ u8 *key_data;
+
+ u8 *pending_phase2_req;
+ size_t pending_phase2_req_len;
+};
+
+
+static void * eap_peap_init(struct eap_sm *sm)
+{
+ struct eap_peap_data *data;
+ struct wpa_ssid *config = eap_get_config(sm);
+
+ data = malloc(sizeof(*data));
+ if (data == NULL)
+ return NULL;
+ sm->peap_done = FALSE;
+ memset(data, 0, sizeof(*data));
+ data->peap_version = EAP_PEAP_VERSION;
+ data->force_peap_version = -1;
+ data->peap_outer_success = 2;
+
+ if (config && config->phase1) {
+ char *pos = strstr(config->phase1, "peapver=");
+ if (pos) {
+ data->force_peap_version = atoi(pos + 8);
+ data->peap_version = data->force_peap_version;
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: Forced PEAP version "
+ "%d", data->force_peap_version);
+ }
+
+ if (strstr(config->phase1, "peaplabel=1")) {
+ data->force_new_label = 1;
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: Force new label for "
+ "key derivation");
+ }
+
+ if (strstr(config->phase1, "peap_outer_success=0")) {
+ data->peap_outer_success = 0;
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: terminate "
+ "authentication on tunneled EAP-Success");
+ } else if (strstr(config->phase1, "peap_outer_success=1")) {
+ data->peap_outer_success = 1;
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: send tunneled "
+ "EAP-Success after receiving tunneled "
+ "EAP-Success");
+ } else if (strstr(config->phase1, "peap_outer_success=2")) {
+ data->peap_outer_success = 2;
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: send PEAP/TLS ACK "
+ "after receiving tunneled EAP-Success");
+ }
+ }
+
+ if (config && config->phase2) {
+ char *start, *pos, *buf;
+ u8 method, *methods = NULL, *_methods;
+ size_t num_methods = 0;
+ start = buf = strdup(config->phase2);
+ if (buf == NULL) {
+ eap_peap_deinit(sm, data);
+ return NULL;
+ }
+ while (start && *start != '\0') {
+ pos = strstr(start, "auth=");
+ if (pos == NULL)
+ break;
+ if (start != pos && *(pos - 1) != ' ') {
+ start = pos + 5;
+ continue;
+ }
+
+ start = pos + 5;
+ pos = strchr(start, ' ');
+ if (pos)
+ *pos++ = '\0';
+ method = eap_get_phase2_type(start);
+ if (method == EAP_TYPE_NONE) {
+ wpa_printf(MSG_ERROR, "EAP-PEAP: Unsupported "
+ "Phase2 method '%s'", start);
+ } else {
+ num_methods++;
+ _methods = realloc(methods, num_methods);
+ if (_methods == NULL) {
+ free(methods);
+ eap_peap_deinit(sm, data);
+ return NULL;
+ }
+ methods = _methods;
+ methods[num_methods - 1] = method;
+ }
+
+ start = pos;
+ }
+ free(buf);
+ data->phase2_types = methods;
+ data->num_phase2_types = num_methods;
+ }
+ if (data->phase2_types == NULL) {
+ data->phase2_types =
+ eap_get_phase2_types(config, &data->num_phase2_types);
+ }
+ if (data->phase2_types == NULL) {
+ wpa_printf(MSG_ERROR, "EAP-PEAP: No Phase2 method available");
+ eap_peap_deinit(sm, data);
+ return NULL;
+ }
+ wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Phase2 EAP types",
+ data->phase2_types, data->num_phase2_types);
+ data->phase2_type = EAP_TYPE_NONE;
+
+ if (eap_tls_ssl_init(sm, &data->ssl, config)) {
+ wpa_printf(MSG_INFO, "EAP-PEAP: Failed to initialize SSL.");
+ eap_peap_deinit(sm, data);
+ return NULL;
+ }
+
+ return data;
+}
+
+
+static void eap_peap_deinit(struct eap_sm *sm, void *priv)
+{
+ struct eap_peap_data *data = priv;
+ if (data == NULL)
+ return;
+ if (data->phase2_priv && data->phase2_method)
+ data->phase2_method->deinit(sm, data->phase2_priv);
+ free(data->phase2_types);
+ eap_tls_ssl_deinit(sm, &data->ssl);
+ free(data->key_data);
+ free(data->pending_phase2_req);
+ free(data);
+}
+
+
+static int eap_peap_encrypt(struct eap_sm *sm, struct eap_peap_data *data,
+ int id, u8 *plain, size_t plain_len,
+ u8 **out_data, size_t *out_len)
+{
+ int res;
+ u8 *pos;
+ struct eap_hdr *resp;
+
+ /* TODO: add support for fragmentation, if needed. This will need to
+ * add TLS Message Length field, if the frame is fragmented.
+ * Note: Microsoft IAS did not seem to like TLS Message Length with
+ * PEAP/MSCHAPv2. */
+ resp = malloc(sizeof(struct eap_hdr) + 2 + data->ssl.tls_out_limit);
+ if (resp == NULL)
+ return -1;
+
+ resp->code = EAP_CODE_RESPONSE;
+ resp->identifier = id;
+
+ pos = (u8 *) (resp + 1);
+ *pos++ = EAP_TYPE_PEAP;
+ *pos++ = data->peap_version;
+
+ res = tls_connection_encrypt(sm->ssl_ctx, data->ssl.conn,
+ plain, plain_len,
+ pos, data->ssl.tls_out_limit);
+ if (res < 0) {
+ wpa_printf(MSG_INFO, "EAP-PEAP: Failed to encrypt Phase 2 "
+ "data");
+ free(resp);
+ return -1;
+ }
+
+ *out_len = sizeof(struct eap_hdr) + 2 + res;
+ resp->length = host_to_be16(*out_len);
+ *out_data = (u8 *) resp;
+ return 0;
+}
+
+
+static int eap_peap_phase2_nak(struct eap_sm *sm,
+ struct eap_peap_data *data,
+ struct eap_hdr *hdr,
+ u8 **resp, size_t *resp_len)
+{
+ struct eap_hdr *resp_hdr;
+ u8 *pos = (u8 *) (hdr + 1);
+
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 Request: Nak type=%d", *pos);
+ wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Allowed Phase2 EAP types",
+ data->phase2_types, data->num_phase2_types);
+ *resp_len = sizeof(struct eap_hdr) + 1 + data->num_phase2_types;
+ *resp = malloc(*resp_len);
+ if (*resp == NULL)
+ return -1;
+
+ resp_hdr = (struct eap_hdr *) (*resp);
+ resp_hdr->code = EAP_CODE_RESPONSE;
+ resp_hdr->identifier = hdr->identifier;
+ resp_hdr->length = host_to_be16(*resp_len);
+ pos = (u8 *) (resp_hdr + 1);
+ *pos++ = EAP_TYPE_NAK;
+ memcpy(pos, data->phase2_types, data->num_phase2_types);
+
+ return 0;
+}
+
+
+static int eap_peap_phase2_request(struct eap_sm *sm,
+ struct eap_peap_data *data,
+ struct eap_method_ret *ret,
+ struct eap_hdr *req,
+ struct eap_hdr *hdr,
+ u8 **resp, size_t *resp_len)
+{
+ size_t len = be_to_host16(hdr->length);
+ u8 *pos;
+ struct eap_method_ret iret;
+ struct wpa_ssid *config = eap_get_config(sm);
+
+ if (len <= sizeof(struct eap_hdr)) {
+ wpa_printf(MSG_INFO, "EAP-PEAP: too short "
+ "Phase 2 request (len=%lu)", (unsigned long) len);
+ return -1;
+ }
+ pos = (u8 *) (hdr + 1);
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 Request: type=%d", *pos);
+ switch (*pos) {
+ case EAP_TYPE_IDENTITY:
+ *resp = eap_sm_buildIdentity(sm, req->identifier, resp_len, 1);
+ break;
+ case EAP_TYPE_TLV:
+ memset(&iret, 0, sizeof(iret));
+ if (eap_tlv_process(sm, &iret, hdr, resp, resp_len)) {
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_FAIL;
+ return -1;
+ }
+ if (iret.methodState == METHOD_DONE ||
+ iret.methodState == METHOD_MAY_CONT) {
+ ret->methodState = iret.methodState;
+ ret->decision = iret.decision;
+ data->phase2_success = 1;
+ }
+ break;
+ default:
+ if (data->phase2_type == EAP_TYPE_NONE) {
+ int i;
+ for (i = 0; i < data->num_phase2_types; i++) {
+ if (data->phase2_types[i] != *pos)
+ continue;
+
+ data->phase2_type = *pos;
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: Selected "
+ "Phase 2 EAP method %d",
+ data->phase2_type);
+ break;
+ }
+ }
+ if (*pos != data->phase2_type || *pos == EAP_TYPE_NONE) {
+ if (eap_peap_phase2_nak(sm, data, hdr, resp, resp_len))
+ return -1;
+ return 0;
+ }
+
+ if (data->phase2_priv == NULL) {
+ data->phase2_method = eap_sm_get_eap_methods(*pos);
+ if (data->phase2_method) {
+ sm->init_phase2 = 1;
+ data->phase2_priv =
+ data->phase2_method->init(sm);
+ sm->init_phase2 = 0;
+ }
+ }
+ if (data->phase2_priv == NULL || data->phase2_method == NULL) {
+ wpa_printf(MSG_INFO, "EAP-PEAP: failed to initialize "
+ "Phase 2 EAP method %d", *pos);
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_FAIL;
+ return -1;
+ }
+ memset(&iret, 0, sizeof(iret));
+ *resp = data->phase2_method->process(sm, data->phase2_priv,
+ &iret, (u8 *) hdr, len,
+ resp_len);
+ if ((iret.methodState == METHOD_DONE ||
+ iret.methodState == METHOD_MAY_CONT) &&
+ (iret.decision == DECISION_UNCOND_SUCC ||
+ iret.decision == DECISION_COND_SUCC)) {
+ data->phase2_success = 1;
+ }
+ break;
+ }
+
+ if (*resp == NULL &&
+ (config->pending_req_identity || config->pending_req_password ||
+ config->pending_req_otp)) {
+ free(data->pending_phase2_req);
+ data->pending_phase2_req = malloc(len);
+ if (data->pending_phase2_req) {
+ memcpy(data->pending_phase2_req, hdr, len);
+ data->pending_phase2_req_len = len;
+ }
+ }
+
+ return 0;
+}
+
+
+static int eap_peap_decrypt(struct eap_sm *sm,
+ struct eap_peap_data *data,
+ struct eap_method_ret *ret,
+ struct eap_hdr *req,
+ u8 *in_data, size_t in_len,
+ u8 **out_data, size_t *out_len)
+{
+ u8 *in_decrypted;
+ int buf_len, len_decrypted, len, skip_change = 0, res;
+ struct eap_hdr *hdr, *rhdr;
+ u8 *resp = NULL;
+ size_t resp_len;
+
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: received %lu bytes encrypted data for"
+ " Phase 2", (unsigned long) in_len);
+
+ if (data->pending_phase2_req) {
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: Pending Phase 2 request - "
+ "skip decryption and use old data");
+ in_decrypted = data->pending_phase2_req;
+ data->pending_phase2_req = NULL;
+ len_decrypted = data->pending_phase2_req_len;
+ skip_change = 1;
+ goto continue_req;
+ }
+
+ res = eap_tls_data_reassemble(sm, &data->ssl, &in_data, &in_len);
+ if (res < 0 || res == 1)
+ return res;
+
+ buf_len = in_len;
+ if (data->ssl.tls_in_total > buf_len)
+ buf_len = data->ssl.tls_in_total;
+ in_decrypted = malloc(buf_len);
+ if (in_decrypted == NULL) {
+ free(data->ssl.tls_in);
+ data->ssl.tls_in = NULL;
+ data->ssl.tls_in_len = 0;
+ wpa_printf(MSG_WARNING, "EAP-PEAP: failed to allocate memory "
+ "for decryption");
+ return -1;
+ }
+
+ len_decrypted = tls_connection_decrypt(sm->ssl_ctx, data->ssl.conn,
+ in_data, in_len,
+ in_decrypted, buf_len);
+ free(data->ssl.tls_in);
+ data->ssl.tls_in = NULL;
+ data->ssl.tls_in_len = 0;
+ if (len_decrypted < 0) {
+ wpa_printf(MSG_INFO, "EAP-PEAP: Failed to decrypt Phase 2 "
+ "data");
+ free(in_decrypted);
+ return 0;
+ }
+
+continue_req:
+ wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Decrypted Phase 2 EAP", in_decrypted,
+ len_decrypted);
+
+ hdr = (struct eap_hdr *) in_decrypted;
+ if (len_decrypted == 5 && hdr->code == EAP_CODE_REQUEST &&
+ be_to_host16(hdr->length) == 5 &&
+ in_decrypted[4] == EAP_TYPE_IDENTITY) {
+ /* At least FreeRADIUS seems to send full EAP header with
+ * EAP Request Identity */
+ skip_change = 1;
+ }
+ if (len_decrypted >= 5 && hdr->code == EAP_CODE_REQUEST &&
+ in_decrypted[4] == EAP_TYPE_TLV) {
+ skip_change = 1;
+ }
+
+ if (data->peap_version == 0 && !skip_change) {
+ struct eap_hdr *nhdr = malloc(sizeof(struct eap_hdr) +
+ len_decrypted);
+ if (nhdr == NULL) {
+ free(in_decrypted);
+ return 0;
+ }
+ memcpy((u8 *) (nhdr + 1), in_decrypted, len_decrypted);
+ free(in_decrypted);
+ nhdr->code = req->code;
+ nhdr->identifier = req->identifier;
+ nhdr->length = host_to_be16(sizeof(struct eap_hdr) +
+ len_decrypted);
+
+ len_decrypted += sizeof(struct eap_hdr);
+ in_decrypted = (u8 *) nhdr;
+ }
+ hdr = (struct eap_hdr *) in_decrypted;
+ if (len_decrypted < sizeof(*hdr)) {
+ free(in_decrypted);
+ wpa_printf(MSG_INFO, "EAP-PEAP: Too short Phase 2 "
+ "EAP frame (len=%d)", len_decrypted);
+ return 0;
+ }
+ len = be_to_host16(hdr->length);
+ if (len > len_decrypted) {
+ free(in_decrypted);
+ wpa_printf(MSG_INFO, "EAP-PEAP: Length mismatch in "
+ "Phase 2 EAP frame (len=%d hdr->length=%d)",
+ len_decrypted, len);
+ return 0;
+ }
+ if (len < len_decrypted) {
+ wpa_printf(MSG_INFO, "EAP-PEAP: Odd.. Phase 2 EAP header has "
+ "shorter length than full decrypted data (%d < %d)",
+ len, len_decrypted);
+ if (sm->workaround && len == 4 && len_decrypted == 5 &&
+ in_decrypted[4] == EAP_TYPE_IDENTITY) {
+ /* Radiator 3.9 seems to set Phase 2 EAP header to use
+ * incorrect length for the EAP-Request Identity
+ * packet, so fix the inner header to interoperate..
+ * This was fixed in 2004-06-23 patch for Radiator and
+ * this workaround can be removed at some point. */
+ wpa_printf(MSG_INFO, "EAP-PEAP: workaround -> replace "
+ "Phase 2 EAP header len (%d) with real "
+ "decrypted len (%d)", len, len_decrypted);
+ len = len_decrypted;
+ hdr->length = host_to_be16(len);
+ }
+ }
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: received Phase 2: code=%d "
+ "identifier=%d length=%d", hdr->code, hdr->identifier, len);
+ switch (hdr->code) {
+ case EAP_CODE_REQUEST:
+ if (eap_peap_phase2_request(sm, data, ret, req, hdr,
+ &resp, &resp_len)) {
+ free(in_decrypted);
+ wpa_printf(MSG_INFO, "EAP-PEAP: Phase2 Request "
+ "processing failed");
+ return 0;
+ }
+ break;
+ case EAP_CODE_SUCCESS:
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 Success");
+ if (data->peap_version == 1) {
+ /* EAP-Success within TLS tunnel is used to indicate
+ * shutdown of the TLS channel. The authentication has
+ * been completed. */
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: Version 1 - "
+ "EAP-Success within TLS tunnel - "
+ "authentication completed");
+ ret->decision = DECISION_UNCOND_SUCC;
+ ret->methodState = METHOD_DONE;
+ data->phase2_success = 1;
+ if (data->peap_outer_success == 2) {
+ free(in_decrypted);
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: Use TLS ACK "
+ "to finish authentication");
+ return 1;
+ } else if (data->peap_outer_success == 1) {
+ /* Reply with EAP-Success within the TLS
+ * channel to complete the authentication. */
+ resp_len = sizeof(struct eap_hdr);
+ resp = malloc(resp_len);
+ if (resp) {
+ memset(resp, 0, resp_len);
+ rhdr = (struct eap_hdr *) resp;
+ rhdr->code = EAP_CODE_SUCCESS;
+ rhdr->identifier = hdr->identifier;
+ rhdr->length = host_to_be16(resp_len);
+ }
+ } else {
+ /* No EAP-Success expected for Phase 1 (outer,
+ * unencrypted auth), so force EAP state
+ * machine to SUCCESS state. */
+ sm->peap_done = TRUE;
+ }
+ } else {
+ /* FIX: ? */
+ }
+ break;
+ case EAP_CODE_FAILURE:
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 Failure");
+ ret->decision = DECISION_FAIL;
+ ret->methodState = METHOD_MAY_CONT;
+ ret->allowNotifications = FALSE;
+ /* Reply with EAP-Failure within the TLS channel to complete
+ * failure reporting. */
+ resp_len = sizeof(struct eap_hdr);
+ resp = malloc(resp_len);
+ if (resp) {
+ memset(resp, 0, resp_len);
+ rhdr = (struct eap_hdr *) resp;
+ rhdr->code = EAP_CODE_FAILURE;
+ rhdr->identifier = hdr->identifier;
+ rhdr->length = host_to_be16(resp_len);
+ }
+ break;
+ default:
+ wpa_printf(MSG_INFO, "EAP-PEAP: Unexpected code=%d in "
+ "Phase 2 EAP header", hdr->code);
+ break;
+ }
+
+ free(in_decrypted);
+
+ if (resp) {
+ u8 *resp_pos;
+ size_t resp_send_len;
+ int skip_change = 0;
+
+ wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: Encrypting Phase 2 data",
+ resp, resp_len);
+ /* PEAP version changes */
+ if (resp_len >= 5 && resp[0] == EAP_CODE_RESPONSE &&
+ resp[4] == EAP_TYPE_TLV)
+ skip_change = 1;
+ if (data->peap_version == 0 && !skip_change) {
+ resp_pos = resp + sizeof(struct eap_hdr);
+ resp_send_len = resp_len - sizeof(struct eap_hdr);
+ } else {
+ resp_pos = resp;
+ resp_send_len = resp_len;
+ }
+
+ if (eap_peap_encrypt(sm, data, req->identifier,
+ resp_pos, resp_send_len,
+ out_data, out_len)) {
+ wpa_printf(MSG_INFO, "EAP-PEAP: Failed to encrypt "
+ "a Phase 2 frame");
+ }
+ free(resp);
+ }
+
+ return 0;
+}
+
+
+static u8 * eap_peap_process(struct eap_sm *sm, void *priv,
+ struct eap_method_ret *ret,
+ u8 *reqData, size_t reqDataLen,
+ size_t *respDataLen)
+{
+ struct eap_hdr *req;
+ int left, res;
+ unsigned int tls_msg_len;
+ u8 flags, *pos, *resp, id;
+ struct eap_peap_data *data = priv;
+
+ if (tls_get_errors(sm->ssl_ctx)) {
+ wpa_printf(MSG_INFO, "EAP-PEAP: TLS errors detected");
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ req = (struct eap_hdr *) reqData;
+ pos = (u8 *) (req + 1);
+ if (reqDataLen < sizeof(*req) + 2 || *pos != EAP_TYPE_PEAP ||
+ (left = be_to_host16(req->length)) > reqDataLen) {
+ wpa_printf(MSG_INFO, "EAP-PEAP: Invalid frame");
+ ret->ignore = TRUE;
+ return NULL;
+ }
+ left -= sizeof(struct eap_hdr);
+ id = req->identifier;
+ pos++;
+ flags = *pos++;
+ left -= 2;
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: Received packet(len=%lu) - "
+ "Flags 0x%02x", (unsigned long) reqDataLen, flags);
+ if (flags & EAP_TLS_FLAGS_LENGTH_INCLUDED) {
+ if (left < 4) {
+ wpa_printf(MSG_INFO, "EAP-PEAP: Short frame with TLS "
+ "length");
+ ret->ignore = TRUE;
+ return NULL;
+ }
+ tls_msg_len = (pos[0] << 24) | (pos[1] << 16) | (pos[2] << 8) |
+ pos[3];
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: TLS Message Length: %d",
+ tls_msg_len);
+ if (data->ssl.tls_in_left == 0) {
+ data->ssl.tls_in_total = tls_msg_len;
+ data->ssl.tls_in_left = tls_msg_len;
+ free(data->ssl.tls_in);
+ data->ssl.tls_in = NULL;
+ data->ssl.tls_in_len = 0;
+ }
+ pos += 4;
+ left -= 4;
+ }
+
+ ret->ignore = FALSE;
+ ret->methodState = METHOD_CONT;
+ ret->decision = DECISION_FAIL;
+ ret->allowNotifications = TRUE;
+
+ if (flags & EAP_TLS_FLAGS_START) {
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: Start (server ver=%d, own "
+ "ver=%d)", flags & EAP_PEAP_VERSION_MASK,
+ data->peap_version);
+ if ((flags & EAP_PEAP_VERSION_MASK) < data->peap_version)
+ data->peap_version = flags & EAP_PEAP_VERSION_MASK;
+ if (data->force_peap_version >= 0 &&
+ data->force_peap_version != data->peap_version) {
+ wpa_printf(MSG_WARNING, "EAP-PEAP: Failed to select "
+ "forced PEAP version %d",
+ data->force_peap_version);
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_FAIL;
+ ret->allowNotifications = FALSE;
+ return NULL;
+ }
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: Using PEAP version %d",
+ data->peap_version);
+ left = 0; /* make sure that this frame is empty, even though it
+ * should always be, anyway */
+ }
+
+ resp = NULL;
+ if (tls_connection_established(sm->ssl_ctx, data->ssl.conn) &&
+ !data->resuming) {
+ res = eap_peap_decrypt(sm, data, ret, req, pos, left,
+ &resp, respDataLen);
+ } else {
+ res = eap_tls_process_helper(sm, &data->ssl, EAP_TYPE_PEAP,
+ data->peap_version, id, pos, left,
+ &resp, respDataLen);
+
+ if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) {
+ char *label;
+ wpa_printf(MSG_DEBUG,
+ "EAP-PEAP: TLS done, proceed to Phase 2");
+ free(data->key_data);
+ /* draft-josefsson-ppext-eap-tls-eap-05.txt
+ * specifies that PEAPv1 would use "client PEAP
+ * encryption" as the label. However, most existing
+ * PEAPv1 implementations seem to be using the old
+ * 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)
+ label = "client PEAP encryption";
+ else
+ label = "client EAP encryption";
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: using label '%s' in "
+ "key derivation", label);
+ data->key_data =
+ eap_tls_derive_key(sm, &data->ssl, label,
+ EAP_TLS_KEY_LEN);
+ if (data->key_data) {
+ wpa_hexdump_key(MSG_DEBUG,
+ "EAP-PEAP: Derived key",
+ data->key_data,
+ EAP_TLS_KEY_LEN);
+ } else {
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: Failed to "
+ "derive key");
+ }
+ data->resuming = 0;
+ }
+ }
+
+ if (ret->methodState == METHOD_DONE) {
+ ret->allowNotifications = FALSE;
+ }
+
+ if (res == 1) {
+ return eap_tls_build_ack(&data->ssl, respDataLen, id,
+ EAP_TYPE_PEAP, data->peap_version);
+ }
+
+ return resp;
+}
+
+
+static Boolean eap_peap_has_reauth_data(struct eap_sm *sm, void *priv)
+{
+ struct eap_peap_data *data = priv;
+ return tls_connection_established(sm->ssl_ctx, data->ssl.conn) &&
+ data->phase2_success;
+}
+
+
+static void eap_peap_deinit_for_reauth(struct eap_sm *sm, void *priv)
+{
+ struct eap_peap_data *data = priv;
+ free(data->pending_phase2_req);
+ data->pending_phase2_req = NULL;
+}
+
+
+static void * eap_peap_init_for_reauth(struct eap_sm *sm, void *priv)
+{
+ struct eap_peap_data *data = priv;
+ free(data->key_data);
+ data->key_data = NULL;
+ if (eap_tls_reauth_init(sm, &data->ssl)) {
+ free(data);
+ return NULL;
+ }
+ data->phase2_success = 0;
+ data->resuming = 1;
+ sm->peap_done = FALSE;
+ return priv;
+}
+
+
+static int eap_peap_get_status(struct eap_sm *sm, void *priv, char *buf,
+ size_t buflen, int verbose)
+{
+ struct eap_peap_data *data = priv;
+ int len;
+
+ len = eap_tls_status(sm, &data->ssl, buf, buflen, verbose);
+ if (data->phase2_method) {
+ len += snprintf(buf + len, buflen - len,
+ "EAP-PEAPv%d Phase2 method=%s\n",
+ data->peap_version, data->phase2_method->name);
+ }
+ return len;
+}
+
+
+static Boolean eap_peap_isKeyAvailable(struct eap_sm *sm, void *priv)
+{
+ struct eap_peap_data *data = priv;
+ return data->key_data != NULL && data->phase2_success;
+}
+
+
+static u8 * eap_peap_getKey(struct eap_sm *sm, void *priv, size_t *len)
+{
+ struct eap_peap_data *data = priv;
+ u8 *key;
+
+ if (data->key_data == NULL || !data->phase2_success)
+ return NULL;
+
+ key = malloc(EAP_TLS_KEY_LEN);
+ if (key == NULL)
+ return NULL;
+
+ *len = EAP_TLS_KEY_LEN;
+ memcpy(key, data->key_data, EAP_TLS_KEY_LEN);
+
+ return key;
+}
+
+
+const struct eap_method eap_method_peap =
+{
+ .method = EAP_TYPE_PEAP,
+ .name = "PEAP",
+ .init = eap_peap_init,
+ .deinit = eap_peap_deinit,
+ .process = eap_peap_process,
+ .isKeyAvailable = eap_peap_isKeyAvailable,
+ .getKey = eap_peap_getKey,
+ .get_status = eap_peap_get_status,
+ .has_reauth_data = eap_peap_has_reauth_data,
+ .deinit_for_reauth = eap_peap_deinit_for_reauth,
+ .init_for_reauth = eap_peap_init_for_reauth,
+};
diff --git a/contrib/wpa_supplicant/eap_psk.c b/contrib/wpa_supplicant/eap_psk.c
new file mode 100644
index 000000000000..3b325b59dd91
--- /dev/null
+++ b/contrib/wpa_supplicant/eap_psk.c
@@ -0,0 +1,563 @@
+/*
+ * WPA Supplicant / EAP-PSK (draft-bersani-eap-psk-05.txt)
+ * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published 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 <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "common.h"
+#include "eap_i.h"
+#include "wpa_supplicant.h"
+#include "config_ssid.h"
+#include "md5.h"
+#include "aes_wrap.h"
+
+
+/* draft-bersani-eap-psk-03.txt mode. This is retained for interop testing and
+ * will be removed once an AS that supports draft5 becomes available. */
+#define EAP_PSK_DRAFT3
+
+#define EAP_PSK_RAND_LEN 16
+#define EAP_PSK_MAC_LEN 16
+#define EAP_PSK_TEK_LEN 16
+#define EAP_PSK_MSK_LEN 64
+
+#define EAP_PSK_R_FLAG_CONT 1
+#define EAP_PSK_R_FLAG_DONE_SUCCESS 2
+#define EAP_PSK_R_FLAG_DONE_FAILURE 3
+
+/* EAP-PSK First Message (AS -> Supplicant) */
+struct eap_psk_hdr_1 {
+ u8 code;
+ u8 identifier;
+ u16 length; /* including code, identifier, and length */
+ u8 type; /* EAP_TYPE_PSK */
+#ifndef EAP_PSK_DRAFT3
+ u8 flags;
+#endif /* EAP_PSK_DRAFT3 */
+ u8 rand_s[EAP_PSK_RAND_LEN];
+#ifndef EAP_PSK_DRAFT3
+ /* Followed by variable length ID_S */
+#endif /* EAP_PSK_DRAFT3 */
+} __attribute__ ((packed));
+
+/* EAP-PSK Second Message (Supplicant -> AS) */
+struct eap_psk_hdr_2 {
+ u8 code;
+ u8 identifier;
+ u16 length; /* including code, identifier, and length */
+ u8 type; /* EAP_TYPE_PSK */
+#ifndef EAP_PSK_DRAFT3
+ u8 flags;
+ u8 rand_s[EAP_PSK_RAND_LEN];
+#endif /* EAP_PSK_DRAFT3 */
+ u8 rand_p[EAP_PSK_RAND_LEN];
+ u8 mac_p[EAP_PSK_MAC_LEN];
+ /* Followed by variable length ID_P */
+} __attribute__ ((packed));
+
+/* EAP-PSK Third Message (AS -> Supplicant) */
+struct eap_psk_hdr_3 {
+ u8 code;
+ u8 identifier;
+ u16 length; /* including code, identifier, and length */
+ u8 type; /* EAP_TYPE_PSK */
+#ifndef EAP_PSK_DRAFT3
+ u8 flags;
+ u8 rand_s[EAP_PSK_RAND_LEN];
+#endif /* EAP_PSK_DRAFT3 */
+ u8 mac_s[EAP_PSK_MAC_LEN];
+ /* Followed by variable length PCHANNEL */
+} __attribute__ ((packed));
+
+/* EAP-PSK Fourth Message (Supplicant -> AS) */
+struct eap_psk_hdr_4 {
+ u8 code;
+ u8 identifier;
+ u16 length; /* including code, identifier, and length */
+ u8 type; /* EAP_TYPE_PSK */
+#ifndef EAP_PSK_DRAFT3
+ u8 flags;
+ u8 rand_s[EAP_PSK_RAND_LEN];
+#endif /* EAP_PSK_DRAFT3 */
+ /* Followed by variable length PCHANNEL */
+} __attribute__ ((packed));
+
+
+
+struct eap_psk_data {
+ enum { PSK_INIT, PSK_MAC_SENT, PSK_DONE } state;
+ u8 rand_s[EAP_PSK_RAND_LEN];
+ u8 rand_p[EAP_PSK_RAND_LEN];
+ u8 ak[16], kdk[16], tek[EAP_PSK_TEK_LEN];
+ u8 *id_s, *id_p;
+ size_t id_s_len, id_p_len;
+ u8 key_data[EAP_PSK_MSK_LEN];
+};
+
+
+#define aes_block_size 16
+
+
+static void eap_psk_key_setup(const u8 *psk, u8 *ak, u8 *kdk)
+{
+ memset(ak, 0, aes_block_size);
+ aes_128_encrypt_block(psk, ak, ak);
+ memcpy(kdk, ak, aes_block_size);
+ ak[aes_block_size - 1] ^= 0x01;
+ kdk[aes_block_size - 1] ^= 0x02;
+ aes_128_encrypt_block(psk, ak, ak);
+ aes_128_encrypt_block(psk, kdk, kdk);
+}
+
+
+static void eap_psk_derive_keys(const u8 *kdk, const u8 *rb, u8 *tek, u8 *msk)
+{
+ u8 hash[aes_block_size];
+ u8 counter = 1;
+ int i;
+
+ aes_128_encrypt_block(kdk, rb, hash);
+
+ hash[aes_block_size - 1] ^= counter;
+ aes_128_encrypt_block(kdk, hash, tek);
+ hash[aes_block_size - 1] ^= counter;
+ counter++;
+
+ for (i = 0; i < EAP_PSK_MSK_LEN / aes_block_size; i++) {
+ hash[aes_block_size - 1] ^= counter;
+ aes_128_encrypt_block(kdk, hash, &msk[i * aes_block_size]);
+ hash[aes_block_size - 1] ^= counter;
+ counter++;
+ }
+}
+
+
+static void * eap_psk_init(struct eap_sm *sm)
+{
+ struct wpa_ssid *config = eap_get_config(sm);
+ struct eap_psk_data *data;
+
+ if (config == NULL || !config->eappsk) {
+ wpa_printf(MSG_INFO, "EAP-PSK: pre-shared key not configured");
+ return NULL;
+ }
+
+ data = malloc(sizeof(*data));
+ if (data == NULL)
+ return NULL;
+ memset(data, 0, sizeof(*data));
+ eap_psk_key_setup(config->eappsk, data->ak, data->kdk);
+ wpa_hexdump_key(MSG_DEBUG, "EAP-PSK: AK", data->ak, 16);
+ wpa_hexdump_key(MSG_DEBUG, "EAP-PSK: KDK", data->kdk, 16);
+ data->state = PSK_INIT;
+
+ if (config->nai) {
+ data->id_p = malloc(config->nai_len);
+ if (data->id_p)
+ memcpy(data->id_p, config->nai, config->nai_len);
+ data->id_p_len = config->nai_len;
+ }
+ if (data->id_p == NULL) {
+ wpa_printf(MSG_INFO, "EAP-PSK: could not get own identity");
+ free(data);
+ return NULL;
+ }
+
+#ifdef EAP_PSK_DRAFT3
+ if (config->server_nai) {
+ data->id_s = malloc(config->server_nai_len);
+ if (data->id_s)
+ memcpy(data->id_s, config->server_nai,
+ config->server_nai_len);
+ data->id_s_len = config->server_nai_len;
+ }
+ if (data->id_s == NULL) {
+ wpa_printf(MSG_INFO, "EAP-PSK: could not get server identity");
+ free(data->id_p);
+ free(data);
+ return NULL;
+ }
+#endif /* EAP_PSK_DRAFT3 */
+
+ return data;
+}
+
+
+static void eap_psk_deinit(struct eap_sm *sm, void *priv)
+{
+ struct eap_psk_data *data = priv;
+ free(data->id_s);
+ free(data->id_p);
+ free(data);
+}
+
+
+static u8 * eap_psk_process_1(struct eap_sm *sm, struct eap_psk_data *data,
+ struct eap_method_ret *ret,
+ u8 *reqData, size_t reqDataLen,
+ size_t *respDataLen)
+{
+ struct eap_psk_hdr_1 *hdr1;
+ struct eap_psk_hdr_2 *hdr2;
+ u8 *resp, *buf, *pos;
+ size_t buflen;
+
+ wpa_printf(MSG_DEBUG, "EAP-PSK: in INIT state");
+
+ hdr1 = (struct eap_psk_hdr_1 *) reqData;
+ if (reqDataLen < sizeof(*hdr1) ||
+ be_to_host16(hdr1->length) < sizeof(*hdr1) ||
+ be_to_host16(hdr1->length) > reqDataLen) {
+ wpa_printf(MSG_INFO, "EAP-PSK: Invalid first message "
+ "length (%lu %d; expected %lu or more)",
+ (unsigned long) reqDataLen,
+ be_to_host16(hdr1->length),
+ (unsigned long) sizeof(*hdr1));
+ ret->ignore = TRUE;
+ return NULL;
+ }
+#ifndef EAP_PSK_DRAFT3
+ wpa_printf(MSG_DEBUG, "EAP-PSK: Flags=0x%x", hdr1->flags);
+ if ((hdr1->flags & 0x03) != 0) {
+ wpa_printf(MSG_INFO, "EAP-PSK: Unexpected T=%d (expected 0)",
+ hdr1->flags & 0x03);
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_FAIL;
+ return NULL;
+ }
+#endif /* EAP_PSK_DRAFT3 */
+ wpa_hexdump(MSG_DEBUG, "EAP-PSK: RAND_S", hdr1->rand_s,
+ EAP_PSK_RAND_LEN);
+ memcpy(data->rand_s, hdr1->rand_s, EAP_PSK_RAND_LEN);
+#ifndef EAP_PSK_DRAFT3
+ free(data->id_s);
+ data->id_s_len = be_to_host16(hdr1->length) - sizeof(*hdr1);
+ data->id_s = malloc(data->id_s_len);
+ if (data->id_s == NULL) {
+ wpa_printf(MSG_ERROR, "EAP-PSK: Failed to allocate memory for "
+ "ID_S (len=%d)", data->id_s_len);
+ ret->ignore = TRUE;
+ return NULL;
+ }
+ memcpy(data->id_s, (u8 *) (hdr1 + 1), data->id_s_len);
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP-PSK: ID_S",
+ data->id_s, data->id_s_len);
+#endif /* EAP_PSK_DRAFT3 */
+
+ if (hostapd_get_rand(data->rand_p, EAP_PSK_RAND_LEN)) {
+ wpa_printf(MSG_ERROR, "EAP-PSK: Failed to get random data");
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ *respDataLen = sizeof(*hdr2) + data->id_p_len;
+ resp = malloc(*respDataLen);
+ if (resp == NULL)
+ return NULL;
+ hdr2 = (struct eap_psk_hdr_2 *) resp;
+ hdr2->code = EAP_CODE_RESPONSE;
+ hdr2->identifier = hdr1->identifier;
+ hdr2->length = host_to_be16(*respDataLen);
+ hdr2->type = EAP_TYPE_PSK;
+#ifndef EAP_PSK_DRAFT3
+ hdr2->flags = 1; /* T=1 */
+ memcpy(hdr2->rand_s, hdr1->rand_s, EAP_PSK_RAND_LEN);
+#endif /* EAP_PSK_DRAFT3 */
+ memcpy(hdr2->rand_p, data->rand_p, EAP_PSK_RAND_LEN);
+ memcpy((u8 *) (hdr2 + 1), data->id_p, data->id_p_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;
+ buf = malloc(buflen);
+ if (buf == NULL) {
+ free(resp);
+ return NULL;
+ }
+ memcpy(buf, data->id_p, data->id_p_len);
+ pos = buf + data->id_p_len;
+ memcpy(pos, data->id_s, data->id_s_len);
+ pos += data->id_s_len;
+ memcpy(pos, data->rand_s, EAP_PSK_RAND_LEN);
+ pos += EAP_PSK_RAND_LEN;
+ memcpy(pos, data->rand_p, EAP_PSK_RAND_LEN);
+ omac1_aes_128(data->ak, buf, buflen, hdr2->mac_p);
+ free(buf);
+ wpa_hexdump(MSG_DEBUG, "EAP-PSK: RAND_P", hdr2->rand_p,
+ EAP_PSK_RAND_LEN);
+ wpa_hexdump(MSG_DEBUG, "EAP-PSK: MAC_P", hdr2->mac_p, EAP_PSK_MAC_LEN);
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP-PSK: ID_P",
+ (u8 *) (hdr2 + 1), data->id_p_len);
+
+ data->state = PSK_MAC_SENT;
+
+ return resp;
+}
+
+
+static u8 * eap_psk_process_3(struct eap_sm *sm, struct eap_psk_data *data,
+ struct eap_method_ret *ret,
+ u8 *reqData, size_t reqDataLen,
+ size_t *respDataLen)
+{
+ struct eap_psk_hdr_3 *hdr3;
+ struct eap_psk_hdr_4 *hdr4;
+ u8 *resp, *buf, *pchannel, *tag, *msg, nonce[16];
+ u8 mac[EAP_PSK_MAC_LEN];
+ size_t buflen, left;
+ int failed = 0;
+
+ wpa_printf(MSG_DEBUG, "EAP-PSK: in MAC_SENT state");
+
+ hdr3 = (struct eap_psk_hdr_3 *) reqData;
+ left = be_to_host16(hdr3->length);
+ if (left < sizeof(*hdr3) || reqDataLen < left) {
+ wpa_printf(MSG_INFO, "EAP-PSK: Invalid third message "
+ "length (%lu %d; expected %lu)",
+ (unsigned long) reqDataLen,
+ be_to_host16(hdr3->length),
+ (unsigned long) sizeof(*hdr3));
+ ret->ignore = TRUE;
+ return NULL;
+ }
+ left -= sizeof(*hdr3);
+ pchannel = (u8 *) (hdr3 + 1);
+#ifndef EAP_PSK_DRAFT3
+ wpa_printf(MSG_DEBUG, "EAP-PSK: Flags=0x%x", hdr3->flags);
+ if ((hdr3->flags & 0x03) != 2) {
+ wpa_printf(MSG_INFO, "EAP-PSK: Unexpected T=%d (expected 2)",
+ hdr3->flags & 0x03);
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_FAIL;
+ return NULL;
+ }
+ wpa_hexdump(MSG_DEBUG, "EAP-PSK: RAND_S", hdr3->rand_s,
+ EAP_PSK_RAND_LEN);
+ /* TODO: would not need to store RAND_S since it is available in this
+ * message. For now, since we store this anyway, verify that it matches
+ * with whatever the server is sending. */
+ if (memcmp(hdr3->rand_s, data->rand_s, EAP_PSK_RAND_LEN) != 0) {
+ wpa_printf(MSG_ERROR, "EAP-PSK: RAND_S did not match");
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_FAIL;
+ return NULL;
+ }
+#endif /* EAP_PSK_DRAFT3 */
+ wpa_hexdump(MSG_DEBUG, "EAP-PSK: MAC_S", hdr3->mac_s, EAP_PSK_MAC_LEN);
+ wpa_hexdump(MSG_DEBUG, "EAP-PSK: PCHANNEL", pchannel, left);
+
+ if (left < 4 + 16 + 1) {
+ wpa_printf(MSG_INFO, "EAP-PSK: Too short PCHANNEL data in "
+ "third message (len=%lu, expected 21)",
+ (unsigned long) left);
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ /* MAC_S = OMAC1-AES-128(AK, ID_S||RAND_P) */
+ buflen = data->id_s_len + EAP_PSK_RAND_LEN;
+ buf = malloc(buflen);
+ if (buf == NULL)
+ return NULL;
+ memcpy(buf, data->id_s, data->id_s_len);
+ memcpy(buf + data->id_s_len, data->rand_p, EAP_PSK_RAND_LEN);
+ omac1_aes_128(data->ak, buf, buflen, mac);
+ free(buf);
+ if (memcmp(mac, hdr3->mac_s, EAP_PSK_MAC_LEN) != 0) {
+ wpa_printf(MSG_WARNING, "EAP-PSK: Invalid MAC_S in third "
+ "message");
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_FAIL;
+ return NULL;
+ }
+ wpa_printf(MSG_DEBUG, "EAP-PSK: MAC_S verified successfully");
+
+ eap_psk_derive_keys(data->kdk, data->rand_p, data->tek,
+ data->key_data);
+ wpa_hexdump_key(MSG_DEBUG, "EAP-PSK: TEK", data->tek, EAP_PSK_TEK_LEN);
+ wpa_hexdump_key(MSG_DEBUG, "EAP-PSK: MSK", data->key_data,
+ EAP_PSK_MSK_LEN);
+
+ memset(nonce, 0, 12);
+ memcpy(nonce + 12, pchannel, 4);
+ pchannel += 4;
+ left -= 4;
+
+ tag = pchannel;
+ pchannel += 16;
+ left -= 16;
+
+ msg = pchannel;
+
+ wpa_hexdump(MSG_MSGDUMP, "EAP-PSK: PCHANNEL - nonce",
+ nonce, sizeof(nonce));
+ wpa_hexdump(MSG_MSGDUMP, "EAP-PSK: PCHANNEL - hdr", reqData, 5);
+ wpa_hexdump(MSG_MSGDUMP, "EAP-PSK: PCHANNEL - cipher msg", msg, left);
+
+#ifdef EAP_PSK_DRAFT3
+ if (aes_128_eax_decrypt(data->tek, nonce, sizeof(nonce),
+ reqData, 5, msg, left, tag))
+#else /* EAP_PSK_DRAFT3 */
+ if (aes_128_eax_decrypt(data->tek, nonce, sizeof(nonce),
+ reqData, 22, msg, left, tag))
+#endif /* EAP_PSK_DRAFT3 */
+ {
+ wpa_printf(MSG_WARNING, "EAP-PSK: PCHANNEL decryption failed");
+ return NULL;
+ }
+ wpa_hexdump(MSG_DEBUG, "EAP-PSK: Decrypted PCHANNEL message",
+ msg, left);
+
+ /* Verify R flag */
+ switch (msg[0] >> 6) {
+ case EAP_PSK_R_FLAG_CONT:
+ wpa_printf(MSG_DEBUG, "EAP-PSK: R flag - CONT - unsupported");
+ return NULL;
+ case EAP_PSK_R_FLAG_DONE_SUCCESS:
+ wpa_printf(MSG_DEBUG, "EAP-PSK: R flag - DONE_SUCCESS");
+ break;
+ case EAP_PSK_R_FLAG_DONE_FAILURE:
+ wpa_printf(MSG_DEBUG, "EAP-PSK: R flag - DONE_FAILURE");
+ wpa_printf(MSG_INFO, "EAP-PSK: Authentication server rejected "
+ "authentication");
+ failed = 1;
+ break;
+ }
+
+ *respDataLen = sizeof(*hdr4) + 4 + 16 + 1;
+ resp = malloc(*respDataLen);
+ if (resp == NULL)
+ return NULL;
+ hdr4 = (struct eap_psk_hdr_4 *) resp;
+ hdr4->code = EAP_CODE_RESPONSE;
+ hdr4->identifier = hdr3->identifier;
+ hdr4->length = host_to_be16(*respDataLen);
+ hdr4->type = EAP_TYPE_PSK;
+#ifndef EAP_PSK_DRAFT3
+ hdr4->flags = 3; /* T=3 */
+ memcpy(hdr4->rand_s, hdr3->rand_s, EAP_PSK_RAND_LEN);
+#endif /* EAP_PSK_DRAFT3 */
+ pchannel = (u8 *) (hdr4 + 1);
+
+ /* nonce++ */
+ inc_byte_array(nonce, sizeof(nonce));
+ memcpy(pchannel, nonce + 12, 4);
+
+ pchannel[4 + 16] = EAP_PSK_R_FLAG_DONE_SUCCESS << 6;
+
+ wpa_hexdump(MSG_DEBUG, "EAP-PSK: reply message (plaintext)",
+ pchannel + 4 + 16, 1);
+#ifdef EAP_PSK_DRAFT3
+ aes_128_eax_encrypt(data->tek, nonce, sizeof(nonce), resp, 5,
+ pchannel + 4 + 16, 1, pchannel + 4);
+#else /* EAP_PSK_DRAFT3 */
+ aes_128_eax_encrypt(data->tek, nonce, sizeof(nonce), resp, 22,
+ pchannel + 4 + 16, 1, pchannel + 4);
+#endif /* EAP_PSK_DRAFT3 */
+ wpa_hexdump(MSG_DEBUG, "EAP-PSK: reply message (PCHANNEL)",
+ pchannel, 4 + 16 + 1);
+
+ wpa_printf(MSG_DEBUG, "EAP-PSK: Completed %ssuccessfully",
+ failed ? "un" : "");
+ data->state = PSK_DONE;
+ ret->methodState = METHOD_DONE;
+ ret->decision = failed ? DECISION_FAIL : DECISION_UNCOND_SUCC;
+
+ return resp;
+}
+
+
+static u8 * eap_psk_process(struct eap_sm *sm, void *priv,
+ struct eap_method_ret *ret,
+ u8 *reqData, size_t reqDataLen,
+ size_t *respDataLen)
+{
+ struct eap_psk_data *data = priv;
+ struct eap_hdr *req;
+ u8 *pos, *resp = NULL;
+ size_t len;
+
+ req = (struct eap_hdr *) reqData;
+ pos = (u8 *) (req + 1);
+ if (reqDataLen < sizeof(*req) + 1 || *pos != EAP_TYPE_PSK ||
+ (len = be_to_host16(req->length)) > reqDataLen) {
+ wpa_printf(MSG_INFO, "EAP-PSK: Invalid frame");
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ ret->ignore = FALSE;
+ ret->methodState = METHOD_CONT;
+ ret->decision = DECISION_FAIL;
+ ret->allowNotifications = TRUE;
+
+ switch (data->state) {
+ case PSK_INIT:
+ resp = eap_psk_process_1(sm, data, ret, reqData, len,
+ respDataLen);
+ break;
+ case PSK_MAC_SENT:
+ resp = eap_psk_process_3(sm, data, ret, reqData, len,
+ respDataLen);
+ break;
+ case PSK_DONE:
+ wpa_printf(MSG_DEBUG, "EAP-PSK: in DONE state - ignore "
+ "unexpected message");
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ if (ret->methodState == METHOD_DONE) {
+ ret->allowNotifications = FALSE;
+ }
+
+ return resp;
+}
+
+
+static Boolean eap_psk_isKeyAvailable(struct eap_sm *sm, void *priv)
+{
+ struct eap_psk_data *data = priv;
+ return data->state == PSK_DONE;
+}
+
+
+static u8 * eap_psk_getKey(struct eap_sm *sm, void *priv, size_t *len)
+{
+ struct eap_psk_data *data = priv;
+ u8 *key;
+
+ if (data->state != PSK_DONE)
+ return NULL;
+
+ key = malloc(EAP_PSK_MSK_LEN);
+ if (key == NULL)
+ return NULL;
+
+ *len = EAP_PSK_MSK_LEN;
+ memcpy(key, data->key_data, EAP_PSK_MSK_LEN);
+
+ return key;
+}
+
+
+const struct eap_method eap_method_psk =
+{
+ .method = EAP_TYPE_PSK,
+ .name = "PSK",
+ .init = eap_psk_init,
+ .deinit = eap_psk_deinit,
+ .process = eap_psk_process,
+ .isKeyAvailable = eap_psk_isKeyAvailable,
+ .getKey = eap_psk_getKey,
+};
diff --git a/contrib/wpa_supplicant/eap_sim.c b/contrib/wpa_supplicant/eap_sim.c
new file mode 100644
index 000000000000..f7ce191a6dda
--- /dev/null
+++ b/contrib/wpa_supplicant/eap_sim.c
@@ -0,0 +1,1004 @@
+/*
+ * WPA Supplicant / EAP-SIM (draft-haverinen-pppext-eap-sim-13.txt)
+ * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published 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 <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "common.h"
+#include "eap_i.h"
+#include "wpa_supplicant.h"
+#include "config_ssid.h"
+#include "sha1.h"
+#include "pcsc_funcs.h"
+#include "eap_sim_common.h"
+
+#define EAP_SIM_VERSION 1
+
+/* EAP-SIM Subtypes */
+#define EAP_SIM_SUBTYPE_START 10
+#define EAP_SIM_SUBTYPE_CHALLENGE 11
+#define EAP_SIM_SUBTYPE_NOTIFICATION 12
+#define EAP_SIM_SUBTYPE_REAUTHENTICATION 13
+#define EAP_SIM_SUBTYPE_CLIENT_ERROR 14
+
+/* AT_CLIENT_ERROR_CODE error codes */
+#define EAP_SIM_UNABLE_TO_PROCESS_PACKET 0
+#define EAP_SIM_UNSUPPORTED_VERSION 1
+#define EAP_SIM_INSUFFICIENT_NUM_OF_CHAL 2
+#define EAP_SIM_RAND_NOT_FRESH 3
+
+#define KC_LEN 8
+#define SRES_LEN 4
+#define EAP_SIM_MAX_FAST_REAUTHS 1000
+
+struct eap_sim_data {
+ u8 *ver_list;
+ size_t ver_list_len;
+ int selected_version;
+ int min_num_chal, num_chal;
+
+ u8 kc[3][KC_LEN];
+ u8 sres[3][SRES_LEN];
+ u8 nonce_mt[EAP_SIM_NONCE_MT_LEN], nonce_s[EAP_SIM_NONCE_S_LEN];
+ u8 mk[EAP_SIM_MK_LEN];
+ u8 k_aut[EAP_SIM_K_AUT_LEN];
+ u8 k_encr[EAP_SIM_K_ENCR_LEN];
+ u8 msk[EAP_SIM_KEYING_DATA_LEN];
+ u8 rand[3][GSM_RAND_LEN];
+
+ int num_id_req, num_notification;
+ u8 *pseudonym;
+ size_t pseudonym_len;
+ u8 *reauth_id;
+ size_t reauth_id_len;
+ int reauth;
+ unsigned int counter, counter_too_small;
+ u8 *last_eap_identity;
+ size_t last_eap_identity_len;
+ enum { CONTINUE, SUCCESS, FAILURE } state;
+};
+
+
+static void * eap_sim_init(struct eap_sm *sm)
+{
+ struct eap_sim_data *data;
+ struct wpa_ssid *config = eap_get_config(sm);
+
+ data = malloc(sizeof(*data));
+ if (data == NULL)
+ return NULL;
+ memset(data, 0, sizeof(*data));
+
+ if (hostapd_get_rand(data->nonce_mt, EAP_SIM_NONCE_MT_LEN)) {
+ wpa_printf(MSG_WARNING, "EAP-SIM: Failed to get random data "
+ "for NONCE_MT");
+ free(data);
+ return NULL;
+ }
+
+ data->min_num_chal = 2;
+ if (config && config->phase1) {
+ char *pos = strstr(config->phase1, "sim_min_num_chal=");
+ if (pos) {
+ data->min_num_chal = atoi(pos + 17);
+ if (data->min_num_chal < 2 || data->min_num_chal > 3) {
+ wpa_printf(MSG_WARNING, "EAP-SIM: Invalid "
+ "sim_min_num_chal configuration "
+ "(%d, expected 2 or 3)",
+ data->min_num_chal);
+ free(data);
+ return NULL;
+ }
+ wpa_printf(MSG_DEBUG, "EAP-SIM: Set minimum number of "
+ "challenges to %d", data->min_num_chal);
+ }
+ }
+
+ data->state = CONTINUE;
+
+ return data;
+}
+
+
+static void eap_sim_deinit(struct eap_sm *sm, void *priv)
+{
+ struct eap_sim_data *data = priv;
+ if (data) {
+ free(data->ver_list);
+ free(data->pseudonym);
+ free(data->reauth_id);
+ free(data->last_eap_identity);
+ free(data);
+ }
+}
+
+
+static int eap_sim_gsm_auth(struct eap_sm *sm, struct eap_sim_data *data)
+{
+ wpa_printf(MSG_DEBUG, "EAP-SIM: GSM authentication algorithm");
+#ifdef PCSC_FUNCS
+ if (scard_gsm_auth(sm->scard_ctx, data->rand[0],
+ data->sres[0], data->kc[0]) ||
+ scard_gsm_auth(sm->scard_ctx, data->rand[1],
+ data->sres[1], data->kc[1]) ||
+ (data->num_chal > 2 &&
+ scard_gsm_auth(sm->scard_ctx, data->rand[2],
+ data->sres[2], data->kc[2]))) {
+ wpa_printf(MSG_DEBUG, "EAP-SIM: GSM SIM authentication could "
+ "not be completed");
+ return -1;
+ }
+#else /* PCSC_FUNCS */
+ /* These hardcoded Kc and SRES values are used for testing. RAND to
+ * KC/SREC mapping is very bogus as far as real authentication is
+ * concerned, but it is quite useful for cases where the AS is rotating
+ * the order of pre-configured values. */
+ {
+ int i;
+ for (i = 0; i < data->num_chal; i++) {
+ if (data->rand[i][0] == 0xaa) {
+ memcpy(data->kc[i],
+ "\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7",
+ KC_LEN);
+ memcpy(data->sres[i], "\xd1\xd2\xd3\xd4",
+ SRES_LEN);
+ } else if (data->rand[i][0] == 0xbb) {
+ memcpy(data->kc[i],
+ "\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7",
+ KC_LEN);
+ memcpy(data->sres[i], "\xe1\xe2\xe3\xe4",
+ SRES_LEN);
+ } else {
+ memcpy(data->kc[i],
+ "\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7",
+ KC_LEN);
+ memcpy(data->sres[i], "\xf1\xf2\xf3\xf4",
+ SRES_LEN);
+ }
+ }
+ }
+#endif /* PCSC_FUNCS */
+ return 0;
+}
+
+
+static int eap_sim_supported_ver(struct eap_sim_data *data, int version)
+{
+ return version == EAP_SIM_VERSION;
+}
+
+
+static void eap_sim_derive_mk(struct eap_sim_data *data,
+ const u8 *identity, size_t identity_len)
+{
+ u8 sel_ver[2];
+ const unsigned char *addr[5];
+ size_t len[5];
+
+ addr[0] = identity;
+ len[0] = identity_len;
+ addr[1] = (u8 *) data->kc;
+ len[1] = data->num_chal * KC_LEN;
+ addr[2] = data->nonce_mt;
+ len[2] = EAP_SIM_NONCE_MT_LEN;
+ addr[3] = data->ver_list;
+ len[3] = data->ver_list_len;
+ addr[4] = sel_ver;
+ len[4] = 2;
+
+ sel_ver[0] = data->selected_version >> 8;
+ sel_ver[1] = data->selected_version & 0xff;
+
+ /* MK = SHA1(Identity|n*Kc|NONCE_MT|Version List|Selected Version) */
+ sha1_vector(5, addr, len, data->mk);
+ wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: MK", data->mk, EAP_SIM_MK_LEN);
+}
+
+
+#define CLEAR_PSEUDONYM 0x01
+#define CLEAR_REAUTH_ID 0x02
+#define CLEAR_EAP_ID 0x04
+
+static void eap_sim_clear_identities(struct eap_sim_data *data, int id)
+{
+ wpa_printf(MSG_DEBUG, "EAP-SIM: forgetting old%s%s%s",
+ id & CLEAR_PSEUDONYM ? " pseudonym" : "",
+ id & CLEAR_REAUTH_ID ? " reauth_id" : "",
+ id & CLEAR_EAP_ID ? " eap_id" : "");
+ if (id & CLEAR_PSEUDONYM) {
+ free(data->pseudonym);
+ data->pseudonym = NULL;
+ data->pseudonym_len = 0;
+ }
+ if (id & CLEAR_REAUTH_ID) {
+ free(data->reauth_id);
+ data->reauth_id = NULL;
+ data->reauth_id_len = 0;
+ }
+ if (id & CLEAR_EAP_ID) {
+ free(data->last_eap_identity);
+ data->last_eap_identity = NULL;
+ data->last_eap_identity_len = 0;
+ }
+}
+
+
+static int eap_sim_learn_ids(struct eap_sim_data *data,
+ struct eap_sim_attrs *attr)
+{
+ if (attr->next_pseudonym) {
+ free(data->pseudonym);
+ data->pseudonym = malloc(attr->next_pseudonym_len);
+ if (data->pseudonym == NULL) {
+ wpa_printf(MSG_INFO, "EAP-SIM: (encr) No memory for "
+ "next pseudonym");
+ return -1;
+ }
+ memcpy(data->pseudonym, attr->next_pseudonym,
+ attr->next_pseudonym_len);
+ data->pseudonym_len = attr->next_pseudonym_len;
+ wpa_hexdump_ascii(MSG_DEBUG,
+ "EAP-SIM: (encr) AT_NEXT_PSEUDONYM",
+ data->pseudonym,
+ data->pseudonym_len);
+ }
+
+ if (attr->next_reauth_id) {
+ free(data->reauth_id);
+ data->reauth_id = malloc(attr->next_reauth_id_len);
+ if (data->reauth_id == NULL) {
+ wpa_printf(MSG_INFO, "EAP-SIM: (encr) No memory for "
+ "next reauth_id");
+ return -1;
+ }
+ memcpy(data->reauth_id, attr->next_reauth_id,
+ attr->next_reauth_id_len);
+ data->reauth_id_len = attr->next_reauth_id_len;
+ wpa_hexdump_ascii(MSG_DEBUG,
+ "EAP-SIM: (encr) AT_NEXT_REAUTH_ID",
+ data->reauth_id,
+ data->reauth_id_len);
+ }
+
+ return 0;
+}
+
+
+static u8 * eap_sim_client_error(struct eap_sm *sm, struct eap_sim_data *data,
+ struct eap_hdr *req,
+ size_t *respDataLen, int err)
+{
+ struct eap_sim_msg *msg;
+
+ data->state = FAILURE;
+ data->num_id_req = 0;
+ data->num_notification = 0;
+
+ msg = eap_sim_msg_init(EAP_CODE_RESPONSE, req->identifier,
+ EAP_TYPE_SIM, EAP_SIM_SUBTYPE_CLIENT_ERROR);
+ eap_sim_msg_add(msg, EAP_SIM_AT_CLIENT_ERROR_CODE, err, NULL, 0);
+ return eap_sim_msg_finish(msg, respDataLen, NULL, NULL, 0);
+}
+
+
+static u8 * eap_sim_response_start(struct eap_sm *sm,
+ struct eap_sim_data *data,
+ struct eap_hdr *req,
+ size_t *respDataLen,
+ enum eap_sim_id_req id_req)
+{
+ struct wpa_ssid *config = eap_get_config(sm);
+ u8 *identity = NULL;
+ size_t identity_len = 0;
+ struct eap_sim_msg *msg;
+
+ data->reauth = 0;
+ if (id_req == ANY_ID && data->reauth_id) {
+ identity = data->reauth_id;
+ identity_len = data->reauth_id_len;
+ data->reauth = 1;
+ } else if ((id_req == ANY_ID || id_req == FULLAUTH_ID) &&
+ data->pseudonym) {
+ identity = data->pseudonym;
+ identity_len = data->pseudonym_len;
+ eap_sim_clear_identities(data, CLEAR_REAUTH_ID);
+ } else if (id_req != NO_ID_REQ && config && config->identity) {
+ identity = config->identity;
+ identity_len = config->identity_len;
+ eap_sim_clear_identities(data,
+ CLEAR_PSEUDONYM | CLEAR_REAUTH_ID);
+ }
+ if (id_req != NO_ID_REQ)
+ eap_sim_clear_identities(data, CLEAR_EAP_ID);
+
+ wpa_printf(MSG_DEBUG, "Generating EAP-SIM Start (id=%d)",
+ req->identifier);
+ msg = eap_sim_msg_init(EAP_CODE_RESPONSE, req->identifier,
+ EAP_TYPE_SIM, EAP_SIM_SUBTYPE_START);
+ wpa_hexdump(MSG_DEBUG, " AT_NONCE_MT",
+ data->nonce_mt, EAP_SIM_NONCE_MT_LEN);
+ eap_sim_msg_add(msg, EAP_SIM_AT_NONCE_MT, 0,
+ data->nonce_mt, EAP_SIM_NONCE_MT_LEN);
+ wpa_printf(MSG_DEBUG, " AT_SELECTED_VERSION %d",
+ data->selected_version);
+ eap_sim_msg_add(msg, EAP_SIM_AT_SELECTED_VERSION,
+ data->selected_version, NULL, 0);
+
+ if (identity) {
+ wpa_hexdump_ascii(MSG_DEBUG, " AT_IDENTITY",
+ identity, identity_len);
+ eap_sim_msg_add(msg, EAP_SIM_AT_IDENTITY, identity_len,
+ identity, identity_len);
+ }
+
+ return eap_sim_msg_finish(msg, respDataLen, NULL, NULL, 0);
+}
+
+
+static u8 * eap_sim_response_challenge(struct eap_sm *sm,
+ struct eap_sim_data *data,
+ struct eap_hdr *req,
+ size_t *respDataLen)
+{
+ struct eap_sim_msg *msg;
+
+ wpa_printf(MSG_DEBUG, "Generating EAP-SIM Challenge (id=%d)",
+ req->identifier);
+ msg = eap_sim_msg_init(EAP_CODE_RESPONSE, req->identifier,
+ EAP_TYPE_SIM, EAP_SIM_SUBTYPE_CHALLENGE);
+ wpa_printf(MSG_DEBUG, " AT_MAC");
+ eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC);
+ return eap_sim_msg_finish(msg, respDataLen, data->k_aut,
+ (u8 *) data->sres,
+ data->num_chal * SRES_LEN);
+}
+
+
+static u8 * eap_sim_response_reauth(struct eap_sm *sm,
+ struct eap_sim_data *data,
+ struct eap_hdr *req,
+ size_t *respDataLen, int counter_too_small)
+{
+ struct eap_sim_msg *msg;
+ unsigned int counter;
+
+ wpa_printf(MSG_DEBUG, "Generating EAP-SIM Reauthentication (id=%d)",
+ req->identifier);
+ msg = eap_sim_msg_init(EAP_CODE_RESPONSE, req->identifier,
+ EAP_TYPE_SIM,
+ EAP_SIM_SUBTYPE_REAUTHENTICATION);
+ wpa_printf(MSG_DEBUG, " AT_IV");
+ wpa_printf(MSG_DEBUG, " AT_ENCR_DATA");
+ eap_sim_msg_add_encr_start(msg, EAP_SIM_AT_IV, EAP_SIM_AT_ENCR_DATA);
+
+ if (counter_too_small) {
+ wpa_printf(MSG_DEBUG, " *AT_COUNTER_TOO_SMALL");
+ eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER_TOO_SMALL, 0, NULL, 0);
+ counter = data->counter_too_small;
+ } else
+ counter = data->counter;
+
+ wpa_printf(MSG_DEBUG, " *AT_COUNTER %d", counter);
+ eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER, counter, NULL, 0);
+
+ if (eap_sim_msg_add_encr_end(msg, data->k_encr, EAP_SIM_AT_PADDING)) {
+ wpa_printf(MSG_WARNING, "EAP-SIM: Failed to encrypt "
+ "AT_ENCR_DATA");
+ eap_sim_msg_free(msg);
+ return NULL;
+ }
+ wpa_printf(MSG_DEBUG, " AT_MAC");
+ eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC);
+ return eap_sim_msg_finish(msg, respDataLen, data->k_aut, data->nonce_s,
+ EAP_SIM_NONCE_S_LEN);
+}
+
+
+static u8 * eap_sim_response_notification(struct eap_sm *sm,
+ struct eap_sim_data *data,
+ struct eap_hdr *req,
+ size_t *respDataLen,
+ u16 notification)
+{
+ struct eap_sim_msg *msg;
+ u8 *k_aut = (notification & 0x4000) == 0 ? data->k_aut : NULL;
+
+ wpa_printf(MSG_DEBUG, "Generating EAP-SIM Notification (id=%d)",
+ req->identifier);
+ msg = eap_sim_msg_init(EAP_CODE_RESPONSE, req->identifier,
+ EAP_TYPE_SIM, EAP_SIM_SUBTYPE_NOTIFICATION);
+ wpa_printf(MSG_DEBUG, " AT_NOTIFICATION");
+ eap_sim_msg_add(msg, EAP_SIM_AT_NOTIFICATION, notification, NULL, 0);
+ if (k_aut && data->reauth) {
+ wpa_printf(MSG_DEBUG, " AT_IV");
+ wpa_printf(MSG_DEBUG, " AT_ENCR_DATA");
+ eap_sim_msg_add_encr_start(msg, EAP_SIM_AT_IV,
+ EAP_SIM_AT_ENCR_DATA);
+ wpa_printf(MSG_DEBUG, " *AT_COUNTER %d", data->counter);
+ eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER, data->counter,
+ NULL, 0);
+ if (eap_sim_msg_add_encr_end(msg, data->k_encr,
+ EAP_SIM_AT_PADDING)) {
+ wpa_printf(MSG_WARNING, "EAP-SIM: Failed to encrypt "
+ "AT_ENCR_DATA");
+ eap_sim_msg_free(msg);
+ return NULL;
+ }
+ }
+ if (k_aut) {
+ wpa_printf(MSG_DEBUG, " AT_MAC");
+ eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC);
+ }
+ return eap_sim_msg_finish(msg, respDataLen, k_aut, (u8 *) "", 0);
+}
+
+
+static u8 * eap_sim_process_start(struct eap_sm *sm, struct eap_sim_data *data,
+ struct eap_hdr *req, size_t reqDataLen,
+ size_t *respDataLen,
+ struct eap_sim_attrs *attr)
+{
+ int i, selected_version = -1, id_error;
+ u8 *pos;
+
+ wpa_printf(MSG_DEBUG, "EAP-SIM: subtype Start");
+ if (attr->version_list == NULL) {
+ wpa_printf(MSG_INFO, "EAP-SIM: No AT_VERSION_LIST in "
+ "SIM/Start");
+ return eap_sim_client_error(sm, data, req, respDataLen,
+ EAP_SIM_UNSUPPORTED_VERSION);
+ }
+
+ free(data->ver_list);
+ data->ver_list = malloc(attr->version_list_len);
+ if (data->ver_list == NULL) {
+ wpa_printf(MSG_DEBUG, "EAP-SIM: Failed to allocate "
+ "memory for version list");
+ return eap_sim_client_error(sm, data, req, respDataLen,
+ EAP_SIM_UNABLE_TO_PROCESS_PACKET);
+ }
+ memcpy(data->ver_list, attr->version_list, attr->version_list_len);
+ data->ver_list_len = attr->version_list_len;
+ pos = data->ver_list;
+ for (i = 0; i < data->ver_list_len / 2; i++) {
+ int ver = pos[0] * 256 + pos[1];
+ pos += 2;
+ if (eap_sim_supported_ver(data, ver)) {
+ selected_version = ver;
+ break;
+ }
+ }
+ if (selected_version < 0) {
+ wpa_printf(MSG_INFO, "EAP-SIM: Could not find a supported "
+ "version");
+ return eap_sim_client_error(sm, data, req, respDataLen,
+ EAP_SIM_UNSUPPORTED_VERSION);
+ }
+ wpa_printf(MSG_DEBUG, "EAP-SIM: Selected Version %d",
+ selected_version);
+ data->selected_version = selected_version;
+
+ id_error = 0;
+ switch (attr->id_req) {
+ case NO_ID_REQ:
+ break;
+ case ANY_ID:
+ if (data->num_id_req > 0)
+ id_error++;
+ data->num_id_req++;
+ break;
+ case FULLAUTH_ID:
+ if (data->num_id_req > 1)
+ id_error++;
+ data->num_id_req++;
+ break;
+ case PERMANENT_ID:
+ if (data->num_id_req > 2)
+ id_error++;
+ data->num_id_req++;
+ break;
+ }
+ if (id_error) {
+ wpa_printf(MSG_INFO, "EAP-SIM: Too many ID requests "
+ "used within one authentication");
+ return eap_sim_client_error(sm, data, req, respDataLen,
+ EAP_SIM_UNABLE_TO_PROCESS_PACKET);
+ }
+
+ return eap_sim_response_start(sm, data, req, respDataLen,
+ attr->id_req);
+}
+
+
+static u8 * eap_sim_process_challenge(struct eap_sm *sm,
+ struct eap_sim_data *data,
+ struct eap_hdr *req, size_t reqDataLen,
+ size_t *respDataLen,
+ struct eap_sim_attrs *attr)
+{
+ struct wpa_ssid *config = eap_get_config(sm);
+ u8 *identity;
+ size_t identity_len;
+ struct eap_sim_attrs eattr;
+
+ wpa_printf(MSG_DEBUG, "EAP-SIM: subtype Challenge");
+ data->reauth = 0;
+ if (!attr->mac || !attr->rand) {
+ wpa_printf(MSG_WARNING, "EAP-SIM: Challenge message "
+ "did not include%s%s",
+ !attr->mac ? " AT_MAC" : "",
+ !attr->rand ? " AT_RAND" : "");
+ return eap_sim_client_error(sm, data, req, respDataLen,
+ EAP_SIM_UNABLE_TO_PROCESS_PACKET);
+ }
+
+ wpa_printf(MSG_DEBUG, "EAP-SIM: %lu challenges",
+ (unsigned long) attr->num_chal);
+ if (attr->num_chal < data->min_num_chal) {
+ wpa_printf(MSG_INFO, "EAP-SIM: Insufficient number of "
+ "challenges (%lu)", (unsigned long) attr->num_chal);
+ return eap_sim_client_error(sm, data, req, respDataLen,
+ EAP_SIM_INSUFFICIENT_NUM_OF_CHAL);
+ }
+ if (attr->num_chal > 3) {
+ wpa_printf(MSG_INFO, "EAP-SIM: Too many challenges "
+ "(%lu)", (unsigned long) attr->num_chal);
+ return eap_sim_client_error(sm, data, req, respDataLen,
+ EAP_SIM_UNABLE_TO_PROCESS_PACKET);
+ }
+
+ /* Verify that RANDs are different */
+ if (memcmp(attr->rand, attr->rand + GSM_RAND_LEN,
+ GSM_RAND_LEN) == 0 ||
+ (attr->num_chal > 2 &&
+ (memcmp(attr->rand, attr->rand + 2 * GSM_RAND_LEN,
+ GSM_RAND_LEN) == 0 ||
+ memcmp(attr->rand + GSM_RAND_LEN,
+ attr->rand + 2 * GSM_RAND_LEN,
+ GSM_RAND_LEN) == 0))) {
+ wpa_printf(MSG_INFO, "EAP-SIM: Same RAND used multiple times");
+ return eap_sim_client_error(sm, data, req, respDataLen,
+ EAP_SIM_RAND_NOT_FRESH);
+ }
+
+ memcpy(data->rand, attr->rand, attr->num_chal * GSM_RAND_LEN);
+ data->num_chal = attr->num_chal;
+
+ if (eap_sim_gsm_auth(sm, data)) {
+ wpa_printf(MSG_WARNING, "EAP-SIM: GSM authentication failed");
+ return eap_sim_client_error(sm, data, req, respDataLen,
+ EAP_SIM_UNABLE_TO_PROCESS_PACKET);
+ }
+ if (data->last_eap_identity) {
+ identity = data->last_eap_identity;
+ identity_len = data->last_eap_identity_len;
+ } else if (data->pseudonym) {
+ identity = data->pseudonym;
+ identity_len = data->pseudonym_len;
+ } else {
+ identity = config->identity;
+ identity_len = config->identity_len;
+ }
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM: Selected identity for MK "
+ "derivation", identity, identity_len);
+ eap_sim_derive_mk(data, identity, identity_len);
+ eap_sim_derive_keys(data->mk, data->k_encr, data->k_aut, data->msk);
+ if (eap_sim_verify_mac(data->k_aut, (u8 *) req, reqDataLen, attr->mac,
+ data->nonce_mt, EAP_SIM_NONCE_MT_LEN)) {
+ wpa_printf(MSG_WARNING, "EAP-SIM: Challenge message "
+ "used invalid AT_MAC");
+ return eap_sim_client_error(sm, data, req, respDataLen,
+ EAP_SIM_UNABLE_TO_PROCESS_PACKET);
+ }
+
+ /* Old reauthentication and pseudonym identities must not be used
+ * anymore. In other words, if no new identities are received, full
+ * authentication will be used on next reauthentication. */
+ eap_sim_clear_identities(data, CLEAR_PSEUDONYM | CLEAR_REAUTH_ID |
+ CLEAR_EAP_ID);
+
+ if (attr->encr_data) {
+ if (eap_sim_parse_encr(data->k_encr, attr->encr_data,
+ attr->encr_data_len, attr->iv, &eattr,
+ 0)) {
+ return eap_sim_client_error(
+ sm, data, req, respDataLen,
+ EAP_SIM_UNABLE_TO_PROCESS_PACKET);
+ }
+ eap_sim_learn_ids(data, &eattr);
+ }
+
+ if (data->state != FAILURE)
+ data->state = SUCCESS;
+
+ data->num_id_req = 0;
+ data->num_notification = 0;
+ /* draft-haverinen-pppext-eap-sim-13.txt specifies that counter
+ * is initialized to one after fullauth, but initializing it to
+ * zero makes it easier to implement reauth verification. */
+ data->counter = 0;
+ return eap_sim_response_challenge(sm, data, req, respDataLen);
+}
+
+
+static int eap_sim_process_notification_reauth(struct eap_sim_data *data,
+ struct eap_hdr *req,
+ size_t reqDataLen,
+ struct eap_sim_attrs *attr)
+{
+ struct eap_sim_attrs eattr;
+
+ if (attr->encr_data == NULL || attr->iv == NULL) {
+ wpa_printf(MSG_WARNING, "EAP-SIM: Notification message after "
+ "reauth did not include encrypted data");
+ return -1;
+ }
+
+ if (eap_sim_parse_encr(data->k_encr, attr->encr_data,
+ attr->encr_data_len, attr->iv, &eattr, 0)) {
+ wpa_printf(MSG_WARNING, "EAP-SIM: Failed to parse encrypted "
+ "data from notification message");
+ return -1;
+ }
+
+ if (eattr.counter != data->counter) {
+ wpa_printf(MSG_WARNING, "EAP-SIM: Counter in notification "
+ "message does not match with counter in reauth "
+ "message");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int eap_sim_process_notification_auth(struct eap_sim_data *data,
+ struct eap_hdr *req,
+ size_t reqDataLen,
+ struct eap_sim_attrs *attr)
+{
+ if (attr->mac == NULL) {
+ wpa_printf(MSG_INFO, "EAP-SIM: no AT_MAC in after_auth "
+ "Notification message");
+ return -1;
+ }
+
+ if (eap_sim_verify_mac(data->k_aut, (u8 *) req, reqDataLen, attr->mac,
+ (u8 *) "", 0)) {
+ wpa_printf(MSG_WARNING, "EAP-SIM: Notification message "
+ "used invalid AT_MAC");
+ return -1;
+ }
+
+ if (data->reauth &&
+ eap_sim_process_notification_reauth(data, req, reqDataLen, attr)) {
+ wpa_printf(MSG_WARNING, "EAP-SIM: Invalid notification "
+ "message after reauth");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static u8 * eap_sim_process_notification(struct eap_sm *sm,
+ struct eap_sim_data *data,
+ struct eap_hdr *req,
+ size_t reqDataLen,
+ size_t *respDataLen,
+ struct eap_sim_attrs *attr)
+{
+ wpa_printf(MSG_DEBUG, "EAP-SIM: subtype Notification");
+ if (data->num_notification > 0) {
+ wpa_printf(MSG_INFO, "EAP-SIM: too many notification "
+ "rounds (only one allowed)");
+ return eap_sim_client_error(sm, data, req, respDataLen,
+ EAP_SIM_UNABLE_TO_PROCESS_PACKET);
+ }
+ data->num_notification++;
+ if (attr->notification == -1) {
+ wpa_printf(MSG_INFO, "EAP-SIM: no AT_NOTIFICATION in "
+ "Notification message");
+ return eap_sim_client_error(sm, data, req, respDataLen,
+ EAP_SIM_UNABLE_TO_PROCESS_PACKET);
+ }
+
+ if ((attr->notification & 0x4000) == 0 &&
+ eap_sim_process_notification_auth(data, req, reqDataLen, attr)) {
+ return eap_sim_client_error(sm, data, req, respDataLen,
+ EAP_SIM_UNABLE_TO_PROCESS_PACKET);
+ }
+
+ eap_sim_report_notification(sm->msg_ctx, attr->notification, 0);
+ if (attr->notification >= 0 && attr->notification < 32768) {
+ data->state = FAILURE;
+ }
+ return eap_sim_response_notification(sm, data, req, respDataLen,
+ attr->notification);
+}
+
+
+static u8 * eap_sim_process_reauthentication(struct eap_sm *sm,
+ struct eap_sim_data *data,
+ struct eap_hdr *req,
+ size_t reqDataLen,
+ size_t *respDataLen,
+ struct eap_sim_attrs *attr)
+{
+ struct eap_sim_attrs eattr;
+
+ wpa_printf(MSG_DEBUG, "EAP-SIM: subtype Reauthentication");
+
+ if (data->reauth_id == NULL) {
+ wpa_printf(MSG_WARNING, "EAP-SIM: Server is trying "
+ "reauthentication, but no reauth_id available");
+ return eap_sim_client_error(sm, data, req, respDataLen,
+ EAP_SIM_UNABLE_TO_PROCESS_PACKET);
+ }
+
+ data->reauth = 1;
+ if (eap_sim_verify_mac(data->k_aut, (u8 *) req, reqDataLen,
+ attr->mac, (u8 *) "", 0)) {
+ wpa_printf(MSG_WARNING, "EAP-SIM: Reauthentication "
+ "did not have valid AT_MAC");
+ return eap_sim_client_error(sm, data, req, respDataLen,
+ EAP_SIM_UNABLE_TO_PROCESS_PACKET);
+ }
+
+ if (attr->encr_data == NULL || attr->iv == NULL) {
+ wpa_printf(MSG_WARNING, "EAP-SIM: Reauthentication "
+ "message did not include encrypted data");
+ return eap_sim_client_error(sm, data, req, respDataLen,
+ EAP_SIM_UNABLE_TO_PROCESS_PACKET);
+ }
+
+ if (eap_sim_parse_encr(data->k_encr, attr->encr_data,
+ attr->encr_data_len, attr->iv, &eattr, 0)) {
+ wpa_printf(MSG_WARNING, "EAP-SIM: Failed to parse encrypted "
+ "data from reauthentication message");
+ return eap_sim_client_error(sm, data, req, respDataLen,
+ EAP_SIM_UNABLE_TO_PROCESS_PACKET);
+ }
+
+ if (eattr.nonce_s == NULL || eattr.counter < 0) {
+ wpa_printf(MSG_INFO, "EAP-SIM: (encr) No%s%s in reauth packet",
+ !eattr.nonce_s ? " AT_NONCE_S" : "",
+ eattr.counter < 0 ? " AT_COUNTER" : "");
+ return eap_sim_client_error(sm, data, req, respDataLen,
+ EAP_SIM_UNABLE_TO_PROCESS_PACKET);
+ }
+
+ if (eattr.counter <= data->counter) {
+ wpa_printf(MSG_INFO, "EAP-SIM: (encr) Invalid counter "
+ "(%d <= %d)", eattr.counter, data->counter);
+ data->counter_too_small = eattr.counter;
+ /* Reply using Re-auth w/ AT_COUNTER_TOO_SMALL. The current
+ * reauth_id must not be used to start a new reauthentication.
+ * However, since it was used in the last EAP-Response-Identity
+ * packet, it has to saved for the following fullauth to be
+ * used in MK derivation. */
+ free(data->last_eap_identity);
+ data->last_eap_identity = data->reauth_id;
+ data->last_eap_identity_len = data->reauth_id_len;
+ data->reauth_id = NULL;
+ data->reauth_id_len = 0;
+ return eap_sim_response_reauth(sm, data, req, respDataLen, 1);
+ }
+ data->counter = eattr.counter;
+
+ memcpy(data->nonce_s, eattr.nonce_s, EAP_SIM_NONCE_S_LEN);
+ wpa_hexdump(MSG_DEBUG, "EAP-SIM: (encr) AT_NONCE_S",
+ data->nonce_s, EAP_SIM_NONCE_S_LEN);
+
+ eap_sim_derive_keys_reauth(data->counter,
+ data->reauth_id, data->reauth_id_len,
+ data->nonce_s, data->mk, data->msk);
+ eap_sim_clear_identities(data, CLEAR_REAUTH_ID | CLEAR_EAP_ID);
+ eap_sim_learn_ids(data, &eattr);
+
+ if (data->state != FAILURE)
+ data->state = SUCCESS;
+
+ data->num_id_req = 0;
+ data->num_notification = 0;
+ 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);
+ }
+ return eap_sim_response_reauth(sm, data, req, respDataLen, 0);
+}
+
+
+static u8 * eap_sim_process(struct eap_sm *sm, void *priv,
+ struct eap_method_ret *ret,
+ u8 *reqData, size_t reqDataLen,
+ size_t *respDataLen)
+{
+ struct eap_sim_data *data = priv;
+ struct wpa_ssid *config = eap_get_config(sm);
+ struct eap_hdr *req;
+ u8 *pos, subtype, *res;
+ struct eap_sim_attrs attr;
+ size_t len;
+
+ wpa_hexdump(MSG_DEBUG, "EAP-SIM: EAP data", reqData, reqDataLen);
+ if (config == NULL || config->identity == NULL) {
+ wpa_printf(MSG_INFO, "EAP-SIM: Identity not configured");
+ eap_sm_request_identity(sm, config);
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ req = (struct eap_hdr *) reqData;
+ pos = (u8 *) (req + 1);
+ if (reqDataLen < sizeof(*req) + 4 || *pos != EAP_TYPE_SIM ||
+ (len = be_to_host16(req->length)) > reqDataLen) {
+ wpa_printf(MSG_INFO, "EAP-SIM: Invalid frame");
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ ret->ignore = FALSE;
+ ret->methodState = METHOD_CONT;
+ ret->decision = DECISION_FAIL;
+ ret->allowNotifications = TRUE;
+
+ pos++;
+ subtype = *pos++;
+ wpa_printf(MSG_DEBUG, "EAP-SIM: Subtype=%d", subtype);
+ pos += 2; /* Reserved */
+
+ if (eap_sim_parse_attr(pos, reqData + len, &attr, 0, 0)) {
+ res = eap_sim_client_error(sm, data, req, respDataLen,
+ EAP_SIM_UNABLE_TO_PROCESS_PACKET);
+ goto done;
+ }
+
+ switch (subtype) {
+ case EAP_SIM_SUBTYPE_START:
+ res = eap_sim_process_start(sm, data, req, len,
+ respDataLen, &attr);
+ break;
+ case EAP_SIM_SUBTYPE_CHALLENGE:
+ res = eap_sim_process_challenge(sm, data, req, len,
+ respDataLen, &attr);
+ break;
+ case EAP_SIM_SUBTYPE_NOTIFICATION:
+ res = eap_sim_process_notification(sm, data, req, len,
+ respDataLen, &attr);
+ break;
+ case EAP_SIM_SUBTYPE_REAUTHENTICATION:
+ res = eap_sim_process_reauthentication(sm, data, req, len,
+ respDataLen, &attr);
+ break;
+ case EAP_SIM_SUBTYPE_CLIENT_ERROR:
+ wpa_printf(MSG_DEBUG, "EAP-SIM: subtype Client-Error");
+ res = eap_sim_client_error(sm, data, req, respDataLen,
+ EAP_SIM_UNABLE_TO_PROCESS_PACKET);
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "EAP-SIM: Unknown subtype=%d", subtype);
+ res = eap_sim_client_error(sm, data, req, respDataLen,
+ EAP_SIM_UNABLE_TO_PROCESS_PACKET);
+ break;
+ }
+
+done:
+ if (data->state == FAILURE) {
+ ret->decision = DECISION_FAIL;
+ ret->methodState = METHOD_DONE;
+ } else if (data->state == SUCCESS) {
+ ret->decision = DECISION_UNCOND_SUCC;
+ ret->methodState = METHOD_DONE;
+ }
+
+ if (ret->methodState == METHOD_DONE) {
+ ret->allowNotifications = FALSE;
+ }
+
+ return res;
+}
+
+
+static Boolean eap_sim_has_reauth_data(struct eap_sm *sm, void *priv)
+{
+ struct eap_sim_data *data = priv;
+ return data->pseudonym || data->reauth_id;
+}
+
+
+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);
+}
+
+
+static void * eap_sim_init_for_reauth(struct eap_sm *sm, void *priv)
+{
+ struct eap_sim_data *data = priv;
+ if (hostapd_get_rand(data->nonce_mt, EAP_SIM_NONCE_MT_LEN)) {
+ wpa_printf(MSG_WARNING, "EAP-SIM: Failed to get random data "
+ "for NONCE_MT");
+ free(data);
+ return NULL;
+ }
+ data->num_id_req = 0;
+ data->num_notification = 0;
+ data->state = CONTINUE;
+ return priv;
+}
+
+
+static const u8 * eap_sim_get_identity(struct eap_sm *sm, void *priv,
+ size_t *len)
+{
+ struct eap_sim_data *data = priv;
+
+ if (data->reauth_id) {
+ *len = data->reauth_id_len;
+ return data->reauth_id;
+ }
+
+ if (data->pseudonym) {
+ *len = data->pseudonym_len;
+ return data->pseudonym;
+ }
+
+ return NULL;
+}
+
+
+static Boolean eap_sim_isKeyAvailable(struct eap_sm *sm, void *priv)
+{
+ struct eap_sim_data *data = priv;
+ return data->state == SUCCESS;
+}
+
+
+static u8 * eap_sim_getKey(struct eap_sm *sm, void *priv, size_t *len)
+{
+ struct eap_sim_data *data = priv;
+ u8 *key;
+
+ if (data->state != SUCCESS)
+ return NULL;
+
+ key = malloc(EAP_SIM_KEYING_DATA_LEN);
+ if (key == NULL)
+ return NULL;
+
+ *len = EAP_SIM_KEYING_DATA_LEN;
+ memcpy(key, data->msk, EAP_SIM_KEYING_DATA_LEN);
+
+ return key;
+}
+
+
+const struct eap_method eap_method_sim =
+{
+ .method = EAP_TYPE_SIM,
+ .name = "SIM",
+ .init = eap_sim_init,
+ .deinit = eap_sim_deinit,
+ .process = eap_sim_process,
+ .isKeyAvailable = eap_sim_isKeyAvailable,
+ .getKey = eap_sim_getKey,
+ .has_reauth_data = eap_sim_has_reauth_data,
+ .deinit_for_reauth = eap_sim_deinit_for_reauth,
+ .init_for_reauth = eap_sim_init_for_reauth,
+ .get_identity = eap_sim_get_identity,
+};
diff --git a/contrib/wpa_supplicant/eap_sim_common.c b/contrib/wpa_supplicant/eap_sim_common.c
new file mode 100644
index 000000000000..98f4fb7d3014
--- /dev/null
+++ b/contrib/wpa_supplicant/eap_sim_common.c
@@ -0,0 +1,788 @@
+/*
+ * WPA Supplicant / EAP-SIM/AKA shared routines
+ * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published 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 <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "common.h"
+#include "eap_i.h"
+#include "sha1.h"
+#include "aes_wrap.h"
+#include "eap_sim_common.h"
+
+
+#define MSK_LEN 8
+#define EMSK_LEN 8
+
+
+static void eap_sim_prf(const u8 *key, u8 *x, size_t xlen)
+{
+ u8 xkey[64];
+ u32 t[5], _t[5];
+ int i, j, m, k;
+ u8 *xpos = x;
+ u32 carry;
+
+ /* FIPS 186-2 + change notice 1 */
+
+ memcpy(xkey, key, EAP_SIM_MK_LEN);
+ memset(xkey + EAP_SIM_MK_LEN, 0, 64 - EAP_SIM_MK_LEN);
+ t[0] = 0x67452301;
+ t[1] = 0xEFCDAB89;
+ t[2] = 0x98BADCFE;
+ t[3] = 0x10325476;
+ t[4] = 0xC3D2E1F0;
+
+ m = xlen / 40;
+ for (j = 0; j < m; j++) {
+ /* XSEED_j = 0 */
+ for (i = 0; i < 2; i++) {
+ /* XVAL = (XKEY + XSEED_j) mod 2^b */
+
+ /* w_i = G(t, XVAL) */
+ memcpy(_t, t, 20);
+ sha1_transform((u8 *) _t, xkey);
+ _t[0] = host_to_be32(_t[0]);
+ _t[1] = host_to_be32(_t[1]);
+ _t[2] = host_to_be32(_t[2]);
+ _t[3] = host_to_be32(_t[3]);
+ _t[4] = host_to_be32(_t[4]);
+ memcpy(xpos, _t, 20);
+
+ /* XKEY = (1 + XKEY + w_i) mod 2^b */
+ carry = 1;
+ for (k = 19; k >= 0; k--) {
+ carry += xkey[k] + xpos[k];
+ xkey[k] = carry & 0xff;
+ carry >>= 8;
+ }
+
+ xpos += SHA1_MAC_LEN;
+ }
+ /* x_j = w_0|w_1 */
+ }
+}
+
+
+void eap_sim_derive_keys(const u8 *mk, u8 *k_encr, u8 *k_aut, u8 *msk)
+{
+ u8 buf[120], *pos;
+ eap_sim_prf(mk, buf, 120);
+ pos = buf;
+ memcpy(k_encr, pos, EAP_SIM_K_ENCR_LEN);
+ pos += EAP_SIM_K_ENCR_LEN;
+ memcpy(k_aut, pos, EAP_SIM_K_AUT_LEN);
+ pos += EAP_SIM_K_AUT_LEN;
+ memcpy(msk, pos, EAP_SIM_KEYING_DATA_LEN);
+ pos += MSK_LEN;
+
+ wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: K_encr",
+ k_encr, EAP_SIM_K_ENCR_LEN);
+ wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: K_aut",
+ k_aut, EAP_SIM_K_ENCR_LEN);
+ wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: MSK",
+ msk, MSK_LEN);
+ wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: Ext. MSK",
+ msk + MSK_LEN, EMSK_LEN);
+ wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: keying material",
+ msk, EAP_SIM_KEYING_DATA_LEN);
+}
+
+
+void eap_sim_derive_keys_reauth(unsigned int _counter,
+ const u8 *identity, size_t identity_len,
+ const u8 *nonce_s, const u8 *mk, u8 *msk)
+{
+ u8 xkey[SHA1_MAC_LEN];
+ u8 counter[2];
+ const u8 *addr[4];
+ size_t len[4];
+
+ addr[0] = identity;
+ len[0] = identity_len;
+ addr[1] = counter;
+ len[1] = 2;
+ addr[2] = nonce_s;
+ len[2] = EAP_SIM_NONCE_S_LEN;
+ addr[3] = mk;
+ len[3] = EAP_SIM_MK_LEN;
+
+ counter[0] = _counter >> 8;
+ counter[1] = _counter & 0xff;
+
+ wpa_printf(MSG_DEBUG, "EAP-SIM: Deriving keying data from reauth");
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM: Identity",
+ identity, identity_len);
+ wpa_hexdump(MSG_DEBUG, "EAP-SIM: counter", counter, 2);
+ wpa_hexdump(MSG_DEBUG, "EAP-SIM: NONCE_S", nonce_s,
+ EAP_SIM_NONCE_S_LEN);
+ wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: MK", mk, EAP_SIM_MK_LEN);
+
+ /* XKEY' = SHA1(Identity|counter|NONCE_S|MK) */
+ sha1_vector(4, addr, len, xkey);
+ wpa_hexdump(MSG_DEBUG, "EAP-SIM: XKEY'", xkey, SHA1_MAC_LEN);
+
+ eap_sim_prf(xkey, msk, EAP_SIM_KEYING_DATA_LEN);
+ wpa_hexdump(MSG_DEBUG, "EAP-SIM: MSK", msk, MSK_LEN);
+ wpa_hexdump(MSG_DEBUG, "EAP-SIM: Ext. MSK", msk + MSK_LEN, EMSK_LEN);
+ wpa_hexdump(MSG_DEBUG, "EAP-SIM: keying material",
+ msk, EAP_SIM_KEYING_DATA_LEN);
+}
+
+
+int eap_sim_verify_mac(const u8 *k_aut, u8 *req, size_t req_len, u8 *mac,
+ u8 *extra, size_t extra_len)
+{
+ unsigned char hmac[SHA1_MAC_LEN];
+ const u8 *addr[2];
+ size_t len[2];
+ u8 rx_mac[EAP_SIM_MAC_LEN];
+
+ if (mac == NULL)
+ return -1;
+
+ addr[0] = req;
+ len[0] = req_len;
+ addr[1] = extra;
+ len[1] = extra_len;
+
+ /* HMAC-SHA1-128 */
+ memcpy(rx_mac, mac, EAP_SIM_MAC_LEN);
+ memset(mac, 0, EAP_SIM_MAC_LEN);
+ hmac_sha1_vector(k_aut, EAP_SIM_K_AUT_LEN, 2, addr, len, hmac);
+ memcpy(mac, rx_mac, EAP_SIM_MAC_LEN);
+
+ return (memcmp(hmac, mac, EAP_SIM_MAC_LEN) == 0) ? 0 : 1;
+}
+
+
+void eap_sim_add_mac(const u8 *k_aut, u8 *msg, size_t msg_len, u8 *mac,
+ const u8 *extra, size_t extra_len)
+{
+ unsigned char hmac[SHA1_MAC_LEN];
+ const u8 *addr[2];
+ size_t len[2];
+
+ addr[0] = msg;
+ len[0] = msg_len;
+ addr[1] = extra;
+ len[1] = extra_len;
+
+ /* HMAC-SHA1-128 */
+ memset(mac, 0, EAP_SIM_MAC_LEN);
+ hmac_sha1_vector(k_aut, EAP_SIM_K_AUT_LEN, 2, addr, len, hmac);
+ memcpy(mac, hmac, EAP_SIM_MAC_LEN);
+}
+
+
+int eap_sim_parse_attr(u8 *start, u8 *end, struct eap_sim_attrs *attr, int aka,
+ int encr)
+{
+ u8 *pos = start, *apos;
+ size_t alen, plen;
+ int list_len, i;
+
+ memset(attr, 0, sizeof(*attr));
+ attr->id_req = NO_ID_REQ;
+ attr->notification = -1;
+ attr->counter = -1;
+ attr->selected_version = -1;
+ attr->client_error_code = -1;
+
+ while (pos < end) {
+ if (pos + 2 > end) {
+ wpa_printf(MSG_INFO, "EAP-SIM: Attribute overflow(1)");
+ return -1;
+ }
+ wpa_printf(MSG_MSGDUMP, "EAP-SIM: Attribute: Type=%d Len=%d",
+ pos[0], pos[1] * 4);
+ if (pos + pos[1] * 4 > end) {
+ wpa_printf(MSG_INFO, "EAP-SIM: Attribute overflow "
+ "(pos=%p len=%d end=%p)",
+ pos, pos[1] * 4, end);
+ return -1;
+ }
+ apos = pos + 2;
+ alen = pos[1] * 4 - 2;
+ wpa_hexdump(MSG_MSGDUMP, "EAP-SIM: Attribute data",
+ apos, alen);
+
+ switch (pos[0]) {
+ case EAP_SIM_AT_RAND:
+ wpa_printf(MSG_DEBUG, "EAP-SIM: AT_RAND");
+ apos += 2;
+ alen -= 2;
+ if ((!aka && (alen % GSM_RAND_LEN)) ||
+ (aka && alen != AKA_RAND_LEN)) {
+ wpa_printf(MSG_INFO, "EAP-SIM: Invalid AT_RAND"
+ " (len %lu)",
+ (unsigned long) alen);
+ return -1;
+ }
+ attr->rand = apos;
+ attr->num_chal = alen / GSM_RAND_LEN;
+ break;
+ case EAP_SIM_AT_AUTN:
+ wpa_printf(MSG_DEBUG, "EAP-AKA: AT_AUTN");
+ if (!aka) {
+ wpa_printf(MSG_DEBUG, "EAP-SIM: "
+ "Unexpected AT_AUTN");
+ return -1;
+ }
+ apos += 2;
+ alen -= 2;
+ if (alen != AKA_AUTN_LEN) {
+ wpa_printf(MSG_INFO, "EAP-AKA: Invalid AT_AUTN"
+ " (len %lu)",
+ (unsigned long) alen);
+ return -1;
+ }
+ attr->autn = apos;
+ break;
+ case EAP_SIM_AT_PADDING:
+ if (!encr) {
+ wpa_printf(MSG_ERROR, "EAP-SIM: Unencrypted "
+ "AT_PADDING");
+ return -1;
+ }
+ wpa_printf(MSG_DEBUG, "EAP-SIM: (encr) AT_PADDING");
+ for (i = 2; i < alen; i++) {
+ if (apos[i] != 0) {
+ wpa_printf(MSG_INFO, "EAP-SIM: (encr) "
+ "AT_PADDING used a non-zero"
+ " padding byte");
+ wpa_hexdump(MSG_DEBUG, "EAP-SIM: "
+ "(encr) padding bytes",
+ apos + 2, alen - 2);
+ return -1;
+ }
+ }
+ break;
+ case EAP_SIM_AT_NONCE_MT:
+ wpa_printf(MSG_DEBUG, "EAP-SIM: AT_NONCE_MT");
+ if (alen != 2 + EAP_SIM_NONCE_MT_LEN) {
+ wpa_printf(MSG_INFO, "EAP-SIM: Invalid "
+ "AT_NONCE_MT length");
+ return -1;
+ }
+ attr->nonce_mt = apos + 2;
+ break;
+ case EAP_SIM_AT_PERMANENT_ID_REQ:
+ wpa_printf(MSG_DEBUG, "EAP-SIM: AT_PERMANENT_ID_REQ");
+ attr->id_req = PERMANENT_ID;
+ break;
+ case EAP_SIM_AT_MAC:
+ wpa_printf(MSG_DEBUG, "EAP-SIM: AT_MAC");
+ if (alen != 2 + EAP_SIM_MAC_LEN) {
+ wpa_printf(MSG_INFO, "EAP-SIM: Invalid AT_MAC "
+ "length");
+ return -1;
+ }
+ attr->mac = apos + 2;
+ break;
+ case EAP_SIM_AT_NOTIFICATION:
+ if (alen != 2) {
+ wpa_printf(MSG_INFO, "EAP-SIM: Invalid "
+ "AT_NOTIFICATION length %lu",
+ (unsigned long) alen);
+ return -1;
+ }
+ attr->notification = apos[0] * 256 + apos[1];
+ wpa_printf(MSG_DEBUG, "EAP-SIM: AT_NOTIFICATION %d",
+ attr->notification);
+ break;
+ case EAP_SIM_AT_ANY_ID_REQ:
+ wpa_printf(MSG_DEBUG, "EAP-SIM: AT_ANY_ID_REQ");
+ attr->id_req = ANY_ID;
+ break;
+ case EAP_SIM_AT_IDENTITY:
+ wpa_printf(MSG_DEBUG, "EAP-SIM: AT_IDENTITY");
+ attr->identity = apos + 2;
+ attr->identity_len = alen - 2;
+ break;
+ case EAP_SIM_AT_VERSION_LIST:
+ if (aka) {
+ wpa_printf(MSG_DEBUG, "EAP-AKA: "
+ "Unexpected AT_VERSION_LIST");
+ return -1;
+ }
+ list_len = apos[0] * 256 + apos[1];
+ wpa_printf(MSG_DEBUG, "EAP-SIM: AT_VERSION_LIST");
+ if (list_len < 2 || list_len > alen - 2) {
+ wpa_printf(MSG_WARNING, "EAP-SIM: Invalid "
+ "AT_VERSION_LIST (list_len=%d "
+ "attr_len=%lu)", list_len,
+ (unsigned long) alen);
+ return -1;
+ }
+ attr->version_list = apos + 2;
+ attr->version_list_len = list_len;
+ break;
+ case EAP_SIM_AT_SELECTED_VERSION:
+ wpa_printf(MSG_DEBUG, "EAP-SIM: AT_SELECTED_VERSION");
+ if (alen != 2) {
+ wpa_printf(MSG_INFO, "EAP-SIM: Invalid "
+ "AT_SELECTED_VERSION length %lu",
+ (unsigned long) alen);
+ return -1;
+ }
+ attr->selected_version = apos[0] * 256 + apos[1];
+ wpa_printf(MSG_DEBUG, "EAP-SIM: AT_SELECTED_VERSION "
+ "%d", attr->selected_version);
+ break;
+ case EAP_SIM_AT_FULLAUTH_ID_REQ:
+ wpa_printf(MSG_DEBUG, "EAP-SIM: AT_FULLAUTH_ID_REQ");
+ attr->id_req = FULLAUTH_ID;
+ break;
+ case EAP_SIM_AT_COUNTER:
+ if (!encr) {
+ wpa_printf(MSG_ERROR, "EAP-SIM: Unencrypted "
+ "AT_COUNTER");
+ return -1;
+ }
+ if (alen != 2) {
+ wpa_printf(MSG_INFO, "EAP-SIM: (encr) Invalid "
+ "AT_COUNTER (alen=%lu)",
+ (unsigned long) alen);
+ return -1;
+ }
+ attr->counter = apos[0] * 256 + apos[1];
+ wpa_printf(MSG_DEBUG, "EAP-SIM: (encr) AT_COUNTER %d",
+ attr->counter);
+ break;
+ case EAP_SIM_AT_NONCE_S:
+ if (!encr) {
+ wpa_printf(MSG_ERROR, "EAP-SIM: Unencrypted "
+ "AT_NONCE_S");
+ return -1;
+ }
+ wpa_printf(MSG_DEBUG, "EAP-SIM: (encr) "
+ "AT_NONCE_S");
+ if (alen != 2 + EAP_SIM_NONCE_S_LEN) {
+ wpa_printf(MSG_INFO, "EAP-SIM: (encr) Invalid "
+ "AT_NONCE_S (alen=%lu)",
+ (unsigned long) alen);
+ return -1;
+ }
+ attr->nonce_s = apos + 2;
+ break;
+ case EAP_SIM_AT_CLIENT_ERROR_CODE:
+ if (alen != 2) {
+ wpa_printf(MSG_INFO, "EAP-SIM: Invalid "
+ "AT_CLIENT_ERROR_CODE length %lu",
+ (unsigned long) alen);
+ return -1;
+ }
+ attr->client_error_code = apos[0] * 256 + apos[1];
+ wpa_printf(MSG_DEBUG, "EAP-SIM: AT_CLIENT_ERROR_CODE "
+ "%d", attr->client_error_code);
+ break;
+ case EAP_SIM_AT_IV:
+ wpa_printf(MSG_DEBUG, "EAP-SIM: AT_IV");
+ if (alen != 2 + EAP_SIM_MAC_LEN) {
+ wpa_printf(MSG_INFO, "EAP-SIM: Invalid AT_IV "
+ "length %lu", (unsigned long) alen);
+ return -1;
+ }
+ attr->iv = apos + 2;
+ break;
+ case EAP_SIM_AT_ENCR_DATA:
+ wpa_printf(MSG_DEBUG, "EAP-SIM: AT_ENCR_DATA");
+ attr->encr_data = apos + 2;
+ attr->encr_data_len = alen - 2;
+ if (attr->encr_data_len % 16) {
+ wpa_printf(MSG_INFO, "EAP-SIM: Invalid "
+ "AT_ENCR_DATA length %lu",
+ (unsigned long)
+ attr->encr_data_len);
+ return -1;
+ }
+ break;
+ case EAP_SIM_AT_NEXT_PSEUDONYM:
+ if (!encr) {
+ wpa_printf(MSG_ERROR, "EAP-SIM: Unencrypted "
+ "AT_NEXT_PSEUDONYM");
+ return -1;
+ }
+ wpa_printf(MSG_DEBUG, "EAP-SIM: (encr) "
+ "AT_NEXT_PSEUDONYM");
+ plen = apos[0] * 256 + apos[1];
+ if (plen > alen - 2) {
+ wpa_printf(MSG_INFO, "EAP-SIM: (encr) Invalid"
+ " AT_NEXT_PSEUDONYM (actual"
+ " len %lu, attr len %lu)",
+ (unsigned long) plen,
+ (unsigned long) alen);
+ return -1;
+ }
+ attr->next_pseudonym = pos + 4;
+ attr->next_pseudonym_len = plen;
+ break;
+ case EAP_SIM_AT_NEXT_REAUTH_ID:
+ if (!encr) {
+ wpa_printf(MSG_ERROR, "EAP-SIM: Unencrypted "
+ "AT_NEXT_REAUTH_ID");
+ return -1;
+ }
+ wpa_printf(MSG_DEBUG, "EAP-SIM: (encr) "
+ "AT_NEXT_REAUTH_ID");
+ plen = apos[0] * 256 + apos[1];
+ if (plen > alen - 2) {
+ wpa_printf(MSG_INFO, "EAP-SIM: (encr) Invalid"
+ " AT_NEXT_REAUTH_ID (actual"
+ " len %lu, attr len %lu)",
+ (unsigned long) plen,
+ (unsigned long) alen);
+ return -1;
+ }
+ attr->next_reauth_id = pos + 4;
+ attr->next_reauth_id_len = plen;
+ break;
+ default:
+ if (pos[0] < 128) {
+ wpa_printf(MSG_INFO, "EAP-SIM: Unrecognized "
+ "non-skippable attribute %d",
+ pos[0]);
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "EAP-SIM: Unrecognized skippable"
+ " attribute %d ignored", pos[0]);
+ break;
+ }
+
+ pos += pos[1] * 4;
+ }
+
+ wpa_printf(MSG_DEBUG, "EAP-SIM: Attributes parsed successfully "
+ "(aka=%d encr=%d)", aka, encr);
+
+ return 0;
+}
+
+
+int eap_sim_parse_encr(const u8 *k_encr, u8 *encr_data, size_t encr_data_len,
+ const u8 *iv, struct eap_sim_attrs *attr, int aka)
+{
+ if (!iv) {
+ wpa_printf(MSG_INFO, "EAP-SIM: Encrypted data, but no IV");
+ return -1;
+ }
+ aes_128_cbc_decrypt(k_encr, iv, encr_data, encr_data_len);
+ wpa_hexdump(MSG_MSGDUMP, "EAP-SIM: Decrypted AT_ENCR_DATA",
+ encr_data, encr_data_len);
+
+ if (eap_sim_parse_attr(encr_data, encr_data + encr_data_len, attr,
+ aka, 1)) {
+ wpa_printf(MSG_INFO, "EAP-SIM: (encr) Failed to parse "
+ "decrypted AT_ENCR_DATA");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+#define EAP_SIM_INIT_LEN 128
+
+struct eap_sim_msg {
+ u8 *buf;
+ size_t buf_len, used;
+ size_t mac, iv, encr; /* index from buf */
+};
+
+
+struct eap_sim_msg * eap_sim_msg_init(int code, int id, int type, int subtype)
+{
+ struct eap_sim_msg *msg;
+ struct eap_hdr *eap;
+ u8 *pos;
+
+ msg = malloc(sizeof(*msg));
+ if (msg == NULL)
+ return NULL;
+ memset(msg, 0, sizeof(*msg));
+
+ msg->buf = malloc(EAP_SIM_INIT_LEN);
+ if (msg->buf == NULL) {
+ free(msg);
+ return NULL;
+ }
+ memset(msg->buf, 0, EAP_SIM_INIT_LEN);
+ msg->buf_len = EAP_SIM_INIT_LEN;
+ eap = (struct eap_hdr *) msg->buf;
+ eap->code = code;
+ eap->identifier = id;
+ msg->used = sizeof(*eap);
+
+ pos = (u8 *) (eap + 1);
+ *pos++ = type;
+ *pos++ = subtype;
+ *pos++ = 0; /* Reserved */
+ *pos++ = 0; /* Reserved */
+ msg->used += 4;
+
+ return msg;
+}
+
+
+u8 * eap_sim_msg_finish(struct eap_sim_msg *msg, size_t *len, const u8 *k_aut,
+ const u8 *extra, size_t extra_len)
+{
+ struct eap_hdr *eap;
+ u8 *buf;
+
+ if (msg == NULL)
+ return NULL;
+
+ eap = (struct eap_hdr *) msg->buf;
+ eap->length = host_to_be16(msg->used);
+
+ if (k_aut && msg->mac) {
+ eap_sim_add_mac(k_aut, msg->buf, msg->used,
+ msg->buf + msg->mac, extra, extra_len);
+ }
+
+ *len = msg->used;
+ buf = msg->buf;
+ free(msg);
+ return buf;
+}
+
+
+void eap_sim_msg_free(struct eap_sim_msg *msg)
+{
+ if (msg) {
+ free(msg->buf);
+ free(msg);
+ }
+}
+
+
+static int eap_sim_msg_resize(struct eap_sim_msg *msg, size_t add_len)
+{
+ if (msg->used + add_len > msg->buf_len) {
+ u8 *nbuf = realloc(msg->buf, msg->used + add_len);
+ if (nbuf == NULL)
+ return -1;
+ msg->buf = nbuf;
+ msg->buf_len = msg->used + add_len;
+ }
+ return 0;
+}
+
+
+u8 * eap_sim_msg_add_full(struct eap_sim_msg *msg, u8 attr,
+ const u8 *data, size_t len)
+{
+ int attr_len = 2 + len;
+ int pad_len;
+ u8 *start, *pos;
+
+ if (msg == NULL)
+ return NULL;
+
+ pad_len = (4 - attr_len % 4) % 4;
+ attr_len += pad_len;
+ if (eap_sim_msg_resize(msg, attr_len))
+ return NULL;
+ start = pos = msg->buf + msg->used;
+ *pos++ = attr;
+ *pos++ = attr_len / 4;
+ memcpy(pos, data, len);
+ if (pad_len) {
+ pos += len;
+ memset(pos, 0, pad_len);
+ }
+ msg->used += attr_len;
+ return start;
+}
+
+
+u8 * eap_sim_msg_add(struct eap_sim_msg *msg, u8 attr, u16 value,
+ const u8 *data, size_t len)
+{
+ int attr_len = 4 + len;
+ int pad_len;
+ u8 *start, *pos;
+
+ if (msg == NULL)
+ return NULL;
+
+ pad_len = (4 - attr_len % 4) % 4;
+ attr_len += pad_len;
+ if (eap_sim_msg_resize(msg, attr_len))
+ return NULL;
+ start = pos = msg->buf + msg->used;
+ *pos++ = attr;
+ *pos++ = attr_len / 4;
+ *pos++ = value >> 8;
+ *pos++ = value & 0xff;
+ if (data)
+ memcpy(pos, data, len);
+ if (pad_len) {
+ pos += len;
+ memset(pos, 0, pad_len);
+ }
+ msg->used += attr_len;
+ return start;
+}
+
+
+u8 * eap_sim_msg_add_mac(struct eap_sim_msg *msg, u8 attr)
+{
+ u8 *pos = eap_sim_msg_add(msg, attr, 0, NULL, EAP_SIM_MAC_LEN);
+ if (pos)
+ msg->mac = (pos - msg->buf) + 4;
+ return pos;
+}
+
+
+int eap_sim_msg_add_encr_start(struct eap_sim_msg *msg, u8 attr_iv,
+ u8 attr_encr)
+{
+ u8 *pos = eap_sim_msg_add(msg, attr_iv, 0, NULL, EAP_SIM_IV_LEN);
+ if (pos == NULL)
+ return -1;
+ msg->iv = (pos - msg->buf) + 4;
+ if (hostapd_get_rand(msg->buf + msg->iv, EAP_SIM_IV_LEN)) {
+ msg->iv = 0;
+ return -1;
+ }
+
+ pos = eap_sim_msg_add(msg, attr_encr, 0, NULL, 0);
+ if (pos == NULL) {
+ msg->iv = 0;
+ return -1;
+ }
+ msg->encr = pos - msg->buf;
+
+ return 0;
+}
+
+
+int eap_sim_msg_add_encr_end(struct eap_sim_msg *msg, u8 *k_encr, int attr_pad)
+{
+ size_t encr_len;
+
+ if (k_encr == NULL || msg->iv == 0 || msg->encr == 0)
+ return -1;
+
+ encr_len = msg->used - msg->encr - 4;
+ if (encr_len % 16) {
+ u8 *pos;
+ int pad_len = 16 - (encr_len % 16);
+ if (pad_len < 4) {
+ wpa_printf(MSG_WARNING, "EAP-SIM: "
+ "eap_sim_msg_add_encr_end - invalid pad_len"
+ " %d", pad_len);
+ return -1;
+ }
+ wpa_printf(MSG_DEBUG, " *AT_PADDING");
+ pos = eap_sim_msg_add(msg, attr_pad, 0, NULL, pad_len - 4);
+ if (pos == NULL)
+ return -1;
+ memset(pos + 4, 0, pad_len - 4);
+ encr_len += pad_len;
+ }
+ wpa_printf(MSG_DEBUG, " (AT_ENCR_DATA data len %lu)",
+ (unsigned long) encr_len);
+ msg->buf[msg->encr + 1] = encr_len / 4 + 1;
+ aes_128_cbc_encrypt(k_encr, msg->buf + msg->iv,
+ msg->buf + msg->encr + 4, encr_len);
+
+ return 0;
+}
+
+
+void eap_sim_report_notification(void *msg_ctx, int notification, int aka)
+{
+ const char *type = aka ? "AKA" : "SIM";
+
+ switch (notification) {
+ case EAP_SIM_GENERAL_FAILURE_AFTER_AUTH:
+ wpa_printf(MSG_WARNING, "EAP-%s: General failure "
+ "notification (after authentication)", type);
+ break;
+ case EAP_SIM_TEMPORARILY_DENIED:
+ wpa_printf(MSG_WARNING, "EAP-%s: Failure notification: "
+ "User has been temporarily denied access to the "
+ "requested service", type);
+ break;
+ case EAP_SIM_NOT_SUBSCRIBED:
+ wpa_printf(MSG_WARNING, "EAP-%s: Failure notification: "
+ "User has not subscribed to the requested service",
+ type);
+ break;
+ case EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH:
+ wpa_printf(MSG_WARNING, "EAP-%s: General failure "
+ "notification (before authentication)", type);
+ break;
+ case EAP_SIM_SUCCESS:
+ wpa_printf(MSG_INFO, "EAP-%s: Successful authentication "
+ "notification", type);
+ break;
+ default:
+ if (notification >= 32768) {
+ wpa_printf(MSG_INFO, "EAP-%s: Unrecognized "
+ "non-failure notification %d",
+ type, notification);
+ } else {
+ wpa_printf(MSG_WARNING, "EAP-%s: Unrecognized "
+ "failure notification %d",
+ type, notification);
+ }
+ }
+}
+
+
+#ifdef TEST_MAIN_EAP_SIM_COMMON
+static int test_eap_sim_prf(void)
+{
+ /* http://csrc.nist.gov/encryption/dss/Examples-1024bit.pdf */
+ u8 xkey[] = {
+ 0xbd, 0x02, 0x9b, 0xbe, 0x7f, 0x51, 0x96, 0x0b,
+ 0xcf, 0x9e, 0xdb, 0x2b, 0x61, 0xf0, 0x6f, 0x0f,
+ 0xeb, 0x5a, 0x38, 0xb6
+ };
+ u8 w[] = {
+ 0x20, 0x70, 0xb3, 0x22, 0x3d, 0xba, 0x37, 0x2f,
+ 0xde, 0x1c, 0x0f, 0xfc, 0x7b, 0x2e, 0x3b, 0x49,
+ 0x8b, 0x26, 0x06, 0x14, 0x3c, 0x6c, 0x18, 0xba,
+ 0xcb, 0x0f, 0x6c, 0x55, 0xba, 0xbb, 0x13, 0x78,
+ 0x8e, 0x20, 0xd7, 0x37, 0xa3, 0x27, 0x51, 0x16
+ };
+ u8 buf[40];
+
+ 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)) {
+ printf("eap_sim_prf failed\n");
+ return 1;
+ }
+
+ return 0;
+}
+
+
+int main(int argc, char *argv[])
+{
+ int errors = 0;
+
+ errors += test_eap_sim_prf();
+
+ return errors;
+}
+#endif /* TEST_MAIN_EAP_SIM_COMMON */
diff --git a/contrib/wpa_supplicant/eap_sim_common.h b/contrib/wpa_supplicant/eap_sim_common.h
new file mode 100644
index 000000000000..c89e04e410b9
--- /dev/null
+++ b/contrib/wpa_supplicant/eap_sim_common.h
@@ -0,0 +1,101 @@
+#ifndef EAP_SIM_COMMON_H
+#define EAP_SIM_COMMON_H
+
+#define EAP_SIM_NONCE_S_LEN 16
+#define EAP_SIM_NONCE_MT_LEN 16
+#define EAP_SIM_MAC_LEN 16
+#define EAP_SIM_MK_LEN 20
+#define EAP_SIM_K_AUT_LEN 16
+#define EAP_SIM_K_ENCR_LEN 16
+#define EAP_SIM_KEYING_DATA_LEN 64
+#define EAP_SIM_IV_LEN 16
+
+#define GSM_RAND_LEN 16
+
+#define AKA_RAND_LEN 16
+#define AKA_AUTN_LEN 16
+
+void eap_sim_derive_keys(const u8 *mk, u8 *k_encr, u8 *k_aut, u8 *msk);
+void eap_sim_derive_keys_reauth(unsigned int _counter,
+ const u8 *identity, size_t identity_len,
+ const u8 *nonce_s, const u8 *mk, u8 *msk);
+int eap_sim_verify_mac(const u8 *k_aut, u8 *req, size_t req_len, u8 *mac,
+ u8 *extra, size_t extra_len);
+void eap_sim_add_mac(const u8 *k_aut, u8 *msg, size_t msg_len, u8 *mac,
+ const u8 *extra, size_t extra_len);
+
+
+/* EAP-SIM/AKA Attributes (0..127 non-skippable) */
+#define EAP_SIM_AT_RAND 1
+#define EAP_SIM_AT_AUTN 2 /* only AKA */
+#define EAP_SIM_AT_RES 3 /* only AKA, only send */
+#define EAP_SIM_AT_AUTS 4 /* only AKA, only send */
+#define EAP_SIM_AT_PADDING 6 /* only encrypted */
+#define EAP_SIM_AT_NONCE_MT 7 /* only SIM, only send */
+#define EAP_SIM_AT_PERMANENT_ID_REQ 10
+#define EAP_SIM_AT_MAC 11
+#define EAP_SIM_AT_NOTIFICATION 12
+#define EAP_SIM_AT_ANY_ID_REQ 13
+#define EAP_SIM_AT_IDENTITY 14 /* only send */
+#define EAP_SIM_AT_VERSION_LIST 15 /* only SIM */
+#define EAP_SIM_AT_SELECTED_VERSION 16 /* only SIM */
+#define EAP_SIM_AT_FULLAUTH_ID_REQ 17
+#define EAP_SIM_AT_COUNTER 19 /* only encrypted */
+#define EAP_SIM_AT_COUNTER_TOO_SMALL 20 /* only encrypted */
+#define EAP_SIM_AT_NONCE_S 21 /* only encrypted */
+#define EAP_SIM_AT_CLIENT_ERROR_CODE 22 /* only send */
+#define EAP_SIM_AT_IV 129
+#define EAP_SIM_AT_ENCR_DATA 130
+#define EAP_SIM_AT_NEXT_PSEUDONYM 132 /* only encrypted */
+#define EAP_SIM_AT_NEXT_REAUTH_ID 133 /* only encrypted */
+#define EAP_SIM_AT_CHECKCODE 134 /* only AKA */
+#define EAP_SIM_AT_RESULT_IND 135
+
+/* AT_NOTIFICATION notification code values */
+#define EAP_SIM_GENERAL_FAILURE_AFTER_AUTH 0
+#define EAP_SIM_TEMPORARILY_DENIED 1026
+#define EAP_SIM_NOT_SUBSCRIBED 1031
+#define EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH 16384
+#define EAP_SIM_SUCCESS 32768
+
+
+enum eap_sim_id_req {
+ NO_ID_REQ, ANY_ID, FULLAUTH_ID, PERMANENT_ID
+};
+
+
+struct eap_sim_attrs {
+ u8 *rand, *autn, *mac, *iv, *encr_data, *version_list, *nonce_s;
+ u8 *next_pseudonym, *next_reauth_id;
+ u8 *nonce_mt, *identity;
+ size_t num_chal, version_list_len, encr_data_len;
+ size_t next_pseudonym_len, next_reauth_id_len, identity_len;
+ enum eap_sim_id_req id_req;
+ int notification, counter, selected_version, client_error_code;
+};
+
+int eap_sim_parse_attr(u8 *start, u8 *end, struct eap_sim_attrs *attr,
+ int aka, int encr);
+int eap_sim_parse_encr(const u8 *k_encr, u8 *encr_data, size_t encr_data_len,
+ const u8 *iv, struct eap_sim_attrs *attr, int aka);
+
+
+struct eap_sim_msg;
+
+struct eap_sim_msg * eap_sim_msg_init(int code, int id, int type, int subtype);
+u8 * eap_sim_msg_finish(struct eap_sim_msg *msg, size_t *len, const u8 *k_aut,
+ const u8 *extra, size_t extra_len);
+void eap_sim_msg_free(struct eap_sim_msg *msg);
+u8 * eap_sim_msg_add_full(struct eap_sim_msg *msg, u8 attr,
+ const u8 *data, size_t len);
+u8 * eap_sim_msg_add(struct eap_sim_msg *msg, u8 attr,
+ u16 value, const u8 *data, size_t len);
+u8 * eap_sim_msg_add_mac(struct eap_sim_msg *msg, u8 attr);
+int eap_sim_msg_add_encr_start(struct eap_sim_msg *msg, u8 attr_iv,
+ u8 attr_encr);
+int eap_sim_msg_add_encr_end(struct eap_sim_msg *msg, u8 *k_encr,
+ int attr_pad);
+
+void eap_sim_report_notification(void *msg_ctx, int notification, int aka);
+
+#endif /* EAP_SIM_COMMON_H */
diff --git a/contrib/wpa_supplicant/eap_testing.txt b/contrib/wpa_supplicant/eap_testing.txt
new file mode 100644
index 000000000000..03ef2858e425
--- /dev/null
+++ b/contrib/wpa_supplicant/eap_testing.txt
@@ -0,0 +1,349 @@
+Automatic regression and interoperability testing of wpa_supplicant's
+IEEE 802.1X/EAPOL authentication
+
+Test program:
+- Linked some parts of IEEE 802.1X Authenticator implementation from
+ hostapd (RADIUS client and RADIUS processing, EAP<->RADIUS
+ encapsulation/decapsulation) into wpa_supplicant.
+- Replaced wpa_supplicant.c and wpa.c with test code that trigger
+ IEEE 802.1X authentication automatically without need for wireless
+ client card or AP.
+- For EAP methods that generate keying material, the key derived by the
+ Supplicant is verified to match with the one received by the (now
+ integrated) Authenticator.
+
+The full automated test suite can now be run in couple of seconds, but
+I'm more than willing to add new RADIUS authentication servers to make
+this take a bit more time.. ;-) As an extra bonus, this can also be
+seen as automatic regression/interoperability testing for the RADIUS
+server, too.
+
+In order for me to be able to use a new authentication server, the
+server need to be available from Internet (at least from one static IP
+address) and I will need to get suitable user name/password pairs,
+certificates, and private keys for testing use. Other alternative
+would be to get an evaluation version of the server so that I can
+install it on my own test setup. If you are interested in providing
+either server access or evaluation version, please contact me
+(jkmaline@cc.hut.fi).
+
+
+Test matrix
+
++) tested successfully
+F) failed
+-) server did not support
+?) not tested
+
+hostapd --------------------------------------------------------.
+Cisco Aironet 1200 AP (local RADIUS server) ----------------. |
+Corriente Elektron -------------------------------------. | |
+Lucent NavisRadiator -------------------------------. | | |
+Interlink RAD-Series ---------------------------. | | | |
+Radiator -----------------------------------. | | | | |
+Meetinghouse Aegis ---------------------. | | | | | |
+Funk Steel-Belted ------------------. | | | | | | |
+Funk Odyssey -------------------. | | | | | | | |
+Microsoft IAS --------------. | | | | | | | | |
+FreeRADIUS -------------. | | | | | | | | | |
+ | | | | | | | | | | |
+
+EAP-MD5 + - - + + + + + - - +
+EAP-GTC + - - ? + + + + - - +
+EAP-OTP - - - - - + - - - - -
+EAP-MSCHAPv2 + - - + + + + + - - +
+EAP-TLS + + + + + + + + - - +
+EAP-PEAPv0/MSCHAPv2 + + + + + + + + + - +
+EAP-PEAPv0/GTC + - + - + + + + - - +
+EAP-PEAPv0/OTP - - - - - + - - - - -
+EAP-PEAPv0/MD5 + - - + + + + + - - +
+EAP-PEAPv0/TLS - + - + + + F + - - -
+EAP-PEAPv1/MSCHAPv2 - - + + + +1 + +5 +8 - +
+EAP-PEAPv1/GTC - - + + + +1 + +5 - - +
+EAP-PEAPv1/OTP - - - - - +1 - - - - -
+EAP-PEAPv1/MD5 - - - + + +1 + +5 - - +
+EAP-PEAPv1/TLS - - - + + +1 F +5 - - -
+EAP-TTLS/CHAP + - +2 + + + + + + - +
+EAP-TTLS/MSCHAP + - + + + + + + + - +
+EAP-TTLS/MSCHAPv2 + - + + + + + + + - +
+EAP-TTLS/PAP + - + + + + + + + - +
+EAP-TTLS/EAP-MD5 + - +2 + + + + + - - +
+EAP-TTLS/EAP-GTC + - +2 ? + + + + - - +
+EAP-TTLS/EAP-OTP - - - - - + - - - - -
+EAP-TTLS/EAP-MSCHAPv2 + - +2 + + + + + + - +
+EAP-TTLS/EAP-TLS - - +2 + F + + + - - -
+EAP-SIM +3 - - ? - + - ? - - +
+EAP-AKA - - - - - + - - - - -
+EAP-PSK +7 - - - - - - - - - -
+EAP-FAST - - - - - - - - - + -
+LEAP + - + + + + F +6 - + -
+
+1) PEAPv1 required new label, "client PEAP encryption" instead of "client EAP
+ encryption", during key derivation (requires phase1="peaplabel=1" in the
+ network configuration in wpa_supplicant.conf)
+2) used FreeRADIUS as inner auth server
+3) required a patch to FreeRADIUS to fix EAP-SIM
+5) PEAPv1 required termination of negotiation on tunneled EAP-Success and new
+ label in key deriviation
+ (phase1="peap_outer_success=0 peaplabel=1") (in "IETF Draft 5" mode)
+6) Authenticator simulator required patching for handling Access-Accept within
+ negotiation (for the first EAP-Success of LEAP)
+7) EAP-PSK is not included in FreeRADIUS distribution; used external
+ rlm_eap_psk implementation from
+ http://perso.rd.francetelecom.fr/bersani/EAP_PSK/
+ EAP-PSKWindowsimplementations.html
+8) PEAPv1 used non-standard version negotiation (client had to force v1 even
+ though server reported v0 as the highest supported version)
+
+
+Automated tests:
+
+FreeRADIUS (1.0pre and CVS snapshot)
+- EAP-MD5-Challenge
+- EAP-GTC
+- EAP-MSCHAPv2
+- EAP-TLS
+- EAP-PEAPv0 / MSCHAPv2
+- EAP-PEAPv0 / GTC
+- EAP-PEAPv0 / MD5-Challenge
+- EAP-TTLS / EAP-MD5-Challenge
+- EAP-TTLS / EAP-GTC
+- EAP-TTLS / EAP-MSCHAPv2
+- EAP-TTLS / CHAP
+- EAP-TTLS / PAP
+- EAP-TTLS / MSCHAP
+- EAP-TTLS / MSCHAPv2
+- EAP-SIM
+* not supported in FreeRADIUS
+ - EAP-PEAP / TLS (Unable to tunnel TLS inside of TLS)
+ - EAP-TTLS / EAP-TLS (Unable to tunnel TLS inside of TLS)
+
+Microsoft Windows Server 2003 / IAS
+- EAP-TLS
+- EAP-PEAPv0 / MSCHAPv2
+- EAP-PEAPv0 / TLS
+- EAP-MD5
+* IAS does not seem to support other EAP methods
+
+Funk Odyssey 2.01.00.653
+- EAP-TLS
+- EAP-PEAPv0 / MSCHAPv2
+- EAP-PEAPv0 / GTC
+- EAP-PEAPv1 / MSCHAPv2
+- EAP-PEAPv1 / GTC
+ Note: PEAPv1 requires TLS key derivation to use label "client EAP encryption"
+- EAP-TTLS / CHAP (using FreeRADIUS as inner auth srv)
+- EAP-TTLS / MSCHAP
+- EAP-TTLS / MSCHAPv2
+- EAP-TTLS / PAP
+- EAP-TTLS / EAP-MD5-Challenge (using FreeRADIUS as inner auth srv)
+- EAP-TTLS / EAP-GTC (using FreeRADIUS as inner auth srv)
+- EAP-TTLS / EAP-MSCHAPv2 (using FreeRADIUS as inner auth srv)
+- EAP-TTLS / EAP-TLS (using FreeRADIUS as inner auth srv)
+* not supported in Odyssey:
+ - EAP-MD5-Challenge
+ - EAP-GTC
+ - EAP-MSCHAPv2
+ - EAP-PEAP / MD5-Challenge
+ - EAP-PEAP / TLS
+
+Funk Steel-Belted Radius Enterprise Edition v4.71.739
+- EAP-MD5-Challenge
+- EAP-MSCHAPv2
+- EAP-TLS
+- EAP-PEAPv0 / MSCHAPv2
+- EAP-PEAPv0 / MD5
+- EAP-PEAPv0 / TLS
+- EAP-PEAPv1 / MSCHAPv2
+- EAP-PEAPv1 / MD5
+- EAP-PEAPv1 / GTC
+- EAP-PEAPv1 / TLS
+ Note: PEAPv1 requires TLS key derivation to use label "client EAP encryption"
+- EAP-TTLS / CHAP
+- EAP-TTLS / MSCHAP
+- EAP-TTLS / MSCHAPv2
+- EAP-TTLS / PAP
+- EAP-TTLS / EAP-MD5-Challenge
+- EAP-TTLS / EAP-MSCHAPv2
+- EAP-TTLS / EAP-TLS
+
+Meetinghouse Aegis 1.1.4
+- EAP-MD5-Challenge
+- EAP-GTC
+- EAP-MSCHAPv2
+- EAP-TLS
+- EAP-PEAPv0 / MSCHAPv2
+- EAP-PEAPv0 / TLS
+- EAP-PEAPv0 / GTC
+- EAP-PEAPv0 / MD5-Challenge
+- EAP-PEAPv1 / MSCHAPv2
+- EAP-PEAPv1 / TLS
+- EAP-PEAPv1 / GTC
+- EAP-PEAPv1 / MD5-Challenge
+ Note: PEAPv1 requires TLS key derivation to use label "client EAP encryption"
+- EAP-TTLS / CHAP
+- EAP-TTLS / MSCHAP
+- EAP-TTLS / MSCHAPv2
+- EAP-TTLS / PAP
+- EAP-TTLS / EAP-MD5-Challenge
+- EAP-TTLS / EAP-GTC
+- EAP-TTLS / EAP-MSCHAPv2
+* did not work
+ - EAP-TTLS / EAP-TLS
+ (Server rejects authentication without any reason in debug log. It
+ looks like the inner TLS negotiation starts properly and the last
+ packet from Supplicant looks like the one sent in the Phase 1. The
+ server generates a valid looking reply in the same way as in Phase
+ 1, but then ends up sending Access-Reject. Maybe an issue with TTLS
+ fragmentation in the Aegis server(?) The packet seems to include
+ 1328 bytes of EAP-Message and this may go beyond the fragmentation
+ limit with AVP encapsulation and TLS tunneling. Note: EAP-PEAP/TLS
+ did work, so this issue seems to be with something TTLS specific.)
+
+Radiator 3.9 (eval, with all patches up to and including 2004-08-30)
+- EAP-MD5-Challenge
+- EAP-GTC
+- EAP-OTP
+- EAP-MSCHAPv2
+- EAP-TLS
+- EAP-PEAPv0 / MSCHAPv2
+- EAP-PEAPv0 / GTC
+- EAP-PEAPv0 / OTP
+- EAP-PEAPv0 / MD5-Challenge
+- EAP-PEAPv0 / TLS
+ Note: Needed to use unknown identity in outer auth and some times the server
+ seems to get confused and fails to send proper Phase 2 data.
+- EAP-PEAPv1 / MSCHAPv2
+- EAP-PEAPv1 / GTC
+- EAP-PEAPv1 / OTP
+- EAP-PEAPv1 / MD5-Challenge
+- EAP-PEAPv1 / TLS
+ Note: This has some additional requirements for EAPTLS_MaxFragmentSize.
+ Using 1300 for outer auth and 500 for inner auth seemed to work.
+ Note: Needed to use unknown identity in outer auth and some times the server
+ seems to get confused and fails to send proper Phase 2 data.
+- EAP-TTLS / CHAP
+- EAP-TTLS / MSCHAP
+- EAP-TTLS / MSCHAPv2
+- EAP-TTLS / PAP
+- EAP-TTLS / EAP-MD5-Challenge
+- EAP-TTLS / EAP-GTC
+- EAP-TTLS / EAP-OTP
+- EAP-TTLS / EAP-MSCHAPv2
+- EAP-TTLS / EAP-TLS
+ Note: This has some additional requirements for EAPTLS_MaxFragmentSize.
+ Using 1300 for outer auth and 500 for inner auth seemed to work.
+- EAP-SIM
+- EAP-AKA
+
+Interlink Networks RAD-Series 6.1.2.7
+- EAP-MD5-Challenge
+- EAP-GTC
+- EAP-MSCHAPv2
+- EAP-TLS
+- EAP-PEAPv0 / MSCHAPv2
+- EAP-PEAPv0 / GTC
+- EAP-PEAPv0 / MD5-Challenge
+- EAP-PEAPv1 / MSCHAPv2
+- EAP-PEAPv1 / GTC
+- EAP-PEAPv1 / MD5-Challenge
+ Note: PEAPv1 requires TLS key derivation to use label "client EAP encryption"
+- EAP-TTLS / CHAP
+- EAP-TTLS / MSCHAP
+- EAP-TTLS / MSCHAPv2
+- EAP-TTLS / PAP
+- EAP-TTLS / EAP-MD5-Challenge
+- EAP-TTLS / EAP-GTC
+- EAP-TTLS / EAP-MSCHAPv2
+- EAP-TTLS / EAP-TLS
+* did not work
+ - EAP-PEAPv0 / TLS
+ - EAP-PEAPv1 / TLS
+ (Failed to decrypt Phase 2 data)
+
+Lucent NavisRadius 4.4.0
+- EAP-MD5-Challenge
+- EAP-GTC
+- EAP-MSCHAPv2
+- EAP-TLS
+- EAP-PEAPv0 / MD5-Challenge
+- EAP-PEAPv0 / MSCHAPv2
+- EAP-PEAPv0 / GTC
+- EAP-PEAPv0 / TLS
+- EAP-PEAPv1 / MD5-Challenge
+- EAP-PEAPv1 / MSCHAPv2
+- EAP-PEAPv1 / GTC
+- EAP-PEAPv1 / TLS
+ "IETF Draft 5" mode requires phase1="peap_outer_success=0 peaplabel=1"
+ 'Cisco ACU 5.05' mode works without phase1 configuration
+- EAP-TTLS / CHAP
+- EAP-TTLS / MSCHAP
+- EAP-TTLS / MSCHAPv2
+- EAP-TTLS / PAP
+- EAP-TTLS / EAP-MD5-Challenge
+- EAP-TTLS / EAP-MSCHAPv2
+- EAP-TTLS / EAP-GTC
+- EAP-TTLS / EAP-TLS
+
+Note: user certificate from NavisRadius had private key in a format
+that wpa_supplicant could not use. Converting this to PKCS#12 and then
+back to PEM allowed wpa_supplicant to use the key.
+
+
+hostapd v0.3.3
+- EAP-MD5-Challenge
+- EAP-GTC
+- EAP-MSCHAPv2
+- EAP-TLS
+- EAP-PEAPv0 / MSCHAPv2
+- EAP-PEAPv0 / GTC
+- EAP-PEAPv0 / MD5-Challenge
+- EAP-PEAPv1 / MSCHAPv2
+- EAP-PEAPv1 / GTC
+- EAP-PEAPv1 / MD5-Challenge
+- EAP-TTLS / CHAP
+- EAP-TTLS / MSCHAP
+- EAP-TTLS / MSCHAPv2
+- EAP-TTLS / PAP
+- EAP-TTLS / EAP-MD5-Challenge
+- EAP-TTLS / EAP-GTC
+- EAP-TTLS / EAP-MSCHAPv2
+- EAP-SIM
+
+
+
+PEAPv1:
+
+Funk Odyssey 2.01.00.653:
+- uses tunneled EAP-Success, expects reply in tunnel or TLS ACK, sends MPPE
+ keys with outer EAP-Success message after this
+- uses label "client EAP encryption"
+- (peap_outer_success 1 and 2 work)
+
+Funk Steel-Belted Radius Enterprise Edition v4.71.739
+- uses tunneled EAP-Success, expects reply in tunnel or TLS ACK, sends MPPE
+ keys with outer EAP-Success message after this
+- uses label "client EAP encryption"
+- (peap_outer_success 1 and 2 work)
+
+Radiator 3.9:
+- uses TLV Success and Reply, sends MPPE keys with outer EAP-Success message
+ after this
+- uses label "client PEAP encryption"
+
+Lucent NavisRadius 4.4.0 (in "IETF Draft 5" mode):
+- sends tunneled EAP-Success with MPPE keys and expects the authentication to
+ terminate at this point (gets somewhat confused with reply to this)
+- uses label "client PEAP encryption"
+- phase1="peap_outer_success=0 peaplabel=1"
+
+Lucent NavisRadius 4.4.0 (in "Cisco ACU 5.05" mode):
+- sends tunneled EAP-Success with MPPE keys and expects to receive TLS ACK
+ as a reply
+- uses label "client EAP encryption"
+
+Meetinghouse Aegis 1.1.4
+- uses tunneled EAP-Success, expects reply in tunnel or TLS ACK, sends MPPE
+ keys with outer EAP-Success message after this
+- uses label "client EAP encryption"
+- peap_outer_success 1 and 2 work
diff --git a/contrib/wpa_supplicant/eap_tls.c b/contrib/wpa_supplicant/eap_tls.c
new file mode 100644
index 000000000000..4b02cca14e1f
--- /dev/null
+++ b/contrib/wpa_supplicant/eap_tls.c
@@ -0,0 +1,245 @@
+/*
+ * WPA Supplicant / EAP-TLS (RFC 2716)
+ * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published 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 <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "common.h"
+#include "eap_i.h"
+#include "eap_tls_common.h"
+#include "wpa_supplicant.h"
+#include "config_ssid.h"
+#include "tls.h"
+
+
+static void eap_tls_deinit(struct eap_sm *sm, void *priv);
+
+
+struct eap_tls_data {
+ struct eap_ssl_data ssl;
+ u8 *key_data;
+};
+
+
+static void * eap_tls_init(struct eap_sm *sm)
+{
+ struct eap_tls_data *data;
+ struct wpa_ssid *config = eap_get_config(sm);
+ if (config == NULL ||
+ (sm->init_phase2 ? config->private_key2 : config->private_key)
+ == NULL) {
+ wpa_printf(MSG_INFO, "EAP-TLS: Private key not configured");
+ return NULL;
+ }
+
+ data = malloc(sizeof(*data));
+ if (data == NULL)
+ return NULL;
+ memset(data, 0, sizeof(*data));
+
+ if (eap_tls_ssl_init(sm, &data->ssl, config)) {
+ wpa_printf(MSG_INFO, "EAP-TLS: Failed to initialize SSL.");
+ eap_tls_deinit(sm, data);
+ return NULL;
+ }
+
+ return data;
+}
+
+
+static void eap_tls_deinit(struct eap_sm *sm, void *priv)
+{
+ struct eap_tls_data *data = priv;
+ if (data == NULL)
+ return;
+ eap_tls_ssl_deinit(sm, &data->ssl);
+ free(data->key_data);
+ free(data);
+}
+
+
+static u8 * eap_tls_process(struct eap_sm *sm, void *priv,
+ struct eap_method_ret *ret,
+ u8 *reqData, size_t reqDataLen,
+ size_t *respDataLen)
+{
+ struct eap_hdr *req;
+ int left, res;
+ unsigned int tls_msg_len;
+ u8 flags, *pos, *resp, id;
+ struct eap_tls_data *data = priv;
+
+ if (tls_get_errors(sm->ssl_ctx)) {
+ wpa_printf(MSG_INFO, "EAP-TLS: TLS errors detected");
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ req = (struct eap_hdr *) reqData;
+ pos = (u8 *) (req + 1);
+ if (reqDataLen < sizeof(*req) + 2 || *pos != EAP_TYPE_TLS) {
+ wpa_printf(MSG_INFO, "EAP-TLS: Invalid frame");
+ ret->ignore = TRUE;
+ return NULL;
+ }
+ id = req->identifier;
+ pos++;
+ flags = *pos++;
+ left = be_to_host16(req->length) - sizeof(struct eap_hdr) - 2;
+ wpa_printf(MSG_DEBUG, "EAP-TLS: Received packet(len=%lu) - "
+ "Flags 0x%02x", (unsigned long) reqDataLen, flags);
+ if (flags & EAP_TLS_FLAGS_LENGTH_INCLUDED) {
+ if (left < 4) {
+ wpa_printf(MSG_INFO, "EAP-TLS: Short frame with TLS "
+ "length");
+ ret->ignore = TRUE;
+ return NULL;
+ }
+ tls_msg_len = (pos[0] << 24) | (pos[1] << 16) | (pos[2] << 8) |
+ pos[3];
+ wpa_printf(MSG_DEBUG, "EAP-TLS: TLS Message Length: %d",
+ tls_msg_len);
+ if (data->ssl.tls_in_left == 0) {
+ data->ssl.tls_in_total = tls_msg_len;
+ data->ssl.tls_in_left = tls_msg_len;
+ free(data->ssl.tls_in);
+ data->ssl.tls_in = NULL;
+ data->ssl.tls_in_len = 0;
+ }
+ pos += 4;
+ left -= 4;
+ }
+
+ ret->ignore = FALSE;
+
+ ret->methodState = METHOD_CONT;
+ ret->decision = DECISION_COND_SUCC;
+ ret->allowNotifications = TRUE;
+
+ if (flags & EAP_TLS_FLAGS_START) {
+ wpa_printf(MSG_DEBUG, "EAP-TLS: Start");
+ left = 0; /* make sure that this frame is empty, even though it
+ * should always be, anyway */
+ }
+
+ resp = NULL;
+ res = eap_tls_process_helper(sm, &data->ssl, EAP_TYPE_TLS, 0, id, pos,
+ left, &resp, respDataLen);
+
+ if (res < 0) {
+ wpa_printf(MSG_DEBUG, "EAP-TLS: TLS processing failed");
+ ret->methodState = METHOD_MAY_CONT;
+ ret->decision = DECISION_FAIL;
+ return eap_tls_build_ack(&data->ssl, respDataLen, id,
+ EAP_TYPE_TLS, 0);
+ }
+
+ if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) {
+ wpa_printf(MSG_DEBUG, "EAP-TLS: Done");
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_UNCOND_SUCC;
+ free(data->key_data);
+ data->key_data = eap_tls_derive_key(sm, &data->ssl,
+ "client EAP encryption",
+ EAP_TLS_KEY_LEN);
+ if (data->key_data) {
+ wpa_hexdump_key(MSG_DEBUG, "EAP-TLS: Derived key",
+ data->key_data, EAP_TLS_KEY_LEN);
+ } else {
+ wpa_printf(MSG_DEBUG, "EAP-TLS: Failed to derive key");
+ }
+ }
+
+ if (res == 1) {
+ return eap_tls_build_ack(&data->ssl, respDataLen, id,
+ EAP_TYPE_TLS, 0);
+ }
+ return resp;
+}
+
+
+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);
+}
+
+
+static void eap_tls_deinit_for_reauth(struct eap_sm *sm, void *priv)
+{
+}
+
+
+static void * eap_tls_init_for_reauth(struct eap_sm *sm, void *priv)
+{
+ struct eap_tls_data *data = priv;
+ free(data->key_data);
+ data->key_data = NULL;
+ if (eap_tls_reauth_init(sm, &data->ssl)) {
+ free(data);
+ return NULL;
+ }
+ return priv;
+}
+
+
+static int eap_tls_get_status(struct eap_sm *sm, void *priv, char *buf,
+ size_t buflen, int verbose)
+{
+ struct eap_tls_data *data = priv;
+ return eap_tls_status(sm, &data->ssl, buf, buflen, verbose);
+}
+
+
+static Boolean eap_tls_isKeyAvailable(struct eap_sm *sm, void *priv)
+{
+ struct eap_tls_data *data = priv;
+ return data->key_data != NULL;
+}
+
+
+static u8 * eap_tls_getKey(struct eap_sm *sm, void *priv, size_t *len)
+{
+ struct eap_tls_data *data = priv;
+ u8 *key;
+
+ if (data->key_data == NULL)
+ return NULL;
+
+ key = malloc(EAP_TLS_KEY_LEN);
+ if (key == NULL)
+ return NULL;
+
+ *len = EAP_TLS_KEY_LEN;
+ memcpy(key, data->key_data, EAP_TLS_KEY_LEN);
+
+ return key;
+}
+
+
+const struct eap_method eap_method_tls =
+{
+ .method = EAP_TYPE_TLS,
+ .name = "TLS",
+ .init = eap_tls_init,
+ .deinit = eap_tls_deinit,
+ .process = eap_tls_process,
+ .isKeyAvailable = eap_tls_isKeyAvailable,
+ .getKey = eap_tls_getKey,
+ .get_status = eap_tls_get_status,
+ .has_reauth_data = eap_tls_has_reauth_data,
+ .deinit_for_reauth = eap_tls_deinit_for_reauth,
+ .init_for_reauth = eap_tls_init_for_reauth,
+};
diff --git a/contrib/wpa_supplicant/eap_tls_common.c b/contrib/wpa_supplicant/eap_tls_common.c
new file mode 100644
index 000000000000..20b141846790
--- /dev/null
+++ b/contrib/wpa_supplicant/eap_tls_common.c
@@ -0,0 +1,338 @@
+/*
+ * WPA Supplicant / EAP-TLS/PEAP/TTLS/FAST common functions
+ * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published 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 <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "common.h"
+#include "eap_i.h"
+#include "eap_tls_common.h"
+#include "wpa_supplicant.h"
+#include "config_ssid.h"
+#include "md5.h"
+#include "sha1.h"
+#include "tls.h"
+
+
+int eap_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data,
+ struct wpa_ssid *config)
+{
+ int ret = -1;
+ char *ca_cert, *client_cert, *private_key, *private_key_passwd,
+ *dh_file, *subject_match;
+
+ data->eap = sm;
+ data->phase2 = sm->init_phase2;
+ if (config == NULL) {
+ ca_cert = NULL;
+ client_cert = NULL;
+ private_key = NULL;
+ private_key_passwd = NULL;
+ dh_file = NULL;
+ subject_match = NULL;
+ } else if (data->phase2) {
+ ca_cert = (char *) config->ca_cert2;
+ client_cert = (char *) config->client_cert2;
+ private_key = (char *) config->private_key2;
+ private_key_passwd = (char *) config->private_key2_passwd;
+ dh_file = (char *) config->dh_file2;
+ subject_match = (char *) config->subject_match2;
+ } else {
+ ca_cert = (char *) config->ca_cert;
+ client_cert = (char *) config->client_cert;
+ private_key = (char *) config->private_key;
+ private_key_passwd = (char *) config->private_key_passwd;
+ dh_file = (char *) config->dh_file;
+ subject_match = (char *) config->subject_match;
+ }
+ data->conn = tls_connection_init(sm->ssl_ctx);
+ if (data->conn == NULL) {
+ wpa_printf(MSG_INFO, "SSL: Failed to initialize new TLS "
+ "connection");
+ goto done;
+ }
+
+ if (tls_connection_ca_cert(sm->ssl_ctx, data->conn, ca_cert,
+ subject_match)) {
+ wpa_printf(MSG_INFO, "TLS: Failed to load root certificate "
+ "'%s'", ca_cert);
+ goto done;
+ }
+
+ if (tls_connection_client_cert(sm->ssl_ctx, data->conn, client_cert)) {
+ wpa_printf(MSG_INFO, "TLS: Failed to load client certificate "
+ "'%s'", client_cert);
+ goto done;
+ }
+
+ if (tls_connection_private_key(sm->ssl_ctx, data->conn, private_key,
+ private_key_passwd)) {
+ wpa_printf(MSG_INFO, "TLS: Failed to load private key '%s'",
+ private_key);
+ goto done;
+ }
+
+ if (dh_file && tls_connection_dh(sm->ssl_ctx, data->conn, dh_file)) {
+ wpa_printf(MSG_INFO, "TLS: Failed to load DH file '%s'",
+ dh_file);
+ goto done;
+ }
+
+ /* TODO: make this configurable */
+ data->tls_out_limit = 1398;
+ if (data->phase2) {
+ /* Limit the fragment size in the inner TLS authentication
+ * since the outer authentication with EAP-PEAP does not yet
+ * support fragmentation */
+ if (data->tls_out_limit > 100)
+ data->tls_out_limit -= 100;
+ }
+
+ if (config && config->phase1 &&
+ strstr(config->phase1, "include_tls_length=1")) {
+ wpa_printf(MSG_DEBUG, "TLS: Include TLS Message Length in "
+ "unfragmented packets");
+ data->include_tls_length = 1;
+ }
+
+ ret = 0;
+
+done:
+ return ret;
+}
+
+
+void eap_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data)
+{
+ tls_connection_deinit(sm->ssl_ctx, data->conn);
+ free(data->tls_in);
+ free(data->tls_out);
+}
+
+
+u8 * eap_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data,
+ char *label, size_t len)
+{
+ struct tls_keys keys;
+ u8 *random;
+ u8 *out;
+
+ if (tls_connection_get_keys(sm->ssl_ctx, data->conn, &keys))
+ return NULL;
+ out = malloc(len);
+ random = malloc(keys.client_random_len + keys.server_random_len);
+ if (out == NULL || random == NULL) {
+ free(out);
+ free(random);
+ return NULL;
+ }
+ memcpy(random, keys.client_random, keys.client_random_len);
+ memcpy(random + keys.client_random_len, keys.server_random,
+ keys.server_random_len);
+
+ if (tls_prf(keys.master_key, keys.master_key_len,
+ label, random, keys.client_random_len +
+ keys.server_random_len, out, len)) {
+ free(random);
+ free(out);
+ return NULL;
+ }
+ free(random);
+ return out;
+}
+
+
+int eap_tls_data_reassemble(struct eap_sm *sm, struct eap_ssl_data *data,
+ u8 **in_data, size_t *in_len)
+{
+ u8 *buf;
+
+ if (data->tls_in_left > *in_len || data->tls_in) {
+ buf = realloc(data->tls_in, data->tls_in_len + *in_len);
+ if (buf == NULL) {
+ free(data->tls_in);
+ data->tls_in = NULL;
+ data->tls_in_len = 0;
+ wpa_printf(MSG_INFO, "SSL: Could not allocate memory "
+ "for TLS data");
+ return -1;
+ }
+ memcpy(buf + data->tls_in_len, *in_data, *in_len);
+ data->tls_in = buf;
+ data->tls_in_len += *in_len;
+ if (*in_len > data->tls_in_left) {
+ wpa_printf(MSG_INFO, "SSL: more data than TLS message "
+ "length indicated");
+ data->tls_in_left = 0;
+ return -1;
+ }
+ data->tls_in_left -= *in_len;
+ if (data->tls_in_left > 0) {
+ wpa_printf(MSG_DEBUG, "SSL: Need %lu bytes more input "
+ "data", (unsigned long) data->tls_in_left);
+ return 1;
+ }
+
+ *in_data = data->tls_in;
+ *in_len = data->tls_in_len;
+ } else
+ data->tls_in_left = 0;
+
+ return 0;
+}
+
+
+int eap_tls_process_helper(struct eap_sm *sm, struct eap_ssl_data *data,
+ int eap_type, int peap_version,
+ u8 id, u8 *in_data, size_t in_len,
+ u8 **out_data, size_t *out_len)
+{
+ size_t len;
+ u8 *pos, *flags;
+ struct eap_hdr *resp;
+
+ WPA_ASSERT(data->tls_out_len == 0 || in_len == 0);
+ *out_len = 0;
+
+ if (data->tls_out_len == 0) {
+ /* No more data to send out - expect to receive more data from
+ * the AS. */
+ int res = eap_tls_data_reassemble(sm, data, &in_data, &in_len);
+ if (res < 0 || res == 1)
+ return res;
+ /* Full TLS message reassembled - continue handshake processing
+ */
+ if (data->tls_out) {
+ /* This should not happen.. */
+ wpa_printf(MSG_INFO, "SSL: eap_tls_process_helper - "
+ "pending tls_out data even though "
+ "tls_out_len = 0");
+ free(data->tls_out);
+ WPA_ASSERT(data->tls_out == NULL);
+ }
+ data->tls_out = tls_connection_handshake(sm->ssl_ctx,
+ data->conn,
+ in_data, in_len,
+ &data->tls_out_len);
+
+ /* Clear reassembled input data (if the buffer was needed). */
+ data->tls_in_left = data->tls_in_total = data->tls_in_len = 0;
+ free(data->tls_in);
+ data->tls_in = NULL;
+ }
+
+ if (data->tls_out == NULL) {
+ data->tls_out_len = 0;
+ return -1;
+ }
+ if (data->tls_out_len == 0) {
+ /* TLS negotiation should now be complete since all other cases
+ * needing more that should have been catched above based on
+ * the TLS Message Length field. */
+ wpa_printf(MSG_DEBUG, "SSL: No data to be sent out");
+ free(data->tls_out);
+ data->tls_out = NULL;
+ return 1;
+ }
+
+ wpa_printf(MSG_DEBUG, "SSL: %lu bytes left to be sent out (of total "
+ "%lu bytes)",
+ (unsigned long) data->tls_out_len - data->tls_out_pos,
+ (unsigned long) data->tls_out_len);
+ resp = malloc(sizeof(struct eap_hdr) + 2 + 4 + data->tls_out_limit);
+ if (resp == NULL) {
+ *out_data = NULL;
+ return -1;
+ }
+ resp->code = EAP_CODE_RESPONSE;
+ resp->identifier = id;
+ pos = (u8 *) (resp + 1);
+ *pos++ = eap_type;
+ flags = pos++;
+ *flags = peap_version;
+ if (data->tls_out_pos == 0 &&
+ (data->tls_out_len > data->tls_out_limit ||
+ data->include_tls_length)) {
+ *flags |= EAP_TLS_FLAGS_LENGTH_INCLUDED;
+ *pos++ = (data->tls_out_len >> 24) & 0xff;
+ *pos++ = (data->tls_out_len >> 16) & 0xff;
+ *pos++ = (data->tls_out_len >> 8) & 0xff;
+ *pos++ = data->tls_out_len & 0xff;
+ }
+
+ len = data->tls_out_len - data->tls_out_pos;
+ if (len > data->tls_out_limit) {
+ *flags |= EAP_TLS_FLAGS_MORE_FRAGMENTS;
+ len = data->tls_out_limit;
+ wpa_printf(MSG_DEBUG, "SSL: sending %lu bytes, more fragments "
+ "will follow", (unsigned long) len);
+ }
+ memcpy(pos, &data->tls_out[data->tls_out_pos], len);
+ data->tls_out_pos += len;
+ *out_len = (pos - (u8 *) resp) + len;
+ resp->length = host_to_be16(*out_len);
+ *out_data = (u8 *) resp;
+
+ if (!(*flags & EAP_TLS_FLAGS_MORE_FRAGMENTS)) {
+ data->tls_out_len = 0;
+ data->tls_out_pos = 0;
+ free(data->tls_out);
+ data->tls_out = NULL;
+ }
+
+ return 0;
+}
+
+
+u8 * eap_tls_build_ack(struct eap_ssl_data *data, size_t *respDataLen, u8 id,
+ int eap_type, int peap_version)
+{
+ struct eap_hdr *resp;
+ u8 *pos;
+
+ *respDataLen = sizeof(struct eap_hdr) + 2;
+ resp = malloc(*respDataLen);
+ if (resp == NULL)
+ return NULL;
+ wpa_printf(MSG_DEBUG, "SSL: Building ACK");
+ resp->code = EAP_CODE_RESPONSE;
+ resp->identifier = id;
+ resp->length = host_to_be16(*respDataLen);
+ pos = (u8 *) (resp + 1);
+ *pos++ = eap_type; /* Type */
+ *pos = peap_version; /* Flags */
+ return (u8 *) resp;
+}
+
+
+int eap_tls_reauth_init(struct eap_sm *sm, struct eap_ssl_data *data)
+{
+ return tls_connection_shutdown(sm->ssl_ctx, data->conn);
+}
+
+
+int eap_tls_status(struct eap_sm *sm, struct eap_ssl_data *data, char *buf,
+ size_t buflen, int verbose)
+{
+ char name[128];
+ int len = 0;
+
+ if (tls_get_cipher(sm->ssl_ctx, data->conn, name, sizeof(name)) == 0) {
+ len += snprintf(buf + len, buflen - len,
+ "EAP TLS cipher=%s\n", name);
+ }
+
+ return len;
+}
diff --git a/contrib/wpa_supplicant/eap_tls_common.h b/contrib/wpa_supplicant/eap_tls_common.h
new file mode 100644
index 000000000000..499cae4c0e10
--- /dev/null
+++ b/contrib/wpa_supplicant/eap_tls_common.h
@@ -0,0 +1,51 @@
+#ifndef EAP_TLS_COMMON_H
+#define EAP_TLS_COMMON_H
+
+struct eap_ssl_data {
+ struct tls_connection *conn;
+
+ u8 *tls_out;
+ size_t tls_out_len;
+ size_t tls_out_pos;
+ size_t tls_out_limit;
+ u8 *tls_in;
+ size_t tls_in_len;
+ size_t tls_in_left;
+ size_t tls_in_total;
+
+ int phase2;
+ int include_tls_length; /* include TLS length field even if the TLS
+ * data is not fragmented */
+
+ struct eap_sm *eap;
+};
+
+
+/* EAP TLS Flags */
+#define EAP_TLS_FLAGS_LENGTH_INCLUDED 0x80
+#define EAP_TLS_FLAGS_MORE_FRAGMENTS 0x40
+#define EAP_TLS_FLAGS_START 0x20
+#define EAP_PEAP_VERSION_MASK 0x07
+
+ /* could be up to 128 bytes, but only the first 64 bytes are used */
+#define EAP_TLS_KEY_LEN 64
+
+
+int eap_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data,
+ struct wpa_ssid *config);
+void eap_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data);
+u8 * eap_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data,
+ char *label, size_t len);
+int eap_tls_data_reassemble(struct eap_sm *sm, struct eap_ssl_data *data,
+ u8 **in_data, size_t *in_len);
+int eap_tls_process_helper(struct eap_sm *sm, struct eap_ssl_data *data,
+ int eap_type, int peap_version,
+ u8 id, u8 *in_data, size_t in_len,
+ u8 **out_data, size_t *out_len);
+u8 * eap_tls_build_ack(struct eap_ssl_data *data, size_t *respDataLen, u8 id,
+ int eap_type, int peap_version);
+int eap_tls_reauth_init(struct eap_sm *sm, struct eap_ssl_data *data);
+int eap_tls_status(struct eap_sm *sm, struct eap_ssl_data *data, char *buf,
+ size_t buflen, int verbose);
+
+#endif /* EAP_TLS_COMMON_H */
diff --git a/contrib/wpa_supplicant/eap_tlv.c b/contrib/wpa_supplicant/eap_tlv.c
new file mode 100644
index 000000000000..5571d8bdf2cb
--- /dev/null
+++ b/contrib/wpa_supplicant/eap_tlv.c
@@ -0,0 +1,176 @@
+/*
+ * WPA Supplicant / EAP-TLV (draft-josefsson-pppext-eap-tls-eap-07.txt)
+ * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published 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 <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "common.h"
+#include "wpa_supplicant.h"
+#include "eap_i.h"
+#include "eap_tlv.h"
+
+
+u8 * eap_tlv_build_nak(int id, int nak_type, size_t *resp_len)
+{
+ struct eap_hdr *hdr;
+ u8 *pos;
+
+ *resp_len = sizeof(struct eap_hdr) + 1 + 10;
+ hdr = malloc(*resp_len);
+ if (hdr == NULL)
+ return NULL;
+
+ hdr->code = EAP_CODE_RESPONSE;
+ hdr->identifier = id;
+ hdr->length = host_to_be16(*resp_len);
+ pos = (u8 *) (hdr + 1);
+ *pos++ = EAP_TYPE_TLV;
+ *pos++ = 0x80; /* Mandatory */
+ *pos++ = EAP_TLV_NAK_TLV;
+ /* Length */
+ *pos++ = 0;
+ *pos++ = 6;
+ /* Vendor-Id */
+ *pos++ = 0;
+ *pos++ = 0;
+ *pos++ = 0;
+ *pos++ = 0;
+ /* NAK-Type */
+ *pos++ = nak_type >> 8;
+ *pos++ = nak_type & 0xff;
+
+ return (u8 *) hdr;
+}
+
+
+u8 * eap_tlv_build_result(int id, int status, size_t *resp_len)
+{
+ struct eap_hdr *hdr;
+ u8 *pos;
+
+ *resp_len = sizeof(struct eap_hdr) + 1 + 6;
+ hdr = malloc(*resp_len);
+ if (hdr == NULL)
+ return NULL;
+
+ hdr->code = EAP_CODE_RESPONSE;
+ hdr->identifier = id;
+ hdr->length = host_to_be16(*resp_len);
+ pos = (u8 *) (hdr + 1);
+ *pos++ = EAP_TYPE_TLV;
+ *pos++ = 0x80; /* Mandatory */
+ *pos++ = EAP_TLV_RESULT_TLV;
+ /* Length */
+ *pos++ = 0;
+ *pos++ = 2;
+ /* Status */
+ *pos++ = status >> 8;
+ *pos++ = status & 0xff;
+
+ return (u8 *) hdr;
+}
+
+
+int eap_tlv_process(struct eap_sm *sm, struct eap_method_ret *ret,
+ struct eap_hdr *hdr, u8 **resp, size_t *resp_len)
+{
+ size_t left;
+ u8 *pos;
+ u8 *result_tlv = NULL;
+ size_t result_tlv_len = 0;
+ int tlv_type, mandatory, tlv_len;
+
+ /* Parse TLVs */
+ left = be_to_host16(hdr->length) - sizeof(struct eap_hdr) - 1;
+ pos = (u8 *) (hdr + 1);
+ pos++;
+ wpa_hexdump(MSG_DEBUG, "EAP-TLV: Received TLVs", pos, left);
+ while (left >= 4) {
+ mandatory = !!(pos[0] & 0x80);
+ tlv_type = pos[0] & 0x3f;
+ tlv_type = (tlv_type << 8) | pos[1];
+ tlv_len = ((int) pos[2] << 8) | pos[3];
+ pos += 4;
+ left -= 4;
+ if (tlv_len > left) {
+ wpa_printf(MSG_DEBUG, "EAP-TLV: TLV underrun "
+ "(tlv_len=%d left=%lu)", tlv_len,
+ (unsigned long) left);
+ return -1;
+ }
+ switch (tlv_type) {
+ case EAP_TLV_RESULT_TLV:
+ result_tlv = pos;
+ result_tlv_len = tlv_len;
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "EAP-TLV: Unsupported TLV Type "
+ "%d%s", tlv_type,
+ mandatory ? " (mandatory)" : "");
+ if (mandatory) {
+ /* NAK TLV and ignore all TLVs in this packet.
+ */
+ *resp = eap_tlv_build_nak(hdr->identifier,
+ tlv_type, resp_len);
+ return *resp == NULL ? -1 : 0;
+ }
+ /* Ignore this TLV, but process other TLVs */
+ break;
+ }
+
+ pos += tlv_len;
+ left -= tlv_len;
+ }
+ if (left) {
+ wpa_printf(MSG_DEBUG, "EAP-TLV: Last TLV too short in "
+ "Request (left=%lu)", (unsigned long) left);
+ return -1;
+ }
+
+ /* Process supported TLVs */
+ if (result_tlv) {
+ int status, resp_status;
+ wpa_hexdump(MSG_DEBUG, "EAP-TLV: Result TLV",
+ result_tlv, result_tlv_len);
+ if (result_tlv_len < 2) {
+ wpa_printf(MSG_INFO, "EAP-TLV: Too short Result TLV "
+ "(len=%lu)",
+ (unsigned long) result_tlv_len);
+ return -1;
+ }
+ status = ((int) result_tlv[0] << 8) | result_tlv[1];
+ if (status == EAP_TLV_RESULT_SUCCESS) {
+ wpa_printf(MSG_INFO, "EAP-TLV: TLV Result - Success "
+ "- EAP-TLV/Phase2 Completed");
+ resp_status = EAP_TLV_RESULT_SUCCESS;
+ ret->decision = DECISION_UNCOND_SUCC;
+ } else if (status == EAP_TLV_RESULT_FAILURE) {
+ wpa_printf(MSG_INFO, "EAP-TLV: TLV Result - Failure");
+ resp_status = EAP_TLV_RESULT_FAILURE;
+ ret->decision = DECISION_FAIL;
+ } else {
+ wpa_printf(MSG_INFO, "EAP-TLV: Unknown TLV Result "
+ "Status %d", status);
+ resp_status = EAP_TLV_RESULT_FAILURE;
+ ret->decision = DECISION_FAIL;
+ }
+ ret->methodState = METHOD_DONE;
+
+ *resp = eap_tlv_build_result(hdr->identifier, resp_status,
+ resp_len);
+ }
+
+ return 0;
+}
diff --git a/contrib/wpa_supplicant/eap_tlv.h b/contrib/wpa_supplicant/eap_tlv.h
new file mode 100644
index 000000000000..439155263716
--- /dev/null
+++ b/contrib/wpa_supplicant/eap_tlv.h
@@ -0,0 +1,73 @@
+#ifndef EAP_TLV_H
+#define EAP_TLV_H
+
+/* EAP-TLV TLVs (draft-josefsson-ppext-eap-tls-eap-07.txt) */
+#define EAP_TLV_RESULT_TLV 3 /* Acknowledged Result */
+#define EAP_TLV_NAK_TLV 4
+#define EAP_TLV_CRYPTO_BINDING_TLV 5
+#define EAP_TLV_CONNECTION_BINDING_TLV 6
+#define EAP_TLV_VENDOR_SPECIFIC_TLV 7
+#define EAP_TLV_URI_TLV 8
+#define EAP_TLV_EAP_PAYLOAD_TLV 9
+#define EAP_TLV_INTERMEDIATE_RESULT_TLV 10
+#define EAP_TLV_PAC_TLV 11 /* draft-cam-winget-eap-fast-01.txt */
+#define EAP_TLV_CRYPTO_BINDING_TLV_ 12 /* draft-cam-winget-eap-fast-01.txt */
+
+#define EAP_TLV_RESULT_SUCCESS 1
+#define EAP_TLV_RESULT_FAILURE 2
+
+#define EAP_TLV_TYPE_MANDATORY 0x8000
+
+struct eap_tlv_hdr {
+ u16 tlv_type;
+ u16 length;
+};
+
+struct eap_tlv_nak_tlv {
+ u16 tlv_type;
+ u16 length;
+ u32 vendor_id;
+ u16 nak_type;
+} __attribute__((packed));
+
+struct eap_tlv_result_tlv {
+ u16 tlv_type;
+ u16 length;
+ u16 status;
+} __attribute__((packed));
+
+struct eap_tlv_intermediate_result_tlv {
+ u16 tlv_type;
+ u16 length;
+ u16 status;
+} __attribute__((packed));
+
+struct eap_tlv_crypto_binding__tlv {
+ u16 tlv_type;
+ u16 length;
+ u8 reserved;
+ u8 version;
+ u8 received_version;
+ u8 subtype;
+ u8 nonce[32];
+ u8 compound_mac[20];
+} __attribute__((packed));
+
+struct eap_tlv_pac_ack_tlv {
+ u16 tlv_type;
+ u16 length;
+ u16 pac_type;
+ u16 pac_len;
+ u16 result;
+} __attribute__((packed));
+
+#define EAP_TLV_CRYPTO_BINDING_SUBTYPE_REQUEST 0
+#define EAP_TLV_CRYPTO_BINDING_SUBTYPE_RESPONSE 1
+
+
+u8 * eap_tlv_build_nak(int id, int nak_type, size_t *resp_len);
+u8 * eap_tlv_build_result(int id, int status, size_t *resp_len);
+int eap_tlv_process(struct eap_sm *sm, struct eap_method_ret *ret,
+ struct eap_hdr *hdr, u8 **resp, size_t *resp_len);
+
+#endif /* EAP_TLV_H */
diff --git a/contrib/wpa_supplicant/eap_ttls.c b/contrib/wpa_supplicant/eap_ttls.c
new file mode 100644
index 000000000000..b639fcd54dc8
--- /dev/null
+++ b/contrib/wpa_supplicant/eap_ttls.c
@@ -0,0 +1,1384 @@
+/*
+ * WPA Supplicant / EAP-TTLS (draft-ietf-pppext-eap-ttls-03.txt)
+ * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published 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 <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "common.h"
+#include "eap_i.h"
+#include "eap_tls_common.h"
+#include "wpa_supplicant.h"
+#include "config_ssid.h"
+#include "ms_funcs.h"
+#include "md5.h"
+#include "tls.h"
+#include "eap_ttls.h"
+
+
+static void eap_ttls_deinit(struct eap_sm *sm, void *priv);
+
+
+struct eap_ttls_data {
+ struct eap_ssl_data ssl;
+
+ const struct eap_method *phase2_method;
+ void *phase2_priv;
+ int phase2_success;
+ int phase2_start;
+
+ enum {
+ EAP_TTLS_PHASE2_EAP,
+ EAP_TTLS_PHASE2_MSCHAPV2,
+ EAP_TTLS_PHASE2_MSCHAP,
+ EAP_TTLS_PHASE2_PAP,
+ EAP_TTLS_PHASE2_CHAP
+ } phase2_type;
+ u8 phase2_eap_type;
+ u8 *phase2_eap_types;
+ size_t num_phase2_eap_types;
+
+ u8 auth_response[20];
+ int auth_response_valid;
+ u8 ident;
+ int resuming; /* starting a resumed session */
+ int reauth; /* reauthentication */
+ u8 *key_data;
+
+ u8 *pending_phase2_req;
+ size_t pending_phase2_req_len;
+};
+
+
+static void * eap_ttls_init(struct eap_sm *sm)
+{
+ struct eap_ttls_data *data;
+ struct wpa_ssid *config = eap_get_config(sm);
+ char *selected;
+
+ data = malloc(sizeof(*data));
+ if (data == NULL)
+ return NULL;
+ memset(data, 0, sizeof(*data));
+ selected = "EAP";
+ data->phase2_type = EAP_TTLS_PHASE2_EAP;
+ if (config && config->phase2) {
+ if (strstr(config->phase2, "autheap=")) {
+ selected = "EAP";
+ data->phase2_type = EAP_TTLS_PHASE2_EAP;
+ } else if (strstr(config->phase2, "auth=MSCHAPV2")) {
+ selected = "MSCHAPV2";
+ data->phase2_type = EAP_TTLS_PHASE2_MSCHAPV2;
+ } else if (strstr(config->phase2, "auth=MSCHAP")) {
+ selected = "MSCHAP";
+ data->phase2_type = EAP_TTLS_PHASE2_MSCHAP;
+ } else if (strstr(config->phase2, "auth=PAP")) {
+ selected = "PAP";
+ data->phase2_type = EAP_TTLS_PHASE2_PAP;
+ } else if (strstr(config->phase2, "auth=CHAP")) {
+ selected = "CHAP";
+ data->phase2_type = EAP_TTLS_PHASE2_CHAP;
+ }
+ }
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase2 type: %s", selected);
+
+ if (data->phase2_type == EAP_TTLS_PHASE2_EAP) {
+ if (config && config->phase2) {
+ char *start, *pos, *buf;
+ u8 method, *methods = NULL, *_methods;
+ size_t num_methods = 0;
+ start = buf = strdup(config->phase2);
+ if (buf == NULL) {
+ eap_ttls_deinit(sm, data);
+ return NULL;
+ }
+ while (start && *start != '\0') {
+ pos = strstr(start, "autheap=");
+ if (pos == NULL)
+ break;
+ if (start != pos && *(pos - 1) != ' ') {
+ start = pos + 8;
+ continue;
+ }
+
+ start = pos + 8;
+ pos = strchr(start, ' ');
+ if (pos)
+ *pos++ = '\0';
+ method = eap_get_phase2_type(start);
+ if (method == EAP_TYPE_NONE) {
+ wpa_printf(MSG_ERROR, "EAP-TTLS: "
+ "Unsupported Phase2 EAP "
+ "method '%s'", start);
+ } else {
+ num_methods++;
+ _methods = realloc(methods,
+ num_methods);
+ if (_methods == NULL) {
+ free(methods);
+ eap_ttls_deinit(sm, data);
+ return NULL;
+ }
+ methods = _methods;
+ methods[num_methods - 1] = method;
+ }
+
+ start = pos;
+ }
+ free(buf);
+ data->phase2_eap_types = methods;
+ data->num_phase2_eap_types = num_methods;
+ }
+ if (data->phase2_eap_types == NULL) {
+ data->phase2_eap_types = eap_get_phase2_types(
+ config, &data->num_phase2_eap_types);
+ }
+ if (data->phase2_eap_types == NULL) {
+ wpa_printf(MSG_ERROR, "EAP-TTLS: No Phase2 EAP method "
+ "available");
+ eap_ttls_deinit(sm, data);
+ return NULL;
+ }
+ wpa_hexdump(MSG_DEBUG, "EAP-TTLS: Phase2 EAP types",
+ data->phase2_eap_types,
+ data->num_phase2_eap_types);
+ data->phase2_eap_type = EAP_TYPE_NONE;
+ }
+
+
+ if (eap_tls_ssl_init(sm, &data->ssl, config)) {
+ wpa_printf(MSG_INFO, "EAP-TTLS: Failed to initialize SSL.");
+ eap_ttls_deinit(sm, data);
+ return NULL;
+ }
+
+ return data;
+}
+
+
+static void eap_ttls_deinit(struct eap_sm *sm, void *priv)
+{
+ struct eap_ttls_data *data = priv;
+ if (data == NULL)
+ return;
+ if (data->phase2_priv && data->phase2_method)
+ data->phase2_method->deinit(sm, data->phase2_priv);
+ free(data->phase2_eap_types);
+ eap_tls_ssl_deinit(sm, &data->ssl);
+ free(data->key_data);
+ free(data->pending_phase2_req);
+ free(data);
+}
+
+
+static int eap_ttls_encrypt(struct eap_sm *sm, struct eap_ttls_data *data,
+ int id, u8 *plain, size_t plain_len,
+ u8 **out_data, size_t *out_len)
+{
+ int res;
+ u8 *pos;
+ struct eap_hdr *resp;
+
+ /* TODO: add support for fragmentation, if needed. This will need to
+ * add TLS Message Length field, if the frame is fragmented. */
+ resp = malloc(sizeof(struct eap_hdr) + 2 + data->ssl.tls_out_limit);
+ if (resp == NULL)
+ return 0;
+
+ resp->code = EAP_CODE_RESPONSE;
+ resp->identifier = id;
+
+ pos = (u8 *) (resp + 1);
+ *pos++ = EAP_TYPE_TTLS;
+ *pos++ = 0;
+
+ res = tls_connection_encrypt(sm->ssl_ctx, data->ssl.conn,
+ plain, plain_len,
+ pos, data->ssl.tls_out_limit);
+ if (res < 0) {
+ wpa_printf(MSG_INFO, "EAP-TTLS: Failed to encrypt Phase 2 "
+ "data");
+ free(resp);
+ return 0;
+ }
+
+ *out_len = sizeof(struct eap_hdr) + 2 + res;
+ resp->length = host_to_be16(*out_len);
+ *out_data = (u8 *) resp;
+ return 0;
+}
+
+
+static u8 * eap_ttls_avp_hdr(u8 *avphdr, u32 avp_code, u32 vendor_id,
+ int mandatory, size_t len)
+{
+ struct ttls_avp_vendor *avp;
+ u8 flags;
+ size_t hdrlen;
+
+ avp = (struct ttls_avp_vendor *) avphdr;
+ flags = mandatory ? AVP_FLAGS_MANDATORY : 0;
+ if (vendor_id) {
+ flags |= AVP_FLAGS_VENDOR;
+ hdrlen = sizeof(*avp);
+ avp->vendor_id = host_to_be32(vendor_id);
+ } else {
+ hdrlen = sizeof(struct ttls_avp);
+ }
+
+ avp->avp_code = host_to_be32(avp_code);
+ avp->avp_length = host_to_be32((flags << 24) | (hdrlen + len));
+
+ return avphdr + hdrlen;
+}
+
+
+static u8 * eap_ttls_avp_add(u8 *start, u8 *avphdr, u32 avp_code,
+ u32 vendor_id, int mandatory,
+ u8 *data, size_t len)
+{
+ u8 *pos;
+ pos = eap_ttls_avp_hdr(avphdr, avp_code, vendor_id, mandatory, len);
+ memcpy(pos, data, len);
+ pos += len;
+ AVP_PAD(start, pos);
+ return pos;
+}
+
+
+static int eap_ttls_avp_encapsulate(u8 **resp, size_t *resp_len, u32 avp_code,
+ int mandatory)
+{
+ u8 *avp, *pos;
+
+ avp = malloc(sizeof(struct ttls_avp) + *resp_len + 4);
+ if (avp == NULL) {
+ free(*resp);
+ *resp_len = 0;
+ return -1;
+ }
+
+ pos = eap_ttls_avp_hdr(avp, avp_code, 0, mandatory, *resp_len);
+ memcpy(pos, *resp, *resp_len);
+ pos += *resp_len;
+ AVP_PAD(avp, pos);
+ free(*resp);
+ *resp = avp;
+ *resp_len = pos - avp;
+ return 0;
+}
+
+
+static int eap_ttls_phase2_nak(struct eap_sm *sm,
+ struct eap_ttls_data *data,
+ struct eap_hdr *hdr,
+ u8 **resp, size_t *resp_len)
+{
+ struct eap_hdr *resp_hdr;
+ u8 *pos = (u8 *) (hdr + 1);
+
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase 2 Request: Nak type=%d", *pos);
+ wpa_hexdump(MSG_DEBUG, "EAP-TTLS: Allowed Phase2 EAP types",
+ data->phase2_eap_types, data->num_phase2_eap_types);
+ *resp_len = sizeof(struct eap_hdr) + 1 + data->num_phase2_eap_types;
+ *resp = malloc(*resp_len);
+ if (*resp == NULL)
+ return -1;
+
+ resp_hdr = (struct eap_hdr *) (*resp);
+ resp_hdr->code = EAP_CODE_RESPONSE;
+ resp_hdr->identifier = hdr->identifier;
+ resp_hdr->length = host_to_be16(*resp_len);
+ pos = (u8 *) (resp_hdr + 1);
+ *pos++ = EAP_TYPE_NAK;
+ memcpy(pos, data->phase2_eap_types, data->num_phase2_eap_types);
+
+ return 0;
+}
+
+
+static int eap_ttls_phase2_request_eap(struct eap_sm *sm,
+ struct eap_ttls_data *data,
+ struct eap_method_ret *ret,
+ struct eap_hdr *req,
+ struct eap_hdr *hdr,
+ u8 **resp, size_t *resp_len)
+{
+ size_t len = be_to_host16(hdr->length);
+ u8 *pos;
+ struct eap_method_ret iret;
+ struct wpa_ssid *config = eap_get_config(sm);
+
+ if (len <= sizeof(struct eap_hdr)) {
+ wpa_printf(MSG_INFO, "EAP-TTLS: too short "
+ "Phase 2 request (len=%lu)", (unsigned long) len);
+ return -1;
+ }
+ pos = (u8 *) (hdr + 1);
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase 2 EAP Request: type=%d", *pos);
+ switch (*pos) {
+ case EAP_TYPE_IDENTITY:
+ *resp = eap_sm_buildIdentity(sm, req->identifier, resp_len, 1);
+ break;
+ default:
+ if (data->phase2_eap_type == EAP_TYPE_NONE) {
+ int i;
+ for (i = 0; i < data->num_phase2_eap_types; i++) {
+ if (data->phase2_eap_types[i] != *pos)
+ continue;
+
+ data->phase2_eap_type = *pos;
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Selected "
+ "Phase 2 EAP method %d",
+ data->phase2_eap_type);
+ break;
+ }
+ }
+ if (*pos != data->phase2_eap_type || *pos == EAP_TYPE_NONE) {
+ if (eap_ttls_phase2_nak(sm, data, hdr, resp, resp_len))
+ return -1;
+ break;
+ }
+
+ if (data->phase2_priv == NULL) {
+ data->phase2_method = eap_sm_get_eap_methods(*pos);
+ if (data->phase2_method) {
+ sm->init_phase2 = 1;
+ data->phase2_priv =
+ data->phase2_method->init(sm);
+ sm->init_phase2 = 0;
+ }
+ }
+ if (data->phase2_priv == NULL || data->phase2_method == NULL) {
+ wpa_printf(MSG_INFO, "EAP-TTLS: failed to initialize "
+ "Phase 2 EAP method %d", *pos);
+ return -1;
+ }
+ memset(&iret, 0, sizeof(iret));
+ *resp = data->phase2_method->process(sm, data->phase2_priv,
+ &iret, (u8 *) hdr, len,
+ resp_len);
+ if ((iret.methodState == METHOD_DONE ||
+ iret.methodState == METHOD_MAY_CONT) &&
+ (iret.decision == DECISION_UNCOND_SUCC ||
+ iret.decision == DECISION_COND_SUCC ||
+ iret.decision == DECISION_FAIL)) {
+ ret->methodState = iret.methodState;
+ ret->decision = iret.decision;
+ }
+ break;
+ }
+
+ if (*resp == NULL &&
+ (config->pending_req_identity || config->pending_req_password ||
+ config->pending_req_otp)) {
+ return 0;
+ }
+
+ if (*resp == NULL)
+ return -1;
+
+ wpa_hexdump(MSG_DEBUG, "EAP-TTLS: AVP encapsulate EAP Response",
+ *resp, *resp_len);
+ return eap_ttls_avp_encapsulate(resp, resp_len,
+ RADIUS_ATTR_EAP_MESSAGE, 1);
+}
+
+
+static int eap_ttls_phase2_request_mschapv2(struct eap_sm *sm,
+ struct eap_ttls_data *data,
+ struct eap_method_ret *ret,
+ struct eap_hdr *req,
+ struct eap_hdr *hdr,
+ u8 **resp, size_t *resp_len)
+{
+ struct wpa_ssid *config = eap_get_config(sm);
+ u8 *buf, *pos, *challenge, *username, *peer_challenge;
+ size_t username_len;
+ int i;
+
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase 2 MSCHAPV2 Request");
+
+ /* MSCHAPv2 does not include optional domain name in the
+ * challenge-response calculation, so remove domain prefix
+ * (if present). */
+ username = config->identity;
+ username_len = config->identity_len;
+ pos = username;
+ for (i = 0; i < username_len; i++) {
+ if (username[i] == '\\') {
+ username_len -= i + 1;
+ username += i + 1;
+ break;
+ }
+ }
+
+ pos = buf = malloc(config->identity_len + 1000);
+ if (buf == NULL) {
+ wpa_printf(MSG_ERROR,
+ "EAP-TTLS/MSCHAPV2: Failed to allocate memory");
+ return -1;
+ }
+
+ /* User-Name */
+ pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_USER_NAME, 0, 1,
+ config->identity, config->identity_len);
+
+ /* MS-CHAP-Challenge */
+ challenge = eap_tls_derive_key(sm, &data->ssl, "ttls challenge",
+ EAP_TTLS_MSCHAPV2_CHALLENGE_LEN * 2 +
+ 1);
+ if (challenge == NULL) {
+ free(buf);
+ wpa_printf(MSG_ERROR, "EAP-TTLS/MSCHAPV2: Failed to derive "
+ "implicit challenge");
+ return -1;
+ }
+ peer_challenge = challenge + 1 + EAP_TTLS_MSCHAPV2_CHALLENGE_LEN;
+
+ pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_MS_CHAP_CHALLENGE,
+ RADIUS_VENDOR_ID_MICROSOFT, 1,
+ challenge, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN);
+
+ /* MS-CHAP2-Response */
+ pos = eap_ttls_avp_hdr(pos, RADIUS_ATTR_MS_CHAP2_RESPONSE,
+ RADIUS_VENDOR_ID_MICROSOFT, 1,
+ EAP_TTLS_MSCHAPV2_RESPONSE_LEN);
+ data->ident = challenge[EAP_TTLS_MSCHAPV2_CHALLENGE_LEN];
+ *pos++ = data->ident;
+ *pos++ = 0; /* Flags */
+ memcpy(pos, peer_challenge, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN);
+ pos += EAP_TTLS_MSCHAPV2_CHALLENGE_LEN;
+ memset(pos, 0, 8); /* Reserved, must be zero */
+ pos += 8;
+ wpa_hexdump(MSG_DEBUG, "EAP-TTLS: MSCHAPV2: implicit auth_challenge",
+ challenge, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN);
+ wpa_hexdump(MSG_DEBUG, "EAP-TTLS: MSCHAPV2: peer_challenge",
+ peer_challenge, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN);
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP-TTLS: MSCHAPV2 username",
+ username, username_len);
+ wpa_hexdump_ascii_key(MSG_DEBUG, "EAP-TTLS: MSCHAPV2 password",
+ config->password, config->password_len);
+ generate_nt_response(challenge, peer_challenge,
+ username, username_len,
+ config->password, config->password_len,
+ pos);
+ wpa_hexdump(MSG_DEBUG, "EAP-TTLS: MSCHAPV2 response", pos, 24);
+ generate_authenticator_response(config->password, config->password_len,
+ peer_challenge, challenge,
+ username, username_len,
+ pos, data->auth_response);
+ data->auth_response_valid = 1;
+
+ pos += 24;
+ free(challenge);
+ AVP_PAD(buf, pos);
+
+ *resp = buf;
+ *resp_len = pos - buf;
+
+ if (sm->workaround) {
+ /* At least FreeRADIUS seems to be terminating
+ * EAP-TTLS/MSHCAPV2 without the expected MS-CHAP-v2 Success
+ * packet. */
+ wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: EAP workaround - "
+ "allow success without tunneled response");
+ ret->methodState = METHOD_MAY_CONT;
+ ret->decision = DECISION_COND_SUCC;
+ }
+
+ return 0;
+}
+
+
+static int eap_ttls_phase2_request_mschap(struct eap_sm *sm,
+ struct eap_ttls_data *data,
+ struct eap_method_ret *ret,
+ struct eap_hdr *req,
+ struct eap_hdr *hdr,
+ u8 **resp, size_t *resp_len)
+{
+ struct wpa_ssid *config = eap_get_config(sm);
+ u8 *buf, *pos, *challenge;
+
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase 2 MSCHAP Request");
+
+ pos = buf = malloc(config->identity_len + 1000);
+ if (buf == NULL) {
+ wpa_printf(MSG_ERROR,
+ "EAP-TTLS/MSCHAP: Failed to allocate memory");
+ return -1;
+ }
+
+ /* User-Name */
+ pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_USER_NAME, 0, 1,
+ config->identity, config->identity_len);
+
+ /* MS-CHAP-Challenge */
+ challenge = eap_tls_derive_key(sm, &data->ssl, "ttls challenge",
+ EAP_TLS_KEY_LEN);
+ if (challenge == NULL) {
+ free(buf);
+ wpa_printf(MSG_ERROR, "EAP-TTLS/MSCHAP: Failed to derive "
+ "implicit challenge");
+ return -1;
+ }
+
+ pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_MS_CHAP_CHALLENGE,
+ RADIUS_VENDOR_ID_MICROSOFT, 1,
+ challenge, EAP_TTLS_MSCHAP_CHALLENGE_LEN);
+
+ /* MS-CHAP-Response */
+ pos = eap_ttls_avp_hdr(pos, RADIUS_ATTR_MS_CHAP_RESPONSE,
+ RADIUS_VENDOR_ID_MICROSOFT, 1,
+ EAP_TTLS_MSCHAP_RESPONSE_LEN);
+ data->ident = challenge[EAP_TTLS_MSCHAP_CHALLENGE_LEN];
+ *pos++ = data->ident;
+ *pos++ = 1; /* Flags: Use NT style passwords */
+ memset(pos, 0, 24); /* LM-Response */
+ pos += 24;
+ nt_challenge_response(challenge,
+ config->password, config->password_len,
+ pos); /* NT-Response */
+ wpa_hexdump_ascii_key(MSG_DEBUG, "EAP-TTLS: MSCHAP password",
+ config->password, config->password_len);
+ wpa_hexdump(MSG_DEBUG, "EAP-TTLS: MSCHAP implicit challenge",
+ challenge, EAP_TTLS_MSCHAP_CHALLENGE_LEN);
+ wpa_hexdump(MSG_DEBUG, "EAP-TTLS: MSCHAP response", pos, 24);
+ pos += 24;
+ free(challenge);
+ AVP_PAD(buf, pos);
+
+ *resp = buf;
+ *resp_len = pos - buf;
+
+ /* EAP-TTLS/MSCHAP does not provide tunneled success notification, so
+ * assume that Phase2 succeeds. */
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_COND_SUCC;
+
+ return 0;
+}
+
+
+static int eap_ttls_phase2_request_pap(struct eap_sm *sm,
+ struct eap_ttls_data *data,
+ struct eap_method_ret *ret,
+ struct eap_hdr *req,
+ struct eap_hdr *hdr,
+ u8 **resp, size_t *resp_len)
+{
+ struct wpa_ssid *config = eap_get_config(sm);
+ u8 *buf, *pos;
+ size_t pad;
+
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase 2 PAP Request");
+
+ pos = buf = malloc(config->identity_len + config->password_len + 100);
+ if (buf == NULL) {
+ wpa_printf(MSG_ERROR,
+ "EAP-TTLS/PAP: Failed to allocate memory");
+ return -1;
+ }
+
+ /* User-Name */
+ pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_USER_NAME, 0, 1,
+ config->identity, config->identity_len);
+
+ /* User-Password; in RADIUS, this is encrypted, but EAP-TTLS encrypts
+ * the data, so no separate encryption is used in the AVP itself.
+ * However, the password is padded to obfuscate its length. */
+ pad = (16 - (config->password_len & 15)) & 15;
+ pos = eap_ttls_avp_hdr(pos, RADIUS_ATTR_USER_PASSWORD, 0, 1,
+ config->password_len + pad);
+ memcpy(pos, config->password, config->password_len);
+ pos += config->password_len;
+ memset(pos, 0, pad);
+ pos += pad;
+ AVP_PAD(buf, pos);
+
+ *resp = buf;
+ *resp_len = pos - buf;
+
+ /* EAP-TTLS/PAP does not provide tunneled success notification, so
+ * assume that Phase2 succeeds. */
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_COND_SUCC;
+
+ return 0;
+}
+
+
+static int eap_ttls_phase2_request_chap(struct eap_sm *sm,
+ struct eap_ttls_data *data,
+ struct eap_method_ret *ret,
+ struct eap_hdr *req,
+ struct eap_hdr *hdr,
+ u8 **resp, size_t *resp_len)
+{
+ struct wpa_ssid *config = eap_get_config(sm);
+ u8 *buf, *pos, *challenge;
+ MD5_CTX context;
+
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase 2 CHAP Request");
+
+ pos = buf = malloc(config->identity_len + 1000);
+ if (buf == NULL) {
+ wpa_printf(MSG_ERROR,
+ "EAP-TTLS/CHAP: Failed to allocate memory");
+ return -1;
+ }
+
+ /* User-Name */
+ pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_USER_NAME, 0, 1,
+ config->identity, config->identity_len);
+
+ /* CHAP-Challenge */
+ challenge = eap_tls_derive_key(sm, &data->ssl, "ttls challenge",
+ EAP_TLS_KEY_LEN);
+ if (challenge == NULL) {
+ free(buf);
+ wpa_printf(MSG_ERROR, "EAP-TTLS/CHAP: Failed to derive "
+ "implicit challenge");
+ return -1;
+ }
+
+ pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_CHAP_CHALLENGE, 0, 1,
+ challenge, EAP_TTLS_CHAP_CHALLENGE_LEN);
+
+ /* CHAP-Password */
+ pos = eap_ttls_avp_hdr(pos, RADIUS_ATTR_CHAP_PASSWORD, 0, 1,
+ 1 + EAP_TTLS_CHAP_PASSWORD_LEN);
+ data->ident = challenge[EAP_TTLS_CHAP_CHALLENGE_LEN];
+ *pos++ = data->ident;
+
+ /* MD5(Ident + Password + Challenge) */
+ MD5Init(&context);
+ MD5Update(&context, &data->ident, 1);
+ MD5Update(&context, config->password, config->password_len);
+ MD5Update(&context, challenge, EAP_TTLS_CHAP_CHALLENGE_LEN);
+ MD5Final(pos, &context);
+
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP-TTLS: CHAP username",
+ config->identity, config->identity_len);
+ wpa_hexdump_ascii_key(MSG_DEBUG, "EAP-TTLS: CHAP password",
+ config->password, config->password_len);
+ wpa_hexdump(MSG_DEBUG, "EAP-TTLS: CHAP implicit challenge",
+ challenge, EAP_TTLS_CHAP_CHALLENGE_LEN);
+ wpa_hexdump(MSG_DEBUG, "EAP-TTLS: CHAP password",
+ pos, EAP_TTLS_CHAP_PASSWORD_LEN);
+ pos += EAP_TTLS_CHAP_PASSWORD_LEN;
+ free(challenge);
+ AVP_PAD(buf, pos);
+
+ *resp = buf;
+ *resp_len = pos - buf;
+
+ /* EAP-TTLS/CHAP does not provide tunneled success notification, so
+ * assume that Phase2 succeeds. */
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_COND_SUCC;
+
+ return 0;
+}
+
+
+static int eap_ttls_phase2_request(struct eap_sm *sm,
+ struct eap_ttls_data *data,
+ struct eap_method_ret *ret,
+ struct eap_hdr *req,
+ struct eap_hdr *hdr,
+ u8 **resp, size_t *resp_len)
+{
+ struct wpa_ssid *config = eap_get_config(sm);
+ int res = 0;
+
+ if (data->phase2_type == EAP_TTLS_PHASE2_MSCHAPV2 ||
+ data->phase2_type == EAP_TTLS_PHASE2_MSCHAP ||
+ data->phase2_type == EAP_TTLS_PHASE2_PAP ||
+ data->phase2_type == EAP_TTLS_PHASE2_CHAP) {
+ if (config == NULL || config->identity == NULL) {
+ wpa_printf(MSG_INFO,
+ "EAP-TTLS: Identity not configured");
+ eap_sm_request_identity(sm, config);
+ if (config->password == NULL)
+ eap_sm_request_password(sm, config);
+ return 0;
+ }
+
+ if (config->password == NULL) {
+ wpa_printf(MSG_INFO,
+ "EAP-TTLS: Password not configured");
+ eap_sm_request_password(sm, config);
+ return 0;
+ }
+ }
+
+ switch (data->phase2_type) {
+ case EAP_TTLS_PHASE2_EAP:
+ res = eap_ttls_phase2_request_eap(sm, data, ret, req, hdr,
+ resp, resp_len);
+ break;
+ case EAP_TTLS_PHASE2_MSCHAPV2:
+ res = eap_ttls_phase2_request_mschapv2(sm, data, ret, req, hdr,
+ resp, resp_len);
+ break;
+ case EAP_TTLS_PHASE2_MSCHAP:
+ res = eap_ttls_phase2_request_mschap(sm, data, ret, req, hdr,
+ resp, resp_len);
+ break;
+ case EAP_TTLS_PHASE2_PAP:
+ res = eap_ttls_phase2_request_pap(sm, data, ret, req, hdr,
+ resp, resp_len);
+ break;
+ case EAP_TTLS_PHASE2_CHAP:
+ res = eap_ttls_phase2_request_chap(sm, data, ret, req, hdr,
+ resp, resp_len);
+ break;
+ default:
+ wpa_printf(MSG_ERROR, "EAP-TTLS: Phase 2 - Unknown");
+ res = -1;
+ break;
+ }
+
+ if (res < 0) {
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_FAIL;
+ }
+
+ return res;
+}
+
+
+static int eap_ttls_decrypt(struct eap_sm *sm, struct eap_ttls_data *data,
+ struct eap_method_ret *ret, struct eap_hdr *req,
+ u8 *in_data, size_t in_len,
+ u8 **out_data, size_t *out_len)
+{
+ u8 *in_decrypted = NULL, *pos;
+ int buf_len, len_decrypted = 0, len, left, retval = 0, res;
+ struct eap_hdr *hdr = NULL;
+ u8 *resp = NULL, *mschapv2 = NULL, *eapdata = NULL;
+ size_t resp_len, eap_len = 0;
+ struct ttls_avp *avp;
+ u8 recv_response[20];
+ int mschapv2_error = 0;
+ struct wpa_ssid *config = eap_get_config(sm);
+
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: received %lu bytes encrypted data for"
+ " Phase 2", (unsigned long) in_len);
+
+ if (data->pending_phase2_req) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Pending Phase 2 request - "
+ "skip decryption and use old data");
+ in_decrypted = data->pending_phase2_req;
+ data->pending_phase2_req = NULL;
+ len_decrypted = data->pending_phase2_req_len;
+ if (data->pending_phase2_req_len == 0) {
+ free(in_decrypted);
+ in_decrypted = NULL;
+ goto fake_req_identity;
+ }
+ goto continue_req;
+ }
+
+ if (in_len == 0 && data->phase2_start) {
+ data->phase2_start = 0;
+ /* EAP-TTLS does not use Phase2 on fast re-auth; this must be
+ * done only if TLS part was indeed resuming a previous
+ * session. Most Authentication Servers terminate EAP-TTLS
+ * before reaching this point, but some do not. Make
+ * wpa_supplicant stop phase 2 here, if needed. */
+ if (data->reauth &&
+ tls_connection_resumed(sm->ssl_ctx, data->ssl.conn)) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Session resumption - "
+ "skip phase 2");
+ *out_data = eap_tls_build_ack(&data->ssl, out_len,
+ req->identifier,
+ EAP_TYPE_TTLS, 0);
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_UNCOND_SUCC;
+ data->phase2_success = 1;
+ return 0;
+ }
+ fake_req_identity:
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: empty data in beginning of "
+ "Phase 2 - use fake EAP-Request Identity");
+ buf_len = sizeof(*hdr) + 1;
+ in_decrypted = malloc(buf_len);
+ if (in_decrypted == NULL) {
+ wpa_printf(MSG_WARNING, "EAP-TTLS: failed to allocate "
+ "memory for fake EAP-Identity Request");
+ retval = -1;
+ goto done;
+ }
+ hdr = (struct eap_hdr *) in_decrypted;
+ hdr->code = EAP_CODE_REQUEST;
+ hdr->identifier = 0;
+ hdr->length = host_to_be16(sizeof(*hdr) + 1);
+ in_decrypted[sizeof(*hdr)] = EAP_TYPE_IDENTITY;
+ goto process_eap;
+ }
+
+ res = eap_tls_data_reassemble(sm, &data->ssl, &in_data, &in_len);
+ if (res < 0 || res == 1) {
+ retval = res;
+ goto done;
+ }
+
+ buf_len = in_len;
+ if (data->ssl.tls_in_total > buf_len)
+ buf_len = data->ssl.tls_in_total;
+ in_decrypted = malloc(buf_len);
+ if (in_decrypted == NULL) {
+ free(data->ssl.tls_in);
+ data->ssl.tls_in = NULL;
+ data->ssl.tls_in_len = 0;
+ wpa_printf(MSG_WARNING, "EAP-TTLS: failed to allocate memory "
+ "for decryption");
+ retval = -1;
+ goto done;
+ }
+
+ len_decrypted = tls_connection_decrypt(sm->ssl_ctx, data->ssl.conn,
+ in_data, in_len,
+ in_decrypted, buf_len);
+ free(data->ssl.tls_in);
+ data->ssl.tls_in = NULL;
+ data->ssl.tls_in_len = 0;
+ if (len_decrypted < 0) {
+ wpa_printf(MSG_INFO, "EAP-TTLS: Failed to decrypt Phase 2 "
+ "data");
+ retval = -1;
+ goto done;
+ }
+
+continue_req:
+ data->phase2_start = 0;
+
+ wpa_hexdump(MSG_DEBUG, "EAP-TTLS: Decrypted Phase 2 AVPs",
+ in_decrypted, len_decrypted);
+ if (len_decrypted < sizeof(struct ttls_avp)) {
+ wpa_printf(MSG_WARNING, "EAP-TTLS: Too short Phase 2 AVP frame"
+ " len=%d expected %lu or more - dropped",
+ len_decrypted,
+ (unsigned long) sizeof(struct ttls_avp));
+ retval = -1;
+ goto done;
+ }
+
+ /* Parse AVPs */
+ pos = in_decrypted;
+ left = len_decrypted;
+ mschapv2 = NULL;
+
+ while (left > 0) {
+ u32 avp_code, avp_length, vendor_id = 0;
+ u8 avp_flags, *dpos;
+ size_t pad, dlen;
+ avp = (struct ttls_avp *) pos;
+ avp_code = be_to_host32(avp->avp_code);
+ avp_length = be_to_host32(avp->avp_length);
+ avp_flags = (avp_length >> 24) & 0xff;
+ avp_length &= 0xffffff;
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: AVP: code=%d flags=0x%02x "
+ "length=%d", (int) avp_code, avp_flags,
+ (int) avp_length);
+ if (avp_length > left) {
+ wpa_printf(MSG_WARNING, "EAP-TTLS: AVP overflow "
+ "(len=%d, left=%d) - dropped",
+ (int) avp_length, left);
+ retval = -1;
+ goto done;
+ }
+ dpos = (u8 *) (avp + 1);
+ dlen = avp_length - sizeof(*avp);
+ if (avp_flags & AVP_FLAGS_VENDOR) {
+ if (dlen < 4) {
+ wpa_printf(MSG_WARNING, "EAP-TTLS: vendor AVP "
+ "underflow");
+ retval = -1;
+ goto done;
+ }
+ vendor_id = be_to_host32(* (u32 *) dpos);
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: AVP vendor_id %d",
+ (int) vendor_id);
+ dpos += 4;
+ dlen -= 4;
+ }
+
+ wpa_hexdump(MSG_DEBUG, "EAP-TTLS: AVP data", dpos, dlen);
+
+ if (vendor_id == 0 && avp_code == RADIUS_ATTR_EAP_MESSAGE) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: AVP - EAP Message");
+ if (eapdata == NULL) {
+ eapdata = malloc(dlen);
+ if (eapdata == NULL) {
+ retval = -1;
+ wpa_printf(MSG_WARNING, "EAP-TTLS: "
+ "failed to allocate memory "
+ "for Phase 2 EAP data");
+ goto done;
+ }
+ memcpy(eapdata, dpos, dlen);
+ eap_len = dlen;
+ } else {
+ u8 *neweap = realloc(eapdata, eap_len + dlen);
+ if (neweap == NULL) {
+ retval = -1;
+ wpa_printf(MSG_WARNING, "EAP-TTLS: "
+ "failed to allocate memory "
+ "for Phase 2 EAP data");
+ goto done;
+ }
+ memcpy(neweap + eap_len, dpos, dlen);
+ eapdata = neweap;
+ eap_len += dlen;
+ }
+ } else if (vendor_id == 0 &&
+ avp_code == RADIUS_ATTR_REPLY_MESSAGE) {
+ /* This is an optional message that can be displayed to
+ * the user. */
+ wpa_hexdump_ascii(MSG_DEBUG,
+ "EAP-TTLS: AVP - Reply-Message",
+ dpos, dlen);
+ } else if (vendor_id == RADIUS_VENDOR_ID_MICROSOFT &&
+ avp_code == RADIUS_ATTR_MS_CHAP2_SUCCESS) {
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP-TTLS: "
+ "MS-CHAP2-Success", dpos, dlen);
+ if (dlen != 43) {
+ wpa_printf(MSG_WARNING, "EAP-TTLS: Unexpected "
+ "MS-CHAP2-Success length "
+ "(len=%lu, expected 43)",
+ (unsigned long) dlen);
+ retval = -1;
+ break;
+ }
+ mschapv2 = dpos;
+ } else if (vendor_id == RADIUS_VENDOR_ID_MICROSOFT &&
+ avp_code == RADIUS_ATTR_MS_CHAP_ERROR) {
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP-TTLS: "
+ "MS-CHAP-Error", dpos, dlen);
+ mschapv2_error = 1;
+ } else if (avp_flags & AVP_FLAGS_MANDATORY) {
+ wpa_printf(MSG_WARNING, "EAP-TTLS: Unsupported "
+ "mandatory AVP code %d vendor_id %d - "
+ "dropped", (int) avp_code, (int) vendor_id);
+ retval = -1;
+ goto done;
+ } else {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Ignoring unsupported "
+ "AVP code %d vendor_id %d",
+ (int) avp_code, (int) vendor_id);
+ }
+
+ pad = (4 - (avp_length & 3)) & 3;
+ pos += avp_length + pad;
+ left -= avp_length + pad;
+ }
+
+ switch (data->phase2_type) {
+ case EAP_TTLS_PHASE2_EAP:
+ if (eapdata == NULL) {
+ wpa_printf(MSG_WARNING, "EAP-TTLS: No EAP Message in "
+ "the packet - dropped");
+ retval = -1;
+ goto done;
+ }
+
+ wpa_hexdump(MSG_DEBUG, "EAP-TTLS: Phase 2 EAP",
+ eapdata, eap_len);
+ hdr = (struct eap_hdr *) eapdata;
+
+ if (eap_len < sizeof(*hdr)) {
+ wpa_printf(MSG_WARNING, "EAP-TTLS: Too short Phase 2 "
+ "EAP frame (len=%lu, expected %lu or more) "
+ "- dropped", (unsigned long) eap_len,
+ (unsigned long) sizeof(*hdr));
+ retval = -1;
+ goto done;
+ }
+ len = be_to_host16(hdr->length);
+ if (len > eap_len) {
+ wpa_printf(MSG_INFO, "EAP-TTLS: Length mismatch in "
+ "Phase 2 EAP frame (EAP hdr len=%d, EAP "
+ "data len in AVP=%lu)", len,
+ (unsigned long) eap_len);
+ retval = -1;
+ goto done;
+ }
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: received Phase 2: code=%d "
+ "identifier=%d length=%d",
+ hdr->code, hdr->identifier, len);
+ process_eap:
+ switch (hdr->code) {
+ case EAP_CODE_REQUEST:
+ if (eap_ttls_phase2_request(sm, data, ret, req, hdr,
+ &resp, &resp_len)) {
+ wpa_printf(MSG_INFO, "EAP-TTLS: Phase2 "
+ "Request processing failed");
+ retval = -1;
+ goto done;
+ }
+ break;
+ default:
+ wpa_printf(MSG_INFO, "EAP-TTLS: Unexpected code=%d in "
+ "Phase 2 EAP header", hdr->code);
+ retval = -1;
+ break;
+ }
+ break;
+ case EAP_TTLS_PHASE2_MSCHAPV2:
+ if (mschapv2_error) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Received "
+ "MS-CHAP-Error - failed");
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_FAIL;
+ *out_data = eap_tls_build_ack(&data->ssl, out_len,
+ req->identifier,
+ EAP_TYPE_TTLS, 0);
+ break;
+ }
+
+ if (mschapv2 == NULL) {
+ wpa_printf(MSG_WARNING, "EAP-TTLS: no MS-CHAP2-Success"
+ " AVP received for Phase2 MSCHAPV2");
+ retval = -1;
+ break;
+ }
+ if (mschapv2[0] != data->ident) {
+ wpa_printf(MSG_WARNING, "EAP-TTLS: Ident mismatch "
+ "for Phase 2 MSCHAPV2 (received Ident "
+ "0x%02x, expected 0x%02x)",
+ mschapv2[0], data->ident);
+ retval = -1;
+ break;
+ }
+ if (!data->auth_response_valid ||
+ mschapv2[1] != 'S' || mschapv2[2] != '=' ||
+ hexstr2bin((char *) (mschapv2 + 3), recv_response, 20) ||
+ memcmp(data->auth_response, recv_response, 20) != 0) {
+ wpa_printf(MSG_WARNING, "EAP-TTLS: Invalid "
+ "authenticator response in Phase 2 "
+ "MSCHAPV2 success request");
+ retval = -1;
+ break;
+ }
+
+ wpa_printf(MSG_INFO, "EAP-TTLS: Phase 2 MSCHAPV2 "
+ "authentication succeeded");
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_UNCOND_SUCC;
+ data->phase2_success = 1;
+
+ /* Reply with empty data; authentication server will reply
+ * with EAP-Success after this. */
+ retval = 1;
+ goto done;
+ case EAP_TTLS_PHASE2_MSCHAP:
+ case EAP_TTLS_PHASE2_PAP:
+ case EAP_TTLS_PHASE2_CHAP:
+ /* EAP-TTLS/{MSCHAP,PAP,CHAP} should not send any TLS tunneled
+ * requests to the supplicant */
+ wpa_printf(MSG_INFO, "EAP-TTLS: Phase 2 received unexpected "
+ "tunneled data");
+ retval = -1;
+ break;
+ }
+
+ if (resp) {
+ wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: Encrypting Phase 2 data",
+ resp, resp_len);
+
+ if (eap_ttls_encrypt(sm, data, req->identifier,
+ resp, resp_len, out_data, out_len)) {
+ wpa_printf(MSG_INFO, "EAP-TTLS: Failed to encrypt "
+ "a Phase 2 frame");
+ }
+ free(resp);
+ } else if (config->pending_req_identity ||
+ config->pending_req_password ||
+ config->pending_req_otp) {
+ free(data->pending_phase2_req);
+ data->pending_phase2_req = malloc(len_decrypted);
+ if (data->pending_phase2_req) {
+ memcpy(data->pending_phase2_req, in_decrypted,
+ len_decrypted);
+ data->pending_phase2_req_len = len_decrypted;
+ }
+ }
+
+done:
+ free(in_decrypted);
+ free(eapdata);
+
+ if (retval < 0) {
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_FAIL;
+ }
+
+ return retval;
+}
+
+
+static u8 * eap_ttls_process(struct eap_sm *sm, void *priv,
+ struct eap_method_ret *ret,
+ u8 *reqData, size_t reqDataLen,
+ size_t *respDataLen)
+{
+ struct eap_hdr *req;
+ int left, res;
+ unsigned int tls_msg_len;
+ u8 flags, *pos, *resp, id;
+ struct eap_ttls_data *data = priv;
+
+ if (tls_get_errors(sm->ssl_ctx)) {
+ wpa_printf(MSG_INFO, "EAP-TTLS: TLS errors detected");
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ req = (struct eap_hdr *) reqData;
+ pos = (u8 *) (req + 1);
+ if (reqDataLen < sizeof(*req) + 2 || *pos != EAP_TYPE_TTLS ||
+ (left = be_to_host16(req->length)) > reqDataLen) {
+ wpa_printf(MSG_INFO, "EAP-TTLS: Invalid frame");
+ ret->ignore = TRUE;
+ return NULL;
+ }
+ left -= sizeof(struct eap_hdr);
+ id = req->identifier;
+ pos++;
+ flags = *pos++;
+ left -= 2;
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Received packet(len=%lu) - "
+ "Flags 0x%02x", (unsigned long) reqDataLen, flags);
+ if (flags & EAP_TLS_FLAGS_LENGTH_INCLUDED) {
+ if (left < 4) {
+ wpa_printf(MSG_INFO, "EAP-TTLS: Short frame with TLS "
+ "length");
+ ret->ignore = TRUE;
+ return NULL;
+ }
+ tls_msg_len = (pos[0] << 24) | (pos[1] << 16) | (pos[2] << 8) |
+ pos[3];
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: TLS Message Length: %d",
+ tls_msg_len);
+ if (data->ssl.tls_in_left == 0) {
+ data->ssl.tls_in_total = tls_msg_len;
+ data->ssl.tls_in_left = tls_msg_len;
+ free(data->ssl.tls_in);
+ data->ssl.tls_in = NULL;
+ data->ssl.tls_in_len = 0;
+ }
+ pos += 4;
+ left -= 4;
+ }
+
+ ret->ignore = FALSE;
+ ret->methodState = METHOD_CONT;
+ ret->decision = DECISION_FAIL;
+ ret->allowNotifications = TRUE;
+
+ if (flags & EAP_TLS_FLAGS_START) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Start");
+ /* draft-ietf-pppext-eap-ttls-03.txt, Ch. 8.1:
+ * EAP-TTLS Start packet may, in a future specification, be
+ * allowed to contain data. Client based on this draft version
+ * must ignore such data but must not reject the Start packet.
+ */
+ left = 0;
+ }
+
+ resp = NULL;
+ if (tls_connection_established(sm->ssl_ctx, data->ssl.conn) &&
+ !data->resuming) {
+ res = eap_ttls_decrypt(sm, data, ret, req, pos, left,
+ &resp, respDataLen);
+ } else {
+ res = eap_tls_process_helper(sm, &data->ssl, EAP_TYPE_TTLS, 0,
+ id, pos, left,
+ &resp, respDataLen);
+
+ if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) {
+ wpa_printf(MSG_DEBUG,
+ "EAP-TTLS: TLS done, proceed to Phase 2");
+ if (data->resuming) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: fast reauth -"
+ " may skip Phase 2");
+ ret->decision = DECISION_COND_SUCC;
+ ret->methodState = METHOD_MAY_CONT;
+ }
+ data->phase2_start = 1;
+ free(data->key_data);
+ data->key_data =
+ eap_tls_derive_key(sm, &data->ssl,
+ "ttls keying material",
+ EAP_TLS_KEY_LEN);
+ if (data->key_data) {
+ wpa_hexdump_key(MSG_DEBUG,
+ "EAP-TTLS: Derived key",
+ data->key_data,
+ EAP_TLS_KEY_LEN);
+ } else {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Failed to "
+ "derive key");
+ }
+
+ if (*respDataLen == 0) {
+ if (eap_ttls_decrypt(sm, data, ret, req, NULL,
+ 0, &resp, respDataLen)) {
+ wpa_printf(MSG_WARNING, "EAP-TTLS: "
+ "failed to process early "
+ "start for Phase 2");
+ }
+ res = 0;
+ }
+ data->resuming = 0;
+ }
+ }
+
+ if (ret->methodState == METHOD_DONE) {
+ ret->allowNotifications = FALSE;
+ if (ret->decision == DECISION_UNCOND_SUCC ||
+ ret->decision == DECISION_COND_SUCC) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Authentication "
+ "completed successfully");
+ data->phase2_success = 1;
+ }
+ } else if (sm->workaround && ret->methodState == METHOD_MAY_CONT &&
+ (ret->decision == DECISION_UNCOND_SUCC ||
+ ret->decision == DECISION_COND_SUCC)) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Authentication "
+ "completed successfully (EAP workaround)");
+ data->phase2_success = 1;
+ }
+
+ if (res == 1) {
+ return eap_tls_build_ack(&data->ssl, respDataLen, id,
+ EAP_TYPE_TTLS, 0);
+ }
+ return resp;
+}
+
+
+static Boolean eap_ttls_has_reauth_data(struct eap_sm *sm, void *priv)
+{
+ struct eap_ttls_data *data = priv;
+ return tls_connection_established(sm->ssl_ctx, data->ssl.conn) &&
+ data->phase2_success;
+}
+
+
+static void eap_ttls_deinit_for_reauth(struct eap_sm *sm, void *priv)
+{
+ struct eap_ttls_data *data = priv;
+ free(data->pending_phase2_req);
+ data->pending_phase2_req = NULL;
+}
+
+
+static void * eap_ttls_init_for_reauth(struct eap_sm *sm, void *priv)
+{
+ struct eap_ttls_data *data = priv;
+ free(data->key_data);
+ data->key_data = NULL;
+ if (eap_tls_reauth_init(sm, &data->ssl)) {
+ free(data);
+ return NULL;
+ }
+ data->phase2_start = 0;
+ data->phase2_success = 0;
+ data->resuming = 1;
+ data->reauth = 1;
+ return priv;
+}
+
+
+static int eap_ttls_get_status(struct eap_sm *sm, void *priv, char *buf,
+ size_t buflen, int verbose)
+{
+ struct eap_ttls_data *data = priv;
+ int len;
+
+ len = eap_tls_status(sm, &data->ssl, buf, buflen, verbose);
+ switch (data->phase2_type) {
+ case EAP_TTLS_PHASE2_EAP:
+ len += snprintf(buf + len, buflen - len,
+ "EAP-TTLS Phase2 method=EAP-%s\n",
+ data->phase2_method ? data->phase2_method->name
+ : "?");
+ break;
+ case EAP_TTLS_PHASE2_MSCHAPV2:
+ len += snprintf(buf + len, buflen - len,
+ "EAP-TTLS Phase2 method=MSCHAPV2\n");
+ break;
+ case EAP_TTLS_PHASE2_MSCHAP:
+ len += snprintf(buf + len, buflen - len,
+ "EAP-TTLS Phase2 method=MSCHAP\n");
+ break;
+ case EAP_TTLS_PHASE2_PAP:
+ len += snprintf(buf + len, buflen - len,
+ "EAP-TTLS Phase2 method=PAP\n");
+ break;
+ case EAP_TTLS_PHASE2_CHAP:
+ len += snprintf(buf + len, buflen - len,
+ "EAP-TTLS Phase2 method=CHAP\n");
+ break;
+ }
+
+ return len;
+}
+
+
+static Boolean eap_ttls_isKeyAvailable(struct eap_sm *sm, void *priv)
+{
+ struct eap_ttls_data *data = priv;
+ return data->key_data != NULL && data->phase2_success;
+}
+
+
+static u8 * eap_ttls_getKey(struct eap_sm *sm, void *priv, size_t *len)
+{
+ struct eap_ttls_data *data = priv;
+ u8 *key;
+
+ if (data->key_data == NULL || !data->phase2_success)
+ return NULL;
+
+ key = malloc(EAP_TLS_KEY_LEN);
+ if (key == NULL)
+ return NULL;
+
+ *len = EAP_TLS_KEY_LEN;
+ memcpy(key, data->key_data, EAP_TLS_KEY_LEN);
+
+ return key;
+}
+
+
+const struct eap_method eap_method_ttls =
+{
+ .method = EAP_TYPE_TTLS,
+ .name = "TTLS",
+ .init = eap_ttls_init,
+ .deinit = eap_ttls_deinit,
+ .process = eap_ttls_process,
+ .isKeyAvailable = eap_ttls_isKeyAvailable,
+ .getKey = eap_ttls_getKey,
+ .get_status = eap_ttls_get_status,
+ .has_reauth_data = eap_ttls_has_reauth_data,
+ .deinit_for_reauth = eap_ttls_deinit_for_reauth,
+ .init_for_reauth = eap_ttls_init_for_reauth,
+};
diff --git a/contrib/wpa_supplicant/eap_ttls.h b/contrib/wpa_supplicant/eap_ttls.h
new file mode 100644
index 000000000000..a187db4d34f7
--- /dev/null
+++ b/contrib/wpa_supplicant/eap_ttls.h
@@ -0,0 +1,57 @@
+#ifndef EAP_TTLS_H
+#define EAP_TTLS_H
+
+struct ttls_avp {
+ u32 avp_code;
+ u32 avp_length; /* 8-bit flags, 24-bit length;
+ * length includes AVP header */
+ /* optional 32-bit Vendor-ID */
+ /* Data */
+};
+
+struct ttls_avp_vendor {
+ u32 avp_code;
+ u32 avp_length; /* 8-bit flags, 24-bit length;
+ * length includes AVP header */
+ u32 vendor_id;
+ /* Data */
+};
+
+#define AVP_FLAGS_VENDOR 0x80
+#define AVP_FLAGS_MANDATORY 0x40
+
+#define AVP_PAD(start, pos) \
+do { \
+ int pad; \
+ pad = (4 - (((pos) - (start)) & 3)) & 3; \
+ memset((pos), 0, pad); \
+ pos += pad; \
+} while(0)
+
+
+/* RFC 2865 */
+#define RADIUS_ATTR_USER_NAME 1
+#define RADIUS_ATTR_USER_PASSWORD 2
+#define RADIUS_ATTR_CHAP_PASSWORD 3
+#define RADIUS_ATTR_REPLY_MESSAGE 18
+#define RADIUS_ATTR_CHAP_CHALLENGE 60
+#define RADIUS_ATTR_EAP_MESSAGE 79
+
+/* RFC 2548 */
+#define RADIUS_VENDOR_ID_MICROSOFT 311
+#define RADIUS_ATTR_MS_CHAP_RESPONSE 1
+#define RADIUS_ATTR_MS_CHAP_ERROR 2
+#define RADIUS_ATTR_MS_CHAP_NT_ENC_PW 6
+#define RADIUS_ATTR_MS_CHAP_CHALLENGE 11
+#define RADIUS_ATTR_MS_CHAP2_RESPONSE 25
+#define RADIUS_ATTR_MS_CHAP2_SUCCESS 26
+#define RADIUS_ATTR_MS_CHAP2_CPW 27
+
+#define EAP_TTLS_MSCHAPV2_CHALLENGE_LEN 16
+#define EAP_TTLS_MSCHAPV2_RESPONSE_LEN 50
+#define EAP_TTLS_MSCHAP_CHALLENGE_LEN 8
+#define EAP_TTLS_MSCHAP_RESPONSE_LEN 50
+#define EAP_TTLS_CHAP_CHALLENGE_LEN 16
+#define EAP_TTLS_CHAP_PASSWORD_LEN 16
+
+#endif /* EAP_TTLS_H */
diff --git a/contrib/wpa_supplicant/eapol_sm.c b/contrib/wpa_supplicant/eapol_sm.c
new file mode 100644
index 000000000000..93f9a54a6c88
--- /dev/null
+++ b/contrib/wpa_supplicant/eapol_sm.c
@@ -0,0 +1,1421 @@
+/*
+ * WPA Supplicant / EAPOL state machines
+ * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published 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 <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "common.h"
+#include "eapol_sm.h"
+#include "eap.h"
+#include "eloop.h"
+#include "wpa_supplicant.h"
+#include "l2_packet.h"
+#include "wpa.h"
+#include "md5.h"
+#include "rc4.h"
+
+
+/* IEEE 802.1aa/D6.1 - Supplicant */
+
+struct eapol_sm {
+ /* Timers */
+ unsigned int authWhile;
+ unsigned int heldWhile;
+ unsigned int startWhen;
+ unsigned int idleWhile; /* for EAP state machine */
+
+ /* Global variables */
+ Boolean eapFail;
+ Boolean eapolEap;
+ Boolean eapSuccess;
+ Boolean initialize;
+ Boolean keyDone;
+ Boolean keyRun;
+ PortControl portControl;
+ Boolean portEnabled;
+ PortStatus suppPortStatus; /* dot1xSuppControlledPortStatus */
+ Boolean portValid;
+ Boolean suppAbort;
+ Boolean suppFail;
+ Boolean suppStart;
+ Boolean suppSuccess;
+ Boolean suppTimeout;
+
+ /* Supplicant PAE state machine */
+ enum {
+ SUPP_PAE_UNKNOWN = 0,
+ SUPP_PAE_DISCONNECTED = 1,
+ SUPP_PAE_LOGOFF = 2,
+ SUPP_PAE_CONNECTING = 3,
+ SUPP_PAE_AUTHENTICATING = 4,
+ SUPP_PAE_AUTHENTICATED = 5,
+ /* unused(6) */
+ SUPP_PAE_HELD = 7,
+ SUPP_PAE_RESTART = 8,
+ SUPP_PAE_S_FORCE_AUTH = 9,
+ SUPP_PAE_S_FORCE_UNAUTH = 10
+ } SUPP_PAE_state; /* dot1xSuppPaeState */
+ /* Variables */
+ Boolean userLogoff;
+ Boolean logoffSent;
+ unsigned int startCount;
+ Boolean eapRestart;
+ PortControl sPortMode;
+ /* Constants */
+ unsigned int heldPeriod; /* dot1xSuppHeldPeriod */
+ unsigned int startPeriod; /* dot1xSuppStartPeriod */
+ unsigned int maxStart; /* dot1xSuppMaxStart */
+
+ /* Key Receive state machine */
+ enum {
+ KEY_RX_UNKNOWN = 0,
+ KEY_RX_NO_KEY_RECEIVE, KEY_RX_KEY_RECEIVE
+ } KEY_RX_state;
+ /* Variables */
+ Boolean rxKey;
+
+ /* Supplicant Backend state machine */
+ enum {
+ SUPP_BE_UNKNOWN = 0,
+ SUPP_BE_INITIALIZE = 1,
+ SUPP_BE_IDLE = 2,
+ SUPP_BE_REQUEST = 3,
+ SUPP_BE_RECEIVE = 4,
+ SUPP_BE_RESPONSE = 5,
+ SUPP_BE_FAIL = 6,
+ SUPP_BE_TIMEOUT = 7,
+ SUPP_BE_SUCCESS = 8
+ } SUPP_BE_state; /* dot1xSuppBackendPaeState */
+ /* Variables */
+ Boolean eapNoResp;
+ Boolean eapReq;
+ Boolean eapResp;
+ /* Constants */
+ unsigned int authPeriod; /* dot1xSuppAuthPeriod */
+
+ /* Statistics */
+ unsigned int dot1xSuppEapolFramesRx;
+ unsigned int dot1xSuppEapolFramesTx;
+ unsigned int dot1xSuppEapolStartFramesTx;
+ unsigned int dot1xSuppEapolLogoffFramesTx;
+ unsigned int dot1xSuppEapolRespFramesTx;
+ unsigned int dot1xSuppEapolReqIdFramesRx;
+ unsigned int dot1xSuppEapolReqFramesRx;
+ unsigned int dot1xSuppInvalidEapolFramesRx;
+ unsigned int dot1xSuppEapLengthErrorFramesRx;
+ unsigned int dot1xSuppLastEapolFrameVersion;
+ unsigned char dot1xSuppLastEapolFrameSource[6];
+
+ /* Miscellaneous variables (not defined in IEEE 802.1aa/D6.1) */
+ Boolean changed;
+ struct eap_sm *eap;
+ struct wpa_ssid *config;
+ Boolean initial_req;
+ u8 *last_rx_key;
+ size_t last_rx_key_len;
+ u8 *eapReqData; /* for EAP */
+ size_t eapReqDataLen; /* for EAP */
+ Boolean altAccept; /* for EAP */
+ Boolean altReject; /* for EAP */
+ Boolean replay_counter_valid;
+ u8 last_replay_counter[16];
+ struct eapol_config conf;
+ struct eapol_ctx *ctx;
+ enum { EAPOL_CB_IN_PROGRESS = 0, EAPOL_CB_SUCCESS, EAPOL_CB_FAILURE }
+ cb_status;
+ Boolean cached_pmk;
+
+ Boolean unicast_key_received, broadcast_key_received;
+};
+
+
+static void eapol_sm_txLogoff(struct eapol_sm *sm);
+static void eapol_sm_txStart(struct eapol_sm *sm);
+static void eapol_sm_processKey(struct eapol_sm *sm);
+static void eapol_sm_getSuppRsp(struct eapol_sm *sm);
+static void eapol_sm_txSuppRsp(struct eapol_sm *sm);
+static void eapol_sm_abortSupp(struct eapol_sm *sm);
+static void eapol_sm_abort_cached(struct eapol_sm *sm);
+static void eapol_sm_step_timeout(void *eloop_ctx, void *timeout_ctx);
+
+static struct eapol_callbacks eapol_cb;
+
+
+/* Definitions for clarifying state machine implementation */
+#define SM_STATE(machine, state) \
+static void sm_ ## machine ## _ ## state ## _Enter(struct eapol_sm *sm, \
+ int global)
+
+#define SM_ENTRY(machine, state) \
+if (!global || sm->machine ## _state != machine ## _ ## state) { \
+ sm->changed = TRUE; \
+ wpa_printf(MSG_DEBUG, "EAPOL: " #machine " entering state " #state); \
+} \
+sm->machine ## _state = machine ## _ ## state;
+
+#define SM_ENTER(machine, state) \
+sm_ ## machine ## _ ## state ## _Enter(sm, 0)
+#define SM_ENTER_GLOBAL(machine, state) \
+sm_ ## machine ## _ ## state ## _Enter(sm, 1)
+
+#define SM_STEP(machine) \
+static void sm_ ## machine ## _Step(struct eapol_sm *sm)
+
+#define SM_STEP_RUN(machine) sm_ ## machine ## _Step(sm)
+
+
+/* Port Timers state machine - implemented as a function that will be called
+ * once a second as a registered event loop timeout */
+static void eapol_port_timers_tick(void *eloop_ctx, void *timeout_ctx)
+{
+ struct eapol_sm *sm = timeout_ctx;
+
+ if (sm->authWhile > 0)
+ sm->authWhile--;
+ if (sm->heldWhile > 0)
+ sm->heldWhile--;
+ if (sm->startWhen > 0)
+ sm->startWhen--;
+ if (sm->idleWhile > 0)
+ sm->idleWhile--;
+ wpa_printf(MSG_MSGDUMP, "EAPOL: Port Timers tick - authWhile=%d "
+ "heldWhile=%d startWhen=%d idleWhile=%d",
+ sm->authWhile, sm->heldWhile, sm->startWhen, sm->idleWhile);
+
+ eapol_sm_step(sm);
+
+ eloop_register_timeout(1, 0, eapol_port_timers_tick, eloop_ctx, sm);
+}
+
+
+SM_STATE(SUPP_PAE, LOGOFF)
+{
+ SM_ENTRY(SUPP_PAE, LOGOFF);
+ eapol_sm_txLogoff(sm);
+ sm->logoffSent = TRUE;
+ sm->suppPortStatus = Unauthorized;
+}
+
+
+SM_STATE(SUPP_PAE, DISCONNECTED)
+{
+ SM_ENTRY(SUPP_PAE, DISCONNECTED);
+ sm->sPortMode = Auto;
+ sm->startCount = 0;
+ sm->logoffSent = FALSE;
+ sm->suppPortStatus = Unauthorized;
+ sm->suppAbort = TRUE;
+
+ sm->unicast_key_received = FALSE;
+ sm->broadcast_key_received = FALSE;
+}
+
+
+SM_STATE(SUPP_PAE, CONNECTING)
+{
+ SM_ENTRY(SUPP_PAE, CONNECTING);
+ sm->startWhen = sm->startPeriod;
+ sm->startCount++;
+ sm->eapolEap = FALSE;
+ eapol_sm_txStart(sm);
+}
+
+
+SM_STATE(SUPP_PAE, AUTHENTICATING)
+{
+ SM_ENTRY(SUPP_PAE, AUTHENTICATING);
+ sm->startCount = 0;
+ sm->suppSuccess = FALSE;
+ sm->suppFail = FALSE;
+ sm->suppTimeout = FALSE;
+ sm->keyRun = FALSE;
+ sm->keyDone = FALSE;
+ sm->suppStart = TRUE;
+}
+
+
+SM_STATE(SUPP_PAE, HELD)
+{
+ SM_ENTRY(SUPP_PAE, HELD);
+ sm->heldWhile = sm->heldPeriod;
+ sm->suppPortStatus = Unauthorized;
+ sm->cb_status = EAPOL_CB_FAILURE;
+}
+
+
+SM_STATE(SUPP_PAE, AUTHENTICATED)
+{
+ SM_ENTRY(SUPP_PAE, AUTHENTICATED);
+ sm->suppPortStatus = Authorized;
+ sm->cb_status = EAPOL_CB_SUCCESS;
+}
+
+
+SM_STATE(SUPP_PAE, RESTART)
+{
+ SM_ENTRY(SUPP_PAE, RESTART);
+ sm->eapRestart = TRUE;
+}
+
+
+SM_STATE(SUPP_PAE, S_FORCE_AUTH)
+{
+ SM_ENTRY(SUPP_PAE, S_FORCE_AUTH);
+ sm->suppPortStatus = Authorized;
+ sm->sPortMode = ForceAuthorized;
+}
+
+
+SM_STATE(SUPP_PAE, S_FORCE_UNAUTH)
+{
+ SM_ENTRY(SUPP_PAE, S_FORCE_UNAUTH);
+ sm->suppPortStatus = Unauthorized;
+ sm->sPortMode = ForceUnauthorized;
+ eapol_sm_txLogoff(sm);
+}
+
+
+SM_STEP(SUPP_PAE)
+{
+ if ((sm->userLogoff && !sm->logoffSent) &&
+ !(sm->initialize || !sm->portEnabled))
+ SM_ENTER_GLOBAL(SUPP_PAE, LOGOFF);
+ else if (((sm->portControl == Auto) &&
+ (sm->sPortMode != sm->portControl)) ||
+ sm->initialize || !sm->portEnabled)
+ SM_ENTER_GLOBAL(SUPP_PAE, DISCONNECTED);
+ else if ((sm->portControl == ForceAuthorized) &&
+ (sm->sPortMode != sm->portControl) &&
+ !(sm->initialize || !sm->portEnabled))
+ SM_ENTER_GLOBAL(SUPP_PAE, S_FORCE_AUTH);
+ else if ((sm->portControl == ForceUnauthorized) &&
+ (sm->sPortMode != sm->portControl) &&
+ !(sm->initialize || !sm->portEnabled))
+ SM_ENTER_GLOBAL(SUPP_PAE, S_FORCE_UNAUTH);
+ else switch (sm->SUPP_PAE_state) {
+ case SUPP_PAE_UNKNOWN:
+ break;
+ case SUPP_PAE_LOGOFF:
+ if (!sm->userLogoff)
+ SM_ENTER(SUPP_PAE, DISCONNECTED);
+ break;
+ case SUPP_PAE_DISCONNECTED:
+ SM_ENTER(SUPP_PAE, CONNECTING);
+ break;
+ case SUPP_PAE_CONNECTING:
+ if (sm->startWhen == 0 && sm->startCount < sm->maxStart)
+ SM_ENTER(SUPP_PAE, CONNECTING);
+ else if (sm->startWhen == 0 &&
+ sm->startCount >= sm->maxStart &&
+ sm->portValid)
+ SM_ENTER(SUPP_PAE, AUTHENTICATED);
+ else if (sm->eapSuccess || sm->eapFail)
+ SM_ENTER(SUPP_PAE, AUTHENTICATING);
+ else if (sm->eapolEap)
+ SM_ENTER(SUPP_PAE, RESTART);
+ else if (sm->startWhen == 0 &&
+ sm->startCount >= sm->maxStart &&
+ !sm->portValid)
+ SM_ENTER(SUPP_PAE, HELD);
+ break;
+ case SUPP_PAE_AUTHENTICATING:
+ if (sm->eapSuccess && !sm->portValid &&
+ sm->conf.accept_802_1x_keys &&
+ sm->conf.required_keys == 0) {
+ wpa_printf(MSG_DEBUG, "EAPOL: IEEE 802.1X for "
+ "plaintext connection; no EAPOL-Key frames "
+ "required");
+ sm->portValid = TRUE;
+ if (sm->ctx->eapol_done_cb)
+ sm->ctx->eapol_done_cb(sm->ctx->ctx);
+ }
+ if (sm->eapSuccess && sm->portValid)
+ SM_ENTER(SUPP_PAE, AUTHENTICATED);
+ else if (sm->eapFail || (sm->keyDone && !sm->portValid))
+ SM_ENTER(SUPP_PAE, HELD);
+ else if (sm->suppTimeout)
+ SM_ENTER(SUPP_PAE, CONNECTING);
+ break;
+ case SUPP_PAE_HELD:
+ if (sm->heldWhile == 0)
+ SM_ENTER(SUPP_PAE, CONNECTING);
+ else if (sm->eapolEap)
+ SM_ENTER(SUPP_PAE, RESTART);
+ break;
+ case SUPP_PAE_AUTHENTICATED:
+ if (sm->eapolEap && sm->portValid)
+ SM_ENTER(SUPP_PAE, RESTART);
+ else if (!sm->portValid)
+ SM_ENTER(SUPP_PAE, DISCONNECTED);
+ break;
+ case SUPP_PAE_RESTART:
+ if (!sm->eapRestart)
+ SM_ENTER(SUPP_PAE, AUTHENTICATING);
+ break;
+ case SUPP_PAE_S_FORCE_AUTH:
+ break;
+ case SUPP_PAE_S_FORCE_UNAUTH:
+ break;
+ }
+}
+
+
+SM_STATE(KEY_RX, NO_KEY_RECEIVE)
+{
+ SM_ENTRY(KEY_RX, NO_KEY_RECEIVE);
+}
+
+
+SM_STATE(KEY_RX, KEY_RECEIVE)
+{
+ SM_ENTRY(KEY_RX, KEY_RECEIVE);
+ eapol_sm_processKey(sm);
+ sm->rxKey = FALSE;
+}
+
+
+SM_STEP(KEY_RX)
+{
+ if (sm->initialize || !sm->portEnabled)
+ SM_ENTER_GLOBAL(KEY_RX, NO_KEY_RECEIVE);
+ switch (sm->KEY_RX_state) {
+ case KEY_RX_UNKNOWN:
+ break;
+ case KEY_RX_NO_KEY_RECEIVE:
+ if (sm->rxKey)
+ SM_ENTER(KEY_RX, KEY_RECEIVE);
+ break;
+ case KEY_RX_KEY_RECEIVE:
+ if (sm->rxKey)
+ SM_ENTER(KEY_RX, KEY_RECEIVE);
+ break;
+ }
+}
+
+
+SM_STATE(SUPP_BE, REQUEST)
+{
+ SM_ENTRY(SUPP_BE, REQUEST);
+ sm->authWhile = 0;
+ sm->eapReq = TRUE;
+ eapol_sm_getSuppRsp(sm);
+}
+
+
+SM_STATE(SUPP_BE, RESPONSE)
+{
+ SM_ENTRY(SUPP_BE, RESPONSE);
+ eapol_sm_txSuppRsp(sm);
+ sm->eapResp = FALSE;
+}
+
+
+SM_STATE(SUPP_BE, SUCCESS)
+{
+ SM_ENTRY(SUPP_BE, SUCCESS);
+ sm->keyRun = TRUE;
+ sm->suppSuccess = TRUE;
+
+ if (eap_key_available(sm->eap)) {
+ /* New key received - clear IEEE 802.1X EAPOL-Key replay
+ * counter */
+ sm->replay_counter_valid = FALSE;
+ }
+}
+
+
+SM_STATE(SUPP_BE, FAIL)
+{
+ SM_ENTRY(SUPP_BE, FAIL);
+ sm->suppFail = TRUE;
+}
+
+
+SM_STATE(SUPP_BE, TIMEOUT)
+{
+ SM_ENTRY(SUPP_BE, TIMEOUT);
+ sm->suppTimeout = TRUE;
+}
+
+
+SM_STATE(SUPP_BE, IDLE)
+{
+ SM_ENTRY(SUPP_BE, IDLE);
+ sm->suppStart = FALSE;
+ sm->initial_req = TRUE;
+}
+
+
+SM_STATE(SUPP_BE, INITIALIZE)
+{
+ SM_ENTRY(SUPP_BE, INITIALIZE);
+ eapol_sm_abortSupp(sm);
+ sm->suppAbort = FALSE;
+}
+
+
+SM_STATE(SUPP_BE, RECEIVE)
+{
+ SM_ENTRY(SUPP_BE, RECEIVE);
+ sm->authWhile = sm->authPeriod;
+ sm->eapolEap = FALSE;
+ sm->eapNoResp = FALSE;
+ sm->initial_req = FALSE;
+}
+
+
+SM_STEP(SUPP_BE)
+{
+ if (sm->initialize || sm->suppAbort)
+ SM_ENTER_GLOBAL(SUPP_BE, INITIALIZE);
+ else switch (sm->SUPP_BE_state) {
+ case SUPP_BE_UNKNOWN:
+ break;
+ case SUPP_BE_REQUEST:
+ if (sm->eapResp && sm->eapNoResp) {
+ wpa_printf(MSG_DEBUG, "EAPOL: SUPP_BE REQUEST: both "
+ "eapResp and eapNoResp set?!");
+ }
+ if (sm->eapResp)
+ SM_ENTER(SUPP_BE, RESPONSE);
+ else if (sm->eapNoResp)
+ SM_ENTER(SUPP_BE, RECEIVE);
+ break;
+ case SUPP_BE_RESPONSE:
+ SM_ENTER(SUPP_BE, RECEIVE);
+ break;
+ case SUPP_BE_SUCCESS:
+ SM_ENTER(SUPP_BE, IDLE);
+ break;
+ case SUPP_BE_FAIL:
+ SM_ENTER(SUPP_BE, IDLE);
+ break;
+ case SUPP_BE_TIMEOUT:
+ SM_ENTER(SUPP_BE, IDLE);
+ break;
+ case SUPP_BE_IDLE:
+ if (sm->eapFail && sm->suppStart)
+ SM_ENTER(SUPP_BE, FAIL);
+ else if (sm->eapolEap && sm->suppStart)
+ SM_ENTER(SUPP_BE, REQUEST);
+ else if (sm->eapSuccess && sm->suppStart)
+ SM_ENTER(SUPP_BE, SUCCESS);
+ break;
+ case SUPP_BE_INITIALIZE:
+ SM_ENTER(SUPP_BE, IDLE);
+ break;
+ case SUPP_BE_RECEIVE:
+ if (sm->eapolEap)
+ SM_ENTER(SUPP_BE, REQUEST);
+ else if (sm->eapFail)
+ SM_ENTER(SUPP_BE, FAIL);
+ else if (sm->authWhile == 0)
+ SM_ENTER(SUPP_BE, TIMEOUT);
+ else if (sm->eapSuccess)
+ SM_ENTER(SUPP_BE, SUCCESS);
+ break;
+ }
+}
+
+
+static void eapol_sm_txLogoff(struct eapol_sm *sm)
+{
+ wpa_printf(MSG_DEBUG, "EAPOL: txLogoff");
+ sm->ctx->eapol_send(sm->ctx->ctx, IEEE802_1X_TYPE_EAPOL_LOGOFF,
+ (u8 *) "", 0);
+ sm->dot1xSuppEapolLogoffFramesTx++;
+ sm->dot1xSuppEapolFramesTx++;
+}
+
+
+static void eapol_sm_txStart(struct eapol_sm *sm)
+{
+ wpa_printf(MSG_DEBUG, "EAPOL: txStart");
+ sm->ctx->eapol_send(sm->ctx->ctx, IEEE802_1X_TYPE_EAPOL_START,
+ (u8 *) "", 0);
+ sm->dot1xSuppEapolStartFramesTx++;
+ sm->dot1xSuppEapolFramesTx++;
+}
+
+
+#define IEEE8021X_ENCR_KEY_LEN 32
+#define IEEE8021X_SIGN_KEY_LEN 32
+
+struct eap_key_data {
+ u8 encr_key[IEEE8021X_ENCR_KEY_LEN];
+ u8 sign_key[IEEE8021X_SIGN_KEY_LEN];
+};
+
+
+static void eapol_sm_processKey(struct eapol_sm *sm)
+{
+ struct ieee802_1x_hdr *hdr;
+ struct ieee802_1x_eapol_key *key;
+ struct eap_key_data keydata;
+ u8 orig_key_sign[IEEE8021X_KEY_SIGN_LEN], datakey[32];
+ u8 ekey[IEEE8021X_KEY_IV_LEN + IEEE8021X_ENCR_KEY_LEN];
+ int key_len, res, sign_key_len, encr_key_len;
+
+ wpa_printf(MSG_DEBUG, "EAPOL: processKey");
+ if (sm->last_rx_key == NULL)
+ return;
+
+ if (!sm->conf.accept_802_1x_keys) {
+ wpa_printf(MSG_WARNING, "EAPOL: Received IEEE 802.1X EAPOL-Key"
+ " even though this was not accepted - "
+ "ignoring this packet");
+ 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) {
+ wpa_printf(MSG_WARNING, "EAPOL: Too short EAPOL-Key frame");
+ return;
+ }
+ wpa_printf(MSG_DEBUG, "EAPOL: RX IEEE 802.1X ver=%d type=%d len=%d "
+ "EAPOL-Key: type=%d key_length=%d key_index=0x%x",
+ hdr->version, hdr->type, be_to_host16(hdr->length),
+ key->type, be_to_host16(key->key_length), key->key_index);
+
+ sign_key_len = IEEE8021X_SIGN_KEY_LEN;
+ encr_key_len = IEEE8021X_ENCR_KEY_LEN;
+ res = eapol_sm_get_key(sm, (u8 *) &keydata, sizeof(keydata));
+ if (res < 0) {
+ wpa_printf(MSG_DEBUG, "EAPOL: Could not get master key for "
+ "decrypting EAPOL-Key keys");
+ return;
+ }
+ if (res == 16) {
+ /* LEAP derives only 16 bytes of keying material. */
+ res = eapol_sm_get_key(sm, (u8 *) &keydata, 16);
+ if (res) {
+ wpa_printf(MSG_DEBUG, "EAPOL: Could not get LEAP "
+ "master key for decrypting EAPOL-Key keys");
+ return;
+ }
+ sign_key_len = 16;
+ encr_key_len = 16;
+ memcpy(keydata.sign_key, keydata.encr_key, 16);
+ } else if (res) {
+ wpa_printf(MSG_DEBUG, "EAPOL: Could not get enough master key "
+ "data for decrypting EAPOL-Key keys (res=%d)", res);
+ return;
+ }
+
+ /* The key replay_counter must increase when same master key */
+ if (sm->replay_counter_valid &&
+ memcmp(sm->last_replay_counter, key->replay_counter,
+ IEEE8021X_REPLAY_COUNTER_LEN) >= 0) {
+ wpa_printf(MSG_WARNING, "EAPOL: EAPOL-Key replay counter did "
+ "not increase - ignoring key");
+ wpa_hexdump(MSG_DEBUG, "EAPOL: last replay counter",
+ sm->last_replay_counter,
+ IEEE8021X_REPLAY_COUNTER_LEN);
+ wpa_hexdump(MSG_DEBUG, "EAPOL: received replay counter",
+ key->replay_counter, IEEE8021X_REPLAY_COUNTER_LEN);
+ return;
+ }
+
+ /* Verify key signature (HMAC-MD5) */
+ memcpy(orig_key_sign, key->key_signature, IEEE8021X_KEY_SIGN_LEN);
+ memset(key->key_signature, 0, IEEE8021X_KEY_SIGN_LEN);
+ hmac_md5(keydata.sign_key, sign_key_len,
+ sm->last_rx_key, sizeof(*hdr) + be_to_host16(hdr->length),
+ key->key_signature);
+ if (memcmp(orig_key_sign, key->key_signature, IEEE8021X_KEY_SIGN_LEN)
+ != 0) {
+ wpa_printf(MSG_DEBUG, "EAPOL: Invalid key signature in "
+ "EAPOL-Key packet");
+ memcpy(key->key_signature, orig_key_sign,
+ IEEE8021X_KEY_SIGN_LEN);
+ return;
+ }
+ wpa_printf(MSG_DEBUG, "EAPOL: EAPOL-Key key signature verified");
+
+ key_len = be_to_host16(hdr->length) - sizeof(*key);
+ if (key_len > 32 || be_to_host16(key->key_length) > 32) {
+ wpa_printf(MSG_WARNING, "EAPOL: Too long key data length %d",
+ key_len ? key_len : be_to_host16(key->key_length));
+ return;
+ }
+ if (key_len == be_to_host16(key->key_length)) {
+ memcpy(ekey, key->key_iv, IEEE8021X_KEY_IV_LEN);
+ memcpy(ekey + IEEE8021X_KEY_IV_LEN, keydata.encr_key,
+ encr_key_len);
+ memcpy(datakey, key + 1, key_len);
+ rc4(datakey, key_len, ekey,
+ IEEE8021X_KEY_IV_LEN + encr_key_len);
+ wpa_hexdump_key(MSG_DEBUG, "EAPOL: Decrypted(RC4) key",
+ datakey, key_len);
+ } else if (key_len == 0) {
+ /* IEEE 802.1X-REV specifies that least significant Key Length
+ * octets from MS-MPPE-Send-Key are used as the key if the key
+ * data is not present. This seems to be meaning the beginning
+ * of the MS-MPPE-Send-Key. In addition, MS-MPPE-Send-Key in
+ * Supplicant corresponds to MS-MPPE-Recv-Key in Authenticator.
+ * Anyway, taking the beginning of the keying material from EAP
+ * seems to interoperate with Authenticators. */
+ key_len = be_to_host16(key->key_length);
+ memcpy(datakey, keydata.encr_key, key_len);
+ wpa_hexdump_key(MSG_DEBUG, "EAPOL: using part of EAP keying "
+ "material data encryption key",
+ datakey, key_len);
+ } else {
+ wpa_printf(MSG_DEBUG, "EAPOL: Invalid key data length %d "
+ "(key_length=%d)", key_len,
+ be_to_host16(key->key_length));
+ return;
+ }
+
+ sm->replay_counter_valid = TRUE;
+ memcpy(sm->last_replay_counter, key->replay_counter,
+ IEEE8021X_REPLAY_COUNTER_LEN);
+
+ wpa_printf(MSG_DEBUG, "EAPOL: Setting dynamic WEP key: %s keyidx %d "
+ "len %d",
+ key->key_index & IEEE8021X_KEY_INDEX_FLAG ?
+ "unicast" : "broadcast",
+ key->key_index & IEEE8021X_KEY_INDEX_MASK, key_len);
+
+ if (sm->ctx->set_wep_key &&
+ sm->ctx->set_wep_key(sm->ctx->ctx,
+ key->key_index & IEEE8021X_KEY_INDEX_FLAG,
+ key->key_index & IEEE8021X_KEY_INDEX_MASK,
+ datakey, key_len) < 0) {
+ wpa_printf(MSG_WARNING, "EAPOL: Failed to set WEP key to the "
+ " driver.");
+ } else {
+ if (key->key_index & IEEE8021X_KEY_INDEX_FLAG)
+ sm->unicast_key_received = TRUE;
+ else
+ sm->broadcast_key_received = TRUE;
+
+ if ((sm->unicast_key_received ||
+ !(sm->conf.required_keys & EAPOL_REQUIRE_KEY_UNICAST)) &&
+ (sm->broadcast_key_received ||
+ !(sm->conf.required_keys & EAPOL_REQUIRE_KEY_BROADCAST)))
+ {
+ wpa_printf(MSG_DEBUG, "EAPOL: all required EAPOL-Key "
+ "frames received");
+ sm->portValid = TRUE;
+ if (sm->ctx->eapol_done_cb)
+ sm->ctx->eapol_done_cb(sm->ctx->ctx);
+ }
+ }
+}
+
+
+static void eapol_sm_getSuppRsp(struct eapol_sm *sm)
+{
+ wpa_printf(MSG_DEBUG, "EAPOL: getSuppRsp");
+ /* EAP layer processing; no special code is needed, since Supplicant
+ * Backend state machine is waiting for eapNoResp or eapResp to be set
+ * and these are only set in the EAP state machine when the processing
+ * has finished. */
+}
+
+
+static void eapol_sm_txSuppRsp(struct eapol_sm *sm)
+{
+ u8 *resp;
+ size_t resp_len;
+
+ wpa_printf(MSG_DEBUG, "EAPOL: txSuppRsp");
+ resp = eap_get_eapRespData(sm->eap, &resp_len);
+ if (resp == NULL) {
+ wpa_printf(MSG_WARNING, "EAPOL: txSuppRsp - EAP response data "
+ "not available");
+ return;
+ }
+
+ /* Send EAP-Packet from the EAP layer to the Authenticator */
+ sm->ctx->eapol_send(sm->ctx->ctx, IEEE802_1X_TYPE_EAP_PACKET,
+ resp, resp_len);
+
+ /* eapRespData is not used anymore, so free it here */
+ free(resp);
+
+ if (sm->initial_req)
+ sm->dot1xSuppEapolReqIdFramesRx++;
+ else
+ sm->dot1xSuppEapolReqFramesRx++;
+ sm->dot1xSuppEapolRespFramesTx++;
+ sm->dot1xSuppEapolFramesTx++;
+}
+
+
+static void eapol_sm_abortSupp(struct eapol_sm *sm)
+{
+ /* release system resources that may have been allocated for the
+ * authentication session */
+ free(sm->last_rx_key);
+ sm->last_rx_key = NULL;
+ free(sm->eapReqData);
+ sm->eapReqData = NULL;
+ eap_sm_abort(sm->eap);
+}
+
+
+struct eapol_sm *eapol_sm_init(struct eapol_ctx *ctx)
+{
+ struct eapol_sm *sm;
+ sm = malloc(sizeof(*sm));
+ if (sm == NULL)
+ return NULL;
+ memset(sm, 0, sizeof(*sm));
+ sm->ctx = ctx;
+
+ sm->portControl = Auto;
+
+ /* Supplicant PAE state machine */
+ sm->heldPeriod = 60;
+ sm->startPeriod = 30;
+ sm->maxStart = 3;
+
+ /* Supplicant Backend state machine */
+ sm->authPeriod = 30;
+
+ sm->eap = eap_sm_init(sm, &eapol_cb, sm->ctx->msg_ctx);
+ if (sm->eap == NULL) {
+ free(sm);
+ return NULL;
+ }
+
+ /* Initialize EAPOL state machines */
+ sm->initialize = TRUE;
+ eapol_sm_step(sm);
+ sm->initialize = FALSE;
+ eapol_sm_step(sm);
+
+ eloop_register_timeout(1, 0, eapol_port_timers_tick, NULL, sm);
+
+ return sm;
+}
+
+
+void eapol_sm_deinit(struct eapol_sm *sm)
+{
+ if (sm == NULL)
+ return;
+ eloop_cancel_timeout(eapol_sm_step_timeout, NULL, sm);
+ eloop_cancel_timeout(eapol_port_timers_tick, NULL, sm);
+ eap_sm_deinit(sm->eap);
+ free(sm->last_rx_key);
+ free(sm->eapReqData);
+ free(sm->ctx);
+ free(sm);
+}
+
+
+static void eapol_sm_step_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ eapol_sm_step(timeout_ctx);
+}
+
+
+void eapol_sm_step(struct eapol_sm *sm)
+{
+ int i;
+
+ /* In theory, it should be ok to run this in loop until !changed.
+ * However, it is better to use a limit on number of iterations to
+ * allow events (e.g., SIGTERM) to stop the program cleanly if the
+ * state machine were to generate a busy loop. */
+ for (i = 0; i < 100; i++) {
+ sm->changed = FALSE;
+ SM_STEP_RUN(SUPP_PAE);
+ SM_STEP_RUN(KEY_RX);
+ SM_STEP_RUN(SUPP_BE);
+ if (eap_sm_step(sm->eap))
+ sm->changed = TRUE;
+ }
+
+ if (sm->changed) {
+ /* restart EAPOL state machine step from timeout call in order
+ * to allow other events to be processed. */
+ eloop_cancel_timeout(eapol_sm_step_timeout, NULL, sm);
+ eloop_register_timeout(0, 0, eapol_sm_step_timeout, NULL, sm);
+ }
+
+ if (sm->ctx->cb && sm->cb_status != EAPOL_CB_IN_PROGRESS) {
+ int success = sm->cb_status == EAPOL_CB_SUCCESS ? 1 : 0;
+ sm->cb_status = EAPOL_CB_IN_PROGRESS;
+ sm->ctx->cb(sm, success, sm->ctx->cb_ctx);
+ }
+}
+
+
+static const char *eapol_supp_pae_state(int state)
+{
+ switch (state) {
+ case SUPP_PAE_LOGOFF:
+ return "LOGOFF";
+ case SUPP_PAE_DISCONNECTED:
+ return "DISCONNECTED";
+ case SUPP_PAE_CONNECTING:
+ return "CONNECTING";
+ case SUPP_PAE_AUTHENTICATING:
+ return "AUTHENTICATING";
+ case SUPP_PAE_HELD:
+ return "HELD";
+ case SUPP_PAE_AUTHENTICATED:
+ return "AUTHENTICATED";
+ case SUPP_PAE_RESTART:
+ return "RESTART";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+
+static const char *eapol_supp_be_state(int state)
+{
+ switch (state) {
+ case SUPP_BE_REQUEST:
+ return "REQUEST";
+ case SUPP_BE_RESPONSE:
+ return "RESPONSE";
+ case SUPP_BE_SUCCESS:
+ return "SUCCESS";
+ case SUPP_BE_FAIL:
+ return "FAIL";
+ case SUPP_BE_TIMEOUT:
+ return "TIMEOUT";
+ case SUPP_BE_IDLE:
+ return "IDLE";
+ case SUPP_BE_INITIALIZE:
+ return "INITIALIZE";
+ case SUPP_BE_RECEIVE:
+ return "RECEIVE";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+
+static const char * eapol_port_status(PortStatus status)
+{
+ if (status == Authorized)
+ return "Authorized";
+ else
+ return "Unauthorized";
+}
+
+
+static const char * eapol_port_control(PortControl ctrl)
+{
+ switch (ctrl) {
+ case Auto:
+ return "Auto";
+ case ForceUnauthorized:
+ return "ForceUnauthorized";
+ case ForceAuthorized:
+ return "ForceAuthorized";
+ default:
+ return "Unknown";
+ }
+}
+
+
+void eapol_sm_configure(struct eapol_sm *sm, int heldPeriod, int authPeriod,
+ int startPeriod, int maxStart)
+{
+ if (sm == NULL)
+ return;
+ if (heldPeriod >= 0)
+ sm->heldPeriod = heldPeriod;
+ if (authPeriod >= 0)
+ sm->authPeriod = authPeriod;
+ if (startPeriod >= 0)
+ sm->startPeriod = startPeriod;
+ if (maxStart >= 0)
+ sm->maxStart = maxStart;
+}
+
+
+int eapol_sm_get_status(struct eapol_sm *sm, char *buf, size_t buflen,
+ int verbose)
+{
+ int len;
+ if (sm == NULL)
+ return 0;
+
+ len = snprintf(buf, buflen,
+ "Supplicant PAE state=%s\n"
+ "suppPortStatus=%s\n",
+ eapol_supp_pae_state(sm->SUPP_PAE_state),
+ eapol_port_status(sm->suppPortStatus));
+
+ if (verbose) {
+ len += snprintf(buf + len, buflen - len,
+ "heldPeriod=%d\n"
+ "authPeriod=%d\n"
+ "startPeriod=%d\n"
+ "maxStart=%d\n"
+ "portControl=%s\n"
+ "Supplicant Backend state=%s\n",
+ sm->heldPeriod,
+ sm->authPeriod,
+ sm->startPeriod,
+ sm->maxStart,
+ eapol_port_control(sm->portControl),
+ eapol_supp_be_state(sm->SUPP_BE_state));
+ }
+
+ len += eap_sm_get_status(sm->eap, buf + len, buflen - len, verbose);
+
+ return len;
+}
+
+
+int eapol_sm_get_mib(struct eapol_sm *sm, char *buf, size_t buflen)
+{
+ int len;
+ if (sm == NULL)
+ return 0;
+ len = snprintf(buf, buflen,
+ "dot1xSuppPaeState=%d\n"
+ "dot1xSuppHeldPeriod=%d\n"
+ "dot1xSuppAuthPeriod=%d\n"
+ "dot1xSuppStartPeriod=%d\n"
+ "dot1xSuppMaxStart=%d\n"
+ "dot1xSuppSuppControlledPortStatus=%s\n"
+ "dot1xSuppBackendPaeState=%d\n"
+ "dot1xSuppEapolFramesRx=%d\n"
+ "dot1xSuppEapolFramesTx=%d\n"
+ "dot1xSuppEapolStartFramesTx=%d\n"
+ "dot1xSuppEapolLogoffFramesTx=%d\n"
+ "dot1xSuppEapolRespFramesTx=%d\n"
+ "dot1xSuppEapolReqIdFramesRx=%d\n"
+ "dot1xSuppEapolReqFramesRx=%d\n"
+ "dot1xSuppInvalidEapolFramesRx=%d\n"
+ "dot1xSuppEapLengthErrorFramesRx=%d\n"
+ "dot1xSuppLastEapolFrameVersion=%d\n"
+ "dot1xSuppLastEapolFrameSource=" MACSTR "\n",
+ sm->SUPP_PAE_state,
+ sm->heldPeriod,
+ sm->authPeriod,
+ sm->startPeriod,
+ sm->maxStart,
+ sm->suppPortStatus == Authorized ?
+ "Authorized" : "Unauthorized",
+ sm->SUPP_BE_state,
+ sm->dot1xSuppEapolFramesRx,
+ sm->dot1xSuppEapolFramesTx,
+ sm->dot1xSuppEapolStartFramesTx,
+ sm->dot1xSuppEapolLogoffFramesTx,
+ sm->dot1xSuppEapolRespFramesTx,
+ sm->dot1xSuppEapolReqIdFramesRx,
+ sm->dot1xSuppEapolReqFramesRx,
+ sm->dot1xSuppInvalidEapolFramesRx,
+ sm->dot1xSuppEapLengthErrorFramesRx,
+ sm->dot1xSuppLastEapolFrameVersion,
+ MAC2STR(sm->dot1xSuppLastEapolFrameSource));
+ return len;
+}
+
+
+void eapol_sm_rx_eapol(struct eapol_sm *sm, u8 *src, u8 *buf, size_t len)
+{
+ struct ieee802_1x_hdr *hdr;
+ struct ieee802_1x_eapol_key *key;
+ int plen, data_len;
+
+ if (sm == NULL)
+ return;
+ sm->dot1xSuppEapolFramesRx++;
+ if (len < sizeof(*hdr)) {
+ sm->dot1xSuppInvalidEapolFramesRx++;
+ return;
+ }
+ hdr = (struct ieee802_1x_hdr *) buf;
+ sm->dot1xSuppLastEapolFrameVersion = hdr->version;
+ memcpy(sm->dot1xSuppLastEapolFrameSource, src, ETH_ALEN);
+ if (hdr->version < EAPOL_VERSION) {
+ /* TODO: backwards compatibility */
+ }
+ plen = be_to_host16(hdr->length);
+ if (plen > len - sizeof(*hdr)) {
+ sm->dot1xSuppEapLengthErrorFramesRx++;
+ return;
+ }
+ data_len = plen + sizeof(*hdr);
+
+ switch (hdr->type) {
+ case IEEE802_1X_TYPE_EAP_PACKET:
+ if (sm->cached_pmk) {
+ /* Trying to use PMKSA caching, but Authenticator did
+ * not seem to have a matching entry. Need to restart
+ * EAPOL state machines.
+ */
+ eapol_sm_abort_cached(sm);
+ }
+ free(sm->eapReqData);
+ sm->eapReqDataLen = plen;
+ sm->eapReqData = malloc(sm->eapReqDataLen);
+ if (sm->eapReqData) {
+ wpa_printf(MSG_DEBUG, "EAPOL: Received EAP-Packet "
+ "frame");
+ memcpy(sm->eapReqData, (u8 *) (hdr + 1),
+ sm->eapReqDataLen);
+ sm->eapolEap = TRUE;
+ eapol_sm_step(sm);
+ }
+ break;
+ case IEEE802_1X_TYPE_EAPOL_KEY:
+ if (plen < sizeof(*key)) {
+ wpa_printf(MSG_DEBUG, "EAPOL: Too short EAPOL-Key "
+ "frame received");
+ break;
+ }
+ key = (struct ieee802_1x_eapol_key *) (hdr + 1);
+ if (key->type == EAPOL_KEY_TYPE_WPA ||
+ key->type == EAPOL_KEY_TYPE_RSN) {
+ /* WPA Supplicant takes care of this frame. */
+ wpa_printf(MSG_DEBUG, "EAPOL: Ignoring WPA EAPOL-Key "
+ "frame in EAPOL state machines");
+ break;
+ }
+ if (key->type != EAPOL_KEY_TYPE_RC4) {
+ wpa_printf(MSG_DEBUG, "EAPOL: Ignored unknown "
+ "EAPOL-Key type %d", key->type);
+ break;
+ }
+ free(sm->last_rx_key);
+ sm->last_rx_key = malloc(data_len);
+ if (sm->last_rx_key) {
+ wpa_printf(MSG_DEBUG, "EAPOL: Received EAPOL-Key "
+ "frame");
+ memcpy(sm->last_rx_key, buf, data_len);
+ sm->last_rx_key_len = data_len;
+ sm->rxKey = TRUE;
+ eapol_sm_step(sm);
+ }
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "EAPOL: Received unknown EAPOL type %d",
+ hdr->type);
+ sm->dot1xSuppInvalidEapolFramesRx++;
+ break;
+ }
+}
+
+
+void eapol_sm_notify_tx_eapol_key(struct eapol_sm *sm)
+{
+ if (sm)
+ sm->dot1xSuppEapolFramesTx++;
+}
+
+
+void eapol_sm_notify_portEnabled(struct eapol_sm *sm, Boolean enabled)
+{
+ if (sm == NULL)
+ return;
+ wpa_printf(MSG_DEBUG, "EAPOL: External notification - "
+ "portEnabled=%d", enabled);
+ sm->portEnabled = enabled;
+ eapol_sm_step(sm);
+}
+
+
+void eapol_sm_notify_portValid(struct eapol_sm *sm, Boolean valid)
+{
+ if (sm == NULL)
+ return;
+ wpa_printf(MSG_DEBUG, "EAPOL: External notification - "
+ "portValid=%d", valid);
+ sm->portValid = valid;
+ eapol_sm_step(sm);
+}
+
+
+void eapol_sm_notify_eap_success(struct eapol_sm *sm, Boolean success)
+{
+ if (sm == NULL)
+ return;
+ wpa_printf(MSG_DEBUG, "EAPOL: External notification - "
+ "EAP success=%d", success);
+ sm->eapSuccess = success;
+ sm->altAccept = success;
+ if (success)
+ eap_notify_success(sm->eap);
+ eapol_sm_step(sm);
+}
+
+
+void eapol_sm_notify_eap_fail(struct eapol_sm *sm, Boolean fail)
+{
+ if (sm == NULL)
+ return;
+ wpa_printf(MSG_DEBUG, "EAPOL: External notification - "
+ "EAP fail=%d", fail);
+ sm->eapFail = fail;
+ sm->altReject = fail;
+ eapol_sm_step(sm);
+}
+
+
+void eapol_sm_notify_config(struct eapol_sm *sm, struct wpa_ssid *config,
+ struct eapol_config *conf)
+{
+ if (sm == NULL)
+ return;
+
+ sm->config = config;
+
+ if (conf == NULL)
+ return;
+
+ sm->conf.accept_802_1x_keys = conf->accept_802_1x_keys;
+ sm->conf.required_keys = conf->required_keys;
+ sm->conf.fast_reauth = conf->fast_reauth;
+ if (sm->eap) {
+ eap_set_fast_reauth(sm->eap, conf->fast_reauth);
+ eap_set_workaround(sm->eap, conf->workaround);
+ }
+}
+
+
+int eapol_sm_get_key(struct eapol_sm *sm, u8 *key, size_t len)
+{
+ u8 *eap_key;
+ size_t eap_len;
+
+ if (sm == NULL || !eap_key_available(sm->eap))
+ return -1;
+ eap_key = eap_get_eapKeyData(sm->eap, &eap_len);
+ if (eap_key == NULL)
+ return -1;
+ if (len > eap_len)
+ return eap_len;
+ memcpy(key, eap_key, len);
+ return 0;
+}
+
+
+void eapol_sm_notify_logoff(struct eapol_sm *sm, Boolean logoff)
+{
+ if (sm) {
+ sm->userLogoff = logoff;
+ eapol_sm_step(sm);
+ }
+}
+
+
+void eapol_sm_notify_cached(struct eapol_sm *sm)
+{
+ if (sm == NULL)
+ return;
+ sm->SUPP_PAE_state = SUPP_PAE_AUTHENTICATED;
+ sm->suppPortStatus = Authorized;
+ eap_notify_success(sm->eap);
+}
+
+
+void eapol_sm_notify_pmkid_attempt(struct eapol_sm *sm, int attempt)
+{
+ if (sm == NULL)
+ return;
+ if (attempt) {
+ wpa_printf(MSG_DEBUG, "RSN: Trying to use cached PMKSA");
+ sm->cached_pmk = TRUE;
+ } else {
+ wpa_printf(MSG_DEBUG, "RSN: Do not try to use cached PMKSA");
+ sm->cached_pmk = FALSE;
+ }
+}
+
+
+static void eapol_sm_abort_cached(struct eapol_sm *sm)
+{
+ wpa_printf(MSG_DEBUG, "RSN: Authenticator did not accept PMKID, "
+ "doing full EAP authentication");
+ if (sm == NULL)
+ return;
+ sm->cached_pmk = FALSE;
+ sm->SUPP_PAE_state = SUPP_PAE_CONNECTING;
+ sm->suppPortStatus = Unauthorized;
+ sm->eapRestart= TRUE;
+}
+
+
+void eapol_sm_register_scard_ctx(struct eapol_sm *sm, void *ctx)
+{
+ if (sm) {
+ sm->ctx->scard_ctx = ctx;
+ eap_register_scard_ctx(sm->eap, ctx);
+ }
+}
+
+
+void eapol_sm_notify_portControl(struct eapol_sm *sm, PortControl portControl)
+{
+ if (sm == NULL)
+ return;
+ wpa_printf(MSG_DEBUG, "EAPOL: External notification - "
+ "portControl=%s", eapol_port_control(portControl));
+ sm->portControl = portControl;
+ eapol_sm_step(sm);
+}
+
+
+void eapol_sm_notify_ctrl_attached(struct eapol_sm *sm)
+{
+ if (sm == NULL)
+ return;
+ eap_sm_notify_ctrl_attached(sm->eap);
+}
+
+
+void eapol_sm_notify_ctrl_response(struct eapol_sm *sm)
+{
+ if (sm == NULL)
+ return;
+ if (sm->eapReqData && !sm->eapReq) {
+ wpa_printf(MSG_DEBUG, "EAPOL: received control response (user "
+ "input) notification - retrying pending EAP "
+ "Request");
+ sm->eapolEap = TRUE;
+ sm->eapReq = TRUE;
+ eapol_sm_step(sm);
+ }
+}
+
+
+static struct wpa_ssid * eapol_sm_get_config(void *ctx)
+{
+ struct eapol_sm *sm = ctx;
+ return sm ? sm->config : NULL;
+}
+
+
+static u8 * eapol_sm_get_eapReqData(void *ctx, size_t *len)
+{
+ struct eapol_sm *sm = ctx;
+ if (sm == NULL || sm->eapReqData == NULL) {
+ *len = 0;
+ return NULL;
+ }
+
+ *len = sm->eapReqDataLen;
+ return sm->eapReqData;
+}
+
+
+static Boolean eapol_sm_get_bool(void *ctx, enum eapol_bool_var variable)
+{
+ struct eapol_sm *sm = ctx;
+ if (sm == NULL)
+ return FALSE;
+ switch (variable) {
+ case EAPOL_eapSuccess:
+ return sm->eapSuccess;
+ case EAPOL_eapRestart:
+ return sm->eapRestart;
+ case EAPOL_eapFail:
+ return sm->eapFail;
+ case EAPOL_eapResp:
+ return sm->eapResp;
+ case EAPOL_eapNoResp:
+ return sm->eapNoResp;
+ case EAPOL_eapReq:
+ return sm->eapReq;
+ case EAPOL_portEnabled:
+ return sm->portEnabled;
+ case EAPOL_altAccept:
+ return sm->altAccept;
+ case EAPOL_altReject:
+ return sm->altReject;
+ }
+ return FALSE;
+}
+
+
+static void eapol_sm_set_bool(void *ctx, enum eapol_bool_var variable,
+ Boolean value)
+{
+ struct eapol_sm *sm = ctx;
+ if (sm == NULL)
+ return;
+ switch (variable) {
+ case EAPOL_eapSuccess:
+ sm->eapSuccess = value;
+ break;
+ case EAPOL_eapRestart:
+ sm->eapRestart = value;
+ break;
+ case EAPOL_eapFail:
+ sm->eapFail = value;
+ break;
+ case EAPOL_eapResp:
+ sm->eapResp = value;
+ break;
+ case EAPOL_eapNoResp:
+ sm->eapNoResp = value;
+ break;
+ case EAPOL_eapReq:
+ sm->eapReq = value;
+ break;
+ case EAPOL_portEnabled:
+ sm->portEnabled = value;
+ break;
+ case EAPOL_altAccept:
+ sm->altAccept = value;
+ break;
+ case EAPOL_altReject:
+ sm->altReject = value;
+ break;
+ }
+}
+
+
+static unsigned int eapol_sm_get_int(void *ctx, enum eapol_int_var variable)
+{
+ struct eapol_sm *sm = ctx;
+ if (sm == NULL)
+ return 0;
+ switch (variable) {
+ case EAPOL_idleWhile:
+ return sm->idleWhile;
+ }
+ return 0;
+}
+
+
+static void eapol_sm_set_int(void *ctx, enum eapol_int_var variable,
+ unsigned int value)
+{
+ struct eapol_sm *sm = ctx;
+ if (sm == NULL)
+ return;
+ switch (variable) {
+ case EAPOL_idleWhile:
+ sm->idleWhile = value;
+ break;
+ }
+}
+
+
+static struct eapol_callbacks eapol_cb =
+{
+ .get_config = eapol_sm_get_config,
+ .get_bool = eapol_sm_get_bool,
+ .set_bool = eapol_sm_set_bool,
+ .get_int = eapol_sm_get_int,
+ .set_int = eapol_sm_set_int,
+ .get_eapReqData = eapol_sm_get_eapReqData,
+};
diff --git a/contrib/wpa_supplicant/eapol_sm.h b/contrib/wpa_supplicant/eapol_sm.h
new file mode 100644
index 000000000000..b9412030c1f3
--- /dev/null
+++ b/contrib/wpa_supplicant/eapol_sm.h
@@ -0,0 +1,138 @@
+#ifndef EAPOL_SM_H
+#define EAPOL_SM_H
+
+#include "defs.h"
+
+typedef enum { Unauthorized, Authorized } PortStatus;
+typedef enum { Auto, ForceUnauthorized, ForceAuthorized } PortControl;
+
+struct eapol_config {
+ int accept_802_1x_keys;
+#define EAPOL_REQUIRE_KEY_UNICAST BIT(0)
+#define EAPOL_REQUIRE_KEY_BROADCAST BIT(1)
+ int required_keys; /* which EAPOL-Key packets are required before
+ * marking connection authenticated */
+ int fast_reauth; /* whether fast EAP reauthentication is enabled */
+ int workaround; /* whether EAP workarounds are enabled */
+};
+
+struct eapol_sm;
+
+struct eapol_ctx {
+ void *ctx; /* pointer to arbitrary upper level context */
+ int preauth; /* This EAPOL state machine is used for IEEE 802.11i/RSN
+ * pre-authentication */
+ void (*cb)(struct eapol_sm *eapol, int success, void *ctx);
+ void *cb_ctx, *msg_ctx, *scard_ctx;
+ void (*eapol_done_cb)(void *ctx);
+ int (*eapol_send)(void *ctx, int type, u8 *buf, size_t len);
+ int (*set_wep_key)(void *ctx, int unicast, int keyidx,
+ u8 *key, size_t keylen);
+};
+
+
+struct wpa_ssid;
+
+#ifdef IEEE8021X_EAPOL
+struct eapol_sm *eapol_sm_init(struct eapol_ctx *ctx);
+void eapol_sm_deinit(struct eapol_sm *sm);
+void eapol_sm_step(struct eapol_sm *sm);
+int eapol_sm_get_status(struct eapol_sm *sm, char *buf, size_t buflen,
+ int verbose);
+int eapol_sm_get_mib(struct eapol_sm *sm, char *buf, size_t buflen);
+void eapol_sm_configure(struct eapol_sm *sm, int heldPeriod, int authPeriod,
+ int startPeriod, int maxStart);
+void eapol_sm_rx_eapol(struct eapol_sm *sm, u8 *src, u8 *buf, size_t len);
+void eapol_sm_notify_tx_eapol_key(struct eapol_sm *sm);
+void eapol_sm_notify_portEnabled(struct eapol_sm *sm, Boolean enabled);
+void eapol_sm_notify_portValid(struct eapol_sm *sm, Boolean valid);
+void eapol_sm_notify_eap_success(struct eapol_sm *sm, Boolean success);
+void eapol_sm_notify_eap_fail(struct eapol_sm *sm, Boolean fail);
+void eapol_sm_notify_config(struct eapol_sm *sm, struct wpa_ssid *config,
+ struct eapol_config *conf);
+int eapol_sm_get_key(struct eapol_sm *sm, u8 *key, size_t len);
+void eapol_sm_notify_logoff(struct eapol_sm *sm, Boolean logoff);
+void eapol_sm_notify_cached(struct eapol_sm *sm);
+void eapol_sm_notify_pmkid_attempt(struct eapol_sm *sm, int attempt);
+void eapol_sm_register_scard_ctx(struct eapol_sm *sm, void *ctx);
+void eapol_sm_notify_portControl(struct eapol_sm *sm, PortControl portControl);
+void eapol_sm_notify_ctrl_attached(struct eapol_sm *sm);
+void eapol_sm_notify_ctrl_response(struct eapol_sm *sm);
+#else /* IEEE8021X_EAPOL */
+static inline struct eapol_sm *eapol_sm_init(struct eapol_ctx *ctx)
+{
+ return (struct eapol_sm *) 1;
+}
+static inline void eapol_sm_deinit(struct eapol_sm *sm)
+{
+}
+static inline void eapol_sm_step(struct eapol_sm *sm)
+{
+}
+static inline int eapol_sm_get_status(struct eapol_sm *sm, char *buf,
+ size_t buflen, int verbose)
+{
+ return 0;
+}
+static inline int eapol_sm_get_mib(struct eapol_sm *sm, char *buf,
+ size_t buflen)
+{
+ return 0;
+}
+static inline void eapol_sm_configure(struct eapol_sm *sm, int heldPeriod,
+ int authPeriod, int startPeriod,
+ int maxStart)
+{
+}
+static inline void eapol_sm_rx_eapol(struct eapol_sm *sm, u8 *src, u8 *buf,
+ size_t len)
+{
+}
+static inline void eapol_sm_notify_tx_eapol_key(struct eapol_sm *sm)
+{
+}
+static inline void eapol_sm_notify_portEnabled(struct eapol_sm *sm,
+ Boolean enabled)
+{
+}
+static inline void eapol_sm_notify_portValid(struct eapol_sm *sm,
+ Boolean valid)
+{
+}
+static inline void eapol_sm_notify_eap_success(struct eapol_sm *sm,
+ Boolean success)
+{
+}
+static inline void eapol_sm_notify_eap_fail(struct eapol_sm *sm, Boolean fail)
+{
+}
+static inline void eapol_sm_notify_config(struct eapol_sm *sm,
+ struct wpa_ssid *config,
+ struct eapol_config *conf)
+{
+}
+static inline int eapol_sm_get_key(struct eapol_sm *sm, u8 *key, size_t len)
+{
+ return -1;
+}
+static inline void eapol_sm_notify_logoff(struct eapol_sm *sm, Boolean logoff)
+{
+}
+static inline void eapol_sm_notify_cached(struct eapol_sm *sm)
+{
+}
+#define eapol_sm_notify_pmkid_attempt(sm, attempt) do { } while (0)
+#define eapol_sm_register_scard_ctx(sm, ctx) do { } while (0)
+static inline void eapol_sm_notify_portControl(struct eapol_sm *sm,
+ PortControl portControl)
+{
+}
+static inline void eapol_sm_notify_ctrl_attached(struct eapol_sm *sm)
+{
+}
+static inline void eapol_sm_notify_ctrl_response(struct eapol_sm *sm)
+{
+}
+#endif /* IEEE8021X_EAPOL */
+
+#endif /* EAPOL_SM_H */
diff --git a/contrib/wpa_supplicant/eapol_test.c b/contrib/wpa_supplicant/eapol_test.c
new file mode 100644
index 000000000000..061ae893d668
--- /dev/null
+++ b/contrib/wpa_supplicant/eapol_test.c
@@ -0,0 +1,1012 @@
+/*
+ * WPA Supplicant - test code
+ * Copyright (c) 2003-2005, Jouni Malinen <jkmaline@cc.hut.fi>
+ *
+ * IEEE 802.1X Supplicant test code (to be used in place of wpa_supplicant.c.
+ * Not used in production version.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <string.h>
+#include <signal.h>
+#include <netinet/in.h>
+#include <assert.h>
+#include <arpa/inet.h>
+
+#include "common.h"
+#include "config.h"
+#include "eapol_sm.h"
+#include "eloop.h"
+#include "wpa.h"
+#include "eap_i.h"
+#include "wpa_supplicant.h"
+#include "wpa_supplicant_i.h"
+#include "radius.h"
+#include "radius_client.h"
+#include "l2_packet.h"
+#include "ctrl_iface.h"
+#include "pcsc_funcs.h"
+
+
+extern int wpa_debug_level;
+extern int wpa_debug_show_keys;
+static int eapol_test_num_reauths = 0;
+static int no_mppe_keys = 0;
+static int num_mppe_ok = 0, num_mppe_mismatch = 0;
+
+static void send_eap_request_identity(void *eloop_ctx, void *timeout_ctx);
+
+
+void wpa_msg(struct wpa_supplicant *wpa_s, int level, char *fmt, ...)
+{
+ va_list ap;
+ char *buf;
+ const int buflen = 2048;
+ int len;
+
+ buf = malloc(buflen);
+ if (buf == NULL) {
+ printf("Failed to allocate message buffer for:\n");
+ va_start(ap, fmt);
+ vprintf(fmt, ap);
+ printf("\n");
+ va_end(ap);
+ return;
+ }
+ va_start(ap, fmt);
+ len = vsnprintf(buf, buflen, fmt, ap);
+ va_end(ap);
+ wpa_printf(level, "%s", buf);
+ wpa_supplicant_ctrl_iface_send(wpa_s, level, buf, len);
+ free(buf);
+}
+
+
+void wpa_supplicant_event(struct wpa_supplicant *wpa_s, wpa_event_type event,
+ union wpa_event_data *data)
+{
+}
+
+
+int rsn_preauth_init(struct wpa_supplicant *wpa_s, u8 *dst)
+{
+ return -1;
+}
+
+
+void rsn_preauth_deinit(struct wpa_supplicant *wpa_s)
+{
+}
+
+
+int pmksa_cache_list(struct wpa_supplicant *wpa_s, char *buf, size_t len)
+{
+ return 0;
+}
+
+
+int wpa_get_mib(struct wpa_supplicant *wpa_s, char *buf, size_t buflen)
+{
+ return 0;
+}
+
+
+void wpa_supplicant_req_scan(struct wpa_supplicant *wpa_s, int sec, int usec)
+{
+}
+
+
+const char * wpa_ssid_txt(u8 *ssid, size_t ssid_len)
+{
+ return NULL;
+}
+
+
+int wpa_supplicant_reload_configuration(struct wpa_supplicant *wpa_s)
+{
+ return -1;
+}
+
+
+static void ieee802_1x_encapsulate_radius(struct wpa_supplicant *wpa_s,
+ u8 *eap, size_t len)
+{
+ struct radius_msg *msg;
+ char buf[128];
+ struct eap_hdr *hdr;
+ u8 *pos;
+
+ wpa_printf(MSG_DEBUG, "Encapsulating EAP message into a RADIUS "
+ "packet");
+
+ wpa_s->radius_identifier = radius_client_get_id(wpa_s);
+ msg = radius_msg_new(RADIUS_CODE_ACCESS_REQUEST,
+ wpa_s->radius_identifier);
+ if (msg == NULL) {
+ printf("Could not create net RADIUS packet\n");
+ return;
+ }
+
+ radius_msg_make_authenticator(msg, (u8 *) wpa_s, sizeof(*wpa_s));
+
+ hdr = (struct eap_hdr *) eap;
+ pos = (u8 *) (hdr + 1);
+ if (len > sizeof(*hdr) && hdr->code == EAP_CODE_RESPONSE &&
+ pos[0] == EAP_TYPE_IDENTITY) {
+ pos++;
+ free(wpa_s->eap_identity);
+ wpa_s->eap_identity_len = len - sizeof(*hdr) - 1;
+ wpa_s->eap_identity = malloc(wpa_s->eap_identity_len);
+ if (wpa_s->eap_identity) {
+ memcpy(wpa_s->eap_identity, pos,
+ wpa_s->eap_identity_len);
+ wpa_hexdump(MSG_DEBUG, "Learned identity from "
+ "EAP-Response-Identity",
+ wpa_s->eap_identity,
+ wpa_s->eap_identity_len);
+ }
+ }
+
+ if (wpa_s->eap_identity &&
+ !radius_msg_add_attr(msg, RADIUS_ATTR_USER_NAME,
+ wpa_s->eap_identity,
+ wpa_s->eap_identity_len)) {
+ printf("Could not add User-Name\n");
+ goto fail;
+ }
+
+ if (!radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IP_ADDRESS,
+ (u8 *) &wpa_s->own_ip_addr, 4)) {
+ printf("Could not add NAS-IP-Address\n");
+ goto fail;
+ }
+
+ snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT,
+ MAC2STR(wpa_s->own_addr));
+ if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLING_STATION_ID,
+ (u8 *) buf, strlen(buf))) {
+ printf("Could not add Calling-Station-Id\n");
+ 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 (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_FRAMED_MTU, 1400)) {
+ printf("Could not add Framed-MTU\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;
+ }
+
+ snprintf(buf, sizeof(buf), "CONNECT 11Mbps 802.11b");
+ if (!radius_msg_add_attr(msg, RADIUS_ATTR_CONNECT_INFO,
+ (u8 *) buf, strlen(buf))) {
+ printf("Could not add Connect-Info\n");
+ goto fail;
+ }
+
+ if (eap && !radius_msg_add_eap(msg, eap, len)) {
+ printf("Could not add EAP-Message\n");
+ goto fail;
+ }
+
+ /* State attribute must be copied if and only if this packet is
+ * Access-Request reply to the previous Access-Challenge */
+ if (wpa_s->last_recv_radius && wpa_s->last_recv_radius->hdr->code ==
+ RADIUS_CODE_ACCESS_CHALLENGE) {
+ int res = radius_msg_copy_attr(msg, wpa_s->last_recv_radius,
+ RADIUS_ATTR_STATE);
+ if (res < 0) {
+ printf("Could not copy State attribute from previous "
+ "Access-Challenge\n");
+ goto fail;
+ }
+ if (res > 0) {
+ wpa_printf(MSG_DEBUG, " Copied RADIUS State "
+ "Attribute");
+ }
+ }
+
+ radius_client_send(wpa_s, msg, RADIUS_AUTH, wpa_s->own_addr);
+ return;
+
+ fail:
+ radius_msg_free(msg);
+ free(msg);
+}
+
+
+static int eapol_test_eapol_send(void *ctx, int type, u8 *buf, size_t len)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+ printf("WPA: wpa_eapol_send(type=%d len=%d)\n", type, len);
+ if (type == IEEE802_1X_TYPE_EAP_PACKET) {
+ wpa_hexdump(MSG_DEBUG, "TX EAP -> RADIUS", buf, len);
+ ieee802_1x_encapsulate_radius(wpa_s, buf, len);
+ }
+ return 0;
+}
+
+
+static void eapol_test_eapol_done_cb(void *ctx)
+{
+ printf("WPA: EAPOL processing complete\n");
+}
+
+
+static void eapol_sm_reauth(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_supplicant *wpa_s = eloop_ctx;
+ printf("\n\n\n\n\neapol_test: Triggering EAP reauthentication\n\n");
+ wpa_s->radius_access_accept_received = 0;
+ send_eap_request_identity(eloop_ctx, timeout_ctx);
+}
+
+
+static int eapol_test_compare_pmk(struct wpa_supplicant *wpa_s)
+{
+ u8 pmk[PMK_LEN];
+ int ret = 1;
+
+ if (eapol_sm_get_key(wpa_s->eapol, pmk, PMK_LEN) == 0) {
+ wpa_hexdump(MSG_DEBUG, "PMK from EAPOL", pmk, PMK_LEN);
+ if (memcmp(pmk, wpa_s->authenticator_pmk, PMK_LEN) != 0)
+ printf("WARNING: PMK mismatch\n");
+ else if (wpa_s->radius_access_accept_received)
+ ret = 0;
+ } else if (wpa_s->authenticator_pmk_len == 16 &&
+ eapol_sm_get_key(wpa_s->eapol, pmk, 16) == 0) {
+ wpa_hexdump(MSG_DEBUG, "LEAP PMK from EAPOL", pmk, 16);
+ if (memcmp(pmk, wpa_s->authenticator_pmk, 16) != 0)
+ printf("WARNING: PMK mismatch\n");
+ else if (wpa_s->radius_access_accept_received)
+ ret = 0;
+ } else if (wpa_s->radius_access_accept_received && no_mppe_keys) {
+ /* No keying material expected */
+ ret = 0;
+ }
+
+ if (ret)
+ num_mppe_mismatch++;
+ else if (!no_mppe_keys)
+ num_mppe_ok++;
+
+ return ret;
+}
+
+
+static void eapol_sm_cb(struct eapol_sm *eapol, int success, void *ctx)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+ printf("eapol_sm_cb: success=%d\n", success);
+ eapol_test_num_reauths--;
+ if (eapol_test_num_reauths < 0)
+ eloop_terminate();
+ else {
+ eapol_test_compare_pmk(wpa_s);
+ eloop_register_timeout(0, 100000, eapol_sm_reauth,
+ wpa_s, NULL);
+ }
+}
+
+
+static int test_eapol(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid)
+{
+ struct eapol_config eapol_conf;
+ struct eapol_ctx *ctx;
+
+ ctx = malloc(sizeof(*ctx));
+ if (ctx == NULL) {
+ printf("Failed to allocate EAPOL context.\n");
+ return -1;
+ }
+ memset(ctx, 0, sizeof(*ctx));
+ ctx->ctx = wpa_s;
+ ctx->msg_ctx = wpa_s;
+ ctx->scard_ctx = wpa_s->scard;
+ ctx->cb = eapol_sm_cb;
+ ctx->cb_ctx = wpa_s;
+ ctx->preauth = 0;
+ ctx->eapol_done_cb = eapol_test_eapol_done_cb;
+ ctx->eapol_send = eapol_test_eapol_send;
+
+ wpa_s->eapol = eapol_sm_init(ctx);
+ if (wpa_s->eapol == NULL) {
+ free(ctx);
+ printf("Failed to initialize EAPOL state machines.\n");
+ return -1;
+ }
+
+ wpa_s->current_ssid = ssid;
+ memset(&eapol_conf, 0, sizeof(eapol_conf));
+ eapol_conf.accept_802_1x_keys = 1;
+ eapol_conf.required_keys = 0;
+ eapol_conf.fast_reauth = wpa_s->conf->fast_reauth;
+ eapol_conf.workaround = ssid->eap_workaround;
+ eapol_sm_notify_config(wpa_s->eapol, ssid, &eapol_conf);
+ eapol_sm_register_scard_ctx(wpa_s->eapol, wpa_s->scard);
+
+
+ eapol_sm_notify_portValid(wpa_s->eapol, FALSE);
+ /* 802.1X::portControl = Auto */
+ eapol_sm_notify_portEnabled(wpa_s->eapol, TRUE);
+
+ return 0;
+}
+
+
+static void test_eapol_clean(struct wpa_supplicant *wpa_s)
+{
+ radius_client_deinit(wpa_s);
+ free(wpa_s->last_eap_radius);
+ if (wpa_s->last_recv_radius) {
+ radius_msg_free(wpa_s->last_recv_radius);
+ free(wpa_s->last_recv_radius);
+ }
+ free(wpa_s->eap_identity);
+ wpa_s->eap_identity = NULL;
+ eapol_sm_deinit(wpa_s->eapol);
+ wpa_s->eapol = NULL;
+ if (wpa_s->auth_server) {
+ free(wpa_s->auth_server->shared_secret);
+ free(wpa_s->auth_server);
+ }
+ scard_deinit(wpa_s->scard);
+ wpa_supplicant_ctrl_iface_deinit(wpa_s);
+ wpa_config_free(wpa_s->conf);
+}
+
+
+static void send_eap_request_identity(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_supplicant *wpa_s = eloop_ctx;
+ u8 buf[100], *pos;
+ struct ieee802_1x_hdr *hdr;
+ struct eap_hdr *eap;
+
+ hdr = (struct ieee802_1x_hdr *) buf;
+ hdr->version = EAPOL_VERSION;
+ hdr->type = IEEE802_1X_TYPE_EAP_PACKET;
+ hdr->length = htons(5);
+
+ eap = (struct eap_hdr *) (hdr + 1);
+ eap->code = EAP_CODE_REQUEST;
+ eap->identifier = 0;
+ eap->length = htons(5);
+ pos = (u8 *) (eap + 1);
+ *pos = EAP_TYPE_IDENTITY;
+
+ printf("Sending fake EAP-Request-Identity\n");
+ eapol_sm_rx_eapol(wpa_s->eapol, wpa_s->bssid, buf,
+ sizeof(*hdr) + 5);
+}
+
+
+static void eapol_test_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_supplicant *wpa_s = eloop_ctx;
+ printf("EAPOL test timed out\n");
+ wpa_s->auth_timed_out = 1;
+ eloop_terminate();
+}
+
+
+static char *eap_type_text(u8 type)
+{
+ switch (type) {
+ case EAP_TYPE_IDENTITY: return "Identity";
+ case EAP_TYPE_NOTIFICATION: return "Notification";
+ case EAP_TYPE_NAK: return "Nak";
+ case EAP_TYPE_TLS: return "TLS";
+ case EAP_TYPE_TTLS: return "TTLS";
+ case EAP_TYPE_PEAP: return "PEAP";
+ case EAP_TYPE_SIM: return "SIM";
+ case EAP_TYPE_GTC: return "GTC";
+ case EAP_TYPE_MD5: return "MD5";
+ case EAP_TYPE_OTP: return "OTP";
+ default: return "Unknown";
+ }
+}
+
+
+static void ieee802_1x_decapsulate_radius(struct wpa_supplicant *wpa_s)
+{
+ u8 *eap;
+ size_t len;
+ struct eap_hdr *hdr;
+ int eap_type = -1;
+ char buf[64];
+ struct radius_msg *msg;
+
+ if (wpa_s->last_recv_radius == NULL)
+ return;
+
+ msg = wpa_s->last_recv_radius;
+
+ eap = radius_msg_get_eap(msg, &len);
+ 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");
+ free(wpa_s->last_eap_radius);
+ wpa_s->last_eap_radius = NULL;
+ wpa_s->last_eap_radius_len = 0;
+ return;
+ }
+
+ if (len < sizeof(*hdr)) {
+ wpa_printf(MSG_DEBUG, "too short EAP packet "
+ "received from authentication server");
+ free(eap);
+ return;
+ }
+
+ if (len > sizeof(*hdr))
+ eap_type = eap[sizeof(*hdr)];
+
+ hdr = (struct eap_hdr *) eap;
+ switch (hdr->code) {
+ case EAP_CODE_REQUEST:
+ snprintf(buf, sizeof(buf), "EAP-Request-%s (%d)",
+ eap_type >= 0 ? eap_type_text(eap_type) : "??",
+ eap_type);
+ break;
+ case EAP_CODE_RESPONSE:
+ snprintf(buf, sizeof(buf), "EAP Response-%s (%d)",
+ eap_type >= 0 ? eap_type_text(eap_type) : "??",
+ eap_type);
+ break;
+ case EAP_CODE_SUCCESS:
+ snprintf(buf, sizeof(buf), "EAP Success");
+ /* LEAP uses EAP Success within an authentication, so must not
+ * stop here with eloop_terminate(); */
+ break;
+ case EAP_CODE_FAILURE:
+ snprintf(buf, sizeof(buf), "EAP Failure");
+ eloop_terminate();
+ break;
+ default:
+ snprintf(buf, sizeof(buf), "unknown EAP code");
+ wpa_hexdump(MSG_DEBUG, "Decapsulated EAP packet", eap, len);
+ break;
+ }
+ wpa_printf(MSG_DEBUG, "decapsulated EAP packet (code=%d "
+ "id=%d len=%d) from RADIUS server: %s",
+ hdr->code, hdr->identifier, ntohs(hdr->length), buf);
+
+ /* sta->eapol_sm->be_auth.idFromServer = hdr->identifier; */
+
+ if (wpa_s->last_eap_radius)
+ free(wpa_s->last_eap_radius);
+ wpa_s->last_eap_radius = eap;
+ wpa_s->last_eap_radius_len = len;
+
+ {
+ struct ieee802_1x_hdr *hdr;
+ hdr = malloc(sizeof(*hdr) + len);
+ assert(hdr != NULL);
+ hdr->version = EAPOL_VERSION;
+ hdr->type = IEEE802_1X_TYPE_EAP_PACKET;
+ hdr->length = htons(len);
+ memcpy((u8 *) (hdr + 1), eap, len);
+ eapol_sm_rx_eapol(wpa_s->eapol, wpa_s->bssid,
+ (u8 *) hdr, sizeof(*hdr) + len);
+ free(hdr);
+ }
+}
+
+
+static void ieee802_1x_get_keys(struct wpa_supplicant *wpa_s,
+ struct radius_msg *msg, struct radius_msg *req,
+ u8 *shared_secret, size_t shared_secret_len)
+{
+ struct radius_ms_mppe_keys *keys;
+
+ keys = radius_msg_get_ms_keys(msg, req, shared_secret,
+ shared_secret_len);
+ if (keys && keys->send == NULL && keys->recv == NULL) {
+ free(keys);
+ keys = radius_msg_get_cisco_keys(msg, req, shared_secret,
+ shared_secret_len);
+ }
+
+ if (keys) {
+ if (keys->send) {
+ wpa_hexdump(MSG_DEBUG, "MS-MPPE-Send-Key (sign)",
+ keys->send, keys->send_len);
+ }
+ if (keys->recv) {
+ wpa_hexdump(MSG_DEBUG, "MS-MPPE-Recv-Key (crypt)",
+ keys->recv, keys->recv_len);
+ wpa_s->authenticator_pmk_len =
+ keys->recv_len > PMK_LEN ? PMK_LEN :
+ keys->recv_len;
+ memcpy(wpa_s->authenticator_pmk, keys->recv,
+ wpa_s->authenticator_pmk_len);
+ }
+
+ free(keys->send);
+ free(keys->recv);
+ free(keys);
+ }
+}
+
+
+/* Process the RADIUS frames from Authentication Server */
+static RadiusRxResult
+ieee802_1x_receive_auth(struct wpa_supplicant *wpa_s,
+ struct radius_msg *msg, struct radius_msg *req,
+ u8 *shared_secret, size_t shared_secret_len,
+ void *data)
+{
+ /* RFC 2869, Ch. 5.13: valid Message-Authenticator attribute MUST be
+ * present when packet contains an EAP-Message attribute */
+ if (msg->hdr->code == RADIUS_CODE_ACCESS_REJECT &&
+ radius_msg_get_attr(msg, RADIUS_ATTR_MESSAGE_AUTHENTICATOR, NULL,
+ 0) < 0 &&
+ radius_msg_get_attr(msg, RADIUS_ATTR_EAP_MESSAGE, NULL, 0) < 0) {
+ wpa_printf(MSG_DEBUG, "Allowing RADIUS "
+ "Access-Reject without Message-Authenticator "
+ "since it does not include EAP-Message\n");
+ } else if (radius_msg_verify(msg, shared_secret, shared_secret_len,
+ req)) {
+ printf("Incoming RADIUS packet did not have correct "
+ "Message-Authenticator - dropped\n");
+ return RADIUS_RX_UNKNOWN;
+ }
+
+ if (msg->hdr->code != RADIUS_CODE_ACCESS_ACCEPT &&
+ msg->hdr->code != RADIUS_CODE_ACCESS_REJECT &&
+ msg->hdr->code != RADIUS_CODE_ACCESS_CHALLENGE) {
+ printf("Unknown RADIUS message code\n");
+ return RADIUS_RX_UNKNOWN;
+ }
+
+ wpa_s->radius_identifier = -1;
+ wpa_printf(MSG_DEBUG, "RADIUS packet matching with station");
+
+ if (wpa_s->last_recv_radius) {
+ radius_msg_free(wpa_s->last_recv_radius);
+ free(wpa_s->last_recv_radius);
+ }
+
+ wpa_s->last_recv_radius = msg;
+
+ switch (msg->hdr->code) {
+ case RADIUS_CODE_ACCESS_ACCEPT:
+ wpa_s->radius_access_accept_received = 1;
+ ieee802_1x_get_keys(wpa_s, msg, req, shared_secret,
+ shared_secret_len);
+ break;
+ case RADIUS_CODE_ACCESS_REJECT:
+ wpa_s->radius_access_reject_received = 1;
+ break;
+ }
+
+ ieee802_1x_decapsulate_radius(wpa_s);
+
+ if ((msg->hdr->code == RADIUS_CODE_ACCESS_ACCEPT &&
+ eapol_test_num_reauths < 0) ||
+ msg->hdr->code == RADIUS_CODE_ACCESS_REJECT) {
+ eloop_terminate();
+ }
+
+ return RADIUS_RX_QUEUED;
+}
+
+
+static void wpa_supplicant_imsi_identity(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid)
+{
+ int aka = 0;
+ u8 *pos = ssid->eap_methods;
+
+ while (pos && *pos != EAP_TYPE_NONE) {
+ if (*pos == EAP_TYPE_AKA) {
+ aka = 1;
+ break;
+ }
+ pos++;
+ }
+
+ if (ssid->identity == NULL && wpa_s->imsi) {
+ ssid->identity = malloc(1 + wpa_s->imsi_len);
+ if (ssid->identity) {
+ ssid->identity[0] = aka ? '0' : '1';
+ memcpy(ssid->identity + 1, wpa_s->imsi,
+ wpa_s->imsi_len);
+ ssid->identity_len = 1 + wpa_s->imsi_len;
+ wpa_hexdump_ascii(MSG_DEBUG, "permanent identity from "
+ "IMSI", ssid->identity,
+ ssid->identity_len);
+ }
+ }
+}
+
+
+static void wpa_supplicant_scard_init(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid)
+{
+ char buf[100];
+ size_t len;
+
+ if (ssid->pcsc == NULL)
+ return;
+ if (wpa_s->scard != NULL) {
+ wpa_supplicant_imsi_identity(wpa_s, ssid);
+ return;
+ }
+ wpa_printf(MSG_DEBUG, "Selected network is configured to use SIM - "
+ "initialize PCSC");
+ wpa_s->scard = scard_init(SCARD_TRY_BOTH, ssid->pin);
+ if (wpa_s->scard == NULL) {
+ wpa_printf(MSG_WARNING, "Failed to initialize SIM "
+ "(pcsc-lite)");
+ /* TODO: what to do here? */
+ return;
+ }
+ eapol_sm_register_scard_ctx(wpa_s->eapol, wpa_s->scard);
+
+ len = sizeof(buf);
+ if (scard_get_imsi(wpa_s->scard, buf, &len)) {
+ wpa_printf(MSG_WARNING, "Failed to get IMSI from SIM");
+ /* TODO: what to do here? */
+ return;
+ }
+
+ wpa_hexdump(MSG_DEBUG, "IMSI", (u8 *) buf, len);
+ free(wpa_s->imsi);
+ wpa_s->imsi = malloc(len);
+ if (wpa_s->imsi) {
+ memcpy(wpa_s->imsi, buf, len);
+ wpa_s->imsi_len = len;
+ wpa_supplicant_imsi_identity(wpa_s, ssid);
+ }
+}
+
+
+static void wpa_init_conf(struct wpa_supplicant *wpa_s, const char *authsrv,
+ int port, const char *secret)
+{
+ struct hostapd_radius_server *as;
+ int res;
+
+ wpa_s->bssid[5] = 1;
+ wpa_s->own_addr[5] = 2;
+ wpa_s->own_ip_addr.s_addr = htonl((127 << 24) | 1);
+ strncpy(wpa_s->ifname, "test", sizeof(wpa_s->ifname));
+
+ wpa_s->num_auth_servers = 1;
+ as = malloc(sizeof(struct hostapd_radius_server));
+ assert(as != NULL);
+ inet_aton(authsrv, &as->addr);
+ as->port = port;
+ as->shared_secret = (u8 *) strdup(secret);
+ as->shared_secret_len = strlen(secret);
+ wpa_s->auth_server = wpa_s->auth_servers = as;
+
+
+ res = radius_client_init(wpa_s);
+ assert(res == 0);
+
+ res = radius_client_register(wpa_s, RADIUS_AUTH,
+ ieee802_1x_receive_auth, NULL);
+ assert(res == 0);
+}
+
+
+static int scard_test(void)
+{
+ struct scard_data *scard;
+ size_t len;
+ char imsi[20];
+ unsigned char rand[16];
+#ifdef PCSC_FUNCS
+ unsigned char sres[4];
+ unsigned char kc[8];
+#endif /* PCSC_FUNCS */
+#define num_triplets 5
+ unsigned char rand_[num_triplets][16];
+ unsigned char sres_[num_triplets][4];
+ unsigned char kc_[num_triplets][8];
+ int i, j, res;
+
+#define AKA_RAND_LEN 16
+#define AKA_AUTN_LEN 16
+#define AKA_AUTS_LEN 14
+#define RES_MAX_LEN 16
+#define IK_LEN 16
+#define CK_LEN 16
+ unsigned char aka_rand[AKA_RAND_LEN];
+ unsigned char aka_autn[AKA_AUTN_LEN];
+ unsigned char aka_auts[AKA_AUTS_LEN];
+ unsigned char aka_res[RES_MAX_LEN];
+ size_t aka_res_len;
+ unsigned char aka_ik[IK_LEN];
+ unsigned char aka_ck[CK_LEN];
+
+ scard = scard_init(SCARD_TRY_BOTH, "1234");
+ if (scard == NULL)
+ return -1;
+
+ len = sizeof(imsi);
+ if (scard_get_imsi(scard, imsi, &len))
+ goto failed;
+ wpa_hexdump_ascii(MSG_DEBUG, "SCARD: IMSI", (u8 *) imsi, len);
+ /* NOTE: Permanent Username: 1 | IMSI */
+
+ memset(rand, 0, sizeof(rand));
+ if (scard_gsm_auth(scard, rand, sres, kc))
+ goto failed;
+
+ memset(rand, 0xff, sizeof(rand));
+ if (scard_gsm_auth(scard, rand, sres, kc))
+ goto failed;
+
+ for (i = 0; i < num_triplets; i++) {
+ memset(rand_[i], i, sizeof(rand_[i]));
+ if (scard_gsm_auth(scard, rand_[i], sres_[i], kc_[i]))
+ goto failed;
+ }
+
+ for (i = 0; i < num_triplets; i++) {
+ printf("1");
+ for (j = 0; j < len; j++)
+ printf("%c", imsi[j]);
+ printf(",");
+ for (j = 0; j < 16; j++)
+ printf("%02X", rand_[i][j]);
+ printf(",");
+ for (j = 0; j < 4; j++)
+ printf("%02X", sres_[i][j]);
+ printf(",");
+ for (j = 0; j < 8; j++)
+ printf("%02X", kc_[i][j]);
+ printf("\n");
+ }
+
+ wpa_printf(MSG_DEBUG, "Trying to use UMTS authentication");
+
+ /* seq 39 (0x28) */
+ memset(aka_rand, 0xaa, 16);
+ memcpy(aka_autn, "\x86\x71\x31\xcb\xa2\xfc\x61\xdf"
+ "\xa3\xb3\x97\x9d\x07\x32\xa2\x12", 16);
+
+ res = scard_umts_auth(scard, aka_rand, aka_autn, aka_res, &aka_res_len,
+ aka_ik, aka_ck, aka_auts);
+ if (res == 0) {
+ wpa_printf(MSG_DEBUG, "UMTS auth completed successfully");
+ wpa_hexdump(MSG_DEBUG, "RES", aka_res, aka_res_len);
+ wpa_hexdump(MSG_DEBUG, "IK", aka_ik, IK_LEN);
+ wpa_hexdump(MSG_DEBUG, "CK", aka_ck, CK_LEN);
+ } else if (res == -2) {
+ wpa_printf(MSG_DEBUG, "UMTS auth resulted in synchronization "
+ "failure");
+ wpa_hexdump(MSG_DEBUG, "AUTS", aka_auts, AKA_AUTS_LEN);
+ } else {
+ wpa_printf(MSG_DEBUG, "UMTS auth failed");
+ }
+
+failed:
+ scard_deinit(scard);
+
+ return 0;
+#undef num_triplets
+}
+
+
+static int scard_get_triplets(int argc, char *argv[])
+{
+ struct scard_data *scard;
+ size_t len;
+ char imsi[20];
+ unsigned char rand[16];
+ unsigned char sres[4];
+ unsigned char kc[8];
+ int num_triplets;
+ int i, j;
+
+ if (argc < 2 || ((num_triplets = atoi(argv[1])) <= 0)) {
+ printf("invalid parameters for sim command\n");
+ return -1;
+ }
+
+ if (argc <= 2 || strcmp(argv[2], "debug") != 0) {
+ /* disable debug output */
+ wpa_debug_level = 99;
+ }
+
+ scard = scard_init(SCARD_GSM_SIM_ONLY, argv[0]);
+ if (scard == NULL) {
+ printf("Failed to open smartcard connection\n");
+ return -1;
+ }
+
+ len = sizeof(imsi);
+ if (scard_get_imsi(scard, imsi, &len)) {
+ scard_deinit(scard);
+ return -1;
+ }
+
+ for (i = 0; i < num_triplets; i++) {
+ memset(rand, i, sizeof(rand));
+ if (scard_gsm_auth(scard, rand, sres, kc))
+ break;
+
+ /* IMSI:Kc:SRES:RAND */
+ for (j = 0; j < len; j++)
+ printf("%c", imsi[j]);
+ printf(":");
+ for (j = 0; j < 8; j++)
+ printf("%02X", kc[j]);
+ printf(":");
+ for (j = 0; j < 4; j++)
+ printf("%02X", sres[j]);
+ printf(":");
+ for (j = 0; j < 16; j++)
+ printf("%02X", rand[j]);
+ printf("\n");
+ }
+
+ scard_deinit(scard);
+
+ return 0;
+}
+
+
+static void eapol_test_terminate(int sig, void *eloop_ctx,
+ void *signal_ctx)
+{
+ struct wpa_supplicant *wpa_s = eloop_ctx;
+ wpa_msg(wpa_s, MSG_INFO, "Signal %d received - terminating", sig);
+ eloop_terminate();
+}
+
+
+static void usage(void)
+{
+ printf("usage:\n"
+ "eapol_test [-n] -c<conf> [-a<AS IP>] [-p<AS port>] "
+ "[-s<AS secret>] [-r<count>]\n"
+ "eapol_test scard\n"
+ "eapol_test sim <PIN> <num triplets> [debug]\n"
+ "\n"
+ "options:\n"
+ " -c<conf> = configuration file\n"
+ " -a<AS IP> = IP address of the authentication server, "
+ "default 127.0.0.1\n"
+ " -p<AS port> = UDP port of the authentication server, "
+ "default 1812\n"
+ " -s<AS secret> = shared secret with the authentication "
+ "server, default 'radius'\n"
+ " -r<count> = number of re-authentications\n"
+ " -n = no MPPE keys expected\n");
+}
+
+
+int main(int argc, char *argv[])
+{
+ struct wpa_supplicant wpa_s;
+ int c, ret = 1;
+ char *as_addr = "127.0.0.1";
+ int as_port = 1812;
+ char *as_secret = "radius";
+ char *conf = NULL;
+
+ wpa_debug_level = 0;
+ wpa_debug_show_keys = 1;
+
+ for (;;) {
+ c = getopt(argc, argv, "a:c:np:r:s:");
+ if (c < 0)
+ break;
+ switch (c) {
+ case 'a':
+ as_addr = optarg;
+ break;
+ case 'c':
+ conf = optarg;
+ break;
+ case 'n':
+ no_mppe_keys++;
+ break;
+ case 'p':
+ as_port = atoi(optarg);
+ break;
+ case 'r':
+ eapol_test_num_reauths = atoi(optarg);
+ break;
+ case 's':
+ as_secret = optarg;
+ break;
+ default:
+ usage();
+ return -1;
+ }
+ }
+
+ if (argc > optind && strcmp(argv[optind], "scard") == 0) {
+ return scard_test();
+ }
+
+ if (argc > optind && strcmp(argv[optind], "sim") == 0) {
+ return scard_get_triplets(argc - optind - 1,
+ &argv[optind + 1]);
+ }
+
+ if (conf == NULL) {
+ usage();
+ printf("Configuration file is required.\n");
+ return -1;
+ }
+
+ eloop_init(&wpa_s);
+
+ memset(&wpa_s, 0, sizeof(wpa_s));
+ wpa_s.conf = wpa_config_read(conf);
+ if (wpa_s.conf == NULL) {
+ printf("Failed to parse configuration file '%s'.\n", conf);
+ return -1;
+ }
+ if (wpa_s.conf->ssid == NULL) {
+ printf("No networks defined.\n");
+ return -1;
+ }
+
+ wpa_init_conf(&wpa_s, as_addr, as_port, as_secret);
+ if (wpa_supplicant_ctrl_iface_init(&wpa_s)) {
+ printf("Failed to initialize control interface '%s'.\n"
+ "You may have another eapol_test process already "
+ "running or the file was\n"
+ "left by an unclean termination of eapol_test in "
+ "which case you will need\n"
+ "to manually remove this file before starting "
+ "eapol_test again.\n",
+ wpa_s.conf->ctrl_interface);
+ return -1;
+ }
+ wpa_supplicant_scard_init(&wpa_s, wpa_s.conf->ssid);
+
+ if (test_eapol(&wpa_s, wpa_s.conf->ssid))
+ return -1;
+
+ eloop_register_timeout(30, 0, eapol_test_timeout, &wpa_s, NULL);
+ eloop_register_timeout(0, 0, send_eap_request_identity, &wpa_s, NULL);
+ eloop_register_signal(SIGINT, eapol_test_terminate, NULL);
+ eloop_register_signal(SIGTERM, eapol_test_terminate, NULL);
+ eloop_register_signal(SIGHUP, eapol_test_terminate, NULL);
+ eloop_run();
+
+ if (eapol_test_compare_pmk(&wpa_s) == 0)
+ ret = 0;
+ if (wpa_s.auth_timed_out)
+ ret = -2;
+ if (wpa_s.radius_access_reject_received)
+ ret = -3;
+
+ test_eapol_clean(&wpa_s);
+
+ eloop_destroy();
+
+ printf("MPPE keys OK: %d mismatch: %d\n",
+ num_mppe_ok, num_mppe_mismatch);
+ if (num_mppe_mismatch)
+ ret = -4;
+ if (ret)
+ printf("FAILURE\n");
+ else
+ printf("SUCCESS\n");
+
+ return ret;
+}
diff --git a/contrib/wpa_supplicant/eloop.c b/contrib/wpa_supplicant/eloop.c
new file mode 100644
index 000000000000..60715089beb2
--- /dev/null
+++ b/contrib/wpa_supplicant/eloop.c
@@ -0,0 +1,380 @@
+/*
+ * Event loop
+ * Copyright (c) 2002-2005, Jouni Malinen <jkmaline@cc.hut.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published 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 <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <errno.h>
+#include <signal.h>
+
+#ifdef CONFIG_NATIVE_WINDOWS
+#include "common.h"
+#endif /* CONFIG_NATIVE_WINDOWS */
+
+#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 timeval 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 {
+ void *user_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;
+};
+
+static struct eloop_data eloop;
+
+
+void eloop_init(void *user_data)
+{
+ memset(&eloop, 0, sizeof(eloop));
+ eloop.user_data = user_data;
+}
+
+
+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;
+
+ 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--;
+}
+
+
+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;
+ gettimeofday(&timeout->time, NULL);
+ timeout->time.tv_sec += secs;
+ timeout->time.tv_usec += usecs;
+ while (timeout->time.tv_usec >= 1000000) {
+ timeout->time.tv_sec++;
+ timeout->time.tv_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 (timercmp(&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;
+}
+
+
+#ifndef CONFIG_NATIVE_WINDOWS
+static void eloop_handle_alarm(int sig)
+{
+ fprintf(stderr, "eloop: could not process SIGINT or SIGTERM in two "
+ "seconds. Looks like there\n"
+ "is a bug that ends up in a busy loop that "
+ "prevents clean shutdown.\n"
+ "Killing program forcefully.\n");
+ exit(1);
+}
+#endif /* CONFIG_NATIVE_WINDOWS */
+
+
+static void eloop_handle_signal(int sig)
+{
+ int i;
+
+#ifndef CONFIG_NATIVE_WINDOWS
+ if ((sig == SIGINT || sig == SIGTERM) && !eloop.pending_terminate) {
+ /* Use SIGALRM to break out from potential busy loops that
+ * would not allow the program to be killed. */
+ eloop.pending_terminate = 1;
+ signal(SIGALRM, eloop_handle_alarm);
+ alarm(2);
+ }
+#endif /* CONFIG_NATIVE_WINDOWS */
+
+ eloop.signaled++;
+ for (i = 0; i < eloop.signal_count; i++) {
+ if (eloop.signals[i].sig == sig) {
+ eloop.signals[i].signaled++;
+ break;
+ }
+ }
+}
+
+
+static void eloop_process_pending_signals(void)
+{
+ int i;
+
+ if (eloop.signaled == 0)
+ return;
+ eloop.signaled = 0;
+
+ if (eloop.pending_terminate) {
+#ifndef CONFIG_NATIVE_WINDOWS
+ alarm(0);
+#endif /* CONFIG_NATIVE_WINDOWS */
+ 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;
+ signal(sig, eloop_handle_signal);
+
+ return 0;
+}
+
+
+void eloop_run(void)
+{
+ fd_set rfds;
+ int i, res;
+ struct timeval tv, now;
+
+ while (!eloop.terminate &&
+ (eloop.timeout || eloop.reader_count > 0)) {
+ if (eloop.timeout) {
+ gettimeofday(&now, NULL);
+ if (timercmp(&now, &eloop.timeout->time, <))
+ timersub(&eloop.timeout->time, &now, &tv);
+ else
+ tv.tv_sec = tv.tv_usec = 0;
+#if 0
+ printf("next timeout in %lu.%06lu sec\n",
+ tv.tv_sec, tv.tv_usec);
+#endif
+ }
+
+ FD_ZERO(&rfds);
+ for (i = 0; i < eloop.reader_count; i++)
+ FD_SET(eloop.readers[i].sock, &rfds);
+ res = select(eloop.max_sock + 1, &rfds, NULL, NULL,
+ eloop.timeout ? &tv : NULL);
+ if (res < 0 && errno != EINTR) {
+ perror("select");
+ return;
+ }
+ eloop_process_pending_signals();
+
+ /* check if some registered timeouts have occurred */
+ if (eloop.timeout) {
+ struct eloop_timeout *tmp;
+
+ gettimeofday(&now, NULL);
+ if (!timercmp(&now, &eloop.timeout->time, <)) {
+ tmp = eloop.timeout;
+ eloop.timeout = eloop.timeout->next;
+ tmp->handler(tmp->eloop_data,
+ tmp->user_data);
+ free(tmp);
+ }
+
+ }
+
+ if (res <= 0)
+ continue;
+
+ for (i = 0; i < eloop.reader_count; i++) {
+ if (FD_ISSET(eloop.readers[i].sock, &rfds)) {
+ eloop.readers[i].handler(
+ eloop.readers[i].sock,
+ eloop.readers[i].eloop_data,
+ eloop.readers[i].user_data);
+ }
+ }
+ }
+}
+
+
+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;
+}
diff --git a/contrib/wpa_supplicant/eloop.h b/contrib/wpa_supplicant/eloop.h
new file mode 100644
index 000000000000..f5b884740421
--- /dev/null
+++ b/contrib/wpa_supplicant/eloop.h
@@ -0,0 +1,53 @@
+#ifndef ELOOP_H
+#define ELOOP_H
+
+/* Magic number for eloop_cancel_timeout() */
+#define ELOOP_ALL_CTX (void *) -1
+
+/* Initialize global event loop data - must be called before any other eloop_*
+ * function. user_data is a pointer to global data structure and will be passed
+ * as eloop_ctx to signal handlers. */
+void eloop_init(void *user_data);
+
+/* Register handler for read event */
+int eloop_register_read_sock(int sock,
+ void (*handler)(int sock, void *eloop_ctx,
+ void *sock_ctx),
+ void *eloop_data, void *user_data);
+void eloop_unregister_read_sock(int sock);
+
+/* Register timeout */
+int eloop_register_timeout(unsigned int secs, unsigned int usecs,
+ void (*handler)(void *eloop_ctx, void *timeout_ctx),
+ void *eloop_data, void *user_data);
+
+/* Cancel timeouts matching <handler,eloop_data,user_data>.
+ * ELOOP_ALL_CTX can be used as a wildcard for cancelling all timeouts
+ * regardless of eloop_data/user_data. */
+int eloop_cancel_timeout(void (*handler)(void *eloop_ctx, void *sock_ctx),
+ void *eloop_data, void *user_data);
+
+/* Register handler for signal.
+ * Note: signals are 'global' events and there is no local eloop_data pointer
+ * like with other handlers. The (global) pointer given to eloop_init() will be
+ * used as eloop_ctx for signal handlers. */
+int eloop_register_signal(int sock,
+ void (*handler)(int sig, void *eloop_ctx,
+ void *signal_ctx),
+ void *user_data);
+
+/* Start event loop and continue running as long as there are any registered
+ * event handlers. */
+void eloop_run(void);
+
+/* Terminate event loop even if there are registered events. */
+void eloop_terminate(void);
+
+/* Free any reserved resources. After calling eloop_destoy(), other eloop_*
+ * functions must not be called before re-running eloop_init(). */
+void eloop_destroy(void);
+
+/* Check whether event loop has been terminated. */
+int eloop_terminated(void);
+
+#endif /* ELOOP_H */
diff --git a/contrib/wpa_supplicant/hostap_common.h b/contrib/wpa_supplicant/hostap_common.h
new file mode 100644
index 000000000000..003ad9ac6b58
--- /dev/null
+++ b/contrib/wpa_supplicant/hostap_common.h
@@ -0,0 +1,557 @@
+#ifndef HOSTAP_COMMON_H
+#define HOSTAP_COMMON_H
+
+#define BIT(x) (1 << (x))
+
+#define MAC2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5]
+#define MACSTR "%02x:%02x:%02x:%02x:%02x:%02x"
+
+
+#ifndef ETH_P_PAE
+#define ETH_P_PAE 0x888E /* Port Access Entity (IEEE 802.1X) */
+#endif /* ETH_P_PAE */
+
+#define ETH_P_PREAUTH 0x88C7 /* IEEE 802.11i pre-authentication */
+
+
+
+/* IEEE 802.11 defines */
+
+#define WLAN_FC_PVER (BIT(1) | BIT(0))
+#define WLAN_FC_TODS BIT(8)
+#define WLAN_FC_FROMDS BIT(9)
+#define WLAN_FC_MOREFRAG BIT(10)
+#define WLAN_FC_RETRY BIT(11)
+#define WLAN_FC_PWRMGT BIT(12)
+#define WLAN_FC_MOREDATA BIT(13)
+#define WLAN_FC_ISWEP BIT(14)
+#define WLAN_FC_ORDER BIT(15)
+
+#define WLAN_FC_GET_TYPE(fc) (((fc) & (BIT(3) | BIT(2))) >> 2)
+#define WLAN_FC_GET_STYPE(fc) \
+ (((fc) & (BIT(7) | BIT(6) | BIT(5) | BIT(4))) >> 4)
+
+#define WLAN_GET_SEQ_FRAG(seq) ((seq) & (BIT(3) | BIT(2) | BIT(1) | BIT(0)))
+#define WLAN_GET_SEQ_SEQ(seq) \
+ (((seq) & (~(BIT(3) | BIT(2) | BIT(1) | BIT(0)))) >> 4)
+
+#define WLAN_FC_TYPE_MGMT 0
+#define WLAN_FC_TYPE_CTRL 1
+#define WLAN_FC_TYPE_DATA 2
+
+/* management */
+#define WLAN_FC_STYPE_ASSOC_REQ 0
+#define WLAN_FC_STYPE_ASSOC_RESP 1
+#define WLAN_FC_STYPE_REASSOC_REQ 2
+#define WLAN_FC_STYPE_REASSOC_RESP 3
+#define WLAN_FC_STYPE_PROBE_REQ 4
+#define WLAN_FC_STYPE_PROBE_RESP 5
+#define WLAN_FC_STYPE_BEACON 8
+#define WLAN_FC_STYPE_ATIM 9
+#define WLAN_FC_STYPE_DISASSOC 10
+#define WLAN_FC_STYPE_AUTH 11
+#define WLAN_FC_STYPE_DEAUTH 12
+
+/* control */
+#define WLAN_FC_STYPE_PSPOLL 10
+#define WLAN_FC_STYPE_RTS 11
+#define WLAN_FC_STYPE_CTS 12
+#define WLAN_FC_STYPE_ACK 13
+#define WLAN_FC_STYPE_CFEND 14
+#define WLAN_FC_STYPE_CFENDACK 15
+
+/* data */
+#define WLAN_FC_STYPE_DATA 0
+#define WLAN_FC_STYPE_DATA_CFACK 1
+#define WLAN_FC_STYPE_DATA_CFPOLL 2
+#define WLAN_FC_STYPE_DATA_CFACKPOLL 3
+#define WLAN_FC_STYPE_NULLFUNC 4
+#define WLAN_FC_STYPE_CFACK 5
+#define WLAN_FC_STYPE_CFPOLL 6
+#define WLAN_FC_STYPE_CFACKPOLL 7
+
+/* Authentication algorithms */
+#define WLAN_AUTH_OPEN 0
+#define WLAN_AUTH_SHARED_KEY 1
+
+#define WLAN_AUTH_CHALLENGE_LEN 128
+
+#define WLAN_CAPABILITY_ESS BIT(0)
+#define WLAN_CAPABILITY_IBSS BIT(1)
+#define WLAN_CAPABILITY_CF_POLLABLE BIT(2)
+#define WLAN_CAPABILITY_CF_POLL_REQUEST BIT(3)
+#define WLAN_CAPABILITY_PRIVACY BIT(4)
+
+/* Status codes */
+#define WLAN_STATUS_SUCCESS 0
+#define WLAN_STATUS_UNSPECIFIED_FAILURE 1
+#define WLAN_STATUS_CAPS_UNSUPPORTED 10
+#define WLAN_STATUS_REASSOC_NO_ASSOC 11
+#define WLAN_STATUS_ASSOC_DENIED_UNSPEC 12
+#define WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG 13
+#define WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION 14
+#define WLAN_STATUS_CHALLENGE_FAIL 15
+#define WLAN_STATUS_AUTH_TIMEOUT 16
+#define WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA 17
+#define WLAN_STATUS_ASSOC_DENIED_RATES 18
+/* 802.11b */
+#define WLAN_STATUS_ASSOC_DENIED_NOSHORT 19
+#define WLAN_STATUS_ASSOC_DENIED_NOPBCC 20
+#define WLAN_STATUS_ASSOC_DENIED_NOAGILITY 21
+/* IEEE 802.11i */
+#define WLAN_STATUS_INVALID_IE 40
+#define WLAN_STATUS_GROUP_CIPHER_NOT_VALID 41
+#define WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID 42
+#define WLAN_STATUS_AKMP_NOT_VALID 43
+#define WLAN_STATUS_UNSUPPORTED_RSN_IE_VERSION 44
+#define WLAN_STATUS_INVALID_RSN_IE_CAPAB 45
+#define WLAN_STATUS_CIPHER_REJECTED_PER_POLICY 46
+
+/* Reason codes */
+#define WLAN_REASON_UNSPECIFIED 1
+#define WLAN_REASON_PREV_AUTH_NOT_VALID 2
+#define WLAN_REASON_DEAUTH_LEAVING 3
+#define WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY 4
+#define WLAN_REASON_DISASSOC_AP_BUSY 5
+#define WLAN_REASON_CLASS2_FRAME_FROM_NONAUTH_STA 6
+#define WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA 7
+#define WLAN_REASON_DISASSOC_STA_HAS_LEFT 8
+#define WLAN_REASON_STA_REQ_ASSOC_WITHOUT_AUTH 9
+/* IEEE 802.11i */
+#define WLAN_REASON_INVALID_IE 13
+#define WLAN_REASON_MICHAEL_MIC_FAILURE 14
+#define WLAN_REASON_4WAY_HANDSHAKE_TIMEOUT 15
+#define WLAN_REASON_GROUP_KEY_UPDATE_TIMEOUT 16
+#define WLAN_REASON_IE_IN_4WAY_DIFFERS 17
+#define WLAN_REASON_GROUP_CIPHER_NOT_VALID 18
+#define WLAN_REASON_PAIRWISE_CIPHER_NOT_VALID 19
+#define WLAN_REASON_AKMP_NOT_VALID 20
+#define WLAN_REASON_UNSUPPORTED_RSN_IE_VERSION 21
+#define WLAN_REASON_INVALID_RSN_IE_CAPAB 22
+#define WLAN_REASON_IEEE_802_1X_AUTH_FAILED 23
+#define WLAN_REASON_CIPHER_SUITE_REJECTED 24
+
+
+/* Information Element IDs */
+#define WLAN_EID_SSID 0
+#define WLAN_EID_SUPP_RATES 1
+#define WLAN_EID_FH_PARAMS 2
+#define WLAN_EID_DS_PARAMS 3
+#define WLAN_EID_CF_PARAMS 4
+#define WLAN_EID_TIM 5
+#define WLAN_EID_IBSS_PARAMS 6
+#define WLAN_EID_CHALLENGE 16
+#define WLAN_EID_RSN 48
+#define WLAN_EID_GENERIC 221
+
+
+/* HFA384X Configuration RIDs */
+#define HFA384X_RID_CNFPORTTYPE 0xFC00
+#define HFA384X_RID_CNFOWNMACADDR 0xFC01
+#define HFA384X_RID_CNFDESIREDSSID 0xFC02
+#define HFA384X_RID_CNFOWNCHANNEL 0xFC03
+#define HFA384X_RID_CNFOWNSSID 0xFC04
+#define HFA384X_RID_CNFOWNATIMWINDOW 0xFC05
+#define HFA384X_RID_CNFSYSTEMSCALE 0xFC06
+#define HFA384X_RID_CNFMAXDATALEN 0xFC07
+#define HFA384X_RID_CNFWDSADDRESS 0xFC08
+#define HFA384X_RID_CNFPMENABLED 0xFC09
+#define HFA384X_RID_CNFPMEPS 0xFC0A
+#define HFA384X_RID_CNFMULTICASTRECEIVE 0xFC0B
+#define HFA384X_RID_CNFMAXSLEEPDURATION 0xFC0C
+#define HFA384X_RID_CNFPMHOLDOVERDURATION 0xFC0D
+#define HFA384X_RID_CNFOWNNAME 0xFC0E
+#define HFA384X_RID_CNFOWNDTIMPERIOD 0xFC10
+#define HFA384X_RID_CNFWDSADDRESS1 0xFC11 /* AP f/w only */
+#define HFA384X_RID_CNFWDSADDRESS2 0xFC12 /* AP f/w only */
+#define HFA384X_RID_CNFWDSADDRESS3 0xFC13 /* AP f/w only */
+#define HFA384X_RID_CNFWDSADDRESS4 0xFC14 /* AP f/w only */
+#define HFA384X_RID_CNFWDSADDRESS5 0xFC15 /* AP f/w only */
+#define HFA384X_RID_CNFWDSADDRESS6 0xFC16 /* AP f/w only */
+#define HFA384X_RID_CNFMULTICASTPMBUFFERING 0xFC17 /* AP f/w only */
+#define HFA384X_RID_UNKNOWN1 0xFC20
+#define HFA384X_RID_UNKNOWN2 0xFC21
+#define HFA384X_RID_CNFWEPDEFAULTKEYID 0xFC23
+#define HFA384X_RID_CNFDEFAULTKEY0 0xFC24
+#define HFA384X_RID_CNFDEFAULTKEY1 0xFC25
+#define HFA384X_RID_CNFDEFAULTKEY2 0xFC26
+#define HFA384X_RID_CNFDEFAULTKEY3 0xFC27
+#define HFA384X_RID_CNFWEPFLAGS 0xFC28
+#define HFA384X_RID_CNFWEPKEYMAPPINGTABLE 0xFC29
+#define HFA384X_RID_CNFAUTHENTICATION 0xFC2A
+#define HFA384X_RID_CNFMAXASSOCSTA 0xFC2B /* AP f/w only */
+#define HFA384X_RID_CNFTXCONTROL 0xFC2C
+#define HFA384X_RID_CNFROAMINGMODE 0xFC2D
+#define HFA384X_RID_CNFHOSTAUTHENTICATION 0xFC2E /* AP f/w only */
+#define HFA384X_RID_CNFRCVCRCERROR 0xFC30
+#define HFA384X_RID_CNFMMLIFE 0xFC31
+#define HFA384X_RID_CNFALTRETRYCOUNT 0xFC32
+#define HFA384X_RID_CNFBEACONINT 0xFC33
+#define HFA384X_RID_CNFAPPCFINFO 0xFC34 /* AP f/w only */
+#define HFA384X_RID_CNFSTAPCFINFO 0xFC35
+#define HFA384X_RID_CNFPRIORITYQUSAGE 0xFC37
+#define HFA384X_RID_CNFTIMCTRL 0xFC40
+#define HFA384X_RID_UNKNOWN3 0xFC41 /* added in STA f/w 0.7.x */
+#define HFA384X_RID_CNFTHIRTY2TALLY 0xFC42 /* added in STA f/w 0.8.0 */
+#define HFA384X_RID_CNFENHSECURITY 0xFC43 /* AP f/w or STA f/w >= 1.6.3 */
+#define HFA384X_RID_CNFDBMADJUST 0xFC46 /* added in STA f/w 1.3.1 */
+#define HFA384X_RID_GENERICELEMENT 0xFC48 /* added in STA f/w 1.7.0;
+ * write only */
+#define HFA384X_RID_PROPAGATIONDELAY 0xFC49 /* added in STA f/w 1.7.6 */
+#define HFA384X_RID_GROUPADDRESSES 0xFC80
+#define HFA384X_RID_CREATEIBSS 0xFC81
+#define HFA384X_RID_FRAGMENTATIONTHRESHOLD 0xFC82
+#define HFA384X_RID_RTSTHRESHOLD 0xFC83
+#define HFA384X_RID_TXRATECONTROL 0xFC84
+#define HFA384X_RID_PROMISCUOUSMODE 0xFC85
+#define HFA384X_RID_FRAGMENTATIONTHRESHOLD0 0xFC90 /* AP f/w only */
+#define HFA384X_RID_FRAGMENTATIONTHRESHOLD1 0xFC91 /* AP f/w only */
+#define HFA384X_RID_FRAGMENTATIONTHRESHOLD2 0xFC92 /* AP f/w only */
+#define HFA384X_RID_FRAGMENTATIONTHRESHOLD3 0xFC93 /* AP f/w only */
+#define HFA384X_RID_FRAGMENTATIONTHRESHOLD4 0xFC94 /* AP f/w only */
+#define HFA384X_RID_FRAGMENTATIONTHRESHOLD5 0xFC95 /* AP f/w only */
+#define HFA384X_RID_FRAGMENTATIONTHRESHOLD6 0xFC96 /* AP f/w only */
+#define HFA384X_RID_RTSTHRESHOLD0 0xFC97 /* AP f/w only */
+#define HFA384X_RID_RTSTHRESHOLD1 0xFC98 /* AP f/w only */
+#define HFA384X_RID_RTSTHRESHOLD2 0xFC99 /* AP f/w only */
+#define HFA384X_RID_RTSTHRESHOLD3 0xFC9A /* AP f/w only */
+#define HFA384X_RID_RTSTHRESHOLD4 0xFC9B /* AP f/w only */
+#define HFA384X_RID_RTSTHRESHOLD5 0xFC9C /* AP f/w only */
+#define HFA384X_RID_RTSTHRESHOLD6 0xFC9D /* AP f/w only */
+#define HFA384X_RID_TXRATECONTROL0 0xFC9E /* AP f/w only */
+#define HFA384X_RID_TXRATECONTROL1 0xFC9F /* AP f/w only */
+#define HFA384X_RID_TXRATECONTROL2 0xFCA0 /* AP f/w only */
+#define HFA384X_RID_TXRATECONTROL3 0xFCA1 /* AP f/w only */
+#define HFA384X_RID_TXRATECONTROL4 0xFCA2 /* AP f/w only */
+#define HFA384X_RID_TXRATECONTROL5 0xFCA3 /* AP f/w only */
+#define HFA384X_RID_TXRATECONTROL6 0xFCA4 /* AP f/w only */
+#define HFA384X_RID_CNFSHORTPREAMBLE 0xFCB0
+#define HFA384X_RID_CNFEXCLUDELONGPREAMBLE 0xFCB1
+#define HFA384X_RID_CNFAUTHENTICATIONRSPTO 0xFCB2
+#define HFA384X_RID_CNFBASICRATES 0xFCB3
+#define HFA384X_RID_CNFSUPPORTEDRATES 0xFCB4
+#define HFA384X_RID_CNFFALLBACKCTRL 0xFCB5 /* added in STA f/w 1.3.1 */
+#define HFA384X_RID_WEPKEYDISABLE 0xFCB6 /* added in STA f/w 1.3.1 */
+#define HFA384X_RID_WEPKEYMAPINDEX 0xFCB7 /* ? */
+#define HFA384X_RID_BROADCASTKEYID 0xFCB8 /* ? */
+#define HFA384X_RID_ENTSECFLAGEYID 0xFCB9 /* ? */
+#define HFA384X_RID_CNFPASSIVESCANCTRL 0xFCBA /* added in STA f/w 1.5.0 */
+#define HFA384X_RID_SSNHANDLINGMODE 0xFCBB /* added in STA f/w 1.7.0 */
+#define HFA384X_RID_MDCCONTROL 0xFCBC /* added in STA f/w 1.7.0 */
+#define HFA384X_RID_MDCCOUNTRY 0xFCBD /* added in STA f/w 1.7.0 */
+#define HFA384X_RID_TXPOWERMAX 0xFCBE /* added in STA f/w 1.7.0 */
+#define HFA384X_RID_CNFLFOENABLED 0xFCBF /* added in STA f/w 1.6.3 */
+#define HFA384X_RID_CAPINFO 0xFCC0 /* added in STA f/w 1.7.0 */
+#define HFA384X_RID_LISTENINTERVAL 0xFCC1 /* added in STA f/w 1.7.0 */
+#define HFA384X_RID_SW_ANT_DIV 0xFCC2 /* added in STA f/w 1.7.0; Prism3 */
+#define HFA384X_RID_LED_CTRL 0xFCC4 /* added in STA f/w 1.7.6 */
+#define HFA384X_RID_HFODELAY 0xFCC5 /* added in STA f/w 1.7.6 */
+#define HFA384X_RID_DISALLOWEDBSSID 0xFCC6 /* added in STA f/w 1.8.0 */
+#define HFA384X_RID_TICKTIME 0xFCE0
+#define HFA384X_RID_SCANREQUEST 0xFCE1
+#define HFA384X_RID_JOINREQUEST 0xFCE2
+#define HFA384X_RID_AUTHENTICATESTATION 0xFCE3 /* AP f/w only */
+#define HFA384X_RID_CHANNELINFOREQUEST 0xFCE4 /* AP f/w only */
+#define HFA384X_RID_HOSTSCAN 0xFCE5 /* added in STA f/w 1.3.1 */
+
+/* HFA384X Information RIDs */
+#define HFA384X_RID_MAXLOADTIME 0xFD00
+#define HFA384X_RID_DOWNLOADBUFFER 0xFD01
+#define HFA384X_RID_PRIID 0xFD02
+#define HFA384X_RID_PRISUPRANGE 0xFD03
+#define HFA384X_RID_CFIACTRANGES 0xFD04
+#define HFA384X_RID_NICSERNUM 0xFD0A
+#define HFA384X_RID_NICID 0xFD0B
+#define HFA384X_RID_MFISUPRANGE 0xFD0C
+#define HFA384X_RID_CFISUPRANGE 0xFD0D
+#define HFA384X_RID_CHANNELLIST 0xFD10
+#define HFA384X_RID_REGULATORYDOMAINS 0xFD11
+#define HFA384X_RID_TEMPTYPE 0xFD12
+#define HFA384X_RID_CIS 0xFD13
+#define HFA384X_RID_STAID 0xFD20
+#define HFA384X_RID_STASUPRANGE 0xFD21
+#define HFA384X_RID_MFIACTRANGES 0xFD22
+#define HFA384X_RID_CFIACTRANGES2 0xFD23
+#define HFA384X_RID_PRODUCTNAME 0xFD24 /* added in STA f/w 1.3.1;
+ * only Prism2.5(?) */
+#define HFA384X_RID_PORTSTATUS 0xFD40
+#define HFA384X_RID_CURRENTSSID 0xFD41
+#define HFA384X_RID_CURRENTBSSID 0xFD42
+#define HFA384X_RID_COMMSQUALITY 0xFD43
+#define HFA384X_RID_CURRENTTXRATE 0xFD44
+#define HFA384X_RID_CURRENTBEACONINTERVAL 0xFD45
+#define HFA384X_RID_CURRENTSCALETHRESHOLDS 0xFD46
+#define HFA384X_RID_PROTOCOLRSPTIME 0xFD47
+#define HFA384X_RID_SHORTRETRYLIMIT 0xFD48
+#define HFA384X_RID_LONGRETRYLIMIT 0xFD49
+#define HFA384X_RID_MAXTRANSMITLIFETIME 0xFD4A
+#define HFA384X_RID_MAXRECEIVELIFETIME 0xFD4B
+#define HFA384X_RID_CFPOLLABLE 0xFD4C
+#define HFA384X_RID_AUTHENTICATIONALGORITHMS 0xFD4D
+#define HFA384X_RID_PRIVACYOPTIONIMPLEMENTED 0xFD4F
+#define HFA384X_RID_DBMCOMMSQUALITY 0xFD51 /* added in STA f/w 1.3.1 */
+#define HFA384X_RID_CURRENTTXRATE1 0xFD80 /* AP f/w only */
+#define HFA384X_RID_CURRENTTXRATE2 0xFD81 /* AP f/w only */
+#define HFA384X_RID_CURRENTTXRATE3 0xFD82 /* AP f/w only */
+#define HFA384X_RID_CURRENTTXRATE4 0xFD83 /* AP f/w only */
+#define HFA384X_RID_CURRENTTXRATE5 0xFD84 /* AP f/w only */
+#define HFA384X_RID_CURRENTTXRATE6 0xFD85 /* AP f/w only */
+#define HFA384X_RID_OWNMACADDR 0xFD86 /* AP f/w only */
+#define HFA384X_RID_SCANRESULTSTABLE 0xFD88 /* added in STA f/w 0.8.3 */
+#define HFA384X_RID_HOSTSCANRESULTS 0xFD89 /* added in STA f/w 1.3.1 */
+#define HFA384X_RID_AUTHENTICATIONUSED 0xFD8A /* added in STA f/w 1.3.4 */
+#define HFA384X_RID_CNFFAASWITCHCTRL 0xFD8B /* added in STA f/w 1.6.3 */
+#define HFA384X_RID_ASSOCIATIONFAILURE 0xFD8D /* added in STA f/w 1.8.0 */
+#define HFA384X_RID_PHYTYPE 0xFDC0
+#define HFA384X_RID_CURRENTCHANNEL 0xFDC1
+#define HFA384X_RID_CURRENTPOWERSTATE 0xFDC2
+#define HFA384X_RID_CCAMODE 0xFDC3
+#define HFA384X_RID_SUPPORTEDDATARATES 0xFDC6
+#define HFA384X_RID_LFO_VOLT_REG_TEST_RES 0xFDC7 /* added in STA f/w 1.7.1 */
+#define HFA384X_RID_BUILDSEQ 0xFFFE
+#define HFA384X_RID_FWID 0xFFFF
+
+
+struct hfa384x_comp_ident
+{
+ u16 id;
+ u16 variant;
+ u16 major;
+ u16 minor;
+} __attribute__ ((packed));
+
+#define HFA384X_COMP_ID_PRI 0x15
+#define HFA384X_COMP_ID_STA 0x1f
+#define HFA384X_COMP_ID_FW_AP 0x14b
+
+struct hfa384x_sup_range
+{
+ u16 role;
+ u16 id;
+ u16 variant;
+ u16 bottom;
+ u16 top;
+} __attribute__ ((packed));
+
+
+struct hfa384x_build_id
+{
+ u16 pri_seq;
+ u16 sec_seq;
+} __attribute__ ((packed));
+
+/* FD01 - Download Buffer */
+struct hfa384x_rid_download_buffer
+{
+ u16 page;
+ u16 offset;
+ u16 length;
+} __attribute__ ((packed));
+
+/* BSS connection quality (RID FD43 range, RID FD51 dBm-normalized) */
+struct hfa384x_comms_quality {
+ u16 comm_qual; /* 0 .. 92 */
+ u16 signal_level; /* 27 .. 154 */
+ u16 noise_level; /* 27 .. 154 */
+} __attribute__ ((packed));
+
+
+/* netdevice private ioctls (used, e.g., with iwpriv from user space) */
+
+/* New wireless extensions API - SET/GET convention (even ioctl numbers are
+ * root only)
+ */
+#define PRISM2_IOCTL_PRISM2_PARAM (SIOCIWFIRSTPRIV + 0)
+#define PRISM2_IOCTL_GET_PRISM2_PARAM (SIOCIWFIRSTPRIV + 1)
+#define PRISM2_IOCTL_WRITEMIF (SIOCIWFIRSTPRIV + 2)
+#define PRISM2_IOCTL_READMIF (SIOCIWFIRSTPRIV + 3)
+#define PRISM2_IOCTL_MONITOR (SIOCIWFIRSTPRIV + 4)
+#define PRISM2_IOCTL_RESET (SIOCIWFIRSTPRIV + 6)
+#define PRISM2_IOCTL_INQUIRE (SIOCIWFIRSTPRIV + 8)
+#define PRISM2_IOCTL_WDS_ADD (SIOCIWFIRSTPRIV + 10)
+#define PRISM2_IOCTL_WDS_DEL (SIOCIWFIRSTPRIV + 12)
+#define PRISM2_IOCTL_SET_RID_WORD (SIOCIWFIRSTPRIV + 14)
+#define PRISM2_IOCTL_MACCMD (SIOCIWFIRSTPRIV + 16)
+#define PRISM2_IOCTL_ADDMAC (SIOCIWFIRSTPRIV + 18)
+#define PRISM2_IOCTL_DELMAC (SIOCIWFIRSTPRIV + 20)
+#define PRISM2_IOCTL_KICKMAC (SIOCIWFIRSTPRIV + 22)
+
+/* following are not in SIOCGIWPRIV list; check permission in the driver code
+ */
+#define PRISM2_IOCTL_DOWNLOAD (SIOCDEVPRIVATE + 13)
+#define PRISM2_IOCTL_HOSTAPD (SIOCDEVPRIVATE + 14)
+
+
+/* PRISM2_IOCTL_PRISM2_PARAM ioctl() subtypes: */
+enum {
+ /* PRISM2_PARAM_PTYPE = 1, */ /* REMOVED 2003-10-22 */
+ PRISM2_PARAM_TXRATECTRL = 2,
+ PRISM2_PARAM_BEACON_INT = 3,
+ PRISM2_PARAM_PSEUDO_IBSS = 4,
+ PRISM2_PARAM_ALC = 5,
+ /* PRISM2_PARAM_TXPOWER = 6, */ /* REMOVED 2003-10-22 */
+ PRISM2_PARAM_DUMP = 7,
+ PRISM2_PARAM_OTHER_AP_POLICY = 8,
+ PRISM2_PARAM_AP_MAX_INACTIVITY = 9,
+ PRISM2_PARAM_AP_BRIDGE_PACKETS = 10,
+ PRISM2_PARAM_DTIM_PERIOD = 11,
+ PRISM2_PARAM_AP_NULLFUNC_ACK = 12,
+ PRISM2_PARAM_MAX_WDS = 13,
+ PRISM2_PARAM_AP_AUTOM_AP_WDS = 14,
+ PRISM2_PARAM_AP_AUTH_ALGS = 15,
+ PRISM2_PARAM_MONITOR_ALLOW_FCSERR = 16,
+ PRISM2_PARAM_HOST_ENCRYPT = 17,
+ PRISM2_PARAM_HOST_DECRYPT = 18,
+ PRISM2_PARAM_BUS_MASTER_THRESHOLD_RX = 19,
+ PRISM2_PARAM_BUS_MASTER_THRESHOLD_TX = 20,
+ PRISM2_PARAM_HOST_ROAMING = 21,
+ PRISM2_PARAM_BCRX_STA_KEY = 22,
+ PRISM2_PARAM_IEEE_802_1X = 23,
+ PRISM2_PARAM_ANTSEL_TX = 24,
+ PRISM2_PARAM_ANTSEL_RX = 25,
+ PRISM2_PARAM_MONITOR_TYPE = 26,
+ PRISM2_PARAM_WDS_TYPE = 27,
+ PRISM2_PARAM_HOSTSCAN = 28,
+ PRISM2_PARAM_AP_SCAN = 29,
+ PRISM2_PARAM_ENH_SEC = 30,
+ PRISM2_PARAM_IO_DEBUG = 31,
+ PRISM2_PARAM_BASIC_RATES = 32,
+ PRISM2_PARAM_OPER_RATES = 33,
+ PRISM2_PARAM_HOSTAPD = 34,
+ PRISM2_PARAM_HOSTAPD_STA = 35,
+ PRISM2_PARAM_WPA = 36,
+ PRISM2_PARAM_PRIVACY_INVOKED = 37,
+ PRISM2_PARAM_TKIP_COUNTERMEASURES = 38,
+ PRISM2_PARAM_DROP_UNENCRYPTED = 39,
+};
+
+enum { HOSTAP_ANTSEL_DO_NOT_TOUCH = 0, HOSTAP_ANTSEL_DIVERSITY = 1,
+ HOSTAP_ANTSEL_LOW = 2, HOSTAP_ANTSEL_HIGH = 3 };
+
+
+/* PRISM2_IOCTL_MACCMD ioctl() subcommands: */
+enum { AP_MAC_CMD_POLICY_OPEN = 0, AP_MAC_CMD_POLICY_ALLOW = 1,
+ AP_MAC_CMD_POLICY_DENY = 2, AP_MAC_CMD_FLUSH = 3,
+ AP_MAC_CMD_KICKALL = 4 };
+
+
+/* PRISM2_IOCTL_DOWNLOAD ioctl() dl_cmd: */
+enum {
+ PRISM2_DOWNLOAD_VOLATILE = 1 /* RAM */,
+ /* Note! Old versions of prism2_srec have a fatal error in CRC-16
+ * calculation, which will corrupt all non-volatile downloads.
+ * PRISM2_DOWNLOAD_NON_VOLATILE used to be 2, but it is now 3 to
+ * prevent use of old versions of prism2_srec for non-volatile
+ * download. */
+ PRISM2_DOWNLOAD_NON_VOLATILE = 3 /* FLASH */,
+ PRISM2_DOWNLOAD_VOLATILE_GENESIS = 4 /* RAM in Genesis mode */,
+ /* Persistent versions of volatile download commands (keep firmware
+ * data in memory and automatically re-download after hw_reset */
+ PRISM2_DOWNLOAD_VOLATILE_PERSISTENT = 5,
+ PRISM2_DOWNLOAD_VOLATILE_GENESIS_PERSISTENT = 6,
+};
+
+struct prism2_download_param {
+ u32 dl_cmd;
+ u32 start_addr;
+ u32 num_areas;
+ struct prism2_download_area {
+ u32 addr; /* wlan card address */
+ u32 len;
+ caddr_t ptr; /* pointer to data in user space */
+ } data[0];
+};
+
+#define PRISM2_MAX_DOWNLOAD_AREA_LEN 131072
+#define PRISM2_MAX_DOWNLOAD_LEN 262144
+
+
+/* PRISM2_IOCTL_HOSTAPD ioctl() cmd: */
+enum {
+ PRISM2_HOSTAPD_FLUSH = 1,
+ PRISM2_HOSTAPD_ADD_STA = 2,
+ PRISM2_HOSTAPD_REMOVE_STA = 3,
+ PRISM2_HOSTAPD_GET_INFO_STA = 4,
+ /* REMOVED: PRISM2_HOSTAPD_RESET_TXEXC_STA = 5, */
+ PRISM2_SET_ENCRYPTION = 6,
+ PRISM2_GET_ENCRYPTION = 7,
+ PRISM2_HOSTAPD_SET_FLAGS_STA = 8,
+ PRISM2_HOSTAPD_GET_RID = 9,
+ PRISM2_HOSTAPD_SET_RID = 10,
+ PRISM2_HOSTAPD_SET_ASSOC_AP_ADDR = 11,
+ PRISM2_HOSTAPD_SET_GENERIC_ELEMENT = 12,
+ PRISM2_HOSTAPD_MLME = 13,
+ PRISM2_HOSTAPD_SCAN_REQ = 14,
+ PRISM2_HOSTAPD_STA_CLEAR_STATS = 15,
+};
+
+#define PRISM2_HOSTAPD_MAX_BUF_SIZE 1024
+#define PRISM2_HOSTAPD_RID_HDR_LEN \
+((int) (&((struct prism2_hostapd_param *) 0)->u.rid.data))
+#define PRISM2_HOSTAPD_GENERIC_ELEMENT_HDR_LEN \
+((int) (&((struct prism2_hostapd_param *) 0)->u.generic_elem.data))
+
+/* Maximum length for algorithm names (-1 for nul termination) used in ioctl()
+ */
+#define HOSTAP_CRYPT_ALG_NAME_LEN 16
+
+
+struct prism2_hostapd_param {
+ u32 cmd;
+ u8 sta_addr[ETH_ALEN];
+ union {
+ struct {
+ u16 aid;
+ u16 capability;
+ u8 tx_supp_rates;
+ } add_sta;
+ struct {
+ u32 inactive_sec;
+ } get_info_sta;
+ struct {
+ u8 alg[HOSTAP_CRYPT_ALG_NAME_LEN];
+ u32 flags;
+ u32 err;
+ u8 idx;
+ u8 seq[8]; /* sequence counter (set: RX, get: TX) */
+ u16 key_len;
+ u8 key[0];
+ } crypt;
+ struct {
+ u32 flags_and;
+ u32 flags_or;
+ } set_flags_sta;
+ struct {
+ u16 rid;
+ u16 len;
+ u8 data[0];
+ } rid;
+ struct {
+ u8 len;
+ u8 data[0];
+ } generic_elem;
+ struct {
+#define MLME_STA_DEAUTH 0
+#define MLME_STA_DISASSOC 1
+ u16 cmd;
+ u16 reason_code;
+ } mlme;
+ struct {
+ u8 ssid_len;
+ u8 ssid[32];
+ } scan_req;
+ } u;
+};
+
+#define HOSTAP_CRYPT_FLAG_SET_TX_KEY BIT(0)
+#define HOSTAP_CRYPT_FLAG_PERMANENT BIT(1)
+
+#define HOSTAP_CRYPT_ERR_UNKNOWN_ALG 2
+#define HOSTAP_CRYPT_ERR_UNKNOWN_ADDR 3
+#define HOSTAP_CRYPT_ERR_CRYPT_INIT_FAILED 4
+#define HOSTAP_CRYPT_ERR_KEY_SET_FAILED 5
+#define HOSTAP_CRYPT_ERR_TX_KEY_SET_FAILED 6
+#define HOSTAP_CRYPT_ERR_CARD_CONF_FAILED 7
+
+
+#endif /* HOSTAP_COMMON_H */
diff --git a/contrib/wpa_supplicant/l2_packet.h b/contrib/wpa_supplicant/l2_packet.h
new file mode 100644
index 000000000000..3e3914ca5502
--- /dev/null
+++ b/contrib/wpa_supplicant/l2_packet.h
@@ -0,0 +1,34 @@
+#ifndef L2_PACKET_H
+#define L2_PACKET_H
+
+#define MAC2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5]
+#define MACSTR "%02x:%02x:%02x:%02x:%02x:%02x"
+
+#ifndef ETH_P_EAPOL
+#define ETH_P_EAPOL 0x888e
+#endif
+
+#ifndef ETH_P_RSN_PREAUTH
+#define ETH_P_RSN_PREAUTH 0x88c7
+#endif
+
+struct l2_packet_data;
+
+struct l2_ethhdr {
+ u8 h_dest[ETH_ALEN];
+ u8 h_source[ETH_ALEN];
+ u16 h_proto;
+} __attribute__ ((packed));
+
+struct l2_packet_data * l2_packet_init(
+ const char *ifname, const u8 *own_addr, unsigned short protocol,
+ void (*rx_callback)(void *ctx, unsigned char *src_addr,
+ unsigned char *buf, size_t len),
+ void *rx_callback_ctx);
+void l2_packet_deinit(struct l2_packet_data *l2);
+
+int l2_packet_get_own_addr(struct l2_packet_data *l2, u8 *addr);
+int l2_packet_send(struct l2_packet_data *l2, u8 *buf, size_t len);
+void l2_packet_set_rx_l2_hdr(struct l2_packet_data *l2, int rx_l2_hdr);
+
+#endif /* L2_PACKET_H */
diff --git a/contrib/wpa_supplicant/md5.c b/contrib/wpa_supplicant/md5.c
new file mode 100644
index 000000000000..1564e8f64797
--- /dev/null
+++ b/contrib/wpa_supplicant/md5.c
@@ -0,0 +1,355 @@
+/*
+ * MD5 hash implementation and interface functions
+ * Copyright (c) 2003-2004, Jouni Malinen <jkmaline@cc.hut.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published 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 <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "common.h"
+#include "md5.h"
+
+
+void md5_mac(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
+ u8 *mac)
+{
+ MD5_CTX context;
+ MD5Init(&context);
+ MD5Update(&context, key, key_len);
+ MD5Update(&context, data, data_len);
+ MD5Update(&context, key, key_len);
+ MD5Final(mac, &context);
+}
+
+
+/* HMAC code is based on RFC 2104 */
+void hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem,
+ const u8 *addr[], const size_t *len, u8 *mac)
+{
+ MD5_CTX context;
+ u8 k_ipad[65]; /* inner padding - key XORd with ipad */
+ u8 k_opad[65]; /* outer padding - key XORd with opad */
+ u8 tk[16];
+ int i;
+
+ /* if key is longer than 64 bytes reset it to key = MD5(key) */
+ if (key_len > 64) {
+ MD5Init(&context);
+ MD5Update(&context, key, key_len);
+ MD5Final(tk, &context);
+
+ 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 pads */
+ memset(k_ipad, 0, sizeof(k_ipad));
+ memset(k_opad, 0, sizeof(k_opad));
+ memcpy(k_ipad, key, key_len);
+ memcpy(k_opad, key, key_len);
+
+ /* XOR key with ipad and opad values */
+ for (i = 0; i < 64; i++) {
+ k_ipad[i] ^= 0x36;
+ k_opad[i] ^= 0x5c;
+ }
+
+ /* perform inner MD5 */
+ MD5Init(&context); /* init context for 1st pass */
+ MD5Update(&context, k_ipad, 64); /* start with inner pad */
+ /* then text of datagram; all fragments */
+ for (i = 0; i < num_elem; i++) {
+ MD5Update(&context, addr[i], len[i]);
+ }
+ MD5Final(mac, &context); /* finish up 1st pass */
+
+ /* perform outer MD5 */
+ MD5Init(&context); /* init context for 2nd pass */
+ MD5Update(&context, k_opad, 64); /* start with outer pad */
+ MD5Update(&context, mac, 16); /* then results of 1st hash */
+ MD5Final(mac, &context); /* finish up 2nd pass */
+}
+
+
+void hmac_md5(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
+ u8 *mac)
+{
+ hmac_md5_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+
+#ifndef EAP_TLS_FUNCS
+
+/* ===== start - public domain MD5 implementation ===== */
+/*
+ * This code implements the MD5 message-digest algorithm.
+ * The algorithm is due to Ron Rivest. This code was
+ * written by Colin Plumb in 1993, no copyright is claimed.
+ * This code is in the public domain; do with it what you wish.
+ *
+ * Equivalent code is available from RSA Data Security, Inc.
+ * This code has been tested against that, and is equivalent,
+ * except that you don't need to include two pages of legalese
+ * with every copy.
+ *
+ * To compute the message digest of a chunk of bytes, declare an
+ * MD5Context structure, pass it to MD5Init, call MD5Update as
+ * needed on buffers full of bytes, and then call MD5Final, which
+ * will fill a supplied 16-byte array with the digest.
+ */
+
+#ifndef WORDS_BIGENDIAN
+#define byteReverse(buf, len) /* Nothing */
+#else
+void byteReverse(unsigned char *buf, unsigned longs);
+
+#ifndef ASM_MD5
+/*
+ * Note: this code is harmless on little-endian machines.
+ */
+void byteReverse(unsigned char *buf, unsigned longs)
+{
+ u32 t;
+ do {
+ t = (u32) ((unsigned) buf[3] << 8 | buf[2]) << 16 |
+ ((unsigned) buf[1] << 8 | buf[0]);
+ *(u32 *) buf = t;
+ buf += 4;
+ } while (--longs);
+}
+#endif
+#endif
+
+/*
+ * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious
+ * initialization constants.
+ */
+void MD5Init(struct MD5Context *ctx)
+{
+ ctx->buf[0] = 0x67452301;
+ ctx->buf[1] = 0xefcdab89;
+ ctx->buf[2] = 0x98badcfe;
+ ctx->buf[3] = 0x10325476;
+
+ ctx->bits[0] = 0;
+ ctx->bits[1] = 0;
+}
+
+/*
+ * Update context to reflect the concatenation of another buffer full
+ * of bytes.
+ */
+void MD5Update(struct MD5Context *ctx, unsigned char const *buf, unsigned len)
+{
+ u32 t;
+
+ /* Update bitcount */
+
+ t = ctx->bits[0];
+ if ((ctx->bits[0] = t + ((u32) len << 3)) < t)
+ ctx->bits[1]++; /* Carry from low to high */
+ ctx->bits[1] += len >> 29;
+
+ t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */
+
+ /* Handle any leading odd-sized chunks */
+
+ if (t) {
+ unsigned char *p = (unsigned char *) ctx->in + t;
+
+ t = 64 - t;
+ if (len < t) {
+ memcpy(p, buf, len);
+ return;
+ }
+ memcpy(p, buf, t);
+ byteReverse(ctx->in, 16);
+ MD5Transform(ctx->buf, (u32 *) ctx->in);
+ buf += t;
+ len -= t;
+ }
+ /* Process data in 64-byte chunks */
+
+ while (len >= 64) {
+ memcpy(ctx->in, buf, 64);
+ byteReverse(ctx->in, 16);
+ MD5Transform(ctx->buf, (u32 *) ctx->in);
+ buf += 64;
+ len -= 64;
+ }
+
+ /* Handle any remaining bytes of data. */
+
+ memcpy(ctx->in, buf, len);
+}
+
+/*
+ * Final wrapup - pad to 64-byte boundary with the bit pattern
+ * 1 0* (64-bit count of bits processed, MSB-first)
+ */
+void MD5Final(unsigned char digest[16], struct MD5Context *ctx)
+{
+ unsigned count;
+ unsigned char *p;
+
+ /* Compute number of bytes mod 64 */
+ count = (ctx->bits[0] >> 3) & 0x3F;
+
+ /* Set the first char of padding to 0x80. This is safe since there is
+ always at least one byte free */
+ p = ctx->in + count;
+ *p++ = 0x80;
+
+ /* Bytes of padding needed to make 64 bytes */
+ count = 64 - 1 - count;
+
+ /* Pad out to 56 mod 64 */
+ if (count < 8) {
+ /* Two lots of padding: Pad the first block to 64 bytes */
+ memset(p, 0, count);
+ byteReverse(ctx->in, 16);
+ MD5Transform(ctx->buf, (u32 *) ctx->in);
+
+ /* Now fill the next block with 56 bytes */
+ memset(ctx->in, 0, 56);
+ } else {
+ /* Pad block to 56 bytes */
+ memset(p, 0, count - 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];
+
+ MD5Transform(ctx->buf, (u32 *) ctx->in);
+ byteReverse((unsigned char *) ctx->buf, 4);
+ memcpy(digest, ctx->buf, 16);
+ memset(ctx, 0, sizeof(ctx)); /* In case it's sensitive */
+}
+
+#ifndef ASM_MD5
+
+/* The four core functions - F1 is optimized somewhat */
+
+/* #define F1(x, y, z) (x & y | ~x & z) */
+#define F1(x, y, z) (z ^ (x & (y ^ z)))
+#define F2(x, y, z) F1(z, x, y)
+#define F3(x, y, z) (x ^ y ^ z)
+#define F4(x, y, z) (y ^ (x | ~z))
+
+/* This is the central step in the MD5 algorithm. */
+#define MD5STEP(f, w, x, y, z, data, s) \
+ ( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x )
+
+/*
+ * The core of the MD5 algorithm, this alters an existing MD5 hash to
+ * reflect the addition of 16 longwords of new data. MD5Update blocks
+ * the data and converts bytes into longwords for this routine.
+ */
+void MD5Transform(u32 buf[4], u32 const in[16])
+{
+ register u32 a, b, c, d;
+
+ a = buf[0];
+ b = buf[1];
+ c = buf[2];
+ d = buf[3];
+
+ MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
+ MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
+ MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
+ MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
+ MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
+ MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
+ MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
+ MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
+ MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
+ MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
+ MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
+ MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
+ MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
+ MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
+ MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
+ MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
+
+ MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
+ MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
+ MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
+ MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
+ MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
+ MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
+ MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
+ MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
+ MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
+ MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
+ MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
+ MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
+ MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
+ MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
+ MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
+ MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
+
+ MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
+ MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
+ MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
+ MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
+ MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
+ MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
+ MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
+ MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
+ MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
+ MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
+ MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
+ MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
+ MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
+ MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
+ MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
+ MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
+
+ MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
+ MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
+ MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
+ MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
+ MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
+ MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
+ MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
+ MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
+ MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
+ MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
+ MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
+ MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
+ MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
+ MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
+ MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
+ MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
+
+ buf[0] += a;
+ buf[1] += b;
+ buf[2] += c;
+ buf[3] += d;
+}
+
+#endif
+/* ===== end - public domain MD5 implementation ===== */
+
+#endif /* !EAP_TLS_FUNCS */
diff --git a/contrib/wpa_supplicant/md5.h b/contrib/wpa_supplicant/md5.h
new file mode 100644
index 000000000000..cc3eb950b507
--- /dev/null
+++ b/contrib/wpa_supplicant/md5.h
@@ -0,0 +1,43 @@
+#ifndef MD5_H
+#define MD5_H
+
+#ifdef EAP_TLS_FUNCS
+
+#include <openssl/md5.h>
+
+#define MD5Init MD5_Init
+#define MD5Update MD5_Update
+#define MD5Final MD5_Final
+#define MD5Transform MD5_Transform
+
+#define MD5_MAC_LEN MD5_DIGEST_LENGTH
+
+#else /* EAP_TLS_FUNCS */
+
+#define MD5_MAC_LEN 16
+
+struct MD5Context {
+ u32 buf[4];
+ u32 bits[2];
+ u8 in[64];
+};
+
+void MD5Init(struct MD5Context *context);
+void MD5Update(struct MD5Context *context, unsigned char const *buf,
+ unsigned len);
+void MD5Final(unsigned char digest[16], struct MD5Context *context);
+void MD5Transform(u32 buf[4], u32 const in[16]);
+
+typedef struct MD5Context MD5_CTX;
+
+#endif /* EAP_TLS_FUNCS */
+
+
+void md5_mac(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
+ u8 *mac);
+void hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem,
+ const u8 *addr[], const size_t *len, u8 *mac);
+void hmac_md5(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
+ u8 *mac);
+
+#endif /* MD5_H */
diff --git a/contrib/wpa_supplicant/ms_funcs.c b/contrib/wpa_supplicant/ms_funcs.c
new file mode 100644
index 000000000000..9c50185ca1d9
--- /dev/null
+++ b/contrib/wpa_supplicant/ms_funcs.c
@@ -0,0 +1,333 @@
+/*
+ * WPA Supplicant / shared MSCHAPV2 helper functions / RFC 2433 / RFC 2759
+ * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published 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 <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "common.h"
+#include "sha1.h"
+#include "ms_funcs.h"
+#include "crypto.h"
+
+
+static void challenge_hash(u8 *peer_challenge, u8 *auth_challenge,
+ u8 *username, size_t username_len,
+ u8 *challenge)
+{
+ u8 hash[SHA1_MAC_LEN];
+ const unsigned char *addr[3];
+ size_t len[3];
+
+ addr[0] = peer_challenge;
+ len[0] = 16;
+ addr[1] = auth_challenge;
+ len[1] = 16;
+ addr[2] = username;
+ len[2] = username_len;
+
+ sha1_vector(3, addr, len, hash);
+ memcpy(challenge, hash, 8);
+}
+
+
+void nt_password_hash(u8 *password, size_t password_len, u8 *password_hash)
+{
+ u8 *buf;
+ int i;
+
+ /* Convert password into unicode */
+ buf = malloc(password_len * 2);
+ if (buf == NULL)
+ return;
+ memset(buf, 0, password_len * 2);
+ for (i = 0; i < password_len; i++)
+ buf[2 * i] = password[i];
+
+ md4(buf, password_len * 2, password_hash);
+ free(buf);
+}
+
+
+void hash_nt_password_hash(u8 *password_hash, u8 *password_hash_hash)
+{
+ md4(password_hash, 16, password_hash_hash);
+}
+
+
+void challenge_response(u8 *challenge, u8 *password_hash, u8 *response)
+{
+ u8 zpwd[7];
+ des_encrypt(challenge, password_hash, response);
+ des_encrypt(challenge, password_hash + 7, response + 8);
+ zpwd[0] = password_hash[14];
+ zpwd[1] = password_hash[15];
+ memset(zpwd + 2, 0, 5);
+ des_encrypt(challenge, zpwd, response + 16);
+}
+
+
+void generate_nt_response(u8 *auth_challenge, u8 *peer_challenge,
+ u8 *username, size_t username_len,
+ u8 *password, size_t password_len,
+ u8 *response)
+{
+ u8 challenge[8];
+ u8 password_hash[16];
+
+ challenge_hash(peer_challenge, auth_challenge, username, username_len,
+ challenge);
+ nt_password_hash(password, password_len, password_hash);
+ challenge_response(challenge, password_hash, response);
+}
+
+
+void generate_authenticator_response(u8 *password, size_t password_len,
+ u8 *peer_challenge,
+ u8 *auth_challenge,
+ u8 *username, size_t username_len,
+ u8 *nt_response, u8 *response)
+{
+ static const u8 magic1[39] = {
+ 0x4D, 0x61, 0x67, 0x69, 0x63, 0x20, 0x73, 0x65, 0x72, 0x76,
+ 0x65, 0x72, 0x20, 0x74, 0x6F, 0x20, 0x63, 0x6C, 0x69, 0x65,
+ 0x6E, 0x74, 0x20, 0x73, 0x69, 0x67, 0x6E, 0x69, 0x6E, 0x67,
+ 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x61, 0x6E, 0x74
+ };
+ static const u8 magic2[41] = {
+ 0x50, 0x61, 0x64, 0x20, 0x74, 0x6F, 0x20, 0x6D, 0x61, 0x6B,
+ 0x65, 0x20, 0x69, 0x74, 0x20, 0x64, 0x6F, 0x20, 0x6D, 0x6F,
+ 0x72, 0x65, 0x20, 0x74, 0x68, 0x61, 0x6E, 0x20, 0x6F, 0x6E,
+ 0x65, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6F,
+ 0x6E
+ };
+
+ u8 password_hash[16], password_hash_hash[16], challenge[8];
+ const unsigned char *addr1[3];
+ const size_t len1[3] = { 16, 24, sizeof(magic1) };
+ const unsigned char *addr2[3];
+ const size_t len2[3] = { SHA1_MAC_LEN, 8, sizeof(magic2) };
+
+ addr1[0] = password_hash_hash;
+ addr1[1] = nt_response;
+ addr1[2] = magic1;
+
+ addr2[0] = response;
+ addr2[1] = challenge;
+ addr2[2] = magic2;
+
+ nt_password_hash(password, password_len, password_hash);
+ hash_nt_password_hash(password_hash, password_hash_hash);
+ sha1_vector(3, addr1, len1, response);
+
+ challenge_hash(peer_challenge, auth_challenge, username, username_len,
+ challenge);
+ sha1_vector(3, addr2, len2, response);
+}
+
+
+void nt_challenge_response(u8 *challenge, u8 *password, size_t password_len,
+ u8 *response)
+{
+ u8 password_hash[16];
+ nt_password_hash(password, password_len, password_hash);
+ challenge_response(challenge, password_hash, response);
+}
+
+
+/* IN: 16-octet password_hash_hash and 24-octet nt_response
+ * OUT: 16-octet master_key */
+void get_master_key(const u8 *password_hash_hash, const u8 *nt_response,
+ u8 *master_key)
+{
+ static const u8 magic1[27] = {
+ 0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74,
+ 0x68, 0x65, 0x20, 0x4d, 0x50, 0x50, 0x45, 0x20, 0x4d,
+ 0x61, 0x73, 0x74, 0x65, 0x72, 0x20, 0x4b, 0x65, 0x79
+ };
+ const unsigned char *addr[3];
+ const size_t len[3] = { 16, 24, sizeof(magic1) };
+
+ addr[0] = password_hash_hash;
+ addr[1] = nt_response;
+ addr[2] = magic1;
+
+ sha1_vector(3, addr, len, master_key);
+}
+
+
+void get_asymetric_start_key(const u8 *master_key, u8 *session_key,
+ size_t session_key_len, int is_send,
+ int is_server)
+{
+ static const u8 magic2[84] = {
+ 0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69,
+ 0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20,
+ 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
+ 0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20, 0x6b, 0x65, 0x79,
+ 0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73,
+ 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73, 0x69, 0x64, 0x65,
+ 0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
+ 0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20,
+ 0x6b, 0x65, 0x79, 0x2e
+ };
+ static const u8 magic3[84] = {
+ 0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69,
+ 0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20,
+ 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
+ 0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20,
+ 0x6b, 0x65, 0x79, 0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68,
+ 0x65, 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73,
+ 0x69, 0x64, 0x65, 0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73,
+ 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20,
+ 0x6b, 0x65, 0x79, 0x2e
+ };
+ static const u8 shs_pad1[40] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+ };
+
+ static const u8 shs_pad2[40] = {
+ 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
+ 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
+ 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
+ 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2
+ };
+ u8 digest[SHA1_MAC_LEN];
+ const unsigned char *addr[4];
+ const size_t len[4] = { 16, 40, 84, 40 };
+
+ addr[0] = master_key;
+ addr[1] = shs_pad1;
+ if (is_send) {
+ addr[2] = is_server ? magic3 : magic2;
+ } else {
+ addr[2] = is_server ? magic2 : magic3;
+ }
+ addr[3] = shs_pad2;
+
+ sha1_vector(4, addr, len, digest);
+
+ if (session_key_len > SHA1_MAC_LEN)
+ session_key_len = SHA1_MAC_LEN;
+ memcpy(session_key, digest, session_key_len);
+}
+
+
+#ifdef TEST_MAIN_MS_FUNCS
+int main(int argc, char *argv[])
+{
+ /* Test vector from RFC2759 example */
+ u8 *username = "User";
+ u8 *password = "clientPass";
+ u8 auth_challenge[] = {
+ 0x5B, 0x5D, 0x7C, 0x7D, 0x7B, 0x3F, 0x2F, 0x3E,
+ 0x3C, 0x2C, 0x60, 0x21, 0x32, 0x26, 0x26, 0x28
+ };
+ u8 peer_challenge[] = {
+ 0x21, 0x40, 0x23, 0x24, 0x25, 0x5E, 0x26, 0x2A,
+ 0x28, 0x29, 0x5F, 0x2B, 0x3A, 0x33, 0x7C, 0x7E
+ };
+ u8 challenge[] = { 0xD0, 0x2E, 0x43, 0x86, 0xBC, 0xE9, 0x12, 0x26 };
+ u8 password_hash[] = {
+ 0x44, 0xEB, 0xBA, 0x8D, 0x53, 0x12, 0xB8, 0xD6,
+ 0x11, 0x47, 0x44, 0x11, 0xF5, 0x69, 0x89, 0xAE
+ };
+ u8 nt_response[] = {
+ 0x82, 0x30, 0x9E, 0xCD, 0x8D, 0x70, 0x8B, 0x5E,
+ 0xA0, 0x8F, 0xAA, 0x39, 0x81, 0xCD, 0x83, 0x54,
+ 0x42, 0x33, 0x11, 0x4A, 0x3D, 0x85, 0xD6, 0xDF
+ };
+ u8 password_hash_hash[] = {
+ 0x41, 0xC0, 0x0C, 0x58, 0x4B, 0xD2, 0xD9, 0x1C,
+ 0x40, 0x17, 0xA2, 0xA1, 0x2F, 0xA5, 0x9F, 0x3F
+ };
+ u8 authenticator_response[] = {
+ 0x40, 0x7A, 0x55, 0x89, 0x11, 0x5F, 0xD0, 0xD6,
+ 0x20, 0x9F, 0x51, 0x0F, 0xE9, 0xC0, 0x45, 0x66,
+ 0x93, 0x2C, 0xDA, 0x56
+ };
+ u8 master_key[] = {
+ 0xFD, 0xEC, 0xE3, 0x71, 0x7A, 0x8C, 0x83, 0x8C,
+ 0xB3, 0x88, 0xE5, 0x27, 0xAE, 0x3C, 0xDD, 0x31
+ };
+ u8 send_start_key[] = {
+ 0x8B, 0x7C, 0xDC, 0x14, 0x9B, 0x99, 0x3A, 0x1B,
+ 0xA1, 0x18, 0xCB, 0x15, 0x3F, 0x56, 0xDC, 0xCB
+ };
+ u8 buf[32];
+
+ int errors = 0;
+
+ printf("Testing ms_funcs.c\n");
+
+ challenge_hash(peer_challenge, auth_challenge,
+ username, strlen(username),
+ buf);
+ if (memcmp(challenge, buf, sizeof(challenge)) != 0) {
+ printf("challenge_hash failed\n");
+ errors++;
+ }
+
+ nt_password_hash(password, strlen(password), buf);
+ if (memcmp(password_hash, buf, sizeof(password_hash)) != 0) {
+ printf("nt_password_hash failed\n");
+ errors++;
+ }
+
+ generate_nt_response(auth_challenge, peer_challenge,
+ username, strlen(username),
+ password, strlen(password),
+ buf);
+ if (memcmp(nt_response, buf, sizeof(nt_response)) != 0) {
+ printf("generate_nt_response failed\n");
+ errors++;
+ }
+
+ hash_nt_password_hash(password_hash, buf);
+ if (memcmp(password_hash_hash, buf, sizeof(password_hash_hash)) != 0) {
+ printf("hash_nt_password_hash failed\n");
+ errors++;
+ }
+
+ generate_authenticator_response(password, strlen(password),
+ peer_challenge, auth_challenge,
+ username, strlen(username),
+ nt_response, buf);
+ if (memcmp(authenticator_response, buf, sizeof(authenticator_response))
+ != 0) {
+ printf("generate_authenticator_response failed\n");
+ errors++;
+ }
+
+ get_master_key(password_hash_hash, nt_response, buf);
+ if (memcmp(master_key, buf, sizeof(master_key)) != 0) {
+ printf("get_master_key failed\n");
+ errors++;
+ }
+
+ get_asymetric_start_key(master_key, buf, sizeof(send_start_key), 1, 1);
+ if (memcmp(send_start_key, buf, sizeof(send_start_key)) != 0) {
+ printf("get_asymetric_start_key failed\n");
+ errors++;
+ }
+
+ if (errors)
+ printf("FAILED! %d errors\n", errors);
+
+ return errors;
+}
+#endif /* TEST_MAIN_MS_FUNCS */
diff --git a/contrib/wpa_supplicant/ms_funcs.h b/contrib/wpa_supplicant/ms_funcs.h
new file mode 100644
index 000000000000..a08ab06d72be
--- /dev/null
+++ b/contrib/wpa_supplicant/ms_funcs.h
@@ -0,0 +1,25 @@
+#ifndef MS_FUNCS_H
+#define MS_FUNCS_H
+
+void generate_nt_response(u8 *auth_challenge, u8 *peer_challenge,
+ u8 *username, size_t username_len,
+ u8 *password, size_t password_len,
+ u8 *response);
+void generate_authenticator_response(u8 *password, size_t password_len,
+ u8 *peer_challenge,
+ u8 *auth_challenge,
+ u8 *username, size_t username_len,
+ u8 *nt_response, u8 *response);
+void nt_challenge_response(u8 *challenge, u8 *password, size_t password_len,
+ u8 *response);
+
+void challenge_response(u8 *challenge, u8 *password_hash, u8 *response);
+void nt_password_hash(u8 *password, size_t password_len, u8 *password_hash);
+void hash_nt_password_hash(u8 *password_hash, u8 *password_hash_hash);
+void get_master_key(const u8 *password_hash_hash, const u8 *nt_response,
+ u8 *master_key);
+void get_asymetric_start_key(const u8 *master_key, u8 *session_key,
+ size_t session_key_len, int is_send,
+ int is_server);
+
+#endif /* MS_FUNCS_H */
diff --git a/contrib/wpa_supplicant/openssl-tls-extensions.patch b/contrib/wpa_supplicant/openssl-tls-extensions.patch
new file mode 100644
index 000000000000..77e9a4132ef0
--- /dev/null
+++ b/contrib/wpa_supplicant/openssl-tls-extensions.patch
@@ -0,0 +1,166 @@
+This is a quick hack for testing EAP-FAST with openssl.
+
+Addition of TLS extensions to ClientHello/ServerHello is more or less
+ok, though not very clean in the way that the caller needs to take
+care of constructing set of all extensions. In addition there is not
+mechanism for reading the TLS extensions, i.e., this would not be
+enough for EAP-FAST authenticator.
+
+Rest of the changes are obviously ugly and/or incorrect for most
+parts, but it demonstrates the minimum set of changes to skip some of
+the error cases that prevented completion of TLS handshake without
+certificates. In other words, this is just a proof-of-concept type of
+example to make it possible to experiment with EAP-FAST. Cleaner patch
+for the needed functionality would be welcome..
+
+
+diff -upr openssl-0.9.7e.orig/include/openssl/ssl.h openssl-0.9.7e/include/openssl/ssl.h
+--- openssl-0.9.7e.orig/include/openssl/ssl.h 2004-07-27 11:28:49.000000000 -0700
++++ openssl-0.9.7e/include/openssl/ssl.h 2004-12-24 20:29:01.000000000 -0800
+@@ -929,6 +929,11 @@ struct ssl_st
+ int first_packet;
+ int client_version; /* what was passed, used for
+ * SSLv3/TLS rollback check */
++
++ /* Optional ClientHello/ServerHello extension to be added to the end
++ * of the SSLv3/TLS hello message. */
++ char *hello_extension;
++ int hello_extension_len;
+ };
+
+ #ifdef __cplusplus
+diff -upr openssl-0.9.7e.orig/ssl/s3_both.c openssl-0.9.7e/ssl/s3_both.c
+--- openssl-0.9.7e.orig/ssl/s3_both.c 2003-02-12 09:05:17.000000000 -0800
++++ openssl-0.9.7e/ssl/s3_both.c 2004-12-31 21:18:15.556846272 -0800
+@@ -199,6 +199,12 @@ int ssl3_get_finished(SSL *s, int a, int
+ 64, /* should actually be 36+4 :-) */
+ &ok);
+
++ if (!ok && s->hello_extension)
++ {
++ /* Quick hack to test EAP-FAST. */
++ return(1);
++ }
++
+ if (!ok) return((int)n);
+
+ /* If this occurs, we have missed a message */
+diff -upr openssl-0.9.7e.orig/ssl/s3_clnt.c openssl-0.9.7e/ssl/s3_clnt.c
+--- openssl-0.9.7e.orig/ssl/s3_clnt.c 2004-05-15 09:39:22.000000000 -0700
++++ openssl-0.9.7e/ssl/s3_clnt.c 2004-12-31 21:16:38.617583280 -0800
+@@ -588,6 +588,12 @@ static int ssl3_client_hello(SSL *s)
+ *(p++)=comp->id;
+ }
+ *(p++)=0; /* Add the NULL method */
++
++ if (s->hello_extension)
++ {
++ memcpy(p,s->hello_extension,s->hello_extension_len);
++ p+=s->hello_extension_len;
++ }
+
+ l=(p-d);
+ d=buf;
+@@ -779,6 +785,11 @@ static int ssl3_get_server_certificate(S
+
+ if (s->s3->tmp.message_type != SSL3_MT_CERTIFICATE)
+ {
++ if (s->hello_extension)
++ {
++ /* Quick hack to test EAP-FAST. */
++ return(1);
++ }
+ al=SSL_AD_UNEXPECTED_MESSAGE;
+ SSLerr(SSL_F_SSL3_GET_SERVER_CERTIFICATE,SSL_R_BAD_MESSAGE_TYPE);
+ goto f_err;
+@@ -951,6 +962,12 @@ static int ssl3_get_key_exchange(SSL *s)
+ DH *dh=NULL;
+ #endif
+
++ if (s->hello_extension)
++ {
++ /* Quick hack to test EAP-FAST. */
++ return(1);
++ }
++
+ /* use same message size as in ssl3_get_certificate_request()
+ * as ServerKeyExchange message may be skipped */
+ n=ssl3_get_message(s,
+@@ -1264,6 +1281,12 @@ static int ssl3_get_certificate_request(
+ unsigned char *p,*d,*q;
+ STACK_OF(X509_NAME) *ca_sk=NULL;
+
++ if (s->hello_extension)
++ {
++ /* Quick hack to test EAP-FAST. */
++ return(1);
++ }
++
+ n=ssl3_get_message(s,
+ SSL3_ST_CR_CERT_REQ_A,
+ SSL3_ST_CR_CERT_REQ_B,
+@@ -1407,6 +1430,12 @@ static int ssl3_get_server_done(SSL *s)
+ int ok,ret=0;
+ long n;
+
++ if (s->hello_extension)
++ {
++ /* Quick hack to test EAP-FAST. */
++ return(1);
++ }
++
+ n=ssl3_get_message(s,
+ SSL3_ST_CR_SRVR_DONE_A,
+ SSL3_ST_CR_SRVR_DONE_B,
+@@ -1439,6 +1468,12 @@ static int ssl3_send_client_key_exchange
+ KSSL_ERR kssl_err;
+ #endif /* OPENSSL_NO_KRB5 */
+
++ if (s->hello_extension)
++ {
++ /* Quick hack to test EAP-FAST. */
++ return(1);
++ }
++
+ if (s->state == SSL3_ST_CW_KEY_EXCH_A)
+ {
+ d=(unsigned char *)s->init_buf->data;
+@@ -1880,6 +1915,12 @@ static int ssl3_check_cert_and_algorithm
+ DH *dh;
+ #endif
+
++ if (s->hello_extension)
++ {
++ /* Quick hack to test EAP-FAST. */
++ return(1);
++ }
++
+ sc=s->session->sess_cert;
+
+ if (sc == NULL)
+diff -upr openssl-0.9.7e.orig/ssl/ssl.h openssl-0.9.7e/ssl/ssl.h
+--- openssl-0.9.7e.orig/ssl/ssl.h 2004-07-27 11:28:49.000000000 -0700
++++ openssl-0.9.7e/ssl/ssl.h 2004-12-24 20:29:01.000000000 -0800
+@@ -929,6 +929,11 @@ struct ssl_st
+ int first_packet;
+ int client_version; /* what was passed, used for
+ * SSLv3/TLS rollback check */
++
++ /* Optional ClientHello/ServerHello extension to be added to the end
++ * of the SSLv3/TLS hello message. */
++ char *hello_extension;
++ int hello_extension_len;
+ };
+
+ #ifdef __cplusplus
+diff -upr openssl-0.9.7e.orig/ssl/ssl_lib.c openssl-0.9.7e/ssl/ssl_lib.c
+--- openssl-0.9.7e.orig/ssl/ssl_lib.c 2004-05-11 05:46:12.000000000 -0700
++++ openssl-0.9.7e/ssl/ssl_lib.c 2004-12-24 20:35:22.000000000 -0800
+@@ -478,6 +478,7 @@ void SSL_free(SSL *s)
+ kssl_ctx_free(s->kssl_ctx);
+ #endif /* OPENSSL_NO_KRB5 */
+
++ OPENSSL_free(s->hello_extension);
+ OPENSSL_free(s);
+ }
+
diff --git a/contrib/wpa_supplicant/pcsc_funcs.c b/contrib/wpa_supplicant/pcsc_funcs.c
new file mode 100644
index 000000000000..541661fb00ed
--- /dev/null
+++ b/contrib/wpa_supplicant/pcsc_funcs.c
@@ -0,0 +1,776 @@
+/*
+ * WPA Supplicant / PC/SC smartcard interface for USIM, GSM SIM
+ * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published 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 <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <winscard.h>
+
+#include "common.h"
+#include "wpa_supplicant.h"
+#include "pcsc_funcs.h"
+
+
+/* See ETSI GSM 11.11 and ETSI TS 102 221 for details.
+ * SIM commands:
+ * Command APDU: CLA INS P1 P2 P3 Data
+ * CLA (class of instruction): A0 for GSM, 00 for USIM
+ * INS (instruction)
+ * P1 P2 P3 (parameters, P3 = length of Data)
+ * Response APDU: Data SW1 SW2
+ * SW1 SW2 (Status words)
+ * Commands (INS P1 P2 P3):
+ * SELECT: A4 00 00 02 <file_id, 2 bytes>
+ * GET RESPONSE: C0 00 00 <len>
+ * RUN GSM ALG: 88 00 00 00 <RAND len = 10>
+ * RUN UMTS ALG: 88 00 81 <len=0x22> data: 0x10 | RAND | 0x10 | AUTN
+ * P1 = ID of alg in card
+ * P2 = ID of secret key
+ * READ BINARY: B0 <offset high> <offset low> <len>
+ * VERIFY CHV: 20 00 <CHV number> 08
+ * CHANGE CHV: 24 00 <CHV number> 10
+ * DISABLE CHV: 26 00 01 08
+ * ENABLE CHV: 28 00 01 08
+ * UNBLOCK CHV: 2C 00 <00=CHV1, 02=CHV2> 10
+ * SLEEP: FA 00 00 00
+ */
+
+/* GSM SIM commands */
+#define SIM_CMD_SELECT 0xa0, 0xa4, 0x00, 0x00, 0x02
+#define SIM_CMD_RUN_GSM_ALG 0xa0, 0x88, 0x00, 0x00, 0x10
+#define SIM_CMD_GET_RESPONSE 0xa0, 0xc0, 0x00, 0x00
+#define SIM_CMD_READ_BIN 0xa0, 0xb0, 0x00, 0x00
+#define SIM_CMD_VERIFY_CHV1 0xa0, 0x20, 0x00, 0x01, 0x08
+
+/* USIM commands */
+#define USIM_CLA 0x00
+#define USIM_CMD_RUN_UMTS_ALG 0x00, 0x88, 0x00, 0x81, 0x22
+#define USIM_CMD_GET_RESPONSE 0x00, 0xc0, 0x00, 0x00
+
+#define USIM_FSP_TEMPL_TAG 0x62
+
+#define USIM_TLV_FILE_DESC 0x82
+#define USIM_TLV_FILE_ID 0x83
+#define USIM_TLV_DF_NAME 0x84
+#define USIM_TLV_PROPR_INFO 0xA5
+#define USIM_TLV_LIFE_CYCLE_STATUS 0x8A
+#define USIM_TLV_FILE_SIZE 0x80
+#define USIM_TLV_TOTAL_FILE_SIZE 0x81
+#define USIM_TLV_PIN_STATUS_TEMPLATE 0xC6
+#define USIM_TLV_SHORT_FILE_ID 0x88
+
+#define USIM_PS_DO_TAG 0x90
+
+#define AKA_RAND_LEN 16
+#define AKA_AUTN_LEN 16
+#define AKA_AUTS_LEN 14
+#define RES_MAX_LEN 16
+#define IK_LEN 16
+#define CK_LEN 16
+
+
+typedef enum { SCARD_GSM_SIM, SCARD_USIM } sim_types;
+
+struct scard_data {
+ long ctx;
+ long card;
+ unsigned long protocol;
+ SCARD_IO_REQUEST recv_pci;
+ sim_types sim_type;
+};
+
+
+static int _scard_select_file(struct scard_data *scard, unsigned short file_id,
+ unsigned char *buf, size_t *buf_len,
+ sim_types sim_type, unsigned char *aid);
+static int scard_select_file(struct scard_data *scard, unsigned short file_id,
+ unsigned char *buf, size_t *buf_len);
+static int scard_verify_pin(struct scard_data *scard, char *pin);
+
+
+static int scard_parse_fsp_templ(unsigned char *buf, size_t buf_len,
+ int *ps_do, int *file_len)
+{
+ 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 (pos[0] == USIM_TLV_FILE_SIZE &&
+ (pos[1] == 1 || pos[1] == 2) && file_len) {
+ if (pos[1] == 1)
+ *file_len = (int) pos[2];
+ else
+ *file_len = ((int) pos[2] << 8) |
+ (int) pos[3];
+ 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 &&
+ pos[3] >= 1 && ps_do) {
+ wpa_printf(MSG_DEBUG, "SCARD: PS_DO=0x%02x",
+ pos[4]);
+ *ps_do = (int) pos[4];
+ }
+
+ pos += 2 + pos[1];
+
+ if (pos == end)
+ return 0;
+ }
+ return -1;
+}
+
+
+static int scard_pin_needed(struct scard_data *scard,
+ unsigned char *hdr, size_t hlen)
+{
+ if (scard->sim_type == SCARD_GSM_SIM) {
+ if (hlen > SCARD_CHV1_OFFSET &&
+ !(hdr[SCARD_CHV1_OFFSET] & SCARD_CHV1_FLAG))
+ return 1;
+ return 0;
+ }
+
+ if (scard->sim_type == SCARD_USIM) {
+ int ps_do;
+ if (scard_parse_fsp_templ(hdr, hlen, &ps_do, NULL))
+ return -1;
+ /* TODO: there could be more than one PS_DO entry because of
+ * multiple PINs in key reference.. */
+ if (ps_do)
+ return 1;
+ }
+
+ return -1;
+}
+
+
+struct scard_data * scard_init(scard_sim_type sim_type, char *pin)
+{
+ long ret, len;
+ struct scard_data *scard;
+ char *readers = NULL;
+ char buf[100];
+ size_t blen;
+
+ wpa_printf(MSG_DEBUG, "SCARD: initializing smart card interface");
+ scard = malloc(sizeof(*scard));
+ if (scard == NULL)
+ return NULL;
+ memset(scard, 0, sizeof(*scard));
+
+ ret = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL,
+ &scard->ctx);
+ if (ret != SCARD_S_SUCCESS) {
+ wpa_printf(MSG_DEBUG, "SCARD: Could not establish smart card "
+ "context (err=%ld)", ret);
+ goto failed;
+ }
+
+ ret = SCardListReaders(scard->ctx, NULL, NULL, &len);
+ if (ret != SCARD_S_SUCCESS) {
+ wpa_printf(MSG_DEBUG, "SCARD: SCardListReaders failed "
+ "(err=%ld)", ret);
+ goto failed;
+ }
+
+ readers = malloc(len);
+ if (readers == NULL) {
+ printf("malloc failed\n");
+ goto failed;
+ }
+
+ ret = SCardListReaders(scard->ctx, NULL, readers, &len);
+ if (ret != SCARD_S_SUCCESS) {
+ wpa_printf(MSG_DEBUG, "SCARD: SCardListReaders failed(2) "
+ "(err=%ld)", ret);
+ goto failed;
+ }
+ if (len < 3) {
+ wpa_printf(MSG_WARNING, "SCARD: No smart card readers "
+ "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_printf(MSG_DEBUG, "SCARD: Selected reader='%s'", readers);
+
+ ret = SCardConnect(scard->ctx, readers, SCARD_SHARE_SHARED,
+ SCARD_PROTOCOL_T0, &scard->card, &scard->protocol);
+ if (ret != SCARD_S_SUCCESS) {
+ if (ret == SCARD_E_NO_SMARTCARD)
+ wpa_printf(MSG_INFO, "No smart card inserted.");
+ else
+ wpa_printf(MSG_WARNING, "SCardConnect err=%lx", ret);
+ goto failed;
+ }
+
+ free(readers);
+ readers = NULL;
+
+ wpa_printf(MSG_DEBUG, "SCARD: card=%ld active_protocol=%lu",
+ scard->card, scard->protocol);
+
+ 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)) {
+ 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;
+ }
+ }
+
+ if (scard->sim_type == SCARD_GSM_SIM) {
+ blen = sizeof(buf);
+ if (scard_select_file(scard, SCARD_FILE_MF, buf, &blen)) {
+ wpa_printf(MSG_DEBUG, "SCARD: Failed to read MF");
+ goto failed;
+ }
+
+ blen = sizeof(buf);
+ if (scard_select_file(scard, SCARD_FILE_GSM_DF, buf, &blen)) {
+ wpa_printf(MSG_DEBUG, "SCARD: Failed to read GSM DF");
+ goto failed;
+ }
+ } else {
+ /* Select based on AID = 3G RID */
+ blen = sizeof(buf);
+ if (_scard_select_file(scard, 0, buf, &blen, scard->sim_type,
+ "\xA0\x00\x00\x00\x87")) {
+ wpa_printf(MSG_DEBUG, "SCARD: Failed to read 3G RID "
+ "AID");
+ goto failed;
+ }
+ }
+
+ /* Verify whether CHV1 (PIN1) is needed to access the card. */
+ if (scard_pin_needed(scard, buf, blen)) {
+ wpa_printf(MSG_DEBUG, "PIN1 needed for SIM access");
+ if (pin == NULL) {
+ wpa_printf(MSG_INFO, "No PIN configured for SIM "
+ "access");
+ /* TODO: ask PIN from user through a frontend (e.g.,
+ * wpa_cli) */
+ goto failed;
+ }
+ if (scard_verify_pin(scard, pin)) {
+ wpa_printf(MSG_INFO, "PIN verification failed for "
+ "SIM access");
+ /* TODO: what to do? */
+ goto failed;
+ }
+ }
+
+ return scard;
+
+failed:
+ free(readers);
+ scard_deinit(scard);
+ return NULL;
+}
+
+
+void scard_deinit(struct scard_data *scard)
+{
+ long ret;
+
+ if (scard == NULL)
+ return;
+
+ wpa_printf(MSG_DEBUG, "SCARD: deinitializing smart card interface");
+ if (scard->card) {
+ ret = SCardDisconnect(scard->card, SCARD_UNPOWER_CARD);
+ if (ret != SCARD_S_SUCCESS) {
+ wpa_printf(MSG_DEBUG, "SCARD: Failed to disconnect "
+ "smart card (err=%ld)", ret);
+ }
+ }
+
+ if (scard->ctx) {
+ ret = SCardReleaseContext(scard->ctx);
+ if (ret != SCARD_S_SUCCESS) {
+ wpa_printf(MSG_DEBUG, "Failed to release smart card "
+ "context (err=%ld)", ret);
+ }
+ }
+ free(scard);
+}
+
+
+static long scard_transmit(struct scard_data *scard,
+ unsigned char *send, size_t send_len,
+ unsigned char *recv, size_t *recv_len)
+{
+ long ret;
+ unsigned long rlen;
+
+ wpa_hexdump_key(MSG_DEBUG, "SCARD: scard_transmit: send",
+ send, send_len);
+ rlen = *recv_len;
+ ret = SCardTransmit(scard->card,
+ scard->protocol == SCARD_PROTOCOL_T1 ?
+ SCARD_PCI_T1 : SCARD_PCI_T0,
+ send, (unsigned long) send_len,
+ &scard->recv_pci, recv, &rlen);
+ *recv_len = rlen;
+ if (ret == SCARD_S_SUCCESS) {
+ wpa_hexdump(MSG_DEBUG, "SCARD: scard_transmit: recv",
+ recv, rlen);
+ } else {
+ wpa_printf(MSG_WARNING, "SCARD: SCardTransmit failed "
+ "(err=0x%lx)", ret);
+ }
+ return ret;
+}
+
+
+static int _scard_select_file(struct scard_data *scard, unsigned short file_id,
+ unsigned char *buf, size_t *buf_len,
+ sim_types sim_type, unsigned char *aid)
+{
+ long ret;
+ unsigned char resp[3];
+ unsigned char cmd[10] = { SIM_CMD_SELECT };
+ int cmdlen;
+ unsigned char get_resp[5] = { SIM_CMD_GET_RESPONSE };
+ size_t len, rlen;
+
+ if (sim_type == SCARD_USIM) {
+ cmd[0] = USIM_CLA;
+ cmd[3] = 0x04;
+ get_resp[0] = USIM_CLA;
+ }
+
+ wpa_printf(MSG_DEBUG, "SCARD: select file %04x", file_id);
+ if (aid) {
+ cmd[2] = 0x04; /* Select by AID */
+ cmd[4] = 5; /* len */
+ memcpy(cmd + 5, aid, 5);
+ cmdlen = 10;
+ } else {
+ cmd[5] = file_id >> 8;
+ cmd[6] = file_id & 0xff;
+ cmdlen = 7;
+ }
+ len = sizeof(resp);
+ ret = scard_transmit(scard, cmd, cmdlen, resp, &len);
+ if (ret != SCARD_S_SUCCESS) {
+ wpa_printf(MSG_WARNING, "SCARD: SCardTransmit failed "
+ "(err=0x%lx)", ret);
+ return -1;
+ }
+
+ if (len != 2) {
+ wpa_printf(MSG_WARNING, "SCARD: unexpected resp len "
+ "%d (expected 2)", (int) len);
+ return -1;
+ }
+
+ if (resp[0] == 0x98 && resp[1] == 0x04) {
+ /* Security status not satisfied (PIN_WLAN) */
+ wpa_printf(MSG_WARNING, "SCARD: Security status not satisfied "
+ "(PIN_WLAN)");
+ return -1;
+ }
+
+ if (resp[0] == 0x6e) {
+ wpa_printf(MSG_DEBUG, "SCARD: used CLA not supported");
+ return -1;
+ }
+
+ if (resp[0] != 0x6c && resp[0] != 0x9f && resp[0] != 0x61) {
+ wpa_printf(MSG_WARNING, "SCARD: unexpected response 0x%02x "
+ "(expected 0x61, 0x6c, or 0x9f)", resp[0]);
+ return -1;
+ }
+ /* Normal ending of command; resp[1] bytes available */
+ get_resp[4] = resp[1];
+ wpa_printf(MSG_DEBUG, "SCARD: trying to get response (%d bytes)",
+ resp[1]);
+
+ rlen = *buf_len;
+ ret = scard_transmit(scard, get_resp, sizeof(get_resp), buf, &rlen);
+ if (ret == SCARD_S_SUCCESS) {
+ *buf_len = resp[1] < rlen ? resp[1] : rlen;
+ return 0;
+ }
+
+ wpa_printf(MSG_WARNING, "SCARD: SCardTransmit err=0x%lx\n", ret);
+ return -1;
+}
+
+
+static int scard_select_file(struct scard_data *scard, unsigned short file_id,
+ unsigned char *buf, size_t *buf_len)
+{
+ return _scard_select_file(scard, file_id, buf, buf_len,
+ scard->sim_type, NULL);
+}
+
+
+static int scard_read_file(struct scard_data *scard,
+ unsigned char *data, size_t len)
+{
+ char cmd[5] = { SIM_CMD_READ_BIN, len };
+ size_t blen = len + 3;
+ unsigned char *buf;
+ long ret;
+
+ buf = malloc(blen);
+ if (buf == NULL)
+ return -1;
+
+ if (scard->sim_type == SCARD_USIM)
+ cmd[0] = USIM_CLA;
+ ret = scard_transmit(scard, cmd, sizeof(cmd), buf, &blen);
+ if (ret != SCARD_S_SUCCESS) {
+ free(buf);
+ return -2;
+ }
+ if (blen != len + 2) {
+ wpa_printf(MSG_DEBUG, "SCARD: file read returned unexpected "
+ "length %d (expected %d)", blen, len + 2);
+ free(buf);
+ return -3;
+ }
+
+ if (buf[len] != 0x90 || buf[len + 1] != 0x00) {
+ wpa_printf(MSG_DEBUG, "SCARD: file read returned unexpected "
+ "status %02x %02x (expected 90 00)",
+ buf[len], buf[len + 1]);
+ free(buf);
+ return -4;
+ }
+
+ memcpy(data, buf, len);
+ free(buf);
+
+ return 0;
+}
+
+
+static int scard_verify_pin(struct scard_data *scard, char *pin)
+{
+ long ret;
+ unsigned char resp[3];
+ char cmd[5 + 8] = { SIM_CMD_VERIFY_CHV1 };
+ size_t len;
+
+ wpa_printf(MSG_DEBUG, "SCARD: verifying PIN");
+
+ if (pin == NULL || strlen(pin) > 8)
+ return -1;
+
+ if (scard->sim_type == SCARD_USIM)
+ cmd[0] = USIM_CLA;
+ memcpy(cmd + 5, pin, strlen(pin));
+ memset(cmd + 5 + strlen(pin), 0xff, 8 - strlen(pin));
+
+ len = sizeof(resp);
+ ret = scard_transmit(scard, cmd, sizeof(cmd), resp, &len);
+ if (ret != SCARD_S_SUCCESS)
+ return -2;
+
+ if (len != 2 || resp[0] != 0x90 || resp[1] != 0x00) {
+ wpa_printf(MSG_WARNING, "SCARD: PIN verification failed");
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "SCARD: PIN verified successfully");
+ return 0;
+}
+
+
+int scard_get_imsi(struct scard_data *scard, char *imsi, size_t *len)
+{
+ char buf[100];
+ size_t blen, imsilen;
+ char *pos;
+ int i;
+
+ wpa_printf(MSG_DEBUG, "SCARD: reading IMSI from (GSM) EF-IMSI");
+ blen = sizeof(buf);
+ if (scard_select_file(scard, SCARD_FILE_GSM_EF_IMSI, buf, &blen))
+ return -1;
+ if (blen < 4) {
+ wpa_printf(MSG_WARNING, "SCARD: too short (GSM) EF-IMSI "
+ "header (len=%d)", blen);
+ return -2;
+ }
+
+ if (scard->sim_type == SCARD_GSM_SIM) {
+ blen = (buf[2] << 8) | buf[3];
+ } else {
+ int file_size;
+ if (scard_parse_fsp_templ(buf, blen, NULL, &file_size))
+ return -3;
+ blen = file_size;
+ }
+ if (blen < 2 || blen > sizeof(buf)) {
+ wpa_printf(MSG_DEBUG, "SCARD: invalid IMSI file length=%d",
+ blen);
+ return -3;
+ }
+
+ imsilen = (blen - 2) * 2 + 1;
+ wpa_printf(MSG_DEBUG, "SCARD: IMSI file length=%d imsilen=%d",
+ blen, imsilen);
+ if (blen < 2 || imsilen > *len) {
+ *len = imsilen;
+ return -4;
+ }
+
+ if (scard_read_file(scard, buf, blen))
+ return -5;
+
+ pos = imsi;
+ *pos++ = '0' + (buf[1] >> 4 & 0x0f);
+ for (i = 2; i < blen; i++) {
+ unsigned char digit;
+
+ digit = buf[i] & 0x0f;
+ if (digit < 10)
+ *pos++ = '0' + digit;
+ else
+ imsilen--;
+
+ digit = buf[i] >> 4 & 0x0f;
+ if (digit < 10)
+ *pos++ = '0' + digit;
+ else
+ imsilen--;
+ }
+ *len = imsilen;
+
+ return 0;
+}
+
+
+int scard_gsm_auth(struct scard_data *scard, unsigned char *rand,
+ unsigned char *sres, unsigned char *kc)
+{
+ unsigned char cmd[5 + 1 + 16] = { SIM_CMD_RUN_GSM_ALG };
+ int cmdlen;
+ unsigned char get_resp[5] = { SIM_CMD_GET_RESPONSE };
+ unsigned char resp[3], buf[12 + 3 + 2];
+ size_t len;
+ long ret;
+
+ if (scard == NULL)
+ return -1;
+
+ wpa_hexdump(MSG_DEBUG, "SCARD: GSM auth - RAND", rand, 16);
+ if (scard->sim_type == SCARD_GSM_SIM) {
+ cmdlen = 5 + 16;
+ memcpy(cmd + 5, rand, 16);
+ } else {
+ cmdlen = 5 + 1 + 16;
+ cmd[0] = USIM_CLA;
+ cmd[3] = 0x80;
+ cmd[4] = 17;
+ cmd[5] = 16;
+ memcpy(cmd + 6, rand, 16);
+ }
+ len = sizeof(resp);
+ ret = scard_transmit(scard, cmd, sizeof(cmd), resp, &len);
+ if (ret != SCARD_S_SUCCESS)
+ return -2;
+
+ if ((scard->sim_type == SCARD_GSM_SIM &&
+ (len != 2 || resp[0] != 0x9f || resp[1] != 0x0c)) ||
+ (scard->sim_type == SCARD_USIM &&
+ (len != 2 || resp[0] != 0x61 || resp[1] != 0x0e))) {
+ wpa_printf(MSG_WARNING, "SCARD: unexpected response for GSM "
+ "auth request (len=%d resp=%02x %02x)",
+ len, resp[0], resp[1]);
+ return -3;
+ }
+ get_resp[4] = resp[1];
+
+ len = sizeof(buf);
+ ret = scard_transmit(scard, get_resp, sizeof(get_resp), buf, &len);
+ if (ret != SCARD_S_SUCCESS)
+ return -4;
+
+ if (scard->sim_type == SCARD_GSM_SIM) {
+ if (len != 4 + 8 + 2) {
+ wpa_printf(MSG_WARNING, "SCARD: unexpected data "
+ "length for GSM auth (len=%d, expected 14)",
+ len);
+ return -5;
+ }
+ memcpy(sres, buf, 4);
+ memcpy(kc, buf + 4, 8);
+ } else {
+ if (len != 1 + 4 + 1 + 8 + 2) {
+ wpa_printf(MSG_WARNING, "SCARD: unexpected data "
+ "length for USIM auth (len=%d, "
+ "expected 16)", len);
+ return -5;
+ }
+ if (buf[0] != 4 || buf[5] != 8) {
+ wpa_printf(MSG_WARNING, "SCARD: unexpected SREC/Kc "
+ "length (%d %d, expected 4 8)",
+ buf[0], buf[5]);
+ }
+ memcpy(sres, buf + 1, 4);
+ memcpy(kc, buf + 6, 8);
+ }
+
+ wpa_hexdump(MSG_DEBUG, "SCARD: GSM auth - SRES", sres, 4);
+ wpa_hexdump(MSG_DEBUG, "SCARD: GSM auth - Kc", kc, 8);
+
+ return 0;
+}
+
+
+int scard_umts_auth(struct scard_data *scard, unsigned char *rand,
+ unsigned char *autn, unsigned char *res, size_t *res_len,
+ unsigned char *ik, unsigned char *ck, unsigned char *auts)
+{
+ unsigned char cmd[5 + 1 + AKA_RAND_LEN + 1 + AKA_AUTN_LEN] =
+ { USIM_CMD_RUN_UMTS_ALG };
+ int cmdlen;
+ unsigned char get_resp[5] = { USIM_CMD_GET_RESPONSE };
+ unsigned char resp[3], buf[64], *pos, *end;
+ size_t len;
+ long ret;
+
+ if (scard == NULL)
+ return -1;
+
+ if (scard->sim_type == SCARD_GSM_SIM) {
+ wpa_printf(MSG_ERROR, "SCARD: Non-USIM card - cannot do UMTS "
+ "auth");
+ return -1;
+ }
+
+ wpa_hexdump(MSG_DEBUG, "SCARD: UMTS auth - RAND", rand, AKA_RAND_LEN);
+ wpa_hexdump(MSG_DEBUG, "SCARD: UMTS auth - AUTN", autn, AKA_AUTN_LEN);
+ cmdlen = 5 + 1 + AKA_RAND_LEN + 1 + AKA_AUTN_LEN;
+ cmd[5] = AKA_RAND_LEN;
+ memcpy(cmd + 6, rand, AKA_RAND_LEN);
+ cmd[6 + AKA_RAND_LEN] = AKA_AUTN_LEN;
+ memcpy(cmd + 6 + AKA_RAND_LEN + 1, autn, AKA_AUTN_LEN);
+
+ len = sizeof(resp);
+ ret = scard_transmit(scard, cmd, sizeof(cmd), resp, &len);
+ if (ret != SCARD_S_SUCCESS)
+ return -1;
+
+ if (len >= 0 && len <= sizeof(resp))
+ wpa_hexdump(MSG_DEBUG, "SCARD: UMTS alg response", resp, len);
+
+ if (len == 2 && resp[0] == 0x98 && resp[1] == 0x62) {
+ wpa_printf(MSG_WARNING, "SCARD: UMTS auth failed - "
+ "MAC != XMAC");
+ return -1;
+ } else if (len != 2 || resp[0] != 0x61) {
+ wpa_printf(MSG_WARNING, "SCARD: unexpected response for UMTS "
+ "auth request (len=%d resp=%02x %02x)",
+ len, resp[0], resp[1]);
+ return -1;
+ }
+ get_resp[4] = resp[1];
+
+ len = sizeof(buf);
+ ret = scard_transmit(scard, get_resp, sizeof(get_resp), buf, &len);
+ if (ret != SCARD_S_SUCCESS || len < 0 || len > sizeof(buf))
+ return -1;
+
+ wpa_hexdump(MSG_DEBUG, "SCARD: UMTS get response result", buf, len);
+ if (len >= 2 + AKA_AUTS_LEN && buf[0] == 0xdc &&
+ buf[1] == AKA_AUTS_LEN) {
+ wpa_printf(MSG_DEBUG, "SCARD: UMTS Synchronization-Failure");
+ memcpy(auts, buf + 2, AKA_AUTS_LEN);
+ wpa_hexdump(MSG_DEBUG, "SCARD: AUTS", auts, AKA_AUTS_LEN);
+ return -2;
+ } else if (len >= 6 + IK_LEN + CK_LEN && buf[0] == 0xdb) {
+ pos = buf + 1;
+ end = buf + len;
+
+ /* RES */
+ if (pos[0] > RES_MAX_LEN || pos + pos[0] > end) {
+ wpa_printf(MSG_DEBUG, "SCARD: Invalid RES");
+ return -1;
+ }
+ *res_len = *pos++;
+ memcpy(res, pos, *res_len);
+ pos += *res_len;
+ wpa_hexdump(MSG_DEBUG, "SCARD: RES", res, *res_len);
+
+ /* CK */
+ if (pos[0] != CK_LEN || pos + CK_LEN > end) {
+ wpa_printf(MSG_DEBUG, "SCARD: Invalid CK");
+ return -1;
+ }
+ pos++;
+ memcpy(ck, pos, CK_LEN);
+ pos += CK_LEN;
+ wpa_hexdump(MSG_DEBUG, "SCARD: CK", ck, CK_LEN);
+
+ /* IK */
+ if (pos[0] != IK_LEN || pos + IK_LEN > end) {
+ wpa_printf(MSG_DEBUG, "SCARD: Invalid IK");
+ return -1;
+ }
+ pos++;
+ memcpy(ik, pos, IK_LEN);
+ pos += IK_LEN;
+ wpa_hexdump(MSG_DEBUG, "SCARD: IK", ik, IK_LEN);
+
+ return 0;
+ }
+
+ wpa_printf(MSG_DEBUG, "SCARD: Unrecognized response");
+ return -1;
+}
diff --git a/contrib/wpa_supplicant/pcsc_funcs.h b/contrib/wpa_supplicant/pcsc_funcs.h
new file mode 100644
index 000000000000..1b64fa279441
--- /dev/null
+++ b/contrib/wpa_supplicant/pcsc_funcs.h
@@ -0,0 +1,50 @@
+#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_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, char *pin);
+void scard_deinit(struct scard_data *scard);
+
+int scard_get_imsi(struct scard_data *scard, char *imsi, size_t *len);
+int scard_gsm_auth(struct scard_data *scard, unsigned char *rand,
+ unsigned char *sres, unsigned char *kc);
+int scard_umts_auth(struct scard_data *scard, unsigned char *rand,
+ unsigned char *autn, unsigned char *res, size_t *res_len,
+ unsigned char *ik, unsigned char *ck, unsigned char *auts);
+
+#else /* PCSC_FUNCS */
+
+#define scard_init(s, p) NULL
+#define scard_deinit(s) do { } while (0)
+#define scard_get_imsi(s, i, l) -1
+#define scard_gsm_auth(s, r, s2, k) -1
+#define scard_umts_auth(s, r, a, r2, rl, i, c, a2) -1
+
+#endif /* PCSC_FUNCS */
+
+#endif /* PCSC_FUNCS_H */
diff --git a/contrib/wpa_supplicant/preauth_test.c b/contrib/wpa_supplicant/preauth_test.c
new file mode 100644
index 000000000000..13741bbaa367
--- /dev/null
+++ b/contrib/wpa_supplicant/preauth_test.c
@@ -0,0 +1,396 @@
+/*
+ * WPA Supplicant - test code for pre-authentication
+ * Copyright (c) 2003-2005, Jouni Malinen <jkmaline@cc.hut.fi>
+ *
+ * IEEE 802.1X Supplicant test code (to be used in place of wpa_supplicant.c.
+ * Not used in production version.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <ctype.h>
+#include <string.h>
+#include <signal.h>
+#include <netinet/in.h>
+#include <assert.h>
+#include <arpa/inet.h>
+
+#include "common.h"
+#include "config.h"
+#include "eapol_sm.h"
+#include "eloop.h"
+#include "wpa.h"
+#include "eap.h"
+#include "wpa_supplicant.h"
+#include "wpa_supplicant_i.h"
+#include "l2_packet.h"
+#include "ctrl_iface.h"
+#include "pcsc_funcs.h"
+
+
+extern int wpa_debug_level;
+extern int wpa_debug_show_keys;
+
+void wpa_msg(struct wpa_supplicant *wpa_s, int level, char *fmt, ...)
+{
+ va_list ap;
+ char *buf;
+ const int buflen = 2048;
+ int len;
+
+ buf = malloc(buflen);
+ if (buf == NULL) {
+ printf("Failed to allocate message buffer for:\n");
+ va_start(ap, fmt);
+ vprintf(fmt, ap);
+ printf("\n");
+ va_end(ap);
+ return;
+ }
+ va_start(ap, fmt);
+ len = vsnprintf(buf, buflen, fmt, ap);
+ va_end(ap);
+ wpa_printf(level, "%s", buf);
+ wpa_supplicant_ctrl_iface_send(wpa_s, level, buf, len);
+ free(buf);
+}
+
+
+void wpa_supplicant_event(struct wpa_supplicant *wpa_s, wpa_event_type event,
+ union wpa_event_data *data)
+{
+}
+
+
+int rsn_preauth_init(struct wpa_supplicant *wpa_s, u8 *dst)
+{
+ return -1;
+}
+
+
+void rsn_preauth_deinit(struct wpa_supplicant *wpa_s)
+{
+}
+
+
+int pmksa_cache_list(struct wpa_supplicant *wpa_s, char *buf, size_t len)
+{
+ return 0;
+}
+
+
+int wpa_get_mib(struct wpa_supplicant *wpa_s, char *buf, size_t buflen)
+{
+ return 0;
+}
+
+
+void wpa_supplicant_req_scan(struct wpa_supplicant *wpa_s, int sec, int usec)
+{
+}
+
+
+const char * wpa_ssid_txt(u8 *ssid, size_t ssid_len)
+{
+ return NULL;
+}
+
+
+int wpa_supplicant_reload_configuration(struct wpa_supplicant *wpa_s)
+{
+ return -1;
+}
+
+
+static int eapol_test_eapol_send(void *ctx, int type, u8 *buf, size_t len)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+ u8 *msg;
+ size_t msglen;
+ struct l2_ethhdr *ethhdr;
+ struct ieee802_1x_hdr *hdr;
+ int res;
+
+ printf("WPA: wpa_eapol_send(type=%d len=%d)\n", type, len);
+
+ if (wpa_s->l2_preauth == NULL)
+ return -1;
+
+ msglen = sizeof(*ethhdr) + sizeof(*hdr) + len;
+ msg = malloc(msglen);
+ if (msg == NULL)
+ return -1;
+
+ ethhdr = (struct l2_ethhdr *) msg;
+ memcpy(ethhdr->h_dest, wpa_s->preauth_bssid, ETH_ALEN);
+ memcpy(ethhdr->h_source, wpa_s->own_addr, ETH_ALEN);
+ ethhdr->h_proto = htons(ETH_P_RSN_PREAUTH);
+
+ hdr = (struct ieee802_1x_hdr *) (ethhdr + 1);
+ hdr->version = wpa_s->conf->eapol_version;
+ hdr->type = type;
+ hdr->length = htons(len);
+
+ memcpy((u8 *) (hdr + 1), buf, len);
+
+ wpa_hexdump(MSG_MSGDUMP, "TX EAPOL (preauth)", msg, msglen);
+ res = l2_packet_send(wpa_s->l2_preauth, msg, msglen);
+ free(msg);
+ return res;
+}
+
+
+static void eapol_test_eapol_done_cb(void *ctx)
+{
+ printf("WPA: EAPOL processing complete\n");
+}
+
+
+static void eapol_sm_cb(struct eapol_sm *eapol, int success, void *ctx)
+{
+ printf("eapol_sm_cb: success=%d\n", success);
+ eloop_terminate();
+}
+
+
+static int test_eapol(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid)
+{
+ struct eapol_config eapol_conf;
+ struct eapol_ctx *ctx;
+
+ ctx = malloc(sizeof(*ctx));
+ if (ctx == NULL) {
+ printf("Failed to allocate EAPOL context.\n");
+ return -1;
+ }
+ memset(ctx, 0, sizeof(*ctx));
+ ctx->ctx = wpa_s;
+ ctx->msg_ctx = wpa_s;
+ ctx->scard_ctx = wpa_s->scard;
+ ctx->cb = eapol_sm_cb;
+ ctx->cb_ctx = wpa_s;
+ ctx->preauth = 0;
+ ctx->eapol_done_cb = eapol_test_eapol_done_cb;
+ ctx->eapol_send = eapol_test_eapol_send;
+
+ wpa_s->preauth_eapol = eapol_sm_init(ctx);
+ if (wpa_s->preauth_eapol == NULL) {
+ free(ctx);
+ printf("Failed to initialize EAPOL state machines.\n");
+ return -1;
+ }
+
+ wpa_s->current_ssid = ssid;
+ memset(&eapol_conf, 0, sizeof(eapol_conf));
+ eapol_conf.accept_802_1x_keys = 1;
+ eapol_conf.required_keys = 0;
+ eapol_conf.workaround = ssid->eap_workaround;
+ eapol_sm_notify_config(wpa_s->preauth_eapol, ssid, &eapol_conf);
+
+
+ eapol_sm_notify_portValid(wpa_s->preauth_eapol, FALSE);
+ /* 802.1X::portControl = Auto */
+ eapol_sm_notify_portEnabled(wpa_s->preauth_eapol, TRUE);
+
+ return 0;
+}
+
+
+static void test_eapol_clean(struct wpa_supplicant *wpa_s)
+{
+ l2_packet_deinit(wpa_s->l2_preauth);
+ eapol_sm_deinit(wpa_s->preauth_eapol);
+ wpa_s->preauth_eapol = NULL;
+ scard_deinit(wpa_s->scard);
+ wpa_supplicant_ctrl_iface_deinit(wpa_s);
+ wpa_config_free(wpa_s->conf);
+}
+
+
+static void eapol_test_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_supplicant *wpa_s = eloop_ctx;
+ printf("EAPOL test timed out\n");
+ wpa_s->auth_timed_out = 1;
+ eloop_terminate();
+}
+
+
+static void wpa_supplicant_imsi_identity(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid)
+{
+ if (ssid->identity == NULL && wpa_s->imsi) {
+ ssid->identity = malloc(1 + wpa_s->imsi_len);
+ if (ssid->identity) {
+ ssid->identity[0] = '1';
+ memcpy(ssid->identity + 1, wpa_s->imsi,
+ wpa_s->imsi_len);
+ ssid->identity_len = 1 + wpa_s->imsi_len;
+ wpa_hexdump_ascii(MSG_DEBUG, "permanent identity from "
+ "IMSI", ssid->identity,
+ ssid->identity_len);
+ }
+ }
+}
+
+
+static void wpa_supplicant_scard_init(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid)
+{
+ char buf[100];
+ size_t len;
+
+ if (ssid->pcsc == NULL)
+ return;
+ if (wpa_s->scard != NULL) {
+ wpa_supplicant_imsi_identity(wpa_s, ssid);
+ return;
+ }
+ wpa_printf(MSG_DEBUG, "Selected network is configured to use SIM - "
+ "initialize PCSC");
+ wpa_s->scard = scard_init(SCARD_TRY_BOTH, ssid->pin);
+ if (wpa_s->scard == NULL) {
+ wpa_printf(MSG_WARNING, "Failed to initialize SIM "
+ "(pcsc-lite)");
+ /* TODO: what to do here? */
+ return;
+ }
+ eapol_sm_register_scard_ctx(wpa_s->preauth_eapol, wpa_s->scard);
+
+ len = sizeof(buf);
+ if (scard_get_imsi(wpa_s->scard, buf, &len)) {
+ wpa_printf(MSG_WARNING, "Failed to get IMSI from SIM");
+ /* TODO: what to do here? */
+ return;
+ }
+
+ wpa_hexdump(MSG_DEBUG, "IMSI", (u8 *) buf, len);
+ free(wpa_s->imsi);
+ wpa_s->imsi = malloc(len);
+ if (wpa_s->imsi) {
+ memcpy(wpa_s->imsi, buf, len);
+ wpa_s->imsi_len = len;
+ wpa_supplicant_imsi_identity(wpa_s, ssid);
+ }
+}
+
+
+static void rsn_preauth_receive(void *ctx, unsigned char *src_addr,
+ unsigned char *buf, size_t len)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+
+ wpa_printf(MSG_DEBUG, "RX pre-auth from " MACSTR, MAC2STR(src_addr));
+ wpa_hexdump(MSG_MSGDUMP, "RX pre-auth", buf, len);
+
+ if (wpa_s->preauth_eapol == NULL ||
+ memcmp(wpa_s->preauth_bssid, "\x00\x00\x00\x00\x00\x00",
+ ETH_ALEN) == 0 ||
+ memcmp(wpa_s->preauth_bssid, src_addr, ETH_ALEN) != 0) {
+ wpa_printf(MSG_WARNING, "RSN pre-auth frame received from "
+ "unexpected source " MACSTR " - dropped",
+ MAC2STR(src_addr));
+ return;
+ }
+
+ eapol_sm_rx_eapol(wpa_s->preauth_eapol, src_addr, buf, len);
+}
+
+
+static void wpa_init_conf(struct wpa_supplicant *wpa_s, const char *target,
+ const char *ifname)
+{
+ strncpy(wpa_s->ifname, ifname, sizeof(wpa_s->ifname));
+
+ if (hwaddr_aton(target, wpa_s->preauth_bssid)) {
+ printf("Failed to parse target address '%s'.\n", target);
+ exit(-1);
+ }
+
+ wpa_s->l2_preauth = l2_packet_init(wpa_s->ifname, NULL,
+ ETH_P_RSN_PREAUTH,
+ rsn_preauth_receive, wpa_s);
+ if (wpa_s->l2_preauth == NULL) {
+ wpa_printf(MSG_WARNING, "RSN: Failed to initialize L2 packet "
+ "processing for pre-authentication");
+ exit(-1);
+ }
+
+ if (l2_packet_get_own_addr(wpa_s->l2_preauth, wpa_s->own_addr)) {
+ wpa_printf(MSG_WARNING, "Failed to get own L2 address\n");
+ exit(-1);
+ }
+}
+
+
+static void eapol_test_terminate(int sig, void *eloop_ctx,
+ void *signal_ctx)
+{
+ struct wpa_supplicant *wpa_s = eloop_ctx;
+ wpa_msg(wpa_s, MSG_INFO, "Signal %d received - terminating", sig);
+ eloop_terminate();
+}
+
+
+int main(int argc, char *argv[])
+{
+ struct wpa_supplicant wpa_s;
+ int ret = 1;
+
+ wpa_debug_level = 0;
+ wpa_debug_show_keys = 1;
+
+ if (argc != 4) {
+ printf("usage: eapol_test <conf> <target MAC address> "
+ "<ifname>\n");
+ return -1;
+ }
+
+ eloop_init(&wpa_s);
+
+ memset(&wpa_s, 0, sizeof(wpa_s));
+ wpa_s.conf = wpa_config_read(argv[1]);
+ if (wpa_s.conf == NULL) {
+ printf("Failed to parse configuration file '%s'.\n", argv[1]);
+ return -1;
+ }
+ if (wpa_s.conf->ssid == NULL) {
+ printf("No networks defined.\n");
+ return -1;
+ }
+
+ wpa_init_conf(&wpa_s, argv[2], argv[3]);
+ if (wpa_supplicant_ctrl_iface_init(&wpa_s)) {
+ printf("Failed to initialize control interface '%s'.\n"
+ "You may have another eapol_test process already "
+ "running or the file was\n"
+ "left by an unclean termination of eapol_test in "
+ "which case you will need\n"
+ "to manually remove this file before starting "
+ "eapol_test again.\n",
+ wpa_s.conf->ctrl_interface);
+ return -1;
+ }
+ wpa_supplicant_scard_init(&wpa_s, wpa_s.conf->ssid);
+
+ if (test_eapol(&wpa_s, wpa_s.conf->ssid))
+ return -1;
+
+ eloop_register_timeout(30, 0, eapol_test_timeout, &wpa_s, NULL);
+ eloop_register_signal(SIGINT, eapol_test_terminate, NULL);
+ eloop_register_signal(SIGTERM, eapol_test_terminate, NULL);
+ eloop_register_signal(SIGHUP, eapol_test_terminate, NULL);
+ eloop_run();
+
+ if (wpa_s.auth_timed_out)
+ ret = -2;
+ else
+ ret = 0;
+
+ test_eapol_clean(&wpa_s);
+
+ eloop_destroy();
+
+ return ret;
+}
diff --git a/contrib/wpa_supplicant/radius_client.c b/contrib/wpa_supplicant/radius_client.c
new file mode 100644
index 000000000000..91ab3c7cda8b
--- /dev/null
+++ b/contrib/wpa_supplicant/radius_client.c
@@ -0,0 +1,674 @@
+/*
+ * Host AP (software wireless LAN access point) user space daemon for
+ * Host AP kernel driver / RADIUS client / modified for eapol_test
+ * Copyright (c) 2002-2004, Jouni Malinen <jkmaline@cc.hut.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it 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.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <netinet/in.h>
+#include <string.h>
+#include <time.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+
+#include "common.h"
+#include "wpa.h"
+#include "config_ssid.h"
+#include "wpa_supplicant.h"
+#include "wpa_supplicant_i.h"
+#include "radius.h"
+#include "radius_client.h"
+#include "eloop.h"
+#include "l2_packet.h"
+
+#include "wpa_supplicant.h"
+#define hostapd_logger(h, a, m, l, t...) wpa_printf(MSG_DEBUG, t)
+#define HOSTAPD_DEBUG(l, a...) wpa_printf(MSG_DEBUG, a)
+#define HOSTAPD_DEBUG_COND(l) 1
+
+/* Defaults for RADIUS retransmit values (exponential backoff) */
+#define RADIUS_CLIENT_FIRST_WAIT 1 /* seconds */
+#define RADIUS_CLIENT_MAX_WAIT 120 /* seconds */
+#define RADIUS_CLIENT_MAX_RETRIES 10 /* maximum number of retransmit attempts
+ * before entry is removed from retransmit
+ * list */
+#define RADIUS_CLIENT_MAX_ENTRIES 30 /* maximum number of entries in retransmit
+ * list (oldest will be removed, if this
+ * limit is exceeded) */
+#define RADIUS_CLIENT_NUM_FAILOVER 4 /* try to change RADIUS server after this
+ * many failed retry attempts */
+
+
+
+static int
+radius_change_server(struct wpa_supplicant *wpa_s, struct hostapd_radius_server *nserv,
+ struct hostapd_radius_server *oserv,
+ int sock, int auth);
+
+
+static void radius_client_msg_free(struct radius_msg_list *req)
+{
+ radius_msg_free(req->msg);
+ free(req->msg);
+ free(req);
+}
+
+
+int radius_client_register(struct wpa_supplicant *wpa_s, RadiusType msg_type,
+ RadiusRxResult (*handler)(struct wpa_supplicant *wpa_s,
+ struct radius_msg *msg,
+ struct radius_msg *req,
+ u8 *shared_secret,
+ size_t shared_secret_len,
+ void *data),
+ void *data)
+{
+ struct radius_rx_handler **handlers, *newh;
+ size_t *num;
+
+ if (msg_type == RADIUS_ACCT) {
+ handlers = &wpa_s->radius->acct_handlers;
+ num = &wpa_s->radius->num_acct_handlers;
+ } else {
+ handlers = &wpa_s->radius->auth_handlers;
+ num = &wpa_s->radius->num_auth_handlers;
+ }
+
+ newh = (struct radius_rx_handler *)
+ realloc(*handlers,
+ (*num + 1) * sizeof(struct radius_rx_handler));
+ if (newh == NULL)
+ return -1;
+
+ newh[*num].handler = handler;
+ newh[*num].data = data;
+ (*num)++;
+ *handlers = newh;
+
+ return 0;
+}
+
+
+static int radius_client_retransmit(struct wpa_supplicant *wpa_s,
+ struct radius_msg_list *entry, time_t now)
+{
+ int s;
+
+ if (entry->msg_type == RADIUS_ACCT ||
+ entry->msg_type == RADIUS_ACCT_INTERIM)
+ s = wpa_s->radius->acct_serv_sock;
+ else
+ s = wpa_s->radius->auth_serv_sock;
+
+ /* retransmit; remove entry if too many attempts */
+ entry->attempts++;
+ HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "Resending RADIUS message (id=%d)"
+ "\n", entry->msg->hdr->identifier);
+
+ if (send(s, entry->msg->buf, entry->msg->buf_used, 0) < 0)
+ perror("send[RADIUS]");
+
+ entry->next_try = now + entry->next_wait;
+ entry->next_wait *= 2;
+ 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");
+ return 1;
+ }
+
+ return 0;
+}
+
+
+static void radius_client_timer(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_supplicant *wpa_s = eloop_ctx;
+ time_t now, first;
+ struct radius_msg_list *entry, *prev, *tmp;
+ int auth_failover = 0, acct_failover = 0;
+
+ entry = wpa_s->radius->msgs;
+ if (!entry)
+ return;
+
+ time(&now);
+ first = 0;
+
+ prev = NULL;
+ while (entry) {
+ if (now >= entry->next_try &&
+ radius_client_retransmit(wpa_s, entry, now)) {
+ if (prev)
+ prev->next = entry->next;
+ else
+ wpa_s->radius->msgs = entry->next;
+
+ tmp = entry;
+ entry = entry->next;
+ radius_client_msg_free(tmp);
+ wpa_s->radius->num_msgs--;
+ continue;
+ }
+
+ if (entry->attempts > RADIUS_CLIENT_NUM_FAILOVER) {
+ if (entry->msg_type == RADIUS_ACCT ||
+ entry->msg_type == RADIUS_ACCT_INTERIM)
+ acct_failover++;
+ else
+ auth_failover++;
+ }
+
+ if (first == 0 || entry->next_try < first)
+ first = entry->next_try;
+
+ prev = entry;
+ entry = entry->next;
+ }
+
+ if (wpa_s->radius->msgs) {
+ if (first < now)
+ first = now;
+ eloop_register_timeout(first - now, 0,
+ radius_client_timer, wpa_s, NULL);
+ }
+
+ if (auth_failover && wpa_s->num_auth_servers > 1) {
+ struct hostapd_radius_server *next, *old;
+ old = wpa_s->auth_server;
+ hostapd_logger(wpa_s, NULL, HOSTAPD_MODULE_RADIUS,
+ HOSTAPD_LEVEL_NOTICE,
+ "No response from Authentication server "
+ "%s:%d - failover",
+ inet_ntoa(old->addr), old->port);
+
+ next = old + 1;
+ if (next > &(wpa_s->auth_servers
+ [wpa_s->num_auth_servers - 1]))
+ next = wpa_s->auth_servers;
+ wpa_s->auth_server = next;
+ radius_change_server(wpa_s, next, old,
+ wpa_s->radius->auth_serv_sock, 1);
+ }
+
+ if (acct_failover && wpa_s->num_acct_servers > 1) {
+ struct hostapd_radius_server *next, *old;
+ old = wpa_s->acct_server;
+ hostapd_logger(wpa_s, NULL, HOSTAPD_MODULE_RADIUS,
+ HOSTAPD_LEVEL_NOTICE,
+ "No response from Accounting server "
+ "%s:%d - failover",
+ inet_ntoa(old->addr), old->port);
+ next = old + 1;
+ if (next > &wpa_s->acct_servers
+ [wpa_s->num_acct_servers - 1])
+ next = wpa_s->acct_servers;
+ wpa_s->acct_server = next;
+ radius_change_server(wpa_s, next, old,
+ wpa_s->radius->acct_serv_sock, 0);
+ }
+}
+
+
+static void radius_client_list_add(struct wpa_supplicant *wpa_s, struct radius_msg *msg,
+ RadiusType msg_type, u8 *shared_secret,
+ size_t shared_secret_len, u8 *addr)
+{
+ struct radius_msg_list *entry, *prev;
+
+ if (eloop_terminated()) {
+ /* No point in adding entries to retransmit queue since event
+ * loop has already been terminated. */
+ radius_msg_free(msg);
+ free(msg);
+ return;
+ }
+
+ entry = malloc(sizeof(*entry));
+ if (entry == NULL) {
+ printf("Failed to add RADIUS packet into retransmit list\n");
+ radius_msg_free(msg);
+ free(msg);
+ return;
+ }
+
+ memset(entry, 0, sizeof(*entry));
+ if (addr)
+ memcpy(entry->addr, addr, ETH_ALEN);
+ entry->msg = msg;
+ entry->msg_type = msg_type;
+ entry->shared_secret = shared_secret;
+ entry->shared_secret_len = shared_secret_len;
+ time(&entry->first_try);
+ entry->next_try = entry->first_try + RADIUS_CLIENT_FIRST_WAIT;
+ entry->attempts = 1;
+ entry->next_wait = RADIUS_CLIENT_FIRST_WAIT * 2;
+
+ if (!wpa_s->radius->msgs)
+ eloop_register_timeout(RADIUS_CLIENT_FIRST_WAIT, 0,
+ radius_client_timer, wpa_s, NULL);
+
+ entry->next = wpa_s->radius->msgs;
+ wpa_s->radius->msgs = entry;
+
+ if (wpa_s->radius->num_msgs >= RADIUS_CLIENT_MAX_ENTRIES) {
+ printf("Removing the oldest un-ACKed RADIUS packet due to "
+ "retransmit list limits.\n");
+ prev = NULL;
+ while (entry->next) {
+ prev = entry;
+ entry = entry->next;
+ }
+ if (prev) {
+ prev->next = NULL;
+ radius_client_msg_free(entry);
+ }
+ } else
+ wpa_s->radius->num_msgs++;
+}
+
+
+static void radius_client_list_del(struct wpa_supplicant *wpa_s,
+ RadiusType msg_type, u8 *addr)
+{
+ struct radius_msg_list *entry, *prev, *tmp;
+
+ if (addr == NULL)
+ return;
+
+ entry = wpa_s->radius->msgs;
+ prev = NULL;
+ while (entry) {
+ if (entry->msg_type == msg_type &&
+ memcmp(entry->addr, addr, ETH_ALEN) == 0) {
+ if (prev)
+ prev->next = entry->next;
+ else
+ wpa_s->radius->msgs = entry->next;
+ tmp = entry;
+ entry = entry->next;
+ HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL,
+ "Removing matching RADIUS message for "
+ MACSTR "\n", MAC2STR(addr));
+ radius_client_msg_free(tmp);
+ wpa_s->radius->num_msgs--;
+ continue;
+ }
+ prev = entry;
+ entry = entry->next;
+ }
+}
+
+
+int radius_client_send(struct wpa_supplicant *wpa_s, struct radius_msg *msg,
+ RadiusType msg_type, u8 *addr)
+{
+ u8 *shared_secret;
+ size_t shared_secret_len;
+ char *name;
+ int s, res;
+
+ if (msg_type == RADIUS_ACCT_INTERIM) {
+ /* Remove any pending interim acct update for the same STA. */
+ radius_client_list_del(wpa_s, msg_type, addr);
+ }
+
+ if (msg_type == RADIUS_ACCT || msg_type == RADIUS_ACCT_INTERIM) {
+ shared_secret = wpa_s->acct_server->shared_secret;
+ shared_secret_len = wpa_s->acct_server->shared_secret_len;
+ radius_msg_finish_acct(msg, shared_secret, shared_secret_len);
+ name = "accounting";
+ s = wpa_s->radius->acct_serv_sock;
+ } else {
+ shared_secret = wpa_s->auth_server->shared_secret;
+ shared_secret_len = wpa_s->auth_server->shared_secret_len;
+ radius_msg_finish(msg, shared_secret, shared_secret_len);
+ name = "authentication";
+ s = wpa_s->radius->auth_serv_sock;
+ }
+
+ HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL,
+ "Sending RADIUS message to %s server\n", name);
+ if (HOSTAPD_DEBUG_COND(HOSTAPD_DEBUG_MSGDUMPS))
+ radius_msg_dump(msg);
+
+ res = send(s, msg->buf, msg->buf_used, 0);
+ if (res < 0)
+ perror("send[RADIUS]");
+
+ radius_client_list_add(wpa_s, msg, msg_type, shared_secret,
+ shared_secret_len, addr);
+
+ return res;
+}
+
+
+static void radius_client_receive(int sock, void *eloop_ctx, void *sock_ctx)
+{
+ struct wpa_supplicant *wpa_s = (struct wpa_supplicant *) eloop_ctx;
+ RadiusType msg_type = (RadiusType) sock_ctx;
+ int len, i;
+ unsigned char buf[3000];
+ struct radius_msg *msg;
+ struct radius_rx_handler *handlers;
+ size_t num_handlers;
+ struct radius_msg_list *req, *prev_req;
+
+ len = recv(sock, buf, sizeof(buf), 0);
+ if (len < 0) {
+ perror("recv[RADIUS]");
+ return;
+ }
+ HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL,
+ "Received %d bytes from RADIUS server\n", len);
+ if (len == sizeof(buf)) {
+ printf("Possibly too long UDP frame for our buffer - "
+ "dropping it\n");
+ return;
+ }
+
+ msg = radius_msg_parse(buf, len);
+ if (msg == NULL) {
+ printf("Parsing incoming RADIUS frame failed\n");
+ return;
+ }
+
+ HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL,
+ "Received RADIUS message\n");
+ if (HOSTAPD_DEBUG_COND(HOSTAPD_DEBUG_MSGDUMPS))
+ radius_msg_dump(msg);
+
+ if (msg_type == RADIUS_ACCT) {
+ handlers = wpa_s->radius->acct_handlers;
+ num_handlers = wpa_s->radius->num_acct_handlers;
+ } else {
+ handlers = wpa_s->radius->auth_handlers;
+ num_handlers = wpa_s->radius->num_auth_handlers;
+ }
+
+ prev_req = NULL;
+ req = wpa_s->radius->msgs;
+ while (req) {
+ /* TODO: also match by src addr:port of the packet when using
+ * alternative RADIUS servers (?) */
+ if ((req->msg_type == msg_type ||
+ (req->msg_type == RADIUS_ACCT_INTERIM &&
+ msg_type == RADIUS_ACCT)) &&
+ req->msg->hdr->identifier == msg->hdr->identifier)
+ break;
+
+ prev_req = req;
+ req = req->next;
+ }
+
+ if (req == NULL) {
+ HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL,
+ "No matching RADIUS request found (type=%d "
+ "id=%d) - dropping packet\n",
+ msg_type, msg->hdr->identifier);
+ goto fail;
+ }
+
+ /* Remove ACKed RADIUS packet from retransmit list */
+ if (prev_req)
+ prev_req->next = req->next;
+ else
+ wpa_s->radius->msgs = req->next;
+ wpa_s->radius->num_msgs--;
+
+ for (i = 0; i < num_handlers; i++) {
+ RadiusRxResult res;
+ res = handlers[i].handler(wpa_s, msg, req->msg,
+ req->shared_secret,
+ req->shared_secret_len,
+ handlers[i].data);
+ switch (res) {
+ case RADIUS_RX_PROCESSED:
+ radius_msg_free(msg);
+ free(msg);
+ /* continue */
+ case RADIUS_RX_QUEUED:
+ radius_client_msg_free(req);
+ return;
+ case RADIUS_RX_UNKNOWN:
+ /* continue with next handler */
+ break;
+ }
+ }
+
+ printf("No RADIUS RX handler found (type=%d code=%d id=%d) - dropping "
+ "packet\n", msg_type, msg->hdr->code, msg->hdr->identifier);
+ radius_client_msg_free(req);
+
+ fail:
+ radius_msg_free(msg);
+ free(msg);
+}
+
+
+u8 radius_client_get_id(struct wpa_supplicant *wpa_s)
+{
+ struct radius_msg_list *entry, *prev, *remove;
+ u8 id = wpa_s->radius->next_radius_identifier++;
+
+ /* remove entries with matching id from retransmit list to avoid
+ * using new reply from the RADIUS server with an old request */
+ entry = wpa_s->radius->msgs;
+ prev = NULL;
+ while (entry) {
+ if (entry->msg->hdr->identifier == id) {
+ HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL,
+ "Removing pending RADIUS message, since "
+ "its id (%d) is reused\n", id);
+ if (prev)
+ prev->next = entry->next;
+ else
+ wpa_s->radius->msgs = entry->next;
+ remove = entry;
+ } else
+ remove = NULL;
+ prev = entry;
+ entry = entry->next;
+
+ if (remove)
+ radius_client_msg_free(remove);
+ }
+
+ return id;
+}
+
+
+void radius_client_flush(struct wpa_supplicant *wpa_s)
+{
+ struct radius_msg_list *entry, *prev;
+
+ if (!wpa_s->radius)
+ return;
+
+ eloop_cancel_timeout(radius_client_timer, wpa_s, NULL);
+
+ entry = wpa_s->radius->msgs;
+ wpa_s->radius->msgs = NULL;
+ wpa_s->radius->num_msgs = 0;
+ while (entry) {
+ prev = entry;
+ entry = entry->next;
+ radius_client_msg_free(prev);
+ }
+}
+
+
+static int
+radius_change_server(struct wpa_supplicant *wpa_s, struct hostapd_radius_server *nserv,
+ struct hostapd_radius_server *oserv,
+ int sock, int auth)
+{
+ struct sockaddr_in serv;
+
+ hostapd_logger(wpa_s, NULL, HOSTAPD_MODULE_RADIUS, HOSTAPD_LEVEL_INFO,
+ "%s server %s:%d",
+ auth ? "Authentication" : "Accounting",
+ inet_ntoa(nserv->addr), nserv->port);
+
+ if (!oserv || nserv->shared_secret_len != oserv->shared_secret_len ||
+ memcmp(nserv->shared_secret, oserv->shared_secret,
+ nserv->shared_secret_len) != 0) {
+ /* Pending RADIUS packets used different shared
+ * secret, so they would need to be modified. Could
+ * update all message authenticators and
+ * User-Passwords, etc. and retry with new server. For
+ * now, just drop all pending packets. */
+ radius_client_flush(wpa_s);
+ } else {
+ /* Reset retry counters for the new server */
+ struct radius_msg_list *entry;
+ entry = wpa_s->radius->msgs;
+ while (entry) {
+ entry->next_try = entry->first_try +
+ RADIUS_CLIENT_FIRST_WAIT;
+ entry->attempts = 0;
+ entry->next_wait = RADIUS_CLIENT_FIRST_WAIT * 2;
+ entry = entry->next;
+ }
+ if (wpa_s->radius->msgs) {
+ eloop_cancel_timeout(radius_client_timer, wpa_s, NULL);
+ eloop_register_timeout(RADIUS_CLIENT_FIRST_WAIT, 0,
+ radius_client_timer, wpa_s,
+ NULL);
+ }
+ }
+
+ memset(&serv, 0, sizeof(serv));
+ serv.sin_family = AF_INET;
+ serv.sin_addr.s_addr = nserv->addr.s_addr;
+ serv.sin_port = htons(nserv->port);
+
+ if (connect(sock, (struct sockaddr *) &serv, sizeof(serv)) < 0) {
+ perror("connect[radius]");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static void radius_retry_primary_timer(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_supplicant *wpa_s = eloop_ctx;
+ struct hostapd_radius_server *oserv;
+
+ if (wpa_s->radius->auth_serv_sock >= 0 && wpa_s->auth_servers &&
+ wpa_s->auth_server != wpa_s->auth_servers) {
+ oserv = wpa_s->auth_server;
+ wpa_s->auth_server = wpa_s->auth_servers;
+ radius_change_server(wpa_s, wpa_s->auth_server, oserv,
+ wpa_s->radius->auth_serv_sock, 1);
+ }
+
+ if (wpa_s->radius->acct_serv_sock >= 0 && wpa_s->acct_servers &&
+ wpa_s->acct_server != wpa_s->acct_servers) {
+ oserv = wpa_s->acct_server;
+ wpa_s->acct_server = wpa_s->acct_servers;
+ radius_change_server(wpa_s, wpa_s->acct_server, oserv,
+ wpa_s->radius->acct_serv_sock, 0);
+ }
+
+ if (wpa_s->radius_retry_primary_interval)
+ eloop_register_timeout(wpa_s->
+ radius_retry_primary_interval, 0,
+ radius_retry_primary_timer, wpa_s, NULL);
+}
+
+
+static int radius_client_init_auth(struct wpa_supplicant *wpa_s)
+{
+ wpa_s->radius->auth_serv_sock = socket(PF_INET, SOCK_DGRAM, 0);
+ if (wpa_s->radius->auth_serv_sock < 0) {
+ perror("socket[PF_INET,SOCK_DGRAM]");
+ return -1;
+ }
+
+ radius_change_server(wpa_s, wpa_s->auth_server, NULL,
+ wpa_s->radius->auth_serv_sock, 1);
+
+ if (eloop_register_read_sock(wpa_s->radius->auth_serv_sock,
+ radius_client_receive, wpa_s,
+ (void *) RADIUS_AUTH)) {
+ printf("Could not register read socket for authentication "
+ "server\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int radius_client_init_acct(struct wpa_supplicant *wpa_s)
+{
+ wpa_s->radius->acct_serv_sock = socket(PF_INET, SOCK_DGRAM, 0);
+ if (wpa_s->radius->acct_serv_sock < 0) {
+ perror("socket[PF_INET,SOCK_DGRAM]");
+ return -1;
+ }
+
+ radius_change_server(wpa_s, wpa_s->acct_server, NULL,
+ wpa_s->radius->acct_serv_sock, 0);
+
+ if (eloop_register_read_sock(wpa_s->radius->acct_serv_sock,
+ radius_client_receive, wpa_s,
+ (void *) RADIUS_ACCT)) {
+ printf("Could not register read socket for accounting "
+ "server\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+int radius_client_init(struct wpa_supplicant *wpa_s)
+{
+ wpa_s->radius = malloc(sizeof(struct radius_client_data));
+ if (wpa_s->radius == NULL)
+ return -1;
+
+ memset(wpa_s->radius, 0, sizeof(struct radius_client_data));
+ wpa_s->radius->auth_serv_sock = wpa_s->radius->acct_serv_sock = -1;
+
+ if (wpa_s->auth_server && radius_client_init_auth(wpa_s))
+ return -1;
+
+ if (wpa_s->acct_server && radius_client_init_acct(wpa_s))
+ return -1;
+
+ if (wpa_s->radius_retry_primary_interval)
+ eloop_register_timeout(wpa_s->radius_retry_primary_interval, 0,
+ radius_retry_primary_timer, wpa_s,
+ NULL);
+
+ return 0;
+}
+
+
+void radius_client_deinit(struct wpa_supplicant *wpa_s)
+{
+ if (!wpa_s->radius)
+ return;
+
+ eloop_cancel_timeout(radius_retry_primary_timer, wpa_s, NULL);
+
+ radius_client_flush(wpa_s);
+ free(wpa_s->radius->auth_handlers);
+ free(wpa_s->radius->acct_handlers);
+ free(wpa_s->radius);
+ wpa_s->radius = NULL;
+}
diff --git a/contrib/wpa_supplicant/radius_client.h b/contrib/wpa_supplicant/radius_client.h
new file mode 100644
index 000000000000..993f8d0b7102
--- /dev/null
+++ b/contrib/wpa_supplicant/radius_client.h
@@ -0,0 +1,81 @@
+#ifndef RADIUS_CLIENT_H
+#define RADIUS_CLIENT_H
+
+typedef enum {
+ RADIUS_AUTH,
+ RADIUS_ACCT,
+ RADIUS_ACCT_INTERIM /* used only with radius_client_send(); just like
+ * RADIUS_ACCT, but removes any pending interim
+ * RADIUS Accounting packages for the same STA
+ * before sending the new interim update */
+} RadiusType;
+
+/* RADIUS message retransmit list */
+struct radius_msg_list {
+ u8 addr[ETH_ALEN]; /* STA/client address; used to find RADIUS messages
+ * for the same STA. */
+ struct radius_msg *msg;
+ RadiusType msg_type;
+ time_t first_try;
+ time_t next_try;
+ int attempts;
+ int next_wait;
+
+ u8 *shared_secret;
+ size_t shared_secret_len;
+
+ /* TODO: server config with failover to backup server(s) */
+
+ struct radius_msg_list *next;
+};
+
+
+typedef enum {
+ RADIUS_RX_PROCESSED,
+ RADIUS_RX_QUEUED,
+ RADIUS_RX_UNKNOWN
+} RadiusRxResult;
+
+struct radius_rx_handler {
+ RadiusRxResult (*handler)(struct wpa_supplicant *wpa_s,
+ struct radius_msg *msg,
+ struct radius_msg *req,
+ u8 *shared_secret, size_t shared_secret_len,
+ void *data);
+ void *data;
+};
+
+struct radius_client_data {
+ int auth_serv_sock; /* socket for authentication RADIUS messages */
+ int acct_serv_sock; /* socket for accounting RADIUS messages */
+
+ struct radius_rx_handler *auth_handlers;
+ size_t num_auth_handlers;
+ struct radius_rx_handler *acct_handlers;
+ size_t num_acct_handlers;
+
+ struct radius_msg_list *msgs;
+ size_t num_msgs;
+
+ u8 next_radius_identifier;
+ u32 acct_session_id_hi;
+ u32 acct_session_id_lo;
+};
+
+
+int radius_client_register(struct wpa_supplicant *wpa_s, RadiusType msg_type,
+ RadiusRxResult (*handler)
+ (struct wpa_supplicant *wpa_s,
+ struct radius_msg *msg, struct radius_msg *req,
+ u8 *shared_secret, size_t shared_secret_len,
+ void *data),
+ void *data);
+int radius_client_send(struct wpa_supplicant *wpa_s, struct radius_msg *msg,
+ RadiusType msg_type, u8 *addr);
+u8 radius_client_get_id(struct wpa_supplicant *wpa_s);
+
+void radius_client_flush(struct wpa_supplicant *wpa_s);
+int radius_client_init(struct wpa_supplicant *wpa_s);
+void radius_client_deinit(struct wpa_supplicant *wpa_s);
+
+#endif /* RADIUS_CLIENT_H */
diff --git a/contrib/wpa_supplicant/rc4.c b/contrib/wpa_supplicant/rc4.c
new file mode 100644
index 000000000000..97ec1b0ae357
--- /dev/null
+++ b/contrib/wpa_supplicant/rc4.c
@@ -0,0 +1,63 @@
+/*
+ * Host AP (software wireless LAN access point) user space daemon for
+ * Host AP kernel driver / RC4
+ * Copyright (c) 2002-2004, Jouni Malinen <jkmaline@cc.hut.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published 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 <stdio.h>
+#include "common.h"
+#include "rc4.h"
+
+#define S_SWAP(a,b) do { u8 t = S[a]; S[a] = S[b]; S[b] = t; } while(0)
+
+void rc4_skip(u8 *key, size_t keylen, size_t skip, u8 *data, size_t data_len)
+{
+ u32 i, j, k;
+ u8 S[256], *pos;
+ int kpos;
+
+ /* Setup RC4 state */
+ for (i = 0; i < 256; i++)
+ S[i] = i;
+ j = 0;
+ kpos = 0;
+ for (i = 0; i < 256; i++) {
+ j = (j + S[i] + key[kpos]) & 0xff;
+ kpos++;
+ if (kpos >= keylen)
+ kpos = 0;
+ S_SWAP(i, j);
+ }
+
+ /* Skip the start of the stream */
+ i = j = 0;
+ for (k = 0; k < skip; k++) {
+ i = (i + 1) & 0xff;
+ j = (j + S[i]) & 0xff;
+ S_SWAP(i, j);
+ }
+
+ /* Apply RC4 to data */
+ pos = data;
+ for (k = 0; k < data_len; k++) {
+ i = (i + 1) & 0xff;
+ j = (j + S[i]) & 0xff;
+ S_SWAP(i, j);
+ *pos++ ^= S[(S[i] + S[j]) & 0xff];
+ }
+}
+
+
+void rc4(u8 *buf, size_t len, u8 *key, size_t key_len)
+{
+ rc4_skip(key, key_len, 0, buf, len);
+}
diff --git a/contrib/wpa_supplicant/rc4.h b/contrib/wpa_supplicant/rc4.h
new file mode 100644
index 000000000000..0e77b7e470d0
--- /dev/null
+++ b/contrib/wpa_supplicant/rc4.h
@@ -0,0 +1,7 @@
+#ifndef RC4_H
+#define RC4_H
+
+void rc4_skip(u8 *key, size_t keylen, size_t skip, u8 *data, size_t data_len);
+void rc4(u8 *buf, size_t len, u8 *key, size_t key_len);
+
+#endif /* RC4_H */
diff --git a/contrib/wpa_supplicant/sha1.c b/contrib/wpa_supplicant/sha1.c
new file mode 100644
index 000000000000..04943b5a775c
--- /dev/null
+++ b/contrib/wpa_supplicant/sha1.c
@@ -0,0 +1,910 @@
+/*
+ * SHA1 hash implementation and interface functions
+ * Copyright (c) 2003-2005, Jouni Malinen <jkmaline@cc.hut.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published 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 <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "common.h"
+#include "sha1.h"
+#include "md5.h"
+
+
+void sha1_mac(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
+ u8 *mac)
+{
+ SHA1_CTX context;
+ SHA1Init(&context);
+ SHA1Update(&context, key, key_len);
+ SHA1Update(&context, data, data_len);
+ SHA1Update(&context, key, key_len);
+ SHA1Final(mac, &context);
+}
+
+
+/* HMAC code is based on RFC 2104 */
+void hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem,
+ const u8 *addr[], const size_t *len, u8 *mac)
+{
+ SHA1_CTX context;
+ unsigned char k_ipad[65]; /* inner padding - key XORd with ipad */
+ unsigned char k_opad[65]; /* outer padding - key XORd with opad */
+ unsigned char tk[20];
+ int i;
+
+ /* if key is longer than 64 bytes reset it to key = SHA1(key) */
+ if (key_len > 64) {
+ SHA1Init(&context);
+ SHA1Update(&context, key, key_len);
+ SHA1Final(tk, &context);
+
+ key = tk;
+ key_len = 20;
+ }
+
+ /* the HMAC_SHA1 transform looks like:
+ *
+ * SHA1(K XOR opad, SHA1(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 pads */
+ memset(k_ipad, 0, sizeof(k_ipad));
+ memset(k_opad, 0, sizeof(k_opad));
+ memcpy(k_ipad, key, key_len);
+ memcpy(k_opad, key, key_len);
+
+ /* XOR key with ipad and opad values */
+ for (i = 0; i < 64; i++) {
+ k_ipad[i] ^= 0x36;
+ k_opad[i] ^= 0x5c;
+ }
+
+ /* perform inner SHA1 */
+ SHA1Init(&context); /* init context for 1st pass */
+ SHA1Update(&context, k_ipad, 64); /* start with inner pad */
+ /* then text of datagram; all fragments */
+ for (i = 0; i < num_elem; i++) {
+ SHA1Update(&context, addr[i], len[i]);
+ }
+ SHA1Final(mac, &context); /* finish up 1st pass */
+
+ /* perform outer SHA1 */
+ SHA1Init(&context); /* init context for 2nd pass */
+ SHA1Update(&context, k_opad, 64); /* start with outer pad */
+ SHA1Update(&context, mac, 20); /* then results of 1st hash */
+ SHA1Final(mac, &context); /* finish up 2nd pass */
+}
+
+
+void hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
+ u8 *mac)
+{
+ hmac_sha1_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+
+void 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 zero = 0, counter = 0;
+ size_t pos, plen;
+ u8 hash[SHA1_MAC_LEN];
+ size_t label_len = strlen(label);
+ const unsigned char *addr[4];
+ size_t len[4];
+
+ addr[0] = (u8 *) label;
+ len[0] = label_len;
+ addr[1] = &zero;
+ len[1] = 1;
+ addr[2] = data;
+ len[2] = data_len;
+ addr[3] = &counter;
+ len[3] = 1;
+
+ pos = 0;
+ while (pos < buf_len) {
+ plen = buf_len - pos;
+ if (plen >= SHA1_MAC_LEN) {
+ hmac_sha1_vector(key, key_len, 4, addr, len,
+ &buf[pos]);
+ pos += SHA1_MAC_LEN;
+ } else {
+ hmac_sha1_vector(key, key_len, 4, addr, len,
+ hash);
+ memcpy(&buf[pos], hash, plen);
+ break;
+ }
+ counter++;
+ }
+}
+
+
+/* draft-cam-winget-eap-fast-00.txt */
+void 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)
+{
+ unsigned char counter = 0;
+ size_t pos, plen;
+ u8 hash[SHA1_MAC_LEN];
+ size_t label_len = strlen(label);
+ u8 output_len[2];
+ const unsigned char *addr[5];
+ size_t len[5];
+
+ addr[0] = hash;
+ len[0] = 0;
+ addr[1] = (unsigned char *) label;
+ len[1] = label_len + 1;
+ addr[2] = seed;
+ len[2] = seed_len;
+ addr[3] = output_len;
+ len[3] = 2;
+ addr[4] = &counter;
+ len[4] = 1;
+
+ output_len[0] = (buf_len >> 8) & 0xff;
+ output_len[1] = buf_len & 0xff;
+ pos = 0;
+ while (pos < buf_len) {
+ counter++;
+ plen = buf_len - pos;
+ hmac_sha1_vector(key, key_len, 5, addr, len, hash);
+ if (plen >= SHA1_MAC_LEN) {
+ memcpy(&buf[pos], hash, SHA1_MAC_LEN);
+ pos += SHA1_MAC_LEN;
+ } else {
+ memcpy(&buf[pos], hash, plen);
+ break;
+ }
+ len[0] = SHA1_MAC_LEN;
+ }
+}
+
+
+/* RFC 2246 */
+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)
+{
+ size_t L_S1, L_S2;
+ const u8 *S1, *S2;
+ u8 A_MD5[MD5_MAC_LEN], A_SHA1[SHA1_MAC_LEN];
+ u8 P_MD5[MD5_MAC_LEN], P_SHA1[SHA1_MAC_LEN];
+ int i, MD5_pos, SHA1_pos;
+ const u8 *MD5_addr[3];
+ size_t MD5_len[3];
+ const unsigned char *SHA1_addr[3];
+ size_t SHA1_len[3];
+
+ if (secret_len & 1)
+ return -1;
+
+ MD5_addr[0] = A_MD5;
+ MD5_len[0] = MD5_MAC_LEN;
+ MD5_addr[1] = (unsigned char *) label;
+ MD5_len[1] = strlen(label);
+ MD5_addr[2] = seed;
+ MD5_len[2] = seed_len;
+
+ SHA1_addr[0] = A_SHA1;
+ SHA1_len[0] = SHA1_MAC_LEN;
+ SHA1_addr[1] = (unsigned char *) label;
+ SHA1_len[1] = strlen(label);
+ SHA1_addr[2] = seed;
+ SHA1_len[2] = seed_len;
+
+ /* RFC 2246, 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 = P_MD5(S1, label + seed) XOR P_SHA-1(S2, label + seed)
+ */
+
+ L_S1 = L_S2 = (secret_len + 1) / 2;
+ S1 = secret;
+ S2 = secret + L_S1;
+
+ 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(S1, L_S1, 3, MD5_addr, MD5_len, P_MD5);
+ MD5_pos = 0;
+ 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,
+ P_SHA1);
+ SHA1_pos = 0;
+ hmac_sha1(S2, L_S2, A_SHA1, SHA1_MAC_LEN, A_SHA1);
+ }
+
+ out[i] = P_MD5[MD5_pos] ^ P_SHA1[SHA1_pos];
+
+ MD5_pos++;
+ SHA1_pos++;
+ }
+
+ return 0;
+}
+
+
+static void pbkdf2_sha1_f(const char *passphrase, const char *ssid,
+ size_t ssid_len, int iterations, int count,
+ u8 *digest)
+{
+ unsigned char tmp[SHA1_MAC_LEN], tmp2[SHA1_MAC_LEN];
+ int i, j;
+ unsigned char count_buf[4];
+ const u8 *addr[2];
+ size_t len[2];
+ size_t passphrase_len = strlen(passphrase);
+
+ addr[0] = (u8 *) ssid;
+ len[0] = ssid_len;
+ addr[1] = count_buf;
+ len[1] = 4;
+
+ /* F(P, S, c, i) = U1 xor U2 xor ... Uc
+ * U1 = PRF(P, S || i)
+ * U2 = PRF(P, U1)
+ * Uc = PRF(P, Uc-1)
+ */
+
+ count_buf[0] = (count >> 24) & 0xff;
+ count_buf[1] = (count >> 16) & 0xff;
+ count_buf[2] = (count >> 8) & 0xff;
+ count_buf[3] = count & 0xff;
+ hmac_sha1_vector((u8 *) passphrase, passphrase_len, 2, addr, len, tmp);
+ memcpy(digest, tmp, SHA1_MAC_LEN);
+
+ for (i = 1; i < iterations; i++) {
+ hmac_sha1((u8 *) passphrase, passphrase_len, tmp, SHA1_MAC_LEN,
+ tmp2);
+ memcpy(tmp, tmp2, SHA1_MAC_LEN);
+ for (j = 0; j < SHA1_MAC_LEN; j++)
+ digest[j] ^= tmp2[j];
+ }
+}
+
+
+void pbkdf2_sha1(const char *passphrase, const char *ssid, size_t ssid_len,
+ int iterations, u8 *buf, size_t buflen)
+{
+ int count = 0;
+ unsigned char *pos = buf;
+ size_t left = buflen, plen;
+ unsigned char digest[SHA1_MAC_LEN];
+
+ while (left > 0) {
+ count++;
+ pbkdf2_sha1_f(passphrase, ssid, ssid_len, iterations, count,
+ digest);
+ plen = left > SHA1_MAC_LEN ? SHA1_MAC_LEN : left;
+ memcpy(pos, digest, plen);
+ pos += plen;
+ left -= plen;
+ }
+}
+
+
+void sha1_transform(u8 *state, u8 data[64])
+{
+#ifdef EAP_TLS_FUNCS
+ SHA_CTX context;
+ memset(&context, 0, sizeof(context));
+ memcpy(&context.h0, state, 5 * 4);
+ SHA1_Transform(&context, data);
+ memcpy(state, &context.h0, 5 * 4);
+#else /* EAP_TLS_FUNCS */
+ SHA1Transform((u32 *) state, data);
+#endif /* EAP_TLS_FUNCS */
+}
+
+
+void sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len,
+ u8 *mac)
+{
+ SHA1_CTX ctx;
+ int i;
+
+ SHA1Init(&ctx);
+ for (i = 0; i < num_elem; i++)
+ SHA1Update(&ctx, addr[i], len[i]);
+ SHA1Final(mac, &ctx);
+}
+
+
+#ifndef EAP_TLS_FUNCS
+
+/* ===== start - public domain SHA1 implementation ===== */
+
+/*
+SHA-1 in C
+By Steve Reid <sreid@sea-to-sky.net>
+100% Public Domain
+
+-----------------
+Modified 7/98
+By James H. Brown <jbrown@burgoyne.com>
+Still 100% Public Domain
+
+Corrected a problem which generated improper hash values on 16 bit machines
+Routine SHA1Update changed from
+ void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned int
+len)
+to
+ void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned
+long len)
+
+The 'len' parameter was declared an int which works fine on 32 bit machines.
+However, on 16 bit machines an int is too small for the shifts being done
+against
+it. This caused the hash function to generate incorrect values if len was
+greater than 8191 (8K - 1) due to the 'len << 3' on line 3 of SHA1Update().
+
+Since the file IO in main() reads 16K at a time, any file 8K or larger would
+be guaranteed to generate the wrong hash (e.g. Test Vector #3, a million
+"a"s).
+
+I also changed the declaration of variables i & j in SHA1Update to
+unsigned long from unsigned int for the same reason.
+
+These changes should make no difference to any 32 bit implementations since
+an
+int and a long are the same size in those environments.
+
+--
+I also corrected a few compiler warnings generated by Borland C.
+1. Added #include <process.h> for exit() prototype
+2. Removed unused variable 'j' in SHA1Final
+3. Changed exit(0) to return(0) at end of main.
+
+ALL changes I made can be located by searching for comments containing 'JHB'
+-----------------
+Modified 8/98
+By Steve Reid <sreid@sea-to-sky.net>
+Still 100% public domain
+
+1- Removed #include <process.h> and used return() instead of exit()
+2- Fixed overwriting of finalcount in SHA1Final() (discovered by Chris Hall)
+3- Changed email address from steve@edmweb.com to sreid@sea-to-sky.net
+
+-----------------
+Modified 4/01
+By Saul Kravitz <Saul.Kravitz@celera.com>
+Still 100% PD
+Modified to run on Compaq Alpha hardware.
+
+-----------------
+Modified 4/01
+By Jouni Malinen <jkmaline@cc.hut.fi>
+Minor changes to match the coding style used in Dynamics.
+
+Modified September 24, 2004
+By Jouni Malinen <jkmaline@cc.hut.fi>
+Fixed alignment issue in SHA1Transform when SHA1HANDSOFF is defined.
+
+*/
+
+/*
+Test Vectors (from FIPS PUB 180-1)
+"abc"
+ A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D
+"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
+ 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1
+A million repetitions of "a"
+ 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F
+*/
+
+#define SHA1HANDSOFF
+
+#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
+
+/* blk0() and blk() perform the initial expand. */
+/* I got the idea of expanding during the round function from SSLeay */
+#ifndef WORDS_BIGENDIAN
+#define blk0(i) (block->l[i] = (rol(block->l[i], 24) & 0xFF00FF00) | \
+ (rol(block->l[i], 8) & 0x00FF00FF))
+#else
+#define blk0(i) block->l[i]
+#endif
+#define blk(i) (block->l[i & 15] = rol(block->l[(i + 13) & 15] ^ \
+ block->l[(i + 8) & 15] ^ block->l[(i + 2) & 15] ^ block->l[i & 15], 1))
+
+/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */
+#define R0(v,w,x,y,z,i) \
+ z += ((w & (x ^ y)) ^ y) + blk0(i) + 0x5A827999 + rol(v, 5); \
+ w = rol(w, 30);
+#define R1(v,w,x,y,z,i) \
+ z += ((w & (x ^ y)) ^ y) + blk(i) + 0x5A827999 + rol(v, 5); \
+ w = rol(w, 30);
+#define R2(v,w,x,y,z,i) \
+ z += (w ^ x ^ y) + blk(i) + 0x6ED9EBA1 + rol(v, 5); w = rol(w, 30);
+#define R3(v,w,x,y,z,i) \
+ z += (((w | x) & y) | (w & x)) + blk(i) + 0x8F1BBCDC + rol(v, 5); \
+ w = rol(w, 30);
+#define R4(v,w,x,y,z,i) \
+ z += (w ^ x ^ y) + blk(i) + 0xCA62C1D6 + rol(v, 5); \
+ w=rol(w, 30);
+
+
+#ifdef VERBOSE /* SAK */
+void SHAPrintContext(SHA1_CTX *context, char *msg)
+{
+ printf("%s (%d,%d) %x %x %x %x %x\n",
+ msg,
+ context->count[0], context->count[1],
+ context->state[0],
+ context->state[1],
+ context->state[2],
+ context->state[3],
+ context->state[4]);
+}
+#endif
+
+/* Hash a single 512-bit block. This is the core of the algorithm. */
+
+void SHA1Transform(u32 state[5], const unsigned char buffer[64])
+{
+ u32 a, b, c, d, e;
+ typedef union {
+ unsigned char c[64];
+ u32 l[16];
+ } CHAR64LONG16;
+ CHAR64LONG16* block;
+#ifdef SHA1HANDSOFF
+ u32 workspace[16];
+ block = (CHAR64LONG16 *) workspace;
+ memcpy(block, buffer, 64);
+#else
+ block = (CHAR64LONG16 *) buffer;
+#endif
+ /* Copy context->state[] to working vars */
+ a = state[0];
+ b = state[1];
+ c = state[2];
+ d = state[3];
+ e = state[4];
+ /* 4 rounds of 20 operations each. Loop unrolled. */
+ R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3);
+ R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7);
+ R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11);
+ R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15);
+ R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19);
+ R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23);
+ R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27);
+ R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31);
+ R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35);
+ R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39);
+ R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43);
+ R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47);
+ R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51);
+ R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55);
+ R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59);
+ R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63);
+ R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67);
+ R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71);
+ R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75);
+ R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79);
+ /* Add the working vars back into context.state[] */
+ state[0] += a;
+ state[1] += b;
+ state[2] += c;
+ state[3] += d;
+ state[4] += e;
+ /* Wipe variables */
+ a = b = c = d = e = 0;
+#ifdef SHA1HANDSOFF
+ memset(block, 0, 64);
+#endif
+}
+
+
+/* SHA1Init - Initialize new context */
+
+void SHA1Init(SHA1_CTX* context)
+{
+ /* SHA1 initialization constants */
+ context->state[0] = 0x67452301;
+ context->state[1] = 0xEFCDAB89;
+ context->state[2] = 0x98BADCFE;
+ context->state[3] = 0x10325476;
+ context->state[4] = 0xC3D2E1F0;
+ context->count[0] = context->count[1] = 0;
+}
+
+
+/* Run your data through this. */
+
+void SHA1Update(SHA1_CTX* context, const void *_data, u32 len)
+{
+ u32 i, j;
+ const unsigned char *data = _data;
+
+#ifdef VERBOSE
+ SHAPrintContext(context, "before");
+#endif
+ j = (context->count[0] >> 3) & 63;
+ if ((context->count[0] += len << 3) < (len << 3))
+ context->count[1]++;
+ context->count[1] += (len >> 29);
+ if ((j + len) > 63) {
+ memcpy(&context->buffer[j], data, (i = 64-j));
+ SHA1Transform(context->state, context->buffer);
+ for ( ; i + 63 < len; i += 64) {
+ SHA1Transform(context->state, &data[i]);
+ }
+ j = 0;
+ }
+ else i = 0;
+ memcpy(&context->buffer[j], &data[i], len - i);
+#ifdef VERBOSE
+ SHAPrintContext(context, "after ");
+#endif
+}
+
+
+/* Add padding and return the message digest. */
+
+void SHA1Final(unsigned char digest[20], SHA1_CTX* context)
+{
+ u32 i;
+ unsigned char finalcount[8];
+
+ for (i = 0; i < 8; i++) {
+ finalcount[i] = (unsigned char)
+ ((context->count[(i >= 4 ? 0 : 1)] >>
+ ((3-(i & 3)) * 8) ) & 255); /* Endian independent */
+ }
+ SHA1Update(context, (unsigned char *) "\200", 1);
+ while ((context->count[0] & 504) != 448) {
+ SHA1Update(context, (unsigned char *) "\0", 1);
+ }
+ SHA1Update(context, finalcount, 8); /* Should cause a SHA1Transform()
+ */
+ for (i = 0; i < 20; i++) {
+ digest[i] = (unsigned char)
+ ((context->state[i >> 2] >> ((3 - (i & 3)) * 8)) &
+ 255);
+ }
+ /* Wipe variables */
+ i = 0;
+ memset(context->buffer, 0, 64);
+ memset(context->state, 0, 20);
+ memset(context->count, 0, 8);
+ memset(finalcount, 0, 8);
+}
+
+/* ===== end - public domain SHA1 implementation ===== */
+
+#endif /* EAP_TLS_FUNCS */
+
+
+#ifdef TEST_MAIN
+
+#include "md5.c"
+
+static int test_eap_fast(void)
+{
+ /* draft-cam-winget-eap-fast-01.txt */
+ const u8 pac_key[] = {
+ 0x0B, 0x97, 0x39, 0x0F, 0x37, 0x51, 0x78, 0x09,
+ 0x81, 0x1E, 0xFD, 0x9C, 0x6E, 0x65, 0x94, 0x2B,
+ 0x63, 0x2C, 0xE9, 0x53, 0x89, 0x38, 0x08, 0xBA,
+ 0x36, 0x0B, 0x03, 0x7C, 0xD1, 0x85, 0xE4, 0x14
+ };
+ const u8 seed[] = {
+ 0x3F, 0xFB, 0x11, 0xC4, 0x6C, 0xBF, 0xA5, 0x7A,
+ 0x54, 0x40, 0xDA, 0xE8, 0x22, 0xD3, 0x11, 0xD3,
+ 0xF7, 0x6D, 0xE4, 0x1D, 0xD9, 0x33, 0xE5, 0x93,
+ 0x70, 0x97, 0xEB, 0xA9, 0xB3, 0x66, 0xF4, 0x2A,
+ 0x00, 0x00, 0x00, 0x02, 0x6A, 0x66, 0x43, 0x2A,
+ 0x8D, 0x14, 0x43, 0x2C, 0xEC, 0x58, 0x2D, 0x2F,
+ 0xC7, 0x9C, 0x33, 0x64, 0xBA, 0x04, 0xAD, 0x3A,
+ 0x52, 0x54, 0xD6, 0xA5, 0x79, 0xAD, 0x1E, 0x00
+ };
+ const u8 master_secret[] = {
+ 0x4A, 0x1A, 0x51, 0x2C, 0x01, 0x60, 0xBC, 0x02,
+ 0x3C, 0xCF, 0xBC, 0x83, 0x3F, 0x03, 0xBC, 0x64,
+ 0x88, 0xC1, 0x31, 0x2F, 0x0B, 0xA9, 0xA2, 0x77,
+ 0x16, 0xA8, 0xD8, 0xE8, 0xBD, 0xC9, 0xD2, 0x29,
+ 0x38, 0x4B, 0x7A, 0x85, 0xBE, 0x16, 0x4D, 0x27,
+ 0x33, 0xD5, 0x24, 0x79, 0x87, 0xB1, 0xC5, 0xA2
+ };
+ const u8 key_block[] = {
+ 0x59, 0x59, 0xBE, 0x8E, 0x41, 0x3A, 0x77, 0x74,
+ 0x8B, 0xB2, 0xE5, 0xD3, 0x60, 0xAC, 0x4D, 0x35,
+ 0xDF, 0xFB, 0xC8, 0x1E, 0x9C, 0x24, 0x9C, 0x8B,
+ 0x0E, 0xC3, 0x1D, 0x72, 0xC8, 0x84, 0x9D, 0x57,
+ 0x48, 0x51, 0x2E, 0x45, 0x97, 0x6C, 0x88, 0x70,
+ 0xBE, 0x5F, 0x01, 0xD3, 0x64, 0xE7, 0x4C, 0xBB,
+ 0x11, 0x24, 0xE3, 0x49, 0xE2, 0x3B, 0xCD, 0xEF,
+ 0x7A, 0xB3, 0x05, 0x39, 0x5D, 0x64, 0x8A, 0x44,
+ 0x11, 0xB6, 0x69, 0x88, 0x34, 0x2E, 0x8E, 0x29,
+ 0xD6, 0x4B, 0x7D, 0x72, 0x17, 0x59, 0x28, 0x05,
+ 0xAF, 0xF9, 0xB7, 0xFF, 0x66, 0x6D, 0xA1, 0x96,
+ 0x8F, 0x0B, 0x5E, 0x06, 0x46, 0x7A, 0x44, 0x84,
+ 0x64, 0xC1, 0xC8, 0x0C, 0x96, 0x44, 0x09, 0x98,
+ 0xFF, 0x92, 0xA8, 0xB4, 0xC6, 0x42, 0x28, 0x71
+ };
+ const u8 sks[] = {
+ 0xD6, 0x4B, 0x7D, 0x72, 0x17, 0x59, 0x28, 0x05,
+ 0xAF, 0xF9, 0xB7, 0xFF, 0x66, 0x6D, 0xA1, 0x96,
+ 0x8F, 0x0B, 0x5E, 0x06, 0x46, 0x7A, 0x44, 0x84,
+ 0x64, 0xC1, 0xC8, 0x0C, 0x96, 0x44, 0x09, 0x98,
+ 0xFF, 0x92, 0xA8, 0xB4, 0xC6, 0x42, 0x28, 0x71
+ };
+ const u8 isk[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+ };
+ const u8 imck[] = {
+ 0x16, 0x15, 0x3C, 0x3F, 0x21, 0x55, 0xEF, 0xD9,
+ 0x7F, 0x34, 0xAE, 0xC8, 0x1A, 0x4E, 0x66, 0x80,
+ 0x4C, 0xC3, 0x76, 0xF2, 0x8A, 0xA9, 0x6F, 0x96,
+ 0xC2, 0x54, 0x5F, 0x8C, 0xAB, 0x65, 0x02, 0xE1,
+ 0x18, 0x40, 0x7B, 0x56, 0xBE, 0xEA, 0xA7, 0xC5,
+ 0x76, 0x5D, 0x8F, 0x0B, 0xC5, 0x07, 0xC6, 0xB9,
+ 0x04, 0xD0, 0x69, 0x56, 0x72, 0x8B, 0x6B, 0xB8,
+ 0x15, 0xEC, 0x57, 0x7B
+ };
+ const u8 msk[] = {
+ 0x4D, 0x83, 0xA9, 0xBE, 0x6F, 0x8A, 0x74, 0xED,
+ 0x6A, 0x02, 0x66, 0x0A, 0x63, 0x4D, 0x2C, 0x33,
+ 0xC2, 0xDA, 0x60, 0x15, 0xC6, 0x37, 0x04, 0x51,
+ 0x90, 0x38, 0x63, 0xDA, 0x54, 0x3E, 0x14, 0xB9,
+ 0x27, 0x99, 0x18, 0x1E, 0x07, 0xBF, 0x0F, 0x5A,
+ 0x5E, 0x3C, 0x32, 0x93, 0x80, 0x8C, 0x6C, 0x49,
+ 0x67, 0xED, 0x24, 0xFE, 0x45, 0x40, 0xA0, 0x59,
+ 0x5E, 0x37, 0xC2, 0xE9, 0xD0, 0x5D, 0x0A, 0xE3
+ };
+ u8 tlv[] = {
+ 0x80, 0x0C, 0x00, 0x38, 0x00, 0x01, 0x01, 0x00,
+ 0xD8, 0x6A, 0x8C, 0x68, 0x3C, 0x32, 0x31, 0xA8,
+ 0x56, 0x63, 0xB6, 0x40, 0x21, 0xFE, 0x21, 0x14,
+ 0x4E, 0xE7, 0x54, 0x20, 0x79, 0x2D, 0x42, 0x62,
+ 0xC9, 0xBF, 0x53, 0x7F, 0x54, 0xFD, 0xAC, 0x58,
+ 0x43, 0x24, 0x6E, 0x30, 0x92, 0x17, 0x6D, 0xCF,
+ 0xE6, 0xE0, 0x69, 0xEB, 0x33, 0x61, 0x6A, 0xCC,
+ 0x05, 0xC5, 0x5B, 0xB7
+ };
+ const u8 compound_mac[] = {
+ 0x43, 0x24, 0x6E, 0x30, 0x92, 0x17, 0x6D, 0xCF,
+ 0xE6, 0xE0, 0x69, 0xEB, 0x33, 0x61, 0x6A, 0xCC,
+ 0x05, 0xC5, 0x5B, 0xB7
+ };
+ u8 buf[512];
+ const u8 *simck, *cmk;
+ int errors = 0;
+
+ printf("EAP-FAST test cases\n");
+
+ printf("- T-PRF (SHA1) test case / master_secret\n");
+ sha1_t_prf(pac_key, sizeof(pac_key), "PAC to master secret label hash",
+ seed, sizeof(seed), buf, sizeof(master_secret));
+ if (memcmp(master_secret, buf, sizeof(master_secret)) != 0) {
+ printf("T-PRF test - FAILED!\n");
+ errors++;
+ }
+
+ printf("- PRF (TLS, SHA1/MD5) test case / key_block\n");
+ tls_prf(master_secret, sizeof(master_secret), "key expansion",
+ seed, sizeof(seed), buf, sizeof(key_block));
+ if (memcmp(key_block, buf, sizeof(key_block)) != 0) {
+ printf("PRF test - FAILED!\n");
+ errors++;
+ }
+
+ printf("- T-PRF (SHA1) test case / IMCK\n");
+ sha1_t_prf(sks, sizeof(sks), "Inner Methods Compound Keys",
+ isk, sizeof(isk), buf, sizeof(imck));
+ if (memcmp(imck, buf, sizeof(imck)) != 0) {
+ printf("T-PRF test - FAILED!\n");
+ errors++;
+ }
+
+ simck = imck;
+ cmk = imck + 40;
+
+ printf("- T-PRF (SHA1) test case / MSK\n");
+ sha1_t_prf(simck, 40, "Session Key Generating Function",
+ "", 0, buf, sizeof(msk));
+ if (memcmp(msk, buf, sizeof(msk)) != 0) {
+ printf("T-PRF test - FAILED!\n");
+ errors++;
+ }
+
+ printf("- Compound MAC test case\n");
+ memset(tlv + sizeof(tlv) - 20, 0, 20);
+ hmac_sha1(cmk, 20, tlv, sizeof(tlv), tlv + sizeof(tlv) - 20);
+ if (memcmp(tlv + sizeof(tlv) - 20, compound_mac, sizeof(compound_mac))
+ != 0) {
+ printf("Compound MAC test - FAILED!\n");
+ errors++;
+ }
+
+ return errors;
+}
+
+
+static u8 key0[] =
+{
+ 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+ 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+ 0x0b, 0x0b, 0x0b, 0x0b
+};
+static u8 data0[] = "Hi There";
+static u8 prf0[] =
+{
+ 0xbc, 0xd4, 0xc6, 0x50, 0xb3, 0x0b, 0x96, 0x84,
+ 0x95, 0x18, 0x29, 0xe0, 0xd7, 0x5f, 0x9d, 0x54,
+ 0xb8, 0x62, 0x17, 0x5e, 0xd9, 0xf0, 0x06, 0x06,
+ 0xe1, 0x7d, 0x8d, 0xa3, 0x54, 0x02, 0xff, 0xee,
+ 0x75, 0xdf, 0x78, 0xc3, 0xd3, 0x1e, 0x0f, 0x88,
+ 0x9f, 0x01, 0x21, 0x20, 0xc0, 0x86, 0x2b, 0xeb,
+ 0x67, 0x75, 0x3e, 0x74, 0x39, 0xae, 0x24, 0x2e,
+ 0xdb, 0x83, 0x73, 0x69, 0x83, 0x56, 0xcf, 0x5a
+};
+
+static u8 key1[] = "Jefe";
+static u8 data1[] = "what do ya want for nothing?";
+static u8 prf1[] =
+{
+ 0x51, 0xf4, 0xde, 0x5b, 0x33, 0xf2, 0x49, 0xad,
+ 0xf8, 0x1a, 0xeb, 0x71, 0x3a, 0x3c, 0x20, 0xf4,
+ 0xfe, 0x63, 0x14, 0x46, 0xfa, 0xbd, 0xfa, 0x58,
+ 0x24, 0x47, 0x59, 0xae, 0x58, 0xef, 0x90, 0x09,
+ 0xa9, 0x9a, 0xbf, 0x4e, 0xac, 0x2c, 0xa5, 0xfa,
+ 0x87, 0xe6, 0x92, 0xc4, 0x40, 0xeb, 0x40, 0x02,
+ 0x3e, 0x7b, 0xab, 0xb2, 0x06, 0xd6, 0x1d, 0xe7,
+ 0xb9, 0x2f, 0x41, 0x52, 0x90, 0x92, 0xb8, 0xfc
+};
+
+
+static u8 key2[] =
+{
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa
+};
+static u8 data2[] =
+{
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd
+};
+static u8 prf2[] =
+{
+ 0xe1, 0xac, 0x54, 0x6e, 0xc4, 0xcb, 0x63, 0x6f,
+ 0x99, 0x76, 0x48, 0x7b, 0xe5, 0xc8, 0x6b, 0xe1,
+ 0x7a, 0x02, 0x52, 0xca, 0x5d, 0x8d, 0x8d, 0xf1,
+ 0x2c, 0xfb, 0x04, 0x73, 0x52, 0x52, 0x49, 0xce,
+ 0x9d, 0xd8, 0xd1, 0x77, 0xea, 0xd7, 0x10, 0xbc,
+ 0x9b, 0x59, 0x05, 0x47, 0x23, 0x91, 0x07, 0xae,
+ 0xf7, 0xb4, 0xab, 0xd4, 0x3d, 0x87, 0xf0, 0xa6,
+ 0x8f, 0x1c, 0xbd, 0x9e, 0x2b, 0x6f, 0x76, 0x07
+};
+
+
+struct passphrase_test {
+ char *passphrase;
+ char *ssid;
+ char psk[32];
+};
+
+static struct passphrase_test passphrase_tests[] =
+{
+ {
+ "password",
+ "IEEE",
+ {
+ 0xf4, 0x2c, 0x6f, 0xc5, 0x2d, 0xf0, 0xeb, 0xef,
+ 0x9e, 0xbb, 0x4b, 0x90, 0xb3, 0x8a, 0x5f, 0x90,
+ 0x2e, 0x83, 0xfe, 0x1b, 0x13, 0x5a, 0x70, 0xe2,
+ 0x3a, 0xed, 0x76, 0x2e, 0x97, 0x10, 0xa1, 0x2e
+ }
+ },
+ {
+ "ThisIsAPassword",
+ "ThisIsASSID",
+ {
+ 0x0d, 0xc0, 0xd6, 0xeb, 0x90, 0x55, 0x5e, 0xd6,
+ 0x41, 0x97, 0x56, 0xb9, 0xa1, 0x5e, 0xc3, 0xe3,
+ 0x20, 0x9b, 0x63, 0xdf, 0x70, 0x7d, 0xd5, 0x08,
+ 0xd1, 0x45, 0x81, 0xf8, 0x98, 0x27, 0x21, 0xaf
+ }
+ },
+ {
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ",
+ {
+ 0xbe, 0xcb, 0x93, 0x86, 0x6b, 0xb8, 0xc3, 0x83,
+ 0x2c, 0xb7, 0x77, 0xc2, 0xf5, 0x59, 0x80, 0x7c,
+ 0x8c, 0x59, 0xaf, 0xcb, 0x6e, 0xae, 0x73, 0x48,
+ 0x85, 0x00, 0x13, 0x00, 0xa9, 0x81, 0xcc, 0x62
+ }
+ },
+};
+
+#define NUM_PASSPHRASE_TESTS \
+(sizeof(passphrase_tests) / sizeof(passphrase_tests[0]))
+
+
+int main(int argc, char *argv[])
+{
+ u8 res[512];
+ int ret = 0, i;
+
+ printf("PRF-SHA1 test cases:\n");
+
+ sha1_prf(key0, sizeof(key0), "prefix", data0, sizeof(data0) - 1,
+ res, sizeof(prf0));
+ if (memcmp(res, prf0, sizeof(prf0)) == 0)
+ printf("Test case 0 - OK\n");
+ else {
+ printf("Test case 0 - FAILED!\n");
+ ret++;
+ }
+
+ sha1_prf(key1, sizeof(key1) - 1, "prefix", data1, sizeof(data1) - 1,
+ res, sizeof(prf1));
+ if (memcmp(res, prf1, sizeof(prf1)) == 0)
+ printf("Test case 1 - OK\n");
+ else {
+ printf("Test case 1 - FAILED!\n");
+ ret++;
+ }
+
+ sha1_prf(key2, sizeof(key2), "prefix", data2, sizeof(data2),
+ res, sizeof(prf2));
+ if (memcmp(res, prf2, sizeof(prf2)) == 0)
+ printf("Test case 2 - OK\n");
+ else {
+ printf("Test case 2 - FAILED!\n");
+ ret++;
+ }
+
+ ret += test_eap_fast();
+
+ printf("PBKDF2-SHA1 Passphrase test cases:\n");
+ for (i = 0; i < NUM_PASSPHRASE_TESTS; i++) {
+ u8 psk[32];
+ struct passphrase_test *test = &passphrase_tests[i];
+ pbkdf2_sha1(test->passphrase,
+ test->ssid, strlen(test->ssid),
+ 4096, psk, 32);
+ if (memcmp(psk, test->psk, 32) == 0)
+ printf("Test case %d - OK\n", i);
+ else {
+ printf("Test case %d - FAILED!\n", i);
+ ret++;
+ }
+ }
+
+ return ret;
+}
+#endif /* TEST_MAIN */
diff --git a/contrib/wpa_supplicant/sha1.h b/contrib/wpa_supplicant/sha1.h
new file mode 100644
index 000000000000..186e3c1c25b1
--- /dev/null
+++ b/contrib/wpa_supplicant/sha1.h
@@ -0,0 +1,50 @@
+#ifndef SHA1_H
+#define SHA1_H
+
+#ifdef EAP_TLS_FUNCS
+
+#include <openssl/sha.h>
+
+#define SHA1_CTX SHA_CTX
+#define SHA1Init SHA1_Init
+#define SHA1Update SHA1_Update
+#define SHA1Final SHA1_Final
+#define SHA1Transform SHA1_Transform
+#define SHA1_MAC_LEN SHA_DIGEST_LENGTH
+
+#else /* EAP_TLS_FUNCS */
+
+#define SHA1_MAC_LEN 20
+
+typedef struct {
+ u32 state[5];
+ u32 count[2];
+ unsigned char buffer[64];
+} SHA1_CTX;
+
+void SHA1Init(SHA1_CTX *context);
+void SHA1Update(SHA1_CTX *context, const void *data, u32 len);
+void SHA1Final(unsigned char digest[20], SHA1_CTX* context);
+void SHA1Transform(u32 state[5], const unsigned char buffer[64]);
+
+#endif /* EAP_TLS_FUNCS */
+
+void sha1_mac(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
+ u8 *mac);
+void hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem,
+ const u8 *addr[], const size_t *len, u8 *mac);
+void hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
+ u8 *mac);
+void 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);
+void 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 tls_prf(const u8 *secret, size_t secret_len, const char *label,
+ const u8 *seed, size_t seed_len, u8 *out, size_t outlen);
+void pbkdf2_sha1(const char *passphrase, const char *ssid, size_t ssid_len,
+ int iterations, u8 *buf, size_t buflen);
+void sha1_transform(u8 *state, u8 data[64]);
+void sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len,
+ u8 *mac);
+
+#endif /* SHA1_H */
diff --git a/contrib/wpa_supplicant/tls.h b/contrib/wpa_supplicant/tls.h
new file mode 100644
index 000000000000..99c9b332360a
--- /dev/null
+++ b/contrib/wpa_supplicant/tls.h
@@ -0,0 +1,312 @@
+#ifndef TLS_H
+#define TLS_H
+
+struct tls_connection;
+
+struct tls_keys {
+ const u8 *master_key;
+ size_t master_key_len;
+ const u8 *client_random;
+ size_t client_random_len;
+ const u8 *server_random;
+ size_t server_random_len;
+};
+
+/**
+ * tls_init - initialize TLS library
+ *
+ * Returns: Context data to be used as @tls_ctx in calls to other functions,
+ * or %NULL on failure.
+ *
+ * Called once during program startup.
+ */
+void * tls_init(void);
+
+/**
+ * tls_deinit - deinitialize TLS library
+ * @tls_ctx: TLS context data from tls_init()
+ *
+ * Called once during program shutdown.
+ */
+void tls_deinit(void *tls_ctx);
+
+/**
+ * tls_get_errors - process pending errors
+ * @tls_ctx: TLS context data from tls_init()
+ *
+ * Returns: Number of found error, 0 if no errors detected.
+ *
+ * Process all pending TLS errors.
+ */
+int tls_get_errors(void *tls_ctx);
+
+/**
+ * tls_connection_init - initialize a new TLS connection
+ * @tls_ctx: TLS context data from tls_init()
+ *
+ * Returns: Connection context data, @conn for other function calls
+ */
+struct tls_connection * tls_connection_init(void *tls_ctx);
+
+/**
+ * tls_connection_deinit - free TLS connection data
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ *
+ * Release all resources allocated for TLS connection.
+ */
+void tls_connection_deinit(void *tls_ctx, struct tls_connection *conn);
+
+/**
+ * tls_connection_established - has the TLS connection been completed?
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ *
+ * Returns: 1 if TLS connection has been completed, 0 if not.
+ */
+int tls_connection_established(void *tls_ctx, struct tls_connection *conn);
+
+/**
+ * tls_connection_shutdown - shutdown TLS connection data.
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ *
+ * Returns: 0 on success, -1 on failure
+ *
+ * Shutdown current TLS connection without releasing all resources. New
+ * connection can be started by using the same @conn without having to call
+ * tls_connection_init() or setting certificates etc. again. The new
+ * connection should try to use session resumption.
+ */
+int tls_connection_shutdown(void *tls_ctx, struct tls_connection *conn);
+
+/**
+ * tls_connection_ca_cert - set trusted CA certificate for TLS connection
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * @ca_cert: File name for CA certificate in PEM or DER format
+ * @subject_match: String to match in the subject of the peer certificate or
+ * %NULL to allow all subjects
+ *
+ * Returns: 0 on success, -1 on failure
+ */
+int tls_connection_ca_cert(void *tls_ctx, struct tls_connection *conn,
+ const char *ca_cert, const char *subject_match);
+
+/**
+ * tls_global_ca_cert - set trusted CA certificate for all TLS connections
+ * @tls_ctx: TLS context data from tls_init()
+ * @ca_cert: File name for CA certificate in PEM or DER format
+ * %NULL to allow all subjects
+ *
+ * Returns: 0 on success, -1 on failure
+ */
+int tls_global_ca_cert(void *tls_ctx, const char *ca_cert);
+
+/**
+ * tls_connection_ca_cert - set trusted CA certificate for TLS connection
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * @verify_peer: 1 = verify peer certificate
+ * @subject_match: String to match in the subject of the peer certificate or
+ * %NULL to allow all subjects
+ *
+ * Returns: 0 on success, -1 on failure
+ */
+int tls_connection_set_verify(void *tls_ctx, struct tls_connection *conn,
+ int verify_peer, const char *subject_match);
+
+/**
+ * tls_connection_client_cert - set client certificate for TLS connection
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * @client_cert: File name for client certificate in PEM or DER format
+ *
+ * Returns: 0 on success, -1 on failure
+ */
+int tls_connection_client_cert(void *tls_ctx, struct tls_connection *conn,
+ const char *client_cert);
+
+/**
+ * tls_global_client_cert - set client certificate for all TLS connections
+ * @tls_ctx: TLS context data from tls_init()
+ * @client_cert: File name for client certificate in PEM or DER format
+ *
+ * Returns: 0 on success, -1 on failure
+ */
+int tls_global_client_cert(void *tls_ctx, const char *client_cert);
+
+/**
+ * tls_connection_private_key - set private key for TLS connection
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * @private_key: File name for client private key in PEM or DER format
+ * @private_key_passwd: Passphrase for decrypted private key, %NULL if no
+ * passphrase is used.
+ *
+ * Returns: 0 on success, -1 on failure
+ */
+int tls_connection_private_key(void *tls_ctx, struct tls_connection *conn,
+ const char *private_key,
+ const char *private_key_passwd);
+
+/**
+ * tls_global_private_key - set private key for all TLS connections
+ * @tls_ctx: TLS context data from tls_init()
+ * @private_key: File name for client private key in PEM or DER format
+ * @private_key_passwd: Passphrase for decrypted private key, %NULL if no
+ * passphrase is used.
+ *
+ * Returns: 0 on success, -1 on failure
+ */
+int tls_global_private_key(void *tls_ctx, const char *private_key,
+ const char *private_key_passwd);
+
+/**
+ * tls_connection_dh - set DH/DSA parameters for TLS connection
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * @dh_file: File name for DH/DSA data in PEM format.
+ *
+ * Returns: 0 on success, -1 on failure
+ */
+int tls_connection_dh(void *tls_ctx, struct tls_connection *conn,
+ const char *dh_file);
+
+/**
+ * tls_connection_get_keys - get master key and random data from TLS connection
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * @keys: Structure of key/random data (filled on success)
+ *
+ * Returns: 0 on success, -1 on failure
+ */
+int tls_connection_get_keys(void *tls_ctx, struct tls_connection *conn,
+ struct tls_keys *keys);
+
+/**
+ * tls_connection_handshake - process TLS handshake (client side)
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * @in_data: Input data from TLS peer
+ * @in_len: Input data length
+ * @out_len: Length of the output buffer.
+ *
+ * Returns: pointer to output data, %NULL on failure
+ *
+ * Caller is responsible for freeing returned output data.
+ */
+u8 * tls_connection_handshake(void *tls_ctx, struct tls_connection *conn,
+ const u8 *in_data, size_t in_len,
+ size_t *out_len);
+
+/**
+ * tls_connection_servr_handshake - process TLS handshake (server side)
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * @in_data: Input data from TLS peer
+ * @in_len: Input data length
+ * @out_len: Length of the output buffer.
+ *
+ * Returns: pointer to output data, %NULL on failure
+ *
+ * Caller is responsible for freeing returned output data.
+ */
+u8 * tls_connection_server_handshake(void *tls_ctx,
+ struct tls_connection *conn,
+ const u8 *in_data, size_t in_len,
+ size_t *out_len);
+
+/**
+ * tls_connection_encrypt - encrypt data into TLS tunnel
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * @in_data: Pointer to plaintext data to be encrypted
+ * @in_len: Input buffer length
+ * @out_data: Pointer to output buffer (encrypted TLS data)
+ * @out_len: Maximum @out_data length
+ *
+ * Returns: Number of bytes written to @out_data, -1 on failure
+ */
+int tls_connection_encrypt(void *tls_ctx, struct tls_connection *conn,
+ u8 *in_data, size_t in_len,
+ u8 *out_data, size_t out_len);
+
+/**
+ * tls_connection_decrypt - decrypt data from TLS tunnel
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_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
+ */
+int tls_connection_decrypt(void *tls_ctx, struct tls_connection *conn,
+ u8 *in_data, size_t in_len,
+ u8 *out_data, size_t out_len);
+
+/**
+ * tls_connection_resumed - was session resumption used
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ *
+ * Returns: 1 if current session used session resumption, 0 if not
+ */
+int tls_connection_resumed(void *tls_ctx, struct tls_connection *conn);
+
+/**
+ * tls_connection_set_master_key - configure master secret for TLS connection
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * @key: TLS pre-master-secret
+ * @key_len: length of @key in bytes
+ *
+ * Returns: 0 on success, -1 on failure
+ */
+int tls_connection_set_master_key(void *ssl_ctx, struct tls_connection *conn,
+ const u8 *key, size_t key_len);
+
+/**
+ * tls_connection_set_anon_dh - configure TLS connection to use anonymous DH
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ *
+ * Returns: 0 on success, -1 on failure
+ *
+ * TODO: consider changing this to more generic routine for configuring allowed
+ * ciphers
+ */
+int tls_connection_set_anon_dh(void *ssl_ctx, struct tls_connection *conn);
+
+/**
+ * tls_get_cipher - get current cipher name
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ *
+ * Returns: 0 on success, -1 on failure
+ *
+ * Get the name of the currently used cipher.
+ */
+int tls_get_cipher(void *ssl_ctx, struct tls_connection *conn,
+ char *buf, size_t buflen);
+
+/**
+ * tls_connection_enable_workaround - enable TLS workaround options
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ *
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is used to enable connection-specific workaround options for
+ * buffer SSL/TLS implementations.
+ */
+int tls_connection_enable_workaround(void *ssl_ctx,
+ struct tls_connection *conn);
+
+int tls_connection_client_hello_ext(void *ssl_ctx, struct tls_connection *conn,
+ int ext_type, const u8 *data,
+ size_t data_len);
+
+#endif /* TLS_H */
diff --git a/contrib/wpa_supplicant/tls_none.c b/contrib/wpa_supplicant/tls_none.c
new file mode 100644
index 000000000000..2b3cafc10415
--- /dev/null
+++ b/contrib/wpa_supplicant/tls_none.c
@@ -0,0 +1,22 @@
+/*
+ * WPA Supplicant / SSL/TLS interface functions for no TLS case
+ * Copyright (c) 2004, Jouni Malinen <jkmaline@cc.hut.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+void * tls_init(void)
+{
+ return (void *) 1;
+}
+
+void tls_deinit(void *ssl_ctx)
+{
+}
diff --git a/contrib/wpa_supplicant/tls_openssl.c b/contrib/wpa_supplicant/tls_openssl.c
new file mode 100644
index 000000000000..097b1c876e34
--- /dev/null
+++ b/contrib/wpa_supplicant/tls_openssl.c
@@ -0,0 +1,876 @@
+/*
+ * WPA Supplicant / SSL/TLS interface functions for openssl
+ * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published 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 <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+#include <openssl/pkcs12.h>
+
+#include "common.h"
+#include "tls.h"
+
+
+struct tls_connection {
+ SSL *ssl;
+ BIO *ssl_in, *ssl_out;
+ char *subject_match;
+};
+
+
+static void ssl_info_cb(const SSL *ssl, int where, int ret)
+{
+ const char *str;
+ int w;
+
+ wpa_printf(MSG_DEBUG, "SSL: (where=0x%x ret=0x%x)", where, ret);
+ w = where & ~SSL_ST_MASK;
+ if (w & SSL_ST_CONNECT)
+ str = "SSL_connect";
+ else if (w & SSL_ST_ACCEPT)
+ str = "SSL_accept";
+ else
+ str = "undefined";
+
+ if (where & SSL_CB_LOOP) {
+ wpa_printf(MSG_DEBUG, "SSL: %s:%s",
+ str, SSL_state_string_long(ssl));
+ } else if (where & SSL_CB_ALERT) {
+ wpa_printf(MSG_INFO, "SSL: SSL3 alert: %s:%s:%s",
+ where & SSL_CB_READ ?
+ "read (authentication server reported an error)" :
+ "write (local SSL3 detected an error)",
+ SSL_alert_type_string_long(ret),
+ SSL_alert_desc_string_long(ret));
+ } else if (where & SSL_CB_EXIT && ret <= 0) {
+ wpa_printf(MSG_DEBUG, "SSL: %s:%s in %s",
+ str, ret == 0 ? "failed" : "error",
+ SSL_state_string_long(ssl));
+ }
+}
+
+
+void * tls_init(void)
+{
+ SSL_CTX *ssl;
+
+ SSL_load_error_strings();
+ SSL_library_init();
+ /* TODO: if /dev/urandom is available, PRNG is seeded automatically.
+ * If this is not the case, random data should be added here. */
+
+#ifdef PKCS12_FUNCS
+ PKCS12_PBE_add();
+#endif /* PKCS12_FUNCS */
+
+ ssl = SSL_CTX_new(TLSv1_method());
+ if (ssl == NULL)
+ return NULL;
+
+ SSL_CTX_set_info_callback(ssl, ssl_info_cb);
+
+ return ssl;
+}
+
+
+void tls_deinit(void *ssl_ctx)
+{
+ SSL_CTX *ssl = ssl_ctx;
+ SSL_CTX_free(ssl);
+ ERR_free_strings();
+ EVP_cleanup();
+}
+
+
+int tls_get_errors(void *ssl_ctx)
+{
+ int count = 0;
+ unsigned long err;
+
+ while ((err = ERR_get_error())) {
+ wpa_printf(MSG_INFO, "TLS - SSL error: %s",
+ ERR_error_string(err, NULL));
+ count++;
+ }
+
+ return count;
+}
+
+struct tls_connection * tls_connection_init(void *ssl_ctx)
+{
+ SSL_CTX *ssl = ssl_ctx;
+ struct tls_connection *conn;
+
+ conn = malloc(sizeof(*conn));
+ if (conn == NULL)
+ return NULL;
+ memset(conn, 0, sizeof(*conn));
+ conn->ssl = SSL_new(ssl);
+ if (conn->ssl == NULL) {
+ wpa_printf(MSG_INFO, "TLS: Failed to initialize new SSL "
+ "connection: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ free(conn);
+ return NULL;
+ }
+
+ SSL_set_options(conn->ssl,
+ SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 |
+ SSL_OP_SINGLE_DH_USE);
+
+ conn->ssl_in = BIO_new(BIO_s_mem());
+ if (!conn->ssl_in) {
+ wpa_printf(MSG_INFO, "SSL: Failed to create a new BIO for "
+ "ssl_in: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ SSL_free(conn->ssl);
+ free(conn);
+ return NULL;
+ }
+
+ conn->ssl_out = BIO_new(BIO_s_mem());
+ if (!conn->ssl_out) {
+ wpa_printf(MSG_INFO, "SSL: Failed to create a new BIO for "
+ "ssl_out: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ SSL_free(conn->ssl);
+ BIO_free(conn->ssl_in);
+ free(conn);
+ return NULL;
+ }
+
+ SSL_set_bio(conn->ssl, conn->ssl_in, conn->ssl_out);
+
+ return conn;
+}
+
+
+void tls_connection_deinit(void *ssl_ctx, struct tls_connection *conn)
+{
+ if (conn == NULL)
+ return;
+ SSL_free(conn->ssl);
+ free(conn->subject_match);
+ free(conn);
+}
+
+
+int tls_connection_established(void *ssl_ctx, struct tls_connection *conn)
+{
+ return conn ? SSL_is_init_finished(conn->ssl) : 0;
+}
+
+
+int tls_connection_shutdown(void *ssl_ctx, struct tls_connection *conn)
+{
+ if (conn == NULL)
+ return -1;
+
+ /* Shutdown previous TLS connection without notifying the peer
+ * because the connection was already terminated in practice
+ * and "close notify" shutdown alert would confuse AS. */
+ SSL_set_quiet_shutdown(conn->ssl, 1);
+ SSL_shutdown(conn->ssl);
+ return 0;
+}
+
+
+static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx)
+{
+ char buf[256];
+ X509 *err_cert;
+ int err, depth;
+ SSL *ssl;
+ struct tls_connection *conn;
+ char *match;
+
+ err_cert = X509_STORE_CTX_get_current_cert(x509_ctx);
+ err = X509_STORE_CTX_get_error(x509_ctx);
+ depth = X509_STORE_CTX_get_error_depth(x509_ctx);
+ ssl = X509_STORE_CTX_get_ex_data(x509_ctx,
+ SSL_get_ex_data_X509_STORE_CTX_idx());
+ X509_NAME_oneline(X509_get_subject_name(err_cert), buf, 256);
+
+ conn = SSL_get_app_data(ssl);
+ match = conn ? conn->subject_match : NULL;
+
+ if (!preverify_ok) {
+ wpa_printf(MSG_WARNING, "TLS: Certificate verification failed,"
+ " error %d (%s) depth %d for '%s'", err,
+ X509_verify_cert_error_string(err), depth, buf);
+ } else {
+ wpa_printf(MSG_DEBUG, "TLS: tls_verify_cb - "
+ "preverify_ok=%d err=%d (%s) depth=%d buf='%s'",
+ preverify_ok, err,
+ X509_verify_cert_error_string(err), depth, buf);
+ if (depth == 0 && match && strstr(buf, match) == NULL) {
+ wpa_printf(MSG_WARNING, "TLS: Subject '%s' did not "
+ "match with '%s'", buf, match);
+ preverify_ok = 0;
+ }
+ }
+
+ return preverify_ok;
+}
+
+
+int tls_connection_ca_cert(void *ssl_ctx, struct tls_connection *conn,
+ const char *ca_cert, const char *subject_match)
+{
+ if (conn == NULL)
+ return -1;
+
+ free(conn->subject_match);
+ conn->subject_match = NULL;
+ if (subject_match) {
+ conn->subject_match = strdup(subject_match);
+ if (conn->subject_match == NULL)
+ return -1;
+ }
+
+ if (ca_cert) {
+ if (SSL_CTX_load_verify_locations(ssl_ctx, ca_cert, NULL) != 1)
+ {
+ wpa_printf(MSG_WARNING, "TLS: Failed to load root "
+ "certificates: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ return -1;
+ } else {
+ wpa_printf(MSG_DEBUG, "TLS: Trusted root "
+ "certificate(s) loaded");
+ tls_get_errors(ssl_ctx);
+ }
+ SSL_set_app_data(conn->ssl, conn);
+ SSL_set_verify(conn->ssl, SSL_VERIFY_PEER, tls_verify_cb);
+ } else {
+ /* No ca_cert configured - do not try to verify server
+ * certificate */
+ SSL_set_verify(conn->ssl, SSL_VERIFY_NONE, NULL);
+ }
+
+ return 0;
+}
+
+
+int tls_global_ca_cert(void *_ssl_ctx, const char *ca_cert)
+{
+ SSL_CTX *ssl_ctx = _ssl_ctx;
+ if (ca_cert) {
+ if (SSL_CTX_load_verify_locations(ssl_ctx, ca_cert, NULL) != 1)
+ {
+ wpa_printf(MSG_WARNING, "TLS: Failed to load root "
+ "certificates: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ return -1;
+ } else {
+ wpa_printf(MSG_DEBUG, "TLS: Trusted root "
+ "certificate(s) loaded");
+ }
+ }
+
+ return 0;
+}
+
+
+int tls_connection_set_verify(void *ssl_ctx, struct tls_connection *conn,
+ int verify_peer, const char *subject_match)
+{
+ if (conn == NULL)
+ return -1;
+
+ free(conn->subject_match);
+ conn->subject_match = NULL;
+ if (subject_match) {
+ conn->subject_match = strdup(subject_match);
+ if (conn->subject_match == NULL)
+ return -1;
+ }
+
+ if (verify_peer) {
+ SSL_set_app_data(conn->ssl, conn);
+ SSL_set_verify(conn->ssl, SSL_VERIFY_PEER |
+ SSL_VERIFY_FAIL_IF_NO_PEER_CERT |
+ SSL_VERIFY_CLIENT_ONCE, tls_verify_cb);
+ } else {
+ SSL_set_verify(conn->ssl, SSL_VERIFY_NONE, NULL);
+ }
+
+ SSL_set_accept_state(conn->ssl);
+
+ return 0;
+}
+
+
+int tls_connection_client_cert(void *ssl_ctx, struct tls_connection *conn,
+ const char *client_cert)
+{
+ if (client_cert == NULL)
+ return 0;
+ if (conn == NULL)
+ return -1;
+
+ if (SSL_use_certificate_file(conn->ssl, client_cert,
+ SSL_FILETYPE_ASN1) != 1 &&
+ SSL_use_certificate_file(conn->ssl, client_cert,
+ SSL_FILETYPE_PEM) != 1) {
+ wpa_printf(MSG_INFO, "TLS: Failed to load client "
+ "certificate: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ return -1;
+ }
+ return 0;
+}
+
+
+int tls_global_client_cert(void *_ssl_ctx, const char *client_cert)
+{
+ SSL_CTX *ssl_ctx = _ssl_ctx;
+ if (client_cert == NULL)
+ return 0;
+
+ if (SSL_CTX_use_certificate_file(ssl_ctx, client_cert,
+ SSL_FILETYPE_ASN1) != 1 &&
+ SSL_CTX_use_certificate_file(ssl_ctx, client_cert,
+ SSL_FILETYPE_PEM) != 1) {
+ wpa_printf(MSG_INFO, "TLS: Failed to load client "
+ "certificate: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ return -1;
+ }
+ return 0;
+}
+
+
+static int tls_passwd_cb(char *buf, int size, int rwflag, void *password)
+{
+ if (password == NULL) {
+ return 0;
+ }
+ strncpy(buf, (char *) password, size);
+ buf[size - 1] = '\0';
+ return strlen(buf);
+}
+
+
+static int tls_read_pkcs12(SSL_CTX *ssl_ctx, SSL *ssl, const char *private_key,
+ const char *passwd)
+{
+#ifdef PKCS12_FUNCS
+ FILE *f;
+ PKCS12 *p12;
+ EVP_PKEY *pkey;
+ X509 *cert;
+ int res = 0;
+
+ f = fopen(private_key, "r");
+ if (f == NULL)
+ return -1;
+
+ p12 = d2i_PKCS12_fp(f, NULL);
+ if (p12 == NULL) {
+ wpa_printf(MSG_DEBUG, "TLS: Failed to read PKCS12 file '%s'",
+ private_key);
+ fclose(f);
+ return -1;
+ }
+ fclose(f);
+
+ pkey = NULL;
+ cert = NULL;
+ if (!PKCS12_parse(p12, passwd, &pkey, &cert, NULL)) {
+ wpa_printf(MSG_DEBUG, "TLS: Failed to parse PKCS12 file '%s': "
+ "%s", private_key,
+ ERR_error_string(ERR_get_error(), NULL));
+ return -1;
+ }
+ wpa_printf(MSG_DEBUG, "TLS: Successfully parsed PKCS12 file '%s'",
+ private_key);
+
+ if (cert) {
+ wpa_printf(MSG_DEBUG, "TLS: Got certificate from PKCS12");
+ if (ssl) {
+ if (SSL_use_certificate(ssl, cert) != 1)
+ res = -1;
+ } else {
+ if (SSL_CTX_use_certificate(ssl_ctx, cert) != 1)
+ res = -1;
+ }
+ X509_free(cert);
+ }
+
+ if (pkey) {
+ wpa_printf(MSG_DEBUG, "TLS: Got private key from PKCS12");
+ if (ssl) {
+ if (SSL_use_PrivateKey(ssl, pkey) != 1)
+ res = -1;
+ } else {
+ if (SSL_CTX_use_PrivateKey(ssl_ctx, pkey) != 1)
+ res = -1;
+ }
+ EVP_PKEY_free(pkey);
+ }
+
+ PKCS12_free(p12);
+
+ return res;
+#else /* PKCS12_FUNCS */
+ wpa_printf(MSG_INFO, "TLS: PKCS12 support disabled - cannot read "
+ "p12/pfx files");
+ return -1;
+#endif /* PKCS12_FUNCS */
+}
+
+
+int tls_connection_private_key(void *_ssl_ctx, struct tls_connection *conn,
+ const char *private_key,
+ const char *private_key_passwd)
+{
+ SSL_CTX *ssl_ctx = _ssl_ctx;
+ char *passwd;
+
+ if (private_key == NULL)
+ return 0;
+ if (conn == NULL)
+ return -1;
+
+ if (private_key_passwd) {
+ passwd = strdup(private_key_passwd);
+ if (passwd == NULL)
+ return -1;
+ } else
+ passwd = NULL;
+
+ SSL_CTX_set_default_passwd_cb(ssl_ctx, tls_passwd_cb);
+ SSL_CTX_set_default_passwd_cb_userdata(ssl_ctx, passwd);
+ if (SSL_use_PrivateKey_file(conn->ssl, private_key,
+ SSL_FILETYPE_ASN1) != 1 &&
+ SSL_use_PrivateKey_file(conn->ssl, private_key,
+ SSL_FILETYPE_PEM) != 1 &&
+ tls_read_pkcs12(ssl_ctx, conn->ssl, private_key, passwd)) {
+ wpa_printf(MSG_INFO, "SSL: Failed to load private key: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ free(passwd);
+ ERR_clear_error();
+ return -1;
+ }
+ ERR_clear_error();
+ free(passwd);
+ SSL_CTX_set_default_passwd_cb(ssl_ctx, NULL);
+
+ if (!SSL_check_private_key(conn->ssl)) {
+ wpa_printf(MSG_INFO, "SSL: Private key failed "
+ "verification: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ return -1;
+ }
+
+ return 0;
+}
+
+
+int tls_global_private_key(void *_ssl_ctx, const char *private_key,
+ const char *private_key_passwd)
+{
+ SSL_CTX *ssl_ctx = _ssl_ctx;
+ char *passwd;
+
+ if (private_key == NULL)
+ return 0;
+
+ passwd = strdup(private_key_passwd);
+ if (passwd == NULL)
+ return -1;
+
+ SSL_CTX_set_default_passwd_cb(ssl_ctx, tls_passwd_cb);
+ SSL_CTX_set_default_passwd_cb_userdata(ssl_ctx, passwd);
+ if (SSL_CTX_use_PrivateKey_file(ssl_ctx, private_key,
+ SSL_FILETYPE_ASN1) != 1 &&
+ SSL_CTX_use_PrivateKey_file(ssl_ctx, private_key,
+ SSL_FILETYPE_PEM) != 1 &&
+ tls_read_pkcs12(ssl_ctx, NULL, private_key, passwd)) {
+ wpa_printf(MSG_INFO, "SSL: Failed to load private key: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ free(passwd);
+ ERR_clear_error();
+ return -1;
+ }
+ free(passwd);
+ ERR_clear_error();
+ SSL_CTX_set_default_passwd_cb(ssl_ctx, NULL);
+
+ if (!SSL_CTX_check_private_key(ssl_ctx)) {
+ wpa_printf(MSG_INFO, "SSL: Private key failed "
+ "verification: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ return -1;
+ }
+
+ return 0;
+}
+
+
+int tls_connection_dh(void *ssl_ctx, struct tls_connection *conn,
+ const char *dh_file)
+{
+#ifdef OPENSSL_NO_DH
+ if (dh_file == NULL)
+ return 0;
+ wpa_printf(MSG_ERROR, "TLS: openssl does not include DH support, but "
+ "dh_file specified");
+ return -1;
+#else /* OPENSSL_NO_DH */
+ DH *dh;
+ BIO *bio;
+
+ if (dh_file == NULL)
+ return 0;
+ if (conn == NULL)
+ return -1;
+
+ bio = BIO_new_file(dh_file, "r");
+ if (bio == NULL) {
+ wpa_printf(MSG_INFO, "TLS: Failed to open DH file '%s': %s",
+ dh_file, ERR_error_string(ERR_get_error(), NULL));
+ return -1;
+ }
+ dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL);
+ BIO_free(bio);
+#ifndef OPENSSL_NO_DSA
+ while (dh == NULL) {
+ DSA *dsa;
+ wpa_printf(MSG_DEBUG, "TLS: Failed to parse DH file '%s': %s -"
+ " trying to parse as DSA params", dh_file,
+ ERR_error_string(ERR_get_error(), NULL));
+ bio = BIO_new_file(dh_file, "r");
+ if (bio == NULL)
+ break;
+ dsa = PEM_read_bio_DSAparams(bio, NULL, NULL, NULL);
+ BIO_free(bio);
+ if (!dsa) {
+ wpa_printf(MSG_DEBUG, "TLS: Failed to parse DSA file "
+ "'%s': %s", dh_file,
+ ERR_error_string(ERR_get_error(), NULL));
+ break;
+ }
+
+ wpa_printf(MSG_DEBUG, "TLS: DH file in DSA param format");
+ dh = DSA_dup_DH(dsa);
+ DSA_free(dsa);
+ if (dh == NULL) {
+ wpa_printf(MSG_INFO, "TLS: Failed to convert DSA "
+ "params into DH params");
+ break;
+ }
+ break;
+ }
+#endif /* !OPENSSL_NO_DSA */
+ if (dh == NULL) {
+ wpa_printf(MSG_INFO, "TLS: Failed to read/parse DH/DSA file "
+ "'%s'", dh_file);
+ return -1;
+ }
+
+ if (SSL_set_tmp_dh(conn->ssl, dh) != 1) {
+ wpa_printf(MSG_INFO, "TLS: Failed to set DH params from '%s': "
+ "%s", dh_file,
+ ERR_error_string(ERR_get_error(), NULL));
+ DH_free(dh);
+ return -1;
+ }
+ DH_free(dh);
+ return 0;
+#endif /* OPENSSL_NO_DH */
+}
+
+
+int tls_connection_get_keys(void *ssl_ctx, struct tls_connection *conn,
+ struct tls_keys *keys)
+{
+ SSL *ssl;
+
+ if (conn == NULL || keys == NULL)
+ return -1;
+ ssl = conn->ssl;
+ if (ssl == NULL || ssl->s3 == NULL || ssl->session == NULL)
+ return -1;
+
+ keys->master_key = ssl->session->master_key;
+ keys->master_key_len = ssl->session->master_key_length;
+ keys->client_random = ssl->s3->client_random;
+ keys->client_random_len = SSL3_RANDOM_SIZE;
+ keys->server_random = ssl->s3->server_random;
+ keys->server_random_len = SSL3_RANDOM_SIZE;
+
+ return 0;
+}
+
+
+u8 * tls_connection_handshake(void *ssl_ctx, struct tls_connection *conn,
+ const u8 *in_data, size_t in_len,
+ size_t *out_len)
+{
+ int res;
+ u8 *out_data;
+
+ if (in_data &&
+ BIO_write(conn->ssl_in, in_data, in_len) < 0) {
+ wpa_printf(MSG_INFO, "TLS: Handshake failed - BIO_write: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ return NULL;
+ }
+
+ res = SSL_connect(conn->ssl);
+ if (res != 1) {
+ int err = SSL_get_error(conn->ssl, res);
+ if (err == SSL_ERROR_WANT_READ)
+ wpa_printf(MSG_DEBUG, "SSL: SSL_connect - want "
+ "more data");
+ else if (err == SSL_ERROR_WANT_WRITE)
+ wpa_printf(MSG_DEBUG, "SSL: SSL_connect - want to "
+ "write");
+ else {
+ wpa_printf(MSG_INFO, "SSL: SSL_connect: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ return NULL;
+ }
+ }
+
+ res = BIO_ctrl_pending(conn->ssl_out);
+ wpa_printf(MSG_DEBUG, "SSL: %d bytes pending from ssl_out", res);
+ out_data = malloc(res == 0 ? 1 : res);
+ if (out_data == NULL) {
+ wpa_printf(MSG_DEBUG, "SSL: Failed to allocate memory for "
+ "handshake output (%d bytes)", res);
+ BIO_reset(conn->ssl_out);
+ *out_len = 0;
+ return NULL;
+ }
+ res = res == 0 ? 0 : BIO_read(conn->ssl_out, out_data, res);
+ if (res < 0) {
+ wpa_printf(MSG_INFO, "TLS: Handshake failed - BIO_read: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ BIO_reset(conn->ssl_out);
+ *out_len = 0;
+ return NULL;
+ }
+ *out_len = res;
+ return out_data;
+}
+
+
+u8 * tls_connection_server_handshake(void *ssl_ctx,
+ struct tls_connection *conn,
+ const u8 *in_data, size_t in_len,
+ size_t *out_len)
+{
+ int res;
+ u8 *out_data;
+ char buf[10];
+
+ if (in_data &&
+ BIO_write(conn->ssl_in, in_data, in_len) < 0) {
+ wpa_printf(MSG_INFO, "TLS: Handshake failed - BIO_write: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ return NULL;
+ }
+
+ res = SSL_read(conn->ssl, buf, sizeof(buf));
+ if (res >= 0) {
+ wpa_printf(MSG_DEBUG, "SSL: Unexpected data from SSL_read "
+ "(res=%d)", res);
+ }
+
+ res = BIO_ctrl_pending(conn->ssl_out);
+ wpa_printf(MSG_DEBUG, "SSL: %d bytes pending from ssl_out", res);
+ out_data = malloc(res == 0 ? 1 : res);
+ if (out_data == NULL) {
+ wpa_printf(MSG_DEBUG, "SSL: Failed to allocate memory for "
+ "handshake output (%d bytes)", res);
+ BIO_reset(conn->ssl_out);
+ *out_len = 0;
+ return NULL;
+ }
+ res = res == 0 ? 0 : BIO_read(conn->ssl_out, out_data, res);
+ if (res < 0) {
+ wpa_printf(MSG_INFO, "TLS: Handshake failed - BIO_read: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ BIO_reset(conn->ssl_out);
+ *out_len = 0;
+ return NULL;
+ }
+ *out_len = res;
+ return out_data;
+}
+
+
+int tls_connection_encrypt(void *ssl_ctx, struct tls_connection *conn,
+ u8 *in_data, size_t in_len,
+ u8 *out_data, size_t out_len)
+{
+ int res;
+
+ if (conn == NULL)
+ return -1;
+
+ BIO_reset(conn->ssl_in);
+ BIO_reset(conn->ssl_out);
+ res = SSL_write(conn->ssl, in_data, in_len);
+ if (res < 0) {
+ wpa_printf(MSG_INFO, "TLS: Encryption failed - SSL_write: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ return res;
+ }
+
+ res = BIO_read(conn->ssl_out, out_data, out_len);
+ if (res < 0) {
+ wpa_printf(MSG_INFO, "TLS: Encryption failed - BIO_read: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ return res;
+ }
+
+ return res;
+}
+
+
+int tls_connection_decrypt(void *ssl_ctx, struct tls_connection *conn,
+ u8 *in_data, size_t in_len,
+ u8 *out_data, size_t out_len)
+{
+ int res;
+
+ res = BIO_write(conn->ssl_in, in_data, in_len);
+ if (res < 0) {
+ wpa_printf(MSG_INFO, "TLS: Decryption failed - BIO_write: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ return res;
+ }
+ BIO_reset(conn->ssl_out);
+
+ res = SSL_read(conn->ssl, out_data, out_len);
+ if (res < 0) {
+ wpa_printf(MSG_INFO, "TLS: Decryption failed - SSL_read: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ return res;
+ }
+
+ return res;
+}
+
+
+int tls_connection_resumed(void *ssl_ctx, struct tls_connection *conn)
+{
+ return conn ? conn->ssl->hit : 0;
+}
+
+
+int tls_connection_set_master_key(void *ssl_ctx, struct tls_connection *conn,
+ const u8 *key, size_t key_len)
+{
+ SSL *ssl;
+
+ if (conn == NULL || key == NULL || key_len > SSL_MAX_MASTER_KEY_LENGTH)
+ return -1;
+ ssl = conn->ssl;
+ if (ssl == NULL || ssl->session == NULL)
+ return -1;
+
+ memcpy(ssl->session->master_key, key, key_len);
+ ssl->session->master_key_length = key_len;
+
+ return 0;
+}
+
+
+int tls_connection_set_anon_dh(void *ssl_ctx, struct tls_connection *conn)
+{
+ if (conn == NULL || conn->ssl == NULL)
+ return -1;
+
+ if (SSL_set_cipher_list(conn->ssl, "ADH-AES128-SHA") != 1) {
+ wpa_printf(MSG_INFO, "TLS: Anon DH configuration failed - %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ return -1;
+ }
+
+ return 0;
+}
+
+
+int tls_get_cipher(void *ssl_ctx, struct tls_connection *conn,
+ char *buf, size_t buflen)
+{
+ const char *name;
+ if (conn == NULL || conn->ssl == NULL)
+ return -1;
+
+ name = SSL_get_cipher(conn->ssl);
+ if (name == NULL)
+ return -1;
+
+ snprintf(buf, buflen, "%s", name);
+ return 0;
+}
+
+
+int tls_connection_enable_workaround(void *ssl_ctx,
+ struct tls_connection *conn)
+{
+ SSL_set_options(conn->ssl, SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS);
+
+ return 0;
+}
+
+
+#ifdef EAP_FAST
+/* ClientHello TLS extensions require a patch to openssl, so this function is
+ * commented out unless explicitly needed for EAP-FAST in order to be able to
+ * build this file with unmodified openssl. */
+int tls_connection_client_hello_ext(void *ssl_ctx, struct tls_connection *conn,
+ int ext_type, const u8 *data,
+ size_t data_len)
+{
+ struct tls_ext_hdr {
+ u16 extensions_len;
+ u16 extension_type;
+ u16 extension_len;
+ } *hdr;
+
+ if (conn == NULL || conn->ssl == NULL)
+ return -1;
+ OPENSSL_free(conn->ssl->hello_extension);
+ if (data == NULL) {
+ conn->ssl->hello_extension = NULL;
+ conn->ssl->hello_extension_len = 0;
+ return 0;
+ }
+ if (data_len == 0) {
+ conn->ssl->hello_extension = OPENSSL_malloc(1);
+ conn->ssl->hello_extension_len = 0;
+ return 0;
+ }
+ conn->ssl->hello_extension = OPENSSL_malloc(sizeof(*hdr) + data_len);
+ if (conn->ssl->hello_extension == NULL)
+ return -1;
+
+ hdr = (struct tls_ext_hdr *) conn->ssl->hello_extension;
+ hdr->extensions_len = host_to_be16(sizeof(*hdr) - 2 + data_len);
+ hdr->extension_type = host_to_be16(ext_type);
+ hdr->extension_len = host_to_be16(data_len);
+ memcpy(hdr + 1, data, data_len);
+ conn->ssl->hello_extension_len = sizeof(*hdr) + data_len;
+
+ return 0;
+}
+#endif /* EAP_FAST */
diff --git a/contrib/wpa_supplicant/todo.txt b/contrib/wpa_supplicant/todo.txt
new file mode 100644
index 000000000000..52f334900098
--- /dev/null
+++ b/contrib/wpa_supplicant/todo.txt
@@ -0,0 +1,54 @@
+To do:
+- add WPA support to Linux Wireless Extensions
+- add support for other drivers
+- implement GUI for WPA Supplicant/Xsupplicant/iwconfig/iwlist
+ (easy to use configuration and network stats, etc.)
+- add support for opportunistic PMKSA caching
+- hostap: try other roaming modes
+ NOTE: current mode (manual roaming) does not really roam at all..
+ Firmware did not notice the current AP disappearing..
+- EAP-MSCHAPv2: add support for password changing
+- add support for WPA with ap_scan=0 (update selected cipher etc. based on
+ AssocInfo; make sure these match with configuration)
+- add driver interface for using wpa_supplicant with wired interface
+ (or a separate program using EAPOL library)
+- wpa_supplicant.conf g+rw so that frontend can change wpa_supplicant.conf
+ and RECONFIG wpa_supplicant (?)
+ (or wpa_supplicant changes .conf and ctrl interface gets support for
+ changing config?)
+- optional security separation (build time option): run EAPOL state machines
+ as non-root (need to add something like socketpair between privileged root
+ process and non-root handler; send EAPOL packets between processes
+ and send keying data from non-root -> privileged)
+ EAPOL-Key processing (WPA & WEP keys) could be in privileged part
+ at least in the beginning; some parts might end up being moved to
+ non-root part eventually
+- consider closing smart card / PCSC connection when EAP-SIM/EAP-AKA
+ authentication has been completed (cache scard data based on serial#(?)
+ and try to optimize next connection if the same card is present for next
+ auth)
+- EAP-AKA: AT_CHECKCODE
+- EAP-SIM/AKA: AT_RESULT_IND
+- abort auth if EAP method initialization fails and there no other
+ accepted methods (i.e., do not send NAK for the same method that just
+ failed)
+- on disconnect event, could try to associate with another AP if one is
+ present in scan results; would need to update scan results periodically..
+- add flag scan_requested and only try to re-associate if this is set when
+ new scan results are received; this would allow background scans without
+ triggering re-assoc..
+- if driver/hw is not WPA2 capable, must remove WPA_PROTO_RSN flag from
+ ssid->proto fields to avoid detecting downgrade attacks when the driver
+ is not reporting RSN IE, but msg 3/4 has one
+- read CA certs from PFX file
+- EAP-SIM/AKA: if SIM reader initialization fails, do not start authentication
+- Cisco AP and non-zero keyidx for unicast -> map to broadcast
+ (actually, this already works with driver_ndis; so maybe just change
+ driver_*.c to do the mapping for drivers that cannot handle non-zero keyidx
+ for unicast)
+- IEEE 802.1X and key update with driver_ndis?? wpa_supplicant did not seem
+ to see unencrypted EAPOL-Key frames at all..
+- update developer.txt to match with current implementation
+ (driver API updates, EAP methods)
+- driver_wext.c and driver that does not support WPA -> fix plaintext, WEP, and
+ IEEE 802.1X operation (e.g., use capabilities to report no support for WPA)
diff --git a/contrib/wpa_supplicant/version.h b/contrib/wpa_supplicant/version.h
new file mode 100644
index 000000000000..d0a1a864fe2b
--- /dev/null
+++ b/contrib/wpa_supplicant/version.h
@@ -0,0 +1,6 @@
+#ifndef VERSION_H
+#define VERSION_H
+
+#define VERSION_STR "0.3.8"
+
+#endif /* VERSION_H */
diff --git a/contrib/wpa_supplicant/wpa.c b/contrib/wpa_supplicant/wpa.c
new file mode 100644
index 000000000000..5d4ce734fe6c
--- /dev/null
+++ b/contrib/wpa_supplicant/wpa.c
@@ -0,0 +1,2445 @@
+/*
+ * WPA Supplicant
+ * Copyright (c) 2003-2005, Jouni Malinen <jkmaline@cc.hut.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published 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 <stdlib.h>
+#include <stdio.h>
+#include <sys/time.h>
+#ifndef CONFIG_NATIVE_WINDOWS
+#include <netinet/in.h>
+#endif /* CONFIG_NATIVE_WINDOWS */
+#include <string.h>
+#include <time.h>
+
+#include "common.h"
+#include "md5.h"
+#include "sha1.h"
+#include "rc4.h"
+#include "aes_wrap.h"
+#include "wpa.h"
+#include "driver.h"
+#include "eloop.h"
+#include "wpa_supplicant.h"
+#include "config.h"
+#include "l2_packet.h"
+#include "eapol_sm.h"
+#include "wpa_supplicant_i.h"
+
+static void rsn_preauth_candidate_process(struct wpa_supplicant *wpa_s);
+
+#define PMKID_CANDIDATE_PRIO_SCAN 1000
+
+/* TODO: make these configurable */
+static const int dot11RSNAConfigPMKLifetime = 43200;
+static const int dot11RSNAConfigPMKReauthThreshold = 70;
+static const int dot11RSNAConfigSATimeout = 60;
+static const int pmksa_cache_max_entries = 32;
+
+static const int WPA_SELECTOR_LEN = 4;
+static const u8 WPA_OUI_TYPE[] = { 0x00, 0x50, 0xf2, 1 };
+static const u16 WPA_VERSION = 1;
+static const u8 WPA_AUTH_KEY_MGMT_NONE[] = { 0x00, 0x50, 0xf2, 0 };
+static const u8 WPA_AUTH_KEY_MGMT_UNSPEC_802_1X[] = { 0x00, 0x50, 0xf2, 1 };
+static const u8 WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X[] = { 0x00, 0x50, 0xf2, 2 };
+static const u8 WPA_CIPHER_SUITE_NONE[] = { 0x00, 0x50, 0xf2, 0 };
+static const u8 WPA_CIPHER_SUITE_WEP40[] = { 0x00, 0x50, 0xf2, 1 };
+static const u8 WPA_CIPHER_SUITE_TKIP[] = { 0x00, 0x50, 0xf2, 2 };
+static const u8 WPA_CIPHER_SUITE_WRAP[] = { 0x00, 0x50, 0xf2, 3 };
+static const u8 WPA_CIPHER_SUITE_CCMP[] = { 0x00, 0x50, 0xf2, 4 };
+static const u8 WPA_CIPHER_SUITE_WEP104[] = { 0x00, 0x50, 0xf2, 5 };
+
+/* WPA IE version 1
+ * 00-50-f2:1 (OUI:OUI type)
+ * 0x01 0x00 (version; little endian)
+ * (all following fields are optional:)
+ * Group Suite Selector (4 octets) (default: TKIP)
+ * Pairwise Suite Count (2 octets, little endian) (default: 1)
+ * Pairwise Suite List (4 * n octets) (default: TKIP)
+ * Authenticated Key Management Suite Count (2 octets, little endian)
+ * (default: 1)
+ * Authenticated Key Management Suite List (4 * n octets)
+ * (default: unspec 802.1x)
+ * WPA Capabilities (2 octets, little endian) (default: 0)
+ */
+
+struct wpa_ie_hdr {
+ u8 elem_id;
+ u8 len;
+ u8 oui[3];
+ u8 oui_type;
+ u16 version;
+} __attribute__ ((packed));
+
+
+static const int RSN_SELECTOR_LEN = 4;
+static const u16 RSN_VERSION = 1;
+static const u8 RSN_AUTH_KEY_MGMT_UNSPEC_802_1X[] = { 0x00, 0x0f, 0xac, 1 };
+static const u8 RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X[] = { 0x00, 0x0f, 0xac, 2 };
+static const u8 RSN_CIPHER_SUITE_NONE[] = { 0x00, 0x0f, 0xac, 0 };
+static const u8 RSN_CIPHER_SUITE_WEP40[] = { 0x00, 0x0f, 0xac, 1 };
+static const u8 RSN_CIPHER_SUITE_TKIP[] = { 0x00, 0x0f, 0xac, 2 };
+static const u8 RSN_CIPHER_SUITE_WRAP[] = { 0x00, 0x0f, 0xac, 3 };
+static const u8 RSN_CIPHER_SUITE_CCMP[] = { 0x00, 0x0f, 0xac, 4 };
+static const u8 RSN_CIPHER_SUITE_WEP104[] = { 0x00, 0x0f, 0xac, 5 };
+
+/* EAPOL-Key Key Data Encapsulation
+ * GroupKey and STAKey require encryption, otherwise, encryption is optional.
+ */
+static const u8 RSN_KEY_DATA_GROUPKEY[] = { 0x00, 0x0f, 0xac, 1 };
+static const u8 RSN_KEY_DATA_STAKEY[] = { 0x00, 0x0f, 0xac, 2 };
+static const u8 RSN_KEY_DATA_MAC_ADDR[] = { 0x00, 0x0f, 0xac, 3 };
+static const u8 RSN_KEY_DATA_PMKID[] = { 0x00, 0x0f, 0xac, 4 };
+
+/* 1/4: PMKID
+ * 2/4: RSN IE
+ * 3/4: one or two RSN IEs + GTK IE (encrypted)
+ * 4/4: empty
+ * 1/2: GTK IE (encrypted)
+ * 2/2: empty
+ */
+
+/* RSN IE version 1
+ * 0x01 0x00 (version; little endian)
+ * (all following fields are optional:)
+ * Group Suite Selector (4 octets) (default: CCMP)
+ * Pairwise Suite Count (2 octets, little endian) (default: 1)
+ * Pairwise Suite List (4 * n octets) (default: CCMP)
+ * Authenticated Key Management Suite Count (2 octets, little endian)
+ * (default: 1)
+ * Authenticated Key Management Suite List (4 * n octets)
+ * (default: unspec 802.1x)
+ * RSN Capabilities (2 octets, little endian) (default: 0)
+ * PMKID Count (2 octets) (default: 0)
+ * PMKID List (16 * n octets)
+ */
+
+struct rsn_ie_hdr {
+ u8 elem_id; /* WLAN_EID_RSN */
+ u8 len;
+ u16 version;
+} __attribute__ ((packed));
+
+
+static int wpa_selector_to_bitfield(u8 *s)
+{
+ if (memcmp(s, WPA_CIPHER_SUITE_NONE, WPA_SELECTOR_LEN) == 0)
+ return WPA_CIPHER_NONE;
+ if (memcmp(s, WPA_CIPHER_SUITE_WEP40, WPA_SELECTOR_LEN) == 0)
+ return WPA_CIPHER_WEP40;
+ if (memcmp(s, WPA_CIPHER_SUITE_TKIP, WPA_SELECTOR_LEN) == 0)
+ return WPA_CIPHER_TKIP;
+ if (memcmp(s, WPA_CIPHER_SUITE_CCMP, WPA_SELECTOR_LEN) == 0)
+ return WPA_CIPHER_CCMP;
+ if (memcmp(s, WPA_CIPHER_SUITE_WEP104, WPA_SELECTOR_LEN) == 0)
+ return WPA_CIPHER_WEP104;
+ return 0;
+}
+
+
+static int wpa_key_mgmt_to_bitfield(u8 *s)
+{
+ if (memcmp(s, WPA_AUTH_KEY_MGMT_UNSPEC_802_1X, WPA_SELECTOR_LEN) == 0)
+ return WPA_KEY_MGMT_IEEE8021X;
+ if (memcmp(s, WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X, WPA_SELECTOR_LEN) ==
+ 0)
+ return WPA_KEY_MGMT_PSK;
+ if (memcmp(s, WPA_AUTH_KEY_MGMT_NONE, WPA_SELECTOR_LEN) == 0)
+ return WPA_KEY_MGMT_WPA_NONE;
+ return 0;
+}
+
+
+static int rsn_selector_to_bitfield(u8 *s)
+{
+ if (memcmp(s, RSN_CIPHER_SUITE_NONE, RSN_SELECTOR_LEN) == 0)
+ return WPA_CIPHER_NONE;
+ if (memcmp(s, RSN_CIPHER_SUITE_WEP40, RSN_SELECTOR_LEN) == 0)
+ return WPA_CIPHER_WEP40;
+ if (memcmp(s, RSN_CIPHER_SUITE_TKIP, RSN_SELECTOR_LEN) == 0)
+ return WPA_CIPHER_TKIP;
+ if (memcmp(s, RSN_CIPHER_SUITE_CCMP, RSN_SELECTOR_LEN) == 0)
+ return WPA_CIPHER_CCMP;
+ if (memcmp(s, RSN_CIPHER_SUITE_WEP104, RSN_SELECTOR_LEN) == 0)
+ return WPA_CIPHER_WEP104;
+ return 0;
+}
+
+
+static int rsn_key_mgmt_to_bitfield(u8 *s)
+{
+ if (memcmp(s, RSN_AUTH_KEY_MGMT_UNSPEC_802_1X, RSN_SELECTOR_LEN) == 0)
+ return WPA_KEY_MGMT_IEEE8021X;
+ if (memcmp(s, RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X, RSN_SELECTOR_LEN) ==
+ 0)
+ return WPA_KEY_MGMT_PSK;
+ return 0;
+}
+
+
+static void rsn_pmkid(u8 *pmk, u8 *aa, u8 *spa, u8 *pmkid)
+{
+ char *title = "PMK Name";
+ const unsigned char *addr[3];
+ const size_t len[3] = { 8, ETH_ALEN, ETH_ALEN };
+ unsigned char hash[SHA1_MAC_LEN];
+
+ addr[0] = (unsigned char *) title;
+ addr[1] = aa;
+ addr[2] = spa;
+
+ hmac_sha1_vector(pmk, PMK_LEN, 3, addr, len, hash);
+ memcpy(pmkid, hash, PMKID_LEN);
+}
+
+
+static void pmksa_cache_set_expiration(struct wpa_supplicant *wpa_s);
+
+
+static void pmksa_cache_free_entry(struct wpa_supplicant *wpa_s,
+ struct rsn_pmksa_cache *entry)
+{
+ free(entry);
+ wpa_s->pmksa_count--;
+ if (wpa_s->cur_pmksa == entry) {
+ wpa_printf(MSG_DEBUG, "RSN: removed current PMKSA entry");
+ /* TODO: should drop PMK and PTK and trigger new key
+ * negotiation */
+ wpa_s->cur_pmksa = NULL;
+ }
+}
+
+
+static void pmksa_cache_expire(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_supplicant *wpa_s = eloop_ctx;
+ time_t now;
+
+ time(&now);
+ while (wpa_s->pmksa && wpa_s->pmksa->expiration <= now) {
+ struct rsn_pmksa_cache *entry = wpa_s->pmksa;
+ wpa_s->pmksa = entry->next;
+ wpa_printf(MSG_DEBUG, "RSN: expired PMKSA cache entry for "
+ MACSTR, MAC2STR(entry->aa));
+ pmksa_cache_free_entry(wpa_s, entry);
+ }
+
+ pmksa_cache_set_expiration(wpa_s);
+}
+
+
+static void pmksa_cache_set_expiration(struct wpa_supplicant *wpa_s)
+{
+ int sec;
+ eloop_cancel_timeout(pmksa_cache_expire, wpa_s, NULL);
+ if (wpa_s->pmksa == NULL)
+ return;
+ sec = wpa_s->pmksa->expiration - time(NULL);
+ if (sec < 0)
+ sec = 0;
+ eloop_register_timeout(sec + 1, 0, pmksa_cache_expire, wpa_s, NULL);
+}
+
+
+static void pmksa_cache_add(struct wpa_supplicant *wpa_s, u8 *pmk,
+ size_t pmk_len, u8 *aa, u8 *spa)
+{
+ struct rsn_pmksa_cache *entry, *pos, *prev;
+
+ if (wpa_s->proto != WPA_PROTO_RSN || pmk_len > PMK_LEN)
+ return;
+
+ entry = malloc(sizeof(*entry));
+ if (entry == NULL)
+ return;
+ memset(entry, 0, sizeof(*entry));
+ memcpy(entry->pmk, pmk, pmk_len);
+ entry->pmk_len = pmk_len;
+ rsn_pmkid(pmk, aa, spa, entry->pmkid);
+ entry->expiration = time(NULL) + dot11RSNAConfigPMKLifetime;
+ entry->akmp = WPA_KEY_MGMT_IEEE8021X;
+ memcpy(entry->aa, aa, ETH_ALEN);
+
+ /* Replace an old entry for the same Authenticator (if found) with the
+ * new entry */
+ pos = wpa_s->pmksa;
+ prev = NULL;
+ while (pos) {
+ if (memcmp(aa, pos->aa, ETH_ALEN) == 0) {
+ if (prev == NULL)
+ wpa_s->pmksa = pos->next;
+ else
+ prev->next = pos->next;
+ pmksa_cache_free_entry(wpa_s, pos);
+ break;
+ }
+ prev = pos;
+ pos = pos->next;
+ }
+
+ if (wpa_s->pmksa_count >= pmksa_cache_max_entries && wpa_s->pmksa) {
+ /* Remove the oldest entry to make room for the new entry */
+ pos = wpa_s->pmksa;
+ wpa_s->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));
+ wpa_drv_remove_pmkid(wpa_s, pos->aa, pos->pmkid);
+ pmksa_cache_free_entry(wpa_s, pos);
+ }
+
+ /* Add the new entry; order by expiration time */
+ pos = wpa_s->pmksa;
+ prev = NULL;
+ while (pos) {
+ if (pos->expiration > entry->expiration)
+ break;
+ prev = pos;
+ pos = pos->next;
+ }
+ if (prev == NULL) {
+ entry->next = wpa_s->pmksa;
+ wpa_s->pmksa = entry;
+ } else {
+ entry->next = prev->next;
+ prev->next = entry;
+ }
+ wpa_s->pmksa_count++;
+ wpa_printf(MSG_DEBUG, "RSN: added PMKSA cache entry for " MACSTR,
+ MAC2STR(entry->aa));
+ wpa_drv_add_pmkid(wpa_s, entry->aa, entry->pmkid);
+}
+
+
+void pmksa_cache_free(struct wpa_supplicant *wpa_s)
+{
+ struct rsn_pmksa_cache *entry, *prev;
+
+ entry = wpa_s->pmksa;
+ wpa_s->pmksa = NULL;
+ while (entry) {
+ prev = entry;
+ entry = entry->next;
+ free(prev);
+ }
+ pmksa_cache_set_expiration(wpa_s);
+ wpa_s->cur_pmksa = NULL;
+}
+
+
+struct rsn_pmksa_cache * pmksa_cache_get(struct wpa_supplicant *wpa_s,
+ u8 *aa, u8 *pmkid)
+{
+ struct rsn_pmksa_cache *entry = wpa_s->pmksa;
+ while (entry) {
+ if ((aa == NULL || memcmp(entry->aa, aa, ETH_ALEN) == 0) &&
+ (pmkid == NULL ||
+ memcmp(entry->pmkid, pmkid, PMKID_LEN) == 0))
+ return entry;
+ entry = entry->next;
+ }
+ return NULL;
+}
+
+
+int pmksa_cache_list(struct wpa_supplicant *wpa_s, char *buf, size_t len)
+{
+ int i, j;
+ char *pos = buf;
+ struct rsn_pmksa_cache *entry;
+ time_t now;
+
+ time(&now);
+ pos += snprintf(pos, buf + len - pos,
+ "Index / AA / PMKID / expiration (in seconds)\n");
+ i = 0;
+ entry = wpa_s->pmksa;
+ while (entry) {
+ i++;
+ pos += snprintf(pos, buf + len - pos, "%d " MACSTR " ",
+ i, MAC2STR(entry->aa));
+ for (j = 0; j < PMKID_LEN; j++)
+ pos += snprintf(pos, buf + len - pos, "%02x",
+ entry->pmkid[j]);
+ pos += snprintf(pos, buf + len - pos, " %d\n",
+ (int) (entry->expiration - now));
+ entry = entry->next;
+ }
+ return pos - buf;
+}
+
+
+void pmksa_candidate_free(struct wpa_supplicant *wpa_s)
+{
+ struct rsn_pmksa_candidate *entry, *prev;
+
+ entry = wpa_s->pmksa_candidates;
+ wpa_s->pmksa_candidates = NULL;
+ while (entry) {
+ prev = entry;
+ entry = entry->next;
+ free(prev);
+ }
+}
+
+
+static int wpa_parse_wpa_ie_wpa(struct wpa_supplicant *wpa_s, u8 *wpa_ie,
+ size_t wpa_ie_len, struct wpa_ie_data *data)
+{
+ struct wpa_ie_hdr *hdr;
+ u8 *pos;
+ int left;
+ int i, count;
+
+ data->proto = WPA_PROTO_WPA;
+ data->pairwise_cipher = WPA_CIPHER_TKIP;
+ data->group_cipher = WPA_CIPHER_TKIP;
+ data->key_mgmt = WPA_KEY_MGMT_IEEE8021X;
+ data->capabilities = 0;
+ data->pmkid = NULL;
+ data->num_pmkid = 0;
+
+ if (wpa_ie_len == 0) {
+ /* No WPA IE - fail silently */
+ return -1;
+ }
+
+ if (wpa_ie_len < sizeof(struct wpa_ie_hdr)) {
+ wpa_printf(MSG_DEBUG, "%s: ie len too short %lu",
+ __func__, (unsigned long) wpa_ie_len);
+ return -1;
+ }
+
+ hdr = (struct wpa_ie_hdr *) wpa_ie;
+
+ if (hdr->elem_id != GENERIC_INFO_ELEM ||
+ hdr->len != wpa_ie_len - 2 ||
+ memcmp(&hdr->oui, WPA_OUI_TYPE, WPA_SELECTOR_LEN) != 0 ||
+ le_to_host16(hdr->version) != WPA_VERSION) {
+ wpa_printf(MSG_DEBUG, "%s: malformed ie or unknown version",
+ __func__);
+ return -1;
+ }
+
+ pos = (u8 *) (hdr + 1);
+ left = wpa_ie_len - sizeof(*hdr);
+
+ if (left >= WPA_SELECTOR_LEN) {
+ data->group_cipher = wpa_selector_to_bitfield(pos);
+ pos += WPA_SELECTOR_LEN;
+ left -= WPA_SELECTOR_LEN;
+ } else if (left > 0) {
+ wpa_printf(MSG_DEBUG, "%s: ie length mismatch, %u too much",
+ __func__, left);
+ return -1;
+ }
+
+ if (left >= 2) {
+ data->pairwise_cipher = 0;
+ count = pos[0] | (pos[1] << 8);
+ pos += 2;
+ left -= 2;
+ if (count == 0 || left < count * WPA_SELECTOR_LEN) {
+ wpa_printf(MSG_DEBUG, "%s: ie count botch (pairwise), "
+ "count %u left %u", __func__, count, left);
+ return -1;
+ }
+ for (i = 0; i < count; i++) {
+ data->pairwise_cipher |= wpa_selector_to_bitfield(pos);
+ pos += WPA_SELECTOR_LEN;
+ left -= WPA_SELECTOR_LEN;
+ }
+ } else if (left == 1) {
+ wpa_printf(MSG_DEBUG, "%s: ie too short (for key mgmt)",
+ __func__);
+ return -1;
+ }
+
+ if (left >= 2) {
+ data->key_mgmt = 0;
+ count = pos[0] | (pos[1] << 8);
+ pos += 2;
+ left -= 2;
+ if (count == 0 || left < count * WPA_SELECTOR_LEN) {
+ wpa_printf(MSG_DEBUG, "%s: ie count botch (key mgmt), "
+ "count %u left %u", __func__, count, left);
+ return -1;
+ }
+ for (i = 0; i < count; i++) {
+ data->key_mgmt |= wpa_key_mgmt_to_bitfield(pos);
+ pos += WPA_SELECTOR_LEN;
+ left -= WPA_SELECTOR_LEN;
+ }
+ } else if (left == 1) {
+ wpa_printf(MSG_DEBUG, "%s: ie too short (for capabilities)",
+ __func__);
+ return -1;
+ }
+
+ if (left >= 2) {
+ data->capabilities = pos[0] | (pos[1] << 8);
+ pos += 2;
+ left -= 2;
+ }
+
+ if (left > 0) {
+ wpa_printf(MSG_DEBUG, "%s: ie has %u trailing bytes",
+ __func__, left);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int wpa_parse_wpa_ie_rsn(struct wpa_supplicant *wpa_s, u8 *rsn_ie,
+ size_t rsn_ie_len, struct wpa_ie_data *data)
+{
+ struct rsn_ie_hdr *hdr;
+ u8 *pos;
+ int left;
+ int i, count;
+
+ data->proto = WPA_PROTO_RSN;
+ data->pairwise_cipher = WPA_CIPHER_CCMP;
+ data->group_cipher = WPA_CIPHER_CCMP;
+ data->key_mgmt = WPA_KEY_MGMT_IEEE8021X;
+ data->capabilities = 0;
+ data->pmkid = NULL;
+ data->num_pmkid = 0;
+
+ if (rsn_ie_len == 0) {
+ /* No RSN IE - fail silently */
+ return -1;
+ }
+
+ if (rsn_ie_len < sizeof(struct rsn_ie_hdr)) {
+ wpa_printf(MSG_DEBUG, "%s: ie len too short %lu",
+ __func__, (unsigned long) rsn_ie_len);
+ return -1;
+ }
+
+ hdr = (struct rsn_ie_hdr *) rsn_ie;
+
+ if (hdr->elem_id != RSN_INFO_ELEM ||
+ hdr->len != rsn_ie_len - 2 ||
+ le_to_host16(hdr->version) != RSN_VERSION) {
+ wpa_printf(MSG_DEBUG, "%s: malformed ie or unknown version",
+ __func__);
+ return -1;
+ }
+
+ pos = (u8 *) (hdr + 1);
+ left = rsn_ie_len - sizeof(*hdr);
+
+ if (left >= RSN_SELECTOR_LEN) {
+ data->group_cipher = rsn_selector_to_bitfield(pos);
+ pos += RSN_SELECTOR_LEN;
+ left -= RSN_SELECTOR_LEN;
+ } else if (left > 0) {
+ wpa_printf(MSG_DEBUG, "%s: ie length mismatch, %u too much",
+ __func__, left);
+ return -1;
+ }
+
+ if (left >= 2) {
+ data->pairwise_cipher = 0;
+ count = pos[0] | (pos[1] << 8);
+ pos += 2;
+ left -= 2;
+ if (count == 0 || left < count * RSN_SELECTOR_LEN) {
+ wpa_printf(MSG_DEBUG, "%s: ie count botch (pairwise), "
+ "count %u left %u", __func__, count, left);
+ return -1;
+ }
+ for (i = 0; i < count; i++) {
+ data->pairwise_cipher |= rsn_selector_to_bitfield(pos);
+ pos += RSN_SELECTOR_LEN;
+ left -= RSN_SELECTOR_LEN;
+ }
+ } else if (left == 1) {
+ wpa_printf(MSG_DEBUG, "%s: ie too short (for key mgmt)",
+ __func__);
+ return -1;
+ }
+
+ if (left >= 2) {
+ data->key_mgmt = 0;
+ count = pos[0] | (pos[1] << 8);
+ pos += 2;
+ left -= 2;
+ if (count == 0 || left < count * RSN_SELECTOR_LEN) {
+ wpa_printf(MSG_DEBUG, "%s: ie count botch (key mgmt), "
+ "count %u left %u", __func__, count, left);
+ return -1;
+ }
+ for (i = 0; i < count; i++) {
+ data->key_mgmt |= rsn_key_mgmt_to_bitfield(pos);
+ pos += RSN_SELECTOR_LEN;
+ left -= RSN_SELECTOR_LEN;
+ }
+ } else if (left == 1) {
+ wpa_printf(MSG_DEBUG, "%s: ie too short (for capabilities)",
+ __func__);
+ return -1;
+ }
+
+ if (left >= 2) {
+ data->capabilities = pos[0] | (pos[1] << 8);
+ pos += 2;
+ left -= 2;
+ }
+
+ if (left >= 2) {
+ data->num_pmkid = pos[0] | (pos[1] << 8);
+ pos += 2;
+ left -= 2;
+ if (left < data->num_pmkid * PMKID_LEN) {
+ wpa_printf(MSG_DEBUG, "%s: PMKID underflow "
+ "(num_pmkid=%d left=%d)",
+ __func__, data->num_pmkid, left);
+ data->num_pmkid = 0;
+ } else {
+ data->pmkid = pos;
+ pos += data->num_pmkid * PMKID_LEN;
+ left -= data->num_pmkid * PMKID_LEN;
+ }
+ }
+
+ if (left > 0) {
+ wpa_printf(MSG_DEBUG, "%s: ie has %u trailing bytes - ignored",
+ __func__, left);
+ }
+
+ return 0;
+}
+
+
+int wpa_parse_wpa_ie(struct wpa_supplicant *wpa_s, u8 *wpa_ie,
+ size_t wpa_ie_len, struct wpa_ie_data *data)
+{
+ if (wpa_ie_len >= 1 && wpa_ie[0] == RSN_INFO_ELEM)
+ return wpa_parse_wpa_ie_rsn(wpa_s, wpa_ie, wpa_ie_len, data);
+ else
+ return wpa_parse_wpa_ie_wpa(wpa_s, wpa_ie, wpa_ie_len, data);
+}
+
+
+static int wpa_gen_wpa_ie_wpa(struct wpa_supplicant *wpa_s, u8 *wpa_ie)
+{
+ u8 *pos;
+ struct wpa_ie_hdr *hdr;
+
+ hdr = (struct wpa_ie_hdr *) wpa_ie;
+ hdr->elem_id = GENERIC_INFO_ELEM;
+ memcpy(&hdr->oui, WPA_OUI_TYPE, WPA_SELECTOR_LEN);
+ hdr->version = host_to_le16(WPA_VERSION);
+ pos = (u8 *) (hdr + 1);
+
+ if (wpa_s->group_cipher == WPA_CIPHER_CCMP) {
+ memcpy(pos, WPA_CIPHER_SUITE_CCMP, WPA_SELECTOR_LEN);
+ } else if (wpa_s->group_cipher == WPA_CIPHER_TKIP) {
+ memcpy(pos, WPA_CIPHER_SUITE_TKIP, WPA_SELECTOR_LEN);
+ } else if (wpa_s->group_cipher == WPA_CIPHER_WEP104) {
+ memcpy(pos, WPA_CIPHER_SUITE_WEP104, WPA_SELECTOR_LEN);
+ } else if (wpa_s->group_cipher == WPA_CIPHER_WEP40) {
+ memcpy(pos, WPA_CIPHER_SUITE_WEP40, WPA_SELECTOR_LEN);
+ } else {
+ wpa_printf(MSG_WARNING, "Invalid group cipher (%d).",
+ wpa_s->group_cipher);
+ return -1;
+ }
+ pos += WPA_SELECTOR_LEN;
+
+ *pos++ = 1;
+ *pos++ = 0;
+ if (wpa_s->pairwise_cipher == WPA_CIPHER_CCMP) {
+ memcpy(pos, WPA_CIPHER_SUITE_CCMP, WPA_SELECTOR_LEN);
+ } else if (wpa_s->pairwise_cipher == WPA_CIPHER_TKIP) {
+ memcpy(pos, WPA_CIPHER_SUITE_TKIP, WPA_SELECTOR_LEN);
+ } else if (wpa_s->pairwise_cipher == WPA_CIPHER_NONE) {
+ memcpy(pos, WPA_CIPHER_SUITE_NONE, WPA_SELECTOR_LEN);
+ } else {
+ wpa_printf(MSG_WARNING, "Invalid pairwise cipher (%d).",
+ wpa_s->pairwise_cipher);
+ return -1;
+ }
+ pos += WPA_SELECTOR_LEN;
+
+ *pos++ = 1;
+ *pos++ = 0;
+ if (wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X) {
+ memcpy(pos, WPA_AUTH_KEY_MGMT_UNSPEC_802_1X, WPA_SELECTOR_LEN);
+ } else if (wpa_s->key_mgmt == WPA_KEY_MGMT_PSK) {
+ memcpy(pos, WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X,
+ WPA_SELECTOR_LEN);
+ } else if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE) {
+ memcpy(pos, WPA_AUTH_KEY_MGMT_NONE, WPA_SELECTOR_LEN);
+ } else {
+ wpa_printf(MSG_WARNING, "Invalid key management type (%d).",
+ wpa_s->key_mgmt);
+ return -1;
+ }
+ pos += WPA_SELECTOR_LEN;
+
+ /* WPA Capabilities; use defaults, so no need to include it */
+
+ hdr->len = (pos - wpa_ie) - 2;
+
+ return pos - wpa_ie;
+}
+
+
+static int wpa_gen_wpa_ie_rsn(struct wpa_supplicant *wpa_s, u8 *rsn_ie)
+{
+ u8 *pos;
+ struct rsn_ie_hdr *hdr;
+
+ hdr = (struct rsn_ie_hdr *) rsn_ie;
+ hdr->elem_id = RSN_INFO_ELEM;
+ hdr->version = host_to_le16(RSN_VERSION);
+ pos = (u8 *) (hdr + 1);
+
+ if (wpa_s->group_cipher == WPA_CIPHER_CCMP) {
+ memcpy(pos, RSN_CIPHER_SUITE_CCMP, RSN_SELECTOR_LEN);
+ } else if (wpa_s->group_cipher == WPA_CIPHER_TKIP) {
+ memcpy(pos, RSN_CIPHER_SUITE_TKIP, RSN_SELECTOR_LEN);
+ } else if (wpa_s->group_cipher == WPA_CIPHER_WEP104) {
+ memcpy(pos, RSN_CIPHER_SUITE_WEP104, RSN_SELECTOR_LEN);
+ } else if (wpa_s->group_cipher == WPA_CIPHER_WEP40) {
+ memcpy(pos, RSN_CIPHER_SUITE_WEP40, RSN_SELECTOR_LEN);
+ } else {
+ wpa_printf(MSG_WARNING, "Invalid group cipher (%d).",
+ wpa_s->group_cipher);
+ return -1;
+ }
+ pos += RSN_SELECTOR_LEN;
+
+ *pos++ = 1;
+ *pos++ = 0;
+ if (wpa_s->pairwise_cipher == WPA_CIPHER_CCMP) {
+ memcpy(pos, RSN_CIPHER_SUITE_CCMP, RSN_SELECTOR_LEN);
+ } else if (wpa_s->pairwise_cipher == WPA_CIPHER_TKIP) {
+ memcpy(pos, RSN_CIPHER_SUITE_TKIP, RSN_SELECTOR_LEN);
+ } else if (wpa_s->pairwise_cipher == WPA_CIPHER_NONE) {
+ memcpy(pos, RSN_CIPHER_SUITE_NONE, RSN_SELECTOR_LEN);
+ } else {
+ wpa_printf(MSG_WARNING, "Invalid pairwise cipher (%d).",
+ wpa_s->pairwise_cipher);
+ return -1;
+ }
+ pos += RSN_SELECTOR_LEN;
+
+ *pos++ = 1;
+ *pos++ = 0;
+ if (wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X) {
+ memcpy(pos, RSN_AUTH_KEY_MGMT_UNSPEC_802_1X, RSN_SELECTOR_LEN);
+ } else if (wpa_s->key_mgmt == WPA_KEY_MGMT_PSK) {
+ memcpy(pos, RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X,
+ RSN_SELECTOR_LEN);
+ } else {
+ wpa_printf(MSG_WARNING, "Invalid key management type (%d).",
+ wpa_s->key_mgmt);
+ return -1;
+ }
+ pos += RSN_SELECTOR_LEN;
+
+ /* RSN Capabilities */
+ *pos++ = 0;
+ *pos++ = 0;
+
+ if (wpa_s->cur_pmksa) {
+ /* PMKID Count (2 octets, little endian) */
+ *pos++ = 1;
+ *pos++ = 0;
+ /* PMKID */
+ memcpy(pos, wpa_s->cur_pmksa->pmkid, PMKID_LEN);
+ pos += PMKID_LEN;
+ }
+
+ hdr->len = (pos - rsn_ie) - 2;
+
+ return pos - rsn_ie;
+}
+
+
+int wpa_gen_wpa_ie(struct wpa_supplicant *wpa_s, u8 *wpa_ie)
+{
+ if (wpa_s->proto == WPA_PROTO_RSN)
+ return wpa_gen_wpa_ie_rsn(wpa_s, wpa_ie);
+ else
+ return wpa_gen_wpa_ie_wpa(wpa_s, wpa_ie);
+}
+
+
+static void wpa_pmk_to_ptk(u8 *pmk, size_t pmk_len, u8 *addr1, u8 *addr2,
+ u8 *nonce1, u8 *nonce2, u8 *ptk, size_t ptk_len)
+{
+ u8 data[2 * ETH_ALEN + 2 * 32];
+
+ /* PTK = PRF-X(PMK, "Pairwise key expansion",
+ * Min(AA, SA) || Max(AA, SA) ||
+ * Min(ANonce, SNonce) || Max(ANonce, SNonce)) */
+
+ if (memcmp(addr1, addr2, ETH_ALEN) < 0) {
+ memcpy(data, addr1, ETH_ALEN);
+ memcpy(data + ETH_ALEN, addr2, ETH_ALEN);
+ } else {
+ memcpy(data, addr2, ETH_ALEN);
+ memcpy(data + ETH_ALEN, addr1, ETH_ALEN);
+ }
+
+ if (memcmp(nonce1, nonce2, 32) < 0) {
+ memcpy(data + 2 * ETH_ALEN, nonce1, 32);
+ memcpy(data + 2 * ETH_ALEN + 32, nonce2, 32);
+ } else {
+ memcpy(data + 2 * ETH_ALEN, nonce2, 32);
+ memcpy(data + 2 * ETH_ALEN + 32, nonce1, 32);
+ }
+
+ sha1_prf(pmk, pmk_len, "Pairwise key expansion", data, sizeof(data),
+ ptk, ptk_len);
+
+ wpa_hexdump_key(MSG_DEBUG, "WPA: PMK", pmk, pmk_len);
+ wpa_hexdump_key(MSG_DEBUG, "WPA: PTK", ptk, ptk_len);
+}
+
+
+struct wpa_ssid * wpa_supplicant_get_ssid(struct wpa_supplicant *wpa_s)
+{
+ struct wpa_ssid *entry;
+ u8 ssid[MAX_SSID_LEN];
+ int ssid_len;
+ u8 bssid[ETH_ALEN];
+
+ ssid_len = wpa_drv_get_ssid(wpa_s, ssid);
+ if (ssid_len < 0) {
+ wpa_printf(MSG_WARNING, "Could not read SSID from driver.");
+ return NULL;
+ }
+
+ if (wpa_drv_get_bssid(wpa_s, bssid) < 0) {
+ wpa_printf(MSG_WARNING, "Could not read BSSID from driver.");
+ return NULL;
+ }
+
+ entry = wpa_s->conf->ssid;
+ while (entry) {
+ if (ssid_len == entry->ssid_len &&
+ memcmp(ssid, entry->ssid, ssid_len) == 0 &&
+ (!entry->bssid_set ||
+ memcmp(bssid, entry->bssid, ETH_ALEN) == 0))
+ return entry;
+ entry = entry->next;
+ }
+
+ return NULL;
+}
+
+
+static void wpa_eapol_key_mic(u8 *key, int ver, u8 *buf, size_t len, u8 *mic)
+{
+ if (ver == WPA_KEY_INFO_TYPE_HMAC_MD5_RC4) {
+ hmac_md5(key, 16, buf, len, mic);
+ } else if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
+ u8 hash[SHA1_MAC_LEN];
+ hmac_sha1(key, 16, buf, len, hash);
+ memcpy(mic, hash, MD5_MAC_LEN);
+ }
+}
+
+
+void wpa_supplicant_key_request(struct wpa_supplicant *wpa_s,
+ int error, int pairwise)
+{
+ int rlen;
+ struct ieee802_1x_hdr *hdr;
+ struct wpa_eapol_key *reply;
+ unsigned char *rbuf;
+ struct l2_ethhdr *ethhdr;
+ int key_info, ver;
+ u8 bssid[ETH_ALEN];
+
+ if (wpa_s->pairwise_cipher == WPA_CIPHER_CCMP)
+ ver = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES;
+ else
+ ver = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4;
+
+ if (wpa_drv_get_bssid(wpa_s, bssid) < 0) {
+ wpa_printf(MSG_WARNING, "Failed to read BSSID for EAPOL-Key "
+ "request");
+ return;
+ }
+
+ rlen = sizeof(*ethhdr) + sizeof(*hdr) + sizeof(*reply);
+ rbuf = malloc(rlen);
+ if (rbuf == NULL)
+ return;
+
+ memset(rbuf, 0, rlen);
+ ethhdr = (struct l2_ethhdr *) rbuf;
+ memcpy(ethhdr->h_dest, bssid, ETH_ALEN);
+ memcpy(ethhdr->h_source, wpa_s->own_addr, ETH_ALEN);
+ ethhdr->h_proto = htons(ETH_P_EAPOL);
+
+ hdr = (struct ieee802_1x_hdr *) (ethhdr + 1);
+ hdr->version = wpa_s->conf->eapol_version;
+ hdr->type = IEEE802_1X_TYPE_EAPOL_KEY;
+ hdr->length = htons(sizeof(*reply));
+
+ reply = (struct wpa_eapol_key *) (hdr + 1);
+ reply->type = wpa_s->proto == WPA_PROTO_RSN ?
+ EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA;
+ key_info = WPA_KEY_INFO_REQUEST | ver;
+ if (wpa_s->ptk_set)
+ key_info |= WPA_KEY_INFO_MIC;
+ if (error)
+ key_info |= WPA_KEY_INFO_ERROR;
+ if (pairwise)
+ key_info |= WPA_KEY_INFO_KEY_TYPE;
+ reply->key_info = host_to_be16(key_info);
+ reply->key_length = 0;
+ memcpy(reply->replay_counter, wpa_s->request_counter,
+ WPA_REPLAY_COUNTER_LEN);
+ inc_byte_array(wpa_s->request_counter, WPA_REPLAY_COUNTER_LEN);
+
+ reply->key_data_length = host_to_be16(0);
+
+ if (key_info & WPA_KEY_INFO_MIC) {
+ wpa_eapol_key_mic(wpa_s->ptk.mic_key, ver, (u8 *) hdr,
+ rlen - sizeof(*ethhdr), reply->key_mic);
+ }
+
+ wpa_printf(MSG_INFO, "WPA: Sending EAPOL-Key Request (error=%d "
+ "pairwise=%d ptk_set=%d len=%d)",
+ error, pairwise, wpa_s->ptk_set, rlen);
+ wpa_hexdump(MSG_MSGDUMP, "WPA: TX EAPOL-Key Request", rbuf, rlen);
+ l2_packet_send(wpa_s->l2, rbuf, rlen);
+ eapol_sm_notify_tx_eapol_key(wpa_s->eapol);
+ free(rbuf);
+}
+
+
+static void wpa_supplicant_process_1_of_4(struct wpa_supplicant *wpa_s,
+ unsigned char *src_addr,
+ struct wpa_eapol_key *key, int ver)
+{
+ int rlen;
+ struct ieee802_1x_hdr *hdr;
+ struct wpa_eapol_key *reply;
+ unsigned char *rbuf;
+ struct l2_ethhdr *ethhdr;
+ struct wpa_ssid *ssid;
+ struct wpa_ptk *ptk;
+ u8 buf[8], wpa_ie_buf[80], *wpa_ie, *pmkid = NULL;
+ int wpa_ie_len;
+ int abort_cached = 0;
+
+ wpa_s->wpa_state = WPA_4WAY_HANDSHAKE;
+ wpa_printf(MSG_DEBUG, "WPA: RX message 1 of 4-Way Handshake from "
+ MACSTR " (ver=%d)", MAC2STR(src_addr), ver);
+
+ ssid = wpa_supplicant_get_ssid(wpa_s);
+ if (ssid == NULL) {
+ wpa_printf(MSG_WARNING, "WPA: No SSID info found (msg 1 of "
+ "4).");
+ return;
+ }
+
+ if (wpa_s->proto == WPA_PROTO_RSN) {
+ /* RSN: msg 1/4 should contain PMKID for the selected PMK */
+ u8 *pos = (u8 *) (key + 1);
+ u8 *end = pos + be_to_host16(key->key_data_length);
+ wpa_hexdump(MSG_DEBUG, "RSN: msg 1/4 key data",
+ pos, be_to_host16(key->key_data_length));
+ while (pos + 1 < end) {
+ if (pos + 2 + pos[1] > end) {
+ wpa_printf(MSG_DEBUG, "RSN: key data "
+ "underflow (ie=%d len=%d)",
+ pos[0], pos[1]);
+ break;
+ }
+ if (pos[0] == GENERIC_INFO_ELEM &&
+ pos + 1 + RSN_SELECTOR_LEN < end &&
+ pos[1] >= RSN_SELECTOR_LEN + PMKID_LEN &&
+ memcmp(pos + 2, RSN_KEY_DATA_PMKID,
+ RSN_SELECTOR_LEN) == 0) {
+ pmkid = pos + 2 + RSN_SELECTOR_LEN;
+ wpa_hexdump(MSG_DEBUG, "RSN: PMKID from "
+ "Authenticator", pmkid, PMKID_LEN);
+ break;
+ } else if (pos[0] == GENERIC_INFO_ELEM &&
+ pos[1] == 0)
+ break;
+ pos += 2 + pos[1];
+ }
+ }
+
+ if (wpa_s->assoc_wpa_ie) {
+ /* The driver reported a WPA IE that may be different from the
+ * one that the Supplicant would use. Message 2/4 has to use
+ * the exact copy of the WPA IE from the Association Request,
+ * so use the value reported by the driver. */
+ wpa_ie = wpa_s->assoc_wpa_ie;
+ wpa_ie_len = wpa_s->assoc_wpa_ie_len;
+ } else {
+ wpa_ie = wpa_ie_buf;
+ wpa_ie_len = wpa_gen_wpa_ie(wpa_s, wpa_ie);
+ if (wpa_ie_len < 0) {
+ wpa_printf(MSG_WARNING, "WPA: Failed to generate "
+ "WPA IE (for msg 2 of 4).");
+ return;
+ }
+ wpa_hexdump(MSG_DEBUG, "WPA: WPA IE for msg 2/4",
+ wpa_ie, wpa_ie_len);
+ }
+
+ rlen = sizeof(*ethhdr) + sizeof(*hdr) + sizeof(*reply) + wpa_ie_len;
+ rbuf = malloc(rlen);
+ if (rbuf == NULL)
+ return;
+
+ memset(rbuf, 0, rlen);
+ ethhdr = (struct l2_ethhdr *) rbuf;
+ memcpy(ethhdr->h_dest, src_addr, ETH_ALEN);
+ memcpy(ethhdr->h_source, wpa_s->own_addr, ETH_ALEN);
+ ethhdr->h_proto = htons(ETH_P_EAPOL);
+
+ hdr = (struct ieee802_1x_hdr *) (ethhdr + 1);
+ hdr->version = wpa_s->conf->eapol_version;
+ hdr->type = IEEE802_1X_TYPE_EAPOL_KEY;
+ hdr->length = htons(sizeof(*reply) + wpa_ie_len);
+
+ reply = (struct wpa_eapol_key *) (hdr + 1);
+ reply->type = wpa_s->proto == WPA_PROTO_RSN ?
+ EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA;
+ reply->key_info = host_to_be16(ver | WPA_KEY_INFO_KEY_TYPE |
+ WPA_KEY_INFO_MIC);
+ reply->key_length = key->key_length;
+ memcpy(reply->replay_counter, key->replay_counter,
+ WPA_REPLAY_COUNTER_LEN);
+
+ reply->key_data_length = host_to_be16(wpa_ie_len);
+ memcpy(reply + 1, wpa_ie, wpa_ie_len);
+
+ if (wpa_s->renew_snonce) {
+ if (hostapd_get_rand(wpa_s->snonce, WPA_NONCE_LEN)) {
+ wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to get "
+ "random data for SNonce");
+ return;
+ }
+ wpa_s->renew_snonce = 0;
+ wpa_hexdump(MSG_DEBUG, "WPA: Renewed SNonce",
+ wpa_s->snonce, WPA_NONCE_LEN);
+ }
+ memcpy(reply->key_nonce, wpa_s->snonce, WPA_NONCE_LEN);
+ ptk = &wpa_s->tptk;
+ memcpy(wpa_s->anonce, key->key_nonce, WPA_NONCE_LEN);
+ if (pmkid && !wpa_s->cur_pmksa) {
+ /* When using drivers that generate RSN IE, wpa_supplicant may
+ * not have enough time to get the association information
+ * event before receiving this 1/4 message, so try to find a
+ * matching PMKSA cache entry here. */
+ wpa_s->cur_pmksa = pmksa_cache_get(wpa_s, src_addr, pmkid);
+ if (wpa_s->cur_pmksa) {
+ wpa_printf(MSG_DEBUG, "RSN: found matching PMKID from "
+ "PMKSA cache");
+ } else {
+ wpa_printf(MSG_DEBUG, "RSN: no matching PMKID found");
+ abort_cached = 1;
+ }
+ }
+
+ if (pmkid && wpa_s->cur_pmksa &&
+ memcmp(pmkid, wpa_s->cur_pmksa->pmkid, PMKID_LEN) == 0) {
+ wpa_hexdump(MSG_DEBUG, "RSN: matched PMKID", pmkid, PMKID_LEN);
+ memcpy(wpa_s->pmk, wpa_s->cur_pmksa->pmk, PMK_LEN);
+ wpa_hexdump_key(MSG_DEBUG, "RSN: PMK from PMKSA cache",
+ wpa_s->pmk, PMK_LEN);
+ eapol_sm_notify_cached(wpa_s->eapol);
+ } else if (wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X && wpa_s->eapol) {
+ int res, pmk_len;
+ pmk_len = PMK_LEN;
+ res = eapol_sm_get_key(wpa_s->eapol, wpa_s->pmk, PMK_LEN);
+#ifdef EAP_LEAP
+ if (res) {
+ res = eapol_sm_get_key(wpa_s->eapol, wpa_s->pmk, 16);
+ pmk_len = 16;
+ }
+#endif /* EAP_LEAP */
+ if (res == 0) {
+ wpa_hexdump_key(MSG_DEBUG, "WPA: PMK from EAPOL state "
+ "machines", wpa_s->pmk, pmk_len);
+ wpa_s->pmk_len = pmk_len;
+ pmksa_cache_add(wpa_s, wpa_s->pmk, pmk_len, src_addr,
+ wpa_s->own_addr);
+ if (!wpa_s->cur_pmksa && pmkid &&
+ pmksa_cache_get(wpa_s, src_addr, pmkid)) {
+ wpa_printf(MSG_DEBUG, "RSN: the new PMK "
+ "matches with the PMKID");
+ abort_cached = 0;
+ }
+ } else {
+ wpa_msg(wpa_s, MSG_WARNING,
+ "WPA: Failed to get master session key from "
+ "EAPOL state machines");
+ wpa_msg(wpa_s, MSG_WARNING,
+ "WPA: Key handshake aborted");
+ if (wpa_s->cur_pmksa) {
+ wpa_printf(MSG_DEBUG, "RSN: Cancelled PMKSA "
+ "caching attempt");
+ wpa_s->cur_pmksa = NULL;
+ abort_cached = 1;
+ } else {
+ return;
+ }
+ }
+#ifdef CONFIG_XSUPPLICANT_IFACE
+ } else if (wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X &&
+ !wpa_s->ext_pmk_received) {
+ wpa_printf(MSG_INFO, "WPA: Master session has not yet "
+ "been received from the external IEEE "
+ "802.1X Supplicant - ignoring WPA "
+ "EAPOL-Key frame");
+ return;
+#endif /* CONFIG_XSUPPLICANT_IFACE */
+ }
+
+ if (abort_cached && wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X) {
+ /* Send EAPOL-Start to trigger full EAP authentication. */
+ wpa_printf(MSG_DEBUG, "RSN: no PMKSA entry found - trigger "
+ "full EAP authenication");
+ wpa_eapol_send(wpa_s, IEEE802_1X_TYPE_EAPOL_START,
+ (u8 *) "", 0);
+ return;
+ }
+
+ wpa_pmk_to_ptk(wpa_s->pmk, wpa_s->pmk_len, wpa_s->own_addr, src_addr,
+ wpa_s->snonce, key->key_nonce,
+ (u8 *) ptk, sizeof(*ptk));
+ /* Supplicant: swap tx/rx Mic keys */
+ memcpy(buf, ptk->u.auth.tx_mic_key, 8);
+ memcpy(ptk->u.auth.tx_mic_key, ptk->u.auth.rx_mic_key, 8);
+ memcpy(ptk->u.auth.rx_mic_key, buf, 8);
+ wpa_s->tptk_set = 1;
+ wpa_eapol_key_mic(wpa_s->tptk.mic_key, ver, (u8 *) hdr,
+ rlen - sizeof(*ethhdr), reply->key_mic);
+ wpa_hexdump(MSG_DEBUG, "WPA: EAPOL-Key MIC", reply->key_mic, 16);
+
+ wpa_printf(MSG_DEBUG, "WPA: Sending EAPOL-Key 2/4");
+ wpa_hexdump(MSG_MSGDUMP, "WPA: TX EAPOL-Key 2/4", rbuf, rlen);
+ l2_packet_send(wpa_s->l2, rbuf, rlen);
+ eapol_sm_notify_tx_eapol_key(wpa_s->eapol);
+ free(rbuf);
+}
+
+
+static void wpa_supplicant_key_neg_complete(struct wpa_supplicant *wpa_s,
+ u8 *addr, int secure)
+{
+ wpa_msg(wpa_s, MSG_INFO, "WPA: Key negotiation completed with "
+ MACSTR " [PTK=%s GTK=%s]", MAC2STR(addr),
+ wpa_cipher_txt(wpa_s->pairwise_cipher),
+ wpa_cipher_txt(wpa_s->group_cipher));
+ eloop_cancel_timeout(wpa_supplicant_scan, wpa_s, NULL);
+ wpa_supplicant_cancel_auth_timeout(wpa_s);
+ wpa_s->wpa_state = WPA_COMPLETED;
+
+ if (secure) {
+ /* MLME.SETPROTECTION.request(TA, Tx_Rx) */
+ eapol_sm_notify_portValid(wpa_s->eapol, TRUE);
+ if (wpa_s->key_mgmt == WPA_KEY_MGMT_PSK)
+ eapol_sm_notify_eap_success(wpa_s->eapol, TRUE);
+ rsn_preauth_candidate_process(wpa_s);
+ }
+}
+
+
+static int wpa_supplicant_install_ptk(struct wpa_supplicant *wpa_s,
+ unsigned char *src_addr,
+ struct wpa_eapol_key *key)
+{
+ int alg, keylen, rsclen;
+ u8 *key_rsc;
+ u8 null_rsc[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+
+ wpa_printf(MSG_DEBUG, "WPA: Installing PTK to the driver.");
+
+ switch (wpa_s->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:
+ wpa_printf(MSG_DEBUG, "WPA: Pairwise Cipher Suite: "
+ "NONE - do not use pairwise keys");
+ return 0;
+ default:
+ wpa_printf(MSG_WARNING, "WPA: Unsupported pairwise cipher %d",
+ wpa_s->pairwise_cipher);
+ return -1;
+ }
+
+ if (wpa_s->proto == WPA_PROTO_RSN) {
+ key_rsc = null_rsc;
+ } else {
+ key_rsc = key->key_rsc;
+ wpa_hexdump(MSG_DEBUG, "WPA: RSC", key_rsc, rsclen);
+ }
+
+ wpa_s->keys_cleared = 0;
+ if (wpa_drv_set_key(wpa_s, alg, src_addr, 0, 1, key_rsc, rsclen,
+ (u8 *) &wpa_s->ptk.tk1, keylen) < 0) {
+ wpa_printf(MSG_WARNING, "WPA: Failed to set PTK to the "
+ "driver.");
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wpa_supplicant_check_group_cipher(struct wpa_supplicant *wpa_s,
+ int keylen, int maxkeylen,
+ int *key_rsc_len, int *alg)
+{
+ switch (wpa_s->group_cipher) {
+ case WPA_CIPHER_CCMP:
+ if (keylen != 16 || maxkeylen < 16) {
+ wpa_printf(MSG_WARNING, "WPA: Unsupported CCMP Group "
+ "Cipher key length %d (%d).",
+ keylen, maxkeylen);
+ return -1;
+ }
+ *key_rsc_len = 6;
+ *alg = WPA_ALG_CCMP;
+ break;
+ case WPA_CIPHER_TKIP:
+ if (keylen != 32 || maxkeylen < 32) {
+ wpa_printf(MSG_WARNING, "WPA: Unsupported TKIP Group "
+ "Cipher key length %d (%d).",
+ keylen, maxkeylen);
+ return -1;
+ }
+ *key_rsc_len = 6;
+ *alg = WPA_ALG_TKIP;
+ break;
+ case WPA_CIPHER_WEP104:
+ if (keylen != 13 || maxkeylen < 13) {
+ wpa_printf(MSG_WARNING, "WPA: Unsupported WEP104 Group"
+ " Cipher key length %d (%d).",
+ keylen, maxkeylen);
+ return -1;
+ }
+ *key_rsc_len = 0;
+ *alg = WPA_ALG_WEP;
+ break;
+ case WPA_CIPHER_WEP40:
+ if (keylen != 5 || maxkeylen < 5) {
+ wpa_printf(MSG_WARNING, "WPA: Unsupported WEP40 Group "
+ "Cipher key length %d (%d).",
+ keylen, maxkeylen);
+ return -1;
+ }
+ *key_rsc_len = 0;
+ *alg = WPA_ALG_WEP;
+ break;
+ default:
+ wpa_printf(MSG_WARNING, "WPA: Unsupport Group Cipher %d",
+ wpa_s->group_cipher);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int wpa_supplicant_install_gtk(struct wpa_supplicant *wpa_s,
+ struct wpa_eapol_key *key, int alg,
+ u8 *gtk, int gtk_len, int keyidx,
+ int key_rsc_len, int tx)
+{
+ wpa_hexdump_key(MSG_DEBUG, "WPA: Group Key", gtk, gtk_len);
+ wpa_printf(MSG_DEBUG, "WPA: Installing GTK to the driver "
+ "(keyidx=%d tx=%d).", keyidx, tx);
+ wpa_hexdump(MSG_DEBUG, "WPA: RSC", key->key_rsc, key_rsc_len);
+ if (wpa_s->group_cipher == WPA_CIPHER_TKIP) {
+ /* Swap Tx/Rx keys for Michael MIC */
+ u8 tmpbuf[8];
+ memcpy(tmpbuf, gtk + 16, 8);
+ memcpy(gtk + 16, gtk + 24, 8);
+ memcpy(gtk + 24, tmpbuf, 8);
+ }
+ wpa_s->keys_cleared = 0;
+ if (wpa_s->pairwise_cipher == WPA_CIPHER_NONE) {
+ if (wpa_drv_set_key(wpa_s, alg,
+ (u8 *) "\xff\xff\xff\xff\xff\xff",
+ keyidx, 1, key->key_rsc, key_rsc_len,
+ gtk, gtk_len) < 0) {
+ wpa_printf(MSG_WARNING, "WPA: Failed to set "
+ "GTK to the driver (Group only).");
+ return -1;
+ }
+ } else if (wpa_drv_set_key(wpa_s, alg,
+ (u8 *) "\xff\xff\xff\xff\xff\xff",
+ keyidx, tx, key->key_rsc, key_rsc_len,
+ gtk, gtk_len) < 0) {
+ wpa_printf(MSG_WARNING, "WPA: Failed to set GTK to "
+ "the driver.");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int wpa_supplicant_pairwise_gtk(struct wpa_supplicant *wpa_s,
+ unsigned char *src_addr,
+ struct wpa_eapol_key *key,
+ u8 *gtk, int gtk_len, int key_info)
+{
+ int keyidx, tx, key_rsc_len = 0, alg;
+
+ wpa_hexdump_key(MSG_DEBUG, "RSN: received GTK in pairwise handshake",
+ gtk, gtk_len);
+
+ keyidx = gtk[0] & 0x3;
+ tx = !!(gtk[0] & BIT(2));
+ if (tx && wpa_s->pairwise_cipher != WPA_CIPHER_NONE) {
+ /* Ignore Tx bit in GTK IE if a pairwise key is used. One AP
+ * seemed to set this bit (incorrectly, since Tx is only when
+ * doing Group Key only APs) and without this workaround, the
+ * data connection does not work because wpa_supplicant
+ * configured non-zero keyidx to be used for unicast. */
+ wpa_printf(MSG_INFO, "RSN: Tx bit set for GTK IE, but "
+ "pairwise keys are used - ignore Tx bit");
+ tx = 0;
+ }
+ gtk += 2;
+ gtk_len -= 2;
+
+ if (wpa_supplicant_check_group_cipher(wpa_s, gtk_len, gtk_len,
+ &key_rsc_len, &alg)) {
+ return -1;
+ }
+
+ if (wpa_supplicant_install_gtk(wpa_s, key, alg, gtk, gtk_len, keyidx,
+ key_rsc_len, tx)) {
+ return -1;
+ }
+
+ wpa_supplicant_key_neg_complete(wpa_s, src_addr,
+ key_info & WPA_KEY_INFO_SECURE);
+ return 0;
+}
+
+
+static void wpa_report_ie_mismatch(struct wpa_supplicant *wpa_s,
+ const char *reason, const u8 *src_addr,
+ const u8 *wpa_ie, size_t wpa_ie_len,
+ const u8 *rsn_ie, size_t rsn_ie_len)
+{
+ wpa_msg(wpa_s, MSG_WARNING, "WPA: %s (src=" MACSTR ")",
+ reason, MAC2STR(src_addr));
+
+ if (wpa_s->ap_wpa_ie) {
+ wpa_hexdump(MSG_INFO, "WPA: WPA IE in Beacon/ProbeResp",
+ wpa_s->ap_wpa_ie, wpa_s->ap_wpa_ie_len);
+ }
+ if (wpa_ie) {
+ if (!wpa_s->ap_wpa_ie) {
+ wpa_printf(MSG_INFO, "WPA: No WPA IE in "
+ "Beacon/ProbeResp");
+ }
+ wpa_hexdump(MSG_INFO, "WPA: WPA IE in 3/4 msg",
+ wpa_ie, wpa_ie_len);
+ }
+
+ if (wpa_s->ap_rsn_ie) {
+ wpa_hexdump(MSG_INFO, "WPA: RSN IE in Beacon/ProbeResp",
+ wpa_s->ap_rsn_ie, wpa_s->ap_rsn_ie_len);
+ }
+ if (rsn_ie) {
+ if (!wpa_s->ap_rsn_ie) {
+ wpa_printf(MSG_INFO, "WPA: No RSN IE in "
+ "Beacon/ProbeResp");
+ }
+ wpa_hexdump(MSG_INFO, "WPA: RSN IE in 3/4 msg",
+ rsn_ie, rsn_ie_len);
+ }
+
+ wpa_supplicant_disassociate(wpa_s, REASON_IE_IN_4WAY_DIFFERS);
+ wpa_supplicant_req_scan(wpa_s, 0, 0);
+}
+
+
+static void wpa_supplicant_process_3_of_4(struct wpa_supplicant *wpa_s,
+ unsigned char *src_addr,
+ struct wpa_eapol_key *key,
+ int extra_len, int ver)
+{
+ int rlen;
+ struct ieee802_1x_hdr *hdr;
+ struct wpa_eapol_key *reply;
+ unsigned char *rbuf;
+ struct l2_ethhdr *ethhdr;
+ int key_info, wpa_ie_len = 0, rsn_ie_len = 0, keylen, gtk_len = 0;
+ u8 *wpa_ie = NULL, *rsn_ie = NULL, *gtk = NULL;
+ u8 *pos, *end;
+ u16 len;
+ struct wpa_ssid *ssid = wpa_s->current_ssid;
+
+ wpa_s->wpa_state = WPA_4WAY_HANDSHAKE;
+ wpa_printf(MSG_DEBUG, "WPA: RX message 3 of 4-Way Handshake from "
+ MACSTR " (ver=%d)", MAC2STR(src_addr), ver);
+
+ key_info = be_to_host16(key->key_info);
+
+ pos = (u8 *) (key + 1);
+ len = be_to_host16(key->key_data_length);
+ end = pos + len;
+ wpa_hexdump(MSG_DEBUG, "WPA: IE KeyData", pos, len);
+ while (pos + 1 < end) {
+ if (pos + 2 + pos[1] > end) {
+ wpa_printf(MSG_DEBUG, "WPA: key data underflow (ie=%d "
+ "len=%d)", pos[0], pos[1]);
+ break;
+ }
+ if (*pos == RSN_INFO_ELEM) {
+ rsn_ie = pos;
+ rsn_ie_len = pos[1] + 2;
+ } else if (*pos == GENERIC_INFO_ELEM && pos[1] >= 6 &&
+ memcmp(pos + 2, WPA_OUI_TYPE, WPA_SELECTOR_LEN) == 0
+ && pos[2 + WPA_SELECTOR_LEN] == 1 &&
+ pos[2 + WPA_SELECTOR_LEN + 1] == 0) {
+ wpa_ie = pos;
+ wpa_ie_len = pos[1] + 2;
+ } else if (pos[0] == GENERIC_INFO_ELEM &&
+ pos[1] > RSN_SELECTOR_LEN + 2 &&
+ memcmp(pos + 2, RSN_KEY_DATA_GROUPKEY,
+ RSN_SELECTOR_LEN) == 0) {
+ if (!(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) {
+ wpa_printf(MSG_WARNING, "WPA: GTK IE in "
+ "unencrypted key data");
+ return;
+ }
+ gtk = pos + 2 + RSN_SELECTOR_LEN;
+ gtk_len = pos[1] - RSN_SELECTOR_LEN;
+ } else if (pos[0] == GENERIC_INFO_ELEM && pos[1] == 0)
+ break;
+
+ pos += 2 + pos[1];
+ }
+
+ if (wpa_s->ap_wpa_ie == NULL && wpa_s->ap_rsn_ie == NULL) {
+ wpa_printf(MSG_DEBUG, "WPA: No WPA/RSN IE for this AP known. "
+ "Trying to get from scan results");
+ if (wpa_supplicant_get_beacon_ie(wpa_s) < 0) {
+ wpa_printf(MSG_WARNING, "WPA: Could not find AP from "
+ "the scan results");
+ } else {
+ wpa_printf(MSG_DEBUG, "WPA: Found the current AP from "
+ "updated scan results");
+ }
+ }
+
+ if ((wpa_ie && wpa_s->ap_wpa_ie &&
+ (wpa_ie_len != wpa_s->ap_wpa_ie_len ||
+ memcmp(wpa_ie, wpa_s->ap_wpa_ie, wpa_ie_len) != 0)) ||
+ (rsn_ie && wpa_s->ap_rsn_ie &&
+ (rsn_ie_len != wpa_s->ap_rsn_ie_len ||
+ memcmp(rsn_ie, wpa_s->ap_rsn_ie, rsn_ie_len) != 0))) {
+ wpa_report_ie_mismatch(wpa_s, "IE in 3/4 msg does not match "
+ "with IE in Beacon/ProbeResp",
+ src_addr, wpa_ie, wpa_ie_len,
+ rsn_ie, rsn_ie_len);
+ return;
+ }
+
+ if (wpa_s->proto == WPA_PROTO_WPA &&
+ rsn_ie && wpa_s->ap_rsn_ie == NULL &&
+ ssid && (ssid->proto & WPA_PROTO_RSN)) {
+ wpa_report_ie_mismatch(wpa_s, "Possible downgrade attack "
+ "detected - RSN was enabled and RSN IE "
+ "was in msg 3/4, but not in "
+ "Beacon/ProbeResp",
+ src_addr, wpa_ie, wpa_ie_len,
+ rsn_ie, rsn_ie_len);
+ return;
+ }
+
+ if (memcmp(wpa_s->anonce, key->key_nonce, WPA_NONCE_LEN) != 0) {
+ wpa_printf(MSG_WARNING, "WPA: ANonce from message 1 of 4-Way "
+ "Handshake differs from 3 of 4-Way Handshake - drop"
+ " packet (src=" MACSTR ")", MAC2STR(src_addr));
+ return;
+ }
+
+ keylen = be_to_host16(key->key_length);
+ switch (wpa_s->pairwise_cipher) {
+ case WPA_CIPHER_CCMP:
+ if (keylen != 16) {
+ wpa_printf(MSG_WARNING, "WPA: Invalid CCMP key length "
+ "%d (src=" MACSTR ")",
+ keylen, MAC2STR(src_addr));
+ return;
+ }
+ break;
+ case WPA_CIPHER_TKIP:
+ if (keylen != 32) {
+ wpa_printf(MSG_WARNING, "WPA: Invalid TKIP key length "
+ "%d (src=" MACSTR ")",
+ keylen, MAC2STR(src_addr));
+ return;
+ }
+ break;
+ }
+
+ rlen = sizeof(*ethhdr) + sizeof(*hdr) + sizeof(*reply);
+ rbuf = malloc(rlen);
+ if (rbuf == NULL)
+ return;
+
+ memset(rbuf, 0, rlen);
+ ethhdr = (struct l2_ethhdr *) rbuf;
+ memcpy(ethhdr->h_dest, src_addr, ETH_ALEN);
+ memcpy(ethhdr->h_source, wpa_s->own_addr, ETH_ALEN);
+ ethhdr->h_proto = htons(ETH_P_EAPOL);
+
+ hdr = (struct ieee802_1x_hdr *) (ethhdr + 1);
+ hdr->version = wpa_s->conf->eapol_version;
+ hdr->type = IEEE802_1X_TYPE_EAPOL_KEY;
+ hdr->length = htons(sizeof(*reply));
+
+ reply = (struct wpa_eapol_key *) (hdr + 1);
+ reply->type = wpa_s->proto == WPA_PROTO_RSN ?
+ EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA;
+ reply->key_info = host_to_be16(ver | WPA_KEY_INFO_KEY_TYPE |
+ WPA_KEY_INFO_MIC |
+ (key_info & WPA_KEY_INFO_SECURE));
+ reply->key_length = key->key_length;
+ memcpy(reply->replay_counter, key->replay_counter,
+ WPA_REPLAY_COUNTER_LEN);
+
+ reply->key_data_length = host_to_be16(0);
+
+ memcpy(reply->key_nonce, wpa_s->snonce, WPA_NONCE_LEN);
+ wpa_eapol_key_mic(wpa_s->ptk.mic_key, ver, (u8 *) hdr,
+ rlen - sizeof(*ethhdr), reply->key_mic);
+
+ wpa_printf(MSG_DEBUG, "WPA: Sending EAPOL-Key 4/4");
+ wpa_hexdump(MSG_MSGDUMP, "WPA: TX EAPOL-Key 4/4", rbuf, rlen);
+ l2_packet_send(wpa_s->l2, rbuf, rlen);
+ eapol_sm_notify_tx_eapol_key(wpa_s->eapol);
+ free(rbuf);
+
+ /* SNonce was successfully used in msg 3/4, so mark it to be renewed
+ * for the next 4-Way Handshake. If msg 3 is received again, the old
+ * SNonce will still be used to avoid changing PTK. */
+ wpa_s->renew_snonce = 1;
+
+ if (key_info & WPA_KEY_INFO_INSTALL) {
+ wpa_supplicant_install_ptk(wpa_s, src_addr, key);
+ }
+
+ if (key_info & WPA_KEY_INFO_SECURE) {
+ /* MLME.SETPROTECTION.request(TA, Tx_Rx) */
+ eapol_sm_notify_portValid(wpa_s->eapol, TRUE);
+ }
+ wpa_s->wpa_state = WPA_GROUP_HANDSHAKE;
+
+ if (gtk) {
+ wpa_supplicant_pairwise_gtk(wpa_s, src_addr, key,
+ gtk, gtk_len, key_info);
+ }
+}
+
+
+static void wpa_supplicant_process_1_of_2(struct wpa_supplicant *wpa_s,
+ unsigned char *src_addr,
+ struct wpa_eapol_key *key,
+ int extra_len, int ver)
+{
+ int rlen;
+ struct ieee802_1x_hdr *hdr;
+ struct wpa_eapol_key *reply;
+ unsigned char *rbuf;
+ struct l2_ethhdr *ethhdr;
+ int key_info, keylen, keydatalen, maxkeylen, keyidx, key_rsc_len = 0;
+ int alg, tx, rekey;
+ u8 ek[32], gtk[32];
+ u8 *gtk_ie = NULL;
+ size_t gtk_ie_len = 0;
+
+ rekey = wpa_s->wpa_state == WPA_COMPLETED;
+ wpa_s->wpa_state = WPA_GROUP_HANDSHAKE;
+ wpa_printf(MSG_DEBUG, "WPA: RX message 1 of Group Key Handshake from "
+ MACSTR " (ver=%d)", MAC2STR(src_addr), ver);
+
+ key_info = be_to_host16(key->key_info);
+ keydatalen = be_to_host16(key->key_data_length);
+
+ if (wpa_s->proto == WPA_PROTO_RSN) {
+ u8 *pos = (u8 *) (key + 1);
+ u8 *end = pos + keydatalen;
+ while (pos + 1 < end) {
+ if (pos + 2 + pos[1] > end) {
+ wpa_printf(MSG_DEBUG, "RSN: key data "
+ "underflow (ie=%d len=%d)",
+ pos[0], pos[1]);
+ break;
+ }
+ if (pos[0] == GENERIC_INFO_ELEM &&
+ pos + 1 + RSN_SELECTOR_LEN < end &&
+ pos[1] > RSN_SELECTOR_LEN + 2 &&
+ memcmp(pos + 2, RSN_KEY_DATA_GROUPKEY,
+ RSN_SELECTOR_LEN) == 0) {
+ if (!(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) {
+ wpa_printf(MSG_WARNING, "WPA: GTK IE "
+ "in unencrypted key data");
+ return;
+ }
+ gtk_ie = pos + 2 + RSN_SELECTOR_LEN;
+ gtk_ie_len = pos[1] - RSN_SELECTOR_LEN;
+ break;
+ } else if (pos[0] == GENERIC_INFO_ELEM &&
+ pos[1] == 0)
+ break;
+
+ pos += 2 + pos[1];
+ }
+
+ if (gtk_ie == NULL) {
+ wpa_printf(MSG_INFO, "WPA: No GTK IE in Group Key "
+ "message 1/2");
+ return;
+ }
+ maxkeylen = keylen = gtk_ie_len - 2;
+ } else {
+ keylen = be_to_host16(key->key_length);
+ maxkeylen = keydatalen;
+ if (keydatalen > extra_len) {
+ wpa_printf(MSG_INFO, "WPA: Truncated EAPOL-Key packet:"
+ " key_data_length=%d > extra_len=%d",
+ keydatalen, extra_len);
+ return;
+ }
+ if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES)
+ maxkeylen -= 8;
+ }
+
+ if (wpa_supplicant_check_group_cipher(wpa_s, keylen, maxkeylen,
+ &key_rsc_len, &alg)) {
+ return;
+ }
+
+ if (wpa_s->proto == WPA_PROTO_RSN) {
+ wpa_hexdump(MSG_DEBUG,
+ "RSN: received GTK in group key handshake",
+ gtk_ie, gtk_ie_len);
+ keyidx = gtk_ie[0] & 0x3;
+ tx = !!(gtk_ie[0] & BIT(2));
+ if (gtk_ie_len - 2 > sizeof(gtk)) {
+ wpa_printf(MSG_INFO, "RSN: Too long GTK in GTK IE "
+ "(len=%lu)",
+ (unsigned long) gtk_ie_len - 2);
+ return;
+ }
+ memcpy(gtk, gtk_ie + 2, gtk_ie_len - 2);
+ } else {
+ keyidx = (key_info & WPA_KEY_INFO_KEY_INDEX_MASK) >>
+ WPA_KEY_INFO_KEY_INDEX_SHIFT;
+ if (ver == WPA_KEY_INFO_TYPE_HMAC_MD5_RC4) {
+ memcpy(ek, key->key_iv, 16);
+ memcpy(ek + 16, wpa_s->ptk.encr_key, 16);
+ rc4_skip(ek, 32, 256, (u8 *) (key + 1), keydatalen);
+ memcpy(gtk, key + 1, keylen);
+ } else if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
+ if (keydatalen % 8) {
+ wpa_printf(MSG_WARNING, "WPA: Unsupported "
+ "AES-WRAP len %d", keydatalen);
+ return;
+ }
+ if (aes_unwrap(wpa_s->ptk.encr_key, maxkeylen / 8,
+ (u8 *) (key + 1), gtk)) {
+ wpa_printf(MSG_WARNING, "WPA: AES unwrap "
+ "failed - could not decrypt GTK");
+ return;
+ }
+ }
+ tx = !!(key_info & WPA_KEY_INFO_TXRX);
+ }
+
+ if (tx && wpa_s->pairwise_cipher != WPA_CIPHER_NONE) {
+ /* Ignore Tx bit in Group Key message if a pairwise key
+ * is used. Some APs seem to setting this bit
+ * (incorrectly, since Tx is only when doing Group Key
+ * only APs) and without this workaround, the data
+ * connection does not work because wpa_supplicant
+ * configured non-zero keyidx to be used for unicast.
+ */
+ wpa_printf(MSG_INFO, "WPA: Tx bit set for GTK, but "
+ "pairwise keys are used - ignore Tx bit");
+ tx = 0;
+ }
+
+ wpa_supplicant_install_gtk(wpa_s, key, alg, gtk, keylen, keyidx,
+ key_rsc_len, tx);
+
+ rlen = sizeof(*ethhdr) + sizeof(*hdr) + sizeof(*reply);
+ rbuf = malloc(rlen);
+ if (rbuf == NULL)
+ return;
+
+ memset(rbuf, 0, rlen);
+ ethhdr = (struct l2_ethhdr *) rbuf;
+ memcpy(ethhdr->h_dest, src_addr, ETH_ALEN);
+ memcpy(ethhdr->h_source, wpa_s->own_addr, ETH_ALEN);
+ ethhdr->h_proto = htons(ETH_P_EAPOL);
+
+ hdr = (struct ieee802_1x_hdr *) (ethhdr + 1);
+ hdr->version = wpa_s->conf->eapol_version;
+ hdr->type = IEEE802_1X_TYPE_EAPOL_KEY;
+ hdr->length = htons(sizeof(*reply));
+
+ reply = (struct wpa_eapol_key *) (hdr + 1);
+ reply->type = wpa_s->proto == WPA_PROTO_RSN ?
+ EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA;
+ reply->key_info =
+ host_to_be16(ver | WPA_KEY_INFO_MIC | WPA_KEY_INFO_SECURE |
+ (key_info & WPA_KEY_INFO_KEY_INDEX_MASK));
+ reply->key_length = key->key_length;
+ memcpy(reply->replay_counter, key->replay_counter,
+ WPA_REPLAY_COUNTER_LEN);
+
+ reply->key_data_length = host_to_be16(0);
+
+ wpa_eapol_key_mic(wpa_s->ptk.mic_key, ver, (u8 *) hdr,
+ rlen - sizeof(*ethhdr), reply->key_mic);
+
+ wpa_printf(MSG_DEBUG, "WPA: Sending EAPOL-Key 2/2");
+ wpa_hexdump(MSG_MSGDUMP, "WPA: TX EAPOL-Key 2/2", rbuf, rlen);
+ l2_packet_send(wpa_s->l2, rbuf, rlen);
+ eapol_sm_notify_tx_eapol_key(wpa_s->eapol);
+ free(rbuf);
+
+ if (rekey) {
+ wpa_msg(wpa_s, MSG_INFO, "WPA: Group rekeying completed with "
+ MACSTR " [GTK=%s]", MAC2STR(src_addr),
+ wpa_cipher_txt(wpa_s->group_cipher));
+ wpa_s->wpa_state = WPA_COMPLETED;
+ } else {
+ wpa_supplicant_key_neg_complete(wpa_s, src_addr,
+ key_info &
+ WPA_KEY_INFO_SECURE);
+ }
+}
+
+
+static int wpa_supplicant_verify_eapol_key_mic(struct wpa_supplicant *wpa_s,
+ struct wpa_eapol_key *key,
+ int ver, u8 *buf, size_t len)
+{
+ u8 mic[16];
+ int ok = 0;
+
+ memcpy(mic, key->key_mic, 16);
+ if (wpa_s->tptk_set) {
+ memset(key->key_mic, 0, 16);
+ wpa_eapol_key_mic(wpa_s->tptk.mic_key, ver, buf, len,
+ key->key_mic);
+ if (memcmp(mic, key->key_mic, 16) != 0) {
+ wpa_printf(MSG_WARNING, "WPA: Invalid EAPOL-Key MIC "
+ "when using TPTK - ignoring TPTK");
+ } else {
+ ok = 1;
+ wpa_s->tptk_set = 0;
+ wpa_s->ptk_set = 1;
+ memcpy(&wpa_s->ptk, &wpa_s->tptk, sizeof(wpa_s->ptk));
+ }
+ }
+
+ if (!ok && wpa_s->ptk_set) {
+ memset(key->key_mic, 0, 16);
+ wpa_eapol_key_mic(wpa_s->ptk.mic_key, ver, buf, len,
+ key->key_mic);
+ if (memcmp(mic, key->key_mic, 16) != 0) {
+ wpa_printf(MSG_WARNING, "WPA: Invalid EAPOL-Key MIC "
+ "- dropping packet");
+ return -1;
+ }
+ ok = 1;
+ }
+
+ if (!ok) {
+ wpa_printf(MSG_WARNING, "WPA: Could not verify EAPOL-Key MIC "
+ "- dropping packet");
+ return -1;
+ }
+
+ memcpy(wpa_s->rx_replay_counter, key->replay_counter,
+ WPA_REPLAY_COUNTER_LEN);
+ wpa_s->rx_replay_counter_set = 1;
+ return 0;
+}
+
+
+/* Decrypt RSN EAPOL-Key key data (RC4 or AES-WRAP) */
+static int wpa_supplicant_decrypt_key_data(struct wpa_supplicant *wpa_s,
+ struct wpa_eapol_key *key, int ver)
+{
+ int keydatalen = be_to_host16(key->key_data_length);
+
+ wpa_hexdump(MSG_DEBUG, "RSN: encrypted key data",
+ (u8 *) (key + 1), keydatalen);
+ if (!wpa_s->ptk_set) {
+ wpa_printf(MSG_WARNING, "WPA: PTK not available, "
+ "cannot decrypt EAPOL-Key key data.");
+ return -1;
+ }
+
+ /* Decrypt key data here so that this operation does not need
+ * to be implemented separately for each message type. */
+ if (ver == WPA_KEY_INFO_TYPE_HMAC_MD5_RC4) {
+ u8 ek[32];
+ memcpy(ek, key->key_iv, 16);
+ memcpy(ek + 16, wpa_s->ptk.encr_key, 16);
+ rc4_skip(ek, 32, 256, (u8 *) (key + 1), keydatalen);
+ } else if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
+ u8 *buf;
+ if (keydatalen % 8) {
+ wpa_printf(MSG_WARNING, "WPA: Unsupported "
+ "AES-WRAP len %d", keydatalen);
+ return -1;
+ }
+ keydatalen -= 8; /* AES-WRAP adds 8 bytes */
+ buf = malloc(keydatalen);
+ if (buf == NULL) {
+ wpa_printf(MSG_WARNING, "WPA: No memory for "
+ "AES-UNWRAP buffer");
+ return -1;
+ }
+ if (aes_unwrap(wpa_s->ptk.encr_key, keydatalen / 8,
+ (u8 *) (key + 1), buf)) {
+ free(buf);
+ wpa_printf(MSG_WARNING, "WPA: AES unwrap failed - "
+ "could not decrypt EAPOL-Key key data");
+ return -1;
+ }
+ memcpy(key + 1, buf, keydatalen);
+ free(buf);
+ key->key_data_length = host_to_be16(keydatalen);
+ }
+ wpa_hexdump_key(MSG_DEBUG, "WPA: decrypted EAPOL-Key key data",
+ (u8 *) (key + 1), keydatalen);
+ return 0;
+}
+
+
+static void wpa_sm_rx_eapol(struct wpa_supplicant *wpa_s,
+ unsigned char *src_addr, unsigned char *buf,
+ size_t len)
+{
+ size_t plen, data_len, extra_len;
+ struct ieee802_1x_hdr *hdr;
+ struct wpa_eapol_key *key;
+ int key_info, ver;
+
+ hdr = (struct ieee802_1x_hdr *) buf;
+ key = (struct wpa_eapol_key *) (hdr + 1);
+ if (len < sizeof(*hdr) + sizeof(*key)) {
+ wpa_printf(MSG_DEBUG, "WPA: EAPOL frame too short, len %lu, "
+ "expecting at least %lu",
+ (unsigned long) len,
+ (unsigned long) sizeof(*hdr) + sizeof(*key));
+ return;
+ }
+ plen = ntohs(hdr->length);
+ data_len = plen + sizeof(*hdr);
+ wpa_printf(MSG_DEBUG, "IEEE 802.1X RX: version=%d type=%d length=%lu",
+ hdr->version, hdr->type, (unsigned long) plen);
+
+ wpa_drv_poll(wpa_s);
+
+ if (hdr->version < EAPOL_VERSION) {
+ /* TODO: backwards compatibility */
+ }
+ if (hdr->type != IEEE802_1X_TYPE_EAPOL_KEY) {
+ wpa_printf(MSG_DEBUG, "WPA: EAPOL frame (type %u) discarded, "
+ "not a Key frame", hdr->type);
+ if (wpa_s->cur_pmksa) {
+ wpa_printf(MSG_DEBUG, "WPA: Cancelling PMKSA caching "
+ "attempt - attempt full EAP "
+ "authentication");
+ eapol_sm_notify_pmkid_attempt(wpa_s->eapol, 0);
+ }
+ return;
+ }
+ if (plen > len - sizeof(*hdr) || plen < sizeof(*key)) {
+ wpa_printf(MSG_DEBUG, "WPA: EAPOL frame payload size %lu "
+ "invalid (frame size %lu)",
+ (unsigned long) plen, (unsigned long) len);
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG, " EAPOL-Key type=%d", key->type);
+ if (key->type != EAPOL_KEY_TYPE_WPA && key->type != EAPOL_KEY_TYPE_RSN)
+ {
+ wpa_printf(MSG_DEBUG, "WPA: EAPOL-Key type (%d) unknown, "
+ "discarded", key->type);
+ return;
+ }
+
+ wpa_hexdump(MSG_MSGDUMP, "WPA: RX EAPOL-Key", buf, len);
+ if (data_len < len) {
+ wpa_printf(MSG_DEBUG, "WPA: ignoring %lu bytes after the IEEE "
+ "802.1X data", (unsigned long) len - data_len);
+ }
+ key_info = be_to_host16(key->key_info);
+ ver = key_info & WPA_KEY_INFO_TYPE_MASK;
+ if (ver != WPA_KEY_INFO_TYPE_HMAC_MD5_RC4 &&
+ ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
+ wpa_printf(MSG_INFO, "WPA: Unsupported EAPOL-Key descriptor "
+ "version %d.", ver);
+ return;
+ }
+
+ if (wpa_s->pairwise_cipher == WPA_CIPHER_CCMP &&
+ ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
+ wpa_printf(MSG_INFO, "WPA: CCMP is used, but EAPOL-Key "
+ "descriptor version (%d) is not 2.", ver);
+ if (wpa_s->group_cipher != WPA_CIPHER_CCMP &&
+ !(key_info & WPA_KEY_INFO_KEY_TYPE)) {
+ /* Earlier versions of IEEE 802.11i did not explicitly
+ * require version 2 descriptor for all EAPOL-Key
+ * packets, so allow group keys to use version 1 if
+ * CCMP is not used for them. */
+ wpa_printf(MSG_INFO, "WPA: Backwards compatibility: "
+ "allow invalid version for non-CCMP group "
+ "keys");
+ } else
+ return;
+ }
+
+ if (wpa_s->rx_replay_counter_set &&
+ memcmp(key->replay_counter, wpa_s->rx_replay_counter,
+ WPA_REPLAY_COUNTER_LEN) <= 0) {
+ wpa_printf(MSG_WARNING, "WPA: EAPOL-Key Replay Counter did not"
+ " increase - dropping packet");
+ return;
+ }
+
+ if (!(key_info & WPA_KEY_INFO_ACK)) {
+ wpa_printf(MSG_INFO, "WPA: No Ack bit in key_info");
+ return;
+ }
+
+ if (key_info & WPA_KEY_INFO_REQUEST) {
+ wpa_printf(MSG_INFO, "WPA: EAPOL-Key with Request bit - "
+ "dropped");
+ return;
+ }
+
+ if ((key_info & WPA_KEY_INFO_MIC) &&
+ wpa_supplicant_verify_eapol_key_mic(wpa_s, key, ver, buf,
+ data_len))
+ return;
+
+ extra_len = data_len - sizeof(*hdr) - sizeof(*key);
+
+ if (be_to_host16(key->key_data_length) > extra_len) {
+ wpa_msg(wpa_s, MSG_INFO, "WPA: Invalid EAPOL-Key frame - "
+ "key_data overflow (%d > %d)",
+ be_to_host16(key->key_data_length), extra_len);
+ return;
+ }
+
+ if (wpa_s->proto == WPA_PROTO_RSN &&
+ (key_info & WPA_KEY_INFO_ENCR_KEY_DATA) &&
+ wpa_supplicant_decrypt_key_data(wpa_s, key, ver))
+ return;
+
+ if (key_info & WPA_KEY_INFO_KEY_TYPE) {
+ if (key_info & WPA_KEY_INFO_KEY_INDEX_MASK) {
+ wpa_printf(MSG_WARNING, "WPA: Ignored EAPOL-Key "
+ "(Pairwise) with non-zero key index");
+ return;
+ }
+ if (key_info & WPA_KEY_INFO_MIC) {
+ /* 3/4 4-Way Handshake */
+ wpa_supplicant_process_3_of_4(wpa_s, src_addr, key,
+ extra_len, ver);
+ } else {
+ /* 1/4 4-Way Handshake */
+ wpa_supplicant_process_1_of_4(wpa_s, src_addr, key,
+ ver);
+ }
+ } else {
+ if (key_info & WPA_KEY_INFO_MIC) {
+ /* 1/2 Group Key Handshake */
+ wpa_supplicant_process_1_of_2(wpa_s, src_addr, key,
+ extra_len, ver);
+ } else {
+ wpa_printf(MSG_WARNING, "WPA: EAPOL-Key (Group) "
+ "without Mic bit - dropped");
+ }
+ }
+}
+
+
+void wpa_supplicant_rx_eapol(void *ctx, unsigned char *src_addr,
+ unsigned char *buf, size_t len)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+
+ wpa_printf(MSG_DEBUG, "RX EAPOL from " MACSTR, MAC2STR(src_addr));
+ wpa_hexdump(MSG_MSGDUMP, "RX EAPOL", buf, len);
+
+ if (wpa_s->eapol_received == 0) {
+ /* Timeout for completing IEEE 802.1X and WPA authentication */
+ wpa_supplicant_req_auth_timeout(
+ wpa_s,
+ (wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X ||
+ wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA) ?
+ 70 : 10, 0);
+ }
+ wpa_s->eapol_received++;
+
+ if (wpa_s->countermeasures) {
+ wpa_printf(MSG_INFO, "WPA: Countermeasures - dropped EAPOL "
+ "packet");
+ return;
+ }
+
+ /* Source address of the incoming EAPOL frame could be compared to the
+ * current BSSID. However, it is possible that a centralized
+ * Authenticator could be using another MAC address than the BSSID of
+ * an AP, so just allow any address to be used for now. The replies are
+ * still sent to the current BSSID (if available), though. */
+
+ memcpy(wpa_s->last_eapol_src, src_addr, ETH_ALEN);
+ eapol_sm_rx_eapol(wpa_s->eapol, src_addr, buf, len);
+ wpa_sm_rx_eapol(wpa_s, src_addr, buf, len);
+}
+
+
+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 const u8 * wpa_key_mgmt_suite(struct wpa_supplicant *wpa_s)
+{
+ static const u8 *dummy = (u8 *) "\x00\x00\x00\x00";
+ switch (wpa_s->key_mgmt) {
+ case WPA_KEY_MGMT_IEEE8021X:
+ return (wpa_s->proto == WPA_PROTO_RSN ?
+ RSN_AUTH_KEY_MGMT_UNSPEC_802_1X :
+ WPA_AUTH_KEY_MGMT_UNSPEC_802_1X);
+ case WPA_KEY_MGMT_PSK:
+ return (wpa_s->proto == WPA_PROTO_RSN ?
+ RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X :
+ WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X);
+ case WPA_KEY_MGMT_WPA_NONE:
+ return WPA_AUTH_KEY_MGMT_NONE;
+ default:
+ return dummy;
+ }
+}
+
+
+static const u8 * wpa_cipher_suite(struct wpa_supplicant *wpa_s, int cipher)
+{
+ static const u8 *dummy = (u8 *) "\x00\x00\x00\x00";
+ switch (cipher) {
+ case WPA_CIPHER_CCMP:
+ return (wpa_s->proto == WPA_PROTO_RSN ?
+ RSN_CIPHER_SUITE_CCMP : WPA_CIPHER_SUITE_CCMP);
+ case WPA_CIPHER_TKIP:
+ return (wpa_s->proto == WPA_PROTO_RSN ?
+ RSN_CIPHER_SUITE_TKIP : WPA_CIPHER_SUITE_TKIP);
+ case WPA_CIPHER_WEP104:
+ return (wpa_s->proto == WPA_PROTO_RSN ?
+ RSN_CIPHER_SUITE_WEP104 : WPA_CIPHER_SUITE_WEP104);
+ case WPA_CIPHER_WEP40:
+ return (wpa_s->proto == WPA_PROTO_RSN ?
+ RSN_CIPHER_SUITE_WEP40 : WPA_CIPHER_SUITE_WEP40);
+ case WPA_CIPHER_NONE:
+ return (wpa_s->proto == WPA_PROTO_RSN ?
+ RSN_CIPHER_SUITE_NONE : WPA_CIPHER_SUITE_NONE);
+ default:
+ return dummy;
+ }
+}
+
+
+#define RSN_SUITE "%02x-%02x-%02x-%d"
+#define RSN_SUITE_ARG(s) (s)[0], (s)[1], (s)[2], (s)[3]
+
+int wpa_get_mib(struct wpa_supplicant *wpa_s, char *buf, size_t buflen)
+{
+ int len, i;
+ char pmkid_txt[PMKID_LEN * 2 + 1];
+
+ if (wpa_s->cur_pmksa) {
+ char *pos = pmkid_txt;
+ for (i = 0; i < PMKID_LEN; i++) {
+ pos += sprintf(pos, "%02x",
+ wpa_s->cur_pmksa->pmkid[i]);
+ }
+ } else
+ pmkid_txt[0] = '\0';
+
+ len = snprintf(buf, buflen,
+ "dot11RSNAConfigVersion=%d\n"
+ "dot11RSNAConfigPairwiseKeysSupported=5\n"
+ "dot11RSNAConfigGroupCipherSize=%d\n"
+ "dot11RSNAConfigPMKLifetime=%d\n"
+ "dot11RSNAConfigPMKReauthThreshold=%d\n"
+ "dot11RSNAConfigNumberOfPTKSAReplayCounters=1\n"
+ "dot11RSNAConfigSATimeout=%d\n"
+ "dot11RSNAAuthenticationSuiteSelected=" RSN_SUITE "\n"
+ "dot11RSNAPairwiseCipherSelected=" RSN_SUITE "\n"
+ "dot11RSNAGroupCipherSelected=" RSN_SUITE "\n"
+ "dot11RSNAPMKIDUsed=%s\n"
+ "dot11RSNAAuthenticationSuiteRequested=" RSN_SUITE "\n"
+ "dot11RSNAPairwiseCipherRequested=" RSN_SUITE "\n"
+ "dot11RSNAGroupCipherRequested=" RSN_SUITE "\n"
+ "dot11RSNAConfigNumberOfGTKSAReplayCounters=0\n",
+ RSN_VERSION,
+ wpa_cipher_bits(wpa_s->group_cipher),
+ dot11RSNAConfigPMKLifetime,
+ dot11RSNAConfigPMKReauthThreshold,
+ dot11RSNAConfigSATimeout,
+ RSN_SUITE_ARG(wpa_key_mgmt_suite(wpa_s)),
+ RSN_SUITE_ARG(wpa_cipher_suite(wpa_s,
+ wpa_s->pairwise_cipher)),
+ RSN_SUITE_ARG(wpa_cipher_suite(wpa_s,
+ wpa_s->group_cipher)),
+ pmkid_txt,
+ RSN_SUITE_ARG(wpa_key_mgmt_suite(wpa_s)),
+ RSN_SUITE_ARG(wpa_cipher_suite(wpa_s,
+ wpa_s->pairwise_cipher)),
+ RSN_SUITE_ARG(wpa_cipher_suite(wpa_s,
+ wpa_s->group_cipher)));
+ return len;
+}
+
+
+#ifdef IEEE8021X_EAPOL
+
+static void rsn_preauth_receive(void *ctx, unsigned char *src_addr,
+ unsigned char *buf, size_t len)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+
+ wpa_printf(MSG_DEBUG, "RX pre-auth from " MACSTR, MAC2STR(src_addr));
+ wpa_hexdump(MSG_MSGDUMP, "RX pre-auth", buf, len);
+
+ if (wpa_s->preauth_eapol == NULL ||
+ memcmp(wpa_s->preauth_bssid, "\x00\x00\x00\x00\x00\x00",
+ ETH_ALEN) == 0 ||
+ memcmp(wpa_s->preauth_bssid, src_addr, ETH_ALEN) != 0) {
+ wpa_printf(MSG_WARNING, "RSN pre-auth frame received from "
+ "unexpected source " MACSTR " - dropped",
+ MAC2STR(src_addr));
+ return;
+ }
+
+ eapol_sm_rx_eapol(wpa_s->preauth_eapol, src_addr, buf, len);
+}
+
+
+static void rsn_preauth_eapol_cb(struct eapol_sm *eapol, int success,
+ void *ctx)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+ u8 pmk[PMK_LEN];
+
+ wpa_msg(wpa_s, MSG_INFO, "RSN: pre-authentication with " MACSTR
+ " %s", MAC2STR(wpa_s->preauth_bssid),
+ success ? "completed successfully" : "failed");
+
+ if (success) {
+ int res, pmk_len;
+ pmk_len = PMK_LEN;
+ res = eapol_sm_get_key(eapol, pmk, PMK_LEN);
+#ifdef EAP_LEAP
+ if (res) {
+ res = eapol_sm_get_key(eapol, pmk, 16);
+ pmk_len = 16;
+ }
+#endif /* EAP_LEAP */
+ if (res == 0) {
+ wpa_hexdump_key(MSG_DEBUG, "RSN: PMK from pre-auth",
+ pmk, pmk_len);
+ wpa_s->pmk_len = pmk_len;
+ pmksa_cache_add(wpa_s, pmk, pmk_len,
+ wpa_s->preauth_bssid, wpa_s->own_addr);
+ } else {
+ wpa_msg(wpa_s, MSG_INFO, "RSN: failed to get master "
+ "session key from pre-auth EAPOL state "
+ "machines");
+ }
+ }
+
+ rsn_preauth_deinit(wpa_s);
+ rsn_preauth_candidate_process(wpa_s);
+}
+
+
+static void rsn_preauth_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_supplicant *wpa_s = eloop_ctx;
+ wpa_msg(wpa_s, MSG_INFO, "RSN: pre-authentication with " MACSTR
+ " timed out", MAC2STR(wpa_s->preauth_bssid));
+ rsn_preauth_deinit(wpa_s);
+ rsn_preauth_candidate_process(wpa_s);
+}
+
+
+int rsn_preauth_init(struct wpa_supplicant *wpa_s, u8 *dst)
+{
+ struct eapol_config eapol_conf;
+ struct eapol_ctx *ctx;
+
+ if (wpa_s->preauth_eapol)
+ return -1;
+
+ wpa_msg(wpa_s, MSG_DEBUG, "RSN: starting pre-authentication with "
+ MACSTR, MAC2STR(dst));
+
+ wpa_s->l2_preauth = l2_packet_init(wpa_s->ifname,
+ wpa_drv_get_mac_addr(wpa_s),
+ ETH_P_RSN_PREAUTH,
+ rsn_preauth_receive, wpa_s);
+ if (wpa_s->l2_preauth == NULL) {
+ wpa_printf(MSG_WARNING, "RSN: Failed to initialize L2 packet "
+ "processing for pre-authentication");
+ return -2;
+ }
+
+ ctx = malloc(sizeof(*ctx));
+ if (ctx == NULL) {
+ wpa_printf(MSG_WARNING, "Failed to allocate EAPOL context.");
+ return -4;
+ }
+ memset(ctx, 0, sizeof(*ctx));
+ ctx->ctx = wpa_s;
+ ctx->preauth = 1;
+ ctx->cb = rsn_preauth_eapol_cb;
+ ctx->cb_ctx = wpa_s;
+ ctx->scard_ctx = wpa_s->scard;
+ ctx->eapol_done_cb = wpa_supplicant_notify_eapol_done;
+ ctx->eapol_send = wpa_eapol_send_preauth;
+
+ wpa_s->preauth_eapol = eapol_sm_init(ctx);
+ if (wpa_s->preauth_eapol == NULL) {
+ free(ctx);
+ wpa_printf(MSG_WARNING, "RSN: Failed to initialize EAPOL "
+ "state machines for pre-authentication");
+ return -3;
+ }
+ memset(&eapol_conf, 0, sizeof(eapol_conf));
+ eapol_conf.accept_802_1x_keys = 0;
+ eapol_conf.required_keys = 0;
+ eapol_conf.fast_reauth = wpa_s->conf->fast_reauth;
+ if (wpa_s->current_ssid)
+ eapol_conf.workaround = wpa_s->current_ssid->eap_workaround;
+ eapol_sm_notify_config(wpa_s->preauth_eapol, wpa_s->current_ssid,
+ &eapol_conf);
+ memcpy(wpa_s->preauth_bssid, dst, ETH_ALEN);
+
+ eapol_sm_notify_portValid(wpa_s->preauth_eapol, TRUE);
+ /* 802.1X::portControl = Auto */
+ eapol_sm_notify_portEnabled(wpa_s->preauth_eapol, TRUE);
+
+ eloop_register_timeout(60, 0, rsn_preauth_timeout, wpa_s, NULL);
+
+ return 0;
+}
+
+
+void rsn_preauth_deinit(struct wpa_supplicant *wpa_s)
+{
+ if (!wpa_s->preauth_eapol)
+ return;
+
+ eloop_cancel_timeout(rsn_preauth_timeout, wpa_s, NULL);
+ eapol_sm_deinit(wpa_s->preauth_eapol);
+ wpa_s->preauth_eapol = NULL;
+ memset(wpa_s->preauth_bssid, 0, ETH_ALEN);
+
+ l2_packet_deinit(wpa_s->l2_preauth);
+ wpa_s->l2_preauth = NULL;
+}
+
+
+static void rsn_preauth_candidate_process(struct wpa_supplicant *wpa_s)
+{
+ struct rsn_pmksa_candidate *candidate;
+
+ if (wpa_s->pmksa_candidates == NULL)
+ return;
+
+ /* TODO: drop priority for old candidate entries */
+
+ wpa_msg(wpa_s, MSG_DEBUG, "RSN: processing PMKSA candidate list");
+ if (wpa_s->preauth_eapol ||
+ wpa_s->proto != WPA_PROTO_RSN ||
+ wpa_s->wpa_state != WPA_COMPLETED ||
+ wpa_s->key_mgmt != WPA_KEY_MGMT_IEEE8021X) {
+ wpa_msg(wpa_s, MSG_DEBUG, "RSN: not in suitable state for new "
+ "pre-authentication");
+ return; /* invalid state for new pre-auth */
+ }
+
+ while (wpa_s->pmksa_candidates) {
+ struct rsn_pmksa_cache *p = NULL;
+ candidate = wpa_s->pmksa_candidates;
+ p = pmksa_cache_get(wpa_s, candidate->bssid, NULL);
+ if (memcmp(wpa_s->bssid, candidate->bssid, ETH_ALEN) != 0 &&
+ p == NULL) {
+ wpa_msg(wpa_s, MSG_DEBUG, "RSN: PMKSA candidate "
+ MACSTR " selected for pre-authentication",
+ MAC2STR(candidate->bssid));
+ wpa_s->pmksa_candidates = candidate->next;
+ rsn_preauth_init(wpa_s, candidate->bssid);
+ free(candidate);
+ return;
+ }
+ wpa_msg(wpa_s, MSG_DEBUG, "RSN: PMKSA candidate " MACSTR
+ " does not need pre-authentication anymore",
+ MAC2STR(candidate->bssid));
+ /* Some drivers (e.g., NDIS) expect to get notified about the
+ * PMKIDs again, so report the existing data now. */
+ if (p)
+ wpa_drv_add_pmkid(wpa_s, candidate->bssid, p->pmkid);
+
+ wpa_s->pmksa_candidates = candidate->next;
+ free(candidate);
+ }
+ wpa_msg(wpa_s, MSG_DEBUG, "RSN: no more pending PMKSA candidates");
+}
+
+
+void pmksa_candidate_add(struct wpa_supplicant *wpa_s, const u8 *bssid,
+ int prio)
+{
+ struct rsn_pmksa_candidate *cand, *prev, *pos;
+
+ /* If BSSID already on candidate list, update the priority of the old
+ * entry. Do not override priority based on normal scan results. */
+ prev = NULL;
+ cand = wpa_s->pmksa_candidates;
+ while (cand) {
+ if (memcmp(cand->bssid, bssid, ETH_ALEN) == 0) {
+ if (prev)
+ prev->next = cand->next;
+ else
+ wpa_s->pmksa_candidates = cand->next;
+ break;
+ }
+ prev = cand;
+ cand = cand->next;
+ }
+
+ if (cand) {
+ if (prio < PMKID_CANDIDATE_PRIO_SCAN)
+ cand->priority = prio;
+ } else {
+ cand = malloc(sizeof(*cand));
+ if (cand == NULL)
+ return;
+ memset(cand, 0, sizeof(*cand));
+ memcpy(cand->bssid, bssid, ETH_ALEN);
+ cand->priority = prio;
+ }
+
+ /* Add candidate to the list; order by increasing priority value. i.e.,
+ * highest priority (smallest value) first. */
+ prev = NULL;
+ pos = wpa_s->pmksa_candidates;
+ while (pos) {
+ if (cand->priority <= pos->priority)
+ break;
+ prev = pos;
+ pos = pos->next;
+ }
+ cand->next = pos;
+ if (prev)
+ prev->next = cand;
+ else
+ wpa_s->pmksa_candidates = cand;
+
+ wpa_msg(wpa_s, MSG_DEBUG, "RSN: added PMKSA cache "
+ "candidate " MACSTR " prio %d", MAC2STR(bssid), prio);
+ rsn_preauth_candidate_process(wpa_s);
+}
+
+
+/* TODO: schedule periodic scans if current AP supports preauth */
+void rsn_preauth_scan_results(struct wpa_supplicant *wpa_s,
+ struct wpa_scan_result *results, int count)
+{
+ struct wpa_scan_result *r;
+ struct wpa_ie_data ie;
+ int i;
+
+ if (wpa_s->current_ssid == NULL)
+ return;
+
+ pmksa_candidate_free(wpa_s);
+
+ for (i = count - 1; i >= 0; i--) {
+ r = &results[i];
+ if (r->ssid_len == wpa_s->current_ssid->ssid_len &&
+ memcmp(r->ssid, wpa_s->current_ssid->ssid, r->ssid_len) ==
+ 0 &&
+ memcmp(r->bssid, wpa_s->bssid, ETH_ALEN) != 0 &&
+ r->rsn_ie_len > 0 &&
+ wpa_parse_wpa_ie(wpa_s, r->rsn_ie, r->rsn_ie_len, &ie) ==
+ 0 &&
+ (ie.capabilities & WPA_CAPABILITY_PREAUTH) &&
+ pmksa_cache_get(wpa_s, r->bssid, NULL) == NULL) {
+ /* Give less priority to candidates found from normal
+ * scan results. */
+ pmksa_candidate_add(wpa_s, r->bssid,
+ PMKID_CANDIDATE_PRIO_SCAN);
+ }
+ }
+}
+
+#else /* IEEE8021X_EAPOL */
+
+static void rsn_preauth_candidate_process(struct wpa_supplicant *wpa_s)
+{
+}
+
+#endif /* IEEE8021X_EAPOL */
diff --git a/contrib/wpa_supplicant/wpa.h b/contrib/wpa_supplicant/wpa.h
new file mode 100644
index 000000000000..a4f3f55c72f2
--- /dev/null
+++ b/contrib/wpa_supplicant/wpa.h
@@ -0,0 +1,112 @@
+#ifndef WPA_H
+#define WPA_H
+
+#define BIT(n) (1 << (n))
+
+struct ieee802_1x_hdr {
+ u8 version;
+ u8 type;
+ u16 length;
+ /* followed by length octets of data */
+} __attribute__ ((packed));
+
+#define EAPOL_VERSION 2
+
+enum { IEEE802_1X_TYPE_EAP_PACKET = 0,
+ IEEE802_1X_TYPE_EAPOL_START = 1,
+ IEEE802_1X_TYPE_EAPOL_LOGOFF = 2,
+ IEEE802_1X_TYPE_EAPOL_KEY = 3,
+ IEEE802_1X_TYPE_EAPOL_ENCAPSULATED_ASF_ALERT = 4
+};
+
+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
+
+struct ieee802_1x_eapol_key {
+ u8 type;
+ u16 key_length;
+ /* 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 */
+} __attribute__ ((packed));
+
+
+#define WPA_NONCE_LEN 32
+#define WPA_REPLAY_COUNTER_LEN 8
+
+struct wpa_eapol_key {
+ u8 type;
+ u16 key_info;
+ u16 key_length;
+ u8 replay_counter[WPA_REPLAY_COUNTER_LEN];
+ u8 key_nonce[WPA_NONCE_LEN];
+ u8 key_iv[16];
+ u8 key_rsc[8];
+ u8 key_id[8]; /* Reserved in IEEE 802.11i/RSN */
+ u8 key_mic[16];
+ u16 key_data_length;
+ /* followed by key_data_length bytes of key_data */
+} __attribute__ ((packed));
+
+#define WPA_KEY_INFO_TYPE_MASK (BIT(0) | BIT(1) | BIT(2))
+#define WPA_KEY_INFO_TYPE_HMAC_MD5_RC4 BIT(0)
+#define WPA_KEY_INFO_TYPE_HMAC_SHA1_AES BIT(1)
+#define WPA_KEY_INFO_KEY_TYPE BIT(3) /* 1 = Pairwise, 0 = Group key */
+/* bit4..5 is used in WPA, but is reserved in IEEE 802.11i/RSN */
+#define WPA_KEY_INFO_KEY_INDEX_MASK (BIT(4) | BIT(5))
+#define WPA_KEY_INFO_KEY_INDEX_SHIFT 4
+#define WPA_KEY_INFO_INSTALL BIT(6) /* pairwise */
+#define WPA_KEY_INFO_TXRX BIT(6) /* group */
+#define WPA_KEY_INFO_ACK BIT(7)
+#define WPA_KEY_INFO_MIC BIT(8)
+#define WPA_KEY_INFO_SECURE BIT(9)
+#define WPA_KEY_INFO_ERROR BIT(10)
+#define WPA_KEY_INFO_REQUEST BIT(11)
+#define WPA_KEY_INFO_ENCR_KEY_DATA BIT(12) /* IEEE 802.11i/RSN only */
+
+#define WPA_CAPABILITY_PREAUTH BIT(0)
+
+#define GENERIC_INFO_ELEM 0xdd
+#define RSN_INFO_ELEM 0x30
+
+enum {
+ REASON_UNSPECIFIED = 1,
+ REASON_DEAUTH_LEAVING = 3,
+ REASON_INVALID_IE = 13,
+ REASON_MICHAEL_MIC_FAILURE = 14,
+ REASON_4WAY_HANDSHAKE_TIMEOUT = 15,
+ REASON_GROUP_KEY_UPDATE_TIMEOUT = 16,
+ REASON_IE_IN_4WAY_DIFFERS = 17,
+ REASON_GROUP_CIPHER_NOT_VALID = 18,
+ REASON_PAIRWISE_CIPHER_NOT_VALID = 19,
+ REASON_AKMP_NOT_VALID = 20,
+ REASON_UNSUPPORTED_RSN_IE_VERSION = 21,
+ REASON_INVALID_RSN_IE_CAPAB = 22,
+ REASON_IEEE_802_1X_AUTH_FAILED = 23,
+ REASON_CIPHER_SUITE_REJECTED = 24
+};
+
+#endif /* WPA_H */
diff --git a/contrib/wpa_supplicant/wpa_cli.c b/contrib/wpa_supplicant/wpa_cli.c
new file mode 100644
index 000000000000..8582fae27864
--- /dev/null
+++ b/contrib/wpa_supplicant/wpa_cli.c
@@ -0,0 +1,850 @@
+/*
+ * WPA Supplicant - command line interface for wpa_supplicant daemon
+ * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published 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 <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <signal.h>
+#include <unistd.h>
+#include <dirent.h>
+#ifdef CONFIG_READLINE
+#include <readline/readline.h>
+#include <readline/history.h>
+#endif /* CONFIG_READLINE */
+
+#include "wpa_ctrl.h"
+#ifdef CONFIG_NATIVE_WINDOWS
+#include "common.h"
+#endif /* CONFIG_NATIVE_WINDOWS */
+#include "version.h"
+
+
+static const char *wpa_cli_version =
+"wpa_cli v" VERSION_STR "\n"
+"Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.fi> 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";
+
+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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\n"
+"\n"
+"Alternatively, this software may be distributed under the terms of the\n"
+"BSD license.\n"
+"\n"
+"Redistribution and use in source and binary forms, with or without\n"
+"modification, are permitted provided that the following conditions are\n"
+"met:\n"
+"\n"
+"1. Redistributions of source code must retain the above copyright\n"
+" notice, this list of conditions and the following disclaimer.\n"
+"\n"
+"2. Redistributions in binary form must reproduce the above copyright\n"
+" notice, this list of conditions and the following disclaimer in the\n"
+" documentation and/or other materials provided with the distribution.\n"
+"\n"
+"3. Neither the name(s) of the above-listed copyright holder(s) nor the\n"
+" names of its contributors may be used to endorse or promote products\n"
+" derived from this software without specific prior written permission.\n"
+"\n"
+"THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n"
+"\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n"
+"LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n"
+"A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n"
+"OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n"
+"SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n"
+"LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n"
+"DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n"
+"THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n"
+"(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n"
+"OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
+"\n";
+
+static const char *commands_help =
+"commands:\n"
+" status [verbose] = get current WPA/EAPOL/EAP status\n"
+" mib = get MIB variables (dot1x, dot11)\n"
+" help = show this usage help\n"
+" interface [ifname] = show interfaces/select interface\n"
+" level <debug level> = change debug level\n"
+" license = show full wpa_cli license\n"
+" logoff = IEEE 802.1X EAPOL state machine logoff\n"
+" logon = IEEE 802.1X EAPOL state machine logon\n"
+" set = set variables (shows list of variables when run without arguments)\n"
+" pmksa = show PMKSA cache\n"
+" reassociate = force reassociation\n"
+" reconfigure = force wpa_supplicant to re-read its configuration file\n"
+" preauthenticate <BSSID> = force preauthentication\n"
+" identity <network id> <identity> = configure identity for an SSID\n"
+" password <network id> <password> = configure password for an SSID\n"
+" otp <network id> <password> = configure one-time-password for an SSID\n"
+" terminate = terminate wpa_supplicant\n"
+" quit = exit wpa_cli\n";
+
+static struct wpa_ctrl *ctrl_conn;
+static int wpa_cli_quit = 0;
+static int wpa_cli_attached = 0;
+static const char *ctrl_iface_dir = "/var/run/wpa_supplicant";
+static char *ctrl_ifname = NULL;
+
+
+static void usage(void)
+{
+ printf("wpa_cli [-p<path to ctrl sockets>] [-i<ifname>] [-hv] "
+ "[command..]\n"
+ " -h = help (show this usage text)\n"
+ " -v = shown version information\n"
+ " default path: /var/run/wpa_supplicant\n"
+ " default interface: first interface found in socket path\n"
+ "%s",
+ commands_help);
+}
+
+
+static struct wpa_ctrl * wpa_cli_open_connection(const char *ifname)
+{
+#ifdef CONFIG_CTRL_IFACE_UDP
+ ctrl_conn = wpa_ctrl_open("");
+ return ctrl_conn;
+#else /* CONFIG_CTRL_IFACE_UDP */
+ char *cfile;
+ int flen;
+
+ if (ifname == NULL)
+ return NULL;
+
+ flen = strlen(ctrl_iface_dir) + strlen(ifname) + 2;
+ cfile = malloc(flen);
+ if (cfile == NULL)
+ return NULL;
+ snprintf(cfile, flen, "%s/%s", ctrl_iface_dir, ifname);
+
+ ctrl_conn = wpa_ctrl_open(cfile);
+ free(cfile);
+ return ctrl_conn;
+#endif /* CONFIG_CTRL_IFACE_UDP */
+}
+
+
+static void wpa_cli_close_connection(void)
+{
+ if (ctrl_conn == NULL)
+ return;
+
+ if (wpa_cli_attached) {
+ wpa_ctrl_detach(ctrl_conn);
+ wpa_cli_attached = 0;
+ }
+ wpa_ctrl_close(ctrl_conn);
+ ctrl_conn = NULL;
+}
+
+
+static void wpa_cli_msg_cb(char *msg, size_t len)
+{
+ printf("%s\n", msg);
+}
+
+
+static int _wpa_ctrl_command(struct wpa_ctrl *ctrl, char *cmd, int print)
+{
+ char buf[2048];
+ size_t len;
+ int ret;
+
+ if (ctrl_conn == NULL) {
+ printf("Not connected to wpa_supplicant - command dropped.\n");
+ return -1;
+ }
+ len = sizeof(buf) - 1;
+ ret = wpa_ctrl_request(ctrl, cmd, strlen(cmd), buf, &len,
+ wpa_cli_msg_cb);
+ if (ret == -2) {
+ printf("'%s' command timed out.\n", cmd);
+ return -2;
+ } else if (ret < 0) {
+ printf("'%s' command failed.\n", cmd);
+ return -1;
+ }
+ if (print) {
+ buf[len] = '\0';
+ printf("%s", buf);
+ }
+ return 0;
+}
+
+
+static int wpa_ctrl_command(struct wpa_ctrl *ctrl, char *cmd)
+{
+ return _wpa_ctrl_command(ctrl, cmd, 1);
+}
+
+
+static int wpa_cli_cmd_status(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+ int verbose = argc > 0 && strcmp(argv[0], "verbose") == 0;
+ return wpa_ctrl_command(ctrl, verbose ? "STATUS-VERBOSE" : "STATUS");
+}
+
+
+static int wpa_cli_cmd_ping(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+ return wpa_ctrl_command(ctrl, "PING");
+}
+
+
+static int wpa_cli_cmd_mib(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+ return wpa_ctrl_command(ctrl, "MIB");
+}
+
+
+static int wpa_cli_cmd_pmksa(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+ return wpa_ctrl_command(ctrl, "PMKSA");
+}
+
+
+static int wpa_cli_cmd_help(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+ printf("%s", commands_help);
+ return 0;
+}
+
+
+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);
+ return 0;
+}
+
+
+static int wpa_cli_cmd_quit(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+ wpa_cli_quit = 1;
+ return 0;
+}
+
+
+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");
+}
+
+
+static int wpa_cli_cmd_set(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+ char cmd[256];
+
+ if (argc == 0) {
+ wpa_cli_show_variables();
+ return 0;
+ }
+
+ if (argc != 2) {
+ printf("Invalid SET command: needs two arguments (variable "
+ "name and value)\n");
+ return 0;
+ }
+
+ if (snprintf(cmd, sizeof(cmd), "SET %s %s", argv[0], argv[1]) >=
+ sizeof(cmd) - 1) {
+ printf("Too long SET command.\n");
+ return 0;
+ }
+ return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+static int wpa_cli_cmd_logoff(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+ return wpa_ctrl_command(ctrl, "LOGOFF");
+}
+
+
+static int wpa_cli_cmd_logon(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+ return wpa_ctrl_command(ctrl, "LOGON");
+}
+
+
+static int wpa_cli_cmd_reassociate(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return wpa_ctrl_command(ctrl, "REASSOCIATE");
+}
+
+
+static int wpa_cli_cmd_preauthenticate(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ char cmd[256];
+
+ if (argc != 1) {
+ printf("Invalid PREAUTH command: needs one argument "
+ "(BSSID)\n");
+ return 0;
+ }
+
+ if (snprintf(cmd, sizeof(cmd), "PREAUTH %s", argv[0]) >=
+ sizeof(cmd) - 1) {
+ printf("Too long PREAUTH command.\n");
+ return 0;
+ }
+ return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+static int wpa_cli_cmd_level(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+ char cmd[256];
+ if (argc != 1) {
+ printf("Invalid LEVEL command: needs one argument (debug "
+ "level)\n");
+ return 0;
+ }
+ snprintf(cmd, sizeof(cmd), "LEVEL %s", argv[0]);
+ return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+static int wpa_cli_cmd_identity(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+ char cmd[256], *pos, *end;
+ int i;
+
+ if (argc < 2) {
+ printf("Invalid IDENTITY command: needs two arguments "
+ "(network id and identity)\n");
+ return 0;
+ }
+
+ end = cmd + sizeof(cmd);
+ pos = cmd;
+ pos += snprintf(pos, end - pos, "CTRL-RSP-IDENTITY-%s:%s",
+ argv[0], argv[1]);
+ for (i = 2; i < argc; i++)
+ pos += snprintf(pos, end - pos, " %s", argv[i]);
+
+ return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+static int wpa_cli_cmd_password(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+ char cmd[256], *pos, *end;
+ int i;
+
+ if (argc < 2) {
+ printf("Invalid PASSWORD command: needs two arguments "
+ "(network id and password)\n");
+ return 0;
+ }
+
+ end = cmd + sizeof(cmd);
+ pos = cmd;
+ pos += snprintf(pos, end - pos, "CTRL-RSP-PASSWORD-%s:%s",
+ argv[0], argv[1]);
+ for (i = 2; i < argc; i++)
+ pos += snprintf(pos, end - pos, " %s", argv[i]);
+
+ return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+static int wpa_cli_cmd_otp(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+ char cmd[256], *pos, *end;
+ int i;
+
+ if (argc < 2) {
+ printf("Invalid OTP command: needs two arguments (network "
+ "id and password)\n");
+ return 0;
+ }
+
+ end = cmd + sizeof(cmd);
+ pos = cmd;
+ pos += snprintf(pos, end - pos, "CTRL-RSP-OTP-%s:%s",
+ argv[0], argv[1]);
+ for (i = 2; i < argc; i++)
+ pos += snprintf(pos, end - pos, " %s", argv[i]);
+
+ return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+static void wpa_cli_list_interfaces(struct wpa_ctrl *ctrl)
+{
+ struct dirent *dent;
+ DIR *dir;
+
+ dir = opendir(ctrl_iface_dir);
+ if (dir == NULL) {
+ printf("Control interface directory '%s' could not be "
+ "openned.\n", ctrl_iface_dir);
+ return;
+ }
+
+ printf("Available interfaces:\n");
+ while ((dent = readdir(dir))) {
+ if (strcmp(dent->d_name, ".") == 0 ||
+ strcmp(dent->d_name, "..") == 0)
+ continue;
+ printf("%s\n", dent->d_name);
+ }
+ closedir(dir);
+}
+
+
+static int wpa_cli_cmd_interface(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+ if (argc < 1) {
+ wpa_cli_list_interfaces(ctrl);
+ return 0;
+ }
+
+ wpa_cli_close_connection();
+ free(ctrl_ifname);
+ ctrl_ifname = strdup(argv[0]);
+
+ if (wpa_cli_open_connection(ctrl_ifname)) {
+ printf("Connected to interface '%s.\n", ctrl_ifname);
+ if (wpa_ctrl_attach(ctrl_conn) == 0) {
+ wpa_cli_attached = 1;
+ } else {
+ printf("Warning: Failed to attach to "
+ "wpa_supplicant.\n");
+ }
+ } else {
+ printf("Could not connect to interface '%s' - re-trying\n",
+ ctrl_ifname);
+ }
+ return 0;
+}
+
+
+static int wpa_cli_cmd_reconfigure(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return wpa_ctrl_command(ctrl, "RECONFIGURE");
+}
+
+
+static int wpa_cli_cmd_terminate(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return wpa_ctrl_command(ctrl, "TERMINATE");
+}
+
+
+struct wpa_cli_cmd {
+ const char *cmd;
+ int (*handler)(struct wpa_ctrl *ctrl, int argc, char *argv[]);
+};
+
+static struct wpa_cli_cmd wpa_cli_commands[] = {
+ { "status", wpa_cli_cmd_status },
+ { "ping", wpa_cli_cmd_ping },
+ { "mib", wpa_cli_cmd_mib },
+ { "help", wpa_cli_cmd_help },
+ { "interface", wpa_cli_cmd_interface },
+ { "level", wpa_cli_cmd_level },
+ { "license", wpa_cli_cmd_license },
+ { "quit", wpa_cli_cmd_quit },
+ { "set", wpa_cli_cmd_set },
+ { "logon", wpa_cli_cmd_logon },
+ { "logoff", wpa_cli_cmd_logoff },
+ { "pmksa", wpa_cli_cmd_pmksa },
+ { "reassociate", wpa_cli_cmd_reassociate },
+ { "preauthenticate", wpa_cli_cmd_preauthenticate },
+ { "identity", wpa_cli_cmd_identity },
+ { "password", wpa_cli_cmd_password },
+ { "otp", wpa_cli_cmd_otp },
+ { "reconfigure", wpa_cli_cmd_reconfigure },
+ { "terminate", wpa_cli_cmd_terminate },
+ { NULL, NULL }
+};
+
+
+static void wpa_request(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+ struct wpa_cli_cmd *cmd, *match = NULL;
+ int count;
+
+ count = 0;
+ cmd = wpa_cli_commands;
+ while (cmd->cmd) {
+ if (strncasecmp(cmd->cmd, argv[0], strlen(argv[0])) == 0) {
+ match = cmd;
+ count++;
+ }
+ cmd++;
+ }
+
+ if (count > 1) {
+ printf("Ambiguous command '%s'; possible commands:", argv[0]);
+ cmd = wpa_cli_commands;
+ while (cmd->cmd) {
+ if (strncasecmp(cmd->cmd, argv[0], strlen(argv[0])) ==
+ 0) {
+ printf(" %s", cmd->cmd);
+ }
+ cmd++;
+ }
+ printf("\n");
+ } else if (count == 0) {
+ printf("Unknown command '%s'\n", argv[0]);
+ } else {
+ match->handler(ctrl, argc - 1, &argv[1]);
+ }
+}
+
+
+static void wpa_cli_recv_pending(struct wpa_ctrl *ctrl, int in_read)
+{
+ int first = 1;
+ if (ctrl_conn == NULL)
+ return;
+ while (wpa_ctrl_pending(ctrl)) {
+ char buf[256];
+ size_t len = sizeof(buf) - 1;
+ if (wpa_ctrl_recv(ctrl, buf, &len) == 0) {
+ buf[len] = '\0';
+ if (in_read && first)
+ printf("\n");
+ first = 0;
+ printf("%s\n", buf);
+ } else {
+ printf("Could not read pending message.\n");
+ break;
+ }
+ }
+}
+
+
+#ifdef CONFIG_READLINE
+static char * wpa_cli_cmd_gen(const char *text, int state)
+{
+ static int i, len;
+ const char *cmd;
+
+ if (state == 0) {
+ i = 0;
+ len = strlen(text);
+ }
+
+ while ((cmd = wpa_cli_commands[i].cmd)) {
+ i++;
+ if (strncasecmp(cmd, text, len) == 0)
+ return strdup(cmd);
+ }
+
+ return NULL;
+}
+
+
+static char * wpa_cli_dummy_gen(const char *text, int state)
+{
+ return NULL;
+}
+
+
+static char ** wpa_cli_completion(const char *text, int start, int end)
+{
+ return rl_completion_matches(text, start == 0 ?
+ wpa_cli_cmd_gen : wpa_cli_dummy_gen);
+}
+#endif /* CONFIG_READLINE */
+
+
+static void wpa_cli_interactive(void)
+{
+#define max_args 10
+ char cmdbuf[256], *cmd, *argv[max_args], *pos;
+ int argc;
+#ifdef CONFIG_READLINE
+ char *home, *hfile = NULL;
+#endif /* CONFIG_READLINE */
+
+ printf("\nInteractive mode\n\n");
+
+#ifdef CONFIG_READLINE
+ rl_attempted_completion_function = wpa_cli_completion;
+ home = getenv("HOME");
+ if (home) {
+ const char *fname = ".wpa_cli_history";
+ int hfile_len = strlen(home) + 1 + strlen(fname) + 1;
+ hfile = malloc(hfile_len);
+ if (hfile) {
+ snprintf(hfile, hfile_len, "%s/%s", home, fname);
+ read_history(hfile);
+ stifle_history(100);
+ }
+ }
+#endif /* CONFIG_READLINE */
+
+ do {
+ wpa_cli_recv_pending(ctrl_conn, 0);
+#ifndef CONFIG_NATIVE_WINDOWS
+ alarm(1);
+#endif /* CONFIG_NATIVE_WINDOWS */
+#ifdef CONFIG_READLINE
+ cmd = readline("> ");
+ if (cmd && *cmd) {
+ HIST_ENTRY *h;
+ while (next_history())
+ ;
+ h = previous_history();
+ if (h == NULL || strcmp(cmd, h->line) != 0)
+ add_history(cmd);
+ next_history();
+ }
+#else /* CONFIG_READLINE */
+ printf("> ");
+ cmd = fgets(cmdbuf, sizeof(cmdbuf), stdin);
+#endif /* CONFIG_READLINE */
+#ifndef CONFIG_NATIVE_WINDOWS
+ alarm(0);
+#endif /* CONFIG_NATIVE_WINDOWS */
+ if (cmd == NULL)
+ break;
+ pos = cmd;
+ while (*pos != '\0') {
+ if (*pos == '\n') {
+ *pos = '\0';
+ break;
+ }
+ pos++;
+ }
+ argc = 0;
+ pos = cmd;
+ for (;;) {
+ while (*pos == ' ')
+ pos++;
+ if (*pos == '\0')
+ break;
+ argv[argc] = pos;
+ argc++;
+ if (argc == max_args)
+ break;
+ while (*pos != '\0' && *pos != ' ')
+ pos++;
+ if (*pos == ' ')
+ *pos++ = '\0';
+ }
+ if (argc)
+ wpa_request(ctrl_conn, argc, argv);
+
+ if (cmd != cmdbuf)
+ free(cmd);
+ } while (!wpa_cli_quit);
+
+#ifdef CONFIG_READLINE
+ if (hfile) {
+ /* Save command history, excluding lines that may contain
+ * passwords. */
+ HIST_ENTRY *h;
+ history_set_pos(0);
+ h = next_history();
+ while (h) {
+ char *p = h->line;
+ while (*p == ' ' || *p == '\t')
+ p++;
+ if (strncasecmp(p, "pa", 2) == 0 ||
+ strncasecmp(p, "o", 1) == 0) {
+ h = remove_history(where_history());
+ if (h) {
+ free(h->line);
+ free(h->data);
+ free(h);
+ }
+ h = current_history();
+ } else {
+ h = next_history();
+ }
+ }
+ write_history(hfile);
+ free(hfile);
+ }
+#endif /* CONFIG_READLINE */
+}
+
+
+static void wpa_cli_terminate(int sig)
+{
+ wpa_cli_close_connection();
+ exit(0);
+}
+
+
+#ifndef CONFIG_NATIVE_WINDOWS
+static void wpa_cli_alarm(int sig)
+{
+ if (ctrl_conn && _wpa_ctrl_command(ctrl_conn, "PING", 0)) {
+ printf("Connection to wpa_supplicant lost - trying to "
+ "reconnect\n");
+ wpa_cli_close_connection();
+ }
+ if (!ctrl_conn) {
+ ctrl_conn = wpa_cli_open_connection(ctrl_ifname);
+ if (ctrl_conn) {
+ printf("Connection to wpa_supplicant "
+ "re-established\n");
+ if (wpa_ctrl_attach(ctrl_conn) == 0) {
+ wpa_cli_attached = 1;
+ } else {
+ printf("Warning: Failed to attach to "
+ "wpa_supplicant.\n");
+ }
+ }
+ }
+ if (ctrl_conn)
+ wpa_cli_recv_pending(ctrl_conn, 1);
+ alarm(1);
+}
+#endif /* CONFIG_NATIVE_WINDOWS */
+
+
+int main(int argc, char *argv[])
+{
+ int interactive;
+ int warning_displayed = 0;
+ int c;
+
+#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 */
+
+ for (;;) {
+ c = getopt(argc, argv, "hi:p:v");
+ if (c < 0)
+ break;
+ switch (c) {
+ case 'h':
+ usage();
+ return 0;
+ case 'v':
+ printf("%s\n", wpa_cli_version);
+ return 0;
+ case 'i':
+ ctrl_ifname = strdup(optarg);
+ break;
+ case 'p':
+ ctrl_iface_dir = optarg;
+ break;
+ default:
+ usage();
+ return -1;
+ }
+ }
+
+ interactive = argc == optind;
+
+ if (interactive)
+ printf("%s\n\n%s\n\n", wpa_cli_version, wpa_cli_license);
+
+ for (;;) {
+ if (ctrl_ifname == NULL) {
+ struct dirent *dent;
+ DIR *dir = opendir(ctrl_iface_dir);
+ if (dir) {
+ while ((dent = readdir(dir))) {
+ if (strcmp(dent->d_name, ".") == 0 ||
+ strcmp(dent->d_name, "..") == 0)
+ continue;
+ printf("Selected interface '%s'\n",
+ dent->d_name);
+ ctrl_ifname = strdup(dent->d_name);
+ break;
+ }
+ closedir(dir);
+ }
+ }
+ ctrl_conn = wpa_cli_open_connection(ctrl_ifname);
+ if (ctrl_conn) {
+ if (warning_displayed)
+ printf("Connection established.\n");
+ break;
+ }
+
+ if (!interactive) {
+ perror("Failed to connect to wpa_supplicant - "
+ "wpa_ctrl_open");
+ return -1;
+ }
+
+ if (!warning_displayed) {
+ printf("Could not connect to wpa_supplicant - "
+ "re-trying\n");
+ warning_displayed = 1;
+ }
+ sleep(1);
+ continue;
+ }
+
+ signal(SIGINT, wpa_cli_terminate);
+ signal(SIGTERM, wpa_cli_terminate);
+#ifndef CONFIG_NATIVE_WINDOWS
+ signal(SIGALRM, wpa_cli_alarm);
+#endif /* CONFIG_NATIVE_WINDOWS */
+
+ if (interactive) {
+ if (wpa_ctrl_attach(ctrl_conn) == 0) {
+ wpa_cli_attached = 1;
+ } else {
+ printf("Warning: Failed to attach to "
+ "wpa_supplicant.\n");
+ }
+ wpa_cli_interactive();
+ } else
+ wpa_request(ctrl_conn, argc - optind, &argv[optind]);
+
+ free(ctrl_ifname);
+ wpa_cli_close_connection();
+
+#ifdef CONFIG_NATIVE_WINDOWS
+ WSACleanup();
+#endif /* CONFIG_NATIVE_WINDOWS */
+
+ return 0;
+}
diff --git a/contrib/wpa_supplicant/wpa_ctrl.c b/contrib/wpa_supplicant/wpa_ctrl.c
new file mode 100644
index 000000000000..16cef5f83548
--- /dev/null
+++ b/contrib/wpa_supplicant/wpa_ctrl.c
@@ -0,0 +1,230 @@
+/*
+ * WPA Supplicant - wpa_supplicant control interface library
+ * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published 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 <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#ifndef CONFIG_NATIVE_WINDOWS
+#include <sys/socket.h>
+#include <sys/un.h>
+#endif /* CONFIG_NATIVE_WINDOWS */
+
+#include "wpa_ctrl.h"
+#ifdef CONFIG_NATIVE_WINDOWS
+#include "common.h"
+#endif /* CONFIG_NATIVE_WINDOWS */
+
+
+struct wpa_ctrl {
+ int s;
+#ifdef CONFIG_CTRL_IFACE_UDP
+ struct sockaddr_in local;
+ struct sockaddr_in dest;
+#else /* CONFIG_CTRL_IFACE_UDP */
+ struct sockaddr_un local;
+ struct sockaddr_un dest;
+#endif /* CONFIG_CTRL_IFACE_UDP */
+};
+
+
+struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path)
+{
+ struct wpa_ctrl *ctrl;
+#ifndef CONFIG_CTRL_IFACE_UDP
+ static int counter = 0;
+#endif /* CONFIG_CTRL_IFACE_UDP */
+
+ ctrl = malloc(sizeof(*ctrl));
+ if (ctrl == NULL)
+ return NULL;
+ memset(ctrl, 0, sizeof(*ctrl));
+
+#ifdef CONFIG_CTRL_IFACE_UDP
+ ctrl->s = socket(PF_INET, SOCK_DGRAM, 0);
+ if (ctrl->s < 0) {
+ perror("socket");
+ free(ctrl);
+ return NULL;
+ }
+
+ ctrl->local.sin_family = AF_INET;
+ ctrl->local.sin_addr.s_addr = htonl((127 << 24) | 1);
+ if (bind(ctrl->s, (struct sockaddr *) &ctrl->local,
+ sizeof(ctrl->local)) < 0) {
+ close(ctrl->s);
+ free(ctrl);
+ return NULL;
+ }
+
+ ctrl->dest.sin_family = AF_INET;
+ ctrl->dest.sin_addr.s_addr = htonl((127 << 24) | 1);
+ ctrl->dest.sin_port = htons(9877);
+ if (connect(ctrl->s, (struct sockaddr *) &ctrl->dest,
+ sizeof(ctrl->dest)) < 0) {
+ perror("connect");
+ close(ctrl->s);
+ free(ctrl);
+ return NULL;
+ }
+#else /* CONFIG_CTRL_IFACE_UDP */
+ ctrl->s = socket(PF_UNIX, SOCK_DGRAM, 0);
+ if (ctrl->s < 0) {
+ free(ctrl);
+ return NULL;
+ }
+
+ ctrl->local.sun_family = AF_UNIX;
+ snprintf(ctrl->local.sun_path, sizeof(ctrl->local.sun_path),
+ "/tmp/wpa_ctrl_%d-%d", getpid(), counter++);
+ if (bind(ctrl->s, (struct sockaddr *) &ctrl->local,
+ sizeof(ctrl->local.sun_family) +
+ strlen(ctrl->local.sun_path)) < 0) {
+ close(ctrl->s);
+ free(ctrl);
+ return NULL;
+ }
+
+ ctrl->dest.sun_family = AF_UNIX;
+ strncpy(ctrl->dest.sun_path, ctrl_path, sizeof(ctrl->dest.sun_path));
+ if (connect(ctrl->s, (struct sockaddr *) &ctrl->dest,
+ sizeof(ctrl->dest.sun_family) +
+ strlen(ctrl->dest.sun_path)) < 0) {
+ close(ctrl->s);
+ unlink(ctrl->local.sun_path);
+ free(ctrl);
+ return NULL;
+ }
+#endif /* CONFIG_CTRL_IFACE_UDP */
+
+ return ctrl;
+}
+
+
+void wpa_ctrl_close(struct wpa_ctrl *ctrl)
+{
+#ifndef CONFIG_CTRL_IFACE_UDP
+ unlink(ctrl->local.sun_path);
+#endif /* CONFIG_CTRL_IFACE_UDP */
+ close(ctrl->s);
+ free(ctrl);
+}
+
+
+int wpa_ctrl_request(struct wpa_ctrl *ctrl, char *cmd, size_t cmd_len,
+ char *reply, size_t *reply_len,
+ void (*msg_cb)(char *msg, size_t len))
+{
+ struct timeval tv;
+ int res;
+ fd_set rfds;
+
+ if (send(ctrl->s, cmd, cmd_len, 0) < 0)
+ return -1;
+
+ for (;;) {
+ tv.tv_sec = 2;
+ tv.tv_usec = 0;
+ FD_ZERO(&rfds);
+ FD_SET(ctrl->s, &rfds);
+ res = select(ctrl->s + 1, &rfds, NULL, NULL, &tv);
+ if (FD_ISSET(ctrl->s, &rfds)) {
+ res = recv(ctrl->s, reply, *reply_len, 0);
+ if (res < 0)
+ return res;
+ if (res > 0 && reply[0] == '<') {
+ /* This is an unsolicited message from
+ * wpa_supplicant, not the reply to the
+ * request. Use msg_cb to report this to the
+ * caller. */
+ if (msg_cb) {
+ /* Make sure the message is nul
+ * terminated. */
+ if (res == *reply_len)
+ res = (*reply_len) - 1;
+ reply[res] = '\0';
+ msg_cb(reply, res);
+ }
+ continue;
+ }
+ *reply_len = res;
+ break;
+ } else {
+ return -2;
+ }
+ }
+ return 0;
+}
+
+
+static int wpa_ctrl_attach_helper(struct wpa_ctrl *ctrl, int attach)
+{
+ char buf[10];
+ int ret;
+ size_t len = 10;
+
+ ret = wpa_ctrl_request(ctrl, attach ? "ATTACH" : "DETACH", 6,
+ buf, &len, NULL);
+ if (ret < 0)
+ return ret;
+ if (len == 3 && memcmp(buf, "OK\n", 3) == 0)
+ return 0;
+ return -1;
+}
+
+
+int wpa_ctrl_attach(struct wpa_ctrl *ctrl)
+{
+ return wpa_ctrl_attach_helper(ctrl, 1);
+}
+
+
+int wpa_ctrl_detach(struct wpa_ctrl *ctrl)
+{
+ return wpa_ctrl_attach_helper(ctrl, 0);
+}
+
+
+int wpa_ctrl_recv(struct wpa_ctrl *ctrl, char *reply, size_t *reply_len)
+{
+ int res;
+
+ res = recv(ctrl->s, reply, *reply_len, 0);
+ if (res < 0)
+ return res;
+ *reply_len = res;
+ return 0;
+}
+
+
+int wpa_ctrl_pending(struct wpa_ctrl *ctrl)
+{
+ struct timeval tv;
+ int res;
+ fd_set rfds;
+ tv.tv_sec = 0;
+ tv.tv_usec = 0;
+ FD_ZERO(&rfds);
+ FD_SET(ctrl->s, &rfds);
+ res = select(ctrl->s + 1, &rfds, NULL, NULL, &tv);
+ return FD_ISSET(ctrl->s, &rfds);
+}
+
+
+int wpa_ctrl_get_fd(struct wpa_ctrl *ctrl)
+{
+ return ctrl->s;
+}
diff --git a/contrib/wpa_supplicant/wpa_ctrl.h b/contrib/wpa_supplicant/wpa_ctrl.h
new file mode 100644
index 000000000000..48b088abea52
--- /dev/null
+++ b/contrib/wpa_supplicant/wpa_ctrl.h
@@ -0,0 +1,15 @@
+#ifndef WPA_CTRL_H
+#define WPA_CTRL_H
+
+struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path);
+void wpa_ctrl_close(struct wpa_ctrl *ctrl);
+int wpa_ctrl_request(struct wpa_ctrl *ctrl, char *cmd, size_t cmd_len,
+ char *reply, size_t *reply_len,
+ void (*msg_cb)(char *msg, size_t len));
+int wpa_ctrl_attach(struct wpa_ctrl *ctrl);
+int wpa_ctrl_detach(struct wpa_ctrl *ctrl);
+int wpa_ctrl_recv(struct wpa_ctrl *ctrl, char *reply, size_t *reply_len);
+int wpa_ctrl_pending(struct wpa_ctrl *ctrl);
+int wpa_ctrl_get_fd(struct wpa_ctrl *ctrl);
+
+#endif /* WPA_CTRL_H */
diff --git a/contrib/wpa_supplicant/wpa_passphrase.c b/contrib/wpa_supplicant/wpa_passphrase.c
new file mode 100644
index 000000000000..5a8203b833f4
--- /dev/null
+++ b/contrib/wpa_supplicant/wpa_passphrase.c
@@ -0,0 +1,53 @@
+/*
+ * WPA Supplicant - ASCII passphrase to WPA PSK tool
+ * Copyright (c) 2003-2004, Jouni Malinen <jkmaline@cc.hut.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published 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 <stdio.h>
+#include <string.h>
+
+#include "common.h"
+#include "sha1.h"
+
+
+int main(int argc, char *argv[])
+{
+ unsigned char psk[32];
+ int i;
+ char *ssid, *passphrase;
+
+ if (argc != 3) {
+ printf("usage: wpa_passphrase <ssid> <passphrase>\n");
+ return 1;
+ }
+
+ ssid = argv[1];
+ passphrase = argv[2];
+
+ if (strlen(passphrase) < 8 || strlen(passphrase) > 63) {
+ printf("Passphrase must be 8..63 characters\n");
+ return 1;
+ }
+
+ pbkdf2_sha1(passphrase, ssid, strlen(ssid), 4096, psk, 32);
+
+ printf("network={\n");
+ printf("\tssid=\"%s\"\n", ssid);
+ printf("\t#psk=\"%s\"\n", passphrase);
+ printf("\tpsk=");
+ for (i = 0; i < 32; i++)
+ printf("%02x", psk[i]);
+ printf("\n");
+ printf("}\n");
+
+ return 0;
+}
diff --git a/contrib/wpa_supplicant/wpa_supplicant.c b/contrib/wpa_supplicant/wpa_supplicant.c
new file mode 100644
index 000000000000..10f12121471a
--- /dev/null
+++ b/contrib/wpa_supplicant/wpa_supplicant.c
@@ -0,0 +1,2439 @@
+/*
+ * WPA Supplicant
+ * Copyright (c) 2003-2005, Jouni Malinen <jkmaline@cc.hut.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published 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 <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/time.h>
+#include <time.h>
+#include <signal.h>
+#include <sys/types.h>
+#ifndef CONFIG_NATIVE_WINDOWS
+#include <sys/socket.h>
+#include <sys/un.h>
+#endif /* CONFIG_NATIVE_WINDOWS */
+#include <unistd.h>
+#include <ctype.h>
+#ifndef CONFIG_NATIVE_WINDOWS
+#include <netinet/in.h>
+#endif /* CONFIG_NATIVE_WINDOWS */
+#include <fcntl.h>
+
+#define OPENSSL_DISABLE_OLD_DES_SUPPORT
+#include "common.h"
+#include "eapol_sm.h"
+#include "eap.h"
+#include "wpa.h"
+#include "driver.h"
+#include "eloop.h"
+#include "wpa_supplicant.h"
+#include "config.h"
+#include "l2_packet.h"
+#include "wpa_supplicant_i.h"
+#include "ctrl_iface.h"
+#include "pcsc_funcs.h"
+#include "version.h"
+
+static const char *wpa_supplicant_version =
+"wpa_supplicant v" VERSION_STR "\n"
+"Copyright (c) 2003-2005, Jouni Malinen <jkmaline@cc.hut.fi> and contributors";
+
+static 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"
+#ifdef EAP_TLS_FUNCS
+"\nThis product includes software developed by the OpenSSL Project\n"
+"for use in the OpenSSL Toolkit (http://www.openssl.org/)\n"
+#endif /* EAP_TLS_FUNCS */
+;
+
+static const char *wpa_supplicant_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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\n"
+"\n"
+"Alternatively, this software may be distributed under the terms of the\n"
+"BSD license.\n"
+"\n"
+"Redistribution and use in source and binary forms, with or without\n"
+"modification, are permitted provided that the following conditions are\n"
+"met:\n"
+"\n"
+"1. Redistributions of source code must retain the above copyright\n"
+" notice, this list of conditions and the following disclaimer.\n"
+"\n"
+"2. Redistributions in binary form must reproduce the above copyright\n"
+" notice, this list of conditions and the following disclaimer in the\n"
+" documentation and/or other materials provided with the distribution.\n"
+"\n"
+"3. Neither the name(s) of the above-listed copyright holder(s) nor the\n"
+" names of its contributors may be used to endorse or promote products\n"
+" derived from this software without specific prior written permission.\n"
+"\n"
+"THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n"
+"\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n"
+"LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n"
+"A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n"
+"OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n"
+"SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n"
+"LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n"
+"DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n"
+"THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n"
+"(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n"
+"OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
+"\n";
+
+extern struct wpa_driver_ops *wpa_supplicant_drivers[];
+
+static void wpa_supplicant_scan_results(struct wpa_supplicant *wpa_s);
+static int wpa_supplicant_driver_init(struct wpa_supplicant *wpa_s,
+ int wait_for_interface);
+static void wpa_supplicant_associate(struct wpa_supplicant *wpa_s,
+ struct wpa_scan_result *bss,
+ struct wpa_ssid *ssid);
+static int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s,
+ struct wpa_scan_result *bss,
+ struct wpa_ssid *ssid,
+ u8 *wpa_ie, int *wpa_ie_len);
+
+
+extern int wpa_debug_level;
+extern int wpa_debug_show_keys;
+extern int wpa_debug_timestamp;
+
+
+void wpa_msg(struct wpa_supplicant *wpa_s, int level, char *fmt, ...)
+{
+ va_list ap;
+ char *buf;
+ const int buflen = 2048;
+ int len;
+
+ buf = malloc(buflen);
+ if (buf == NULL) {
+ printf("Failed to allocate message buffer for:\n");
+ va_start(ap, fmt);
+ vprintf(fmt, ap);
+ printf("\n");
+ va_end(ap);
+ return;
+ }
+ va_start(ap, fmt);
+ len = vsnprintf(buf, buflen, fmt, ap);
+ va_end(ap);
+ wpa_printf(level, "%s", buf);
+ wpa_supplicant_ctrl_iface_send(wpa_s, level, buf, len);
+ free(buf);
+}
+
+
+int wpa_eapol_send(void *ctx, int type, u8 *buf, size_t len)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+ u8 *msg, *dst, bssid[ETH_ALEN];
+ size_t msglen;
+ struct l2_ethhdr *ethhdr;
+ struct ieee802_1x_hdr *hdr;
+ int res;
+
+ /* TODO: could add l2_packet_sendmsg that allows fragments to avoid
+ * extra copy here */
+
+ if (wpa_s->key_mgmt == WPA_KEY_MGMT_PSK ||
+ wpa_s->key_mgmt == WPA_KEY_MGMT_NONE) {
+ /* Current SSID is not using IEEE 802.1X/EAP, so drop possible
+ * EAPOL frames (mainly, EAPOL-Start) from EAPOL state
+ * machines. */
+ wpa_printf(MSG_DEBUG, "WPA: drop TX EAPOL in non-IEEE 802.1X "
+ "mode (type=%d len=%lu)", type,
+ (unsigned long) len);
+ return -1;
+ }
+
+ if (wpa_s->cur_pmksa && type == IEEE802_1X_TYPE_EAPOL_START) {
+ /* Trying to use PMKSA caching - do not send EAPOL-Start frames
+ * since they will trigger full EAPOL authentication. */
+ wpa_printf(MSG_DEBUG, "RSN: PMKSA caching - do not send "
+ "EAPOL-Start");
+ return -1;
+ }
+
+ if (memcmp(wpa_s->bssid, "\x00\x00\x00\x00\x00\x00", ETH_ALEN) == 0) {
+ wpa_printf(MSG_DEBUG, "BSSID not set when trying to send an "
+ "EAPOL frame");
+ if (wpa_drv_get_bssid(wpa_s, bssid) == 0 &&
+ memcmp(bssid, "\x00\x00\x00\x00\x00\x00", ETH_ALEN) != 0) {
+ dst = bssid;
+ wpa_printf(MSG_DEBUG, "Using current BSSID " MACSTR
+ " from the driver as the EAPOL destination",
+ MAC2STR(dst));
+ } else {
+ dst = wpa_s->last_eapol_src;
+ wpa_printf(MSG_DEBUG, "Using the source address of the"
+ " last received EAPOL frame " MACSTR " as "
+ "the EAPOL destination",
+ MAC2STR(dst));
+ }
+ } else {
+ /* BSSID was already set (from (Re)Assoc event, so use it as
+ * the EAPOL destination. */
+ dst = wpa_s->bssid;
+ }
+
+ msglen = sizeof(*ethhdr) + sizeof(*hdr) + len;
+ msg = malloc(msglen);
+ if (msg == NULL)
+ return -1;
+
+ ethhdr = (struct l2_ethhdr *) msg;
+ memcpy(ethhdr->h_dest, dst, ETH_ALEN);
+ memcpy(ethhdr->h_source, wpa_s->own_addr, ETH_ALEN);
+ ethhdr->h_proto = htons(ETH_P_EAPOL);
+
+ hdr = (struct ieee802_1x_hdr *) (ethhdr + 1);
+ hdr->version = wpa_s->conf->eapol_version;
+ hdr->type = type;
+ hdr->length = htons(len);
+
+ memcpy((u8 *) (hdr + 1), buf, len);
+
+ wpa_hexdump(MSG_MSGDUMP, "TX EAPOL", msg, msglen);
+ res = l2_packet_send(wpa_s->l2, msg, msglen);
+ free(msg);
+ return res;
+}
+
+
+int wpa_eapol_send_preauth(void *ctx, int type, u8 *buf, size_t len)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+ u8 *msg;
+ size_t msglen;
+ struct l2_ethhdr *ethhdr;
+ struct ieee802_1x_hdr *hdr;
+ int res;
+
+ /* TODO: could add l2_packet_sendmsg that allows fragments to avoid
+ * extra copy here */
+
+ if (wpa_s->l2_preauth == NULL)
+ return -1;
+
+ msglen = sizeof(*ethhdr) + sizeof(*hdr) + len;
+ msg = malloc(msglen);
+ if (msg == NULL)
+ return -1;
+
+ ethhdr = (struct l2_ethhdr *) msg;
+ memcpy(ethhdr->h_dest, wpa_s->preauth_bssid, ETH_ALEN);
+ memcpy(ethhdr->h_source, wpa_s->own_addr, ETH_ALEN);
+ ethhdr->h_proto = htons(ETH_P_RSN_PREAUTH);
+
+ hdr = (struct ieee802_1x_hdr *) (ethhdr + 1);
+ hdr->version = wpa_s->conf->eapol_version;
+ hdr->type = type;
+ hdr->length = htons(len);
+
+ memcpy((u8 *) (hdr + 1), buf, len);
+
+ wpa_hexdump(MSG_MSGDUMP, "TX EAPOL (preauth)", msg, msglen);
+ res = l2_packet_send(wpa_s->l2_preauth, msg, msglen);
+ free(msg);
+ return res;
+}
+
+
+/**
+ * wpa_eapol_set_wep_key - set WEP key for the driver
+ * @ctx: pointer to wpa_supplicant data
+ * @unicast: 1 = individual unicast key, 0 = broadcast key
+ * @keyidx: WEP key index (0..3)
+ * @key: pointer to key data
+ * @keylen: key length in bytes
+ *
+ * Returns 0 on success or < 0 on error.
+ */
+static int wpa_eapol_set_wep_key(void *ctx, int unicast, int keyidx,
+ u8 *key, size_t keylen)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+ wpa_s->keys_cleared = 0;
+ return wpa_drv_set_key(wpa_s, WPA_ALG_WEP,
+ unicast ? wpa_s->bssid :
+ (u8 *) "\xff\xff\xff\xff\xff\xff",
+ keyidx, unicast, (u8 *) "", 0, key, keylen);
+}
+
+
+/* Configure default/group WEP key for static WEP */
+static int wpa_set_wep_key(void *ctx, int set_tx, int keyidx, const u8 *key,
+ size_t keylen)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+ wpa_s->keys_cleared = 0;
+ return wpa_drv_set_key(wpa_s, WPA_ALG_WEP,
+ (u8 *) "\xff\xff\xff\xff\xff\xff",
+ keyidx, set_tx, (u8 *) "", 0, key, keylen);
+}
+
+
+static int wpa_supplicant_set_wpa_none_key(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid)
+{
+ u8 key[32];
+ size_t keylen;
+ wpa_alg alg;
+ u8 seq[6] = { 0 };
+
+ /* IBSS/WPA-None uses only one key (Group) for both receiving and
+ * sending unicast and multicast packets. */
+
+ if (ssid->mode != IEEE80211_MODE_IBSS) {
+ wpa_printf(MSG_INFO, "WPA: Invalid mode %d (not IBSS/ad-hoc) "
+ "for WPA-None", ssid->mode);
+ return -1;
+ }
+
+ if (!ssid->psk_set) {
+ wpa_printf(MSG_INFO, "WPA: No PSK configured for WPA-None");
+ return -1;
+ }
+
+ switch (wpa_s->group_cipher) {
+ case WPA_CIPHER_CCMP:
+ memcpy(key, ssid->psk, 16);
+ keylen = 16;
+ alg = WPA_ALG_CCMP;
+ break;
+ case WPA_CIPHER_TKIP:
+ /* WPA-None uses the same Michael MIC key for both TX and RX */
+ memcpy(key, ssid->psk, 16 + 8);
+ memcpy(key + 16 + 8, ssid->psk + 16, 8);
+ keylen = 32;
+ alg = WPA_ALG_TKIP;
+ break;
+ default:
+ wpa_printf(MSG_INFO, "WPA: Invalid group cipher %d for "
+ "WPA-None", wpa_s->group_cipher);
+ return -1;
+ }
+
+ /* TODO: should actually remember the previously used seq#, both for TX
+ * and RX from each STA.. */
+
+ return wpa_drv_set_key(wpa_s, alg, (u8 *) "\xff\xff\xff\xff\xff\xff",
+ 0, 1, seq, 6, key, keylen);
+}
+
+
+void wpa_supplicant_notify_eapol_done(void *ctx)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+ wpa_msg(wpa_s, MSG_DEBUG, "WPA: EAPOL processing complete");
+ eloop_cancel_timeout(wpa_supplicant_scan, wpa_s, NULL);
+ wpa_supplicant_cancel_auth_timeout(wpa_s);
+}
+
+
+static int wpa_blacklisted(struct wpa_supplicant *wpa_s, const u8 *bssid)
+{
+ struct wpa_blacklist *e;
+
+ e = wpa_s->blacklist;
+ while (e) {
+ if (memcmp(e->bssid, bssid, ETH_ALEN) == 0)
+ return 1;
+ e = e->next;
+ }
+
+ return 0;
+}
+
+
+static int wpa_blacklist_add(struct wpa_supplicant *wpa_s, const u8 *bssid)
+{
+ struct wpa_blacklist *e;
+
+ if (wpa_blacklisted(wpa_s, bssid))
+ return 0;
+
+ e = malloc(sizeof(*e));
+ if (e == NULL)
+ return -1;
+ memset(e, 0, sizeof(*e));
+ memcpy(e->bssid, bssid, ETH_ALEN);
+ e->next = wpa_s->blacklist;
+ wpa_s->blacklist = e;
+ wpa_printf(MSG_DEBUG, "Added BSSID " MACSTR " into blacklist",
+ MAC2STR(bssid));
+
+ return 0;
+}
+
+
+static int wpa_blacklist_del(struct wpa_supplicant *wpa_s, const u8 *bssid)
+{
+ struct wpa_blacklist *e, *prev = NULL;
+
+ e = wpa_s->blacklist;
+ while (e) {
+ if (memcmp(e->bssid, bssid, ETH_ALEN) == 0) {
+ if (prev == NULL) {
+ wpa_s->blacklist = e->next;
+ } else {
+ prev->next = e->next;
+ }
+ wpa_printf(MSG_DEBUG, "Removed BSSID " MACSTR " from "
+ "blacklist", MAC2STR(bssid));
+ free(e);
+ return 0;
+ }
+ prev = e;
+ e = e->next;
+ }
+ return -1;
+}
+
+
+static void wpa_blacklist_clear(struct wpa_supplicant *wpa_s)
+{
+ struct wpa_blacklist *e, *prev;
+
+ e = wpa_s->blacklist;
+ wpa_s->blacklist = NULL;
+ while (e) {
+ prev = e;
+ e = e->next;
+ wpa_printf(MSG_DEBUG, "Removed BSSID " MACSTR " from "
+ "blacklist (clear)", MAC2STR(prev->bssid));
+ free(prev);
+ }
+}
+
+
+const char * wpa_ssid_txt(u8 *ssid, size_t ssid_len)
+{
+ static char ssid_txt[MAX_SSID_LEN + 1];
+ char *pos;
+
+ if (ssid_len > MAX_SSID_LEN)
+ ssid_len = MAX_SSID_LEN;
+ 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 = '_';
+ }
+ return ssid_txt;
+}
+
+
+void wpa_supplicant_req_scan(struct wpa_supplicant *wpa_s, int sec, int usec)
+{
+ wpa_msg(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);
+}
+
+
+void wpa_supplicant_cancel_scan(struct wpa_supplicant *wpa_s)
+{
+ wpa_msg(wpa_s, MSG_DEBUG, "Cancelling scan request");
+ eloop_cancel_timeout(wpa_supplicant_scan, wpa_s, NULL);
+}
+
+
+static void wpa_supplicant_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_supplicant *wpa_s = eloop_ctx;
+ wpa_msg(wpa_s, MSG_INFO, "Authentication with " MACSTR " timed out.",
+ MAC2STR(wpa_s->bssid));
+ wpa_blacklist_add(wpa_s, wpa_s->bssid);
+ wpa_supplicant_disassociate(wpa_s, REASON_DEAUTH_LEAVING);
+ wpa_s->reassociate = 1;
+ wpa_supplicant_req_scan(wpa_s, 0, 0);
+}
+
+
+void wpa_supplicant_req_auth_timeout(struct wpa_supplicant *wpa_s,
+ int sec, int usec)
+{
+ wpa_msg(wpa_s, MSG_DEBUG, "Setting authentication timeout: %d sec "
+ "%d usec", sec, usec);
+ eloop_cancel_timeout(wpa_supplicant_timeout, wpa_s, NULL);
+ eloop_register_timeout(sec, usec, wpa_supplicant_timeout, wpa_s, NULL);
+}
+
+
+void wpa_supplicant_cancel_auth_timeout(struct wpa_supplicant *wpa_s)
+{
+ wpa_msg(wpa_s, MSG_DEBUG, "Cancelling authentication timeout");
+ eloop_cancel_timeout(wpa_supplicant_timeout, wpa_s, NULL);
+ wpa_blacklist_del(wpa_s, wpa_s->bssid);
+}
+
+
+static void wpa_supplicant_initiate_eapol(struct wpa_supplicant *wpa_s)
+{
+ struct eapol_config eapol_conf;
+ struct wpa_ssid *ssid = wpa_s->current_ssid;
+
+ if (wpa_s->key_mgmt == WPA_KEY_MGMT_PSK) {
+ eapol_sm_notify_eap_success(wpa_s->eapol, FALSE);
+ eapol_sm_notify_eap_fail(wpa_s->eapol, FALSE);
+ }
+ if (wpa_s->key_mgmt == WPA_KEY_MGMT_NONE ||
+ wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE)
+ eapol_sm_notify_portControl(wpa_s->eapol, ForceAuthorized);
+ else
+ eapol_sm_notify_portControl(wpa_s->eapol, Auto);
+
+ memset(&eapol_conf, 0, sizeof(eapol_conf));
+ if (wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA) {
+ eapol_conf.accept_802_1x_keys = 1;
+ eapol_conf.required_keys = 0;
+ if (ssid->eapol_flags & EAPOL_FLAG_REQUIRE_KEY_UNICAST) {
+ eapol_conf.required_keys |= EAPOL_REQUIRE_KEY_UNICAST;
+ }
+ if (ssid->eapol_flags & EAPOL_FLAG_REQUIRE_KEY_BROADCAST) {
+ eapol_conf.required_keys |=
+ EAPOL_REQUIRE_KEY_BROADCAST;
+ }
+ }
+ eapol_conf.fast_reauth = wpa_s->conf->fast_reauth;
+ eapol_conf.workaround = ssid->eap_workaround;
+ eapol_sm_notify_config(wpa_s->eapol, ssid, &eapol_conf);
+}
+
+
+static void wpa_supplicant_set_non_wpa_policy(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid)
+{
+ int i;
+
+ if (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA)
+ wpa_s->key_mgmt = WPA_KEY_MGMT_IEEE8021X_NO_WPA;
+ else
+ wpa_s->key_mgmt = WPA_KEY_MGMT_NONE;
+ free(wpa_s->ap_wpa_ie);
+ wpa_s->ap_wpa_ie = NULL;
+ wpa_s->ap_wpa_ie_len = 0;
+ free(wpa_s->ap_rsn_ie);
+ wpa_s->ap_rsn_ie = NULL;
+ wpa_s->ap_rsn_ie_len = 0;
+ free(wpa_s->assoc_wpa_ie);
+ wpa_s->assoc_wpa_ie = NULL;
+ wpa_s->assoc_wpa_ie_len = 0;
+ wpa_s->pairwise_cipher = WPA_CIPHER_NONE;
+ wpa_s->group_cipher = WPA_CIPHER_NONE;
+
+ for (i = 0; i < NUM_WEP_KEYS; i++) {
+ if (ssid->wep_key_len[i] > 5) {
+ wpa_s->pairwise_cipher = WPA_CIPHER_WEP104;
+ wpa_s->group_cipher = WPA_CIPHER_WEP104;
+ break;
+ } else if (ssid->wep_key_len[i] > 0) {
+ wpa_s->pairwise_cipher = WPA_CIPHER_WEP40;
+ wpa_s->group_cipher = WPA_CIPHER_WEP40;
+ break;
+ }
+ }
+
+ wpa_s->cur_pmksa = NULL;
+}
+
+
+static int wpa_supplicant_select_config(struct wpa_supplicant *wpa_s)
+{
+ struct wpa_ssid *ssid;
+
+ if (wpa_s->conf->ap_scan == 1)
+ return 0;
+
+ ssid = wpa_supplicant_get_ssid(wpa_s);
+ if (ssid == NULL) {
+ wpa_printf(MSG_INFO, "No network configuration found for the "
+ "current AP");
+ return -1;
+ }
+
+ wpa_printf(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)) {
+ u8 wpa_ie[80];
+ int wpa_ie_len;
+ wpa_supplicant_set_suites(wpa_s, NULL, ssid,
+ wpa_ie, &wpa_ie_len);
+ } else {
+ wpa_supplicant_set_non_wpa_policy(wpa_s, ssid);
+ }
+
+ wpa_s->current_ssid = ssid;
+ wpa_supplicant_initiate_eapol(wpa_s);
+
+ return 0;
+}
+
+
+static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s)
+{
+ scard_deinit(wpa_s->scard);
+ wpa_s->scard = NULL;
+ eapol_sm_register_scard_ctx(wpa_s->eapol, NULL);
+ l2_packet_deinit(wpa_s->l2);
+ wpa_s->l2 = NULL;
+
+#ifdef CONFIG_XSUPPLICANT_IFACE
+ if (wpa_s->dot1x_s > -1) {
+ close(wpa_s->dot1x_s);
+ wpa_s->dot1x_s = -1;
+ }
+#endif /* CONFIG_XSUPPLICANT_IFACE */
+
+ wpa_supplicant_ctrl_iface_deinit(wpa_s);
+ if (wpa_s->conf != NULL) {
+ wpa_config_free(wpa_s->conf);
+ wpa_s->conf = NULL;
+ }
+
+ free(wpa_s->assoc_wpa_ie);
+ wpa_s->assoc_wpa_ie = NULL;
+
+ free(wpa_s->ap_wpa_ie);
+ wpa_s->ap_wpa_ie = NULL;
+ free(wpa_s->ap_rsn_ie);
+ wpa_s->ap_rsn_ie = NULL;
+
+ free(wpa_s->confname);
+ wpa_s->confname = NULL;
+
+ eapol_sm_deinit(wpa_s->eapol);
+ wpa_s->eapol = NULL;
+
+ rsn_preauth_deinit(wpa_s);
+
+ pmksa_candidate_free(wpa_s);
+ pmksa_cache_free(wpa_s);
+ wpa_blacklist_clear(wpa_s);
+
+ free(wpa_s->scan_results);
+ wpa_s->scan_results = NULL;
+ wpa_s->num_scan_results = 0;
+}
+
+
+static void wpa_clear_keys(struct wpa_supplicant *wpa_s, u8 *addr)
+{
+ u8 *bcast = (u8 *) "\xff\xff\xff\xff\xff\xff";
+
+ 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_printf(MSG_DEBUG, "No keys have been configured - "
+ "skip key clearing");
+ return;
+ }
+
+ wpa_drv_set_key(wpa_s, WPA_ALG_NONE, bcast, 0, 0, NULL, 0, NULL, 0);
+ wpa_drv_set_key(wpa_s, WPA_ALG_NONE, bcast, 1, 0, NULL, 0, NULL, 0);
+ wpa_drv_set_key(wpa_s, WPA_ALG_NONE, bcast, 2, 0, NULL, 0, NULL, 0);
+ wpa_drv_set_key(wpa_s, WPA_ALG_NONE, bcast, 3, 0, NULL, 0, NULL, 0);
+ if (addr) {
+ wpa_drv_set_key(wpa_s, WPA_ALG_NONE, addr, 0, 0, NULL, 0, NULL,
+ 0);
+ }
+ wpa_s->keys_cleared = 1;
+}
+
+
+static void wpa_supplicant_stop_countermeasures(void *eloop_ctx,
+ void *sock_ctx)
+{
+ struct wpa_supplicant *wpa_s = eloop_ctx;
+
+ if (wpa_s->countermeasures) {
+ wpa_s->countermeasures = 0;
+ wpa_drv_set_countermeasures(wpa_s, 0);
+ wpa_msg(wpa_s, MSG_INFO, "WPA: TKIP countermeasures stopped");
+ wpa_supplicant_req_scan(wpa_s, 0, 0);
+ }
+}
+
+
+static void wpa_supplicant_mark_disassoc(struct wpa_supplicant *wpa_s)
+{
+ wpa_s->wpa_state = WPA_DISCONNECTED;
+ memset(wpa_s->bssid, 0, ETH_ALEN);
+ eapol_sm_notify_portEnabled(wpa_s->eapol, FALSE);
+ eapol_sm_notify_portValid(wpa_s->eapol, FALSE);
+ if (wpa_s->key_mgmt == WPA_KEY_MGMT_PSK)
+ eapol_sm_notify_eap_success(wpa_s->eapol, FALSE);
+}
+
+
+static void wpa_find_assoc_pmkid(struct wpa_supplicant *wpa_s)
+{
+ struct wpa_ie_data ie;
+ int i;
+
+ if (wpa_parse_wpa_ie(wpa_s, wpa_s->assoc_wpa_ie,
+ wpa_s->assoc_wpa_ie_len, &ie) < 0 ||
+ ie.pmkid == NULL)
+ return;
+
+ for (i = 0; i < ie.num_pmkid; i++) {
+ wpa_s->cur_pmksa = pmksa_cache_get(wpa_s, NULL,
+ ie.pmkid + i * PMKID_LEN);
+ if (wpa_s->cur_pmksa) {
+ eapol_sm_notify_pmkid_attempt(wpa_s->eapol, 1);
+ break;
+ }
+ }
+
+ wpa_printf(MSG_DEBUG, "RSN: PMKID from assoc IE %sfound from PMKSA "
+ "cache", wpa_s->cur_pmksa ? "" : "not ");
+}
+
+
+static void wpa_supplicant_add_pmkid_candidate(struct wpa_supplicant *wpa_s,
+ union wpa_event_data *data)
+{
+ if (data == NULL) {
+ wpa_printf(MSG_DEBUG, "RSN: No data in PMKID candidate event");
+ return;
+ }
+ wpa_printf(MSG_DEBUG, "RSN: PMKID candidate event - bssid=" MACSTR
+ " index=%d preauth=%d",
+ MAC2STR(data->pmkid_candidate.bssid),
+ data->pmkid_candidate.index,
+ data->pmkid_candidate.preauth);
+
+ if (!data->pmkid_candidate.preauth) {
+ wpa_printf(MSG_DEBUG, "RSN: Ignored PMKID candidate without "
+ "preauth flag");
+ return;
+ }
+
+ pmksa_candidate_add(wpa_s, data->pmkid_candidate.bssid,
+ data->pmkid_candidate.index);
+}
+
+
+static int wpa_supplicant_dynamic_keys(struct wpa_supplicant *wpa_s)
+{
+ if (wpa_s->key_mgmt == WPA_KEY_MGMT_NONE ||
+ wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE)
+ return 0;
+
+ if (wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA &&
+ wpa_s->current_ssid &&
+ !(wpa_s->current_ssid->eapol_flags &
+ (EAPOL_FLAG_REQUIRE_KEY_UNICAST |
+ EAPOL_FLAG_REQUIRE_KEY_BROADCAST))) {
+ /* IEEE 802.1X, but not using dynamic WEP keys (i.e., either
+ * plaintext or static WEP keys). */
+ return 0;
+ }
+
+ return 1;
+}
+
+
+static void wpa_supplicant_associnfo(struct wpa_supplicant *wpa_s,
+ union wpa_event_data *data)
+{
+ int l, len;
+ u8 *p;
+
+ wpa_printf(MSG_DEBUG, "Association info event");
+ wpa_hexdump(MSG_DEBUG, "req_ies", data->assoc_info.req_ies,
+ data->assoc_info.req_ies_len);
+ wpa_hexdump(MSG_DEBUG, "resp_ies", data->assoc_info.resp_ies,
+ data->assoc_info.resp_ies_len);
+ if (wpa_s->assoc_wpa_ie) {
+ free(wpa_s->assoc_wpa_ie);
+ wpa_s->assoc_wpa_ie = NULL;
+ wpa_s->assoc_wpa_ie_len = 0;
+ }
+
+ 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 (l >= 2) {
+ len = p[1] + 2;
+ if (len > l) {
+ wpa_hexdump(MSG_DEBUG, "Truncated IE in assoc_info",
+ p, l);
+ break;
+ }
+ if ((p[0] == GENERIC_INFO_ELEM && p[1] >= 6 &&
+ (memcmp(&p[2], "\x00\x50\xF2\x01\x01\x00", 6) == 0)) ||
+ (p[0] == RSN_INFO_ELEM && p[1] >= 2)) {
+ wpa_s->assoc_wpa_ie = malloc(len);
+ if (wpa_s->assoc_wpa_ie == NULL)
+ break;
+ wpa_s->assoc_wpa_ie_len = len;
+ memcpy(wpa_s->assoc_wpa_ie, p, len);
+ wpa_hexdump(MSG_DEBUG, "assoc_wpa_ie",
+ wpa_s->assoc_wpa_ie,
+ wpa_s->assoc_wpa_ie_len);
+ wpa_find_assoc_pmkid(wpa_s);
+ break;
+ }
+ l -= len;
+ p += len;
+ }
+
+ /* WPA/RSN IE from Beacon/ProbeResp */
+ free(wpa_s->ap_wpa_ie);
+ wpa_s->ap_wpa_ie = NULL;
+ wpa_s->ap_wpa_ie_len = 0;
+ free(wpa_s->ap_rsn_ie);
+ wpa_s->ap_rsn_ie = NULL;
+ wpa_s->ap_rsn_ie_len = 0;
+
+ p = data->assoc_info.beacon_ies;
+ l = data->assoc_info.beacon_ies_len;
+
+ /* Go through the IEs and make a copy of the WPA/RSN IEs, if present.
+ */
+ while (l >= 2) {
+ len = p[1] + 2;
+ if (len > l) {
+ wpa_hexdump(MSG_DEBUG, "Truncated IE in beacon_ies",
+ p, l);
+ break;
+ }
+ if (wpa_s->ap_wpa_ie == NULL &&
+ p[0] == GENERIC_INFO_ELEM && p[1] >= 6 &&
+ memcmp(&p[2], "\x00\x50\xF2\x01\x01\x00", 6) == 0) {
+ wpa_s->ap_wpa_ie = malloc(len);
+ if (wpa_s->ap_wpa_ie) {
+ memcpy(wpa_s->ap_wpa_ie, p, len);
+ wpa_s->ap_wpa_ie_len = len;
+ }
+ }
+
+ if (wpa_s->ap_rsn_ie == NULL &&
+ p[0] == RSN_INFO_ELEM && p[1] >= 2) {
+ wpa_s->ap_rsn_ie = malloc(len);
+ if (wpa_s->ap_rsn_ie) {
+ memcpy(wpa_s->ap_rsn_ie, p, len);
+ wpa_s->ap_rsn_ie_len = len;
+ }
+
+ }
+
+ l -= len;
+ p += len;
+ }
+
+}
+
+
+void wpa_supplicant_event(struct wpa_supplicant *wpa_s, wpa_event_type event,
+ union wpa_event_data *data)
+{
+ int pairwise;
+ time_t now;
+ u8 bssid[ETH_ALEN];
+
+ switch (event) {
+ case EVENT_ASSOC:
+ wpa_s->wpa_state = WPA_ASSOCIATED;
+ wpa_printf(MSG_DEBUG, "Association event - clear replay "
+ "counter");
+ memset(wpa_s->rx_replay_counter, 0, WPA_REPLAY_COUNTER_LEN);
+ wpa_s->rx_replay_counter_set = 0;
+ wpa_s->renew_snonce = 1;
+ if (wpa_drv_get_bssid(wpa_s, bssid) >= 0 &&
+ memcmp(bssid, wpa_s->bssid, ETH_ALEN) != 0) {
+ wpa_msg(wpa_s, MSG_DEBUG, "Associated to a new BSS: "
+ "BSSID=" MACSTR, MAC2STR(bssid));
+ memcpy(wpa_s->bssid, bssid, ETH_ALEN);
+ if (wpa_supplicant_dynamic_keys(wpa_s)) {
+ wpa_clear_keys(wpa_s, bssid);
+ }
+ wpa_supplicant_select_config(wpa_s);
+ }
+ wpa_msg(wpa_s, MSG_INFO, "Associated with " MACSTR,
+ MAC2STR(bssid));
+ /* Set portEnabled first to FALSE in order to get EAP state
+ * machine out of the SUCCESS state and eapSuccess cleared.
+ * Without this, EAPOL PAE state machine may transit to
+ * AUTHENTICATING state based on obsolete eapSuccess and then
+ * trigger BE_AUTH to SUCCESS and PAE to AUTHENTICATED without
+ * ever giving chance to EAP state machine to reset the state.
+ */
+ eapol_sm_notify_portEnabled(wpa_s->eapol, FALSE);
+ eapol_sm_notify_portValid(wpa_s->eapol, FALSE);
+ if (wpa_s->key_mgmt == WPA_KEY_MGMT_PSK)
+ eapol_sm_notify_eap_success(wpa_s->eapol, FALSE);
+ /* 802.1X::portControl = Auto */
+ eapol_sm_notify_portEnabled(wpa_s->eapol, TRUE);
+ wpa_s->eapol_received = 0;
+ if (wpa_s->key_mgmt == WPA_KEY_MGMT_NONE ||
+ wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE) {
+ wpa_supplicant_cancel_auth_timeout(wpa_s);
+ } else {
+ /* Timeout for receiving the first EAPOL packet */
+ wpa_supplicant_req_auth_timeout(wpa_s, 10, 0);
+ }
+ break;
+ case EVENT_DISASSOC:
+ if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE) {
+ /* At least Host AP driver and a Prism3 card seemed to
+ * be generating streams of disconnected events when
+ * configuring IBSS for WPA-None. Ignore them for now.
+ */
+ wpa_printf(MSG_DEBUG, "Disconnect event - ignore in "
+ "IBSS/WPA-None mode");
+ break;
+ }
+ if (wpa_s->wpa_state >= WPA_ASSOCIATED)
+ wpa_supplicant_req_scan(wpa_s, 0, 100000);
+ wpa_blacklist_add(wpa_s, wpa_s->bssid);
+ wpa_supplicant_mark_disassoc(wpa_s);
+ wpa_msg(wpa_s, MSG_INFO, "Disconnect event - remove keys");
+ if (wpa_supplicant_dynamic_keys(wpa_s)) {
+ wpa_s->keys_cleared = 0;
+ wpa_clear_keys(wpa_s, wpa_s->bssid);
+ }
+ break;
+ case EVENT_MICHAEL_MIC_FAILURE:
+ wpa_msg(wpa_s, MSG_WARNING, "Michael MIC failure detected");
+ pairwise = (data && data->michael_mic_failure.unicast);
+ wpa_supplicant_key_request(wpa_s, 1, pairwise);
+ time(&now);
+ if (wpa_s->last_michael_mic_error &&
+ now - wpa_s->last_michael_mic_error <= 60) {
+ /* initialize countermeasures */
+ wpa_s->countermeasures = 1;
+ wpa_msg(wpa_s, MSG_WARNING, "TKIP countermeasures "
+ "started");
+
+ /* Need to wait for completion of request frame. We do
+ * not get any callback for the message completion, so
+ * just wait a short while and hope for the best. */
+ usleep(10000);
+
+ wpa_drv_set_countermeasures(wpa_s, 1);
+ wpa_supplicant_deauthenticate(
+ wpa_s, REASON_MICHAEL_MIC_FAILURE);
+ eloop_cancel_timeout(
+ wpa_supplicant_stop_countermeasures, wpa_s,
+ NULL);
+ eloop_register_timeout(
+ 60, 0, wpa_supplicant_stop_countermeasures,
+ wpa_s, NULL);
+ /* TODO: mark the AP rejected for 60 second. STA is
+ * allowed to associate with another AP.. */
+ }
+ wpa_s->last_michael_mic_error = now;
+ break;
+ case EVENT_SCAN_RESULTS:
+ wpa_supplicant_scan_results(wpa_s);
+ break;
+ case EVENT_ASSOCINFO:
+ wpa_supplicant_associnfo(wpa_s, data);
+ break;
+ case EVENT_INTERFACE_STATUS:
+ if (strcmp(wpa_s->ifname, data->interface_status.ifname) != 0)
+ break;
+ switch (data->interface_status.ievent) {
+ case EVENT_INTERFACE_ADDED:
+ if (!wpa_s->interface_removed)
+ break;
+ wpa_s->interface_removed = 0;
+ wpa_printf(MSG_DEBUG, "Configured interface was "
+ "added.");
+ if (wpa_supplicant_driver_init(wpa_s, 1) < 0) {
+ wpa_printf(MSG_INFO, "Failed to initialize "
+ "the driver after interface was "
+ "added.");
+ }
+ break;
+ case EVENT_INTERFACE_REMOVED:
+ wpa_printf(MSG_DEBUG, "Configured interface was "
+ "removed.");
+ wpa_s->interface_removed = 1;
+ wpa_supplicant_mark_disassoc(wpa_s);
+ l2_packet_deinit(wpa_s->l2);
+ break;
+ }
+ break;
+ case EVENT_PMKID_CANDIDATE:
+ wpa_supplicant_add_pmkid_candidate(wpa_s, data);
+ break;
+ default:
+ wpa_printf(MSG_INFO, "Unknown event %d", event);
+ break;
+ }
+}
+
+
+static void wpa_supplicant_terminate(int sig, void *eloop_ctx,
+ void *signal_ctx)
+{
+ struct wpa_supplicant *wpa_s = eloop_ctx;
+ for (wpa_s = wpa_s->head; wpa_s; wpa_s = wpa_s->next) {
+ wpa_msg(wpa_s, MSG_INFO, "Signal %d received - terminating",
+ sig);
+ }
+ eloop_terminate();
+}
+
+
+int wpa_supplicant_reload_configuration(struct wpa_supplicant *wpa_s)
+{
+ struct wpa_config *conf;
+ int reconf_ctrl;
+ if (wpa_s->confname == NULL)
+ return -1;
+ conf = wpa_config_read(wpa_s->confname);
+ if (conf == NULL) {
+ wpa_msg(wpa_s, MSG_ERROR, "Failed to parse the configuration "
+ "file '%s' - exiting", wpa_s->confname);
+ return -1;
+ }
+
+ reconf_ctrl = !!conf->ctrl_interface != !!wpa_s->conf->ctrl_interface
+ || (conf->ctrl_interface && wpa_s->conf->ctrl_interface &&
+ strcmp(conf->ctrl_interface, wpa_s->conf->ctrl_interface)
+ != 0);
+
+ if (reconf_ctrl)
+ wpa_supplicant_ctrl_iface_deinit(wpa_s);
+
+ wpa_s->current_ssid = NULL;
+ eapol_sm_notify_config(wpa_s->eapol, NULL, NULL);
+ rsn_preauth_deinit(wpa_s);
+ wpa_config_free(wpa_s->conf);
+ wpa_s->conf = conf;
+ if (reconf_ctrl)
+ wpa_supplicant_ctrl_iface_init(wpa_s);
+ wpa_s->reassociate = 1;
+ wpa_supplicant_req_scan(wpa_s, 0, 0);
+ wpa_msg(wpa_s, MSG_DEBUG, "Reconfiguration completed");
+ return 0;
+}
+
+
+#ifndef CONFIG_NATIVE_WINDOWS
+static void wpa_supplicant_reconfig(int sig, void *eloop_ctx,
+ void *signal_ctx)
+{
+ struct wpa_supplicant *wpa_s = eloop_ctx;
+ wpa_printf(MSG_DEBUG, "Signal %d received - reconfiguring", sig);
+ for (wpa_s = wpa_s->head; wpa_s; wpa_s = wpa_s->next) {
+ if (wpa_supplicant_reload_configuration(wpa_s) < 0) {
+ eloop_terminate();
+ }
+ }
+}
+#endif /* CONFIG_NATIVE_WINDOWS */
+
+
+static void wpa_supplicant_gen_assoc_event(struct wpa_supplicant *wpa_s)
+{
+ struct wpa_ssid *ssid;
+ union wpa_event_data data;
+
+ ssid = wpa_supplicant_get_ssid(wpa_s);
+ if (ssid == NULL)
+ return;
+
+ wpa_printf(MSG_DEBUG, "Already associated with a configured network - "
+ "generating associated event");
+ memset(&data, 0, sizeof(data));
+ wpa_supplicant_event(wpa_s, EVENT_ASSOC, &data);
+}
+
+
+void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_supplicant *wpa_s = eloop_ctx;
+ struct wpa_ssid *ssid;
+
+ if (wpa_s->conf->ap_scan == 0) {
+ wpa_supplicant_gen_assoc_event(wpa_s);
+ return;
+ }
+
+ if (wpa_s->conf->ap_scan == 2) {
+ ssid = wpa_s->conf->ssid;
+ if (ssid == NULL)
+ return;
+ wpa_supplicant_associate(wpa_s, NULL, ssid);
+ return;
+ }
+
+ if (wpa_s->wpa_state == WPA_DISCONNECTED)
+ wpa_s->wpa_state = WPA_SCANNING;
+
+ ssid = wpa_s->conf->ssid;
+ if (wpa_s->prev_scan_ssid != BROADCAST_SSID_SCAN) {
+ while (ssid) {
+ if (ssid == wpa_s->prev_scan_ssid) {
+ ssid = ssid->next;
+ break;
+ }
+ ssid = ssid->next;
+ }
+ }
+ while (ssid) {
+ if (ssid->scan_ssid)
+ break;
+ ssid = ssid->next;
+ }
+
+ wpa_printf(MSG_DEBUG, "Starting AP scan (%s SSID)",
+ ssid ? "specific": "broadcast");
+ if (ssid) {
+ wpa_hexdump_ascii(MSG_DEBUG, "Scan SSID",
+ ssid->ssid, ssid->ssid_len);
+ wpa_s->prev_scan_ssid = ssid;
+ } else
+ wpa_s->prev_scan_ssid = BROADCAST_SSID_SCAN;
+
+ if (wpa_drv_scan(wpa_s, ssid ? ssid->ssid : NULL,
+ ssid ? ssid->ssid_len : 0)) {
+ wpa_printf(MSG_WARNING, "Failed to initiate AP scan.");
+ wpa_supplicant_req_scan(wpa_s, 10, 0);
+ }
+}
+
+
+static 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;
+ }
+}
+
+
+static 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_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) {
+ if (wpa_s->assoc_wpa_ie == NULL)
+ return -1;
+
+ if (wpa_parse_wpa_ie(wpa_s, wpa_s->assoc_wpa_ie,
+ wpa_s->assoc_wpa_ie_len, ie)) {
+ wpa_msg(wpa_s, MSG_INFO, "WPA: Failed to parse WPA IE from "
+ "association info");
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "WPA: Using WPA IE from AssocReq to set cipher "
+ "suites");
+ if (!(ie->group_cipher & ssid->group_cipher)) {
+ wpa_msg(wpa_s, MSG_INFO, "WPA: Driver used disabled group "
+ "cipher 0x%x (mask 0x%x) - reject",
+ ie->group_cipher, ssid->group_cipher);
+ return -1;
+ }
+ if (!(ie->pairwise_cipher & ssid->pairwise_cipher)) {
+ wpa_msg(wpa_s, MSG_INFO, "WPA: Driver used disabled pairwise "
+ "cipher 0x%x (mask 0x%x) - reject",
+ ie->pairwise_cipher, ssid->pairwise_cipher);
+ return -1;
+ }
+ if (!(ie->key_mgmt & ssid->key_mgmt)) {
+ wpa_msg(wpa_s, MSG_INFO, "WPA: Driver used disabled key "
+ "management 0x%x (mask 0x%x) - reject",
+ ie->key_mgmt, ssid->key_mgmt);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s,
+ struct wpa_scan_result *bss,
+ struct wpa_ssid *ssid,
+ u8 *wpa_ie, int *wpa_ie_len)
+{
+ struct wpa_ie_data ie;
+ int sel, proto;
+ u8 *ap_ie;
+ size_t ap_ie_len;
+
+ if (bss && bss->rsn_ie_len && (ssid->proto & WPA_PROTO_RSN)) {
+ wpa_msg(wpa_s, MSG_DEBUG, "RSN: using IEEE 802.11i/D9.0");
+ proto = WPA_PROTO_RSN;
+ ap_ie = bss->rsn_ie;
+ ap_ie_len = bss->rsn_ie_len;
+ } else if (bss) {
+ wpa_msg(wpa_s, MSG_DEBUG, "WPA: using IEEE 802.11i/D3.0");
+ proto = WPA_PROTO_WPA;
+ ap_ie = bss->wpa_ie;
+ ap_ie_len = bss->wpa_ie_len;
+ } else {
+ if (ssid->proto & WPA_PROTO_RSN)
+ proto = WPA_PROTO_RSN;
+ else
+ proto = WPA_PROTO_WPA;
+ ap_ie = NULL;
+ ap_ie_len = 0;
+ if (wpa_supplicant_suites_from_ai(wpa_s, ssid, &ie) < 0) {
+ memset(&ie, 0, sizeof(ie));
+ ie.group_cipher = ssid->group_cipher;
+ ie.pairwise_cipher = ssid->pairwise_cipher;
+ ie.key_mgmt = ssid->key_mgmt;
+ wpa_printf(MSG_DEBUG, "WPA: Set cipher suites based "
+ "on configuration");
+ }
+ }
+
+ if (ap_ie && wpa_parse_wpa_ie(wpa_s, ap_ie, ap_ie_len, &ie)) {
+ wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to parse WPA IE for "
+ "the selected BSS.");
+ return -1;
+ }
+ wpa_printf(MSG_DEBUG, "WPA: Selected cipher suites: group %d "
+ "pairwise %d key_mgmt %d",
+ ie.group_cipher, ie.pairwise_cipher, ie.key_mgmt);
+
+ wpa_s->proto = proto;
+
+ free(wpa_s->ap_wpa_ie);
+ wpa_s->ap_wpa_ie = NULL;
+ wpa_s->ap_wpa_ie_len = 0;
+ if (bss && bss->wpa_ie_len) {
+ wpa_s->ap_wpa_ie = malloc(bss->wpa_ie_len);
+ if (wpa_s->ap_wpa_ie == NULL) {
+ wpa_printf(MSG_INFO, "WPA: malloc failed");
+ return -1;
+ }
+ memcpy(wpa_s->ap_wpa_ie, bss->wpa_ie, bss->wpa_ie_len);
+ wpa_s->ap_wpa_ie_len = bss->wpa_ie_len;
+ }
+
+ free(wpa_s->ap_rsn_ie);
+ wpa_s->ap_rsn_ie = NULL;
+ wpa_s->ap_rsn_ie_len = 0;
+ if (bss && bss->rsn_ie_len) {
+ wpa_s->ap_rsn_ie = malloc(bss->rsn_ie_len);
+ if (wpa_s->ap_rsn_ie == NULL) {
+ wpa_printf(MSG_INFO, "WPA: malloc failed");
+ return -1;
+ }
+ memcpy(wpa_s->ap_rsn_ie, bss->rsn_ie, bss->rsn_ie_len);
+ wpa_s->ap_rsn_ie_len = bss->rsn_ie_len;
+ }
+
+ sel = ie.group_cipher & ssid->group_cipher;
+ if (sel & WPA_CIPHER_CCMP) {
+ wpa_s->group_cipher = WPA_CIPHER_CCMP;
+ wpa_msg(wpa_s, MSG_DEBUG, "WPA: using GTK CCMP");
+ } else if (sel & WPA_CIPHER_TKIP) {
+ wpa_s->group_cipher = WPA_CIPHER_TKIP;
+ wpa_msg(wpa_s, MSG_DEBUG, "WPA: using GTK TKIP");
+ } else if (sel & WPA_CIPHER_WEP104) {
+ wpa_s->group_cipher = WPA_CIPHER_WEP104;
+ wpa_msg(wpa_s, MSG_DEBUG, "WPA: using GTK WEP104");
+ } else if (sel & WPA_CIPHER_WEP40) {
+ wpa_s->group_cipher = WPA_CIPHER_WEP40;
+ wpa_msg(wpa_s, MSG_DEBUG, "WPA: using GTK WEP40");
+ } else {
+ wpa_printf(MSG_WARNING, "WPA: Failed to select group cipher.");
+ return -1;
+ }
+
+ sel = ie.pairwise_cipher & ssid->pairwise_cipher;
+ if (sel & WPA_CIPHER_CCMP) {
+ wpa_s->pairwise_cipher = WPA_CIPHER_CCMP;
+ wpa_msg(wpa_s, MSG_DEBUG, "WPA: using PTK CCMP");
+ } else if (sel & WPA_CIPHER_TKIP) {
+ wpa_s->pairwise_cipher = WPA_CIPHER_TKIP;
+ wpa_msg(wpa_s, MSG_DEBUG, "WPA: using PTK TKIP");
+ } else if (sel & WPA_CIPHER_NONE) {
+ wpa_s->pairwise_cipher = WPA_CIPHER_NONE;
+ wpa_msg(wpa_s, MSG_DEBUG, "WPA: using PTK NONE");
+ } else {
+ wpa_printf(MSG_WARNING, "WPA: Failed to select pairwise "
+ "cipher.");
+ return -1;
+ }
+
+ sel = ie.key_mgmt & ssid->key_mgmt;
+ if (sel & WPA_KEY_MGMT_IEEE8021X) {
+ wpa_s->key_mgmt = WPA_KEY_MGMT_IEEE8021X;
+ wpa_msg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT 802.1X");
+ } else if (sel & WPA_KEY_MGMT_PSK) {
+ wpa_s->key_mgmt = WPA_KEY_MGMT_PSK;
+ wpa_msg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT WPA-PSK");
+ } else if (sel & WPA_KEY_MGMT_WPA_NONE) {
+ wpa_s->key_mgmt = WPA_KEY_MGMT_WPA_NONE;
+ wpa_msg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT WPA-NONE");
+ } else {
+ wpa_printf(MSG_WARNING, "WPA: Failed to select authenticated "
+ "key management type.");
+ return -1;
+ }
+
+ *wpa_ie_len = wpa_gen_wpa_ie(wpa_s, wpa_ie);
+ if (*wpa_ie_len < 0) {
+ wpa_printf(MSG_WARNING, "WPA: Failed to generate WPA IE.");
+ return -1;
+ }
+ wpa_hexdump(MSG_DEBUG, "WPA: Own WPA IE", wpa_ie, *wpa_ie_len);
+
+ if (ssid->key_mgmt & WPA_KEY_MGMT_PSK) {
+ wpa_s->pmk_len = PMK_LEN;
+ memcpy(wpa_s->pmk, ssid->psk, PMK_LEN);
+ } else if (wpa_s->cur_pmksa) {
+ wpa_s->pmk_len = wpa_s->cur_pmksa->pmk_len;
+ memcpy(wpa_s->pmk, wpa_s->cur_pmksa->pmk, wpa_s->pmk_len);
+ } else {
+ wpa_s->pmk_len = PMK_LEN;
+ memset(wpa_s->pmk, 0, PMK_LEN);
+#ifdef CONFIG_XSUPPLICANT_IFACE
+ wpa_s->ext_pmk_received = 0;
+#endif /* CONFIG_XSUPPLICANT_IFACE */
+ }
+
+ return 0;
+}
+
+
+static void wpa_supplicant_associate(struct wpa_supplicant *wpa_s,
+ struct wpa_scan_result *bss,
+ struct wpa_ssid *ssid)
+{
+ u8 wpa_ie[80];
+ int wpa_ie_len;
+ int use_crypt;
+ int algs = AUTH_ALG_OPEN_SYSTEM;
+ int cipher_pairwise, cipher_group;
+ struct wpa_driver_associate_params params;
+ int wep_keys_set = 0;
+ struct wpa_driver_capa capa;
+
+ wpa_s->reassociate = 0;
+ if (bss) {
+ wpa_msg(wpa_s, MSG_INFO, "Trying to associate with " MACSTR
+ " (SSID='%s' freq=%d MHz)", MAC2STR(bss->bssid),
+ wpa_ssid_txt(ssid->ssid, ssid->ssid_len), bss->freq);
+ memset(wpa_s->bssid, 0, ETH_ALEN);
+ } else {
+ wpa_msg(wpa_s, MSG_INFO, "Trying to associate with SSID '%s'",
+ wpa_ssid_txt(ssid->ssid, ssid->ssid_len));
+ }
+ wpa_supplicant_cancel_scan(wpa_s);
+
+ /* Starting new association, so clear the possibly used WPA IE from the
+ * previous association. */
+ free(wpa_s->assoc_wpa_ie);
+ wpa_s->assoc_wpa_ie = NULL;
+ wpa_s->assoc_wpa_ie_len = 0;
+
+ if (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA) {
+ if (ssid->leap) {
+ if (ssid->non_leap == 0)
+ algs = AUTH_ALG_LEAP;
+ else
+ algs |= AUTH_ALG_LEAP;
+ }
+ }
+ wpa_printf(MSG_DEBUG, "Automatic auth_alg selection: 0x%x", algs);
+ if (ssid->auth_alg) {
+ algs = 0;
+ if (ssid->auth_alg & WPA_AUTH_ALG_OPEN)
+ algs |= AUTH_ALG_OPEN_SYSTEM;
+ if (ssid->auth_alg & WPA_AUTH_ALG_SHARED)
+ algs |= AUTH_ALG_SHARED_KEY;
+ if (ssid->auth_alg & WPA_AUTH_ALG_LEAP)
+ algs |= AUTH_ALG_LEAP;
+ wpa_printf(MSG_DEBUG, "Overriding auth_alg selection: 0x%x",
+ algs);
+ }
+ wpa_drv_set_auth_alg(wpa_s, algs);
+
+ if (bss && (bss->wpa_ie_len || bss->rsn_ie_len) &&
+ (ssid->key_mgmt & (WPA_KEY_MGMT_IEEE8021X | WPA_KEY_MGMT_PSK))) {
+ wpa_s->cur_pmksa = pmksa_cache_get(wpa_s, bss->bssid, NULL);
+ if (wpa_s->cur_pmksa) {
+ wpa_hexdump(MSG_DEBUG, "RSN: PMKID",
+ wpa_s->cur_pmksa->pmkid, PMKID_LEN);
+ eapol_sm_notify_pmkid_attempt(wpa_s->eapol, 1);
+ }
+ if (wpa_supplicant_set_suites(wpa_s, bss, ssid,
+ wpa_ie, &wpa_ie_len)) {
+ wpa_printf(MSG_WARNING, "WPA: Failed to set WPA key "
+ "management and encryption suites");
+ return;
+ }
+ } else if (ssid->key_mgmt &
+ (WPA_KEY_MGMT_PSK | WPA_KEY_MGMT_IEEE8021X |
+ WPA_KEY_MGMT_WPA_NONE)) {
+ if (wpa_supplicant_set_suites(wpa_s, NULL, ssid,
+ wpa_ie, &wpa_ie_len)) {
+ wpa_printf(MSG_WARNING, "WPA: Failed to set WPA key "
+ "management and encryption suites (no scan "
+ "results)");
+ return;
+ }
+ } else {
+ wpa_supplicant_set_non_wpa_policy(wpa_s, ssid);
+ wpa_ie_len = 0;
+ }
+
+ 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);
+ if (wpa_s->key_mgmt == WPA_KEY_MGMT_NONE ||
+ wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA) {
+ int i;
+ if (wpa_s->key_mgmt == WPA_KEY_MGMT_NONE)
+ use_crypt = 0;
+ for (i = 0; i < NUM_WEP_KEYS; i++) {
+ if (ssid->wep_key_len[i]) {
+ use_crypt = 1;
+ wep_keys_set = 1;
+ wpa_set_wep_key(wpa_s,
+ i == ssid->wep_tx_keyidx,
+ i, ssid->wep_key[i],
+ ssid->wep_key_len[i]);
+ }
+ }
+ }
+
+ if (wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA) {
+ if ((ssid->eapol_flags &
+ (EAPOL_FLAG_REQUIRE_KEY_UNICAST |
+ EAPOL_FLAG_REQUIRE_KEY_BROADCAST)) == 0 &&
+ !wep_keys_set) {
+ use_crypt = 0;
+ } else {
+ /* 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;
+ }
+ }
+
+ if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE) {
+ /* Set the key before (and later after) association */
+ wpa_supplicant_set_wpa_none_key(wpa_s, ssid);
+ }
+
+ wpa_drv_set_drop_unencrypted(wpa_s, use_crypt);
+ wpa_s->wpa_state = WPA_ASSOCIATING;
+ memset(&params, 0, sizeof(params));
+ if (bss) {
+ params.bssid = bss->bssid;
+ params.ssid = bss->ssid;
+ params.ssid_len = bss->ssid_len;
+ params.freq = bss->freq;
+ } else {
+ params.ssid = ssid->ssid;
+ params.ssid_len = ssid->ssid_len;
+ }
+ params.wpa_ie = wpa_ie;
+ 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.auth_alg = algs;
+ params.mode = ssid->mode;
+ if (wpa_drv_associate(wpa_s, &params) < 0) {
+ wpa_msg(wpa_s, MSG_INFO, "Association request to the driver "
+ "failed");
+ /* try to continue anyway; new association will be tried again
+ * after timeout */
+ }
+
+ if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE) {
+ /* Set the key after the association just in case association
+ * cleared the previously configured key. */
+ wpa_supplicant_set_wpa_none_key(wpa_s, ssid);
+ /* No need to timeout authentication since there is no key
+ * management. */
+ wpa_supplicant_cancel_auth_timeout(wpa_s);
+ } else {
+ /* Timeout for IEEE 802.11 authentication and association */
+ wpa_supplicant_req_auth_timeout(wpa_s, 5, 0);
+ }
+
+ if (wep_keys_set && wpa_drv_get_capa(wpa_s, &capa) == 0 &&
+ capa.flags & WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC) {
+ /* Set static WEP keys again */
+ int i;
+ for (i = 0; i < NUM_WEP_KEYS; i++) {
+ if (ssid->wep_key_len[i]) {
+ wpa_set_wep_key(wpa_s,
+ i == ssid->wep_tx_keyidx,
+ i, ssid->wep_key[i],
+ ssid->wep_key_len[i]);
+ }
+ }
+ }
+
+ wpa_s->current_ssid = ssid;
+ wpa_supplicant_initiate_eapol(wpa_s);
+}
+
+
+void wpa_supplicant_disassociate(struct wpa_supplicant *wpa_s,
+ int reason_code)
+{
+ u8 *addr = NULL;
+ wpa_s->wpa_state = WPA_DISCONNECTED;
+ if (memcmp(wpa_s->bssid, "\x00\x00\x00\x00\x00\x00", ETH_ALEN) != 0) {
+ wpa_drv_disassociate(wpa_s, wpa_s->bssid, reason_code);
+ addr = wpa_s->bssid;
+ }
+ wpa_clear_keys(wpa_s, addr);
+ wpa_s->current_ssid = NULL;
+ eapol_sm_notify_config(wpa_s->eapol, NULL, NULL);
+ eapol_sm_notify_portEnabled(wpa_s->eapol, FALSE);
+ eapol_sm_notify_portValid(wpa_s->eapol, FALSE);
+}
+
+
+void wpa_supplicant_deauthenticate(struct wpa_supplicant *wpa_s,
+ int reason_code)
+{
+ u8 *addr = NULL;
+ wpa_s->wpa_state = WPA_DISCONNECTED;
+ if (memcmp(wpa_s->bssid, "\x00\x00\x00\x00\x00\x00", ETH_ALEN) != 0) {
+ wpa_drv_deauthenticate(wpa_s, wpa_s->bssid, reason_code);
+ addr = wpa_s->bssid;
+ }
+ wpa_clear_keys(wpa_s, addr);
+ wpa_s->current_ssid = NULL;
+ eapol_sm_notify_config(wpa_s->eapol, NULL, NULL);
+ eapol_sm_notify_portEnabled(wpa_s->eapol, FALSE);
+ eapol_sm_notify_portValid(wpa_s->eapol, FALSE);
+}
+
+
+static void wpa_supplicant_imsi_identity(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid)
+{
+ int aka = 0;
+ u8 *pos = ssid->eap_methods;
+
+ while (pos && *pos != EAP_TYPE_NONE) {
+ if (*pos == EAP_TYPE_AKA) {
+ aka = 1;
+ break;
+ }
+ pos++;
+ }
+
+ if (ssid->identity == NULL && wpa_s->imsi) {
+ ssid->identity = malloc(1 + wpa_s->imsi_len);
+ if (ssid->identity) {
+ ssid->identity[0] = aka ? '0' : '1';
+ memcpy(ssid->identity + 1, wpa_s->imsi,
+ wpa_s->imsi_len);
+ ssid->identity_len = 1 + wpa_s->imsi_len;
+ wpa_hexdump_ascii(MSG_DEBUG, "permanent identity from "
+ "IMSI", ssid->identity,
+ ssid->identity_len);
+ }
+ }
+}
+
+
+static void wpa_supplicant_scard_init(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid)
+{
+ char buf[100];
+ size_t len;
+
+ if (ssid->pcsc == NULL)
+ return;
+ if (wpa_s->scard != NULL) {
+ wpa_supplicant_imsi_identity(wpa_s, ssid);
+ return;
+ }
+ wpa_printf(MSG_DEBUG, "Selected network is configured to use SIM - "
+ "initialize PCSC");
+ wpa_s->scard = scard_init(SCARD_TRY_BOTH, ssid->pin);
+ if (wpa_s->scard == NULL) {
+ wpa_printf(MSG_WARNING, "Failed to initialize SIM "
+ "(pcsc-lite)");
+ /* TODO: what to do here? */
+ return;
+ }
+ eapol_sm_register_scard_ctx(wpa_s->eapol, wpa_s->scard);
+
+ len = sizeof(buf);
+ if (scard_get_imsi(wpa_s->scard, buf, &len)) {
+ wpa_printf(MSG_WARNING, "Failed to get IMSI from SIM");
+ /* TODO: what to do here? */
+ return;
+ }
+
+ wpa_hexdump_ascii(MSG_DEBUG, "IMSI", (u8 *) buf, len);
+ free(wpa_s->imsi);
+ wpa_s->imsi = malloc(len);
+ if (wpa_s->imsi) {
+ memcpy(wpa_s->imsi, buf, len);
+ wpa_s->imsi_len = len;
+ wpa_supplicant_imsi_identity(wpa_s, ssid);
+ }
+}
+
+
+static struct wpa_scan_result *
+wpa_supplicant_select_bss(struct wpa_supplicant *wpa_s, struct wpa_ssid *group,
+ struct wpa_scan_result *results, int num,
+ struct wpa_ssid **selected_ssid)
+{
+ struct wpa_ssid *ssid;
+ struct wpa_scan_result *bss, *selected = NULL;
+ int i;
+
+ wpa_printf(MSG_DEBUG, "Selecting BSS from priority group %d",
+ group->priority);
+
+ bss = NULL;
+ ssid = NULL;
+ /* First, try to find WPA-enabled AP */
+ for (i = 0; i < num && !selected; i++) {
+ bss = &results[i];
+ wpa_printf(MSG_DEBUG, "%d: " MACSTR " ssid='%s' "
+ "wpa_ie_len=%lu rsn_ie_len=%lu",
+ i, MAC2STR(bss->bssid),
+ wpa_ssid_txt(bss->ssid, bss->ssid_len),
+ (unsigned long) bss->wpa_ie_len,
+ (unsigned long) bss->rsn_ie_len);
+ if (wpa_blacklisted(wpa_s, bss->bssid)) {
+ wpa_printf(MSG_DEBUG, " skip - blacklisted");
+ continue;
+ }
+
+ if (bss->wpa_ie_len == 0 && bss->rsn_ie_len == 0) {
+ wpa_printf(MSG_DEBUG, " skip - no WPA/RSN IE");
+ continue;
+ }
+
+ for (ssid = group; ssid; ssid = ssid->pnext) {
+ struct wpa_ie_data ie;
+ if (bss->ssid_len != ssid->ssid_len ||
+ memcmp(bss->ssid, ssid->ssid,
+ bss->ssid_len) != 0) {
+ wpa_printf(MSG_DEBUG, " skip - "
+ "SSID mismatch");
+ continue;
+ }
+ if (ssid->bssid_set &&
+ memcmp(bss->bssid, ssid->bssid, ETH_ALEN) != 0) {
+ wpa_printf(MSG_DEBUG, " skip - "
+ "BSSID mismatch");
+ continue;
+ }
+ if (!(((ssid->proto & WPA_PROTO_RSN) &&
+ wpa_parse_wpa_ie(wpa_s, bss->rsn_ie,
+ bss->rsn_ie_len, &ie) == 0) ||
+ ((ssid->proto & WPA_PROTO_WPA) &&
+ wpa_parse_wpa_ie(wpa_s, bss->wpa_ie,
+ bss->wpa_ie_len, &ie) == 0))) {
+ wpa_printf(MSG_DEBUG, " skip - "
+ "could not parse WPA/RSN IE");
+ continue;
+ }
+ if (!(ie.proto & ssid->proto)) {
+ wpa_printf(MSG_DEBUG, " skip - "
+ "proto mismatch");
+ continue;
+ }
+ if (!(ie.pairwise_cipher & ssid->pairwise_cipher)) {
+ wpa_printf(MSG_DEBUG, " skip - "
+ "PTK cipher mismatch");
+ continue;
+ }
+ if (!(ie.group_cipher & ssid->group_cipher)) {
+ wpa_printf(MSG_DEBUG, " skip - "
+ "GTK cipher mismatch");
+ continue;
+ }
+ if (!(ie.key_mgmt & ssid->key_mgmt)) {
+ wpa_printf(MSG_DEBUG, " skip - "
+ "key mgmt mismatch");
+ continue;
+ }
+
+ selected = bss;
+ *selected_ssid = ssid;
+ wpa_printf(MSG_DEBUG, " selected");
+ break;
+ }
+ }
+
+ /* If no WPA-enabled AP found, try to find non-WPA AP, if configuration
+ * allows this. */
+ for (i = 0; i < num && !selected; i++) {
+ bss = &results[i];
+ if (wpa_blacklisted(wpa_s, bss->bssid)) {
+ continue;
+ }
+ for (ssid = group; ssid; ssid = ssid->pnext) {
+ if (bss->ssid_len == ssid->ssid_len &&
+ memcmp(bss->ssid, ssid->ssid, bss->ssid_len) == 0
+ &&
+ (!ssid->bssid_set ||
+ memcmp(bss->bssid, ssid->bssid, ETH_ALEN) == 0) &&
+ ((ssid->key_mgmt & WPA_KEY_MGMT_NONE) ||
+ (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA)))
+ {
+ selected = bss;
+ *selected_ssid = ssid;
+ wpa_printf(MSG_DEBUG, " selected non-WPA AP "
+ MACSTR " ssid='%s'",
+ MAC2STR(bss->bssid),
+ wpa_ssid_txt(bss->ssid,
+ bss->ssid_len));
+ break;
+ }
+ }
+ }
+
+ return selected;
+}
+
+
+static int wpa_supplicant_get_scan_results(struct wpa_supplicant *wpa_s)
+{
+#define SCAN_AP_LIMIT 128
+ struct wpa_scan_result *results, *tmp;
+ int num;
+
+ results = malloc(SCAN_AP_LIMIT * sizeof(struct wpa_scan_result));
+ if (results == NULL) {
+ wpa_printf(MSG_WARNING, "Failed to allocate memory for scan "
+ "results");
+ return -1;
+ }
+
+ num = wpa_drv_get_scan_results(wpa_s, results, SCAN_AP_LIMIT);
+ wpa_printf(MSG_DEBUG, "Scan results: %d", num);
+ if (num < 0) {
+ wpa_printf(MSG_DEBUG, "Failed to get scan results");
+ free(results);
+ return -1;
+ }
+ if (num > SCAN_AP_LIMIT) {
+ wpa_printf(MSG_INFO, "Not enough room for all APs (%d < %d)",
+ num, SCAN_AP_LIMIT);
+ num = SCAN_AP_LIMIT;
+ }
+
+ /* Free unneeded memory for unused scan result entries */
+ tmp = realloc(results, num * sizeof(struct wpa_scan_result));
+ if (tmp || num == 0) {
+ results = tmp;
+ }
+
+ free(wpa_s->scan_results);
+ wpa_s->scan_results = results;
+ wpa_s->num_scan_results = num;
+
+ return 0;
+}
+
+
+static void wpa_supplicant_scan_results(struct wpa_supplicant *wpa_s)
+{
+ int num, prio;
+ struct wpa_scan_result *selected = NULL;
+ struct wpa_ssid *ssid;
+ struct wpa_scan_result *results;
+
+ if (wpa_supplicant_get_scan_results(wpa_s) < 0) {
+ wpa_printf(MSG_DEBUG, "Failed to get scan results - try "
+ "scanning again");
+ wpa_supplicant_req_scan(wpa_s, 1, 0);
+ return;
+ }
+ results = wpa_s->scan_results;
+ num = wpa_s->num_scan_results;
+
+ while (selected == NULL) {
+ for (prio = 0; prio < wpa_s->conf->num_prio; prio++) {
+ selected = wpa_supplicant_select_bss(
+ wpa_s, wpa_s->conf->pssid[prio], results, num,
+ &ssid);
+ if (selected)
+ break;
+ }
+
+ if (selected == NULL && wpa_s->blacklist) {
+ wpa_printf(MSG_DEBUG, "No APs found - clear blacklist "
+ "and try again");
+ wpa_blacklist_clear(wpa_s);
+ } else if (selected == NULL) {
+ break;
+ }
+ }
+
+ if (selected) {
+ if (wpa_s->reassociate ||
+ memcmp(selected->bssid, wpa_s->bssid, ETH_ALEN) != 0) {
+ wpa_supplicant_scard_init(wpa_s, ssid);
+ wpa_supplicant_associate(wpa_s, selected, ssid);
+ } else {
+ wpa_printf(MSG_DEBUG, "Already associated with the "
+ "selected AP.");
+ }
+ rsn_preauth_scan_results(wpa_s, results, num);
+ } else {
+ wpa_printf(MSG_DEBUG, "No suitable AP found.");
+ wpa_supplicant_req_scan(wpa_s, 5, 0);
+ }
+}
+
+
+static int wpa_get_beacon_ie(struct wpa_supplicant *wpa_s)
+{
+ int i, ret = 0;
+ struct wpa_scan_result *results, *curr = NULL;
+
+ results = wpa_s->scan_results;
+ if (results == NULL) {
+ return -1;
+ }
+
+ for (i = 0; i < wpa_s->num_scan_results; i++) {
+ if (memcmp(results[i].bssid, wpa_s->bssid, ETH_ALEN) == 0) {
+ curr = &results[i];
+ break;
+ }
+ }
+
+ if (curr) {
+ free(wpa_s->ap_wpa_ie);
+ wpa_s->ap_wpa_ie_len = curr->wpa_ie_len;
+ if (curr->wpa_ie_len) {
+ wpa_s->ap_wpa_ie = malloc(wpa_s->ap_wpa_ie_len);
+ if (wpa_s->ap_wpa_ie) {
+ memcpy(wpa_s->ap_wpa_ie, curr->wpa_ie,
+ curr->wpa_ie_len);
+ } else {
+ ret = -1;
+ }
+ } else {
+ wpa_s->ap_wpa_ie = NULL;
+ }
+
+ free(wpa_s->ap_rsn_ie);
+ wpa_s->ap_rsn_ie_len = curr->rsn_ie_len;
+ if (curr->rsn_ie_len) {
+ wpa_s->ap_rsn_ie = malloc(wpa_s->ap_rsn_ie_len);
+ if (wpa_s->ap_rsn_ie) {
+ memcpy(wpa_s->ap_rsn_ie, curr->rsn_ie,
+ curr->rsn_ie_len);
+ } else {
+ ret = -1;
+ }
+ } else {
+ wpa_s->ap_rsn_ie = NULL;
+ }
+ } else {
+ ret = -1;
+ }
+
+ return ret;
+}
+
+
+int wpa_supplicant_get_beacon_ie(struct wpa_supplicant *wpa_s)
+{
+ if (wpa_get_beacon_ie(wpa_s) == 0) {
+ return 0;
+ }
+
+ /* No WPA/RSN IE found in the cached scan results. Try to get updated
+ * scan results from the driver. */
+ if (wpa_supplicant_get_scan_results(wpa_s) < 0) {
+ return -1;
+ }
+
+ return wpa_get_beacon_ie(wpa_s);
+}
+
+
+#ifdef CONFIG_XSUPPLICANT_IFACE
+static void wpa_supplicant_dot1x_receive(int sock, void *eloop_ctx,
+ void *sock_ctx)
+{
+ struct wpa_supplicant *wpa_s = eloop_ctx;
+ u8 buf[128];
+ int res;
+
+ res = recv(sock, buf, sizeof(buf), 0);
+ wpa_printf(MSG_DEBUG, "WPA: Receive from dot1x (Xsupplicant) socket "
+ "==> %d", res);
+ if (res < 0) {
+ perror("recv");
+ return;
+ }
+
+ if (res != PMK_LEN) {
+ wpa_printf(MSG_WARNING, "WPA: Invalid master key length (%d) "
+ "from dot1x", res);
+ return;
+ }
+
+ wpa_hexdump(MSG_DEBUG, "WPA: Master key (dot1x)", buf, PMK_LEN);
+ if (wpa_s->key_mgmt & WPA_KEY_MGMT_IEEE8021X) {
+ memcpy(wpa_s->pmk, buf, PMK_LEN);
+ wpa_s->ext_pmk_received = 1;
+ } else {
+ wpa_printf(MSG_INFO, "WPA: Not in IEEE 802.1X mode - dropping "
+ "dot1x PMK update (%d)", wpa_s->key_mgmt);
+ }
+}
+
+
+static int wpa_supplicant_802_1x_init(struct wpa_supplicant *wpa_s)
+{
+ int s;
+ struct sockaddr_un addr;
+
+ s = socket(AF_LOCAL, SOCK_DGRAM, 0);
+ if (s < 0) {
+ perror("socket");
+ return -1;
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sun_family = AF_LOCAL;
+ addr.sun_path[0] = '\0';
+ snprintf(addr.sun_path + 1, sizeof(addr.sun_path) - 1,
+ "wpa_supplicant");
+ if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ perror("bind");
+ close(s);
+ return -1;
+ }
+
+ wpa_s->dot1x_s = s;
+ eloop_register_read_sock(s, wpa_supplicant_dot1x_receive, wpa_s,
+ NULL);
+ return 0;
+}
+#endif /* CONFIG_XSUPPLICANT_IFACE */
+
+
+static int wpa_supplicant_set_driver(struct wpa_supplicant *wpa_s,
+ const char *name)
+{
+ int i;
+
+ if (wpa_s == NULL)
+ return -1;
+
+ if (wpa_supplicant_drivers[0] == NULL) {
+ wpa_printf(MSG_ERROR, "No driver interfaces build into "
+ "wpa_supplicant.");
+ return -1;
+ }
+
+ if (name == NULL) {
+ /* default to first driver in the list */
+ wpa_s->driver = wpa_supplicant_drivers[0];
+ return 0;
+ }
+
+ for (i = 0; wpa_supplicant_drivers[i]; i++) {
+ if (strcmp(name, wpa_supplicant_drivers[i]->name) == 0) {
+ wpa_s->driver = wpa_supplicant_drivers[i];
+ return 0;
+ }
+ }
+
+ printf("Unsupported driver '%s'.\n", name);
+ return -1;
+}
+
+
+static void wpa_supplicant_fd_workaround(void)
+{
+ int s, 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;
+ }
+ }
+}
+
+
+static int wpa_supplicant_driver_init(struct wpa_supplicant *wpa_s,
+ int wait_for_interface)
+{
+ static int interface_count = 0;
+
+ for (;;) {
+ wpa_s->l2 = l2_packet_init(wpa_s->ifname,
+ wpa_drv_get_mac_addr(wpa_s),
+ ETH_P_EAPOL,
+ wpa_supplicant_rx_eapol, wpa_s);
+ if (wpa_s->l2)
+ break;
+ else if (!wait_for_interface)
+ return -1;
+ printf("Waiting for interface..\n");
+ sleep(5);
+ }
+
+ if (l2_packet_get_own_addr(wpa_s->l2, wpa_s->own_addr)) {
+ fprintf(stderr, "Failed to get own L2 address\n");
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "Own MAC address: " MACSTR,
+ MAC2STR(wpa_s->own_addr));
+
+ if (wpa_drv_set_wpa(wpa_s, 1) < 0) {
+ fprintf(stderr, "Failed to enable WPA in the driver.\n");
+ return -1;
+ }
+
+ wpa_clear_keys(wpa_s, NULL);
+
+ /* Make sure that TKIP countermeasures are not left enabled (could
+ * happen if wpa_supplicant is killed during countermeasures. */
+ wpa_drv_set_countermeasures(wpa_s, 0);
+
+ wpa_drv_set_drop_unencrypted(wpa_s, 1);
+
+ wpa_s->prev_scan_ssid = BROADCAST_SSID_SCAN;
+ wpa_supplicant_req_scan(wpa_s, interface_count, 100000);
+ interface_count++;
+
+ return 0;
+}
+
+
+static void usage(void)
+{
+ int i;
+ printf("%s\n\n%s\n"
+ "usage:\n"
+ " wpa_supplicant [-BddehLqqvw] -i<ifname> -c<config file> "
+ "[-D<driver>] \\\n"
+ " [-N -i<ifname> -c<conf> [-D<driver>] ...]\n"
+ "\n"
+ "drivers:\n",
+ wpa_supplicant_version, wpa_supplicant_license);
+
+ for (i = 0; wpa_supplicant_drivers[i]; i++) {
+ printf(" %s = %s\n",
+ wpa_supplicant_drivers[i]->name,
+ wpa_supplicant_drivers[i]->desc);
+ }
+
+ printf("options:\n"
+ " -B = run daemon in the background\n"
+ " -d = increase debugging verbosity (-dd even more)\n"
+ " -K = include keys (passwords, etc.) in debug output\n"
+ " -t = include timestamp in debug messages\n"
+#ifdef CONFIG_XSUPPLICANT_IFACE
+#ifdef IEEE8021X_EAPOL
+ " -e = use external IEEE 802.1X Supplicant (e.g., "
+ "xsupplicant)\n"
+ " (this disables the internal Supplicant)\n"
+#endif /* IEEE8021X_EAPOL */
+#endif /* CONFIG_XSUPPLICANT_IFACE */
+ " -h = show this help text\n"
+ " -L = show license (GPL and BSD)\n"
+ " -q = decrease debugging verbosity (-qq even less)\n"
+ " -v = show version\n"
+ " -w = wait for interface to be added, if needed\n"
+ " -N = start describing new interface\n");
+}
+
+
+static void license(void)
+{
+ printf("%s\n\n%s\n",
+ wpa_supplicant_version, wpa_supplicant_full_license);
+}
+
+
+static struct wpa_supplicant * wpa_supplicant_alloc(void)
+{
+ struct wpa_supplicant *wpa_s;
+
+ wpa_s = malloc(sizeof(*wpa_s));
+ if (wpa_s == NULL)
+ return NULL;
+ memset(wpa_s, 0, sizeof(*wpa_s));
+#ifdef CONFIG_XSUPPLICANT_IFACE
+ wpa_s->dot1x_s = -1;
+#endif /* CONFIG_XSUPPLICANT_IFACE */
+
+ return wpa_s;
+}
+
+
+static int wpa_supplicant_init(struct wpa_supplicant *wpa_s,
+ const char *confname, const char *driver,
+ const char *ifname)
+{
+ wpa_printf(MSG_DEBUG, "Initializing interface '%s' conf '%s' driver "
+ "'%s'", ifname, confname, driver ? driver : "default");
+
+ if (wpa_supplicant_set_driver(wpa_s, driver) < 0) {
+ return -1;
+ }
+
+ if (confname) {
+ wpa_s->confname = rel2abs_path(confname);
+ if (wpa_s->confname == NULL) {
+ wpa_printf(MSG_ERROR, "Failed to get absolute path "
+ "for configuration file '%s'.", confname);
+ return -1;
+ }
+ wpa_printf(MSG_DEBUG, "Configuration file '%s' -> '%s'",
+ confname, wpa_s->confname);
+ wpa_s->conf = wpa_config_read(wpa_s->confname);
+ if (wpa_s->conf == NULL) {
+ printf("Failed to read configuration file '%s'.\n",
+ wpa_s->confname);
+ return -1;
+ }
+ }
+
+ if (wpa_s->conf == NULL || wpa_s->conf->ssid == NULL) {
+ usage();
+ printf("\nNo networks (SSID) configured.\n");
+ return -1;
+ }
+
+ if (ifname == NULL) {
+ usage();
+ printf("\nInterface name is required.\n");
+ return -1;
+ }
+ if (strlen(ifname) >= sizeof(wpa_s->ifname)) {
+ printf("Too long interface name '%s'.\n", ifname);
+ return -1;
+ }
+ strncpy(wpa_s->ifname, ifname, sizeof(wpa_s->ifname));
+
+ return 0;
+}
+
+
+static int wpa_supplicant_init2(struct wpa_supplicant *wpa_s,
+ int disable_eapol, int wait_for_interface)
+{
+ const char *ifname;
+
+ wpa_printf(MSG_DEBUG, "Initializing interface (2) '%s'",
+ wpa_s->ifname);
+
+ if (!disable_eapol) {
+ struct eapol_ctx *ctx;
+ ctx = malloc(sizeof(*ctx));
+ if (ctx == NULL) {
+ printf("Failed to allocate EAPOL context.\n");
+ return -1;
+ }
+ memset(ctx, 0, sizeof(*ctx));
+ ctx->ctx = wpa_s;
+ ctx->msg_ctx = wpa_s;
+ ctx->preauth = 0;
+ ctx->eapol_done_cb = wpa_supplicant_notify_eapol_done;
+ ctx->eapol_send = wpa_eapol_send;
+ ctx->set_wep_key = wpa_eapol_set_wep_key;
+ wpa_s->eapol = eapol_sm_init(ctx);
+ if (wpa_s->eapol == NULL) {
+ free(ctx);
+ printf("Failed to initialize EAPOL state machines.\n");
+ return -1;
+ }
+ }
+
+ /* RSNA Supplicant Key Management - INITIALIZE */
+ eapol_sm_notify_portEnabled(wpa_s->eapol, FALSE);
+ eapol_sm_notify_portValid(wpa_s->eapol, FALSE);
+
+ /* Initialize driver interface and register driver event handler before
+ * L2 receive handler so that association events are processed before
+ * EAPOL-Key packets if both become available for the same select()
+ * call. */
+ wpa_s->drv_priv = wpa_drv_init(wpa_s, wpa_s->ifname);
+ if (wpa_s->drv_priv == NULL) {
+ fprintf(stderr, "Failed to initialize driver interface\n");
+ return -1;
+ }
+
+ ifname = wpa_drv_get_ifname(wpa_s);
+ if (ifname && strcmp(ifname, wpa_s->ifname) != 0) {
+ wpa_printf(MSG_DEBUG, "Driver interface replaced interface "
+ "name with '%s'", ifname);
+ strncpy(wpa_s->ifname, ifname, sizeof(wpa_s->ifname));
+ }
+
+ wpa_s->renew_snonce = 1;
+ if (wpa_supplicant_driver_init(wpa_s, wait_for_interface) < 0) {
+ return -1;
+ }
+
+ if (wpa_supplicant_ctrl_iface_init(wpa_s)) {
+ printf("Failed to initialize control interface '%s'.\n"
+ "You may have another wpa_supplicant process already "
+ "running or the file was\n"
+ "left by an unclean termination of wpa_supplicant in "
+ "which case you will need\n"
+ "to manually remove this file before starting "
+ "wpa_supplicant again.\n",
+ wpa_s->conf->ctrl_interface);
+ return -1;
+ }
+
+#ifdef CONFIG_XSUPPLICANT_IFACE
+ if (disable_eapol)
+ wpa_supplicant_802_1x_init(wpa_s);
+#endif /* CONFIG_XSUPPLICANT_IFACE */
+
+ return 0;
+}
+
+
+static void wpa_supplicant_deinit(struct wpa_supplicant *wpa_s)
+{
+ if (wpa_s->drv_priv) {
+ if (wpa_drv_set_wpa(wpa_s, 0) < 0) {
+ fprintf(stderr, "Failed to disable WPA in the "
+ "driver.\n");
+ }
+
+ wpa_drv_set_drop_unencrypted(wpa_s, 0);
+ wpa_drv_set_countermeasures(wpa_s, 0);
+
+ wpa_drv_deinit(wpa_s);
+ }
+ wpa_supplicant_cleanup(wpa_s);
+}
+
+
+int main(int argc, char *argv[])
+{
+ struct wpa_supplicant *head, *wpa_s;
+ int c;
+ const char *confname, *driver, *ifname;
+ int daemonize = 0, wait_for_interface = 0, disable_eapol = 0, exitcode;
+
+#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 */
+
+ head = wpa_s = wpa_supplicant_alloc();
+ if (wpa_s == NULL)
+ return -1;
+ wpa_s->head = head;
+
+ wpa_supplicant_fd_workaround();
+ eloop_init(head);
+
+ ifname = confname = driver = NULL;
+
+ for (;;) {
+ c = getopt(argc, argv, "Bc:D:dehi:KLNqtvw");
+ if (c < 0)
+ break;
+ switch (c) {
+ case 'B':
+ daemonize++;
+ break;
+ case 'c':
+ confname = optarg;
+ break;
+ case 'D':
+ driver = optarg;
+ break;
+ case 'd':
+ wpa_debug_level--;
+ break;
+#ifdef CONFIG_XSUPPLICANT_IFACE
+#ifdef IEEE8021X_EAPOL
+ case 'e':
+ disable_eapol++;
+ break;
+#endif /* IEEE8021X_EAPOL */
+#endif /* CONFIG_XSUPPLICANT_IFACE */
+ case 'h':
+ usage();
+ return -1;
+ case 'i':
+ ifname = optarg;
+ break;
+ case 'K':
+ wpa_debug_show_keys++;
+ break;
+ case 'L':
+ license();
+ return -1;
+ case 'q':
+ wpa_debug_level++;
+ break;
+ case 't':
+ wpa_debug_timestamp++;
+ break;
+ case 'v':
+ printf("%s\n", wpa_supplicant_version);
+ return -1;
+ case 'w':
+ wait_for_interface++;
+ break;
+ case 'N':
+ if (wpa_supplicant_init(wpa_s, confname, driver,
+ ifname))
+ return -1;
+ wpa_s->next = wpa_supplicant_alloc();
+ wpa_s = wpa_s->next;
+ if (wpa_s == NULL)
+ return -1;
+ wpa_s->head = head;
+ ifname = confname = driver = NULL;
+ break;
+ default:
+ usage();
+ return -1;
+ }
+ }
+
+ if (wpa_supplicant_init(wpa_s, confname, driver, ifname))
+ return -1;
+
+ exitcode = 0;
+
+ if (wait_for_interface && daemonize) {
+ wpa_printf(MSG_DEBUG, "Daemonize..");
+ if (daemon(0, 0)) {
+ perror("daemon");
+ exitcode = -1;
+ goto cleanup;
+ }
+ }
+
+ for (wpa_s = head; wpa_s; wpa_s = wpa_s->next) {
+ if (wpa_supplicant_init2(wpa_s, disable_eapol,
+ wait_for_interface)) {
+ exitcode = -1;
+ goto cleanup;
+ }
+ }
+
+ if (!wait_for_interface && daemonize) {
+ wpa_printf(MSG_DEBUG, "Daemonize..");
+ if (daemon(0, 0)) {
+ perror("daemon");
+ exitcode = -1;
+ goto cleanup;
+ }
+ }
+
+ eloop_register_signal(SIGINT, wpa_supplicant_terminate, NULL);
+ eloop_register_signal(SIGTERM, wpa_supplicant_terminate, NULL);
+#ifndef CONFIG_NATIVE_WINDOWS
+ eloop_register_signal(SIGHUP, wpa_supplicant_reconfig, NULL);
+#endif /* CONFIG_NATIVE_WINDOWS */
+
+ eloop_run();
+
+ for (wpa_s = head; wpa_s; wpa_s = wpa_s->next) {
+ wpa_supplicant_deauthenticate(wpa_s, REASON_DEAUTH_LEAVING);
+ }
+
+cleanup:
+ wpa_s = head;
+ while (wpa_s) {
+ struct wpa_supplicant *prev;
+ wpa_supplicant_deinit(wpa_s);
+ prev = wpa_s;
+ wpa_s = wpa_s->next;
+ free(prev);
+ }
+
+ eloop_destroy();
+
+#ifdef CONFIG_NATIVE_WINDOWS
+ WSACleanup();
+#endif /* CONFIG_NATIVE_WINDOWS */
+
+ return exitcode;
+}
diff --git a/contrib/wpa_supplicant/wpa_supplicant.conf b/contrib/wpa_supplicant/wpa_supplicant.conf
new file mode 100644
index 000000000000..e8be91a19c7b
--- /dev/null
+++ b/contrib/wpa_supplicant/wpa_supplicant.conf
@@ -0,0 +1,505 @@
+##### Example wpa_supplicant configuration file ###############################
+# Empty lines and lines starting with # are ignored
+
+# NOTE! This file may contain password information and should probably be made
+# readable only by root user on multiuser systems.
+
+# global configuration (shared by all network blocks)
+#
+# Interface for separate control program. If this is specified, wpa_supplicant
+# will create this directory and a UNIX domain socket for listening to requests
+# from external programs (CLI/GUI, etc.) for status information and
+# configuration. The socket file will be named based on the interface name, so
+# multiple wpa_supplicant processes can be run at the same time if more than
+# one interface is used.
+# /var/run/wpa_supplicant is the recommended directory for sockets and by
+# default, wpa_cli will use it when trying to connect with wpa_supplicant.
+ctrl_interface=/var/run/wpa_supplicant
+
+# Access control for the control interface can be configured by setting the
+# directory to allow only members of a group to use sockets. This way, it is
+# possible to run wpa_supplicant as root (since it needs to change network
+# configuration and open raw sockets) and still allow GUI/CLI components to be
+# run as non-root users. However, since the control interface can be used to
+# change the network configuration, this access needs to be protected in many
+# cases. By default, wpa_supplicant is configured to use gid 0 (root). If you
+# want to allow non-root users to use the control interface, add a new group
+# and change this value to match with that group. Add users that should have
+# control interface access to this group. If this variable is commented out or
+# not included in the configuration file, group will not be changed from the
+# value it got by default when the directory or socket was created.
+#
+# This variable can be a group name or gid.
+#ctrl_interface_group=wheel
+ctrl_interface_group=0
+
+# IEEE 802.1X/EAPOL version
+# wpa_supplicant was implemented based on IEEE 802-1X-REV-d8 which defines
+# EAPOL version 2. However, there are many APs that do not handle the new
+# version number correctly (they seem to drop the frames completely). In order
+# to make wpa_supplicant interoperate with these APs, the version number is set
+# to 1 by default. This configuration value can be used to set it to the new
+# version (2).
+eapol_version=1
+
+# AP scanning/selection
+# By default, wpa_supplicant requests driver to perform AP scanning and then
+# uses the scan results to select a suitable AP. Another alternative is to
+# allow the driver to take care of AP scanning and selection and use
+# wpa_supplicant just to process EAPOL frames based on IEEE 802.11 association
+# information from the driver.
+# 1: wpa_supplicant initiates scanning and AP selection
+# 0: driver takes care of scanning, AP selection, and IEEE 802.11 association
+# parameters (e.g., WPA IE generation); this mode can also be used with
+# non-WPA drivers when using IEEE 802.1X mode; do not try to associate with
+# APs (i.e., external program needs to control association)
+# 2: like 0, but associate with APs using security policy and SSID (but not
+# BSSID); this can be used, e.g., with ndiswrapper and NDIS driver to
+# enable operation with hidden SSIDs and optimized roaming; in this mode,
+# only the first network block in the configuration file is used and this
+# configuration should have explicit security policy (i.e., only one option
+# in the lists) for key_mgmt, pairwise, group, proto variables
+ap_scan=1
+
+# EAP fast re-authentication
+# By default, fast re-authentication is enabled for all EAP methods that
+# support it. This variable can be used to disable fast re-authentication.
+# Normally, there is no need to disable this.
+fast_reauth=1
+
+# network block
+#
+# Each network (usually AP's sharing the same SSID) is configured as a separate
+# block in this configuration file. The network blocks are in preference order
+# (the first match is used).
+#
+# network block fields:
+#
+# ssid: SSID (mandatory); either as an ASCII string with double quotation or
+# as hex string; network name
+#
+# scan_ssid:
+# 0 = do not scan this SSID with specific Probe Request frames (default)
+# 1 = scan with SSID-specific Probe Request frames (this can be used to
+# find APs that do not accept broadcast SSID or use multiple SSIDs;
+# this will add latency to scanning, so enable this only when needed)
+#
+# bssid: BSSID (optional); if set, this network block is used only when
+# associating with the AP using the configured BSSID
+#
+# priority: priority group (integer)
+# By default, all networks will get same priority group (0). If some of the
+# networks are more desirable, this field can be used to change the order in
+# which wpa_supplicant goes through the networks when selecting a BSS. The
+# priority groups will be iterated in decreasing priority (i.e., the larger the
+# priority value, the sooner the network is matched against the scan results).
+# Within each priority group, networks will be selected based on security
+# policy, signal strength, etc.
+# Please note that AP scanning with scan_ssid=1 is not using this priority to
+# select the order for scanning. Instead, it uses the order the networks are in
+# the configuration file.
+#
+# mode: IEEE 802.11 operation mode
+# 0 = infrastructure (Managed) mode, i.e., associate with an AP (default)
+# 1 = IBSS (ad-hoc, peer-to-peer)
+# 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.
+#
+# 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)
+# If not set, this defaults to: WPA RSN
+#
+# key_mgmt: list of accepted authenticated key management protocols
+# WPA-PSK = WPA pre-shared key (this requires 'psk' field)
+# WPA-EAP = WPA using EAP authentication (this can use an external
+# program, e.g., Xsupplicant, for IEEE 802.1X EAP Authentication
+# IEEE8021X = IEEE 802.1X using EAP authentication and (optionally) dynamically
+# generated WEP keys
+# NONE = WPA is not used; plaintext or static WEP could be used
+# If not set, this defaults to: WPA-PSK WPA-EAP
+#
+# 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)
+# LEAP = LEAP/Network EAP (only used with LEAP)
+# If not set, automatic selection is used (Open System with LEAP enabled if
+# LEAP is allowed as one of the EAP methods).
+#
+# pairwise: list of accepted pairwise (unicast) ciphers for WPA
+# CCMP = AES in Counter mode with CBC-MAC [RFC 3610, IEEE 802.11i/D7.0]
+# TKIP = Temporal Key Integrity Protocol [IEEE 802.11i/D7.0]
+# NONE = Use only Group Keys (deprecated, should not be included if APs support
+# pairwise keys)
+# If not set, this defaults to: CCMP TKIP
+#
+# group: list of accepted group (broadcast/multicast) ciphers for WPA
+# CCMP = AES in Counter mode with CBC-MAC [RFC 3610, IEEE 802.11i/D7.0]
+# TKIP = Temporal Key Integrity Protocol [IEEE 802.11i/D7.0]
+# WEP104 = WEP (Wired Equivalent Privacy) with 104-bit key
+# WEP40 = WEP (Wired Equivalent Privacy) with 40-bit key [IEEE 802.11]
+# If not set, this defaults to: CCMP TKIP WEP104 WEP40
+#
+# psk: WPA preshared key; 256-bit pre-shared key
+# 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).
+# 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
+# startup and reconfiguration time can be optimized by generating the PSK only
+# only when the passphrase or SSID has actually changed.
+#
+# eapol_flags: IEEE 802.1X/EAPOL options (bit field)
+# Dynamic WEP key require for non-WPA mode
+# bit0 (1): require dynamically generated unicast WEP key
+# bit1 (2): require dynamically generated broadcast WEP key
+# (3 = require both keys; default)
+#
+# Following fields are only used with internal EAP implementation.
+# eap: space-separated list of accepted EAP methods
+# MD5 = EAP-MD5 (unsecure and does not generate keying material ->
+# cannot be used with WPA; to be used as a Phase 2 method
+# with EAP-PEAP or EAP-TTLS)
+# MSCHAPV2 = EAP-MSCHAPv2 (cannot be used separately with WPA; to be used
+# as a Phase 2 method with EAP-PEAP or EAP-TTLS)
+# OTP = EAP-OTP (cannot be used separately with WPA; to be used
+# as a Phase 2 method with EAP-PEAP or EAP-TTLS)
+# GTC = EAP-GTC (cannot be used separately with WPA; to be used
+# as a Phase 2 method with EAP-PEAP or EAP-TTLS)
+# TLS = EAP-TLS (client and server certificate)
+# PEAP = EAP-PEAP (with tunnelled EAP authentication)
+# TTLS = EAP-TTLS (with tunnelled EAP or PAP/CHAP/MSCHAP/MSCHAPV2
+# authentication)
+# If not set, all compiled in methods are allowed.
+#
+# identity: Identity string for EAP
+# 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)
+# password: Password string for EAP
+# ca_cert: File path to CA certificate file. This file can have one or more
+# trusted CA certificates. If ca_cert is not included, server certificate
+# will not be verified. This is insecure and the CA file should always be
+# configured.
+# client_cert: File path to client certificate file (PEM/DER)
+# 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.
+# private_key_passwd: Password for private key file
+# 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 not use this configuration. However, it is possible
+# setup RSA to use ephemeral DH key exchange. In addition, ciphers with
+# DSA keys always use ephemeral DH keys. This can be used to achieve
+# forward secrecy. If the file is in DSA parameters format, it will be
+# automatically converted into DH params.
+# subject_match: Substring to be matched against the subject of the
+# authentication server certificate. If this string is set, the server
+# sertificate is only accepted if it contains this string in the subject.
+# The subject string is in following format:
+# /C=US/ST=CA/L=San Francisco/CN=Test AS/emailAddress=as@example.com
+# phase1: Phase1 (outer authentication, i.e., TLS tunnel) parameters
+# (string with field-value pairs, e.g., "peapver=0" or
+# "peapver=1 peaplabel=1")
+# 'peapver' can be used to force which PEAP version (0 or 1) is used.
+# 'peaplabel=1' can be used to force new label, "client PEAP encryption",
+# to be used during key derivation when PEAPv1 or newer. Most existing
+# PEAPv1 implementation seem to be using the old label, "client EAP
+# encryption", and wpa_supplicant is now using that as the default value.
+# Some servers, e.g., Radiator, may require peaplabel=1 configuration to
+# interoperate with PEAPv1; see eap_testing.txt for more details.
+# 'peap_outer_success=0' can be used to terminate PEAP authentication on
+# tunneled EAP-Success. This is required with some RADIUS servers that
+# implement draft-josefsson-pppext-eap-tls-eap-05.txt (e.g.,
+# Lucent NavisRadius v4.4.0 with PEAP in "IETF Draft 5" mode)
+# include_tls_length=1 can be used to force wpa_supplicant to include
+# TLS Message Length field in all TLS messages even if they are not
+# fragmented.
+# sim_min_num_chal=3 can be used to configure EAP-SIM to require three
+# challenges (by default, it accepts 2 or 3)
+# 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)
+# 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
+# trusted CA certificates. If ca_cert2 is not included, server
+# certificate will not be verified. This is insecure and the CA file
+# should always be configured.
+# client_cert2: File path to client certificate file
+# private_key2: File path to client private key file
+# private_key2_passwd: Password for private key file
+# dh_file2: File path to DH/DSA parameters file (in PEM format)
+# subject_match2: Substring to be matched against the subject of the
+# authentication server certificate.
+#
+# EAP-PSK variables:
+# eappsk: 16-byte (128-bit, 32 hex digits) pre-shared key in hex format
+# nai: user NAI
+# server_nai: authentication server NAI
+#
+# 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
+# provisioned or refreshed.
+# phase1: fast_provisioning=1 option enables in-line provisioning of EAP-FAST
+# credentials (PAC)
+#
+# wpa_supplicant supports number of "EAP workarounds" to work around
+# interoperability issues with incorrectly behaving authentication servers.
+# These are enabled by default because some of the issues are present in large
+# number of authentication servers. Strict EAP conformance mode can be
+# configured by disabling workarounds with eap_workaround=0.
+
+# Example blocks:
+
+# Simple case: WPA-PSK, PSK as an ASCII passphrase, allow all valid ciphers
+network={
+ ssid="simple"
+ psk="very secret passphrase"
+ priority=5
+}
+
+# Same as previous, but request SSID-specific scanning (for APs that reject
+# broadcast SSID)
+network={
+ ssid="second ssid"
+ scan_ssid=1
+ psk="very secret passphrase"
+ priority=2
+}
+
+# Only WPA-PSK is used. Any valid cipher combination is accepted.
+network={
+ ssid="example"
+ proto=WPA
+ key_mgmt=WPA-PSK
+ pairwise=CCMP TKIP
+ group=CCMP TKIP WEP104 WEP40
+ psk=06b4be19da289f475aa46a33cb793029d4ab3db7a23ee92382eb0106c72ac7bb
+ priority=2
+}
+
+# Only WPA-EAP is used. Both CCMP and TKIP is accepted. An AP that used WEP104
+# or WEP40 as the group cipher will not be accepted.
+network={
+ ssid="example"
+ proto=RSN
+ key_mgmt=WPA-EAP
+ pairwise=CCMP TKIP
+ group=CCMP TKIP
+ eap=TLS
+ identity="user@example.com"
+ ca_cert="/etc/cert/ca.pem"
+ client_cert="/etc/cert/user.pem"
+ private_key="/etc/cert/user.prv"
+ private_key_passwd="password"
+ priority=1
+}
+
+# EAP-PEAP/MSCHAPv2 configuration for RADIUS servers that use the new peaplabel
+# (e.g., Radiator)
+network={
+ ssid="example"
+ key_mgmt=WPA-EAP
+ eap=PEAP
+ identity="user@example.com"
+ password="foobar"
+ ca_cert="/etc/cert/ca.pem"
+ phase1="peaplabel=1"
+ phase2="auth=MSCHAPV2"
+ priority=10
+}
+
+# EAP-TTLS/EAP-MD5-Challenge configuration with anonymous identity for the
+# unencrypted use. Real identity is sent only within an encrypted TLS tunnel.
+network={
+ ssid="example"
+ key_mgmt=WPA-EAP
+ eap=TTLS
+ identity="user@example.com"
+ anonymous_identity="anonymous@example.com"
+ password="foobar"
+ ca_cert="/etc/cert/ca.pem"
+ priority=2
+}
+
+# EAP-TTLS/MSCHAPv2 configuration with anonymous identity for the unencrypted
+# use. Real identity is sent only within an encrypted TLS tunnel.
+network={
+ ssid="example"
+ key_mgmt=WPA-EAP
+ eap=TTLS
+ identity="user@example.com"
+ anonymous_identity="anonymous@example.com"
+ password="foobar"
+ ca_cert="/etc/cert/ca.pem"
+ phase2="auth=MSCHAPV2"
+}
+
+# WPA-EAP, EAP-TTLS with different CA certificate used for outer and inner
+# authentication.
+network={
+ ssid="example"
+ key_mgmt=WPA-EAP
+ eap=TTLS
+ # Phase1 / outer authentication
+ anonymous_identity="anonymous@example.com"
+ ca_cert="/etc/cert/ca.pem"
+ # Phase 2 / inner authentication
+ phase2="autheap=TLS"
+ ca_cert2="/etc/cert/ca2.pem"
+ client_cert2="/etc/cer/user.pem"
+ private_key2="/etc/cer/user.prv"
+ private_key2_passwd="password"
+ priority=2
+}
+
+# Both WPA-PSK and WPA-EAP is accepted. Only CCMP is accepted as pairwise and
+# group cipher.
+network={
+ ssid="example"
+ bssid=00:11:22:33:44:55
+ proto=WPA RSN
+ key_mgmt=WPA-PSK WPA-EAP
+ pairwise=CCMP
+ group=CCMP
+ psk=06b4be19da289f475aa46a33cb793029d4ab3db7a23ee92382eb0106c72ac7bb
+}
+
+# Special characters in SSID, so use hex string. Default to WPA-PSK, WPA-EAP
+# and all valid ciphers.
+network={
+ ssid=00010203
+ psk=000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f
+}
+
+
+# EAP-SIM with a GSM SIM or USIM
+network={
+ ssid="eap-sim-test"
+ key_mgmt=WPA-EAP
+ eap=SIM
+ pin="1234"
+ pcsc=""
+}
+
+
+# EAP-PSK
+network={
+ ssid="eap-psk-test"
+ key_mgmt=WPA-EAP
+ eap=PSK
+ identity="eap_psk_user"
+ eappsk=06b4be19da289f475aa46a33cb793029
+ nai="eap_psk_user@example.com"
+ server_nai="as@example.com"
+}
+
+
+# IEEE 802.1X/EAPOL with dynamically generated WEP keys (i.e., no WPA) using
+# EAP-TLS for authentication and key generation; require both unicast and
+# broadcast WEP keys.
+network={
+ ssid="1x-test"
+ key_mgmt=IEEE8021X
+ eap=TLS
+ identity="user@example.com"
+ ca_cert="/etc/cert/ca.pem"
+ client_cert="/etc/cert/user.pem"
+ private_key="/etc/cert/user.prv"
+ private_key_passwd="password"
+ eapol_flags=3
+}
+
+
+# LEAP with dynamic WEP keys
+network={
+ ssid="leap-example"
+ key_mgmt=IEEE8021X
+ eap=LEAP
+ identity="user"
+ password="foobar"
+}
+
+# EAP-FAST with WPA (WPA or WPA2)
+network={
+ ssid="eap-fast-test"
+ key_mgmt=WPA-EAP
+ eap=FAST
+ anonymous_identity="FAST-000102030405"
+ identity="username"
+ password="password"
+ phase1="fast_provisioning=1"
+ pac_file="/etc/wpa_supplicant.eap-fast-pac"
+}
+
+# Plaintext connection (no WPA, no IEEE 802.1X)
+network={
+ ssid="plaintext-test"
+ key_mgmt=NONE
+}
+
+
+# Shared WEP key connection (no WPA, no IEEE 802.1X)
+network={
+ ssid="static-wep-test"
+ key_mgmt=NONE
+ wep_key0="abcde"
+ wep_key1=0102030405
+ wep_key2="1234567890123"
+ wep_tx_keyidx=0
+ priority=5
+}
+
+
+# Shared WEP key connection (no WPA, no IEEE 802.1X) using Shared Key
+# IEEE 802.11 authentication
+network={
+ ssid="static-wep-test2"
+ key_mgmt=NONE
+ wep_key0="abcde"
+ wep_key1=0102030405
+ wep_key2="1234567890123"
+ wep_tx_keyidx=0
+ priority=5
+ auth_alg=SHARED
+}
+
+
+# IBSS/ad-hoc network with WPA-None/TKIP.
+network={
+ ssid="test adhoc"
+ mode=1
+ proto=WPA
+ key_mgmt=WPA-NONE
+ pairwise=NONE
+ group=TKIP
+ psk="secret passphrase"
+}
+
+
+# Catch all example that allows more or less all configuration modes
+network={
+ ssid="example"
+ scan_ssid=1
+ key_mgmt=WPA-EAP WPA-PSK IEEE8021X NONE
+ pairwise=CCMP TKIP
+ group=CCMP TKIP WEP104 WEP40
+ psk="very secret passphrase"
+ eap=TTLS PEAP TLS
+ identity="user@example.com"
+ password="foobar"
+ ca_cert="/etc/cert/ca.pem"
+ client_cert="/etc/cert/user.pem"
+ private_key="/etc/cert/user.prv"
+ private_key_passwd="password"
+ phase1="peaplabel=0"
+}
diff --git a/contrib/wpa_supplicant/wpa_supplicant.h b/contrib/wpa_supplicant/wpa_supplicant.h
new file mode 100644
index 000000000000..e5ad182fc1d6
--- /dev/null
+++ b/contrib/wpa_supplicant/wpa_supplicant.h
@@ -0,0 +1,81 @@
+#ifndef WPA_SUPPLICANT_H
+#define WPA_SUPPLICANT_H
+
+/* Driver wrappers are not supposed to directly touch the internal data
+ * structure used in wpa_supplicant, so that definition is not provided here.
+ */
+struct wpa_supplicant;
+
+typedef enum {
+ EVENT_ASSOC, EVENT_DISASSOC, EVENT_MICHAEL_MIC_FAILURE,
+ EVENT_SCAN_RESULTS, EVENT_ASSOCINFO, EVENT_INTERFACE_STATUS,
+ EVENT_PMKID_CANDIDATE
+} wpa_event_type;
+
+union wpa_event_data {
+ struct {
+ /* Optional request information data: IEs included in AssocReq
+ * and AssocResp. If these are not returned by the driver,
+ * WPA Supplicant will generate the WPA/RSN IE. */
+ u8 *req_ies, *resp_ies;
+ size_t req_ies_len, resp_ies_len;
+
+ /* Optional Beacon/ProbeResp data: IEs included in Beacon or
+ * Probe Response frames from the current AP (i.e., the one
+ * that the client just associated with). This information is
+ * used to update WPA/RSN IE for the AP. If this field is not
+ * set, the results from previous scan will be used. If no
+ * data for the new AP is found, scan results will be requested
+ * again (without scan request). At this point, the driver is
+ * expected to provide WPA/RSN IE for the AP (if WPA/WPA2 is
+ * used). */
+ u8 *beacon_ies; /* beacon or probe resp IEs */
+ size_t beacon_ies_len;
+ } assoc_info;
+ struct {
+ int unicast;
+ } michael_mic_failure;
+ struct {
+ char ifname[20];
+ enum {
+ EVENT_INTERFACE_ADDED, EVENT_INTERFACE_REMOVED
+ } ievent;
+ } interface_status;
+ struct {
+ u8 bssid[ETH_ALEN];
+ int index; /* smaller the index, higher the priority */
+ int preauth;
+ } pmkid_candidate;
+};
+
+/**
+ * wpa_supplicant_event - report a driver event for wpa_supplicant
+ * @wpa_s: pointer to wpa_supplicant data; this is the @ctx variable registered
+ * with wpa_driver_events_init()
+ * @event: event type (defined above)
+ * @data: possible extra data for the event
+ *
+ * Driver wrapper code should call this function whenever an event is received
+ * from the driver.
+ */
+void wpa_supplicant_event(struct wpa_supplicant *wpa_s, wpa_event_type event,
+ union wpa_event_data *data);
+
+/**
+ * wpa_msg - conditional printf for default target and ctrl_iface monitors
+ * @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. The
+ * output may be directed to stdout, stderr, and/or syslog based on
+ * configuration. This function is like wpa_printf(), but it also sends the
+ * same message to all attached ctrl_iface monitors.
+ *
+ * Note: New line '\n' is added to the end of the text when printing to stdout.
+ */
+void wpa_msg(struct wpa_supplicant *wpa_s, int level, char *fmt, ...)
+__attribute__ ((format (printf, 3, 4)));
+
+const char * wpa_ssid_txt(u8 *ssid, size_t ssid_len);
+
+#endif /* WPA_SUPPLICANT_H */
diff --git a/contrib/wpa_supplicant/wpa_supplicant_i.h b/contrib/wpa_supplicant/wpa_supplicant_i.h
new file mode 100644
index 000000000000..3883393c8aa6
--- /dev/null
+++ b/contrib/wpa_supplicant/wpa_supplicant_i.h
@@ -0,0 +1,469 @@
+#ifndef WPA_SUPPLICANT_I_H
+#define WPA_SUPPLICANT_I_H
+
+#include "driver.h"
+
+#ifdef EAPOL_TEST
+#include <netinet/in.h>
+
+struct hostapd_radius_server {
+ struct in_addr addr;
+ int port;
+ u8 *shared_secret;
+ size_t shared_secret_len;
+};
+#endif /* EAPOL_TEST */
+
+#define PMKID_LEN 16
+struct rsn_pmksa_cache {
+ struct rsn_pmksa_cache *next;
+ u8 pmkid[PMKID_LEN];
+ u8 pmk[PMK_LEN];
+ size_t pmk_len;
+ time_t expiration;
+ int akmp; /* WPA_KEY_MGMT_* */
+ u8 aa[ETH_ALEN];
+};
+
+struct rsn_pmksa_candidate {
+ struct rsn_pmksa_candidate *next;
+ u8 bssid[ETH_ALEN];
+ int priority;
+};
+
+
+struct wpa_ptk {
+ u8 mic_key[16]; /* EAPOL-Key MIC Key (MK) */
+ u8 encr_key[16]; /* EAPOL-Key Encryption Key (EK) */
+ u8 tk1[16]; /* Temporal Key 1 (TK1) */
+ union {
+ u8 tk2[16]; /* Temporal Key 2 (TK2) */
+ struct {
+ u8 tx_mic_key[8];
+ u8 rx_mic_key[8];
+ } auth;
+ } u;
+} __attribute__ ((packed));
+
+
+struct wpa_blacklist {
+ struct wpa_blacklist *next;
+ u8 bssid[ETH_ALEN];
+};
+
+
+struct wpa_supplicant {
+ struct wpa_supplicant *head;
+ struct wpa_supplicant *next;
+ struct l2_packet_data *l2;
+ unsigned char own_addr[ETH_ALEN];
+ char ifname[100];
+#ifdef CONFIG_XSUPPLICANT_IFACE
+ int dot1x_s; /* socket for connection to Xsupplicant */
+ int ext_pmk_received; /* 1 = PMK was received from Xsupplicant */
+#endif /* CONFIG_XSUPPLICANT_IFACE */
+
+ u8 pmk[PMK_LEN];
+ size_t pmk_len;
+ u8 snonce[WPA_NONCE_LEN];
+ u8 anonce[WPA_NONCE_LEN]; /* ANonce from the last 1/4 msg */
+ struct wpa_ptk ptk, tptk;
+ int ptk_set, tptk_set;
+ int renew_snonce;
+ char *confname;
+ struct wpa_config *conf;
+ u8 request_counter[WPA_REPLAY_COUNTER_LEN];
+ int countermeasures;
+ time_t last_michael_mic_error;
+ u8 rx_replay_counter[WPA_REPLAY_COUNTER_LEN];
+ int rx_replay_counter_set;
+ u8 bssid[ETH_ALEN];
+ int reassociate; /* reassociation requested */
+ struct wpa_ssid *current_ssid;
+ u8 *ap_wpa_ie, *ap_rsn_ie;
+ size_t ap_wpa_ie_len, ap_rsn_ie_len;
+ u8 *assoc_wpa_ie;
+ size_t assoc_wpa_ie_len;
+
+ /* Selected configuration (based on Beacon/ProbeResp WPA IE) */
+ int proto;
+ int pairwise_cipher;
+ int group_cipher;
+ int key_mgmt;
+
+ void *drv_priv; /* private data used by driver_ops */
+
+ struct wpa_ssid *prev_scan_ssid; /* previously scanned SSID;
+ * NULL = not yet initialized (start
+ * with broadcast SSID)
+ * BROADCAST_SSID_SCAN = broadcast
+ * SSID was used in the previous scan
+ */
+#define BROADCAST_SSID_SCAN ((struct wpa_ssid *) 1)
+
+ struct wpa_scan_result *scan_results;
+ int num_scan_results;
+
+ struct wpa_driver_ops *driver;
+ int interface_removed; /* whether the network interface has been
+ * removed */
+ struct eapol_sm *eapol;
+
+ int ctrl_sock; /* UNIX domain socket for control interface or -1 if
+ * not used */
+ struct wpa_ctrl_dst *ctrl_dst;
+
+ enum {
+ WPA_DISCONNECTED, WPA_SCANNING, WPA_ASSOCIATING,
+ WPA_ASSOCIATED, WPA_4WAY_HANDSHAKE, WPA_GROUP_HANDSHAKE,
+ WPA_COMPLETED
+ } wpa_state;
+
+ struct rsn_pmksa_cache *pmksa; /* PMKSA cache */
+ int pmksa_count; /* number of entries in PMKSA cache */
+ struct rsn_pmksa_cache *cur_pmksa; /* current PMKSA entry */
+ struct rsn_pmksa_candidate *pmksa_candidates;
+
+ struct l2_packet_data *l2_preauth;
+ u8 preauth_bssid[ETH_ALEN]; /* current RSN pre-auth peer or
+ * 00:00:00:00:00:00 if no pre-auth is
+ * in progress */
+ struct eapol_sm *preauth_eapol;
+
+ int eapol_received; /* number of EAPOL packets received after the
+ * previous association event */
+
+ u8 *imsi;
+ size_t imsi_len;
+ struct scard_data *scard;
+
+ unsigned char last_eapol_src[ETH_ALEN];
+
+ int keys_cleared;
+
+ struct wpa_blacklist *blacklist;
+
+#ifdef EAPOL_TEST
+ u8 radius_identifier;
+ struct radius_msg *last_recv_radius;
+ struct in_addr own_ip_addr;
+ struct radius_client_data *radius;
+
+ /* RADIUS Authentication and Accounting servers in priority order */
+ struct hostapd_radius_server *auth_servers, *auth_server;
+ int num_auth_servers;
+ struct hostapd_radius_server *acct_servers, *acct_server;
+ int num_acct_servers;
+
+ int radius_retry_primary_interval;
+ int radius_acct_interim_interval;
+
+ u8 *last_eap_radius; /* last received EAP Response from Authentication
+ * Server */
+ size_t last_eap_radius_len;
+
+ u8 authenticator_pmk[PMK_LEN];
+ size_t authenticator_pmk_len;
+ int radius_access_accept_received;
+ int radius_access_reject_received;
+ int auth_timed_out;
+
+ u8 *eap_identity;
+ size_t eap_identity_len;
+#endif /* EAPOL_TEST */
+};
+
+
+/* wpa_supplicant.c */
+void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx);
+
+void wpa_supplicant_req_scan(struct wpa_supplicant *wpa_s, int sec, int usec);
+
+void wpa_supplicant_cancel_scan(struct wpa_supplicant *wpa_s);
+
+void wpa_supplicant_disassociate(struct wpa_supplicant *wpa_s,
+ int reason_code);
+void wpa_supplicant_deauthenticate(struct wpa_supplicant *wpa_s,
+ int reason_code);
+
+void wpa_supplicant_req_auth_timeout(struct wpa_supplicant *wpa_s,
+ int sec, int usec);
+
+void wpa_supplicant_cancel_auth_timeout(struct wpa_supplicant *wpa_s);
+
+int wpa_supplicant_reload_configuration(struct wpa_supplicant *wpa_s);
+
+int wpa_supplicant_get_beacon_ie(struct wpa_supplicant *wpa_s);
+
+
+/* wpa.c */
+void wpa_supplicant_key_request(struct wpa_supplicant *wpa_s,
+ int error, int pairwise);
+
+struct wpa_ie_data {
+ int proto;
+ int pairwise_cipher;
+ int group_cipher;
+ int key_mgmt;
+ int capabilities;
+ int num_pmkid;
+ u8 *pmkid;
+};
+
+int wpa_parse_wpa_ie(struct wpa_supplicant *wpa_s, u8 *wpa_ie,
+ size_t wpa_ie_len, struct wpa_ie_data *data);
+
+int wpa_gen_wpa_ie(struct wpa_supplicant *wpa_s, u8 *wpa_ie);
+
+void wpa_supplicant_rx_eapol(void *ctx, unsigned char *src_addr,
+ unsigned char *buf, size_t len);
+
+struct wpa_ssid * wpa_supplicant_get_ssid(struct wpa_supplicant *wpa_s);
+
+void pmksa_cache_free(struct wpa_supplicant *wpa_s);
+struct rsn_pmksa_cache * pmksa_cache_get(struct wpa_supplicant *wpa_s,
+ u8 *aa, u8 *pmkid);
+int pmksa_cache_list(struct wpa_supplicant *wpa_s, char *buf, size_t len);
+void pmksa_candidate_free(struct wpa_supplicant *wpa_s);
+
+int wpa_get_mib(struct wpa_supplicant *wpa_s, char *buf, size_t buflen);
+
+struct wpa_scan_result;
+#ifdef IEEE8021X_EAPOL
+int rsn_preauth_init(struct wpa_supplicant *wpa_s, u8 *dst);
+void rsn_preauth_deinit(struct wpa_supplicant *wpa_s);
+void rsn_preauth_scan_results(struct wpa_supplicant *wpa_s,
+ struct wpa_scan_result *results, int count);
+void pmksa_candidate_add(struct wpa_supplicant *wpa_s, const u8 *bssid,
+ int prio);
+#else /* IEEE8021X_EAPOL */
+static inline int rsn_preauth_init(struct wpa_supplicant *wpa_s, u8 *dst)
+{
+ return -1;
+}
+
+static inline void rsn_preauth_deinit(struct wpa_supplicant *wpa_s)
+{
+}
+static inline void rsn_preauth_scan_results(struct wpa_supplicant *wpa_s,
+ struct wpa_scan_result *results,
+ int count)
+{
+}
+
+static inline void pmksa_candidate_add(struct wpa_supplicant *wpa_s,
+ const u8 *bssid,
+ int prio)
+{
+}
+#endif /* IEEE8021X_EAPOL */
+
+void wpa_supplicant_notify_eapol_done(void *ctx);
+
+/**
+ * wpa_eapol_send - send IEEE 802.1X EAPOL packet to the Authenticator
+ * @ctx: pointer to wpa_supplicant data
+ * @type: IEEE 802.1X packet type (IEEE802_1X_TYPE_*)
+ * @buf: EAPOL payload (after IEEE 802.1X header)
+ * @len: EAPOL payload length
+ *
+ * This function adds Ethernet and IEEE 802.1X header and sends the EAPOL frame
+ * to the current Authenticator or in case of pre-authentication, to the peer
+ * of the authentication.
+ */
+int wpa_eapol_send(void *ctx, int type, u8 *buf, size_t len);
+int wpa_eapol_send_preauth(void *ctx, int type, u8 *buf, size_t len);
+
+
+/* driver_ops */
+static inline void * wpa_drv_init(struct wpa_supplicant *wpa_s,
+ const char *ifname)
+{
+ if (wpa_s->driver->init) {
+ return wpa_s->driver->init(wpa_s, ifname);
+ }
+ return NULL;
+}
+
+static inline void wpa_drv_deinit(struct wpa_supplicant *wpa_s)
+{
+ if (wpa_s->driver->deinit)
+ wpa_s->driver->deinit(wpa_s->drv_priv);
+}
+
+static inline int wpa_drv_set_drop_unencrypted(struct wpa_supplicant *wpa_s,
+ int enabled)
+{
+ if (wpa_s->driver->set_drop_unencrypted) {
+ return wpa_s->driver->set_drop_unencrypted(wpa_s->drv_priv,
+ enabled);
+ }
+ return -1;
+}
+
+static inline int wpa_drv_set_countermeasures(struct wpa_supplicant *wpa_s,
+ int enabled)
+{
+ if (wpa_s->driver->set_countermeasures) {
+ return wpa_s->driver->set_countermeasures(wpa_s->drv_priv,
+ enabled);
+ }
+ return -1;
+}
+
+static inline int wpa_drv_set_auth_alg(struct wpa_supplicant *wpa_s,
+ int auth_alg)
+{
+ if (wpa_s->driver->set_auth_alg) {
+ return wpa_s->driver->set_auth_alg(wpa_s->drv_priv,
+ auth_alg);
+ }
+ return -1;
+}
+
+static inline int wpa_drv_set_wpa(struct wpa_supplicant *wpa_s, int enabled)
+{
+ if (wpa_s->driver->set_wpa) {
+ return wpa_s->driver->set_wpa(wpa_s->drv_priv, enabled);
+ }
+ return -1;
+}
+
+static inline int wpa_drv_associate(struct wpa_supplicant *wpa_s,
+ struct wpa_driver_associate_params *params)
+{
+ if (wpa_s->driver->associate) {
+ return wpa_s->driver->associate(wpa_s->drv_priv, params);
+ }
+ return -1;
+}
+
+static inline int wpa_drv_scan(struct wpa_supplicant *wpa_s, const u8 *ssid,
+ size_t ssid_len)
+{
+ if (wpa_s->driver->scan) {
+ return wpa_s->driver->scan(wpa_s->drv_priv, ssid, ssid_len);
+ }
+ return -1;
+}
+
+static inline int wpa_drv_get_scan_results(struct wpa_supplicant *wpa_s,
+ struct wpa_scan_result *results,
+ size_t max_size)
+{
+ if (wpa_s->driver->get_scan_results) {
+ return wpa_s->driver->get_scan_results(wpa_s->drv_priv,
+ results, max_size);
+ }
+ return -1;
+}
+
+static inline int wpa_drv_get_bssid(struct wpa_supplicant *wpa_s, u8 *bssid)
+{
+ if (wpa_s->driver->get_bssid) {
+ return wpa_s->driver->get_bssid(wpa_s->drv_priv, bssid);
+ }
+ return -1;
+}
+
+static inline int wpa_drv_get_ssid(struct wpa_supplicant *wpa_s, u8 *ssid)
+{
+ if (wpa_s->driver->get_ssid) {
+ return wpa_s->driver->get_ssid(wpa_s->drv_priv, ssid);
+ }
+ return -1;
+}
+
+static inline int wpa_drv_set_key(struct wpa_supplicant *wpa_s, 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)
+{
+ if (wpa_s->driver->set_key) {
+ return wpa_s->driver->set_key(wpa_s->drv_priv, alg, addr,
+ key_idx, set_tx, seq, seq_len,
+ key, key_len);
+ }
+ return -1;
+}
+
+static inline int wpa_drv_deauthenticate(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);
+ }
+ return -1;
+}
+
+static inline int wpa_drv_disassociate(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);
+ }
+ return -1;
+}
+
+static inline int wpa_drv_add_pmkid(struct wpa_supplicant *wpa_s,
+ const u8 *bssid, const u8 *pmkid)
+{
+ if (wpa_s->driver->add_pmkid) {
+ return wpa_s->driver->add_pmkid(wpa_s->drv_priv, bssid, pmkid);
+ }
+ return -1;
+}
+
+static inline int wpa_drv_remove_pmkid(struct wpa_supplicant *wpa_s,
+ const u8 *bssid, const u8 *pmkid)
+{
+ if (wpa_s->driver->remove_pmkid) {
+ return wpa_s->driver->remove_pmkid(wpa_s->drv_priv, bssid,
+ pmkid);
+ }
+ return -1;
+}
+
+static inline int wpa_drv_flush_pmkid(struct wpa_supplicant *wpa_s)
+{
+ if (wpa_s->driver->flush_pmkid) {
+ return wpa_s->driver->flush_pmkid(wpa_s->drv_priv);
+ }
+ return -1;
+}
+
+static inline int wpa_drv_get_capa(struct wpa_supplicant *wpa_s,
+ struct wpa_driver_capa *capa)
+{
+ if (wpa_s->driver->get_capa) {
+ return wpa_s->driver->get_capa(wpa_s->drv_priv, capa);
+ }
+ return -1;
+}
+
+static inline void wpa_drv_poll(struct wpa_supplicant *wpa_s)
+{
+ if (wpa_s->driver->poll) {
+ wpa_s->driver->poll(wpa_s->drv_priv);
+ }
+}
+
+static inline const char * wpa_drv_get_ifname(struct wpa_supplicant *wpa_s)
+{
+ if (wpa_s->driver->get_ifname) {
+ return wpa_s->driver->get_ifname(wpa_s->drv_priv);
+ }
+ return NULL;
+}
+
+static inline const u8 * wpa_drv_get_mac_addr(struct wpa_supplicant *wpa_s)
+{
+ if (wpa_s->driver->get_mac_addr) {
+ return wpa_s->driver->get_mac_addr(wpa_s->drv_priv);
+ }
+ return NULL;
+}
+
+#endif /* WPA_SUPPLICANT_I_H */