diff options
author | sam <sam@FreeBSD.org> | 2005-06-05 20:52:14 +0000 |
---|---|---|
committer | sam <sam@FreeBSD.org> | 2005-06-05 20:52:14 +0000 |
commit | 22428bd35b9f9c7c9348cb8aac58e86776aedf98 (patch) | |
tree | 53b5670fadb24e915539b980077f0334bb5686d8 | |
parent | 86f5e64b886eb113ffce74b106a891398bd242ec (diff) | |
parent | 2b0ba7bae5b60321ee7843871a2cf15ad6b3f65b (diff) | |
download | FreeBSD-src-22428bd35b9f9c7c9348cb8aac58e86776aedf98.zip FreeBSD-src-22428bd35b9f9c7c9348cb8aac58e86776aedf98.tar.gz |
This commit was generated by cvs2svn to compensate for changes in r147013,
which included commits to RCS files with non-trunk default branches.
82 files changed, 35297 insertions, 0 deletions
diff --git a/contrib/wpa_supplicant/COPYING b/contrib/wpa_supplicant/COPYING new file mode 100644 index 0000000..60549be --- /dev/null +++ b/contrib/wpa_supplicant/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) 19yy <name of author> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + <signature of Ty Coon>, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/contrib/wpa_supplicant/ChangeLog b/contrib/wpa_supplicant/ChangeLog new file mode 100644 index 0000000..5709858 --- /dev/null +++ b/contrib/wpa_supplicant/ChangeLog @@ -0,0 +1,395 @@ +ChangeLog for wpa_supplicant + +2005-02-13 - v0.3.8 + * fixed EAPOL-Key validation to drop packets with invalid Key Data + Length; such frames could have crashed wpa_supplicant due to buffer + overflow + +2005-02-12 - v0.3.7 (beginning of 0.3.x stable releases) + * added new phase1 option parameter, include_tls_length=1, to force + wpa_supplicant to add TLS Message Length field to all TLS messages + even if the packet is not fragmented; this may be needed with some + authentication servers + * fixed WPA/RSN IE verification in message 3 of 4-Way Handshake when + using drivers that take care of AP selection (e.g., when using + ap_scan=2) + * fixed reprocessing of pending request after ctrl_iface requests for + identity/password/otp + * fixed ctrl_iface requests for identity/password/otp in Phase 2 of + EAP-PEAP and EAP-TTLS + * all drivers using driver_wext: set interface up and select Managed + mode when starting wpa_supplicant; set interface down when exiting + * renamed driver_ipw2100.c to driver_ipw.c since it now supports both + ipw2100 and ipw2200; please note that this also changed the + configuration variable in .config to CONFIG_DRIVER_IPW + +2005-01-24 - v0.3.6 + * fixed a busy loop introduced in v0.3.5 for scan result processing + when no matching AP is found + +2005-01-23 - v0.3.5 + * added a workaround for an interoperability issue with a Cisco AP + when using WPA2-PSK + * fixed non-WPA IEEE 802.1X to use the same authentication timeout as + WPA with IEEE 802.1X (i.e., timeout 10 -> 70 sec to allow + retransmission of dropped frames) + * fixed issues with 64-bit CPUs and SHA1 cleanup in previous version + (e.g., segfault when processing EAPOL-Key frames) + * fixed EAP workaround and fast reauthentication configuration for + RSN pre-authentication; previously these were disabled and + pre-authentication would fail if the used authentication server + requires EAP workarounds + * added support for blacklisting APs that fail or timeout + authentication in ap_scan=1 mode so that all APs are tried in cases + where the ones with strongest signal level are failing authentication + * fixed CA certificate loading after a failed EAP-TLS/PEAP/TTLS + authentication attempt + * allow EAP-PEAP/TTLS fast reauthentication only if Phase 2 succeeded + in the previous authentication (previously, only Phase 1 success was + verified) + +2005-01-09 - v0.3.4 + * added preliminary support for IBSS (ad-hoc) mode configuration + (mode=1 in network block); this included a new key_mgmt mode + WPA-NONE, i.e., TKIP or CCMP with a fixed key (based on psk) and no + key management; see wpa_supplicant.conf for more details and an + example on how to configure this (note: this is currently implemented + only for driver_hostapd.c, but the changes should be trivial to add + in associate() handler for other drivers, too (assuming the driver + supports WPA-None) + * added preliminary port for native Windows (i.e., no cygwin) using + mingw + +2005-01-02 - v0.3.3 + * added optional support for GNU Readline and History Libraries for + wpa_cli (CONFIG_READLINE) + * cleaned up EAP state machine <-> method interface and number of + small problems with error case processing not terminating on + EAP-Failure but waiting for timeout + * added couple of workarounds for interoperability issues with a + Cisco AP when using WPA2 + * added support for EAP-FAST (draft-cam-winget-eap-fast-00.txt); + Note: This requires a patch for openssl to add support for TLS + extensions and number of workarounds for operations without + certificates. Proof of concept type of experimental patch is + included in openssl-tls-extensions.patch. + +2004-12-19 - v0.3.2 + * fixed private key loading for cases where passphrase is not set + * fixed Windows/cygwin L2 packet handler freeing; previous version + could cause a segfault when RSN pre-authentication was completed + * added support for PMKSA caching with drivers that generate RSN IEs + (e.g., NDIS); currently, this is only implemented in driver_ndis.c, + but similar code can be easily added to driver_ndiswrapper.c once + ndiswrapper gets full support for RSN PMKSA caching + * improved recovery from PMKID mismatches by requesting full EAP + authentication in case of failed PMKSA caching attempt + * driver_ndis: added support for NDIS NdisMIncidateStatus() events + (this requires that ndis_events is ran while wpa_supplicant is + running) + * driver_ndis: use ADD_WEP/REMOVE_WEP when configuring WEP keys + * added support for driver interfaces to replace the interface name + based on driver/OS specific mapping, e.g., in case of driver_ndis, + this allows the beginning of the adapter description to be used as + the interface name + * added support for CR+LF (Windows-style) line ends in configuration + file + * driver_ndis: enable radio before starting scanning, disable radio + when exiting + * modified association event handler to set portEnabled = FALSE before + clearing port Valid in order to reset EAP state machine and avoid + problems with new authentication getting ignored because of state + machines ending up in AUTHENTICATED/SUCCESS state based on old + information + * added support for driver events to add PMKID candidates in order to + allow drivers to give priority to most likely roaming candidates + * driver_hostap: moved PrivacyInvoked configuration to associate() + function so that this will not be set for plaintext connections + * added KEY_MGMT_802_1X_NO_WPA as a new key_mgmt type so that driver + interface can distinguish plaintext and IEEE 802.1X (no WPA) + authentication + * fixed static WEP key configuration to use broadcast/default type for + all keys (previously, the default TX key was configured as pairwise/ + unicast key) + * driver_ndis: added legacy WPA capability detection for non-WPA2 + drivers + * added support for setting static WEP keys for IEEE 802.1X without + dynamic WEP keying (eapol_flags=0) + +2004-12-12 - v0.3.1 + * added support for reading PKCS#12 (PFX) files (as a replacement for + PEM/DER) to get certificate and private key (CONFIG_PKCS12) + * fixed compilation with CONFIG_PCSC=y + * added new ap_scan mode, ap_scan=2, for drivers that take care of + association, but need to be configured with security policy and SSID, + e.g., ndiswrapper and NDIS driver; this mode should allow such + drivers to work with hidden SSIDs and optimized roaming; when + ap_scan=2 is used, only the first network block in the configuration + file is used and this configuration should have explicit security + policy (i.e., only one option in the lists) for key_mgmt, pairwise, + group, proto variables + * added experimental port of wpa_supplicant for Windows + - driver_ndis.c driver interface (NDIS OIDs) + - currently, this requires cygwin and WinPcap + - small utility, win_if_list, can be used to get interface name + * control interface can now be removed at build time; add + CONFIG_CTRL_IFACE=y to .config to maintain old functionality + * optional Xsupplicant interface can now be removed at build time; + (CONFIG_XSUPPLICANT_IFACE=y in .config to bring it back) + * added auth_alg to driver interface associate() parameters to make it + easier for drivers to configure authentication algorithm as part of + the association + +2004-12-05 - v0.3.0 (beginning of 0.3.x development releases) + * driver_broadcom: added new driver interface for Broadcom wl.o driver + (a generic driver for Broadcom IEEE 802.11a/g cards) + * wpa_cli: fixed parsing of -p <path> command line argument + * PEAPv1: fixed tunneled EAP-Success reply handling to reply with TLS + ACK, not tunneled EAP-Success (of which only the first byte was + actually send due to a bug in previous code); this seems to + interoperate with most RADIUS servers that implements PEAPv1 + * PEAPv1: added support for terminating PEAP authentication on tunneled + EAP-Success message; this can be configured by adding + peap_outer_success=0 on phase1 parameters in wpa_supplicant.conf + (some RADIUS servers require this whereas others require a tunneled + reply + * PEAPv1: changed phase1 option peaplabel to use default to 0, i.e., to + the old label for key derivation; previously, the default was 1, + but it looks like most existing PEAPv1 implementations use the old + label which is thus more suitable default option + * added support for EAP-PSK (draft-bersani-eap-psk-03.txt) + * fixed parsing of wep_tx_keyidx + * added support for configuring list of allowed Phase 2 EAP types + (for both EAP-PEAP and EAP-TTLS) instead of only one type + * added support for configuring IEEE 802.11 authentication algorithm + (auth_alg; mainly for using Shared Key authentication with static + WEP keys) + * added support for EAP-AKA (with UMTS SIM) + * fixed couple of errors in PCSC handling that could have caused + random-looking errors for EAP-SIM + * added support for EAP-SIM pseudonyms and fast re-authentication + * added support for EAP-TLS/PEAP/TTLS fast re-authentication (TLS + session resumption) + * added support for EAP-SIM with two challanges + (phase1="sim_min_num_chal=3" can be used to require three challenges) + * added support for configuring DH/DSA parameters for an ephemeral DH + key exchange (EAP-TLS/PEAP/TTLS) using new configuration parameters + dh_file and dh_file2 (phase 2); this adds support for using DSA keys + and optional DH key exchange to achieve forward secracy with RSA keys + * added support for matching subject of the authentication server + certificate with a substring when using EAP-TLS/PEAP/TTLS; new + configuration variables subject_match and subject_match2 + * changed SSID configuration in driver_wext.c (used by many driver + interfaces) to use ssid_len+1 as the length for SSID since some Linux + drivers expect this + * fixed couple of unaligned reads in scan result parsing to fix WPA + connection on some platforms (e.g., ARM) + * added driver interface for Intel ipw2100 driver + * added support for LEAP with WPA + * added support for larger scan results report (old limit was 4 kB of + data, i.e., about 35 or so APs) when using Linux wireless extensions + v17 or newer + * fixed a bug in PMKSA cache processing: skip sending of EAPOL-Start + only if there is a PMKSA cache entry for the current AP + * fixed error handling for case where reading of scan results fails: + must schedule a new scan or wpa_supplicant will remain waiting + forever + * changed debug output to remove shared password/key material by + default; all key information can be included with -K command line + argument to match the previous behavior + * added support for timestamping debug log messages (disabled by + default, can be enabled with -t command line argument) + * set pairwise/group cipher suite for non-WPA IEEE 802.1X to WEP-104 + if keys are not configured to be used; this fixes IEEE 802.1X mode + with drivers that use this information to configure whether Privacy + bit can be in Beacon frames (e.g., ndiswrapper) + * avoid clearing driver keys if no keys have been configured since last + key clear request; this seems to improve reliability of group key + handshake for ndiswrapper & NDIS driver which seems to be suffering + of some kind of timing issue when the keys are cleared again after + association + * changed driver interface API: + - WPA_SUPPLICANT_DRIVER_VERSION define can be used to determine which + version is being used (now, this is set to 2; previously, it was + not defined) + - pass pointer to private data structure to all calls + - the new API is not backwards compatible; all in-tree driver + interfaces has been converted to the new API + * added support for controlling multiple interfaces (radios) per + wpa_supplicant process; each interface needs to be listed on the + command line (-c, -i, -D arguments) with -N as a separator + (-cwpa1.conf -iwlan0 -Dhostap -N -cwpa2.conf -iath0 -Dmadwifi) + * added a workaround for EAP servers that incorrectly use same Id for + sequential EAP packets + * changed libpcap/libdnet configuration to use .config variable, + CONFIG_DNET_PCAP, instead of requiring Makefile modification + * improved downgrade attack detection in IE verification of msg 3/4: + verify both WPA and RSN IEs, if present, not only the selected one; + reject the AP if an RSN IE is found in msg 3/4, but not in Beacon or + Probe Response frame, and RSN is enabled in wpa_supplicant + configuration + * fixed WPA msg 3/4 processing to allow Key Data field contain other + IEs than just one WPA IE + * added support for FreeBSD and driver interface for the BSD net80211 + layer (CONFIG_DRIVER_BSD=y in .config); please note that some of the + required kernel mods have not yet been committed + * made EAP workarounds configurable; enabled by default, can be + disabled with network block option eap_workaround=0 + +2004-07-17 - v0.2.4 (beginning of 0.2.x stable releases) + * resolved couple of interoperability issues with EAP-PEAPv1 and + Phase 2 (inner EAP) fragment reassembly + * driver_madwifi: fixed WEP key configuration for IEEE 802.1X when the + AP is using non-zero key index for the unicast key and key index zero + for the broadcast key + * driver_hostap: fixed IEEE 802.1X WEP key updates and + re-authentication by allowing unencrypted EAPOL frames when not using + WPA + * added a new driver interface, 'wext', which uses only standard, + driver independent functionality in Linux wireless extensions; + currently, this can be used only for non-WPA IEEE 802.1X mode, but + eventually, this is to be extended to support full WPA/WPA2 once + Linux wireless extensions get support for this + * added support for mode in which the driver is responsible for AP + scanning and selection; this is disabled by default and can be + enabled with global ap_scan=0 variable in wpa_supplicant.conf; + this mode can be used, e.g., with generic 'wext' driver interface to + use wpa_supplicant as IEEE 802.1X Supplicant with any Linux driver + supporting wireless extensions. + * driver_madwifi: fixed WPA2 configuration and scan_ssid=1 (e.g., + operation with an AP that does not include SSID in the Beacon frames) + * added support for new EAP authentication methods: + EAP-TTLS/EAP-OTP, EAP-PEAPv0/OTP, EAP-PEAPv1/OTP, EAP-OTP + * added support for asking one-time-passwords from frontends (e.g., + wpa_cli); this 'otp' command works otherwise like 'password' command, + but the password is used only once and the frontend will be asked for + a new password whenever a request from authenticator requires a + password; this can be used with both EAP-OTP and EAP-GTC + * changed wpa_cli to automatically re-establish connection so that it + does not need to be re-started when wpa_supplicant is terminated and + started again + * improved user data (identity/password/otp) requests through + frontends: process pending EAPOL packets after getting new + information so that full authentication does not need to be + restarted; in addition, send pending requests again whenever a new + frontend is attached + * changed control frontends to use a new directory for socket files to + make it easier for wpa_cli to automatically select between interfaces + and to provide access control for the control interface; + wpa_supplicant.conf: ctrl_interface is now a path + (/var/run/wpa_supplicant is the recommended path) and + ctrl_interface_group can be used to select which group gets access to + the control interface; + wpa_cli: by default, try to connect to the first interface available + in /var/run/wpa_supplicant; this path can be overriden with -p option + and an interface can be selected with -i option (i.e., in most common + cases, wpa_cli does not need to get any arguments) + * added support for LEAP + * added driver interface for Linux ndiswrapper + * added priority option for network blocks in the configuration file; + this allows networks to be grouped based on priority (the scan + results are searched for matches with network blocks in this order) + +2004-06-20 - v0.2.3 + * sort scan results to improve AP selection + * fixed control interface socket removal for some error cases + * improved scan requesting and authentication timeout + * small improvements/bug fixes for EAP-MSCHAPv2, EAP-PEAP, and + TLS processing + * PEAP version can now be forced with phase1="peapver=<ver>" + (mostly for testing; by default, the highest version supported by + both the Supplicant and Authentication Server is selected + automatically) + * added support for madwifi driver (Atheros ar521x) + * added a workaround for cases where AP sets Install Tx/Rx bit for + WPA Group Key messages when pairwise keys are used (without this, + the Group Key would be used for Tx and the AP would drop frames + from the station) + * added GSM SIM/USIM interface for GSM authentication algorithm for + EAP-SIM; this requires pcsc-lite + * added support for ATMEL AT76C5XXx driver + * fixed IEEE 802.1X WEP key derivation in the case where Authenticator + does not include key data in the EAPOL-Key frame (i.e., part of + EAP keying material is used as data encryption key) + * added support for using plaintext and static WEP networks + (key_mgmt=NONE) + +2004-05-31 - v0.2.2 + * added support for new EAP authentication methods: + EAP-TTLS/EAP-MD5-Challenge + EAP-TTLS/EAP-GTC + EAP-TTLS/EAP-MSCHAPv2 + EAP-TTLS/EAP-TLS + EAP-TTLS/MSCHAPv2 + EAP-TTLS/MSCHAP + EAP-TTLS/PAP + EAP-TTLS/CHAP + EAP-PEAP/TLS + EAP-PEAP/GTC + EAP-PEAP/MD5-Challenge + EAP-GTC + EAP-SIM (not yet complete; needs GSM/SIM authentication interface) + * added support for anonymous identity (to be used when identity is + sent in plaintext; real identity will be used within TLS protected + tunnel (e.g., with EAP-TTLS) + * added event messages from wpa_supplicant to frontends, e.g., wpa_cli + * added support for requesting identity and password information using + control interface; in other words, the password for EAP-PEAP or + EAP-TTLS does not need to be included in the configuration file since + a frontand (e.g., wpa_cli) can ask it from the user + * improved RSN pre-authentication to use a candidate list and process + all candidates from each scan; not only one per scan + * fixed RSN IE and WPA IE capabilities field parsing + * ignore Tx bit in GTK IE when Pairwise keys are used + * avoid making new scan requests during IEEE 802.1X negotiation + * use openssl/libcrypto for MD5 and SHA-1 when compiling wpa_supplicant + with TLS support (this replaces the included implementation with + library code to save about 8 kB since the library code is needed + anyway for TLS) + * fixed WPA-PSK only mode when compiled without IEEE 802.1X support + (i.e., without CONFIG_IEEE8021X_EAPOL=y in .config) + +2004-05-06 - v0.2.1 + * added support for internal IEEE 802.1X (actually, IEEE 802.1aa/D6.1) + Supplicant + - EAPOL state machines for Supplicant [IEEE 802.1aa/D6.1] + - EAP peer state machine [draft-ietf-eap-statemachine-02.pdf] + - EAP-MD5 (cannot be used with WPA-RADIUS) + [draft-ietf-eap-rfc2284bis-09.txt] + - EAP-TLS [RFC 2716] + - EAP-MSCHAPv2 (currently used only with EAP-PEAP) + - EAP-PEAP/MSCHAPv2 [draft-josefsson-pppext-eap-tls-eap-07.txt] + [draft-kamath-pppext-eap-mschapv2-00.txt] + (PEAP version 0, 1, and parts of 2; only 0 and 1 are enabled by + default; tested with FreeRADIUS, Microsoft IAS, and Funk Odyssey) + - new configuration file options: eap, identity, password, ca_cert, + client_cert, privatekey, private_key_passwd + - Xsupplicant is not required anymore, but it can be used by + disabling the internal IEEE 802.1X Supplicant with -e command line + option + - this code is not included in the default build; Makefile need to + be edited for this (uncomment lines for selected functionality) + - EAP-TLS and EAP-PEAP require openssl libraries + * use module prefix in debug messages (WPA, EAP, EAP-TLS, ..) + * added support for non-WPA IEEE 802.1X mode with dynamic WEP keys + (i.e., complete IEEE 802.1X/EAP authentication and use IEEE 802.1X + EAPOL-Key frames instead of WPA key handshakes) + * added support for IEEE 802.11i/RSN (WPA2) + - improved PTK Key Handshake + - PMKSA caching, pre-authentication + * fixed wpa_supplicant to ignore possible extra data after WPA + EAPOL-Key packets (this fixes 'Invalid EAPOL-Key MIC when using + TPTK' error from message 3 of 4-Way Handshake in case the AP + includes extra data after the EAPOL-Key) + * added interface for external programs (frontends) to control + wpa_supplicant + - CLI example (wpa_cli) with interactive mode and command line + mode + - replaced SIGUSR1 status/statistics with the new control interface + * made some feature compile time configurable + - .config file for make + - driver interfaces (hostap, hermes, ..) + - EAPOL/EAP functions + +2004-02-15 - v0.2.0 + * Initial version of wpa_supplicant diff --git a/contrib/wpa_supplicant/Makefile b/contrib/wpa_supplicant/Makefile new file mode 100644 index 0000000..fa91243 --- /dev/null +++ b/contrib/wpa_supplicant/Makefile @@ -0,0 +1,385 @@ +ifndef CC +CC=gcc +endif + +ifndef CFLAGS +CFLAGS = -MMD -O2 -Wall -g +endif + +# Include directories for CVS version +CFLAGS += -I../driver/modules -I../utils -I../hostapd + +ALL=wpa_supplicant wpa_passphrase wpa_cli + +all: verify_config $(ALL) + +verify_config: + @if [ ! -r .config ]; then \ + echo 'Building wpa_supplicant requires a configuration file'; \ + echo '(.config). See README for more instructions. You can'; \ + echo 'run "cp defconfig .config" to create an example'; \ + echo 'configuration.'; \ + exit 1; \ + fi + +mkconfig: + @if [ -e .config ]; then \ + echo '.config exists - did not replace it'; \ + exit 1; \ + fi + echo CONFIG_DRIVER_HOSTAP=y >> .config + echo CONFIG_DRIVER_WEXT=y >> .config + echo CONFIG_WIRELESS_EXTENSION=y >> .config + +install: all + mkdir -p $(DESTDIR)/usr/local/sbin/ + for i in $(ALL); do cp $$i $(DESTDIR)/usr/local/sbin/$$i; done + +OBJS = config.o \ + eloop.o common.o md5.o \ + rc4.o sha1.o aes_wrap.o +OBJS_p = wpa_passphrase.o sha1.o md5.o +OBJS_c = wpa_cli.o wpa_ctrl.o + +-include .config + +ifdef CONFIG_EAPOL_TEST +CFLAGS += -Werror -DEAPOL_TEST +endif + +ifdef CONFIG_DRIVER_HOSTAP +CFLAGS += -DCONFIG_DRIVER_HOSTAP +OBJS += driver_hostap.o +CONFIG_WIRELESS_EXTENSION=y +endif + +ifdef CONFIG_DRIVER_WEXT +CFLAGS += -DCONFIG_DRIVER_WEXT +CONFIG_WIRELESS_EXTENSION=y +endif + +ifdef CONFIG_DRIVER_PRISM54 +CFLAGS += -DCONFIG_DRIVER_PRISM54 +OBJS += driver_prism54.o +CONFIG_WIRELESS_EXTENSION=y +endif + +ifdef CONFIG_DRIVER_HERMES +CFLAGS += -DCONFIG_DRIVER_HERMES +OBJS += driver_hermes.o +CONFIG_WIRELESS_EXTENSION=y +endif + +ifdef CONFIG_DRIVER_MADWIFI +CFLAGS += -DCONFIG_DRIVER_MADWIFI +OBJS += driver_madwifi.o +CONFIG_WIRELESS_EXTENSION=y +endif + +ifdef CONFIG_DRIVER_ATMEL +CFLAGS += -DCONFIG_DRIVER_ATMEL +OBJS += driver_atmel.o +CONFIG_WIRELESS_EXTENSION=y +endif + +ifdef CONFIG_DRIVER_NDISWRAPPER +CFLAGS += -DCONFIG_DRIVER_NDISWRAPPER +OBJS += driver_ndiswrapper.o +CONFIG_WIRELESS_EXTENSION=y +endif + +ifdef CONFIG_DRIVER_BROADCOM +CFLAGS += -DCONFIG_DRIVER_BROADCOM +OBJS += driver_broadcom.o +endif + +ifdef CONFIG_DRIVER_IPW +CFLAGS += -DCONFIG_DRIVER_IPW +OBJS += driver_ipw.o +CONFIG_WIRELESS_EXTENSION=y +endif + +ifdef CONFIG_DRIVER_BSD +CFLAGS += -DCONFIG_DRIVER_BSD +OBJS += driver_bsd.o +CONFIG_DNET_PCAP=y +endif + +ifdef CONFIG_DRIVER_NDIS +CFLAGS += -DCONFIG_DRIVER_NDIS +OBJS += driver_ndis.o driver_ndis_.o +CONFIG_DNET_PCAP=y +CONFIG_WINPCAP=y +endif + +ifdef CONFIG_DRIVER_TEST +CFLAGS += -DCONFIG_DRIVER_TEST +OBJS += driver_test.o +endif + +ifdef CONFIG_DNET_PCAP +CFLAGS += -DUSE_DNET_PCAP +ifdef CONFIG_WINPCAP +CFLAGS += -DCONFIG_WINPCAP +LIBS += -lwpcap -lpacket +LIBS_w += -lwpcap +else +LIBS += -ldnet -lpcap +endif +endif + +ifdef CONFIG_EAP_TLS +# EAP-TLS +CFLAGS += -DEAP_TLS +OBJS += eap_tls.o +TLS_FUNCS=y +CONFIG_IEEE8021X_EAPOL=y +endif + +ifdef CONFIG_EAP_PEAP +# EAP-PEAP +CFLAGS += -DEAP_PEAP +OBJS += eap_peap.o +TLS_FUNCS=y +CONFIG_EAP_MSCHAPV2=y +CONFIG_IEEE8021X_EAPOL=y +CONFIG_EAP_TLV=y +endif + +ifdef CONFIG_EAP_TTLS +# EAP-TTLS +CFLAGS += -DEAP_TTLS +OBJS += eap_ttls.o +MS_FUNCS=y +TLS_FUNCS=y +CONFIG_EAP_MD5=y +CONFIG_IEEE8021X_EAPOL=y +endif + +ifdef CONFIG_EAP_MD5 +# EAP-MD5 (also used by EAP-TTLS) +CFLAGS += -DEAP_MD5 +OBJS += eap_md5.o +CONFIG_IEEE8021X_EAPOL=y +endif + +# backwards compatibility for old spelling +ifdef CONFIG_MSCHAPV2 +CONFIG_EAP_MSCHAPV2=y +endif + +ifdef CONFIG_EAP_MSCHAPV2 +# EAP-MSCHAPv2 (also used by EAP-PEAP) +CFLAGS += -DEAP_MSCHAPv2 +OBJS += eap_mschapv2.o +MS_FUNCS=y +CONFIG_IEEE8021X_EAPOL=y +endif + +ifdef CONFIG_EAP_GTC +# EAP-GTC (also used by EAP-PEAP) +CFLAGS += -DEAP_GTC +OBJS += eap_gtc.o +CONFIG_IEEE8021X_EAPOL=y +endif + +ifdef CONFIG_EAP_OTP +# EAP-OTP +CFLAGS += -DEAP_OTP +OBJS += eap_otp.o +CONFIG_IEEE8021X_EAPOL=y +endif + +ifdef CONFIG_EAP_SIM +# EAP-SIM +CFLAGS += -DEAP_SIM +OBJS += eap_sim.o +CONFIG_IEEE8021X_EAPOL=y +CONFIG_EAP_SIM_COMMON=y +endif + +ifdef CONFIG_EAP_LEAP +# EAP-LEAP +CFLAGS += -DEAP_LEAP +OBJS += eap_leap.o +MS_FUNCS=y +CONFIG_IEEE8021X_EAPOL=y +endif + +ifdef CONFIG_EAP_PSK +# EAP-PSK +CFLAGS += -DEAP_PSK +OBJS += eap_psk.o +CONFIG_IEEE8021X_EAPOL=y +endif + +ifdef CONFIG_EAP_AKA +# EAP-AKA +CFLAGS += -DEAP_AKA +OBJS += eap_aka.o +CONFIG_IEEE8021X_EAPOL=y +CONFIG_EAP_SIM_COMMON=y +endif + +ifdef CONFIG_EAP_SIM_COMMON +OBJS += eap_sim_common.o +endif + +ifdef CONFIG_EAP_TLV +# EAP-TLV +CFLAGS += -DEAP_TLV +OBJS += eap_tlv.o +endif + +ifdef CONFIG_EAP_FAST +# EAP-FAST +CFLAGS += -DEAP_FAST +OBJS += eap_fast.o +TLS_FUNCS=y +endif + +ifdef CONFIG_IEEE8021X_EAPOL +# IEEE 802.1X/EAPOL state machines (e.g., for RADIUS authentication) +CFLAGS += -DIEEE8021X_EAPOL +OBJS += eapol_sm.o eap.o +endif + +ifdef CONFIG_PCSC +# PC/SC interface for smartcards (USIM, GSM SIM) +CFLAGS += -DPCSC_FUNCS -I/usr/include/PCSC +OBJS += pcsc_funcs.o +# -lpthread may not be needed depending on how pcsc-lite was configured +LIBS += -lpcsclite -lpthread +endif + +ifdef TLS_FUNCS +# Shared TLS functions (needed for EAP_TLS, EAP_PEAP, and EAP_TTLS) +CFLAGS += -DEAP_TLS_FUNCS +OBJS += eap_tls_common.o tls_openssl.o +LIBS += -lssl -lcrypto +LIBS_p += -lcrypto +else +OBJS += tls_none.o +endif + +ifdef CONFIG_PKCS12 +CFLAGS += -DPKCS12_FUNCS +endif + +ifdef MS_FUNCS +ifndef TLS_FUNCS +LIBS += -lcrypto +endif +OBJS += ms_funcs.o crypto.o +endif + +ifdef CONFIG_WIRELESS_EXTENSION +CFLAGS += -DCONFIG_WIRELESS_EXTENSION +OBJS += driver_wext.o +endif + +ifdef CONFIG_CTRL_IFACE +CFLAGS += -DCONFIG_CTRL_IFACE +OBJS += ctrl_iface.o +endif + +ifdef CONFIG_XSUPPLICANT_IFACE +CFLAGS += -DCONFIG_XSUPPLICANT_IFACE +endif + +ifdef CONFIG_READLINE +CFLAGS += -DCONFIG_READLINE +LIBS_c += -lncurses -lreadline +endif + +ifdef CONFIG_NATIVE_WINDOWS +CFLAGS += -DCONFIG_NATIVE_WINDOWS -DCONFIG_CTRL_IFACE_UDP +LIBS += -lws2_32 -lgdi32 +LIBS_c += -lws2_32 +endif + +OBJS_t := $(OBJS) eapol_test.o radius.o radius_client.o +OBJS_t2 := $(OBJS) preauth_test.o l2_packet.o +OBJS += wpa_supplicant.o wpa.o l2_packet.o drivers.o + +wpa_supplicant: .config $(OBJS) + $(CC) -o wpa_supplicant $(OBJS) $(LIBS) + +eapol_test: .config $(OBJS_t) + $(CC) -o eapol_test $(OBJS_t) $(LIBS) + +preauth_test: .config $(OBJS_t2) + $(CC) -o preauth_test $(OBJS_t2) $(LIBS) + +wpa_passphrase: $(OBJS_p) + $(CC) -o wpa_passphrase $(OBJS_p) $(LIBS_p) + +wpa_cli: $(OBJS_c) + $(CC) -o wpa_cli $(OBJS_c) $(LIBS_c) + +win_if_list: win_if_list.c + $(CC) -o $@ win_if_list.c $(CFLAGS) $(LIBS_w) + +# parameters for Microsoft Visual C++ Toolkit 2003 compiler +CL=cl +CLDIR=C:\Program Files\Microsoft Visual C++ Toolkit 2003 +PSDKDIR=C:\Program Files\Microsoft Platform SDK for Windows XP SP2 +CLFLAGS=-O +CLLIBS=wbemuuid.lib libcmt.lib kernel32.lib uuid.lib ole32.lib oleaut32.lib \ + ws2_32.lib + +ndis_events: ndis_events.cpp + INCLUDE="$(CLDIR)\include;$(PSDKDIR)\Include" \ + LIB="$(CLDIR)\lib;$(PSDKDIR)\Lib" \ + $(CL) $(CLFLAGS) -o ndis_events.exe ndis_events.cpp \ + /link -nodefaultlib $(CLLIBS) + +wpa_supplicant.exe: wpa_supplicant + mv -f $< $@ +wpa_cli.exe: wpa_cli + mv -f $< $@ +wpa_passphrase.exe: wpa_passphrase + mv -f $< $@ +win_if_list.exe: win_if_list + mv -f $< $@ + +WINALL=wpa_supplicant.exe wpa_cli.exe wpa_passphrase.exe win_if_list.exe + +windows-bin: $(WINALL) + $(STRIP) $(WINALL) + +TEST_SRC_MS_FUNCS = ms_funcs.c crypto.c sha1.c md5.c +test-ms_funcs: $(TEST_SRC_MS_FUNCS) + $(CC) -o test-ms_funcs -Wall -Werror $(TEST_SRC_MS_FUNCS) \ + -DTEST_MAIN_MS_FUNCS -lcrypto -I../hostapd + ./test-ms_funcs + rm test-ms_funcs + +TEST_SRC_SHA1 = sha1.c +test-sha1: $(TEST_SRC_SHA1) + $(CC) -o test-sha1 -Wall -Werror $(TEST_SRC_SHA1) \ + -DTEST_MAIN -I../hostad + ./test-sha1 + rm test-sha1 + +TEST_SRC_AES_WRAP = aes_wrap.c +test-aes_wrap: $(TEST_SRC_AES_WRAP) + $(CC) -o test-aes_wrap -Wall -Werror $(TEST_SRC_AES_WRAP) \ + -DTEST_MAIN -I../hostad + ./test-aes_wrap + rm test-aes_wrap + +TEST_SRC_EAP_SIM_COMMON = eap_sim_common.c sha1.c md5.c \ + aes_wrap.c common.c +test-eap_sim_common: $(TEST_SRC_EAP_SIM_COMMON) + $(CC) -o test-eap_sim_common -Wall -Werror $(TEST_SRC_EAP_SIM_COMMON) \ + -DTEST_MAIN_EAP_SIM_COMMON -I../hostapd + ./test-eap_sim_common + rm test-eap_sim_common + +tests: test-ms_funcs test-sha1 test-aes_wrap test-eap_sim_common + +clean: + rm -f core *~ *.o *.d $(ALL) $(WINALL) + +-include $(OBJS:%.o=%.d) diff --git a/contrib/wpa_supplicant/README b/contrib/wpa_supplicant/README new file mode 100644 index 0000000..7b5f547 --- /dev/null +++ b/contrib/wpa_supplicant/README @@ -0,0 +1,860 @@ +WPA Supplicant +============== + +Copyright (c) 2003-2005, Jouni Malinen <jkmaline@cc.hut.fi> and +contributors +All Rights Reserved. + +This program is dual-licensed under both the GPL version 2 and BSD +license. Either license may be used at your option. Please note that +some of the driver interface implementations (driver_*.c) may be +licensed under a different license. + + + +License +------- + +GPL v2: + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License version 2 as +published by the Free Software Foundation. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +(this copy of the license is in COPYING file) + + +Alternatively, this software may be distributed under the terms of BSD +license: + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +3. Neither the name(s) of the above-listed copyright holder(s) nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + +Features +-------- + +Supported WPA/IEEE 802.11i features: +- WPA-PSK ("WPA-Personal") +- WPA with EAP (e.g., with RADIUS authentication server) ("WPA-Enterprise") + Following authentication methods are supported with an integrate IEEE 802.1X + Supplicant: + * EAP-TLS + * EAP-PEAP/MSCHAPv2 (both PEAPv0 and PEAPv1) + * EAP-PEAP/TLS (both PEAPv0 and PEAPv1) + * EAP-PEAP/GTC (both PEAPv0 and PEAPv1) + * EAP-PEAP/OTP (both PEAPv0 and PEAPv1) + * EAP-PEAP/MD5-Challenge (both PEAPv0 and PEAPv1) + * EAP-TTLS/EAP-MD5-Challenge + * EAP-TTLS/EAP-GTC + * EAP-TTLS/EAP-OTP + * EAP-TTLS/EAP-MSCHAPv2 + * EAP-TTLS/EAP-TLS + * EAP-TTLS/MSCHAPv2 + * EAP-TTLS/MSCHAP + * EAP-TTLS/PAP + * EAP-TTLS/CHAP + * EAP-SIM + * EAP-AKA + * EAP-PSK + * LEAP (note: requires special support from the driver for IEEE 802.11 + authentication) + (following methods are supported, but since they do not generate keying + material, they cannot be used with WPA or IEEE 802.1X WEP keying) + * EAP-MD5-Challenge + * EAP-MSCHAPv2 + * EAP-GTC + * EAP-OTP + Alternatively, an external program, e.g., Xsupplicant, can be used for EAP + authentication. +- key management for CCMP, TKIP, WEP104, WEP40 +- RSN/WPA2 (IEEE 802.11i) + * pre-authentication + * PMKSA caching + + + +Requirements +------------ + +Current hardware/software requirements: +- Linux kernel 2.4.x or 2.6.x with Linux Wireless Extensions v15 or newer +- FreeBSD 6-CURRENT +- Microsoft Windows with WinPcap (at least WinXP, may work with other versions) +- drivers: + Host AP driver for Prism2/2.5/3 (development snapshot/v0.2.x) + (http://hostap.epitest.fi/) + Driver need to be set in Managed mode ('iwconfig wlan0 mode managed'). + Please note that station firmware version needs to be 1.7.0 or newer + to work in WPA mode. + + Linuxant DriverLoader (http://www.linuxant.com/driverloader/) + with Windows NDIS driver for your wlan card supporting WPA. + + Agere Systems Inc. Linux Driver + (http://www.agere.com/support/drivers/) + Please note that the driver interface file (driver_hermes.c) and + hardware specific include files are not included in the + wpa_supplicant distribution. You will need to copy these from the + source package of the Agere driver. + + madwifi driver for cards based on Atheros chip set (ar521x) + (http://sourceforge.net/projects/madwifi/) + Please note that you will need to modify the wpa_supplicant .config + file to use the correct path for the madwifi driver root directory + (CFLAGS += -I../madwifi/wpa line in example defconfig). + + ATMEL AT76C5XXx driver for USB and PCMCIA cards + (http://atmelwlandriver.sourceforge.net/). + + Linux ndiswrapper (http://ndiswrapper.sourceforge.net/) with + Windows NDIS driver. + + Broadcom wl.o driver + This is a generic Linux driver for Broadcom IEEE 802.11a/g cards. + However, it is proprietary driver that is not publicly available + except for couple of exceptions, mainly Broadcom-based APs/wireless + routers that use Linux. The driver binary can be downloaded, e.g., + from Linksys support site (http://www.linksys.com/support/gpl.asp) + for Linksys WRT54G. The GPL tarball includes cross-compiler and + the needed header file, wlioctl.h, for compiling wpa_supplicant. + This driver support in wpa_supplicant is expected to work also with + other devices based on Broadcom driver (assuming the driver includes + client mode support). + + Intel ipw2100 driver + (http://sourceforge.net/projects/ipw2100/) + + Intel ipw2200 driver + (http://sourceforge.net/projects/ipw2200/) + + In theory, any driver that supports Linux wireless extensions can be + used with IEEE 802.1X (i.e., not WPA) when using ap_scan=0 option in + configuration file. + + BSD net80211 layer (e.g., Atheros driver) + At the moment, this is for FreeBSD 6-CURRENT branch. + + Windows NDIS + The current Windows port requires WinPcap (http://winpcap.polito.it/). + See README-Windows.txt for more information. + +wpa_supplicant was designed to be portable for different drivers and +operating systems. Hopefully, support for more wlan cards and OSes will be +added in the future. See developer.txt for more information about the +design of wpa_supplicant and porting to other drivers. One main goal +is to add full WPA/WPA2 support to Linux wireless extensions to allow +new drivers to be supported without having to implement new +driver-specific interface code in wpa_supplicant. + +Optional libraries for layer2 packet processing: +- libpcap (tested with 0.7.2, most relatively recent versions assumed to work, + this is likely to be available with most distributions, + http://tcpdump.org/) +- libdnet (tested with v1.4, most versions assumed to work, + http://libdnet.sourceforge.net/) + +These libraries are _not_ used in the default Linux build. Instead, +internal Linux specific implementation is used. libpcap/libdnet are +more portable and they can be used by adding CONFIG_DNET_PCAP=y into +.config. They may also be selected automatically for other operating +systems. + + +Optional libraries for EAP-TLS, EAP-PEAP, and EAP-TTLS: +- openssl (tested with 0.9.7c and 0.9.7d, assumed to work with most + relatively recent versions; this is likely to be available with most + distributions, http://www.openssl.org/) + +This library is only needed when EAP-TLS, EAP-PEAP, or EAP-TTLS +support is enabled. WPA-PSK mode does not require this or EAPOL/EAP +implementation. A configuration file, .config, for compilation is +needed to enable IEEE 802.1X/EAPOL and EAP methods. Note that EAP-MD5, +EAP-GTC, EAP-OTP, and EAP-MSCHAPV2 cannot be used alone with WPA, so +they should only be enabled if testing the EAPOL/EAP state +machines. However, there can be used as inner authentication +algorithms with EAP-PEAP and EAP-TTLS. + +See Building and installing section below for more detailed +information about the wpa_supplicant build time configuration. + + + +WPA +--- + +The original security mechanism of IEEE 802.11 standard was not +designed to be strong and has proven to be insufficient for most +networks that require some kind of security. Task group I (Security) +of IEEE 802.11 working group (http://www.ieee802.org/11/) has worked +to address the flaws of the base standard and has in practice +completed its work in May 2004. The IEEE 802.11i amendment to the IEEE +802.11 standard was approved in June 2004 and this amendment is likely +to be published in July 2004. + +Wi-Fi Alliance (http://www.wi-fi.org/) used a draft version of the +IEEE 802.11i work (draft 3.0) to define a subset of the security +enhancements that can be implemented with existing wlan hardware. This +is called Wi-Fi Protected Access<TM> (WPA). This has now become a +mandatory component of interoperability testing and certification done +by Wi-Fi Alliance. Wi-Fi provides information about WPA at its web +site (http://www.wi-fi.org/OpenSection/protected_access.asp). + +IEEE 802.11 standard defined wired equivalent privacy (WEP) algorithm +for protecting wireless networks. WEP uses RC4 with 40-bit keys, +24-bit initialization vector (IV), and CRC32 to protect against packet +forgery. All these choices have proven to be insufficient: key space is +too small against current attacks, RC4 key scheduling is insufficient +(beginning of the pseudorandom stream should be skipped), IV space is +too small and IV reuse makes attacks easier, there is no replay +protection, and non-keyed authentication does not protect against bit +flipping packet data. + +WPA is an intermediate solution for the security issues. It uses +Temporal Key Integrity Protocol (TKIP) to replace WEP. TKIP is a +compromise on strong security and possibility to use existing +hardware. It still uses RC4 for the encryption like WEP, but with +per-packet RC4 keys. In addition, it implements replay protection, +keyed packet authentication mechanism (Michael MIC). + +Keys can be managed using two different mechanisms. WPA can either use +an external authentication server (e.g., RADIUS) and EAP just like +IEEE 802.1X is using or pre-shared keys without need for additional +servers. Wi-Fi calls these "WPA-Enterprise" and "WPA-Personal", +respectively. Both mechanisms will generate a master session key for +the Authenticator (AP) and Supplicant (client station). + +WPA implements a new key handshake (4-Way Handshake and Group Key +Handshake) for generating and exchanging data encryption keys between +the Authenticator and Supplicant. This handshake is also used to +verify that both Authenticator and Supplicant know the master session +key. These handshakes are identical regardless of the selected key +management mechanism (only the method for generating master session +key changes). + + + +IEEE 802.11i / WPA2 +------------------- + +The design for parts of IEEE 802.11i that were not included in WPA has +finished (May 2004) and this amendment to IEEE 802.11 was approved in +June 2004. Wi-Fi Alliance is using the final IEEE 802.11i as a new +version of WPA called WPA2. This includes, e.g., support for more +robust encryption algorithm (CCMP: AES in Counter mode with CBC-MAC) +to replace TKIP and optimizations for handoff (reduced number of +messages in initial key handshake, pre-authentication, and PMKSA caching). + +Some wireless LAN vendors are already providing support for CCMP in +their WPA products. There is no "official" interoperability +certification for CCMP and/or mixed modes using both TKIP and CCMP, so +some interoperability issues can be expected even though many +combinations seem to be working with equipment from different vendors. +Certification for WPA2 is likely to start during the second half of +2004. + + + +wpa_supplicant +-------------- + +wpa_supplicant is an implementation of the WPA Supplicant component, +i.e., the part that runs in the client stations. It implements WPA key +negotiation with a WPA Authenticator and EAP authentication with +Authentication Server. In addition, it controls the roaming and IEEE +802.11 authentication/association of the wlan driver. + +wpa_supplicant is designed to be a "daemon" program that runs in the +background and acts as the backend component controlling the wireless +connection. wpa_supplicant supports separate frontend programs and an +example text-based frontend, wpa_cli, is included with wpa_supplicant. + +Following steps are used when associating with an AP using WPA: + +- wpa_supplicant requests the kernel driver to scan neighboring BSSes +- wpa_supplicant selects a BSS based on its configuration +- wpa_supplicant requests the kernel driver to associate with the chosen + BSS +- If WPA-EAP: integrated IEEE 802.1X Supplicant or external Xsupplicant + completes EAP authentication with the authentication server (proxied + by the Authenticator in the AP) +- If WPA-EAP: master key is received from the IEEE 802.1X Supplicant +- If WPA-PSK: wpa_supplicant uses PSK as the master session key +- wpa_supplicant completes WPA 4-Way Handshake and Group Key Handshake + with the Authenticator (AP) +- wpa_supplicant configures encryption keys for unicast and broadcast +- normal data packets can be transmitted and received + + + +Building and installing +----------------------- + +In order to be able to build wpa_supplicant, you will first need to +select which parts of it will be included. This is done by creating a +build time configuration file, .config, in the wpa_supplicant root +directory. Configuration options are text lines using following +format: CONFIG_<option>=y. Lines starting with # are considered +comments and are ignored. See defconfig file for example configuration +and list of available option. + +The build time configuration can be used to select only the needed +features and limit the binary size and requirements for external +libraries. The main configuration parts are the selection of which +driver interfaces (e.g., hostap, madwifi, ..) and which authentication +methods (e.g., EAP-TLS, EAP-PEAP, ..) are included. + +Following build time configuration options are used to control IEEE +802.1X/EAPOL and EAP state machines and all EAP methods. Including +TLS, PEAP, or TTLS will require linking wpa_supplicant with openssl +library for TLS implementation. + +CONFIG_IEEE8021X_EAPOL=y +CONFIG_EAP_MD5=y +CONFIG_EAP_MSCHAPV2=y +CONFIG_EAP_TLS=y +CONFIG_EAP_PEAP=y +CONFIG_EAP_TTLS=y +CONFIG_EAP_GTC=y +CONFIG_EAP_OTP=y +CONFIG_EAP_SIM=y +CONFIG_EAP_AKA=y +CONFIG_EAP_PSK=y +CONFIG_EAP_LEAP=y + +Following option can be used to include GSM SIM/USIM interface for GSM/UMTS +authentication algorithm (for EAP-SIM/EAP-AKA). This requires pcsc-lite +(http://www.linuxnet.com/) for smart card access. + +CONFIG_PCSC=y + +Following option can be used to replace the native Linux packet socket +interface with libpcap/libdnet. + +CONFIG_DNET_PCAP=y + +Following options can be added to .config to select which driver +interfaces are included. Prism54.org driver is not yet complete and +Hermes driver interface needs to be downloaded from Agere (see above). +Most Linux driver need to include CONFIG_WIRELESS_EXTENSION. + +CONFIG_WIRELESS_EXTENSION=y +CONFIG_DRIVER_HOSTAP=y +CONFIG_DRIVER_PRISM54=y +CONFIG_DRIVER_HERMES=y +CONFIG_DRIVER_MADWIFI=y +CONFIG_DRIVER_ATMEL=y +CONFIG_DRIVER_WEXT=y +CONFIG_DRIVER_NDISWRAPPER=y +CONFIG_DRIVER_BROADCOM=y +CONFIG_DRIVER_IPW=y +CONFIG_DRIVER_BSD=y +CONFIG_DRIVER_NDIS=y + +Following example includes all features and driver interfaces that are +included in the wpa_supplicant package: + +CONFIG_DRIVER_HOSTAP=y +CONFIG_DRIVER_PRISM54=y +CONFIG_DRIVER_HERMES=y +CONFIG_DRIVER_MADWIFI=y +CONFIG_DRIVER_ATMEL=y +CONFIG_DRIVER_WEXT=y +CONFIG_DRIVER_NDISWRAPPER=y +CONFIG_DRIVER_BROADCOM=y +CONFIG_DRIVER_IPW=y +CONFIG_DRIVER_BSD=y +CONFIG_DRIVER_NDIS=y +CONFIG_WIRELESS_EXTENSION=y +CONFIG_IEEE8021X_EAPOL=y +CONFIG_EAP_MD5=y +CONFIG_EAP_MSCHAPV2=y +CONFIG_EAP_TLS=y +CONFIG_EAP_PEAP=y +CONFIG_EAP_TTLS=y +CONFIG_EAP_GTC=y +CONFIG_EAP_OTP=y +CONFIG_EAP_SIM=y +CONFIG_EAP_AKA=y +CONFIG_EAP_PSK=y +CONFIG_EAP_LEAP=y +CONFIG_PCSC=y + +EAP-PEAP and EAP-TTLS will automatically include configured EAP +methods (MD5, OTP, GTC, MSCHAPV2) for inner authentication selection. + + +After you have created a configuration file, you can build +wpa_supplicant and wpa_cli with 'make' command. You may then install +the binaries to a suitable system directory, e.g., /usr/local/bin. + +Example commands: + +# build wpa_supplicant and wpa_cli +make +# install binaries (this may need root privileges) +cp wpa_cli wpa_supplicant /usr/local/bin + + +You will need to make a configuration file, e.g., +/etc/wpa_supplicant.conf, with network configuration for the networks +you are going to use. Configuration file section below includes +explanation fo the configuration file format and includes various +examples. Once the configuration is ready, you can test whether the +configuration work by first running wpa_supplicant with following +command to start it on foreground with debugging enabled: + +wpa_supplicant -iwlan0 -c/etc/wpa_supplicant.conf -d + +Assuming everything goes fine, you can start using following command +to start wpa_supplicant on background without debugging: + +wpa_supplicant -iwlan0 -c/etc/wpa_supplicant.conf -B + +Please note that if you included more than one driver interface in the +build time configuration (.config), you may need to specify which +interface to use by including -D<driver name> option on the command +line. See following section for more details on command line options +for wpa_supplicant. + + + +Command line options +-------------------- + +usage: + wpa_supplicant [-BddehLqqvw] -i<ifname> -c<config file> [-D<driver>] \ + [-N -i<ifname> -c<conf> [-D<driver>] ...] + +options: + -B = run daemon in the background + -d = increase debugging verbosity (-dd even more) + -K = include keys (passwords, etc.) in debug output + -t = include timestamp in debug messages + -e = use external IEEE 802.1X Supplicant (e.g., xsupplicant) + (this disables the internal Supplicant) + -h = show this help text + -L = show license (GPL and BSD) + -q = decrease debugging verbosity (-qq even less) + -v = show version + -w = wait for interface to be added, if needed + -N = start describing new interface + +drivers: + hostap = Host AP driver (Intersil Prism2/2.5/3) [default] + (this can also be used with Linuxant DriverLoader) + prism54 = Prism54.org driver (Intersil Prism GT/Duette/Indigo) + not yet fully implemented + hermes = Agere Systems Inc. driver (Hermes-I/Hermes-II) + madwifi = MADWIFI 802.11 support (Atheros, etc.) + atmel = ATMEL AT76C5XXx (USB, PCMCIA) + wext = Linux wireless extensions (generic) + ndiswrapper = Linux ndiswrapper + broadcom = Broadcom wl.o driver + ipw = Intel ipw2100/2200 driver + bsd = BSD 802.11 support (Atheros, etc.) + ndis = Windows NDIS driver + +In most common cases, wpa_supplicant is started with + +wpa_supplicant -Bw -c/etc/wpa_supplicant.conf -iwlan0 + +This makes the process fork into background and wait for the wlan0 +interface if it is not available at startup time. + +The easiest way to debug problems, and to get debug log for bug +reports, is to start wpa_supplicant on foreground with debugging +enabled: + +wpa_supplicant -c/etc/wpa_supplicant.conf -iwlan0 -d + + +wpa_supplicant can control multiple interfaces (radios) either by +running one process for each interface separately or by running just +one process and list of options at command line. Each interface is +separated with -N argument. As an example, following command would +start wpa_supplicant for two interfaces: + +wpa_supplicant \ + -c wpa1.conf -i wlan0 -D hostap -N \ + -c wpa2.conf -i ath0 -D madwifi + + +Configuration file +------------------ + +wpa_supplicant is configured using a text file that lists all accepted +networks and security policies, including pre-shared keys. See +example configuration file, wpa_supplicant.conf, for detailed +information about the configuration format and supported fields. + +Changes to configuration file can be reloaded be sending SIGHUP signal +to wpa_supplicant ('killall -HUP wpa_supplicant'). Similarly, +reloading can be triggered with 'wpa_cli reconfigure' command. + +Configuration file can include one or more network blocks, e.g., one +for each used SSID. wpa_supplicant will automatically select the best +betwork based on the order of network blocks in the configuration +file, network security level (WPA/WPA2 is prefered), and signal +strength. + +Example configuration files for some common configurations: + +1) WPA-Personal (PSK) as home network and WPA-Enterprise with EAP-TLS as work + network + +# allow frontend (e.g., wpa_cli) to be used by all users in 'wheel' group +ctrl_interface=/var/run/wpa_supplicant +ctrl_interface_group=wheel +# +# home network; allow all valid ciphers +network={ + ssid="home" + scan_ssid=1 + key_mgmt=WPA-PSK + psk="very secret passphrase" +} +# +# work network; use EAP-TLS with WPA; allow only CCMP and TKIP ciphers +network={ + ssid="work" + scan_ssid=1 + key_mgmt=WPA-EAP + pairwise=CCMP TKIP + group=CCMP TKIP + eap=TLS + identity="user@example.com" + ca_cert="/etc/cert/ca.pem" + client_cert="/etc/cert/user.pem" + private_key="/etc/cert/user.prv" + private_key_passwd="password" +} + + +2) WPA-RADIUS/EAP-PEAP/MSCHAPv2 with RADIUS servers that use old peaplabel + (e.g., Funk Odyssey and SBR, Meetinghouse Aegis, Interlink RAD-Series) + +ctrl_interface=/var/run/wpa_supplicant +ctrl_interface_group=wheel +network={ + ssid="example" + scan_ssid=1 + key_mgmt=WPA-EAP + eap=PEAP + identity="user@example.com" + password="foobar" + ca_cert="/etc/cert/ca.pem" + phase1="peaplabel=0" + phase2="auth=MSCHAPV2" +} + + +3) EAP-TTLS/EAP-MD5-Challenge configuration with anonymous identity for the + unencrypted use. Real identity is sent only within an encrypted TLS tunnel. + +ctrl_interface=/var/run/wpa_supplicant +ctrl_interface_group=wheel +network={ + ssid="example" + scan_ssid=1 + key_mgmt=WPA-EAP + eap=TTLS + identity="user@example.com" + anonymous_identity="anonymous@example.com" + password="foobar" + ca_cert="/etc/cert/ca.pem" + phase2="auth=MD5" +} + + +4) IEEE 802.1X (i.e., no WPA) with dynamic WEP keys (require both unicast and + broadcast); use EAP-TLS for authentication + +ctrl_interface=/var/run/wpa_supplicant +ctrl_interface_group=wheel +network={ + ssid="1x-test" + scan_ssid=1 + key_mgmt=IEEE8021X + eap=TLS + identity="user@example.com" + ca_cert="/etc/cert/ca.pem" + client_cert="/etc/cert/user.pem" + private_key="/etc/cert/user.prv" + private_key_passwd="password" + eapol_flags=3 +} + + +5) Catch all example that allows more or less all configuration modes. The + configuration options are used based on what security policy is used in the + selected SSID. This is mostly for testing and is not recommended for normal + use. + +ctrl_interface=/var/run/wpa_supplicant +ctrl_interface_group=wheel +network={ + ssid="example" + scan_ssid=1 + key_mgmt=WPA-EAP WPA-PSK IEEE8021X NONE + pairwise=CCMP TKIP + group=CCMP TKIP WEP104 WEP40 + psk="very secret passphrase" + eap=TTLS PEAP TLS + identity="user@example.com" + password="foobar" + ca_cert="/etc/cert/ca.pem" + client_cert="/etc/cert/user.pem" + private_key="/etc/cert/user.prv" + private_key_passwd="password" + phase1="peaplabel=0" + ca_cert2="/etc/cert/ca2.pem" + client_cert2="/etc/cer/user.pem" + private_key2="/etc/cer/user.prv" + private_key2_passwd="password" +} + + + +Certificates +------------ + +Some EAP authentication methods require use of certificates. EAP-TLS +uses both server side and client certificates whereas EAP-PEAP and +EAP-TTLS only require the server side certificate. When client +certificate is used, a matching private key file has to also be +included in configuration. If the private key uses a passphrase, this +has to be configured in wpa_supplicant.conf ("private_key_passwd"). + +wpa_supplicant supports X.509 certificates in PEM and DER +formats. User certificate and private key can be included in the same +file. + +If the user certificate and private key is received in PKCS#12/PFX +format, they need to be converted to suitable PEM/DER format for +wpa_supplicant. This can be done, e.g., with following commands: + +# convert client certificate and private key to PEM format +openssl pkcs12 -in example.pfx -out user.pem -clcerts +# convert CA certificate (if included in PFX file) to PEM format +openssl pkcs12 -in example.pfx -out ca.pem -cacerts -nokeys + + + +wpa_cli +------- + +wpa_cli is a text-based frontend program for interacting with +wpa_supplicant. It is used to query current status, change +configuration, trigger events, and request interactive user input. + +wpa_cli can show the current authentication status, selected security +mode, dot11 and dot1x MIBs, etc. In addition, it can configuring some +variables like EAPOL state machine parameters and trigger events like +reassociation and IEEE 802.1X logoff/logon. wpa_cli provides a user +interface to request authentication information, like username and +password, if these are not included in the configuration. This can be +used to implement, e.g., one-time-passwords or generic token card +authentication where the authentication is based on a +challenge-response that uses an external device for generating the +response. + +The control interface of wpa_supplicant can be configured to allow +non-root user access (ctrl_interface_group in the configuration +file). This makes it possible to run wpa_cli with a normal user +account. + +wpa_cli supports two modes: interactive and command line. Both modes +share the same command set and the main difference is in interactive +mode providing access to unsolicited messages (event messages, +username/password requests). + +Interactive mode is started when wpa_cli is executed without including +the command as a command line parameter. Commands are then entered on +the wpa_cli prompt. In command line mode, the same commands are +entered as command line arguments for wpa_cli. + + +Interactive authentication parameters request + +When wpa_supplicant need authentication parameters, like username and +password, which are not present in the configuration file, it sends a +request message to all attached frontend programs, e.g., wpa_cli in +interactive mode. wpa_cli shows these requests with +"CTRL-REQ-<type>-<id>:<text>" prefix. <type> is IDENTITY, PASSWORD, or +OTP (one-time-password). <id> is a unique identifier for the current +network. <text> is description of the request. In case of OTP request, +it includes the challenge from the authentication server. + +The reply to these requests can be given with 'identity', 'password', +and 'otp' commands. <id> needs to be copied from the the matching +request. 'password' and 'otp' commands can be used regardless of +whether the request was for PASSWORD or OTP. The main difference +between these two commands is that values given with 'password' are +remembered as long as wpa_supplicant is running whereas values given +with 'otp' are used only once and then forgotten, i.e., wpa_supplicant +will ask frontend for a new value for every use. This can be used to +implement one-time-password lists and generic token card -based +authentication. + +Example request for password and a matching reply: + +CTRL-REQ-PASSWORD-1:Password needed for SSID foobar +> password 1 mysecretpassword + +Example request for generic token card challenge-response: + +CTRL-REQ-OTP-2:Challenge 1235663 needed for SSID foobar +> otp 2 9876 + + +wpa_cli commands + + status = get current WPA/EAPOL/EAP status + mib = get MIB variables (dot1x, dot11) + help = show this usage help + interface [ifname] = show interfaces/select interface + level <debug level> = change debug level + license = show full wpa_cli license + logoff = IEEE 802.1X EAPOL state machine logoff + logon = IEEE 802.1X EAPOL state machine logon + set = set variables (shows list of variables when run without arguments) + pmksa = show PMKSA cache + reassociate = force reassociation + reconfigure = force wpa_supplicant to re-read its configuration file + preauthenticate <BSSID> = force preauthentication + identity <network id> <identity> = configure identity for an SSID + password <network id> <password> = configure password for an SSID + otp <network id> <password> = configure one-time-password for an SSID + terminate = terminate wpa_supplicant + quit = exit wpa_cli + + + +Integrating with pcmcia-cs/cardmgr scripts +------------------------------------------ + +wpa_supplicant needs to be running when using a wireless network with +WPA. It can be started either from system startup scripts or from +pcmcia-cs/cardmgr scripts (when using PC Cards). WPA handshake must be +completed before data frames can be exchanged, so wpa_supplicant +should be started before DHCP client. + +Command line option '-w' can be used if wpa_supplicant is started +before the wireless LAN interface is present (e.g., before inserting +the PC Card) or is not yet up. + +For example, following small changes to pcmcia-cs scripts can be used +to enable WPA support: + +Add MODE="Managed" and WPA="y" to the network scheme in +/etc/pcmcia/wireless.opts. + +Add the following block to the end of 'start' action handler in +/etc/pcmcia/wireless: + + if [ "$WPA" = "y" -a -x /usr/local/bin/wpa_supplicant ]; then + /usr/local/bin/wpa_supplicant -Bw -c/etc/wpa_supplicant.conf \ + -i$DEVICE + fi + +Add the following block to the end of 'stop' action handler (may need +to be separated from other actions) in /etc/pcmcia/wireless: + + if [ "$WPA" = "y" -a -x /usr/local/bin/wpa_supplicant ]; then + killall wpa_supplicant + fi + +This will make cardmgr start wpa_supplicant when the card is plugged +in. wpa_supplicant will wait until the interface is set up--either +when a static IP address is configured or when DHCP client is +started--and will then negotiate keys with the AP. + + + +Optional integration with Xsupplicant +------------------------------------- + +wpa_supplicant has an integrated IEEE 802.1X Supplicant that supports +most commonly used EAP methods. In addition, wpa_supplicant has an +experimental interface for integrating it with Xsupplicant +(http://www.open1x.org/) for the WPA with EAP authentication. + +When using WPA-EAP, both wpa_supplicant and Xsupplicant must be +configured with the network security policy. See Xsupplicant documents +for information about its configuration. Please also note, that a new +command line option -W (enable WPA) must be used when starting +xsupplicant. + +Example configuration for xsupplicant: + +network_list = all +default_netname = jkm + +jkm +{ + type = wireless + allow_types = eap_peap + identity = <BEGIN_ID>jkm<END_ID> + eap-peap { + random_file = /dev/urandom + root_cert = /home/jkm/CA.pem + chunk_size = 1398 + allow_types = eap_mschapv2 + eap-mschapv2 { + username = <BEGIN_UNAME>jkm<END_UNAME> + password = <BEGIN_PASS>jkm<END_PASS> + } + } +} + + +Example configuration for wpa_supplicant: + +network={ + ssid="jkm" + key_mgmt=WPA-EAP +} + + +Both wpa_supplicant and xsupplicant need to be started. Please remember +to add '-W' option for xsupplicant in order to provide keying material +for wpa_supplicant and '-e' option for wpa_supplicant to disable internal +IEEE 802.1X implementation. + +wpa_supplicant -iwlan0 -cwpa_supplicant.conf -e +xsupplicant -iwlan0 -cxsupplicant.conf -W diff --git a/contrib/wpa_supplicant/aes.c b/contrib/wpa_supplicant/aes.c new file mode 100644 index 0000000..eabebd0 --- /dev/null +++ b/contrib/wpa_supplicant/aes.c @@ -0,0 +1,1132 @@ +/* + * Modifications to public domain implementation: + * - support only 128-bit keys + * - cleanup + * Copyright (c) 2003-2004, Jouni Malinen <jkmaline@cc.hut.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +/** + * rijndael-alg-fst.c + * + * @version 3.0 (December 2000) + * + * Optimised ANSI C code for the Rijndael cipher (now AES) + * + * @author Vincent Rijmen <vincent.rijmen@esat.kuleuven.ac.be> + * @author Antoon Bosselaers <antoon.bosselaers@esat.kuleuven.ac.be> + * @author Paulo Barreto <paulo.barreto@terra.com.br> + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#define FULL_UNROLL + + +/* +Te0[x] = S [x].[02, 01, 01, 03]; +Te1[x] = S [x].[03, 02, 01, 01]; +Te2[x] = S [x].[01, 03, 02, 01]; +Te3[x] = S [x].[01, 01, 03, 02]; +Te4[x] = S [x].[01, 01, 01, 01]; + +Td0[x] = Si[x].[0e, 09, 0d, 0b]; +Td1[x] = Si[x].[0b, 0e, 09, 0d]; +Td2[x] = Si[x].[0d, 0b, 0e, 09]; +Td3[x] = Si[x].[09, 0d, 0b, 0e]; +Td4[x] = Si[x].[01, 01, 01, 01]; +*/ + +static const u32 Te0[256] = { + 0xc66363a5U, 0xf87c7c84U, 0xee777799U, 0xf67b7b8dU, + 0xfff2f20dU, 0xd66b6bbdU, 0xde6f6fb1U, 0x91c5c554U, + 0x60303050U, 0x02010103U, 0xce6767a9U, 0x562b2b7dU, + 0xe7fefe19U, 0xb5d7d762U, 0x4dababe6U, 0xec76769aU, + 0x8fcaca45U, 0x1f82829dU, 0x89c9c940U, 0xfa7d7d87U, + 0xeffafa15U, 0xb25959ebU, 0x8e4747c9U, 0xfbf0f00bU, + 0x41adadecU, 0xb3d4d467U, 0x5fa2a2fdU, 0x45afafeaU, + 0x239c9cbfU, 0x53a4a4f7U, 0xe4727296U, 0x9bc0c05bU, + 0x75b7b7c2U, 0xe1fdfd1cU, 0x3d9393aeU, 0x4c26266aU, + 0x6c36365aU, 0x7e3f3f41U, 0xf5f7f702U, 0x83cccc4fU, + 0x6834345cU, 0x51a5a5f4U, 0xd1e5e534U, 0xf9f1f108U, + 0xe2717193U, 0xabd8d873U, 0x62313153U, 0x2a15153fU, + 0x0804040cU, 0x95c7c752U, 0x46232365U, 0x9dc3c35eU, + 0x30181828U, 0x379696a1U, 0x0a05050fU, 0x2f9a9ab5U, + 0x0e070709U, 0x24121236U, 0x1b80809bU, 0xdfe2e23dU, + 0xcdebeb26U, 0x4e272769U, 0x7fb2b2cdU, 0xea75759fU, + 0x1209091bU, 0x1d83839eU, 0x582c2c74U, 0x341a1a2eU, + 0x361b1b2dU, 0xdc6e6eb2U, 0xb45a5aeeU, 0x5ba0a0fbU, + 0xa45252f6U, 0x763b3b4dU, 0xb7d6d661U, 0x7db3b3ceU, + 0x5229297bU, 0xdde3e33eU, 0x5e2f2f71U, 0x13848497U, + 0xa65353f5U, 0xb9d1d168U, 0x00000000U, 0xc1eded2cU, + 0x40202060U, 0xe3fcfc1fU, 0x79b1b1c8U, 0xb65b5bedU, + 0xd46a6abeU, 0x8dcbcb46U, 0x67bebed9U, 0x7239394bU, + 0x944a4adeU, 0x984c4cd4U, 0xb05858e8U, 0x85cfcf4aU, + 0xbbd0d06bU, 0xc5efef2aU, 0x4faaaae5U, 0xedfbfb16U, + 0x864343c5U, 0x9a4d4dd7U, 0x66333355U, 0x11858594U, + 0x8a4545cfU, 0xe9f9f910U, 0x04020206U, 0xfe7f7f81U, + 0xa05050f0U, 0x783c3c44U, 0x259f9fbaU, 0x4ba8a8e3U, + 0xa25151f3U, 0x5da3a3feU, 0x804040c0U, 0x058f8f8aU, + 0x3f9292adU, 0x219d9dbcU, 0x70383848U, 0xf1f5f504U, + 0x63bcbcdfU, 0x77b6b6c1U, 0xafdada75U, 0x42212163U, + 0x20101030U, 0xe5ffff1aU, 0xfdf3f30eU, 0xbfd2d26dU, + 0x81cdcd4cU, 0x180c0c14U, 0x26131335U, 0xc3ecec2fU, + 0xbe5f5fe1U, 0x359797a2U, 0x884444ccU, 0x2e171739U, + 0x93c4c457U, 0x55a7a7f2U, 0xfc7e7e82U, 0x7a3d3d47U, + 0xc86464acU, 0xba5d5de7U, 0x3219192bU, 0xe6737395U, + 0xc06060a0U, 0x19818198U, 0x9e4f4fd1U, 0xa3dcdc7fU, + 0x44222266U, 0x542a2a7eU, 0x3b9090abU, 0x0b888883U, + 0x8c4646caU, 0xc7eeee29U, 0x6bb8b8d3U, 0x2814143cU, + 0xa7dede79U, 0xbc5e5ee2U, 0x160b0b1dU, 0xaddbdb76U, + 0xdbe0e03bU, 0x64323256U, 0x743a3a4eU, 0x140a0a1eU, + 0x924949dbU, 0x0c06060aU, 0x4824246cU, 0xb85c5ce4U, + 0x9fc2c25dU, 0xbdd3d36eU, 0x43acacefU, 0xc46262a6U, + 0x399191a8U, 0x319595a4U, 0xd3e4e437U, 0xf279798bU, + 0xd5e7e732U, 0x8bc8c843U, 0x6e373759U, 0xda6d6db7U, + 0x018d8d8cU, 0xb1d5d564U, 0x9c4e4ed2U, 0x49a9a9e0U, + 0xd86c6cb4U, 0xac5656faU, 0xf3f4f407U, 0xcfeaea25U, + 0xca6565afU, 0xf47a7a8eU, 0x47aeaee9U, 0x10080818U, + 0x6fbabad5U, 0xf0787888U, 0x4a25256fU, 0x5c2e2e72U, + 0x381c1c24U, 0x57a6a6f1U, 0x73b4b4c7U, 0x97c6c651U, + 0xcbe8e823U, 0xa1dddd7cU, 0xe874749cU, 0x3e1f1f21U, + 0x964b4bddU, 0x61bdbddcU, 0x0d8b8b86U, 0x0f8a8a85U, + 0xe0707090U, 0x7c3e3e42U, 0x71b5b5c4U, 0xcc6666aaU, + 0x904848d8U, 0x06030305U, 0xf7f6f601U, 0x1c0e0e12U, + 0xc26161a3U, 0x6a35355fU, 0xae5757f9U, 0x69b9b9d0U, + 0x17868691U, 0x99c1c158U, 0x3a1d1d27U, 0x279e9eb9U, + 0xd9e1e138U, 0xebf8f813U, 0x2b9898b3U, 0x22111133U, + 0xd26969bbU, 0xa9d9d970U, 0x078e8e89U, 0x339494a7U, + 0x2d9b9bb6U, 0x3c1e1e22U, 0x15878792U, 0xc9e9e920U, + 0x87cece49U, 0xaa5555ffU, 0x50282878U, 0xa5dfdf7aU, + 0x038c8c8fU, 0x59a1a1f8U, 0x09898980U, 0x1a0d0d17U, + 0x65bfbfdaU, 0xd7e6e631U, 0x844242c6U, 0xd06868b8U, + 0x824141c3U, 0x299999b0U, 0x5a2d2d77U, 0x1e0f0f11U, + 0x7bb0b0cbU, 0xa85454fcU, 0x6dbbbbd6U, 0x2c16163aU, +}; +static const u32 Te1[256] = { + 0xa5c66363U, 0x84f87c7cU, 0x99ee7777U, 0x8df67b7bU, + 0x0dfff2f2U, 0xbdd66b6bU, 0xb1de6f6fU, 0x5491c5c5U, + 0x50603030U, 0x03020101U, 0xa9ce6767U, 0x7d562b2bU, + 0x19e7fefeU, 0x62b5d7d7U, 0xe64dababU, 0x9aec7676U, + 0x458fcacaU, 0x9d1f8282U, 0x4089c9c9U, 0x87fa7d7dU, + 0x15effafaU, 0xebb25959U, 0xc98e4747U, 0x0bfbf0f0U, + 0xec41adadU, 0x67b3d4d4U, 0xfd5fa2a2U, 0xea45afafU, + 0xbf239c9cU, 0xf753a4a4U, 0x96e47272U, 0x5b9bc0c0U, + 0xc275b7b7U, 0x1ce1fdfdU, 0xae3d9393U, 0x6a4c2626U, + 0x5a6c3636U, 0x417e3f3fU, 0x02f5f7f7U, 0x4f83ccccU, + 0x5c683434U, 0xf451a5a5U, 0x34d1e5e5U, 0x08f9f1f1U, + 0x93e27171U, 0x73abd8d8U, 0x53623131U, 0x3f2a1515U, + 0x0c080404U, 0x5295c7c7U, 0x65462323U, 0x5e9dc3c3U, + 0x28301818U, 0xa1379696U, 0x0f0a0505U, 0xb52f9a9aU, + 0x090e0707U, 0x36241212U, 0x9b1b8080U, 0x3ddfe2e2U, + 0x26cdebebU, 0x694e2727U, 0xcd7fb2b2U, 0x9fea7575U, + 0x1b120909U, 0x9e1d8383U, 0x74582c2cU, 0x2e341a1aU, + 0x2d361b1bU, 0xb2dc6e6eU, 0xeeb45a5aU, 0xfb5ba0a0U, + 0xf6a45252U, 0x4d763b3bU, 0x61b7d6d6U, 0xce7db3b3U, + 0x7b522929U, 0x3edde3e3U, 0x715e2f2fU, 0x97138484U, + 0xf5a65353U, 0x68b9d1d1U, 0x00000000U, 0x2cc1ededU, + 0x60402020U, 0x1fe3fcfcU, 0xc879b1b1U, 0xedb65b5bU, + 0xbed46a6aU, 0x468dcbcbU, 0xd967bebeU, 0x4b723939U, + 0xde944a4aU, 0xd4984c4cU, 0xe8b05858U, 0x4a85cfcfU, + 0x6bbbd0d0U, 0x2ac5efefU, 0xe54faaaaU, 0x16edfbfbU, + 0xc5864343U, 0xd79a4d4dU, 0x55663333U, 0x94118585U, + 0xcf8a4545U, 0x10e9f9f9U, 0x06040202U, 0x81fe7f7fU, + 0xf0a05050U, 0x44783c3cU, 0xba259f9fU, 0xe34ba8a8U, + 0xf3a25151U, 0xfe5da3a3U, 0xc0804040U, 0x8a058f8fU, + 0xad3f9292U, 0xbc219d9dU, 0x48703838U, 0x04f1f5f5U, + 0xdf63bcbcU, 0xc177b6b6U, 0x75afdadaU, 0x63422121U, + 0x30201010U, 0x1ae5ffffU, 0x0efdf3f3U, 0x6dbfd2d2U, + 0x4c81cdcdU, 0x14180c0cU, 0x35261313U, 0x2fc3ececU, + 0xe1be5f5fU, 0xa2359797U, 0xcc884444U, 0x392e1717U, + 0x5793c4c4U, 0xf255a7a7U, 0x82fc7e7eU, 0x477a3d3dU, + 0xacc86464U, 0xe7ba5d5dU, 0x2b321919U, 0x95e67373U, + 0xa0c06060U, 0x98198181U, 0xd19e4f4fU, 0x7fa3dcdcU, + 0x66442222U, 0x7e542a2aU, 0xab3b9090U, 0x830b8888U, + 0xca8c4646U, 0x29c7eeeeU, 0xd36bb8b8U, 0x3c281414U, + 0x79a7dedeU, 0xe2bc5e5eU, 0x1d160b0bU, 0x76addbdbU, + 0x3bdbe0e0U, 0x56643232U, 0x4e743a3aU, 0x1e140a0aU, + 0xdb924949U, 0x0a0c0606U, 0x6c482424U, 0xe4b85c5cU, + 0x5d9fc2c2U, 0x6ebdd3d3U, 0xef43acacU, 0xa6c46262U, + 0xa8399191U, 0xa4319595U, 0x37d3e4e4U, 0x8bf27979U, + 0x32d5e7e7U, 0x438bc8c8U, 0x596e3737U, 0xb7da6d6dU, + 0x8c018d8dU, 0x64b1d5d5U, 0xd29c4e4eU, 0xe049a9a9U, + 0xb4d86c6cU, 0xfaac5656U, 0x07f3f4f4U, 0x25cfeaeaU, + 0xafca6565U, 0x8ef47a7aU, 0xe947aeaeU, 0x18100808U, + 0xd56fbabaU, 0x88f07878U, 0x6f4a2525U, 0x725c2e2eU, + 0x24381c1cU, 0xf157a6a6U, 0xc773b4b4U, 0x5197c6c6U, + 0x23cbe8e8U, 0x7ca1ddddU, 0x9ce87474U, 0x213e1f1fU, + 0xdd964b4bU, 0xdc61bdbdU, 0x860d8b8bU, 0x850f8a8aU, + 0x90e07070U, 0x427c3e3eU, 0xc471b5b5U, 0xaacc6666U, + 0xd8904848U, 0x05060303U, 0x01f7f6f6U, 0x121c0e0eU, + 0xa3c26161U, 0x5f6a3535U, 0xf9ae5757U, 0xd069b9b9U, + 0x91178686U, 0x5899c1c1U, 0x273a1d1dU, 0xb9279e9eU, + 0x38d9e1e1U, 0x13ebf8f8U, 0xb32b9898U, 0x33221111U, + 0xbbd26969U, 0x70a9d9d9U, 0x89078e8eU, 0xa7339494U, + 0xb62d9b9bU, 0x223c1e1eU, 0x92158787U, 0x20c9e9e9U, + 0x4987ceceU, 0xffaa5555U, 0x78502828U, 0x7aa5dfdfU, + 0x8f038c8cU, 0xf859a1a1U, 0x80098989U, 0x171a0d0dU, + 0xda65bfbfU, 0x31d7e6e6U, 0xc6844242U, 0xb8d06868U, + 0xc3824141U, 0xb0299999U, 0x775a2d2dU, 0x111e0f0fU, + 0xcb7bb0b0U, 0xfca85454U, 0xd66dbbbbU, 0x3a2c1616U, +}; +static const u32 Te2[256] = { + 0x63a5c663U, 0x7c84f87cU, 0x7799ee77U, 0x7b8df67bU, + 0xf20dfff2U, 0x6bbdd66bU, 0x6fb1de6fU, 0xc55491c5U, + 0x30506030U, 0x01030201U, 0x67a9ce67U, 0x2b7d562bU, + 0xfe19e7feU, 0xd762b5d7U, 0xabe64dabU, 0x769aec76U, + 0xca458fcaU, 0x829d1f82U, 0xc94089c9U, 0x7d87fa7dU, + 0xfa15effaU, 0x59ebb259U, 0x47c98e47U, 0xf00bfbf0U, + 0xadec41adU, 0xd467b3d4U, 0xa2fd5fa2U, 0xafea45afU, + 0x9cbf239cU, 0xa4f753a4U, 0x7296e472U, 0xc05b9bc0U, + 0xb7c275b7U, 0xfd1ce1fdU, 0x93ae3d93U, 0x266a4c26U, + 0x365a6c36U, 0x3f417e3fU, 0xf702f5f7U, 0xcc4f83ccU, + 0x345c6834U, 0xa5f451a5U, 0xe534d1e5U, 0xf108f9f1U, + 0x7193e271U, 0xd873abd8U, 0x31536231U, 0x153f2a15U, + 0x040c0804U, 0xc75295c7U, 0x23654623U, 0xc35e9dc3U, + 0x18283018U, 0x96a13796U, 0x050f0a05U, 0x9ab52f9aU, + 0x07090e07U, 0x12362412U, 0x809b1b80U, 0xe23ddfe2U, + 0xeb26cdebU, 0x27694e27U, 0xb2cd7fb2U, 0x759fea75U, + 0x091b1209U, 0x839e1d83U, 0x2c74582cU, 0x1a2e341aU, + 0x1b2d361bU, 0x6eb2dc6eU, 0x5aeeb45aU, 0xa0fb5ba0U, + 0x52f6a452U, 0x3b4d763bU, 0xd661b7d6U, 0xb3ce7db3U, + 0x297b5229U, 0xe33edde3U, 0x2f715e2fU, 0x84971384U, + 0x53f5a653U, 0xd168b9d1U, 0x00000000U, 0xed2cc1edU, + 0x20604020U, 0xfc1fe3fcU, 0xb1c879b1U, 0x5bedb65bU, + 0x6abed46aU, 0xcb468dcbU, 0xbed967beU, 0x394b7239U, + 0x4ade944aU, 0x4cd4984cU, 0x58e8b058U, 0xcf4a85cfU, + 0xd06bbbd0U, 0xef2ac5efU, 0xaae54faaU, 0xfb16edfbU, + 0x43c58643U, 0x4dd79a4dU, 0x33556633U, 0x85941185U, + 0x45cf8a45U, 0xf910e9f9U, 0x02060402U, 0x7f81fe7fU, + 0x50f0a050U, 0x3c44783cU, 0x9fba259fU, 0xa8e34ba8U, + 0x51f3a251U, 0xa3fe5da3U, 0x40c08040U, 0x8f8a058fU, + 0x92ad3f92U, 0x9dbc219dU, 0x38487038U, 0xf504f1f5U, + 0xbcdf63bcU, 0xb6c177b6U, 0xda75afdaU, 0x21634221U, + 0x10302010U, 0xff1ae5ffU, 0xf30efdf3U, 0xd26dbfd2U, + 0xcd4c81cdU, 0x0c14180cU, 0x13352613U, 0xec2fc3ecU, + 0x5fe1be5fU, 0x97a23597U, 0x44cc8844U, 0x17392e17U, + 0xc45793c4U, 0xa7f255a7U, 0x7e82fc7eU, 0x3d477a3dU, + 0x64acc864U, 0x5de7ba5dU, 0x192b3219U, 0x7395e673U, + 0x60a0c060U, 0x81981981U, 0x4fd19e4fU, 0xdc7fa3dcU, + 0x22664422U, 0x2a7e542aU, 0x90ab3b90U, 0x88830b88U, + 0x46ca8c46U, 0xee29c7eeU, 0xb8d36bb8U, 0x143c2814U, + 0xde79a7deU, 0x5ee2bc5eU, 0x0b1d160bU, 0xdb76addbU, + 0xe03bdbe0U, 0x32566432U, 0x3a4e743aU, 0x0a1e140aU, + 0x49db9249U, 0x060a0c06U, 0x246c4824U, 0x5ce4b85cU, + 0xc25d9fc2U, 0xd36ebdd3U, 0xacef43acU, 0x62a6c462U, + 0x91a83991U, 0x95a43195U, 0xe437d3e4U, 0x798bf279U, + 0xe732d5e7U, 0xc8438bc8U, 0x37596e37U, 0x6db7da6dU, + 0x8d8c018dU, 0xd564b1d5U, 0x4ed29c4eU, 0xa9e049a9U, + 0x6cb4d86cU, 0x56faac56U, 0xf407f3f4U, 0xea25cfeaU, + 0x65afca65U, 0x7a8ef47aU, 0xaee947aeU, 0x08181008U, + 0xbad56fbaU, 0x7888f078U, 0x256f4a25U, 0x2e725c2eU, + 0x1c24381cU, 0xa6f157a6U, 0xb4c773b4U, 0xc65197c6U, + 0xe823cbe8U, 0xdd7ca1ddU, 0x749ce874U, 0x1f213e1fU, + 0x4bdd964bU, 0xbddc61bdU, 0x8b860d8bU, 0x8a850f8aU, + 0x7090e070U, 0x3e427c3eU, 0xb5c471b5U, 0x66aacc66U, + 0x48d89048U, 0x03050603U, 0xf601f7f6U, 0x0e121c0eU, + 0x61a3c261U, 0x355f6a35U, 0x57f9ae57U, 0xb9d069b9U, + 0x86911786U, 0xc15899c1U, 0x1d273a1dU, 0x9eb9279eU, + 0xe138d9e1U, 0xf813ebf8U, 0x98b32b98U, 0x11332211U, + 0x69bbd269U, 0xd970a9d9U, 0x8e89078eU, 0x94a73394U, + 0x9bb62d9bU, 0x1e223c1eU, 0x87921587U, 0xe920c9e9U, + 0xce4987ceU, 0x55ffaa55U, 0x28785028U, 0xdf7aa5dfU, + 0x8c8f038cU, 0xa1f859a1U, 0x89800989U, 0x0d171a0dU, + 0xbfda65bfU, 0xe631d7e6U, 0x42c68442U, 0x68b8d068U, + 0x41c38241U, 0x99b02999U, 0x2d775a2dU, 0x0f111e0fU, + 0xb0cb7bb0U, 0x54fca854U, 0xbbd66dbbU, 0x163a2c16U, +}; +static const u32 Te3[256] = { + + 0x6363a5c6U, 0x7c7c84f8U, 0x777799eeU, 0x7b7b8df6U, + 0xf2f20dffU, 0x6b6bbdd6U, 0x6f6fb1deU, 0xc5c55491U, + 0x30305060U, 0x01010302U, 0x6767a9ceU, 0x2b2b7d56U, + 0xfefe19e7U, 0xd7d762b5U, 0xababe64dU, 0x76769aecU, + 0xcaca458fU, 0x82829d1fU, 0xc9c94089U, 0x7d7d87faU, + 0xfafa15efU, 0x5959ebb2U, 0x4747c98eU, 0xf0f00bfbU, + 0xadadec41U, 0xd4d467b3U, 0xa2a2fd5fU, 0xafafea45U, + 0x9c9cbf23U, 0xa4a4f753U, 0x727296e4U, 0xc0c05b9bU, + 0xb7b7c275U, 0xfdfd1ce1U, 0x9393ae3dU, 0x26266a4cU, + 0x36365a6cU, 0x3f3f417eU, 0xf7f702f5U, 0xcccc4f83U, + 0x34345c68U, 0xa5a5f451U, 0xe5e534d1U, 0xf1f108f9U, + 0x717193e2U, 0xd8d873abU, 0x31315362U, 0x15153f2aU, + 0x04040c08U, 0xc7c75295U, 0x23236546U, 0xc3c35e9dU, + 0x18182830U, 0x9696a137U, 0x05050f0aU, 0x9a9ab52fU, + 0x0707090eU, 0x12123624U, 0x80809b1bU, 0xe2e23ddfU, + 0xebeb26cdU, 0x2727694eU, 0xb2b2cd7fU, 0x75759feaU, + 0x09091b12U, 0x83839e1dU, 0x2c2c7458U, 0x1a1a2e34U, + 0x1b1b2d36U, 0x6e6eb2dcU, 0x5a5aeeb4U, 0xa0a0fb5bU, + 0x5252f6a4U, 0x3b3b4d76U, 0xd6d661b7U, 0xb3b3ce7dU, + 0x29297b52U, 0xe3e33eddU, 0x2f2f715eU, 0x84849713U, + 0x5353f5a6U, 0xd1d168b9U, 0x00000000U, 0xeded2cc1U, + 0x20206040U, 0xfcfc1fe3U, 0xb1b1c879U, 0x5b5bedb6U, + 0x6a6abed4U, 0xcbcb468dU, 0xbebed967U, 0x39394b72U, + 0x4a4ade94U, 0x4c4cd498U, 0x5858e8b0U, 0xcfcf4a85U, + 0xd0d06bbbU, 0xefef2ac5U, 0xaaaae54fU, 0xfbfb16edU, + 0x4343c586U, 0x4d4dd79aU, 0x33335566U, 0x85859411U, + 0x4545cf8aU, 0xf9f910e9U, 0x02020604U, 0x7f7f81feU, + 0x5050f0a0U, 0x3c3c4478U, 0x9f9fba25U, 0xa8a8e34bU, + 0x5151f3a2U, 0xa3a3fe5dU, 0x4040c080U, 0x8f8f8a05U, + 0x9292ad3fU, 0x9d9dbc21U, 0x38384870U, 0xf5f504f1U, + 0xbcbcdf63U, 0xb6b6c177U, 0xdada75afU, 0x21216342U, + 0x10103020U, 0xffff1ae5U, 0xf3f30efdU, 0xd2d26dbfU, + 0xcdcd4c81U, 0x0c0c1418U, 0x13133526U, 0xecec2fc3U, + 0x5f5fe1beU, 0x9797a235U, 0x4444cc88U, 0x1717392eU, + 0xc4c45793U, 0xa7a7f255U, 0x7e7e82fcU, 0x3d3d477aU, + 0x6464acc8U, 0x5d5de7baU, 0x19192b32U, 0x737395e6U, + 0x6060a0c0U, 0x81819819U, 0x4f4fd19eU, 0xdcdc7fa3U, + 0x22226644U, 0x2a2a7e54U, 0x9090ab3bU, 0x8888830bU, + 0x4646ca8cU, 0xeeee29c7U, 0xb8b8d36bU, 0x14143c28U, + 0xdede79a7U, 0x5e5ee2bcU, 0x0b0b1d16U, 0xdbdb76adU, + 0xe0e03bdbU, 0x32325664U, 0x3a3a4e74U, 0x0a0a1e14U, + 0x4949db92U, 0x06060a0cU, 0x24246c48U, 0x5c5ce4b8U, + 0xc2c25d9fU, 0xd3d36ebdU, 0xacacef43U, 0x6262a6c4U, + 0x9191a839U, 0x9595a431U, 0xe4e437d3U, 0x79798bf2U, + 0xe7e732d5U, 0xc8c8438bU, 0x3737596eU, 0x6d6db7daU, + 0x8d8d8c01U, 0xd5d564b1U, 0x4e4ed29cU, 0xa9a9e049U, + 0x6c6cb4d8U, 0x5656faacU, 0xf4f407f3U, 0xeaea25cfU, + 0x6565afcaU, 0x7a7a8ef4U, 0xaeaee947U, 0x08081810U, + 0xbabad56fU, 0x787888f0U, 0x25256f4aU, 0x2e2e725cU, + 0x1c1c2438U, 0xa6a6f157U, 0xb4b4c773U, 0xc6c65197U, + 0xe8e823cbU, 0xdddd7ca1U, 0x74749ce8U, 0x1f1f213eU, + 0x4b4bdd96U, 0xbdbddc61U, 0x8b8b860dU, 0x8a8a850fU, + 0x707090e0U, 0x3e3e427cU, 0xb5b5c471U, 0x6666aaccU, + 0x4848d890U, 0x03030506U, 0xf6f601f7U, 0x0e0e121cU, + 0x6161a3c2U, 0x35355f6aU, 0x5757f9aeU, 0xb9b9d069U, + 0x86869117U, 0xc1c15899U, 0x1d1d273aU, 0x9e9eb927U, + 0xe1e138d9U, 0xf8f813ebU, 0x9898b32bU, 0x11113322U, + 0x6969bbd2U, 0xd9d970a9U, 0x8e8e8907U, 0x9494a733U, + 0x9b9bb62dU, 0x1e1e223cU, 0x87879215U, 0xe9e920c9U, + 0xcece4987U, 0x5555ffaaU, 0x28287850U, 0xdfdf7aa5U, + 0x8c8c8f03U, 0xa1a1f859U, 0x89898009U, 0x0d0d171aU, + 0xbfbfda65U, 0xe6e631d7U, 0x4242c684U, 0x6868b8d0U, + 0x4141c382U, 0x9999b029U, 0x2d2d775aU, 0x0f0f111eU, + 0xb0b0cb7bU, 0x5454fca8U, 0xbbbbd66dU, 0x16163a2cU, +}; +static const u32 Te4[256] = { + 0x63636363U, 0x7c7c7c7cU, 0x77777777U, 0x7b7b7b7bU, + 0xf2f2f2f2U, 0x6b6b6b6bU, 0x6f6f6f6fU, 0xc5c5c5c5U, + 0x30303030U, 0x01010101U, 0x67676767U, 0x2b2b2b2bU, + 0xfefefefeU, 0xd7d7d7d7U, 0xababababU, 0x76767676U, + 0xcacacacaU, 0x82828282U, 0xc9c9c9c9U, 0x7d7d7d7dU, + 0xfafafafaU, 0x59595959U, 0x47474747U, 0xf0f0f0f0U, + 0xadadadadU, 0xd4d4d4d4U, 0xa2a2a2a2U, 0xafafafafU, + 0x9c9c9c9cU, 0xa4a4a4a4U, 0x72727272U, 0xc0c0c0c0U, + 0xb7b7b7b7U, 0xfdfdfdfdU, 0x93939393U, 0x26262626U, + 0x36363636U, 0x3f3f3f3fU, 0xf7f7f7f7U, 0xccccccccU, + 0x34343434U, 0xa5a5a5a5U, 0xe5e5e5e5U, 0xf1f1f1f1U, + 0x71717171U, 0xd8d8d8d8U, 0x31313131U, 0x15151515U, + 0x04040404U, 0xc7c7c7c7U, 0x23232323U, 0xc3c3c3c3U, + 0x18181818U, 0x96969696U, 0x05050505U, 0x9a9a9a9aU, + 0x07070707U, 0x12121212U, 0x80808080U, 0xe2e2e2e2U, + 0xebebebebU, 0x27272727U, 0xb2b2b2b2U, 0x75757575U, + 0x09090909U, 0x83838383U, 0x2c2c2c2cU, 0x1a1a1a1aU, + 0x1b1b1b1bU, 0x6e6e6e6eU, 0x5a5a5a5aU, 0xa0a0a0a0U, + 0x52525252U, 0x3b3b3b3bU, 0xd6d6d6d6U, 0xb3b3b3b3U, + 0x29292929U, 0xe3e3e3e3U, 0x2f2f2f2fU, 0x84848484U, + 0x53535353U, 0xd1d1d1d1U, 0x00000000U, 0xededededU, + 0x20202020U, 0xfcfcfcfcU, 0xb1b1b1b1U, 0x5b5b5b5bU, + 0x6a6a6a6aU, 0xcbcbcbcbU, 0xbebebebeU, 0x39393939U, + 0x4a4a4a4aU, 0x4c4c4c4cU, 0x58585858U, 0xcfcfcfcfU, + 0xd0d0d0d0U, 0xefefefefU, 0xaaaaaaaaU, 0xfbfbfbfbU, + 0x43434343U, 0x4d4d4d4dU, 0x33333333U, 0x85858585U, + 0x45454545U, 0xf9f9f9f9U, 0x02020202U, 0x7f7f7f7fU, + 0x50505050U, 0x3c3c3c3cU, 0x9f9f9f9fU, 0xa8a8a8a8U, + 0x51515151U, 0xa3a3a3a3U, 0x40404040U, 0x8f8f8f8fU, + 0x92929292U, 0x9d9d9d9dU, 0x38383838U, 0xf5f5f5f5U, + 0xbcbcbcbcU, 0xb6b6b6b6U, 0xdadadadaU, 0x21212121U, + 0x10101010U, 0xffffffffU, 0xf3f3f3f3U, 0xd2d2d2d2U, + 0xcdcdcdcdU, 0x0c0c0c0cU, 0x13131313U, 0xececececU, + 0x5f5f5f5fU, 0x97979797U, 0x44444444U, 0x17171717U, + 0xc4c4c4c4U, 0xa7a7a7a7U, 0x7e7e7e7eU, 0x3d3d3d3dU, + 0x64646464U, 0x5d5d5d5dU, 0x19191919U, 0x73737373U, + 0x60606060U, 0x81818181U, 0x4f4f4f4fU, 0xdcdcdcdcU, + 0x22222222U, 0x2a2a2a2aU, 0x90909090U, 0x88888888U, + 0x46464646U, 0xeeeeeeeeU, 0xb8b8b8b8U, 0x14141414U, + 0xdedededeU, 0x5e5e5e5eU, 0x0b0b0b0bU, 0xdbdbdbdbU, + 0xe0e0e0e0U, 0x32323232U, 0x3a3a3a3aU, 0x0a0a0a0aU, + 0x49494949U, 0x06060606U, 0x24242424U, 0x5c5c5c5cU, + 0xc2c2c2c2U, 0xd3d3d3d3U, 0xacacacacU, 0x62626262U, + 0x91919191U, 0x95959595U, 0xe4e4e4e4U, 0x79797979U, + 0xe7e7e7e7U, 0xc8c8c8c8U, 0x37373737U, 0x6d6d6d6dU, + 0x8d8d8d8dU, 0xd5d5d5d5U, 0x4e4e4e4eU, 0xa9a9a9a9U, + 0x6c6c6c6cU, 0x56565656U, 0xf4f4f4f4U, 0xeaeaeaeaU, + 0x65656565U, 0x7a7a7a7aU, 0xaeaeaeaeU, 0x08080808U, + 0xbabababaU, 0x78787878U, 0x25252525U, 0x2e2e2e2eU, + 0x1c1c1c1cU, 0xa6a6a6a6U, 0xb4b4b4b4U, 0xc6c6c6c6U, + 0xe8e8e8e8U, 0xddddddddU, 0x74747474U, 0x1f1f1f1fU, + 0x4b4b4b4bU, 0xbdbdbdbdU, 0x8b8b8b8bU, 0x8a8a8a8aU, + 0x70707070U, 0x3e3e3e3eU, 0xb5b5b5b5U, 0x66666666U, + 0x48484848U, 0x03030303U, 0xf6f6f6f6U, 0x0e0e0e0eU, + 0x61616161U, 0x35353535U, 0x57575757U, 0xb9b9b9b9U, + 0x86868686U, 0xc1c1c1c1U, 0x1d1d1d1dU, 0x9e9e9e9eU, + 0xe1e1e1e1U, 0xf8f8f8f8U, 0x98989898U, 0x11111111U, + 0x69696969U, 0xd9d9d9d9U, 0x8e8e8e8eU, 0x94949494U, + 0x9b9b9b9bU, 0x1e1e1e1eU, 0x87878787U, 0xe9e9e9e9U, + 0xcecececeU, 0x55555555U, 0x28282828U, 0xdfdfdfdfU, + 0x8c8c8c8cU, 0xa1a1a1a1U, 0x89898989U, 0x0d0d0d0dU, + 0xbfbfbfbfU, 0xe6e6e6e6U, 0x42424242U, 0x68686868U, + 0x41414141U, 0x99999999U, 0x2d2d2d2dU, 0x0f0f0f0fU, + 0xb0b0b0b0U, 0x54545454U, 0xbbbbbbbbU, 0x16161616U, +}; +static const u32 Td0[256] = { + 0x51f4a750U, 0x7e416553U, 0x1a17a4c3U, 0x3a275e96U, + 0x3bab6bcbU, 0x1f9d45f1U, 0xacfa58abU, 0x4be30393U, + 0x2030fa55U, 0xad766df6U, 0x88cc7691U, 0xf5024c25U, + 0x4fe5d7fcU, 0xc52acbd7U, 0x26354480U, 0xb562a38fU, + 0xdeb15a49U, 0x25ba1b67U, 0x45ea0e98U, 0x5dfec0e1U, + 0xc32f7502U, 0x814cf012U, 0x8d4697a3U, 0x6bd3f9c6U, + 0x038f5fe7U, 0x15929c95U, 0xbf6d7aebU, 0x955259daU, + 0xd4be832dU, 0x587421d3U, 0x49e06929U, 0x8ec9c844U, + 0x75c2896aU, 0xf48e7978U, 0x99583e6bU, 0x27b971ddU, + 0xbee14fb6U, 0xf088ad17U, 0xc920ac66U, 0x7dce3ab4U, + 0x63df4a18U, 0xe51a3182U, 0x97513360U, 0x62537f45U, + 0xb16477e0U, 0xbb6bae84U, 0xfe81a01cU, 0xf9082b94U, + 0x70486858U, 0x8f45fd19U, 0x94de6c87U, 0x527bf8b7U, + 0xab73d323U, 0x724b02e2U, 0xe31f8f57U, 0x6655ab2aU, + 0xb2eb2807U, 0x2fb5c203U, 0x86c57b9aU, 0xd33708a5U, + 0x302887f2U, 0x23bfa5b2U, 0x02036abaU, 0xed16825cU, + 0x8acf1c2bU, 0xa779b492U, 0xf307f2f0U, 0x4e69e2a1U, + 0x65daf4cdU, 0x0605bed5U, 0xd134621fU, 0xc4a6fe8aU, + 0x342e539dU, 0xa2f355a0U, 0x058ae132U, 0xa4f6eb75U, + 0x0b83ec39U, 0x4060efaaU, 0x5e719f06U, 0xbd6e1051U, + 0x3e218af9U, 0x96dd063dU, 0xdd3e05aeU, 0x4de6bd46U, + 0x91548db5U, 0x71c45d05U, 0x0406d46fU, 0x605015ffU, + 0x1998fb24U, 0xd6bde997U, 0x894043ccU, 0x67d99e77U, + 0xb0e842bdU, 0x07898b88U, 0xe7195b38U, 0x79c8eedbU, + 0xa17c0a47U, 0x7c420fe9U, 0xf8841ec9U, 0x00000000U, + 0x09808683U, 0x322bed48U, 0x1e1170acU, 0x6c5a724eU, + 0xfd0efffbU, 0x0f853856U, 0x3daed51eU, 0x362d3927U, + 0x0a0fd964U, 0x685ca621U, 0x9b5b54d1U, 0x24362e3aU, + 0x0c0a67b1U, 0x9357e70fU, 0xb4ee96d2U, 0x1b9b919eU, + 0x80c0c54fU, 0x61dc20a2U, 0x5a774b69U, 0x1c121a16U, + 0xe293ba0aU, 0xc0a02ae5U, 0x3c22e043U, 0x121b171dU, + 0x0e090d0bU, 0xf28bc7adU, 0x2db6a8b9U, 0x141ea9c8U, + 0x57f11985U, 0xaf75074cU, 0xee99ddbbU, 0xa37f60fdU, + 0xf701269fU, 0x5c72f5bcU, 0x44663bc5U, 0x5bfb7e34U, + 0x8b432976U, 0xcb23c6dcU, 0xb6edfc68U, 0xb8e4f163U, + 0xd731dccaU, 0x42638510U, 0x13972240U, 0x84c61120U, + 0x854a247dU, 0xd2bb3df8U, 0xaef93211U, 0xc729a16dU, + 0x1d9e2f4bU, 0xdcb230f3U, 0x0d8652ecU, 0x77c1e3d0U, + 0x2bb3166cU, 0xa970b999U, 0x119448faU, 0x47e96422U, + 0xa8fc8cc4U, 0xa0f03f1aU, 0x567d2cd8U, 0x223390efU, + 0x87494ec7U, 0xd938d1c1U, 0x8ccaa2feU, 0x98d40b36U, + 0xa6f581cfU, 0xa57ade28U, 0xdab78e26U, 0x3fadbfa4U, + 0x2c3a9de4U, 0x5078920dU, 0x6a5fcc9bU, 0x547e4662U, + 0xf68d13c2U, 0x90d8b8e8U, 0x2e39f75eU, 0x82c3aff5U, + 0x9f5d80beU, 0x69d0937cU, 0x6fd52da9U, 0xcf2512b3U, + 0xc8ac993bU, 0x10187da7U, 0xe89c636eU, 0xdb3bbb7bU, + 0xcd267809U, 0x6e5918f4U, 0xec9ab701U, 0x834f9aa8U, + 0xe6956e65U, 0xaaffe67eU, 0x21bccf08U, 0xef15e8e6U, + 0xbae79bd9U, 0x4a6f36ceU, 0xea9f09d4U, 0x29b07cd6U, + 0x31a4b2afU, 0x2a3f2331U, 0xc6a59430U, 0x35a266c0U, + 0x744ebc37U, 0xfc82caa6U, 0xe090d0b0U, 0x33a7d815U, + 0xf104984aU, 0x41ecdaf7U, 0x7fcd500eU, 0x1791f62fU, + 0x764dd68dU, 0x43efb04dU, 0xccaa4d54U, 0xe49604dfU, + 0x9ed1b5e3U, 0x4c6a881bU, 0xc12c1fb8U, 0x4665517fU, + 0x9d5eea04U, 0x018c355dU, 0xfa877473U, 0xfb0b412eU, + 0xb3671d5aU, 0x92dbd252U, 0xe9105633U, 0x6dd64713U, + 0x9ad7618cU, 0x37a10c7aU, 0x59f8148eU, 0xeb133c89U, + 0xcea927eeU, 0xb761c935U, 0xe11ce5edU, 0x7a47b13cU, + 0x9cd2df59U, 0x55f2733fU, 0x1814ce79U, 0x73c737bfU, + 0x53f7cdeaU, 0x5ffdaa5bU, 0xdf3d6f14U, 0x7844db86U, + 0xcaaff381U, 0xb968c43eU, 0x3824342cU, 0xc2a3405fU, + 0x161dc372U, 0xbce2250cU, 0x283c498bU, 0xff0d9541U, + 0x39a80171U, 0x080cb3deU, 0xd8b4e49cU, 0x6456c190U, + 0x7bcb8461U, 0xd532b670U, 0x486c5c74U, 0xd0b85742U, +}; +static const u32 Td1[256] = { + 0x5051f4a7U, 0x537e4165U, 0xc31a17a4U, 0x963a275eU, + 0xcb3bab6bU, 0xf11f9d45U, 0xabacfa58U, 0x934be303U, + 0x552030faU, 0xf6ad766dU, 0x9188cc76U, 0x25f5024cU, + 0xfc4fe5d7U, 0xd7c52acbU, 0x80263544U, 0x8fb562a3U, + 0x49deb15aU, 0x6725ba1bU, 0x9845ea0eU, 0xe15dfec0U, + 0x02c32f75U, 0x12814cf0U, 0xa38d4697U, 0xc66bd3f9U, + 0xe7038f5fU, 0x9515929cU, 0xebbf6d7aU, 0xda955259U, + 0x2dd4be83U, 0xd3587421U, 0x2949e069U, 0x448ec9c8U, + 0x6a75c289U, 0x78f48e79U, 0x6b99583eU, 0xdd27b971U, + 0xb6bee14fU, 0x17f088adU, 0x66c920acU, 0xb47dce3aU, + 0x1863df4aU, 0x82e51a31U, 0x60975133U, 0x4562537fU, + 0xe0b16477U, 0x84bb6baeU, 0x1cfe81a0U, 0x94f9082bU, + 0x58704868U, 0x198f45fdU, 0x8794de6cU, 0xb7527bf8U, + 0x23ab73d3U, 0xe2724b02U, 0x57e31f8fU, 0x2a6655abU, + 0x07b2eb28U, 0x032fb5c2U, 0x9a86c57bU, 0xa5d33708U, + 0xf2302887U, 0xb223bfa5U, 0xba02036aU, 0x5ced1682U, + 0x2b8acf1cU, 0x92a779b4U, 0xf0f307f2U, 0xa14e69e2U, + 0xcd65daf4U, 0xd50605beU, 0x1fd13462U, 0x8ac4a6feU, + 0x9d342e53U, 0xa0a2f355U, 0x32058ae1U, 0x75a4f6ebU, + 0x390b83ecU, 0xaa4060efU, 0x065e719fU, 0x51bd6e10U, + 0xf93e218aU, 0x3d96dd06U, 0xaedd3e05U, 0x464de6bdU, + 0xb591548dU, 0x0571c45dU, 0x6f0406d4U, 0xff605015U, + 0x241998fbU, 0x97d6bde9U, 0xcc894043U, 0x7767d99eU, + 0xbdb0e842U, 0x8807898bU, 0x38e7195bU, 0xdb79c8eeU, + 0x47a17c0aU, 0xe97c420fU, 0xc9f8841eU, 0x00000000U, + 0x83098086U, 0x48322bedU, 0xac1e1170U, 0x4e6c5a72U, + 0xfbfd0effU, 0x560f8538U, 0x1e3daed5U, 0x27362d39U, + 0x640a0fd9U, 0x21685ca6U, 0xd19b5b54U, 0x3a24362eU, + 0xb10c0a67U, 0x0f9357e7U, 0xd2b4ee96U, 0x9e1b9b91U, + 0x4f80c0c5U, 0xa261dc20U, 0x695a774bU, 0x161c121aU, + 0x0ae293baU, 0xe5c0a02aU, 0x433c22e0U, 0x1d121b17U, + 0x0b0e090dU, 0xadf28bc7U, 0xb92db6a8U, 0xc8141ea9U, + 0x8557f119U, 0x4caf7507U, 0xbbee99ddU, 0xfda37f60U, + 0x9ff70126U, 0xbc5c72f5U, 0xc544663bU, 0x345bfb7eU, + 0x768b4329U, 0xdccb23c6U, 0x68b6edfcU, 0x63b8e4f1U, + 0xcad731dcU, 0x10426385U, 0x40139722U, 0x2084c611U, + 0x7d854a24U, 0xf8d2bb3dU, 0x11aef932U, 0x6dc729a1U, + 0x4b1d9e2fU, 0xf3dcb230U, 0xec0d8652U, 0xd077c1e3U, + 0x6c2bb316U, 0x99a970b9U, 0xfa119448U, 0x2247e964U, + 0xc4a8fc8cU, 0x1aa0f03fU, 0xd8567d2cU, 0xef223390U, + 0xc787494eU, 0xc1d938d1U, 0xfe8ccaa2U, 0x3698d40bU, + 0xcfa6f581U, 0x28a57adeU, 0x26dab78eU, 0xa43fadbfU, + 0xe42c3a9dU, 0x0d507892U, 0x9b6a5fccU, 0x62547e46U, + 0xc2f68d13U, 0xe890d8b8U, 0x5e2e39f7U, 0xf582c3afU, + 0xbe9f5d80U, 0x7c69d093U, 0xa96fd52dU, 0xb3cf2512U, + 0x3bc8ac99U, 0xa710187dU, 0x6ee89c63U, 0x7bdb3bbbU, + 0x09cd2678U, 0xf46e5918U, 0x01ec9ab7U, 0xa8834f9aU, + 0x65e6956eU, 0x7eaaffe6U, 0x0821bccfU, 0xe6ef15e8U, + 0xd9bae79bU, 0xce4a6f36U, 0xd4ea9f09U, 0xd629b07cU, + 0xaf31a4b2U, 0x312a3f23U, 0x30c6a594U, 0xc035a266U, + 0x37744ebcU, 0xa6fc82caU, 0xb0e090d0U, 0x1533a7d8U, + 0x4af10498U, 0xf741ecdaU, 0x0e7fcd50U, 0x2f1791f6U, + 0x8d764dd6U, 0x4d43efb0U, 0x54ccaa4dU, 0xdfe49604U, + 0xe39ed1b5U, 0x1b4c6a88U, 0xb8c12c1fU, 0x7f466551U, + 0x049d5eeaU, 0x5d018c35U, 0x73fa8774U, 0x2efb0b41U, + 0x5ab3671dU, 0x5292dbd2U, 0x33e91056U, 0x136dd647U, + 0x8c9ad761U, 0x7a37a10cU, 0x8e59f814U, 0x89eb133cU, + 0xeecea927U, 0x35b761c9U, 0xede11ce5U, 0x3c7a47b1U, + 0x599cd2dfU, 0x3f55f273U, 0x791814ceU, 0xbf73c737U, + 0xea53f7cdU, 0x5b5ffdaaU, 0x14df3d6fU, 0x867844dbU, + 0x81caaff3U, 0x3eb968c4U, 0x2c382434U, 0x5fc2a340U, + 0x72161dc3U, 0x0cbce225U, 0x8b283c49U, 0x41ff0d95U, + 0x7139a801U, 0xde080cb3U, 0x9cd8b4e4U, 0x906456c1U, + 0x617bcb84U, 0x70d532b6U, 0x74486c5cU, 0x42d0b857U, +}; +static const u32 Td2[256] = { + 0xa75051f4U, 0x65537e41U, 0xa4c31a17U, 0x5e963a27U, + 0x6bcb3babU, 0x45f11f9dU, 0x58abacfaU, 0x03934be3U, + 0xfa552030U, 0x6df6ad76U, 0x769188ccU, 0x4c25f502U, + 0xd7fc4fe5U, 0xcbd7c52aU, 0x44802635U, 0xa38fb562U, + 0x5a49deb1U, 0x1b6725baU, 0x0e9845eaU, 0xc0e15dfeU, + 0x7502c32fU, 0xf012814cU, 0x97a38d46U, 0xf9c66bd3U, + 0x5fe7038fU, 0x9c951592U, 0x7aebbf6dU, 0x59da9552U, + 0x832dd4beU, 0x21d35874U, 0x692949e0U, 0xc8448ec9U, + 0x896a75c2U, 0x7978f48eU, 0x3e6b9958U, 0x71dd27b9U, + 0x4fb6bee1U, 0xad17f088U, 0xac66c920U, 0x3ab47dceU, + 0x4a1863dfU, 0x3182e51aU, 0x33609751U, 0x7f456253U, + 0x77e0b164U, 0xae84bb6bU, 0xa01cfe81U, 0x2b94f908U, + 0x68587048U, 0xfd198f45U, 0x6c8794deU, 0xf8b7527bU, + 0xd323ab73U, 0x02e2724bU, 0x8f57e31fU, 0xab2a6655U, + 0x2807b2ebU, 0xc2032fb5U, 0x7b9a86c5U, 0x08a5d337U, + 0x87f23028U, 0xa5b223bfU, 0x6aba0203U, 0x825ced16U, + 0x1c2b8acfU, 0xb492a779U, 0xf2f0f307U, 0xe2a14e69U, + 0xf4cd65daU, 0xbed50605U, 0x621fd134U, 0xfe8ac4a6U, + 0x539d342eU, 0x55a0a2f3U, 0xe132058aU, 0xeb75a4f6U, + 0xec390b83U, 0xefaa4060U, 0x9f065e71U, 0x1051bd6eU, + + 0x8af93e21U, 0x063d96ddU, 0x05aedd3eU, 0xbd464de6U, + 0x8db59154U, 0x5d0571c4U, 0xd46f0406U, 0x15ff6050U, + 0xfb241998U, 0xe997d6bdU, 0x43cc8940U, 0x9e7767d9U, + 0x42bdb0e8U, 0x8b880789U, 0x5b38e719U, 0xeedb79c8U, + 0x0a47a17cU, 0x0fe97c42U, 0x1ec9f884U, 0x00000000U, + 0x86830980U, 0xed48322bU, 0x70ac1e11U, 0x724e6c5aU, + 0xfffbfd0eU, 0x38560f85U, 0xd51e3daeU, 0x3927362dU, + 0xd9640a0fU, 0xa621685cU, 0x54d19b5bU, 0x2e3a2436U, + 0x67b10c0aU, 0xe70f9357U, 0x96d2b4eeU, 0x919e1b9bU, + 0xc54f80c0U, 0x20a261dcU, 0x4b695a77U, 0x1a161c12U, + 0xba0ae293U, 0x2ae5c0a0U, 0xe0433c22U, 0x171d121bU, + 0x0d0b0e09U, 0xc7adf28bU, 0xa8b92db6U, 0xa9c8141eU, + 0x198557f1U, 0x074caf75U, 0xddbbee99U, 0x60fda37fU, + 0x269ff701U, 0xf5bc5c72U, 0x3bc54466U, 0x7e345bfbU, + 0x29768b43U, 0xc6dccb23U, 0xfc68b6edU, 0xf163b8e4U, + 0xdccad731U, 0x85104263U, 0x22401397U, 0x112084c6U, + 0x247d854aU, 0x3df8d2bbU, 0x3211aef9U, 0xa16dc729U, + 0x2f4b1d9eU, 0x30f3dcb2U, 0x52ec0d86U, 0xe3d077c1U, + 0x166c2bb3U, 0xb999a970U, 0x48fa1194U, 0x642247e9U, + 0x8cc4a8fcU, 0x3f1aa0f0U, 0x2cd8567dU, 0x90ef2233U, + 0x4ec78749U, 0xd1c1d938U, 0xa2fe8ccaU, 0x0b3698d4U, + 0x81cfa6f5U, 0xde28a57aU, 0x8e26dab7U, 0xbfa43fadU, + 0x9de42c3aU, 0x920d5078U, 0xcc9b6a5fU, 0x4662547eU, + 0x13c2f68dU, 0xb8e890d8U, 0xf75e2e39U, 0xaff582c3U, + 0x80be9f5dU, 0x937c69d0U, 0x2da96fd5U, 0x12b3cf25U, + 0x993bc8acU, 0x7da71018U, 0x636ee89cU, 0xbb7bdb3bU, + 0x7809cd26U, 0x18f46e59U, 0xb701ec9aU, 0x9aa8834fU, + 0x6e65e695U, 0xe67eaaffU, 0xcf0821bcU, 0xe8e6ef15U, + 0x9bd9bae7U, 0x36ce4a6fU, 0x09d4ea9fU, 0x7cd629b0U, + 0xb2af31a4U, 0x23312a3fU, 0x9430c6a5U, 0x66c035a2U, + 0xbc37744eU, 0xcaa6fc82U, 0xd0b0e090U, 0xd81533a7U, + 0x984af104U, 0xdaf741ecU, 0x500e7fcdU, 0xf62f1791U, + 0xd68d764dU, 0xb04d43efU, 0x4d54ccaaU, 0x04dfe496U, + 0xb5e39ed1U, 0x881b4c6aU, 0x1fb8c12cU, 0x517f4665U, + 0xea049d5eU, 0x355d018cU, 0x7473fa87U, 0x412efb0bU, + 0x1d5ab367U, 0xd25292dbU, 0x5633e910U, 0x47136dd6U, + 0x618c9ad7U, 0x0c7a37a1U, 0x148e59f8U, 0x3c89eb13U, + 0x27eecea9U, 0xc935b761U, 0xe5ede11cU, 0xb13c7a47U, + 0xdf599cd2U, 0x733f55f2U, 0xce791814U, 0x37bf73c7U, + 0xcdea53f7U, 0xaa5b5ffdU, 0x6f14df3dU, 0xdb867844U, + 0xf381caafU, 0xc43eb968U, 0x342c3824U, 0x405fc2a3U, + 0xc372161dU, 0x250cbce2U, 0x498b283cU, 0x9541ff0dU, + 0x017139a8U, 0xb3de080cU, 0xe49cd8b4U, 0xc1906456U, + 0x84617bcbU, 0xb670d532U, 0x5c74486cU, 0x5742d0b8U, +}; +static const u32 Td3[256] = { + 0xf4a75051U, 0x4165537eU, 0x17a4c31aU, 0x275e963aU, + 0xab6bcb3bU, 0x9d45f11fU, 0xfa58abacU, 0xe303934bU, + 0x30fa5520U, 0x766df6adU, 0xcc769188U, 0x024c25f5U, + 0xe5d7fc4fU, 0x2acbd7c5U, 0x35448026U, 0x62a38fb5U, + 0xb15a49deU, 0xba1b6725U, 0xea0e9845U, 0xfec0e15dU, + 0x2f7502c3U, 0x4cf01281U, 0x4697a38dU, 0xd3f9c66bU, + 0x8f5fe703U, 0x929c9515U, 0x6d7aebbfU, 0x5259da95U, + 0xbe832dd4U, 0x7421d358U, 0xe0692949U, 0xc9c8448eU, + 0xc2896a75U, 0x8e7978f4U, 0x583e6b99U, 0xb971dd27U, + 0xe14fb6beU, 0x88ad17f0U, 0x20ac66c9U, 0xce3ab47dU, + 0xdf4a1863U, 0x1a3182e5U, 0x51336097U, 0x537f4562U, + 0x6477e0b1U, 0x6bae84bbU, 0x81a01cfeU, 0x082b94f9U, + 0x48685870U, 0x45fd198fU, 0xde6c8794U, 0x7bf8b752U, + 0x73d323abU, 0x4b02e272U, 0x1f8f57e3U, 0x55ab2a66U, + 0xeb2807b2U, 0xb5c2032fU, 0xc57b9a86U, 0x3708a5d3U, + 0x2887f230U, 0xbfa5b223U, 0x036aba02U, 0x16825cedU, + 0xcf1c2b8aU, 0x79b492a7U, 0x07f2f0f3U, 0x69e2a14eU, + 0xdaf4cd65U, 0x05bed506U, 0x34621fd1U, 0xa6fe8ac4U, + 0x2e539d34U, 0xf355a0a2U, 0x8ae13205U, 0xf6eb75a4U, + 0x83ec390bU, 0x60efaa40U, 0x719f065eU, 0x6e1051bdU, + 0x218af93eU, 0xdd063d96U, 0x3e05aeddU, 0xe6bd464dU, + 0x548db591U, 0xc45d0571U, 0x06d46f04U, 0x5015ff60U, + 0x98fb2419U, 0xbde997d6U, 0x4043cc89U, 0xd99e7767U, + 0xe842bdb0U, 0x898b8807U, 0x195b38e7U, 0xc8eedb79U, + 0x7c0a47a1U, 0x420fe97cU, 0x841ec9f8U, 0x00000000U, + 0x80868309U, 0x2bed4832U, 0x1170ac1eU, 0x5a724e6cU, + 0x0efffbfdU, 0x8538560fU, 0xaed51e3dU, 0x2d392736U, + 0x0fd9640aU, 0x5ca62168U, 0x5b54d19bU, 0x362e3a24U, + 0x0a67b10cU, 0x57e70f93U, 0xee96d2b4U, 0x9b919e1bU, + 0xc0c54f80U, 0xdc20a261U, 0x774b695aU, 0x121a161cU, + 0x93ba0ae2U, 0xa02ae5c0U, 0x22e0433cU, 0x1b171d12U, + 0x090d0b0eU, 0x8bc7adf2U, 0xb6a8b92dU, 0x1ea9c814U, + 0xf1198557U, 0x75074cafU, 0x99ddbbeeU, 0x7f60fda3U, + 0x01269ff7U, 0x72f5bc5cU, 0x663bc544U, 0xfb7e345bU, + 0x4329768bU, 0x23c6dccbU, 0xedfc68b6U, 0xe4f163b8U, + 0x31dccad7U, 0x63851042U, 0x97224013U, 0xc6112084U, + 0x4a247d85U, 0xbb3df8d2U, 0xf93211aeU, 0x29a16dc7U, + 0x9e2f4b1dU, 0xb230f3dcU, 0x8652ec0dU, 0xc1e3d077U, + 0xb3166c2bU, 0x70b999a9U, 0x9448fa11U, 0xe9642247U, + 0xfc8cc4a8U, 0xf03f1aa0U, 0x7d2cd856U, 0x3390ef22U, + 0x494ec787U, 0x38d1c1d9U, 0xcaa2fe8cU, 0xd40b3698U, + 0xf581cfa6U, 0x7ade28a5U, 0xb78e26daU, 0xadbfa43fU, + 0x3a9de42cU, 0x78920d50U, 0x5fcc9b6aU, 0x7e466254U, + 0x8d13c2f6U, 0xd8b8e890U, 0x39f75e2eU, 0xc3aff582U, + 0x5d80be9fU, 0xd0937c69U, 0xd52da96fU, 0x2512b3cfU, + 0xac993bc8U, 0x187da710U, 0x9c636ee8U, 0x3bbb7bdbU, + 0x267809cdU, 0x5918f46eU, 0x9ab701ecU, 0x4f9aa883U, + 0x956e65e6U, 0xffe67eaaU, 0xbccf0821U, 0x15e8e6efU, + 0xe79bd9baU, 0x6f36ce4aU, 0x9f09d4eaU, 0xb07cd629U, + 0xa4b2af31U, 0x3f23312aU, 0xa59430c6U, 0xa266c035U, + 0x4ebc3774U, 0x82caa6fcU, 0x90d0b0e0U, 0xa7d81533U, + 0x04984af1U, 0xecdaf741U, 0xcd500e7fU, 0x91f62f17U, + 0x4dd68d76U, 0xefb04d43U, 0xaa4d54ccU, 0x9604dfe4U, + 0xd1b5e39eU, 0x6a881b4cU, 0x2c1fb8c1U, 0x65517f46U, + 0x5eea049dU, 0x8c355d01U, 0x877473faU, 0x0b412efbU, + 0x671d5ab3U, 0xdbd25292U, 0x105633e9U, 0xd647136dU, + 0xd7618c9aU, 0xa10c7a37U, 0xf8148e59U, 0x133c89ebU, + 0xa927eeceU, 0x61c935b7U, 0x1ce5ede1U, 0x47b13c7aU, + 0xd2df599cU, 0xf2733f55U, 0x14ce7918U, 0xc737bf73U, + 0xf7cdea53U, 0xfdaa5b5fU, 0x3d6f14dfU, 0x44db8678U, + 0xaff381caU, 0x68c43eb9U, 0x24342c38U, 0xa3405fc2U, + 0x1dc37216U, 0xe2250cbcU, 0x3c498b28U, 0x0d9541ffU, + 0xa8017139U, 0x0cb3de08U, 0xb4e49cd8U, 0x56c19064U, + 0xcb84617bU, 0x32b670d5U, 0x6c5c7448U, 0xb85742d0U, +}; +static const u32 Td4[256] = { + 0x52525252U, 0x09090909U, 0x6a6a6a6aU, 0xd5d5d5d5U, + 0x30303030U, 0x36363636U, 0xa5a5a5a5U, 0x38383838U, + 0xbfbfbfbfU, 0x40404040U, 0xa3a3a3a3U, 0x9e9e9e9eU, + 0x81818181U, 0xf3f3f3f3U, 0xd7d7d7d7U, 0xfbfbfbfbU, + 0x7c7c7c7cU, 0xe3e3e3e3U, 0x39393939U, 0x82828282U, + 0x9b9b9b9bU, 0x2f2f2f2fU, 0xffffffffU, 0x87878787U, + 0x34343434U, 0x8e8e8e8eU, 0x43434343U, 0x44444444U, + 0xc4c4c4c4U, 0xdedededeU, 0xe9e9e9e9U, 0xcbcbcbcbU, + 0x54545454U, 0x7b7b7b7bU, 0x94949494U, 0x32323232U, + 0xa6a6a6a6U, 0xc2c2c2c2U, 0x23232323U, 0x3d3d3d3dU, + 0xeeeeeeeeU, 0x4c4c4c4cU, 0x95959595U, 0x0b0b0b0bU, + 0x42424242U, 0xfafafafaU, 0xc3c3c3c3U, 0x4e4e4e4eU, + 0x08080808U, 0x2e2e2e2eU, 0xa1a1a1a1U, 0x66666666U, + 0x28282828U, 0xd9d9d9d9U, 0x24242424U, 0xb2b2b2b2U, + 0x76767676U, 0x5b5b5b5bU, 0xa2a2a2a2U, 0x49494949U, + 0x6d6d6d6dU, 0x8b8b8b8bU, 0xd1d1d1d1U, 0x25252525U, + 0x72727272U, 0xf8f8f8f8U, 0xf6f6f6f6U, 0x64646464U, + 0x86868686U, 0x68686868U, 0x98989898U, 0x16161616U, + 0xd4d4d4d4U, 0xa4a4a4a4U, 0x5c5c5c5cU, 0xccccccccU, + 0x5d5d5d5dU, 0x65656565U, 0xb6b6b6b6U, 0x92929292U, + 0x6c6c6c6cU, 0x70707070U, 0x48484848U, 0x50505050U, + 0xfdfdfdfdU, 0xededededU, 0xb9b9b9b9U, 0xdadadadaU, + 0x5e5e5e5eU, 0x15151515U, 0x46464646U, 0x57575757U, + 0xa7a7a7a7U, 0x8d8d8d8dU, 0x9d9d9d9dU, 0x84848484U, + 0x90909090U, 0xd8d8d8d8U, 0xababababU, 0x00000000U, + 0x8c8c8c8cU, 0xbcbcbcbcU, 0xd3d3d3d3U, 0x0a0a0a0aU, + 0xf7f7f7f7U, 0xe4e4e4e4U, 0x58585858U, 0x05050505U, + 0xb8b8b8b8U, 0xb3b3b3b3U, 0x45454545U, 0x06060606U, + 0xd0d0d0d0U, 0x2c2c2c2cU, 0x1e1e1e1eU, 0x8f8f8f8fU, + 0xcacacacaU, 0x3f3f3f3fU, 0x0f0f0f0fU, 0x02020202U, + 0xc1c1c1c1U, 0xafafafafU, 0xbdbdbdbdU, 0x03030303U, + 0x01010101U, 0x13131313U, 0x8a8a8a8aU, 0x6b6b6b6bU, + 0x3a3a3a3aU, 0x91919191U, 0x11111111U, 0x41414141U, + 0x4f4f4f4fU, 0x67676767U, 0xdcdcdcdcU, 0xeaeaeaeaU, + 0x97979797U, 0xf2f2f2f2U, 0xcfcfcfcfU, 0xcecececeU, + 0xf0f0f0f0U, 0xb4b4b4b4U, 0xe6e6e6e6U, 0x73737373U, + 0x96969696U, 0xacacacacU, 0x74747474U, 0x22222222U, + 0xe7e7e7e7U, 0xadadadadU, 0x35353535U, 0x85858585U, + 0xe2e2e2e2U, 0xf9f9f9f9U, 0x37373737U, 0xe8e8e8e8U, + 0x1c1c1c1cU, 0x75757575U, 0xdfdfdfdfU, 0x6e6e6e6eU, + 0x47474747U, 0xf1f1f1f1U, 0x1a1a1a1aU, 0x71717171U, + 0x1d1d1d1dU, 0x29292929U, 0xc5c5c5c5U, 0x89898989U, + 0x6f6f6f6fU, 0xb7b7b7b7U, 0x62626262U, 0x0e0e0e0eU, + 0xaaaaaaaaU, 0x18181818U, 0xbebebebeU, 0x1b1b1b1bU, + 0xfcfcfcfcU, 0x56565656U, 0x3e3e3e3eU, 0x4b4b4b4bU, + 0xc6c6c6c6U, 0xd2d2d2d2U, 0x79797979U, 0x20202020U, + 0x9a9a9a9aU, 0xdbdbdbdbU, 0xc0c0c0c0U, 0xfefefefeU, + 0x78787878U, 0xcdcdcdcdU, 0x5a5a5a5aU, 0xf4f4f4f4U, + 0x1f1f1f1fU, 0xddddddddU, 0xa8a8a8a8U, 0x33333333U, + 0x88888888U, 0x07070707U, 0xc7c7c7c7U, 0x31313131U, + 0xb1b1b1b1U, 0x12121212U, 0x10101010U, 0x59595959U, + 0x27272727U, 0x80808080U, 0xececececU, 0x5f5f5f5fU, + 0x60606060U, 0x51515151U, 0x7f7f7f7fU, 0xa9a9a9a9U, + 0x19191919U, 0xb5b5b5b5U, 0x4a4a4a4aU, 0x0d0d0d0dU, + 0x2d2d2d2dU, 0xe5e5e5e5U, 0x7a7a7a7aU, 0x9f9f9f9fU, + 0x93939393U, 0xc9c9c9c9U, 0x9c9c9c9cU, 0xefefefefU, + 0xa0a0a0a0U, 0xe0e0e0e0U, 0x3b3b3b3bU, 0x4d4d4d4dU, + 0xaeaeaeaeU, 0x2a2a2a2aU, 0xf5f5f5f5U, 0xb0b0b0b0U, + 0xc8c8c8c8U, 0xebebebebU, 0xbbbbbbbbU, 0x3c3c3c3cU, + 0x83838383U, 0x53535353U, 0x99999999U, 0x61616161U, + 0x17171717U, 0x2b2b2b2bU, 0x04040404U, 0x7e7e7e7eU, + 0xbabababaU, 0x77777777U, 0xd6d6d6d6U, 0x26262626U, + 0xe1e1e1e1U, 0x69696969U, 0x14141414U, 0x63636363U, + 0x55555555U, 0x21212121U, 0x0c0c0c0cU, 0x7d7d7d7dU, +}; +static const u32 rcon[] = { + 0x01000000, 0x02000000, 0x04000000, 0x08000000, + 0x10000000, 0x20000000, 0x40000000, 0x80000000, + 0x1B000000, 0x36000000, /* for 128-bit blocks, Rijndael never uses more than 10 rcon values */ +}; + +#define SWAP(x) (_lrotl(x, 8) & 0x00ff00ff | _lrotr(x, 8) & 0xff00ff00) + +#ifdef _MSC_VER +#define GETU32(p) SWAP(*((u32 *)(p))) +#define PUTU32(ct, st) { *((u32 *)(ct)) = SWAP((st)); } +#else +#define GETU32(pt) (((u32)(pt)[0] << 24) ^ ((u32)(pt)[1] << 16) ^ \ +((u32)(pt)[2] << 8) ^ ((u32)(pt)[3])) +#define PUTU32(ct, st) { \ +(ct)[0] = (u8)((st) >> 24); (ct)[1] = (u8)((st) >> 16); \ +(ct)[2] = (u8)((st) >> 8); (ct)[3] = (u8)(st); } +#endif + +/** + * Expand the cipher key into the encryption key schedule. + * + * @return the number of rounds for the given cipher key size. + */ +void rijndaelKeySetupEnc(u32 rk[/*44*/], const u8 cipherKey[]) +{ + int i; + u32 temp; + + rk[0] = GETU32(cipherKey ); + rk[1] = GETU32(cipherKey + 4); + rk[2] = GETU32(cipherKey + 8); + rk[3] = GETU32(cipherKey + 12); + for (i = 0; i < 10; i++) { + temp = rk[3]; + rk[4] = rk[0] ^ + (Te4[(temp >> 16) & 0xff] & 0xff000000) ^ + (Te4[(temp >> 8) & 0xff] & 0x00ff0000) ^ + (Te4[(temp ) & 0xff] & 0x0000ff00) ^ + (Te4[(temp >> 24) ] & 0x000000ff) ^ + rcon[i]; + rk[5] = rk[1] ^ rk[4]; + rk[6] = rk[2] ^ rk[5]; + rk[7] = rk[3] ^ rk[6]; + rk += 4; + } +} + +/** + * Expand the cipher key into the decryption key schedule. + * + * @return the number of rounds for the given cipher key size. + */ +void rijndaelKeySetupDec(u32 rk[/*44*/], const u8 cipherKey[]) +{ + int Nr = 10, i, j; + u32 temp; + + /* expand the cipher key: */ + rijndaelKeySetupEnc(rk, cipherKey); + /* invert the order of the round keys: */ + for (i = 0, j = 4*Nr; i < j; i += 4, j -= 4) { + temp = rk[i ]; rk[i ] = rk[j ]; rk[j ] = temp; + temp = rk[i + 1]; rk[i + 1] = rk[j + 1]; rk[j + 1] = temp; + temp = rk[i + 2]; rk[i + 2] = rk[j + 2]; rk[j + 2] = temp; + temp = rk[i + 3]; rk[i + 3] = rk[j + 3]; rk[j + 3] = temp; + } + /* apply the inverse MixColumn transform to all round keys but the + * first and the last: */ + for (i = 1; i < Nr; i++) { + rk += 4; + rk[0] = + Td0[Te4[(rk[0] >> 24) ] & 0xff] ^ + Td1[Te4[(rk[0] >> 16) & 0xff] & 0xff] ^ + Td2[Te4[(rk[0] >> 8) & 0xff] & 0xff] ^ + Td3[Te4[(rk[0] ) & 0xff] & 0xff]; + rk[1] = + Td0[Te4[(rk[1] >> 24) ] & 0xff] ^ + Td1[Te4[(rk[1] >> 16) & 0xff] & 0xff] ^ + Td2[Te4[(rk[1] >> 8) & 0xff] & 0xff] ^ + Td3[Te4[(rk[1] ) & 0xff] & 0xff]; + rk[2] = + Td0[Te4[(rk[2] >> 24) ] & 0xff] ^ + Td1[Te4[(rk[2] >> 16) & 0xff] & 0xff] ^ + Td2[Te4[(rk[2] >> 8) & 0xff] & 0xff] ^ + Td3[Te4[(rk[2] ) & 0xff] & 0xff]; + rk[3] = + Td0[Te4[(rk[3] >> 24) ] & 0xff] ^ + Td1[Te4[(rk[3] >> 16) & 0xff] & 0xff] ^ + Td2[Te4[(rk[3] >> 8) & 0xff] & 0xff] ^ + Td3[Te4[(rk[3] ) & 0xff] & 0xff]; + } +} + +void rijndaelEncrypt(const u32 rk[/*44*/], const u8 pt[16], u8 ct[16]) +{ + u32 s0, s1, s2, s3, t0, t1, t2, t3; + int Nr = 10; +#ifndef FULL_UNROLL + int r; +#endif /* ?FULL_UNROLL */ + + /* + * map byte array block to cipher state + * and add initial round key: + */ + s0 = GETU32(pt ) ^ rk[0]; + s1 = GETU32(pt + 4) ^ rk[1]; + s2 = GETU32(pt + 8) ^ rk[2]; + s3 = GETU32(pt + 12) ^ rk[3]; +#ifdef FULL_UNROLL + /* round 1: */ + t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[ 4]; + t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[ 5]; + t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[ 6]; + t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[ 7]; + /* round 2: */ + s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[ 8]; + s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[ 9]; + s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[10]; + s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[11]; + /* round 3: */ + t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[12]; + t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[13]; + t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[14]; + t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[15]; + /* round 4: */ + s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[16]; + s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[17]; + s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[18]; + s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[19]; + /* round 5: */ + t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[20]; + t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[21]; + t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[22]; + t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[23]; + /* round 6: */ + s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[24]; + s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[25]; + s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[26]; + s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[27]; + /* round 7: */ + t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[28]; + t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[29]; + t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[30]; + t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[31]; + /* round 8: */ + s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[32]; + s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[33]; + s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[34]; + s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[35]; + /* round 9: */ + t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[36]; + t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[37]; + t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[38]; + t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[39]; + rk += Nr << 2; +#else /* !FULL_UNROLL */ + /* + * Nr - 1 full rounds: + */ + r = Nr >> 1; + for (;;) { + t0 = + Te0[(s0 >> 24) ] ^ + Te1[(s1 >> 16) & 0xff] ^ + Te2[(s2 >> 8) & 0xff] ^ + Te3[(s3 ) & 0xff] ^ + rk[4]; + t1 = + Te0[(s1 >> 24) ] ^ + Te1[(s2 >> 16) & 0xff] ^ + Te2[(s3 >> 8) & 0xff] ^ + Te3[(s0 ) & 0xff] ^ + rk[5]; + t2 = + Te0[(s2 >> 24) ] ^ + Te1[(s3 >> 16) & 0xff] ^ + Te2[(s0 >> 8) & 0xff] ^ + Te3[(s1 ) & 0xff] ^ + rk[6]; + t3 = + Te0[(s3 >> 24) ] ^ + Te1[(s0 >> 16) & 0xff] ^ + Te2[(s1 >> 8) & 0xff] ^ + Te3[(s2 ) & 0xff] ^ + rk[7]; + + rk += 8; + if (--r == 0) { + break; + } + + s0 = + Te0[(t0 >> 24) ] ^ + Te1[(t1 >> 16) & 0xff] ^ + Te2[(t2 >> 8) & 0xff] ^ + Te3[(t3 ) & 0xff] ^ + rk[0]; + s1 = + Te0[(t1 >> 24) ] ^ + Te1[(t2 >> 16) & 0xff] ^ + Te2[(t3 >> 8) & 0xff] ^ + Te3[(t0 ) & 0xff] ^ + rk[1]; + s2 = + Te0[(t2 >> 24) ] ^ + Te1[(t3 >> 16) & 0xff] ^ + Te2[(t0 >> 8) & 0xff] ^ + Te3[(t1 ) & 0xff] ^ + rk[2]; + s3 = + Te0[(t3 >> 24) ] ^ + Te1[(t0 >> 16) & 0xff] ^ + Te2[(t1 >> 8) & 0xff] ^ + Te3[(t2 ) & 0xff] ^ + rk[3]; + } +#endif /* ?FULL_UNROLL */ + /* + * apply last round and + * map cipher state to byte array block: + */ + s0 = + (Te4[(t0 >> 24) ] & 0xff000000) ^ + (Te4[(t1 >> 16) & 0xff] & 0x00ff0000) ^ + (Te4[(t2 >> 8) & 0xff] & 0x0000ff00) ^ + (Te4[(t3 ) & 0xff] & 0x000000ff) ^ + rk[0]; + PUTU32(ct , s0); + s1 = + (Te4[(t1 >> 24) ] & 0xff000000) ^ + (Te4[(t2 >> 16) & 0xff] & 0x00ff0000) ^ + (Te4[(t3 >> 8) & 0xff] & 0x0000ff00) ^ + (Te4[(t0 ) & 0xff] & 0x000000ff) ^ + rk[1]; + PUTU32(ct + 4, s1); + s2 = + (Te4[(t2 >> 24) ] & 0xff000000) ^ + (Te4[(t3 >> 16) & 0xff] & 0x00ff0000) ^ + (Te4[(t0 >> 8) & 0xff] & 0x0000ff00) ^ + (Te4[(t1 ) & 0xff] & 0x000000ff) ^ + rk[2]; + PUTU32(ct + 8, s2); + s3 = + (Te4[(t3 >> 24) ] & 0xff000000) ^ + (Te4[(t0 >> 16) & 0xff] & 0x00ff0000) ^ + (Te4[(t1 >> 8) & 0xff] & 0x0000ff00) ^ + (Te4[(t2 ) & 0xff] & 0x000000ff) ^ + rk[3]; + PUTU32(ct + 12, s3); +} + +void rijndaelDecrypt(const u32 rk[/*44*/], const u8 ct[16], u8 pt[16]) +{ + u32 s0, s1, s2, s3, t0, t1, t2, t3; + int Nr = 10; +#ifndef FULL_UNROLL + int r; +#endif /* ?FULL_UNROLL */ + + /* + * map byte array block to cipher state + * and add initial round key: + */ + s0 = GETU32(ct ) ^ rk[0]; + s1 = GETU32(ct + 4) ^ rk[1]; + s2 = GETU32(ct + 8) ^ rk[2]; + s3 = GETU32(ct + 12) ^ rk[3]; +#ifdef FULL_UNROLL + /* round 1: */ + t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[ 4]; + t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[ 5]; + t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[ 6]; + t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[ 7]; + /* round 2: */ + s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[ 8]; + s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[ 9]; + s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[10]; + s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[11]; + /* round 3: */ + t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[12]; + t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[13]; + t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[14]; + t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[15]; + /* round 4: */ + s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[16]; + s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[17]; + s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[18]; + s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[19]; + /* round 5: */ + t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[20]; + t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[21]; + t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[22]; + t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[23]; + /* round 6: */ + s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[24]; + s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[25]; + s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[26]; + s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[27]; + /* round 7: */ + t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[28]; + t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[29]; + t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[30]; + t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[31]; + /* round 8: */ + s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[32]; + s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[33]; + s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[34]; + s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[35]; + /* round 9: */ + t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[36]; + t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[37]; + t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[38]; + t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[39]; + rk += Nr << 2; +#else /* !FULL_UNROLL */ + /* + * Nr - 1 full rounds: + */ + r = Nr >> 1; + for (;;) { + t0 = + Td0[(s0 >> 24) ] ^ + Td1[(s3 >> 16) & 0xff] ^ + Td2[(s2 >> 8) & 0xff] ^ + Td3[(s1 ) & 0xff] ^ + rk[4]; + t1 = + Td0[(s1 >> 24) ] ^ + Td1[(s0 >> 16) & 0xff] ^ + Td2[(s3 >> 8) & 0xff] ^ + Td3[(s2 ) & 0xff] ^ + rk[5]; + t2 = + Td0[(s2 >> 24) ] ^ + Td1[(s1 >> 16) & 0xff] ^ + Td2[(s0 >> 8) & 0xff] ^ + Td3[(s3 ) & 0xff] ^ + rk[6]; + t3 = + Td0[(s3 >> 24) ] ^ + Td1[(s2 >> 16) & 0xff] ^ + Td2[(s1 >> 8) & 0xff] ^ + Td3[(s0 ) & 0xff] ^ + rk[7]; + + rk += 8; + if (--r == 0) { + break; + } + + s0 = + Td0[(t0 >> 24) ] ^ + Td1[(t3 >> 16) & 0xff] ^ + Td2[(t2 >> 8) & 0xff] ^ + Td3[(t1 ) & 0xff] ^ + rk[0]; + s1 = + Td0[(t1 >> 24) ] ^ + Td1[(t0 >> 16) & 0xff] ^ + Td2[(t3 >> 8) & 0xff] ^ + Td3[(t2 ) & 0xff] ^ + rk[1]; + s2 = + Td0[(t2 >> 24) ] ^ + Td1[(t1 >> 16) & 0xff] ^ + Td2[(t0 >> 8) & 0xff] ^ + Td3[(t3 ) & 0xff] ^ + rk[2]; + s3 = + Td0[(t3 >> 24) ] ^ + Td1[(t2 >> 16) & 0xff] ^ + Td2[(t1 >> 8) & 0xff] ^ + Td3[(t0 ) & 0xff] ^ + rk[3]; + } +#endif /* ?FULL_UNROLL */ + /* + * apply last round and + * map cipher state to byte array block: + */ + s0 = + (Td4[(t0 >> 24) ] & 0xff000000) ^ + (Td4[(t3 >> 16) & 0xff] & 0x00ff0000) ^ + (Td4[(t2 >> 8) & 0xff] & 0x0000ff00) ^ + (Td4[(t1 ) & 0xff] & 0x000000ff) ^ + rk[0]; + PUTU32(pt , s0); + s1 = + (Td4[(t1 >> 24) ] & 0xff000000) ^ + (Td4[(t0 >> 16) & 0xff] & 0x00ff0000) ^ + (Td4[(t3 >> 8) & 0xff] & 0x0000ff00) ^ + (Td4[(t2 ) & 0xff] & 0x000000ff) ^ + rk[1]; + PUTU32(pt + 4, s1); + s2 = + (Td4[(t2 >> 24) ] & 0xff000000) ^ + (Td4[(t1 >> 16) & 0xff] & 0x00ff0000) ^ + (Td4[(t0 >> 8) & 0xff] & 0x0000ff00) ^ + (Td4[(t3 ) & 0xff] & 0x000000ff) ^ + rk[2]; + PUTU32(pt + 8, s2); + s3 = + (Td4[(t3 >> 24) ] & 0xff000000) ^ + (Td4[(t2 >> 16) & 0xff] & 0x00ff0000) ^ + (Td4[(t1 >> 8) & 0xff] & 0x0000ff00) ^ + (Td4[(t0 ) & 0xff] & 0x000000ff) ^ + rk[3]; + PUTU32(pt + 12, s3); +} diff --git a/contrib/wpa_supplicant/aes_wrap.c b/contrib/wpa_supplicant/aes_wrap.c new file mode 100644 index 0000000..dbcc136 --- /dev/null +++ b/contrib/wpa_supplicant/aes_wrap.c @@ -0,0 +1,642 @@ +/* + * AES Key Wrap Algorithm (128-bit KEK) (RFC3394) + * One-Key CBC MAC (OMAC1) hash with AES-128 + * AES-128 CTR mode encryption + * AES-128 EAX mode encryption/decryption + * AES-128 CBC + * Copyright (c) 2003-2004, Jouni Malinen <jkmaline@cc.hut.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include "common.h" +#include "aes_wrap.h" + +#ifdef EAP_TLS_FUNCS + +#include <openssl/aes.h> + +#else /* EAP_TLS_FUNCS */ + +#include "aes.c" + +struct aes_key_st { + u32 rk[44]; +}; +typedef struct aes_key_st AES_KEY; + +#define AES_set_encrypt_key(userKey, bits, key) \ + rijndaelKeySetupEnc((key)->rk, (userKey)) +#define AES_set_decrypt_key(userKey, bits, key) \ + rijndaelKeySetupDec((key)->rk, (userKey)) +#define AES_encrypt(in, out, key) \ + rijndaelEncrypt((key)->rk, in, out) +#define AES_decrypt(in, out, key) \ + rijndaelDecrypt((key)->rk, in, out) + +#endif /* EAP_TLS_FUNCS */ + + +/* + * @kek: key encryption key (KEK) + * @n: length of the wrapped key in 64-bit units; e.g., 2 = 128-bit = 16 bytes + * @plain: plaintext key to be wrapped, n * 64 bit + * @cipher: wrapped key, (n + 1) * 64 bit + */ +void aes_wrap(u8 *kek, int n, u8 *plain, u8 *cipher) +{ + u8 *a, *r, b[16]; + int i, j; + AES_KEY key; + + a = cipher; + r = cipher + 8; + + /* 1) Initialize variables. */ + memset(a, 0xa6, 8); + memcpy(r, plain, 8 * n); + + AES_set_encrypt_key(kek, 128, &key); + + /* 2) Calculate intermediate values. + * For j = 0 to 5 + * For i=1 to n + * B = AES(K, A | R[i]) + * A = MSB(64, B) ^ t where t = (n*j)+i + * R[i] = LSB(64, B) + */ + for (j = 0; j <= 5; j++) { + r = cipher + 8; + for (i = 1; i <= n; i++) { + memcpy(b, a, 8); + memcpy(b + 8, r, 8); + AES_encrypt(b, b, &key); + memcpy(a, b, 8); + a[7] ^= n * j + i; + memcpy(r, b + 8, 8); + r += 8; + } + } + + /* 3) Output the results. + * + * These are already in @cipher due to the location of temporary + * variables. + */ +} + + +/* + * @kek: key encryption key (KEK) + * @n: length of the wrapped key in 64-bit units; e.g., 2 = 128-bit = 16 bytes + * @cipher: wrapped key to be unwrapped, (n + 1) * 64 bit + * @plain: plaintext key, n * 64 bit + */ +int aes_unwrap(u8 *kek, int n, u8 *cipher, u8 *plain) +{ + u8 a[8], *r, b[16]; + int i, j; + AES_KEY key; + + /* 1) Initialize variables. */ + memcpy(a, cipher, 8); + r = plain; + memcpy(r, cipher + 8, 8 * n); + + AES_set_decrypt_key(kek, 128, &key); + + /* 2) Compute intermediate values. + * For j = 5 to 0 + * For i = n to 1 + * B = AES-1(K, (A ^ t) | R[i]) where t = n*j+i + * A = MSB(64, B) + * R[i] = LSB(64, B) + */ + for (j = 5; j >= 0; j--) { + r = plain + (n - 1) * 8; + for (i = n; i >= 1; i--) { + memcpy(b, a, 8); + b[7] ^= n * j + i; + + memcpy(b + 8, r, 8); + AES_decrypt(b, b, &key); + memcpy(a, b, 8); + memcpy(r, b + 8, 8); + r -= 8; + } + } + + /* 3) Output results. + * + * These are already in @plain due to the location of temporary + * variables. Just verify that the IV matches with the expected value. + */ + for (i = 0; i < 8; i++) { + if (a[i] != 0xa6) + return -1; + } + + return 0; +} + + +#define BLOCK_SIZE 16 + +static void gf_mulx(u8 *pad) +{ + int i, carry; + + carry = pad[0] & 0x80; + for (i = 0; i < BLOCK_SIZE - 1; i++) + pad[i] = (pad[i] << 1) | (pad[i + 1] >> 7); + pad[BLOCK_SIZE - 1] <<= 1; + if (carry) + pad[BLOCK_SIZE - 1] ^= 0x87; +} + + +void omac1_aes_128(const u8 *key, const u8 *data, size_t data_len, u8 *mac) +{ + AES_KEY akey; + u8 cbc[BLOCK_SIZE], pad[BLOCK_SIZE]; + const u8 *pos = data; + int i; + size_t left = data_len; + + AES_set_encrypt_key(key, 128, &akey); + memset(cbc, 0, BLOCK_SIZE); + + while (left >= BLOCK_SIZE) { + for (i = 0; i < BLOCK_SIZE; i++) + cbc[i] ^= *pos++; + if (left > BLOCK_SIZE) + AES_encrypt(cbc, cbc, &akey); + left -= BLOCK_SIZE; + } + + memset(pad, 0, BLOCK_SIZE); + AES_encrypt(pad, pad, &akey); + gf_mulx(pad); + + if (left || data_len == 0) { + for (i = 0; i < left; i++) + cbc[i] ^= *pos++; + cbc[left] ^= 0x80; + gf_mulx(pad); + } + + for (i = 0; i < BLOCK_SIZE; i++) + pad[i] ^= cbc[i]; + AES_encrypt(pad, mac, &akey); +} + + +void aes_128_encrypt_block(const u8 *key, const u8 *in, u8 *out) +{ + AES_KEY akey; + AES_set_encrypt_key(key, 128, &akey); + AES_encrypt(in, out, &akey); +} + + +void aes_128_ctr_encrypt(const u8 *key, const u8 *nonce, + u8 *data, size_t data_len) +{ + AES_KEY akey; + size_t len, left = data_len; + int i; + u8 *pos = data; + u8 counter[BLOCK_SIZE], buf[BLOCK_SIZE]; + + AES_set_encrypt_key(key, 128, &akey); + memcpy(counter, nonce, BLOCK_SIZE); + + while (left > 0) { + AES_encrypt(counter, buf, &akey); + + len = (left < BLOCK_SIZE) ? left : BLOCK_SIZE; + for (i = 0; i < len; i++) + pos[i] ^= buf[i]; + pos += len; + left -= len; + + for (i = BLOCK_SIZE - 1; i >= 0; i--) { + counter[i]++; + if (counter[i]) + break; + } + } +} + + +int aes_128_eax_encrypt(const u8 *key, const u8 *nonce, size_t nonce_len, + const u8 *hdr, size_t hdr_len, + u8 *data, size_t data_len, u8 *tag) +{ + u8 *buf; + size_t buf_len; + u8 nonce_mac[BLOCK_SIZE], hdr_mac[BLOCK_SIZE], data_mac[BLOCK_SIZE]; + int i; + + if (nonce_len > data_len) + buf_len = nonce_len; + else + buf_len = data_len; + if (hdr_len > buf_len) + buf_len = hdr_len; + buf_len += 16; + + buf = malloc(buf_len); + if (buf == NULL) + return -1; + + memset(buf, 0, 15); + + buf[15] = 0; + memcpy(buf + 16, nonce, nonce_len); + omac1_aes_128(key, buf, 16 + nonce_len, nonce_mac); + + buf[15] = 1; + memcpy(buf + 16, hdr, hdr_len); + omac1_aes_128(key, buf, 16 + hdr_len, hdr_mac); + + aes_128_ctr_encrypt(key, nonce_mac, data, data_len); + buf[15] = 2; + memcpy(buf + 16, data, data_len); + omac1_aes_128(key, buf, 16 + data_len, data_mac); + + free(buf); + + for (i = 0; i < BLOCK_SIZE; i++) + tag[i] = nonce_mac[i] ^ data_mac[i] ^ hdr_mac[i]; + + return 0; +} + + +int aes_128_eax_decrypt(const u8 *key, const u8 *nonce, size_t nonce_len, + const u8 *hdr, size_t hdr_len, + u8 *data, size_t data_len, const u8 *tag) +{ + u8 *buf; + size_t buf_len; + u8 nonce_mac[BLOCK_SIZE], hdr_mac[BLOCK_SIZE], data_mac[BLOCK_SIZE]; + int i; + + if (nonce_len > data_len) + buf_len = nonce_len; + else + buf_len = data_len; + if (hdr_len > buf_len) + buf_len = hdr_len; + buf_len += 16; + + buf = malloc(buf_len); + if (buf == NULL) + return -1; + + memset(buf, 0, 15); + + buf[15] = 0; + memcpy(buf + 16, nonce, nonce_len); + omac1_aes_128(key, buf, 16 + nonce_len, nonce_mac); + + buf[15] = 1; + memcpy(buf + 16, hdr, hdr_len); + omac1_aes_128(key, buf, 16 + hdr_len, hdr_mac); + + buf[15] = 2; + memcpy(buf + 16, data, data_len); + omac1_aes_128(key, buf, 16 + data_len, data_mac); + + free(buf); + + for (i = 0; i < BLOCK_SIZE; i++) { + if (tag[i] != (nonce_mac[i] ^ data_mac[i] ^ hdr_mac[i])) + return -2; + } + + aes_128_ctr_encrypt(key, nonce_mac, data, data_len); + + return 0; +} + + +void aes_128_cbc_encrypt(const u8 *key, const u8 *iv, u8 *data, + size_t data_len) +{ + AES_KEY akey; + u8 cbc[BLOCK_SIZE]; + u8 *pos = data; + int i, j, blocks; + + AES_set_encrypt_key(key, 128, &akey); + memcpy(cbc, iv, BLOCK_SIZE); + + blocks = data_len / BLOCK_SIZE; + for (i = 0; i < blocks; i++) { + for (j = 0; j < BLOCK_SIZE; j++) + cbc[j] ^= pos[j]; + AES_encrypt(cbc, cbc, &akey); + memcpy(pos, cbc, BLOCK_SIZE); + pos += BLOCK_SIZE; + } +} + + +void aes_128_cbc_decrypt(const u8 *key, const u8 *iv, u8 *data, + size_t data_len) +{ + AES_KEY akey; + u8 cbc[BLOCK_SIZE], tmp[BLOCK_SIZE]; + u8 *pos = data; + int i, j, blocks; + + AES_set_decrypt_key(key, 128, &akey); + memcpy(cbc, iv, BLOCK_SIZE); + + blocks = data_len / BLOCK_SIZE; + for (i = 0; i < blocks; i++) { + memcpy(tmp, pos, BLOCK_SIZE); + AES_decrypt(pos, pos, &akey); + for (j = 0; j < BLOCK_SIZE; j++) + pos[j] ^= cbc[j]; + memcpy(cbc, tmp, BLOCK_SIZE); + pos += BLOCK_SIZE; + } +} + + +#ifdef TEST_MAIN + +#ifdef __i386__ +#define rdtscll(val) \ + __asm__ __volatile__("rdtsc" : "=A" (val)) + +static void test_aes_perf(void) +{ + const int num_iters = 10; + int i; + unsigned int start, end; + AES_KEY akey; + u8 key[16], pt[16], ct[16]; + + printf("keySetupEnc:"); + for (i = 0; i < num_iters; i++) { + rdtscll(start); + AES_set_encrypt_key(key, 128, &akey); + rdtscll(end); + printf(" %d", end - start); + } + printf("\n"); + + printf("Encrypt:"); + for (i = 0; i < num_iters; i++) { + rdtscll(start); + AES_encrypt(pt, ct, &akey); + rdtscll(end); + printf(" %d", end - start); + } + printf("\n"); +} +#endif /* __i386__ */ + + +static int test_eax(void) +{ + u8 msg[] = { 0xF7, 0xFB }; + u8 key[] = { 0x91, 0x94, 0x5D, 0x3F, 0x4D, 0xCB, 0xEE, 0x0B, + 0xF4, 0x5E, 0xF5, 0x22, 0x55, 0xF0, 0x95, 0xA4 }; + u8 nonce[] = { 0xBE, 0xCA, 0xF0, 0x43, 0xB0, 0xA2, 0x3D, 0x84, + 0x31, 0x94, 0xBA, 0x97, 0x2C, 0x66, 0xDE, 0xBD }; + u8 hdr[] = { 0xFA, 0x3B, 0xFD, 0x48, 0x06, 0xEB, 0x53, 0xFA }; + u8 cipher[] = { 0x19, 0xDD, 0x5C, 0x4C, 0x93, 0x31, 0x04, 0x9D, + 0x0B, 0xDA, 0xB0, 0x27, 0x74, 0x08, 0xF6, 0x79, + 0x67, 0xE5 }; + u8 data[sizeof(msg)], tag[BLOCK_SIZE]; + + memcpy(data, msg, sizeof(msg)); + if (aes_128_eax_encrypt(key, nonce, sizeof(nonce), hdr, sizeof(hdr), + data, sizeof(data), tag)) { + printf("AES-128 EAX mode encryption failed\n"); + return 1; + } + if (memcmp(data, cipher, sizeof(data)) != 0) { + printf("AES-128 EAX mode encryption returned invalid cipher " + "text\n"); + return 1; + } + if (memcmp(tag, cipher + sizeof(data), BLOCK_SIZE) != 0) { + printf("AES-128 EAX mode encryption returned invalid tag\n"); + return 1; + } + + if (aes_128_eax_decrypt(key, nonce, sizeof(nonce), hdr, sizeof(hdr), + data, sizeof(data), tag)) { + printf("AES-128 EAX mode decryption failed\n"); + return 1; + } + if (memcmp(data, msg, sizeof(data)) != 0) { + printf("AES-128 EAX mode decryption returned invalid plain " + "text\n"); + return 1; + } + + return 0; +} + + +static int test_cbc(void) +{ + struct cbc_test_vector { + u8 key[16]; + u8 iv[16]; + u8 plain[32]; + u8 cipher[32]; + size_t len; + } vectors[] = { + { + { 0x06, 0xa9, 0x21, 0x40, 0x36, 0xb8, 0xa1, 0x5b, + 0x51, 0x2e, 0x03, 0xd5, 0x34, 0x12, 0x00, 0x06 }, + { 0x3d, 0xaf, 0xba, 0x42, 0x9d, 0x9e, 0xb4, 0x30, + 0xb4, 0x22, 0xda, 0x80, 0x2c, 0x9f, 0xac, 0x41 }, + "Single block msg", + { 0xe3, 0x53, 0x77, 0x9c, 0x10, 0x79, 0xae, 0xb8, + 0x27, 0x08, 0x94, 0x2d, 0xbe, 0x77, 0x18, 0x1a }, + 16 + }, + { + { 0xc2, 0x86, 0x69, 0x6d, 0x88, 0x7c, 0x9a, 0xa0, + 0x61, 0x1b, 0xbb, 0x3e, 0x20, 0x25, 0xa4, 0x5a }, + { 0x56, 0x2e, 0x17, 0x99, 0x6d, 0x09, 0x3d, 0x28, + 0xdd, 0xb3, 0xba, 0x69, 0x5a, 0x2e, 0x6f, 0x58 }, + { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f }, + { 0xd2, 0x96, 0xcd, 0x94, 0xc2, 0xcc, 0xcf, 0x8a, + 0x3a, 0x86, 0x30, 0x28, 0xb5, 0xe1, 0xdc, 0x0a, + 0x75, 0x86, 0x60, 0x2d, 0x25, 0x3c, 0xff, 0xf9, + 0x1b, 0x82, 0x66, 0xbe, 0xa6, 0xd6, 0x1a, 0xb1 }, + 32 + } + }; + int i, ret = 0; + u8 *buf; + + for (i = 0; i < sizeof(vectors) / sizeof(vectors[0]); i++) { + struct cbc_test_vector *tv = &vectors[i]; + buf = malloc(tv->len); + if (buf == NULL) { + ret++; + break; + } + memcpy(buf, tv->plain, tv->len); + aes_128_cbc_encrypt(tv->key, tv->iv, buf, tv->len); + if (memcmp(buf, tv->cipher, tv->len) != 0) { + printf("AES-CBC encrypt %d failed\n", i); + ret++; + } + memcpy(buf, tv->cipher, tv->len); + aes_128_cbc_decrypt(tv->key, tv->iv, buf, tv->len); + if (memcmp(buf, tv->plain, tv->len) != 0) { + printf("AES-CBC decrypt %d failed\n", i); + ret++; + } + free(buf); + } + + return ret; +} + + +/* OMAC1 AES-128 test vectors from + * http://csrc.nist.gov/CryptoToolkit/modes/proposedmodes/omac/omac-ad.pdf + */ + +struct omac1_test_vector { + u8 k[16]; + u8 msg[64]; + int msg_len; + u8 tag[16]; +}; + +static struct omac1_test_vector test_vectors[] = +{ + { + { 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, + 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c }, + { }, + 0, + { 0xbb, 0x1d, 0x69, 0x29, 0xe9, 0x59, 0x37, 0x28, + 0x7f, 0xa3, 0x7d, 0x12, 0x9b, 0x75, 0x67, 0x46 } + }, + { + { 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, + 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c }, + { 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, + 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a}, + 16, + { 0x07, 0x0a, 0x16, 0xb4, 0x6b, 0x4d, 0x41, 0x44, + 0xf7, 0x9b, 0xdd, 0x9d, 0xd0, 0x4a, 0x28, 0x7c } + }, + { + { 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, + 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c }, + { 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, + 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a, + 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, + 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51, + 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11 }, + 40, + { 0xdf, 0xa6, 0x67, 0x47, 0xde, 0x9a, 0xe6, 0x30, + 0x30, 0xca, 0x32, 0x61, 0x14, 0x97, 0xc8, 0x27 } + }, + { + { 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, + 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c }, + { 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, + 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a, + 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, + 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51, + 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11, + 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef, + 0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17, + 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10 }, + 64, + { 0x51, 0xf0, 0xbe, 0xbf, 0x7e, 0x3b, 0x9d, 0x92, + 0xfc, 0x49, 0x74, 0x17, 0x79, 0x36, 0x3c, 0xfe } + }, +}; + + +int main(int argc, char *argv[]) +{ + u8 kek[] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f + }; + u8 plain[] = { + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, + 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff + }; + u8 crypt[] = { + 0x1F, 0xA6, 0x8B, 0x0A, 0x81, 0x12, 0xB4, 0x47, + 0xAE, 0xF3, 0x4B, 0xD8, 0xFB, 0x5A, 0x7B, 0x82, + 0x9D, 0x3E, 0x86, 0x23, 0x71, 0xD2, 0xCF, 0xE5 + }; + u8 result[24]; + int ret = 0, i; + struct omac1_test_vector *tv; + + aes_wrap(kek, 2, plain, result); + if (memcmp(result, crypt, 24) != 0) { + printf("AES-WRAP-128-128 failed\n"); + ret++; + } + if (aes_unwrap(kek, 2, crypt, result)) { + printf("AES-UNWRAP-128-128 reported failure\n"); + ret++; + } + if (memcmp(result, plain, 16) != 0) { + int i; + printf("AES-UNWRAP-128-128 failed\n"); + ret++; + for (i = 0; i < 16; i++) + printf(" %02x", result[i]); + printf("\n"); + } + +#ifdef __i386__ + test_aes_perf(); +#endif /* __i386__ */ + + for (i = 0; i < sizeof(test_vectors) / sizeof(test_vectors[0]); i++) { + tv = &test_vectors[i]; + omac1_aes_128(tv->k, tv->msg, tv->msg_len, result); + if (memcmp(result, tv->tag, 16) != 0) { + printf("OMAC1-AES-128 test vector %d failed\n", i); + ret++; + } + } + + ret += test_eax(); + + ret += test_cbc(); + + if (ret) + printf("FAILED!\n"); + + return ret; +} +#endif /* TEST_MAIN */ diff --git a/contrib/wpa_supplicant/aes_wrap.h b/contrib/wpa_supplicant/aes_wrap.h new file mode 100644 index 0000000..70e83ea --- /dev/null +++ b/contrib/wpa_supplicant/aes_wrap.h @@ -0,0 +1,21 @@ +#ifndef AES_WRAP_H +#define AES_WRAP_H + +void aes_wrap(u8 *kek, int n, u8 *plain, u8 *cipher); +int aes_unwrap(u8 *kek, int n, u8 *cipher, u8 *plain); +void omac1_aes_128(const u8 *key, const u8 *data, size_t data_len, u8 *mac); +void aes_128_encrypt_block(const u8 *key, const u8 *in, u8 *out); +void aes_128_ctr_encrypt(const u8 *key, const u8 *nonce, + u8 *data, size_t data_len); +int aes_128_eax_encrypt(const u8 *key, const u8 *nonce, size_t nonce_len, + const u8 *hdr, size_t hdr_len, + u8 *data, size_t data_len, u8 *tag); +int aes_128_eax_decrypt(const u8 *key, const u8 *nonce, size_t nonce_len, + const u8 *hdr, size_t hdr_len, + u8 *data, size_t data_len, const u8 *tag); +void aes_128_cbc_encrypt(const u8 *key, const u8 *iv, u8 *data, + size_t data_len); +void aes_128_cbc_decrypt(const u8 *key, const u8 *iv, u8 *data, + size_t data_len); + +#endif /* AES_WRAP_H */ diff --git a/contrib/wpa_supplicant/common.c b/contrib/wpa_supplicant/common.c new file mode 100644 index 0000000..071ffe8 --- /dev/null +++ b/contrib/wpa_supplicant/common.c @@ -0,0 +1,335 @@ +/* + * Host AP (software wireless LAN access point) user space daemon for + * Host AP kernel driver / common helper functions, etc. + * Copyright (c) 2002-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <errno.h> +#include <stdarg.h> +#include <ctype.h> +#include <time.h> +#include <sys/time.h> + +#include "common.h" + + +int wpa_debug_level = MSG_INFO; +int wpa_debug_show_keys = 0; +int wpa_debug_timestamp = 0; + + +int hostapd_get_rand(u8 *buf, size_t len) +{ +#ifdef CONFIG_NATIVE_WINDOWS + int i; + /* FIX: use more secure pseudo random number generator */ + for (i = 0; i < len; i++) { + buf[i] = rand(); + } + return 0; +#else /* CONFIG_NATIVE_WINDOWS */ + FILE *f; + size_t rc; + + f = fopen("/dev/urandom", "r"); + if (f == NULL) { + printf("Could not open /dev/urandom.\n"); + return -1; + } + + rc = fread(buf, 1, len, f); + fclose(f); + + return rc != len ? -1 : 0; +#endif /* CONFIG_NATIVE_WINDOWS */ +} + + +void hostapd_hexdump(const char *title, const u8 *buf, size_t len) +{ + size_t i; + printf("%s - hexdump(len=%lu):", title, (unsigned long) len); + for (i = 0; i < len; i++) + printf(" %02x", buf[i]); + printf("\n"); +} + + +static int hex2num(char c) +{ + if (c >= '0' && c <= '9') + return c - '0'; + if (c >= 'a' && c <= 'f') + return c - 'a' + 10; + if (c >= 'A' && c <= 'F') + return c - 'A' + 10; + return -1; +} + + +static int hex2byte(const char *hex) +{ + int a, b; + a = hex2num(*hex++); + if (a < 0) + return -1; + b = hex2num(*hex++); + if (b < 0) + return -1; + return (a << 4) | b; +} + + +int hwaddr_aton(const char *txt, u8 *addr) +{ + int i; + + for (i = 0; i < 6; i++) { + int a, b; + + a = hex2num(*txt++); + if (a < 0) + return -1; + b = hex2num(*txt++); + if (b < 0) + return -1; + *addr++ = (a << 4) | b; + if (i < 5 && *txt++ != ':') + return -1; + } + + return 0; +} + + +int hexstr2bin(const char *hex, u8 *buf, size_t len) +{ + int i, a; + const char *ipos = hex; + u8 *opos = buf; + + for (i = 0; i < len; i++) { + a = hex2byte(ipos); + if (a < 0) + return -1; + *opos++ = a; + ipos += 2; + } + return 0; +} + + +char * rel2abs_path(const char *rel_path) +{ + char *buf = NULL, *cwd, *ret; + size_t len = 128, cwd_len, rel_len, ret_len; + + if (rel_path[0] == '/') + return strdup(rel_path); + + for (;;) { + buf = malloc(len); + if (buf == NULL) + return NULL; + cwd = getcwd(buf, len); + if (cwd == NULL) { + free(buf); + if (errno != ERANGE) { + return NULL; + } + len *= 2; + } else { + break; + } + } + + cwd_len = strlen(cwd); + rel_len = strlen(rel_path); + ret_len = cwd_len + 1 + rel_len + 1; + ret = malloc(ret_len); + if (ret) { + memcpy(ret, cwd, cwd_len); + ret[cwd_len] = '/'; + memcpy(ret + cwd_len + 1, rel_path, rel_len); + ret[ret_len - 1] = '\0'; + } + free(buf); + return ret; +} + + +void inc_byte_array(u8 *counter, size_t len) +{ + int pos = len - 1; + while (pos >= 0) { + counter[pos]++; + if (counter[pos] != 0) + break; + pos--; + } +} + + +void print_char(char c) +{ + if (c >= 32 && c < 127) + printf("%c", c); + else + printf("<%02x>", c); +} + + +void fprint_char(FILE *f, char c) +{ + if (c >= 32 && c < 127) + fprintf(f, "%c", c); + else + fprintf(f, "<%02x>", c); +} + + +static void wpa_debug_print_timestamp(void) +{ + struct timeval tv; + char buf[16]; + + if (!wpa_debug_timestamp) + return; + + gettimeofday(&tv, NULL); + if (strftime(buf, sizeof(buf), "%b %d %H:%M:%S", + localtime((const time_t *) &tv.tv_sec)) <= 0) { + snprintf(buf, sizeof(buf), "%u", (int) tv.tv_sec); + } + printf("%s.%06u: ", buf, (unsigned int) tv.tv_usec); +} + + +void wpa_printf(int level, char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + if (level >= wpa_debug_level) { + wpa_debug_print_timestamp(); + vprintf(fmt, ap); + printf("\n"); + } + va_end(ap); +} + + +static void _wpa_hexdump(int level, const char *title, const u8 *buf, + size_t len, int show) +{ + size_t i; + if (level < wpa_debug_level) + return; + wpa_debug_print_timestamp(); + printf("%s - hexdump(len=%lu):", title, (unsigned long) len); + if (show) { + for (i = 0; i < len; i++) + printf(" %02x", buf[i]); + } else { + printf(" [REMOVED]"); + } + printf("\n"); +} + +void wpa_hexdump(int level, const char *title, const u8 *buf, size_t len) +{ + _wpa_hexdump(level, title, buf, len, 1); +} + + +void wpa_hexdump_key(int level, const char *title, const u8 *buf, size_t len) +{ + _wpa_hexdump(level, title, buf, len, wpa_debug_show_keys); +} + + +static void _wpa_hexdump_ascii(int level, const char *title, const u8 *buf, + size_t len, int show) +{ + int i, llen; + const u8 *pos = buf; + const int line_len = 16; + + if (level < wpa_debug_level) + return; + wpa_debug_print_timestamp(); + if (!show) { + printf("%s - hexdump_ascii(len=%lu): [REMOVED]\n", + title, (unsigned long) len); + return; + } + printf("%s - hexdump_ascii(len=%lu):\n", title, (unsigned long) len); + while (len) { + llen = len > line_len ? line_len : len; + printf(" "); + for (i = 0; i < llen; i++) + printf(" %02x", pos[i]); + for (i = llen; i < line_len; i++) + printf(" "); + printf(" "); + for (i = 0; i < llen; i++) { + if (isprint(pos[i])) + printf("%c", pos[i]); + else + printf("_"); + } + for (i = llen; i < line_len; i++) + printf(" "); + printf("\n"); + pos += llen; + len -= llen; + } +} + + +void wpa_hexdump_ascii(int level, const char *title, const u8 *buf, size_t len) +{ + _wpa_hexdump_ascii(level, title, buf, len, 1); +} + + +void wpa_hexdump_ascii_key(int level, const char *title, const u8 *buf, + size_t len) +{ + _wpa_hexdump_ascii(level, title, buf, len, wpa_debug_show_keys); +} + + +#ifdef CONFIG_NATIVE_WINDOWS + +#define EPOCHFILETIME (116444736000000000ULL) + +int gettimeofday(struct timeval *tv, struct timezone *tz) +{ + FILETIME ft; + LARGE_INTEGER li; + ULONGLONG t; + + GetSystemTimeAsFileTime(&ft); + li.LowPart = ft.dwLowDateTime; + li.HighPart = ft.dwHighDateTime; + t = (li.QuadPart - EPOCHFILETIME) / 10; + tv->tv_sec = (long) (t / 1000000); + tv->tv_usec = (long) (t % 1000000); + + return 0; +} +#endif /* CONFIG_NATIVE_WINDOWS */ diff --git a/contrib/wpa_supplicant/common.h b/contrib/wpa_supplicant/common.h new file mode 100644 index 0000000..aa6429c --- /dev/null +++ b/contrib/wpa_supplicant/common.h @@ -0,0 +1,222 @@ +#ifndef COMMON_H +#define COMMON_H + +#ifdef __linux__ +#include <endian.h> +#include <byteswap.h> +#endif +#ifdef __FreeBSD__ +#include <sys/types.h> +#include <sys/endian.h> +#define bswap_16 bswap16 +#define bswap_32 bswap32 +#endif + +#ifdef CONFIG_NATIVE_WINDOWS +#include <winsock.h> +#include <winsock2.h> + +static inline int daemon(int nochdir, int noclose) +{ + printf("Windows - daemon() not supported yet\n"); + return -1; +} + +static inline void sleep(int seconds) +{ + Sleep(seconds * 1000); +} + +static inline void usleep(unsigned long usec) +{ + Sleep(usec / 1000); +} + +#ifndef timersub +#define timersub(a, b, res) do { \ + (res)->tv_sec = (a)->tv_sec - (b)->tv_sec; \ + (res)->tv_usec = (a)->tv_usec - (b)->tv_usec; \ + if ((res)->tv_usec < 0) { \ + (res)->tv_sec--; \ + (res)->tv_usec += 1000000; \ + } \ +} while (0) +#endif + +struct timezone { + int tz_minuteswest; + int tz_dsttime; +}; + +int gettimeofday(struct timeval *tv, struct timezone *tz); + +#endif /* CONFIG_NATIVE_WINDOWS */ + +#if defined(__CYGWIN__) || defined(CONFIG_NATIVE_WINDOWS) + +static inline unsigned short wpa_swap_16(unsigned short v) +{ + return ((v & 0xff) << 8) | (v >> 8); +} + +static inline unsigned int wpa_swap_32(unsigned int v) +{ + return ((v & 0xff) << 24) | ((v & 0xff00) << 8) | + ((v & 0xff0000) >> 8) | (v >> 24); +} + +#define le_to_host16(n) (n) +#define host_to_le16(n) (n) +#define be_to_host16(n) wpa_swap_16(n) +#define host_to_be16(n) wpa_swap_16(n) +#define le_to_host32(n) (n) +#define be_to_host32(n) wpa_swap_32(n) +#define host_to_be32(n) wpa_swap_32(n) + +#else /* __CYGWIN__ */ + +#if __BYTE_ORDER == __LITTLE_ENDIAN +#define le_to_host16(n) (n) +#define host_to_le16(n) (n) +#define be_to_host16(n) bswap_16(n) +#define host_to_be16(n) bswap_16(n) +#define le_to_host32(n) (n) +#define be_to_host32(n) bswap_32(n) +#define host_to_be32(n) bswap_32(n) +#elif __BYTE_ORDER == __BIG_ENDIAN +#define le_to_host16(n) bswap_16(n) +#define host_to_le16(n) bswap_16(n) +#define be_to_host16(n) (n) +#define host_to_be16(n) (n) +#define le_to_host32(n) bswap_32(n) +#define be_to_host32(n) (n) +#define host_to_be32(n) (n) +#ifndef WORDS_BIGENDIAN +#define WORDS_BIGENDIAN +#endif +#else +#error Could not determine CPU byte order +#endif + +#endif /* __CYGWIN__ */ + + +#ifndef ETH_ALEN +#define ETH_ALEN 6 +#endif + +#include <stdint.h> +typedef uint64_t u64; +typedef uint32_t u32; +typedef uint16_t u16; +typedef uint8_t u8; +typedef int64_t s64; +typedef int32_t s32; +typedef int16_t s16; +typedef int8_t s8; + +int hostapd_get_rand(u8 *buf, size_t len); +void hostapd_hexdump(const char *title, const u8 *buf, size_t len); +int hwaddr_aton(const char *txt, u8 *addr); +int hexstr2bin(const char *hex, u8 *buf, size_t len); +char * rel2abs_path(const char *rel_path); +void inc_byte_array(u8 *counter, size_t len); +void print_char(char c); +void fprint_char(FILE *f, char c); + + +/* Debugging function - conditional printf and hex dump. Driver wrappers can + * use these for debugging purposes. */ + +enum { MSG_MSGDUMP, MSG_DEBUG, MSG_INFO, MSG_WARNING, MSG_ERROR }; + +/** + * wpa_printf - conditional printf + * @level: priority level (MSG_*) of the message + * @fmt: printf format string, followed by optional arguments + * + * This function is used to print conditional debugging and error messages. The + * output may be directed to stdout, stderr, and/or syslog based on + * configuration. + * + * Note: New line '\n' is added to the end of the text when printing to stdout. + */ +void wpa_printf(int level, char *fmt, ...) +__attribute__ ((format (printf, 2, 3))); + +/** + * wpa_hexdump - conditional hex dump + * @level: priority level (MSG_*) of the message + * @title: title of for the message + * @buf: data buffer to be dumped + * @len: length of the @buf + * + * This function is used to print conditional debugging and error messages. The + * output may be directed to stdout, stderr, and/or syslog based on + * configuration. The contents of @buf is printed out has hex dump. + */ +void wpa_hexdump(int level, const char *title, const u8 *buf, size_t len); + +/** + * wpa_hexdump_key - conditional hex dump, hide keys + * @level: priority level (MSG_*) of the message + * @title: title of for the message + * @buf: data buffer to be dumped + * @len: length of the @buf + * + * This function is used to print conditional debugging and error messages. The + * output may be directed to stdout, stderr, and/or syslog based on + * configuration. The contents of @buf is printed out has hex dump. This works + * like wpa_hexdump(), but by default, does not include secret keys (passwords, + * etc.) in debug output. + */ +void wpa_hexdump_key(int level, const char *title, const u8 *buf, size_t len); + +/** + * wpa_hexdump_ascii - conditional hex dump + * @level: priority level (MSG_*) of the message + * @title: title of for the message + * @buf: data buffer to be dumped + * @len: length of the @buf + * + * This function is used to print conditional debugging and error messages. The + * output may be directed to stdout, stderr, and/or syslog based on + * configuration. The contents of @buf is printed out has hex dump with both + * the hex numbers and ASCII characters (for printable range) are shown. 16 + * bytes per line will be shown. + */ +void wpa_hexdump_ascii(int level, const char *title, const u8 *buf, + size_t len); + +/** + * wpa_hexdump_ascii_key - conditional hex dump, hide keys + * @level: priority level (MSG_*) of the message + * @title: title of for the message + * @buf: data buffer to be dumped + * @len: length of the @buf + * + * This function is used to print conditional debugging and error messages. The + * output may be directed to stdout, stderr, and/or syslog based on + * configuration. The contents of @buf is printed out has hex dump with both + * the hex numbers and ASCII characters (for printable range) are shown. 16 + * bytes per line will be shown. This works like wpa_hexdump_ascii(), but by + * default, does not include secret keys (passwords, etc.) in debug output. + */ +void wpa_hexdump_ascii_key(int level, const char *title, const u8 *buf, + size_t len); + +#ifdef EAPOL_TEST +#define WPA_ASSERT(a) \ + do { \ + if (!(a)) { \ + printf("WPA_ASSERT FAILED '" #a "' " \ + "%s %s:%d\n", \ + __FUNCTION__, __FILE__, __LINE__); \ + exit(1); \ + } \ + } while (0) +#else +#define WPA_ASSERT(a) do { } while (0) +#endif + +#endif /* COMMON_H */ diff --git a/contrib/wpa_supplicant/config.c b/contrib/wpa_supplicant/config.c new file mode 100644 index 0000000..241c755 --- /dev/null +++ b/contrib/wpa_supplicant/config.c @@ -0,0 +1,1051 @@ +/* + * WPA Supplicant / Configuration file parser + * Copyright (c) 2003-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include "common.h" +#include "wpa.h" +#include "config.h" +#include "sha1.h" +#include "wpa_supplicant.h" +#include "eapol_sm.h" +#include "eap.h" +#include "config.h" + + +struct parse_data { + char *name; + int (*parser)(struct parse_data *data, int line, const char *value); + void *param1, *param2, *param3, *param4; + struct wpa_ssid *ssid; + int key_data; +}; + + +static char * wpa_config_get_line(char *s, int size, FILE *stream, int *line) +{ + char *pos, *end, *sstart; + + while (fgets(s, size, stream)) { + (*line)++; + s[size - 1] = '\0'; + pos = s; + + while (*pos == ' ' || *pos == '\t' || *pos == '\r') + pos++; + if (*pos == '#' || *pos == '\n' || *pos == '\0' || + *pos == '\r') + continue; + + /* Remove # comments unless they are within a double quoted + * string. Remove trailing white space. */ + sstart = strchr(pos, '"'); + if (sstart) + sstart = strchr(sstart + 1, '"'); + if (!sstart) + sstart = pos; + end = strchr(sstart, '#'); + if (end) + *end-- = '\0'; + else + end = pos + strlen(pos) - 1; + while (end > pos && + (*end == '\n' || *end == ' ' || *end == '\t' || + *end == '\r')) { + *end-- = '\0'; + } + if (*pos == '\0') + continue; + + return pos; + } + + return NULL; +} + + +static char * wpa_config_parse_string(const char *value, size_t *len) +{ + if (*value == '"') { + char *pos; + value++; + pos = strchr(value, '"'); + if (pos == NULL || pos[1] != '\0') + return NULL; + *pos = '\0'; + *len = strlen(value); + return strdup(value); + } else { + u8 *str; + int hlen = strlen(value); + if (hlen % 1) + return NULL; + *len = hlen / 2; + str = malloc(*len); + if (str == NULL) + return NULL; + if (hexstr2bin(value, str, *len)) { + free(str); + return NULL; + } + return (char *) str; + } +} + + +static int wpa_config_parse_str(struct parse_data *data, + int line, const char *value) +{ + size_t res_len, *dst_len; + char **dst; + + dst = (char **) (((u8 *) data->ssid) + (long) data->param1); + dst_len = (size_t *) (((u8 *) data->ssid) + (long) data->param2); + + free(*dst); + *dst = wpa_config_parse_string(value, &res_len); + if (*dst == NULL) { + wpa_printf(MSG_ERROR, "Line %d: failed to parse %s '%s'.", + line, data->name, value); + return -1; + } + if (data->param2) + *dst_len = res_len; + + if (data->key_data) { + wpa_hexdump_ascii_key(MSG_MSGDUMP, data->name, + (u8 *) *dst, res_len); + } else { + wpa_hexdump_ascii(MSG_MSGDUMP, data->name, + (u8 *) *dst, res_len); + } + + if (data->param3 && res_len < (size_t) data->param3) { + wpa_printf(MSG_ERROR, "Line %d: too short %s (len=%lu " + "min_len=%ld)", line, data->name, + (unsigned long) res_len, (long) data->param3); + free(*dst); + *dst = NULL; + return -1; + } + + if (data->param4 && res_len > (size_t) data->param4) { + wpa_printf(MSG_ERROR, "Line %d: too long %s (len=%lu " + "max_len=%ld)", line, data->name, + (unsigned long) res_len, (long) data->param4); + free(*dst); + *dst = NULL; + return -1; + } + + return 0; +} + + +static int wpa_config_parse_int(struct parse_data *data, + int line, const char *value) +{ + int *dst; + + dst = (int *) (((u8 *) data->ssid) + (long) data->param1); + *dst = atoi(value); + wpa_printf(MSG_MSGDUMP, "%s=%d (0x%x)", data->name, *dst, *dst); + + if (data->param3 && *dst < (long) data->param3) { + wpa_printf(MSG_ERROR, "Line %d: too small %s (value=%d " + "min_value=%ld)", line, data->name, *dst, + (long) data->param3); + *dst = (long) data->param3; + return -1; + } + + if (data->param4 && *dst > (long) data->param4) { + wpa_printf(MSG_ERROR, "Line %d: too large %s (value=%d " + "max_value=%ld)", line, data->name, *dst, + (long) data->param4); + *dst = (long) data->param4; + return -1; + } + + return 0; +} + + +static int wpa_config_parse_bssid(struct parse_data *data, int line, + const char *value) +{ + if (hwaddr_aton(value, data->ssid->bssid)) { + wpa_printf(MSG_ERROR, "Line %d: Invalid BSSID '%s'.", + line, value); + return -1; + } + data->ssid->bssid_set = 1; + wpa_hexdump(MSG_MSGDUMP, "BSSID", data->ssid->bssid, ETH_ALEN); + return 0; +} + + +static int wpa_config_parse_psk(struct parse_data *data, int line, + const char *value) +{ + if (*value == '"') { + char *pos; + int len; + + value++; + pos = strrchr(value, '"'); + if (pos) + *pos = '\0'; + len = strlen(value); + if (len < 8 || len > 63) { + wpa_printf(MSG_ERROR, "Line %d: Invalid passphrase " + "length %d (expected: 8..63) '%s'.", + line, len, value); + return -1; + } + wpa_hexdump_ascii_key(MSG_MSGDUMP, "PSK (ASCII passphrase)", + (u8 *) value, len); + data->ssid->passphrase = strdup(value); + return data->ssid->passphrase == NULL ? -1 : 0; + } + + if (hexstr2bin(value, data->ssid->psk, PMK_LEN) || + value[PMK_LEN * 2] != '\0') { + wpa_printf(MSG_ERROR, "Line %d: Invalid PSK '%s'.", + line, value); + return -1; + } + data->ssid->psk_set = 1; + wpa_hexdump_key(MSG_MSGDUMP, "PSK", data->ssid->psk, PMK_LEN); + return 0; +} + + +static int wpa_config_parse_proto(struct parse_data *data, int line, + const char *value) +{ + int val = 0, last, errors = 0; + char *start, *end, *buf; + + buf = strdup(value); + if (buf == NULL) + return -1; + start = buf; + + while (start != '\0') { + while (*start == ' ' || *start == '\t') + start++; + if (*start == '\0') + break; + end = start; + while (*end != ' ' && *end != '\t' && *end != '\0') + end++; + last = *end == '\0'; + *end = '\0'; + if (strcmp(start, "WPA") == 0) + val |= WPA_PROTO_WPA; + else if (strcmp(start, "RSN") == 0 || + strcmp(start, "WPA2") == 0) + val |= WPA_PROTO_RSN; + else { + wpa_printf(MSG_ERROR, "Line %d: invalid proto '%s'", + line, start); + errors++; + } + + if (last) + break; + start = end + 1; + } + free(buf); + + if (val == 0) { + wpa_printf(MSG_ERROR, + "Line %d: no proto values configured.", line); + errors++; + } + + wpa_printf(MSG_MSGDUMP, "proto: 0x%x", val); + data->ssid->proto = val; + return errors ? -1 : 0; +} + + +static int wpa_config_parse_key_mgmt(struct parse_data *data, int line, + const char *value) +{ + int val = 0, last, errors = 0; + char *start, *end, *buf; + + buf = strdup(value); + if (buf == NULL) + return -1; + start = buf; + + while (start != '\0') { + while (*start == ' ' || *start == '\t') + start++; + if (*start == '\0') + break; + end = start; + while (*end != ' ' && *end != '\t' && *end != '\0') + end++; + last = *end == '\0'; + *end = '\0'; + if (strcmp(start, "WPA-PSK") == 0) + val |= WPA_KEY_MGMT_PSK; + else if (strcmp(start, "WPA-EAP") == 0) + val |= WPA_KEY_MGMT_IEEE8021X; + else if (strcmp(start, "IEEE8021X") == 0) + val |= WPA_KEY_MGMT_IEEE8021X_NO_WPA; + else if (strcmp(start, "NONE") == 0) + val |= WPA_KEY_MGMT_NONE; + else if (strcmp(start, "WPA-NONE") == 0) + val |= WPA_KEY_MGMT_WPA_NONE; + else { + wpa_printf(MSG_ERROR, "Line %d: invalid key_mgmt '%s'", + line, start); + errors++; + } + + if (last) + break; + start = end + 1; + } + free(buf); + + if (val == 0) { + wpa_printf(MSG_ERROR, + "Line %d: no key_mgmt values configured.", line); + errors++; + } + + wpa_printf(MSG_MSGDUMP, "key_mgmt: 0x%x", val); + data->ssid->key_mgmt = val; + return errors ? -1 : 0; +} + + +static int wpa_config_parse_cipher(int line, const char *value) +{ + int val = 0, last; + char *start, *end, *buf; + + buf = strdup(value); + if (buf == NULL) + return -1; + start = buf; + + while (start != '\0') { + while (*start == ' ' || *start == '\t') + start++; + if (*start == '\0') + break; + end = start; + while (*end != ' ' && *end != '\t' && *end != '\0') + end++; + last = *end == '\0'; + *end = '\0'; + if (strcmp(start, "CCMP") == 0) + val |= WPA_CIPHER_CCMP; + else if (strcmp(start, "TKIP") == 0) + val |= WPA_CIPHER_TKIP; + else if (strcmp(start, "WEP104") == 0) + val |= WPA_CIPHER_WEP104; + else if (strcmp(start, "WEP40") == 0) + val |= WPA_CIPHER_WEP40; + else if (strcmp(start, "NONE") == 0) + val |= WPA_CIPHER_NONE; + else { + wpa_printf(MSG_ERROR, "Line %d: invalid cipher '%s'.", + line, start); + free(buf); + return -1; + } + + if (last) + break; + start = end + 1; + } + free(buf); + + if (val == 0) { + wpa_printf(MSG_ERROR, "Line %d: no cipher values configured.", + line); + return -1; + } + return val; +} + + +static int wpa_config_parse_pairwise(struct parse_data *data, int line, + const char *value) +{ + int val; + val = wpa_config_parse_cipher(line, value); + if (val == -1) + return -1; + if (val & ~(WPA_CIPHER_CCMP | WPA_CIPHER_TKIP | WPA_CIPHER_NONE)) { + wpa_printf(MSG_ERROR, "Line %d: not allowed pairwise cipher " + "(0x%x).", line, val); + return -1; + } + + wpa_printf(MSG_MSGDUMP, "pairwise: 0x%x", val); + data->ssid->pairwise_cipher = val; + return 0; +} + + +static int wpa_config_parse_group(struct parse_data *data, int line, + const char *value) +{ + int val; + val = wpa_config_parse_cipher(line, value); + if (val == -1) + return -1; + if (val & ~(WPA_CIPHER_CCMP | WPA_CIPHER_TKIP | WPA_CIPHER_WEP104 | + WPA_CIPHER_WEP40)) { + wpa_printf(MSG_ERROR, "Line %d: not allowed group cipher " + "(0x%x).", line, val); + return -1; + } + + wpa_printf(MSG_MSGDUMP, "group: 0x%x", val); + data->ssid->group_cipher = val; + return 0; +} + + +static int wpa_config_parse_auth_alg(struct parse_data *data, int line, + const char *value) +{ + int val = 0, last, errors = 0; + char *start, *end, *buf; + + buf = strdup(value); + if (buf == NULL) + return -1; + start = buf; + + while (start != '\0') { + while (*start == ' ' || *start == '\t') + start++; + if (*start == '\0') + break; + end = start; + while (*end != ' ' && *end != '\t' && *end != '\0') + end++; + last = *end == '\0'; + *end = '\0'; + if (strcmp(start, "OPEN") == 0) + val |= WPA_AUTH_ALG_OPEN; + else if (strcmp(start, "SHARED") == 0) + val |= WPA_AUTH_ALG_SHARED; + else if (strcmp(start, "LEAP") == 0) + val |= WPA_AUTH_ALG_LEAP; + else { + wpa_printf(MSG_ERROR, "Line %d: invalid auth_alg '%s'", + line, start); + errors++; + } + + if (last) + break; + start = end + 1; + } + free(buf); + + if (val == 0) { + wpa_printf(MSG_ERROR, + "Line %d: no auth_alg values configured.", line); + errors++; + } + + wpa_printf(MSG_MSGDUMP, "auth_alg: 0x%x", val); + data->ssid->auth_alg = val; + return errors ? -1 : 0; +} + + +static int wpa_config_parse_eap(struct parse_data *data, int line, + const char *value) +{ + int last, errors = 0; + char *start, *end, *buf; + u8 *methods = NULL, *tmp; + size_t num_methods = 0; + + buf = strdup(value); + if (buf == NULL) + return -1; + start = buf; + + while (start != '\0') { + while (*start == ' ' || *start == '\t') + start++; + if (*start == '\0') + break; + end = start; + while (*end != ' ' && *end != '\t' && *end != '\0') + end++; + last = *end == '\0'; + *end = '\0'; + tmp = methods; + methods = realloc(methods, num_methods + 1); + if (methods == NULL) { + free(tmp); + return -1; + } + methods[num_methods] = eap_get_type(start); + if (methods[num_methods] == EAP_TYPE_NONE) { + wpa_printf(MSG_ERROR, "Line %d: unknown EAP method " + "'%s'", line, start); + wpa_printf(MSG_ERROR, "You may need to add support for" + " this EAP method during wpa_supplicant\n" + "build time configuration.\n" + "See README for more information."); + errors++; + } else if (methods[num_methods] == EAP_TYPE_LEAP) + data->ssid->leap++; + else + data->ssid->non_leap++; + num_methods++; + if (last) + break; + start = end + 1; + } + free(buf); + + tmp = methods; + methods = realloc(methods, num_methods + 1); + if (methods == NULL) { + free(tmp); + return -1; + } + methods[num_methods] = EAP_TYPE_NONE; + num_methods++; + + wpa_hexdump(MSG_MSGDUMP, "eap methods", methods, num_methods); + data->ssid->eap_methods = methods; + return errors ? -1 : 0; +} + + +static int wpa_config_parse_wep_key(u8 *key, size_t *len, int line, + const char *value, int idx) +{ + char *buf, title[20]; + + buf = wpa_config_parse_string(value, len); + if (buf == NULL) { + wpa_printf(MSG_ERROR, "Line %d: Invalid WEP key %d '%s'.", + line, idx, value); + return -1; + } + if (*len > MAX_WEP_KEY_LEN) { + wpa_printf(MSG_ERROR, "Line %d: Too long WEP key %d '%s'.", + line, idx, value); + free(buf); + return -1; + } + memcpy(key, buf, *len); + free(buf); + snprintf(title, sizeof(title), "wep_key%d", idx); + wpa_hexdump_key(MSG_MSGDUMP, title, key, *len); + return 0; +} + + +static int wpa_config_parse_wep_key0(struct parse_data *data, int line, + const char *value) +{ + return wpa_config_parse_wep_key(data->ssid->wep_key[0], + &data->ssid->wep_key_len[0], line, + value, 0); +} + + +static int wpa_config_parse_wep_key1(struct parse_data *data, int line, + const char *value) +{ + return wpa_config_parse_wep_key(data->ssid->wep_key[1], + &data->ssid->wep_key_len[1], line, + value, 1); +} + + +static int wpa_config_parse_wep_key2(struct parse_data *data, int line, + const char *value) +{ + return wpa_config_parse_wep_key(data->ssid->wep_key[2], + &data->ssid->wep_key_len[2], line, + value, 2); +} + + +static int wpa_config_parse_wep_key3(struct parse_data *data, int line, + const char *value) +{ + return wpa_config_parse_wep_key(data->ssid->wep_key[3], + &data->ssid->wep_key_len[3], line, + value, 3); +} + + +#define OFFSET(v) ((void *) &((struct wpa_ssid *) 0)->v) +#define STR(f) .name = #f, .parser = wpa_config_parse_str, .param1 = OFFSET(f) +#define STR_LEN(f) STR(f), .param2 = OFFSET(f ## _len) +#define STR_RANGE(f, min, max) STR_LEN(f), .param3 = (void *) (min), \ + .param4 = (void *) (max) +#define INT(f) .name = #f, .parser = wpa_config_parse_int, \ + .param1 = OFFSET(f), .param2 = (void *) 0 +#define INT_RANGE(f, min, max) INT(f), .param3 = (void *) (min), \ + .param4 = (void *) (max) +#define FUNC(f) .name = #f, .parser = wpa_config_parse_ ## f + +static struct parse_data ssid_fields[] = { + { STR_RANGE(ssid, 0, MAX_SSID_LEN) }, + { INT_RANGE(scan_ssid, 0, 1) }, + { FUNC(bssid) }, + { FUNC(psk), .key_data = 1 }, + { FUNC(proto) }, + { FUNC(key_mgmt) }, + { FUNC(pairwise) }, + { FUNC(group) }, + { FUNC(auth_alg) }, + { FUNC(eap) }, + { STR_LEN(identity) }, + { STR_LEN(anonymous_identity) }, + { STR_RANGE(eappsk, EAP_PSK_LEN, EAP_PSK_LEN), .key_data = 1 }, + { STR_LEN(nai) }, + { STR_LEN(server_nai) }, + { STR_LEN(password), .key_data = 1 }, + { STR(ca_cert) }, + { STR(client_cert) }, + { STR(private_key) }, + { STR(private_key_passwd), .key_data = 1 }, + { STR(dh_file) }, + { STR(subject_match) }, + { STR(ca_cert2) }, + { STR(client_cert2) }, + { STR(private_key2) }, + { STR(private_key2_passwd), .key_data = 1 }, + { STR(dh_file2) }, + { STR(subject_match2) }, + { STR(phase1) }, + { STR(phase2) }, + { STR(pcsc) }, + { STR(pin), .key_data = 1 }, + { INT(eapol_flags) }, + { FUNC(wep_key0), .key_data = 1 }, + { FUNC(wep_key1), .key_data = 1 }, + { FUNC(wep_key2), .key_data = 1 }, + { FUNC(wep_key3), .key_data = 1 }, + { INT(wep_tx_keyidx) }, + { INT(priority) }, + { INT(eap_workaround) }, + { STR(pac_file) }, + { INT_RANGE(mode, 0, 1) }, +}; + +#undef OFFSET +#undef STR +#undef STR_LEN +#undef STR_RANGE +#undef INT +#undef INT_RANGE +#undef FUNC +#define NUM_SSID_FIELDS (sizeof(ssid_fields) / sizeof(ssid_fields[0])) + + +static struct wpa_ssid * wpa_config_read_network(FILE *f, int *line, int id) +{ + struct wpa_ssid *ssid; + int errors = 0, i, end = 0; + char buf[256], *pos, *pos2; + + wpa_printf(MSG_MSGDUMP, "Line: %d - start of a new network block", + *line); + ssid = (struct wpa_ssid *) malloc(sizeof(*ssid)); + if (ssid == NULL) + return NULL; + memset(ssid, 0, sizeof(*ssid)); + ssid->id = id; + + ssid->proto = WPA_PROTO_WPA | WPA_PROTO_RSN; + ssid->pairwise_cipher = WPA_CIPHER_CCMP | WPA_CIPHER_TKIP; + ssid->group_cipher = WPA_CIPHER_CCMP | WPA_CIPHER_TKIP | + WPA_CIPHER_WEP104 | WPA_CIPHER_WEP40; + ssid->key_mgmt = WPA_KEY_MGMT_PSK | WPA_KEY_MGMT_IEEE8021X; + ssid->eapol_flags = EAPOL_FLAG_REQUIRE_KEY_UNICAST | + EAPOL_FLAG_REQUIRE_KEY_BROADCAST; + ssid->eap_workaround = (unsigned int) -1; + + while ((pos = wpa_config_get_line(buf, sizeof(buf), f, line))) { + if (strcmp(pos, "}") == 0) { + end = 1; + break; + } + + pos2 = strchr(pos, '='); + if (pos2 == NULL) { + wpa_printf(MSG_ERROR, "Line %d: Invalid SSID line " + "'%s'.", *line, pos); + errors++; + continue; + } + + *pos2++ = '\0'; + if (*pos2 == '"') { + if (strchr(pos2 + 1, '"') == NULL) { + wpa_printf(MSG_ERROR, "Line %d: invalid " + "quotation '%s'.", *line, pos2); + errors++; + continue; + } + } + + for (i = 0; i < NUM_SSID_FIELDS; i++) { + struct parse_data *field = &ssid_fields[i]; + if (strcmp(pos, field->name) != 0) + continue; + + field->ssid = ssid; + if (field->parser(field, *line, pos2)) { + wpa_printf(MSG_ERROR, "Line %d: failed to " + "parse %s '%s'.", *line, pos, pos2); + errors++; + } + break; + } + if (i == NUM_SSID_FIELDS) { + wpa_printf(MSG_ERROR, "Line %d: unknown network field " + "'%s'.", *line, pos); + errors++; + } + } + + if (!end) { + wpa_printf(MSG_ERROR, "Line %d: network block was not " + "terminated properly.", *line); + errors++; + } + + if (ssid->passphrase) { + if (ssid->psk_set) { + wpa_printf(MSG_ERROR, "Line %d: both PSK and " + "passphrase configured.", *line); + errors++; + } + pbkdf2_sha1(ssid->passphrase, + (char *) ssid->ssid, ssid->ssid_len, 4096, + ssid->psk, PMK_LEN); + wpa_hexdump_key(MSG_MSGDUMP, "PSK (from passphrase)", + ssid->psk, PMK_LEN); + ssid->psk_set = 1; + } + + if ((ssid->key_mgmt & WPA_KEY_MGMT_PSK) && !ssid->psk_set) { + wpa_printf(MSG_ERROR, "Line %d: WPA-PSK accepted for key " + "management, but no PSK configured.", *line); + errors++; + } + + if ((ssid->group_cipher & WPA_CIPHER_CCMP) && + !(ssid->pairwise_cipher & WPA_CIPHER_CCMP)) { + /* Group cipher cannot be stronger than the pairwise cipher. */ + wpa_printf(MSG_DEBUG, "Line %d: removed CCMP from group cipher" + " list since it was not allowed for pairwise " + "cipher", *line); + ssid->group_cipher &= ~WPA_CIPHER_CCMP; + } + + if (errors) { + free(ssid); + ssid = NULL; + } + + return ssid; +} + + +static int wpa_config_add_prio_network(struct wpa_config *config, + struct wpa_ssid *ssid) +{ + int prio; + struct wpa_ssid *prev, **nlist; + + for (prio = 0; prio < config->num_prio; prio++) { + prev = config->pssid[prio]; + if (prev->priority == ssid->priority) { + while (prev->pnext) + prev = prev->pnext; + prev->pnext = ssid; + return 0; + } + } + + /* First network for this priority - add new priority list */ + nlist = realloc(config->pssid, + (config->num_prio + 1) * sizeof(struct wpa_ssid *)); + if (nlist == NULL) + return -1; + + for (prio = 0; prio < config->num_prio; prio++) { + if (nlist[prio]->priority < ssid->priority) + break; + } + + memmove(&nlist[prio + 1], &nlist[prio], + (config->num_prio - prio) * sizeof(struct wpa_ssid *)); + + nlist[prio] = ssid; + config->num_prio++; + config->pssid = nlist; + + return 0; +} + + +struct wpa_config * wpa_config_read(const char *config_file) +{ + FILE *f; + char buf[256], *pos; + int errors = 0, line = 0; + struct wpa_ssid *ssid, *tail = NULL, *head = NULL; + struct wpa_config *config; + int id = 0, prio; + + config = malloc(sizeof(*config)); + if (config == NULL) + return NULL; + memset(config, 0, sizeof(*config)); + config->eapol_version = 1; + config->ap_scan = 1; + config->fast_reauth = 1; + wpa_printf(MSG_DEBUG, "Reading configuration file '%s'", + config_file); + f = fopen(config_file, "r"); + if (f == NULL) { + free(config); + return NULL; + } + + while ((pos = wpa_config_get_line(buf, sizeof(buf), f, &line))) { + if (strcmp(pos, "network={") == 0) { + ssid = wpa_config_read_network(f, &line, id++); + if (ssid == NULL) { + wpa_printf(MSG_ERROR, "Line %d: failed to " + "parse network block.", line); + errors++; + continue; + } + if (head == NULL) { + head = tail = ssid; + } else { + tail->next = ssid; + tail = ssid; + } + if (wpa_config_add_prio_network(config, ssid)) { + wpa_printf(MSG_ERROR, "Line %d: failed to add " + "network block to priority list.", + line); + errors++; + continue; + } +#ifdef CONFIG_CTRL_IFACE + } else if (strncmp(pos, "ctrl_interface=", 15) == 0) { + free(config->ctrl_interface); + config->ctrl_interface = strdup(pos + 15); + wpa_printf(MSG_DEBUG, "ctrl_interface='%s'", + config->ctrl_interface); +#ifndef CONFIG_CTRL_IFACE_UDP + } else if (strncmp(pos, "ctrl_interface_group=", 21) == 0) { + struct group *grp; + char *endp; + const char *group = pos + 21; + + grp = getgrnam(group); + if (grp) { + config->ctrl_interface_gid = grp->gr_gid; + config->ctrl_interface_gid_set = 1; + wpa_printf(MSG_DEBUG, "ctrl_interface_group=%d" + " (from group name '%s')", + (int) config->ctrl_interface_gid, + group); + continue; + } + + /* Group name not found - try to parse this as gid */ + config->ctrl_interface_gid = strtol(group, &endp, 10); + if (*group == '\0' || *endp != '\0') { + wpa_printf(MSG_DEBUG, "Line %d: Invalid group " + "'%s'", line, group); + errors++; + continue; + } + wpa_printf(MSG_DEBUG, "ctrl_interface_group=%d", + (int) config->ctrl_interface_gid); +#endif /* CONFIG_CTRL_IFACE_UDP */ +#endif /* CONFIG_CTRL_IFACE */ + } else if (strncmp(pos, "eapol_version=", 14) == 0) { + config->eapol_version = atoi(pos + 14); + if (config->eapol_version < 1 || + config->eapol_version > 2) { + wpa_printf(MSG_ERROR, "Line %d: Invalid EAPOL " + "version (%d): '%s'.", + line, config->eapol_version, pos); + errors++; + continue; + } + wpa_printf(MSG_DEBUG, "eapol_version=%d", + config->eapol_version); + } else if (strncmp(pos, "ap_scan=", 8) == 0) { + config->ap_scan = atoi(pos + 8); + wpa_printf(MSG_DEBUG, "ap_scan=%d", config->ap_scan); + } else if (strncmp(pos, "fast_reauth=", 12) == 0) { + config->fast_reauth = atoi(pos + 12); + wpa_printf(MSG_DEBUG, "fast_reauth=%d", + config->fast_reauth); + } else { + wpa_printf(MSG_ERROR, "Line %d: Invalid configuration " + "line '%s'.", line, pos); + errors++; + continue; + } + } + + fclose(f); + + config->ssid = head; + for (prio = 0; prio < config->num_prio; prio++) { + ssid = config->pssid[prio]; + wpa_printf(MSG_DEBUG, "Priority group %d", + ssid->priority); + while (ssid) { + wpa_printf(MSG_DEBUG, " id=%d ssid='%s'", + ssid->id, + wpa_ssid_txt(ssid->ssid, ssid->ssid_len)); + ssid = ssid->pnext; + } + } + if (errors) { + wpa_config_free(config); + config = NULL; + head = NULL; + } + + return config; +} + + +void wpa_config_free(struct wpa_config *config) +{ + struct wpa_ssid *ssid, *prev = NULL; + ssid = config->ssid; + while (ssid) { + prev = ssid; + ssid = ssid->next; + free(prev->ssid); + free(prev->passphrase); + free(prev->eap_methods); + free(prev->identity); + free(prev->anonymous_identity); + free(prev->eappsk); + free(prev->nai); + free(prev->server_nai); + free(prev->password); + free(prev->ca_cert); + free(prev->client_cert); + free(prev->private_key); + free(prev->private_key_passwd); + free(prev->dh_file); + free(prev->subject_match); + free(prev->ca_cert2); + free(prev->client_cert2); + free(prev->private_key2); + free(prev->private_key2_passwd); + free(prev->dh_file2); + free(prev->subject_match2); + free(prev->phase1); + free(prev->phase2); + free(prev->pcsc); + free(prev->pin); + free(prev->otp); + free(prev->pending_req_otp); + free(prev->pac_file); + free(prev); + } + free(config->ctrl_interface); + free(config->pssid); + free(config); +} + + +int wpa_config_allowed_eap_method(struct wpa_ssid *ssid, int method) +{ + u8 *pos; + + if (ssid == NULL || ssid->eap_methods == NULL) + return 1; + + pos = ssid->eap_methods; + while (*pos != EAP_TYPE_NONE) { + if (*pos == method) + return 1; + pos++; + } + return 0; +} + + +const char * wpa_cipher_txt(int cipher) +{ + switch (cipher) { + case WPA_CIPHER_NONE: + return "NONE"; + case WPA_CIPHER_WEP40: + return "WEP-40"; + case WPA_CIPHER_WEP104: + return "WEP-104"; + case WPA_CIPHER_TKIP: + return "TKIP"; + case WPA_CIPHER_CCMP: + return "CCMP"; + default: + return "UNKNOWN"; + } +} + + +const char * wpa_key_mgmt_txt(int key_mgmt, int proto) +{ + switch (key_mgmt) { + case WPA_KEY_MGMT_IEEE8021X: + return proto == WPA_PROTO_RSN ? + "WPA2/IEEE 802.1X/EAP" : "WPA/IEEE 802.1X/EAP"; + case WPA_KEY_MGMT_PSK: + return proto == WPA_PROTO_RSN ? + "WPA2-PSK" : "WPA-PSK"; + case WPA_KEY_MGMT_NONE: + return "NONE"; + case WPA_KEY_MGMT_IEEE8021X_NO_WPA: + return "IEEE 802.1X (no WPA)"; + default: + return "UNKNOWN"; + } +} diff --git a/contrib/wpa_supplicant/config.h b/contrib/wpa_supplicant/config.h new file mode 100644 index 0000000..13deb3e --- /dev/null +++ b/contrib/wpa_supplicant/config.h @@ -0,0 +1,33 @@ +#ifndef CONFIG_H +#define CONFIG_H + +#ifdef CONFIG_CTRL_IFACE +#ifndef CONFIG_CTRL_IFACE_UDP +#include <grp.h> +#endif /* CONFIG_CTRL_IFACE_UDP */ +#endif /* CONFIG_CTRL_IFACE */ + +#include "config_ssid.h" + +struct wpa_config { + struct wpa_ssid *ssid; /* global network list */ + struct wpa_ssid **pssid; /* per priority network lists (in priority + * order) */ + int num_prio; /* number of different priorities */ + int eapol_version; + int ap_scan; + char *ctrl_interface; /* directory for UNIX domain sockets */ +#ifdef CONFIG_CTRL_IFACE +#ifndef CONFIG_CTRL_IFACE_UDP + gid_t ctrl_interface_gid; +#endif /* CONFIG_CTRL_IFACE_UDP */ + int ctrl_interface_gid_set; +#endif /* CONFIG_CTRL_IFACE */ + int fast_reauth; +}; + + +struct wpa_config * wpa_config_read(const char *config_file); +void wpa_config_free(struct wpa_config *ssid); + +#endif /* CONFIG_H */ diff --git a/contrib/wpa_supplicant/config_ssid.h b/contrib/wpa_supplicant/config_ssid.h new file mode 100644 index 0000000..44bc989 --- /dev/null +++ b/contrib/wpa_supplicant/config_ssid.h @@ -0,0 +1,109 @@ +#ifndef CONFIG_SSID_H +#define CONFIG_SSID_H + +#define WPA_CIPHER_NONE BIT(0) +#define WPA_CIPHER_WEP40 BIT(1) +#define WPA_CIPHER_WEP104 BIT(2) +#define WPA_CIPHER_TKIP BIT(3) +#define WPA_CIPHER_CCMP BIT(4) + +#define WPA_KEY_MGMT_IEEE8021X BIT(0) +#define WPA_KEY_MGMT_PSK BIT(1) +#define WPA_KEY_MGMT_NONE BIT(2) +#define WPA_KEY_MGMT_IEEE8021X_NO_WPA BIT(3) +#define WPA_KEY_MGMT_WPA_NONE BIT(4) + +#define WPA_PROTO_WPA BIT(0) +#define WPA_PROTO_RSN BIT(1) + +#define WPA_AUTH_ALG_OPEN BIT(0) +#define WPA_AUTH_ALG_SHARED BIT(1) +#define WPA_AUTH_ALG_LEAP BIT(2) + +#define MAX_SSID_LEN 32 +#define PMK_LEN 32 +#define EAP_PSK_LEN 16 + +struct wpa_ssid { + struct wpa_ssid *next; /* next network in global list */ + struct wpa_ssid *pnext; /* next network in per-priority list */ + int id; /* unique id for ctrl_iface */ + int priority; + u8 *ssid; + size_t ssid_len; + u8 bssid[ETH_ALEN]; + int bssid_set; + u8 psk[PMK_LEN]; + int psk_set; + char *passphrase; + /* Bitfields of allowed Pairwise/Group Ciphers, WPA_CIPHER_* */ + int pairwise_cipher; + int group_cipher; + int key_mgmt; + int proto; /* Bitfield of allowed protocols (WPA_PROTO_*) */ + int auth_alg; /* Bitfield of allow authentication algorithms + * (WPA_AUTH_ALG_*) */ + int scan_ssid; /* scan this SSID with Probe Requests */ + u8 *identity; /* EAP Identity */ + size_t identity_len; + u8 *anonymous_identity; /* Anonymous EAP Identity (for unencrypted use + * with EAP types that support different + * tunnelled identity, e.g., EAP-TTLS) */ + size_t anonymous_identity_len; + u8 *eappsk; + size_t eappsk_len; + u8 *nai; + size_t nai_len; + u8 *server_nai; + size_t server_nai_len; + u8 *password; + size_t password_len; + u8 *ca_cert; + u8 *client_cert; + u8 *private_key; + u8 *private_key_passwd; + u8 *dh_file; + u8 *subject_match; + u8 *ca_cert2; + u8 *client_cert2; + u8 *private_key2; + u8 *private_key2_passwd; + u8 *dh_file2; + u8 *subject_match2; + u8 *eap_methods; /* zero (EAP_TYPE_NONE) terminated list of allowed + * EAP methods or NULL = any */ + char *phase1; + char *phase2; + char *pcsc; + char *pin; + +#define EAPOL_FLAG_REQUIRE_KEY_UNICAST BIT(0) +#define EAPOL_FLAG_REQUIRE_KEY_BROADCAST BIT(1) + int eapol_flags; /* bit field of IEEE 802.1X/EAPOL options */ + +#define NUM_WEP_KEYS 4 +#define MAX_WEP_KEY_LEN 16 + u8 wep_key[NUM_WEP_KEYS][MAX_WEP_KEY_LEN]; + size_t wep_key_len[NUM_WEP_KEYS]; + int wep_tx_keyidx; + + /* Per SSID variables that are not read from the configuration file */ + u8 *otp; + size_t otp_len; + int pending_req_identity, pending_req_password; + char *pending_req_otp; + size_t pending_req_otp_len; + int leap, non_leap; + + unsigned int eap_workaround; + + char *pac_file; + + int mode; +}; + +int wpa_config_allowed_eap_method(struct wpa_ssid *ssid, int method); +const char * wpa_cipher_txt(int cipher); +const char * wpa_key_mgmt_txt(int key_mgmt, int proto); + +#endif /* CONFIG_SSID_H */ diff --git a/contrib/wpa_supplicant/crypto.c b/contrib/wpa_supplicant/crypto.c new file mode 100644 index 0000000..cd278e0 --- /dev/null +++ b/contrib/wpa_supplicant/crypto.c @@ -0,0 +1,71 @@ +/* + * WPA Supplicant / wrapper functions for libcrypto + * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include <openssl/md4.h> +#include <openssl/des.h> + +#include "common.h" + + +#if OPENSSL_VERSION_NUMBER < 0x00907000 +#define DES_key_schedule des_key_schedule +#define DES_cblock des_cblock +#define DES_set_key(key, schedule) des_set_key((key), *(schedule)) +#define DES_ecb_encrypt(input, output, ks, enc) \ + des_ecb_encrypt((input), (output), *(ks), (enc)) +#endif /* openssl < 0.9.7 */ + + +void md4_vector(size_t num_elem, const u8 *addr[], size_t *len, u8 *mac) +{ + MD4_CTX ctx; + int i; + + MD4_Init(&ctx); + for (i = 0; i < num_elem; i++) + MD4_Update(&ctx, addr[i], len[i]); + MD4_Final(mac, &ctx); +} + + +void md4(const u8 *addr, size_t len, u8 *mac) +{ + md4_vector(1, &addr, &len, mac); +} + + +/** + * @clear: 8 octets (in) + * @key: 7 octets (in) (no parity bits included) + * @cypher: 8 octets (out) + */ +void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher) +{ + u8 pkey[8], next, tmp; + int i; + DES_key_schedule ks; + + /* Add parity bits to the key */ + next = 0; + for (i = 0; i < 7; i++) { + tmp = key[i]; + pkey[i] = (tmp >> i) | next | 1; + next = tmp << (7 - i); + } + pkey[i] = next | 1; + + DES_set_key(&pkey, &ks); + DES_ecb_encrypt((DES_cblock *) clear, (DES_cblock *) cypher, &ks, + DES_ENCRYPT); +} diff --git a/contrib/wpa_supplicant/crypto.h b/contrib/wpa_supplicant/crypto.h new file mode 100644 index 0000000..3e1a0e5 --- /dev/null +++ b/contrib/wpa_supplicant/crypto.h @@ -0,0 +1,8 @@ +#ifndef CRYPTO_H +#define CRYPTO_H + +void md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac); +void md4(const u8 *addr, size_t len, u8 *mac); +void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher); + +#endif /* CRYPTO_H */ diff --git a/contrib/wpa_supplicant/ctrl_iface.c b/contrib/wpa_supplicant/ctrl_iface.c new file mode 100644 index 0000000..e418811 --- /dev/null +++ b/contrib/wpa_supplicant/ctrl_iface.c @@ -0,0 +1,728 @@ +/* + * WPA Supplicant / UNIX domain socket -based control interface + * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <errno.h> +#ifndef CONFIG_NATIVE_WINDOWS +#include <sys/socket.h> +#include <sys/un.h> +#include <sys/uio.h> +#endif /* CONFIG_NATIVE_WINDOWS */ + +#include "common.h" +#include "eloop.h" +#include "wpa.h" +#include "wpa_supplicant.h" +#include "config.h" +#include "eapol_sm.h" +#include "wpa_supplicant_i.h" +#include "ctrl_iface.h" +#include "l2_packet.h" + + +#ifdef CONFIG_NATIVE_WINDOWS +typedef int socklen_t; +#endif /* CONFIG_NATIVE_WINDOWS */ + +#ifdef CONFIG_CTRL_IFACE_UDP +#define CTRL_IFACE_SOCK struct sockaddr_in +#else /* CONFIG_CTRL_IFACE_UDP */ +#define CTRL_IFACE_SOCK struct sockaddr_un +#endif /* CONFIG_CTRL_IFACE_UDP */ + + +struct wpa_ctrl_dst { + struct wpa_ctrl_dst *next; + CTRL_IFACE_SOCK addr; + socklen_t addrlen; + int debug_level; + int errors; +}; + + +static const char * wpa_state_txt(int state) +{ + switch (state) { + case WPA_DISCONNECTED: + return "DISCONNECTED"; + case WPA_SCANNING: + return "SCANNING"; + case WPA_ASSOCIATING: + return "ASSOCIATING"; + case WPA_ASSOCIATED: + return "ASSOCIATED"; + case WPA_4WAY_HANDSHAKE: + return "4WAY_HANDSHAKE"; + case WPA_GROUP_HANDSHAKE: + return "GROUP_HANDSHAKE"; + case WPA_COMPLETED: + return "COMPLETED"; + default: + return "UNKNOWN"; + } +} + + +static int wpa_supplicant_ctrl_iface_set(struct wpa_supplicant *wpa_s, + char *cmd) +{ + char *value; + + value = strchr(cmd, ' '); + if (value == NULL) + return -1; + *value++ = '\0'; + + wpa_printf(MSG_DEBUG, "CTRL_IFACE SET '%s'='%s'", cmd, value); + if (strcasecmp(cmd, "EAPOL::heldPeriod") == 0) { + eapol_sm_configure(wpa_s->eapol, + atoi(value), -1, -1, -1); + } else if (strcasecmp(cmd, "EAPOL::authPeriod") == 0) { + eapol_sm_configure(wpa_s->eapol, + -1, atoi(value), -1, -1); + } else if (strcasecmp(cmd, "EAPOL::startPeriod") == 0) { + eapol_sm_configure(wpa_s->eapol, + -1, -1, atoi(value), -1); + } else if (strcasecmp(cmd, "EAPOL::maxStart") == 0) { + eapol_sm_configure(wpa_s->eapol, + -1, -1, -1, atoi(value)); + } else + return -1; + return 0; +} + + +static int wpa_supplicant_ctrl_iface_preauth(struct wpa_supplicant *wpa_s, + char *addr) +{ + u8 bssid[ETH_ALEN]; + + if (hwaddr_aton(addr, bssid)) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE PREAUTH: invalid address " + "'%s'", addr); + return -1; + } + + wpa_printf(MSG_DEBUG, "CTRL_IFACE PREAUTH " MACSTR, MAC2STR(bssid)); + rsn_preauth_deinit(wpa_s); + if (rsn_preauth_init(wpa_s, bssid)) + return -1; + + return 0; +} + + +static int wpa_supplicant_ctrl_iface_attach(struct wpa_supplicant *wpa_s, + CTRL_IFACE_SOCK *from, + socklen_t fromlen) +{ + struct wpa_ctrl_dst *dst; + + dst = malloc(sizeof(*dst)); + if (dst == NULL) + return -1; + memset(dst, 0, sizeof(*dst)); + memcpy(&dst->addr, from, sizeof(CTRL_IFACE_SOCK)); + dst->addrlen = fromlen; + dst->debug_level = MSG_INFO; + dst->next = wpa_s->ctrl_dst; + wpa_s->ctrl_dst = dst; +#ifdef CONFIG_CTRL_IFACE_UDP + wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor attached %s:%d", + inet_ntoa(from->sin_addr), ntohs(from->sin_port)); +#else /* CONFIG_CTRL_IFACE_UDP */ + wpa_hexdump(MSG_DEBUG, "CTRL_IFACE monitor attached", + (u8 *) from->sun_path, fromlen); +#endif /* CONFIG_CTRL_IFACE_UDP */ + return 0; +} + + +static int wpa_supplicant_ctrl_iface_detach(struct wpa_supplicant *wpa_s, + CTRL_IFACE_SOCK *from, + socklen_t fromlen) +{ + struct wpa_ctrl_dst *dst, *prev = NULL; + + dst = wpa_s->ctrl_dst; + while (dst) { +#ifdef CONFIG_CTRL_IFACE_UDP + if (from->sin_addr.s_addr == dst->addr.sin_addr.s_addr && + from->sin_port == dst->addr.sin_port) { + if (prev == NULL) + wpa_s->ctrl_dst = dst->next; + else + prev->next = dst->next; + free(dst); + wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor detached " + "%s:%d", inet_ntoa(from->sin_addr), + ntohs(from->sin_port)); + return 0; + } +#else /* CONFIG_CTRL_IFACE_UDP */ + if (fromlen == dst->addrlen && + memcmp(from->sun_path, dst->addr.sun_path, fromlen) == 0) { + if (prev == NULL) + wpa_s->ctrl_dst = dst->next; + else + prev->next = dst->next; + free(dst); + wpa_hexdump(MSG_DEBUG, "CTRL_IFACE monitor detached", + (u8 *) from->sun_path, fromlen); + return 0; + } +#endif /* CONFIG_CTRL_IFACE_UDP */ + prev = dst; + dst = dst->next; + } + return -1; +} + + +static int wpa_supplicant_ctrl_iface_level(struct wpa_supplicant *wpa_s, + CTRL_IFACE_SOCK *from, + socklen_t fromlen, + char *level) +{ + struct wpa_ctrl_dst *dst; + + wpa_printf(MSG_DEBUG, "CTRL_IFACE LEVEL %s", level); + + dst = wpa_s->ctrl_dst; + while (dst) { +#ifdef CONFIG_CTRL_IFACE_UDP + if (from->sin_addr.s_addr == dst->addr.sin_addr.s_addr && + from->sin_port == dst->addr.sin_port) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE changed monitor " + "level %s:%d", inet_ntoa(from->sin_addr), + ntohs(from->sin_port)); + dst->debug_level = atoi(level); + return 0; + } +#else /* CONFIG_CTRL_IFACE_UDP */ + if (fromlen == dst->addrlen && + memcmp(from->sun_path, dst->addr.sun_path, fromlen) == 0) { + wpa_hexdump(MSG_DEBUG, "CTRL_IFACE changed monitor " + "level", (u8 *) from->sun_path, fromlen); + dst->debug_level = atoi(level); + return 0; + } +#endif /* CONFIG_CTRL_IFACE_UDP */ + dst = dst->next; + } + + return -1; +} + + +static int wpa_supplicant_ctrl_iface_ctrl_rsp(struct wpa_supplicant *wpa_s, + char *rsp) +{ + char *pos, *id_pos; + int id; + struct wpa_ssid *ssid; + + pos = strchr(rsp, '-'); + if (pos == NULL) + return -1; + *pos++ = '\0'; + id_pos = pos; + pos = strchr(pos, ':'); + if (pos == NULL) + return -1; + *pos++ = '\0'; + id = atoi(id_pos); + wpa_printf(MSG_DEBUG, "CTRL_IFACE: field=%s id=%d value='%s'", + rsp, id, pos); + + ssid = wpa_s->conf->ssid; + while (ssid) { + if (id == ssid->id) + break; + ssid = ssid->next; + } + + if (ssid == NULL) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find SSID id=%d " + "to update", id); + return -1; + } + + if (strcmp(rsp, "IDENTITY") == 0) { + free(ssid->identity); + ssid->identity = (u8 *) strdup(pos); + ssid->identity_len = strlen(pos); + ssid->pending_req_identity = 0; + if (ssid == wpa_s->current_ssid) + wpa_s->reassociate = 1; + } else if (strcmp(rsp, "PASSWORD") == 0) { + free(ssid->password); + ssid->password = (u8 *) strdup(pos); + ssid->password_len = strlen(pos); + ssid->pending_req_password = 0; + if (ssid == wpa_s->current_ssid) + wpa_s->reassociate = 1; + } else if (strcmp(rsp, "OTP") == 0) { + free(ssid->otp); + ssid->otp = (u8 *) strdup(pos); + ssid->otp_len = strlen(pos); + free(ssid->pending_req_otp); + ssid->pending_req_otp = NULL; + ssid->pending_req_otp_len = 0; + } else { + wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown field '%s'", rsp); + return -1; + } + + return 0; +} + + +static int wpa_supplicant_ctrl_iface_status(struct wpa_supplicant *wpa_s, + const char *params, + char *buf, size_t buflen) +{ + char *pos, *end; + int res, verbose; + + verbose = strcmp(params, "-VERBOSE") == 0; + pos = buf; + end = buf + buflen; + pos += snprintf(pos, end - pos, "bssid=" MACSTR "\n", + MAC2STR(wpa_s->bssid)); + if (wpa_s->current_ssid) { + pos += snprintf(pos, end - pos, "ssid=%s\n", + wpa_ssid_txt(wpa_s->current_ssid->ssid, + wpa_s->current_ssid->ssid_len)); + } + pos += snprintf(pos, end - pos, + "pairwise_cipher=%s\n" + "group_cipher=%s\n" + "key_mgmt=%s\n" + "wpa_state=%s\n", + wpa_cipher_txt(wpa_s->pairwise_cipher), + wpa_cipher_txt(wpa_s->group_cipher), + wpa_key_mgmt_txt(wpa_s->key_mgmt, wpa_s->proto), + wpa_state_txt(wpa_s->wpa_state)); + + res = eapol_sm_get_status(wpa_s->eapol, pos, end - pos, verbose); + if (res >= 0) + pos += res; + + if (wpa_s->preauth_eapol) { + pos += snprintf(pos, end - pos, "Pre-authentication " + "EAPOL state machines:\n"); + res = eapol_sm_get_status(wpa_s->preauth_eapol, + pos, end - pos, verbose); + if (res >= 0) + pos += res; + } + + return pos - buf; +} + + +static void wpa_supplicant_ctrl_iface_receive(int sock, void *eloop_ctx, + void *sock_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + char buf[256]; + int res; + CTRL_IFACE_SOCK from; + socklen_t fromlen = sizeof(from); + char *reply; + const int reply_size = 2048; + int reply_len; + int new_attached = 0, ctrl_rsp = 0; + + res = recvfrom(sock, buf, sizeof(buf) - 1, 0, + (struct sockaddr *) &from, &fromlen); + if (res < 0) { + perror("recvfrom(ctrl_iface)"); + return; + } + buf[res] = '\0'; + if (strncmp(buf, "CTRL-RSP-", 9) == 0) { + wpa_hexdump_ascii_key(MSG_DEBUG, "RX ctrl_iface", + (u8 *) buf, res); + } else { + wpa_hexdump_ascii(MSG_DEBUG, "RX ctrl_iface", (u8 *) buf, res); + } + + reply = malloc(reply_size); + if (reply == NULL) { + sendto(sock, "FAIL\n", 5, 0, (struct sockaddr *) &from, + fromlen); + return; + } + + memcpy(reply, "OK\n", 3); + reply_len = 3; + + if (strcmp(buf, "PING") == 0) { + memcpy(reply, "PONG\n", 5); + reply_len = 5; + } else if (strcmp(buf, "MIB") == 0) { + reply_len = wpa_get_mib(wpa_s, reply, reply_size); + if (reply_len >= 0) { + res = eapol_sm_get_mib(wpa_s->eapol, reply + reply_len, + reply_size - reply_len); + if (res < 0) + reply_len = -1; + else + reply_len += res; + } + } else if (strncmp(buf, "STATUS", 6) == 0) { + reply_len = wpa_supplicant_ctrl_iface_status( + wpa_s, buf + 6, reply, reply_size); + } else if (strcmp(buf, "PMKSA") == 0) { + reply_len = pmksa_cache_list(wpa_s, reply, reply_size); + } else if (strncmp(buf, "SET ", 4) == 0) { + if (wpa_supplicant_ctrl_iface_set(wpa_s, buf + 4)) + reply_len = -1; + } else if (strcmp(buf, "LOGON") == 0) { + eapol_sm_notify_logoff(wpa_s->eapol, FALSE); + } else if (strcmp(buf, "LOGOFF") == 0) { + eapol_sm_notify_logoff(wpa_s->eapol, TRUE); + } else if (strcmp(buf, "REASSOCIATE") == 0) { + wpa_s->reassociate = 1; + wpa_supplicant_req_scan(wpa_s, 0, 0); + } else if (strncmp(buf, "PREAUTH ", 8) == 0) { + if (wpa_supplicant_ctrl_iface_preauth(wpa_s, buf + 8)) + reply_len = -1; + } else if (strcmp(buf, "ATTACH") == 0) { + if (wpa_supplicant_ctrl_iface_attach(wpa_s, &from, fromlen)) + reply_len = -1; + else + new_attached = 1; + } else if (strcmp(buf, "DETACH") == 0) { + if (wpa_supplicant_ctrl_iface_detach(wpa_s, &from, fromlen)) + reply_len = -1; + } else if (strncmp(buf, "LEVEL ", 6) == 0) { + if (wpa_supplicant_ctrl_iface_level(wpa_s, &from, fromlen, + buf + 6)) + reply_len = -1; + } else if (strncmp(buf, "CTRL-RSP-", 9) == 0) { + if (wpa_supplicant_ctrl_iface_ctrl_rsp(wpa_s, buf + 9)) + reply_len = -1; + else + ctrl_rsp = 1; + } else if (strcmp(buf, "RECONFIGURE") == 0) { + if (wpa_supplicant_reload_configuration(wpa_s)) + reply_len = -1; + } else if (strcmp(buf, "TERMINATE") == 0) { + eloop_terminate(); + } else { + memcpy(reply, "UNKNOWN COMMAND\n", 16); + reply_len = 16; + } + + if (reply_len < 0) { + memcpy(reply, "FAIL\n", 5); + reply_len = 5; + } + sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from, fromlen); + free(reply); + + if (new_attached) + eapol_sm_notify_ctrl_attached(wpa_s->eapol); + if (ctrl_rsp) + eapol_sm_notify_ctrl_response(wpa_s->eapol); +} + + +static char * wpa_supplicant_ctrl_iface_path(struct wpa_supplicant *wpa_s) +{ + char *buf; + size_t len; + + if (wpa_s->conf->ctrl_interface == NULL) + return NULL; + + len = strlen(wpa_s->conf->ctrl_interface) + strlen(wpa_s->ifname) + 2; + buf = malloc(len); + if (buf == NULL) + return NULL; + + snprintf(buf, len, "%s/%s", + wpa_s->conf->ctrl_interface, wpa_s->ifname); +#ifdef __CYGWIN__ + { + /* Windows/WinPcap uses interface names that are not suitable + * as a file name - convert invalid chars to underscores */ + char *pos = buf; + while (*pos) { + if (*pos == '\\') + *pos = '_'; + pos++; + } + } +#endif /* __CYGWIN__ */ + return buf; +} + + +int wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s) +{ + CTRL_IFACE_SOCK addr; + int s = -1; + char *fname = NULL; + + wpa_s->ctrl_sock = -1; + + if (wpa_s->conf->ctrl_interface == NULL) + return 0; + +#ifdef CONFIG_CTRL_IFACE_UDP + s = socket(PF_INET, SOCK_DGRAM, 0); + if (s < 0) { + perror("socket(PF_INET)"); + goto fail; + } + + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = htonl((127 << 24) | 1); + addr.sin_port = htons(9877); + if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + perror("bind(AF_UNIX)"); + goto fail; + } +#else /* CONFIG_CTRL_IFACE_UDP */ + if (mkdir(wpa_s->conf->ctrl_interface, S_IRWXU | S_IRWXG) < 0) { + if (errno == EEXIST) { + wpa_printf(MSG_DEBUG, "Using existing control " + "interface directory."); + } else { + perror("mkdir[ctrl_interface]"); + goto fail; + } + } + + if (wpa_s->conf->ctrl_interface_gid_set && + chown(wpa_s->conf->ctrl_interface, 0, + wpa_s->conf->ctrl_interface_gid) < 0) { + perror("chown[ctrl_interface]"); + return -1; + } + + if (strlen(wpa_s->conf->ctrl_interface) + 1 + strlen(wpa_s->ifname) >= + sizeof(addr.sun_path)) + goto fail; + + s = socket(PF_UNIX, SOCK_DGRAM, 0); + if (s < 0) { + perror("socket(PF_UNIX)"); + goto fail; + } + + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + fname = wpa_supplicant_ctrl_iface_path(wpa_s); + if (fname == NULL) + goto fail; + strncpy(addr.sun_path, fname, sizeof(addr.sun_path)); + if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + perror("bind(PF_UNIX)"); + if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + wpa_printf(MSG_DEBUG, "ctrl_iface exists, but does not" + " allow connections - assuming it was left" + "over from forced program termination"); + if (unlink(fname) < 0) { + perror("unlink[ctrl_iface]"); + wpa_printf(MSG_ERROR, "Could not unlink " + "existing ctrl_iface socket '%s'", + fname); + goto fail; + } + if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < + 0) { + perror("bind(PF_UNIX)"); + goto fail; + } + wpa_printf(MSG_DEBUG, "Successfully replaced leftover " + "ctrl_iface socket '%s'", fname); + } else { + wpa_printf(MSG_INFO, "ctrl_iface exists and seems to " + "be in use - cannot override it"); + wpa_printf(MSG_INFO, "Delete '%s' manually if it is " + "not used anymore", fname); + free(fname); + fname = NULL; + goto fail; + } + } + + if (wpa_s->conf->ctrl_interface_gid_set && + chown(fname, 0, wpa_s->conf->ctrl_interface_gid) < 0) { + perror("chown[ctrl_interface/ifname]"); + goto fail; + } + + if (chmod(fname, S_IRWXU | S_IRWXG) < 0) { + perror("chmod[ctrl_interface/ifname]"); + goto fail; + } + free(fname); +#endif /* CONFIG_CTRL_IFACE_UDP */ + + wpa_s->ctrl_sock = s; + eloop_register_read_sock(s, wpa_supplicant_ctrl_iface_receive, wpa_s, + NULL); + + return 0; + +fail: + if (s >= 0) + close(s); + if (fname) { + unlink(fname); + free(fname); + } + return -1; +} + + +void wpa_supplicant_ctrl_iface_deinit(struct wpa_supplicant *wpa_s) +{ + struct wpa_ctrl_dst *dst, *prev; + + if (wpa_s->ctrl_sock > -1) { + char *fname; + eloop_unregister_read_sock(wpa_s->ctrl_sock); + close(wpa_s->ctrl_sock); + wpa_s->ctrl_sock = -1; + fname = wpa_supplicant_ctrl_iface_path(wpa_s); + if (fname) + unlink(fname); + free(fname); + + if (rmdir(wpa_s->conf->ctrl_interface) < 0) { + if (errno == ENOTEMPTY) { + wpa_printf(MSG_DEBUG, "Control interface " + "directory not empty - leaving it " + "behind"); + } else { + perror("rmdir[ctrl_interface]"); + } + } + } + + dst = wpa_s->ctrl_dst; + while (dst) { + prev = dst; + dst = dst->next; + free(prev); + } +} + + +void wpa_supplicant_ctrl_iface_send(struct wpa_supplicant *wpa_s, int level, + char *buf, size_t len) +{ + struct wpa_ctrl_dst *dst, *next; + char levelstr[10]; + int idx; +#ifdef CONFIG_CTRL_IFACE_UDP + char *sbuf; + int llen; + + dst = wpa_s->ctrl_dst; + if (wpa_s->ctrl_sock < 0 || dst == NULL) + return; + + snprintf(levelstr, sizeof(levelstr), "<%d>", level); + + llen = strlen(levelstr); + sbuf = malloc(llen + len); + if (sbuf == NULL) + return; + + memcpy(sbuf, levelstr, llen); + memcpy(sbuf + llen, buf, len); + + idx = 0; + while (dst) { + next = dst->next; + if (level >= dst->debug_level) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor send %s:%d", + inet_ntoa(dst->addr.sin_addr), + ntohs(dst->addr.sin_port)); + if (sendto(wpa_s->ctrl_sock, sbuf, llen + len, 0, + (struct sockaddr *) &dst->addr, + sizeof(dst->addr)) < 0) { + fprintf(stderr, "CTRL_IFACE monitor[%d]: ", + idx); + perror("sendto"); + dst->errors++; + if (dst->errors > 10) { + wpa_supplicant_ctrl_iface_detach( + wpa_s, &dst->addr, + dst->addrlen); + } + } else + dst->errors = 0; + } + idx++; + dst = next; + } + free(sbuf); +#else /* CONFIG_CTRL_IFACE_UDP */ + struct msghdr msg; + struct iovec io[2]; + + dst = wpa_s->ctrl_dst; + if (wpa_s->ctrl_sock < 0 || dst == NULL) + return; + + snprintf(levelstr, sizeof(levelstr), "<%d>", level); + io[0].iov_base = levelstr; + io[0].iov_len = strlen(levelstr); + io[1].iov_base = buf; + io[1].iov_len = len; + memset(&msg, 0, sizeof(msg)); + msg.msg_iov = io; + msg.msg_iovlen = 2; + + idx = 0; + while (dst) { + next = dst->next; + if (level >= dst->debug_level) { + wpa_hexdump(MSG_DEBUG, "CTRL_IFACE monitor send", + (u8 *) dst->addr.sun_path, dst->addrlen); + msg.msg_name = &dst->addr; + msg.msg_namelen = dst->addrlen; + if (sendmsg(wpa_s->ctrl_sock, &msg, 0) < 0) { + fprintf(stderr, "CTRL_IFACE monitor[%d]: ", + idx); + perror("sendmsg"); + dst->errors++; + if (dst->errors > 10) { + wpa_supplicant_ctrl_iface_detach( + wpa_s, &dst->addr, + dst->addrlen); + } + } else + dst->errors = 0; + } + idx++; + dst = next; + } +#endif /* CONFIG_CTRL_IFACE_UDP */ +} diff --git a/contrib/wpa_supplicant/ctrl_iface.h b/contrib/wpa_supplicant/ctrl_iface.h new file mode 100644 index 0000000..d7ad6df --- /dev/null +++ b/contrib/wpa_supplicant/ctrl_iface.h @@ -0,0 +1,31 @@ +#ifndef CTRL_IFACE_H +#define CTRL_IFACE_H + +#ifdef CONFIG_CTRL_IFACE + +int wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s); +void wpa_supplicant_ctrl_iface_deinit(struct wpa_supplicant *wpa_s); +void wpa_supplicant_ctrl_iface_send(struct wpa_supplicant *wpa_s, int level, + char *buf, size_t len); + +#else /* CONFIG_CTRL_IFACE */ + +static inline int wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s) +{ + return 0; +} + +static inline void +wpa_supplicant_ctrl_iface_deinit(struct wpa_supplicant *wpa_s) +{ +} + +static inline void +wpa_supplicant_ctrl_iface_send(struct wpa_supplicant *wpa_s, int level, + char *buf, size_t len) +{ +} + +#endif /* CONFIG_CTRL_IFACE */ + +#endif /* CTRL_IFACE_H */ diff --git a/contrib/wpa_supplicant/defconfig b/contrib/wpa_supplicant/defconfig new file mode 100644 index 0000000..7e8e381 --- /dev/null +++ b/contrib/wpa_supplicant/defconfig @@ -0,0 +1,154 @@ +# Example wpa_supplicant build time configuration +# +# This file lists the configuration options that are used when building the +# hostapd binary. All lines starting with # are ignored. Configuration option +# lines must be commented out complete, if they are not to be included, i.e., +# just setting VARIABLE=n is not disabling that variable. +# +# This file is included in Makefile, so variables like CFLAGS and LIBS can also +# be modified from here. In most cass, these lines should use += in order not +# to override previous values of the variables. + + +# Uncomment following two lines and fix the paths if you have installed openssl +# in non-default location +#CFLAGS += -I/usr/local/openssl/include +#LIBS += -L/usr/local/openssl/lib + +# Example configuration for various cross-compilation platforms + +#### sveasoft (e.g., for Linksys WRT54G) ###################################### +#CC=mipsel-uclibc-gcc +#CC=/opt/brcm/hndtools-mipsel-uclibc/bin/mipsel-uclibc-gcc +#CFLAGS += -Os +#CPPFLAGS += -I../src/include -I../../src/router/openssl/include +#LIBS += -L/opt/brcm/hndtools-mipsel-uclibc-0.9.19/lib -lssl +############################################################################### + +#### openwrt (e.g., for Linksys WRT54G) ####################################### +#CC=mipsel-uclibc-gcc +#CC=/opt/brcm/hndtools-mipsel-uclibc/bin/mipsel-uclibc-gcc +#CFLAGS += -Os +#CPPFLAGS=-I../src/include -I../openssl-0.9.7d/include \ +# -I../WRT54GS/release/src/include +#LIBS = -lssl +############################################################################### + + +# Driver interface for Host AP driver +CONFIG_DRIVER_HOSTAP=y + +# Driver interface for Agere driver +#CONFIG_DRIVER_HERMES=y +# Change include directories to match with the local setup +#CFLAGS += -I../../hcf -I../../include -I../../include/hcf +#CFLAGS += -I../../include/wireless + +# Driver interface for madwifi driver +#CONFIG_DRIVER_MADWIFI=y +# Change include directories to match with the local setup +#CFLAGS += -I../madwifi/wpa + +# Driver interface for Prism54 driver +CONFIG_DRIVER_PRISM54=y + +# Driver interface for ndiswrapper +#CONFIG_DRIVER_NDISWRAPPER=y + +# Driver interface for Atmel driver +CONFIG_DRIVER_ATMEL=y + +# Driver interface for Broadcom driver +#CONFIG_DRIVER_BROADCOM=y +# Example path for wlioctl.h; change to match your configuration +#CFLAGS += -I/opt/WRT54GS/release/src/include + +# Driver interface for Intel ipw2100/2200 driver +#CONFIG_DRIVER_IPW=y + +# Driver interface for generic Linux wireless extensions +CONFIG_DRIVER_WEXT=y + +# Driver interface for FreeBSD net80211 layer (e.g., Atheros driver) +#CONFIG_DRIVER_BSD=y +#CFLAGS += -I/usr/local/include +#LIBS += -L/usr/local/lib + +# Driver interface for Windows NDIS +#CONFIG_DRIVER_NDIS=y +#CFLAGS += -I/usr/include/w32api/ddk +#LIBS += -L/usr/local/lib +# For native build using mingw +#CONFIG_NATIVE_WINDOWS=y +# Additional directories for cross-compilation on Linux host for mingw target +#CFLAGS += -I/opt/mingw/mingw32/include/ddk +#LIBS += -L/opt/mingw/mingw32/lib +#CC=mingw32-gcc + +# Driver interface for development testing +#CONFIG_DRIVER_TEST=y + +# Enable IEEE 802.1X Supplicant (automatically included if any EAP method is +# included) +CONFIG_IEEE8021X_EAPOL=y + +# EAP-MD5 (automatically included if EAP-TTLS is enabled) +CONFIG_EAP_MD5=y + +# EAP-MSCHAPv2 (automatically included if EAP-PEAP is enabled) +CONFIG_EAP_MSCHAPV2=y + +# EAP-TLS +CONFIG_EAP_TLS=y + +# EAL-PEAP +CONFIG_EAP_PEAP=y + +# EAP-TTLS +CONFIG_EAP_TTLS=y + +# EAP-GTC +CONFIG_EAP_GTC=y + +# EAP-OTP +CONFIG_EAP_OTP=y + +# EAP-SIM (enable CONFIG_PCSC, if EAP-SIM is used) +#CONFIG_EAP_SIM=y + +# EAP-PSK (experimental; this is _not_ needed for WPA-PSK) +#CONFIG_EAP_PSK=y + +# LEAP +CONFIG_EAP_LEAP=y + +# EAP-AKA (enable CONFIG_PCSC, if EAP-AKA is used) +#CONFIG_EAP_AKA=y + +# PKCS#12 (PFX) support (used to read private key and certificate file from +# a file that usually has extension .p12 or .pfx) +CONFIG_PKCS12=y + +# PC/SC interface for smartcards (USIM, GSM SIM) +# Enable this if EAP-SIM or EAP-AKA is included +#CONFIG_PCSC=y + +# Development testing +#CONFIG_EAPOL_TEST=y + +# Replace native Linux implementation of packet sockets with libdnet/libpcap. +# This will be automatically set for non-Linux OS. +#CONFIG_DNET_PCAP=y + +# Include control interface for external programs, e.g, wpa_cli +CONFIG_CTRL_IFACE=y + +# Include interface for using external supplicant (Xsupplicant) for EAP +# authentication +#CONFIG_XSUPPLICANT_IFACE=y + +# Include support for GNU Readline and History Libraries in wpa_cli. +# When building a wpa_cli binary for distribution, please note that these +# libraries are licensed under GPL and as such, BSD license may not apply for +# the resulting binary. +#CONFIG_READLINE=y diff --git a/contrib/wpa_supplicant/defs.h b/contrib/wpa_supplicant/defs.h new file mode 100644 index 0000000..a5a515c --- /dev/null +++ b/contrib/wpa_supplicant/defs.h @@ -0,0 +1,14 @@ +#ifndef DEFS_H +#define DEFS_H + +#ifdef CONFIG_NATIVE_WINDOWS +#ifdef FALSE +#undef FALSE +#endif +#ifdef TRUE +#undef TRUE +#endif +#endif /* CONFIG_NATIVE_WINDOWS */ +typedef enum { FALSE = 0, TRUE = 1 } Boolean; + +#endif /* DEFS_H */ diff --git a/contrib/wpa_supplicant/developer.txt b/contrib/wpa_supplicant/developer.txt new file mode 100644 index 0000000..bc5b346 --- /dev/null +++ b/contrib/wpa_supplicant/developer.txt @@ -0,0 +1,458 @@ +Developer notes for wpa_supplicant +================================== + +The design goal for wpa_supplicant was to use hardware, driver, and OS +independent, portable C code for all WPA functionality. All +hardware/driver specific functionality is in separate files that +implement a well-defined driver API. + +The goal of this file and the comments in the header files is to give +enough information for other developers to be able to port the example +code. If any information is missing, feel free to contact Jouni Malinen +<jkmaline@cc.hut.fi> for more information. Contributions as patch files +are also very welcome at the same address. + +Structure of the source code +---------------------------- + +Program initialization, main control loop and event handling is +implemented in wpa_supplicant.c. WPA state machines and 4-Way/Group +Key Handshake processing in in wpa.c. IEEE 802.1X/EAPOL processing and +state machines are in eapol_sm.c. EAP state machine is in eap.c. EAP +methods for the internal EAP peer are in eap_*.c. Parser for the +configuration file is implemented in config.c. + +Driver interface API is defined in driver.h and all hardware/driver +dependent functionality is implemented in driver_*.c (see below). + + +Generic helper functions +------------------------ + +wpa_supplicant uses generic helper functions some of which are shared +with with hostapd. The following C files are currently used: + +eloop.[ch] + event loop (select() loop with registerable timeouts, socket read + callbacks, and signal callbacks) + +common.[ch] + common helper functions + +defs.h + definitions shared by multiple files + +l2_packet.[ch] + Layer 2 (link) access wrapper (includes native Linux implementation + and wrappers for libdnet/libpcap) + +pcsc_funcs.[ch] + Wrapper for PC/SC lite SIM and smart card readers + + +Cryptographic functions +----------------------- + +md5.c + MD5 (replaced with openssl/crypto if TLS support is included) + HMAC-MD5 (keyed checksum for message authenticity validation) + +rc4.c + RC4 (broadcast/default key encryption) + +sha1.c + SHA-1 (replaced with openssl/crypto if TLS support is included) + HMAC-SHA-1 (keyed checksum for message authenticity validation) + PRF-SHA-1 (pseudorandom (key/nonce generation) function) + PBKDF2-SHA-1 (ASCII passphrase to shared secret) + T-PRF (for EAP-FAST) + TLS-PRF (RFC 2246) + +aes_wrap.[ch], aes.c + AES + AES Key Wrap Algorithm with 128-bit KEK, RFC3394 (broadcast/default + key encryption) + One-Key CBC MAC (OMAC1) hash with AES-128 + AES-128 CTR mode encryption + AES-128 EAX mode encryption/decryption + AES-128 CBC + +crypto.[ch] + Wrapper functions for libcrypto (MD4 and DES) + +ms_funcs.[ch] + Helper functions for MSCHAPV2 and LEAP + +tls.h + Definition of TLS library wrapper + +tls_none.c + Dummy implementation of TLS library wrapper for cases where TLS + functionality is not included. + +tls_openssl.c + TLS library wrapper for openssl + + +Configuration +------------- + +config_ssid.h + Definition of per network configuration items + +config.h + Definition of the wpa_supplicant configuration + +config.c + Configuration file parser + + +Control interface +----------------- + +wpa_supplicant has a control interface that can be used to get status +information and manage operations from external programs. An example, +command line interface, wpa_cli, for this interface is included in the +wpa_supplicant distribution. + +ctrl_iface.[ch] + wpa_supplicant-side of the control interface + +wpa_ctrl.[ch] + Library functions for external programs to provide access to the + wpa_supplicant control interface + +wpa_cli.c + Example program for using wpa_supplicant control interface + + +EAP peer +-------- + +eap.[ch] + EAP state machine + +eap_defs.h + Common EAP definitions + +eap_i.h + Internal definitions for EAP state machine and EAP methods + +eap_sim_common.[ch] + Common code for EAP-SIM and EAP-AKA + +eap_tls_common.[ch] + Common code for EAP-PEAP, EAP-TTLS, and EAP-FAST + +eap_tlv.[ch] + EAP-TLV code for EAP-PEAP and EAP-FAST + +eap_{aka,fast,gtc,leap,md5,mschapv2,otp,peap,psk,sim,tls,ttls}.c + EAP method implementations + + +EAPOL supplicant +---------------- + +eapol_sm.[ch] + EAPOL supplicant state machine and IEEE 802.1X processing + + +Windows port +------------ + +ndis_events.cpp + External program for receiving NdisMIndicateStatus() events and + delivering them to wpa_supplicant in more easier to use form + +win_if_list.c + External program for listing current network interface + + +Test programs +------------- + +radius_client.[ch] + RADIUS authentication client implementation for eapol_test + +eapol_test.c + Standalone EAP testing tool with integrated RADIUS authentication + client + +preauth_test.c + Standalone RSN pre-authentication tool + + +wpa_supplicant.c +---------------- + +main() +- parse command line +- call config file parser +- initialize Supplicant data structures +- call functions to initialize WPA support in the driver +- initialize event loop +- cleanup when exiting + +wpa_supplicant_dot1x_receive() +- receive master session key update from Xsupplicant (optional) + +wpa_supplicant_get_beacon_ie() + +wpa_supplicant_deauthenticate() + +wpa_supplicant_disassociate() + +wpa_supplicant_scan() + +wpa_supplicant_reconfig() +- SIGHUP signal processing + +wpa_supplicant_terminate() +- SIGINT and SIGTERM processing + +wpa_supplicant_reload_configuration() + +wpa_supplicant_event() +- receive driver events (through driver wrapper functions) + * wpa_supplicant_scan_results(): process scan result event, BSS selection + * wpa_supplicant_associnfo(): process association information event + +wpa_supplicant_associate() +- control association (select cipher and key management suites, initiate + association) + +wpa_supplicant_req_auth_timeout() + +wpa_supplicant_cancel_auth_timeout() + +wpa_supplicant_req_scan() + +wpa_supplicant_cancel_scan() + +wpa_supplicant_notify_eapol_done() + +wpa_eapol_send() +- send EAPOL frames + +wpa_eapol_send_preauth() +- send RSN preauthentication frames + +wpa_msg() +- event/debug function + + +wpa_supplicant.h +---------------- + +- driver event definition +- common function definition (e.g., wpa_msg) + + +wpa_supplicant_i.h +------------------ +- internal definitions for wpa_supplicant; must not be included into + common code, EAP methods, driver interface implementations + + +wpa.[ch] +-------- +- WPA supplicant state machine and 4-Way/Group Key Handshake processing +- PMKSA cache and RSN pre-authentication + +pmksa_cache_free() + +pmksa_cache_get() + +pmksa_cache_list() + +pmksa_candidate_free() + +wpa_parse_wpa_ie() +- WPA/RSN IE parsing + +wpa_gen_wpa_ei() +- WPA/RSN IE generation + +wpa_supplicant_get_ssid() + +wpa_supplicant_key_request() +- trigger function to start key requests + +wpa_sm_rx_eapol() +- WPA processing for received EAPOL-Key frames + * wpa_supplicant_process_1_of_4() (message 1 of 4-Way Handshake) + * wpa_supplicant_process_3_of_4() (message 3 of 4-Way Handshake) + * wpa_supplicant_process_1_of_2() (message 1 of Group Key Handshake) + +wpa_supplicant_rx_eapol() +- l2_packet RX callback for EAPOL frames; sends the frames to WPA and EAPOL + state machines for further processing + +wpa_get_mib() + +rsn_preauth_receive() +- l2_packet RX callback for preauthentication frames + +rsn_preauth_eapol_cb() +- callback function to be called when EAPOL authentication has been completed + (either successfully or unsuccessfully) for RSN pre-authentication + +rsn_preauth_init() +rsn_preauth_deinit() + +pmksa_candidate_add() +- add a BSSID to PMKSA candidate list + +rsn_preauth_scan_results() +- update RSN pre-authentication candidate list based on scan results + + +Driver wrapper implementation (driver.h, drivers.c) +--------------------------------------------------- + +All hardware and driver dependent functionality is implemented in as a +separate C file(s) implementing defined wrapper functions. Other parts +of the wpa_supplicant are designed to be hardware, driver, and operating +system independent. + +Driver wrappers need to implement whatever calls are used in the +target operating system/driver for controlling wireless LAN +devices. As an example, in case of Linux, these are mostly some glue +code and ioctl() calls and netlink message parsing for Linux Wireless +Extensions. Since all features required for WPA are not yet included +in Wireless Extensions, some driver specific code is used in the +example implementation for Host AP driver. These driver dependent parts +are to be replaced with generic code once the needed changes are +included in the Wireless Extensions. After that, all Linux drivers, at +least in theory, could use the same driver wrapper code. + +A driver wrapper needs to implement some or all of the functions +defined in driver.h (see that file for detailed documentation of the +functions). Hardware independent parts of wpa_supplicant will call +these functions to control the driver/wlan card. In addition, support +for driver events is required. The event callback function, +wpa_supplicant_event(), and its parameters are documented in +wpa_supplicant.h. In addition, pointer to the 'struct wpa_driver_ops' +needs to be registered in drivers.c file. + +When porting to other operating systems, driver wrapper should be +modified to use the native interface of the target OS. It is possible +that some extra requirements for the interface between the driver +wrapper and generic wpa_supplicant code are discovered during porting +to a new operating system. These will be addresses on case by case +basic by modifying the interface and updating the other driver +wrappers for this. The goal is to avoid changing this interface +without very good reasons in order to limit the number of changes +needed to other wrappers and hardware independent parts of +wpa_supplicant. + +Generic Linux Wireless Extensions functions are implemented in +driver_wext.c. All Linux driver wrappers can use these when the kernel +driver supports the generic ioctl()s and wireless events. Driver +specific functions are implemented in separate C files, e.g., +driver_hostap.c. These files need to define struct wpa_driver_ops +entry that will be used in wpa_supplicant.c when calling driver +functions. These entries need to be added to the lists in +wpa_supplicant_set_driver() and usage() functions in wpa_supplicant.c. + +In general, it is likely to be useful to first take a look at couple +of the driver interfaces before starting on implementing a new +one. driver_hostap.c and driver_wext.c include a complete +implementation for Linux drivers that use wpa_supplicant-based control +of WPA IE and roaming. driver_ndis.c (with help from driver_ndis_.c) +is an example of a complete interface for Windows NDIS interface for +drivers that generate WPA IE themselves and decide when to roam. These +example implementations include full support for all security modes. + + +Driver requirements for WPA +--------------------------- + +WPA introduces new requirements for the device driver. At least some +of these need to be implemented in order to provide enough support for +wpa_supplicant. + +TKIP/CCMP + +WPA requires that the pairwise cipher suite (encryption algorithm for +unicast data packets) is TKIP or CCMP. These are new encryption +protocols and thus, the driver will need to be modified to support +them. Depending on the used wlan hardware, some parts of these may be +implemented by the hardware/firmware. + +Specification for both TKIP and CCMP is available from IEEE (IEEE +802.11i draft version 3.0). Fully functional, hardware independent +implementation of both encryption protocols is also available in Host +AP driver (driver/modules/hostap_{tkip,ccmp}.c). + +The driver will also need to provide configuration mechanism to allow +user space programs to configure TKIP and CCMP. Current Linux Wireless +Extensions (v16) does not yet support these algorithms or +individual/non-default keys. Host AP driver has an example of private +ioctl()s for this. Eventually, this should be replaced with modified +Linux Wireless Extensions. + +Roaming control and scanning support + +wpa_supplicant controls AP selections based on the information +received from Beacon and/or Probe Response frames. This means that the +driver should support external control for scan process. In case of +Linux, use of new Wireless Extensions scan support (i.e., 'iwlist +wlan0 scan') is recommended. The current driver wrapper (driver_wext.c) +uses this for scan results. + +Scan results must also include WPA information element. This is not +yet defined in Linux Wireless Extensions and Host AP driver uses a +custom event to provide the full WPA IE (including element id and +length) as a hex string that is included in the scan results. +Eventually, this should be defined as a Wireless Extensions ioctl +that can be used both with scan results and with configuration of WPA IE +for association request (and Beacon/Probe Response in case of an +AP/IBSS). + +wpa_supplicant needs to also be able to request the driver to +associate with a specific BSS. Current Host AP driver and matching +driver_hostap.c wrapper uses following sequence for this +request. Similar/identical mechanism should be usable also with other +drivers. + +- set WPA IE for AssocReq with private ioctl +- set SSID with SIOCSIWESSID +- set channel/frequency with SIOCSIWFREQ +- set BSSID with SIOCSIWAP + (this last ioctl will trigger the driver to request association) + +WPA IE generation + +wpa_supplicant selects which cipher suites and key management suites +are used. Based on this information, it generates a WPA IE. This is +provided to the driver interface in the associate call. This does not +match with Windows NDIS drivers which generate the WPA IE +themselves. + +wpa_supplicant allows Windows NDIS-like behavior by providing the +selected cipher and key management suites in the associate call. If +the driver generates its own WPA IE and that differs from the one +generated by wpa_supplicant, the driver has to inform wpa_supplicant +about the used WPA IE (i.e., the one it used in (Re)Associate +Request). This notification is done using EVENT_ASSOCINFO event (see +wpa_supplicant.h). + +Driver events + +wpa_supplicant needs to receive event callbacks when certain events +occur (association, disassociation, Michael MIC failure, scan results +available, PMKSA caching candidate). These events and the callback +details are defined in wpa_supplicant.h. + +On Linux, association and disassociation can use existing Wireless +Extensions event that is reporting new AP with SIOCGIWAP +event. Similarly, completion of scan can be reported with SIOCGIWSCAN +event. + +Michael MIC failure event is not yet included in Wireless Extensions, +so this needs a custom event. Host AP driver uses custom event with +following contents: MLME-MICHAELMICFAILURE.indication(keyid=# +broadcast/unicast addr=addr2). This is the recommended format until +the event is added to Linux Wireless Extensions. diff --git a/contrib/wpa_supplicant/doc/wpa_supplicant.fig b/contrib/wpa_supplicant/doc/wpa_supplicant.fig new file mode 100644 index 0000000..dc1d9df --- /dev/null +++ b/contrib/wpa_supplicant/doc/wpa_supplicant.fig @@ -0,0 +1,221 @@ +#FIG 3.2 +Landscape +Center +Inches +Letter +100.00 +Single +-2 +1200 2 +6 1875 4050 2925 4350 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 1875 4050 2925 4050 2925 4350 1875 4350 1875 4050 +4 0 0 50 -1 0 12 0.0000 4 180 735 2025 4275 l2_packet\001 +-6 +6 3450 1200 4275 1500 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 3450 1200 4275 1200 4275 1500 3450 1500 3450 1200 +4 0 0 50 -1 0 12 0.0000 4 180 585 3600 1425 wpa_cli\001 +-6 +6 4725 1200 5925 1500 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 4725 1200 5925 1200 5925 1500 4725 1500 4725 1200 +4 0 0 50 -1 0 12 0.0000 4 135 1005 4800 1425 GUI frontend\001 +-6 +6 6000 2700 7200 3225 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 6000 2700 7200 2700 7200 3225 6000 3225 6000 2700 +4 0 0 50 -1 0 12 0.0000 4 135 975 6075 2925 WPA/WPA2\001 +4 0 0 50 -1 0 12 0.0000 4 135 1065 6075 3150 state machine\001 +-6 +6 6000 4950 7200 5475 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 6000 4950 7200 4950 7200 5475 6000 5475 6000 4950 +4 0 0 50 -1 0 12 0.0000 4 135 360 6075 5175 EAP\001 +4 0 0 50 -1 0 12 0.0000 4 135 1065 6075 5400 state machine\001 +-6 +6 8700 3000 9375 3300 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 8700 3000 9375 3000 9375 3300 8700 3300 8700 3000 +4 0 0 50 -1 0 12 0.0000 4 150 480 8775 3225 crypto\001 +-6 +6 4350 3900 5025 4425 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 4350 3900 5025 3900 5025 4425 4350 4425 4350 3900 +4 0 0 50 -1 0 12 0.0000 4 105 420 4500 4125 event\001 +4 0 0 50 -1 0 12 0.0000 4 180 315 4500 4350 loop\001 +-6 +6 4275 2550 5100 2850 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 4275 2550 5100 2550 5100 2850 4275 2850 4275 2550 +4 0 0 50 -1 0 12 0.0000 4 135 450 4425 2775 ctrl i/f\001 +-6 +6 6000 3900 7200 4425 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 6000 3900 7200 3900 7200 4425 6000 4425 6000 3900 +4 0 0 50 -1 0 12 0.0000 4 135 600 6075 4125 EAPOL\001 +4 0 0 50 -1 0 12 0.0000 4 135 1065 6075 4350 state machine\001 +-6 +6 1800 6000 7800 8100 +6 1800 6000 7800 7200 +6 1800 6900 2700 7200 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 1800 6900 2700 6900 2700 7200 1800 7200 1800 6900 +4 0 0 50 -1 0 12 0.0000 4 105 375 1875 7125 wext\001 +-6 +6 4725 6900 5625 7200 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 4725 6900 5625 6900 5625 7200 4725 7200 4725 6900 +4 0 0 50 -1 0 12 0.0000 4 135 555 4800 7125 hermes\001 +-6 +6 6675 6900 7800 7200 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 6675 6900 7800 6900 7800 7200 6675 7200 6675 6900 +4 0 0 50 -1 0 12 0.0000 4 180 930 6750 7125 ndiswrapper\001 +-6 +6 5700 6900 6600 7200 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 5700 6900 6600 6900 6600 7200 5700 7200 5700 6900 +4 0 0 50 -1 0 12 0.0000 4 135 420 5775 7125 atmel\001 +-6 +6 4275 6000 5100 6300 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 4275 6000 5100 6000 5100 6300 4275 6300 4275 6000 +4 0 0 50 -1 0 12 0.0000 4 135 630 4350 6225 driver i/f\001 +-6 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 2775 6900 3675 6900 3675 7200 2775 7200 2775 6900 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 3750 6900 4650 6900 4650 7200 3750 7200 3750 6900 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 4 + 2250 6900 2250 6600 7200 6600 7200 6900 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 3225 6900 3225 6600 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 4200 6900 4200 6600 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 5175 6900 5175 6600 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 6150 6900 6150 6600 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 4650 6600 4650 6300 +4 0 0 50 -1 0 12 0.0000 4 180 510 2850 7125 hostap\001 +4 0 0 50 -1 0 12 0.0000 4 135 600 3825 7125 madwifi\001 +-6 +6 3525 7800 5775 8100 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 3525 7800 5775 7800 5775 8100 3525 8100 3525 7800 +4 0 0 50 -1 0 12 0.0000 4 135 2145 3600 8025 kernel network device driver\001 +-6 +2 1 1 1 0 7 50 -1 -1 4.000 0 0 -1 0 0 2 + 2250 7200 4200 7800 +2 1 1 1 0 7 50 -1 -1 4.000 0 0 -1 0 0 2 + 7200 7200 5100 7800 +-6 +6 9600 3000 10275 3300 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 9600 3000 10275 3000 10275 3300 9600 3300 9600 3000 +4 0 0 50 -1 0 12 0.0000 4 135 315 9750 3225 TLS\001 +-6 +6 8100 4425 10425 6975 +6 8175 4725 9225 5025 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 8175 4725 9225 4725 9225 5025 8175 5025 8175 4725 +4 0 0 50 -1 0 12 0.0000 4 135 735 8250 4950 EAP-TLS\001 +-6 +6 9300 4725 10350 5025 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 9300 4725 10350 4725 10350 5025 9300 5025 9300 4725 +4 0 0 50 -1 0 12 0.0000 4 135 810 9375 4950 EAP-MD5\001 +-6 +6 8175 5100 9225 5400 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 8175 5100 9225 5100 9225 5400 8175 5400 8175 5100 +4 0 0 50 -1 0 12 0.0000 4 135 885 8250 5325 EAP-PEAP\001 +-6 +6 9300 5100 10350 5400 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 9300 5100 10350 5100 10350 5400 9300 5400 9300 5100 +4 0 0 50 -1 0 12 0.0000 4 135 840 9375 5325 EAP-TTLS\001 +-6 +6 8175 5475 9225 5775 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 8175 5475 9225 5475 9225 5775 8175 5775 8175 5475 +4 0 0 50 -1 0 12 0.0000 4 135 780 8250 5700 EAP-GTC\001 +-6 +6 9300 5475 10350 5775 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 9300 5475 10350 5475 10350 5775 9300 5775 9300 5475 +4 0 0 50 -1 0 12 0.0000 4 135 765 9375 5700 EAP-OTP\001 +-6 +6 8175 5850 9225 6150 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 8175 5850 9225 5850 9225 6150 8175 6150 8175 5850 +4 0 0 50 -1 0 12 0.0000 4 135 750 8250 6075 EAP-SIM\001 +-6 +6 8175 6600 9675 6900 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 8175 6600 9675 6600 9675 6900 8175 6900 8175 6600 +4 0 0 50 -1 0 12 0.0000 4 135 1365 8250 6825 EAP-MSCHAPv2\001 +-6 +6 9300 6225 10350 6525 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 9300 6225 10350 6225 10350 6525 9300 6525 9300 6225 +4 0 0 50 -1 0 12 0.0000 4 135 465 9375 6450 LEAP\001 +-6 +6 8175 6225 9225 6525 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 8175 6225 9225 6225 9225 6525 8175 6525 8175 6225 +4 0 0 50 -1 0 12 0.0000 4 135 765 8250 6450 EAP-PSK\001 +-6 +6 9300 5850 10350 6150 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 9300 5850 10350 5850 10350 6150 9300 6150 9300 5850 +4 0 0 50 -1 0 12 0.0000 4 135 825 9375 6075 EAP-AKA\001 +-6 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 8100 6975 10425 6975 10425 4425 8100 4425 8100 6975 +4 0 0 50 -1 0 12 0.0000 4 135 1050 8700 4650 EAP methods\001 +-6 +2 1 1 1 0 7 50 -1 -1 3.000 0 0 -1 0 0 2 + 1275 4200 1875 4200 +2 1 1 1 0 7 50 -1 -1 4.000 0 0 -1 0 0 2 + 4500 2550 3900 1500 +2 1 1 1 0 7 50 -1 -1 4.000 0 0 -1 0 0 2 + 4800 2550 5400 1500 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 2925 4200 4350 4200 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 5025 3900 6000 3000 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 5025 4200 6000 4200 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 4650 6000 4650 4425 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 6600 4425 6600 4950 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 6600 3225 6600 3900 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 7200 5250 8100 5250 +2 1 0 1 2 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 9075 4425 9075 3300 +2 1 0 1 2 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 7200 3000 8700 3150 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 4650 3900 4650 2850 +2 1 0 1 2 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 7200 4125 8700 3300 +2 1 0 1 2 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 6000 4350 5025 6000 +2 1 0 1 2 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 6000 3150 4875 6000 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 1500 2100 10800 2100 10800 7500 1500 7500 1500 2100 +2 1 0 1 2 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 9900 4425 9900 3300 +4 0 0 50 -1 0 12 0.0000 4 135 915 375 3975 EAPOL and\001 +4 0 0 50 -1 0 12 0.0000 4 180 630 375 4200 pre-auth\001 +4 0 0 50 -1 0 12 0.0000 4 180 810 375 4425 ethertypes\001 +4 0 0 50 -1 0 12 0.0000 4 135 1050 375 4650 from/to kernel\001 +4 0 0 50 -1 0 12 0.0000 4 135 1920 3675 1875 frontend control interface\001 +4 0 0 50 -1 2 14 0.0000 4 195 1425 1637 2371 wpa_supplicant\001 diff --git a/contrib/wpa_supplicant/driver.h b/contrib/wpa_supplicant/driver.h new file mode 100644 index 0000000..da28014 --- /dev/null +++ b/contrib/wpa_supplicant/driver.h @@ -0,0 +1,436 @@ +#ifndef DRIVER_H +#define DRIVER_H + +#define WPA_SUPPLICANT_DRIVER_VERSION 2 + +typedef enum { WPA_ALG_NONE, WPA_ALG_WEP, WPA_ALG_TKIP, WPA_ALG_CCMP } wpa_alg; +typedef enum { CIPHER_NONE, CIPHER_WEP40, CIPHER_TKIP, CIPHER_CCMP, + CIPHER_WEP104 } wpa_cipher; +typedef enum { KEY_MGMT_802_1X, KEY_MGMT_PSK, KEY_MGMT_NONE, + KEY_MGMT_802_1X_NO_WPA, KEY_MGMT_WPA_NONE } wpa_key_mgmt; + +#define AUTH_ALG_OPEN_SYSTEM 0x01 +#define AUTH_ALG_SHARED_KEY 0x02 +#define AUTH_ALG_LEAP 0x04 + +#define IEEE80211_MODE_INFRA 0 +#define IEEE80211_MODE_IBSS 1 + +#define SSID_MAX_WPA_IE_LEN 40 +struct wpa_scan_result { + u8 bssid[ETH_ALEN]; + u8 ssid[32]; + size_t ssid_len; + u8 wpa_ie[SSID_MAX_WPA_IE_LEN]; + size_t wpa_ie_len; + u8 rsn_ie[SSID_MAX_WPA_IE_LEN]; + size_t rsn_ie_len; + int freq; /* MHz */ + int caps; /* e.g. privacy */ + int qual; /* signal quality */ + int noise; + int level; + int maxrate; +}; + +/* Parameters for associate driver_ops. */ +struct wpa_driver_associate_params { + /* BSSID of the selected AP */ + const u8 *bssid; + + /* The selected SSID and its length in bytes */ + const u8 *ssid; + size_t ssid_len; + + /* frequency that the selected AP is using (in MHz as + * reported in the scan results) */ + int freq; + + /* WPA information element to be included in (Re)Association + * Request (including information element id and length). Use + * of this WPA IE is optional. If the driver generates the WPA + * IE, it can use @pairwise_suite, @group_suite, and + * @key_mgmt_suite to select proper algorithms. In this case, + * the driver has to notify wpa_supplicant about the used WPA + * IE by generating an event that the interface code will + * convert into EVENT_ASSOCINFO data (see wpa_supplicant.h). + * When using WPA2/IEEE 802.11i, @wpa_ie is used for RSN IE + * instead. The driver can determine which version is used by + * looking at the first byte of the IE (0xdd for WPA, 0x30 for + * WPA2/RSN). @wpa_ie_len: length of the @wpa_ie */ + const u8 *wpa_ie; + size_t wpa_ie_len; + + /* The selected pairwise/group cipher and key management + * suites. These are usually ignored if @wpa_ie is used. */ + wpa_cipher pairwise_suite; + wpa_cipher group_suite; + wpa_key_mgmt key_mgmt_suite; + + int auth_alg; /* bit field of AUTH_ALG_* */ + int mode; /* IEEE80211_MODE_* */ +}; + +struct wpa_driver_capa { +#define WPA_DRIVER_CAPA_KEY_MGMT_WPA 0x00000001 +#define WPA_DRIVER_CAPA_KEY_MGMT_WPA2 0x00000002 +#define WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK 0x00000004 +#define WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK 0x00000008 +#define WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE 0x00000010 + unsigned int key_mgmt; + +#define WPA_DRIVER_CAPA_ENC_WEP40 0x00000001 +#define WPA_DRIVER_CAPA_ENC_WEP104 0x00000002 +#define WPA_DRIVER_CAPA_ENC_TKIP 0x00000004 +#define WPA_DRIVER_CAPA_ENC_CCMP 0x00000008 + unsigned int enc; + +#define WPA_DRIVER_AUTH_OPEN 0x00000001 +#define WPA_DRIVER_AUTH_SHARED 0x00000002 +#define WPA_DRIVER_AUTH_LEAP 0x00000004 + unsigned int auth; + +/* Driver generated WPA/RSN IE */ +#define WPA_DRIVER_FLAGS_DRIVER_IE 0x00000001 +#define WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC 0x00000002 + unsigned int flags; +}; + + +struct wpa_driver_ops { + /* name of the driver interface */ + const char *name; + /* one line description of the driver interface */ + const char *desc; + + /** + * get_bssid - get the current BSSID + * @priv: private driver interface data + * @bssid: buffer for BSSID (ETH_ALEN = 6 bytes) + * + * Returns: 0 on success, -1 on failure + * + * Query kernel driver for the current BSSID and copy it to @bssid. + * Setting @bssid to 00:00:00:00:00:00 is recommended if the STA is not + * associated. + */ + int (*get_bssid)(void *priv, u8 *bssid); + + /** + * get_ssid - get the current SSID + * @priv: private driver interface data + * @ssid: buffer for SSID (at least 32 bytes) + * + * Returns: length of the SSID on success, -1 on failure + * + * Query kernel driver for the current SSID and copy it to @ssid. + * Returning zero is recommended if the STA is not associated. + * + * Note: SSID is an array of octets, i.e., it is not nul terminated and + * can, at least in theory, contain control characters (including nul) + * and as such, should be processed as binary data, not a printable + * string. + */ + int (*get_ssid)(void *priv, u8 *ssid); + + /** + * set_wpa - enable/disable WPA support + * @priv: private driver interface data + * @enabled: 1 = enable, 0 = disable + * + * Returns: 0 on success, -1 on failure + * + * Configure the kernel driver to enable/disable WPA support. This may + * be empty function, if WPA support is always enabled. Common + * configuration items are WPA IE (clearing it when WPA support is + * disabled), Privacy flag for capability field, roaming mode (need to + * allow wpa_supplicant to control roaming). + */ + int (*set_wpa)(void *priv, int enabled); + + /** + * set_key - configure encryption key + * @priv: private driver interface data + * @alg: encryption algorithm (%WPA_ALG_NONE, %WPA_ALG_WEP, + * %WPA_ALG_TKIP, %WPA_ALG_CCMP); %WPA_ALG_NONE clears the key. + * @addr: address of the peer STA or ff:ff:ff:ff:ff:ff for + * broadcast/default keys + * @key_idx: key index (0..3), always 0 for unicast keys + * @set_tx: configure this key as the default Tx key (only used when + * driver does not support separate unicast/individual key + * @seq: sequence number/packet number, @seq_len octets, the next + * packet number to be used for in replay protection; configured + * for Rx keys (in most cases, this is only used with broadcast + * keys and set to zero for unicast keys) + * @seq_len: length of the @seq, depends on the algorithm: + * TKIP: 6 octets, CCMP: 6 octets + * @key: key buffer; TKIP: 16-byte temporal key, 8-byte Tx Mic key, + * 8-byte Rx Mic Key + * @key_len: length of the key buffer in octets (WEP: 5 or 13, + * TKIP: 32, CCMP: 16) + * + * Returns: 0 on success, -1 on failure + * + * Configure the given key for the kernel driver. If the driver + * supports separate individual keys (4 default keys + 1 individual), + * @addr can be used to determine whether the key is default or + * individual. If only 4 keys are supported, the default key with key + * index 0 is used as the individual key. STA must be configured to use + * it as the default Tx key (@set_tx is set) and accept Rx for all the + * key indexes. In most cases, WPA uses only key indexes 1 and 2 for + * broadcast keys, so key index 0 is available for this kind of + * configuration. + */ + int (*set_key)(void *priv, wpa_alg alg, const u8 *addr, + int key_idx, int set_tx, const u8 *seq, size_t seq_len, + const u8 *key, size_t key_len); + + /** + * init - initialize driver interface + * @ctx: context to be used when calling wpa_supplicant functions, + * e.g., wpa_supplicant_event() + * @ifname: interface name, e.g., wlan0 + * + * Return: pointer to private data, %NULL on failure + * + * Initialize driver interface, including event processing for kernel + * driver events (e.g., associated, scan results, Michael MIC failure). + * This function can allocate a private configuration data area for + * @ctx, file descriptor, interface name, etc. information that may be + * needed in future driver operations. If this is not used, non-NULL + * value will need to be returned because %NULL is used to indicate + * failure. The returned value will be used as 'void *priv' data for + * all other driver_ops functions. + * + * The main event loop (eloop.c) of wpa_supplicant can be used to + * register callback for read sockets (eloop_register_read_sock()). + * + * See wpa_supplicant.h for more information about events and + * wpa_supplicant_event() function. + */ + void * (*init)(void *ctx, const char *ifname); + + /** + * deinit - deinitialize driver interface + * @priv: pointer to private data (from matching + * wpa_driver_events_init()) + * + * Shut down driver interface and processing of driver events. Free + * private data buffer if one was allocated in init() handler. + */ + void (*deinit)(void *priv); + + /** + * set_countermeasures - enable/disable TKIP countermeasures + * @priv: private driver interface data + * @enabled: 1 = countermeasures enabled, 0 = disabled + * + * Return: 0 on success, -1 on failure + * + * Configure TKIP countermeasures. When these are enabled, the driver + * should drop all received and queued frames that are using TKIP. + */ + int (*set_countermeasures)(void *priv, int enabled); + + /** + * set_drop_unencrypted - enable/disable unencrypted frame filtering + * @priv: private driver interface data + * @enabled: 1 = unencrypted Tx/Rx frames will be dropped, 0 = disabled + * + * Return: 0 on success, -1 on failure + * + * Configure the driver to drop all non-EAPOL frames (both receive and + * transmit paths). Unencrypted EAPOL frames (ethertype 0x888e) must + * still be allowed for key negotiation. + */ + int (*set_drop_unencrypted)(void *priv, int enabled); + + /** + * scan - request the driver to initiate scan + * @priv: private driver interface data + * @ssid: specific SSID to scan for (ProbeReq) or %NULL to scan for + * all SSIDs (either active scan with broadcast SSID or passive + * scan + * @ssid_len: length of the SSID + * + * Return: 0 on success, -1 on failure + * + * Once the scan results are ready, the driver should report scan + * results event for wpa_supplicant which will eventually request the + * results with wpa_driver_get_scan_results(). + */ + int (*scan)(void *priv, const u8 *ssid, size_t ssid_len); + + /** + * get_scan_results - fetch the latest scan results + * @priv: private driver interface data + * @results: pointer to buffer for scan results + * @max_size: maximum number of entries (buffer size) + * + * Return: number of scan result entries used on success, -1 on failure + * + * If scan results include more than @max_size BSSes, @max_size will be + * returned and the remaining entries will not be included in the + * buffer. + */ + int (*get_scan_results)(void *priv, + struct wpa_scan_result *results, + size_t max_size); + + /** + * deauthenticate - request driver to deauthenticate + * @priv: private driver interface data + * @addr: peer address (BSSID of the AP) + * @reason_code: 16-bit reason code to be sent in the deauthentication + * frame + * + * Return: 0 on success, -1 on failure + */ + int (*deauthenticate)(void *priv, const u8 *addr, int reason_code); + + /** + * disassociate - request driver to disassociate + * @priv: private driver interface data + * @addr: peer address (BSSID of the AP) + * @reason_code: 16-bit reason code to be sent in the disassociation + * frame + * + * Return: 0 on success, -1 on failure + */ + int (*disassociate)(void *priv, const u8 *addr, int reason_code); + + /** + * associate - request driver to associate + * @priv: private driver interface data + * @params: association parameters + * + * Return: 0 on success, -1 on failure + */ + int (*associate)(void *priv, + struct wpa_driver_associate_params *params); + + /** + * set_auth_alg - set IEEE 802.11 authentication algorithm + * @priv: private driver interface data + * @auth_alg: bit field of AUTH_ALG_* + * + * If the driver supports more than one authentication algorithm at the + * same time, it should configure all supported algorithms. If not, one + * algorithm needs to be selected arbitrarily. Open System + * authentication should be ok for most cases and it is recommended to + * be used if other options are not supported. Static WEP configuration + * may also use Shared Key authentication and LEAP requires its own + * algorithm number. For LEAP, user can make sure that only one + * algorithm is used at a time by configuring LEAP as the only + * supported EAP method. This information is also available in + * associate() params, so set_auth_alg may not be needed in case of + * most drivers. + * + * Return: 0 on success, -1 on failure + */ + int (*set_auth_alg)(void *priv, int auth_alg); + + /** + * add_pmkid - add PMKSA cache entry to the driver + * @priv: private driver interface data + * @bssid: BSSID for the PMKSA cache entry + * @pmkid: PMKID for the PMKSA cache entry + * + * Return: 0 on success, -1 on failure + * + * This function is called when a new PMK is received, as a result of + * either normal authentication or RSN pre-authentication. + * + * If the driver generates RSN IE, i.e., it does not use @wpa_ie in + * associate(), add_pmkid() can be used to add new PMKSA cache entries + * in the driver. If the driver uses @wpa_ie from wpa_supplicant, this + * driver_ops function does not need to be implemented. Likewise, if + * the driver does not support WPA, this function is not needed. + */ + int (*add_pmkid)(void *priv, const u8 *bssid, const u8 *pmkid); + + /** + * remove_pmkid - remove PMKSA cache entry to the driver + * @priv: private driver interface data + * @bssid: BSSID for the PMKSA cache entry + * @pmkid: PMKID for the PMKSA cache entry + * + * Return: 0 on success, -1 on failure + * + * This function is called when the supplicant drops a PMKSA cache + * entry for any reason. + * + * If the driver generates RSN IE, i.e., it does not use @wpa_ie in + * associate(), remove_pmkid() can be used to synchronize PMKSA caches + * between the driver and wpa_supplicant. If the driver uses @wpa_ie + * from wpa_supplicant, this driver_ops function does not need to be + * implemented. Likewise, if the driver does not support WPA, this + * function is not needed. + */ + int (*remove_pmkid)(void *priv, const u8 *bssid, const u8 *pmkid); + + /** + * flush_pmkid - flush PMKSA cache + * @priv: private driver interface data + * + * Return: 0 on success, -1 on failure + * + * This function is called when the supplicant drops all PMKSA cache + * entries for any reason. + * + * If the driver generates RSN IE, i.e., it does not use @wpa_ie in + * associate(), remove_pmkid() can be used to synchronize PMKSA caches + * between the driver and wpa_supplicant. If the driver uses @wpa_ie + * from wpa_supplicant, this driver_ops function does not need to be + * implemented. Likewise, if the driver does not support WPA, this + * function is not needed. + */ + int (*flush_pmkid)(void *priv); + + /** + * flush_pmkid - flush PMKSA cache + * @priv: private driver interface data + * + * Return: 0 on success, -1 on failure + * + * Get driver/firmware/hardware capabilities. + */ + int (*get_capa)(void *priv, struct wpa_driver_capa *capa); + + /** + * poll - poll driver for association information + * @priv: private driver interface data + * + * This is an option callback that can be used when the driver does not + * provide event mechanism for association events. This is called when + * receiving WPA EAPOL-Key messages that require association + * information. The driver interface is supposed to generate associnfo + * event before returning from this callback function. In addition, the + * driver interface should generate an association event after having + * sent out associnfo. + */ + void (*poll)(void *priv); + + /** + * get_ifname - get interface name + * + * This optional function can be used to allow the driver interface to + * replace the interface name with something else, e.g., based on an + * interface mapping from a more descriptive name. + * + * Returns a pointer to the interface name. This can differ from the + * interface name used in init() call. + */ + const char * (*get_ifname)(void *priv); + + /** + * get_mac_addr - get own MAC address + * + * This optional function can be used to get the own MAC address of the + * device from the driver interface code. This is only needed if the + * l2_packet implementation for the OS does not provide easy access to + * a MAC address. */ + const u8 * (*get_mac_addr)(void *priv); +}; + +#endif /* DRIVER_H */ diff --git a/contrib/wpa_supplicant/drivers.c b/contrib/wpa_supplicant/drivers.c new file mode 100644 index 0000000..cb016ba --- /dev/null +++ b/contrib/wpa_supplicant/drivers.c @@ -0,0 +1,96 @@ +/* + * WPA Supplicant / driver interface list + * Copyright (c) 2004, Jouni Malinen <jkmaline@cc.hut.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include <stdlib.h> + + +#ifdef CONFIG_DRIVER_HOSTAP +extern struct wpa_driver_ops wpa_driver_hostap_ops; /* driver_hostap.c */ +#endif /* CONFIG_DRIVER_HOSTAP */ +#ifdef CONFIG_DRIVER_PRISM54 +extern struct wpa_driver_ops wpa_driver_prism54_ops; /* driver_prism54.c */ +#endif /* CONFIG_DRIVER_PRISM54 */ +#ifdef CONFIG_DRIVER_HERMES +extern struct wpa_driver_ops wpa_driver_hermes_ops; /* driver_hermes.c */ +#endif /* CONFIG_DRIVER_HERMES */ +#ifdef CONFIG_DRIVER_MADWIFI +extern struct wpa_driver_ops wpa_driver_madwifi_ops; /* driver_madwifi.c */ +#endif /* CONFIG_DRIVER_MADWIFI */ +#ifdef CONFIG_DRIVER_ATMEL +extern struct wpa_driver_ops wpa_driver_atmel_ops; /* driver_atmel.c */ +#endif /* CONFIG_DRIVER_ATMEL */ +#ifdef CONFIG_DRIVER_WEXT +extern struct wpa_driver_ops wpa_driver_wext_ops; /* driver_wext.c */ +#endif /* CONFIG_DRIVER_WEXT */ +#ifdef CONFIG_DRIVER_NDISWRAPPER +/* driver_ndiswrapper.c */ +extern struct wpa_driver_ops wpa_driver_ndiswrapper_ops; +#endif /* CONFIG_DRIVER_NDISWRAPPER */ +#ifdef CONFIG_DRIVER_BROADCOM +extern struct wpa_driver_ops wpa_driver_broadcom_ops; /* driver_broadcom.c */ +#endif /* CONFIG_DRIVER_BROADCOM */ +#ifdef CONFIG_DRIVER_IPW +extern struct wpa_driver_ops wpa_driver_ipw_ops; /* driver_ipw.c */ +#endif /* CONFIG_DRIVER_IPW */ +#ifdef CONFIG_DRIVER_BSD +extern struct wpa_driver_ops wpa_driver_bsd_ops; /* driver_bsd.c */ +#endif /* CONFIG_DRIVER_BSD */ +#ifdef CONFIG_DRIVER_NDIS +extern struct wpa_driver_ops wpa_driver_ndis_ops; /* driver_ndis.c */ +#endif /* CONFIG_DRIVER_NDIS */ +#ifdef CONFIG_DRIVER_TEST +extern struct wpa_driver_ops wpa_driver_test_ops; /* driver_test.c */ +#endif /* CONFIG_DRIVER_TEST */ + + +struct wpa_driver_ops *wpa_supplicant_drivers[] = +{ +#ifdef CONFIG_DRIVER_HOSTAP + &wpa_driver_hostap_ops, +#endif /* CONFIG_DRIVER_HOSTAP */ +#ifdef CONFIG_DRIVER_PRISM54 + &wpa_driver_prism54_ops, +#endif /* CONFIG_DRIVER_PRISM54 */ +#ifdef CONFIG_DRIVER_HERMES + &wpa_driver_hermes_ops, +#endif /* CONFIG_DRIVER_HERMES */ +#ifdef CONFIG_DRIVER_MADWIFI + &wpa_driver_madwifi_ops, +#endif /* CONFIG_DRIVER_MADWIFI */ +#ifdef CONFIG_DRIVER_ATMEL + &wpa_driver_atmel_ops, +#endif /* CONFIG_DRIVER_ATMEL */ +#ifdef CONFIG_DRIVER_WEXT + &wpa_driver_wext_ops, +#endif /* CONFIG_DRIVER_WEXT */ +#ifdef CONFIG_DRIVER_NDISWRAPPER + &wpa_driver_ndiswrapper_ops, +#endif /* CONFIG_DRIVER_NDISWRAPPER */ +#ifdef CONFIG_DRIVER_BROADCOM + &wpa_driver_broadcom_ops, +#endif /* CONFIG_DRIVER_BROADCOM */ +#ifdef CONFIG_DRIVER_IPW + &wpa_driver_ipw_ops, +#endif /* CONFIG_DRIVER_IPW */ +#ifdef CONFIG_DRIVER_BSD + &wpa_driver_bsd_ops, +#endif /* CONFIG_DRIVER_BSD */ +#ifdef CONFIG_DRIVER_NDIS + &wpa_driver_ndis_ops, +#endif /* CONFIG_DRIVER_NDIS */ +#ifdef CONFIG_DRIVER_TEST + &wpa_driver_test_ops, +#endif /* CONFIG_DRIVER_TEST */ + NULL +}; diff --git a/contrib/wpa_supplicant/eap.c b/contrib/wpa_supplicant/eap.c new file mode 100644 index 0000000..a76b942 --- /dev/null +++ b/contrib/wpa_supplicant/eap.c @@ -0,0 +1,1243 @@ +/* + * WPA Supplicant / EAP state machines + * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include "common.h" +#include "eap_i.h" +#include "wpa_supplicant.h" +#include "config_ssid.h" +#include "tls.h" +#include "md5.h" + + +#define EAP_MAX_AUTH_ROUNDS 50 + + +#ifdef EAP_MD5 +extern const struct eap_method eap_method_md5; +#endif +#ifdef EAP_TLS +extern const struct eap_method eap_method_tls; +#endif +#ifdef EAP_MSCHAPv2 +extern const struct eap_method eap_method_mschapv2; +#endif +#ifdef EAP_PEAP +extern const struct eap_method eap_method_peap; +#endif +#ifdef EAP_TTLS +extern const struct eap_method eap_method_ttls; +#endif +#ifdef EAP_GTC +extern const struct eap_method eap_method_gtc; +#endif +#ifdef EAP_OTP +extern const struct eap_method eap_method_otp; +#endif +#ifdef EAP_SIM +extern const struct eap_method eap_method_sim; +#endif +#ifdef EAP_LEAP +extern const struct eap_method eap_method_leap; +#endif +#ifdef EAP_PSK +extern const struct eap_method eap_method_psk; +#endif +#ifdef EAP_AKA +extern const struct eap_method eap_method_aka; +#endif +#ifdef EAP_FAST +extern const struct eap_method eap_method_fast; +#endif + +static const struct eap_method *eap_methods[] = +{ +#ifdef EAP_MD5 + &eap_method_md5, +#endif +#ifdef EAP_TLS + &eap_method_tls, +#endif +#ifdef EAP_MSCHAPv2 + &eap_method_mschapv2, +#endif +#ifdef EAP_PEAP + &eap_method_peap, +#endif +#ifdef EAP_TTLS + &eap_method_ttls, +#endif +#ifdef EAP_GTC + &eap_method_gtc, +#endif +#ifdef EAP_OTP + &eap_method_otp, +#endif +#ifdef EAP_SIM + &eap_method_sim, +#endif +#ifdef EAP_LEAP + &eap_method_leap, +#endif +#ifdef EAP_PSK + &eap_method_psk, +#endif +#ifdef EAP_AKA + &eap_method_aka, +#endif +#ifdef EAP_FAST + &eap_method_fast, +#endif +}; +#define NUM_EAP_METHODS (sizeof(eap_methods) / sizeof(eap_methods[0])) + + +const struct eap_method * eap_sm_get_eap_methods(int method) +{ + int i; + for (i = 0; i < NUM_EAP_METHODS; i++) { + if (eap_methods[i]->method == method) + return eap_methods[i]; + } + return NULL; +} + + +static Boolean eap_sm_allowMethod(struct eap_sm *sm, EapType method); +static u8 * eap_sm_buildNak(struct eap_sm *sm, int id, size_t *len); +static void eap_sm_processIdentity(struct eap_sm *sm, u8 *req, size_t len); +static void eap_sm_processNotify(struct eap_sm *sm, u8 *req, size_t len); +static u8 * eap_sm_buildNotify(struct eap_sm *sm, int id, size_t *len); +static void eap_sm_parseEapReq(struct eap_sm *sm, u8 *req, size_t len); +static const char * eap_sm_method_state_txt(int state); +static const char * eap_sm_decision_txt(int decision); + + +/* Definitions for clarifying state machine implementation */ +#define SM_STATE(machine, state) \ +static void sm_ ## machine ## _ ## state ## _Enter(struct eap_sm *sm, \ + int global) + +#define SM_ENTRY(machine, state) \ +if (!global || sm->machine ## _state != machine ## _ ## state) { \ + sm->changed = TRUE; \ + wpa_printf(MSG_DEBUG, "EAP: " #machine " entering state " #state); \ +} \ +sm->machine ## _state = machine ## _ ## state; + +#define SM_ENTER(machine, state) \ +sm_ ## machine ## _ ## state ## _Enter(sm, 0) +#define SM_ENTER_GLOBAL(machine, state) \ +sm_ ## machine ## _ ## state ## _Enter(sm, 1) + +#define SM_STEP(machine) \ +static void sm_ ## machine ## _Step(struct eap_sm *sm) + +#define SM_STEP_RUN(machine) sm_ ## machine ## _Step(sm) + + +static Boolean eapol_get_bool(struct eap_sm *sm, enum eapol_bool_var var) +{ + return sm->eapol_cb->get_bool(sm->eapol_ctx, var); +} + + +static void eapol_set_bool(struct eap_sm *sm, enum eapol_bool_var var, + Boolean value) +{ + sm->eapol_cb->set_bool(sm->eapol_ctx, var, value); +} + + +static unsigned int eapol_get_int(struct eap_sm *sm, enum eapol_int_var var) +{ + return sm->eapol_cb->get_int(sm->eapol_ctx, var); +} + + +static void eapol_set_int(struct eap_sm *sm, enum eapol_int_var var, + unsigned int value) +{ + sm->eapol_cb->set_int(sm->eapol_ctx, var, value); +} + + +static u8 * eapol_get_eapReqData(struct eap_sm *sm, size_t *len) +{ + return sm->eapol_cb->get_eapReqData(sm->eapol_ctx, len); +} + + +static void eap_deinit_prev_method(struct eap_sm *sm, const char *txt) +{ + if (sm->m == NULL || sm->eap_method_priv == NULL) + return; + + wpa_printf(MSG_DEBUG, "EAP: deinitialize previously used EAP method " + "(%d, %s) at %s", sm->selectedMethod, sm->m->name, txt); + sm->m->deinit(sm, sm->eap_method_priv); + sm->eap_method_priv = NULL; + sm->m = NULL; +} + + +SM_STATE(EAP, INITIALIZE) +{ + SM_ENTRY(EAP, INITIALIZE); + if (sm->fast_reauth && sm->m && sm->m->has_reauth_data && + sm->m->has_reauth_data(sm, sm->eap_method_priv)) { + wpa_printf(MSG_DEBUG, "EAP: maintaining EAP method data for " + "fast reauthentication"); + sm->m->deinit_for_reauth(sm, sm->eap_method_priv); + } else { + eap_deinit_prev_method(sm, "INITIALIZE"); + } + sm->selectedMethod = EAP_TYPE_NONE; + sm->methodState = METHOD_NONE; + sm->allowNotifications = TRUE; + sm->decision = DECISION_FAIL; + eapol_set_int(sm, EAPOL_idleWhile, sm->ClientTimeout); + eapol_set_bool(sm, EAPOL_eapSuccess, FALSE); + eapol_set_bool(sm, EAPOL_eapFail, FALSE); + free(sm->eapKeyData); + sm->eapKeyData = NULL; + sm->eapKeyAvailable = FALSE; + eapol_set_bool(sm, EAPOL_eapRestart, FALSE); + sm->lastId = -1; /* new session - make sure this does not match with + * the first EAP-Packet */ + /* draft-ietf-eap-statemachine-02.pdf does not reset eapResp and + * eapNoResp here. However, this seemed to be able to trigger cases + * where both were set and if EAPOL state machine uses eapNoResp first, + * it may end up not sending a real reply correctly. This occurred + * when the workaround in FAIL state set eapNoResp = TRUE.. Maybe that + * workaround needs to be fixed to do something else(?) */ + eapol_set_bool(sm, EAPOL_eapResp, FALSE); + eapol_set_bool(sm, EAPOL_eapNoResp, FALSE); + sm->num_rounds = 0; +} + + +SM_STATE(EAP, DISABLED) +{ + SM_ENTRY(EAP, DISABLED); + sm->num_rounds = 0; +} + + +SM_STATE(EAP, IDLE) +{ + SM_ENTRY(EAP, IDLE); +} + + +SM_STATE(EAP, RECEIVED) +{ + u8 *eapReqData; + size_t eapReqDataLen; + + SM_ENTRY(EAP, RECEIVED); + eapReqData = eapol_get_eapReqData(sm, &eapReqDataLen); + /* parse rxReq, rxSuccess, rxFailure, reqId, reqMethod */ + eap_sm_parseEapReq(sm, eapReqData, eapReqDataLen); + sm->num_rounds++; +} + + +SM_STATE(EAP, GET_METHOD) +{ + SM_ENTRY(EAP, GET_METHOD); + if (eap_sm_allowMethod(sm, sm->reqMethod)) { + int reinit = 0; + if (sm->fast_reauth && + sm->m && sm->m->method == sm->reqMethod && + sm->m->has_reauth_data && + sm->m->has_reauth_data(sm, sm->eap_method_priv)) { + wpa_printf(MSG_DEBUG, "EAP: using previous method data" + " for fast re-authentication"); + reinit = 1; + } else + eap_deinit_prev_method(sm, "GET_METHOD"); + sm->selectedMethod = sm->reqMethod; + if (sm->m == NULL) + sm->m = eap_sm_get_eap_methods(sm->selectedMethod); + if (sm->m) { + wpa_printf(MSG_DEBUG, "EAP: initialize selected EAP " + "method (%d, %s)", + sm->selectedMethod, sm->m->name); + if (reinit) + sm->eap_method_priv = sm->m->init_for_reauth( + sm, sm->eap_method_priv); + else + sm->eap_method_priv = sm->m->init(sm); + if (sm->eap_method_priv == NULL) { + wpa_printf(MSG_DEBUG, "EAP: Failed to " + "initialize EAP method %d", + sm->selectedMethod); + sm->m = NULL; + sm->methodState = METHOD_NONE; + sm->selectedMethod = EAP_TYPE_NONE; + } else { + sm->methodState = METHOD_INIT; + return; + } + } + } + + free(sm->eapRespData); + sm->eapRespData = eap_sm_buildNak(sm, sm->reqId, &sm->eapRespDataLen); +} + + +SM_STATE(EAP, METHOD) +{ + u8 *eapReqData; + size_t eapReqDataLen; + struct eap_method_ret ret; + + SM_ENTRY(EAP, METHOD); + if (sm->m == NULL) { + wpa_printf(MSG_WARNING, "EAP::METHOD - method not selected"); + return; + } + + eapReqData = eapol_get_eapReqData(sm, &eapReqDataLen); + + /* Get ignore, methodState, decision, allowNotifications, and + * eapRespData. */ + memset(&ret, 0, sizeof(ret)); + ret.ignore = sm->ignore; + ret.methodState = sm->methodState; + ret.decision = sm->decision; + ret.allowNotifications = sm->allowNotifications; + free(sm->eapRespData); + sm->eapRespData = sm->m->process(sm, sm->eap_method_priv, &ret, + eapReqData, eapReqDataLen, + &sm->eapRespDataLen); + wpa_printf(MSG_DEBUG, "EAP: method process -> ignore=%s " + "methodState=%s decision=%s", + ret.ignore ? "TRUE" : "FALSE", + eap_sm_method_state_txt(ret.methodState), + eap_sm_decision_txt(ret.decision)); + + sm->ignore = ret.ignore; + if (sm->ignore) + return; + sm->methodState = ret.methodState; + sm->decision = ret.decision; + sm->allowNotifications = ret.allowNotifications; + + if (sm->m->isKeyAvailable && sm->m->getKey && + sm->m->isKeyAvailable(sm, sm->eap_method_priv)) { + free(sm->eapKeyData); + sm->eapKeyData = sm->m->getKey(sm, sm->eap_method_priv, + &sm->eapKeyDataLen); + } +} + + +SM_STATE(EAP, SEND_RESPONSE) +{ + SM_ENTRY(EAP, SEND_RESPONSE); + free(sm->lastRespData); + if (sm->eapRespData) { + if (sm->workaround) + memcpy(sm->last_md5, sm->req_md5, 16); + sm->lastId = sm->reqId; + sm->lastRespData = malloc(sm->eapRespDataLen); + if (sm->lastRespData) { + memcpy(sm->lastRespData, sm->eapRespData, + sm->eapRespDataLen); + sm->lastRespDataLen = sm->eapRespDataLen; + } + eapol_set_bool(sm, EAPOL_eapResp, TRUE); + } else + sm->lastRespData = NULL; + eapol_set_bool(sm, EAPOL_eapReq, FALSE); + eapol_set_int(sm, EAPOL_idleWhile, sm->ClientTimeout); +} + + +SM_STATE(EAP, DISCARD) +{ + SM_ENTRY(EAP, DISCARD); + eapol_set_bool(sm, EAPOL_eapReq, FALSE); + eapol_set_bool(sm, EAPOL_eapNoResp, TRUE); +} + + +SM_STATE(EAP, IDENTITY) +{ + u8 *eapReqData; + size_t eapReqDataLen; + + SM_ENTRY(EAP, IDENTITY); + eapReqData = eapol_get_eapReqData(sm, &eapReqDataLen); + eap_sm_processIdentity(sm, eapReqData, eapReqDataLen); + free(sm->eapRespData); + sm->eapRespData = eap_sm_buildIdentity(sm, sm->reqId, + &sm->eapRespDataLen, 0); +} + + +SM_STATE(EAP, NOTIFICATION) +{ + u8 *eapReqData; + size_t eapReqDataLen; + + SM_ENTRY(EAP, NOTIFICATION); + eapReqData = eapol_get_eapReqData(sm, &eapReqDataLen); + eap_sm_processNotify(sm, eapReqData, eapReqDataLen); + free(sm->eapRespData); + sm->eapRespData = eap_sm_buildNotify(sm, sm->reqId, + &sm->eapRespDataLen); +} + + +SM_STATE(EAP, RETRANSMIT) +{ + SM_ENTRY(EAP, RETRANSMIT); + free(sm->eapRespData); + if (sm->lastRespData) { + sm->eapRespData = malloc(sm->lastRespDataLen); + if (sm->eapRespData) { + memcpy(sm->eapRespData, sm->lastRespData, + sm->lastRespDataLen); + sm->eapRespDataLen = sm->lastRespDataLen; + } + } else + sm->eapRespData = NULL; +} + + +SM_STATE(EAP, SUCCESS) +{ + SM_ENTRY(EAP, SUCCESS); + if (sm->eapKeyData != NULL) + sm->eapKeyAvailable = TRUE; + eapol_set_bool(sm, EAPOL_eapSuccess, TRUE); + /* draft-ietf-eap-statemachine-02.pdf does not clear eapReq here, but + * this seems to be required to avoid processing the same request + * twice when state machine is initialized. */ + eapol_set_bool(sm, EAPOL_eapReq, FALSE); + /* draft-ietf-eap-statemachine-02.pdf does not set eapNoResp here, but + * this seems to be required to get EAPOL Supplicant backend state + * machine into SUCCESS state. In addition, either eapResp or eapNoResp + * is required to be set after processing the received EAP frame. */ + eapol_set_bool(sm, EAPOL_eapNoResp, TRUE); +} + + +SM_STATE(EAP, FAILURE) +{ + SM_ENTRY(EAP, FAILURE); + eapol_set_bool(sm, EAPOL_eapFail, TRUE); + /* draft-ietf-eap-statemachine-02.pdf does not clear eapReq here, but + * this seems to be required to avoid processing the same request + * twice when state machine is initialized. */ + eapol_set_bool(sm, EAPOL_eapReq, FALSE); + /* draft-ietf-eap-statemachine-02.pdf does not set eapNoResp here. + * However, either eapResp or eapNoResp is required to be set after + * processing the received EAP frame. */ + eapol_set_bool(sm, EAPOL_eapNoResp, TRUE); +} + + +static int eap_success_workaround(struct eap_sm *sm, int reqId, int lastId) +{ + /* At least Microsoft IAS and Meetinghouse Aegis seem to be sending + * EAP-Success/Failure with lastId + 1 even though RFC 3748 and + * draft-ietf-eap-statemachine-05.pdf require that reqId == lastId. + * Accept this kind of Id if EAP workarounds are enabled. These are + * unauthenticated plaintext messages, so this should have minimal + * security implications (bit easier to fake EAP-Success/Failure). */ + if (sm->workaround && reqId == ((lastId + 1) & 0xff)) { + wpa_printf(MSG_DEBUG, "EAP: Workaround for unexpected " + "identifier field in EAP Success: " + "reqId=%d lastId=%d (these are supposed to be " + "same)", reqId, lastId); + return 1; + } + return 0; +} + + +SM_STEP(EAP) +{ + int duplicate; + + if (eapol_get_bool(sm, EAPOL_eapRestart) && + eapol_get_bool(sm, EAPOL_portEnabled)) + SM_ENTER_GLOBAL(EAP, INITIALIZE); + else if (!eapol_get_bool(sm, EAPOL_portEnabled)) + SM_ENTER_GLOBAL(EAP, DISABLED); + else if (sm->num_rounds > EAP_MAX_AUTH_ROUNDS) { + if (sm->num_rounds == EAP_MAX_AUTH_ROUNDS + 1) { + wpa_printf(MSG_DEBUG, "EAP: more than %d " + "authentication rounds - abort", + EAP_MAX_AUTH_ROUNDS); + sm->num_rounds++; + SM_ENTER_GLOBAL(EAP, FAILURE); + } + } else switch (sm->EAP_state) { + case EAP_INITIALIZE: + SM_ENTER(EAP, IDLE); + break; + case EAP_DISABLED: + if (eapol_get_bool(sm, EAPOL_portEnabled)) + SM_ENTER(EAP, INITIALIZE); + break; + case EAP_IDLE: + if (eapol_get_bool(sm, EAPOL_eapReq)) + SM_ENTER(EAP, RECEIVED); + else if ((eapol_get_bool(sm, EAPOL_altAccept) && + sm->decision != DECISION_FAIL) || + (eapol_get_int(sm, EAPOL_idleWhile) == 0 && + sm->decision == DECISION_UNCOND_SUCC)) + SM_ENTER(EAP, SUCCESS); + else if (eapol_get_bool(sm, EAPOL_altReject) || + (eapol_get_int(sm, EAPOL_idleWhile) == 0 && + sm->decision != DECISION_UNCOND_SUCC) || + (eapol_get_bool(sm, EAPOL_altAccept) && + sm->methodState != METHOD_CONT && + sm->decision == DECISION_FAIL)) + SM_ENTER(EAP, FAILURE); + else if (sm->selectedMethod == EAP_TYPE_LEAP && + sm->leap_done && sm->decision != DECISION_FAIL && + sm->methodState == METHOD_DONE) + SM_ENTER(EAP, SUCCESS); + else if (sm->selectedMethod == EAP_TYPE_PEAP && + sm->peap_done && sm->decision != DECISION_FAIL && + sm->methodState == METHOD_DONE) + SM_ENTER(EAP, SUCCESS); + break; + case EAP_RECEIVED: + duplicate = sm->reqId == sm->lastId; + if (sm->workaround && duplicate && + memcmp(sm->req_md5, sm->last_md5, 16) != 0) { + /* draft-ietf-eap-statemachine-05.txt uses + * (reqId == lastId) as the only verification for + * duplicate EAP requests. However, this misses cases + * where the AS is incorrectly using the same id again; + * and unfortunately, such implementations exist. Use + * MD5 hash as an extra verification for the packets + * being duplicate to workaround these issues. */ + wpa_printf(MSG_DEBUG, "EAP: AS used the same Id again," + " but EAP packets were not identical"); + wpa_printf(MSG_DEBUG, "EAP: workaround - assume this " + "is not a duplicate packet"); + duplicate = 0; + } + + if (sm->rxSuccess && + (sm->reqId == sm->lastId || + eap_success_workaround(sm, sm->reqId, sm->lastId)) && + sm->decision != DECISION_FAIL) + SM_ENTER(EAP, SUCCESS); + else if (sm->methodState != METHOD_CONT && + ((sm->rxFailure && + sm->decision != DECISION_UNCOND_SUCC) || + (sm->rxSuccess && sm->decision == DECISION_FAIL)) && + (sm->reqId == sm->lastId || + eap_success_workaround(sm, sm->reqId, sm->lastId))) + SM_ENTER(EAP, FAILURE); + else if (sm->rxReq && duplicate) + SM_ENTER(EAP, RETRANSMIT); + else if (sm->rxReq && !duplicate && + sm->reqMethod == EAP_TYPE_NOTIFICATION && + sm->allowNotifications) + SM_ENTER(EAP, NOTIFICATION); + else if (sm->rxReq && !duplicate && + sm->selectedMethod == EAP_TYPE_NONE && + sm->reqMethod == EAP_TYPE_IDENTITY) + SM_ENTER(EAP, IDENTITY); + else if (sm->rxReq && !duplicate && + sm->selectedMethod == EAP_TYPE_NONE && + sm->reqMethod != EAP_TYPE_IDENTITY && + sm->reqMethod != EAP_TYPE_NOTIFICATION) + SM_ENTER(EAP, GET_METHOD); + else if (sm->rxReq && !duplicate && + sm->reqMethod == sm->selectedMethod && + sm->methodState != METHOD_DONE) + SM_ENTER(EAP, METHOD); + else if (sm->selectedMethod == EAP_TYPE_LEAP && + (sm->rxSuccess || sm->rxResp)) + SM_ENTER(EAP, METHOD); + else + SM_ENTER(EAP, DISCARD); + break; + case EAP_GET_METHOD: + if (sm->selectedMethod == sm->reqMethod) + SM_ENTER(EAP, METHOD); + else + SM_ENTER(EAP, SEND_RESPONSE); + break; + case EAP_METHOD: + if (sm->ignore) + SM_ENTER(EAP, DISCARD); + else + SM_ENTER(EAP, SEND_RESPONSE); + break; + case EAP_SEND_RESPONSE: + SM_ENTER(EAP, IDLE); + break; + case EAP_DISCARD: + SM_ENTER(EAP, IDLE); + break; + case EAP_IDENTITY: + SM_ENTER(EAP, SEND_RESPONSE); + break; + case EAP_NOTIFICATION: + SM_ENTER(EAP, SEND_RESPONSE); + break; + case EAP_RETRANSMIT: + SM_ENTER(EAP, SEND_RESPONSE); + break; + case EAP_SUCCESS: + break; + case EAP_FAILURE: + break; + } +} + + +static Boolean eap_sm_allowMethod(struct eap_sm *sm, EapType method) +{ + struct wpa_ssid *config = eap_get_config(sm); + int i; + + if (!wpa_config_allowed_eap_method(config, method)) + return FALSE; + for (i = 0; i < NUM_EAP_METHODS; i++) { + if (eap_methods[i]->method == method) + return TRUE; + } + return FALSE; +} + + +static u8 *eap_sm_buildNak(struct eap_sm *sm, int id, size_t *len) +{ + struct wpa_ssid *config = eap_get_config(sm); + struct eap_hdr *resp; + u8 *pos; + int i, found = 0; + + wpa_printf(MSG_DEBUG, "EAP: Building EAP-Nak (requested type %d not " + "allowed)", sm->reqMethod); + *len = sizeof(struct eap_hdr) + 1; + resp = malloc(*len + NUM_EAP_METHODS); + if (resp == NULL) + return NULL; + + resp->code = EAP_CODE_RESPONSE; + resp->identifier = id; + pos = (u8 *) (resp + 1); + *pos++ = EAP_TYPE_NAK; + + for (i = 0; i < NUM_EAP_METHODS; i++) { + if (wpa_config_allowed_eap_method(config, + eap_methods[i]->method)) { + *pos++ = eap_methods[i]->method; + (*len)++; + found++; + } + } + if (!found) { + *pos = EAP_TYPE_NONE; + (*len)++; + } + wpa_hexdump(MSG_DEBUG, "EAP: allowed methods", + ((u8 *) (resp + 1)) + 1, found); + + resp->length = host_to_be16(*len); + + return (u8 *) resp; +} + + +static void eap_sm_processIdentity(struct eap_sm *sm, u8 *req, size_t len) +{ + struct eap_hdr *hdr = (struct eap_hdr *) req; + u8 *pos = (u8 *) (hdr + 1); + pos++; + /* TODO: could save displayable message so that it can be shown to the + * user in case of interaction is required */ + wpa_hexdump_ascii(MSG_DEBUG, "EAP: EAP-Request Identity data", + pos, be_to_host16(hdr->length) - 5); +} + + +u8 *eap_sm_buildIdentity(struct eap_sm *sm, int id, size_t *len, + int encrypted) +{ + struct wpa_ssid *config = eap_get_config(sm); + struct eap_hdr *resp; + u8 *pos; + const u8 *identity; + size_t identity_len; + + if (config == NULL) { + wpa_printf(MSG_WARNING, "EAP: buildIdentity: configuration " + "was not available"); + return NULL; + } + + if (sm->m && sm->m->get_identity && + (identity = sm->m->get_identity(sm, sm->eap_method_priv, + &identity_len)) != NULL) { + wpa_hexdump_ascii(MSG_DEBUG, "EAP: using method re-auth " + "identity", identity, identity_len); + } else if (!encrypted && config->anonymous_identity) { + identity = config->anonymous_identity; + identity_len = config->anonymous_identity_len; + wpa_hexdump_ascii(MSG_DEBUG, "EAP: using anonymous identity", + identity, identity_len); + } else { + identity = config->identity; + identity_len = config->identity_len; + wpa_hexdump_ascii(MSG_DEBUG, "EAP: using real identity", + identity, identity_len); + } + + if (identity == NULL) { + wpa_printf(MSG_WARNING, "EAP: buildIdentity: identity " + "configuration was not available"); + eap_sm_request_identity(sm, config); + return NULL; + } + + + *len = sizeof(struct eap_hdr) + 1 + identity_len; + resp = malloc(*len); + if (resp == NULL) + return NULL; + + resp->code = EAP_CODE_RESPONSE; + resp->identifier = id; + resp->length = host_to_be16(*len); + pos = (u8 *) (resp + 1); + *pos++ = EAP_TYPE_IDENTITY; + memcpy(pos, identity, identity_len); + + return (u8 *) resp; +} + + +static void eap_sm_processNotify(struct eap_sm *sm, u8 *req, size_t len) +{ + struct eap_hdr *hdr = (struct eap_hdr *) req; + u8 *pos = (u8 *) (hdr + 1); + pos++; + /* TODO: log the Notification Request and make it available for UI */ + wpa_hexdump_ascii(MSG_DEBUG, "EAP: EAP-Request Notification data", + pos, be_to_host16(hdr->length) - 5); +} + + +static u8 *eap_sm_buildNotify(struct eap_sm *sm, int id, size_t *len) +{ + struct eap_hdr *resp; + u8 *pos; + + wpa_printf(MSG_DEBUG, "EAP: Generating EAP-Response Notification"); + *len = sizeof(struct eap_hdr) + 1; + resp = malloc(*len); + if (resp == NULL) + return NULL; + + resp->code = EAP_CODE_RESPONSE; + resp->identifier = id; + resp->length = host_to_be16(*len); + pos = (u8 *) (resp + 1); + *pos = EAP_TYPE_NOTIFICATION; + + return (u8 *) resp; +} + + +static void eap_sm_parseEapReq(struct eap_sm *sm, u8 *req, size_t len) +{ + struct eap_hdr *hdr; + size_t plen; + MD5_CTX context; + + sm->rxReq = sm->rxSuccess = sm->rxFailure = FALSE; + sm->reqId = 0; + sm->reqMethod = EAP_TYPE_NONE; + + if (req == NULL || len < sizeof(*hdr)) + return; + + hdr = (struct eap_hdr *) req; + plen = be_to_host16(hdr->length); + if (plen > len) { + wpa_printf(MSG_DEBUG, "EAP: Ignored truncated EAP-Packet " + "(len=%lu plen=%lu)", + (unsigned long) len, (unsigned long) plen); + return; + } + + sm->reqId = hdr->identifier; + + if (sm->workaround) { + MD5Init(&context); + MD5Update(&context, req, len); + MD5Final(sm->req_md5, &context); + } + + switch (hdr->code) { + case EAP_CODE_REQUEST: + sm->rxReq = TRUE; + if (plen > sizeof(*hdr)) + sm->reqMethod = *((u8 *) (hdr + 1)); + wpa_printf(MSG_DEBUG, "EAP: Received EAP-Request method=%d " + "id=%d", sm->reqMethod, sm->reqId); + break; + case EAP_CODE_RESPONSE: + if (sm->selectedMethod == EAP_TYPE_LEAP) { + sm->rxResp = TRUE; + if (plen > sizeof(*hdr)) + sm->reqMethod = *((u8 *) (hdr + 1)); + wpa_printf(MSG_DEBUG, "EAP: Received EAP-Response for " + "LEAP method=%d id=%d", + sm->reqMethod, sm->reqId); + break; + } + wpa_printf(MSG_DEBUG, "EAP: Ignored EAP-Response"); + break; + case EAP_CODE_SUCCESS: + wpa_printf(MSG_DEBUG, "EAP: Received EAP-Success"); + sm->rxSuccess = TRUE; + break; + case EAP_CODE_FAILURE: + wpa_printf(MSG_DEBUG, "EAP: Received EAP-Failure"); + sm->rxFailure = TRUE; + break; + default: + wpa_printf(MSG_DEBUG, "EAP: Ignored EAP-Packet with unknown " + "code %d", hdr->code); + break; + } +} + + +struct eap_sm *eap_sm_init(void *eapol_ctx, struct eapol_callbacks *eapol_cb, + void *msg_ctx) +{ + struct eap_sm *sm; + + sm = malloc(sizeof(*sm)); + if (sm == NULL) + return NULL; + memset(sm, 0, sizeof(*sm)); + sm->eapol_ctx = eapol_ctx; + sm->eapol_cb = eapol_cb; + sm->msg_ctx = msg_ctx; + sm->ClientTimeout = 60; + + sm->ssl_ctx = tls_init(); + if (sm->ssl_ctx == NULL) { + wpa_printf(MSG_WARNING, "SSL: Failed to initialize TLS " + "context."); + free(sm); + return NULL; + } + + return sm; +} + + +void eap_sm_deinit(struct eap_sm *sm) +{ + if (sm == NULL) + return; + eap_deinit_prev_method(sm, "EAP deinit"); + free(sm->lastRespData); + free(sm->eapRespData); + free(sm->eapKeyData); + tls_deinit(sm->ssl_ctx); + free(sm); +} + + +int eap_sm_step(struct eap_sm *sm) +{ + int res = 0; + do { + sm->changed = FALSE; + SM_STEP_RUN(EAP); + if (sm->changed) + res = 1; + } while (sm->changed); + return res; +} + + +void eap_sm_abort(struct eap_sm *sm) +{ + /* release system resources that may have been allocated for the + * authentication session */ + free(sm->eapRespData); + sm->eapRespData = NULL; + free(sm->eapKeyData); + sm->eapKeyData = NULL; +} + + +static const char * eap_sm_state_txt(int state) +{ + switch (state) { + case EAP_INITIALIZE: + return "INITIALIZE"; + case EAP_DISABLED: + return "DISABLED"; + case EAP_IDLE: + return "IDLE"; + case EAP_RECEIVED: + return "RECEIVED"; + case EAP_GET_METHOD: + return "GET_METHOD"; + case EAP_METHOD: + return "METHOD"; + case EAP_SEND_RESPONSE: + return "SEND_RESPONSE"; + case EAP_DISCARD: + return "DISCARD"; + case EAP_IDENTITY: + return "IDENTITY"; + case EAP_NOTIFICATION: + return "NOTIFICATION"; + case EAP_RETRANSMIT: + return "RETRANSMIT"; + case EAP_SUCCESS: + return "SUCCESS"; + case EAP_FAILURE: + return "FAILURE"; + default: + return "UNKNOWN"; + } +} + + +static const char * eap_sm_method_state_txt(int state) +{ + switch (state) { + case METHOD_NONE: + return "NONE"; + case METHOD_INIT: + return "INIT"; + case METHOD_CONT: + return "CONT"; + case METHOD_MAY_CONT: + return "MAY_CONT"; + case METHOD_DONE: + return "DONE"; + default: + return "UNKNOWN"; + } +} + + +static const char * eap_sm_decision_txt(int decision) +{ + switch (decision) { + case DECISION_FAIL: + return "FAIL"; + case DECISION_COND_SUCC: + return "COND_SUCC"; + case DECISION_UNCOND_SUCC: + return "UNCOND_SUCC"; + default: + return "UNKNOWN"; + } +} + + +int eap_sm_get_status(struct eap_sm *sm, char *buf, size_t buflen, int verbose) +{ + int len; + + if (sm == NULL) + return 0; + + len = snprintf(buf, buflen, + "EAP state=%s\n", + eap_sm_state_txt(sm->EAP_state)); + + if (sm->selectedMethod != EAP_TYPE_NONE) { + const char *name; + if (sm->m) { + name = sm->m->name; + } else { + const struct eap_method *m = + eap_sm_get_eap_methods(sm->selectedMethod); + if (m) + name = m->name; + else + name = "?"; + } + len += snprintf(buf + len, buflen - len, + "selectedMethod=%d (EAP-%s)\n", + sm->selectedMethod, name); + + if (sm->m && sm->m->get_status) { + len += sm->m->get_status(sm, sm->eap_method_priv, + buf + len, buflen - len, + verbose); + } + } + + if (verbose) { + len += snprintf(buf + len, buflen - len, + "reqMethod=%d\n" + "methodState=%s\n" + "decision=%s\n" + "ClientTimeout=%d\n", + sm->reqMethod, + eap_sm_method_state_txt(sm->methodState), + eap_sm_decision_txt(sm->decision), + sm->ClientTimeout); + } + + return len; +} + + +typedef enum { TYPE_IDENTITY, TYPE_PASSWORD, TYPE_OTP } eap_ctrl_req_type; + +static void eap_sm_request(struct eap_sm *sm, struct wpa_ssid *config, + eap_ctrl_req_type type, char *msg, size_t msglen) +{ + char *buf; + size_t buflen; + int len; + char *field; + char *txt, *tmp; + + if (config == NULL || sm == NULL) + return; + + switch (type) { + case TYPE_IDENTITY: + field = "IDENTITY"; + txt = "Identity"; + config->pending_req_identity++; + break; + case TYPE_PASSWORD: + field = "PASSWORD"; + txt = "Password"; + config->pending_req_password++; + break; + case TYPE_OTP: + field = "OTP"; + if (msg) { + tmp = malloc(msglen + 3); + if (tmp == NULL) + return; + tmp[0] = '['; + memcpy(tmp + 1, msg, msglen); + tmp[msglen + 1] = ']'; + tmp[msglen + 2] = '\0'; + txt = tmp; + free(config->pending_req_otp); + config->pending_req_otp = tmp; + config->pending_req_otp_len = msglen + 3; + } else { + if (config->pending_req_otp == NULL) + return; + txt = config->pending_req_otp; + } + break; + default: + return; + } + + buflen = 100 + strlen(txt) + config->ssid_len; + buf = malloc(buflen); + if (buf == NULL) + return; + len = snprintf(buf, buflen, "CTRL-REQ-%s-%d:%s needed for SSID ", + field, config->id, txt); + if (config->ssid && buflen > len + config->ssid_len) { + memcpy(buf + len, config->ssid, config->ssid_len); + len += config->ssid_len; + buf[len] = '\0'; + } + wpa_msg(sm->msg_ctx, MSG_INFO, buf); + free(buf); +} + + +void eap_sm_request_identity(struct eap_sm *sm, struct wpa_ssid *config) +{ + eap_sm_request(sm, config, TYPE_IDENTITY, NULL, 0); +} + + +void eap_sm_request_password(struct eap_sm *sm, struct wpa_ssid *config) +{ + eap_sm_request(sm, config, TYPE_PASSWORD, NULL, 0); +} + + +void eap_sm_request_otp(struct eap_sm *sm, struct wpa_ssid *config, + char *msg, size_t msg_len) +{ + eap_sm_request(sm, config, TYPE_OTP, msg, msg_len); +} + + +void eap_sm_notify_ctrl_attached(struct eap_sm *sm) +{ + struct wpa_ssid *config = eap_get_config(sm); + + if (config == NULL) + return; + + /* Re-send any pending requests for user data since a new control + * interface was added. This handles cases where the EAP authentication + * starts immediately after system startup when the user interface is + * not yet running. */ + if (config->pending_req_identity) + eap_sm_request_identity(sm, config); + if (config->pending_req_password) + eap_sm_request_password(sm, config); + if (config->pending_req_otp) + eap_sm_request_otp(sm, config, NULL, 0); +} + + +u8 eap_get_type(const char *name) +{ + int i; + for (i = 0; i < NUM_EAP_METHODS; i++) { + if (strcmp(eap_methods[i]->name, name) == 0) + return eap_methods[i]->method; + } + return EAP_TYPE_NONE; +} + + +static int eap_allowed_phase2_type(int type) +{ + return type != EAP_TYPE_PEAP && type != EAP_TYPE_TTLS && + type != EAP_TYPE_FAST; +} + + +u8 eap_get_phase2_type(const char *name) +{ + u8 type = eap_get_type(name); + if (eap_allowed_phase2_type(type)) + return type; + return EAP_TYPE_NONE; +} + + +u8 *eap_get_phase2_types(struct wpa_ssid *config, size_t *count) +{ + u8 *buf, method; + int i; + + *count = 0; + buf = malloc(NUM_EAP_METHODS); + if (buf == NULL) + return NULL; + + for (i = 0; i < NUM_EAP_METHODS; i++) { + method = eap_methods[i]->method; + if (eap_allowed_phase2_type(method)) { + if (method == EAP_TYPE_TLS && config && + config->private_key2 == NULL) + continue; + buf[*count] = method; + (*count)++; + } + } + + return buf; +} + + +void eap_set_fast_reauth(struct eap_sm *sm, int enabled) +{ + sm->fast_reauth = enabled; +} + + +void eap_set_workaround(struct eap_sm *sm, unsigned int workaround) +{ + sm->workaround = workaround; +} + + +struct wpa_ssid * eap_get_config(struct eap_sm *sm) +{ + return sm->eapol_cb->get_config(sm->eapol_ctx); +} + + +int eap_key_available(struct eap_sm *sm) +{ + return sm ? sm->eapKeyAvailable : 0; +} + + +void eap_notify_success(struct eap_sm *sm) +{ + if (sm) { + sm->decision = DECISION_COND_SUCC; + sm->EAP_state = EAP_SUCCESS; + } +} + + +u8 * eap_get_eapKeyData(struct eap_sm *sm, size_t *len) +{ + if (sm == NULL || sm->eapKeyData == NULL) { + *len = 0; + return NULL; + } + + *len = sm->eapKeyDataLen; + return sm->eapKeyData; +} + + +u8 * eap_get_eapRespData(struct eap_sm *sm, size_t *len) +{ + u8 *resp; + + if (sm == NULL || sm->eapRespData == NULL) { + *len = 0; + return NULL; + } + + resp = sm->eapRespData; + *len = sm->eapRespDataLen; + sm->eapRespData = NULL; + sm->eapRespDataLen = 0; + + return resp; +} + + +void eap_register_scard_ctx(struct eap_sm *sm, void *ctx) +{ + if (sm) + sm->scard_ctx = ctx; +} diff --git a/contrib/wpa_supplicant/eap.h b/contrib/wpa_supplicant/eap.h new file mode 100644 index 0000000..3d7cc72 --- /dev/null +++ b/contrib/wpa_supplicant/eap.h @@ -0,0 +1,70 @@ +#ifndef EAP_H +#define EAP_H + +#include "defs.h" +#include "eap_defs.h" + +struct eap_sm; +struct wpa_ssid; + + +#ifdef IEEE8021X_EAPOL + +enum eapol_bool_var { + EAPOL_eapSuccess, EAPOL_eapRestart, EAPOL_eapFail, EAPOL_eapResp, + EAPOL_eapNoResp, EAPOL_eapReq, EAPOL_portEnabled, EAPOL_altAccept, + EAPOL_altReject +}; + +enum eapol_int_var { + EAPOL_idleWhile +}; + +struct eapol_callbacks { + struct wpa_ssid * (*get_config)(void *ctx); + Boolean (*get_bool)(void *ctx, enum eapol_bool_var variable); + void (*set_bool)(void *ctx, enum eapol_bool_var variable, + Boolean value); + unsigned int (*get_int)(void *ctx, enum eapol_int_var variable); + void (*set_int)(void *ctx, enum eapol_int_var variable, + unsigned int value); + u8 * (*get_eapReqData)(void *ctx, size_t *len); +}; + +struct eap_sm *eap_sm_init(void *eapol_ctx, struct eapol_callbacks *eapol_cb, + void *msg_ctx); +void eap_sm_deinit(struct eap_sm *sm); +int eap_sm_step(struct eap_sm *sm); +void eap_sm_abort(struct eap_sm *sm); +int eap_sm_get_status(struct eap_sm *sm, char *buf, size_t buflen, + int verbose); +u8 *eap_sm_buildIdentity(struct eap_sm *sm, int id, size_t *len, + int encrypted); +const struct eap_method * eap_sm_get_eap_methods(int method); +void eap_sm_request_identity(struct eap_sm *sm, struct wpa_ssid *config); +void eap_sm_request_password(struct eap_sm *sm, struct wpa_ssid *config); +void eap_sm_request_otp(struct eap_sm *sm, struct wpa_ssid *config, + char *msg, size_t msg_len); +void eap_sm_notify_ctrl_attached(struct eap_sm *sm); +u8 eap_get_type(const char *name); +u8 eap_get_phase2_type(const char *name); +u8 *eap_get_phase2_types(struct wpa_ssid *config, size_t *count); +void eap_set_fast_reauth(struct eap_sm *sm, int enabled); +void eap_set_workaround(struct eap_sm *sm, unsigned int workaround); +struct wpa_ssid * eap_get_config(struct eap_sm *sm); +int eap_key_available(struct eap_sm *sm); +void eap_notify_success(struct eap_sm *sm); +u8 * eap_get_eapKeyData(struct eap_sm *sm, size_t *len); +u8 * eap_get_eapRespData(struct eap_sm *sm, size_t *len); +void eap_register_scard_ctx(struct eap_sm *sm, void *ctx); + +#else /* IEEE8021X_EAPOL */ + +static inline u8 eap_get_type(const char *name) +{ + return EAP_TYPE_NONE; +} + +#endif /* IEEE8021X_EAPOL */ + +#endif /* EAP_H */ diff --git a/contrib/wpa_supplicant/eap_aka.c b/contrib/wpa_supplicant/eap_aka.c new file mode 100644 index 0000000..7fe6449 --- /dev/null +++ b/contrib/wpa_supplicant/eap_aka.c @@ -0,0 +1,915 @@ +/* + * WPA Supplicant / EAP-AKA (draft-arkko-pppext-eap-aka-12.txt) + * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include "common.h" +#include "eap_i.h" +#include "wpa_supplicant.h" +#include "config_ssid.h" +#include "sha1.h" +#include "pcsc_funcs.h" +#include "eap_sim_common.h" + +/* EAP-AKA Subtypes */ +#define EAP_AKA_SUBTYPE_CHALLENGE 1 +#define EAP_AKA_SUBTYPE_AUTHENTICATION_REJECT 2 +#define EAP_AKA_SUBTYPE_SYNCHRONIZATION_FAILURE 4 +#define EAP_AKA_SUBTYPE_IDENTITY 5 +#define EAP_AKA_SUBTYPE_NOTIFICATION 12 +#define EAP_AKA_SUBTYPE_REAUTHENTICATION 13 +#define EAP_AKA_SUBTYPE_CLIENT_ERROR 14 + +/* AT_CLIENT_ERROR_CODE error codes */ +#define EAP_AKA_UNABLE_TO_PROCESS_PACKET 0 + +#define AKA_AUTS_LEN 14 +#define RES_MAX_LEN 16 +#define IK_LEN 16 +#define CK_LEN 16 +#define EAP_AKA_MAX_FAST_REAUTHS 1000 + +struct eap_aka_data { + u8 ik[IK_LEN], ck[CK_LEN], res[RES_MAX_LEN]; + size_t res_len; + u8 nonce_s[EAP_SIM_NONCE_S_LEN]; + u8 mk[EAP_SIM_MK_LEN]; + u8 k_aut[EAP_SIM_K_AUT_LEN]; + u8 k_encr[EAP_SIM_K_ENCR_LEN]; + u8 msk[EAP_SIM_KEYING_DATA_LEN]; + u8 rand[AKA_RAND_LEN], autn[AKA_AUTN_LEN]; + u8 auts[AKA_AUTS_LEN]; + + int num_id_req, num_notification; + u8 *pseudonym; + size_t pseudonym_len; + u8 *reauth_id; + size_t reauth_id_len; + int reauth; + unsigned int counter, counter_too_small; + u8 *last_eap_identity; + size_t last_eap_identity_len; + enum { CONTINUE, SUCCESS, FAILURE } state; +}; + + +static void * eap_aka_init(struct eap_sm *sm) +{ + struct eap_aka_data *data; + data = malloc(sizeof(*data)); + if (data == NULL) + return NULL; + memset(data, 0, sizeof(*data)); + + data->state = CONTINUE; + + return data; +} + + +static void eap_aka_deinit(struct eap_sm *sm, void *priv) +{ + struct eap_aka_data *data = priv; + if (data) { + free(data->pseudonym); + free(data->reauth_id); + free(data->last_eap_identity); + free(data); + } +} + + +static int eap_aka_umts_auth(struct eap_sm *sm, struct eap_aka_data *data) +{ + wpa_printf(MSG_DEBUG, "EAP-AKA: UMTS authentication algorithm"); +#ifdef PCSC_FUNCS + return scard_umts_auth(sm->scard_ctx, data->rand, + data->autn, data->res, &data->res_len, + data->ik, data->ck, data->auts); +#else /* PCSC_FUNCS */ + /* These hardcoded Kc and SRES values are used for testing. + * Could consider making them configurable. */ + memset(data->res, '2', RES_MAX_LEN); + data->res_len = 16; + memset(data->ik, '3', IK_LEN); + memset(data->ck, '4', CK_LEN); + { + u8 autn[AKA_AUTN_LEN]; + memset(autn, '1', AKA_AUTN_LEN); + if (memcmp(autn, data->autn, AKA_AUTN_LEN) != 0) { + wpa_printf(MSG_WARNING, "EAP-AKA: AUTN did not match " + "with expected value"); + return -1; + } + } + return 0; +#endif /* PCSC_FUNCS */ +} + + +static void eap_aka_derive_mk(struct eap_aka_data *data, + const u8 *identity, size_t identity_len) +{ + const u8 *addr[3]; + size_t len[3]; + + addr[0] = identity; + len[0] = identity_len; + addr[1] = data->ik; + len[1] = IK_LEN; + addr[2] = data->ck; + len[2] = CK_LEN; + + /* MK = SHA1(Identity|IK|CK) */ + sha1_vector(3, addr, len, data->mk); + wpa_hexdump_key(MSG_DEBUG, "EAP-AKA: IK", data->ik, IK_LEN); + wpa_hexdump_key(MSG_DEBUG, "EAP-AKA: CK", data->ck, CK_LEN); + wpa_hexdump_key(MSG_DEBUG, "EAP-AKA: MK", data->mk, EAP_SIM_MK_LEN); +} + + +#define CLEAR_PSEUDONYM 0x01 +#define CLEAR_REAUTH_ID 0x02 +#define CLEAR_EAP_ID 0x04 + +static void eap_aka_clear_identities(struct eap_aka_data *data, int id) +{ + wpa_printf(MSG_DEBUG, "EAP-AKA: forgetting old%s%s%s", + id & CLEAR_PSEUDONYM ? " pseudonym" : "", + id & CLEAR_REAUTH_ID ? " reauth_id" : "", + id & CLEAR_EAP_ID ? " eap_id" : ""); + if (id & CLEAR_PSEUDONYM) { + free(data->pseudonym); + data->pseudonym = NULL; + data->pseudonym_len = 0; + } + if (id & CLEAR_REAUTH_ID) { + free(data->reauth_id); + data->reauth_id = NULL; + data->reauth_id_len = 0; + } + if (id & CLEAR_EAP_ID) { + free(data->last_eap_identity); + data->last_eap_identity = NULL; + data->last_eap_identity_len = 0; + } +} + + +static int eap_aka_learn_ids(struct eap_aka_data *data, + struct eap_sim_attrs *attr) +{ + if (attr->next_pseudonym) { + free(data->pseudonym); + data->pseudonym = malloc(attr->next_pseudonym_len); + if (data->pseudonym == NULL) { + wpa_printf(MSG_INFO, "EAP-AKA: (encr) No memory for " + "next pseudonym"); + return -1; + } + memcpy(data->pseudonym, attr->next_pseudonym, + attr->next_pseudonym_len); + data->pseudonym_len = attr->next_pseudonym_len; + wpa_hexdump_ascii(MSG_DEBUG, + "EAP-AKA: (encr) AT_NEXT_PSEUDONYM", + data->pseudonym, + data->pseudonym_len); + } + + if (attr->next_reauth_id) { + free(data->reauth_id); + data->reauth_id = malloc(attr->next_reauth_id_len); + if (data->reauth_id == NULL) { + wpa_printf(MSG_INFO, "EAP-AKA: (encr) No memory for " + "next reauth_id"); + return -1; + } + memcpy(data->reauth_id, attr->next_reauth_id, + attr->next_reauth_id_len); + data->reauth_id_len = attr->next_reauth_id_len; + wpa_hexdump_ascii(MSG_DEBUG, + "EAP-AKA: (encr) AT_NEXT_REAUTH_ID", + data->reauth_id, + data->reauth_id_len); + } + + return 0; +} + + +static u8 * eap_aka_client_error(struct eap_sm *sm, struct eap_aka_data *data, + struct eap_hdr *req, + size_t *respDataLen, int err) +{ + struct eap_sim_msg *msg; + + data->state = FAILURE; + data->num_id_req = 0; + data->num_notification = 0; + + msg = eap_sim_msg_init(EAP_CODE_RESPONSE, req->identifier, + EAP_TYPE_AKA, EAP_AKA_SUBTYPE_CLIENT_ERROR); + eap_sim_msg_add(msg, EAP_SIM_AT_CLIENT_ERROR_CODE, err, NULL, 0); + return eap_sim_msg_finish(msg, respDataLen, NULL, NULL, 0); +} + + +static u8 * eap_aka_authentication_reject(struct eap_sm *sm, + struct eap_aka_data *data, + struct eap_hdr *req, + size_t *respDataLen) +{ + struct eap_sim_msg *msg; + + data->state = FAILURE; + data->num_id_req = 0; + data->num_notification = 0; + + wpa_printf(MSG_DEBUG, "Generating EAP-AKA Authentication-Reject " + "(id=%d)", req->identifier); + msg = eap_sim_msg_init(EAP_CODE_RESPONSE, req->identifier, + EAP_TYPE_AKA, + EAP_AKA_SUBTYPE_AUTHENTICATION_REJECT); + return eap_sim_msg_finish(msg, respDataLen, NULL, NULL, 0); +} + + +static u8 * eap_aka_synchronization_failure(struct eap_sm *sm, + struct eap_aka_data *data, + struct eap_hdr *req, + size_t *respDataLen) +{ + struct eap_sim_msg *msg; + + data->state = FAILURE; + data->num_id_req = 0; + data->num_notification = 0; + + wpa_printf(MSG_DEBUG, "Generating EAP-AKA Synchronization-Failure " + "(id=%d)", req->identifier); + msg = eap_sim_msg_init(EAP_CODE_RESPONSE, req->identifier, + EAP_TYPE_AKA, + EAP_AKA_SUBTYPE_SYNCHRONIZATION_FAILURE); + wpa_printf(MSG_DEBUG, " AT_AUTS"); + eap_sim_msg_add_full(msg, EAP_SIM_AT_AUTS, data->auts, AKA_AUTS_LEN); + return eap_sim_msg_finish(msg, respDataLen, NULL, NULL, 0); +} + + +static u8 * eap_aka_response_identity(struct eap_sm *sm, + struct eap_aka_data *data, + struct eap_hdr *req, + size_t *respDataLen, + enum eap_sim_id_req id_req) +{ + struct wpa_ssid *config = eap_get_config(sm); + u8 *identity = NULL; + size_t identity_len = 0; + struct eap_sim_msg *msg; + + data->reauth = 0; + if (id_req == ANY_ID && data->reauth_id) { + identity = data->reauth_id; + identity_len = data->reauth_id_len; + data->reauth = 1; + } else if ((id_req == ANY_ID || id_req == FULLAUTH_ID) && + data->pseudonym) { + identity = data->pseudonym; + identity_len = data->pseudonym_len; + eap_aka_clear_identities(data, CLEAR_REAUTH_ID); + } else if (id_req != NO_ID_REQ && config && config->identity) { + identity = config->identity; + identity_len = config->identity_len; + eap_aka_clear_identities(data, + CLEAR_PSEUDONYM | CLEAR_REAUTH_ID); + } + if (id_req != NO_ID_REQ) + eap_aka_clear_identities(data, CLEAR_EAP_ID); + + wpa_printf(MSG_DEBUG, "Generating EAP-AKA Identity (id=%d)", + req->identifier); + msg = eap_sim_msg_init(EAP_CODE_RESPONSE, req->identifier, + EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY); + + if (identity) { + wpa_hexdump_ascii(MSG_DEBUG, " AT_IDENTITY", + identity, identity_len); + eap_sim_msg_add(msg, EAP_SIM_AT_IDENTITY, identity_len, + identity, identity_len); + } + + return eap_sim_msg_finish(msg, respDataLen, NULL, NULL, 0); +} + + +static u8 * eap_aka_response_challenge(struct eap_sm *sm, + struct eap_aka_data *data, + struct eap_hdr *req, + size_t *respDataLen) +{ + struct eap_sim_msg *msg; + + wpa_printf(MSG_DEBUG, "Generating EAP-AKA Challenge (id=%d)", + req->identifier); + msg = eap_sim_msg_init(EAP_CODE_RESPONSE, req->identifier, + EAP_TYPE_AKA, EAP_AKA_SUBTYPE_CHALLENGE); + wpa_printf(MSG_DEBUG, " AT_RES"); + eap_sim_msg_add(msg, EAP_SIM_AT_RES, data->res_len, + data->res, data->res_len); + wpa_printf(MSG_DEBUG, " AT_MAC"); + eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC); + return eap_sim_msg_finish(msg, respDataLen, data->k_aut, (u8 *) "", 0); +} + + +static u8 * eap_aka_response_reauth(struct eap_sm *sm, + struct eap_aka_data *data, + struct eap_hdr *req, + size_t *respDataLen, int counter_too_small) +{ + struct eap_sim_msg *msg; + unsigned int counter; + + wpa_printf(MSG_DEBUG, "Generating EAP-AKA Reauthentication (id=%d)", + req->identifier); + msg = eap_sim_msg_init(EAP_CODE_RESPONSE, req->identifier, + EAP_TYPE_AKA, + EAP_AKA_SUBTYPE_REAUTHENTICATION); + wpa_printf(MSG_DEBUG, " AT_IV"); + wpa_printf(MSG_DEBUG, " AT_ENCR_DATA"); + eap_sim_msg_add_encr_start(msg, EAP_SIM_AT_IV, EAP_SIM_AT_ENCR_DATA); + + if (counter_too_small) { + wpa_printf(MSG_DEBUG, " *AT_COUNTER_TOO_SMALL"); + eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER_TOO_SMALL, 0, NULL, 0); + counter = data->counter_too_small; + } else + counter = data->counter; + + wpa_printf(MSG_DEBUG, " *AT_COUNTER %d", counter); + eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER, counter, NULL, 0); + + if (eap_sim_msg_add_encr_end(msg, data->k_encr, EAP_SIM_AT_PADDING)) { + wpa_printf(MSG_WARNING, "EAP-AKA: Failed to encrypt " + "AT_ENCR_DATA"); + eap_sim_msg_free(msg); + return NULL; + } + wpa_printf(MSG_DEBUG, " AT_MAC"); + eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC); + return eap_sim_msg_finish(msg, respDataLen, data->k_aut, data->nonce_s, + EAP_SIM_NONCE_S_LEN); +} + + +static u8 * eap_aka_response_notification(struct eap_sm *sm, + struct eap_aka_data *data, + struct eap_hdr *req, + size_t *respDataLen, + u16 notification) +{ + struct eap_sim_msg *msg; + u8 *k_aut = (notification & 0x4000) == 0 ? data->k_aut : NULL; + + wpa_printf(MSG_DEBUG, "Generating EAP-AKA Notification (id=%d)", + req->identifier); + msg = eap_sim_msg_init(EAP_CODE_RESPONSE, req->identifier, + EAP_TYPE_AKA, EAP_AKA_SUBTYPE_NOTIFICATION); + wpa_printf(MSG_DEBUG, " AT_NOTIFICATION"); + eap_sim_msg_add(msg, EAP_SIM_AT_NOTIFICATION, notification, NULL, 0); + if (k_aut && data->reauth) { + wpa_printf(MSG_DEBUG, " AT_IV"); + wpa_printf(MSG_DEBUG, " AT_ENCR_DATA"); + eap_sim_msg_add_encr_start(msg, EAP_SIM_AT_IV, + EAP_SIM_AT_ENCR_DATA); + wpa_printf(MSG_DEBUG, " *AT_COUNTER %d", data->counter); + eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER, data->counter, + NULL, 0); + if (eap_sim_msg_add_encr_end(msg, data->k_encr, + EAP_SIM_AT_PADDING)) { + wpa_printf(MSG_WARNING, "EAP-AKA: Failed to encrypt " + "AT_ENCR_DATA"); + eap_sim_msg_free(msg); + return NULL; + } + } + if (k_aut) { + wpa_printf(MSG_DEBUG, " AT_MAC"); + eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC); + } + return eap_sim_msg_finish(msg, respDataLen, k_aut, (u8 *) "", 0); +} + + +static u8 * eap_aka_process_identity(struct eap_sm *sm, + struct eap_aka_data *data, + struct eap_hdr *req, size_t reqDataLen, + size_t *respDataLen, + struct eap_sim_attrs *attr) +{ + int id_error; + + wpa_printf(MSG_DEBUG, "EAP-AKA: subtype Identity"); + + id_error = 0; + switch (attr->id_req) { + case NO_ID_REQ: + break; + case ANY_ID: + if (data->num_id_req > 0) + id_error++; + data->num_id_req++; + break; + case FULLAUTH_ID: + if (data->num_id_req > 1) + id_error++; + data->num_id_req++; + break; + case PERMANENT_ID: + if (data->num_id_req > 2) + id_error++; + data->num_id_req++; + break; + } + if (id_error) { + wpa_printf(MSG_INFO, "EAP-AKA: Too many ID requests " + "used within one authentication"); + return eap_aka_client_error(sm, data, req, respDataLen, + EAP_AKA_UNABLE_TO_PROCESS_PACKET); + } + + return eap_aka_response_identity(sm, data, req, respDataLen, + attr->id_req); +} + + +static u8 * eap_aka_process_challenge(struct eap_sm *sm, + struct eap_aka_data *data, + struct eap_hdr *req, size_t reqDataLen, + size_t *respDataLen, + struct eap_sim_attrs *attr) +{ + struct wpa_ssid *config = eap_get_config(sm); + u8 *identity; + size_t identity_len; + int res; + struct eap_sim_attrs eattr; + + wpa_printf(MSG_DEBUG, "EAP-AKA: subtype Challenge"); + data->reauth = 0; + if (!attr->mac || !attr->rand || !attr->autn) { + wpa_printf(MSG_WARNING, "EAP-AKA: Challenge message " + "did not include%s%s%s", + !attr->mac ? " AT_MAC" : "", + !attr->rand ? " AT_RAND" : "", + !attr->autn ? " AT_AUTN" : ""); + return eap_aka_client_error(sm, data, req, respDataLen, + EAP_AKA_UNABLE_TO_PROCESS_PACKET); + } + memcpy(data->rand, attr->rand, AKA_RAND_LEN); + memcpy(data->autn, attr->autn, AKA_AUTN_LEN); + + res = eap_aka_umts_auth(sm, data); + if (res == -1) { + wpa_printf(MSG_WARNING, "EAP-AKA: UMTS authentication " + "failed (AUTN)"); + return eap_aka_authentication_reject(sm, data, req, + respDataLen); + } else if (res == -2) { + wpa_printf(MSG_WARNING, "EAP-AKA: UMTS authentication " + "failed (AUTN seq# -> AUTS)"); + return eap_aka_synchronization_failure(sm, data, req, + respDataLen); + } else if (res) { + wpa_printf(MSG_WARNING, "EAP-AKA: UMTS authentication failed"); + return eap_aka_client_error(sm, data, req, respDataLen, + EAP_AKA_UNABLE_TO_PROCESS_PACKET); + } + if (data->last_eap_identity) { + identity = data->last_eap_identity; + identity_len = data->last_eap_identity_len; + } else if (data->pseudonym) { + identity = data->pseudonym; + identity_len = data->pseudonym_len; + } else { + identity = config->identity; + identity_len = config->identity_len; + } + wpa_hexdump_ascii(MSG_DEBUG, "EAP-AKA: Selected identity for MK " + "derivation", identity, identity_len); + eap_aka_derive_mk(data, identity, identity_len); + eap_sim_derive_keys(data->mk, data->k_encr, data->k_aut, data->msk); + if (eap_sim_verify_mac(data->k_aut, (u8 *) req, reqDataLen, attr->mac, + (u8 *) "", 0)) { + wpa_printf(MSG_WARNING, "EAP-AKA: Challenge message " + "used invalid AT_MAC"); + return eap_aka_client_error(sm, data, req, respDataLen, + EAP_AKA_UNABLE_TO_PROCESS_PACKET); + } + + /* Old reauthentication and pseudonym identities must not be used + * anymore. In other words, if no new identities are received, full + * authentication will be used on next reauthentication. */ + eap_aka_clear_identities(data, CLEAR_PSEUDONYM | CLEAR_REAUTH_ID | + CLEAR_EAP_ID); + + if (attr->encr_data) { + if (eap_sim_parse_encr(data->k_encr, attr->encr_data, + attr->encr_data_len, attr->iv, &eattr, + 0)) { + return eap_aka_client_error( + sm, data, req, respDataLen, + EAP_AKA_UNABLE_TO_PROCESS_PACKET); + } + eap_aka_learn_ids(data, &eattr); + } + + if (data->state != FAILURE) + data->state = SUCCESS; + + data->num_id_req = 0; + data->num_notification = 0; + /* draft-arkko-pppext-eap-aka-12.txt specifies that counter + * is initialized to one after fullauth, but initializing it to + * zero makes it easier to implement reauth verification. */ + data->counter = 0; + return eap_aka_response_challenge(sm, data, req, respDataLen); +} + + +static int eap_aka_process_notification_reauth(struct eap_aka_data *data, + struct eap_hdr *req, + size_t reqDataLen, + struct eap_sim_attrs *attr) +{ + struct eap_sim_attrs eattr; + + if (attr->encr_data == NULL || attr->iv == NULL) { + wpa_printf(MSG_WARNING, "EAP-AKA: Notification message after " + "reauth did not include encrypted data"); + return -1; + } + + if (eap_sim_parse_encr(data->k_encr, attr->encr_data, + attr->encr_data_len, attr->iv, &eattr, 0)) { + wpa_printf(MSG_WARNING, "EAP-AKA: Failed to parse encrypted " + "data from notification message"); + return -1; + } + + if (eattr.counter != data->counter) { + wpa_printf(MSG_WARNING, "EAP-AKA: Counter in notification " + "message does not match with counter in reauth " + "message"); + return -1; + } + + return 0; +} + + +static int eap_aka_process_notification_auth(struct eap_aka_data *data, + struct eap_hdr *req, + size_t reqDataLen, + struct eap_sim_attrs *attr) +{ + if (attr->mac == NULL) { + wpa_printf(MSG_INFO, "EAP-AKA: no AT_MAC in after_auth " + "Notification message"); + return -1; + } + + if (eap_sim_verify_mac(data->k_aut, (u8 *) req, reqDataLen, attr->mac, + (u8 *) "", 0)) { + wpa_printf(MSG_WARNING, "EAP-AKA: Notification message " + "used invalid AT_MAC"); + return -1; + } + + if (data->reauth && + eap_aka_process_notification_reauth(data, req, reqDataLen, attr)) { + wpa_printf(MSG_WARNING, "EAP-AKA: Invalid notification " + "message after reauth"); + return -1; + } + + return 0; +} + + +static u8 * eap_aka_process_notification(struct eap_sm *sm, + struct eap_aka_data *data, + struct eap_hdr *req, + size_t reqDataLen, + size_t *respDataLen, + struct eap_sim_attrs *attr) +{ + wpa_printf(MSG_DEBUG, "EAP-AKA: subtype Notification"); + if (data->num_notification > 0) { + wpa_printf(MSG_INFO, "EAP-AKA: too many notification " + "rounds (only one allowed)"); + return eap_aka_client_error(sm, data, req, respDataLen, + EAP_AKA_UNABLE_TO_PROCESS_PACKET); + } + data->num_notification++; + if (attr->notification == -1) { + wpa_printf(MSG_INFO, "EAP-AKA: no AT_NOTIFICATION in " + "Notification message"); + return eap_aka_client_error(sm, data, req, respDataLen, + EAP_AKA_UNABLE_TO_PROCESS_PACKET); + } + + if ((attr->notification & 0x4000) == 0 && + eap_aka_process_notification_auth(data, req, reqDataLen, attr)) { + return eap_aka_client_error(sm, data, req, respDataLen, + EAP_AKA_UNABLE_TO_PROCESS_PACKET); + } + + eap_sim_report_notification(sm->msg_ctx, attr->notification, 1); + if (attr->notification >= 0 && attr->notification < 32768) { + data->state = FAILURE; + } + return eap_aka_response_notification(sm, data, req, respDataLen, + attr->notification); +} + + +static u8 * eap_aka_process_reauthentication(struct eap_sm *sm, + struct eap_aka_data *data, + struct eap_hdr *req, + size_t reqDataLen, + size_t *respDataLen, + struct eap_sim_attrs *attr) +{ + struct eap_sim_attrs eattr; + + wpa_printf(MSG_DEBUG, "EAP-AKA: subtype Reauthentication"); + + if (data->reauth_id == NULL) { + wpa_printf(MSG_WARNING, "EAP-AKA: Server is trying " + "reauthentication, but no reauth_id available"); + return eap_aka_client_error(sm, data, req, respDataLen, + EAP_AKA_UNABLE_TO_PROCESS_PACKET); + } + + data->reauth = 1; + if (eap_sim_verify_mac(data->k_aut, (u8 *) req, reqDataLen, + attr->mac, (u8 *) "", 0)) { + wpa_printf(MSG_WARNING, "EAP-AKA: Reauthentication " + "did not have valid AT_MAC"); + return eap_aka_client_error(sm, data, req, respDataLen, + EAP_AKA_UNABLE_TO_PROCESS_PACKET); + } + + if (attr->encr_data == NULL || attr->iv == NULL) { + wpa_printf(MSG_WARNING, "EAP-AKA: Reauthentication " + "message did not include encrypted data"); + return eap_aka_client_error(sm, data, req, respDataLen, + EAP_AKA_UNABLE_TO_PROCESS_PACKET); + } + + if (eap_sim_parse_encr(data->k_encr, attr->encr_data, + attr->encr_data_len, attr->iv, &eattr, 0)) { + wpa_printf(MSG_WARNING, "EAP-AKA: Failed to parse encrypted " + "data from reauthentication message"); + return eap_aka_client_error(sm, data, req, respDataLen, + EAP_AKA_UNABLE_TO_PROCESS_PACKET); + } + + if (eattr.nonce_s == NULL || eattr.counter < 0) { + wpa_printf(MSG_INFO, "EAP-AKA: (encr) No%s%s in reauth packet", + !eattr.nonce_s ? " AT_NONCE_S" : "", + eattr.counter < 0 ? " AT_COUNTER" : ""); + return eap_aka_client_error(sm, data, req, respDataLen, + EAP_AKA_UNABLE_TO_PROCESS_PACKET); + } + + if (eattr.counter <= data->counter) { + wpa_printf(MSG_INFO, "EAP-AKA: (encr) Invalid counter " + "(%d <= %d)", eattr.counter, data->counter); + data->counter_too_small = eattr.counter; + /* Reply using Re-auth w/ AT_COUNTER_TOO_SMALL. The current + * reauth_id must not be used to start a new reauthentication. + * However, since it was used in the last EAP-Response-Identity + * packet, it has to saved for the following fullauth to be + * used in MK derivation. */ + free(data->last_eap_identity); + data->last_eap_identity = data->reauth_id; + data->last_eap_identity_len = data->reauth_id_len; + data->reauth_id = NULL; + data->reauth_id_len = 0; + return eap_aka_response_reauth(sm, data, req, respDataLen, 1); + } + data->counter = eattr.counter; + + memcpy(data->nonce_s, eattr.nonce_s, EAP_SIM_NONCE_S_LEN); + wpa_hexdump(MSG_DEBUG, "EAP-AKA: (encr) AT_NONCE_S", + data->nonce_s, EAP_SIM_NONCE_S_LEN); + + eap_sim_derive_keys_reauth(data->counter, + data->reauth_id, data->reauth_id_len, + data->nonce_s, data->mk, data->msk); + eap_aka_clear_identities(data, CLEAR_REAUTH_ID | CLEAR_EAP_ID); + eap_aka_learn_ids(data, &eattr); + + if (data->state != FAILURE) + data->state = SUCCESS; + + data->num_id_req = 0; + data->num_notification = 0; + if (data->counter > EAP_AKA_MAX_FAST_REAUTHS) { + wpa_printf(MSG_DEBUG, "EAP-AKA: Maximum number of " + "fast reauths performed - force fullauth"); + eap_aka_clear_identities(data, CLEAR_REAUTH_ID | CLEAR_EAP_ID); + } + return eap_aka_response_reauth(sm, data, req, respDataLen, 0); +} + + +static u8 * eap_aka_process(struct eap_sm *sm, void *priv, + struct eap_method_ret *ret, + u8 *reqData, size_t reqDataLen, + size_t *respDataLen) +{ + struct eap_aka_data *data = priv; + struct wpa_ssid *config = eap_get_config(sm); + struct eap_hdr *req; + u8 *pos, subtype, *res; + struct eap_sim_attrs attr; + size_t len; + + wpa_hexdump(MSG_DEBUG, "EAP-AKA: EAP data", reqData, reqDataLen); + if (config == NULL || config->identity == NULL) { + wpa_printf(MSG_INFO, "EAP-AKA: Identity not configured"); + eap_sm_request_identity(sm, config); + ret->ignore = TRUE; + return NULL; + } + + req = (struct eap_hdr *) reqData; + pos = (u8 *) (req + 1); + if (reqDataLen < sizeof(*req) + 4 || *pos != EAP_TYPE_AKA || + (len = be_to_host16(req->length)) > reqDataLen) { + wpa_printf(MSG_INFO, "EAP-AKA: Invalid frame"); + ret->ignore = TRUE; + return NULL; + } + + ret->ignore = FALSE; + ret->methodState = METHOD_CONT; + ret->decision = DECISION_FAIL; + ret->allowNotifications = TRUE; + + pos++; + subtype = *pos++; + wpa_printf(MSG_DEBUG, "EAP-AKA: Subtype=%d", subtype); + pos += 2; /* Reserved */ + + if (eap_sim_parse_attr(pos, reqData + len, &attr, 1, 0)) { + res = eap_aka_client_error(sm, data, req, respDataLen, + EAP_AKA_UNABLE_TO_PROCESS_PACKET); + goto done; + } + + switch (subtype) { + case EAP_AKA_SUBTYPE_IDENTITY: + res = eap_aka_process_identity(sm, data, req, len, + respDataLen, &attr); + break; + case EAP_AKA_SUBTYPE_CHALLENGE: + res = eap_aka_process_challenge(sm, data, req, len, + respDataLen, &attr); + break; + case EAP_AKA_SUBTYPE_NOTIFICATION: + res = eap_aka_process_notification(sm, data, req, len, + respDataLen, &attr); + break; + case EAP_AKA_SUBTYPE_REAUTHENTICATION: + res = eap_aka_process_reauthentication(sm, data, req, len, + respDataLen, &attr); + break; + case EAP_AKA_SUBTYPE_CLIENT_ERROR: + wpa_printf(MSG_DEBUG, "EAP-AKA: subtype Client-Error"); + res = eap_aka_client_error(sm, data, req, respDataLen, + EAP_AKA_UNABLE_TO_PROCESS_PACKET); + break; + default: + wpa_printf(MSG_DEBUG, "EAP-AKA: Unknown subtype=%d", subtype); + res = eap_aka_client_error(sm, data, req, respDataLen, + EAP_AKA_UNABLE_TO_PROCESS_PACKET); + break; + } + +done: + if (data->state == FAILURE) { + ret->decision = DECISION_FAIL; + ret->methodState = METHOD_DONE; + } else if (data->state == SUCCESS) { + ret->decision = DECISION_UNCOND_SUCC; + ret->methodState = METHOD_DONE; + } + + if (ret->methodState == METHOD_DONE) { + ret->allowNotifications = FALSE; + } + + return res; +} + + +static Boolean eap_aka_has_reauth_data(struct eap_sm *sm, void *priv) +{ + struct eap_aka_data *data = priv; + return data->pseudonym || data->reauth_id; +} + + +static void eap_aka_deinit_for_reauth(struct eap_sm *sm, void *priv) +{ + struct eap_aka_data *data = priv; + eap_aka_clear_identities(data, CLEAR_EAP_ID); +} + + +static void * eap_aka_init_for_reauth(struct eap_sm *sm, void *priv) +{ + struct eap_aka_data *data = priv; + data->num_id_req = 0; + data->num_notification = 0; + data->state = CONTINUE; + return priv; +} + + +static const u8 * eap_aka_get_identity(struct eap_sm *sm, void *priv, + size_t *len) +{ + struct eap_aka_data *data = priv; + + if (data->reauth_id) { + *len = data->reauth_id_len; + return data->reauth_id; + } + + if (data->pseudonym) { + *len = data->pseudonym_len; + return data->pseudonym; + } + + return NULL; +} + + +static Boolean eap_aka_isKeyAvailable(struct eap_sm *sm, void *priv) +{ + struct eap_aka_data *data = priv; + return data->state == SUCCESS; +} + + +static u8 * eap_aka_getKey(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_aka_data *data = priv; + u8 *key; + + if (data->state != SUCCESS) + return NULL; + + key = malloc(EAP_SIM_KEYING_DATA_LEN); + if (key == NULL) + return NULL; + + *len = EAP_SIM_KEYING_DATA_LEN; + memcpy(key, data->msk, EAP_SIM_KEYING_DATA_LEN); + + return key; +} + + +const struct eap_method eap_method_aka = +{ + .method = EAP_TYPE_AKA, + .name = "AKA", + .init = eap_aka_init, + .deinit = eap_aka_deinit, + .process = eap_aka_process, + .isKeyAvailable = eap_aka_isKeyAvailable, + .getKey = eap_aka_getKey, + .has_reauth_data = eap_aka_has_reauth_data, + .deinit_for_reauth = eap_aka_deinit_for_reauth, + .init_for_reauth = eap_aka_init_for_reauth, + .get_identity = eap_aka_get_identity, +}; diff --git a/contrib/wpa_supplicant/eap_defs.h b/contrib/wpa_supplicant/eap_defs.h new file mode 100644 index 0000000..effe665 --- /dev/null +++ b/contrib/wpa_supplicant/eap_defs.h @@ -0,0 +1,41 @@ +#ifndef EAP_DEFS_H +#define EAP_DEFS_H + +/* RFC 3748 - Extensible Authentication Protocol (EAP) */ + +struct eap_hdr { + u8 code; + u8 identifier; + u16 length; /* including code and identifier */ + /* followed by length-4 octets of data */ +} __attribute__ ((packed)); + +enum { EAP_CODE_REQUEST = 1, EAP_CODE_RESPONSE = 2, EAP_CODE_SUCCESS = 3, + EAP_CODE_FAILURE = 4 }; + +/* EAP Request and Response data begins with one octet Type. Success and + * Failure do not have additional data. */ + +typedef enum { + EAP_TYPE_NONE = 0, + EAP_TYPE_IDENTITY = 1, + EAP_TYPE_NOTIFICATION = 2, + EAP_TYPE_NAK = 3 /* Response only */, + EAP_TYPE_MD5 = 4, + EAP_TYPE_OTP = 5 /* RFC 2284 */, + EAP_TYPE_GTC = 6, /* RFC 2284 */ + EAP_TYPE_TLS = 13 /* RFC 2716 */, + EAP_TYPE_LEAP = 17 /* Cisco proprietary */, + EAP_TYPE_SIM = 18 /* draft-haverinen-pppext-eap-sim-12.txt */, + EAP_TYPE_TTLS = 21 /* draft-ietf-pppext-eap-ttls-02.txt */, + EAP_TYPE_AKA = 23 /* draft-arkko-pppext-eap-aka-12.txt */, + EAP_TYPE_PEAP = 25 /* draft-josefsson-pppext-eap-tls-eap-06.txt */, + EAP_TYPE_MSCHAPV2 = 26 /* draft-kamath-pppext-eap-mschapv2-00.txt */, + EAP_TYPE_TLV = 33 /* draft-josefsson-pppext-eap-tls-eap-07.txt */, + EAP_TYPE_FAST = 43 /* draft-cam-winget-eap-fast-00.txt */, + EAP_TYPE_EXPANDED_NAK = 254 /* RFC 3748 */, + EAP_TYPE_PSK = 255 /* EXPERIMENTAL - type not yet allocated + * draft-bersani-eap-psk-03 */ +} EapType; + +#endif /* EAP_DEFS_H */ diff --git a/contrib/wpa_supplicant/eap_fast.c b/contrib/wpa_supplicant/eap_fast.c new file mode 100644 index 0000000..1c9c3ff --- /dev/null +++ b/contrib/wpa_supplicant/eap_fast.c @@ -0,0 +1,1906 @@ +/* + * WPA Supplicant / EAP-FAST (draft-cam-winget-eap-fast-00.txt) + * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include "common.h" +#include "eap_i.h" +#include "eap_tls_common.h" +#include "wpa_supplicant.h" +#include "config_ssid.h" +#include "tls.h" +#include "eap_tlv.h" +#include "sha1.h" + +/* TODO: + * - encrypt PAC-Key in the PAC file + * - test session resumption and enable it if it interoperates + */ + +#define EAP_FAST_VERSION 1 +#define EAP_FAST_KEY_LEN 64 +#define EAP_FAST_PAC_KEY_LEN 32 + +#define TLS_EXT_PAC_OPAQUE 35 + +static char *pac_file_hdr = "wpa_supplicant EAP-FAST PAC file - version 1"; + + +static void eap_fast_deinit(struct eap_sm *sm, void *priv); + + +#define PAC_TYPE_PAC_KEY 1 +#define PAC_TYPE_PAC_OPAQUE 2 +#define PAC_TYPE_CRED_LIFETIME 3 +#define PAC_TYPE_A_ID 4 +#define PAC_TYPE_I_ID 5 +#define PAC_TYPE_SERVER_PROTECTED_DATA 6 +#define PAC_TYPE_A_ID_INFO 7 +#define PAC_TYPE_PAC_ACKNOWLEDGEMENT 8 +#define PAC_TYPE_PAC_INFO 9 + +struct pac_tlv_hdr { + u16 type; + u16 len; +}; + + +/* draft-cam-winget-eap-fast-00.txt, 6.2 EAP-FAST Authentication Phase 1 */ +struct eap_fast_key_block_auth { + /* RC4-128-SHA */ + u8 client_write_MAC_secret[20]; + u8 server_write_MAC_secret[20]; + u8 client_write_key[16]; + u8 server_write_key[16]; + /* u8 client_write_IV[0]; */ + /* u8 server_write_IV[0]; */ + u8 session_key_seed[40]; +}; + + +/* draft-cam-winget-eap-fast-00.txt, 7.3 EAP-FAST Provisioning Exchange */ +struct eap_fast_key_block_provisioning { + /* AES128-SHA */ + u8 client_write_MAC_secret[20]; + u8 server_write_MAC_secret[20]; + u8 client_write_key[16]; + u8 server_write_key[16]; + u8 client_write_IV[16]; + u8 server_write_IV[16]; + u8 session_key_seed[40]; + u8 server_challenge[16]; + u8 client_challenge[16]; +}; + + +struct eap_fast_pac { + struct eap_fast_pac *next; + + u8 pac_key[EAP_FAST_PAC_KEY_LEN]; + u8 *pac_opaque; + size_t pac_opaque_len; + u8 *pac_info; + size_t pac_info_len; + u8 *a_id; + size_t a_id_len; + u8 *i_id; + size_t i_id_len; + u8 *a_id_info; + size_t a_id_info_len; +}; + + +struct eap_fast_data { + struct eap_ssl_data ssl; + + int fast_version; + + const struct eap_method *phase2_method; + void *phase2_priv; + int phase2_success; + + u8 phase2_type; + u8 *phase2_types; + size_t num_phase2_types; + int resuming; /* starting a resumed session */ + struct eap_fast_key_block_auth *key_block_a; + struct eap_fast_key_block_provisioning *key_block_p; + int provisioning_allowed; /* is PAC provisioning allowed */ + int provisioning; /* doing PAC provisioning (not the normal auth) */ + + u8 key_data[EAP_FAST_KEY_LEN]; + int success; + + struct eap_fast_pac *pac; + struct eap_fast_pac *current_pac; + + int tls_master_secret_set; +}; + + +static void eap_fast_free_pac(struct eap_fast_pac *pac) +{ + free(pac->pac_opaque); + free(pac->pac_info); + free(pac->a_id); + free(pac->i_id); + free(pac->a_id_info); + free(pac); +} + + +static struct eap_fast_pac * eap_fast_get_pac(struct eap_fast_data *data, + const u8 *a_id, size_t a_id_len) +{ + struct eap_fast_pac *pac = data->pac; + + while (pac) { + if (pac->a_id_len == a_id_len && + memcmp(pac->a_id, a_id, a_id_len) == 0) { + return pac; + } + pac = pac->next; + } + return NULL; +} + + +static int eap_fast_add_pac(struct eap_fast_data *data, + struct eap_fast_pac *entry) +{ + struct eap_fast_pac *pac, *prev; + + if (entry == NULL || entry->a_id == NULL) + return -1; + + /* Remove a possible old entry for the matching A-ID. */ + pac = data->pac; + prev = NULL; + while (pac) { + if (pac->a_id_len == entry->a_id_len && + memcmp(pac->a_id, entry->a_id, pac->a_id_len) == 0) { + if (prev == NULL) { + data->pac = pac->next; + } else { + prev->next = pac->next; + } + if (data->current_pac == pac) + data->current_pac = NULL; + eap_fast_free_pac(pac); + break; + } + prev = pac; + pac = pac->next; + } + + /* Allocate a new entry and add it to the list of PACs. */ + pac = malloc(sizeof(*pac)); + if (pac == NULL) + return -1; + + memset(pac, 0, sizeof(*pac)); + memcpy(pac->pac_key, entry->pac_key, EAP_FAST_PAC_KEY_LEN); + if (entry->pac_opaque) { + pac->pac_opaque = malloc(entry->pac_opaque_len); + if (pac->pac_opaque == NULL) { + eap_fast_free_pac(pac); + return -1; + } + memcpy(pac->pac_opaque, entry->pac_opaque, + entry->pac_opaque_len); + pac->pac_opaque_len = entry->pac_opaque_len; + } + if (entry->pac_info) { + pac->pac_info = malloc(entry->pac_info_len); + if (pac->pac_info == NULL) { + eap_fast_free_pac(pac); + return -1; + } + memcpy(pac->pac_info, entry->pac_info, + entry->pac_info_len); + pac->pac_info_len = entry->pac_info_len; + } + if (entry->a_id) { + pac->a_id = malloc(entry->a_id_len); + if (pac->a_id == NULL) { + eap_fast_free_pac(pac); + return -1; + } + memcpy(pac->a_id, entry->a_id, + entry->a_id_len); + pac->a_id_len = entry->a_id_len; + } + if (entry->i_id) { + pac->i_id = malloc(entry->i_id_len); + if (pac->i_id == NULL) { + eap_fast_free_pac(pac); + return -1; + } + memcpy(pac->i_id, entry->i_id, + entry->i_id_len); + pac->i_id_len = entry->i_id_len; + } + if (entry->a_id_info) { + pac->a_id_info = malloc(entry->a_id_info_len); + if (pac->a_id_info == NULL) { + eap_fast_free_pac(pac); + return -1; + } + memcpy(pac->a_id_info, entry->a_id_info, + entry->a_id_info_len); + pac->a_id_info_len = entry->a_id_info_len; + } + pac->next = data->pac; + data->pac = pac; + return 0; +} + + +static int eap_fast_read_line(FILE *f, char *buf, size_t buf_len) +{ + char *pos; + + if (fgets(buf, buf_len, f) == NULL) { + return -1; + } + + buf[buf_len - 1] = '\0'; + pos = buf; + while (*pos != '\0') { + if (*pos == '\n' || *pos == '\r') { + *pos = '\0'; + break; + } + pos++; + } + + return 0; +} + + +static u8 * eap_fast_parse_hex(const char *value, size_t *len) +{ + int hlen; + u8 *buf; + + if (value == NULL) + return NULL; + hlen = strlen(value); + if (hlen & 1) + return NULL; + *len = hlen / 2; + buf = malloc(*len); + if (buf == NULL) + return NULL; + if (hexstr2bin(value, buf, *len)) { + free(buf); + return NULL; + } + return buf; +} + + +static int eap_fast_load_pac(struct eap_fast_data *data, const char *pac_file) +{ + FILE *f; + struct eap_fast_pac *pac = NULL; + int count = 0; + char *buf, *pos; + const int buf_len = 2048; + int ret = 0, line = 0; + + if (pac_file == NULL) + return -1; + + f = fopen(pac_file, "r"); + if (f == NULL) { + wpa_printf(MSG_INFO, "EAP-FAST: No PAC file '%s' - assume no " + "PAC entries have been provisioned", pac_file); + return 0; + } + + buf = malloc(buf_len); + if (buf == NULL) { + return -1; + } + + line++; + if (eap_fast_read_line(f, buf, buf_len) < 0 || + strcmp(pac_file_hdr, buf) != 0) { + wpa_printf(MSG_INFO, "EAP-FAST: Unrecognized header line in " + "PAC file '%s'", pac_file); + free(buf); + fclose(f); + return -1; + } + + while (eap_fast_read_line(f, buf, buf_len) == 0) { + line++; + pos = strchr(buf, '='); + if (pos) { + *pos++ = '\0'; + } + + if (strcmp(buf, "START") == 0) { + if (pac) { + wpa_printf(MSG_INFO, "EAP-FAST: START line " + "without END in '%s:%d'", + pac_file, line); + ret = -1; + break; + } + pac = malloc(sizeof(*pac)); + if (pac == NULL) { + wpa_printf(MSG_INFO, "EAP-FAST: No memory for " + "PAC entry"); + ret = -1; + break; + } + memset(pac, 0, sizeof(*pac)); + } else if (strcmp(buf, "END") == 0) { + if (pac == NULL) { + wpa_printf(MSG_INFO, "EAP-FAST: END line " + "without START in '%s:%d'", + pac_file, line); + ret = -1; + break; + } + pac->next = data->pac; + data->pac = pac; + pac = NULL; + count++; + } else if (pac && strcmp(buf, "PAC-Key") == 0) { + u8 *key; + size_t key_len; + key = eap_fast_parse_hex(pos, &key_len); + if (key == NULL || key_len != EAP_FAST_PAC_KEY_LEN) { + wpa_printf(MSG_INFO, "EAP-FAST: Invalid " + "PAC-Key '%s:%d'", pac_file, line); + ret = -1; + free(key); + break; + } + + memcpy(pac->pac_key, key, EAP_FAST_PAC_KEY_LEN); + free(key); + } else if (pac && strcmp(buf, "PAC-Opaque") == 0) { + pac->pac_opaque = + eap_fast_parse_hex(pos, &pac->pac_opaque_len); + if (pac->pac_opaque == NULL) { + wpa_printf(MSG_INFO, "EAP-FAST: Invalid " + "PAC-Opaque '%s:%d'", + pac_file, line); + ret = -1; + break; + } + } else if (pac && strcmp(buf, "A-ID") == 0) { + pac->a_id = eap_fast_parse_hex(pos, &pac->a_id_len); + if (pac->a_id == NULL) { + wpa_printf(MSG_INFO, "EAP-FAST: Invalid " + "A-ID '%s:%d'", pac_file, line); + ret = -1; + break; + } + } else if (pac && strcmp(buf, "I-ID") == 0) { + pac->i_id = eap_fast_parse_hex(pos, &pac->i_id_len); + if (pac->i_id == NULL) { + wpa_printf(MSG_INFO, "EAP-FAST: Invalid " + "I-ID '%s:%d'", pac_file, line); + ret = -1; + break; + } + } else if (pac && strcmp(buf, "A-ID-Info") == 0) { + pac->a_id_info = + eap_fast_parse_hex(pos, &pac->a_id_info_len); + if (pac->a_id_info == NULL) { + wpa_printf(MSG_INFO, "EAP-FAST: Invalid " + "A-ID-Info '%s:%d'", + pac_file, line); + ret = -1; + break; + } + } + } + + if (pac) { + wpa_printf(MSG_INFO, "EAP-FAST: PAC block not terminated with " + "END in '%s'", pac_file); + eap_fast_free_pac(pac); + ret = -1; + } + + free(buf); + fclose(f); + + if (ret == 0) { + wpa_printf(MSG_DEBUG, "EAP-FAST: read %d PAC entries from " + "'%s'", count, pac_file); + } + + return ret; +} + + +static void eap_fast_write(FILE *f, const char *field, const u8 *data, + size_t len, int txt) +{ + int i; + + if (data == NULL) + return; + + fprintf(f, "%s=", field); + for (i = 0; i < len; i++) { + fprintf(f, "%02x", data[i]); + } + fprintf(f, "\n"); + + if (txt) { + fprintf(f, "%s-txt=", field); + for (i = 0; i < len; i++) { + fprintf(f, "%c", data[i]); + } + fprintf(f, "\n"); + } +} + + +static int eap_fast_save_pac(struct eap_fast_data *data, const char *pac_file) +{ + FILE *f; + struct eap_fast_pac *pac; + int count = 0; + + if (pac_file == NULL) + return -1; + + f = fopen(pac_file, "w"); + if (f == NULL) { + wpa_printf(MSG_INFO, "EAP-FAST: Failed to open PAC file '%s' " + "for writing", pac_file); + return -1; + } + + fprintf(f, "%s\n", pac_file_hdr); + + pac = data->pac; + while (pac) { + fprintf(f, "START\n"); + eap_fast_write(f, "PAC-Key", pac->pac_key, + EAP_FAST_PAC_KEY_LEN, 0); + eap_fast_write(f, "PAC-Opaque", pac->pac_opaque, + pac->pac_opaque_len, 0); + eap_fast_write(f, "PAC-Info", pac->pac_info, + pac->pac_info_len, 0); + eap_fast_write(f, "A-ID", pac->a_id, pac->a_id_len, 0); + eap_fast_write(f, "I-ID", pac->i_id, pac->i_id_len, 1); + eap_fast_write(f, "A-ID-Info", pac->a_id_info, + pac->a_id_info_len, 1); + fprintf(f, "END\n"); + count++; + pac = pac->next; + } + + fclose(f); + + wpa_printf(MSG_DEBUG, "EAP-FAST: wrote %d PAC entries into '%s'", + count, pac_file); + + return 0; +} + + +static void * eap_fast_init(struct eap_sm *sm) +{ + struct eap_fast_data *data; + struct wpa_ssid *config = eap_get_config(sm); + + data = malloc(sizeof(*data)); + if (data == NULL) + return NULL; + memset(data, 0, sizeof(*data)); + data->fast_version = EAP_FAST_VERSION; + + if (config && config->phase1) { + if (strstr(config->phase1, "fast_provisioning=1")) { + data->provisioning_allowed = 1; + wpa_printf(MSG_DEBUG, "EAP-FAST: Automatic PAC " + "provisioning is allowed"); + } + } + + if (config && config->phase2) { + char *start, *pos, *buf; + u8 method, *methods = NULL, *_methods; + size_t num_methods = 0; + start = buf = strdup(config->phase2); + if (buf == NULL) { + eap_fast_deinit(sm, data); + return NULL; + } + while (start && *start != '\0') { + pos = strstr(start, "auth="); + if (pos == NULL) + break; + if (start != pos && *(pos - 1) != ' ') { + start = pos + 5; + continue; + } + + start = pos + 5; + pos = strchr(start, ' '); + if (pos) + *pos++ = '\0'; + method = eap_get_phase2_type(start); + if (method == EAP_TYPE_NONE) { + wpa_printf(MSG_ERROR, "EAP-FAST: Unsupported " + "Phase2 method '%s'", start); + } else { + num_methods++; + _methods = realloc(methods, num_methods); + if (_methods == NULL) { + free(methods); + eap_fast_deinit(sm, data); + return NULL; + } + methods = _methods; + methods[num_methods - 1] = method; + } + + start = pos; + } + free(buf); + data->phase2_types = methods; + data->num_phase2_types = num_methods; + } + if (data->phase2_types == NULL) { + data->phase2_types = + eap_get_phase2_types(config, &data->num_phase2_types); + } + if (data->phase2_types == NULL) { + wpa_printf(MSG_ERROR, "EAP-FAST: No Phase2 method available"); + eap_fast_deinit(sm, data); + return NULL; + } + wpa_hexdump(MSG_DEBUG, "EAP-FAST: Phase2 EAP types", + data->phase2_types, data->num_phase2_types); + data->phase2_type = EAP_TYPE_NONE; + + if (eap_tls_ssl_init(sm, &data->ssl, config)) { + wpa_printf(MSG_INFO, "EAP-FAST: Failed to initialize SSL."); + eap_fast_deinit(sm, data); + return NULL; + } + + /* The local RADIUS server in a Cisco AP does not seem to like empty + * fragments before data, so disable that workaround for CBC. + * TODO: consider making this configurable */ + tls_connection_enable_workaround(sm->ssl_ctx, data->ssl.conn); + + if (eap_fast_load_pac(data, config->pac_file) < 0) { + eap_fast_deinit(sm, data); + return NULL; + } + + if (data->pac == NULL && !data->provisioning_allowed) { + wpa_printf(MSG_INFO, "EAP-FAST: No PAC configured and " + "provisioning disabled"); + eap_fast_deinit(sm, data); + return NULL; + } + + return data; +} + + +static void eap_fast_deinit(struct eap_sm *sm, void *priv) +{ + struct eap_fast_data *data = priv; + struct eap_fast_pac *pac, *prev; + + if (data == NULL) + return; + if (data->phase2_priv && data->phase2_method) + data->phase2_method->deinit(sm, data->phase2_priv); + free(data->phase2_types); + free(data->key_block_a); + free(data->key_block_p); + eap_tls_ssl_deinit(sm, &data->ssl); + + pac = data->pac; + prev = NULL; + while (pac) { + prev = pac; + pac = pac->next; + eap_fast_free_pac(prev); + } + free(data); +} + + +static int eap_fast_encrypt(struct eap_sm *sm, struct eap_fast_data *data, + int id, u8 *plain, size_t plain_len, + u8 **out_data, size_t *out_len) +{ + int res; + u8 *pos; + struct eap_hdr *resp; + + /* TODO: add support for fragmentation, if needed. This will need to + * add TLS Message Length field, if the frame is fragmented. */ + resp = malloc(sizeof(struct eap_hdr) + 2 + data->ssl.tls_out_limit); + if (resp == NULL) + return 0; + + resp->code = EAP_CODE_RESPONSE; + resp->identifier = id; + + pos = (u8 *) (resp + 1); + *pos++ = EAP_TYPE_FAST; + *pos++ = data->fast_version; + + res = tls_connection_encrypt(sm->ssl_ctx, data->ssl.conn, + plain, plain_len, + pos, data->ssl.tls_out_limit); + if (res < 0) { + wpa_printf(MSG_INFO, "EAP-FAST: Failed to encrypt Phase 2 " + "data"); + free(resp); + return 0; + } + + *out_len = sizeof(struct eap_hdr) + 2 + res; + resp->length = host_to_be16(*out_len); + *out_data = (u8 *) resp; + return 0; +} + + +static int eap_fast_phase2_nak(struct eap_sm *sm, + struct eap_fast_data *data, + struct eap_hdr *hdr, + u8 **resp, size_t *resp_len) +{ + struct eap_hdr *resp_hdr; + u8 *pos = (u8 *) (hdr + 1); + + wpa_printf(MSG_DEBUG, "EAP-FAST: Phase 2 Request: Nak type=%d", *pos); + wpa_hexdump(MSG_DEBUG, "EAP-FAST: Allowed Phase2 EAP types", + data->phase2_types, data->num_phase2_types); + *resp_len = sizeof(struct eap_hdr) + 1 + data->num_phase2_types; + *resp = malloc(*resp_len); + if (*resp == NULL) + return -1; + + resp_hdr = (struct eap_hdr *) (*resp); + resp_hdr->code = EAP_CODE_RESPONSE; + resp_hdr->identifier = hdr->identifier; + resp_hdr->length = host_to_be16(*resp_len); + pos = (u8 *) (resp_hdr + 1); + *pos++ = EAP_TYPE_NAK; + memcpy(pos, data->phase2_types, data->num_phase2_types); + + return 0; +} + + +static int eap_fast_derive_msk(struct eap_sm *sm, struct eap_fast_data *data) +{ + u8 isk[32]; + u8 imck[60]; + + if (data->key_block_a == NULL) + return -1; + + memset(isk, 0, sizeof(isk)); + sha1_t_prf(data->key_block_a->session_key_seed, + sizeof(data->key_block_a->session_key_seed), + "Inner Methods Compound Keys", + isk, sizeof(isk), imck, sizeof(imck)); + sha1_t_prf(imck, 40, "Session Key Generating Function", (u8 *) "", 0, + data->key_data, EAP_FAST_KEY_LEN); + + wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: Derived key (MSK)", + data->key_data, EAP_FAST_KEY_LEN); + + data->success = 1; + + return 0; +} + + +static int eap_fast_set_tls_master_secret(struct eap_sm *sm, + struct eap_fast_data *data, + const u8 *tls, size_t tls_len) +{ + struct tls_keys keys; + u8 master_secret[48], *seed; + const u8 *server_random; + size_t seed_len, server_random_len; + + if (data->tls_master_secret_set || !data->current_pac || + tls_connection_get_keys(sm->ssl_ctx, data->ssl.conn, &keys)) { + return 0; + } + + wpa_hexdump(MSG_DEBUG, "EAP-FAST: client_random", + keys.client_random, keys.client_random_len); + + /* TLS master secret is needed before TLS library has processed this + * message which includes both ServerHello and an encrypted handshake + * message, so we need to parse server_random from this message before + * passing it to TLS library. + * + * Example TLS packet header: + * (16 03 01 00 2a 02 00 00 26 03 01 <32 bytes server_random>) + * Content Type: Handshake: 0x16 + * Version: TLS 1.0 (0x0301) + * Lenghth: 42 (0x002a) + * Handshake Type: Server Hello: 0x02 + * Length: 38 (0x000026) + * Version TLS 1.0 (0x0301) + * Random: 32 bytes + */ + if (tls_len < 43 || tls[0] != 0x16 || + tls[1] != 0x03 || tls[2] != 0x01 || + tls[5] != 0x02 || tls[9] != 0x03 || tls[10] != 0x01) { + wpa_hexdump(MSG_DEBUG, "EAP-FAST: unrecognized TLS " + "ServerHello", tls, tls_len); + return -1; + } + server_random = tls + 11; + server_random_len = 32; + wpa_hexdump(MSG_DEBUG, "EAP-FAST: server_random", + server_random, server_random_len); + + + seed_len = keys.client_random_len + server_random_len; + seed = malloc(seed_len); + if (seed == NULL) + return -1; + memcpy(seed, server_random, server_random_len); + memcpy(seed + server_random_len, + keys.client_random, keys.client_random_len); + + wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: T-PRF seed", seed, seed_len); + wpa_hexdump_key(MSG_MSGDUMP, "EAP-FAST: PAC-Key", + data->current_pac->pac_key, EAP_FAST_PAC_KEY_LEN); + /* master_secret = T-PRF(PAC-Key, "PAC to master secret label hash", + * server_random + client_random, 48) */ + sha1_t_prf(data->current_pac->pac_key, EAP_FAST_PAC_KEY_LEN, + "PAC to master secret label hash", + seed, seed_len, master_secret, sizeof(master_secret)); + free(seed); + wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: TLS pre-master-secret", + master_secret, sizeof(master_secret)); + + data->tls_master_secret_set = 1; + + return tls_connection_set_master_key(sm->ssl_ctx, data->ssl.conn, + master_secret, + sizeof(master_secret)); +} + + +static u8 * eap_fast_derive_key(struct eap_sm *sm, struct eap_ssl_data *data, + char *label, size_t len) +{ + struct tls_keys keys; + u8 *random; + u8 *out; + + if (tls_connection_get_keys(sm->ssl_ctx, data->conn, &keys)) + return NULL; + out = malloc(len); + random = malloc(keys.client_random_len + keys.server_random_len); + if (out == NULL || random == NULL) { + free(out); + free(random); + return NULL; + } + memcpy(random, keys.server_random, keys.server_random_len); + memcpy(random + keys.server_random_len, keys.client_random, + keys.client_random_len); + + wpa_hexdump_key(MSG_MSGDUMP, "EAP-FAST: master_secret for key " + "expansion", keys.master_key, keys.master_key_len); + if (tls_prf(keys.master_key, keys.master_key_len, + label, random, keys.client_random_len + + keys.server_random_len, out, len)) { + free(random); + free(out); + return NULL; + } + free(random); + return out; +} + + +static void eap_fast_derive_key_auth(struct eap_sm *sm, + struct eap_fast_data *data) +{ + free(data->key_block_a); + data->key_block_a = (struct eap_fast_key_block_auth *) + eap_fast_derive_key(sm, &data->ssl, "key expansion", + sizeof(*data->key_block_a)); + wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: session_key_seed", + data->key_block_a->session_key_seed, + sizeof(data->key_block_a->session_key_seed)); +} + + +static void eap_fast_derive_key_provisioning(struct eap_sm *sm, + struct eap_fast_data *data) +{ + free(data->key_block_p); + data->key_block_p = (struct eap_fast_key_block_provisioning *) + eap_fast_derive_key(sm, &data->ssl, "key expansion", + sizeof(*data->key_block_p)); + if (data->key_block_p == NULL) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to derive key block"); + return; + } + wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: session_key_seed", + data->key_block_p->session_key_seed, + sizeof(data->key_block_p->session_key_seed)); + wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: server_challenge", + data->key_block_p->server_challenge, + sizeof(data->key_block_p->server_challenge)); + wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: client_challenge", + data->key_block_p->client_challenge, + sizeof(data->key_block_p->client_challenge)); +} + + +static void eap_fast_derive_keys(struct eap_sm *sm, struct eap_fast_data *data) +{ + if (data->current_pac) { + eap_fast_derive_key_auth(sm, data); + } else { + eap_fast_derive_key_provisioning(sm, data); + } +} + + +static int eap_fast_phase2_request(struct eap_sm *sm, + struct eap_fast_data *data, + struct eap_method_ret *ret, + struct eap_hdr *req, + struct eap_hdr *hdr, + u8 **resp, size_t *resp_len) +{ + size_t len = be_to_host16(hdr->length); + u8 *pos; + struct eap_method_ret iret; + + if (len <= sizeof(struct eap_hdr)) { + wpa_printf(MSG_INFO, "EAP-FAST: too short " + "Phase 2 request (len=%lu)", (unsigned long) len); + return -1; + } + pos = (u8 *) (hdr + 1); + wpa_printf(MSG_DEBUG, "EAP-FAST: Phase 2 Request: type=%d", *pos); + switch (*pos) { + case EAP_TYPE_IDENTITY: + *resp = eap_sm_buildIdentity(sm, req->identifier, resp_len, 1); + break; + default: + if (data->phase2_type == EAP_TYPE_NONE) { + int i; + for (i = 0; i < data->num_phase2_types; i++) { + if (data->phase2_types[i] != *pos) + continue; + + data->phase2_type = *pos; + wpa_printf(MSG_DEBUG, "EAP-FAST: Selected " + "Phase 2 EAP method %d", + data->phase2_type); + break; + } + } + if (*pos != data->phase2_type || *pos == EAP_TYPE_NONE) { + if (eap_fast_phase2_nak(sm, data, hdr, resp, resp_len)) + return -1; + return 0; + } + + if (data->phase2_priv == NULL) { + data->phase2_method = eap_sm_get_eap_methods(*pos); + if (data->phase2_method) { + if (data->key_block_p) { + sm->auth_challenge = + data->key_block_p-> + server_challenge; + sm->peer_challenge = + data->key_block_p-> + client_challenge; + } + sm->init_phase2 = 1; + data->phase2_priv = + data->phase2_method->init(sm); + sm->init_phase2 = 0; + sm->auth_challenge = NULL; + sm->peer_challenge = NULL; + } + } + if (data->phase2_priv == NULL || data->phase2_method == NULL) { + wpa_printf(MSG_INFO, "EAP-FAST: failed to initialize " + "Phase 2 EAP method %d", *pos); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + return -1; + } + memset(&iret, 0, sizeof(iret)); + *resp = data->phase2_method->process(sm, data->phase2_priv, + &iret, (u8 *) hdr, len, + resp_len); + if (*resp == NULL || + (iret.methodState == METHOD_DONE && + iret.decision == DECISION_FAIL)) { + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + } else if ((iret.methodState == METHOD_DONE || + iret.methodState == METHOD_MAY_CONT) && + (iret.decision == DECISION_UNCOND_SUCC || + iret.decision == DECISION_COND_SUCC)) { + data->phase2_success = 1; + } + if (*resp == NULL) + return -1; + break; + } + return 0; +} + + +static u8 * eap_fast_tlv_nak(int vendor_id, int tlv_type, size_t *len) +{ + struct eap_tlv_nak_tlv *nak; + *len = sizeof(*nak); + nak = malloc(*len); + if (nak == NULL) + return NULL; + nak->tlv_type = host_to_be16(EAP_TLV_TYPE_MANDATORY | EAP_TLV_NAK_TLV); + nak->length = host_to_be16(6); + nak->vendor_id = host_to_be32(vendor_id); + nak->nak_type = host_to_be16(tlv_type); + return (u8 *) nak; +} + + +static u8 * eap_fast_tlv_result(int status, int intermediate, size_t *len) +{ + struct eap_tlv_intermediate_result_tlv *result; + *len = sizeof(*result); + result = malloc(*len); + if (result == NULL) + return NULL; + result->tlv_type = host_to_be16(EAP_TLV_TYPE_MANDATORY | + (intermediate ? + EAP_TLV_INTERMEDIATE_RESULT_TLV : + EAP_TLV_RESULT_TLV)); + result->length = host_to_be16(2); + result->status = host_to_be16(status); + return (u8 *) result; +} + + +static u8 * eap_fast_tlv_pac_ack(size_t *len) +{ + struct eap_tlv_result_tlv *res; + struct eap_tlv_pac_ack_tlv *ack; + + *len = sizeof(*res) + sizeof(*ack); + res = malloc(*len); + if (res == NULL) + return NULL; + + memset(res, 0, *len); + res->tlv_type = host_to_be16(EAP_TLV_RESULT_TLV | + EAP_TLV_TYPE_MANDATORY); + res->length = host_to_be16(sizeof(*res) - sizeof(struct eap_tlv_hdr)); + res->status = host_to_be16(EAP_TLV_RESULT_SUCCESS); + + ack = (struct eap_tlv_pac_ack_tlv *) (res + 1); + ack->tlv_type = host_to_be16(EAP_TLV_PAC_TLV | + EAP_TLV_TYPE_MANDATORY); + ack->length = host_to_be16(sizeof(*ack) - sizeof(struct eap_tlv_hdr)); + ack->pac_type = host_to_be16(PAC_TYPE_PAC_ACKNOWLEDGEMENT); + ack->pac_len = host_to_be16(2); + ack->result = host_to_be16(EAP_TLV_RESULT_SUCCESS); + + return (u8 *) res; +} + + +static u8 * eap_fast_tlv_eap_payload(u8 *buf, size_t *len) +{ + struct eap_tlv_hdr *tlv; + + /* Encapsulate EAP packet in EAP Payload TLV */ + tlv = malloc(sizeof(*tlv) + *len); + if (tlv == NULL) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to " + "allocate memory for TLV " + "encapsulation"); + free(buf); + return NULL; + } + tlv->tlv_type = host_to_be16(EAP_TLV_TYPE_MANDATORY | + EAP_TLV_EAP_PAYLOAD_TLV); + tlv->length = host_to_be16(*len); + memcpy(tlv + 1, buf, *len); + free(buf); + *len += sizeof(*tlv); + return (u8 *) tlv; +} + + +static u8 * eap_fast_process_crypto_binding( + struct eap_sm *sm, struct eap_fast_data *data, + struct eap_method_ret *ret, + struct eap_tlv_crypto_binding__tlv *bind, size_t bind_len, + size_t *resp_len, int final) +{ + u8 *resp, *sks = NULL; + struct eap_tlv_intermediate_result_tlv *rresult; + struct eap_tlv_crypto_binding__tlv *rbind; + u8 isk[32], imck[60], *cmk, cmac[20], *key; + size_t key_len; + int res; + + wpa_printf(MSG_DEBUG, "EAP-FAST: Crypto-Binding TLV: Version %d " + "Received Version %d SubType %d", + bind->version, bind->received_version, bind->subtype); + wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: NONCE", + bind->nonce, sizeof(bind->nonce)); + wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Compound MAC", + bind->compound_mac, sizeof(bind->compound_mac)); + + if (bind->version != EAP_FAST_VERSION || + bind->received_version != EAP_FAST_VERSION || + bind->subtype != EAP_TLV_CRYPTO_BINDING_SUBTYPE_REQUEST) { + wpa_printf(MSG_INFO, "EAP-FAST: Invalid version/subtype in " + "Crypto-Binding TLV: Version %d " + "Received Version %d SubType %d", + bind->version, bind->received_version, + bind->subtype); + resp = eap_fast_tlv_result(EAP_TLV_RESULT_FAILURE, 1, + resp_len); + return resp; + } + + + if (data->provisioning) { + if (data->key_block_p) { + sks = data->key_block_p->session_key_seed; + } + } else { + if (data->key_block_a) { + sks = data->key_block_a->session_key_seed; + } + } + if (sks == NULL) { + wpa_printf(MSG_INFO, "EAP-FAST: No Session Key Seed available " + "for processing Crypto-Binding TLV"); + return NULL; + } + + wpa_printf(MSG_DEBUG, "EAP-FAST: Determining CMK for Compound MIC " + "calculation"); + wpa_hexdump_key(MSG_MSGDUMP, "EAP-FAST: S-IMCK[0] = SKS", sks, 40); + + memset(isk, 0, sizeof(isk)); + if (data->phase2_method == NULL || data->phase2_priv == NULL) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Phase 2 method not " + "available"); + return NULL; + } + if (data->phase2_method->isKeyAvailable && data->phase2_method->getKey) + { + if (!data->phase2_method->isKeyAvailable(sm, data->phase2_priv) + || + (key = data->phase2_method->getKey(sm, data->phase2_priv, + &key_len)) == NULL) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Could not get key " + "material from Phase 2"); + return NULL; + } + if (key_len > sizeof(isk)) + key_len = sizeof(isk); + /* FIX: which end is being padded? */ +#if 0 + memcpy(isk + (sizeof(isk) - key_len), key, key_len); +#else + memcpy(isk, key, key_len); +#endif + free(key); + } + wpa_hexdump_key(MSG_MSGDUMP, "EAP-FAST: ISK[0]", isk, sizeof(isk)); + sha1_t_prf(sks, 40, "Inner Methods Compound Keys", + isk, sizeof(isk), imck, sizeof(imck)); + /* S-IMCK[1] = imkc[0..39] */ + wpa_hexdump_key(MSG_MSGDUMP, "EAP-FAST: S-IMCK[1]", imck, 40); + cmk = imck + 40; + wpa_hexdump_key(MSG_MSGDUMP, "EAP-FAST: CMK", cmk, 20); + + memcpy(cmac, bind->compound_mac, sizeof(cmac)); + memset(bind->compound_mac, 0, sizeof(cmac)); + wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Crypto-Binding TLV for Compound " + "MAC calculation", (u8 *) bind, bind_len); + hmac_sha1(cmk, 20, (u8 *) bind, bind_len, bind->compound_mac); + res = memcmp(cmac, bind->compound_mac, sizeof(cmac)); + wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Received Compound MAC", + cmac, sizeof(cmac)); + wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Calculated Compound MAC", + bind->compound_mac, sizeof(cmac)); + if (res != 0) { + wpa_printf(MSG_INFO, "EAP-FAST: Compound MAC did not match"); + resp = eap_fast_tlv_result(EAP_TLV_RESULT_FAILURE, 1, + resp_len); + memcpy(bind->compound_mac, cmac, sizeof(cmac)); + return resp; + } + + *resp_len = sizeof(*rresult) + sizeof(*rbind); + resp = malloc(*resp_len); + if (resp == NULL) + return NULL; + memset(resp, 0, *resp_len); + + /* Both intermediate and final Result TLVs are identical, so ok to use + * the same structure definition for them. */ + rresult = (struct eap_tlv_intermediate_result_tlv *) resp; + rresult->tlv_type = host_to_be16(EAP_TLV_TYPE_MANDATORY | + (final ? EAP_TLV_RESULT_TLV : + EAP_TLV_INTERMEDIATE_RESULT_TLV)); + rresult->length = host_to_be16(2); + rresult->status = host_to_be16(EAP_TLV_RESULT_SUCCESS); + + if (!data->provisioning && data->phase2_success && + eap_fast_derive_msk(sm, data) < 0) { + wpa_printf(MSG_INFO, "EAP-FAST: Failed to generate MSK"); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + rresult->status = host_to_be16(EAP_TLV_RESULT_FAILURE); + data->phase2_success = 0; + } + + rbind = (struct eap_tlv_crypto_binding__tlv *) (rresult + 1); + rbind->tlv_type = host_to_be16(EAP_TLV_TYPE_MANDATORY | + EAP_TLV_CRYPTO_BINDING_TLV_); + rbind->length = host_to_be16(sizeof(*rbind) - + sizeof(struct eap_tlv_hdr)); + rbind->version = EAP_FAST_VERSION; + rbind->received_version = bind->version; + rbind->subtype = EAP_TLV_CRYPTO_BINDING_SUBTYPE_RESPONSE; + memcpy(rbind->nonce, bind->nonce, sizeof(bind->nonce)); + inc_byte_array(rbind->nonce, sizeof(bind->nonce)); + hmac_sha1(cmk, 20, (u8 *) rbind, sizeof(*rbind), rbind->compound_mac); + + wpa_printf(MSG_DEBUG, "EAP-FAST: Reply Crypto-Binding TLV: Version %d " + "Received Version %d SubType %d", + rbind->version, rbind->received_version, rbind->subtype); + wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: NONCE", + rbind->nonce, sizeof(rbind->nonce)); + wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Compound MAC", + rbind->compound_mac, sizeof(rbind->compound_mac)); + + if (final && data->phase2_success) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Authentication completed " + "successfully."); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_UNCOND_SUCC; + } + + return resp; +} + + +static u8 * eap_fast_process_pac(struct eap_sm *sm, struct eap_fast_data *data, + struct eap_method_ret *ret, + u8 *pac, size_t pac_len, size_t *resp_len) +{ + struct wpa_ssid *config = eap_get_config(sm); + struct pac_tlv_hdr *hdr; + u8 *pos; + size_t left, len; + int type, pac_key_found = 0; + struct eap_fast_pac entry; + + memset(&entry, 0, sizeof(entry)); + pos = pac; + left = pac_len; + while (left > sizeof(*hdr)) { + hdr = (struct pac_tlv_hdr *) pos; + type = be_to_host16(hdr->type); + len = be_to_host16(hdr->len); + pos += sizeof(*hdr); + left -= sizeof(*hdr); + if (len > left) { + wpa_printf(MSG_DEBUG, "EAP-FAST: PAC TLV overrun " + "(type=%d len=%lu left=%lu)", + type, (unsigned long) len, + (unsigned long) left); + return eap_fast_tlv_result(EAP_TLV_RESULT_FAILURE, 0, + resp_len); + } + switch (type) { + case PAC_TYPE_PAC_KEY: + wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: PAC-Key", + pos, len); + if (len != EAP_FAST_PAC_KEY_LEN) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Invalid " + "PAC-Key length %lu", + (unsigned long) len); + break; + } + pac_key_found = 1; + memcpy(entry.pac_key, pos, len); + break; + case PAC_TYPE_PAC_OPAQUE: + wpa_hexdump(MSG_DEBUG, "EAP-FAST: PAC-Opaque", + pos, len); + entry.pac_opaque = pos; + entry.pac_opaque_len = len; + break; + case PAC_TYPE_PAC_INFO: + wpa_hexdump(MSG_DEBUG, "EAP-FAST: PAC-Info", + pos, len); + entry.pac_info = pos; + entry.pac_info_len = len; + break; + default: + wpa_printf(MSG_DEBUG, "EAP-FAST: Ignored unknown PAC " + "type %d", type); + break; + } + + pos += len; + left -= len; + } + + if (!pac_key_found || !entry.pac_opaque || !entry.pac_info) { + wpa_printf(MSG_DEBUG, "EAP-FAST: PAC TLV does not include " + "all the required fields"); + return eap_fast_tlv_result(EAP_TLV_RESULT_FAILURE, 0, + resp_len); + } + + pos = entry.pac_info; + left = entry.pac_info_len; + while (left > sizeof(*hdr)) { + hdr = (struct pac_tlv_hdr *) pos; + type = be_to_host16(hdr->type); + len = be_to_host16(hdr->len); + pos += sizeof(*hdr); + left -= sizeof(*hdr); + if (len > left) { + wpa_printf(MSG_DEBUG, "EAP-FAST: PAC-Info overrun " + "(type=%d len=%lu left=%lu)", + type, (unsigned long) len, + (unsigned long) left); + return eap_fast_tlv_result(EAP_TLV_RESULT_FAILURE, 0, + resp_len); + } + switch (type) { + case PAC_TYPE_A_ID: + wpa_hexdump_ascii(MSG_DEBUG, "EAP-FAST: PAC-Info - " + "A-ID", pos, len); + entry.a_id = pos; + entry.a_id_len = len; + break; + case PAC_TYPE_I_ID: + wpa_hexdump_ascii(MSG_DEBUG, "EAP-FAST: PAC-Info - " + "I-ID", pos, len); + entry.i_id = pos; + entry.i_id_len = len; + break; + case PAC_TYPE_A_ID_INFO: + wpa_hexdump_ascii(MSG_DEBUG, "EAP-FAST: PAC-Info - " + "A-ID-Info", pos, len); + entry.a_id_info = pos; + entry.a_id_info_len = len; + break; + default: + wpa_printf(MSG_DEBUG, "EAP-FAST: Ignored unknown " + "PAC-Info type %d", type); + break; + } + + pos += len; + left -= len; + } + + if (entry.a_id == NULL || entry.a_id_info == NULL) { + wpa_printf(MSG_DEBUG, "EAP-FAST: PAC-Info does not include " + "all the required fields"); + return eap_fast_tlv_result(EAP_TLV_RESULT_FAILURE, 0, + resp_len); + } + + eap_fast_add_pac(data, &entry); + eap_fast_save_pac(data, config->pac_file); + + if (data->provisioning) { + /* EAP-FAST provisioning does not provide keying material and + * must end with an EAP-Failure. Authentication will be done + * separately after this. */ + data->success = 0; + ret->decision = DECISION_FAIL; + wpa_printf(MSG_DEBUG, "EAP-FAST: Send PAC-Acknowledgement TLV " + "- Provisioning completed successfully"); + } else { + /* This is PAC refreshing, i.e., normal authentication that is + * expected to be completed with an EAP-Success. */ + wpa_printf(MSG_DEBUG, "EAP-FAST: Send PAC-Acknowledgement TLV " + "- PAC refreshing completed successfully"); + ret->decision = DECISION_UNCOND_SUCC; + } + ret->methodState = METHOD_DONE; + return eap_fast_tlv_pac_ack(resp_len); +} + + +static int eap_fast_decrypt(struct eap_sm *sm, struct eap_fast_data *data, + struct eap_method_ret *ret, struct eap_hdr *req, + u8 *in_data, size_t in_len, + u8 **out_data, size_t *out_len) +{ + u8 *in_decrypted, *pos, *end; + int buf_len, len_decrypted, len, res; + struct eap_hdr *hdr; + u8 *resp = NULL; + size_t resp_len; + int mandatory, tlv_type; + u8 *eap_payload_tlv = NULL, *pac = NULL; + size_t eap_payload_tlv_len = 0, pac_len = 0; + int iresult = 0, result = 0; + struct eap_tlv_crypto_binding__tlv *crypto_binding = NULL; + size_t crypto_binding_len = 0; + + wpa_printf(MSG_DEBUG, "EAP-FAST: received %lu bytes encrypted data for" + " Phase 2", (unsigned long) in_len); + + res = eap_tls_data_reassemble(sm, &data->ssl, &in_data, &in_len); + if (res < 0 || res == 1) + return res; + + buf_len = in_len; + if (data->ssl.tls_in_total > buf_len) + buf_len = data->ssl.tls_in_total; + in_decrypted = malloc(buf_len); + if (in_decrypted == NULL) { + free(data->ssl.tls_in); + data->ssl.tls_in = NULL; + data->ssl.tls_in_len = 0; + wpa_printf(MSG_WARNING, "EAP-FAST: failed to allocate memory " + "for decryption"); + return -1; + } + + len_decrypted = tls_connection_decrypt(sm->ssl_ctx, data->ssl.conn, + in_data, in_len, + in_decrypted, buf_len); + free(data->ssl.tls_in); + data->ssl.tls_in = NULL; + data->ssl.tls_in_len = 0; + if (len_decrypted < 0) { + wpa_printf(MSG_INFO, "EAP-FAST: Failed to decrypt Phase 2 " + "data"); + free(in_decrypted); + return -1; + } + + wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Decrypted Phase 2 TLV(s)", + in_decrypted, len_decrypted); + + if (len_decrypted < 4) { + free(in_decrypted); + wpa_printf(MSG_INFO, "EAP-FAST: Too short Phase 2 " + "TLV frame (len=%d)", len_decrypted); + return -1; + } + + pos = in_decrypted; + end = in_decrypted + len_decrypted; + while (pos < end) { + mandatory = pos[0] & 0x80; + tlv_type = ((pos[0] & 0x3f) << 8) | pos[1]; + len = (pos[2] << 8) | pos[3]; + pos += 4; + if (pos + len > end) { + free(in_decrypted); + wpa_printf(MSG_INFO, "EAP-FAST: TLV overflow"); + return 0; + } + wpa_printf(MSG_DEBUG, "EAP-FAST: received Phase 2: " + "TLV type %d length %d%s", + tlv_type, len, mandatory ? " (mandatory)" : ""); + + switch (tlv_type) { + case EAP_TLV_EAP_PAYLOAD_TLV: + wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: EAP Payload TLV", + pos, len); + eap_payload_tlv = pos; + eap_payload_tlv_len = len; + break; + case EAP_TLV_RESULT_TLV: + wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Result TLV", + pos, len); + if (len < 2) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Too short " + "Result TLV"); + result = EAP_TLV_RESULT_FAILURE; + break; + } + result = (pos[0] << 16) | pos[1]; + if (result != EAP_TLV_RESULT_SUCCESS && + result != EAP_TLV_RESULT_FAILURE) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Unknown " + "Result %d", result); + result = EAP_TLV_RESULT_FAILURE; + } + wpa_printf(MSG_DEBUG, "EAP-FAST: Result: %s", + result == EAP_TLV_RESULT_SUCCESS ? + "Success" : "Failure"); + break; + case EAP_TLV_INTERMEDIATE_RESULT_TLV: + wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Intermediate " + "Result TLV", pos, len); + if (len < 2) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Too short " + "Intermediate Result TLV"); + iresult = EAP_TLV_RESULT_FAILURE; + break; + } + iresult = (pos[0] << 16) | pos[1]; + if (iresult != EAP_TLV_RESULT_SUCCESS && + iresult != EAP_TLV_RESULT_FAILURE) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Unknown " + "Intermediate Result %d", iresult); + iresult = EAP_TLV_RESULT_FAILURE; + } + wpa_printf(MSG_DEBUG, + "EAP-FAST: Intermediate Result: %s", + iresult == EAP_TLV_RESULT_SUCCESS ? + "Success" : "Failure"); + break; + case EAP_TLV_CRYPTO_BINDING_TLV_: + wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Crypto-Binding " + "TLV", pos, len); + crypto_binding_len = sizeof(struct eap_tlv_hdr) + len; + if (crypto_binding_len < sizeof(*crypto_binding)) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Too short " + "Crypto-Binding TLV"); + iresult = EAP_TLV_RESULT_FAILURE; + pos = end; + break; + } + crypto_binding = + (struct eap_tlv_crypto_binding__tlv *) + (pos - sizeof(struct eap_tlv_hdr)); + break; + case EAP_TLV_PAC_TLV: + wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: PAC TLV", + pos, len); + pac = pos; + pac_len = len; + break; + default: + if (mandatory) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Nak unknown " + "mandatory TLV type %d", tlv_type); + resp = eap_fast_tlv_nak(0, tlv_type, + &resp_len); + pos = end; + } else { + wpa_printf(MSG_DEBUG, "EAP-FAST: ignored " + "unknown optional TLV type %d", + tlv_type); + } + break; + } + + pos += len; + } + + if (!resp && result == EAP_TLV_RESULT_FAILURE) { + resp = eap_fast_tlv_result(EAP_TLV_RESULT_FAILURE, 0, + &resp_len); + if (!resp) { + free(in_decrypted); + return 0; + } + } + + if (!resp && iresult == EAP_TLV_RESULT_FAILURE) { + resp = eap_fast_tlv_result(EAP_TLV_RESULT_FAILURE, 1, + &resp_len); + if (!resp) { + free(in_decrypted); + return 0; + } + } + + if (!resp && eap_payload_tlv) { + if (eap_payload_tlv_len < sizeof(*hdr)) { + wpa_printf(MSG_DEBUG, "EAP-FAST: too short EAP " + "Payload TLV (len=%lu)", + (unsigned long) eap_payload_tlv_len); + free(in_decrypted); + return 0; + } + hdr = (struct eap_hdr *) eap_payload_tlv; + if (be_to_host16(hdr->length) > eap_payload_tlv_len) { + wpa_printf(MSG_DEBUG, "EAP-FAST: EAP packet overflow " + "in EAP Payload TLV"); + } + if (hdr->code == EAP_CODE_REQUEST) { + if (eap_fast_phase2_request(sm, data, ret, req, hdr, + &resp, &resp_len)) { + free(in_decrypted); + wpa_printf(MSG_INFO, "EAP-FAST: Phase2 " + "Request processing failed"); + return 0; + } + resp = eap_fast_tlv_eap_payload(resp, &resp_len); + if (resp == NULL) { + free(in_decrypted); + return 0; + } + } else { + wpa_printf(MSG_INFO, "EAP-FAST: Unexpected code=%d in " + "Phase 2 EAP header", hdr->code); + free(in_decrypted); + return 0; + } + } + + if (!resp && crypto_binding) { + int final = result == EAP_TLV_RESULT_SUCCESS; + resp = eap_fast_process_crypto_binding(sm, data, ret, + crypto_binding, + crypto_binding_len, + &resp_len, final); + if (!resp) { + free(in_decrypted); + return 0; + } + } + + if (!resp && pac && result != EAP_TLV_RESULT_SUCCESS) { + wpa_printf(MSG_DEBUG, "EAP-FAST: PAC TLV without Result TLV " + "acnowledging success"); + resp = eap_fast_tlv_result(EAP_TLV_RESULT_FAILURE, 0, + &resp_len); + if (!resp) { + free(in_decrypted); + return 0; + } + } + + if (!resp && pac && result == EAP_TLV_RESULT_SUCCESS) { + resp = eap_fast_process_pac(sm, data, ret, pac, pac_len, + &resp_len); + if (!resp) { + free(in_decrypted); + return 0; + } + } + + free(in_decrypted); + + if (resp == NULL) { + wpa_printf(MSG_DEBUG, "EAP-FAST: No recognized TLVs - send " + "empty response packet"); + resp = malloc(0); + if (resp == NULL) + return 0; + resp_len = 0; + } + + wpa_hexdump(MSG_DEBUG, "EAP-FAST: Encrypting Phase 2 data", + resp, resp_len); + if (eap_fast_encrypt(sm, data, req->identifier, resp, resp_len, + out_data, out_len)) { + wpa_printf(MSG_INFO, "EAP-FAST: Failed to encrypt a Phase 2 " + "frame"); + } + free(resp); + + return 0; +} + + +static u8 * eap_fast_process(struct eap_sm *sm, void *priv, + struct eap_method_ret *ret, + u8 *reqData, size_t reqDataLen, + size_t *respDataLen) +{ + struct eap_hdr *req; + int left, res; + unsigned int tls_msg_len; + u8 flags, *pos, *resp, id; + struct eap_fast_data *data = priv; + + if (tls_get_errors(sm->ssl_ctx)) { + wpa_printf(MSG_INFO, "EAP-FAST: TLS errors detected"); + ret->ignore = TRUE; + return NULL; + } + + req = (struct eap_hdr *) reqData; + pos = (u8 *) (req + 1); + if (reqDataLen < sizeof(*req) + 2 || *pos != EAP_TYPE_FAST || + (left = host_to_be16(req->length)) > reqDataLen) { + wpa_printf(MSG_INFO, "EAP-FAST: Invalid frame"); + ret->ignore = TRUE; + return NULL; + } + left -= sizeof(struct eap_hdr); + id = req->identifier; + pos++; + flags = *pos++; + left -= 2; + wpa_printf(MSG_DEBUG, "EAP-FAST: Received packet(len=%lu) - " + "Flags 0x%02x", (unsigned long) reqDataLen, flags); + if (flags & EAP_TLS_FLAGS_LENGTH_INCLUDED) { + if (left < 4) { + wpa_printf(MSG_INFO, "EAP-FAST: Short frame with TLS " + "length"); + ret->ignore = TRUE; + return NULL; + } + tls_msg_len = (pos[0] << 24) | (pos[1] << 16) | (pos[2] << 8) | + pos[3]; + wpa_printf(MSG_DEBUG, "EAP-FAST: TLS Message Length: %d", + tls_msg_len); + if (data->ssl.tls_in_left == 0) { + data->ssl.tls_in_total = tls_msg_len; + data->ssl.tls_in_left = tls_msg_len; + free(data->ssl.tls_in); + data->ssl.tls_in = NULL; + data->ssl.tls_in_len = 0; + } + pos += 4; + left -= 4; + } + + ret->ignore = FALSE; + ret->methodState = METHOD_CONT; + ret->decision = DECISION_FAIL; + ret->allowNotifications = TRUE; + + if (flags & EAP_TLS_FLAGS_START) { + u8 *a_id; + size_t a_id_len; + struct pac_tlv_hdr *hdr; + + wpa_printf(MSG_DEBUG, "EAP-FAST: Start (server ver=%d, own " + "ver=%d)", flags & EAP_PEAP_VERSION_MASK, + data->fast_version); + if ((flags & EAP_PEAP_VERSION_MASK) < data->fast_version) + data->fast_version = flags & EAP_PEAP_VERSION_MASK; + wpa_printf(MSG_DEBUG, "EAP-FAST: Using FAST version %d", + data->fast_version); + + a_id = pos; + a_id_len = left; + if (left > sizeof(*hdr)) { + int tlen; + hdr = (struct pac_tlv_hdr *) pos; + tlen = be_to_host16(hdr->len); + if (be_to_host16(hdr->type) == PAC_TYPE_A_ID && + sizeof(*hdr) + tlen <= left) { + a_id = (u8 *) (hdr + 1); + a_id_len = tlen; + } + } + wpa_hexdump_ascii(MSG_DEBUG, "EAP-FAST: A-ID", a_id, a_id_len); + + data->current_pac = eap_fast_get_pac(data, a_id, a_id_len); + if (data->current_pac) { + wpa_printf(MSG_DEBUG, "EAP-FAST: PAC found for this " + "A-ID"); + wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-FAST: A-ID-Info", + data->current_pac->a_id_info, + data->current_pac->a_id_info_len); + } + + if (data->resuming && data->current_pac) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Trying to resume " + "session - do not add PAC-Opaque to TLS " + "ClientHello"); + if (tls_connection_client_hello_ext( + sm->ssl_ctx, data->ssl.conn, + TLS_EXT_PAC_OPAQUE, NULL, 0) < 0) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to " + "remove PAC-Opaque TLS extension"); + return NULL; + } + + } else if (data->current_pac) { + u8 *tlv; + size_t tlv_len, olen; + struct eap_tlv_hdr *hdr; + olen = data->current_pac->pac_opaque_len; + tlv_len = sizeof(*hdr) + olen; + tlv = malloc(tlv_len); + if (tlv) { + hdr = (struct eap_tlv_hdr *) tlv; + hdr->tlv_type = + host_to_be16(PAC_TYPE_PAC_OPAQUE); + hdr->length = host_to_be16(olen); + memcpy(hdr + 1, data->current_pac->pac_opaque, + olen); + } + if (tlv == NULL || + tls_connection_client_hello_ext( + sm->ssl_ctx, data->ssl.conn, + TLS_EXT_PAC_OPAQUE, tlv, tlv_len) < 0) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to " + "add PAC-Opaque TLS extension"); + free(tlv); + return NULL; + } + free(tlv); + } else { + if (!data->provisioning_allowed) { + wpa_printf(MSG_DEBUG, "EAP-FAST: No PAC found " + "and provisioning disabled"); + return NULL; + } + wpa_printf(MSG_DEBUG, "EAP-FAST: No PAC found - " + "starting provisioning"); + if (tls_connection_set_anon_dh(sm->ssl_ctx, + data->ssl.conn)) { + wpa_printf(MSG_INFO, "EAP-FAST: Could not " + "configure anonymous DH for TLS " + "connection"); + return NULL; + } + if (tls_connection_client_hello_ext( + sm->ssl_ctx, data->ssl.conn, + TLS_EXT_PAC_OPAQUE, NULL, 0) < 0) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to " + "remove PAC-Opaque TLS extension"); + return NULL; + } + data->provisioning = 1; + } + + left = 0; /* A-ID is not used in further packet processing */ + } + + resp = NULL; + if (tls_connection_established(sm->ssl_ctx, data->ssl.conn) && + !data->resuming) { + res = eap_fast_decrypt(sm, data, ret, req, pos, left, + &resp, respDataLen); + if (res < 0) { + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + /* Ack possible Alert that may have caused failure in + * decryption */ + res = 1; + } + } else { + if (eap_fast_set_tls_master_secret(sm, data, pos, left) < 0) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to configure " + "TLS master secret"); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + return NULL; + } + + res = eap_tls_process_helper(sm, &data->ssl, EAP_TYPE_FAST, + data->fast_version, id, pos, left, + &resp, respDataLen); + + if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) { + wpa_printf(MSG_DEBUG, + "EAP-FAST: TLS done, proceed to Phase 2"); + data->resuming = 0; + eap_fast_derive_keys(sm, data); + } + } + + if (res == 1) + return eap_tls_build_ack(&data->ssl, respDataLen, id, + EAP_TYPE_FAST, data->fast_version); + return resp; +} + + +#if 0 /* FIX */ +static Boolean eap_fast_has_reauth_data(struct eap_sm *sm, void *priv) +{ + struct eap_fast_data *data = priv; + return tls_connection_established(sm->ssl_ctx, data->ssl.conn); +} + + +static void eap_fast_deinit_for_reauth(struct eap_sm *sm, void *priv) +{ +} + + +static void * eap_fast_init_for_reauth(struct eap_sm *sm, void *priv) +{ + struct eap_fast_data *data = priv; + if (eap_tls_reauth_init(sm, &data->ssl)) { + free(data); + return NULL; + } + data->phase2_success = 0; + data->resuming = 1; + data->provisioning = 0; + return priv; +} +#endif + + +static int eap_fast_get_status(struct eap_sm *sm, void *priv, char *buf, + size_t buflen, int verbose) +{ + struct eap_fast_data *data = priv; + return eap_tls_status(sm, &data->ssl, buf, buflen, verbose); +} + + +static Boolean eap_fast_isKeyAvailable(struct eap_sm *sm, void *priv) +{ + struct eap_fast_data *data = priv; + return data->success; +} + + +static u8 * eap_fast_getKey(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_fast_data *data = priv; + u8 *key; + + if (!data->success) + return NULL; + + key = malloc(EAP_FAST_KEY_LEN); + if (key == NULL) + return NULL; + + *len = EAP_FAST_KEY_LEN; + memcpy(key, data->key_data, EAP_FAST_KEY_LEN); + + return key; +} + + +const struct eap_method eap_method_fast = +{ + .method = EAP_TYPE_FAST, + .name = "FAST", + .init = eap_fast_init, + .deinit = eap_fast_deinit, + .process = eap_fast_process, + .isKeyAvailable = eap_fast_isKeyAvailable, + .getKey = eap_fast_getKey, + .get_status = eap_fast_get_status, +#if 0 + .has_reauth_data = eap_fast_has_reauth_data, + .deinit_for_reauth = eap_fast_deinit_for_reauth, + .init_for_reauth = eap_fast_init_for_reauth, +#endif +}; diff --git a/contrib/wpa_supplicant/eap_gtc.c b/contrib/wpa_supplicant/eap_gtc.c new file mode 100644 index 0000000..42a7a49 --- /dev/null +++ b/contrib/wpa_supplicant/eap_gtc.c @@ -0,0 +1,164 @@ +/* + * WPA Supplicant / EAP-GTC (RFC 2284) + * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include "common.h" +#include "eap_i.h" +#include "wpa_supplicant.h" +#include "config_ssid.h" + + +struct eap_gtc_data { + int prefix; +}; + + +static void * eap_gtc_init(struct eap_sm *sm) +{ + struct eap_gtc_data *data; + data = malloc(sizeof(*data)); + if (data == NULL) + return NULL; + memset(data, 0, sizeof(*data)); + + if (sm->m && sm->m->method == EAP_TYPE_FAST) { + wpa_printf(MSG_DEBUG, "EAP-GTC: EAP-FAST tunnel - use prefix " + "with challenge/response"); + data->prefix = 1; + } + return data; +} + + +static void eap_gtc_deinit(struct eap_sm *sm, void *priv) +{ + struct eap_gtc_data *data = priv; + free(data); +} + + +static u8 * eap_gtc_process(struct eap_sm *sm, void *priv, + struct eap_method_ret *ret, + u8 *reqData, size_t reqDataLen, + size_t *respDataLen) +{ + struct eap_gtc_data *data = priv; + struct wpa_ssid *config = eap_get_config(sm); + struct eap_hdr *req, *resp; + u8 *pos, *password; + size_t password_len, len, msg_len; + + req = (struct eap_hdr *) reqData; + pos = (u8 *) (req + 1); + if (reqDataLen < sizeof(*req) + 1 || *pos != EAP_TYPE_GTC || + (len = be_to_host16(req->length)) > reqDataLen) { + wpa_printf(MSG_INFO, "EAP-GTC: Invalid frame"); + ret->ignore = TRUE; + return NULL; + } + pos++; + msg_len = len - sizeof(*req) - 1; + wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-GTC: Request message", + pos, msg_len); + if (data->prefix && + (msg_len < 10 || memcmp(pos, "CHALLENGE=", 10) != 0)) { + wpa_printf(MSG_DEBUG, "EAP-GTC: Challenge did not start with " + "expected prefix"); + + /* Send an empty response in order to allow tunneled + * acknowledgement of the failure. This will also cover the + * error case which seems to use EAP-MSCHAPv2 like error + * reporting with EAP-GTC inside EAP-FAST tunnel. */ + *respDataLen = sizeof(struct eap_hdr) + 1; + resp = malloc(*respDataLen); + if (resp == NULL) + return NULL; + resp->code = EAP_CODE_RESPONSE; + resp->identifier = req->identifier; + resp->length = host_to_be16(*respDataLen); + pos = (u8 *) (resp + 1); + *pos++ = EAP_TYPE_GTC; + return (u8 *) resp; + } + + if (config == NULL || + (config->password == NULL && config->otp == NULL)) { + wpa_printf(MSG_INFO, "EAP-GTC: Password not configured"); + eap_sm_request_otp(sm, config, (char *) pos, + len - sizeof(*req) - 1); + ret->ignore = TRUE; + return NULL; + } + + if (config->otp) { + password = config->otp; + password_len = config->otp_len; + } else { + password = config->password; + password_len = config->password_len; + } + + ret->ignore = FALSE; + + ret->methodState = data->prefix ? METHOD_MAY_CONT : METHOD_DONE; + ret->decision = DECISION_COND_SUCC; + ret->allowNotifications = FALSE; + + *respDataLen = sizeof(struct eap_hdr) + 1 + password_len; + if (data->prefix) { + *respDataLen += 9 + config->identity_len + 1; + } + resp = malloc(*respDataLen); + if (resp == NULL) + return NULL; + resp->code = EAP_CODE_RESPONSE; + resp->identifier = req->identifier; + resp->length = host_to_be16(*respDataLen); + pos = (u8 *) (resp + 1); + *pos++ = EAP_TYPE_GTC; + if (data->prefix) { + memcpy(pos, "RESPONSE=", 9); + pos += 9; + memcpy(pos, config->identity, config->identity_len); + pos += config->identity_len; + *pos++ = '\0'; + } + memcpy(pos, password, password_len); + wpa_hexdump_ascii_key(MSG_MSGDUMP, "EAP-GTC: Response", + (u8 *) (resp + 1) + 1, + *respDataLen - sizeof(struct eap_hdr) - 1); + + if (config->otp) { + wpa_printf(MSG_DEBUG, "EAP-GTC: Forgetting used password"); + memset(config->otp, 0, config->otp_len); + free(config->otp); + config->otp = NULL; + config->otp_len = 0; + } + + return (u8 *) resp; +} + + +const struct eap_method eap_method_gtc = +{ + .method = EAP_TYPE_GTC, + .name = "GTC", + .init = eap_gtc_init, + .deinit = eap_gtc_deinit, + .process = eap_gtc_process, +}; diff --git a/contrib/wpa_supplicant/eap_i.h b/contrib/wpa_supplicant/eap_i.h new file mode 100644 index 0000000..c719812 --- /dev/null +++ b/contrib/wpa_supplicant/eap_i.h @@ -0,0 +1,106 @@ +#ifndef EAP_I_H +#define EAP_I_H + +#include "eap.h" + +/* draft-ietf-eap-statemachine-05.pdf - Peer state machine */ + +typedef enum { + DECISION_FAIL, DECISION_COND_SUCC, DECISION_UNCOND_SUCC +} EapDecision; + +typedef enum { + METHOD_NONE, METHOD_INIT, METHOD_CONT, METHOD_MAY_CONT, METHOD_DONE +} EapMethodState; + +struct eap_method_ret { + Boolean ignore; + EapMethodState methodState; + EapDecision decision; + Boolean allowNotifications; +}; + + +struct eap_method { + EapType method; + const char *name; + + void * (*init)(struct eap_sm *sm); + void (*deinit)(struct eap_sm *sm, void *priv); + u8 * (*process)(struct eap_sm *sm, void *priv, + struct eap_method_ret *ret, + u8 *reqData, size_t reqDataLen, + size_t *respDataLen); + Boolean (*isKeyAvailable)(struct eap_sm *sm, void *priv); + u8 * (*getKey)(struct eap_sm *sm, void *priv, size_t *len); + int (*get_status)(struct eap_sm *sm, void *priv, char *buf, + size_t buflen, int verbose); + + /* Optional handlers for fast re-authentication */ + Boolean (*has_reauth_data)(struct eap_sm *sm, void *priv); + void (*deinit_for_reauth)(struct eap_sm *sm, void *priv); + void * (*init_for_reauth)(struct eap_sm *sm, void *priv); + const u8 * (*get_identity)(struct eap_sm *sm, void *priv, size_t *len); +}; + + +struct eap_sm { + enum { + EAP_INITIALIZE, EAP_DISABLED, EAP_IDLE, EAP_RECEIVED, + EAP_GET_METHOD, EAP_METHOD, EAP_SEND_RESPONSE, EAP_DISCARD, + EAP_IDENTITY, EAP_NOTIFICATION, EAP_RETRANSMIT, EAP_SUCCESS, + EAP_FAILURE + } EAP_state; + /* Long-term local variables */ + EapType selectedMethod; + EapMethodState methodState; + int lastId; + u8 *lastRespData; + size_t lastRespDataLen; + EapDecision decision; + /* Short-term local variables */ + Boolean rxReq; + Boolean rxSuccess; + Boolean rxFailure; + int reqId; + EapType reqMethod; + Boolean ignore; + /* Constants */ + int ClientTimeout; + + /* Miscellaneous variables */ + Boolean allowNotifications; /* peer state machine <-> methods */ + u8 *eapRespData; /* peer to lower layer */ + size_t eapRespDataLen; /* peer to lower layer */ + Boolean eapKeyAvailable; /* peer to lower layer */ + u8 *eapKeyData; /* peer to lower layer */ + size_t eapKeyDataLen; /* peer to lower layer */ + const struct eap_method *m; /* selected EAP method */ + /* not defined in draft-ietf-eap-statemachine-02 */ + Boolean changed; + void *eapol_ctx; + struct eapol_callbacks *eapol_cb; + void *eap_method_priv; + int init_phase2; + int fast_reauth; + + Boolean rxResp /* LEAP only */; + Boolean leap_done; + Boolean peap_done; + u8 req_md5[16]; /* MD5() of the current EAP packet */ + u8 last_md5[16]; /* MD5() of the previously received EAP packet; used + * in duplicate request detection. */ + + void *msg_ctx; + void *scard_ctx; + void *ssl_ctx; + + unsigned int workaround; + + /* Optional challenges generated in Phase 1 (EAP-FAST) */ + u8 *peer_challenge, *auth_challenge; + + int num_rounds; +}; + +#endif /* EAP_I_H */ diff --git a/contrib/wpa_supplicant/eap_leap.c b/contrib/wpa_supplicant/eap_leap.c new file mode 100644 index 0000000..6409a68 --- /dev/null +++ b/contrib/wpa_supplicant/eap_leap.c @@ -0,0 +1,379 @@ +/* + * WPA Supplicant / EAP-LEAP + * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include "common.h" +#include "eap_i.h" +#include "wpa_supplicant.h" +#include "config_ssid.h" +#include "ms_funcs.h" +#include "md5.h" + +#define LEAP_VERSION 1 +#define LEAP_CHALLENGE_LEN 8 +#define LEAP_RESPONSE_LEN 24 +#define LEAP_KEY_LEN 16 + + +struct eap_leap_data { + enum { + LEAP_WAIT_CHALLENGE, + LEAP_WAIT_SUCCESS, + LEAP_WAIT_RESPONSE, + LEAP_DONE + } state; + + u8 peer_challenge[LEAP_CHALLENGE_LEN]; + u8 peer_response[LEAP_RESPONSE_LEN]; + + u8 ap_challenge[LEAP_CHALLENGE_LEN]; + u8 ap_response[LEAP_RESPONSE_LEN]; +}; + + +static void * eap_leap_init(struct eap_sm *sm) +{ + struct eap_leap_data *data; + + data = malloc(sizeof(*data)); + if (data == NULL) + return NULL; + memset(data, 0, sizeof(*data)); + data->state = LEAP_WAIT_CHALLENGE; + + sm->leap_done = FALSE; + return data; +} + + +static void eap_leap_deinit(struct eap_sm *sm, void *priv) +{ + free(priv); +} + + +static u8 * eap_leap_process_request(struct eap_sm *sm, void *priv, + struct eap_method_ret *ret, + u8 *reqData, size_t reqDataLen, + size_t *respDataLen) +{ + struct eap_leap_data *data = priv; + struct wpa_ssid *config = eap_get_config(sm); + struct eap_hdr *req, *resp; + u8 *pos, *challenge, challenge_len; + + wpa_printf(MSG_DEBUG, "EAP-LEAP: Processing EAP-Request"); + + req = (struct eap_hdr *) reqData; + pos = (u8 *) (req + 1); + if (reqDataLen < sizeof(*req) + 4 || *pos != EAP_TYPE_LEAP) { + wpa_printf(MSG_INFO, "EAP-LEAP: Invalid EAP-Request frame"); + ret->ignore = TRUE; + return NULL; + } + pos++; + + if (*pos != LEAP_VERSION) { + wpa_printf(MSG_WARNING, "EAP-LEAP: Unsupported LEAP version " + "%d", *pos); + ret->ignore = TRUE; + return NULL; + } + pos++; + + pos++; /* skip unused byte */ + + challenge_len = *pos++; + if (challenge_len != LEAP_CHALLENGE_LEN || + challenge_len > reqDataLen - sizeof(*req) - 4) { + wpa_printf(MSG_INFO, "EAP-LEAP: Invalid challenge " + "(challenge_len=%d reqDataLen=%lu", + challenge_len, (unsigned long) reqDataLen); + ret->ignore = TRUE; + return NULL; + } + challenge = pos; + memcpy(data->peer_challenge, challenge, LEAP_CHALLENGE_LEN); + wpa_hexdump(MSG_MSGDUMP, "EAP-LEAP: Challenge from AP", + challenge, LEAP_CHALLENGE_LEN); + + wpa_printf(MSG_DEBUG, "EAP-LEAP: Generating Challenge Response"); + + *respDataLen = sizeof(struct eap_hdr) + 1 + 3 + LEAP_RESPONSE_LEN + + config->identity_len; + resp = malloc(*respDataLen); + if (resp == NULL) + return NULL; + resp->code = EAP_CODE_RESPONSE; + resp->identifier = req->identifier; + resp->length = host_to_be16(*respDataLen); + pos = (u8 *) (resp + 1); + *pos++ = EAP_TYPE_LEAP; + *pos++ = LEAP_VERSION; + *pos++ = 0; /* unused */ + *pos++ = LEAP_RESPONSE_LEN; + nt_challenge_response(challenge, + config->password, config->password_len, pos); + memcpy(data->peer_response, pos, LEAP_RESPONSE_LEN); + wpa_hexdump(MSG_MSGDUMP, "EAP-LEAP: Response", pos, LEAP_RESPONSE_LEN); + pos += LEAP_RESPONSE_LEN; + memcpy(pos, config->identity, config->identity_len); + + data->state = LEAP_WAIT_SUCCESS; + + return (u8 *) resp; +} + + +static u8 * eap_leap_process_success(struct eap_sm *sm, void *priv, + struct eap_method_ret *ret, + u8 *reqData, size_t reqDataLen, + size_t *respDataLen) +{ + struct eap_leap_data *data = priv; + struct wpa_ssid *config = eap_get_config(sm); + struct eap_hdr *req, *resp; + u8 *pos; + + wpa_printf(MSG_DEBUG, "EAP-LEAP: Processing EAP-Success"); + + if (data->state != LEAP_WAIT_SUCCESS) { + wpa_printf(MSG_INFO, "EAP-LEAP: EAP-Success received in " + "unexpected state (%d) - ignored", data->state); + ret->ignore = TRUE; + return NULL; + } + + req = (struct eap_hdr *) reqData; + + *respDataLen = sizeof(struct eap_hdr) + 1 + 3 + LEAP_CHALLENGE_LEN + + config->identity_len; + resp = malloc(*respDataLen); + if (resp == NULL) + return NULL; + resp->code = EAP_CODE_REQUEST; + resp->identifier = req->identifier; + resp->length = host_to_be16(*respDataLen); + pos = (u8 *) (resp + 1); + *pos++ = EAP_TYPE_LEAP; + *pos++ = LEAP_VERSION; + *pos++ = 0; /* unused */ + *pos++ = LEAP_CHALLENGE_LEN; + if (hostapd_get_rand(pos, LEAP_CHALLENGE_LEN)) { + wpa_printf(MSG_WARNING, "EAP-LEAP: Failed to read random data " + "for challenge"); + free(resp); + ret->ignore = TRUE; + return NULL; + } + memcpy(data->ap_challenge, pos, LEAP_CHALLENGE_LEN); + wpa_hexdump(MSG_MSGDUMP, "EAP-LEAP: Challenge to AP/AS", pos, + LEAP_CHALLENGE_LEN); + pos += LEAP_CHALLENGE_LEN; + memcpy(pos, config->identity, config->identity_len); + + data->state = LEAP_WAIT_RESPONSE; + + return (u8 *) resp; +} + + +static u8 * eap_leap_process_response(struct eap_sm *sm, void *priv, + struct eap_method_ret *ret, + u8 *reqData, size_t reqDataLen, + size_t *respDataLen) +{ + struct eap_leap_data *data = priv; + struct wpa_ssid *config = eap_get_config(sm); + struct eap_hdr *resp; + u8 *pos, response_len, pw_hash[16], pw_hash_hash[16], + expected[LEAP_RESPONSE_LEN]; + + wpa_printf(MSG_DEBUG, "EAP-LEAP: Processing EAP-Response"); + + resp = (struct eap_hdr *) reqData; + pos = (u8 *) (resp + 1); + if (reqDataLen < sizeof(*resp) + 4 || *pos != EAP_TYPE_LEAP) { + wpa_printf(MSG_INFO, "EAP-LEAP: Invalid EAP-Response frame"); + ret->ignore = TRUE; + return NULL; + } + pos++; + + if (*pos != LEAP_VERSION) { + wpa_printf(MSG_WARNING, "EAP-LEAP: Unsupported LEAP version " + "%d", *pos); + ret->ignore = TRUE; + return NULL; + } + pos++; + + pos++; /* skip unused byte */ + + response_len = *pos++; + if (response_len != LEAP_RESPONSE_LEN || + response_len > reqDataLen - sizeof(*resp) - 4) { + wpa_printf(MSG_INFO, "EAP-LEAP: Invalid response " + "(response_len=%d reqDataLen=%lu", + response_len, (unsigned long) reqDataLen); + ret->ignore = TRUE; + return NULL; + } + + wpa_hexdump(MSG_DEBUG, "EAP-LEAP: Response from AP", + pos, LEAP_RESPONSE_LEN); + memcpy(data->ap_response, pos, LEAP_RESPONSE_LEN); + + nt_password_hash(config->password, config->password_len, pw_hash); + hash_nt_password_hash(pw_hash, pw_hash_hash); + challenge_response(data->ap_challenge, pw_hash_hash, expected); + + ret->methodState = METHOD_DONE; + ret->allowNotifications = FALSE; + + if (memcmp(pos, expected, LEAP_RESPONSE_LEN) != 0) { + wpa_printf(MSG_WARNING, "EAP-LEAP: AP sent an invalid " + "response - authentication failed"); + wpa_hexdump(MSG_DEBUG, "EAP-LEAP: Expected response from AP", + expected, LEAP_RESPONSE_LEN); + ret->decision = DECISION_FAIL; + return NULL; + } + + ret->decision = DECISION_UNCOND_SUCC; + + /* LEAP is somewhat odd method since it sends EAP-Success in the middle + * of the authentication. Use special variable to transit EAP state + * machine to SUCCESS state. */ + sm->leap_done = TRUE; + data->state = LEAP_DONE; + + /* No more authentication messages expected; AP will send EAPOL-Key + * frames if encryption is enabled. */ + return NULL; +} + + +static u8 * eap_leap_process(struct eap_sm *sm, void *priv, + struct eap_method_ret *ret, + u8 *reqData, size_t reqDataLen, + size_t *respDataLen) +{ + struct wpa_ssid *config = eap_get_config(sm); + struct eap_hdr *eap; + size_t len; + + if (config == NULL || config->password == NULL) { + wpa_printf(MSG_INFO, "EAP-LEAP: Password not configured"); + eap_sm_request_password(sm, config); + ret->ignore = TRUE; + return NULL; + } + + eap = (struct eap_hdr *) reqData; + + if (reqDataLen < sizeof(*eap) || + (len = be_to_host16(eap->length)) > reqDataLen) { + wpa_printf(MSG_INFO, "EAP-LEAP: Invalid frame"); + ret->ignore = TRUE; + return NULL; + } + + ret->ignore = FALSE; + ret->allowNotifications = TRUE; + ret->methodState = METHOD_CONT; + ret->decision = DECISION_FAIL; + + sm->leap_done = FALSE; + + switch (eap->code) { + case EAP_CODE_REQUEST: + return eap_leap_process_request(sm, priv, ret, reqData, len, + respDataLen); + case EAP_CODE_SUCCESS: + return eap_leap_process_success(sm, priv, ret, reqData, len, + respDataLen); + case EAP_CODE_RESPONSE: + return eap_leap_process_response(sm, priv, ret, reqData, len, + respDataLen); + default: + wpa_printf(MSG_INFO, "EAP-LEAP: Unexpected EAP code (%d) - " + "ignored", eap->code); + ret->ignore = TRUE; + return NULL; + } +} + + +static Boolean eap_leap_isKeyAvailable(struct eap_sm *sm, void *priv) +{ + struct eap_leap_data *data = priv; + return data->state == LEAP_DONE; +} + + +static u8 * eap_leap_getKey(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_leap_data *data = priv; + struct wpa_ssid *config = eap_get_config(sm); + u8 *key, pw_hash_hash[16], pw_hash[16]; + MD5_CTX context; + + if (data->state != LEAP_DONE) + return NULL; + + key = malloc(LEAP_KEY_LEN); + if (key == NULL) + return NULL; + + nt_password_hash(config->password, config->password_len, pw_hash); + hash_nt_password_hash(pw_hash, pw_hash_hash); + wpa_hexdump_key(MSG_DEBUG, "EAP-LEAP: pw_hash_hash", + pw_hash_hash, 16); + wpa_hexdump(MSG_DEBUG, "EAP-LEAP: peer_challenge", + data->peer_challenge, LEAP_CHALLENGE_LEN); + wpa_hexdump(MSG_DEBUG, "EAP-LEAP: peer_response", + data->peer_response, LEAP_RESPONSE_LEN); + wpa_hexdump(MSG_DEBUG, "EAP-LEAP: ap_challenge", + data->ap_challenge, LEAP_CHALLENGE_LEN); + wpa_hexdump(MSG_DEBUG, "EAP-LEAP: ap_response", + data->ap_response, LEAP_RESPONSE_LEN); + + MD5Init(&context); + MD5Update(&context, pw_hash_hash, 16); + MD5Update(&context, data->ap_challenge, LEAP_CHALLENGE_LEN); + MD5Update(&context, data->ap_response, LEAP_RESPONSE_LEN); + MD5Update(&context, data->peer_challenge, LEAP_CHALLENGE_LEN); + MD5Update(&context, data->peer_response, LEAP_RESPONSE_LEN); + MD5Final(key, &context); + wpa_hexdump_key(MSG_DEBUG, "EAP-LEAP: master key", key, LEAP_KEY_LEN); + *len = LEAP_KEY_LEN; + + return key; +} + + +const struct eap_method eap_method_leap = +{ + .method = EAP_TYPE_LEAP, + .name = "LEAP", + .init = eap_leap_init, + .deinit = eap_leap_deinit, + .process = eap_leap_process, + .isKeyAvailable = eap_leap_isKeyAvailable, + .getKey = eap_leap_getKey, +}; diff --git a/contrib/wpa_supplicant/eap_md5.c b/contrib/wpa_supplicant/eap_md5.c new file mode 100644 index 0000000..bcb5558 --- /dev/null +++ b/contrib/wpa_supplicant/eap_md5.c @@ -0,0 +1,112 @@ +/* + * WPA Supplicant / EAP-MD5 + * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include <stdlib.h> +#include <stdio.h> + +#include "common.h" +#include "eap_i.h" +#include "wpa_supplicant.h" +#include "config_ssid.h" +#include "md5.h" + + +static void * eap_md5_init(struct eap_sm *sm) +{ + return (void *) 1; +} + + +static void eap_md5_deinit(struct eap_sm *sm, void *priv) +{ +} + + +static u8 * eap_md5_process(struct eap_sm *sm, void *priv, + struct eap_method_ret *ret, + u8 *reqData, size_t reqDataLen, + size_t *respDataLen) +{ + struct wpa_ssid *config = eap_get_config(sm); + struct eap_hdr *req, *resp; + u8 *pos, *challenge; + int challenge_len; + MD5_CTX context; + size_t len; + + if (config == NULL || config->password == NULL) { + wpa_printf(MSG_INFO, "EAP-MD5: Password not configured"); + eap_sm_request_password(sm, config); + ret->ignore = TRUE; + return NULL; + } + + req = (struct eap_hdr *) reqData; + pos = (u8 *) (req + 1); + if (reqDataLen < sizeof(*req) + 2 || *pos != EAP_TYPE_MD5 || + (len = be_to_host16(req->length)) > reqDataLen) { + wpa_printf(MSG_INFO, "EAP-MD5: Invalid frame"); + ret->ignore = TRUE; + return NULL; + } + pos++; + challenge_len = *pos++; + if (challenge_len == 0 || + challenge_len > len - sizeof(*req) - 2) { + wpa_printf(MSG_INFO, "EAP-MD5: Invalid challenge " + "(challenge_len=%d len=%lu", + challenge_len, (unsigned long) len); + ret->ignore = TRUE; + return NULL; + } + ret->ignore = FALSE; + challenge = pos; + wpa_hexdump(MSG_MSGDUMP, "EAP-MD5: Challenge", + challenge, challenge_len); + + wpa_printf(MSG_DEBUG, "EAP-MD5: generating Challenge Response"); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_UNCOND_SUCC; + ret->allowNotifications = TRUE; + + *respDataLen = sizeof(struct eap_hdr) + 1 + 1 + MD5_MAC_LEN; + resp = malloc(*respDataLen); + if (resp == NULL) + return NULL; + resp->code = EAP_CODE_RESPONSE; + resp->identifier = req->identifier; + resp->length = host_to_be16(*respDataLen); + pos = (u8 *) (resp + 1); + *pos++ = EAP_TYPE_MD5; + *pos++ = MD5_MAC_LEN; /* Value-Size */ + + MD5Init(&context); + MD5Update(&context, &resp->identifier, 1); + MD5Update(&context, config->password, config->password_len); + MD5Update(&context, challenge, challenge_len); + MD5Final(pos, &context); + wpa_hexdump(MSG_MSGDUMP, "EAP-MD5: Response", pos, MD5_MAC_LEN); + + return (u8 *) resp; +} + + +const struct eap_method eap_method_md5 = +{ + .method = EAP_TYPE_MD5, + .name = "MD5", + .init = eap_md5_init, + .deinit = eap_md5_deinit, + .process = eap_md5_process, +}; diff --git a/contrib/wpa_supplicant/eap_mschapv2.c b/contrib/wpa_supplicant/eap_mschapv2.c new file mode 100644 index 0000000..a39a5d3 --- /dev/null +++ b/contrib/wpa_supplicant/eap_mschapv2.c @@ -0,0 +1,564 @@ +/* + * WPA Supplicant / EAP-MSCHAPV2 (draft-kamath-pppext-eap-mschapv2-00.txt) + * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include "common.h" +#include "eap_i.h" +#include "wpa_supplicant.h" +#include "config_ssid.h" +#include "ms_funcs.h" + + +struct eap_mschapv2_hdr { + u8 code; + u8 identifier; + u16 length; /* including code, identifier, and length */ + u8 type; /* EAP_TYPE_MSCHAPV2 */ + u8 op_code; /* MSCHAPV2_OP_* */ + u8 mschapv2_id; /* usually same as identifier */ + u8 ms_length[2]; /* Note: misaligned; length - 5 */ + /* followed by data */ +} __attribute__ ((packed)); + +#define MSCHAPV2_OP_CHALLENGE 1 +#define MSCHAPV2_OP_RESPONSE 2 +#define MSCHAPV2_OP_SUCCESS 3 +#define MSCHAPV2_OP_FAILURE 4 +#define MSCHAPV2_OP_CHANGE_PASSWORD 7 + +#define MSCHAPV2_RESP_LEN 49 + +#define ERROR_RESTRICTED_LOGON_HOURS 646 +#define ERROR_ACCT_DISABLED 647 +#define ERROR_PASSWD_EXPIRED 648 +#define ERROR_NO_DIALIN_PERMISSION 649 +#define ERROR_AUTHENTICATION_FAILURE 691 +#define ERROR_CHANGING_PASSWORD 709 + +#define PASSWD_CHANGE_CHAL_LEN 16 +#define MSCHAPV2_KEY_LEN 16 + + +struct eap_mschapv2_data { + u8 auth_response[20]; + int auth_response_valid; + + int prev_error; + u8 passwd_change_challenge[PASSWD_CHANGE_CHAL_LEN]; + int passwd_change_challenge_valid; + int passwd_change_version; + + /* Optional challenge values generated in EAP-FAST Phase 1 negotiation + */ + u8 *peer_challenge; + u8 *auth_challenge; + + int phase2; + u8 master_key[16]; + int master_key_valid; + int success; +}; + + +static void eap_mschapv2_deinit(struct eap_sm *sm, void *priv); + + +static void * eap_mschapv2_init(struct eap_sm *sm) +{ + struct eap_mschapv2_data *data; + data = malloc(sizeof(*data)); + if (data == NULL) + return NULL; + memset(data, 0, sizeof(*data)); + + if (sm->peer_challenge) { + data->peer_challenge = malloc(16); + if (data->peer_challenge == NULL) { + eap_mschapv2_deinit(sm, data); + return NULL; + } + memcpy(data->peer_challenge, sm->peer_challenge, 16); + } + + if (sm->auth_challenge) { + data->auth_challenge = malloc(16); + if (data->auth_challenge == NULL) { + eap_mschapv2_deinit(sm, data); + return NULL; + } + memcpy(data->auth_challenge, sm->auth_challenge, 16); + } + + data->phase2 = sm->init_phase2; + + return data; +} + + +static void eap_mschapv2_deinit(struct eap_sm *sm, void *priv) +{ + struct eap_mschapv2_data *data = priv; + free(data->peer_challenge); + free(data->auth_challenge); + free(data); +} + + +static u8 * eap_mschapv2_challenge(struct eap_sm *sm, + struct eap_mschapv2_data *data, + struct eap_method_ret *ret, + struct eap_mschapv2_hdr *req, + size_t *respDataLen) +{ + struct wpa_ssid *config = eap_get_config(sm); + u8 *challenge, *peer_challenge, *username, *pos; + int challenge_len, i, ms_len; + size_t len, username_len; + struct eap_mschapv2_hdr *resp; + u8 password_hash[16], password_hash_hash[16]; + + /* MSCHAPv2 does not include optional domain name in the + * challenge-response calculation, so remove domain prefix + * (if present). */ + username = config->identity; + username_len = config->identity_len; + for (i = 0; i < username_len; i++) { + if (username[i] == '\\') { + username_len -= i + 1; + username += i + 1; + break; + } + } + + wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received challenge"); + len = be_to_host16(req->length); + pos = (u8 *) (req + 1); + challenge_len = *pos++; + if (challenge_len != 16) { + wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Invalid challenge length " + "%d", challenge_len); + ret->ignore = TRUE; + return NULL; + } + + if (len - challenge_len - 10 < 0) { + wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Too short challenge" + " packet: len=%lu challenge_len=%d", + (unsigned long) len, challenge_len); + } + + challenge = pos; + pos += challenge_len; + wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Authentication Servername", + pos, len - challenge_len - 10); + + ret->ignore = FALSE; + ret->methodState = METHOD_CONT; + ret->decision = DECISION_FAIL; + ret->allowNotifications = TRUE; + + wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Generating Challenge Response"); + + *respDataLen = sizeof(*resp) + 1 + MSCHAPV2_RESP_LEN + + config->identity_len; + resp = malloc(*respDataLen); + if (resp == NULL) + return NULL; + memset(resp, 0, *respDataLen); + resp->code = EAP_CODE_RESPONSE; + resp->identifier = req->identifier; + resp->length = host_to_be16(*respDataLen); + resp->type = EAP_TYPE_MSCHAPV2; + resp->op_code = MSCHAPV2_OP_RESPONSE; + resp->mschapv2_id = req->mschapv2_id; + ms_len = *respDataLen - 5; + resp->ms_length[0] = ms_len >> 8; + resp->ms_length[1] = ms_len & 0xff; + pos = (u8 *) (resp + 1); + *pos++ = MSCHAPV2_RESP_LEN; /* Value-Size */ + + /* Response */ + peer_challenge = pos; + if (data->peer_challenge) { + wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: peer_challenge generated " + "in Phase 1"); + peer_challenge = data->peer_challenge; + } else if (hostapd_get_rand(peer_challenge, 16)) { + free(resp); + return NULL; + } + pos += 16; + pos += 8; /* Reserved, must be zero */ + if (data->auth_challenge) { + wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: auth_challenge generated " + "in Phase 1"); + challenge = data->auth_challenge; + } + wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: auth_challenge", challenge, 16); + wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: peer_challenge", + peer_challenge, 16); + wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: username", + username, username_len); + wpa_hexdump_ascii_key(MSG_DEBUG, "EAP-MSCHAPV2: password", + config->password, config->password_len); + generate_nt_response(challenge, peer_challenge, + username, username_len, + config->password, config->password_len, + pos); + wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: response", pos, 24); + /* Authenticator response is not really needed yet, but calculate it + * here so that challenges need not be saved. */ + generate_authenticator_response(config->password, config->password_len, + peer_challenge, challenge, + username, username_len, pos, + data->auth_response); + data->auth_response_valid = 1; + + /* Likewise, generate master_key here since we have the needed data + * available. */ + nt_password_hash(config->password, config->password_len, + password_hash); + hash_nt_password_hash(password_hash, password_hash_hash); + get_master_key(password_hash_hash, pos /* nt_response */, + data->master_key); + data->master_key_valid = 1; + + pos += 24; + pos++; /* Flag / reserved, must be zero */ + + memcpy(pos, config->identity, config->identity_len); + return (u8 *) resp; +} + + +static u8 * eap_mschapv2_success(struct eap_sm *sm, + struct eap_mschapv2_data *data, + struct eap_method_ret *ret, + struct eap_mschapv2_hdr *req, + size_t *respDataLen) +{ + struct eap_mschapv2_hdr *resp; + u8 *pos, recv_response[20]; + int len, left; + + wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received success"); + len = be_to_host16(req->length); + pos = (u8 *) (req + 1); + if (!data->auth_response_valid || len < sizeof(*req) + 42 || + pos[0] != 'S' || pos[1] != '=' || + hexstr2bin((char *) (pos + 2), recv_response, 20) || + memcmp(data->auth_response, recv_response, 20) != 0) { + wpa_printf(MSG_WARNING, "EAP-MSCHAPV2: Invalid authenticator " + "response in success request"); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + return NULL; + } + pos += 42; + left = len - sizeof(*req) - 42; + while (left > 0 && *pos == ' ') { + pos++; + left--; + } + wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Success message", + pos, left); + wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Authentication succeeded"); + *respDataLen = 6; + resp = malloc(6); + if (resp == NULL) { + wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Failed to allocate " + "buffer for success response"); + ret->ignore = TRUE; + return NULL; + } + + resp->code = EAP_CODE_RESPONSE; + resp->identifier = req->identifier; + resp->length = host_to_be16(6); + resp->type = EAP_TYPE_MSCHAPV2; + resp->op_code = MSCHAPV2_OP_SUCCESS; + + ret->methodState = METHOD_DONE; + ret->decision = DECISION_UNCOND_SUCC; + ret->allowNotifications = FALSE; + data->success = 1; + + return (u8 *) resp; +} + + +static int eap_mschapv2_failure_txt(struct eap_sm *sm, + struct eap_mschapv2_data *data, char *txt) +{ + char *pos, *msg = ""; + int retry = 1; + struct wpa_ssid *config = eap_get_config(sm); + + /* For example: + * E=691 R=1 C=<32 octets hex challenge> V=3 M=Authentication Failure + */ + + pos = txt; + + if (pos && strncmp(pos, "E=", 2) == 0) { + pos += 2; + data->prev_error = atoi(pos); + wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: error %d", + data->prev_error); + pos = strchr(pos, ' '); + if (pos) + pos++; + } + + if (pos && strncmp(pos, "R=", 2) == 0) { + pos += 2; + retry = atoi(pos); + wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: retry is %sallowed", + retry == 1 ? "" : "not "); + pos = strchr(pos, ' '); + if (pos) + pos++; + } + + if (pos && strncmp(pos, "C=", 2) == 0) { + int hex_len; + pos += 2; + hex_len = strchr(pos, ' ') - (char *) pos; + if (hex_len == PASSWD_CHANGE_CHAL_LEN * 2) { + if (hexstr2bin(pos, data->passwd_change_challenge, + PASSWD_CHANGE_CHAL_LEN)) { + wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: invalid " + "failure challenge"); + } else { + wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: failure " + "challenge", + data->passwd_change_challenge, + PASSWD_CHANGE_CHAL_LEN); + data->passwd_change_challenge_valid = 1; + } + } else { + wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: invalid failure " + "challenge len %d", hex_len); + } + pos = strchr(pos, ' '); + if (pos) + pos++; + } else { + wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: required challenge field " + "was not present in failure message"); + } + + if (pos && strncmp(pos, "V=", 2) == 0) { + pos += 2; + data->passwd_change_version = atoi(pos); + wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: password changing " + "protocol version %d", data->passwd_change_version); + pos = strchr(pos, ' '); + if (pos) + pos++; + } + + if (pos && strncmp(pos, "M=", 2) == 0) { + pos += 2; + msg = pos; + } + wpa_msg(sm->msg_ctx, MSG_WARNING, + "EAP-MSCHAPV2: failure message: '%s' (retry %sallowed, error " + "%d)", + msg, retry == 1 ? "" : "not ", data->prev_error); + if (retry == 1 && config) { + /* TODO: could prevent the current password from being used + * again at least for some period of time */ + eap_sm_request_identity(sm, config); + eap_sm_request_password(sm, config); + } else if (config) { + /* TODO: prevent retries using same username/password */ + } + + return retry == 1; +} + + +static u8 * eap_mschapv2_failure(struct eap_sm *sm, + struct eap_mschapv2_data *data, + struct eap_method_ret *ret, + struct eap_mschapv2_hdr *req, + size_t *respDataLen) +{ + struct eap_mschapv2_hdr *resp; + u8 *msdata = (u8 *) (req + 1); + char *buf; + int len = be_to_host16(req->length) - sizeof(*req); + int retry = 0; + + wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received failure"); + wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Failure data", + msdata, len); + buf = malloc(len + 1); + if (buf) { + memcpy(buf, msdata, len); + buf[len] = '\0'; + retry = eap_mschapv2_failure_txt(sm, data, buf); + free(buf); + } + + ret->ignore = FALSE; + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + ret->allowNotifications = FALSE; + + if (retry) { + /* TODO: could try to retry authentication, e.g, after having + * changed the username/password. In this case, EAP MS-CHAP-v2 + * Failure Response would not be sent here. */ + } + + *respDataLen = 6; + resp = malloc(6); + if (resp == NULL) { + return NULL; + } + + resp->code = EAP_CODE_RESPONSE; + resp->identifier = req->identifier; + resp->length = host_to_be16(6); + resp->type = EAP_TYPE_MSCHAPV2; + resp->op_code = MSCHAPV2_OP_FAILURE; + + return (u8 *) resp; +} + + +static u8 * eap_mschapv2_process(struct eap_sm *sm, void *priv, + struct eap_method_ret *ret, + u8 *reqData, size_t reqDataLen, + size_t *respDataLen) +{ + struct eap_mschapv2_data *data = priv; + struct wpa_ssid *config = eap_get_config(sm); + struct eap_mschapv2_hdr *req; + int ms_len, len; + + if (config == NULL || config->identity == NULL) { + wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Identity not configured"); + eap_sm_request_identity(sm, config); + ret->ignore = TRUE; + return NULL; + } + + if (config->password == NULL) { + wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Password not configured"); + eap_sm_request_password(sm, config); + ret->ignore = TRUE; + return NULL; + } + + req = (struct eap_mschapv2_hdr *) reqData; + len = be_to_host16(req->length); + if (len < sizeof(*req) + 2 || req->type != EAP_TYPE_MSCHAPV2) { + wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Invalid frame"); + ret->ignore = TRUE; + return NULL; + } + + ms_len = ((int) req->ms_length[0] << 8) | req->ms_length[1]; + if (ms_len != len - 5) { + wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Invalid header: len=%d " + "ms_len=%d", len, ms_len); + if (sm->workaround) { + /* Some authentication servers use invalid ms_len, + * ignore it for interoperability. */ + wpa_printf(MSG_INFO, "EAP-MSCHAPV2: workaround, ignore" + " invalid ms_len"); + } else { + ret->ignore = TRUE; + return NULL; + } + } + + switch (req->op_code) { + case MSCHAPV2_OP_CHALLENGE: + return eap_mschapv2_challenge(sm, data, ret, req, respDataLen); + case MSCHAPV2_OP_SUCCESS: + return eap_mschapv2_success(sm, data, ret, req, respDataLen); + case MSCHAPV2_OP_FAILURE: + return eap_mschapv2_failure(sm, data, ret, req, respDataLen); + default: + wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Unknown op %d - ignored", + req->op_code); + ret->ignore = TRUE; + return NULL; + } +} + + +static Boolean eap_mschapv2_isKeyAvailable(struct eap_sm *sm, void *priv) +{ + struct eap_mschapv2_data *data = priv; + return data->success && data->master_key_valid; +} + + +static u8 * eap_mschapv2_getKey(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_mschapv2_data *data = priv; + u8 *key; + int key_len; + + if (!data->master_key_valid || !data->success) + return NULL; + + if (data->peer_challenge) { + /* EAP-FAST needs both send and receive keys */ + key_len = 2 * MSCHAPV2_KEY_LEN; + } else { + key_len = MSCHAPV2_KEY_LEN; + } + + key = malloc(key_len); + if (key == NULL) + return NULL; + + if (data->peer_challenge) { + get_asymetric_start_key(data->master_key, key, + MSCHAPV2_KEY_LEN, 0, 0); + get_asymetric_start_key(data->master_key, + key + MSCHAPV2_KEY_LEN, + MSCHAPV2_KEY_LEN, 1, 0); + } else { + get_asymetric_start_key(data->master_key, key, + MSCHAPV2_KEY_LEN, 1, 0); + } + + wpa_hexdump_key(MSG_DEBUG, "EAP-MSCHAPV2: Derived key", + key, key_len); + + *len = key_len; + return key; +} + + +const struct eap_method eap_method_mschapv2 = +{ + .method = EAP_TYPE_MSCHAPV2, + .name = "MSCHAPV2", + .init = eap_mschapv2_init, + .deinit = eap_mschapv2_deinit, + .process = eap_mschapv2_process, + .isKeyAvailable = eap_mschapv2_isKeyAvailable, + .getKey = eap_mschapv2_getKey, +}; diff --git a/contrib/wpa_supplicant/eap_otp.c b/contrib/wpa_supplicant/eap_otp.c new file mode 100644 index 0000000..e50de63 --- /dev/null +++ b/contrib/wpa_supplicant/eap_otp.c @@ -0,0 +1,113 @@ +/* + * WPA Supplicant / EAP-OTP (RFC 3748) + * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include "common.h" +#include "eap_i.h" +#include "wpa_supplicant.h" +#include "config_ssid.h" + + +static void * eap_otp_init(struct eap_sm *sm) +{ + return (void *) 1; +} + + +static void eap_otp_deinit(struct eap_sm *sm, void *priv) +{ +} + + +static u8 * eap_otp_process(struct eap_sm *sm, void *priv, + struct eap_method_ret *ret, + u8 *reqData, size_t reqDataLen, + size_t *respDataLen) +{ + struct wpa_ssid *config = eap_get_config(sm); + struct eap_hdr *req, *resp; + u8 *pos, *password; + size_t password_len, len; + + req = (struct eap_hdr *) reqData; + pos = (u8 *) (req + 1); + if (reqDataLen < sizeof(*req) + 1 || *pos != EAP_TYPE_OTP || + (len = be_to_host16(req->length)) > reqDataLen) { + wpa_printf(MSG_INFO, "EAP-OTP: Invalid frame"); + ret->ignore = TRUE; + return NULL; + } + pos++; + wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-OTP: Request message", + pos, len - sizeof(*req) - 1); + + if (config == NULL || + (config->password == NULL && config->otp == NULL)) { + wpa_printf(MSG_INFO, "EAP-OTP: Password not configured"); + eap_sm_request_otp(sm, config, (char *) pos, + len - sizeof(*req) - 1); + ret->ignore = TRUE; + return NULL; + } + + if (config->otp) { + password = config->otp; + password_len = config->otp_len; + } else { + password = config->password; + password_len = config->password_len; + } + + ret->ignore = FALSE; + + ret->methodState = METHOD_DONE; + ret->decision = DECISION_COND_SUCC; + ret->allowNotifications = FALSE; + + *respDataLen = sizeof(struct eap_hdr) + 1 + password_len; + resp = malloc(*respDataLen); + if (resp == NULL) + return NULL; + resp->code = EAP_CODE_RESPONSE; + resp->identifier = req->identifier; + resp->length = host_to_be16(*respDataLen); + pos = (u8 *) (resp + 1); + *pos++ = EAP_TYPE_OTP; + memcpy(pos, password, password_len); + wpa_hexdump_ascii_key(MSG_MSGDUMP, "EAP-OTP: Response", + password, password_len); + + if (config->otp) { + wpa_printf(MSG_DEBUG, "EAP-OTP: Forgetting used password"); + memset(config->otp, 0, config->otp_len); + free(config->otp); + config->otp = NULL; + config->otp_len = 0; + } + + return (u8 *) resp; +} + + +const struct eap_method eap_method_otp = +{ + .method = EAP_TYPE_OTP, + .name = "OTP", + .init = eap_otp_init, + .deinit = eap_otp_deinit, + .process = eap_otp_process, +}; diff --git a/contrib/wpa_supplicant/eap_peap.c b/contrib/wpa_supplicant/eap_peap.c new file mode 100644 index 0000000..27f0793 --- /dev/null +++ b/contrib/wpa_supplicant/eap_peap.c @@ -0,0 +1,820 @@ +/* + * WPA Supplicant / EAP-PEAP (draft-josefsson-pppext-eap-tls-eap-07.txt) + * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include "common.h" +#include "eap_i.h" +#include "eap_tls_common.h" +#include "wpa_supplicant.h" +#include "config_ssid.h" +#include "tls.h" +#include "eap_tlv.h" + + +/* Maximum supported PEAP version + * 0 = Microsoft's PEAP version 0; draft-kamath-pppext-peapv0-00.txt + * 1 = draft-josefsson-ppext-eap-tls-eap-05.txt + * 2 = draft-josefsson-ppext-eap-tls-eap-07.txt + */ +#define EAP_PEAP_VERSION 1 + + +static void eap_peap_deinit(struct eap_sm *sm, void *priv); + + +struct eap_peap_data { + struct eap_ssl_data ssl; + + int peap_version, force_peap_version, force_new_label; + + const struct eap_method *phase2_method; + void *phase2_priv; + int phase2_success; + + u8 phase2_type; + u8 *phase2_types; + size_t num_phase2_types; + + int peap_outer_success; /* 0 = PEAP terminated on Phase 2 inner + * EAP-Success + * 1 = reply with tunneled EAP-Success to inner + * EAP-Success and expect AS to send outer + * (unencrypted) EAP-Success after this + * 2 = reply with PEAP/TLS ACK to inner + * EAP-Success and expect AS to send outer + * (unencrypted) EAP-Success after this */ + int resuming; /* starting a resumed session */ + u8 *key_data; + + u8 *pending_phase2_req; + size_t pending_phase2_req_len; +}; + + +static void * eap_peap_init(struct eap_sm *sm) +{ + struct eap_peap_data *data; + struct wpa_ssid *config = eap_get_config(sm); + + data = malloc(sizeof(*data)); + if (data == NULL) + return NULL; + sm->peap_done = FALSE; + memset(data, 0, sizeof(*data)); + data->peap_version = EAP_PEAP_VERSION; + data->force_peap_version = -1; + data->peap_outer_success = 2; + + if (config && config->phase1) { + char *pos = strstr(config->phase1, "peapver="); + if (pos) { + data->force_peap_version = atoi(pos + 8); + data->peap_version = data->force_peap_version; + wpa_printf(MSG_DEBUG, "EAP-PEAP: Forced PEAP version " + "%d", data->force_peap_version); + } + + if (strstr(config->phase1, "peaplabel=1")) { + data->force_new_label = 1; + wpa_printf(MSG_DEBUG, "EAP-PEAP: Force new label for " + "key derivation"); + } + + if (strstr(config->phase1, "peap_outer_success=0")) { + data->peap_outer_success = 0; + wpa_printf(MSG_DEBUG, "EAP-PEAP: terminate " + "authentication on tunneled EAP-Success"); + } else if (strstr(config->phase1, "peap_outer_success=1")) { + data->peap_outer_success = 1; + wpa_printf(MSG_DEBUG, "EAP-PEAP: send tunneled " + "EAP-Success after receiving tunneled " + "EAP-Success"); + } else if (strstr(config->phase1, "peap_outer_success=2")) { + data->peap_outer_success = 2; + wpa_printf(MSG_DEBUG, "EAP-PEAP: send PEAP/TLS ACK " + "after receiving tunneled EAP-Success"); + } + } + + if (config && config->phase2) { + char *start, *pos, *buf; + u8 method, *methods = NULL, *_methods; + size_t num_methods = 0; + start = buf = strdup(config->phase2); + if (buf == NULL) { + eap_peap_deinit(sm, data); + return NULL; + } + while (start && *start != '\0') { + pos = strstr(start, "auth="); + if (pos == NULL) + break; + if (start != pos && *(pos - 1) != ' ') { + start = pos + 5; + continue; + } + + start = pos + 5; + pos = strchr(start, ' '); + if (pos) + *pos++ = '\0'; + method = eap_get_phase2_type(start); + if (method == EAP_TYPE_NONE) { + wpa_printf(MSG_ERROR, "EAP-PEAP: Unsupported " + "Phase2 method '%s'", start); + } else { + num_methods++; + _methods = realloc(methods, num_methods); + if (_methods == NULL) { + free(methods); + eap_peap_deinit(sm, data); + return NULL; + } + methods = _methods; + methods[num_methods - 1] = method; + } + + start = pos; + } + free(buf); + data->phase2_types = methods; + data->num_phase2_types = num_methods; + } + if (data->phase2_types == NULL) { + data->phase2_types = + eap_get_phase2_types(config, &data->num_phase2_types); + } + if (data->phase2_types == NULL) { + wpa_printf(MSG_ERROR, "EAP-PEAP: No Phase2 method available"); + eap_peap_deinit(sm, data); + return NULL; + } + wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Phase2 EAP types", + data->phase2_types, data->num_phase2_types); + data->phase2_type = EAP_TYPE_NONE; + + if (eap_tls_ssl_init(sm, &data->ssl, config)) { + wpa_printf(MSG_INFO, "EAP-PEAP: Failed to initialize SSL."); + eap_peap_deinit(sm, data); + return NULL; + } + + return data; +} + + +static void eap_peap_deinit(struct eap_sm *sm, void *priv) +{ + struct eap_peap_data *data = priv; + if (data == NULL) + return; + if (data->phase2_priv && data->phase2_method) + data->phase2_method->deinit(sm, data->phase2_priv); + free(data->phase2_types); + eap_tls_ssl_deinit(sm, &data->ssl); + free(data->key_data); + free(data->pending_phase2_req); + free(data); +} + + +static int eap_peap_encrypt(struct eap_sm *sm, struct eap_peap_data *data, + int id, u8 *plain, size_t plain_len, + u8 **out_data, size_t *out_len) +{ + int res; + u8 *pos; + struct eap_hdr *resp; + + /* TODO: add support for fragmentation, if needed. This will need to + * add TLS Message Length field, if the frame is fragmented. + * Note: Microsoft IAS did not seem to like TLS Message Length with + * PEAP/MSCHAPv2. */ + resp = malloc(sizeof(struct eap_hdr) + 2 + data->ssl.tls_out_limit); + if (resp == NULL) + return -1; + + resp->code = EAP_CODE_RESPONSE; + resp->identifier = id; + + pos = (u8 *) (resp + 1); + *pos++ = EAP_TYPE_PEAP; + *pos++ = data->peap_version; + + res = tls_connection_encrypt(sm->ssl_ctx, data->ssl.conn, + plain, plain_len, + pos, data->ssl.tls_out_limit); + if (res < 0) { + wpa_printf(MSG_INFO, "EAP-PEAP: Failed to encrypt Phase 2 " + "data"); + free(resp); + return -1; + } + + *out_len = sizeof(struct eap_hdr) + 2 + res; + resp->length = host_to_be16(*out_len); + *out_data = (u8 *) resp; + return 0; +} + + +static int eap_peap_phase2_nak(struct eap_sm *sm, + struct eap_peap_data *data, + struct eap_hdr *hdr, + u8 **resp, size_t *resp_len) +{ + struct eap_hdr *resp_hdr; + u8 *pos = (u8 *) (hdr + 1); + + wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 Request: Nak type=%d", *pos); + wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Allowed Phase2 EAP types", + data->phase2_types, data->num_phase2_types); + *resp_len = sizeof(struct eap_hdr) + 1 + data->num_phase2_types; + *resp = malloc(*resp_len); + if (*resp == NULL) + return -1; + + resp_hdr = (struct eap_hdr *) (*resp); + resp_hdr->code = EAP_CODE_RESPONSE; + resp_hdr->identifier = hdr->identifier; + resp_hdr->length = host_to_be16(*resp_len); + pos = (u8 *) (resp_hdr + 1); + *pos++ = EAP_TYPE_NAK; + memcpy(pos, data->phase2_types, data->num_phase2_types); + + return 0; +} + + +static int eap_peap_phase2_request(struct eap_sm *sm, + struct eap_peap_data *data, + struct eap_method_ret *ret, + struct eap_hdr *req, + struct eap_hdr *hdr, + u8 **resp, size_t *resp_len) +{ + size_t len = be_to_host16(hdr->length); + u8 *pos; + struct eap_method_ret iret; + struct wpa_ssid *config = eap_get_config(sm); + + if (len <= sizeof(struct eap_hdr)) { + wpa_printf(MSG_INFO, "EAP-PEAP: too short " + "Phase 2 request (len=%lu)", (unsigned long) len); + return -1; + } + pos = (u8 *) (hdr + 1); + wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 Request: type=%d", *pos); + switch (*pos) { + case EAP_TYPE_IDENTITY: + *resp = eap_sm_buildIdentity(sm, req->identifier, resp_len, 1); + break; + case EAP_TYPE_TLV: + memset(&iret, 0, sizeof(iret)); + if (eap_tlv_process(sm, &iret, hdr, resp, resp_len)) { + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + return -1; + } + if (iret.methodState == METHOD_DONE || + iret.methodState == METHOD_MAY_CONT) { + ret->methodState = iret.methodState; + ret->decision = iret.decision; + data->phase2_success = 1; + } + break; + default: + if (data->phase2_type == EAP_TYPE_NONE) { + int i; + for (i = 0; i < data->num_phase2_types; i++) { + if (data->phase2_types[i] != *pos) + continue; + + data->phase2_type = *pos; + wpa_printf(MSG_DEBUG, "EAP-PEAP: Selected " + "Phase 2 EAP method %d", + data->phase2_type); + break; + } + } + if (*pos != data->phase2_type || *pos == EAP_TYPE_NONE) { + if (eap_peap_phase2_nak(sm, data, hdr, resp, resp_len)) + return -1; + return 0; + } + + if (data->phase2_priv == NULL) { + data->phase2_method = eap_sm_get_eap_methods(*pos); + if (data->phase2_method) { + sm->init_phase2 = 1; + data->phase2_priv = + data->phase2_method->init(sm); + sm->init_phase2 = 0; + } + } + if (data->phase2_priv == NULL || data->phase2_method == NULL) { + wpa_printf(MSG_INFO, "EAP-PEAP: failed to initialize " + "Phase 2 EAP method %d", *pos); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + return -1; + } + memset(&iret, 0, sizeof(iret)); + *resp = data->phase2_method->process(sm, data->phase2_priv, + &iret, (u8 *) hdr, len, + resp_len); + if ((iret.methodState == METHOD_DONE || + iret.methodState == METHOD_MAY_CONT) && + (iret.decision == DECISION_UNCOND_SUCC || + iret.decision == DECISION_COND_SUCC)) { + data->phase2_success = 1; + } + break; + } + + if (*resp == NULL && + (config->pending_req_identity || config->pending_req_password || + config->pending_req_otp)) { + free(data->pending_phase2_req); + data->pending_phase2_req = malloc(len); + if (data->pending_phase2_req) { + memcpy(data->pending_phase2_req, hdr, len); + data->pending_phase2_req_len = len; + } + } + + return 0; +} + + +static int eap_peap_decrypt(struct eap_sm *sm, + struct eap_peap_data *data, + struct eap_method_ret *ret, + struct eap_hdr *req, + u8 *in_data, size_t in_len, + u8 **out_data, size_t *out_len) +{ + u8 *in_decrypted; + int buf_len, len_decrypted, len, skip_change = 0, res; + struct eap_hdr *hdr, *rhdr; + u8 *resp = NULL; + size_t resp_len; + + wpa_printf(MSG_DEBUG, "EAP-PEAP: received %lu bytes encrypted data for" + " Phase 2", (unsigned long) in_len); + + if (data->pending_phase2_req) { + wpa_printf(MSG_DEBUG, "EAP-PEAP: Pending Phase 2 request - " + "skip decryption and use old data"); + in_decrypted = data->pending_phase2_req; + data->pending_phase2_req = NULL; + len_decrypted = data->pending_phase2_req_len; + skip_change = 1; + goto continue_req; + } + + res = eap_tls_data_reassemble(sm, &data->ssl, &in_data, &in_len); + if (res < 0 || res == 1) + return res; + + buf_len = in_len; + if (data->ssl.tls_in_total > buf_len) + buf_len = data->ssl.tls_in_total; + in_decrypted = malloc(buf_len); + if (in_decrypted == NULL) { + free(data->ssl.tls_in); + data->ssl.tls_in = NULL; + data->ssl.tls_in_len = 0; + wpa_printf(MSG_WARNING, "EAP-PEAP: failed to allocate memory " + "for decryption"); + return -1; + } + + len_decrypted = tls_connection_decrypt(sm->ssl_ctx, data->ssl.conn, + in_data, in_len, + in_decrypted, buf_len); + free(data->ssl.tls_in); + data->ssl.tls_in = NULL; + data->ssl.tls_in_len = 0; + if (len_decrypted < 0) { + wpa_printf(MSG_INFO, "EAP-PEAP: Failed to decrypt Phase 2 " + "data"); + free(in_decrypted); + return 0; + } + +continue_req: + wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Decrypted Phase 2 EAP", in_decrypted, + len_decrypted); + + hdr = (struct eap_hdr *) in_decrypted; + if (len_decrypted == 5 && hdr->code == EAP_CODE_REQUEST && + be_to_host16(hdr->length) == 5 && + in_decrypted[4] == EAP_TYPE_IDENTITY) { + /* At least FreeRADIUS seems to send full EAP header with + * EAP Request Identity */ + skip_change = 1; + } + if (len_decrypted >= 5 && hdr->code == EAP_CODE_REQUEST && + in_decrypted[4] == EAP_TYPE_TLV) { + skip_change = 1; + } + + if (data->peap_version == 0 && !skip_change) { + struct eap_hdr *nhdr = malloc(sizeof(struct eap_hdr) + + len_decrypted); + if (nhdr == NULL) { + free(in_decrypted); + return 0; + } + memcpy((u8 *) (nhdr + 1), in_decrypted, len_decrypted); + free(in_decrypted); + nhdr->code = req->code; + nhdr->identifier = req->identifier; + nhdr->length = host_to_be16(sizeof(struct eap_hdr) + + len_decrypted); + + len_decrypted += sizeof(struct eap_hdr); + in_decrypted = (u8 *) nhdr; + } + hdr = (struct eap_hdr *) in_decrypted; + if (len_decrypted < sizeof(*hdr)) { + free(in_decrypted); + wpa_printf(MSG_INFO, "EAP-PEAP: Too short Phase 2 " + "EAP frame (len=%d)", len_decrypted); + return 0; + } + len = be_to_host16(hdr->length); + if (len > len_decrypted) { + free(in_decrypted); + wpa_printf(MSG_INFO, "EAP-PEAP: Length mismatch in " + "Phase 2 EAP frame (len=%d hdr->length=%d)", + len_decrypted, len); + return 0; + } + if (len < len_decrypted) { + wpa_printf(MSG_INFO, "EAP-PEAP: Odd.. Phase 2 EAP header has " + "shorter length than full decrypted data (%d < %d)", + len, len_decrypted); + if (sm->workaround && len == 4 && len_decrypted == 5 && + in_decrypted[4] == EAP_TYPE_IDENTITY) { + /* Radiator 3.9 seems to set Phase 2 EAP header to use + * incorrect length for the EAP-Request Identity + * packet, so fix the inner header to interoperate.. + * This was fixed in 2004-06-23 patch for Radiator and + * this workaround can be removed at some point. */ + wpa_printf(MSG_INFO, "EAP-PEAP: workaround -> replace " + "Phase 2 EAP header len (%d) with real " + "decrypted len (%d)", len, len_decrypted); + len = len_decrypted; + hdr->length = host_to_be16(len); + } + } + wpa_printf(MSG_DEBUG, "EAP-PEAP: received Phase 2: code=%d " + "identifier=%d length=%d", hdr->code, hdr->identifier, len); + switch (hdr->code) { + case EAP_CODE_REQUEST: + if (eap_peap_phase2_request(sm, data, ret, req, hdr, + &resp, &resp_len)) { + free(in_decrypted); + wpa_printf(MSG_INFO, "EAP-PEAP: Phase2 Request " + "processing failed"); + return 0; + } + break; + case EAP_CODE_SUCCESS: + wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 Success"); + if (data->peap_version == 1) { + /* EAP-Success within TLS tunnel is used to indicate + * shutdown of the TLS channel. The authentication has + * been completed. */ + wpa_printf(MSG_DEBUG, "EAP-PEAP: Version 1 - " + "EAP-Success within TLS tunnel - " + "authentication completed"); + ret->decision = DECISION_UNCOND_SUCC; + ret->methodState = METHOD_DONE; + data->phase2_success = 1; + if (data->peap_outer_success == 2) { + free(in_decrypted); + wpa_printf(MSG_DEBUG, "EAP-PEAP: Use TLS ACK " + "to finish authentication"); + return 1; + } else if (data->peap_outer_success == 1) { + /* Reply with EAP-Success within the TLS + * channel to complete the authentication. */ + resp_len = sizeof(struct eap_hdr); + resp = malloc(resp_len); + if (resp) { + memset(resp, 0, resp_len); + rhdr = (struct eap_hdr *) resp; + rhdr->code = EAP_CODE_SUCCESS; + rhdr->identifier = hdr->identifier; + rhdr->length = host_to_be16(resp_len); + } + } else { + /* No EAP-Success expected for Phase 1 (outer, + * unencrypted auth), so force EAP state + * machine to SUCCESS state. */ + sm->peap_done = TRUE; + } + } else { + /* FIX: ? */ + } + break; + case EAP_CODE_FAILURE: + wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 Failure"); + ret->decision = DECISION_FAIL; + ret->methodState = METHOD_MAY_CONT; + ret->allowNotifications = FALSE; + /* Reply with EAP-Failure within the TLS channel to complete + * failure reporting. */ + resp_len = sizeof(struct eap_hdr); + resp = malloc(resp_len); + if (resp) { + memset(resp, 0, resp_len); + rhdr = (struct eap_hdr *) resp; + rhdr->code = EAP_CODE_FAILURE; + rhdr->identifier = hdr->identifier; + rhdr->length = host_to_be16(resp_len); + } + break; + default: + wpa_printf(MSG_INFO, "EAP-PEAP: Unexpected code=%d in " + "Phase 2 EAP header", hdr->code); + break; + } + + free(in_decrypted); + + if (resp) { + u8 *resp_pos; + size_t resp_send_len; + int skip_change = 0; + + wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: Encrypting Phase 2 data", + resp, resp_len); + /* PEAP version changes */ + if (resp_len >= 5 && resp[0] == EAP_CODE_RESPONSE && + resp[4] == EAP_TYPE_TLV) + skip_change = 1; + if (data->peap_version == 0 && !skip_change) { + resp_pos = resp + sizeof(struct eap_hdr); + resp_send_len = resp_len - sizeof(struct eap_hdr); + } else { + resp_pos = resp; + resp_send_len = resp_len; + } + + if (eap_peap_encrypt(sm, data, req->identifier, + resp_pos, resp_send_len, + out_data, out_len)) { + wpa_printf(MSG_INFO, "EAP-PEAP: Failed to encrypt " + "a Phase 2 frame"); + } + free(resp); + } + + return 0; +} + + +static u8 * eap_peap_process(struct eap_sm *sm, void *priv, + struct eap_method_ret *ret, + u8 *reqData, size_t reqDataLen, + size_t *respDataLen) +{ + struct eap_hdr *req; + int left, res; + unsigned int tls_msg_len; + u8 flags, *pos, *resp, id; + struct eap_peap_data *data = priv; + + if (tls_get_errors(sm->ssl_ctx)) { + wpa_printf(MSG_INFO, "EAP-PEAP: TLS errors detected"); + ret->ignore = TRUE; + return NULL; + } + + req = (struct eap_hdr *) reqData; + pos = (u8 *) (req + 1); + if (reqDataLen < sizeof(*req) + 2 || *pos != EAP_TYPE_PEAP || + (left = be_to_host16(req->length)) > reqDataLen) { + wpa_printf(MSG_INFO, "EAP-PEAP: Invalid frame"); + ret->ignore = TRUE; + return NULL; + } + left -= sizeof(struct eap_hdr); + id = req->identifier; + pos++; + flags = *pos++; + left -= 2; + wpa_printf(MSG_DEBUG, "EAP-PEAP: Received packet(len=%lu) - " + "Flags 0x%02x", (unsigned long) reqDataLen, flags); + if (flags & EAP_TLS_FLAGS_LENGTH_INCLUDED) { + if (left < 4) { + wpa_printf(MSG_INFO, "EAP-PEAP: Short frame with TLS " + "length"); + ret->ignore = TRUE; + return NULL; + } + tls_msg_len = (pos[0] << 24) | (pos[1] << 16) | (pos[2] << 8) | + pos[3]; + wpa_printf(MSG_DEBUG, "EAP-PEAP: TLS Message Length: %d", + tls_msg_len); + if (data->ssl.tls_in_left == 0) { + data->ssl.tls_in_total = tls_msg_len; + data->ssl.tls_in_left = tls_msg_len; + free(data->ssl.tls_in); + data->ssl.tls_in = NULL; + data->ssl.tls_in_len = 0; + } + pos += 4; + left -= 4; + } + + ret->ignore = FALSE; + ret->methodState = METHOD_CONT; + ret->decision = DECISION_FAIL; + ret->allowNotifications = TRUE; + + if (flags & EAP_TLS_FLAGS_START) { + wpa_printf(MSG_DEBUG, "EAP-PEAP: Start (server ver=%d, own " + "ver=%d)", flags & EAP_PEAP_VERSION_MASK, + data->peap_version); + if ((flags & EAP_PEAP_VERSION_MASK) < data->peap_version) + data->peap_version = flags & EAP_PEAP_VERSION_MASK; + if (data->force_peap_version >= 0 && + data->force_peap_version != data->peap_version) { + wpa_printf(MSG_WARNING, "EAP-PEAP: Failed to select " + "forced PEAP version %d", + data->force_peap_version); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + ret->allowNotifications = FALSE; + return NULL; + } + wpa_printf(MSG_DEBUG, "EAP-PEAP: Using PEAP version %d", + data->peap_version); + left = 0; /* make sure that this frame is empty, even though it + * should always be, anyway */ + } + + resp = NULL; + if (tls_connection_established(sm->ssl_ctx, data->ssl.conn) && + !data->resuming) { + res = eap_peap_decrypt(sm, data, ret, req, pos, left, + &resp, respDataLen); + } else { + res = eap_tls_process_helper(sm, &data->ssl, EAP_TYPE_PEAP, + data->peap_version, id, pos, left, + &resp, respDataLen); + + if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) { + char *label; + wpa_printf(MSG_DEBUG, + "EAP-PEAP: TLS done, proceed to Phase 2"); + free(data->key_data); + /* draft-josefsson-ppext-eap-tls-eap-05.txt + * specifies that PEAPv1 would use "client PEAP + * encryption" as the label. However, most existing + * PEAPv1 implementations seem to be using the old + * label, "client EAP encryption", instead. Use the old + * label by default, but allow it to be configured with + * phase1 parameter peaplabel=1. */ + if (data->peap_version > 1 || data->force_new_label) + label = "client PEAP encryption"; + else + label = "client EAP encryption"; + wpa_printf(MSG_DEBUG, "EAP-PEAP: using label '%s' in " + "key derivation", label); + data->key_data = + eap_tls_derive_key(sm, &data->ssl, label, + EAP_TLS_KEY_LEN); + if (data->key_data) { + wpa_hexdump_key(MSG_DEBUG, + "EAP-PEAP: Derived key", + data->key_data, + EAP_TLS_KEY_LEN); + } else { + wpa_printf(MSG_DEBUG, "EAP-PEAP: Failed to " + "derive key"); + } + data->resuming = 0; + } + } + + if (ret->methodState == METHOD_DONE) { + ret->allowNotifications = FALSE; + } + + if (res == 1) { + return eap_tls_build_ack(&data->ssl, respDataLen, id, + EAP_TYPE_PEAP, data->peap_version); + } + + return resp; +} + + +static Boolean eap_peap_has_reauth_data(struct eap_sm *sm, void *priv) +{ + struct eap_peap_data *data = priv; + return tls_connection_established(sm->ssl_ctx, data->ssl.conn) && + data->phase2_success; +} + + +static void eap_peap_deinit_for_reauth(struct eap_sm *sm, void *priv) +{ + struct eap_peap_data *data = priv; + free(data->pending_phase2_req); + data->pending_phase2_req = NULL; +} + + +static void * eap_peap_init_for_reauth(struct eap_sm *sm, void *priv) +{ + struct eap_peap_data *data = priv; + free(data->key_data); + data->key_data = NULL; + if (eap_tls_reauth_init(sm, &data->ssl)) { + free(data); + return NULL; + } + data->phase2_success = 0; + data->resuming = 1; + sm->peap_done = FALSE; + return priv; +} + + +static int eap_peap_get_status(struct eap_sm *sm, void *priv, char *buf, + size_t buflen, int verbose) +{ + struct eap_peap_data *data = priv; + int len; + + len = eap_tls_status(sm, &data->ssl, buf, buflen, verbose); + if (data->phase2_method) { + len += snprintf(buf + len, buflen - len, + "EAP-PEAPv%d Phase2 method=%s\n", + data->peap_version, data->phase2_method->name); + } + return len; +} + + +static Boolean eap_peap_isKeyAvailable(struct eap_sm *sm, void *priv) +{ + struct eap_peap_data *data = priv; + return data->key_data != NULL && data->phase2_success; +} + + +static u8 * eap_peap_getKey(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_peap_data *data = priv; + u8 *key; + + if (data->key_data == NULL || !data->phase2_success) + return NULL; + + key = malloc(EAP_TLS_KEY_LEN); + if (key == NULL) + return NULL; + + *len = EAP_TLS_KEY_LEN; + memcpy(key, data->key_data, EAP_TLS_KEY_LEN); + + return key; +} + + +const struct eap_method eap_method_peap = +{ + .method = EAP_TYPE_PEAP, + .name = "PEAP", + .init = eap_peap_init, + .deinit = eap_peap_deinit, + .process = eap_peap_process, + .isKeyAvailable = eap_peap_isKeyAvailable, + .getKey = eap_peap_getKey, + .get_status = eap_peap_get_status, + .has_reauth_data = eap_peap_has_reauth_data, + .deinit_for_reauth = eap_peap_deinit_for_reauth, + .init_for_reauth = eap_peap_init_for_reauth, +}; diff --git a/contrib/wpa_supplicant/eap_psk.c b/contrib/wpa_supplicant/eap_psk.c new file mode 100644 index 0000000..3b325b5 --- /dev/null +++ b/contrib/wpa_supplicant/eap_psk.c @@ -0,0 +1,563 @@ +/* + * WPA Supplicant / EAP-PSK (draft-bersani-eap-psk-05.txt) + * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include "common.h" +#include "eap_i.h" +#include "wpa_supplicant.h" +#include "config_ssid.h" +#include "md5.h" +#include "aes_wrap.h" + + +/* draft-bersani-eap-psk-03.txt mode. This is retained for interop testing and + * will be removed once an AS that supports draft5 becomes available. */ +#define EAP_PSK_DRAFT3 + +#define EAP_PSK_RAND_LEN 16 +#define EAP_PSK_MAC_LEN 16 +#define EAP_PSK_TEK_LEN 16 +#define EAP_PSK_MSK_LEN 64 + +#define EAP_PSK_R_FLAG_CONT 1 +#define EAP_PSK_R_FLAG_DONE_SUCCESS 2 +#define EAP_PSK_R_FLAG_DONE_FAILURE 3 + +/* EAP-PSK First Message (AS -> Supplicant) */ +struct eap_psk_hdr_1 { + u8 code; + u8 identifier; + u16 length; /* including code, identifier, and length */ + u8 type; /* EAP_TYPE_PSK */ +#ifndef EAP_PSK_DRAFT3 + u8 flags; +#endif /* EAP_PSK_DRAFT3 */ + u8 rand_s[EAP_PSK_RAND_LEN]; +#ifndef EAP_PSK_DRAFT3 + /* Followed by variable length ID_S */ +#endif /* EAP_PSK_DRAFT3 */ +} __attribute__ ((packed)); + +/* EAP-PSK Second Message (Supplicant -> AS) */ +struct eap_psk_hdr_2 { + u8 code; + u8 identifier; + u16 length; /* including code, identifier, and length */ + u8 type; /* EAP_TYPE_PSK */ +#ifndef EAP_PSK_DRAFT3 + u8 flags; + u8 rand_s[EAP_PSK_RAND_LEN]; +#endif /* EAP_PSK_DRAFT3 */ + u8 rand_p[EAP_PSK_RAND_LEN]; + u8 mac_p[EAP_PSK_MAC_LEN]; + /* Followed by variable length ID_P */ +} __attribute__ ((packed)); + +/* EAP-PSK Third Message (AS -> Supplicant) */ +struct eap_psk_hdr_3 { + u8 code; + u8 identifier; + u16 length; /* including code, identifier, and length */ + u8 type; /* EAP_TYPE_PSK */ +#ifndef EAP_PSK_DRAFT3 + u8 flags; + u8 rand_s[EAP_PSK_RAND_LEN]; +#endif /* EAP_PSK_DRAFT3 */ + u8 mac_s[EAP_PSK_MAC_LEN]; + /* Followed by variable length PCHANNEL */ +} __attribute__ ((packed)); + +/* EAP-PSK Fourth Message (Supplicant -> AS) */ +struct eap_psk_hdr_4 { + u8 code; + u8 identifier; + u16 length; /* including code, identifier, and length */ + u8 type; /* EAP_TYPE_PSK */ +#ifndef EAP_PSK_DRAFT3 + u8 flags; + u8 rand_s[EAP_PSK_RAND_LEN]; +#endif /* EAP_PSK_DRAFT3 */ + /* Followed by variable length PCHANNEL */ +} __attribute__ ((packed)); + + + +struct eap_psk_data { + enum { PSK_INIT, PSK_MAC_SENT, PSK_DONE } state; + u8 rand_s[EAP_PSK_RAND_LEN]; + u8 rand_p[EAP_PSK_RAND_LEN]; + u8 ak[16], kdk[16], tek[EAP_PSK_TEK_LEN]; + u8 *id_s, *id_p; + size_t id_s_len, id_p_len; + u8 key_data[EAP_PSK_MSK_LEN]; +}; + + +#define aes_block_size 16 + + +static void eap_psk_key_setup(const u8 *psk, u8 *ak, u8 *kdk) +{ + memset(ak, 0, aes_block_size); + aes_128_encrypt_block(psk, ak, ak); + memcpy(kdk, ak, aes_block_size); + ak[aes_block_size - 1] ^= 0x01; + kdk[aes_block_size - 1] ^= 0x02; + aes_128_encrypt_block(psk, ak, ak); + aes_128_encrypt_block(psk, kdk, kdk); +} + + +static void eap_psk_derive_keys(const u8 *kdk, const u8 *rb, u8 *tek, u8 *msk) +{ + u8 hash[aes_block_size]; + u8 counter = 1; + int i; + + aes_128_encrypt_block(kdk, rb, hash); + + hash[aes_block_size - 1] ^= counter; + aes_128_encrypt_block(kdk, hash, tek); + hash[aes_block_size - 1] ^= counter; + counter++; + + for (i = 0; i < EAP_PSK_MSK_LEN / aes_block_size; i++) { + hash[aes_block_size - 1] ^= counter; + aes_128_encrypt_block(kdk, hash, &msk[i * aes_block_size]); + hash[aes_block_size - 1] ^= counter; + counter++; + } +} + + +static void * eap_psk_init(struct eap_sm *sm) +{ + struct wpa_ssid *config = eap_get_config(sm); + struct eap_psk_data *data; + + if (config == NULL || !config->eappsk) { + wpa_printf(MSG_INFO, "EAP-PSK: pre-shared key not configured"); + return NULL; + } + + data = malloc(sizeof(*data)); + if (data == NULL) + return NULL; + memset(data, 0, sizeof(*data)); + eap_psk_key_setup(config->eappsk, data->ak, data->kdk); + wpa_hexdump_key(MSG_DEBUG, "EAP-PSK: AK", data->ak, 16); + wpa_hexdump_key(MSG_DEBUG, "EAP-PSK: KDK", data->kdk, 16); + data->state = PSK_INIT; + + if (config->nai) { + data->id_p = malloc(config->nai_len); + if (data->id_p) + memcpy(data->id_p, config->nai, config->nai_len); + data->id_p_len = config->nai_len; + } + if (data->id_p == NULL) { + wpa_printf(MSG_INFO, "EAP-PSK: could not get own identity"); + free(data); + return NULL; + } + +#ifdef EAP_PSK_DRAFT3 + if (config->server_nai) { + data->id_s = malloc(config->server_nai_len); + if (data->id_s) + memcpy(data->id_s, config->server_nai, + config->server_nai_len); + data->id_s_len = config->server_nai_len; + } + if (data->id_s == NULL) { + wpa_printf(MSG_INFO, "EAP-PSK: could not get server identity"); + free(data->id_p); + free(data); + return NULL; + } +#endif /* EAP_PSK_DRAFT3 */ + + return data; +} + + +static void eap_psk_deinit(struct eap_sm *sm, void *priv) +{ + struct eap_psk_data *data = priv; + free(data->id_s); + free(data->id_p); + free(data); +} + + +static u8 * eap_psk_process_1(struct eap_sm *sm, struct eap_psk_data *data, + struct eap_method_ret *ret, + u8 *reqData, size_t reqDataLen, + size_t *respDataLen) +{ + struct eap_psk_hdr_1 *hdr1; + struct eap_psk_hdr_2 *hdr2; + u8 *resp, *buf, *pos; + size_t buflen; + + wpa_printf(MSG_DEBUG, "EAP-PSK: in INIT state"); + + hdr1 = (struct eap_psk_hdr_1 *) reqData; + if (reqDataLen < sizeof(*hdr1) || + be_to_host16(hdr1->length) < sizeof(*hdr1) || + be_to_host16(hdr1->length) > reqDataLen) { + wpa_printf(MSG_INFO, "EAP-PSK: Invalid first message " + "length (%lu %d; expected %lu or more)", + (unsigned long) reqDataLen, + be_to_host16(hdr1->length), + (unsigned long) sizeof(*hdr1)); + ret->ignore = TRUE; + return NULL; + } +#ifndef EAP_PSK_DRAFT3 + wpa_printf(MSG_DEBUG, "EAP-PSK: Flags=0x%x", hdr1->flags); + if ((hdr1->flags & 0x03) != 0) { + wpa_printf(MSG_INFO, "EAP-PSK: Unexpected T=%d (expected 0)", + hdr1->flags & 0x03); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + return NULL; + } +#endif /* EAP_PSK_DRAFT3 */ + wpa_hexdump(MSG_DEBUG, "EAP-PSK: RAND_S", hdr1->rand_s, + EAP_PSK_RAND_LEN); + memcpy(data->rand_s, hdr1->rand_s, EAP_PSK_RAND_LEN); +#ifndef EAP_PSK_DRAFT3 + free(data->id_s); + data->id_s_len = be_to_host16(hdr1->length) - sizeof(*hdr1); + data->id_s = malloc(data->id_s_len); + if (data->id_s == NULL) { + wpa_printf(MSG_ERROR, "EAP-PSK: Failed to allocate memory for " + "ID_S (len=%d)", data->id_s_len); + ret->ignore = TRUE; + return NULL; + } + memcpy(data->id_s, (u8 *) (hdr1 + 1), data->id_s_len); + wpa_hexdump_ascii(MSG_DEBUG, "EAP-PSK: ID_S", + data->id_s, data->id_s_len); +#endif /* EAP_PSK_DRAFT3 */ + + if (hostapd_get_rand(data->rand_p, EAP_PSK_RAND_LEN)) { + wpa_printf(MSG_ERROR, "EAP-PSK: Failed to get random data"); + ret->ignore = TRUE; + return NULL; + } + + *respDataLen = sizeof(*hdr2) + data->id_p_len; + resp = malloc(*respDataLen); + if (resp == NULL) + return NULL; + hdr2 = (struct eap_psk_hdr_2 *) resp; + hdr2->code = EAP_CODE_RESPONSE; + hdr2->identifier = hdr1->identifier; + hdr2->length = host_to_be16(*respDataLen); + hdr2->type = EAP_TYPE_PSK; +#ifndef EAP_PSK_DRAFT3 + hdr2->flags = 1; /* T=1 */ + memcpy(hdr2->rand_s, hdr1->rand_s, EAP_PSK_RAND_LEN); +#endif /* EAP_PSK_DRAFT3 */ + memcpy(hdr2->rand_p, data->rand_p, EAP_PSK_RAND_LEN); + memcpy((u8 *) (hdr2 + 1), data->id_p, data->id_p_len); + /* MAC_P = OMAC1-AES-128(AK, ID_P||ID_S||RAND_S||RAND_P) */ + buflen = data->id_p_len + data->id_s_len + 2 * EAP_PSK_RAND_LEN; + buf = malloc(buflen); + if (buf == NULL) { + free(resp); + return NULL; + } + memcpy(buf, data->id_p, data->id_p_len); + pos = buf + data->id_p_len; + memcpy(pos, data->id_s, data->id_s_len); + pos += data->id_s_len; + memcpy(pos, data->rand_s, EAP_PSK_RAND_LEN); + pos += EAP_PSK_RAND_LEN; + memcpy(pos, data->rand_p, EAP_PSK_RAND_LEN); + omac1_aes_128(data->ak, buf, buflen, hdr2->mac_p); + free(buf); + wpa_hexdump(MSG_DEBUG, "EAP-PSK: RAND_P", hdr2->rand_p, + EAP_PSK_RAND_LEN); + wpa_hexdump(MSG_DEBUG, "EAP-PSK: MAC_P", hdr2->mac_p, EAP_PSK_MAC_LEN); + wpa_hexdump_ascii(MSG_DEBUG, "EAP-PSK: ID_P", + (u8 *) (hdr2 + 1), data->id_p_len); + + data->state = PSK_MAC_SENT; + + return resp; +} + + +static u8 * eap_psk_process_3(struct eap_sm *sm, struct eap_psk_data *data, + struct eap_method_ret *ret, + u8 *reqData, size_t reqDataLen, + size_t *respDataLen) +{ + struct eap_psk_hdr_3 *hdr3; + struct eap_psk_hdr_4 *hdr4; + u8 *resp, *buf, *pchannel, *tag, *msg, nonce[16]; + u8 mac[EAP_PSK_MAC_LEN]; + size_t buflen, left; + int failed = 0; + + wpa_printf(MSG_DEBUG, "EAP-PSK: in MAC_SENT state"); + + hdr3 = (struct eap_psk_hdr_3 *) reqData; + left = be_to_host16(hdr3->length); + if (left < sizeof(*hdr3) || reqDataLen < left) { + wpa_printf(MSG_INFO, "EAP-PSK: Invalid third message " + "length (%lu %d; expected %lu)", + (unsigned long) reqDataLen, + be_to_host16(hdr3->length), + (unsigned long) sizeof(*hdr3)); + ret->ignore = TRUE; + return NULL; + } + left -= sizeof(*hdr3); + pchannel = (u8 *) (hdr3 + 1); +#ifndef EAP_PSK_DRAFT3 + wpa_printf(MSG_DEBUG, "EAP-PSK: Flags=0x%x", hdr3->flags); + if ((hdr3->flags & 0x03) != 2) { + wpa_printf(MSG_INFO, "EAP-PSK: Unexpected T=%d (expected 2)", + hdr3->flags & 0x03); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + return NULL; + } + wpa_hexdump(MSG_DEBUG, "EAP-PSK: RAND_S", hdr3->rand_s, + EAP_PSK_RAND_LEN); + /* TODO: would not need to store RAND_S since it is available in this + * message. For now, since we store this anyway, verify that it matches + * with whatever the server is sending. */ + if (memcmp(hdr3->rand_s, data->rand_s, EAP_PSK_RAND_LEN) != 0) { + wpa_printf(MSG_ERROR, "EAP-PSK: RAND_S did not match"); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + return NULL; + } +#endif /* EAP_PSK_DRAFT3 */ + wpa_hexdump(MSG_DEBUG, "EAP-PSK: MAC_S", hdr3->mac_s, EAP_PSK_MAC_LEN); + wpa_hexdump(MSG_DEBUG, "EAP-PSK: PCHANNEL", pchannel, left); + + if (left < 4 + 16 + 1) { + wpa_printf(MSG_INFO, "EAP-PSK: Too short PCHANNEL data in " + "third message (len=%lu, expected 21)", + (unsigned long) left); + ret->ignore = TRUE; + return NULL; + } + + /* MAC_S = OMAC1-AES-128(AK, ID_S||RAND_P) */ + buflen = data->id_s_len + EAP_PSK_RAND_LEN; + buf = malloc(buflen); + if (buf == NULL) + return NULL; + memcpy(buf, data->id_s, data->id_s_len); + memcpy(buf + data->id_s_len, data->rand_p, EAP_PSK_RAND_LEN); + omac1_aes_128(data->ak, buf, buflen, mac); + free(buf); + if (memcmp(mac, hdr3->mac_s, EAP_PSK_MAC_LEN) != 0) { + wpa_printf(MSG_WARNING, "EAP-PSK: Invalid MAC_S in third " + "message"); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + return NULL; + } + wpa_printf(MSG_DEBUG, "EAP-PSK: MAC_S verified successfully"); + + eap_psk_derive_keys(data->kdk, data->rand_p, data->tek, + data->key_data); + wpa_hexdump_key(MSG_DEBUG, "EAP-PSK: TEK", data->tek, EAP_PSK_TEK_LEN); + wpa_hexdump_key(MSG_DEBUG, "EAP-PSK: MSK", data->key_data, + EAP_PSK_MSK_LEN); + + memset(nonce, 0, 12); + memcpy(nonce + 12, pchannel, 4); + pchannel += 4; + left -= 4; + + tag = pchannel; + pchannel += 16; + left -= 16; + + msg = pchannel; + + wpa_hexdump(MSG_MSGDUMP, "EAP-PSK: PCHANNEL - nonce", + nonce, sizeof(nonce)); + wpa_hexdump(MSG_MSGDUMP, "EAP-PSK: PCHANNEL - hdr", reqData, 5); + wpa_hexdump(MSG_MSGDUMP, "EAP-PSK: PCHANNEL - cipher msg", msg, left); + +#ifdef EAP_PSK_DRAFT3 + if (aes_128_eax_decrypt(data->tek, nonce, sizeof(nonce), + reqData, 5, msg, left, tag)) +#else /* EAP_PSK_DRAFT3 */ + if (aes_128_eax_decrypt(data->tek, nonce, sizeof(nonce), + reqData, 22, msg, left, tag)) +#endif /* EAP_PSK_DRAFT3 */ + { + wpa_printf(MSG_WARNING, "EAP-PSK: PCHANNEL decryption failed"); + return NULL; + } + wpa_hexdump(MSG_DEBUG, "EAP-PSK: Decrypted PCHANNEL message", + msg, left); + + /* Verify R flag */ + switch (msg[0] >> 6) { + case EAP_PSK_R_FLAG_CONT: + wpa_printf(MSG_DEBUG, "EAP-PSK: R flag - CONT - unsupported"); + return NULL; + case EAP_PSK_R_FLAG_DONE_SUCCESS: + wpa_printf(MSG_DEBUG, "EAP-PSK: R flag - DONE_SUCCESS"); + break; + case EAP_PSK_R_FLAG_DONE_FAILURE: + wpa_printf(MSG_DEBUG, "EAP-PSK: R flag - DONE_FAILURE"); + wpa_printf(MSG_INFO, "EAP-PSK: Authentication server rejected " + "authentication"); + failed = 1; + break; + } + + *respDataLen = sizeof(*hdr4) + 4 + 16 + 1; + resp = malloc(*respDataLen); + if (resp == NULL) + return NULL; + hdr4 = (struct eap_psk_hdr_4 *) resp; + hdr4->code = EAP_CODE_RESPONSE; + hdr4->identifier = hdr3->identifier; + hdr4->length = host_to_be16(*respDataLen); + hdr4->type = EAP_TYPE_PSK; +#ifndef EAP_PSK_DRAFT3 + hdr4->flags = 3; /* T=3 */ + memcpy(hdr4->rand_s, hdr3->rand_s, EAP_PSK_RAND_LEN); +#endif /* EAP_PSK_DRAFT3 */ + pchannel = (u8 *) (hdr4 + 1); + + /* nonce++ */ + inc_byte_array(nonce, sizeof(nonce)); + memcpy(pchannel, nonce + 12, 4); + + pchannel[4 + 16] = EAP_PSK_R_FLAG_DONE_SUCCESS << 6; + + wpa_hexdump(MSG_DEBUG, "EAP-PSK: reply message (plaintext)", + pchannel + 4 + 16, 1); +#ifdef EAP_PSK_DRAFT3 + aes_128_eax_encrypt(data->tek, nonce, sizeof(nonce), resp, 5, + pchannel + 4 + 16, 1, pchannel + 4); +#else /* EAP_PSK_DRAFT3 */ + aes_128_eax_encrypt(data->tek, nonce, sizeof(nonce), resp, 22, + pchannel + 4 + 16, 1, pchannel + 4); +#endif /* EAP_PSK_DRAFT3 */ + wpa_hexdump(MSG_DEBUG, "EAP-PSK: reply message (PCHANNEL)", + pchannel, 4 + 16 + 1); + + wpa_printf(MSG_DEBUG, "EAP-PSK: Completed %ssuccessfully", + failed ? "un" : ""); + data->state = PSK_DONE; + ret->methodState = METHOD_DONE; + ret->decision = failed ? DECISION_FAIL : DECISION_UNCOND_SUCC; + + return resp; +} + + +static u8 * eap_psk_process(struct eap_sm *sm, void *priv, + struct eap_method_ret *ret, + u8 *reqData, size_t reqDataLen, + size_t *respDataLen) +{ + struct eap_psk_data *data = priv; + struct eap_hdr *req; + u8 *pos, *resp = NULL; + size_t len; + + req = (struct eap_hdr *) reqData; + pos = (u8 *) (req + 1); + if (reqDataLen < sizeof(*req) + 1 || *pos != EAP_TYPE_PSK || + (len = be_to_host16(req->length)) > reqDataLen) { + wpa_printf(MSG_INFO, "EAP-PSK: Invalid frame"); + ret->ignore = TRUE; + return NULL; + } + + ret->ignore = FALSE; + ret->methodState = METHOD_CONT; + ret->decision = DECISION_FAIL; + ret->allowNotifications = TRUE; + + switch (data->state) { + case PSK_INIT: + resp = eap_psk_process_1(sm, data, ret, reqData, len, + respDataLen); + break; + case PSK_MAC_SENT: + resp = eap_psk_process_3(sm, data, ret, reqData, len, + respDataLen); + break; + case PSK_DONE: + wpa_printf(MSG_DEBUG, "EAP-PSK: in DONE state - ignore " + "unexpected message"); + ret->ignore = TRUE; + return NULL; + } + + if (ret->methodState == METHOD_DONE) { + ret->allowNotifications = FALSE; + } + + return resp; +} + + +static Boolean eap_psk_isKeyAvailable(struct eap_sm *sm, void *priv) +{ + struct eap_psk_data *data = priv; + return data->state == PSK_DONE; +} + + +static u8 * eap_psk_getKey(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_psk_data *data = priv; + u8 *key; + + if (data->state != PSK_DONE) + return NULL; + + key = malloc(EAP_PSK_MSK_LEN); + if (key == NULL) + return NULL; + + *len = EAP_PSK_MSK_LEN; + memcpy(key, data->key_data, EAP_PSK_MSK_LEN); + + return key; +} + + +const struct eap_method eap_method_psk = +{ + .method = EAP_TYPE_PSK, + .name = "PSK", + .init = eap_psk_init, + .deinit = eap_psk_deinit, + .process = eap_psk_process, + .isKeyAvailable = eap_psk_isKeyAvailable, + .getKey = eap_psk_getKey, +}; diff --git a/contrib/wpa_supplicant/eap_sim.c b/contrib/wpa_supplicant/eap_sim.c new file mode 100644 index 0000000..f7ce191 --- /dev/null +++ b/contrib/wpa_supplicant/eap_sim.c @@ -0,0 +1,1004 @@ +/* + * WPA Supplicant / EAP-SIM (draft-haverinen-pppext-eap-sim-13.txt) + * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include "common.h" +#include "eap_i.h" +#include "wpa_supplicant.h" +#include "config_ssid.h" +#include "sha1.h" +#include "pcsc_funcs.h" +#include "eap_sim_common.h" + +#define EAP_SIM_VERSION 1 + +/* EAP-SIM Subtypes */ +#define EAP_SIM_SUBTYPE_START 10 +#define EAP_SIM_SUBTYPE_CHALLENGE 11 +#define EAP_SIM_SUBTYPE_NOTIFICATION 12 +#define EAP_SIM_SUBTYPE_REAUTHENTICATION 13 +#define EAP_SIM_SUBTYPE_CLIENT_ERROR 14 + +/* AT_CLIENT_ERROR_CODE error codes */ +#define EAP_SIM_UNABLE_TO_PROCESS_PACKET 0 +#define EAP_SIM_UNSUPPORTED_VERSION 1 +#define EAP_SIM_INSUFFICIENT_NUM_OF_CHAL 2 +#define EAP_SIM_RAND_NOT_FRESH 3 + +#define KC_LEN 8 +#define SRES_LEN 4 +#define EAP_SIM_MAX_FAST_REAUTHS 1000 + +struct eap_sim_data { + u8 *ver_list; + size_t ver_list_len; + int selected_version; + int min_num_chal, num_chal; + + u8 kc[3][KC_LEN]; + u8 sres[3][SRES_LEN]; + u8 nonce_mt[EAP_SIM_NONCE_MT_LEN], nonce_s[EAP_SIM_NONCE_S_LEN]; + u8 mk[EAP_SIM_MK_LEN]; + u8 k_aut[EAP_SIM_K_AUT_LEN]; + u8 k_encr[EAP_SIM_K_ENCR_LEN]; + u8 msk[EAP_SIM_KEYING_DATA_LEN]; + u8 rand[3][GSM_RAND_LEN]; + + int num_id_req, num_notification; + u8 *pseudonym; + size_t pseudonym_len; + u8 *reauth_id; + size_t reauth_id_len; + int reauth; + unsigned int counter, counter_too_small; + u8 *last_eap_identity; + size_t last_eap_identity_len; + enum { CONTINUE, SUCCESS, FAILURE } state; +}; + + +static void * eap_sim_init(struct eap_sm *sm) +{ + struct eap_sim_data *data; + struct wpa_ssid *config = eap_get_config(sm); + + data = malloc(sizeof(*data)); + if (data == NULL) + return NULL; + memset(data, 0, sizeof(*data)); + + if (hostapd_get_rand(data->nonce_mt, EAP_SIM_NONCE_MT_LEN)) { + wpa_printf(MSG_WARNING, "EAP-SIM: Failed to get random data " + "for NONCE_MT"); + free(data); + return NULL; + } + + data->min_num_chal = 2; + if (config && config->phase1) { + char *pos = strstr(config->phase1, "sim_min_num_chal="); + if (pos) { + data->min_num_chal = atoi(pos + 17); + if (data->min_num_chal < 2 || data->min_num_chal > 3) { + wpa_printf(MSG_WARNING, "EAP-SIM: Invalid " + "sim_min_num_chal configuration " + "(%d, expected 2 or 3)", + data->min_num_chal); + free(data); + return NULL; + } + wpa_printf(MSG_DEBUG, "EAP-SIM: Set minimum number of " + "challenges to %d", data->min_num_chal); + } + } + + data->state = CONTINUE; + + return data; +} + + +static void eap_sim_deinit(struct eap_sm *sm, void *priv) +{ + struct eap_sim_data *data = priv; + if (data) { + free(data->ver_list); + free(data->pseudonym); + free(data->reauth_id); + free(data->last_eap_identity); + free(data); + } +} + + +static int eap_sim_gsm_auth(struct eap_sm *sm, struct eap_sim_data *data) +{ + wpa_printf(MSG_DEBUG, "EAP-SIM: GSM authentication algorithm"); +#ifdef PCSC_FUNCS + if (scard_gsm_auth(sm->scard_ctx, data->rand[0], + data->sres[0], data->kc[0]) || + scard_gsm_auth(sm->scard_ctx, data->rand[1], + data->sres[1], data->kc[1]) || + (data->num_chal > 2 && + scard_gsm_auth(sm->scard_ctx, data->rand[2], + data->sres[2], data->kc[2]))) { + wpa_printf(MSG_DEBUG, "EAP-SIM: GSM SIM authentication could " + "not be completed"); + return -1; + } +#else /* PCSC_FUNCS */ + /* These hardcoded Kc and SRES values are used for testing. RAND to + * KC/SREC mapping is very bogus as far as real authentication is + * concerned, but it is quite useful for cases where the AS is rotating + * the order of pre-configured values. */ + { + int i; + for (i = 0; i < data->num_chal; i++) { + if (data->rand[i][0] == 0xaa) { + memcpy(data->kc[i], + "\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7", + KC_LEN); + memcpy(data->sres[i], "\xd1\xd2\xd3\xd4", + SRES_LEN); + } else if (data->rand[i][0] == 0xbb) { + memcpy(data->kc[i], + "\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7", + KC_LEN); + memcpy(data->sres[i], "\xe1\xe2\xe3\xe4", + SRES_LEN); + } else { + memcpy(data->kc[i], + "\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7", + KC_LEN); + memcpy(data->sres[i], "\xf1\xf2\xf3\xf4", + SRES_LEN); + } + } + } +#endif /* PCSC_FUNCS */ + return 0; +} + + +static int eap_sim_supported_ver(struct eap_sim_data *data, int version) +{ + return version == EAP_SIM_VERSION; +} + + +static void eap_sim_derive_mk(struct eap_sim_data *data, + const u8 *identity, size_t identity_len) +{ + u8 sel_ver[2]; + const unsigned char *addr[5]; + size_t len[5]; + + addr[0] = identity; + len[0] = identity_len; + addr[1] = (u8 *) data->kc; + len[1] = data->num_chal * KC_LEN; + addr[2] = data->nonce_mt; + len[2] = EAP_SIM_NONCE_MT_LEN; + addr[3] = data->ver_list; + len[3] = data->ver_list_len; + addr[4] = sel_ver; + len[4] = 2; + + sel_ver[0] = data->selected_version >> 8; + sel_ver[1] = data->selected_version & 0xff; + + /* MK = SHA1(Identity|n*Kc|NONCE_MT|Version List|Selected Version) */ + sha1_vector(5, addr, len, data->mk); + wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: MK", data->mk, EAP_SIM_MK_LEN); +} + + +#define CLEAR_PSEUDONYM 0x01 +#define CLEAR_REAUTH_ID 0x02 +#define CLEAR_EAP_ID 0x04 + +static void eap_sim_clear_identities(struct eap_sim_data *data, int id) +{ + wpa_printf(MSG_DEBUG, "EAP-SIM: forgetting old%s%s%s", + id & CLEAR_PSEUDONYM ? " pseudonym" : "", + id & CLEAR_REAUTH_ID ? " reauth_id" : "", + id & CLEAR_EAP_ID ? " eap_id" : ""); + if (id & CLEAR_PSEUDONYM) { + free(data->pseudonym); + data->pseudonym = NULL; + data->pseudonym_len = 0; + } + if (id & CLEAR_REAUTH_ID) { + free(data->reauth_id); + data->reauth_id = NULL; + data->reauth_id_len = 0; + } + if (id & CLEAR_EAP_ID) { + free(data->last_eap_identity); + data->last_eap_identity = NULL; + data->last_eap_identity_len = 0; + } +} + + +static int eap_sim_learn_ids(struct eap_sim_data *data, + struct eap_sim_attrs *attr) +{ + if (attr->next_pseudonym) { + free(data->pseudonym); + data->pseudonym = malloc(attr->next_pseudonym_len); + if (data->pseudonym == NULL) { + wpa_printf(MSG_INFO, "EAP-SIM: (encr) No memory for " + "next pseudonym"); + return -1; + } + memcpy(data->pseudonym, attr->next_pseudonym, + attr->next_pseudonym_len); + data->pseudonym_len = attr->next_pseudonym_len; + wpa_hexdump_ascii(MSG_DEBUG, + "EAP-SIM: (encr) AT_NEXT_PSEUDONYM", + data->pseudonym, + data->pseudonym_len); + } + + if (attr->next_reauth_id) { + free(data->reauth_id); + data->reauth_id = malloc(attr->next_reauth_id_len); + if (data->reauth_id == NULL) { + wpa_printf(MSG_INFO, "EAP-SIM: (encr) No memory for " + "next reauth_id"); + return -1; + } + memcpy(data->reauth_id, attr->next_reauth_id, + attr->next_reauth_id_len); + data->reauth_id_len = attr->next_reauth_id_len; + wpa_hexdump_ascii(MSG_DEBUG, + "EAP-SIM: (encr) AT_NEXT_REAUTH_ID", + data->reauth_id, + data->reauth_id_len); + } + + return 0; +} + + +static u8 * eap_sim_client_error(struct eap_sm *sm, struct eap_sim_data *data, + struct eap_hdr *req, + size_t *respDataLen, int err) +{ + struct eap_sim_msg *msg; + + data->state = FAILURE; + data->num_id_req = 0; + data->num_notification = 0; + + msg = eap_sim_msg_init(EAP_CODE_RESPONSE, req->identifier, + EAP_TYPE_SIM, EAP_SIM_SUBTYPE_CLIENT_ERROR); + eap_sim_msg_add(msg, EAP_SIM_AT_CLIENT_ERROR_CODE, err, NULL, 0); + return eap_sim_msg_finish(msg, respDataLen, NULL, NULL, 0); +} + + +static u8 * eap_sim_response_start(struct eap_sm *sm, + struct eap_sim_data *data, + struct eap_hdr *req, + size_t *respDataLen, + enum eap_sim_id_req id_req) +{ + struct wpa_ssid *config = eap_get_config(sm); + u8 *identity = NULL; + size_t identity_len = 0; + struct eap_sim_msg *msg; + + data->reauth = 0; + if (id_req == ANY_ID && data->reauth_id) { + identity = data->reauth_id; + identity_len = data->reauth_id_len; + data->reauth = 1; + } else if ((id_req == ANY_ID || id_req == FULLAUTH_ID) && + data->pseudonym) { + identity = data->pseudonym; + identity_len = data->pseudonym_len; + eap_sim_clear_identities(data, CLEAR_REAUTH_ID); + } else if (id_req != NO_ID_REQ && config && config->identity) { + identity = config->identity; + identity_len = config->identity_len; + eap_sim_clear_identities(data, + CLEAR_PSEUDONYM | CLEAR_REAUTH_ID); + } + if (id_req != NO_ID_REQ) + eap_sim_clear_identities(data, CLEAR_EAP_ID); + + wpa_printf(MSG_DEBUG, "Generating EAP-SIM Start (id=%d)", + req->identifier); + msg = eap_sim_msg_init(EAP_CODE_RESPONSE, req->identifier, + EAP_TYPE_SIM, EAP_SIM_SUBTYPE_START); + wpa_hexdump(MSG_DEBUG, " AT_NONCE_MT", + data->nonce_mt, EAP_SIM_NONCE_MT_LEN); + eap_sim_msg_add(msg, EAP_SIM_AT_NONCE_MT, 0, + data->nonce_mt, EAP_SIM_NONCE_MT_LEN); + wpa_printf(MSG_DEBUG, " AT_SELECTED_VERSION %d", + data->selected_version); + eap_sim_msg_add(msg, EAP_SIM_AT_SELECTED_VERSION, + data->selected_version, NULL, 0); + + if (identity) { + wpa_hexdump_ascii(MSG_DEBUG, " AT_IDENTITY", + identity, identity_len); + eap_sim_msg_add(msg, EAP_SIM_AT_IDENTITY, identity_len, + identity, identity_len); + } + + return eap_sim_msg_finish(msg, respDataLen, NULL, NULL, 0); +} + + +static u8 * eap_sim_response_challenge(struct eap_sm *sm, + struct eap_sim_data *data, + struct eap_hdr *req, + size_t *respDataLen) +{ + struct eap_sim_msg *msg; + + wpa_printf(MSG_DEBUG, "Generating EAP-SIM Challenge (id=%d)", + req->identifier); + msg = eap_sim_msg_init(EAP_CODE_RESPONSE, req->identifier, + EAP_TYPE_SIM, EAP_SIM_SUBTYPE_CHALLENGE); + wpa_printf(MSG_DEBUG, " AT_MAC"); + eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC); + return eap_sim_msg_finish(msg, respDataLen, data->k_aut, + (u8 *) data->sres, + data->num_chal * SRES_LEN); +} + + +static u8 * eap_sim_response_reauth(struct eap_sm *sm, + struct eap_sim_data *data, + struct eap_hdr *req, + size_t *respDataLen, int counter_too_small) +{ + struct eap_sim_msg *msg; + unsigned int counter; + + wpa_printf(MSG_DEBUG, "Generating EAP-SIM Reauthentication (id=%d)", + req->identifier); + msg = eap_sim_msg_init(EAP_CODE_RESPONSE, req->identifier, + EAP_TYPE_SIM, + EAP_SIM_SUBTYPE_REAUTHENTICATION); + wpa_printf(MSG_DEBUG, " AT_IV"); + wpa_printf(MSG_DEBUG, " AT_ENCR_DATA"); + eap_sim_msg_add_encr_start(msg, EAP_SIM_AT_IV, EAP_SIM_AT_ENCR_DATA); + + if (counter_too_small) { + wpa_printf(MSG_DEBUG, " *AT_COUNTER_TOO_SMALL"); + eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER_TOO_SMALL, 0, NULL, 0); + counter = data->counter_too_small; + } else + counter = data->counter; + + wpa_printf(MSG_DEBUG, " *AT_COUNTER %d", counter); + eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER, counter, NULL, 0); + + if (eap_sim_msg_add_encr_end(msg, data->k_encr, EAP_SIM_AT_PADDING)) { + wpa_printf(MSG_WARNING, "EAP-SIM: Failed to encrypt " + "AT_ENCR_DATA"); + eap_sim_msg_free(msg); + return NULL; + } + wpa_printf(MSG_DEBUG, " AT_MAC"); + eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC); + return eap_sim_msg_finish(msg, respDataLen, data->k_aut, data->nonce_s, + EAP_SIM_NONCE_S_LEN); +} + + +static u8 * eap_sim_response_notification(struct eap_sm *sm, + struct eap_sim_data *data, + struct eap_hdr *req, + size_t *respDataLen, + u16 notification) +{ + struct eap_sim_msg *msg; + u8 *k_aut = (notification & 0x4000) == 0 ? data->k_aut : NULL; + + wpa_printf(MSG_DEBUG, "Generating EAP-SIM Notification (id=%d)", + req->identifier); + msg = eap_sim_msg_init(EAP_CODE_RESPONSE, req->identifier, + EAP_TYPE_SIM, EAP_SIM_SUBTYPE_NOTIFICATION); + wpa_printf(MSG_DEBUG, " AT_NOTIFICATION"); + eap_sim_msg_add(msg, EAP_SIM_AT_NOTIFICATION, notification, NULL, 0); + if (k_aut && data->reauth) { + wpa_printf(MSG_DEBUG, " AT_IV"); + wpa_printf(MSG_DEBUG, " AT_ENCR_DATA"); + eap_sim_msg_add_encr_start(msg, EAP_SIM_AT_IV, + EAP_SIM_AT_ENCR_DATA); + wpa_printf(MSG_DEBUG, " *AT_COUNTER %d", data->counter); + eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER, data->counter, + NULL, 0); + if (eap_sim_msg_add_encr_end(msg, data->k_encr, + EAP_SIM_AT_PADDING)) { + wpa_printf(MSG_WARNING, "EAP-SIM: Failed to encrypt " + "AT_ENCR_DATA"); + eap_sim_msg_free(msg); + return NULL; + } + } + if (k_aut) { + wpa_printf(MSG_DEBUG, " AT_MAC"); + eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC); + } + return eap_sim_msg_finish(msg, respDataLen, k_aut, (u8 *) "", 0); +} + + +static u8 * eap_sim_process_start(struct eap_sm *sm, struct eap_sim_data *data, + struct eap_hdr *req, size_t reqDataLen, + size_t *respDataLen, + struct eap_sim_attrs *attr) +{ + int i, selected_version = -1, id_error; + u8 *pos; + + wpa_printf(MSG_DEBUG, "EAP-SIM: subtype Start"); + if (attr->version_list == NULL) { + wpa_printf(MSG_INFO, "EAP-SIM: No AT_VERSION_LIST in " + "SIM/Start"); + return eap_sim_client_error(sm, data, req, respDataLen, + EAP_SIM_UNSUPPORTED_VERSION); + } + + free(data->ver_list); + data->ver_list = malloc(attr->version_list_len); + if (data->ver_list == NULL) { + wpa_printf(MSG_DEBUG, "EAP-SIM: Failed to allocate " + "memory for version list"); + return eap_sim_client_error(sm, data, req, respDataLen, + EAP_SIM_UNABLE_TO_PROCESS_PACKET); + } + memcpy(data->ver_list, attr->version_list, attr->version_list_len); + data->ver_list_len = attr->version_list_len; + pos = data->ver_list; + for (i = 0; i < data->ver_list_len / 2; i++) { + int ver = pos[0] * 256 + pos[1]; + pos += 2; + if (eap_sim_supported_ver(data, ver)) { + selected_version = ver; + break; + } + } + if (selected_version < 0) { + wpa_printf(MSG_INFO, "EAP-SIM: Could not find a supported " + "version"); + return eap_sim_client_error(sm, data, req, respDataLen, + EAP_SIM_UNSUPPORTED_VERSION); + } + wpa_printf(MSG_DEBUG, "EAP-SIM: Selected Version %d", + selected_version); + data->selected_version = selected_version; + + id_error = 0; + switch (attr->id_req) { + case NO_ID_REQ: + break; + case ANY_ID: + if (data->num_id_req > 0) + id_error++; + data->num_id_req++; + break; + case FULLAUTH_ID: + if (data->num_id_req > 1) + id_error++; + data->num_id_req++; + break; + case PERMANENT_ID: + if (data->num_id_req > 2) + id_error++; + data->num_id_req++; + break; + } + if (id_error) { + wpa_printf(MSG_INFO, "EAP-SIM: Too many ID requests " + "used within one authentication"); + return eap_sim_client_error(sm, data, req, respDataLen, + EAP_SIM_UNABLE_TO_PROCESS_PACKET); + } + + return eap_sim_response_start(sm, data, req, respDataLen, + attr->id_req); +} + + +static u8 * eap_sim_process_challenge(struct eap_sm *sm, + struct eap_sim_data *data, + struct eap_hdr *req, size_t reqDataLen, + size_t *respDataLen, + struct eap_sim_attrs *attr) +{ + struct wpa_ssid *config = eap_get_config(sm); + u8 *identity; + size_t identity_len; + struct eap_sim_attrs eattr; + + wpa_printf(MSG_DEBUG, "EAP-SIM: subtype Challenge"); + data->reauth = 0; + if (!attr->mac || !attr->rand) { + wpa_printf(MSG_WARNING, "EAP-SIM: Challenge message " + "did not include%s%s", + !attr->mac ? " AT_MAC" : "", + !attr->rand ? " AT_RAND" : ""); + return eap_sim_client_error(sm, data, req, respDataLen, + EAP_SIM_UNABLE_TO_PROCESS_PACKET); + } + + wpa_printf(MSG_DEBUG, "EAP-SIM: %lu challenges", + (unsigned long) attr->num_chal); + if (attr->num_chal < data->min_num_chal) { + wpa_printf(MSG_INFO, "EAP-SIM: Insufficient number of " + "challenges (%lu)", (unsigned long) attr->num_chal); + return eap_sim_client_error(sm, data, req, respDataLen, + EAP_SIM_INSUFFICIENT_NUM_OF_CHAL); + } + if (attr->num_chal > 3) { + wpa_printf(MSG_INFO, "EAP-SIM: Too many challenges " + "(%lu)", (unsigned long) attr->num_chal); + return eap_sim_client_error(sm, data, req, respDataLen, + EAP_SIM_UNABLE_TO_PROCESS_PACKET); + } + + /* Verify that RANDs are different */ + if (memcmp(attr->rand, attr->rand + GSM_RAND_LEN, + GSM_RAND_LEN) == 0 || + (attr->num_chal > 2 && + (memcmp(attr->rand, attr->rand + 2 * GSM_RAND_LEN, + GSM_RAND_LEN) == 0 || + memcmp(attr->rand + GSM_RAND_LEN, + attr->rand + 2 * GSM_RAND_LEN, + GSM_RAND_LEN) == 0))) { + wpa_printf(MSG_INFO, "EAP-SIM: Same RAND used multiple times"); + return eap_sim_client_error(sm, data, req, respDataLen, + EAP_SIM_RAND_NOT_FRESH); + } + + memcpy(data->rand, attr->rand, attr->num_chal * GSM_RAND_LEN); + data->num_chal = attr->num_chal; + + if (eap_sim_gsm_auth(sm, data)) { + wpa_printf(MSG_WARNING, "EAP-SIM: GSM authentication failed"); + return eap_sim_client_error(sm, data, req, respDataLen, + EAP_SIM_UNABLE_TO_PROCESS_PACKET); + } + if (data->last_eap_identity) { + identity = data->last_eap_identity; + identity_len = data->last_eap_identity_len; + } else if (data->pseudonym) { + identity = data->pseudonym; + identity_len = data->pseudonym_len; + } else { + identity = config->identity; + identity_len = config->identity_len; + } + wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM: Selected identity for MK " + "derivation", identity, identity_len); + eap_sim_derive_mk(data, identity, identity_len); + eap_sim_derive_keys(data->mk, data->k_encr, data->k_aut, data->msk); + if (eap_sim_verify_mac(data->k_aut, (u8 *) req, reqDataLen, attr->mac, + data->nonce_mt, EAP_SIM_NONCE_MT_LEN)) { + wpa_printf(MSG_WARNING, "EAP-SIM: Challenge message " + "used invalid AT_MAC"); + return eap_sim_client_error(sm, data, req, respDataLen, + EAP_SIM_UNABLE_TO_PROCESS_PACKET); + } + + /* Old reauthentication and pseudonym identities must not be used + * anymore. In other words, if no new identities are received, full + * authentication will be used on next reauthentication. */ + eap_sim_clear_identities(data, CLEAR_PSEUDONYM | CLEAR_REAUTH_ID | + CLEAR_EAP_ID); + + if (attr->encr_data) { + if (eap_sim_parse_encr(data->k_encr, attr->encr_data, + attr->encr_data_len, attr->iv, &eattr, + 0)) { + return eap_sim_client_error( + sm, data, req, respDataLen, + EAP_SIM_UNABLE_TO_PROCESS_PACKET); + } + eap_sim_learn_ids(data, &eattr); + } + + if (data->state != FAILURE) + data->state = SUCCESS; + + data->num_id_req = 0; + data->num_notification = 0; + /* draft-haverinen-pppext-eap-sim-13.txt specifies that counter + * is initialized to one after fullauth, but initializing it to + * zero makes it easier to implement reauth verification. */ + data->counter = 0; + return eap_sim_response_challenge(sm, data, req, respDataLen); +} + + +static int eap_sim_process_notification_reauth(struct eap_sim_data *data, + struct eap_hdr *req, + size_t reqDataLen, + struct eap_sim_attrs *attr) +{ + struct eap_sim_attrs eattr; + + if (attr->encr_data == NULL || attr->iv == NULL) { + wpa_printf(MSG_WARNING, "EAP-SIM: Notification message after " + "reauth did not include encrypted data"); + return -1; + } + + if (eap_sim_parse_encr(data->k_encr, attr->encr_data, + attr->encr_data_len, attr->iv, &eattr, 0)) { + wpa_printf(MSG_WARNING, "EAP-SIM: Failed to parse encrypted " + "data from notification message"); + return -1; + } + + if (eattr.counter != data->counter) { + wpa_printf(MSG_WARNING, "EAP-SIM: Counter in notification " + "message does not match with counter in reauth " + "message"); + return -1; + } + + return 0; +} + + +static int eap_sim_process_notification_auth(struct eap_sim_data *data, + struct eap_hdr *req, + size_t reqDataLen, + struct eap_sim_attrs *attr) +{ + if (attr->mac == NULL) { + wpa_printf(MSG_INFO, "EAP-SIM: no AT_MAC in after_auth " + "Notification message"); + return -1; + } + + if (eap_sim_verify_mac(data->k_aut, (u8 *) req, reqDataLen, attr->mac, + (u8 *) "", 0)) { + wpa_printf(MSG_WARNING, "EAP-SIM: Notification message " + "used invalid AT_MAC"); + return -1; + } + + if (data->reauth && + eap_sim_process_notification_reauth(data, req, reqDataLen, attr)) { + wpa_printf(MSG_WARNING, "EAP-SIM: Invalid notification " + "message after reauth"); + return -1; + } + + return 0; +} + + +static u8 * eap_sim_process_notification(struct eap_sm *sm, + struct eap_sim_data *data, + struct eap_hdr *req, + size_t reqDataLen, + size_t *respDataLen, + struct eap_sim_attrs *attr) +{ + wpa_printf(MSG_DEBUG, "EAP-SIM: subtype Notification"); + if (data->num_notification > 0) { + wpa_printf(MSG_INFO, "EAP-SIM: too many notification " + "rounds (only one allowed)"); + return eap_sim_client_error(sm, data, req, respDataLen, + EAP_SIM_UNABLE_TO_PROCESS_PACKET); + } + data->num_notification++; + if (attr->notification == -1) { + wpa_printf(MSG_INFO, "EAP-SIM: no AT_NOTIFICATION in " + "Notification message"); + return eap_sim_client_error(sm, data, req, respDataLen, + EAP_SIM_UNABLE_TO_PROCESS_PACKET); + } + + if ((attr->notification & 0x4000) == 0 && + eap_sim_process_notification_auth(data, req, reqDataLen, attr)) { + return eap_sim_client_error(sm, data, req, respDataLen, + EAP_SIM_UNABLE_TO_PROCESS_PACKET); + } + + eap_sim_report_notification(sm->msg_ctx, attr->notification, 0); + if (attr->notification >= 0 && attr->notification < 32768) { + data->state = FAILURE; + } + return eap_sim_response_notification(sm, data, req, respDataLen, + attr->notification); +} + + +static u8 * eap_sim_process_reauthentication(struct eap_sm *sm, + struct eap_sim_data *data, + struct eap_hdr *req, + size_t reqDataLen, + size_t *respDataLen, + struct eap_sim_attrs *attr) +{ + struct eap_sim_attrs eattr; + + wpa_printf(MSG_DEBUG, "EAP-SIM: subtype Reauthentication"); + + if (data->reauth_id == NULL) { + wpa_printf(MSG_WARNING, "EAP-SIM: Server is trying " + "reauthentication, but no reauth_id available"); + return eap_sim_client_error(sm, data, req, respDataLen, + EAP_SIM_UNABLE_TO_PROCESS_PACKET); + } + + data->reauth = 1; + if (eap_sim_verify_mac(data->k_aut, (u8 *) req, reqDataLen, + attr->mac, (u8 *) "", 0)) { + wpa_printf(MSG_WARNING, "EAP-SIM: Reauthentication " + "did not have valid AT_MAC"); + return eap_sim_client_error(sm, data, req, respDataLen, + EAP_SIM_UNABLE_TO_PROCESS_PACKET); + } + + if (attr->encr_data == NULL || attr->iv == NULL) { + wpa_printf(MSG_WARNING, "EAP-SIM: Reauthentication " + "message did not include encrypted data"); + return eap_sim_client_error(sm, data, req, respDataLen, + EAP_SIM_UNABLE_TO_PROCESS_PACKET); + } + + if (eap_sim_parse_encr(data->k_encr, attr->encr_data, + attr->encr_data_len, attr->iv, &eattr, 0)) { + wpa_printf(MSG_WARNING, "EAP-SIM: Failed to parse encrypted " + "data from reauthentication message"); + return eap_sim_client_error(sm, data, req, respDataLen, + EAP_SIM_UNABLE_TO_PROCESS_PACKET); + } + + if (eattr.nonce_s == NULL || eattr.counter < 0) { + wpa_printf(MSG_INFO, "EAP-SIM: (encr) No%s%s in reauth packet", + !eattr.nonce_s ? " AT_NONCE_S" : "", + eattr.counter < 0 ? " AT_COUNTER" : ""); + return eap_sim_client_error(sm, data, req, respDataLen, + EAP_SIM_UNABLE_TO_PROCESS_PACKET); + } + + if (eattr.counter <= data->counter) { + wpa_printf(MSG_INFO, "EAP-SIM: (encr) Invalid counter " + "(%d <= %d)", eattr.counter, data->counter); + data->counter_too_small = eattr.counter; + /* Reply using Re-auth w/ AT_COUNTER_TOO_SMALL. The current + * reauth_id must not be used to start a new reauthentication. + * However, since it was used in the last EAP-Response-Identity + * packet, it has to saved for the following fullauth to be + * used in MK derivation. */ + free(data->last_eap_identity); + data->last_eap_identity = data->reauth_id; + data->last_eap_identity_len = data->reauth_id_len; + data->reauth_id = NULL; + data->reauth_id_len = 0; + return eap_sim_response_reauth(sm, data, req, respDataLen, 1); + } + data->counter = eattr.counter; + + memcpy(data->nonce_s, eattr.nonce_s, EAP_SIM_NONCE_S_LEN); + wpa_hexdump(MSG_DEBUG, "EAP-SIM: (encr) AT_NONCE_S", + data->nonce_s, EAP_SIM_NONCE_S_LEN); + + eap_sim_derive_keys_reauth(data->counter, + data->reauth_id, data->reauth_id_len, + data->nonce_s, data->mk, data->msk); + eap_sim_clear_identities(data, CLEAR_REAUTH_ID | CLEAR_EAP_ID); + eap_sim_learn_ids(data, &eattr); + + if (data->state != FAILURE) + data->state = SUCCESS; + + data->num_id_req = 0; + data->num_notification = 0; + if (data->counter > EAP_SIM_MAX_FAST_REAUTHS) { + wpa_printf(MSG_DEBUG, "EAP-SIM: Maximum number of " + "fast reauths performed - force fullauth"); + eap_sim_clear_identities(data, CLEAR_REAUTH_ID | CLEAR_EAP_ID); + } + return eap_sim_response_reauth(sm, data, req, respDataLen, 0); +} + + +static u8 * eap_sim_process(struct eap_sm *sm, void *priv, + struct eap_method_ret *ret, + u8 *reqData, size_t reqDataLen, + size_t *respDataLen) +{ + struct eap_sim_data *data = priv; + struct wpa_ssid *config = eap_get_config(sm); + struct eap_hdr *req; + u8 *pos, subtype, *res; + struct eap_sim_attrs attr; + size_t len; + + wpa_hexdump(MSG_DEBUG, "EAP-SIM: EAP data", reqData, reqDataLen); + if (config == NULL || config->identity == NULL) { + wpa_printf(MSG_INFO, "EAP-SIM: Identity not configured"); + eap_sm_request_identity(sm, config); + ret->ignore = TRUE; + return NULL; + } + + req = (struct eap_hdr *) reqData; + pos = (u8 *) (req + 1); + if (reqDataLen < sizeof(*req) + 4 || *pos != EAP_TYPE_SIM || + (len = be_to_host16(req->length)) > reqDataLen) { + wpa_printf(MSG_INFO, "EAP-SIM: Invalid frame"); + ret->ignore = TRUE; + return NULL; + } + + ret->ignore = FALSE; + ret->methodState = METHOD_CONT; + ret->decision = DECISION_FAIL; + ret->allowNotifications = TRUE; + + pos++; + subtype = *pos++; + wpa_printf(MSG_DEBUG, "EAP-SIM: Subtype=%d", subtype); + pos += 2; /* Reserved */ + + if (eap_sim_parse_attr(pos, reqData + len, &attr, 0, 0)) { + res = eap_sim_client_error(sm, data, req, respDataLen, + EAP_SIM_UNABLE_TO_PROCESS_PACKET); + goto done; + } + + switch (subtype) { + case EAP_SIM_SUBTYPE_START: + res = eap_sim_process_start(sm, data, req, len, + respDataLen, &attr); + break; + case EAP_SIM_SUBTYPE_CHALLENGE: + res = eap_sim_process_challenge(sm, data, req, len, + respDataLen, &attr); + break; + case EAP_SIM_SUBTYPE_NOTIFICATION: + res = eap_sim_process_notification(sm, data, req, len, + respDataLen, &attr); + break; + case EAP_SIM_SUBTYPE_REAUTHENTICATION: + res = eap_sim_process_reauthentication(sm, data, req, len, + respDataLen, &attr); + break; + case EAP_SIM_SUBTYPE_CLIENT_ERROR: + wpa_printf(MSG_DEBUG, "EAP-SIM: subtype Client-Error"); + res = eap_sim_client_error(sm, data, req, respDataLen, + EAP_SIM_UNABLE_TO_PROCESS_PACKET); + break; + default: + wpa_printf(MSG_DEBUG, "EAP-SIM: Unknown subtype=%d", subtype); + res = eap_sim_client_error(sm, data, req, respDataLen, + EAP_SIM_UNABLE_TO_PROCESS_PACKET); + break; + } + +done: + if (data->state == FAILURE) { + ret->decision = DECISION_FAIL; + ret->methodState = METHOD_DONE; + } else if (data->state == SUCCESS) { + ret->decision = DECISION_UNCOND_SUCC; + ret->methodState = METHOD_DONE; + } + + if (ret->methodState == METHOD_DONE) { + ret->allowNotifications = FALSE; + } + + return res; +} + + +static Boolean eap_sim_has_reauth_data(struct eap_sm *sm, void *priv) +{ + struct eap_sim_data *data = priv; + return data->pseudonym || data->reauth_id; +} + + +static void eap_sim_deinit_for_reauth(struct eap_sm *sm, void *priv) +{ + struct eap_sim_data *data = priv; + eap_sim_clear_identities(data, CLEAR_EAP_ID); +} + + +static void * eap_sim_init_for_reauth(struct eap_sm *sm, void *priv) +{ + struct eap_sim_data *data = priv; + if (hostapd_get_rand(data->nonce_mt, EAP_SIM_NONCE_MT_LEN)) { + wpa_printf(MSG_WARNING, "EAP-SIM: Failed to get random data " + "for NONCE_MT"); + free(data); + return NULL; + } + data->num_id_req = 0; + data->num_notification = 0; + data->state = CONTINUE; + return priv; +} + + +static const u8 * eap_sim_get_identity(struct eap_sm *sm, void *priv, + size_t *len) +{ + struct eap_sim_data *data = priv; + + if (data->reauth_id) { + *len = data->reauth_id_len; + return data->reauth_id; + } + + if (data->pseudonym) { + *len = data->pseudonym_len; + return data->pseudonym; + } + + return NULL; +} + + +static Boolean eap_sim_isKeyAvailable(struct eap_sm *sm, void *priv) +{ + struct eap_sim_data *data = priv; + return data->state == SUCCESS; +} + + +static u8 * eap_sim_getKey(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_sim_data *data = priv; + u8 *key; + + if (data->state != SUCCESS) + return NULL; + + key = malloc(EAP_SIM_KEYING_DATA_LEN); + if (key == NULL) + return NULL; + + *len = EAP_SIM_KEYING_DATA_LEN; + memcpy(key, data->msk, EAP_SIM_KEYING_DATA_LEN); + + return key; +} + + +const struct eap_method eap_method_sim = +{ + .method = EAP_TYPE_SIM, + .name = "SIM", + .init = eap_sim_init, + .deinit = eap_sim_deinit, + .process = eap_sim_process, + .isKeyAvailable = eap_sim_isKeyAvailable, + .getKey = eap_sim_getKey, + .has_reauth_data = eap_sim_has_reauth_data, + .deinit_for_reauth = eap_sim_deinit_for_reauth, + .init_for_reauth = eap_sim_init_for_reauth, + .get_identity = eap_sim_get_identity, +}; diff --git a/contrib/wpa_supplicant/eap_sim_common.c b/contrib/wpa_supplicant/eap_sim_common.c new file mode 100644 index 0000000..98f4fb7 --- /dev/null +++ b/contrib/wpa_supplicant/eap_sim_common.c @@ -0,0 +1,788 @@ +/* + * WPA Supplicant / EAP-SIM/AKA shared routines + * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include "common.h" +#include "eap_i.h" +#include "sha1.h" +#include "aes_wrap.h" +#include "eap_sim_common.h" + + +#define MSK_LEN 8 +#define EMSK_LEN 8 + + +static void eap_sim_prf(const u8 *key, u8 *x, size_t xlen) +{ + u8 xkey[64]; + u32 t[5], _t[5]; + int i, j, m, k; + u8 *xpos = x; + u32 carry; + + /* FIPS 186-2 + change notice 1 */ + + memcpy(xkey, key, EAP_SIM_MK_LEN); + memset(xkey + EAP_SIM_MK_LEN, 0, 64 - EAP_SIM_MK_LEN); + t[0] = 0x67452301; + t[1] = 0xEFCDAB89; + t[2] = 0x98BADCFE; + t[3] = 0x10325476; + t[4] = 0xC3D2E1F0; + + m = xlen / 40; + for (j = 0; j < m; j++) { + /* XSEED_j = 0 */ + for (i = 0; i < 2; i++) { + /* XVAL = (XKEY + XSEED_j) mod 2^b */ + + /* w_i = G(t, XVAL) */ + memcpy(_t, t, 20); + sha1_transform((u8 *) _t, xkey); + _t[0] = host_to_be32(_t[0]); + _t[1] = host_to_be32(_t[1]); + _t[2] = host_to_be32(_t[2]); + _t[3] = host_to_be32(_t[3]); + _t[4] = host_to_be32(_t[4]); + memcpy(xpos, _t, 20); + + /* XKEY = (1 + XKEY + w_i) mod 2^b */ + carry = 1; + for (k = 19; k >= 0; k--) { + carry += xkey[k] + xpos[k]; + xkey[k] = carry & 0xff; + carry >>= 8; + } + + xpos += SHA1_MAC_LEN; + } + /* x_j = w_0|w_1 */ + } +} + + +void eap_sim_derive_keys(const u8 *mk, u8 *k_encr, u8 *k_aut, u8 *msk) +{ + u8 buf[120], *pos; + eap_sim_prf(mk, buf, 120); + pos = buf; + memcpy(k_encr, pos, EAP_SIM_K_ENCR_LEN); + pos += EAP_SIM_K_ENCR_LEN; + memcpy(k_aut, pos, EAP_SIM_K_AUT_LEN); + pos += EAP_SIM_K_AUT_LEN; + memcpy(msk, pos, EAP_SIM_KEYING_DATA_LEN); + pos += MSK_LEN; + + wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: K_encr", + k_encr, EAP_SIM_K_ENCR_LEN); + wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: K_aut", + k_aut, EAP_SIM_K_ENCR_LEN); + wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: MSK", + msk, MSK_LEN); + wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: Ext. MSK", + msk + MSK_LEN, EMSK_LEN); + wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: keying material", + msk, EAP_SIM_KEYING_DATA_LEN); +} + + +void eap_sim_derive_keys_reauth(unsigned int _counter, + const u8 *identity, size_t identity_len, + const u8 *nonce_s, const u8 *mk, u8 *msk) +{ + u8 xkey[SHA1_MAC_LEN]; + u8 counter[2]; + const u8 *addr[4]; + size_t len[4]; + + addr[0] = identity; + len[0] = identity_len; + addr[1] = counter; + len[1] = 2; + addr[2] = nonce_s; + len[2] = EAP_SIM_NONCE_S_LEN; + addr[3] = mk; + len[3] = EAP_SIM_MK_LEN; + + counter[0] = _counter >> 8; + counter[1] = _counter & 0xff; + + wpa_printf(MSG_DEBUG, "EAP-SIM: Deriving keying data from reauth"); + wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM: Identity", + identity, identity_len); + wpa_hexdump(MSG_DEBUG, "EAP-SIM: counter", counter, 2); + wpa_hexdump(MSG_DEBUG, "EAP-SIM: NONCE_S", nonce_s, + EAP_SIM_NONCE_S_LEN); + wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: MK", mk, EAP_SIM_MK_LEN); + + /* XKEY' = SHA1(Identity|counter|NONCE_S|MK) */ + sha1_vector(4, addr, len, xkey); + wpa_hexdump(MSG_DEBUG, "EAP-SIM: XKEY'", xkey, SHA1_MAC_LEN); + + eap_sim_prf(xkey, msk, EAP_SIM_KEYING_DATA_LEN); + wpa_hexdump(MSG_DEBUG, "EAP-SIM: MSK", msk, MSK_LEN); + wpa_hexdump(MSG_DEBUG, "EAP-SIM: Ext. MSK", msk + MSK_LEN, EMSK_LEN); + wpa_hexdump(MSG_DEBUG, "EAP-SIM: keying material", + msk, EAP_SIM_KEYING_DATA_LEN); +} + + +int eap_sim_verify_mac(const u8 *k_aut, u8 *req, size_t req_len, u8 *mac, + u8 *extra, size_t extra_len) +{ + unsigned char hmac[SHA1_MAC_LEN]; + const u8 *addr[2]; + size_t len[2]; + u8 rx_mac[EAP_SIM_MAC_LEN]; + + if (mac == NULL) + return -1; + + addr[0] = req; + len[0] = req_len; + addr[1] = extra; + len[1] = extra_len; + + /* HMAC-SHA1-128 */ + memcpy(rx_mac, mac, EAP_SIM_MAC_LEN); + memset(mac, 0, EAP_SIM_MAC_LEN); + hmac_sha1_vector(k_aut, EAP_SIM_K_AUT_LEN, 2, addr, len, hmac); + memcpy(mac, rx_mac, EAP_SIM_MAC_LEN); + + return (memcmp(hmac, mac, EAP_SIM_MAC_LEN) == 0) ? 0 : 1; +} + + +void eap_sim_add_mac(const u8 *k_aut, u8 *msg, size_t msg_len, u8 *mac, + const u8 *extra, size_t extra_len) +{ + unsigned char hmac[SHA1_MAC_LEN]; + const u8 *addr[2]; + size_t len[2]; + + addr[0] = msg; + len[0] = msg_len; + addr[1] = extra; + len[1] = extra_len; + + /* HMAC-SHA1-128 */ + memset(mac, 0, EAP_SIM_MAC_LEN); + hmac_sha1_vector(k_aut, EAP_SIM_K_AUT_LEN, 2, addr, len, hmac); + memcpy(mac, hmac, EAP_SIM_MAC_LEN); +} + + +int eap_sim_parse_attr(u8 *start, u8 *end, struct eap_sim_attrs *attr, int aka, + int encr) +{ + u8 *pos = start, *apos; + size_t alen, plen; + int list_len, i; + + memset(attr, 0, sizeof(*attr)); + attr->id_req = NO_ID_REQ; + attr->notification = -1; + attr->counter = -1; + attr->selected_version = -1; + attr->client_error_code = -1; + + while (pos < end) { + if (pos + 2 > end) { + wpa_printf(MSG_INFO, "EAP-SIM: Attribute overflow(1)"); + return -1; + } + wpa_printf(MSG_MSGDUMP, "EAP-SIM: Attribute: Type=%d Len=%d", + pos[0], pos[1] * 4); + if (pos + pos[1] * 4 > end) { + wpa_printf(MSG_INFO, "EAP-SIM: Attribute overflow " + "(pos=%p len=%d end=%p)", + pos, pos[1] * 4, end); + return -1; + } + apos = pos + 2; + alen = pos[1] * 4 - 2; + wpa_hexdump(MSG_MSGDUMP, "EAP-SIM: Attribute data", + apos, alen); + + switch (pos[0]) { + case EAP_SIM_AT_RAND: + wpa_printf(MSG_DEBUG, "EAP-SIM: AT_RAND"); + apos += 2; + alen -= 2; + if ((!aka && (alen % GSM_RAND_LEN)) || + (aka && alen != AKA_RAND_LEN)) { + wpa_printf(MSG_INFO, "EAP-SIM: Invalid AT_RAND" + " (len %lu)", + (unsigned long) alen); + return -1; + } + attr->rand = apos; + attr->num_chal = alen / GSM_RAND_LEN; + break; + case EAP_SIM_AT_AUTN: + wpa_printf(MSG_DEBUG, "EAP-AKA: AT_AUTN"); + if (!aka) { + wpa_printf(MSG_DEBUG, "EAP-SIM: " + "Unexpected AT_AUTN"); + return -1; + } + apos += 2; + alen -= 2; + if (alen != AKA_AUTN_LEN) { + wpa_printf(MSG_INFO, "EAP-AKA: Invalid AT_AUTN" + " (len %lu)", + (unsigned long) alen); + return -1; + } + attr->autn = apos; + break; + case EAP_SIM_AT_PADDING: + if (!encr) { + wpa_printf(MSG_ERROR, "EAP-SIM: Unencrypted " + "AT_PADDING"); + return -1; + } + wpa_printf(MSG_DEBUG, "EAP-SIM: (encr) AT_PADDING"); + for (i = 2; i < alen; i++) { + if (apos[i] != 0) { + wpa_printf(MSG_INFO, "EAP-SIM: (encr) " + "AT_PADDING used a non-zero" + " padding byte"); + wpa_hexdump(MSG_DEBUG, "EAP-SIM: " + "(encr) padding bytes", + apos + 2, alen - 2); + return -1; + } + } + break; + case EAP_SIM_AT_NONCE_MT: + wpa_printf(MSG_DEBUG, "EAP-SIM: AT_NONCE_MT"); + if (alen != 2 + EAP_SIM_NONCE_MT_LEN) { + wpa_printf(MSG_INFO, "EAP-SIM: Invalid " + "AT_NONCE_MT length"); + return -1; + } + attr->nonce_mt = apos + 2; + break; + case EAP_SIM_AT_PERMANENT_ID_REQ: + wpa_printf(MSG_DEBUG, "EAP-SIM: AT_PERMANENT_ID_REQ"); + attr->id_req = PERMANENT_ID; + break; + case EAP_SIM_AT_MAC: + wpa_printf(MSG_DEBUG, "EAP-SIM: AT_MAC"); + if (alen != 2 + EAP_SIM_MAC_LEN) { + wpa_printf(MSG_INFO, "EAP-SIM: Invalid AT_MAC " + "length"); + return -1; + } + attr->mac = apos + 2; + break; + case EAP_SIM_AT_NOTIFICATION: + if (alen != 2) { + wpa_printf(MSG_INFO, "EAP-SIM: Invalid " + "AT_NOTIFICATION length %lu", + (unsigned long) alen); + return -1; + } + attr->notification = apos[0] * 256 + apos[1]; + wpa_printf(MSG_DEBUG, "EAP-SIM: AT_NOTIFICATION %d", + attr->notification); + break; + case EAP_SIM_AT_ANY_ID_REQ: + wpa_printf(MSG_DEBUG, "EAP-SIM: AT_ANY_ID_REQ"); + attr->id_req = ANY_ID; + break; + case EAP_SIM_AT_IDENTITY: + wpa_printf(MSG_DEBUG, "EAP-SIM: AT_IDENTITY"); + attr->identity = apos + 2; + attr->identity_len = alen - 2; + break; + case EAP_SIM_AT_VERSION_LIST: + if (aka) { + wpa_printf(MSG_DEBUG, "EAP-AKA: " + "Unexpected AT_VERSION_LIST"); + return -1; + } + list_len = apos[0] * 256 + apos[1]; + wpa_printf(MSG_DEBUG, "EAP-SIM: AT_VERSION_LIST"); + if (list_len < 2 || list_len > alen - 2) { + wpa_printf(MSG_WARNING, "EAP-SIM: Invalid " + "AT_VERSION_LIST (list_len=%d " + "attr_len=%lu)", list_len, + (unsigned long) alen); + return -1; + } + attr->version_list = apos + 2; + attr->version_list_len = list_len; + break; + case EAP_SIM_AT_SELECTED_VERSION: + wpa_printf(MSG_DEBUG, "EAP-SIM: AT_SELECTED_VERSION"); + if (alen != 2) { + wpa_printf(MSG_INFO, "EAP-SIM: Invalid " + "AT_SELECTED_VERSION length %lu", + (unsigned long) alen); + return -1; + } + attr->selected_version = apos[0] * 256 + apos[1]; + wpa_printf(MSG_DEBUG, "EAP-SIM: AT_SELECTED_VERSION " + "%d", attr->selected_version); + break; + case EAP_SIM_AT_FULLAUTH_ID_REQ: + wpa_printf(MSG_DEBUG, "EAP-SIM: AT_FULLAUTH_ID_REQ"); + attr->id_req = FULLAUTH_ID; + break; + case EAP_SIM_AT_COUNTER: + if (!encr) { + wpa_printf(MSG_ERROR, "EAP-SIM: Unencrypted " + "AT_COUNTER"); + return -1; + } + if (alen != 2) { + wpa_printf(MSG_INFO, "EAP-SIM: (encr) Invalid " + "AT_COUNTER (alen=%lu)", + (unsigned long) alen); + return -1; + } + attr->counter = apos[0] * 256 + apos[1]; + wpa_printf(MSG_DEBUG, "EAP-SIM: (encr) AT_COUNTER %d", + attr->counter); + break; + case EAP_SIM_AT_NONCE_S: + if (!encr) { + wpa_printf(MSG_ERROR, "EAP-SIM: Unencrypted " + "AT_NONCE_S"); + return -1; + } + wpa_printf(MSG_DEBUG, "EAP-SIM: (encr) " + "AT_NONCE_S"); + if (alen != 2 + EAP_SIM_NONCE_S_LEN) { + wpa_printf(MSG_INFO, "EAP-SIM: (encr) Invalid " + "AT_NONCE_S (alen=%lu)", + (unsigned long) alen); + return -1; + } + attr->nonce_s = apos + 2; + break; + case EAP_SIM_AT_CLIENT_ERROR_CODE: + if (alen != 2) { + wpa_printf(MSG_INFO, "EAP-SIM: Invalid " + "AT_CLIENT_ERROR_CODE length %lu", + (unsigned long) alen); + return -1; + } + attr->client_error_code = apos[0] * 256 + apos[1]; + wpa_printf(MSG_DEBUG, "EAP-SIM: AT_CLIENT_ERROR_CODE " + "%d", attr->client_error_code); + break; + case EAP_SIM_AT_IV: + wpa_printf(MSG_DEBUG, "EAP-SIM: AT_IV"); + if (alen != 2 + EAP_SIM_MAC_LEN) { + wpa_printf(MSG_INFO, "EAP-SIM: Invalid AT_IV " + "length %lu", (unsigned long) alen); + return -1; + } + attr->iv = apos + 2; + break; + case EAP_SIM_AT_ENCR_DATA: + wpa_printf(MSG_DEBUG, "EAP-SIM: AT_ENCR_DATA"); + attr->encr_data = apos + 2; + attr->encr_data_len = alen - 2; + if (attr->encr_data_len % 16) { + wpa_printf(MSG_INFO, "EAP-SIM: Invalid " + "AT_ENCR_DATA length %lu", + (unsigned long) + attr->encr_data_len); + return -1; + } + break; + case EAP_SIM_AT_NEXT_PSEUDONYM: + if (!encr) { + wpa_printf(MSG_ERROR, "EAP-SIM: Unencrypted " + "AT_NEXT_PSEUDONYM"); + return -1; + } + wpa_printf(MSG_DEBUG, "EAP-SIM: (encr) " + "AT_NEXT_PSEUDONYM"); + plen = apos[0] * 256 + apos[1]; + if (plen > alen - 2) { + wpa_printf(MSG_INFO, "EAP-SIM: (encr) Invalid" + " AT_NEXT_PSEUDONYM (actual" + " len %lu, attr len %lu)", + (unsigned long) plen, + (unsigned long) alen); + return -1; + } + attr->next_pseudonym = pos + 4; + attr->next_pseudonym_len = plen; + break; + case EAP_SIM_AT_NEXT_REAUTH_ID: + if (!encr) { + wpa_printf(MSG_ERROR, "EAP-SIM: Unencrypted " + "AT_NEXT_REAUTH_ID"); + return -1; + } + wpa_printf(MSG_DEBUG, "EAP-SIM: (encr) " + "AT_NEXT_REAUTH_ID"); + plen = apos[0] * 256 + apos[1]; + if (plen > alen - 2) { + wpa_printf(MSG_INFO, "EAP-SIM: (encr) Invalid" + " AT_NEXT_REAUTH_ID (actual" + " len %lu, attr len %lu)", + (unsigned long) plen, + (unsigned long) alen); + return -1; + } + attr->next_reauth_id = pos + 4; + attr->next_reauth_id_len = plen; + break; + default: + if (pos[0] < 128) { + wpa_printf(MSG_INFO, "EAP-SIM: Unrecognized " + "non-skippable attribute %d", + pos[0]); + return -1; + } + + wpa_printf(MSG_DEBUG, "EAP-SIM: Unrecognized skippable" + " attribute %d ignored", pos[0]); + break; + } + + pos += pos[1] * 4; + } + + wpa_printf(MSG_DEBUG, "EAP-SIM: Attributes parsed successfully " + "(aka=%d encr=%d)", aka, encr); + + return 0; +} + + +int eap_sim_parse_encr(const u8 *k_encr, u8 *encr_data, size_t encr_data_len, + const u8 *iv, struct eap_sim_attrs *attr, int aka) +{ + if (!iv) { + wpa_printf(MSG_INFO, "EAP-SIM: Encrypted data, but no IV"); + return -1; + } + aes_128_cbc_decrypt(k_encr, iv, encr_data, encr_data_len); + wpa_hexdump(MSG_MSGDUMP, "EAP-SIM: Decrypted AT_ENCR_DATA", + encr_data, encr_data_len); + + if (eap_sim_parse_attr(encr_data, encr_data + encr_data_len, attr, + aka, 1)) { + wpa_printf(MSG_INFO, "EAP-SIM: (encr) Failed to parse " + "decrypted AT_ENCR_DATA"); + return -1; + } + + return 0; +} + + +#define EAP_SIM_INIT_LEN 128 + +struct eap_sim_msg { + u8 *buf; + size_t buf_len, used; + size_t mac, iv, encr; /* index from buf */ +}; + + +struct eap_sim_msg * eap_sim_msg_init(int code, int id, int type, int subtype) +{ + struct eap_sim_msg *msg; + struct eap_hdr *eap; + u8 *pos; + + msg = malloc(sizeof(*msg)); + if (msg == NULL) + return NULL; + memset(msg, 0, sizeof(*msg)); + + msg->buf = malloc(EAP_SIM_INIT_LEN); + if (msg->buf == NULL) { + free(msg); + return NULL; + } + memset(msg->buf, 0, EAP_SIM_INIT_LEN); + msg->buf_len = EAP_SIM_INIT_LEN; + eap = (struct eap_hdr *) msg->buf; + eap->code = code; + eap->identifier = id; + msg->used = sizeof(*eap); + + pos = (u8 *) (eap + 1); + *pos++ = type; + *pos++ = subtype; + *pos++ = 0; /* Reserved */ + *pos++ = 0; /* Reserved */ + msg->used += 4; + + return msg; +} + + +u8 * eap_sim_msg_finish(struct eap_sim_msg *msg, size_t *len, const u8 *k_aut, + const u8 *extra, size_t extra_len) +{ + struct eap_hdr *eap; + u8 *buf; + + if (msg == NULL) + return NULL; + + eap = (struct eap_hdr *) msg->buf; + eap->length = host_to_be16(msg->used); + + if (k_aut && msg->mac) { + eap_sim_add_mac(k_aut, msg->buf, msg->used, + msg->buf + msg->mac, extra, extra_len); + } + + *len = msg->used; + buf = msg->buf; + free(msg); + return buf; +} + + +void eap_sim_msg_free(struct eap_sim_msg *msg) +{ + if (msg) { + free(msg->buf); + free(msg); + } +} + + +static int eap_sim_msg_resize(struct eap_sim_msg *msg, size_t add_len) +{ + if (msg->used + add_len > msg->buf_len) { + u8 *nbuf = realloc(msg->buf, msg->used + add_len); + if (nbuf == NULL) + return -1; + msg->buf = nbuf; + msg->buf_len = msg->used + add_len; + } + return 0; +} + + +u8 * eap_sim_msg_add_full(struct eap_sim_msg *msg, u8 attr, + const u8 *data, size_t len) +{ + int attr_len = 2 + len; + int pad_len; + u8 *start, *pos; + + if (msg == NULL) + return NULL; + + pad_len = (4 - attr_len % 4) % 4; + attr_len += pad_len; + if (eap_sim_msg_resize(msg, attr_len)) + return NULL; + start = pos = msg->buf + msg->used; + *pos++ = attr; + *pos++ = attr_len / 4; + memcpy(pos, data, len); + if (pad_len) { + pos += len; + memset(pos, 0, pad_len); + } + msg->used += attr_len; + return start; +} + + +u8 * eap_sim_msg_add(struct eap_sim_msg *msg, u8 attr, u16 value, + const u8 *data, size_t len) +{ + int attr_len = 4 + len; + int pad_len; + u8 *start, *pos; + + if (msg == NULL) + return NULL; + + pad_len = (4 - attr_len % 4) % 4; + attr_len += pad_len; + if (eap_sim_msg_resize(msg, attr_len)) + return NULL; + start = pos = msg->buf + msg->used; + *pos++ = attr; + *pos++ = attr_len / 4; + *pos++ = value >> 8; + *pos++ = value & 0xff; + if (data) + memcpy(pos, data, len); + if (pad_len) { + pos += len; + memset(pos, 0, pad_len); + } + msg->used += attr_len; + return start; +} + + +u8 * eap_sim_msg_add_mac(struct eap_sim_msg *msg, u8 attr) +{ + u8 *pos = eap_sim_msg_add(msg, attr, 0, NULL, EAP_SIM_MAC_LEN); + if (pos) + msg->mac = (pos - msg->buf) + 4; + return pos; +} + + +int eap_sim_msg_add_encr_start(struct eap_sim_msg *msg, u8 attr_iv, + u8 attr_encr) +{ + u8 *pos = eap_sim_msg_add(msg, attr_iv, 0, NULL, EAP_SIM_IV_LEN); + if (pos == NULL) + return -1; + msg->iv = (pos - msg->buf) + 4; + if (hostapd_get_rand(msg->buf + msg->iv, EAP_SIM_IV_LEN)) { + msg->iv = 0; + return -1; + } + + pos = eap_sim_msg_add(msg, attr_encr, 0, NULL, 0); + if (pos == NULL) { + msg->iv = 0; + return -1; + } + msg->encr = pos - msg->buf; + + return 0; +} + + +int eap_sim_msg_add_encr_end(struct eap_sim_msg *msg, u8 *k_encr, int attr_pad) +{ + size_t encr_len; + + if (k_encr == NULL || msg->iv == 0 || msg->encr == 0) + return -1; + + encr_len = msg->used - msg->encr - 4; + if (encr_len % 16) { + u8 *pos; + int pad_len = 16 - (encr_len % 16); + if (pad_len < 4) { + wpa_printf(MSG_WARNING, "EAP-SIM: " + "eap_sim_msg_add_encr_end - invalid pad_len" + " %d", pad_len); + return -1; + } + wpa_printf(MSG_DEBUG, " *AT_PADDING"); + pos = eap_sim_msg_add(msg, attr_pad, 0, NULL, pad_len - 4); + if (pos == NULL) + return -1; + memset(pos + 4, 0, pad_len - 4); + encr_len += pad_len; + } + wpa_printf(MSG_DEBUG, " (AT_ENCR_DATA data len %lu)", + (unsigned long) encr_len); + msg->buf[msg->encr + 1] = encr_len / 4 + 1; + aes_128_cbc_encrypt(k_encr, msg->buf + msg->iv, + msg->buf + msg->encr + 4, encr_len); + + return 0; +} + + +void eap_sim_report_notification(void *msg_ctx, int notification, int aka) +{ + const char *type = aka ? "AKA" : "SIM"; + + switch (notification) { + case EAP_SIM_GENERAL_FAILURE_AFTER_AUTH: + wpa_printf(MSG_WARNING, "EAP-%s: General failure " + "notification (after authentication)", type); + break; + case EAP_SIM_TEMPORARILY_DENIED: + wpa_printf(MSG_WARNING, "EAP-%s: Failure notification: " + "User has been temporarily denied access to the " + "requested service", type); + break; + case EAP_SIM_NOT_SUBSCRIBED: + wpa_printf(MSG_WARNING, "EAP-%s: Failure notification: " + "User has not subscribed to the requested service", + type); + break; + case EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH: + wpa_printf(MSG_WARNING, "EAP-%s: General failure " + "notification (before authentication)", type); + break; + case EAP_SIM_SUCCESS: + wpa_printf(MSG_INFO, "EAP-%s: Successful authentication " + "notification", type); + break; + default: + if (notification >= 32768) { + wpa_printf(MSG_INFO, "EAP-%s: Unrecognized " + "non-failure notification %d", + type, notification); + } else { + wpa_printf(MSG_WARNING, "EAP-%s: Unrecognized " + "failure notification %d", + type, notification); + } + } +} + + +#ifdef TEST_MAIN_EAP_SIM_COMMON +static int test_eap_sim_prf(void) +{ + /* http://csrc.nist.gov/encryption/dss/Examples-1024bit.pdf */ + u8 xkey[] = { + 0xbd, 0x02, 0x9b, 0xbe, 0x7f, 0x51, 0x96, 0x0b, + 0xcf, 0x9e, 0xdb, 0x2b, 0x61, 0xf0, 0x6f, 0x0f, + 0xeb, 0x5a, 0x38, 0xb6 + }; + u8 w[] = { + 0x20, 0x70, 0xb3, 0x22, 0x3d, 0xba, 0x37, 0x2f, + 0xde, 0x1c, 0x0f, 0xfc, 0x7b, 0x2e, 0x3b, 0x49, + 0x8b, 0x26, 0x06, 0x14, 0x3c, 0x6c, 0x18, 0xba, + 0xcb, 0x0f, 0x6c, 0x55, 0xba, 0xbb, 0x13, 0x78, + 0x8e, 0x20, 0xd7, 0x37, 0xa3, 0x27, 0x51, 0x16 + }; + u8 buf[40]; + + printf("Testing EAP-SIM PRF (FIPS 186-2 + change notice 1)\n"); + eap_sim_prf(xkey, buf, sizeof(buf)); + if (memcmp(w, buf, sizeof(w) != 0)) { + printf("eap_sim_prf failed\n"); + return 1; + } + + return 0; +} + + +int main(int argc, char *argv[]) +{ + int errors = 0; + + errors += test_eap_sim_prf(); + + return errors; +} +#endif /* TEST_MAIN_EAP_SIM_COMMON */ diff --git a/contrib/wpa_supplicant/eap_sim_common.h b/contrib/wpa_supplicant/eap_sim_common.h new file mode 100644 index 0000000..c89e04e --- /dev/null +++ b/contrib/wpa_supplicant/eap_sim_common.h @@ -0,0 +1,101 @@ +#ifndef EAP_SIM_COMMON_H +#define EAP_SIM_COMMON_H + +#define EAP_SIM_NONCE_S_LEN 16 +#define EAP_SIM_NONCE_MT_LEN 16 +#define EAP_SIM_MAC_LEN 16 +#define EAP_SIM_MK_LEN 20 +#define EAP_SIM_K_AUT_LEN 16 +#define EAP_SIM_K_ENCR_LEN 16 +#define EAP_SIM_KEYING_DATA_LEN 64 +#define EAP_SIM_IV_LEN 16 + +#define GSM_RAND_LEN 16 + +#define AKA_RAND_LEN 16 +#define AKA_AUTN_LEN 16 + +void eap_sim_derive_keys(const u8 *mk, u8 *k_encr, u8 *k_aut, u8 *msk); +void eap_sim_derive_keys_reauth(unsigned int _counter, + const u8 *identity, size_t identity_len, + const u8 *nonce_s, const u8 *mk, u8 *msk); +int eap_sim_verify_mac(const u8 *k_aut, u8 *req, size_t req_len, u8 *mac, + u8 *extra, size_t extra_len); +void eap_sim_add_mac(const u8 *k_aut, u8 *msg, size_t msg_len, u8 *mac, + const u8 *extra, size_t extra_len); + + +/* EAP-SIM/AKA Attributes (0..127 non-skippable) */ +#define EAP_SIM_AT_RAND 1 +#define EAP_SIM_AT_AUTN 2 /* only AKA */ +#define EAP_SIM_AT_RES 3 /* only AKA, only send */ +#define EAP_SIM_AT_AUTS 4 /* only AKA, only send */ +#define EAP_SIM_AT_PADDING 6 /* only encrypted */ +#define EAP_SIM_AT_NONCE_MT 7 /* only SIM, only send */ +#define EAP_SIM_AT_PERMANENT_ID_REQ 10 +#define EAP_SIM_AT_MAC 11 +#define EAP_SIM_AT_NOTIFICATION 12 +#define EAP_SIM_AT_ANY_ID_REQ 13 +#define EAP_SIM_AT_IDENTITY 14 /* only send */ +#define EAP_SIM_AT_VERSION_LIST 15 /* only SIM */ +#define EAP_SIM_AT_SELECTED_VERSION 16 /* only SIM */ +#define EAP_SIM_AT_FULLAUTH_ID_REQ 17 +#define EAP_SIM_AT_COUNTER 19 /* only encrypted */ +#define EAP_SIM_AT_COUNTER_TOO_SMALL 20 /* only encrypted */ +#define EAP_SIM_AT_NONCE_S 21 /* only encrypted */ +#define EAP_SIM_AT_CLIENT_ERROR_CODE 22 /* only send */ +#define EAP_SIM_AT_IV 129 +#define EAP_SIM_AT_ENCR_DATA 130 +#define EAP_SIM_AT_NEXT_PSEUDONYM 132 /* only encrypted */ +#define EAP_SIM_AT_NEXT_REAUTH_ID 133 /* only encrypted */ +#define EAP_SIM_AT_CHECKCODE 134 /* only AKA */ +#define EAP_SIM_AT_RESULT_IND 135 + +/* AT_NOTIFICATION notification code values */ +#define EAP_SIM_GENERAL_FAILURE_AFTER_AUTH 0 +#define EAP_SIM_TEMPORARILY_DENIED 1026 +#define EAP_SIM_NOT_SUBSCRIBED 1031 +#define EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH 16384 +#define EAP_SIM_SUCCESS 32768 + + +enum eap_sim_id_req { + NO_ID_REQ, ANY_ID, FULLAUTH_ID, PERMANENT_ID +}; + + +struct eap_sim_attrs { + u8 *rand, *autn, *mac, *iv, *encr_data, *version_list, *nonce_s; + u8 *next_pseudonym, *next_reauth_id; + u8 *nonce_mt, *identity; + size_t num_chal, version_list_len, encr_data_len; + size_t next_pseudonym_len, next_reauth_id_len, identity_len; + enum eap_sim_id_req id_req; + int notification, counter, selected_version, client_error_code; +}; + +int eap_sim_parse_attr(u8 *start, u8 *end, struct eap_sim_attrs *attr, + int aka, int encr); +int eap_sim_parse_encr(const u8 *k_encr, u8 *encr_data, size_t encr_data_len, + const u8 *iv, struct eap_sim_attrs *attr, int aka); + + +struct eap_sim_msg; + +struct eap_sim_msg * eap_sim_msg_init(int code, int id, int type, int subtype); +u8 * eap_sim_msg_finish(struct eap_sim_msg *msg, size_t *len, const u8 *k_aut, + const u8 *extra, size_t extra_len); +void eap_sim_msg_free(struct eap_sim_msg *msg); +u8 * eap_sim_msg_add_full(struct eap_sim_msg *msg, u8 attr, + const u8 *data, size_t len); +u8 * eap_sim_msg_add(struct eap_sim_msg *msg, u8 attr, + u16 value, const u8 *data, size_t len); +u8 * eap_sim_msg_add_mac(struct eap_sim_msg *msg, u8 attr); +int eap_sim_msg_add_encr_start(struct eap_sim_msg *msg, u8 attr_iv, + u8 attr_encr); +int eap_sim_msg_add_encr_end(struct eap_sim_msg *msg, u8 *k_encr, + int attr_pad); + +void eap_sim_report_notification(void *msg_ctx, int notification, int aka); + +#endif /* EAP_SIM_COMMON_H */ diff --git a/contrib/wpa_supplicant/eap_testing.txt b/contrib/wpa_supplicant/eap_testing.txt new file mode 100644 index 0000000..03ef285 --- /dev/null +++ b/contrib/wpa_supplicant/eap_testing.txt @@ -0,0 +1,349 @@ +Automatic regression and interoperability testing of wpa_supplicant's +IEEE 802.1X/EAPOL authentication + +Test program: +- Linked some parts of IEEE 802.1X Authenticator implementation from + hostapd (RADIUS client and RADIUS processing, EAP<->RADIUS + encapsulation/decapsulation) into wpa_supplicant. +- Replaced wpa_supplicant.c and wpa.c with test code that trigger + IEEE 802.1X authentication automatically without need for wireless + client card or AP. +- For EAP methods that generate keying material, the key derived by the + Supplicant is verified to match with the one received by the (now + integrated) Authenticator. + +The full automated test suite can now be run in couple of seconds, but +I'm more than willing to add new RADIUS authentication servers to make +this take a bit more time.. ;-) As an extra bonus, this can also be +seen as automatic regression/interoperability testing for the RADIUS +server, too. + +In order for me to be able to use a new authentication server, the +server need to be available from Internet (at least from one static IP +address) and I will need to get suitable user name/password pairs, +certificates, and private keys for testing use. Other alternative +would be to get an evaluation version of the server so that I can +install it on my own test setup. If you are interested in providing +either server access or evaluation version, please contact me +(jkmaline@cc.hut.fi). + + +Test matrix + ++) tested successfully +F) failed +-) server did not support +?) not tested + +hostapd --------------------------------------------------------. +Cisco Aironet 1200 AP (local RADIUS server) ----------------. | +Corriente Elektron -------------------------------------. | | +Lucent NavisRadiator -------------------------------. | | | +Interlink RAD-Series ---------------------------. | | | | +Radiator -----------------------------------. | | | | | +Meetinghouse Aegis ---------------------. | | | | | | +Funk Steel-Belted ------------------. | | | | | | | +Funk Odyssey -------------------. | | | | | | | | +Microsoft IAS --------------. | | | | | | | | | +FreeRADIUS -------------. | | | | | | | | | | + | | | | | | | | | | | + +EAP-MD5 + - - + + + + + - - + +EAP-GTC + - - ? + + + + - - + +EAP-OTP - - - - - + - - - - - +EAP-MSCHAPv2 + - - + + + + + - - + +EAP-TLS + + + + + + + + - - + +EAP-PEAPv0/MSCHAPv2 + + + + + + + + + - + +EAP-PEAPv0/GTC + - + - + + + + - - + +EAP-PEAPv0/OTP - - - - - + - - - - - +EAP-PEAPv0/MD5 + - - + + + + + - - + +EAP-PEAPv0/TLS - + - + + + F + - - - +EAP-PEAPv1/MSCHAPv2 - - + + + +1 + +5 +8 - + +EAP-PEAPv1/GTC - - + + + +1 + +5 - - + +EAP-PEAPv1/OTP - - - - - +1 - - - - - +EAP-PEAPv1/MD5 - - - + + +1 + +5 - - + +EAP-PEAPv1/TLS - - - + + +1 F +5 - - - +EAP-TTLS/CHAP + - +2 + + + + + + - + +EAP-TTLS/MSCHAP + - + + + + + + + - + +EAP-TTLS/MSCHAPv2 + - + + + + + + + - + +EAP-TTLS/PAP + - + + + + + + + - + +EAP-TTLS/EAP-MD5 + - +2 + + + + + - - + +EAP-TTLS/EAP-GTC + - +2 ? + + + + - - + +EAP-TTLS/EAP-OTP - - - - - + - - - - - +EAP-TTLS/EAP-MSCHAPv2 + - +2 + + + + + + - + +EAP-TTLS/EAP-TLS - - +2 + F + + + - - - +EAP-SIM +3 - - ? - + - ? - - + +EAP-AKA - - - - - + - - - - - +EAP-PSK +7 - - - - - - - - - - +EAP-FAST - - - - - - - - - + - +LEAP + - + + + + F +6 - + - + +1) PEAPv1 required new label, "client PEAP encryption" instead of "client EAP + encryption", during key derivation (requires phase1="peaplabel=1" in the + network configuration in wpa_supplicant.conf) +2) used FreeRADIUS as inner auth server +3) required a patch to FreeRADIUS to fix EAP-SIM +5) PEAPv1 required termination of negotiation on tunneled EAP-Success and new + label in key deriviation + (phase1="peap_outer_success=0 peaplabel=1") (in "IETF Draft 5" mode) +6) Authenticator simulator required patching for handling Access-Accept within + negotiation (for the first EAP-Success of LEAP) +7) EAP-PSK is not included in FreeRADIUS distribution; used external + rlm_eap_psk implementation from + http://perso.rd.francetelecom.fr/bersani/EAP_PSK/ + EAP-PSKWindowsimplementations.html +8) PEAPv1 used non-standard version negotiation (client had to force v1 even + though server reported v0 as the highest supported version) + + +Automated tests: + +FreeRADIUS (1.0pre and CVS snapshot) +- EAP-MD5-Challenge +- EAP-GTC +- EAP-MSCHAPv2 +- EAP-TLS +- EAP-PEAPv0 / MSCHAPv2 +- EAP-PEAPv0 / GTC +- EAP-PEAPv0 / MD5-Challenge +- EAP-TTLS / EAP-MD5-Challenge +- EAP-TTLS / EAP-GTC +- EAP-TTLS / EAP-MSCHAPv2 +- EAP-TTLS / CHAP +- EAP-TTLS / PAP +- EAP-TTLS / MSCHAP +- EAP-TTLS / MSCHAPv2 +- EAP-SIM +* not supported in FreeRADIUS + - EAP-PEAP / TLS (Unable to tunnel TLS inside of TLS) + - EAP-TTLS / EAP-TLS (Unable to tunnel TLS inside of TLS) + +Microsoft Windows Server 2003 / IAS +- EAP-TLS +- EAP-PEAPv0 / MSCHAPv2 +- EAP-PEAPv0 / TLS +- EAP-MD5 +* IAS does not seem to support other EAP methods + +Funk Odyssey 2.01.00.653 +- EAP-TLS +- EAP-PEAPv0 / MSCHAPv2 +- EAP-PEAPv0 / GTC +- EAP-PEAPv1 / MSCHAPv2 +- EAP-PEAPv1 / GTC + Note: PEAPv1 requires TLS key derivation to use label "client EAP encryption" +- EAP-TTLS / CHAP (using FreeRADIUS as inner auth srv) +- EAP-TTLS / MSCHAP +- EAP-TTLS / MSCHAPv2 +- EAP-TTLS / PAP +- EAP-TTLS / EAP-MD5-Challenge (using FreeRADIUS as inner auth srv) +- EAP-TTLS / EAP-GTC (using FreeRADIUS as inner auth srv) +- EAP-TTLS / EAP-MSCHAPv2 (using FreeRADIUS as inner auth srv) +- EAP-TTLS / EAP-TLS (using FreeRADIUS as inner auth srv) +* not supported in Odyssey: + - EAP-MD5-Challenge + - EAP-GTC + - EAP-MSCHAPv2 + - EAP-PEAP / MD5-Challenge + - EAP-PEAP / TLS + +Funk Steel-Belted Radius Enterprise Edition v4.71.739 +- EAP-MD5-Challenge +- EAP-MSCHAPv2 +- EAP-TLS +- EAP-PEAPv0 / MSCHAPv2 +- EAP-PEAPv0 / MD5 +- EAP-PEAPv0 / TLS +- EAP-PEAPv1 / MSCHAPv2 +- EAP-PEAPv1 / MD5 +- EAP-PEAPv1 / GTC +- EAP-PEAPv1 / TLS + Note: PEAPv1 requires TLS key derivation to use label "client EAP encryption" +- EAP-TTLS / CHAP +- EAP-TTLS / MSCHAP +- EAP-TTLS / MSCHAPv2 +- EAP-TTLS / PAP +- EAP-TTLS / EAP-MD5-Challenge +- EAP-TTLS / EAP-MSCHAPv2 +- EAP-TTLS / EAP-TLS + +Meetinghouse Aegis 1.1.4 +- EAP-MD5-Challenge +- EAP-GTC +- EAP-MSCHAPv2 +- EAP-TLS +- EAP-PEAPv0 / MSCHAPv2 +- EAP-PEAPv0 / TLS +- EAP-PEAPv0 / GTC +- EAP-PEAPv0 / MD5-Challenge +- EAP-PEAPv1 / MSCHAPv2 +- EAP-PEAPv1 / TLS +- EAP-PEAPv1 / GTC +- EAP-PEAPv1 / MD5-Challenge + Note: PEAPv1 requires TLS key derivation to use label "client EAP encryption" +- EAP-TTLS / CHAP +- EAP-TTLS / MSCHAP +- EAP-TTLS / MSCHAPv2 +- EAP-TTLS / PAP +- EAP-TTLS / EAP-MD5-Challenge +- EAP-TTLS / EAP-GTC +- EAP-TTLS / EAP-MSCHAPv2 +* did not work + - EAP-TTLS / EAP-TLS + (Server rejects authentication without any reason in debug log. It + looks like the inner TLS negotiation starts properly and the last + packet from Supplicant looks like the one sent in the Phase 1. The + server generates a valid looking reply in the same way as in Phase + 1, but then ends up sending Access-Reject. Maybe an issue with TTLS + fragmentation in the Aegis server(?) The packet seems to include + 1328 bytes of EAP-Message and this may go beyond the fragmentation + limit with AVP encapsulation and TLS tunneling. Note: EAP-PEAP/TLS + did work, so this issue seems to be with something TTLS specific.) + +Radiator 3.9 (eval, with all patches up to and including 2004-08-30) +- EAP-MD5-Challenge +- EAP-GTC +- EAP-OTP +- EAP-MSCHAPv2 +- EAP-TLS +- EAP-PEAPv0 / MSCHAPv2 +- EAP-PEAPv0 / GTC +- EAP-PEAPv0 / OTP +- EAP-PEAPv0 / MD5-Challenge +- EAP-PEAPv0 / TLS + Note: Needed to use unknown identity in outer auth and some times the server + seems to get confused and fails to send proper Phase 2 data. +- EAP-PEAPv1 / MSCHAPv2 +- EAP-PEAPv1 / GTC +- EAP-PEAPv1 / OTP +- EAP-PEAPv1 / MD5-Challenge +- EAP-PEAPv1 / TLS + Note: This has some additional requirements for EAPTLS_MaxFragmentSize. + Using 1300 for outer auth and 500 for inner auth seemed to work. + Note: Needed to use unknown identity in outer auth and some times the server + seems to get confused and fails to send proper Phase 2 data. +- EAP-TTLS / CHAP +- EAP-TTLS / MSCHAP +- EAP-TTLS / MSCHAPv2 +- EAP-TTLS / PAP +- EAP-TTLS / EAP-MD5-Challenge +- EAP-TTLS / EAP-GTC +- EAP-TTLS / EAP-OTP +- EAP-TTLS / EAP-MSCHAPv2 +- EAP-TTLS / EAP-TLS + Note: This has some additional requirements for EAPTLS_MaxFragmentSize. + Using 1300 for outer auth and 500 for inner auth seemed to work. +- EAP-SIM +- EAP-AKA + +Interlink Networks RAD-Series 6.1.2.7 +- EAP-MD5-Challenge +- EAP-GTC +- EAP-MSCHAPv2 +- EAP-TLS +- EAP-PEAPv0 / MSCHAPv2 +- EAP-PEAPv0 / GTC +- EAP-PEAPv0 / MD5-Challenge +- EAP-PEAPv1 / MSCHAPv2 +- EAP-PEAPv1 / GTC +- EAP-PEAPv1 / MD5-Challenge + Note: PEAPv1 requires TLS key derivation to use label "client EAP encryption" +- EAP-TTLS / CHAP +- EAP-TTLS / MSCHAP +- EAP-TTLS / MSCHAPv2 +- EAP-TTLS / PAP +- EAP-TTLS / EAP-MD5-Challenge +- EAP-TTLS / EAP-GTC +- EAP-TTLS / EAP-MSCHAPv2 +- EAP-TTLS / EAP-TLS +* did not work + - EAP-PEAPv0 / TLS + - EAP-PEAPv1 / TLS + (Failed to decrypt Phase 2 data) + +Lucent NavisRadius 4.4.0 +- EAP-MD5-Challenge +- EAP-GTC +- EAP-MSCHAPv2 +- EAP-TLS +- EAP-PEAPv0 / MD5-Challenge +- EAP-PEAPv0 / MSCHAPv2 +- EAP-PEAPv0 / GTC +- EAP-PEAPv0 / TLS +- EAP-PEAPv1 / MD5-Challenge +- EAP-PEAPv1 / MSCHAPv2 +- EAP-PEAPv1 / GTC +- EAP-PEAPv1 / TLS + "IETF Draft 5" mode requires phase1="peap_outer_success=0 peaplabel=1" + 'Cisco ACU 5.05' mode works without phase1 configuration +- EAP-TTLS / CHAP +- EAP-TTLS / MSCHAP +- EAP-TTLS / MSCHAPv2 +- EAP-TTLS / PAP +- EAP-TTLS / EAP-MD5-Challenge +- EAP-TTLS / EAP-MSCHAPv2 +- EAP-TTLS / EAP-GTC +- EAP-TTLS / EAP-TLS + +Note: user certificate from NavisRadius had private key in a format +that wpa_supplicant could not use. Converting this to PKCS#12 and then +back to PEM allowed wpa_supplicant to use the key. + + +hostapd v0.3.3 +- EAP-MD5-Challenge +- EAP-GTC +- EAP-MSCHAPv2 +- EAP-TLS +- EAP-PEAPv0 / MSCHAPv2 +- EAP-PEAPv0 / GTC +- EAP-PEAPv0 / MD5-Challenge +- EAP-PEAPv1 / MSCHAPv2 +- EAP-PEAPv1 / GTC +- EAP-PEAPv1 / MD5-Challenge +- EAP-TTLS / CHAP +- EAP-TTLS / MSCHAP +- EAP-TTLS / MSCHAPv2 +- EAP-TTLS / PAP +- EAP-TTLS / EAP-MD5-Challenge +- EAP-TTLS / EAP-GTC +- EAP-TTLS / EAP-MSCHAPv2 +- EAP-SIM + + + +PEAPv1: + +Funk Odyssey 2.01.00.653: +- uses tunneled EAP-Success, expects reply in tunnel or TLS ACK, sends MPPE + keys with outer EAP-Success message after this +- uses label "client EAP encryption" +- (peap_outer_success 1 and 2 work) + +Funk Steel-Belted Radius Enterprise Edition v4.71.739 +- uses tunneled EAP-Success, expects reply in tunnel or TLS ACK, sends MPPE + keys with outer EAP-Success message after this +- uses label "client EAP encryption" +- (peap_outer_success 1 and 2 work) + +Radiator 3.9: +- uses TLV Success and Reply, sends MPPE keys with outer EAP-Success message + after this +- uses label "client PEAP encryption" + +Lucent NavisRadius 4.4.0 (in "IETF Draft 5" mode): +- sends tunneled EAP-Success with MPPE keys and expects the authentication to + terminate at this point (gets somewhat confused with reply to this) +- uses label "client PEAP encryption" +- phase1="peap_outer_success=0 peaplabel=1" + +Lucent NavisRadius 4.4.0 (in "Cisco ACU 5.05" mode): +- sends tunneled EAP-Success with MPPE keys and expects to receive TLS ACK + as a reply +- uses label "client EAP encryption" + +Meetinghouse Aegis 1.1.4 +- uses tunneled EAP-Success, expects reply in tunnel or TLS ACK, sends MPPE + keys with outer EAP-Success message after this +- uses label "client EAP encryption" +- peap_outer_success 1 and 2 work diff --git a/contrib/wpa_supplicant/eap_tls.c b/contrib/wpa_supplicant/eap_tls.c new file mode 100644 index 0000000..4b02cca --- /dev/null +++ b/contrib/wpa_supplicant/eap_tls.c @@ -0,0 +1,245 @@ +/* + * WPA Supplicant / EAP-TLS (RFC 2716) + * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include "common.h" +#include "eap_i.h" +#include "eap_tls_common.h" +#include "wpa_supplicant.h" +#include "config_ssid.h" +#include "tls.h" + + +static void eap_tls_deinit(struct eap_sm *sm, void *priv); + + +struct eap_tls_data { + struct eap_ssl_data ssl; + u8 *key_data; +}; + + +static void * eap_tls_init(struct eap_sm *sm) +{ + struct eap_tls_data *data; + struct wpa_ssid *config = eap_get_config(sm); + if (config == NULL || + (sm->init_phase2 ? config->private_key2 : config->private_key) + == NULL) { + wpa_printf(MSG_INFO, "EAP-TLS: Private key not configured"); + return NULL; + } + + data = malloc(sizeof(*data)); + if (data == NULL) + return NULL; + memset(data, 0, sizeof(*data)); + + if (eap_tls_ssl_init(sm, &data->ssl, config)) { + wpa_printf(MSG_INFO, "EAP-TLS: Failed to initialize SSL."); + eap_tls_deinit(sm, data); + return NULL; + } + + return data; +} + + +static void eap_tls_deinit(struct eap_sm *sm, void *priv) +{ + struct eap_tls_data *data = priv; + if (data == NULL) + return; + eap_tls_ssl_deinit(sm, &data->ssl); + free(data->key_data); + free(data); +} + + +static u8 * eap_tls_process(struct eap_sm *sm, void *priv, + struct eap_method_ret *ret, + u8 *reqData, size_t reqDataLen, + size_t *respDataLen) +{ + struct eap_hdr *req; + int left, res; + unsigned int tls_msg_len; + u8 flags, *pos, *resp, id; + struct eap_tls_data *data = priv; + + if (tls_get_errors(sm->ssl_ctx)) { + wpa_printf(MSG_INFO, "EAP-TLS: TLS errors detected"); + ret->ignore = TRUE; + return NULL; + } + + req = (struct eap_hdr *) reqData; + pos = (u8 *) (req + 1); + if (reqDataLen < sizeof(*req) + 2 || *pos != EAP_TYPE_TLS) { + wpa_printf(MSG_INFO, "EAP-TLS: Invalid frame"); + ret->ignore = TRUE; + return NULL; + } + id = req->identifier; + pos++; + flags = *pos++; + left = be_to_host16(req->length) - sizeof(struct eap_hdr) - 2; + wpa_printf(MSG_DEBUG, "EAP-TLS: Received packet(len=%lu) - " + "Flags 0x%02x", (unsigned long) reqDataLen, flags); + if (flags & EAP_TLS_FLAGS_LENGTH_INCLUDED) { + if (left < 4) { + wpa_printf(MSG_INFO, "EAP-TLS: Short frame with TLS " + "length"); + ret->ignore = TRUE; + return NULL; + } + tls_msg_len = (pos[0] << 24) | (pos[1] << 16) | (pos[2] << 8) | + pos[3]; + wpa_printf(MSG_DEBUG, "EAP-TLS: TLS Message Length: %d", + tls_msg_len); + if (data->ssl.tls_in_left == 0) { + data->ssl.tls_in_total = tls_msg_len; + data->ssl.tls_in_left = tls_msg_len; + free(data->ssl.tls_in); + data->ssl.tls_in = NULL; + data->ssl.tls_in_len = 0; + } + pos += 4; + left -= 4; + } + + ret->ignore = FALSE; + + ret->methodState = METHOD_CONT; + ret->decision = DECISION_COND_SUCC; + ret->allowNotifications = TRUE; + + if (flags & EAP_TLS_FLAGS_START) { + wpa_printf(MSG_DEBUG, "EAP-TLS: Start"); + left = 0; /* make sure that this frame is empty, even though it + * should always be, anyway */ + } + + resp = NULL; + res = eap_tls_process_helper(sm, &data->ssl, EAP_TYPE_TLS, 0, id, pos, + left, &resp, respDataLen); + + if (res < 0) { + wpa_printf(MSG_DEBUG, "EAP-TLS: TLS processing failed"); + ret->methodState = METHOD_MAY_CONT; + ret->decision = DECISION_FAIL; + return eap_tls_build_ack(&data->ssl, respDataLen, id, + EAP_TYPE_TLS, 0); + } + + if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) { + wpa_printf(MSG_DEBUG, "EAP-TLS: Done"); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_UNCOND_SUCC; + free(data->key_data); + data->key_data = eap_tls_derive_key(sm, &data->ssl, + "client EAP encryption", + EAP_TLS_KEY_LEN); + if (data->key_data) { + wpa_hexdump_key(MSG_DEBUG, "EAP-TLS: Derived key", + data->key_data, EAP_TLS_KEY_LEN); + } else { + wpa_printf(MSG_DEBUG, "EAP-TLS: Failed to derive key"); + } + } + + if (res == 1) { + return eap_tls_build_ack(&data->ssl, respDataLen, id, + EAP_TYPE_TLS, 0); + } + return resp; +} + + +static Boolean eap_tls_has_reauth_data(struct eap_sm *sm, void *priv) +{ + struct eap_tls_data *data = priv; + return tls_connection_established(sm->ssl_ctx, data->ssl.conn); +} + + +static void eap_tls_deinit_for_reauth(struct eap_sm *sm, void *priv) +{ +} + + +static void * eap_tls_init_for_reauth(struct eap_sm *sm, void *priv) +{ + struct eap_tls_data *data = priv; + free(data->key_data); + data->key_data = NULL; + if (eap_tls_reauth_init(sm, &data->ssl)) { + free(data); + return NULL; + } + return priv; +} + + +static int eap_tls_get_status(struct eap_sm *sm, void *priv, char *buf, + size_t buflen, int verbose) +{ + struct eap_tls_data *data = priv; + return eap_tls_status(sm, &data->ssl, buf, buflen, verbose); +} + + +static Boolean eap_tls_isKeyAvailable(struct eap_sm *sm, void *priv) +{ + struct eap_tls_data *data = priv; + return data->key_data != NULL; +} + + +static u8 * eap_tls_getKey(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_tls_data *data = priv; + u8 *key; + + if (data->key_data == NULL) + return NULL; + + key = malloc(EAP_TLS_KEY_LEN); + if (key == NULL) + return NULL; + + *len = EAP_TLS_KEY_LEN; + memcpy(key, data->key_data, EAP_TLS_KEY_LEN); + + return key; +} + + +const struct eap_method eap_method_tls = +{ + .method = EAP_TYPE_TLS, + .name = "TLS", + .init = eap_tls_init, + .deinit = eap_tls_deinit, + .process = eap_tls_process, + .isKeyAvailable = eap_tls_isKeyAvailable, + .getKey = eap_tls_getKey, + .get_status = eap_tls_get_status, + .has_reauth_data = eap_tls_has_reauth_data, + .deinit_for_reauth = eap_tls_deinit_for_reauth, + .init_for_reauth = eap_tls_init_for_reauth, +}; diff --git a/contrib/wpa_supplicant/eap_tls_common.c b/contrib/wpa_supplicant/eap_tls_common.c new file mode 100644 index 0000000..20b1418 --- /dev/null +++ b/contrib/wpa_supplicant/eap_tls_common.c @@ -0,0 +1,338 @@ +/* + * WPA Supplicant / EAP-TLS/PEAP/TTLS/FAST common functions + * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include "common.h" +#include "eap_i.h" +#include "eap_tls_common.h" +#include "wpa_supplicant.h" +#include "config_ssid.h" +#include "md5.h" +#include "sha1.h" +#include "tls.h" + + +int eap_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data, + struct wpa_ssid *config) +{ + int ret = -1; + char *ca_cert, *client_cert, *private_key, *private_key_passwd, + *dh_file, *subject_match; + + data->eap = sm; + data->phase2 = sm->init_phase2; + if (config == NULL) { + ca_cert = NULL; + client_cert = NULL; + private_key = NULL; + private_key_passwd = NULL; + dh_file = NULL; + subject_match = NULL; + } else if (data->phase2) { + ca_cert = (char *) config->ca_cert2; + client_cert = (char *) config->client_cert2; + private_key = (char *) config->private_key2; + private_key_passwd = (char *) config->private_key2_passwd; + dh_file = (char *) config->dh_file2; + subject_match = (char *) config->subject_match2; + } else { + ca_cert = (char *) config->ca_cert; + client_cert = (char *) config->client_cert; + private_key = (char *) config->private_key; + private_key_passwd = (char *) config->private_key_passwd; + dh_file = (char *) config->dh_file; + subject_match = (char *) config->subject_match; + } + data->conn = tls_connection_init(sm->ssl_ctx); + if (data->conn == NULL) { + wpa_printf(MSG_INFO, "SSL: Failed to initialize new TLS " + "connection"); + goto done; + } + + if (tls_connection_ca_cert(sm->ssl_ctx, data->conn, ca_cert, + subject_match)) { + wpa_printf(MSG_INFO, "TLS: Failed to load root certificate " + "'%s'", ca_cert); + goto done; + } + + if (tls_connection_client_cert(sm->ssl_ctx, data->conn, client_cert)) { + wpa_printf(MSG_INFO, "TLS: Failed to load client certificate " + "'%s'", client_cert); + goto done; + } + + if (tls_connection_private_key(sm->ssl_ctx, data->conn, private_key, + private_key_passwd)) { + wpa_printf(MSG_INFO, "TLS: Failed to load private key '%s'", + private_key); + goto done; + } + + if (dh_file && tls_connection_dh(sm->ssl_ctx, data->conn, dh_file)) { + wpa_printf(MSG_INFO, "TLS: Failed to load DH file '%s'", + dh_file); + goto done; + } + + /* TODO: make this configurable */ + data->tls_out_limit = 1398; + if (data->phase2) { + /* Limit the fragment size in the inner TLS authentication + * since the outer authentication with EAP-PEAP does not yet + * support fragmentation */ + if (data->tls_out_limit > 100) + data->tls_out_limit -= 100; + } + + if (config && config->phase1 && + strstr(config->phase1, "include_tls_length=1")) { + wpa_printf(MSG_DEBUG, "TLS: Include TLS Message Length in " + "unfragmented packets"); + data->include_tls_length = 1; + } + + ret = 0; + +done: + return ret; +} + + +void eap_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data) +{ + tls_connection_deinit(sm->ssl_ctx, data->conn); + free(data->tls_in); + free(data->tls_out); +} + + +u8 * eap_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data, + char *label, size_t len) +{ + struct tls_keys keys; + u8 *random; + u8 *out; + + if (tls_connection_get_keys(sm->ssl_ctx, data->conn, &keys)) + return NULL; + out = malloc(len); + random = malloc(keys.client_random_len + keys.server_random_len); + if (out == NULL || random == NULL) { + free(out); + free(random); + return NULL; + } + memcpy(random, keys.client_random, keys.client_random_len); + memcpy(random + keys.client_random_len, keys.server_random, + keys.server_random_len); + + if (tls_prf(keys.master_key, keys.master_key_len, + label, random, keys.client_random_len + + keys.server_random_len, out, len)) { + free(random); + free(out); + return NULL; + } + free(random); + return out; +} + + +int eap_tls_data_reassemble(struct eap_sm *sm, struct eap_ssl_data *data, + u8 **in_data, size_t *in_len) +{ + u8 *buf; + + if (data->tls_in_left > *in_len || data->tls_in) { + buf = realloc(data->tls_in, data->tls_in_len + *in_len); + if (buf == NULL) { + free(data->tls_in); + data->tls_in = NULL; + data->tls_in_len = 0; + wpa_printf(MSG_INFO, "SSL: Could not allocate memory " + "for TLS data"); + return -1; + } + memcpy(buf + data->tls_in_len, *in_data, *in_len); + data->tls_in = buf; + data->tls_in_len += *in_len; + if (*in_len > data->tls_in_left) { + wpa_printf(MSG_INFO, "SSL: more data than TLS message " + "length indicated"); + data->tls_in_left = 0; + return -1; + } + data->tls_in_left -= *in_len; + if (data->tls_in_left > 0) { + wpa_printf(MSG_DEBUG, "SSL: Need %lu bytes more input " + "data", (unsigned long) data->tls_in_left); + return 1; + } + + *in_data = data->tls_in; + *in_len = data->tls_in_len; + } else + data->tls_in_left = 0; + + return 0; +} + + +int eap_tls_process_helper(struct eap_sm *sm, struct eap_ssl_data *data, + int eap_type, int peap_version, + u8 id, u8 *in_data, size_t in_len, + u8 **out_data, size_t *out_len) +{ + size_t len; + u8 *pos, *flags; + struct eap_hdr *resp; + + WPA_ASSERT(data->tls_out_len == 0 || in_len == 0); + *out_len = 0; + + if (data->tls_out_len == 0) { + /* No more data to send out - expect to receive more data from + * the AS. */ + int res = eap_tls_data_reassemble(sm, data, &in_data, &in_len); + if (res < 0 || res == 1) + return res; + /* Full TLS message reassembled - continue handshake processing + */ + if (data->tls_out) { + /* This should not happen.. */ + wpa_printf(MSG_INFO, "SSL: eap_tls_process_helper - " + "pending tls_out data even though " + "tls_out_len = 0"); + free(data->tls_out); + WPA_ASSERT(data->tls_out == NULL); + } + data->tls_out = tls_connection_handshake(sm->ssl_ctx, + data->conn, + in_data, in_len, + &data->tls_out_len); + + /* Clear reassembled input data (if the buffer was needed). */ + data->tls_in_left = data->tls_in_total = data->tls_in_len = 0; + free(data->tls_in); + data->tls_in = NULL; + } + + if (data->tls_out == NULL) { + data->tls_out_len = 0; + return -1; + } + if (data->tls_out_len == 0) { + /* TLS negotiation should now be complete since all other cases + * needing more that should have been catched above based on + * the TLS Message Length field. */ + wpa_printf(MSG_DEBUG, "SSL: No data to be sent out"); + free(data->tls_out); + data->tls_out = NULL; + return 1; + } + + wpa_printf(MSG_DEBUG, "SSL: %lu bytes left to be sent out (of total " + "%lu bytes)", + (unsigned long) data->tls_out_len - data->tls_out_pos, + (unsigned long) data->tls_out_len); + resp = malloc(sizeof(struct eap_hdr) + 2 + 4 + data->tls_out_limit); + if (resp == NULL) { + *out_data = NULL; + return -1; + } + resp->code = EAP_CODE_RESPONSE; + resp->identifier = id; + pos = (u8 *) (resp + 1); + *pos++ = eap_type; + flags = pos++; + *flags = peap_version; + if (data->tls_out_pos == 0 && + (data->tls_out_len > data->tls_out_limit || + data->include_tls_length)) { + *flags |= EAP_TLS_FLAGS_LENGTH_INCLUDED; + *pos++ = (data->tls_out_len >> 24) & 0xff; + *pos++ = (data->tls_out_len >> 16) & 0xff; + *pos++ = (data->tls_out_len >> 8) & 0xff; + *pos++ = data->tls_out_len & 0xff; + } + + len = data->tls_out_len - data->tls_out_pos; + if (len > data->tls_out_limit) { + *flags |= EAP_TLS_FLAGS_MORE_FRAGMENTS; + len = data->tls_out_limit; + wpa_printf(MSG_DEBUG, "SSL: sending %lu bytes, more fragments " + "will follow", (unsigned long) len); + } + memcpy(pos, &data->tls_out[data->tls_out_pos], len); + data->tls_out_pos += len; + *out_len = (pos - (u8 *) resp) + len; + resp->length = host_to_be16(*out_len); + *out_data = (u8 *) resp; + + if (!(*flags & EAP_TLS_FLAGS_MORE_FRAGMENTS)) { + data->tls_out_len = 0; + data->tls_out_pos = 0; + free(data->tls_out); + data->tls_out = NULL; + } + + return 0; +} + + +u8 * eap_tls_build_ack(struct eap_ssl_data *data, size_t *respDataLen, u8 id, + int eap_type, int peap_version) +{ + struct eap_hdr *resp; + u8 *pos; + + *respDataLen = sizeof(struct eap_hdr) + 2; + resp = malloc(*respDataLen); + if (resp == NULL) + return NULL; + wpa_printf(MSG_DEBUG, "SSL: Building ACK"); + resp->code = EAP_CODE_RESPONSE; + resp->identifier = id; + resp->length = host_to_be16(*respDataLen); + pos = (u8 *) (resp + 1); + *pos++ = eap_type; /* Type */ + *pos = peap_version; /* Flags */ + return (u8 *) resp; +} + + +int eap_tls_reauth_init(struct eap_sm *sm, struct eap_ssl_data *data) +{ + return tls_connection_shutdown(sm->ssl_ctx, data->conn); +} + + +int eap_tls_status(struct eap_sm *sm, struct eap_ssl_data *data, char *buf, + size_t buflen, int verbose) +{ + char name[128]; + int len = 0; + + if (tls_get_cipher(sm->ssl_ctx, data->conn, name, sizeof(name)) == 0) { + len += snprintf(buf + len, buflen - len, + "EAP TLS cipher=%s\n", name); + } + + return len; +} diff --git a/contrib/wpa_supplicant/eap_tls_common.h b/contrib/wpa_supplicant/eap_tls_common.h new file mode 100644 index 0000000..499cae4 --- /dev/null +++ b/contrib/wpa_supplicant/eap_tls_common.h @@ -0,0 +1,51 @@ +#ifndef EAP_TLS_COMMON_H +#define EAP_TLS_COMMON_H + +struct eap_ssl_data { + struct tls_connection *conn; + + u8 *tls_out; + size_t tls_out_len; + size_t tls_out_pos; + size_t tls_out_limit; + u8 *tls_in; + size_t tls_in_len; + size_t tls_in_left; + size_t tls_in_total; + + int phase2; + int include_tls_length; /* include TLS length field even if the TLS + * data is not fragmented */ + + struct eap_sm *eap; +}; + + +/* EAP TLS Flags */ +#define EAP_TLS_FLAGS_LENGTH_INCLUDED 0x80 +#define EAP_TLS_FLAGS_MORE_FRAGMENTS 0x40 +#define EAP_TLS_FLAGS_START 0x20 +#define EAP_PEAP_VERSION_MASK 0x07 + + /* could be up to 128 bytes, but only the first 64 bytes are used */ +#define EAP_TLS_KEY_LEN 64 + + +int eap_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data, + struct wpa_ssid *config); +void eap_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data); +u8 * eap_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data, + char *label, size_t len); +int eap_tls_data_reassemble(struct eap_sm *sm, struct eap_ssl_data *data, + u8 **in_data, size_t *in_len); +int eap_tls_process_helper(struct eap_sm *sm, struct eap_ssl_data *data, + int eap_type, int peap_version, + u8 id, u8 *in_data, size_t in_len, + u8 **out_data, size_t *out_len); +u8 * eap_tls_build_ack(struct eap_ssl_data *data, size_t *respDataLen, u8 id, + int eap_type, int peap_version); +int eap_tls_reauth_init(struct eap_sm *sm, struct eap_ssl_data *data); +int eap_tls_status(struct eap_sm *sm, struct eap_ssl_data *data, char *buf, + size_t buflen, int verbose); + +#endif /* EAP_TLS_COMMON_H */ diff --git a/contrib/wpa_supplicant/eap_tlv.c b/contrib/wpa_supplicant/eap_tlv.c new file mode 100644 index 0000000..5571d8b --- /dev/null +++ b/contrib/wpa_supplicant/eap_tlv.c @@ -0,0 +1,176 @@ +/* + * WPA Supplicant / EAP-TLV (draft-josefsson-pppext-eap-tls-eap-07.txt) + * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include "common.h" +#include "wpa_supplicant.h" +#include "eap_i.h" +#include "eap_tlv.h" + + +u8 * eap_tlv_build_nak(int id, int nak_type, size_t *resp_len) +{ + struct eap_hdr *hdr; + u8 *pos; + + *resp_len = sizeof(struct eap_hdr) + 1 + 10; + hdr = malloc(*resp_len); + if (hdr == NULL) + return NULL; + + hdr->code = EAP_CODE_RESPONSE; + hdr->identifier = id; + hdr->length = host_to_be16(*resp_len); + pos = (u8 *) (hdr + 1); + *pos++ = EAP_TYPE_TLV; + *pos++ = 0x80; /* Mandatory */ + *pos++ = EAP_TLV_NAK_TLV; + /* Length */ + *pos++ = 0; + *pos++ = 6; + /* Vendor-Id */ + *pos++ = 0; + *pos++ = 0; + *pos++ = 0; + *pos++ = 0; + /* NAK-Type */ + *pos++ = nak_type >> 8; + *pos++ = nak_type & 0xff; + + return (u8 *) hdr; +} + + +u8 * eap_tlv_build_result(int id, int status, size_t *resp_len) +{ + struct eap_hdr *hdr; + u8 *pos; + + *resp_len = sizeof(struct eap_hdr) + 1 + 6; + hdr = malloc(*resp_len); + if (hdr == NULL) + return NULL; + + hdr->code = EAP_CODE_RESPONSE; + hdr->identifier = id; + hdr->length = host_to_be16(*resp_len); + pos = (u8 *) (hdr + 1); + *pos++ = EAP_TYPE_TLV; + *pos++ = 0x80; /* Mandatory */ + *pos++ = EAP_TLV_RESULT_TLV; + /* Length */ + *pos++ = 0; + *pos++ = 2; + /* Status */ + *pos++ = status >> 8; + *pos++ = status & 0xff; + + return (u8 *) hdr; +} + + +int eap_tlv_process(struct eap_sm *sm, struct eap_method_ret *ret, + struct eap_hdr *hdr, u8 **resp, size_t *resp_len) +{ + size_t left; + u8 *pos; + u8 *result_tlv = NULL; + size_t result_tlv_len = 0; + int tlv_type, mandatory, tlv_len; + + /* Parse TLVs */ + left = be_to_host16(hdr->length) - sizeof(struct eap_hdr) - 1; + pos = (u8 *) (hdr + 1); + pos++; + wpa_hexdump(MSG_DEBUG, "EAP-TLV: Received TLVs", pos, left); + while (left >= 4) { + mandatory = !!(pos[0] & 0x80); + tlv_type = pos[0] & 0x3f; + tlv_type = (tlv_type << 8) | pos[1]; + tlv_len = ((int) pos[2] << 8) | pos[3]; + pos += 4; + left -= 4; + if (tlv_len > left) { + wpa_printf(MSG_DEBUG, "EAP-TLV: TLV underrun " + "(tlv_len=%d left=%lu)", tlv_len, + (unsigned long) left); + return -1; + } + switch (tlv_type) { + case EAP_TLV_RESULT_TLV: + result_tlv = pos; + result_tlv_len = tlv_len; + break; + default: + wpa_printf(MSG_DEBUG, "EAP-TLV: Unsupported TLV Type " + "%d%s", tlv_type, + mandatory ? " (mandatory)" : ""); + if (mandatory) { + /* NAK TLV and ignore all TLVs in this packet. + */ + *resp = eap_tlv_build_nak(hdr->identifier, + tlv_type, resp_len); + return *resp == NULL ? -1 : 0; + } + /* Ignore this TLV, but process other TLVs */ + break; + } + + pos += tlv_len; + left -= tlv_len; + } + if (left) { + wpa_printf(MSG_DEBUG, "EAP-TLV: Last TLV too short in " + "Request (left=%lu)", (unsigned long) left); + return -1; + } + + /* Process supported TLVs */ + if (result_tlv) { + int status, resp_status; + wpa_hexdump(MSG_DEBUG, "EAP-TLV: Result TLV", + result_tlv, result_tlv_len); + if (result_tlv_len < 2) { + wpa_printf(MSG_INFO, "EAP-TLV: Too short Result TLV " + "(len=%lu)", + (unsigned long) result_tlv_len); + return -1; + } + status = ((int) result_tlv[0] << 8) | result_tlv[1]; + if (status == EAP_TLV_RESULT_SUCCESS) { + wpa_printf(MSG_INFO, "EAP-TLV: TLV Result - Success " + "- EAP-TLV/Phase2 Completed"); + resp_status = EAP_TLV_RESULT_SUCCESS; + ret->decision = DECISION_UNCOND_SUCC; + } else if (status == EAP_TLV_RESULT_FAILURE) { + wpa_printf(MSG_INFO, "EAP-TLV: TLV Result - Failure"); + resp_status = EAP_TLV_RESULT_FAILURE; + ret->decision = DECISION_FAIL; + } else { + wpa_printf(MSG_INFO, "EAP-TLV: Unknown TLV Result " + "Status %d", status); + resp_status = EAP_TLV_RESULT_FAILURE; + ret->decision = DECISION_FAIL; + } + ret->methodState = METHOD_DONE; + + *resp = eap_tlv_build_result(hdr->identifier, resp_status, + resp_len); + } + + return 0; +} diff --git a/contrib/wpa_supplicant/eap_tlv.h b/contrib/wpa_supplicant/eap_tlv.h new file mode 100644 index 0000000..4391552 --- /dev/null +++ b/contrib/wpa_supplicant/eap_tlv.h @@ -0,0 +1,73 @@ +#ifndef EAP_TLV_H +#define EAP_TLV_H + +/* EAP-TLV TLVs (draft-josefsson-ppext-eap-tls-eap-07.txt) */ +#define EAP_TLV_RESULT_TLV 3 /* Acknowledged Result */ +#define EAP_TLV_NAK_TLV 4 +#define EAP_TLV_CRYPTO_BINDING_TLV 5 +#define EAP_TLV_CONNECTION_BINDING_TLV 6 +#define EAP_TLV_VENDOR_SPECIFIC_TLV 7 +#define EAP_TLV_URI_TLV 8 +#define EAP_TLV_EAP_PAYLOAD_TLV 9 +#define EAP_TLV_INTERMEDIATE_RESULT_TLV 10 +#define EAP_TLV_PAC_TLV 11 /* draft-cam-winget-eap-fast-01.txt */ +#define EAP_TLV_CRYPTO_BINDING_TLV_ 12 /* draft-cam-winget-eap-fast-01.txt */ + +#define EAP_TLV_RESULT_SUCCESS 1 +#define EAP_TLV_RESULT_FAILURE 2 + +#define EAP_TLV_TYPE_MANDATORY 0x8000 + +struct eap_tlv_hdr { + u16 tlv_type; + u16 length; +}; + +struct eap_tlv_nak_tlv { + u16 tlv_type; + u16 length; + u32 vendor_id; + u16 nak_type; +} __attribute__((packed)); + +struct eap_tlv_result_tlv { + u16 tlv_type; + u16 length; + u16 status; +} __attribute__((packed)); + +struct eap_tlv_intermediate_result_tlv { + u16 tlv_type; + u16 length; + u16 status; +} __attribute__((packed)); + +struct eap_tlv_crypto_binding__tlv { + u16 tlv_type; + u16 length; + u8 reserved; + u8 version; + u8 received_version; + u8 subtype; + u8 nonce[32]; + u8 compound_mac[20]; +} __attribute__((packed)); + +struct eap_tlv_pac_ack_tlv { + u16 tlv_type; + u16 length; + u16 pac_type; + u16 pac_len; + u16 result; +} __attribute__((packed)); + +#define EAP_TLV_CRYPTO_BINDING_SUBTYPE_REQUEST 0 +#define EAP_TLV_CRYPTO_BINDING_SUBTYPE_RESPONSE 1 + + +u8 * eap_tlv_build_nak(int id, int nak_type, size_t *resp_len); +u8 * eap_tlv_build_result(int id, int status, size_t *resp_len); +int eap_tlv_process(struct eap_sm *sm, struct eap_method_ret *ret, + struct eap_hdr *hdr, u8 **resp, size_t *resp_len); + +#endif /* EAP_TLV_H */ diff --git a/contrib/wpa_supplicant/eap_ttls.c b/contrib/wpa_supplicant/eap_ttls.c new file mode 100644 index 0000000..b639fcd --- /dev/null +++ b/contrib/wpa_supplicant/eap_ttls.c @@ -0,0 +1,1384 @@ +/* + * WPA Supplicant / EAP-TTLS (draft-ietf-pppext-eap-ttls-03.txt) + * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include "common.h" +#include "eap_i.h" +#include "eap_tls_common.h" +#include "wpa_supplicant.h" +#include "config_ssid.h" +#include "ms_funcs.h" +#include "md5.h" +#include "tls.h" +#include "eap_ttls.h" + + +static void eap_ttls_deinit(struct eap_sm *sm, void *priv); + + +struct eap_ttls_data { + struct eap_ssl_data ssl; + + const struct eap_method *phase2_method; + void *phase2_priv; + int phase2_success; + int phase2_start; + + enum { + EAP_TTLS_PHASE2_EAP, + EAP_TTLS_PHASE2_MSCHAPV2, + EAP_TTLS_PHASE2_MSCHAP, + EAP_TTLS_PHASE2_PAP, + EAP_TTLS_PHASE2_CHAP + } phase2_type; + u8 phase2_eap_type; + u8 *phase2_eap_types; + size_t num_phase2_eap_types; + + u8 auth_response[20]; + int auth_response_valid; + u8 ident; + int resuming; /* starting a resumed session */ + int reauth; /* reauthentication */ + u8 *key_data; + + u8 *pending_phase2_req; + size_t pending_phase2_req_len; +}; + + +static void * eap_ttls_init(struct eap_sm *sm) +{ + struct eap_ttls_data *data; + struct wpa_ssid *config = eap_get_config(sm); + char *selected; + + data = malloc(sizeof(*data)); + if (data == NULL) + return NULL; + memset(data, 0, sizeof(*data)); + selected = "EAP"; + data->phase2_type = EAP_TTLS_PHASE2_EAP; + if (config && config->phase2) { + if (strstr(config->phase2, "autheap=")) { + selected = "EAP"; + data->phase2_type = EAP_TTLS_PHASE2_EAP; + } else if (strstr(config->phase2, "auth=MSCHAPV2")) { + selected = "MSCHAPV2"; + data->phase2_type = EAP_TTLS_PHASE2_MSCHAPV2; + } else if (strstr(config->phase2, "auth=MSCHAP")) { + selected = "MSCHAP"; + data->phase2_type = EAP_TTLS_PHASE2_MSCHAP; + } else if (strstr(config->phase2, "auth=PAP")) { + selected = "PAP"; + data->phase2_type = EAP_TTLS_PHASE2_PAP; + } else if (strstr(config->phase2, "auth=CHAP")) { + selected = "CHAP"; + data->phase2_type = EAP_TTLS_PHASE2_CHAP; + } + } + wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase2 type: %s", selected); + + if (data->phase2_type == EAP_TTLS_PHASE2_EAP) { + if (config && config->phase2) { + char *start, *pos, *buf; + u8 method, *methods = NULL, *_methods; + size_t num_methods = 0; + start = buf = strdup(config->phase2); + if (buf == NULL) { + eap_ttls_deinit(sm, data); + return NULL; + } + while (start && *start != '\0') { + pos = strstr(start, "autheap="); + if (pos == NULL) + break; + if (start != pos && *(pos - 1) != ' ') { + start = pos + 8; + continue; + } + + start = pos + 8; + pos = strchr(start, ' '); + if (pos) + *pos++ = '\0'; + method = eap_get_phase2_type(start); + if (method == EAP_TYPE_NONE) { + wpa_printf(MSG_ERROR, "EAP-TTLS: " + "Unsupported Phase2 EAP " + "method '%s'", start); + } else { + num_methods++; + _methods = realloc(methods, + num_methods); + if (_methods == NULL) { + free(methods); + eap_ttls_deinit(sm, data); + return NULL; + } + methods = _methods; + methods[num_methods - 1] = method; + } + + start = pos; + } + free(buf); + data->phase2_eap_types = methods; + data->num_phase2_eap_types = num_methods; + } + if (data->phase2_eap_types == NULL) { + data->phase2_eap_types = eap_get_phase2_types( + config, &data->num_phase2_eap_types); + } + if (data->phase2_eap_types == NULL) { + wpa_printf(MSG_ERROR, "EAP-TTLS: No Phase2 EAP method " + "available"); + eap_ttls_deinit(sm, data); + return NULL; + } + wpa_hexdump(MSG_DEBUG, "EAP-TTLS: Phase2 EAP types", + data->phase2_eap_types, + data->num_phase2_eap_types); + data->phase2_eap_type = EAP_TYPE_NONE; + } + + + if (eap_tls_ssl_init(sm, &data->ssl, config)) { + wpa_printf(MSG_INFO, "EAP-TTLS: Failed to initialize SSL."); + eap_ttls_deinit(sm, data); + return NULL; + } + + return data; +} + + +static void eap_ttls_deinit(struct eap_sm *sm, void *priv) +{ + struct eap_ttls_data *data = priv; + if (data == NULL) + return; + if (data->phase2_priv && data->phase2_method) + data->phase2_method->deinit(sm, data->phase2_priv); + free(data->phase2_eap_types); + eap_tls_ssl_deinit(sm, &data->ssl); + free(data->key_data); + free(data->pending_phase2_req); + free(data); +} + + +static int eap_ttls_encrypt(struct eap_sm *sm, struct eap_ttls_data *data, + int id, u8 *plain, size_t plain_len, + u8 **out_data, size_t *out_len) +{ + int res; + u8 *pos; + struct eap_hdr *resp; + + /* TODO: add support for fragmentation, if needed. This will need to + * add TLS Message Length field, if the frame is fragmented. */ + resp = malloc(sizeof(struct eap_hdr) + 2 + data->ssl.tls_out_limit); + if (resp == NULL) + return 0; + + resp->code = EAP_CODE_RESPONSE; + resp->identifier = id; + + pos = (u8 *) (resp + 1); + *pos++ = EAP_TYPE_TTLS; + *pos++ = 0; + + res = tls_connection_encrypt(sm->ssl_ctx, data->ssl.conn, + plain, plain_len, + pos, data->ssl.tls_out_limit); + if (res < 0) { + wpa_printf(MSG_INFO, "EAP-TTLS: Failed to encrypt Phase 2 " + "data"); + free(resp); + return 0; + } + + *out_len = sizeof(struct eap_hdr) + 2 + res; + resp->length = host_to_be16(*out_len); + *out_data = (u8 *) resp; + return 0; +} + + +static u8 * eap_ttls_avp_hdr(u8 *avphdr, u32 avp_code, u32 vendor_id, + int mandatory, size_t len) +{ + struct ttls_avp_vendor *avp; + u8 flags; + size_t hdrlen; + + avp = (struct ttls_avp_vendor *) avphdr; + flags = mandatory ? AVP_FLAGS_MANDATORY : 0; + if (vendor_id) { + flags |= AVP_FLAGS_VENDOR; + hdrlen = sizeof(*avp); + avp->vendor_id = host_to_be32(vendor_id); + } else { + hdrlen = sizeof(struct ttls_avp); + } + + avp->avp_code = host_to_be32(avp_code); + avp->avp_length = host_to_be32((flags << 24) | (hdrlen + len)); + + return avphdr + hdrlen; +} + + +static u8 * eap_ttls_avp_add(u8 *start, u8 *avphdr, u32 avp_code, + u32 vendor_id, int mandatory, + u8 *data, size_t len) +{ + u8 *pos; + pos = eap_ttls_avp_hdr(avphdr, avp_code, vendor_id, mandatory, len); + memcpy(pos, data, len); + pos += len; + AVP_PAD(start, pos); + return pos; +} + + +static int eap_ttls_avp_encapsulate(u8 **resp, size_t *resp_len, u32 avp_code, + int mandatory) +{ + u8 *avp, *pos; + + avp = malloc(sizeof(struct ttls_avp) + *resp_len + 4); + if (avp == NULL) { + free(*resp); + *resp_len = 0; + return -1; + } + + pos = eap_ttls_avp_hdr(avp, avp_code, 0, mandatory, *resp_len); + memcpy(pos, *resp, *resp_len); + pos += *resp_len; + AVP_PAD(avp, pos); + free(*resp); + *resp = avp; + *resp_len = pos - avp; + return 0; +} + + +static int eap_ttls_phase2_nak(struct eap_sm *sm, + struct eap_ttls_data *data, + struct eap_hdr *hdr, + u8 **resp, size_t *resp_len) +{ + struct eap_hdr *resp_hdr; + u8 *pos = (u8 *) (hdr + 1); + + wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase 2 Request: Nak type=%d", *pos); + wpa_hexdump(MSG_DEBUG, "EAP-TTLS: Allowed Phase2 EAP types", + data->phase2_eap_types, data->num_phase2_eap_types); + *resp_len = sizeof(struct eap_hdr) + 1 + data->num_phase2_eap_types; + *resp = malloc(*resp_len); + if (*resp == NULL) + return -1; + + resp_hdr = (struct eap_hdr *) (*resp); + resp_hdr->code = EAP_CODE_RESPONSE; + resp_hdr->identifier = hdr->identifier; + resp_hdr->length = host_to_be16(*resp_len); + pos = (u8 *) (resp_hdr + 1); + *pos++ = EAP_TYPE_NAK; + memcpy(pos, data->phase2_eap_types, data->num_phase2_eap_types); + + return 0; +} + + +static int eap_ttls_phase2_request_eap(struct eap_sm *sm, + struct eap_ttls_data *data, + struct eap_method_ret *ret, + struct eap_hdr *req, + struct eap_hdr *hdr, + u8 **resp, size_t *resp_len) +{ + size_t len = be_to_host16(hdr->length); + u8 *pos; + struct eap_method_ret iret; + struct wpa_ssid *config = eap_get_config(sm); + + if (len <= sizeof(struct eap_hdr)) { + wpa_printf(MSG_INFO, "EAP-TTLS: too short " + "Phase 2 request (len=%lu)", (unsigned long) len); + return -1; + } + pos = (u8 *) (hdr + 1); + wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase 2 EAP Request: type=%d", *pos); + switch (*pos) { + case EAP_TYPE_IDENTITY: + *resp = eap_sm_buildIdentity(sm, req->identifier, resp_len, 1); + break; + default: + if (data->phase2_eap_type == EAP_TYPE_NONE) { + int i; + for (i = 0; i < data->num_phase2_eap_types; i++) { + if (data->phase2_eap_types[i] != *pos) + continue; + + data->phase2_eap_type = *pos; + wpa_printf(MSG_DEBUG, "EAP-TTLS: Selected " + "Phase 2 EAP method %d", + data->phase2_eap_type); + break; + } + } + if (*pos != data->phase2_eap_type || *pos == EAP_TYPE_NONE) { + if (eap_ttls_phase2_nak(sm, data, hdr, resp, resp_len)) + return -1; + break; + } + + if (data->phase2_priv == NULL) { + data->phase2_method = eap_sm_get_eap_methods(*pos); + if (data->phase2_method) { + sm->init_phase2 = 1; + data->phase2_priv = + data->phase2_method->init(sm); + sm->init_phase2 = 0; + } + } + if (data->phase2_priv == NULL || data->phase2_method == NULL) { + wpa_printf(MSG_INFO, "EAP-TTLS: failed to initialize " + "Phase 2 EAP method %d", *pos); + return -1; + } + memset(&iret, 0, sizeof(iret)); + *resp = data->phase2_method->process(sm, data->phase2_priv, + &iret, (u8 *) hdr, len, + resp_len); + if ((iret.methodState == METHOD_DONE || + iret.methodState == METHOD_MAY_CONT) && + (iret.decision == DECISION_UNCOND_SUCC || + iret.decision == DECISION_COND_SUCC || + iret.decision == DECISION_FAIL)) { + ret->methodState = iret.methodState; + ret->decision = iret.decision; + } + break; + } + + if (*resp == NULL && + (config->pending_req_identity || config->pending_req_password || + config->pending_req_otp)) { + return 0; + } + + if (*resp == NULL) + return -1; + + wpa_hexdump(MSG_DEBUG, "EAP-TTLS: AVP encapsulate EAP Response", + *resp, *resp_len); + return eap_ttls_avp_encapsulate(resp, resp_len, + RADIUS_ATTR_EAP_MESSAGE, 1); +} + + +static int eap_ttls_phase2_request_mschapv2(struct eap_sm *sm, + struct eap_ttls_data *data, + struct eap_method_ret *ret, + struct eap_hdr *req, + struct eap_hdr *hdr, + u8 **resp, size_t *resp_len) +{ + struct wpa_ssid *config = eap_get_config(sm); + u8 *buf, *pos, *challenge, *username, *peer_challenge; + size_t username_len; + int i; + + wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase 2 MSCHAPV2 Request"); + + /* MSCHAPv2 does not include optional domain name in the + * challenge-response calculation, so remove domain prefix + * (if present). */ + username = config->identity; + username_len = config->identity_len; + pos = username; + for (i = 0; i < username_len; i++) { + if (username[i] == '\\') { + username_len -= i + 1; + username += i + 1; + break; + } + } + + pos = buf = malloc(config->identity_len + 1000); + if (buf == NULL) { + wpa_printf(MSG_ERROR, + "EAP-TTLS/MSCHAPV2: Failed to allocate memory"); + return -1; + } + + /* User-Name */ + pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_USER_NAME, 0, 1, + config->identity, config->identity_len); + + /* MS-CHAP-Challenge */ + challenge = eap_tls_derive_key(sm, &data->ssl, "ttls challenge", + EAP_TTLS_MSCHAPV2_CHALLENGE_LEN * 2 + + 1); + if (challenge == NULL) { + free(buf); + wpa_printf(MSG_ERROR, "EAP-TTLS/MSCHAPV2: Failed to derive " + "implicit challenge"); + return -1; + } + peer_challenge = challenge + 1 + EAP_TTLS_MSCHAPV2_CHALLENGE_LEN; + + pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_MS_CHAP_CHALLENGE, + RADIUS_VENDOR_ID_MICROSOFT, 1, + challenge, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN); + + /* MS-CHAP2-Response */ + pos = eap_ttls_avp_hdr(pos, RADIUS_ATTR_MS_CHAP2_RESPONSE, + RADIUS_VENDOR_ID_MICROSOFT, 1, + EAP_TTLS_MSCHAPV2_RESPONSE_LEN); + data->ident = challenge[EAP_TTLS_MSCHAPV2_CHALLENGE_LEN]; + *pos++ = data->ident; + *pos++ = 0; /* Flags */ + memcpy(pos, peer_challenge, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN); + pos += EAP_TTLS_MSCHAPV2_CHALLENGE_LEN; + memset(pos, 0, 8); /* Reserved, must be zero */ + pos += 8; + wpa_hexdump(MSG_DEBUG, "EAP-TTLS: MSCHAPV2: implicit auth_challenge", + challenge, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN); + wpa_hexdump(MSG_DEBUG, "EAP-TTLS: MSCHAPV2: peer_challenge", + peer_challenge, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN); + wpa_hexdump_ascii(MSG_DEBUG, "EAP-TTLS: MSCHAPV2 username", + username, username_len); + wpa_hexdump_ascii_key(MSG_DEBUG, "EAP-TTLS: MSCHAPV2 password", + config->password, config->password_len); + generate_nt_response(challenge, peer_challenge, + username, username_len, + config->password, config->password_len, + pos); + wpa_hexdump(MSG_DEBUG, "EAP-TTLS: MSCHAPV2 response", pos, 24); + generate_authenticator_response(config->password, config->password_len, + peer_challenge, challenge, + username, username_len, + pos, data->auth_response); + data->auth_response_valid = 1; + + pos += 24; + free(challenge); + AVP_PAD(buf, pos); + + *resp = buf; + *resp_len = pos - buf; + + if (sm->workaround) { + /* At least FreeRADIUS seems to be terminating + * EAP-TTLS/MSHCAPV2 without the expected MS-CHAP-v2 Success + * packet. */ + wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: EAP workaround - " + "allow success without tunneled response"); + ret->methodState = METHOD_MAY_CONT; + ret->decision = DECISION_COND_SUCC; + } + + return 0; +} + + +static int eap_ttls_phase2_request_mschap(struct eap_sm *sm, + struct eap_ttls_data *data, + struct eap_method_ret *ret, + struct eap_hdr *req, + struct eap_hdr *hdr, + u8 **resp, size_t *resp_len) +{ + struct wpa_ssid *config = eap_get_config(sm); + u8 *buf, *pos, *challenge; + + wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase 2 MSCHAP Request"); + + pos = buf = malloc(config->identity_len + 1000); + if (buf == NULL) { + wpa_printf(MSG_ERROR, + "EAP-TTLS/MSCHAP: Failed to allocate memory"); + return -1; + } + + /* User-Name */ + pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_USER_NAME, 0, 1, + config->identity, config->identity_len); + + /* MS-CHAP-Challenge */ + challenge = eap_tls_derive_key(sm, &data->ssl, "ttls challenge", + EAP_TLS_KEY_LEN); + if (challenge == NULL) { + free(buf); + wpa_printf(MSG_ERROR, "EAP-TTLS/MSCHAP: Failed to derive " + "implicit challenge"); + return -1; + } + + pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_MS_CHAP_CHALLENGE, + RADIUS_VENDOR_ID_MICROSOFT, 1, + challenge, EAP_TTLS_MSCHAP_CHALLENGE_LEN); + + /* MS-CHAP-Response */ + pos = eap_ttls_avp_hdr(pos, RADIUS_ATTR_MS_CHAP_RESPONSE, + RADIUS_VENDOR_ID_MICROSOFT, 1, + EAP_TTLS_MSCHAP_RESPONSE_LEN); + data->ident = challenge[EAP_TTLS_MSCHAP_CHALLENGE_LEN]; + *pos++ = data->ident; + *pos++ = 1; /* Flags: Use NT style passwords */ + memset(pos, 0, 24); /* LM-Response */ + pos += 24; + nt_challenge_response(challenge, + config->password, config->password_len, + pos); /* NT-Response */ + wpa_hexdump_ascii_key(MSG_DEBUG, "EAP-TTLS: MSCHAP password", + config->password, config->password_len); + wpa_hexdump(MSG_DEBUG, "EAP-TTLS: MSCHAP implicit challenge", + challenge, EAP_TTLS_MSCHAP_CHALLENGE_LEN); + wpa_hexdump(MSG_DEBUG, "EAP-TTLS: MSCHAP response", pos, 24); + pos += 24; + free(challenge); + AVP_PAD(buf, pos); + + *resp = buf; + *resp_len = pos - buf; + + /* EAP-TTLS/MSCHAP does not provide tunneled success notification, so + * assume that Phase2 succeeds. */ + ret->methodState = METHOD_DONE; + ret->decision = DECISION_COND_SUCC; + + return 0; +} + + +static int eap_ttls_phase2_request_pap(struct eap_sm *sm, + struct eap_ttls_data *data, + struct eap_method_ret *ret, + struct eap_hdr *req, + struct eap_hdr *hdr, + u8 **resp, size_t *resp_len) +{ + struct wpa_ssid *config = eap_get_config(sm); + u8 *buf, *pos; + size_t pad; + + wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase 2 PAP Request"); + + pos = buf = malloc(config->identity_len + config->password_len + 100); + if (buf == NULL) { + wpa_printf(MSG_ERROR, + "EAP-TTLS/PAP: Failed to allocate memory"); + return -1; + } + + /* User-Name */ + pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_USER_NAME, 0, 1, + config->identity, config->identity_len); + + /* User-Password; in RADIUS, this is encrypted, but EAP-TTLS encrypts + * the data, so no separate encryption is used in the AVP itself. + * However, the password is padded to obfuscate its length. */ + pad = (16 - (config->password_len & 15)) & 15; + pos = eap_ttls_avp_hdr(pos, RADIUS_ATTR_USER_PASSWORD, 0, 1, + config->password_len + pad); + memcpy(pos, config->password, config->password_len); + pos += config->password_len; + memset(pos, 0, pad); + pos += pad; + AVP_PAD(buf, pos); + + *resp = buf; + *resp_len = pos - buf; + + /* EAP-TTLS/PAP does not provide tunneled success notification, so + * assume that Phase2 succeeds. */ + ret->methodState = METHOD_DONE; + ret->decision = DECISION_COND_SUCC; + + return 0; +} + + +static int eap_ttls_phase2_request_chap(struct eap_sm *sm, + struct eap_ttls_data *data, + struct eap_method_ret *ret, + struct eap_hdr *req, + struct eap_hdr *hdr, + u8 **resp, size_t *resp_len) +{ + struct wpa_ssid *config = eap_get_config(sm); + u8 *buf, *pos, *challenge; + MD5_CTX context; + + wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase 2 CHAP Request"); + + pos = buf = malloc(config->identity_len + 1000); + if (buf == NULL) { + wpa_printf(MSG_ERROR, + "EAP-TTLS/CHAP: Failed to allocate memory"); + return -1; + } + + /* User-Name */ + pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_USER_NAME, 0, 1, + config->identity, config->identity_len); + + /* CHAP-Challenge */ + challenge = eap_tls_derive_key(sm, &data->ssl, "ttls challenge", + EAP_TLS_KEY_LEN); + if (challenge == NULL) { + free(buf); + wpa_printf(MSG_ERROR, "EAP-TTLS/CHAP: Failed to derive " + "implicit challenge"); + return -1; + } + + pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_CHAP_CHALLENGE, 0, 1, + challenge, EAP_TTLS_CHAP_CHALLENGE_LEN); + + /* CHAP-Password */ + pos = eap_ttls_avp_hdr(pos, RADIUS_ATTR_CHAP_PASSWORD, 0, 1, + 1 + EAP_TTLS_CHAP_PASSWORD_LEN); + data->ident = challenge[EAP_TTLS_CHAP_CHALLENGE_LEN]; + *pos++ = data->ident; + + /* MD5(Ident + Password + Challenge) */ + MD5Init(&context); + MD5Update(&context, &data->ident, 1); + MD5Update(&context, config->password, config->password_len); + MD5Update(&context, challenge, EAP_TTLS_CHAP_CHALLENGE_LEN); + MD5Final(pos, &context); + + wpa_hexdump_ascii(MSG_DEBUG, "EAP-TTLS: CHAP username", + config->identity, config->identity_len); + wpa_hexdump_ascii_key(MSG_DEBUG, "EAP-TTLS: CHAP password", + config->password, config->password_len); + wpa_hexdump(MSG_DEBUG, "EAP-TTLS: CHAP implicit challenge", + challenge, EAP_TTLS_CHAP_CHALLENGE_LEN); + wpa_hexdump(MSG_DEBUG, "EAP-TTLS: CHAP password", + pos, EAP_TTLS_CHAP_PASSWORD_LEN); + pos += EAP_TTLS_CHAP_PASSWORD_LEN; + free(challenge); + AVP_PAD(buf, pos); + + *resp = buf; + *resp_len = pos - buf; + + /* EAP-TTLS/CHAP does not provide tunneled success notification, so + * assume that Phase2 succeeds. */ + ret->methodState = METHOD_DONE; + ret->decision = DECISION_COND_SUCC; + + return 0; +} + + +static int eap_ttls_phase2_request(struct eap_sm *sm, + struct eap_ttls_data *data, + struct eap_method_ret *ret, + struct eap_hdr *req, + struct eap_hdr *hdr, + u8 **resp, size_t *resp_len) +{ + struct wpa_ssid *config = eap_get_config(sm); + int res = 0; + + if (data->phase2_type == EAP_TTLS_PHASE2_MSCHAPV2 || + data->phase2_type == EAP_TTLS_PHASE2_MSCHAP || + data->phase2_type == EAP_TTLS_PHASE2_PAP || + data->phase2_type == EAP_TTLS_PHASE2_CHAP) { + if (config == NULL || config->identity == NULL) { + wpa_printf(MSG_INFO, + "EAP-TTLS: Identity not configured"); + eap_sm_request_identity(sm, config); + if (config->password == NULL) + eap_sm_request_password(sm, config); + return 0; + } + + if (config->password == NULL) { + wpa_printf(MSG_INFO, + "EAP-TTLS: Password not configured"); + eap_sm_request_password(sm, config); + return 0; + } + } + + switch (data->phase2_type) { + case EAP_TTLS_PHASE2_EAP: + res = eap_ttls_phase2_request_eap(sm, data, ret, req, hdr, + resp, resp_len); + break; + case EAP_TTLS_PHASE2_MSCHAPV2: + res = eap_ttls_phase2_request_mschapv2(sm, data, ret, req, hdr, + resp, resp_len); + break; + case EAP_TTLS_PHASE2_MSCHAP: + res = eap_ttls_phase2_request_mschap(sm, data, ret, req, hdr, + resp, resp_len); + break; + case EAP_TTLS_PHASE2_PAP: + res = eap_ttls_phase2_request_pap(sm, data, ret, req, hdr, + resp, resp_len); + break; + case EAP_TTLS_PHASE2_CHAP: + res = eap_ttls_phase2_request_chap(sm, data, ret, req, hdr, + resp, resp_len); + break; + default: + wpa_printf(MSG_ERROR, "EAP-TTLS: Phase 2 - Unknown"); + res = -1; + break; + } + + if (res < 0) { + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + } + + return res; +} + + +static int eap_ttls_decrypt(struct eap_sm *sm, struct eap_ttls_data *data, + struct eap_method_ret *ret, struct eap_hdr *req, + u8 *in_data, size_t in_len, + u8 **out_data, size_t *out_len) +{ + u8 *in_decrypted = NULL, *pos; + int buf_len, len_decrypted = 0, len, left, retval = 0, res; + struct eap_hdr *hdr = NULL; + u8 *resp = NULL, *mschapv2 = NULL, *eapdata = NULL; + size_t resp_len, eap_len = 0; + struct ttls_avp *avp; + u8 recv_response[20]; + int mschapv2_error = 0; + struct wpa_ssid *config = eap_get_config(sm); + + wpa_printf(MSG_DEBUG, "EAP-TTLS: received %lu bytes encrypted data for" + " Phase 2", (unsigned long) in_len); + + if (data->pending_phase2_req) { + wpa_printf(MSG_DEBUG, "EAP-TTLS: Pending Phase 2 request - " + "skip decryption and use old data"); + in_decrypted = data->pending_phase2_req; + data->pending_phase2_req = NULL; + len_decrypted = data->pending_phase2_req_len; + if (data->pending_phase2_req_len == 0) { + free(in_decrypted); + in_decrypted = NULL; + goto fake_req_identity; + } + goto continue_req; + } + + if (in_len == 0 && data->phase2_start) { + data->phase2_start = 0; + /* EAP-TTLS does not use Phase2 on fast re-auth; this must be + * done only if TLS part was indeed resuming a previous + * session. Most Authentication Servers terminate EAP-TTLS + * before reaching this point, but some do not. Make + * wpa_supplicant stop phase 2 here, if needed. */ + if (data->reauth && + tls_connection_resumed(sm->ssl_ctx, data->ssl.conn)) { + wpa_printf(MSG_DEBUG, "EAP-TTLS: Session resumption - " + "skip phase 2"); + *out_data = eap_tls_build_ack(&data->ssl, out_len, + req->identifier, + EAP_TYPE_TTLS, 0); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_UNCOND_SUCC; + data->phase2_success = 1; + return 0; + } + fake_req_identity: + wpa_printf(MSG_DEBUG, "EAP-TTLS: empty data in beginning of " + "Phase 2 - use fake EAP-Request Identity"); + buf_len = sizeof(*hdr) + 1; + in_decrypted = malloc(buf_len); + if (in_decrypted == NULL) { + wpa_printf(MSG_WARNING, "EAP-TTLS: failed to allocate " + "memory for fake EAP-Identity Request"); + retval = -1; + goto done; + } + hdr = (struct eap_hdr *) in_decrypted; + hdr->code = EAP_CODE_REQUEST; + hdr->identifier = 0; + hdr->length = host_to_be16(sizeof(*hdr) + 1); + in_decrypted[sizeof(*hdr)] = EAP_TYPE_IDENTITY; + goto process_eap; + } + + res = eap_tls_data_reassemble(sm, &data->ssl, &in_data, &in_len); + if (res < 0 || res == 1) { + retval = res; + goto done; + } + + buf_len = in_len; + if (data->ssl.tls_in_total > buf_len) + buf_len = data->ssl.tls_in_total; + in_decrypted = malloc(buf_len); + if (in_decrypted == NULL) { + free(data->ssl.tls_in); + data->ssl.tls_in = NULL; + data->ssl.tls_in_len = 0; + wpa_printf(MSG_WARNING, "EAP-TTLS: failed to allocate memory " + "for decryption"); + retval = -1; + goto done; + } + + len_decrypted = tls_connection_decrypt(sm->ssl_ctx, data->ssl.conn, + in_data, in_len, + in_decrypted, buf_len); + free(data->ssl.tls_in); + data->ssl.tls_in = NULL; + data->ssl.tls_in_len = 0; + if (len_decrypted < 0) { + wpa_printf(MSG_INFO, "EAP-TTLS: Failed to decrypt Phase 2 " + "data"); + retval = -1; + goto done; + } + +continue_req: + data->phase2_start = 0; + + wpa_hexdump(MSG_DEBUG, "EAP-TTLS: Decrypted Phase 2 AVPs", + in_decrypted, len_decrypted); + if (len_decrypted < sizeof(struct ttls_avp)) { + wpa_printf(MSG_WARNING, "EAP-TTLS: Too short Phase 2 AVP frame" + " len=%d expected %lu or more - dropped", + len_decrypted, + (unsigned long) sizeof(struct ttls_avp)); + retval = -1; + goto done; + } + + /* Parse AVPs */ + pos = in_decrypted; + left = len_decrypted; + mschapv2 = NULL; + + while (left > 0) { + u32 avp_code, avp_length, vendor_id = 0; + u8 avp_flags, *dpos; + size_t pad, dlen; + avp = (struct ttls_avp *) pos; + avp_code = be_to_host32(avp->avp_code); + avp_length = be_to_host32(avp->avp_length); + avp_flags = (avp_length >> 24) & 0xff; + avp_length &= 0xffffff; + wpa_printf(MSG_DEBUG, "EAP-TTLS: AVP: code=%d flags=0x%02x " + "length=%d", (int) avp_code, avp_flags, + (int) avp_length); + if (avp_length > left) { + wpa_printf(MSG_WARNING, "EAP-TTLS: AVP overflow " + "(len=%d, left=%d) - dropped", + (int) avp_length, left); + retval = -1; + goto done; + } + dpos = (u8 *) (avp + 1); + dlen = avp_length - sizeof(*avp); + if (avp_flags & AVP_FLAGS_VENDOR) { + if (dlen < 4) { + wpa_printf(MSG_WARNING, "EAP-TTLS: vendor AVP " + "underflow"); + retval = -1; + goto done; + } + vendor_id = be_to_host32(* (u32 *) dpos); + wpa_printf(MSG_DEBUG, "EAP-TTLS: AVP vendor_id %d", + (int) vendor_id); + dpos += 4; + dlen -= 4; + } + + wpa_hexdump(MSG_DEBUG, "EAP-TTLS: AVP data", dpos, dlen); + + if (vendor_id == 0 && avp_code == RADIUS_ATTR_EAP_MESSAGE) { + wpa_printf(MSG_DEBUG, "EAP-TTLS: AVP - EAP Message"); + if (eapdata == NULL) { + eapdata = malloc(dlen); + if (eapdata == NULL) { + retval = -1; + wpa_printf(MSG_WARNING, "EAP-TTLS: " + "failed to allocate memory " + "for Phase 2 EAP data"); + goto done; + } + memcpy(eapdata, dpos, dlen); + eap_len = dlen; + } else { + u8 *neweap = realloc(eapdata, eap_len + dlen); + if (neweap == NULL) { + retval = -1; + wpa_printf(MSG_WARNING, "EAP-TTLS: " + "failed to allocate memory " + "for Phase 2 EAP data"); + goto done; + } + memcpy(neweap + eap_len, dpos, dlen); + eapdata = neweap; + eap_len += dlen; + } + } else if (vendor_id == 0 && + avp_code == RADIUS_ATTR_REPLY_MESSAGE) { + /* This is an optional message that can be displayed to + * the user. */ + wpa_hexdump_ascii(MSG_DEBUG, + "EAP-TTLS: AVP - Reply-Message", + dpos, dlen); + } else if (vendor_id == RADIUS_VENDOR_ID_MICROSOFT && + avp_code == RADIUS_ATTR_MS_CHAP2_SUCCESS) { + wpa_hexdump_ascii(MSG_DEBUG, "EAP-TTLS: " + "MS-CHAP2-Success", dpos, dlen); + if (dlen != 43) { + wpa_printf(MSG_WARNING, "EAP-TTLS: Unexpected " + "MS-CHAP2-Success length " + "(len=%lu, expected 43)", + (unsigned long) dlen); + retval = -1; + break; + } + mschapv2 = dpos; + } else if (vendor_id == RADIUS_VENDOR_ID_MICROSOFT && + avp_code == RADIUS_ATTR_MS_CHAP_ERROR) { + wpa_hexdump_ascii(MSG_DEBUG, "EAP-TTLS: " + "MS-CHAP-Error", dpos, dlen); + mschapv2_error = 1; + } else if (avp_flags & AVP_FLAGS_MANDATORY) { + wpa_printf(MSG_WARNING, "EAP-TTLS: Unsupported " + "mandatory AVP code %d vendor_id %d - " + "dropped", (int) avp_code, (int) vendor_id); + retval = -1; + goto done; + } else { + wpa_printf(MSG_DEBUG, "EAP-TTLS: Ignoring unsupported " + "AVP code %d vendor_id %d", + (int) avp_code, (int) vendor_id); + } + + pad = (4 - (avp_length & 3)) & 3; + pos += avp_length + pad; + left -= avp_length + pad; + } + + switch (data->phase2_type) { + case EAP_TTLS_PHASE2_EAP: + if (eapdata == NULL) { + wpa_printf(MSG_WARNING, "EAP-TTLS: No EAP Message in " + "the packet - dropped"); + retval = -1; + goto done; + } + + wpa_hexdump(MSG_DEBUG, "EAP-TTLS: Phase 2 EAP", + eapdata, eap_len); + hdr = (struct eap_hdr *) eapdata; + + if (eap_len < sizeof(*hdr)) { + wpa_printf(MSG_WARNING, "EAP-TTLS: Too short Phase 2 " + "EAP frame (len=%lu, expected %lu or more) " + "- dropped", (unsigned long) eap_len, + (unsigned long) sizeof(*hdr)); + retval = -1; + goto done; + } + len = be_to_host16(hdr->length); + if (len > eap_len) { + wpa_printf(MSG_INFO, "EAP-TTLS: Length mismatch in " + "Phase 2 EAP frame (EAP hdr len=%d, EAP " + "data len in AVP=%lu)", len, + (unsigned long) eap_len); + retval = -1; + goto done; + } + wpa_printf(MSG_DEBUG, "EAP-TTLS: received Phase 2: code=%d " + "identifier=%d length=%d", + hdr->code, hdr->identifier, len); + process_eap: + switch (hdr->code) { + case EAP_CODE_REQUEST: + if (eap_ttls_phase2_request(sm, data, ret, req, hdr, + &resp, &resp_len)) { + wpa_printf(MSG_INFO, "EAP-TTLS: Phase2 " + "Request processing failed"); + retval = -1; + goto done; + } + break; + default: + wpa_printf(MSG_INFO, "EAP-TTLS: Unexpected code=%d in " + "Phase 2 EAP header", hdr->code); + retval = -1; + break; + } + break; + case EAP_TTLS_PHASE2_MSCHAPV2: + if (mschapv2_error) { + wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Received " + "MS-CHAP-Error - failed"); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + *out_data = eap_tls_build_ack(&data->ssl, out_len, + req->identifier, + EAP_TYPE_TTLS, 0); + break; + } + + if (mschapv2 == NULL) { + wpa_printf(MSG_WARNING, "EAP-TTLS: no MS-CHAP2-Success" + " AVP received for Phase2 MSCHAPV2"); + retval = -1; + break; + } + if (mschapv2[0] != data->ident) { + wpa_printf(MSG_WARNING, "EAP-TTLS: Ident mismatch " + "for Phase 2 MSCHAPV2 (received Ident " + "0x%02x, expected 0x%02x)", + mschapv2[0], data->ident); + retval = -1; + break; + } + if (!data->auth_response_valid || + mschapv2[1] != 'S' || mschapv2[2] != '=' || + hexstr2bin((char *) (mschapv2 + 3), recv_response, 20) || + memcmp(data->auth_response, recv_response, 20) != 0) { + wpa_printf(MSG_WARNING, "EAP-TTLS: Invalid " + "authenticator response in Phase 2 " + "MSCHAPV2 success request"); + retval = -1; + break; + } + + wpa_printf(MSG_INFO, "EAP-TTLS: Phase 2 MSCHAPV2 " + "authentication succeeded"); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_UNCOND_SUCC; + data->phase2_success = 1; + + /* Reply with empty data; authentication server will reply + * with EAP-Success after this. */ + retval = 1; + goto done; + case EAP_TTLS_PHASE2_MSCHAP: + case EAP_TTLS_PHASE2_PAP: + case EAP_TTLS_PHASE2_CHAP: + /* EAP-TTLS/{MSCHAP,PAP,CHAP} should not send any TLS tunneled + * requests to the supplicant */ + wpa_printf(MSG_INFO, "EAP-TTLS: Phase 2 received unexpected " + "tunneled data"); + retval = -1; + break; + } + + if (resp) { + wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: Encrypting Phase 2 data", + resp, resp_len); + + if (eap_ttls_encrypt(sm, data, req->identifier, + resp, resp_len, out_data, out_len)) { + wpa_printf(MSG_INFO, "EAP-TTLS: Failed to encrypt " + "a Phase 2 frame"); + } + free(resp); + } else if (config->pending_req_identity || + config->pending_req_password || + config->pending_req_otp) { + free(data->pending_phase2_req); + data->pending_phase2_req = malloc(len_decrypted); + if (data->pending_phase2_req) { + memcpy(data->pending_phase2_req, in_decrypted, + len_decrypted); + data->pending_phase2_req_len = len_decrypted; + } + } + +done: + free(in_decrypted); + free(eapdata); + + if (retval < 0) { + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + } + + return retval; +} + + +static u8 * eap_ttls_process(struct eap_sm *sm, void *priv, + struct eap_method_ret *ret, + u8 *reqData, size_t reqDataLen, + size_t *respDataLen) +{ + struct eap_hdr *req; + int left, res; + unsigned int tls_msg_len; + u8 flags, *pos, *resp, id; + struct eap_ttls_data *data = priv; + + if (tls_get_errors(sm->ssl_ctx)) { + wpa_printf(MSG_INFO, "EAP-TTLS: TLS errors detected"); + ret->ignore = TRUE; + return NULL; + } + + req = (struct eap_hdr *) reqData; + pos = (u8 *) (req + 1); + if (reqDataLen < sizeof(*req) + 2 || *pos != EAP_TYPE_TTLS || + (left = be_to_host16(req->length)) > reqDataLen) { + wpa_printf(MSG_INFO, "EAP-TTLS: Invalid frame"); + ret->ignore = TRUE; + return NULL; + } + left -= sizeof(struct eap_hdr); + id = req->identifier; + pos++; + flags = *pos++; + left -= 2; + wpa_printf(MSG_DEBUG, "EAP-TTLS: Received packet(len=%lu) - " + "Flags 0x%02x", (unsigned long) reqDataLen, flags); + if (flags & EAP_TLS_FLAGS_LENGTH_INCLUDED) { + if (left < 4) { + wpa_printf(MSG_INFO, "EAP-TTLS: Short frame with TLS " + "length"); + ret->ignore = TRUE; + return NULL; + } + tls_msg_len = (pos[0] << 24) | (pos[1] << 16) | (pos[2] << 8) | + pos[3]; + wpa_printf(MSG_DEBUG, "EAP-TTLS: TLS Message Length: %d", + tls_msg_len); + if (data->ssl.tls_in_left == 0) { + data->ssl.tls_in_total = tls_msg_len; + data->ssl.tls_in_left = tls_msg_len; + free(data->ssl.tls_in); + data->ssl.tls_in = NULL; + data->ssl.tls_in_len = 0; + } + pos += 4; + left -= 4; + } + + ret->ignore = FALSE; + ret->methodState = METHOD_CONT; + ret->decision = DECISION_FAIL; + ret->allowNotifications = TRUE; + + if (flags & EAP_TLS_FLAGS_START) { + wpa_printf(MSG_DEBUG, "EAP-TTLS: Start"); + /* draft-ietf-pppext-eap-ttls-03.txt, Ch. 8.1: + * EAP-TTLS Start packet may, in a future specification, be + * allowed to contain data. Client based on this draft version + * must ignore such data but must not reject the Start packet. + */ + left = 0; + } + + resp = NULL; + if (tls_connection_established(sm->ssl_ctx, data->ssl.conn) && + !data->resuming) { + res = eap_ttls_decrypt(sm, data, ret, req, pos, left, + &resp, respDataLen); + } else { + res = eap_tls_process_helper(sm, &data->ssl, EAP_TYPE_TTLS, 0, + id, pos, left, + &resp, respDataLen); + + if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) { + wpa_printf(MSG_DEBUG, + "EAP-TTLS: TLS done, proceed to Phase 2"); + if (data->resuming) { + wpa_printf(MSG_DEBUG, "EAP-TTLS: fast reauth -" + " may skip Phase 2"); + ret->decision = DECISION_COND_SUCC; + ret->methodState = METHOD_MAY_CONT; + } + data->phase2_start = 1; + free(data->key_data); + data->key_data = + eap_tls_derive_key(sm, &data->ssl, + "ttls keying material", + EAP_TLS_KEY_LEN); + if (data->key_data) { + wpa_hexdump_key(MSG_DEBUG, + "EAP-TTLS: Derived key", + data->key_data, + EAP_TLS_KEY_LEN); + } else { + wpa_printf(MSG_DEBUG, "EAP-TTLS: Failed to " + "derive key"); + } + + if (*respDataLen == 0) { + if (eap_ttls_decrypt(sm, data, ret, req, NULL, + 0, &resp, respDataLen)) { + wpa_printf(MSG_WARNING, "EAP-TTLS: " + "failed to process early " + "start for Phase 2"); + } + res = 0; + } + data->resuming = 0; + } + } + + if (ret->methodState == METHOD_DONE) { + ret->allowNotifications = FALSE; + if (ret->decision == DECISION_UNCOND_SUCC || + ret->decision == DECISION_COND_SUCC) { + wpa_printf(MSG_DEBUG, "EAP-TTLS: Authentication " + "completed successfully"); + data->phase2_success = 1; + } + } else if (sm->workaround && ret->methodState == METHOD_MAY_CONT && + (ret->decision == DECISION_UNCOND_SUCC || + ret->decision == DECISION_COND_SUCC)) { + wpa_printf(MSG_DEBUG, "EAP-TTLS: Authentication " + "completed successfully (EAP workaround)"); + data->phase2_success = 1; + } + + if (res == 1) { + return eap_tls_build_ack(&data->ssl, respDataLen, id, + EAP_TYPE_TTLS, 0); + } + return resp; +} + + +static Boolean eap_ttls_has_reauth_data(struct eap_sm *sm, void *priv) +{ + struct eap_ttls_data *data = priv; + return tls_connection_established(sm->ssl_ctx, data->ssl.conn) && + data->phase2_success; +} + + +static void eap_ttls_deinit_for_reauth(struct eap_sm *sm, void *priv) +{ + struct eap_ttls_data *data = priv; + free(data->pending_phase2_req); + data->pending_phase2_req = NULL; +} + + +static void * eap_ttls_init_for_reauth(struct eap_sm *sm, void *priv) +{ + struct eap_ttls_data *data = priv; + free(data->key_data); + data->key_data = NULL; + if (eap_tls_reauth_init(sm, &data->ssl)) { + free(data); + return NULL; + } + data->phase2_start = 0; + data->phase2_success = 0; + data->resuming = 1; + data->reauth = 1; + return priv; +} + + +static int eap_ttls_get_status(struct eap_sm *sm, void *priv, char *buf, + size_t buflen, int verbose) +{ + struct eap_ttls_data *data = priv; + int len; + + len = eap_tls_status(sm, &data->ssl, buf, buflen, verbose); + switch (data->phase2_type) { + case EAP_TTLS_PHASE2_EAP: + len += snprintf(buf + len, buflen - len, + "EAP-TTLS Phase2 method=EAP-%s\n", + data->phase2_method ? data->phase2_method->name + : "?"); + break; + case EAP_TTLS_PHASE2_MSCHAPV2: + len += snprintf(buf + len, buflen - len, + "EAP-TTLS Phase2 method=MSCHAPV2\n"); + break; + case EAP_TTLS_PHASE2_MSCHAP: + len += snprintf(buf + len, buflen - len, + "EAP-TTLS Phase2 method=MSCHAP\n"); + break; + case EAP_TTLS_PHASE2_PAP: + len += snprintf(buf + len, buflen - len, + "EAP-TTLS Phase2 method=PAP\n"); + break; + case EAP_TTLS_PHASE2_CHAP: + len += snprintf(buf + len, buflen - len, + "EAP-TTLS Phase2 method=CHAP\n"); + break; + } + + return len; +} + + +static Boolean eap_ttls_isKeyAvailable(struct eap_sm *sm, void *priv) +{ + struct eap_ttls_data *data = priv; + return data->key_data != NULL && data->phase2_success; +} + + +static u8 * eap_ttls_getKey(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_ttls_data *data = priv; + u8 *key; + + if (data->key_data == NULL || !data->phase2_success) + return NULL; + + key = malloc(EAP_TLS_KEY_LEN); + if (key == NULL) + return NULL; + + *len = EAP_TLS_KEY_LEN; + memcpy(key, data->key_data, EAP_TLS_KEY_LEN); + + return key; +} + + +const struct eap_method eap_method_ttls = +{ + .method = EAP_TYPE_TTLS, + .name = "TTLS", + .init = eap_ttls_init, + .deinit = eap_ttls_deinit, + .process = eap_ttls_process, + .isKeyAvailable = eap_ttls_isKeyAvailable, + .getKey = eap_ttls_getKey, + .get_status = eap_ttls_get_status, + .has_reauth_data = eap_ttls_has_reauth_data, + .deinit_for_reauth = eap_ttls_deinit_for_reauth, + .init_for_reauth = eap_ttls_init_for_reauth, +}; diff --git a/contrib/wpa_supplicant/eap_ttls.h b/contrib/wpa_supplicant/eap_ttls.h new file mode 100644 index 0000000..a187db4 --- /dev/null +++ b/contrib/wpa_supplicant/eap_ttls.h @@ -0,0 +1,57 @@ +#ifndef EAP_TTLS_H +#define EAP_TTLS_H + +struct ttls_avp { + u32 avp_code; + u32 avp_length; /* 8-bit flags, 24-bit length; + * length includes AVP header */ + /* optional 32-bit Vendor-ID */ + /* Data */ +}; + +struct ttls_avp_vendor { + u32 avp_code; + u32 avp_length; /* 8-bit flags, 24-bit length; + * length includes AVP header */ + u32 vendor_id; + /* Data */ +}; + +#define AVP_FLAGS_VENDOR 0x80 +#define AVP_FLAGS_MANDATORY 0x40 + +#define AVP_PAD(start, pos) \ +do { \ + int pad; \ + pad = (4 - (((pos) - (start)) & 3)) & 3; \ + memset((pos), 0, pad); \ + pos += pad; \ +} while(0) + + +/* RFC 2865 */ +#define RADIUS_ATTR_USER_NAME 1 +#define RADIUS_ATTR_USER_PASSWORD 2 +#define RADIUS_ATTR_CHAP_PASSWORD 3 +#define RADIUS_ATTR_REPLY_MESSAGE 18 +#define RADIUS_ATTR_CHAP_CHALLENGE 60 +#define RADIUS_ATTR_EAP_MESSAGE 79 + +/* RFC 2548 */ +#define RADIUS_VENDOR_ID_MICROSOFT 311 +#define RADIUS_ATTR_MS_CHAP_RESPONSE 1 +#define RADIUS_ATTR_MS_CHAP_ERROR 2 +#define RADIUS_ATTR_MS_CHAP_NT_ENC_PW 6 +#define RADIUS_ATTR_MS_CHAP_CHALLENGE 11 +#define RADIUS_ATTR_MS_CHAP2_RESPONSE 25 +#define RADIUS_ATTR_MS_CHAP2_SUCCESS 26 +#define RADIUS_ATTR_MS_CHAP2_CPW 27 + +#define EAP_TTLS_MSCHAPV2_CHALLENGE_LEN 16 +#define EAP_TTLS_MSCHAPV2_RESPONSE_LEN 50 +#define EAP_TTLS_MSCHAP_CHALLENGE_LEN 8 +#define EAP_TTLS_MSCHAP_RESPONSE_LEN 50 +#define EAP_TTLS_CHAP_CHALLENGE_LEN 16 +#define EAP_TTLS_CHAP_PASSWORD_LEN 16 + +#endif /* EAP_TTLS_H */ diff --git a/contrib/wpa_supplicant/eapol_sm.c b/contrib/wpa_supplicant/eapol_sm.c new file mode 100644 index 0000000..93f9a54 --- /dev/null +++ b/contrib/wpa_supplicant/eapol_sm.c @@ -0,0 +1,1421 @@ +/* + * WPA Supplicant / EAPOL state machines + * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include "common.h" +#include "eapol_sm.h" +#include "eap.h" +#include "eloop.h" +#include "wpa_supplicant.h" +#include "l2_packet.h" +#include "wpa.h" +#include "md5.h" +#include "rc4.h" + + +/* IEEE 802.1aa/D6.1 - Supplicant */ + +struct eapol_sm { + /* Timers */ + unsigned int authWhile; + unsigned int heldWhile; + unsigned int startWhen; + unsigned int idleWhile; /* for EAP state machine */ + + /* Global variables */ + Boolean eapFail; + Boolean eapolEap; + Boolean eapSuccess; + Boolean initialize; + Boolean keyDone; + Boolean keyRun; + PortControl portControl; + Boolean portEnabled; + PortStatus suppPortStatus; /* dot1xSuppControlledPortStatus */ + Boolean portValid; + Boolean suppAbort; + Boolean suppFail; + Boolean suppStart; + Boolean suppSuccess; + Boolean suppTimeout; + + /* Supplicant PAE state machine */ + enum { + SUPP_PAE_UNKNOWN = 0, + SUPP_PAE_DISCONNECTED = 1, + SUPP_PAE_LOGOFF = 2, + SUPP_PAE_CONNECTING = 3, + SUPP_PAE_AUTHENTICATING = 4, + SUPP_PAE_AUTHENTICATED = 5, + /* unused(6) */ + SUPP_PAE_HELD = 7, + SUPP_PAE_RESTART = 8, + SUPP_PAE_S_FORCE_AUTH = 9, + SUPP_PAE_S_FORCE_UNAUTH = 10 + } SUPP_PAE_state; /* dot1xSuppPaeState */ + /* Variables */ + Boolean userLogoff; + Boolean logoffSent; + unsigned int startCount; + Boolean eapRestart; + PortControl sPortMode; + /* Constants */ + unsigned int heldPeriod; /* dot1xSuppHeldPeriod */ + unsigned int startPeriod; /* dot1xSuppStartPeriod */ + unsigned int maxStart; /* dot1xSuppMaxStart */ + + /* Key Receive state machine */ + enum { + KEY_RX_UNKNOWN = 0, + KEY_RX_NO_KEY_RECEIVE, KEY_RX_KEY_RECEIVE + } KEY_RX_state; + /* Variables */ + Boolean rxKey; + + /* Supplicant Backend state machine */ + enum { + SUPP_BE_UNKNOWN = 0, + SUPP_BE_INITIALIZE = 1, + SUPP_BE_IDLE = 2, + SUPP_BE_REQUEST = 3, + SUPP_BE_RECEIVE = 4, + SUPP_BE_RESPONSE = 5, + SUPP_BE_FAIL = 6, + SUPP_BE_TIMEOUT = 7, + SUPP_BE_SUCCESS = 8 + } SUPP_BE_state; /* dot1xSuppBackendPaeState */ + /* Variables */ + Boolean eapNoResp; + Boolean eapReq; + Boolean eapResp; + /* Constants */ + unsigned int authPeriod; /* dot1xSuppAuthPeriod */ + + /* Statistics */ + unsigned int dot1xSuppEapolFramesRx; + unsigned int dot1xSuppEapolFramesTx; + unsigned int dot1xSuppEapolStartFramesTx; + unsigned int dot1xSuppEapolLogoffFramesTx; + unsigned int dot1xSuppEapolRespFramesTx; + unsigned int dot1xSuppEapolReqIdFramesRx; + unsigned int dot1xSuppEapolReqFramesRx; + unsigned int dot1xSuppInvalidEapolFramesRx; + unsigned int dot1xSuppEapLengthErrorFramesRx; + unsigned int dot1xSuppLastEapolFrameVersion; + unsigned char dot1xSuppLastEapolFrameSource[6]; + + /* Miscellaneous variables (not defined in IEEE 802.1aa/D6.1) */ + Boolean changed; + struct eap_sm *eap; + struct wpa_ssid *config; + Boolean initial_req; + u8 *last_rx_key; + size_t last_rx_key_len; + u8 *eapReqData; /* for EAP */ + size_t eapReqDataLen; /* for EAP */ + Boolean altAccept; /* for EAP */ + Boolean altReject; /* for EAP */ + Boolean replay_counter_valid; + u8 last_replay_counter[16]; + struct eapol_config conf; + struct eapol_ctx *ctx; + enum { EAPOL_CB_IN_PROGRESS = 0, EAPOL_CB_SUCCESS, EAPOL_CB_FAILURE } + cb_status; + Boolean cached_pmk; + + Boolean unicast_key_received, broadcast_key_received; +}; + + +static void eapol_sm_txLogoff(struct eapol_sm *sm); +static void eapol_sm_txStart(struct eapol_sm *sm); +static void eapol_sm_processKey(struct eapol_sm *sm); +static void eapol_sm_getSuppRsp(struct eapol_sm *sm); +static void eapol_sm_txSuppRsp(struct eapol_sm *sm); +static void eapol_sm_abortSupp(struct eapol_sm *sm); +static void eapol_sm_abort_cached(struct eapol_sm *sm); +static void eapol_sm_step_timeout(void *eloop_ctx, void *timeout_ctx); + +static struct eapol_callbacks eapol_cb; + + +/* Definitions for clarifying state machine implementation */ +#define SM_STATE(machine, state) \ +static void sm_ ## machine ## _ ## state ## _Enter(struct eapol_sm *sm, \ + int global) + +#define SM_ENTRY(machine, state) \ +if (!global || sm->machine ## _state != machine ## _ ## state) { \ + sm->changed = TRUE; \ + wpa_printf(MSG_DEBUG, "EAPOL: " #machine " entering state " #state); \ +} \ +sm->machine ## _state = machine ## _ ## state; + +#define SM_ENTER(machine, state) \ +sm_ ## machine ## _ ## state ## _Enter(sm, 0) +#define SM_ENTER_GLOBAL(machine, state) \ +sm_ ## machine ## _ ## state ## _Enter(sm, 1) + +#define SM_STEP(machine) \ +static void sm_ ## machine ## _Step(struct eapol_sm *sm) + +#define SM_STEP_RUN(machine) sm_ ## machine ## _Step(sm) + + +/* Port Timers state machine - implemented as a function that will be called + * once a second as a registered event loop timeout */ +static void eapol_port_timers_tick(void *eloop_ctx, void *timeout_ctx) +{ + struct eapol_sm *sm = timeout_ctx; + + if (sm->authWhile > 0) + sm->authWhile--; + if (sm->heldWhile > 0) + sm->heldWhile--; + if (sm->startWhen > 0) + sm->startWhen--; + if (sm->idleWhile > 0) + sm->idleWhile--; + wpa_printf(MSG_MSGDUMP, "EAPOL: Port Timers tick - authWhile=%d " + "heldWhile=%d startWhen=%d idleWhile=%d", + sm->authWhile, sm->heldWhile, sm->startWhen, sm->idleWhile); + + eapol_sm_step(sm); + + eloop_register_timeout(1, 0, eapol_port_timers_tick, eloop_ctx, sm); +} + + +SM_STATE(SUPP_PAE, LOGOFF) +{ + SM_ENTRY(SUPP_PAE, LOGOFF); + eapol_sm_txLogoff(sm); + sm->logoffSent = TRUE; + sm->suppPortStatus = Unauthorized; +} + + +SM_STATE(SUPP_PAE, DISCONNECTED) +{ + SM_ENTRY(SUPP_PAE, DISCONNECTED); + sm->sPortMode = Auto; + sm->startCount = 0; + sm->logoffSent = FALSE; + sm->suppPortStatus = Unauthorized; + sm->suppAbort = TRUE; + + sm->unicast_key_received = FALSE; + sm->broadcast_key_received = FALSE; +} + + +SM_STATE(SUPP_PAE, CONNECTING) +{ + SM_ENTRY(SUPP_PAE, CONNECTING); + sm->startWhen = sm->startPeriod; + sm->startCount++; + sm->eapolEap = FALSE; + eapol_sm_txStart(sm); +} + + +SM_STATE(SUPP_PAE, AUTHENTICATING) +{ + SM_ENTRY(SUPP_PAE, AUTHENTICATING); + sm->startCount = 0; + sm->suppSuccess = FALSE; + sm->suppFail = FALSE; + sm->suppTimeout = FALSE; + sm->keyRun = FALSE; + sm->keyDone = FALSE; + sm->suppStart = TRUE; +} + + +SM_STATE(SUPP_PAE, HELD) +{ + SM_ENTRY(SUPP_PAE, HELD); + sm->heldWhile = sm->heldPeriod; + sm->suppPortStatus = Unauthorized; + sm->cb_status = EAPOL_CB_FAILURE; +} + + +SM_STATE(SUPP_PAE, AUTHENTICATED) +{ + SM_ENTRY(SUPP_PAE, AUTHENTICATED); + sm->suppPortStatus = Authorized; + sm->cb_status = EAPOL_CB_SUCCESS; +} + + +SM_STATE(SUPP_PAE, RESTART) +{ + SM_ENTRY(SUPP_PAE, RESTART); + sm->eapRestart = TRUE; +} + + +SM_STATE(SUPP_PAE, S_FORCE_AUTH) +{ + SM_ENTRY(SUPP_PAE, S_FORCE_AUTH); + sm->suppPortStatus = Authorized; + sm->sPortMode = ForceAuthorized; +} + + +SM_STATE(SUPP_PAE, S_FORCE_UNAUTH) +{ + SM_ENTRY(SUPP_PAE, S_FORCE_UNAUTH); + sm->suppPortStatus = Unauthorized; + sm->sPortMode = ForceUnauthorized; + eapol_sm_txLogoff(sm); +} + + +SM_STEP(SUPP_PAE) +{ + if ((sm->userLogoff && !sm->logoffSent) && + !(sm->initialize || !sm->portEnabled)) + SM_ENTER_GLOBAL(SUPP_PAE, LOGOFF); + else if (((sm->portControl == Auto) && + (sm->sPortMode != sm->portControl)) || + sm->initialize || !sm->portEnabled) + SM_ENTER_GLOBAL(SUPP_PAE, DISCONNECTED); + else if ((sm->portControl == ForceAuthorized) && + (sm->sPortMode != sm->portControl) && + !(sm->initialize || !sm->portEnabled)) + SM_ENTER_GLOBAL(SUPP_PAE, S_FORCE_AUTH); + else if ((sm->portControl == ForceUnauthorized) && + (sm->sPortMode != sm->portControl) && + !(sm->initialize || !sm->portEnabled)) + SM_ENTER_GLOBAL(SUPP_PAE, S_FORCE_UNAUTH); + else switch (sm->SUPP_PAE_state) { + case SUPP_PAE_UNKNOWN: + break; + case SUPP_PAE_LOGOFF: + if (!sm->userLogoff) + SM_ENTER(SUPP_PAE, DISCONNECTED); + break; + case SUPP_PAE_DISCONNECTED: + SM_ENTER(SUPP_PAE, CONNECTING); + break; + case SUPP_PAE_CONNECTING: + if (sm->startWhen == 0 && sm->startCount < sm->maxStart) + SM_ENTER(SUPP_PAE, CONNECTING); + else if (sm->startWhen == 0 && + sm->startCount >= sm->maxStart && + sm->portValid) + SM_ENTER(SUPP_PAE, AUTHENTICATED); + else if (sm->eapSuccess || sm->eapFail) + SM_ENTER(SUPP_PAE, AUTHENTICATING); + else if (sm->eapolEap) + SM_ENTER(SUPP_PAE, RESTART); + else if (sm->startWhen == 0 && + sm->startCount >= sm->maxStart && + !sm->portValid) + SM_ENTER(SUPP_PAE, HELD); + break; + case SUPP_PAE_AUTHENTICATING: + if (sm->eapSuccess && !sm->portValid && + sm->conf.accept_802_1x_keys && + sm->conf.required_keys == 0) { + wpa_printf(MSG_DEBUG, "EAPOL: IEEE 802.1X for " + "plaintext connection; no EAPOL-Key frames " + "required"); + sm->portValid = TRUE; + if (sm->ctx->eapol_done_cb) + sm->ctx->eapol_done_cb(sm->ctx->ctx); + } + if (sm->eapSuccess && sm->portValid) + SM_ENTER(SUPP_PAE, AUTHENTICATED); + else if (sm->eapFail || (sm->keyDone && !sm->portValid)) + SM_ENTER(SUPP_PAE, HELD); + else if (sm->suppTimeout) + SM_ENTER(SUPP_PAE, CONNECTING); + break; + case SUPP_PAE_HELD: + if (sm->heldWhile == 0) + SM_ENTER(SUPP_PAE, CONNECTING); + else if (sm->eapolEap) + SM_ENTER(SUPP_PAE, RESTART); + break; + case SUPP_PAE_AUTHENTICATED: + if (sm->eapolEap && sm->portValid) + SM_ENTER(SUPP_PAE, RESTART); + else if (!sm->portValid) + SM_ENTER(SUPP_PAE, DISCONNECTED); + break; + case SUPP_PAE_RESTART: + if (!sm->eapRestart) + SM_ENTER(SUPP_PAE, AUTHENTICATING); + break; + case SUPP_PAE_S_FORCE_AUTH: + break; + case SUPP_PAE_S_FORCE_UNAUTH: + break; + } +} + + +SM_STATE(KEY_RX, NO_KEY_RECEIVE) +{ + SM_ENTRY(KEY_RX, NO_KEY_RECEIVE); +} + + +SM_STATE(KEY_RX, KEY_RECEIVE) +{ + SM_ENTRY(KEY_RX, KEY_RECEIVE); + eapol_sm_processKey(sm); + sm->rxKey = FALSE; +} + + +SM_STEP(KEY_RX) +{ + if (sm->initialize || !sm->portEnabled) + SM_ENTER_GLOBAL(KEY_RX, NO_KEY_RECEIVE); + switch (sm->KEY_RX_state) { + case KEY_RX_UNKNOWN: + break; + case KEY_RX_NO_KEY_RECEIVE: + if (sm->rxKey) + SM_ENTER(KEY_RX, KEY_RECEIVE); + break; + case KEY_RX_KEY_RECEIVE: + if (sm->rxKey) + SM_ENTER(KEY_RX, KEY_RECEIVE); + break; + } +} + + +SM_STATE(SUPP_BE, REQUEST) +{ + SM_ENTRY(SUPP_BE, REQUEST); + sm->authWhile = 0; + sm->eapReq = TRUE; + eapol_sm_getSuppRsp(sm); +} + + +SM_STATE(SUPP_BE, RESPONSE) +{ + SM_ENTRY(SUPP_BE, RESPONSE); + eapol_sm_txSuppRsp(sm); + sm->eapResp = FALSE; +} + + +SM_STATE(SUPP_BE, SUCCESS) +{ + SM_ENTRY(SUPP_BE, SUCCESS); + sm->keyRun = TRUE; + sm->suppSuccess = TRUE; + + if (eap_key_available(sm->eap)) { + /* New key received - clear IEEE 802.1X EAPOL-Key replay + * counter */ + sm->replay_counter_valid = FALSE; + } +} + + +SM_STATE(SUPP_BE, FAIL) +{ + SM_ENTRY(SUPP_BE, FAIL); + sm->suppFail = TRUE; +} + + +SM_STATE(SUPP_BE, TIMEOUT) +{ + SM_ENTRY(SUPP_BE, TIMEOUT); + sm->suppTimeout = TRUE; +} + + +SM_STATE(SUPP_BE, IDLE) +{ + SM_ENTRY(SUPP_BE, IDLE); + sm->suppStart = FALSE; + sm->initial_req = TRUE; +} + + +SM_STATE(SUPP_BE, INITIALIZE) +{ + SM_ENTRY(SUPP_BE, INITIALIZE); + eapol_sm_abortSupp(sm); + sm->suppAbort = FALSE; +} + + +SM_STATE(SUPP_BE, RECEIVE) +{ + SM_ENTRY(SUPP_BE, RECEIVE); + sm->authWhile = sm->authPeriod; + sm->eapolEap = FALSE; + sm->eapNoResp = FALSE; + sm->initial_req = FALSE; +} + + +SM_STEP(SUPP_BE) +{ + if (sm->initialize || sm->suppAbort) + SM_ENTER_GLOBAL(SUPP_BE, INITIALIZE); + else switch (sm->SUPP_BE_state) { + case SUPP_BE_UNKNOWN: + break; + case SUPP_BE_REQUEST: + if (sm->eapResp && sm->eapNoResp) { + wpa_printf(MSG_DEBUG, "EAPOL: SUPP_BE REQUEST: both " + "eapResp and eapNoResp set?!"); + } + if (sm->eapResp) + SM_ENTER(SUPP_BE, RESPONSE); + else if (sm->eapNoResp) + SM_ENTER(SUPP_BE, RECEIVE); + break; + case SUPP_BE_RESPONSE: + SM_ENTER(SUPP_BE, RECEIVE); + break; + case SUPP_BE_SUCCESS: + SM_ENTER(SUPP_BE, IDLE); + break; + case SUPP_BE_FAIL: + SM_ENTER(SUPP_BE, IDLE); + break; + case SUPP_BE_TIMEOUT: + SM_ENTER(SUPP_BE, IDLE); + break; + case SUPP_BE_IDLE: + if (sm->eapFail && sm->suppStart) + SM_ENTER(SUPP_BE, FAIL); + else if (sm->eapolEap && sm->suppStart) + SM_ENTER(SUPP_BE, REQUEST); + else if (sm->eapSuccess && sm->suppStart) + SM_ENTER(SUPP_BE, SUCCESS); + break; + case SUPP_BE_INITIALIZE: + SM_ENTER(SUPP_BE, IDLE); + break; + case SUPP_BE_RECEIVE: + if (sm->eapolEap) + SM_ENTER(SUPP_BE, REQUEST); + else if (sm->eapFail) + SM_ENTER(SUPP_BE, FAIL); + else if (sm->authWhile == 0) + SM_ENTER(SUPP_BE, TIMEOUT); + else if (sm->eapSuccess) + SM_ENTER(SUPP_BE, SUCCESS); + break; + } +} + + +static void eapol_sm_txLogoff(struct eapol_sm *sm) +{ + wpa_printf(MSG_DEBUG, "EAPOL: txLogoff"); + sm->ctx->eapol_send(sm->ctx->ctx, IEEE802_1X_TYPE_EAPOL_LOGOFF, + (u8 *) "", 0); + sm->dot1xSuppEapolLogoffFramesTx++; + sm->dot1xSuppEapolFramesTx++; +} + + +static void eapol_sm_txStart(struct eapol_sm *sm) +{ + wpa_printf(MSG_DEBUG, "EAPOL: txStart"); + sm->ctx->eapol_send(sm->ctx->ctx, IEEE802_1X_TYPE_EAPOL_START, + (u8 *) "", 0); + sm->dot1xSuppEapolStartFramesTx++; + sm->dot1xSuppEapolFramesTx++; +} + + +#define IEEE8021X_ENCR_KEY_LEN 32 +#define IEEE8021X_SIGN_KEY_LEN 32 + +struct eap_key_data { + u8 encr_key[IEEE8021X_ENCR_KEY_LEN]; + u8 sign_key[IEEE8021X_SIGN_KEY_LEN]; +}; + + +static void eapol_sm_processKey(struct eapol_sm *sm) +{ + struct ieee802_1x_hdr *hdr; + struct ieee802_1x_eapol_key *key; + struct eap_key_data keydata; + u8 orig_key_sign[IEEE8021X_KEY_SIGN_LEN], datakey[32]; + u8 ekey[IEEE8021X_KEY_IV_LEN + IEEE8021X_ENCR_KEY_LEN]; + int key_len, res, sign_key_len, encr_key_len; + + wpa_printf(MSG_DEBUG, "EAPOL: processKey"); + if (sm->last_rx_key == NULL) + return; + + if (!sm->conf.accept_802_1x_keys) { + wpa_printf(MSG_WARNING, "EAPOL: Received IEEE 802.1X EAPOL-Key" + " even though this was not accepted - " + "ignoring this packet"); + return; + } + + hdr = (struct ieee802_1x_hdr *) sm->last_rx_key; + key = (struct ieee802_1x_eapol_key *) (hdr + 1); + if (sizeof(*hdr) + be_to_host16(hdr->length) > sm->last_rx_key_len) { + wpa_printf(MSG_WARNING, "EAPOL: Too short EAPOL-Key frame"); + return; + } + wpa_printf(MSG_DEBUG, "EAPOL: RX IEEE 802.1X ver=%d type=%d len=%d " + "EAPOL-Key: type=%d key_length=%d key_index=0x%x", + hdr->version, hdr->type, be_to_host16(hdr->length), + key->type, be_to_host16(key->key_length), key->key_index); + + sign_key_len = IEEE8021X_SIGN_KEY_LEN; + encr_key_len = IEEE8021X_ENCR_KEY_LEN; + res = eapol_sm_get_key(sm, (u8 *) &keydata, sizeof(keydata)); + if (res < 0) { + wpa_printf(MSG_DEBUG, "EAPOL: Could not get master key for " + "decrypting EAPOL-Key keys"); + return; + } + if (res == 16) { + /* LEAP derives only 16 bytes of keying material. */ + res = eapol_sm_get_key(sm, (u8 *) &keydata, 16); + if (res) { + wpa_printf(MSG_DEBUG, "EAPOL: Could not get LEAP " + "master key for decrypting EAPOL-Key keys"); + return; + } + sign_key_len = 16; + encr_key_len = 16; + memcpy(keydata.sign_key, keydata.encr_key, 16); + } else if (res) { + wpa_printf(MSG_DEBUG, "EAPOL: Could not get enough master key " + "data for decrypting EAPOL-Key keys (res=%d)", res); + return; + } + + /* The key replay_counter must increase when same master key */ + if (sm->replay_counter_valid && + memcmp(sm->last_replay_counter, key->replay_counter, + IEEE8021X_REPLAY_COUNTER_LEN) >= 0) { + wpa_printf(MSG_WARNING, "EAPOL: EAPOL-Key replay counter did " + "not increase - ignoring key"); + wpa_hexdump(MSG_DEBUG, "EAPOL: last replay counter", + sm->last_replay_counter, + IEEE8021X_REPLAY_COUNTER_LEN); + wpa_hexdump(MSG_DEBUG, "EAPOL: received replay counter", + key->replay_counter, IEEE8021X_REPLAY_COUNTER_LEN); + return; + } + + /* Verify key signature (HMAC-MD5) */ + memcpy(orig_key_sign, key->key_signature, IEEE8021X_KEY_SIGN_LEN); + memset(key->key_signature, 0, IEEE8021X_KEY_SIGN_LEN); + hmac_md5(keydata.sign_key, sign_key_len, + sm->last_rx_key, sizeof(*hdr) + be_to_host16(hdr->length), + key->key_signature); + if (memcmp(orig_key_sign, key->key_signature, IEEE8021X_KEY_SIGN_LEN) + != 0) { + wpa_printf(MSG_DEBUG, "EAPOL: Invalid key signature in " + "EAPOL-Key packet"); + memcpy(key->key_signature, orig_key_sign, + IEEE8021X_KEY_SIGN_LEN); + return; + } + wpa_printf(MSG_DEBUG, "EAPOL: EAPOL-Key key signature verified"); + + key_len = be_to_host16(hdr->length) - sizeof(*key); + if (key_len > 32 || be_to_host16(key->key_length) > 32) { + wpa_printf(MSG_WARNING, "EAPOL: Too long key data length %d", + key_len ? key_len : be_to_host16(key->key_length)); + return; + } + if (key_len == be_to_host16(key->key_length)) { + memcpy(ekey, key->key_iv, IEEE8021X_KEY_IV_LEN); + memcpy(ekey + IEEE8021X_KEY_IV_LEN, keydata.encr_key, + encr_key_len); + memcpy(datakey, key + 1, key_len); + rc4(datakey, key_len, ekey, + IEEE8021X_KEY_IV_LEN + encr_key_len); + wpa_hexdump_key(MSG_DEBUG, "EAPOL: Decrypted(RC4) key", + datakey, key_len); + } else if (key_len == 0) { + /* IEEE 802.1X-REV specifies that least significant Key Length + * octets from MS-MPPE-Send-Key are used as the key if the key + * data is not present. This seems to be meaning the beginning + * of the MS-MPPE-Send-Key. In addition, MS-MPPE-Send-Key in + * Supplicant corresponds to MS-MPPE-Recv-Key in Authenticator. + * Anyway, taking the beginning of the keying material from EAP + * seems to interoperate with Authenticators. */ + key_len = be_to_host16(key->key_length); + memcpy(datakey, keydata.encr_key, key_len); + wpa_hexdump_key(MSG_DEBUG, "EAPOL: using part of EAP keying " + "material data encryption key", + datakey, key_len); + } else { + wpa_printf(MSG_DEBUG, "EAPOL: Invalid key data length %d " + "(key_length=%d)", key_len, + be_to_host16(key->key_length)); + return; + } + + sm->replay_counter_valid = TRUE; + memcpy(sm->last_replay_counter, key->replay_counter, + IEEE8021X_REPLAY_COUNTER_LEN); + + wpa_printf(MSG_DEBUG, "EAPOL: Setting dynamic WEP key: %s keyidx %d " + "len %d", + key->key_index & IEEE8021X_KEY_INDEX_FLAG ? + "unicast" : "broadcast", + key->key_index & IEEE8021X_KEY_INDEX_MASK, key_len); + + if (sm->ctx->set_wep_key && + sm->ctx->set_wep_key(sm->ctx->ctx, + key->key_index & IEEE8021X_KEY_INDEX_FLAG, + key->key_index & IEEE8021X_KEY_INDEX_MASK, + datakey, key_len) < 0) { + wpa_printf(MSG_WARNING, "EAPOL: Failed to set WEP key to the " + " driver."); + } else { + if (key->key_index & IEEE8021X_KEY_INDEX_FLAG) + sm->unicast_key_received = TRUE; + else + sm->broadcast_key_received = TRUE; + + if ((sm->unicast_key_received || + !(sm->conf.required_keys & EAPOL_REQUIRE_KEY_UNICAST)) && + (sm->broadcast_key_received || + !(sm->conf.required_keys & EAPOL_REQUIRE_KEY_BROADCAST))) + { + wpa_printf(MSG_DEBUG, "EAPOL: all required EAPOL-Key " + "frames received"); + sm->portValid = TRUE; + if (sm->ctx->eapol_done_cb) + sm->ctx->eapol_done_cb(sm->ctx->ctx); + } + } +} + + +static void eapol_sm_getSuppRsp(struct eapol_sm *sm) +{ + wpa_printf(MSG_DEBUG, "EAPOL: getSuppRsp"); + /* EAP layer processing; no special code is needed, since Supplicant + * Backend state machine is waiting for eapNoResp or eapResp to be set + * and these are only set in the EAP state machine when the processing + * has finished. */ +} + + +static void eapol_sm_txSuppRsp(struct eapol_sm *sm) +{ + u8 *resp; + size_t resp_len; + + wpa_printf(MSG_DEBUG, "EAPOL: txSuppRsp"); + resp = eap_get_eapRespData(sm->eap, &resp_len); + if (resp == NULL) { + wpa_printf(MSG_WARNING, "EAPOL: txSuppRsp - EAP response data " + "not available"); + return; + } + + /* Send EAP-Packet from the EAP layer to the Authenticator */ + sm->ctx->eapol_send(sm->ctx->ctx, IEEE802_1X_TYPE_EAP_PACKET, + resp, resp_len); + + /* eapRespData is not used anymore, so free it here */ + free(resp); + + if (sm->initial_req) + sm->dot1xSuppEapolReqIdFramesRx++; + else + sm->dot1xSuppEapolReqFramesRx++; + sm->dot1xSuppEapolRespFramesTx++; + sm->dot1xSuppEapolFramesTx++; +} + + +static void eapol_sm_abortSupp(struct eapol_sm *sm) +{ + /* release system resources that may have been allocated for the + * authentication session */ + free(sm->last_rx_key); + sm->last_rx_key = NULL; + free(sm->eapReqData); + sm->eapReqData = NULL; + eap_sm_abort(sm->eap); +} + + +struct eapol_sm *eapol_sm_init(struct eapol_ctx *ctx) +{ + struct eapol_sm *sm; + sm = malloc(sizeof(*sm)); + if (sm == NULL) + return NULL; + memset(sm, 0, sizeof(*sm)); + sm->ctx = ctx; + + sm->portControl = Auto; + + /* Supplicant PAE state machine */ + sm->heldPeriod = 60; + sm->startPeriod = 30; + sm->maxStart = 3; + + /* Supplicant Backend state machine */ + sm->authPeriod = 30; + + sm->eap = eap_sm_init(sm, &eapol_cb, sm->ctx->msg_ctx); + if (sm->eap == NULL) { + free(sm); + return NULL; + } + + /* Initialize EAPOL state machines */ + sm->initialize = TRUE; + eapol_sm_step(sm); + sm->initialize = FALSE; + eapol_sm_step(sm); + + eloop_register_timeout(1, 0, eapol_port_timers_tick, NULL, sm); + + return sm; +} + + +void eapol_sm_deinit(struct eapol_sm *sm) +{ + if (sm == NULL) + return; + eloop_cancel_timeout(eapol_sm_step_timeout, NULL, sm); + eloop_cancel_timeout(eapol_port_timers_tick, NULL, sm); + eap_sm_deinit(sm->eap); + free(sm->last_rx_key); + free(sm->eapReqData); + free(sm->ctx); + free(sm); +} + + +static void eapol_sm_step_timeout(void *eloop_ctx, void *timeout_ctx) +{ + eapol_sm_step(timeout_ctx); +} + + +void eapol_sm_step(struct eapol_sm *sm) +{ + int i; + + /* In theory, it should be ok to run this in loop until !changed. + * However, it is better to use a limit on number of iterations to + * allow events (e.g., SIGTERM) to stop the program cleanly if the + * state machine were to generate a busy loop. */ + for (i = 0; i < 100; i++) { + sm->changed = FALSE; + SM_STEP_RUN(SUPP_PAE); + SM_STEP_RUN(KEY_RX); + SM_STEP_RUN(SUPP_BE); + if (eap_sm_step(sm->eap)) + sm->changed = TRUE; + } + + if (sm->changed) { + /* restart EAPOL state machine step from timeout call in order + * to allow other events to be processed. */ + eloop_cancel_timeout(eapol_sm_step_timeout, NULL, sm); + eloop_register_timeout(0, 0, eapol_sm_step_timeout, NULL, sm); + } + + if (sm->ctx->cb && sm->cb_status != EAPOL_CB_IN_PROGRESS) { + int success = sm->cb_status == EAPOL_CB_SUCCESS ? 1 : 0; + sm->cb_status = EAPOL_CB_IN_PROGRESS; + sm->ctx->cb(sm, success, sm->ctx->cb_ctx); + } +} + + +static const char *eapol_supp_pae_state(int state) +{ + switch (state) { + case SUPP_PAE_LOGOFF: + return "LOGOFF"; + case SUPP_PAE_DISCONNECTED: + return "DISCONNECTED"; + case SUPP_PAE_CONNECTING: + return "CONNECTING"; + case SUPP_PAE_AUTHENTICATING: + return "AUTHENTICATING"; + case SUPP_PAE_HELD: + return "HELD"; + case SUPP_PAE_AUTHENTICATED: + return "AUTHENTICATED"; + case SUPP_PAE_RESTART: + return "RESTART"; + default: + return "UNKNOWN"; + } +} + + +static const char *eapol_supp_be_state(int state) +{ + switch (state) { + case SUPP_BE_REQUEST: + return "REQUEST"; + case SUPP_BE_RESPONSE: + return "RESPONSE"; + case SUPP_BE_SUCCESS: + return "SUCCESS"; + case SUPP_BE_FAIL: + return "FAIL"; + case SUPP_BE_TIMEOUT: + return "TIMEOUT"; + case SUPP_BE_IDLE: + return "IDLE"; + case SUPP_BE_INITIALIZE: + return "INITIALIZE"; + case SUPP_BE_RECEIVE: + return "RECEIVE"; + default: + return "UNKNOWN"; + } +} + + +static const char * eapol_port_status(PortStatus status) +{ + if (status == Authorized) + return "Authorized"; + else + return "Unauthorized"; +} + + +static const char * eapol_port_control(PortControl ctrl) +{ + switch (ctrl) { + case Auto: + return "Auto"; + case ForceUnauthorized: + return "ForceUnauthorized"; + case ForceAuthorized: + return "ForceAuthorized"; + default: + return "Unknown"; + } +} + + +void eapol_sm_configure(struct eapol_sm *sm, int heldPeriod, int authPeriod, + int startPeriod, int maxStart) +{ + if (sm == NULL) + return; + if (heldPeriod >= 0) + sm->heldPeriod = heldPeriod; + if (authPeriod >= 0) + sm->authPeriod = authPeriod; + if (startPeriod >= 0) + sm->startPeriod = startPeriod; + if (maxStart >= 0) + sm->maxStart = maxStart; +} + + +int eapol_sm_get_status(struct eapol_sm *sm, char *buf, size_t buflen, + int verbose) +{ + int len; + if (sm == NULL) + return 0; + + len = snprintf(buf, buflen, + "Supplicant PAE state=%s\n" + "suppPortStatus=%s\n", + eapol_supp_pae_state(sm->SUPP_PAE_state), + eapol_port_status(sm->suppPortStatus)); + + if (verbose) { + len += snprintf(buf + len, buflen - len, + "heldPeriod=%d\n" + "authPeriod=%d\n" + "startPeriod=%d\n" + "maxStart=%d\n" + "portControl=%s\n" + "Supplicant Backend state=%s\n", + sm->heldPeriod, + sm->authPeriod, + sm->startPeriod, + sm->maxStart, + eapol_port_control(sm->portControl), + eapol_supp_be_state(sm->SUPP_BE_state)); + } + + len += eap_sm_get_status(sm->eap, buf + len, buflen - len, verbose); + + return len; +} + + +int eapol_sm_get_mib(struct eapol_sm *sm, char *buf, size_t buflen) +{ + int len; + if (sm == NULL) + return 0; + len = snprintf(buf, buflen, + "dot1xSuppPaeState=%d\n" + "dot1xSuppHeldPeriod=%d\n" + "dot1xSuppAuthPeriod=%d\n" + "dot1xSuppStartPeriod=%d\n" + "dot1xSuppMaxStart=%d\n" + "dot1xSuppSuppControlledPortStatus=%s\n" + "dot1xSuppBackendPaeState=%d\n" + "dot1xSuppEapolFramesRx=%d\n" + "dot1xSuppEapolFramesTx=%d\n" + "dot1xSuppEapolStartFramesTx=%d\n" + "dot1xSuppEapolLogoffFramesTx=%d\n" + "dot1xSuppEapolRespFramesTx=%d\n" + "dot1xSuppEapolReqIdFramesRx=%d\n" + "dot1xSuppEapolReqFramesRx=%d\n" + "dot1xSuppInvalidEapolFramesRx=%d\n" + "dot1xSuppEapLengthErrorFramesRx=%d\n" + "dot1xSuppLastEapolFrameVersion=%d\n" + "dot1xSuppLastEapolFrameSource=" MACSTR "\n", + sm->SUPP_PAE_state, + sm->heldPeriod, + sm->authPeriod, + sm->startPeriod, + sm->maxStart, + sm->suppPortStatus == Authorized ? + "Authorized" : "Unauthorized", + sm->SUPP_BE_state, + sm->dot1xSuppEapolFramesRx, + sm->dot1xSuppEapolFramesTx, + sm->dot1xSuppEapolStartFramesTx, + sm->dot1xSuppEapolLogoffFramesTx, + sm->dot1xSuppEapolRespFramesTx, + sm->dot1xSuppEapolReqIdFramesRx, + sm->dot1xSuppEapolReqFramesRx, + sm->dot1xSuppInvalidEapolFramesRx, + sm->dot1xSuppEapLengthErrorFramesRx, + sm->dot1xSuppLastEapolFrameVersion, + MAC2STR(sm->dot1xSuppLastEapolFrameSource)); + return len; +} + + +void eapol_sm_rx_eapol(struct eapol_sm *sm, u8 *src, u8 *buf, size_t len) +{ + struct ieee802_1x_hdr *hdr; + struct ieee802_1x_eapol_key *key; + int plen, data_len; + + if (sm == NULL) + return; + sm->dot1xSuppEapolFramesRx++; + if (len < sizeof(*hdr)) { + sm->dot1xSuppInvalidEapolFramesRx++; + return; + } + hdr = (struct ieee802_1x_hdr *) buf; + sm->dot1xSuppLastEapolFrameVersion = hdr->version; + memcpy(sm->dot1xSuppLastEapolFrameSource, src, ETH_ALEN); + if (hdr->version < EAPOL_VERSION) { + /* TODO: backwards compatibility */ + } + plen = be_to_host16(hdr->length); + if (plen > len - sizeof(*hdr)) { + sm->dot1xSuppEapLengthErrorFramesRx++; + return; + } + data_len = plen + sizeof(*hdr); + + switch (hdr->type) { + case IEEE802_1X_TYPE_EAP_PACKET: + if (sm->cached_pmk) { + /* Trying to use PMKSA caching, but Authenticator did + * not seem to have a matching entry. Need to restart + * EAPOL state machines. + */ + eapol_sm_abort_cached(sm); + } + free(sm->eapReqData); + sm->eapReqDataLen = plen; + sm->eapReqData = malloc(sm->eapReqDataLen); + if (sm->eapReqData) { + wpa_printf(MSG_DEBUG, "EAPOL: Received EAP-Packet " + "frame"); + memcpy(sm->eapReqData, (u8 *) (hdr + 1), + sm->eapReqDataLen); + sm->eapolEap = TRUE; + eapol_sm_step(sm); + } + break; + case IEEE802_1X_TYPE_EAPOL_KEY: + if (plen < sizeof(*key)) { + wpa_printf(MSG_DEBUG, "EAPOL: Too short EAPOL-Key " + "frame received"); + break; + } + key = (struct ieee802_1x_eapol_key *) (hdr + 1); + if (key->type == EAPOL_KEY_TYPE_WPA || + key->type == EAPOL_KEY_TYPE_RSN) { + /* WPA Supplicant takes care of this frame. */ + wpa_printf(MSG_DEBUG, "EAPOL: Ignoring WPA EAPOL-Key " + "frame in EAPOL state machines"); + break; + } + if (key->type != EAPOL_KEY_TYPE_RC4) { + wpa_printf(MSG_DEBUG, "EAPOL: Ignored unknown " + "EAPOL-Key type %d", key->type); + break; + } + free(sm->last_rx_key); + sm->last_rx_key = malloc(data_len); + if (sm->last_rx_key) { + wpa_printf(MSG_DEBUG, "EAPOL: Received EAPOL-Key " + "frame"); + memcpy(sm->last_rx_key, buf, data_len); + sm->last_rx_key_len = data_len; + sm->rxKey = TRUE; + eapol_sm_step(sm); + } + break; + default: + wpa_printf(MSG_DEBUG, "EAPOL: Received unknown EAPOL type %d", + hdr->type); + sm->dot1xSuppInvalidEapolFramesRx++; + break; + } +} + + +void eapol_sm_notify_tx_eapol_key(struct eapol_sm *sm) +{ + if (sm) + sm->dot1xSuppEapolFramesTx++; +} + + +void eapol_sm_notify_portEnabled(struct eapol_sm *sm, Boolean enabled) +{ + if (sm == NULL) + return; + wpa_printf(MSG_DEBUG, "EAPOL: External notification - " + "portEnabled=%d", enabled); + sm->portEnabled = enabled; + eapol_sm_step(sm); +} + + +void eapol_sm_notify_portValid(struct eapol_sm *sm, Boolean valid) +{ + if (sm == NULL) + return; + wpa_printf(MSG_DEBUG, "EAPOL: External notification - " + "portValid=%d", valid); + sm->portValid = valid; + eapol_sm_step(sm); +} + + +void eapol_sm_notify_eap_success(struct eapol_sm *sm, Boolean success) +{ + if (sm == NULL) + return; + wpa_printf(MSG_DEBUG, "EAPOL: External notification - " + "EAP success=%d", success); + sm->eapSuccess = success; + sm->altAccept = success; + if (success) + eap_notify_success(sm->eap); + eapol_sm_step(sm); +} + + +void eapol_sm_notify_eap_fail(struct eapol_sm *sm, Boolean fail) +{ + if (sm == NULL) + return; + wpa_printf(MSG_DEBUG, "EAPOL: External notification - " + "EAP fail=%d", fail); + sm->eapFail = fail; + sm->altReject = fail; + eapol_sm_step(sm); +} + + +void eapol_sm_notify_config(struct eapol_sm *sm, struct wpa_ssid *config, + struct eapol_config *conf) +{ + if (sm == NULL) + return; + + sm->config = config; + + if (conf == NULL) + return; + + sm->conf.accept_802_1x_keys = conf->accept_802_1x_keys; + sm->conf.required_keys = conf->required_keys; + sm->conf.fast_reauth = conf->fast_reauth; + if (sm->eap) { + eap_set_fast_reauth(sm->eap, conf->fast_reauth); + eap_set_workaround(sm->eap, conf->workaround); + } +} + + +int eapol_sm_get_key(struct eapol_sm *sm, u8 *key, size_t len) +{ + u8 *eap_key; + size_t eap_len; + + if (sm == NULL || !eap_key_available(sm->eap)) + return -1; + eap_key = eap_get_eapKeyData(sm->eap, &eap_len); + if (eap_key == NULL) + return -1; + if (len > eap_len) + return eap_len; + memcpy(key, eap_key, len); + return 0; +} + + +void eapol_sm_notify_logoff(struct eapol_sm *sm, Boolean logoff) +{ + if (sm) { + sm->userLogoff = logoff; + eapol_sm_step(sm); + } +} + + +void eapol_sm_notify_cached(struct eapol_sm *sm) +{ + if (sm == NULL) + return; + sm->SUPP_PAE_state = SUPP_PAE_AUTHENTICATED; + sm->suppPortStatus = Authorized; + eap_notify_success(sm->eap); +} + + +void eapol_sm_notify_pmkid_attempt(struct eapol_sm *sm, int attempt) +{ + if (sm == NULL) + return; + if (attempt) { + wpa_printf(MSG_DEBUG, "RSN: Trying to use cached PMKSA"); + sm->cached_pmk = TRUE; + } else { + wpa_printf(MSG_DEBUG, "RSN: Do not try to use cached PMKSA"); + sm->cached_pmk = FALSE; + } +} + + +static void eapol_sm_abort_cached(struct eapol_sm *sm) +{ + wpa_printf(MSG_DEBUG, "RSN: Authenticator did not accept PMKID, " + "doing full EAP authentication"); + if (sm == NULL) + return; + sm->cached_pmk = FALSE; + sm->SUPP_PAE_state = SUPP_PAE_CONNECTING; + sm->suppPortStatus = Unauthorized; + sm->eapRestart= TRUE; +} + + +void eapol_sm_register_scard_ctx(struct eapol_sm *sm, void *ctx) +{ + if (sm) { + sm->ctx->scard_ctx = ctx; + eap_register_scard_ctx(sm->eap, ctx); + } +} + + +void eapol_sm_notify_portControl(struct eapol_sm *sm, PortControl portControl) +{ + if (sm == NULL) + return; + wpa_printf(MSG_DEBUG, "EAPOL: External notification - " + "portControl=%s", eapol_port_control(portControl)); + sm->portControl = portControl; + eapol_sm_step(sm); +} + + +void eapol_sm_notify_ctrl_attached(struct eapol_sm *sm) +{ + if (sm == NULL) + return; + eap_sm_notify_ctrl_attached(sm->eap); +} + + +void eapol_sm_notify_ctrl_response(struct eapol_sm *sm) +{ + if (sm == NULL) + return; + if (sm->eapReqData && !sm->eapReq) { + wpa_printf(MSG_DEBUG, "EAPOL: received control response (user " + "input) notification - retrying pending EAP " + "Request"); + sm->eapolEap = TRUE; + sm->eapReq = TRUE; + eapol_sm_step(sm); + } +} + + +static struct wpa_ssid * eapol_sm_get_config(void *ctx) +{ + struct eapol_sm *sm = ctx; + return sm ? sm->config : NULL; +} + + +static u8 * eapol_sm_get_eapReqData(void *ctx, size_t *len) +{ + struct eapol_sm *sm = ctx; + if (sm == NULL || sm->eapReqData == NULL) { + *len = 0; + return NULL; + } + + *len = sm->eapReqDataLen; + return sm->eapReqData; +} + + +static Boolean eapol_sm_get_bool(void *ctx, enum eapol_bool_var variable) +{ + struct eapol_sm *sm = ctx; + if (sm == NULL) + return FALSE; + switch (variable) { + case EAPOL_eapSuccess: + return sm->eapSuccess; + case EAPOL_eapRestart: + return sm->eapRestart; + case EAPOL_eapFail: + return sm->eapFail; + case EAPOL_eapResp: + return sm->eapResp; + case EAPOL_eapNoResp: + return sm->eapNoResp; + case EAPOL_eapReq: + return sm->eapReq; + case EAPOL_portEnabled: + return sm->portEnabled; + case EAPOL_altAccept: + return sm->altAccept; + case EAPOL_altReject: + return sm->altReject; + } + return FALSE; +} + + +static void eapol_sm_set_bool(void *ctx, enum eapol_bool_var variable, + Boolean value) +{ + struct eapol_sm *sm = ctx; + if (sm == NULL) + return; + switch (variable) { + case EAPOL_eapSuccess: + sm->eapSuccess = value; + break; + case EAPOL_eapRestart: + sm->eapRestart = value; + break; + case EAPOL_eapFail: + sm->eapFail = value; + break; + case EAPOL_eapResp: + sm->eapResp = value; + break; + case EAPOL_eapNoResp: + sm->eapNoResp = value; + break; + case EAPOL_eapReq: + sm->eapReq = value; + break; + case EAPOL_portEnabled: + sm->portEnabled = value; + break; + case EAPOL_altAccept: + sm->altAccept = value; + break; + case EAPOL_altReject: + sm->altReject = value; + break; + } +} + + +static unsigned int eapol_sm_get_int(void *ctx, enum eapol_int_var variable) +{ + struct eapol_sm *sm = ctx; + if (sm == NULL) + return 0; + switch (variable) { + case EAPOL_idleWhile: + return sm->idleWhile; + } + return 0; +} + + +static void eapol_sm_set_int(void *ctx, enum eapol_int_var variable, + unsigned int value) +{ + struct eapol_sm *sm = ctx; + if (sm == NULL) + return; + switch (variable) { + case EAPOL_idleWhile: + sm->idleWhile = value; + break; + } +} + + +static struct eapol_callbacks eapol_cb = +{ + .get_config = eapol_sm_get_config, + .get_bool = eapol_sm_get_bool, + .set_bool = eapol_sm_set_bool, + .get_int = eapol_sm_get_int, + .set_int = eapol_sm_set_int, + .get_eapReqData = eapol_sm_get_eapReqData, +}; diff --git a/contrib/wpa_supplicant/eapol_sm.h b/contrib/wpa_supplicant/eapol_sm.h new file mode 100644 index 0000000..b941203 --- /dev/null +++ b/contrib/wpa_supplicant/eapol_sm.h @@ -0,0 +1,138 @@ +#ifndef EAPOL_SM_H +#define EAPOL_SM_H + +#include "defs.h" + +typedef enum { Unauthorized, Authorized } PortStatus; +typedef enum { Auto, ForceUnauthorized, ForceAuthorized } PortControl; + +struct eapol_config { + int accept_802_1x_keys; +#define EAPOL_REQUIRE_KEY_UNICAST BIT(0) +#define EAPOL_REQUIRE_KEY_BROADCAST BIT(1) + int required_keys; /* which EAPOL-Key packets are required before + * marking connection authenticated */ + int fast_reauth; /* whether fast EAP reauthentication is enabled */ + int workaround; /* whether EAP workarounds are enabled */ +}; + +struct eapol_sm; + +struct eapol_ctx { + void *ctx; /* pointer to arbitrary upper level context */ + int preauth; /* This EAPOL state machine is used for IEEE 802.11i/RSN + * pre-authentication */ + void (*cb)(struct eapol_sm *eapol, int success, void *ctx); + void *cb_ctx, *msg_ctx, *scard_ctx; + void (*eapol_done_cb)(void *ctx); + int (*eapol_send)(void *ctx, int type, u8 *buf, size_t len); + int (*set_wep_key)(void *ctx, int unicast, int keyidx, + u8 *key, size_t keylen); +}; + + +struct wpa_ssid; + +#ifdef IEEE8021X_EAPOL +struct eapol_sm *eapol_sm_init(struct eapol_ctx *ctx); +void eapol_sm_deinit(struct eapol_sm *sm); +void eapol_sm_step(struct eapol_sm *sm); +int eapol_sm_get_status(struct eapol_sm *sm, char *buf, size_t buflen, + int verbose); +int eapol_sm_get_mib(struct eapol_sm *sm, char *buf, size_t buflen); +void eapol_sm_configure(struct eapol_sm *sm, int heldPeriod, int authPeriod, + int startPeriod, int maxStart); +void eapol_sm_rx_eapol(struct eapol_sm *sm, u8 *src, u8 *buf, size_t len); +void eapol_sm_notify_tx_eapol_key(struct eapol_sm *sm); +void eapol_sm_notify_portEnabled(struct eapol_sm *sm, Boolean enabled); +void eapol_sm_notify_portValid(struct eapol_sm *sm, Boolean valid); +void eapol_sm_notify_eap_success(struct eapol_sm *sm, Boolean success); +void eapol_sm_notify_eap_fail(struct eapol_sm *sm, Boolean fail); +void eapol_sm_notify_config(struct eapol_sm *sm, struct wpa_ssid *config, + struct eapol_config *conf); +int eapol_sm_get_key(struct eapol_sm *sm, u8 *key, size_t len); +void eapol_sm_notify_logoff(struct eapol_sm *sm, Boolean logoff); +void eapol_sm_notify_cached(struct eapol_sm *sm); +void eapol_sm_notify_pmkid_attempt(struct eapol_sm *sm, int attempt); +void eapol_sm_register_scard_ctx(struct eapol_sm *sm, void *ctx); +void eapol_sm_notify_portControl(struct eapol_sm *sm, PortControl portControl); +void eapol_sm_notify_ctrl_attached(struct eapol_sm *sm); +void eapol_sm_notify_ctrl_response(struct eapol_sm *sm); +#else /* IEEE8021X_EAPOL */ +static inline struct eapol_sm *eapol_sm_init(struct eapol_ctx *ctx) +{ + return (struct eapol_sm *) 1; +} +static inline void eapol_sm_deinit(struct eapol_sm *sm) +{ +} +static inline void eapol_sm_step(struct eapol_sm *sm) +{ +} +static inline int eapol_sm_get_status(struct eapol_sm *sm, char *buf, + size_t buflen, int verbose) +{ + return 0; +} +static inline int eapol_sm_get_mib(struct eapol_sm *sm, char *buf, + size_t buflen) +{ + return 0; +} +static inline void eapol_sm_configure(struct eapol_sm *sm, int heldPeriod, + int authPeriod, int startPeriod, + int maxStart) +{ +} +static inline void eapol_sm_rx_eapol(struct eapol_sm *sm, u8 *src, u8 *buf, + size_t len) +{ +} +static inline void eapol_sm_notify_tx_eapol_key(struct eapol_sm *sm) +{ +} +static inline void eapol_sm_notify_portEnabled(struct eapol_sm *sm, + Boolean enabled) +{ +} +static inline void eapol_sm_notify_portValid(struct eapol_sm *sm, + Boolean valid) +{ +} +static inline void eapol_sm_notify_eap_success(struct eapol_sm *sm, + Boolean success) +{ +} +static inline void eapol_sm_notify_eap_fail(struct eapol_sm *sm, Boolean fail) +{ +} +static inline void eapol_sm_notify_config(struct eapol_sm *sm, + struct wpa_ssid *config, + struct eapol_config *conf) +{ +} +static inline int eapol_sm_get_key(struct eapol_sm *sm, u8 *key, size_t len) +{ + return -1; +} +static inline void eapol_sm_notify_logoff(struct eapol_sm *sm, Boolean logoff) +{ +} +static inline void eapol_sm_notify_cached(struct eapol_sm *sm) +{ +} +#define eapol_sm_notify_pmkid_attempt(sm, attempt) do { } while (0) +#define eapol_sm_register_scard_ctx(sm, ctx) do { } while (0) +static inline void eapol_sm_notify_portControl(struct eapol_sm *sm, + PortControl portControl) +{ +} +static inline void eapol_sm_notify_ctrl_attached(struct eapol_sm *sm) +{ +} +static inline void eapol_sm_notify_ctrl_response(struct eapol_sm *sm) +{ +} +#endif /* IEEE8021X_EAPOL */ + +#endif /* EAPOL_SM_H */ diff --git a/contrib/wpa_supplicant/eapol_test.c b/contrib/wpa_supplicant/eapol_test.c new file mode 100644 index 0000000..061ae89 --- /dev/null +++ b/contrib/wpa_supplicant/eapol_test.c @@ -0,0 +1,1012 @@ +/* + * WPA Supplicant - test code + * Copyright (c) 2003-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * + * IEEE 802.1X Supplicant test code (to be used in place of wpa_supplicant.c. + * Not used in production version. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <unistd.h> +#include <ctype.h> +#include <string.h> +#include <signal.h> +#include <netinet/in.h> +#include <assert.h> +#include <arpa/inet.h> + +#include "common.h" +#include "config.h" +#include "eapol_sm.h" +#include "eloop.h" +#include "wpa.h" +#include "eap_i.h" +#include "wpa_supplicant.h" +#include "wpa_supplicant_i.h" +#include "radius.h" +#include "radius_client.h" +#include "l2_packet.h" +#include "ctrl_iface.h" +#include "pcsc_funcs.h" + + +extern int wpa_debug_level; +extern int wpa_debug_show_keys; +static int eapol_test_num_reauths = 0; +static int no_mppe_keys = 0; +static int num_mppe_ok = 0, num_mppe_mismatch = 0; + +static void send_eap_request_identity(void *eloop_ctx, void *timeout_ctx); + + +void wpa_msg(struct wpa_supplicant *wpa_s, int level, char *fmt, ...) +{ + va_list ap; + char *buf; + const int buflen = 2048; + int len; + + buf = malloc(buflen); + if (buf == NULL) { + printf("Failed to allocate message buffer for:\n"); + va_start(ap, fmt); + vprintf(fmt, ap); + printf("\n"); + va_end(ap); + return; + } + va_start(ap, fmt); + len = vsnprintf(buf, buflen, fmt, ap); + va_end(ap); + wpa_printf(level, "%s", buf); + wpa_supplicant_ctrl_iface_send(wpa_s, level, buf, len); + free(buf); +} + + +void wpa_supplicant_event(struct wpa_supplicant *wpa_s, wpa_event_type event, + union wpa_event_data *data) +{ +} + + +int rsn_preauth_init(struct wpa_supplicant *wpa_s, u8 *dst) +{ + return -1; +} + + +void rsn_preauth_deinit(struct wpa_supplicant *wpa_s) +{ +} + + +int pmksa_cache_list(struct wpa_supplicant *wpa_s, char *buf, size_t len) +{ + return 0; +} + + +int wpa_get_mib(struct wpa_supplicant *wpa_s, char *buf, size_t buflen) +{ + return 0; +} + + +void wpa_supplicant_req_scan(struct wpa_supplicant *wpa_s, int sec, int usec) +{ +} + + +const char * wpa_ssid_txt(u8 *ssid, size_t ssid_len) +{ + return NULL; +} + + +int wpa_supplicant_reload_configuration(struct wpa_supplicant *wpa_s) +{ + return -1; +} + + +static void ieee802_1x_encapsulate_radius(struct wpa_supplicant *wpa_s, + u8 *eap, size_t len) +{ + struct radius_msg *msg; + char buf[128]; + struct eap_hdr *hdr; + u8 *pos; + + wpa_printf(MSG_DEBUG, "Encapsulating EAP message into a RADIUS " + "packet"); + + wpa_s->radius_identifier = radius_client_get_id(wpa_s); + msg = radius_msg_new(RADIUS_CODE_ACCESS_REQUEST, + wpa_s->radius_identifier); + if (msg == NULL) { + printf("Could not create net RADIUS packet\n"); + return; + } + + radius_msg_make_authenticator(msg, (u8 *) wpa_s, sizeof(*wpa_s)); + + hdr = (struct eap_hdr *) eap; + pos = (u8 *) (hdr + 1); + if (len > sizeof(*hdr) && hdr->code == EAP_CODE_RESPONSE && + pos[0] == EAP_TYPE_IDENTITY) { + pos++; + free(wpa_s->eap_identity); + wpa_s->eap_identity_len = len - sizeof(*hdr) - 1; + wpa_s->eap_identity = malloc(wpa_s->eap_identity_len); + if (wpa_s->eap_identity) { + memcpy(wpa_s->eap_identity, pos, + wpa_s->eap_identity_len); + wpa_hexdump(MSG_DEBUG, "Learned identity from " + "EAP-Response-Identity", + wpa_s->eap_identity, + wpa_s->eap_identity_len); + } + } + + if (wpa_s->eap_identity && + !radius_msg_add_attr(msg, RADIUS_ATTR_USER_NAME, + wpa_s->eap_identity, + wpa_s->eap_identity_len)) { + printf("Could not add User-Name\n"); + goto fail; + } + + if (!radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IP_ADDRESS, + (u8 *) &wpa_s->own_ip_addr, 4)) { + printf("Could not add NAS-IP-Address\n"); + goto fail; + } + + snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT, + MAC2STR(wpa_s->own_addr)); + if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLING_STATION_ID, + (u8 *) buf, strlen(buf))) { + printf("Could not add Calling-Station-Id\n"); + goto fail; + } + + /* TODO: should probably check MTU from driver config; 2304 is max for + * IEEE 802.11, but use 1400 to avoid problems with too large packets + */ + if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_FRAMED_MTU, 1400)) { + printf("Could not add Framed-MTU\n"); + goto fail; + } + + if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_NAS_PORT_TYPE, + RADIUS_NAS_PORT_TYPE_IEEE_802_11)) { + printf("Could not add NAS-Port-Type\n"); + goto fail; + } + + snprintf(buf, sizeof(buf), "CONNECT 11Mbps 802.11b"); + if (!radius_msg_add_attr(msg, RADIUS_ATTR_CONNECT_INFO, + (u8 *) buf, strlen(buf))) { + printf("Could not add Connect-Info\n"); + goto fail; + } + + if (eap && !radius_msg_add_eap(msg, eap, len)) { + printf("Could not add EAP-Message\n"); + goto fail; + } + + /* State attribute must be copied if and only if this packet is + * Access-Request reply to the previous Access-Challenge */ + if (wpa_s->last_recv_radius && wpa_s->last_recv_radius->hdr->code == + RADIUS_CODE_ACCESS_CHALLENGE) { + int res = radius_msg_copy_attr(msg, wpa_s->last_recv_radius, + RADIUS_ATTR_STATE); + if (res < 0) { + printf("Could not copy State attribute from previous " + "Access-Challenge\n"); + goto fail; + } + if (res > 0) { + wpa_printf(MSG_DEBUG, " Copied RADIUS State " + "Attribute"); + } + } + + radius_client_send(wpa_s, msg, RADIUS_AUTH, wpa_s->own_addr); + return; + + fail: + radius_msg_free(msg); + free(msg); +} + + +static int eapol_test_eapol_send(void *ctx, int type, u8 *buf, size_t len) +{ + struct wpa_supplicant *wpa_s = ctx; + printf("WPA: wpa_eapol_send(type=%d len=%d)\n", type, len); + if (type == IEEE802_1X_TYPE_EAP_PACKET) { + wpa_hexdump(MSG_DEBUG, "TX EAP -> RADIUS", buf, len); + ieee802_1x_encapsulate_radius(wpa_s, buf, len); + } + return 0; +} + + +static void eapol_test_eapol_done_cb(void *ctx) +{ + printf("WPA: EAPOL processing complete\n"); +} + + +static void eapol_sm_reauth(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + printf("\n\n\n\n\neapol_test: Triggering EAP reauthentication\n\n"); + wpa_s->radius_access_accept_received = 0; + send_eap_request_identity(eloop_ctx, timeout_ctx); +} + + +static int eapol_test_compare_pmk(struct wpa_supplicant *wpa_s) +{ + u8 pmk[PMK_LEN]; + int ret = 1; + + if (eapol_sm_get_key(wpa_s->eapol, pmk, PMK_LEN) == 0) { + wpa_hexdump(MSG_DEBUG, "PMK from EAPOL", pmk, PMK_LEN); + if (memcmp(pmk, wpa_s->authenticator_pmk, PMK_LEN) != 0) + printf("WARNING: PMK mismatch\n"); + else if (wpa_s->radius_access_accept_received) + ret = 0; + } else if (wpa_s->authenticator_pmk_len == 16 && + eapol_sm_get_key(wpa_s->eapol, pmk, 16) == 0) { + wpa_hexdump(MSG_DEBUG, "LEAP PMK from EAPOL", pmk, 16); + if (memcmp(pmk, wpa_s->authenticator_pmk, 16) != 0) + printf("WARNING: PMK mismatch\n"); + else if (wpa_s->radius_access_accept_received) + ret = 0; + } else if (wpa_s->radius_access_accept_received && no_mppe_keys) { + /* No keying material expected */ + ret = 0; + } + + if (ret) + num_mppe_mismatch++; + else if (!no_mppe_keys) + num_mppe_ok++; + + return ret; +} + + +static void eapol_sm_cb(struct eapol_sm *eapol, int success, void *ctx) +{ + struct wpa_supplicant *wpa_s = ctx; + printf("eapol_sm_cb: success=%d\n", success); + eapol_test_num_reauths--; + if (eapol_test_num_reauths < 0) + eloop_terminate(); + else { + eapol_test_compare_pmk(wpa_s); + eloop_register_timeout(0, 100000, eapol_sm_reauth, + wpa_s, NULL); + } +} + + +static int test_eapol(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) +{ + struct eapol_config eapol_conf; + struct eapol_ctx *ctx; + + ctx = malloc(sizeof(*ctx)); + if (ctx == NULL) { + printf("Failed to allocate EAPOL context.\n"); + return -1; + } + memset(ctx, 0, sizeof(*ctx)); + ctx->ctx = wpa_s; + ctx->msg_ctx = wpa_s; + ctx->scard_ctx = wpa_s->scard; + ctx->cb = eapol_sm_cb; + ctx->cb_ctx = wpa_s; + ctx->preauth = 0; + ctx->eapol_done_cb = eapol_test_eapol_done_cb; + ctx->eapol_send = eapol_test_eapol_send; + + wpa_s->eapol = eapol_sm_init(ctx); + if (wpa_s->eapol == NULL) { + free(ctx); + printf("Failed to initialize EAPOL state machines.\n"); + return -1; + } + + wpa_s->current_ssid = ssid; + memset(&eapol_conf, 0, sizeof(eapol_conf)); + eapol_conf.accept_802_1x_keys = 1; + eapol_conf.required_keys = 0; + eapol_conf.fast_reauth = wpa_s->conf->fast_reauth; + eapol_conf.workaround = ssid->eap_workaround; + eapol_sm_notify_config(wpa_s->eapol, ssid, &eapol_conf); + eapol_sm_register_scard_ctx(wpa_s->eapol, wpa_s->scard); + + + eapol_sm_notify_portValid(wpa_s->eapol, FALSE); + /* 802.1X::portControl = Auto */ + eapol_sm_notify_portEnabled(wpa_s->eapol, TRUE); + + return 0; +} + + +static void test_eapol_clean(struct wpa_supplicant *wpa_s) +{ + radius_client_deinit(wpa_s); + free(wpa_s->last_eap_radius); + if (wpa_s->last_recv_radius) { + radius_msg_free(wpa_s->last_recv_radius); + free(wpa_s->last_recv_radius); + } + free(wpa_s->eap_identity); + wpa_s->eap_identity = NULL; + eapol_sm_deinit(wpa_s->eapol); + wpa_s->eapol = NULL; + if (wpa_s->auth_server) { + free(wpa_s->auth_server->shared_secret); + free(wpa_s->auth_server); + } + scard_deinit(wpa_s->scard); + wpa_supplicant_ctrl_iface_deinit(wpa_s); + wpa_config_free(wpa_s->conf); +} + + +static void send_eap_request_identity(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + u8 buf[100], *pos; + struct ieee802_1x_hdr *hdr; + struct eap_hdr *eap; + + hdr = (struct ieee802_1x_hdr *) buf; + hdr->version = EAPOL_VERSION; + hdr->type = IEEE802_1X_TYPE_EAP_PACKET; + hdr->length = htons(5); + + eap = (struct eap_hdr *) (hdr + 1); + eap->code = EAP_CODE_REQUEST; + eap->identifier = 0; + eap->length = htons(5); + pos = (u8 *) (eap + 1); + *pos = EAP_TYPE_IDENTITY; + + printf("Sending fake EAP-Request-Identity\n"); + eapol_sm_rx_eapol(wpa_s->eapol, wpa_s->bssid, buf, + sizeof(*hdr) + 5); +} + + +static void eapol_test_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + printf("EAPOL test timed out\n"); + wpa_s->auth_timed_out = 1; + eloop_terminate(); +} + + +static char *eap_type_text(u8 type) +{ + switch (type) { + case EAP_TYPE_IDENTITY: return "Identity"; + case EAP_TYPE_NOTIFICATION: return "Notification"; + case EAP_TYPE_NAK: return "Nak"; + case EAP_TYPE_TLS: return "TLS"; + case EAP_TYPE_TTLS: return "TTLS"; + case EAP_TYPE_PEAP: return "PEAP"; + case EAP_TYPE_SIM: return "SIM"; + case EAP_TYPE_GTC: return "GTC"; + case EAP_TYPE_MD5: return "MD5"; + case EAP_TYPE_OTP: return "OTP"; + default: return "Unknown"; + } +} + + +static void ieee802_1x_decapsulate_radius(struct wpa_supplicant *wpa_s) +{ + u8 *eap; + size_t len; + struct eap_hdr *hdr; + int eap_type = -1; + char buf[64]; + struct radius_msg *msg; + + if (wpa_s->last_recv_radius == NULL) + return; + + msg = wpa_s->last_recv_radius; + + eap = radius_msg_get_eap(msg, &len); + if (eap == NULL) { + /* draft-aboba-radius-rfc2869bis-20.txt, Chap. 2.6.3: + * RADIUS server SHOULD NOT send Access-Reject/no EAP-Message + * attribute */ + wpa_printf(MSG_DEBUG, "could not extract " + "EAP-Message from RADIUS message"); + free(wpa_s->last_eap_radius); + wpa_s->last_eap_radius = NULL; + wpa_s->last_eap_radius_len = 0; + return; + } + + if (len < sizeof(*hdr)) { + wpa_printf(MSG_DEBUG, "too short EAP packet " + "received from authentication server"); + free(eap); + return; + } + + if (len > sizeof(*hdr)) + eap_type = eap[sizeof(*hdr)]; + + hdr = (struct eap_hdr *) eap; + switch (hdr->code) { + case EAP_CODE_REQUEST: + snprintf(buf, sizeof(buf), "EAP-Request-%s (%d)", + eap_type >= 0 ? eap_type_text(eap_type) : "??", + eap_type); + break; + case EAP_CODE_RESPONSE: + snprintf(buf, sizeof(buf), "EAP Response-%s (%d)", + eap_type >= 0 ? eap_type_text(eap_type) : "??", + eap_type); + break; + case EAP_CODE_SUCCESS: + snprintf(buf, sizeof(buf), "EAP Success"); + /* LEAP uses EAP Success within an authentication, so must not + * stop here with eloop_terminate(); */ + break; + case EAP_CODE_FAILURE: + snprintf(buf, sizeof(buf), "EAP Failure"); + eloop_terminate(); + break; + default: + snprintf(buf, sizeof(buf), "unknown EAP code"); + wpa_hexdump(MSG_DEBUG, "Decapsulated EAP packet", eap, len); + break; + } + wpa_printf(MSG_DEBUG, "decapsulated EAP packet (code=%d " + "id=%d len=%d) from RADIUS server: %s", + hdr->code, hdr->identifier, ntohs(hdr->length), buf); + + /* sta->eapol_sm->be_auth.idFromServer = hdr->identifier; */ + + if (wpa_s->last_eap_radius) + free(wpa_s->last_eap_radius); + wpa_s->last_eap_radius = eap; + wpa_s->last_eap_radius_len = len; + + { + struct ieee802_1x_hdr *hdr; + hdr = malloc(sizeof(*hdr) + len); + assert(hdr != NULL); + hdr->version = EAPOL_VERSION; + hdr->type = IEEE802_1X_TYPE_EAP_PACKET; + hdr->length = htons(len); + memcpy((u8 *) (hdr + 1), eap, len); + eapol_sm_rx_eapol(wpa_s->eapol, wpa_s->bssid, + (u8 *) hdr, sizeof(*hdr) + len); + free(hdr); + } +} + + +static void ieee802_1x_get_keys(struct wpa_supplicant *wpa_s, + struct radius_msg *msg, struct radius_msg *req, + u8 *shared_secret, size_t shared_secret_len) +{ + struct radius_ms_mppe_keys *keys; + + keys = radius_msg_get_ms_keys(msg, req, shared_secret, + shared_secret_len); + if (keys && keys->send == NULL && keys->recv == NULL) { + free(keys); + keys = radius_msg_get_cisco_keys(msg, req, shared_secret, + shared_secret_len); + } + + if (keys) { + if (keys->send) { + wpa_hexdump(MSG_DEBUG, "MS-MPPE-Send-Key (sign)", + keys->send, keys->send_len); + } + if (keys->recv) { + wpa_hexdump(MSG_DEBUG, "MS-MPPE-Recv-Key (crypt)", + keys->recv, keys->recv_len); + wpa_s->authenticator_pmk_len = + keys->recv_len > PMK_LEN ? PMK_LEN : + keys->recv_len; + memcpy(wpa_s->authenticator_pmk, keys->recv, + wpa_s->authenticator_pmk_len); + } + + free(keys->send); + free(keys->recv); + free(keys); + } +} + + +/* Process the RADIUS frames from Authentication Server */ +static RadiusRxResult +ieee802_1x_receive_auth(struct wpa_supplicant *wpa_s, + struct radius_msg *msg, struct radius_msg *req, + u8 *shared_secret, size_t shared_secret_len, + void *data) +{ + /* RFC 2869, Ch. 5.13: valid Message-Authenticator attribute MUST be + * present when packet contains an EAP-Message attribute */ + if (msg->hdr->code == RADIUS_CODE_ACCESS_REJECT && + radius_msg_get_attr(msg, RADIUS_ATTR_MESSAGE_AUTHENTICATOR, NULL, + 0) < 0 && + radius_msg_get_attr(msg, RADIUS_ATTR_EAP_MESSAGE, NULL, 0) < 0) { + wpa_printf(MSG_DEBUG, "Allowing RADIUS " + "Access-Reject without Message-Authenticator " + "since it does not include EAP-Message\n"); + } else if (radius_msg_verify(msg, shared_secret, shared_secret_len, + req)) { + printf("Incoming RADIUS packet did not have correct " + "Message-Authenticator - dropped\n"); + return RADIUS_RX_UNKNOWN; + } + + if (msg->hdr->code != RADIUS_CODE_ACCESS_ACCEPT && + msg->hdr->code != RADIUS_CODE_ACCESS_REJECT && + msg->hdr->code != RADIUS_CODE_ACCESS_CHALLENGE) { + printf("Unknown RADIUS message code\n"); + return RADIUS_RX_UNKNOWN; + } + + wpa_s->radius_identifier = -1; + wpa_printf(MSG_DEBUG, "RADIUS packet matching with station"); + + if (wpa_s->last_recv_radius) { + radius_msg_free(wpa_s->last_recv_radius); + free(wpa_s->last_recv_radius); + } + + wpa_s->last_recv_radius = msg; + + switch (msg->hdr->code) { + case RADIUS_CODE_ACCESS_ACCEPT: + wpa_s->radius_access_accept_received = 1; + ieee802_1x_get_keys(wpa_s, msg, req, shared_secret, + shared_secret_len); + break; + case RADIUS_CODE_ACCESS_REJECT: + wpa_s->radius_access_reject_received = 1; + break; + } + + ieee802_1x_decapsulate_radius(wpa_s); + + if ((msg->hdr->code == RADIUS_CODE_ACCESS_ACCEPT && + eapol_test_num_reauths < 0) || + msg->hdr->code == RADIUS_CODE_ACCESS_REJECT) { + eloop_terminate(); + } + + return RADIUS_RX_QUEUED; +} + + +static void wpa_supplicant_imsi_identity(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid) +{ + int aka = 0; + u8 *pos = ssid->eap_methods; + + while (pos && *pos != EAP_TYPE_NONE) { + if (*pos == EAP_TYPE_AKA) { + aka = 1; + break; + } + pos++; + } + + if (ssid->identity == NULL && wpa_s->imsi) { + ssid->identity = malloc(1 + wpa_s->imsi_len); + if (ssid->identity) { + ssid->identity[0] = aka ? '0' : '1'; + memcpy(ssid->identity + 1, wpa_s->imsi, + wpa_s->imsi_len); + ssid->identity_len = 1 + wpa_s->imsi_len; + wpa_hexdump_ascii(MSG_DEBUG, "permanent identity from " + "IMSI", ssid->identity, + ssid->identity_len); + } + } +} + + +static void wpa_supplicant_scard_init(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid) +{ + char buf[100]; + size_t len; + + if (ssid->pcsc == NULL) + return; + if (wpa_s->scard != NULL) { + wpa_supplicant_imsi_identity(wpa_s, ssid); + return; + } + wpa_printf(MSG_DEBUG, "Selected network is configured to use SIM - " + "initialize PCSC"); + wpa_s->scard = scard_init(SCARD_TRY_BOTH, ssid->pin); + if (wpa_s->scard == NULL) { + wpa_printf(MSG_WARNING, "Failed to initialize SIM " + "(pcsc-lite)"); + /* TODO: what to do here? */ + return; + } + eapol_sm_register_scard_ctx(wpa_s->eapol, wpa_s->scard); + + len = sizeof(buf); + if (scard_get_imsi(wpa_s->scard, buf, &len)) { + wpa_printf(MSG_WARNING, "Failed to get IMSI from SIM"); + /* TODO: what to do here? */ + return; + } + + wpa_hexdump(MSG_DEBUG, "IMSI", (u8 *) buf, len); + free(wpa_s->imsi); + wpa_s->imsi = malloc(len); + if (wpa_s->imsi) { + memcpy(wpa_s->imsi, buf, len); + wpa_s->imsi_len = len; + wpa_supplicant_imsi_identity(wpa_s, ssid); + } +} + + +static void wpa_init_conf(struct wpa_supplicant *wpa_s, const char *authsrv, + int port, const char *secret) +{ + struct hostapd_radius_server *as; + int res; + + wpa_s->bssid[5] = 1; + wpa_s->own_addr[5] = 2; + wpa_s->own_ip_addr.s_addr = htonl((127 << 24) | 1); + strncpy(wpa_s->ifname, "test", sizeof(wpa_s->ifname)); + + wpa_s->num_auth_servers = 1; + as = malloc(sizeof(struct hostapd_radius_server)); + assert(as != NULL); + inet_aton(authsrv, &as->addr); + as->port = port; + as->shared_secret = (u8 *) strdup(secret); + as->shared_secret_len = strlen(secret); + wpa_s->auth_server = wpa_s->auth_servers = as; + + + res = radius_client_init(wpa_s); + assert(res == 0); + + res = radius_client_register(wpa_s, RADIUS_AUTH, + ieee802_1x_receive_auth, NULL); + assert(res == 0); +} + + +static int scard_test(void) +{ + struct scard_data *scard; + size_t len; + char imsi[20]; + unsigned char rand[16]; +#ifdef PCSC_FUNCS + unsigned char sres[4]; + unsigned char kc[8]; +#endif /* PCSC_FUNCS */ +#define num_triplets 5 + unsigned char rand_[num_triplets][16]; + unsigned char sres_[num_triplets][4]; + unsigned char kc_[num_triplets][8]; + int i, j, res; + +#define AKA_RAND_LEN 16 +#define AKA_AUTN_LEN 16 +#define AKA_AUTS_LEN 14 +#define RES_MAX_LEN 16 +#define IK_LEN 16 +#define CK_LEN 16 + unsigned char aka_rand[AKA_RAND_LEN]; + unsigned char aka_autn[AKA_AUTN_LEN]; + unsigned char aka_auts[AKA_AUTS_LEN]; + unsigned char aka_res[RES_MAX_LEN]; + size_t aka_res_len; + unsigned char aka_ik[IK_LEN]; + unsigned char aka_ck[CK_LEN]; + + scard = scard_init(SCARD_TRY_BOTH, "1234"); + if (scard == NULL) + return -1; + + len = sizeof(imsi); + if (scard_get_imsi(scard, imsi, &len)) + goto failed; + wpa_hexdump_ascii(MSG_DEBUG, "SCARD: IMSI", (u8 *) imsi, len); + /* NOTE: Permanent Username: 1 | IMSI */ + + memset(rand, 0, sizeof(rand)); + if (scard_gsm_auth(scard, rand, sres, kc)) + goto failed; + + memset(rand, 0xff, sizeof(rand)); + if (scard_gsm_auth(scard, rand, sres, kc)) + goto failed; + + for (i = 0; i < num_triplets; i++) { + memset(rand_[i], i, sizeof(rand_[i])); + if (scard_gsm_auth(scard, rand_[i], sres_[i], kc_[i])) + goto failed; + } + + for (i = 0; i < num_triplets; i++) { + printf("1"); + for (j = 0; j < len; j++) + printf("%c", imsi[j]); + printf(","); + for (j = 0; j < 16; j++) + printf("%02X", rand_[i][j]); + printf(","); + for (j = 0; j < 4; j++) + printf("%02X", sres_[i][j]); + printf(","); + for (j = 0; j < 8; j++) + printf("%02X", kc_[i][j]); + printf("\n"); + } + + wpa_printf(MSG_DEBUG, "Trying to use UMTS authentication"); + + /* seq 39 (0x28) */ + memset(aka_rand, 0xaa, 16); + memcpy(aka_autn, "\x86\x71\x31\xcb\xa2\xfc\x61\xdf" + "\xa3\xb3\x97\x9d\x07\x32\xa2\x12", 16); + + res = scard_umts_auth(scard, aka_rand, aka_autn, aka_res, &aka_res_len, + aka_ik, aka_ck, aka_auts); + if (res == 0) { + wpa_printf(MSG_DEBUG, "UMTS auth completed successfully"); + wpa_hexdump(MSG_DEBUG, "RES", aka_res, aka_res_len); + wpa_hexdump(MSG_DEBUG, "IK", aka_ik, IK_LEN); + wpa_hexdump(MSG_DEBUG, "CK", aka_ck, CK_LEN); + } else if (res == -2) { + wpa_printf(MSG_DEBUG, "UMTS auth resulted in synchronization " + "failure"); + wpa_hexdump(MSG_DEBUG, "AUTS", aka_auts, AKA_AUTS_LEN); + } else { + wpa_printf(MSG_DEBUG, "UMTS auth failed"); + } + +failed: + scard_deinit(scard); + + return 0; +#undef num_triplets +} + + +static int scard_get_triplets(int argc, char *argv[]) +{ + struct scard_data *scard; + size_t len; + char imsi[20]; + unsigned char rand[16]; + unsigned char sres[4]; + unsigned char kc[8]; + int num_triplets; + int i, j; + + if (argc < 2 || ((num_triplets = atoi(argv[1])) <= 0)) { + printf("invalid parameters for sim command\n"); + return -1; + } + + if (argc <= 2 || strcmp(argv[2], "debug") != 0) { + /* disable debug output */ + wpa_debug_level = 99; + } + + scard = scard_init(SCARD_GSM_SIM_ONLY, argv[0]); + if (scard == NULL) { + printf("Failed to open smartcard connection\n"); + return -1; + } + + len = sizeof(imsi); + if (scard_get_imsi(scard, imsi, &len)) { + scard_deinit(scard); + return -1; + } + + for (i = 0; i < num_triplets; i++) { + memset(rand, i, sizeof(rand)); + if (scard_gsm_auth(scard, rand, sres, kc)) + break; + + /* IMSI:Kc:SRES:RAND */ + for (j = 0; j < len; j++) + printf("%c", imsi[j]); + printf(":"); + for (j = 0; j < 8; j++) + printf("%02X", kc[j]); + printf(":"); + for (j = 0; j < 4; j++) + printf("%02X", sres[j]); + printf(":"); + for (j = 0; j < 16; j++) + printf("%02X", rand[j]); + printf("\n"); + } + + scard_deinit(scard); + + return 0; +} + + +static void eapol_test_terminate(int sig, void *eloop_ctx, + void *signal_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + wpa_msg(wpa_s, MSG_INFO, "Signal %d received - terminating", sig); + eloop_terminate(); +} + + +static void usage(void) +{ + printf("usage:\n" + "eapol_test [-n] -c<conf> [-a<AS IP>] [-p<AS port>] " + "[-s<AS secret>] [-r<count>]\n" + "eapol_test scard\n" + "eapol_test sim <PIN> <num triplets> [debug]\n" + "\n" + "options:\n" + " -c<conf> = configuration file\n" + " -a<AS IP> = IP address of the authentication server, " + "default 127.0.0.1\n" + " -p<AS port> = UDP port of the authentication server, " + "default 1812\n" + " -s<AS secret> = shared secret with the authentication " + "server, default 'radius'\n" + " -r<count> = number of re-authentications\n" + " -n = no MPPE keys expected\n"); +} + + +int main(int argc, char *argv[]) +{ + struct wpa_supplicant wpa_s; + int c, ret = 1; + char *as_addr = "127.0.0.1"; + int as_port = 1812; + char *as_secret = "radius"; + char *conf = NULL; + + wpa_debug_level = 0; + wpa_debug_show_keys = 1; + + for (;;) { + c = getopt(argc, argv, "a:c:np:r:s:"); + if (c < 0) + break; + switch (c) { + case 'a': + as_addr = optarg; + break; + case 'c': + conf = optarg; + break; + case 'n': + no_mppe_keys++; + break; + case 'p': + as_port = atoi(optarg); + break; + case 'r': + eapol_test_num_reauths = atoi(optarg); + break; + case 's': + as_secret = optarg; + break; + default: + usage(); + return -1; + } + } + + if (argc > optind && strcmp(argv[optind], "scard") == 0) { + return scard_test(); + } + + if (argc > optind && strcmp(argv[optind], "sim") == 0) { + return scard_get_triplets(argc - optind - 1, + &argv[optind + 1]); + } + + if (conf == NULL) { + usage(); + printf("Configuration file is required.\n"); + return -1; + } + + eloop_init(&wpa_s); + + memset(&wpa_s, 0, sizeof(wpa_s)); + wpa_s.conf = wpa_config_read(conf); + if (wpa_s.conf == NULL) { + printf("Failed to parse configuration file '%s'.\n", conf); + return -1; + } + if (wpa_s.conf->ssid == NULL) { + printf("No networks defined.\n"); + return -1; + } + + wpa_init_conf(&wpa_s, as_addr, as_port, as_secret); + if (wpa_supplicant_ctrl_iface_init(&wpa_s)) { + printf("Failed to initialize control interface '%s'.\n" + "You may have another eapol_test process already " + "running or the file was\n" + "left by an unclean termination of eapol_test in " + "which case you will need\n" + "to manually remove this file before starting " + "eapol_test again.\n", + wpa_s.conf->ctrl_interface); + return -1; + } + wpa_supplicant_scard_init(&wpa_s, wpa_s.conf->ssid); + + if (test_eapol(&wpa_s, wpa_s.conf->ssid)) + return -1; + + eloop_register_timeout(30, 0, eapol_test_timeout, &wpa_s, NULL); + eloop_register_timeout(0, 0, send_eap_request_identity, &wpa_s, NULL); + eloop_register_signal(SIGINT, eapol_test_terminate, NULL); + eloop_register_signal(SIGTERM, eapol_test_terminate, NULL); + eloop_register_signal(SIGHUP, eapol_test_terminate, NULL); + eloop_run(); + + if (eapol_test_compare_pmk(&wpa_s) == 0) + ret = 0; + if (wpa_s.auth_timed_out) + ret = -2; + if (wpa_s.radius_access_reject_received) + ret = -3; + + test_eapol_clean(&wpa_s); + + eloop_destroy(); + + printf("MPPE keys OK: %d mismatch: %d\n", + num_mppe_ok, num_mppe_mismatch); + if (num_mppe_mismatch) + ret = -4; + if (ret) + printf("FAILURE\n"); + else + printf("SUCCESS\n"); + + return ret; +} diff --git a/contrib/wpa_supplicant/eloop.c b/contrib/wpa_supplicant/eloop.c new file mode 100644 index 0000000..6071508 --- /dev/null +++ b/contrib/wpa_supplicant/eloop.c @@ -0,0 +1,380 @@ +/* + * Event loop + * Copyright (c) 2002-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <sys/time.h> +#include <sys/types.h> +#include <unistd.h> +#include <errno.h> +#include <signal.h> + +#ifdef CONFIG_NATIVE_WINDOWS +#include "common.h" +#endif /* CONFIG_NATIVE_WINDOWS */ + +#include "eloop.h" + + +struct eloop_sock { + int sock; + void *eloop_data; + void *user_data; + void (*handler)(int sock, void *eloop_ctx, void *sock_ctx); +}; + +struct eloop_timeout { + struct timeval time; + void *eloop_data; + void *user_data; + void (*handler)(void *eloop_ctx, void *sock_ctx); + struct eloop_timeout *next; +}; + +struct eloop_signal { + int sig; + void *user_data; + void (*handler)(int sig, void *eloop_ctx, void *signal_ctx); + int signaled; +}; + +struct eloop_data { + void *user_data; + + int max_sock, reader_count; + struct eloop_sock *readers; + + struct eloop_timeout *timeout; + + int signal_count; + struct eloop_signal *signals; + int signaled; + int pending_terminate; + + int terminate; +}; + +static struct eloop_data eloop; + + +void eloop_init(void *user_data) +{ + memset(&eloop, 0, sizeof(eloop)); + eloop.user_data = user_data; +} + + +int eloop_register_read_sock(int sock, + void (*handler)(int sock, void *eloop_ctx, + void *sock_ctx), + void *eloop_data, void *user_data) +{ + struct eloop_sock *tmp; + + tmp = (struct eloop_sock *) + realloc(eloop.readers, + (eloop.reader_count + 1) * sizeof(struct eloop_sock)); + if (tmp == NULL) + return -1; + + tmp[eloop.reader_count].sock = sock; + tmp[eloop.reader_count].eloop_data = eloop_data; + tmp[eloop.reader_count].user_data = user_data; + tmp[eloop.reader_count].handler = handler; + eloop.reader_count++; + eloop.readers = tmp; + if (sock > eloop.max_sock) + eloop.max_sock = sock; + + return 0; +} + + +void eloop_unregister_read_sock(int sock) +{ + int i; + + if (eloop.readers == NULL || eloop.reader_count == 0) + return; + + for (i = 0; i < eloop.reader_count; i++) { + if (eloop.readers[i].sock == sock) + break; + } + if (i == eloop.reader_count) + return; + if (i != eloop.reader_count - 1) { + memmove(&eloop.readers[i], &eloop.readers[i + 1], + (eloop.reader_count - i - 1) * + sizeof(struct eloop_sock)); + } + eloop.reader_count--; +} + + +int eloop_register_timeout(unsigned int secs, unsigned int usecs, + void (*handler)(void *eloop_ctx, void *timeout_ctx), + void *eloop_data, void *user_data) +{ + struct eloop_timeout *timeout, *tmp, *prev; + + timeout = (struct eloop_timeout *) malloc(sizeof(*timeout)); + if (timeout == NULL) + return -1; + gettimeofday(&timeout->time, NULL); + timeout->time.tv_sec += secs; + timeout->time.tv_usec += usecs; + while (timeout->time.tv_usec >= 1000000) { + timeout->time.tv_sec++; + timeout->time.tv_usec -= 1000000; + } + timeout->eloop_data = eloop_data; + timeout->user_data = user_data; + timeout->handler = handler; + timeout->next = NULL; + + if (eloop.timeout == NULL) { + eloop.timeout = timeout; + return 0; + } + + prev = NULL; + tmp = eloop.timeout; + while (tmp != NULL) { + if (timercmp(&timeout->time, &tmp->time, <)) + break; + prev = tmp; + tmp = tmp->next; + } + + if (prev == NULL) { + timeout->next = eloop.timeout; + eloop.timeout = timeout; + } else { + timeout->next = prev->next; + prev->next = timeout; + } + + return 0; +} + + +int eloop_cancel_timeout(void (*handler)(void *eloop_ctx, void *sock_ctx), + void *eloop_data, void *user_data) +{ + struct eloop_timeout *timeout, *prev, *next; + int removed = 0; + + prev = NULL; + timeout = eloop.timeout; + while (timeout != NULL) { + next = timeout->next; + + if (timeout->handler == handler && + (timeout->eloop_data == eloop_data || + eloop_data == ELOOP_ALL_CTX) && + (timeout->user_data == user_data || + user_data == ELOOP_ALL_CTX)) { + if (prev == NULL) + eloop.timeout = next; + else + prev->next = next; + free(timeout); + removed++; + } else + prev = timeout; + + timeout = next; + } + + return removed; +} + + +#ifndef CONFIG_NATIVE_WINDOWS +static void eloop_handle_alarm(int sig) +{ + fprintf(stderr, "eloop: could not process SIGINT or SIGTERM in two " + "seconds. Looks like there\n" + "is a bug that ends up in a busy loop that " + "prevents clean shutdown.\n" + "Killing program forcefully.\n"); + exit(1); +} +#endif /* CONFIG_NATIVE_WINDOWS */ + + +static void eloop_handle_signal(int sig) +{ + int i; + +#ifndef CONFIG_NATIVE_WINDOWS + if ((sig == SIGINT || sig == SIGTERM) && !eloop.pending_terminate) { + /* Use SIGALRM to break out from potential busy loops that + * would not allow the program to be killed. */ + eloop.pending_terminate = 1; + signal(SIGALRM, eloop_handle_alarm); + alarm(2); + } +#endif /* CONFIG_NATIVE_WINDOWS */ + + eloop.signaled++; + for (i = 0; i < eloop.signal_count; i++) { + if (eloop.signals[i].sig == sig) { + eloop.signals[i].signaled++; + break; + } + } +} + + +static void eloop_process_pending_signals(void) +{ + int i; + + if (eloop.signaled == 0) + return; + eloop.signaled = 0; + + if (eloop.pending_terminate) { +#ifndef CONFIG_NATIVE_WINDOWS + alarm(0); +#endif /* CONFIG_NATIVE_WINDOWS */ + eloop.pending_terminate = 0; + } + + for (i = 0; i < eloop.signal_count; i++) { + if (eloop.signals[i].signaled) { + eloop.signals[i].signaled = 0; + eloop.signals[i].handler(eloop.signals[i].sig, + eloop.user_data, + eloop.signals[i].user_data); + } + } +} + + +int eloop_register_signal(int sig, + void (*handler)(int sig, void *eloop_ctx, + void *signal_ctx), + void *user_data) +{ + struct eloop_signal *tmp; + + tmp = (struct eloop_signal *) + realloc(eloop.signals, + (eloop.signal_count + 1) * + sizeof(struct eloop_signal)); + if (tmp == NULL) + return -1; + + tmp[eloop.signal_count].sig = sig; + tmp[eloop.signal_count].user_data = user_data; + tmp[eloop.signal_count].handler = handler; + tmp[eloop.signal_count].signaled = 0; + eloop.signal_count++; + eloop.signals = tmp; + signal(sig, eloop_handle_signal); + + return 0; +} + + +void eloop_run(void) +{ + fd_set rfds; + int i, res; + struct timeval tv, now; + + while (!eloop.terminate && + (eloop.timeout || eloop.reader_count > 0)) { + if (eloop.timeout) { + gettimeofday(&now, NULL); + if (timercmp(&now, &eloop.timeout->time, <)) + timersub(&eloop.timeout->time, &now, &tv); + else + tv.tv_sec = tv.tv_usec = 0; +#if 0 + printf("next timeout in %lu.%06lu sec\n", + tv.tv_sec, tv.tv_usec); +#endif + } + + FD_ZERO(&rfds); + for (i = 0; i < eloop.reader_count; i++) + FD_SET(eloop.readers[i].sock, &rfds); + res = select(eloop.max_sock + 1, &rfds, NULL, NULL, + eloop.timeout ? &tv : NULL); + if (res < 0 && errno != EINTR) { + perror("select"); + return; + } + eloop_process_pending_signals(); + + /* check if some registered timeouts have occurred */ + if (eloop.timeout) { + struct eloop_timeout *tmp; + + gettimeofday(&now, NULL); + if (!timercmp(&now, &eloop.timeout->time, <)) { + tmp = eloop.timeout; + eloop.timeout = eloop.timeout->next; + tmp->handler(tmp->eloop_data, + tmp->user_data); + free(tmp); + } + + } + + if (res <= 0) + continue; + + for (i = 0; i < eloop.reader_count; i++) { + if (FD_ISSET(eloop.readers[i].sock, &rfds)) { + eloop.readers[i].handler( + eloop.readers[i].sock, + eloop.readers[i].eloop_data, + eloop.readers[i].user_data); + } + } + } +} + + +void eloop_terminate(void) +{ + eloop.terminate = 1; +} + + +void eloop_destroy(void) +{ + struct eloop_timeout *timeout, *prev; + + timeout = eloop.timeout; + while (timeout != NULL) { + prev = timeout; + timeout = timeout->next; + free(prev); + } + free(eloop.readers); + free(eloop.signals); +} + + +int eloop_terminated(void) +{ + return eloop.terminate; +} diff --git a/contrib/wpa_supplicant/eloop.h b/contrib/wpa_supplicant/eloop.h new file mode 100644 index 0000000..f5b8847 --- /dev/null +++ b/contrib/wpa_supplicant/eloop.h @@ -0,0 +1,53 @@ +#ifndef ELOOP_H +#define ELOOP_H + +/* Magic number for eloop_cancel_timeout() */ +#define ELOOP_ALL_CTX (void *) -1 + +/* Initialize global event loop data - must be called before any other eloop_* + * function. user_data is a pointer to global data structure and will be passed + * as eloop_ctx to signal handlers. */ +void eloop_init(void *user_data); + +/* Register handler for read event */ +int eloop_register_read_sock(int sock, + void (*handler)(int sock, void *eloop_ctx, + void *sock_ctx), + void *eloop_data, void *user_data); +void eloop_unregister_read_sock(int sock); + +/* Register timeout */ +int eloop_register_timeout(unsigned int secs, unsigned int usecs, + void (*handler)(void *eloop_ctx, void *timeout_ctx), + void *eloop_data, void *user_data); + +/* Cancel timeouts matching <handler,eloop_data,user_data>. + * ELOOP_ALL_CTX can be used as a wildcard for cancelling all timeouts + * regardless of eloop_data/user_data. */ +int eloop_cancel_timeout(void (*handler)(void *eloop_ctx, void *sock_ctx), + void *eloop_data, void *user_data); + +/* Register handler for signal. + * Note: signals are 'global' events and there is no local eloop_data pointer + * like with other handlers. The (global) pointer given to eloop_init() will be + * used as eloop_ctx for signal handlers. */ +int eloop_register_signal(int sock, + void (*handler)(int sig, void *eloop_ctx, + void *signal_ctx), + void *user_data); + +/* Start event loop and continue running as long as there are any registered + * event handlers. */ +void eloop_run(void); + +/* Terminate event loop even if there are registered events. */ +void eloop_terminate(void); + +/* Free any reserved resources. After calling eloop_destoy(), other eloop_* + * functions must not be called before re-running eloop_init(). */ +void eloop_destroy(void); + +/* Check whether event loop has been terminated. */ +int eloop_terminated(void); + +#endif /* ELOOP_H */ diff --git a/contrib/wpa_supplicant/hostap_common.h b/contrib/wpa_supplicant/hostap_common.h new file mode 100644 index 0000000..003ad9a --- /dev/null +++ b/contrib/wpa_supplicant/hostap_common.h @@ -0,0 +1,557 @@ +#ifndef HOSTAP_COMMON_H +#define HOSTAP_COMMON_H + +#define BIT(x) (1 << (x)) + +#define MAC2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5] +#define MACSTR "%02x:%02x:%02x:%02x:%02x:%02x" + + +#ifndef ETH_P_PAE +#define ETH_P_PAE 0x888E /* Port Access Entity (IEEE 802.1X) */ +#endif /* ETH_P_PAE */ + +#define ETH_P_PREAUTH 0x88C7 /* IEEE 802.11i pre-authentication */ + + + +/* IEEE 802.11 defines */ + +#define WLAN_FC_PVER (BIT(1) | BIT(0)) +#define WLAN_FC_TODS BIT(8) +#define WLAN_FC_FROMDS BIT(9) +#define WLAN_FC_MOREFRAG BIT(10) +#define WLAN_FC_RETRY BIT(11) +#define WLAN_FC_PWRMGT BIT(12) +#define WLAN_FC_MOREDATA BIT(13) +#define WLAN_FC_ISWEP BIT(14) +#define WLAN_FC_ORDER BIT(15) + +#define WLAN_FC_GET_TYPE(fc) (((fc) & (BIT(3) | BIT(2))) >> 2) +#define WLAN_FC_GET_STYPE(fc) \ + (((fc) & (BIT(7) | BIT(6) | BIT(5) | BIT(4))) >> 4) + +#define WLAN_GET_SEQ_FRAG(seq) ((seq) & (BIT(3) | BIT(2) | BIT(1) | BIT(0))) +#define WLAN_GET_SEQ_SEQ(seq) \ + (((seq) & (~(BIT(3) | BIT(2) | BIT(1) | BIT(0)))) >> 4) + +#define WLAN_FC_TYPE_MGMT 0 +#define WLAN_FC_TYPE_CTRL 1 +#define WLAN_FC_TYPE_DATA 2 + +/* management */ +#define WLAN_FC_STYPE_ASSOC_REQ 0 +#define WLAN_FC_STYPE_ASSOC_RESP 1 +#define WLAN_FC_STYPE_REASSOC_REQ 2 +#define WLAN_FC_STYPE_REASSOC_RESP 3 +#define WLAN_FC_STYPE_PROBE_REQ 4 +#define WLAN_FC_STYPE_PROBE_RESP 5 +#define WLAN_FC_STYPE_BEACON 8 +#define WLAN_FC_STYPE_ATIM 9 +#define WLAN_FC_STYPE_DISASSOC 10 +#define WLAN_FC_STYPE_AUTH 11 +#define WLAN_FC_STYPE_DEAUTH 12 + +/* control */ +#define WLAN_FC_STYPE_PSPOLL 10 +#define WLAN_FC_STYPE_RTS 11 +#define WLAN_FC_STYPE_CTS 12 +#define WLAN_FC_STYPE_ACK 13 +#define WLAN_FC_STYPE_CFEND 14 +#define WLAN_FC_STYPE_CFENDACK 15 + +/* data */ +#define WLAN_FC_STYPE_DATA 0 +#define WLAN_FC_STYPE_DATA_CFACK 1 +#define WLAN_FC_STYPE_DATA_CFPOLL 2 +#define WLAN_FC_STYPE_DATA_CFACKPOLL 3 +#define WLAN_FC_STYPE_NULLFUNC 4 +#define WLAN_FC_STYPE_CFACK 5 +#define WLAN_FC_STYPE_CFPOLL 6 +#define WLAN_FC_STYPE_CFACKPOLL 7 + +/* Authentication algorithms */ +#define WLAN_AUTH_OPEN 0 +#define WLAN_AUTH_SHARED_KEY 1 + +#define WLAN_AUTH_CHALLENGE_LEN 128 + +#define WLAN_CAPABILITY_ESS BIT(0) +#define WLAN_CAPABILITY_IBSS BIT(1) +#define WLAN_CAPABILITY_CF_POLLABLE BIT(2) +#define WLAN_CAPABILITY_CF_POLL_REQUEST BIT(3) +#define WLAN_CAPABILITY_PRIVACY BIT(4) + +/* Status codes */ +#define WLAN_STATUS_SUCCESS 0 +#define WLAN_STATUS_UNSPECIFIED_FAILURE 1 +#define WLAN_STATUS_CAPS_UNSUPPORTED 10 +#define WLAN_STATUS_REASSOC_NO_ASSOC 11 +#define WLAN_STATUS_ASSOC_DENIED_UNSPEC 12 +#define WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG 13 +#define WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION 14 +#define WLAN_STATUS_CHALLENGE_FAIL 15 +#define WLAN_STATUS_AUTH_TIMEOUT 16 +#define WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA 17 +#define WLAN_STATUS_ASSOC_DENIED_RATES 18 +/* 802.11b */ +#define WLAN_STATUS_ASSOC_DENIED_NOSHORT 19 +#define WLAN_STATUS_ASSOC_DENIED_NOPBCC 20 +#define WLAN_STATUS_ASSOC_DENIED_NOAGILITY 21 +/* IEEE 802.11i */ +#define WLAN_STATUS_INVALID_IE 40 +#define WLAN_STATUS_GROUP_CIPHER_NOT_VALID 41 +#define WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID 42 +#define WLAN_STATUS_AKMP_NOT_VALID 43 +#define WLAN_STATUS_UNSUPPORTED_RSN_IE_VERSION 44 +#define WLAN_STATUS_INVALID_RSN_IE_CAPAB 45 +#define WLAN_STATUS_CIPHER_REJECTED_PER_POLICY 46 + +/* Reason codes */ +#define WLAN_REASON_UNSPECIFIED 1 +#define WLAN_REASON_PREV_AUTH_NOT_VALID 2 +#define WLAN_REASON_DEAUTH_LEAVING 3 +#define WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY 4 +#define WLAN_REASON_DISASSOC_AP_BUSY 5 +#define WLAN_REASON_CLASS2_FRAME_FROM_NONAUTH_STA 6 +#define WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA 7 +#define WLAN_REASON_DISASSOC_STA_HAS_LEFT 8 +#define WLAN_REASON_STA_REQ_ASSOC_WITHOUT_AUTH 9 +/* IEEE 802.11i */ +#define WLAN_REASON_INVALID_IE 13 +#define WLAN_REASON_MICHAEL_MIC_FAILURE 14 +#define WLAN_REASON_4WAY_HANDSHAKE_TIMEOUT 15 +#define WLAN_REASON_GROUP_KEY_UPDATE_TIMEOUT 16 +#define WLAN_REASON_IE_IN_4WAY_DIFFERS 17 +#define WLAN_REASON_GROUP_CIPHER_NOT_VALID 18 +#define WLAN_REASON_PAIRWISE_CIPHER_NOT_VALID 19 +#define WLAN_REASON_AKMP_NOT_VALID 20 +#define WLAN_REASON_UNSUPPORTED_RSN_IE_VERSION 21 +#define WLAN_REASON_INVALID_RSN_IE_CAPAB 22 +#define WLAN_REASON_IEEE_802_1X_AUTH_FAILED 23 +#define WLAN_REASON_CIPHER_SUITE_REJECTED 24 + + +/* Information Element IDs */ +#define WLAN_EID_SSID 0 +#define WLAN_EID_SUPP_RATES 1 +#define WLAN_EID_FH_PARAMS 2 +#define WLAN_EID_DS_PARAMS 3 +#define WLAN_EID_CF_PARAMS 4 +#define WLAN_EID_TIM 5 +#define WLAN_EID_IBSS_PARAMS 6 +#define WLAN_EID_CHALLENGE 16 +#define WLAN_EID_RSN 48 +#define WLAN_EID_GENERIC 221 + + +/* HFA384X Configuration RIDs */ +#define HFA384X_RID_CNFPORTTYPE 0xFC00 +#define HFA384X_RID_CNFOWNMACADDR 0xFC01 +#define HFA384X_RID_CNFDESIREDSSID 0xFC02 +#define HFA384X_RID_CNFOWNCHANNEL 0xFC03 +#define HFA384X_RID_CNFOWNSSID 0xFC04 +#define HFA384X_RID_CNFOWNATIMWINDOW 0xFC05 +#define HFA384X_RID_CNFSYSTEMSCALE 0xFC06 +#define HFA384X_RID_CNFMAXDATALEN 0xFC07 +#define HFA384X_RID_CNFWDSADDRESS 0xFC08 +#define HFA384X_RID_CNFPMENABLED 0xFC09 +#define HFA384X_RID_CNFPMEPS 0xFC0A +#define HFA384X_RID_CNFMULTICASTRECEIVE 0xFC0B +#define HFA384X_RID_CNFMAXSLEEPDURATION 0xFC0C +#define HFA384X_RID_CNFPMHOLDOVERDURATION 0xFC0D +#define HFA384X_RID_CNFOWNNAME 0xFC0E +#define HFA384X_RID_CNFOWNDTIMPERIOD 0xFC10 +#define HFA384X_RID_CNFWDSADDRESS1 0xFC11 /* AP f/w only */ +#define HFA384X_RID_CNFWDSADDRESS2 0xFC12 /* AP f/w only */ +#define HFA384X_RID_CNFWDSADDRESS3 0xFC13 /* AP f/w only */ +#define HFA384X_RID_CNFWDSADDRESS4 0xFC14 /* AP f/w only */ +#define HFA384X_RID_CNFWDSADDRESS5 0xFC15 /* AP f/w only */ +#define HFA384X_RID_CNFWDSADDRESS6 0xFC16 /* AP f/w only */ +#define HFA384X_RID_CNFMULTICASTPMBUFFERING 0xFC17 /* AP f/w only */ +#define HFA384X_RID_UNKNOWN1 0xFC20 +#define HFA384X_RID_UNKNOWN2 0xFC21 +#define HFA384X_RID_CNFWEPDEFAULTKEYID 0xFC23 +#define HFA384X_RID_CNFDEFAULTKEY0 0xFC24 +#define HFA384X_RID_CNFDEFAULTKEY1 0xFC25 +#define HFA384X_RID_CNFDEFAULTKEY2 0xFC26 +#define HFA384X_RID_CNFDEFAULTKEY3 0xFC27 +#define HFA384X_RID_CNFWEPFLAGS 0xFC28 +#define HFA384X_RID_CNFWEPKEYMAPPINGTABLE 0xFC29 +#define HFA384X_RID_CNFAUTHENTICATION 0xFC2A +#define HFA384X_RID_CNFMAXASSOCSTA 0xFC2B /* AP f/w only */ +#define HFA384X_RID_CNFTXCONTROL 0xFC2C +#define HFA384X_RID_CNFROAMINGMODE 0xFC2D +#define HFA384X_RID_CNFHOSTAUTHENTICATION 0xFC2E /* AP f/w only */ +#define HFA384X_RID_CNFRCVCRCERROR 0xFC30 +#define HFA384X_RID_CNFMMLIFE 0xFC31 +#define HFA384X_RID_CNFALTRETRYCOUNT 0xFC32 +#define HFA384X_RID_CNFBEACONINT 0xFC33 +#define HFA384X_RID_CNFAPPCFINFO 0xFC34 /* AP f/w only */ +#define HFA384X_RID_CNFSTAPCFINFO 0xFC35 +#define HFA384X_RID_CNFPRIORITYQUSAGE 0xFC37 +#define HFA384X_RID_CNFTIMCTRL 0xFC40 +#define HFA384X_RID_UNKNOWN3 0xFC41 /* added in STA f/w 0.7.x */ +#define HFA384X_RID_CNFTHIRTY2TALLY 0xFC42 /* added in STA f/w 0.8.0 */ +#define HFA384X_RID_CNFENHSECURITY 0xFC43 /* AP f/w or STA f/w >= 1.6.3 */ +#define HFA384X_RID_CNFDBMADJUST 0xFC46 /* added in STA f/w 1.3.1 */ +#define HFA384X_RID_GENERICELEMENT 0xFC48 /* added in STA f/w 1.7.0; + * write only */ +#define HFA384X_RID_PROPAGATIONDELAY 0xFC49 /* added in STA f/w 1.7.6 */ +#define HFA384X_RID_GROUPADDRESSES 0xFC80 +#define HFA384X_RID_CREATEIBSS 0xFC81 +#define HFA384X_RID_FRAGMENTATIONTHRESHOLD 0xFC82 +#define HFA384X_RID_RTSTHRESHOLD 0xFC83 +#define HFA384X_RID_TXRATECONTROL 0xFC84 +#define HFA384X_RID_PROMISCUOUSMODE 0xFC85 +#define HFA384X_RID_FRAGMENTATIONTHRESHOLD0 0xFC90 /* AP f/w only */ +#define HFA384X_RID_FRAGMENTATIONTHRESHOLD1 0xFC91 /* AP f/w only */ +#define HFA384X_RID_FRAGMENTATIONTHRESHOLD2 0xFC92 /* AP f/w only */ +#define HFA384X_RID_FRAGMENTATIONTHRESHOLD3 0xFC93 /* AP f/w only */ +#define HFA384X_RID_FRAGMENTATIONTHRESHOLD4 0xFC94 /* AP f/w only */ +#define HFA384X_RID_FRAGMENTATIONTHRESHOLD5 0xFC95 /* AP f/w only */ +#define HFA384X_RID_FRAGMENTATIONTHRESHOLD6 0xFC96 /* AP f/w only */ +#define HFA384X_RID_RTSTHRESHOLD0 0xFC97 /* AP f/w only */ +#define HFA384X_RID_RTSTHRESHOLD1 0xFC98 /* AP f/w only */ +#define HFA384X_RID_RTSTHRESHOLD2 0xFC99 /* AP f/w only */ +#define HFA384X_RID_RTSTHRESHOLD3 0xFC9A /* AP f/w only */ +#define HFA384X_RID_RTSTHRESHOLD4 0xFC9B /* AP f/w only */ +#define HFA384X_RID_RTSTHRESHOLD5 0xFC9C /* AP f/w only */ +#define HFA384X_RID_RTSTHRESHOLD6 0xFC9D /* AP f/w only */ +#define HFA384X_RID_TXRATECONTROL0 0xFC9E /* AP f/w only */ +#define HFA384X_RID_TXRATECONTROL1 0xFC9F /* AP f/w only */ +#define HFA384X_RID_TXRATECONTROL2 0xFCA0 /* AP f/w only */ +#define HFA384X_RID_TXRATECONTROL3 0xFCA1 /* AP f/w only */ +#define HFA384X_RID_TXRATECONTROL4 0xFCA2 /* AP f/w only */ +#define HFA384X_RID_TXRATECONTROL5 0xFCA3 /* AP f/w only */ +#define HFA384X_RID_TXRATECONTROL6 0xFCA4 /* AP f/w only */ +#define HFA384X_RID_CNFSHORTPREAMBLE 0xFCB0 +#define HFA384X_RID_CNFEXCLUDELONGPREAMBLE 0xFCB1 +#define HFA384X_RID_CNFAUTHENTICATIONRSPTO 0xFCB2 +#define HFA384X_RID_CNFBASICRATES 0xFCB3 +#define HFA384X_RID_CNFSUPPORTEDRATES 0xFCB4 +#define HFA384X_RID_CNFFALLBACKCTRL 0xFCB5 /* added in STA f/w 1.3.1 */ +#define HFA384X_RID_WEPKEYDISABLE 0xFCB6 /* added in STA f/w 1.3.1 */ +#define HFA384X_RID_WEPKEYMAPINDEX 0xFCB7 /* ? */ +#define HFA384X_RID_BROADCASTKEYID 0xFCB8 /* ? */ +#define HFA384X_RID_ENTSECFLAGEYID 0xFCB9 /* ? */ +#define HFA384X_RID_CNFPASSIVESCANCTRL 0xFCBA /* added in STA f/w 1.5.0 */ +#define HFA384X_RID_SSNHANDLINGMODE 0xFCBB /* added in STA f/w 1.7.0 */ +#define HFA384X_RID_MDCCONTROL 0xFCBC /* added in STA f/w 1.7.0 */ +#define HFA384X_RID_MDCCOUNTRY 0xFCBD /* added in STA f/w 1.7.0 */ +#define HFA384X_RID_TXPOWERMAX 0xFCBE /* added in STA f/w 1.7.0 */ +#define HFA384X_RID_CNFLFOENABLED 0xFCBF /* added in STA f/w 1.6.3 */ +#define HFA384X_RID_CAPINFO 0xFCC0 /* added in STA f/w 1.7.0 */ +#define HFA384X_RID_LISTENINTERVAL 0xFCC1 /* added in STA f/w 1.7.0 */ +#define HFA384X_RID_SW_ANT_DIV 0xFCC2 /* added in STA f/w 1.7.0; Prism3 */ +#define HFA384X_RID_LED_CTRL 0xFCC4 /* added in STA f/w 1.7.6 */ +#define HFA384X_RID_HFODELAY 0xFCC5 /* added in STA f/w 1.7.6 */ +#define HFA384X_RID_DISALLOWEDBSSID 0xFCC6 /* added in STA f/w 1.8.0 */ +#define HFA384X_RID_TICKTIME 0xFCE0 +#define HFA384X_RID_SCANREQUEST 0xFCE1 +#define HFA384X_RID_JOINREQUEST 0xFCE2 +#define HFA384X_RID_AUTHENTICATESTATION 0xFCE3 /* AP f/w only */ +#define HFA384X_RID_CHANNELINFOREQUEST 0xFCE4 /* AP f/w only */ +#define HFA384X_RID_HOSTSCAN 0xFCE5 /* added in STA f/w 1.3.1 */ + +/* HFA384X Information RIDs */ +#define HFA384X_RID_MAXLOADTIME 0xFD00 +#define HFA384X_RID_DOWNLOADBUFFER 0xFD01 +#define HFA384X_RID_PRIID 0xFD02 +#define HFA384X_RID_PRISUPRANGE 0xFD03 +#define HFA384X_RID_CFIACTRANGES 0xFD04 +#define HFA384X_RID_NICSERNUM 0xFD0A +#define HFA384X_RID_NICID 0xFD0B +#define HFA384X_RID_MFISUPRANGE 0xFD0C +#define HFA384X_RID_CFISUPRANGE 0xFD0D +#define HFA384X_RID_CHANNELLIST 0xFD10 +#define HFA384X_RID_REGULATORYDOMAINS 0xFD11 +#define HFA384X_RID_TEMPTYPE 0xFD12 +#define HFA384X_RID_CIS 0xFD13 +#define HFA384X_RID_STAID 0xFD20 +#define HFA384X_RID_STASUPRANGE 0xFD21 +#define HFA384X_RID_MFIACTRANGES 0xFD22 +#define HFA384X_RID_CFIACTRANGES2 0xFD23 +#define HFA384X_RID_PRODUCTNAME 0xFD24 /* added in STA f/w 1.3.1; + * only Prism2.5(?) */ +#define HFA384X_RID_PORTSTATUS 0xFD40 +#define HFA384X_RID_CURRENTSSID 0xFD41 +#define HFA384X_RID_CURRENTBSSID 0xFD42 +#define HFA384X_RID_COMMSQUALITY 0xFD43 +#define HFA384X_RID_CURRENTTXRATE 0xFD44 +#define HFA384X_RID_CURRENTBEACONINTERVAL 0xFD45 +#define HFA384X_RID_CURRENTSCALETHRESHOLDS 0xFD46 +#define HFA384X_RID_PROTOCOLRSPTIME 0xFD47 +#define HFA384X_RID_SHORTRETRYLIMIT 0xFD48 +#define HFA384X_RID_LONGRETRYLIMIT 0xFD49 +#define HFA384X_RID_MAXTRANSMITLIFETIME 0xFD4A +#define HFA384X_RID_MAXRECEIVELIFETIME 0xFD4B +#define HFA384X_RID_CFPOLLABLE 0xFD4C +#define HFA384X_RID_AUTHENTICATIONALGORITHMS 0xFD4D +#define HFA384X_RID_PRIVACYOPTIONIMPLEMENTED 0xFD4F +#define HFA384X_RID_DBMCOMMSQUALITY 0xFD51 /* added in STA f/w 1.3.1 */ +#define HFA384X_RID_CURRENTTXRATE1 0xFD80 /* AP f/w only */ +#define HFA384X_RID_CURRENTTXRATE2 0xFD81 /* AP f/w only */ +#define HFA384X_RID_CURRENTTXRATE3 0xFD82 /* AP f/w only */ +#define HFA384X_RID_CURRENTTXRATE4 0xFD83 /* AP f/w only */ +#define HFA384X_RID_CURRENTTXRATE5 0xFD84 /* AP f/w only */ +#define HFA384X_RID_CURRENTTXRATE6 0xFD85 /* AP f/w only */ +#define HFA384X_RID_OWNMACADDR 0xFD86 /* AP f/w only */ +#define HFA384X_RID_SCANRESULTSTABLE 0xFD88 /* added in STA f/w 0.8.3 */ +#define HFA384X_RID_HOSTSCANRESULTS 0xFD89 /* added in STA f/w 1.3.1 */ +#define HFA384X_RID_AUTHENTICATIONUSED 0xFD8A /* added in STA f/w 1.3.4 */ +#define HFA384X_RID_CNFFAASWITCHCTRL 0xFD8B /* added in STA f/w 1.6.3 */ +#define HFA384X_RID_ASSOCIATIONFAILURE 0xFD8D /* added in STA f/w 1.8.0 */ +#define HFA384X_RID_PHYTYPE 0xFDC0 +#define HFA384X_RID_CURRENTCHANNEL 0xFDC1 +#define HFA384X_RID_CURRENTPOWERSTATE 0xFDC2 +#define HFA384X_RID_CCAMODE 0xFDC3 +#define HFA384X_RID_SUPPORTEDDATARATES 0xFDC6 +#define HFA384X_RID_LFO_VOLT_REG_TEST_RES 0xFDC7 /* added in STA f/w 1.7.1 */ +#define HFA384X_RID_BUILDSEQ 0xFFFE +#define HFA384X_RID_FWID 0xFFFF + + +struct hfa384x_comp_ident +{ + u16 id; + u16 variant; + u16 major; + u16 minor; +} __attribute__ ((packed)); + +#define HFA384X_COMP_ID_PRI 0x15 +#define HFA384X_COMP_ID_STA 0x1f +#define HFA384X_COMP_ID_FW_AP 0x14b + +struct hfa384x_sup_range +{ + u16 role; + u16 id; + u16 variant; + u16 bottom; + u16 top; +} __attribute__ ((packed)); + + +struct hfa384x_build_id +{ + u16 pri_seq; + u16 sec_seq; +} __attribute__ ((packed)); + +/* FD01 - Download Buffer */ +struct hfa384x_rid_download_buffer +{ + u16 page; + u16 offset; + u16 length; +} __attribute__ ((packed)); + +/* BSS connection quality (RID FD43 range, RID FD51 dBm-normalized) */ +struct hfa384x_comms_quality { + u16 comm_qual; /* 0 .. 92 */ + u16 signal_level; /* 27 .. 154 */ + u16 noise_level; /* 27 .. 154 */ +} __attribute__ ((packed)); + + +/* netdevice private ioctls (used, e.g., with iwpriv from user space) */ + +/* New wireless extensions API - SET/GET convention (even ioctl numbers are + * root only) + */ +#define PRISM2_IOCTL_PRISM2_PARAM (SIOCIWFIRSTPRIV + 0) +#define PRISM2_IOCTL_GET_PRISM2_PARAM (SIOCIWFIRSTPRIV + 1) +#define PRISM2_IOCTL_WRITEMIF (SIOCIWFIRSTPRIV + 2) +#define PRISM2_IOCTL_READMIF (SIOCIWFIRSTPRIV + 3) +#define PRISM2_IOCTL_MONITOR (SIOCIWFIRSTPRIV + 4) +#define PRISM2_IOCTL_RESET (SIOCIWFIRSTPRIV + 6) +#define PRISM2_IOCTL_INQUIRE (SIOCIWFIRSTPRIV + 8) +#define PRISM2_IOCTL_WDS_ADD (SIOCIWFIRSTPRIV + 10) +#define PRISM2_IOCTL_WDS_DEL (SIOCIWFIRSTPRIV + 12) +#define PRISM2_IOCTL_SET_RID_WORD (SIOCIWFIRSTPRIV + 14) +#define PRISM2_IOCTL_MACCMD (SIOCIWFIRSTPRIV + 16) +#define PRISM2_IOCTL_ADDMAC (SIOCIWFIRSTPRIV + 18) +#define PRISM2_IOCTL_DELMAC (SIOCIWFIRSTPRIV + 20) +#define PRISM2_IOCTL_KICKMAC (SIOCIWFIRSTPRIV + 22) + +/* following are not in SIOCGIWPRIV list; check permission in the driver code + */ +#define PRISM2_IOCTL_DOWNLOAD (SIOCDEVPRIVATE + 13) +#define PRISM2_IOCTL_HOSTAPD (SIOCDEVPRIVATE + 14) + + +/* PRISM2_IOCTL_PRISM2_PARAM ioctl() subtypes: */ +enum { + /* PRISM2_PARAM_PTYPE = 1, */ /* REMOVED 2003-10-22 */ + PRISM2_PARAM_TXRATECTRL = 2, + PRISM2_PARAM_BEACON_INT = 3, + PRISM2_PARAM_PSEUDO_IBSS = 4, + PRISM2_PARAM_ALC = 5, + /* PRISM2_PARAM_TXPOWER = 6, */ /* REMOVED 2003-10-22 */ + PRISM2_PARAM_DUMP = 7, + PRISM2_PARAM_OTHER_AP_POLICY = 8, + PRISM2_PARAM_AP_MAX_INACTIVITY = 9, + PRISM2_PARAM_AP_BRIDGE_PACKETS = 10, + PRISM2_PARAM_DTIM_PERIOD = 11, + PRISM2_PARAM_AP_NULLFUNC_ACK = 12, + PRISM2_PARAM_MAX_WDS = 13, + PRISM2_PARAM_AP_AUTOM_AP_WDS = 14, + PRISM2_PARAM_AP_AUTH_ALGS = 15, + PRISM2_PARAM_MONITOR_ALLOW_FCSERR = 16, + PRISM2_PARAM_HOST_ENCRYPT = 17, + PRISM2_PARAM_HOST_DECRYPT = 18, + PRISM2_PARAM_BUS_MASTER_THRESHOLD_RX = 19, + PRISM2_PARAM_BUS_MASTER_THRESHOLD_TX = 20, + PRISM2_PARAM_HOST_ROAMING = 21, + PRISM2_PARAM_BCRX_STA_KEY = 22, + PRISM2_PARAM_IEEE_802_1X = 23, + PRISM2_PARAM_ANTSEL_TX = 24, + PRISM2_PARAM_ANTSEL_RX = 25, + PRISM2_PARAM_MONITOR_TYPE = 26, + PRISM2_PARAM_WDS_TYPE = 27, + PRISM2_PARAM_HOSTSCAN = 28, + PRISM2_PARAM_AP_SCAN = 29, + PRISM2_PARAM_ENH_SEC = 30, + PRISM2_PARAM_IO_DEBUG = 31, + PRISM2_PARAM_BASIC_RATES = 32, + PRISM2_PARAM_OPER_RATES = 33, + PRISM2_PARAM_HOSTAPD = 34, + PRISM2_PARAM_HOSTAPD_STA = 35, + PRISM2_PARAM_WPA = 36, + PRISM2_PARAM_PRIVACY_INVOKED = 37, + PRISM2_PARAM_TKIP_COUNTERMEASURES = 38, + PRISM2_PARAM_DROP_UNENCRYPTED = 39, +}; + +enum { HOSTAP_ANTSEL_DO_NOT_TOUCH = 0, HOSTAP_ANTSEL_DIVERSITY = 1, + HOSTAP_ANTSEL_LOW = 2, HOSTAP_ANTSEL_HIGH = 3 }; + + +/* PRISM2_IOCTL_MACCMD ioctl() subcommands: */ +enum { AP_MAC_CMD_POLICY_OPEN = 0, AP_MAC_CMD_POLICY_ALLOW = 1, + AP_MAC_CMD_POLICY_DENY = 2, AP_MAC_CMD_FLUSH = 3, + AP_MAC_CMD_KICKALL = 4 }; + + +/* PRISM2_IOCTL_DOWNLOAD ioctl() dl_cmd: */ +enum { + PRISM2_DOWNLOAD_VOLATILE = 1 /* RAM */, + /* Note! Old versions of prism2_srec have a fatal error in CRC-16 + * calculation, which will corrupt all non-volatile downloads. + * PRISM2_DOWNLOAD_NON_VOLATILE used to be 2, but it is now 3 to + * prevent use of old versions of prism2_srec for non-volatile + * download. */ + PRISM2_DOWNLOAD_NON_VOLATILE = 3 /* FLASH */, + PRISM2_DOWNLOAD_VOLATILE_GENESIS = 4 /* RAM in Genesis mode */, + /* Persistent versions of volatile download commands (keep firmware + * data in memory and automatically re-download after hw_reset */ + PRISM2_DOWNLOAD_VOLATILE_PERSISTENT = 5, + PRISM2_DOWNLOAD_VOLATILE_GENESIS_PERSISTENT = 6, +}; + +struct prism2_download_param { + u32 dl_cmd; + u32 start_addr; + u32 num_areas; + struct prism2_download_area { + u32 addr; /* wlan card address */ + u32 len; + caddr_t ptr; /* pointer to data in user space */ + } data[0]; +}; + +#define PRISM2_MAX_DOWNLOAD_AREA_LEN 131072 +#define PRISM2_MAX_DOWNLOAD_LEN 262144 + + +/* PRISM2_IOCTL_HOSTAPD ioctl() cmd: */ +enum { + PRISM2_HOSTAPD_FLUSH = 1, + PRISM2_HOSTAPD_ADD_STA = 2, + PRISM2_HOSTAPD_REMOVE_STA = 3, + PRISM2_HOSTAPD_GET_INFO_STA = 4, + /* REMOVED: PRISM2_HOSTAPD_RESET_TXEXC_STA = 5, */ + PRISM2_SET_ENCRYPTION = 6, + PRISM2_GET_ENCRYPTION = 7, + PRISM2_HOSTAPD_SET_FLAGS_STA = 8, + PRISM2_HOSTAPD_GET_RID = 9, + PRISM2_HOSTAPD_SET_RID = 10, + PRISM2_HOSTAPD_SET_ASSOC_AP_ADDR = 11, + PRISM2_HOSTAPD_SET_GENERIC_ELEMENT = 12, + PRISM2_HOSTAPD_MLME = 13, + PRISM2_HOSTAPD_SCAN_REQ = 14, + PRISM2_HOSTAPD_STA_CLEAR_STATS = 15, +}; + +#define PRISM2_HOSTAPD_MAX_BUF_SIZE 1024 +#define PRISM2_HOSTAPD_RID_HDR_LEN \ +((int) (&((struct prism2_hostapd_param *) 0)->u.rid.data)) +#define PRISM2_HOSTAPD_GENERIC_ELEMENT_HDR_LEN \ +((int) (&((struct prism2_hostapd_param *) 0)->u.generic_elem.data)) + +/* Maximum length for algorithm names (-1 for nul termination) used in ioctl() + */ +#define HOSTAP_CRYPT_ALG_NAME_LEN 16 + + +struct prism2_hostapd_param { + u32 cmd; + u8 sta_addr[ETH_ALEN]; + union { + struct { + u16 aid; + u16 capability; + u8 tx_supp_rates; + } add_sta; + struct { + u32 inactive_sec; + } get_info_sta; + struct { + u8 alg[HOSTAP_CRYPT_ALG_NAME_LEN]; + u32 flags; + u32 err; + u8 idx; + u8 seq[8]; /* sequence counter (set: RX, get: TX) */ + u16 key_len; + u8 key[0]; + } crypt; + struct { + u32 flags_and; + u32 flags_or; + } set_flags_sta; + struct { + u16 rid; + u16 len; + u8 data[0]; + } rid; + struct { + u8 len; + u8 data[0]; + } generic_elem; + struct { +#define MLME_STA_DEAUTH 0 +#define MLME_STA_DISASSOC 1 + u16 cmd; + u16 reason_code; + } mlme; + struct { + u8 ssid_len; + u8 ssid[32]; + } scan_req; + } u; +}; + +#define HOSTAP_CRYPT_FLAG_SET_TX_KEY BIT(0) +#define HOSTAP_CRYPT_FLAG_PERMANENT BIT(1) + +#define HOSTAP_CRYPT_ERR_UNKNOWN_ALG 2 +#define HOSTAP_CRYPT_ERR_UNKNOWN_ADDR 3 +#define HOSTAP_CRYPT_ERR_CRYPT_INIT_FAILED 4 +#define HOSTAP_CRYPT_ERR_KEY_SET_FAILED 5 +#define HOSTAP_CRYPT_ERR_TX_KEY_SET_FAILED 6 +#define HOSTAP_CRYPT_ERR_CARD_CONF_FAILED 7 + + +#endif /* HOSTAP_COMMON_H */ diff --git a/contrib/wpa_supplicant/l2_packet.h b/contrib/wpa_supplicant/l2_packet.h new file mode 100644 index 0000000..3e3914c --- /dev/null +++ b/contrib/wpa_supplicant/l2_packet.h @@ -0,0 +1,34 @@ +#ifndef L2_PACKET_H +#define L2_PACKET_H + +#define MAC2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5] +#define MACSTR "%02x:%02x:%02x:%02x:%02x:%02x" + +#ifndef ETH_P_EAPOL +#define ETH_P_EAPOL 0x888e +#endif + +#ifndef ETH_P_RSN_PREAUTH +#define ETH_P_RSN_PREAUTH 0x88c7 +#endif + +struct l2_packet_data; + +struct l2_ethhdr { + u8 h_dest[ETH_ALEN]; + u8 h_source[ETH_ALEN]; + u16 h_proto; +} __attribute__ ((packed)); + +struct l2_packet_data * l2_packet_init( + const char *ifname, const u8 *own_addr, unsigned short protocol, + void (*rx_callback)(void *ctx, unsigned char *src_addr, + unsigned char *buf, size_t len), + void *rx_callback_ctx); +void l2_packet_deinit(struct l2_packet_data *l2); + +int l2_packet_get_own_addr(struct l2_packet_data *l2, u8 *addr); +int l2_packet_send(struct l2_packet_data *l2, u8 *buf, size_t len); +void l2_packet_set_rx_l2_hdr(struct l2_packet_data *l2, int rx_l2_hdr); + +#endif /* L2_PACKET_H */ diff --git a/contrib/wpa_supplicant/md5.c b/contrib/wpa_supplicant/md5.c new file mode 100644 index 0000000..1564e8f --- /dev/null +++ b/contrib/wpa_supplicant/md5.c @@ -0,0 +1,355 @@ +/* + * MD5 hash implementation and interface functions + * Copyright (c) 2003-2004, Jouni Malinen <jkmaline@cc.hut.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include "common.h" +#include "md5.h" + + +void md5_mac(const u8 *key, size_t key_len, const u8 *data, size_t data_len, + u8 *mac) +{ + MD5_CTX context; + MD5Init(&context); + MD5Update(&context, key, key_len); + MD5Update(&context, data, data_len); + MD5Update(&context, key, key_len); + MD5Final(mac, &context); +} + + +/* HMAC code is based on RFC 2104 */ +void hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + MD5_CTX context; + u8 k_ipad[65]; /* inner padding - key XORd with ipad */ + u8 k_opad[65]; /* outer padding - key XORd with opad */ + u8 tk[16]; + int i; + + /* if key is longer than 64 bytes reset it to key = MD5(key) */ + if (key_len > 64) { + MD5Init(&context); + MD5Update(&context, key, key_len); + MD5Final(tk, &context); + + key = tk; + key_len = 16; + } + + /* the HMAC_MD5 transform looks like: + * + * MD5(K XOR opad, MD5(K XOR ipad, text)) + * + * where K is an n byte key + * ipad is the byte 0x36 repeated 64 times + * opad is the byte 0x5c repeated 64 times + * and text is the data being protected */ + + /* start out by storing key in pads */ + memset(k_ipad, 0, sizeof(k_ipad)); + memset(k_opad, 0, sizeof(k_opad)); + memcpy(k_ipad, key, key_len); + memcpy(k_opad, key, key_len); + + /* XOR key with ipad and opad values */ + for (i = 0; i < 64; i++) { + k_ipad[i] ^= 0x36; + k_opad[i] ^= 0x5c; + } + + /* perform inner MD5 */ + MD5Init(&context); /* init context for 1st pass */ + MD5Update(&context, k_ipad, 64); /* start with inner pad */ + /* then text of datagram; all fragments */ + for (i = 0; i < num_elem; i++) { + MD5Update(&context, addr[i], len[i]); + } + MD5Final(mac, &context); /* finish up 1st pass */ + + /* perform outer MD5 */ + MD5Init(&context); /* init context for 2nd pass */ + MD5Update(&context, k_opad, 64); /* start with outer pad */ + MD5Update(&context, mac, 16); /* then results of 1st hash */ + MD5Final(mac, &context); /* finish up 2nd pass */ +} + + +void hmac_md5(const u8 *key, size_t key_len, const u8 *data, size_t data_len, + u8 *mac) +{ + hmac_md5_vector(key, key_len, 1, &data, &data_len, mac); +} + + +#ifndef EAP_TLS_FUNCS + +/* ===== start - public domain MD5 implementation ===== */ +/* + * This code implements the MD5 message-digest algorithm. + * The algorithm is due to Ron Rivest. This code was + * written by Colin Plumb in 1993, no copyright is claimed. + * This code is in the public domain; do with it what you wish. + * + * Equivalent code is available from RSA Data Security, Inc. + * This code has been tested against that, and is equivalent, + * except that you don't need to include two pages of legalese + * with every copy. + * + * To compute the message digest of a chunk of bytes, declare an + * MD5Context structure, pass it to MD5Init, call MD5Update as + * needed on buffers full of bytes, and then call MD5Final, which + * will fill a supplied 16-byte array with the digest. + */ + +#ifndef WORDS_BIGENDIAN +#define byteReverse(buf, len) /* Nothing */ +#else +void byteReverse(unsigned char *buf, unsigned longs); + +#ifndef ASM_MD5 +/* + * Note: this code is harmless on little-endian machines. + */ +void byteReverse(unsigned char *buf, unsigned longs) +{ + u32 t; + do { + t = (u32) ((unsigned) buf[3] << 8 | buf[2]) << 16 | + ((unsigned) buf[1] << 8 | buf[0]); + *(u32 *) buf = t; + buf += 4; + } while (--longs); +} +#endif +#endif + +/* + * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious + * initialization constants. + */ +void MD5Init(struct MD5Context *ctx) +{ + ctx->buf[0] = 0x67452301; + ctx->buf[1] = 0xefcdab89; + ctx->buf[2] = 0x98badcfe; + ctx->buf[3] = 0x10325476; + + ctx->bits[0] = 0; + ctx->bits[1] = 0; +} + +/* + * Update context to reflect the concatenation of another buffer full + * of bytes. + */ +void MD5Update(struct MD5Context *ctx, unsigned char const *buf, unsigned len) +{ + u32 t; + + /* Update bitcount */ + + t = ctx->bits[0]; + if ((ctx->bits[0] = t + ((u32) len << 3)) < t) + ctx->bits[1]++; /* Carry from low to high */ + ctx->bits[1] += len >> 29; + + t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */ + + /* Handle any leading odd-sized chunks */ + + if (t) { + unsigned char *p = (unsigned char *) ctx->in + t; + + t = 64 - t; + if (len < t) { + memcpy(p, buf, len); + return; + } + memcpy(p, buf, t); + byteReverse(ctx->in, 16); + MD5Transform(ctx->buf, (u32 *) ctx->in); + buf += t; + len -= t; + } + /* Process data in 64-byte chunks */ + + while (len >= 64) { + memcpy(ctx->in, buf, 64); + byteReverse(ctx->in, 16); + MD5Transform(ctx->buf, (u32 *) ctx->in); + buf += 64; + len -= 64; + } + + /* Handle any remaining bytes of data. */ + + memcpy(ctx->in, buf, len); +} + +/* + * Final wrapup - pad to 64-byte boundary with the bit pattern + * 1 0* (64-bit count of bits processed, MSB-first) + */ +void MD5Final(unsigned char digest[16], struct MD5Context *ctx) +{ + unsigned count; + unsigned char *p; + + /* Compute number of bytes mod 64 */ + count = (ctx->bits[0] >> 3) & 0x3F; + + /* Set the first char of padding to 0x80. This is safe since there is + always at least one byte free */ + p = ctx->in + count; + *p++ = 0x80; + + /* Bytes of padding needed to make 64 bytes */ + count = 64 - 1 - count; + + /* Pad out to 56 mod 64 */ + if (count < 8) { + /* Two lots of padding: Pad the first block to 64 bytes */ + memset(p, 0, count); + byteReverse(ctx->in, 16); + MD5Transform(ctx->buf, (u32 *) ctx->in); + + /* Now fill the next block with 56 bytes */ + memset(ctx->in, 0, 56); + } else { + /* Pad block to 56 bytes */ + memset(p, 0, count - 8); + } + byteReverse(ctx->in, 14); + + /* Append length in bits and transform */ + ((u32 *) ctx->in)[14] = ctx->bits[0]; + ((u32 *) ctx->in)[15] = ctx->bits[1]; + + MD5Transform(ctx->buf, (u32 *) ctx->in); + byteReverse((unsigned char *) ctx->buf, 4); + memcpy(digest, ctx->buf, 16); + memset(ctx, 0, sizeof(ctx)); /* In case it's sensitive */ +} + +#ifndef ASM_MD5 + +/* The four core functions - F1 is optimized somewhat */ + +/* #define F1(x, y, z) (x & y | ~x & z) */ +#define F1(x, y, z) (z ^ (x & (y ^ z))) +#define F2(x, y, z) F1(z, x, y) +#define F3(x, y, z) (x ^ y ^ z) +#define F4(x, y, z) (y ^ (x | ~z)) + +/* This is the central step in the MD5 algorithm. */ +#define MD5STEP(f, w, x, y, z, data, s) \ + ( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x ) + +/* + * The core of the MD5 algorithm, this alters an existing MD5 hash to + * reflect the addition of 16 longwords of new data. MD5Update blocks + * the data and converts bytes into longwords for this routine. + */ +void MD5Transform(u32 buf[4], u32 const in[16]) +{ + register u32 a, b, c, d; + + a = buf[0]; + b = buf[1]; + c = buf[2]; + d = buf[3]; + + MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7); + MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12); + MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17); + MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22); + MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7); + MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12); + MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17); + MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22); + MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7); + MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12); + MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17); + MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22); + MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7); + MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12); + MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17); + MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22); + + MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5); + MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9); + MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14); + MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20); + MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5); + MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9); + MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14); + MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20); + MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5); + MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9); + MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14); + MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20); + MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5); + MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9); + MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14); + MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20); + + MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4); + MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11); + MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16); + MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23); + MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4); + MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11); + MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16); + MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23); + MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4); + MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11); + MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16); + MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23); + MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4); + MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11); + MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16); + MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23); + + MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6); + MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10); + MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15); + MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21); + MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6); + MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10); + MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15); + MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21); + MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6); + MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10); + MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15); + MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21); + MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6); + MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10); + MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15); + MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21); + + buf[0] += a; + buf[1] += b; + buf[2] += c; + buf[3] += d; +} + +#endif +/* ===== end - public domain MD5 implementation ===== */ + +#endif /* !EAP_TLS_FUNCS */ diff --git a/contrib/wpa_supplicant/md5.h b/contrib/wpa_supplicant/md5.h new file mode 100644 index 0000000..cc3eb95 --- /dev/null +++ b/contrib/wpa_supplicant/md5.h @@ -0,0 +1,43 @@ +#ifndef MD5_H +#define MD5_H + +#ifdef EAP_TLS_FUNCS + +#include <openssl/md5.h> + +#define MD5Init MD5_Init +#define MD5Update MD5_Update +#define MD5Final MD5_Final +#define MD5Transform MD5_Transform + +#define MD5_MAC_LEN MD5_DIGEST_LENGTH + +#else /* EAP_TLS_FUNCS */ + +#define MD5_MAC_LEN 16 + +struct MD5Context { + u32 buf[4]; + u32 bits[2]; + u8 in[64]; +}; + +void MD5Init(struct MD5Context *context); +void MD5Update(struct MD5Context *context, unsigned char const *buf, + unsigned len); +void MD5Final(unsigned char digest[16], struct MD5Context *context); +void MD5Transform(u32 buf[4], u32 const in[16]); + +typedef struct MD5Context MD5_CTX; + +#endif /* EAP_TLS_FUNCS */ + + +void md5_mac(const u8 *key, size_t key_len, const u8 *data, size_t data_len, + u8 *mac); +void hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac); +void hmac_md5(const u8 *key, size_t key_len, const u8 *data, size_t data_len, + u8 *mac); + +#endif /* MD5_H */ diff --git a/contrib/wpa_supplicant/ms_funcs.c b/contrib/wpa_supplicant/ms_funcs.c new file mode 100644 index 0000000..9c50185 --- /dev/null +++ b/contrib/wpa_supplicant/ms_funcs.c @@ -0,0 +1,333 @@ +/* + * WPA Supplicant / shared MSCHAPV2 helper functions / RFC 2433 / RFC 2759 + * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include "common.h" +#include "sha1.h" +#include "ms_funcs.h" +#include "crypto.h" + + +static void challenge_hash(u8 *peer_challenge, u8 *auth_challenge, + u8 *username, size_t username_len, + u8 *challenge) +{ + u8 hash[SHA1_MAC_LEN]; + const unsigned char *addr[3]; + size_t len[3]; + + addr[0] = peer_challenge; + len[0] = 16; + addr[1] = auth_challenge; + len[1] = 16; + addr[2] = username; + len[2] = username_len; + + sha1_vector(3, addr, len, hash); + memcpy(challenge, hash, 8); +} + + +void nt_password_hash(u8 *password, size_t password_len, u8 *password_hash) +{ + u8 *buf; + int i; + + /* Convert password into unicode */ + buf = malloc(password_len * 2); + if (buf == NULL) + return; + memset(buf, 0, password_len * 2); + for (i = 0; i < password_len; i++) + buf[2 * i] = password[i]; + + md4(buf, password_len * 2, password_hash); + free(buf); +} + + +void hash_nt_password_hash(u8 *password_hash, u8 *password_hash_hash) +{ + md4(password_hash, 16, password_hash_hash); +} + + +void challenge_response(u8 *challenge, u8 *password_hash, u8 *response) +{ + u8 zpwd[7]; + des_encrypt(challenge, password_hash, response); + des_encrypt(challenge, password_hash + 7, response + 8); + zpwd[0] = password_hash[14]; + zpwd[1] = password_hash[15]; + memset(zpwd + 2, 0, 5); + des_encrypt(challenge, zpwd, response + 16); +} + + +void generate_nt_response(u8 *auth_challenge, u8 *peer_challenge, + u8 *username, size_t username_len, + u8 *password, size_t password_len, + u8 *response) +{ + u8 challenge[8]; + u8 password_hash[16]; + + challenge_hash(peer_challenge, auth_challenge, username, username_len, + challenge); + nt_password_hash(password, password_len, password_hash); + challenge_response(challenge, password_hash, response); +} + + +void generate_authenticator_response(u8 *password, size_t password_len, + u8 *peer_challenge, + u8 *auth_challenge, + u8 *username, size_t username_len, + u8 *nt_response, u8 *response) +{ + static const u8 magic1[39] = { + 0x4D, 0x61, 0x67, 0x69, 0x63, 0x20, 0x73, 0x65, 0x72, 0x76, + 0x65, 0x72, 0x20, 0x74, 0x6F, 0x20, 0x63, 0x6C, 0x69, 0x65, + 0x6E, 0x74, 0x20, 0x73, 0x69, 0x67, 0x6E, 0x69, 0x6E, 0x67, + 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x61, 0x6E, 0x74 + }; + static const u8 magic2[41] = { + 0x50, 0x61, 0x64, 0x20, 0x74, 0x6F, 0x20, 0x6D, 0x61, 0x6B, + 0x65, 0x20, 0x69, 0x74, 0x20, 0x64, 0x6F, 0x20, 0x6D, 0x6F, + 0x72, 0x65, 0x20, 0x74, 0x68, 0x61, 0x6E, 0x20, 0x6F, 0x6E, + 0x65, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6F, + 0x6E + }; + + u8 password_hash[16], password_hash_hash[16], challenge[8]; + const unsigned char *addr1[3]; + const size_t len1[3] = { 16, 24, sizeof(magic1) }; + const unsigned char *addr2[3]; + const size_t len2[3] = { SHA1_MAC_LEN, 8, sizeof(magic2) }; + + addr1[0] = password_hash_hash; + addr1[1] = nt_response; + addr1[2] = magic1; + + addr2[0] = response; + addr2[1] = challenge; + addr2[2] = magic2; + + nt_password_hash(password, password_len, password_hash); + hash_nt_password_hash(password_hash, password_hash_hash); + sha1_vector(3, addr1, len1, response); + + challenge_hash(peer_challenge, auth_challenge, username, username_len, + challenge); + sha1_vector(3, addr2, len2, response); +} + + +void nt_challenge_response(u8 *challenge, u8 *password, size_t password_len, + u8 *response) +{ + u8 password_hash[16]; + nt_password_hash(password, password_len, password_hash); + challenge_response(challenge, password_hash, response); +} + + +/* IN: 16-octet password_hash_hash and 24-octet nt_response + * OUT: 16-octet master_key */ +void get_master_key(const u8 *password_hash_hash, const u8 *nt_response, + u8 *master_key) +{ + static const u8 magic1[27] = { + 0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, + 0x68, 0x65, 0x20, 0x4d, 0x50, 0x50, 0x45, 0x20, 0x4d, + 0x61, 0x73, 0x74, 0x65, 0x72, 0x20, 0x4b, 0x65, 0x79 + }; + const unsigned char *addr[3]; + const size_t len[3] = { 16, 24, sizeof(magic1) }; + + addr[0] = password_hash_hash; + addr[1] = nt_response; + addr[2] = magic1; + + sha1_vector(3, addr, len, master_key); +} + + +void get_asymetric_start_key(const u8 *master_key, u8 *session_key, + size_t session_key_len, int is_send, + int is_server) +{ + static const u8 magic2[84] = { + 0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69, + 0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20, + 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68, + 0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20, 0x6b, 0x65, 0x79, + 0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, + 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73, 0x69, 0x64, 0x65, + 0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68, + 0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20, + 0x6b, 0x65, 0x79, 0x2e + }; + static const u8 magic3[84] = { + 0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69, + 0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20, + 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68, + 0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20, + 0x6b, 0x65, 0x79, 0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68, + 0x65, 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73, + 0x69, 0x64, 0x65, 0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73, + 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20, + 0x6b, 0x65, 0x79, 0x2e + }; + static const u8 shs_pad1[40] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + + static const u8 shs_pad2[40] = { + 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, + 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, + 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, + 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2 + }; + u8 digest[SHA1_MAC_LEN]; + const unsigned char *addr[4]; + const size_t len[4] = { 16, 40, 84, 40 }; + + addr[0] = master_key; + addr[1] = shs_pad1; + if (is_send) { + addr[2] = is_server ? magic3 : magic2; + } else { + addr[2] = is_server ? magic2 : magic3; + } + addr[3] = shs_pad2; + + sha1_vector(4, addr, len, digest); + + if (session_key_len > SHA1_MAC_LEN) + session_key_len = SHA1_MAC_LEN; + memcpy(session_key, digest, session_key_len); +} + + +#ifdef TEST_MAIN_MS_FUNCS +int main(int argc, char *argv[]) +{ + /* Test vector from RFC2759 example */ + u8 *username = "User"; + u8 *password = "clientPass"; + u8 auth_challenge[] = { + 0x5B, 0x5D, 0x7C, 0x7D, 0x7B, 0x3F, 0x2F, 0x3E, + 0x3C, 0x2C, 0x60, 0x21, 0x32, 0x26, 0x26, 0x28 + }; + u8 peer_challenge[] = { + 0x21, 0x40, 0x23, 0x24, 0x25, 0x5E, 0x26, 0x2A, + 0x28, 0x29, 0x5F, 0x2B, 0x3A, 0x33, 0x7C, 0x7E + }; + u8 challenge[] = { 0xD0, 0x2E, 0x43, 0x86, 0xBC, 0xE9, 0x12, 0x26 }; + u8 password_hash[] = { + 0x44, 0xEB, 0xBA, 0x8D, 0x53, 0x12, 0xB8, 0xD6, + 0x11, 0x47, 0x44, 0x11, 0xF5, 0x69, 0x89, 0xAE + }; + u8 nt_response[] = { + 0x82, 0x30, 0x9E, 0xCD, 0x8D, 0x70, 0x8B, 0x5E, + 0xA0, 0x8F, 0xAA, 0x39, 0x81, 0xCD, 0x83, 0x54, + 0x42, 0x33, 0x11, 0x4A, 0x3D, 0x85, 0xD6, 0xDF + }; + u8 password_hash_hash[] = { + 0x41, 0xC0, 0x0C, 0x58, 0x4B, 0xD2, 0xD9, 0x1C, + 0x40, 0x17, 0xA2, 0xA1, 0x2F, 0xA5, 0x9F, 0x3F + }; + u8 authenticator_response[] = { + 0x40, 0x7A, 0x55, 0x89, 0x11, 0x5F, 0xD0, 0xD6, + 0x20, 0x9F, 0x51, 0x0F, 0xE9, 0xC0, 0x45, 0x66, + 0x93, 0x2C, 0xDA, 0x56 + }; + u8 master_key[] = { + 0xFD, 0xEC, 0xE3, 0x71, 0x7A, 0x8C, 0x83, 0x8C, + 0xB3, 0x88, 0xE5, 0x27, 0xAE, 0x3C, 0xDD, 0x31 + }; + u8 send_start_key[] = { + 0x8B, 0x7C, 0xDC, 0x14, 0x9B, 0x99, 0x3A, 0x1B, + 0xA1, 0x18, 0xCB, 0x15, 0x3F, 0x56, 0xDC, 0xCB + }; + u8 buf[32]; + + int errors = 0; + + printf("Testing ms_funcs.c\n"); + + challenge_hash(peer_challenge, auth_challenge, + username, strlen(username), + buf); + if (memcmp(challenge, buf, sizeof(challenge)) != 0) { + printf("challenge_hash failed\n"); + errors++; + } + + nt_password_hash(password, strlen(password), buf); + if (memcmp(password_hash, buf, sizeof(password_hash)) != 0) { + printf("nt_password_hash failed\n"); + errors++; + } + + generate_nt_response(auth_challenge, peer_challenge, + username, strlen(username), + password, strlen(password), + buf); + if (memcmp(nt_response, buf, sizeof(nt_response)) != 0) { + printf("generate_nt_response failed\n"); + errors++; + } + + hash_nt_password_hash(password_hash, buf); + if (memcmp(password_hash_hash, buf, sizeof(password_hash_hash)) != 0) { + printf("hash_nt_password_hash failed\n"); + errors++; + } + + generate_authenticator_response(password, strlen(password), + peer_challenge, auth_challenge, + username, strlen(username), + nt_response, buf); + if (memcmp(authenticator_response, buf, sizeof(authenticator_response)) + != 0) { + printf("generate_authenticator_response failed\n"); + errors++; + } + + get_master_key(password_hash_hash, nt_response, buf); + if (memcmp(master_key, buf, sizeof(master_key)) != 0) { + printf("get_master_key failed\n"); + errors++; + } + + get_asymetric_start_key(master_key, buf, sizeof(send_start_key), 1, 1); + if (memcmp(send_start_key, buf, sizeof(send_start_key)) != 0) { + printf("get_asymetric_start_key failed\n"); + errors++; + } + + if (errors) + printf("FAILED! %d errors\n", errors); + + return errors; +} +#endif /* TEST_MAIN_MS_FUNCS */ diff --git a/contrib/wpa_supplicant/ms_funcs.h b/contrib/wpa_supplicant/ms_funcs.h new file mode 100644 index 0000000..a08ab06 --- /dev/null +++ b/contrib/wpa_supplicant/ms_funcs.h @@ -0,0 +1,25 @@ +#ifndef MS_FUNCS_H +#define MS_FUNCS_H + +void generate_nt_response(u8 *auth_challenge, u8 *peer_challenge, + u8 *username, size_t username_len, + u8 *password, size_t password_len, + u8 *response); +void generate_authenticator_response(u8 *password, size_t password_len, + u8 *peer_challenge, + u8 *auth_challenge, + u8 *username, size_t username_len, + u8 *nt_response, u8 *response); +void nt_challenge_response(u8 *challenge, u8 *password, size_t password_len, + u8 *response); + +void challenge_response(u8 *challenge, u8 *password_hash, u8 *response); +void nt_password_hash(u8 *password, size_t password_len, u8 *password_hash); +void hash_nt_password_hash(u8 *password_hash, u8 *password_hash_hash); +void get_master_key(const u8 *password_hash_hash, const u8 *nt_response, + u8 *master_key); +void get_asymetric_start_key(const u8 *master_key, u8 *session_key, + size_t session_key_len, int is_send, + int is_server); + +#endif /* MS_FUNCS_H */ diff --git a/contrib/wpa_supplicant/openssl-tls-extensions.patch b/contrib/wpa_supplicant/openssl-tls-extensions.patch new file mode 100644 index 0000000..77e9a41 --- /dev/null +++ b/contrib/wpa_supplicant/openssl-tls-extensions.patch @@ -0,0 +1,166 @@ +This is a quick hack for testing EAP-FAST with openssl. + +Addition of TLS extensions to ClientHello/ServerHello is more or less +ok, though not very clean in the way that the caller needs to take +care of constructing set of all extensions. In addition there is not +mechanism for reading the TLS extensions, i.e., this would not be +enough for EAP-FAST authenticator. + +Rest of the changes are obviously ugly and/or incorrect for most +parts, but it demonstrates the minimum set of changes to skip some of +the error cases that prevented completion of TLS handshake without +certificates. In other words, this is just a proof-of-concept type of +example to make it possible to experiment with EAP-FAST. Cleaner patch +for the needed functionality would be welcome.. + + +diff -upr openssl-0.9.7e.orig/include/openssl/ssl.h openssl-0.9.7e/include/openssl/ssl.h +--- openssl-0.9.7e.orig/include/openssl/ssl.h 2004-07-27 11:28:49.000000000 -0700 ++++ openssl-0.9.7e/include/openssl/ssl.h 2004-12-24 20:29:01.000000000 -0800 +@@ -929,6 +929,11 @@ struct ssl_st + int first_packet; + int client_version; /* what was passed, used for + * SSLv3/TLS rollback check */ ++ ++ /* Optional ClientHello/ServerHello extension to be added to the end ++ * of the SSLv3/TLS hello message. */ ++ char *hello_extension; ++ int hello_extension_len; + }; + + #ifdef __cplusplus +diff -upr openssl-0.9.7e.orig/ssl/s3_both.c openssl-0.9.7e/ssl/s3_both.c +--- openssl-0.9.7e.orig/ssl/s3_both.c 2003-02-12 09:05:17.000000000 -0800 ++++ openssl-0.9.7e/ssl/s3_both.c 2004-12-31 21:18:15.556846272 -0800 +@@ -199,6 +199,12 @@ int ssl3_get_finished(SSL *s, int a, int + 64, /* should actually be 36+4 :-) */ + &ok); + ++ if (!ok && s->hello_extension) ++ { ++ /* Quick hack to test EAP-FAST. */ ++ return(1); ++ } ++ + if (!ok) return((int)n); + + /* If this occurs, we have missed a message */ +diff -upr openssl-0.9.7e.orig/ssl/s3_clnt.c openssl-0.9.7e/ssl/s3_clnt.c +--- openssl-0.9.7e.orig/ssl/s3_clnt.c 2004-05-15 09:39:22.000000000 -0700 ++++ openssl-0.9.7e/ssl/s3_clnt.c 2004-12-31 21:16:38.617583280 -0800 +@@ -588,6 +588,12 @@ static int ssl3_client_hello(SSL *s) + *(p++)=comp->id; + } + *(p++)=0; /* Add the NULL method */ ++ ++ if (s->hello_extension) ++ { ++ memcpy(p,s->hello_extension,s->hello_extension_len); ++ p+=s->hello_extension_len; ++ } + + l=(p-d); + d=buf; +@@ -779,6 +785,11 @@ static int ssl3_get_server_certificate(S + + if (s->s3->tmp.message_type != SSL3_MT_CERTIFICATE) + { ++ if (s->hello_extension) ++ { ++ /* Quick hack to test EAP-FAST. */ ++ return(1); ++ } + al=SSL_AD_UNEXPECTED_MESSAGE; + SSLerr(SSL_F_SSL3_GET_SERVER_CERTIFICATE,SSL_R_BAD_MESSAGE_TYPE); + goto f_err; +@@ -951,6 +962,12 @@ static int ssl3_get_key_exchange(SSL *s) + DH *dh=NULL; + #endif + ++ if (s->hello_extension) ++ { ++ /* Quick hack to test EAP-FAST. */ ++ return(1); ++ } ++ + /* use same message size as in ssl3_get_certificate_request() + * as ServerKeyExchange message may be skipped */ + n=ssl3_get_message(s, +@@ -1264,6 +1281,12 @@ static int ssl3_get_certificate_request( + unsigned char *p,*d,*q; + STACK_OF(X509_NAME) *ca_sk=NULL; + ++ if (s->hello_extension) ++ { ++ /* Quick hack to test EAP-FAST. */ ++ return(1); ++ } ++ + n=ssl3_get_message(s, + SSL3_ST_CR_CERT_REQ_A, + SSL3_ST_CR_CERT_REQ_B, +@@ -1407,6 +1430,12 @@ static int ssl3_get_server_done(SSL *s) + int ok,ret=0; + long n; + ++ if (s->hello_extension) ++ { ++ /* Quick hack to test EAP-FAST. */ ++ return(1); ++ } ++ + n=ssl3_get_message(s, + SSL3_ST_CR_SRVR_DONE_A, + SSL3_ST_CR_SRVR_DONE_B, +@@ -1439,6 +1468,12 @@ static int ssl3_send_client_key_exchange + KSSL_ERR kssl_err; + #endif /* OPENSSL_NO_KRB5 */ + ++ if (s->hello_extension) ++ { ++ /* Quick hack to test EAP-FAST. */ ++ return(1); ++ } ++ + if (s->state == SSL3_ST_CW_KEY_EXCH_A) + { + d=(unsigned char *)s->init_buf->data; +@@ -1880,6 +1915,12 @@ static int ssl3_check_cert_and_algorithm + DH *dh; + #endif + ++ if (s->hello_extension) ++ { ++ /* Quick hack to test EAP-FAST. */ ++ return(1); ++ } ++ + sc=s->session->sess_cert; + + if (sc == NULL) +diff -upr openssl-0.9.7e.orig/ssl/ssl.h openssl-0.9.7e/ssl/ssl.h +--- openssl-0.9.7e.orig/ssl/ssl.h 2004-07-27 11:28:49.000000000 -0700 ++++ openssl-0.9.7e/ssl/ssl.h 2004-12-24 20:29:01.000000000 -0800 +@@ -929,6 +929,11 @@ struct ssl_st + int first_packet; + int client_version; /* what was passed, used for + * SSLv3/TLS rollback check */ ++ ++ /* Optional ClientHello/ServerHello extension to be added to the end ++ * of the SSLv3/TLS hello message. */ ++ char *hello_extension; ++ int hello_extension_len; + }; + + #ifdef __cplusplus +diff -upr openssl-0.9.7e.orig/ssl/ssl_lib.c openssl-0.9.7e/ssl/ssl_lib.c +--- openssl-0.9.7e.orig/ssl/ssl_lib.c 2004-05-11 05:46:12.000000000 -0700 ++++ openssl-0.9.7e/ssl/ssl_lib.c 2004-12-24 20:35:22.000000000 -0800 +@@ -478,6 +478,7 @@ void SSL_free(SSL *s) + kssl_ctx_free(s->kssl_ctx); + #endif /* OPENSSL_NO_KRB5 */ + ++ OPENSSL_free(s->hello_extension); + OPENSSL_free(s); + } + diff --git a/contrib/wpa_supplicant/pcsc_funcs.c b/contrib/wpa_supplicant/pcsc_funcs.c new file mode 100644 index 0000000..541661f --- /dev/null +++ b/contrib/wpa_supplicant/pcsc_funcs.c @@ -0,0 +1,776 @@ +/* + * WPA Supplicant / PC/SC smartcard interface for USIM, GSM SIM + * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <winscard.h> + +#include "common.h" +#include "wpa_supplicant.h" +#include "pcsc_funcs.h" + + +/* See ETSI GSM 11.11 and ETSI TS 102 221 for details. + * SIM commands: + * Command APDU: CLA INS P1 P2 P3 Data + * CLA (class of instruction): A0 for GSM, 00 for USIM + * INS (instruction) + * P1 P2 P3 (parameters, P3 = length of Data) + * Response APDU: Data SW1 SW2 + * SW1 SW2 (Status words) + * Commands (INS P1 P2 P3): + * SELECT: A4 00 00 02 <file_id, 2 bytes> + * GET RESPONSE: C0 00 00 <len> + * RUN GSM ALG: 88 00 00 00 <RAND len = 10> + * RUN UMTS ALG: 88 00 81 <len=0x22> data: 0x10 | RAND | 0x10 | AUTN + * P1 = ID of alg in card + * P2 = ID of secret key + * READ BINARY: B0 <offset high> <offset low> <len> + * VERIFY CHV: 20 00 <CHV number> 08 + * CHANGE CHV: 24 00 <CHV number> 10 + * DISABLE CHV: 26 00 01 08 + * ENABLE CHV: 28 00 01 08 + * UNBLOCK CHV: 2C 00 <00=CHV1, 02=CHV2> 10 + * SLEEP: FA 00 00 00 + */ + +/* GSM SIM commands */ +#define SIM_CMD_SELECT 0xa0, 0xa4, 0x00, 0x00, 0x02 +#define SIM_CMD_RUN_GSM_ALG 0xa0, 0x88, 0x00, 0x00, 0x10 +#define SIM_CMD_GET_RESPONSE 0xa0, 0xc0, 0x00, 0x00 +#define SIM_CMD_READ_BIN 0xa0, 0xb0, 0x00, 0x00 +#define SIM_CMD_VERIFY_CHV1 0xa0, 0x20, 0x00, 0x01, 0x08 + +/* USIM commands */ +#define USIM_CLA 0x00 +#define USIM_CMD_RUN_UMTS_ALG 0x00, 0x88, 0x00, 0x81, 0x22 +#define USIM_CMD_GET_RESPONSE 0x00, 0xc0, 0x00, 0x00 + +#define USIM_FSP_TEMPL_TAG 0x62 + +#define USIM_TLV_FILE_DESC 0x82 +#define USIM_TLV_FILE_ID 0x83 +#define USIM_TLV_DF_NAME 0x84 +#define USIM_TLV_PROPR_INFO 0xA5 +#define USIM_TLV_LIFE_CYCLE_STATUS 0x8A +#define USIM_TLV_FILE_SIZE 0x80 +#define USIM_TLV_TOTAL_FILE_SIZE 0x81 +#define USIM_TLV_PIN_STATUS_TEMPLATE 0xC6 +#define USIM_TLV_SHORT_FILE_ID 0x88 + +#define USIM_PS_DO_TAG 0x90 + +#define AKA_RAND_LEN 16 +#define AKA_AUTN_LEN 16 +#define AKA_AUTS_LEN 14 +#define RES_MAX_LEN 16 +#define IK_LEN 16 +#define CK_LEN 16 + + +typedef enum { SCARD_GSM_SIM, SCARD_USIM } sim_types; + +struct scard_data { + long ctx; + long card; + unsigned long protocol; + SCARD_IO_REQUEST recv_pci; + sim_types sim_type; +}; + + +static int _scard_select_file(struct scard_data *scard, unsigned short file_id, + unsigned char *buf, size_t *buf_len, + sim_types sim_type, unsigned char *aid); +static int scard_select_file(struct scard_data *scard, unsigned short file_id, + unsigned char *buf, size_t *buf_len); +static int scard_verify_pin(struct scard_data *scard, char *pin); + + +static int scard_parse_fsp_templ(unsigned char *buf, size_t buf_len, + int *ps_do, int *file_len) +{ + unsigned char *pos, *end; + + if (ps_do) + *ps_do = -1; + if (file_len) + *file_len = -1; + + pos = buf; + end = pos + buf_len; + if (*pos != USIM_FSP_TEMPL_TAG) { + wpa_printf(MSG_DEBUG, "SCARD: file header did not " + "start with FSP template tag"); + return -1; + } + pos++; + if (pos >= end) + return -1; + if ((pos + pos[0]) < end) + end = pos + 1 + pos[0]; + pos++; + wpa_hexdump(MSG_DEBUG, "SCARD: file header FSP template", + pos, end - pos); + + while (pos + 1 < end) { + wpa_printf(MSG_MSGDUMP, "SCARD: file header TLV " + "0x%02x len=%d", pos[0], pos[1]); + if (pos + 2 + pos[1] > end) + break; + + if (pos[0] == USIM_TLV_FILE_SIZE && + (pos[1] == 1 || pos[1] == 2) && file_len) { + if (pos[1] == 1) + *file_len = (int) pos[2]; + else + *file_len = ((int) pos[2] << 8) | + (int) pos[3]; + wpa_printf(MSG_DEBUG, "SCARD: file_size=%d", + *file_len); + } + + if (pos[0] == USIM_TLV_PIN_STATUS_TEMPLATE && + pos[1] >= 2 && pos[2] == USIM_PS_DO_TAG && + pos[3] >= 1 && ps_do) { + wpa_printf(MSG_DEBUG, "SCARD: PS_DO=0x%02x", + pos[4]); + *ps_do = (int) pos[4]; + } + + pos += 2 + pos[1]; + + if (pos == end) + return 0; + } + return -1; +} + + +static int scard_pin_needed(struct scard_data *scard, + unsigned char *hdr, size_t hlen) +{ + if (scard->sim_type == SCARD_GSM_SIM) { + if (hlen > SCARD_CHV1_OFFSET && + !(hdr[SCARD_CHV1_OFFSET] & SCARD_CHV1_FLAG)) + return 1; + return 0; + } + + if (scard->sim_type == SCARD_USIM) { + int ps_do; + if (scard_parse_fsp_templ(hdr, hlen, &ps_do, NULL)) + return -1; + /* TODO: there could be more than one PS_DO entry because of + * multiple PINs in key reference.. */ + if (ps_do) + return 1; + } + + return -1; +} + + +struct scard_data * scard_init(scard_sim_type sim_type, char *pin) +{ + long ret, len; + struct scard_data *scard; + char *readers = NULL; + char buf[100]; + size_t blen; + + wpa_printf(MSG_DEBUG, "SCARD: initializing smart card interface"); + scard = malloc(sizeof(*scard)); + if (scard == NULL) + return NULL; + memset(scard, 0, sizeof(*scard)); + + ret = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, + &scard->ctx); + if (ret != SCARD_S_SUCCESS) { + wpa_printf(MSG_DEBUG, "SCARD: Could not establish smart card " + "context (err=%ld)", ret); + goto failed; + } + + ret = SCardListReaders(scard->ctx, NULL, NULL, &len); + if (ret != SCARD_S_SUCCESS) { + wpa_printf(MSG_DEBUG, "SCARD: SCardListReaders failed " + "(err=%ld)", ret); + goto failed; + } + + readers = malloc(len); + if (readers == NULL) { + printf("malloc failed\n"); + goto failed; + } + + ret = SCardListReaders(scard->ctx, NULL, readers, &len); + if (ret != SCARD_S_SUCCESS) { + wpa_printf(MSG_DEBUG, "SCARD: SCardListReaders failed(2) " + "(err=%ld)", ret); + goto failed; + } + if (len < 3) { + wpa_printf(MSG_WARNING, "SCARD: No smart card readers " + "available."); + goto failed; + } + /* readers is a list of available reader. Last entry is terminated with + * double NUL. + * TODO: add support for selecting the reader; now just use the first + * one.. */ + wpa_printf(MSG_DEBUG, "SCARD: Selected reader='%s'", readers); + + ret = SCardConnect(scard->ctx, readers, SCARD_SHARE_SHARED, + SCARD_PROTOCOL_T0, &scard->card, &scard->protocol); + if (ret != SCARD_S_SUCCESS) { + if (ret == SCARD_E_NO_SMARTCARD) + wpa_printf(MSG_INFO, "No smart card inserted."); + else + wpa_printf(MSG_WARNING, "SCardConnect err=%lx", ret); + goto failed; + } + + free(readers); + readers = NULL; + + wpa_printf(MSG_DEBUG, "SCARD: card=%ld active_protocol=%lu", + scard->card, scard->protocol); + + blen = sizeof(buf); + + scard->sim_type = SCARD_GSM_SIM; + if (sim_type == SCARD_USIM_ONLY || sim_type == SCARD_TRY_BOTH) { + wpa_printf(MSG_DEBUG, "SCARD: verifying USIM support"); + if (_scard_select_file(scard, SCARD_FILE_MF, buf, &blen, + SCARD_USIM, NULL)) { + wpa_printf(MSG_DEBUG, "SCARD: USIM is not supported"); + if (sim_type == SCARD_USIM_ONLY) + goto failed; + wpa_printf(MSG_DEBUG, "SCARD: Trying to use GSM SIM"); + scard->sim_type = SCARD_GSM_SIM; + } else { + wpa_printf(MSG_DEBUG, "SCARD: USIM is supported"); + scard->sim_type = SCARD_USIM; + } + } + + if (scard->sim_type == SCARD_GSM_SIM) { + blen = sizeof(buf); + if (scard_select_file(scard, SCARD_FILE_MF, buf, &blen)) { + wpa_printf(MSG_DEBUG, "SCARD: Failed to read MF"); + goto failed; + } + + blen = sizeof(buf); + if (scard_select_file(scard, SCARD_FILE_GSM_DF, buf, &blen)) { + wpa_printf(MSG_DEBUG, "SCARD: Failed to read GSM DF"); + goto failed; + } + } else { + /* Select based on AID = 3G RID */ + blen = sizeof(buf); + if (_scard_select_file(scard, 0, buf, &blen, scard->sim_type, + "\xA0\x00\x00\x00\x87")) { + wpa_printf(MSG_DEBUG, "SCARD: Failed to read 3G RID " + "AID"); + goto failed; + } + } + + /* Verify whether CHV1 (PIN1) is needed to access the card. */ + if (scard_pin_needed(scard, buf, blen)) { + wpa_printf(MSG_DEBUG, "PIN1 needed for SIM access"); + if (pin == NULL) { + wpa_printf(MSG_INFO, "No PIN configured for SIM " + "access"); + /* TODO: ask PIN from user through a frontend (e.g., + * wpa_cli) */ + goto failed; + } + if (scard_verify_pin(scard, pin)) { + wpa_printf(MSG_INFO, "PIN verification failed for " + "SIM access"); + /* TODO: what to do? */ + goto failed; + } + } + + return scard; + +failed: + free(readers); + scard_deinit(scard); + return NULL; +} + + +void scard_deinit(struct scard_data *scard) +{ + long ret; + + if (scard == NULL) + return; + + wpa_printf(MSG_DEBUG, "SCARD: deinitializing smart card interface"); + if (scard->card) { + ret = SCardDisconnect(scard->card, SCARD_UNPOWER_CARD); + if (ret != SCARD_S_SUCCESS) { + wpa_printf(MSG_DEBUG, "SCARD: Failed to disconnect " + "smart card (err=%ld)", ret); + } + } + + if (scard->ctx) { + ret = SCardReleaseContext(scard->ctx); + if (ret != SCARD_S_SUCCESS) { + wpa_printf(MSG_DEBUG, "Failed to release smart card " + "context (err=%ld)", ret); + } + } + free(scard); +} + + +static long scard_transmit(struct scard_data *scard, + unsigned char *send, size_t send_len, + unsigned char *recv, size_t *recv_len) +{ + long ret; + unsigned long rlen; + + wpa_hexdump_key(MSG_DEBUG, "SCARD: scard_transmit: send", + send, send_len); + rlen = *recv_len; + ret = SCardTransmit(scard->card, + scard->protocol == SCARD_PROTOCOL_T1 ? + SCARD_PCI_T1 : SCARD_PCI_T0, + send, (unsigned long) send_len, + &scard->recv_pci, recv, &rlen); + *recv_len = rlen; + if (ret == SCARD_S_SUCCESS) { + wpa_hexdump(MSG_DEBUG, "SCARD: scard_transmit: recv", + recv, rlen); + } else { + wpa_printf(MSG_WARNING, "SCARD: SCardTransmit failed " + "(err=0x%lx)", ret); + } + return ret; +} + + +static int _scard_select_file(struct scard_data *scard, unsigned short file_id, + unsigned char *buf, size_t *buf_len, + sim_types sim_type, unsigned char *aid) +{ + long ret; + unsigned char resp[3]; + unsigned char cmd[10] = { SIM_CMD_SELECT }; + int cmdlen; + unsigned char get_resp[5] = { SIM_CMD_GET_RESPONSE }; + size_t len, rlen; + + if (sim_type == SCARD_USIM) { + cmd[0] = USIM_CLA; + cmd[3] = 0x04; + get_resp[0] = USIM_CLA; + } + + wpa_printf(MSG_DEBUG, "SCARD: select file %04x", file_id); + if (aid) { + cmd[2] = 0x04; /* Select by AID */ + cmd[4] = 5; /* len */ + memcpy(cmd + 5, aid, 5); + cmdlen = 10; + } else { + cmd[5] = file_id >> 8; + cmd[6] = file_id & 0xff; + cmdlen = 7; + } + len = sizeof(resp); + ret = scard_transmit(scard, cmd, cmdlen, resp, &len); + if (ret != SCARD_S_SUCCESS) { + wpa_printf(MSG_WARNING, "SCARD: SCardTransmit failed " + "(err=0x%lx)", ret); + return -1; + } + + if (len != 2) { + wpa_printf(MSG_WARNING, "SCARD: unexpected resp len " + "%d (expected 2)", (int) len); + return -1; + } + + if (resp[0] == 0x98 && resp[1] == 0x04) { + /* Security status not satisfied (PIN_WLAN) */ + wpa_printf(MSG_WARNING, "SCARD: Security status not satisfied " + "(PIN_WLAN)"); + return -1; + } + + if (resp[0] == 0x6e) { + wpa_printf(MSG_DEBUG, "SCARD: used CLA not supported"); + return -1; + } + + if (resp[0] != 0x6c && resp[0] != 0x9f && resp[0] != 0x61) { + wpa_printf(MSG_WARNING, "SCARD: unexpected response 0x%02x " + "(expected 0x61, 0x6c, or 0x9f)", resp[0]); + return -1; + } + /* Normal ending of command; resp[1] bytes available */ + get_resp[4] = resp[1]; + wpa_printf(MSG_DEBUG, "SCARD: trying to get response (%d bytes)", + resp[1]); + + rlen = *buf_len; + ret = scard_transmit(scard, get_resp, sizeof(get_resp), buf, &rlen); + if (ret == SCARD_S_SUCCESS) { + *buf_len = resp[1] < rlen ? resp[1] : rlen; + return 0; + } + + wpa_printf(MSG_WARNING, "SCARD: SCardTransmit err=0x%lx\n", ret); + return -1; +} + + +static int scard_select_file(struct scard_data *scard, unsigned short file_id, + unsigned char *buf, size_t *buf_len) +{ + return _scard_select_file(scard, file_id, buf, buf_len, + scard->sim_type, NULL); +} + + +static int scard_read_file(struct scard_data *scard, + unsigned char *data, size_t len) +{ + char cmd[5] = { SIM_CMD_READ_BIN, len }; + size_t blen = len + 3; + unsigned char *buf; + long ret; + + buf = malloc(blen); + if (buf == NULL) + return -1; + + if (scard->sim_type == SCARD_USIM) + cmd[0] = USIM_CLA; + ret = scard_transmit(scard, cmd, sizeof(cmd), buf, &blen); + if (ret != SCARD_S_SUCCESS) { + free(buf); + return -2; + } + if (blen != len + 2) { + wpa_printf(MSG_DEBUG, "SCARD: file read returned unexpected " + "length %d (expected %d)", blen, len + 2); + free(buf); + return -3; + } + + if (buf[len] != 0x90 || buf[len + 1] != 0x00) { + wpa_printf(MSG_DEBUG, "SCARD: file read returned unexpected " + "status %02x %02x (expected 90 00)", + buf[len], buf[len + 1]); + free(buf); + return -4; + } + + memcpy(data, buf, len); + free(buf); + + return 0; +} + + +static int scard_verify_pin(struct scard_data *scard, char *pin) +{ + long ret; + unsigned char resp[3]; + char cmd[5 + 8] = { SIM_CMD_VERIFY_CHV1 }; + size_t len; + + wpa_printf(MSG_DEBUG, "SCARD: verifying PIN"); + + if (pin == NULL || strlen(pin) > 8) + return -1; + + if (scard->sim_type == SCARD_USIM) + cmd[0] = USIM_CLA; + memcpy(cmd + 5, pin, strlen(pin)); + memset(cmd + 5 + strlen(pin), 0xff, 8 - strlen(pin)); + + len = sizeof(resp); + ret = scard_transmit(scard, cmd, sizeof(cmd), resp, &len); + if (ret != SCARD_S_SUCCESS) + return -2; + + if (len != 2 || resp[0] != 0x90 || resp[1] != 0x00) { + wpa_printf(MSG_WARNING, "SCARD: PIN verification failed"); + return -1; + } + + wpa_printf(MSG_DEBUG, "SCARD: PIN verified successfully"); + return 0; +} + + +int scard_get_imsi(struct scard_data *scard, char *imsi, size_t *len) +{ + char buf[100]; + size_t blen, imsilen; + char *pos; + int i; + + wpa_printf(MSG_DEBUG, "SCARD: reading IMSI from (GSM) EF-IMSI"); + blen = sizeof(buf); + if (scard_select_file(scard, SCARD_FILE_GSM_EF_IMSI, buf, &blen)) + return -1; + if (blen < 4) { + wpa_printf(MSG_WARNING, "SCARD: too short (GSM) EF-IMSI " + "header (len=%d)", blen); + return -2; + } + + if (scard->sim_type == SCARD_GSM_SIM) { + blen = (buf[2] << 8) | buf[3]; + } else { + int file_size; + if (scard_parse_fsp_templ(buf, blen, NULL, &file_size)) + return -3; + blen = file_size; + } + if (blen < 2 || blen > sizeof(buf)) { + wpa_printf(MSG_DEBUG, "SCARD: invalid IMSI file length=%d", + blen); + return -3; + } + + imsilen = (blen - 2) * 2 + 1; + wpa_printf(MSG_DEBUG, "SCARD: IMSI file length=%d imsilen=%d", + blen, imsilen); + if (blen < 2 || imsilen > *len) { + *len = imsilen; + return -4; + } + + if (scard_read_file(scard, buf, blen)) + return -5; + + pos = imsi; + *pos++ = '0' + (buf[1] >> 4 & 0x0f); + for (i = 2; i < blen; i++) { + unsigned char digit; + + digit = buf[i] & 0x0f; + if (digit < 10) + *pos++ = '0' + digit; + else + imsilen--; + + digit = buf[i] >> 4 & 0x0f; + if (digit < 10) + *pos++ = '0' + digit; + else + imsilen--; + } + *len = imsilen; + + return 0; +} + + +int scard_gsm_auth(struct scard_data *scard, unsigned char *rand, + unsigned char *sres, unsigned char *kc) +{ + unsigned char cmd[5 + 1 + 16] = { SIM_CMD_RUN_GSM_ALG }; + int cmdlen; + unsigned char get_resp[5] = { SIM_CMD_GET_RESPONSE }; + unsigned char resp[3], buf[12 + 3 + 2]; + size_t len; + long ret; + + if (scard == NULL) + return -1; + + wpa_hexdump(MSG_DEBUG, "SCARD: GSM auth - RAND", rand, 16); + if (scard->sim_type == SCARD_GSM_SIM) { + cmdlen = 5 + 16; + memcpy(cmd + 5, rand, 16); + } else { + cmdlen = 5 + 1 + 16; + cmd[0] = USIM_CLA; + cmd[3] = 0x80; + cmd[4] = 17; + cmd[5] = 16; + memcpy(cmd + 6, rand, 16); + } + len = sizeof(resp); + ret = scard_transmit(scard, cmd, sizeof(cmd), resp, &len); + if (ret != SCARD_S_SUCCESS) + return -2; + + if ((scard->sim_type == SCARD_GSM_SIM && + (len != 2 || resp[0] != 0x9f || resp[1] != 0x0c)) || + (scard->sim_type == SCARD_USIM && + (len != 2 || resp[0] != 0x61 || resp[1] != 0x0e))) { + wpa_printf(MSG_WARNING, "SCARD: unexpected response for GSM " + "auth request (len=%d resp=%02x %02x)", + len, resp[0], resp[1]); + return -3; + } + get_resp[4] = resp[1]; + + len = sizeof(buf); + ret = scard_transmit(scard, get_resp, sizeof(get_resp), buf, &len); + if (ret != SCARD_S_SUCCESS) + return -4; + + if (scard->sim_type == SCARD_GSM_SIM) { + if (len != 4 + 8 + 2) { + wpa_printf(MSG_WARNING, "SCARD: unexpected data " + "length for GSM auth (len=%d, expected 14)", + len); + return -5; + } + memcpy(sres, buf, 4); + memcpy(kc, buf + 4, 8); + } else { + if (len != 1 + 4 + 1 + 8 + 2) { + wpa_printf(MSG_WARNING, "SCARD: unexpected data " + "length for USIM auth (len=%d, " + "expected 16)", len); + return -5; + } + if (buf[0] != 4 || buf[5] != 8) { + wpa_printf(MSG_WARNING, "SCARD: unexpected SREC/Kc " + "length (%d %d, expected 4 8)", + buf[0], buf[5]); + } + memcpy(sres, buf + 1, 4); + memcpy(kc, buf + 6, 8); + } + + wpa_hexdump(MSG_DEBUG, "SCARD: GSM auth - SRES", sres, 4); + wpa_hexdump(MSG_DEBUG, "SCARD: GSM auth - Kc", kc, 8); + + return 0; +} + + +int scard_umts_auth(struct scard_data *scard, unsigned char *rand, + unsigned char *autn, unsigned char *res, size_t *res_len, + unsigned char *ik, unsigned char *ck, unsigned char *auts) +{ + unsigned char cmd[5 + 1 + AKA_RAND_LEN + 1 + AKA_AUTN_LEN] = + { USIM_CMD_RUN_UMTS_ALG }; + int cmdlen; + unsigned char get_resp[5] = { USIM_CMD_GET_RESPONSE }; + unsigned char resp[3], buf[64], *pos, *end; + size_t len; + long ret; + + if (scard == NULL) + return -1; + + if (scard->sim_type == SCARD_GSM_SIM) { + wpa_printf(MSG_ERROR, "SCARD: Non-USIM card - cannot do UMTS " + "auth"); + return -1; + } + + wpa_hexdump(MSG_DEBUG, "SCARD: UMTS auth - RAND", rand, AKA_RAND_LEN); + wpa_hexdump(MSG_DEBUG, "SCARD: UMTS auth - AUTN", autn, AKA_AUTN_LEN); + cmdlen = 5 + 1 + AKA_RAND_LEN + 1 + AKA_AUTN_LEN; + cmd[5] = AKA_RAND_LEN; + memcpy(cmd + 6, rand, AKA_RAND_LEN); + cmd[6 + AKA_RAND_LEN] = AKA_AUTN_LEN; + memcpy(cmd + 6 + AKA_RAND_LEN + 1, autn, AKA_AUTN_LEN); + + len = sizeof(resp); + ret = scard_transmit(scard, cmd, sizeof(cmd), resp, &len); + if (ret != SCARD_S_SUCCESS) + return -1; + + if (len >= 0 && len <= sizeof(resp)) + wpa_hexdump(MSG_DEBUG, "SCARD: UMTS alg response", resp, len); + + if (len == 2 && resp[0] == 0x98 && resp[1] == 0x62) { + wpa_printf(MSG_WARNING, "SCARD: UMTS auth failed - " + "MAC != XMAC"); + return -1; + } else if (len != 2 || resp[0] != 0x61) { + wpa_printf(MSG_WARNING, "SCARD: unexpected response for UMTS " + "auth request (len=%d resp=%02x %02x)", + len, resp[0], resp[1]); + return -1; + } + get_resp[4] = resp[1]; + + len = sizeof(buf); + ret = scard_transmit(scard, get_resp, sizeof(get_resp), buf, &len); + if (ret != SCARD_S_SUCCESS || len < 0 || len > sizeof(buf)) + return -1; + + wpa_hexdump(MSG_DEBUG, "SCARD: UMTS get response result", buf, len); + if (len >= 2 + AKA_AUTS_LEN && buf[0] == 0xdc && + buf[1] == AKA_AUTS_LEN) { + wpa_printf(MSG_DEBUG, "SCARD: UMTS Synchronization-Failure"); + memcpy(auts, buf + 2, AKA_AUTS_LEN); + wpa_hexdump(MSG_DEBUG, "SCARD: AUTS", auts, AKA_AUTS_LEN); + return -2; + } else if (len >= 6 + IK_LEN + CK_LEN && buf[0] == 0xdb) { + pos = buf + 1; + end = buf + len; + + /* RES */ + if (pos[0] > RES_MAX_LEN || pos + pos[0] > end) { + wpa_printf(MSG_DEBUG, "SCARD: Invalid RES"); + return -1; + } + *res_len = *pos++; + memcpy(res, pos, *res_len); + pos += *res_len; + wpa_hexdump(MSG_DEBUG, "SCARD: RES", res, *res_len); + + /* CK */ + if (pos[0] != CK_LEN || pos + CK_LEN > end) { + wpa_printf(MSG_DEBUG, "SCARD: Invalid CK"); + return -1; + } + pos++; + memcpy(ck, pos, CK_LEN); + pos += CK_LEN; + wpa_hexdump(MSG_DEBUG, "SCARD: CK", ck, CK_LEN); + + /* IK */ + if (pos[0] != IK_LEN || pos + IK_LEN > end) { + wpa_printf(MSG_DEBUG, "SCARD: Invalid IK"); + return -1; + } + pos++; + memcpy(ik, pos, IK_LEN); + pos += IK_LEN; + wpa_hexdump(MSG_DEBUG, "SCARD: IK", ik, IK_LEN); + + return 0; + } + + wpa_printf(MSG_DEBUG, "SCARD: Unrecognized response"); + return -1; +} diff --git a/contrib/wpa_supplicant/pcsc_funcs.h b/contrib/wpa_supplicant/pcsc_funcs.h new file mode 100644 index 0000000..1b64fa2 --- /dev/null +++ b/contrib/wpa_supplicant/pcsc_funcs.h @@ -0,0 +1,50 @@ +#ifndef PCSC_FUNCS_H +#define PCSC_FUNCS_H + +/* GSM files + * File type in first octet: + * 3F = Master File + * 7F = Dedicated File + * 2F = Elementary File under the Master File + * 6F = Elementary File under a Dedicated File + */ +#define SCARD_FILE_MF 0x3F00 +#define SCARD_FILE_GSM_DF 0x7F20 +#define SCARD_FILE_UMTS_DF 0x7F50 +#define SCARD_FILE_GSM_EF_IMSI 0x6F07 +#define SCARD_FILE_EF_ICCID 0x2FE2 +#define SCARD_FILE_EF_CK 0x6FE1 +#define SCARD_FILE_EF_IK 0x6FE2 + +#define SCARD_CHV1_OFFSET 13 +#define SCARD_CHV1_FLAG 0x80 + +typedef enum { + SCARD_GSM_SIM_ONLY, + SCARD_USIM_ONLY, + SCARD_TRY_BOTH +} scard_sim_type; + + +#ifdef PCSC_FUNCS +struct scard_data * scard_init(scard_sim_type sim_type, char *pin); +void scard_deinit(struct scard_data *scard); + +int scard_get_imsi(struct scard_data *scard, char *imsi, size_t *len); +int scard_gsm_auth(struct scard_data *scard, unsigned char *rand, + unsigned char *sres, unsigned char *kc); +int scard_umts_auth(struct scard_data *scard, unsigned char *rand, + unsigned char *autn, unsigned char *res, size_t *res_len, + unsigned char *ik, unsigned char *ck, unsigned char *auts); + +#else /* PCSC_FUNCS */ + +#define scard_init(s, p) NULL +#define scard_deinit(s) do { } while (0) +#define scard_get_imsi(s, i, l) -1 +#define scard_gsm_auth(s, r, s2, k) -1 +#define scard_umts_auth(s, r, a, r2, rl, i, c, a2) -1 + +#endif /* PCSC_FUNCS */ + +#endif /* PCSC_FUNCS_H */ diff --git a/contrib/wpa_supplicant/preauth_test.c b/contrib/wpa_supplicant/preauth_test.c new file mode 100644 index 0000000..13741bb --- /dev/null +++ b/contrib/wpa_supplicant/preauth_test.c @@ -0,0 +1,396 @@ +/* + * WPA Supplicant - test code for pre-authentication + * Copyright (c) 2003-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * + * IEEE 802.1X Supplicant test code (to be used in place of wpa_supplicant.c. + * Not used in production version. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <ctype.h> +#include <string.h> +#include <signal.h> +#include <netinet/in.h> +#include <assert.h> +#include <arpa/inet.h> + +#include "common.h" +#include "config.h" +#include "eapol_sm.h" +#include "eloop.h" +#include "wpa.h" +#include "eap.h" +#include "wpa_supplicant.h" +#include "wpa_supplicant_i.h" +#include "l2_packet.h" +#include "ctrl_iface.h" +#include "pcsc_funcs.h" + + +extern int wpa_debug_level; +extern int wpa_debug_show_keys; + +void wpa_msg(struct wpa_supplicant *wpa_s, int level, char *fmt, ...) +{ + va_list ap; + char *buf; + const int buflen = 2048; + int len; + + buf = malloc(buflen); + if (buf == NULL) { + printf("Failed to allocate message buffer for:\n"); + va_start(ap, fmt); + vprintf(fmt, ap); + printf("\n"); + va_end(ap); + return; + } + va_start(ap, fmt); + len = vsnprintf(buf, buflen, fmt, ap); + va_end(ap); + wpa_printf(level, "%s", buf); + wpa_supplicant_ctrl_iface_send(wpa_s, level, buf, len); + free(buf); +} + + +void wpa_supplicant_event(struct wpa_supplicant *wpa_s, wpa_event_type event, + union wpa_event_data *data) +{ +} + + +int rsn_preauth_init(struct wpa_supplicant *wpa_s, u8 *dst) +{ + return -1; +} + + +void rsn_preauth_deinit(struct wpa_supplicant *wpa_s) +{ +} + + +int pmksa_cache_list(struct wpa_supplicant *wpa_s, char *buf, size_t len) +{ + return 0; +} + + +int wpa_get_mib(struct wpa_supplicant *wpa_s, char *buf, size_t buflen) +{ + return 0; +} + + +void wpa_supplicant_req_scan(struct wpa_supplicant *wpa_s, int sec, int usec) +{ +} + + +const char * wpa_ssid_txt(u8 *ssid, size_t ssid_len) +{ + return NULL; +} + + +int wpa_supplicant_reload_configuration(struct wpa_supplicant *wpa_s) +{ + return -1; +} + + +static int eapol_test_eapol_send(void *ctx, int type, u8 *buf, size_t len) +{ + struct wpa_supplicant *wpa_s = ctx; + u8 *msg; + size_t msglen; + struct l2_ethhdr *ethhdr; + struct ieee802_1x_hdr *hdr; + int res; + + printf("WPA: wpa_eapol_send(type=%d len=%d)\n", type, len); + + if (wpa_s->l2_preauth == NULL) + return -1; + + msglen = sizeof(*ethhdr) + sizeof(*hdr) + len; + msg = malloc(msglen); + if (msg == NULL) + return -1; + + ethhdr = (struct l2_ethhdr *) msg; + memcpy(ethhdr->h_dest, wpa_s->preauth_bssid, ETH_ALEN); + memcpy(ethhdr->h_source, wpa_s->own_addr, ETH_ALEN); + ethhdr->h_proto = htons(ETH_P_RSN_PREAUTH); + + hdr = (struct ieee802_1x_hdr *) (ethhdr + 1); + hdr->version = wpa_s->conf->eapol_version; + hdr->type = type; + hdr->length = htons(len); + + memcpy((u8 *) (hdr + 1), buf, len); + + wpa_hexdump(MSG_MSGDUMP, "TX EAPOL (preauth)", msg, msglen); + res = l2_packet_send(wpa_s->l2_preauth, msg, msglen); + free(msg); + return res; +} + + +static void eapol_test_eapol_done_cb(void *ctx) +{ + printf("WPA: EAPOL processing complete\n"); +} + + +static void eapol_sm_cb(struct eapol_sm *eapol, int success, void *ctx) +{ + printf("eapol_sm_cb: success=%d\n", success); + eloop_terminate(); +} + + +static int test_eapol(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) +{ + struct eapol_config eapol_conf; + struct eapol_ctx *ctx; + + ctx = malloc(sizeof(*ctx)); + if (ctx == NULL) { + printf("Failed to allocate EAPOL context.\n"); + return -1; + } + memset(ctx, 0, sizeof(*ctx)); + ctx->ctx = wpa_s; + ctx->msg_ctx = wpa_s; + ctx->scard_ctx = wpa_s->scard; + ctx->cb = eapol_sm_cb; + ctx->cb_ctx = wpa_s; + ctx->preauth = 0; + ctx->eapol_done_cb = eapol_test_eapol_done_cb; + ctx->eapol_send = eapol_test_eapol_send; + + wpa_s->preauth_eapol = eapol_sm_init(ctx); + if (wpa_s->preauth_eapol == NULL) { + free(ctx); + printf("Failed to initialize EAPOL state machines.\n"); + return -1; + } + + wpa_s->current_ssid = ssid; + memset(&eapol_conf, 0, sizeof(eapol_conf)); + eapol_conf.accept_802_1x_keys = 1; + eapol_conf.required_keys = 0; + eapol_conf.workaround = ssid->eap_workaround; + eapol_sm_notify_config(wpa_s->preauth_eapol, ssid, &eapol_conf); + + + eapol_sm_notify_portValid(wpa_s->preauth_eapol, FALSE); + /* 802.1X::portControl = Auto */ + eapol_sm_notify_portEnabled(wpa_s->preauth_eapol, TRUE); + + return 0; +} + + +static void test_eapol_clean(struct wpa_supplicant *wpa_s) +{ + l2_packet_deinit(wpa_s->l2_preauth); + eapol_sm_deinit(wpa_s->preauth_eapol); + wpa_s->preauth_eapol = NULL; + scard_deinit(wpa_s->scard); + wpa_supplicant_ctrl_iface_deinit(wpa_s); + wpa_config_free(wpa_s->conf); +} + + +static void eapol_test_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + printf("EAPOL test timed out\n"); + wpa_s->auth_timed_out = 1; + eloop_terminate(); +} + + +static void wpa_supplicant_imsi_identity(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid) +{ + if (ssid->identity == NULL && wpa_s->imsi) { + ssid->identity = malloc(1 + wpa_s->imsi_len); + if (ssid->identity) { + ssid->identity[0] = '1'; + memcpy(ssid->identity + 1, wpa_s->imsi, + wpa_s->imsi_len); + ssid->identity_len = 1 + wpa_s->imsi_len; + wpa_hexdump_ascii(MSG_DEBUG, "permanent identity from " + "IMSI", ssid->identity, + ssid->identity_len); + } + } +} + + +static void wpa_supplicant_scard_init(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid) +{ + char buf[100]; + size_t len; + + if (ssid->pcsc == NULL) + return; + if (wpa_s->scard != NULL) { + wpa_supplicant_imsi_identity(wpa_s, ssid); + return; + } + wpa_printf(MSG_DEBUG, "Selected network is configured to use SIM - " + "initialize PCSC"); + wpa_s->scard = scard_init(SCARD_TRY_BOTH, ssid->pin); + if (wpa_s->scard == NULL) { + wpa_printf(MSG_WARNING, "Failed to initialize SIM " + "(pcsc-lite)"); + /* TODO: what to do here? */ + return; + } + eapol_sm_register_scard_ctx(wpa_s->preauth_eapol, wpa_s->scard); + + len = sizeof(buf); + if (scard_get_imsi(wpa_s->scard, buf, &len)) { + wpa_printf(MSG_WARNING, "Failed to get IMSI from SIM"); + /* TODO: what to do here? */ + return; + } + + wpa_hexdump(MSG_DEBUG, "IMSI", (u8 *) buf, len); + free(wpa_s->imsi); + wpa_s->imsi = malloc(len); + if (wpa_s->imsi) { + memcpy(wpa_s->imsi, buf, len); + wpa_s->imsi_len = len; + wpa_supplicant_imsi_identity(wpa_s, ssid); + } +} + + +static void rsn_preauth_receive(void *ctx, unsigned char *src_addr, + unsigned char *buf, size_t len) +{ + struct wpa_supplicant *wpa_s = ctx; + + wpa_printf(MSG_DEBUG, "RX pre-auth from " MACSTR, MAC2STR(src_addr)); + wpa_hexdump(MSG_MSGDUMP, "RX pre-auth", buf, len); + + if (wpa_s->preauth_eapol == NULL || + memcmp(wpa_s->preauth_bssid, "\x00\x00\x00\x00\x00\x00", + ETH_ALEN) == 0 || + memcmp(wpa_s->preauth_bssid, src_addr, ETH_ALEN) != 0) { + wpa_printf(MSG_WARNING, "RSN pre-auth frame received from " + "unexpected source " MACSTR " - dropped", + MAC2STR(src_addr)); + return; + } + + eapol_sm_rx_eapol(wpa_s->preauth_eapol, src_addr, buf, len); +} + + +static void wpa_init_conf(struct wpa_supplicant *wpa_s, const char *target, + const char *ifname) +{ + strncpy(wpa_s->ifname, ifname, sizeof(wpa_s->ifname)); + + if (hwaddr_aton(target, wpa_s->preauth_bssid)) { + printf("Failed to parse target address '%s'.\n", target); + exit(-1); + } + + wpa_s->l2_preauth = l2_packet_init(wpa_s->ifname, NULL, + ETH_P_RSN_PREAUTH, + rsn_preauth_receive, wpa_s); + if (wpa_s->l2_preauth == NULL) { + wpa_printf(MSG_WARNING, "RSN: Failed to initialize L2 packet " + "processing for pre-authentication"); + exit(-1); + } + + if (l2_packet_get_own_addr(wpa_s->l2_preauth, wpa_s->own_addr)) { + wpa_printf(MSG_WARNING, "Failed to get own L2 address\n"); + exit(-1); + } +} + + +static void eapol_test_terminate(int sig, void *eloop_ctx, + void *signal_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + wpa_msg(wpa_s, MSG_INFO, "Signal %d received - terminating", sig); + eloop_terminate(); +} + + +int main(int argc, char *argv[]) +{ + struct wpa_supplicant wpa_s; + int ret = 1; + + wpa_debug_level = 0; + wpa_debug_show_keys = 1; + + if (argc != 4) { + printf("usage: eapol_test <conf> <target MAC address> " + "<ifname>\n"); + return -1; + } + + eloop_init(&wpa_s); + + memset(&wpa_s, 0, sizeof(wpa_s)); + wpa_s.conf = wpa_config_read(argv[1]); + if (wpa_s.conf == NULL) { + printf("Failed to parse configuration file '%s'.\n", argv[1]); + return -1; + } + if (wpa_s.conf->ssid == NULL) { + printf("No networks defined.\n"); + return -1; + } + + wpa_init_conf(&wpa_s, argv[2], argv[3]); + if (wpa_supplicant_ctrl_iface_init(&wpa_s)) { + printf("Failed to initialize control interface '%s'.\n" + "You may have another eapol_test process already " + "running or the file was\n" + "left by an unclean termination of eapol_test in " + "which case you will need\n" + "to manually remove this file before starting " + "eapol_test again.\n", + wpa_s.conf->ctrl_interface); + return -1; + } + wpa_supplicant_scard_init(&wpa_s, wpa_s.conf->ssid); + + if (test_eapol(&wpa_s, wpa_s.conf->ssid)) + return -1; + + eloop_register_timeout(30, 0, eapol_test_timeout, &wpa_s, NULL); + eloop_register_signal(SIGINT, eapol_test_terminate, NULL); + eloop_register_signal(SIGTERM, eapol_test_terminate, NULL); + eloop_register_signal(SIGHUP, eapol_test_terminate, NULL); + eloop_run(); + + if (wpa_s.auth_timed_out) + ret = -2; + else + ret = 0; + + test_eapol_clean(&wpa_s); + + eloop_destroy(); + + return ret; +} diff --git a/contrib/wpa_supplicant/radius_client.c b/contrib/wpa_supplicant/radius_client.c new file mode 100644 index 0000000..91ab3c7 --- /dev/null +++ b/contrib/wpa_supplicant/radius_client.c @@ -0,0 +1,674 @@ +/* + * Host AP (software wireless LAN access point) user space daemon for + * Host AP kernel driver / RADIUS client / modified for eapol_test + * Copyright (c) 2002-2004, Jouni Malinen <jkmaline@cc.hut.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. See README and COPYING for + * more details. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <netinet/in.h> +#include <string.h> +#include <time.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <arpa/inet.h> + +#include "common.h" +#include "wpa.h" +#include "config_ssid.h" +#include "wpa_supplicant.h" +#include "wpa_supplicant_i.h" +#include "radius.h" +#include "radius_client.h" +#include "eloop.h" +#include "l2_packet.h" + +#include "wpa_supplicant.h" +#define hostapd_logger(h, a, m, l, t...) wpa_printf(MSG_DEBUG, t) +#define HOSTAPD_DEBUG(l, a...) wpa_printf(MSG_DEBUG, a) +#define HOSTAPD_DEBUG_COND(l) 1 + +/* Defaults for RADIUS retransmit values (exponential backoff) */ +#define RADIUS_CLIENT_FIRST_WAIT 1 /* seconds */ +#define RADIUS_CLIENT_MAX_WAIT 120 /* seconds */ +#define RADIUS_CLIENT_MAX_RETRIES 10 /* maximum number of retransmit attempts + * before entry is removed from retransmit + * list */ +#define RADIUS_CLIENT_MAX_ENTRIES 30 /* maximum number of entries in retransmit + * list (oldest will be removed, if this + * limit is exceeded) */ +#define RADIUS_CLIENT_NUM_FAILOVER 4 /* try to change RADIUS server after this + * many failed retry attempts */ + + + +static int +radius_change_server(struct wpa_supplicant *wpa_s, struct hostapd_radius_server *nserv, + struct hostapd_radius_server *oserv, + int sock, int auth); + + +static void radius_client_msg_free(struct radius_msg_list *req) +{ + radius_msg_free(req->msg); + free(req->msg); + free(req); +} + + +int radius_client_register(struct wpa_supplicant *wpa_s, RadiusType msg_type, + RadiusRxResult (*handler)(struct wpa_supplicant *wpa_s, + struct radius_msg *msg, + struct radius_msg *req, + u8 *shared_secret, + size_t shared_secret_len, + void *data), + void *data) +{ + struct radius_rx_handler **handlers, *newh; + size_t *num; + + if (msg_type == RADIUS_ACCT) { + handlers = &wpa_s->radius->acct_handlers; + num = &wpa_s->radius->num_acct_handlers; + } else { + handlers = &wpa_s->radius->auth_handlers; + num = &wpa_s->radius->num_auth_handlers; + } + + newh = (struct radius_rx_handler *) + realloc(*handlers, + (*num + 1) * sizeof(struct radius_rx_handler)); + if (newh == NULL) + return -1; + + newh[*num].handler = handler; + newh[*num].data = data; + (*num)++; + *handlers = newh; + + return 0; +} + + +static int radius_client_retransmit(struct wpa_supplicant *wpa_s, + struct radius_msg_list *entry, time_t now) +{ + int s; + + if (entry->msg_type == RADIUS_ACCT || + entry->msg_type == RADIUS_ACCT_INTERIM) + s = wpa_s->radius->acct_serv_sock; + else + s = wpa_s->radius->auth_serv_sock; + + /* retransmit; remove entry if too many attempts */ + entry->attempts++; + HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "Resending RADIUS message (id=%d)" + "\n", entry->msg->hdr->identifier); + + if (send(s, entry->msg->buf, entry->msg->buf_used, 0) < 0) + perror("send[RADIUS]"); + + entry->next_try = now + entry->next_wait; + entry->next_wait *= 2; + if (entry->next_wait > RADIUS_CLIENT_MAX_WAIT) + entry->next_wait = RADIUS_CLIENT_MAX_WAIT; + if (entry->attempts >= RADIUS_CLIENT_MAX_RETRIES) { + printf("Removing un-ACKed RADIUS message due to too many " + "failed retransmit attempts\n"); + return 1; + } + + return 0; +} + + +static void radius_client_timer(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + time_t now, first; + struct radius_msg_list *entry, *prev, *tmp; + int auth_failover = 0, acct_failover = 0; + + entry = wpa_s->radius->msgs; + if (!entry) + return; + + time(&now); + first = 0; + + prev = NULL; + while (entry) { + if (now >= entry->next_try && + radius_client_retransmit(wpa_s, entry, now)) { + if (prev) + prev->next = entry->next; + else + wpa_s->radius->msgs = entry->next; + + tmp = entry; + entry = entry->next; + radius_client_msg_free(tmp); + wpa_s->radius->num_msgs--; + continue; + } + + if (entry->attempts > RADIUS_CLIENT_NUM_FAILOVER) { + if (entry->msg_type == RADIUS_ACCT || + entry->msg_type == RADIUS_ACCT_INTERIM) + acct_failover++; + else + auth_failover++; + } + + if (first == 0 || entry->next_try < first) + first = entry->next_try; + + prev = entry; + entry = entry->next; + } + + if (wpa_s->radius->msgs) { + if (first < now) + first = now; + eloop_register_timeout(first - now, 0, + radius_client_timer, wpa_s, NULL); + } + + if (auth_failover && wpa_s->num_auth_servers > 1) { + struct hostapd_radius_server *next, *old; + old = wpa_s->auth_server; + hostapd_logger(wpa_s, NULL, HOSTAPD_MODULE_RADIUS, + HOSTAPD_LEVEL_NOTICE, + "No response from Authentication server " + "%s:%d - failover", + inet_ntoa(old->addr), old->port); + + next = old + 1; + if (next > &(wpa_s->auth_servers + [wpa_s->num_auth_servers - 1])) + next = wpa_s->auth_servers; + wpa_s->auth_server = next; + radius_change_server(wpa_s, next, old, + wpa_s->radius->auth_serv_sock, 1); + } + + if (acct_failover && wpa_s->num_acct_servers > 1) { + struct hostapd_radius_server *next, *old; + old = wpa_s->acct_server; + hostapd_logger(wpa_s, NULL, HOSTAPD_MODULE_RADIUS, + HOSTAPD_LEVEL_NOTICE, + "No response from Accounting server " + "%s:%d - failover", + inet_ntoa(old->addr), old->port); + next = old + 1; + if (next > &wpa_s->acct_servers + [wpa_s->num_acct_servers - 1]) + next = wpa_s->acct_servers; + wpa_s->acct_server = next; + radius_change_server(wpa_s, next, old, + wpa_s->radius->acct_serv_sock, 0); + } +} + + +static void radius_client_list_add(struct wpa_supplicant *wpa_s, struct radius_msg *msg, + RadiusType msg_type, u8 *shared_secret, + size_t shared_secret_len, u8 *addr) +{ + struct radius_msg_list *entry, *prev; + + if (eloop_terminated()) { + /* No point in adding entries to retransmit queue since event + * loop has already been terminated. */ + radius_msg_free(msg); + free(msg); + return; + } + + entry = malloc(sizeof(*entry)); + if (entry == NULL) { + printf("Failed to add RADIUS packet into retransmit list\n"); + radius_msg_free(msg); + free(msg); + return; + } + + memset(entry, 0, sizeof(*entry)); + if (addr) + memcpy(entry->addr, addr, ETH_ALEN); + entry->msg = msg; + entry->msg_type = msg_type; + entry->shared_secret = shared_secret; + entry->shared_secret_len = shared_secret_len; + time(&entry->first_try); + entry->next_try = entry->first_try + RADIUS_CLIENT_FIRST_WAIT; + entry->attempts = 1; + entry->next_wait = RADIUS_CLIENT_FIRST_WAIT * 2; + + if (!wpa_s->radius->msgs) + eloop_register_timeout(RADIUS_CLIENT_FIRST_WAIT, 0, + radius_client_timer, wpa_s, NULL); + + entry->next = wpa_s->radius->msgs; + wpa_s->radius->msgs = entry; + + if (wpa_s->radius->num_msgs >= RADIUS_CLIENT_MAX_ENTRIES) { + printf("Removing the oldest un-ACKed RADIUS packet due to " + "retransmit list limits.\n"); + prev = NULL; + while (entry->next) { + prev = entry; + entry = entry->next; + } + if (prev) { + prev->next = NULL; + radius_client_msg_free(entry); + } + } else + wpa_s->radius->num_msgs++; +} + + +static void radius_client_list_del(struct wpa_supplicant *wpa_s, + RadiusType msg_type, u8 *addr) +{ + struct radius_msg_list *entry, *prev, *tmp; + + if (addr == NULL) + return; + + entry = wpa_s->radius->msgs; + prev = NULL; + while (entry) { + if (entry->msg_type == msg_type && + memcmp(entry->addr, addr, ETH_ALEN) == 0) { + if (prev) + prev->next = entry->next; + else + wpa_s->radius->msgs = entry->next; + tmp = entry; + entry = entry->next; + HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, + "Removing matching RADIUS message for " + MACSTR "\n", MAC2STR(addr)); + radius_client_msg_free(tmp); + wpa_s->radius->num_msgs--; + continue; + } + prev = entry; + entry = entry->next; + } +} + + +int radius_client_send(struct wpa_supplicant *wpa_s, struct radius_msg *msg, + RadiusType msg_type, u8 *addr) +{ + u8 *shared_secret; + size_t shared_secret_len; + char *name; + int s, res; + + if (msg_type == RADIUS_ACCT_INTERIM) { + /* Remove any pending interim acct update for the same STA. */ + radius_client_list_del(wpa_s, msg_type, addr); + } + + if (msg_type == RADIUS_ACCT || msg_type == RADIUS_ACCT_INTERIM) { + shared_secret = wpa_s->acct_server->shared_secret; + shared_secret_len = wpa_s->acct_server->shared_secret_len; + radius_msg_finish_acct(msg, shared_secret, shared_secret_len); + name = "accounting"; + s = wpa_s->radius->acct_serv_sock; + } else { + shared_secret = wpa_s->auth_server->shared_secret; + shared_secret_len = wpa_s->auth_server->shared_secret_len; + radius_msg_finish(msg, shared_secret, shared_secret_len); + name = "authentication"; + s = wpa_s->radius->auth_serv_sock; + } + + HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, + "Sending RADIUS message to %s server\n", name); + if (HOSTAPD_DEBUG_COND(HOSTAPD_DEBUG_MSGDUMPS)) + radius_msg_dump(msg); + + res = send(s, msg->buf, msg->buf_used, 0); + if (res < 0) + perror("send[RADIUS]"); + + radius_client_list_add(wpa_s, msg, msg_type, shared_secret, + shared_secret_len, addr); + + return res; +} + + +static void radius_client_receive(int sock, void *eloop_ctx, void *sock_ctx) +{ + struct wpa_supplicant *wpa_s = (struct wpa_supplicant *) eloop_ctx; + RadiusType msg_type = (RadiusType) sock_ctx; + int len, i; + unsigned char buf[3000]; + struct radius_msg *msg; + struct radius_rx_handler *handlers; + size_t num_handlers; + struct radius_msg_list *req, *prev_req; + + len = recv(sock, buf, sizeof(buf), 0); + if (len < 0) { + perror("recv[RADIUS]"); + return; + } + HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, + "Received %d bytes from RADIUS server\n", len); + if (len == sizeof(buf)) { + printf("Possibly too long UDP frame for our buffer - " + "dropping it\n"); + return; + } + + msg = radius_msg_parse(buf, len); + if (msg == NULL) { + printf("Parsing incoming RADIUS frame failed\n"); + return; + } + + HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, + "Received RADIUS message\n"); + if (HOSTAPD_DEBUG_COND(HOSTAPD_DEBUG_MSGDUMPS)) + radius_msg_dump(msg); + + if (msg_type == RADIUS_ACCT) { + handlers = wpa_s->radius->acct_handlers; + num_handlers = wpa_s->radius->num_acct_handlers; + } else { + handlers = wpa_s->radius->auth_handlers; + num_handlers = wpa_s->radius->num_auth_handlers; + } + + prev_req = NULL; + req = wpa_s->radius->msgs; + while (req) { + /* TODO: also match by src addr:port of the packet when using + * alternative RADIUS servers (?) */ + if ((req->msg_type == msg_type || + (req->msg_type == RADIUS_ACCT_INTERIM && + msg_type == RADIUS_ACCT)) && + req->msg->hdr->identifier == msg->hdr->identifier) + break; + + prev_req = req; + req = req->next; + } + + if (req == NULL) { + HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, + "No matching RADIUS request found (type=%d " + "id=%d) - dropping packet\n", + msg_type, msg->hdr->identifier); + goto fail; + } + + /* Remove ACKed RADIUS packet from retransmit list */ + if (prev_req) + prev_req->next = req->next; + else + wpa_s->radius->msgs = req->next; + wpa_s->radius->num_msgs--; + + for (i = 0; i < num_handlers; i++) { + RadiusRxResult res; + res = handlers[i].handler(wpa_s, msg, req->msg, + req->shared_secret, + req->shared_secret_len, + handlers[i].data); + switch (res) { + case RADIUS_RX_PROCESSED: + radius_msg_free(msg); + free(msg); + /* continue */ + case RADIUS_RX_QUEUED: + radius_client_msg_free(req); + return; + case RADIUS_RX_UNKNOWN: + /* continue with next handler */ + break; + } + } + + printf("No RADIUS RX handler found (type=%d code=%d id=%d) - dropping " + "packet\n", msg_type, msg->hdr->code, msg->hdr->identifier); + radius_client_msg_free(req); + + fail: + radius_msg_free(msg); + free(msg); +} + + +u8 radius_client_get_id(struct wpa_supplicant *wpa_s) +{ + struct radius_msg_list *entry, *prev, *remove; + u8 id = wpa_s->radius->next_radius_identifier++; + + /* remove entries with matching id from retransmit list to avoid + * using new reply from the RADIUS server with an old request */ + entry = wpa_s->radius->msgs; + prev = NULL; + while (entry) { + if (entry->msg->hdr->identifier == id) { + HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, + "Removing pending RADIUS message, since " + "its id (%d) is reused\n", id); + if (prev) + prev->next = entry->next; + else + wpa_s->radius->msgs = entry->next; + remove = entry; + } else + remove = NULL; + prev = entry; + entry = entry->next; + + if (remove) + radius_client_msg_free(remove); + } + + return id; +} + + +void radius_client_flush(struct wpa_supplicant *wpa_s) +{ + struct radius_msg_list *entry, *prev; + + if (!wpa_s->radius) + return; + + eloop_cancel_timeout(radius_client_timer, wpa_s, NULL); + + entry = wpa_s->radius->msgs; + wpa_s->radius->msgs = NULL; + wpa_s->radius->num_msgs = 0; + while (entry) { + prev = entry; + entry = entry->next; + radius_client_msg_free(prev); + } +} + + +static int +radius_change_server(struct wpa_supplicant *wpa_s, struct hostapd_radius_server *nserv, + struct hostapd_radius_server *oserv, + int sock, int auth) +{ + struct sockaddr_in serv; + + hostapd_logger(wpa_s, NULL, HOSTAPD_MODULE_RADIUS, HOSTAPD_LEVEL_INFO, + "%s server %s:%d", + auth ? "Authentication" : "Accounting", + inet_ntoa(nserv->addr), nserv->port); + + if (!oserv || nserv->shared_secret_len != oserv->shared_secret_len || + memcmp(nserv->shared_secret, oserv->shared_secret, + nserv->shared_secret_len) != 0) { + /* Pending RADIUS packets used different shared + * secret, so they would need to be modified. Could + * update all message authenticators and + * User-Passwords, etc. and retry with new server. For + * now, just drop all pending packets. */ + radius_client_flush(wpa_s); + } else { + /* Reset retry counters for the new server */ + struct radius_msg_list *entry; + entry = wpa_s->radius->msgs; + while (entry) { + entry->next_try = entry->first_try + + RADIUS_CLIENT_FIRST_WAIT; + entry->attempts = 0; + entry->next_wait = RADIUS_CLIENT_FIRST_WAIT * 2; + entry = entry->next; + } + if (wpa_s->radius->msgs) { + eloop_cancel_timeout(radius_client_timer, wpa_s, NULL); + eloop_register_timeout(RADIUS_CLIENT_FIRST_WAIT, 0, + radius_client_timer, wpa_s, + NULL); + } + } + + memset(&serv, 0, sizeof(serv)); + serv.sin_family = AF_INET; + serv.sin_addr.s_addr = nserv->addr.s_addr; + serv.sin_port = htons(nserv->port); + + if (connect(sock, (struct sockaddr *) &serv, sizeof(serv)) < 0) { + perror("connect[radius]"); + return -1; + } + + return 0; +} + + +static void radius_retry_primary_timer(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + struct hostapd_radius_server *oserv; + + if (wpa_s->radius->auth_serv_sock >= 0 && wpa_s->auth_servers && + wpa_s->auth_server != wpa_s->auth_servers) { + oserv = wpa_s->auth_server; + wpa_s->auth_server = wpa_s->auth_servers; + radius_change_server(wpa_s, wpa_s->auth_server, oserv, + wpa_s->radius->auth_serv_sock, 1); + } + + if (wpa_s->radius->acct_serv_sock >= 0 && wpa_s->acct_servers && + wpa_s->acct_server != wpa_s->acct_servers) { + oserv = wpa_s->acct_server; + wpa_s->acct_server = wpa_s->acct_servers; + radius_change_server(wpa_s, wpa_s->acct_server, oserv, + wpa_s->radius->acct_serv_sock, 0); + } + + if (wpa_s->radius_retry_primary_interval) + eloop_register_timeout(wpa_s-> + radius_retry_primary_interval, 0, + radius_retry_primary_timer, wpa_s, NULL); +} + + +static int radius_client_init_auth(struct wpa_supplicant *wpa_s) +{ + wpa_s->radius->auth_serv_sock = socket(PF_INET, SOCK_DGRAM, 0); + if (wpa_s->radius->auth_serv_sock < 0) { + perror("socket[PF_INET,SOCK_DGRAM]"); + return -1; + } + + radius_change_server(wpa_s, wpa_s->auth_server, NULL, + wpa_s->radius->auth_serv_sock, 1); + + if (eloop_register_read_sock(wpa_s->radius->auth_serv_sock, + radius_client_receive, wpa_s, + (void *) RADIUS_AUTH)) { + printf("Could not register read socket for authentication " + "server\n"); + return -1; + } + + return 0; +} + + +static int radius_client_init_acct(struct wpa_supplicant *wpa_s) +{ + wpa_s->radius->acct_serv_sock = socket(PF_INET, SOCK_DGRAM, 0); + if (wpa_s->radius->acct_serv_sock < 0) { + perror("socket[PF_INET,SOCK_DGRAM]"); + return -1; + } + + radius_change_server(wpa_s, wpa_s->acct_server, NULL, + wpa_s->radius->acct_serv_sock, 0); + + if (eloop_register_read_sock(wpa_s->radius->acct_serv_sock, + radius_client_receive, wpa_s, + (void *) RADIUS_ACCT)) { + printf("Could not register read socket for accounting " + "server\n"); + return -1; + } + + return 0; +} + + +int radius_client_init(struct wpa_supplicant *wpa_s) +{ + wpa_s->radius = malloc(sizeof(struct radius_client_data)); + if (wpa_s->radius == NULL) + return -1; + + memset(wpa_s->radius, 0, sizeof(struct radius_client_data)); + wpa_s->radius->auth_serv_sock = wpa_s->radius->acct_serv_sock = -1; + + if (wpa_s->auth_server && radius_client_init_auth(wpa_s)) + return -1; + + if (wpa_s->acct_server && radius_client_init_acct(wpa_s)) + return -1; + + if (wpa_s->radius_retry_primary_interval) + eloop_register_timeout(wpa_s->radius_retry_primary_interval, 0, + radius_retry_primary_timer, wpa_s, + NULL); + + return 0; +} + + +void radius_client_deinit(struct wpa_supplicant *wpa_s) +{ + if (!wpa_s->radius) + return; + + eloop_cancel_timeout(radius_retry_primary_timer, wpa_s, NULL); + + radius_client_flush(wpa_s); + free(wpa_s->radius->auth_handlers); + free(wpa_s->radius->acct_handlers); + free(wpa_s->radius); + wpa_s->radius = NULL; +} diff --git a/contrib/wpa_supplicant/radius_client.h b/contrib/wpa_supplicant/radius_client.h new file mode 100644 index 0000000..993f8d0 --- /dev/null +++ b/contrib/wpa_supplicant/radius_client.h @@ -0,0 +1,81 @@ +#ifndef RADIUS_CLIENT_H +#define RADIUS_CLIENT_H + +typedef enum { + RADIUS_AUTH, + RADIUS_ACCT, + RADIUS_ACCT_INTERIM /* used only with radius_client_send(); just like + * RADIUS_ACCT, but removes any pending interim + * RADIUS Accounting packages for the same STA + * before sending the new interim update */ +} RadiusType; + +/* RADIUS message retransmit list */ +struct radius_msg_list { + u8 addr[ETH_ALEN]; /* STA/client address; used to find RADIUS messages + * for the same STA. */ + struct radius_msg *msg; + RadiusType msg_type; + time_t first_try; + time_t next_try; + int attempts; + int next_wait; + + u8 *shared_secret; + size_t shared_secret_len; + + /* TODO: server config with failover to backup server(s) */ + + struct radius_msg_list *next; +}; + + +typedef enum { + RADIUS_RX_PROCESSED, + RADIUS_RX_QUEUED, + RADIUS_RX_UNKNOWN +} RadiusRxResult; + +struct radius_rx_handler { + RadiusRxResult (*handler)(struct wpa_supplicant *wpa_s, + struct radius_msg *msg, + struct radius_msg *req, + u8 *shared_secret, size_t shared_secret_len, + void *data); + void *data; +}; + +struct radius_client_data { + int auth_serv_sock; /* socket for authentication RADIUS messages */ + int acct_serv_sock; /* socket for accounting RADIUS messages */ + + struct radius_rx_handler *auth_handlers; + size_t num_auth_handlers; + struct radius_rx_handler *acct_handlers; + size_t num_acct_handlers; + + struct radius_msg_list *msgs; + size_t num_msgs; + + u8 next_radius_identifier; + u32 acct_session_id_hi; + u32 acct_session_id_lo; +}; + + +int radius_client_register(struct wpa_supplicant *wpa_s, RadiusType msg_type, + RadiusRxResult (*handler) + (struct wpa_supplicant *wpa_s, + struct radius_msg *msg, struct radius_msg *req, + u8 *shared_secret, size_t shared_secret_len, + void *data), + void *data); +int radius_client_send(struct wpa_supplicant *wpa_s, struct radius_msg *msg, + RadiusType msg_type, u8 *addr); +u8 radius_client_get_id(struct wpa_supplicant *wpa_s); + +void radius_client_flush(struct wpa_supplicant *wpa_s); +int radius_client_init(struct wpa_supplicant *wpa_s); +void radius_client_deinit(struct wpa_supplicant *wpa_s); + +#endif /* RADIUS_CLIENT_H */ diff --git a/contrib/wpa_supplicant/rc4.c b/contrib/wpa_supplicant/rc4.c new file mode 100644 index 0000000..97ec1b0 --- /dev/null +++ b/contrib/wpa_supplicant/rc4.c @@ -0,0 +1,63 @@ +/* + * Host AP (software wireless LAN access point) user space daemon for + * Host AP kernel driver / RC4 + * Copyright (c) 2002-2004, Jouni Malinen <jkmaline@cc.hut.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include <stdio.h> +#include "common.h" +#include "rc4.h" + +#define S_SWAP(a,b) do { u8 t = S[a]; S[a] = S[b]; S[b] = t; } while(0) + +void rc4_skip(u8 *key, size_t keylen, size_t skip, u8 *data, size_t data_len) +{ + u32 i, j, k; + u8 S[256], *pos; + int kpos; + + /* Setup RC4 state */ + for (i = 0; i < 256; i++) + S[i] = i; + j = 0; + kpos = 0; + for (i = 0; i < 256; i++) { + j = (j + S[i] + key[kpos]) & 0xff; + kpos++; + if (kpos >= keylen) + kpos = 0; + S_SWAP(i, j); + } + + /* Skip the start of the stream */ + i = j = 0; + for (k = 0; k < skip; k++) { + i = (i + 1) & 0xff; + j = (j + S[i]) & 0xff; + S_SWAP(i, j); + } + + /* Apply RC4 to data */ + pos = data; + for (k = 0; k < data_len; k++) { + i = (i + 1) & 0xff; + j = (j + S[i]) & 0xff; + S_SWAP(i, j); + *pos++ ^= S[(S[i] + S[j]) & 0xff]; + } +} + + +void rc4(u8 *buf, size_t len, u8 *key, size_t key_len) +{ + rc4_skip(key, key_len, 0, buf, len); +} diff --git a/contrib/wpa_supplicant/rc4.h b/contrib/wpa_supplicant/rc4.h new file mode 100644 index 0000000..0e77b7e --- /dev/null +++ b/contrib/wpa_supplicant/rc4.h @@ -0,0 +1,7 @@ +#ifndef RC4_H +#define RC4_H + +void rc4_skip(u8 *key, size_t keylen, size_t skip, u8 *data, size_t data_len); +void rc4(u8 *buf, size_t len, u8 *key, size_t key_len); + +#endif /* RC4_H */ diff --git a/contrib/wpa_supplicant/sha1.c b/contrib/wpa_supplicant/sha1.c new file mode 100644 index 0000000..04943b5 --- /dev/null +++ b/contrib/wpa_supplicant/sha1.c @@ -0,0 +1,910 @@ +/* + * SHA1 hash implementation and interface functions + * Copyright (c) 2003-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include "common.h" +#include "sha1.h" +#include "md5.h" + + +void sha1_mac(const u8 *key, size_t key_len, const u8 *data, size_t data_len, + u8 *mac) +{ + SHA1_CTX context; + SHA1Init(&context); + SHA1Update(&context, key, key_len); + SHA1Update(&context, data, data_len); + SHA1Update(&context, key, key_len); + SHA1Final(mac, &context); +} + + +/* HMAC code is based on RFC 2104 */ +void hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + SHA1_CTX context; + unsigned char k_ipad[65]; /* inner padding - key XORd with ipad */ + unsigned char k_opad[65]; /* outer padding - key XORd with opad */ + unsigned char tk[20]; + int i; + + /* if key is longer than 64 bytes reset it to key = SHA1(key) */ + if (key_len > 64) { + SHA1Init(&context); + SHA1Update(&context, key, key_len); + SHA1Final(tk, &context); + + key = tk; + key_len = 20; + } + + /* the HMAC_SHA1 transform looks like: + * + * SHA1(K XOR opad, SHA1(K XOR ipad, text)) + * + * where K is an n byte key + * ipad is the byte 0x36 repeated 64 times + * opad is the byte 0x5c repeated 64 times + * and text is the data being protected */ + + /* start out by storing key in pads */ + memset(k_ipad, 0, sizeof(k_ipad)); + memset(k_opad, 0, sizeof(k_opad)); + memcpy(k_ipad, key, key_len); + memcpy(k_opad, key, key_len); + + /* XOR key with ipad and opad values */ + for (i = 0; i < 64; i++) { + k_ipad[i] ^= 0x36; + k_opad[i] ^= 0x5c; + } + + /* perform inner SHA1 */ + SHA1Init(&context); /* init context for 1st pass */ + SHA1Update(&context, k_ipad, 64); /* start with inner pad */ + /* then text of datagram; all fragments */ + for (i = 0; i < num_elem; i++) { + SHA1Update(&context, addr[i], len[i]); + } + SHA1Final(mac, &context); /* finish up 1st pass */ + + /* perform outer SHA1 */ + SHA1Init(&context); /* init context for 2nd pass */ + SHA1Update(&context, k_opad, 64); /* start with outer pad */ + SHA1Update(&context, mac, 20); /* then results of 1st hash */ + SHA1Final(mac, &context); /* finish up 2nd pass */ +} + + +void hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len, + u8 *mac) +{ + hmac_sha1_vector(key, key_len, 1, &data, &data_len, mac); +} + + +void sha1_prf(const u8 *key, size_t key_len, const char *label, + const u8 *data, size_t data_len, u8 *buf, size_t buf_len) +{ + u8 zero = 0, counter = 0; + size_t pos, plen; + u8 hash[SHA1_MAC_LEN]; + size_t label_len = strlen(label); + const unsigned char *addr[4]; + size_t len[4]; + + addr[0] = (u8 *) label; + len[0] = label_len; + addr[1] = &zero; + len[1] = 1; + addr[2] = data; + len[2] = data_len; + addr[3] = &counter; + len[3] = 1; + + pos = 0; + while (pos < buf_len) { + plen = buf_len - pos; + if (plen >= SHA1_MAC_LEN) { + hmac_sha1_vector(key, key_len, 4, addr, len, + &buf[pos]); + pos += SHA1_MAC_LEN; + } else { + hmac_sha1_vector(key, key_len, 4, addr, len, + hash); + memcpy(&buf[pos], hash, plen); + break; + } + counter++; + } +} + + +/* draft-cam-winget-eap-fast-00.txt */ +void sha1_t_prf(const u8 *key, size_t key_len, const char *label, + const u8 *seed, size_t seed_len, u8 *buf, size_t buf_len) +{ + unsigned char counter = 0; + size_t pos, plen; + u8 hash[SHA1_MAC_LEN]; + size_t label_len = strlen(label); + u8 output_len[2]; + const unsigned char *addr[5]; + size_t len[5]; + + addr[0] = hash; + len[0] = 0; + addr[1] = (unsigned char *) label; + len[1] = label_len + 1; + addr[2] = seed; + len[2] = seed_len; + addr[3] = output_len; + len[3] = 2; + addr[4] = &counter; + len[4] = 1; + + output_len[0] = (buf_len >> 8) & 0xff; + output_len[1] = buf_len & 0xff; + pos = 0; + while (pos < buf_len) { + counter++; + plen = buf_len - pos; + hmac_sha1_vector(key, key_len, 5, addr, len, hash); + if (plen >= SHA1_MAC_LEN) { + memcpy(&buf[pos], hash, SHA1_MAC_LEN); + pos += SHA1_MAC_LEN; + } else { + memcpy(&buf[pos], hash, plen); + break; + } + len[0] = SHA1_MAC_LEN; + } +} + + +/* RFC 2246 */ +int tls_prf(const u8 *secret, size_t secret_len, const char *label, + const u8 *seed, size_t seed_len, u8 *out, size_t outlen) +{ + size_t L_S1, L_S2; + const u8 *S1, *S2; + u8 A_MD5[MD5_MAC_LEN], A_SHA1[SHA1_MAC_LEN]; + u8 P_MD5[MD5_MAC_LEN], P_SHA1[SHA1_MAC_LEN]; + int i, MD5_pos, SHA1_pos; + const u8 *MD5_addr[3]; + size_t MD5_len[3]; + const unsigned char *SHA1_addr[3]; + size_t SHA1_len[3]; + + if (secret_len & 1) + return -1; + + MD5_addr[0] = A_MD5; + MD5_len[0] = MD5_MAC_LEN; + MD5_addr[1] = (unsigned char *) label; + MD5_len[1] = strlen(label); + MD5_addr[2] = seed; + MD5_len[2] = seed_len; + + SHA1_addr[0] = A_SHA1; + SHA1_len[0] = SHA1_MAC_LEN; + SHA1_addr[1] = (unsigned char *) label; + SHA1_len[1] = strlen(label); + SHA1_addr[2] = seed; + SHA1_len[2] = seed_len; + + /* RFC 2246, Chapter 5 + * A(0) = seed, A(i) = HMAC(secret, A(i-1)) + * P_hash = HMAC(secret, A(1) + seed) + HMAC(secret, A(2) + seed) + .. + * PRF = P_MD5(S1, label + seed) XOR P_SHA-1(S2, label + seed) + */ + + L_S1 = L_S2 = (secret_len + 1) / 2; + S1 = secret; + S2 = secret + L_S1; + + hmac_md5_vector(S1, L_S1, 2, &MD5_addr[1], &MD5_len[1], A_MD5); + hmac_sha1_vector(S2, L_S2, 2, &SHA1_addr[1], &SHA1_len[1], A_SHA1); + + MD5_pos = MD5_MAC_LEN; + SHA1_pos = SHA1_MAC_LEN; + for (i = 0; i < outlen; i++) { + if (MD5_pos == MD5_MAC_LEN) { + hmac_md5_vector(S1, L_S1, 3, MD5_addr, MD5_len, P_MD5); + MD5_pos = 0; + hmac_md5(S1, L_S1, A_MD5, MD5_MAC_LEN, A_MD5); + } + if (SHA1_pos == SHA1_MAC_LEN) { + hmac_sha1_vector(S2, L_S2, 3, SHA1_addr, SHA1_len, + P_SHA1); + SHA1_pos = 0; + hmac_sha1(S2, L_S2, A_SHA1, SHA1_MAC_LEN, A_SHA1); + } + + out[i] = P_MD5[MD5_pos] ^ P_SHA1[SHA1_pos]; + + MD5_pos++; + SHA1_pos++; + } + + return 0; +} + + +static void pbkdf2_sha1_f(const char *passphrase, const char *ssid, + size_t ssid_len, int iterations, int count, + u8 *digest) +{ + unsigned char tmp[SHA1_MAC_LEN], tmp2[SHA1_MAC_LEN]; + int i, j; + unsigned char count_buf[4]; + const u8 *addr[2]; + size_t len[2]; + size_t passphrase_len = strlen(passphrase); + + addr[0] = (u8 *) ssid; + len[0] = ssid_len; + addr[1] = count_buf; + len[1] = 4; + + /* F(P, S, c, i) = U1 xor U2 xor ... Uc + * U1 = PRF(P, S || i) + * U2 = PRF(P, U1) + * Uc = PRF(P, Uc-1) + */ + + count_buf[0] = (count >> 24) & 0xff; + count_buf[1] = (count >> 16) & 0xff; + count_buf[2] = (count >> 8) & 0xff; + count_buf[3] = count & 0xff; + hmac_sha1_vector((u8 *) passphrase, passphrase_len, 2, addr, len, tmp); + memcpy(digest, tmp, SHA1_MAC_LEN); + + for (i = 1; i < iterations; i++) { + hmac_sha1((u8 *) passphrase, passphrase_len, tmp, SHA1_MAC_LEN, + tmp2); + memcpy(tmp, tmp2, SHA1_MAC_LEN); + for (j = 0; j < SHA1_MAC_LEN; j++) + digest[j] ^= tmp2[j]; + } +} + + +void pbkdf2_sha1(const char *passphrase, const char *ssid, size_t ssid_len, + int iterations, u8 *buf, size_t buflen) +{ + int count = 0; + unsigned char *pos = buf; + size_t left = buflen, plen; + unsigned char digest[SHA1_MAC_LEN]; + + while (left > 0) { + count++; + pbkdf2_sha1_f(passphrase, ssid, ssid_len, iterations, count, + digest); + plen = left > SHA1_MAC_LEN ? SHA1_MAC_LEN : left; + memcpy(pos, digest, plen); + pos += plen; + left -= plen; + } +} + + +void sha1_transform(u8 *state, u8 data[64]) +{ +#ifdef EAP_TLS_FUNCS + SHA_CTX context; + memset(&context, 0, sizeof(context)); + memcpy(&context.h0, state, 5 * 4); + SHA1_Transform(&context, data); + memcpy(state, &context.h0, 5 * 4); +#else /* EAP_TLS_FUNCS */ + SHA1Transform((u32 *) state, data); +#endif /* EAP_TLS_FUNCS */ +} + + +void sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, + u8 *mac) +{ + SHA1_CTX ctx; + int i; + + SHA1Init(&ctx); + for (i = 0; i < num_elem; i++) + SHA1Update(&ctx, addr[i], len[i]); + SHA1Final(mac, &ctx); +} + + +#ifndef EAP_TLS_FUNCS + +/* ===== start - public domain SHA1 implementation ===== */ + +/* +SHA-1 in C +By Steve Reid <sreid@sea-to-sky.net> +100% Public Domain + +----------------- +Modified 7/98 +By James H. Brown <jbrown@burgoyne.com> +Still 100% Public Domain + +Corrected a problem which generated improper hash values on 16 bit machines +Routine SHA1Update changed from + void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned int +len) +to + void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned +long len) + +The 'len' parameter was declared an int which works fine on 32 bit machines. +However, on 16 bit machines an int is too small for the shifts being done +against +it. This caused the hash function to generate incorrect values if len was +greater than 8191 (8K - 1) due to the 'len << 3' on line 3 of SHA1Update(). + +Since the file IO in main() reads 16K at a time, any file 8K or larger would +be guaranteed to generate the wrong hash (e.g. Test Vector #3, a million +"a"s). + +I also changed the declaration of variables i & j in SHA1Update to +unsigned long from unsigned int for the same reason. + +These changes should make no difference to any 32 bit implementations since +an +int and a long are the same size in those environments. + +-- +I also corrected a few compiler warnings generated by Borland C. +1. Added #include <process.h> for exit() prototype +2. Removed unused variable 'j' in SHA1Final +3. Changed exit(0) to return(0) at end of main. + +ALL changes I made can be located by searching for comments containing 'JHB' +----------------- +Modified 8/98 +By Steve Reid <sreid@sea-to-sky.net> +Still 100% public domain + +1- Removed #include <process.h> and used return() instead of exit() +2- Fixed overwriting of finalcount in SHA1Final() (discovered by Chris Hall) +3- Changed email address from steve@edmweb.com to sreid@sea-to-sky.net + +----------------- +Modified 4/01 +By Saul Kravitz <Saul.Kravitz@celera.com> +Still 100% PD +Modified to run on Compaq Alpha hardware. + +----------------- +Modified 4/01 +By Jouni Malinen <jkmaline@cc.hut.fi> +Minor changes to match the coding style used in Dynamics. + +Modified September 24, 2004 +By Jouni Malinen <jkmaline@cc.hut.fi> +Fixed alignment issue in SHA1Transform when SHA1HANDSOFF is defined. + +*/ + +/* +Test Vectors (from FIPS PUB 180-1) +"abc" + A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D +"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" + 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1 +A million repetitions of "a" + 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F +*/ + +#define SHA1HANDSOFF + +#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) + +/* blk0() and blk() perform the initial expand. */ +/* I got the idea of expanding during the round function from SSLeay */ +#ifndef WORDS_BIGENDIAN +#define blk0(i) (block->l[i] = (rol(block->l[i], 24) & 0xFF00FF00) | \ + (rol(block->l[i], 8) & 0x00FF00FF)) +#else +#define blk0(i) block->l[i] +#endif +#define blk(i) (block->l[i & 15] = rol(block->l[(i + 13) & 15] ^ \ + block->l[(i + 8) & 15] ^ block->l[(i + 2) & 15] ^ block->l[i & 15], 1)) + +/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */ +#define R0(v,w,x,y,z,i) \ + z += ((w & (x ^ y)) ^ y) + blk0(i) + 0x5A827999 + rol(v, 5); \ + w = rol(w, 30); +#define R1(v,w,x,y,z,i) \ + z += ((w & (x ^ y)) ^ y) + blk(i) + 0x5A827999 + rol(v, 5); \ + w = rol(w, 30); +#define R2(v,w,x,y,z,i) \ + z += (w ^ x ^ y) + blk(i) + 0x6ED9EBA1 + rol(v, 5); w = rol(w, 30); +#define R3(v,w,x,y,z,i) \ + z += (((w | x) & y) | (w & x)) + blk(i) + 0x8F1BBCDC + rol(v, 5); \ + w = rol(w, 30); +#define R4(v,w,x,y,z,i) \ + z += (w ^ x ^ y) + blk(i) + 0xCA62C1D6 + rol(v, 5); \ + w=rol(w, 30); + + +#ifdef VERBOSE /* SAK */ +void SHAPrintContext(SHA1_CTX *context, char *msg) +{ + printf("%s (%d,%d) %x %x %x %x %x\n", + msg, + context->count[0], context->count[1], + context->state[0], + context->state[1], + context->state[2], + context->state[3], + context->state[4]); +} +#endif + +/* Hash a single 512-bit block. This is the core of the algorithm. */ + +void SHA1Transform(u32 state[5], const unsigned char buffer[64]) +{ + u32 a, b, c, d, e; + typedef union { + unsigned char c[64]; + u32 l[16]; + } CHAR64LONG16; + CHAR64LONG16* block; +#ifdef SHA1HANDSOFF + u32 workspace[16]; + block = (CHAR64LONG16 *) workspace; + memcpy(block, buffer, 64); +#else + block = (CHAR64LONG16 *) buffer; +#endif + /* Copy context->state[] to working vars */ + a = state[0]; + b = state[1]; + c = state[2]; + d = state[3]; + e = state[4]; + /* 4 rounds of 20 operations each. Loop unrolled. */ + R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3); + R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7); + R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11); + R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15); + R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19); + R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23); + R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27); + R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31); + R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35); + R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39); + R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43); + R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47); + R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51); + R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55); + R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59); + R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63); + R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67); + R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71); + R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75); + R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79); + /* Add the working vars back into context.state[] */ + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + state[4] += e; + /* Wipe variables */ + a = b = c = d = e = 0; +#ifdef SHA1HANDSOFF + memset(block, 0, 64); +#endif +} + + +/* SHA1Init - Initialize new context */ + +void SHA1Init(SHA1_CTX* context) +{ + /* SHA1 initialization constants */ + context->state[0] = 0x67452301; + context->state[1] = 0xEFCDAB89; + context->state[2] = 0x98BADCFE; + context->state[3] = 0x10325476; + context->state[4] = 0xC3D2E1F0; + context->count[0] = context->count[1] = 0; +} + + +/* Run your data through this. */ + +void SHA1Update(SHA1_CTX* context, const void *_data, u32 len) +{ + u32 i, j; + const unsigned char *data = _data; + +#ifdef VERBOSE + SHAPrintContext(context, "before"); +#endif + j = (context->count[0] >> 3) & 63; + if ((context->count[0] += len << 3) < (len << 3)) + context->count[1]++; + context->count[1] += (len >> 29); + if ((j + len) > 63) { + memcpy(&context->buffer[j], data, (i = 64-j)); + SHA1Transform(context->state, context->buffer); + for ( ; i + 63 < len; i += 64) { + SHA1Transform(context->state, &data[i]); + } + j = 0; + } + else i = 0; + memcpy(&context->buffer[j], &data[i], len - i); +#ifdef VERBOSE + SHAPrintContext(context, "after "); +#endif +} + + +/* Add padding and return the message digest. */ + +void SHA1Final(unsigned char digest[20], SHA1_CTX* context) +{ + u32 i; + unsigned char finalcount[8]; + + for (i = 0; i < 8; i++) { + finalcount[i] = (unsigned char) + ((context->count[(i >= 4 ? 0 : 1)] >> + ((3-(i & 3)) * 8) ) & 255); /* Endian independent */ + } + SHA1Update(context, (unsigned char *) "\200", 1); + while ((context->count[0] & 504) != 448) { + SHA1Update(context, (unsigned char *) "\0", 1); + } + SHA1Update(context, finalcount, 8); /* Should cause a SHA1Transform() + */ + for (i = 0; i < 20; i++) { + digest[i] = (unsigned char) + ((context->state[i >> 2] >> ((3 - (i & 3)) * 8)) & + 255); + } + /* Wipe variables */ + i = 0; + memset(context->buffer, 0, 64); + memset(context->state, 0, 20); + memset(context->count, 0, 8); + memset(finalcount, 0, 8); +} + +/* ===== end - public domain SHA1 implementation ===== */ + +#endif /* EAP_TLS_FUNCS */ + + +#ifdef TEST_MAIN + +#include "md5.c" + +static int test_eap_fast(void) +{ + /* draft-cam-winget-eap-fast-01.txt */ + const u8 pac_key[] = { + 0x0B, 0x97, 0x39, 0x0F, 0x37, 0x51, 0x78, 0x09, + 0x81, 0x1E, 0xFD, 0x9C, 0x6E, 0x65, 0x94, 0x2B, + 0x63, 0x2C, 0xE9, 0x53, 0x89, 0x38, 0x08, 0xBA, + 0x36, 0x0B, 0x03, 0x7C, 0xD1, 0x85, 0xE4, 0x14 + }; + const u8 seed[] = { + 0x3F, 0xFB, 0x11, 0xC4, 0x6C, 0xBF, 0xA5, 0x7A, + 0x54, 0x40, 0xDA, 0xE8, 0x22, 0xD3, 0x11, 0xD3, + 0xF7, 0x6D, 0xE4, 0x1D, 0xD9, 0x33, 0xE5, 0x93, + 0x70, 0x97, 0xEB, 0xA9, 0xB3, 0x66, 0xF4, 0x2A, + 0x00, 0x00, 0x00, 0x02, 0x6A, 0x66, 0x43, 0x2A, + 0x8D, 0x14, 0x43, 0x2C, 0xEC, 0x58, 0x2D, 0x2F, + 0xC7, 0x9C, 0x33, 0x64, 0xBA, 0x04, 0xAD, 0x3A, + 0x52, 0x54, 0xD6, 0xA5, 0x79, 0xAD, 0x1E, 0x00 + }; + const u8 master_secret[] = { + 0x4A, 0x1A, 0x51, 0x2C, 0x01, 0x60, 0xBC, 0x02, + 0x3C, 0xCF, 0xBC, 0x83, 0x3F, 0x03, 0xBC, 0x64, + 0x88, 0xC1, 0x31, 0x2F, 0x0B, 0xA9, 0xA2, 0x77, + 0x16, 0xA8, 0xD8, 0xE8, 0xBD, 0xC9, 0xD2, 0x29, + 0x38, 0x4B, 0x7A, 0x85, 0xBE, 0x16, 0x4D, 0x27, + 0x33, 0xD5, 0x24, 0x79, 0x87, 0xB1, 0xC5, 0xA2 + }; + const u8 key_block[] = { + 0x59, 0x59, 0xBE, 0x8E, 0x41, 0x3A, 0x77, 0x74, + 0x8B, 0xB2, 0xE5, 0xD3, 0x60, 0xAC, 0x4D, 0x35, + 0xDF, 0xFB, 0xC8, 0x1E, 0x9C, 0x24, 0x9C, 0x8B, + 0x0E, 0xC3, 0x1D, 0x72, 0xC8, 0x84, 0x9D, 0x57, + 0x48, 0x51, 0x2E, 0x45, 0x97, 0x6C, 0x88, 0x70, + 0xBE, 0x5F, 0x01, 0xD3, 0x64, 0xE7, 0x4C, 0xBB, + 0x11, 0x24, 0xE3, 0x49, 0xE2, 0x3B, 0xCD, 0xEF, + 0x7A, 0xB3, 0x05, 0x39, 0x5D, 0x64, 0x8A, 0x44, + 0x11, 0xB6, 0x69, 0x88, 0x34, 0x2E, 0x8E, 0x29, + 0xD6, 0x4B, 0x7D, 0x72, 0x17, 0x59, 0x28, 0x05, + 0xAF, 0xF9, 0xB7, 0xFF, 0x66, 0x6D, 0xA1, 0x96, + 0x8F, 0x0B, 0x5E, 0x06, 0x46, 0x7A, 0x44, 0x84, + 0x64, 0xC1, 0xC8, 0x0C, 0x96, 0x44, 0x09, 0x98, + 0xFF, 0x92, 0xA8, 0xB4, 0xC6, 0x42, 0x28, 0x71 + }; + const u8 sks[] = { + 0xD6, 0x4B, 0x7D, 0x72, 0x17, 0x59, 0x28, 0x05, + 0xAF, 0xF9, 0xB7, 0xFF, 0x66, 0x6D, 0xA1, 0x96, + 0x8F, 0x0B, 0x5E, 0x06, 0x46, 0x7A, 0x44, 0x84, + 0x64, 0xC1, 0xC8, 0x0C, 0x96, 0x44, 0x09, 0x98, + 0xFF, 0x92, 0xA8, 0xB4, 0xC6, 0x42, 0x28, 0x71 + }; + const u8 isk[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + const u8 imck[] = { + 0x16, 0x15, 0x3C, 0x3F, 0x21, 0x55, 0xEF, 0xD9, + 0x7F, 0x34, 0xAE, 0xC8, 0x1A, 0x4E, 0x66, 0x80, + 0x4C, 0xC3, 0x76, 0xF2, 0x8A, 0xA9, 0x6F, 0x96, + 0xC2, 0x54, 0x5F, 0x8C, 0xAB, 0x65, 0x02, 0xE1, + 0x18, 0x40, 0x7B, 0x56, 0xBE, 0xEA, 0xA7, 0xC5, + 0x76, 0x5D, 0x8F, 0x0B, 0xC5, 0x07, 0xC6, 0xB9, + 0x04, 0xD0, 0x69, 0x56, 0x72, 0x8B, 0x6B, 0xB8, + 0x15, 0xEC, 0x57, 0x7B + }; + const u8 msk[] = { + 0x4D, 0x83, 0xA9, 0xBE, 0x6F, 0x8A, 0x74, 0xED, + 0x6A, 0x02, 0x66, 0x0A, 0x63, 0x4D, 0x2C, 0x33, + 0xC2, 0xDA, 0x60, 0x15, 0xC6, 0x37, 0x04, 0x51, + 0x90, 0x38, 0x63, 0xDA, 0x54, 0x3E, 0x14, 0xB9, + 0x27, 0x99, 0x18, 0x1E, 0x07, 0xBF, 0x0F, 0x5A, + 0x5E, 0x3C, 0x32, 0x93, 0x80, 0x8C, 0x6C, 0x49, + 0x67, 0xED, 0x24, 0xFE, 0x45, 0x40, 0xA0, 0x59, + 0x5E, 0x37, 0xC2, 0xE9, 0xD0, 0x5D, 0x0A, 0xE3 + }; + u8 tlv[] = { + 0x80, 0x0C, 0x00, 0x38, 0x00, 0x01, 0x01, 0x00, + 0xD8, 0x6A, 0x8C, 0x68, 0x3C, 0x32, 0x31, 0xA8, + 0x56, 0x63, 0xB6, 0x40, 0x21, 0xFE, 0x21, 0x14, + 0x4E, 0xE7, 0x54, 0x20, 0x79, 0x2D, 0x42, 0x62, + 0xC9, 0xBF, 0x53, 0x7F, 0x54, 0xFD, 0xAC, 0x58, + 0x43, 0x24, 0x6E, 0x30, 0x92, 0x17, 0x6D, 0xCF, + 0xE6, 0xE0, 0x69, 0xEB, 0x33, 0x61, 0x6A, 0xCC, + 0x05, 0xC5, 0x5B, 0xB7 + }; + const u8 compound_mac[] = { + 0x43, 0x24, 0x6E, 0x30, 0x92, 0x17, 0x6D, 0xCF, + 0xE6, 0xE0, 0x69, 0xEB, 0x33, 0x61, 0x6A, 0xCC, + 0x05, 0xC5, 0x5B, 0xB7 + }; + u8 buf[512]; + const u8 *simck, *cmk; + int errors = 0; + + printf("EAP-FAST test cases\n"); + + printf("- T-PRF (SHA1) test case / master_secret\n"); + sha1_t_prf(pac_key, sizeof(pac_key), "PAC to master secret label hash", + seed, sizeof(seed), buf, sizeof(master_secret)); + if (memcmp(master_secret, buf, sizeof(master_secret)) != 0) { + printf("T-PRF test - FAILED!\n"); + errors++; + } + + printf("- PRF (TLS, SHA1/MD5) test case / key_block\n"); + tls_prf(master_secret, sizeof(master_secret), "key expansion", + seed, sizeof(seed), buf, sizeof(key_block)); + if (memcmp(key_block, buf, sizeof(key_block)) != 0) { + printf("PRF test - FAILED!\n"); + errors++; + } + + printf("- T-PRF (SHA1) test case / IMCK\n"); + sha1_t_prf(sks, sizeof(sks), "Inner Methods Compound Keys", + isk, sizeof(isk), buf, sizeof(imck)); + if (memcmp(imck, buf, sizeof(imck)) != 0) { + printf("T-PRF test - FAILED!\n"); + errors++; + } + + simck = imck; + cmk = imck + 40; + + printf("- T-PRF (SHA1) test case / MSK\n"); + sha1_t_prf(simck, 40, "Session Key Generating Function", + "", 0, buf, sizeof(msk)); + if (memcmp(msk, buf, sizeof(msk)) != 0) { + printf("T-PRF test - FAILED!\n"); + errors++; + } + + printf("- Compound MAC test case\n"); + memset(tlv + sizeof(tlv) - 20, 0, 20); + hmac_sha1(cmk, 20, tlv, sizeof(tlv), tlv + sizeof(tlv) - 20); + if (memcmp(tlv + sizeof(tlv) - 20, compound_mac, sizeof(compound_mac)) + != 0) { + printf("Compound MAC test - FAILED!\n"); + errors++; + } + + return errors; +} + + +static u8 key0[] = +{ + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b +}; +static u8 data0[] = "Hi There"; +static u8 prf0[] = +{ + 0xbc, 0xd4, 0xc6, 0x50, 0xb3, 0x0b, 0x96, 0x84, + 0x95, 0x18, 0x29, 0xe0, 0xd7, 0x5f, 0x9d, 0x54, + 0xb8, 0x62, 0x17, 0x5e, 0xd9, 0xf0, 0x06, 0x06, + 0xe1, 0x7d, 0x8d, 0xa3, 0x54, 0x02, 0xff, 0xee, + 0x75, 0xdf, 0x78, 0xc3, 0xd3, 0x1e, 0x0f, 0x88, + 0x9f, 0x01, 0x21, 0x20, 0xc0, 0x86, 0x2b, 0xeb, + 0x67, 0x75, 0x3e, 0x74, 0x39, 0xae, 0x24, 0x2e, + 0xdb, 0x83, 0x73, 0x69, 0x83, 0x56, 0xcf, 0x5a +}; + +static u8 key1[] = "Jefe"; +static u8 data1[] = "what do ya want for nothing?"; +static u8 prf1[] = +{ + 0x51, 0xf4, 0xde, 0x5b, 0x33, 0xf2, 0x49, 0xad, + 0xf8, 0x1a, 0xeb, 0x71, 0x3a, 0x3c, 0x20, 0xf4, + 0xfe, 0x63, 0x14, 0x46, 0xfa, 0xbd, 0xfa, 0x58, + 0x24, 0x47, 0x59, 0xae, 0x58, 0xef, 0x90, 0x09, + 0xa9, 0x9a, 0xbf, 0x4e, 0xac, 0x2c, 0xa5, 0xfa, + 0x87, 0xe6, 0x92, 0xc4, 0x40, 0xeb, 0x40, 0x02, + 0x3e, 0x7b, 0xab, 0xb2, 0x06, 0xd6, 0x1d, 0xe7, + 0xb9, 0x2f, 0x41, 0x52, 0x90, 0x92, 0xb8, 0xfc +}; + + +static u8 key2[] = +{ + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa +}; +static u8 data2[] = +{ + 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, + 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, + 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, + 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, + 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, + 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, + 0xdd, 0xdd +}; +static u8 prf2[] = +{ + 0xe1, 0xac, 0x54, 0x6e, 0xc4, 0xcb, 0x63, 0x6f, + 0x99, 0x76, 0x48, 0x7b, 0xe5, 0xc8, 0x6b, 0xe1, + 0x7a, 0x02, 0x52, 0xca, 0x5d, 0x8d, 0x8d, 0xf1, + 0x2c, 0xfb, 0x04, 0x73, 0x52, 0x52, 0x49, 0xce, + 0x9d, 0xd8, 0xd1, 0x77, 0xea, 0xd7, 0x10, 0xbc, + 0x9b, 0x59, 0x05, 0x47, 0x23, 0x91, 0x07, 0xae, + 0xf7, 0xb4, 0xab, 0xd4, 0x3d, 0x87, 0xf0, 0xa6, + 0x8f, 0x1c, 0xbd, 0x9e, 0x2b, 0x6f, 0x76, 0x07 +}; + + +struct passphrase_test { + char *passphrase; + char *ssid; + char psk[32]; +}; + +static struct passphrase_test passphrase_tests[] = +{ + { + "password", + "IEEE", + { + 0xf4, 0x2c, 0x6f, 0xc5, 0x2d, 0xf0, 0xeb, 0xef, + 0x9e, 0xbb, 0x4b, 0x90, 0xb3, 0x8a, 0x5f, 0x90, + 0x2e, 0x83, 0xfe, 0x1b, 0x13, 0x5a, 0x70, 0xe2, + 0x3a, 0xed, 0x76, 0x2e, 0x97, 0x10, 0xa1, 0x2e + } + }, + { + "ThisIsAPassword", + "ThisIsASSID", + { + 0x0d, 0xc0, 0xd6, 0xeb, 0x90, 0x55, 0x5e, 0xd6, + 0x41, 0x97, 0x56, 0xb9, 0xa1, 0x5e, 0xc3, 0xe3, + 0x20, 0x9b, 0x63, 0xdf, 0x70, 0x7d, 0xd5, 0x08, + 0xd1, 0x45, 0x81, 0xf8, 0x98, 0x27, 0x21, 0xaf + } + }, + { + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ", + { + 0xbe, 0xcb, 0x93, 0x86, 0x6b, 0xb8, 0xc3, 0x83, + 0x2c, 0xb7, 0x77, 0xc2, 0xf5, 0x59, 0x80, 0x7c, + 0x8c, 0x59, 0xaf, 0xcb, 0x6e, 0xae, 0x73, 0x48, + 0x85, 0x00, 0x13, 0x00, 0xa9, 0x81, 0xcc, 0x62 + } + }, +}; + +#define NUM_PASSPHRASE_TESTS \ +(sizeof(passphrase_tests) / sizeof(passphrase_tests[0])) + + +int main(int argc, char *argv[]) +{ + u8 res[512]; + int ret = 0, i; + + printf("PRF-SHA1 test cases:\n"); + + sha1_prf(key0, sizeof(key0), "prefix", data0, sizeof(data0) - 1, + res, sizeof(prf0)); + if (memcmp(res, prf0, sizeof(prf0)) == 0) + printf("Test case 0 - OK\n"); + else { + printf("Test case 0 - FAILED!\n"); + ret++; + } + + sha1_prf(key1, sizeof(key1) - 1, "prefix", data1, sizeof(data1) - 1, + res, sizeof(prf1)); + if (memcmp(res, prf1, sizeof(prf1)) == 0) + printf("Test case 1 - OK\n"); + else { + printf("Test case 1 - FAILED!\n"); + ret++; + } + + sha1_prf(key2, sizeof(key2), "prefix", data2, sizeof(data2), + res, sizeof(prf2)); + if (memcmp(res, prf2, sizeof(prf2)) == 0) + printf("Test case 2 - OK\n"); + else { + printf("Test case 2 - FAILED!\n"); + ret++; + } + + ret += test_eap_fast(); + + printf("PBKDF2-SHA1 Passphrase test cases:\n"); + for (i = 0; i < NUM_PASSPHRASE_TESTS; i++) { + u8 psk[32]; + struct passphrase_test *test = &passphrase_tests[i]; + pbkdf2_sha1(test->passphrase, + test->ssid, strlen(test->ssid), + 4096, psk, 32); + if (memcmp(psk, test->psk, 32) == 0) + printf("Test case %d - OK\n", i); + else { + printf("Test case %d - FAILED!\n", i); + ret++; + } + } + + return ret; +} +#endif /* TEST_MAIN */ diff --git a/contrib/wpa_supplicant/sha1.h b/contrib/wpa_supplicant/sha1.h new file mode 100644 index 0000000..186e3c1 --- /dev/null +++ b/contrib/wpa_supplicant/sha1.h @@ -0,0 +1,50 @@ +#ifndef SHA1_H +#define SHA1_H + +#ifdef EAP_TLS_FUNCS + +#include <openssl/sha.h> + +#define SHA1_CTX SHA_CTX +#define SHA1Init SHA1_Init +#define SHA1Update SHA1_Update +#define SHA1Final SHA1_Final +#define SHA1Transform SHA1_Transform +#define SHA1_MAC_LEN SHA_DIGEST_LENGTH + +#else /* EAP_TLS_FUNCS */ + +#define SHA1_MAC_LEN 20 + +typedef struct { + u32 state[5]; + u32 count[2]; + unsigned char buffer[64]; +} SHA1_CTX; + +void SHA1Init(SHA1_CTX *context); +void SHA1Update(SHA1_CTX *context, const void *data, u32 len); +void SHA1Final(unsigned char digest[20], SHA1_CTX* context); +void SHA1Transform(u32 state[5], const unsigned char buffer[64]); + +#endif /* EAP_TLS_FUNCS */ + +void sha1_mac(const u8 *key, size_t key_len, const u8 *data, size_t data_len, + u8 *mac); +void hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac); +void hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len, + u8 *mac); +void sha1_prf(const u8 *key, size_t key_len, const char *label, + const u8 *data, size_t data_len, u8 *buf, size_t buf_len); +void sha1_t_prf(const u8 *key, size_t key_len, const char *label, + const u8 *seed, size_t seed_len, u8 *buf, size_t buf_len); +int tls_prf(const u8 *secret, size_t secret_len, const char *label, + const u8 *seed, size_t seed_len, u8 *out, size_t outlen); +void pbkdf2_sha1(const char *passphrase, const char *ssid, size_t ssid_len, + int iterations, u8 *buf, size_t buflen); +void sha1_transform(u8 *state, u8 data[64]); +void sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, + u8 *mac); + +#endif /* SHA1_H */ diff --git a/contrib/wpa_supplicant/tls.h b/contrib/wpa_supplicant/tls.h new file mode 100644 index 0000000..99c9b33 --- /dev/null +++ b/contrib/wpa_supplicant/tls.h @@ -0,0 +1,312 @@ +#ifndef TLS_H +#define TLS_H + +struct tls_connection; + +struct tls_keys { + const u8 *master_key; + size_t master_key_len; + const u8 *client_random; + size_t client_random_len; + const u8 *server_random; + size_t server_random_len; +}; + +/** + * tls_init - initialize TLS library + * + * Returns: Context data to be used as @tls_ctx in calls to other functions, + * or %NULL on failure. + * + * Called once during program startup. + */ +void * tls_init(void); + +/** + * tls_deinit - deinitialize TLS library + * @tls_ctx: TLS context data from tls_init() + * + * Called once during program shutdown. + */ +void tls_deinit(void *tls_ctx); + +/** + * tls_get_errors - process pending errors + * @tls_ctx: TLS context data from tls_init() + * + * Returns: Number of found error, 0 if no errors detected. + * + * Process all pending TLS errors. + */ +int tls_get_errors(void *tls_ctx); + +/** + * tls_connection_init - initialize a new TLS connection + * @tls_ctx: TLS context data from tls_init() + * + * Returns: Connection context data, @conn for other function calls + */ +struct tls_connection * tls_connection_init(void *tls_ctx); + +/** + * tls_connection_deinit - free TLS connection data + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * + * Release all resources allocated for TLS connection. + */ +void tls_connection_deinit(void *tls_ctx, struct tls_connection *conn); + +/** + * tls_connection_established - has the TLS connection been completed? + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * + * Returns: 1 if TLS connection has been completed, 0 if not. + */ +int tls_connection_established(void *tls_ctx, struct tls_connection *conn); + +/** + * tls_connection_shutdown - shutdown TLS connection data. + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * + * Returns: 0 on success, -1 on failure + * + * Shutdown current TLS connection without releasing all resources. New + * connection can be started by using the same @conn without having to call + * tls_connection_init() or setting certificates etc. again. The new + * connection should try to use session resumption. + */ +int tls_connection_shutdown(void *tls_ctx, struct tls_connection *conn); + +/** + * tls_connection_ca_cert - set trusted CA certificate for TLS connection + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * @ca_cert: File name for CA certificate in PEM or DER format + * @subject_match: String to match in the subject of the peer certificate or + * %NULL to allow all subjects + * + * Returns: 0 on success, -1 on failure + */ +int tls_connection_ca_cert(void *tls_ctx, struct tls_connection *conn, + const char *ca_cert, const char *subject_match); + +/** + * tls_global_ca_cert - set trusted CA certificate for all TLS connections + * @tls_ctx: TLS context data from tls_init() + * @ca_cert: File name for CA certificate in PEM or DER format + * %NULL to allow all subjects + * + * Returns: 0 on success, -1 on failure + */ +int tls_global_ca_cert(void *tls_ctx, const char *ca_cert); + +/** + * tls_connection_ca_cert - set trusted CA certificate for TLS connection + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * @verify_peer: 1 = verify peer certificate + * @subject_match: String to match in the subject of the peer certificate or + * %NULL to allow all subjects + * + * Returns: 0 on success, -1 on failure + */ +int tls_connection_set_verify(void *tls_ctx, struct tls_connection *conn, + int verify_peer, const char *subject_match); + +/** + * tls_connection_client_cert - set client certificate for TLS connection + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * @client_cert: File name for client certificate in PEM or DER format + * + * Returns: 0 on success, -1 on failure + */ +int tls_connection_client_cert(void *tls_ctx, struct tls_connection *conn, + const char *client_cert); + +/** + * tls_global_client_cert - set client certificate for all TLS connections + * @tls_ctx: TLS context data from tls_init() + * @client_cert: File name for client certificate in PEM or DER format + * + * Returns: 0 on success, -1 on failure + */ +int tls_global_client_cert(void *tls_ctx, const char *client_cert); + +/** + * tls_connection_private_key - set private key for TLS connection + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * @private_key: File name for client private key in PEM or DER format + * @private_key_passwd: Passphrase for decrypted private key, %NULL if no + * passphrase is used. + * + * Returns: 0 on success, -1 on failure + */ +int tls_connection_private_key(void *tls_ctx, struct tls_connection *conn, + const char *private_key, + const char *private_key_passwd); + +/** + * tls_global_private_key - set private key for all TLS connections + * @tls_ctx: TLS context data from tls_init() + * @private_key: File name for client private key in PEM or DER format + * @private_key_passwd: Passphrase for decrypted private key, %NULL if no + * passphrase is used. + * + * Returns: 0 on success, -1 on failure + */ +int tls_global_private_key(void *tls_ctx, const char *private_key, + const char *private_key_passwd); + +/** + * tls_connection_dh - set DH/DSA parameters for TLS connection + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * @dh_file: File name for DH/DSA data in PEM format. + * + * Returns: 0 on success, -1 on failure + */ +int tls_connection_dh(void *tls_ctx, struct tls_connection *conn, + const char *dh_file); + +/** + * tls_connection_get_keys - get master key and random data from TLS connection + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * @keys: Structure of key/random data (filled on success) + * + * Returns: 0 on success, -1 on failure + */ +int tls_connection_get_keys(void *tls_ctx, struct tls_connection *conn, + struct tls_keys *keys); + +/** + * tls_connection_handshake - process TLS handshake (client side) + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * @in_data: Input data from TLS peer + * @in_len: Input data length + * @out_len: Length of the output buffer. + * + * Returns: pointer to output data, %NULL on failure + * + * Caller is responsible for freeing returned output data. + */ +u8 * tls_connection_handshake(void *tls_ctx, struct tls_connection *conn, + const u8 *in_data, size_t in_len, + size_t *out_len); + +/** + * tls_connection_servr_handshake - process TLS handshake (server side) + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * @in_data: Input data from TLS peer + * @in_len: Input data length + * @out_len: Length of the output buffer. + * + * Returns: pointer to output data, %NULL on failure + * + * Caller is responsible for freeing returned output data. + */ +u8 * tls_connection_server_handshake(void *tls_ctx, + struct tls_connection *conn, + const u8 *in_data, size_t in_len, + size_t *out_len); + +/** + * tls_connection_encrypt - encrypt data into TLS tunnel + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * @in_data: Pointer to plaintext data to be encrypted + * @in_len: Input buffer length + * @out_data: Pointer to output buffer (encrypted TLS data) + * @out_len: Maximum @out_data length + * + * Returns: Number of bytes written to @out_data, -1 on failure + */ +int tls_connection_encrypt(void *tls_ctx, struct tls_connection *conn, + u8 *in_data, size_t in_len, + u8 *out_data, size_t out_len); + +/** + * tls_connection_decrypt - decrypt data from TLS tunnel + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * @in_data: Pointer to input buffer (encrypted TLS data) + * @in_len: Input buffer length + * @out_data: Pointer to output buffer (decrypted data from TLS tunnel) + * @out_len: Maximum @out_data length + * + * Returns: Number of bytes written to @out_data, -1 on failure + */ +int tls_connection_decrypt(void *tls_ctx, struct tls_connection *conn, + u8 *in_data, size_t in_len, + u8 *out_data, size_t out_len); + +/** + * tls_connection_resumed - was session resumption used + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * + * Returns: 1 if current session used session resumption, 0 if not + */ +int tls_connection_resumed(void *tls_ctx, struct tls_connection *conn); + +/** + * tls_connection_set_master_key - configure master secret for TLS connection + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * @key: TLS pre-master-secret + * @key_len: length of @key in bytes + * + * Returns: 0 on success, -1 on failure + */ +int tls_connection_set_master_key(void *ssl_ctx, struct tls_connection *conn, + const u8 *key, size_t key_len); + +/** + * tls_connection_set_anon_dh - configure TLS connection to use anonymous DH + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * + * Returns: 0 on success, -1 on failure + * + * TODO: consider changing this to more generic routine for configuring allowed + * ciphers + */ +int tls_connection_set_anon_dh(void *ssl_ctx, struct tls_connection *conn); + +/** + * tls_get_cipher - get current cipher name + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * + * Returns: 0 on success, -1 on failure + * + * Get the name of the currently used cipher. + */ +int tls_get_cipher(void *ssl_ctx, struct tls_connection *conn, + char *buf, size_t buflen); + +/** + * tls_connection_enable_workaround - enable TLS workaround options + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * + * Returns: 0 on success, -1 on failure + * + * This function is used to enable connection-specific workaround options for + * buffer SSL/TLS implementations. + */ +int tls_connection_enable_workaround(void *ssl_ctx, + struct tls_connection *conn); + +int tls_connection_client_hello_ext(void *ssl_ctx, struct tls_connection *conn, + int ext_type, const u8 *data, + size_t data_len); + +#endif /* TLS_H */ diff --git a/contrib/wpa_supplicant/tls_none.c b/contrib/wpa_supplicant/tls_none.c new file mode 100644 index 0000000..2b3cafc --- /dev/null +++ b/contrib/wpa_supplicant/tls_none.c @@ -0,0 +1,22 @@ +/* + * WPA Supplicant / SSL/TLS interface functions for no TLS case + * Copyright (c) 2004, Jouni Malinen <jkmaline@cc.hut.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +void * tls_init(void) +{ + return (void *) 1; +} + +void tls_deinit(void *ssl_ctx) +{ +} diff --git a/contrib/wpa_supplicant/tls_openssl.c b/contrib/wpa_supplicant/tls_openssl.c new file mode 100644 index 0000000..097b1c8 --- /dev/null +++ b/contrib/wpa_supplicant/tls_openssl.c @@ -0,0 +1,876 @@ +/* + * WPA Supplicant / SSL/TLS interface functions for openssl + * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <openssl/ssl.h> +#include <openssl/err.h> +#include <openssl/pkcs12.h> + +#include "common.h" +#include "tls.h" + + +struct tls_connection { + SSL *ssl; + BIO *ssl_in, *ssl_out; + char *subject_match; +}; + + +static void ssl_info_cb(const SSL *ssl, int where, int ret) +{ + const char *str; + int w; + + wpa_printf(MSG_DEBUG, "SSL: (where=0x%x ret=0x%x)", where, ret); + w = where & ~SSL_ST_MASK; + if (w & SSL_ST_CONNECT) + str = "SSL_connect"; + else if (w & SSL_ST_ACCEPT) + str = "SSL_accept"; + else + str = "undefined"; + + if (where & SSL_CB_LOOP) { + wpa_printf(MSG_DEBUG, "SSL: %s:%s", + str, SSL_state_string_long(ssl)); + } else if (where & SSL_CB_ALERT) { + wpa_printf(MSG_INFO, "SSL: SSL3 alert: %s:%s:%s", + where & SSL_CB_READ ? + "read (authentication server reported an error)" : + "write (local SSL3 detected an error)", + SSL_alert_type_string_long(ret), + SSL_alert_desc_string_long(ret)); + } else if (where & SSL_CB_EXIT && ret <= 0) { + wpa_printf(MSG_DEBUG, "SSL: %s:%s in %s", + str, ret == 0 ? "failed" : "error", + SSL_state_string_long(ssl)); + } +} + + +void * tls_init(void) +{ + SSL_CTX *ssl; + + SSL_load_error_strings(); + SSL_library_init(); + /* TODO: if /dev/urandom is available, PRNG is seeded automatically. + * If this is not the case, random data should be added here. */ + +#ifdef PKCS12_FUNCS + PKCS12_PBE_add(); +#endif /* PKCS12_FUNCS */ + + ssl = SSL_CTX_new(TLSv1_method()); + if (ssl == NULL) + return NULL; + + SSL_CTX_set_info_callback(ssl, ssl_info_cb); + + return ssl; +} + + +void tls_deinit(void *ssl_ctx) +{ + SSL_CTX *ssl = ssl_ctx; + SSL_CTX_free(ssl); + ERR_free_strings(); + EVP_cleanup(); +} + + +int tls_get_errors(void *ssl_ctx) +{ + int count = 0; + unsigned long err; + + while ((err = ERR_get_error())) { + wpa_printf(MSG_INFO, "TLS - SSL error: %s", + ERR_error_string(err, NULL)); + count++; + } + + return count; +} + +struct tls_connection * tls_connection_init(void *ssl_ctx) +{ + SSL_CTX *ssl = ssl_ctx; + struct tls_connection *conn; + + conn = malloc(sizeof(*conn)); + if (conn == NULL) + return NULL; + memset(conn, 0, sizeof(*conn)); + conn->ssl = SSL_new(ssl); + if (conn->ssl == NULL) { + wpa_printf(MSG_INFO, "TLS: Failed to initialize new SSL " + "connection: %s", + ERR_error_string(ERR_get_error(), NULL)); + free(conn); + return NULL; + } + + SSL_set_options(conn->ssl, + SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | + SSL_OP_SINGLE_DH_USE); + + conn->ssl_in = BIO_new(BIO_s_mem()); + if (!conn->ssl_in) { + wpa_printf(MSG_INFO, "SSL: Failed to create a new BIO for " + "ssl_in: %s", + ERR_error_string(ERR_get_error(), NULL)); + SSL_free(conn->ssl); + free(conn); + return NULL; + } + + conn->ssl_out = BIO_new(BIO_s_mem()); + if (!conn->ssl_out) { + wpa_printf(MSG_INFO, "SSL: Failed to create a new BIO for " + "ssl_out: %s", + ERR_error_string(ERR_get_error(), NULL)); + SSL_free(conn->ssl); + BIO_free(conn->ssl_in); + free(conn); + return NULL; + } + + SSL_set_bio(conn->ssl, conn->ssl_in, conn->ssl_out); + + return conn; +} + + +void tls_connection_deinit(void *ssl_ctx, struct tls_connection *conn) +{ + if (conn == NULL) + return; + SSL_free(conn->ssl); + free(conn->subject_match); + free(conn); +} + + +int tls_connection_established(void *ssl_ctx, struct tls_connection *conn) +{ + return conn ? SSL_is_init_finished(conn->ssl) : 0; +} + + +int tls_connection_shutdown(void *ssl_ctx, struct tls_connection *conn) +{ + if (conn == NULL) + return -1; + + /* Shutdown previous TLS connection without notifying the peer + * because the connection was already terminated in practice + * and "close notify" shutdown alert would confuse AS. */ + SSL_set_quiet_shutdown(conn->ssl, 1); + SSL_shutdown(conn->ssl); + return 0; +} + + +static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx) +{ + char buf[256]; + X509 *err_cert; + int err, depth; + SSL *ssl; + struct tls_connection *conn; + char *match; + + err_cert = X509_STORE_CTX_get_current_cert(x509_ctx); + err = X509_STORE_CTX_get_error(x509_ctx); + depth = X509_STORE_CTX_get_error_depth(x509_ctx); + ssl = X509_STORE_CTX_get_ex_data(x509_ctx, + SSL_get_ex_data_X509_STORE_CTX_idx()); + X509_NAME_oneline(X509_get_subject_name(err_cert), buf, 256); + + conn = SSL_get_app_data(ssl); + match = conn ? conn->subject_match : NULL; + + if (!preverify_ok) { + wpa_printf(MSG_WARNING, "TLS: Certificate verification failed," + " error %d (%s) depth %d for '%s'", err, + X509_verify_cert_error_string(err), depth, buf); + } else { + wpa_printf(MSG_DEBUG, "TLS: tls_verify_cb - " + "preverify_ok=%d err=%d (%s) depth=%d buf='%s'", + preverify_ok, err, + X509_verify_cert_error_string(err), depth, buf); + if (depth == 0 && match && strstr(buf, match) == NULL) { + wpa_printf(MSG_WARNING, "TLS: Subject '%s' did not " + "match with '%s'", buf, match); + preverify_ok = 0; + } + } + + return preverify_ok; +} + + +int tls_connection_ca_cert(void *ssl_ctx, struct tls_connection *conn, + const char *ca_cert, const char *subject_match) +{ + if (conn == NULL) + return -1; + + free(conn->subject_match); + conn->subject_match = NULL; + if (subject_match) { + conn->subject_match = strdup(subject_match); + if (conn->subject_match == NULL) + return -1; + } + + if (ca_cert) { + if (SSL_CTX_load_verify_locations(ssl_ctx, ca_cert, NULL) != 1) + { + wpa_printf(MSG_WARNING, "TLS: Failed to load root " + "certificates: %s", + ERR_error_string(ERR_get_error(), NULL)); + return -1; + } else { + wpa_printf(MSG_DEBUG, "TLS: Trusted root " + "certificate(s) loaded"); + tls_get_errors(ssl_ctx); + } + SSL_set_app_data(conn->ssl, conn); + SSL_set_verify(conn->ssl, SSL_VERIFY_PEER, tls_verify_cb); + } else { + /* No ca_cert configured - do not try to verify server + * certificate */ + SSL_set_verify(conn->ssl, SSL_VERIFY_NONE, NULL); + } + + return 0; +} + + +int tls_global_ca_cert(void *_ssl_ctx, const char *ca_cert) +{ + SSL_CTX *ssl_ctx = _ssl_ctx; + if (ca_cert) { + if (SSL_CTX_load_verify_locations(ssl_ctx, ca_cert, NULL) != 1) + { + wpa_printf(MSG_WARNING, "TLS: Failed to load root " + "certificates: %s", + ERR_error_string(ERR_get_error(), NULL)); + return -1; + } else { + wpa_printf(MSG_DEBUG, "TLS: Trusted root " + "certificate(s) loaded"); + } + } + + return 0; +} + + +int tls_connection_set_verify(void *ssl_ctx, struct tls_connection *conn, + int verify_peer, const char *subject_match) +{ + if (conn == NULL) + return -1; + + free(conn->subject_match); + conn->subject_match = NULL; + if (subject_match) { + conn->subject_match = strdup(subject_match); + if (conn->subject_match == NULL) + return -1; + } + + if (verify_peer) { + SSL_set_app_data(conn->ssl, conn); + SSL_set_verify(conn->ssl, SSL_VERIFY_PEER | + SSL_VERIFY_FAIL_IF_NO_PEER_CERT | + SSL_VERIFY_CLIENT_ONCE, tls_verify_cb); + } else { + SSL_set_verify(conn->ssl, SSL_VERIFY_NONE, NULL); + } + + SSL_set_accept_state(conn->ssl); + + return 0; +} + + +int tls_connection_client_cert(void *ssl_ctx, struct tls_connection *conn, + const char *client_cert) +{ + if (client_cert == NULL) + return 0; + if (conn == NULL) + return -1; + + if (SSL_use_certificate_file(conn->ssl, client_cert, + SSL_FILETYPE_ASN1) != 1 && + SSL_use_certificate_file(conn->ssl, client_cert, + SSL_FILETYPE_PEM) != 1) { + wpa_printf(MSG_INFO, "TLS: Failed to load client " + "certificate: %s", + ERR_error_string(ERR_get_error(), NULL)); + return -1; + } + return 0; +} + + +int tls_global_client_cert(void *_ssl_ctx, const char *client_cert) +{ + SSL_CTX *ssl_ctx = _ssl_ctx; + if (client_cert == NULL) + return 0; + + if (SSL_CTX_use_certificate_file(ssl_ctx, client_cert, + SSL_FILETYPE_ASN1) != 1 && + SSL_CTX_use_certificate_file(ssl_ctx, client_cert, + SSL_FILETYPE_PEM) != 1) { + wpa_printf(MSG_INFO, "TLS: Failed to load client " + "certificate: %s", + ERR_error_string(ERR_get_error(), NULL)); + return -1; + } + return 0; +} + + +static int tls_passwd_cb(char *buf, int size, int rwflag, void *password) +{ + if (password == NULL) { + return 0; + } + strncpy(buf, (char *) password, size); + buf[size - 1] = '\0'; + return strlen(buf); +} + + +static int tls_read_pkcs12(SSL_CTX *ssl_ctx, SSL *ssl, const char *private_key, + const char *passwd) +{ +#ifdef PKCS12_FUNCS + FILE *f; + PKCS12 *p12; + EVP_PKEY *pkey; + X509 *cert; + int res = 0; + + f = fopen(private_key, "r"); + if (f == NULL) + return -1; + + p12 = d2i_PKCS12_fp(f, NULL); + if (p12 == NULL) { + wpa_printf(MSG_DEBUG, "TLS: Failed to read PKCS12 file '%s'", + private_key); + fclose(f); + return -1; + } + fclose(f); + + pkey = NULL; + cert = NULL; + if (!PKCS12_parse(p12, passwd, &pkey, &cert, NULL)) { + wpa_printf(MSG_DEBUG, "TLS: Failed to parse PKCS12 file '%s': " + "%s", private_key, + ERR_error_string(ERR_get_error(), NULL)); + return -1; + } + wpa_printf(MSG_DEBUG, "TLS: Successfully parsed PKCS12 file '%s'", + private_key); + + if (cert) { + wpa_printf(MSG_DEBUG, "TLS: Got certificate from PKCS12"); + if (ssl) { + if (SSL_use_certificate(ssl, cert) != 1) + res = -1; + } else { + if (SSL_CTX_use_certificate(ssl_ctx, cert) != 1) + res = -1; + } + X509_free(cert); + } + + if (pkey) { + wpa_printf(MSG_DEBUG, "TLS: Got private key from PKCS12"); + if (ssl) { + if (SSL_use_PrivateKey(ssl, pkey) != 1) + res = -1; + } else { + if (SSL_CTX_use_PrivateKey(ssl_ctx, pkey) != 1) + res = -1; + } + EVP_PKEY_free(pkey); + } + + PKCS12_free(p12); + + return res; +#else /* PKCS12_FUNCS */ + wpa_printf(MSG_INFO, "TLS: PKCS12 support disabled - cannot read " + "p12/pfx files"); + return -1; +#endif /* PKCS12_FUNCS */ +} + + +int tls_connection_private_key(void *_ssl_ctx, struct tls_connection *conn, + const char *private_key, + const char *private_key_passwd) +{ + SSL_CTX *ssl_ctx = _ssl_ctx; + char *passwd; + + if (private_key == NULL) + return 0; + if (conn == NULL) + return -1; + + if (private_key_passwd) { + passwd = strdup(private_key_passwd); + if (passwd == NULL) + return -1; + } else + passwd = NULL; + + SSL_CTX_set_default_passwd_cb(ssl_ctx, tls_passwd_cb); + SSL_CTX_set_default_passwd_cb_userdata(ssl_ctx, passwd); + if (SSL_use_PrivateKey_file(conn->ssl, private_key, + SSL_FILETYPE_ASN1) != 1 && + SSL_use_PrivateKey_file(conn->ssl, private_key, + SSL_FILETYPE_PEM) != 1 && + tls_read_pkcs12(ssl_ctx, conn->ssl, private_key, passwd)) { + wpa_printf(MSG_INFO, "SSL: Failed to load private key: %s", + ERR_error_string(ERR_get_error(), NULL)); + free(passwd); + ERR_clear_error(); + return -1; + } + ERR_clear_error(); + free(passwd); + SSL_CTX_set_default_passwd_cb(ssl_ctx, NULL); + + if (!SSL_check_private_key(conn->ssl)) { + wpa_printf(MSG_INFO, "SSL: Private key failed " + "verification: %s", + ERR_error_string(ERR_get_error(), NULL)); + return -1; + } + + return 0; +} + + +int tls_global_private_key(void *_ssl_ctx, const char *private_key, + const char *private_key_passwd) +{ + SSL_CTX *ssl_ctx = _ssl_ctx; + char *passwd; + + if (private_key == NULL) + return 0; + + passwd = strdup(private_key_passwd); + if (passwd == NULL) + return -1; + + SSL_CTX_set_default_passwd_cb(ssl_ctx, tls_passwd_cb); + SSL_CTX_set_default_passwd_cb_userdata(ssl_ctx, passwd); + if (SSL_CTX_use_PrivateKey_file(ssl_ctx, private_key, + SSL_FILETYPE_ASN1) != 1 && + SSL_CTX_use_PrivateKey_file(ssl_ctx, private_key, + SSL_FILETYPE_PEM) != 1 && + tls_read_pkcs12(ssl_ctx, NULL, private_key, passwd)) { + wpa_printf(MSG_INFO, "SSL: Failed to load private key: %s", + ERR_error_string(ERR_get_error(), NULL)); + free(passwd); + ERR_clear_error(); + return -1; + } + free(passwd); + ERR_clear_error(); + SSL_CTX_set_default_passwd_cb(ssl_ctx, NULL); + + if (!SSL_CTX_check_private_key(ssl_ctx)) { + wpa_printf(MSG_INFO, "SSL: Private key failed " + "verification: %s", + ERR_error_string(ERR_get_error(), NULL)); + return -1; + } + + return 0; +} + + +int tls_connection_dh(void *ssl_ctx, struct tls_connection *conn, + const char *dh_file) +{ +#ifdef OPENSSL_NO_DH + if (dh_file == NULL) + return 0; + wpa_printf(MSG_ERROR, "TLS: openssl does not include DH support, but " + "dh_file specified"); + return -1; +#else /* OPENSSL_NO_DH */ + DH *dh; + BIO *bio; + + if (dh_file == NULL) + return 0; + if (conn == NULL) + return -1; + + bio = BIO_new_file(dh_file, "r"); + if (bio == NULL) { + wpa_printf(MSG_INFO, "TLS: Failed to open DH file '%s': %s", + dh_file, ERR_error_string(ERR_get_error(), NULL)); + return -1; + } + dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL); + BIO_free(bio); +#ifndef OPENSSL_NO_DSA + while (dh == NULL) { + DSA *dsa; + wpa_printf(MSG_DEBUG, "TLS: Failed to parse DH file '%s': %s -" + " trying to parse as DSA params", dh_file, + ERR_error_string(ERR_get_error(), NULL)); + bio = BIO_new_file(dh_file, "r"); + if (bio == NULL) + break; + dsa = PEM_read_bio_DSAparams(bio, NULL, NULL, NULL); + BIO_free(bio); + if (!dsa) { + wpa_printf(MSG_DEBUG, "TLS: Failed to parse DSA file " + "'%s': %s", dh_file, + ERR_error_string(ERR_get_error(), NULL)); + break; + } + + wpa_printf(MSG_DEBUG, "TLS: DH file in DSA param format"); + dh = DSA_dup_DH(dsa); + DSA_free(dsa); + if (dh == NULL) { + wpa_printf(MSG_INFO, "TLS: Failed to convert DSA " + "params into DH params"); + break; + } + break; + } +#endif /* !OPENSSL_NO_DSA */ + if (dh == NULL) { + wpa_printf(MSG_INFO, "TLS: Failed to read/parse DH/DSA file " + "'%s'", dh_file); + return -1; + } + + if (SSL_set_tmp_dh(conn->ssl, dh) != 1) { + wpa_printf(MSG_INFO, "TLS: Failed to set DH params from '%s': " + "%s", dh_file, + ERR_error_string(ERR_get_error(), NULL)); + DH_free(dh); + return -1; + } + DH_free(dh); + return 0; +#endif /* OPENSSL_NO_DH */ +} + + +int tls_connection_get_keys(void *ssl_ctx, struct tls_connection *conn, + struct tls_keys *keys) +{ + SSL *ssl; + + if (conn == NULL || keys == NULL) + return -1; + ssl = conn->ssl; + if (ssl == NULL || ssl->s3 == NULL || ssl->session == NULL) + return -1; + + keys->master_key = ssl->session->master_key; + keys->master_key_len = ssl->session->master_key_length; + keys->client_random = ssl->s3->client_random; + keys->client_random_len = SSL3_RANDOM_SIZE; + keys->server_random = ssl->s3->server_random; + keys->server_random_len = SSL3_RANDOM_SIZE; + + return 0; +} + + +u8 * tls_connection_handshake(void *ssl_ctx, struct tls_connection *conn, + const u8 *in_data, size_t in_len, + size_t *out_len) +{ + int res; + u8 *out_data; + + if (in_data && + BIO_write(conn->ssl_in, in_data, in_len) < 0) { + wpa_printf(MSG_INFO, "TLS: Handshake failed - BIO_write: %s", + ERR_error_string(ERR_get_error(), NULL)); + return NULL; + } + + res = SSL_connect(conn->ssl); + if (res != 1) { + int err = SSL_get_error(conn->ssl, res); + if (err == SSL_ERROR_WANT_READ) + wpa_printf(MSG_DEBUG, "SSL: SSL_connect - want " + "more data"); + else if (err == SSL_ERROR_WANT_WRITE) + wpa_printf(MSG_DEBUG, "SSL: SSL_connect - want to " + "write"); + else { + wpa_printf(MSG_INFO, "SSL: SSL_connect: %s", + ERR_error_string(ERR_get_error(), NULL)); + return NULL; + } + } + + res = BIO_ctrl_pending(conn->ssl_out); + wpa_printf(MSG_DEBUG, "SSL: %d bytes pending from ssl_out", res); + out_data = malloc(res == 0 ? 1 : res); + if (out_data == NULL) { + wpa_printf(MSG_DEBUG, "SSL: Failed to allocate memory for " + "handshake output (%d bytes)", res); + BIO_reset(conn->ssl_out); + *out_len = 0; + return NULL; + } + res = res == 0 ? 0 : BIO_read(conn->ssl_out, out_data, res); + if (res < 0) { + wpa_printf(MSG_INFO, "TLS: Handshake failed - BIO_read: %s", + ERR_error_string(ERR_get_error(), NULL)); + BIO_reset(conn->ssl_out); + *out_len = 0; + return NULL; + } + *out_len = res; + return out_data; +} + + +u8 * tls_connection_server_handshake(void *ssl_ctx, + struct tls_connection *conn, + const u8 *in_data, size_t in_len, + size_t *out_len) +{ + int res; + u8 *out_data; + char buf[10]; + + if (in_data && + BIO_write(conn->ssl_in, in_data, in_len) < 0) { + wpa_printf(MSG_INFO, "TLS: Handshake failed - BIO_write: %s", + ERR_error_string(ERR_get_error(), NULL)); + return NULL; + } + + res = SSL_read(conn->ssl, buf, sizeof(buf)); + if (res >= 0) { + wpa_printf(MSG_DEBUG, "SSL: Unexpected data from SSL_read " + "(res=%d)", res); + } + + res = BIO_ctrl_pending(conn->ssl_out); + wpa_printf(MSG_DEBUG, "SSL: %d bytes pending from ssl_out", res); + out_data = malloc(res == 0 ? 1 : res); + if (out_data == NULL) { + wpa_printf(MSG_DEBUG, "SSL: Failed to allocate memory for " + "handshake output (%d bytes)", res); + BIO_reset(conn->ssl_out); + *out_len = 0; + return NULL; + } + res = res == 0 ? 0 : BIO_read(conn->ssl_out, out_data, res); + if (res < 0) { + wpa_printf(MSG_INFO, "TLS: Handshake failed - BIO_read: %s", + ERR_error_string(ERR_get_error(), NULL)); + BIO_reset(conn->ssl_out); + *out_len = 0; + return NULL; + } + *out_len = res; + return out_data; +} + + +int tls_connection_encrypt(void *ssl_ctx, struct tls_connection *conn, + u8 *in_data, size_t in_len, + u8 *out_data, size_t out_len) +{ + int res; + + if (conn == NULL) + return -1; + + BIO_reset(conn->ssl_in); + BIO_reset(conn->ssl_out); + res = SSL_write(conn->ssl, in_data, in_len); + if (res < 0) { + wpa_printf(MSG_INFO, "TLS: Encryption failed - SSL_write: %s", + ERR_error_string(ERR_get_error(), NULL)); + return res; + } + + res = BIO_read(conn->ssl_out, out_data, out_len); + if (res < 0) { + wpa_printf(MSG_INFO, "TLS: Encryption failed - BIO_read: %s", + ERR_error_string(ERR_get_error(), NULL)); + return res; + } + + return res; +} + + +int tls_connection_decrypt(void *ssl_ctx, struct tls_connection *conn, + u8 *in_data, size_t in_len, + u8 *out_data, size_t out_len) +{ + int res; + + res = BIO_write(conn->ssl_in, in_data, in_len); + if (res < 0) { + wpa_printf(MSG_INFO, "TLS: Decryption failed - BIO_write: %s", + ERR_error_string(ERR_get_error(), NULL)); + return res; + } + BIO_reset(conn->ssl_out); + + res = SSL_read(conn->ssl, out_data, out_len); + if (res < 0) { + wpa_printf(MSG_INFO, "TLS: Decryption failed - SSL_read: %s", + ERR_error_string(ERR_get_error(), NULL)); + return res; + } + + return res; +} + + +int tls_connection_resumed(void *ssl_ctx, struct tls_connection *conn) +{ + return conn ? conn->ssl->hit : 0; +} + + +int tls_connection_set_master_key(void *ssl_ctx, struct tls_connection *conn, + const u8 *key, size_t key_len) +{ + SSL *ssl; + + if (conn == NULL || key == NULL || key_len > SSL_MAX_MASTER_KEY_LENGTH) + return -1; + ssl = conn->ssl; + if (ssl == NULL || ssl->session == NULL) + return -1; + + memcpy(ssl->session->master_key, key, key_len); + ssl->session->master_key_length = key_len; + + return 0; +} + + +int tls_connection_set_anon_dh(void *ssl_ctx, struct tls_connection *conn) +{ + if (conn == NULL || conn->ssl == NULL) + return -1; + + if (SSL_set_cipher_list(conn->ssl, "ADH-AES128-SHA") != 1) { + wpa_printf(MSG_INFO, "TLS: Anon DH configuration failed - %s", + ERR_error_string(ERR_get_error(), NULL)); + return -1; + } + + return 0; +} + + +int tls_get_cipher(void *ssl_ctx, struct tls_connection *conn, + char *buf, size_t buflen) +{ + const char *name; + if (conn == NULL || conn->ssl == NULL) + return -1; + + name = SSL_get_cipher(conn->ssl); + if (name == NULL) + return -1; + + snprintf(buf, buflen, "%s", name); + return 0; +} + + +int tls_connection_enable_workaround(void *ssl_ctx, + struct tls_connection *conn) +{ + SSL_set_options(conn->ssl, SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS); + + return 0; +} + + +#ifdef EAP_FAST +/* ClientHello TLS extensions require a patch to openssl, so this function is + * commented out unless explicitly needed for EAP-FAST in order to be able to + * build this file with unmodified openssl. */ +int tls_connection_client_hello_ext(void *ssl_ctx, struct tls_connection *conn, + int ext_type, const u8 *data, + size_t data_len) +{ + struct tls_ext_hdr { + u16 extensions_len; + u16 extension_type; + u16 extension_len; + } *hdr; + + if (conn == NULL || conn->ssl == NULL) + return -1; + OPENSSL_free(conn->ssl->hello_extension); + if (data == NULL) { + conn->ssl->hello_extension = NULL; + conn->ssl->hello_extension_len = 0; + return 0; + } + if (data_len == 0) { + conn->ssl->hello_extension = OPENSSL_malloc(1); + conn->ssl->hello_extension_len = 0; + return 0; + } + conn->ssl->hello_extension = OPENSSL_malloc(sizeof(*hdr) + data_len); + if (conn->ssl->hello_extension == NULL) + return -1; + + hdr = (struct tls_ext_hdr *) conn->ssl->hello_extension; + hdr->extensions_len = host_to_be16(sizeof(*hdr) - 2 + data_len); + hdr->extension_type = host_to_be16(ext_type); + hdr->extension_len = host_to_be16(data_len); + memcpy(hdr + 1, data, data_len); + conn->ssl->hello_extension_len = sizeof(*hdr) + data_len; + + return 0; +} +#endif /* EAP_FAST */ diff --git a/contrib/wpa_supplicant/todo.txt b/contrib/wpa_supplicant/todo.txt new file mode 100644 index 0000000..52f3349 --- /dev/null +++ b/contrib/wpa_supplicant/todo.txt @@ -0,0 +1,54 @@ +To do: +- add WPA support to Linux Wireless Extensions +- add support for other drivers +- implement GUI for WPA Supplicant/Xsupplicant/iwconfig/iwlist + (easy to use configuration and network stats, etc.) +- add support for opportunistic PMKSA caching +- hostap: try other roaming modes + NOTE: current mode (manual roaming) does not really roam at all.. + Firmware did not notice the current AP disappearing.. +- EAP-MSCHAPv2: add support for password changing +- add support for WPA with ap_scan=0 (update selected cipher etc. based on + AssocInfo; make sure these match with configuration) +- add driver interface for using wpa_supplicant with wired interface + (or a separate program using EAPOL library) +- wpa_supplicant.conf g+rw so that frontend can change wpa_supplicant.conf + and RECONFIG wpa_supplicant (?) + (or wpa_supplicant changes .conf and ctrl interface gets support for + changing config?) +- optional security separation (build time option): run EAPOL state machines + as non-root (need to add something like socketpair between privileged root + process and non-root handler; send EAPOL packets between processes + and send keying data from non-root -> privileged) + EAPOL-Key processing (WPA & WEP keys) could be in privileged part + at least in the beginning; some parts might end up being moved to + non-root part eventually +- consider closing smart card / PCSC connection when EAP-SIM/EAP-AKA + authentication has been completed (cache scard data based on serial#(?) + and try to optimize next connection if the same card is present for next + auth) +- EAP-AKA: AT_CHECKCODE +- EAP-SIM/AKA: AT_RESULT_IND +- abort auth if EAP method initialization fails and there no other + accepted methods (i.e., do not send NAK for the same method that just + failed) +- on disconnect event, could try to associate with another AP if one is + present in scan results; would need to update scan results periodically.. +- add flag scan_requested and only try to re-associate if this is set when + new scan results are received; this would allow background scans without + triggering re-assoc.. +- if driver/hw is not WPA2 capable, must remove WPA_PROTO_RSN flag from + ssid->proto fields to avoid detecting downgrade attacks when the driver + is not reporting RSN IE, but msg 3/4 has one +- read CA certs from PFX file +- EAP-SIM/AKA: if SIM reader initialization fails, do not start authentication +- Cisco AP and non-zero keyidx for unicast -> map to broadcast + (actually, this already works with driver_ndis; so maybe just change + driver_*.c to do the mapping for drivers that cannot handle non-zero keyidx + for unicast) +- IEEE 802.1X and key update with driver_ndis?? wpa_supplicant did not seem + to see unencrypted EAPOL-Key frames at all.. +- update developer.txt to match with current implementation + (driver API updates, EAP methods) +- driver_wext.c and driver that does not support WPA -> fix plaintext, WEP, and + IEEE 802.1X operation (e.g., use capabilities to report no support for WPA) diff --git a/contrib/wpa_supplicant/version.h b/contrib/wpa_supplicant/version.h new file mode 100644 index 0000000..d0a1a86 --- /dev/null +++ b/contrib/wpa_supplicant/version.h @@ -0,0 +1,6 @@ +#ifndef VERSION_H +#define VERSION_H + +#define VERSION_STR "0.3.8" + +#endif /* VERSION_H */ diff --git a/contrib/wpa_supplicant/wpa.c b/contrib/wpa_supplicant/wpa.c new file mode 100644 index 0000000..5d4ce73 --- /dev/null +++ b/contrib/wpa_supplicant/wpa.c @@ -0,0 +1,2445 @@ +/* + * WPA Supplicant + * Copyright (c) 2003-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <sys/time.h> +#ifndef CONFIG_NATIVE_WINDOWS +#include <netinet/in.h> +#endif /* CONFIG_NATIVE_WINDOWS */ +#include <string.h> +#include <time.h> + +#include "common.h" +#include "md5.h" +#include "sha1.h" +#include "rc4.h" +#include "aes_wrap.h" +#include "wpa.h" +#include "driver.h" +#include "eloop.h" +#include "wpa_supplicant.h" +#include "config.h" +#include "l2_packet.h" +#include "eapol_sm.h" +#include "wpa_supplicant_i.h" + +static void rsn_preauth_candidate_process(struct wpa_supplicant *wpa_s); + +#define PMKID_CANDIDATE_PRIO_SCAN 1000 + +/* TODO: make these configurable */ +static const int dot11RSNAConfigPMKLifetime = 43200; +static const int dot11RSNAConfigPMKReauthThreshold = 70; +static const int dot11RSNAConfigSATimeout = 60; +static const int pmksa_cache_max_entries = 32; + +static const int WPA_SELECTOR_LEN = 4; +static const u8 WPA_OUI_TYPE[] = { 0x00, 0x50, 0xf2, 1 }; +static const u16 WPA_VERSION = 1; +static const u8 WPA_AUTH_KEY_MGMT_NONE[] = { 0x00, 0x50, 0xf2, 0 }; +static const u8 WPA_AUTH_KEY_MGMT_UNSPEC_802_1X[] = { 0x00, 0x50, 0xf2, 1 }; +static const u8 WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X[] = { 0x00, 0x50, 0xf2, 2 }; +static const u8 WPA_CIPHER_SUITE_NONE[] = { 0x00, 0x50, 0xf2, 0 }; +static const u8 WPA_CIPHER_SUITE_WEP40[] = { 0x00, 0x50, 0xf2, 1 }; +static const u8 WPA_CIPHER_SUITE_TKIP[] = { 0x00, 0x50, 0xf2, 2 }; +static const u8 WPA_CIPHER_SUITE_WRAP[] = { 0x00, 0x50, 0xf2, 3 }; +static const u8 WPA_CIPHER_SUITE_CCMP[] = { 0x00, 0x50, 0xf2, 4 }; +static const u8 WPA_CIPHER_SUITE_WEP104[] = { 0x00, 0x50, 0xf2, 5 }; + +/* WPA IE version 1 + * 00-50-f2:1 (OUI:OUI type) + * 0x01 0x00 (version; little endian) + * (all following fields are optional:) + * Group Suite Selector (4 octets) (default: TKIP) + * Pairwise Suite Count (2 octets, little endian) (default: 1) + * Pairwise Suite List (4 * n octets) (default: TKIP) + * Authenticated Key Management Suite Count (2 octets, little endian) + * (default: 1) + * Authenticated Key Management Suite List (4 * n octets) + * (default: unspec 802.1x) + * WPA Capabilities (2 octets, little endian) (default: 0) + */ + +struct wpa_ie_hdr { + u8 elem_id; + u8 len; + u8 oui[3]; + u8 oui_type; + u16 version; +} __attribute__ ((packed)); + + +static const int RSN_SELECTOR_LEN = 4; +static const u16 RSN_VERSION = 1; +static const u8 RSN_AUTH_KEY_MGMT_UNSPEC_802_1X[] = { 0x00, 0x0f, 0xac, 1 }; +static const u8 RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X[] = { 0x00, 0x0f, 0xac, 2 }; +static const u8 RSN_CIPHER_SUITE_NONE[] = { 0x00, 0x0f, 0xac, 0 }; +static const u8 RSN_CIPHER_SUITE_WEP40[] = { 0x00, 0x0f, 0xac, 1 }; +static const u8 RSN_CIPHER_SUITE_TKIP[] = { 0x00, 0x0f, 0xac, 2 }; +static const u8 RSN_CIPHER_SUITE_WRAP[] = { 0x00, 0x0f, 0xac, 3 }; +static const u8 RSN_CIPHER_SUITE_CCMP[] = { 0x00, 0x0f, 0xac, 4 }; +static const u8 RSN_CIPHER_SUITE_WEP104[] = { 0x00, 0x0f, 0xac, 5 }; + +/* EAPOL-Key Key Data Encapsulation + * GroupKey and STAKey require encryption, otherwise, encryption is optional. + */ +static const u8 RSN_KEY_DATA_GROUPKEY[] = { 0x00, 0x0f, 0xac, 1 }; +static const u8 RSN_KEY_DATA_STAKEY[] = { 0x00, 0x0f, 0xac, 2 }; +static const u8 RSN_KEY_DATA_MAC_ADDR[] = { 0x00, 0x0f, 0xac, 3 }; +static const u8 RSN_KEY_DATA_PMKID[] = { 0x00, 0x0f, 0xac, 4 }; + +/* 1/4: PMKID + * 2/4: RSN IE + * 3/4: one or two RSN IEs + GTK IE (encrypted) + * 4/4: empty + * 1/2: GTK IE (encrypted) + * 2/2: empty + */ + +/* RSN IE version 1 + * 0x01 0x00 (version; little endian) + * (all following fields are optional:) + * Group Suite Selector (4 octets) (default: CCMP) + * Pairwise Suite Count (2 octets, little endian) (default: 1) + * Pairwise Suite List (4 * n octets) (default: CCMP) + * Authenticated Key Management Suite Count (2 octets, little endian) + * (default: 1) + * Authenticated Key Management Suite List (4 * n octets) + * (default: unspec 802.1x) + * RSN Capabilities (2 octets, little endian) (default: 0) + * PMKID Count (2 octets) (default: 0) + * PMKID List (16 * n octets) + */ + +struct rsn_ie_hdr { + u8 elem_id; /* WLAN_EID_RSN */ + u8 len; + u16 version; +} __attribute__ ((packed)); + + +static int wpa_selector_to_bitfield(u8 *s) +{ + if (memcmp(s, WPA_CIPHER_SUITE_NONE, WPA_SELECTOR_LEN) == 0) + return WPA_CIPHER_NONE; + if (memcmp(s, WPA_CIPHER_SUITE_WEP40, WPA_SELECTOR_LEN) == 0) + return WPA_CIPHER_WEP40; + if (memcmp(s, WPA_CIPHER_SUITE_TKIP, WPA_SELECTOR_LEN) == 0) + return WPA_CIPHER_TKIP; + if (memcmp(s, WPA_CIPHER_SUITE_CCMP, WPA_SELECTOR_LEN) == 0) + return WPA_CIPHER_CCMP; + if (memcmp(s, WPA_CIPHER_SUITE_WEP104, WPA_SELECTOR_LEN) == 0) + return WPA_CIPHER_WEP104; + return 0; +} + + +static int wpa_key_mgmt_to_bitfield(u8 *s) +{ + if (memcmp(s, WPA_AUTH_KEY_MGMT_UNSPEC_802_1X, WPA_SELECTOR_LEN) == 0) + return WPA_KEY_MGMT_IEEE8021X; + if (memcmp(s, WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X, WPA_SELECTOR_LEN) == + 0) + return WPA_KEY_MGMT_PSK; + if (memcmp(s, WPA_AUTH_KEY_MGMT_NONE, WPA_SELECTOR_LEN) == 0) + return WPA_KEY_MGMT_WPA_NONE; + return 0; +} + + +static int rsn_selector_to_bitfield(u8 *s) +{ + if (memcmp(s, RSN_CIPHER_SUITE_NONE, RSN_SELECTOR_LEN) == 0) + return WPA_CIPHER_NONE; + if (memcmp(s, RSN_CIPHER_SUITE_WEP40, RSN_SELECTOR_LEN) == 0) + return WPA_CIPHER_WEP40; + if (memcmp(s, RSN_CIPHER_SUITE_TKIP, RSN_SELECTOR_LEN) == 0) + return WPA_CIPHER_TKIP; + if (memcmp(s, RSN_CIPHER_SUITE_CCMP, RSN_SELECTOR_LEN) == 0) + return WPA_CIPHER_CCMP; + if (memcmp(s, RSN_CIPHER_SUITE_WEP104, RSN_SELECTOR_LEN) == 0) + return WPA_CIPHER_WEP104; + return 0; +} + + +static int rsn_key_mgmt_to_bitfield(u8 *s) +{ + if (memcmp(s, RSN_AUTH_KEY_MGMT_UNSPEC_802_1X, RSN_SELECTOR_LEN) == 0) + return WPA_KEY_MGMT_IEEE8021X; + if (memcmp(s, RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X, RSN_SELECTOR_LEN) == + 0) + return WPA_KEY_MGMT_PSK; + return 0; +} + + +static void rsn_pmkid(u8 *pmk, u8 *aa, u8 *spa, u8 *pmkid) +{ + char *title = "PMK Name"; + const unsigned char *addr[3]; + const size_t len[3] = { 8, ETH_ALEN, ETH_ALEN }; + unsigned char hash[SHA1_MAC_LEN]; + + addr[0] = (unsigned char *) title; + addr[1] = aa; + addr[2] = spa; + + hmac_sha1_vector(pmk, PMK_LEN, 3, addr, len, hash); + memcpy(pmkid, hash, PMKID_LEN); +} + + +static void pmksa_cache_set_expiration(struct wpa_supplicant *wpa_s); + + +static void pmksa_cache_free_entry(struct wpa_supplicant *wpa_s, + struct rsn_pmksa_cache *entry) +{ + free(entry); + wpa_s->pmksa_count--; + if (wpa_s->cur_pmksa == entry) { + wpa_printf(MSG_DEBUG, "RSN: removed current PMKSA entry"); + /* TODO: should drop PMK and PTK and trigger new key + * negotiation */ + wpa_s->cur_pmksa = NULL; + } +} + + +static void pmksa_cache_expire(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + time_t now; + + time(&now); + while (wpa_s->pmksa && wpa_s->pmksa->expiration <= now) { + struct rsn_pmksa_cache *entry = wpa_s->pmksa; + wpa_s->pmksa = entry->next; + wpa_printf(MSG_DEBUG, "RSN: expired PMKSA cache entry for " + MACSTR, MAC2STR(entry->aa)); + pmksa_cache_free_entry(wpa_s, entry); + } + + pmksa_cache_set_expiration(wpa_s); +} + + +static void pmksa_cache_set_expiration(struct wpa_supplicant *wpa_s) +{ + int sec; + eloop_cancel_timeout(pmksa_cache_expire, wpa_s, NULL); + if (wpa_s->pmksa == NULL) + return; + sec = wpa_s->pmksa->expiration - time(NULL); + if (sec < 0) + sec = 0; + eloop_register_timeout(sec + 1, 0, pmksa_cache_expire, wpa_s, NULL); +} + + +static void pmksa_cache_add(struct wpa_supplicant *wpa_s, u8 *pmk, + size_t pmk_len, u8 *aa, u8 *spa) +{ + struct rsn_pmksa_cache *entry, *pos, *prev; + + if (wpa_s->proto != WPA_PROTO_RSN || pmk_len > PMK_LEN) + return; + + entry = malloc(sizeof(*entry)); + if (entry == NULL) + return; + memset(entry, 0, sizeof(*entry)); + memcpy(entry->pmk, pmk, pmk_len); + entry->pmk_len = pmk_len; + rsn_pmkid(pmk, aa, spa, entry->pmkid); + entry->expiration = time(NULL) + dot11RSNAConfigPMKLifetime; + entry->akmp = WPA_KEY_MGMT_IEEE8021X; + memcpy(entry->aa, aa, ETH_ALEN); + + /* Replace an old entry for the same Authenticator (if found) with the + * new entry */ + pos = wpa_s->pmksa; + prev = NULL; + while (pos) { + if (memcmp(aa, pos->aa, ETH_ALEN) == 0) { + if (prev == NULL) + wpa_s->pmksa = pos->next; + else + prev->next = pos->next; + pmksa_cache_free_entry(wpa_s, pos); + break; + } + prev = pos; + pos = pos->next; + } + + if (wpa_s->pmksa_count >= pmksa_cache_max_entries && wpa_s->pmksa) { + /* Remove the oldest entry to make room for the new entry */ + pos = wpa_s->pmksa; + wpa_s->pmksa = pos->next; + wpa_printf(MSG_DEBUG, "RSN: removed the oldest PMKSA cache " + "entry (for " MACSTR ") to make room for new one", + MAC2STR(pos->aa)); + wpa_drv_remove_pmkid(wpa_s, pos->aa, pos->pmkid); + pmksa_cache_free_entry(wpa_s, pos); + } + + /* Add the new entry; order by expiration time */ + pos = wpa_s->pmksa; + prev = NULL; + while (pos) { + if (pos->expiration > entry->expiration) + break; + prev = pos; + pos = pos->next; + } + if (prev == NULL) { + entry->next = wpa_s->pmksa; + wpa_s->pmksa = entry; + } else { + entry->next = prev->next; + prev->next = entry; + } + wpa_s->pmksa_count++; + wpa_printf(MSG_DEBUG, "RSN: added PMKSA cache entry for " MACSTR, + MAC2STR(entry->aa)); + wpa_drv_add_pmkid(wpa_s, entry->aa, entry->pmkid); +} + + +void pmksa_cache_free(struct wpa_supplicant *wpa_s) +{ + struct rsn_pmksa_cache *entry, *prev; + + entry = wpa_s->pmksa; + wpa_s->pmksa = NULL; + while (entry) { + prev = entry; + entry = entry->next; + free(prev); + } + pmksa_cache_set_expiration(wpa_s); + wpa_s->cur_pmksa = NULL; +} + + +struct rsn_pmksa_cache * pmksa_cache_get(struct wpa_supplicant *wpa_s, + u8 *aa, u8 *pmkid) +{ + struct rsn_pmksa_cache *entry = wpa_s->pmksa; + while (entry) { + if ((aa == NULL || memcmp(entry->aa, aa, ETH_ALEN) == 0) && + (pmkid == NULL || + memcmp(entry->pmkid, pmkid, PMKID_LEN) == 0)) + return entry; + entry = entry->next; + } + return NULL; +} + + +int pmksa_cache_list(struct wpa_supplicant *wpa_s, char *buf, size_t len) +{ + int i, j; + char *pos = buf; + struct rsn_pmksa_cache *entry; + time_t now; + + time(&now); + pos += snprintf(pos, buf + len - pos, + "Index / AA / PMKID / expiration (in seconds)\n"); + i = 0; + entry = wpa_s->pmksa; + while (entry) { + i++; + pos += snprintf(pos, buf + len - pos, "%d " MACSTR " ", + i, MAC2STR(entry->aa)); + for (j = 0; j < PMKID_LEN; j++) + pos += snprintf(pos, buf + len - pos, "%02x", + entry->pmkid[j]); + pos += snprintf(pos, buf + len - pos, " %d\n", + (int) (entry->expiration - now)); + entry = entry->next; + } + return pos - buf; +} + + +void pmksa_candidate_free(struct wpa_supplicant *wpa_s) +{ + struct rsn_pmksa_candidate *entry, *prev; + + entry = wpa_s->pmksa_candidates; + wpa_s->pmksa_candidates = NULL; + while (entry) { + prev = entry; + entry = entry->next; + free(prev); + } +} + + +static int wpa_parse_wpa_ie_wpa(struct wpa_supplicant *wpa_s, u8 *wpa_ie, + size_t wpa_ie_len, struct wpa_ie_data *data) +{ + struct wpa_ie_hdr *hdr; + u8 *pos; + int left; + int i, count; + + data->proto = WPA_PROTO_WPA; + data->pairwise_cipher = WPA_CIPHER_TKIP; + data->group_cipher = WPA_CIPHER_TKIP; + data->key_mgmt = WPA_KEY_MGMT_IEEE8021X; + data->capabilities = 0; + data->pmkid = NULL; + data->num_pmkid = 0; + + if (wpa_ie_len == 0) { + /* No WPA IE - fail silently */ + return -1; + } + + if (wpa_ie_len < sizeof(struct wpa_ie_hdr)) { + wpa_printf(MSG_DEBUG, "%s: ie len too short %lu", + __func__, (unsigned long) wpa_ie_len); + return -1; + } + + hdr = (struct wpa_ie_hdr *) wpa_ie; + + if (hdr->elem_id != GENERIC_INFO_ELEM || + hdr->len != wpa_ie_len - 2 || + memcmp(&hdr->oui, WPA_OUI_TYPE, WPA_SELECTOR_LEN) != 0 || + le_to_host16(hdr->version) != WPA_VERSION) { + wpa_printf(MSG_DEBUG, "%s: malformed ie or unknown version", + __func__); + return -1; + } + + pos = (u8 *) (hdr + 1); + left = wpa_ie_len - sizeof(*hdr); + + if (left >= WPA_SELECTOR_LEN) { + data->group_cipher = wpa_selector_to_bitfield(pos); + pos += WPA_SELECTOR_LEN; + left -= WPA_SELECTOR_LEN; + } else if (left > 0) { + wpa_printf(MSG_DEBUG, "%s: ie length mismatch, %u too much", + __func__, left); + return -1; + } + + if (left >= 2) { + data->pairwise_cipher = 0; + count = pos[0] | (pos[1] << 8); + pos += 2; + left -= 2; + if (count == 0 || left < count * WPA_SELECTOR_LEN) { + wpa_printf(MSG_DEBUG, "%s: ie count botch (pairwise), " + "count %u left %u", __func__, count, left); + return -1; + } + for (i = 0; i < count; i++) { + data->pairwise_cipher |= wpa_selector_to_bitfield(pos); + pos += WPA_SELECTOR_LEN; + left -= WPA_SELECTOR_LEN; + } + } else if (left == 1) { + wpa_printf(MSG_DEBUG, "%s: ie too short (for key mgmt)", + __func__); + return -1; + } + + if (left >= 2) { + data->key_mgmt = 0; + count = pos[0] | (pos[1] << 8); + pos += 2; + left -= 2; + if (count == 0 || left < count * WPA_SELECTOR_LEN) { + wpa_printf(MSG_DEBUG, "%s: ie count botch (key mgmt), " + "count %u left %u", __func__, count, left); + return -1; + } + for (i = 0; i < count; i++) { + data->key_mgmt |= wpa_key_mgmt_to_bitfield(pos); + pos += WPA_SELECTOR_LEN; + left -= WPA_SELECTOR_LEN; + } + } else if (left == 1) { + wpa_printf(MSG_DEBUG, "%s: ie too short (for capabilities)", + __func__); + return -1; + } + + if (left >= 2) { + data->capabilities = pos[0] | (pos[1] << 8); + pos += 2; + left -= 2; + } + + if (left > 0) { + wpa_printf(MSG_DEBUG, "%s: ie has %u trailing bytes", + __func__, left); + return -1; + } + + return 0; +} + + +static int wpa_parse_wpa_ie_rsn(struct wpa_supplicant *wpa_s, u8 *rsn_ie, + size_t rsn_ie_len, struct wpa_ie_data *data) +{ + struct rsn_ie_hdr *hdr; + u8 *pos; + int left; + int i, count; + + data->proto = WPA_PROTO_RSN; + data->pairwise_cipher = WPA_CIPHER_CCMP; + data->group_cipher = WPA_CIPHER_CCMP; + data->key_mgmt = WPA_KEY_MGMT_IEEE8021X; + data->capabilities = 0; + data->pmkid = NULL; + data->num_pmkid = 0; + + if (rsn_ie_len == 0) { + /* No RSN IE - fail silently */ + return -1; + } + + if (rsn_ie_len < sizeof(struct rsn_ie_hdr)) { + wpa_printf(MSG_DEBUG, "%s: ie len too short %lu", + __func__, (unsigned long) rsn_ie_len); + return -1; + } + + hdr = (struct rsn_ie_hdr *) rsn_ie; + + if (hdr->elem_id != RSN_INFO_ELEM || + hdr->len != rsn_ie_len - 2 || + le_to_host16(hdr->version) != RSN_VERSION) { + wpa_printf(MSG_DEBUG, "%s: malformed ie or unknown version", + __func__); + return -1; + } + + pos = (u8 *) (hdr + 1); + left = rsn_ie_len - sizeof(*hdr); + + if (left >= RSN_SELECTOR_LEN) { + data->group_cipher = rsn_selector_to_bitfield(pos); + pos += RSN_SELECTOR_LEN; + left -= RSN_SELECTOR_LEN; + } else if (left > 0) { + wpa_printf(MSG_DEBUG, "%s: ie length mismatch, %u too much", + __func__, left); + return -1; + } + + if (left >= 2) { + data->pairwise_cipher = 0; + count = pos[0] | (pos[1] << 8); + pos += 2; + left -= 2; + if (count == 0 || left < count * RSN_SELECTOR_LEN) { + wpa_printf(MSG_DEBUG, "%s: ie count botch (pairwise), " + "count %u left %u", __func__, count, left); + return -1; + } + for (i = 0; i < count; i++) { + data->pairwise_cipher |= rsn_selector_to_bitfield(pos); + pos += RSN_SELECTOR_LEN; + left -= RSN_SELECTOR_LEN; + } + } else if (left == 1) { + wpa_printf(MSG_DEBUG, "%s: ie too short (for key mgmt)", + __func__); + return -1; + } + + if (left >= 2) { + data->key_mgmt = 0; + count = pos[0] | (pos[1] << 8); + pos += 2; + left -= 2; + if (count == 0 || left < count * RSN_SELECTOR_LEN) { + wpa_printf(MSG_DEBUG, "%s: ie count botch (key mgmt), " + "count %u left %u", __func__, count, left); + return -1; + } + for (i = 0; i < count; i++) { + data->key_mgmt |= rsn_key_mgmt_to_bitfield(pos); + pos += RSN_SELECTOR_LEN; + left -= RSN_SELECTOR_LEN; + } + } else if (left == 1) { + wpa_printf(MSG_DEBUG, "%s: ie too short (for capabilities)", + __func__); + return -1; + } + + if (left >= 2) { + data->capabilities = pos[0] | (pos[1] << 8); + pos += 2; + left -= 2; + } + + if (left >= 2) { + data->num_pmkid = pos[0] | (pos[1] << 8); + pos += 2; + left -= 2; + if (left < data->num_pmkid * PMKID_LEN) { + wpa_printf(MSG_DEBUG, "%s: PMKID underflow " + "(num_pmkid=%d left=%d)", + __func__, data->num_pmkid, left); + data->num_pmkid = 0; + } else { + data->pmkid = pos; + pos += data->num_pmkid * PMKID_LEN; + left -= data->num_pmkid * PMKID_LEN; + } + } + + if (left > 0) { + wpa_printf(MSG_DEBUG, "%s: ie has %u trailing bytes - ignored", + __func__, left); + } + + return 0; +} + + +int wpa_parse_wpa_ie(struct wpa_supplicant *wpa_s, u8 *wpa_ie, + size_t wpa_ie_len, struct wpa_ie_data *data) +{ + if (wpa_ie_len >= 1 && wpa_ie[0] == RSN_INFO_ELEM) + return wpa_parse_wpa_ie_rsn(wpa_s, wpa_ie, wpa_ie_len, data); + else + return wpa_parse_wpa_ie_wpa(wpa_s, wpa_ie, wpa_ie_len, data); +} + + +static int wpa_gen_wpa_ie_wpa(struct wpa_supplicant *wpa_s, u8 *wpa_ie) +{ + u8 *pos; + struct wpa_ie_hdr *hdr; + + hdr = (struct wpa_ie_hdr *) wpa_ie; + hdr->elem_id = GENERIC_INFO_ELEM; + memcpy(&hdr->oui, WPA_OUI_TYPE, WPA_SELECTOR_LEN); + hdr->version = host_to_le16(WPA_VERSION); + pos = (u8 *) (hdr + 1); + + if (wpa_s->group_cipher == WPA_CIPHER_CCMP) { + memcpy(pos, WPA_CIPHER_SUITE_CCMP, WPA_SELECTOR_LEN); + } else if (wpa_s->group_cipher == WPA_CIPHER_TKIP) { + memcpy(pos, WPA_CIPHER_SUITE_TKIP, WPA_SELECTOR_LEN); + } else if (wpa_s->group_cipher == WPA_CIPHER_WEP104) { + memcpy(pos, WPA_CIPHER_SUITE_WEP104, WPA_SELECTOR_LEN); + } else if (wpa_s->group_cipher == WPA_CIPHER_WEP40) { + memcpy(pos, WPA_CIPHER_SUITE_WEP40, WPA_SELECTOR_LEN); + } else { + wpa_printf(MSG_WARNING, "Invalid group cipher (%d).", + wpa_s->group_cipher); + return -1; + } + pos += WPA_SELECTOR_LEN; + + *pos++ = 1; + *pos++ = 0; + if (wpa_s->pairwise_cipher == WPA_CIPHER_CCMP) { + memcpy(pos, WPA_CIPHER_SUITE_CCMP, WPA_SELECTOR_LEN); + } else if (wpa_s->pairwise_cipher == WPA_CIPHER_TKIP) { + memcpy(pos, WPA_CIPHER_SUITE_TKIP, WPA_SELECTOR_LEN); + } else if (wpa_s->pairwise_cipher == WPA_CIPHER_NONE) { + memcpy(pos, WPA_CIPHER_SUITE_NONE, WPA_SELECTOR_LEN); + } else { + wpa_printf(MSG_WARNING, "Invalid pairwise cipher (%d).", + wpa_s->pairwise_cipher); + return -1; + } + pos += WPA_SELECTOR_LEN; + + *pos++ = 1; + *pos++ = 0; + if (wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X) { + memcpy(pos, WPA_AUTH_KEY_MGMT_UNSPEC_802_1X, WPA_SELECTOR_LEN); + } else if (wpa_s->key_mgmt == WPA_KEY_MGMT_PSK) { + memcpy(pos, WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X, + WPA_SELECTOR_LEN); + } else if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE) { + memcpy(pos, WPA_AUTH_KEY_MGMT_NONE, WPA_SELECTOR_LEN); + } else { + wpa_printf(MSG_WARNING, "Invalid key management type (%d).", + wpa_s->key_mgmt); + return -1; + } + pos += WPA_SELECTOR_LEN; + + /* WPA Capabilities; use defaults, so no need to include it */ + + hdr->len = (pos - wpa_ie) - 2; + + return pos - wpa_ie; +} + + +static int wpa_gen_wpa_ie_rsn(struct wpa_supplicant *wpa_s, u8 *rsn_ie) +{ + u8 *pos; + struct rsn_ie_hdr *hdr; + + hdr = (struct rsn_ie_hdr *) rsn_ie; + hdr->elem_id = RSN_INFO_ELEM; + hdr->version = host_to_le16(RSN_VERSION); + pos = (u8 *) (hdr + 1); + + if (wpa_s->group_cipher == WPA_CIPHER_CCMP) { + memcpy(pos, RSN_CIPHER_SUITE_CCMP, RSN_SELECTOR_LEN); + } else if (wpa_s->group_cipher == WPA_CIPHER_TKIP) { + memcpy(pos, RSN_CIPHER_SUITE_TKIP, RSN_SELECTOR_LEN); + } else if (wpa_s->group_cipher == WPA_CIPHER_WEP104) { + memcpy(pos, RSN_CIPHER_SUITE_WEP104, RSN_SELECTOR_LEN); + } else if (wpa_s->group_cipher == WPA_CIPHER_WEP40) { + memcpy(pos, RSN_CIPHER_SUITE_WEP40, RSN_SELECTOR_LEN); + } else { + wpa_printf(MSG_WARNING, "Invalid group cipher (%d).", + wpa_s->group_cipher); + return -1; + } + pos += RSN_SELECTOR_LEN; + + *pos++ = 1; + *pos++ = 0; + if (wpa_s->pairwise_cipher == WPA_CIPHER_CCMP) { + memcpy(pos, RSN_CIPHER_SUITE_CCMP, RSN_SELECTOR_LEN); + } else if (wpa_s->pairwise_cipher == WPA_CIPHER_TKIP) { + memcpy(pos, RSN_CIPHER_SUITE_TKIP, RSN_SELECTOR_LEN); + } else if (wpa_s->pairwise_cipher == WPA_CIPHER_NONE) { + memcpy(pos, RSN_CIPHER_SUITE_NONE, RSN_SELECTOR_LEN); + } else { + wpa_printf(MSG_WARNING, "Invalid pairwise cipher (%d).", + wpa_s->pairwise_cipher); + return -1; + } + pos += RSN_SELECTOR_LEN; + + *pos++ = 1; + *pos++ = 0; + if (wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X) { + memcpy(pos, RSN_AUTH_KEY_MGMT_UNSPEC_802_1X, RSN_SELECTOR_LEN); + } else if (wpa_s->key_mgmt == WPA_KEY_MGMT_PSK) { + memcpy(pos, RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X, + RSN_SELECTOR_LEN); + } else { + wpa_printf(MSG_WARNING, "Invalid key management type (%d).", + wpa_s->key_mgmt); + return -1; + } + pos += RSN_SELECTOR_LEN; + + /* RSN Capabilities */ + *pos++ = 0; + *pos++ = 0; + + if (wpa_s->cur_pmksa) { + /* PMKID Count (2 octets, little endian) */ + *pos++ = 1; + *pos++ = 0; + /* PMKID */ + memcpy(pos, wpa_s->cur_pmksa->pmkid, PMKID_LEN); + pos += PMKID_LEN; + } + + hdr->len = (pos - rsn_ie) - 2; + + return pos - rsn_ie; +} + + +int wpa_gen_wpa_ie(struct wpa_supplicant *wpa_s, u8 *wpa_ie) +{ + if (wpa_s->proto == WPA_PROTO_RSN) + return wpa_gen_wpa_ie_rsn(wpa_s, wpa_ie); + else + return wpa_gen_wpa_ie_wpa(wpa_s, wpa_ie); +} + + +static void wpa_pmk_to_ptk(u8 *pmk, size_t pmk_len, u8 *addr1, u8 *addr2, + u8 *nonce1, u8 *nonce2, u8 *ptk, size_t ptk_len) +{ + u8 data[2 * ETH_ALEN + 2 * 32]; + + /* PTK = PRF-X(PMK, "Pairwise key expansion", + * Min(AA, SA) || Max(AA, SA) || + * Min(ANonce, SNonce) || Max(ANonce, SNonce)) */ + + if (memcmp(addr1, addr2, ETH_ALEN) < 0) { + memcpy(data, addr1, ETH_ALEN); + memcpy(data + ETH_ALEN, addr2, ETH_ALEN); + } else { + memcpy(data, addr2, ETH_ALEN); + memcpy(data + ETH_ALEN, addr1, ETH_ALEN); + } + + if (memcmp(nonce1, nonce2, 32) < 0) { + memcpy(data + 2 * ETH_ALEN, nonce1, 32); + memcpy(data + 2 * ETH_ALEN + 32, nonce2, 32); + } else { + memcpy(data + 2 * ETH_ALEN, nonce2, 32); + memcpy(data + 2 * ETH_ALEN + 32, nonce1, 32); + } + + sha1_prf(pmk, pmk_len, "Pairwise key expansion", data, sizeof(data), + ptk, ptk_len); + + wpa_hexdump_key(MSG_DEBUG, "WPA: PMK", pmk, pmk_len); + wpa_hexdump_key(MSG_DEBUG, "WPA: PTK", ptk, ptk_len); +} + + +struct wpa_ssid * wpa_supplicant_get_ssid(struct wpa_supplicant *wpa_s) +{ + struct wpa_ssid *entry; + u8 ssid[MAX_SSID_LEN]; + int ssid_len; + u8 bssid[ETH_ALEN]; + + ssid_len = wpa_drv_get_ssid(wpa_s, ssid); + if (ssid_len < 0) { + wpa_printf(MSG_WARNING, "Could not read SSID from driver."); + return NULL; + } + + if (wpa_drv_get_bssid(wpa_s, bssid) < 0) { + wpa_printf(MSG_WARNING, "Could not read BSSID from driver."); + return NULL; + } + + entry = wpa_s->conf->ssid; + while (entry) { + if (ssid_len == entry->ssid_len && + memcmp(ssid, entry->ssid, ssid_len) == 0 && + (!entry->bssid_set || + memcmp(bssid, entry->bssid, ETH_ALEN) == 0)) + return entry; + entry = entry->next; + } + + return NULL; +} + + +static void wpa_eapol_key_mic(u8 *key, int ver, u8 *buf, size_t len, u8 *mic) +{ + if (ver == WPA_KEY_INFO_TYPE_HMAC_MD5_RC4) { + hmac_md5(key, 16, buf, len, mic); + } else if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) { + u8 hash[SHA1_MAC_LEN]; + hmac_sha1(key, 16, buf, len, hash); + memcpy(mic, hash, MD5_MAC_LEN); + } +} + + +void wpa_supplicant_key_request(struct wpa_supplicant *wpa_s, + int error, int pairwise) +{ + int rlen; + struct ieee802_1x_hdr *hdr; + struct wpa_eapol_key *reply; + unsigned char *rbuf; + struct l2_ethhdr *ethhdr; + int key_info, ver; + u8 bssid[ETH_ALEN]; + + if (wpa_s->pairwise_cipher == WPA_CIPHER_CCMP) + ver = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES; + else + ver = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4; + + if (wpa_drv_get_bssid(wpa_s, bssid) < 0) { + wpa_printf(MSG_WARNING, "Failed to read BSSID for EAPOL-Key " + "request"); + return; + } + + rlen = sizeof(*ethhdr) + sizeof(*hdr) + sizeof(*reply); + rbuf = malloc(rlen); + if (rbuf == NULL) + return; + + memset(rbuf, 0, rlen); + ethhdr = (struct l2_ethhdr *) rbuf; + memcpy(ethhdr->h_dest, bssid, ETH_ALEN); + memcpy(ethhdr->h_source, wpa_s->own_addr, ETH_ALEN); + ethhdr->h_proto = htons(ETH_P_EAPOL); + + hdr = (struct ieee802_1x_hdr *) (ethhdr + 1); + hdr->version = wpa_s->conf->eapol_version; + hdr->type = IEEE802_1X_TYPE_EAPOL_KEY; + hdr->length = htons(sizeof(*reply)); + + reply = (struct wpa_eapol_key *) (hdr + 1); + reply->type = wpa_s->proto == WPA_PROTO_RSN ? + EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA; + key_info = WPA_KEY_INFO_REQUEST | ver; + if (wpa_s->ptk_set) + key_info |= WPA_KEY_INFO_MIC; + if (error) + key_info |= WPA_KEY_INFO_ERROR; + if (pairwise) + key_info |= WPA_KEY_INFO_KEY_TYPE; + reply->key_info = host_to_be16(key_info); + reply->key_length = 0; + memcpy(reply->replay_counter, wpa_s->request_counter, + WPA_REPLAY_COUNTER_LEN); + inc_byte_array(wpa_s->request_counter, WPA_REPLAY_COUNTER_LEN); + + reply->key_data_length = host_to_be16(0); + + if (key_info & WPA_KEY_INFO_MIC) { + wpa_eapol_key_mic(wpa_s->ptk.mic_key, ver, (u8 *) hdr, + rlen - sizeof(*ethhdr), reply->key_mic); + } + + wpa_printf(MSG_INFO, "WPA: Sending EAPOL-Key Request (error=%d " + "pairwise=%d ptk_set=%d len=%d)", + error, pairwise, wpa_s->ptk_set, rlen); + wpa_hexdump(MSG_MSGDUMP, "WPA: TX EAPOL-Key Request", rbuf, rlen); + l2_packet_send(wpa_s->l2, rbuf, rlen); + eapol_sm_notify_tx_eapol_key(wpa_s->eapol); + free(rbuf); +} + + +static void wpa_supplicant_process_1_of_4(struct wpa_supplicant *wpa_s, + unsigned char *src_addr, + struct wpa_eapol_key *key, int ver) +{ + int rlen; + struct ieee802_1x_hdr *hdr; + struct wpa_eapol_key *reply; + unsigned char *rbuf; + struct l2_ethhdr *ethhdr; + struct wpa_ssid *ssid; + struct wpa_ptk *ptk; + u8 buf[8], wpa_ie_buf[80], *wpa_ie, *pmkid = NULL; + int wpa_ie_len; + int abort_cached = 0; + + wpa_s->wpa_state = WPA_4WAY_HANDSHAKE; + wpa_printf(MSG_DEBUG, "WPA: RX message 1 of 4-Way Handshake from " + MACSTR " (ver=%d)", MAC2STR(src_addr), ver); + + ssid = wpa_supplicant_get_ssid(wpa_s); + if (ssid == NULL) { + wpa_printf(MSG_WARNING, "WPA: No SSID info found (msg 1 of " + "4)."); + return; + } + + if (wpa_s->proto == WPA_PROTO_RSN) { + /* RSN: msg 1/4 should contain PMKID for the selected PMK */ + u8 *pos = (u8 *) (key + 1); + u8 *end = pos + be_to_host16(key->key_data_length); + wpa_hexdump(MSG_DEBUG, "RSN: msg 1/4 key data", + pos, be_to_host16(key->key_data_length)); + while (pos + 1 < end) { + if (pos + 2 + pos[1] > end) { + wpa_printf(MSG_DEBUG, "RSN: key data " + "underflow (ie=%d len=%d)", + pos[0], pos[1]); + break; + } + if (pos[0] == GENERIC_INFO_ELEM && + pos + 1 + RSN_SELECTOR_LEN < end && + pos[1] >= RSN_SELECTOR_LEN + PMKID_LEN && + memcmp(pos + 2, RSN_KEY_DATA_PMKID, + RSN_SELECTOR_LEN) == 0) { + pmkid = pos + 2 + RSN_SELECTOR_LEN; + wpa_hexdump(MSG_DEBUG, "RSN: PMKID from " + "Authenticator", pmkid, PMKID_LEN); + break; + } else if (pos[0] == GENERIC_INFO_ELEM && + pos[1] == 0) + break; + pos += 2 + pos[1]; + } + } + + if (wpa_s->assoc_wpa_ie) { + /* The driver reported a WPA IE that may be different from the + * one that the Supplicant would use. Message 2/4 has to use + * the exact copy of the WPA IE from the Association Request, + * so use the value reported by the driver. */ + wpa_ie = wpa_s->assoc_wpa_ie; + wpa_ie_len = wpa_s->assoc_wpa_ie_len; + } else { + wpa_ie = wpa_ie_buf; + wpa_ie_len = wpa_gen_wpa_ie(wpa_s, wpa_ie); + if (wpa_ie_len < 0) { + wpa_printf(MSG_WARNING, "WPA: Failed to generate " + "WPA IE (for msg 2 of 4)."); + return; + } + wpa_hexdump(MSG_DEBUG, "WPA: WPA IE for msg 2/4", + wpa_ie, wpa_ie_len); + } + + rlen = sizeof(*ethhdr) + sizeof(*hdr) + sizeof(*reply) + wpa_ie_len; + rbuf = malloc(rlen); + if (rbuf == NULL) + return; + + memset(rbuf, 0, rlen); + ethhdr = (struct l2_ethhdr *) rbuf; + memcpy(ethhdr->h_dest, src_addr, ETH_ALEN); + memcpy(ethhdr->h_source, wpa_s->own_addr, ETH_ALEN); + ethhdr->h_proto = htons(ETH_P_EAPOL); + + hdr = (struct ieee802_1x_hdr *) (ethhdr + 1); + hdr->version = wpa_s->conf->eapol_version; + hdr->type = IEEE802_1X_TYPE_EAPOL_KEY; + hdr->length = htons(sizeof(*reply) + wpa_ie_len); + + reply = (struct wpa_eapol_key *) (hdr + 1); + reply->type = wpa_s->proto == WPA_PROTO_RSN ? + EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA; + reply->key_info = host_to_be16(ver | WPA_KEY_INFO_KEY_TYPE | + WPA_KEY_INFO_MIC); + reply->key_length = key->key_length; + memcpy(reply->replay_counter, key->replay_counter, + WPA_REPLAY_COUNTER_LEN); + + reply->key_data_length = host_to_be16(wpa_ie_len); + memcpy(reply + 1, wpa_ie, wpa_ie_len); + + if (wpa_s->renew_snonce) { + if (hostapd_get_rand(wpa_s->snonce, WPA_NONCE_LEN)) { + wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to get " + "random data for SNonce"); + return; + } + wpa_s->renew_snonce = 0; + wpa_hexdump(MSG_DEBUG, "WPA: Renewed SNonce", + wpa_s->snonce, WPA_NONCE_LEN); + } + memcpy(reply->key_nonce, wpa_s->snonce, WPA_NONCE_LEN); + ptk = &wpa_s->tptk; + memcpy(wpa_s->anonce, key->key_nonce, WPA_NONCE_LEN); + if (pmkid && !wpa_s->cur_pmksa) { + /* When using drivers that generate RSN IE, wpa_supplicant may + * not have enough time to get the association information + * event before receiving this 1/4 message, so try to find a + * matching PMKSA cache entry here. */ + wpa_s->cur_pmksa = pmksa_cache_get(wpa_s, src_addr, pmkid); + if (wpa_s->cur_pmksa) { + wpa_printf(MSG_DEBUG, "RSN: found matching PMKID from " + "PMKSA cache"); + } else { + wpa_printf(MSG_DEBUG, "RSN: no matching PMKID found"); + abort_cached = 1; + } + } + + if (pmkid && wpa_s->cur_pmksa && + memcmp(pmkid, wpa_s->cur_pmksa->pmkid, PMKID_LEN) == 0) { + wpa_hexdump(MSG_DEBUG, "RSN: matched PMKID", pmkid, PMKID_LEN); + memcpy(wpa_s->pmk, wpa_s->cur_pmksa->pmk, PMK_LEN); + wpa_hexdump_key(MSG_DEBUG, "RSN: PMK from PMKSA cache", + wpa_s->pmk, PMK_LEN); + eapol_sm_notify_cached(wpa_s->eapol); + } else if (wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X && wpa_s->eapol) { + int res, pmk_len; + pmk_len = PMK_LEN; + res = eapol_sm_get_key(wpa_s->eapol, wpa_s->pmk, PMK_LEN); +#ifdef EAP_LEAP + if (res) { + res = eapol_sm_get_key(wpa_s->eapol, wpa_s->pmk, 16); + pmk_len = 16; + } +#endif /* EAP_LEAP */ + if (res == 0) { + wpa_hexdump_key(MSG_DEBUG, "WPA: PMK from EAPOL state " + "machines", wpa_s->pmk, pmk_len); + wpa_s->pmk_len = pmk_len; + pmksa_cache_add(wpa_s, wpa_s->pmk, pmk_len, src_addr, + wpa_s->own_addr); + if (!wpa_s->cur_pmksa && pmkid && + pmksa_cache_get(wpa_s, src_addr, pmkid)) { + wpa_printf(MSG_DEBUG, "RSN: the new PMK " + "matches with the PMKID"); + abort_cached = 0; + } + } else { + wpa_msg(wpa_s, MSG_WARNING, + "WPA: Failed to get master session key from " + "EAPOL state machines"); + wpa_msg(wpa_s, MSG_WARNING, + "WPA: Key handshake aborted"); + if (wpa_s->cur_pmksa) { + wpa_printf(MSG_DEBUG, "RSN: Cancelled PMKSA " + "caching attempt"); + wpa_s->cur_pmksa = NULL; + abort_cached = 1; + } else { + return; + } + } +#ifdef CONFIG_XSUPPLICANT_IFACE + } else if (wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X && + !wpa_s->ext_pmk_received) { + wpa_printf(MSG_INFO, "WPA: Master session has not yet " + "been received from the external IEEE " + "802.1X Supplicant - ignoring WPA " + "EAPOL-Key frame"); + return; +#endif /* CONFIG_XSUPPLICANT_IFACE */ + } + + if (abort_cached && wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X) { + /* Send EAPOL-Start to trigger full EAP authentication. */ + wpa_printf(MSG_DEBUG, "RSN: no PMKSA entry found - trigger " + "full EAP authenication"); + wpa_eapol_send(wpa_s, IEEE802_1X_TYPE_EAPOL_START, + (u8 *) "", 0); + return; + } + + wpa_pmk_to_ptk(wpa_s->pmk, wpa_s->pmk_len, wpa_s->own_addr, src_addr, + wpa_s->snonce, key->key_nonce, + (u8 *) ptk, sizeof(*ptk)); + /* Supplicant: swap tx/rx Mic keys */ + memcpy(buf, ptk->u.auth.tx_mic_key, 8); + memcpy(ptk->u.auth.tx_mic_key, ptk->u.auth.rx_mic_key, 8); + memcpy(ptk->u.auth.rx_mic_key, buf, 8); + wpa_s->tptk_set = 1; + wpa_eapol_key_mic(wpa_s->tptk.mic_key, ver, (u8 *) hdr, + rlen - sizeof(*ethhdr), reply->key_mic); + wpa_hexdump(MSG_DEBUG, "WPA: EAPOL-Key MIC", reply->key_mic, 16); + + wpa_printf(MSG_DEBUG, "WPA: Sending EAPOL-Key 2/4"); + wpa_hexdump(MSG_MSGDUMP, "WPA: TX EAPOL-Key 2/4", rbuf, rlen); + l2_packet_send(wpa_s->l2, rbuf, rlen); + eapol_sm_notify_tx_eapol_key(wpa_s->eapol); + free(rbuf); +} + + +static void wpa_supplicant_key_neg_complete(struct wpa_supplicant *wpa_s, + u8 *addr, int secure) +{ + wpa_msg(wpa_s, MSG_INFO, "WPA: Key negotiation completed with " + MACSTR " [PTK=%s GTK=%s]", MAC2STR(addr), + wpa_cipher_txt(wpa_s->pairwise_cipher), + wpa_cipher_txt(wpa_s->group_cipher)); + eloop_cancel_timeout(wpa_supplicant_scan, wpa_s, NULL); + wpa_supplicant_cancel_auth_timeout(wpa_s); + wpa_s->wpa_state = WPA_COMPLETED; + + if (secure) { + /* MLME.SETPROTECTION.request(TA, Tx_Rx) */ + eapol_sm_notify_portValid(wpa_s->eapol, TRUE); + if (wpa_s->key_mgmt == WPA_KEY_MGMT_PSK) + eapol_sm_notify_eap_success(wpa_s->eapol, TRUE); + rsn_preauth_candidate_process(wpa_s); + } +} + + +static int wpa_supplicant_install_ptk(struct wpa_supplicant *wpa_s, + unsigned char *src_addr, + struct wpa_eapol_key *key) +{ + int alg, keylen, rsclen; + u8 *key_rsc; + u8 null_rsc[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + + wpa_printf(MSG_DEBUG, "WPA: Installing PTK to the driver."); + + switch (wpa_s->pairwise_cipher) { + case WPA_CIPHER_CCMP: + alg = WPA_ALG_CCMP; + keylen = 16; + rsclen = 6; + break; + case WPA_CIPHER_TKIP: + alg = WPA_ALG_TKIP; + keylen = 32; + rsclen = 6; + break; + case WPA_CIPHER_NONE: + wpa_printf(MSG_DEBUG, "WPA: Pairwise Cipher Suite: " + "NONE - do not use pairwise keys"); + return 0; + default: + wpa_printf(MSG_WARNING, "WPA: Unsupported pairwise cipher %d", + wpa_s->pairwise_cipher); + return -1; + } + + if (wpa_s->proto == WPA_PROTO_RSN) { + key_rsc = null_rsc; + } else { + key_rsc = key->key_rsc; + wpa_hexdump(MSG_DEBUG, "WPA: RSC", key_rsc, rsclen); + } + + wpa_s->keys_cleared = 0; + if (wpa_drv_set_key(wpa_s, alg, src_addr, 0, 1, key_rsc, rsclen, + (u8 *) &wpa_s->ptk.tk1, keylen) < 0) { + wpa_printf(MSG_WARNING, "WPA: Failed to set PTK to the " + "driver."); + return -1; + } + return 0; +} + + +static int wpa_supplicant_check_group_cipher(struct wpa_supplicant *wpa_s, + int keylen, int maxkeylen, + int *key_rsc_len, int *alg) +{ + switch (wpa_s->group_cipher) { + case WPA_CIPHER_CCMP: + if (keylen != 16 || maxkeylen < 16) { + wpa_printf(MSG_WARNING, "WPA: Unsupported CCMP Group " + "Cipher key length %d (%d).", + keylen, maxkeylen); + return -1; + } + *key_rsc_len = 6; + *alg = WPA_ALG_CCMP; + break; + case WPA_CIPHER_TKIP: + if (keylen != 32 || maxkeylen < 32) { + wpa_printf(MSG_WARNING, "WPA: Unsupported TKIP Group " + "Cipher key length %d (%d).", + keylen, maxkeylen); + return -1; + } + *key_rsc_len = 6; + *alg = WPA_ALG_TKIP; + break; + case WPA_CIPHER_WEP104: + if (keylen != 13 || maxkeylen < 13) { + wpa_printf(MSG_WARNING, "WPA: Unsupported WEP104 Group" + " Cipher key length %d (%d).", + keylen, maxkeylen); + return -1; + } + *key_rsc_len = 0; + *alg = WPA_ALG_WEP; + break; + case WPA_CIPHER_WEP40: + if (keylen != 5 || maxkeylen < 5) { + wpa_printf(MSG_WARNING, "WPA: Unsupported WEP40 Group " + "Cipher key length %d (%d).", + keylen, maxkeylen); + return -1; + } + *key_rsc_len = 0; + *alg = WPA_ALG_WEP; + break; + default: + wpa_printf(MSG_WARNING, "WPA: Unsupport Group Cipher %d", + wpa_s->group_cipher); + return -1; + } + + return 0; +} + + +static int wpa_supplicant_install_gtk(struct wpa_supplicant *wpa_s, + struct wpa_eapol_key *key, int alg, + u8 *gtk, int gtk_len, int keyidx, + int key_rsc_len, int tx) +{ + wpa_hexdump_key(MSG_DEBUG, "WPA: Group Key", gtk, gtk_len); + wpa_printf(MSG_DEBUG, "WPA: Installing GTK to the driver " + "(keyidx=%d tx=%d).", keyidx, tx); + wpa_hexdump(MSG_DEBUG, "WPA: RSC", key->key_rsc, key_rsc_len); + if (wpa_s->group_cipher == WPA_CIPHER_TKIP) { + /* Swap Tx/Rx keys for Michael MIC */ + u8 tmpbuf[8]; + memcpy(tmpbuf, gtk + 16, 8); + memcpy(gtk + 16, gtk + 24, 8); + memcpy(gtk + 24, tmpbuf, 8); + } + wpa_s->keys_cleared = 0; + if (wpa_s->pairwise_cipher == WPA_CIPHER_NONE) { + if (wpa_drv_set_key(wpa_s, alg, + (u8 *) "\xff\xff\xff\xff\xff\xff", + keyidx, 1, key->key_rsc, key_rsc_len, + gtk, gtk_len) < 0) { + wpa_printf(MSG_WARNING, "WPA: Failed to set " + "GTK to the driver (Group only)."); + return -1; + } + } else if (wpa_drv_set_key(wpa_s, alg, + (u8 *) "\xff\xff\xff\xff\xff\xff", + keyidx, tx, key->key_rsc, key_rsc_len, + gtk, gtk_len) < 0) { + wpa_printf(MSG_WARNING, "WPA: Failed to set GTK to " + "the driver."); + return -1; + } + + return 0; +} + + +static int wpa_supplicant_pairwise_gtk(struct wpa_supplicant *wpa_s, + unsigned char *src_addr, + struct wpa_eapol_key *key, + u8 *gtk, int gtk_len, int key_info) +{ + int keyidx, tx, key_rsc_len = 0, alg; + + wpa_hexdump_key(MSG_DEBUG, "RSN: received GTK in pairwise handshake", + gtk, gtk_len); + + keyidx = gtk[0] & 0x3; + tx = !!(gtk[0] & BIT(2)); + if (tx && wpa_s->pairwise_cipher != WPA_CIPHER_NONE) { + /* Ignore Tx bit in GTK IE if a pairwise key is used. One AP + * seemed to set this bit (incorrectly, since Tx is only when + * doing Group Key only APs) and without this workaround, the + * data connection does not work because wpa_supplicant + * configured non-zero keyidx to be used for unicast. */ + wpa_printf(MSG_INFO, "RSN: Tx bit set for GTK IE, but " + "pairwise keys are used - ignore Tx bit"); + tx = 0; + } + gtk += 2; + gtk_len -= 2; + + if (wpa_supplicant_check_group_cipher(wpa_s, gtk_len, gtk_len, + &key_rsc_len, &alg)) { + return -1; + } + + if (wpa_supplicant_install_gtk(wpa_s, key, alg, gtk, gtk_len, keyidx, + key_rsc_len, tx)) { + return -1; + } + + wpa_supplicant_key_neg_complete(wpa_s, src_addr, + key_info & WPA_KEY_INFO_SECURE); + return 0; +} + + +static void wpa_report_ie_mismatch(struct wpa_supplicant *wpa_s, + const char *reason, const u8 *src_addr, + const u8 *wpa_ie, size_t wpa_ie_len, + const u8 *rsn_ie, size_t rsn_ie_len) +{ + wpa_msg(wpa_s, MSG_WARNING, "WPA: %s (src=" MACSTR ")", + reason, MAC2STR(src_addr)); + + if (wpa_s->ap_wpa_ie) { + wpa_hexdump(MSG_INFO, "WPA: WPA IE in Beacon/ProbeResp", + wpa_s->ap_wpa_ie, wpa_s->ap_wpa_ie_len); + } + if (wpa_ie) { + if (!wpa_s->ap_wpa_ie) { + wpa_printf(MSG_INFO, "WPA: No WPA IE in " + "Beacon/ProbeResp"); + } + wpa_hexdump(MSG_INFO, "WPA: WPA IE in 3/4 msg", + wpa_ie, wpa_ie_len); + } + + if (wpa_s->ap_rsn_ie) { + wpa_hexdump(MSG_INFO, "WPA: RSN IE in Beacon/ProbeResp", + wpa_s->ap_rsn_ie, wpa_s->ap_rsn_ie_len); + } + if (rsn_ie) { + if (!wpa_s->ap_rsn_ie) { + wpa_printf(MSG_INFO, "WPA: No RSN IE in " + "Beacon/ProbeResp"); + } + wpa_hexdump(MSG_INFO, "WPA: RSN IE in 3/4 msg", + rsn_ie, rsn_ie_len); + } + + wpa_supplicant_disassociate(wpa_s, REASON_IE_IN_4WAY_DIFFERS); + wpa_supplicant_req_scan(wpa_s, 0, 0); +} + + +static void wpa_supplicant_process_3_of_4(struct wpa_supplicant *wpa_s, + unsigned char *src_addr, + struct wpa_eapol_key *key, + int extra_len, int ver) +{ + int rlen; + struct ieee802_1x_hdr *hdr; + struct wpa_eapol_key *reply; + unsigned char *rbuf; + struct l2_ethhdr *ethhdr; + int key_info, wpa_ie_len = 0, rsn_ie_len = 0, keylen, gtk_len = 0; + u8 *wpa_ie = NULL, *rsn_ie = NULL, *gtk = NULL; + u8 *pos, *end; + u16 len; + struct wpa_ssid *ssid = wpa_s->current_ssid; + + wpa_s->wpa_state = WPA_4WAY_HANDSHAKE; + wpa_printf(MSG_DEBUG, "WPA: RX message 3 of 4-Way Handshake from " + MACSTR " (ver=%d)", MAC2STR(src_addr), ver); + + key_info = be_to_host16(key->key_info); + + pos = (u8 *) (key + 1); + len = be_to_host16(key->key_data_length); + end = pos + len; + wpa_hexdump(MSG_DEBUG, "WPA: IE KeyData", pos, len); + while (pos + 1 < end) { + if (pos + 2 + pos[1] > end) { + wpa_printf(MSG_DEBUG, "WPA: key data underflow (ie=%d " + "len=%d)", pos[0], pos[1]); + break; + } + if (*pos == RSN_INFO_ELEM) { + rsn_ie = pos; + rsn_ie_len = pos[1] + 2; + } else if (*pos == GENERIC_INFO_ELEM && pos[1] >= 6 && + memcmp(pos + 2, WPA_OUI_TYPE, WPA_SELECTOR_LEN) == 0 + && pos[2 + WPA_SELECTOR_LEN] == 1 && + pos[2 + WPA_SELECTOR_LEN + 1] == 0) { + wpa_ie = pos; + wpa_ie_len = pos[1] + 2; + } else if (pos[0] == GENERIC_INFO_ELEM && + pos[1] > RSN_SELECTOR_LEN + 2 && + memcmp(pos + 2, RSN_KEY_DATA_GROUPKEY, + RSN_SELECTOR_LEN) == 0) { + if (!(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) { + wpa_printf(MSG_WARNING, "WPA: GTK IE in " + "unencrypted key data"); + return; + } + gtk = pos + 2 + RSN_SELECTOR_LEN; + gtk_len = pos[1] - RSN_SELECTOR_LEN; + } else if (pos[0] == GENERIC_INFO_ELEM && pos[1] == 0) + break; + + pos += 2 + pos[1]; + } + + if (wpa_s->ap_wpa_ie == NULL && wpa_s->ap_rsn_ie == NULL) { + wpa_printf(MSG_DEBUG, "WPA: No WPA/RSN IE for this AP known. " + "Trying to get from scan results"); + if (wpa_supplicant_get_beacon_ie(wpa_s) < 0) { + wpa_printf(MSG_WARNING, "WPA: Could not find AP from " + "the scan results"); + } else { + wpa_printf(MSG_DEBUG, "WPA: Found the current AP from " + "updated scan results"); + } + } + + if ((wpa_ie && wpa_s->ap_wpa_ie && + (wpa_ie_len != wpa_s->ap_wpa_ie_len || + memcmp(wpa_ie, wpa_s->ap_wpa_ie, wpa_ie_len) != 0)) || + (rsn_ie && wpa_s->ap_rsn_ie && + (rsn_ie_len != wpa_s->ap_rsn_ie_len || + memcmp(rsn_ie, wpa_s->ap_rsn_ie, rsn_ie_len) != 0))) { + wpa_report_ie_mismatch(wpa_s, "IE in 3/4 msg does not match " + "with IE in Beacon/ProbeResp", + src_addr, wpa_ie, wpa_ie_len, + rsn_ie, rsn_ie_len); + return; + } + + if (wpa_s->proto == WPA_PROTO_WPA && + rsn_ie && wpa_s->ap_rsn_ie == NULL && + ssid && (ssid->proto & WPA_PROTO_RSN)) { + wpa_report_ie_mismatch(wpa_s, "Possible downgrade attack " + "detected - RSN was enabled and RSN IE " + "was in msg 3/4, but not in " + "Beacon/ProbeResp", + src_addr, wpa_ie, wpa_ie_len, + rsn_ie, rsn_ie_len); + return; + } + + if (memcmp(wpa_s->anonce, key->key_nonce, WPA_NONCE_LEN) != 0) { + wpa_printf(MSG_WARNING, "WPA: ANonce from message 1 of 4-Way " + "Handshake differs from 3 of 4-Way Handshake - drop" + " packet (src=" MACSTR ")", MAC2STR(src_addr)); + return; + } + + keylen = be_to_host16(key->key_length); + switch (wpa_s->pairwise_cipher) { + case WPA_CIPHER_CCMP: + if (keylen != 16) { + wpa_printf(MSG_WARNING, "WPA: Invalid CCMP key length " + "%d (src=" MACSTR ")", + keylen, MAC2STR(src_addr)); + return; + } + break; + case WPA_CIPHER_TKIP: + if (keylen != 32) { + wpa_printf(MSG_WARNING, "WPA: Invalid TKIP key length " + "%d (src=" MACSTR ")", + keylen, MAC2STR(src_addr)); + return; + } + break; + } + + rlen = sizeof(*ethhdr) + sizeof(*hdr) + sizeof(*reply); + rbuf = malloc(rlen); + if (rbuf == NULL) + return; + + memset(rbuf, 0, rlen); + ethhdr = (struct l2_ethhdr *) rbuf; + memcpy(ethhdr->h_dest, src_addr, ETH_ALEN); + memcpy(ethhdr->h_source, wpa_s->own_addr, ETH_ALEN); + ethhdr->h_proto = htons(ETH_P_EAPOL); + + hdr = (struct ieee802_1x_hdr *) (ethhdr + 1); + hdr->version = wpa_s->conf->eapol_version; + hdr->type = IEEE802_1X_TYPE_EAPOL_KEY; + hdr->length = htons(sizeof(*reply)); + + reply = (struct wpa_eapol_key *) (hdr + 1); + reply->type = wpa_s->proto == WPA_PROTO_RSN ? + EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA; + reply->key_info = host_to_be16(ver | WPA_KEY_INFO_KEY_TYPE | + WPA_KEY_INFO_MIC | + (key_info & WPA_KEY_INFO_SECURE)); + reply->key_length = key->key_length; + memcpy(reply->replay_counter, key->replay_counter, + WPA_REPLAY_COUNTER_LEN); + + reply->key_data_length = host_to_be16(0); + + memcpy(reply->key_nonce, wpa_s->snonce, WPA_NONCE_LEN); + wpa_eapol_key_mic(wpa_s->ptk.mic_key, ver, (u8 *) hdr, + rlen - sizeof(*ethhdr), reply->key_mic); + + wpa_printf(MSG_DEBUG, "WPA: Sending EAPOL-Key 4/4"); + wpa_hexdump(MSG_MSGDUMP, "WPA: TX EAPOL-Key 4/4", rbuf, rlen); + l2_packet_send(wpa_s->l2, rbuf, rlen); + eapol_sm_notify_tx_eapol_key(wpa_s->eapol); + free(rbuf); + + /* SNonce was successfully used in msg 3/4, so mark it to be renewed + * for the next 4-Way Handshake. If msg 3 is received again, the old + * SNonce will still be used to avoid changing PTK. */ + wpa_s->renew_snonce = 1; + + if (key_info & WPA_KEY_INFO_INSTALL) { + wpa_supplicant_install_ptk(wpa_s, src_addr, key); + } + + if (key_info & WPA_KEY_INFO_SECURE) { + /* MLME.SETPROTECTION.request(TA, Tx_Rx) */ + eapol_sm_notify_portValid(wpa_s->eapol, TRUE); + } + wpa_s->wpa_state = WPA_GROUP_HANDSHAKE; + + if (gtk) { + wpa_supplicant_pairwise_gtk(wpa_s, src_addr, key, + gtk, gtk_len, key_info); + } +} + + +static void wpa_supplicant_process_1_of_2(struct wpa_supplicant *wpa_s, + unsigned char *src_addr, + struct wpa_eapol_key *key, + int extra_len, int ver) +{ + int rlen; + struct ieee802_1x_hdr *hdr; + struct wpa_eapol_key *reply; + unsigned char *rbuf; + struct l2_ethhdr *ethhdr; + int key_info, keylen, keydatalen, maxkeylen, keyidx, key_rsc_len = 0; + int alg, tx, rekey; + u8 ek[32], gtk[32]; + u8 *gtk_ie = NULL; + size_t gtk_ie_len = 0; + + rekey = wpa_s->wpa_state == WPA_COMPLETED; + wpa_s->wpa_state = WPA_GROUP_HANDSHAKE; + wpa_printf(MSG_DEBUG, "WPA: RX message 1 of Group Key Handshake from " + MACSTR " (ver=%d)", MAC2STR(src_addr), ver); + + key_info = be_to_host16(key->key_info); + keydatalen = be_to_host16(key->key_data_length); + + if (wpa_s->proto == WPA_PROTO_RSN) { + u8 *pos = (u8 *) (key + 1); + u8 *end = pos + keydatalen; + while (pos + 1 < end) { + if (pos + 2 + pos[1] > end) { + wpa_printf(MSG_DEBUG, "RSN: key data " + "underflow (ie=%d len=%d)", + pos[0], pos[1]); + break; + } + if (pos[0] == GENERIC_INFO_ELEM && + pos + 1 + RSN_SELECTOR_LEN < end && + pos[1] > RSN_SELECTOR_LEN + 2 && + memcmp(pos + 2, RSN_KEY_DATA_GROUPKEY, + RSN_SELECTOR_LEN) == 0) { + if (!(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) { + wpa_printf(MSG_WARNING, "WPA: GTK IE " + "in unencrypted key data"); + return; + } + gtk_ie = pos + 2 + RSN_SELECTOR_LEN; + gtk_ie_len = pos[1] - RSN_SELECTOR_LEN; + break; + } else if (pos[0] == GENERIC_INFO_ELEM && + pos[1] == 0) + break; + + pos += 2 + pos[1]; + } + + if (gtk_ie == NULL) { + wpa_printf(MSG_INFO, "WPA: No GTK IE in Group Key " + "message 1/2"); + return; + } + maxkeylen = keylen = gtk_ie_len - 2; + } else { + keylen = be_to_host16(key->key_length); + maxkeylen = keydatalen; + if (keydatalen > extra_len) { + wpa_printf(MSG_INFO, "WPA: Truncated EAPOL-Key packet:" + " key_data_length=%d > extra_len=%d", + keydatalen, extra_len); + return; + } + if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) + maxkeylen -= 8; + } + + if (wpa_supplicant_check_group_cipher(wpa_s, keylen, maxkeylen, + &key_rsc_len, &alg)) { + return; + } + + if (wpa_s->proto == WPA_PROTO_RSN) { + wpa_hexdump(MSG_DEBUG, + "RSN: received GTK in group key handshake", + gtk_ie, gtk_ie_len); + keyidx = gtk_ie[0] & 0x3; + tx = !!(gtk_ie[0] & BIT(2)); + if (gtk_ie_len - 2 > sizeof(gtk)) { + wpa_printf(MSG_INFO, "RSN: Too long GTK in GTK IE " + "(len=%lu)", + (unsigned long) gtk_ie_len - 2); + return; + } + memcpy(gtk, gtk_ie + 2, gtk_ie_len - 2); + } else { + keyidx = (key_info & WPA_KEY_INFO_KEY_INDEX_MASK) >> + WPA_KEY_INFO_KEY_INDEX_SHIFT; + if (ver == WPA_KEY_INFO_TYPE_HMAC_MD5_RC4) { + memcpy(ek, key->key_iv, 16); + memcpy(ek + 16, wpa_s->ptk.encr_key, 16); + rc4_skip(ek, 32, 256, (u8 *) (key + 1), keydatalen); + memcpy(gtk, key + 1, keylen); + } else if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) { + if (keydatalen % 8) { + wpa_printf(MSG_WARNING, "WPA: Unsupported " + "AES-WRAP len %d", keydatalen); + return; + } + if (aes_unwrap(wpa_s->ptk.encr_key, maxkeylen / 8, + (u8 *) (key + 1), gtk)) { + wpa_printf(MSG_WARNING, "WPA: AES unwrap " + "failed - could not decrypt GTK"); + return; + } + } + tx = !!(key_info & WPA_KEY_INFO_TXRX); + } + + if (tx && wpa_s->pairwise_cipher != WPA_CIPHER_NONE) { + /* Ignore Tx bit in Group Key message if a pairwise key + * is used. Some APs seem to setting this bit + * (incorrectly, since Tx is only when doing Group Key + * only APs) and without this workaround, the data + * connection does not work because wpa_supplicant + * configured non-zero keyidx to be used for unicast. + */ + wpa_printf(MSG_INFO, "WPA: Tx bit set for GTK, but " + "pairwise keys are used - ignore Tx bit"); + tx = 0; + } + + wpa_supplicant_install_gtk(wpa_s, key, alg, gtk, keylen, keyidx, + key_rsc_len, tx); + + rlen = sizeof(*ethhdr) + sizeof(*hdr) + sizeof(*reply); + rbuf = malloc(rlen); + if (rbuf == NULL) + return; + + memset(rbuf, 0, rlen); + ethhdr = (struct l2_ethhdr *) rbuf; + memcpy(ethhdr->h_dest, src_addr, ETH_ALEN); + memcpy(ethhdr->h_source, wpa_s->own_addr, ETH_ALEN); + ethhdr->h_proto = htons(ETH_P_EAPOL); + + hdr = (struct ieee802_1x_hdr *) (ethhdr + 1); + hdr->version = wpa_s->conf->eapol_version; + hdr->type = IEEE802_1X_TYPE_EAPOL_KEY; + hdr->length = htons(sizeof(*reply)); + + reply = (struct wpa_eapol_key *) (hdr + 1); + reply->type = wpa_s->proto == WPA_PROTO_RSN ? + EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA; + reply->key_info = + host_to_be16(ver | WPA_KEY_INFO_MIC | WPA_KEY_INFO_SECURE | + (key_info & WPA_KEY_INFO_KEY_INDEX_MASK)); + reply->key_length = key->key_length; + memcpy(reply->replay_counter, key->replay_counter, + WPA_REPLAY_COUNTER_LEN); + + reply->key_data_length = host_to_be16(0); + + wpa_eapol_key_mic(wpa_s->ptk.mic_key, ver, (u8 *) hdr, + rlen - sizeof(*ethhdr), reply->key_mic); + + wpa_printf(MSG_DEBUG, "WPA: Sending EAPOL-Key 2/2"); + wpa_hexdump(MSG_MSGDUMP, "WPA: TX EAPOL-Key 2/2", rbuf, rlen); + l2_packet_send(wpa_s->l2, rbuf, rlen); + eapol_sm_notify_tx_eapol_key(wpa_s->eapol); + free(rbuf); + + if (rekey) { + wpa_msg(wpa_s, MSG_INFO, "WPA: Group rekeying completed with " + MACSTR " [GTK=%s]", MAC2STR(src_addr), + wpa_cipher_txt(wpa_s->group_cipher)); + wpa_s->wpa_state = WPA_COMPLETED; + } else { + wpa_supplicant_key_neg_complete(wpa_s, src_addr, + key_info & + WPA_KEY_INFO_SECURE); + } +} + + +static int wpa_supplicant_verify_eapol_key_mic(struct wpa_supplicant *wpa_s, + struct wpa_eapol_key *key, + int ver, u8 *buf, size_t len) +{ + u8 mic[16]; + int ok = 0; + + memcpy(mic, key->key_mic, 16); + if (wpa_s->tptk_set) { + memset(key->key_mic, 0, 16); + wpa_eapol_key_mic(wpa_s->tptk.mic_key, ver, buf, len, + key->key_mic); + if (memcmp(mic, key->key_mic, 16) != 0) { + wpa_printf(MSG_WARNING, "WPA: Invalid EAPOL-Key MIC " + "when using TPTK - ignoring TPTK"); + } else { + ok = 1; + wpa_s->tptk_set = 0; + wpa_s->ptk_set = 1; + memcpy(&wpa_s->ptk, &wpa_s->tptk, sizeof(wpa_s->ptk)); + } + } + + if (!ok && wpa_s->ptk_set) { + memset(key->key_mic, 0, 16); + wpa_eapol_key_mic(wpa_s->ptk.mic_key, ver, buf, len, + key->key_mic); + if (memcmp(mic, key->key_mic, 16) != 0) { + wpa_printf(MSG_WARNING, "WPA: Invalid EAPOL-Key MIC " + "- dropping packet"); + return -1; + } + ok = 1; + } + + if (!ok) { + wpa_printf(MSG_WARNING, "WPA: Could not verify EAPOL-Key MIC " + "- dropping packet"); + return -1; + } + + memcpy(wpa_s->rx_replay_counter, key->replay_counter, + WPA_REPLAY_COUNTER_LEN); + wpa_s->rx_replay_counter_set = 1; + return 0; +} + + +/* Decrypt RSN EAPOL-Key key data (RC4 or AES-WRAP) */ +static int wpa_supplicant_decrypt_key_data(struct wpa_supplicant *wpa_s, + struct wpa_eapol_key *key, int ver) +{ + int keydatalen = be_to_host16(key->key_data_length); + + wpa_hexdump(MSG_DEBUG, "RSN: encrypted key data", + (u8 *) (key + 1), keydatalen); + if (!wpa_s->ptk_set) { + wpa_printf(MSG_WARNING, "WPA: PTK not available, " + "cannot decrypt EAPOL-Key key data."); + return -1; + } + + /* Decrypt key data here so that this operation does not need + * to be implemented separately for each message type. */ + if (ver == WPA_KEY_INFO_TYPE_HMAC_MD5_RC4) { + u8 ek[32]; + memcpy(ek, key->key_iv, 16); + memcpy(ek + 16, wpa_s->ptk.encr_key, 16); + rc4_skip(ek, 32, 256, (u8 *) (key + 1), keydatalen); + } else if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) { + u8 *buf; + if (keydatalen % 8) { + wpa_printf(MSG_WARNING, "WPA: Unsupported " + "AES-WRAP len %d", keydatalen); + return -1; + } + keydatalen -= 8; /* AES-WRAP adds 8 bytes */ + buf = malloc(keydatalen); + if (buf == NULL) { + wpa_printf(MSG_WARNING, "WPA: No memory for " + "AES-UNWRAP buffer"); + return -1; + } + if (aes_unwrap(wpa_s->ptk.encr_key, keydatalen / 8, + (u8 *) (key + 1), buf)) { + free(buf); + wpa_printf(MSG_WARNING, "WPA: AES unwrap failed - " + "could not decrypt EAPOL-Key key data"); + return -1; + } + memcpy(key + 1, buf, keydatalen); + free(buf); + key->key_data_length = host_to_be16(keydatalen); + } + wpa_hexdump_key(MSG_DEBUG, "WPA: decrypted EAPOL-Key key data", + (u8 *) (key + 1), keydatalen); + return 0; +} + + +static void wpa_sm_rx_eapol(struct wpa_supplicant *wpa_s, + unsigned char *src_addr, unsigned char *buf, + size_t len) +{ + size_t plen, data_len, extra_len; + struct ieee802_1x_hdr *hdr; + struct wpa_eapol_key *key; + int key_info, ver; + + hdr = (struct ieee802_1x_hdr *) buf; + key = (struct wpa_eapol_key *) (hdr + 1); + if (len < sizeof(*hdr) + sizeof(*key)) { + wpa_printf(MSG_DEBUG, "WPA: EAPOL frame too short, len %lu, " + "expecting at least %lu", + (unsigned long) len, + (unsigned long) sizeof(*hdr) + sizeof(*key)); + return; + } + plen = ntohs(hdr->length); + data_len = plen + sizeof(*hdr); + wpa_printf(MSG_DEBUG, "IEEE 802.1X RX: version=%d type=%d length=%lu", + hdr->version, hdr->type, (unsigned long) plen); + + wpa_drv_poll(wpa_s); + + if (hdr->version < EAPOL_VERSION) { + /* TODO: backwards compatibility */ + } + if (hdr->type != IEEE802_1X_TYPE_EAPOL_KEY) { + wpa_printf(MSG_DEBUG, "WPA: EAPOL frame (type %u) discarded, " + "not a Key frame", hdr->type); + if (wpa_s->cur_pmksa) { + wpa_printf(MSG_DEBUG, "WPA: Cancelling PMKSA caching " + "attempt - attempt full EAP " + "authentication"); + eapol_sm_notify_pmkid_attempt(wpa_s->eapol, 0); + } + return; + } + if (plen > len - sizeof(*hdr) || plen < sizeof(*key)) { + wpa_printf(MSG_DEBUG, "WPA: EAPOL frame payload size %lu " + "invalid (frame size %lu)", + (unsigned long) plen, (unsigned long) len); + return; + } + + wpa_printf(MSG_DEBUG, " EAPOL-Key type=%d", key->type); + if (key->type != EAPOL_KEY_TYPE_WPA && key->type != EAPOL_KEY_TYPE_RSN) + { + wpa_printf(MSG_DEBUG, "WPA: EAPOL-Key type (%d) unknown, " + "discarded", key->type); + return; + } + + wpa_hexdump(MSG_MSGDUMP, "WPA: RX EAPOL-Key", buf, len); + if (data_len < len) { + wpa_printf(MSG_DEBUG, "WPA: ignoring %lu bytes after the IEEE " + "802.1X data", (unsigned long) len - data_len); + } + key_info = be_to_host16(key->key_info); + ver = key_info & WPA_KEY_INFO_TYPE_MASK; + if (ver != WPA_KEY_INFO_TYPE_HMAC_MD5_RC4 && + ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) { + wpa_printf(MSG_INFO, "WPA: Unsupported EAPOL-Key descriptor " + "version %d.", ver); + return; + } + + if (wpa_s->pairwise_cipher == WPA_CIPHER_CCMP && + ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) { + wpa_printf(MSG_INFO, "WPA: CCMP is used, but EAPOL-Key " + "descriptor version (%d) is not 2.", ver); + if (wpa_s->group_cipher != WPA_CIPHER_CCMP && + !(key_info & WPA_KEY_INFO_KEY_TYPE)) { + /* Earlier versions of IEEE 802.11i did not explicitly + * require version 2 descriptor for all EAPOL-Key + * packets, so allow group keys to use version 1 if + * CCMP is not used for them. */ + wpa_printf(MSG_INFO, "WPA: Backwards compatibility: " + "allow invalid version for non-CCMP group " + "keys"); + } else + return; + } + + if (wpa_s->rx_replay_counter_set && + memcmp(key->replay_counter, wpa_s->rx_replay_counter, + WPA_REPLAY_COUNTER_LEN) <= 0) { + wpa_printf(MSG_WARNING, "WPA: EAPOL-Key Replay Counter did not" + " increase - dropping packet"); + return; + } + + if (!(key_info & WPA_KEY_INFO_ACK)) { + wpa_printf(MSG_INFO, "WPA: No Ack bit in key_info"); + return; + } + + if (key_info & WPA_KEY_INFO_REQUEST) { + wpa_printf(MSG_INFO, "WPA: EAPOL-Key with Request bit - " + "dropped"); + return; + } + + if ((key_info & WPA_KEY_INFO_MIC) && + wpa_supplicant_verify_eapol_key_mic(wpa_s, key, ver, buf, + data_len)) + return; + + extra_len = data_len - sizeof(*hdr) - sizeof(*key); + + if (be_to_host16(key->key_data_length) > extra_len) { + wpa_msg(wpa_s, MSG_INFO, "WPA: Invalid EAPOL-Key frame - " + "key_data overflow (%d > %d)", + be_to_host16(key->key_data_length), extra_len); + return; + } + + if (wpa_s->proto == WPA_PROTO_RSN && + (key_info & WPA_KEY_INFO_ENCR_KEY_DATA) && + wpa_supplicant_decrypt_key_data(wpa_s, key, ver)) + return; + + if (key_info & WPA_KEY_INFO_KEY_TYPE) { + if (key_info & WPA_KEY_INFO_KEY_INDEX_MASK) { + wpa_printf(MSG_WARNING, "WPA: Ignored EAPOL-Key " + "(Pairwise) with non-zero key index"); + return; + } + if (key_info & WPA_KEY_INFO_MIC) { + /* 3/4 4-Way Handshake */ + wpa_supplicant_process_3_of_4(wpa_s, src_addr, key, + extra_len, ver); + } else { + /* 1/4 4-Way Handshake */ + wpa_supplicant_process_1_of_4(wpa_s, src_addr, key, + ver); + } + } else { + if (key_info & WPA_KEY_INFO_MIC) { + /* 1/2 Group Key Handshake */ + wpa_supplicant_process_1_of_2(wpa_s, src_addr, key, + extra_len, ver); + } else { + wpa_printf(MSG_WARNING, "WPA: EAPOL-Key (Group) " + "without Mic bit - dropped"); + } + } +} + + +void wpa_supplicant_rx_eapol(void *ctx, unsigned char *src_addr, + unsigned char *buf, size_t len) +{ + struct wpa_supplicant *wpa_s = ctx; + + wpa_printf(MSG_DEBUG, "RX EAPOL from " MACSTR, MAC2STR(src_addr)); + wpa_hexdump(MSG_MSGDUMP, "RX EAPOL", buf, len); + + if (wpa_s->eapol_received == 0) { + /* Timeout for completing IEEE 802.1X and WPA authentication */ + wpa_supplicant_req_auth_timeout( + wpa_s, + (wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X || + wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA) ? + 70 : 10, 0); + } + wpa_s->eapol_received++; + + if (wpa_s->countermeasures) { + wpa_printf(MSG_INFO, "WPA: Countermeasures - dropped EAPOL " + "packet"); + return; + } + + /* Source address of the incoming EAPOL frame could be compared to the + * current BSSID. However, it is possible that a centralized + * Authenticator could be using another MAC address than the BSSID of + * an AP, so just allow any address to be used for now. The replies are + * still sent to the current BSSID (if available), though. */ + + memcpy(wpa_s->last_eapol_src, src_addr, ETH_ALEN); + eapol_sm_rx_eapol(wpa_s->eapol, src_addr, buf, len); + wpa_sm_rx_eapol(wpa_s, src_addr, buf, len); +} + + +static int wpa_cipher_bits(int cipher) +{ + switch (cipher) { + case WPA_CIPHER_CCMP: + return 128; + case WPA_CIPHER_TKIP: + return 256; + case WPA_CIPHER_WEP104: + return 104; + case WPA_CIPHER_WEP40: + return 40; + default: + return 0; + } +} + + +static const u8 * wpa_key_mgmt_suite(struct wpa_supplicant *wpa_s) +{ + static const u8 *dummy = (u8 *) "\x00\x00\x00\x00"; + switch (wpa_s->key_mgmt) { + case WPA_KEY_MGMT_IEEE8021X: + return (wpa_s->proto == WPA_PROTO_RSN ? + RSN_AUTH_KEY_MGMT_UNSPEC_802_1X : + WPA_AUTH_KEY_MGMT_UNSPEC_802_1X); + case WPA_KEY_MGMT_PSK: + return (wpa_s->proto == WPA_PROTO_RSN ? + RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X : + WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X); + case WPA_KEY_MGMT_WPA_NONE: + return WPA_AUTH_KEY_MGMT_NONE; + default: + return dummy; + } +} + + +static const u8 * wpa_cipher_suite(struct wpa_supplicant *wpa_s, int cipher) +{ + static const u8 *dummy = (u8 *) "\x00\x00\x00\x00"; + switch (cipher) { + case WPA_CIPHER_CCMP: + return (wpa_s->proto == WPA_PROTO_RSN ? + RSN_CIPHER_SUITE_CCMP : WPA_CIPHER_SUITE_CCMP); + case WPA_CIPHER_TKIP: + return (wpa_s->proto == WPA_PROTO_RSN ? + RSN_CIPHER_SUITE_TKIP : WPA_CIPHER_SUITE_TKIP); + case WPA_CIPHER_WEP104: + return (wpa_s->proto == WPA_PROTO_RSN ? + RSN_CIPHER_SUITE_WEP104 : WPA_CIPHER_SUITE_WEP104); + case WPA_CIPHER_WEP40: + return (wpa_s->proto == WPA_PROTO_RSN ? + RSN_CIPHER_SUITE_WEP40 : WPA_CIPHER_SUITE_WEP40); + case WPA_CIPHER_NONE: + return (wpa_s->proto == WPA_PROTO_RSN ? + RSN_CIPHER_SUITE_NONE : WPA_CIPHER_SUITE_NONE); + default: + return dummy; + } +} + + +#define RSN_SUITE "%02x-%02x-%02x-%d" +#define RSN_SUITE_ARG(s) (s)[0], (s)[1], (s)[2], (s)[3] + +int wpa_get_mib(struct wpa_supplicant *wpa_s, char *buf, size_t buflen) +{ + int len, i; + char pmkid_txt[PMKID_LEN * 2 + 1]; + + if (wpa_s->cur_pmksa) { + char *pos = pmkid_txt; + for (i = 0; i < PMKID_LEN; i++) { + pos += sprintf(pos, "%02x", + wpa_s->cur_pmksa->pmkid[i]); + } + } else + pmkid_txt[0] = '\0'; + + len = snprintf(buf, buflen, + "dot11RSNAConfigVersion=%d\n" + "dot11RSNAConfigPairwiseKeysSupported=5\n" + "dot11RSNAConfigGroupCipherSize=%d\n" + "dot11RSNAConfigPMKLifetime=%d\n" + "dot11RSNAConfigPMKReauthThreshold=%d\n" + "dot11RSNAConfigNumberOfPTKSAReplayCounters=1\n" + "dot11RSNAConfigSATimeout=%d\n" + "dot11RSNAAuthenticationSuiteSelected=" RSN_SUITE "\n" + "dot11RSNAPairwiseCipherSelected=" RSN_SUITE "\n" + "dot11RSNAGroupCipherSelected=" RSN_SUITE "\n" + "dot11RSNAPMKIDUsed=%s\n" + "dot11RSNAAuthenticationSuiteRequested=" RSN_SUITE "\n" + "dot11RSNAPairwiseCipherRequested=" RSN_SUITE "\n" + "dot11RSNAGroupCipherRequested=" RSN_SUITE "\n" + "dot11RSNAConfigNumberOfGTKSAReplayCounters=0\n", + RSN_VERSION, + wpa_cipher_bits(wpa_s->group_cipher), + dot11RSNAConfigPMKLifetime, + dot11RSNAConfigPMKReauthThreshold, + dot11RSNAConfigSATimeout, + RSN_SUITE_ARG(wpa_key_mgmt_suite(wpa_s)), + RSN_SUITE_ARG(wpa_cipher_suite(wpa_s, + wpa_s->pairwise_cipher)), + RSN_SUITE_ARG(wpa_cipher_suite(wpa_s, + wpa_s->group_cipher)), + pmkid_txt, + RSN_SUITE_ARG(wpa_key_mgmt_suite(wpa_s)), + RSN_SUITE_ARG(wpa_cipher_suite(wpa_s, + wpa_s->pairwise_cipher)), + RSN_SUITE_ARG(wpa_cipher_suite(wpa_s, + wpa_s->group_cipher))); + return len; +} + + +#ifdef IEEE8021X_EAPOL + +static void rsn_preauth_receive(void *ctx, unsigned char *src_addr, + unsigned char *buf, size_t len) +{ + struct wpa_supplicant *wpa_s = ctx; + + wpa_printf(MSG_DEBUG, "RX pre-auth from " MACSTR, MAC2STR(src_addr)); + wpa_hexdump(MSG_MSGDUMP, "RX pre-auth", buf, len); + + if (wpa_s->preauth_eapol == NULL || + memcmp(wpa_s->preauth_bssid, "\x00\x00\x00\x00\x00\x00", + ETH_ALEN) == 0 || + memcmp(wpa_s->preauth_bssid, src_addr, ETH_ALEN) != 0) { + wpa_printf(MSG_WARNING, "RSN pre-auth frame received from " + "unexpected source " MACSTR " - dropped", + MAC2STR(src_addr)); + return; + } + + eapol_sm_rx_eapol(wpa_s->preauth_eapol, src_addr, buf, len); +} + + +static void rsn_preauth_eapol_cb(struct eapol_sm *eapol, int success, + void *ctx) +{ + struct wpa_supplicant *wpa_s = ctx; + u8 pmk[PMK_LEN]; + + wpa_msg(wpa_s, MSG_INFO, "RSN: pre-authentication with " MACSTR + " %s", MAC2STR(wpa_s->preauth_bssid), + success ? "completed successfully" : "failed"); + + if (success) { + int res, pmk_len; + pmk_len = PMK_LEN; + res = eapol_sm_get_key(eapol, pmk, PMK_LEN); +#ifdef EAP_LEAP + if (res) { + res = eapol_sm_get_key(eapol, pmk, 16); + pmk_len = 16; + } +#endif /* EAP_LEAP */ + if (res == 0) { + wpa_hexdump_key(MSG_DEBUG, "RSN: PMK from pre-auth", + pmk, pmk_len); + wpa_s->pmk_len = pmk_len; + pmksa_cache_add(wpa_s, pmk, pmk_len, + wpa_s->preauth_bssid, wpa_s->own_addr); + } else { + wpa_msg(wpa_s, MSG_INFO, "RSN: failed to get master " + "session key from pre-auth EAPOL state " + "machines"); + } + } + + rsn_preauth_deinit(wpa_s); + rsn_preauth_candidate_process(wpa_s); +} + + +static void rsn_preauth_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + wpa_msg(wpa_s, MSG_INFO, "RSN: pre-authentication with " MACSTR + " timed out", MAC2STR(wpa_s->preauth_bssid)); + rsn_preauth_deinit(wpa_s); + rsn_preauth_candidate_process(wpa_s); +} + + +int rsn_preauth_init(struct wpa_supplicant *wpa_s, u8 *dst) +{ + struct eapol_config eapol_conf; + struct eapol_ctx *ctx; + + if (wpa_s->preauth_eapol) + return -1; + + wpa_msg(wpa_s, MSG_DEBUG, "RSN: starting pre-authentication with " + MACSTR, MAC2STR(dst)); + + wpa_s->l2_preauth = l2_packet_init(wpa_s->ifname, + wpa_drv_get_mac_addr(wpa_s), + ETH_P_RSN_PREAUTH, + rsn_preauth_receive, wpa_s); + if (wpa_s->l2_preauth == NULL) { + wpa_printf(MSG_WARNING, "RSN: Failed to initialize L2 packet " + "processing for pre-authentication"); + return -2; + } + + ctx = malloc(sizeof(*ctx)); + if (ctx == NULL) { + wpa_printf(MSG_WARNING, "Failed to allocate EAPOL context."); + return -4; + } + memset(ctx, 0, sizeof(*ctx)); + ctx->ctx = wpa_s; + ctx->preauth = 1; + ctx->cb = rsn_preauth_eapol_cb; + ctx->cb_ctx = wpa_s; + ctx->scard_ctx = wpa_s->scard; + ctx->eapol_done_cb = wpa_supplicant_notify_eapol_done; + ctx->eapol_send = wpa_eapol_send_preauth; + + wpa_s->preauth_eapol = eapol_sm_init(ctx); + if (wpa_s->preauth_eapol == NULL) { + free(ctx); + wpa_printf(MSG_WARNING, "RSN: Failed to initialize EAPOL " + "state machines for pre-authentication"); + return -3; + } + memset(&eapol_conf, 0, sizeof(eapol_conf)); + eapol_conf.accept_802_1x_keys = 0; + eapol_conf.required_keys = 0; + eapol_conf.fast_reauth = wpa_s->conf->fast_reauth; + if (wpa_s->current_ssid) + eapol_conf.workaround = wpa_s->current_ssid->eap_workaround; + eapol_sm_notify_config(wpa_s->preauth_eapol, wpa_s->current_ssid, + &eapol_conf); + memcpy(wpa_s->preauth_bssid, dst, ETH_ALEN); + + eapol_sm_notify_portValid(wpa_s->preauth_eapol, TRUE); + /* 802.1X::portControl = Auto */ + eapol_sm_notify_portEnabled(wpa_s->preauth_eapol, TRUE); + + eloop_register_timeout(60, 0, rsn_preauth_timeout, wpa_s, NULL); + + return 0; +} + + +void rsn_preauth_deinit(struct wpa_supplicant *wpa_s) +{ + if (!wpa_s->preauth_eapol) + return; + + eloop_cancel_timeout(rsn_preauth_timeout, wpa_s, NULL); + eapol_sm_deinit(wpa_s->preauth_eapol); + wpa_s->preauth_eapol = NULL; + memset(wpa_s->preauth_bssid, 0, ETH_ALEN); + + l2_packet_deinit(wpa_s->l2_preauth); + wpa_s->l2_preauth = NULL; +} + + +static void rsn_preauth_candidate_process(struct wpa_supplicant *wpa_s) +{ + struct rsn_pmksa_candidate *candidate; + + if (wpa_s->pmksa_candidates == NULL) + return; + + /* TODO: drop priority for old candidate entries */ + + wpa_msg(wpa_s, MSG_DEBUG, "RSN: processing PMKSA candidate list"); + if (wpa_s->preauth_eapol || + wpa_s->proto != WPA_PROTO_RSN || + wpa_s->wpa_state != WPA_COMPLETED || + wpa_s->key_mgmt != WPA_KEY_MGMT_IEEE8021X) { + wpa_msg(wpa_s, MSG_DEBUG, "RSN: not in suitable state for new " + "pre-authentication"); + return; /* invalid state for new pre-auth */ + } + + while (wpa_s->pmksa_candidates) { + struct rsn_pmksa_cache *p = NULL; + candidate = wpa_s->pmksa_candidates; + p = pmksa_cache_get(wpa_s, candidate->bssid, NULL); + if (memcmp(wpa_s->bssid, candidate->bssid, ETH_ALEN) != 0 && + p == NULL) { + wpa_msg(wpa_s, MSG_DEBUG, "RSN: PMKSA candidate " + MACSTR " selected for pre-authentication", + MAC2STR(candidate->bssid)); + wpa_s->pmksa_candidates = candidate->next; + rsn_preauth_init(wpa_s, candidate->bssid); + free(candidate); + return; + } + wpa_msg(wpa_s, MSG_DEBUG, "RSN: PMKSA candidate " MACSTR + " does not need pre-authentication anymore", + MAC2STR(candidate->bssid)); + /* Some drivers (e.g., NDIS) expect to get notified about the + * PMKIDs again, so report the existing data now. */ + if (p) + wpa_drv_add_pmkid(wpa_s, candidate->bssid, p->pmkid); + + wpa_s->pmksa_candidates = candidate->next; + free(candidate); + } + wpa_msg(wpa_s, MSG_DEBUG, "RSN: no more pending PMKSA candidates"); +} + + +void pmksa_candidate_add(struct wpa_supplicant *wpa_s, const u8 *bssid, + int prio) +{ + struct rsn_pmksa_candidate *cand, *prev, *pos; + + /* If BSSID already on candidate list, update the priority of the old + * entry. Do not override priority based on normal scan results. */ + prev = NULL; + cand = wpa_s->pmksa_candidates; + while (cand) { + if (memcmp(cand->bssid, bssid, ETH_ALEN) == 0) { + if (prev) + prev->next = cand->next; + else + wpa_s->pmksa_candidates = cand->next; + break; + } + prev = cand; + cand = cand->next; + } + + if (cand) { + if (prio < PMKID_CANDIDATE_PRIO_SCAN) + cand->priority = prio; + } else { + cand = malloc(sizeof(*cand)); + if (cand == NULL) + return; + memset(cand, 0, sizeof(*cand)); + memcpy(cand->bssid, bssid, ETH_ALEN); + cand->priority = prio; + } + + /* Add candidate to the list; order by increasing priority value. i.e., + * highest priority (smallest value) first. */ + prev = NULL; + pos = wpa_s->pmksa_candidates; + while (pos) { + if (cand->priority <= pos->priority) + break; + prev = pos; + pos = pos->next; + } + cand->next = pos; + if (prev) + prev->next = cand; + else + wpa_s->pmksa_candidates = cand; + + wpa_msg(wpa_s, MSG_DEBUG, "RSN: added PMKSA cache " + "candidate " MACSTR " prio %d", MAC2STR(bssid), prio); + rsn_preauth_candidate_process(wpa_s); +} + + +/* TODO: schedule periodic scans if current AP supports preauth */ +void rsn_preauth_scan_results(struct wpa_supplicant *wpa_s, + struct wpa_scan_result *results, int count) +{ + struct wpa_scan_result *r; + struct wpa_ie_data ie; + int i; + + if (wpa_s->current_ssid == NULL) + return; + + pmksa_candidate_free(wpa_s); + + for (i = count - 1; i >= 0; i--) { + r = &results[i]; + if (r->ssid_len == wpa_s->current_ssid->ssid_len && + memcmp(r->ssid, wpa_s->current_ssid->ssid, r->ssid_len) == + 0 && + memcmp(r->bssid, wpa_s->bssid, ETH_ALEN) != 0 && + r->rsn_ie_len > 0 && + wpa_parse_wpa_ie(wpa_s, r->rsn_ie, r->rsn_ie_len, &ie) == + 0 && + (ie.capabilities & WPA_CAPABILITY_PREAUTH) && + pmksa_cache_get(wpa_s, r->bssid, NULL) == NULL) { + /* Give less priority to candidates found from normal + * scan results. */ + pmksa_candidate_add(wpa_s, r->bssid, + PMKID_CANDIDATE_PRIO_SCAN); + } + } +} + +#else /* IEEE8021X_EAPOL */ + +static void rsn_preauth_candidate_process(struct wpa_supplicant *wpa_s) +{ +} + +#endif /* IEEE8021X_EAPOL */ diff --git a/contrib/wpa_supplicant/wpa.h b/contrib/wpa_supplicant/wpa.h new file mode 100644 index 0000000..a4f3f55 --- /dev/null +++ b/contrib/wpa_supplicant/wpa.h @@ -0,0 +1,112 @@ +#ifndef WPA_H +#define WPA_H + +#define BIT(n) (1 << (n)) + +struct ieee802_1x_hdr { + u8 version; + u8 type; + u16 length; + /* followed by length octets of data */ +} __attribute__ ((packed)); + +#define EAPOL_VERSION 2 + +enum { IEEE802_1X_TYPE_EAP_PACKET = 0, + IEEE802_1X_TYPE_EAPOL_START = 1, + IEEE802_1X_TYPE_EAPOL_LOGOFF = 2, + IEEE802_1X_TYPE_EAPOL_KEY = 3, + IEEE802_1X_TYPE_EAPOL_ENCAPSULATED_ASF_ALERT = 4 +}; + +enum { EAPOL_KEY_TYPE_RC4 = 1, EAPOL_KEY_TYPE_RSN = 2, + EAPOL_KEY_TYPE_WPA = 254 }; + + +#define IEEE8021X_REPLAY_COUNTER_LEN 8 +#define IEEE8021X_KEY_SIGN_LEN 16 +#define IEEE8021X_KEY_IV_LEN 16 + +#define IEEE8021X_KEY_INDEX_FLAG 0x80 +#define IEEE8021X_KEY_INDEX_MASK 0x03 + +struct ieee802_1x_eapol_key { + u8 type; + u16 key_length; + /* does not repeat within the life of the keying material used to + * encrypt the Key field; 64-bit NTP timestamp MAY be used here */ + u8 replay_counter[IEEE8021X_REPLAY_COUNTER_LEN]; + u8 key_iv[IEEE8021X_KEY_IV_LEN]; /* cryptographically random number */ + u8 key_index; /* key flag in the most significant bit: + * 0 = broadcast (default key), + * 1 = unicast (key mapping key); key index is in the + * 7 least significant bits */ + /* HMAC-MD5 message integrity check computed with MS-MPPE-Send-Key as + * the key */ + u8 key_signature[IEEE8021X_KEY_SIGN_LEN]; + + /* followed by key: if packet body length = 44 + key length, then the + * key field (of key_length bytes) contains the key in encrypted form; + * if packet body length = 44, key field is absent and key_length + * represents the number of least significant octets from + * MS-MPPE-Send-Key attribute to be used as the keying material; + * RC4 key used in encryption = Key-IV + MS-MPPE-Recv-Key */ +} __attribute__ ((packed)); + + +#define WPA_NONCE_LEN 32 +#define WPA_REPLAY_COUNTER_LEN 8 + +struct wpa_eapol_key { + u8 type; + u16 key_info; + u16 key_length; + u8 replay_counter[WPA_REPLAY_COUNTER_LEN]; + u8 key_nonce[WPA_NONCE_LEN]; + u8 key_iv[16]; + u8 key_rsc[8]; + u8 key_id[8]; /* Reserved in IEEE 802.11i/RSN */ + u8 key_mic[16]; + u16 key_data_length; + /* followed by key_data_length bytes of key_data */ +} __attribute__ ((packed)); + +#define WPA_KEY_INFO_TYPE_MASK (BIT(0) | BIT(1) | BIT(2)) +#define WPA_KEY_INFO_TYPE_HMAC_MD5_RC4 BIT(0) +#define WPA_KEY_INFO_TYPE_HMAC_SHA1_AES BIT(1) +#define WPA_KEY_INFO_KEY_TYPE BIT(3) /* 1 = Pairwise, 0 = Group key */ +/* bit4..5 is used in WPA, but is reserved in IEEE 802.11i/RSN */ +#define WPA_KEY_INFO_KEY_INDEX_MASK (BIT(4) | BIT(5)) +#define WPA_KEY_INFO_KEY_INDEX_SHIFT 4 +#define WPA_KEY_INFO_INSTALL BIT(6) /* pairwise */ +#define WPA_KEY_INFO_TXRX BIT(6) /* group */ +#define WPA_KEY_INFO_ACK BIT(7) +#define WPA_KEY_INFO_MIC BIT(8) +#define WPA_KEY_INFO_SECURE BIT(9) +#define WPA_KEY_INFO_ERROR BIT(10) +#define WPA_KEY_INFO_REQUEST BIT(11) +#define WPA_KEY_INFO_ENCR_KEY_DATA BIT(12) /* IEEE 802.11i/RSN only */ + +#define WPA_CAPABILITY_PREAUTH BIT(0) + +#define GENERIC_INFO_ELEM 0xdd +#define RSN_INFO_ELEM 0x30 + +enum { + REASON_UNSPECIFIED = 1, + REASON_DEAUTH_LEAVING = 3, + REASON_INVALID_IE = 13, + REASON_MICHAEL_MIC_FAILURE = 14, + REASON_4WAY_HANDSHAKE_TIMEOUT = 15, + REASON_GROUP_KEY_UPDATE_TIMEOUT = 16, + REASON_IE_IN_4WAY_DIFFERS = 17, + REASON_GROUP_CIPHER_NOT_VALID = 18, + REASON_PAIRWISE_CIPHER_NOT_VALID = 19, + REASON_AKMP_NOT_VALID = 20, + REASON_UNSUPPORTED_RSN_IE_VERSION = 21, + REASON_INVALID_RSN_IE_CAPAB = 22, + REASON_IEEE_802_1X_AUTH_FAILED = 23, + REASON_CIPHER_SUITE_REJECTED = 24 +}; + +#endif /* WPA_H */ diff --git a/contrib/wpa_supplicant/wpa_cli.c b/contrib/wpa_supplicant/wpa_cli.c new file mode 100644 index 0000000..8582fae --- /dev/null +++ b/contrib/wpa_supplicant/wpa_cli.c @@ -0,0 +1,850 @@ +/* + * WPA Supplicant - command line interface for wpa_supplicant daemon + * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <signal.h> +#include <unistd.h> +#include <dirent.h> +#ifdef CONFIG_READLINE +#include <readline/readline.h> +#include <readline/history.h> +#endif /* CONFIG_READLINE */ + +#include "wpa_ctrl.h" +#ifdef CONFIG_NATIVE_WINDOWS +#include "common.h" +#endif /* CONFIG_NATIVE_WINDOWS */ +#include "version.h" + + +static const char *wpa_cli_version = +"wpa_cli v" VERSION_STR "\n" +"Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.fi> and contributors"; + + +static const char *wpa_cli_license = +"This program is free software. You can distribute it and/or modify it\n" +"under the terms of the GNU General Public License version 2.\n" +"\n" +"Alternatively, this software may be distributed under the terms of the\n" +"BSD license. See README and COPYING for more details.\n"; + +static const char *wpa_cli_full_license = +"This program is free software; you can redistribute it and/or modify\n" +"it under the terms of the GNU General Public License version 2 as\n" +"published by the Free Software Foundation.\n" +"\n" +"This program is distributed in the hope that it will be useful,\n" +"but WITHOUT ANY WARRANTY; without even the implied warranty of\n" +"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" +"GNU General Public License for more details.\n" +"\n" +"You should have received a copy of the GNU General Public License\n" +"along with this program; if not, write to the Free Software\n" +"Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\n" +"\n" +"Alternatively, this software may be distributed under the terms of the\n" +"BSD license.\n" +"\n" +"Redistribution and use in source and binary forms, with or without\n" +"modification, are permitted provided that the following conditions are\n" +"met:\n" +"\n" +"1. Redistributions of source code must retain the above copyright\n" +" notice, this list of conditions and the following disclaimer.\n" +"\n" +"2. Redistributions in binary form must reproduce the above copyright\n" +" notice, this list of conditions and the following disclaimer in the\n" +" documentation and/or other materials provided with the distribution.\n" +"\n" +"3. Neither the name(s) of the above-listed copyright holder(s) nor the\n" +" names of its contributors may be used to endorse or promote products\n" +" derived from this software without specific prior written permission.\n" +"\n" +"THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n" +"\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n" +"LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n" +"A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n" +"OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n" +"SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n" +"LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n" +"DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n" +"THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n" +"(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n" +"OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n" +"\n"; + +static const char *commands_help = +"commands:\n" +" status [verbose] = get current WPA/EAPOL/EAP status\n" +" mib = get MIB variables (dot1x, dot11)\n" +" help = show this usage help\n" +" interface [ifname] = show interfaces/select interface\n" +" level <debug level> = change debug level\n" +" license = show full wpa_cli license\n" +" logoff = IEEE 802.1X EAPOL state machine logoff\n" +" logon = IEEE 802.1X EAPOL state machine logon\n" +" set = set variables (shows list of variables when run without arguments)\n" +" pmksa = show PMKSA cache\n" +" reassociate = force reassociation\n" +" reconfigure = force wpa_supplicant to re-read its configuration file\n" +" preauthenticate <BSSID> = force preauthentication\n" +" identity <network id> <identity> = configure identity for an SSID\n" +" password <network id> <password> = configure password for an SSID\n" +" otp <network id> <password> = configure one-time-password for an SSID\n" +" terminate = terminate wpa_supplicant\n" +" quit = exit wpa_cli\n"; + +static struct wpa_ctrl *ctrl_conn; +static int wpa_cli_quit = 0; +static int wpa_cli_attached = 0; +static const char *ctrl_iface_dir = "/var/run/wpa_supplicant"; +static char *ctrl_ifname = NULL; + + +static void usage(void) +{ + printf("wpa_cli [-p<path to ctrl sockets>] [-i<ifname>] [-hv] " + "[command..]\n" + " -h = help (show this usage text)\n" + " -v = shown version information\n" + " default path: /var/run/wpa_supplicant\n" + " default interface: first interface found in socket path\n" + "%s", + commands_help); +} + + +static struct wpa_ctrl * wpa_cli_open_connection(const char *ifname) +{ +#ifdef CONFIG_CTRL_IFACE_UDP + ctrl_conn = wpa_ctrl_open(""); + return ctrl_conn; +#else /* CONFIG_CTRL_IFACE_UDP */ + char *cfile; + int flen; + + if (ifname == NULL) + return NULL; + + flen = strlen(ctrl_iface_dir) + strlen(ifname) + 2; + cfile = malloc(flen); + if (cfile == NULL) + return NULL; + snprintf(cfile, flen, "%s/%s", ctrl_iface_dir, ifname); + + ctrl_conn = wpa_ctrl_open(cfile); + free(cfile); + return ctrl_conn; +#endif /* CONFIG_CTRL_IFACE_UDP */ +} + + +static void wpa_cli_close_connection(void) +{ + if (ctrl_conn == NULL) + return; + + if (wpa_cli_attached) { + wpa_ctrl_detach(ctrl_conn); + wpa_cli_attached = 0; + } + wpa_ctrl_close(ctrl_conn); + ctrl_conn = NULL; +} + + +static void wpa_cli_msg_cb(char *msg, size_t len) +{ + printf("%s\n", msg); +} + + +static int _wpa_ctrl_command(struct wpa_ctrl *ctrl, char *cmd, int print) +{ + char buf[2048]; + size_t len; + int ret; + + if (ctrl_conn == NULL) { + printf("Not connected to wpa_supplicant - command dropped.\n"); + return -1; + } + len = sizeof(buf) - 1; + ret = wpa_ctrl_request(ctrl, cmd, strlen(cmd), buf, &len, + wpa_cli_msg_cb); + if (ret == -2) { + printf("'%s' command timed out.\n", cmd); + return -2; + } else if (ret < 0) { + printf("'%s' command failed.\n", cmd); + return -1; + } + if (print) { + buf[len] = '\0'; + printf("%s", buf); + } + return 0; +} + + +static int wpa_ctrl_command(struct wpa_ctrl *ctrl, char *cmd) +{ + return _wpa_ctrl_command(ctrl, cmd, 1); +} + + +static int wpa_cli_cmd_status(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + int verbose = argc > 0 && strcmp(argv[0], "verbose") == 0; + return wpa_ctrl_command(ctrl, verbose ? "STATUS-VERBOSE" : "STATUS"); +} + + +static int wpa_cli_cmd_ping(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + return wpa_ctrl_command(ctrl, "PING"); +} + + +static int wpa_cli_cmd_mib(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + return wpa_ctrl_command(ctrl, "MIB"); +} + + +static int wpa_cli_cmd_pmksa(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + return wpa_ctrl_command(ctrl, "PMKSA"); +} + + +static int wpa_cli_cmd_help(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + printf("%s", commands_help); + return 0; +} + + +static int wpa_cli_cmd_license(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + printf("%s\n\n%s\n", wpa_cli_version, wpa_cli_full_license); + return 0; +} + + +static int wpa_cli_cmd_quit(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + wpa_cli_quit = 1; + return 0; +} + + +static void wpa_cli_show_variables(void) +{ + printf("set variables:\n" + " EAPOL::heldPeriod (EAPOL state machine held period, " + "in seconds)\n" + " EAPOL::authPeriod (EAPOL state machine authentication " + "period, in seconds)\n" + " EAPOL::startPeriod (EAPOL state machine start period, in " + "seconds)\n" + " EAPOL::maxStart (EAPOL state machine maximum start " + "attempts)\n"); +} + + +static int wpa_cli_cmd_set(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + char cmd[256]; + + if (argc == 0) { + wpa_cli_show_variables(); + return 0; + } + + if (argc != 2) { + printf("Invalid SET command: needs two arguments (variable " + "name and value)\n"); + return 0; + } + + if (snprintf(cmd, sizeof(cmd), "SET %s %s", argv[0], argv[1]) >= + sizeof(cmd) - 1) { + printf("Too long SET command.\n"); + return 0; + } + return wpa_ctrl_command(ctrl, cmd); +} + + +static int wpa_cli_cmd_logoff(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + return wpa_ctrl_command(ctrl, "LOGOFF"); +} + + +static int wpa_cli_cmd_logon(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + return wpa_ctrl_command(ctrl, "LOGON"); +} + + +static int wpa_cli_cmd_reassociate(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "REASSOCIATE"); +} + + +static int wpa_cli_cmd_preauthenticate(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char cmd[256]; + + if (argc != 1) { + printf("Invalid PREAUTH command: needs one argument " + "(BSSID)\n"); + return 0; + } + + if (snprintf(cmd, sizeof(cmd), "PREAUTH %s", argv[0]) >= + sizeof(cmd) - 1) { + printf("Too long PREAUTH command.\n"); + return 0; + } + return wpa_ctrl_command(ctrl, cmd); +} + + +static int wpa_cli_cmd_level(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + char cmd[256]; + if (argc != 1) { + printf("Invalid LEVEL command: needs one argument (debug " + "level)\n"); + return 0; + } + snprintf(cmd, sizeof(cmd), "LEVEL %s", argv[0]); + return wpa_ctrl_command(ctrl, cmd); +} + + +static int wpa_cli_cmd_identity(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + char cmd[256], *pos, *end; + int i; + + if (argc < 2) { + printf("Invalid IDENTITY command: needs two arguments " + "(network id and identity)\n"); + return 0; + } + + end = cmd + sizeof(cmd); + pos = cmd; + pos += snprintf(pos, end - pos, "CTRL-RSP-IDENTITY-%s:%s", + argv[0], argv[1]); + for (i = 2; i < argc; i++) + pos += snprintf(pos, end - pos, " %s", argv[i]); + + return wpa_ctrl_command(ctrl, cmd); +} + + +static int wpa_cli_cmd_password(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + char cmd[256], *pos, *end; + int i; + + if (argc < 2) { + printf("Invalid PASSWORD command: needs two arguments " + "(network id and password)\n"); + return 0; + } + + end = cmd + sizeof(cmd); + pos = cmd; + pos += snprintf(pos, end - pos, "CTRL-RSP-PASSWORD-%s:%s", + argv[0], argv[1]); + for (i = 2; i < argc; i++) + pos += snprintf(pos, end - pos, " %s", argv[i]); + + return wpa_ctrl_command(ctrl, cmd); +} + + +static int wpa_cli_cmd_otp(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + char cmd[256], *pos, *end; + int i; + + if (argc < 2) { + printf("Invalid OTP command: needs two arguments (network " + "id and password)\n"); + return 0; + } + + end = cmd + sizeof(cmd); + pos = cmd; + pos += snprintf(pos, end - pos, "CTRL-RSP-OTP-%s:%s", + argv[0], argv[1]); + for (i = 2; i < argc; i++) + pos += snprintf(pos, end - pos, " %s", argv[i]); + + return wpa_ctrl_command(ctrl, cmd); +} + + +static void wpa_cli_list_interfaces(struct wpa_ctrl *ctrl) +{ + struct dirent *dent; + DIR *dir; + + dir = opendir(ctrl_iface_dir); + if (dir == NULL) { + printf("Control interface directory '%s' could not be " + "openned.\n", ctrl_iface_dir); + return; + } + + printf("Available interfaces:\n"); + while ((dent = readdir(dir))) { + if (strcmp(dent->d_name, ".") == 0 || + strcmp(dent->d_name, "..") == 0) + continue; + printf("%s\n", dent->d_name); + } + closedir(dir); +} + + +static int wpa_cli_cmd_interface(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + if (argc < 1) { + wpa_cli_list_interfaces(ctrl); + return 0; + } + + wpa_cli_close_connection(); + free(ctrl_ifname); + ctrl_ifname = strdup(argv[0]); + + if (wpa_cli_open_connection(ctrl_ifname)) { + printf("Connected to interface '%s.\n", ctrl_ifname); + if (wpa_ctrl_attach(ctrl_conn) == 0) { + wpa_cli_attached = 1; + } else { + printf("Warning: Failed to attach to " + "wpa_supplicant.\n"); + } + } else { + printf("Could not connect to interface '%s' - re-trying\n", + ctrl_ifname); + } + return 0; +} + + +static int wpa_cli_cmd_reconfigure(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "RECONFIGURE"); +} + + +static int wpa_cli_cmd_terminate(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "TERMINATE"); +} + + +struct wpa_cli_cmd { + const char *cmd; + int (*handler)(struct wpa_ctrl *ctrl, int argc, char *argv[]); +}; + +static struct wpa_cli_cmd wpa_cli_commands[] = { + { "status", wpa_cli_cmd_status }, + { "ping", wpa_cli_cmd_ping }, + { "mib", wpa_cli_cmd_mib }, + { "help", wpa_cli_cmd_help }, + { "interface", wpa_cli_cmd_interface }, + { "level", wpa_cli_cmd_level }, + { "license", wpa_cli_cmd_license }, + { "quit", wpa_cli_cmd_quit }, + { "set", wpa_cli_cmd_set }, + { "logon", wpa_cli_cmd_logon }, + { "logoff", wpa_cli_cmd_logoff }, + { "pmksa", wpa_cli_cmd_pmksa }, + { "reassociate", wpa_cli_cmd_reassociate }, + { "preauthenticate", wpa_cli_cmd_preauthenticate }, + { "identity", wpa_cli_cmd_identity }, + { "password", wpa_cli_cmd_password }, + { "otp", wpa_cli_cmd_otp }, + { "reconfigure", wpa_cli_cmd_reconfigure }, + { "terminate", wpa_cli_cmd_terminate }, + { NULL, NULL } +}; + + +static void wpa_request(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + struct wpa_cli_cmd *cmd, *match = NULL; + int count; + + count = 0; + cmd = wpa_cli_commands; + while (cmd->cmd) { + if (strncasecmp(cmd->cmd, argv[0], strlen(argv[0])) == 0) { + match = cmd; + count++; + } + cmd++; + } + + if (count > 1) { + printf("Ambiguous command '%s'; possible commands:", argv[0]); + cmd = wpa_cli_commands; + while (cmd->cmd) { + if (strncasecmp(cmd->cmd, argv[0], strlen(argv[0])) == + 0) { + printf(" %s", cmd->cmd); + } + cmd++; + } + printf("\n"); + } else if (count == 0) { + printf("Unknown command '%s'\n", argv[0]); + } else { + match->handler(ctrl, argc - 1, &argv[1]); + } +} + + +static void wpa_cli_recv_pending(struct wpa_ctrl *ctrl, int in_read) +{ + int first = 1; + if (ctrl_conn == NULL) + return; + while (wpa_ctrl_pending(ctrl)) { + char buf[256]; + size_t len = sizeof(buf) - 1; + if (wpa_ctrl_recv(ctrl, buf, &len) == 0) { + buf[len] = '\0'; + if (in_read && first) + printf("\n"); + first = 0; + printf("%s\n", buf); + } else { + printf("Could not read pending message.\n"); + break; + } + } +} + + +#ifdef CONFIG_READLINE +static char * wpa_cli_cmd_gen(const char *text, int state) +{ + static int i, len; + const char *cmd; + + if (state == 0) { + i = 0; + len = strlen(text); + } + + while ((cmd = wpa_cli_commands[i].cmd)) { + i++; + if (strncasecmp(cmd, text, len) == 0) + return strdup(cmd); + } + + return NULL; +} + + +static char * wpa_cli_dummy_gen(const char *text, int state) +{ + return NULL; +} + + +static char ** wpa_cli_completion(const char *text, int start, int end) +{ + return rl_completion_matches(text, start == 0 ? + wpa_cli_cmd_gen : wpa_cli_dummy_gen); +} +#endif /* CONFIG_READLINE */ + + +static void wpa_cli_interactive(void) +{ +#define max_args 10 + char cmdbuf[256], *cmd, *argv[max_args], *pos; + int argc; +#ifdef CONFIG_READLINE + char *home, *hfile = NULL; +#endif /* CONFIG_READLINE */ + + printf("\nInteractive mode\n\n"); + +#ifdef CONFIG_READLINE + rl_attempted_completion_function = wpa_cli_completion; + home = getenv("HOME"); + if (home) { + const char *fname = ".wpa_cli_history"; + int hfile_len = strlen(home) + 1 + strlen(fname) + 1; + hfile = malloc(hfile_len); + if (hfile) { + snprintf(hfile, hfile_len, "%s/%s", home, fname); + read_history(hfile); + stifle_history(100); + } + } +#endif /* CONFIG_READLINE */ + + do { + wpa_cli_recv_pending(ctrl_conn, 0); +#ifndef CONFIG_NATIVE_WINDOWS + alarm(1); +#endif /* CONFIG_NATIVE_WINDOWS */ +#ifdef CONFIG_READLINE + cmd = readline("> "); + if (cmd && *cmd) { + HIST_ENTRY *h; + while (next_history()) + ; + h = previous_history(); + if (h == NULL || strcmp(cmd, h->line) != 0) + add_history(cmd); + next_history(); + } +#else /* CONFIG_READLINE */ + printf("> "); + cmd = fgets(cmdbuf, sizeof(cmdbuf), stdin); +#endif /* CONFIG_READLINE */ +#ifndef CONFIG_NATIVE_WINDOWS + alarm(0); +#endif /* CONFIG_NATIVE_WINDOWS */ + if (cmd == NULL) + break; + pos = cmd; + while (*pos != '\0') { + if (*pos == '\n') { + *pos = '\0'; + break; + } + pos++; + } + argc = 0; + pos = cmd; + for (;;) { + while (*pos == ' ') + pos++; + if (*pos == '\0') + break; + argv[argc] = pos; + argc++; + if (argc == max_args) + break; + while (*pos != '\0' && *pos != ' ') + pos++; + if (*pos == ' ') + *pos++ = '\0'; + } + if (argc) + wpa_request(ctrl_conn, argc, argv); + + if (cmd != cmdbuf) + free(cmd); + } while (!wpa_cli_quit); + +#ifdef CONFIG_READLINE + if (hfile) { + /* Save command history, excluding lines that may contain + * passwords. */ + HIST_ENTRY *h; + history_set_pos(0); + h = next_history(); + while (h) { + char *p = h->line; + while (*p == ' ' || *p == '\t') + p++; + if (strncasecmp(p, "pa", 2) == 0 || + strncasecmp(p, "o", 1) == 0) { + h = remove_history(where_history()); + if (h) { + free(h->line); + free(h->data); + free(h); + } + h = current_history(); + } else { + h = next_history(); + } + } + write_history(hfile); + free(hfile); + } +#endif /* CONFIG_READLINE */ +} + + +static void wpa_cli_terminate(int sig) +{ + wpa_cli_close_connection(); + exit(0); +} + + +#ifndef CONFIG_NATIVE_WINDOWS +static void wpa_cli_alarm(int sig) +{ + if (ctrl_conn && _wpa_ctrl_command(ctrl_conn, "PING", 0)) { + printf("Connection to wpa_supplicant lost - trying to " + "reconnect\n"); + wpa_cli_close_connection(); + } + if (!ctrl_conn) { + ctrl_conn = wpa_cli_open_connection(ctrl_ifname); + if (ctrl_conn) { + printf("Connection to wpa_supplicant " + "re-established\n"); + if (wpa_ctrl_attach(ctrl_conn) == 0) { + wpa_cli_attached = 1; + } else { + printf("Warning: Failed to attach to " + "wpa_supplicant.\n"); + } + } + } + if (ctrl_conn) + wpa_cli_recv_pending(ctrl_conn, 1); + alarm(1); +} +#endif /* CONFIG_NATIVE_WINDOWS */ + + +int main(int argc, char *argv[]) +{ + int interactive; + int warning_displayed = 0; + int c; + +#ifdef CONFIG_NATIVE_WINDOWS + WSADATA wsaData; + if (WSAStartup(MAKEWORD(2, 0), &wsaData)) { + printf("Could not find a usable WinSock.dll\n"); + return -1; + } +#endif /* CONFIG_NATIVE_WINDOWS */ + + for (;;) { + c = getopt(argc, argv, "hi:p:v"); + if (c < 0) + break; + switch (c) { + case 'h': + usage(); + return 0; + case 'v': + printf("%s\n", wpa_cli_version); + return 0; + case 'i': + ctrl_ifname = strdup(optarg); + break; + case 'p': + ctrl_iface_dir = optarg; + break; + default: + usage(); + return -1; + } + } + + interactive = argc == optind; + + if (interactive) + printf("%s\n\n%s\n\n", wpa_cli_version, wpa_cli_license); + + for (;;) { + if (ctrl_ifname == NULL) { + struct dirent *dent; + DIR *dir = opendir(ctrl_iface_dir); + if (dir) { + while ((dent = readdir(dir))) { + if (strcmp(dent->d_name, ".") == 0 || + strcmp(dent->d_name, "..") == 0) + continue; + printf("Selected interface '%s'\n", + dent->d_name); + ctrl_ifname = strdup(dent->d_name); + break; + } + closedir(dir); + } + } + ctrl_conn = wpa_cli_open_connection(ctrl_ifname); + if (ctrl_conn) { + if (warning_displayed) + printf("Connection established.\n"); + break; + } + + if (!interactive) { + perror("Failed to connect to wpa_supplicant - " + "wpa_ctrl_open"); + return -1; + } + + if (!warning_displayed) { + printf("Could not connect to wpa_supplicant - " + "re-trying\n"); + warning_displayed = 1; + } + sleep(1); + continue; + } + + signal(SIGINT, wpa_cli_terminate); + signal(SIGTERM, wpa_cli_terminate); +#ifndef CONFIG_NATIVE_WINDOWS + signal(SIGALRM, wpa_cli_alarm); +#endif /* CONFIG_NATIVE_WINDOWS */ + + if (interactive) { + if (wpa_ctrl_attach(ctrl_conn) == 0) { + wpa_cli_attached = 1; + } else { + printf("Warning: Failed to attach to " + "wpa_supplicant.\n"); + } + wpa_cli_interactive(); + } else + wpa_request(ctrl_conn, argc - optind, &argv[optind]); + + free(ctrl_ifname); + wpa_cli_close_connection(); + +#ifdef CONFIG_NATIVE_WINDOWS + WSACleanup(); +#endif /* CONFIG_NATIVE_WINDOWS */ + + return 0; +} diff --git a/contrib/wpa_supplicant/wpa_ctrl.c b/contrib/wpa_supplicant/wpa_ctrl.c new file mode 100644 index 0000000..16cef5f --- /dev/null +++ b/contrib/wpa_supplicant/wpa_ctrl.c @@ -0,0 +1,230 @@ +/* + * WPA Supplicant - wpa_supplicant control interface library + * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/time.h> +#ifndef CONFIG_NATIVE_WINDOWS +#include <sys/socket.h> +#include <sys/un.h> +#endif /* CONFIG_NATIVE_WINDOWS */ + +#include "wpa_ctrl.h" +#ifdef CONFIG_NATIVE_WINDOWS +#include "common.h" +#endif /* CONFIG_NATIVE_WINDOWS */ + + +struct wpa_ctrl { + int s; +#ifdef CONFIG_CTRL_IFACE_UDP + struct sockaddr_in local; + struct sockaddr_in dest; +#else /* CONFIG_CTRL_IFACE_UDP */ + struct sockaddr_un local; + struct sockaddr_un dest; +#endif /* CONFIG_CTRL_IFACE_UDP */ +}; + + +struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path) +{ + struct wpa_ctrl *ctrl; +#ifndef CONFIG_CTRL_IFACE_UDP + static int counter = 0; +#endif /* CONFIG_CTRL_IFACE_UDP */ + + ctrl = malloc(sizeof(*ctrl)); + if (ctrl == NULL) + return NULL; + memset(ctrl, 0, sizeof(*ctrl)); + +#ifdef CONFIG_CTRL_IFACE_UDP + ctrl->s = socket(PF_INET, SOCK_DGRAM, 0); + if (ctrl->s < 0) { + perror("socket"); + free(ctrl); + return NULL; + } + + ctrl->local.sin_family = AF_INET; + ctrl->local.sin_addr.s_addr = htonl((127 << 24) | 1); + if (bind(ctrl->s, (struct sockaddr *) &ctrl->local, + sizeof(ctrl->local)) < 0) { + close(ctrl->s); + free(ctrl); + return NULL; + } + + ctrl->dest.sin_family = AF_INET; + ctrl->dest.sin_addr.s_addr = htonl((127 << 24) | 1); + ctrl->dest.sin_port = htons(9877); + if (connect(ctrl->s, (struct sockaddr *) &ctrl->dest, + sizeof(ctrl->dest)) < 0) { + perror("connect"); + close(ctrl->s); + free(ctrl); + return NULL; + } +#else /* CONFIG_CTRL_IFACE_UDP */ + ctrl->s = socket(PF_UNIX, SOCK_DGRAM, 0); + if (ctrl->s < 0) { + free(ctrl); + return NULL; + } + + ctrl->local.sun_family = AF_UNIX; + snprintf(ctrl->local.sun_path, sizeof(ctrl->local.sun_path), + "/tmp/wpa_ctrl_%d-%d", getpid(), counter++); + if (bind(ctrl->s, (struct sockaddr *) &ctrl->local, + sizeof(ctrl->local.sun_family) + + strlen(ctrl->local.sun_path)) < 0) { + close(ctrl->s); + free(ctrl); + return NULL; + } + + ctrl->dest.sun_family = AF_UNIX; + strncpy(ctrl->dest.sun_path, ctrl_path, sizeof(ctrl->dest.sun_path)); + if (connect(ctrl->s, (struct sockaddr *) &ctrl->dest, + sizeof(ctrl->dest.sun_family) + + strlen(ctrl->dest.sun_path)) < 0) { + close(ctrl->s); + unlink(ctrl->local.sun_path); + free(ctrl); + return NULL; + } +#endif /* CONFIG_CTRL_IFACE_UDP */ + + return ctrl; +} + + +void wpa_ctrl_close(struct wpa_ctrl *ctrl) +{ +#ifndef CONFIG_CTRL_IFACE_UDP + unlink(ctrl->local.sun_path); +#endif /* CONFIG_CTRL_IFACE_UDP */ + close(ctrl->s); + free(ctrl); +} + + +int wpa_ctrl_request(struct wpa_ctrl *ctrl, char *cmd, size_t cmd_len, + char *reply, size_t *reply_len, + void (*msg_cb)(char *msg, size_t len)) +{ + struct timeval tv; + int res; + fd_set rfds; + + if (send(ctrl->s, cmd, cmd_len, 0) < 0) + return -1; + + for (;;) { + tv.tv_sec = 2; + tv.tv_usec = 0; + FD_ZERO(&rfds); + FD_SET(ctrl->s, &rfds); + res = select(ctrl->s + 1, &rfds, NULL, NULL, &tv); + if (FD_ISSET(ctrl->s, &rfds)) { + res = recv(ctrl->s, reply, *reply_len, 0); + if (res < 0) + return res; + if (res > 0 && reply[0] == '<') { + /* This is an unsolicited message from + * wpa_supplicant, not the reply to the + * request. Use msg_cb to report this to the + * caller. */ + if (msg_cb) { + /* Make sure the message is nul + * terminated. */ + if (res == *reply_len) + res = (*reply_len) - 1; + reply[res] = '\0'; + msg_cb(reply, res); + } + continue; + } + *reply_len = res; + break; + } else { + return -2; + } + } + return 0; +} + + +static int wpa_ctrl_attach_helper(struct wpa_ctrl *ctrl, int attach) +{ + char buf[10]; + int ret; + size_t len = 10; + + ret = wpa_ctrl_request(ctrl, attach ? "ATTACH" : "DETACH", 6, + buf, &len, NULL); + if (ret < 0) + return ret; + if (len == 3 && memcmp(buf, "OK\n", 3) == 0) + return 0; + return -1; +} + + +int wpa_ctrl_attach(struct wpa_ctrl *ctrl) +{ + return wpa_ctrl_attach_helper(ctrl, 1); +} + + +int wpa_ctrl_detach(struct wpa_ctrl *ctrl) +{ + return wpa_ctrl_attach_helper(ctrl, 0); +} + + +int wpa_ctrl_recv(struct wpa_ctrl *ctrl, char *reply, size_t *reply_len) +{ + int res; + + res = recv(ctrl->s, reply, *reply_len, 0); + if (res < 0) + return res; + *reply_len = res; + return 0; +} + + +int wpa_ctrl_pending(struct wpa_ctrl *ctrl) +{ + struct timeval tv; + int res; + fd_set rfds; + tv.tv_sec = 0; + tv.tv_usec = 0; + FD_ZERO(&rfds); + FD_SET(ctrl->s, &rfds); + res = select(ctrl->s + 1, &rfds, NULL, NULL, &tv); + return FD_ISSET(ctrl->s, &rfds); +} + + +int wpa_ctrl_get_fd(struct wpa_ctrl *ctrl) +{ + return ctrl->s; +} diff --git a/contrib/wpa_supplicant/wpa_ctrl.h b/contrib/wpa_supplicant/wpa_ctrl.h new file mode 100644 index 0000000..48b088a --- /dev/null +++ b/contrib/wpa_supplicant/wpa_ctrl.h @@ -0,0 +1,15 @@ +#ifndef WPA_CTRL_H +#define WPA_CTRL_H + +struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path); +void wpa_ctrl_close(struct wpa_ctrl *ctrl); +int wpa_ctrl_request(struct wpa_ctrl *ctrl, char *cmd, size_t cmd_len, + char *reply, size_t *reply_len, + void (*msg_cb)(char *msg, size_t len)); +int wpa_ctrl_attach(struct wpa_ctrl *ctrl); +int wpa_ctrl_detach(struct wpa_ctrl *ctrl); +int wpa_ctrl_recv(struct wpa_ctrl *ctrl, char *reply, size_t *reply_len); +int wpa_ctrl_pending(struct wpa_ctrl *ctrl); +int wpa_ctrl_get_fd(struct wpa_ctrl *ctrl); + +#endif /* WPA_CTRL_H */ diff --git a/contrib/wpa_supplicant/wpa_passphrase.c b/contrib/wpa_supplicant/wpa_passphrase.c new file mode 100644 index 0000000..5a8203b --- /dev/null +++ b/contrib/wpa_supplicant/wpa_passphrase.c @@ -0,0 +1,53 @@ +/* + * WPA Supplicant - ASCII passphrase to WPA PSK tool + * Copyright (c) 2003-2004, Jouni Malinen <jkmaline@cc.hut.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include <stdio.h> +#include <string.h> + +#include "common.h" +#include "sha1.h" + + +int main(int argc, char *argv[]) +{ + unsigned char psk[32]; + int i; + char *ssid, *passphrase; + + if (argc != 3) { + printf("usage: wpa_passphrase <ssid> <passphrase>\n"); + return 1; + } + + ssid = argv[1]; + passphrase = argv[2]; + + if (strlen(passphrase) < 8 || strlen(passphrase) > 63) { + printf("Passphrase must be 8..63 characters\n"); + return 1; + } + + pbkdf2_sha1(passphrase, ssid, strlen(ssid), 4096, psk, 32); + + printf("network={\n"); + printf("\tssid=\"%s\"\n", ssid); + printf("\t#psk=\"%s\"\n", passphrase); + printf("\tpsk="); + for (i = 0; i < 32; i++) + printf("%02x", psk[i]); + printf("\n"); + printf("}\n"); + + return 0; +} diff --git a/contrib/wpa_supplicant/wpa_supplicant.c b/contrib/wpa_supplicant/wpa_supplicant.c new file mode 100644 index 0000000..10f1212 --- /dev/null +++ b/contrib/wpa_supplicant/wpa_supplicant.c @@ -0,0 +1,2439 @@ +/* + * WPA Supplicant + * Copyright (c) 2003-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <stdarg.h> +#include <unistd.h> +#include <string.h> +#include <sys/time.h> +#include <time.h> +#include <signal.h> +#include <sys/types.h> +#ifndef CONFIG_NATIVE_WINDOWS +#include <sys/socket.h> +#include <sys/un.h> +#endif /* CONFIG_NATIVE_WINDOWS */ +#include <unistd.h> +#include <ctype.h> +#ifndef CONFIG_NATIVE_WINDOWS +#include <netinet/in.h> +#endif /* CONFIG_NATIVE_WINDOWS */ +#include <fcntl.h> + +#define OPENSSL_DISABLE_OLD_DES_SUPPORT +#include "common.h" +#include "eapol_sm.h" +#include "eap.h" +#include "wpa.h" +#include "driver.h" +#include "eloop.h" +#include "wpa_supplicant.h" +#include "config.h" +#include "l2_packet.h" +#include "wpa_supplicant_i.h" +#include "ctrl_iface.h" +#include "pcsc_funcs.h" +#include "version.h" + +static const char *wpa_supplicant_version = +"wpa_supplicant v" VERSION_STR "\n" +"Copyright (c) 2003-2005, Jouni Malinen <jkmaline@cc.hut.fi> and contributors"; + +static const char *wpa_supplicant_license = +"This program is free software. You can distribute it and/or modify it\n" +"under the terms of the GNU General Public License version 2.\n" +"\n" +"Alternatively, this software may be distributed under the terms of the\n" +"BSD license. See README and COPYING for more details.\n" +#ifdef EAP_TLS_FUNCS +"\nThis product includes software developed by the OpenSSL Project\n" +"for use in the OpenSSL Toolkit (http://www.openssl.org/)\n" +#endif /* EAP_TLS_FUNCS */ +; + +static const char *wpa_supplicant_full_license = +"This program is free software; you can redistribute it and/or modify\n" +"it under the terms of the GNU General Public License version 2 as\n" +"published by the Free Software Foundation.\n" +"\n" +"This program is distributed in the hope that it will be useful,\n" +"but WITHOUT ANY WARRANTY; without even the implied warranty of\n" +"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" +"GNU General Public License for more details.\n" +"\n" +"You should have received a copy of the GNU General Public License\n" +"along with this program; if not, write to the Free Software\n" +"Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\n" +"\n" +"Alternatively, this software may be distributed under the terms of the\n" +"BSD license.\n" +"\n" +"Redistribution and use in source and binary forms, with or without\n" +"modification, are permitted provided that the following conditions are\n" +"met:\n" +"\n" +"1. Redistributions of source code must retain the above copyright\n" +" notice, this list of conditions and the following disclaimer.\n" +"\n" +"2. Redistributions in binary form must reproduce the above copyright\n" +" notice, this list of conditions and the following disclaimer in the\n" +" documentation and/or other materials provided with the distribution.\n" +"\n" +"3. Neither the name(s) of the above-listed copyright holder(s) nor the\n" +" names of its contributors may be used to endorse or promote products\n" +" derived from this software without specific prior written permission.\n" +"\n" +"THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n" +"\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n" +"LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n" +"A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n" +"OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n" +"SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n" +"LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n" +"DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n" +"THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n" +"(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n" +"OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n" +"\n"; + +extern struct wpa_driver_ops *wpa_supplicant_drivers[]; + +static void wpa_supplicant_scan_results(struct wpa_supplicant *wpa_s); +static int wpa_supplicant_driver_init(struct wpa_supplicant *wpa_s, + int wait_for_interface); +static void wpa_supplicant_associate(struct wpa_supplicant *wpa_s, + struct wpa_scan_result *bss, + struct wpa_ssid *ssid); +static int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, + struct wpa_scan_result *bss, + struct wpa_ssid *ssid, + u8 *wpa_ie, int *wpa_ie_len); + + +extern int wpa_debug_level; +extern int wpa_debug_show_keys; +extern int wpa_debug_timestamp; + + +void wpa_msg(struct wpa_supplicant *wpa_s, int level, char *fmt, ...) +{ + va_list ap; + char *buf; + const int buflen = 2048; + int len; + + buf = malloc(buflen); + if (buf == NULL) { + printf("Failed to allocate message buffer for:\n"); + va_start(ap, fmt); + vprintf(fmt, ap); + printf("\n"); + va_end(ap); + return; + } + va_start(ap, fmt); + len = vsnprintf(buf, buflen, fmt, ap); + va_end(ap); + wpa_printf(level, "%s", buf); + wpa_supplicant_ctrl_iface_send(wpa_s, level, buf, len); + free(buf); +} + + +int wpa_eapol_send(void *ctx, int type, u8 *buf, size_t len) +{ + struct wpa_supplicant *wpa_s = ctx; + u8 *msg, *dst, bssid[ETH_ALEN]; + size_t msglen; + struct l2_ethhdr *ethhdr; + struct ieee802_1x_hdr *hdr; + int res; + + /* TODO: could add l2_packet_sendmsg that allows fragments to avoid + * extra copy here */ + + if (wpa_s->key_mgmt == WPA_KEY_MGMT_PSK || + wpa_s->key_mgmt == WPA_KEY_MGMT_NONE) { + /* Current SSID is not using IEEE 802.1X/EAP, so drop possible + * EAPOL frames (mainly, EAPOL-Start) from EAPOL state + * machines. */ + wpa_printf(MSG_DEBUG, "WPA: drop TX EAPOL in non-IEEE 802.1X " + "mode (type=%d len=%lu)", type, + (unsigned long) len); + return -1; + } + + if (wpa_s->cur_pmksa && type == IEEE802_1X_TYPE_EAPOL_START) { + /* Trying to use PMKSA caching - do not send EAPOL-Start frames + * since they will trigger full EAPOL authentication. */ + wpa_printf(MSG_DEBUG, "RSN: PMKSA caching - do not send " + "EAPOL-Start"); + return -1; + } + + if (memcmp(wpa_s->bssid, "\x00\x00\x00\x00\x00\x00", ETH_ALEN) == 0) { + wpa_printf(MSG_DEBUG, "BSSID not set when trying to send an " + "EAPOL frame"); + if (wpa_drv_get_bssid(wpa_s, bssid) == 0 && + memcmp(bssid, "\x00\x00\x00\x00\x00\x00", ETH_ALEN) != 0) { + dst = bssid; + wpa_printf(MSG_DEBUG, "Using current BSSID " MACSTR + " from the driver as the EAPOL destination", + MAC2STR(dst)); + } else { + dst = wpa_s->last_eapol_src; + wpa_printf(MSG_DEBUG, "Using the source address of the" + " last received EAPOL frame " MACSTR " as " + "the EAPOL destination", + MAC2STR(dst)); + } + } else { + /* BSSID was already set (from (Re)Assoc event, so use it as + * the EAPOL destination. */ + dst = wpa_s->bssid; + } + + msglen = sizeof(*ethhdr) + sizeof(*hdr) + len; + msg = malloc(msglen); + if (msg == NULL) + return -1; + + ethhdr = (struct l2_ethhdr *) msg; + memcpy(ethhdr->h_dest, dst, ETH_ALEN); + memcpy(ethhdr->h_source, wpa_s->own_addr, ETH_ALEN); + ethhdr->h_proto = htons(ETH_P_EAPOL); + + hdr = (struct ieee802_1x_hdr *) (ethhdr + 1); + hdr->version = wpa_s->conf->eapol_version; + hdr->type = type; + hdr->length = htons(len); + + memcpy((u8 *) (hdr + 1), buf, len); + + wpa_hexdump(MSG_MSGDUMP, "TX EAPOL", msg, msglen); + res = l2_packet_send(wpa_s->l2, msg, msglen); + free(msg); + return res; +} + + +int wpa_eapol_send_preauth(void *ctx, int type, u8 *buf, size_t len) +{ + struct wpa_supplicant *wpa_s = ctx; + u8 *msg; + size_t msglen; + struct l2_ethhdr *ethhdr; + struct ieee802_1x_hdr *hdr; + int res; + + /* TODO: could add l2_packet_sendmsg that allows fragments to avoid + * extra copy here */ + + if (wpa_s->l2_preauth == NULL) + return -1; + + msglen = sizeof(*ethhdr) + sizeof(*hdr) + len; + msg = malloc(msglen); + if (msg == NULL) + return -1; + + ethhdr = (struct l2_ethhdr *) msg; + memcpy(ethhdr->h_dest, wpa_s->preauth_bssid, ETH_ALEN); + memcpy(ethhdr->h_source, wpa_s->own_addr, ETH_ALEN); + ethhdr->h_proto = htons(ETH_P_RSN_PREAUTH); + + hdr = (struct ieee802_1x_hdr *) (ethhdr + 1); + hdr->version = wpa_s->conf->eapol_version; + hdr->type = type; + hdr->length = htons(len); + + memcpy((u8 *) (hdr + 1), buf, len); + + wpa_hexdump(MSG_MSGDUMP, "TX EAPOL (preauth)", msg, msglen); + res = l2_packet_send(wpa_s->l2_preauth, msg, msglen); + free(msg); + return res; +} + + +/** + * wpa_eapol_set_wep_key - set WEP key for the driver + * @ctx: pointer to wpa_supplicant data + * @unicast: 1 = individual unicast key, 0 = broadcast key + * @keyidx: WEP key index (0..3) + * @key: pointer to key data + * @keylen: key length in bytes + * + * Returns 0 on success or < 0 on error. + */ +static int wpa_eapol_set_wep_key(void *ctx, int unicast, int keyidx, + u8 *key, size_t keylen) +{ + struct wpa_supplicant *wpa_s = ctx; + wpa_s->keys_cleared = 0; + return wpa_drv_set_key(wpa_s, WPA_ALG_WEP, + unicast ? wpa_s->bssid : + (u8 *) "\xff\xff\xff\xff\xff\xff", + keyidx, unicast, (u8 *) "", 0, key, keylen); +} + + +/* Configure default/group WEP key for static WEP */ +static int wpa_set_wep_key(void *ctx, int set_tx, int keyidx, const u8 *key, + size_t keylen) +{ + struct wpa_supplicant *wpa_s = ctx; + wpa_s->keys_cleared = 0; + return wpa_drv_set_key(wpa_s, WPA_ALG_WEP, + (u8 *) "\xff\xff\xff\xff\xff\xff", + keyidx, set_tx, (u8 *) "", 0, key, keylen); +} + + +static int wpa_supplicant_set_wpa_none_key(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid) +{ + u8 key[32]; + size_t keylen; + wpa_alg alg; + u8 seq[6] = { 0 }; + + /* IBSS/WPA-None uses only one key (Group) for both receiving and + * sending unicast and multicast packets. */ + + if (ssid->mode != IEEE80211_MODE_IBSS) { + wpa_printf(MSG_INFO, "WPA: Invalid mode %d (not IBSS/ad-hoc) " + "for WPA-None", ssid->mode); + return -1; + } + + if (!ssid->psk_set) { + wpa_printf(MSG_INFO, "WPA: No PSK configured for WPA-None"); + return -1; + } + + switch (wpa_s->group_cipher) { + case WPA_CIPHER_CCMP: + memcpy(key, ssid->psk, 16); + keylen = 16; + alg = WPA_ALG_CCMP; + break; + case WPA_CIPHER_TKIP: + /* WPA-None uses the same Michael MIC key for both TX and RX */ + memcpy(key, ssid->psk, 16 + 8); + memcpy(key + 16 + 8, ssid->psk + 16, 8); + keylen = 32; + alg = WPA_ALG_TKIP; + break; + default: + wpa_printf(MSG_INFO, "WPA: Invalid group cipher %d for " + "WPA-None", wpa_s->group_cipher); + return -1; + } + + /* TODO: should actually remember the previously used seq#, both for TX + * and RX from each STA.. */ + + return wpa_drv_set_key(wpa_s, alg, (u8 *) "\xff\xff\xff\xff\xff\xff", + 0, 1, seq, 6, key, keylen); +} + + +void wpa_supplicant_notify_eapol_done(void *ctx) +{ + struct wpa_supplicant *wpa_s = ctx; + wpa_msg(wpa_s, MSG_DEBUG, "WPA: EAPOL processing complete"); + eloop_cancel_timeout(wpa_supplicant_scan, wpa_s, NULL); + wpa_supplicant_cancel_auth_timeout(wpa_s); +} + + +static int wpa_blacklisted(struct wpa_supplicant *wpa_s, const u8 *bssid) +{ + struct wpa_blacklist *e; + + e = wpa_s->blacklist; + while (e) { + if (memcmp(e->bssid, bssid, ETH_ALEN) == 0) + return 1; + e = e->next; + } + + return 0; +} + + +static int wpa_blacklist_add(struct wpa_supplicant *wpa_s, const u8 *bssid) +{ + struct wpa_blacklist *e; + + if (wpa_blacklisted(wpa_s, bssid)) + return 0; + + e = malloc(sizeof(*e)); + if (e == NULL) + return -1; + memset(e, 0, sizeof(*e)); + memcpy(e->bssid, bssid, ETH_ALEN); + e->next = wpa_s->blacklist; + wpa_s->blacklist = e; + wpa_printf(MSG_DEBUG, "Added BSSID " MACSTR " into blacklist", + MAC2STR(bssid)); + + return 0; +} + + +static int wpa_blacklist_del(struct wpa_supplicant *wpa_s, const u8 *bssid) +{ + struct wpa_blacklist *e, *prev = NULL; + + e = wpa_s->blacklist; + while (e) { + if (memcmp(e->bssid, bssid, ETH_ALEN) == 0) { + if (prev == NULL) { + wpa_s->blacklist = e->next; + } else { + prev->next = e->next; + } + wpa_printf(MSG_DEBUG, "Removed BSSID " MACSTR " from " + "blacklist", MAC2STR(bssid)); + free(e); + return 0; + } + prev = e; + e = e->next; + } + return -1; +} + + +static void wpa_blacklist_clear(struct wpa_supplicant *wpa_s) +{ + struct wpa_blacklist *e, *prev; + + e = wpa_s->blacklist; + wpa_s->blacklist = NULL; + while (e) { + prev = e; + e = e->next; + wpa_printf(MSG_DEBUG, "Removed BSSID " MACSTR " from " + "blacklist (clear)", MAC2STR(prev->bssid)); + free(prev); + } +} + + +const char * wpa_ssid_txt(u8 *ssid, size_t ssid_len) +{ + static char ssid_txt[MAX_SSID_LEN + 1]; + char *pos; + + if (ssid_len > MAX_SSID_LEN) + ssid_len = MAX_SSID_LEN; + memcpy(ssid_txt, ssid, ssid_len); + ssid_txt[ssid_len] = '\0'; + for (pos = ssid_txt; *pos != '\0'; pos++) { + if ((u8) *pos < 32 || (u8) *pos >= 127) + *pos = '_'; + } + return ssid_txt; +} + + +void wpa_supplicant_req_scan(struct wpa_supplicant *wpa_s, int sec, int usec) +{ + wpa_msg(wpa_s, MSG_DEBUG, "Setting scan request: %d sec %d usec", + sec, usec); + eloop_cancel_timeout(wpa_supplicant_scan, wpa_s, NULL); + eloop_register_timeout(sec, usec, wpa_supplicant_scan, wpa_s, NULL); +} + + +void wpa_supplicant_cancel_scan(struct wpa_supplicant *wpa_s) +{ + wpa_msg(wpa_s, MSG_DEBUG, "Cancelling scan request"); + eloop_cancel_timeout(wpa_supplicant_scan, wpa_s, NULL); +} + + +static void wpa_supplicant_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + wpa_msg(wpa_s, MSG_INFO, "Authentication with " MACSTR " timed out.", + MAC2STR(wpa_s->bssid)); + wpa_blacklist_add(wpa_s, wpa_s->bssid); + wpa_supplicant_disassociate(wpa_s, REASON_DEAUTH_LEAVING); + wpa_s->reassociate = 1; + wpa_supplicant_req_scan(wpa_s, 0, 0); +} + + +void wpa_supplicant_req_auth_timeout(struct wpa_supplicant *wpa_s, + int sec, int usec) +{ + wpa_msg(wpa_s, MSG_DEBUG, "Setting authentication timeout: %d sec " + "%d usec", sec, usec); + eloop_cancel_timeout(wpa_supplicant_timeout, wpa_s, NULL); + eloop_register_timeout(sec, usec, wpa_supplicant_timeout, wpa_s, NULL); +} + + +void wpa_supplicant_cancel_auth_timeout(struct wpa_supplicant *wpa_s) +{ + wpa_msg(wpa_s, MSG_DEBUG, "Cancelling authentication timeout"); + eloop_cancel_timeout(wpa_supplicant_timeout, wpa_s, NULL); + wpa_blacklist_del(wpa_s, wpa_s->bssid); +} + + +static void wpa_supplicant_initiate_eapol(struct wpa_supplicant *wpa_s) +{ + struct eapol_config eapol_conf; + struct wpa_ssid *ssid = wpa_s->current_ssid; + + if (wpa_s->key_mgmt == WPA_KEY_MGMT_PSK) { + eapol_sm_notify_eap_success(wpa_s->eapol, FALSE); + eapol_sm_notify_eap_fail(wpa_s->eapol, FALSE); + } + if (wpa_s->key_mgmt == WPA_KEY_MGMT_NONE || + wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE) + eapol_sm_notify_portControl(wpa_s->eapol, ForceAuthorized); + else + eapol_sm_notify_portControl(wpa_s->eapol, Auto); + + memset(&eapol_conf, 0, sizeof(eapol_conf)); + if (wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA) { + eapol_conf.accept_802_1x_keys = 1; + eapol_conf.required_keys = 0; + if (ssid->eapol_flags & EAPOL_FLAG_REQUIRE_KEY_UNICAST) { + eapol_conf.required_keys |= EAPOL_REQUIRE_KEY_UNICAST; + } + if (ssid->eapol_flags & EAPOL_FLAG_REQUIRE_KEY_BROADCAST) { + eapol_conf.required_keys |= + EAPOL_REQUIRE_KEY_BROADCAST; + } + } + eapol_conf.fast_reauth = wpa_s->conf->fast_reauth; + eapol_conf.workaround = ssid->eap_workaround; + eapol_sm_notify_config(wpa_s->eapol, ssid, &eapol_conf); +} + + +static void wpa_supplicant_set_non_wpa_policy(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid) +{ + int i; + + if (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA) + wpa_s->key_mgmt = WPA_KEY_MGMT_IEEE8021X_NO_WPA; + else + wpa_s->key_mgmt = WPA_KEY_MGMT_NONE; + free(wpa_s->ap_wpa_ie); + wpa_s->ap_wpa_ie = NULL; + wpa_s->ap_wpa_ie_len = 0; + free(wpa_s->ap_rsn_ie); + wpa_s->ap_rsn_ie = NULL; + wpa_s->ap_rsn_ie_len = 0; + free(wpa_s->assoc_wpa_ie); + wpa_s->assoc_wpa_ie = NULL; + wpa_s->assoc_wpa_ie_len = 0; + wpa_s->pairwise_cipher = WPA_CIPHER_NONE; + wpa_s->group_cipher = WPA_CIPHER_NONE; + + for (i = 0; i < NUM_WEP_KEYS; i++) { + if (ssid->wep_key_len[i] > 5) { + wpa_s->pairwise_cipher = WPA_CIPHER_WEP104; + wpa_s->group_cipher = WPA_CIPHER_WEP104; + break; + } else if (ssid->wep_key_len[i] > 0) { + wpa_s->pairwise_cipher = WPA_CIPHER_WEP40; + wpa_s->group_cipher = WPA_CIPHER_WEP40; + break; + } + } + + wpa_s->cur_pmksa = NULL; +} + + +static int wpa_supplicant_select_config(struct wpa_supplicant *wpa_s) +{ + struct wpa_ssid *ssid; + + if (wpa_s->conf->ap_scan == 1) + return 0; + + ssid = wpa_supplicant_get_ssid(wpa_s); + if (ssid == NULL) { + wpa_printf(MSG_INFO, "No network configuration found for the " + "current AP"); + return -1; + } + + wpa_printf(MSG_DEBUG, "Network configuration found for the current " + "AP"); + if (ssid->key_mgmt & (WPA_KEY_MGMT_PSK | WPA_KEY_MGMT_IEEE8021X | + WPA_KEY_MGMT_WPA_NONE)) { + u8 wpa_ie[80]; + int wpa_ie_len; + wpa_supplicant_set_suites(wpa_s, NULL, ssid, + wpa_ie, &wpa_ie_len); + } else { + wpa_supplicant_set_non_wpa_policy(wpa_s, ssid); + } + + wpa_s->current_ssid = ssid; + wpa_supplicant_initiate_eapol(wpa_s); + + return 0; +} + + +static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s) +{ + scard_deinit(wpa_s->scard); + wpa_s->scard = NULL; + eapol_sm_register_scard_ctx(wpa_s->eapol, NULL); + l2_packet_deinit(wpa_s->l2); + wpa_s->l2 = NULL; + +#ifdef CONFIG_XSUPPLICANT_IFACE + if (wpa_s->dot1x_s > -1) { + close(wpa_s->dot1x_s); + wpa_s->dot1x_s = -1; + } +#endif /* CONFIG_XSUPPLICANT_IFACE */ + + wpa_supplicant_ctrl_iface_deinit(wpa_s); + if (wpa_s->conf != NULL) { + wpa_config_free(wpa_s->conf); + wpa_s->conf = NULL; + } + + free(wpa_s->assoc_wpa_ie); + wpa_s->assoc_wpa_ie = NULL; + + free(wpa_s->ap_wpa_ie); + wpa_s->ap_wpa_ie = NULL; + free(wpa_s->ap_rsn_ie); + wpa_s->ap_rsn_ie = NULL; + + free(wpa_s->confname); + wpa_s->confname = NULL; + + eapol_sm_deinit(wpa_s->eapol); + wpa_s->eapol = NULL; + + rsn_preauth_deinit(wpa_s); + + pmksa_candidate_free(wpa_s); + pmksa_cache_free(wpa_s); + wpa_blacklist_clear(wpa_s); + + free(wpa_s->scan_results); + wpa_s->scan_results = NULL; + wpa_s->num_scan_results = 0; +} + + +static void wpa_clear_keys(struct wpa_supplicant *wpa_s, u8 *addr) +{ + u8 *bcast = (u8 *) "\xff\xff\xff\xff\xff\xff"; + + if (wpa_s->keys_cleared) { + /* Some drivers (e.g., ndiswrapper & NDIS drivers) seem to have + * timing issues with keys being cleared just before new keys + * are set or just after association or something similar. This + * shows up in group key handshake failing often because of the + * client not receiving the first encrypted packets correctly. + * Skipping some of the extra key clearing steps seems to help + * in completing group key handshake more reliably. */ + wpa_printf(MSG_DEBUG, "No keys have been configured - " + "skip key clearing"); + return; + } + + wpa_drv_set_key(wpa_s, WPA_ALG_NONE, bcast, 0, 0, NULL, 0, NULL, 0); + wpa_drv_set_key(wpa_s, WPA_ALG_NONE, bcast, 1, 0, NULL, 0, NULL, 0); + wpa_drv_set_key(wpa_s, WPA_ALG_NONE, bcast, 2, 0, NULL, 0, NULL, 0); + wpa_drv_set_key(wpa_s, WPA_ALG_NONE, bcast, 3, 0, NULL, 0, NULL, 0); + if (addr) { + wpa_drv_set_key(wpa_s, WPA_ALG_NONE, addr, 0, 0, NULL, 0, NULL, + 0); + } + wpa_s->keys_cleared = 1; +} + + +static void wpa_supplicant_stop_countermeasures(void *eloop_ctx, + void *sock_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + + if (wpa_s->countermeasures) { + wpa_s->countermeasures = 0; + wpa_drv_set_countermeasures(wpa_s, 0); + wpa_msg(wpa_s, MSG_INFO, "WPA: TKIP countermeasures stopped"); + wpa_supplicant_req_scan(wpa_s, 0, 0); + } +} + + +static void wpa_supplicant_mark_disassoc(struct wpa_supplicant *wpa_s) +{ + wpa_s->wpa_state = WPA_DISCONNECTED; + memset(wpa_s->bssid, 0, ETH_ALEN); + eapol_sm_notify_portEnabled(wpa_s->eapol, FALSE); + eapol_sm_notify_portValid(wpa_s->eapol, FALSE); + if (wpa_s->key_mgmt == WPA_KEY_MGMT_PSK) + eapol_sm_notify_eap_success(wpa_s->eapol, FALSE); +} + + +static void wpa_find_assoc_pmkid(struct wpa_supplicant *wpa_s) +{ + struct wpa_ie_data ie; + int i; + + if (wpa_parse_wpa_ie(wpa_s, wpa_s->assoc_wpa_ie, + wpa_s->assoc_wpa_ie_len, &ie) < 0 || + ie.pmkid == NULL) + return; + + for (i = 0; i < ie.num_pmkid; i++) { + wpa_s->cur_pmksa = pmksa_cache_get(wpa_s, NULL, + ie.pmkid + i * PMKID_LEN); + if (wpa_s->cur_pmksa) { + eapol_sm_notify_pmkid_attempt(wpa_s->eapol, 1); + break; + } + } + + wpa_printf(MSG_DEBUG, "RSN: PMKID from assoc IE %sfound from PMKSA " + "cache", wpa_s->cur_pmksa ? "" : "not "); +} + + +static void wpa_supplicant_add_pmkid_candidate(struct wpa_supplicant *wpa_s, + union wpa_event_data *data) +{ + if (data == NULL) { + wpa_printf(MSG_DEBUG, "RSN: No data in PMKID candidate event"); + return; + } + wpa_printf(MSG_DEBUG, "RSN: PMKID candidate event - bssid=" MACSTR + " index=%d preauth=%d", + MAC2STR(data->pmkid_candidate.bssid), + data->pmkid_candidate.index, + data->pmkid_candidate.preauth); + + if (!data->pmkid_candidate.preauth) { + wpa_printf(MSG_DEBUG, "RSN: Ignored PMKID candidate without " + "preauth flag"); + return; + } + + pmksa_candidate_add(wpa_s, data->pmkid_candidate.bssid, + data->pmkid_candidate.index); +} + + +static int wpa_supplicant_dynamic_keys(struct wpa_supplicant *wpa_s) +{ + if (wpa_s->key_mgmt == WPA_KEY_MGMT_NONE || + wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE) + return 0; + + if (wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA && + wpa_s->current_ssid && + !(wpa_s->current_ssid->eapol_flags & + (EAPOL_FLAG_REQUIRE_KEY_UNICAST | + EAPOL_FLAG_REQUIRE_KEY_BROADCAST))) { + /* IEEE 802.1X, but not using dynamic WEP keys (i.e., either + * plaintext or static WEP keys). */ + return 0; + } + + return 1; +} + + +static void wpa_supplicant_associnfo(struct wpa_supplicant *wpa_s, + union wpa_event_data *data) +{ + int l, len; + u8 *p; + + wpa_printf(MSG_DEBUG, "Association info event"); + wpa_hexdump(MSG_DEBUG, "req_ies", data->assoc_info.req_ies, + data->assoc_info.req_ies_len); + wpa_hexdump(MSG_DEBUG, "resp_ies", data->assoc_info.resp_ies, + data->assoc_info.resp_ies_len); + if (wpa_s->assoc_wpa_ie) { + free(wpa_s->assoc_wpa_ie); + wpa_s->assoc_wpa_ie = NULL; + wpa_s->assoc_wpa_ie_len = 0; + } + + p = data->assoc_info.req_ies; + l = data->assoc_info.req_ies_len; + + /* Go through the IEs and make a copy of the WPA/RSN IE, if present. */ + while (l >= 2) { + len = p[1] + 2; + if (len > l) { + wpa_hexdump(MSG_DEBUG, "Truncated IE in assoc_info", + p, l); + break; + } + if ((p[0] == GENERIC_INFO_ELEM && p[1] >= 6 && + (memcmp(&p[2], "\x00\x50\xF2\x01\x01\x00", 6) == 0)) || + (p[0] == RSN_INFO_ELEM && p[1] >= 2)) { + wpa_s->assoc_wpa_ie = malloc(len); + if (wpa_s->assoc_wpa_ie == NULL) + break; + wpa_s->assoc_wpa_ie_len = len; + memcpy(wpa_s->assoc_wpa_ie, p, len); + wpa_hexdump(MSG_DEBUG, "assoc_wpa_ie", + wpa_s->assoc_wpa_ie, + wpa_s->assoc_wpa_ie_len); + wpa_find_assoc_pmkid(wpa_s); + break; + } + l -= len; + p += len; + } + + /* WPA/RSN IE from Beacon/ProbeResp */ + free(wpa_s->ap_wpa_ie); + wpa_s->ap_wpa_ie = NULL; + wpa_s->ap_wpa_ie_len = 0; + free(wpa_s->ap_rsn_ie); + wpa_s->ap_rsn_ie = NULL; + wpa_s->ap_rsn_ie_len = 0; + + p = data->assoc_info.beacon_ies; + l = data->assoc_info.beacon_ies_len; + + /* Go through the IEs and make a copy of the WPA/RSN IEs, if present. + */ + while (l >= 2) { + len = p[1] + 2; + if (len > l) { + wpa_hexdump(MSG_DEBUG, "Truncated IE in beacon_ies", + p, l); + break; + } + if (wpa_s->ap_wpa_ie == NULL && + p[0] == GENERIC_INFO_ELEM && p[1] >= 6 && + memcmp(&p[2], "\x00\x50\xF2\x01\x01\x00", 6) == 0) { + wpa_s->ap_wpa_ie = malloc(len); + if (wpa_s->ap_wpa_ie) { + memcpy(wpa_s->ap_wpa_ie, p, len); + wpa_s->ap_wpa_ie_len = len; + } + } + + if (wpa_s->ap_rsn_ie == NULL && + p[0] == RSN_INFO_ELEM && p[1] >= 2) { + wpa_s->ap_rsn_ie = malloc(len); + if (wpa_s->ap_rsn_ie) { + memcpy(wpa_s->ap_rsn_ie, p, len); + wpa_s->ap_rsn_ie_len = len; + } + + } + + l -= len; + p += len; + } + +} + + +void wpa_supplicant_event(struct wpa_supplicant *wpa_s, wpa_event_type event, + union wpa_event_data *data) +{ + int pairwise; + time_t now; + u8 bssid[ETH_ALEN]; + + switch (event) { + case EVENT_ASSOC: + wpa_s->wpa_state = WPA_ASSOCIATED; + wpa_printf(MSG_DEBUG, "Association event - clear replay " + "counter"); + memset(wpa_s->rx_replay_counter, 0, WPA_REPLAY_COUNTER_LEN); + wpa_s->rx_replay_counter_set = 0; + wpa_s->renew_snonce = 1; + if (wpa_drv_get_bssid(wpa_s, bssid) >= 0 && + memcmp(bssid, wpa_s->bssid, ETH_ALEN) != 0) { + wpa_msg(wpa_s, MSG_DEBUG, "Associated to a new BSS: " + "BSSID=" MACSTR, MAC2STR(bssid)); + memcpy(wpa_s->bssid, bssid, ETH_ALEN); + if (wpa_supplicant_dynamic_keys(wpa_s)) { + wpa_clear_keys(wpa_s, bssid); + } + wpa_supplicant_select_config(wpa_s); + } + wpa_msg(wpa_s, MSG_INFO, "Associated with " MACSTR, + MAC2STR(bssid)); + /* Set portEnabled first to FALSE in order to get EAP state + * machine out of the SUCCESS state and eapSuccess cleared. + * Without this, EAPOL PAE state machine may transit to + * AUTHENTICATING state based on obsolete eapSuccess and then + * trigger BE_AUTH to SUCCESS and PAE to AUTHENTICATED without + * ever giving chance to EAP state machine to reset the state. + */ + eapol_sm_notify_portEnabled(wpa_s->eapol, FALSE); + eapol_sm_notify_portValid(wpa_s->eapol, FALSE); + if (wpa_s->key_mgmt == WPA_KEY_MGMT_PSK) + eapol_sm_notify_eap_success(wpa_s->eapol, FALSE); + /* 802.1X::portControl = Auto */ + eapol_sm_notify_portEnabled(wpa_s->eapol, TRUE); + wpa_s->eapol_received = 0; + if (wpa_s->key_mgmt == WPA_KEY_MGMT_NONE || + wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE) { + wpa_supplicant_cancel_auth_timeout(wpa_s); + } else { + /* Timeout for receiving the first EAPOL packet */ + wpa_supplicant_req_auth_timeout(wpa_s, 10, 0); + } + break; + case EVENT_DISASSOC: + if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE) { + /* At least Host AP driver and a Prism3 card seemed to + * be generating streams of disconnected events when + * configuring IBSS for WPA-None. Ignore them for now. + */ + wpa_printf(MSG_DEBUG, "Disconnect event - ignore in " + "IBSS/WPA-None mode"); + break; + } + if (wpa_s->wpa_state >= WPA_ASSOCIATED) + wpa_supplicant_req_scan(wpa_s, 0, 100000); + wpa_blacklist_add(wpa_s, wpa_s->bssid); + wpa_supplicant_mark_disassoc(wpa_s); + wpa_msg(wpa_s, MSG_INFO, "Disconnect event - remove keys"); + if (wpa_supplicant_dynamic_keys(wpa_s)) { + wpa_s->keys_cleared = 0; + wpa_clear_keys(wpa_s, wpa_s->bssid); + } + break; + case EVENT_MICHAEL_MIC_FAILURE: + wpa_msg(wpa_s, MSG_WARNING, "Michael MIC failure detected"); + pairwise = (data && data->michael_mic_failure.unicast); + wpa_supplicant_key_request(wpa_s, 1, pairwise); + time(&now); + if (wpa_s->last_michael_mic_error && + now - wpa_s->last_michael_mic_error <= 60) { + /* initialize countermeasures */ + wpa_s->countermeasures = 1; + wpa_msg(wpa_s, MSG_WARNING, "TKIP countermeasures " + "started"); + + /* Need to wait for completion of request frame. We do + * not get any callback for the message completion, so + * just wait a short while and hope for the best. */ + usleep(10000); + + wpa_drv_set_countermeasures(wpa_s, 1); + wpa_supplicant_deauthenticate( + wpa_s, REASON_MICHAEL_MIC_FAILURE); + eloop_cancel_timeout( + wpa_supplicant_stop_countermeasures, wpa_s, + NULL); + eloop_register_timeout( + 60, 0, wpa_supplicant_stop_countermeasures, + wpa_s, NULL); + /* TODO: mark the AP rejected for 60 second. STA is + * allowed to associate with another AP.. */ + } + wpa_s->last_michael_mic_error = now; + break; + case EVENT_SCAN_RESULTS: + wpa_supplicant_scan_results(wpa_s); + break; + case EVENT_ASSOCINFO: + wpa_supplicant_associnfo(wpa_s, data); + break; + case EVENT_INTERFACE_STATUS: + if (strcmp(wpa_s->ifname, data->interface_status.ifname) != 0) + break; + switch (data->interface_status.ievent) { + case EVENT_INTERFACE_ADDED: + if (!wpa_s->interface_removed) + break; + wpa_s->interface_removed = 0; + wpa_printf(MSG_DEBUG, "Configured interface was " + "added."); + if (wpa_supplicant_driver_init(wpa_s, 1) < 0) { + wpa_printf(MSG_INFO, "Failed to initialize " + "the driver after interface was " + "added."); + } + break; + case EVENT_INTERFACE_REMOVED: + wpa_printf(MSG_DEBUG, "Configured interface was " + "removed."); + wpa_s->interface_removed = 1; + wpa_supplicant_mark_disassoc(wpa_s); + l2_packet_deinit(wpa_s->l2); + break; + } + break; + case EVENT_PMKID_CANDIDATE: + wpa_supplicant_add_pmkid_candidate(wpa_s, data); + break; + default: + wpa_printf(MSG_INFO, "Unknown event %d", event); + break; + } +} + + +static void wpa_supplicant_terminate(int sig, void *eloop_ctx, + void *signal_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + for (wpa_s = wpa_s->head; wpa_s; wpa_s = wpa_s->next) { + wpa_msg(wpa_s, MSG_INFO, "Signal %d received - terminating", + sig); + } + eloop_terminate(); +} + + +int wpa_supplicant_reload_configuration(struct wpa_supplicant *wpa_s) +{ + struct wpa_config *conf; + int reconf_ctrl; + if (wpa_s->confname == NULL) + return -1; + conf = wpa_config_read(wpa_s->confname); + if (conf == NULL) { + wpa_msg(wpa_s, MSG_ERROR, "Failed to parse the configuration " + "file '%s' - exiting", wpa_s->confname); + return -1; + } + + reconf_ctrl = !!conf->ctrl_interface != !!wpa_s->conf->ctrl_interface + || (conf->ctrl_interface && wpa_s->conf->ctrl_interface && + strcmp(conf->ctrl_interface, wpa_s->conf->ctrl_interface) + != 0); + + if (reconf_ctrl) + wpa_supplicant_ctrl_iface_deinit(wpa_s); + + wpa_s->current_ssid = NULL; + eapol_sm_notify_config(wpa_s->eapol, NULL, NULL); + rsn_preauth_deinit(wpa_s); + wpa_config_free(wpa_s->conf); + wpa_s->conf = conf; + if (reconf_ctrl) + wpa_supplicant_ctrl_iface_init(wpa_s); + wpa_s->reassociate = 1; + wpa_supplicant_req_scan(wpa_s, 0, 0); + wpa_msg(wpa_s, MSG_DEBUG, "Reconfiguration completed"); + return 0; +} + + +#ifndef CONFIG_NATIVE_WINDOWS +static void wpa_supplicant_reconfig(int sig, void *eloop_ctx, + void *signal_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + wpa_printf(MSG_DEBUG, "Signal %d received - reconfiguring", sig); + for (wpa_s = wpa_s->head; wpa_s; wpa_s = wpa_s->next) { + if (wpa_supplicant_reload_configuration(wpa_s) < 0) { + eloop_terminate(); + } + } +} +#endif /* CONFIG_NATIVE_WINDOWS */ + + +static void wpa_supplicant_gen_assoc_event(struct wpa_supplicant *wpa_s) +{ + struct wpa_ssid *ssid; + union wpa_event_data data; + + ssid = wpa_supplicant_get_ssid(wpa_s); + if (ssid == NULL) + return; + + wpa_printf(MSG_DEBUG, "Already associated with a configured network - " + "generating associated event"); + memset(&data, 0, sizeof(data)); + wpa_supplicant_event(wpa_s, EVENT_ASSOC, &data); +} + + +void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + struct wpa_ssid *ssid; + + if (wpa_s->conf->ap_scan == 0) { + wpa_supplicant_gen_assoc_event(wpa_s); + return; + } + + if (wpa_s->conf->ap_scan == 2) { + ssid = wpa_s->conf->ssid; + if (ssid == NULL) + return; + wpa_supplicant_associate(wpa_s, NULL, ssid); + return; + } + + if (wpa_s->wpa_state == WPA_DISCONNECTED) + wpa_s->wpa_state = WPA_SCANNING; + + ssid = wpa_s->conf->ssid; + if (wpa_s->prev_scan_ssid != BROADCAST_SSID_SCAN) { + while (ssid) { + if (ssid == wpa_s->prev_scan_ssid) { + ssid = ssid->next; + break; + } + ssid = ssid->next; + } + } + while (ssid) { + if (ssid->scan_ssid) + break; + ssid = ssid->next; + } + + wpa_printf(MSG_DEBUG, "Starting AP scan (%s SSID)", + ssid ? "specific": "broadcast"); + if (ssid) { + wpa_hexdump_ascii(MSG_DEBUG, "Scan SSID", + ssid->ssid, ssid->ssid_len); + wpa_s->prev_scan_ssid = ssid; + } else + wpa_s->prev_scan_ssid = BROADCAST_SSID_SCAN; + + if (wpa_drv_scan(wpa_s, ssid ? ssid->ssid : NULL, + ssid ? ssid->ssid_len : 0)) { + wpa_printf(MSG_WARNING, "Failed to initiate AP scan."); + wpa_supplicant_req_scan(wpa_s, 10, 0); + } +} + + +static wpa_cipher cipher_suite2driver(int cipher) +{ + switch (cipher) { + case WPA_CIPHER_NONE: + return CIPHER_NONE; + case WPA_CIPHER_WEP40: + return CIPHER_WEP40; + case WPA_CIPHER_WEP104: + return CIPHER_WEP104; + case WPA_CIPHER_CCMP: + return CIPHER_CCMP; + case WPA_CIPHER_TKIP: + default: + return CIPHER_TKIP; + } +} + + +static wpa_key_mgmt key_mgmt2driver(int key_mgmt) +{ + switch (key_mgmt) { + case WPA_KEY_MGMT_NONE: + return KEY_MGMT_NONE; + case WPA_KEY_MGMT_IEEE8021X_NO_WPA: + return KEY_MGMT_802_1X_NO_WPA; + case WPA_KEY_MGMT_IEEE8021X: + return KEY_MGMT_802_1X; + case WPA_KEY_MGMT_WPA_NONE: + return KEY_MGMT_WPA_NONE; + case WPA_KEY_MGMT_PSK: + default: + return KEY_MGMT_PSK; + } +} + + +static int wpa_supplicant_suites_from_ai(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid, + struct wpa_ie_data *ie) { + if (wpa_s->assoc_wpa_ie == NULL) + return -1; + + if (wpa_parse_wpa_ie(wpa_s, wpa_s->assoc_wpa_ie, + wpa_s->assoc_wpa_ie_len, ie)) { + wpa_msg(wpa_s, MSG_INFO, "WPA: Failed to parse WPA IE from " + "association info"); + return -1; + } + + wpa_printf(MSG_DEBUG, "WPA: Using WPA IE from AssocReq to set cipher " + "suites"); + if (!(ie->group_cipher & ssid->group_cipher)) { + wpa_msg(wpa_s, MSG_INFO, "WPA: Driver used disabled group " + "cipher 0x%x (mask 0x%x) - reject", + ie->group_cipher, ssid->group_cipher); + return -1; + } + if (!(ie->pairwise_cipher & ssid->pairwise_cipher)) { + wpa_msg(wpa_s, MSG_INFO, "WPA: Driver used disabled pairwise " + "cipher 0x%x (mask 0x%x) - reject", + ie->pairwise_cipher, ssid->pairwise_cipher); + return -1; + } + if (!(ie->key_mgmt & ssid->key_mgmt)) { + wpa_msg(wpa_s, MSG_INFO, "WPA: Driver used disabled key " + "management 0x%x (mask 0x%x) - reject", + ie->key_mgmt, ssid->key_mgmt); + return -1; + } + + return 0; +} + + +static int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, + struct wpa_scan_result *bss, + struct wpa_ssid *ssid, + u8 *wpa_ie, int *wpa_ie_len) +{ + struct wpa_ie_data ie; + int sel, proto; + u8 *ap_ie; + size_t ap_ie_len; + + if (bss && bss->rsn_ie_len && (ssid->proto & WPA_PROTO_RSN)) { + wpa_msg(wpa_s, MSG_DEBUG, "RSN: using IEEE 802.11i/D9.0"); + proto = WPA_PROTO_RSN; + ap_ie = bss->rsn_ie; + ap_ie_len = bss->rsn_ie_len; + } else if (bss) { + wpa_msg(wpa_s, MSG_DEBUG, "WPA: using IEEE 802.11i/D3.0"); + proto = WPA_PROTO_WPA; + ap_ie = bss->wpa_ie; + ap_ie_len = bss->wpa_ie_len; + } else { + if (ssid->proto & WPA_PROTO_RSN) + proto = WPA_PROTO_RSN; + else + proto = WPA_PROTO_WPA; + ap_ie = NULL; + ap_ie_len = 0; + if (wpa_supplicant_suites_from_ai(wpa_s, ssid, &ie) < 0) { + memset(&ie, 0, sizeof(ie)); + ie.group_cipher = ssid->group_cipher; + ie.pairwise_cipher = ssid->pairwise_cipher; + ie.key_mgmt = ssid->key_mgmt; + wpa_printf(MSG_DEBUG, "WPA: Set cipher suites based " + "on configuration"); + } + } + + if (ap_ie && wpa_parse_wpa_ie(wpa_s, ap_ie, ap_ie_len, &ie)) { + wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to parse WPA IE for " + "the selected BSS."); + return -1; + } + wpa_printf(MSG_DEBUG, "WPA: Selected cipher suites: group %d " + "pairwise %d key_mgmt %d", + ie.group_cipher, ie.pairwise_cipher, ie.key_mgmt); + + wpa_s->proto = proto; + + free(wpa_s->ap_wpa_ie); + wpa_s->ap_wpa_ie = NULL; + wpa_s->ap_wpa_ie_len = 0; + if (bss && bss->wpa_ie_len) { + wpa_s->ap_wpa_ie = malloc(bss->wpa_ie_len); + if (wpa_s->ap_wpa_ie == NULL) { + wpa_printf(MSG_INFO, "WPA: malloc failed"); + return -1; + } + memcpy(wpa_s->ap_wpa_ie, bss->wpa_ie, bss->wpa_ie_len); + wpa_s->ap_wpa_ie_len = bss->wpa_ie_len; + } + + free(wpa_s->ap_rsn_ie); + wpa_s->ap_rsn_ie = NULL; + wpa_s->ap_rsn_ie_len = 0; + if (bss && bss->rsn_ie_len) { + wpa_s->ap_rsn_ie = malloc(bss->rsn_ie_len); + if (wpa_s->ap_rsn_ie == NULL) { + wpa_printf(MSG_INFO, "WPA: malloc failed"); + return -1; + } + memcpy(wpa_s->ap_rsn_ie, bss->rsn_ie, bss->rsn_ie_len); + wpa_s->ap_rsn_ie_len = bss->rsn_ie_len; + } + + sel = ie.group_cipher & ssid->group_cipher; + if (sel & WPA_CIPHER_CCMP) { + wpa_s->group_cipher = WPA_CIPHER_CCMP; + wpa_msg(wpa_s, MSG_DEBUG, "WPA: using GTK CCMP"); + } else if (sel & WPA_CIPHER_TKIP) { + wpa_s->group_cipher = WPA_CIPHER_TKIP; + wpa_msg(wpa_s, MSG_DEBUG, "WPA: using GTK TKIP"); + } else if (sel & WPA_CIPHER_WEP104) { + wpa_s->group_cipher = WPA_CIPHER_WEP104; + wpa_msg(wpa_s, MSG_DEBUG, "WPA: using GTK WEP104"); + } else if (sel & WPA_CIPHER_WEP40) { + wpa_s->group_cipher = WPA_CIPHER_WEP40; + wpa_msg(wpa_s, MSG_DEBUG, "WPA: using GTK WEP40"); + } else { + wpa_printf(MSG_WARNING, "WPA: Failed to select group cipher."); + return -1; + } + + sel = ie.pairwise_cipher & ssid->pairwise_cipher; + if (sel & WPA_CIPHER_CCMP) { + wpa_s->pairwise_cipher = WPA_CIPHER_CCMP; + wpa_msg(wpa_s, MSG_DEBUG, "WPA: using PTK CCMP"); + } else if (sel & WPA_CIPHER_TKIP) { + wpa_s->pairwise_cipher = WPA_CIPHER_TKIP; + wpa_msg(wpa_s, MSG_DEBUG, "WPA: using PTK TKIP"); + } else if (sel & WPA_CIPHER_NONE) { + wpa_s->pairwise_cipher = WPA_CIPHER_NONE; + wpa_msg(wpa_s, MSG_DEBUG, "WPA: using PTK NONE"); + } else { + wpa_printf(MSG_WARNING, "WPA: Failed to select pairwise " + "cipher."); + return -1; + } + + sel = ie.key_mgmt & ssid->key_mgmt; + if (sel & WPA_KEY_MGMT_IEEE8021X) { + wpa_s->key_mgmt = WPA_KEY_MGMT_IEEE8021X; + wpa_msg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT 802.1X"); + } else if (sel & WPA_KEY_MGMT_PSK) { + wpa_s->key_mgmt = WPA_KEY_MGMT_PSK; + wpa_msg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT WPA-PSK"); + } else if (sel & WPA_KEY_MGMT_WPA_NONE) { + wpa_s->key_mgmt = WPA_KEY_MGMT_WPA_NONE; + wpa_msg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT WPA-NONE"); + } else { + wpa_printf(MSG_WARNING, "WPA: Failed to select authenticated " + "key management type."); + return -1; + } + + *wpa_ie_len = wpa_gen_wpa_ie(wpa_s, wpa_ie); + if (*wpa_ie_len < 0) { + wpa_printf(MSG_WARNING, "WPA: Failed to generate WPA IE."); + return -1; + } + wpa_hexdump(MSG_DEBUG, "WPA: Own WPA IE", wpa_ie, *wpa_ie_len); + + if (ssid->key_mgmt & WPA_KEY_MGMT_PSK) { + wpa_s->pmk_len = PMK_LEN; + memcpy(wpa_s->pmk, ssid->psk, PMK_LEN); + } else if (wpa_s->cur_pmksa) { + wpa_s->pmk_len = wpa_s->cur_pmksa->pmk_len; + memcpy(wpa_s->pmk, wpa_s->cur_pmksa->pmk, wpa_s->pmk_len); + } else { + wpa_s->pmk_len = PMK_LEN; + memset(wpa_s->pmk, 0, PMK_LEN); +#ifdef CONFIG_XSUPPLICANT_IFACE + wpa_s->ext_pmk_received = 0; +#endif /* CONFIG_XSUPPLICANT_IFACE */ + } + + return 0; +} + + +static void wpa_supplicant_associate(struct wpa_supplicant *wpa_s, + struct wpa_scan_result *bss, + struct wpa_ssid *ssid) +{ + u8 wpa_ie[80]; + int wpa_ie_len; + int use_crypt; + int algs = AUTH_ALG_OPEN_SYSTEM; + int cipher_pairwise, cipher_group; + struct wpa_driver_associate_params params; + int wep_keys_set = 0; + struct wpa_driver_capa capa; + + wpa_s->reassociate = 0; + if (bss) { + wpa_msg(wpa_s, MSG_INFO, "Trying to associate with " MACSTR + " (SSID='%s' freq=%d MHz)", MAC2STR(bss->bssid), + wpa_ssid_txt(ssid->ssid, ssid->ssid_len), bss->freq); + memset(wpa_s->bssid, 0, ETH_ALEN); + } else { + wpa_msg(wpa_s, MSG_INFO, "Trying to associate with SSID '%s'", + wpa_ssid_txt(ssid->ssid, ssid->ssid_len)); + } + wpa_supplicant_cancel_scan(wpa_s); + + /* Starting new association, so clear the possibly used WPA IE from the + * previous association. */ + free(wpa_s->assoc_wpa_ie); + wpa_s->assoc_wpa_ie = NULL; + wpa_s->assoc_wpa_ie_len = 0; + + if (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA) { + if (ssid->leap) { + if (ssid->non_leap == 0) + algs = AUTH_ALG_LEAP; + else + algs |= AUTH_ALG_LEAP; + } + } + wpa_printf(MSG_DEBUG, "Automatic auth_alg selection: 0x%x", algs); + if (ssid->auth_alg) { + algs = 0; + if (ssid->auth_alg & WPA_AUTH_ALG_OPEN) + algs |= AUTH_ALG_OPEN_SYSTEM; + if (ssid->auth_alg & WPA_AUTH_ALG_SHARED) + algs |= AUTH_ALG_SHARED_KEY; + if (ssid->auth_alg & WPA_AUTH_ALG_LEAP) + algs |= AUTH_ALG_LEAP; + wpa_printf(MSG_DEBUG, "Overriding auth_alg selection: 0x%x", + algs); + } + wpa_drv_set_auth_alg(wpa_s, algs); + + if (bss && (bss->wpa_ie_len || bss->rsn_ie_len) && + (ssid->key_mgmt & (WPA_KEY_MGMT_IEEE8021X | WPA_KEY_MGMT_PSK))) { + wpa_s->cur_pmksa = pmksa_cache_get(wpa_s, bss->bssid, NULL); + if (wpa_s->cur_pmksa) { + wpa_hexdump(MSG_DEBUG, "RSN: PMKID", + wpa_s->cur_pmksa->pmkid, PMKID_LEN); + eapol_sm_notify_pmkid_attempt(wpa_s->eapol, 1); + } + if (wpa_supplicant_set_suites(wpa_s, bss, ssid, + wpa_ie, &wpa_ie_len)) { + wpa_printf(MSG_WARNING, "WPA: Failed to set WPA key " + "management and encryption suites"); + return; + } + } else if (ssid->key_mgmt & + (WPA_KEY_MGMT_PSK | WPA_KEY_MGMT_IEEE8021X | + WPA_KEY_MGMT_WPA_NONE)) { + if (wpa_supplicant_set_suites(wpa_s, NULL, ssid, + wpa_ie, &wpa_ie_len)) { + wpa_printf(MSG_WARNING, "WPA: Failed to set WPA key " + "management and encryption suites (no scan " + "results)"); + return; + } + } else { + wpa_supplicant_set_non_wpa_policy(wpa_s, ssid); + wpa_ie_len = 0; + } + + wpa_clear_keys(wpa_s, bss ? bss->bssid : NULL); + use_crypt = 1; + cipher_pairwise = cipher_suite2driver(wpa_s->pairwise_cipher); + cipher_group = cipher_suite2driver(wpa_s->group_cipher); + if (wpa_s->key_mgmt == WPA_KEY_MGMT_NONE || + wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA) { + int i; + if (wpa_s->key_mgmt == WPA_KEY_MGMT_NONE) + use_crypt = 0; + for (i = 0; i < NUM_WEP_KEYS; i++) { + if (ssid->wep_key_len[i]) { + use_crypt = 1; + wep_keys_set = 1; + wpa_set_wep_key(wpa_s, + i == ssid->wep_tx_keyidx, + i, ssid->wep_key[i], + ssid->wep_key_len[i]); + } + } + } + + if (wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA) { + if ((ssid->eapol_flags & + (EAPOL_FLAG_REQUIRE_KEY_UNICAST | + EAPOL_FLAG_REQUIRE_KEY_BROADCAST)) == 0 && + !wep_keys_set) { + use_crypt = 0; + } else { + /* Assume that dynamic WEP-104 keys will be used and + * set cipher suites in order for drivers to expect + * encryption. */ + cipher_pairwise = cipher_group = CIPHER_WEP104; + } + } + + if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE) { + /* Set the key before (and later after) association */ + wpa_supplicant_set_wpa_none_key(wpa_s, ssid); + } + + wpa_drv_set_drop_unencrypted(wpa_s, use_crypt); + wpa_s->wpa_state = WPA_ASSOCIATING; + memset(¶ms, 0, sizeof(params)); + if (bss) { + params.bssid = bss->bssid; + params.ssid = bss->ssid; + params.ssid_len = bss->ssid_len; + params.freq = bss->freq; + } else { + params.ssid = ssid->ssid; + params.ssid_len = ssid->ssid_len; + } + params.wpa_ie = wpa_ie; + params.wpa_ie_len = wpa_ie_len; + params.pairwise_suite = cipher_pairwise; + params.group_suite = cipher_group; + params.key_mgmt_suite = key_mgmt2driver(wpa_s->key_mgmt); + params.auth_alg = algs; + params.mode = ssid->mode; + if (wpa_drv_associate(wpa_s, ¶ms) < 0) { + wpa_msg(wpa_s, MSG_INFO, "Association request to the driver " + "failed"); + /* try to continue anyway; new association will be tried again + * after timeout */ + } + + if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE) { + /* Set the key after the association just in case association + * cleared the previously configured key. */ + wpa_supplicant_set_wpa_none_key(wpa_s, ssid); + /* No need to timeout authentication since there is no key + * management. */ + wpa_supplicant_cancel_auth_timeout(wpa_s); + } else { + /* Timeout for IEEE 802.11 authentication and association */ + wpa_supplicant_req_auth_timeout(wpa_s, 5, 0); + } + + if (wep_keys_set && wpa_drv_get_capa(wpa_s, &capa) == 0 && + capa.flags & WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC) { + /* Set static WEP keys again */ + int i; + for (i = 0; i < NUM_WEP_KEYS; i++) { + if (ssid->wep_key_len[i]) { + wpa_set_wep_key(wpa_s, + i == ssid->wep_tx_keyidx, + i, ssid->wep_key[i], + ssid->wep_key_len[i]); + } + } + } + + wpa_s->current_ssid = ssid; + wpa_supplicant_initiate_eapol(wpa_s); +} + + +void wpa_supplicant_disassociate(struct wpa_supplicant *wpa_s, + int reason_code) +{ + u8 *addr = NULL; + wpa_s->wpa_state = WPA_DISCONNECTED; + if (memcmp(wpa_s->bssid, "\x00\x00\x00\x00\x00\x00", ETH_ALEN) != 0) { + wpa_drv_disassociate(wpa_s, wpa_s->bssid, reason_code); + addr = wpa_s->bssid; + } + wpa_clear_keys(wpa_s, addr); + wpa_s->current_ssid = NULL; + eapol_sm_notify_config(wpa_s->eapol, NULL, NULL); + eapol_sm_notify_portEnabled(wpa_s->eapol, FALSE); + eapol_sm_notify_portValid(wpa_s->eapol, FALSE); +} + + +void wpa_supplicant_deauthenticate(struct wpa_supplicant *wpa_s, + int reason_code) +{ + u8 *addr = NULL; + wpa_s->wpa_state = WPA_DISCONNECTED; + if (memcmp(wpa_s->bssid, "\x00\x00\x00\x00\x00\x00", ETH_ALEN) != 0) { + wpa_drv_deauthenticate(wpa_s, wpa_s->bssid, reason_code); + addr = wpa_s->bssid; + } + wpa_clear_keys(wpa_s, addr); + wpa_s->current_ssid = NULL; + eapol_sm_notify_config(wpa_s->eapol, NULL, NULL); + eapol_sm_notify_portEnabled(wpa_s->eapol, FALSE); + eapol_sm_notify_portValid(wpa_s->eapol, FALSE); +} + + +static void wpa_supplicant_imsi_identity(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid) +{ + int aka = 0; + u8 *pos = ssid->eap_methods; + + while (pos && *pos != EAP_TYPE_NONE) { + if (*pos == EAP_TYPE_AKA) { + aka = 1; + break; + } + pos++; + } + + if (ssid->identity == NULL && wpa_s->imsi) { + ssid->identity = malloc(1 + wpa_s->imsi_len); + if (ssid->identity) { + ssid->identity[0] = aka ? '0' : '1'; + memcpy(ssid->identity + 1, wpa_s->imsi, + wpa_s->imsi_len); + ssid->identity_len = 1 + wpa_s->imsi_len; + wpa_hexdump_ascii(MSG_DEBUG, "permanent identity from " + "IMSI", ssid->identity, + ssid->identity_len); + } + } +} + + +static void wpa_supplicant_scard_init(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid) +{ + char buf[100]; + size_t len; + + if (ssid->pcsc == NULL) + return; + if (wpa_s->scard != NULL) { + wpa_supplicant_imsi_identity(wpa_s, ssid); + return; + } + wpa_printf(MSG_DEBUG, "Selected network is configured to use SIM - " + "initialize PCSC"); + wpa_s->scard = scard_init(SCARD_TRY_BOTH, ssid->pin); + if (wpa_s->scard == NULL) { + wpa_printf(MSG_WARNING, "Failed to initialize SIM " + "(pcsc-lite)"); + /* TODO: what to do here? */ + return; + } + eapol_sm_register_scard_ctx(wpa_s->eapol, wpa_s->scard); + + len = sizeof(buf); + if (scard_get_imsi(wpa_s->scard, buf, &len)) { + wpa_printf(MSG_WARNING, "Failed to get IMSI from SIM"); + /* TODO: what to do here? */ + return; + } + + wpa_hexdump_ascii(MSG_DEBUG, "IMSI", (u8 *) buf, len); + free(wpa_s->imsi); + wpa_s->imsi = malloc(len); + if (wpa_s->imsi) { + memcpy(wpa_s->imsi, buf, len); + wpa_s->imsi_len = len; + wpa_supplicant_imsi_identity(wpa_s, ssid); + } +} + + +static struct wpa_scan_result * +wpa_supplicant_select_bss(struct wpa_supplicant *wpa_s, struct wpa_ssid *group, + struct wpa_scan_result *results, int num, + struct wpa_ssid **selected_ssid) +{ + struct wpa_ssid *ssid; + struct wpa_scan_result *bss, *selected = NULL; + int i; + + wpa_printf(MSG_DEBUG, "Selecting BSS from priority group %d", + group->priority); + + bss = NULL; + ssid = NULL; + /* First, try to find WPA-enabled AP */ + for (i = 0; i < num && !selected; i++) { + bss = &results[i]; + wpa_printf(MSG_DEBUG, "%d: " MACSTR " ssid='%s' " + "wpa_ie_len=%lu rsn_ie_len=%lu", + i, MAC2STR(bss->bssid), + wpa_ssid_txt(bss->ssid, bss->ssid_len), + (unsigned long) bss->wpa_ie_len, + (unsigned long) bss->rsn_ie_len); + if (wpa_blacklisted(wpa_s, bss->bssid)) { + wpa_printf(MSG_DEBUG, " skip - blacklisted"); + continue; + } + + if (bss->wpa_ie_len == 0 && bss->rsn_ie_len == 0) { + wpa_printf(MSG_DEBUG, " skip - no WPA/RSN IE"); + continue; + } + + for (ssid = group; ssid; ssid = ssid->pnext) { + struct wpa_ie_data ie; + if (bss->ssid_len != ssid->ssid_len || + memcmp(bss->ssid, ssid->ssid, + bss->ssid_len) != 0) { + wpa_printf(MSG_DEBUG, " skip - " + "SSID mismatch"); + continue; + } + if (ssid->bssid_set && + memcmp(bss->bssid, ssid->bssid, ETH_ALEN) != 0) { + wpa_printf(MSG_DEBUG, " skip - " + "BSSID mismatch"); + continue; + } + if (!(((ssid->proto & WPA_PROTO_RSN) && + wpa_parse_wpa_ie(wpa_s, bss->rsn_ie, + bss->rsn_ie_len, &ie) == 0) || + ((ssid->proto & WPA_PROTO_WPA) && + wpa_parse_wpa_ie(wpa_s, bss->wpa_ie, + bss->wpa_ie_len, &ie) == 0))) { + wpa_printf(MSG_DEBUG, " skip - " + "could not parse WPA/RSN IE"); + continue; + } + if (!(ie.proto & ssid->proto)) { + wpa_printf(MSG_DEBUG, " skip - " + "proto mismatch"); + continue; + } + if (!(ie.pairwise_cipher & ssid->pairwise_cipher)) { + wpa_printf(MSG_DEBUG, " skip - " + "PTK cipher mismatch"); + continue; + } + if (!(ie.group_cipher & ssid->group_cipher)) { + wpa_printf(MSG_DEBUG, " skip - " + "GTK cipher mismatch"); + continue; + } + if (!(ie.key_mgmt & ssid->key_mgmt)) { + wpa_printf(MSG_DEBUG, " skip - " + "key mgmt mismatch"); + continue; + } + + selected = bss; + *selected_ssid = ssid; + wpa_printf(MSG_DEBUG, " selected"); + break; + } + } + + /* If no WPA-enabled AP found, try to find non-WPA AP, if configuration + * allows this. */ + for (i = 0; i < num && !selected; i++) { + bss = &results[i]; + if (wpa_blacklisted(wpa_s, bss->bssid)) { + continue; + } + for (ssid = group; ssid; ssid = ssid->pnext) { + if (bss->ssid_len == ssid->ssid_len && + memcmp(bss->ssid, ssid->ssid, bss->ssid_len) == 0 + && + (!ssid->bssid_set || + memcmp(bss->bssid, ssid->bssid, ETH_ALEN) == 0) && + ((ssid->key_mgmt & WPA_KEY_MGMT_NONE) || + (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA))) + { + selected = bss; + *selected_ssid = ssid; + wpa_printf(MSG_DEBUG, " selected non-WPA AP " + MACSTR " ssid='%s'", + MAC2STR(bss->bssid), + wpa_ssid_txt(bss->ssid, + bss->ssid_len)); + break; + } + } + } + + return selected; +} + + +static int wpa_supplicant_get_scan_results(struct wpa_supplicant *wpa_s) +{ +#define SCAN_AP_LIMIT 128 + struct wpa_scan_result *results, *tmp; + int num; + + results = malloc(SCAN_AP_LIMIT * sizeof(struct wpa_scan_result)); + if (results == NULL) { + wpa_printf(MSG_WARNING, "Failed to allocate memory for scan " + "results"); + return -1; + } + + num = wpa_drv_get_scan_results(wpa_s, results, SCAN_AP_LIMIT); + wpa_printf(MSG_DEBUG, "Scan results: %d", num); + if (num < 0) { + wpa_printf(MSG_DEBUG, "Failed to get scan results"); + free(results); + return -1; + } + if (num > SCAN_AP_LIMIT) { + wpa_printf(MSG_INFO, "Not enough room for all APs (%d < %d)", + num, SCAN_AP_LIMIT); + num = SCAN_AP_LIMIT; + } + + /* Free unneeded memory for unused scan result entries */ + tmp = realloc(results, num * sizeof(struct wpa_scan_result)); + if (tmp || num == 0) { + results = tmp; + } + + free(wpa_s->scan_results); + wpa_s->scan_results = results; + wpa_s->num_scan_results = num; + + return 0; +} + + +static void wpa_supplicant_scan_results(struct wpa_supplicant *wpa_s) +{ + int num, prio; + struct wpa_scan_result *selected = NULL; + struct wpa_ssid *ssid; + struct wpa_scan_result *results; + + if (wpa_supplicant_get_scan_results(wpa_s) < 0) { + wpa_printf(MSG_DEBUG, "Failed to get scan results - try " + "scanning again"); + wpa_supplicant_req_scan(wpa_s, 1, 0); + return; + } + results = wpa_s->scan_results; + num = wpa_s->num_scan_results; + + while (selected == NULL) { + for (prio = 0; prio < wpa_s->conf->num_prio; prio++) { + selected = wpa_supplicant_select_bss( + wpa_s, wpa_s->conf->pssid[prio], results, num, + &ssid); + if (selected) + break; + } + + if (selected == NULL && wpa_s->blacklist) { + wpa_printf(MSG_DEBUG, "No APs found - clear blacklist " + "and try again"); + wpa_blacklist_clear(wpa_s); + } else if (selected == NULL) { + break; + } + } + + if (selected) { + if (wpa_s->reassociate || + memcmp(selected->bssid, wpa_s->bssid, ETH_ALEN) != 0) { + wpa_supplicant_scard_init(wpa_s, ssid); + wpa_supplicant_associate(wpa_s, selected, ssid); + } else { + wpa_printf(MSG_DEBUG, "Already associated with the " + "selected AP."); + } + rsn_preauth_scan_results(wpa_s, results, num); + } else { + wpa_printf(MSG_DEBUG, "No suitable AP found."); + wpa_supplicant_req_scan(wpa_s, 5, 0); + } +} + + +static int wpa_get_beacon_ie(struct wpa_supplicant *wpa_s) +{ + int i, ret = 0; + struct wpa_scan_result *results, *curr = NULL; + + results = wpa_s->scan_results; + if (results == NULL) { + return -1; + } + + for (i = 0; i < wpa_s->num_scan_results; i++) { + if (memcmp(results[i].bssid, wpa_s->bssid, ETH_ALEN) == 0) { + curr = &results[i]; + break; + } + } + + if (curr) { + free(wpa_s->ap_wpa_ie); + wpa_s->ap_wpa_ie_len = curr->wpa_ie_len; + if (curr->wpa_ie_len) { + wpa_s->ap_wpa_ie = malloc(wpa_s->ap_wpa_ie_len); + if (wpa_s->ap_wpa_ie) { + memcpy(wpa_s->ap_wpa_ie, curr->wpa_ie, + curr->wpa_ie_len); + } else { + ret = -1; + } + } else { + wpa_s->ap_wpa_ie = NULL; + } + + free(wpa_s->ap_rsn_ie); + wpa_s->ap_rsn_ie_len = curr->rsn_ie_len; + if (curr->rsn_ie_len) { + wpa_s->ap_rsn_ie = malloc(wpa_s->ap_rsn_ie_len); + if (wpa_s->ap_rsn_ie) { + memcpy(wpa_s->ap_rsn_ie, curr->rsn_ie, + curr->rsn_ie_len); + } else { + ret = -1; + } + } else { + wpa_s->ap_rsn_ie = NULL; + } + } else { + ret = -1; + } + + return ret; +} + + +int wpa_supplicant_get_beacon_ie(struct wpa_supplicant *wpa_s) +{ + if (wpa_get_beacon_ie(wpa_s) == 0) { + return 0; + } + + /* No WPA/RSN IE found in the cached scan results. Try to get updated + * scan results from the driver. */ + if (wpa_supplicant_get_scan_results(wpa_s) < 0) { + return -1; + } + + return wpa_get_beacon_ie(wpa_s); +} + + +#ifdef CONFIG_XSUPPLICANT_IFACE +static void wpa_supplicant_dot1x_receive(int sock, void *eloop_ctx, + void *sock_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + u8 buf[128]; + int res; + + res = recv(sock, buf, sizeof(buf), 0); + wpa_printf(MSG_DEBUG, "WPA: Receive from dot1x (Xsupplicant) socket " + "==> %d", res); + if (res < 0) { + perror("recv"); + return; + } + + if (res != PMK_LEN) { + wpa_printf(MSG_WARNING, "WPA: Invalid master key length (%d) " + "from dot1x", res); + return; + } + + wpa_hexdump(MSG_DEBUG, "WPA: Master key (dot1x)", buf, PMK_LEN); + if (wpa_s->key_mgmt & WPA_KEY_MGMT_IEEE8021X) { + memcpy(wpa_s->pmk, buf, PMK_LEN); + wpa_s->ext_pmk_received = 1; + } else { + wpa_printf(MSG_INFO, "WPA: Not in IEEE 802.1X mode - dropping " + "dot1x PMK update (%d)", wpa_s->key_mgmt); + } +} + + +static int wpa_supplicant_802_1x_init(struct wpa_supplicant *wpa_s) +{ + int s; + struct sockaddr_un addr; + + s = socket(AF_LOCAL, SOCK_DGRAM, 0); + if (s < 0) { + perror("socket"); + return -1; + } + + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_LOCAL; + addr.sun_path[0] = '\0'; + snprintf(addr.sun_path + 1, sizeof(addr.sun_path) - 1, + "wpa_supplicant"); + if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + perror("bind"); + close(s); + return -1; + } + + wpa_s->dot1x_s = s; + eloop_register_read_sock(s, wpa_supplicant_dot1x_receive, wpa_s, + NULL); + return 0; +} +#endif /* CONFIG_XSUPPLICANT_IFACE */ + + +static int wpa_supplicant_set_driver(struct wpa_supplicant *wpa_s, + const char *name) +{ + int i; + + if (wpa_s == NULL) + return -1; + + if (wpa_supplicant_drivers[0] == NULL) { + wpa_printf(MSG_ERROR, "No driver interfaces build into " + "wpa_supplicant."); + return -1; + } + + if (name == NULL) { + /* default to first driver in the list */ + wpa_s->driver = wpa_supplicant_drivers[0]; + return 0; + } + + for (i = 0; wpa_supplicant_drivers[i]; i++) { + if (strcmp(name, wpa_supplicant_drivers[i]->name) == 0) { + wpa_s->driver = wpa_supplicant_drivers[i]; + return 0; + } + } + + printf("Unsupported driver '%s'.\n", name); + return -1; +} + + +static void wpa_supplicant_fd_workaround(void) +{ + int s, i; + /* When started from pcmcia-cs scripts, wpa_supplicant might start with + * fd 0, 1, and 2 closed. This will cause some issues because many + * places in wpa_supplicant are still printing out to stdout. As a + * workaround, make sure that fd's 0, 1, and 2 are not used for other + * sockets. */ + for (i = 0; i < 3; i++) { + s = open("/dev/null", O_RDWR); + if (s > 2) { + close(s); + break; + } + } +} + + +static int wpa_supplicant_driver_init(struct wpa_supplicant *wpa_s, + int wait_for_interface) +{ + static int interface_count = 0; + + for (;;) { + wpa_s->l2 = l2_packet_init(wpa_s->ifname, + wpa_drv_get_mac_addr(wpa_s), + ETH_P_EAPOL, + wpa_supplicant_rx_eapol, wpa_s); + if (wpa_s->l2) + break; + else if (!wait_for_interface) + return -1; + printf("Waiting for interface..\n"); + sleep(5); + } + + if (l2_packet_get_own_addr(wpa_s->l2, wpa_s->own_addr)) { + fprintf(stderr, "Failed to get own L2 address\n"); + return -1; + } + + wpa_printf(MSG_DEBUG, "Own MAC address: " MACSTR, + MAC2STR(wpa_s->own_addr)); + + if (wpa_drv_set_wpa(wpa_s, 1) < 0) { + fprintf(stderr, "Failed to enable WPA in the driver.\n"); + return -1; + } + + wpa_clear_keys(wpa_s, NULL); + + /* Make sure that TKIP countermeasures are not left enabled (could + * happen if wpa_supplicant is killed during countermeasures. */ + wpa_drv_set_countermeasures(wpa_s, 0); + + wpa_drv_set_drop_unencrypted(wpa_s, 1); + + wpa_s->prev_scan_ssid = BROADCAST_SSID_SCAN; + wpa_supplicant_req_scan(wpa_s, interface_count, 100000); + interface_count++; + + return 0; +} + + +static void usage(void) +{ + int i; + printf("%s\n\n%s\n" + "usage:\n" + " wpa_supplicant [-BddehLqqvw] -i<ifname> -c<config file> " + "[-D<driver>] \\\n" + " [-N -i<ifname> -c<conf> [-D<driver>] ...]\n" + "\n" + "drivers:\n", + wpa_supplicant_version, wpa_supplicant_license); + + for (i = 0; wpa_supplicant_drivers[i]; i++) { + printf(" %s = %s\n", + wpa_supplicant_drivers[i]->name, + wpa_supplicant_drivers[i]->desc); + } + + printf("options:\n" + " -B = run daemon in the background\n" + " -d = increase debugging verbosity (-dd even more)\n" + " -K = include keys (passwords, etc.) in debug output\n" + " -t = include timestamp in debug messages\n" +#ifdef CONFIG_XSUPPLICANT_IFACE +#ifdef IEEE8021X_EAPOL + " -e = use external IEEE 802.1X Supplicant (e.g., " + "xsupplicant)\n" + " (this disables the internal Supplicant)\n" +#endif /* IEEE8021X_EAPOL */ +#endif /* CONFIG_XSUPPLICANT_IFACE */ + " -h = show this help text\n" + " -L = show license (GPL and BSD)\n" + " -q = decrease debugging verbosity (-qq even less)\n" + " -v = show version\n" + " -w = wait for interface to be added, if needed\n" + " -N = start describing new interface\n"); +} + + +static void license(void) +{ + printf("%s\n\n%s\n", + wpa_supplicant_version, wpa_supplicant_full_license); +} + + +static struct wpa_supplicant * wpa_supplicant_alloc(void) +{ + struct wpa_supplicant *wpa_s; + + wpa_s = malloc(sizeof(*wpa_s)); + if (wpa_s == NULL) + return NULL; + memset(wpa_s, 0, sizeof(*wpa_s)); +#ifdef CONFIG_XSUPPLICANT_IFACE + wpa_s->dot1x_s = -1; +#endif /* CONFIG_XSUPPLICANT_IFACE */ + + return wpa_s; +} + + +static int wpa_supplicant_init(struct wpa_supplicant *wpa_s, + const char *confname, const char *driver, + const char *ifname) +{ + wpa_printf(MSG_DEBUG, "Initializing interface '%s' conf '%s' driver " + "'%s'", ifname, confname, driver ? driver : "default"); + + if (wpa_supplicant_set_driver(wpa_s, driver) < 0) { + return -1; + } + + if (confname) { + wpa_s->confname = rel2abs_path(confname); + if (wpa_s->confname == NULL) { + wpa_printf(MSG_ERROR, "Failed to get absolute path " + "for configuration file '%s'.", confname); + return -1; + } + wpa_printf(MSG_DEBUG, "Configuration file '%s' -> '%s'", + confname, wpa_s->confname); + wpa_s->conf = wpa_config_read(wpa_s->confname); + if (wpa_s->conf == NULL) { + printf("Failed to read configuration file '%s'.\n", + wpa_s->confname); + return -1; + } + } + + if (wpa_s->conf == NULL || wpa_s->conf->ssid == NULL) { + usage(); + printf("\nNo networks (SSID) configured.\n"); + return -1; + } + + if (ifname == NULL) { + usage(); + printf("\nInterface name is required.\n"); + return -1; + } + if (strlen(ifname) >= sizeof(wpa_s->ifname)) { + printf("Too long interface name '%s'.\n", ifname); + return -1; + } + strncpy(wpa_s->ifname, ifname, sizeof(wpa_s->ifname)); + + return 0; +} + + +static int wpa_supplicant_init2(struct wpa_supplicant *wpa_s, + int disable_eapol, int wait_for_interface) +{ + const char *ifname; + + wpa_printf(MSG_DEBUG, "Initializing interface (2) '%s'", + wpa_s->ifname); + + if (!disable_eapol) { + struct eapol_ctx *ctx; + ctx = malloc(sizeof(*ctx)); + if (ctx == NULL) { + printf("Failed to allocate EAPOL context.\n"); + return -1; + } + memset(ctx, 0, sizeof(*ctx)); + ctx->ctx = wpa_s; + ctx->msg_ctx = wpa_s; + ctx->preauth = 0; + ctx->eapol_done_cb = wpa_supplicant_notify_eapol_done; + ctx->eapol_send = wpa_eapol_send; + ctx->set_wep_key = wpa_eapol_set_wep_key; + wpa_s->eapol = eapol_sm_init(ctx); + if (wpa_s->eapol == NULL) { + free(ctx); + printf("Failed to initialize EAPOL state machines.\n"); + return -1; + } + } + + /* RSNA Supplicant Key Management - INITIALIZE */ + eapol_sm_notify_portEnabled(wpa_s->eapol, FALSE); + eapol_sm_notify_portValid(wpa_s->eapol, FALSE); + + /* Initialize driver interface and register driver event handler before + * L2 receive handler so that association events are processed before + * EAPOL-Key packets if both become available for the same select() + * call. */ + wpa_s->drv_priv = wpa_drv_init(wpa_s, wpa_s->ifname); + if (wpa_s->drv_priv == NULL) { + fprintf(stderr, "Failed to initialize driver interface\n"); + return -1; + } + + ifname = wpa_drv_get_ifname(wpa_s); + if (ifname && strcmp(ifname, wpa_s->ifname) != 0) { + wpa_printf(MSG_DEBUG, "Driver interface replaced interface " + "name with '%s'", ifname); + strncpy(wpa_s->ifname, ifname, sizeof(wpa_s->ifname)); + } + + wpa_s->renew_snonce = 1; + if (wpa_supplicant_driver_init(wpa_s, wait_for_interface) < 0) { + return -1; + } + + if (wpa_supplicant_ctrl_iface_init(wpa_s)) { + printf("Failed to initialize control interface '%s'.\n" + "You may have another wpa_supplicant process already " + "running or the file was\n" + "left by an unclean termination of wpa_supplicant in " + "which case you will need\n" + "to manually remove this file before starting " + "wpa_supplicant again.\n", + wpa_s->conf->ctrl_interface); + return -1; + } + +#ifdef CONFIG_XSUPPLICANT_IFACE + if (disable_eapol) + wpa_supplicant_802_1x_init(wpa_s); +#endif /* CONFIG_XSUPPLICANT_IFACE */ + + return 0; +} + + +static void wpa_supplicant_deinit(struct wpa_supplicant *wpa_s) +{ + if (wpa_s->drv_priv) { + if (wpa_drv_set_wpa(wpa_s, 0) < 0) { + fprintf(stderr, "Failed to disable WPA in the " + "driver.\n"); + } + + wpa_drv_set_drop_unencrypted(wpa_s, 0); + wpa_drv_set_countermeasures(wpa_s, 0); + + wpa_drv_deinit(wpa_s); + } + wpa_supplicant_cleanup(wpa_s); +} + + +int main(int argc, char *argv[]) +{ + struct wpa_supplicant *head, *wpa_s; + int c; + const char *confname, *driver, *ifname; + int daemonize = 0, wait_for_interface = 0, disable_eapol = 0, exitcode; + +#ifdef CONFIG_NATIVE_WINDOWS + WSADATA wsaData; + if (WSAStartup(MAKEWORD(2, 0), &wsaData)) { + printf("Could not find a usable WinSock.dll\n"); + return -1; + } +#endif /* CONFIG_NATIVE_WINDOWS */ + + head = wpa_s = wpa_supplicant_alloc(); + if (wpa_s == NULL) + return -1; + wpa_s->head = head; + + wpa_supplicant_fd_workaround(); + eloop_init(head); + + ifname = confname = driver = NULL; + + for (;;) { + c = getopt(argc, argv, "Bc:D:dehi:KLNqtvw"); + if (c < 0) + break; + switch (c) { + case 'B': + daemonize++; + break; + case 'c': + confname = optarg; + break; + case 'D': + driver = optarg; + break; + case 'd': + wpa_debug_level--; + break; +#ifdef CONFIG_XSUPPLICANT_IFACE +#ifdef IEEE8021X_EAPOL + case 'e': + disable_eapol++; + break; +#endif /* IEEE8021X_EAPOL */ +#endif /* CONFIG_XSUPPLICANT_IFACE */ + case 'h': + usage(); + return -1; + case 'i': + ifname = optarg; + break; + case 'K': + wpa_debug_show_keys++; + break; + case 'L': + license(); + return -1; + case 'q': + wpa_debug_level++; + break; + case 't': + wpa_debug_timestamp++; + break; + case 'v': + printf("%s\n", wpa_supplicant_version); + return -1; + case 'w': + wait_for_interface++; + break; + case 'N': + if (wpa_supplicant_init(wpa_s, confname, driver, + ifname)) + return -1; + wpa_s->next = wpa_supplicant_alloc(); + wpa_s = wpa_s->next; + if (wpa_s == NULL) + return -1; + wpa_s->head = head; + ifname = confname = driver = NULL; + break; + default: + usage(); + return -1; + } + } + + if (wpa_supplicant_init(wpa_s, confname, driver, ifname)) + return -1; + + exitcode = 0; + + if (wait_for_interface && daemonize) { + wpa_printf(MSG_DEBUG, "Daemonize.."); + if (daemon(0, 0)) { + perror("daemon"); + exitcode = -1; + goto cleanup; + } + } + + for (wpa_s = head; wpa_s; wpa_s = wpa_s->next) { + if (wpa_supplicant_init2(wpa_s, disable_eapol, + wait_for_interface)) { + exitcode = -1; + goto cleanup; + } + } + + if (!wait_for_interface && daemonize) { + wpa_printf(MSG_DEBUG, "Daemonize.."); + if (daemon(0, 0)) { + perror("daemon"); + exitcode = -1; + goto cleanup; + } + } + + eloop_register_signal(SIGINT, wpa_supplicant_terminate, NULL); + eloop_register_signal(SIGTERM, wpa_supplicant_terminate, NULL); +#ifndef CONFIG_NATIVE_WINDOWS + eloop_register_signal(SIGHUP, wpa_supplicant_reconfig, NULL); +#endif /* CONFIG_NATIVE_WINDOWS */ + + eloop_run(); + + for (wpa_s = head; wpa_s; wpa_s = wpa_s->next) { + wpa_supplicant_deauthenticate(wpa_s, REASON_DEAUTH_LEAVING); + } + +cleanup: + wpa_s = head; + while (wpa_s) { + struct wpa_supplicant *prev; + wpa_supplicant_deinit(wpa_s); + prev = wpa_s; + wpa_s = wpa_s->next; + free(prev); + } + + eloop_destroy(); + +#ifdef CONFIG_NATIVE_WINDOWS + WSACleanup(); +#endif /* CONFIG_NATIVE_WINDOWS */ + + return exitcode; +} diff --git a/contrib/wpa_supplicant/wpa_supplicant.conf b/contrib/wpa_supplicant/wpa_supplicant.conf new file mode 100644 index 0000000..e8be91a --- /dev/null +++ b/contrib/wpa_supplicant/wpa_supplicant.conf @@ -0,0 +1,505 @@ +##### Example wpa_supplicant configuration file ############################### +# Empty lines and lines starting with # are ignored + +# NOTE! This file may contain password information and should probably be made +# readable only by root user on multiuser systems. + +# global configuration (shared by all network blocks) +# +# Interface for separate control program. If this is specified, wpa_supplicant +# will create this directory and a UNIX domain socket for listening to requests +# from external programs (CLI/GUI, etc.) for status information and +# configuration. The socket file will be named based on the interface name, so +# multiple wpa_supplicant processes can be run at the same time if more than +# one interface is used. +# /var/run/wpa_supplicant is the recommended directory for sockets and by +# default, wpa_cli will use it when trying to connect with wpa_supplicant. +ctrl_interface=/var/run/wpa_supplicant + +# Access control for the control interface can be configured by setting the +# directory to allow only members of a group to use sockets. This way, it is +# possible to run wpa_supplicant as root (since it needs to change network +# configuration and open raw sockets) and still allow GUI/CLI components to be +# run as non-root users. However, since the control interface can be used to +# change the network configuration, this access needs to be protected in many +# cases. By default, wpa_supplicant is configured to use gid 0 (root). If you +# want to allow non-root users to use the control interface, add a new group +# and change this value to match with that group. Add users that should have +# control interface access to this group. If this variable is commented out or +# not included in the configuration file, group will not be changed from the +# value it got by default when the directory or socket was created. +# +# This variable can be a group name or gid. +#ctrl_interface_group=wheel +ctrl_interface_group=0 + +# IEEE 802.1X/EAPOL version +# wpa_supplicant was implemented based on IEEE 802-1X-REV-d8 which defines +# EAPOL version 2. However, there are many APs that do not handle the new +# version number correctly (they seem to drop the frames completely). In order +# to make wpa_supplicant interoperate with these APs, the version number is set +# to 1 by default. This configuration value can be used to set it to the new +# version (2). +eapol_version=1 + +# AP scanning/selection +# By default, wpa_supplicant requests driver to perform AP scanning and then +# uses the scan results to select a suitable AP. Another alternative is to +# allow the driver to take care of AP scanning and selection and use +# wpa_supplicant just to process EAPOL frames based on IEEE 802.11 association +# information from the driver. +# 1: wpa_supplicant initiates scanning and AP selection +# 0: driver takes care of scanning, AP selection, and IEEE 802.11 association +# parameters (e.g., WPA IE generation); this mode can also be used with +# non-WPA drivers when using IEEE 802.1X mode; do not try to associate with +# APs (i.e., external program needs to control association) +# 2: like 0, but associate with APs using security policy and SSID (but not +# BSSID); this can be used, e.g., with ndiswrapper and NDIS driver to +# enable operation with hidden SSIDs and optimized roaming; in this mode, +# only the first network block in the configuration file is used and this +# configuration should have explicit security policy (i.e., only one option +# in the lists) for key_mgmt, pairwise, group, proto variables +ap_scan=1 + +# EAP fast re-authentication +# By default, fast re-authentication is enabled for all EAP methods that +# support it. This variable can be used to disable fast re-authentication. +# Normally, there is no need to disable this. +fast_reauth=1 + +# network block +# +# Each network (usually AP's sharing the same SSID) is configured as a separate +# block in this configuration file. The network blocks are in preference order +# (the first match is used). +# +# network block fields: +# +# ssid: SSID (mandatory); either as an ASCII string with double quotation or +# as hex string; network name +# +# scan_ssid: +# 0 = do not scan this SSID with specific Probe Request frames (default) +# 1 = scan with SSID-specific Probe Request frames (this can be used to +# find APs that do not accept broadcast SSID or use multiple SSIDs; +# this will add latency to scanning, so enable this only when needed) +# +# bssid: BSSID (optional); if set, this network block is used only when +# associating with the AP using the configured BSSID +# +# priority: priority group (integer) +# By default, all networks will get same priority group (0). If some of the +# networks are more desirable, this field can be used to change the order in +# which wpa_supplicant goes through the networks when selecting a BSS. The +# priority groups will be iterated in decreasing priority (i.e., the larger the +# priority value, the sooner the network is matched against the scan results). +# Within each priority group, networks will be selected based on security +# policy, signal strength, etc. +# Please note that AP scanning with scan_ssid=1 is not using this priority to +# select the order for scanning. Instead, it uses the order the networks are in +# the configuration file. +# +# mode: IEEE 802.11 operation mode +# 0 = infrastructure (Managed) mode, i.e., associate with an AP (default) +# 1 = IBSS (ad-hoc, peer-to-peer) +# Note: IBSS can only be used with key_mgmt NONE (plaintext and static WEP) +# and key_mgmt=WPA-NONE (fixed group key TKIP/CCMP). In addition, ap_scan has +# to be set to 2 for IBSS. WPA-None requires following network block options: +# proto=WPA, key_mgmt=WPA-NONE, pairwise=NONE, group=TKIP (or CCMP, but not +# both), and psk must also be set. +# +# proto: list of accepted protocols +# WPA = WPA/IEEE 802.11i/D3.0 +# RSN = WPA2/IEEE 802.11i (also WPA2 can be used as an alias for RSN) +# If not set, this defaults to: WPA RSN +# +# key_mgmt: list of accepted authenticated key management protocols +# WPA-PSK = WPA pre-shared key (this requires 'psk' field) +# WPA-EAP = WPA using EAP authentication (this can use an external +# program, e.g., Xsupplicant, for IEEE 802.1X EAP Authentication +# IEEE8021X = IEEE 802.1X using EAP authentication and (optionally) dynamically +# generated WEP keys +# NONE = WPA is not used; plaintext or static WEP could be used +# If not set, this defaults to: WPA-PSK WPA-EAP +# +# auth_alg: list of allowed IEEE 802.11 authentication algorithms +# OPEN = Open System authentication (required for WPA/WPA2) +# SHARED = Shared Key authentication (requires static WEP keys) +# LEAP = LEAP/Network EAP (only used with LEAP) +# If not set, automatic selection is used (Open System with LEAP enabled if +# LEAP is allowed as one of the EAP methods). +# +# pairwise: list of accepted pairwise (unicast) ciphers for WPA +# CCMP = AES in Counter mode with CBC-MAC [RFC 3610, IEEE 802.11i/D7.0] +# TKIP = Temporal Key Integrity Protocol [IEEE 802.11i/D7.0] +# NONE = Use only Group Keys (deprecated, should not be included if APs support +# pairwise keys) +# If not set, this defaults to: CCMP TKIP +# +# group: list of accepted group (broadcast/multicast) ciphers for WPA +# CCMP = AES in Counter mode with CBC-MAC [RFC 3610, IEEE 802.11i/D7.0] +# TKIP = Temporal Key Integrity Protocol [IEEE 802.11i/D7.0] +# WEP104 = WEP (Wired Equivalent Privacy) with 104-bit key +# WEP40 = WEP (Wired Equivalent Privacy) with 40-bit key [IEEE 802.11] +# If not set, this defaults to: CCMP TKIP WEP104 WEP40 +# +# psk: WPA preshared key; 256-bit pre-shared key +# The key used in WPA-PSK mode can be entered either as 64 hex-digits, i.e., +# 32 bytes or as an ASCII passphrase (in which case, the real PSK will be +# generated using the passphrase and SSID). ASCII passphrase must be between +# 8 and 63 characters (inclusive). +# This field is not needed, if WPA-EAP is used. +# Note: Separate tool, wpa_passphrase, can be used to generate 256-bit keys +# from ASCII passphrase. This process uses lot of CPU and wpa_supplicant +# startup and reconfiguration time can be optimized by generating the PSK only +# only when the passphrase or SSID has actually changed. +# +# eapol_flags: IEEE 802.1X/EAPOL options (bit field) +# Dynamic WEP key require for non-WPA mode +# bit0 (1): require dynamically generated unicast WEP key +# bit1 (2): require dynamically generated broadcast WEP key +# (3 = require both keys; default) +# +# Following fields are only used with internal EAP implementation. +# eap: space-separated list of accepted EAP methods +# MD5 = EAP-MD5 (unsecure and does not generate keying material -> +# cannot be used with WPA; to be used as a Phase 2 method +# with EAP-PEAP or EAP-TTLS) +# MSCHAPV2 = EAP-MSCHAPv2 (cannot be used separately with WPA; to be used +# as a Phase 2 method with EAP-PEAP or EAP-TTLS) +# OTP = EAP-OTP (cannot be used separately with WPA; to be used +# as a Phase 2 method with EAP-PEAP or EAP-TTLS) +# GTC = EAP-GTC (cannot be used separately with WPA; to be used +# as a Phase 2 method with EAP-PEAP or EAP-TTLS) +# TLS = EAP-TLS (client and server certificate) +# PEAP = EAP-PEAP (with tunnelled EAP authentication) +# TTLS = EAP-TTLS (with tunnelled EAP or PAP/CHAP/MSCHAP/MSCHAPV2 +# authentication) +# If not set, all compiled in methods are allowed. +# +# identity: Identity string for EAP +# anonymous_identity: Anonymous identity string for EAP (to be used as the +# unencrypted identity with EAP types that support different tunnelled +# identity, e.g., EAP-TTLS) +# password: Password string for EAP +# ca_cert: File path to CA certificate file. This file can have one or more +# trusted CA certificates. If ca_cert is not included, server certificate +# will not be verified. This is insecure and the CA file should always be +# configured. +# client_cert: File path to client certificate file (PEM/DER) +# private_key: File path to client private key file (PEM/DER/PFX) +# When PKCS#12/PFX file (.p12/.pfx) is used, client_cert should be +# commented out. Both the private key and certificate will be read from +# the PKCS#12 file in this case. +# private_key_passwd: Password for private key file +# dh_file: File path to DH/DSA parameters file (in PEM format) +# This is an optional configuration file for setting parameters for an +# ephemeral DH key exchange. In most cases, the default RSA +# authentication does not use this configuration. However, it is possible +# setup RSA to use ephemeral DH key exchange. In addition, ciphers with +# DSA keys always use ephemeral DH keys. This can be used to achieve +# forward secrecy. If the file is in DSA parameters format, it will be +# automatically converted into DH params. +# subject_match: Substring to be matched against the subject of the +# authentication server certificate. If this string is set, the server +# sertificate is only accepted if it contains this string in the subject. +# The subject string is in following format: +# /C=US/ST=CA/L=San Francisco/CN=Test AS/emailAddress=as@example.com +# phase1: Phase1 (outer authentication, i.e., TLS tunnel) parameters +# (string with field-value pairs, e.g., "peapver=0" or +# "peapver=1 peaplabel=1") +# 'peapver' can be used to force which PEAP version (0 or 1) is used. +# 'peaplabel=1' can be used to force new label, "client PEAP encryption", +# to be used during key derivation when PEAPv1 or newer. Most existing +# PEAPv1 implementation seem to be using the old label, "client EAP +# encryption", and wpa_supplicant is now using that as the default value. +# Some servers, e.g., Radiator, may require peaplabel=1 configuration to +# interoperate with PEAPv1; see eap_testing.txt for more details. +# 'peap_outer_success=0' can be used to terminate PEAP authentication on +# tunneled EAP-Success. This is required with some RADIUS servers that +# implement draft-josefsson-pppext-eap-tls-eap-05.txt (e.g., +# Lucent NavisRadius v4.4.0 with PEAP in "IETF Draft 5" mode) +# include_tls_length=1 can be used to force wpa_supplicant to include +# TLS Message Length field in all TLS messages even if they are not +# fragmented. +# sim_min_num_chal=3 can be used to configure EAP-SIM to require three +# challenges (by default, it accepts 2 or 3) +# phase2: Phase2 (inner authentication with TLS tunnel) parameters +# (string with field-value pairs, e.g., "auth=MSCHAPV2" for EAP-PEAP or +# "autheap=MSCHAPV2 autheap=MD5" for EAP-TTLS) +# Following certificate/private key fields are used in inner Phase2 +# authentication when using EAP-TTLS or EAP-PEAP. +# ca_cert2: File path to CA certificate file. This file can have one or more +# trusted CA certificates. If ca_cert2 is not included, server +# certificate will not be verified. This is insecure and the CA file +# should always be configured. +# client_cert2: File path to client certificate file +# private_key2: File path to client private key file +# private_key2_passwd: Password for private key file +# dh_file2: File path to DH/DSA parameters file (in PEM format) +# subject_match2: Substring to be matched against the subject of the +# authentication server certificate. +# +# EAP-PSK variables: +# eappsk: 16-byte (128-bit, 32 hex digits) pre-shared key in hex format +# nai: user NAI +# server_nai: authentication server NAI +# +# EAP-FAST variables: +# pac_file: File path for the PAC entries. wpa_supplicant will need to be able +# to create this file and write updates to it when PAC is being +# provisioned or refreshed. +# phase1: fast_provisioning=1 option enables in-line provisioning of EAP-FAST +# credentials (PAC) +# +# wpa_supplicant supports number of "EAP workarounds" to work around +# interoperability issues with incorrectly behaving authentication servers. +# These are enabled by default because some of the issues are present in large +# number of authentication servers. Strict EAP conformance mode can be +# configured by disabling workarounds with eap_workaround=0. + +# Example blocks: + +# Simple case: WPA-PSK, PSK as an ASCII passphrase, allow all valid ciphers +network={ + ssid="simple" + psk="very secret passphrase" + priority=5 +} + +# Same as previous, but request SSID-specific scanning (for APs that reject +# broadcast SSID) +network={ + ssid="second ssid" + scan_ssid=1 + psk="very secret passphrase" + priority=2 +} + +# Only WPA-PSK is used. Any valid cipher combination is accepted. +network={ + ssid="example" + proto=WPA + key_mgmt=WPA-PSK + pairwise=CCMP TKIP + group=CCMP TKIP WEP104 WEP40 + psk=06b4be19da289f475aa46a33cb793029d4ab3db7a23ee92382eb0106c72ac7bb + priority=2 +} + +# Only WPA-EAP is used. Both CCMP and TKIP is accepted. An AP that used WEP104 +# or WEP40 as the group cipher will not be accepted. +network={ + ssid="example" + proto=RSN + key_mgmt=WPA-EAP + pairwise=CCMP TKIP + group=CCMP TKIP + eap=TLS + identity="user@example.com" + ca_cert="/etc/cert/ca.pem" + client_cert="/etc/cert/user.pem" + private_key="/etc/cert/user.prv" + private_key_passwd="password" + priority=1 +} + +# EAP-PEAP/MSCHAPv2 configuration for RADIUS servers that use the new peaplabel +# (e.g., Radiator) +network={ + ssid="example" + key_mgmt=WPA-EAP + eap=PEAP + identity="user@example.com" + password="foobar" + ca_cert="/etc/cert/ca.pem" + phase1="peaplabel=1" + phase2="auth=MSCHAPV2" + priority=10 +} + +# EAP-TTLS/EAP-MD5-Challenge configuration with anonymous identity for the +# unencrypted use. Real identity is sent only within an encrypted TLS tunnel. +network={ + ssid="example" + key_mgmt=WPA-EAP + eap=TTLS + identity="user@example.com" + anonymous_identity="anonymous@example.com" + password="foobar" + ca_cert="/etc/cert/ca.pem" + priority=2 +} + +# EAP-TTLS/MSCHAPv2 configuration with anonymous identity for the unencrypted +# use. Real identity is sent only within an encrypted TLS tunnel. +network={ + ssid="example" + key_mgmt=WPA-EAP + eap=TTLS + identity="user@example.com" + anonymous_identity="anonymous@example.com" + password="foobar" + ca_cert="/etc/cert/ca.pem" + phase2="auth=MSCHAPV2" +} + +# WPA-EAP, EAP-TTLS with different CA certificate used for outer and inner +# authentication. +network={ + ssid="example" + key_mgmt=WPA-EAP + eap=TTLS + # Phase1 / outer authentication + anonymous_identity="anonymous@example.com" + ca_cert="/etc/cert/ca.pem" + # Phase 2 / inner authentication + phase2="autheap=TLS" + ca_cert2="/etc/cert/ca2.pem" + client_cert2="/etc/cer/user.pem" + private_key2="/etc/cer/user.prv" + private_key2_passwd="password" + priority=2 +} + +# Both WPA-PSK and WPA-EAP is accepted. Only CCMP is accepted as pairwise and +# group cipher. +network={ + ssid="example" + bssid=00:11:22:33:44:55 + proto=WPA RSN + key_mgmt=WPA-PSK WPA-EAP + pairwise=CCMP + group=CCMP + psk=06b4be19da289f475aa46a33cb793029d4ab3db7a23ee92382eb0106c72ac7bb +} + +# Special characters in SSID, so use hex string. Default to WPA-PSK, WPA-EAP +# and all valid ciphers. +network={ + ssid=00010203 + psk=000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f +} + + +# EAP-SIM with a GSM SIM or USIM +network={ + ssid="eap-sim-test" + key_mgmt=WPA-EAP + eap=SIM + pin="1234" + pcsc="" +} + + +# EAP-PSK +network={ + ssid="eap-psk-test" + key_mgmt=WPA-EAP + eap=PSK + identity="eap_psk_user" + eappsk=06b4be19da289f475aa46a33cb793029 + nai="eap_psk_user@example.com" + server_nai="as@example.com" +} + + +# IEEE 802.1X/EAPOL with dynamically generated WEP keys (i.e., no WPA) using +# EAP-TLS for authentication and key generation; require both unicast and +# broadcast WEP keys. +network={ + ssid="1x-test" + key_mgmt=IEEE8021X + eap=TLS + identity="user@example.com" + ca_cert="/etc/cert/ca.pem" + client_cert="/etc/cert/user.pem" + private_key="/etc/cert/user.prv" + private_key_passwd="password" + eapol_flags=3 +} + + +# LEAP with dynamic WEP keys +network={ + ssid="leap-example" + key_mgmt=IEEE8021X + eap=LEAP + identity="user" + password="foobar" +} + +# EAP-FAST with WPA (WPA or WPA2) +network={ + ssid="eap-fast-test" + key_mgmt=WPA-EAP + eap=FAST + anonymous_identity="FAST-000102030405" + identity="username" + password="password" + phase1="fast_provisioning=1" + pac_file="/etc/wpa_supplicant.eap-fast-pac" +} + +# Plaintext connection (no WPA, no IEEE 802.1X) +network={ + ssid="plaintext-test" + key_mgmt=NONE +} + + +# Shared WEP key connection (no WPA, no IEEE 802.1X) +network={ + ssid="static-wep-test" + key_mgmt=NONE + wep_key0="abcde" + wep_key1=0102030405 + wep_key2="1234567890123" + wep_tx_keyidx=0 + priority=5 +} + + +# Shared WEP key connection (no WPA, no IEEE 802.1X) using Shared Key +# IEEE 802.11 authentication +network={ + ssid="static-wep-test2" + key_mgmt=NONE + wep_key0="abcde" + wep_key1=0102030405 + wep_key2="1234567890123" + wep_tx_keyidx=0 + priority=5 + auth_alg=SHARED +} + + +# IBSS/ad-hoc network with WPA-None/TKIP. +network={ + ssid="test adhoc" + mode=1 + proto=WPA + key_mgmt=WPA-NONE + pairwise=NONE + group=TKIP + psk="secret passphrase" +} + + +# Catch all example that allows more or less all configuration modes +network={ + ssid="example" + scan_ssid=1 + key_mgmt=WPA-EAP WPA-PSK IEEE8021X NONE + pairwise=CCMP TKIP + group=CCMP TKIP WEP104 WEP40 + psk="very secret passphrase" + eap=TTLS PEAP TLS + identity="user@example.com" + password="foobar" + ca_cert="/etc/cert/ca.pem" + client_cert="/etc/cert/user.pem" + private_key="/etc/cert/user.prv" + private_key_passwd="password" + phase1="peaplabel=0" +} diff --git a/contrib/wpa_supplicant/wpa_supplicant.h b/contrib/wpa_supplicant/wpa_supplicant.h new file mode 100644 index 0000000..e5ad182 --- /dev/null +++ b/contrib/wpa_supplicant/wpa_supplicant.h @@ -0,0 +1,81 @@ +#ifndef WPA_SUPPLICANT_H +#define WPA_SUPPLICANT_H + +/* Driver wrappers are not supposed to directly touch the internal data + * structure used in wpa_supplicant, so that definition is not provided here. + */ +struct wpa_supplicant; + +typedef enum { + EVENT_ASSOC, EVENT_DISASSOC, EVENT_MICHAEL_MIC_FAILURE, + EVENT_SCAN_RESULTS, EVENT_ASSOCINFO, EVENT_INTERFACE_STATUS, + EVENT_PMKID_CANDIDATE +} wpa_event_type; + +union wpa_event_data { + struct { + /* Optional request information data: IEs included in AssocReq + * and AssocResp. If these are not returned by the driver, + * WPA Supplicant will generate the WPA/RSN IE. */ + u8 *req_ies, *resp_ies; + size_t req_ies_len, resp_ies_len; + + /* Optional Beacon/ProbeResp data: IEs included in Beacon or + * Probe Response frames from the current AP (i.e., the one + * that the client just associated with). This information is + * used to update WPA/RSN IE for the AP. If this field is not + * set, the results from previous scan will be used. If no + * data for the new AP is found, scan results will be requested + * again (without scan request). At this point, the driver is + * expected to provide WPA/RSN IE for the AP (if WPA/WPA2 is + * used). */ + u8 *beacon_ies; /* beacon or probe resp IEs */ + size_t beacon_ies_len; + } assoc_info; + struct { + int unicast; + } michael_mic_failure; + struct { + char ifname[20]; + enum { + EVENT_INTERFACE_ADDED, EVENT_INTERFACE_REMOVED + } ievent; + } interface_status; + struct { + u8 bssid[ETH_ALEN]; + int index; /* smaller the index, higher the priority */ + int preauth; + } pmkid_candidate; +}; + +/** + * wpa_supplicant_event - report a driver event for wpa_supplicant + * @wpa_s: pointer to wpa_supplicant data; this is the @ctx variable registered + * with wpa_driver_events_init() + * @event: event type (defined above) + * @data: possible extra data for the event + * + * Driver wrapper code should call this function whenever an event is received + * from the driver. + */ +void wpa_supplicant_event(struct wpa_supplicant *wpa_s, wpa_event_type event, + union wpa_event_data *data); + +/** + * wpa_msg - conditional printf for default target and ctrl_iface monitors + * @level: priority level (MSG_*) of the message + * @fmt: printf format string, followed by optional arguments + * + * This function is used to print conditional debugging and error messages. The + * output may be directed to stdout, stderr, and/or syslog based on + * configuration. This function is like wpa_printf(), but it also sends the + * same message to all attached ctrl_iface monitors. + * + * Note: New line '\n' is added to the end of the text when printing to stdout. + */ +void wpa_msg(struct wpa_supplicant *wpa_s, int level, char *fmt, ...) +__attribute__ ((format (printf, 3, 4))); + +const char * wpa_ssid_txt(u8 *ssid, size_t ssid_len); + +#endif /* WPA_SUPPLICANT_H */ diff --git a/contrib/wpa_supplicant/wpa_supplicant_i.h b/contrib/wpa_supplicant/wpa_supplicant_i.h new file mode 100644 index 0000000..3883393 --- /dev/null +++ b/contrib/wpa_supplicant/wpa_supplicant_i.h @@ -0,0 +1,469 @@ +#ifndef WPA_SUPPLICANT_I_H +#define WPA_SUPPLICANT_I_H + +#include "driver.h" + +#ifdef EAPOL_TEST +#include <netinet/in.h> + +struct hostapd_radius_server { + struct in_addr addr; + int port; + u8 *shared_secret; + size_t shared_secret_len; +}; +#endif /* EAPOL_TEST */ + +#define PMKID_LEN 16 +struct rsn_pmksa_cache { + struct rsn_pmksa_cache *next; + u8 pmkid[PMKID_LEN]; + u8 pmk[PMK_LEN]; + size_t pmk_len; + time_t expiration; + int akmp; /* WPA_KEY_MGMT_* */ + u8 aa[ETH_ALEN]; +}; + +struct rsn_pmksa_candidate { + struct rsn_pmksa_candidate *next; + u8 bssid[ETH_ALEN]; + int priority; +}; + + +struct wpa_ptk { + u8 mic_key[16]; /* EAPOL-Key MIC Key (MK) */ + u8 encr_key[16]; /* EAPOL-Key Encryption Key (EK) */ + u8 tk1[16]; /* Temporal Key 1 (TK1) */ + union { + u8 tk2[16]; /* Temporal Key 2 (TK2) */ + struct { + u8 tx_mic_key[8]; + u8 rx_mic_key[8]; + } auth; + } u; +} __attribute__ ((packed)); + + +struct wpa_blacklist { + struct wpa_blacklist *next; + u8 bssid[ETH_ALEN]; +}; + + +struct wpa_supplicant { + struct wpa_supplicant *head; + struct wpa_supplicant *next; + struct l2_packet_data *l2; + unsigned char own_addr[ETH_ALEN]; + char ifname[100]; +#ifdef CONFIG_XSUPPLICANT_IFACE + int dot1x_s; /* socket for connection to Xsupplicant */ + int ext_pmk_received; /* 1 = PMK was received from Xsupplicant */ +#endif /* CONFIG_XSUPPLICANT_IFACE */ + + u8 pmk[PMK_LEN]; + size_t pmk_len; + u8 snonce[WPA_NONCE_LEN]; + u8 anonce[WPA_NONCE_LEN]; /* ANonce from the last 1/4 msg */ + struct wpa_ptk ptk, tptk; + int ptk_set, tptk_set; + int renew_snonce; + char *confname; + struct wpa_config *conf; + u8 request_counter[WPA_REPLAY_COUNTER_LEN]; + int countermeasures; + time_t last_michael_mic_error; + u8 rx_replay_counter[WPA_REPLAY_COUNTER_LEN]; + int rx_replay_counter_set; + u8 bssid[ETH_ALEN]; + int reassociate; /* reassociation requested */ + struct wpa_ssid *current_ssid; + u8 *ap_wpa_ie, *ap_rsn_ie; + size_t ap_wpa_ie_len, ap_rsn_ie_len; + u8 *assoc_wpa_ie; + size_t assoc_wpa_ie_len; + + /* Selected configuration (based on Beacon/ProbeResp WPA IE) */ + int proto; + int pairwise_cipher; + int group_cipher; + int key_mgmt; + + void *drv_priv; /* private data used by driver_ops */ + + struct wpa_ssid *prev_scan_ssid; /* previously scanned SSID; + * NULL = not yet initialized (start + * with broadcast SSID) + * BROADCAST_SSID_SCAN = broadcast + * SSID was used in the previous scan + */ +#define BROADCAST_SSID_SCAN ((struct wpa_ssid *) 1) + + struct wpa_scan_result *scan_results; + int num_scan_results; + + struct wpa_driver_ops *driver; + int interface_removed; /* whether the network interface has been + * removed */ + struct eapol_sm *eapol; + + int ctrl_sock; /* UNIX domain socket for control interface or -1 if + * not used */ + struct wpa_ctrl_dst *ctrl_dst; + + enum { + WPA_DISCONNECTED, WPA_SCANNING, WPA_ASSOCIATING, + WPA_ASSOCIATED, WPA_4WAY_HANDSHAKE, WPA_GROUP_HANDSHAKE, + WPA_COMPLETED + } wpa_state; + + struct rsn_pmksa_cache *pmksa; /* PMKSA cache */ + int pmksa_count; /* number of entries in PMKSA cache */ + struct rsn_pmksa_cache *cur_pmksa; /* current PMKSA entry */ + struct rsn_pmksa_candidate *pmksa_candidates; + + struct l2_packet_data *l2_preauth; + u8 preauth_bssid[ETH_ALEN]; /* current RSN pre-auth peer or + * 00:00:00:00:00:00 if no pre-auth is + * in progress */ + struct eapol_sm *preauth_eapol; + + int eapol_received; /* number of EAPOL packets received after the + * previous association event */ + + u8 *imsi; + size_t imsi_len; + struct scard_data *scard; + + unsigned char last_eapol_src[ETH_ALEN]; + + int keys_cleared; + + struct wpa_blacklist *blacklist; + +#ifdef EAPOL_TEST + u8 radius_identifier; + struct radius_msg *last_recv_radius; + struct in_addr own_ip_addr; + struct radius_client_data *radius; + + /* RADIUS Authentication and Accounting servers in priority order */ + struct hostapd_radius_server *auth_servers, *auth_server; + int num_auth_servers; + struct hostapd_radius_server *acct_servers, *acct_server; + int num_acct_servers; + + int radius_retry_primary_interval; + int radius_acct_interim_interval; + + u8 *last_eap_radius; /* last received EAP Response from Authentication + * Server */ + size_t last_eap_radius_len; + + u8 authenticator_pmk[PMK_LEN]; + size_t authenticator_pmk_len; + int radius_access_accept_received; + int radius_access_reject_received; + int auth_timed_out; + + u8 *eap_identity; + size_t eap_identity_len; +#endif /* EAPOL_TEST */ +}; + + +/* wpa_supplicant.c */ +void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx); + +void wpa_supplicant_req_scan(struct wpa_supplicant *wpa_s, int sec, int usec); + +void wpa_supplicant_cancel_scan(struct wpa_supplicant *wpa_s); + +void wpa_supplicant_disassociate(struct wpa_supplicant *wpa_s, + int reason_code); +void wpa_supplicant_deauthenticate(struct wpa_supplicant *wpa_s, + int reason_code); + +void wpa_supplicant_req_auth_timeout(struct wpa_supplicant *wpa_s, + int sec, int usec); + +void wpa_supplicant_cancel_auth_timeout(struct wpa_supplicant *wpa_s); + +int wpa_supplicant_reload_configuration(struct wpa_supplicant *wpa_s); + +int wpa_supplicant_get_beacon_ie(struct wpa_supplicant *wpa_s); + + +/* wpa.c */ +void wpa_supplicant_key_request(struct wpa_supplicant *wpa_s, + int error, int pairwise); + +struct wpa_ie_data { + int proto; + int pairwise_cipher; + int group_cipher; + int key_mgmt; + int capabilities; + int num_pmkid; + u8 *pmkid; +}; + +int wpa_parse_wpa_ie(struct wpa_supplicant *wpa_s, u8 *wpa_ie, + size_t wpa_ie_len, struct wpa_ie_data *data); + +int wpa_gen_wpa_ie(struct wpa_supplicant *wpa_s, u8 *wpa_ie); + +void wpa_supplicant_rx_eapol(void *ctx, unsigned char *src_addr, + unsigned char *buf, size_t len); + +struct wpa_ssid * wpa_supplicant_get_ssid(struct wpa_supplicant *wpa_s); + +void pmksa_cache_free(struct wpa_supplicant *wpa_s); +struct rsn_pmksa_cache * pmksa_cache_get(struct wpa_supplicant *wpa_s, + u8 *aa, u8 *pmkid); +int pmksa_cache_list(struct wpa_supplicant *wpa_s, char *buf, size_t len); +void pmksa_candidate_free(struct wpa_supplicant *wpa_s); + +int wpa_get_mib(struct wpa_supplicant *wpa_s, char *buf, size_t buflen); + +struct wpa_scan_result; +#ifdef IEEE8021X_EAPOL +int rsn_preauth_init(struct wpa_supplicant *wpa_s, u8 *dst); +void rsn_preauth_deinit(struct wpa_supplicant *wpa_s); +void rsn_preauth_scan_results(struct wpa_supplicant *wpa_s, + struct wpa_scan_result *results, int count); +void pmksa_candidate_add(struct wpa_supplicant *wpa_s, const u8 *bssid, + int prio); +#else /* IEEE8021X_EAPOL */ +static inline int rsn_preauth_init(struct wpa_supplicant *wpa_s, u8 *dst) +{ + return -1; +} + +static inline void rsn_preauth_deinit(struct wpa_supplicant *wpa_s) +{ +} +static inline void rsn_preauth_scan_results(struct wpa_supplicant *wpa_s, + struct wpa_scan_result *results, + int count) +{ +} + +static inline void pmksa_candidate_add(struct wpa_supplicant *wpa_s, + const u8 *bssid, + int prio) +{ +} +#endif /* IEEE8021X_EAPOL */ + +void wpa_supplicant_notify_eapol_done(void *ctx); + +/** + * wpa_eapol_send - send IEEE 802.1X EAPOL packet to the Authenticator + * @ctx: pointer to wpa_supplicant data + * @type: IEEE 802.1X packet type (IEEE802_1X_TYPE_*) + * @buf: EAPOL payload (after IEEE 802.1X header) + * @len: EAPOL payload length + * + * This function adds Ethernet and IEEE 802.1X header and sends the EAPOL frame + * to the current Authenticator or in case of pre-authentication, to the peer + * of the authentication. + */ +int wpa_eapol_send(void *ctx, int type, u8 *buf, size_t len); +int wpa_eapol_send_preauth(void *ctx, int type, u8 *buf, size_t len); + + +/* driver_ops */ +static inline void * wpa_drv_init(struct wpa_supplicant *wpa_s, + const char *ifname) +{ + if (wpa_s->driver->init) { + return wpa_s->driver->init(wpa_s, ifname); + } + return NULL; +} + +static inline void wpa_drv_deinit(struct wpa_supplicant *wpa_s) +{ + if (wpa_s->driver->deinit) + wpa_s->driver->deinit(wpa_s->drv_priv); +} + +static inline int wpa_drv_set_drop_unencrypted(struct wpa_supplicant *wpa_s, + int enabled) +{ + if (wpa_s->driver->set_drop_unencrypted) { + return wpa_s->driver->set_drop_unencrypted(wpa_s->drv_priv, + enabled); + } + return -1; +} + +static inline int wpa_drv_set_countermeasures(struct wpa_supplicant *wpa_s, + int enabled) +{ + if (wpa_s->driver->set_countermeasures) { + return wpa_s->driver->set_countermeasures(wpa_s->drv_priv, + enabled); + } + return -1; +} + +static inline int wpa_drv_set_auth_alg(struct wpa_supplicant *wpa_s, + int auth_alg) +{ + if (wpa_s->driver->set_auth_alg) { + return wpa_s->driver->set_auth_alg(wpa_s->drv_priv, + auth_alg); + } + return -1; +} + +static inline int wpa_drv_set_wpa(struct wpa_supplicant *wpa_s, int enabled) +{ + if (wpa_s->driver->set_wpa) { + return wpa_s->driver->set_wpa(wpa_s->drv_priv, enabled); + } + return -1; +} + +static inline int wpa_drv_associate(struct wpa_supplicant *wpa_s, + struct wpa_driver_associate_params *params) +{ + if (wpa_s->driver->associate) { + return wpa_s->driver->associate(wpa_s->drv_priv, params); + } + return -1; +} + +static inline int wpa_drv_scan(struct wpa_supplicant *wpa_s, const u8 *ssid, + size_t ssid_len) +{ + if (wpa_s->driver->scan) { + return wpa_s->driver->scan(wpa_s->drv_priv, ssid, ssid_len); + } + return -1; +} + +static inline int wpa_drv_get_scan_results(struct wpa_supplicant *wpa_s, + struct wpa_scan_result *results, + size_t max_size) +{ + if (wpa_s->driver->get_scan_results) { + return wpa_s->driver->get_scan_results(wpa_s->drv_priv, + results, max_size); + } + return -1; +} + +static inline int wpa_drv_get_bssid(struct wpa_supplicant *wpa_s, u8 *bssid) +{ + if (wpa_s->driver->get_bssid) { + return wpa_s->driver->get_bssid(wpa_s->drv_priv, bssid); + } + return -1; +} + +static inline int wpa_drv_get_ssid(struct wpa_supplicant *wpa_s, u8 *ssid) +{ + if (wpa_s->driver->get_ssid) { + return wpa_s->driver->get_ssid(wpa_s->drv_priv, ssid); + } + return -1; +} + +static inline int wpa_drv_set_key(struct wpa_supplicant *wpa_s, wpa_alg alg, + const u8 *addr, int key_idx, int set_tx, + const u8 *seq, size_t seq_len, + const u8 *key, size_t key_len) +{ + if (wpa_s->driver->set_key) { + return wpa_s->driver->set_key(wpa_s->drv_priv, alg, addr, + key_idx, set_tx, seq, seq_len, + key, key_len); + } + return -1; +} + +static inline int wpa_drv_deauthenticate(struct wpa_supplicant *wpa_s, + const u8 *addr, int reason_code) +{ + if (wpa_s->driver->deauthenticate) { + return wpa_s->driver->deauthenticate(wpa_s->drv_priv, addr, + reason_code); + } + return -1; +} + +static inline int wpa_drv_disassociate(struct wpa_supplicant *wpa_s, + const u8 *addr, int reason_code) +{ + if (wpa_s->driver->disassociate) { + return wpa_s->driver->disassociate(wpa_s->drv_priv, addr, + reason_code); + } + return -1; +} + +static inline int wpa_drv_add_pmkid(struct wpa_supplicant *wpa_s, + const u8 *bssid, const u8 *pmkid) +{ + if (wpa_s->driver->add_pmkid) { + return wpa_s->driver->add_pmkid(wpa_s->drv_priv, bssid, pmkid); + } + return -1; +} + +static inline int wpa_drv_remove_pmkid(struct wpa_supplicant *wpa_s, + const u8 *bssid, const u8 *pmkid) +{ + if (wpa_s->driver->remove_pmkid) { + return wpa_s->driver->remove_pmkid(wpa_s->drv_priv, bssid, + pmkid); + } + return -1; +} + +static inline int wpa_drv_flush_pmkid(struct wpa_supplicant *wpa_s) +{ + if (wpa_s->driver->flush_pmkid) { + return wpa_s->driver->flush_pmkid(wpa_s->drv_priv); + } + return -1; +} + +static inline int wpa_drv_get_capa(struct wpa_supplicant *wpa_s, + struct wpa_driver_capa *capa) +{ + if (wpa_s->driver->get_capa) { + return wpa_s->driver->get_capa(wpa_s->drv_priv, capa); + } + return -1; +} + +static inline void wpa_drv_poll(struct wpa_supplicant *wpa_s) +{ + if (wpa_s->driver->poll) { + wpa_s->driver->poll(wpa_s->drv_priv); + } +} + +static inline const char * wpa_drv_get_ifname(struct wpa_supplicant *wpa_s) +{ + if (wpa_s->driver->get_ifname) { + return wpa_s->driver->get_ifname(wpa_s->drv_priv); + } + return NULL; +} + +static inline const u8 * wpa_drv_get_mac_addr(struct wpa_supplicant *wpa_s) +{ + if (wpa_s->driver->get_mac_addr) { + return wpa_s->driver->get_mac_addr(wpa_s->drv_priv); + } + return NULL; +} + +#endif /* WPA_SUPPLICANT_I_H */ |