aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSam Leffler <sam@FreeBSD.org>2005-06-05 22:35:03 +0000
committerSam Leffler <sam@FreeBSD.org>2005-06-05 22:35:03 +0000
commit5d48cb5da295e638c3a5c1cb2ad6731da3615f18 (patch)
treea7d225a062cd128980707f3fa918dec2d015c46b
downloadsrc-5d48cb5da295e638c3a5c1cb2ad6731da3615f18.tar.gz
src-5d48cb5da295e638c3a5c1cb2ad6731da3615f18.zip
Stripped down import of hostapd v0.3.7vendor/hostapd/0.3.7
Notes
Notes: svn path=/vendor/hostapd/dist/; revision=147021 svn path=/vendor/hostapd/0.3.7/; revision=147023; tag=vendor/hostapd/0.3.7
-rw-r--r--contrib/hostapd/COPYING340
-rw-r--r--contrib/hostapd/ChangeLog186
-rw-r--r--contrib/hostapd/Makefile233
-rw-r--r--contrib/hostapd/README397
-rw-r--r--contrib/hostapd/accounting.c441
-rw-r--r--contrib/hostapd/accounting.h13
-rw-r--r--contrib/hostapd/aes.c1132
-rw-r--r--contrib/hostapd/aes_wrap.c642
-rw-r--r--contrib/hostapd/aes_wrap.h21
-rw-r--r--contrib/hostapd/ap.h99
-rw-r--r--contrib/hostapd/common.c335
-rw-r--r--contrib/hostapd/common.h222
-rw-r--r--contrib/hostapd/config.c1136
-rw-r--r--contrib/hostapd/config.h177
-rw-r--r--contrib/hostapd/crypto.c71
-rw-r--r--contrib/hostapd/crypto.h8
-rw-r--r--contrib/hostapd/ctrl_iface.c452
-rw-r--r--contrib/hostapd/ctrl_iface.h9
-rw-r--r--contrib/hostapd/defconfig66
-rw-r--r--contrib/hostapd/defs.h14
-rw-r--r--contrib/hostapd/developer.txt219
-rw-r--r--contrib/hostapd/driver.h262
-rw-r--r--contrib/hostapd/driver_test.c78
-rw-r--r--contrib/hostapd/driver_wired.c395
-rw-r--r--contrib/hostapd/eap.c911
-rw-r--r--contrib/hostapd/eap.h83
-rw-r--r--contrib/hostapd/eap_defs.h41
-rw-r--r--contrib/hostapd/eap_gtc.c158
-rw-r--r--contrib/hostapd/eap_i.h110
-rw-r--r--contrib/hostapd/eap_identity.c174
-rw-r--r--contrib/hostapd/eap_md5.c180
-rw-r--r--contrib/hostapd/eap_mschapv2.c483
-rw-r--r--contrib/hostapd/eap_peap.c720
-rw-r--r--contrib/hostapd/eap_sim.c431
-rw-r--r--contrib/hostapd/eap_sim_common.c788
-rw-r--r--contrib/hostapd/eap_sim_common.h101
-rw-r--r--contrib/hostapd/eap_sim_db.c242
-rw-r--r--contrib/hostapd/eap_sim_db.h44
-rw-r--r--contrib/hostapd/eap_tls.c246
-rw-r--r--contrib/hostapd/eap_tls_common.c272
-rw-r--r--contrib/hostapd/eap_tls_common.h47
-rw-r--r--contrib/hostapd/eap_tlv.c248
-rw-r--r--contrib/hostapd/eap_ttls.c1183
-rw-r--r--contrib/hostapd/eap_ttls.h57
-rw-r--r--contrib/hostapd/eapol_sm.c1183
-rw-r--r--contrib/hostapd/eapol_sm.h213
-rw-r--r--contrib/hostapd/eloop.c380
-rw-r--r--contrib/hostapd/eloop.h53
-rw-r--r--contrib/hostapd/hostap_common.h557
-rw-r--r--contrib/hostapd/hostapd.accept5
-rw-r--r--contrib/hostapd/hostapd.c802
-rw-r--r--contrib/hostapd/hostapd.conf293
-rw-r--r--contrib/hostapd/hostapd.deny5
-rw-r--r--contrib/hostapd/hostapd.eap_user45
-rw-r--r--contrib/hostapd/hostapd.h134
-rw-r--r--contrib/hostapd/hostapd.radius_clients4
-rw-r--r--contrib/hostapd/hostapd.sim_db9
-rw-r--r--contrib/hostapd/hostapd.wpa_psk9
-rw-r--r--contrib/hostapd/hostapd_cli.c600
-rw-r--r--contrib/hostapd/hostapd_ctrl.c188
-rw-r--r--contrib/hostapd/hostapd_ctrl.h18
-rw-r--r--contrib/hostapd/iapp.c521
-rw-r--r--contrib/hostapd/iapp.h31
-rw-r--r--contrib/hostapd/ieee802_11.c1217
-rw-r--r--contrib/hostapd/ieee802_11.h99
-rw-r--r--contrib/hostapd/ieee802_11_auth.c447
-rw-r--r--contrib/hostapd/ieee802_11_auth.h16
-rw-r--r--contrib/hostapd/ieee802_1x.c1649
-rw-r--r--contrib/hostapd/ieee802_1x.h84
-rw-r--r--contrib/hostapd/l2_packet.h34
-rw-r--r--contrib/hostapd/madwifi.conf201
-rw-r--r--contrib/hostapd/md5.c355
-rw-r--r--contrib/hostapd/md5.h43
-rw-r--r--contrib/hostapd/ms_funcs.c333
-rw-r--r--contrib/hostapd/ms_funcs.h25
-rw-r--r--contrib/hostapd/radius.c1125
-rw-r--r--contrib/hostapd/radius.h224
-rw-r--r--contrib/hostapd/radius_client.c1016
-rw-r--r--contrib/hostapd/radius_client.h41
-rw-r--r--contrib/hostapd/radius_server.c923
-rw-r--r--contrib/hostapd/radius_server.h45
-rw-r--r--contrib/hostapd/rc4.c63
-rw-r--r--contrib/hostapd/rc4.h7
-rw-r--r--contrib/hostapd/sha1.c910
-rw-r--r--contrib/hostapd/sha1.h50
-rw-r--r--contrib/hostapd/sta_info.c354
-rw-r--r--contrib/hostapd/sta_info.h19
-rw-r--r--contrib/hostapd/tls.h312
-rw-r--r--contrib/hostapd/tls_none.c22
-rw-r--r--contrib/hostapd/tls_openssl.c876
-rw-r--r--contrib/hostapd/version.h6
-rw-r--r--contrib/hostapd/wired.conf38
-rw-r--r--contrib/hostapd/wpa.c2820
-rw-r--r--contrib/hostapd/wpa.h193
94 files changed, 32064 insertions, 0 deletions
diff --git a/contrib/hostapd/COPYING b/contrib/hostapd/COPYING
new file mode 100644
index 000000000000..60549be514af
--- /dev/null
+++ b/contrib/hostapd/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/hostapd/ChangeLog b/contrib/hostapd/ChangeLog
new file mode 100644
index 000000000000..e4b73ec7e6e7
--- /dev/null
+++ b/contrib/hostapd/ChangeLog
@@ -0,0 +1,186 @@
+ChangeLog for hostapd
+
+2005-02-12 - v0.3.7 (beginning of 0.3.x stable releases)
+
+2005-01-23 - v0.3.5
+ * added support for configuring a forced PEAP version based on the
+ Phase 1 identity
+ * fixed PEAPv1 to use tunneled EAP-Success/Failure instead of EAP-TLV
+ to terminate authentication
+ * fixed EAP identifier duplicate processing with the new IEEE 802.1X
+ draft
+ * clear accounting data in the driver when starting a new accounting
+ session
+ * driver_madwifi: filter wireless events based on ifindex to allow more
+ than one network interface to be used
+ * fixed WPA message 2/4 processing not to cancel timeout for TimeoutEvt
+ setting if the packet does not pass MIC verification (e.g., due to
+ incorrect PSK); previously, message 1/4 was not tried again if an
+ invalid message 2/4 was received
+ * fixed reconfiguration of RADIUS client retransmission timer when
+ adding a new message to the pending list; previously, timer was not
+ updated at this point and if there was a pending message with long
+ time for the next retry, the new message needed to wait that long for
+ its first retry, too
+
+2005-01-09 - v0.3.4
+ * added support for configuring multiple allowed EAP types for Phase 2
+ authentication (EAP-PEAP, EAP-TTLS)
+ * fixed EAPOL-Start processing to trigger WPA reauthentication
+ (previously, only EAPOL authentication was done)
+
+2005-01-02 - v0.3.3
+ * added support for EAP-PEAP in the integrated EAP authenticator
+ * added support for EAP-GTC in the integrated EAP authenticator
+ * added support for configuring list of EAP methods for Phase 1 so that
+ the integrated EAP authenticator can, e.g., use the wildcard entry
+ for EAP-TLS and EAP-PEAP
+ * added support for EAP-TTLS in the integrated EAP authenticator
+ * added support for EAP-SIM in the integrated EAP authenticator
+ * added support for using hostapd as a RADIUS authentication server
+ with the integrated EAP authenticator taking care of EAP
+ authentication (new hostapd.conf options: radius_server_clients and
+ radius_server_auth_port); this is not included in default build; use
+ CONFIG_RADIUS_SERVER=y in .config to include
+
+2004-12-19 - v0.3.2
+ * removed 'daemonize' configuration file option since it has not really
+ been used at all for more than year
+ * driver_madwifi: fixed group key setup and added get_ssid method
+ * added support for EAP-MSCHAPv2 in the integrated EAP authenticator
+
+2004-12-12 - v0.3.1
+ * added support for integrated EAP-TLS authentication (new hostapd.conf
+ variables: ca_cert, server_cert, private_key, private_key_passwd);
+ this enabled dynamic keying (WPA2/WPA/IEEE 802.1X/WEP) without
+ external RADIUS server
+ * added support for reading PKCS#12 (PFX) files (as a replacement for
+ PEM/DER) to get certificate and private key (CONFIG_PKCS12)
+
+2004-12-05 - v0.3.0 (beginning of 0.3.x development releases)
+ * added support for Acct-{Input,Output}-Gigawords
+ * added support for Event-Timestamp (in RADIUS Accounting-Requests)
+ * added support for RADIUS Authentication Client MIB (RFC2618)
+ * added support for RADIUS Accounting Client MIB (RFC2620)
+ * made EAP re-authentication period configurable (eap_reauth_period)
+ * fixed EAPOL reauthentication to trigger WPA/WPA2 reauthentication
+ * fixed EAPOL state machine to stop if STA is removed during
+ eapol_sm_step(); this fixes at least one segfault triggering bug with
+ IEEE 802.11i pre-authentication
+ * added support for multiple WPA pre-shared keys (e.g., one for each
+ client MAC address or keys shared by a group of clients);
+ new hostapd.conf field wpa_psk_file for setting path to a text file
+ containing PSKs, see hostapd.wpa_psk for an example
+ * added support for multiple driver interfaces to allow hostapd to be
+ used with other drivers
+ * added wired authenticator driver interface (driver=wired in
+ hostapd.conf, see wired.conf for example configuration)
+ * added madwifi driver interface (driver=madwifi in hostapd.conf, see
+ madwifi.conf for example configuration; Note: include files from
+ madwifi project is needed for building and a configuration file,
+ .config, needs to be created in hostapd directory with
+ CONFIG_DRIVER_MADWIFI=y to include this driver interface in hostapd
+ build)
+ * fixed an alignment issue that could cause SHA-1 to fail on some
+ platforms (e.g., Intel ixp425 with a compiler that does not 32-bit
+ align variables)
+ * fixed RADIUS reconnection after an error in sending interim
+ accounting packets
+ * added hostapd control interface for external programs and an example
+ CLI, hostapd_cli (like wpa_cli for wpa_supplicant)
+ * started adding dot11, dot1x, radius MIBs ('hostapd_cli mib',
+ 'hostapd_cli sta <addr>')
+ * finished update from IEEE 802.1X-2001 to IEEE 802.1X-REV (now d11)
+ * added support for strict GTK rekeying (wpa_strict_rekey in
+ hostapd.conf)
+ * updated IAPP to use UDP port 3517 and multicast address 224.0.1.178
+ (instead of broadcast) for IAPP ADD-notify (moved from draft 3 to
+ IEEE 802.11F-2003)
+ * added Prism54 driver interface (driver=prism54 in hostapd.conf;
+ note: .config needs to be created in hostapd directory with
+ CONFIG_DRIVER_PRISM54=y to include this driver interface in hostapd
+ build)
+ * dual-licensed hostapd (GPLv2 and BSD licenses)
+ * fixed RADIUS accounting to generate a new session id for cases where
+ a station reassociates without first being complete deauthenticated
+ * fixed STA disassociation handler to mark next timeout state to
+ deauthenticate the station, i.e., skip long wait for inactivity poll
+ and extra disassociation, if the STA disassociates without
+ deauthenticating
+ * added integrated EAP authenticator that can be used instead of
+ external RADIUS authentication server; currently, only EAP-MD5 is
+ supported, so this cannot yet be used for key distribution; the EAP
+ method interface is generic, though, so adding new EAP methods should
+ be straightforward; new hostapd.conf variables: 'eap_authenticator'
+ and 'eap_user_file'; this obsoletes "minimal authentication server"
+ ('minimal_eap' in hostapd.conf) which is now removed
+ * added support for FreeBSD and driver interface for the BSD net80211
+ layer (driver=bsd in hostapd.conf and CONFIG_DRIVER_BSD=y in
+ .config); please note that some of the required kernel mods have not
+ yet been committed
+
+2004-07-17 - v0.2.4 (beginning of 0.2.x stable releases)
+ * fixed some accounting cases where Accounting-Start was sent when
+ IEEE 802.1X port was being deauthorized
+
+2004-06-20 - v0.2.3
+ * modified RADIUS client to re-connect the socket in case of certain
+ error codes that are generated when a network interface state is
+ changes (e.g., when IP address changes or the interface is set UP)
+ * fixed couple of cases where EAPOL state for a station was freed
+ twice causing a segfault for hostapd
+ * fixed couple of bugs in processing WPA deauthentication (freed data
+ was used)
+
+2004-05-31 - v0.2.2
+ * fixed WPA/WPA2 group rekeying to use key index correctly (GN/GM)
+ * fixed group rekeying to send zero TSC in EAPOL-Key messages to fix
+ cases where STAs dropped multicast frames as replay attacks
+ * added support for copying RADIUS Attribute 'Class' from
+ authentication messages into accounting messages
+ * send canned EAP failure if RADIUS server sends Access-Reject without
+ EAP message (previously, Supplicant was not notified in this case)
+ * fixed mixed WPA-PSK and WPA-EAP mode to work with WPA-PSK (i.e., do
+ not start EAPOL state machines if the STA selected to use WPA-PSK)
+
+2004-05-06 - v0.2.1
+ * added WPA and IEEE 802.11i/RSN (WPA2) Authenticator functionality
+ - based on IEEE 802.11i/D10.0 but modified to interoperate with WPA
+ (i.e., IEEE 802.11i/D3.0)
+ - supports WPA-only, RSN-only, and mixed WPA/RSN mode
+ - both WPA-PSK and WPA-RADIUS/EAP are supported
+ - PMKSA caching and pre-authentication
+ - new hostapd.conf variables: wpa, wpa_psk, wpa_passphrase,
+ wpa_key_mgmt, wpa_pairwise, wpa_group_rekey, wpa_gmk_rekey,
+ rsn_preauth, rsn_preauth_interfaces
+ * fixed interim accounting to remove any pending accounting messages
+ to the STA before sending a new one
+
+2004-02-15 - v0.2.0
+ * added support for Acct-Interim-Interval:
+ - draft-ietf-radius-acct-interim-01.txt
+ - use Acct-Interim-Interval attribute from Access-Accept if local
+ 'radius_acct_interim_interval' is not set
+ - allow different update intervals for each STA
+ * fixed event loop to call signal handlers only after returning from
+ the real signal handler
+ * reset sta->timeout_next after successful association to make sure
+ that the previously registered inactivity timer will not remove the
+ STA immediately (e.g., if STA deauthenticates and re-associates
+ before the timer is triggered).
+ * added new hostapd.conf variable, nas_identifier, that can be used to
+ add an optional RADIUS Attribute, NAS-Identifier, into authentication
+ and accounting messages
+ * added support for Accounting-On and Accounting-Off messages
+ * fixed accounting session handling to send Accounting-Start only once
+ per session and not to send Accounting-Stop if the session was not
+ initialized properly
+ * fixed Accounting-Stop statistics in cases where the message was
+ previously sent after the kernel entry for the STA (and/or IEEE
+ 802.1X data) was removed
+
+
+Note:
+
+Older changes up to and including v0.1.0 are included in the ChangeLog
+of the Host AP driver.
diff --git a/contrib/hostapd/Makefile b/contrib/hostapd/Makefile
new file mode 100644
index 000000000000..4c9c49edea8c
--- /dev/null
+++ b/contrib/hostapd/Makefile
@@ -0,0 +1,233 @@
+CC=gcc
+DIR_WPA_SUPPLICANT=.
+DIR_HOSTAP=.
+
+ifndef CFLAGS
+CFLAGS = -MMD -O2 -Wall -g
+endif
+
+# define HOSTAPD_DUMP_STATE to include SIGUSR1 handler for dumping state to
+# a file (undefine it, if you want to save in binary size)
+CFLAGS += -DHOSTAPD_DUMP_STATE
+
+# Include directories for CVS version
+CFLAGS += -I. -I$(DIR_HOSTAP) -I../utils -I$(DIR_WPA_SUPPLICANT)
+
+# Uncomment following line and set the path to your kernel tree include
+# directory if your C library does not include all header files.
+# CFLAGS += -DUSE_KERNEL_HEADERS -I/usr/src/linux/include
+
+OBJS = hostapd.o eloop.o ieee802_1x.o eapol_sm.o radius.o md5.o rc4.o \
+ common.o ieee802_11.o config.o ieee802_11_auth.o accounting.o \
+ sta_info.o radius_client.o sha1.o wpa.o aes_wrap.o ctrl_iface.o \
+ driver_conf.o
+
+-include .config
+
+ifdef CONFIG_IAPP
+CFLAGS += -DCONFIG_IAPP
+OBJS += iapp.o
+endif
+
+ifdef CONFIG_RSN_PREAUTH
+CFLAGS += -DCONFIG_RSN_PREAUTH
+CONFIG_L2_PACKET=y
+endif
+
+ifdef CONFIG_DRIVER_HOSTAP
+CFLAGS += -DCONFIG_DRIVER_HOSTAP
+OBJS += driver.o
+endif
+
+ifdef CONFIG_DRIVER_WIRED
+CFLAGS += -DCONFIG_DRIVER_WIRED
+OBJS += driver_wired.o
+endif
+
+ifdef CONFIG_DRIVER_MADWIFI
+CFLAGS += -DCONFIG_DRIVER_MADWIFI
+OBJS += driver_madwifi.o
+CONFIG_L2_PACKET=y
+endif
+
+ifdef CONFIG_DRIVER_PRISM54
+CFLAGS += -DCONFIG_DRIVER_PRISM54
+OBJS += driver_prism54.o
+endif
+
+ifdef CONFIG_DRIVER_BSD
+CFLAGS += -DCONFIG_DRIVER_BSD
+OBJS += driver_bsd.o
+CONFIG_L2_PACKET=y
+CONFIG_DNET_PCAP=y
+endif
+
+ifdef CONFIG_DRIVER_TEST
+CFLAGS += -DCONFIG_DRIVER_TEST
+OBJS += driver_test.o
+endif
+
+ifdef CONFIG_L2_PACKET
+OBJS += $(DIR_WPA_SUPPLICANT)/l2_packet.o
+endif
+
+ifdef CONFIG_DNET_PCAP
+CFLAGS += -DUSE_DNET_PCAP
+LIBS +=-ldnet -lpcap
+endif
+
+ifdef CONFIG_EAP_MD5
+CFLAGS += -DEAP_MD5
+OBJS += eap_md5.o
+endif
+
+ifdef CONFIG_EAP_TLS
+CFLAGS += -DEAP_TLS
+OBJS += eap_tls.o
+TLS_FUNCS=y
+endif
+
+ifdef CONFIG_EAP_PEAP
+CFLAGS += -DEAP_PEAP
+OBJS += eap_peap.o
+TLS_FUNCS=y
+CONFIG_EAP_TLV=y
+CONFIG_EAP_MSCHAPV2=y
+endif
+
+ifdef CONFIG_EAP_TTLS
+CFLAGS += -DEAP_TTLS
+OBJS += eap_ttls.o
+TLS_FUNCS=y
+endif
+
+ifdef CONFIG_EAP_MSCHAPV2
+CFLAGS += -DEAP_MSCHAPv2
+OBJS += eap_mschapv2.o
+MS_FUNCS=y
+endif
+
+ifdef CONFIG_EAP_GTC
+CFLAGS += -DEAP_GTC
+OBJS += eap_gtc.o
+endif
+
+ifdef CONFIG_EAP_SIM
+CFLAGS += -DEAP_SIM
+OBJS += eap_sim.o $(DIR_WPA_SUPPLICANT)/eap_sim_common.o
+# Example EAP-SIM interface for GSM authentication. This can be replaced with
+# another file implementating the interface specified in eap_sim_db.h.
+OBJS += eap_sim_db.o
+endif
+
+ifdef CONFIG_EAP_TLV
+CFLAGS += -DEAP_TLV
+OBJS += eap_tlv.o
+endif
+
+ifdef CONFIG_EAP
+CFLAGS += -DEAP_AUTHENTICATOR
+OBJS += eap.o eap_identity.o
+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 $(DIR_WPA_SUPPLICANT)/tls_openssl.o
+LIBS += -lssl -lcrypto
+LIBS_p += -lcrypto
+else
+OBJS += $(DIR_WPA_SUPPLICANT)/tls_none.o
+endif
+
+ifdef CONFIG_PKCS12
+CFLAGS += -DPKCS12_FUNCS
+endif
+
+ifdef MS_FUNCS
+ifndef TLS_FUNCS
+LIBS += -lcrypto
+endif
+OBJS += $(DIR_WPA_SUPPLICANT)/ms_funcs.o $(DIR_WPA_SUPPLICANT)/crypto.o
+endif
+
+ifdef CONFIG_RADIUS_SERVER
+CFLAGS += -DRADIUS_SERVER
+OBJS += radius_server.o
+endif
+
+ALL=hostapd hostapd_cli
+
+all: verify_config $(ALL)
+
+verify_config:
+ @if [ ! -r .config ]; then \
+ echo 'Building hostapd 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
+
+install: all
+ for i in $(ALL); do cp $$i /usr/local/bin/$$i; done
+
+hostapd: $(OBJS)
+ $(CC) -o hostapd $(OBJS) $(LIBS)
+
+driver_conf.c: Makefile .config
+ rm -f driver_conf.c
+ echo '/* THIS FILE AUTOMATICALLY GENERATED, DO NOT EDIT! */' \
+ > driver_conf.c
+ echo '#include <stdlib.h>' >> driver_conf.c
+ echo '#include <stdio.h>' >> driver_conf.c
+ echo '#include <sys/types.h>' >> driver_conf.c
+ echo '#include <netinet/in.h>' >> driver_conf.c
+ echo '#include "hostapd.h"' >> driver_conf.c
+ echo '#include "driver.h"' >> driver_conf.c
+ifdef CONFIG_DRIVER_HOSTAP
+ echo "void hostap_driver_register(void);" >> driver_conf.c
+endif
+ifdef CONFIG_DRIVER_WIRED
+ echo "void wired_driver_register(void);" >> driver_conf.c
+endif
+ifdef CONFIG_DRIVER_MADWIFI
+ echo "void madwifi_driver_register(void);" >> driver_conf.c
+endif
+ifdef CONFIG_DRIVER_PRISM54
+ echo "void prism54_driver_register(void);" >> driver_conf.c
+endif
+ifdef CONFIG_DRIVER_BSD
+ echo "void bsd_driver_register(void);" >> driver_conf.c
+endif
+ifdef CONFIG_DRIVER_TEST
+ echo "void test_driver_register(void);" >> driver_conf.c
+endif
+ echo 'void register_drivers(void) {' >> driver_conf.c
+ifdef CONFIG_DRIVER_HOSTAP
+ echo "hostap_driver_register();" >> driver_conf.c
+endif
+ifdef CONFIG_DRIVER_WIRED
+ echo "wired_driver_register();" >> driver_conf.c
+endif
+ifdef CONFIG_DRIVER_MADWIFI
+ echo "madwifi_driver_register();" >> driver_conf.c
+endif
+ifdef CONFIG_DRIVER_PRISM54
+ echo "prism54_driver_register();" >> driver_conf.c
+endif
+ifdef CONFIG_DRIVER_BSD
+ echo "bsd_driver_register();" >> driver_conf.c
+endif
+ifdef CONFIG_DRIVER_TEST
+ echo "test_driver_register();" >> driver_conf.c
+endif
+ echo '}' >> driver_conf.c
+
+hostapd_cli: hostapd_cli.o hostapd_ctrl.o
+ $(CC) -o hostapd_cli hostapd_cli.o hostapd_ctrl.o
+
+clean:
+ rm -f core *~ *.o hostapd *.d driver_conf.c
+
+-include $(OBJS:%.o=%.d)
diff --git a/contrib/hostapd/README b/contrib/hostapd/README
new file mode 100644
index 000000000000..18fc1793cac8
--- /dev/null
+++ b/contrib/hostapd/README
@@ -0,0 +1,397 @@
+hostapd - user space IEEE 802.11 AP and IEEE 802.1X/WPA/WPA2/EAP
+ Authenticator and RADIUS authentication server
+================================================================
+
+Copyright (c) 2002-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.
+
+
+
+Introduction
+============
+
+Originally, hostapd was an optional user space component for Host AP
+driver. It adds more features to the basic IEEE 802.11 management
+included in the kernel driver: using external RADIUS authentication
+server for MAC address based access control, IEEE 802.1X Authenticator
+and dynamic WEP keying, RADIUS accounting, WPA/WPA2 (IEEE 802.11i/RSN)
+Authenticator and dynamic TKIP/CCMP keying.
+
+The current version includes support for other drivers, an integrated
+EAP authenticator (i.e., allow full authentication without requiring
+an external RADIUS authentication server), and RADIUS authentication
+server for EAP authentication.
+
+
+Requirements
+------------
+
+Current hardware/software requirements:
+- drivers:
+ Host AP driver for Prism2/2.5/3.
+ (http://hostap.epitest.fi/)
+ Please note that station firmware version needs to be 1.7.0 or newer
+ to work in WPA mode.
+
+ madwifi driver for cards based on Atheros chip set (ar521x)
+ (http://sourceforge.net/projects/madwifi/)
+ Please note that you will need to modify the hostapd Makefile
+ to use correct path for madwifi driver root directory
+ (CFLAGS += -I../head line in Makefile).
+
+ Prism54 driver for Intersil/Conexant Prism GT/Duette/Indigo
+ (http://www.prism54.org/)
+
+ Any wired Ethernet driver for wired IEEE 802.1X authentication
+ (experimental code)
+
+ FreeBSD -current (with some kernel mods that have not yet been
+ committed when hostapd v0.3.0 was released)
+ BSD net80211 layer (e.g., Atheros driver)
+
+
+Build configuration
+-------------------
+
+In order to be able to build hostapd, you will need to create a build
+time configuration file, .config that selects which optional
+components are included. See defconfig file for example configuration
+and list of available options.
+
+
+
+IEEE 802.1X
+===========
+
+IEEE Std 802.1X-2001 is a standard for port-based network access
+control. In case of IEEE 802.11 networks, a "virtual port" is used
+between each associated station and the AP. IEEE 802.11 specifies
+minimal authentication mechanism for stations, whereas IEEE 802.1X
+introduces a extensible mechanism for authenticating and authorizing
+users.
+
+IEEE 802.1X uses elements called Supplicant, Authenticator, Port
+Access Entity, and Authentication Server. Supplicant is a component in
+a station and it performs the authentication with the Authentication
+Server. An access point includes an Authenticator that relays the packets
+between a Supplicant and an Authentication Server. In addition, it has a
+Port Access Entity (PAE) with Authenticator functionality for
+controlling the virtual port authorization, i.e., whether to accept
+packets from or to the station.
+
+IEEE 802.1X uses Extensible Authentication Protocol (EAP). The frames
+between a Supplicant and an Authenticator are sent using EAP over LAN
+(EAPOL) and the Authenticator relays these frames to the Authentication
+Server (and similarly, relays the messages from the Authentication
+Server to the Supplicant). The Authentication Server can be colocated with the
+Authenticator, in which case there is no need for additional protocol
+for EAP frame transmission. However, a more common configuration is to
+use an external Authentication Server and encapsulate EAP frame in the
+frames used by that server. RADIUS is suitable for this, but IEEE
+802.1X would also allow other mechanisms.
+
+Host AP driver includes PAE functionality in the kernel driver. It
+is a relatively simple mechanism for denying normal frames going to
+or coming from an unauthorized port. PAE allows IEEE 802.1X related
+frames to be passed between the Supplicant and the Authenticator even
+on an unauthorized port.
+
+User space daemon, hostapd, includes Authenticator functionality. It
+receives 802.1X (EAPOL) frames from the Supplicant using the wlan#ap
+device that is also used with IEEE 802.11 management frames. The
+frames to the Supplicant are sent using the same device.
+
+hostapd includes a minimal colocated Authentication Server for testing
+purposes. It only requests the identity of the Supplicant and
+authorizes any host that is able to send a valid EAP Response
+frame. This can be used for quick testing since it does not require an
+external Authentication Server, but it should not be used for any real
+authentication purposes since no keys are required and anyone can
+authenticate.
+
+The normal configuration of the Authenticator would use an external
+Authentication Server. hostapd supports RADIUS encapsulation of EAP
+packets, so the Authentication Server should be a RADIUS server, like
+FreeRADIUS (http://www.freeradius.org/). The Authenticator in hostapd
+relays the frames between the Supplicant and the Authentication
+Server. It also controls the PAE functionality in the kernel driver by
+controlling virtual port authorization, i.e., station-AP
+connection, based on the IEEE 802.1X state.
+
+When a station would like to use the services of an access point, it
+will first perform IEEE 802.11 authentication. This is normally done
+with open systems authentication, so there is no security. After
+this, IEEE 802.11 association is performed. If IEEE 802.1X is
+configured to be used, the virtual port for the station is set in
+Unauthorized state and only IEEE 802.1X frames are accepted at this
+point. The Authenticator will then ask the Supplicant to authenticate
+with the Authentication Server. After this is completed successfully,
+the virtual port is set to Authorized state and frames from and to the
+station are accepted.
+
+Host AP configuration for IEEE 802.1X
+-------------------------------------
+
+The user space daemon has its own configuration file that can be used to
+define AP options. Distribution package contains an example
+configuration file (hostapd/hostapd.conf) that can be used as a basis
+for configuration. It includes examples of all supported configuration
+options and short description of each option. hostapd should be started
+with full path to the configuration file as the command line argument,
+e.g., './hostapd /etc/hostapd.conf'. If you have more that one wireless
+LAN card, you can use one hostapd process for multiple interfaces by
+giving a list of configuration files (one per interface) in the command
+line.
+
+hostapd includes a minimal co-located IEEE 802.1X server which can be
+used to test IEEE 802.1X authentication. However, it should not be
+used in normal use since it does not provide any security. This can be
+configured by setting ieee8021x and minimal_eap options in the
+configuration file.
+
+An external Authentication Server (RADIUS) is configured with
+auth_server_{addr,port,shared_secret} options. In addition,
+ieee8021x and own_ip_addr must be set for this mode. With such
+configuration, the co-located Authentication Server is not used and EAP
+frames will be relayed using EAPOL between the Supplicant and the
+Authenticator and RADIUS encapsulation between the Authenticator and
+the Authentication Server. Other than this, the functionality is similar
+to the case with the co-located Authentication Server.
+
+Authentication Server and Supplicant
+------------------------------------
+
+Any RADIUS server supporting EAP should be usable as an IEEE 802.1X
+Authentication Server with hostapd Authenticator. FreeRADIUS
+(http://www.freeradius.org/) has been successfully tested with hostapd
+Authenticator and both Xsupplicant (http://www.open1x.org) and Windows
+XP Supplicants. EAP/TLS was used with Xsupplicant and
+EAP/MD5-Challenge with Windows XP.
+
+http://www.missl.cs.umd.edu/wireless/eaptls/ has useful information
+about using EAP/TLS with FreeRADIUS and Xsupplicant (just replace
+Cisco access point with Host AP driver, hostapd daemon, and a Prism2
+card ;-). http://www.freeradius.org/doc/EAP-MD5.html has information
+about using EAP/MD5 with FreeRADIUS, including instructions for WinXP
+configuration. http://www.denobula.com/EAPTLS.pdf has a HOWTO on
+EAP/TLS use with WinXP Supplicant.
+
+Automatic WEP key configuration
+-------------------------------
+
+EAP/TLS generates a session key that can be used to send WEP keys from
+an AP to authenticated stations. The Authenticator in hostapd can be
+configured to automatically select a random default/broadcast key
+(shared by all authenticated stations) with wep_key_len_broadcast
+option (5 for 40-bit WEP or 13 for 104-bit WEP). In addition,
+wep_key_len_unicast option can be used to configure individual unicast
+keys for stations. This requires support for individual keys in the
+station driver.
+
+WEP keys can be automatically updated by configuring rekeying. This
+will improve security of the network since same WEP key will only be
+used for a limited period of time. wep_rekey_period option sets the
+interval for rekeying in seconds.
+
+
+WPA/WPA2
+========
+
+Features
+--------
+
+Supported WPA/IEEE 802.11i features:
+- WPA-PSK ("WPA-Personal")
+- WPA with EAP (e.g., with RADIUS authentication server) ("WPA-Enterprise")
+- key management for CCMP, TKIP, WEP104, WEP40
+- RSN/WPA2 (IEEE 802.11i), including PMKSA caching and pre-authentication
+
+WPA
+---
+
+The original security mechanism of IEEE 802.11 standard was not
+designed to be strong and has proved 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.
+Testing for WPA2 is likely to start during the second half of 2004.
+
+hostapd configuration for WPA/WPA2
+----------------------------------
+
+TODO
+
+# Enable WPA. Setting this variable configures the AP to require WPA (either
+# WPA-PSK or WPA-RADIUS/EAP based on other configuration). For WPA-PSK, either
+# wpa_psk or wpa_passphrase must be set and wpa_key_mgmt must include WPA-PSK.
+# For WPA-RADIUS/EAP, ieee8021x must be set (but without dynamic WEP keys),
+# RADIUS authentication server must be configured, and WPA-EAP must be included
+# in wpa_key_mgmt.
+# This field is a bit field that can be used to enable WPA (IEEE 802.11i/D3.0)
+# and/or WPA2 (full IEEE 802.11i/RSN):
+# bit0 = WPA
+# bit1 = IEEE 802.11i/RSN (WPA2)
+#wpa=1
+
+# WPA pre-shared keys for WPA-PSK. This can be either entered as a 256-bit
+# secret in hex format (64 hex digits), wpa_psk, or as an ASCII passphrase
+# (8..63 characters) that will be converted to PSK. This conversion uses SSID
+# so the PSK changes when ASCII passphrase is used and the SSID is changed.
+#wpa_psk=0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
+#wpa_passphrase=secret passphrase
+
+# Set of accepted key management algorithms (WPA-PSK, WPA-EAP, or both). The
+# entries are separated with a space.
+#wpa_key_mgmt=WPA-PSK WPA-EAP
+
+# Set of accepted cipher suites (encryption algorithms) for pairwise keys
+# (unicast packets). This is a space separated list of algorithms:
+# CCMP = AES in Counter mode with CBC-MAC [RFC 3610, IEEE 802.11i]
+# TKIP = Temporal Key Integrity Protocol [IEEE 802.11i]
+# Group cipher suite (encryption algorithm for broadcast and multicast frames)
+# is automatically selected based on this configuration. If only CCMP is
+# allowed as the pairwise cipher, group cipher will also be CCMP. Otherwise,
+# TKIP will be used as the group cipher.
+#wpa_pairwise=TKIP CCMP
+
+# Time interval for rekeying GTK (broadcast/multicast encryption keys) in
+# seconds.
+#wpa_group_rekey=600
+
+# Time interval for rekeying GMK (master key used internally to generate GTKs
+# (in seconds).
+#wpa_gmk_rekey=86400
+
+# Enable IEEE 802.11i/RSN/WPA2 pre-authentication. This is used to speed up
+# roaming be pre-authenticating IEEE 802.1X/EAP part of the full RSN
+# authentication and key handshake before actually associating with a new AP.
+#rsn_preauth=1
+#
+# Space separated list of interfaces from which pre-authentication frames are
+# accepted (e.g., 'eth0' or 'eth0 wlan0wds0'. This list should include all
+# interface that are used for connections to other APs. This could include
+# wired interfaces and WDS links. The normal wireless data interface towards
+# associated stations (e.g., wlan0) should not be added, since
+# pre-authentication is only used with APs other than the currently associated
+# one.
+#rsn_preauth_interfaces=eth0
diff --git a/contrib/hostapd/accounting.c b/contrib/hostapd/accounting.c
new file mode 100644
index 000000000000..188f8598b0b1
--- /dev/null
+++ b/contrib/hostapd/accounting.c
@@ -0,0 +1,441 @@
+/*
+ * Host AP (software wireless LAN access point) user space daemon for
+ * Host AP kernel driver / Accounting
+ * 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 <unistd.h>
+#include <netinet/in.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <signal.h>
+#include <assert.h>
+#include <time.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+
+
+#include "hostapd.h"
+#include "radius.h"
+#include "radius_client.h"
+#include "eloop.h"
+#include "accounting.h"
+#include "ieee802_1x.h"
+#include "driver.h"
+
+
+/* Default interval in seconds for polling TX/RX octets from the driver if
+ * STA is not using interim accounting. This detects wrap arounds for
+ * input/output octets and updates Acct-{Input,Output}-Gigawords. */
+#define ACCT_DEFAULT_UPDATE_INTERVAL 300
+
+static struct radius_msg * accounting_msg(hostapd *hapd, struct sta_info *sta,
+ int status_type)
+{
+ struct radius_msg *msg;
+ char buf[128];
+ u8 *val;
+ size_t len;
+
+ msg = radius_msg_new(RADIUS_CODE_ACCOUNTING_REQUEST,
+ radius_client_get_id(hapd->radius));
+ if (msg == NULL) {
+ printf("Could not create net RADIUS packet\n");
+ return NULL;
+ }
+
+ if (sta) {
+ radius_msg_make_authenticator(msg, (u8 *) sta, sizeof(*sta));
+
+ snprintf(buf, sizeof(buf), "%08X-%08X",
+ sta->acct_session_id_hi, sta->acct_session_id_lo);
+ if (!radius_msg_add_attr(msg, RADIUS_ATTR_ACCT_SESSION_ID,
+ (u8 *) buf, strlen(buf))) {
+ printf("Could not add Acct-Session-Id\n");
+ goto fail;
+ }
+ } else {
+ radius_msg_make_authenticator(msg, (u8 *) hapd, sizeof(*hapd));
+ }
+
+ if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_STATUS_TYPE,
+ status_type)) {
+ printf("Could not add Acct-Status-Type\n");
+ goto fail;
+ }
+
+ if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_AUTHENTIC,
+ hapd->conf->ieee802_1x ?
+ RADIUS_ACCT_AUTHENTIC_RADIUS :
+ RADIUS_ACCT_AUTHENTIC_LOCAL)) {
+ printf("Could not add Acct-Authentic\n");
+ goto fail;
+ }
+
+ if (sta) {
+ val = ieee802_1x_get_identity(sta->eapol_sm, &len);
+ if (!val) {
+ snprintf(buf, sizeof(buf), RADIUS_ADDR_FORMAT,
+ MAC2STR(sta->addr));
+ val = (u8 *) buf;
+ len = strlen(buf);
+ }
+
+ if (!radius_msg_add_attr(msg, RADIUS_ATTR_USER_NAME, val,
+ len)) {
+ printf("Could not add User-Name\n");
+ goto fail;
+ }
+ }
+
+ if (!radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IP_ADDRESS,
+ (u8 *) &hapd->conf->own_ip_addr, 4)) {
+ printf("Could not add NAS-IP-Address\n");
+ goto fail;
+ }
+
+ if (hapd->conf->nas_identifier &&
+ !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IDENTIFIER,
+ (u8 *) hapd->conf->nas_identifier,
+ strlen(hapd->conf->nas_identifier))) {
+ printf("Could not add NAS-Identifier\n");
+ goto fail;
+ }
+
+ if (sta &&
+ !radius_msg_add_attr_int32(msg, RADIUS_ATTR_NAS_PORT, sta->aid)) {
+ printf("Could not add NAS-Port\n");
+ goto fail;
+ }
+
+ snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT ":%s",
+ MAC2STR(hapd->own_addr), hapd->conf->ssid);
+ if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLED_STATION_ID,
+ (u8 *) buf, strlen(buf))) {
+ printf("Could not add Called-Station-Id\n");
+ goto fail;
+ }
+
+ if (sta) {
+ snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT,
+ MAC2STR(sta->addr));
+ if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLING_STATION_ID,
+ (u8 *) buf, strlen(buf))) {
+ printf("Could not add Calling-Station-Id\n");
+ goto fail;
+ }
+
+ if (!radius_msg_add_attr_int32(
+ msg, RADIUS_ATTR_NAS_PORT_TYPE,
+ RADIUS_NAS_PORT_TYPE_IEEE_802_11)) {
+ printf("Could not add NAS-Port-Type\n");
+ goto fail;
+ }
+
+ 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;
+ }
+
+ val = ieee802_1x_get_radius_class(sta->eapol_sm, &len);
+ if (val &&
+ !radius_msg_add_attr(msg, RADIUS_ATTR_CLASS, val, len)) {
+ printf("Could not add Class\n");
+ goto fail;
+ }
+ }
+
+ return msg;
+
+ fail:
+ radius_msg_free(msg);
+ free(msg);
+ return NULL;
+}
+
+
+static int accounting_sta_update_stats(struct hostapd_data *hapd,
+ struct sta_info *sta,
+ struct hostap_sta_driver_data *data)
+{
+ if (hostapd_read_sta_data(hapd, data, sta->addr))
+ return -1;
+
+ if (sta->last_rx_bytes > data->rx_bytes)
+ sta->acct_input_gigawords++;
+ if (sta->last_tx_bytes > data->tx_bytes)
+ sta->acct_output_gigawords++;
+ sta->last_rx_bytes = data->rx_bytes;
+ sta->last_tx_bytes = data->tx_bytes;
+
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS,
+ HOSTAPD_LEVEL_DEBUG, "updated TX/RX stats: "
+ "Acct-Input-Octets=%lu Acct-Input-Gigawords=%u "
+ "Acct-Output-Octets=%lu Acct-Output-Gigawords=%u",
+ sta->last_rx_bytes, sta->acct_input_gigawords,
+ sta->last_tx_bytes, sta->acct_output_gigawords);
+
+ return 0;
+}
+
+
+static void accounting_interim_update(void *eloop_ctx, void *timeout_ctx)
+{
+ hostapd *hapd = eloop_ctx;
+ struct sta_info *sta = timeout_ctx;
+ int interval;
+
+ if (sta->acct_interim_interval) {
+ accounting_sta_interim(hapd, sta);
+ interval = sta->acct_interim_interval;
+ } else {
+ struct hostap_sta_driver_data data;
+ accounting_sta_update_stats(hapd, sta, &data);
+ interval = ACCT_DEFAULT_UPDATE_INTERVAL;
+ }
+
+ eloop_register_timeout(interval, 0, accounting_interim_update,
+ hapd, sta);
+}
+
+
+void accounting_sta_start(hostapd *hapd, struct sta_info *sta)
+{
+ struct radius_msg *msg;
+ int interval;
+
+ if (sta->acct_session_started)
+ return;
+
+ time(&sta->acct_session_start);
+ sta->last_rx_bytes = sta->last_tx_bytes = 0;
+ sta->acct_input_gigawords = sta->acct_output_gigawords = 0;
+ hostapd_sta_clear_stats(hapd, sta->addr);
+
+ if (!hapd->conf->acct_server)
+ return;
+
+ if (sta->acct_interim_interval)
+ interval = sta->acct_interim_interval;
+ else
+ interval = ACCT_DEFAULT_UPDATE_INTERVAL;
+ eloop_register_timeout(interval, 0, accounting_interim_update,
+ hapd, sta);
+
+ msg = accounting_msg(hapd, sta, RADIUS_ACCT_STATUS_TYPE_START);
+ if (msg)
+ radius_client_send(hapd->radius, msg, RADIUS_ACCT, sta->addr);
+
+ sta->acct_session_started = 1;
+}
+
+
+void accounting_sta_report(hostapd *hapd, struct sta_info *sta, int stop)
+{
+ struct radius_msg *msg;
+ int cause = sta->acct_terminate_cause;
+ struct hostap_sta_driver_data data;
+ u32 gigawords;
+
+ if (!hapd->conf->acct_server)
+ return;
+
+ msg = accounting_msg(hapd, sta,
+ stop ? RADIUS_ACCT_STATUS_TYPE_STOP :
+ RADIUS_ACCT_STATUS_TYPE_INTERIM_UPDATE);
+ if (!msg) {
+ printf("Could not create RADIUS Accounting message\n");
+ return;
+ }
+
+ if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_SESSION_TIME,
+ time(NULL) - sta->acct_session_start)) {
+ printf("Could not add Acct-Session-Time\n");
+ goto fail;
+ }
+
+ if (accounting_sta_update_stats(hapd, sta, &data) == 0) {
+ if (!radius_msg_add_attr_int32(msg,
+ RADIUS_ATTR_ACCT_INPUT_PACKETS,
+ data.rx_packets)) {
+ printf("Could not add Acct-Input-Packets\n");
+ goto fail;
+ }
+ if (!radius_msg_add_attr_int32(msg,
+ RADIUS_ATTR_ACCT_OUTPUT_PACKETS,
+ data.tx_packets)) {
+ printf("Could not add Acct-Output-Packets\n");
+ goto fail;
+ }
+ if (!radius_msg_add_attr_int32(msg,
+ RADIUS_ATTR_ACCT_INPUT_OCTETS,
+ data.rx_bytes)) {
+ printf("Could not add Acct-Input-Octets\n");
+ goto fail;
+ }
+ gigawords = sta->acct_input_gigawords;
+#if __WORDSIZE == 64
+ gigawords += data.rx_bytes >> 32;
+#endif
+ if (gigawords &&
+ !radius_msg_add_attr_int32(
+ msg, RADIUS_ATTR_ACCT_INPUT_GIGAWORDS,
+ gigawords)) {
+ printf("Could not add Acct-Input-Gigawords\n");
+ goto fail;
+ }
+ if (!radius_msg_add_attr_int32(msg,
+ RADIUS_ATTR_ACCT_OUTPUT_OCTETS,
+ data.tx_bytes)) {
+ printf("Could not add Acct-Output-Octets\n");
+ goto fail;
+ }
+ gigawords = sta->acct_output_gigawords;
+#if __WORDSIZE == 64
+ gigawords += data.tx_bytes >> 32;
+#endif
+ if (gigawords &&
+ !radius_msg_add_attr_int32(
+ msg, RADIUS_ATTR_ACCT_OUTPUT_GIGAWORDS,
+ gigawords)) {
+ printf("Could not add Acct-Output-Gigawords\n");
+ goto fail;
+ }
+ }
+
+ if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_EVENT_TIMESTAMP,
+ time(NULL))) {
+ printf("Could not add Event-Timestamp\n");
+ goto fail;
+ }
+
+ if (eloop_terminated())
+ cause = RADIUS_ACCT_TERMINATE_CAUSE_ADMIN_REBOOT;
+
+ if (stop && cause &&
+ !radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_TERMINATE_CAUSE,
+ cause)) {
+ printf("Could not add Acct-Terminate-Cause\n");
+ goto fail;
+ }
+
+ radius_client_send(hapd->radius, msg,
+ stop ? RADIUS_ACCT : RADIUS_ACCT_INTERIM,
+ sta->addr);
+ return;
+
+ fail:
+ radius_msg_free(msg);
+ free(msg);
+}
+
+
+void accounting_sta_interim(hostapd *hapd, struct sta_info *sta)
+{
+ if (sta->acct_session_started)
+ accounting_sta_report(hapd, sta, 0);
+}
+
+
+void accounting_sta_stop(hostapd *hapd, struct sta_info *sta)
+{
+ if (sta->acct_session_started) {
+ accounting_sta_report(hapd, sta, 1);
+ eloop_cancel_timeout(accounting_interim_update, hapd, sta);
+ sta->acct_session_started = 0;
+ }
+}
+
+
+void accounting_sta_get_id(struct hostapd_data *hapd, struct sta_info *sta)
+{
+ sta->acct_session_id_lo = hapd->acct_session_id_lo++;
+ if (hapd->acct_session_id_lo == 0) {
+ hapd->acct_session_id_hi++;
+ }
+ sta->acct_session_id_hi = hapd->acct_session_id_hi;
+}
+
+
+/* Process the RADIUS frames from Accounting Server */
+static RadiusRxResult
+accounting_receive(struct radius_msg *msg, struct radius_msg *req,
+ u8 *shared_secret, size_t shared_secret_len, void *data)
+{
+ if (msg->hdr->code != RADIUS_CODE_ACCOUNTING_RESPONSE) {
+ printf("Unknown RADIUS message code\n");
+ return RADIUS_RX_UNKNOWN;
+ }
+
+ if (radius_msg_verify_acct(msg, shared_secret, shared_secret_len, req))
+ {
+ printf("Incoming RADIUS packet did not have correct "
+ "Authenticator - dropped\n");
+ return RADIUS_RX_INVALID_AUTHENTICATOR;
+ }
+
+ return RADIUS_RX_PROCESSED;
+}
+
+
+static void accounting_report_state(struct hostapd_data *hapd, int on)
+{
+ struct radius_msg *msg;
+
+ if (!hapd->conf->acct_server || hapd->radius == NULL)
+ return;
+
+ /* Inform RADIUS server that accounting will start/stop so that the
+ * server can close old accounting sessions. */
+ msg = accounting_msg(hapd, NULL,
+ on ? RADIUS_ACCT_STATUS_TYPE_ACCOUNTING_ON :
+ RADIUS_ACCT_STATUS_TYPE_ACCOUNTING_OFF);
+ if (!msg)
+ return;
+
+ if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_TERMINATE_CAUSE,
+ RADIUS_ACCT_TERMINATE_CAUSE_NAS_REBOOT))
+ {
+ printf("Could not add Acct-Terminate-Cause\n");
+ radius_msg_free(msg);
+ free(msg);
+ return;
+ }
+
+ radius_client_send(hapd->radius, msg, RADIUS_ACCT, NULL);
+}
+
+
+int accounting_init(hostapd *hapd)
+{
+ /* Acct-Session-Id should be unique over reboots. If reliable clock is
+ * not available, this could be replaced with reboot counter, etc. */
+ hapd->acct_session_id_hi = time(NULL);
+
+ if (radius_client_register(hapd->radius, RADIUS_ACCT,
+ accounting_receive, hapd))
+ return -1;
+
+ accounting_report_state(hapd, 1);
+
+ return 0;
+}
+
+
+void accounting_deinit(hostapd *hapd)
+{
+ accounting_report_state(hapd, 0);
+}
diff --git a/contrib/hostapd/accounting.h b/contrib/hostapd/accounting.h
new file mode 100644
index 000000000000..8af3eac59cb5
--- /dev/null
+++ b/contrib/hostapd/accounting.h
@@ -0,0 +1,13 @@
+#ifndef ACCOUNTING_H
+#define ACCOUNTING_H
+
+
+void accounting_sta_start(hostapd *hapd, struct sta_info *sta);
+void accounting_sta_interim(hostapd *hapd, struct sta_info *sta);
+void accounting_sta_stop(hostapd *hapd, struct sta_info *sta);
+void accounting_sta_get_id(struct hostapd_data *hapd, struct sta_info *sta);
+int accounting_init(hostapd *hapd);
+void accounting_deinit(hostapd *hapd);
+
+
+#endif /* ACCOUNTING_H */
diff --git a/contrib/hostapd/aes.c b/contrib/hostapd/aes.c
new file mode 100644
index 000000000000..eabebd074653
--- /dev/null
+++ b/contrib/hostapd/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/hostapd/aes_wrap.c b/contrib/hostapd/aes_wrap.c
new file mode 100644
index 000000000000..dbcc136517e7
--- /dev/null
+++ b/contrib/hostapd/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/hostapd/aes_wrap.h b/contrib/hostapd/aes_wrap.h
new file mode 100644
index 000000000000..70e83ea09d73
--- /dev/null
+++ b/contrib/hostapd/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/hostapd/ap.h b/contrib/hostapd/ap.h
new file mode 100644
index 000000000000..e874ffd2b237
--- /dev/null
+++ b/contrib/hostapd/ap.h
@@ -0,0 +1,99 @@
+#ifndef AP_H
+#define AP_H
+
+/* STA flags */
+#define WLAN_STA_AUTH BIT(0)
+#define WLAN_STA_ASSOC BIT(1)
+#define WLAN_STA_PS BIT(2)
+#define WLAN_STA_TIM BIT(3)
+#define WLAN_STA_PERM BIT(4)
+#define WLAN_STA_AUTHORIZED BIT(5)
+#define WLAN_STA_PENDING_POLL BIT(6) /* pending activity poll not ACKed */
+#define WLAN_STA_PREAUTH BIT(7)
+
+#define WLAN_RATE_1M BIT(0)
+#define WLAN_RATE_2M BIT(1)
+#define WLAN_RATE_5M5 BIT(2)
+#define WLAN_RATE_11M BIT(3)
+#define WLAN_RATE_COUNT 4
+
+/* Maximum size of Supported Rates info element. IEEE 802.11 has a limit of 8,
+ * but some pre-standard IEEE 802.11g products use longer elements. */
+#define WLAN_SUPP_RATES_MAX 32
+
+
+struct sta_info {
+ struct sta_info *next; /* next entry in sta list */
+ struct sta_info *hnext; /* next entry in hash table list */
+ u8 addr[6];
+ u16 aid; /* STA's unique AID (1 .. 2007) or 0 if not yet assigned */
+ u32 flags;
+ u16 capability;
+ u16 listen_interval; /* or beacon_int for APs */
+ u8 supported_rates[WLAN_SUPP_RATES_MAX];
+ u8 tx_supp_rates;
+
+ enum {
+ STA_NULLFUNC = 0, STA_DISASSOC, STA_DEAUTH, STA_REMOVE
+ } timeout_next;
+
+ /* IEEE 802.1X related data */
+ struct eapol_state_machine *eapol_sm;
+
+ /* IEEE 802.11f (IAPP) related data */
+ struct ieee80211_mgmt *last_assoc_req;
+
+ u32 acct_session_id_hi;
+ u32 acct_session_id_lo;
+ time_t acct_session_start;
+ int acct_session_started;
+ int acct_terminate_cause; /* Acct-Terminate-Cause */
+ int acct_interim_interval; /* Acct-Interim-Interval */
+
+ unsigned long last_rx_bytes;
+ unsigned long last_tx_bytes;
+ u32 acct_input_gigawords; /* Acct-Input-Gigawords */
+ u32 acct_output_gigawords; /* Acct-Output-Gigawords */
+
+ u8 *challenge; /* IEEE 802.11 Shared Key Authentication Challenge */
+
+ int pairwise; /* Pairwise cipher suite, WPA_CIPHER_* */
+ u8 *wpa_ie;
+ size_t wpa_ie_len;
+ struct wpa_state_machine *wpa_sm;
+ enum {
+ WPA_VERSION_NO_WPA = 0 /* WPA not used */,
+ WPA_VERSION_WPA = 1 /* WPA / IEEE 802.11i/D3.0 */,
+ WPA_VERSION_WPA2 = 2 /* WPA2 / IEEE 802.11i */
+ } wpa;
+ int wpa_key_mgmt; /* the selected WPA_KEY_MGMT_* */
+ struct rsn_pmksa_cache *pmksa;
+ struct rsn_preauth_interface *preauth_iface;
+ u8 req_replay_counter[8 /* WPA_REPLAY_COUNTER_LEN */];
+ int req_replay_counter_used;
+ u32 dot11RSNAStatsTKIPLocalMICFailures;
+ u32 dot11RSNAStatsTKIPRemoteMICFailures;
+};
+
+
+#define MAX_STA_COUNT 1024
+
+/* Maximum number of AIDs to use for STAs; must be 2007 or lower
+ * (8802.11 limitation) */
+#define MAX_AID_TABLE_SIZE 128
+
+#define STA_HASH_SIZE 256
+#define STA_HASH(sta) (sta[5])
+
+
+/* Default value for maximum station inactivity. After AP_MAX_INACTIVITY has
+ * passed since last received frame from the station, a nullfunc data frame is
+ * sent to the station. If this frame is not acknowledged and no other frames
+ * have been received, the station will be disassociated after
+ * AP_DISASSOC_DELAY seconds. Similarily, the station will be deauthenticated
+ * after AP_DEAUTH_DELAY seconds has passed after disassociation. */
+#define AP_MAX_INACTIVITY (5 * 60)
+#define AP_DISASSOC_DELAY (1)
+#define AP_DEAUTH_DELAY (1)
+
+#endif /* AP_H */
diff --git a/contrib/hostapd/common.c b/contrib/hostapd/common.c
new file mode 100644
index 000000000000..071ffe87c1d1
--- /dev/null
+++ b/contrib/hostapd/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/hostapd/common.h b/contrib/hostapd/common.h
new file mode 100644
index 000000000000..aa6429c76b96
--- /dev/null
+++ b/contrib/hostapd/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/hostapd/config.c b/contrib/hostapd/config.c
new file mode 100644
index 000000000000..ede009aef925
--- /dev/null
+++ b/contrib/hostapd/config.c
@@ -0,0 +1,1136 @@
+/*
+ * Host AP (software wireless LAN access point) user space daemon for
+ * Host AP kernel driver / Configuration file
+ * 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 <unistd.h>
+#include <netinet/in.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <grp.h>
+
+#include "hostapd.h"
+#include "driver.h"
+#include "sha1.h"
+#include "eap.h"
+
+
+static struct hostapd_config *hostapd_config_defaults(void)
+{
+ struct hostapd_config *conf;
+
+ conf = malloc(sizeof(*conf));
+ if (conf == NULL) {
+ printf("Failed to allocate memory for configuration data.\n");
+ return NULL;
+ }
+ memset(conf, 0, sizeof(*conf));
+
+ /* set default driver based on configuration */
+ conf->driver = driver_lookup("default");
+ if (conf->driver == NULL) {
+ printf("No default driver registered!\n");
+ free(conf);
+ return NULL;
+ }
+
+ conf->wep_rekeying_period = 300;
+ conf->eap_reauth_period = 3600;
+
+ conf->logger_syslog_level = HOSTAPD_LEVEL_INFO;
+ conf->logger_stdout_level = HOSTAPD_LEVEL_INFO;
+ conf->logger_syslog = (unsigned int) -1;
+ conf->logger_stdout = (unsigned int) -1;
+
+ conf->auth_algs = HOSTAPD_AUTH_OPEN | HOSTAPD_AUTH_SHARED_KEY;
+
+ conf->wpa_group_rekey = 600;
+ conf->wpa_gmk_rekey = 86400;
+ conf->wpa_key_mgmt = WPA_KEY_MGMT_PSK;
+ conf->wpa_pairwise = WPA_CIPHER_TKIP;
+ conf->wpa_group = WPA_CIPHER_TKIP;
+
+ conf->radius_server_auth_port = 1812;
+
+ return conf;
+}
+
+
+static int mac_comp(const void *a, const void *b)
+{
+ return memcmp(a, b, sizeof(macaddr));
+}
+
+
+static int hostapd_config_read_maclist(const char *fname, macaddr **acl,
+ int *num)
+{
+ FILE *f;
+ char buf[128], *pos;
+ int line = 0;
+ u8 addr[ETH_ALEN];
+ macaddr *newacl;
+
+ if (!fname)
+ return 0;
+
+ f = fopen(fname, "r");
+ if (!f) {
+ printf("MAC list file '%s' not found.\n", fname);
+ return -1;
+ }
+
+ while (fgets(buf, sizeof(buf), f)) {
+ line++;
+
+ if (buf[0] == '#')
+ continue;
+ pos = buf;
+ while (*pos != '\0') {
+ if (*pos == '\n') {
+ *pos = '\0';
+ break;
+ }
+ pos++;
+ }
+ if (buf[0] == '\0')
+ continue;
+
+ if (hwaddr_aton(buf, addr)) {
+ printf("Invalid MAC address '%s' at line %d in '%s'\n",
+ buf, line, fname);
+ fclose(f);
+ return -1;
+ }
+
+ newacl = (macaddr *) realloc(*acl, (*num + 1) * ETH_ALEN);
+ if (newacl == NULL) {
+ printf("MAC list reallocation failed\n");
+ fclose(f);
+ return -1;
+ }
+
+ *acl = newacl;
+ memcpy((*acl)[*num], addr, ETH_ALEN);
+ (*num)++;
+ }
+
+ fclose(f);
+
+ qsort(*acl, *num, sizeof(macaddr), mac_comp);
+
+ return 0;
+}
+
+
+static int hostapd_config_read_wpa_psk(const char *fname,
+ struct hostapd_config *conf)
+{
+ FILE *f;
+ char buf[128], *pos;
+ int line = 0, ret = 0, len, ok;
+ u8 addr[ETH_ALEN];
+ struct hostapd_wpa_psk *psk;
+
+ if (!fname)
+ return 0;
+
+ f = fopen(fname, "r");
+ if (!f) {
+ printf("WPA PSK file '%s' not found.\n", fname);
+ return -1;
+ }
+
+ while (fgets(buf, sizeof(buf), f)) {
+ line++;
+
+ if (buf[0] == '#')
+ continue;
+ pos = buf;
+ while (*pos != '\0') {
+ if (*pos == '\n') {
+ *pos = '\0';
+ break;
+ }
+ pos++;
+ }
+ if (buf[0] == '\0')
+ continue;
+
+ if (hwaddr_aton(buf, addr)) {
+ printf("Invalid MAC address '%s' on line %d in '%s'\n",
+ buf, line, fname);
+ ret = -1;
+ break;
+ }
+
+ psk = malloc(sizeof(*psk));
+ if (psk == NULL) {
+ printf("WPA PSK allocation failed\n");
+ ret = -1;
+ break;
+ }
+ memset(psk, 0, sizeof(*psk));
+ if (memcmp(addr, "\x00\x00\x00\x00\x00\x00", ETH_ALEN) == 0)
+ psk->group = 1;
+ else
+ memcpy(psk->addr, addr, ETH_ALEN);
+
+ pos = buf + 17;
+ if (pos == '\0') {
+ printf("No PSK on line %d in '%s'\n", line, fname);
+ free(psk);
+ ret = -1;
+ break;
+ }
+ pos++;
+
+ ok = 0;
+ len = strlen(pos);
+ if (len == 64 && hexstr2bin(pos, psk->psk, PMK_LEN) == 0)
+ ok = 1;
+ else if (len >= 8 && len < 64) {
+ pbkdf2_sha1(pos, conf->ssid, conf->ssid_len,
+ 4096, psk->psk, PMK_LEN);
+ ok = 1;
+ }
+ if (!ok) {
+ printf("Invalid PSK '%s' on line %d in '%s'\n",
+ pos, line, fname);
+ free(psk);
+ ret = -1;
+ break;
+ }
+
+ psk->next = conf->wpa_psk;
+ conf->wpa_psk = psk;
+ }
+
+ fclose(f);
+
+ return ret;
+}
+
+
+int hostapd_setup_wpa_psk(struct hostapd_config *conf)
+{
+ if (conf->wpa_passphrase != NULL) {
+ if (conf->wpa_psk != NULL) {
+ printf("Warning: both WPA PSK and passphrase set. "
+ "Using passphrase.\n");
+ free(conf->wpa_psk);
+ }
+ conf->wpa_psk = malloc(sizeof(struct hostapd_wpa_psk));
+ if (conf->wpa_psk == NULL) {
+ printf("Unable to alloc space for PSK\n");
+ return -1;
+ }
+ wpa_hexdump_ascii(MSG_DEBUG, "SSID",
+ (u8 *) conf->ssid, conf->ssid_len);
+ wpa_hexdump_ascii(MSG_DEBUG, "PSK (ASCII passphrase)",
+ (u8 *) conf->wpa_passphrase,
+ strlen(conf->wpa_passphrase));
+ memset(conf->wpa_psk, 0, sizeof(struct hostapd_wpa_psk));
+ pbkdf2_sha1(conf->wpa_passphrase,
+ conf->ssid, conf->ssid_len,
+ 4096, conf->wpa_psk->psk, PMK_LEN);
+ wpa_hexdump(MSG_DEBUG, "PSK (from passphrase)",
+ conf->wpa_psk->psk, PMK_LEN);
+ conf->wpa_psk->group = 1;
+
+ memset(conf->wpa_passphrase, 0, strlen(conf->wpa_passphrase));
+ free(conf->wpa_passphrase);
+ conf->wpa_passphrase = 0;
+ }
+
+ if (conf->wpa_psk_file) {
+ if (hostapd_config_read_wpa_psk(conf->wpa_psk_file, conf))
+ return -1;
+ free(conf->wpa_psk_file);
+ conf->wpa_psk_file = NULL;
+ }
+
+ return 0;
+}
+
+
+#ifdef EAP_AUTHENTICATOR
+static int hostapd_config_read_eap_user(const char *fname,
+ struct hostapd_config *conf)
+{
+ FILE *f;
+ char buf[512], *pos, *start;
+ int line = 0, ret = 0, num_methods;
+ struct hostapd_eap_user *user, *tail = NULL;
+
+ if (!fname)
+ return 0;
+
+ f = fopen(fname, "r");
+ if (!f) {
+ printf("EAP user file '%s' not found.\n", fname);
+ return -1;
+ }
+
+ /* Lines: "user" METHOD,METHOD2 "password" (password optional) */
+ while (fgets(buf, sizeof(buf), f)) {
+ line++;
+
+ if (buf[0] == '#')
+ continue;
+ pos = buf;
+ while (*pos != '\0') {
+ if (*pos == '\n') {
+ *pos = '\0';
+ break;
+ }
+ pos++;
+ }
+ if (buf[0] == '\0')
+ continue;
+
+ user = NULL;
+
+ if (buf[0] != '"' && buf[0] != '*') {
+ printf("Invalid EAP identity (no \" in start) on "
+ "line %d in '%s'\n", line, fname);
+ goto failed;
+ }
+
+ user = malloc(sizeof(*user));
+ if (user == NULL) {
+ printf("EAP user allocation failed\n");
+ goto failed;
+ }
+ memset(user, 0, sizeof(*user));
+ user->force_version = -1;
+
+ if (buf[0] == '*') {
+ pos = buf;
+ } else {
+ pos = buf + 1;
+ start = pos;
+ while (*pos != '"' && *pos != '\0')
+ pos++;
+ if (*pos == '\0') {
+ printf("Invalid EAP identity (no \" in end) on"
+ " line %d in '%s'\n", line, fname);
+ goto failed;
+ }
+
+ user->identity = malloc(pos - start);
+ if (user->identity == NULL) {
+ printf("Failed to allocate memory for EAP "
+ "identity\n");
+ goto failed;
+ }
+ memcpy(user->identity, start, pos - start);
+ user->identity_len = pos - start;
+ }
+ pos++;
+ while (*pos == ' ' || *pos == '\t')
+ pos++;
+
+ if (*pos == '\0') {
+ printf("No EAP method on line %d in '%s'\n",
+ line, fname);
+ goto failed;
+ }
+
+ start = pos;
+ while (*pos != ' ' && *pos != '\t' && *pos != '\0')
+ pos++;
+ if (*pos == '\0') {
+ pos = NULL;
+ } else {
+ *pos = '\0';
+ pos++;
+ }
+ num_methods = 0;
+ while (*start) {
+ char *pos2 = strchr(start, ',');
+ if (pos2) {
+ *pos2++ = '\0';
+ }
+ user->methods[num_methods] = eap_get_type(start);
+ if (user->methods[num_methods] == EAP_TYPE_NONE) {
+ printf("Unsupported EAP type '%s' on line %d "
+ "in '%s'\n", start, line, fname);
+ goto failed;
+ }
+
+ num_methods++;
+ if (num_methods >= EAP_USER_MAX_METHODS)
+ break;
+ if (pos2 == NULL)
+ break;
+ start = pos2;
+ }
+ if (num_methods == 0) {
+ printf("No EAP types configured on line %d in '%s'\n",
+ line, fname);
+ goto failed;
+ }
+
+ if (pos == NULL)
+ goto done;
+
+ while (*pos == ' ' || *pos == '\t')
+ pos++;
+ if (*pos == '\0')
+ goto done;
+
+ if (strncmp(pos, "[ver=0]", 7) == 0) {
+ user->force_version = 0;
+ goto done;
+ }
+
+ if (strncmp(pos, "[ver=1]", 7) == 0) {
+ user->force_version = 1;
+ goto done;
+ }
+
+ if (strncmp(pos, "[2]", 3) == 0) {
+ user->phase2 = 1;
+ goto done;
+ }
+
+ if (*pos != '"') {
+ printf("Invalid EAP password (no \" in start) on "
+ "line %d in '%s'\n", line, fname);
+ goto failed;
+ }
+ pos++;
+ start = pos;
+ while (*pos != '"' && *pos != '\0')
+ pos++;
+ if (*pos == '\0') {
+ printf("Invalid EAP password (no \" in end) on "
+ "line %d in '%s'\n", line, fname);
+ goto failed;
+ }
+
+ user->password = malloc(pos - start);
+ if (user->password == NULL) {
+ printf("Failed to allocate memory for EAP password\n");
+ goto failed;
+ }
+ memcpy(user->password, start, pos - start);
+ user->password_len = pos - start;
+
+ pos++;
+ while (*pos == ' ' || *pos == '\t')
+ pos++;
+ if (strncmp(pos, "[2]", 3) == 0) {
+ user->phase2 = 1;
+ }
+
+ done:
+ if (tail == NULL) {
+ tail = conf->eap_user = user;
+ } else {
+ tail->next = user;
+ tail = user;
+ }
+ continue;
+
+ failed:
+ if (user) {
+ free(user->identity);
+ free(user);
+ }
+ ret = -1;
+ break;
+ }
+
+ fclose(f);
+
+ return ret;
+}
+#endif /* EAP_AUTHENTICATOR */
+
+
+static int
+hostapd_config_read_radius_addr(struct hostapd_radius_server **server,
+ int *num_server, const char *val, int def_port,
+ struct hostapd_radius_server **curr_serv)
+{
+ struct hostapd_radius_server *nserv;
+ int ret;
+ static int server_index = 1;
+
+ nserv = realloc(*server, (*num_server + 1) * sizeof(*nserv));
+ if (nserv == NULL)
+ return -1;
+
+ *server = nserv;
+ nserv = &nserv[*num_server];
+ (*num_server)++;
+ (*curr_serv) = nserv;
+
+ memset(nserv, 0, sizeof(*nserv));
+ nserv->port = def_port;
+ ret = !inet_aton(val, &nserv->addr);
+ nserv->index = server_index++;
+
+ return ret;
+}
+
+
+static int hostapd_config_parse_key_mgmt(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, "WPA-PSK") == 0)
+ val |= WPA_KEY_MGMT_PSK;
+ else if (strcmp(start, "WPA-EAP") == 0)
+ val |= WPA_KEY_MGMT_IEEE8021X;
+ else {
+ printf("Line %d: invalid key_mgmt '%s'", line, start);
+ free(buf);
+ return -1;
+ }
+
+ if (last)
+ break;
+ start = end + 1;
+ }
+
+ free(buf);
+ if (val == 0) {
+ printf("Line %d: no key_mgmt values configured.", line);
+ return -1;
+ }
+
+ return val;
+}
+
+
+static int hostapd_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 {
+ printf("Line %d: invalid cipher '%s'.", line, start);
+ free(buf);
+ return -1;
+ }
+
+ if (last)
+ break;
+ start = end + 1;
+ }
+ free(buf);
+
+ if (val == 0) {
+ printf("Line %d: no cipher values configured.", line);
+ return -1;
+ }
+ return val;
+}
+
+
+static int hostapd_config_check(struct hostapd_config *conf)
+{
+ if (conf->ieee802_1x && !conf->eap_authenticator &&
+ !conf->auth_servers) {
+ printf("Invalid IEEE 802.1X configuration (no EAP "
+ "authenticator configured).\n");
+ return -1;
+ }
+
+ if (conf->wpa && (conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK) &&
+ conf->wpa_psk == NULL && conf->wpa_passphrase == NULL) {
+ printf("WPA-PSK enabled, but PSK or passphrase is not "
+ "configured.\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+struct hostapd_config * hostapd_config_read(const char *fname)
+{
+ struct hostapd_config *conf;
+ FILE *f;
+ char buf[256], *pos;
+ int line = 0;
+ int errors = 0;
+ char *accept_mac_file = NULL, *deny_mac_file = NULL;
+#ifdef EAP_AUTHENTICATOR
+ char *eap_user_file = NULL;
+#endif /* EAP_AUTHENTICATOR */
+
+ f = fopen(fname, "r");
+ if (f == NULL) {
+ printf("Could not open configuration file '%s' for reading.\n",
+ fname);
+ return NULL;
+ }
+
+ conf = hostapd_config_defaults();
+ if (conf == NULL) {
+ fclose(f);
+ return NULL;
+ }
+
+ while (fgets(buf, sizeof(buf), f)) {
+ line++;
+
+ if (buf[0] == '#')
+ continue;
+ pos = buf;
+ while (*pos != '\0') {
+ if (*pos == '\n') {
+ *pos = '\0';
+ break;
+ }
+ pos++;
+ }
+ if (buf[0] == '\0')
+ continue;
+
+ pos = strchr(buf, '=');
+ if (pos == NULL) {
+ printf("Line %d: invalid line '%s'\n", line, buf);
+ errors++;
+ continue;
+ }
+ *pos = '\0';
+ pos++;
+
+ if (strcmp(buf, "interface") == 0) {
+ snprintf(conf->iface, sizeof(conf->iface), "%s", pos);
+ } else if (strcmp(buf, "bridge") == 0) {
+ snprintf(conf->bridge, sizeof(conf->bridge), "%s",
+ pos);
+ } else if (strcmp(buf, "driver") == 0) {
+ conf->driver = driver_lookup(pos);
+ if (conf->driver == NULL) {
+ printf("Line %d: invalid/unknown driver "
+ "'%s'\n", line, pos);
+ errors++;
+ }
+ } else if (strcmp(buf, "debug") == 0) {
+ conf->debug = atoi(pos);
+ } else if (strcmp(buf, "logger_syslog_level") == 0) {
+ conf->logger_syslog_level = atoi(pos);
+ } else if (strcmp(buf, "logger_stdout_level") == 0) {
+ conf->logger_stdout_level = atoi(pos);
+ } else if (strcmp(buf, "logger_syslog") == 0) {
+ conf->logger_syslog = atoi(pos);
+ } else if (strcmp(buf, "logger_stdout") == 0) {
+ conf->logger_stdout = atoi(pos);
+ } else if (strcmp(buf, "dump_file") == 0) {
+ conf->dump_log_name = strdup(pos);
+ } else if (strcmp(buf, "ssid") == 0) {
+ conf->ssid_len = strlen(pos);
+ if (conf->ssid_len >= HOSTAPD_SSID_LEN ||
+ conf->ssid_len < 1) {
+ printf("Line %d: invalid SSID '%s'\n", line,
+ pos);
+ errors++;
+ }
+ memcpy(conf->ssid, pos, conf->ssid_len);
+ conf->ssid[conf->ssid_len] = '\0';
+ conf->ssid_set = 1;
+ } else if (strcmp(buf, "macaddr_acl") == 0) {
+ conf->macaddr_acl = atoi(pos);
+ if (conf->macaddr_acl != ACCEPT_UNLESS_DENIED &&
+ conf->macaddr_acl != DENY_UNLESS_ACCEPTED &&
+ conf->macaddr_acl != USE_EXTERNAL_RADIUS_AUTH) {
+ printf("Line %d: unknown macaddr_acl %d\n",
+ line, conf->macaddr_acl);
+ }
+ } else if (strcmp(buf, "accept_mac_file") == 0) {
+ accept_mac_file = strdup(pos);
+ if (!accept_mac_file) {
+ printf("Line %d: allocation failed\n", line);
+ errors++;
+ }
+ } else if (strcmp(buf, "deny_mac_file") == 0) {
+ deny_mac_file = strdup(pos);
+ if (!deny_mac_file) {
+ printf("Line %d: allocation failed\n", line);
+ errors++;
+ }
+ } else if (strcmp(buf, "assoc_ap_addr") == 0) {
+ if (hwaddr_aton(pos, conf->assoc_ap_addr)) {
+ printf("Line %d: invalid MAC address '%s'\n",
+ line, pos);
+ errors++;
+ }
+ conf->assoc_ap = 1;
+ } else if (strcmp(buf, "ieee8021x") == 0) {
+ conf->ieee802_1x = atoi(pos);
+#ifdef EAP_AUTHENTICATOR
+ } else if (strcmp(buf, "eap_authenticator") == 0) {
+ conf->eap_authenticator = atoi(pos);
+ } else if (strcmp(buf, "eap_user_file") == 0) {
+ free(eap_user_file);
+ eap_user_file = strdup(pos);
+ if (!eap_user_file) {
+ printf("Line %d: allocation failed\n", line);
+ errors++;
+ }
+ } else if (strcmp(buf, "ca_cert") == 0) {
+ free(conf->ca_cert);
+ conf->ca_cert = strdup(pos);
+ } else if (strcmp(buf, "server_cert") == 0) {
+ free(conf->server_cert);
+ conf->server_cert = strdup(pos);
+ } else if (strcmp(buf, "private_key") == 0) {
+ free(conf->private_key);
+ conf->private_key = strdup(pos);
+ } else if (strcmp(buf, "private_key_passwd") == 0) {
+ free(conf->private_key_passwd);
+ conf->private_key_passwd = strdup(pos);
+#ifdef EAP_SIM
+ } else if (strcmp(buf, "eap_sim_db") == 0) {
+ free(conf->eap_sim_db);
+ conf->eap_sim_db = strdup(pos);
+#endif /* EAP_SIM */
+#endif /* EAP_AUTHENTICATOR */
+ } else if (strcmp(buf, "eap_message") == 0) {
+ conf->eap_req_id_text = strdup(pos);
+ } else if (strcmp(buf, "wep_key_len_broadcast") == 0) {
+ conf->default_wep_key_len = atoi(pos);
+ if (conf->default_wep_key_len > 13) {
+ printf("Line %d: invalid WEP key len %lu "
+ "(= %lu bits)\n", line,
+ (unsigned long)
+ conf->default_wep_key_len,
+ (unsigned long)
+ conf->default_wep_key_len * 8);
+ errors++;
+ }
+ } else if (strcmp(buf, "wep_key_len_unicast") == 0) {
+ conf->individual_wep_key_len = atoi(pos);
+ if (conf->individual_wep_key_len < 0 ||
+ conf->individual_wep_key_len > 13) {
+ printf("Line %d: invalid WEP key len %d "
+ "(= %d bits)\n", line,
+ conf->individual_wep_key_len,
+ conf->individual_wep_key_len * 8);
+ errors++;
+ }
+ } else if (strcmp(buf, "wep_rekey_period") == 0) {
+ conf->wep_rekeying_period = atoi(pos);
+ if (conf->wep_rekeying_period < 0) {
+ printf("Line %d: invalid period %d\n",
+ line, conf->wep_rekeying_period);
+ errors++;
+ }
+ } else if (strcmp(buf, "eap_reauth_period") == 0) {
+ conf->eap_reauth_period = atoi(pos);
+ if (conf->eap_reauth_period < 0) {
+ printf("Line %d: invalid period %d\n",
+ line, conf->eap_reauth_period);
+ errors++;
+ }
+ } else if (strcmp(buf, "eapol_key_index_workaround") == 0) {
+ conf->eapol_key_index_workaround = atoi(pos);
+#ifdef CONFIG_IAPP
+ } else if (strcmp(buf, "iapp_interface") == 0) {
+ conf->ieee802_11f = 1;
+ snprintf(conf->iapp_iface, sizeof(conf->iapp_iface),
+ "%s", pos);
+#endif /* CONFIG_IAPP */
+ } else if (strcmp(buf, "own_ip_addr") == 0) {
+ if (!inet_aton(pos, &conf->own_ip_addr)) {
+ printf("Line %d: invalid IP address '%s'\n",
+ line, pos);
+ errors++;
+ }
+ } else if (strcmp(buf, "nas_identifier") == 0) {
+ conf->nas_identifier = strdup(pos);
+ } else if (strcmp(buf, "auth_server_addr") == 0) {
+ if (hostapd_config_read_radius_addr(
+ &conf->auth_servers,
+ &conf->num_auth_servers, pos, 1812,
+ &conf->auth_server)) {
+ printf("Line %d: invalid IP address '%s'\n",
+ line, pos);
+ errors++;
+ }
+ } else if (conf->auth_server &&
+ strcmp(buf, "auth_server_port") == 0) {
+ conf->auth_server->port = atoi(pos);
+ } else if (conf->auth_server &&
+ strcmp(buf, "auth_server_shared_secret") == 0) {
+ int len = strlen(pos);
+ if (len == 0) {
+ /* RFC 2865, Ch. 3 */
+ printf("Line %d: empty shared secret is not "
+ "allowed.\n", line);
+ errors++;
+ }
+ conf->auth_server->shared_secret = (u8 *) strdup(pos);
+ conf->auth_server->shared_secret_len = len;
+ } else if (strcmp(buf, "acct_server_addr") == 0) {
+ if (hostapd_config_read_radius_addr(
+ &conf->acct_servers,
+ &conf->num_acct_servers, pos, 1813,
+ &conf->acct_server)) {
+ printf("Line %d: invalid IP address '%s'\n",
+ line, pos);
+ errors++;
+ }
+ } else if (conf->acct_server &&
+ strcmp(buf, "acct_server_port") == 0) {
+ conf->acct_server->port = atoi(pos);
+ } else if (conf->acct_server &&
+ strcmp(buf, "acct_server_shared_secret") == 0) {
+ int len = strlen(pos);
+ if (len == 0) {
+ /* RFC 2865, Ch. 3 */
+ printf("Line %d: empty shared secret is not "
+ "allowed.\n", line);
+ errors++;
+ }
+ conf->acct_server->shared_secret = (u8 *) strdup(pos);
+ conf->acct_server->shared_secret_len = len;
+ } else if (strcmp(buf, "radius_retry_primary_interval") == 0) {
+ conf->radius_retry_primary_interval = atoi(pos);
+ } else if (strcmp(buf, "radius_acct_interim_interval") == 0) {
+ conf->radius_acct_interim_interval = atoi(pos);
+ } else if (strcmp(buf, "auth_algs") == 0) {
+ conf->auth_algs = atoi(pos);
+ if (conf->auth_algs == 0) {
+ printf("Line %d: no authentication algorithms "
+ "allowed\n",
+ line);
+ errors++;
+ }
+ } else if (strcmp(buf, "wpa") == 0) {
+ conf->wpa = atoi(pos);
+ } else if (strcmp(buf, "wpa_group_rekey") == 0) {
+ conf->wpa_group_rekey = atoi(pos);
+ } else if (strcmp(buf, "wpa_strict_rekey") == 0) {
+ conf->wpa_strict_rekey = atoi(pos);
+ } else if (strcmp(buf, "wpa_gmk_rekey") == 0) {
+ conf->wpa_gmk_rekey = atoi(pos);
+ } else if (strcmp(buf, "wpa_passphrase") == 0) {
+ int len = strlen(pos);
+ if (len < 8 || len > 63) {
+ printf("Line %d: invalid WPA passphrase length"
+ " %d (expected 8..63)\n", line, len);
+ errors++;
+ } else {
+ free(conf->wpa_passphrase);
+ conf->wpa_passphrase = strdup(pos);
+ }
+ } else if (strcmp(buf, "wpa_psk") == 0) {
+ free(conf->wpa_psk);
+ conf->wpa_psk = malloc(sizeof(struct hostapd_wpa_psk));
+ if (conf->wpa_psk) {
+ memset(conf->wpa_psk, 0,
+ sizeof(struct hostapd_wpa_psk));
+ }
+ if (conf->wpa_psk == NULL)
+ errors++;
+ else if (hexstr2bin(pos, conf->wpa_psk->psk, PMK_LEN)
+ || pos[PMK_LEN * 2] != '\0') {
+ printf("Line %d: Invalid PSK '%s'.\n", line,
+ pos);
+ errors++;
+ } else {
+ conf->wpa_psk->group = 1;
+ }
+ } else if (strcmp(buf, "wpa_psk_file") == 0) {
+ free(conf->wpa_psk_file);
+ conf->wpa_psk_file = strdup(pos);
+ if (!conf->wpa_psk_file) {
+ printf("Line %d: allocation failed\n", line);
+ errors++;
+ }
+ } else if (strcmp(buf, "wpa_key_mgmt") == 0) {
+ conf->wpa_key_mgmt =
+ hostapd_config_parse_key_mgmt(line, pos);
+ if (conf->wpa_key_mgmt == -1)
+ errors++;
+ } else if (strcmp(buf, "wpa_pairwise") == 0) {
+ conf->wpa_pairwise =
+ hostapd_config_parse_cipher(line, pos);
+ if (conf->wpa_pairwise == -1 ||
+ conf->wpa_pairwise == 0)
+ errors++;
+ else if (conf->wpa_pairwise &
+ (WPA_CIPHER_NONE | WPA_CIPHER_WEP40 |
+ WPA_CIPHER_WEP104)) {
+ printf("Line %d: unsupported pairwise "
+ "cipher suite '%s'\n",
+ conf->wpa_pairwise, pos);
+ errors++;
+ } else {
+ if (conf->wpa_pairwise & WPA_CIPHER_TKIP)
+ conf->wpa_group = WPA_CIPHER_TKIP;
+ else
+ conf->wpa_group = WPA_CIPHER_CCMP;
+ }
+#ifdef CONFIG_RSN_PREAUTH
+ } else if (strcmp(buf, "rsn_preauth") == 0) {
+ conf->rsn_preauth = atoi(pos);
+ } else if (strcmp(buf, "rsn_preauth_interfaces") == 0) {
+ conf->rsn_preauth_interfaces = strdup(pos);
+#endif /* CONFIG_RSN_PREAUTH */
+ } else if (strcmp(buf, "ctrl_interface") == 0) {
+ free(conf->ctrl_interface);
+ conf->ctrl_interface = strdup(pos);
+ } else if (strcmp(buf, "ctrl_interface_group") == 0) {
+ struct group *grp;
+ char *endp;
+ const char *group = pos;
+
+ grp = getgrnam(group);
+ if (grp) {
+ conf->ctrl_interface_gid = grp->gr_gid;
+ wpa_printf(MSG_DEBUG, "ctrl_interface_group=%d"
+ " (from group name '%s')",
+ conf->ctrl_interface_gid, group);
+ continue;
+ }
+
+ /* Group name not found - try to parse this as gid */
+ conf->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",
+ conf->ctrl_interface_gid);
+#ifdef RADIUS_SERVER
+ } else if (strcmp(buf, "radius_server_clients") == 0) {
+ free(conf->radius_server_clients);
+ conf->radius_server_clients = strdup(pos);
+ } else if (strcmp(buf, "radius_server_auth_port") == 0) {
+ conf->radius_server_auth_port = atoi(pos);
+#endif /* RADIUS_SERVER */
+ } else {
+ printf("Line %d: unknown configuration item '%s'\n",
+ line, buf);
+ errors++;
+ }
+ }
+
+ fclose(f);
+
+ if (hostapd_config_read_maclist(accept_mac_file, &conf->accept_mac,
+ &conf->num_accept_mac))
+ errors++;
+ free(accept_mac_file);
+ if (hostapd_config_read_maclist(deny_mac_file, &conf->deny_mac,
+ &conf->num_deny_mac))
+ errors++;
+ free(deny_mac_file);
+
+#ifdef EAP_AUTHENTICATOR
+ if (hostapd_config_read_eap_user(eap_user_file, conf))
+ errors++;
+ free(eap_user_file);
+#endif /* EAP_AUTHENTICATOR */
+
+ conf->auth_server = conf->auth_servers;
+ conf->acct_server = conf->acct_servers;
+
+ if (hostapd_config_check(conf))
+ errors++;
+
+ if (errors) {
+ printf("%d errors found in configuration file '%s'\n",
+ errors, fname);
+ hostapd_config_free(conf);
+ conf = NULL;
+ }
+
+ return conf;
+}
+
+
+static void hostapd_config_free_radius(struct hostapd_radius_server *servers,
+ int num_servers)
+{
+ int i;
+
+ for (i = 0; i < num_servers; i++) {
+ free(servers[i].shared_secret);
+ }
+ free(servers);
+}
+
+
+static void hostapd_config_free_eap_user(struct hostapd_eap_user *user)
+{
+ free(user->identity);
+ free(user->password);
+ free(user);
+}
+
+
+void hostapd_config_free(struct hostapd_config *conf)
+{
+ struct hostapd_wpa_psk *psk, *prev;
+ struct hostapd_eap_user *user, *prev_user;
+
+ if (conf == NULL)
+ return;
+
+ psk = conf->wpa_psk;
+ while (psk) {
+ prev = psk;
+ psk = psk->next;
+ free(prev);
+ }
+
+ free(conf->wpa_passphrase);
+ free(conf->wpa_psk_file);
+
+ user = conf->eap_user;
+ while (user) {
+ prev_user = user;
+ user = user->next;
+ hostapd_config_free_eap_user(prev_user);
+ }
+
+ free(conf->dump_log_name);
+ free(conf->eap_req_id_text);
+ free(conf->accept_mac);
+ free(conf->deny_mac);
+ free(conf->nas_identifier);
+ hostapd_config_free_radius(conf->auth_servers, conf->num_auth_servers);
+ hostapd_config_free_radius(conf->acct_servers, conf->num_acct_servers);
+ free(conf->rsn_preauth_interfaces);
+ free(conf->ctrl_interface);
+ free(conf->ca_cert);
+ free(conf->server_cert);
+ free(conf->private_key);
+ free(conf->private_key_passwd);
+ free(conf->eap_sim_db);
+ free(conf->radius_server_clients);
+ free(conf);
+}
+
+
+/* Perform a binary search for given MAC address from a pre-sorted list.
+ * Returns 1 if address is in the list or 0 if not. */
+int hostapd_maclist_found(macaddr *list, int num_entries, u8 *addr)
+{
+ int start, end, middle, res;
+
+ start = 0;
+ end = num_entries - 1;
+
+ while (start <= end) {
+ middle = (start + end) / 2;
+ res = memcmp(list[middle], addr, ETH_ALEN);
+ if (res == 0)
+ return 1;
+ if (res < 0)
+ start = middle + 1;
+ else
+ end = middle - 1;
+ }
+
+ return 0;
+}
+
+
+const u8 * hostapd_get_psk(const struct hostapd_config *conf, const u8 *addr,
+ const u8 *prev_psk)
+{
+ struct hostapd_wpa_psk *psk;
+ int next_ok = prev_psk == NULL;
+
+ for (psk = conf->wpa_psk; psk != NULL; psk = psk->next) {
+ if (next_ok &&
+ (psk->group || memcmp(psk->addr, addr, ETH_ALEN) == 0))
+ return psk->psk;
+
+ if (psk->psk == prev_psk)
+ next_ok = 1;
+ }
+
+ return NULL;
+}
+
+
+const struct hostapd_eap_user *
+hostapd_get_eap_user(const struct hostapd_config *conf, const u8 *identity,
+ size_t identity_len, int phase2)
+{
+ struct hostapd_eap_user *user = conf->eap_user;
+
+ while (user) {
+ if (!phase2 && user->identity == NULL) {
+ /* Wildcard match */
+ break;
+ }
+ if (user->phase2 == !!phase2 &&
+ user->identity_len == identity_len &&
+ memcmp(user->identity, identity, identity_len) == 0)
+ break;
+ user = user->next;
+ }
+
+ return user;
+}
diff --git a/contrib/hostapd/config.h b/contrib/hostapd/config.h
new file mode 100644
index 000000000000..0ffc3adb4366
--- /dev/null
+++ b/contrib/hostapd/config.h
@@ -0,0 +1,177 @@
+#ifndef CONFIG_H
+#define CONFIG_H
+
+typedef u8 macaddr[ETH_ALEN];
+
+struct hostapd_radius_server {
+ /* MIB prefix for shared variables:
+ * @ = radiusAuth or radiusAcc depending on the type of the server */
+ struct in_addr addr; /* @ServerAddress */
+ int port; /* @ClientServerPortNumber */
+ u8 *shared_secret;
+ size_t shared_secret_len;
+
+ /* Dynamic (not from configuration file) MIB data */
+ int index; /* @ServerIndex */
+ int round_trip_time; /* @ClientRoundTripTime; in hundredths of a
+ * second */
+ u32 requests; /* @Client{Access,}Requests */
+ u32 retransmissions; /* @Client{Access,}Retransmissions */
+ u32 access_accepts; /* radiusAuthClientAccessAccepts */
+ u32 access_rejects; /* radiusAuthClientAccessRejects */
+ u32 access_challenges; /* radiusAuthClientAccessChallenges */
+ u32 responses; /* radiusAccClientResponses */
+ u32 malformed_responses; /* @ClientMalformed{Access,}Responses */
+ u32 bad_authenticators; /* @ClientBadAuthenticators */
+ u32 timeouts; /* @ClientTimeouts */
+ u32 unknown_types; /* @ClientUnknownTypes */
+ u32 packets_dropped; /* @ClientPacketsDropped */
+ /* @ClientPendingRequests: length of hapd->radius->msgs for matching
+ * msg_type */
+};
+
+#define PMK_LEN 32
+struct hostapd_wpa_psk {
+ struct hostapd_wpa_psk *next;
+ int group;
+ u8 psk[PMK_LEN];
+ u8 addr[ETH_ALEN];
+};
+
+#define EAP_USER_MAX_METHODS 8
+struct hostapd_eap_user {
+ struct hostapd_eap_user *next;
+ u8 *identity;
+ size_t identity_len;
+ u8 methods[EAP_USER_MAX_METHODS];
+ u8 *password;
+ size_t password_len;
+ int phase2;
+ int force_version;
+};
+
+struct hostapd_config {
+ char iface[IFNAMSIZ + 1];
+ char bridge[IFNAMSIZ + 1];
+
+ const struct driver_ops *driver;
+
+ enum {
+ HOSTAPD_LEVEL_DEBUG_VERBOSE = 0,
+ HOSTAPD_LEVEL_DEBUG = 1,
+ HOSTAPD_LEVEL_INFO = 2,
+ HOSTAPD_LEVEL_NOTICE = 3,
+ HOSTAPD_LEVEL_WARNING = 4
+ } logger_syslog_level, logger_stdout_level;
+
+#define HOSTAPD_MODULE_IEEE80211 BIT(0)
+#define HOSTAPD_MODULE_IEEE8021X BIT(1)
+#define HOSTAPD_MODULE_RADIUS BIT(2)
+#define HOSTAPD_MODULE_WPA BIT(3)
+#define HOSTAPD_MODULE_DRIVER BIT(4)
+#define HOSTAPD_MODULE_IAPP BIT(5)
+ unsigned int logger_syslog; /* module bitfield */
+ unsigned int logger_stdout; /* module bitfield */
+
+ enum { HOSTAPD_DEBUG_NO = 0, HOSTAPD_DEBUG_MINIMAL = 1,
+ HOSTAPD_DEBUG_VERBOSE = 2,
+ HOSTAPD_DEBUG_MSGDUMPS = 3,
+ HOSTAPD_DEBUG_EXCESSIVE = 4 } debug; /* debug verbosity level */
+ char *dump_log_name; /* file name for state dump (SIGUSR1) */
+
+ int ieee802_1x; /* use IEEE 802.1X */
+ int eap_authenticator; /* Use internal EAP authenticator instead of
+ * external RADIUS server */
+ struct hostapd_eap_user *eap_user;
+ char *eap_sim_db;
+ struct in_addr own_ip_addr;
+ char *nas_identifier;
+ /* 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;
+#define HOSTAPD_SSID_LEN 32
+ char ssid[HOSTAPD_SSID_LEN + 1];
+ size_t ssid_len;
+ int ssid_set;
+ char *eap_req_id_text; /* optional displayable message sent with
+ * EAP Request-Identity */
+ int eapol_key_index_workaround;
+
+ size_t default_wep_key_len;
+ int individual_wep_key_len;
+ int wep_rekeying_period;
+ int eap_reauth_period;
+
+ int ieee802_11f; /* use IEEE 802.11f (IAPP) */
+ char iapp_iface[IFNAMSIZ + 1]; /* interface used with IAPP broadcast
+ * frames */
+
+ u8 assoc_ap_addr[ETH_ALEN];
+ int assoc_ap; /* whether assoc_ap_addr is set */
+
+ enum {
+ ACCEPT_UNLESS_DENIED = 0,
+ DENY_UNLESS_ACCEPTED = 1,
+ USE_EXTERNAL_RADIUS_AUTH = 2
+ } macaddr_acl;
+ macaddr *accept_mac;
+ int num_accept_mac;
+ macaddr *deny_mac;
+ int num_deny_mac;
+
+#define HOSTAPD_AUTH_OPEN BIT(0)
+#define HOSTAPD_AUTH_SHARED_KEY BIT(1)
+ int auth_algs; /* bitfield of allowed IEEE 802.11 authentication
+ * algorithms */
+
+#define HOSTAPD_WPA_VERSION_WPA BIT(0)
+#define HOSTAPD_WPA_VERSION_WPA2 BIT(1)
+ int wpa;
+ struct hostapd_wpa_psk *wpa_psk;
+ char *wpa_passphrase;
+ char *wpa_psk_file;
+#define WPA_KEY_MGMT_IEEE8021X BIT(0)
+#define WPA_KEY_MGMT_PSK BIT(1)
+ int wpa_key_mgmt;
+#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)
+ int wpa_pairwise;
+ int wpa_group;
+ int wpa_group_rekey;
+ int wpa_strict_rekey;
+ int wpa_gmk_rekey;
+ int rsn_preauth;
+ char *rsn_preauth_interfaces;
+
+ char *ctrl_interface; /* directory for UNIX domain sockets */
+ gid_t ctrl_interface_gid;
+
+ char *ca_cert;
+ char *server_cert;
+ char *private_key;
+ char *private_key_passwd;
+
+ char *radius_server_clients;
+ int radius_server_auth_port;
+};
+
+
+struct hostapd_config * hostapd_config_read(const char *fname);
+void hostapd_config_free(struct hostapd_config *conf);
+int hostapd_maclist_found(macaddr *list, int num_entries, u8 *addr);
+const u8 * hostapd_get_psk(const struct hostapd_config *conf, const u8 *addr,
+ const u8 *prev_psk);
+int hostapd_setup_wpa_psk(struct hostapd_config *conf);
+const struct hostapd_eap_user *
+hostapd_get_eap_user(const struct hostapd_config *conf, const u8 *identity,
+ size_t identity_len, int phase2);
+
+#endif /* CONFIG_H */
diff --git a/contrib/hostapd/crypto.c b/contrib/hostapd/crypto.c
new file mode 100644
index 000000000000..cd278e0fae59
--- /dev/null
+++ b/contrib/hostapd/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/hostapd/crypto.h b/contrib/hostapd/crypto.h
new file mode 100644
index 000000000000..3e1a0e5cd3f8
--- /dev/null
+++ b/contrib/hostapd/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/hostapd/ctrl_iface.c b/contrib/hostapd/ctrl_iface.c
new file mode 100644
index 000000000000..8cf356cb460f
--- /dev/null
+++ b/contrib/hostapd/ctrl_iface.c
@@ -0,0 +1,452 @@
+/*
+ * Host AP (software wireless LAN access point) user space daemon for
+ * Host AP kernel driver / UNIX domain socket -based control interface
+ * 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>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <netinet/in.h>
+
+#include "hostapd.h"
+#include "eloop.h"
+#include "config.h"
+#include "eapol_sm.h"
+#include "ieee802_1x.h"
+#include "wpa.h"
+#include "radius_client.h"
+#include "ieee802_11.h"
+#include "ctrl_iface.h"
+#include "sta_info.h"
+
+
+struct wpa_ctrl_dst {
+ struct wpa_ctrl_dst *next;
+ struct sockaddr_un addr;
+ socklen_t addrlen;
+ int debug_level;
+ int errors;
+};
+
+
+static int hostapd_ctrl_iface_attach(struct hostapd_data *hapd,
+ struct sockaddr_un *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(struct sockaddr_un));
+ dst->addrlen = fromlen;
+ dst->debug_level = MSG_INFO;
+ dst->next = hapd->ctrl_dst;
+ hapd->ctrl_dst = dst;
+ wpa_hexdump(MSG_DEBUG, "CTRL_IFACE monitor attached",
+ (u8 *) from->sun_path, fromlen);
+ return 0;
+}
+
+
+static int hostapd_ctrl_iface_detach(struct hostapd_data *hapd,
+ struct sockaddr_un *from,
+ socklen_t fromlen)
+{
+ struct wpa_ctrl_dst *dst, *prev = NULL;
+
+ dst = hapd->ctrl_dst;
+ while (dst) {
+ if (fromlen == dst->addrlen &&
+ memcmp(from->sun_path, dst->addr.sun_path, fromlen) == 0) {
+ if (prev == NULL)
+ hapd->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;
+ }
+ prev = dst;
+ dst = dst->next;
+ }
+ return -1;
+}
+
+
+static int hostapd_ctrl_iface_level(struct hostapd_data *hapd,
+ struct sockaddr_un *from,
+ socklen_t fromlen,
+ char *level)
+{
+ struct wpa_ctrl_dst *dst;
+
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE LEVEL %s", level);
+
+ dst = hapd->ctrl_dst;
+ while (dst) {
+ if (fromlen == dst->addrlen &&
+ 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;
+ }
+ dst = dst->next;
+ }
+
+ return -1;
+}
+
+
+static int hostapd_ctrl_iface_sta_mib(struct hostapd_data *hapd,
+ struct sta_info *sta,
+ char *buf, size_t buflen)
+{
+ int len, res;
+
+ if (sta == NULL) {
+ return snprintf(buf, buflen, "FAIL\n");
+ }
+
+ len = 0;
+ len += snprintf(buf + len, buflen - len, MACSTR "\n",
+ MAC2STR(sta->addr));
+
+ res = ieee802_11_get_mib_sta(hapd, sta, buf + len, buflen - len);
+ if (res >= 0)
+ len += res;
+ res = wpa_get_mib_sta(hapd, sta, buf + len, buflen - len);
+ if (res >= 0)
+ len += res;
+ res = ieee802_1x_get_mib_sta(hapd, sta, buf + len, buflen - len);
+ if (res >= 0)
+ len += res;
+
+ return len;
+}
+
+
+static int hostapd_ctrl_iface_sta_first(struct hostapd_data *hapd,
+ char *buf, size_t buflen)
+{
+ return hostapd_ctrl_iface_sta_mib(hapd, hapd->sta_list, buf, buflen);
+}
+
+
+static int hostapd_ctrl_iface_sta(struct hostapd_data *hapd,
+ const char *txtaddr,
+ char *buf, size_t buflen)
+{
+ u8 addr[ETH_ALEN];
+
+ if (hwaddr_aton(txtaddr, addr))
+ return snprintf(buf, buflen, "FAIL\n");
+ return hostapd_ctrl_iface_sta_mib(hapd, ap_get_sta(hapd, addr),
+ buf, buflen);
+}
+
+
+static int hostapd_ctrl_iface_sta_next(struct hostapd_data *hapd,
+ const char *txtaddr,
+ char *buf, size_t buflen)
+{
+ u8 addr[ETH_ALEN];
+ struct sta_info *sta;
+
+ if (hwaddr_aton(txtaddr, addr) ||
+ (sta = ap_get_sta(hapd, addr)) == NULL)
+ return snprintf(buf, buflen, "FAIL\n");
+ return hostapd_ctrl_iface_sta_mib(hapd, sta->next, buf, buflen);
+}
+
+
+static void hostapd_ctrl_iface_receive(int sock, void *eloop_ctx,
+ void *sock_ctx)
+{
+ struct hostapd_data *hapd = eloop_ctx;
+ char buf[256];
+ int res;
+ struct sockaddr_un from;
+ socklen_t fromlen = sizeof(from);
+ char *reply;
+ const int reply_size = 4096;
+ int reply_len;
+
+ res = recvfrom(sock, buf, sizeof(buf) - 1, 0,
+ (struct sockaddr *) &from, &fromlen);
+ if (res < 0) {
+ perror("recvfrom(ctrl_iface)");
+ return;
+ }
+ buf[res] = '\0';
+ wpa_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 = ieee802_11_get_mib(hapd, reply, reply_size);
+ if (reply_len >= 0) {
+ res = wpa_get_mib(hapd, reply + reply_len,
+ reply_size - reply_len);
+ if (res < 0)
+ reply_len = -1;
+ else
+ reply_len += res;
+ }
+ if (reply_len >= 0) {
+ res = ieee802_1x_get_mib(hapd, reply + reply_len,
+ reply_size - reply_len);
+ if (res < 0)
+ reply_len = -1;
+ else
+ reply_len += res;
+ }
+ if (reply_len >= 0) {
+ res = radius_client_get_mib(hapd->radius,
+ reply + reply_len,
+ reply_size - reply_len);
+ if (res < 0)
+ reply_len = -1;
+ else
+ reply_len += res;
+ }
+ } else if (strcmp(buf, "STA-FIRST") == 0) {
+ reply_len = hostapd_ctrl_iface_sta_first(hapd, reply,
+ reply_size);
+ } else if (strncmp(buf, "STA ", 4) == 0) {
+ reply_len = hostapd_ctrl_iface_sta(hapd, buf + 4, reply,
+ reply_size);
+ } else if (strncmp(buf, "STA-NEXT ", 9) == 0) {
+ reply_len = hostapd_ctrl_iface_sta_next(hapd, buf + 9, reply,
+ reply_size);
+ } else if (strcmp(buf, "ATTACH") == 0) {
+ if (hostapd_ctrl_iface_attach(hapd, &from, fromlen))
+ reply_len = -1;
+ } else if (strcmp(buf, "DETACH") == 0) {
+ if (hostapd_ctrl_iface_detach(hapd, &from, fromlen))
+ reply_len = -1;
+ } else if (strncmp(buf, "LEVEL ", 6) == 0) {
+ if (hostapd_ctrl_iface_level(hapd, &from, fromlen,
+ buf + 6))
+ reply_len = -1;
+ } 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);
+}
+
+
+static char * hostapd_ctrl_iface_path(struct hostapd_data *hapd)
+{
+ char *buf;
+ size_t len;
+
+ if (hapd->conf->ctrl_interface == NULL)
+ return NULL;
+
+ len = strlen(hapd->conf->ctrl_interface) + strlen(hapd->conf->iface) +
+ 2;
+ buf = malloc(len);
+ if (buf == NULL)
+ return NULL;
+
+ snprintf(buf, len, "%s/%s",
+ hapd->conf->ctrl_interface, hapd->conf->iface);
+ return buf;
+}
+
+
+int hostapd_ctrl_iface_init(struct hostapd_data *hapd)
+{
+ struct sockaddr_un addr;
+ int s = -1;
+ char *fname = NULL;
+
+ hapd->ctrl_sock = -1;
+
+ if (hapd->conf->ctrl_interface == NULL)
+ return 0;
+
+ if (mkdir(hapd->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 (chown(hapd->conf->ctrl_interface, 0,
+ hapd->conf->ctrl_interface_gid) < 0) {
+ perror("chown[ctrl_interface]");
+ return -1;
+ }
+
+ if (strlen(hapd->conf->ctrl_interface) + 1 + strlen(hapd->conf->iface)
+ >= 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 = hostapd_ctrl_iface_path(hapd);
+ 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)");
+ goto fail;
+ }
+
+ if (chown(fname, 0, hapd->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);
+
+ hapd->ctrl_sock = s;
+ eloop_register_read_sock(s, hostapd_ctrl_iface_receive, hapd,
+ NULL);
+
+ return 0;
+
+fail:
+ if (s >= 0)
+ close(s);
+ if (fname) {
+ unlink(fname);
+ free(fname);
+ }
+ return -1;
+}
+
+
+void hostapd_ctrl_iface_deinit(struct hostapd_data *hapd)
+{
+ struct wpa_ctrl_dst *dst, *prev;
+
+ if (hapd->ctrl_sock > -1) {
+ char *fname;
+ eloop_unregister_read_sock(hapd->ctrl_sock);
+ close(hapd->ctrl_sock);
+ hapd->ctrl_sock = -1;
+ fname = hostapd_ctrl_iface_path(hapd);
+ if (fname)
+ unlink(fname);
+ free(fname);
+
+ if (rmdir(hapd->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 = hapd->ctrl_dst;
+ while (dst) {
+ prev = dst;
+ dst = dst->next;
+ free(prev);
+ }
+}
+
+
+void hostapd_ctrl_iface_send(struct hostapd_data *hapd, int level,
+ char *buf, size_t len)
+{
+ struct wpa_ctrl_dst *dst, *next;
+ struct msghdr msg;
+ int idx;
+ struct iovec io[2];
+ char levelstr[10];
+
+ dst = hapd->ctrl_dst;
+ if (hapd->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(hapd->ctrl_sock, &msg, 0) < 0) {
+ fprintf(stderr, "CTRL_IFACE monitor[%d]: ",
+ idx);
+ perror("sendmsg");
+ dst->errors++;
+ if (dst->errors > 10) {
+ hostapd_ctrl_iface_detach(
+ hapd, &dst->addr,
+ dst->addrlen);
+ }
+ } else
+ dst->errors = 0;
+ }
+ idx++;
+ dst = next;
+ }
+}
diff --git a/contrib/hostapd/ctrl_iface.h b/contrib/hostapd/ctrl_iface.h
new file mode 100644
index 000000000000..ef1a541c94c8
--- /dev/null
+++ b/contrib/hostapd/ctrl_iface.h
@@ -0,0 +1,9 @@
+#ifndef CTRL_IFACE_H
+#define CTRL_IFACE_H
+
+int hostapd_ctrl_iface_init(struct hostapd_data *hapd);
+void hostapd_ctrl_iface_deinit(struct hostapd_data *hapd);
+void hostapd_ctrl_iface_send(struct hostapd_data *hapd, int level,
+ char *buf, size_t len);
+
+#endif /* CTRL_IFACE_H */
diff --git a/contrib/hostapd/defconfig b/contrib/hostapd/defconfig
new file mode 100644
index 000000000000..b27c0374da5e
--- /dev/null
+++ b/contrib/hostapd/defconfig
@@ -0,0 +1,66 @@
+# Example hostapd build time configuration
+#
+# This file lists the configuration options that are used when building the
+# hostapd binary. All lines starting with # are ignored. Configuration option
+# lines must be commented out complete, if they are not to be included, i.e.,
+# just setting VARIABLE=n is not disabling that variable.
+#
+# This file is included in Makefile, so variables like CFLAGS and LIBS can also
+# be modified from here. In most cass, these lines should use += in order not
+# to override previous values of the variables.
+
+# Driver interface for Host AP driver
+CONFIG_DRIVER_HOSTAP=y
+
+# Driver interface for wired authenticator
+#CONFIG_DRIVER_WIRED=y
+
+# Driver interface for madwifi driver
+#CONFIG_DRIVER_MADWIFI=y
+#CFLAGS += -I../head # change to reflect local setup; directory for madwifi src
+
+# Driver interface for Prism54 driver
+#CONFIG_DRIVER_PRISM54=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
+
+# IEEE 802.11F/IAPP
+CONFIG_IAPP=y
+
+# WPA2/IEEE 802.11i RSN pre-authentication
+CONFIG_RSN_PREAUTH=y
+
+# Integrated EAP authenticator
+CONFIG_EAP=y
+
+# EAP-MD5 for the integrated EAP authenticator
+CONFIG_EAP_MD5=y
+
+# EAP-TLS for the integrated EAP authenticator
+CONFIG_EAP_TLS=y
+
+# EAP-MSCHAPv2 for the integrated EAP authenticator
+CONFIG_EAP_MSCHAPV2=y
+
+# EAP-PEAP for the integrated EAP authenticator
+CONFIG_EAP_PEAP=y
+
+# EAP-GTC for the integrated EAP authenticator
+CONFIG_EAP_GTC=y
+
+# EAP-TTLS for the integrated EAP authenticator
+CONFIG_EAP_TTLS=y
+
+# EAP-SIM for the integrated EAP authenticator
+#CONFIG_EAP_SIM=y
+
+# PKCS#12 (PFX) support (used to read private key and certificate file from
+# a file that usually has extension .p12 or .pfx)
+CONFIG_PKCS12=y
+
+# RADIUS authentication server. This provides access to the integrated EAP
+# authenticator from external hosts using RADIUS.
+#CONFIG_RADIUS_SERVER=y
diff --git a/contrib/hostapd/defs.h b/contrib/hostapd/defs.h
new file mode 100644
index 000000000000..a5a515c552f5
--- /dev/null
+++ b/contrib/hostapd/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/hostapd/developer.txt b/contrib/hostapd/developer.txt
new file mode 100644
index 000000000000..e1d316341390
--- /dev/null
+++ b/contrib/hostapd/developer.txt
@@ -0,0 +1,219 @@
+Developer notes for hostapd
+===========================
+
+hostapd daemon setup, operations, and shutdown
+----------------------------------------------
+
+Files: hostapd.[ch]
+
+Externally called functions:
+ hostapd_new_assoc_sta() is called when a station associates with the AP
+
+Event loop functions:
+ handle_term() is called on SIGINT and SIGTERM to terminate hostapd process
+ handle_reload() is called on SIGHUP to reload configuration
+ handle_dump_state() is called on SIGUSR1 to dump station state data to a
+ text file
+ hostapd_rotate_wep() is called to periodically change WEP keys
+
+
+Configuration parsing
+---------------------
+
+Configuration file parsing and data structure definition.
+
+Files: config.[ch]
+
+Externally called functions:
+ hostapd_config_read() is called to read and parse a configuration file;
+ allocates and returns configuration data structure
+ hostapd_config_free() is called to free configuration data structure
+ hostapd_maclist_found() is called to check whether a given address is found
+ in a list of MAC addresses
+
+
+Kernel driver access
+--------------------
+
+Helper functions for configuring the Host AP kernel driver and
+accessing data from it.
+
+Files: driver.[ch]
+
+
+IEEE 802.11 frame handling (netdevice wlan#ap)
+----------------------------------------------
+
+Receive all incoming IEEE 802.11 frames from the kernel driver via
+wlan#ap interface.
+
+Files: receive.c
+
+Externally called functions:
+ hostapd_init_sockets() is called to initialize sockets for receiving and
+ sending IEEE 802.11 frames via wlan#ap interface
+
+Event loop functions:
+ handle_read() is called for each incoming packet from wlan#ap net device
+
+
+Station table
+-------------
+
+Files: sta_info.[ch], ap.h
+
+Event loop functions:
+ ap_handle_timer() is called to check station activity and to remove
+ inactive stations
+
+
+IEEE 802.11 management
+----------------------
+
+IEEE 802.11 management frame sending and processing (mainly,
+authentication and association). IEEE 802.11 station functionality
+(authenticate and associate with another AP as an station).
+
+Files: ieee802_11.[ch]
+
+Externally called functions:
+ ieee802_11_mgmt() is called for each received IEEE 802.11 management frame
+ (from handle_frame() in hostapd.c)
+ ieee802_11_mgmt_cb() is called for each received TX callback of IEEE 802.11
+ management frame (from handle_tx_callback() in hostapd.c)
+ ieee802_11_send_deauth() is called to send deauthentication frame
+ ieee802_11_send_disassoc() is called to send disassociation frame
+ ieee802_11_parse_elems() is used to parse information elements in
+ IEEE 802.11 management frames
+
+Event loop functions:
+ ieee802_11_sta_authenticate() called to retry authentication (with another
+ AP)
+ ieee802_11_sta_associate() called to retry association (with another AP)
+
+
+IEEE 802.11 authentication
+--------------------------
+
+Access control list for IEEE 802.11 authentication. Uses staticly
+configured ACL from configuration files or an external RADIUS
+server. Results from external RADIUS queries are cached to allow
+faster authentication frame processing.
+
+Files: ieee802_11_auth.[ch]
+
+Externally called functions:
+ hostapd_acl_init() called once during hostapd startup
+ hostapd_acl_deinit() called once during hostapd shutdown
+ hostapd_acl_recv_radius() called by IEEE 802.1X code for incoming RADIUS
+ Authentication messages (returns 0 if message was processed)
+ hostapd_allowed_address() called to check whether a specified station can be
+ authenticated
+
+Event loop functions:
+ hostapd_acl_expire() is called to expire ACL cache entries
+
+
+IEEE 802.1X Authenticator
+-------------------------
+
+Files: ieee802_1x.[ch]
+
+
+Externally called functions:
+ ieee802_1x_receive() is called for each incoming EAPOL frame from the
+ wireless interface
+ ieee802_1x_new_station() is called to start IEEE 802.1X authentication when
+ a new station completes IEEE 802.11 association
+
+Event loop functions:
+ ieee802_1x_receive_auth() called for each incoming RADIUS Authentication
+ message
+
+
+EAPOL state machine
+-------------------
+
+IEEE 802.1X state machine for EAPOL.
+
+Files: eapol_sm.[ch]
+
+Externally called functions:
+ eapol_sm_step() is called to advance EAPOL state machines after any change
+ that could affect their state
+
+Event loop functions:
+ eapol_port_timers_tick() called once per second to advance Port Timers state
+ machine
+
+
+IEEE 802.11f (IAPP)
+-------------------
+
+Files: iapp.[ch]
+
+Externally called functions:
+ iapp_new_station() is called to start accounting session when a new station
+ completes IEEE 802.11 association or IEEE 802.1X authentication
+
+Event loop functions:
+ iapp_receive_udp() is called for incoming IAPP frames over UDP
+
+
+Per station accounting
+----------------------
+
+Send RADIUS Accounting start and stop messages to a RADIUS Accounting
+server. Process incoming RADIUS Accounting messages.
+
+Files: accounting.[ch]
+
+Externally called functions:
+ accounting_init() called once during hostapd startup
+ accounting_deinit() called once during hostapd shutdown
+ accounting_sta_start() called when a station starts new session
+ accounting_sta_stop() called when a station session is terminated
+
+Event loop functions:
+ accounting_receive() called for each incoming RADIUS Accounting message
+ accounting_list_timer() called to retransmit accounting messages and to
+ remove expired entries
+
+
+RADIUS messages
+---------------
+
+RADIUS message generation and parsing functions.
+
+Files: radius.[ch]
+
+
+Event loop
+----------
+
+Event loop for registering timeout calls, signal handlers, and socket
+read events.
+
+Files: eloop.[ch]
+
+
+RC4
+---
+
+RC4 encryption
+
+Files: rc4.[ch]
+
+
+MD5
+---
+
+MD5 hash and HMAC-MD5.
+
+Files: md5.[ch]
+
+
+Miscellaneous helper functions
+------------------------------
+
+Files: common.[ch]
diff --git a/contrib/hostapd/driver.h b/contrib/hostapd/driver.h
new file mode 100644
index 000000000000..365deda48a2a
--- /dev/null
+++ b/contrib/hostapd/driver.h
@@ -0,0 +1,262 @@
+#ifndef DRIVER_H
+#define DRIVER_H
+
+struct driver_ops {
+ const char *name; /* as appears in the config file */
+
+ int (*init)(struct hostapd_data *hapd);
+ void (*deinit)(void *priv);
+
+ int (*wireless_event_init)(void *priv);
+ void (*wireless_event_deinit)(void *priv);
+
+ /**
+ * set_8021x - enable/disable 802.1x support
+ * @priv: driver private data
+ * @enabled: 1 = enable, 0 = disable
+ *
+ * Returns: 0 on success, -1 on failure
+ *
+ * Configure the kernel driver to enable/disable 802.1x support.
+ * This may be an empty function if 802.1x support is always enabled.
+ */
+ int (*set_ieee8021x)(void *priv, int enabled);
+
+ /**
+ * set_privacy - enable/disable privacy
+ * @priv: driver private data
+ * @enabled: 1 = privacy enabled, 0 = disabled
+ *
+ * Return: 0 on success, -1 on failure
+ *
+ * Configure privacy.
+ */
+ int (*set_privacy)(void *priv, int enabled);
+
+ int (*set_encryption)(void *priv, const char *alg, u8 *addr,
+ int idx, u8 *key, size_t key_len);
+ int (*get_seqnum)(void *priv, u8 *addr, int idx, u8 *seq);
+ int (*flush)(void *priv);
+ int (*set_generic_elem)(void *priv, const u8 *elem, size_t elem_len);
+
+ int (*read_sta_data)(void *priv, struct hostap_sta_driver_data *data,
+ u8 *addr);
+ int (*send_eapol)(void *priv, u8 *addr, u8 *data, size_t data_len,
+ int encrypt);
+ int (*set_sta_authorized)(void *driver, u8 *addr, int authorized);
+ int (*sta_deauth)(void *priv, u8 *addr, int reason);
+ int (*sta_disassoc)(void *priv, u8 *addr, int reason);
+ int (*sta_remove)(void *priv, u8 *addr);
+ int (*get_ssid)(void *priv, u8 *buf, int len);
+ int (*set_ssid)(void *priv, u8 *buf, int len);
+ int (*send_mgmt_frame)(void *priv, const void *msg, size_t len,
+ int flags);
+ int (*set_assoc_ap)(void *priv, u8 *addr);
+ int (*sta_add)(void *priv, u8 *addr, u16 aid, u16 capability,
+ u8 tx_supp_rates);
+ int (*get_inact_sec)(void *priv, u8 *addr);
+ int (*sta_clear_stats)(void *priv, u8 *addr);
+};
+
+static inline int
+hostapd_driver_init(struct hostapd_data *hapd)
+{
+ if (hapd->driver == NULL || hapd->driver->init == NULL)
+ return -1;
+ return hapd->driver->init(hapd);
+}
+
+static inline void
+hostapd_driver_deinit(struct hostapd_data *hapd)
+{
+ if (hapd->driver == NULL || hapd->driver->deinit == NULL)
+ return;
+ hapd->driver->deinit(hapd->driver);
+}
+
+static inline int
+hostapd_wireless_event_init(struct hostapd_data *hapd)
+{
+ if (hapd->driver == NULL ||
+ hapd->driver->wireless_event_init == NULL)
+ return 0;
+ return hapd->driver->wireless_event_init(hapd->driver);
+}
+
+static inline void
+hostapd_wireless_event_deinit(struct hostapd_data *hapd)
+{
+ if (hapd->driver == NULL ||
+ hapd->driver->wireless_event_deinit == NULL)
+ return;
+ hapd->driver->wireless_event_deinit(hapd->driver);
+}
+
+static inline int
+hostapd_set_ieee8021x(struct hostapd_data *hapd, int enabled)
+{
+ if (hapd->driver == NULL || hapd->driver->set_ieee8021x == NULL)
+ return 0;
+ return hapd->driver->set_ieee8021x(hapd->driver, enabled);
+}
+
+static inline int
+hostapd_set_privacy(struct hostapd_data *hapd, int enabled)
+{
+ if (hapd->driver == NULL || hapd->driver->set_privacy == NULL)
+ return 0;
+ return hapd->driver->set_privacy(hapd->driver, enabled);
+}
+
+static inline int
+hostapd_set_encryption(struct hostapd_data *hapd, const char *alg, u8 *addr,
+ int idx, u8 *key, size_t key_len)
+{
+ if (hapd->driver == NULL || hapd->driver->set_encryption == NULL)
+ return 0;
+ return hapd->driver->set_encryption(hapd->driver, alg, addr, idx, key,
+ key_len);
+}
+
+static inline int
+hostapd_get_seqnum(struct hostapd_data *hapd, u8 *addr, int idx, u8 *seq)
+{
+ if (hapd->driver == NULL || hapd->driver->get_seqnum == NULL)
+ return 0;
+ return hapd->driver->get_seqnum(hapd->driver, addr, idx, seq);
+}
+
+static inline int
+hostapd_flush(struct hostapd_data *hapd)
+{
+ if (hapd->driver == NULL || hapd->driver->flush == NULL)
+ return 0;
+ return hapd->driver->flush(hapd->driver);
+}
+
+static inline int
+hostapd_set_generic_elem(struct hostapd_data *hapd, const u8 *elem,
+ size_t elem_len)
+{
+ if (hapd->driver == NULL || hapd->driver->set_generic_elem == NULL)
+ return 0;
+ return hapd->driver->set_generic_elem(hapd->driver, elem, elem_len);
+}
+
+static inline int
+hostapd_read_sta_data(struct hostapd_data *hapd,
+ struct hostap_sta_driver_data *data, u8 *addr)
+{
+ if (hapd->driver == NULL || hapd->driver->read_sta_data == NULL)
+ return -1;
+ return hapd->driver->read_sta_data(hapd->driver, data, addr);
+}
+
+static inline int
+hostapd_send_eapol(struct hostapd_data *hapd, u8 *addr, u8 *data,
+ size_t data_len, int encrypt)
+{
+ if (hapd->driver == NULL || hapd->driver->send_eapol == NULL)
+ return 0;
+ return hapd->driver->send_eapol(hapd->driver, addr, data, data_len,
+ encrypt);
+}
+
+static inline int
+hostapd_set_sta_authorized(struct hostapd_data *hapd, u8 *addr, int authorized)
+{
+ if (hapd->driver == NULL || hapd->driver->set_sta_authorized == NULL)
+ return 0;
+ return hapd->driver->set_sta_authorized(hapd->driver, addr,
+ authorized);
+}
+
+static inline int
+hostapd_sta_deauth(struct hostapd_data *hapd, u8 *addr, int reason)
+{
+ if (hapd->driver == NULL || hapd->driver->sta_deauth == NULL)
+ return 0;
+ return hapd->driver->sta_deauth(hapd->driver, addr, reason);
+}
+
+static inline int
+hostapd_sta_disassoc(struct hostapd_data *hapd, u8 *addr, int reason)
+{
+ if (hapd->driver == NULL || hapd->driver->sta_disassoc == NULL)
+ return 0;
+ return hapd->driver->sta_disassoc(hapd->driver, addr, reason);
+}
+
+static inline int
+hostapd_sta_remove(struct hostapd_data *hapd, u8 *addr)
+{
+ if (hapd->driver == NULL || hapd->driver->sta_remove == NULL)
+ return 0;
+ return hapd->driver->sta_remove(hapd->driver, addr);
+}
+
+static inline int
+hostapd_get_ssid(struct hostapd_data *hapd, u8 *buf, size_t len)
+{
+ if (hapd->driver == NULL || hapd->driver->get_ssid == NULL)
+ return 0;
+ return hapd->driver->get_ssid(hapd->driver, buf, len);
+}
+
+static inline int
+hostapd_set_ssid(struct hostapd_data *hapd, u8 *buf, size_t len)
+{
+ if (hapd->driver == NULL || hapd->driver->set_ssid == NULL)
+ return 0;
+ return hapd->driver->set_ssid(hapd->driver, buf, len);
+}
+
+static inline int
+hostapd_send_mgmt_frame(struct hostapd_data *hapd, const void *msg, size_t len,
+ int flags)
+{
+ if (hapd->driver == NULL || hapd->driver->send_mgmt_frame == NULL)
+ return 0;
+ return hapd->driver->send_mgmt_frame(hapd->driver, msg, len, flags);
+}
+
+static inline int
+hostapd_set_assoc_ap(struct hostapd_data *hapd, u8 *addr)
+{
+ if (hapd->driver == NULL || hapd->driver->set_assoc_ap == NULL)
+ return 0;
+ return hapd->driver->set_assoc_ap(hapd->driver, addr);
+}
+
+static inline int
+hostapd_sta_add(struct hostapd_data *hapd, u8 *addr, u16 aid, u16 capability,
+ u8 tx_supp_rates)
+{
+ if (hapd->driver == NULL || hapd->driver->sta_add == NULL)
+ return 0;
+ return hapd->driver->sta_add(hapd->driver, addr, aid, capability,
+ tx_supp_rates);
+}
+
+static inline int
+hostapd_get_inact_sec(struct hostapd_data *hapd, u8 *addr)
+{
+ if (hapd->driver == NULL || hapd->driver->get_inact_sec == NULL)
+ return 0;
+ return hapd->driver->get_inact_sec(hapd->driver, addr);
+}
+
+
+void driver_register(const char *name, const struct driver_ops *ops);
+void driver_unregister(const char *name);
+const struct driver_ops *driver_lookup(const char *name);
+
+static inline int
+hostapd_sta_clear_stats(struct hostapd_data *hapd, u8 *addr)
+{
+ if (hapd->driver == NULL || hapd->driver->sta_clear_stats == NULL)
+ return 0;
+ return hapd->driver->sta_clear_stats(hapd->driver, addr);
+}
+
+#endif /* DRIVER_H */
diff --git a/contrib/hostapd/driver_test.c b/contrib/hostapd/driver_test.c
new file mode 100644
index 000000000000..612d1dcc5a24
--- /dev/null
+++ b/contrib/hostapd/driver_test.c
@@ -0,0 +1,78 @@
+/*
+ * Host AP (software wireless LAN access point) user space daemon for
+ * Host AP kernel driver / Driver interface for development testing
+ * 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>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <netinet/in.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include "hostapd.h"
+#include "driver.h"
+
+
+struct test_driver_data {
+ struct driver_ops ops;
+ struct hostapd_data *hapd;
+};
+
+static const struct driver_ops test_driver_ops;
+
+
+static int test_driver_init(struct hostapd_data *hapd)
+{
+ struct test_driver_data *drv;
+
+ drv = malloc(sizeof(struct test_driver_data));
+ if (drv == NULL) {
+ printf("Could not allocate memory for test driver data\n");
+ return -1;
+ }
+
+ memset(drv, 0, sizeof(*drv));
+ drv->ops = test_driver_ops;
+ drv->hapd = hapd;
+
+ hapd->driver = &drv->ops;
+ return 0;
+}
+
+
+static void test_driver_deinit(void *priv)
+{
+ struct test_driver_data *drv = priv;
+
+ drv->hapd->driver = NULL;
+
+ free(drv);
+}
+
+
+static const struct driver_ops test_driver_ops = {
+ .name = "test",
+ .init = test_driver_init,
+ .deinit = test_driver_deinit,
+};
+
+
+void test_driver_register(void)
+{
+ driver_register(test_driver_ops.name, &test_driver_ops);
+}
diff --git a/contrib/hostapd/driver_wired.c b/contrib/hostapd/driver_wired.c
new file mode 100644
index 000000000000..3e21268514f8
--- /dev/null
+++ b/contrib/hostapd/driver_wired.c
@@ -0,0 +1,395 @@
+/*
+ * Host AP (software wireless LAN access point) user space daemon for
+ * Host AP kernel driver / Kernel driver communication
+ * Copyright (c) 2002-2005, Jouni Malinen <jkmaline@cc.hut.fi>
+ * Copyright (c) 2004, Gunter Burchardt <tira@isx.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published 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 <sys/ioctl.h>
+#include <netinet/in.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#ifdef USE_KERNEL_HEADERS
+#include <asm/types.h>
+#include <linux/if_packet.h>
+#include <linux/if_ether.h> /* The L2 protocols */
+#include <linux/if_arp.h>
+#include <linux/if.h>
+#else /* USE_KERNEL_HEADERS */
+#include <net/if_arp.h>
+#include <net/if.h>
+#include <netpacket/packet.h>
+#endif /* USE_KERNEL_HEADERS */
+
+#include "hostapd.h"
+#include "ieee802_1x.h"
+#include "eloop.h"
+#include "sta_info.h"
+#include "driver.h"
+#include "accounting.h"
+
+
+struct wired_driver_data {
+ struct driver_ops ops;
+ struct hostapd_data *hapd;
+
+ int sock; /* raw packet socket for driver access */
+ int dhcp_sock; /* socket for dhcp packets */
+};
+
+static const struct driver_ops wired_driver_ops;
+
+
+#define WIRED_EAPOL_MULTICAST_GROUP {0x01,0x80,0xc2,0x00,0x00,0x03}
+
+
+/* TODO: detecting new devices should eventually be changed from using DHCP
+ * snooping to trigger on any packet from a new layer 2 MAC address, e.g.,
+ * based on ebtables, etc. */
+
+struct dhcp_message {
+ u_int8_t op;
+ u_int8_t htype;
+ u_int8_t hlen;
+ u_int8_t hops;
+ u_int32_t xid;
+ u_int16_t secs;
+ u_int16_t flags;
+ u_int32_t ciaddr;
+ u_int32_t yiaddr;
+ u_int32_t siaddr;
+ u_int32_t giaddr;
+ u_int8_t chaddr[16];
+ u_int8_t sname[64];
+ u_int8_t file[128];
+ u_int32_t cookie;
+ u_int8_t options[308]; /* 312 - cookie */
+};
+
+
+static void wired_possible_new_sta(struct hostapd_data *hapd, u8 *addr)
+{
+ struct sta_info *sta;
+
+ sta = ap_get_sta(hapd, addr);
+ if (sta)
+ return;
+
+ HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "Data frame from unknown STA "
+ MACSTR " - adding a new STA\n", MAC2STR(addr));
+ sta = ap_sta_add(hapd, addr);
+ if (sta) {
+ hostapd_new_assoc_sta(hapd, sta);
+ accounting_sta_get_id(hapd, sta);
+ } else {
+ HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "Failed to add STA entry "
+ "for " MACSTR "\n", MAC2STR(addr));
+ }
+}
+
+
+static void handle_data(struct hostapd_data *hapd, char *buf, size_t len)
+{
+ struct ieee8023_hdr *hdr;
+ u8 *pos, *sa;
+ size_t left;
+
+ /* must contain at least ieee8023_hdr 6 byte source, 6 byte dest,
+ * 2 byte ethertype */
+ if (len < 14) {
+ HOSTAPD_DEBUG(HOSTAPD_DEBUG_VERBOSE, "handle_data: too short "
+ "(%lu)\n", (unsigned long) len);
+ return;
+ }
+
+ hdr = (struct ieee8023_hdr *) buf;
+
+ switch (ntohs(hdr->ethertype)) {
+ case ETH_P_PAE:
+ HOSTAPD_DEBUG(HOSTAPD_DEBUG_VERBOSE,
+ "Received EAPOL packet\n");
+ sa = hdr->src;
+ wired_possible_new_sta(hapd, sa);
+
+ pos = (u8 *) (hdr + 1);
+ left = len - sizeof(*hdr);
+
+ ieee802_1x_receive(hapd, sa, pos, left);
+ break;
+
+ default:
+ HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL,
+ "Unknown ethertype 0x%04x in data frame\n",
+ ntohs(hdr->ethertype));
+ break;
+ }
+}
+
+
+static void handle_read(int sock, void *eloop_ctx, void *sock_ctx)
+{
+ struct hostapd_data *hapd = (struct hostapd_data *) eloop_ctx;
+ int len;
+ unsigned char buf[3000];
+
+ len = recv(sock, buf, sizeof(buf), 0);
+ if (len < 0) {
+ perror("recv");
+ return;
+ }
+
+ handle_data(hapd, buf, len);
+}
+
+
+static void handle_dhcp(int sock, void *eloop_ctx, void *sock_ctx)
+{
+ struct hostapd_data *hapd = (struct hostapd_data *) eloop_ctx;
+ int len;
+ unsigned char buf[3000];
+ struct dhcp_message *msg;
+ u8 *mac_address;
+
+ len = recv(sock, buf, sizeof(buf), 0);
+ if (len < 0) {
+ perror("recv");
+ return;
+ }
+
+ /* must contain at least dhcp_message->chaddr */
+ if (len < 44) {
+ HOSTAPD_DEBUG(HOSTAPD_DEBUG_VERBOSE, "handle_dhcp: too short "
+ "(%d)\n", len);
+ return;
+ }
+
+ msg = (struct dhcp_message *) buf;
+ mac_address = (u8 *) &(msg->chaddr);
+
+ HOSTAPD_DEBUG(HOSTAPD_DEBUG_VERBOSE,
+ "Got DHCP broadcast packet from " MACSTR "\n",
+ MAC2STR(mac_address));
+
+ wired_possible_new_sta(hapd, mac_address);
+}
+
+
+static int wired_init_sockets(struct wired_driver_data *drv)
+{
+ struct hostapd_data *hapd = drv->hapd;
+ struct ifreq ifr;
+ struct sockaddr_ll addr;
+ struct sockaddr_in addr2;
+ struct packet_mreq mreq;
+ u8 multicastgroup_eapol[6] = WIRED_EAPOL_MULTICAST_GROUP;
+ int n = 1;
+
+ drv->sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_PAE));
+ if (drv->sock < 0) {
+ perror("socket[PF_PACKET,SOCK_RAW]");
+ return -1;
+ }
+
+ if (eloop_register_read_sock(drv->sock, handle_read, hapd, NULL)) {
+ printf("Could not register read socket\n");
+ return -1;
+ }
+
+ memset(&ifr, 0, sizeof(ifr));
+ snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s",
+ hapd->conf->iface);
+ if (ioctl(drv->sock, SIOCGIFINDEX, &ifr) != 0) {
+ perror("ioctl(SIOCGIFINDEX)");
+ return -1;
+ }
+
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sll_family = AF_PACKET;
+ addr.sll_ifindex = ifr.ifr_ifindex;
+ HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL,
+ "Opening raw packet socket for ifindex %d\n",
+ addr.sll_ifindex);
+
+ if (bind(drv->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ perror("bind");
+ return -1;
+ }
+
+ /* filter multicast address */
+ memset(&mreq, 0, sizeof(mreq));
+ mreq.mr_ifindex = ifr.ifr_ifindex;
+ mreq.mr_type = PACKET_MR_MULTICAST;
+ mreq.mr_alen = 6;
+ memcpy(mreq.mr_address, multicastgroup_eapol, mreq.mr_alen);
+
+ if (setsockopt(drv->sock, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq,
+ sizeof(mreq)) < 0) {
+ perror("setsockopt[SOL_SOCKET,PACKET_ADD_MEMBERSHIP]");
+ return -1;
+ }
+
+ memset(&ifr, 0, sizeof(ifr));
+ snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s", hapd->conf->iface);
+ if (ioctl(drv->sock, SIOCGIFHWADDR, &ifr) != 0) {
+ perror("ioctl(SIOCGIFHWADDR)");
+ return -1;
+ }
+
+ if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) {
+ printf("Invalid HW-addr family 0x%04x\n",
+ ifr.ifr_hwaddr.sa_family);
+ return -1;
+ }
+ memcpy(hapd->own_addr, ifr.ifr_hwaddr.sa_data, ETH_ALEN);
+
+ /* setup dhcp listen socket for sta detection */
+ if ((drv->dhcp_sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
+ perror("socket call failed for dhcp");
+ return -1;
+ }
+
+ if (eloop_register_read_sock(drv->dhcp_sock, handle_dhcp, hapd, NULL))
+ {
+ printf("Could not register read socket\n");
+ return -1;
+ }
+
+ memset(&addr2, 0, sizeof(addr2));
+ addr2.sin_family = AF_INET;
+ addr2.sin_port = htons(67);
+ addr2.sin_addr.s_addr = INADDR_ANY;
+
+ if (setsockopt(drv->dhcp_sock, SOL_SOCKET, SO_REUSEADDR, (char *) &n,
+ sizeof(n)) == -1) {
+ perror("setsockopt[SOL_SOCKET,SO_REUSEADDR]");
+ return -1;
+ }
+ if (setsockopt(drv->dhcp_sock, SOL_SOCKET, SO_BROADCAST, (char *) &n,
+ sizeof(n)) == -1) {
+ perror("setsockopt[SOL_SOCKET,SO_BROADCAST]");
+ return -1;
+ }
+
+ memset(&ifr, 0, sizeof(ifr));
+ strncpy(ifr.ifr_ifrn.ifrn_name, hapd->conf->iface, IFNAMSIZ);
+ if (setsockopt(drv->dhcp_sock, SOL_SOCKET, SO_BINDTODEVICE,
+ (char *) &ifr, sizeof(ifr)) < 0) {
+ perror("setsockopt[SOL_SOCKET,SO_BINDTODEVICE]");
+ return -1;
+ }
+
+ if (bind(drv->dhcp_sock, (struct sockaddr *) &addr2,
+ sizeof(struct sockaddr)) == -1) {
+ perror("bind");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int wired_send_eapol(void *priv, u8 *addr,
+ u8 *data, size_t data_len, int encrypt)
+{
+ struct wired_driver_data *drv = priv;
+
+ struct ieee8023_hdr *hdr;
+ size_t len;
+ u8 *pos;
+ int res;
+
+ len = sizeof(*hdr) + data_len;
+ hdr = malloc(len);
+ if (hdr == NULL) {
+ printf("malloc() failed for wired_send_eapol(len=%lu)\n",
+ (unsigned long) len);
+ return -1;
+ }
+
+ memset(hdr, 0, len);
+ memcpy(hdr->dest, addr, ETH_ALEN);
+ memcpy(hdr->src, drv->hapd->own_addr, ETH_ALEN);
+ hdr->ethertype = htons(ETH_P_PAE);
+
+ pos = (u8 *) (hdr + 1);
+ memcpy(pos, data, data_len);
+
+ res = send(drv->sock, (u8 *) hdr, len, 0);
+ free(hdr);
+
+ if (res < 0) {
+ perror("wired_send_eapol: send");
+ printf("wired_send_eapol - packet len: %lu - failed\n",
+ (unsigned long) len);
+ }
+
+ return res;
+}
+
+
+static int wired_driver_init(struct hostapd_data *hapd)
+{
+ struct wired_driver_data *drv;
+
+ drv = malloc(sizeof(struct wired_driver_data));
+ if (drv == NULL) {
+ printf("Could not allocate memory for wired driver data\n");
+ return -1;
+ }
+
+ memset(drv, 0, sizeof(*drv));
+ drv->ops = wired_driver_ops;
+ drv->hapd = hapd;
+
+ if (wired_init_sockets(drv))
+ return -1;
+
+ hapd->driver = &drv->ops;
+ return 0;
+}
+
+
+static void wired_driver_deinit(void *priv)
+{
+ struct wired_driver_data *drv = priv;
+
+ drv->hapd->driver = NULL;
+
+ if (drv->sock >= 0)
+ close(drv->sock);
+
+ if (drv->dhcp_sock >= 0)
+ close(drv->dhcp_sock);
+
+ free(drv);
+}
+
+
+static const struct driver_ops wired_driver_ops = {
+ .name = "wired",
+ .init = wired_driver_init,
+ .deinit = wired_driver_deinit,
+ .send_eapol = wired_send_eapol,
+};
+
+void wired_driver_register(void)
+{
+ driver_register(wired_driver_ops.name, &wired_driver_ops);
+}
diff --git a/contrib/hostapd/eap.c b/contrib/hostapd/eap.c
new file mode 100644
index 000000000000..7a21c802e5a4
--- /dev/null
+++ b/contrib/hostapd/eap.c
@@ -0,0 +1,911 @@
+/*
+ * hostapd / EAP Standalone Authenticator state machine
+ * 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 <unistd.h>
+#include <netinet/in.h>
+#include <string.h>
+#include <sys/socket.h>
+
+#include "hostapd.h"
+#include "eloop.h"
+#include "sta_info.h"
+#include "eap_i.h"
+
+
+extern const struct eap_method eap_method_identity;
+#ifdef EAP_MD5
+extern const struct eap_method eap_method_md5;
+#endif /* EAP_MD5 */
+#ifdef EAP_TLS
+extern const struct eap_method eap_method_tls;
+#endif /* EAP_TLS */
+#ifdef EAP_MSCHAPv2
+extern const struct eap_method eap_method_mschapv2;
+#endif /* EAP_MSCHAPv2 */
+#ifdef EAP_PEAP
+extern const struct eap_method eap_method_peap;
+#endif /* EAP_PEAP */
+#ifdef EAP_TLV
+extern const struct eap_method eap_method_tlv;
+#endif /* EAP_TLV */
+#ifdef EAP_GTC
+extern const struct eap_method eap_method_gtc;
+#endif /* EAP_GTC */
+#ifdef EAP_TTLS
+extern const struct eap_method eap_method_ttls;
+#endif /* EAP_TTLS */
+#ifdef EAP_SIM
+extern const struct eap_method eap_method_sim;
+#endif /* EAP_SIM */
+
+static const struct eap_method *eap_methods[] =
+{
+ &eap_method_identity,
+#ifdef EAP_MD5
+ &eap_method_md5,
+#endif /* EAP_MD5 */
+#ifdef EAP_TLS
+ &eap_method_tls,
+#endif /* EAP_TLS */
+#ifdef EAP_MSCHAPv2
+ &eap_method_mschapv2,
+#endif /* EAP_MSCHAPv2 */
+#ifdef EAP_PEAP
+ &eap_method_peap,
+#endif /* EAP_PEAP */
+#ifdef EAP_TTLS
+ &eap_method_ttls,
+#endif /* EAP_TTLS */
+#ifdef EAP_TLV
+ &eap_method_tlv,
+#endif /* EAP_TLV */
+#ifdef EAP_GTC
+ &eap_method_gtc,
+#endif /* EAP_GTC */
+#ifdef EAP_SIM
+ &eap_method_sim,
+#endif /* EAP_SIM */
+};
+#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 void eap_user_free(struct eap_user *user);
+
+
+/* EAP state machines are described in draft-ietf-eap-statemachine-05.txt */
+
+static int eap_sm_calculateTimeout(struct eap_sm *sm, int retransCount,
+ int eapSRTT, int eapRTTVAR,
+ int methodTimeout);
+static void eap_sm_parseEapResp(struct eap_sm *sm, u8 *resp, size_t len);
+static u8 * eap_sm_buildSuccess(struct eap_sm *sm, int id, size_t *len);
+static u8 * eap_sm_buildFailure(struct eap_sm *sm, int id, size_t *len);
+static int eap_sm_nextId(struct eap_sm *sm, int id);
+static void eap_sm_Policy_update(struct eap_sm *sm, u8 *nak_list, size_t len);
+static EapType eap_sm_Policy_getNextMethod(struct eap_sm *sm);
+static int eap_sm_Policy_getDecision(struct eap_sm *sm);
+static Boolean eap_sm_Policy_doPickUp(struct eap_sm *sm, EapType method);
+
+
+/* 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 void eapol_set_eapReqData(struct eap_sm *sm,
+ const u8 *eapReqData, size_t eapReqDataLen)
+{
+ wpa_hexdump(MSG_MSGDUMP, "EAP: eapReqData -> EAPOL",
+ sm->eapReqData, sm->eapReqDataLen);
+ sm->eapol_cb->set_eapReqData(sm->eapol_ctx, eapReqData, eapReqDataLen);
+}
+
+
+static void eapol_set_eapKeyData(struct eap_sm *sm,
+ const u8 *eapKeyData, size_t eapKeyDataLen)
+{
+ wpa_hexdump(MSG_MSGDUMP, "EAP: eapKeyData -> EAPOL",
+ sm->eapKeyData, sm->eapKeyDataLen);
+ sm->eapol_cb->set_eapKeyData(sm->eapol_ctx, eapKeyData, eapKeyDataLen);
+}
+
+
+int eap_user_get(struct eap_sm *sm, const u8 *identity, size_t identity_len,
+ int phase2)
+{
+ struct eap_user *user;
+
+ if (sm == NULL || sm->eapol_cb == NULL ||
+ sm->eapol_cb->get_eap_user == NULL)
+ return -1;
+
+ eap_user_free(sm->user);
+ sm->user = NULL;
+
+ user = malloc(sizeof(*user));
+ if (user == NULL)
+ return -1;
+ memset(user, 0, sizeof(*user));
+
+ if (sm->eapol_cb->get_eap_user(sm->eapol_ctx, identity,
+ identity_len, phase2, user) != 0) {
+ eap_user_free(user);
+ return -1;
+ }
+
+ sm->user = user;
+ sm->user_eap_method_index = 0;
+
+ return 0;
+}
+
+
+SM_STATE(EAP, DISABLED)
+{
+ SM_ENTRY(EAP, DISABLED);
+}
+
+
+SM_STATE(EAP, INITIALIZE)
+{
+ SM_ENTRY(EAP, INITIALIZE);
+
+ sm->currentId = -1;
+ eapol_set_bool(sm, EAPOL_eapSuccess, FALSE);
+ eapol_set_bool(sm, EAPOL_eapFail, FALSE);
+ eapol_set_bool(sm, EAPOL_eapTimeout, FALSE);
+ free(sm->eapKeyData);
+ sm->eapKeyData = NULL;
+ sm->eapKeyDataLen = 0;
+ /* eapKeyAvailable = FALSE */
+ eapol_set_bool(sm, EAPOL_eapRestart, FALSE);
+
+ /* This is not defined in draft-ietf-eap-statemachine-05.txt, but
+ * method state needs to be reseted here so that it does not remain in
+ * success state when re-authentication starts. */
+ if (sm->m && sm->eap_method_priv) {
+ sm->m->reset(sm, sm->eap_method_priv);
+ sm->eap_method_priv = NULL;
+ }
+ sm->m = NULL;
+ sm->user_eap_method_index = 0;
+
+ if (sm->backend_auth) {
+ sm->currentMethod = EAP_TYPE_NONE;
+ /* parse rxResp, respId, respMethod */
+ eap_sm_parseEapResp(sm, sm->eapRespData, sm->eapRespDataLen);
+ if (sm->rxResp) {
+ sm->currentId = sm->respId;
+ }
+ }
+}
+
+
+SM_STATE(EAP, PICK_UP_METHOD)
+{
+ SM_ENTRY(EAP, PICK_UP_METHOD);
+
+ if (eap_sm_Policy_doPickUp(sm, sm->respMethod)) {
+ sm->currentMethod = sm->respMethod;
+ if (sm->m && sm->eap_method_priv) {
+ sm->m->reset(sm, sm->eap_method_priv);
+ sm->eap_method_priv = NULL;
+ }
+ sm->m = eap_sm_get_eap_methods(sm->currentMethod);
+ if (sm->m && sm->m->initPickUp) {
+ sm->eap_method_priv = sm->m->initPickUp(sm);
+ if (sm->eap_method_priv == NULL) {
+ wpa_printf(MSG_DEBUG, "EAP: Failed to "
+ "initialize EAP method %d",
+ sm->currentMethod);
+ sm->m = NULL;
+ sm->currentMethod = EAP_TYPE_NONE;
+ }
+ } else {
+ sm->m = NULL;
+ sm->currentMethod = EAP_TYPE_NONE;
+ }
+ }
+}
+
+
+SM_STATE(EAP, IDLE)
+{
+ SM_ENTRY(EAP, IDLE);
+
+ sm->retransWhile = eap_sm_calculateTimeout(sm, sm->retransCount,
+ sm->eapSRTT, sm->eapRTTVAR,
+ sm->methodTimeout);
+}
+
+
+SM_STATE(EAP, RETRANSMIT)
+{
+ SM_ENTRY(EAP, RETRANSMIT);
+
+ /* TODO: Is this needed since EAPOL state machines take care of
+ * retransmit? */
+}
+
+
+SM_STATE(EAP, RECEIVED)
+{
+ SM_ENTRY(EAP, RECEIVED);
+
+ /* parse rxResp, respId, respMethod */
+ eap_sm_parseEapResp(sm, sm->eapRespData, sm->eapRespDataLen);
+}
+
+
+SM_STATE(EAP, DISCARD)
+{
+ SM_ENTRY(EAP, DISCARD);
+ eapol_set_bool(sm, EAPOL_eapResp, FALSE);
+ eapol_set_bool(sm, EAPOL_eapNoReq, TRUE);
+}
+
+
+SM_STATE(EAP, SEND_REQUEST)
+{
+ SM_ENTRY(EAP, SEND_REQUEST);
+
+ sm->retransCount = 0;
+ if (sm->eapReqData) {
+ eapol_set_eapReqData(sm, sm->eapReqData, sm->eapReqDataLen);
+ free(sm->lastReqData);
+ sm->lastReqData = sm->eapReqData;
+ sm->lastReqDataLen = sm->eapReqDataLen;
+ sm->eapReqData = NULL;
+ sm->eapReqDataLen = 0;
+ eapol_set_bool(sm, EAPOL_eapResp, FALSE);
+ eapol_set_bool(sm, EAPOL_eapReq, TRUE);
+ } else {
+ wpa_printf(MSG_INFO, "EAP: SEND_REQUEST - no eapReqData");
+ eapol_set_bool(sm, EAPOL_eapResp, FALSE);
+ eapol_set_bool(sm, EAPOL_eapReq, FALSE);
+ eapol_set_bool(sm, EAPOL_eapNoReq, TRUE);
+ }
+}
+
+
+SM_STATE(EAP, INTEGRITY_CHECK)
+{
+ SM_ENTRY(EAP, INTEGRITY_CHECK);
+
+ if (sm->m->check) {
+ sm->ignore = sm->m->check(sm, sm->eap_method_priv,
+ sm->eapRespData, sm->eapRespDataLen);
+ }
+}
+
+
+SM_STATE(EAP, METHOD_REQUEST)
+{
+ SM_ENTRY(EAP, METHOD_REQUEST);
+
+ if (sm->m == NULL) {
+ wpa_printf(MSG_DEBUG, "EAP: method not initialized");
+ return;
+ }
+
+ sm->currentId = eap_sm_nextId(sm, sm->currentId);
+ wpa_printf(MSG_DEBUG, "EAP: building EAP-Request: Identifier %d",
+ sm->currentId);
+ sm->lastId = sm->currentId;
+ free(sm->eapReqData);
+ sm->eapReqData = sm->m->buildReq(sm, sm->eap_method_priv,
+ sm->currentId, &sm->eapReqDataLen);
+ if (sm->m->getTimeout)
+ sm->methodTimeout = sm->m->getTimeout(sm, sm->eap_method_priv);
+ else
+ sm->methodTimeout = 0;
+}
+
+
+SM_STATE(EAP, METHOD_RESPONSE)
+{
+ SM_ENTRY(EAP, METHOD_RESPONSE);
+
+ sm->m->process(sm, sm->eap_method_priv, sm->eapRespData,
+ sm->eapRespDataLen);
+ if (sm->m->isDone(sm, sm->eap_method_priv)) {
+ eap_sm_Policy_update(sm, NULL, 0);
+ free(sm->eapKeyData);
+ if (sm->m->getKey) {
+ sm->eapKeyData = sm->m->getKey(sm, sm->eap_method_priv,
+ &sm->eapKeyDataLen);
+ } else {
+ sm->eapKeyData = NULL;
+ sm->eapKeyDataLen = 0;
+ }
+ sm->methodState = METHOD_END;
+ } else {
+ sm->methodState = METHOD_CONTINUE;
+ }
+}
+
+
+SM_STATE(EAP, PROPOSE_METHOD)
+{
+ SM_ENTRY(EAP, PROPOSE_METHOD);
+
+ sm->currentMethod = eap_sm_Policy_getNextMethod(sm);
+ if (sm->m && sm->eap_method_priv) {
+ sm->m->reset(sm, sm->eap_method_priv);
+ sm->eap_method_priv = NULL;
+ }
+ sm->m = eap_sm_get_eap_methods(sm->currentMethod);
+ if (sm->m) {
+ 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->currentMethod);
+ sm->m = NULL;
+ sm->currentMethod = EAP_TYPE_NONE;
+ }
+ }
+ if (sm->currentMethod == EAP_TYPE_IDENTITY ||
+ sm->currentMethod == EAP_TYPE_NOTIFICATION)
+ sm->methodState = METHOD_CONTINUE;
+ else
+ sm->methodState = METHOD_PROPOSED;
+}
+
+
+SM_STATE(EAP, NAK)
+{
+ struct eap_hdr *nak;
+ size_t len = 0;
+ u8 *pos, *nak_list = NULL;
+
+ SM_ENTRY(EAP, NAK);
+
+ if (sm->eap_method_priv) {
+ sm->m->reset(sm, sm->eap_method_priv);
+ sm->eap_method_priv = NULL;
+ }
+ sm->m = NULL;
+
+ nak = (struct eap_hdr *) sm->eapRespData;
+ if (nak && sm->eapRespDataLen > sizeof(*nak)) {
+ len = ntohs(nak->length);
+ if (len > sm->eapRespDataLen)
+ len = sm->eapRespDataLen;
+ pos = (u8 *) (nak + 1);
+ len -= sizeof(*nak);
+ if (*pos == EAP_TYPE_NAK) {
+ pos++;
+ len--;
+ nak_list = pos;
+ }
+ }
+ eap_sm_Policy_update(sm, nak_list, len);
+}
+
+
+SM_STATE(EAP, SELECT_ACTION)
+{
+ SM_ENTRY(EAP, SELECT_ACTION);
+
+ sm->decision = eap_sm_Policy_getDecision(sm);
+}
+
+
+SM_STATE(EAP, TIMEOUT_FAILURE)
+{
+ SM_ENTRY(EAP, TIMEOUT_FAILURE);
+
+ eapol_set_bool(sm, EAPOL_eapTimeout, TRUE);
+}
+
+
+SM_STATE(EAP, FAILURE)
+{
+ SM_ENTRY(EAP, FAILURE);
+
+ free(sm->eapReqData);
+ sm->eapReqData = eap_sm_buildFailure(sm, sm->currentId,
+ &sm->eapReqDataLen);
+ if (sm->eapReqData) {
+ eapol_set_eapReqData(sm, sm->eapReqData, sm->eapReqDataLen);
+ free(sm->eapReqData);
+ sm->eapReqData = NULL;
+ sm->eapReqDataLen = 0;
+ }
+ free(sm->lastReqData);
+ sm->lastReqData = NULL;
+ sm->lastReqDataLen = 0;
+ eapol_set_bool(sm, EAPOL_eapFail, TRUE);
+}
+
+
+SM_STATE(EAP, SUCCESS)
+{
+ SM_ENTRY(EAP, SUCCESS);
+
+ free(sm->eapReqData);
+ sm->eapReqData = eap_sm_buildSuccess(sm, sm->currentId,
+ &sm->eapReqDataLen);
+ if (sm->eapReqData) {
+ eapol_set_eapReqData(sm, sm->eapReqData, sm->eapReqDataLen);
+ free(sm->eapReqData);
+ sm->eapReqData = NULL;
+ sm->eapReqDataLen = 0;
+ }
+ free(sm->lastReqData);
+ sm->lastReqData = NULL;
+ sm->lastReqDataLen = 0;
+ if (sm->eapKeyData) {
+ eapol_set_eapKeyData(sm, sm->eapKeyData, sm->eapKeyDataLen);
+ }
+ eapol_set_bool(sm, EAPOL_eapSuccess, TRUE);
+}
+
+
+SM_STEP(EAP)
+{
+ 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 switch (sm->EAP_state) {
+ case EAP_INITIALIZE:
+ if (sm->backend_auth) {
+ if (!sm->rxResp)
+ SM_ENTER(EAP, SELECT_ACTION);
+ else if (sm->rxResp &&
+ (sm->respMethod == EAP_TYPE_NAK ||
+ sm->respMethod == EAP_TYPE_EXPANDED_NAK))
+ SM_ENTER(EAP, NAK);
+ else
+ SM_ENTER(EAP, PICK_UP_METHOD);
+ } else {
+ SM_ENTER(EAP, SELECT_ACTION);
+ }
+ break;
+ case EAP_PICK_UP_METHOD:
+ if (sm->currentMethod == EAP_TYPE_NONE) {
+ SM_ENTER(EAP, SELECT_ACTION);
+ } else {
+ SM_ENTER(EAP, METHOD_RESPONSE);
+ }
+ break;
+ case EAP_DISABLED:
+ if (eapol_get_bool(sm, EAPOL_portEnabled))
+ SM_ENTER(EAP, INITIALIZE);
+ break;
+ case EAP_IDLE:
+ if (sm->retransWhile == 0)
+ SM_ENTER(EAP, RETRANSMIT);
+ else if (eapol_get_bool(sm, EAPOL_eapResp))
+ SM_ENTER(EAP, RECEIVED);
+ break;
+ case EAP_RETRANSMIT:
+ if (sm->retransCount > sm->MaxRetrans)
+ SM_ENTER(EAP, TIMEOUT_FAILURE);
+ else
+ SM_ENTER(EAP, IDLE);
+ break;
+ case EAP_RECEIVED:
+ if (sm->rxResp && (sm->respId == sm->currentId) &&
+ (sm->respMethod == EAP_TYPE_NAK ||
+ sm->respMethod == EAP_TYPE_EXPANDED_NAK)
+ && (sm->methodState == METHOD_PROPOSED))
+ SM_ENTER(EAP, NAK);
+ else if (sm->rxResp && (sm->respId == sm->currentId) &&
+ (sm->respMethod == sm->currentMethod))
+ SM_ENTER(EAP, INTEGRITY_CHECK);
+ else
+ SM_ENTER(EAP, DISCARD);
+ break;
+ case EAP_DISCARD:
+ SM_ENTER(EAP, IDLE);
+ break;
+ case EAP_SEND_REQUEST:
+ SM_ENTER(EAP, IDLE);
+ break;
+ case EAP_INTEGRITY_CHECK:
+ if (sm->ignore)
+ SM_ENTER(EAP, DISCARD);
+ else
+ SM_ENTER(EAP, METHOD_RESPONSE);
+ break;
+ case EAP_METHOD_REQUEST:
+ SM_ENTER(EAP, SEND_REQUEST);
+ break;
+ case EAP_METHOD_RESPONSE:
+ if (sm->methodState == METHOD_END)
+ SM_ENTER(EAP, SELECT_ACTION);
+ else
+ SM_ENTER(EAP, METHOD_REQUEST);
+ break;
+ case EAP_PROPOSE_METHOD:
+ SM_ENTER(EAP, METHOD_REQUEST);
+ break;
+ case EAP_NAK:
+ SM_ENTER(EAP, SELECT_ACTION);
+ break;
+ case EAP_SELECT_ACTION:
+ if (sm->decision == DECISION_FAILURE)
+ SM_ENTER(EAP, FAILURE);
+ else if (sm->decision == DECISION_SUCCESS)
+ SM_ENTER(EAP, SUCCESS);
+ else
+ SM_ENTER(EAP, PROPOSE_METHOD);
+ break;
+ case EAP_TIMEOUT_FAILURE:
+ break;
+ case EAP_FAILURE:
+ break;
+ case EAP_SUCCESS:
+ break;
+ }
+}
+
+
+static int eap_sm_calculateTimeout(struct eap_sm *sm, int retransCount,
+ int eapSRTT, int eapRTTVAR,
+ int methodTimeout)
+{
+ /* For now, retransmission is done in EAPOL state machines, so make
+ * sure EAP state machine does not end up trying to retransmit packets.
+ */
+ return 1;
+}
+
+
+static void eap_sm_parseEapResp(struct eap_sm *sm, u8 *resp, size_t len)
+{
+ struct eap_hdr *hdr;
+ size_t plen;
+
+ /* parse rxResp, respId, respMethod */
+ sm->rxResp = FALSE;
+ sm->respId = -1;
+ sm->respMethod = EAP_TYPE_NONE;
+
+ if (resp == NULL || len < sizeof(*hdr))
+ return;
+
+ hdr = (struct eap_hdr *) resp;
+ plen = ntohs(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->respId = hdr->identifier;
+
+ if (hdr->code == EAP_CODE_RESPONSE)
+ sm->rxResp = TRUE;
+
+ if (len > sizeof(*hdr))
+ sm->respMethod = *((u8 *) (hdr + 1));
+
+ wpa_printf(MSG_DEBUG, "EAP: parseEapResp: rxResp=%d respId=%d "
+ "respMethod=%d", sm->rxResp, sm->respId, sm->respMethod);
+}
+
+
+static u8 * eap_sm_buildSuccess(struct eap_sm *sm, int id, size_t *len)
+{
+ struct eap_hdr *resp;
+ wpa_printf(MSG_DEBUG, "EAP: Building EAP-Success (id=%d)", id);
+
+ *len = sizeof(*resp);
+ resp = malloc(*len);
+ if (resp == NULL)
+ return NULL;
+ resp->code = EAP_CODE_SUCCESS;
+ resp->identifier = id;
+ resp->length = htons(*len);
+
+ return (u8 *) resp;
+}
+
+
+static u8 * eap_sm_buildFailure(struct eap_sm *sm, int id, size_t *len)
+{
+ struct eap_hdr *resp;
+ wpa_printf(MSG_DEBUG, "EAP: Building EAP-Failure (id=%d)", id);
+
+ *len = sizeof(*resp);
+ resp = malloc(*len);
+ if (resp == NULL)
+ return NULL;
+ resp->code = EAP_CODE_FAILURE;
+ resp->identifier = id;
+ resp->length = htons(*len);
+
+ return (u8 *) resp;
+}
+
+
+static int eap_sm_nextId(struct eap_sm *sm, int id)
+{
+ if (id < 0) {
+ /* RFC 3748 Ch 4.1: recommended to initalize Identifier with a
+ * random number */
+ id = rand() & 0xff;
+ if (id != sm->lastId)
+ return id;
+ }
+ return (id + 1) & 0xff;
+}
+
+
+void eap_sm_process_nak(struct eap_sm *sm, u8 *nak_list, size_t len)
+{
+ int i, j;
+
+ wpa_printf(MSG_MSGDUMP, "EAP: processing NAK (current EAP method "
+ "index %d)", sm->user_eap_method_index);
+
+ wpa_hexdump(MSG_MSGDUMP, "EAP: configured methods",
+ sm->user->methods, EAP_MAX_METHODS);
+ wpa_hexdump(MSG_MSGDUMP, "EAP: list of methods supported by the peer",
+ nak_list, len);
+
+ i = sm->user_eap_method_index;
+ while (i < EAP_MAX_METHODS && sm->user->methods[i] != EAP_TYPE_NONE) {
+ for (j = 0; j < len; j++) {
+ if (nak_list[j] == sm->user->methods[i]) {
+ break;
+ }
+ }
+
+ if (j < len) {
+ /* found */
+ i++;
+ continue;
+ }
+
+ /* not found - remove from the list */
+ memmove(&sm->user->methods[i], &sm->user->methods[i + 1],
+ EAP_MAX_METHODS - i - 1);
+ sm->user->methods[EAP_MAX_METHODS - 1] = EAP_TYPE_NONE;
+ }
+
+ wpa_hexdump(MSG_MSGDUMP, "EAP: new list of configured methods",
+ sm->user->methods, EAP_MAX_METHODS);
+}
+
+
+static void eap_sm_Policy_update(struct eap_sm *sm, u8 *nak_list, size_t len)
+{
+ if (nak_list == NULL || sm == NULL || sm->user == NULL)
+ return;
+
+ if (sm->user->phase2) {
+ wpa_printf(MSG_DEBUG, "EAP: EAP-Nak received after Phase2 user"
+ " info was selected - reject");
+ sm->decision = DECISION_FAILURE;
+ return;
+ }
+
+ eap_sm_process_nak(sm, nak_list, len);
+}
+
+
+static EapType eap_sm_Policy_getNextMethod(struct eap_sm *sm)
+{
+ EapType next;
+
+ /* In theory, there should be no problems with starting
+ * re-authentication with something else than EAP-Request/Identity and
+ * this does indeed work with wpa_supplicant. However, at least Funk
+ * Supplicant seemed to ignore re-auth if it skipped
+ * EAP-Request/Identity.
+ * Re-auth sets currentId == -1, so that can be used here to select
+ * whether Identity needs to be requested again. */
+ if (sm->identity == NULL || sm->currentId == -1) {
+ next = EAP_TYPE_IDENTITY;
+ sm->update_user = TRUE;
+ } else if (sm->user && sm->user_eap_method_index < EAP_MAX_METHODS &&
+ sm->user->methods[sm->user_eap_method_index] !=
+ EAP_TYPE_NONE) {
+ next = sm->user->methods[sm->user_eap_method_index++];
+ } else {
+ next = EAP_TYPE_NONE;
+ }
+ wpa_printf(MSG_DEBUG, "EAP: getNextMethod: type %d", next);
+ return next;
+}
+
+
+static int eap_sm_Policy_getDecision(struct eap_sm *sm)
+{
+ if (sm->m && sm->currentMethod != EAP_TYPE_IDENTITY &&
+ sm->m->isSuccess(sm, sm->eap_method_priv)) {
+ wpa_printf(MSG_DEBUG, "EAP: getDecision: method succeeded -> "
+ "SUCCESS");
+ sm->update_user = TRUE;
+ return DECISION_SUCCESS;
+ }
+
+ if (sm->m && sm->m->isDone(sm, sm->eap_method_priv) &&
+ !sm->m->isSuccess(sm, sm->eap_method_priv)) {
+ wpa_printf(MSG_DEBUG, "EAP: getDecision: method failed -> "
+ "FAILURE");
+ sm->update_user = TRUE;
+ return DECISION_FAILURE;
+ }
+
+ if ((sm->user == NULL || sm->update_user) && sm->identity) {
+ if (eap_user_get(sm, sm->identity, sm->identity_len, 0) != 0) {
+ wpa_printf(MSG_DEBUG, "EAP: getDecision: user not "
+ "found from database -> FAILURE");
+ return DECISION_FAILURE;
+ }
+ sm->update_user = FALSE;
+ }
+
+ if (sm->user && sm->user_eap_method_index < EAP_MAX_METHODS &&
+ sm->user->methods[sm->user_eap_method_index] != EAP_TYPE_NONE) {
+ wpa_printf(MSG_DEBUG, "EAP: getDecision: another method "
+ "available -> CONTINUE");
+ return DECISION_CONTINUE;
+ }
+
+ if (sm->identity == NULL || sm->currentId == -1) {
+ wpa_printf(MSG_DEBUG, "EAP: getDecision: no identity known "
+ "yet -> CONTINUE");
+ return DECISION_CONTINUE;
+ }
+
+ wpa_printf(MSG_DEBUG, "EAP: getDecision: no more methods available -> "
+ "FAILURE");
+ return DECISION_FAILURE;
+}
+
+
+static Boolean eap_sm_Policy_doPickUp(struct eap_sm *sm, EapType method)
+{
+ return method == EAP_TYPE_IDENTITY ? TRUE : FALSE;
+}
+
+
+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;
+}
+
+
+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;
+}
+
+
+void eap_set_eapRespData(struct eap_sm *sm, const u8 *eapRespData,
+ size_t eapRespDataLen)
+{
+ if (sm == NULL)
+ return;
+ free(sm->eapRespData);
+ sm->eapRespData = malloc(eapRespDataLen);
+ if (sm->eapRespData == NULL)
+ return;
+ memcpy(sm->eapRespData, eapRespData, eapRespDataLen);
+ sm->eapRespDataLen = eapRespDataLen;
+ wpa_hexdump(MSG_MSGDUMP, "EAP: EAP-Response received",
+ eapRespData, eapRespDataLen);
+}
+
+
+static void eap_user_free(struct eap_user *user)
+{
+ if (user == NULL)
+ return;
+ free(user->password);
+ user->password = NULL;
+ free(user);
+}
+
+
+struct eap_sm * eap_sm_init(void *eapol_ctx, struct eapol_callbacks *eapol_cb,
+ struct eap_config *eap_conf)
+{
+ 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->MaxRetrans = 10;
+ sm->ssl_ctx = eap_conf->ssl_ctx;
+ sm->eap_sim_db_priv = eap_conf->eap_sim_db_priv;
+ sm->backend_auth = eap_conf->backend_auth;
+
+ wpa_printf(MSG_DEBUG, "EAP: State machine created");
+
+ return sm;
+}
+
+
+void eap_sm_deinit(struct eap_sm *sm)
+{
+ if (sm == NULL)
+ return;
+ wpa_printf(MSG_DEBUG, "EAP: State machine removed");
+ if (sm->m && sm->eap_method_priv)
+ sm->m->reset(sm, sm->eap_method_priv);
+ free(sm->eapReqData);
+ free(sm->eapKeyData);
+ free(sm->lastReqData);
+ free(sm->eapRespData);
+ free(sm->identity);
+ eap_user_free(sm->user);
+ free(sm);
+}
diff --git a/contrib/hostapd/eap.h b/contrib/hostapd/eap.h
new file mode 100644
index 000000000000..01f47dad12e4
--- /dev/null
+++ b/contrib/hostapd/eap.h
@@ -0,0 +1,83 @@
+#ifndef EAP_H
+#define EAP_H
+
+#include "defs.h"
+#include "eap_defs.h"
+
+struct eap_sm;
+
+#define EAP_MAX_METHODS 8
+struct eap_user {
+ u8 methods[EAP_MAX_METHODS];
+ u8 *password;
+ size_t password_len;
+ int phase2;
+ int force_version;
+};
+
+enum eapol_bool_var {
+ EAPOL_eapSuccess, EAPOL_eapRestart, EAPOL_eapFail, EAPOL_eapResp,
+ EAPOL_eapReq, EAPOL_eapNoReq, EAPOL_portEnabled, EAPOL_eapTimeout
+};
+
+struct eapol_callbacks {
+ Boolean (*get_bool)(void *ctx, enum eapol_bool_var variable);
+ void (*set_bool)(void *ctx, enum eapol_bool_var variable,
+ Boolean value);
+ void (*set_eapReqData)(void *ctx, const u8 *eapReqData,
+ size_t eapReqDataLen);
+ void (*set_eapKeyData)(void *ctx, const u8 *eapKeyData,
+ size_t eapKeyDataLen);
+ int (*get_eap_user)(void *ctx, const u8 *identity, size_t identity_len,
+ int phase2, struct eap_user *user);
+};
+
+struct eap_config {
+ void *ssl_ctx;
+ void *eap_sim_db_priv;
+ Boolean backend_auth;
+};
+
+
+#ifdef EAP_AUTHENTICATOR
+
+struct eap_sm * eap_sm_init(void *eapol_ctx, struct eapol_callbacks *eapol_cb,
+ struct eap_config *eap_conf);
+void eap_sm_deinit(struct eap_sm *sm);
+int eap_sm_step(struct eap_sm *sm);
+u8 eap_get_type(const char *name);
+void eap_set_eapRespData(struct eap_sm *sm, const u8 *eapRespData,
+ size_t eapRespDataLen);
+
+#else /* EAP_AUTHENTICATOR */
+
+static inline struct eap_sm * eap_sm_init(void *eapol_ctx,
+ struct eapol_callbacks *eapol_cb,
+ struct eap_config *eap_conf)
+{
+ return NULL;
+}
+
+static inline void eap_sm_deinit(struct eap_sm *sm)
+{
+}
+
+static inline int eap_sm_step(struct eap_sm *sm)
+{
+ return 0;
+}
+
+static inline u8 eap_get_type(const char *name)
+{
+ return EAP_TYPE_NONE;
+}
+
+static inline void eap_set_eapRespData(struct eap_sm *sm,
+ const u8 *eapRespData,
+ size_t eapRespDataLen)
+{
+}
+
+#endif /* EAP_AUTHENTICATOR */
+
+#endif /* EAP_H */
diff --git a/contrib/hostapd/eap_defs.h b/contrib/hostapd/eap_defs.h
new file mode 100644
index 000000000000..effe665f0919
--- /dev/null
+++ b/contrib/hostapd/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/hostapd/eap_gtc.c b/contrib/hostapd/eap_gtc.c
new file mode 100644
index 000000000000..674f83735f1f
--- /dev/null
+++ b/contrib/hostapd/eap_gtc.c
@@ -0,0 +1,158 @@
+/*
+ * hostapd / EAP-GTC (RFC 3748)
+ * 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>
+#include <stdio.h>
+#include <string.h>
+#include <netinet/in.h>
+
+#include "hostapd.h"
+#include "common.h"
+#include "eap_i.h"
+
+
+struct eap_gtc_data {
+ enum { CONTINUE, SUCCESS, FAILURE } state;
+};
+
+
+static void * eap_gtc_init(struct eap_sm *sm)
+{
+ struct eap_gtc_data *data;
+
+ data = malloc(sizeof(*data));
+ if (data == NULL)
+ return data;
+ memset(data, 0, sizeof(*data));
+ data->state = CONTINUE;
+
+ return data;
+}
+
+
+static void eap_gtc_reset(struct eap_sm *sm, void *priv)
+{
+ struct eap_gtc_data *data = priv;
+ free(data);
+}
+
+
+static u8 * eap_gtc_buildReq(struct eap_sm *sm, void *priv, int id,
+ size_t *reqDataLen)
+{
+ struct eap_gtc_data *data = priv;
+ struct eap_hdr *req;
+ u8 *pos;
+ char *msg = "Password";
+ size_t msg_len;
+
+ msg_len = strlen(msg);
+ *reqDataLen = sizeof(*req) + 1 + msg_len;
+ req = malloc(*reqDataLen);
+ if (req == NULL) {
+ wpa_printf(MSG_ERROR, "EAP-GTC: Failed to allocate memory for "
+ "request");
+ data->state = FAILURE;
+ return NULL;
+ }
+
+ req->code = EAP_CODE_REQUEST;
+ req->identifier = id;
+ req->length = htons(*reqDataLen);
+ pos = (u8 *) (req + 1);
+ *pos++ = EAP_TYPE_GTC;
+ memcpy(pos, msg, msg_len);
+
+ data->state = CONTINUE;
+
+ return (u8 *) req;
+}
+
+
+static Boolean eap_gtc_check(struct eap_sm *sm, void *priv,
+ u8 *respData, size_t respDataLen)
+{
+ struct eap_hdr *resp;
+ u8 *pos;
+ size_t len;
+
+ resp = (struct eap_hdr *) respData;
+ pos = (u8 *) (resp + 1);
+ if (respDataLen < sizeof(*resp) + 2 || *pos != EAP_TYPE_GTC ||
+ (len = ntohs(resp->length)) > respDataLen) {
+ wpa_printf(MSG_INFO, "EAP-GTC: Invalid frame");
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+static void eap_gtc_process(struct eap_sm *sm, void *priv,
+ u8 *respData, size_t respDataLen)
+{
+ struct eap_gtc_data *data = priv;
+ struct eap_hdr *resp;
+ u8 *pos;
+ size_t rlen;
+
+ if (sm->user == NULL || sm->user->password == NULL) {
+ wpa_printf(MSG_INFO, "EAP-GTC: Password not configured");
+ data->state = FAILURE;
+ return;
+ }
+
+ resp = (struct eap_hdr *) respData;
+ pos = (u8 *) (resp + 1);
+ pos++;
+ rlen = ntohs(resp->length) - sizeof(*resp) - 1;
+ wpa_hexdump_key(MSG_MSGDUMP, "EAP-GTC: Response", pos, rlen);
+
+ if (rlen != sm->user->password_len ||
+ memcmp(pos, sm->user->password, rlen) != 0) {
+ wpa_printf(MSG_DEBUG, "EAP-GTC: Done - Failure");
+ data->state = FAILURE;
+ } else {
+ wpa_printf(MSG_DEBUG, "EAP-GTC: Done - Success");
+ data->state = SUCCESS;
+ }
+}
+
+
+static Boolean eap_gtc_isDone(struct eap_sm *sm, void *priv)
+{
+ struct eap_gtc_data *data = priv;
+ return data->state != CONTINUE;
+}
+
+
+static Boolean eap_gtc_isSuccess(struct eap_sm *sm, void *priv)
+{
+ struct eap_gtc_data *data = priv;
+ return data->state == SUCCESS;
+}
+
+
+const struct eap_method eap_method_gtc =
+{
+ .method = EAP_TYPE_GTC,
+ .name = "GTC",
+ .init = eap_gtc_init,
+ .reset = eap_gtc_reset,
+ .buildReq = eap_gtc_buildReq,
+ .check = eap_gtc_check,
+ .process = eap_gtc_process,
+ .isDone = eap_gtc_isDone,
+ .isSuccess = eap_gtc_isSuccess,
+};
diff --git a/contrib/hostapd/eap_i.h b/contrib/hostapd/eap_i.h
new file mode 100644
index 000000000000..39b02579ef25
--- /dev/null
+++ b/contrib/hostapd/eap_i.h
@@ -0,0 +1,110 @@
+#ifndef EAP_I_H
+#define EAP_I_H
+
+#include "eap.h"
+
+/* draft-ietf-eap-statemachine-05.pdf - EAP Standalone Authenticator */
+
+struct eap_method {
+ EapType method;
+ const char *name;
+
+ void * (*init)(struct eap_sm *sm);
+ void * (*initPickUp)(struct eap_sm *sm);
+ void (*reset)(struct eap_sm *sm, void *priv);
+
+ u8 * (*buildReq)(struct eap_sm *sm, void *priv, int id,
+ size_t *reqDataLen);
+ int (*getTimeout)(struct eap_sm *sm, void *priv);
+ Boolean (*check)(struct eap_sm *sm, void *priv,
+ u8 *respData, size_t respDataLen);
+ void (*process)(struct eap_sm *sm, void *priv,
+ u8 *respData, size_t respDataLen);
+ Boolean (*isDone)(struct eap_sm *sm, void *priv);
+ u8 * (*getKey)(struct eap_sm *sm, void *priv, size_t *len);
+ /* isSuccess is not specified in draft-ietf-eap-statemachine-05.txt,
+ * but it is useful in implementing Policy.getDecision() */
+ Boolean (*isSuccess)(struct eap_sm *sm, void *priv);
+};
+
+struct eap_sm {
+ enum {
+ EAP_DISABLED, EAP_INITIALIZE, EAP_IDLE, EAP_RECEIVED,
+ EAP_INTEGRITY_CHECK, EAP_METHOD_RESPONSE, EAP_METHOD_REQUEST,
+ EAP_PROPOSE_METHOD, EAP_SELECT_ACTION, EAP_SEND_REQUEST,
+ EAP_DISCARD, EAP_NAK, EAP_RETRANSMIT, EAP_SUCCESS, EAP_FAILURE,
+ EAP_TIMEOUT_FAILURE, EAP_PICK_UP_METHOD
+ } EAP_state;
+
+ /* Constants */
+ int MaxRetrans;
+
+ /* Lower layer to standalone authenticator variables */
+ /* eapResp: eapol_sm->be_auth.eapResp */
+ /* portEnabled: eapol_sm->portEnabled */
+ /* eapRestart: eapol_sm->auth_pae.eapRestart */
+ u8 *eapRespData;
+ size_t eapRespDataLen;
+ int retransWhile;
+ int eapSRTT;
+ int eapRTTVAR;
+
+ /* Standalone authenticator to lower layer variables */
+ /* eapReq: eapol_sm->be_auth.eapReq */
+ /* eapNoReq: eapol_sm->be_auth.eapNoReq */
+ /* eapSuccess: eapol_sm->eapSuccess */
+ /* eapFail: eapol_sm->eapFail */
+ /* eapTimeout: eapol_sm->eapTimeout */
+ u8 *eapReqData;
+ size_t eapReqDataLen;
+ u8 *eapKeyData; /* also eapKeyAvailable (boolean) */
+ size_t eapKeyDataLen;
+
+ /* Standalone authenticator state machine local variables */
+
+ /* Long-term (maintained betwen packets) */
+ EapType currentMethod;
+ int currentId;
+ enum {
+ METHOD_PROPOSED, METHOD_CONTINUE, METHOD_END
+ } methodState;
+ int retransCount;
+ u8 *lastReqData;
+ size_t lastReqDataLen;
+ int methodTimeout;
+
+ /* Short-term (not maintained between packets) */
+ Boolean rxResp;
+ int respId;
+ EapType respMethod;
+ Boolean ignore;
+ enum {
+ DECISION_SUCCESS, DECISION_FAILURE, DECISION_CONTINUE
+ } decision;
+
+ /* Miscellaneous variables */
+ const struct eap_method *m; /* selected EAP method */
+ /* not defined in draft-ietf-eap-statemachine-02 */
+ Boolean changed;
+ void *eapol_ctx, *msg_ctx;
+ struct eapol_callbacks *eapol_cb;
+ void *eap_method_priv;
+ u8 *identity;
+ size_t identity_len;
+ int lastId; /* Identifier used in the last EAP-Packet */
+ struct eap_user *user;
+ int user_eap_method_index;
+ int init_phase2;
+ void *ssl_ctx;
+ enum { TLV_REQ_NONE, TLV_REQ_SUCCESS, TLV_REQ_FAILURE } tlv_request;
+ void *eap_sim_db_priv;
+ Boolean backend_auth;
+ Boolean update_user;
+};
+
+const struct eap_method * eap_sm_get_eap_methods(int method);
+int eap_user_get(struct eap_sm *sm, const u8 *identity, size_t identity_len,
+ int phase2);
+void eap_sm_process_nak(struct eap_sm *sm, u8 *nak_list, size_t len);
+
+#endif /* EAP_I_H */
diff --git a/contrib/hostapd/eap_identity.c b/contrib/hostapd/eap_identity.c
new file mode 100644
index 000000000000..31aedc4e8255
--- /dev/null
+++ b/contrib/hostapd/eap_identity.c
@@ -0,0 +1,174 @@
+/*
+ * hostapd / EAP-Identity
+ * 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 <netinet/in.h>
+
+#include "hostapd.h"
+#include "common.h"
+#include "eap_i.h"
+
+
+struct eap_identity_data {
+ enum { CONTINUE, SUCCESS, FAILURE } state;
+ int pick_up;
+};
+
+
+static void * eap_identity_init(struct eap_sm *sm)
+{
+ struct eap_identity_data *data;
+
+ data = malloc(sizeof(*data));
+ if (data == NULL)
+ return data;
+ memset(data, 0, sizeof(*data));
+ data->state = CONTINUE;
+
+ return data;
+}
+
+
+static void * eap_identity_initPickUp(struct eap_sm *sm)
+{
+ struct eap_identity_data *data;
+ data = eap_identity_init(sm);
+ if (data) {
+ data->pick_up = 1;
+ }
+ return data;
+}
+
+
+static void eap_identity_reset(struct eap_sm *sm, void *priv)
+{
+ struct eap_identity_data *data = priv;
+ free(data);
+}
+
+
+static u8 * eap_identity_buildReq(struct eap_sm *sm, void *priv, int id,
+ size_t *reqDataLen)
+{
+ struct eap_identity_data *data = priv;
+ struct eap_hdr *req;
+ u8 *pos;
+
+ *reqDataLen = sizeof(*req) + 1;
+ req = malloc(*reqDataLen);
+ if (req == NULL) {
+ wpa_printf(MSG_ERROR, "EAP-Identity: Failed to allocate "
+ "memory for request");
+ data->state = FAILURE;
+ return NULL;
+ }
+
+ req->code = EAP_CODE_REQUEST;
+ req->identifier = id;
+ req->length = htons(*reqDataLen);
+ pos = (u8 *) (req + 1);
+ *pos = EAP_TYPE_IDENTITY;
+
+ return (u8 *) req;
+}
+
+
+static Boolean eap_identity_check(struct eap_sm *sm, void *priv,
+ u8 *respData, size_t respDataLen)
+{
+ struct eap_hdr *resp;
+ u8 *pos;
+ size_t len;
+
+ resp = (struct eap_hdr *) respData;
+ pos = (u8 *) (resp + 1);
+ if (respDataLen < sizeof(*resp) + 1 || *pos != EAP_TYPE_IDENTITY ||
+ (len = ntohs(resp->length)) > respDataLen) {
+ wpa_printf(MSG_INFO, "EAP-Identity: Invalid frame");
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+static void eap_identity_process(struct eap_sm *sm, void *priv,
+ u8 *respData, size_t respDataLen)
+{
+ struct eap_identity_data *data = priv;
+ struct eap_hdr *resp;
+ u8 *pos;
+ int len;
+
+ if (data->pick_up) {
+ if (eap_identity_check(sm, data, respData, respDataLen)) {
+ wpa_printf(MSG_DEBUG, "EAP-Identity: failed to pick "
+ "up already started negotiation");
+ data->state = FAILURE;
+ return;
+ }
+ data->pick_up = 0;
+ }
+
+ resp = (struct eap_hdr *) respData;
+ len = ntohs(resp->length);
+ pos = (u8 *) (resp + 1);
+ pos++;
+ len -= sizeof(*resp) + 1;
+ if (len < 0) {
+ data->state = FAILURE;
+ return;
+ }
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP-Identity: Peer identity", pos, len);
+ free(sm->identity);
+ sm->identity = malloc(len);
+ if (sm->identity == NULL) {
+ data->state = FAILURE;
+ } else {
+ memcpy(sm->identity, pos, len);
+ sm->identity_len = len;
+ data->state = SUCCESS;
+ }
+}
+
+
+static Boolean eap_identity_isDone(struct eap_sm *sm, void *priv)
+{
+ struct eap_identity_data *data = priv;
+ return data->state != CONTINUE;
+}
+
+
+static Boolean eap_identity_isSuccess(struct eap_sm *sm, void *priv)
+{
+ struct eap_identity_data *data = priv;
+ return data->state == SUCCESS;
+}
+
+
+const struct eap_method eap_method_identity =
+{
+ .method = EAP_TYPE_IDENTITY,
+ .name = "Identity",
+ .init = eap_identity_init,
+ .initPickUp = eap_identity_initPickUp,
+ .reset = eap_identity_reset,
+ .buildReq = eap_identity_buildReq,
+ .check = eap_identity_check,
+ .process = eap_identity_process,
+ .isDone = eap_identity_isDone,
+ .isSuccess = eap_identity_isSuccess,
+};
diff --git a/contrib/hostapd/eap_md5.c b/contrib/hostapd/eap_md5.c
new file mode 100644
index 000000000000..5675c500d234
--- /dev/null
+++ b/contrib/hostapd/eap_md5.c
@@ -0,0 +1,180 @@
+/*
+ * hostapd / EAP-MD5 server
+ * 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 <netinet/in.h>
+
+#include "hostapd.h"
+#include "common.h"
+#include "eap_i.h"
+#include "md5.h"
+
+
+#define CHALLENGE_LEN 16
+
+struct eap_md5_data {
+ u8 challenge[CHALLENGE_LEN];
+ enum { CONTINUE, SUCCESS, FAILURE } state;
+};
+
+
+static void * eap_md5_init(struct eap_sm *sm)
+{
+ struct eap_md5_data *data;
+
+ data = malloc(sizeof(*data));
+ if (data == NULL)
+ return data;
+ memset(data, 0, sizeof(*data));
+ data->state = CONTINUE;
+
+ return data;
+}
+
+
+static void eap_md5_reset(struct eap_sm *sm, void *priv)
+{
+ struct eap_md5_data *data = priv;
+ free(data);
+}
+
+
+static u8 * eap_md5_buildReq(struct eap_sm *sm, void *priv, int id,
+ size_t *reqDataLen)
+{
+ struct eap_md5_data *data = priv;
+ struct eap_hdr *req;
+ u8 *pos;
+
+ if (hostapd_get_rand(data->challenge, CHALLENGE_LEN)) {
+ wpa_printf(MSG_ERROR, "EAP-MD5: Failed to get random data");
+ data->state = FAILURE;
+ return NULL;
+ }
+
+ *reqDataLen = sizeof(*req) + 2 + CHALLENGE_LEN;
+ req = malloc(*reqDataLen);
+ if (req == NULL) {
+ wpa_printf(MSG_ERROR, "EAP-MD5: Failed to allocate memory for "
+ "request");
+ data->state = FAILURE;
+ return NULL;
+ }
+
+ req->code = EAP_CODE_REQUEST;
+ req->identifier = id;
+ req->length = htons(*reqDataLen);
+ pos = (u8 *) (req + 1);
+ *pos++ = EAP_TYPE_MD5;
+ *pos++ = CHALLENGE_LEN;
+ memcpy(pos, data->challenge, CHALLENGE_LEN);
+ wpa_hexdump(MSG_MSGDUMP, "EAP-MD5: Challenge", pos, CHALLENGE_LEN);
+
+ data->state = CONTINUE;
+
+ return (u8 *) req;
+}
+
+
+static Boolean eap_md5_check(struct eap_sm *sm, void *priv,
+ u8 *respData, size_t respDataLen)
+{
+ struct eap_hdr *resp;
+ u8 *pos;
+ size_t len;
+
+ resp = (struct eap_hdr *) respData;
+ pos = (u8 *) (resp + 1);
+ if (respDataLen < sizeof(*resp) + 2 || *pos != EAP_TYPE_MD5 ||
+ (len = ntohs(resp->length)) > respDataLen) {
+ wpa_printf(MSG_INFO, "EAP-MD5: Invalid frame");
+ return TRUE;
+ }
+ pos++;
+ if (*pos != MD5_MAC_LEN ||
+ sizeof(*resp) + 2 + MD5_MAC_LEN > len) {
+ wpa_printf(MSG_INFO, "EAP-MD5: Invalid response "
+ "(response_len=%d respDataLen=%lu",
+ *pos, (unsigned long) respDataLen);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+static void eap_md5_process(struct eap_sm *sm, void *priv,
+ u8 *respData, size_t respDataLen)
+{
+ struct eap_md5_data *data = priv;
+ struct eap_hdr *resp;
+ u8 *pos;
+ MD5_CTX context;
+ u8 hash[MD5_MAC_LEN];
+
+ if (sm->user == NULL || sm->user->password == NULL) {
+ wpa_printf(MSG_INFO, "EAP-MD5: Password not configured");
+ data->state = FAILURE;
+ return;
+ }
+
+ resp = (struct eap_hdr *) respData;
+ pos = (u8 *) (resp + 1);
+ pos += 2; /* Skip type and len */
+ wpa_hexdump(MSG_MSGDUMP, "EAP-MD5: Response", pos, MD5_MAC_LEN);
+
+ MD5Init(&context);
+ MD5Update(&context, &resp->identifier, 1);
+ MD5Update(&context, sm->user->password, sm->user->password_len);
+ MD5Update(&context, data->challenge, CHALLENGE_LEN);
+ MD5Final(hash, &context);
+
+ if (memcmp(hash, pos, MD5_MAC_LEN) == 0) {
+ wpa_printf(MSG_DEBUG, "EAP-MD5: Done - Success");
+ data->state = SUCCESS;
+ } else {
+ wpa_printf(MSG_DEBUG, "EAP-MD5: Done - Failure");
+ data->state = FAILURE;
+ }
+}
+
+
+static Boolean eap_md5_isDone(struct eap_sm *sm, void *priv)
+{
+ struct eap_md5_data *data = priv;
+ return data->state != CONTINUE;
+}
+
+
+static Boolean eap_md5_isSuccess(struct eap_sm *sm, void *priv)
+{
+ struct eap_md5_data *data = priv;
+ return data->state == SUCCESS;
+}
+
+
+const struct eap_method eap_method_md5 =
+{
+ .method = EAP_TYPE_MD5,
+ .name = "MD5",
+ .init = eap_md5_init,
+ .reset = eap_md5_reset,
+ .buildReq = eap_md5_buildReq,
+ .check = eap_md5_check,
+ .process = eap_md5_process,
+ .isDone = eap_md5_isDone,
+ .isSuccess = eap_md5_isSuccess,
+};
diff --git a/contrib/hostapd/eap_mschapv2.c b/contrib/hostapd/eap_mschapv2.c
new file mode 100644
index 000000000000..5cbf6eb71289
--- /dev/null
+++ b/contrib/hostapd/eap_mschapv2.c
@@ -0,0 +1,483 @@
+/*
+ * hostapd / EAP-MSCHAPv2 (draft-kamath-pppext-eap-mschapv2-00.txt) server
+ * 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 <netinet/in.h>
+
+#include "hostapd.h"
+#include "common.h"
+#include "eap_i.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; /* must be changed for challenges, but not for
+ * success/failure */
+ 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 CHALLENGE_LEN 16
+
+struct eap_mschapv2_data {
+ u8 auth_challenge[CHALLENGE_LEN];
+ u8 auth_response[20];
+ enum { CHALLENGE, SUCCESS_REQ, FAILURE_REQ, SUCCESS, FAILURE } state;
+ u8 resp_mschapv2_id;
+};
+
+
+static void * eap_mschapv2_init(struct eap_sm *sm)
+{
+ struct eap_mschapv2_data *data;
+
+ data = malloc(sizeof(*data));
+ if (data == NULL)
+ return data;
+ memset(data, 0, sizeof(*data));
+ data->state = CHALLENGE;
+
+ return data;
+}
+
+
+static void eap_mschapv2_reset(struct eap_sm *sm, void *priv)
+{
+ struct eap_mschapv2_data *data = priv;
+ free(data);
+}
+
+
+static u8 * eap_mschapv2_build_challenge(struct eap_sm *sm,
+ struct eap_mschapv2_data *data,
+ int id, size_t *reqDataLen)
+{
+ struct eap_mschapv2_hdr *req;
+ u8 *pos;
+ char *name = "hostapd"; /* TODO: make this configurable */
+
+ if (hostapd_get_rand(data->auth_challenge, CHALLENGE_LEN)) {
+ wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to get random "
+ "data");
+ data->state = FAILURE;
+ return NULL;
+ }
+
+ *reqDataLen = sizeof(*req) + 1 + CHALLENGE_LEN + strlen(name);
+ req = malloc(*reqDataLen);
+ if (req == NULL) {
+ wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to allocate memory"
+ " for request");
+ data->state = FAILURE;
+ return NULL;
+ }
+
+ req->code = EAP_CODE_REQUEST;
+ req->identifier = id;
+ req->length = htons(*reqDataLen);
+ req->type = EAP_TYPE_MSCHAPV2;
+ req->op_code = MSCHAPV2_OP_CHALLENGE;
+ req->mschapv2_id = id;
+ req->ms_length[0] = (*reqDataLen - 5) >> 8;
+ req->ms_length[1] = (*reqDataLen - 5) & 0xff;
+ pos = (u8 *) (req + 1);
+ *pos++ = CHALLENGE_LEN;
+ memcpy(pos, data->auth_challenge, CHALLENGE_LEN);
+ wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: Challenge", pos,
+ CHALLENGE_LEN);
+ pos += CHALLENGE_LEN;
+ memcpy(pos, name, strlen(name));
+
+ return (u8 *) req;
+}
+
+
+static u8 * eap_mschapv2_build_success_req(struct eap_sm *sm,
+ struct eap_mschapv2_data *data,
+ int id, size_t *reqDataLen)
+{
+ struct eap_mschapv2_hdr *req;
+ u8 *pos, *msg, *end;
+ char *message = "OK";
+ size_t msg_len;
+ int i;
+
+ msg_len = 2 + 2 * sizeof(data->auth_response) + 3 + strlen(message);
+ *reqDataLen = sizeof(*req) + msg_len;
+ req = malloc(*reqDataLen + 1);
+ if (req == NULL) {
+ wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to allocate memory"
+ " for request");
+ data->state = FAILURE;
+ return NULL;
+ }
+
+ req->code = EAP_CODE_REQUEST;
+ req->identifier = id;
+ req->length = htons(*reqDataLen);
+ req->type = EAP_TYPE_MSCHAPV2;
+ req->op_code = MSCHAPV2_OP_SUCCESS;
+ req->mschapv2_id = data->resp_mschapv2_id;
+ req->ms_length[0] = (*reqDataLen - 5) >> 8;
+ req->ms_length[1] = (*reqDataLen - 5) & 0xff;
+
+ msg = pos = (u8 *) (req + 1);
+ end = ((u8 *) req) + *reqDataLen + 1;
+
+ pos += snprintf((char *) pos, end - pos, "S=");
+ for (i = 0; i < sizeof(data->auth_response); i++) {
+ pos += snprintf((char *) pos, end - pos, "%02X",
+ data->auth_response[i]);
+ }
+ pos += snprintf((char *) pos, end - pos, " M=%s", message);
+
+ wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: Success Request Message",
+ msg, msg_len);
+
+ return (u8 *) req;
+}
+
+
+static u8 * eap_mschapv2_build_failure_req(struct eap_sm *sm,
+ struct eap_mschapv2_data *data,
+ int id, size_t *reqDataLen)
+{
+ struct eap_mschapv2_hdr *req;
+ u8 *pos;
+ char *message = "E=691 R=0 C=00000000000000000000000000000000 V=3 "
+ "M=FAILED";
+ size_t msg_len;
+
+ msg_len = strlen(message);
+ *reqDataLen = sizeof(*req) + msg_len;
+ req = malloc(*reqDataLen + 1);
+ if (req == NULL) {
+ wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to allocate memory"
+ " for request");
+ data->state = FAILURE;
+ return NULL;
+ }
+
+ req->code = EAP_CODE_REQUEST;
+ req->identifier = id;
+ req->length = htons(*reqDataLen);
+ req->type = EAP_TYPE_MSCHAPV2;
+ req->op_code = MSCHAPV2_OP_FAILURE;
+ req->mschapv2_id = data->resp_mschapv2_id;
+ req->ms_length[0] = (*reqDataLen - 5) >> 8;
+ req->ms_length[1] = (*reqDataLen - 5) & 0xff;
+
+ pos = (u8 *) (req + 1);
+ memcpy(pos, message, msg_len);
+
+ wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: Failure Request Message",
+ (u8 *) message, msg_len);
+
+ return (u8 *) req;
+}
+
+
+static u8 * eap_mschapv2_buildReq(struct eap_sm *sm, void *priv, int id,
+ size_t *reqDataLen)
+{
+ struct eap_mschapv2_data *data = priv;
+
+ switch (data->state) {
+ case CHALLENGE:
+ return eap_mschapv2_build_challenge(sm, data, id, reqDataLen);
+ case SUCCESS_REQ:
+ return eap_mschapv2_build_success_req(sm, data, id,
+ reqDataLen);
+ case FAILURE_REQ:
+ return eap_mschapv2_build_failure_req(sm, data, id,
+ reqDataLen);
+ default:
+ wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Unknown state %d in "
+ "buildReq", data->state);
+ break;
+ }
+ return NULL;
+}
+
+
+static Boolean eap_mschapv2_check(struct eap_sm *sm, void *priv,
+ u8 *respData, size_t respDataLen)
+{
+ struct eap_mschapv2_data *data = priv;
+ struct eap_mschapv2_hdr *resp;
+ u8 *pos;
+ size_t len;
+
+ resp = (struct eap_mschapv2_hdr *) respData;
+ pos = (u8 *) (resp + 1);
+ if (respDataLen < 6 || resp->type != EAP_TYPE_MSCHAPV2 ||
+ (len = ntohs(resp->length)) > respDataLen) {
+ wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Invalid frame");
+ return TRUE;
+ }
+
+ if (data->state == CHALLENGE &&
+ resp->op_code != MSCHAPV2_OP_RESPONSE) {
+ wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Expected Response - "
+ "ignore op %d", resp->op_code);
+ return TRUE;
+ }
+
+ if (data->state == SUCCESS_REQ &&
+ resp->op_code != MSCHAPV2_OP_SUCCESS &&
+ resp->op_code != MSCHAPV2_OP_FAILURE) {
+ wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Expected Success or "
+ "Failure - ignore op %d", resp->op_code);
+ return TRUE;
+ }
+
+ if (data->state == FAILURE_REQ &&
+ resp->op_code != MSCHAPV2_OP_FAILURE) {
+ wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Expected Failure "
+ "- ignore op %d", resp->op_code);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+static void eap_mschapv2_process_response(struct eap_sm *sm,
+ struct eap_mschapv2_data *data,
+ u8 *respData, size_t respDataLen)
+{
+ struct eap_mschapv2_hdr *resp;
+ u8 *pos;
+ u8 *peer_challenge, *nt_response, flags, *name;
+ size_t name_len;
+ u8 expected[24];
+ int i;
+ u8 *username, *user;
+ size_t username_len, user_len;
+
+ resp = (struct eap_mschapv2_hdr *) respData;
+ pos = (u8 *) (resp + 1);
+
+ if (respDataLen < sizeof(resp) + 1 + 49 ||
+ resp->op_code != MSCHAPV2_OP_RESPONSE ||
+ pos[0] != 49) {
+ wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: Invalid response",
+ respData, respDataLen);
+ data->state = FAILURE;
+ return;
+ }
+ data->resp_mschapv2_id = resp->mschapv2_id;
+ pos++;
+ peer_challenge = pos;
+ pos += 16 + 8;
+ nt_response = pos;
+ pos += 24;
+ flags = *pos++;
+ name = pos;
+ name_len = respData + respDataLen - name;
+
+ wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: Peer-Challenge",
+ peer_challenge, 16);
+ wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: NT-Response", nt_response, 24);
+ wpa_printf(MSG_MSGDUMP, "EAP-MSCHAPV2: Flags 0x%x", flags);
+ wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: Name", name, name_len);
+
+ /* MSCHAPv2 does not include optional domain name in the
+ * challenge-response calculation, so remove domain prefix
+ * (if present). */
+ username = sm->identity;
+ username_len = sm->identity_len;
+ for (i = 0; i < username_len; i++) {
+ if (username[i] == '\\') {
+ username_len -= i + 1;
+ username += i + 1;
+ break;
+ }
+ }
+
+ user = name;
+ user_len = name_len;
+ for (i = 0; i < user_len; i++) {
+ if (user[i] == '\\') {
+ user_len -= i + 1;
+ user += i + 1;
+ break;
+ }
+ }
+
+ if (username_len != user_len ||
+ memcmp(username, user, username_len) != 0) {
+ wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Mismatch in user names");
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Expected user "
+ "name", username, username_len);
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Received user "
+ "name", user, user_len);
+ data->state = FAILURE;
+ return;
+ }
+
+ wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: User name",
+ username, username_len);
+
+ generate_nt_response(data->auth_challenge, peer_challenge,
+ username, username_len,
+ sm->user->password, sm->user->password_len,
+ expected);
+
+ if (memcmp(nt_response, expected, 24) == 0) {
+ wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Correct NT-Response");
+ data->state = SUCCESS_REQ;
+
+
+ /* Authenticator response is not really needed yet, but
+ * calculate it here so that peer_challenge and username need
+ * not be saved. */
+ generate_authenticator_response(sm->user->password,
+ sm->user->password_len,
+ peer_challenge,
+ data->auth_challenge,
+ username, username_len,
+ nt_response,
+ data->auth_response);
+ } else {
+ wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: Expected NT-Response",
+ expected, 24);
+ wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Invalid NT-Response");
+ data->state = FAILURE_REQ;
+ }
+}
+
+
+static void eap_mschapv2_process_success_resp(struct eap_sm *sm,
+ struct eap_mschapv2_data *data,
+ u8 *respData, size_t respDataLen)
+{
+ struct eap_mschapv2_hdr *resp;
+
+ resp = (struct eap_mschapv2_hdr *) respData;
+
+ if (resp->op_code == MSCHAPV2_OP_SUCCESS) {
+ wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received Success Response"
+ " - authentication completed successfully");
+ data->state = SUCCESS;
+ } else {
+ wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Did not receive Success "
+ "Response - peer rejected authentication");
+ data->state = FAILURE;
+ }
+}
+
+
+static void eap_mschapv2_process_failure_resp(struct eap_sm *sm,
+ struct eap_mschapv2_data *data,
+ u8 *respData, size_t respDataLen)
+{
+ struct eap_mschapv2_hdr *resp;
+
+ resp = (struct eap_mschapv2_hdr *) respData;
+
+ if (resp->op_code == MSCHAPV2_OP_FAILURE) {
+ wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received Failure Response"
+ " - authentication failed");
+ } else {
+ wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Did not receive Failure "
+ "Response - authentication failed");
+ }
+
+ data->state = FAILURE;
+}
+
+
+static void eap_mschapv2_process(struct eap_sm *sm, void *priv,
+ u8 *respData, size_t respDataLen)
+{
+ struct eap_mschapv2_data *data = priv;
+
+ if (sm->user == NULL || sm->user->password == NULL) {
+ wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Password not configured");
+ data->state = FAILURE;
+ return;
+ }
+
+ switch (data->state) {
+ case CHALLENGE:
+ eap_mschapv2_process_response(sm, data, respData, respDataLen);
+ break;
+ case SUCCESS_REQ:
+ eap_mschapv2_process_success_resp(sm, data, respData,
+ respDataLen);
+ break;
+ case FAILURE_REQ:
+ eap_mschapv2_process_failure_resp(sm, data, respData,
+ respDataLen);
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Unknown state %d in "
+ "process", data->state);
+ break;
+ }
+}
+
+
+static Boolean eap_mschapv2_isDone(struct eap_sm *sm, void *priv)
+{
+ struct eap_mschapv2_data *data = priv;
+ return data->state == SUCCESS || data->state == FAILURE;
+}
+
+
+static Boolean eap_mschapv2_isSuccess(struct eap_sm *sm, void *priv)
+{
+ struct eap_mschapv2_data *data = priv;
+ return data->state == SUCCESS;
+}
+
+
+const struct eap_method eap_method_mschapv2 =
+{
+ .method = EAP_TYPE_MSCHAPV2,
+ .name = "MSCHAPV2",
+ .init = eap_mschapv2_init,
+ .reset = eap_mschapv2_reset,
+ .buildReq = eap_mschapv2_buildReq,
+ .check = eap_mschapv2_check,
+ .process = eap_mschapv2_process,
+ .isDone = eap_mschapv2_isDone,
+ .isSuccess = eap_mschapv2_isSuccess,
+};
diff --git a/contrib/hostapd/eap_peap.c b/contrib/hostapd/eap_peap.c
new file mode 100644
index 000000000000..aa91976e777a
--- /dev/null
+++ b/contrib/hostapd/eap_peap.c
@@ -0,0 +1,720 @@
+/*
+ * hostapd / 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 <netinet/in.h>
+
+#include "hostapd.h"
+#include "common.h"
+#include "eap_i.h"
+#include "eap_tls_common.h"
+#include "tls.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_reset(struct eap_sm *sm, void *priv);
+
+
+struct eap_peap_data {
+ struct eap_ssl_data ssl;
+ enum {
+ START, PHASE1, PHASE2_START, PHASE2_ID, PHASE2_METHOD,
+ PHASE2_TLV, SUCCESS_REQ, FAILURE_REQ, SUCCESS, FAILURE
+ } state;
+
+ int peap_version;
+ const struct eap_method *phase2_method;
+ void *phase2_priv;
+ int force_version;
+};
+
+
+static const char * eap_peap_state_txt(int state)
+{
+ switch (state) {
+ case START:
+ return "START";
+ case PHASE1:
+ return "PHASE1";
+ case PHASE2_START:
+ return "PHASE2_START";
+ case PHASE2_ID:
+ return "PHASE2_ID";
+ case PHASE2_METHOD:
+ return "PHASE2_METHOD";
+ case PHASE2_TLV:
+ return "PHASE2_TLV";
+ case SUCCESS_REQ:
+ return "SUCCESS_REQ";
+ case FAILURE_REQ:
+ return "FAILURE_REQ";
+ case SUCCESS:
+ return "SUCCESS";
+ case FAILURE:
+ return "FAILURE";
+ default:
+ return "Unknown?!";
+ }
+}
+
+
+static void eap_peap_state(struct eap_peap_data *data, int state)
+{
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: %s -> %s",
+ eap_peap_state_txt(data->state),
+ eap_peap_state_txt(state));
+ data->state = state;
+}
+
+
+static EapType eap_peap_req_success(struct eap_sm *sm,
+ struct eap_peap_data *data)
+{
+ if (data->state == FAILURE || data->state == FAILURE_REQ) {
+ eap_peap_state(data, FAILURE);
+ return EAP_TYPE_NONE;
+ }
+
+ if (data->peap_version == 0) {
+ sm->tlv_request = TLV_REQ_SUCCESS;
+ eap_peap_state(data, PHASE2_TLV);
+ return EAP_TYPE_TLV;
+ } else {
+ eap_peap_state(data, SUCCESS_REQ);
+ return EAP_TYPE_NONE;
+ }
+}
+
+
+static EapType eap_peap_req_failure(struct eap_sm *sm,
+ struct eap_peap_data *data)
+{
+ if (data->state == FAILURE || data->state == FAILURE_REQ ||
+ data->state == SUCCESS_REQ ||
+ (data->phase2_method &&
+ data->phase2_method->method == EAP_TYPE_TLV)) {
+ eap_peap_state(data, FAILURE);
+ return EAP_TYPE_NONE;
+ }
+
+ if (data->peap_version == 0) {
+ sm->tlv_request = TLV_REQ_FAILURE;
+ eap_peap_state(data, PHASE2_TLV);
+ return EAP_TYPE_TLV;
+ } else {
+ eap_peap_state(data, FAILURE_REQ);
+ return EAP_TYPE_NONE;
+ }
+}
+
+
+static void * eap_peap_init(struct eap_sm *sm)
+{
+ struct eap_peap_data *data;
+
+ data = malloc(sizeof(*data));
+ if (data == NULL)
+ return data;
+ memset(data, 0, sizeof(*data));
+ data->peap_version = EAP_PEAP_VERSION;
+ data->force_version = -1;
+ if (sm->user && sm->user->force_version >= 0) {
+ data->force_version = sm->user->force_version;
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: forcing version %d",
+ data->force_version);
+ data->peap_version = data->force_version;
+ }
+ data->state = START;
+
+ if (eap_tls_ssl_init(sm, &data->ssl, 0)) {
+ wpa_printf(MSG_INFO, "EAP-PEAP: Failed to initialize SSL.");
+ eap_peap_reset(sm, data);
+ return NULL;
+ }
+
+ return data;
+}
+
+
+static void eap_peap_reset(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->reset(sm, data->phase2_priv);
+ eap_tls_ssl_deinit(sm, &data->ssl);
+ free(data);
+}
+
+
+static u8 * eap_peap_build_start(struct eap_sm *sm, struct eap_peap_data *data,
+ int id, size_t *reqDataLen)
+{
+ struct eap_hdr *req;
+ u8 *pos;
+
+ *reqDataLen = sizeof(*req) + 2;
+ req = malloc(*reqDataLen);
+ if (req == NULL) {
+ wpa_printf(MSG_ERROR, "EAP-PEAP: Failed to allocate memory for"
+ " request");
+ eap_peap_state(data, FAILURE);
+ return NULL;
+ }
+
+ req->code = EAP_CODE_REQUEST;
+ req->identifier = id;
+ req->length = htons(*reqDataLen);
+ pos = (u8 *) (req + 1);
+ *pos++ = EAP_TYPE_PEAP;
+ *pos = EAP_TLS_FLAGS_START | data->peap_version;
+
+ eap_peap_state(data, PHASE1);
+
+ return (u8 *) req;
+}
+
+
+static u8 * eap_peap_build_req(struct eap_sm *sm, struct eap_peap_data *data,
+ int id, size_t *reqDataLen)
+{
+ int res;
+ u8 *req;
+
+ res = eap_tls_buildReq_helper(sm, &data->ssl, EAP_TYPE_PEAP,
+ data->peap_version, id, &req,
+ reqDataLen);
+
+ if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) {
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase1 done, starting "
+ "Phase2");
+ eap_peap_state(data, PHASE2_START);
+ }
+
+ if (res == 1)
+ return eap_tls_build_ack(reqDataLen, id, EAP_TYPE_PEAP,
+ data->peap_version);
+ return req;
+}
+
+
+static u8 * eap_peap_encrypt(struct eap_sm *sm, struct eap_peap_data *data,
+ int id, u8 *plain, size_t plain_len,
+ size_t *out_len)
+{
+ int res;
+ u8 *pos;
+ struct eap_hdr *req;
+
+ /* TODO: add support for fragmentation, if needed. This will need to
+ * add TLS Message Length field, if the frame is fragmented. */
+ req = malloc(sizeof(struct eap_hdr) + 2 + data->ssl.tls_out_limit);
+ if (req == NULL)
+ return NULL;
+
+ req->code = EAP_CODE_REQUEST;
+ req->identifier = id;
+
+ pos = (u8 *) (req + 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(req);
+ return NULL;
+ }
+
+ *out_len = sizeof(struct eap_hdr) + 2 + res;
+ req->length = host_to_be16(*out_len);
+ return (u8 *) req;
+}
+
+
+static u8 * eap_peap_build_phase2_req(struct eap_sm *sm,
+ struct eap_peap_data *data,
+ int id, size_t *reqDataLen)
+{
+ u8 *req, *buf, *encr_req;
+ size_t req_len;
+
+ buf = req = data->phase2_method->buildReq(sm, data->phase2_priv, id,
+ &req_len);
+ if (req == NULL)
+ return NULL;
+
+ wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: Encrypting Phase 2 data",
+ req, req_len);
+
+ if (data->peap_version == 0 &&
+ data->phase2_method->method != EAP_TYPE_TLV) {
+ req += sizeof(struct eap_hdr);
+ req_len -= sizeof(struct eap_hdr);
+ }
+
+ encr_req = eap_peap_encrypt(sm, data, id, req, req_len, reqDataLen);
+ free(buf);
+
+ return encr_req;
+}
+
+
+static u8 * eap_peap_build_phase2_term(struct eap_sm *sm,
+ struct eap_peap_data *data,
+ int id, size_t *reqDataLen, int success)
+{
+ u8 *encr_req;
+ size_t req_len;
+ struct eap_hdr *hdr;
+
+ req_len = sizeof(*hdr);
+ hdr = malloc(req_len);
+ if (hdr == NULL) {
+ return NULL;
+ }
+
+ memset(hdr, 0, req_len);
+ hdr->code = success ? EAP_CODE_SUCCESS : EAP_CODE_FAILURE;
+ hdr->identifier = id;
+ hdr->length = htons(req_len);
+
+ wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: Encrypting Phase 2 data",
+ (u8 *) hdr, req_len);
+
+ encr_req = eap_peap_encrypt(sm, data, id, (u8 *) hdr, req_len,
+ reqDataLen);
+ free(hdr);
+
+ return encr_req;
+}
+
+
+static u8 * eap_peap_buildReq(struct eap_sm *sm, void *priv, int id,
+ size_t *reqDataLen)
+{
+ struct eap_peap_data *data = priv;
+
+ switch (data->state) {
+ case START:
+ return eap_peap_build_start(sm, data, id, reqDataLen);
+ case PHASE1:
+ return eap_peap_build_req(sm, data, id, reqDataLen);
+ case PHASE2_ID:
+ case PHASE2_METHOD:
+ case PHASE2_TLV:
+ return eap_peap_build_phase2_req(sm, data, id, reqDataLen);
+ case SUCCESS_REQ:
+ return eap_peap_build_phase2_term(sm, data, id, reqDataLen, 1);
+ case FAILURE_REQ:
+ return eap_peap_build_phase2_term(sm, data, id, reqDataLen, 0);
+ default:
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: %s - unexpected state %d",
+ __func__, data->state);
+ return NULL;
+ }
+}
+
+
+static Boolean eap_peap_check(struct eap_sm *sm, void *priv,
+ u8 *respData, size_t respDataLen)
+{
+ struct eap_hdr *resp;
+ u8 *pos;
+ size_t len;
+
+ resp = (struct eap_hdr *) respData;
+ pos = (u8 *) (resp + 1);
+ if (respDataLen < sizeof(*resp) + 2 || *pos != EAP_TYPE_PEAP ||
+ (len = ntohs(resp->length)) > respDataLen) {
+ wpa_printf(MSG_INFO, "EAP-PEAP: Invalid frame");
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+static int eap_peap_phase2_init(struct eap_sm *sm, struct eap_peap_data *data,
+ u8 eap_type)
+{
+ if (data->phase2_priv && data->phase2_method) {
+ data->phase2_method->reset(sm, data->phase2_priv);
+ data->phase2_method = NULL;
+ data->phase2_priv = NULL;
+ }
+ data->phase2_method = eap_sm_get_eap_methods(eap_type);
+ if (!data->phase2_method)
+ return -1;
+
+ sm->init_phase2 = 1;
+ data->phase2_priv = data->phase2_method->init(sm);
+ sm->init_phase2 = 0;
+ return 0;
+}
+
+
+static void eap_peap_process_phase2_response(struct eap_sm *sm,
+ struct eap_peap_data *data,
+ u8 *in_data, size_t in_len)
+{
+ u8 next_type = EAP_TYPE_NONE;
+ struct eap_hdr *hdr;
+ u8 *pos;
+ size_t left;
+
+ if (data->phase2_priv == NULL) {
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: %s - Phase2 not "
+ "initialized?!", __func__);
+ return;
+ }
+
+ hdr = (struct eap_hdr *) in_data;
+ pos = (u8 *) (hdr + 1);
+ left = in_len - sizeof(*hdr);
+
+ if (in_len > sizeof(*hdr) && *pos == EAP_TYPE_NAK) {
+ wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Phase2 type Nak'ed; "
+ "allowed types", pos + 1, left - 1);
+ eap_sm_process_nak(sm, pos + 1, left - 1);
+ if (sm->user && sm->user_eap_method_index < EAP_MAX_METHODS &&
+ sm->user->methods[sm->user_eap_method_index] !=
+ EAP_TYPE_NONE) {
+ next_type =
+ sm->user->methods[sm->user_eap_method_index++];
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: try EAP type %d",
+ next_type);
+ } else {
+ next_type = eap_peap_req_failure(sm, data);
+ }
+ eap_peap_phase2_init(sm, data, next_type);
+ return;
+ }
+
+ if (data->phase2_method->check(sm, data->phase2_priv, in_data,
+ in_len)) {
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase2 check() asked to "
+ "ignore the packet");
+ return;
+ }
+
+ data->phase2_method->process(sm, data->phase2_priv, in_data, in_len);
+
+ if (!data->phase2_method->isDone(sm, data->phase2_priv))
+ return;
+
+
+ if (!data->phase2_method->isSuccess(sm, data->phase2_priv)) {
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase2 method failed");
+ next_type = eap_peap_req_failure(sm, data);
+ eap_peap_phase2_init(sm, data, next_type);
+ return;
+ }
+
+ switch (data->state) {
+ case PHASE2_ID:
+ if (eap_user_get(sm, sm->identity, sm->identity_len, 1) != 0) {
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP_PEAP: Phase2 "
+ "Identity not found in the user "
+ "database",
+ sm->identity, sm->identity_len);
+ next_type = eap_peap_req_failure(sm, data);
+ break;
+ }
+
+ eap_peap_state(data, PHASE2_METHOD);
+ next_type = sm->user->methods[0];
+ sm->user_eap_method_index = 1;
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: try EAP type %d", next_type);
+ break;
+ case PHASE2_METHOD:
+ next_type = eap_peap_req_success(sm, data);
+ break;
+ case PHASE2_TLV:
+ if (sm->tlv_request == TLV_REQ_SUCCESS ||
+ data->state == SUCCESS_REQ) {
+ eap_peap_state(data, SUCCESS);
+ } else {
+ eap_peap_state(data, FAILURE);
+ }
+ break;
+ case FAILURE:
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: %s - unexpected state %d",
+ __func__, data->state);
+ break;
+ }
+
+ eap_peap_phase2_init(sm, data, next_type);
+}
+
+
+static void eap_peap_process_phase2(struct eap_sm *sm,
+ struct eap_peap_data *data,
+ struct eap_hdr *resp,
+ u8 *in_data, size_t in_len)
+{
+ u8 *in_decrypted;
+ int buf_len, len_decrypted, len, res;
+ struct eap_hdr *hdr;
+
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: 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;
+
+ 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;
+ }
+
+ 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);
+ eap_peap_state(data, FAILURE);
+ return;
+ }
+
+ wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: Decrypted Phase 2 EAP",
+ in_decrypted, len_decrypted);
+
+ hdr = (struct eap_hdr *) in_decrypted;
+
+ if (data->peap_version == 0 && data->state != PHASE2_TLV) {
+ struct eap_hdr *nhdr = malloc(sizeof(struct eap_hdr) +
+ len_decrypted);
+ if (nhdr == NULL) {
+ free(in_decrypted);
+ return;
+ }
+ memcpy((u8 *) (nhdr + 1), in_decrypted, len_decrypted);
+ free(in_decrypted);
+ nhdr->code = resp->code;
+ nhdr->identifier = resp->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);
+ eap_peap_req_failure(sm, data);
+ return;
+ }
+ 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);
+ eap_peap_req_failure(sm, data);
+ return;
+ }
+ 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_RESPONSE:
+ eap_peap_process_phase2_response(sm, data, (u8 *) hdr, len);
+ break;
+ case EAP_CODE_SUCCESS:
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 Success");
+ if (data->state == SUCCESS_REQ) {
+ eap_peap_state(data, SUCCESS);
+ }
+ break;
+ case EAP_CODE_FAILURE:
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 Failure");
+ eap_peap_state(data, FAILURE);
+ break;
+ default:
+ wpa_printf(MSG_INFO, "EAP-PEAP: Unexpected code=%d in "
+ "Phase 2 EAP header", hdr->code);
+ break;
+ }
+
+ free(in_decrypted);
+ }
+
+
+static void eap_peap_process(struct eap_sm *sm, void *priv,
+ u8 *respData, size_t respDataLen)
+{
+ struct eap_peap_data *data = priv;
+ struct eap_hdr *resp;
+ u8 *pos, flags;
+ int left;
+ unsigned int tls_msg_len;
+ int peer_version;
+
+ resp = (struct eap_hdr *) respData;
+ pos = (u8 *) (resp + 1);
+ pos++;
+ flags = *pos++;
+ left = htons(resp->length) - sizeof(struct eap_hdr) - 2;
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: Received packet(len=%lu) - "
+ "Flags 0x%02x", (unsigned long) respDataLen, flags);
+ peer_version = flags & EAP_PEAP_VERSION_MASK;
+ if (data->force_version >= 0 && peer_version != data->force_version) {
+ wpa_printf(MSG_INFO, "EAP-PEAP: peer did not select the forced"
+ " version (forced=%d peer=%d) - reject",
+ data->force_version, peer_version);
+ eap_peap_state(data, FAILURE);
+ return;
+ }
+ if (peer_version < data->peap_version) {
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: peer ver=%d, own ver=%d; "
+ "use version %d",
+ peer_version, data->peap_version, peer_version);
+ data->peap_version = peer_version;
+
+ }
+ if (flags & EAP_TLS_FLAGS_LENGTH_INCLUDED) {
+ if (left < 4) {
+ wpa_printf(MSG_INFO, "EAP-PEAP: Short frame with TLS "
+ "length");
+ eap_peap_state(data, FAILURE);
+ return;
+ }
+ 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;
+ }
+
+ switch (data->state) {
+ case PHASE1:
+ if (eap_tls_process_helper(sm, &data->ssl, pos, left) < 0) {
+ wpa_printf(MSG_INFO, "EAP-PEAP: TLS processing "
+ "failed");
+ eap_peap_state(data, FAILURE);
+ }
+ break;
+ case PHASE2_START:
+ eap_peap_state(data, PHASE2_ID);
+ eap_peap_phase2_init(sm, data, EAP_TYPE_IDENTITY);
+ break;
+ case PHASE2_ID:
+ case PHASE2_METHOD:
+ case PHASE2_TLV:
+ eap_peap_process_phase2(sm, data, resp, pos, left);
+ break;
+ case SUCCESS_REQ:
+ eap_peap_state(data, SUCCESS);
+ break;
+ case FAILURE_REQ:
+ eap_peap_state(data, FAILURE);
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: Unexpected state %d in %s",
+ data->state, __func__);
+ break;
+ }
+}
+
+
+static Boolean eap_peap_isDone(struct eap_sm *sm, void *priv)
+{
+ struct eap_peap_data *data = priv;
+ return data->state == SUCCESS || data->state == FAILURE;
+}
+
+
+static u8 * eap_peap_getKey(struct eap_sm *sm, void *priv, size_t *len)
+{
+ struct eap_peap_data *data = priv;
+ u8 *eapKeyData;
+
+ if (data->state != SUCCESS)
+ return NULL;
+
+ /* TODO: PEAPv1 - different label in some cases */
+ eapKeyData = eap_tls_derive_key(sm, &data->ssl,
+ "client EAP encryption",
+ EAP_TLS_KEY_LEN);
+ if (eapKeyData) {
+ *len = EAP_TLS_KEY_LEN;
+ wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Derived key",
+ eapKeyData, EAP_TLS_KEY_LEN);
+ } else {
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: Failed to derive key");
+ }
+
+ return eapKeyData;
+}
+
+
+static Boolean eap_peap_isSuccess(struct eap_sm *sm, void *priv)
+{
+ struct eap_peap_data *data = priv;
+ return data->state == SUCCESS;
+}
+
+
+const struct eap_method eap_method_peap =
+{
+ .method = EAP_TYPE_PEAP,
+ .name = "PEAP",
+ .init = eap_peap_init,
+ .reset = eap_peap_reset,
+ .buildReq = eap_peap_buildReq,
+ .check = eap_peap_check,
+ .process = eap_peap_process,
+ .isDone = eap_peap_isDone,
+ .getKey = eap_peap_getKey,
+ .isSuccess = eap_peap_isSuccess,
+};
diff --git a/contrib/hostapd/eap_sim.c b/contrib/hostapd/eap_sim.c
new file mode 100644
index 000000000000..aade18161d1d
--- /dev/null
+++ b/contrib/hostapd/eap_sim.c
@@ -0,0 +1,431 @@
+/*
+ * hostapd / EAP-SIM (draft-haverinen-pppext-eap-sim-15.txt)
+ * Copyright (c) 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 <netinet/in.h>
+
+#include "hostapd.h"
+#include "common.h"
+#include "sha1.h"
+#include "eap_i.h"
+#include "eap_sim_common.h"
+#include "eap_sim_db.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
+
+#define EAP_SIM_MAX_CHAL 3
+
+struct eap_sim_data {
+ u8 mk[EAP_SIM_MK_LEN];
+ u8 nonce_mt[EAP_SIM_NONCE_MT_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 kc[EAP_SIM_MAX_CHAL][KC_LEN];
+ u8 sres[EAP_SIM_MAX_CHAL][SRES_LEN];
+ u8 rand[EAP_SIM_MAX_CHAL][GSM_RAND_LEN];
+ int num_chal;
+ enum { START, CHALLENGE, SUCCESS, FAILURE } state;
+};
+
+
+static const char * eap_sim_state_txt(int state)
+{
+ switch (state) {
+ case START:
+ return "START";
+ case CHALLENGE:
+ return "CHALLENGE";
+ case SUCCESS:
+ return "SUCCESS";
+ case FAILURE:
+ return "FAILURE";
+ default:
+ return "Unknown?!";
+ }
+}
+
+
+static void eap_sim_state(struct eap_sim_data *data, int state)
+{
+ wpa_printf(MSG_DEBUG, "EAP-SIM %s -> %s",
+ eap_sim_state_txt(data->state),
+ eap_sim_state_txt(state));
+ data->state = state;
+}
+
+
+static void * eap_sim_init(struct eap_sm *sm)
+{
+ struct eap_sim_data *data;
+
+ if (sm->eap_sim_db_priv == NULL) {
+ wpa_printf(MSG_WARNING, "EAP-SIM: eap_sim_db not configured");
+ return NULL;
+ }
+
+ data = malloc(sizeof(*data));
+ if (data == NULL)
+ return data;
+ memset(data, 0, sizeof(*data));
+ data->state = START;
+
+ return data;
+}
+
+
+static void eap_sim_reset(struct eap_sm *sm, void *priv)
+{
+ struct eap_sim_data *data = priv;
+ free(data);
+}
+
+
+static u8 * eap_sim_build_start(struct eap_sm *sm, struct eap_sim_data *data,
+ int id, size_t *reqDataLen)
+{
+ struct eap_sim_msg *msg;
+ u8 ver[2];
+
+ msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, EAP_TYPE_SIM,
+ EAP_SIM_SUBTYPE_START);
+ if (eap_sim_db_identity_known(sm->eap_sim_db_priv, sm->identity,
+ sm->identity_len)) {
+ eap_sim_msg_add(msg, EAP_SIM_AT_PERMANENT_ID_REQ, 0, NULL, 0);
+ }
+ ver[0] = 0;
+ ver[1] = EAP_SIM_VERSION;
+ eap_sim_msg_add(msg, EAP_SIM_AT_VERSION_LIST, sizeof(ver),
+ ver, sizeof(ver));
+ return eap_sim_msg_finish(msg, reqDataLen, NULL, NULL, 0);
+}
+
+
+static u8 * eap_sim_build_challenge(struct eap_sm *sm,
+ struct eap_sim_data *data,
+ int id, size_t *reqDataLen)
+{
+ struct eap_sim_msg *msg;
+
+ msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, EAP_TYPE_SIM,
+ EAP_SIM_SUBTYPE_CHALLENGE);
+ eap_sim_msg_add(msg, EAP_SIM_AT_RAND, 0, (u8 *) data->rand,
+ data->num_chal * GSM_RAND_LEN);
+ eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC);
+ return eap_sim_msg_finish(msg, reqDataLen, data->k_aut, data->nonce_mt,
+ EAP_SIM_NONCE_MT_LEN);
+}
+
+
+static u8 * eap_sim_buildReq(struct eap_sm *sm, void *priv, int id,
+ size_t *reqDataLen)
+{
+ struct eap_sim_data *data = priv;
+
+ switch (data->state) {
+ case START:
+ return eap_sim_build_start(sm, data, id, reqDataLen);
+ case CHALLENGE:
+ return eap_sim_build_challenge(sm, data, id, reqDataLen);
+ default:
+ wpa_printf(MSG_DEBUG, "EAP-SIM: Unknown state %d in "
+ "buildReq", data->state);
+ break;
+ }
+ return NULL;
+}
+
+
+static Boolean eap_sim_check(struct eap_sm *sm, void *priv,
+ u8 *respData, size_t respDataLen)
+{
+ struct eap_sim_data *data = priv;
+ struct eap_hdr *resp;
+ u8 *pos, subtype;
+ size_t len;
+
+ resp = (struct eap_hdr *) respData;
+ pos = (u8 *) (resp + 1);
+ if (respDataLen < sizeof(*resp) + 4 || *pos != EAP_TYPE_SIM ||
+ (len = ntohs(resp->length)) > respDataLen) {
+ wpa_printf(MSG_INFO, "EAP-SIM: Invalid frame");
+ return TRUE;
+ }
+ subtype = pos[1];
+
+ if (subtype == EAP_SIM_SUBTYPE_CLIENT_ERROR)
+ return FALSE;
+
+ switch (data->state) {
+ case START:
+ if (subtype != EAP_SIM_SUBTYPE_START) {
+ wpa_printf(MSG_INFO, "EAP-SIM: Unexpected response "
+ "subtype %d", subtype);
+ return TRUE;
+ }
+ break;
+ case CHALLENGE:
+ if (subtype != EAP_SIM_SUBTYPE_CHALLENGE) {
+ wpa_printf(MSG_INFO, "EAP-SIM: Unexpected response "
+ "subtype %d", subtype);
+ return TRUE;
+ }
+ break;
+ default:
+ wpa_printf(MSG_INFO, "EAP-SIM: Unexpected state (%d) for "
+ "processing a response", data->state);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+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,
+ const u8 *nonce_mt, int selected_version,
+ int num_chal, const u8 *kc)
+{
+ u8 sel_ver[2], ver_list[2];
+ const unsigned char *addr[5];
+ size_t len[5];
+
+ addr[0] = identity;
+ addr[1] = kc;
+ addr[2] = nonce_mt;
+ addr[3] = ver_list;
+ addr[4] = sel_ver;
+
+ len[0] = identity_len;
+ len[1] = num_chal * KC_LEN;
+ len[2] = EAP_SIM_NONCE_MT_LEN;
+ len[3] = sizeof(ver_list);
+ len[4] = sizeof(sel_ver);
+
+ ver_list[0] = 0;
+ ver_list[1] = EAP_SIM_VERSION;
+ sel_ver[0] = selected_version >> 8;
+ sel_ver[1] = 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);
+}
+
+
+static void eap_sim_process_start(struct eap_sm *sm,
+ struct eap_sim_data *data,
+ u8 *respData, size_t respDataLen,
+ struct eap_sim_attrs *attr)
+{
+ wpa_printf(MSG_DEBUG, "EAP-SIM: Receive start response");
+
+ if (attr->nonce_mt == NULL || attr->selected_version < 0) {
+ wpa_printf(MSG_DEBUG, "EAP-SIM: Start/Response missing "
+ "required attributes");
+ eap_sim_state(data, FAILURE);
+ return;
+ }
+
+ if (!eap_sim_supported_ver(data, attr->selected_version)) {
+ wpa_printf(MSG_DEBUG, "EAP-SIM: Peer selected unsupported "
+ "version %d", attr->selected_version);
+ eap_sim_state(data, FAILURE);
+ return;
+ }
+
+ if (attr->identity) {
+ free(sm->identity);
+ sm->identity = malloc(attr->identity_len);
+ if (sm->identity) {
+ memcpy(sm->identity, attr->identity,
+ attr->identity_len);
+ sm->identity_len = attr->identity_len;
+ }
+ }
+
+ if (sm->identity == NULL || sm->identity_len < 1 ||
+ sm->identity[0] != '1') {
+ wpa_printf(MSG_DEBUG, "EAP-SIM: Could not get proper permanent"
+ " user name");
+ eap_sim_state(data, FAILURE);
+ return;
+ }
+
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM: Identity",
+ sm->identity, sm->identity_len);
+
+ data->num_chal = eap_sim_db_get_gsm_triplets(
+ sm->eap_sim_db_priv, sm->identity, sm->identity_len,
+ EAP_SIM_MAX_CHAL,
+ (u8 *) data->rand, (u8 *) data->kc, (u8 *) data->sres);
+ if (data->num_chal < 2) {
+ wpa_printf(MSG_INFO, "EAP-SIM: Failed to get GSM "
+ "authentication triplets for the peer");
+ eap_sim_state(data, FAILURE);
+ return;
+ }
+
+ memcpy(data->nonce_mt, attr->nonce_mt, EAP_SIM_NONCE_MT_LEN);
+ eap_sim_derive_mk(data, sm->identity, sm->identity_len, attr->nonce_mt,
+ attr->selected_version, data->num_chal,
+ (u8 *) data->kc);
+ eap_sim_derive_keys(data->mk, data->k_encr, data->k_aut, data->msk);
+
+ eap_sim_state(data, CHALLENGE);
+}
+
+
+static void eap_sim_process_challenge(struct eap_sm *sm,
+ struct eap_sim_data *data,
+ u8 *respData, size_t respDataLen,
+ struct eap_sim_attrs *attr)
+{
+ if (attr->mac == NULL ||
+ eap_sim_verify_mac(data->k_aut, respData, respDataLen, attr->mac,
+ (u8 *) data->sres, data->num_chal * SRES_LEN)) {
+ wpa_printf(MSG_WARNING, "EAP-SIM: Challenge message "
+ "did not include valid AT_MAC");
+ eap_sim_state(data, FAILURE);
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG, "EAP-SIM: Challenge response includes the "
+ "correct AT_MAC");
+ eap_sim_state(data, SUCCESS);
+}
+
+
+static void eap_sim_process_client_error(struct eap_sm *sm,
+ struct eap_sim_data *data,
+ u8 *respData, size_t respDataLen,
+ struct eap_sim_attrs *attr)
+{
+ wpa_printf(MSG_DEBUG, "EAP-SIM: Client reported error %d",
+ attr->client_error_code);
+ eap_sim_state(data, FAILURE);
+}
+
+
+static void eap_sim_process(struct eap_sm *sm, void *priv,
+ u8 *respData, size_t respDataLen)
+{
+ struct eap_sim_data *data = priv;
+ struct eap_hdr *resp;
+ u8 *pos, subtype;
+ size_t len;
+ struct eap_sim_attrs attr;
+
+ resp = (struct eap_hdr *) respData;
+ pos = (u8 *) (resp + 1);
+ subtype = pos[1];
+ len = ntohs(resp->length);
+ pos += 4;
+
+ if (eap_sim_parse_attr(pos, respData + len, &attr, 0, 0)) {
+ wpa_printf(MSG_DEBUG, "EAP-SIM: Failed to parse attributes");
+ eap_sim_state(data, FAILURE);
+ return;
+ }
+
+ if (subtype == EAP_SIM_SUBTYPE_CLIENT_ERROR) {
+ eap_sim_process_client_error(sm, data, respData, len, &attr);
+ return;
+ }
+
+ switch (data->state) {
+ case START:
+ eap_sim_process_start(sm, data, respData, len, &attr);
+ break;
+ case CHALLENGE:
+ eap_sim_process_challenge(sm, data, respData, len, &attr);
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "EAP-SIM: Unknown state %d in "
+ "process", data->state);
+ break;
+ }
+}
+
+
+static Boolean eap_sim_isDone(struct eap_sm *sm, void *priv)
+{
+ struct eap_sim_data *data = priv;
+ return data->state == SUCCESS || data->state == FAILURE;
+}
+
+
+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;
+ memcpy(key, data->msk, EAP_SIM_KEYING_DATA_LEN);
+ *len = EAP_SIM_KEYING_DATA_LEN;
+ return key;
+}
+
+
+static Boolean eap_sim_isSuccess(struct eap_sm *sm, void *priv)
+{
+ struct eap_sim_data *data = priv;
+ return data->state == SUCCESS;
+}
+
+
+const struct eap_method eap_method_sim =
+{
+ .method = EAP_TYPE_SIM,
+ .name = "SIM",
+ .init = eap_sim_init,
+ .reset = eap_sim_reset,
+ .buildReq = eap_sim_buildReq,
+ .check = eap_sim_check,
+ .process = eap_sim_process,
+ .isDone = eap_sim_isDone,
+ .getKey = eap_sim_getKey,
+ .isSuccess = eap_sim_isSuccess,
+};
diff --git a/contrib/hostapd/eap_sim_common.c b/contrib/hostapd/eap_sim_common.c
new file mode 100644
index 000000000000..98f4fb7d3014
--- /dev/null
+++ b/contrib/hostapd/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/hostapd/eap_sim_common.h b/contrib/hostapd/eap_sim_common.h
new file mode 100644
index 000000000000..c89e04e410b9
--- /dev/null
+++ b/contrib/hostapd/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/hostapd/eap_sim_db.c b/contrib/hostapd/eap_sim_db.c
new file mode 100644
index 000000000000..7f617ce860f1
--- /dev/null
+++ b/contrib/hostapd/eap_sim_db.c
@@ -0,0 +1,242 @@
+/*
+ * hostapd / EAP-SIM database/authenticator gateway
+ * Copyright (c) 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.
+ */
+
+/* This is an example implementation of the EAP-SIM database/authentication
+ * gateway interface that is expected to be replaced with an implementation of
+ * SS7 gateway to GSM authentication center (HLR/AuC) or a local
+ * implementation of SIM triplet generator.
+ *
+ * The example implementation here reads triplets from a text file in
+ * IMSI:Kc:SRES:RAND format, IMSI in ASCII, other fields as hex strings. This
+ * is used to simulate an HLR/AuC. As such, it is not very useful for real life
+ * authentication, but it is useful both as an example implementation and for
+ * EAP-SIM testing.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "common.h"
+#include "eap_sim_common.h"
+#include "eap_sim_db.h"
+
+
+/* TODO: add an alternative callback based version of the interface. This is
+ * needed to work better with the single threaded design of hostapd. For this,
+ * the EAP data has to be stored somewhere and eap_sim_db is given a context
+ * pointer for this and a callback function. The callback function will re-send
+ * the EAP data through normal operations which will eventually end up calling
+ * eap_sim_db_get_gsm_triplets() again for the same user. This time, eap_sim_db
+ * should have the triplets available immediately. */
+
+
+struct eap_sim_db_data {
+ char *fname;
+};
+
+#define KC_LEN 8
+#define SRES_LEN 4
+#define RAND_LEN 16
+
+
+/* Initialize EAP-SIM database/authentication gateway interface.
+ * Returns pointer to a private data structure. */
+void * eap_sim_db_init(const char *config)
+{
+ struct eap_sim_db_data *data;
+
+ data = malloc(sizeof(*data));
+ if (data == NULL) {
+ return NULL;
+ }
+
+ memset(data, 0, sizeof(*data));
+ data->fname = strdup(config);
+ if (data->fname == NULL) {
+ free(data);
+ return NULL;
+ }
+
+ return data;
+}
+
+/* Deinitialize EAP-SIM database/authentication gateway interface.
+ * priv is the pointer from eap_sim_db_init(). */
+void eap_sim_db_deinit(void *priv)
+{
+ struct eap_sim_db_data *data = priv;
+ free(data->fname);
+ free(data);
+}
+
+
+/* Get GSM triplets for user name identity (identity_len bytes). In most cases,
+ * the user name is '1' | IMSI, i.e., 1 followed by the IMSI in ASCII format.
+ * The identity may also include NAI realm (@realm).
+ * priv is the pointer from eap_sim_db_init().
+ * Returns the number of triplets received (has to be less than or equal to
+ * max_chal) or -1 on error (e.g., user not found). rand, kc, and sres are
+ * pointers to data areas for the triplets. */
+int eap_sim_db_get_gsm_triplets(void *priv, const u8 *identity,
+ size_t identity_len, int max_chal,
+ u8 *rand, u8 *kc, u8 *sres)
+{
+ struct eap_sim_db_data *data = priv;
+ FILE *f;
+ int count, i;
+ char buf[80], *pos, *next;
+
+ f = fopen(data->fname, "r");
+ if (f == NULL) {
+ wpa_printf(MSG_DEBUG, "EAP-SIM DB: could not open triplet "
+ "file '%s'", data->fname);
+ return -1;
+ }
+
+ if (identity_len < 2 || identity[0] != '1') {
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity",
+ identity, identity_len);
+ return -1;
+ }
+ identity++;
+ identity_len--;
+ for (i = 0; i < identity_len; i++) {
+ if (identity[i] == '@') {
+ identity_len = i;
+ break;
+ }
+ }
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: get triplets for IMSI",
+ identity, identity_len);
+
+ count = 0;
+ while (count < max_chal && fgets(buf, sizeof(buf), f)) {
+ /* Parse IMSI:Kc:SRES:RAND and match IMSI with identity. */
+ buf[sizeof(buf) - 1] = '\0';
+ pos = buf;
+ while (*pos != '\0' && *pos != '\n')
+ pos++;
+ if (*pos == '\n')
+ *pos = '\0';
+ if (pos - buf < 60 || pos[0] == '#')
+ continue;
+
+ pos = strchr(buf, ':');
+ if (pos == NULL)
+ continue;
+ *pos++ = '\0';
+ if (strlen(buf) != identity_len ||
+ memcmp(buf, identity, identity_len) != 0)
+ continue;
+
+ next = strchr(pos, ':');
+ if (next == NULL)
+ continue;
+ *next++ = '\0';
+ if (hexstr2bin(pos, &kc[count * KC_LEN], KC_LEN) < 0)
+ continue;
+
+ pos = next;
+ next = strchr(pos, ':');
+ if (next == NULL)
+ continue;
+ *next++ = '\0';
+ if (hexstr2bin(pos, &sres[count * SRES_LEN], SRES_LEN) < 0)
+ continue;
+
+ if (hexstr2bin(next, &rand[count * RAND_LEN], RAND_LEN) < 0)
+ continue;
+
+ count++;
+ }
+
+ fclose(f);
+
+ if (count == 0) {
+ wpa_printf(MSG_DEBUG, "EAP-SIM DB: no triplets found");
+ count = -1;
+ }
+
+ return count;
+}
+
+
+/* Verify whether the given user identity (identity_len bytes) is known. In
+ * most cases, the user name is '1' | IMSI, i.e., 1 followed by the IMSI in
+ * ASCII format.
+ * priv is the pointer from eap_sim_db_init().
+ * Returns 0 if the user is found and GSM triplets would be available for it or
+ * -1 on error (e.g., user not found or no triplets available). */
+int eap_sim_db_identity_known(void *priv, const u8 *identity,
+ size_t identity_len)
+{
+ struct eap_sim_db_data *data = priv;
+ FILE *f;
+ char buf[80], *pos;
+ int i;
+
+ if (identity_len < 1 || identity[0] != '1') {
+ return -1;
+ }
+
+ f = fopen(data->fname, "r");
+ if (f == NULL) {
+ wpa_printf(MSG_DEBUG, "EAP-SIM DB: could not open triplet "
+ "file '%s'", data->fname);
+ return -1;
+ }
+
+ if (identity_len < 2 || identity[0] != '1') {
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity",
+ identity, identity_len);
+ return -1;
+ }
+ identity++;
+ identity_len--;
+ for (i = 0; i < identity_len; i++) {
+ if (identity[i] == '@') {
+ identity_len = i;
+ break;
+ }
+ }
+
+ while (fgets(buf, sizeof(buf), f)) {
+ /* Parse IMSI:Kc:SRES:RAND and match IMSI with identity. */
+ buf[sizeof(buf) - 1] = '\0';
+ pos = buf;
+ while (*pos != '\0' && *pos != '\n')
+ pos++;
+ if (*pos == '\n')
+ *pos = '\0';
+ if (pos - buf < 60 || pos[0] == '#')
+ continue;
+
+ pos = strchr(buf, ':');
+ if (pos == NULL)
+ continue;
+ *pos++ = '\0';
+ if (strlen(buf) != identity_len ||
+ memcmp(buf, identity, identity_len) != 0)
+ continue;
+
+ fclose(f);
+ return 0;
+ }
+
+ /* IMSI not found */
+
+ fclose(f);
+ return -1;
+}
diff --git a/contrib/hostapd/eap_sim_db.h b/contrib/hostapd/eap_sim_db.h
new file mode 100644
index 000000000000..57a9871073e2
--- /dev/null
+++ b/contrib/hostapd/eap_sim_db.h
@@ -0,0 +1,44 @@
+#ifndef EAP_SIM_DB_H
+#define EAP_SIM_DB_H
+
+#ifdef EAP_SIM
+
+/* Initialize EAP-SIM database/authentication gateway interface.
+ * Returns pointer to a private data structure. */
+void * eap_sim_db_init(const char *config);
+
+/* Deinitialize EAP-SIM database/authentication gateway interface.
+ * priv is the pointer from eap_sim_db_init(). */
+void eap_sim_db_deinit(void *priv);
+
+/* Get GSM triplets for user name identity (identity_len bytes). In most cases,
+ * the user name is '1' | IMSI, i.e., 1 followed by the IMSI in ASCII format.
+ * priv is the pointer from eap_sim_db_init().
+ * Returns the number of triplets received (has to be less than or equal to
+ * max_chal) or -1 on error (e.g., user not found). rand, kc, and sres are
+ * pointers to data areas for the triplets. */
+int eap_sim_db_get_gsm_triplets(void *priv, const u8 *identity,
+ size_t identity_len, int max_chal,
+ u8 *rand, u8 *kc, u8 *sres);
+
+/* Verify whether the given user identity (identity_len bytes) is known. In
+ * most cases, the user name is '1' | IMSI, i.e., 1 followed by the IMSI in
+ * ASCII format.
+ * priv is the pointer from eap_sim_db_init().
+ * Returns 0 if the user is found and GSM triplets would be available for it or
+ * -1 on error (e.g., user not found or no triplets available). */
+int eap_sim_db_identity_known(void *priv, const u8 *identity,
+ size_t identity_len);
+
+#else /* EAP_SIM */
+static inline void * eap_sim_db_init(const char *config)
+{
+ return NULL;
+}
+
+static inline void eap_sim_db_deinit(void *priv)
+{
+}
+#endif /* EAP_SIM */
+
+#endif /* EAP_SIM_DB_H */
diff --git a/contrib/hostapd/eap_tls.c b/contrib/hostapd/eap_tls.c
new file mode 100644
index 000000000000..58ab2776a83c
--- /dev/null
+++ b/contrib/hostapd/eap_tls.c
@@ -0,0 +1,246 @@
+/*
+ * hostapd / 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 <netinet/in.h>
+
+#include "hostapd.h"
+#include "common.h"
+#include "eap_i.h"
+#include "eap_tls_common.h"
+#include "tls.h"
+
+
+static void eap_tls_reset(struct eap_sm *sm, void *priv);
+
+
+struct eap_tls_data {
+ struct eap_ssl_data ssl;
+ enum { START, CONTINUE, SUCCESS, FAILURE } state;
+};
+
+
+static void * eap_tls_init(struct eap_sm *sm)
+{
+ struct eap_tls_data *data;
+
+ data = malloc(sizeof(*data));
+ if (data == NULL)
+ return data;
+ memset(data, 0, sizeof(*data));
+ data->state = START;
+
+ if (eap_tls_ssl_init(sm, &data->ssl, 1)) {
+ wpa_printf(MSG_INFO, "EAP-TLS: Failed to initialize SSL.");
+ eap_tls_reset(sm, data);
+ return NULL;
+ }
+
+ return data;
+}
+
+
+static void eap_tls_reset(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);
+}
+
+
+static u8 * eap_tls_build_start(struct eap_sm *sm, struct eap_tls_data *data,
+ int id, size_t *reqDataLen)
+{
+ struct eap_hdr *req;
+ u8 *pos;
+
+ *reqDataLen = sizeof(*req) + 2;
+ req = malloc(*reqDataLen);
+ if (req == NULL) {
+ wpa_printf(MSG_ERROR, "EAP-TLS: Failed to allocate memory for "
+ "request");
+ data->state = FAILURE;
+ return NULL;
+ }
+
+ req->code = EAP_CODE_REQUEST;
+ req->identifier = id;
+ req->length = htons(*reqDataLen);
+ pos = (u8 *) (req + 1);
+ *pos++ = EAP_TYPE_TLS;
+ *pos = EAP_TLS_FLAGS_START;
+
+ data->state = CONTINUE;
+
+ return (u8 *) req;
+}
+
+
+static u8 * eap_tls_build_req(struct eap_sm *sm, struct eap_tls_data *data,
+ int id, size_t *reqDataLen)
+{
+ int res;
+ u8 *req;
+
+ res = eap_tls_buildReq_helper(sm, &data->ssl, EAP_TYPE_TLS, 0, id,
+ &req, reqDataLen);
+
+ if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) {
+ wpa_printf(MSG_DEBUG, "EAP-TLS: Done");
+ data->state = SUCCESS;
+ }
+
+ if (res == 1)
+ return eap_tls_build_ack(reqDataLen, id, EAP_TYPE_TLS, 0);
+ return req;
+}
+
+
+static u8 * eap_tls_buildReq(struct eap_sm *sm, void *priv, int id,
+ size_t *reqDataLen)
+{
+ struct eap_tls_data *data = priv;
+
+ switch (data->state) {
+ case START:
+ return eap_tls_build_start(sm, data, id, reqDataLen);
+ case CONTINUE:
+ return eap_tls_build_req(sm, data, id, reqDataLen);
+ default:
+ wpa_printf(MSG_DEBUG, "EAP-TLS: %s - unexpected state %d",
+ __func__, data->state);
+ return NULL;
+ }
+}
+
+
+static Boolean eap_tls_check(struct eap_sm *sm, void *priv,
+ u8 *respData, size_t respDataLen)
+{
+ struct eap_hdr *resp;
+ u8 *pos;
+ size_t len;
+
+ resp = (struct eap_hdr *) respData;
+ pos = (u8 *) (resp + 1);
+ if (respDataLen < sizeof(*resp) + 2 || *pos != EAP_TYPE_TLS ||
+ (len = ntohs(resp->length)) > respDataLen) {
+ wpa_printf(MSG_INFO, "EAP-TLS: Invalid frame");
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+static void eap_tls_process(struct eap_sm *sm, void *priv,
+ u8 *respData, size_t respDataLen)
+{
+ struct eap_tls_data *data = priv;
+ struct eap_hdr *resp;
+ u8 *pos, flags;
+ int left;
+ unsigned int tls_msg_len;
+
+ resp = (struct eap_hdr *) respData;
+ pos = (u8 *) (resp + 1);
+ pos++;
+ flags = *pos++;
+ left = htons(resp->length) - sizeof(struct eap_hdr) - 2;
+ wpa_printf(MSG_DEBUG, "EAP-TLS: Received packet(len=%lu) - "
+ "Flags 0x%02x", (unsigned long) respDataLen, flags);
+ if (flags & EAP_TLS_FLAGS_LENGTH_INCLUDED) {
+ if (left < 4) {
+ wpa_printf(MSG_INFO, "EAP-TLS: Short frame with TLS "
+ "length");
+ data->state = FAILURE;
+ return;
+ }
+ 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;
+ }
+
+ if (eap_tls_process_helper(sm, &data->ssl, pos, left) < 0) {
+ wpa_printf(MSG_INFO, "EAP-TLS: TLS processing failed");
+ data->state = FAILURE;
+ return;
+ }
+}
+
+
+static Boolean eap_tls_isDone(struct eap_sm *sm, void *priv)
+{
+ struct eap_tls_data *data = priv;
+ return data->state == SUCCESS || data->state == FAILURE;
+}
+
+
+static u8 * eap_tls_getKey(struct eap_sm *sm, void *priv, size_t *len)
+{
+ struct eap_tls_data *data = priv;
+ u8 *eapKeyData;
+
+ if (data->state != SUCCESS)
+ return NULL;
+
+ eapKeyData = eap_tls_derive_key(sm, &data->ssl,
+ "client EAP encryption",
+ EAP_TLS_KEY_LEN);
+ if (eapKeyData) {
+ *len = EAP_TLS_KEY_LEN;
+ wpa_hexdump(MSG_DEBUG, "EAP-TLS: Derived key",
+ eapKeyData, EAP_TLS_KEY_LEN);
+ } else {
+ wpa_printf(MSG_DEBUG, "EAP-TLS: Failed to derive key");
+ }
+
+ return eapKeyData;
+}
+
+
+static Boolean eap_tls_isSuccess(struct eap_sm *sm, void *priv)
+{
+ struct eap_tls_data *data = priv;
+ return data->state == SUCCESS;
+}
+
+
+const struct eap_method eap_method_tls =
+{
+ .method = EAP_TYPE_TLS,
+ .name = "TLS",
+ .init = eap_tls_init,
+ .reset = eap_tls_reset,
+ .buildReq = eap_tls_buildReq,
+ .check = eap_tls_check,
+ .process = eap_tls_process,
+ .isDone = eap_tls_isDone,
+ .getKey = eap_tls_getKey,
+ .isSuccess = eap_tls_isSuccess,
+};
diff --git a/contrib/hostapd/eap_tls_common.c b/contrib/hostapd/eap_tls_common.c
new file mode 100644
index 000000000000..ca10eca7e46e
--- /dev/null
+++ b/contrib/hostapd/eap_tls_common.c
@@ -0,0 +1,272 @@
+/*
+ * hostapd / EAP-TLS/PEAP/TTLS 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 <netinet/in.h>
+
+#include "hostapd.h"
+#include "common.h"
+#include "eap_i.h"
+#include "eap_tls_common.h"
+#include "sha1.h"
+#include "tls.h"
+
+
+int eap_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data,
+ int verify_peer)
+{
+ data->eap = sm;
+ data->phase2 = sm->init_phase2;
+
+ data->conn = tls_connection_init(sm->ssl_ctx);
+ if (data->conn == NULL) {
+ wpa_printf(MSG_INFO, "SSL: Failed to initialize new TLS "
+ "connection");
+ return -1;
+ }
+
+ if (tls_connection_set_verify(sm->ssl_ctx, data->conn, verify_peer,
+ NULL)) {
+ wpa_printf(MSG_INFO, "SSL: Failed to configure verification "
+ "of TLS peer certificate");
+ tls_connection_deinit(sm->ssl_ctx, data->conn);
+ data->conn = NULL;
+ return -1;
+ }
+
+ /* 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;
+ }
+ return 0;
+}
+
+
+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,
+ u8 *in_data, size_t in_len)
+{
+ WPA_ASSERT(data->tls_out_len == 0 || in_len == 0);
+
+ if (data->tls_out_len == 0) {
+ /* No more data to send out - expect to receive more data from
+ * the peer. */
+ int res = eap_tls_data_reassemble(sm, data, &in_data, &in_len);
+ if (res < 0 || res == 1) {
+ wpa_printf(MSG_DEBUG, "SSL: data reassembly failed");
+ 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_server_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) {
+ wpa_printf(MSG_DEBUG, "SSL: failed to generate output data");
+ 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);
+
+ return 0;
+}
+
+
+int eap_tls_buildReq_helper(struct eap_sm *sm, struct eap_ssl_data *data,
+ int eap_type, int peap_version, u8 id,
+ u8 **out_data, size_t *out_len)
+{
+ size_t len;
+ u8 *pos, *flags;
+ struct eap_hdr *req;
+
+ *out_len = 0;
+
+ req = malloc(sizeof(struct eap_hdr) + 2 + 4 + data->tls_out_limit);
+ if (req == NULL) {
+ *out_data = NULL;
+ return -1;
+ }
+ req->code = EAP_CODE_REQUEST;
+ req->identifier = id;
+ pos = (u8 *) (req + 1);
+ *pos++ = eap_type;
+ flags = pos++;
+ *flags = peap_version;
+ if (data->tls_out_pos == 0 &&
+ data->tls_out_len > data->tls_out_limit) {
+ *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 *) req) + len;
+ req->length = htons(*out_len);
+ *out_data = (u8 *) req;
+
+ 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(size_t *reqDataLen, u8 id, int eap_type,
+ int peap_version)
+{
+ struct eap_hdr *req;
+ u8 *pos;
+
+ *reqDataLen = sizeof(struct eap_hdr) + 2;
+ req = malloc(*reqDataLen);
+ if (req == NULL)
+ return NULL;
+ wpa_printf(MSG_DEBUG, "SSL: Building ACK");
+ req->code = EAP_CODE_REQUEST;
+ req->identifier = id;
+ req->length = htons(*reqDataLen);
+ pos = (u8 *) (req + 1);
+ *pos++ = eap_type; /* Type */
+ *pos = peap_version; /* Flags */
+ return (u8 *) req;
+}
diff --git a/contrib/hostapd/eap_tls_common.h b/contrib/hostapd/eap_tls_common.h
new file mode 100644
index 000000000000..659ee849f7b0
--- /dev/null
+++ b/contrib/hostapd/eap_tls_common.h
@@ -0,0 +1,47 @@
+#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;
+
+ 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,
+ int verify_peer);
+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,
+ u8 *in_data, size_t in_len);
+int eap_tls_buildReq_helper(struct eap_sm *sm, struct eap_ssl_data *data,
+ int eap_type, int peap_version, u8 id,
+ u8 **out_data, size_t *out_len);
+u8 * eap_tls_build_ack(size_t *reqDataLen, u8 id, int eap_type,
+ int peap_version);
+
+#endif /* EAP_TLS_COMMON_H */
diff --git a/contrib/hostapd/eap_tlv.c b/contrib/hostapd/eap_tlv.c
new file mode 100644
index 000000000000..b7609dcf2e91
--- /dev/null
+++ b/contrib/hostapd/eap_tlv.c
@@ -0,0 +1,248 @@
+/*
+ * hostapd / EAP-TLV (draft-josefsson-pppext-eap-tls-eap-07.txt)
+ * 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>
+#include <stdio.h>
+#include <string.h>
+#include <netinet/in.h>
+
+#include "hostapd.h"
+#include "common.h"
+#include "eap_i.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_RESULT_SUCCESS 1
+#define EAP_TLV_RESULT_FAILURE 2
+
+
+struct eap_tlv_data {
+ enum { CONTINUE, SUCCESS, FAILURE } state;
+};
+
+
+static void * eap_tlv_init(struct eap_sm *sm)
+{
+ struct eap_tlv_data *data;
+
+ data = malloc(sizeof(*data));
+ if (data == NULL)
+ return data;
+ memset(data, 0, sizeof(*data));
+ data->state = CONTINUE;
+
+ return data;
+}
+
+
+static void eap_tlv_reset(struct eap_sm *sm, void *priv)
+{
+ struct eap_tlv_data *data = priv;
+ free(data);
+}
+
+
+static u8 * eap_tlv_buildReq(struct eap_sm *sm, void *priv, int id,
+ size_t *reqDataLen)
+{
+ struct eap_hdr *req;
+ u8 *pos;
+ u16 status;
+
+ if (sm->tlv_request == TLV_REQ_SUCCESS) {
+ status = EAP_TLV_RESULT_SUCCESS;
+ } else {
+ status = EAP_TLV_RESULT_FAILURE;
+ }
+
+ *reqDataLen = sizeof(struct eap_hdr) + 1 + 6;
+ req = malloc(*reqDataLen);
+ if (req == NULL)
+ return NULL;
+
+ req->code = EAP_CODE_REQUEST;
+ req->identifier = id;
+ req->length = host_to_be16(*reqDataLen);
+ pos = (u8 *) (req + 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 *) req;
+}
+
+
+static Boolean eap_tlv_check(struct eap_sm *sm, void *priv,
+ u8 *respData, size_t respDataLen)
+{
+ struct eap_hdr *resp;
+ u8 *pos;
+ size_t len;
+
+ resp = (struct eap_hdr *) respData;
+ pos = (u8 *) (resp + 1);
+ if (respDataLen < sizeof(*resp) + 1 || *pos != EAP_TYPE_TLV ||
+ (len = ntohs(resp->length)) > respDataLen) {
+ wpa_printf(MSG_INFO, "EAP-TLV: Invalid frame");
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+static void eap_tlv_process(struct eap_sm *sm, void *priv,
+ u8 *respData, size_t respDataLen)
+{
+ struct eap_tlv_data *data = priv;
+ struct eap_hdr *resp;
+ u8 *pos;
+ int len;
+ size_t left;
+ u8 *result_tlv = NULL;
+ size_t result_tlv_len = 0;
+ int tlv_type, mandatory, tlv_len;
+
+ resp = (struct eap_hdr *) respData;
+ len = ntohs(resp->length);
+ pos = (u8 *) (resp + 1);
+
+ /* Parse TLVs */
+ left = be_to_host16(resp->length) - sizeof(struct eap_hdr) - 1;
+ pos = (u8 *) (resp + 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);
+ data->state = FAILURE;
+ return;
+ }
+ 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) {
+ data->state = FAILURE;
+ return;
+ }
+ /* 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);
+ data->state = FAILURE;
+ return;
+ }
+
+ /* Process supported TLVs */
+ if (result_tlv) {
+ int status;
+ const char *requested;
+
+ 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);
+ data->state = FAILURE;
+ return;
+ }
+ requested = sm->tlv_request == TLV_REQ_SUCCESS ? "Success" :
+ "Failure";
+ status = ((int) result_tlv[0] << 8) | result_tlv[1];
+ if (status == EAP_TLV_RESULT_SUCCESS) {
+ wpa_printf(MSG_INFO, "EAP-TLV: TLV Result - Success "
+ "- requested %s", requested);
+ if (sm->tlv_request == TLV_REQ_SUCCESS)
+ data->state = SUCCESS;
+ else
+ data->state = FAILURE;
+
+ } else if (status == EAP_TLV_RESULT_FAILURE) {
+ wpa_printf(MSG_INFO, "EAP-TLV: TLV Result - Failure - "
+ "requested %s", requested);
+ if (sm->tlv_request == TLV_REQ_FAILURE)
+ data->state = SUCCESS;
+ else
+ data->state = FAILURE;
+ } else {
+ wpa_printf(MSG_INFO, "EAP-TLV: Unknown TLV Result "
+ "Status %d", status);
+ data->state = FAILURE;
+ }
+ }
+}
+
+
+static Boolean eap_tlv_isDone(struct eap_sm *sm, void *priv)
+{
+ struct eap_tlv_data *data = priv;
+ return data->state != CONTINUE;
+}
+
+
+static Boolean eap_tlv_isSuccess(struct eap_sm *sm, void *priv)
+{
+ struct eap_tlv_data *data = priv;
+ return data->state == SUCCESS;
+}
+
+
+const struct eap_method eap_method_tlv =
+{
+ .method = EAP_TYPE_TLV,
+ .name = "TLV",
+ .init = eap_tlv_init,
+ .reset = eap_tlv_reset,
+ .buildReq = eap_tlv_buildReq,
+ .check = eap_tlv_check,
+ .process = eap_tlv_process,
+ .isDone = eap_tlv_isDone,
+ .isSuccess = eap_tlv_isSuccess,
+};
diff --git a/contrib/hostapd/eap_ttls.c b/contrib/hostapd/eap_ttls.c
new file mode 100644
index 000000000000..1e4be75c70b7
--- /dev/null
+++ b/contrib/hostapd/eap_ttls.c
@@ -0,0 +1,1183 @@
+/*
+ * hostapd / EAP-TTLS (draft-ietf-pppext-eap-ttls-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 <netinet/in.h>
+
+#include "hostapd.h"
+#include "common.h"
+#include "eap_i.h"
+#include "eap_tls_common.h"
+#include "ms_funcs.h"
+#include "md5.h"
+#include "tls.h"
+#include "eap_ttls.h"
+
+#define EAP_TTLS_VERSION 0
+
+
+static void eap_ttls_reset(struct eap_sm *sm, void *priv);
+
+
+struct eap_ttls_data {
+ struct eap_ssl_data ssl;
+ enum {
+ START, PHASE1, PHASE2_START, PHASE2_METHOD,
+ PHASE2_MSCHAPV2_RESP, SUCCESS, FAILURE
+ } state;
+
+ int ttls_version;
+ const struct eap_method *phase2_method;
+ void *phase2_priv;
+ int mschapv2_resp_ok;
+ u8 mschapv2_auth_response[20];
+ u8 mschapv2_ident;
+};
+
+
+static const char * eap_ttls_state_txt(int state)
+{
+ switch (state) {
+ case START:
+ return "START";
+ case PHASE1:
+ return "PHASE1";
+ case PHASE2_START:
+ return "PHASE2_START";
+ case PHASE2_METHOD:
+ return "PHASE2_METHOD";
+ case PHASE2_MSCHAPV2_RESP:
+ return "PHASE2_MSCHAPV2_RESP";
+ case SUCCESS:
+ return "SUCCESS";
+ case FAILURE:
+ return "FAILURE";
+ default:
+ return "Unknown?!";
+ }
+}
+
+
+static void eap_ttls_state(struct eap_ttls_data *data, int state)
+{
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: %s -> %s",
+ eap_ttls_state_txt(data->state),
+ eap_ttls_state_txt(state));
+ data->state = state;
+}
+
+
+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 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;
+}
+
+
+struct eap_ttls_avp {
+ /* Note: eap is allocated memory; caller is responsible for freeing
+ * it. All the other pointers are pointing to the packet data, i.e.,
+ * they must not be freed separately. */
+ u8 *eap;
+ size_t eap_len;
+ u8 *user_name;
+ size_t user_name_len;
+ u8 *user_password;
+ size_t user_password_len;
+ u8 *chap_challenge;
+ size_t chap_challenge_len;
+ u8 *chap_password;
+ size_t chap_password_len;
+ u8 *mschap_challenge;
+ size_t mschap_challenge_len;
+ u8 *mschap_response;
+ size_t mschap_response_len;
+ u8 *mschap2_response;
+ size_t mschap2_response_len;
+};
+
+
+static int eap_ttls_avp_parse(u8 *buf, size_t len, struct eap_ttls_avp *parse)
+{
+ struct ttls_avp *avp;
+ u8 *pos;
+ int left;
+
+ pos = buf;
+ left = len;
+ memset(parse, 0, sizeof(*parse));
+
+ 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);
+ return -1;
+ }
+ 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");
+ return -1;
+ }
+ 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 (parse->eap == NULL) {
+ parse->eap = malloc(dlen);
+ if (parse->eap == NULL) {
+ wpa_printf(MSG_WARNING, "EAP-TTLS: "
+ "failed to allocate memory "
+ "for Phase 2 EAP data");
+ return -1;
+ }
+ memcpy(parse->eap, dpos, dlen);
+ parse->eap_len = dlen;
+ } else {
+ u8 *neweap = realloc(parse->eap,
+ parse->eap_len + dlen);
+ if (neweap == NULL) {
+ wpa_printf(MSG_WARNING, "EAP-TTLS: "
+ "failed to allocate memory "
+ "for Phase 2 EAP data");
+ free(parse->eap);
+ parse->eap = NULL;
+ return -1;
+ }
+ memcpy(neweap + parse->eap_len, dpos, dlen);
+ parse->eap = neweap;
+ parse->eap_len += dlen;
+ }
+ } else if (vendor_id == 0 &&
+ avp_code == RADIUS_ATTR_USER_NAME) {
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP-TTLS: User-Name",
+ dpos, dlen);
+ parse->user_name = dpos;
+ parse->user_name_len = dlen;
+ } else if (vendor_id == 0 &&
+ avp_code == RADIUS_ATTR_USER_PASSWORD) {
+ u8 *password = dpos;
+ size_t password_len = dlen;
+ while (password_len > 0 &&
+ password[password_len - 1] == '\0') {
+ password_len--;
+ }
+ wpa_hexdump_ascii_key(MSG_DEBUG, "EAP-TTLS: "
+ "User-Password (PAP)",
+ password, password_len);
+ parse->user_password = password;
+ parse->user_password_len = password_len;
+ } else if (vendor_id == 0 &&
+ avp_code == RADIUS_ATTR_CHAP_CHALLENGE) {
+ wpa_hexdump(MSG_DEBUG,
+ "EAP-TTLS: CHAP-Challenge (CHAP)",
+ dpos, dlen);
+ parse->chap_challenge = dpos;
+ parse->chap_challenge_len = dlen;
+ } else if (vendor_id == 0 &&
+ avp_code == RADIUS_ATTR_CHAP_PASSWORD) {
+ wpa_hexdump(MSG_DEBUG,
+ "EAP-TTLS: CHAP-Password (CHAP)",
+ dpos, dlen);
+ parse->chap_password = dpos;
+ parse->chap_password_len = dlen;
+ } else if (vendor_id == RADIUS_VENDOR_ID_MICROSOFT &&
+ avp_code == RADIUS_ATTR_MS_CHAP_CHALLENGE) {
+ wpa_hexdump(MSG_DEBUG,
+ "EAP-TTLS: MS-CHAP-Challenge",
+ dpos, dlen);
+ parse->mschap_challenge = dpos;
+ parse->mschap_challenge_len = dlen;
+ } else if (vendor_id == RADIUS_VENDOR_ID_MICROSOFT &&
+ avp_code == RADIUS_ATTR_MS_CHAP_RESPONSE) {
+ wpa_hexdump(MSG_DEBUG,
+ "EAP-TTLS: MS-CHAP-Response (MSCHAP)",
+ dpos, dlen);
+ parse->mschap_response = dpos;
+ parse->mschap_response_len = dlen;
+ } else if (vendor_id == RADIUS_VENDOR_ID_MICROSOFT &&
+ avp_code == RADIUS_ATTR_MS_CHAP2_RESPONSE) {
+ wpa_hexdump(MSG_DEBUG,
+ "EAP-TTLS: MS-CHAP2-Response (MSCHAPV2)",
+ dpos, dlen);
+ parse->mschap2_response = dpos;
+ parse->mschap2_response_len = dlen;
+ } 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);
+ return -1;
+ } 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;
+ }
+
+ return 0;
+}
+
+
+static void * eap_ttls_init(struct eap_sm *sm)
+{
+ struct eap_ttls_data *data;
+
+ data = malloc(sizeof(*data));
+ if (data == NULL)
+ return data;
+ memset(data, 0, sizeof(*data));
+ data->ttls_version = EAP_TTLS_VERSION;
+ data->state = START;
+
+ if (eap_tls_ssl_init(sm, &data->ssl, 0)) {
+ wpa_printf(MSG_INFO, "EAP-TTLS: Failed to initialize SSL.");
+ eap_ttls_reset(sm, data);
+ return NULL;
+ }
+
+ return data;
+}
+
+
+static void eap_ttls_reset(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->reset(sm, data->phase2_priv);
+ eap_tls_ssl_deinit(sm, &data->ssl);
+ free(data);
+}
+
+
+static u8 * eap_ttls_build_start(struct eap_sm *sm, struct eap_ttls_data *data,
+ int id, size_t *reqDataLen)
+{
+ struct eap_hdr *req;
+ u8 *pos;
+
+ *reqDataLen = sizeof(*req) + 2;
+ req = malloc(*reqDataLen);
+ if (req == NULL) {
+ wpa_printf(MSG_ERROR, "EAP-TTLS: Failed to allocate memory for"
+ " request");
+ eap_ttls_state(data, FAILURE);
+ return NULL;
+ }
+
+ req->code = EAP_CODE_REQUEST;
+ req->identifier = id;
+ req->length = htons(*reqDataLen);
+ pos = (u8 *) (req + 1);
+ *pos++ = EAP_TYPE_TTLS;
+ *pos = EAP_TLS_FLAGS_START | data->ttls_version;
+
+ eap_ttls_state(data, PHASE1);
+
+ return (u8 *) req;
+}
+
+
+static u8 * eap_ttls_build_req(struct eap_sm *sm, struct eap_ttls_data *data,
+ int id, size_t *reqDataLen)
+{
+ int res;
+ u8 *req;
+
+ res = eap_tls_buildReq_helper(sm, &data->ssl, EAP_TYPE_TTLS,
+ data->ttls_version, id, &req,
+ reqDataLen);
+
+ if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase1 done, starting "
+ "Phase2");
+ eap_ttls_state(data, PHASE2_START);
+ }
+
+ if (res == 1)
+ return eap_tls_build_ack(reqDataLen, id, EAP_TYPE_TTLS,
+ data->ttls_version);
+ return req;
+}
+
+
+static u8 * eap_ttls_encrypt(struct eap_sm *sm, struct eap_ttls_data *data,
+ int id, u8 *plain, size_t plain_len,
+ size_t *out_len)
+{
+ int res;
+ u8 *pos;
+ struct eap_hdr *req;
+
+ /* TODO: add support for fragmentation, if needed. This will need to
+ * add TLS Message Length field, if the frame is fragmented. */
+ req = malloc(sizeof(struct eap_hdr) + 2 + data->ssl.tls_out_limit);
+ if (req == NULL)
+ return NULL;
+
+ req->code = EAP_CODE_REQUEST;
+ req->identifier = id;
+
+ pos = (u8 *) (req + 1);
+ *pos++ = EAP_TYPE_TTLS;
+ *pos++ = data->ttls_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-TTLS: Failed to encrypt Phase 2 "
+ "data");
+ free(req);
+ return NULL;
+ }
+
+ *out_len = sizeof(struct eap_hdr) + 2 + res;
+ req->length = host_to_be16(*out_len);
+ return (u8 *) req;
+}
+
+
+static u8 * eap_ttls_build_phase2_eap_req(struct eap_sm *sm,
+ struct eap_ttls_data *data,
+ int id, size_t *reqDataLen)
+{
+ u8 *req, *encr_req;
+ size_t req_len;
+
+
+ req = data->phase2_method->buildReq(sm, data->phase2_priv, id,
+ &req_len);
+ if (req == NULL)
+ return NULL;
+
+ wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS/EAP: Encapsulate Phase 2 data",
+ req, req_len);
+
+ if (eap_ttls_avp_encapsulate(&req, &req_len, RADIUS_ATTR_EAP_MESSAGE,
+ 1) < 0) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS/EAP: Failed to encapsulate "
+ "packet");
+ return NULL;
+ }
+
+ wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS/EAP: Encrypt encapsulated Phase "
+ "2 data", req, req_len);
+
+ encr_req = eap_ttls_encrypt(sm, data, id, req, req_len, reqDataLen);
+ free(req);
+
+ return encr_req;
+}
+
+
+static u8 * eap_ttls_build_phase2_mschapv2(struct eap_sm *sm,
+ struct eap_ttls_data *data,
+ int id, size_t *reqDataLen)
+{
+ u8 *req, *encr_req, *pos, *end;
+ size_t req_len;
+ int i;
+
+ pos = req = malloc(100);
+ if (req == NULL)
+ return NULL;
+ end = req + 200;
+
+ if (data->mschapv2_resp_ok) {
+ pos = eap_ttls_avp_hdr(pos, RADIUS_ATTR_MS_CHAP2_SUCCESS,
+ RADIUS_VENDOR_ID_MICROSOFT, 1, 43);
+ *pos++ = data->mschapv2_ident;
+ pos += snprintf((char *) pos, end - pos, "S=");
+ for (i = 0; i < sizeof(data->mschapv2_auth_response); i++) {
+ pos += snprintf((char *) pos, end - pos, "%02X",
+ data->mschapv2_auth_response[i]);
+ }
+ } else {
+ pos = eap_ttls_avp_hdr(pos, RADIUS_ATTR_MS_CHAP_ERROR,
+ RADIUS_VENDOR_ID_MICROSOFT, 1, 6);
+ memcpy(pos, "Failed", 6);
+ pos += 6;
+ AVP_PAD(req, pos);
+ }
+
+ req_len = pos - req;
+ wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Encrypting Phase 2 "
+ "data", req, req_len);
+
+ encr_req = eap_ttls_encrypt(sm, data, id, req, req_len, reqDataLen);
+ free(req);
+
+ return encr_req;
+}
+
+
+static u8 * eap_ttls_buildReq(struct eap_sm *sm, void *priv, int id,
+ size_t *reqDataLen)
+{
+ struct eap_ttls_data *data = priv;
+
+ switch (data->state) {
+ case START:
+ return eap_ttls_build_start(sm, data, id, reqDataLen);
+ case PHASE1:
+ return eap_ttls_build_req(sm, data, id, reqDataLen);
+ case PHASE2_METHOD:
+ return eap_ttls_build_phase2_eap_req(sm, data, id, reqDataLen);
+ case PHASE2_MSCHAPV2_RESP:
+ return eap_ttls_build_phase2_mschapv2(sm, data, id,
+ reqDataLen);
+ default:
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: %s - unexpected state %d",
+ __func__, data->state);
+ return NULL;
+ }
+}
+
+
+static Boolean eap_ttls_check(struct eap_sm *sm, void *priv,
+ u8 *respData, size_t respDataLen)
+{
+ struct eap_hdr *resp;
+ u8 *pos;
+ size_t len;
+
+ resp = (struct eap_hdr *) respData;
+ pos = (u8 *) (resp + 1);
+ if (respDataLen < sizeof(*resp) + 2 || *pos != EAP_TYPE_TTLS ||
+ (len = ntohs(resp->length)) > respDataLen) {
+ wpa_printf(MSG_INFO, "EAP-TTLS: Invalid frame");
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+static void eap_ttls_process_phase2_pap(struct eap_sm *sm,
+ struct eap_ttls_data *data,
+ const u8 *user_password,
+ size_t user_password_len)
+{
+ /* TODO: add support for verifying that the user entry accepts
+ * EAP-TTLS/PAP. */
+ if (!sm->user || !sm->user->password) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS/PAP: No user password "
+ "configured");
+ eap_ttls_state(data, FAILURE);
+ return;
+ }
+
+ if (sm->user->password_len != user_password_len ||
+ memcmp(sm->user->password, user_password, user_password_len) != 0)
+ {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS/PAP: Invalid user password");
+ eap_ttls_state(data, FAILURE);
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG, "EAP-TTLS/PAP: Correct user password");
+ eap_ttls_state(data, SUCCESS);
+}
+
+
+static void eap_ttls_process_phase2_chap(struct eap_sm *sm,
+ struct eap_ttls_data *data,
+ const u8 *challenge,
+ size_t challenge_len,
+ const u8 *password,
+ size_t password_len)
+{
+ MD5_CTX context;
+ u8 *chal, hash[MD5_MAC_LEN];
+
+ if (challenge == NULL || password == NULL ||
+ challenge_len != EAP_TTLS_CHAP_CHALLENGE_LEN ||
+ password_len != 1 + EAP_TTLS_CHAP_PASSWORD_LEN) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS/CHAP: Invalid CHAP attributes "
+ "(challenge len %lu password len %lu)",
+ (unsigned long) challenge_len,
+ (unsigned long) password_len);
+ eap_ttls_state(data, FAILURE);
+ return;
+ }
+
+ /* TODO: add support for verifying that the user entry accepts
+ * EAP-TTLS/CHAP. */
+ if (!sm->user || !sm->user->password) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS/CHAP: No user password "
+ "configured");
+ eap_ttls_state(data, FAILURE);
+ return;
+ }
+
+ chal = eap_tls_derive_key(sm, &data->ssl, "ttls challenge",
+ EAP_TTLS_CHAP_CHALLENGE_LEN + 1);
+ if (chal == NULL) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS/CHAP: Failed to generate "
+ "challenge from TLS data");
+ eap_ttls_state(data, FAILURE);
+ return;
+ }
+
+ if (memcmp(challenge, chal, EAP_TTLS_CHAP_CHALLENGE_LEN) != 0 ||
+ password[0] != chal[EAP_TTLS_CHAP_CHALLENGE_LEN]) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS/CHAP: Challenge mismatch");
+ free(chal);
+ eap_ttls_state(data, FAILURE);
+ return;
+ }
+ free(chal);
+
+ /* MD5(Ident + Password + Challenge) */
+ MD5Init(&context);
+ MD5Update(&context, password, 1);
+ MD5Update(&context, sm->user->password, sm->user->password_len);
+ MD5Update(&context, challenge, challenge_len);
+ MD5Final(hash, &context);
+
+ if (memcmp(hash, password + 1, EAP_TTLS_CHAP_PASSWORD_LEN) == 0) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS/CHAP: Correct user password");
+ eap_ttls_state(data, SUCCESS);
+ } else {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS/CHAP: Invalid user password");
+ eap_ttls_state(data, FAILURE);
+ }
+}
+
+
+static void eap_ttls_process_phase2_mschap(struct eap_sm *sm,
+ struct eap_ttls_data *data,
+ u8 *challenge, size_t challenge_len,
+ u8 *response, size_t response_len)
+{
+ u8 *chal, nt_response[24];
+
+ if (challenge == NULL || response == NULL ||
+ challenge_len != EAP_TTLS_MSCHAP_CHALLENGE_LEN ||
+ response_len != EAP_TTLS_MSCHAP_RESPONSE_LEN) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAP: Invalid MS-CHAP "
+ "attributes (challenge len %lu response len %lu)",
+ (unsigned long) challenge_len,
+ (unsigned long) response_len);
+ eap_ttls_state(data, FAILURE);
+ return;
+ }
+
+ /* TODO: add support for verifying that the user entry accepts
+ * EAP-TTLS/MSCHAP. */
+ if (!sm->user || !sm->user->password) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAP: No user password "
+ "configured");
+ eap_ttls_state(data, FAILURE);
+ return;
+ }
+
+ chal = eap_tls_derive_key(sm, &data->ssl, "ttls challenge",
+ EAP_TTLS_MSCHAP_CHALLENGE_LEN + 1);
+ if (chal == NULL) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAP: Failed to generate "
+ "challenge from TLS data");
+ eap_ttls_state(data, FAILURE);
+ return;
+ }
+
+ if (memcmp(challenge, chal, EAP_TTLS_MSCHAP_CHALLENGE_LEN) != 0 ||
+ response[0] != chal[EAP_TTLS_MSCHAP_CHALLENGE_LEN]) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAP: Challenge mismatch");
+ free(chal);
+ eap_ttls_state(data, FAILURE);
+ return;
+ }
+ free(chal);
+
+ nt_challenge_response(challenge, sm->user->password,
+ sm->user->password_len, nt_response);
+
+ if (memcmp(nt_response, response + 2 + 24, 24) == 0) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAP: Correct response");
+ eap_ttls_state(data, SUCCESS);
+ } else {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAP: Invalid NT-Response");
+ wpa_hexdump(MSG_MSGDUMP, "EAP-TTLS/MSCHAP: Received",
+ response + 2 + 24, 24);
+ wpa_hexdump(MSG_MSGDUMP, "EAP-TTLS/MSCHAP: Expected",
+ nt_response, 24);
+ eap_ttls_state(data, FAILURE);
+ }
+}
+
+
+static void eap_ttls_process_phase2_mschapv2(struct eap_sm *sm,
+ struct eap_ttls_data *data,
+ u8 *challenge,
+ size_t challenge_len,
+ u8 *response, size_t response_len)
+{
+ u8 *chal, *username, nt_response[24], *pos, *rx_resp, *peer_challenge,
+ *auth_challenge;
+ size_t username_len;
+ int i;
+
+ if (challenge == NULL || response == NULL ||
+ challenge_len != EAP_TTLS_MSCHAPV2_CHALLENGE_LEN ||
+ response_len != EAP_TTLS_MSCHAPV2_RESPONSE_LEN) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Invalid MS-CHAP2 "
+ "attributes (challenge len %lu response len %lu)",
+ (unsigned long) challenge_len,
+ (unsigned long) response_len);
+ eap_ttls_state(data, FAILURE);
+ return;
+ }
+
+ /* TODO: add support for verifying that the user entry accepts
+ * EAP-TTLS/MSCHAPV2. */
+ if (!sm->user || !sm->user->password) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: No user password "
+ "configured");
+ eap_ttls_state(data, FAILURE);
+ return;
+ }
+
+ /* MSCHAPv2 does not include optional domain name in the
+ * challenge-response calculation, so remove domain prefix
+ * (if present). */
+ username = sm->identity;
+ username_len = sm->identity_len;
+ pos = username;
+ for (i = 0; i < username_len; i++) {
+ if (username[i] == '\\') {
+ username_len -= i + 1;
+ username += i + 1;
+ break;
+ }
+ }
+
+ chal = eap_tls_derive_key(sm, &data->ssl, "ttls challenge",
+ EAP_TTLS_MSCHAPV2_CHALLENGE_LEN + 1);
+ if (chal == NULL) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Failed to generate "
+ "challenge from TLS data");
+ eap_ttls_state(data, FAILURE);
+ return;
+ }
+
+ if (memcmp(challenge, chal, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN) != 0 ||
+ response[0] != chal[EAP_TTLS_MSCHAPV2_CHALLENGE_LEN]) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Challenge mismatch");
+ free(chal);
+ eap_ttls_state(data, FAILURE);
+ return;
+ }
+ free(chal);
+
+ auth_challenge = challenge;
+ peer_challenge = response + 2;
+
+ wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-TTLS/MSCHAPV2: User",
+ username, username_len);
+ wpa_hexdump(MSG_MSGDUMP, "EAP-TTLS/MSCHAPV2: auth_challenge",
+ auth_challenge, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN);
+ wpa_hexdump(MSG_MSGDUMP, "EAP-TTLS/MSCHAPV2: peer_challenge",
+ peer_challenge, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN);
+
+ generate_nt_response(auth_challenge, peer_challenge,
+ username, username_len,
+ sm->user->password, sm->user->password_len,
+ nt_response);
+
+ rx_resp = response + 2 + EAP_TTLS_MSCHAPV2_CHALLENGE_LEN + 8;
+ if (memcmp(nt_response, rx_resp, 24) == 0) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Correct "
+ "NT-Response");
+ data->mschapv2_resp_ok = 1;
+
+ generate_authenticator_response(sm->user->password,
+ sm->user->password_len,
+ peer_challenge,
+ auth_challenge,
+ username, username_len,
+ nt_response,
+ data->mschapv2_auth_response);
+
+ } else {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Invalid "
+ "NT-Response");
+ wpa_hexdump(MSG_MSGDUMP, "EAP-TTLS/MSCHAPV2: Received",
+ rx_resp, 24);
+ wpa_hexdump(MSG_MSGDUMP, "EAP-TTLS/MSCHAPV2: Expected",
+ nt_response, 24);
+ data->mschapv2_resp_ok = 0;
+ }
+ eap_ttls_state(data, PHASE2_MSCHAPV2_RESP);
+ data->mschapv2_ident = response[0];
+}
+
+
+static int eap_ttls_phase2_eap_init(struct eap_sm *sm,
+ struct eap_ttls_data *data, u8 eap_type)
+{
+ if (data->phase2_priv && data->phase2_method) {
+ data->phase2_method->reset(sm, data->phase2_priv);
+ data->phase2_method = NULL;
+ data->phase2_priv = NULL;
+ }
+ data->phase2_method = eap_sm_get_eap_methods(eap_type);
+ if (!data->phase2_method)
+ return -1;
+
+ sm->init_phase2 = 1;
+ data->phase2_priv = data->phase2_method->init(sm);
+ sm->init_phase2 = 0;
+ return 0;
+}
+
+
+static void eap_ttls_process_phase2_eap_response(struct eap_sm *sm,
+ struct eap_ttls_data *data,
+ u8 *in_data, size_t in_len)
+{
+ u8 next_type = EAP_TYPE_NONE;
+ struct eap_hdr *hdr;
+ u8 *pos;
+ size_t left;
+
+ if (data->phase2_priv == NULL) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS/EAP: %s - Phase2 not "
+ "initialized?!", __func__);
+ return;
+ }
+
+ hdr = (struct eap_hdr *) in_data;
+ pos = (u8 *) (hdr + 1);
+ left = in_len - sizeof(*hdr);
+
+ if (in_len > sizeof(*hdr) && *pos == EAP_TYPE_NAK) {
+ wpa_hexdump(MSG_DEBUG, "EAP-TTLS/EAP: Phase2 type Nak'ed; "
+ "allowed types", pos + 1, left - 1);
+ eap_sm_process_nak(sm, pos + 1, left - 1);
+ if (sm->user && sm->user_eap_method_index < EAP_MAX_METHODS &&
+ sm->user->methods[sm->user_eap_method_index] !=
+ EAP_TYPE_NONE) {
+ next_type =
+ sm->user->methods[sm->user_eap_method_index++];
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: try EAP type %d",
+ next_type);
+ eap_ttls_phase2_eap_init(sm, data, next_type);
+ } else {
+ eap_ttls_state(data, FAILURE);
+ }
+ return;
+ }
+
+ if (data->phase2_method->check(sm, data->phase2_priv, in_data,
+ in_len)) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS/EAP: Phase2 check() asked to "
+ "ignore the packet");
+ return;
+ }
+
+ data->phase2_method->process(sm, data->phase2_priv, in_data, in_len);
+
+ if (!data->phase2_method->isDone(sm, data->phase2_priv))
+ return;
+
+ if (!data->phase2_method->isSuccess(sm, data->phase2_priv)) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS/EAP: Phase2 method failed");
+ eap_ttls_state(data, FAILURE);
+ return;
+ }
+
+ switch (data->state) {
+ case PHASE2_START:
+ if (eap_user_get(sm, sm->identity, sm->identity_len, 1) != 0) {
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP_TTLS: Phase2 "
+ "Identity not found in the user "
+ "database",
+ sm->identity, sm->identity_len);
+ eap_ttls_state(data, FAILURE);
+ break;
+ }
+
+ eap_ttls_state(data, PHASE2_METHOD);
+ next_type = sm->user->methods[0];
+ sm->user_eap_method_index = 1;
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: try EAP type %d", next_type);
+ break;
+ case PHASE2_METHOD:
+ eap_ttls_state(data, SUCCESS);
+ break;
+ case FAILURE:
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: %s - unexpected state %d",
+ __func__, data->state);
+ break;
+ }
+
+ eap_ttls_phase2_eap_init(sm, data, next_type);
+}
+
+
+static void eap_ttls_process_phase2_eap(struct eap_sm *sm,
+ struct eap_ttls_data *data,
+ const u8 *eap, size_t eap_len)
+{
+ struct eap_hdr *hdr;
+ size_t len;
+
+ if (data->state == PHASE2_START) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS/EAP: initializing Phase 2");
+ if (eap_ttls_phase2_eap_init(sm, data, EAP_TYPE_IDENTITY) < 0)
+ {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS/EAP: failed to "
+ "initialize EAP-Identity");
+ return;
+ }
+ }
+
+ if (eap_len < sizeof(*hdr)) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS/EAP: too short Phase 2 EAP "
+ "packet (len=%lu)", (unsigned long) eap_len);
+ return;
+ }
+
+ hdr = (struct eap_hdr *) eap;
+ len = be_to_host16(hdr->length);
+ wpa_printf(MSG_DEBUG, "EAP-TTLS/EAP: received Phase 2 EAP: code=%d "
+ "identifier=%d length=%lu", hdr->code, hdr->identifier,
+ (unsigned long) len);
+ if (len > eap_len) {
+ wpa_printf(MSG_INFO, "EAP-TTLS/EAP: Length mismatch in Phase 2"
+ " EAP frame (hdr len=%lu, data len in AVP=%lu)",
+ (unsigned long) len, (unsigned long) eap_len);
+ return;
+ }
+
+ switch (hdr->code) {
+ case EAP_CODE_RESPONSE:
+ eap_ttls_process_phase2_eap_response(sm, data, (u8 *) hdr,
+ len);
+ break;
+ default:
+ wpa_printf(MSG_INFO, "EAP-TTLS/EAP: Unexpected code=%d in "
+ "Phase 2 EAP header", hdr->code);
+ break;
+ }
+}
+
+
+static void eap_ttls_process_phase2(struct eap_sm *sm,
+ struct eap_ttls_data *data,
+ struct eap_hdr *resp,
+ u8 *in_data, size_t in_len)
+{
+ u8 *in_decrypted;
+ int buf_len, len_decrypted, res;
+ struct eap_ttls_avp parse;
+
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: 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;
+
+ 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");
+ return;
+ }
+
+ 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");
+ free(in_decrypted);
+ eap_ttls_state(data, FAILURE);
+ return;
+ }
+
+ wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: Decrypted Phase 2 EAP",
+ in_decrypted, len_decrypted);
+
+ if (eap_ttls_avp_parse(in_decrypted, len_decrypted, &parse) < 0) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Failed to parse AVPs");
+ free(in_decrypted);
+ eap_ttls_state(data, FAILURE);
+ return;
+ }
+
+ if (parse.user_name) {
+ free(sm->identity);
+ sm->identity = malloc(parse.user_name_len);
+ if (sm->identity) {
+ memcpy(sm->identity, parse.user_name,
+ parse.user_name_len);
+ sm->identity_len = parse.user_name_len;
+ }
+ if (eap_user_get(sm, parse.user_name, parse.user_name_len, 1)
+ != 0) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase2 Identity not "
+ "found in the user database");
+ eap_ttls_state(data, FAILURE);
+ goto done;
+ }
+ }
+
+ if (parse.eap) {
+ eap_ttls_process_phase2_eap(sm, data, parse.eap,
+ parse.eap_len);
+ } else if (parse.user_password) {
+ eap_ttls_process_phase2_pap(sm, data, parse.user_password,
+ parse.user_password_len);
+ } else if (parse.chap_password) {
+ eap_ttls_process_phase2_chap(sm, data,
+ parse.chap_challenge,
+ parse.chap_challenge_len,
+ parse.chap_password,
+ parse.chap_password_len);
+ } else if (parse.mschap_response) {
+ eap_ttls_process_phase2_mschap(sm, data,
+ parse.mschap_challenge,
+ parse.mschap_challenge_len,
+ parse.mschap_response,
+ parse.mschap_response_len);
+ } else if (parse.mschap2_response) {
+ eap_ttls_process_phase2_mschapv2(sm, data,
+ parse.mschap_challenge,
+ parse.mschap_challenge_len,
+ parse.mschap2_response,
+ parse.mschap2_response_len);
+ }
+
+done:
+ free(in_decrypted);
+ free(parse.eap);
+ }
+
+
+static void eap_ttls_process(struct eap_sm *sm, void *priv,
+ u8 *respData, size_t respDataLen)
+{
+ struct eap_ttls_data *data = priv;
+ struct eap_hdr *resp;
+ u8 *pos, flags;
+ int left;
+ unsigned int tls_msg_len;
+ int peer_version;
+
+ resp = (struct eap_hdr *) respData;
+ pos = (u8 *) (resp + 1);
+ pos++;
+ flags = *pos++;
+ left = htons(resp->length) - sizeof(struct eap_hdr) - 2;
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Received packet(len=%lu) - "
+ "Flags 0x%02x", (unsigned long) respDataLen, flags);
+ peer_version = flags & EAP_PEAP_VERSION_MASK;
+ if (peer_version < data->ttls_version) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: peer ver=%d, own ver=%d; "
+ "use version %d",
+ peer_version, data->ttls_version, peer_version);
+ data->ttls_version = peer_version;
+
+ }
+ if (flags & EAP_TLS_FLAGS_LENGTH_INCLUDED) {
+ if (left < 4) {
+ wpa_printf(MSG_INFO, "EAP-TTLS: Short frame with TLS "
+ "length");
+ eap_ttls_state(data, FAILURE);
+ return;
+ }
+ 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;
+ }
+
+ switch (data->state) {
+ case PHASE1:
+ if (eap_tls_process_helper(sm, &data->ssl, pos, left) < 0) {
+ wpa_printf(MSG_INFO, "EAP-TTLS: TLS processing "
+ "failed");
+ eap_ttls_state(data, FAILURE);
+ }
+ break;
+ case PHASE2_START:
+ case PHASE2_METHOD:
+ eap_ttls_process_phase2(sm, data, resp, pos, left);
+ break;
+ case PHASE2_MSCHAPV2_RESP:
+ if (data->mschapv2_resp_ok && left == 0) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Peer "
+ "acknowledged response");
+ eap_ttls_state(data, SUCCESS);
+ } else if (!data->mschapv2_resp_ok) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Peer "
+ "acknowledged error");
+ eap_ttls_state(data, FAILURE);
+ } else {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Unexpected "
+ "frame from peer (payload len %d, expected "
+ "empty frame)", left);
+ eap_ttls_state(data, FAILURE);
+ }
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Unexpected state %d in %s",
+ data->state, __func__);
+ break;
+ }
+}
+
+
+static Boolean eap_ttls_isDone(struct eap_sm *sm, void *priv)
+{
+ struct eap_ttls_data *data = priv;
+ return data->state == SUCCESS || data->state == FAILURE;
+}
+
+
+static u8 * eap_ttls_getKey(struct eap_sm *sm, void *priv, size_t *len)
+{
+ struct eap_ttls_data *data = priv;
+ u8 *eapKeyData;
+
+ if (data->state != SUCCESS)
+ return NULL;
+
+ eapKeyData = eap_tls_derive_key(sm, &data->ssl,
+ "ttls keying material",
+ EAP_TLS_KEY_LEN);
+ if (eapKeyData) {
+ *len = EAP_TLS_KEY_LEN;
+ wpa_hexdump(MSG_DEBUG, "EAP-TTLS: Derived key",
+ eapKeyData, EAP_TLS_KEY_LEN);
+ } else {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Failed to derive key");
+ }
+
+ return eapKeyData;
+}
+
+
+static Boolean eap_ttls_isSuccess(struct eap_sm *sm, void *priv)
+{
+ struct eap_ttls_data *data = priv;
+ return data->state == SUCCESS;
+}
+
+
+const struct eap_method eap_method_ttls =
+{
+ .method = EAP_TYPE_TTLS,
+ .name = "TTLS",
+ .init = eap_ttls_init,
+ .reset = eap_ttls_reset,
+ .buildReq = eap_ttls_buildReq,
+ .check = eap_ttls_check,
+ .process = eap_ttls_process,
+ .isDone = eap_ttls_isDone,
+ .getKey = eap_ttls_getKey,
+ .isSuccess = eap_ttls_isSuccess,
+};
diff --git a/contrib/hostapd/eap_ttls.h b/contrib/hostapd/eap_ttls.h
new file mode 100644
index 000000000000..a187db4d34f7
--- /dev/null
+++ b/contrib/hostapd/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/hostapd/eapol_sm.c b/contrib/hostapd/eapol_sm.c
new file mode 100644
index 000000000000..fce7c6d3391a
--- /dev/null
+++ b/contrib/hostapd/eapol_sm.c
@@ -0,0 +1,1183 @@
+/*
+ * Host AP (software wireless LAN access point) user space daemon for
+ * Host AP kernel driver / IEEE 802.1X Authenticator - EAPOL state machine
+ * 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 <unistd.h>
+#include <netinet/in.h>
+#include <string.h>
+#include <sys/socket.h>
+
+#include "hostapd.h"
+#include "ieee802_1x.h"
+#include "eapol_sm.h"
+#include "eloop.h"
+#include "wpa.h"
+#include "sta_info.h"
+#include "eap.h"
+
+static struct eapol_callbacks eapol_cb;
+
+/* EAPOL state machines are described in IEEE Std 802.1X-REV-d11, Chap. 8.2 */
+
+#define setPortAuthorized() \
+ieee802_1x_set_sta_authorized(sm->hapd, sm->sta, 1)
+#define setPortUnauthorized() \
+ieee802_1x_set_sta_authorized(sm->hapd, sm->sta, 0)
+
+/* procedures */
+#define txCannedFail() ieee802_1x_tx_canned_eap(sm->hapd, sm->sta, 0)
+#define txCannedSuccess() ieee802_1x_tx_canned_eap(sm->hapd, sm->sta, 1)
+#define txReq() ieee802_1x_tx_req(sm->hapd, sm->sta)
+#define sendRespToServer() ieee802_1x_send_resp_to_server(sm->hapd, sm->sta)
+#define abortAuth() ieee802_1x_abort_auth(sm->hapd, sm->sta)
+#define txKey() ieee802_1x_tx_key(sm->hapd, sm->sta)
+#define processKey() do { } while (0)
+
+
+/* Definitions for clarifying state machine implementation */
+#define SM_STATE(machine, state) \
+static void sm_ ## machine ## _ ## state ## _Enter(struct eapol_state_machine \
+*sm)
+
+#define SM_ENTRY(machine, _state, _data) \
+sm->_data.state = machine ## _ ## _state; \
+if (sm->hapd->conf->debug >= HOSTAPD_DEBUG_MINIMAL) \
+ printf("IEEE 802.1X: " MACSTR " " #machine " entering state " #_state \
+ "\n", MAC2STR(sm->addr));
+
+#define SM_ENTER(machine, state) sm_ ## machine ## _ ## state ## _Enter(sm)
+
+#define SM_STEP(machine) \
+static void sm_ ## machine ## _Step(struct eapol_state_machine *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_state_machine *state = timeout_ctx;
+
+ if (state->aWhile > 0)
+ state->aWhile--;
+ if (state->quietWhile > 0)
+ state->quietWhile--;
+ if (state->reAuthWhen > 0)
+ state->reAuthWhen--;
+
+ if (state->hapd->conf->debug >= HOSTAPD_DEBUG_MSGDUMPS)
+ printf("IEEE 802.1X: " MACSTR " Port Timers TICK "
+ "(timers: %d %d %d)\n", MAC2STR(state->addr),
+ state->aWhile, state->quietWhile, state->reAuthWhen);
+
+ eapol_sm_step(state);
+
+ eloop_register_timeout(1, 0, eapol_port_timers_tick, eloop_ctx, state);
+}
+
+
+
+/* Authenticator PAE state machine */
+
+SM_STATE(AUTH_PAE, INITIALIZE)
+{
+ SM_ENTRY(AUTH_PAE, INITIALIZE, auth_pae);
+ sm->auth_pae.portMode = Auto;
+
+ sm->currentId = 255;
+}
+
+
+SM_STATE(AUTH_PAE, DISCONNECTED)
+{
+ int from_initialize = sm->auth_pae.state == AUTH_PAE_INITIALIZE;
+
+ if (sm->auth_pae.eapolLogoff) {
+ if (sm->auth_pae.state == AUTH_PAE_CONNECTING)
+ sm->auth_pae.authEapLogoffsWhileConnecting++;
+ else if (sm->auth_pae.state == AUTH_PAE_AUTHENTICATED)
+ sm->auth_pae.authAuthEapLogoffWhileAuthenticated++;
+ }
+
+ SM_ENTRY(AUTH_PAE, DISCONNECTED, auth_pae);
+
+ sm->authPortStatus = Unauthorized;
+ setPortUnauthorized();
+ sm->auth_pae.reAuthCount = 0;
+ sm->auth_pae.eapolLogoff = FALSE;
+ if (!from_initialize) {
+ if (sm->flags & EAPOL_SM_PREAUTH)
+ rsn_preauth_finished(sm->hapd, sm->sta, 0);
+ }
+}
+
+
+SM_STATE(AUTH_PAE, RESTART)
+{
+ if (sm->auth_pae.state == AUTH_PAE_AUTHENTICATED) {
+ if (sm->reAuthenticate)
+ sm->auth_pae.authAuthReauthsWhileAuthenticated++;
+ if (sm->auth_pae.eapolStart)
+ sm->auth_pae.authAuthEapStartsWhileAuthenticated++;
+ if (sm->auth_pae.eapolLogoff)
+ sm->auth_pae.authAuthEapLogoffWhileAuthenticated++;
+ }
+
+ SM_ENTRY(AUTH_PAE, RESTART, auth_pae);
+
+ sm->auth_pae.eapRestart = TRUE;
+ ieee802_1x_request_identity(sm->hapd, sm->sta);
+}
+
+
+SM_STATE(AUTH_PAE, CONNECTING)
+{
+ if (sm->auth_pae.state != AUTH_PAE_CONNECTING)
+ sm->auth_pae.authEntersConnecting++;
+
+ SM_ENTRY(AUTH_PAE, CONNECTING, auth_pae);
+
+ sm->reAuthenticate = FALSE;
+ sm->auth_pae.reAuthCount++;
+}
+
+
+SM_STATE(AUTH_PAE, HELD)
+{
+ if (sm->auth_pae.state == AUTH_PAE_AUTHENTICATING && sm->authFail)
+ sm->auth_pae.authAuthFailWhileAuthenticating++;
+
+ SM_ENTRY(AUTH_PAE, HELD, auth_pae);
+
+ sm->authPortStatus = Unauthorized;
+ setPortUnauthorized();
+ sm->quietWhile = sm->auth_pae.quietPeriod;
+ sm->auth_pae.eapolLogoff = FALSE;
+
+ hostapd_logger(sm->hapd, sm->addr, HOSTAPD_MODULE_IEEE8021X,
+ HOSTAPD_LEVEL_WARNING, "authentication failed");
+ if (sm->flags & EAPOL_SM_PREAUTH)
+ rsn_preauth_finished(sm->hapd, sm->sta, 0);
+}
+
+
+SM_STATE(AUTH_PAE, AUTHENTICATED)
+{
+ if (sm->auth_pae.state == AUTH_PAE_AUTHENTICATING && sm->authSuccess)
+ sm->auth_pae.authAuthSuccessesWhileAuthenticating++;
+
+ SM_ENTRY(AUTH_PAE, AUTHENTICATED, auth_pae);
+
+ sm->authPortStatus = Authorized;
+ setPortAuthorized();
+ sm->auth_pae.reAuthCount = 0;
+ hostapd_logger(sm->hapd, sm->addr, HOSTAPD_MODULE_IEEE8021X,
+ HOSTAPD_LEVEL_INFO, "authenticated");
+ if (sm->flags & EAPOL_SM_PREAUTH)
+ rsn_preauth_finished(sm->hapd, sm->sta, 1);
+}
+
+
+SM_STATE(AUTH_PAE, AUTHENTICATING)
+{
+ if (sm->auth_pae.state == AUTH_PAE_CONNECTING && sm->rx_identity) {
+ sm->auth_pae.authEntersAuthenticating++;
+ sm->rx_identity = FALSE;
+ }
+
+ SM_ENTRY(AUTH_PAE, AUTHENTICATING, auth_pae);
+
+ sm->auth_pae.eapolStart = FALSE;
+ sm->authSuccess = FALSE;
+ sm->authFail = FALSE;
+ sm->authTimeout = FALSE;
+ sm->authStart = TRUE;
+ sm->keyRun = FALSE;
+ sm->keyDone = FALSE;
+}
+
+
+SM_STATE(AUTH_PAE, ABORTING)
+{
+ if (sm->auth_pae.state == AUTH_PAE_AUTHENTICATING) {
+ if (sm->authTimeout)
+ sm->auth_pae.authAuthTimeoutsWhileAuthenticating++;
+ if (sm->auth_pae.eapolStart)
+ sm->auth_pae.authAuthEapStartsWhileAuthenticating++;
+ if (sm->auth_pae.eapolLogoff)
+ sm->auth_pae.authAuthEapLogoffWhileAuthenticating++;
+ }
+
+ SM_ENTRY(AUTH_PAE, ABORTING, auth_pae);
+
+ sm->authAbort = TRUE;
+ sm->keyRun = FALSE;
+ sm->keyDone = FALSE;
+}
+
+
+SM_STATE(AUTH_PAE, FORCE_AUTH)
+{
+ SM_ENTRY(AUTH_PAE, FORCE_AUTH, auth_pae);
+
+ sm->authPortStatus = Authorized;
+ setPortAuthorized();
+ sm->auth_pae.portMode = ForceAuthorized;
+ sm->auth_pae.eapolStart = FALSE;
+ txCannedSuccess();
+}
+
+
+SM_STATE(AUTH_PAE, FORCE_UNAUTH)
+{
+ SM_ENTRY(AUTH_PAE, FORCE_UNAUTH, auth_pae);
+
+ sm->authPortStatus = Unauthorized;
+ setPortUnauthorized();
+ sm->auth_pae.portMode = ForceUnauthorized;
+ sm->auth_pae.eapolStart = FALSE;
+ txCannedFail();
+}
+
+
+SM_STEP(AUTH_PAE)
+{
+ if ((sm->portControl == Auto &&
+ sm->auth_pae.portMode != sm->portControl) ||
+ sm->initialize || !sm->portEnabled)
+ SM_ENTER(AUTH_PAE, INITIALIZE);
+ else if (sm->portControl == ForceAuthorized &&
+ sm->auth_pae.portMode != sm->portControl &&
+ !(sm->initialize || !sm->portEnabled))
+ SM_ENTER(AUTH_PAE, FORCE_AUTH);
+ else if (sm->portControl == ForceUnauthorized &&
+ sm->auth_pae.portMode != sm->portControl &&
+ !(sm->initialize || !sm->portEnabled))
+ SM_ENTER(AUTH_PAE, FORCE_UNAUTH);
+ else {
+ switch (sm->auth_pae.state) {
+ case AUTH_PAE_INITIALIZE:
+ SM_ENTER(AUTH_PAE, DISCONNECTED);
+ break;
+ case AUTH_PAE_DISCONNECTED:
+ SM_ENTER(AUTH_PAE, RESTART);
+ break;
+ case AUTH_PAE_RESTART:
+ if (!sm->auth_pae.eapRestart)
+ SM_ENTER(AUTH_PAE, CONNECTING);
+ break;
+ case AUTH_PAE_HELD:
+ if (sm->quietWhile == 0)
+ SM_ENTER(AUTH_PAE, RESTART);
+ break;
+ case AUTH_PAE_CONNECTING:
+ if (sm->auth_pae.eapolLogoff ||
+ sm->auth_pae.reAuthCount > sm->auth_pae.reAuthMax)
+ SM_ENTER(AUTH_PAE, DISCONNECTED);
+ else if ((sm->be_auth.eapReq &&
+ sm->auth_pae.reAuthCount <=
+ sm->auth_pae.reAuthMax) ||
+ sm->eapSuccess || sm->eapFail)
+ SM_ENTER(AUTH_PAE, AUTHENTICATING);
+ break;
+ case AUTH_PAE_AUTHENTICATED:
+ if (sm->auth_pae.eapolStart || sm->reAuthenticate)
+ SM_ENTER(AUTH_PAE, RESTART);
+ else if (sm->auth_pae.eapolLogoff || !sm->portValid)
+ SM_ENTER(AUTH_PAE, DISCONNECTED);
+ break;
+ case AUTH_PAE_AUTHENTICATING:
+ if (sm->authSuccess && sm->portValid)
+ SM_ENTER(AUTH_PAE, AUTHENTICATED);
+ else if (sm->authFail ||
+ (sm->keyDone && !sm->portValid))
+ SM_ENTER(AUTH_PAE, HELD);
+ else if (sm->auth_pae.eapolStart ||
+ sm->auth_pae.eapolLogoff || sm->authTimeout)
+ SM_ENTER(AUTH_PAE, ABORTING);
+ break;
+ case AUTH_PAE_ABORTING:
+ if (sm->auth_pae.eapolLogoff && !sm->authAbort)
+ SM_ENTER(AUTH_PAE, DISCONNECTED);
+ else if (!sm->auth_pae.eapolLogoff && !sm->authAbort)
+ SM_ENTER(AUTH_PAE, RESTART);
+ break;
+ case AUTH_PAE_FORCE_AUTH:
+ if (sm->auth_pae.eapolStart)
+ SM_ENTER(AUTH_PAE, FORCE_AUTH);
+ break;
+ case AUTH_PAE_FORCE_UNAUTH:
+ if (sm->auth_pae.eapolStart)
+ SM_ENTER(AUTH_PAE, FORCE_UNAUTH);
+ break;
+ }
+ }
+}
+
+
+
+/* Backend Authentication state machine */
+
+SM_STATE(BE_AUTH, INITIALIZE)
+{
+ SM_ENTRY(BE_AUTH, INITIALIZE, be_auth);
+
+ abortAuth();
+ sm->be_auth.eapNoReq = FALSE;
+ sm->authAbort = FALSE;
+}
+
+
+SM_STATE(BE_AUTH, REQUEST)
+{
+ SM_ENTRY(BE_AUTH, REQUEST, be_auth);
+
+ txReq();
+ sm->be_auth.eapReq = FALSE;
+ sm->be_auth.backendOtherRequestsToSupplicant++;
+}
+
+
+SM_STATE(BE_AUTH, RESPONSE)
+{
+ SM_ENTRY(BE_AUTH, RESPONSE, be_auth);
+
+ sm->authTimeout = FALSE;
+ sm->eapolEap = FALSE;
+ sm->be_auth.eapNoReq = FALSE;
+ sm->aWhile = sm->be_auth.serverTimeout;
+ sm->be_auth.eapResp = TRUE;
+ sendRespToServer();
+ sm->be_auth.backendResponses++;
+}
+
+
+SM_STATE(BE_AUTH, SUCCESS)
+{
+ SM_ENTRY(BE_AUTH, SUCCESS, be_auth);
+
+ txReq();
+ sm->authSuccess = TRUE;
+ sm->keyRun = TRUE;
+}
+
+
+SM_STATE(BE_AUTH, FAIL)
+{
+ SM_ENTRY(BE_AUTH, FAIL, be_auth);
+
+ /* Note: IEEE 802.1X-REV-d11 has unconditional txReq() here.
+ * txCannelFail() is used as a workaround for the case where
+ * authentication server does not include EAP-Message with
+ * Access-Reject. */
+ if (sm->last_eap_radius == NULL)
+ txCannedFail();
+ else
+ txReq();
+ sm->authFail = TRUE;
+}
+
+
+SM_STATE(BE_AUTH, TIMEOUT)
+{
+ SM_ENTRY(BE_AUTH, TIMEOUT, be_auth);
+
+ sm->authTimeout = TRUE;
+}
+
+
+SM_STATE(BE_AUTH, IDLE)
+{
+ SM_ENTRY(BE_AUTH, IDLE, be_auth);
+
+ sm->authStart = FALSE;
+}
+
+
+SM_STATE(BE_AUTH, IGNORE)
+{
+ SM_ENTRY(BE_AUTH, IGNORE, be_auth);
+
+ sm->be_auth.eapNoReq = FALSE;
+}
+
+
+SM_STEP(BE_AUTH)
+{
+ if (sm->portControl != Auto || sm->initialize || sm->authAbort) {
+ SM_ENTER(BE_AUTH, INITIALIZE);
+ return;
+ }
+
+ switch (sm->be_auth.state) {
+ case BE_AUTH_INITIALIZE:
+ SM_ENTER(BE_AUTH, IDLE);
+ break;
+ case BE_AUTH_REQUEST:
+ if (sm->eapolEap)
+ SM_ENTER(BE_AUTH, RESPONSE);
+ else if (sm->be_auth.eapReq)
+ SM_ENTER(BE_AUTH, REQUEST);
+ else if (sm->eapTimeout)
+ SM_ENTER(BE_AUTH, TIMEOUT);
+ break;
+ case BE_AUTH_RESPONSE:
+ if (sm->be_auth.eapNoReq)
+ SM_ENTER(BE_AUTH, IGNORE);
+ if (sm->be_auth.eapReq) {
+ sm->be_auth.backendAccessChallenges++;
+ SM_ENTER(BE_AUTH, REQUEST);
+ } else if (sm->aWhile == 0)
+ SM_ENTER(BE_AUTH, TIMEOUT);
+ else if (sm->eapFail) {
+ sm->be_auth.backendAuthFails++;
+ SM_ENTER(BE_AUTH, FAIL);
+ } else if (sm->eapSuccess) {
+ sm->be_auth.backendAuthSuccesses++;
+ SM_ENTER(BE_AUTH, SUCCESS);
+ }
+ break;
+ case BE_AUTH_SUCCESS:
+ SM_ENTER(BE_AUTH, IDLE);
+ break;
+ case BE_AUTH_FAIL:
+ SM_ENTER(BE_AUTH, IDLE);
+ break;
+ case BE_AUTH_TIMEOUT:
+ SM_ENTER(BE_AUTH, IDLE);
+ break;
+ case BE_AUTH_IDLE:
+ if (sm->eapFail && sm->authStart)
+ SM_ENTER(BE_AUTH, FAIL);
+ else if (sm->be_auth.eapReq && sm->authStart)
+ SM_ENTER(BE_AUTH, REQUEST);
+ else if (sm->eapSuccess && sm->authStart)
+ SM_ENTER(BE_AUTH, SUCCESS);
+ break;
+ case BE_AUTH_IGNORE:
+ if (sm->eapolEap)
+ SM_ENTER(BE_AUTH, RESPONSE);
+ else if (sm->be_auth.eapReq)
+ SM_ENTER(BE_AUTH, REQUEST);
+ else if (sm->eapTimeout)
+ SM_ENTER(BE_AUTH, TIMEOUT);
+ break;
+ }
+}
+
+
+
+/* Reauthentication Timer state machine */
+
+SM_STATE(REAUTH_TIMER, INITIALIZE)
+{
+ SM_ENTRY(REAUTH_TIMER, INITIALIZE, reauth_timer);
+
+ sm->reAuthWhen = sm->reauth_timer.reAuthPeriod;
+}
+
+
+SM_STATE(REAUTH_TIMER, REAUTHENTICATE)
+{
+ SM_ENTRY(REAUTH_TIMER, REAUTHENTICATE, reauth_timer);
+
+ sm->reAuthenticate = TRUE;
+ wpa_sm_event(sm->hapd, sm->sta, WPA_REAUTH_EAPOL);
+}
+
+
+SM_STEP(REAUTH_TIMER)
+{
+ if (sm->portControl != Auto || sm->initialize ||
+ sm->authPortStatus == Unauthorized ||
+ !sm->reauth_timer.reAuthEnabled) {
+ SM_ENTER(REAUTH_TIMER, INITIALIZE);
+ return;
+ }
+
+ switch (sm->reauth_timer.state) {
+ case REAUTH_TIMER_INITIALIZE:
+ if (sm->reAuthWhen == 0)
+ SM_ENTER(REAUTH_TIMER, REAUTHENTICATE);
+ break;
+ case REAUTH_TIMER_REAUTHENTICATE:
+ SM_ENTER(REAUTH_TIMER, INITIALIZE);
+ break;
+ }
+}
+
+
+
+/* Authenticator Key Transmit state machine */
+
+SM_STATE(AUTH_KEY_TX, NO_KEY_TRANSMIT)
+{
+ SM_ENTRY(AUTH_KEY_TX, NO_KEY_TRANSMIT, auth_key_tx);
+}
+
+
+SM_STATE(AUTH_KEY_TX, KEY_TRANSMIT)
+{
+ SM_ENTRY(AUTH_KEY_TX, KEY_TRANSMIT, auth_key_tx);
+
+ txKey();
+ sm->keyAvailable = FALSE;
+ sm->keyDone = TRUE;
+}
+
+
+SM_STEP(AUTH_KEY_TX)
+{
+ if (sm->initialize || sm->portControl != Auto) {
+ SM_ENTER(AUTH_KEY_TX, NO_KEY_TRANSMIT);
+ return;
+ }
+
+ switch (sm->auth_key_tx.state) {
+ case AUTH_KEY_TX_NO_KEY_TRANSMIT:
+ if (sm->keyTxEnabled && sm->keyAvailable && sm->keyRun &&
+ !sm->sta->wpa)
+ SM_ENTER(AUTH_KEY_TX, KEY_TRANSMIT);
+ break;
+ case AUTH_KEY_TX_KEY_TRANSMIT:
+ if (!sm->keyTxEnabled || !sm->keyRun)
+ SM_ENTER(AUTH_KEY_TX, NO_KEY_TRANSMIT);
+ else if (sm->keyAvailable)
+ SM_ENTER(AUTH_KEY_TX, KEY_TRANSMIT);
+ break;
+ }
+}
+
+
+
+/* Key Receive state machine */
+
+SM_STATE(KEY_RX, NO_KEY_RECEIVE)
+{
+ SM_ENTRY(KEY_RX, NO_KEY_RECEIVE, key_rx);
+}
+
+
+SM_STATE(KEY_RX, KEY_RECEIVE)
+{
+ SM_ENTRY(KEY_RX, KEY_RECEIVE, key_rx);
+
+ processKey();
+ sm->key_rx.rxKey = FALSE;
+}
+
+
+SM_STEP(KEY_RX)
+{
+ if (sm->initialize || !sm->portEnabled) {
+ SM_ENTER(KEY_RX, NO_KEY_RECEIVE);
+ return;
+ }
+
+ switch (sm->key_rx.state) {
+ case KEY_RX_NO_KEY_RECEIVE:
+ if (sm->key_rx.rxKey)
+ SM_ENTER(KEY_RX, KEY_RECEIVE);
+ break;
+ case KEY_RX_KEY_RECEIVE:
+ if (sm->key_rx.rxKey)
+ SM_ENTER(KEY_RX, KEY_RECEIVE);
+ break;
+ }
+}
+
+
+
+/* Controlled Directions state machine */
+
+SM_STATE(CTRL_DIR, FORCE_BOTH)
+{
+ SM_ENTRY(CTRL_DIR, FORCE_BOTH, ctrl_dir);
+ sm->ctrl_dir.operControlledDirections = Both;
+}
+
+
+SM_STATE(CTRL_DIR, IN_OR_BOTH)
+{
+ SM_ENTRY(CTRL_DIR, IN_OR_BOTH, ctrl_dir);
+ sm->ctrl_dir.operControlledDirections =
+ sm->ctrl_dir.adminControlledDirections;
+}
+
+
+SM_STEP(CTRL_DIR)
+{
+ if (sm->initialize) {
+ SM_ENTER(CTRL_DIR, IN_OR_BOTH);
+ return;
+ }
+
+ switch (sm->ctrl_dir.state) {
+ case CTRL_DIR_FORCE_BOTH:
+ if (sm->portEnabled && sm->ctrl_dir.operEdge)
+ SM_ENTER(CTRL_DIR, IN_OR_BOTH);
+ break;
+ case CTRL_DIR_IN_OR_BOTH:
+ if (sm->ctrl_dir.operControlledDirections !=
+ sm->ctrl_dir.adminControlledDirections)
+ SM_ENTER(CTRL_DIR, IN_OR_BOTH);
+ if (!sm->portEnabled || !sm->ctrl_dir.operEdge)
+ SM_ENTER(CTRL_DIR, FORCE_BOTH);
+ break;
+ }
+}
+
+
+
+struct eapol_state_machine *
+eapol_sm_alloc(hostapd *hapd, struct sta_info *sta)
+{
+ struct eapol_state_machine *sm;
+
+ sm = (struct eapol_state_machine *) malloc(sizeof(*sm));
+ if (sm == NULL) {
+ printf("IEEE 802.1X port state allocation failed\n");
+ return NULL;
+ }
+ memset(sm, 0, sizeof(*sm));
+ sm->radius_identifier = -1;
+ memcpy(sm->addr, sta->addr, ETH_ALEN);
+ if (sta->flags & WLAN_STA_PREAUTH)
+ sm->flags |= EAPOL_SM_PREAUTH;
+
+ sm->hapd = hapd;
+ sm->sta = sta;
+
+ /* Set default values for state machine constants */
+ sm->auth_pae.state = AUTH_PAE_INITIALIZE;
+ sm->auth_pae.quietPeriod = AUTH_PAE_DEFAULT_quietPeriod;
+ sm->auth_pae.reAuthMax = AUTH_PAE_DEFAULT_reAuthMax;
+
+ sm->be_auth.state = BE_AUTH_INITIALIZE;
+ sm->be_auth.serverTimeout = BE_AUTH_DEFAULT_serverTimeout;
+
+ sm->reauth_timer.state = REAUTH_TIMER_INITIALIZE;
+ sm->reauth_timer.reAuthPeriod = hapd->conf->eap_reauth_period;
+ sm->reauth_timer.reAuthEnabled = hapd->conf->eap_reauth_period > 0 ?
+ TRUE : FALSE;
+
+ sm->auth_key_tx.state = AUTH_KEY_TX_NO_KEY_TRANSMIT;
+
+ sm->key_rx.state = KEY_RX_NO_KEY_RECEIVE;
+
+ sm->ctrl_dir.state = CTRL_DIR_IN_OR_BOTH;
+
+ sm->portEnabled = FALSE;
+ sm->portControl = Auto;
+
+ sm->keyAvailable = FALSE;
+ if (!hapd->conf->wpa &&
+ (hapd->default_wep_key || hapd->conf->individual_wep_key_len > 0))
+ sm->keyTxEnabled = TRUE;
+ else
+ sm->keyTxEnabled = FALSE;
+ if (hapd->conf->wpa)
+ sm->portValid = FALSE;
+ else
+ sm->portValid = TRUE;
+
+ if (hapd->conf->eap_authenticator) {
+ struct eap_config eap_conf;
+ memset(&eap_conf, 0, sizeof(eap_conf));
+ eap_conf.ssl_ctx = hapd->ssl_ctx;
+ eap_conf.eap_sim_db_priv = hapd->eap_sim_db_priv;
+ sm->eap = eap_sm_init(sm, &eapol_cb, &eap_conf);
+ if (sm->eap == NULL) {
+ eapol_sm_free(sm);
+ return NULL;
+ }
+ }
+
+ eapol_sm_initialize(sm);
+
+ return sm;
+}
+
+
+void eapol_sm_free(struct eapol_state_machine *sm)
+{
+ if (sm == NULL)
+ return;
+
+ eloop_cancel_timeout(eapol_port_timers_tick, sm->hapd, sm);
+ if (sm->eap)
+ eap_sm_deinit(sm->eap);
+ free(sm);
+}
+
+
+static int eapol_sm_sta_entry_alive(struct hostapd_data *hapd, u8 *addr)
+{
+ struct sta_info *sta;
+ sta = ap_get_sta(hapd, addr);
+ if (sta == NULL || sta->eapol_sm == NULL)
+ return 0;
+ return 1;
+}
+
+
+void eapol_sm_step(struct eapol_state_machine *sm)
+{
+ struct hostapd_data *hapd = sm->hapd;
+ u8 addr[ETH_ALEN];
+ int prev_auth_pae, prev_be_auth, prev_reauth_timer, prev_auth_key_tx,
+ prev_key_rx, prev_ctrl_dir;
+
+ /* FIX: could re-run eapol_sm_step from registered timeout (after
+ * 0 sec) to make sure that other possible timeouts/events are
+ * processed */
+
+ memcpy(addr, sm->sta->addr, ETH_ALEN);
+restart:
+ do {
+ prev_auth_pae = sm->auth_pae.state;
+ prev_be_auth = sm->be_auth.state;
+ prev_reauth_timer = sm->reauth_timer.state;
+ prev_auth_key_tx = sm->auth_key_tx.state;
+ prev_key_rx = sm->key_rx.state;
+ prev_ctrl_dir = sm->ctrl_dir.state;
+
+ SM_STEP_RUN(AUTH_PAE);
+ if (!eapol_sm_sta_entry_alive(hapd, addr))
+ break;
+ SM_STEP_RUN(BE_AUTH);
+ if (!eapol_sm_sta_entry_alive(hapd, addr))
+ break;
+ SM_STEP_RUN(REAUTH_TIMER);
+ if (!eapol_sm_sta_entry_alive(hapd, addr))
+ break;
+ SM_STEP_RUN(AUTH_KEY_TX);
+ if (!eapol_sm_sta_entry_alive(hapd, addr))
+ break;
+ SM_STEP_RUN(KEY_RX);
+ if (!eapol_sm_sta_entry_alive(hapd, addr))
+ break;
+ SM_STEP_RUN(CTRL_DIR);
+ if (!eapol_sm_sta_entry_alive(hapd, addr))
+ break;
+ } while (prev_auth_pae != sm->auth_pae.state ||
+ prev_be_auth != sm->be_auth.state ||
+ prev_reauth_timer != sm->reauth_timer.state ||
+ prev_auth_key_tx != sm->auth_key_tx.state ||
+ prev_key_rx != sm->key_rx.state ||
+ prev_ctrl_dir != sm->ctrl_dir.state);
+
+ if (eapol_sm_sta_entry_alive(hapd, addr) && sm->eap) {
+ if (eap_sm_step(sm->eap))
+ goto restart;
+ }
+
+ if (eapol_sm_sta_entry_alive(hapd, addr))
+ wpa_sm_notify(sm->hapd, sm->sta);
+}
+
+
+void eapol_sm_initialize(struct eapol_state_machine *sm)
+{
+ /* Initialize the state machines by asserting initialize and then
+ * deasserting it after one step */
+ sm->initialize = TRUE;
+ eapol_sm_step(sm);
+ sm->initialize = FALSE;
+ eapol_sm_step(sm);
+
+ /* Start one second tick for port timers state machine */
+ eloop_cancel_timeout(eapol_port_timers_tick, sm->hapd, sm);
+ eloop_register_timeout(1, 0, eapol_port_timers_tick, sm->hapd, sm);
+}
+
+
+#ifdef HOSTAPD_DUMP_STATE
+static inline const char * port_type_txt(PortTypes pt)
+{
+ switch (pt) {
+ case ForceUnauthorized: return "ForceUnauthorized";
+ case ForceAuthorized: return "ForceAuthorized";
+ case Auto: return "Auto";
+ default: return "Unknown";
+ }
+}
+
+
+static inline const char * port_state_txt(PortState ps)
+{
+ switch (ps) {
+ case Unauthorized: return "Unauthorized";
+ case Authorized: return "Authorized";
+ default: return "Unknown";
+ }
+}
+
+
+static inline const char * ctrl_dir_txt(ControlledDirection dir)
+{
+ switch (dir) {
+ case Both: return "Both";
+ case In: return "In";
+ default: return "Unknown";
+ }
+}
+
+
+static inline const char * auth_pae_state_txt(int s)
+{
+ switch (s) {
+ case AUTH_PAE_INITIALIZE: return "INITIALIZE";
+ case AUTH_PAE_DISCONNECTED: return "DISCONNECTED";
+ case AUTH_PAE_CONNECTING: return "CONNECTING";
+ case AUTH_PAE_AUTHENTICATING: return "AUTHENTICATING";
+ case AUTH_PAE_AUTHENTICATED: return "AUTHENTICATED";
+ case AUTH_PAE_ABORTING: return "ABORTING";
+ case AUTH_PAE_HELD: return "HELD";
+ case AUTH_PAE_FORCE_AUTH: return "FORCE_AUTH";
+ case AUTH_PAE_FORCE_UNAUTH: return "FORCE_UNAUTH";
+ case AUTH_PAE_RESTART: return "RESTART";
+ default: return "Unknown";
+ }
+}
+
+
+static inline const char * be_auth_state_txt(int s)
+{
+ switch (s) {
+ case BE_AUTH_REQUEST: return "REQUEST";
+ case BE_AUTH_RESPONSE: return "RESPONSE";
+ case BE_AUTH_SUCCESS: return "SUCCESS";
+ case BE_AUTH_FAIL: return "FAIL";
+ case BE_AUTH_TIMEOUT: return "TIMEOUT";
+ case BE_AUTH_IDLE: return "IDLE";
+ case BE_AUTH_INITIALIZE: return "INITIALIZE";
+ case BE_AUTH_IGNORE: return "IGNORE";
+ default: return "Unknown";
+ }
+}
+
+
+static inline const char * reauth_timer_state_txt(int s)
+{
+ switch (s) {
+ case REAUTH_TIMER_INITIALIZE: return "INITIALIZE";
+ case REAUTH_TIMER_REAUTHENTICATE: return "REAUTHENTICATE";
+ default: return "Unknown";
+ }
+}
+
+
+static inline const char * auth_key_tx_state_txt(int s)
+{
+ switch (s) {
+ case AUTH_KEY_TX_NO_KEY_TRANSMIT: return "NO_KEY_TRANSMIT";
+ case AUTH_KEY_TX_KEY_TRANSMIT: return "KEY_TRANSMIT";
+ default: return "Unknown";
+ }
+}
+
+
+static inline const char * key_rx_state_txt(int s)
+{
+ switch (s) {
+ case KEY_RX_NO_KEY_RECEIVE: return "NO_KEY_RECEIVE";
+ case KEY_RX_KEY_RECEIVE: return "KEY_RECEIVE";
+ default: return "Unknown";
+ }
+}
+
+
+static inline const char * ctrl_dir_state_txt(int s)
+{
+ switch (s) {
+ case CTRL_DIR_FORCE_BOTH: return "FORCE_BOTH";
+ case CTRL_DIR_IN_OR_BOTH: return "IN_OR_BOTH";
+ default: return "Unknown";
+ }
+}
+
+
+void eapol_sm_dump_state(FILE *f, const char *prefix,
+ struct eapol_state_machine *sm)
+{
+ fprintf(f, "%sEAPOL state machine:\n", prefix);
+ fprintf(f, "%s aWhile=%d quietWhile=%d reAuthWhen=%d\n", prefix,
+ sm->aWhile, sm->quietWhile, sm->reAuthWhen);
+#define _SB(b) ((b) ? "TRUE" : "FALSE")
+ fprintf(f,
+ "%s authAbort=%s authFail=%s authPortStatus=%s authStart=%s\n"
+ "%s authTimeout=%s authSuccess=%s eapFail=%s eapolEap=%s\n"
+ "%s eapSuccess=%s eapTimeout=%s initialize=%s "
+ "keyAvailable=%s\n"
+ "%s keyDone=%s keyRun=%s keyTxEnabled=%s portControl=%s\n"
+ "%s portEnabled=%s portValid=%s reAuthenticate=%s\n",
+ prefix, _SB(sm->authAbort), _SB(sm->authFail),
+ port_state_txt(sm->authPortStatus), _SB(sm->authStart),
+ prefix, _SB(sm->authTimeout), _SB(sm->authSuccess),
+ _SB(sm->eapFail), _SB(sm->eapolEap),
+ prefix, _SB(sm->eapSuccess), _SB(sm->eapTimeout),
+ _SB(sm->initialize), _SB(sm->keyAvailable),
+ prefix, _SB(sm->keyDone), _SB(sm->keyRun),
+ _SB(sm->keyTxEnabled), port_type_txt(sm->portControl),
+ prefix, _SB(sm->portEnabled), _SB(sm->portValid),
+ _SB(sm->reAuthenticate));
+
+ fprintf(f, "%s Authenticator PAE:\n"
+ "%s state=%s\n"
+ "%s eapolLogoff=%s eapolStart=%s eapRestart=%s\n"
+ "%s portMode=%s reAuthCount=%d\n"
+ "%s quietPeriod=%d reAuthMax=%d\n"
+ "%s authEntersConnecting=%d\n"
+ "%s authEapLogoffsWhileConnecting=%d\n"
+ "%s authEntersAuthenticating=%d\n"
+ "%s authAuthSuccessesWhileAuthenticating=%d\n"
+ "%s authAuthTimeoutsWhileAuthenticating=%d\n"
+ "%s authAuthFailWhileAuthenticating=%d\n"
+ "%s authAuthEapStartsWhileAuthenticating=%d\n"
+ "%s authAuthEapLogoffWhileAuthenticating=%d\n"
+ "%s authAuthReauthsWhileAuthenticated=%d\n"
+ "%s authAuthEapStartsWhileAuthenticated=%d\n"
+ "%s authAuthEapLogoffWhileAuthenticated=%d\n",
+ prefix, prefix, auth_pae_state_txt(sm->auth_pae.state), prefix,
+ _SB(sm->auth_pae.eapolLogoff), _SB(sm->auth_pae.eapolStart),
+ _SB(sm->auth_pae.eapRestart), prefix,
+ port_type_txt(sm->auth_pae.portMode), sm->auth_pae.reAuthCount,
+ prefix, sm->auth_pae.quietPeriod, sm->auth_pae.reAuthMax,
+ prefix, sm->auth_pae.authEntersConnecting,
+ prefix, sm->auth_pae.authEapLogoffsWhileConnecting,
+ prefix, sm->auth_pae.authEntersAuthenticating,
+ prefix, sm->auth_pae.authAuthSuccessesWhileAuthenticating,
+ prefix, sm->auth_pae.authAuthTimeoutsWhileAuthenticating,
+ prefix, sm->auth_pae.authAuthFailWhileAuthenticating,
+ prefix, sm->auth_pae.authAuthEapStartsWhileAuthenticating,
+ prefix, sm->auth_pae.authAuthEapLogoffWhileAuthenticating,
+ prefix, sm->auth_pae.authAuthReauthsWhileAuthenticated,
+ prefix, sm->auth_pae.authAuthEapStartsWhileAuthenticated,
+ prefix, sm->auth_pae.authAuthEapLogoffWhileAuthenticated);
+
+ fprintf(f, "%s Backend Authentication:\n"
+ "%s state=%s\n"
+ "%s eapNoReq=%s eapReq=%s eapResp=%s\n"
+ "%s serverTimeout=%d\n"
+ "%s backendResponses=%d\n"
+ "%s backendAccessChallenges=%d\n"
+ "%s backendOtherRequestsToSupplicant=%d\n"
+ "%s backendAuthSuccesses=%d\n"
+ "%s backendAuthFails=%d\n",
+ prefix, prefix,
+ be_auth_state_txt(sm->be_auth.state),
+ prefix, _SB(sm->be_auth.eapNoReq), _SB(sm->be_auth.eapReq),
+ _SB(sm->be_auth.eapResp),
+ prefix, sm->be_auth.serverTimeout,
+ prefix, sm->be_auth.backendResponses,
+ prefix, sm->be_auth.backendAccessChallenges,
+ prefix, sm->be_auth.backendOtherRequestsToSupplicant,
+ prefix, sm->be_auth.backendAuthSuccesses,
+ prefix, sm->be_auth.backendAuthFails);
+
+ fprintf(f, "%s Reauthentication Timer:\n"
+ "%s state=%s\n"
+ "%s reAuthPeriod=%d reAuthEnabled=%s\n", prefix, prefix,
+ reauth_timer_state_txt(sm->reauth_timer.state), prefix,
+ sm->reauth_timer.reAuthPeriod,
+ _SB(sm->reauth_timer.reAuthEnabled));
+
+ fprintf(f, "%s Authenticator Key Transmit:\n"
+ "%s state=%s\n", prefix, prefix,
+ auth_key_tx_state_txt(sm->auth_key_tx.state));
+
+ fprintf(f, "%s Key Receive:\n"
+ "%s state=%s\n"
+ "%s rxKey=%s\n", prefix, prefix,
+ key_rx_state_txt(sm->key_rx.state),
+ prefix, _SB(sm->key_rx.rxKey));
+
+ fprintf(f, "%s Controlled Directions:\n"
+ "%s state=%s\n"
+ "%s adminControlledDirections=%s "
+ "operControlledDirections=%s\n"
+ "%s operEdge=%s\n", prefix, prefix,
+ ctrl_dir_state_txt(sm->ctrl_dir.state),
+ prefix, ctrl_dir_txt(sm->ctrl_dir.adminControlledDirections),
+ ctrl_dir_txt(sm->ctrl_dir.operControlledDirections),
+ prefix, _SB(sm->ctrl_dir.operEdge));
+#undef _SB
+}
+#endif /* HOSTAPD_DUMP_STATE */
+
+
+static Boolean eapol_sm_get_bool(void *ctx, enum eapol_bool_var variable)
+{
+ struct eapol_state_machine *sm = ctx;
+ if (sm == NULL)
+ return FALSE;
+ switch (variable) {
+ case EAPOL_eapSuccess:
+ return sm->eapSuccess;
+ case EAPOL_eapRestart:
+ return sm->auth_pae.eapRestart;
+ case EAPOL_eapFail:
+ return sm->eapFail;
+ case EAPOL_eapResp:
+ return sm->be_auth.eapResp;
+ case EAPOL_eapReq:
+ return sm->be_auth.eapReq;
+ case EAPOL_eapNoReq:
+ return sm->be_auth.eapNoReq;
+ case EAPOL_portEnabled:
+ return sm->portEnabled;
+ case EAPOL_eapTimeout:
+ return sm->eapTimeout;
+ }
+ return FALSE;
+}
+
+
+static void eapol_sm_set_bool(void *ctx, enum eapol_bool_var variable,
+ Boolean value)
+{
+ struct eapol_state_machine *sm = ctx;
+ if (sm == NULL)
+ return;
+ switch (variable) {
+ case EAPOL_eapSuccess:
+ sm->eapSuccess = value;
+ break;
+ case EAPOL_eapRestart:
+ sm->auth_pae.eapRestart = value;
+ break;
+ case EAPOL_eapFail:
+ sm->eapFail = value;
+ break;
+ case EAPOL_eapResp:
+ sm->be_auth.eapResp = value;
+ break;
+ case EAPOL_eapReq:
+ sm->be_auth.eapReq = value;
+ break;
+ case EAPOL_eapNoReq:
+ sm->be_auth.eapNoReq = value;
+ break;
+ case EAPOL_portEnabled:
+ sm->portEnabled = value;
+ break;
+ case EAPOL_eapTimeout:
+ sm->eapTimeout = value;
+ break;
+ }
+}
+
+
+static void eapol_sm_set_eapReqData(void *ctx, const u8 *eapReqData,
+ size_t eapReqDataLen)
+{
+ struct eapol_state_machine *sm = ctx;
+ if (sm == NULL)
+ return;
+
+ free(sm->last_eap_radius);
+ sm->last_eap_radius = malloc(eapReqDataLen);
+ if (sm->last_eap_radius == NULL)
+ return;
+ memcpy(sm->last_eap_radius, eapReqData, eapReqDataLen);
+ sm->last_eap_radius_len = eapReqDataLen;
+}
+
+
+static void eapol_sm_set_eapKeyData(void *ctx, const u8 *eapKeyData,
+ size_t eapKeyDataLen)
+{
+ struct eapol_state_machine *sm = ctx;
+ struct hostapd_data *hapd;
+
+ if (sm == NULL)
+ return;
+
+ hapd = sm->hapd;
+
+ if (eapKeyData && eapKeyDataLen >= 64) {
+ free(sm->eapol_key_sign);
+ free(sm->eapol_key_crypt);
+ sm->eapol_key_crypt = malloc(32);
+ if (sm->eapol_key_crypt) {
+ memcpy(sm->eapol_key_crypt, eapKeyData, 32);
+ sm->eapol_key_crypt_len = 32;
+ }
+ sm->eapol_key_sign = malloc(32);
+ if (sm->eapol_key_sign) {
+ memcpy(sm->eapol_key_sign, eapKeyData + 32, 32);
+ sm->eapol_key_sign_len = 32;
+ }
+ if (hapd->default_wep_key ||
+ hapd->conf->individual_wep_key_len > 0 ||
+ hapd->conf->wpa)
+ sm->keyAvailable = TRUE;
+ } else {
+ free(sm->eapol_key_sign);
+ free(sm->eapol_key_crypt);
+ sm->eapol_key_sign = NULL;
+ sm->eapol_key_crypt = NULL;
+ sm->eapol_key_sign_len = 0;
+ sm->eapol_key_crypt_len = 0;
+ sm->keyAvailable = FALSE;
+ }
+}
+
+
+static int eapol_sm_get_eap_user(void *ctx, const u8 *identity,
+ size_t identity_len, int phase2,
+ struct eap_user *user)
+{
+ struct eapol_state_machine *sm = ctx;
+ const struct hostapd_eap_user *eap_user;
+
+ eap_user = hostapd_get_eap_user(sm->hapd->conf, identity,
+ identity_len, phase2);
+ if (eap_user == NULL)
+ return -1;
+
+ memset(user, 0, sizeof(*user));
+ user->phase2 = phase2;
+ memcpy(user->methods, eap_user->methods,
+ EAP_USER_MAX_METHODS > EAP_MAX_METHODS ?
+ EAP_USER_MAX_METHODS : EAP_MAX_METHODS);
+
+ if (eap_user->password) {
+ user->password = malloc(eap_user->password_len);
+ if (user->password == NULL)
+ return -1;
+ memcpy(user->password, eap_user->password,
+ eap_user->password_len);
+ user->password_len = eap_user->password_len;
+ }
+ user->force_version = eap_user->force_version;
+
+ return 0;
+}
+
+
+static struct eapol_callbacks eapol_cb =
+{
+ .get_bool = eapol_sm_get_bool,
+ .set_bool = eapol_sm_set_bool,
+ .set_eapReqData = eapol_sm_set_eapReqData,
+ .set_eapKeyData = eapol_sm_set_eapKeyData,
+ .get_eap_user = eapol_sm_get_eap_user,
+};
diff --git a/contrib/hostapd/eapol_sm.h b/contrib/hostapd/eapol_sm.h
new file mode 100644
index 000000000000..7b74ed1c607a
--- /dev/null
+++ b/contrib/hostapd/eapol_sm.h
@@ -0,0 +1,213 @@
+#ifndef EAPOL_SM_H
+#define EAPOL_SM_H
+
+#include "defs.h"
+
+/* IEEE Std 802.1X-REV-d11, Ch. 8.2 */
+
+typedef enum { ForceUnauthorized = 1, ForceAuthorized = 3, Auto = 2 }
+ PortTypes;
+typedef enum { Unauthorized = 2, Authorized = 1 } PortState;
+typedef enum { Both = 0, In = 1 } ControlledDirection;
+typedef unsigned int Counter;
+
+
+/* Authenticator PAE state machine */
+struct eapol_auth_pae_sm {
+ /* variables */
+ Boolean eapolLogoff;
+ Boolean eapolStart;
+ Boolean eapRestart;
+ PortTypes portMode;
+ unsigned int reAuthCount;
+
+ /* constants */
+ unsigned int quietPeriod; /* default 60; 0..65535 */
+#define AUTH_PAE_DEFAULT_quietPeriod 60
+ unsigned int reAuthMax; /* default 2 */
+#define AUTH_PAE_DEFAULT_reAuthMax 2
+
+ /* counters */
+ Counter authEntersConnecting;
+ Counter authEapLogoffsWhileConnecting;
+ Counter authEntersAuthenticating;
+ Counter authAuthSuccessesWhileAuthenticating;
+ Counter authAuthTimeoutsWhileAuthenticating;
+ Counter authAuthFailWhileAuthenticating;
+ Counter authAuthEapStartsWhileAuthenticating;
+ Counter authAuthEapLogoffWhileAuthenticating;
+ Counter authAuthReauthsWhileAuthenticated;
+ Counter authAuthEapStartsWhileAuthenticated;
+ Counter authAuthEapLogoffWhileAuthenticated;
+
+ enum { AUTH_PAE_INITIALIZE, AUTH_PAE_DISCONNECTED, AUTH_PAE_CONNECTING,
+ AUTH_PAE_AUTHENTICATING, AUTH_PAE_AUTHENTICATED,
+ AUTH_PAE_ABORTING, AUTH_PAE_HELD, AUTH_PAE_FORCE_AUTH,
+ AUTH_PAE_FORCE_UNAUTH, AUTH_PAE_RESTART } state;
+};
+
+
+/* Backend Authentication state machine */
+struct eapol_backend_auth_sm {
+ /* variables */
+ Boolean eapNoReq;
+ Boolean eapReq;
+ Boolean eapResp;
+
+ /* constants */
+ unsigned int serverTimeout; /* default 30; 1..X */
+#define BE_AUTH_DEFAULT_serverTimeout 30
+
+ /* counters */
+ Counter backendResponses;
+ Counter backendAccessChallenges;
+ Counter backendOtherRequestsToSupplicant;
+ Counter backendAuthSuccesses;
+ Counter backendAuthFails;
+
+ enum { BE_AUTH_REQUEST, BE_AUTH_RESPONSE, BE_AUTH_SUCCESS,
+ BE_AUTH_FAIL, BE_AUTH_TIMEOUT, BE_AUTH_IDLE, BE_AUTH_INITIALIZE,
+ BE_AUTH_IGNORE
+ } state;
+};
+
+
+/* Reauthentication Timer state machine */
+struct eapol_reauth_timer_sm {
+ /* constants */
+ unsigned int reAuthPeriod; /* default 3600 s */
+ Boolean reAuthEnabled;
+
+ enum { REAUTH_TIMER_INITIALIZE, REAUTH_TIMER_REAUTHENTICATE } state;
+};
+
+
+/* Authenticator Key Transmit state machine */
+struct eapol_auth_key_tx {
+ enum { AUTH_KEY_TX_NO_KEY_TRANSMIT, AUTH_KEY_TX_KEY_TRANSMIT } state;
+};
+
+
+/* Key Receive state machine */
+struct eapol_key_rx {
+ /* variables */
+ Boolean rxKey;
+
+ enum { KEY_RX_NO_KEY_RECEIVE, KEY_RX_KEY_RECEIVE } state;
+};
+
+
+/* Controlled Directions state machine */
+struct eapol_ctrl_dir {
+ /* variables */
+ ControlledDirection adminControlledDirections;
+ ControlledDirection operControlledDirections;
+ Boolean operEdge;
+
+ enum { CTRL_DIR_FORCE_BOTH, CTRL_DIR_IN_OR_BOTH } state;
+};
+
+
+struct eap_sm;
+
+struct eapol_state_machine {
+ /* timers */
+ int aWhile;
+ int quietWhile;
+ int reAuthWhen;
+
+ /* global variables */
+ Boolean authAbort;
+ Boolean authFail;
+ PortState authPortStatus;
+ Boolean authStart;
+ Boolean authTimeout;
+ Boolean authSuccess;
+ Boolean eapFail;
+ Boolean eapolEap;
+ Boolean eapSuccess;
+ Boolean eapTimeout;
+ Boolean initialize;
+ Boolean keyAvailable;
+ Boolean keyDone;
+ Boolean keyRun;
+ Boolean keyTxEnabled;
+ PortTypes portControl;
+ Boolean portEnabled;
+ Boolean portValid;
+ Boolean reAuthenticate;
+
+ /* Port Timers state machine */
+ /* 'Boolean tick' implicitly handled as registered timeout */
+
+ struct eapol_auth_pae_sm auth_pae;
+ struct eapol_backend_auth_sm be_auth;
+ struct eapol_reauth_timer_sm reauth_timer;
+ struct eapol_auth_key_tx auth_key_tx;
+ struct eapol_key_rx key_rx;
+ struct eapol_ctrl_dir ctrl_dir;
+
+ /* Authenticator Statistics Table */
+ Counter dot1xAuthEapolFramesRx;
+ Counter dot1xAuthEapolFramesTx;
+ Counter dot1xAuthEapolStartFramesRx;
+ Counter dot1xAuthEapolLogoffFramesRx;
+ Counter dot1xAuthEapolRespIdFramesRx;
+ Counter dot1xAuthEapolRespFramesRx;
+ Counter dot1xAuthEapolReqIdFramesTx;
+ Counter dot1xAuthEapolReqFramesTx;
+ Counter dot1xAuthInvalidEapolFramesRx;
+ Counter dot1xAuthEapLengthErrorFramesRx;
+ Counter dot1xAuthLastEapolFrameVersion;
+
+ /* Other variables - not defined in IEEE 802.1X */
+ u8 addr[ETH_ALEN]; /* Supplicant address */
+#define EAPOL_SM_PREAUTH BIT(0)
+ int flags; /* EAPOL_SM_* */
+
+ int radius_identifier;
+ /* TODO: check when the last messages can be released */
+ struct radius_msg *last_recv_radius;
+ u8 *last_eap_supp; /* last received EAP Response from Supplicant */
+ size_t last_eap_supp_len;
+ u8 *last_eap_radius; /* last received EAP Response from Authentication
+ * Server */
+ size_t last_eap_radius_len;
+ u8 *identity;
+ size_t identity_len;
+ u8 *radius_class;
+ size_t radius_class_len;
+
+ /* Keys for encrypting and signing EAPOL-Key frames */
+ u8 *eapol_key_sign;
+ size_t eapol_key_sign_len;
+ u8 *eapol_key_crypt;
+ size_t eapol_key_crypt_len;
+
+ Boolean rx_identity; /* set to TRUE on reception of
+ * EAP-Response/Identity */
+
+ struct eap_sm *eap;
+
+ /* currentId was removed in IEEE 802.1X-REV, but it is needed to filter
+ * out EAP-Responses to old packets (e.g., to two EAP-Request/Identity
+ * packets that are often sent in the beginning of the authentication).
+ */
+ u8 currentId;
+
+ /* Somewhat nasty pointers to global hostapd and STA data to avoid
+ * passing these to every function */
+ struct hostapd_data *hapd;
+ struct sta_info *sta;
+};
+
+
+struct eapol_state_machine *eapol_sm_alloc(hostapd *hapd,
+ struct sta_info *sta);
+void eapol_sm_free(struct eapol_state_machine *sm);
+void eapol_sm_step(struct eapol_state_machine *sm);
+void eapol_sm_initialize(struct eapol_state_machine *sm);
+void eapol_sm_dump_state(FILE *f, const char *prefix,
+ struct eapol_state_machine *sm);
+
+#endif /* EAPOL_SM_H */
diff --git a/contrib/hostapd/eloop.c b/contrib/hostapd/eloop.c
new file mode 100644
index 000000000000..60715089beb2
--- /dev/null
+++ b/contrib/hostapd/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/hostapd/eloop.h b/contrib/hostapd/eloop.h
new file mode 100644
index 000000000000..f5b884740421
--- /dev/null
+++ b/contrib/hostapd/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/hostapd/hostap_common.h b/contrib/hostapd/hostap_common.h
new file mode 100644
index 000000000000..003ad9ac6b58
--- /dev/null
+++ b/contrib/hostapd/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/hostapd/hostapd.accept b/contrib/hostapd/hostapd.accept
new file mode 100644
index 000000000000..57122b663449
--- /dev/null
+++ b/contrib/hostapd/hostapd.accept
@@ -0,0 +1,5 @@
+# List of MAC addresses that are allowed to authenticate (IEEE 802.11)
+# with the AP.
+00:11:22:33:44:55
+00:66:77:88:99:aa
+00:00:22:33:44:55
diff --git a/contrib/hostapd/hostapd.c b/contrib/hostapd/hostapd.c
new file mode 100644
index 000000000000..88196417a3be
--- /dev/null
+++ b/contrib/hostapd/hostapd.c
@@ -0,0 +1,802 @@
+/*
+ * Host AP (software wireless LAN access point) user space daemon for
+ * Host AP kernel driver
+ * 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 <unistd.h>
+#include <netinet/in.h>
+#include <string.h>
+#include <signal.h>
+#include <time.h>
+#include <syslog.h>
+#include <stdarg.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include "eloop.h"
+#include "hostapd.h"
+#include "ieee802_1x.h"
+#include "ieee802_11.h"
+#include "accounting.h"
+#include "eapol_sm.h"
+#include "iapp.h"
+#include "ap.h"
+#include "ieee802_11_auth.h"
+#include "sta_info.h"
+#include "driver.h"
+#include "radius_client.h"
+#include "radius_server.h"
+#include "wpa.h"
+#include "ctrl_iface.h"
+#include "tls.h"
+#include "eap_sim_db.h"
+#include "version.h"
+
+
+struct hapd_interfaces {
+ int count;
+ hostapd **hapd;
+};
+
+unsigned char rfc1042_header[6] = { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 };
+
+
+extern int wpa_debug_level;
+extern int wpa_debug_show_keys;
+extern int wpa_debug_timestamp;
+
+
+void hostapd_logger(hostapd *hapd, u8 *addr, unsigned int module, int level,
+ char *fmt, ...)
+{
+ char *format, *module_str;
+ int maxlen;
+ va_list ap;
+ int conf_syslog_level, conf_stdout_level;
+ unsigned int conf_syslog, conf_stdout;
+
+ maxlen = strlen(fmt) + 100;
+ format = malloc(maxlen);
+ if (!format)
+ return;
+
+ va_start(ap, fmt);
+
+ if (hapd && hapd->conf) {
+ conf_syslog_level = hapd->conf->logger_syslog_level;
+ conf_stdout_level = hapd->conf->logger_stdout_level;
+ conf_syslog = hapd->conf->logger_syslog;
+ conf_stdout = hapd->conf->logger_stdout;
+ } else {
+ conf_syslog_level = conf_stdout_level = 0;
+ conf_syslog = conf_stdout = (unsigned int) -1;
+ }
+
+ switch (module) {
+ case HOSTAPD_MODULE_IEEE80211:
+ module_str = "IEEE 802.11";
+ break;
+ case HOSTAPD_MODULE_IEEE8021X:
+ module_str = "IEEE 802.1X";
+ break;
+ case HOSTAPD_MODULE_RADIUS:
+ module_str = "RADIUS";
+ break;
+ case HOSTAPD_MODULE_WPA:
+ module_str = "WPA";
+ break;
+ case HOSTAPD_MODULE_DRIVER:
+ module_str = "DRIVER";
+ break;
+ case HOSTAPD_MODULE_IAPP:
+ module_str = "IAPP";
+ break;
+ default:
+ module_str = NULL;
+ break;
+ }
+
+ if (hapd && hapd->conf && addr)
+ snprintf(format, maxlen, "%s: STA " MACSTR "%s%s: %s",
+ hapd->conf->iface, MAC2STR(addr),
+ module_str ? " " : "", module_str, fmt);
+ else if (hapd && hapd->conf)
+ snprintf(format, maxlen, "%s:%s%s %s",
+ hapd->conf->iface, module_str ? " " : "",
+ module_str, fmt);
+ else if (addr)
+ snprintf(format, maxlen, "STA " MACSTR "%s%s: %s",
+ MAC2STR(addr), module_str ? " " : "",
+ module_str, fmt);
+ else
+ snprintf(format, maxlen, "%s%s%s",
+ module_str, module_str ? ": " : "", fmt);
+
+ if ((conf_stdout & module) && level >= conf_stdout_level) {
+ vprintf(format, ap);
+ printf("\n");
+ }
+
+ if ((conf_syslog & module) && level >= conf_syslog_level) {
+ int priority;
+ switch (level) {
+ case HOSTAPD_LEVEL_DEBUG_VERBOSE:
+ case HOSTAPD_LEVEL_DEBUG:
+ priority = LOG_DEBUG;
+ break;
+ case HOSTAPD_LEVEL_INFO:
+ priority = LOG_INFO;
+ break;
+ case HOSTAPD_LEVEL_NOTICE:
+ priority = LOG_NOTICE;
+ break;
+ case HOSTAPD_LEVEL_WARNING:
+ priority = LOG_WARNING;
+ break;
+ default:
+ priority = LOG_INFO;
+ break;
+ }
+ vsyslog(priority, format, ap);
+ }
+
+ free(format);
+
+ va_end(ap);
+}
+
+
+static void hostapd_deauth_all_stas(hostapd *hapd)
+{
+#if 0
+ u8 addr[ETH_ALEN];
+
+ memset(addr, 0xff, ETH_ALEN);
+ hostapd_sta_deauth(hapd, addr, WLAN_REASON_PREV_AUTH_NOT_VALID);
+#else
+ /* New Prism2.5/3 STA firmware versions seem to have issues with this
+ * broadcast deauth frame. This gets the firmware in odd state where
+ * nothing works correctly, so let's skip sending this for a while
+ * until the issue has been resolved. */
+#endif
+}
+
+
+/* This function will be called whenever a station associates with the AP */
+void hostapd_new_assoc_sta(hostapd *hapd, struct sta_info *sta)
+{
+ /* IEEE 802.11F (IAPP) */
+ if (hapd->conf->ieee802_11f)
+ iapp_new_station(hapd->iapp, sta);
+
+ /* Start accounting here, if IEEE 802.1X is not used. IEEE 802.1X code
+ * will start accounting after the station has been authorized. */
+ if (!hapd->conf->ieee802_1x)
+ accounting_sta_start(hapd, sta);
+
+ /* Start IEEE 802.1x authentication process for new stations */
+ ieee802_1x_new_station(hapd, sta);
+ wpa_new_station(hapd, sta);
+}
+
+
+static void handle_term(int sig, void *eloop_ctx, void *signal_ctx)
+{
+ printf("Signal %d received - terminating\n", sig);
+ eloop_terminate();
+}
+
+
+static void handle_reload(int sig, void *eloop_ctx, void *signal_ctx)
+{
+ struct hapd_interfaces *hapds = (struct hapd_interfaces *) eloop_ctx;
+ struct hostapd_config *newconf;
+ int i;
+
+ printf("Signal %d received - reloading configuration\n", sig);
+
+ for (i = 0; i < hapds->count; i++) {
+ hostapd *hapd = hapds->hapd[i];
+ newconf = hostapd_config_read(hapd->config_fname);
+ if (newconf == NULL) {
+ printf("Failed to read new configuration file - "
+ "continuing with old.\n");
+ continue;
+ }
+ /* TODO: update dynamic data based on changed configuration
+ * items (e.g., open/close sockets, remove stations added to
+ * deny list, etc.) */
+ radius_client_flush(hapd->radius);
+ hostapd_config_free(hapd->conf);
+ hapd->conf = newconf;
+ }
+}
+
+
+#ifdef HOSTAPD_DUMP_STATE
+static void hostapd_dump_state(hostapd *hapd)
+{
+ FILE *f;
+ time_t now;
+ struct sta_info *sta;
+ int i;
+ char *buf;
+
+ if (!hapd->conf->dump_log_name) {
+ printf("Dump file not defined - ignoring dump request\n");
+ return;
+ }
+
+ printf("Dumping hostapd state to '%s'\n", hapd->conf->dump_log_name);
+ f = fopen(hapd->conf->dump_log_name, "w");
+ if (f == NULL) {
+ printf("Could not open dump file '%s' for writing.\n",
+ hapd->conf->dump_log_name);
+ return;
+ }
+
+ time(&now);
+ fprintf(f, "hostapd state dump - %s", ctime(&now));
+
+ for (sta = hapd->sta_list; sta != NULL; sta = sta->next) {
+ fprintf(f, "\nSTA=" MACSTR "\n", MAC2STR(sta->addr));
+
+ fprintf(f,
+ " AID=%d flags=0x%x %s%s%s%s%s%s\n"
+ " capability=0x%x listen_interval=%d\n",
+ sta->aid,
+ sta->flags,
+ (sta->flags & WLAN_STA_AUTH ? "[AUTH]" : ""),
+ (sta->flags & WLAN_STA_ASSOC ? "[ASSOC]" : ""),
+ (sta->flags & WLAN_STA_PS ? "[PS]" : ""),
+ (sta->flags & WLAN_STA_TIM ? "[TIM]" : ""),
+ (sta->flags & WLAN_STA_PERM ? "[PERM]" : ""),
+ (sta->flags & WLAN_STA_AUTHORIZED ? "[AUTHORIZED]" :
+ ""),
+ sta->capability,
+ sta->listen_interval);
+
+ fprintf(f, " supported_rates=");
+ for (i = 0; i < sizeof(sta->supported_rates); i++)
+ if (sta->supported_rates[i] != 0)
+ fprintf(f, "%02x ", sta->supported_rates[i]);
+ fprintf(f, "%s%s%s%s\n",
+ (sta->tx_supp_rates & WLAN_RATE_1M ? "[1M]" : ""),
+ (sta->tx_supp_rates & WLAN_RATE_2M ? "[2M]" : ""),
+ (sta->tx_supp_rates & WLAN_RATE_5M5 ? "[5.5M]" : ""),
+ (sta->tx_supp_rates & WLAN_RATE_11M ? "[11M]" : ""));
+
+ fprintf(f,
+ " timeout_next=%s\n",
+ (sta->timeout_next == STA_NULLFUNC ? "NULLFUNC POLL" :
+ (sta->timeout_next == STA_DISASSOC ? "DISASSOC" :
+ "DEAUTH")));
+
+ ieee802_1x_dump_state(f, " ", sta);
+ }
+
+ buf = malloc(4096);
+ if (buf) {
+ int count = radius_client_get_mib(hapd->radius, buf, 4096);
+ if (count < 0)
+ count = 0;
+ else if (count > 4095)
+ count = 4095;
+ buf[count] = '\0';
+ fprintf(f, "%s", buf);
+
+ count = radius_server_get_mib(hapd->radius_srv, buf, 4096);
+ if (count < 0)
+ count = 0;
+ else if (count > 4095)
+ count = 4095;
+ buf[count] = '\0';
+ fprintf(f, "%s", buf);
+ free(buf);
+ }
+ fclose(f);
+}
+#endif /* HOSTAPD_DUMP_STATE */
+
+
+static void handle_dump_state(int sig, void *eloop_ctx, void *signal_ctx)
+{
+#ifdef HOSTAPD_DUMP_STATE
+ struct hapd_interfaces *hapds = (struct hapd_interfaces *) eloop_ctx;
+ int i;
+
+ for (i = 0; i < hapds->count; i++) {
+ hostapd *hapd = hapds->hapd[i];
+ hostapd_dump_state(hapd);
+ }
+#endif /* HOSTAPD_DUMP_STATE */
+}
+
+
+static void hostapd_cleanup(struct hostapd_data *hapd)
+{
+ hostapd_ctrl_iface_deinit(hapd);
+
+ free(hapd->default_wep_key);
+ hapd->default_wep_key = NULL;
+ iapp_deinit(hapd->iapp);
+ accounting_deinit(hapd);
+ wpa_deinit(hapd);
+ ieee802_1x_deinit(hapd);
+ hostapd_acl_deinit(hapd);
+ radius_client_deinit(hapd->radius);
+ hapd->radius = NULL;
+ radius_server_deinit(hapd->radius_srv);
+ hapd->radius_srv = NULL;
+
+ hostapd_wireless_event_deinit(hapd);
+
+ if (hapd->driver)
+ hostapd_driver_deinit(hapd);
+
+ hostapd_config_free(hapd->conf);
+ hapd->conf = NULL;
+
+ free(hapd->config_fname);
+
+#ifdef EAP_TLS_FUNCS
+ if (hapd->ssl_ctx) {
+ tls_deinit(hapd->ssl_ctx);
+ hapd->ssl_ctx = NULL;
+ }
+#endif /* EAP_TLS_FUNCS */
+
+ if (hapd->eap_sim_db_priv)
+ eap_sim_db_deinit(hapd->eap_sim_db_priv);
+}
+
+
+static int hostapd_flush_old_stations(hostapd *hapd)
+{
+ int ret = 0;
+
+ printf("Flushing old station entries\n");
+ if (hostapd_flush(hapd)) {
+ printf("Could not connect to kernel driver.\n");
+ ret = -1;
+ }
+ printf("Deauthenticate all stations\n");
+ hostapd_deauth_all_stas(hapd);
+
+ return ret;
+}
+
+
+static int hostapd_setup_interface(struct hostapd_data *hapd)
+{
+ struct hostapd_config *conf = hapd->conf;
+ u8 ssid[HOSTAPD_SSID_LEN + 1];
+ int ssid_len, set_ssid;
+ int ret = 0;
+
+ if (hostapd_driver_init(hapd)) {
+ printf("%s driver initialization failed.\n",
+ hapd->driver ? hapd->driver->name : "Unknown");
+ hapd->driver = NULL;
+ return -1;
+ }
+
+ /*
+ * Fetch the SSID from the system and use it or,
+ * if one was specified in the config file, verify they
+ * match.
+ */
+ ssid_len = hostapd_get_ssid(hapd, ssid, sizeof(ssid));
+ if (ssid_len < 0) {
+ printf("Could not read SSID from system\n");
+ return -1;
+ }
+ if (conf->ssid_set) {
+ /*
+ * If SSID is specified in the config file and it differs
+ * from what is being used then force installation of the
+ * new SSID.
+ */
+ set_ssid = (conf->ssid_len != ssid_len ||
+ memcmp(conf->ssid, ssid, ssid_len) != 0);
+ } else {
+ /*
+ * No SSID in the config file; just use the one we got
+ * from the system.
+ */
+ set_ssid = 0;
+ conf->ssid_len = ssid_len;
+ memcpy(conf->ssid, ssid, conf->ssid_len);
+ conf->ssid[conf->ssid_len] = '\0';
+ }
+
+ printf("Using interface %s with hwaddr " MACSTR " and ssid '%s'\n",
+ hapd->conf->iface, MAC2STR(hapd->own_addr), hapd->conf->ssid);
+
+ if (hostapd_setup_wpa_psk(conf)) {
+ printf("WPA-PSK setup failed.\n");
+ return -1;
+ }
+
+ /* Set SSID for the kernel driver (to be used in beacon and probe
+ * response frames) */
+ if (set_ssid && hostapd_set_ssid(hapd, (u8 *) conf->ssid,
+ conf->ssid_len)) {
+ printf("Could not set SSID for kernel driver\n");
+ return -1;
+ }
+
+ hapd->radius = radius_client_init(hapd);
+ if (hapd->radius == NULL) {
+ printf("RADIUS client initialization failed.\n");
+ return -1;
+ }
+ if (conf->radius_server_clients) {
+ struct radius_server_conf srv;
+ memset(&srv, 0, sizeof(srv));
+ srv.client_file = conf->radius_server_clients;
+ srv.auth_port = conf->radius_server_auth_port;
+ srv.hostapd_conf = conf;
+ srv.eap_sim_db_priv = hapd->eap_sim_db_priv;
+ srv.ssl_ctx = hapd->ssl_ctx;
+ hapd->radius_srv = radius_server_init(&srv);
+ if (hapd->radius_srv == NULL) {
+ printf("RADIUS server initialization failed.\n");
+ return -1;
+ }
+ }
+ if (hostapd_acl_init(hapd)) {
+ printf("ACL initialization failed.\n");
+ return -1;
+ }
+ if (ieee802_1x_init(hapd)) {
+ printf("IEEE 802.1X initialization failed.\n");
+ return -1;
+ }
+
+ if (hapd->conf->wpa && wpa_init(hapd)) {
+ printf("WPA initialization failed.\n");
+ return -1;
+ }
+
+ if (accounting_init(hapd)) {
+ printf("Accounting initialization failed.\n");
+ return -1;
+ }
+
+ if (hapd->conf->ieee802_11f &&
+ (hapd->iapp = iapp_init(hapd, hapd->conf->iapp_iface)) == NULL) {
+ printf("IEEE 802.11F (IAPP) initialization failed.\n");
+ return -1;
+ }
+
+ if (hostapd_wireless_event_init(hapd) < 0)
+ return -1;
+
+ if (hostapd_flush_old_stations(hapd))
+ return -1;
+
+ if (hostapd_ctrl_iface_init(hapd)) {
+ printf("Failed to setup control interface\n");
+ ret = -1;
+ }
+
+ return ret;
+}
+
+
+struct driver {
+ struct driver *next;
+ char *name;
+ const struct driver_ops *ops;
+};
+static struct driver *drivers = NULL;
+
+void driver_register(const char *name, const struct driver_ops *ops)
+{
+ struct driver *d;
+
+ d = malloc(sizeof(struct driver));
+ if (d == NULL) {
+ printf("Failed to register driver %s!\n", name);
+ return;
+ }
+ d->name = strdup(name);
+ if (d->name == NULL) {
+ printf("Failed to register driver %s!\n", name);
+ free(d);
+ return;
+ }
+ d->ops = ops;
+
+ d->next = drivers;
+ drivers = d;
+}
+
+
+void driver_unregister(const char *name)
+{
+ struct driver *p, **pp;
+
+ for (pp = &drivers; (p = *pp) != NULL; pp = &p->next) {
+ if (strcasecmp(p->name, name) == 0) {
+ *pp = p->next;
+ p->next = NULL;
+ free(p->name);
+ free(p);
+ break;
+ }
+ }
+}
+
+
+static void driver_unregister_all(void)
+{
+ struct driver *p, *pp;
+ p = drivers;
+ drivers = NULL;
+ while (p) {
+ pp = p;
+ p = p->next;
+ free(pp->name);
+ free(pp);
+ }
+}
+
+
+const struct driver_ops * driver_lookup(const char *name)
+{
+ struct driver *p;
+
+ if (strcmp(name, "default") == 0) {
+ p = drivers;
+ while (p && p->next)
+ p = p->next;
+ return p->ops;
+ }
+
+ for (p = drivers; p != NULL; p = p->next) {
+ if (strcasecmp(p->name, name) == 0)
+ return p->ops;
+ }
+
+ return NULL;
+}
+
+
+static void show_version(void)
+{
+ fprintf(stderr,
+ "hostapd v" VERSION_STR "\n"
+ "Host AP user space daemon for management functionality of "
+ "Host AP kernel driver\n"
+ "Copyright (c) 2002-2005, Jouni Malinen <jkmaline@cc.hut.fi> "
+ "and contributors\n");
+}
+
+
+static void usage(void)
+{
+ show_version();
+ fprintf(stderr,
+ "\n"
+ "usage: hostapd [-hdB] <configuration file(s)>\n"
+ "\n"
+ "options:\n"
+ " -h show this usage\n"
+ " -d show more debug messages (-dd for even more)\n"
+ " -B run daemon in the background\n"
+ " -K include key data in debug messages\n"
+ " -t include timestamps in some debug messages\n"
+ " -v show hostapd version\n");
+
+ exit(1);
+}
+
+
+static hostapd * hostapd_init(const char *config_file)
+{
+ hostapd *hapd;
+
+ hapd = malloc(sizeof(*hapd));
+ if (hapd == NULL) {
+ printf("Could not allocate memory for hostapd data\n");
+ goto fail;
+ }
+ memset(hapd, 0, sizeof(*hapd));
+
+ hapd->config_fname = strdup(config_file);
+ if (hapd->config_fname == NULL) {
+ printf("Could not allocate memory for config_fname\n");
+ goto fail;
+ }
+
+ hapd->conf = hostapd_config_read(hapd->config_fname);
+ if (hapd->conf == NULL) {
+ goto fail;
+ }
+
+ if (hapd->conf->individual_wep_key_len > 0) {
+ /* use key0 in individual key and key1 in broadcast key */
+ hapd->default_wep_key_idx = 1;
+ }
+
+#ifdef EAP_TLS_FUNCS
+ if (hapd->conf->eap_authenticator &&
+ (hapd->conf->ca_cert || hapd->conf->server_cert)) {
+ hapd->ssl_ctx = tls_init();
+ if (hapd->ssl_ctx == NULL) {
+ printf("Failed to initialize TLS\n");
+ goto fail;
+ }
+ if (tls_global_ca_cert(hapd->ssl_ctx, hapd->conf->ca_cert)) {
+ printf("Failed to load CA certificate (%s)\n",
+ hapd->conf->ca_cert);
+ goto fail;
+ }
+ if (tls_global_client_cert(hapd->ssl_ctx,
+ hapd->conf->server_cert)) {
+ printf("Failed to load server certificate (%s)\n",
+ hapd->conf->server_cert);
+ goto fail;
+ }
+ if (tls_global_private_key(hapd->ssl_ctx,
+ hapd->conf->private_key,
+ hapd->conf->private_key_passwd)) {
+ printf("Failed to load private key (%s)\n",
+ hapd->conf->private_key);
+ }
+ }
+#endif /* EAP_TLS_FUNCS */
+
+ if (hapd->conf->eap_sim_db) {
+ hapd->eap_sim_db_priv =
+ eap_sim_db_init(hapd->conf->eap_sim_db);
+ if (hapd->eap_sim_db_priv == NULL) {
+ printf("Failed to initialize EAP-SIM database "
+ "interface\n");
+ goto fail;
+ }
+ }
+
+ if (hapd->conf->assoc_ap)
+ hapd->assoc_ap_state = WAIT_BEACON;
+
+ /* FIX: need to fix this const vs. not */
+ hapd->driver = (struct driver_ops *) hapd->conf->driver;
+
+ return hapd;
+
+fail:
+ if (hapd) {
+ if (hapd->ssl_ctx)
+ tls_deinit(hapd->ssl_ctx);
+ if (hapd->conf)
+ hostapd_config_free(hapd->conf);
+ free(hapd->config_fname);
+ free(hapd);
+ }
+ return NULL;
+}
+
+
+void register_drivers(void);
+
+int main(int argc, char *argv[])
+{
+ struct hapd_interfaces interfaces;
+ int ret = 1, i, j;
+ int c, debug = 0, daemonize = 0;
+
+ for (;;) {
+ c = getopt(argc, argv, "BdhKtv");
+ if (c < 0)
+ break;
+ switch (c) {
+ case 'h':
+ usage();
+ break;
+ case 'd':
+ debug++;
+ break;
+ case 'B':
+ daemonize++;
+ break;
+ case 'K':
+ wpa_debug_show_keys++;
+ break;
+ case 't':
+ wpa_debug_timestamp++;
+ break;
+ case 'v':
+ show_version();
+ exit(1);
+ break;
+
+ default:
+ usage();
+ break;
+ }
+ }
+
+ if (optind == argc)
+ usage();
+
+ register_drivers(); /* NB: generated by Makefile */
+
+ interfaces.count = argc - optind;
+
+ interfaces.hapd = malloc(interfaces.count * sizeof(hostapd *));
+ if (interfaces.hapd == NULL) {
+ printf("malloc failed\n");
+ exit(1);
+ }
+
+ eloop_init(&interfaces);
+ eloop_register_signal(SIGHUP, handle_reload, NULL);
+ eloop_register_signal(SIGINT, handle_term, NULL);
+ eloop_register_signal(SIGTERM, handle_term, NULL);
+ eloop_register_signal(SIGUSR1, handle_dump_state, NULL);
+
+ for (i = 0; i < interfaces.count; i++) {
+ printf("Configuration file: %s\n", argv[optind + i]);
+ interfaces.hapd[i] = hostapd_init(argv[optind + i]);
+ if (!interfaces.hapd[i])
+ goto out;
+ for (j = 0; j < debug; j++) {
+ if (interfaces.hapd[i]->conf->logger_stdout_level > 0)
+ interfaces.hapd[i]->conf->
+ logger_stdout_level--;
+ interfaces.hapd[i]->conf->debug++;
+ }
+ if (hostapd_setup_interface(interfaces.hapd[i]))
+ goto out;
+ wpa_debug_level -= interfaces.hapd[0]->conf->debug;
+ }
+
+ if (daemonize && daemon(0, 0)) {
+ perror("daemon");
+ goto out;
+ }
+
+ openlog("hostapd", 0, LOG_DAEMON);
+
+ eloop_run();
+
+ for (i = 0; i < interfaces.count; i++) {
+ hostapd_free_stas(interfaces.hapd[i]);
+ hostapd_flush_old_stations(interfaces.hapd[i]);
+ }
+
+ ret = 0;
+
+ out:
+ for (i = 0; i < interfaces.count; i++) {
+ if (!interfaces.hapd[i])
+ continue;
+
+ hostapd_cleanup(interfaces.hapd[i]);
+ free(interfaces.hapd[i]);
+ }
+ free(interfaces.hapd);
+
+ eloop_destroy();
+
+ closelog();
+
+ driver_unregister_all();
+
+ return ret;
+}
diff --git a/contrib/hostapd/hostapd.conf b/contrib/hostapd/hostapd.conf
new file mode 100644
index 000000000000..bb792dc26009
--- /dev/null
+++ b/contrib/hostapd/hostapd.conf
@@ -0,0 +1,293 @@
+##### hostapd configuration file ##############################################
+# Empty lines and lines starting with # are ignored
+
+# AP netdevice name (without 'ap' prefix, i.e., wlan0 uses wlan0ap for
+# management frames)
+interface=wlan0
+
+# Driver interface type (hostap/wired/madwifi/prism54; default: hostap)
+# driver=hostap
+
+# hostapd event logger configuration
+#
+# Two output method: syslog and stdout (only usable if not forking to
+# background).
+#
+# Module bitfield (ORed bitfield of modules that will be logged; -1 = all
+# modules):
+# bit 0 (1) = IEEE 802.11
+# bit 1 (2) = IEEE 802.1X
+# bit 2 (4) = RADIUS
+# bit 3 (8) = WPA
+# bit 4 (16) = driver interface
+# bit 5 (32) = IAPP
+#
+# Levels (minimum value for logged events):
+# 0 = verbose debugging
+# 1 = debugging
+# 2 = informational messages
+# 3 = notification
+# 4 = warning
+#
+logger_syslog=-1
+logger_syslog_level=2
+logger_stdout=-1
+logger_stdout_level=2
+
+# Debugging: 0 = no, 1 = minimal, 2 = verbose, 3 = msg dumps, 4 = excessive
+debug=0
+
+# Dump file for state information (on SIGUSR1)
+dump_file=/tmp/hostapd.dump
+
+# 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 hostapd processes/interfaces can be run at the same time if more
+# than one interface is used.
+# /var/run/hostapd is the recommended directory for sockets and by default,
+# hostapd_cli will use it when trying to connect with hostapd.
+ctrl_interface=/var/run/hostapd
+
+# 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 contron 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.
+#
+# This variable can be a group name or gid.
+#ctrl_interface_group=wheel
+ctrl_interface_group=0
+
+
+##### IEEE 802.11 related configuration #######################################
+
+# SSID to be used in IEEE 802.11 management frames
+ssid=test
+
+# Station MAC address -based authentication
+# 0 = accept unless in deny list
+# 1 = deny unless in accept list
+# 2 = use external RADIUS server (accept/deny lists are searched first)
+macaddr_acl=0
+
+# Accept/deny lists are read from separate files (containing list of
+# MAC addresses, one per line). Use absolute path name to make sure that the
+# files can be read on SIGHUP configuration reloads.
+#accept_mac_file=/etc/hostapd.accept
+#deny_mac_file=/etc/hostapd.deny
+
+# IEEE 802.11 specifies two authentication algorithms. hostapd can be
+# configured to allow both of these or only one. Open system authentication
+# should be used with IEEE 802.1X.
+# Bit fields of allowed authentication algorithms:
+# bit 0 = Open System Authentication
+# bit 1 = Shared Key Authentication (requires WEP)
+auth_algs=3
+
+# Associate as a station to another AP while still acting as an AP on the same
+# channel.
+#assoc_ap_addr=00:12:34:56:78:9a
+
+
+##### IEEE 802.1X (and IEEE 802.1aa/D4) related configuration #################
+
+# Require IEEE 802.1X authorization
+#ieee8021x=1
+
+# Use integrated EAP authenticator instead of external RADIUS authentication
+# server
+eap_authenticator=0
+
+# Path for EAP authenticator user database
+#eap_user_file=/etc/hostapd.eap_user
+
+# CA certificate (PEM or DER file) for EAP-TLS/PEAP/TTLS
+#ca_cert=/etc/hostapd.ca.pem
+
+# Server certificate (PEM or DER file) for EAP-TLS/PEAP/TTLS
+#server_cert=/etc/hostapd.server.pem
+
+# Private key matching with the server certificate for EAP-TLS/PEAP/TTLS
+# This may point to the same file as server_cert if both certificate and key
+# are included in a single file. PKCS#12 (PFX) file (.p12/.pfx) can also be
+# used by commenting out server_cert and specifying the PFX file as the
+# private_key.
+#private_key=/etc/hostapd.server.prv
+
+# Passphrase for private key
+#private_key_passwd=secret passphrase
+
+# Configuration data for EAP-SIM database/authentication gateway interface.
+# This is a text string in implementation specific format. The example
+# implementation in eap_sim_db.c uses this as the file name for the GSM
+# authentication triplets.
+#eap_sim_db=/etc/hostapd.sim_db
+
+# Optional displayable message sent with EAP Request-Identity
+eap_message=hello
+
+# WEP rekeying (disabled if key lengths are not set or are set to 0)
+# Key lengths for default/broadcast and individual/unicast keys:
+# 5 = 40-bit WEP (also known as 64-bit WEP with 40 secret bits)
+# 13 = 104-bit WEP (also known as 128-bit WEP with 104 secret bits)
+#wep_key_len_broadcast=5
+#wep_key_len_unicast=5
+# Rekeying period in seconds. 0 = do not rekey (i.e., set keys only once)
+#wep_rekey_period=300
+
+# EAPOL-Key index workaround (set bit7) for WinXP Supplicant (needed only if
+# only broadcast keys are used)
+eapol_key_index_workaround=0
+
+# EAP reauthentication period in seconds (default: 3600 seconds; 0 = disable
+# reauthentication).
+#eap_reauth_period=3600
+
+##### IEEE 802.11f - Inter-Access Point Protocol (IAPP) #######################
+
+# Interface to be used for IAPP broadcast packets
+#iapp_interface=eth0
+
+
+##### RADIUS configuration ####################################################
+# for IEEE 802.1X with external Authentication Server, IEEE 802.11
+# authentication with external ACL for MAC addresses, and accounting
+
+# The own IP address of the access point (used as NAS-IP-Address)
+own_ip_addr=127.0.0.1
+
+# Optional NAS-Identifier string for RADIUS messages. When used, this should be
+# a unique to the NAS within the scope of the RADIUS server. For example, a
+# fully qualified domain name can be used here.
+#nas_identifier=ap.example.com
+
+# RADIUS authentication server
+#auth_server_addr=127.0.0.1
+#auth_server_port=1812
+#auth_server_shared_secret=secret
+
+# RADIUS accounting server
+#acct_server_addr=127.0.0.1
+#acct_server_port=1813
+#acct_server_shared_secret=secret
+
+# Secondary RADIUS servers; to be used if primary one does not reply to
+# RADIUS packets. These are optional and there can be more than one secondary
+# server listed.
+#auth_server_addr=127.0.0.2
+#auth_server_port=1812
+#auth_server_shared_secret=secret2
+#
+#acct_server_addr=127.0.0.2
+#acct_server_port=1813
+#acct_server_shared_secret=secret2
+
+# Retry interval for trying to return to the primary RADIUS server (in
+# seconds). RADIUS client code will automatically try to use the next server
+# when the current server is not replying to requests. If this interval is set,
+# primary server will be retried after configured amount of time even if the
+# currently used secondary server is still working.
+#radius_retry_primary_interval=600
+
+
+# Interim accounting update interval
+# If this is set (larger than 0) and acct_server is configured, hostapd will
+# send interim accounting updates every N seconds. Note: if set, this overrides
+# possible Acct-Interim-Interval attribute in Access-Accept message. Thus, this
+# value should not be configured in hostapd.conf, if RADIUS server is used to
+# control the interim interval.
+# This value should not be less 600 (10 minutes) and must not be less than
+# 60 (1 minute).
+#radius_acct_interim_interval=600
+
+
+# hostapd can be used as a RADIUS authentication server for other hosts. This
+# requires that the integrated EAP authenticator is also enabled and both
+# authentication services are sharing the same configuration.
+
+# File name of the RADIUS clients configuration for the RADIUS server. If this
+# commented out, RADIUS server is disabled.
+#radius_server_clients=/etc/hostapd.radius_clients
+
+# The UDP port number for the RADIUS authentication server
+#radius_server_auth_port=1812
+
+
+##### WPA/IEEE 802.11i configuration ##########################################
+
+# Enable WPA. Setting this variable configures the AP to require WPA (either
+# WPA-PSK or WPA-RADIUS/EAP based on other configuration). For WPA-PSK, either
+# wpa_psk or wpa_passphrase must be set and wpa_key_mgmt must include WPA-PSK.
+# For WPA-RADIUS/EAP, ieee8021x must be set (but without dynamic WEP keys),
+# RADIUS authentication server must be configured, and WPA-EAP must be included
+# in wpa_key_mgmt.
+# This field is a bit field that can be used to enable WPA (IEEE 802.11i/D3.0)
+# and/or WPA2 (full IEEE 802.11i/RSN):
+# bit0 = WPA
+# bit1 = IEEE 802.11i/RSN (WPA2) (dot11RSNAEnabled)
+#wpa=1
+
+# WPA pre-shared keys for WPA-PSK. This can be either entered as a 256-bit
+# secret in hex format (64 hex digits), wpa_psk, or as an ASCII passphrase
+# (8..63 characters) that will be converted to PSK. This conversion uses SSID
+# so the PSK changes when ASCII passphrase is used and the SSID is changed.
+# wpa_psk (dot11RSNAConfigPSKValue)
+# wpa_passphrase (dot11RSNAConfigPSKPassPhrase)
+#wpa_psk=0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
+#wpa_passphrase=secret passphrase
+
+# Optionally, WPA PSKs can be read from a separate text file (containing list
+# of (PSK,MAC address) pairs. This allows more than one PSK to be configured.
+# Use absolute path name to make sure that the files can be read on SIGHUP
+# configuration reloads.
+#wpa_psk_file=/etc/hostapd.wpa_psk
+
+# Set of accepted key management algorithms (WPA-PSK, WPA-EAP, or both). The
+# entries are separated with a space.
+# (dot11RSNAConfigAuthenticationSuitesTable)
+#wpa_key_mgmt=WPA-PSK WPA-EAP
+
+# Set of accepted cipher suites (encryption algorithms) for pairwise keys
+# (unicast packets). This is a space separated list of algorithms:
+# CCMP = AES in Counter mode with CBC-MAC [RFC 3610, IEEE 802.11i/D7.0]
+# TKIP = Temporal Key Integrity Protocol [IEEE 802.11i/D7.0]
+# Group cipher suite (encryption algorithm for broadcast and multicast frames)
+# is automatically selected based on this configuration. If only CCMP is
+# allowed as the pairwise cipher, group cipher will also be CCMP. Otherwise,
+# TKIP will be used as the group cipher.
+# (dot11RSNAConfigPairwiseCiphersTable)
+#wpa_pairwise=TKIP CCMP
+
+# Time interval for rekeying GTK (broadcast/multicast encryption keys) in
+# seconds. (dot11RSNAConfigGroupRekeyTime)
+#wpa_group_rekey=600
+
+# Rekey GTK when any STA that possesses the current GTK is leaving the BSS.
+# (dot11RSNAConfigGroupRekeyStrict)
+#wpa_strict_rekey=1
+
+# Time interval for rekeying GMK (master key used internally to generate GTKs
+# (in seconds).
+#wpa_gmk_rekey=86400
+
+# Enable IEEE 802.11i/RSN/WPA2 pre-authentication. This is used to speed up
+# roaming be pre-authenticating IEEE 802.1X/EAP part of the full RSN
+# authentication and key handshake before actually associating with a new AP.
+# (dot11RSNAPreauthenticationEnabled)
+#rsn_preauth=1
+#
+# Space separated list of interfaces from which pre-authentication frames are
+# accepted (e.g., 'eth0' or 'eth0 wlan0wds0'. This list should include all
+# interface that are used for connections to other APs. This could include
+# wired interfaces and WDS links. The normal wireless data interface towards
+# associated stations (e.g., wlan0) should not be added, since
+# pre-authentication is only used with APs other than the currently associated
+# one.
+#rsn_preauth_interfaces=eth0
diff --git a/contrib/hostapd/hostapd.deny b/contrib/hostapd/hostapd.deny
new file mode 100644
index 000000000000..1616678f579e
--- /dev/null
+++ b/contrib/hostapd/hostapd.deny
@@ -0,0 +1,5 @@
+# List of MAC addresses that are not allowed to authenticate (IEEE 802.11)
+# with the AP.
+00:20:30:40:50:60
+00:ab:cd:ef:12:34
+00:00:30:40:50:60
diff --git a/contrib/hostapd/hostapd.eap_user b/contrib/hostapd/hostapd.eap_user
new file mode 100644
index 000000000000..529334a0e157
--- /dev/null
+++ b/contrib/hostapd/hostapd.eap_user
@@ -0,0 +1,45 @@
+# hostapd user database for integrated EAP authenticator
+# Each line must contain an identity, EAP method(s), and an optional password
+# separated with whitespace (space or tab). The identity and password must be
+# double quoted ("user"). [2] flag in the end of the line can be used to mark
+# users for tunneled phase 2 authentication (e.g., within EAP-PEAP). In these
+# cases, an anonymous identity can be used in the unencrypted phase 1 and the
+# real user identity is transmitted only within the encrypted tunnel in phase
+# 2. If non-anonymous access is needed, two user entries is needed, one for
+# phase 1 and another with the same username for phase 2.
+#
+# EAP-TLS, EAP-PEAP, EAP-TTLS, and EAP-SIM do not use password option.
+# EAP-MD5, EAP-MSCHAPV2, and EAP-GTC require a password.
+# EAP-PEAP and EAP-TTLS require Phase 2 configuration.
+#
+# * can be used as a wildcard to match any user identity. The main purposes for
+# this are to set anonymous phase 1 identity for EAP-PEAP and EAP-TTLS and to
+# avoid having to configure every certificate for EAP-TLS authentication. The
+# first matching entry is selected, so * should be used as the last phase 1
+# user entry.
+#
+# Multiple methods can be configured to make the authenticator try them one by
+# one until the peer accepts one. The method names are separated with a
+# comma (,).
+#
+# [ver=0] and [ver=1] flags after EAP type PEAP can be used to force PEAP
+# version based on the Phase 1 identity. Without this flag, the EAP
+# authenticator advertises the highest supported version and select the version
+# based on the first PEAP packet from the supplicant.
+
+# Phase 1 users
+"user" MD5 "password"
+"test user" MD5 "secret"
+"example user" TLS
+"DOMAIN\user" MSCHAPV2 "password"
+"gtc user" GTC "password"
+"ttls" TTLS
+"not anonymous" PEAP
+* PEAP,TTLS,TLS,SIM
+
+# Phase 2 (tunnelled within EAP-PEAP or EAP-TTLS) users
+"t-md5" MD5 "password" [2]
+"DOMAIN\t-mschapv2" MSCHAPV2 "password" [2]
+"t-gtc" GTC "password" [2]
+"not anonymous" MSCHAPV2 "password" [2]
+"user" MD5,GTC,MSCHAPV2 "password" [2]
diff --git a/contrib/hostapd/hostapd.h b/contrib/hostapd/hostapd.h
new file mode 100644
index 000000000000..9e6da65b6cdc
--- /dev/null
+++ b/contrib/hostapd/hostapd.h
@@ -0,0 +1,134 @@
+#ifndef HOSTAPD_H
+#define HOSTAPD_H
+
+#include "common.h"
+#include "ap.h"
+
+#ifndef ETH_ALEN
+#define ETH_ALEN 6
+#endif
+#ifndef IFNAMSIZ
+#define IFNAMSIZ 16
+#endif
+#ifndef ETH_P_ALL
+#define ETH_P_ALL 0x0003
+#endif
+
+#include "hostap_common.h"
+#include "config.h"
+
+struct ieee8023_hdr {
+ u8 dest[6];
+ u8 src[6];
+ u16 ethertype;
+} __attribute__ ((packed));
+
+
+struct ieee80211_hdr {
+ u16 frame_control;
+ u16 duration_id;
+ u8 addr1[6];
+ u8 addr2[6];
+ u8 addr3[6];
+ u16 seq_ctrl;
+ /* followed by 'u8 addr4[6];' if ToDS and FromDS is set in data frame
+ */
+} __attribute__ ((packed));
+
+#define IEEE80211_DA_FROMDS addr1
+#define IEEE80211_BSSID_FROMDS addr2
+#define IEEE80211_SA_FROMDS addr3
+
+#define IEEE80211_HDRLEN (sizeof(struct ieee80211_hdr))
+
+#define IEEE80211_FC(type, stype) host_to_le16((type << 2) | (stype << 4))
+
+/* MTU to be set for the wlan#ap device; this is mainly needed for IEEE 802.1X
+ * frames that might be longer than normal default MTU and they are not
+ * fragmented */
+#define HOSTAPD_MTU 2290
+
+extern unsigned char rfc1042_header[6];
+
+typedef struct hostapd_data hostapd;
+
+struct hostap_sta_driver_data {
+ unsigned long rx_packets, tx_packets, rx_bytes, tx_bytes;
+};
+
+struct driver_ops;
+struct wpa_ctrl_dst;
+struct radius_server_data;
+
+struct hostapd_data {
+ struct hostapd_config *conf;
+ char *config_fname;
+
+ u8 own_addr[6];
+
+ int num_sta; /* number of entries in sta_list */
+ struct sta_info *sta_list; /* STA info list head */
+ struct sta_info *sta_hash[STA_HASH_SIZE];
+
+ /* pointers to STA info; based on allocated AID or NULL if AID free
+ * AID is in the range 1-2007, so sta_aid[0] corresponders to AID 1
+ * and so on
+ */
+ struct sta_info *sta_aid[MAX_AID_TABLE_SIZE];
+
+ struct driver_ops *driver;
+
+ u8 *default_wep_key;
+ u8 default_wep_key_idx;
+
+ struct radius_client_data *radius;
+ u32 acct_session_id_hi, acct_session_id_lo;
+
+ struct iapp_data *iapp;
+
+ enum { DO_NOT_ASSOC = 0, WAIT_BEACON, AUTHENTICATE, ASSOCIATE,
+ ASSOCIATED } assoc_ap_state;
+ char assoc_ap_ssid[33];
+ int assoc_ap_ssid_len;
+ u16 assoc_ap_aid;
+
+ struct hostapd_cached_radius_acl *acl_cache;
+ struct hostapd_acl_query_data *acl_queries;
+
+ u8 *wpa_ie;
+ size_t wpa_ie_len;
+ struct wpa_authenticator *wpa_auth;
+
+#define PMKID_HASH_SIZE 128
+#define PMKID_HASH(pmkid) (unsigned int) ((pmkid)[0] & 0x7f)
+ struct rsn_pmksa_cache *pmkid[PMKID_HASH_SIZE];
+ struct rsn_pmksa_cache *pmksa;
+ int pmksa_count;
+
+ struct rsn_preauth_interface *preauth_iface;
+ time_t michael_mic_failure;
+ int michael_mic_failures;
+ int tkip_countermeasures;
+
+ int ctrl_sock;
+ struct wpa_ctrl_dst *ctrl_dst;
+
+ void *ssl_ctx;
+ void *eap_sim_db_priv;
+ struct radius_server_data *radius_srv;
+};
+
+void hostapd_new_assoc_sta(hostapd *hapd, struct sta_info *sta);
+void hostapd_logger(hostapd *hapd, u8 *addr, unsigned int module, int level,
+ char *fmt, ...) __attribute__ ((format (printf, 5, 6)));
+
+
+#define HOSTAPD_DEBUG(level, args...) \
+do { \
+ if (hapd->conf == NULL || hapd->conf->debug >= (level)) \
+ printf(args); \
+} while (0)
+
+#define HOSTAPD_DEBUG_COND(level) (hapd->conf->debug >= (level))
+
+#endif /* HOSTAPD_H */
diff --git a/contrib/hostapd/hostapd.radius_clients b/contrib/hostapd/hostapd.radius_clients
new file mode 100644
index 000000000000..3980427253b4
--- /dev/null
+++ b/contrib/hostapd/hostapd.radius_clients
@@ -0,0 +1,4 @@
+# RADIUS client configuration for the RADIUS server
+10.1.2.3 secret passphrase
+192.168.1.0/24 another very secret passphrase
+0.0.0.0/0 radius
diff --git a/contrib/hostapd/hostapd.sim_db b/contrib/hostapd/hostapd.sim_db
new file mode 100644
index 000000000000..01c593de8d2d
--- /dev/null
+++ b/contrib/hostapd/hostapd.sim_db
@@ -0,0 +1,9 @@
+# Example GSM authentication triplet file for EAP-SIM authenticator
+# IMSI:Kc:SRES:RAND
+# IMSI: ASCII string (numbers)
+# Kc: hex, 8 octets
+# SRES: hex, 4 octets
+# RAND: hex, 16 octets
+234567898765432:A0A1A2A3A4A5A6A7:D1D2D3D4:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+234567898765432:B0B1B2B3B4B5B6B7:E1E2E3E4:BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
+234567898765432:C0C1C2C3C4C5C6C7:F1F2F3F4:CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
diff --git a/contrib/hostapd/hostapd.wpa_psk b/contrib/hostapd/hostapd.wpa_psk
new file mode 100644
index 000000000000..0a9499acd736
--- /dev/null
+++ b/contrib/hostapd/hostapd.wpa_psk
@@ -0,0 +1,9 @@
+# List of WPA PSKs. Each line, except for empty lines and lines starting
+# with #, must contain a MAC address and PSK separated with a space.
+# Special MAC address 00:00:00:00:00:00 can be used to configure PSKs that
+# anyone can use. PSK can be configured as an ASCII passphrase of 8..63
+# characters or as a 256-bit hex PSK (64 hex digits).
+00:00:00:00:00:00 secret passphrase
+00:11:22:33:44:55 another passphrase
+00:22:33:44:55:66 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
+00:00:00:00:00:00 another passphrase for all STAs
diff --git a/contrib/hostapd/hostapd_cli.c b/contrib/hostapd/hostapd_cli.c
new file mode 100644
index 000000000000..5e305dcee988
--- /dev/null
+++ b/contrib/hostapd/hostapd_cli.c
@@ -0,0 +1,600 @@
+/*
+ * hostapd - command line interface for hostapd 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>
+
+#include "hostapd_ctrl.h"
+#include "version.h"
+
+
+static const char *hostapd_cli_version =
+"hostapd_cli v" VERSION_STR "\n"
+"Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.fi> and contributors";
+
+
+static const char *hostapd_cli_license =
+"This program is free software. You can distribute it and/or modify it\n"
+"under the terms of the GNU General Public License version 2.\n"
+"\n"
+"Alternatively, this software may be distributed under the terms of the\n"
+"BSD license. See README and COPYING for more details.\n";
+
+static const char *hostapd_cli_full_license =
+"This program is free software; you can redistribute it and/or modify\n"
+"it under the terms of the GNU General Public License version 2 as\n"
+"published by the Free Software Foundation.\n"
+"\n"
+"This program is distributed in the hope that it will be useful,\n"
+"but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
+"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
+"GNU General Public License for more details.\n"
+"\n"
+"You should have received a copy of the GNU General Public License\n"
+"along with this program; if not, write to the Free Software\n"
+"Foundation, Inc., 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"
+" mib = get MIB variables (dot1x, dot11, radius)\n"
+" sta <addr> = get MIB vatiables for one station\n"
+" all_sta = get MIB variables for all stations\n"
+" help = show this usage help\n"
+" interface [ifname] = show interfaces/select interface\n"
+" level <debug level> = change debug level\n"
+" license = show full hostapd_cli license\n"
+" quit = exit hostapd_cli\n";
+
+static struct hostapd_ctrl *ctrl_conn;
+static int hostapd_cli_quit = 0;
+static int hostapd_cli_attached = 0;
+static const char *ctrl_iface_dir = "/var/run/hostapd";
+static char *ctrl_ifname = NULL;
+
+
+static void usage(void)
+{
+ printf("hostapd_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/hostapd\n"
+ " default interface: first interface found in socket path\n"
+ "%s",
+ commands_help);
+}
+
+
+static struct hostapd_ctrl * hostapd_cli_open_connection(const char *ifname)
+{
+ 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 = hostapd_ctrl_open(cfile);
+ free(cfile);
+ return ctrl_conn;
+}
+
+
+static void hostapd_cli_close_connection(void)
+{
+ if (ctrl_conn == NULL)
+ return;
+
+ if (hostapd_cli_attached) {
+ hostapd_ctrl_detach(ctrl_conn);
+ hostapd_cli_attached = 0;
+ }
+ hostapd_ctrl_close(ctrl_conn);
+ ctrl_conn = NULL;
+}
+
+
+static void hostapd_cli_msg_cb(char *msg, size_t len)
+{
+ printf("%s\n", msg);
+}
+
+
+static int _hostapd_ctrl_command(struct hostapd_ctrl *ctrl, char *cmd,
+ int print)
+{
+ char buf[4096];
+ size_t len;
+ int ret;
+
+ if (ctrl_conn == NULL) {
+ printf("Not connected to hostapd - command dropped.\n");
+ return -1;
+ }
+ len = sizeof(buf) - 1;
+ ret = hostapd_ctrl_request(ctrl, cmd, strlen(cmd), buf, &len,
+ hostapd_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 inline int hostapd_ctrl_command(struct hostapd_ctrl *ctrl, char *cmd)
+{
+ return _hostapd_ctrl_command(ctrl, cmd, 1);
+}
+
+
+static int hostapd_cli_cmd_ping(struct hostapd_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return hostapd_ctrl_command(ctrl, "PING");
+}
+
+
+static int hostapd_cli_cmd_mib(struct hostapd_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return hostapd_ctrl_command(ctrl, "MIB");
+}
+
+
+static int hostapd_cli_cmd_sta(struct hostapd_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ char buf[64];
+ if (argc != 1) {
+ printf("Invalid 'sta' command - exactly one argument, STA "
+ "address, is required.\n");
+ return -1;
+ }
+ snprintf(buf, sizeof(buf), "STA %s", argv[0]);
+ return hostapd_ctrl_command(ctrl, buf);
+}
+
+
+static int hostapd_ctrl_command_sta(struct hostapd_ctrl *ctrl, char *cmd,
+ char *addr, size_t addr_len)
+{
+ char buf[4096], *pos;
+ size_t len;
+ int ret;
+
+ if (ctrl_conn == NULL) {
+ printf("Not connected to hostapd - command dropped.\n");
+ return -1;
+ }
+ len = sizeof(buf) - 1;
+ ret = hostapd_ctrl_request(ctrl, cmd, strlen(cmd), buf, &len,
+ hostapd_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;
+ }
+
+ buf[len] = '\0';
+ if (memcmp(buf, "FAIL", 4) == 0)
+ return -1;
+ printf("%s", buf);
+
+ pos = buf;
+ while (*pos != '\0' && *pos != '\n')
+ pos++;
+ *pos = '\0';
+ snprintf(addr, addr_len, "%s", buf);
+ return 0;
+}
+
+
+static int hostapd_cli_cmd_all_sta(struct hostapd_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ char addr[32], cmd[64];
+
+ if (hostapd_ctrl_command_sta(ctrl, "STA-FIRST", addr, sizeof(addr)))
+ return 0;
+ do {
+ snprintf(cmd, sizeof(cmd), "STA-NEXT %s", addr);
+ } while (hostapd_ctrl_command_sta(ctrl, cmd, addr, sizeof(addr)) == 0);
+
+ return -1;
+}
+
+
+static int hostapd_cli_cmd_help(struct hostapd_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ printf("%s", commands_help);
+ return 0;
+}
+
+
+static int hostapd_cli_cmd_license(struct hostapd_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ printf("%s\n\n%s\n", hostapd_cli_version, hostapd_cli_full_license);
+ return 0;
+}
+
+
+static int hostapd_cli_cmd_quit(struct hostapd_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ hostapd_cli_quit = 1;
+ return 0;
+}
+
+
+static int hostapd_cli_cmd_level(struct hostapd_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 hostapd_ctrl_command(ctrl, cmd);
+}
+
+
+static void hostapd_cli_list_interfaces(struct hostapd_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 hostapd_cli_cmd_interface(struct hostapd_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ if (argc < 1) {
+ hostapd_cli_list_interfaces(ctrl);
+ return 0;
+ }
+
+ hostapd_cli_close_connection();
+ free(ctrl_ifname);
+ ctrl_ifname = strdup(argv[0]);
+
+ if (hostapd_cli_open_connection(ctrl_ifname)) {
+ printf("Connected to interface '%s.\n", ctrl_ifname);
+ if (hostapd_ctrl_attach(ctrl_conn) == 0) {
+ hostapd_cli_attached = 1;
+ } else {
+ printf("Warning: Failed to attach to "
+ "hostapd.\n");
+ }
+ } else {
+ printf("Could not connect to interface '%s' - re-trying\n",
+ ctrl_ifname);
+ }
+ return 0;
+}
+
+
+struct hostapd_cli_cmd {
+ const char *cmd;
+ int (*handler)(struct hostapd_ctrl *ctrl, int argc, char *argv[]);
+};
+
+static struct hostapd_cli_cmd hostapd_cli_commands[] = {
+ { "ping", hostapd_cli_cmd_ping },
+ { "mib", hostapd_cli_cmd_mib },
+ { "sta", hostapd_cli_cmd_sta },
+ { "all_sta", hostapd_cli_cmd_all_sta },
+ { "help", hostapd_cli_cmd_help },
+ { "interface", hostapd_cli_cmd_interface },
+ { "level", hostapd_cli_cmd_level },
+ { "license", hostapd_cli_cmd_license },
+ { "quit", hostapd_cli_cmd_quit },
+ { NULL, NULL }
+};
+
+
+static void wpa_request(struct hostapd_ctrl *ctrl, int argc, char *argv[])
+{
+ struct hostapd_cli_cmd *cmd, *match = NULL;
+ int count;
+
+ count = 0;
+ cmd = hostapd_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 = hostapd_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 hostapd_cli_recv_pending(struct hostapd_ctrl *ctrl, int in_read)
+{
+ int first = 1;
+ if (ctrl_conn == NULL)
+ return;
+ while (hostapd_ctrl_pending(ctrl)) {
+ char buf[256];
+ size_t len = sizeof(buf) - 1;
+ if (hostapd_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;
+ }
+ }
+}
+
+
+static void hostapd_cli_interactive(void)
+{
+ const int max_args = 10;
+ char cmd[256], *res, *argv[max_args], *pos;
+ int argc;
+
+ printf("\nInteractive mode\n\n");
+
+ do {
+ hostapd_cli_recv_pending(ctrl_conn, 0);
+ printf("> ");
+ alarm(1);
+ res = fgets(cmd, sizeof(cmd), stdin);
+ alarm(0);
+ if (res == 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);
+ } while (!hostapd_cli_quit);
+}
+
+
+static void hostapd_cli_terminate(int sig)
+{
+ hostapd_cli_close_connection();
+ exit(0);
+}
+
+
+static void hostapd_cli_alarm(int sig)
+{
+ if (ctrl_conn && _hostapd_ctrl_command(ctrl_conn, "PING", 0)) {
+ printf("Connection to hostapd lost - trying to reconnect\n");
+ hostapd_cli_close_connection();
+ }
+ if (!ctrl_conn) {
+ ctrl_conn = hostapd_cli_open_connection(ctrl_ifname);
+ if (ctrl_conn) {
+ printf("Connection to hostapd re-established\n");
+ if (hostapd_ctrl_attach(ctrl_conn) == 0) {
+ hostapd_cli_attached = 1;
+ } else {
+ printf("Warning: Failed to attach to "
+ "hostapd.\n");
+ }
+ }
+ }
+ if (ctrl_conn)
+ hostapd_cli_recv_pending(ctrl_conn, 1);
+ alarm(1);
+}
+
+
+int main(int argc, char *argv[])
+{
+ int interactive;
+ int warning_displayed = 0;
+ int c;
+
+ for (;;) {
+ c = getopt(argc, argv, "hi:p:v");
+ if (c < 0)
+ break;
+ switch (c) {
+ case 'h':
+ usage();
+ return 0;
+ case 'v':
+ printf("%s\n", hostapd_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", hostapd_cli_version,
+ hostapd_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 = hostapd_cli_open_connection(ctrl_ifname);
+ if (ctrl_conn) {
+ if (warning_displayed)
+ printf("Connection established.\n");
+ break;
+ }
+
+ if (!interactive) {
+ perror("Failed to connect to hostapd - "
+ "hostapd_ctrl_open");
+ return -1;
+ }
+
+ if (!warning_displayed) {
+ printf("Could not connect to hostapd - re-trying\n");
+ warning_displayed = 1;
+ }
+ sleep(1);
+ continue;
+ }
+
+ signal(SIGINT, hostapd_cli_terminate);
+ signal(SIGTERM, hostapd_cli_terminate);
+ signal(SIGALRM, hostapd_cli_alarm);
+
+ if (interactive) {
+ if (hostapd_ctrl_attach(ctrl_conn) == 0) {
+ hostapd_cli_attached = 1;
+ } else {
+ printf("Warning: Failed to attach to hostapd.\n");
+ }
+ hostapd_cli_interactive();
+ } else
+ wpa_request(ctrl_conn, argc - optind, &argv[optind]);
+
+ free(ctrl_ifname);
+ hostapd_cli_close_connection();
+ return 0;
+}
diff --git a/contrib/hostapd/hostapd_ctrl.c b/contrib/hostapd/hostapd_ctrl.c
new file mode 100644
index 000000000000..55b690451b50
--- /dev/null
+++ b/contrib/hostapd/hostapd_ctrl.c
@@ -0,0 +1,188 @@
+/*
+ * hostapd - hostapd control interface library
+ * 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>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/un.h>
+
+#include "hostapd_ctrl.h"
+
+
+struct hostapd_ctrl {
+ int s;
+ struct sockaddr_un local;
+ struct sockaddr_un dest;
+};
+
+
+struct hostapd_ctrl * hostapd_ctrl_open(const char *ctrl_path)
+{
+ struct hostapd_ctrl *ctrl;
+ static int counter = 0;
+
+ ctrl = malloc(sizeof(*ctrl));
+ if (ctrl == NULL)
+ return NULL;
+ memset(ctrl, 0, sizeof(*ctrl));
+
+ 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/hostapd_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;
+ }
+
+ return ctrl;
+}
+
+
+void hostapd_ctrl_close(struct hostapd_ctrl *ctrl)
+{
+ unlink(ctrl->local.sun_path);
+ close(ctrl->s);
+ free(ctrl);
+}
+
+
+int hostapd_ctrl_request(struct hostapd_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 hostapd_ctrl_attach_helper(struct hostapd_ctrl *ctrl, int attach)
+{
+ char buf[10];
+ int ret;
+ size_t len = 10;
+
+ ret = hostapd_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 hostapd_ctrl_attach(struct hostapd_ctrl *ctrl)
+{
+ return hostapd_ctrl_attach_helper(ctrl, 1);
+}
+
+
+int hostapd_ctrl_detach(struct hostapd_ctrl *ctrl)
+{
+ return hostapd_ctrl_attach_helper(ctrl, 0);
+}
+
+
+int hostapd_ctrl_recv(struct hostapd_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 hostapd_ctrl_pending(struct hostapd_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 hostapd_ctrl_get_fd(struct hostapd_ctrl *ctrl)
+{
+ return ctrl->s;
+}
diff --git a/contrib/hostapd/hostapd_ctrl.h b/contrib/hostapd/hostapd_ctrl.h
new file mode 100644
index 000000000000..7ba221e19fc1
--- /dev/null
+++ b/contrib/hostapd/hostapd_ctrl.h
@@ -0,0 +1,18 @@
+#ifndef HOSTAPD_CTRL_H
+#define HOSTAPD_CTRL_H
+
+struct hostapd_ctrl;
+
+struct hostapd_ctrl * hostapd_ctrl_open(const char *ctrl_path);
+void hostapd_ctrl_close(struct hostapd_ctrl *ctrl);
+int hostapd_ctrl_request(struct hostapd_ctrl *ctrl, char *cmd, size_t cmd_len,
+ char *reply, size_t *reply_len,
+ void (*msg_cb)(char *msg, size_t len));
+int hostapd_ctrl_attach(struct hostapd_ctrl *ctrl);
+int hostapd_ctrl_detach(struct hostapd_ctrl *ctrl);
+int hostapd_ctrl_recv(struct hostapd_ctrl *ctrl, char *reply,
+ size_t *reply_len);
+int hostapd_ctrl_pending(struct hostapd_ctrl *ctrl);
+int hostapd_ctrl_get_fd(struct hostapd_ctrl *ctrl);
+
+#endif /* HOSTAPD_CTRL_H */
diff --git a/contrib/hostapd/iapp.c b/contrib/hostapd/iapp.c
new file mode 100644
index 000000000000..792a4714c4ff
--- /dev/null
+++ b/contrib/hostapd/iapp.c
@@ -0,0 +1,521 @@
+/*
+ * Host AP (software wireless LAN access point) user space daemon for
+ * Host AP kernel driver / IEEE 802.11F-2003 Inter-Access Point Protocol (IAPP)
+ * 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.
+ */
+
+/* TODO:
+ * Level 1: no administrative or security support
+ * (e.g., static BSSID to IP address mapping in each AP)
+ * Level 2: support for dynamic mapping of BSSID to IP address
+ * Level 3: support for encryption and authentication of IAPP messages
+ * - add support for MOVE-notify and MOVE-response (this requires support for
+ * finding out IP address for previous AP using RADIUS)
+ * - add support for Send- and ACK-Security-Block to speedup IEEE 802.1X during
+ * reassociation to another AP
+ * - implement counters etc. for IAPP MIB
+ * - verify endianness of fields in IAPP messages; are they big-endian as
+ * used here?
+ * - RADIUS connection for AP registration and BSSID to IP address mapping
+ * - TCP connection for IAPP MOVE, CACHE
+ * - broadcast ESP for IAPP ADD-notify
+ * - ESP for IAPP MOVE messages
+ * - security block sending/processing
+ * - IEEE 802.11 context transfer
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <netinet/in.h>
+#include <net/if.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#ifdef USE_KERNEL_HEADERS
+#include <linux/if_packet.h>
+#else /* USE_KERNEL_HEADERS */
+#include <netpacket/packet.h>
+#endif /* USE_KERNEL_HEADERS */
+
+#include "hostapd.h"
+#include "ieee802_11.h"
+#include "iapp.h"
+#include "eloop.h"
+#include "sta_info.h"
+
+
+#define IAPP_MULTICAST "224.0.1.178"
+#define IAPP_UDP_PORT 3517
+#define IAPP_TCP_PORT 3517
+
+struct iapp_hdr {
+ u8 version;
+ u8 command;
+ u16 identifier;
+ u16 length;
+ /* followed by length-6 octets of data */
+} __attribute__ ((packed));
+
+#define IAPP_VERSION 0
+
+enum IAPP_COMMAND {
+ IAPP_CMD_ADD_notify = 0,
+ IAPP_CMD_MOVE_notify = 1,
+ IAPP_CMD_MOVE_response = 2,
+ IAPP_CMD_Send_Security_Block = 3,
+ IAPP_CMD_ACK_Security_Block = 4,
+ IAPP_CMD_CACHE_notify = 5,
+ IAPP_CMD_CACHE_response = 6,
+};
+
+
+/* ADD-notify - multicast UDP on the local LAN */
+struct iapp_add_notify {
+ u8 addr_len; /* ETH_ALEN */
+ u8 reserved;
+ u8 mac_addr[ETH_ALEN];
+ u16 seq_num;
+} __attribute__ ((packed));
+
+
+/* Layer 2 Update frame (802.2 Type 1 LLC XID Update response) */
+struct iapp_layer2_update {
+ u8 da[ETH_ALEN]; /* broadcast */
+ u8 sa[ETH_ALEN]; /* STA addr */
+ u16 len; /* 6 */
+ u8 dsap; /* null DSAP address */
+ u8 ssap; /* null SSAP address, CR=Response */
+ u8 control;
+ u8 xid_info[3];
+} __attribute__ ((packed));
+
+
+/* MOVE-notify - unicast TCP */
+struct iapp_move_notify {
+ u8 addr_len; /* ETH_ALEN */
+ u8 reserved;
+ u8 mac_addr[ETH_ALEN];
+ u16 seq_num;
+ u16 ctx_block_len;
+ /* followed by ctx_block_len bytes */
+} __attribute__ ((packed));
+
+
+/* MOVE-response - unicast TCP */
+struct iapp_move_response {
+ u8 addr_len; /* ETH_ALEN */
+ u8 status;
+ u8 mac_addr[ETH_ALEN];
+ u16 seq_num;
+ u16 ctx_block_len;
+ /* followed by ctx_block_len bytes */
+} __attribute__ ((packed));
+
+enum {
+ IAPP_MOVE_SUCCESSFUL = 0,
+ IAPP_MOVE_DENIED = 1,
+ IAPP_MOVE_STALE_MOVE = 2,
+};
+
+
+/* CACHE-notify */
+struct iapp_cache_notify {
+ u8 addr_len; /* ETH_ALEN */
+ u8 reserved;
+ u8 mac_addr[ETH_ALEN];
+ u16 seq_num;
+ u8 current_ap[ETH_ALEN];
+ u16 ctx_block_len;
+ /* ctx_block_len bytes of context block followed by 16-bit context
+ * timeout */
+} __attribute__ ((packed));
+
+
+/* CACHE-response - unicast TCP */
+struct iapp_cache_response {
+ u8 addr_len; /* ETH_ALEN */
+ u8 status;
+ u8 mac_addr[ETH_ALEN];
+ u16 seq_num;
+} __attribute__ ((packed));
+
+enum {
+ IAPP_CACHE_SUCCESSFUL = 0,
+ IAPP_CACHE_STALE_CACHE = 1,
+};
+
+
+/* Send-Security-Block - unicast TCP */
+struct iapp_send_security_block {
+ u8 iv[8];
+ u16 sec_block_len;
+ /* followed by sec_block_len bytes of security block */
+} __attribute__ ((packed));
+
+
+/* ACK-Security-Block - unicast TCP */
+struct iapp_ack_security_block {
+ u8 iv[8];
+ u8 new_ap_ack_authenticator[48];
+} __attribute__ ((packed));
+
+
+struct iapp_data {
+ struct hostapd_data *hapd;
+ u16 identifier; /* next IAPP identifier */
+ struct in_addr own, multicast;
+ int udp_sock;
+ int packet_sock;
+};
+
+
+static void iapp_send_add(struct iapp_data *iapp, u8 *macaddr, u16 seq_num)
+{
+ char buf[128];
+ struct iapp_hdr *hdr;
+ struct iapp_add_notify *add;
+ struct sockaddr_in addr;
+
+ /* Send IAPP ADD-notify to remove possible association from other APs
+ */
+
+ hdr = (struct iapp_hdr *) buf;
+ hdr->version = IAPP_VERSION;
+ hdr->command = IAPP_CMD_ADD_notify;
+ hdr->identifier = host_to_be16(iapp->identifier++);
+ hdr->length = host_to_be16(sizeof(*hdr) + sizeof(*add));
+
+ add = (struct iapp_add_notify *) (hdr + 1);
+ add->addr_len = ETH_ALEN;
+ add->reserved = 0;
+ memcpy(add->mac_addr, macaddr, ETH_ALEN);
+
+ add->seq_num = host_to_be16(seq_num);
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = iapp->multicast.s_addr;
+ addr.sin_port = htons(IAPP_UDP_PORT);
+ if (sendto(iapp->udp_sock, buf, (char *) (add + 1) - buf, 0,
+ (struct sockaddr *) &addr, sizeof(addr)) < 0)
+ perror("sendto[IAPP-ADD]");
+}
+
+
+static void iapp_send_layer2_update(struct iapp_data *iapp, u8 *addr)
+{
+ struct iapp_layer2_update msg;
+
+ /* Send Level 2 Update Frame to update forwarding tables in layer 2
+ * bridge devices */
+
+ /* 802.2 Type 1 Logical Link Control (LLC) Exchange Identifier (XID)
+ * Update response frame; IEEE Std 802.2-1998, 5.4.1.2.1 */
+
+ memset(msg.da, 0xff, ETH_ALEN);
+ memcpy(msg.sa, addr, ETH_ALEN);
+ msg.len = host_to_be16(6);
+ msg.dsap = 0; /* NULL DSAP address */
+ msg.ssap = 0x01; /* NULL SSAP address, CR Bit: Response */
+ msg.control = 0xaf; /* XID response lsb.1111F101.
+ * F=0 (no poll command; unsolicited frame) */
+ msg.xid_info[0] = 0x81; /* XID format identifier */
+ msg.xid_info[1] = 1; /* LLC types/classes: Type 1 LLC */
+ msg.xid_info[2] = 1 << 1; /* XID sender's receive window size (RW)
+ * FIX: what is correct RW with 802.11? */
+
+ if (send(iapp->packet_sock, &msg, sizeof(msg), 0) < 0)
+ perror("send[L2 Update]");
+}
+
+
+void iapp_new_station(struct iapp_data *iapp, struct sta_info *sta)
+{
+ struct ieee80211_mgmt *assoc;
+ u16 seq;
+
+ if (iapp == NULL)
+ return;
+
+ assoc = sta->last_assoc_req;
+ seq = assoc ? WLAN_GET_SEQ_SEQ(le_to_host16(assoc->seq_ctrl)) : 0;
+
+ /* IAPP-ADD.request(MAC Address, Sequence Number, Timeout) */
+ hostapd_logger(iapp->hapd, sta->addr, HOSTAPD_MODULE_IAPP,
+ HOSTAPD_LEVEL_DEBUG, "IAPP-ADD.request(seq=%d)", seq);
+ iapp_send_layer2_update(iapp, sta->addr);
+ iapp_send_add(iapp, sta->addr, seq);
+
+ if (assoc && WLAN_FC_GET_STYPE(le_to_host16(assoc->frame_control)) ==
+ WLAN_FC_STYPE_REASSOC_REQ) {
+ /* IAPP-MOVE.request(MAC Address, Sequence Number, Old AP,
+ * Context Block, Timeout)
+ */
+ /* TODO: Send IAPP-MOVE to the old AP; Map Old AP BSSID to
+ * IP address */
+ }
+}
+
+
+static void iapp_process_add_notify(struct iapp_data *iapp,
+ struct sockaddr_in *from,
+ struct iapp_hdr *hdr, int len)
+{
+ struct iapp_add_notify *add = (struct iapp_add_notify *) (hdr + 1);
+ struct sta_info *sta;
+
+ if (len != sizeof(*add)) {
+ printf("Invalid IAPP-ADD packet length %d (expected %lu)\n",
+ len, (unsigned long) sizeof(*add));
+ return;
+ }
+
+ sta = ap_get_sta(iapp->hapd, add->mac_addr);
+
+ /* IAPP-ADD.indication(MAC Address, Sequence Number) */
+ hostapd_logger(iapp->hapd, add->mac_addr, HOSTAPD_MODULE_IAPP,
+ HOSTAPD_LEVEL_INFO,
+ "Received IAPP ADD-notify (seq# %d) from %s:%d%s",
+ be_to_host16(add->seq_num),
+ inet_ntoa(from->sin_addr), ntohs(from->sin_port),
+ sta ? "" : " (STA not found)");
+
+ if (!sta)
+ return;
+
+ /* TODO: could use seq_num to try to determine whether last association
+ * to this AP is newer than the one advertised in IAPP-ADD. Although,
+ * this is not really a reliable verification. */
+
+ hostapd_logger(iapp->hapd, add->mac_addr, HOSTAPD_MODULE_IAPP,
+ HOSTAPD_LEVEL_DEBUG,
+ "Removing STA due to IAPP ADD-notify");
+ sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC | WLAN_STA_AUTHORIZED);
+ eloop_cancel_timeout(ap_handle_timer, iapp->hapd, sta);
+ eloop_register_timeout(0, 0, ap_handle_timer, iapp->hapd, sta);
+ sta->timeout_next = STA_REMOVE;
+}
+
+
+static void iapp_receive_udp(int sock, void *eloop_ctx, void *sock_ctx)
+{
+ struct iapp_data *iapp = eloop_ctx;
+ int len, hlen;
+ unsigned char buf[128];
+ struct sockaddr_in from;
+ socklen_t fromlen;
+ struct iapp_hdr *hdr;
+
+ /* Handle incoming IAPP frames (over UDP/IP) */
+
+ fromlen = sizeof(from);
+ len = recvfrom(iapp->udp_sock, buf, sizeof(buf), 0,
+ (struct sockaddr *) &from, &fromlen);
+ if (len < 0)
+ perror("recvfrom");
+
+ if (from.sin_addr.s_addr == iapp->own.s_addr)
+ return; /* ignore own IAPP messages */
+
+ hostapd_logger(iapp->hapd, NULL, HOSTAPD_MODULE_IAPP,
+ HOSTAPD_LEVEL_DEBUG,
+ "Received %d byte IAPP frame from %s%s\n",
+ len, inet_ntoa(from.sin_addr),
+ len < sizeof(*hdr) ? " (too short)" : "");
+
+ if (len < sizeof(*hdr))
+ return;
+
+ hdr = (struct iapp_hdr *) buf;
+ hlen = be_to_host16(hdr->length);
+ hostapd_logger(iapp->hapd, NULL, HOSTAPD_MODULE_IAPP,
+ HOSTAPD_LEVEL_DEBUG,
+ "RX: version=%d command=%d id=%d len=%d\n",
+ hdr->version, hdr->command,
+ be_to_host16(hdr->identifier), hlen);
+ if (hdr->version != IAPP_VERSION) {
+ printf("Dropping IAPP frame with unknown version %d\n",
+ hdr->version);
+ return;
+ }
+ if (hlen > len) {
+ printf("Underflow IAPP frame (hlen=%d len=%d)\n", hlen, len);
+ return;
+ }
+ if (hlen < len) {
+ printf("Ignoring %d extra bytes from IAPP frame\n",
+ len - hlen);
+ len = hlen;
+ }
+
+ switch (hdr->command) {
+ case IAPP_CMD_ADD_notify:
+ iapp_process_add_notify(iapp, &from, hdr, hlen - sizeof(*hdr));
+ break;
+ case IAPP_CMD_MOVE_notify:
+ /* TODO: MOVE is using TCP; so move this to TCP handler once it
+ * is implemented.. */
+ /* IAPP-MOVE.indication(MAC Address, New BSSID,
+ * Sequence Number, AP Address, Context Block) */
+ /* TODO: process */
+ break;
+ default:
+ printf("Unknown IAPP command %d\n", hdr->command);
+ break;
+ }
+}
+
+
+struct iapp_data * iapp_init(struct hostapd_data *hapd, const char *iface)
+{
+ struct ifreq ifr;
+ struct sockaddr_ll addr;
+ int ifindex;
+ struct sockaddr_in *paddr, uaddr;
+ struct iapp_data *iapp;
+ struct ip_mreqn mreq;
+
+ iapp = malloc(sizeof(*iapp));
+ if (iapp == NULL)
+ return NULL;
+ memset(iapp, 0, sizeof(*iapp));
+ iapp->hapd = hapd;
+ iapp->udp_sock = iapp->packet_sock = -1;
+
+ /* TODO:
+ * open socket for sending and receiving IAPP frames over TCP
+ */
+
+ iapp->udp_sock = socket(PF_INET, SOCK_DGRAM, 0);
+ if (iapp->udp_sock < 0) {
+ perror("socket[PF_INET,SOCK_DGRAM]");
+ iapp_deinit(iapp);
+ return NULL;
+ }
+
+ memset(&ifr, 0, sizeof(ifr));
+ strncpy(ifr.ifr_name, iface, sizeof(ifr.ifr_name));
+ if (ioctl(iapp->udp_sock, SIOCGIFINDEX, &ifr) != 0) {
+ perror("ioctl(SIOCGIFINDEX)");
+ iapp_deinit(iapp);
+ return NULL;
+ }
+ ifindex = ifr.ifr_ifindex;
+
+ if (ioctl(iapp->udp_sock, SIOCGIFADDR, &ifr) != 0) {
+ perror("ioctl(SIOCGIFADDR)");
+ iapp_deinit(iapp);
+ return NULL;
+ }
+ paddr = (struct sockaddr_in *) &ifr.ifr_addr;
+ if (paddr->sin_family != AF_INET) {
+ printf("Invalid address family %i (SIOCGIFADDR)\n",
+ paddr->sin_family);
+ iapp_deinit(iapp);
+ return NULL;
+ }
+ iapp->own.s_addr = paddr->sin_addr.s_addr;
+
+ if (ioctl(iapp->udp_sock, SIOCGIFBRDADDR, &ifr) != 0) {
+ perror("ioctl(SIOCGIFBRDADDR)");
+ iapp_deinit(iapp);
+ return NULL;
+ }
+ paddr = (struct sockaddr_in *) &ifr.ifr_addr;
+ if (paddr->sin_family != AF_INET) {
+ printf("Invalid address family %i (SIOCGIFBRDADDR)\n",
+ paddr->sin_family);
+ iapp_deinit(iapp);
+ return NULL;
+ }
+ inet_aton(IAPP_MULTICAST, &iapp->multicast);
+
+ memset(&uaddr, 0, sizeof(uaddr));
+ uaddr.sin_family = AF_INET;
+ uaddr.sin_port = htons(IAPP_UDP_PORT);
+ if (bind(iapp->udp_sock, (struct sockaddr *) &uaddr,
+ sizeof(uaddr)) < 0) {
+ perror("bind[UDP]");
+ iapp_deinit(iapp);
+ return NULL;
+ }
+
+ memset(&mreq, 0, sizeof(mreq));
+ mreq.imr_multiaddr = iapp->multicast;
+ mreq.imr_address.s_addr = INADDR_ANY;
+ mreq.imr_ifindex = 0;
+ if (setsockopt(iapp->udp_sock, SOL_IP, IP_ADD_MEMBERSHIP, &mreq,
+ sizeof(mreq)) < 0) {
+ perror("setsockopt[UDP,IP_ADD_MEMBERSHIP]");
+ return NULL;
+ }
+
+ iapp->packet_sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
+ if (iapp->packet_sock < 0) {
+ perror("socket[PF_PACKET,SOCK_RAW]");
+ return NULL;
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sll_family = AF_PACKET;
+ addr.sll_ifindex = ifindex;
+ if (bind(iapp->packet_sock, (struct sockaddr *) &addr,
+ sizeof(addr)) < 0) {
+ perror("bind[PACKET]");
+ return NULL;
+ }
+
+ if (eloop_register_read_sock(iapp->udp_sock, iapp_receive_udp,
+ iapp, NULL)) {
+ printf("Could not register read socket for IAPP.\n");
+ return NULL;
+ }
+
+ printf("IEEE 802.11F (IAPP) using interface %s\n", iface);
+
+ /* TODO: For levels 2 and 3: send RADIUS Initiate-Request, receive
+ * RADIUS Initiate-Accept or Initiate-Reject. IAPP port should actually
+ * be openned only after receiving Initiate-Accept. If Initiate-Reject
+ * is received, IAPP is not started. */
+
+ return iapp;
+}
+
+
+void iapp_deinit(struct iapp_data *iapp)
+{
+ struct ip_mreqn mreq;
+
+ if (iapp == NULL)
+ return;
+
+ if (iapp->udp_sock >= 0) {
+ memset(&mreq, 0, sizeof(mreq));
+ mreq.imr_multiaddr = iapp->multicast;
+ mreq.imr_address.s_addr = INADDR_ANY;
+ mreq.imr_ifindex = 0;
+ if (setsockopt(iapp->udp_sock, SOL_IP, IP_DROP_MEMBERSHIP,
+ &mreq, sizeof(mreq)) < 0) {
+ perror("setsockopt[UDP,IP_DEL_MEMBERSHIP]");
+ }
+
+ eloop_unregister_read_sock(iapp->udp_sock);
+ close(iapp->udp_sock);
+ }
+ if (iapp->packet_sock >= 0) {
+ eloop_unregister_read_sock(iapp->packet_sock);
+ close(iapp->packet_sock);
+ }
+ free(iapp);
+}
diff --git a/contrib/hostapd/iapp.h b/contrib/hostapd/iapp.h
new file mode 100644
index 000000000000..d6e8f6570e2b
--- /dev/null
+++ b/contrib/hostapd/iapp.h
@@ -0,0 +1,31 @@
+#ifndef IAPP_H
+#define IAPP_H
+
+struct iapp_data;
+
+#ifdef CONFIG_IAPP
+
+void iapp_new_station(struct iapp_data *iapp, struct sta_info *sta);
+struct iapp_data * iapp_init(struct hostapd_data *hapd, const char *iface);
+void iapp_deinit(struct iapp_data *iapp);
+
+#else /* CONFIG_IAPP */
+
+static inline void iapp_new_station(struct iapp_data *iapp,
+ struct sta_info *sta)
+{
+}
+
+static inline struct iapp_data * iapp_init(struct hostapd_data *hapd,
+ const char *iface)
+{
+ return NULL;
+}
+
+static inline void iapp_deinit(struct iapp_data *iapp)
+{
+}
+
+#endif /* CONFIG_IAPP */
+
+#endif /* IAPP_H */
diff --git a/contrib/hostapd/ieee802_11.c b/contrib/hostapd/ieee802_11.c
new file mode 100644
index 000000000000..16a3d0e7741c
--- /dev/null
+++ b/contrib/hostapd/ieee802_11.c
@@ -0,0 +1,1217 @@
+/*
+ * Host AP (software wireless LAN access point) user space daemon for
+ * Host AP kernel driver / IEEE 802.11 Management
+ * 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/socket.h>
+#include <net/if.h>
+#include <arpa/inet.h>
+#include <time.h>
+
+#include "eloop.h"
+#include "hostapd.h"
+#include "ieee802_11.h"
+#include "radius.h"
+#include "radius_client.h"
+#include "ieee802_11_auth.h"
+#include "sta_info.h"
+#include "eapol_sm.h"
+#include "rc4.h"
+#include "ieee802_1x.h"
+#include "wpa.h"
+#include "accounting.h"
+#include "driver.h"
+
+
+static u8 * hostapd_eid_supp_rates(hostapd *hapd, u8 *eid)
+{
+ u8 *pos = eid;
+
+ *pos++ = WLAN_EID_SUPP_RATES;
+ *pos++ = 4; /* len */
+ *pos++ = 0x82; /* 1 Mbps, base set */
+ *pos++ = 0x84; /* 2 Mbps, base set */
+ *pos++ = 0x0b; /* 5.5 Mbps */
+ *pos++ = 0x16; /* 11 Mbps */
+
+ return pos;
+}
+
+
+static u16 hostapd_own_capab_info(hostapd *hapd)
+{
+ int capab = WLAN_CAPABILITY_ESS;
+ if (hapd->conf->wpa ||
+ (hapd->conf->ieee802_1x &&
+ (hapd->conf->default_wep_key_len ||
+ hapd->conf->individual_wep_key_len)))
+ capab |= WLAN_CAPABILITY_PRIVACY;
+ return capab;
+}
+
+
+static const u8 WPA_OUI_TYPE[] = { 0x00, 0x50, 0xf2, 1 };
+
+ParseRes ieee802_11_parse_elems(struct hostapd_data *hapd, u8 *start,
+ size_t len,
+ struct ieee802_11_elems *elems,
+ int show_errors)
+{
+ size_t left = len;
+ u8 *pos = start;
+ int unknown = 0;
+
+ memset(elems, 0, sizeof(*elems));
+
+ while (left >= 2) {
+ u8 id, elen;
+
+ id = *pos++;
+ elen = *pos++;
+ left -= 2;
+
+ if (elen > left) {
+ if (show_errors) {
+ HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL,
+ "IEEE 802.11 element parse "
+ "failed (id=%d elen=%d "
+ "left=%lu)\n",
+ id, elen, (unsigned long) left);
+ if (HOSTAPD_DEBUG_COND(HOSTAPD_DEBUG_MINIMAL))
+ hostapd_hexdump("IEs", start, len);
+ }
+ return ParseFailed;
+ }
+
+ switch (id) {
+ case WLAN_EID_SSID:
+ elems->ssid = pos;
+ elems->ssid_len = elen;
+ break;
+ case WLAN_EID_SUPP_RATES:
+ elems->supp_rates = pos;
+ elems->supp_rates_len = elen;
+ break;
+ case WLAN_EID_FH_PARAMS:
+ elems->fh_params = pos;
+ elems->fh_params_len = elen;
+ break;
+ case WLAN_EID_DS_PARAMS:
+ elems->ds_params = pos;
+ elems->ds_params_len = elen;
+ break;
+ case WLAN_EID_CF_PARAMS:
+ elems->cf_params = pos;
+ elems->cf_params_len = elen;
+ break;
+ case WLAN_EID_TIM:
+ elems->tim = pos;
+ elems->tim_len = elen;
+ break;
+ case WLAN_EID_IBSS_PARAMS:
+ elems->ibss_params = pos;
+ elems->ibss_params_len = elen;
+ break;
+ case WLAN_EID_CHALLENGE:
+ elems->challenge = pos;
+ elems->challenge_len = elen;
+ break;
+ case WLAN_EID_GENERIC:
+ if (elen > 4 && memcmp(pos, WPA_OUI_TYPE, 4) == 0) {
+ elems->wpa_ie = pos;
+ elems->wpa_ie_len = elen;
+ } else if (show_errors) {
+ HOSTAPD_DEBUG(HOSTAPD_DEBUG_EXCESSIVE,
+ "IEEE 802.11 element parse "
+ "ignored unknown generic element"
+ " (id=%d elen=%d OUI:type="
+ "%02x-%02x-%02x:%d)\n",
+ id, elen,
+ elen >= 1 ? pos[0] : 0,
+ elen >= 2 ? pos[1] : 0,
+ elen >= 3 ? pos[2] : 0,
+ elen >= 4 ? pos[3] : 0);
+ unknown++;
+ } else {
+ unknown++;
+ }
+ break;
+ case WLAN_EID_RSN:
+ elems->rsn_ie = pos;
+ elems->rsn_ie_len = elen;
+ break;
+ default:
+ unknown++;
+ if (!show_errors)
+ break;
+ HOSTAPD_DEBUG(HOSTAPD_DEBUG_EXCESSIVE,
+ "IEEE 802.11 element parse ignored "
+ "unknown element (id=%d elen=%d)\n",
+ id, elen);
+ break;
+ }
+
+ left -= elen;
+ pos += elen;
+ }
+
+ if (left)
+ return ParseFailed;
+
+ return unknown ? ParseUnknown : ParseOK;
+}
+
+
+static void ieee802_11_print_ssid(u8 *ssid, u8 len)
+{
+ int i;
+ for (i = 0; i < len; i++) {
+ if (ssid[i] >= 32 && ssid[i] < 127)
+ printf("%c", ssid[i]);
+ else
+ printf("<%02x>", ssid[i]);
+ }
+}
+
+
+static void ieee802_11_sta_authenticate(void *eloop_ctx, void *timeout_ctx)
+{
+ hostapd *hapd = eloop_ctx;
+ struct ieee80211_mgmt mgmt;
+
+ if (hapd->assoc_ap_state == WAIT_BEACON)
+ hapd->assoc_ap_state = AUTHENTICATE;
+ if (hapd->assoc_ap_state != AUTHENTICATE)
+ return;
+
+ printf("Authenticate with AP " MACSTR " SSID=",
+ MAC2STR(hapd->conf->assoc_ap_addr));
+ ieee802_11_print_ssid(hapd->assoc_ap_ssid, hapd->assoc_ap_ssid_len);
+ printf(" (as station)\n");
+
+ memset(&mgmt, 0, sizeof(mgmt));
+ mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+ WLAN_FC_STYPE_AUTH);
+ /* Request TX callback */
+ mgmt.frame_control |= host_to_le16(BIT(1));
+ memcpy(mgmt.da, hapd->conf->assoc_ap_addr, ETH_ALEN);
+ memcpy(mgmt.sa, hapd->own_addr, ETH_ALEN);
+ memcpy(mgmt.bssid, hapd->conf->assoc_ap_addr, ETH_ALEN);
+ mgmt.u.auth.auth_alg = host_to_le16(WLAN_AUTH_OPEN);
+ mgmt.u.auth.auth_transaction = host_to_le16(1);
+ mgmt.u.auth.status_code = host_to_le16(0);
+ if (hostapd_send_mgmt_frame(hapd, &mgmt, IEEE80211_HDRLEN +
+ sizeof(mgmt.u.auth), 0) < 0)
+ perror("ieee802_11_sta_authenticate: send");
+
+ /* Try to authenticate again, if this attempt fails or times out. */
+ eloop_register_timeout(5, 0, ieee802_11_sta_authenticate, hapd, NULL);
+}
+
+
+static void ieee802_11_sta_associate(void *eloop_ctx, void *timeout_ctx)
+{
+ hostapd *hapd = eloop_ctx;
+ u8 buf[256];
+ struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *) buf;
+ u8 *p;
+
+ if (hapd->assoc_ap_state == AUTHENTICATE)
+ hapd->assoc_ap_state = ASSOCIATE;
+ if (hapd->assoc_ap_state != ASSOCIATE)
+ return;
+
+ printf("Associate with AP " MACSTR " SSID=",
+ MAC2STR(hapd->conf->assoc_ap_addr));
+ ieee802_11_print_ssid(hapd->assoc_ap_ssid, hapd->assoc_ap_ssid_len);
+ printf(" (as station)\n");
+
+ memset(mgmt, 0, sizeof(*mgmt));
+ mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+ WLAN_FC_STYPE_ASSOC_REQ);
+ /* Request TX callback */
+ mgmt->frame_control |= host_to_le16(BIT(1));
+ memcpy(mgmt->da, hapd->conf->assoc_ap_addr, ETH_ALEN);
+ memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN);
+ memcpy(mgmt->bssid, hapd->conf->assoc_ap_addr, ETH_ALEN);
+ mgmt->u.assoc_req.capab_info = host_to_le16(0);
+ mgmt->u.assoc_req.listen_interval = host_to_le16(1);
+ p = &mgmt->u.assoc_req.variable[0];
+
+ *p++ = WLAN_EID_SSID;
+ *p++ = hapd->assoc_ap_ssid_len;
+ memcpy(p, hapd->assoc_ap_ssid, hapd->assoc_ap_ssid_len);
+ p += hapd->assoc_ap_ssid_len;
+
+ p = hostapd_eid_supp_rates(hapd, p);
+
+ if (hostapd_send_mgmt_frame(hapd, mgmt, p - (u8 *) mgmt, 0) < 0)
+ perror("ieee802_11_sta_associate: send");
+
+ /* Try to authenticate again, if this attempt fails or times out. */
+ eloop_register_timeout(5, 0, ieee802_11_sta_associate, hapd, NULL);
+}
+
+
+static u16 auth_shared_key(hostapd *hapd, struct sta_info *sta,
+ u16 auth_transaction, u8 *challenge, int iswep)
+{
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG,
+ "authentication (shared key, transaction %d)",
+ auth_transaction);
+
+ if (auth_transaction == 1) {
+ if (!sta->challenge) {
+ /* Generate a pseudo-random challenge */
+ u8 key[8];
+ time_t now;
+ int r;
+ sta->challenge = malloc(WLAN_AUTH_CHALLENGE_LEN);
+ if (!sta->challenge)
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ memset(sta->challenge, 0, WLAN_AUTH_CHALLENGE_LEN);
+
+ now = time(NULL);
+ r = random();
+ memcpy(key, &now, 4);
+ memcpy(key + 4, &r, 4);
+ rc4(sta->challenge, WLAN_AUTH_CHALLENGE_LEN,
+ key, sizeof(key));
+ }
+ return 0;
+ }
+
+ if (auth_transaction != 3)
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+
+ /* Transaction 3 */
+ if (!iswep || !sta->challenge || !challenge ||
+ memcmp(sta->challenge, challenge, WLAN_AUTH_CHALLENGE_LEN)) {
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO,
+ "shared key authentication - invalid "
+ "challenge-response");
+ return WLAN_STATUS_CHALLENGE_FAIL;
+ }
+
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG,
+ "authentication OK (shared key)");
+#ifdef IEEE80211_REQUIRE_AUTH_ACK
+ /* Station will be marked authenticated if it ACKs the
+ * authentication reply. */
+#else
+ sta->flags |= WLAN_STA_AUTH;
+ wpa_sm_event(hapd, sta, WPA_AUTH);
+#endif
+ free(sta->challenge);
+ sta->challenge = NULL;
+
+ return 0;
+}
+
+
+static void send_auth_reply(hostapd *hapd, struct ieee80211_mgmt *mgmt,
+ u16 auth_alg, u16 auth_transaction, u16 resp,
+ u8 *challenge)
+{
+ u8 buf[IEEE80211_HDRLEN + sizeof(mgmt->u.auth) + 2 +
+ WLAN_AUTH_CHALLENGE_LEN];
+ struct ieee80211_mgmt *reply;
+ size_t rlen;
+
+ memset(buf, 0, sizeof(buf));
+ reply = (struct ieee80211_mgmt *) buf;
+ reply->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+ WLAN_FC_STYPE_AUTH);
+ /* Request TX callback */
+ reply->frame_control |= host_to_le16(BIT(1));
+ memcpy(reply->da, mgmt->sa, ETH_ALEN);
+ memcpy(reply->sa, hapd->own_addr, ETH_ALEN);
+ memcpy(reply->bssid, mgmt->bssid, ETH_ALEN);
+
+ reply->u.auth.auth_alg = host_to_le16(auth_alg);
+ reply->u.auth.auth_transaction = host_to_le16(auth_transaction);
+ reply->u.auth.status_code = host_to_le16(resp);
+ rlen = IEEE80211_HDRLEN + sizeof(reply->u.auth);
+ if (auth_alg == WLAN_AUTH_SHARED_KEY && auth_transaction == 2 &&
+ challenge) {
+ u8 *p = reply->u.auth.variable;
+ *p++ = WLAN_EID_CHALLENGE;
+ *p++ = WLAN_AUTH_CHALLENGE_LEN;
+ memcpy(p, challenge, WLAN_AUTH_CHALLENGE_LEN);
+ rlen += 2 + WLAN_AUTH_CHALLENGE_LEN;
+ } else
+ challenge = NULL;
+
+ HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL,
+ "authentication reply: STA=" MACSTR " auth_alg=%d "
+ "auth_transaction=%d resp=%d%s\n",
+ MAC2STR(mgmt->sa), auth_alg, auth_transaction,
+ resp, challenge ? " challenge" : "");
+ if (hostapd_send_mgmt_frame(hapd, reply, rlen, 0) < 0)
+ perror("send_auth_reply: send");
+}
+
+
+static void handle_auth(hostapd *hapd, struct ieee80211_mgmt *mgmt, size_t len)
+{
+ u16 auth_alg, auth_transaction, status_code;
+ u16 resp = WLAN_STATUS_SUCCESS;
+ struct sta_info *sta = NULL;
+ int res;
+ u16 fc;
+ u8 *challenge = NULL;
+ u32 session_timeout, acct_interim_interval;
+
+ if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth)) {
+ printf("handle_auth - too short payload (len=%lu)\n",
+ (unsigned long) len);
+ return;
+ }
+
+ auth_alg = le_to_host16(mgmt->u.auth.auth_alg);
+ auth_transaction = le_to_host16(mgmt->u.auth.auth_transaction);
+ status_code = le_to_host16(mgmt->u.auth.status_code);
+ fc = le_to_host16(mgmt->frame_control);
+
+ if (len >= IEEE80211_HDRLEN + sizeof(mgmt->u.auth) +
+ 2 + WLAN_AUTH_CHALLENGE_LEN &&
+ mgmt->u.auth.variable[0] == WLAN_EID_CHALLENGE &&
+ mgmt->u.auth.variable[1] == WLAN_AUTH_CHALLENGE_LEN)
+ challenge = &mgmt->u.auth.variable[2];
+
+ HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL,
+ "authentication: STA=" MACSTR " auth_alg=%d "
+ "auth_transaction=%d status_code=%d wep=%d%s\n",
+ MAC2STR(mgmt->sa), auth_alg, auth_transaction,
+ status_code, !!(fc & WLAN_FC_ISWEP),
+ challenge ? " challenge" : "");
+
+ if (hapd->assoc_ap_state == AUTHENTICATE && auth_transaction == 2 &&
+ memcmp(mgmt->sa, hapd->conf->assoc_ap_addr, ETH_ALEN) == 0 &&
+ memcmp(mgmt->bssid, hapd->conf->assoc_ap_addr, ETH_ALEN) == 0) {
+ if (status_code != 0) {
+ printf("Authentication (as station) with AP "
+ MACSTR " failed (status_code=%d)\n",
+ MAC2STR(hapd->conf->assoc_ap_addr),
+ status_code);
+ return;
+ }
+ printf("Authenticated (as station) with AP " MACSTR "\n",
+ MAC2STR(hapd->conf->assoc_ap_addr));
+ ieee802_11_sta_associate(hapd, NULL);
+ return;
+ }
+
+ if (hapd->tkip_countermeasures) {
+ resp = WLAN_REASON_MICHAEL_MIC_FAILURE;
+ goto fail;
+ }
+
+ if (!(((hapd->conf->auth_algs & HOSTAPD_AUTH_OPEN) &&
+ auth_alg == WLAN_AUTH_OPEN) ||
+ ((hapd->conf->auth_algs & HOSTAPD_AUTH_SHARED_KEY) &&
+ auth_alg == WLAN_AUTH_SHARED_KEY))) {
+ printf("Unsupported authentication algorithm (%d)\n",
+ auth_alg);
+ resp = WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG;
+ goto fail;
+ }
+
+ if (!(auth_transaction == 1 ||
+ (auth_alg == WLAN_AUTH_SHARED_KEY && auth_transaction == 3))) {
+ printf("Unknown authentication transaction number (%d)\n",
+ auth_transaction);
+ resp = WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION;
+ goto fail;
+ }
+
+ if (memcmp(mgmt->sa, hapd->own_addr, ETH_ALEN) == 0) {
+ printf("Station " MACSTR " not allowed to authenticate.\n",
+ MAC2STR(mgmt->sa));
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto fail;
+ }
+
+ res = hostapd_allowed_address(hapd, mgmt->sa, (u8 *) mgmt, len,
+ &session_timeout,
+ &acct_interim_interval);
+ if (res == HOSTAPD_ACL_REJECT) {
+ printf("Station " MACSTR " not allowed to authenticate.\n",
+ MAC2STR(mgmt->sa));
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto fail;
+ }
+ if (res == HOSTAPD_ACL_PENDING) {
+ HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "Authentication frame "
+ "from " MACSTR " waiting for an external "
+ "authentication\n", MAC2STR(mgmt->sa));
+ /* Authentication code will re-send the authentication frame
+ * after it has received (and cached) information from the
+ * external source. */
+ return;
+ }
+
+ sta = ap_sta_add(hapd, mgmt->sa);
+ if (!sta) {
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto fail;
+ }
+ sta->flags &= ~WLAN_STA_PREAUTH;
+ ieee802_1x_notify_pre_auth(sta->eapol_sm, 0);
+
+ if (hapd->conf->radius_acct_interim_interval == 0 &&
+ acct_interim_interval)
+ sta->acct_interim_interval = acct_interim_interval;
+ if (res == HOSTAPD_ACL_ACCEPT_TIMEOUT)
+ ap_sta_session_timeout(hapd, sta, session_timeout);
+ else
+ ap_sta_no_session_timeout(hapd, sta);
+
+ switch (auth_alg) {
+ case WLAN_AUTH_OPEN:
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG,
+ "authentication OK (open system)");
+#ifdef IEEE80211_REQUIRE_AUTH_ACK
+ /* Station will be marked authenticated if it ACKs the
+ * authentication reply. */
+#else
+ sta->flags |= WLAN_STA_AUTH;
+ wpa_sm_event(hapd, sta, WPA_AUTH);
+#endif
+ break;
+ case WLAN_AUTH_SHARED_KEY:
+ resp = auth_shared_key(hapd, sta, auth_transaction, challenge,
+ fc & WLAN_FC_ISWEP);
+ break;
+ }
+
+ fail:
+ send_auth_reply(hapd, mgmt, auth_alg, auth_transaction + 1, resp,
+ sta ? sta->challenge : NULL);
+}
+
+
+static void handle_assoc(hostapd *hapd, struct ieee80211_mgmt *mgmt,
+ size_t len, int reassoc)
+{
+ u16 capab_info, listen_interval;
+ u16 resp = WLAN_STATUS_SUCCESS;
+ u8 *pos, *wpa_ie;
+ size_t wpa_ie_len;
+ int send_deauth = 0, send_len, left, i;
+ struct sta_info *sta;
+ struct ieee802_11_elems elems;
+
+ if (len < IEEE80211_HDRLEN + (reassoc ? sizeof(mgmt->u.reassoc_req) :
+ sizeof(mgmt->u.assoc_req))) {
+ printf("handle_assoc(reassoc=%d) - too short payload (len=%lu)"
+ "\n", reassoc, (unsigned long) len);
+ return;
+ }
+
+ if (reassoc) {
+ capab_info = le_to_host16(mgmt->u.reassoc_req.capab_info);
+ listen_interval = le_to_host16(
+ mgmt->u.reassoc_req.listen_interval);
+ HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL,
+ "reassociation request: STA=" MACSTR
+ " capab_info=0x%02x "
+ "listen_interval=%d current_ap=" MACSTR "\n",
+ MAC2STR(mgmt->sa), capab_info, listen_interval,
+ MAC2STR(mgmt->u.reassoc_req.current_ap));
+ left = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.reassoc_req));
+ pos = mgmt->u.reassoc_req.variable;
+ } else {
+ capab_info = le_to_host16(mgmt->u.assoc_req.capab_info);
+ listen_interval = le_to_host16(
+ mgmt->u.assoc_req.listen_interval);
+ HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL,
+ "association request: STA=" MACSTR
+ " capab_info=0x%02x listen_interval=%d\n",
+ MAC2STR(mgmt->sa), capab_info, listen_interval);
+ left = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.assoc_req));
+ pos = mgmt->u.assoc_req.variable;
+ }
+
+ sta = ap_get_sta(hapd, mgmt->sa);
+ if (sta == NULL || (sta->flags & WLAN_STA_AUTH) == 0) {
+ printf("STA " MACSTR " trying to associate before "
+ "authentication\n", MAC2STR(mgmt->sa));
+ if (sta) {
+ printf(" sta: addr=" MACSTR " aid=%d flags=0x%04x\n",
+ MAC2STR(sta->addr), sta->aid, sta->flags);
+ }
+ send_deauth = 1;
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto fail;
+ }
+
+ if (hapd->tkip_countermeasures) {
+ resp = WLAN_REASON_MICHAEL_MIC_FAILURE;
+ goto fail;
+ }
+
+ sta->capability = capab_info;
+
+ /* followed by SSID and Supported rates */
+ if (ieee802_11_parse_elems(hapd, pos, left, &elems, 1) == ParseFailed
+ || !elems.ssid) {
+ printf("STA " MACSTR " sent invalid association request\n",
+ MAC2STR(sta->addr));
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto fail;
+ }
+
+ if (elems.ssid_len != hapd->conf->ssid_len ||
+ memcmp(elems.ssid, hapd->conf->ssid, elems.ssid_len) != 0) {
+ printf("Station " MACSTR " tried to associate with "
+ "unknown SSID '", MAC2STR(sta->addr));
+ ieee802_11_print_ssid(elems.ssid, elems.ssid_len);
+ printf("'\n");
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto fail;
+ }
+
+ if (elems.supp_rates) {
+ if (elems.supp_rates_len > sizeof(sta->supported_rates)) {
+ printf("STA " MACSTR ": Invalid supported rates "
+ "element length %d\n", MAC2STR(sta->addr),
+ elems.supp_rates_len);
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto fail;
+ }
+
+ memset(sta->supported_rates, 0, sizeof(sta->supported_rates));
+ memcpy(sta->supported_rates, elems.supp_rates,
+ elems.supp_rates_len);
+
+ sta->tx_supp_rates = 0;
+ for (i = 0; i < elems.supp_rates_len; i++) {
+ if ((sta->supported_rates[i] & 0x7f) == 2)
+ sta->tx_supp_rates |= WLAN_RATE_1M;
+ if ((sta->supported_rates[i] & 0x7f) == 4)
+ sta->tx_supp_rates |= WLAN_RATE_2M;
+ if ((sta->supported_rates[i] & 0x7f) == 11)
+ sta->tx_supp_rates |= WLAN_RATE_5M5;
+ if ((sta->supported_rates[i] & 0x7f) == 22)
+ sta->tx_supp_rates |= WLAN_RATE_11M;
+ }
+ } else
+ sta->tx_supp_rates = 0xff;
+
+ if ((hapd->conf->wpa & HOSTAPD_WPA_VERSION_WPA2) && elems.rsn_ie) {
+ wpa_ie = elems.rsn_ie;
+ wpa_ie_len = elems.rsn_ie_len;
+ } else if ((hapd->conf->wpa & HOSTAPD_WPA_VERSION_WPA) &&
+ elems.wpa_ie) {
+ wpa_ie = elems.wpa_ie;
+ wpa_ie_len = elems.wpa_ie_len;
+ } else {
+ wpa_ie = NULL;
+ wpa_ie_len = 0;
+ }
+ if (hapd->conf->wpa && wpa_ie == NULL) {
+ printf("STA " MACSTR ": No WPA/RSN IE in association "
+ "request\n", MAC2STR(sta->addr));
+ resp = WLAN_STATUS_INVALID_IE;
+ goto fail;
+ }
+
+ if (hapd->conf->wpa) {
+ int res;
+ wpa_ie -= 2;
+ wpa_ie_len += 2;
+ res = wpa_validate_wpa_ie(hapd, sta, wpa_ie, wpa_ie_len,
+ elems.rsn_ie ?
+ HOSTAPD_WPA_VERSION_WPA2 :
+ HOSTAPD_WPA_VERSION_WPA);
+ if (res == WPA_INVALID_GROUP)
+ resp = WLAN_STATUS_GROUP_CIPHER_NOT_VALID;
+ else if (res == WPA_INVALID_PAIRWISE)
+ resp = WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID;
+ else if (res == WPA_INVALID_AKMP)
+ resp = WLAN_STATUS_AKMP_NOT_VALID;
+ else if (res != WPA_IE_OK)
+ resp = WLAN_STATUS_INVALID_IE;
+ if (resp != WLAN_STATUS_SUCCESS)
+ goto fail;
+ free(sta->wpa_ie);
+ sta->wpa_ie = malloc(wpa_ie_len);
+ if (sta->wpa_ie == NULL) {
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto fail;
+ }
+ sta->wpa_ie_len = wpa_ie_len;
+ memcpy(sta->wpa_ie, wpa_ie, wpa_ie_len);
+ }
+
+ /* get a unique AID */
+ if (sta->aid > 0) {
+ HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL,
+ " old AID %d\n", sta->aid);
+ } else {
+ for (sta->aid = 1; sta->aid <= MAX_AID_TABLE_SIZE; sta->aid++)
+ if (hapd->sta_aid[sta->aid - 1] == NULL)
+ break;
+ if (sta->aid > MAX_AID_TABLE_SIZE) {
+ sta->aid = 0;
+ resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
+ printf(" no room for more AIDs\n");
+ goto fail;
+ } else {
+ hapd->sta_aid[sta->aid - 1] = sta;
+ HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL,
+ " new AID %d\n", sta->aid);
+ }
+ }
+
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG,
+ "association OK (aid %d)", sta->aid);
+ /* Station will be marked associated, after it acknowledges AssocResp
+ */
+
+ if (sta->last_assoc_req)
+ free(sta->last_assoc_req);
+ sta->last_assoc_req = (struct ieee80211_mgmt *) malloc(len);
+ if (sta->last_assoc_req)
+ memcpy(sta->last_assoc_req, mgmt, len);
+
+ /* Make sure that the previously registered inactivity timer will not
+ * remove the STA immediately. */
+ sta->timeout_next = STA_NULLFUNC;
+
+ fail:
+
+ /* use the queued buffer for transmission because it is large enough
+ * and not needed anymore */
+ mgmt->frame_control =
+ IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+ (send_deauth ? WLAN_FC_STYPE_DEAUTH :
+ (reassoc ? WLAN_FC_STYPE_REASSOC_RESP :
+ WLAN_FC_STYPE_ASSOC_RESP)));
+ memcpy(mgmt->da, mgmt->sa, ETH_ALEN);
+ memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN);
+ /* Addr3 = BSSID - already set */
+
+ send_len = IEEE80211_HDRLEN;
+ if (send_deauth) {
+ send_len += sizeof(mgmt->u.deauth);
+ mgmt->u.deauth.reason_code = host_to_le16(resp);
+ } else {
+ u8 *p;
+ send_len += sizeof(mgmt->u.assoc_resp);
+ mgmt->u.assoc_resp.capab_info =
+ host_to_le16(hostapd_own_capab_info(hapd));
+ mgmt->u.assoc_resp.status_code = host_to_le16(resp);
+ mgmt->u.assoc_resp.aid = host_to_le16((sta ? sta->aid : 0)
+ | BIT(14) | BIT(15));
+ /* Supported rates */
+ p = hostapd_eid_supp_rates(hapd, mgmt->u.assoc_resp.variable);
+ send_len += p - mgmt->u.assoc_resp.variable;
+
+ /* Request TX callback */
+ mgmt->frame_control |= host_to_le16(BIT(1));
+ }
+
+ if (hostapd_send_mgmt_frame(hapd, mgmt, send_len, 0) < 0)
+ perror("handle_assoc: send");
+}
+
+
+static void handle_assoc_resp(hostapd *hapd, struct ieee80211_mgmt *mgmt,
+ size_t len)
+{
+ u16 status_code, aid;
+
+ if (hapd->assoc_ap_state != ASSOCIATE) {
+ printf("Unexpected association response received from " MACSTR
+ "\n", MAC2STR(mgmt->sa));
+ return;
+ }
+
+ if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.assoc_resp)) {
+ printf("handle_assoc_resp - too short payload (len=%lu)\n",
+ (unsigned long) len);
+ return;
+ }
+
+ if (memcmp(mgmt->sa, hapd->conf->assoc_ap_addr, ETH_ALEN) != 0 ||
+ memcmp(mgmt->bssid, hapd->conf->assoc_ap_addr, ETH_ALEN) != 0) {
+ printf("Received association response from unexpected address "
+ "(SA=" MACSTR " BSSID=" MACSTR "\n",
+ MAC2STR(mgmt->sa), MAC2STR(mgmt->bssid));
+ return;
+ }
+
+ status_code = le_to_host16(mgmt->u.assoc_resp.status_code);
+ aid = le_to_host16(mgmt->u.assoc_resp.aid);
+ aid &= ~(BIT(14) | BIT(15));
+
+ if (status_code != 0) {
+ printf("Association (as station) with AP " MACSTR " failed "
+ "(status_code=%d)\n",
+ MAC2STR(hapd->conf->assoc_ap_addr), status_code);
+ /* Try to authenticate again */
+ hapd->assoc_ap_state = AUTHENTICATE;
+ eloop_register_timeout(5, 0, ieee802_11_sta_authenticate,
+ hapd, NULL);
+ }
+
+ printf("Associated (as station) with AP " MACSTR " (aid=%d)\n",
+ MAC2STR(hapd->conf->assoc_ap_addr), aid);
+ hapd->assoc_ap_aid = aid;
+ hapd->assoc_ap_state = ASSOCIATED;
+
+ if (hostapd_set_assoc_ap(hapd, hapd->conf->assoc_ap_addr)) {
+ printf("Could not set associated AP address to kernel "
+ "driver.\n");
+ }
+}
+
+
+static void handle_disassoc(hostapd *hapd, struct ieee80211_mgmt *mgmt,
+ size_t len)
+{
+ struct sta_info *sta;
+
+ if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.disassoc)) {
+ printf("handle_disassoc - too short payload (len=%lu)\n",
+ (unsigned long) len);
+ return;
+ }
+
+ HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL,
+ "disassocation: STA=" MACSTR " reason_code=%d\n",
+ MAC2STR(mgmt->sa),
+ le_to_host16(mgmt->u.disassoc.reason_code));
+
+ if (hapd->assoc_ap_state != DO_NOT_ASSOC &&
+ memcmp(mgmt->sa, hapd->conf->assoc_ap_addr, ETH_ALEN) == 0) {
+ printf("Assoc AP " MACSTR " sent disassociation "
+ "(reason_code=%d) - try to authenticate\n",
+ MAC2STR(hapd->conf->assoc_ap_addr),
+ le_to_host16(mgmt->u.disassoc.reason_code));
+ hapd->assoc_ap_state = AUTHENTICATE;
+ ieee802_11_sta_authenticate(hapd, NULL);
+ eloop_register_timeout(0, 500000, ieee802_11_sta_authenticate,
+ hapd, NULL);
+ return;
+ }
+
+ sta = ap_get_sta(hapd, mgmt->sa);
+ if (sta == NULL) {
+ printf("Station " MACSTR " trying to disassociate, but it "
+ "is not associated.\n", MAC2STR(mgmt->sa));
+ return;
+ }
+
+ sta->flags &= ~WLAN_STA_ASSOC;
+ wpa_sm_event(hapd, sta, WPA_DISASSOC);
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO, "disassociated");
+ sta->acct_terminate_cause = RADIUS_ACCT_TERMINATE_CAUSE_USER_REQUEST;
+ ieee802_1x_set_port_enabled(hapd, sta, 0);
+ /* Stop Accounting and IEEE 802.1X sessions, but leave the STA
+ * authenticated. */
+ accounting_sta_stop(hapd, sta);
+ ieee802_1x_free_station(sta);
+ hostapd_sta_remove(hapd, sta->addr);
+
+ if (sta->timeout_next == STA_NULLFUNC ||
+ sta->timeout_next == STA_DISASSOC) {
+ sta->timeout_next = STA_DEAUTH;
+ eloop_cancel_timeout(ap_handle_timer, hapd, sta);
+ eloop_register_timeout(AP_DEAUTH_DELAY, 0, ap_handle_timer,
+ hapd, sta);
+ }
+}
+
+
+static void handle_deauth(hostapd *hapd, struct ieee80211_mgmt *mgmt,
+ size_t len)
+{
+ struct sta_info *sta;
+
+ if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.deauth)) {
+ printf("handle_deauth - too short payload (len=%lu)\n",
+ (unsigned long) len);
+ return;
+ }
+
+ HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL,
+ "deauthentication: STA=" MACSTR " reason_code=%d\n",
+ MAC2STR(mgmt->sa),
+ le_to_host16(mgmt->u.deauth.reason_code));
+
+ if (hapd->assoc_ap_state != DO_NOT_ASSOC &&
+ memcmp(mgmt->sa, hapd->conf->assoc_ap_addr, ETH_ALEN) == 0) {
+ printf("Assoc AP " MACSTR " sent deauthentication "
+ "(reason_code=%d) - try to authenticate\n",
+ MAC2STR(hapd->conf->assoc_ap_addr),
+ le_to_host16(mgmt->u.deauth.reason_code));
+ hapd->assoc_ap_state = AUTHENTICATE;
+ eloop_register_timeout(0, 500000, ieee802_11_sta_authenticate,
+ hapd, NULL);
+ return;
+ }
+
+ sta = ap_get_sta(hapd, mgmt->sa);
+ if (sta == NULL) {
+ printf("Station " MACSTR " trying to deauthenticate, but it "
+ "is not authenticated.\n", MAC2STR(mgmt->sa));
+ return;
+ }
+
+ sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC);
+ wpa_sm_event(hapd, sta, WPA_DEAUTH);
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG, "deauthenticated");
+ sta->acct_terminate_cause = RADIUS_ACCT_TERMINATE_CAUSE_USER_REQUEST;
+ ieee802_1x_set_port_enabled(hapd, sta, 0);
+ ap_free_sta(hapd, sta);
+}
+
+
+static void handle_beacon(hostapd *hapd, struct ieee80211_mgmt *mgmt,
+ size_t len)
+{
+ struct ieee802_11_elems elems;
+
+ if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.beacon)) {
+ printf("handle_beacon - too short payload (len=%lu)\n",
+ (unsigned long) len);
+ return;
+ }
+
+ (void) ieee802_11_parse_elems(hapd, mgmt->u.beacon.variable,
+ len - (IEEE80211_HDRLEN +
+ sizeof(mgmt->u.beacon)), &elems,
+ 0);
+
+ if (hapd->assoc_ap_state == WAIT_BEACON &&
+ memcmp(mgmt->sa, hapd->conf->assoc_ap_addr, ETH_ALEN) == 0) {
+ if (elems.ssid && elems.ssid_len <= 32) {
+ memcpy(hapd->assoc_ap_ssid, elems.ssid,
+ elems.ssid_len);
+ hapd->assoc_ap_ssid[elems.ssid_len] = '\0';
+ hapd->assoc_ap_ssid_len = elems.ssid_len;
+ }
+ ieee802_11_sta_authenticate(hapd, NULL);
+ }
+
+ if (!HOSTAPD_DEBUG_COND(HOSTAPD_DEBUG_EXCESSIVE))
+ return;
+
+ printf("Beacon from " MACSTR, MAC2STR(mgmt->sa));
+ if (elems.ssid) {
+ printf(" SSID='");
+ ieee802_11_print_ssid(elems.ssid, elems.ssid_len);
+ printf("'");
+ }
+ if (elems.ds_params && elems.ds_params_len == 1)
+ printf(" CHAN=%d", elems.ds_params[0]);
+ printf("\n");
+}
+
+
+void ieee802_11_mgmt(struct hostapd_data *hapd, u8 *buf, size_t len, u16 stype)
+{
+ struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *) buf;
+
+ if (stype == WLAN_FC_STYPE_BEACON) {
+ HOSTAPD_DEBUG(HOSTAPD_DEBUG_EXCESSIVE, "mgmt::beacon\n");
+ handle_beacon(hapd, mgmt, len);
+ return;
+ }
+
+ if (memcmp(mgmt->bssid, hapd->own_addr, ETH_ALEN) != 0 &&
+ (hapd->assoc_ap_state == DO_NOT_ASSOC ||
+ memcmp(mgmt->bssid, hapd->conf->assoc_ap_addr, ETH_ALEN) != 0)) {
+ printf("MGMT: BSSID=" MACSTR " not our address\n",
+ MAC2STR(mgmt->bssid));
+ return;
+ }
+
+
+ if (stype == WLAN_FC_STYPE_PROBE_REQ) {
+ printf("mgmt::probe_req\n");
+ return;
+ }
+
+ if (memcmp(mgmt->da, hapd->own_addr, ETH_ALEN) != 0) {
+ printf("MGMT: DA=" MACSTR " not our address\n",
+ MAC2STR(mgmt->da));
+ return;
+ }
+
+ switch (stype) {
+ case WLAN_FC_STYPE_AUTH:
+ HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "mgmt::auth\n");
+ handle_auth(hapd, mgmt, len);
+ break;
+ case WLAN_FC_STYPE_ASSOC_REQ:
+ HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "mgmt::assoc_req\n");
+ handle_assoc(hapd, mgmt, len, 0);
+ break;
+ case WLAN_FC_STYPE_ASSOC_RESP:
+ HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "mgmt::assoc_resp\n");
+ handle_assoc_resp(hapd, mgmt, len);
+ break;
+ case WLAN_FC_STYPE_REASSOC_REQ:
+ HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "mgmt::reassoc_req\n");
+ handle_assoc(hapd, mgmt, len, 1);
+ break;
+ case WLAN_FC_STYPE_DISASSOC:
+ HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "mgmt::disassoc\n");
+ handle_disassoc(hapd, mgmt, len);
+ break;
+ case WLAN_FC_STYPE_DEAUTH:
+ HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "mgmt::deauth\n");
+ handle_deauth(hapd, mgmt, len);
+ break;
+ default:
+ printf("unknown mgmt frame subtype %d\n", stype);
+ break;
+ }
+}
+
+
+static void handle_auth_cb(hostapd *hapd, struct ieee80211_mgmt *mgmt,
+ size_t len, int ok)
+{
+ u16 auth_alg, auth_transaction, status_code;
+ struct sta_info *sta;
+
+ if (!ok) {
+ hostapd_logger(hapd, mgmt->da, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_NOTICE,
+ "did not acknowledge authentication response");
+ return;
+ }
+
+ if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth)) {
+ printf("handle_auth_cb - too short payload (len=%lu)\n",
+ (unsigned long) len);
+ return;
+ }
+
+ auth_alg = le_to_host16(mgmt->u.auth.auth_alg);
+ auth_transaction = le_to_host16(mgmt->u.auth.auth_transaction);
+ status_code = le_to_host16(mgmt->u.auth.status_code);
+
+ sta = ap_get_sta(hapd, mgmt->da);
+ if (!sta) {
+ printf("handle_auth_cb: STA " MACSTR " not found\n",
+ MAC2STR(mgmt->da));
+ return;
+ }
+
+ if (status_code == WLAN_STATUS_SUCCESS &&
+ ((auth_alg == WLAN_AUTH_OPEN && auth_transaction == 2) ||
+ (auth_alg == WLAN_AUTH_SHARED_KEY && auth_transaction == 4))) {
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO, "authenticated");
+ sta->flags |= WLAN_STA_AUTH;
+ }
+}
+
+
+static void handle_assoc_cb(hostapd *hapd, struct ieee80211_mgmt *mgmt,
+ size_t len, int reassoc, int ok)
+{
+ u16 status;
+ struct sta_info *sta;
+ int new_assoc = 1;
+
+ if (!ok) {
+ hostapd_logger(hapd, mgmt->da, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG,
+ "did not acknowledge association response");
+ return;
+ }
+
+ if (len < IEEE80211_HDRLEN + (reassoc ? sizeof(mgmt->u.reassoc_req) :
+ sizeof(mgmt->u.assoc_req))) {
+ printf("handle_assoc_cb(reassoc=%d) - too short payload "
+ "(len=%lu)\n", reassoc, (unsigned long) len);
+ return;
+ }
+
+ if (reassoc)
+ status = le_to_host16(mgmt->u.reassoc_resp.status_code);
+ else
+ status = le_to_host16(mgmt->u.assoc_resp.status_code);
+
+ sta = ap_get_sta(hapd, mgmt->da);
+ if (!sta) {
+ printf("handle_assoc_cb: STA " MACSTR " not found\n",
+ MAC2STR(mgmt->da));
+ return;
+ }
+
+ if (status != WLAN_STATUS_SUCCESS)
+ goto fail;
+
+ /* Stop previous accounting session, if one is started, and allocate
+ * new session id for the new session. */
+ accounting_sta_stop(hapd, sta);
+ accounting_sta_get_id(hapd, sta);
+
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO,
+ "associated (aid %d, accounting session %08X-%08X)",
+ sta->aid, sta->acct_session_id_hi,
+ sta->acct_session_id_lo);
+
+ if (sta->flags & WLAN_STA_ASSOC)
+ new_assoc = 0;
+ sta->flags |= WLAN_STA_ASSOC;
+
+ if (hostapd_sta_add(hapd, sta->addr, sta->aid, sta->capability,
+ sta->tx_supp_rates)) {
+ printf("Could not add station to kernel driver.\n");
+ }
+
+ wpa_sm_event(hapd, sta, WPA_ASSOC);
+ if (new_assoc)
+ hostapd_new_assoc_sta(hapd, sta);
+ else
+ wpa_sm_event(hapd, sta, WPA_REAUTH);
+
+ ieee802_1x_notify_port_enabled(sta->eapol_sm, 1);
+
+ fail:
+ /* Copy of the association request is not needed anymore */
+ if (sta->last_assoc_req) {
+ free(sta->last_assoc_req);
+ sta->last_assoc_req = NULL;
+ }
+}
+
+
+void ieee802_11_mgmt_cb(struct hostapd_data *hapd, u8 *buf, size_t len,
+ u16 stype, int ok)
+{
+ struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *) buf;
+
+ switch (stype) {
+ case WLAN_FC_STYPE_AUTH:
+ HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "mgmt::auth cb\n");
+ handle_auth_cb(hapd, mgmt, len, ok);
+ break;
+ case WLAN_FC_STYPE_ASSOC_RESP:
+ HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL,
+ "mgmt::assoc_resp cb\n");
+ handle_assoc_cb(hapd, mgmt, len, 0, ok);
+ break;
+ case WLAN_FC_STYPE_REASSOC_RESP:
+ HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL,
+ "mgmt::reassoc_resp cb\n");
+ handle_assoc_cb(hapd, mgmt, len, 1, ok);
+ break;
+ default:
+ printf("unknown mgmt cb frame subtype %d\n", stype);
+ break;
+ }
+}
+
+
+static void ieee80211_tkip_countermeasures_stop(void *eloop_ctx,
+ void *timeout_ctx)
+{
+ struct hostapd_data *hapd = eloop_ctx;
+ hapd->tkip_countermeasures = 0;
+ hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO, "TKIP countermeasures ended");
+}
+
+
+static void ieee80211_tkip_countermeasures_start(struct hostapd_data *hapd)
+{
+ struct sta_info *sta;
+
+ hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO, "TKIP countermeasures initiated");
+
+ if (hapd->wpa_auth)
+ hapd->wpa_auth->dot11RSNATKIPCounterMeasuresInvoked++;
+ hapd->tkip_countermeasures = 1;
+ wpa_gtk_rekey(hapd);
+ eloop_cancel_timeout(ieee80211_tkip_countermeasures_stop, hapd, NULL);
+ eloop_register_timeout(60, 0, ieee80211_tkip_countermeasures_stop,
+ hapd, NULL);
+ for (sta = hapd->sta_list; sta != NULL; sta = sta->next) {
+ hostapd_sta_deauth(hapd, sta->addr,
+ WLAN_REASON_MICHAEL_MIC_FAILURE);
+ sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC |
+ WLAN_STA_AUTHORIZED);
+ hostapd_sta_remove(hapd, sta->addr);
+ }
+}
+
+
+void ieee80211_michael_mic_failure(struct hostapd_data *hapd, u8 *addr,
+ int local)
+{
+ time_t now;
+
+ if (addr && local) {
+ struct sta_info *sta = ap_get_sta(hapd, addr);
+ if (sta != NULL) {
+ sta->dot11RSNAStatsTKIPLocalMICFailures++;
+ hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO,
+ "Michael MIC failure detected in "
+ "received frame");
+ } else {
+ HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL,
+ "MLME-MICHAELMICFAILURE.indication "
+ "for not associated STA (" MACSTR
+ ") ignored\n", MAC2STR(addr));
+ return;
+ }
+ }
+
+ time(&now);
+ if (now > hapd->michael_mic_failure + 60) {
+ hapd->michael_mic_failures = 1;
+ } else {
+ hapd->michael_mic_failures++;
+ if (hapd->michael_mic_failures > 1)
+ ieee80211_tkip_countermeasures_start(hapd);
+ }
+ hapd->michael_mic_failure = now;
+}
+
+
+int ieee802_11_get_mib(struct hostapd_data *hapd, char *buf, size_t buflen)
+{
+ /* TODO */
+ return 0;
+}
+
+
+int ieee802_11_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta,
+ char *buf, size_t buflen)
+{
+ /* TODO */
+ return 0;
+}
diff --git a/contrib/hostapd/ieee802_11.h b/contrib/hostapd/ieee802_11.h
new file mode 100644
index 000000000000..39cc34207b4a
--- /dev/null
+++ b/contrib/hostapd/ieee802_11.h
@@ -0,0 +1,99 @@
+#ifndef IEEE802_11_H
+#define IEEE802_11_H
+
+
+struct ieee80211_mgmt {
+ u16 frame_control;
+ u16 duration;
+ u8 da[6];
+ u8 sa[6];
+ u8 bssid[6];
+ u16 seq_ctrl;
+ union {
+ struct {
+ u16 auth_alg;
+ u16 auth_transaction;
+ u16 status_code;
+ /* possibly followed by Challenge text */
+ u8 variable[0];
+ } __attribute__ ((packed)) auth;
+ struct {
+ u16 reason_code;
+ } __attribute__ ((packed)) deauth;
+ struct {
+ u16 capab_info;
+ u16 listen_interval;
+ /* followed by SSID and Supported rates */
+ u8 variable[0];
+ } __attribute__ ((packed)) assoc_req;
+ struct {
+ u16 capab_info;
+ u16 status_code;
+ u16 aid;
+ /* followed by Supported rates */
+ u8 variable[0];
+ } __attribute__ ((packed)) assoc_resp, reassoc_resp;
+ struct {
+ u16 capab_info;
+ u16 listen_interval;
+ u8 current_ap[6];
+ /* followed by SSID and Supported rates */
+ u8 variable[0];
+ } __attribute__ ((packed)) reassoc_req;
+ struct {
+ u16 reason_code;
+ } __attribute__ ((packed)) disassoc;
+ struct {
+ u8 timestamp[8];
+ u16 beacon_int;
+ u16 capab_info;
+ /* followed by some of SSID, Supported rates,
+ * FH Params, DS Params, CF Params, IBSS Params, TIM */
+ u8 variable[0];
+ } __attribute__ ((packed)) beacon;
+ } u;
+} __attribute__ ((packed));
+
+
+/* Parsed Information Elements */
+struct ieee802_11_elems {
+ u8 *ssid;
+ u8 ssid_len;
+ u8 *supp_rates;
+ u8 supp_rates_len;
+ u8 *fh_params;
+ u8 fh_params_len;
+ u8 *ds_params;
+ u8 ds_params_len;
+ u8 *cf_params;
+ u8 cf_params_len;
+ u8 *tim;
+ u8 tim_len;
+ u8 *ibss_params;
+ u8 ibss_params_len;
+ u8 *challenge;
+ u8 challenge_len;
+ u8 *wpa_ie;
+ u8 wpa_ie_len;
+ u8 *rsn_ie;
+ u8 rsn_ie_len;
+};
+
+typedef enum { ParseOK = 0, ParseUnknown = 1, ParseFailed = -1 } ParseRes;
+
+
+void ieee802_11_mgmt(struct hostapd_data *hapd, u8 *buf, size_t len,
+ u16 stype);
+void ieee802_11_mgmt_cb(struct hostapd_data *hapd, u8 *buf, size_t len,
+ u16 stype, int ok);
+ParseRes ieee802_11_parse_elems(struct hostapd_data *hapd, u8 *start,
+ size_t len,
+ struct ieee802_11_elems *elems,
+ int show_errors);
+void ieee80211_michael_mic_failure(struct hostapd_data *hapd, u8 *addr,
+ int local);
+int ieee802_11_get_mib(struct hostapd_data *hapd, char *buf, size_t buflen);
+int ieee802_11_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta,
+ char *buf, size_t buflen);
+
+#endif /* IEEE802_11_H */
diff --git a/contrib/hostapd/ieee802_11_auth.c b/contrib/hostapd/ieee802_11_auth.c
new file mode 100644
index 000000000000..8dbcee188dfd
--- /dev/null
+++ b/contrib/hostapd/ieee802_11_auth.c
@@ -0,0 +1,447 @@
+/*
+ * Host AP (software wireless LAN access point) user space daemon for
+ * Host AP kernel driver / IEEE 802.11 authentication (ACL)
+ * 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 <time.h>
+#include <arpa/inet.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include "hostapd.h"
+#include "ieee802_11.h"
+#include "ieee802_11_auth.h"
+#include "radius.h"
+#include "radius_client.h"
+#include "eloop.h"
+
+#define RADIUS_ACL_TIMEOUT 30
+
+
+struct hostapd_cached_radius_acl {
+ time_t timestamp;
+ macaddr addr;
+ int accepted; /* HOSTAPD_ACL_* */
+ struct hostapd_cached_radius_acl *next;
+ u32 session_timeout;
+ u32 acct_interim_interval;
+};
+
+
+struct hostapd_acl_query_data {
+ time_t timestamp;
+ u8 radius_id;
+ macaddr addr;
+ u8 *auth_msg; /* IEEE 802.11 authentication frame from station */
+ size_t auth_msg_len;
+ struct hostapd_acl_query_data *next;
+};
+
+
+static void hostapd_acl_cache_free(struct hostapd_cached_radius_acl *acl_cache)
+{
+ struct hostapd_cached_radius_acl *prev;
+
+ while (acl_cache) {
+ prev = acl_cache;
+ acl_cache = acl_cache->next;
+ free(prev);
+ }
+}
+
+
+static int hostapd_acl_cache_get(struct hostapd_data *hapd, u8 *addr,
+ u32 *session_timeout,
+ u32 *acct_interim_interval)
+{
+ struct hostapd_cached_radius_acl *entry;
+ time_t now;
+
+ time(&now);
+ entry = hapd->acl_cache;
+
+ while (entry) {
+ if (memcmp(entry->addr, addr, ETH_ALEN) == 0) {
+ if (now - entry->timestamp > RADIUS_ACL_TIMEOUT)
+ return -1; /* entry has expired */
+ if (entry->accepted == HOSTAPD_ACL_ACCEPT_TIMEOUT)
+ *session_timeout = entry->session_timeout;
+ *acct_interim_interval = entry->acct_interim_interval;
+ return entry->accepted;
+ }
+
+ entry = entry->next;
+ }
+
+ return -1;
+}
+
+
+static void hostapd_acl_query_free(struct hostapd_acl_query_data *query)
+{
+ if (query == NULL)
+ return;
+ free(query->auth_msg);
+ free(query);
+}
+
+
+static int hostapd_radius_acl_query(hostapd *hapd, u8 *addr,
+ struct hostapd_acl_query_data *query)
+{
+ struct radius_msg *msg;
+ char buf[128];
+
+ query->radius_id = radius_client_get_id(hapd->radius);
+ msg = radius_msg_new(RADIUS_CODE_ACCESS_REQUEST, query->radius_id);
+ if (msg == NULL)
+ return -1;
+
+ radius_msg_make_authenticator(msg, addr, ETH_ALEN);
+
+ snprintf(buf, sizeof(buf), RADIUS_ADDR_FORMAT, MAC2STR(addr));
+ if (!radius_msg_add_attr(msg, RADIUS_ATTR_USER_NAME, (u8 *) buf,
+ strlen(buf))) {
+ printf("Could not add User-Name\n");
+ goto fail;
+ }
+
+ if (!radius_msg_add_attr_user_password(
+ msg, (u8 *) buf, strlen(buf),
+ hapd->conf->auth_server->shared_secret,
+ hapd->conf->auth_server->shared_secret_len)) {
+ printf("Could not add User-Password\n");
+ goto fail;
+ }
+
+ if (!radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IP_ADDRESS,
+ (u8 *) &hapd->conf->own_ip_addr, 4)) {
+ printf("Could not add NAS-IP-Address\n");
+ goto fail;
+ }
+
+ if (hapd->conf->nas_identifier &&
+ !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IDENTIFIER,
+ (u8 *) hapd->conf->nas_identifier,
+ strlen(hapd->conf->nas_identifier))) {
+ printf("Could not add NAS-Identifier\n");
+ goto fail;
+ }
+
+ snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT ":%s",
+ MAC2STR(hapd->own_addr), hapd->conf->ssid);
+ if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLED_STATION_ID,
+ (u8 *) buf, strlen(buf))) {
+ printf("Could not add Called-Station-Id\n");
+ goto fail;
+ }
+
+ snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT,
+ MAC2STR(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;
+ }
+
+ 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;
+ }
+
+ radius_client_send(hapd->radius, msg, RADIUS_AUTH, addr);
+ return 0;
+
+ fail:
+ radius_msg_free(msg);
+ free(msg);
+ return -1;
+}
+
+
+int hostapd_allowed_address(hostapd *hapd, u8 *addr, u8 *msg, size_t len,
+ u32 *session_timeout, u32 *acct_interim_interval)
+{
+ *session_timeout = 0;
+ *acct_interim_interval = 0;
+
+ if (hostapd_maclist_found(hapd->conf->accept_mac,
+ hapd->conf->num_accept_mac, addr))
+ return HOSTAPD_ACL_ACCEPT;
+
+ if (hostapd_maclist_found(hapd->conf->deny_mac,
+ hapd->conf->num_deny_mac, addr))
+ return HOSTAPD_ACL_REJECT;
+
+ if (hapd->conf->macaddr_acl == ACCEPT_UNLESS_DENIED)
+ return HOSTAPD_ACL_ACCEPT;
+ if (hapd->conf->macaddr_acl == DENY_UNLESS_ACCEPTED)
+ return HOSTAPD_ACL_REJECT;
+
+ if (hapd->conf->macaddr_acl == USE_EXTERNAL_RADIUS_AUTH) {
+ struct hostapd_acl_query_data *query;
+
+ /* Check whether ACL cache has an entry for this station */
+ int res = hostapd_acl_cache_get(hapd, addr, session_timeout,
+ acct_interim_interval);
+ if (res == HOSTAPD_ACL_ACCEPT ||
+ res == HOSTAPD_ACL_ACCEPT_TIMEOUT)
+ return res;
+ if (res == HOSTAPD_ACL_REJECT)
+ return HOSTAPD_ACL_REJECT;
+
+ query = hapd->acl_queries;
+ while (query) {
+ if (memcmp(query->addr, addr, ETH_ALEN) == 0) {
+ /* pending query in RADIUS retransmit queue;
+ * do not generate a new one */
+ return HOSTAPD_ACL_PENDING;
+ }
+ query = query->next;
+ }
+
+ if (!hapd->conf->auth_server)
+ return HOSTAPD_ACL_REJECT;
+
+ /* No entry in the cache - query external RADIUS server */
+ query = malloc(sizeof(*query));
+ if (query == NULL) {
+ printf("malloc for query data failed\n");
+ return HOSTAPD_ACL_REJECT;
+ }
+ memset(query, 0, sizeof(*query));
+ time(&query->timestamp);
+ memcpy(query->addr, addr, ETH_ALEN);
+ if (hostapd_radius_acl_query(hapd, addr, query)) {
+ printf("Failed to send Access-Request for ACL "
+ "query.\n");
+ hostapd_acl_query_free(query);
+ return HOSTAPD_ACL_REJECT;
+ }
+
+ query->auth_msg = malloc(len);
+ if (query->auth_msg == NULL) {
+ printf("Failed to allocate memory for auth frame.\n");
+ hostapd_acl_query_free(query);
+ return HOSTAPD_ACL_REJECT;
+ }
+ memcpy(query->auth_msg, msg, len);
+ query->auth_msg_len = len;
+ query->next = hapd->acl_queries;
+ hapd->acl_queries = query;
+
+ /* Queued data will be processed in hostapd_acl_recv_radius()
+ * when RADIUS server replies to the sent Access-Request. */
+ return HOSTAPD_ACL_PENDING;
+ }
+
+ return HOSTAPD_ACL_REJECT;
+}
+
+
+static void hostapd_acl_expire_cache(hostapd *hapd, time_t now)
+{
+ struct hostapd_cached_radius_acl *prev, *entry, *tmp;
+
+ prev = NULL;
+ entry = hapd->acl_cache;
+
+ while (entry) {
+ if (now - entry->timestamp > RADIUS_ACL_TIMEOUT) {
+ HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL,
+ "Cached ACL entry for " MACSTR
+ " has expired.\n", MAC2STR(entry->addr));
+ if (prev)
+ prev->next = entry->next;
+ else
+ hapd->acl_cache = entry->next;
+
+ tmp = entry;
+ entry = entry->next;
+ free(tmp);
+ continue;
+ }
+
+ prev = entry;
+ entry = entry->next;
+ }
+}
+
+
+static void hostapd_acl_expire_queries(hostapd *hapd, time_t now)
+{
+ struct hostapd_acl_query_data *prev, *entry, *tmp;
+
+ prev = NULL;
+ entry = hapd->acl_queries;
+
+ while (entry) {
+ if (now - entry->timestamp > RADIUS_ACL_TIMEOUT) {
+ HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL,
+ "ACL query for " MACSTR
+ " has expired.\n", MAC2STR(entry->addr));
+ if (prev)
+ prev->next = entry->next;
+ else
+ hapd->acl_queries = entry->next;
+
+ tmp = entry;
+ entry = entry->next;
+ hostapd_acl_query_free(tmp);
+ continue;
+ }
+
+ prev = entry;
+ entry = entry->next;
+ }
+}
+
+
+static void hostapd_acl_expire(void *eloop_ctx, void *timeout_ctx)
+{
+ hostapd *hapd = eloop_ctx;
+ time_t now;
+
+ time(&now);
+ hostapd_acl_expire_cache(hapd, now);
+ hostapd_acl_expire_queries(hapd, now);
+
+ eloop_register_timeout(10, 0, hostapd_acl_expire, hapd, NULL);
+}
+
+
+/* Return 0 if RADIUS message was a reply to ACL query (and was processed here)
+ * or -1 if not. */
+static RadiusRxResult
+hostapd_acl_recv_radius(struct radius_msg *msg, struct radius_msg *req,
+ u8 *shared_secret, size_t shared_secret_len,
+ void *data)
+{
+ struct hostapd_data *hapd = data;
+ struct hostapd_acl_query_data *query, *prev;
+ struct hostapd_cached_radius_acl *cache;
+
+ query = hapd->acl_queries;
+ prev = NULL;
+ while (query) {
+ if (query->radius_id == msg->hdr->identifier)
+ break;
+ prev = query;
+ query = query->next;
+ }
+ if (query == NULL)
+ return RADIUS_RX_UNKNOWN;
+
+ HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "Found matching Access-Request "
+ "for RADIUS message (id=%d)\n", query->radius_id);
+
+ if (radius_msg_verify_acct(msg, shared_secret, shared_secret_len,
+ req)) {
+ printf("Incoming RADIUS packet did not have correct "
+ "authenticator - dropped\n");
+ return RADIUS_RX_INVALID_AUTHENTICATOR;
+ }
+
+ if (msg->hdr->code != RADIUS_CODE_ACCESS_ACCEPT &&
+ msg->hdr->code != RADIUS_CODE_ACCESS_REJECT) {
+ printf("Unknown RADIUS message code %d to ACL query\n",
+ msg->hdr->code);
+ return RADIUS_RX_UNKNOWN;
+ }
+
+ /* Insert Accept/Reject info into ACL cache */
+ cache = malloc(sizeof(*cache));
+ if (cache == NULL) {
+ printf("Failed to add ACL cache entry\n");
+ goto done;
+ }
+ memset(cache, 0, sizeof(*cache));
+ time(&cache->timestamp);
+ memcpy(cache->addr, query->addr, sizeof(cache->addr));
+ if (msg->hdr->code == RADIUS_CODE_ACCESS_ACCEPT) {
+ if (radius_msg_get_attr_int32(msg, RADIUS_ATTR_SESSION_TIMEOUT,
+ &cache->session_timeout) == 0)
+ cache->accepted = HOSTAPD_ACL_ACCEPT_TIMEOUT;
+ else
+ cache->accepted = HOSTAPD_ACL_ACCEPT;
+
+ if (radius_msg_get_attr_int32(
+ msg, RADIUS_ATTR_ACCT_INTERIM_INTERVAL,
+ &cache->acct_interim_interval) == 0 &&
+ cache->acct_interim_interval < 60) {
+ HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "Ignored too "
+ "small Acct-Interim-Interval %d for "
+ "STA " MACSTR "\n",
+ cache->acct_interim_interval,
+ MAC2STR(query->addr));
+ cache->acct_interim_interval = 0;
+ }
+ } else
+ cache->accepted = HOSTAPD_ACL_REJECT;
+ cache->next = hapd->acl_cache;
+ hapd->acl_cache = cache;
+
+ /* Re-send original authentication frame for 802.11 processing */
+ HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "Re-sending authentication frame "
+ "after successful RADIUS ACL query\n");
+ ieee802_11_mgmt(hapd, query->auth_msg, query->auth_msg_len,
+ WLAN_FC_STYPE_AUTH);
+
+ done:
+ if (prev == NULL)
+ hapd->acl_queries = query->next;
+ else
+ prev->next = query->next;
+
+ hostapd_acl_query_free(query);
+
+ return RADIUS_RX_PROCESSED;
+}
+
+
+int hostapd_acl_init(hostapd *hapd)
+{
+ if (radius_client_register(hapd->radius, RADIUS_AUTH,
+ hostapd_acl_recv_radius, hapd))
+ return -1;
+
+ eloop_register_timeout(10, 0, hostapd_acl_expire, hapd, NULL);
+
+ return 0;
+}
+
+
+void hostapd_acl_deinit(hostapd *hapd)
+{
+ struct hostapd_acl_query_data *query, *prev;
+
+ hostapd_acl_cache_free(hapd->acl_cache);
+
+ query = hapd->acl_queries;
+ while (query) {
+ prev = query;
+ query = query->next;
+ hostapd_acl_query_free(prev);
+ }
+}
diff --git a/contrib/hostapd/ieee802_11_auth.h b/contrib/hostapd/ieee802_11_auth.h
new file mode 100644
index 000000000000..90adc8f174ca
--- /dev/null
+++ b/contrib/hostapd/ieee802_11_auth.h
@@ -0,0 +1,16 @@
+#ifndef IEEE802_11_AUTH_H
+#define IEEE802_11_AUTH_H
+
+enum {
+ HOSTAPD_ACL_REJECT = 0,
+ HOSTAPD_ACL_ACCEPT = 1,
+ HOSTAPD_ACL_PENDING = 2,
+ HOSTAPD_ACL_ACCEPT_TIMEOUT = 3
+};
+
+int hostapd_allowed_address(hostapd *hapd, u8 *addr, u8 *msg, size_t len,
+ u32 *session_timeout, u32 *acct_interim_interval);
+int hostapd_acl_init(hostapd *hapd);
+void hostapd_acl_deinit(hostapd *hapd);
+
+#endif /* IEEE802_11_AUTH_H */
diff --git a/contrib/hostapd/ieee802_1x.c b/contrib/hostapd/ieee802_1x.c
new file mode 100644
index 000000000000..870d52ab5cac
--- /dev/null
+++ b/contrib/hostapd/ieee802_1x.c
@@ -0,0 +1,1649 @@
+/*
+ * Host AP (software wireless LAN access point) user space daemon for
+ * Host AP kernel driver / IEEE 802.1X Authenticator
+ * 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 <unistd.h>
+#include <netinet/in.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <signal.h>
+#include <assert.h>
+#include <time.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+
+#include "hostapd.h"
+#include "ieee802_1x.h"
+#include "accounting.h"
+#include "radius.h"
+#include "radius_client.h"
+#include "eapol_sm.h"
+#include "md5.h"
+#include "rc4.h"
+#include "eloop.h"
+#include "sta_info.h"
+#include "wpa.h"
+#include "driver.h"
+#include "eap.h"
+
+
+static void ieee802_1x_new_auth_session(struct hostapd_data *hapd,
+ struct sta_info *sta);
+
+
+static void ieee802_1x_send(hostapd *hapd, struct sta_info *sta, u8 type,
+ u8 *data, size_t datalen)
+{
+ u8 *buf;
+ struct ieee802_1x_hdr *xhdr;
+ size_t len;
+ int encrypt = 0;
+
+ len = sizeof(*xhdr) + datalen;
+ buf = malloc(len);
+ if (buf == NULL) {
+ printf("malloc() failed for ieee802_1x_send(len=%lu)\n",
+ (unsigned long) len);
+ return;
+ }
+
+ memset(buf, 0, len);
+#if 0
+ /* TODO:
+ * According to IEEE 802.1aa/D4 EAPOL-Key should be sent before any
+ * remaining EAP frames, if possible. This would allow rest of the
+ * frames to be encrypted. This code could be used to request
+ * encryption from the kernel driver. */
+ if (sta->eapol_sm &&
+ sta->eapol_sm->be_auth.state == BE_AUTH_SUCCESS &&
+ sta->eapol_sm->keyTxEnabled)
+ encrypt = 1;
+#endif
+
+ xhdr = (struct ieee802_1x_hdr *) buf;
+ xhdr->version = EAPOL_VERSION;
+ xhdr->type = type;
+ xhdr->length = htons(datalen);
+
+ if (datalen > 0 && data != NULL)
+ memcpy(xhdr + 1, data, datalen);
+
+ if (sta->wpa_sm && sta->wpa_sm->pairwise_set)
+ encrypt = 1;
+ if (sta->flags & WLAN_STA_PREAUTH) {
+ rsn_preauth_send(hapd, sta, buf, len);
+ } else {
+ hostapd_send_eapol(hapd, sta->addr, buf, len, encrypt);
+ }
+
+ free(buf);
+}
+
+
+void ieee802_1x_set_sta_authorized(hostapd *hapd, struct sta_info *sta,
+ int authorized)
+{
+ if (sta->flags & WLAN_STA_PREAUTH)
+ return;
+
+ if (authorized) {
+ sta->flags |= WLAN_STA_AUTHORIZED;
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
+ HOSTAPD_LEVEL_DEBUG, "authorizing port");
+ } else {
+ sta->flags &= ~WLAN_STA_AUTHORIZED;
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
+ HOSTAPD_LEVEL_DEBUG, "unauthorizing port");
+ }
+
+ hostapd_set_sta_authorized(hapd, sta->addr, authorized);
+ if (authorized)
+ accounting_sta_start(hapd, sta);
+}
+
+
+static void ieee802_1x_eap_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ struct sta_info *sta = eloop_ctx;
+ struct eapol_state_machine *sm = sta->eapol_sm;
+ if (sm == NULL)
+ return;
+ hostapd_logger(sm->hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
+ HOSTAPD_LEVEL_DEBUG, "EAP timeout");
+ sm->eapTimeout = TRUE;
+ eapol_sm_step(sm);
+}
+
+
+void ieee802_1x_request_identity(struct hostapd_data *hapd,
+ struct sta_info *sta)
+{
+ u8 *buf;
+ struct eap_hdr *eap;
+ int extra, tlen;
+ u8 *pos;
+ struct eapol_state_machine *sm = sta->eapol_sm;
+
+ if (hapd->conf->eap_authenticator) {
+ HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL,
+ "IEEE 802.1X: Integrated EAP Authenticator in "
+ "use - do not generate EAP-Request/Identity\n");
+ return;
+ }
+
+ if (sm == NULL || !sm->auth_pae.eapRestart)
+ return;
+
+ ieee802_1x_new_auth_session(hapd, sta);
+
+ if (hapd->conf->eap_req_id_text)
+ extra = strlen(hapd->conf->eap_req_id_text);
+ else
+ extra = 0;
+
+ tlen = sizeof(*eap) + 1 + extra;
+
+ buf = malloc(tlen);
+ if (buf == NULL) {
+ printf("Could not allocate memory for identity request\n");
+ return;
+ }
+
+ memset(buf, 0, tlen);
+
+ eap = (struct eap_hdr *) buf;
+ eap->code = EAP_CODE_REQUEST;
+ eap->identifier = ++sm->currentId;
+ eap->length = htons(tlen);
+ pos = (u8 *) (eap + 1);
+ *pos++ = EAP_TYPE_IDENTITY;
+ if (hapd->conf->eap_req_id_text)
+ memcpy(pos, hapd->conf->eap_req_id_text, extra);
+
+ sm->be_auth.eapReq = TRUE;
+ free(sm->last_eap_radius);
+ sm->last_eap_radius = buf;
+ sm->last_eap_radius_len = tlen;
+
+ eloop_cancel_timeout(ieee802_1x_eap_timeout, sta, NULL);
+ eloop_register_timeout(30, 0, ieee802_1x_eap_timeout, sta, NULL);
+ sm->eapTimeout = FALSE;
+
+ HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL,
+ "IEEE 802.1X: Generated EAP Request-Identity for " MACSTR
+ " (identifier %d, timeout 30)\n", MAC2STR(sta->addr),
+ eap->identifier);
+
+ sm->auth_pae.eapRestart = FALSE;
+}
+
+
+void ieee802_1x_tx_canned_eap(struct hostapd_data *hapd, struct sta_info *sta,
+ int success)
+{
+ struct eap_hdr eap;
+ struct eapol_state_machine *sm = sta->eapol_sm;
+
+ memset(&eap, 0, sizeof(eap));
+
+ eap.code = success ? EAP_CODE_SUCCESS : EAP_CODE_FAILURE;
+ eap.identifier = 1;
+ if (sm && sm->last_eap_radius) {
+ struct eap_hdr *hdr = (struct eap_hdr *) sm->last_eap_radius;
+ eap.identifier = hdr->identifier + 1;
+ }
+ eap.length = htons(sizeof(eap));
+
+ HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL,
+ "IEEE 802.1X: Sending canned EAP packet %s to " MACSTR
+ " (identifier %d)\n", success ? "SUCCESS" : "FAILURE",
+ MAC2STR(sta->addr), eap.identifier);
+ ieee802_1x_send(hapd, sta, IEEE802_1X_TYPE_EAP_PACKET, (u8 *) &eap,
+ sizeof(eap));
+ if (sm)
+ sm->dot1xAuthEapolFramesTx++;
+}
+
+
+void ieee802_1x_tx_req(struct hostapd_data *hapd, struct sta_info *sta)
+{
+ struct eap_hdr *eap;
+ struct eapol_state_machine *sm = sta->eapol_sm;
+ u8 *type;
+ if (sm == NULL)
+ return;
+
+ if (sm->last_eap_radius == NULL) {
+ printf("Error: TxReq called for station " MACSTR ", but there "
+ "is no EAP request from the authentication server\n",
+ MAC2STR(sm->addr));
+ return;
+ }
+
+ eap = (struct eap_hdr *) sm->last_eap_radius;
+ HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL,
+ "IEEE 802.1X: Sending EAP Packet to " MACSTR
+ " (identifier %d)\n", MAC2STR(sm->addr),
+ eap->identifier);
+ sm->currentId = eap->identifier;
+ ieee802_1x_send(hapd, sta, IEEE802_1X_TYPE_EAP_PACKET,
+ sm->last_eap_radius, sm->last_eap_radius_len);
+ sm->dot1xAuthEapolFramesTx++;
+ type = (u8 *) (eap + 1);
+ if (sm->last_eap_radius_len > sizeof(*eap) &&
+ *type == EAP_TYPE_IDENTITY)
+ sm->dot1xAuthEapolReqIdFramesTx++;
+ else
+ sm->dot1xAuthEapolReqFramesTx++;
+}
+
+
+void hostapd_get_ntp_timestamp(u8 *buf)
+{
+ struct timeval now;
+ u32 sec, usec;
+
+ /* 64-bit NTP timestamp (time from 1900-01-01 00:00:00) */
+ gettimeofday(&now, NULL);
+ sec = htonl(now.tv_sec + 2208988800U); /* Epoch to 1900 */
+ /* Estimate 2^32/10^6 = 4295 - 1/32 - 1/512 */
+ usec = now.tv_usec;
+ usec = htonl(4295 * usec - (usec >> 5) - (usec >> 9));
+ memcpy(buf, (u8 *) &sec, 4);
+ memcpy(buf + 4, (u8 *) &usec, 4);
+}
+
+
+static void ieee802_1x_tx_key_one(hostapd *hapd, struct sta_info *sta,
+ int index, int broadcast,
+ u8 *key_data, size_t key_len)
+{
+ u8 *buf, *ekey;
+ struct ieee802_1x_hdr *hdr;
+ struct ieee802_1x_eapol_key *key;
+ size_t len, ekey_len;
+ struct eapol_state_machine *sm = sta->eapol_sm;
+
+ if (sm == NULL)
+ return;
+
+ len = sizeof(*key) + key_len;
+ buf = malloc(sizeof(*hdr) + len);
+ if (buf == NULL)
+ return;
+
+ memset(buf, 0, sizeof(*hdr) + len);
+ hdr = (struct ieee802_1x_hdr *) buf;
+ key = (struct ieee802_1x_eapol_key *) (hdr + 1);
+ key->type = EAPOL_KEY_TYPE_RC4;
+ key->key_length = htons(key_len);
+ hostapd_get_ntp_timestamp(key->replay_counter);
+
+ if (hostapd_get_rand(key->key_iv, sizeof(key->key_iv))) {
+ printf("Could not get random numbers\n");
+ free(buf);
+ return;
+ }
+
+ key->key_index = index | (broadcast ? 0 : BIT(7));
+ if (hapd->conf->eapol_key_index_workaround) {
+ /* According to some information, WinXP Supplicant seems to
+ * interpret bit7 as an indication whether the key is to be
+ * activated, so make it possible to enable workaround that
+ * sets this bit for all keys. */
+ key->key_index |= BIT(7);
+ }
+
+ /* Key is encrypted using "Key-IV + sm->eapol_key_crypt" as the
+ * RC4-key */
+ memcpy((u8 *) (key + 1), key_data, key_len);
+ ekey_len = sizeof(key->key_iv) + sm->eapol_key_crypt_len;
+ ekey = malloc(ekey_len);
+ if (ekey == NULL) {
+ printf("Could not encrypt key\n");
+ free(buf);
+ return;
+ }
+ memcpy(ekey, key->key_iv, sizeof(key->key_iv));
+ memcpy(ekey + sizeof(key->key_iv), sm->eapol_key_crypt,
+ sm->eapol_key_crypt_len);
+ rc4((u8 *) (key + 1), key_len, ekey, ekey_len);
+ free(ekey);
+
+ /* This header is needed here for HMAC-MD5, but it will be regenerated
+ * in ieee802_1x_send() */
+ hdr->version = EAPOL_VERSION;
+ hdr->type = IEEE802_1X_TYPE_EAPOL_KEY;
+ hdr->length = htons(len);
+ hmac_md5(sm->eapol_key_sign, sm->eapol_key_sign_len,
+ buf, sizeof(*hdr) + len,
+ key->key_signature);
+
+ HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL,
+ "IEEE 802.1X: Sending EAPOL-Key to " MACSTR
+ " (%s index=%d)\n", MAC2STR(sm->addr),
+ broadcast ? "broadcast" : "unicast", index);
+ ieee802_1x_send(hapd, sta, IEEE802_1X_TYPE_EAPOL_KEY, (u8 *) key, len);
+ if (sta->eapol_sm)
+ sta->eapol_sm->dot1xAuthEapolFramesTx++;
+ free(buf);
+}
+
+
+void ieee802_1x_tx_key(struct hostapd_data *hapd, struct sta_info *sta)
+{
+ struct eapol_state_machine *sm = sta->eapol_sm;
+
+ if (sm == NULL || !sm->eapol_key_sign || !sm->eapol_key_crypt)
+ return;
+
+ HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL,
+ "IEEE 802.1X: Sending EAPOL-Key(s) to " MACSTR "\n",
+ MAC2STR(sta->addr));
+
+ if (hapd->default_wep_key)
+ ieee802_1x_tx_key_one(hapd, sta, hapd->default_wep_key_idx, 1,
+ hapd->default_wep_key,
+ hapd->conf->default_wep_key_len);
+
+ if (hapd->conf->individual_wep_key_len > 0) {
+ u8 *ikey;
+ ikey = malloc(hapd->conf->individual_wep_key_len);
+ if (ikey == NULL ||
+ hostapd_get_rand(ikey,
+ hapd->conf->individual_wep_key_len)) {
+ printf("Could not generate random individual WEP "
+ "key.\n");
+ free(ikey);
+ return;
+ }
+
+ if (HOSTAPD_DEBUG_COND(HOSTAPD_DEBUG_MINIMAL))
+ hostapd_hexdump("Individual WEP key",
+ ikey,
+ hapd->conf->individual_wep_key_len);
+
+ ieee802_1x_tx_key_one(hapd, sta, 0, 0, ikey,
+ hapd->conf->individual_wep_key_len);
+
+ /* TODO: set encryption in TX callback, i.e., only after STA
+ * has ACKed EAPOL-Key frame */
+ if (hostapd_set_encryption(hapd, "WEP", sta->addr, 0, ikey,
+ hapd->conf->
+ individual_wep_key_len)) {
+ printf("Could not set individual WEP encryption.\n");
+ }
+
+ free(ikey);
+ }
+}
+
+
+static void ieee802_1x_encapsulate_radius(hostapd *hapd, struct sta_info *sta,
+ u8 *eap, size_t len)
+{
+ struct radius_msg *msg;
+ char buf[128];
+ struct eapol_state_machine *sm = sta->eapol_sm;
+
+ if (sm == NULL)
+ return;
+
+ HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL,
+ "Encapsulating EAP message into a RADIUS packet\n");
+
+ sm->radius_identifier = radius_client_get_id(hapd->radius);
+ msg = radius_msg_new(RADIUS_CODE_ACCESS_REQUEST,
+ sm->radius_identifier);
+ if (msg == NULL) {
+ printf("Could not create net RADIUS packet\n");
+ return;
+ }
+
+ radius_msg_make_authenticator(msg, (u8 *) sta, sizeof(*sta));
+
+ if (sm->identity &&
+ !radius_msg_add_attr(msg, RADIUS_ATTR_USER_NAME,
+ sm->identity, sm->identity_len)) {
+ printf("Could not add User-Name\n");
+ goto fail;
+ }
+
+ if (!radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IP_ADDRESS,
+ (u8 *) &hapd->conf->own_ip_addr, 4)) {
+ printf("Could not add NAS-IP-Address\n");
+ goto fail;
+ }
+
+ if (hapd->conf->nas_identifier &&
+ !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IDENTIFIER,
+ (u8 *) hapd->conf->nas_identifier,
+ strlen(hapd->conf->nas_identifier))) {
+ printf("Could not add NAS-Identifier\n");
+ goto fail;
+ }
+
+ if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_NAS_PORT, sta->aid)) {
+ printf("Could not add NAS-Port\n");
+ goto fail;
+ }
+
+ snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT ":%s",
+ MAC2STR(hapd->own_addr), hapd->conf->ssid);
+ if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLED_STATION_ID,
+ (u8 *) buf, strlen(buf))) {
+ printf("Could not add Called-Station-Id\n");
+ goto fail;
+ }
+
+ snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT,
+ MAC2STR(sta->addr));
+ if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLING_STATION_ID,
+ (u8 *) buf, 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 (sm->last_recv_radius && sm->last_recv_radius->hdr->code ==
+ RADIUS_CODE_ACCESS_CHALLENGE) {
+ int res = radius_msg_copy_attr(msg, sm->last_recv_radius,
+ RADIUS_ATTR_STATE);
+ if (res < 0) {
+ printf("Could not copy State attribute from previous "
+ "Access-Challenge\n");
+ goto fail;
+ }
+ if (res > 0) {
+ HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL,
+ " Copied RADIUS State Attribute\n");
+ }
+ }
+
+ radius_client_send(hapd->radius, msg, RADIUS_AUTH, sta->addr);
+ return;
+
+ fail:
+ radius_msg_free(msg);
+ free(msg);
+}
+
+
+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_MD5: return "MD5-Challenge";
+ case EAP_TYPE_OTP: return "One-Time Password";
+ case EAP_TYPE_GTC: return "Generic Token Card";
+ case EAP_TYPE_TLS: return "TLS";
+ case EAP_TYPE_TTLS: return "TTLS";
+ case EAP_TYPE_PEAP: return "PEAP";
+ default: return "Unknown";
+ }
+}
+
+
+static void handle_eap_response(hostapd *hapd, struct sta_info *sta,
+ struct eap_hdr *eap, u8 *data, size_t len)
+{
+ u8 type;
+ struct eapol_state_machine *sm = sta->eapol_sm;
+ if (sm == NULL)
+ return;
+
+ if (eap->identifier != sm->currentId) {
+ hostapd_logger(hapd, sm->addr, HOSTAPD_MODULE_IEEE8021X,
+ HOSTAPD_LEVEL_DEBUG,
+ "EAP Identifier of the Response-Identity does "
+ "not match (was %d, expected %d) - ignored",
+ eap->identifier, sm->currentId);
+ return;
+ }
+
+ if (len < 1) {
+ printf("handle_eap_response: too short response data\n");
+ return;
+ }
+
+ eloop_cancel_timeout(ieee802_1x_eap_timeout, sta, NULL);
+
+ free(sm->last_eap_supp);
+ sm->last_eap_supp_len = sizeof(*eap) + len;
+ sm->last_eap_supp = (u8 *) malloc(sm->last_eap_supp_len);
+ if (sm->last_eap_supp == NULL) {
+ printf("Could not alloc memory for last EAP Response\n");
+ return;
+ }
+ memcpy(sm->last_eap_supp, eap, sizeof(*eap));
+ memcpy(sm->last_eap_supp + sizeof(*eap), data, len);
+
+ type = data[0];
+ data++;
+ len--;
+
+ hostapd_logger(hapd, sm->addr, HOSTAPD_MODULE_IEEE8021X,
+ HOSTAPD_LEVEL_DEBUG, "received EAP packet (code=%d "
+ "id=%d len=%d) from STA: EAP Response-%s (%d)",
+ eap->code, eap->identifier, ntohs(eap->length),
+ eap_type_text(type), type);
+
+ if (type == EAP_TYPE_IDENTITY) {
+ char *buf, *pos;
+ int i;
+ buf = malloc(4 * len + 1);
+ if (buf) {
+ pos = buf;
+ for (i = 0; i < len; i++) {
+ if (data[i] >= 32 && data[i] < 127)
+ *pos++ = data[i];
+ else {
+ snprintf(pos, 5, "{%02x}", data[i]);
+ pos += 4;
+ }
+ }
+ *pos = '\0';
+ hostapd_logger(hapd, sm->addr,
+ HOSTAPD_MODULE_IEEE8021X,
+ HOSTAPD_LEVEL_DEBUG,
+ "STA identity '%s'", buf);
+ free(buf);
+ }
+
+ sm->rx_identity = TRUE;
+ sm->dot1xAuthEapolRespIdFramesRx++;
+
+ /* Save station identity for future RADIUS packets */
+ free(sm->identity);
+ sm->identity = malloc(len + 1);
+ if (sm->identity) {
+ memcpy(sm->identity, data, len);
+ sm->identity[len] = '\0';
+ sm->identity_len = len;
+ }
+ } else
+ sm->dot1xAuthEapolRespFramesRx++;
+
+ sm->eapolEap = TRUE;
+}
+
+
+/* Process incoming EAP packet from Supplicant */
+static void handle_eap(hostapd *hapd, struct sta_info *sta, u8 *buf,
+ size_t len)
+{
+ struct eap_hdr *eap;
+ u16 eap_len;
+
+ if (len < sizeof(*eap)) {
+ printf(" too short EAP packet\n");
+ return;
+ }
+
+ eap = (struct eap_hdr *) buf;
+
+ eap_len = ntohs(eap->length);
+ HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL,
+ " EAP: code=%d identifier=%d length=%d",
+ eap->code, eap->identifier, eap_len);
+ if (eap_len < sizeof(*eap)) {
+ printf(" Invalid EAP length\n");
+ return;
+ } else if (eap_len > len) {
+ printf(" Too short frame to contain this EAP packet\n");
+ return;
+ } else if (eap_len < len) {
+ printf(" Ignoring %lu extra bytes after EAP packet\n",
+ (unsigned long) len - eap_len);
+ }
+
+ eap_len -= sizeof(*eap);
+
+ switch (eap->code) {
+ case EAP_CODE_REQUEST:
+ HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, " (request)\n");
+ return;
+ case EAP_CODE_RESPONSE:
+ HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, " (response)\n");
+ handle_eap_response(hapd, sta, eap, (u8 *) (eap + 1), eap_len);
+ break;
+ case EAP_CODE_SUCCESS:
+ HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, " (success)\n");
+ return;
+ case EAP_CODE_FAILURE:
+ HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, " (failure)\n");
+ return;
+ default:
+ HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, " (unknown code)\n");
+ return;
+ }
+}
+
+
+/* Process the EAPOL frames from the Supplicant */
+void ieee802_1x_receive(hostapd *hapd, u8 *sa, u8 *buf, size_t len)
+{
+ struct sta_info *sta;
+ struct ieee802_1x_hdr *hdr;
+ struct ieee802_1x_eapol_key *key;
+ u16 datalen;
+
+ if (!hapd->conf->ieee802_1x && !hapd->conf->wpa)
+ return;
+
+ HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL,
+ "IEEE 802.1X: %lu bytes from " MACSTR "\n",
+ (unsigned long) len, MAC2STR(sa));
+ sta = ap_get_sta(hapd, sa);
+ if (!sta) {
+ printf(" no station information available\n");
+ return;
+ }
+
+ if (len < sizeof(*hdr)) {
+ printf(" too short IEEE 802.1X packet\n");
+ return;
+ }
+
+ hdr = (struct ieee802_1x_hdr *) buf;
+ datalen = ntohs(hdr->length);
+ HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL,
+ " IEEE 802.1X: version=%d type=%d length=%d\n",
+ hdr->version, hdr->type, datalen);
+
+ if (len - sizeof(*hdr) < datalen) {
+ printf(" frame too short for this IEEE 802.1X packet\n");
+ if (sta->eapol_sm)
+ sta->eapol_sm->dot1xAuthEapLengthErrorFramesRx++;
+ return;
+ }
+ if (len - sizeof(*hdr) > datalen) {
+ HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL,
+ " ignoring %lu extra octets after IEEE 802.1X "
+ "packet\n",
+ (unsigned long) len - sizeof(*hdr) - datalen);
+ }
+
+ if (sta->eapol_sm) {
+ sta->eapol_sm->dot1xAuthLastEapolFrameVersion = hdr->version;
+ sta->eapol_sm->dot1xAuthEapolFramesRx++;
+ }
+
+ key = (struct ieee802_1x_eapol_key *) (hdr + 1);
+ if (datalen >= sizeof(struct ieee802_1x_eapol_key) &&
+ hdr->type == IEEE802_1X_TYPE_EAPOL_KEY &&
+ (key->type == EAPOL_KEY_TYPE_WPA ||
+ key->type == EAPOL_KEY_TYPE_RSN)) {
+ wpa_receive(hapd, sta, (u8 *) hdr, sizeof(*hdr) + datalen);
+ return;
+ }
+
+ if (!hapd->conf->ieee802_1x || sta->wpa_key_mgmt == WPA_KEY_MGMT_PSK)
+ return;
+
+ if (!sta->eapol_sm) {
+ sta->eapol_sm = eapol_sm_alloc(hapd, sta);
+ if (!sta->eapol_sm)
+ return;
+ }
+
+ /* since we support version 1, we can ignore version field and proceed
+ * as specified in version 1 standard [IEEE Std 802.1X-2001, 7.5.5] */
+ /* TODO: actually, we are not version 1 anymore.. However, Version 2
+ * does not change frame contents, so should be ok to process frames
+ * more or less identically. Some changes might be needed for
+ * verification of fields. */
+
+ switch (hdr->type) {
+ case IEEE802_1X_TYPE_EAP_PACKET:
+ handle_eap(hapd, sta, (u8 *) (hdr + 1), datalen);
+ break;
+
+ case IEEE802_1X_TYPE_EAPOL_START:
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
+ HOSTAPD_LEVEL_DEBUG, "received EAPOL-Start "
+ "from STA");
+ if (sta->pmksa) {
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA,
+ HOSTAPD_LEVEL_DEBUG, "cached PMKSA "
+ "available - ignore it since "
+ "STA sent EAPOL-Start");
+ sta->pmksa = NULL;
+ }
+ sta->eapol_sm->auth_pae.eapolStart = TRUE;
+ sta->eapol_sm->dot1xAuthEapolStartFramesRx++;
+ wpa_sm_event(hapd, sta, WPA_REAUTH_EAPOL);
+ eapol_sm_step(sta->eapol_sm);
+ break;
+
+ case IEEE802_1X_TYPE_EAPOL_LOGOFF:
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
+ HOSTAPD_LEVEL_DEBUG, "received EAPOL-Logoff "
+ "from STA");
+ sta->acct_terminate_cause =
+ RADIUS_ACCT_TERMINATE_CAUSE_USER_REQUEST;
+ sta->eapol_sm->auth_pae.eapolLogoff = TRUE;
+ sta->eapol_sm->dot1xAuthEapolLogoffFramesRx++;
+ eapol_sm_step(sta->eapol_sm);
+ break;
+
+ case IEEE802_1X_TYPE_EAPOL_KEY:
+ HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, " EAPOL-Key\n");
+ if (!(sta->flags & WLAN_STA_AUTHORIZED)) {
+ printf(" Dropped key data from unauthorized "
+ "Supplicant\n");
+ break;
+ }
+ break;
+
+ case IEEE802_1X_TYPE_EAPOL_ENCAPSULATED_ASF_ALERT:
+ HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL,
+ " EAPOL-Encapsulated-ASF-Alert\n");
+ /* TODO: implement support for this; show data */
+ break;
+
+ default:
+ HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL,
+ " unknown IEEE 802.1X packet type\n");
+ sta->eapol_sm->dot1xAuthInvalidEapolFramesRx++;
+ break;
+ }
+
+ eapol_sm_step(sta->eapol_sm);
+}
+
+
+void ieee802_1x_new_station(hostapd *hapd, struct sta_info *sta)
+{
+ if (!hapd->conf->ieee802_1x || sta->wpa_key_mgmt == WPA_KEY_MGMT_PSK)
+ return;
+
+ if (sta->eapol_sm) {
+ sta->eapol_sm->portEnabled = TRUE;
+ eapol_sm_step(sta->eapol_sm);
+ return;
+ }
+
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
+ HOSTAPD_LEVEL_DEBUG, "start authentication");
+ sta->eapol_sm = eapol_sm_alloc(hapd, sta);
+ if (sta->eapol_sm)
+ sta->eapol_sm->portEnabled = TRUE;
+}
+
+
+void ieee802_1x_free_station(struct sta_info *sta)
+{
+ struct eapol_state_machine *sm = sta->eapol_sm;
+
+ eloop_cancel_timeout(ieee802_1x_eap_timeout, sta, NULL);
+
+ if (sm == NULL)
+ return;
+
+ sta->eapol_sm = NULL;
+
+ if (sm->last_recv_radius) {
+ radius_msg_free(sm->last_recv_radius);
+ free(sm->last_recv_radius);
+ }
+
+ free(sm->last_eap_supp);
+ free(sm->last_eap_radius);
+ free(sm->identity);
+ free(sm->radius_class);
+ free(sm->eapol_key_sign);
+ free(sm->eapol_key_crypt);
+ eapol_sm_free(sm);
+}
+
+
+static void ieee802_1x_decapsulate_radius(hostapd *hapd, struct sta_info *sta)
+{
+ u8 *eap;
+ size_t len;
+ struct eap_hdr *hdr;
+ int eap_type = -1;
+ char buf[64];
+ struct radius_msg *msg;
+ struct eapol_state_machine *sm = sta->eapol_sm;
+
+ if (sm == NULL || sm->last_recv_radius == NULL) {
+ if (sm)
+ sm->be_auth.eapNoReq = TRUE;
+ return;
+ }
+
+ msg = sm->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 */
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
+ HOSTAPD_LEVEL_WARNING, "could not extract "
+ "EAP-Message from RADIUS message");
+ free(sm->last_eap_radius);
+ sm->last_eap_radius = NULL;
+ sm->last_eap_radius_len = 0;
+ sm->be_auth.eapNoReq = TRUE;
+ return;
+ }
+
+ if (len < sizeof(*hdr)) {
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
+ HOSTAPD_LEVEL_WARNING, "too short EAP packet "
+ "received from authentication server");
+ free(eap);
+ sm->be_auth.eapNoReq = TRUE;
+ 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");
+ break;
+ case EAP_CODE_FAILURE:
+ snprintf(buf, sizeof(buf), "EAP Failure");
+ break;
+ default:
+ snprintf(buf, sizeof(buf), "unknown EAP code");
+ break;
+ }
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
+ HOSTAPD_LEVEL_DEBUG, "decapsulated EAP packet (code=%d "
+ "id=%d len=%d) from RADIUS server: %s",
+ hdr->code, hdr->identifier, ntohs(hdr->length), buf);
+ sm->be_auth.eapReq = TRUE;
+
+ free(sm->last_eap_radius);
+ sm->last_eap_radius = eap;
+ sm->last_eap_radius_len = len;
+}
+
+
+static void ieee802_1x_get_keys(hostapd *hapd, struct sta_info *sta,
+ struct radius_msg *msg, struct radius_msg *req,
+ u8 *shared_secret, size_t shared_secret_len)
+{
+ struct radius_ms_mppe_keys *keys;
+ struct eapol_state_machine *sm = sta->eapol_sm;
+ if (sm == NULL)
+ return;
+
+ keys = radius_msg_get_ms_keys(msg, req, shared_secret,
+ shared_secret_len);
+
+ if (keys) {
+ if (HOSTAPD_DEBUG_COND(HOSTAPD_DEBUG_MINIMAL) && keys->send) {
+ size_t i;
+ printf("MS-MPPE-Send-Key (len=%lu):",
+ (unsigned long) keys->send_len);
+ for (i = 0; i < keys->send_len; i++)
+ printf(" %02x", keys->send[i]);
+ printf("\n");
+ }
+ if (HOSTAPD_DEBUG_COND(HOSTAPD_DEBUG_MINIMAL) && keys->recv) {
+ size_t i;
+ printf("MS-MPPE-Recv-Key (len=%lu):",
+ (unsigned long) keys->recv_len);
+ for (i = 0; i < keys->recv_len; i++)
+ printf(" %02x", keys->recv[i]);
+ printf("\n");
+ }
+
+ if (keys->send && keys->recv) {
+ free(sm->eapol_key_sign);
+ free(sm->eapol_key_crypt);
+ sm->eapol_key_sign = keys->send;
+ sm->eapol_key_sign_len = keys->send_len;
+ sm->eapol_key_crypt = keys->recv;
+ sm->eapol_key_crypt_len = keys->recv_len;
+ if (hapd->default_wep_key ||
+ hapd->conf->individual_wep_key_len > 0 ||
+ hapd->conf->wpa)
+ sta->eapol_sm->keyAvailable = TRUE;
+ } else {
+ free(keys->send);
+ free(keys->recv);
+ }
+ free(keys);
+ }
+}
+
+
+static void ieee802_1x_store_radius_class(struct hostapd_data *hapd,
+ struct sta_info *sta,
+ struct radius_msg *msg)
+{
+ u8 *class;
+ size_t class_len;
+ struct eapol_state_machine *sm = sta->eapol_sm;
+
+ if (!hapd->conf->acct_server || hapd->radius == NULL || sm == NULL)
+ return;
+
+ if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_CLASS, &class,
+ &class_len) < 0 ||
+ class_len < 1) {
+ free(sm->radius_class);
+ sm->radius_class = NULL;
+ sm->radius_class_len = 0;
+ return;
+ }
+
+ if (sm->radius_class == NULL ||
+ sm->radius_class_len < class_len) {
+ free(sm->radius_class);
+ sm->radius_class = malloc(class_len);
+ if (sm->radius_class == NULL) {
+ sm->radius_class_len = 0;
+ return;
+ }
+ }
+
+ memcpy(sm->radius_class, class, class_len);
+ sm->radius_class_len = class_len;
+}
+
+
+struct sta_id_search {
+ u8 identifier;
+ struct eapol_state_machine *sm;
+};
+
+
+static int ieee802_1x_select_radius_identifier(struct hostapd_data *hapd,
+ struct sta_info *sta,
+ void *ctx)
+{
+ struct sta_id_search *id_search = ctx;
+ struct eapol_state_machine *sm = sta->eapol_sm;
+
+ if (sm && sm->radius_identifier >= 0 &&
+ sm->radius_identifier == id_search->identifier) {
+ id_search->sm = sm;
+ return 1;
+ }
+ return 0;
+}
+
+
+static struct eapol_state_machine *
+ieee802_1x_search_radius_identifier(struct hostapd_data *hapd, u8 identifier)
+{
+ struct sta_id_search id_search;
+ id_search.identifier = identifier;
+ id_search.sm = NULL;
+ ap_for_each_sta(hapd, ieee802_1x_select_radius_identifier, &id_search);
+ return id_search.sm;
+}
+
+
+/* Process the RADIUS frames from Authentication Server */
+static RadiusRxResult
+ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req,
+ u8 *shared_secret, size_t shared_secret_len,
+ void *data)
+{
+ struct hostapd_data *hapd = data;
+ struct sta_info *sta;
+ u32 session_timeout, termination_action, acct_interim_interval;
+ int session_timeout_set;
+ int eap_timeout;
+ struct eapol_state_machine *sm;
+ int override_eapReq = 0;
+
+ sm = ieee802_1x_search_radius_identifier(hapd, msg->hdr->identifier);
+ if (sm == NULL) {
+ HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "IEEE 802.1X: Could not "
+ "find matching station for this RADIUS "
+ "message\n");
+ return RADIUS_RX_UNKNOWN;
+ }
+ sta = sm->sta;
+
+ /* 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) {
+ HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "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_INVALID_AUTHENTICATOR;
+ }
+
+ 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;
+ }
+
+ sm->radius_identifier = -1;
+ HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL,
+ "RADIUS packet matching with station " MACSTR "\n",
+ MAC2STR(sta->addr));
+
+ if (sm->last_recv_radius) {
+ radius_msg_free(sm->last_recv_radius);
+ free(sm->last_recv_radius);
+ }
+
+ sm->last_recv_radius = msg;
+
+ session_timeout_set =
+ !radius_msg_get_attr_int32(msg, RADIUS_ATTR_SESSION_TIMEOUT,
+ &session_timeout);
+ if (radius_msg_get_attr_int32(msg, RADIUS_ATTR_TERMINATION_ACTION,
+ &termination_action))
+ termination_action = RADIUS_TERMINATION_ACTION_DEFAULT;
+
+ if (hapd->conf->radius_acct_interim_interval == 0 &&
+ msg->hdr->code == RADIUS_CODE_ACCESS_ACCEPT &&
+ radius_msg_get_attr_int32(msg, RADIUS_ATTR_ACCT_INTERIM_INTERVAL,
+ &acct_interim_interval) == 0) {
+ if (acct_interim_interval < 60) {
+ hostapd_logger(hapd, sta->addr,
+ HOSTAPD_MODULE_IEEE8021X,
+ HOSTAPD_LEVEL_INFO,
+ "ignored too small "
+ "Acct-Interim-Interval %d",
+ acct_interim_interval);
+ } else
+ sta->acct_interim_interval = acct_interim_interval;
+ }
+
+
+ switch (msg->hdr->code) {
+ case RADIUS_CODE_ACCESS_ACCEPT:
+ /* RFC 3580, Ch. 3.17 */
+ if (session_timeout_set && termination_action ==
+ RADIUS_TERMINATION_ACTION_RADIUS_REQUEST) {
+ sm->reauth_timer.reAuthPeriod = session_timeout;
+ } else if (session_timeout_set)
+ ap_sta_session_timeout(hapd, sta, session_timeout);
+
+ sm->eapSuccess = TRUE;
+ override_eapReq = 1;
+ ieee802_1x_get_keys(hapd, sta, msg, req, shared_secret,
+ shared_secret_len);
+ if (sm->keyAvailable) {
+ pmksa_cache_add(hapd, sta, sm->eapol_key_crypt,
+ session_timeout_set ?
+ session_timeout : -1);
+ }
+ break;
+ case RADIUS_CODE_ACCESS_REJECT:
+ sm->eapFail = TRUE;
+ override_eapReq = 1;
+ break;
+ case RADIUS_CODE_ACCESS_CHALLENGE:
+ if (session_timeout_set) {
+ /* RFC 2869, Ch. 2.3.2; RFC 3580, Ch. 3.17 */
+ eap_timeout = session_timeout;
+ } else
+ eap_timeout = 30;
+ hostapd_logger(hapd, sm->addr, HOSTAPD_MODULE_IEEE8021X,
+ HOSTAPD_LEVEL_DEBUG,
+ "using EAP timeout of %d seconds%s",
+ eap_timeout,
+ session_timeout_set ? " (from RADIUS)" : "");
+ eloop_cancel_timeout(ieee802_1x_eap_timeout, sta, NULL);
+ eloop_register_timeout(eap_timeout, 0, ieee802_1x_eap_timeout,
+ sta, NULL);
+ sm->eapTimeout = FALSE;
+ break;
+ }
+
+ ieee802_1x_store_radius_class(hapd, sta, msg);
+ ieee802_1x_decapsulate_radius(hapd, sta);
+ if (override_eapReq)
+ sm->be_auth.eapReq = FALSE;
+
+ eapol_sm_step(sm);
+
+ return RADIUS_RX_QUEUED;
+}
+
+
+/* Handler for EAPOL Backend Authentication state machine sendRespToServer.
+ * Forward the EAP Response from Supplicant to Authentication Server. */
+void ieee802_1x_send_resp_to_server(hostapd *hapd, struct sta_info *sta)
+{
+ struct eapol_state_machine *sm = sta->eapol_sm;
+ if (sm == NULL)
+ return;
+
+ if (hapd->conf->eap_authenticator) {
+ eap_set_eapRespData(sm->eap, sm->last_eap_supp,
+ sm->last_eap_supp_len);
+ } else {
+ ieee802_1x_encapsulate_radius(hapd, sta, sm->last_eap_supp,
+ sm->last_eap_supp_len);
+ }
+}
+
+
+void ieee802_1x_abort_auth(struct hostapd_data *hapd, struct sta_info *sta)
+{
+ struct eapol_state_machine *sm = sta->eapol_sm;
+ if (sm == NULL)
+ return;
+
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
+ HOSTAPD_LEVEL_DEBUG, "aborting authentication");
+
+ if (sm->last_recv_radius) {
+ radius_msg_free(sm->last_recv_radius);
+ free(sm->last_recv_radius);
+ sm->last_recv_radius = NULL;
+ }
+ free(sm->last_eap_supp);
+ sm->last_eap_supp = NULL;
+ sm->last_eap_supp_len = 0;
+ free(sm->last_eap_radius);
+ sm->last_eap_radius = NULL;
+ sm->last_eap_radius_len = 0;
+ free(sm->radius_class);
+ sm->radius_class = NULL;
+ sm->radius_class_len = 0;
+}
+
+
+void ieee802_1x_set_port_enabled(hostapd *hapd, struct sta_info *sta,
+ int enabled)
+{
+ if (!sta->eapol_sm)
+ return;
+
+ HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL,
+ "IEEE 802.1X: station " MACSTR " port %s\n",
+ MAC2STR(sta->addr), enabled ? "enabled" : "disabled");
+ sta->eapol_sm->portEnabled = enabled ? TRUE : FALSE;
+ eapol_sm_step(sta->eapol_sm);
+}
+
+
+#ifdef HOSTAPD_DUMP_STATE
+void ieee802_1x_dump_state(FILE *f, const char *prefix, struct sta_info *sta)
+{
+ struct eapol_state_machine *sm = sta->eapol_sm;
+ if (sm == NULL)
+ return;
+
+ fprintf(f, "%sIEEE 802.1X:\n", prefix);
+
+ if (sm->identity) {
+ size_t i;
+ fprintf(f, "%sidentity=", prefix);
+ for (i = 0; i < sm->identity_len; i++)
+ fprint_char(f, sm->identity[i]);
+ fprintf(f, "\n");
+ }
+
+ fprintf(f, "%scached_packets=%s%s%s\n", prefix,
+ sm->last_recv_radius ? "[RX RADIUS]" : "",
+ sm->last_eap_radius ? "[EAP RADIUS]" : "",
+ sm->last_eap_supp ? "[EAP SUPPLICANT]" : "");
+
+ eapol_sm_dump_state(f, prefix, sm);
+}
+#endif /* HOSTAPD_DUMP_STATE */
+
+
+static int ieee802_1x_rekey_broadcast(hostapd *hapd)
+{
+ if (hapd->conf->default_wep_key_len < 1)
+ return 0;
+
+ free(hapd->default_wep_key);
+ hapd->default_wep_key = malloc(hapd->conf->default_wep_key_len);
+ if (hapd->default_wep_key == NULL ||
+ hostapd_get_rand(hapd->default_wep_key,
+ hapd->conf->default_wep_key_len)) {
+ printf("Could not generate random WEP key.\n");
+ free(hapd->default_wep_key);
+ hapd->default_wep_key = NULL;
+ return -1;
+ }
+
+ if (HOSTAPD_DEBUG_COND(HOSTAPD_DEBUG_MINIMAL)) {
+ hostapd_hexdump("IEEE 802.1X: New default WEP key",
+ hapd->default_wep_key,
+ hapd->conf->default_wep_key_len);
+ }
+
+ return 0;
+}
+
+
+static int ieee802_1x_sta_key_available(struct hostapd_data *hapd,
+ struct sta_info *sta, void *ctx)
+{
+ if (sta->eapol_sm) {
+ sta->eapol_sm->keyAvailable = TRUE;
+ eapol_sm_step(sta->eapol_sm);
+ }
+ return 0;
+}
+
+
+static void ieee802_1x_rekey(void *eloop_ctx, void *timeout_ctx)
+{
+ struct hostapd_data *hapd = eloop_ctx;
+
+ if (hapd->default_wep_key_idx >= 3)
+ hapd->default_wep_key_idx =
+ hapd->conf->individual_wep_key_len > 0 ? 1 : 0;
+ else
+ hapd->default_wep_key_idx++;
+
+ HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "IEEE 802.1X: New default WEP "
+ "key index %d\n", hapd->default_wep_key_idx);
+
+ if (ieee802_1x_rekey_broadcast(hapd)) {
+ hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE8021X,
+ HOSTAPD_LEVEL_WARNING, "failed to generate a "
+ "new broadcast key");
+ free(hapd->default_wep_key);
+ hapd->default_wep_key = NULL;
+ return;
+ }
+
+ /* TODO: Could setup key for RX here, but change default TX keyid only
+ * after new broadcast key has been sent to all stations. */
+ if (hostapd_set_encryption(hapd, "WEP", NULL,
+ hapd->default_wep_key_idx,
+ hapd->default_wep_key,
+ hapd->conf->default_wep_key_len)) {
+ hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE8021X,
+ HOSTAPD_LEVEL_WARNING, "failed to configure a "
+ "new broadcast key");
+ free(hapd->default_wep_key);
+ hapd->default_wep_key = NULL;
+ return;
+ }
+
+ ap_for_each_sta(hapd, ieee802_1x_sta_key_available, NULL);
+
+ if (hapd->conf->wep_rekeying_period > 0) {
+ eloop_register_timeout(hapd->conf->wep_rekeying_period, 0,
+ ieee802_1x_rekey, hapd, NULL);
+ }
+}
+
+
+int ieee802_1x_init(hostapd *hapd)
+{
+ int i;
+
+ if ((hapd->conf->ieee802_1x || hapd->conf->wpa) &&
+ hostapd_set_ieee8021x(hapd, 1))
+ return -1;
+
+ if (radius_client_register(hapd->radius, RADIUS_AUTH,
+ ieee802_1x_receive_auth, hapd))
+ return -1;
+
+ if (hapd->conf->default_wep_key_len) {
+ for (i = 0; i < 4; i++)
+ hostapd_set_encryption(hapd, "none", NULL, i, NULL, 0);
+
+ ieee802_1x_rekey(hapd, NULL);
+
+ if (hapd->default_wep_key == NULL)
+ return -1;
+ }
+
+ return 0;
+}
+
+
+void ieee802_1x_deinit(hostapd *hapd)
+{
+ if (hapd->driver != NULL &&
+ (hapd->conf->ieee802_1x || hapd->conf->wpa))
+ hostapd_set_ieee8021x(hapd, 0);
+}
+
+
+static void ieee802_1x_new_auth_session(struct hostapd_data *hapd,
+ struct sta_info *sta)
+{
+ struct eapol_state_machine *sm = sta->eapol_sm;
+ if (sm == NULL)
+ return;
+
+ HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL,
+ "IEEE 802.1X: station " MACSTR " - new auth session, "
+ "clearing State\n", MAC2STR(sta->addr));
+
+ if (sm->last_recv_radius) {
+ radius_msg_free(sm->last_recv_radius);
+ free(sm->last_recv_radius);
+ sm->last_recv_radius = NULL;
+ }
+
+ sm->eapSuccess = FALSE;
+ sm->eapFail = FALSE;
+}
+
+
+int ieee802_1x_tx_status(hostapd *hapd, struct sta_info *sta, u8 *buf,
+ size_t len, int ack)
+{
+ struct ieee80211_hdr *hdr;
+ struct ieee802_1x_hdr *xhdr;
+ struct ieee802_1x_eapol_key *key;
+ u8 *pos;
+
+ if (sta == NULL)
+ return -1;
+ if (len < sizeof(*hdr) + sizeof(rfc1042_header) + 2 + sizeof(*xhdr))
+ return 0;
+
+ hdr = (struct ieee80211_hdr *) buf;
+ pos = (u8 *) (hdr + 1);
+ if (memcmp(pos, rfc1042_header, sizeof(rfc1042_header)) != 0)
+ return 0;
+ pos += sizeof(rfc1042_header);
+ if (((pos[0] << 8) | pos[1]) != ETH_P_PAE)
+ return 0;
+ pos += 2;
+
+ xhdr = (struct ieee802_1x_hdr *) pos;
+ pos += sizeof(*xhdr);
+
+ HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "IEEE 802.1X: " MACSTR
+ " TX status - version=%d type=%d length=%d - ack=%d\n",
+ MAC2STR(sta->addr), xhdr->version, xhdr->type,
+ ntohs(xhdr->length), ack);
+
+ /* EAPOL EAP-Packet packets are eventually re-sent by either Supplicant
+ * or Authenticator state machines, but EAPOL-Key packets are not
+ * retransmitted in case of failure. Try to re-sent failed EAPOL-Key
+ * packets couple of times because otherwise STA keys become
+ * unsynchronized with AP. */
+ if (xhdr->type == IEEE802_1X_TYPE_EAPOL_KEY && !ack &&
+ pos + sizeof(*key) <= buf + len) {
+ key = (struct ieee802_1x_eapol_key *) pos;
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
+ HOSTAPD_LEVEL_DEBUG, "did not Ack EAPOL-Key "
+ "frame (%scast index=%d)",
+ key->key_index & BIT(7) ? "uni" : "broad",
+ key->key_index & ~BIT(7));
+ /* TODO: re-send EAPOL-Key couple of times (with short delay
+ * between them?). If all attempt fail, report error and
+ * deauthenticate STA so that it will get new keys when
+ * authenticating again (e.g., after returning in range).
+ * Separate limit/transmit state needed both for unicast and
+ * broadcast keys(?) */
+ }
+ /* TODO: could move unicast key configuration from ieee802_1x_tx_key()
+ * to here and change the key only if the EAPOL-Key packet was Acked.
+ */
+
+ return 1;
+}
+
+
+u8 * ieee802_1x_get_identity(struct eapol_state_machine *sm, size_t *len)
+{
+ if (sm == NULL || sm->identity == NULL)
+ return NULL;
+
+ *len = sm->identity_len;
+ return sm->identity;
+}
+
+
+u8 * ieee802_1x_get_radius_class(struct eapol_state_machine *sm, size_t *len)
+{
+ if (sm == NULL || sm->radius_class == NULL)
+ return NULL;
+
+ *len = sm->radius_class_len;
+ return sm->radius_class;
+}
+
+
+u8 * ieee802_1x_get_key_crypt(struct eapol_state_machine *sm, size_t *len)
+{
+ if (sm == NULL)
+ return NULL;
+
+ *len = sm->eapol_key_crypt_len;
+ return sm->eapol_key_crypt;
+}
+
+
+void ieee802_1x_notify_port_enabled(struct eapol_state_machine *sm,
+ int enabled)
+{
+ if (sm == NULL)
+ return;
+ sm->portEnabled = enabled ? TRUE : FALSE;
+}
+
+
+void ieee802_1x_notify_port_valid(struct eapol_state_machine *sm,
+ int valid)
+{
+ if (sm == NULL)
+ return;
+ sm->portValid = valid ? TRUE : FALSE;
+}
+
+
+void ieee802_1x_notify_pre_auth(struct eapol_state_machine *sm, int pre_auth)
+{
+ if (sm == NULL)
+ return;
+ if (pre_auth)
+ sm->flags |= EAPOL_SM_PREAUTH;
+ else
+ sm->flags &= ~EAPOL_SM_PREAUTH;
+}
+
+
+static const char * bool_txt(Boolean bool)
+{
+ return bool ? "TRUE" : "FALSE";
+}
+
+
+int ieee802_1x_get_mib(struct hostapd_data *hapd, char *buf, size_t buflen)
+{
+ /* TODO */
+ return 0;
+}
+
+
+int ieee802_1x_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta,
+ char *buf, size_t buflen)
+{
+ int len = 0;
+ struct eapol_state_machine *sm = sta->eapol_sm;
+
+ if (sm == NULL)
+ return 0;
+
+ len += snprintf(buf + len, buflen - len,
+ "dot1xPaePortNumber=%d\n"
+ "dot1xPaePortProtocolVersion=%d\n"
+ "dot1xPaePortCapabilities=1\n"
+ "dot1xPaePortInitialize=%d\n"
+ "dot1xPaePortReauthenticate=FALSE\n",
+ sta->aid,
+ EAPOL_VERSION,
+ sm->initialize);
+
+ /* dot1xAuthConfigTable */
+ len += snprintf(buf + len, buflen - len,
+ "dot1xAuthPaeState=%d\n"
+ "dot1xAuthBackendAuthState=%d\n"
+ "dot1xAuthAdminControlledDirections=%d\n"
+ "dot1xAuthOperControlledDirections=%d\n"
+ "dot1xAuthAuthControlledPortStatus=%d\n"
+ "dot1xAuthAuthControlledPortControl=%d\n"
+ "dot1xAuthQuietPeriod=%u\n"
+ "dot1xAuthServerTimeout=%u\n"
+ "dot1xAuthReAuthPeriod=%u\n"
+ "dot1xAuthReAuthEnabled=%s\n"
+ "dot1xAuthKeyTxEnabled=%s\n",
+ sm->auth_pae.state + 1,
+ sm->be_auth.state + 1,
+ sm->ctrl_dir.adminControlledDirections,
+ sm->ctrl_dir.operControlledDirections,
+ sm->authPortStatus,
+ sm->portControl,
+ sm->auth_pae.quietPeriod,
+ sm->be_auth.serverTimeout,
+ sm->reauth_timer.reAuthPeriod,
+ bool_txt(sm->reauth_timer.reAuthEnabled),
+ bool_txt(sm->keyTxEnabled));
+
+ /* dot1xAuthStatsTable */
+ len += snprintf(buf + len, buflen - len,
+ "dot1xAuthEapolFramesRx=%u\n"
+ "dot1xAuthEapolFramesTx=%u\n"
+ "dot1xAuthEapolStartFramesRx=%u\n"
+ "dot1xAuthEapolLogoffFramesRx=%u\n"
+ "dot1xAuthEapolRespIdFramesRx=%u\n"
+ "dot1xAuthEapolRespFramesRx=%u\n"
+ "dot1xAuthEapolReqIdFramesTx=%u\n"
+ "dot1xAuthEapolReqFramesTx=%u\n"
+ "dot1xAuthInvalidEapolFramesRx=%u\n"
+ "dot1xAuthEapLengthErrorFramesRx=%u\n"
+ "dot1xAuthLastEapolFrameVersion=%u\n"
+ "dot1xAuthLastEapolFrameSource=" MACSTR "\n",
+ sm->dot1xAuthEapolFramesRx,
+ sm->dot1xAuthEapolFramesTx,
+ sm->dot1xAuthEapolStartFramesRx,
+ sm->dot1xAuthEapolLogoffFramesRx,
+ sm->dot1xAuthEapolRespIdFramesRx,
+ sm->dot1xAuthEapolRespFramesRx,
+ sm->dot1xAuthEapolReqIdFramesTx,
+ sm->dot1xAuthEapolReqFramesTx,
+ sm->dot1xAuthInvalidEapolFramesRx,
+ sm->dot1xAuthEapLengthErrorFramesRx,
+ sm->dot1xAuthLastEapolFrameVersion,
+ MAC2STR(sm->addr));
+
+ /* dot1xAuthDiagTable */
+ len += snprintf(buf + len, buflen - len,
+ "dot1xAuthEntersConnecting=%u\n"
+ "dot1xAuthEapLogoffsWhileConnecting=%u\n"
+ "dot1xAuthEntersAuthenticating=%u\n"
+ "dot1xAuthAuthSuccessesWhileAuthenticating=%u\n"
+ "dot1xAuthAuthTimeoutsWhileAuthenticating=%u\n"
+ "dot1xAuthAuthFailWhileAuthenticating=%u\n"
+ "dot1xAuthAuthEapStartsWhileAuthenticating=%u\n"
+ "dot1xAuthAuthEapLogoffWhileAuthenticating=%u\n"
+ "dot1xAuthAuthReauthsWhileAuthenticated=%u\n"
+ "dot1xAuthAuthEapStartsWhileAuthenticated=%u\n"
+ "dot1xAuthAuthEapLogoffWhileAuthenticated=%u\n"
+ "dot1xAuthBackendResponses=%u\n"
+ "dot1xAuthBackendAccessChallenges=%u\n"
+ "dot1xAuthBackendOtherRequestsToSupplicant=%u\n"
+ "dot1xAuthBackendAuthSuccesses=%u\n"
+ "dot1xAuthBackendAuthFails=%u\n",
+ sm->auth_pae.authEntersConnecting,
+ sm->auth_pae.authEapLogoffsWhileConnecting,
+ sm->auth_pae.authEntersAuthenticating,
+ sm->auth_pae.authAuthSuccessesWhileAuthenticating,
+ sm->auth_pae.authAuthTimeoutsWhileAuthenticating,
+ sm->auth_pae.authAuthFailWhileAuthenticating,
+ sm->auth_pae.authAuthEapStartsWhileAuthenticating,
+ sm->auth_pae.authAuthEapLogoffWhileAuthenticating,
+ sm->auth_pae.authAuthReauthsWhileAuthenticated,
+ sm->auth_pae.authAuthEapStartsWhileAuthenticated,
+ sm->auth_pae.authAuthEapLogoffWhileAuthenticated,
+ sm->be_auth.backendResponses,
+ sm->be_auth.backendAccessChallenges,
+ sm->be_auth.backendOtherRequestsToSupplicant,
+ sm->be_auth.backendAuthSuccesses,
+ sm->be_auth.backendAuthFails);
+
+ /* dot1xAuthSessionStatsTable */
+ len += snprintf(buf + len, buflen - len,
+ /* TODO: dot1xAuthSessionOctetsRx */
+ /* TODO: dot1xAuthSessionOctetsTx */
+ /* TODO: dot1xAuthSessionFramesRx */
+ /* TODO: dot1xAuthSessionFramesTx */
+ "dot1xAuthSessionId=%08X-%08X\n"
+ "dot1xAuthSessionAuthenticMethod=%d\n"
+ "dot1xAuthSessionTime=%u\n"
+ "dot1xAuthSessionTerminateCause=999\n"
+ "dot1xAuthSessionUserName=%s\n",
+ sta->acct_session_id_hi, sta->acct_session_id_lo,
+ sta->wpa_key_mgmt == WPA_KEY_MGMT_IEEE8021X ? 1 : 2,
+ (unsigned int) (time(NULL) - sta->acct_session_start),
+ sm->identity);
+
+ return len;
+}
diff --git a/contrib/hostapd/ieee802_1x.h b/contrib/hostapd/ieee802_1x.h
new file mode 100644
index 000000000000..3916138c0b41
--- /dev/null
+++ b/contrib/hostapd/ieee802_1x.h
@@ -0,0 +1,84 @@
+#ifndef IEEE802_1X_H
+#define IEEE802_1X_H
+
+/* IEEE Std 802.1X-REV-d11, 7.2 */
+
+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
+};
+
+/* draft-congdon-radius-8021x-20.txt */
+
+struct ieee802_1x_eapol_key {
+ u8 type;
+ u16 key_length;
+ u8 replay_counter[8]; /* does not repeat within the life of the keying
+ * material used to encrypt the Key field;
+ * 64-bit NTP timestamp MAY be used here */
+ u8 key_iv[16]; /* cryptographically random number */
+ u8 key_index; /* key flag in the most significant bit:
+ * 0 = broadcast (default key),
+ * 1 = unicast (key mapping key); key index is in the
+ * 7 least significant bits */
+ u8 key_signature[16]; /* HMAC-MD5 message integrity check computed with
+ * MS-MPPE-Send-Key as the key */
+
+ /* followed by key: if packet body length = 44 + key length, then the
+ * key field (of key_length bytes) contains the key in encrypted form;
+ * if packet body length = 44, key field is absent and key_length
+ * represents the number of least significant octets from
+ * MS-MPPE-Send-Key attribute to be used as the keying material;
+ * RC4 key used in encryption = Key-IV + MS-MPPE-Recv-Key */
+} __attribute__ ((packed));
+
+enum { EAPOL_KEY_TYPE_RC4 = 1, EAPOL_KEY_TYPE_RSN = 2,
+ EAPOL_KEY_TYPE_WPA = 254 };
+
+
+void ieee802_1x_receive(hostapd *hapd, u8 *sa, u8 *buf, size_t len);
+void ieee802_1x_new_station(hostapd *hapd, struct sta_info *sta);
+void ieee802_1x_free_station(struct sta_info *sta);
+
+void ieee802_1x_request_identity(struct hostapd_data *hapd,
+ struct sta_info *sta);
+void ieee802_1x_tx_canned_eap(struct hostapd_data *hapd, struct sta_info *sta,
+ int success);
+void ieee802_1x_tx_req(hostapd *hapd, struct sta_info *sta);
+void ieee802_1x_tx_key(struct hostapd_data *hapd, struct sta_info *sta);
+void ieee802_1x_send_resp_to_server(hostapd *hapd, struct sta_info *sta);
+void ieee802_1x_abort_auth(struct hostapd_data *hapd, struct sta_info *sta);
+void ieee802_1x_set_sta_authorized(hostapd *hapd, struct sta_info *sta,
+ int authorized);
+void ieee802_1x_set_port_enabled(hostapd *hapd, struct sta_info *sta,
+ int enabled);
+void ieee802_1x_dump_state(FILE *f, const char *prefix, struct sta_info *sta);
+int ieee802_1x_init(hostapd *hapd);
+void ieee802_1x_deinit(hostapd *hapd);
+int ieee802_1x_tx_status(hostapd *hapd, struct sta_info *sta, u8 *buf,
+ size_t len, int ack);
+u8 * ieee802_1x_get_identity(struct eapol_state_machine *sm, size_t *len);
+u8 * ieee802_1x_get_radius_class(struct eapol_state_machine *sm, size_t *len);
+u8 * ieee802_1x_get_key_crypt(struct eapol_state_machine *sm, size_t *len);
+void ieee802_1x_notify_port_enabled(struct eapol_state_machine *sm,
+ int enabled);
+void ieee802_1x_notify_port_valid(struct eapol_state_machine *sm,
+ int valid);
+void ieee802_1x_notify_pre_auth(struct eapol_state_machine *sm, int pre_auth);
+int ieee802_1x_get_mib(struct hostapd_data *hapd, char *buf, size_t buflen);
+int ieee802_1x_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta,
+ char *buf, size_t buflen);
+void hostapd_get_ntp_timestamp(u8 *buf);
+
+#endif /* IEEE802_1X_H */
diff --git a/contrib/hostapd/l2_packet.h b/contrib/hostapd/l2_packet.h
new file mode 100644
index 000000000000..3e3914ca5502
--- /dev/null
+++ b/contrib/hostapd/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/hostapd/madwifi.conf b/contrib/hostapd/madwifi.conf
new file mode 100644
index 000000000000..f72750e3d7b6
--- /dev/null
+++ b/contrib/hostapd/madwifi.conf
@@ -0,0 +1,201 @@
+##### hostapd configuration file ##############################################
+# Empty lines and lines starting with # are ignored
+
+# AP netdevice name (without 'ap' prefix, i.e., wlan0 uses wlan0ap for
+# management frames)
+interface=ath0
+bridge=br0
+
+# Driver interface type (hostap/wired/madwifi; default: hostap)
+driver=madwifi
+
+# hostapd event logger configuration
+#
+# Two output method: syslog and stdout (only usable if not forking to
+# background).
+#
+# Module bitfield (ORed bitfield of modules that will be logged; -1 = all
+# modules):
+# bit 0 (1) = IEEE 802.11
+# bit 1 (2) = IEEE 802.1X
+# bit 2 (4) = RADIUS
+# bit 3 (8) = WPA
+# bit 4 (16) = driver interface
+#
+# Levels (minimum value for logged events):
+# 0 = verbose debugging
+# 1 = debugging
+# 2 = informational messages
+# 3 = notification
+# 4 = warning
+#
+logger_syslog=-1
+logger_syslog_level=2
+logger_stdout=-1
+logger_stdout_level=2
+
+# Debugging: 0 = no, 1 = minimal, 2 = verbose, 3 = msg dumps, 4 = excessive
+debug=0
+
+# Dump file for state information (on SIGUSR1)
+dump_file=/tmp/hostapd.dump
+
+
+##### IEEE 802.11 related configuration #######################################
+
+# SSID to be used in IEEE 802.11 management frames
+ssid=wpa-test
+
+##### IEEE 802.1X (and IEEE 802.1aa/D4) related configuration #################
+
+# Require IEEE 802.1X authorization
+#ieee8021x=1
+
+# Use internal minimal EAP Authentication Server for testing IEEE 802.1X.
+# This should only be used for testing since it authorizes all users that
+# support IEEE 802.1X without any keys or certificates. Please also note that
+# the EAP method used with this minimal server does not generate any keying
+# material and as such, it cannot be used with dynamic WEP keying
+# (wep_key_len_broadcast and wep_key_len_unicast).
+minimal_eap=0
+
+# Optional displayable message sent with EAP Request-Identity
+eap_message=hello
+
+# WEP rekeying (disabled if key lengths are not set or are set to 0)
+# Key lengths for default/broadcast and individual/unicast keys:
+# 5 = 40-bit WEP (also known as 64-bit WEP with 40 secret bits)
+# 13 = 104-bit WEP (also known as 128-bit WEP with 104 secret bits)
+#wep_key_len_broadcast=5
+#wep_key_len_unicast=5
+# Rekeying period in seconds. 0 = do not rekey (i.e., set keys only once)
+#wep_rekey_period=300
+
+# EAPOL-Key index workaround (set bit7) for WinXP Supplicant (needed only if
+# only broadcast keys are used)
+eapol_key_index_workaround=0
+
+# EAP reauthentication period in seconds (default: 3600 seconds; 0 = disable
+# reauthentication).
+#eap_reauth_period=3600
+
+##### IEEE 802.11f - Inter-Access Point Protocol (IAPP) #######################
+
+# Interface to be used for IAPP broadcast packets
+#iapp_interface=eth0
+
+
+##### RADIUS configuration ####################################################
+# for IEEE 802.1X with external Authentication Server, IEEE 802.11
+# authentication with external ACL for MAC addresses, and accounting
+
+# The own IP address of the access point (used as NAS-IP-Address)
+own_ip_addr=127.0.0.1
+
+# Optional NAS-Identifier string for RADIUS messages. When used, this should be
+# a unique to the NAS within the scope of the RADIUS server. For example, a
+# fully qualified domain name can be used here.
+#nas_identifier=ap.example.com
+
+# RADIUS authentication server
+#auth_server_addr=127.0.0.1
+#auth_server_port=1812
+#auth_server_shared_secret=secret
+
+# RADIUS accounting server
+#acct_server_addr=127.0.0.1
+#acct_server_port=1813
+#acct_server_shared_secret=secret
+
+# Secondary RADIUS servers; to be used if primary one does not reply to
+# RADIUS packets. These are optional and there can be more than one secondary
+# server listed.
+#auth_server_addr=127.0.0.2
+#auth_server_port=1812
+#auth_server_shared_secret=secret2
+#
+#acct_server_addr=127.0.0.2
+#acct_server_port=1813
+#acct_server_shared_secret=secret2
+
+# Retry interval for trying to return to the primary RADIUS server (in
+# seconds). RADIUS client code will automatically try to use the next server
+# when the current server is not replying to requests. If this interval is set,
+# primary server will be retried after configured amount of time even if the
+# currently used secondary server is still working.
+#radius_retry_primary_interval=600
+
+
+# Interim accounting update interval
+# If this is set (larger than 0) and acct_server is configured, hostapd will
+# send interim accounting updates every N seconds. Note: if set, this overrides
+# possible Acct-Interim-Interval attribute in Access-Accept message. Thus, this
+# value should not be configured in hostapd.conf, if RADIUS server is used to
+# control the interim interval.
+# This value should not be less 600 (10 minutes) and must not be less than
+# 60 (1 minute).
+#radius_acct_interim_interval=600
+
+
+##### WPA/IEEE 802.11i configuration ##########################################
+
+# Enable WPA. Setting this variable configures the AP to require WPA (either
+# WPA-PSK or WPA-RADIUS/EAP based on other configuration). For WPA-PSK, either
+# wpa_psk or wpa_passphrase must be set and wpa_key_mgmt must include WPA-PSK.
+# For WPA-RADIUS/EAP, ieee8021x must be set (but without dynamic WEP keys),
+# RADIUS authentication server must be configured, and WPA-EAP must be included
+# in wpa_key_mgmt.
+# This field is a bit field that can be used to enable WPA (IEEE 802.11i/D3.0)
+# and/or WPA2 (full IEEE 802.11i/RSN):
+# bit0 = WPA
+# bit1 = IEEE 802.11i/RSN (WPA2)
+#wpa=1
+
+# WPA pre-shared keys for WPA-PSK. This can be either entered as a 256-bit
+# secret in hex format (64 hex digits), wpa_psk, or as an ASCII passphrase
+# (8..63 characters) that will be converted to PSK. This conversion uses SSID
+# so the PSK changed when ASCII passphrase is used and the SSID is changed.
+#wpa_psk=0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
+#wpa_passphrase=secret passphrase
+
+# Optionally, WPA PSKs can be read from a separate text file (containing list
+# of (PSK,MAC address) pairs. This allows more than one PSK to be configured.
+# Use absolute path name to make sure that the files can be read on SIGHUP
+# configuration reloads.
+#wpa_psk_file=/etc/hostapd.wpa_psk
+
+# Set of accepted key management algorithms (WPA-PSK, WPA-EAP, or both). The
+# entries are separated with a space.
+#wpa_key_mgmt=WPA-PSK WPA-EAP
+
+# Set of accepted cipher suites (encryption algorithms) for pairwise keys
+# (unicast packets). This is a space separated list of algorithms:
+# CCMP = AES in Counter mode with CBC-MAC [RFC 3610, IEEE 802.11i/D7.0]
+# TKIP = Temporal Key Integrity Protocol [IEEE 802.11i/D7.0]
+# Group cipher suite (encryption algorithm for broadcast and multicast frames)
+# is automatically selected based on this configuration. If only CCMP is
+# allowed as the pairwise cipher, group cipher will also be CCMP. Otherwise,
+# TKIP will be used as the group cipher.
+#wpa_pairwise=TKIP CCMP
+
+# Time interval for rekeying GTK (broadcast/multicast encryption keys) in
+# seconds.
+#wpa_group_rekey=600
+
+# Time interval for rekeying GMK (master key used internally to generate GTKs
+# (in seconds).
+#wpa_gmk_rekey=86400
+
+# Enable IEEE 802.11i/RSN/WPA2 pre-authentication. This is used to speed up
+# roaming be pre-authenticating IEEE 802.1X/EAP part of the full RSN
+# authentication and key handshake before actually associating with a new AP.
+#rsn_preauth=1
+#
+# Space separated list of interfaces from which pre-authentication frames are
+# accepted (e.g., 'eth0' or 'eth0 wlan0wds0'. This list should include all
+# interface that are used for connections to other APs. This could include
+# wired interfaces and WDS links. The normal wireless data interface towards
+# associated stations (e.g., wlan0) should not be added, since
+# pre-authentication is only used with APs other than the currently associated
+# one.
+#rsn_preauth_interfaces=eth0
diff --git a/contrib/hostapd/md5.c b/contrib/hostapd/md5.c
new file mode 100644
index 000000000000..1564e8f64797
--- /dev/null
+++ b/contrib/hostapd/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/hostapd/md5.h b/contrib/hostapd/md5.h
new file mode 100644
index 000000000000..cc3eb950b507
--- /dev/null
+++ b/contrib/hostapd/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/hostapd/ms_funcs.c b/contrib/hostapd/ms_funcs.c
new file mode 100644
index 000000000000..9c50185ca1d9
--- /dev/null
+++ b/contrib/hostapd/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/hostapd/ms_funcs.h b/contrib/hostapd/ms_funcs.h
new file mode 100644
index 000000000000..a08ab06d72be
--- /dev/null
+++ b/contrib/hostapd/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/hostapd/radius.c b/contrib/hostapd/radius.c
new file mode 100644
index 000000000000..5fd323d65c85
--- /dev/null
+++ b/contrib/hostapd/radius.c
@@ -0,0 +1,1125 @@
+/*
+ * Host AP (software wireless LAN access point) user space daemon for
+ * Host AP kernel driver / RADIUS client
+ * 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 <unistd.h>
+#include <netinet/in.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <signal.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+
+
+#include "common.h"
+#include "radius.h"
+#include "md5.h"
+
+
+struct radius_msg *radius_msg_new(u8 code, u8 identifier)
+{
+ struct radius_msg *msg;
+
+ msg = (struct radius_msg *) malloc(sizeof(*msg));
+ if (msg == NULL)
+ return NULL;
+
+ if (radius_msg_initialize(msg, RADIUS_DEFAULT_MSG_SIZE)) {
+ free(msg);
+ return NULL;
+ }
+
+ radius_msg_set_hdr(msg, code, identifier);
+
+ return msg;
+}
+
+
+int radius_msg_initialize(struct radius_msg *msg, size_t init_len)
+{
+ if (msg == NULL || init_len < sizeof(struct radius_hdr))
+ return -1;
+
+ memset(msg, 0, sizeof(*msg));
+ msg->buf = (unsigned char *) malloc(init_len);
+ if (msg->buf == NULL)
+ return -1;
+ memset(msg->buf, 0, init_len);
+
+ msg->buf_size = init_len;
+ msg->hdr = (struct radius_hdr *) msg->buf;
+ msg->buf_used = sizeof(*msg->hdr);
+
+ msg->attrs = (struct radius_attr_hdr **)
+ malloc(RADIUS_DEFAULT_ATTR_COUNT * sizeof(*msg->attrs));
+ if (msg->attrs == NULL) {
+ free(msg->buf);
+ msg->buf = NULL;
+ msg->hdr = NULL;
+ return -1;
+ }
+
+ msg->attr_size = RADIUS_DEFAULT_ATTR_COUNT;
+ msg->attr_used = 0;
+
+ return 0;
+}
+
+
+void radius_msg_set_hdr(struct radius_msg *msg, u8 code, u8 identifier)
+{
+ msg->hdr->code = code;
+ msg->hdr->identifier = identifier;
+}
+
+
+void radius_msg_free(struct radius_msg *msg)
+{
+ if (msg->buf != NULL) {
+ free(msg->buf);
+ msg->buf = NULL;
+ msg->hdr = NULL;
+ }
+ msg->buf_size = msg->buf_used = 0;
+
+ if (msg->attrs != NULL) {
+ free(msg->attrs);
+ msg->attrs = NULL;
+ }
+ msg->attr_size = msg->attr_used = 0;
+}
+
+
+static const char *radius_code_string(u8 code)
+{
+ switch (code) {
+ case RADIUS_CODE_ACCESS_REQUEST: return "Access-Request";
+ case RADIUS_CODE_ACCESS_ACCEPT: return "Access-Accept";
+ case RADIUS_CODE_ACCESS_REJECT: return "Access-Reject";
+ case RADIUS_CODE_ACCOUNTING_REQUEST: return "Accounting-Request";
+ case RADIUS_CODE_ACCOUNTING_RESPONSE: return "Accounting-Response";
+ case RADIUS_CODE_ACCESS_CHALLENGE: return "Access-Challenge";
+ case RADIUS_CODE_STATUS_SERVER: return "Status-Server";
+ case RADIUS_CODE_STATUS_CLIENT: return "Status-Client";
+ case RADIUS_CODE_RESERVED: return "Reserved";
+ default: return "?Unknown?";
+ }
+}
+
+
+struct radius_attr_type {
+ u8 type;
+ char *name;
+ enum { RADIUS_ATTR_UNDIST, RADIUS_ATTR_TEXT, RADIUS_ATTR_IP,
+ RADIUS_ATTR_HEXDUMP, RADIUS_ATTR_INT32 } data_type;
+};
+
+static struct radius_attr_type radius_attrs[] =
+{
+ { RADIUS_ATTR_USER_NAME, "User-Name", RADIUS_ATTR_TEXT },
+ { RADIUS_ATTR_USER_PASSWORD, "User-Password", RADIUS_ATTR_UNDIST },
+ { RADIUS_ATTR_NAS_IP_ADDRESS, "NAS-IP-Address", RADIUS_ATTR_IP },
+ { RADIUS_ATTR_NAS_PORT, "NAS-Port", RADIUS_ATTR_INT32 },
+ { RADIUS_ATTR_FRAMED_MTU, "Framed-MTU", RADIUS_ATTR_INT32 },
+ { RADIUS_ATTR_STATE, "State", RADIUS_ATTR_UNDIST },
+ { RADIUS_ATTR_CLASS, "Class", RADIUS_ATTR_UNDIST },
+ { RADIUS_ATTR_VENDOR_SPECIFIC, "Vendor-Specific", RADIUS_ATTR_UNDIST },
+ { RADIUS_ATTR_SESSION_TIMEOUT, "Session-Timeout", RADIUS_ATTR_INT32 },
+ { RADIUS_ATTR_IDLE_TIMEOUT, "Idle-Timeout", RADIUS_ATTR_INT32 },
+ { RADIUS_ATTR_TERMINATION_ACTION, "Termination-Action",
+ RADIUS_ATTR_INT32 },
+ { RADIUS_ATTR_CALLED_STATION_ID, "Called-Station-Id",
+ RADIUS_ATTR_TEXT },
+ { RADIUS_ATTR_CALLING_STATION_ID, "Calling-Station-Id",
+ RADIUS_ATTR_TEXT },
+ { RADIUS_ATTR_NAS_IDENTIFIER, "NAS-Identifier", RADIUS_ATTR_TEXT },
+ { RADIUS_ATTR_ACCT_STATUS_TYPE, "Acct-Status-Type",
+ RADIUS_ATTR_INT32 },
+ { RADIUS_ATTR_ACCT_DELAY_TIME, "Acct-Delay-Time", RADIUS_ATTR_INT32 },
+ { RADIUS_ATTR_ACCT_INPUT_OCTETS, "Acct-Input-Octets",
+ RADIUS_ATTR_INT32 },
+ { RADIUS_ATTR_ACCT_OUTPUT_OCTETS, "Acct-Output-Octets",
+ RADIUS_ATTR_INT32 },
+ { RADIUS_ATTR_ACCT_SESSION_ID, "Acct-Session-Id", RADIUS_ATTR_TEXT },
+ { RADIUS_ATTR_ACCT_AUTHENTIC, "Acct-Authentic", RADIUS_ATTR_INT32 },
+ { RADIUS_ATTR_ACCT_SESSION_TIME, "Acct-Session-Time",
+ RADIUS_ATTR_INT32 },
+ { RADIUS_ATTR_ACCT_INPUT_PACKETS, "Acct-Input-Packets",
+ RADIUS_ATTR_INT32 },
+ { RADIUS_ATTR_ACCT_OUTPUT_PACKETS, "Acct-Output-Packets",
+ RADIUS_ATTR_INT32 },
+ { RADIUS_ATTR_ACCT_TERMINATE_CAUSE, "Acct-Terminate-Cause",
+ RADIUS_ATTR_INT32 },
+ { RADIUS_ATTR_ACCT_MULTI_SESSION_ID, "Acct-Multi-Session-Id",
+ RADIUS_ATTR_TEXT },
+ { RADIUS_ATTR_ACCT_LINK_COUNT, "Acct-Link-Count", RADIUS_ATTR_INT32 },
+ { RADIUS_ATTR_ACCT_INPUT_GIGAWORDS, "Acct-Input-Gigawords",
+ RADIUS_ATTR_INT32 },
+ { RADIUS_ATTR_ACCT_OUTPUT_GIGAWORDS, "Acct-Output-Gigawords",
+ RADIUS_ATTR_INT32 },
+ { RADIUS_ATTR_EVENT_TIMESTAMP, "Event-Timestamp",
+ RADIUS_ATTR_INT32 },
+ { RADIUS_ATTR_NAS_PORT_TYPE, "NAS-Port-Type", RADIUS_ATTR_INT32 },
+ { RADIUS_ATTR_CONNECT_INFO, "Connect-Info", RADIUS_ATTR_TEXT },
+ { RADIUS_ATTR_EAP_MESSAGE, "EAP-Message", RADIUS_ATTR_UNDIST },
+ { RADIUS_ATTR_MESSAGE_AUTHENTICATOR, "Message-Authenticator",
+ RADIUS_ATTR_UNDIST },
+ { RADIUS_ATTR_ACCT_INTERIM_INTERVAL, "Acct-Interim-Interval",
+ RADIUS_ATTR_INT32 }
+
+};
+#define RADIUS_ATTRS (sizeof(radius_attrs) / sizeof(radius_attrs[0]))
+
+
+static struct radius_attr_type *radius_get_attr_type(u8 type)
+{
+ int i;
+
+ for (i = 0; i < RADIUS_ATTRS; i++) {
+ if (type == radius_attrs[i].type)
+ return &radius_attrs[i];
+ }
+
+ return NULL;
+}
+
+
+static void radius_msg_dump_attr(struct radius_attr_hdr *hdr)
+{
+ struct radius_attr_type *attr;
+ int i, len;
+ unsigned char *pos;
+
+ attr = radius_get_attr_type(hdr->type);
+
+ printf(" Attribute %d (%s) length=%d\n",
+ hdr->type, attr ? attr->name : "?Unknown?", hdr->length);
+
+ if (attr == NULL)
+ return;
+
+ len = hdr->length - sizeof(struct radius_attr_hdr);
+ pos = (unsigned char *) (hdr + 1);
+
+ switch (attr->data_type) {
+ case RADIUS_ATTR_TEXT:
+ printf(" Value: '");
+ for (i = 0; i < len; i++)
+ print_char(pos[i]);
+ printf("'\n");
+ break;
+
+ case RADIUS_ATTR_IP:
+ if (len == 4) {
+ struct in_addr *addr = (struct in_addr *) pos;
+ printf(" Value: %s\n", inet_ntoa(*addr));
+ } else
+ printf(" Invalid IP address length %d\n", len);
+ break;
+
+ case RADIUS_ATTR_HEXDUMP:
+ case RADIUS_ATTR_UNDIST:
+ printf(" Value:");
+ for (i = 0; i < len; i++)
+ printf(" %02x", pos[i]);
+ printf("\n");
+ break;
+
+ case RADIUS_ATTR_INT32:
+ if (len == 4) {
+ u32 *val = (u32 *) pos;
+ printf(" Value: %d\n", ntohl(*val));
+ } else
+ printf(" Invalid INT32 length %d\n", len);
+ break;
+
+ default:
+ break;
+ }
+}
+
+
+void radius_msg_dump(struct radius_msg *msg)
+{
+ int i;
+
+ printf("RADIUS message: code=%d (%s) identifier=%d length=%d\n",
+ msg->hdr->code, radius_code_string(msg->hdr->code),
+ msg->hdr->identifier, ntohs(msg->hdr->length));
+
+ for (i = 0; i < msg->attr_used; i++) {
+ radius_msg_dump_attr(msg->attrs[i]);
+ }
+}
+
+
+int radius_msg_finish(struct radius_msg *msg, u8 *secret, size_t secret_len)
+{
+ if (secret) {
+ u8 auth[MD5_MAC_LEN];
+ struct radius_attr_hdr *attr;
+
+ memset(auth, 0, MD5_MAC_LEN);
+ attr = radius_msg_add_attr(msg,
+ RADIUS_ATTR_MESSAGE_AUTHENTICATOR,
+ auth, MD5_MAC_LEN);
+ if (attr == NULL) {
+ printf("WARNING: Could not add "
+ "Message-Authenticator\n");
+ return -1;
+ }
+ msg->hdr->length = htons(msg->buf_used);
+ hmac_md5(secret, secret_len, msg->buf, msg->buf_used,
+ (u8 *) (attr + 1));
+ } else
+ msg->hdr->length = htons(msg->buf_used);
+
+ if (msg->buf_used > 0xffff) {
+ printf("WARNING: too long RADIUS message (%lu)\n",
+ (unsigned long) msg->buf_used);
+ return -1;
+ }
+ return 0;
+}
+
+
+int radius_msg_finish_srv(struct radius_msg *msg, const u8 *secret,
+ size_t secret_len, const u8 *req_authenticator)
+{
+ u8 auth[MD5_MAC_LEN];
+ struct radius_attr_hdr *attr;
+ MD5_CTX context;
+
+ memset(auth, 0, MD5_MAC_LEN);
+ attr = radius_msg_add_attr(msg, RADIUS_ATTR_MESSAGE_AUTHENTICATOR,
+ auth, MD5_MAC_LEN);
+ if (attr == NULL) {
+ printf("WARNING: Could not add Message-Authenticator\n");
+ return -1;
+ }
+ msg->hdr->length = htons(msg->buf_used);
+ memcpy(msg->hdr->authenticator, req_authenticator,
+ sizeof(msg->hdr->authenticator));
+ hmac_md5(secret, secret_len, msg->buf, msg->buf_used,
+ (u8 *) (attr + 1));
+
+ /* ResponseAuth = MD5(Code+ID+Length+RequestAuth+Attributes+Secret) */
+ MD5Init(&context);
+ MD5Update(&context, (u8 *) msg->hdr, 1 + 1 + 2);
+ MD5Update(&context, req_authenticator, MD5_MAC_LEN);
+ MD5Update(&context, (u8 *) (msg->hdr + 1),
+ msg->buf_used - sizeof(*msg->hdr));
+ MD5Update(&context, secret, secret_len);
+ MD5Final(msg->hdr->authenticator, &context);
+
+ if (msg->buf_used > 0xffff) {
+ printf("WARNING: too long RADIUS message (%lu)\n",
+ (unsigned long) msg->buf_used);
+ return -1;
+ }
+ return 0;
+}
+
+
+void radius_msg_finish_acct(struct radius_msg *msg, u8 *secret,
+ size_t secret_len)
+{
+ MD5_CTX context;
+
+ msg->hdr->length = htons(msg->buf_used);
+ memset(msg->hdr->authenticator, 0, MD5_MAC_LEN);
+ MD5Init(&context);
+ MD5Update(&context, msg->buf, msg->buf_used);
+ MD5Update(&context, secret, secret_len);
+ MD5Final(msg->hdr->authenticator, &context);
+
+ if (msg->buf_used > 0xffff) {
+ printf("WARNING: too long RADIUS messages (%lu)\n",
+ (unsigned long) msg->buf_used);
+ }
+}
+
+
+static int radius_msg_add_attr_to_array(struct radius_msg *msg,
+ struct radius_attr_hdr *attr)
+{
+ if (msg->attr_used >= msg->attr_size) {
+ struct radius_attr_hdr **nattrs;
+ int nlen = msg->attr_size * 2;
+
+ nattrs = (struct radius_attr_hdr **)
+ realloc(msg->attrs, nlen * sizeof(*msg->attrs));
+
+ if (nattrs == NULL)
+ return -1;
+
+ msg->attrs = nattrs;
+ msg->attr_size = nlen;
+ }
+
+ msg->attrs[msg->attr_used++] = attr;
+
+ return 0;
+}
+
+
+struct radius_attr_hdr *radius_msg_add_attr(struct radius_msg *msg, u8 type,
+ u8 *data, size_t data_len)
+{
+ size_t buf_needed;
+ struct radius_attr_hdr *attr;
+
+ if (data_len > RADIUS_MAX_ATTR_LEN) {
+ printf("radius_msg_add_attr: too long attribute (%lu bytes)\n",
+ (unsigned long) data_len);
+ return NULL;
+ }
+
+ buf_needed = msg->buf_used + sizeof(*attr) + data_len;
+
+ if (msg->buf_size < buf_needed) {
+ /* allocate more space for message buffer */
+ unsigned char *nbuf;
+ int nlen = msg->buf_size;
+ int diff, i;
+
+ while (nlen < buf_needed)
+ nlen *= 2;
+ nbuf = (unsigned char *) realloc(msg->buf, nlen);
+ if (nbuf == NULL)
+ return NULL;
+ diff = nbuf - msg->buf;
+ msg->buf = nbuf;
+ msg->hdr = (struct radius_hdr *) msg->buf;
+ /* adjust attr pointers to match with the new buffer */
+ for (i = 0; i < msg->attr_used; i++)
+ msg->attrs[i] = (struct radius_attr_hdr *)
+ (((u8 *) msg->attrs[i]) + diff);
+ memset(msg->buf + msg->buf_size, 0, nlen - msg->buf_size);
+ msg->buf_size = nlen;
+ }
+
+ attr = (struct radius_attr_hdr *) (msg->buf + msg->buf_used);
+ attr->type = type;
+ attr->length = sizeof(*attr) + data_len;
+ if (data_len > 0)
+ memcpy(attr + 1, data, data_len);
+
+ msg->buf_used += sizeof(*attr) + data_len;
+
+ if (radius_msg_add_attr_to_array(msg, attr))
+ return NULL;
+
+ return attr;
+}
+
+
+struct radius_msg *radius_msg_parse(const u8 *data, size_t len)
+{
+ struct radius_msg *msg;
+ struct radius_hdr *hdr;
+ struct radius_attr_hdr *attr;
+ size_t msg_len;
+ unsigned char *pos, *end;
+
+ if (data == NULL || len < sizeof(*hdr))
+ return NULL;
+
+ hdr = (struct radius_hdr *) data;
+
+ msg_len = ntohs(hdr->length);
+ if (msg_len < sizeof(*hdr) || msg_len > len) {
+ printf("Invalid RADIUS message length\n");
+ return NULL;
+ }
+
+ if (msg_len < len) {
+ printf("Ignored %lu extra bytes after RADIUS message\n",
+ (unsigned long) len - msg_len);
+ }
+
+ msg = (struct radius_msg *) malloc(sizeof(*msg));
+ if (msg == NULL)
+ return NULL;
+
+ if (radius_msg_initialize(msg, msg_len)) {
+ free(msg);
+ return NULL;
+ }
+
+ memcpy(msg->buf, data, msg_len);
+ msg->buf_size = msg->buf_used = msg_len;
+
+ /* parse attributes */
+ pos = (unsigned char *) (msg->hdr + 1);
+ end = msg->buf + msg->buf_used;
+ while (pos < end) {
+ if (end - pos < sizeof(*attr))
+ goto fail;
+
+ attr = (struct radius_attr_hdr *) pos;
+
+ if (pos + attr->length > end || attr->length < sizeof(*attr))
+ goto fail;
+
+ /* TODO: check that attr->length is suitable for attr->type */
+
+ if (radius_msg_add_attr_to_array(msg, attr))
+ goto fail;
+
+ pos += attr->length;
+ }
+
+ return msg;
+
+ fail:
+ radius_msg_free(msg);
+ free(msg);
+ return NULL;
+}
+
+
+int radius_msg_add_eap(struct radius_msg *msg, u8 *data, size_t data_len)
+{
+ u8 *pos = data;
+ size_t left = data_len;
+
+ while (left > 0) {
+ int len;
+ if (left > RADIUS_MAX_ATTR_LEN)
+ len = RADIUS_MAX_ATTR_LEN;
+ else
+ len = left;
+
+ if (!radius_msg_add_attr(msg, RADIUS_ATTR_EAP_MESSAGE,
+ pos, len))
+ return 0;
+
+ pos += len;
+ left -= len;
+ }
+
+ return 1;
+}
+
+
+u8 *radius_msg_get_eap(struct radius_msg *msg, size_t *eap_len)
+{
+ u8 *eap, *pos;
+ size_t len;
+ int i;
+
+ if (msg == NULL)
+ return NULL;
+
+ len = 0;
+ for (i = 0; i < msg->attr_used; i++) {
+ if (msg->attrs[i]->type == RADIUS_ATTR_EAP_MESSAGE)
+ len += msg->attrs[i]->length -
+ sizeof(struct radius_attr_hdr);
+ }
+
+ if (len == 0)
+ return NULL;
+
+ eap = malloc(len);
+ if (eap == NULL)
+ return NULL;
+
+ pos = eap;
+ for (i = 0; i < msg->attr_used; i++) {
+ if (msg->attrs[i]->type == RADIUS_ATTR_EAP_MESSAGE) {
+ struct radius_attr_hdr *attr = msg->attrs[i];
+ int flen = attr->length - sizeof(*attr);
+ memcpy(pos, attr + 1, flen);
+ pos += flen;
+ }
+ }
+
+ if (eap_len)
+ *eap_len = len;
+
+ return eap;
+}
+
+
+int radius_msg_verify_msg_auth(struct radius_msg *msg, const u8 *secret,
+ size_t secret_len, const u8 *req_auth)
+{
+ u8 auth[MD5_MAC_LEN], orig[MD5_MAC_LEN];
+ u8 orig_authenticator[16];
+ struct radius_attr_hdr *attr = NULL;
+ int i;
+
+ for (i = 0; i < msg->attr_used; i++) {
+ if (msg->attrs[i]->type == RADIUS_ATTR_MESSAGE_AUTHENTICATOR) {
+ if (attr != NULL) {
+ printf("Multiple Message-Authenticator "
+ "attributes in RADIUS message\n");
+ return 1;
+ }
+ attr = msg->attrs[i];
+ }
+ }
+
+ if (attr == NULL) {
+ printf("No Message-Authenticator attribute found\n");
+ return 1;
+ }
+
+ memcpy(orig, attr + 1, MD5_MAC_LEN);
+ memset(attr + 1, 0, MD5_MAC_LEN);
+ if (req_auth) {
+ memcpy(orig_authenticator, msg->hdr->authenticator,
+ sizeof(orig_authenticator));
+ memcpy(msg->hdr->authenticator, req_auth,
+ sizeof(msg->hdr->authenticator));
+ }
+ hmac_md5(secret, secret_len, msg->buf, msg->buf_used, auth);
+ memcpy(attr + 1, orig, MD5_MAC_LEN);
+ if (req_auth) {
+ memcpy(msg->hdr->authenticator, orig_authenticator,
+ sizeof(orig_authenticator));
+ }
+
+ if (memcmp(orig, auth, MD5_MAC_LEN) != 0) {
+ printf("Invalid Message-Authenticator!\n");
+ return 1;
+ }
+
+ return 0;
+}
+
+
+int radius_msg_verify(struct radius_msg *msg, u8 *secret, size_t secret_len,
+ struct radius_msg *sent_msg)
+{
+ MD5_CTX context;
+ u8 hash[MD5_MAC_LEN];
+
+ if (sent_msg == NULL) {
+ printf("No matching Access-Request message found\n");
+ return 1;
+ }
+
+ if (radius_msg_verify_msg_auth(msg, secret, secret_len,
+ sent_msg->hdr->authenticator)) {
+ return 1;
+ }
+
+ /* ResponseAuth = MD5(Code+ID+Length+RequestAuth+Attributes+Secret) */
+ MD5Init(&context);
+ MD5Update(&context, (u8 *) msg->hdr, 1 + 1 + 2);
+ MD5Update(&context, sent_msg->hdr->authenticator, MD5_MAC_LEN);
+ MD5Update(&context, (u8 *) (msg->hdr + 1),
+ msg->buf_used - sizeof(*msg->hdr));
+ MD5Update(&context, secret, secret_len);
+ MD5Final(hash, &context);
+ if (memcmp(hash, msg->hdr->authenticator, MD5_MAC_LEN) != 0) {
+ printf("Response Authenticator invalid!\n");
+ return 1;
+ }
+
+ return 0;
+
+}
+
+
+int radius_msg_verify_acct(struct radius_msg *msg, u8 *secret,
+ size_t secret_len, struct radius_msg *sent_msg)
+{
+ MD5_CTX context;
+ u8 hash[MD5_MAC_LEN];
+
+ MD5Init(&context);
+ MD5Update(&context, msg->buf, 4);
+ MD5Update(&context, sent_msg->hdr->authenticator, MD5_MAC_LEN);
+ if (msg->buf_used > sizeof(struct radius_hdr))
+ MD5Update(&context, msg->buf + sizeof(struct radius_hdr),
+ msg->buf_used - sizeof(struct radius_hdr));
+ MD5Update(&context, secret, secret_len);
+ MD5Final(hash, &context);
+ if (memcmp(hash, msg->hdr->authenticator, MD5_MAC_LEN) != 0) {
+ printf("Response Authenticator invalid!\n");
+ return 1;
+ }
+
+ return 0;
+}
+
+
+int radius_msg_copy_attr(struct radius_msg *dst, struct radius_msg *src,
+ u8 type)
+{
+ struct radius_attr_hdr *attr = NULL;
+ int i;
+
+ for (i = 0; i < src->attr_used; i++) {
+ if (src->attrs[i]->type == type) {
+ attr = src->attrs[i];
+ break;
+ }
+ }
+
+ if (attr == NULL)
+ return 0;
+
+ if (!radius_msg_add_attr(dst, type, (u8 *) (attr + 1),
+ attr->length - sizeof(*attr)))
+ return -1;
+
+ return 1;
+}
+
+
+/* Create Request Authenticator. The value should be unique over the lifetime
+ * of the shared secret between authenticator and authentication server.
+ * Use one-way MD5 hash calculated from current timestamp and some data given
+ * by the caller. */
+void radius_msg_make_authenticator(struct radius_msg *msg,
+ u8 *data, size_t len)
+{
+ struct timeval tv;
+ MD5_CTX context;
+ long int l;
+
+ gettimeofday(&tv, NULL);
+ l = random();
+ MD5Init(&context);
+ MD5Update(&context, (u8 *) &tv, sizeof(tv));
+ MD5Update(&context, data, len);
+ MD5Update(&context, (u8 *) &l, sizeof(l));
+ MD5Final(msg->hdr->authenticator, &context);
+}
+
+
+/* Get Vendor-specific RADIUS Attribute from a parsed RADIUS message.
+ * Returns the Attribute payload and sets alen to indicate the length of the
+ * payload if a vendor attribute with subtype is found, otherwise returns NULL.
+ * The returned payload is allocated with malloc() and caller must free it.
+ */
+static u8 *radius_msg_get_vendor_attr(struct radius_msg *msg, u32 vendor,
+ u8 subtype, size_t *alen)
+{
+ u8 *data, *pos;
+ int i;
+ size_t len;
+
+ if (msg == NULL)
+ return NULL;
+
+ for (i = 0; i < msg->attr_used; i++) {
+ struct radius_attr_hdr *attr = msg->attrs[i];
+ int left;
+ u32 vendor_id;
+ struct radius_attr_vendor *vhdr;
+
+ if (attr->type != RADIUS_ATTR_VENDOR_SPECIFIC)
+ continue;
+
+ left = attr->length - sizeof(*attr);
+ if (left < 4)
+ continue;
+
+ pos = (u8 *) (attr + 1);
+
+ memcpy(&vendor_id, pos, 4);
+ pos += 4;
+ left -= 4;
+
+ if (ntohl(vendor_id) != vendor)
+ continue;
+
+ while (left >= sizeof(*vhdr)) {
+ vhdr = (struct radius_attr_vendor *) pos;
+ if (vhdr->vendor_length > left ||
+ vhdr->vendor_length < sizeof(*vhdr)) {
+ left = 0;
+ break;
+ }
+ if (vhdr->vendor_type != subtype) {
+ pos += vhdr->vendor_length;
+ left -= vhdr->vendor_length;
+ continue;
+ }
+
+ len = vhdr->vendor_length - sizeof(*vhdr);
+ data = malloc(len);
+ if (data == NULL)
+ return NULL;
+ memcpy(data, pos + sizeof(*vhdr), len);
+ if (alen)
+ *alen = len;
+ return data;
+ }
+ }
+
+ return NULL;
+}
+
+
+static u8 * decrypt_ms_key(const u8 *key, size_t len,
+ const u8 *req_authenticator,
+ const u8 *secret, size_t secret_len, size_t *reslen)
+{
+ u8 *plain, *ppos, *res;
+ const u8 *pos;
+ size_t left, plen;
+ u8 hash[MD5_MAC_LEN];
+ MD5_CTX context;
+ int i, first = 1;
+
+ /* key: 16-bit salt followed by encrypted key info */
+
+ if (len < 2 + 16)
+ return NULL;
+
+ pos = key + 2;
+ left = len - 2;
+ if (left % 16) {
+ printf("Invalid ms key len %lu\n", (unsigned long) left);
+ return NULL;
+ }
+
+ plen = left;
+ ppos = plain = malloc(plen);
+ if (plain == NULL)
+ return NULL;
+
+ while (left > 0) {
+ /* b(1) = MD5(Secret + Request-Authenticator + Salt)
+ * b(i) = MD5(Secret + c(i - 1)) for i > 1 */
+
+ MD5Init(&context);
+ MD5Update(&context, secret, secret_len);
+ if (first) {
+ MD5Update(&context, req_authenticator, MD5_MAC_LEN);
+ MD5Update(&context, key, 2); /* Salt */
+ first = 0;
+ } else
+ MD5Update(&context, pos - MD5_MAC_LEN, MD5_MAC_LEN);
+ MD5Final(hash, &context);
+
+ for (i = 0; i < MD5_MAC_LEN; i++)
+ *ppos++ = *pos++ ^ hash[i];
+ left -= MD5_MAC_LEN;
+ }
+
+ if (plain[0] > plen - 1) {
+ printf("Failed to decrypt MPPE key\n");
+ free(plain);
+ return NULL;
+ }
+
+ res = malloc(plain[0]);
+ if (res == NULL) {
+ free(plain);
+ return NULL;
+ }
+ memcpy(res, plain + 1, plain[0]);
+ if (reslen)
+ *reslen = plain[0];
+ free(plain);
+ return res;
+}
+
+
+static void encrypt_ms_key(const u8 *key, size_t key_len, u16 salt,
+ const u8 *req_authenticator,
+ const u8 *secret, size_t secret_len,
+ u8 *ebuf, size_t *elen)
+{
+ int i, len, first = 1;
+ u8 hash[MD5_MAC_LEN], saltbuf[2], *pos;
+ MD5_CTX context;
+
+ saltbuf[0] = salt >> 8;
+ saltbuf[1] = salt;
+
+ len = 1 + key_len;
+ if (len & 0x0f) {
+ len = (len & 0xf0) + 16;
+ }
+ memset(ebuf, 0, len);
+ ebuf[0] = key_len;
+ memcpy(ebuf + 1, key, key_len);
+
+ *elen = len;
+
+ pos = ebuf;
+ while (len > 0) {
+ /* b(1) = MD5(Secret + Request-Authenticator + Salt)
+ * b(i) = MD5(Secret + c(i - 1)) for i > 1 */
+ MD5Init(&context);
+ MD5Update(&context, secret, secret_len);
+ if (first) {
+ MD5Update(&context, req_authenticator, MD5_MAC_LEN);
+ MD5Update(&context, saltbuf, sizeof(saltbuf));
+ first = 0;
+ } else {
+ MD5Update(&context, pos - MD5_MAC_LEN, MD5_MAC_LEN);
+ }
+ MD5Final(hash, &context);
+
+ for (i = 0; i < MD5_MAC_LEN; i++)
+ *pos++ ^= hash[i];
+
+ len -= MD5_MAC_LEN;
+ }
+}
+
+
+struct radius_ms_mppe_keys *
+radius_msg_get_ms_keys(struct radius_msg *msg, struct radius_msg *sent_msg,
+ u8 *secret, size_t secret_len)
+{
+ u8 *key;
+ size_t keylen;
+ struct radius_ms_mppe_keys *keys;
+
+ if (msg == NULL || sent_msg == NULL)
+ return NULL;
+
+ keys = (struct radius_ms_mppe_keys *) malloc(sizeof(*keys));
+ if (keys == NULL)
+ return NULL;
+
+ memset(keys, 0, sizeof(*keys));
+
+ key = radius_msg_get_vendor_attr(msg, RADIUS_VENDOR_ID_MICROSOFT,
+ RADIUS_VENDOR_ATTR_MS_MPPE_SEND_KEY,
+ &keylen);
+ if (key) {
+ keys->send = decrypt_ms_key(key, keylen,
+ sent_msg->hdr->authenticator,
+ secret, secret_len,
+ &keys->send_len);
+ free(key);
+ }
+
+ key = radius_msg_get_vendor_attr(msg, RADIUS_VENDOR_ID_MICROSOFT,
+ RADIUS_VENDOR_ATTR_MS_MPPE_RECV_KEY,
+ &keylen);
+ if (key) {
+ keys->recv = decrypt_ms_key(key, keylen,
+ sent_msg->hdr->authenticator,
+ secret, secret_len,
+ &keys->recv_len);
+ free(key);
+ }
+
+ return keys;
+}
+
+
+struct radius_ms_mppe_keys *
+radius_msg_get_cisco_keys(struct radius_msg *msg, struct radius_msg *sent_msg,
+ u8 *secret, size_t secret_len)
+{
+ u8 *key;
+ size_t keylen;
+ struct radius_ms_mppe_keys *keys;
+
+ if (msg == NULL || sent_msg == NULL)
+ return NULL;
+
+ keys = (struct radius_ms_mppe_keys *) malloc(sizeof(*keys));
+ if (keys == NULL)
+ return NULL;
+
+ memset(keys, 0, sizeof(*keys));
+
+ key = radius_msg_get_vendor_attr(msg, RADIUS_VENDOR_ID_CISCO,
+ RADIUS_CISCO_AV_PAIR, &keylen);
+ if (key && keylen == 51 && memcmp(key, "leap:session-key=", 17) == 0) {
+ keys->recv = decrypt_ms_key(key + 17, keylen - 17,
+ sent_msg->hdr->authenticator,
+ secret, secret_len,
+ &keys->recv_len);
+ free(key);
+ }
+
+ return keys;
+}
+
+
+int radius_msg_add_mppe_keys(struct radius_msg *msg,
+ const u8 *req_authenticator,
+ const u8 *secret, size_t secret_len,
+ const u8 *send_key, size_t send_key_len,
+ const u8 *recv_key, size_t recv_key_len)
+{
+ struct radius_attr_hdr *attr;
+ u32 vendor_id = htonl(RADIUS_VENDOR_ID_MICROSOFT);
+ u8 *buf;
+ struct radius_attr_vendor *vhdr;
+ u8 *pos;
+ size_t elen;
+ int hlen;
+ u16 salt;
+
+ hlen = sizeof(vendor_id) + sizeof(*vhdr) + 2;
+
+ /* MS-MPPE-Send-Key */
+ buf = malloc(hlen + send_key_len + 16);
+ if (buf == NULL) {
+ return 0;
+ }
+ pos = buf;
+ memcpy(pos, &vendor_id, sizeof(vendor_id));
+ pos += sizeof(vendor_id);
+ vhdr = (struct radius_attr_vendor *) pos;
+ vhdr->vendor_type = RADIUS_VENDOR_ATTR_MS_MPPE_SEND_KEY;
+ pos = (u8 *) (vhdr + 1);
+ salt = random() | 0x8000;
+ *pos++ = salt >> 8;
+ *pos++ = salt;
+ encrypt_ms_key(send_key, send_key_len, salt, req_authenticator, secret,
+ secret_len, pos, &elen);
+ vhdr->vendor_length = hlen + elen - sizeof(vendor_id);
+
+ attr = radius_msg_add_attr(msg, RADIUS_ATTR_VENDOR_SPECIFIC,
+ buf, hlen + elen);
+ free(buf);
+ if (attr == NULL) {
+ return 0;
+ }
+
+ /* MS-MPPE-Recv-Key */
+ buf = malloc(hlen + send_key_len + 16);
+ if (buf == NULL) {
+ return 0;
+ }
+ pos = buf;
+ memcpy(pos, &vendor_id, sizeof(vendor_id));
+ pos += sizeof(vendor_id);
+ vhdr = (struct radius_attr_vendor *) pos;
+ vhdr->vendor_type = RADIUS_VENDOR_ATTR_MS_MPPE_RECV_KEY;
+ pos = (u8 *) (vhdr + 1);
+ salt ^= 1;
+ *pos++ = salt >> 8;
+ *pos++ = salt;
+ encrypt_ms_key(recv_key, recv_key_len, salt, req_authenticator, secret,
+ secret_len, pos, &elen);
+ vhdr->vendor_length = hlen + elen - sizeof(vendor_id);
+
+ attr = radius_msg_add_attr(msg, RADIUS_ATTR_VENDOR_SPECIFIC,
+ buf, hlen + elen);
+ free(buf);
+ if (attr == NULL) {
+ return 0;
+ }
+
+ return 1;
+}
+
+
+/* Add User-Password attribute to a RADIUS message and encrypt it as specified
+ * in RFC 2865, Chap. 5.2 */
+struct radius_attr_hdr *
+radius_msg_add_attr_user_password(struct radius_msg *msg,
+ u8 *data, size_t data_len,
+ u8 *secret, size_t secret_len)
+{
+ u8 buf[128];
+ int padlen, i, pos;
+ MD5_CTX context;
+ size_t buf_len;
+ u8 hash[16];
+
+ if (data_len > 128)
+ return NULL;
+
+ memcpy(buf, data, data_len);
+ buf_len = data_len;
+
+ padlen = data_len % 16;
+ if (padlen) {
+ padlen = 16 - padlen;
+ memset(buf + data_len, 0, padlen);
+ buf_len += padlen;
+ }
+
+ MD5Init(&context);
+ MD5Update(&context, secret, secret_len);
+ MD5Update(&context, msg->hdr->authenticator, 16);
+ MD5Final(hash, &context);
+
+ for (i = 0; i < 16; i++)
+ buf[i] ^= hash[i];
+ pos = 16;
+
+ while (pos < buf_len) {
+ MD5Init(&context);
+ MD5Update(&context, secret, secret_len);
+ MD5Update(&context, &buf[pos - 16], 16);
+ MD5Final(hash, &context);
+
+ for (i = 0; i < 16; i++)
+ buf[pos + i] ^= hash[i];
+
+ pos += 16;
+ }
+
+ return radius_msg_add_attr(msg, RADIUS_ATTR_USER_PASSWORD,
+ buf, buf_len);
+}
+
+
+int radius_msg_get_attr(struct radius_msg *msg, u8 type, u8 *buf, size_t len)
+{
+ int i;
+ struct radius_attr_hdr *attr = NULL;
+ size_t dlen;
+
+ for (i = 0; i < msg->attr_used; i++) {
+ if (msg->attrs[i]->type == type) {
+ attr = msg->attrs[i];
+ break;
+ }
+ }
+
+ if (!attr)
+ return -1;
+
+ dlen = attr->length - sizeof(*attr);
+ if (buf)
+ memcpy(buf, (attr + 1), dlen > len ? len : dlen);
+ return dlen;
+}
+
+
+int radius_msg_get_attr_ptr(struct radius_msg *msg, u8 type, u8 **buf,
+ size_t *len)
+{
+ int i;
+ struct radius_attr_hdr *attr = NULL;
+
+ for (i = 0; i < msg->attr_used; i++) {
+ if (msg->attrs[i]->type == type) {
+ attr = msg->attrs[i];
+ break;
+ }
+ }
+
+ if (!attr)
+ return -1;
+
+ *buf = (u8 *) (attr + 1);
+ *len = attr->length - sizeof(*attr);
+ return 0;
+}
diff --git a/contrib/hostapd/radius.h b/contrib/hostapd/radius.h
new file mode 100644
index 000000000000..318e0adecf24
--- /dev/null
+++ b/contrib/hostapd/radius.h
@@ -0,0 +1,224 @@
+#ifndef RADIUS_H
+#define RADIUS_H
+
+/* RFC 2865 - RADIUS */
+
+struct radius_hdr {
+ u8 code;
+ u8 identifier;
+ u16 length; /* including this header */
+ u8 authenticator[16];
+ /* followed by length-20 octets of attributes */
+} __attribute__ ((packed));
+
+enum { RADIUS_CODE_ACCESS_REQUEST = 1,
+ RADIUS_CODE_ACCESS_ACCEPT = 2,
+ RADIUS_CODE_ACCESS_REJECT = 3,
+ RADIUS_CODE_ACCOUNTING_REQUEST = 4,
+ RADIUS_CODE_ACCOUNTING_RESPONSE = 5,
+ RADIUS_CODE_ACCESS_CHALLENGE = 11,
+ RADIUS_CODE_STATUS_SERVER = 12,
+ RADIUS_CODE_STATUS_CLIENT = 13,
+ RADIUS_CODE_RESERVED = 255
+};
+
+struct radius_attr_hdr {
+ u8 type;
+ u8 length; /* including this header */
+ /* followed by length-2 octets of attribute value */
+} __attribute__ ((packed));
+
+#define RADIUS_MAX_ATTR_LEN (255 - sizeof(struct radius_attr_hdr))
+
+enum { RADIUS_ATTR_USER_NAME = 1,
+ RADIUS_ATTR_USER_PASSWORD = 2,
+ RADIUS_ATTR_NAS_IP_ADDRESS = 4,
+ RADIUS_ATTR_NAS_PORT = 5,
+ RADIUS_ATTR_FRAMED_MTU = 12,
+ RADIUS_ATTR_STATE = 24,
+ RADIUS_ATTR_CLASS = 25,
+ RADIUS_ATTR_VENDOR_SPECIFIC = 26,
+ RADIUS_ATTR_SESSION_TIMEOUT = 27,
+ RADIUS_ATTR_IDLE_TIMEOUT = 28,
+ RADIUS_ATTR_TERMINATION_ACTION = 29,
+ RADIUS_ATTR_CALLED_STATION_ID = 30,
+ RADIUS_ATTR_CALLING_STATION_ID = 31,
+ RADIUS_ATTR_NAS_IDENTIFIER = 32,
+ RADIUS_ATTR_ACCT_STATUS_TYPE = 40,
+ RADIUS_ATTR_ACCT_DELAY_TIME = 41,
+ RADIUS_ATTR_ACCT_INPUT_OCTETS = 42,
+ RADIUS_ATTR_ACCT_OUTPUT_OCTETS = 43,
+ RADIUS_ATTR_ACCT_SESSION_ID = 44,
+ RADIUS_ATTR_ACCT_AUTHENTIC = 45,
+ RADIUS_ATTR_ACCT_SESSION_TIME = 46,
+ RADIUS_ATTR_ACCT_INPUT_PACKETS = 47,
+ RADIUS_ATTR_ACCT_OUTPUT_PACKETS = 48,
+ RADIUS_ATTR_ACCT_TERMINATE_CAUSE = 49,
+ RADIUS_ATTR_ACCT_MULTI_SESSION_ID = 50,
+ RADIUS_ATTR_ACCT_LINK_COUNT = 51,
+ RADIUS_ATTR_ACCT_INPUT_GIGAWORDS = 52,
+ RADIUS_ATTR_ACCT_OUTPUT_GIGAWORDS = 53,
+ RADIUS_ATTR_EVENT_TIMESTAMP = 55,
+ RADIUS_ATTR_NAS_PORT_TYPE = 61,
+ RADIUS_ATTR_CONNECT_INFO = 77,
+ RADIUS_ATTR_EAP_MESSAGE = 79,
+ RADIUS_ATTR_MESSAGE_AUTHENTICATOR = 80,
+ RADIUS_ATTR_ACCT_INTERIM_INTERVAL = 85
+};
+
+
+/* Termination-Action */
+#define RADIUS_TERMINATION_ACTION_DEFAULT 0
+#define RADIUS_TERMINATION_ACTION_RADIUS_REQUEST 1
+
+/* NAS-Port-Type */
+#define RADIUS_NAS_PORT_TYPE_IEEE_802_11 19
+
+/* Acct-Status-Type */
+#define RADIUS_ACCT_STATUS_TYPE_START 1
+#define RADIUS_ACCT_STATUS_TYPE_STOP 2
+#define RADIUS_ACCT_STATUS_TYPE_INTERIM_UPDATE 3
+#define RADIUS_ACCT_STATUS_TYPE_ACCOUNTING_ON 7
+#define RADIUS_ACCT_STATUS_TYPE_ACCOUNTING_OFF 8
+
+/* Acct-Authentic */
+#define RADIUS_ACCT_AUTHENTIC_RADIUS 1
+#define RADIUS_ACCT_AUTHENTIC_LOCAL 2
+#define RADIUS_ACCT_AUTHENTIC_REMOTE 3
+
+/* Acct-Terminate-Cause */
+#define RADIUS_ACCT_TERMINATE_CAUSE_USER_REQUEST 1
+#define RADIUS_ACCT_TERMINATE_CAUSE_LOST_CARRIER 2
+#define RADIUS_ACCT_TERMINATE_CAUSE_LOST_SERVICE 3
+#define RADIUS_ACCT_TERMINATE_CAUSE_IDLE_TIMEOUT 4
+#define RADIUS_ACCT_TERMINATE_CAUSE_SESSION_TIMEOUT 5
+#define RADIUS_ACCT_TERMINATE_CAUSE_ADMIN_RESET 6
+#define RADIUS_ACCT_TERMINATE_CAUSE_ADMIN_REBOOT 7
+#define RADIUS_ACCT_TERMINATE_CAUSE_PORT_ERROR 8
+#define RADIUS_ACCT_TERMINATE_CAUSE_NAS_ERROR 9
+#define RADIUS_ACCT_TERMINATE_CAUSE_NAS_REQUEST 10
+#define RADIUS_ACCT_TERMINATE_CAUSE_NAS_REBOOT 11
+#define RADIUS_ACCT_TERMINATE_CAUSE_PORT_UNNEEDED 12
+#define RADIUS_ACCT_TERMINATE_CAUSE_PORT_PREEMPTED 13
+#define RADIUS_ACCT_TERMINATE_CAUSE_PORT_SUSPENDED 14
+#define RADIUS_ACCT_TERMINATE_CAUSE_SERVICE_UNAVAILABLE 15
+#define RADIUS_ACCT_TERMINATE_CAUSE_CALLBACK 16
+#define RADIUS_ACCT_TERMINATE_CAUSE_USER_ERROR 17
+#define RADIUS_ACCT_TERMINATE_CAUSE_HOST_REQUEST 18
+
+
+struct radius_attr_vendor {
+ u8 vendor_type;
+ u8 vendor_length;
+} __attribute__ ((packed));
+
+#define RADIUS_VENDOR_ID_CISCO 9
+#define RADIUS_CISCO_AV_PAIR 1
+
+/* RFC 2548 - Microsoft Vendor-specific RADIUS Attributes */
+#define RADIUS_VENDOR_ID_MICROSOFT 311
+
+enum { RADIUS_VENDOR_ATTR_MS_MPPE_SEND_KEY = 16,
+ RADIUS_VENDOR_ATTR_MS_MPPE_RECV_KEY = 17
+};
+
+struct radius_ms_mppe_keys {
+ u8 *send;
+ size_t send_len;
+ u8 *recv;
+ size_t recv_len;
+};
+
+
+/* RADIUS message structure for new and parsed messages */
+struct radius_msg {
+ unsigned char *buf;
+ size_t buf_size; /* total size allocated for buf */
+ size_t buf_used; /* bytes used in buf */
+
+ struct radius_hdr *hdr;
+
+ struct radius_attr_hdr **attrs; /* array of pointers to attributes */
+ size_t attr_size; /* total size of the attribute pointer array */
+ size_t attr_used; /* total number of attributes in the array */
+};
+
+
+/* Default size to be allocated for new RADIUS messages */
+#define RADIUS_DEFAULT_MSG_SIZE 1024
+
+/* Default size to be allocated for attribute array */
+#define RADIUS_DEFAULT_ATTR_COUNT 16
+
+
+/* MAC address ASCII format for IEEE 802.1X use
+ * (draft-congdon-radius-8021x-20.txt) */
+#define RADIUS_802_1X_ADDR_FORMAT "%02X-%02X-%02X-%02X-%02X-%02X"
+/* MAC address ASCII format for non-802.1X use */
+#define RADIUS_ADDR_FORMAT "%02x%02x%02x%02x%02x%02x"
+
+struct radius_msg *radius_msg_new(u8 code, u8 identifier);
+int radius_msg_initialize(struct radius_msg *msg, size_t init_len);
+void radius_msg_set_hdr(struct radius_msg *msg, u8 code, u8 identifier);
+void radius_msg_free(struct radius_msg *msg);
+void radius_msg_dump(struct radius_msg *msg);
+int radius_msg_finish(struct radius_msg *msg, u8 *secret, size_t secret_len);
+int radius_msg_finish_srv(struct radius_msg *msg, const u8 *secret,
+ size_t secret_len, const u8 *req_authenticator);
+void radius_msg_finish_acct(struct radius_msg *msg, u8 *secret,
+ size_t secret_len);
+struct radius_attr_hdr *radius_msg_add_attr(struct radius_msg *msg, u8 type,
+ u8 *data, size_t data_len);
+struct radius_msg *radius_msg_parse(const u8 *data, size_t len);
+int radius_msg_add_eap(struct radius_msg *msg, u8 *data, size_t data_len);
+u8 *radius_msg_get_eap(struct radius_msg *msg, size_t *len);
+int radius_msg_verify(struct radius_msg *msg, u8 *secret, size_t secret_len,
+ struct radius_msg *sent_msg);
+int radius_msg_verify_msg_auth(struct radius_msg *msg, const u8 *secret,
+ size_t secret_len, const u8 *req_auth);
+int radius_msg_verify_acct(struct radius_msg *msg, u8 *secret,
+ size_t secret_len, struct radius_msg *sent_msg);
+int radius_msg_copy_attr(struct radius_msg *dst, struct radius_msg *src,
+ u8 type);
+void radius_msg_make_authenticator(struct radius_msg *msg,
+ u8 *data, size_t len);
+struct radius_ms_mppe_keys *
+radius_msg_get_ms_keys(struct radius_msg *msg, struct radius_msg *sent_msg,
+ u8 *secret, size_t secret_len);
+struct radius_ms_mppe_keys *
+radius_msg_get_cisco_keys(struct radius_msg *msg, struct radius_msg *sent_msg,
+ u8 *secret, size_t secret_len);
+int radius_msg_add_mppe_keys(struct radius_msg *msg,
+ const u8 *req_authenticator,
+ const u8 *secret, size_t secret_len,
+ const u8 *send_key, size_t send_key_len,
+ const u8 *recv_key, size_t recv_key_len);
+struct radius_attr_hdr *
+radius_msg_add_attr_user_password(struct radius_msg *msg,
+ u8 *data, size_t data_len,
+ u8 *secret, size_t secret_len);
+int radius_msg_get_attr(struct radius_msg *msg, u8 type, u8 *buf, size_t len);
+
+static inline int radius_msg_add_attr_int32(struct radius_msg *msg, u8 type,
+ u32 value)
+{
+ u32 val = htonl(value);
+ return radius_msg_add_attr(msg, type, (u8 *) &val, 4) != NULL;
+}
+
+static inline int radius_msg_get_attr_int32(struct radius_msg *msg, u8 type,
+ u32 *value)
+{
+ u32 val;
+ int res;
+ res = radius_msg_get_attr(msg, type, (u8 *) &val, 4);
+ if (res != 4)
+ return -1;
+
+ *value = ntohl(val);
+ return 0;
+}
+int radius_msg_get_attr_ptr(struct radius_msg *msg, u8 type, u8 **buf,
+ size_t *len);
+
+#endif /* RADIUS_H */
diff --git a/contrib/hostapd/radius_client.c b/contrib/hostapd/radius_client.c
new file mode 100644
index 000000000000..e17377549168
--- /dev/null
+++ b/contrib/hostapd/radius_client.c
@@ -0,0 +1,1016 @@
+/*
+ * Host AP (software wireless LAN access point) user space daemon for
+ * Host AP kernel driver / RADIUS client
+ * 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 <unistd.h>
+#include <netinet/in.h>
+#include <string.h>
+#include <time.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <errno.h>
+
+#include "hostapd.h"
+#include "radius.h"
+#include "radius_client.h"
+#include "eloop.h"
+
+/* Defaults for RADIUS retransmit values (exponential backoff) */
+#define RADIUS_CLIENT_FIRST_WAIT 3 /* 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 */
+
+
+struct radius_rx_handler {
+ RadiusRxResult (*handler)(struct radius_msg *msg,
+ struct radius_msg *req,
+ u8 *shared_secret, size_t shared_secret_len,
+ void *data);
+ void *data;
+};
+
+
+/* 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;
+ struct timeval last_attempt;
+
+ u8 *shared_secret;
+ size_t shared_secret_len;
+
+ /* TODO: server config with failover to backup server(s) */
+
+ struct radius_msg_list *next;
+};
+
+
+struct radius_client_data {
+ struct hostapd_data *hapd;
+
+ 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;
+};
+
+
+static int
+radius_change_server(struct radius_client_data *radius,
+ struct hostapd_radius_server *nserv,
+ struct hostapd_radius_server *oserv,
+ int sock, int auth);
+static int radius_client_init_acct(struct radius_client_data *radius);
+static int radius_client_init_auth(struct radius_client_data *radius);
+
+
+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 radius_client_data *radius,
+ RadiusType msg_type,
+ RadiusRxResult (*handler)(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 = &radius->acct_handlers;
+ num = &radius->num_acct_handlers;
+ } else {
+ handlers = &radius->auth_handlers;
+ num = &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 void radius_client_handle_send_error(struct radius_client_data *radius,
+ int s, RadiusType msg_type)
+{
+ struct hostapd_data *hapd = radius->hapd;
+ int _errno = errno;
+ perror("send[RADIUS]");
+ if (_errno == ENOTCONN || _errno == EDESTADDRREQ || _errno == EINVAL) {
+ hostapd_logger(hapd, NULL, HOSTAPD_MODULE_RADIUS,
+ HOSTAPD_LEVEL_INFO,
+ "Send failed - maybe interface status changed -"
+ " try to connect again");
+ eloop_unregister_read_sock(s);
+ close(s);
+ if (msg_type == RADIUS_ACCT || msg_type == RADIUS_ACCT_INTERIM)
+ radius_client_init_acct(radius);
+ else
+ radius_client_init_auth(radius);
+ }
+}
+
+
+static int radius_client_retransmit(struct radius_client_data *radius,
+ struct radius_msg_list *entry, time_t now)
+{
+ struct hostapd_data *hapd = radius->hapd;
+ int s;
+
+ if (entry->msg_type == RADIUS_ACCT ||
+ entry->msg_type == RADIUS_ACCT_INTERIM) {
+ s = radius->acct_serv_sock;
+ if (entry->attempts == 0)
+ hapd->conf->acct_server->requests++;
+ else {
+ hapd->conf->acct_server->timeouts++;
+ hapd->conf->acct_server->retransmissions++;
+ }
+ } else {
+ s = radius->auth_serv_sock;
+ if (entry->attempts == 0)
+ hapd->conf->auth_server->requests++;
+ else {
+ hapd->conf->auth_server->timeouts++;
+ hapd->conf->auth_server->retransmissions++;
+ }
+ }
+
+ /* retransmit; remove entry if too many attempts */
+ entry->attempts++;
+ HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "Resending RADIUS message (id=%d)"
+ "\n", entry->msg->hdr->identifier);
+
+ gettimeofday(&entry->last_attempt, NULL);
+ if (send(s, entry->msg->buf, entry->msg->buf_used, 0) < 0)
+ radius_client_handle_send_error(radius, s, entry->msg_type);
+
+ 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 radius_client_data *radius = eloop_ctx;
+ struct hostapd_data *hapd = radius->hapd;
+ time_t now, first;
+ struct radius_msg_list *entry, *prev, *tmp;
+ int auth_failover = 0, acct_failover = 0;
+
+ entry = radius->msgs;
+ if (!entry)
+ return;
+
+ time(&now);
+ first = 0;
+
+ prev = NULL;
+ while (entry) {
+ if (now >= entry->next_try &&
+ radius_client_retransmit(radius, entry, now)) {
+ if (prev)
+ prev->next = entry->next;
+ else
+ radius->msgs = entry->next;
+
+ tmp = entry;
+ entry = entry->next;
+ radius_client_msg_free(tmp);
+ 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 (radius->msgs) {
+ if (first < now)
+ first = now;
+ eloop_register_timeout(first - now, 0,
+ radius_client_timer, radius, NULL);
+ HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "Next RADIUS client "
+ "retransmit in %ld seconds\n",
+ (long int) (first - now));
+
+ }
+
+ if (auth_failover && hapd->conf->num_auth_servers > 1) {
+ struct hostapd_radius_server *next, *old;
+ old = hapd->conf->auth_server;
+ hostapd_logger(hapd, NULL, HOSTAPD_MODULE_RADIUS,
+ HOSTAPD_LEVEL_NOTICE,
+ "No response from Authentication server "
+ "%s:%d - failover",
+ inet_ntoa(old->addr), old->port);
+
+ for (entry = radius->msgs; entry; entry = entry->next) {
+ if (entry->msg_type == RADIUS_AUTH)
+ old->timeouts++;
+ }
+
+ next = old + 1;
+ if (next > &(hapd->conf->auth_servers
+ [hapd->conf->num_auth_servers - 1]))
+ next = hapd->conf->auth_servers;
+ hapd->conf->auth_server = next;
+ radius_change_server(radius, next, old,
+ radius->auth_serv_sock, 1);
+ }
+
+ if (acct_failover && hapd->conf->num_acct_servers > 1) {
+ struct hostapd_radius_server *next, *old;
+ old = hapd->conf->acct_server;
+ hostapd_logger(hapd, NULL, HOSTAPD_MODULE_RADIUS,
+ HOSTAPD_LEVEL_NOTICE,
+ "No response from Accounting server "
+ "%s:%d - failover",
+ inet_ntoa(old->addr), old->port);
+
+ for (entry = radius->msgs; entry; entry = entry->next) {
+ if (entry->msg_type == RADIUS_ACCT ||
+ entry->msg_type == RADIUS_ACCT_INTERIM)
+ old->timeouts++;
+ }
+
+ next = old + 1;
+ if (next > &hapd->conf->acct_servers
+ [hapd->conf->num_acct_servers - 1])
+ next = hapd->conf->acct_servers;
+ hapd->conf->acct_server = next;
+ radius_change_server(radius, next, old,
+ radius->acct_serv_sock, 0);
+ }
+}
+
+
+static void radius_client_update_timeout(struct radius_client_data *radius)
+{
+ struct hostapd_data *hapd = radius->hapd;
+ time_t now, first;
+ struct radius_msg_list *entry;
+
+ eloop_cancel_timeout(radius_client_timer, radius, NULL);
+
+ if (radius->msgs == NULL) {
+ return;
+ }
+
+ first = 0;
+ for (entry = radius->msgs; entry; entry = entry->next) {
+ if (first == 0 || entry->next_try < first)
+ first = entry->next_try;
+ }
+
+ time(&now);
+ if (first < now)
+ first = now;
+ eloop_register_timeout(first - now, 0, radius_client_timer, radius,
+ NULL);
+ HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "Next RADIUS client retransmit in"
+ " %ld seconds\n", (long int) (first - now));
+}
+
+
+static void radius_client_list_add(struct radius_client_data *radius,
+ 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;
+ gettimeofday(&entry->last_attempt, NULL);
+ entry->next_wait = RADIUS_CLIENT_FIRST_WAIT * 2;
+ entry->next = radius->msgs;
+ radius->msgs = entry;
+ radius_client_update_timeout(radius);
+
+ if (radius->num_msgs >= RADIUS_CLIENT_MAX_ENTRIES) {
+ printf("Removing the oldest un-ACKed RADIUS packet due to "
+ "retransmit list limits.\n");
+ prev = NULL;
+ while (entry->next) {
+ prev = entry;
+ entry = entry->next;
+ }
+ if (prev) {
+ prev->next = NULL;
+ radius_client_msg_free(entry);
+ }
+ } else
+ radius->num_msgs++;
+}
+
+
+static void radius_client_list_del(struct radius_client_data *radius,
+ RadiusType msg_type, u8 *addr)
+{
+ struct hostapd_data *hapd = radius->hapd;
+ struct radius_msg_list *entry, *prev, *tmp;
+
+ if (addr == NULL)
+ return;
+
+ entry = 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
+ 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);
+ radius->num_msgs--;
+ continue;
+ }
+ prev = entry;
+ entry = entry->next;
+ }
+}
+
+
+int radius_client_send(struct radius_client_data *radius,
+ struct radius_msg *msg, RadiusType msg_type, u8 *addr)
+{
+ struct hostapd_data *hapd = radius->hapd;
+ 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(radius, msg_type, addr);
+ }
+
+ if (msg_type == RADIUS_ACCT || msg_type == RADIUS_ACCT_INTERIM) {
+ shared_secret = hapd->conf->acct_server->shared_secret;
+ shared_secret_len = hapd->conf->acct_server->shared_secret_len;
+ radius_msg_finish_acct(msg, shared_secret, shared_secret_len);
+ name = "accounting";
+ s = radius->acct_serv_sock;
+ hapd->conf->acct_server->requests++;
+ } else {
+ shared_secret = hapd->conf->auth_server->shared_secret;
+ shared_secret_len = hapd->conf->auth_server->shared_secret_len;
+ radius_msg_finish(msg, shared_secret, shared_secret_len);
+ name = "authentication";
+ s = radius->auth_serv_sock;
+ hapd->conf->auth_server->requests++;
+ }
+
+ 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)
+ radius_client_handle_send_error(radius, s, msg_type);
+
+ radius_client_list_add(radius, 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 radius_client_data *radius = eloop_ctx;
+ struct hostapd_data *hapd = radius->hapd;
+ RadiusType msg_type = (RadiusType) sock_ctx;
+ int len, i, roundtrip;
+ unsigned char buf[3000];
+ struct radius_msg *msg;
+ struct radius_rx_handler *handlers;
+ size_t num_handlers;
+ struct radius_msg_list *req, *prev_req;
+ struct timeval tv;
+ struct hostapd_radius_server *rconf;
+ int invalid_authenticator = 0;
+
+ if (msg_type == RADIUS_ACCT) {
+ handlers = radius->acct_handlers;
+ num_handlers = radius->num_acct_handlers;
+ rconf = hapd->conf->acct_server;
+ } else {
+ handlers = radius->auth_handlers;
+ num_handlers = radius->num_auth_handlers;
+ rconf = hapd->conf->auth_server;
+ }
+
+ 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");
+ rconf->malformed_responses++;
+ return;
+ }
+
+ HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL,
+ "Received RADIUS message\n");
+ if (HOSTAPD_DEBUG_COND(HOSTAPD_DEBUG_MSGDUMPS))
+ radius_msg_dump(msg);
+
+ switch (msg->hdr->code) {
+ case RADIUS_CODE_ACCESS_ACCEPT:
+ rconf->access_accepts++;
+ break;
+ case RADIUS_CODE_ACCESS_REJECT:
+ rconf->access_rejects++;
+ break;
+ case RADIUS_CODE_ACCESS_CHALLENGE:
+ rconf->access_challenges++;
+ break;
+ case RADIUS_CODE_ACCOUNTING_RESPONSE:
+ rconf->responses++;
+ break;
+ }
+
+ prev_req = NULL;
+ req = 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;
+ }
+
+ gettimeofday(&tv, NULL);
+ roundtrip = (tv.tv_sec - req->last_attempt.tv_sec) * 100 +
+ (tv.tv_usec - req->last_attempt.tv_usec) / 10000;
+ HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "Received RADIUS packet matched "
+ "with a pending request, round trip time %d.%02d sec\n",
+ roundtrip / 100, roundtrip % 100);
+ rconf->round_trip_time = roundtrip;
+
+ /* Remove ACKed RADIUS packet from retransmit list */
+ if (prev_req)
+ prev_req->next = req->next;
+ else
+ radius->msgs = req->next;
+ radius->num_msgs--;
+
+ for (i = 0; i < num_handlers; i++) {
+ RadiusRxResult res;
+ res = handlers[i].handler(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_INVALID_AUTHENTICATOR:
+ invalid_authenticator++;
+ /* continue */
+ case RADIUS_RX_UNKNOWN:
+ /* continue with next handler */
+ break;
+ }
+ }
+
+ if (invalid_authenticator)
+ rconf->bad_authenticators++;
+ else
+ rconf->unknown_types++;
+ hostapd_logger(hapd, req->addr, HOSTAPD_MODULE_RADIUS,
+ HOSTAPD_LEVEL_DEBUG, "No RADIUS RX handler found "
+ "(type=%d code=%d id=%d)%s - dropping packet",
+ msg_type, msg->hdr->code, msg->hdr->identifier,
+ invalid_authenticator ? " [INVALID AUTHENTICATOR]" :
+ "");
+ radius_client_msg_free(req);
+
+ fail:
+ radius_msg_free(msg);
+ free(msg);
+}
+
+
+u8 radius_client_get_id(struct radius_client_data *radius)
+{
+ struct hostapd_data *hapd = radius->hapd;
+ struct radius_msg_list *entry, *prev, *remove;
+ u8 id = 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 = 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
+ 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 radius_client_data *radius)
+{
+ struct radius_msg_list *entry, *prev;
+
+ if (!radius)
+ return;
+
+ eloop_cancel_timeout(radius_client_timer, radius, NULL);
+
+ entry = radius->msgs;
+ radius->msgs = NULL;
+ radius->num_msgs = 0;
+ while (entry) {
+ prev = entry;
+ entry = entry->next;
+ radius_client_msg_free(prev);
+ }
+}
+
+
+static int
+radius_change_server(struct radius_client_data *radius,
+ struct hostapd_radius_server *nserv,
+ struct hostapd_radius_server *oserv,
+ int sock, int auth)
+{
+ struct hostapd_data *hapd = radius->hapd;
+ struct sockaddr_in serv;
+
+ hostapd_logger(hapd, 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(radius);
+ } else {
+ /* Reset retry counters for the new server */
+ struct radius_msg_list *entry;
+ entry = 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 (radius->msgs) {
+ eloop_cancel_timeout(radius_client_timer, radius,
+ NULL);
+ eloop_register_timeout(RADIUS_CLIENT_FIRST_WAIT, 0,
+ radius_client_timer, radius,
+ 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 radius_client_data *radius = eloop_ctx;
+ struct hostapd_data *hapd = radius->hapd;
+ struct hostapd_radius_server *oserv;
+
+ if (radius->auth_serv_sock >= 0 && hapd->conf->auth_servers &&
+ hapd->conf->auth_server != hapd->conf->auth_servers) {
+ oserv = hapd->conf->auth_server;
+ hapd->conf->auth_server = hapd->conf->auth_servers;
+ radius_change_server(radius, hapd->conf->auth_server, oserv,
+ radius->auth_serv_sock, 1);
+ }
+
+ if (radius->acct_serv_sock >= 0 && hapd->conf->acct_servers &&
+ hapd->conf->acct_server != hapd->conf->acct_servers) {
+ oserv = hapd->conf->acct_server;
+ hapd->conf->acct_server = hapd->conf->acct_servers;
+ radius_change_server(radius, hapd->conf->acct_server, oserv,
+ radius->acct_serv_sock, 0);
+ }
+
+ if (hapd->conf->radius_retry_primary_interval)
+ eloop_register_timeout(hapd->conf->
+ radius_retry_primary_interval, 0,
+ radius_retry_primary_timer, radius,
+ NULL);
+}
+
+
+static int radius_client_init_auth(struct radius_client_data *radius)
+{
+ struct hostapd_data *hapd = radius->hapd;
+ radius->auth_serv_sock = socket(PF_INET, SOCK_DGRAM, 0);
+ if (radius->auth_serv_sock < 0) {
+ perror("socket[PF_INET,SOCK_DGRAM]");
+ return -1;
+ }
+
+ radius_change_server(radius, hapd->conf->auth_server, NULL,
+ radius->auth_serv_sock, 1);
+
+ if (eloop_register_read_sock(radius->auth_serv_sock,
+ radius_client_receive, radius,
+ (void *) RADIUS_AUTH)) {
+ printf("Could not register read socket for authentication "
+ "server\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int radius_client_init_acct(struct radius_client_data *radius)
+{
+ struct hostapd_data *hapd = radius->hapd;
+ radius->acct_serv_sock = socket(PF_INET, SOCK_DGRAM, 0);
+ if (radius->acct_serv_sock < 0) {
+ perror("socket[PF_INET,SOCK_DGRAM]");
+ return -1;
+ }
+
+ radius_change_server(radius, hapd->conf->acct_server, NULL,
+ radius->acct_serv_sock, 0);
+
+ if (eloop_register_read_sock(radius->acct_serv_sock,
+ radius_client_receive, radius,
+ (void *) RADIUS_ACCT)) {
+ printf("Could not register read socket for accounting "
+ "server\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+struct radius_client_data * radius_client_init(struct hostapd_data *hapd)
+{
+ struct radius_client_data *radius;
+
+ radius = malloc(sizeof(struct radius_client_data));
+ if (radius == NULL)
+ return NULL;
+
+ memset(radius, 0, sizeof(struct radius_client_data));
+ radius->hapd = hapd;
+ radius->auth_serv_sock = radius->acct_serv_sock = -1;
+
+ if (hapd->conf->auth_server && radius_client_init_auth(radius)) {
+ radius_client_deinit(radius);
+ return NULL;
+ }
+
+ if (hapd->conf->acct_server && radius_client_init_acct(radius)) {
+ radius_client_deinit(radius);
+ return NULL;
+ }
+
+ if (hapd->conf->radius_retry_primary_interval)
+ eloop_register_timeout(hapd->conf->
+ radius_retry_primary_interval, 0,
+ radius_retry_primary_timer, radius,
+ NULL);
+
+ return radius;
+}
+
+
+void radius_client_deinit(struct radius_client_data *radius)
+{
+ if (!radius)
+ return;
+
+ eloop_cancel_timeout(radius_retry_primary_timer, radius, NULL);
+
+ radius_client_flush(radius);
+ free(radius->auth_handlers);
+ free(radius->acct_handlers);
+ free(radius);
+}
+
+
+void radius_client_flush_auth(struct radius_client_data *radius, u8 *addr)
+{
+ struct hostapd_data *hapd = radius->hapd;
+ struct radius_msg_list *entry, *prev, *tmp;
+
+ prev = NULL;
+ entry = radius->msgs;
+ while (entry) {
+ if (entry->msg_type == RADIUS_AUTH &&
+ memcmp(entry->addr, addr, ETH_ALEN) == 0) {
+ hostapd_logger(hapd, addr, HOSTAPD_MODULE_RADIUS,
+ HOSTAPD_LEVEL_DEBUG,
+ "Removing pending RADIUS authentication"
+ " message for removed client");
+
+ if (prev)
+ prev->next = entry->next;
+ else
+ radius->msgs = entry->next;
+
+ tmp = entry;
+ entry = entry->next;
+ radius_client_msg_free(tmp);
+ radius->num_msgs--;
+ continue;
+ }
+
+ prev = entry;
+ entry = entry->next;
+ }
+}
+
+
+static int radius_client_dump_auth_server(char *buf, size_t buflen,
+ struct hostapd_radius_server *serv,
+ struct radius_client_data *cli)
+{
+ int pending = 0;
+ struct radius_msg_list *msg;
+
+ if (cli) {
+ for (msg = cli->msgs; msg; msg = msg->next) {
+ if (msg->msg_type == RADIUS_AUTH)
+ pending++;
+ }
+ }
+
+ return snprintf(buf, buflen,
+ "radiusAuthServerIndex=%d\n"
+ "radiusAuthServerAddress=%s\n"
+ "radiusAuthClientServerPortNumber=%d\n"
+ "radiusAuthClientRoundTripTime=%d\n"
+ "radiusAuthClientAccessRequests=%u\n"
+ "radiusAuthClientAccessRetransmissions=%u\n"
+ "radiusAuthClientAccessAccepts=%u\n"
+ "radiusAuthClientAccessRejects=%u\n"
+ "radiusAuthClientAccessChallenges=%u\n"
+ "radiusAuthClientMalformedAccessResponses=%u\n"
+ "radiusAuthClientBadAuthenticators=%u\n"
+ "radiusAuthClientPendingRequests=%u\n"
+ "radiusAuthClientTimeouts=%u\n"
+ "radiusAuthClientUnknownTypes=%u\n"
+ "radiusAuthClientPacketsDropped=%u\n",
+ serv->index,
+ inet_ntoa(serv->addr),
+ serv->port,
+ serv->round_trip_time,
+ serv->requests,
+ serv->retransmissions,
+ serv->access_accepts,
+ serv->access_rejects,
+ serv->access_challenges,
+ serv->malformed_responses,
+ serv->bad_authenticators,
+ pending,
+ serv->timeouts,
+ serv->unknown_types,
+ serv->packets_dropped);
+}
+
+
+static int radius_client_dump_acct_server(char *buf, size_t buflen,
+ struct hostapd_radius_server *serv,
+ struct radius_client_data *cli)
+{
+ int pending = 0;
+ struct radius_msg_list *msg;
+
+ if (cli) {
+ for (msg = cli->msgs; msg; msg = msg->next) {
+ if (msg->msg_type == RADIUS_ACCT ||
+ msg->msg_type == RADIUS_ACCT_INTERIM)
+ pending++;
+ }
+ }
+
+ return snprintf(buf, buflen,
+ "radiusAccServerIndex=%d\n"
+ "radiusAccServerAddress=%s\n"
+ "radiusAccClientServerPortNumber=%d\n"
+ "radiusAccClientRoundTripTime=%d\n"
+ "radiusAccClientRequests=%u\n"
+ "radiusAccClientRetransmissions=%u\n"
+ "radiusAccClientResponses=%u\n"
+ "radiusAccClientMalformedResponses=%u\n"
+ "radiusAccClientBadAuthenticators=%u\n"
+ "radiusAccClientPendingRequests=%u\n"
+ "radiusAccClientTimeouts=%u\n"
+ "radiusAccClientUnknownTypes=%u\n"
+ "radiusAccClientPacketsDropped=%u\n",
+ serv->index,
+ inet_ntoa(serv->addr),
+ serv->port,
+ serv->round_trip_time,
+ serv->requests,
+ serv->retransmissions,
+ serv->responses,
+ serv->malformed_responses,
+ serv->bad_authenticators,
+ pending,
+ serv->timeouts,
+ serv->unknown_types,
+ serv->packets_dropped);
+}
+
+
+int radius_client_get_mib(struct radius_client_data *radius, char *buf,
+ size_t buflen)
+{
+ struct hostapd_data *hapd = radius->hapd;
+ int i;
+ struct hostapd_radius_server *serv;
+ int count = 0;
+
+ if (hapd->conf->auth_servers) {
+ for (i = 0; i < hapd->conf->num_auth_servers; i++) {
+ serv = &hapd->conf->auth_servers[i];
+ count += radius_client_dump_auth_server(
+ buf + count, buflen - count, serv,
+ serv == hapd->conf->auth_server ?
+ radius : NULL);
+ }
+ }
+
+ if (hapd->conf->acct_servers) {
+ for (i = 0; i < hapd->conf->num_acct_servers; i++) {
+ serv = &hapd->conf->acct_servers[i];
+ count += radius_client_dump_acct_server(
+ buf + count, buflen - count, serv,
+ serv == hapd->conf->acct_server ?
+ radius : NULL);
+ }
+ }
+
+ return count;
+}
diff --git a/contrib/hostapd/radius_client.h b/contrib/hostapd/radius_client.h
new file mode 100644
index 000000000000..cff201a665e7
--- /dev/null
+++ b/contrib/hostapd/radius_client.h
@@ -0,0 +1,41 @@
+#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;
+
+typedef enum {
+ RADIUS_RX_PROCESSED,
+ RADIUS_RX_QUEUED,
+ RADIUS_RX_UNKNOWN,
+ RADIUS_RX_INVALID_AUTHENTICATOR
+} RadiusRxResult;
+
+struct radius_client_data;
+
+int radius_client_register(struct radius_client_data *radius,
+ RadiusType msg_type,
+ RadiusRxResult (*handler)
+ (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 radius_client_data *radius,
+ struct radius_msg *msg,
+ RadiusType msg_type, u8 *addr);
+u8 radius_client_get_id(struct radius_client_data *radius);
+
+void radius_client_flush(struct radius_client_data *radius);
+struct radius_client_data * radius_client_init(struct hostapd_data *hapd);
+void radius_client_deinit(struct radius_client_data *radius);
+void radius_client_flush_auth(struct radius_client_data *radius, u8 *addr);
+int radius_client_get_mib(struct radius_client_data *radius, char *buf,
+ size_t buflen);
+
+#endif /* RADIUS_CLIENT_H */
diff --git a/contrib/hostapd/radius_server.c b/contrib/hostapd/radius_server.c
new file mode 100644
index 000000000000..bfc784f4759a
--- /dev/null
+++ b/contrib/hostapd/radius_server.c
@@ -0,0 +1,923 @@
+/*
+ * hostapd / RADIUS authentication server
+ * Copyright (c) 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 <unistd.h>
+#include <string.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <net/if.h>
+
+#include "common.h"
+#include "radius.h"
+#include "eloop.h"
+#include "config.h"
+#include "eap.h"
+#include "radius_server.h"
+
+#define RADIUS_SESSION_TIMEOUT 60
+#define RADIUS_MAX_SESSION 100
+#define RADIUS_MAX_MSG_LEN 3000
+
+static struct eapol_callbacks radius_server_eapol_cb;
+
+struct radius_client;
+struct radius_server_data;
+
+struct radius_session {
+ struct radius_session *next;
+ struct radius_client *client;
+ struct radius_server_data *server;
+ unsigned int sess_id;
+ struct eap_sm *eap;
+ u8 *eapKeyData, *eapReqData;
+ size_t eapKeyDataLen, eapReqDataLen;
+ Boolean eapSuccess, eapRestart, eapFail, eapResp, eapReq, eapNoReq;
+ Boolean portEnabled, eapTimeout;
+};
+
+struct radius_client {
+ struct radius_client *next;
+ struct in_addr addr;
+ struct in_addr mask;
+ char *shared_secret;
+ int shared_secret_len;
+ struct radius_session *sessions;
+};
+
+struct radius_server_data {
+ int auth_sock;
+ struct radius_client *clients;
+ struct radius_server_session *sessions;
+ unsigned int next_sess_id;
+ void *hostapd_conf;
+ int num_sess;
+ void *eap_sim_db_priv;
+ void *ssl_ctx;
+};
+
+
+extern int wpa_debug_level;
+
+#define RADIUS_DEBUG(args...) \
+wpa_printf(MSG_DEBUG, "RADIUS SRV: " args)
+#define RADIUS_ERROR(args...) \
+wpa_printf(MSG_ERROR, "RADIUS SRV: " args)
+#define RADIUS_DUMP(args...) \
+wpa_hexdump(MSG_MSGDUMP, "RADIUS SRV: " args)
+#define RADIUS_DUMP_ASCII(args...) \
+wpa_hexdump_ascii(MSG_MSGDUMP, "RADIUS SRV: " args)
+
+
+static void radius_server_session_timeout(void *eloop_ctx, void *timeout_ctx);
+
+
+
+static struct radius_client *
+radius_server_get_client(struct radius_server_data *data, struct in_addr *addr)
+{
+ struct radius_client *client = data->clients;
+
+ while (client) {
+ if ((client->addr.s_addr & client->mask.s_addr) ==
+ (addr->s_addr & client->mask.s_addr)) {
+ break;
+ }
+
+ client = client->next;
+ }
+
+ return client;
+}
+
+
+static struct radius_session *
+radius_server_get_session(struct radius_client *client, unsigned int sess_id)
+{
+ struct radius_session *sess = client->sessions;
+
+ while (sess) {
+ if (sess->sess_id == sess_id) {
+ break;
+ }
+ sess = sess->next;
+ }
+
+ return sess;
+}
+
+
+static void radius_server_session_free(struct radius_server_data *data,
+ struct radius_session *sess)
+{
+ eloop_cancel_timeout(radius_server_session_timeout, data, sess);
+ free(sess->eapKeyData);
+ free(sess->eapReqData);
+ eap_sm_deinit(sess->eap);
+ free(sess);
+ data->num_sess--;
+}
+
+
+static void radius_server_session_remove(struct radius_server_data *data,
+ struct radius_session *sess)
+{
+ struct radius_client *client = sess->client;
+ struct radius_session *session, *prev;
+
+ prev = NULL;
+ session = client->sessions;
+ while (session) {
+ if (session == sess) {
+ if (prev == NULL) {
+ client->sessions = sess->next;
+ } else {
+ prev->next = sess->next;
+ }
+ radius_server_session_free(data, sess);
+ break;
+ }
+ prev = session;
+ session = session->next;
+ }
+}
+
+
+static void radius_server_session_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ struct radius_server_data *data = eloop_ctx;
+ struct radius_session *sess = timeout_ctx;
+
+ RADIUS_DEBUG("Timing out authentication session 0x%x", sess->sess_id);
+ radius_server_session_remove(data, sess);
+}
+
+
+static struct radius_session *
+radius_server_new_session(struct radius_server_data *data,
+ struct radius_client *client)
+{
+ struct radius_session *sess;
+
+ if (data->num_sess >= RADIUS_MAX_SESSION) {
+ RADIUS_DEBUG("Maximum number of existing session - no room "
+ "for a new session");
+ return NULL;
+ }
+
+ sess = malloc(sizeof(*sess));
+ if (sess == NULL) {
+ return NULL;
+ }
+ memset(sess, 0, sizeof(*sess));
+ sess->server = data;
+ sess->client = client;
+ sess->sess_id = data->next_sess_id++;
+ sess->next = client->sessions;
+ client->sessions = sess;
+ eloop_register_timeout(RADIUS_SESSION_TIMEOUT, 0,
+ radius_server_session_timeout, data, sess);
+ data->num_sess++;
+ return sess;
+}
+
+
+static struct radius_session *
+radius_server_get_new_session(struct radius_server_data *data,
+ struct radius_client *client,
+ struct radius_msg *msg)
+{
+ u8 *user;
+ size_t user_len;
+ const struct hostapd_eap_user *eap_user;
+ int res;
+ struct radius_session *sess;
+ struct eap_config eap_conf;
+
+ RADIUS_DEBUG("Creating a new session");
+
+ user = malloc(256);
+ if (user == NULL) {
+ return NULL;
+ }
+ res = radius_msg_get_attr(msg, RADIUS_ATTR_USER_NAME, user, 256);
+ if (res < 0 || res > 256) {
+ RADIUS_DEBUG("Could not get User-Name");
+ free(user);
+ return NULL;
+ }
+ user_len = res;
+ RADIUS_DUMP_ASCII("User-Name", user, user_len);
+
+ eap_user = hostapd_get_eap_user(data->hostapd_conf, user, user_len, 0);
+ free(user);
+
+ if (eap_user) {
+ RADIUS_DEBUG("Matching user entry found");
+ sess = radius_server_new_session(data, client);
+ if (sess == NULL) {
+ RADIUS_DEBUG("Failed to create a new session");
+ return NULL;
+ }
+ } else {
+ RADIUS_DEBUG("User-Name not found from user database");
+ return NULL;
+ }
+
+ memset(&eap_conf, 0, sizeof(eap_conf));
+ eap_conf.ssl_ctx = data->ssl_ctx;
+ eap_conf.eap_sim_db_priv = data->eap_sim_db_priv;
+ eap_conf.backend_auth = TRUE;
+ sess->eap = eap_sm_init(sess, &radius_server_eapol_cb, &eap_conf);
+ if (sess->eap == NULL) {
+ RADIUS_DEBUG("Failed to initialize EAP state machine for the "
+ "new session");
+ radius_server_session_free(data, sess);
+ return NULL;
+ }
+ sess->eapRestart = TRUE;
+ sess->portEnabled = TRUE;
+
+ RADIUS_DEBUG("New session 0x%x initialized", sess->sess_id);
+
+ return sess;
+}
+
+
+static struct radius_msg *
+radius_server_encapsulate_eap(struct radius_server_data *data,
+ struct radius_client *client,
+ struct radius_session *sess,
+ struct radius_msg *request)
+{
+ struct radius_msg *msg;
+ int code;
+ unsigned int sess_id;
+
+ if (sess->eapFail) {
+ code = RADIUS_CODE_ACCESS_REJECT;
+ } else if (sess->eapSuccess) {
+ code = RADIUS_CODE_ACCESS_ACCEPT;
+ } else {
+ code = RADIUS_CODE_ACCESS_CHALLENGE;
+ }
+
+ msg = radius_msg_new(code, request->hdr->identifier);
+ if (msg == NULL) {
+ return NULL;
+ }
+
+ sess_id = htonl(sess->sess_id);
+ if (code == RADIUS_CODE_ACCESS_CHALLENGE &&
+ !radius_msg_add_attr(msg, RADIUS_ATTR_STATE,
+ (u8 *) &sess_id, sizeof(sess_id))) {
+ RADIUS_DEBUG("Failed to add State attribute");
+ }
+
+ if (sess->eapReqData &&
+ !radius_msg_add_eap(msg, sess->eapReqData, sess->eapReqDataLen)) {
+ RADIUS_DEBUG("Failed to add EAP-Message attribute");
+ }
+
+ if (code == RADIUS_CODE_ACCESS_ACCEPT && sess->eapKeyData) {
+ int len;
+ if (sess->eapKeyDataLen > 64) {
+ len = 32;
+ } else {
+ len = sess->eapKeyDataLen / 2;
+ }
+ if (!radius_msg_add_mppe_keys(msg, request->hdr->authenticator,
+ (u8 *) client->shared_secret,
+ client->shared_secret_len,
+ sess->eapKeyData + len, len,
+ sess->eapKeyData, len)) {
+ RADIUS_DEBUG("Failed to add MPPE key attributes");
+ }
+ }
+
+ if (radius_msg_finish_srv(msg, (u8 *) client->shared_secret,
+ client->shared_secret_len,
+ request->hdr->authenticator) < 0) {
+ RADIUS_DEBUG("Failed to add Message-Authenticator attribute");
+ }
+
+ return msg;
+}
+
+
+static int radius_server_reject(struct radius_server_data *data,
+ struct radius_client *client,
+ struct radius_msg *request,
+ struct sockaddr_in *from)
+{
+ struct radius_msg *msg;
+ int ret = 0;
+
+ RADIUS_DEBUG("Reject invalid request from %s:%d",
+ inet_ntoa(from->sin_addr), ntohs(from->sin_port));
+
+ msg = radius_msg_new(RADIUS_CODE_ACCESS_REJECT,
+ request->hdr->identifier);
+ if (msg == NULL) {
+ return -1;
+ }
+
+ if (radius_msg_finish_srv(msg, (u8 *) client->shared_secret,
+ client->shared_secret_len,
+ request->hdr->authenticator) < 0) {
+ RADIUS_DEBUG("Failed to add Message-Authenticator attribute");
+ }
+
+ if (wpa_debug_level <= MSG_MSGDUMP) {
+ radius_msg_dump(msg);
+ }
+
+ if (sendto(data->auth_sock, msg->buf, msg->buf_used, 0,
+ (struct sockaddr *) from, sizeof(*from)) < 0) {
+ perror("sendto[RADIUS SRV]");
+ ret = -1;
+ }
+
+ radius_msg_free(msg);
+ free(msg);
+
+ return ret;
+}
+
+
+static int radius_server_request(struct radius_server_data *data,
+ struct radius_msg *msg,
+ struct sockaddr_in *from,
+ struct radius_client *client)
+{
+ u8 *eap = NULL;
+ size_t eap_len;
+ int res, state_included;
+ u8 statebuf[4], resp_id;
+ unsigned int state;
+ struct radius_session *sess;
+ struct radius_msg *reply;
+ struct eap_hdr *hdr;
+
+ /* TODO: Implement duplicate packet processing */
+
+ res = radius_msg_get_attr(msg, RADIUS_ATTR_STATE, statebuf,
+ sizeof(statebuf));
+ state_included = res >= 0;
+ if (res == sizeof(statebuf)) {
+ state = (statebuf[0] << 24) | (statebuf[1] << 16) |
+ (statebuf[2] << 8) | statebuf[3];
+ sess = radius_server_get_session(client, state);
+ } else {
+ sess = NULL;
+ }
+
+ if (sess) {
+ RADIUS_DEBUG("Request for session 0x%x", sess->sess_id);
+ } else if (state_included) {
+ RADIUS_DEBUG("State attribute included but no session found");
+ radius_server_reject(data, client, msg, from);
+ return -1;
+ } else {
+ sess = radius_server_get_new_session(data, client, msg);
+ if (sess == NULL) {
+ RADIUS_DEBUG("Could not create a new session");
+ return -1;
+ }
+ }
+
+ eap = radius_msg_get_eap(msg, &eap_len);
+ if (eap == NULL) {
+ RADIUS_DEBUG("No EAP-Message in RADIUS packet from %s",
+ inet_ntoa(from->sin_addr));
+ return -1;
+ }
+
+ RADIUS_DUMP("Received EAP data", eap, eap_len);
+ if (eap_len >= sizeof(*hdr)) {
+ hdr = (struct eap_hdr *) eap;
+ resp_id = hdr->identifier;
+ } else {
+ resp_id = 0;
+ }
+
+ eap_set_eapRespData(sess->eap, eap, eap_len);
+ free(eap);
+ eap = NULL;
+ sess->eapResp = TRUE;
+ eap_sm_step(sess->eap);
+
+ if (sess->eapReqData) {
+ RADIUS_DUMP("EAP data from the state machine",
+ sess->eapReqData, sess->eapReqDataLen);
+ } else if (sess->eapFail) {
+ RADIUS_DEBUG("No EAP data from the state machine, but eapFail "
+ "set - generate EAP-Failure");
+ hdr = malloc(sizeof(*hdr));
+ if (hdr) {
+ memset(hdr, 0, sizeof(*hdr));
+ hdr->identifier = resp_id;
+ hdr->length = htons(sizeof(*hdr));
+ sess->eapReqData = (u8 *) hdr;
+ sess->eapReqDataLen = sizeof(*hdr);
+ }
+ } else {
+ RADIUS_DEBUG("No EAP data from the state machine - ignore this"
+ " Access-Request silently (assuming it was a "
+ "duplicate)");
+ return -1;
+ }
+
+ reply = radius_server_encapsulate_eap(data, client, sess, msg);
+
+ free(sess->eapReqData);
+ sess->eapReqData = NULL;
+ sess->eapReqDataLen = 0;
+
+ if (reply) {
+ RADIUS_DEBUG("Reply to %s:%d", inet_ntoa(from->sin_addr),
+ ntohs(from->sin_port));
+ if (wpa_debug_level <= MSG_MSGDUMP) {
+ radius_msg_dump(reply);
+ }
+
+ res = sendto(data->auth_sock, reply->buf, reply->buf_used, 0,
+ (struct sockaddr *) from, sizeof(*from));
+ if (res < 0) {
+ perror("sendto[RADIUS SRV]");
+ }
+ radius_msg_free(reply);
+ free(reply);
+ }
+
+ if (sess->eapSuccess || sess->eapFail) {
+ RADIUS_DEBUG("Removing completed session 0x%x", sess->sess_id);
+ radius_server_session_remove(data, sess);
+ }
+
+ return 0;
+}
+
+
+static void radius_server_receive_auth(int sock, void *eloop_ctx,
+ void *sock_ctx)
+{
+ struct radius_server_data *data = eloop_ctx;
+ u8 *buf = NULL;
+ struct sockaddr_in from;
+ socklen_t fromlen;
+ int len;
+ struct radius_client *client;
+ struct radius_msg *msg = NULL;
+
+ buf = malloc(RADIUS_MAX_MSG_LEN);
+ if (buf == NULL) {
+ goto fail;
+ }
+
+ fromlen = sizeof(from);
+ len = recvfrom(sock, buf, RADIUS_MAX_MSG_LEN, 0,
+ (struct sockaddr *) &from, &fromlen);
+ if (len < 0) {
+ perror("recvfrom[radius_server]");
+ goto fail;
+ }
+
+ RADIUS_DEBUG("Received %d bytes from %s:%d",
+ len, inet_ntoa(from.sin_addr), ntohs(from.sin_port));
+ RADIUS_DUMP("Received data", buf, len);
+
+ client = radius_server_get_client(data, &from.sin_addr);
+ if (client == NULL) {
+ RADIUS_DEBUG("Unknown client %s - packet ignored",
+ inet_ntoa(from.sin_addr));
+ goto fail;
+ }
+
+ msg = radius_msg_parse(buf, len);
+ if (msg == NULL) {
+ RADIUS_DEBUG("Parsing incoming RADIUS frame failed");
+ goto fail;
+ }
+
+ free(buf);
+ buf = NULL;
+
+ if (wpa_debug_level <= MSG_MSGDUMP) {
+ radius_msg_dump(msg);
+ }
+
+ if (msg->hdr->code != RADIUS_CODE_ACCESS_REQUEST) {
+ RADIUS_DEBUG("Unexpected RADIUS code %d", msg->hdr->code);
+ goto fail;
+ }
+
+ if (radius_msg_verify_msg_auth(msg, (u8 *) client->shared_secret,
+ client->shared_secret_len, NULL)) {
+ RADIUS_DEBUG("Invalid Message-Authenticator from %s",
+ inet_ntoa(from.sin_addr));
+ goto fail;
+ }
+
+ radius_server_request(data, msg, &from, client);
+
+fail:
+ if (msg) {
+ radius_msg_free(msg);
+ free(msg);
+ }
+ free(buf);
+}
+
+
+static int radius_server_open_socket(int port)
+{
+ int s;
+ struct sockaddr_in addr;
+
+ s = socket(PF_INET, SOCK_DGRAM, 0);
+ if (s < 0) {
+ perror("socket");
+ return -1;
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(port);
+ if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ perror("bind");
+ close(s);
+ return -1;
+ }
+
+ return s;
+}
+
+
+static void radius_server_free_sessions(struct radius_server_data *data,
+ struct radius_session *sessions)
+{
+ struct radius_session *session, *prev;
+
+ session = sessions;
+ while (session) {
+ prev = session;
+ session = session->next;
+ radius_server_session_free(data, prev);
+ }
+}
+
+
+static void radius_server_free_clients(struct radius_server_data *data,
+ struct radius_client *clients)
+{
+ struct radius_client *client, *prev;
+
+ client = clients;
+ while (client) {
+ prev = client;
+ client = client->next;
+
+ radius_server_free_sessions(data, prev->sessions);
+ free(prev->shared_secret);
+ free(prev);
+ }
+}
+
+
+static struct radius_client *
+radius_server_read_clients(const char *client_file)
+{
+ FILE *f;
+ const int buf_size = 1024;
+ char *buf, *pos;
+ struct radius_client *clients, *tail, *entry;
+ int line = 0, mask, failed = 0, i;
+ struct in_addr addr;
+ unsigned int val;
+
+ f = fopen(client_file, "r");
+ if (f == NULL) {
+ RADIUS_ERROR("Could not open client file '%s'", client_file);
+ return NULL;
+ }
+
+ buf = malloc(buf_size);
+ if (buf == NULL) {
+ fclose(f);
+ return NULL;
+ }
+
+ clients = tail = NULL;
+ while (fgets(buf, buf_size, f)) {
+ /* Configuration file format:
+ * 192.168.1.0/24 secret
+ * 192.168.1.2 secret
+ */
+ line++;
+ buf[buf_size - 1] = '\0';
+ pos = buf;
+ while (*pos != '\0' && *pos != '\n')
+ pos++;
+ if (*pos == '\n')
+ *pos = '\0';
+ if (*buf == '\0' || *buf == '#')
+ continue;
+
+ pos = buf;
+ while ((*pos >= '0' && *pos <= '9') || *pos == '.') {
+ pos++;
+ }
+
+ if (*pos == '\0') {
+ failed = 1;
+ break;
+ }
+
+ if (*pos == '/') {
+ char *end;
+ *pos++ = '\0';
+ mask = strtol(pos, &end, 10);
+ if ((pos == end) || (mask < 0 || mask > 32)) {
+ failed = 1;
+ break;
+ }
+ pos = end;
+ } else {
+ mask = 32;
+ *pos++ = '\0';
+ }
+
+ if (inet_aton(buf, &addr) == 0) {
+ failed = 1;
+ break;
+ }
+
+ while (*pos == ' ' || *pos == '\t') {
+ pos++;
+ }
+
+ if (*pos == '\0') {
+ failed = 1;
+ break;
+ }
+
+ entry = malloc(sizeof(*entry));
+ if (entry == NULL) {
+ failed = 1;
+ break;
+ }
+ memset(entry, 0, sizeof(*entry));
+ entry->shared_secret = strdup(pos);
+ if (entry->shared_secret == NULL) {
+ failed = 1;
+ free(entry);
+ break;
+ }
+ entry->shared_secret_len = strlen(entry->shared_secret);
+ entry->addr.s_addr = addr.s_addr;
+ val = 0;
+ for (i = 0; i < mask; i++)
+ val |= 1 << (31 - i);
+ entry->mask.s_addr = htonl(val);
+
+ if (tail == NULL) {
+ clients = tail = entry;
+ } else {
+ tail->next = entry;
+ tail = entry;
+ }
+ }
+
+ if (failed) {
+ RADIUS_ERROR("Invalid line %d in '%s'", line, client_file);
+ radius_server_free_clients(NULL, clients);
+ clients = NULL;
+ }
+
+ free(buf);
+ fclose(f);
+
+ return clients;
+}
+
+
+struct radius_server_data *
+radius_server_init(struct radius_server_conf *conf)
+{
+ struct radius_server_data *data;
+
+ data = malloc(sizeof(*data));
+ if (data == NULL) {
+ return NULL;
+ }
+ memset(data, 0, sizeof(*data));
+ data->hostapd_conf = conf->hostapd_conf;
+ data->eap_sim_db_priv = conf->eap_sim_db_priv;
+ data->ssl_ctx = conf->ssl_ctx;
+
+ data->clients = radius_server_read_clients(conf->client_file);
+ if (data->clients == NULL) {
+ printf("No RADIUS clients configured.\n");
+ radius_server_deinit(data);
+ return NULL;
+ }
+
+ data->auth_sock = radius_server_open_socket(conf->auth_port);
+ if (data->auth_sock < 0) {
+ printf("Failed to open UDP socket for RADIUS authentication "
+ "server\n");
+ radius_server_deinit(data);
+ return NULL;
+ }
+ if (eloop_register_read_sock(data->auth_sock,
+ radius_server_receive_auth,
+ data, NULL)) {
+ radius_server_deinit(data);
+ return NULL;
+ }
+
+ return data;
+}
+
+
+void radius_server_deinit(struct radius_server_data *data)
+{
+ if (data == NULL)
+ return;
+
+ if (data->auth_sock >= 0) {
+ eloop_unregister_read_sock(data->auth_sock);
+ close(data->auth_sock);
+ }
+
+ radius_server_free_clients(data, data->clients);
+
+ free(data);
+}
+
+
+int radius_server_get_mib(struct radius_server_data *data, char *buf,
+ size_t buflen)
+{
+ /* TODO: add support for RADIUS authentication server MIB */
+ return 0;
+}
+
+
+static Boolean radius_server_get_bool(void *ctx, enum eapol_bool_var variable)
+{
+ struct radius_session *sess = ctx;
+ if (sess == NULL)
+ return FALSE;
+ switch (variable) {
+ case EAPOL_eapSuccess:
+ return sess->eapSuccess;
+ case EAPOL_eapRestart:
+ return sess->eapRestart;
+ case EAPOL_eapFail:
+ return sess->eapFail;
+ case EAPOL_eapResp:
+ return sess->eapResp;
+ case EAPOL_eapReq:
+ return sess->eapReq;
+ case EAPOL_eapNoReq:
+ return sess->eapNoReq;
+ case EAPOL_portEnabled:
+ return sess->portEnabled;
+ case EAPOL_eapTimeout:
+ return sess->eapTimeout;
+ }
+ return FALSE;
+}
+
+
+static void radius_server_set_bool(void *ctx, enum eapol_bool_var variable,
+ Boolean value)
+{
+ struct radius_session *sess = ctx;
+ if (sess == NULL)
+ return;
+ switch (variable) {
+ case EAPOL_eapSuccess:
+ sess->eapSuccess = value;
+ break;
+ case EAPOL_eapRestart:
+ sess->eapRestart = value;
+ break;
+ case EAPOL_eapFail:
+ sess->eapFail = value;
+ break;
+ case EAPOL_eapResp:
+ sess->eapResp = value;
+ break;
+ case EAPOL_eapReq:
+ sess->eapReq = value;
+ break;
+ case EAPOL_eapNoReq:
+ sess->eapNoReq = value;
+ break;
+ case EAPOL_portEnabled:
+ sess->portEnabled = value;
+ break;
+ case EAPOL_eapTimeout:
+ sess->eapTimeout = value;
+ break;
+ }
+}
+
+
+static void radius_server_set_eapReqData(void *ctx, const u8 *eapReqData,
+ size_t eapReqDataLen)
+{
+ struct radius_session *sess = ctx;
+ if (sess == NULL)
+ return;
+
+ free(sess->eapReqData);
+ sess->eapReqData = malloc(eapReqDataLen);
+ if (sess->eapReqData) {
+ memcpy(sess->eapReqData, eapReqData, eapReqDataLen);
+ sess->eapReqDataLen = eapReqDataLen;
+ } else {
+ sess->eapReqDataLen = 0;
+ }
+}
+
+
+static void radius_server_set_eapKeyData(void *ctx, const u8 *eapKeyData,
+ size_t eapKeyDataLen)
+{
+ struct radius_session *sess = ctx;
+
+ if (sess == NULL)
+ return;
+
+ free(sess->eapKeyData);
+ if (eapKeyData) {
+ sess->eapKeyData = malloc(eapKeyDataLen);
+ if (sess->eapKeyData) {
+ memcpy(sess->eapKeyData, eapKeyData, eapKeyDataLen);
+ sess->eapKeyDataLen = eapKeyDataLen;
+ } else {
+ sess->eapKeyDataLen = 0;
+ }
+ } else {
+ sess->eapKeyData = NULL;
+ sess->eapKeyDataLen = 0;
+ }
+}
+
+
+static int radius_server_get_eap_user(void *ctx, const u8 *identity,
+ size_t identity_len, int phase2,
+ struct eap_user *user)
+{
+ struct radius_session *sess = ctx;
+ const struct hostapd_eap_user *eap_user;
+
+ eap_user = hostapd_get_eap_user(sess->server->hostapd_conf, identity,
+ identity_len, phase2);
+ if (eap_user == NULL)
+ return -1;
+
+ memset(user, 0, sizeof(*user));
+ memcpy(user->methods, eap_user->methods,
+ EAP_USER_MAX_METHODS > EAP_MAX_METHODS ?
+ EAP_USER_MAX_METHODS : EAP_MAX_METHODS);
+
+ if (eap_user->password) {
+ user->password = malloc(eap_user->password_len);
+ if (user->password == NULL)
+ return -1;
+ memcpy(user->password, eap_user->password,
+ eap_user->password_len);
+ user->password_len = eap_user->password_len;
+ }
+ user->force_version = eap_user->force_version;
+
+ return 0;
+}
+
+
+static struct eapol_callbacks radius_server_eapol_cb =
+{
+ .get_bool = radius_server_get_bool,
+ .set_bool = radius_server_set_bool,
+ .set_eapReqData = radius_server_set_eapReqData,
+ .set_eapKeyData = radius_server_set_eapKeyData,
+ .get_eap_user = radius_server_get_eap_user,
+};
diff --git a/contrib/hostapd/radius_server.h b/contrib/hostapd/radius_server.h
new file mode 100644
index 000000000000..a725f0a5aebc
--- /dev/null
+++ b/contrib/hostapd/radius_server.h
@@ -0,0 +1,45 @@
+#ifndef RADIUS_SERVER_H
+#define RADIUS_SERVER_H
+
+struct radius_server_data;
+
+struct radius_server_conf {
+ int auth_port;
+ char *client_file;
+ void *hostapd_conf;
+ void *eap_sim_db_priv;
+ void *ssl_ctx;
+};
+
+
+#ifdef RADIUS_SERVER
+
+struct radius_server_data *
+radius_server_init(struct radius_server_conf *conf);
+
+void radius_server_deinit(struct radius_server_data *data);
+
+int radius_server_get_mib(struct radius_server_data *data, char *buf,
+ size_t buflen);
+
+#else /* RADIUS_SERVER */
+
+static inline struct radius_server_data *
+radius_server_init(struct radius_server_conf *conf)
+{
+ return NULL;
+}
+
+static inline void radius_server_deinit(struct radius_server_data *data)
+{
+}
+
+static inline int radius_server_get_mib(struct radius_server_data *data,
+ char *buf, size_t buflen)
+{
+ return 0;
+}
+
+#endif /* RADIUS_SERVER */
+
+#endif /* RADIUS_SERVER_H */
diff --git a/contrib/hostapd/rc4.c b/contrib/hostapd/rc4.c
new file mode 100644
index 000000000000..97ec1b0ae357
--- /dev/null
+++ b/contrib/hostapd/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/hostapd/rc4.h b/contrib/hostapd/rc4.h
new file mode 100644
index 000000000000..0e77b7e470d0
--- /dev/null
+++ b/contrib/hostapd/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/hostapd/sha1.c b/contrib/hostapd/sha1.c
new file mode 100644
index 000000000000..04943b5a775c
--- /dev/null
+++ b/contrib/hostapd/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/hostapd/sha1.h b/contrib/hostapd/sha1.h
new file mode 100644
index 000000000000..186e3c1c25b1
--- /dev/null
+++ b/contrib/hostapd/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/hostapd/sta_info.c b/contrib/hostapd/sta_info.c
new file mode 100644
index 000000000000..9a4daa362a71
--- /dev/null
+++ b/contrib/hostapd/sta_info.c
@@ -0,0 +1,354 @@
+/*
+ * Host AP (software wireless LAN access point) user space daemon for
+ * Host AP kernel driver / Station table
+ * 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 <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <netinet/in.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include "hostapd.h"
+#include "sta_info.h"
+#include "eloop.h"
+#include "accounting.h"
+#include "ieee802_1x.h"
+#include "ieee802_11.h"
+#include "radius.h"
+#include "eapol_sm.h"
+#include "wpa.h"
+#include "radius_client.h"
+#include "driver.h"
+
+
+int ap_for_each_sta(struct hostapd_data *hapd,
+ int (*cb)(struct hostapd_data *hapd, struct sta_info *sta,
+ void *ctx),
+ void *ctx)
+{
+ struct sta_info *sta;
+
+ for (sta = hapd->sta_list; sta; sta = sta->next) {
+ if (cb(hapd, sta, ctx))
+ return 1;
+ }
+
+ return 0;
+}
+
+
+struct sta_info* ap_get_sta(hostapd *hapd, u8 *sta)
+{
+ struct sta_info *s;
+
+ s = hapd->sta_hash[STA_HASH(sta)];
+ while (s != NULL && memcmp(s->addr, sta, 6) != 0)
+ s = s->hnext;
+ return s;
+}
+
+
+static void ap_sta_list_del(hostapd *hapd, struct sta_info *sta)
+{
+ struct sta_info *tmp;
+
+ if (hapd->sta_list == sta) {
+ hapd->sta_list = sta->next;
+ return;
+ }
+
+ tmp = hapd->sta_list;
+ while (tmp != NULL && tmp->next != sta)
+ tmp = tmp->next;
+ if (tmp == NULL) {
+ printf("Could not remove STA " MACSTR " from list.\n",
+ MAC2STR(sta->addr));
+ } else
+ tmp->next = sta->next;
+}
+
+
+void ap_sta_hash_add(hostapd *hapd, struct sta_info *sta)
+{
+ sta->hnext = hapd->sta_hash[STA_HASH(sta->addr)];
+ hapd->sta_hash[STA_HASH(sta->addr)] = sta;
+}
+
+
+static void ap_sta_hash_del(hostapd *hapd, struct sta_info *sta)
+{
+ struct sta_info *s;
+
+ s = hapd->sta_hash[STA_HASH(sta->addr)];
+ if (s == NULL) return;
+ if (memcmp(s->addr, sta->addr, 6) == 0) {
+ hapd->sta_hash[STA_HASH(sta->addr)] = s->hnext;
+ return;
+ }
+
+ while (s->hnext != NULL && memcmp(s->hnext->addr, sta->addr, 6) != 0)
+ s = s->hnext;
+ if (s->hnext != NULL)
+ s->hnext = s->hnext->hnext;
+ else
+ printf("AP: could not remove STA " MACSTR " from hash table\n",
+ MAC2STR(sta->addr));
+}
+
+
+void ap_free_sta(hostapd *hapd, struct sta_info *sta)
+{
+ accounting_sta_stop(hapd, sta);
+ if (!(sta->flags & WLAN_STA_PREAUTH))
+ hostapd_sta_remove(hapd, sta->addr);
+
+ ap_sta_hash_del(hapd, sta);
+ ap_sta_list_del(hapd, sta);
+
+ if (sta->aid > 0)
+ hapd->sta_aid[sta->aid - 1] = NULL;
+
+ hapd->num_sta--;
+ eloop_cancel_timeout(ap_handle_timer, hapd, sta);
+
+ ieee802_1x_free_station(sta);
+ wpa_free_station(sta);
+ radius_client_flush_auth(hapd->radius, sta->addr);
+
+ if (sta->last_assoc_req)
+ free(sta->last_assoc_req);
+
+ free(sta->challenge);
+ free(sta->wpa_ie);
+
+ free(sta);
+}
+
+
+void hostapd_free_stas(hostapd *hapd)
+{
+ struct sta_info *sta, *prev;
+
+ sta = hapd->sta_list;
+
+ while (sta) {
+ prev = sta;
+ sta = sta->next;
+ printf("Removing station " MACSTR "\n", MAC2STR(prev->addr));
+ ap_free_sta(hapd, prev);
+ }
+}
+
+
+void ap_handle_timer(void *eloop_ctx, void *timeout_ctx)
+{
+ hostapd *hapd = eloop_ctx;
+ struct sta_info *sta = timeout_ctx;
+ unsigned long next_time = 0;
+
+ if (sta->timeout_next == STA_REMOVE) {
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO, "deauthenticated due to "
+ "local deauth request");
+ ap_free_sta(hapd, sta);
+ return;
+ }
+
+ if ((sta->flags & WLAN_STA_ASSOC) &&
+ (sta->timeout_next == STA_NULLFUNC ||
+ sta->timeout_next == STA_DISASSOC)) {
+ int inactive_sec;
+ HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL,
+ "Checking STA " MACSTR " inactivity:\n",
+ MAC2STR(sta->addr));
+ inactive_sec = hostapd_get_inact_sec(hapd, sta->addr);
+ if (inactive_sec == -1) {
+ printf(" Could not get station info from kernel "
+ "driver for " MACSTR ".\n",
+ MAC2STR(sta->addr));
+ } else if (inactive_sec < AP_MAX_INACTIVITY &&
+ sta->flags & WLAN_STA_ASSOC) {
+ /* station activity detected; reset timeout state */
+ HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL,
+ " Station has been active\n");
+ sta->timeout_next = STA_NULLFUNC;
+ next_time = AP_MAX_INACTIVITY - inactive_sec;
+ }
+ }
+
+ if ((sta->flags & WLAN_STA_ASSOC) &&
+ sta->timeout_next == STA_DISASSOC &&
+ !(sta->flags & WLAN_STA_PENDING_POLL)) {
+ HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL,
+ " Station has ACKed data poll\n");
+ /* data nullfunc frame poll did not produce TX errors; assume
+ * station ACKed it */
+ sta->timeout_next = STA_NULLFUNC;
+ next_time = AP_MAX_INACTIVITY;
+ }
+
+ if (next_time) {
+ eloop_register_timeout(next_time, 0, ap_handle_timer, hapd,
+ sta);
+ return;
+ }
+
+ if (sta->timeout_next == STA_NULLFUNC &&
+ (sta->flags & WLAN_STA_ASSOC)) {
+ /* send data frame to poll STA and check whether this frame
+ * is ACKed */
+ struct ieee80211_hdr hdr;
+
+ HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL,
+ " Polling STA with data frame\n");
+ sta->flags |= WLAN_STA_PENDING_POLL;
+
+ /* FIX: WLAN_FC_STYPE_NULLFUNC would be more appropriate, but
+ * it is apparently not retried so TX Exc events are not
+ * received for it */
+ memset(&hdr, 0, sizeof(hdr));
+ hdr.frame_control =
+ IEEE80211_FC(WLAN_FC_TYPE_DATA, WLAN_FC_STYPE_DATA);
+ hdr.frame_control |= host_to_le16(BIT(1));
+ hdr.frame_control |= host_to_le16(WLAN_FC_FROMDS);
+ memcpy(hdr.IEEE80211_DA_FROMDS, sta->addr, ETH_ALEN);
+ memcpy(hdr.IEEE80211_BSSID_FROMDS, hapd->own_addr, ETH_ALEN);
+ memcpy(hdr.IEEE80211_SA_FROMDS, hapd->own_addr, ETH_ALEN);
+
+ if (hostapd_send_mgmt_frame(hapd, &hdr, sizeof(hdr), 0) < 0)
+ perror("ap_handle_timer: send");
+ } else if (sta->timeout_next != STA_REMOVE) {
+ int deauth = sta->timeout_next == STA_DEAUTH;
+
+ printf(" Sending %s info to STA " MACSTR "\n",
+ deauth ? "deauthentication" : "disassociation",
+ MAC2STR(sta->addr));
+
+ if (deauth) {
+ hostapd_sta_deauth(hapd, sta->addr,
+ WLAN_REASON_PREV_AUTH_NOT_VALID);
+ } else {
+ hostapd_sta_disassoc(
+ hapd, sta->addr,
+ WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY);
+ }
+ }
+
+ switch (sta->timeout_next) {
+ case STA_NULLFUNC:
+ sta->timeout_next = STA_DISASSOC;
+ eloop_register_timeout(AP_DISASSOC_DELAY, 0, ap_handle_timer,
+ hapd, sta);
+ break;
+ case STA_DISASSOC:
+ sta->flags &= ~WLAN_STA_ASSOC;
+ ieee802_1x_set_port_enabled(hapd, sta, 0);
+ if (!sta->acct_terminate_cause)
+ sta->acct_terminate_cause =
+ RADIUS_ACCT_TERMINATE_CAUSE_IDLE_TIMEOUT;
+ accounting_sta_stop(hapd, sta);
+ ieee802_1x_free_station(sta);
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO, "disassociated due to "
+ "inactivity");
+ sta->timeout_next = STA_DEAUTH;
+ eloop_register_timeout(AP_DEAUTH_DELAY, 0, ap_handle_timer,
+ hapd, sta);
+ break;
+ case STA_DEAUTH:
+ case STA_REMOVE:
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO, "deauthenticated due to "
+ "inactivity");
+ if (!sta->acct_terminate_cause)
+ sta->acct_terminate_cause =
+ RADIUS_ACCT_TERMINATE_CAUSE_IDLE_TIMEOUT;
+ ap_free_sta(hapd, sta);
+ break;
+ }
+}
+
+
+void ap_handle_session_timer(void *eloop_ctx, void *timeout_ctx)
+{
+ hostapd *hapd = eloop_ctx;
+ struct sta_info *sta = timeout_ctx;
+
+ if (!(sta->flags & WLAN_STA_AUTH))
+ return;
+
+ hostapd_sta_deauth(hapd, sta->addr, WLAN_REASON_PREV_AUTH_NOT_VALID);
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO, "deauthenticated due to "
+ "session timeout");
+ sta->acct_terminate_cause =
+ RADIUS_ACCT_TERMINATE_CAUSE_SESSION_TIMEOUT;
+ ap_free_sta(hapd, sta);
+}
+
+
+void ap_sta_session_timeout(hostapd *hapd, struct sta_info *sta,
+ u32 session_timeout)
+{
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG, "setting session timeout to %d "
+ "seconds", session_timeout);
+ eloop_cancel_timeout(ap_handle_session_timer, hapd, sta);
+ eloop_register_timeout(session_timeout, 0, ap_handle_session_timer,
+ hapd, sta);
+}
+
+
+void ap_sta_no_session_timeout(hostapd *hapd, struct sta_info *sta)
+{
+ eloop_cancel_timeout(ap_handle_session_timer, hapd, sta);
+}
+
+
+struct sta_info * ap_sta_add(struct hostapd_data *hapd, u8 *addr)
+{
+ struct sta_info *sta;
+
+ sta = ap_get_sta(hapd, addr);
+ if (sta)
+ return sta;
+
+ HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, " New STA\n");
+ if (hapd->num_sta >= MAX_STA_COUNT) {
+ /* FIX: might try to remove some old STAs first? */
+ printf(" no more room for new STAs (%d/%d)\n",
+ hapd->num_sta, MAX_STA_COUNT);
+ return NULL;
+ }
+
+ sta = (struct sta_info *) malloc(sizeof(struct sta_info));
+ if (sta == NULL) {
+ printf(" malloc failed\n");
+ return NULL;
+ }
+ memset(sta, 0, sizeof(struct sta_info));
+ sta->acct_interim_interval = hapd->conf->radius_acct_interim_interval;
+
+ /* initialize STA info data */
+ eloop_register_timeout(AP_MAX_INACTIVITY, 0, ap_handle_timer,
+ hapd, sta);
+ memcpy(sta->addr, addr, ETH_ALEN);
+ sta->next = hapd->sta_list;
+ hapd->sta_list = sta;
+ hapd->num_sta++;
+ ap_sta_hash_add(hapd, sta);
+
+ return sta;
+}
diff --git a/contrib/hostapd/sta_info.h b/contrib/hostapd/sta_info.h
new file mode 100644
index 000000000000..417df71730e7
--- /dev/null
+++ b/contrib/hostapd/sta_info.h
@@ -0,0 +1,19 @@
+#ifndef STA_INFO_H
+#define STA_INFO_H
+
+int ap_for_each_sta(struct hostapd_data *hapd,
+ int (*cb)(struct hostapd_data *hapd, struct sta_info *sta,
+ void *ctx),
+ void *ctx);
+struct sta_info* ap_get_sta(hostapd *hapd, u8 *sta);
+void ap_sta_hash_add(hostapd *hapd, struct sta_info *sta);
+void ap_free_sta(hostapd *hapd, struct sta_info *sta);
+void ap_free_sta(hostapd *hapd, struct sta_info *sta);
+void hostapd_free_stas(hostapd *hapd);
+void ap_handle_timer(void *eloop_ctx, void *timeout_ctx);
+void ap_sta_session_timeout(hostapd *hapd, struct sta_info *sta,
+ u32 session_timeout);
+void ap_sta_no_session_timeout(hostapd *hapd, struct sta_info *sta);
+struct sta_info * ap_sta_add(struct hostapd_data *hapd, u8 *addr);
+
+#endif /* STA_INFO_H */
diff --git a/contrib/hostapd/tls.h b/contrib/hostapd/tls.h
new file mode 100644
index 000000000000..99c9b332360a
--- /dev/null
+++ b/contrib/hostapd/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/hostapd/tls_none.c b/contrib/hostapd/tls_none.c
new file mode 100644
index 000000000000..2b3cafc10415
--- /dev/null
+++ b/contrib/hostapd/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/hostapd/tls_openssl.c b/contrib/hostapd/tls_openssl.c
new file mode 100644
index 000000000000..097b1c876e34
--- /dev/null
+++ b/contrib/hostapd/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/hostapd/version.h b/contrib/hostapd/version.h
new file mode 100644
index 000000000000..074fd52bfba9
--- /dev/null
+++ b/contrib/hostapd/version.h
@@ -0,0 +1,6 @@
+#ifndef VERSION_H
+#define VERSION_H
+
+#define VERSION_STR "0.3.7"
+
+#endif /* VERSION_H */
diff --git a/contrib/hostapd/wired.conf b/contrib/hostapd/wired.conf
new file mode 100644
index 000000000000..d1f3c4e3ed22
--- /dev/null
+++ b/contrib/hostapd/wired.conf
@@ -0,0 +1,38 @@
+##### hostapd configuration file ##############################################
+# Empty lines and lines starting with # are ignored
+
+# Example configuration file for wired authenticator. See hostapd.conf for
+# more details.
+
+interface=eth0
+driver=wired
+logger_stdout=-1
+logger_stdout_level=1
+debug=2
+dump_file=/tmp/hostapd.dump
+
+ieee8021x=1
+eap_reauth_period=3600
+
+
+##### RADIUS configuration ####################################################
+# for IEEE 802.1X with external Authentication Server, IEEE 802.11
+# authentication with external ACL for MAC addresses, and accounting
+
+# The own IP address of the access point (used as NAS-IP-Address)
+own_ip_addr=127.0.0.1
+
+# Optional NAS-Identifier string for RADIUS messages. When used, this should be
+# a unique to the NAS within the scope of the RADIUS server. For example, a
+# fully qualified domain name can be used here.
+nas_identifier=ap.example.com
+
+# RADIUS authentication server
+auth_server_addr=127.0.0.1
+auth_server_port=1812
+auth_server_shared_secret=radius
+
+# RADIUS accounting server
+acct_server_addr=127.0.0.1
+acct_server_port=1813
+acct_server_shared_secret=radius
diff --git a/contrib/hostapd/wpa.c b/contrib/hostapd/wpa.c
new file mode 100644
index 000000000000..8beab8deaadc
--- /dev/null
+++ b/contrib/hostapd/wpa.c
@@ -0,0 +1,2820 @@
+/*
+ * Host AP (software wireless LAN access point) user space daemon for
+ * Host AP kernel driver / WPA Authenticator
+ * 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 <netinet/in.h>
+#include <sys/time.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "hostapd.h"
+#include "eapol_sm.h"
+#include "wpa.h"
+#include "driver.h"
+#include "sha1.h"
+#include "md5.h"
+#include "rc4.h"
+#include "aes_wrap.h"
+#include "ieee802_1x.h"
+#include "ieee802_11.h"
+#include "eloop.h"
+#include "sta_info.h"
+#include "l2_packet.h"
+
+
+static void wpa_send_eapol_timeout(void *eloop_ctx, void *timeout_ctx);
+static void wpa_sm_step(struct wpa_state_machine *sm);
+static int wpa_verify_key_mic(struct wpa_ptk *PTK, u8 *data, size_t data_len);
+static void wpa_sm_call_step(void *eloop_ctx, void *timeout_ctx);
+static void wpa_group_sm_step(struct hostapd_data *hapd);
+static void pmksa_cache_free(struct hostapd_data *hapd);
+static struct rsn_pmksa_cache * pmksa_cache_get(struct hostapd_data *hapd,
+ u8 *spa, u8 *pmkid);
+
+
+/* Default timeouts are 100 ms, but this seems to be a bit too fast for most
+ * WPA Supplicants, so use a bit longer timeout. */
+static const u32 dot11RSNAConfigGroupUpdateTimeOut = 1000; /* ms */
+static const u32 dot11RSNAConfigGroupUpdateCount = 3;
+static const u32 dot11RSNAConfigPairwiseUpdateTimeOut = 1000; /* ms */
+static const u32 dot11RSNAConfigPairwiseUpdateCount = 3;
+
+/* 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 = 1024;
+
+
+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_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 };
+
+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 };
+
+/* 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));
+
+
+/* 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_write_wpa_ie(struct hostapd_data *hapd, u8 *buf, size_t len)
+{
+ struct wpa_ie_hdr *hdr;
+ int num_suites;
+ u8 *pos, *count;
+
+ hdr = (struct wpa_ie_hdr *) buf;
+ hdr->elem_id = WLAN_EID_GENERIC;
+ memcpy(&hdr->oui, WPA_OUI_TYPE, WPA_SELECTOR_LEN);
+ hdr->version = host_to_le16(WPA_VERSION);
+ pos = (u8 *) (hdr + 1);
+
+ if (hapd->conf->wpa_group == WPA_CIPHER_CCMP) {
+ memcpy(pos, WPA_CIPHER_SUITE_CCMP, WPA_SELECTOR_LEN);
+ } else if (hapd->conf->wpa_group == WPA_CIPHER_TKIP) {
+ memcpy(pos, WPA_CIPHER_SUITE_TKIP, WPA_SELECTOR_LEN);
+ } else if (hapd->conf->wpa_group == WPA_CIPHER_WEP104) {
+ memcpy(pos, WPA_CIPHER_SUITE_WEP104, WPA_SELECTOR_LEN);
+ } else if (hapd->conf->wpa_group == WPA_CIPHER_WEP40) {
+ memcpy(pos, WPA_CIPHER_SUITE_WEP40, WPA_SELECTOR_LEN);
+ } else {
+ printf("Invalid group cipher (%d).\n", hapd->conf->wpa_group);
+ return -1;
+ }
+ pos += WPA_SELECTOR_LEN;
+
+ num_suites = 0;
+ count = pos;
+ pos += 2;
+
+ if (hapd->conf->wpa_pairwise & WPA_CIPHER_CCMP) {
+ memcpy(pos, WPA_CIPHER_SUITE_CCMP, WPA_SELECTOR_LEN);
+ pos += WPA_SELECTOR_LEN;
+ num_suites++;
+ }
+ if (hapd->conf->wpa_pairwise & WPA_CIPHER_TKIP) {
+ memcpy(pos, WPA_CIPHER_SUITE_TKIP, WPA_SELECTOR_LEN);
+ pos += WPA_SELECTOR_LEN;
+ num_suites++;
+ }
+ if (hapd->conf->wpa_pairwise & WPA_CIPHER_NONE) {
+ memcpy(pos, WPA_CIPHER_SUITE_NONE, WPA_SELECTOR_LEN);
+ pos += WPA_SELECTOR_LEN;
+ num_suites++;
+ }
+
+ if (num_suites == 0) {
+ printf("Invalid pairwise cipher (%d).\n",
+ hapd->conf->wpa_pairwise);
+ return -1;
+ }
+ *count++ = num_suites & 0xff;
+ *count = (num_suites >> 8) & 0xff;
+
+ num_suites = 0;
+ count = pos;
+ pos += 2;
+
+ if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X) {
+ memcpy(pos, WPA_AUTH_KEY_MGMT_UNSPEC_802_1X, WPA_SELECTOR_LEN);
+ pos += WPA_SELECTOR_LEN;
+ num_suites++;
+ }
+ if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK) {
+ memcpy(pos, WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X,
+ WPA_SELECTOR_LEN);
+ pos += WPA_SELECTOR_LEN;
+ num_suites++;
+ }
+
+ if (num_suites == 0) {
+ printf("Invalid key management type (%d).\n",
+ hapd->conf->wpa_key_mgmt);
+ return -1;
+ }
+ *count++ = num_suites & 0xff;
+ *count = (num_suites >> 8) & 0xff;
+
+ /* WPA Capabilities; use defaults, so no need to include it */
+
+ hdr->len = (pos - buf) - 2;
+
+ return pos - buf;
+}
+
+
+static int wpa_write_rsn_ie(struct hostapd_data *hapd, u8 *buf, size_t len)
+{
+ struct rsn_ie_hdr *hdr;
+ int num_suites;
+ u8 *pos, *count;
+
+ hdr = (struct rsn_ie_hdr *) buf;
+ hdr->elem_id = WLAN_EID_RSN;
+ pos = (u8 *) &hdr->version;
+ *pos++ = RSN_VERSION & 0xff;
+ *pos++ = RSN_VERSION >> 8;
+ pos = (u8 *) (hdr + 1);
+
+ if (hapd->conf->wpa_group == WPA_CIPHER_CCMP) {
+ memcpy(pos, RSN_CIPHER_SUITE_CCMP, RSN_SELECTOR_LEN);
+ } else if (hapd->conf->wpa_group == WPA_CIPHER_TKIP) {
+ memcpy(pos, RSN_CIPHER_SUITE_TKIP, RSN_SELECTOR_LEN);
+ } else if (hapd->conf->wpa_group == WPA_CIPHER_WEP104) {
+ memcpy(pos, RSN_CIPHER_SUITE_WEP104, RSN_SELECTOR_LEN);
+ } else if (hapd->conf->wpa_group == WPA_CIPHER_WEP40) {
+ memcpy(pos, RSN_CIPHER_SUITE_WEP40, RSN_SELECTOR_LEN);
+ } else {
+ printf("Invalid group cipher (%d).\n", hapd->conf->wpa_group);
+ return -1;
+ }
+ pos += RSN_SELECTOR_LEN;
+
+ num_suites = 0;
+ count = pos;
+ pos += 2;
+
+ if (hapd->conf->wpa_pairwise & WPA_CIPHER_CCMP) {
+ memcpy(pos, RSN_CIPHER_SUITE_CCMP, RSN_SELECTOR_LEN);
+ pos += RSN_SELECTOR_LEN;
+ num_suites++;
+ }
+ if (hapd->conf->wpa_pairwise & WPA_CIPHER_TKIP) {
+ memcpy(pos, RSN_CIPHER_SUITE_TKIP, RSN_SELECTOR_LEN);
+ pos += RSN_SELECTOR_LEN;
+ num_suites++;
+ }
+ if (hapd->conf->wpa_pairwise & WPA_CIPHER_NONE) {
+ memcpy(pos, RSN_CIPHER_SUITE_NONE, RSN_SELECTOR_LEN);
+ pos += RSN_SELECTOR_LEN;
+ num_suites++;
+ }
+
+ if (num_suites == 0) {
+ printf("Invalid pairwise cipher (%d).\n",
+ hapd->conf->wpa_pairwise);
+ return -1;
+ }
+ *count++ = num_suites & 0xff;
+ *count = (num_suites >> 8) & 0xff;
+
+ num_suites = 0;
+ count = pos;
+ pos += 2;
+
+ if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X) {
+ memcpy(pos, RSN_AUTH_KEY_MGMT_UNSPEC_802_1X, RSN_SELECTOR_LEN);
+ pos += RSN_SELECTOR_LEN;
+ num_suites++;
+ }
+ if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK) {
+ memcpy(pos, RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X,
+ RSN_SELECTOR_LEN);
+ pos += RSN_SELECTOR_LEN;
+ num_suites++;
+ }
+
+ if (num_suites == 0) {
+ printf("Invalid key management type (%d).\n",
+ hapd->conf->wpa_key_mgmt);
+ return -1;
+ }
+ *count++ = num_suites & 0xff;
+ *count = (num_suites >> 8) & 0xff;
+
+ /* RSN Capabilities */
+ *pos++ = hapd->conf->rsn_preauth ? BIT(0) : 0;
+ *pos++ = 0;
+
+ hdr->len = (pos - buf) - 2;
+
+ return pos - buf;
+}
+
+
+static int wpa_gen_wpa_ie(struct hostapd_data *hapd)
+{
+ u8 *pos, buf[100];
+ int res;
+
+ pos = buf;
+
+ if (hapd->conf->wpa & HOSTAPD_WPA_VERSION_WPA2) {
+ res = wpa_write_rsn_ie(hapd, pos, buf + sizeof(buf) - pos);
+ if (res < 0)
+ return res;
+ pos += res;
+ }
+ if (hapd->conf->wpa & HOSTAPD_WPA_VERSION_WPA) {
+ res = wpa_write_wpa_ie(hapd, pos, buf + sizeof(buf) - pos);
+ if (res < 0)
+ return res;
+ pos += res;
+ }
+
+ free(hapd->wpa_ie);
+ hapd->wpa_ie = malloc(pos - buf);
+ if (hapd->wpa_ie == NULL)
+ return -1;
+ memcpy(hapd->wpa_ie, buf, pos - buf);
+ hapd->wpa_ie_len = pos - buf;
+
+ return 0;
+}
+
+
+static void wpa_sta_disconnect(struct hostapd_data *hapd, struct sta_info *sta)
+{
+ hostapd_sta_deauth(hapd, sta->addr, WLAN_REASON_PREV_AUTH_NOT_VALID);
+ sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC | WLAN_STA_AUTHORIZED);
+ eloop_cancel_timeout(ap_handle_timer, hapd, sta);
+ eloop_register_timeout(0, 0, ap_handle_timer, hapd, sta);
+ sta->timeout_next = STA_REMOVE;
+}
+
+
+static void wpa_rekey_gmk(void *eloop_ctx, void *timeout_ctx)
+{
+ struct hostapd_data *hapd = eloop_ctx;
+
+ if (hapd->wpa_auth) {
+ if (hostapd_get_rand(hapd->wpa_auth->GMK, WPA_GMK_LEN)) {
+ printf("Failed to get random data for WPA "
+ "initialization.\n");
+ } else {
+ hostapd_logger(hapd, NULL, HOSTAPD_MODULE_WPA,
+ HOSTAPD_LEVEL_DEBUG,
+ "GMK rekeyd");
+ }
+ }
+
+ if (hapd->conf->wpa_gmk_rekey) {
+ eloop_register_timeout(hapd->conf->wpa_gmk_rekey, 0,
+ wpa_rekey_gmk, hapd, NULL);
+ }
+}
+
+
+static void wpa_rekey_gtk(void *eloop_ctx, void *timeout_ctx)
+{
+ struct hostapd_data *hapd = eloop_ctx;
+
+ if (hapd->wpa_auth) {
+ hostapd_logger(hapd, NULL, HOSTAPD_MODULE_WPA,
+ HOSTAPD_LEVEL_DEBUG, "rekeying GTK");
+ hapd->wpa_auth->GTKReKey = TRUE;
+ do {
+ hapd->wpa_auth->changed = FALSE;
+ wpa_group_sm_step(hapd);
+ } while (hapd->wpa_auth->changed);
+ }
+ if (hapd->conf->wpa_group_rekey) {
+ eloop_register_timeout(hapd->conf->wpa_group_rekey, 0,
+ wpa_rekey_gtk, hapd, NULL);
+ }
+}
+
+
+#ifdef CONFIG_RSN_PREAUTH
+
+static void rsn_preauth_receive(void *ctx, unsigned char *src_addr,
+ unsigned char *buf, size_t len)
+{
+ struct rsn_preauth_interface *piface = ctx;
+ struct hostapd_data *hapd = piface->hapd;
+ struct ieee802_1x_hdr *hdr;
+ struct sta_info *sta;
+ struct l2_ethhdr *ethhdr;
+
+ HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "RSN: receive pre-auth packet "
+ "from interface '%s'\n", piface->ifname);
+ if (len < sizeof(*ethhdr) + sizeof(*hdr)) {
+ HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "RSN: too short pre-auth "
+ "packet (len=%lu)\n", (unsigned long) len);
+ return;
+ }
+
+ ethhdr = (struct l2_ethhdr *) buf;
+ hdr = (struct ieee802_1x_hdr *) (ethhdr + 1);
+
+ if (memcmp(ethhdr->h_dest, hapd->own_addr, ETH_ALEN) != 0) {
+ HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "RSN: pre-auth for "
+ "foreign address " MACSTR "\n",
+ MAC2STR(ethhdr->h_dest));
+ return;
+ }
+
+ sta = ap_get_sta(hapd, ethhdr->h_source);
+ if (sta && (sta->flags & WLAN_STA_ASSOC)) {
+ HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "RSN: pre-auth for "
+ "already association STA " MACSTR "\n",
+ MAC2STR(sta->addr));
+ return;
+ }
+ if (!sta && hdr->type == IEEE802_1X_TYPE_EAPOL_START) {
+ sta = (struct sta_info *) malloc(sizeof(struct sta_info));
+ if (sta == NULL)
+ return;
+ memset(sta, 0, sizeof(*sta));
+ memcpy(sta->addr, ethhdr->h_source, ETH_ALEN);
+ sta->flags = WLAN_STA_PREAUTH;
+ sta->next = hapd->sta_list;
+ sta->wpa = WPA_VERSION_WPA2;
+ hapd->sta_list = sta;
+ hapd->num_sta++;
+ ap_sta_hash_add(hapd, sta);
+
+ ieee802_1x_new_station(hapd, sta);
+ if (sta->eapol_sm == NULL) {
+ ap_free_sta(hapd, sta);
+ sta = NULL;
+ } else {
+ sta->eapol_sm->radius_identifier = -1;
+ sta->eapol_sm->portValid = TRUE;
+ sta->eapol_sm->flags |= EAPOL_SM_PREAUTH;
+ }
+ }
+ if (sta == NULL)
+ return;
+ sta->preauth_iface = piface;
+ ieee802_1x_receive(hapd, ethhdr->h_source, (u8 *) (ethhdr + 1),
+ len - sizeof(*ethhdr));
+}
+
+
+static int rsn_preauth_iface_add(struct hostapd_data *hapd, const char *ifname)
+{
+ struct rsn_preauth_interface *piface;
+
+ HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "RSN pre-auth interface '%s'\n",
+ ifname);
+
+ piface = malloc(sizeof(*piface));
+ if (piface == NULL)
+ return -1;
+ memset(piface, 0, sizeof(*piface));
+ piface->hapd = hapd;
+
+ piface->ifname = strdup(ifname);
+ if (piface->ifname == NULL) {
+ goto fail1;
+ }
+
+ piface->l2 = l2_packet_init(piface->ifname, NULL, ETH_P_PREAUTH,
+ rsn_preauth_receive, piface);
+ if (piface->l2 == NULL) {
+ printf("Failed to open register layer 2 access to "
+ "ETH_P_PREAUTH\n");
+ goto fail2;
+ }
+ l2_packet_set_rx_l2_hdr(piface->l2, 1);
+
+ piface->next = hapd->preauth_iface;
+ hapd->preauth_iface = piface;
+ return 0;
+
+fail2:
+ free(piface->ifname);
+fail1:
+ free(piface);
+ return -1;
+}
+
+
+static void rsn_preauth_iface_deinit(struct hostapd_data *hapd)
+{
+ struct rsn_preauth_interface *piface, *prev;
+
+ piface = hapd->preauth_iface;
+ hapd->preauth_iface = NULL;
+ while (piface) {
+ prev = piface;
+ piface = piface->next;
+ l2_packet_deinit(prev->l2);
+ free(prev->ifname);
+ free(prev);
+ }
+}
+
+
+static int rsn_preauth_iface_init(struct hostapd_data *hapd)
+{
+ char *tmp, *start, *end;
+
+ if (hapd->conf->rsn_preauth_interfaces == NULL)
+ return 0;
+
+ tmp = strdup(hapd->conf->rsn_preauth_interfaces);
+ if (tmp == NULL)
+ return -1;
+ start = tmp;
+ for (;;) {
+ while (*start == ' ')
+ start++;
+ if (*start == '\0')
+ break;
+ end = strchr(start, ' ');
+ if (end)
+ *end = '\0';
+
+ if (rsn_preauth_iface_add(hapd, start)) {
+ rsn_preauth_iface_deinit(hapd);
+ return -1;
+ }
+
+ if (end)
+ start = end + 1;
+ else
+ break;
+ }
+ free(tmp);
+ return 0;
+}
+
+
+void rsn_preauth_finished(struct hostapd_data *hapd, struct sta_info *sta,
+ int success)
+{
+ u8 *key;
+ size_t len;
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA,
+ HOSTAPD_LEVEL_INFO, "pre-authentication %s",
+ success ? "succeeded" : "failed");
+
+ key = ieee802_1x_get_key_crypt(sta->eapol_sm, &len);
+ if (success && key) {
+ pmksa_cache_add(hapd, sta, key, dot11RSNAConfigPMKLifetime);
+ }
+
+ ap_free_sta(hapd, sta);
+}
+
+
+void rsn_preauth_send(struct hostapd_data *hapd, struct sta_info *sta,
+ u8 *buf, size_t len)
+{
+ struct rsn_preauth_interface *piface;
+ struct l2_ethhdr *ethhdr;
+
+ piface = hapd->preauth_iface;
+ while (piface) {
+ if (piface == sta->preauth_iface)
+ break;
+ piface = piface->next;
+ }
+
+ if (piface == NULL) {
+ HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "RSN: Could not find "
+ "pre-authentication interface for " MACSTR "\n",
+ MAC2STR(sta->addr));
+ return;
+ }
+
+ ethhdr = malloc(sizeof(*ethhdr) + len);
+ if (ethhdr == NULL)
+ return;
+
+ memcpy(ethhdr->h_dest, sta->addr, ETH_ALEN);
+ memcpy(ethhdr->h_source, hapd->own_addr, ETH_ALEN);
+ ethhdr->h_proto = htons(ETH_P_PREAUTH);
+ memcpy(ethhdr + 1, buf, len);
+
+ if (l2_packet_send(piface->l2, (u8 *) ethhdr, sizeof(*ethhdr) + len) <
+ 0) {
+ printf("Failed to send preauth packet using l2_packet_send\n");
+ }
+ free(ethhdr);
+}
+
+#else /* CONFIG_RSN_PREAUTH */
+
+static inline int rsn_preauth_iface_init(struct hostapd_data *hapd)
+{
+ return 0;
+}
+
+static inline void rsn_preauth_iface_deinit(struct hostapd_data *hapd)
+{
+}
+
+void rsn_preauth_finished(struct hostapd_data *hapd, struct sta_info *sta,
+ int success)
+{
+}
+
+void rsn_preauth_send(struct hostapd_data *hapd, struct sta_info *sta,
+ u8 *buf, size_t len)
+{
+}
+
+#endif /* CONFIG_RSN_PREAUTH */
+
+
+int wpa_init(struct hostapd_data *hapd)
+{
+ u8 rkey[32];
+ u8 buf[ETH_ALEN + 8];
+
+ if (rsn_preauth_iface_init(hapd))
+ return -1;
+
+ if (hostapd_set_privacy(hapd, 1)) {
+ printf("Could not set PrivacyInvoked for interface %s\n",
+ hapd->conf->iface);
+ return -1;
+ }
+
+ if (wpa_gen_wpa_ie(hapd)) {
+ printf("Could not generate WPA IE.\n");
+ return -1;
+ }
+
+ if (hostapd_set_generic_elem(hapd, hapd->wpa_ie, hapd->wpa_ie_len)) {
+ printf("Failed to configure WPA IE for the kernel driver.\n");
+ return -1;
+ }
+
+ hapd->wpa_auth = malloc(sizeof(struct wpa_authenticator));
+ if (hapd->wpa_auth == NULL)
+ return -1;
+ memset(hapd->wpa_auth, 0, sizeof(struct wpa_authenticator));
+ hapd->wpa_auth->GTKAuthenticator = TRUE;
+ switch (hapd->conf->wpa_group) {
+ case WPA_CIPHER_CCMP:
+ hapd->wpa_auth->GTK_len = 16;
+ break;
+ case WPA_CIPHER_TKIP:
+ hapd->wpa_auth->GTK_len = 32;
+ break;
+ case WPA_CIPHER_WEP104:
+ hapd->wpa_auth->GTK_len = 13;
+ break;
+ case WPA_CIPHER_WEP40:
+ hapd->wpa_auth->GTK_len = 5;
+ break;
+ }
+
+ /* Counter = PRF-256(Random number, "Init Counter",
+ * Local MAC Address || Time)
+ */
+ memcpy(buf, hapd->own_addr, ETH_ALEN);
+ hostapd_get_ntp_timestamp(buf + ETH_ALEN);
+ if (hostapd_get_rand(rkey, sizeof(rkey)) ||
+ hostapd_get_rand(hapd->wpa_auth->GMK, WPA_GMK_LEN)) {
+ printf("Failed to get random data for WPA initialization.\n");
+ free(hapd->wpa_auth);
+ hapd->wpa_auth = NULL;
+ return -1;
+ }
+
+ sha1_prf(rkey, sizeof(rkey), "Init Counter", buf, sizeof(buf),
+ hapd->wpa_auth->Counter, WPA_NONCE_LEN);
+
+ if (hapd->conf->wpa_gmk_rekey) {
+ eloop_register_timeout(hapd->conf->wpa_gmk_rekey, 0,
+ wpa_rekey_gmk, hapd, NULL);
+ }
+
+ if (hapd->conf->wpa_group_rekey) {
+ eloop_register_timeout(hapd->conf->wpa_group_rekey, 0,
+ wpa_rekey_gtk, hapd, NULL);
+ }
+
+ hapd->wpa_auth->GInit = TRUE;
+ wpa_group_sm_step(hapd);
+ hapd->wpa_auth->GInit = FALSE;
+ wpa_group_sm_step(hapd);
+
+ return 0;
+}
+
+
+void wpa_deinit(struct hostapd_data *hapd)
+{
+ rsn_preauth_iface_deinit(hapd);
+
+ eloop_cancel_timeout(wpa_rekey_gmk, hapd, NULL);
+ eloop_cancel_timeout(wpa_rekey_gtk, hapd, NULL);
+
+ if (hostapd_set_privacy(hapd, 0)) {
+ printf("Could not disable PrivacyInvoked for interface %s\n",
+ hapd->conf->iface);
+ }
+
+ if (hostapd_set_generic_elem(hapd, (u8 *) "", 0)) {
+ printf("Could not remove generic information element from "
+ "interface %s\n", hapd->conf->iface);
+ }
+
+ free(hapd->wpa_ie);
+ hapd->wpa_ie = NULL;
+ free(hapd->wpa_auth);
+ hapd->wpa_auth = NULL;
+
+ pmksa_cache_free(hapd);
+}
+
+
+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;
+ 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(const u8 *pmk, const u8 *aa, const u8 *spa, u8 *pmkid)
+{
+ char *title = "PMK Name";
+ const u8 *addr[3];
+ const size_t len[3] = { 8, ETH_ALEN, ETH_ALEN };
+ unsigned char hash[SHA1_MAC_LEN];
+
+ addr[0] = (u8 *) 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 hostapd_data *hapd);
+
+
+static void pmksa_cache_free_entry(struct hostapd_data *hapd,
+ struct rsn_pmksa_cache *entry)
+{
+ struct sta_info *sta;
+ struct rsn_pmksa_cache *pos, *prev;
+ hapd->pmksa_count--;
+ for (sta = hapd->sta_list; sta != NULL; sta = sta->next) {
+ if (sta->pmksa == entry)
+ sta->pmksa = NULL;
+ }
+ pos = hapd->pmkid[PMKID_HASH(entry->pmkid)];
+ prev = NULL;
+ while (pos) {
+ if (pos == entry) {
+ if (prev != NULL) {
+ prev->hnext = pos->hnext;
+ } else {
+ hapd->pmkid[PMKID_HASH(entry->pmkid)] =
+ pos->hnext;
+ }
+ break;
+ }
+ prev = pos;
+ pos = pos->hnext;
+ }
+
+ pos = hapd->pmksa;
+ prev = NULL;
+ while (pos) {
+ if (pos == entry) {
+ if (prev != NULL)
+ prev->next = pos->next;
+ else
+ hapd->pmksa = pos->next;
+ break;
+ }
+ prev = pos;
+ pos = pos->next;
+ }
+ free(entry);
+}
+
+
+static void pmksa_cache_expire(void *eloop_ctx, void *timeout_ctx)
+{
+ struct hostapd_data *hapd = eloop_ctx;
+ time_t now;
+
+ time(&now);
+ while (hapd->pmksa && hapd->pmksa->expiration <= now) {
+ struct rsn_pmksa_cache *entry = hapd->pmksa;
+ hapd->pmksa = entry->next;
+ HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL,
+ "RSN: expired PMKSA cache entry for "
+ MACSTR, MAC2STR(entry->spa));
+ pmksa_cache_free_entry(hapd, entry);
+ }
+
+ pmksa_cache_set_expiration(hapd);
+}
+
+
+static void pmksa_cache_set_expiration(struct hostapd_data *hapd)
+{
+ int sec;
+ eloop_cancel_timeout(pmksa_cache_expire, hapd, NULL);
+ if (hapd->pmksa == NULL)
+ return;
+ sec = hapd->pmksa->expiration - time(NULL);
+ if (sec < 0)
+ sec = 0;
+ eloop_register_timeout(sec + 1, 0, pmksa_cache_expire, hapd, NULL);
+}
+
+
+void pmksa_cache_add(struct hostapd_data *hapd, struct sta_info *sta, u8 *pmk,
+ int session_timeout)
+{
+ struct rsn_pmksa_cache *entry, *pos, *prev;
+
+ if (sta->wpa != WPA_VERSION_WPA2)
+ return;
+
+ entry = malloc(sizeof(*entry));
+ if (entry == NULL)
+ return;
+ memset(entry, 0, sizeof(*entry));
+ memcpy(entry->pmk, pmk, PMK_LEN);
+ rsn_pmkid(pmk, hapd->own_addr, sta->addr, entry->pmkid);
+ time(&entry->expiration);
+ if (session_timeout > 0)
+ entry->expiration += session_timeout;
+ else
+ entry->expiration += dot11RSNAConfigPMKLifetime;
+ entry->akmp = WPA_KEY_MGMT_IEEE8021X;
+ memcpy(entry->spa, sta->addr, ETH_ALEN);
+
+ /* Replace an old entry for the same STA (if found) with the new entry
+ */
+ pos = pmksa_cache_get(hapd, sta->addr, NULL);
+ if (pos)
+ pmksa_cache_free_entry(hapd, pos);
+
+ if (hapd->pmksa_count >= pmksa_cache_max_entries && hapd->pmksa) {
+ /* Remove the oldest entry to make room for the new entry */
+ HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL,
+ "RSN: removed the oldest PMKSA cache entry (for "
+ MACSTR ") to make room for new one",
+ MAC2STR(hapd->pmksa->spa));
+ pmksa_cache_free_entry(hapd, hapd->pmksa);
+ }
+
+ /* Add the new entry; order by expiration time */
+ pos = hapd->pmksa;
+ prev = NULL;
+ while (pos) {
+ if (pos->expiration > entry->expiration)
+ break;
+ prev = pos;
+ pos = pos->next;
+ }
+ if (prev == NULL) {
+ entry->next = hapd->pmksa;
+ hapd->pmksa = entry;
+ } else {
+ entry->next = prev->next;
+ prev->next = entry;
+ }
+ entry->hnext = hapd->pmkid[PMKID_HASH(entry->pmkid)];
+ hapd->pmkid[PMKID_HASH(entry->pmkid)] = entry;
+
+ hapd->pmksa_count++;
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA,
+ HOSTAPD_LEVEL_DEBUG,
+ "added PMKSA cache entry");
+ if (HOSTAPD_DEBUG_COND(HOSTAPD_DEBUG_MINIMAL)) {
+ hostapd_hexdump("RSN: added PMKID", entry->pmkid, PMKID_LEN);
+ }
+}
+
+
+static void pmksa_cache_free(struct hostapd_data *hapd)
+{
+ struct rsn_pmksa_cache *entry, *prev;
+ int i;
+ struct sta_info *sta;
+
+ entry = hapd->pmksa;
+ hapd->pmksa = NULL;
+ while (entry) {
+ prev = entry;
+ entry = entry->next;
+ free(prev);
+ }
+ eloop_cancel_timeout(pmksa_cache_expire, hapd, NULL);
+ for (i = 0; i < PMKID_HASH_SIZE; i++)
+ hapd->pmkid[i] = NULL;
+ for (sta = hapd->sta_list; sta; sta = sta->next)
+ sta->pmksa = NULL;
+}
+
+
+static struct rsn_pmksa_cache * pmksa_cache_get(struct hostapd_data *hapd,
+ u8 *spa, u8 *pmkid)
+{
+ struct rsn_pmksa_cache *entry;
+
+ if (pmkid)
+ entry = hapd->pmkid[PMKID_HASH(pmkid)];
+ else
+ entry = hapd->pmksa;
+ while (entry) {
+ if ((spa == NULL || memcmp(entry->spa, spa, ETH_ALEN) == 0) &&
+ (pmkid == NULL ||
+ memcmp(entry->pmkid, pmkid, PMKID_LEN) == 0))
+ return entry;
+ entry = pmkid ? entry->hnext : entry->next;
+ }
+ return NULL;
+}
+
+
+struct wpa_ie_data {
+ int pairwise_cipher;
+ int group_cipher;
+ int key_mgmt;
+ int capabilities;
+ size_t num_pmkid;
+ u8 *pmkid;
+};
+
+
+static int wpa_parse_wpa_ie_wpa(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;
+
+ memset(data, 0, sizeof(*data));
+ data->pairwise_cipher = WPA_CIPHER_TKIP;
+ data->group_cipher = WPA_CIPHER_TKIP;
+ data->key_mgmt = WPA_KEY_MGMT_IEEE8021X;
+
+ if (wpa_ie_len < sizeof(struct wpa_ie_hdr))
+ return -1;
+
+ hdr = (struct wpa_ie_hdr *) wpa_ie;
+
+ if (hdr->elem_id != WLAN_EID_GENERIC ||
+ hdr->len != wpa_ie_len - 2 ||
+ memcmp(&hdr->oui, WPA_OUI_TYPE, WPA_SELECTOR_LEN) != 0 ||
+ le_to_host16(hdr->version) != WPA_VERSION) {
+ return -2;
+ }
+
+ 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)
+ return -3;
+
+ 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)
+ return -4;
+ 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)
+ return -5;
+
+ 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)
+ return -6;
+ 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)
+ return -7;
+
+ if (left >= 2) {
+ data->capabilities = pos[0] | (pos[1] << 8);
+ pos += 2;
+ left -= 2;
+ }
+
+ if (left > 0) {
+ return -8;
+ }
+
+ return 0;
+}
+
+
+static int wpa_parse_wpa_ie_rsn(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;
+
+ memset(data, 0, sizeof(*data));
+ data->pairwise_cipher = WPA_CIPHER_CCMP;
+ data->group_cipher = WPA_CIPHER_CCMP;
+ data->key_mgmt = WPA_KEY_MGMT_IEEE8021X;
+
+ if (rsn_ie_len < sizeof(struct rsn_ie_hdr))
+ return -1;
+
+ hdr = (struct rsn_ie_hdr *) rsn_ie;
+
+ if (hdr->elem_id != WLAN_EID_RSN ||
+ hdr->len != rsn_ie_len - 2 ||
+ le_to_host16(hdr->version) != RSN_VERSION) {
+ return -2;
+ }
+
+ 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)
+ return -3;
+
+ 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)
+ return -4;
+ 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)
+ return -5;
+
+ 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)
+ return -6;
+ 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)
+ return -7;
+
+ 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) {
+ printf("RSN: too short RSN IE for PMKIDs "
+ "(num=%lu, left=%d)\n",
+ (unsigned long) data->num_pmkid, left);
+ return -9;
+ }
+ data->pmkid = pos;
+ pos += data->num_pmkid * PMKID_LEN;
+ left -= data->num_pmkid * PMKID_LEN;
+ }
+
+ if (left > 0) {
+ return -8;
+ }
+
+ return 0;
+}
+
+
+int wpa_validate_wpa_ie(struct hostapd_data *hapd, struct sta_info *sta,
+ u8 *wpa_ie, size_t wpa_ie_len, int version)
+{
+ struct wpa_ie_data data;
+ int ciphers, key_mgmt, res, i;
+ const u8 *selector;
+
+ if (version == HOSTAPD_WPA_VERSION_WPA2) {
+ res = wpa_parse_wpa_ie_rsn(wpa_ie, wpa_ie_len, &data);
+
+ selector = RSN_AUTH_KEY_MGMT_UNSPEC_802_1X;
+ if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X)
+ selector = RSN_AUTH_KEY_MGMT_UNSPEC_802_1X;
+ else if (data.key_mgmt & WPA_KEY_MGMT_PSK)
+ selector = RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X;
+ memcpy(hapd->wpa_auth->dot11RSNAAuthenticationSuiteSelected,
+ selector, RSN_SELECTOR_LEN);
+
+ selector = RSN_CIPHER_SUITE_CCMP;
+ if (data.pairwise_cipher & WPA_CIPHER_CCMP)
+ selector = RSN_CIPHER_SUITE_CCMP;
+ else if (data.pairwise_cipher & WPA_CIPHER_TKIP)
+ selector = RSN_CIPHER_SUITE_TKIP;
+ else if (data.pairwise_cipher & WPA_CIPHER_WEP104)
+ selector = RSN_CIPHER_SUITE_WEP104;
+ else if (data.pairwise_cipher & WPA_CIPHER_WEP40)
+ selector = RSN_CIPHER_SUITE_WEP40;
+ else if (data.pairwise_cipher & WPA_CIPHER_NONE)
+ selector = RSN_CIPHER_SUITE_NONE;
+ memcpy(hapd->wpa_auth->dot11RSNAPairwiseCipherSelected,
+ selector, RSN_SELECTOR_LEN);
+
+ selector = RSN_CIPHER_SUITE_CCMP;
+ if (data.group_cipher & WPA_CIPHER_CCMP)
+ selector = RSN_CIPHER_SUITE_CCMP;
+ else if (data.group_cipher & WPA_CIPHER_TKIP)
+ selector = RSN_CIPHER_SUITE_TKIP;
+ else if (data.group_cipher & WPA_CIPHER_WEP104)
+ selector = RSN_CIPHER_SUITE_WEP104;
+ else if (data.group_cipher & WPA_CIPHER_WEP40)
+ selector = RSN_CIPHER_SUITE_WEP40;
+ else if (data.group_cipher & WPA_CIPHER_NONE)
+ selector = RSN_CIPHER_SUITE_NONE;
+ memcpy(hapd->wpa_auth->dot11RSNAGroupCipherSelected,
+ selector, RSN_SELECTOR_LEN);
+ } else {
+ res = wpa_parse_wpa_ie_wpa(wpa_ie, wpa_ie_len, &data);
+
+ selector = WPA_AUTH_KEY_MGMT_UNSPEC_802_1X;
+ if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X)
+ selector = WPA_AUTH_KEY_MGMT_UNSPEC_802_1X;
+ else if (data.key_mgmt & WPA_KEY_MGMT_PSK)
+ selector = WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X;
+ memcpy(hapd->wpa_auth->dot11RSNAAuthenticationSuiteSelected,
+ selector, WPA_SELECTOR_LEN);
+
+ selector = WPA_CIPHER_SUITE_TKIP;
+ if (data.pairwise_cipher & WPA_CIPHER_CCMP)
+ selector = WPA_CIPHER_SUITE_CCMP;
+ else if (data.pairwise_cipher & WPA_CIPHER_TKIP)
+ selector = WPA_CIPHER_SUITE_TKIP;
+ else if (data.pairwise_cipher & WPA_CIPHER_WEP104)
+ selector = WPA_CIPHER_SUITE_WEP104;
+ else if (data.pairwise_cipher & WPA_CIPHER_WEP40)
+ selector = WPA_CIPHER_SUITE_WEP40;
+ else if (data.pairwise_cipher & WPA_CIPHER_NONE)
+ selector = WPA_CIPHER_SUITE_NONE;
+ memcpy(hapd->wpa_auth->dot11RSNAPairwiseCipherSelected,
+ selector, WPA_SELECTOR_LEN);
+
+ selector = WPA_CIPHER_SUITE_TKIP;
+ if (data.group_cipher & WPA_CIPHER_CCMP)
+ selector = WPA_CIPHER_SUITE_CCMP;
+ else if (data.group_cipher & WPA_CIPHER_TKIP)
+ selector = WPA_CIPHER_SUITE_TKIP;
+ else if (data.group_cipher & WPA_CIPHER_WEP104)
+ selector = WPA_CIPHER_SUITE_WEP104;
+ else if (data.group_cipher & WPA_CIPHER_WEP40)
+ selector = WPA_CIPHER_SUITE_WEP40;
+ else if (data.group_cipher & WPA_CIPHER_NONE)
+ selector = WPA_CIPHER_SUITE_NONE;
+ memcpy(hapd->wpa_auth->dot11RSNAGroupCipherSelected,
+ selector, WPA_SELECTOR_LEN);
+ }
+ if (res) {
+ printf("Failed to parse WPA/RSN IE from " MACSTR " (res=%d)\n",
+ MAC2STR(sta->addr), res);
+ hostapd_hexdump("WPA/RSN IE", wpa_ie, wpa_ie_len);
+ return WPA_INVALID_IE;
+ }
+
+ if (data.group_cipher != hapd->conf->wpa_group) {
+ printf("Invalid WPA group cipher (0x%x) from " MACSTR "\n",
+ data.group_cipher, MAC2STR(sta->addr));
+ return WPA_INVALID_GROUP;
+ }
+
+ key_mgmt = data.key_mgmt & hapd->conf->wpa_key_mgmt;
+ if (!key_mgmt) {
+ printf("Invalid WPA key mgmt (0x%x) from " MACSTR "\n",
+ data.key_mgmt, MAC2STR(sta->addr));
+ return WPA_INVALID_AKMP;
+ }
+ if (key_mgmt & WPA_KEY_MGMT_IEEE8021X)
+ sta->wpa_key_mgmt = WPA_KEY_MGMT_IEEE8021X;
+ else
+ sta->wpa_key_mgmt = WPA_KEY_MGMT_PSK;
+
+ ciphers = data.pairwise_cipher & hapd->conf->wpa_pairwise;
+ if (!ciphers) {
+ printf("Invalid WPA pairwise cipher (0x%x) from " MACSTR "\n",
+ data.pairwise_cipher, MAC2STR(sta->addr));
+ return WPA_INVALID_PAIRWISE;
+ }
+
+ if (ciphers & WPA_CIPHER_CCMP)
+ sta->pairwise = WPA_CIPHER_CCMP;
+ else
+ sta->pairwise = WPA_CIPHER_TKIP;
+
+ /* TODO: clear WPA/WPA2 state if STA changes from one to another */
+ if (wpa_ie[0] == WLAN_EID_RSN)
+ sta->wpa = WPA_VERSION_WPA2;
+ else
+ sta->wpa = WPA_VERSION_WPA;
+
+ for (i = 0; i < data.num_pmkid; i++) {
+ if (HOSTAPD_DEBUG_COND(HOSTAPD_DEBUG_MINIMAL)) {
+ hostapd_hexdump("RSN IE: STA PMKID",
+ &data.pmkid[i * PMKID_LEN], PMKID_LEN);
+ }
+ sta->pmksa = pmksa_cache_get(hapd, sta->addr,
+ &data.pmkid[i * PMKID_LEN]);
+ if (sta->pmksa) {
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA,
+ HOSTAPD_LEVEL_DEBUG,
+ "PMKID found from PMKSA cache");
+ if (hapd->wpa_auth) {
+ memcpy(hapd->wpa_auth->dot11RSNAPMKIDUsed,
+ sta->pmksa->pmkid, PMKID_LEN);
+ }
+ break;
+ }
+ }
+
+ return WPA_IE_OK;
+}
+
+
+void wpa_new_station(struct hostapd_data *hapd, struct sta_info *sta)
+{
+ struct wpa_state_machine *sm;
+
+ if (!hapd->conf->wpa)
+ return;
+
+ if (sta->wpa_sm) {
+ sm = sta->wpa_sm;
+ memset(sm->key_replay_counter, 0, WPA_REPLAY_COUNTER_LEN);
+ sm->ReAuthenticationRequest = TRUE;
+ wpa_sm_step(sm);
+ return;
+ }
+
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA,
+ HOSTAPD_LEVEL_DEBUG, "start authentication");
+ sm = malloc(sizeof(struct wpa_state_machine));
+ if (sm == NULL)
+ return;
+ memset(sm, 0, sizeof(struct wpa_state_machine));
+
+ sm->hapd = hapd;
+ sm->sta = sta;
+ sta->wpa_sm = sm;
+
+ sm->Init = TRUE;
+ wpa_sm_step(sm);
+ sm->Init = FALSE;
+ sm->AuthenticationRequest = TRUE;
+ wpa_sm_step(sm);
+}
+
+
+void wpa_free_station(struct sta_info *sta)
+{
+ struct wpa_state_machine *sm = sta->wpa_sm;
+
+ if (sm == NULL)
+ return;
+
+ if (sm->hapd->conf->wpa_strict_rekey && sm->has_GTK) {
+ hostapd_logger(sm->hapd, sta->addr, HOSTAPD_MODULE_WPA,
+ HOSTAPD_LEVEL_DEBUG, "strict rekeying - force "
+ "GTK rekey since STA is leaving");
+ eloop_cancel_timeout(wpa_rekey_gtk, sm->hapd, NULL);
+ eloop_register_timeout(0, 500000, wpa_rekey_gtk, sm->hapd,
+ NULL);
+ }
+
+ eloop_cancel_timeout(wpa_send_eapol_timeout, sm->hapd, sta);
+ eloop_cancel_timeout(wpa_sm_call_step, sm->hapd, sta->wpa_sm);
+ free(sm->last_rx_eapol_key);
+ free(sm);
+ sta->wpa_sm = NULL;
+}
+
+
+static void wpa_request_new_ptk(struct hostapd_data *hapd,
+ struct sta_info *sta)
+{
+ struct wpa_state_machine *sm = sta->wpa_sm;
+
+ if (sm == NULL)
+ return;
+
+ sm->PTKRequest = TRUE;
+ sm->PTK_valid = 0;
+}
+
+
+void wpa_receive(struct hostapd_data *hapd, struct sta_info *sta,
+ u8 *data, size_t data_len)
+{
+ struct wpa_state_machine *sm = sta->wpa_sm;
+ struct ieee802_1x_hdr *hdr;
+ struct wpa_eapol_key *key;
+ u16 key_info, key_data_length;
+ enum { PAIRWISE_2, PAIRWISE_4, GROUP_2, REQUEST } msg;
+ char *msgtxt;
+
+ if (!hapd->conf->wpa)
+ return;
+
+ if (sm == NULL)
+ return;
+
+ if (data_len < sizeof(*hdr) + sizeof(*key))
+ return;
+
+ hdr = (struct ieee802_1x_hdr *) data;
+ key = (struct wpa_eapol_key *) (hdr + 1);
+ key_info = ntohs(key->key_info);
+ key_data_length = ntohs(key->key_data_length);
+
+ /* FIX: verify that the EAPOL-Key frame was encrypted if pairwise keys
+ * are set */
+
+ if (key_info & WPA_KEY_INFO_REQUEST) {
+ msg = REQUEST;
+ msgtxt = "Request";
+ } else if (!(key_info & WPA_KEY_INFO_KEY_TYPE)) {
+ msg = GROUP_2;
+ msgtxt = "2/2 Group";
+ } else if (key_data_length == 0) {
+ msg = PAIRWISE_4;
+ msgtxt = "4/4 Pairwise";
+ } else {
+ msg = PAIRWISE_2;
+ msgtxt = "2/4 Pairwise";
+ }
+
+ if (key_info & WPA_KEY_INFO_REQUEST) {
+ if (sta->req_replay_counter_used &&
+ memcmp(key->replay_counter, sta->req_replay_counter,
+ WPA_REPLAY_COUNTER_LEN) <= 0) {
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA,
+ HOSTAPD_LEVEL_WARNING,
+ "received EAPOL-Key request with "
+ "replayed counter");
+ return;
+ }
+ }
+
+ if (!(key_info & WPA_KEY_INFO_REQUEST) &&
+ (!sm->key_replay_counter_valid ||
+ memcmp(key->replay_counter, sm->key_replay_counter,
+ WPA_REPLAY_COUNTER_LEN) != 0)) {
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA,
+ HOSTAPD_LEVEL_INFO,
+ "received EAPOL-Key %s with unexpected replay "
+ "counter", msgtxt);
+ if (HOSTAPD_DEBUG_COND(HOSTAPD_DEBUG_MINIMAL)) {
+ hostapd_hexdump("expected replay counter",
+ sm->key_replay_counter,
+ WPA_REPLAY_COUNTER_LEN);
+ hostapd_hexdump("received replay counter",
+ key->replay_counter,
+ WPA_REPLAY_COUNTER_LEN);
+ }
+ return;
+ }
+
+ switch (msg) {
+ case PAIRWISE_2:
+ if (sm->wpa_ptk_state != WPA_PTK_PTKSTART &&
+ sm->wpa_ptk_state != WPA_PTK_PTKCALCNEGOTIATING) {
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA,
+ HOSTAPD_LEVEL_INFO,
+ "received EAPOL-Key msg 2/4 in invalid"
+ " state (%d) - dropped",
+ sm->wpa_ptk_state);
+ return;
+ }
+ if (sta->wpa_ie == NULL ||
+ sta->wpa_ie_len != key_data_length ||
+ memcmp(sta->wpa_ie, key + 1, key_data_length) != 0) {
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA,
+ HOSTAPD_LEVEL_INFO,
+ "WPA IE from (Re)AssocReq did not match"
+ " with msg 2/4");
+ if (HOSTAPD_DEBUG_COND(HOSTAPD_DEBUG_MINIMAL)) {
+ if (sta->wpa_ie) {
+ hostapd_hexdump("WPA IE in AssocReq",
+ sta->wpa_ie,
+ sta->wpa_ie_len);
+ }
+ hostapd_hexdump("WPA IE in msg 2/4",
+ (u8 *) (key + 1),
+ key_data_length);
+ }
+ /* MLME-DEAUTHENTICATE.request */
+ wpa_sta_disconnect(hapd, sta);
+ return;
+ }
+ break;
+ case PAIRWISE_4:
+ if (sm->wpa_ptk_state != WPA_PTK_PTKINITNEGOTIATING ||
+ !sm->PTK_valid) {
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA,
+ HOSTAPD_LEVEL_INFO,
+ "received EAPOL-Key msg 4/4 in invalid"
+ " state (%d) - dropped",
+ sm->wpa_ptk_state);
+ return;
+ }
+ break;
+ case GROUP_2:
+ if (sm->wpa_ptk_group_state != WPA_PTK_GROUP_REKEYNEGOTIATING
+ || !sm->PTK_valid) {
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA,
+ HOSTAPD_LEVEL_INFO,
+ "received EAPOL-Key msg 2/2 in invalid"
+ " state (%d) - dropped",
+ sm->wpa_ptk_group_state);
+ return;
+ }
+ break;
+ case REQUEST:
+ break;
+ }
+
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA,
+ HOSTAPD_LEVEL_DEBUG, "received EAPOL-Key frame (%s)",
+ msgtxt);
+
+ if (key_info & WPA_KEY_INFO_ACK) {
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA,
+ HOSTAPD_LEVEL_INFO,
+ "received invalid EAPOL-Key: Key Ack set");
+ return;
+ }
+
+ if (!(key_info & WPA_KEY_INFO_MIC)) {
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA,
+ HOSTAPD_LEVEL_INFO,
+ "received invalid EAPOL-Key: Key MIC not set");
+ return;
+ }
+
+ sm->MICVerified = FALSE;
+ if (sm->PTK_valid) {
+ if (wpa_verify_key_mic(&sm->PTK, data, data_len)) {
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA,
+ HOSTAPD_LEVEL_INFO,
+ "received EAPOL-Key with invalid MIC");
+ return;
+ }
+ sm->MICVerified = TRUE;
+ eloop_cancel_timeout(wpa_send_eapol_timeout, sta->wpa_sm->hapd,
+ sta);
+ }
+
+ if (key_info & WPA_KEY_INFO_REQUEST) {
+ if (sm->MICVerified) {
+ sta->req_replay_counter_used = 1;
+ memcpy(sta->req_replay_counter, key->replay_counter,
+ WPA_REPLAY_COUNTER_LEN);
+ } else {
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA,
+ HOSTAPD_LEVEL_INFO,
+ "received EAPOL-Key request with "
+ "invalid MIC");
+ return;
+ }
+
+ if (key_info & WPA_KEY_INFO_ERROR) {
+ /* Supplicant reported a Michael MIC error */
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA,
+ HOSTAPD_LEVEL_INFO,
+ "received EAPOL-Key Error Request "
+ "(STA detected Michael MIC failure)");
+ ieee80211_michael_mic_failure(hapd, sta->addr, 0);
+ sta->dot11RSNAStatsTKIPRemoteMICFailures++;
+ hapd->wpa_auth->dot11RSNAStatsTKIPRemoteMICFailures++;
+ /* Error report is not a request for a new key
+ * handshake, but since Authenticator may do it, let's
+ * change the keys now anyway. */
+ wpa_request_new_ptk(hapd, sta);
+ } else if (key_info & WPA_KEY_INFO_KEY_TYPE) {
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA,
+ HOSTAPD_LEVEL_INFO,
+ "received EAPOL-Key Request for new "
+ "4-Way Handshake");
+ wpa_request_new_ptk(hapd, sta);
+ } else {
+ /* TODO: this could also be a request for STAKey
+ * if Key Data fields contains peer MAC address KDE.
+ * STAKey request should have 0xdd <len> 00-0F-AC:2 in
+ * the beginning of Key Data */
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA,
+ HOSTAPD_LEVEL_INFO,
+ "received EAPOL-Key Request for GTK "
+ "rekeying");
+ wpa_request_new_ptk(hapd, sta);
+ eloop_cancel_timeout(wpa_rekey_gtk, hapd, NULL);
+ wpa_rekey_gtk(hapd, NULL);
+ }
+ } else {
+ /* Do not allow the same key replay counter to be reused. */
+ sm->key_replay_counter_valid = FALSE;
+ }
+
+ free(sm->last_rx_eapol_key);
+ sm->last_rx_eapol_key = malloc(data_len);
+ if (sm->last_rx_eapol_key == NULL)
+ return;
+ memcpy(sm->last_rx_eapol_key, data, data_len);
+ sm->last_rx_eapol_key_len = data_len;
+
+ sm->EAPOLKeyReceived = TRUE;
+ sm->EAPOLKeyPairwise = !!(key_info & WPA_KEY_INFO_KEY_TYPE);
+ sm->EAPOLKeyRequest = !!(key_info & WPA_KEY_INFO_REQUEST);
+ memcpy(sm->SNonce, key->key_nonce, WPA_NONCE_LEN);
+ wpa_sm_step(sm);
+}
+
+
+static void wpa_pmk_to_ptk(struct hostapd_data *hapd, const u8 *pmk,
+ const u8 *addr1, const u8 *addr2,
+ const u8 *nonce1, const u8 *nonce2,
+ u8 *ptk, size_t ptk_len)
+{
+ u8 data[2 * ETH_ALEN + 2 * WPA_NONCE_LEN];
+
+ /* 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, WPA_NONCE_LEN) < 0) {
+ memcpy(data + 2 * ETH_ALEN, nonce1, WPA_NONCE_LEN);
+ memcpy(data + 2 * ETH_ALEN + WPA_NONCE_LEN, nonce2,
+ WPA_NONCE_LEN);
+ } else {
+ memcpy(data + 2 * ETH_ALEN, nonce2, WPA_NONCE_LEN);
+ memcpy(data + 2 * ETH_ALEN + WPA_NONCE_LEN, nonce1,
+ WPA_NONCE_LEN);
+ }
+
+ sha1_prf(pmk, WPA_PMK_LEN, "Pairwise key expansion",
+ data, sizeof(data), ptk, ptk_len);
+
+ if (HOSTAPD_DEBUG_COND(HOSTAPD_DEBUG_MINIMAL)) {
+ hostapd_hexdump("PMK", pmk, WPA_PMK_LEN);
+ hostapd_hexdump("PTK", ptk, ptk_len);
+ }
+}
+
+
+static void wpa_gmk_to_gtk(struct hostapd_data *hapd, u8 *gmk,
+ u8 *addr, u8 *gnonce, u8 *gtk, size_t gtk_len)
+{
+ u8 data[ETH_ALEN + WPA_NONCE_LEN];
+
+ /* GTK = PRF-X(GMK, "Group key expansion", AA || GNonce) */
+ memcpy(data, addr, ETH_ALEN);
+ memcpy(data + ETH_ALEN, gnonce, WPA_NONCE_LEN);
+
+ sha1_prf(gmk, WPA_GMK_LEN, "Group key expansion",
+ data, sizeof(data), gtk, gtk_len);
+
+ if (HOSTAPD_DEBUG_COND(HOSTAPD_DEBUG_MINIMAL)) {
+ hostapd_hexdump("GMK", gmk, WPA_GMK_LEN);
+ hostapd_hexdump("GTK", gtk, gtk_len);
+ }
+}
+
+
+static void wpa_send_eapol_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ struct hostapd_data *hapd = eloop_ctx;
+ struct sta_info *sta = timeout_ctx;
+
+ if (!sta->wpa_sm || !(sta->flags & WLAN_STA_ASSOC))
+ return;
+
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA,
+ HOSTAPD_LEVEL_DEBUG, "EAPOL-Key timeout");
+ sta->wpa_sm->TimeoutEvt = TRUE;
+ wpa_sm_step(sta->wpa_sm);
+}
+
+
+static int wpa_calc_eapol_key_mic(int ver, u8 *key, u8 *data, size_t len,
+ u8 *mic)
+{
+ u8 hash[SHA1_MAC_LEN];
+
+ switch (ver) {
+ case WPA_KEY_INFO_TYPE_HMAC_MD5_RC4:
+ hmac_md5(key, 16, data, len, mic);
+ break;
+ case WPA_KEY_INFO_TYPE_HMAC_SHA1_AES:
+ hmac_sha1(key, 16, data, len, hash);
+ memcpy(mic, hash, MD5_MAC_LEN);
+ break;
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+
+static void wpa_send_eapol(struct hostapd_data *hapd, struct sta_info *sta,
+ int secure, int mic, int ack, int install,
+ int pairwise, u8 *key_rsc, u8 *nonce,
+ u8 *ie, size_t ie_len, u8 *gtk, size_t gtk_len,
+ int keyidx)
+{
+ struct wpa_state_machine *sm = sta->wpa_sm;
+ struct ieee802_1x_hdr *hdr;
+ struct wpa_eapol_key *key;
+ size_t len;
+ int key_info, alg;
+ int timeout_ms;
+ int key_data_len, pad_len = 0;
+ u8 *buf, *pos;
+
+ if (sm == NULL)
+ return;
+
+ len = sizeof(struct ieee802_1x_hdr) + sizeof(struct wpa_eapol_key);
+
+ if (sta->wpa == WPA_VERSION_WPA2) {
+ key_data_len = ie_len + gtk_len;
+ if (gtk_len)
+ key_data_len += 2 + RSN_SELECTOR_LEN + 2;
+ } else {
+ if (pairwise) {
+ /* WPA does not include GTK in 4-Way Handshake */
+ gtk = NULL;
+ gtk_len = 0;
+
+ /* key_rsc is for group key, so mask it out in case of
+ * WPA Pairwise key negotiation. */
+ key_rsc = NULL;
+ }
+ key_data_len = ie_len + gtk_len;
+ }
+
+ if (sta->pairwise == WPA_CIPHER_CCMP) {
+ key_info = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES;
+ if (gtk) {
+ pad_len = key_data_len % 8;
+ if (pad_len)
+ pad_len = 8 - pad_len;
+ key_data_len += pad_len + 8;
+ }
+ } else {
+ key_info = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4;
+ }
+
+ len += key_data_len;
+
+ hdr = malloc(len);
+ if (hdr == NULL)
+ return;
+ memset(hdr, 0, len);
+ hdr->version = EAPOL_VERSION;
+ hdr->type = IEEE802_1X_TYPE_EAPOL_KEY;
+ hdr->length = htons(len - sizeof(*hdr));
+ key = (struct wpa_eapol_key *) (hdr + 1);
+
+ key->type = sta->wpa == WPA_VERSION_WPA2 ?
+ EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA;
+ if (secure)
+ key_info |= WPA_KEY_INFO_SECURE;
+ if (mic)
+ key_info |= WPA_KEY_INFO_MIC;
+ if (ack)
+ key_info |= WPA_KEY_INFO_ACK;
+ if (install)
+ key_info |= WPA_KEY_INFO_INSTALL;
+ if (pairwise)
+ key_info |= WPA_KEY_INFO_KEY_TYPE;
+ if (gtk && sta->wpa == WPA_VERSION_WPA2)
+ key_info |= WPA_KEY_INFO_ENCR_KEY_DATA;
+ if (sta->wpa != WPA_VERSION_WPA2) {
+ if (pairwise)
+ keyidx = 0;
+ key_info |= keyidx << WPA_KEY_INFO_KEY_INDEX_SHIFT;
+ }
+ key->key_info = htons(key_info);
+
+ alg = pairwise ? sta->pairwise : hapd->conf->wpa_group;
+ switch (alg) {
+ case WPA_CIPHER_CCMP:
+ key->key_length = htons(16);
+ break;
+ case WPA_CIPHER_TKIP:
+ key->key_length = htons(32);
+ break;
+ case WPA_CIPHER_WEP40:
+ key->key_length = htons(5);
+ break;
+ case WPA_CIPHER_WEP104:
+ key->key_length = htons(13);
+ break;
+ }
+
+ inc_byte_array(sm->key_replay_counter, WPA_REPLAY_COUNTER_LEN);
+ memcpy(key->replay_counter, sm->key_replay_counter,
+ WPA_REPLAY_COUNTER_LEN);
+ sm->key_replay_counter_valid = TRUE;
+
+ if (nonce)
+ memcpy(key->key_nonce, nonce, WPA_NONCE_LEN);
+
+ if (key_rsc)
+ memcpy(key->key_rsc, key_rsc, WPA_KEY_RSC_LEN);
+
+ if (ie && !gtk) {
+ memcpy(key + 1, ie, ie_len);
+ key->key_data_length = htons(ie_len);
+ } else if (gtk) {
+ buf = malloc(key_data_len);
+ if (buf == NULL) {
+ free(hdr);
+ return;
+ }
+ memset(buf, 0, key_data_len);
+ pos = buf;
+ if (ie) {
+ memcpy(pos, ie, ie_len);
+ pos += ie_len;
+ }
+ if (sta->wpa == WPA_VERSION_WPA2) {
+ *pos++ = WLAN_EID_GENERIC;
+ *pos++ = RSN_SELECTOR_LEN + 2 + gtk_len;
+ memcpy(pos, RSN_KEY_DATA_GROUPKEY, RSN_SELECTOR_LEN);
+ pos += RSN_SELECTOR_LEN;
+ *pos++ = keyidx & 0x03;
+ *pos++ = 0;
+ }
+ memcpy(pos, gtk, gtk_len);
+ pos += gtk_len;
+ if (pad_len)
+ *pos++ = 0xdd;
+
+ if (HOSTAPD_DEBUG_COND(HOSTAPD_DEBUG_MINIMAL)) {
+ hostapd_hexdump("Plaintext EAPOL-Key Key Data",
+ buf, key_data_len);
+ }
+ if (key_info & WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
+ aes_wrap(sm->PTK.encr_key, (key_data_len - 8) / 8, buf,
+ (u8 *) (key + 1));
+ key->key_data_length = htons(key_data_len);
+ } else {
+ u8 ek[32];
+ memcpy(key->key_iv,
+ hapd->wpa_auth->Counter + WPA_NONCE_LEN - 16,
+ 16);
+ inc_byte_array(hapd->wpa_auth->Counter, WPA_NONCE_LEN);
+ memcpy(ek, key->key_iv, 16);
+ memcpy(ek + 16, sm->PTK.encr_key, 16);
+ memcpy(key + 1, buf, key_data_len);
+ rc4_skip(ek, 32, 256, (u8 *) (key + 1), key_data_len);
+ key->key_data_length = htons(key_data_len);
+ }
+ free(buf);
+ }
+
+ if (mic) {
+ if (!sm->PTK_valid) {
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA,
+ HOSTAPD_LEVEL_DEBUG, "PTK not valid "
+ "when sending EAPOL-Key frame");
+ free(hdr);
+ return;
+ }
+ wpa_calc_eapol_key_mic(key_info & WPA_KEY_INFO_TYPE_MASK,
+ sm->PTK.mic_key, (u8 *) hdr, len,
+ key->key_mic);
+ }
+
+ if (sta->eapol_sm)
+ sta->eapol_sm->dot1xAuthEapolFramesTx++;
+ hostapd_send_eapol(hapd, sta->addr, (u8 *) hdr, len, sm->pairwise_set);
+ free(hdr);
+
+ timeout_ms = pairwise ? dot11RSNAConfigPairwiseUpdateTimeOut :
+ dot11RSNAConfigGroupUpdateTimeOut;
+ eloop_register_timeout(timeout_ms / 1000, (timeout_ms % 1000) * 1000,
+ wpa_send_eapol_timeout, hapd, sta);
+}
+
+
+static int wpa_verify_key_mic(struct wpa_ptk *PTK, u8 *data, size_t data_len)
+{
+ struct ieee802_1x_hdr *hdr;
+ struct wpa_eapol_key *key;
+ u16 key_info;
+ int type, ret = 0;
+ u8 mic[16];
+
+ if (data_len < sizeof(*hdr) + sizeof(*key))
+ return -1;
+
+ hdr = (struct ieee802_1x_hdr *) data;
+ key = (struct wpa_eapol_key *) (hdr + 1);
+ key_info = ntohs(key->key_info);
+ type = key_info & WPA_KEY_INFO_TYPE_MASK;
+ memcpy(mic, key->key_mic, 16);
+ memset(key->key_mic, 0, 16);
+ if (wpa_calc_eapol_key_mic(key_info & WPA_KEY_INFO_TYPE_MASK,
+ PTK->mic_key, data, data_len, key->key_mic)
+ || memcmp(mic, key->key_mic, 16) != 0)
+ ret = -1;
+ memcpy(key->key_mic, mic, 16);
+ return ret;
+}
+
+
+void wpa_sm_event(struct hostapd_data *hapd, struct sta_info *sta,
+ wpa_event event)
+{
+ struct wpa_state_machine *sm = sta->wpa_sm;
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA,
+ HOSTAPD_LEVEL_DEBUG,
+ "event %d notification", event);
+ if (sm == NULL)
+ return;
+
+ switch (event) {
+ case WPA_AUTH:
+ case WPA_ASSOC:
+ break;
+ case WPA_DEAUTH:
+ case WPA_DISASSOC:
+ sm->DeauthenticationRequest = TRUE;
+ break;
+ case WPA_REAUTH:
+ case WPA_REAUTH_EAPOL:
+ sm->ReAuthenticationRequest = TRUE;
+ break;
+ }
+
+ if ((event == WPA_ASSOC || event == WPA_REAUTH) &&
+ sta->eapol_sm && sta->pmksa) {
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA,
+ HOSTAPD_LEVEL_DEBUG,
+ "PMK from PMKSA cache - skip IEEE 802.1X/EAP");
+ /* Setup EAPOL state machines to already authenticated state
+ * because of existing PMKSA information in the cache. */
+ sta->eapol_sm->keyRun = TRUE;
+ sta->eapol_sm->keyAvailable = TRUE;
+ sta->eapol_sm->auth_pae.state = AUTH_PAE_AUTHENTICATING;
+ sta->eapol_sm->be_auth.state = BE_AUTH_SUCCESS;
+ sta->eapol_sm->authSuccess = TRUE;
+ }
+
+ sm->PTK_valid = FALSE;
+ memset(&sm->PTK, 0, sizeof(sm->PTK));
+
+ if (event != WPA_REAUTH_EAPOL) {
+ sm->pairwise_set = FALSE;
+ hostapd_set_encryption(sm->hapd, "none", sm->sta->addr, 0,
+ (u8 *) "", 0);
+ }
+
+ wpa_sm_step(sm);
+}
+
+
+static const char * wpa_alg_txt(int alg)
+{
+ switch (alg) {
+ case WPA_CIPHER_CCMP:
+ return "CCMP";
+ case WPA_CIPHER_TKIP:
+ return "TKIP";
+ case WPA_CIPHER_WEP104:
+ case WPA_CIPHER_WEP40:
+ return "WEP";
+ default:
+ return "";
+ }
+}
+
+
+/* Definitions for clarifying state machine implementation */
+#define SM_STATE(machine, state) \
+static void sm_ ## machine ## _ ## state ## _Enter(struct wpa_state_machine \
+*sm)
+
+#define SM_ENTRY(machine, _state, _data) \
+sm->changed = TRUE; \
+sm->_data ## _ ## state = machine ## _ ## _state; \
+if (sm->hapd->conf->debug >= HOSTAPD_DEBUG_MINIMAL) \
+ printf("WPA: " MACSTR " " #machine " entering state " #_state \
+ "\n", MAC2STR(sm->sta->addr));
+
+#define SM_ENTER(machine, state) sm_ ## machine ## _ ## state ## _Enter(sm)
+
+#define SM_STEP(machine) \
+static void sm_ ## machine ## _Step(struct wpa_state_machine *sm)
+
+#define SM_STEP_RUN(machine) sm_ ## machine ## _Step(sm)
+
+
+SM_STATE(WPA_PTK, INITIALIZE)
+{
+ struct hostapd_data *hapd = sm->hapd;
+
+ SM_ENTRY(WPA_PTK, INITIALIZE, wpa_ptk);
+ if (sm->Init) {
+ /* Init flag is not cleared here, so avoid busy
+ * loop by claiming nothing changed. */
+ sm->changed = FALSE;
+ }
+
+ sm->keycount = 0;
+ if (sm->GUpdateStationKeys)
+ hapd->wpa_auth->GKeyDoneStations--;
+ sm->GUpdateStationKeys = FALSE;
+ if (sm->sta->wpa == WPA_VERSION_WPA)
+ sm->PInitAKeys = FALSE;
+ if (1 /* Unicast cipher supported AND (ESS OR ((IBSS or WDS) and
+ * Local AA > Remote AA)) */) {
+ sm->Pair = TRUE;
+ }
+ ieee802_1x_notify_port_enabled(sm->sta->eapol_sm, 0);
+ hostapd_set_encryption(sm->hapd, "none", sm->sta->addr, 0, (u8 *) "",
+ 0);
+ sm->pairwise_set = FALSE;
+ sm->PTK_valid = FALSE;
+ memset(&sm->PTK, 0, sizeof(sm->PTK));
+ ieee802_1x_notify_port_valid(sm->sta->eapol_sm, 0);
+ sm->TimeoutCtr = 0;
+ if (sm->sta->wpa_key_mgmt == WPA_KEY_MGMT_PSK)
+ ieee802_1x_set_sta_authorized(sm->hapd, sm->sta, 0);
+}
+
+
+SM_STATE(WPA_PTK, DISCONNECT)
+{
+ SM_ENTRY(WPA_PTK, DISCONNECT, wpa_ptk);
+ sm->Disconnect = FALSE;
+ wpa_sta_disconnect(sm->hapd, sm->sta);
+}
+
+
+SM_STATE(WPA_PTK, DISCONNECTED)
+{
+ SM_ENTRY(WPA_PTK, DISCONNECTED, wpa_ptk);
+ sm->hapd->wpa_auth->GNoStations--;
+ sm->DeauthenticationRequest = FALSE;
+}
+
+
+SM_STATE(WPA_PTK, AUTHENTICATION)
+{
+ SM_ENTRY(WPA_PTK, AUTHENTICATION, wpa_ptk);
+ sm->hapd->wpa_auth->GNoStations++;
+ memset(&sm->PTK, 0, sizeof(sm->PTK));
+ sm->PTK_valid = FALSE;
+ if (sm->sta->eapol_sm) {
+ sm->sta->eapol_sm->portControl = Auto;
+ sm->sta->eapol_sm->portEnabled = TRUE;
+ }
+ sm->AuthenticationRequest = FALSE;
+}
+
+
+SM_STATE(WPA_PTK, AUTHENTICATION2)
+{
+ SM_ENTRY(WPA_PTK, AUTHENTICATION2, wpa_ptk);
+ memcpy(sm->ANonce, sm->hapd->wpa_auth->Counter, WPA_NONCE_LEN);
+ inc_byte_array(sm->hapd->wpa_auth->Counter, WPA_NONCE_LEN);
+ sm->ReAuthenticationRequest = FALSE;
+ /* IEEE 802.11i/D9.0 does not clear TimeoutCtr here, but this is more
+ * logical place than INITIALIZE since AUTHENTICATION2 can be
+ * re-entered on ReAuthenticationRequest without going through
+ * INITIALIZE. */
+ sm->TimeoutCtr = 0;
+}
+
+
+SM_STATE(WPA_PTK, INITPMK)
+{
+ u8 *key;
+ size_t len;
+ SM_ENTRY(WPA_PTK, INITPMK, wpa_ptk);
+ if (sm->sta->pmksa) {
+ wpa_printf(MSG_DEBUG, "WPA: PMK from PMKSA cache");
+ memcpy(sm->PMK, sm->sta->pmksa->pmk, WPA_PMK_LEN);
+ } else if ((key = ieee802_1x_get_key_crypt(sm->sta->eapol_sm, &len))) {
+ wpa_printf(MSG_DEBUG, "WPA: PMK from EAPOL state machine "
+ "(len=%lu)", (unsigned long) len);
+ if (len > WPA_PMK_LEN)
+ len = WPA_PMK_LEN;
+ memcpy(sm->PMK, key, len);
+ } else {
+ wpa_printf(MSG_DEBUG, "WPA: Could not get PMK");
+ }
+ sm->sta->req_replay_counter_used = 0;
+ /* IEEE 802.11i/D9.0 does not set keyRun to FALSE, but not doing this
+ * will break reauthentication since EAPOL state machines may not be
+ * get into AUTHENTICATING state that clears keyRun before WPA state
+ * machine enters AUTHENTICATION2 state and goes immediately to INITPMK
+ * state and takes PMK from the previously used AAA Key. This will
+ * eventually fail in 4-Way Handshake because Supplicant uses PMK
+ * derived from the new AAA Key. Setting keyRun = FALSE here seems to
+ * be good workaround for this issue. */
+ if (sm->sta->eapol_sm)
+ sm->sta->eapol_sm->keyRun = FALSE;
+}
+
+
+SM_STATE(WPA_PTK, INITPSK)
+{
+ const u8 *psk;
+ SM_ENTRY(WPA_PTK, INITPSK, wpa_ptk);
+ psk = hostapd_get_psk(sm->hapd->conf, sm->sta->addr, NULL);
+ if (psk)
+ memcpy(sm->PMK, psk, WPA_PMK_LEN);
+ sm->sta->req_replay_counter_used = 0;
+}
+
+
+SM_STATE(WPA_PTK, PTKSTART)
+{
+ u8 *pmkid = NULL;
+ size_t pmkid_len = 0;
+
+ SM_ENTRY(WPA_PTK, PTKSTART, wpa_ptk);
+ sm->PTKRequest = FALSE;
+ sm->TimeoutEvt = FALSE;
+ hostapd_logger(sm->hapd, sm->sta->addr, HOSTAPD_MODULE_WPA,
+ HOSTAPD_LEVEL_DEBUG,
+ "sending 1/4 msg of 4-Way Handshake");
+ if (sm->sta->pmksa &&
+ (pmkid = malloc(2 + RSN_SELECTOR_LEN + PMKID_LEN))) {
+ pmkid_len = 2 + RSN_SELECTOR_LEN + PMKID_LEN;
+ pmkid[0] = WLAN_EID_GENERIC;
+ pmkid[1] = RSN_SELECTOR_LEN + PMKID_LEN;
+ memcpy(&pmkid[2], RSN_KEY_DATA_PMKID, RSN_SELECTOR_LEN);
+ memcpy(&pmkid[2 + RSN_SELECTOR_LEN], sm->sta->pmksa->pmkid,
+ PMKID_LEN);
+ }
+ wpa_send_eapol(sm->hapd, sm->sta, 0, 0, 1, 0, 1, NULL, sm->ANonce,
+ pmkid, pmkid_len, NULL, 0, 0);
+ free(pmkid);
+ sm->TimeoutCtr++;
+}
+
+
+SM_STATE(WPA_PTK, PTKCALCNEGOTIATING)
+{
+ struct wpa_ptk PTK;
+ int ok = 0;
+ const u8 *pmk = NULL;
+
+ SM_ENTRY(WPA_PTK, PTKCALCNEGOTIATING, wpa_ptk);
+ sm->EAPOLKeyReceived = FALSE;
+
+ /* WPA with IEEE 802.1X: use the derived PMK from EAP
+ * WPA-PSK: iterate through possible PSKs and select the one matching
+ * the packet */
+ for (;;) {
+ if (sm->sta->wpa_key_mgmt == WPA_KEY_MGMT_PSK) {
+ pmk = hostapd_get_psk(sm->hapd->conf, sm->sta->addr,
+ pmk);
+ if (pmk == NULL)
+ break;
+ } else
+ pmk = sm->PMK;
+
+ wpa_pmk_to_ptk(sm->hapd, pmk, sm->hapd->own_addr,
+ sm->sta->addr, sm->ANonce, sm->SNonce,
+ (u8 *) &PTK, sizeof(PTK));
+
+ if (wpa_verify_key_mic(&PTK, sm->last_rx_eapol_key,
+ sm->last_rx_eapol_key_len) == 0) {
+ ok = 1;
+ break;
+ }
+
+ if (sm->sta->wpa_key_mgmt != WPA_KEY_MGMT_PSK)
+ break;
+ }
+
+ if (!ok) {
+ hostapd_logger(sm->hapd, sm->sta->addr, HOSTAPD_MODULE_WPA,
+ HOSTAPD_LEVEL_DEBUG, "invalid MIC in msg 2/4 "
+ "of 4-Way Handshake");
+ return;
+ }
+
+ eloop_cancel_timeout(wpa_send_eapol_timeout, sm->hapd, sm->sta);
+
+ if (sm->sta->wpa_key_mgmt == WPA_KEY_MGMT_PSK) {
+ /* PSK may have changed from the previous choice, so update
+ * state machine data based on whatever PSK was selected here.
+ */
+ memcpy(sm->PMK, pmk, WPA_PMK_LEN);
+ }
+
+ sm->MICVerified = TRUE;
+
+ memcpy(&sm->PTK, &PTK, sizeof(PTK));
+ sm->PTK_valid = TRUE;
+}
+
+
+SM_STATE(WPA_PTK, PTKCALCNEGOTIATING2)
+{
+ SM_ENTRY(WPA_PTK, PTKCALCNEGOTIATING2, wpa_ptk);
+ sm->TimeoutCtr = 0;
+}
+
+
+SM_STATE(WPA_PTK, PTKINITNEGOTIATING)
+{
+ u8 rsc[WPA_KEY_RSC_LEN];
+ struct wpa_authenticator *gsm = sm->hapd->wpa_auth;
+ u8 *wpa_ie;
+ int wpa_ie_len;
+
+ SM_ENTRY(WPA_PTK, PTKINITNEGOTIATING, wpa_ptk);
+ sm->TimeoutEvt = FALSE;
+ /* Send EAPOL(1, 1, 1, Pair, P, RSC, ANonce, MIC(PTK), RSNIE, GTK[GN])
+ */
+ memset(rsc, 0, WPA_KEY_RSC_LEN);
+ hostapd_get_seqnum(sm->hapd, NULL, gsm->GN, rsc);
+ wpa_ie = sm->hapd->wpa_ie;
+ wpa_ie_len = sm->hapd->wpa_ie_len;
+ if (sm->sta->wpa == WPA_VERSION_WPA &&
+ (sm->hapd->conf->wpa & HOSTAPD_WPA_VERSION_WPA2) &&
+ wpa_ie_len > wpa_ie[1] + 2 && wpa_ie[0] == WLAN_EID_RSN) {
+ /* WPA-only STA, remove RSN IE */
+ wpa_ie = wpa_ie + wpa_ie[1] + 2;
+ wpa_ie_len = wpa_ie[1] + 2;
+ }
+ hostapd_logger(sm->hapd, sm->sta->addr, HOSTAPD_MODULE_WPA,
+ HOSTAPD_LEVEL_DEBUG,
+ "sending 3/4 msg of 4-Way Handshake");
+ wpa_send_eapol(sm->hapd, sm->sta,
+ sm->sta->wpa == WPA_VERSION_WPA2 ? 1 : 0,
+ 1, 1, 1, 1, rsc, sm->ANonce,
+ wpa_ie, wpa_ie_len,
+ gsm->GTK[gsm->GN - 1], gsm->GTK_len, gsm->GN);
+ sm->TimeoutCtr++;
+}
+
+
+SM_STATE(WPA_PTK, PTKINITDONE)
+{
+ SM_ENTRY(WPA_PTK, PTKINITDONE, wpa_ptk);
+ sm->EAPOLKeyReceived = FALSE;
+ if (sm->Pair) {
+ char *alg;
+ int klen;
+ if (sm->sta->pairwise == WPA_CIPHER_TKIP) {
+ alg = "TKIP";
+ klen = 32;
+ } else {
+ alg = "CCMP";
+ klen = 16;
+ }
+ if (hostapd_set_encryption(sm->hapd, alg, sm->sta->addr, 0,
+ sm->PTK.tk1, klen)) {
+ wpa_sta_disconnect(sm->hapd, sm->sta);
+ return;
+ }
+ /* FIX: MLME-SetProtection.Request(TA, Tx_Rx) */
+ sm->pairwise_set = TRUE;
+
+ if (sm->sta->wpa_key_mgmt == WPA_KEY_MGMT_PSK)
+ ieee802_1x_set_sta_authorized(sm->hapd, sm->sta, 1);
+ }
+
+ if (0 /* IBSS == TRUE */) {
+ sm->keycount++;
+ if (sm->keycount == 2) {
+ ieee802_1x_notify_port_valid(sm->sta->eapol_sm, 1);
+ }
+ } else {
+ ieee802_1x_notify_port_valid(sm->sta->eapol_sm, 1);
+ }
+ if (sm->sta->eapol_sm) {
+ sm->sta->eapol_sm->keyAvailable = FALSE;
+ sm->sta->eapol_sm->keyDone = TRUE;
+ }
+ if (sm->sta->wpa == WPA_VERSION_WPA)
+ sm->PInitAKeys = TRUE;
+ else
+ sm->has_GTK = TRUE;
+ hostapd_logger(sm->hapd, sm->sta->addr, HOSTAPD_MODULE_WPA,
+ HOSTAPD_LEVEL_INFO, "pairwise key handshake completed "
+ "(%s)",
+ sm->sta->wpa == WPA_VERSION_WPA ? "WPA" : "RSN");
+}
+
+
+SM_STEP(WPA_PTK)
+{
+ struct wpa_authenticator *wpa_auth = sm->hapd->wpa_auth;
+
+ if (sm->Init)
+ SM_ENTER(WPA_PTK, INITIALIZE);
+ else if (sm->Disconnect
+ /* || FIX: dot11RSNAConfigSALifetime timeout */)
+ SM_ENTER(WPA_PTK, DISCONNECT);
+ else if (sm->DeauthenticationRequest)
+ SM_ENTER(WPA_PTK, DISCONNECTED);
+ else if (sm->AuthenticationRequest)
+ SM_ENTER(WPA_PTK, AUTHENTICATION);
+ else if (sm->ReAuthenticationRequest)
+ SM_ENTER(WPA_PTK, AUTHENTICATION2);
+ else if (sm->PTKRequest)
+ SM_ENTER(WPA_PTK, PTKSTART);
+ else switch (sm->wpa_ptk_state) {
+ case WPA_PTK_INITIALIZE:
+ break;
+ case WPA_PTK_DISCONNECT:
+ SM_ENTER(WPA_PTK, DISCONNECTED);
+ break;
+ case WPA_PTK_DISCONNECTED:
+ SM_ENTER(WPA_PTK, INITIALIZE);
+ break;
+ case WPA_PTK_AUTHENTICATION:
+ SM_ENTER(WPA_PTK, AUTHENTICATION2);
+ break;
+ case WPA_PTK_AUTHENTICATION2:
+ if ((sm->sta->wpa_key_mgmt == WPA_KEY_MGMT_IEEE8021X) &&
+ sm->sta->eapol_sm && sm->sta->eapol_sm->keyRun)
+ SM_ENTER(WPA_PTK, INITPMK);
+ else if ((sm->sta->wpa_key_mgmt == WPA_KEY_MGMT_PSK)
+ /* FIX: && 802.1X::keyRun */)
+ SM_ENTER(WPA_PTK, INITPSK);
+ break;
+ case WPA_PTK_INITPMK:
+ if (sm->sta->eapol_sm && sm->sta->eapol_sm->keyAvailable)
+ SM_ENTER(WPA_PTK, PTKSTART);
+ else {
+ wpa_auth->dot11RSNA4WayHandshakeFailures++;
+ SM_ENTER(WPA_PTK, DISCONNECT);
+ }
+ break;
+ case WPA_PTK_INITPSK:
+ if (hostapd_get_psk(sm->hapd->conf, sm->sta->addr, NULL))
+ SM_ENTER(WPA_PTK, PTKSTART);
+ else {
+ hostapd_logger(sm->hapd, sm->sta->addr,
+ HOSTAPD_MODULE_WPA,
+ HOSTAPD_LEVEL_INFO,
+ "no PSK configured for the STA");
+ wpa_auth->dot11RSNA4WayHandshakeFailures++;
+ SM_ENTER(WPA_PTK, DISCONNECT);
+ }
+ break;
+ case WPA_PTK_PTKSTART:
+ if (sm->EAPOLKeyReceived && !sm->EAPOLKeyRequest &&
+ sm->EAPOLKeyPairwise)
+ SM_ENTER(WPA_PTK, PTKCALCNEGOTIATING);
+ else if (sm->TimeoutCtr > dot11RSNAConfigPairwiseUpdateCount) {
+ wpa_auth->dot11RSNA4WayHandshakeFailures++;
+ SM_ENTER(WPA_PTK, DISCONNECT);
+ } else if (sm->TimeoutEvt)
+ SM_ENTER(WPA_PTK, PTKSTART);
+ break;
+ case WPA_PTK_PTKCALCNEGOTIATING:
+ if (sm->MICVerified)
+ SM_ENTER(WPA_PTK, PTKCALCNEGOTIATING2);
+ else if (sm->EAPOLKeyReceived && !sm->EAPOLKeyRequest &&
+ sm->EAPOLKeyPairwise)
+ SM_ENTER(WPA_PTK, PTKCALCNEGOTIATING);
+ else if (sm->TimeoutEvt)
+ SM_ENTER(WPA_PTK, PTKSTART);
+ break;
+ case WPA_PTK_PTKCALCNEGOTIATING2:
+ SM_ENTER(WPA_PTK, PTKINITNEGOTIATING);
+ break;
+ case WPA_PTK_PTKINITNEGOTIATING:
+ if (sm->EAPOLKeyReceived && !sm->EAPOLKeyRequest &&
+ sm->EAPOLKeyPairwise && sm->MICVerified)
+ SM_ENTER(WPA_PTK, PTKINITDONE);
+ else if (sm->TimeoutCtr > dot11RSNAConfigPairwiseUpdateCount) {
+ wpa_auth->dot11RSNA4WayHandshakeFailures++;
+ SM_ENTER(WPA_PTK, DISCONNECT);
+ } else if (sm->TimeoutEvt)
+ SM_ENTER(WPA_PTK, PTKINITNEGOTIATING);
+ break;
+ case WPA_PTK_PTKINITDONE:
+ break;
+ }
+}
+
+
+SM_STATE(WPA_PTK_GROUP, IDLE)
+{
+ SM_ENTRY(WPA_PTK_GROUP, IDLE, wpa_ptk_group);
+ if (sm->Init) {
+ /* Init flag is not cleared here, so avoid busy
+ * loop by claiming nothing changed. */
+ sm->changed = FALSE;
+ }
+ sm->GTimeoutCtr = 0;
+}
+
+
+SM_STATE(WPA_PTK_GROUP, REKEYNEGOTIATING)
+{
+ u8 rsc[WPA_KEY_RSC_LEN];
+ struct wpa_authenticator *gsm = sm->hapd->wpa_auth;
+
+ SM_ENTRY(WPA_PTK_GROUP, REKEYNEGOTIATING, wpa_ptk_group);
+ if (sm->sta->wpa == WPA_VERSION_WPA)
+ sm->PInitAKeys = FALSE;
+ sm->TimeoutEvt = FALSE;
+ /* Send EAPOL(1, 1, 1, !Pair, G, RSC, GNonce, MIC(PTK), GTK[GN]) */
+ memset(rsc, 0, WPA_KEY_RSC_LEN);
+ if (gsm->wpa_group_state == WPA_GROUP_SETKEYSDONE)
+ hostapd_get_seqnum(sm->hapd, NULL, gsm->GN, rsc);
+ hostapd_logger(sm->hapd, sm->sta->addr, HOSTAPD_MODULE_WPA,
+ HOSTAPD_LEVEL_DEBUG,
+ "sending 1/2 msg of Group Key Handshake");
+ wpa_send_eapol(sm->hapd, sm->sta, 1, 1, 1, !sm->Pair, 0, rsc,
+ gsm->GNonce, NULL, 0,
+ gsm->GTK[gsm->GN - 1], gsm->GTK_len, gsm->GN);
+ sm->GTimeoutCtr++;
+}
+
+
+SM_STATE(WPA_PTK_GROUP, REKEYESTABLISHED)
+{
+ SM_ENTRY(WPA_PTK_GROUP, REKEYESTABLISHED, wpa_ptk_group);
+ sm->EAPOLKeyReceived = FALSE;
+ sm->GUpdateStationKeys = FALSE;
+ sm->hapd->wpa_auth->GKeyDoneStations--;
+ sm->GTimeoutCtr = 0;
+ /* FIX: MLME.SetProtection.Request(TA, Tx_Rx) */
+ hostapd_logger(sm->hapd, sm->sta->addr, HOSTAPD_MODULE_WPA,
+ HOSTAPD_LEVEL_INFO, "group key handshake completed "
+ "(%s)",
+ sm->sta->wpa == WPA_VERSION_WPA ? "WPA" : "RSN");
+ sm->has_GTK = TRUE;
+}
+
+
+SM_STATE(WPA_PTK_GROUP, KEYERROR)
+{
+ SM_ENTRY(WPA_PTK_GROUP, KEYERROR, wpa_ptk_group);
+ sm->hapd->wpa_auth->GKeyDoneStations--;
+ sm->GUpdateStationKeys = FALSE;
+ sm->Disconnect = TRUE;
+}
+
+
+SM_STEP(WPA_PTK_GROUP)
+{
+ if (sm->Init)
+ SM_ENTER(WPA_PTK_GROUP, IDLE);
+ else switch (sm->wpa_ptk_group_state) {
+ case WPA_PTK_GROUP_IDLE:
+ if (sm->GUpdateStationKeys ||
+ (sm->sta->wpa == WPA_VERSION_WPA && sm->PInitAKeys))
+ SM_ENTER(WPA_PTK_GROUP, REKEYNEGOTIATING);
+ break;
+ case WPA_PTK_GROUP_REKEYNEGOTIATING:
+ if (sm->EAPOLKeyReceived && !sm->EAPOLKeyRequest &&
+ !sm->EAPOLKeyPairwise && sm->MICVerified)
+ SM_ENTER(WPA_PTK_GROUP, REKEYESTABLISHED);
+ else if (sm->GTimeoutCtr > dot11RSNAConfigGroupUpdateCount)
+ SM_ENTER(WPA_PTK_GROUP, KEYERROR);
+ else if (sm->TimeoutEvt)
+ SM_ENTER(WPA_PTK_GROUP, REKEYNEGOTIATING);
+ break;
+ case WPA_PTK_GROUP_KEYERROR:
+ SM_ENTER(WPA_PTK_GROUP, IDLE);
+ break;
+ case WPA_PTK_GROUP_REKEYESTABLISHED:
+ SM_ENTER(WPA_PTK_GROUP, IDLE);
+ break;
+ }
+}
+
+
+static void wpa_group_gtk_init(struct hostapd_data *hapd)
+{
+ struct wpa_authenticator *sm = hapd->wpa_auth;
+ HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "WPA: group state machine "
+ "entering state GTK_INIT\n");
+ sm->changed = FALSE; /* GInit is not cleared here; avoid loop */
+ sm->wpa_group_state = WPA_GROUP_GTK_INIT;
+
+ /* GTK[0..N] = 0 */
+ memset(sm->GTK, 0, sizeof(sm->GTK));
+ sm->GN = 1;
+ sm->GM = 2;
+ /* GTK[GN] = CalcGTK() */
+ /* FIX: is this the correct way of getting GNonce? */
+ memcpy(sm->GNonce, sm->Counter, WPA_NONCE_LEN);
+ inc_byte_array(sm->Counter, WPA_NONCE_LEN);
+ wpa_gmk_to_gtk(hapd, sm->GMK, hapd->own_addr, sm->GNonce,
+ sm->GTK[sm->GN - 1], sm->GTK_len);
+}
+
+
+static void wpa_group_setkeys(struct hostapd_data *hapd)
+{
+ struct wpa_authenticator *sm = hapd->wpa_auth;
+ struct sta_info *sta;
+ int tmp;
+
+ HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "WPA: group state machine "
+ "entering state SETKEYS\n");
+ sm->changed = TRUE;
+ sm->wpa_group_state = WPA_GROUP_SETKEYS;
+ sm->GTKReKey = FALSE;
+ tmp = sm->GM;
+ sm->GM = sm->GN;
+ sm->GN = tmp;
+ sm->GKeyDoneStations = sm->GNoStations;
+ /* FIX: is this the correct way of getting GNonce? */
+ memcpy(sm->GNonce, sm->Counter, WPA_NONCE_LEN);
+ inc_byte_array(sm->Counter, WPA_NONCE_LEN);
+ wpa_gmk_to_gtk(hapd, sm->GMK, hapd->own_addr, sm->GNonce,
+ sm->GTK[sm->GN - 1], sm->GTK_len);
+
+ sta = hapd->sta_list;
+ while (sta) {
+ if (sta->wpa_sm) {
+ sta->wpa_sm->GUpdateStationKeys = TRUE;
+ wpa_sm_step(sta->wpa_sm);
+ }
+ sta = sta->next;
+ }
+}
+
+
+static void wpa_group_setkeysdone(struct hostapd_data *hapd)
+{
+ struct wpa_authenticator *sm = hapd->wpa_auth;
+
+ HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "WPA: group state machine "
+ "entering state SETKEYSDONE\n");
+ sm->changed = TRUE;
+ sm->wpa_group_state = WPA_GROUP_SETKEYSDONE;
+ hostapd_set_encryption(hapd, wpa_alg_txt(hapd->conf->wpa_group),
+ NULL, sm->GN, sm->GTK[sm->GN - 1], sm->GTK_len);
+}
+
+
+static void wpa_group_sm_step(struct hostapd_data *hapd)
+{
+ struct wpa_authenticator *sm = hapd->wpa_auth;
+
+ if (sm->GInit) {
+ wpa_group_gtk_init(hapd);
+ } else if (sm->wpa_group_state == WPA_GROUP_GTK_INIT &&
+ sm->GTKAuthenticator) {
+ wpa_group_setkeysdone(hapd);
+ } else if (sm->wpa_group_state == WPA_GROUP_SETKEYSDONE &&
+ sm->GTKReKey) {
+ wpa_group_setkeys(hapd);
+ } else if (sm->wpa_group_state == WPA_GROUP_SETKEYS) {
+ if (sm->GKeyDoneStations == 0)
+ wpa_group_setkeysdone(hapd);
+ else if (sm->GTKReKey)
+ wpa_group_setkeys(hapd);
+ }
+}
+
+
+static int wpa_sm_sta_entry_alive(struct hostapd_data *hapd, u8 *addr)
+{
+ struct sta_info *sta;
+ sta = ap_get_sta(hapd, addr);
+ if (sta == NULL || sta->wpa_sm == NULL)
+ return 0;
+ return 1;
+}
+
+
+static void wpa_sm_step(struct wpa_state_machine *sm)
+{
+ struct hostapd_data *hapd = sm->hapd;
+ u8 addr[6];
+ if (sm == NULL || sm->sta == NULL || sm->sta->wpa_sm == NULL)
+ return;
+
+ memcpy(addr, sm->sta->addr, 6);
+ do {
+ sm->changed = FALSE;
+ sm->hapd->wpa_auth->changed = FALSE;
+
+ SM_STEP_RUN(WPA_PTK);
+ if (!wpa_sm_sta_entry_alive(hapd, addr))
+ break;
+ SM_STEP_RUN(WPA_PTK_GROUP);
+ if (!wpa_sm_sta_entry_alive(hapd, addr))
+ break;
+ wpa_group_sm_step(sm->hapd);
+ if (!wpa_sm_sta_entry_alive(hapd, addr))
+ break;
+ } while (sm->changed || sm->hapd->wpa_auth->changed);
+}
+
+
+static void wpa_sm_call_step(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_state_machine *sm = timeout_ctx;
+ wpa_sm_step(sm);
+}
+
+
+void wpa_sm_notify(struct hostapd_data *hapd, struct sta_info *sta)
+{
+ if (sta->wpa_sm == NULL)
+ return;
+ eloop_register_timeout(0, 0, wpa_sm_call_step, hapd, sta->wpa_sm);
+}
+
+
+void wpa_gtk_rekey(struct hostapd_data *hapd)
+{
+ struct wpa_authenticator *sm = hapd->wpa_auth;
+ int tmp, i;
+
+ if (sm == NULL)
+ return;
+
+ for (i = 0; i < 2; i++) {
+ tmp = sm->GM;
+ sm->GM = sm->GN;
+ sm->GN = tmp;
+ memcpy(sm->GNonce, sm->Counter, WPA_NONCE_LEN);
+ inc_byte_array(sm->Counter, WPA_NONCE_LEN);
+ wpa_gmk_to_gtk(hapd, sm->GMK, hapd->own_addr, sm->GNonce,
+ sm->GTK[sm->GN - 1], sm->GTK_len);
+ }
+}
+
+
+static const char * wpa_bool_txt(int bool)
+{
+ return bool ? "TRUE" : "FALSE";
+}
+
+
+static int wpa_cipher_bits(int cipher)
+{
+ switch (cipher) {
+ case WPA_CIPHER_CCMP:
+ return 128;
+ case WPA_CIPHER_TKIP:
+ return 256;
+ case WPA_CIPHER_WEP104:
+ return 104;
+ case WPA_CIPHER_WEP40:
+ return 40;
+ default:
+ return 0;
+ }
+}
+
+
+#define RSN_SUITE "%02x-%02x-%02x-%d"
+#define RSN_SUITE_ARG(s) (s)[0], (s)[1], (s)[2], (s)[3]
+
+int wpa_get_mib(struct hostapd_data *hapd, char *buf, size_t buflen)
+{
+ int len = 0, i;
+ char pmkid_txt[PMKID_LEN * 2 + 1], *pos;
+
+ len += snprintf(buf + len, buflen - len,
+ "dot11RSNAOptionImplemented=TRUE\n"
+ "dot11RSNAPreauthenticationImplemented=TRUE\n"
+ "dot11RSNAEnabled=%s\n"
+ "dot11RSNAPreauthenticationEnabled=%s\n",
+ wpa_bool_txt(hapd->conf->wpa &
+ HOSTAPD_WPA_VERSION_WPA2),
+ wpa_bool_txt(hapd->conf->rsn_preauth));
+
+ if (hapd->wpa_auth == NULL)
+ return len;
+
+ pos = pmkid_txt;
+ for (i = 0; i < PMKID_LEN; i++) {
+ pos += sprintf(pos, "%02x",
+ hapd->wpa_auth->dot11RSNAPMKIDUsed[i]);
+ }
+
+ len += snprintf(buf + len, buflen - len,
+ "dot11RSNAConfigVersion=%u\n"
+ "dot11RSNAConfigPairwiseKeysSupported=9999\n"
+ /* FIX: dot11RSNAConfigGroupCipher */
+ /* FIX: dot11RSNAConfigGroupRekeyMethod */
+ /* FIX: dot11RSNAConfigGroupRekeyTime */
+ /* FIX: dot11RSNAConfigGroupRekeyPackets */
+ "dot11RSNAConfigGroupRekeyStrict=%u\n"
+ "dot11RSNAConfigGroupUpdateCount=%u\n"
+ "dot11RSNAConfigPairwiseUpdateCount=%u\n"
+ "dot11RSNAConfigGroupCipherSize=%u\n"
+ "dot11RSNAConfigPMKLifetime=%u\n"
+ "dot11RSNAConfigPMKReauthThreshold=%u\n"
+ "dot11RSNAConfigNumberOfPTKSAReplayCounters=0\n"
+ "dot11RSNAConfigSATimeout=%u\n"
+ "dot11RSNAAuthenticationSuiteSelected=" RSN_SUITE "\n"
+ "dot11RSNAPairwiseCipherSelected=" RSN_SUITE "\n"
+ "dot11RSNAGroupCipherSelected=" RSN_SUITE "\n"
+ "dot11RSNAPMKIDUsed=%s\n"
+ "dot11RSNAAuthenticationSuiteRequested=" RSN_SUITE "\n"
+ "dot11RSNAPairwiseCipherRequested=" RSN_SUITE "\n"
+ "dot11RSNAGroupCipherRequested=" RSN_SUITE "\n"
+ "dot11RSNATKIPCounterMeasuresInvoked=%u\n"
+ "dot11RSNA4WayHandshakeFailures=%u\n"
+ "dot11RSNAConfigNumberOfGTKSAReplayCounters=0\n",
+ RSN_VERSION,
+ !!hapd->conf->wpa_strict_rekey,
+ dot11RSNAConfigGroupUpdateCount,
+ dot11RSNAConfigPairwiseUpdateCount,
+ wpa_cipher_bits(hapd->conf->wpa_group),
+ dot11RSNAConfigPMKLifetime,
+ dot11RSNAConfigPMKReauthThreshold,
+ dot11RSNAConfigSATimeout,
+ RSN_SUITE_ARG(hapd->wpa_auth->
+ dot11RSNAAuthenticationSuiteSelected),
+ RSN_SUITE_ARG(hapd->wpa_auth->
+ dot11RSNAPairwiseCipherSelected),
+ RSN_SUITE_ARG(hapd->wpa_auth->
+ dot11RSNAGroupCipherSelected),
+ pmkid_txt,
+ RSN_SUITE_ARG(hapd->wpa_auth->
+ dot11RSNAAuthenticationSuiteRequested),
+ RSN_SUITE_ARG(hapd->wpa_auth->
+ dot11RSNAPairwiseCipherRequested),
+ RSN_SUITE_ARG(hapd->wpa_auth->
+ dot11RSNAGroupCipherRequested),
+ hapd->wpa_auth->dot11RSNATKIPCounterMeasuresInvoked,
+ hapd->wpa_auth->dot11RSNA4WayHandshakeFailures);
+
+ /* TODO: dot11RSNAConfigPairwiseCiphersTable */
+ /* TODO: dot11RSNAConfigAuthenticationSuitesTable */
+
+ /* Private MIB */
+ len += snprintf(buf + len, buflen - len,
+ "hostapdWPAGroupState=%d\n",
+ hapd->wpa_auth->wpa_group_state);
+
+ return len;
+}
+
+
+int wpa_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta,
+ char *buf, size_t buflen)
+{
+ int len = 0;
+ u8 not_used[4] = { 0, 0, 0, 0 };
+ const u8 *pairwise = not_used;
+
+ /* TODO: FF-FF-FF-FF-FF-FF entry for broadcast/multicast stats */
+
+ /* dot11RSNAStatsEntry */
+
+ if (sta->wpa == WPA_VERSION_WPA) {
+ if (sta->pairwise == WPA_CIPHER_CCMP)
+ pairwise = WPA_CIPHER_SUITE_CCMP;
+ else if (sta->pairwise == WPA_CIPHER_TKIP)
+ pairwise = WPA_CIPHER_SUITE_TKIP;
+ else if (sta->pairwise == WPA_CIPHER_WEP104)
+ pairwise = WPA_CIPHER_SUITE_WEP104;
+ else if (sta->pairwise == WPA_CIPHER_WEP40)
+ pairwise = WPA_CIPHER_SUITE_WEP40;
+ else if (sta->pairwise == WPA_CIPHER_NONE)
+ pairwise = WPA_CIPHER_SUITE_NONE;
+ } else if (sta->wpa == WPA_VERSION_WPA2) {
+ if (sta->pairwise == WPA_CIPHER_CCMP)
+ pairwise = RSN_CIPHER_SUITE_CCMP;
+ else if (sta->pairwise == WPA_CIPHER_TKIP)
+ pairwise = RSN_CIPHER_SUITE_TKIP;
+ else if (sta->pairwise == WPA_CIPHER_WEP104)
+ pairwise = RSN_CIPHER_SUITE_WEP104;
+ else if (sta->pairwise == WPA_CIPHER_WEP40)
+ pairwise = RSN_CIPHER_SUITE_WEP40;
+ else if (sta->pairwise == WPA_CIPHER_NONE)
+ pairwise = RSN_CIPHER_SUITE_NONE;
+ } else
+ return 0;
+
+ len += snprintf(buf + len, buflen - len,
+ /* TODO: dot11RSNAStatsIndex */
+ "dot11RSNAStatsSTAAddress=" MACSTR "\n"
+ "dot11RSNAStatsVersion=1\n"
+ "dot11RSNAStatsSelectedPairwiseCipher=" RSN_SUITE "\n"
+ /* TODO: dot11RSNAStatsTKIPICVErrors */
+ "dot11RSNAStatsTKIPLocalMICFailures=%u\n"
+ "dot11RSNAStatsTKIPRemoveMICFailures=%u\n"
+ /* TODO: dot11RSNAStatsCCMPReplays */
+ /* TODO: dot11RSNAStatsCCMPDecryptErrors */
+ /* TODO: dot11RSNAStatsTKIPReplays */,
+ MAC2STR(sta->addr),
+ RSN_SUITE_ARG(pairwise),
+ sta->dot11RSNAStatsTKIPLocalMICFailures,
+ sta->dot11RSNAStatsTKIPRemoteMICFailures);
+
+ if (sta->wpa_sm == NULL)
+ return len;
+
+ /* Private MIB */
+ len += snprintf(buf + len, buflen - len,
+ "hostapdWPAPTKState=%d\n"
+ "hostapdWPAPTKGroupState=%d\n",
+ sta->wpa_sm->wpa_ptk_state,
+ sta->wpa_sm->wpa_ptk_group_state);
+
+ return len;
+}
diff --git a/contrib/hostapd/wpa.h b/contrib/hostapd/wpa.h
new file mode 100644
index 000000000000..929e4a810030
--- /dev/null
+++ b/contrib/hostapd/wpa.h
@@ -0,0 +1,193 @@
+#ifndef WPA_H
+#define WPA_H
+
+#define WPA_NONCE_LEN 32
+#define WPA_PMK_LEN PMK_LEN
+#define WPA_REPLAY_COUNTER_LEN 8
+#define WPA_GMK_LEN 32
+#define WPA_GTK_MAX_LEN 32
+#define WPA_KEY_RSC_LEN 8
+#define PMKID_LEN 16
+
+struct rsn_pmksa_cache {
+ struct rsn_pmksa_cache *next, *hnext;
+ u8 pmkid[PMKID_LEN];
+ u8 pmk[PMK_LEN];
+ time_t expiration;
+ int akmp; /* WPA_KEY_MGMT_* */
+ u8 spa[ETH_ALEN];
+};
+
+struct rsn_preauth_interface {
+ struct rsn_preauth_interface *next;
+ struct hostapd_data *hapd;
+ struct l2_packet_data *l2;
+ char *ifname;
+ int ifindex;
+};
+
+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[WPA_KEY_RSC_LEN];
+ u8 key_id[8]; /* Reserved */
+ 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)
+
+
+/* per STA state machine data */
+
+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_state_machine {
+ struct hostapd_data *hapd;
+ struct sta_info *sta;
+
+ enum {
+ WPA_PTK_INITIALIZE, WPA_PTK_DISCONNECT, WPA_PTK_DISCONNECTED,
+ WPA_PTK_AUTHENTICATION, WPA_PTK_AUTHENTICATION2,
+ WPA_PTK_INITPMK, WPA_PTK_INITPSK, WPA_PTK_PTKSTART,
+ WPA_PTK_PTKCALCNEGOTIATING, WPA_PTK_PTKCALCNEGOTIATING2,
+ WPA_PTK_PTKINITNEGOTIATING, WPA_PTK_PTKINITDONE
+ } wpa_ptk_state;
+
+ enum {
+ WPA_PTK_GROUP_IDLE = 0,
+ WPA_PTK_GROUP_REKEYNEGOTIATING,
+ WPA_PTK_GROUP_REKEYESTABLISHED,
+ WPA_PTK_GROUP_KEYERROR
+ } wpa_ptk_group_state;
+
+ Boolean Init;
+ Boolean DeauthenticationRequest;
+ Boolean AuthenticationRequest;
+ Boolean ReAuthenticationRequest;
+ Boolean Disconnect;
+ int TimeoutCtr;
+ int GTimeoutCtr;
+ Boolean TimeoutEvt;
+ Boolean EAPOLKeyReceived;
+ Boolean EAPOLKeyPairwise;
+ Boolean EAPOLKeyRequest;
+ Boolean MICVerified;
+ Boolean GUpdateStationKeys;
+ u8 ANonce[WPA_NONCE_LEN];
+ u8 SNonce[WPA_NONCE_LEN];
+ u8 PMK[WPA_PMK_LEN];
+ struct wpa_ptk PTK;
+ Boolean PTK_valid;
+ Boolean pairwise_set;
+ int keycount;
+ Boolean Pair;
+ u8 key_replay_counter[WPA_REPLAY_COUNTER_LEN];
+ Boolean key_replay_counter_valid;
+ Boolean PInitAKeys; /* WPA only, not in IEEE 802.11i/D8 */
+ Boolean PTKRequest; /* not in IEEE 802.11i state machine */
+ Boolean has_GTK;
+
+ u8 *last_rx_eapol_key; /* starting from IEEE 802.1X header */
+ size_t last_rx_eapol_key_len;
+
+ Boolean changed;
+};
+
+/* per authenticator data */
+struct wpa_authenticator {
+ Boolean GInit;
+ int GNoStations;
+ int GKeyDoneStations;
+ Boolean GTKReKey;
+ int GTK_len;
+ int GN, GM;
+ Boolean GTKAuthenticator;
+ u8 Counter[WPA_NONCE_LEN];
+
+ enum {
+ WPA_GROUP_GTK_INIT = 0,
+ WPA_GROUP_SETKEYS, WPA_GROUP_SETKEYSDONE
+ } wpa_group_state;
+
+ u8 GMK[WPA_GMK_LEN];
+ u8 GTK[2][WPA_GTK_MAX_LEN];
+ u8 GNonce[WPA_NONCE_LEN];
+ Boolean changed;
+
+ unsigned int dot11RSNAStatsTKIPRemoteMICFailures;
+ u8 dot11RSNAAuthenticationSuiteSelected[4];
+ u8 dot11RSNAPairwiseCipherSelected[4];
+ u8 dot11RSNAGroupCipherSelected[4];
+ u8 dot11RSNAPMKIDUsed[PMKID_LEN];
+ u8 dot11RSNAAuthenticationSuiteRequested[4]; /* FIX: update */
+ u8 dot11RSNAPairwiseCipherRequested[4]; /* FIX: update */
+ u8 dot11RSNAGroupCipherRequested[4]; /* FIX: update */
+ unsigned int dot11RSNATKIPCounterMeasuresInvoked;
+ unsigned int dot11RSNA4WayHandshakeFailures;
+};
+
+
+int wpa_init(struct hostapd_data *hapd);
+void wpa_deinit(struct hostapd_data *hapd);
+
+enum {
+ WPA_IE_OK, WPA_INVALID_IE, WPA_INVALID_GROUP, WPA_INVALID_PAIRWISE,
+ WPA_INVALID_AKMP
+};
+
+int wpa_validate_wpa_ie(struct hostapd_data *hapd, struct sta_info *sta,
+ u8 *wpa_ie, size_t wpa_ie_len, int version);
+void wpa_new_station(struct hostapd_data *hapd, struct sta_info *sta);
+void wpa_free_station(struct sta_info *sta);
+void wpa_receive(struct hostapd_data *hapd, struct sta_info *sta,
+ u8 *data, size_t data_len);
+typedef enum {
+ WPA_AUTH, WPA_ASSOC, WPA_DISASSOC, WPA_DEAUTH, WPA_REAUTH,
+ WPA_REAUTH_EAPOL
+} wpa_event;
+void wpa_sm_event(struct hostapd_data *hapd, struct sta_info *sta,
+ wpa_event event);
+void wpa_sm_notify(struct hostapd_data *hapd, struct sta_info *sta);
+void pmksa_cache_add(struct hostapd_data *hapd, struct sta_info *sta, u8 *pmk,
+ int session_timeout);
+void rsn_preauth_finished(struct hostapd_data *hapd, struct sta_info *sta,
+ int success);
+void rsn_preauth_send(struct hostapd_data *hapd, struct sta_info *sta,
+ u8 *buf, size_t len);
+void wpa_gtk_rekey(struct hostapd_data *hapd);
+int wpa_get_mib(struct hostapd_data *hapd, char *buf, size_t buflen);
+int wpa_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta,
+ char *buf, size_t buflen);
+
+#endif /* WPA_H */