diff options
author | Sam Leffler <sam@FreeBSD.org> | 2005-06-05 20:52:14 +0000 |
---|---|---|
committer | Sam Leffler <sam@FreeBSD.org> | 2005-06-05 20:52:14 +0000 |
commit | f1fb6907abb279cfabe81d79e2ffac54c4e7aa98 (patch) | |
tree | 98be326632e2ea3857ee0d9f831c91ea0823bb0d | |
download | src-f1fb6907abb279cfabe81d79e2ffac54c4e7aa98.tar.gz src-f1fb6907abb279cfabe81d79e2ffac54c4e7aa98.zip |
Stripped down import of wpa_supplicant v0.3.8vendor/wpa_supplicant/0.3.8
Notes
Notes:
svn path=/vendor/wpa_supplicant/dist/; revision=147013
svn path=/vendor/wpa_supplicant/0.3.8/; revision=147015; tag=vendor/wpa_supplicant/0.3.8
82 files changed, 35297 insertions, 0 deletions
diff --git a/contrib/wpa_supplicant/COPYING b/contrib/wpa_supplicant/COPYING new file mode 100644 index 000000000000..60549be514af --- /dev/null +++ b/contrib/wpa_supplicant/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) 19yy <name of author> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + <signature of Ty Coon>, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/contrib/wpa_supplicant/ChangeLog b/contrib/wpa_supplicant/ChangeLog new file mode 100644 index 000000000000..57098581aee3 --- /dev/null +++ b/contrib/wpa_supplicant/ChangeLog @@ -0,0 +1,395 @@ +ChangeLog for wpa_supplicant + +2005-02-13 - v0.3.8 + * fixed EAPOL-Key validation to drop packets with invalid Key Data + Length; such frames could have crashed wpa_supplicant due to buffer + overflow + +2005-02-12 - v0.3.7 (beginning of 0.3.x stable releases) + * added new phase1 option parameter, include_tls_length=1, to force + wpa_supplicant to add TLS Message Length field to all TLS messages + even if the packet is not fragmented; this may be needed with some + authentication servers + * fixed WPA/RSN IE verification in message 3 of 4-Way Handshake when + using drivers that take care of AP selection (e.g., when using + ap_scan=2) + * fixed reprocessing of pending request after ctrl_iface requests for + identity/password/otp + * fixed ctrl_iface requests for identity/password/otp in Phase 2 of + EAP-PEAP and EAP-TTLS + * all drivers using driver_wext: set interface up and select Managed + mode when starting wpa_supplicant; set interface down when exiting + * renamed driver_ipw2100.c to driver_ipw.c since it now supports both + ipw2100 and ipw2200; please note that this also changed the + configuration variable in .config to CONFIG_DRIVER_IPW + +2005-01-24 - v0.3.6 + * fixed a busy loop introduced in v0.3.5 for scan result processing + when no matching AP is found + +2005-01-23 - v0.3.5 + * added a workaround for an interoperability issue with a Cisco AP + when using WPA2-PSK + * fixed non-WPA IEEE 802.1X to use the same authentication timeout as + WPA with IEEE 802.1X (i.e., timeout 10 -> 70 sec to allow + retransmission of dropped frames) + * fixed issues with 64-bit CPUs and SHA1 cleanup in previous version + (e.g., segfault when processing EAPOL-Key frames) + * fixed EAP workaround and fast reauthentication configuration for + RSN pre-authentication; previously these were disabled and + pre-authentication would fail if the used authentication server + requires EAP workarounds + * added support for blacklisting APs that fail or timeout + authentication in ap_scan=1 mode so that all APs are tried in cases + where the ones with strongest signal level are failing authentication + * fixed CA certificate loading after a failed EAP-TLS/PEAP/TTLS + authentication attempt + * allow EAP-PEAP/TTLS fast reauthentication only if Phase 2 succeeded + in the previous authentication (previously, only Phase 1 success was + verified) + +2005-01-09 - v0.3.4 + * added preliminary support for IBSS (ad-hoc) mode configuration + (mode=1 in network block); this included a new key_mgmt mode + WPA-NONE, i.e., TKIP or CCMP with a fixed key (based on psk) and no + key management; see wpa_supplicant.conf for more details and an + example on how to configure this (note: this is currently implemented + only for driver_hostapd.c, but the changes should be trivial to add + in associate() handler for other drivers, too (assuming the driver + supports WPA-None) + * added preliminary port for native Windows (i.e., no cygwin) using + mingw + +2005-01-02 - v0.3.3 + * added optional support for GNU Readline and History Libraries for + wpa_cli (CONFIG_READLINE) + * cleaned up EAP state machine <-> method interface and number of + small problems with error case processing not terminating on + EAP-Failure but waiting for timeout + * added couple of workarounds for interoperability issues with a + Cisco AP when using WPA2 + * added support for EAP-FAST (draft-cam-winget-eap-fast-00.txt); + Note: This requires a patch for openssl to add support for TLS + extensions and number of workarounds for operations without + certificates. Proof of concept type of experimental patch is + included in openssl-tls-extensions.patch. + +2004-12-19 - v0.3.2 + * fixed private key loading for cases where passphrase is not set + * fixed Windows/cygwin L2 packet handler freeing; previous version + could cause a segfault when RSN pre-authentication was completed + * added support for PMKSA caching with drivers that generate RSN IEs + (e.g., NDIS); currently, this is only implemented in driver_ndis.c, + but similar code can be easily added to driver_ndiswrapper.c once + ndiswrapper gets full support for RSN PMKSA caching + * improved recovery from PMKID mismatches by requesting full EAP + authentication in case of failed PMKSA caching attempt + * driver_ndis: added support for NDIS NdisMIncidateStatus() events + (this requires that ndis_events is ran while wpa_supplicant is + running) + * driver_ndis: use ADD_WEP/REMOVE_WEP when configuring WEP keys + * added support for driver interfaces to replace the interface name + based on driver/OS specific mapping, e.g., in case of driver_ndis, + this allows the beginning of the adapter description to be used as + the interface name + * added support for CR+LF (Windows-style) line ends in configuration + file + * driver_ndis: enable radio before starting scanning, disable radio + when exiting + * modified association event handler to set portEnabled = FALSE before + clearing port Valid in order to reset EAP state machine and avoid + problems with new authentication getting ignored because of state + machines ending up in AUTHENTICATED/SUCCESS state based on old + information + * added support for driver events to add PMKID candidates in order to + allow drivers to give priority to most likely roaming candidates + * driver_hostap: moved PrivacyInvoked configuration to associate() + function so that this will not be set for plaintext connections + * added KEY_MGMT_802_1X_NO_WPA as a new key_mgmt type so that driver + interface can distinguish plaintext and IEEE 802.1X (no WPA) + authentication + * fixed static WEP key configuration to use broadcast/default type for + all keys (previously, the default TX key was configured as pairwise/ + unicast key) + * driver_ndis: added legacy WPA capability detection for non-WPA2 + drivers + * added support for setting static WEP keys for IEEE 802.1X without + dynamic WEP keying (eapol_flags=0) + +2004-12-12 - v0.3.1 + * added support for reading PKCS#12 (PFX) files (as a replacement for + PEM/DER) to get certificate and private key (CONFIG_PKCS12) + * fixed compilation with CONFIG_PCSC=y + * added new ap_scan mode, ap_scan=2, for drivers that take care of + association, but need to be configured with security policy and SSID, + e.g., ndiswrapper and NDIS driver; this mode should allow such + drivers to work with hidden SSIDs and optimized roaming; when + ap_scan=2 is used, only the first network block in the configuration + file is used and this configuration should have explicit security + policy (i.e., only one option in the lists) for key_mgmt, pairwise, + group, proto variables + * added experimental port of wpa_supplicant for Windows + - driver_ndis.c driver interface (NDIS OIDs) + - currently, this requires cygwin and WinPcap + - small utility, win_if_list, can be used to get interface name + * control interface can now be removed at build time; add + CONFIG_CTRL_IFACE=y to .config to maintain old functionality + * optional Xsupplicant interface can now be removed at build time; + (CONFIG_XSUPPLICANT_IFACE=y in .config to bring it back) + * added auth_alg to driver interface associate() parameters to make it + easier for drivers to configure authentication algorithm as part of + the association + +2004-12-05 - v0.3.0 (beginning of 0.3.x development releases) + * driver_broadcom: added new driver interface for Broadcom wl.o driver + (a generic driver for Broadcom IEEE 802.11a/g cards) + * wpa_cli: fixed parsing of -p <path> command line argument + * PEAPv1: fixed tunneled EAP-Success reply handling to reply with TLS + ACK, not tunneled EAP-Success (of which only the first byte was + actually send due to a bug in previous code); this seems to + interoperate with most RADIUS servers that implements PEAPv1 + * PEAPv1: added support for terminating PEAP authentication on tunneled + EAP-Success message; this can be configured by adding + peap_outer_success=0 on phase1 parameters in wpa_supplicant.conf + (some RADIUS servers require this whereas others require a tunneled + reply + * PEAPv1: changed phase1 option peaplabel to use default to 0, i.e., to + the old label for key derivation; previously, the default was 1, + but it looks like most existing PEAPv1 implementations use the old + label which is thus more suitable default option + * added support for EAP-PSK (draft-bersani-eap-psk-03.txt) + * fixed parsing of wep_tx_keyidx + * added support for configuring list of allowed Phase 2 EAP types + (for both EAP-PEAP and EAP-TTLS) instead of only one type + * added support for configuring IEEE 802.11 authentication algorithm + (auth_alg; mainly for using Shared Key authentication with static + WEP keys) + * added support for EAP-AKA (with UMTS SIM) + * fixed couple of errors in PCSC handling that could have caused + random-looking errors for EAP-SIM + * added support for EAP-SIM pseudonyms and fast re-authentication + * added support for EAP-TLS/PEAP/TTLS fast re-authentication (TLS + session resumption) + * added support for EAP-SIM with two challanges + (phase1="sim_min_num_chal=3" can be used to require three challenges) + * added support for configuring DH/DSA parameters for an ephemeral DH + key exchange (EAP-TLS/PEAP/TTLS) using new configuration parameters + dh_file and dh_file2 (phase 2); this adds support for using DSA keys + and optional DH key exchange to achieve forward secracy with RSA keys + * added support for matching subject of the authentication server + certificate with a substring when using EAP-TLS/PEAP/TTLS; new + configuration variables subject_match and subject_match2 + * changed SSID configuration in driver_wext.c (used by many driver + interfaces) to use ssid_len+1 as the length for SSID since some Linux + drivers expect this + * fixed couple of unaligned reads in scan result parsing to fix WPA + connection on some platforms (e.g., ARM) + * added driver interface for Intel ipw2100 driver + * added support for LEAP with WPA + * added support for larger scan results report (old limit was 4 kB of + data, i.e., about 35 or so APs) when using Linux wireless extensions + v17 or newer + * fixed a bug in PMKSA cache processing: skip sending of EAPOL-Start + only if there is a PMKSA cache entry for the current AP + * fixed error handling for case where reading of scan results fails: + must schedule a new scan or wpa_supplicant will remain waiting + forever + * changed debug output to remove shared password/key material by + default; all key information can be included with -K command line + argument to match the previous behavior + * added support for timestamping debug log messages (disabled by + default, can be enabled with -t command line argument) + * set pairwise/group cipher suite for non-WPA IEEE 802.1X to WEP-104 + if keys are not configured to be used; this fixes IEEE 802.1X mode + with drivers that use this information to configure whether Privacy + bit can be in Beacon frames (e.g., ndiswrapper) + * avoid clearing driver keys if no keys have been configured since last + key clear request; this seems to improve reliability of group key + handshake for ndiswrapper & NDIS driver which seems to be suffering + of some kind of timing issue when the keys are cleared again after + association + * changed driver interface API: + - WPA_SUPPLICANT_DRIVER_VERSION define can be used to determine which + version is being used (now, this is set to 2; previously, it was + not defined) + - pass pointer to private data structure to all calls + - the new API is not backwards compatible; all in-tree driver + interfaces has been converted to the new API + * added support for controlling multiple interfaces (radios) per + wpa_supplicant process; each interface needs to be listed on the + command line (-c, -i, -D arguments) with -N as a separator + (-cwpa1.conf -iwlan0 -Dhostap -N -cwpa2.conf -iath0 -Dmadwifi) + * added a workaround for EAP servers that incorrectly use same Id for + sequential EAP packets + * changed libpcap/libdnet configuration to use .config variable, + CONFIG_DNET_PCAP, instead of requiring Makefile modification + * improved downgrade attack detection in IE verification of msg 3/4: + verify both WPA and RSN IEs, if present, not only the selected one; + reject the AP if an RSN IE is found in msg 3/4, but not in Beacon or + Probe Response frame, and RSN is enabled in wpa_supplicant + configuration + * fixed WPA msg 3/4 processing to allow Key Data field contain other + IEs than just one WPA IE + * added support for FreeBSD and driver interface for the BSD net80211 + layer (CONFIG_DRIVER_BSD=y in .config); please note that some of the + required kernel mods have not yet been committed + * made EAP workarounds configurable; enabled by default, can be + disabled with network block option eap_workaround=0 + +2004-07-17 - v0.2.4 (beginning of 0.2.x stable releases) + * resolved couple of interoperability issues with EAP-PEAPv1 and + Phase 2 (inner EAP) fragment reassembly + * driver_madwifi: fixed WEP key configuration for IEEE 802.1X when the + AP is using non-zero key index for the unicast key and key index zero + for the broadcast key + * driver_hostap: fixed IEEE 802.1X WEP key updates and + re-authentication by allowing unencrypted EAPOL frames when not using + WPA + * added a new driver interface, 'wext', which uses only standard, + driver independent functionality in Linux wireless extensions; + currently, this can be used only for non-WPA IEEE 802.1X mode, but + eventually, this is to be extended to support full WPA/WPA2 once + Linux wireless extensions get support for this + * added support for mode in which the driver is responsible for AP + scanning and selection; this is disabled by default and can be + enabled with global ap_scan=0 variable in wpa_supplicant.conf; + this mode can be used, e.g., with generic 'wext' driver interface to + use wpa_supplicant as IEEE 802.1X Supplicant with any Linux driver + supporting wireless extensions. + * driver_madwifi: fixed WPA2 configuration and scan_ssid=1 (e.g., + operation with an AP that does not include SSID in the Beacon frames) + * added support for new EAP authentication methods: + EAP-TTLS/EAP-OTP, EAP-PEAPv0/OTP, EAP-PEAPv1/OTP, EAP-OTP + * added support for asking one-time-passwords from frontends (e.g., + wpa_cli); this 'otp' command works otherwise like 'password' command, + but the password is used only once and the frontend will be asked for + a new password whenever a request from authenticator requires a + password; this can be used with both EAP-OTP and EAP-GTC + * changed wpa_cli to automatically re-establish connection so that it + does not need to be re-started when wpa_supplicant is terminated and + started again + * improved user data (identity/password/otp) requests through + frontends: process pending EAPOL packets after getting new + information so that full authentication does not need to be + restarted; in addition, send pending requests again whenever a new + frontend is attached + * changed control frontends to use a new directory for socket files to + make it easier for wpa_cli to automatically select between interfaces + and to provide access control for the control interface; + wpa_supplicant.conf: ctrl_interface is now a path + (/var/run/wpa_supplicant is the recommended path) and + ctrl_interface_group can be used to select which group gets access to + the control interface; + wpa_cli: by default, try to connect to the first interface available + in /var/run/wpa_supplicant; this path can be overriden with -p option + and an interface can be selected with -i option (i.e., in most common + cases, wpa_cli does not need to get any arguments) + * added support for LEAP + * added driver interface for Linux ndiswrapper + * added priority option for network blocks in the configuration file; + this allows networks to be grouped based on priority (the scan + results are searched for matches with network blocks in this order) + +2004-06-20 - v0.2.3 + * sort scan results to improve AP selection + * fixed control interface socket removal for some error cases + * improved scan requesting and authentication timeout + * small improvements/bug fixes for EAP-MSCHAPv2, EAP-PEAP, and + TLS processing + * PEAP version can now be forced with phase1="peapver=<ver>" + (mostly for testing; by default, the highest version supported by + both the Supplicant and Authentication Server is selected + automatically) + * added support for madwifi driver (Atheros ar521x) + * added a workaround for cases where AP sets Install Tx/Rx bit for + WPA Group Key messages when pairwise keys are used (without this, + the Group Key would be used for Tx and the AP would drop frames + from the station) + * added GSM SIM/USIM interface for GSM authentication algorithm for + EAP-SIM; this requires pcsc-lite + * added support for ATMEL AT76C5XXx driver + * fixed IEEE 802.1X WEP key derivation in the case where Authenticator + does not include key data in the EAPOL-Key frame (i.e., part of + EAP keying material is used as data encryption key) + * added support for using plaintext and static WEP networks + (key_mgmt=NONE) + +2004-05-31 - v0.2.2 + * added support for new EAP authentication methods: + EAP-TTLS/EAP-MD5-Challenge + EAP-TTLS/EAP-GTC + EAP-TTLS/EAP-MSCHAPv2 + EAP-TTLS/EAP-TLS + EAP-TTLS/MSCHAPv2 + EAP-TTLS/MSCHAP + EAP-TTLS/PAP + EAP-TTLS/CHAP + EAP-PEAP/TLS + EAP-PEAP/GTC + EAP-PEAP/MD5-Challenge + EAP-GTC + EAP-SIM (not yet complete; needs GSM/SIM authentication interface) + * added support for anonymous identity (to be used when identity is + sent in plaintext; real identity will be used within TLS protected + tunnel (e.g., with EAP-TTLS) + * added event messages from wpa_supplicant to frontends, e.g., wpa_cli + * added support for requesting identity and password information using + control interface; in other words, the password for EAP-PEAP or + EAP-TTLS does not need to be included in the configuration file since + a frontand (e.g., wpa_cli) can ask it from the user + * improved RSN pre-authentication to use a candidate list and process + all candidates from each scan; not only one per scan + * fixed RSN IE and WPA IE capabilities field parsing + * ignore Tx bit in GTK IE when Pairwise keys are used + * avoid making new scan requests during IEEE 802.1X negotiation + * use openssl/libcrypto for MD5 and SHA-1 when compiling wpa_supplicant + with TLS support (this replaces the included implementation with + library code to save about 8 kB since the library code is needed + anyway for TLS) + * fixed WPA-PSK only mode when compiled without IEEE 802.1X support + (i.e., without CONFIG_IEEE8021X_EAPOL=y in .config) + +2004-05-06 - v0.2.1 + * added support for internal IEEE 802.1X (actually, IEEE 802.1aa/D6.1) + Supplicant + - EAPOL state machines for Supplicant [IEEE 802.1aa/D6.1] + - EAP peer state machine [draft-ietf-eap-statemachine-02.pdf] + - EAP-MD5 (cannot be used with WPA-RADIUS) + [draft-ietf-eap-rfc2284bis-09.txt] + - EAP-TLS [RFC 2716] + - EAP-MSCHAPv2 (currently used only with EAP-PEAP) + - EAP-PEAP/MSCHAPv2 [draft-josefsson-pppext-eap-tls-eap-07.txt] + [draft-kamath-pppext-eap-mschapv2-00.txt] + (PEAP version 0, 1, and parts of 2; only 0 and 1 are enabled by + default; tested with FreeRADIUS, Microsoft IAS, and Funk Odyssey) + - new configuration file options: eap, identity, password, ca_cert, + client_cert, privatekey, private_key_passwd + - Xsupplicant is not required anymore, but it can be used by + disabling the internal IEEE 802.1X Supplicant with -e command line + option + - this code is not included in the default build; Makefile need to + be edited for this (uncomment lines for selected functionality) + - EAP-TLS and EAP-PEAP require openssl libraries + * use module prefix in debug messages (WPA, EAP, EAP-TLS, ..) + * added support for non-WPA IEEE 802.1X mode with dynamic WEP keys + (i.e., complete IEEE 802.1X/EAP authentication and use IEEE 802.1X + EAPOL-Key frames instead of WPA key handshakes) + * added support for IEEE 802.11i/RSN (WPA2) + - improved PTK Key Handshake + - PMKSA caching, pre-authentication + * fixed wpa_supplicant to ignore possible extra data after WPA + EAPOL-Key packets (this fixes 'Invalid EAPOL-Key MIC when using + TPTK' error from message 3 of 4-Way Handshake in case the AP + includes extra data after the EAPOL-Key) + * added interface for external programs (frontends) to control + wpa_supplicant + - CLI example (wpa_cli) with interactive mode and command line + mode + - replaced SIGUSR1 status/statistics with the new control interface + * made some feature compile time configurable + - .config file for make + - driver interfaces (hostap, hermes, ..) + - EAPOL/EAP functions + +2004-02-15 - v0.2.0 + * Initial version of wpa_supplicant diff --git a/contrib/wpa_supplicant/Makefile b/contrib/wpa_supplicant/Makefile new file mode 100644 index 000000000000..fa912436c86e --- /dev/null +++ b/contrib/wpa_supplicant/Makefile @@ -0,0 +1,385 @@ +ifndef CC +CC=gcc +endif + +ifndef CFLAGS +CFLAGS = -MMD -O2 -Wall -g +endif + +# Include directories for CVS version +CFLAGS += -I../driver/modules -I../utils -I../hostapd + +ALL=wpa_supplicant wpa_passphrase wpa_cli + +all: verify_config $(ALL) + +verify_config: + @if [ ! -r .config ]; then \ + echo 'Building wpa_supplicant requires a configuration file'; \ + echo '(.config). See README for more instructions. You can'; \ + echo 'run "cp defconfig .config" to create an example'; \ + echo 'configuration.'; \ + exit 1; \ + fi + +mkconfig: + @if [ -e .config ]; then \ + echo '.config exists - did not replace it'; \ + exit 1; \ + fi + echo CONFIG_DRIVER_HOSTAP=y >> .config + echo CONFIG_DRIVER_WEXT=y >> .config + echo CONFIG_WIRELESS_EXTENSION=y >> .config + +install: all + mkdir -p $(DESTDIR)/usr/local/sbin/ + for i in $(ALL); do cp $$i $(DESTDIR)/usr/local/sbin/$$i; done + +OBJS = config.o \ + eloop.o common.o md5.o \ + rc4.o sha1.o aes_wrap.o +OBJS_p = wpa_passphrase.o sha1.o md5.o +OBJS_c = wpa_cli.o wpa_ctrl.o + +-include .config + +ifdef CONFIG_EAPOL_TEST +CFLAGS += -Werror -DEAPOL_TEST +endif + +ifdef CONFIG_DRIVER_HOSTAP +CFLAGS += -DCONFIG_DRIVER_HOSTAP +OBJS += driver_hostap.o +CONFIG_WIRELESS_EXTENSION=y +endif + +ifdef CONFIG_DRIVER_WEXT +CFLAGS += -DCONFIG_DRIVER_WEXT +CONFIG_WIRELESS_EXTENSION=y +endif + +ifdef CONFIG_DRIVER_PRISM54 +CFLAGS += -DCONFIG_DRIVER_PRISM54 +OBJS += driver_prism54.o +CONFIG_WIRELESS_EXTENSION=y +endif + +ifdef CONFIG_DRIVER_HERMES +CFLAGS += -DCONFIG_DRIVER_HERMES +OBJS += driver_hermes.o +CONFIG_WIRELESS_EXTENSION=y +endif + +ifdef CONFIG_DRIVER_MADWIFI +CFLAGS += -DCONFIG_DRIVER_MADWIFI +OBJS += driver_madwifi.o +CONFIG_WIRELESS_EXTENSION=y +endif + +ifdef CONFIG_DRIVER_ATMEL +CFLAGS += -DCONFIG_DRIVER_ATMEL +OBJS += driver_atmel.o +CONFIG_WIRELESS_EXTENSION=y +endif + +ifdef CONFIG_DRIVER_NDISWRAPPER +CFLAGS += -DCONFIG_DRIVER_NDISWRAPPER +OBJS += driver_ndiswrapper.o +CONFIG_WIRELESS_EXTENSION=y +endif + +ifdef CONFIG_DRIVER_BROADCOM +CFLAGS += -DCONFIG_DRIVER_BROADCOM +OBJS += driver_broadcom.o +endif + +ifdef CONFIG_DRIVER_IPW +CFLAGS += -DCONFIG_DRIVER_IPW +OBJS += driver_ipw.o +CONFIG_WIRELESS_EXTENSION=y +endif + +ifdef CONFIG_DRIVER_BSD +CFLAGS += -DCONFIG_DRIVER_BSD +OBJS += driver_bsd.o +CONFIG_DNET_PCAP=y +endif + +ifdef CONFIG_DRIVER_NDIS +CFLAGS += -DCONFIG_DRIVER_NDIS +OBJS += driver_ndis.o driver_ndis_.o +CONFIG_DNET_PCAP=y +CONFIG_WINPCAP=y +endif + +ifdef CONFIG_DRIVER_TEST +CFLAGS += -DCONFIG_DRIVER_TEST +OBJS += driver_test.o +endif + +ifdef CONFIG_DNET_PCAP +CFLAGS += -DUSE_DNET_PCAP +ifdef CONFIG_WINPCAP +CFLAGS += -DCONFIG_WINPCAP +LIBS += -lwpcap -lpacket +LIBS_w += -lwpcap +else +LIBS += -ldnet -lpcap +endif +endif + +ifdef CONFIG_EAP_TLS +# EAP-TLS +CFLAGS += -DEAP_TLS +OBJS += eap_tls.o +TLS_FUNCS=y +CONFIG_IEEE8021X_EAPOL=y +endif + +ifdef CONFIG_EAP_PEAP +# EAP-PEAP +CFLAGS += -DEAP_PEAP +OBJS += eap_peap.o +TLS_FUNCS=y +CONFIG_EAP_MSCHAPV2=y +CONFIG_IEEE8021X_EAPOL=y +CONFIG_EAP_TLV=y +endif + +ifdef CONFIG_EAP_TTLS +# EAP-TTLS +CFLAGS += -DEAP_TTLS +OBJS += eap_ttls.o +MS_FUNCS=y +TLS_FUNCS=y +CONFIG_EAP_MD5=y +CONFIG_IEEE8021X_EAPOL=y +endif + +ifdef CONFIG_EAP_MD5 +# EAP-MD5 (also used by EAP-TTLS) +CFLAGS += -DEAP_MD5 +OBJS += eap_md5.o +CONFIG_IEEE8021X_EAPOL=y +endif + +# backwards compatibility for old spelling +ifdef CONFIG_MSCHAPV2 +CONFIG_EAP_MSCHAPV2=y +endif + +ifdef CONFIG_EAP_MSCHAPV2 +# EAP-MSCHAPv2 (also used by EAP-PEAP) +CFLAGS += -DEAP_MSCHAPv2 +OBJS += eap_mschapv2.o +MS_FUNCS=y +CONFIG_IEEE8021X_EAPOL=y +endif + +ifdef CONFIG_EAP_GTC +# EAP-GTC (also used by EAP-PEAP) +CFLAGS += -DEAP_GTC +OBJS += eap_gtc.o +CONFIG_IEEE8021X_EAPOL=y +endif + +ifdef CONFIG_EAP_OTP +# EAP-OTP +CFLAGS += -DEAP_OTP +OBJS += eap_otp.o +CONFIG_IEEE8021X_EAPOL=y +endif + +ifdef CONFIG_EAP_SIM +# EAP-SIM +CFLAGS += -DEAP_SIM +OBJS += eap_sim.o +CONFIG_IEEE8021X_EAPOL=y +CONFIG_EAP_SIM_COMMON=y +endif + +ifdef CONFIG_EAP_LEAP +# EAP-LEAP +CFLAGS += -DEAP_LEAP +OBJS += eap_leap.o +MS_FUNCS=y +CONFIG_IEEE8021X_EAPOL=y +endif + +ifdef CONFIG_EAP_PSK +# EAP-PSK +CFLAGS += -DEAP_PSK +OBJS += eap_psk.o +CONFIG_IEEE8021X_EAPOL=y +endif + +ifdef CONFIG_EAP_AKA +# EAP-AKA +CFLAGS += -DEAP_AKA +OBJS += eap_aka.o +CONFIG_IEEE8021X_EAPOL=y +CONFIG_EAP_SIM_COMMON=y +endif + +ifdef CONFIG_EAP_SIM_COMMON +OBJS += eap_sim_common.o +endif + +ifdef CONFIG_EAP_TLV +# EAP-TLV +CFLAGS += -DEAP_TLV +OBJS += eap_tlv.o +endif + +ifdef CONFIG_EAP_FAST +# EAP-FAST +CFLAGS += -DEAP_FAST +OBJS += eap_fast.o +TLS_FUNCS=y +endif + +ifdef CONFIG_IEEE8021X_EAPOL +# IEEE 802.1X/EAPOL state machines (e.g., for RADIUS authentication) +CFLAGS += -DIEEE8021X_EAPOL +OBJS += eapol_sm.o eap.o +endif + +ifdef CONFIG_PCSC +# PC/SC interface for smartcards (USIM, GSM SIM) +CFLAGS += -DPCSC_FUNCS -I/usr/include/PCSC +OBJS += pcsc_funcs.o +# -lpthread may not be needed depending on how pcsc-lite was configured +LIBS += -lpcsclite -lpthread +endif + +ifdef TLS_FUNCS +# Shared TLS functions (needed for EAP_TLS, EAP_PEAP, and EAP_TTLS) +CFLAGS += -DEAP_TLS_FUNCS +OBJS += eap_tls_common.o tls_openssl.o +LIBS += -lssl -lcrypto +LIBS_p += -lcrypto +else +OBJS += tls_none.o +endif + +ifdef CONFIG_PKCS12 +CFLAGS += -DPKCS12_FUNCS +endif + +ifdef MS_FUNCS +ifndef TLS_FUNCS +LIBS += -lcrypto +endif +OBJS += ms_funcs.o crypto.o +endif + +ifdef CONFIG_WIRELESS_EXTENSION +CFLAGS += -DCONFIG_WIRELESS_EXTENSION +OBJS += driver_wext.o +endif + +ifdef CONFIG_CTRL_IFACE +CFLAGS += -DCONFIG_CTRL_IFACE +OBJS += ctrl_iface.o +endif + +ifdef CONFIG_XSUPPLICANT_IFACE +CFLAGS += -DCONFIG_XSUPPLICANT_IFACE +endif + +ifdef CONFIG_READLINE +CFLAGS += -DCONFIG_READLINE +LIBS_c += -lncurses -lreadline +endif + +ifdef CONFIG_NATIVE_WINDOWS +CFLAGS += -DCONFIG_NATIVE_WINDOWS -DCONFIG_CTRL_IFACE_UDP +LIBS += -lws2_32 -lgdi32 +LIBS_c += -lws2_32 +endif + +OBJS_t := $(OBJS) eapol_test.o radius.o radius_client.o +OBJS_t2 := $(OBJS) preauth_test.o l2_packet.o +OBJS += wpa_supplicant.o wpa.o l2_packet.o drivers.o + +wpa_supplicant: .config $(OBJS) + $(CC) -o wpa_supplicant $(OBJS) $(LIBS) + +eapol_test: .config $(OBJS_t) + $(CC) -o eapol_test $(OBJS_t) $(LIBS) + +preauth_test: .config $(OBJS_t2) + $(CC) -o preauth_test $(OBJS_t2) $(LIBS) + +wpa_passphrase: $(OBJS_p) + $(CC) -o wpa_passphrase $(OBJS_p) $(LIBS_p) + +wpa_cli: $(OBJS_c) + $(CC) -o wpa_cli $(OBJS_c) $(LIBS_c) + +win_if_list: win_if_list.c + $(CC) -o $@ win_if_list.c $(CFLAGS) $(LIBS_w) + +# parameters for Microsoft Visual C++ Toolkit 2003 compiler +CL=cl +CLDIR=C:\Program Files\Microsoft Visual C++ Toolkit 2003 +PSDKDIR=C:\Program Files\Microsoft Platform SDK for Windows XP SP2 +CLFLAGS=-O +CLLIBS=wbemuuid.lib libcmt.lib kernel32.lib uuid.lib ole32.lib oleaut32.lib \ + ws2_32.lib + +ndis_events: ndis_events.cpp + INCLUDE="$(CLDIR)\include;$(PSDKDIR)\Include" \ + LIB="$(CLDIR)\lib;$(PSDKDIR)\Lib" \ + $(CL) $(CLFLAGS) -o ndis_events.exe ndis_events.cpp \ + /link -nodefaultlib $(CLLIBS) + +wpa_supplicant.exe: wpa_supplicant + mv -f $< $@ +wpa_cli.exe: wpa_cli + mv -f $< $@ +wpa_passphrase.exe: wpa_passphrase + mv -f $< $@ +win_if_list.exe: win_if_list + mv -f $< $@ + +WINALL=wpa_supplicant.exe wpa_cli.exe wpa_passphrase.exe win_if_list.exe + +windows-bin: $(WINALL) + $(STRIP) $(WINALL) + +TEST_SRC_MS_FUNCS = ms_funcs.c crypto.c sha1.c md5.c +test-ms_funcs: $(TEST_SRC_MS_FUNCS) + $(CC) -o test-ms_funcs -Wall -Werror $(TEST_SRC_MS_FUNCS) \ + -DTEST_MAIN_MS_FUNCS -lcrypto -I../hostapd + ./test-ms_funcs + rm test-ms_funcs + +TEST_SRC_SHA1 = sha1.c +test-sha1: $(TEST_SRC_SHA1) + $(CC) -o test-sha1 -Wall -Werror $(TEST_SRC_SHA1) \ + -DTEST_MAIN -I../hostad + ./test-sha1 + rm test-sha1 + +TEST_SRC_AES_WRAP = aes_wrap.c +test-aes_wrap: $(TEST_SRC_AES_WRAP) + $(CC) -o test-aes_wrap -Wall -Werror $(TEST_SRC_AES_WRAP) \ + -DTEST_MAIN -I../hostad + ./test-aes_wrap + rm test-aes_wrap + +TEST_SRC_EAP_SIM_COMMON = eap_sim_common.c sha1.c md5.c \ + aes_wrap.c common.c +test-eap_sim_common: $(TEST_SRC_EAP_SIM_COMMON) + $(CC) -o test-eap_sim_common -Wall -Werror $(TEST_SRC_EAP_SIM_COMMON) \ + -DTEST_MAIN_EAP_SIM_COMMON -I../hostapd + ./test-eap_sim_common + rm test-eap_sim_common + +tests: test-ms_funcs test-sha1 test-aes_wrap test-eap_sim_common + +clean: + rm -f core *~ *.o *.d $(ALL) $(WINALL) + +-include $(OBJS:%.o=%.d) diff --git a/contrib/wpa_supplicant/README b/contrib/wpa_supplicant/README new file mode 100644 index 000000000000..7b5f5476a160 --- /dev/null +++ b/contrib/wpa_supplicant/README @@ -0,0 +1,860 @@ +WPA Supplicant +============== + +Copyright (c) 2003-2005, Jouni Malinen <jkmaline@cc.hut.fi> and +contributors +All Rights Reserved. + +This program is dual-licensed under both the GPL version 2 and BSD +license. Either license may be used at your option. Please note that +some of the driver interface implementations (driver_*.c) may be +licensed under a different license. + + + +License +------- + +GPL v2: + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License version 2 as +published by the Free Software Foundation. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +(this copy of the license is in COPYING file) + + +Alternatively, this software may be distributed under the terms of BSD +license: + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +3. Neither the name(s) of the above-listed copyright holder(s) nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + +Features +-------- + +Supported WPA/IEEE 802.11i features: +- WPA-PSK ("WPA-Personal") +- WPA with EAP (e.g., with RADIUS authentication server) ("WPA-Enterprise") + Following authentication methods are supported with an integrate IEEE 802.1X + Supplicant: + * EAP-TLS + * EAP-PEAP/MSCHAPv2 (both PEAPv0 and PEAPv1) + * EAP-PEAP/TLS (both PEAPv0 and PEAPv1) + * EAP-PEAP/GTC (both PEAPv0 and PEAPv1) + * EAP-PEAP/OTP (both PEAPv0 and PEAPv1) + * EAP-PEAP/MD5-Challenge (both PEAPv0 and PEAPv1) + * EAP-TTLS/EAP-MD5-Challenge + * EAP-TTLS/EAP-GTC + * EAP-TTLS/EAP-OTP + * EAP-TTLS/EAP-MSCHAPv2 + * EAP-TTLS/EAP-TLS + * EAP-TTLS/MSCHAPv2 + * EAP-TTLS/MSCHAP + * EAP-TTLS/PAP + * EAP-TTLS/CHAP + * EAP-SIM + * EAP-AKA + * EAP-PSK + * LEAP (note: requires special support from the driver for IEEE 802.11 + authentication) + (following methods are supported, but since they do not generate keying + material, they cannot be used with WPA or IEEE 802.1X WEP keying) + * EAP-MD5-Challenge + * EAP-MSCHAPv2 + * EAP-GTC + * EAP-OTP + Alternatively, an external program, e.g., Xsupplicant, can be used for EAP + authentication. +- key management for CCMP, TKIP, WEP104, WEP40 +- RSN/WPA2 (IEEE 802.11i) + * pre-authentication + * PMKSA caching + + + +Requirements +------------ + +Current hardware/software requirements: +- Linux kernel 2.4.x or 2.6.x with Linux Wireless Extensions v15 or newer +- FreeBSD 6-CURRENT +- Microsoft Windows with WinPcap (at least WinXP, may work with other versions) +- drivers: + Host AP driver for Prism2/2.5/3 (development snapshot/v0.2.x) + (http://hostap.epitest.fi/) + Driver need to be set in Managed mode ('iwconfig wlan0 mode managed'). + Please note that station firmware version needs to be 1.7.0 or newer + to work in WPA mode. + + Linuxant DriverLoader (http://www.linuxant.com/driverloader/) + with Windows NDIS driver for your wlan card supporting WPA. + + Agere Systems Inc. Linux Driver + (http://www.agere.com/support/drivers/) + Please note that the driver interface file (driver_hermes.c) and + hardware specific include files are not included in the + wpa_supplicant distribution. You will need to copy these from the + source package of the Agere driver. + + madwifi driver for cards based on Atheros chip set (ar521x) + (http://sourceforge.net/projects/madwifi/) + Please note that you will need to modify the wpa_supplicant .config + file to use the correct path for the madwifi driver root directory + (CFLAGS += -I../madwifi/wpa line in example defconfig). + + ATMEL AT76C5XXx driver for USB and PCMCIA cards + (http://atmelwlandriver.sourceforge.net/). + + Linux ndiswrapper (http://ndiswrapper.sourceforge.net/) with + Windows NDIS driver. + + Broadcom wl.o driver + This is a generic Linux driver for Broadcom IEEE 802.11a/g cards. + However, it is proprietary driver that is not publicly available + except for couple of exceptions, mainly Broadcom-based APs/wireless + routers that use Linux. The driver binary can be downloaded, e.g., + from Linksys support site (http://www.linksys.com/support/gpl.asp) + for Linksys WRT54G. The GPL tarball includes cross-compiler and + the needed header file, wlioctl.h, for compiling wpa_supplicant. + This driver support in wpa_supplicant is expected to work also with + other devices based on Broadcom driver (assuming the driver includes + client mode support). + + Intel ipw2100 driver + (http://sourceforge.net/projects/ipw2100/) + + Intel ipw2200 driver + (http://sourceforge.net/projects/ipw2200/) + + In theory, any driver that supports Linux wireless extensions can be + used with IEEE 802.1X (i.e., not WPA) when using ap_scan=0 option in + configuration file. + + BSD net80211 layer (e.g., Atheros driver) + At the moment, this is for FreeBSD 6-CURRENT branch. + + Windows NDIS + The current Windows port requires WinPcap (http://winpcap.polito.it/). + See README-Windows.txt for more information. + +wpa_supplicant was designed to be portable for different drivers and +operating systems. Hopefully, support for more wlan cards and OSes will be +added in the future. See developer.txt for more information about the +design of wpa_supplicant and porting to other drivers. One main goal +is to add full WPA/WPA2 support to Linux wireless extensions to allow +new drivers to be supported without having to implement new +driver-specific interface code in wpa_supplicant. + +Optional libraries for layer2 packet processing: +- libpcap (tested with 0.7.2, most relatively recent versions assumed to work, + this is likely to be available with most distributions, + http://tcpdump.org/) +- libdnet (tested with v1.4, most versions assumed to work, + http://libdnet.sourceforge.net/) + +These libraries are _not_ used in the default Linux build. Instead, +internal Linux specific implementation is used. libpcap/libdnet are +more portable and they can be used by adding CONFIG_DNET_PCAP=y into +.config. They may also be selected automatically for other operating +systems. + + +Optional libraries for EAP-TLS, EAP-PEAP, and EAP-TTLS: +- openssl (tested with 0.9.7c and 0.9.7d, assumed to work with most + relatively recent versions; this is likely to be available with most + distributions, http://www.openssl.org/) + +This library is only needed when EAP-TLS, EAP-PEAP, or EAP-TTLS +support is enabled. WPA-PSK mode does not require this or EAPOL/EAP +implementation. A configuration file, .config, for compilation is +needed to enable IEEE 802.1X/EAPOL and EAP methods. Note that EAP-MD5, +EAP-GTC, EAP-OTP, and EAP-MSCHAPV2 cannot be used alone with WPA, so +they should only be enabled if testing the EAPOL/EAP state +machines. However, there can be used as inner authentication +algorithms with EAP-PEAP and EAP-TTLS. + +See Building and installing section below for more detailed +information about the wpa_supplicant build time configuration. + + + +WPA +--- + +The original security mechanism of IEEE 802.11 standard was not +designed to be strong and has proven to be insufficient for most +networks that require some kind of security. Task group I (Security) +of IEEE 802.11 working group (http://www.ieee802.org/11/) has worked +to address the flaws of the base standard and has in practice +completed its work in May 2004. The IEEE 802.11i amendment to the IEEE +802.11 standard was approved in June 2004 and this amendment is likely +to be published in July 2004. + +Wi-Fi Alliance (http://www.wi-fi.org/) used a draft version of the +IEEE 802.11i work (draft 3.0) to define a subset of the security +enhancements that can be implemented with existing wlan hardware. This +is called Wi-Fi Protected Access<TM> (WPA). This has now become a +mandatory component of interoperability testing and certification done +by Wi-Fi Alliance. Wi-Fi provides information about WPA at its web +site (http://www.wi-fi.org/OpenSection/protected_access.asp). + +IEEE 802.11 standard defined wired equivalent privacy (WEP) algorithm +for protecting wireless networks. WEP uses RC4 with 40-bit keys, +24-bit initialization vector (IV), and CRC32 to protect against packet +forgery. All these choices have proven to be insufficient: key space is +too small against current attacks, RC4 key scheduling is insufficient +(beginning of the pseudorandom stream should be skipped), IV space is +too small and IV reuse makes attacks easier, there is no replay +protection, and non-keyed authentication does not protect against bit +flipping packet data. + +WPA is an intermediate solution for the security issues. It uses +Temporal Key Integrity Protocol (TKIP) to replace WEP. TKIP is a +compromise on strong security and possibility to use existing +hardware. It still uses RC4 for the encryption like WEP, but with +per-packet RC4 keys. In addition, it implements replay protection, +keyed packet authentication mechanism (Michael MIC). + +Keys can be managed using two different mechanisms. WPA can either use +an external authentication server (e.g., RADIUS) and EAP just like +IEEE 802.1X is using or pre-shared keys without need for additional +servers. Wi-Fi calls these "WPA-Enterprise" and "WPA-Personal", +respectively. Both mechanisms will generate a master session key for +the Authenticator (AP) and Supplicant (client station). + +WPA implements a new key handshake (4-Way Handshake and Group Key +Handshake) for generating and exchanging data encryption keys between +the Authenticator and Supplicant. This handshake is also used to +verify that both Authenticator and Supplicant know the master session +key. These handshakes are identical regardless of the selected key +management mechanism (only the method for generating master session +key changes). + + + +IEEE 802.11i / WPA2 +------------------- + +The design for parts of IEEE 802.11i that were not included in WPA has +finished (May 2004) and this amendment to IEEE 802.11 was approved in +June 2004. Wi-Fi Alliance is using the final IEEE 802.11i as a new +version of WPA called WPA2. This includes, e.g., support for more +robust encryption algorithm (CCMP: AES in Counter mode with CBC-MAC) +to replace TKIP and optimizations for handoff (reduced number of +messages in initial key handshake, pre-authentication, and PMKSA caching). + +Some wireless LAN vendors are already providing support for CCMP in +their WPA products. There is no "official" interoperability +certification for CCMP and/or mixed modes using both TKIP and CCMP, so +some interoperability issues can be expected even though many +combinations seem to be working with equipment from different vendors. +Certification for WPA2 is likely to start during the second half of +2004. + + + +wpa_supplicant +-------------- + +wpa_supplicant is an implementation of the WPA Supplicant component, +i.e., the part that runs in the client stations. It implements WPA key +negotiation with a WPA Authenticator and EAP authentication with +Authentication Server. In addition, it controls the roaming and IEEE +802.11 authentication/association of the wlan driver. + +wpa_supplicant is designed to be a "daemon" program that runs in the +background and acts as the backend component controlling the wireless +connection. wpa_supplicant supports separate frontend programs and an +example text-based frontend, wpa_cli, is included with wpa_supplicant. + +Following steps are used when associating with an AP using WPA: + +- wpa_supplicant requests the kernel driver to scan neighboring BSSes +- wpa_supplicant selects a BSS based on its configuration +- wpa_supplicant requests the kernel driver to associate with the chosen + BSS +- If WPA-EAP: integrated IEEE 802.1X Supplicant or external Xsupplicant + completes EAP authentication with the authentication server (proxied + by the Authenticator in the AP) +- If WPA-EAP: master key is received from the IEEE 802.1X Supplicant +- If WPA-PSK: wpa_supplicant uses PSK as the master session key +- wpa_supplicant completes WPA 4-Way Handshake and Group Key Handshake + with the Authenticator (AP) +- wpa_supplicant configures encryption keys for unicast and broadcast +- normal data packets can be transmitted and received + + + +Building and installing +----------------------- + +In order to be able to build wpa_supplicant, you will first need to +select which parts of it will be included. This is done by creating a +build time configuration file, .config, in the wpa_supplicant root +directory. Configuration options are text lines using following +format: CONFIG_<option>=y. Lines starting with # are considered +comments and are ignored. See defconfig file for example configuration +and list of available option. + +The build time configuration can be used to select only the needed +features and limit the binary size and requirements for external +libraries. The main configuration parts are the selection of which +driver interfaces (e.g., hostap, madwifi, ..) and which authentication +methods (e.g., EAP-TLS, EAP-PEAP, ..) are included. + +Following build time configuration options are used to control IEEE +802.1X/EAPOL and EAP state machines and all EAP methods. Including +TLS, PEAP, or TTLS will require linking wpa_supplicant with openssl +library for TLS implementation. + +CONFIG_IEEE8021X_EAPOL=y +CONFIG_EAP_MD5=y +CONFIG_EAP_MSCHAPV2=y +CONFIG_EAP_TLS=y +CONFIG_EAP_PEAP=y +CONFIG_EAP_TTLS=y +CONFIG_EAP_GTC=y +CONFIG_EAP_OTP=y +CONFIG_EAP_SIM=y +CONFIG_EAP_AKA=y +CONFIG_EAP_PSK=y +CONFIG_EAP_LEAP=y + +Following option can be used to include GSM SIM/USIM interface for GSM/UMTS +authentication algorithm (for EAP-SIM/EAP-AKA). This requires pcsc-lite +(http://www.linuxnet.com/) for smart card access. + +CONFIG_PCSC=y + +Following option can be used to replace the native Linux packet socket +interface with libpcap/libdnet. + +CONFIG_DNET_PCAP=y + +Following options can be added to .config to select which driver +interfaces are included. Prism54.org driver is not yet complete and +Hermes driver interface needs to be downloaded from Agere (see above). +Most Linux driver need to include CONFIG_WIRELESS_EXTENSION. + +CONFIG_WIRELESS_EXTENSION=y +CONFIG_DRIVER_HOSTAP=y +CONFIG_DRIVER_PRISM54=y +CONFIG_DRIVER_HERMES=y +CONFIG_DRIVER_MADWIFI=y +CONFIG_DRIVER_ATMEL=y +CONFIG_DRIVER_WEXT=y +CONFIG_DRIVER_NDISWRAPPER=y +CONFIG_DRIVER_BROADCOM=y +CONFIG_DRIVER_IPW=y +CONFIG_DRIVER_BSD=y +CONFIG_DRIVER_NDIS=y + +Following example includes all features and driver interfaces that are +included in the wpa_supplicant package: + +CONFIG_DRIVER_HOSTAP=y +CONFIG_DRIVER_PRISM54=y +CONFIG_DRIVER_HERMES=y +CONFIG_DRIVER_MADWIFI=y +CONFIG_DRIVER_ATMEL=y +CONFIG_DRIVER_WEXT=y +CONFIG_DRIVER_NDISWRAPPER=y +CONFIG_DRIVER_BROADCOM=y +CONFIG_DRIVER_IPW=y +CONFIG_DRIVER_BSD=y +CONFIG_DRIVER_NDIS=y +CONFIG_WIRELESS_EXTENSION=y +CONFIG_IEEE8021X_EAPOL=y +CONFIG_EAP_MD5=y +CONFIG_EAP_MSCHAPV2=y +CONFIG_EAP_TLS=y +CONFIG_EAP_PEAP=y +CONFIG_EAP_TTLS=y +CONFIG_EAP_GTC=y +CONFIG_EAP_OTP=y +CONFIG_EAP_SIM=y +CONFIG_EAP_AKA=y +CONFIG_EAP_PSK=y +CONFIG_EAP_LEAP=y +CONFIG_PCSC=y + +EAP-PEAP and EAP-TTLS will automatically include configured EAP +methods (MD5, OTP, GTC, MSCHAPV2) for inner authentication selection. + + +After you have created a configuration file, you can build +wpa_supplicant and wpa_cli with 'make' command. You may then install +the binaries to a suitable system directory, e.g., /usr/local/bin. + +Example commands: + +# build wpa_supplicant and wpa_cli +make +# install binaries (this may need root privileges) +cp wpa_cli wpa_supplicant /usr/local/bin + + +You will need to make a configuration file, e.g., +/etc/wpa_supplicant.conf, with network configuration for the networks +you are going to use. Configuration file section below includes +explanation fo the configuration file format and includes various +examples. Once the configuration is ready, you can test whether the +configuration work by first running wpa_supplicant with following +command to start it on foreground with debugging enabled: + +wpa_supplicant -iwlan0 -c/etc/wpa_supplicant.conf -d + +Assuming everything goes fine, you can start using following command +to start wpa_supplicant on background without debugging: + +wpa_supplicant -iwlan0 -c/etc/wpa_supplicant.conf -B + +Please note that if you included more than one driver interface in the +build time configuration (.config), you may need to specify which +interface to use by including -D<driver name> option on the command +line. See following section for more details on command line options +for wpa_supplicant. + + + +Command line options +-------------------- + +usage: + wpa_supplicant [-BddehLqqvw] -i<ifname> -c<config file> [-D<driver>] \ + [-N -i<ifname> -c<conf> [-D<driver>] ...] + +options: + -B = run daemon in the background + -d = increase debugging verbosity (-dd even more) + -K = include keys (passwords, etc.) in debug output + -t = include timestamp in debug messages + -e = use external IEEE 802.1X Supplicant (e.g., xsupplicant) + (this disables the internal Supplicant) + -h = show this help text + -L = show license (GPL and BSD) + -q = decrease debugging verbosity (-qq even less) + -v = show version + -w = wait for interface to be added, if needed + -N = start describing new interface + +drivers: + hostap = Host AP driver (Intersil Prism2/2.5/3) [default] + (this can also be used with Linuxant DriverLoader) + prism54 = Prism54.org driver (Intersil Prism GT/Duette/Indigo) + not yet fully implemented + hermes = Agere Systems Inc. driver (Hermes-I/Hermes-II) + madwifi = MADWIFI 802.11 support (Atheros, etc.) + atmel = ATMEL AT76C5XXx (USB, PCMCIA) + wext = Linux wireless extensions (generic) + ndiswrapper = Linux ndiswrapper + broadcom = Broadcom wl.o driver + ipw = Intel ipw2100/2200 driver + bsd = BSD 802.11 support (Atheros, etc.) + ndis = Windows NDIS driver + +In most common cases, wpa_supplicant is started with + +wpa_supplicant -Bw -c/etc/wpa_supplicant.conf -iwlan0 + +This makes the process fork into background and wait for the wlan0 +interface if it is not available at startup time. + +The easiest way to debug problems, and to get debug log for bug +reports, is to start wpa_supplicant on foreground with debugging +enabled: + +wpa_supplicant -c/etc/wpa_supplicant.conf -iwlan0 -d + + +wpa_supplicant can control multiple interfaces (radios) either by +running one process for each interface separately or by running just +one process and list of options at command line. Each interface is +separated with -N argument. As an example, following command would +start wpa_supplicant for two interfaces: + +wpa_supplicant \ + -c wpa1.conf -i wlan0 -D hostap -N \ + -c wpa2.conf -i ath0 -D madwifi + + +Configuration file +------------------ + +wpa_supplicant is configured using a text file that lists all accepted +networks and security policies, including pre-shared keys. See +example configuration file, wpa_supplicant.conf, for detailed +information about the configuration format and supported fields. + +Changes to configuration file can be reloaded be sending SIGHUP signal +to wpa_supplicant ('killall -HUP wpa_supplicant'). Similarly, +reloading can be triggered with 'wpa_cli reconfigure' command. + +Configuration file can include one or more network blocks, e.g., one +for each used SSID. wpa_supplicant will automatically select the best +betwork based on the order of network blocks in the configuration +file, network security level (WPA/WPA2 is prefered), and signal +strength. + +Example configuration files for some common configurations: + +1) WPA-Personal (PSK) as home network and WPA-Enterprise with EAP-TLS as work + network + +# allow frontend (e.g., wpa_cli) to be used by all users in 'wheel' group +ctrl_interface=/var/run/wpa_supplicant +ctrl_interface_group=wheel +# +# home network; allow all valid ciphers +network={ + ssid="home" + scan_ssid=1 + key_mgmt=WPA-PSK + psk="very secret passphrase" +} +# +# work network; use EAP-TLS with WPA; allow only CCMP and TKIP ciphers +network={ + ssid="work" + scan_ssid=1 + key_mgmt=WPA-EAP + pairwise=CCMP TKIP + group=CCMP TKIP + eap=TLS + identity="user@example.com" + ca_cert="/etc/cert/ca.pem" + client_cert="/etc/cert/user.pem" + private_key="/etc/cert/user.prv" + private_key_passwd="password" +} + + +2) WPA-RADIUS/EAP-PEAP/MSCHAPv2 with RADIUS servers that use old peaplabel + (e.g., Funk Odyssey and SBR, Meetinghouse Aegis, Interlink RAD-Series) + +ctrl_interface=/var/run/wpa_supplicant +ctrl_interface_group=wheel +network={ + ssid="example" + scan_ssid=1 + key_mgmt=WPA-EAP + eap=PEAP + identity="user@example.com" + password="foobar" + ca_cert="/etc/cert/ca.pem" + phase1="peaplabel=0" + phase2="auth=MSCHAPV2" +} + + +3) EAP-TTLS/EAP-MD5-Challenge configuration with anonymous identity for the + unencrypted use. Real identity is sent only within an encrypted TLS tunnel. + +ctrl_interface=/var/run/wpa_supplicant +ctrl_interface_group=wheel +network={ + ssid="example" + scan_ssid=1 + key_mgmt=WPA-EAP + eap=TTLS + identity="user@example.com" + anonymous_identity="anonymous@example.com" + password="foobar" + ca_cert="/etc/cert/ca.pem" + phase2="auth=MD5" +} + + +4) IEEE 802.1X (i.e., no WPA) with dynamic WEP keys (require both unicast and + broadcast); use EAP-TLS for authentication + +ctrl_interface=/var/run/wpa_supplicant +ctrl_interface_group=wheel +network={ + ssid="1x-test" + scan_ssid=1 + key_mgmt=IEEE8021X + eap=TLS + identity="user@example.com" + ca_cert="/etc/cert/ca.pem" + client_cert="/etc/cert/user.pem" + private_key="/etc/cert/user.prv" + private_key_passwd="password" + eapol_flags=3 +} + + +5) Catch all example that allows more or less all configuration modes. The + configuration options are used based on what security policy is used in the + selected SSID. This is mostly for testing and is not recommended for normal + use. + +ctrl_interface=/var/run/wpa_supplicant +ctrl_interface_group=wheel +network={ + ssid="example" + scan_ssid=1 + key_mgmt=WPA-EAP WPA-PSK IEEE8021X NONE + pairwise=CCMP TKIP + group=CCMP TKIP WEP104 WEP40 + psk="very secret passphrase" + eap=TTLS PEAP TLS + identity="user@example.com" + password="foobar" + ca_cert="/etc/cert/ca.pem" + client_cert="/etc/cert/user.pem" + private_key="/etc/cert/user.prv" + private_key_passwd="password" + phase1="peaplabel=0" + ca_cert2="/etc/cert/ca2.pem" + client_cert2="/etc/cer/user.pem" + private_key2="/etc/cer/user.prv" + private_key2_passwd="password" +} + + + +Certificates +------------ + +Some EAP authentication methods require use of certificates. EAP-TLS +uses both server side and client certificates whereas EAP-PEAP and +EAP-TTLS only require the server side certificate. When client +certificate is used, a matching private key file has to also be +included in configuration. If the private key uses a passphrase, this +has to be configured in wpa_supplicant.conf ("private_key_passwd"). + +wpa_supplicant supports X.509 certificates in PEM and DER +formats. User certificate and private key can be included in the same +file. + +If the user certificate and private key is received in PKCS#12/PFX +format, they need to be converted to suitable PEM/DER format for +wpa_supplicant. This can be done, e.g., with following commands: + +# convert client certificate and private key to PEM format +openssl pkcs12 -in example.pfx -out user.pem -clcerts +# convert CA certificate (if included in PFX file) to PEM format +openssl pkcs12 -in example.pfx -out ca.pem -cacerts -nokeys + + + +wpa_cli +------- + +wpa_cli is a text-based frontend program for interacting with +wpa_supplicant. It is used to query current status, change +configuration, trigger events, and request interactive user input. + +wpa_cli can show the current authentication status, selected security +mode, dot11 and dot1x MIBs, etc. In addition, it can configuring some +variables like EAPOL state machine parameters and trigger events like +reassociation and IEEE 802.1X logoff/logon. wpa_cli provides a user +interface to request authentication information, like username and +password, if these are not included in the configuration. This can be +used to implement, e.g., one-time-passwords or generic token card +authentication where the authentication is based on a +challenge-response that uses an external device for generating the +response. + +The control interface of wpa_supplicant can be configured to allow +non-root user access (ctrl_interface_group in the configuration +file). This makes it possible to run wpa_cli with a normal user +account. + +wpa_cli supports two modes: interactive and command line. Both modes +share the same command set and the main difference is in interactive +mode providing access to unsolicited messages (event messages, +username/password requests). + +Interactive mode is started when wpa_cli is executed without including +the command as a command line parameter. Commands are then entered on +the wpa_cli prompt. In command line mode, the same commands are +entered as command line arguments for wpa_cli. + + +Interactive authentication parameters request + +When wpa_supplicant need authentication parameters, like username and +password, which are not present in the configuration file, it sends a +request message to all attached frontend programs, e.g., wpa_cli in +interactive mode. wpa_cli shows these requests with +"CTRL-REQ-<type>-<id>:<text>" prefix. <type> is IDENTITY, PASSWORD, or +OTP (one-time-password). <id> is a unique identifier for the current +network. <text> is description of the request. In case of OTP request, +it includes the challenge from the authentication server. + +The reply to these requests can be given with 'identity', 'password', +and 'otp' commands. <id> needs to be copied from the the matching +request. 'password' and 'otp' commands can be used regardless of +whether the request was for PASSWORD or OTP. The main difference +between these two commands is that values given with 'password' are +remembered as long as wpa_supplicant is running whereas values given +with 'otp' are used only once and then forgotten, i.e., wpa_supplicant +will ask frontend for a new value for every use. This can be used to +implement one-time-password lists and generic token card -based +authentication. + +Example request for password and a matching reply: + +CTRL-REQ-PASSWORD-1:Password needed for SSID foobar +> password 1 mysecretpassword + +Example request for generic token card challenge-response: + +CTRL-REQ-OTP-2:Challenge 1235663 needed for SSID foobar +> otp 2 9876 + + +wpa_cli commands + + status = get current WPA/EAPOL/EAP status + mib = get MIB variables (dot1x, dot11) + help = show this usage help + interface [ifname] = show interfaces/select interface + level <debug level> = change debug level + license = show full wpa_cli license + logoff = IEEE 802.1X EAPOL state machine logoff + logon = IEEE 802.1X EAPOL state machine logon + set = set variables (shows list of variables when run without arguments) + pmksa = show PMKSA cache + reassociate = force reassociation + reconfigure = force wpa_supplicant to re-read its configuration file + preauthenticate <BSSID> = force preauthentication + identity <network id> <identity> = configure identity for an SSID + password <network id> <password> = configure password for an SSID + otp <network id> <password> = configure one-time-password for an SSID + terminate = terminate wpa_supplicant + quit = exit wpa_cli + + + +Integrating with pcmcia-cs/cardmgr scripts +------------------------------------------ + +wpa_supplicant needs to be running when using a wireless network with +WPA. It can be started either from system startup scripts or from +pcmcia-cs/cardmgr scripts (when using PC Cards). WPA handshake must be +completed before data frames can be exchanged, so wpa_supplicant +should be started before DHCP client. + +Command line option '-w' can be used if wpa_supplicant is started +before the wireless LAN interface is present (e.g., before inserting +the PC Card) or is not yet up. + +For example, following small changes to pcmcia-cs scripts can be used +to enable WPA support: + +Add MODE="Managed" and WPA="y" to the network scheme in +/etc/pcmcia/wireless.opts. + +Add the following block to the end of 'start' action handler in +/etc/pcmcia/wireless: + + if [ "$WPA" = "y" -a -x /usr/local/bin/wpa_supplicant ]; then + /usr/local/bin/wpa_supplicant -Bw -c/etc/wpa_supplicant.conf \ + -i$DEVICE + fi + +Add the following block to the end of 'stop' action handler (may need +to be separated from other actions) in /etc/pcmcia/wireless: + + if [ "$WPA" = "y" -a -x /usr/local/bin/wpa_supplicant ]; then + killall wpa_supplicant + fi + +This will make cardmgr start wpa_supplicant when the card is plugged +in. wpa_supplicant will wait until the interface is set up--either +when a static IP address is configured or when DHCP client is +started--and will then negotiate keys with the AP. + + + +Optional integration with Xsupplicant +------------------------------------- + +wpa_supplicant has an integrated IEEE 802.1X Supplicant that supports +most commonly used EAP methods. In addition, wpa_supplicant has an +experimental interface for integrating it with Xsupplicant +(http://www.open1x.org/) for the WPA with EAP authentication. + +When using WPA-EAP, both wpa_supplicant and Xsupplicant must be +configured with the network security policy. See Xsupplicant documents +for information about its configuration. Please also note, that a new +command line option -W (enable WPA) must be used when starting +xsupplicant. + +Example configuration for xsupplicant: + +network_list = all +default_netname = jkm + +jkm +{ + type = wireless + allow_types = eap_peap + identity = <BEGIN_ID>jkm<END_ID> + eap-peap { + random_file = /dev/urandom + root_cert = /home/jkm/CA.pem + chunk_size = 1398 + allow_types = eap_mschapv2 + eap-mschapv2 { + username = <BEGIN_UNAME>jkm<END_UNAME> + password = <BEGIN_PASS>jkm<END_PASS> + } + } +} + + +Example configuration for wpa_supplicant: + +network={ + ssid="jkm" + key_mgmt=WPA-EAP +} + + +Both wpa_supplicant and xsupplicant need to be started. Please remember +to add '-W' option for xsupplicant in order to provide keying material +for wpa_supplicant and '-e' option for wpa_supplicant to disable internal +IEEE 802.1X implementation. + +wpa_supplicant -iwlan0 -cwpa_supplicant.conf -e +xsupplicant -iwlan0 -cxsupplicant.conf -W diff --git a/contrib/wpa_supplicant/aes.c b/contrib/wpa_supplicant/aes.c new file mode 100644 index 000000000000..eabebd074653 --- /dev/null +++ b/contrib/wpa_supplicant/aes.c @@ -0,0 +1,1132 @@ +/* + * Modifications to public domain implementation: + * - support only 128-bit keys + * - cleanup + * Copyright (c) 2003-2004, Jouni Malinen <jkmaline@cc.hut.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +/** + * rijndael-alg-fst.c + * + * @version 3.0 (December 2000) + * + * Optimised ANSI C code for the Rijndael cipher (now AES) + * + * @author Vincent Rijmen <vincent.rijmen@esat.kuleuven.ac.be> + * @author Antoon Bosselaers <antoon.bosselaers@esat.kuleuven.ac.be> + * @author Paulo Barreto <paulo.barreto@terra.com.br> + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#define FULL_UNROLL + + +/* +Te0[x] = S [x].[02, 01, 01, 03]; +Te1[x] = S [x].[03, 02, 01, 01]; +Te2[x] = S [x].[01, 03, 02, 01]; +Te3[x] = S [x].[01, 01, 03, 02]; +Te4[x] = S [x].[01, 01, 01, 01]; + +Td0[x] = Si[x].[0e, 09, 0d, 0b]; +Td1[x] = Si[x].[0b, 0e, 09, 0d]; +Td2[x] = Si[x].[0d, 0b, 0e, 09]; +Td3[x] = Si[x].[09, 0d, 0b, 0e]; +Td4[x] = Si[x].[01, 01, 01, 01]; +*/ + +static const u32 Te0[256] = { + 0xc66363a5U, 0xf87c7c84U, 0xee777799U, 0xf67b7b8dU, + 0xfff2f20dU, 0xd66b6bbdU, 0xde6f6fb1U, 0x91c5c554U, + 0x60303050U, 0x02010103U, 0xce6767a9U, 0x562b2b7dU, + 0xe7fefe19U, 0xb5d7d762U, 0x4dababe6U, 0xec76769aU, + 0x8fcaca45U, 0x1f82829dU, 0x89c9c940U, 0xfa7d7d87U, + 0xeffafa15U, 0xb25959ebU, 0x8e4747c9U, 0xfbf0f00bU, + 0x41adadecU, 0xb3d4d467U, 0x5fa2a2fdU, 0x45afafeaU, + 0x239c9cbfU, 0x53a4a4f7U, 0xe4727296U, 0x9bc0c05bU, + 0x75b7b7c2U, 0xe1fdfd1cU, 0x3d9393aeU, 0x4c26266aU, + 0x6c36365aU, 0x7e3f3f41U, 0xf5f7f702U, 0x83cccc4fU, + 0x6834345cU, 0x51a5a5f4U, 0xd1e5e534U, 0xf9f1f108U, + 0xe2717193U, 0xabd8d873U, 0x62313153U, 0x2a15153fU, + 0x0804040cU, 0x95c7c752U, 0x46232365U, 0x9dc3c35eU, + 0x30181828U, 0x379696a1U, 0x0a05050fU, 0x2f9a9ab5U, + 0x0e070709U, 0x24121236U, 0x1b80809bU, 0xdfe2e23dU, + 0xcdebeb26U, 0x4e272769U, 0x7fb2b2cdU, 0xea75759fU, + 0x1209091bU, 0x1d83839eU, 0x582c2c74U, 0x341a1a2eU, + 0x361b1b2dU, 0xdc6e6eb2U, 0xb45a5aeeU, 0x5ba0a0fbU, + 0xa45252f6U, 0x763b3b4dU, 0xb7d6d661U, 0x7db3b3ceU, + 0x5229297bU, 0xdde3e33eU, 0x5e2f2f71U, 0x13848497U, + 0xa65353f5U, 0xb9d1d168U, 0x00000000U, 0xc1eded2cU, + 0x40202060U, 0xe3fcfc1fU, 0x79b1b1c8U, 0xb65b5bedU, + 0xd46a6abeU, 0x8dcbcb46U, 0x67bebed9U, 0x7239394bU, + 0x944a4adeU, 0x984c4cd4U, 0xb05858e8U, 0x85cfcf4aU, + 0xbbd0d06bU, 0xc5efef2aU, 0x4faaaae5U, 0xedfbfb16U, + 0x864343c5U, 0x9a4d4dd7U, 0x66333355U, 0x11858594U, + 0x8a4545cfU, 0xe9f9f910U, 0x04020206U, 0xfe7f7f81U, + 0xa05050f0U, 0x783c3c44U, 0x259f9fbaU, 0x4ba8a8e3U, + 0xa25151f3U, 0x5da3a3feU, 0x804040c0U, 0x058f8f8aU, + 0x3f9292adU, 0x219d9dbcU, 0x70383848U, 0xf1f5f504U, + 0x63bcbcdfU, 0x77b6b6c1U, 0xafdada75U, 0x42212163U, + 0x20101030U, 0xe5ffff1aU, 0xfdf3f30eU, 0xbfd2d26dU, + 0x81cdcd4cU, 0x180c0c14U, 0x26131335U, 0xc3ecec2fU, + 0xbe5f5fe1U, 0x359797a2U, 0x884444ccU, 0x2e171739U, + 0x93c4c457U, 0x55a7a7f2U, 0xfc7e7e82U, 0x7a3d3d47U, + 0xc86464acU, 0xba5d5de7U, 0x3219192bU, 0xe6737395U, + 0xc06060a0U, 0x19818198U, 0x9e4f4fd1U, 0xa3dcdc7fU, + 0x44222266U, 0x542a2a7eU, 0x3b9090abU, 0x0b888883U, + 0x8c4646caU, 0xc7eeee29U, 0x6bb8b8d3U, 0x2814143cU, + 0xa7dede79U, 0xbc5e5ee2U, 0x160b0b1dU, 0xaddbdb76U, + 0xdbe0e03bU, 0x64323256U, 0x743a3a4eU, 0x140a0a1eU, + 0x924949dbU, 0x0c06060aU, 0x4824246cU, 0xb85c5ce4U, + 0x9fc2c25dU, 0xbdd3d36eU, 0x43acacefU, 0xc46262a6U, + 0x399191a8U, 0x319595a4U, 0xd3e4e437U, 0xf279798bU, + 0xd5e7e732U, 0x8bc8c843U, 0x6e373759U, 0xda6d6db7U, + 0x018d8d8cU, 0xb1d5d564U, 0x9c4e4ed2U, 0x49a9a9e0U, + 0xd86c6cb4U, 0xac5656faU, 0xf3f4f407U, 0xcfeaea25U, + 0xca6565afU, 0xf47a7a8eU, 0x47aeaee9U, 0x10080818U, + 0x6fbabad5U, 0xf0787888U, 0x4a25256fU, 0x5c2e2e72U, + 0x381c1c24U, 0x57a6a6f1U, 0x73b4b4c7U, 0x97c6c651U, + 0xcbe8e823U, 0xa1dddd7cU, 0xe874749cU, 0x3e1f1f21U, + 0x964b4bddU, 0x61bdbddcU, 0x0d8b8b86U, 0x0f8a8a85U, + 0xe0707090U, 0x7c3e3e42U, 0x71b5b5c4U, 0xcc6666aaU, + 0x904848d8U, 0x06030305U, 0xf7f6f601U, 0x1c0e0e12U, + 0xc26161a3U, 0x6a35355fU, 0xae5757f9U, 0x69b9b9d0U, + 0x17868691U, 0x99c1c158U, 0x3a1d1d27U, 0x279e9eb9U, + 0xd9e1e138U, 0xebf8f813U, 0x2b9898b3U, 0x22111133U, + 0xd26969bbU, 0xa9d9d970U, 0x078e8e89U, 0x339494a7U, + 0x2d9b9bb6U, 0x3c1e1e22U, 0x15878792U, 0xc9e9e920U, + 0x87cece49U, 0xaa5555ffU, 0x50282878U, 0xa5dfdf7aU, + 0x038c8c8fU, 0x59a1a1f8U, 0x09898980U, 0x1a0d0d17U, + 0x65bfbfdaU, 0xd7e6e631U, 0x844242c6U, 0xd06868b8U, + 0x824141c3U, 0x299999b0U, 0x5a2d2d77U, 0x1e0f0f11U, + 0x7bb0b0cbU, 0xa85454fcU, 0x6dbbbbd6U, 0x2c16163aU, +}; +static const u32 Te1[256] = { + 0xa5c66363U, 0x84f87c7cU, 0x99ee7777U, 0x8df67b7bU, + 0x0dfff2f2U, 0xbdd66b6bU, 0xb1de6f6fU, 0x5491c5c5U, + 0x50603030U, 0x03020101U, 0xa9ce6767U, 0x7d562b2bU, + 0x19e7fefeU, 0x62b5d7d7U, 0xe64dababU, 0x9aec7676U, + 0x458fcacaU, 0x9d1f8282U, 0x4089c9c9U, 0x87fa7d7dU, + 0x15effafaU, 0xebb25959U, 0xc98e4747U, 0x0bfbf0f0U, + 0xec41adadU, 0x67b3d4d4U, 0xfd5fa2a2U, 0xea45afafU, + 0xbf239c9cU, 0xf753a4a4U, 0x96e47272U, 0x5b9bc0c0U, + 0xc275b7b7U, 0x1ce1fdfdU, 0xae3d9393U, 0x6a4c2626U, + 0x5a6c3636U, 0x417e3f3fU, 0x02f5f7f7U, 0x4f83ccccU, + 0x5c683434U, 0xf451a5a5U, 0x34d1e5e5U, 0x08f9f1f1U, + 0x93e27171U, 0x73abd8d8U, 0x53623131U, 0x3f2a1515U, + 0x0c080404U, 0x5295c7c7U, 0x65462323U, 0x5e9dc3c3U, + 0x28301818U, 0xa1379696U, 0x0f0a0505U, 0xb52f9a9aU, + 0x090e0707U, 0x36241212U, 0x9b1b8080U, 0x3ddfe2e2U, + 0x26cdebebU, 0x694e2727U, 0xcd7fb2b2U, 0x9fea7575U, + 0x1b120909U, 0x9e1d8383U, 0x74582c2cU, 0x2e341a1aU, + 0x2d361b1bU, 0xb2dc6e6eU, 0xeeb45a5aU, 0xfb5ba0a0U, + 0xf6a45252U, 0x4d763b3bU, 0x61b7d6d6U, 0xce7db3b3U, + 0x7b522929U, 0x3edde3e3U, 0x715e2f2fU, 0x97138484U, + 0xf5a65353U, 0x68b9d1d1U, 0x00000000U, 0x2cc1ededU, + 0x60402020U, 0x1fe3fcfcU, 0xc879b1b1U, 0xedb65b5bU, + 0xbed46a6aU, 0x468dcbcbU, 0xd967bebeU, 0x4b723939U, + 0xde944a4aU, 0xd4984c4cU, 0xe8b05858U, 0x4a85cfcfU, + 0x6bbbd0d0U, 0x2ac5efefU, 0xe54faaaaU, 0x16edfbfbU, + 0xc5864343U, 0xd79a4d4dU, 0x55663333U, 0x94118585U, + 0xcf8a4545U, 0x10e9f9f9U, 0x06040202U, 0x81fe7f7fU, + 0xf0a05050U, 0x44783c3cU, 0xba259f9fU, 0xe34ba8a8U, + 0xf3a25151U, 0xfe5da3a3U, 0xc0804040U, 0x8a058f8fU, + 0xad3f9292U, 0xbc219d9dU, 0x48703838U, 0x04f1f5f5U, + 0xdf63bcbcU, 0xc177b6b6U, 0x75afdadaU, 0x63422121U, + 0x30201010U, 0x1ae5ffffU, 0x0efdf3f3U, 0x6dbfd2d2U, + 0x4c81cdcdU, 0x14180c0cU, 0x35261313U, 0x2fc3ececU, + 0xe1be5f5fU, 0xa2359797U, 0xcc884444U, 0x392e1717U, + 0x5793c4c4U, 0xf255a7a7U, 0x82fc7e7eU, 0x477a3d3dU, + 0xacc86464U, 0xe7ba5d5dU, 0x2b321919U, 0x95e67373U, + 0xa0c06060U, 0x98198181U, 0xd19e4f4fU, 0x7fa3dcdcU, + 0x66442222U, 0x7e542a2aU, 0xab3b9090U, 0x830b8888U, + 0xca8c4646U, 0x29c7eeeeU, 0xd36bb8b8U, 0x3c281414U, + 0x79a7dedeU, 0xe2bc5e5eU, 0x1d160b0bU, 0x76addbdbU, + 0x3bdbe0e0U, 0x56643232U, 0x4e743a3aU, 0x1e140a0aU, + 0xdb924949U, 0x0a0c0606U, 0x6c482424U, 0xe4b85c5cU, + 0x5d9fc2c2U, 0x6ebdd3d3U, 0xef43acacU, 0xa6c46262U, + 0xa8399191U, 0xa4319595U, 0x37d3e4e4U, 0x8bf27979U, + 0x32d5e7e7U, 0x438bc8c8U, 0x596e3737U, 0xb7da6d6dU, + 0x8c018d8dU, 0x64b1d5d5U, 0xd29c4e4eU, 0xe049a9a9U, + 0xb4d86c6cU, 0xfaac5656U, 0x07f3f4f4U, 0x25cfeaeaU, + 0xafca6565U, 0x8ef47a7aU, 0xe947aeaeU, 0x18100808U, + 0xd56fbabaU, 0x88f07878U, 0x6f4a2525U, 0x725c2e2eU, + 0x24381c1cU, 0xf157a6a6U, 0xc773b4b4U, 0x5197c6c6U, + 0x23cbe8e8U, 0x7ca1ddddU, 0x9ce87474U, 0x213e1f1fU, + 0xdd964b4bU, 0xdc61bdbdU, 0x860d8b8bU, 0x850f8a8aU, + 0x90e07070U, 0x427c3e3eU, 0xc471b5b5U, 0xaacc6666U, + 0xd8904848U, 0x05060303U, 0x01f7f6f6U, 0x121c0e0eU, + 0xa3c26161U, 0x5f6a3535U, 0xf9ae5757U, 0xd069b9b9U, + 0x91178686U, 0x5899c1c1U, 0x273a1d1dU, 0xb9279e9eU, + 0x38d9e1e1U, 0x13ebf8f8U, 0xb32b9898U, 0x33221111U, + 0xbbd26969U, 0x70a9d9d9U, 0x89078e8eU, 0xa7339494U, + 0xb62d9b9bU, 0x223c1e1eU, 0x92158787U, 0x20c9e9e9U, + 0x4987ceceU, 0xffaa5555U, 0x78502828U, 0x7aa5dfdfU, + 0x8f038c8cU, 0xf859a1a1U, 0x80098989U, 0x171a0d0dU, + 0xda65bfbfU, 0x31d7e6e6U, 0xc6844242U, 0xb8d06868U, + 0xc3824141U, 0xb0299999U, 0x775a2d2dU, 0x111e0f0fU, + 0xcb7bb0b0U, 0xfca85454U, 0xd66dbbbbU, 0x3a2c1616U, +}; +static const u32 Te2[256] = { + 0x63a5c663U, 0x7c84f87cU, 0x7799ee77U, 0x7b8df67bU, + 0xf20dfff2U, 0x6bbdd66bU, 0x6fb1de6fU, 0xc55491c5U, + 0x30506030U, 0x01030201U, 0x67a9ce67U, 0x2b7d562bU, + 0xfe19e7feU, 0xd762b5d7U, 0xabe64dabU, 0x769aec76U, + 0xca458fcaU, 0x829d1f82U, 0xc94089c9U, 0x7d87fa7dU, + 0xfa15effaU, 0x59ebb259U, 0x47c98e47U, 0xf00bfbf0U, + 0xadec41adU, 0xd467b3d4U, 0xa2fd5fa2U, 0xafea45afU, + 0x9cbf239cU, 0xa4f753a4U, 0x7296e472U, 0xc05b9bc0U, + 0xb7c275b7U, 0xfd1ce1fdU, 0x93ae3d93U, 0x266a4c26U, + 0x365a6c36U, 0x3f417e3fU, 0xf702f5f7U, 0xcc4f83ccU, + 0x345c6834U, 0xa5f451a5U, 0xe534d1e5U, 0xf108f9f1U, + 0x7193e271U, 0xd873abd8U, 0x31536231U, 0x153f2a15U, + 0x040c0804U, 0xc75295c7U, 0x23654623U, 0xc35e9dc3U, + 0x18283018U, 0x96a13796U, 0x050f0a05U, 0x9ab52f9aU, + 0x07090e07U, 0x12362412U, 0x809b1b80U, 0xe23ddfe2U, + 0xeb26cdebU, 0x27694e27U, 0xb2cd7fb2U, 0x759fea75U, + 0x091b1209U, 0x839e1d83U, 0x2c74582cU, 0x1a2e341aU, + 0x1b2d361bU, 0x6eb2dc6eU, 0x5aeeb45aU, 0xa0fb5ba0U, + 0x52f6a452U, 0x3b4d763bU, 0xd661b7d6U, 0xb3ce7db3U, + 0x297b5229U, 0xe33edde3U, 0x2f715e2fU, 0x84971384U, + 0x53f5a653U, 0xd168b9d1U, 0x00000000U, 0xed2cc1edU, + 0x20604020U, 0xfc1fe3fcU, 0xb1c879b1U, 0x5bedb65bU, + 0x6abed46aU, 0xcb468dcbU, 0xbed967beU, 0x394b7239U, + 0x4ade944aU, 0x4cd4984cU, 0x58e8b058U, 0xcf4a85cfU, + 0xd06bbbd0U, 0xef2ac5efU, 0xaae54faaU, 0xfb16edfbU, + 0x43c58643U, 0x4dd79a4dU, 0x33556633U, 0x85941185U, + 0x45cf8a45U, 0xf910e9f9U, 0x02060402U, 0x7f81fe7fU, + 0x50f0a050U, 0x3c44783cU, 0x9fba259fU, 0xa8e34ba8U, + 0x51f3a251U, 0xa3fe5da3U, 0x40c08040U, 0x8f8a058fU, + 0x92ad3f92U, 0x9dbc219dU, 0x38487038U, 0xf504f1f5U, + 0xbcdf63bcU, 0xb6c177b6U, 0xda75afdaU, 0x21634221U, + 0x10302010U, 0xff1ae5ffU, 0xf30efdf3U, 0xd26dbfd2U, + 0xcd4c81cdU, 0x0c14180cU, 0x13352613U, 0xec2fc3ecU, + 0x5fe1be5fU, 0x97a23597U, 0x44cc8844U, 0x17392e17U, + 0xc45793c4U, 0xa7f255a7U, 0x7e82fc7eU, 0x3d477a3dU, + 0x64acc864U, 0x5de7ba5dU, 0x192b3219U, 0x7395e673U, + 0x60a0c060U, 0x81981981U, 0x4fd19e4fU, 0xdc7fa3dcU, + 0x22664422U, 0x2a7e542aU, 0x90ab3b90U, 0x88830b88U, + 0x46ca8c46U, 0xee29c7eeU, 0xb8d36bb8U, 0x143c2814U, + 0xde79a7deU, 0x5ee2bc5eU, 0x0b1d160bU, 0xdb76addbU, + 0xe03bdbe0U, 0x32566432U, 0x3a4e743aU, 0x0a1e140aU, + 0x49db9249U, 0x060a0c06U, 0x246c4824U, 0x5ce4b85cU, + 0xc25d9fc2U, 0xd36ebdd3U, 0xacef43acU, 0x62a6c462U, + 0x91a83991U, 0x95a43195U, 0xe437d3e4U, 0x798bf279U, + 0xe732d5e7U, 0xc8438bc8U, 0x37596e37U, 0x6db7da6dU, + 0x8d8c018dU, 0xd564b1d5U, 0x4ed29c4eU, 0xa9e049a9U, + 0x6cb4d86cU, 0x56faac56U, 0xf407f3f4U, 0xea25cfeaU, + 0x65afca65U, 0x7a8ef47aU, 0xaee947aeU, 0x08181008U, + 0xbad56fbaU, 0x7888f078U, 0x256f4a25U, 0x2e725c2eU, + 0x1c24381cU, 0xa6f157a6U, 0xb4c773b4U, 0xc65197c6U, + 0xe823cbe8U, 0xdd7ca1ddU, 0x749ce874U, 0x1f213e1fU, + 0x4bdd964bU, 0xbddc61bdU, 0x8b860d8bU, 0x8a850f8aU, + 0x7090e070U, 0x3e427c3eU, 0xb5c471b5U, 0x66aacc66U, + 0x48d89048U, 0x03050603U, 0xf601f7f6U, 0x0e121c0eU, + 0x61a3c261U, 0x355f6a35U, 0x57f9ae57U, 0xb9d069b9U, + 0x86911786U, 0xc15899c1U, 0x1d273a1dU, 0x9eb9279eU, + 0xe138d9e1U, 0xf813ebf8U, 0x98b32b98U, 0x11332211U, + 0x69bbd269U, 0xd970a9d9U, 0x8e89078eU, 0x94a73394U, + 0x9bb62d9bU, 0x1e223c1eU, 0x87921587U, 0xe920c9e9U, + 0xce4987ceU, 0x55ffaa55U, 0x28785028U, 0xdf7aa5dfU, + 0x8c8f038cU, 0xa1f859a1U, 0x89800989U, 0x0d171a0dU, + 0xbfda65bfU, 0xe631d7e6U, 0x42c68442U, 0x68b8d068U, + 0x41c38241U, 0x99b02999U, 0x2d775a2dU, 0x0f111e0fU, + 0xb0cb7bb0U, 0x54fca854U, 0xbbd66dbbU, 0x163a2c16U, +}; +static const u32 Te3[256] = { + + 0x6363a5c6U, 0x7c7c84f8U, 0x777799eeU, 0x7b7b8df6U, + 0xf2f20dffU, 0x6b6bbdd6U, 0x6f6fb1deU, 0xc5c55491U, + 0x30305060U, 0x01010302U, 0x6767a9ceU, 0x2b2b7d56U, + 0xfefe19e7U, 0xd7d762b5U, 0xababe64dU, 0x76769aecU, + 0xcaca458fU, 0x82829d1fU, 0xc9c94089U, 0x7d7d87faU, + 0xfafa15efU, 0x5959ebb2U, 0x4747c98eU, 0xf0f00bfbU, + 0xadadec41U, 0xd4d467b3U, 0xa2a2fd5fU, 0xafafea45U, + 0x9c9cbf23U, 0xa4a4f753U, 0x727296e4U, 0xc0c05b9bU, + 0xb7b7c275U, 0xfdfd1ce1U, 0x9393ae3dU, 0x26266a4cU, + 0x36365a6cU, 0x3f3f417eU, 0xf7f702f5U, 0xcccc4f83U, + 0x34345c68U, 0xa5a5f451U, 0xe5e534d1U, 0xf1f108f9U, + 0x717193e2U, 0xd8d873abU, 0x31315362U, 0x15153f2aU, + 0x04040c08U, 0xc7c75295U, 0x23236546U, 0xc3c35e9dU, + 0x18182830U, 0x9696a137U, 0x05050f0aU, 0x9a9ab52fU, + 0x0707090eU, 0x12123624U, 0x80809b1bU, 0xe2e23ddfU, + 0xebeb26cdU, 0x2727694eU, 0xb2b2cd7fU, 0x75759feaU, + 0x09091b12U, 0x83839e1dU, 0x2c2c7458U, 0x1a1a2e34U, + 0x1b1b2d36U, 0x6e6eb2dcU, 0x5a5aeeb4U, 0xa0a0fb5bU, + 0x5252f6a4U, 0x3b3b4d76U, 0xd6d661b7U, 0xb3b3ce7dU, + 0x29297b52U, 0xe3e33eddU, 0x2f2f715eU, 0x84849713U, + 0x5353f5a6U, 0xd1d168b9U, 0x00000000U, 0xeded2cc1U, + 0x20206040U, 0xfcfc1fe3U, 0xb1b1c879U, 0x5b5bedb6U, + 0x6a6abed4U, 0xcbcb468dU, 0xbebed967U, 0x39394b72U, + 0x4a4ade94U, 0x4c4cd498U, 0x5858e8b0U, 0xcfcf4a85U, + 0xd0d06bbbU, 0xefef2ac5U, 0xaaaae54fU, 0xfbfb16edU, + 0x4343c586U, 0x4d4dd79aU, 0x33335566U, 0x85859411U, + 0x4545cf8aU, 0xf9f910e9U, 0x02020604U, 0x7f7f81feU, + 0x5050f0a0U, 0x3c3c4478U, 0x9f9fba25U, 0xa8a8e34bU, + 0x5151f3a2U, 0xa3a3fe5dU, 0x4040c080U, 0x8f8f8a05U, + 0x9292ad3fU, 0x9d9dbc21U, 0x38384870U, 0xf5f504f1U, + 0xbcbcdf63U, 0xb6b6c177U, 0xdada75afU, 0x21216342U, + 0x10103020U, 0xffff1ae5U, 0xf3f30efdU, 0xd2d26dbfU, + 0xcdcd4c81U, 0x0c0c1418U, 0x13133526U, 0xecec2fc3U, + 0x5f5fe1beU, 0x9797a235U, 0x4444cc88U, 0x1717392eU, + 0xc4c45793U, 0xa7a7f255U, 0x7e7e82fcU, 0x3d3d477aU, + 0x6464acc8U, 0x5d5de7baU, 0x19192b32U, 0x737395e6U, + 0x6060a0c0U, 0x81819819U, 0x4f4fd19eU, 0xdcdc7fa3U, + 0x22226644U, 0x2a2a7e54U, 0x9090ab3bU, 0x8888830bU, + 0x4646ca8cU, 0xeeee29c7U, 0xb8b8d36bU, 0x14143c28U, + 0xdede79a7U, 0x5e5ee2bcU, 0x0b0b1d16U, 0xdbdb76adU, + 0xe0e03bdbU, 0x32325664U, 0x3a3a4e74U, 0x0a0a1e14U, + 0x4949db92U, 0x06060a0cU, 0x24246c48U, 0x5c5ce4b8U, + 0xc2c25d9fU, 0xd3d36ebdU, 0xacacef43U, 0x6262a6c4U, + 0x9191a839U, 0x9595a431U, 0xe4e437d3U, 0x79798bf2U, + 0xe7e732d5U, 0xc8c8438bU, 0x3737596eU, 0x6d6db7daU, + 0x8d8d8c01U, 0xd5d564b1U, 0x4e4ed29cU, 0xa9a9e049U, + 0x6c6cb4d8U, 0x5656faacU, 0xf4f407f3U, 0xeaea25cfU, + 0x6565afcaU, 0x7a7a8ef4U, 0xaeaee947U, 0x08081810U, + 0xbabad56fU, 0x787888f0U, 0x25256f4aU, 0x2e2e725cU, + 0x1c1c2438U, 0xa6a6f157U, 0xb4b4c773U, 0xc6c65197U, + 0xe8e823cbU, 0xdddd7ca1U, 0x74749ce8U, 0x1f1f213eU, + 0x4b4bdd96U, 0xbdbddc61U, 0x8b8b860dU, 0x8a8a850fU, + 0x707090e0U, 0x3e3e427cU, 0xb5b5c471U, 0x6666aaccU, + 0x4848d890U, 0x03030506U, 0xf6f601f7U, 0x0e0e121cU, + 0x6161a3c2U, 0x35355f6aU, 0x5757f9aeU, 0xb9b9d069U, + 0x86869117U, 0xc1c15899U, 0x1d1d273aU, 0x9e9eb927U, + 0xe1e138d9U, 0xf8f813ebU, 0x9898b32bU, 0x11113322U, + 0x6969bbd2U, 0xd9d970a9U, 0x8e8e8907U, 0x9494a733U, + 0x9b9bb62dU, 0x1e1e223cU, 0x87879215U, 0xe9e920c9U, + 0xcece4987U, 0x5555ffaaU, 0x28287850U, 0xdfdf7aa5U, + 0x8c8c8f03U, 0xa1a1f859U, 0x89898009U, 0x0d0d171aU, + 0xbfbfda65U, 0xe6e631d7U, 0x4242c684U, 0x6868b8d0U, + 0x4141c382U, 0x9999b029U, 0x2d2d775aU, 0x0f0f111eU, + 0xb0b0cb7bU, 0x5454fca8U, 0xbbbbd66dU, 0x16163a2cU, +}; +static const u32 Te4[256] = { + 0x63636363U, 0x7c7c7c7cU, 0x77777777U, 0x7b7b7b7bU, + 0xf2f2f2f2U, 0x6b6b6b6bU, 0x6f6f6f6fU, 0xc5c5c5c5U, + 0x30303030U, 0x01010101U, 0x67676767U, 0x2b2b2b2bU, + 0xfefefefeU, 0xd7d7d7d7U, 0xababababU, 0x76767676U, + 0xcacacacaU, 0x82828282U, 0xc9c9c9c9U, 0x7d7d7d7dU, + 0xfafafafaU, 0x59595959U, 0x47474747U, 0xf0f0f0f0U, + 0xadadadadU, 0xd4d4d4d4U, 0xa2a2a2a2U, 0xafafafafU, + 0x9c9c9c9cU, 0xa4a4a4a4U, 0x72727272U, 0xc0c0c0c0U, + 0xb7b7b7b7U, 0xfdfdfdfdU, 0x93939393U, 0x26262626U, + 0x36363636U, 0x3f3f3f3fU, 0xf7f7f7f7U, 0xccccccccU, + 0x34343434U, 0xa5a5a5a5U, 0xe5e5e5e5U, 0xf1f1f1f1U, + 0x71717171U, 0xd8d8d8d8U, 0x31313131U, 0x15151515U, + 0x04040404U, 0xc7c7c7c7U, 0x23232323U, 0xc3c3c3c3U, + 0x18181818U, 0x96969696U, 0x05050505U, 0x9a9a9a9aU, + 0x07070707U, 0x12121212U, 0x80808080U, 0xe2e2e2e2U, + 0xebebebebU, 0x27272727U, 0xb2b2b2b2U, 0x75757575U, + 0x09090909U, 0x83838383U, 0x2c2c2c2cU, 0x1a1a1a1aU, + 0x1b1b1b1bU, 0x6e6e6e6eU, 0x5a5a5a5aU, 0xa0a0a0a0U, + 0x52525252U, 0x3b3b3b3bU, 0xd6d6d6d6U, 0xb3b3b3b3U, + 0x29292929U, 0xe3e3e3e3U, 0x2f2f2f2fU, 0x84848484U, + 0x53535353U, 0xd1d1d1d1U, 0x00000000U, 0xededededU, + 0x20202020U, 0xfcfcfcfcU, 0xb1b1b1b1U, 0x5b5b5b5bU, + 0x6a6a6a6aU, 0xcbcbcbcbU, 0xbebebebeU, 0x39393939U, + 0x4a4a4a4aU, 0x4c4c4c4cU, 0x58585858U, 0xcfcfcfcfU, + 0xd0d0d0d0U, 0xefefefefU, 0xaaaaaaaaU, 0xfbfbfbfbU, + 0x43434343U, 0x4d4d4d4dU, 0x33333333U, 0x85858585U, + 0x45454545U, 0xf9f9f9f9U, 0x02020202U, 0x7f7f7f7fU, + 0x50505050U, 0x3c3c3c3cU, 0x9f9f9f9fU, 0xa8a8a8a8U, + 0x51515151U, 0xa3a3a3a3U, 0x40404040U, 0x8f8f8f8fU, + 0x92929292U, 0x9d9d9d9dU, 0x38383838U, 0xf5f5f5f5U, + 0xbcbcbcbcU, 0xb6b6b6b6U, 0xdadadadaU, 0x21212121U, + 0x10101010U, 0xffffffffU, 0xf3f3f3f3U, 0xd2d2d2d2U, + 0xcdcdcdcdU, 0x0c0c0c0cU, 0x13131313U, 0xececececU, + 0x5f5f5f5fU, 0x97979797U, 0x44444444U, 0x17171717U, + 0xc4c4c4c4U, 0xa7a7a7a7U, 0x7e7e7e7eU, 0x3d3d3d3dU, + 0x64646464U, 0x5d5d5d5dU, 0x19191919U, 0x73737373U, + 0x60606060U, 0x81818181U, 0x4f4f4f4fU, 0xdcdcdcdcU, + 0x22222222U, 0x2a2a2a2aU, 0x90909090U, 0x88888888U, + 0x46464646U, 0xeeeeeeeeU, 0xb8b8b8b8U, 0x14141414U, + 0xdedededeU, 0x5e5e5e5eU, 0x0b0b0b0bU, 0xdbdbdbdbU, + 0xe0e0e0e0U, 0x32323232U, 0x3a3a3a3aU, 0x0a0a0a0aU, + 0x49494949U, 0x06060606U, 0x24242424U, 0x5c5c5c5cU, + 0xc2c2c2c2U, 0xd3d3d3d3U, 0xacacacacU, 0x62626262U, + 0x91919191U, 0x95959595U, 0xe4e4e4e4U, 0x79797979U, + 0xe7e7e7e7U, 0xc8c8c8c8U, 0x37373737U, 0x6d6d6d6dU, + 0x8d8d8d8dU, 0xd5d5d5d5U, 0x4e4e4e4eU, 0xa9a9a9a9U, + 0x6c6c6c6cU, 0x56565656U, 0xf4f4f4f4U, 0xeaeaeaeaU, + 0x65656565U, 0x7a7a7a7aU, 0xaeaeaeaeU, 0x08080808U, + 0xbabababaU, 0x78787878U, 0x25252525U, 0x2e2e2e2eU, + 0x1c1c1c1cU, 0xa6a6a6a6U, 0xb4b4b4b4U, 0xc6c6c6c6U, + 0xe8e8e8e8U, 0xddddddddU, 0x74747474U, 0x1f1f1f1fU, + 0x4b4b4b4bU, 0xbdbdbdbdU, 0x8b8b8b8bU, 0x8a8a8a8aU, + 0x70707070U, 0x3e3e3e3eU, 0xb5b5b5b5U, 0x66666666U, + 0x48484848U, 0x03030303U, 0xf6f6f6f6U, 0x0e0e0e0eU, + 0x61616161U, 0x35353535U, 0x57575757U, 0xb9b9b9b9U, + 0x86868686U, 0xc1c1c1c1U, 0x1d1d1d1dU, 0x9e9e9e9eU, + 0xe1e1e1e1U, 0xf8f8f8f8U, 0x98989898U, 0x11111111U, + 0x69696969U, 0xd9d9d9d9U, 0x8e8e8e8eU, 0x94949494U, + 0x9b9b9b9bU, 0x1e1e1e1eU, 0x87878787U, 0xe9e9e9e9U, + 0xcecececeU, 0x55555555U, 0x28282828U, 0xdfdfdfdfU, + 0x8c8c8c8cU, 0xa1a1a1a1U, 0x89898989U, 0x0d0d0d0dU, + 0xbfbfbfbfU, 0xe6e6e6e6U, 0x42424242U, 0x68686868U, + 0x41414141U, 0x99999999U, 0x2d2d2d2dU, 0x0f0f0f0fU, + 0xb0b0b0b0U, 0x54545454U, 0xbbbbbbbbU, 0x16161616U, +}; +static const u32 Td0[256] = { + 0x51f4a750U, 0x7e416553U, 0x1a17a4c3U, 0x3a275e96U, + 0x3bab6bcbU, 0x1f9d45f1U, 0xacfa58abU, 0x4be30393U, + 0x2030fa55U, 0xad766df6U, 0x88cc7691U, 0xf5024c25U, + 0x4fe5d7fcU, 0xc52acbd7U, 0x26354480U, 0xb562a38fU, + 0xdeb15a49U, 0x25ba1b67U, 0x45ea0e98U, 0x5dfec0e1U, + 0xc32f7502U, 0x814cf012U, 0x8d4697a3U, 0x6bd3f9c6U, + 0x038f5fe7U, 0x15929c95U, 0xbf6d7aebU, 0x955259daU, + 0xd4be832dU, 0x587421d3U, 0x49e06929U, 0x8ec9c844U, + 0x75c2896aU, 0xf48e7978U, 0x99583e6bU, 0x27b971ddU, + 0xbee14fb6U, 0xf088ad17U, 0xc920ac66U, 0x7dce3ab4U, + 0x63df4a18U, 0xe51a3182U, 0x97513360U, 0x62537f45U, + 0xb16477e0U, 0xbb6bae84U, 0xfe81a01cU, 0xf9082b94U, + 0x70486858U, 0x8f45fd19U, 0x94de6c87U, 0x527bf8b7U, + 0xab73d323U, 0x724b02e2U, 0xe31f8f57U, 0x6655ab2aU, + 0xb2eb2807U, 0x2fb5c203U, 0x86c57b9aU, 0xd33708a5U, + 0x302887f2U, 0x23bfa5b2U, 0x02036abaU, 0xed16825cU, + 0x8acf1c2bU, 0xa779b492U, 0xf307f2f0U, 0x4e69e2a1U, + 0x65daf4cdU, 0x0605bed5U, 0xd134621fU, 0xc4a6fe8aU, + 0x342e539dU, 0xa2f355a0U, 0x058ae132U, 0xa4f6eb75U, + 0x0b83ec39U, 0x4060efaaU, 0x5e719f06U, 0xbd6e1051U, + 0x3e218af9U, 0x96dd063dU, 0xdd3e05aeU, 0x4de6bd46U, + 0x91548db5U, 0x71c45d05U, 0x0406d46fU, 0x605015ffU, + 0x1998fb24U, 0xd6bde997U, 0x894043ccU, 0x67d99e77U, + 0xb0e842bdU, 0x07898b88U, 0xe7195b38U, 0x79c8eedbU, + 0xa17c0a47U, 0x7c420fe9U, 0xf8841ec9U, 0x00000000U, + 0x09808683U, 0x322bed48U, 0x1e1170acU, 0x6c5a724eU, + 0xfd0efffbU, 0x0f853856U, 0x3daed51eU, 0x362d3927U, + 0x0a0fd964U, 0x685ca621U, 0x9b5b54d1U, 0x24362e3aU, + 0x0c0a67b1U, 0x9357e70fU, 0xb4ee96d2U, 0x1b9b919eU, + 0x80c0c54fU, 0x61dc20a2U, 0x5a774b69U, 0x1c121a16U, + 0xe293ba0aU, 0xc0a02ae5U, 0x3c22e043U, 0x121b171dU, + 0x0e090d0bU, 0xf28bc7adU, 0x2db6a8b9U, 0x141ea9c8U, + 0x57f11985U, 0xaf75074cU, 0xee99ddbbU, 0xa37f60fdU, + 0xf701269fU, 0x5c72f5bcU, 0x44663bc5U, 0x5bfb7e34U, + 0x8b432976U, 0xcb23c6dcU, 0xb6edfc68U, 0xb8e4f163U, + 0xd731dccaU, 0x42638510U, 0x13972240U, 0x84c61120U, + 0x854a247dU, 0xd2bb3df8U, 0xaef93211U, 0xc729a16dU, + 0x1d9e2f4bU, 0xdcb230f3U, 0x0d8652ecU, 0x77c1e3d0U, + 0x2bb3166cU, 0xa970b999U, 0x119448faU, 0x47e96422U, + 0xa8fc8cc4U, 0xa0f03f1aU, 0x567d2cd8U, 0x223390efU, + 0x87494ec7U, 0xd938d1c1U, 0x8ccaa2feU, 0x98d40b36U, + 0xa6f581cfU, 0xa57ade28U, 0xdab78e26U, 0x3fadbfa4U, + 0x2c3a9de4U, 0x5078920dU, 0x6a5fcc9bU, 0x547e4662U, + 0xf68d13c2U, 0x90d8b8e8U, 0x2e39f75eU, 0x82c3aff5U, + 0x9f5d80beU, 0x69d0937cU, 0x6fd52da9U, 0xcf2512b3U, + 0xc8ac993bU, 0x10187da7U, 0xe89c636eU, 0xdb3bbb7bU, + 0xcd267809U, 0x6e5918f4U, 0xec9ab701U, 0x834f9aa8U, + 0xe6956e65U, 0xaaffe67eU, 0x21bccf08U, 0xef15e8e6U, + 0xbae79bd9U, 0x4a6f36ceU, 0xea9f09d4U, 0x29b07cd6U, + 0x31a4b2afU, 0x2a3f2331U, 0xc6a59430U, 0x35a266c0U, + 0x744ebc37U, 0xfc82caa6U, 0xe090d0b0U, 0x33a7d815U, + 0xf104984aU, 0x41ecdaf7U, 0x7fcd500eU, 0x1791f62fU, + 0x764dd68dU, 0x43efb04dU, 0xccaa4d54U, 0xe49604dfU, + 0x9ed1b5e3U, 0x4c6a881bU, 0xc12c1fb8U, 0x4665517fU, + 0x9d5eea04U, 0x018c355dU, 0xfa877473U, 0xfb0b412eU, + 0xb3671d5aU, 0x92dbd252U, 0xe9105633U, 0x6dd64713U, + 0x9ad7618cU, 0x37a10c7aU, 0x59f8148eU, 0xeb133c89U, + 0xcea927eeU, 0xb761c935U, 0xe11ce5edU, 0x7a47b13cU, + 0x9cd2df59U, 0x55f2733fU, 0x1814ce79U, 0x73c737bfU, + 0x53f7cdeaU, 0x5ffdaa5bU, 0xdf3d6f14U, 0x7844db86U, + 0xcaaff381U, 0xb968c43eU, 0x3824342cU, 0xc2a3405fU, + 0x161dc372U, 0xbce2250cU, 0x283c498bU, 0xff0d9541U, + 0x39a80171U, 0x080cb3deU, 0xd8b4e49cU, 0x6456c190U, + 0x7bcb8461U, 0xd532b670U, 0x486c5c74U, 0xd0b85742U, +}; +static const u32 Td1[256] = { + 0x5051f4a7U, 0x537e4165U, 0xc31a17a4U, 0x963a275eU, + 0xcb3bab6bU, 0xf11f9d45U, 0xabacfa58U, 0x934be303U, + 0x552030faU, 0xf6ad766dU, 0x9188cc76U, 0x25f5024cU, + 0xfc4fe5d7U, 0xd7c52acbU, 0x80263544U, 0x8fb562a3U, + 0x49deb15aU, 0x6725ba1bU, 0x9845ea0eU, 0xe15dfec0U, + 0x02c32f75U, 0x12814cf0U, 0xa38d4697U, 0xc66bd3f9U, + 0xe7038f5fU, 0x9515929cU, 0xebbf6d7aU, 0xda955259U, + 0x2dd4be83U, 0xd3587421U, 0x2949e069U, 0x448ec9c8U, + 0x6a75c289U, 0x78f48e79U, 0x6b99583eU, 0xdd27b971U, + 0xb6bee14fU, 0x17f088adU, 0x66c920acU, 0xb47dce3aU, + 0x1863df4aU, 0x82e51a31U, 0x60975133U, 0x4562537fU, + 0xe0b16477U, 0x84bb6baeU, 0x1cfe81a0U, 0x94f9082bU, + 0x58704868U, 0x198f45fdU, 0x8794de6cU, 0xb7527bf8U, + 0x23ab73d3U, 0xe2724b02U, 0x57e31f8fU, 0x2a6655abU, + 0x07b2eb28U, 0x032fb5c2U, 0x9a86c57bU, 0xa5d33708U, + 0xf2302887U, 0xb223bfa5U, 0xba02036aU, 0x5ced1682U, + 0x2b8acf1cU, 0x92a779b4U, 0xf0f307f2U, 0xa14e69e2U, + 0xcd65daf4U, 0xd50605beU, 0x1fd13462U, 0x8ac4a6feU, + 0x9d342e53U, 0xa0a2f355U, 0x32058ae1U, 0x75a4f6ebU, + 0x390b83ecU, 0xaa4060efU, 0x065e719fU, 0x51bd6e10U, + 0xf93e218aU, 0x3d96dd06U, 0xaedd3e05U, 0x464de6bdU, + 0xb591548dU, 0x0571c45dU, 0x6f0406d4U, 0xff605015U, + 0x241998fbU, 0x97d6bde9U, 0xcc894043U, 0x7767d99eU, + 0xbdb0e842U, 0x8807898bU, 0x38e7195bU, 0xdb79c8eeU, + 0x47a17c0aU, 0xe97c420fU, 0xc9f8841eU, 0x00000000U, + 0x83098086U, 0x48322bedU, 0xac1e1170U, 0x4e6c5a72U, + 0xfbfd0effU, 0x560f8538U, 0x1e3daed5U, 0x27362d39U, + 0x640a0fd9U, 0x21685ca6U, 0xd19b5b54U, 0x3a24362eU, + 0xb10c0a67U, 0x0f9357e7U, 0xd2b4ee96U, 0x9e1b9b91U, + 0x4f80c0c5U, 0xa261dc20U, 0x695a774bU, 0x161c121aU, + 0x0ae293baU, 0xe5c0a02aU, 0x433c22e0U, 0x1d121b17U, + 0x0b0e090dU, 0xadf28bc7U, 0xb92db6a8U, 0xc8141ea9U, + 0x8557f119U, 0x4caf7507U, 0xbbee99ddU, 0xfda37f60U, + 0x9ff70126U, 0xbc5c72f5U, 0xc544663bU, 0x345bfb7eU, + 0x768b4329U, 0xdccb23c6U, 0x68b6edfcU, 0x63b8e4f1U, + 0xcad731dcU, 0x10426385U, 0x40139722U, 0x2084c611U, + 0x7d854a24U, 0xf8d2bb3dU, 0x11aef932U, 0x6dc729a1U, + 0x4b1d9e2fU, 0xf3dcb230U, 0xec0d8652U, 0xd077c1e3U, + 0x6c2bb316U, 0x99a970b9U, 0xfa119448U, 0x2247e964U, + 0xc4a8fc8cU, 0x1aa0f03fU, 0xd8567d2cU, 0xef223390U, + 0xc787494eU, 0xc1d938d1U, 0xfe8ccaa2U, 0x3698d40bU, + 0xcfa6f581U, 0x28a57adeU, 0x26dab78eU, 0xa43fadbfU, + 0xe42c3a9dU, 0x0d507892U, 0x9b6a5fccU, 0x62547e46U, + 0xc2f68d13U, 0xe890d8b8U, 0x5e2e39f7U, 0xf582c3afU, + 0xbe9f5d80U, 0x7c69d093U, 0xa96fd52dU, 0xb3cf2512U, + 0x3bc8ac99U, 0xa710187dU, 0x6ee89c63U, 0x7bdb3bbbU, + 0x09cd2678U, 0xf46e5918U, 0x01ec9ab7U, 0xa8834f9aU, + 0x65e6956eU, 0x7eaaffe6U, 0x0821bccfU, 0xe6ef15e8U, + 0xd9bae79bU, 0xce4a6f36U, 0xd4ea9f09U, 0xd629b07cU, + 0xaf31a4b2U, 0x312a3f23U, 0x30c6a594U, 0xc035a266U, + 0x37744ebcU, 0xa6fc82caU, 0xb0e090d0U, 0x1533a7d8U, + 0x4af10498U, 0xf741ecdaU, 0x0e7fcd50U, 0x2f1791f6U, + 0x8d764dd6U, 0x4d43efb0U, 0x54ccaa4dU, 0xdfe49604U, + 0xe39ed1b5U, 0x1b4c6a88U, 0xb8c12c1fU, 0x7f466551U, + 0x049d5eeaU, 0x5d018c35U, 0x73fa8774U, 0x2efb0b41U, + 0x5ab3671dU, 0x5292dbd2U, 0x33e91056U, 0x136dd647U, + 0x8c9ad761U, 0x7a37a10cU, 0x8e59f814U, 0x89eb133cU, + 0xeecea927U, 0x35b761c9U, 0xede11ce5U, 0x3c7a47b1U, + 0x599cd2dfU, 0x3f55f273U, 0x791814ceU, 0xbf73c737U, + 0xea53f7cdU, 0x5b5ffdaaU, 0x14df3d6fU, 0x867844dbU, + 0x81caaff3U, 0x3eb968c4U, 0x2c382434U, 0x5fc2a340U, + 0x72161dc3U, 0x0cbce225U, 0x8b283c49U, 0x41ff0d95U, + 0x7139a801U, 0xde080cb3U, 0x9cd8b4e4U, 0x906456c1U, + 0x617bcb84U, 0x70d532b6U, 0x74486c5cU, 0x42d0b857U, +}; +static const u32 Td2[256] = { + 0xa75051f4U, 0x65537e41U, 0xa4c31a17U, 0x5e963a27U, + 0x6bcb3babU, 0x45f11f9dU, 0x58abacfaU, 0x03934be3U, + 0xfa552030U, 0x6df6ad76U, 0x769188ccU, 0x4c25f502U, + 0xd7fc4fe5U, 0xcbd7c52aU, 0x44802635U, 0xa38fb562U, + 0x5a49deb1U, 0x1b6725baU, 0x0e9845eaU, 0xc0e15dfeU, + 0x7502c32fU, 0xf012814cU, 0x97a38d46U, 0xf9c66bd3U, + 0x5fe7038fU, 0x9c951592U, 0x7aebbf6dU, 0x59da9552U, + 0x832dd4beU, 0x21d35874U, 0x692949e0U, 0xc8448ec9U, + 0x896a75c2U, 0x7978f48eU, 0x3e6b9958U, 0x71dd27b9U, + 0x4fb6bee1U, 0xad17f088U, 0xac66c920U, 0x3ab47dceU, + 0x4a1863dfU, 0x3182e51aU, 0x33609751U, 0x7f456253U, + 0x77e0b164U, 0xae84bb6bU, 0xa01cfe81U, 0x2b94f908U, + 0x68587048U, 0xfd198f45U, 0x6c8794deU, 0xf8b7527bU, + 0xd323ab73U, 0x02e2724bU, 0x8f57e31fU, 0xab2a6655U, + 0x2807b2ebU, 0xc2032fb5U, 0x7b9a86c5U, 0x08a5d337U, + 0x87f23028U, 0xa5b223bfU, 0x6aba0203U, 0x825ced16U, + 0x1c2b8acfU, 0xb492a779U, 0xf2f0f307U, 0xe2a14e69U, + 0xf4cd65daU, 0xbed50605U, 0x621fd134U, 0xfe8ac4a6U, + 0x539d342eU, 0x55a0a2f3U, 0xe132058aU, 0xeb75a4f6U, + 0xec390b83U, 0xefaa4060U, 0x9f065e71U, 0x1051bd6eU, + + 0x8af93e21U, 0x063d96ddU, 0x05aedd3eU, 0xbd464de6U, + 0x8db59154U, 0x5d0571c4U, 0xd46f0406U, 0x15ff6050U, + 0xfb241998U, 0xe997d6bdU, 0x43cc8940U, 0x9e7767d9U, + 0x42bdb0e8U, 0x8b880789U, 0x5b38e719U, 0xeedb79c8U, + 0x0a47a17cU, 0x0fe97c42U, 0x1ec9f884U, 0x00000000U, + 0x86830980U, 0xed48322bU, 0x70ac1e11U, 0x724e6c5aU, + 0xfffbfd0eU, 0x38560f85U, 0xd51e3daeU, 0x3927362dU, + 0xd9640a0fU, 0xa621685cU, 0x54d19b5bU, 0x2e3a2436U, + 0x67b10c0aU, 0xe70f9357U, 0x96d2b4eeU, 0x919e1b9bU, + 0xc54f80c0U, 0x20a261dcU, 0x4b695a77U, 0x1a161c12U, + 0xba0ae293U, 0x2ae5c0a0U, 0xe0433c22U, 0x171d121bU, + 0x0d0b0e09U, 0xc7adf28bU, 0xa8b92db6U, 0xa9c8141eU, + 0x198557f1U, 0x074caf75U, 0xddbbee99U, 0x60fda37fU, + 0x269ff701U, 0xf5bc5c72U, 0x3bc54466U, 0x7e345bfbU, + 0x29768b43U, 0xc6dccb23U, 0xfc68b6edU, 0xf163b8e4U, + 0xdccad731U, 0x85104263U, 0x22401397U, 0x112084c6U, + 0x247d854aU, 0x3df8d2bbU, 0x3211aef9U, 0xa16dc729U, + 0x2f4b1d9eU, 0x30f3dcb2U, 0x52ec0d86U, 0xe3d077c1U, + 0x166c2bb3U, 0xb999a970U, 0x48fa1194U, 0x642247e9U, + 0x8cc4a8fcU, 0x3f1aa0f0U, 0x2cd8567dU, 0x90ef2233U, + 0x4ec78749U, 0xd1c1d938U, 0xa2fe8ccaU, 0x0b3698d4U, + 0x81cfa6f5U, 0xde28a57aU, 0x8e26dab7U, 0xbfa43fadU, + 0x9de42c3aU, 0x920d5078U, 0xcc9b6a5fU, 0x4662547eU, + 0x13c2f68dU, 0xb8e890d8U, 0xf75e2e39U, 0xaff582c3U, + 0x80be9f5dU, 0x937c69d0U, 0x2da96fd5U, 0x12b3cf25U, + 0x993bc8acU, 0x7da71018U, 0x636ee89cU, 0xbb7bdb3bU, + 0x7809cd26U, 0x18f46e59U, 0xb701ec9aU, 0x9aa8834fU, + 0x6e65e695U, 0xe67eaaffU, 0xcf0821bcU, 0xe8e6ef15U, + 0x9bd9bae7U, 0x36ce4a6fU, 0x09d4ea9fU, 0x7cd629b0U, + 0xb2af31a4U, 0x23312a3fU, 0x9430c6a5U, 0x66c035a2U, + 0xbc37744eU, 0xcaa6fc82U, 0xd0b0e090U, 0xd81533a7U, + 0x984af104U, 0xdaf741ecU, 0x500e7fcdU, 0xf62f1791U, + 0xd68d764dU, 0xb04d43efU, 0x4d54ccaaU, 0x04dfe496U, + 0xb5e39ed1U, 0x881b4c6aU, 0x1fb8c12cU, 0x517f4665U, + 0xea049d5eU, 0x355d018cU, 0x7473fa87U, 0x412efb0bU, + 0x1d5ab367U, 0xd25292dbU, 0x5633e910U, 0x47136dd6U, + 0x618c9ad7U, 0x0c7a37a1U, 0x148e59f8U, 0x3c89eb13U, + 0x27eecea9U, 0xc935b761U, 0xe5ede11cU, 0xb13c7a47U, + 0xdf599cd2U, 0x733f55f2U, 0xce791814U, 0x37bf73c7U, + 0xcdea53f7U, 0xaa5b5ffdU, 0x6f14df3dU, 0xdb867844U, + 0xf381caafU, 0xc43eb968U, 0x342c3824U, 0x405fc2a3U, + 0xc372161dU, 0x250cbce2U, 0x498b283cU, 0x9541ff0dU, + 0x017139a8U, 0xb3de080cU, 0xe49cd8b4U, 0xc1906456U, + 0x84617bcbU, 0xb670d532U, 0x5c74486cU, 0x5742d0b8U, +}; +static const u32 Td3[256] = { + 0xf4a75051U, 0x4165537eU, 0x17a4c31aU, 0x275e963aU, + 0xab6bcb3bU, 0x9d45f11fU, 0xfa58abacU, 0xe303934bU, + 0x30fa5520U, 0x766df6adU, 0xcc769188U, 0x024c25f5U, + 0xe5d7fc4fU, 0x2acbd7c5U, 0x35448026U, 0x62a38fb5U, + 0xb15a49deU, 0xba1b6725U, 0xea0e9845U, 0xfec0e15dU, + 0x2f7502c3U, 0x4cf01281U, 0x4697a38dU, 0xd3f9c66bU, + 0x8f5fe703U, 0x929c9515U, 0x6d7aebbfU, 0x5259da95U, + 0xbe832dd4U, 0x7421d358U, 0xe0692949U, 0xc9c8448eU, + 0xc2896a75U, 0x8e7978f4U, 0x583e6b99U, 0xb971dd27U, + 0xe14fb6beU, 0x88ad17f0U, 0x20ac66c9U, 0xce3ab47dU, + 0xdf4a1863U, 0x1a3182e5U, 0x51336097U, 0x537f4562U, + 0x6477e0b1U, 0x6bae84bbU, 0x81a01cfeU, 0x082b94f9U, + 0x48685870U, 0x45fd198fU, 0xde6c8794U, 0x7bf8b752U, + 0x73d323abU, 0x4b02e272U, 0x1f8f57e3U, 0x55ab2a66U, + 0xeb2807b2U, 0xb5c2032fU, 0xc57b9a86U, 0x3708a5d3U, + 0x2887f230U, 0xbfa5b223U, 0x036aba02U, 0x16825cedU, + 0xcf1c2b8aU, 0x79b492a7U, 0x07f2f0f3U, 0x69e2a14eU, + 0xdaf4cd65U, 0x05bed506U, 0x34621fd1U, 0xa6fe8ac4U, + 0x2e539d34U, 0xf355a0a2U, 0x8ae13205U, 0xf6eb75a4U, + 0x83ec390bU, 0x60efaa40U, 0x719f065eU, 0x6e1051bdU, + 0x218af93eU, 0xdd063d96U, 0x3e05aeddU, 0xe6bd464dU, + 0x548db591U, 0xc45d0571U, 0x06d46f04U, 0x5015ff60U, + 0x98fb2419U, 0xbde997d6U, 0x4043cc89U, 0xd99e7767U, + 0xe842bdb0U, 0x898b8807U, 0x195b38e7U, 0xc8eedb79U, + 0x7c0a47a1U, 0x420fe97cU, 0x841ec9f8U, 0x00000000U, + 0x80868309U, 0x2bed4832U, 0x1170ac1eU, 0x5a724e6cU, + 0x0efffbfdU, 0x8538560fU, 0xaed51e3dU, 0x2d392736U, + 0x0fd9640aU, 0x5ca62168U, 0x5b54d19bU, 0x362e3a24U, + 0x0a67b10cU, 0x57e70f93U, 0xee96d2b4U, 0x9b919e1bU, + 0xc0c54f80U, 0xdc20a261U, 0x774b695aU, 0x121a161cU, + 0x93ba0ae2U, 0xa02ae5c0U, 0x22e0433cU, 0x1b171d12U, + 0x090d0b0eU, 0x8bc7adf2U, 0xb6a8b92dU, 0x1ea9c814U, + 0xf1198557U, 0x75074cafU, 0x99ddbbeeU, 0x7f60fda3U, + 0x01269ff7U, 0x72f5bc5cU, 0x663bc544U, 0xfb7e345bU, + 0x4329768bU, 0x23c6dccbU, 0xedfc68b6U, 0xe4f163b8U, + 0x31dccad7U, 0x63851042U, 0x97224013U, 0xc6112084U, + 0x4a247d85U, 0xbb3df8d2U, 0xf93211aeU, 0x29a16dc7U, + 0x9e2f4b1dU, 0xb230f3dcU, 0x8652ec0dU, 0xc1e3d077U, + 0xb3166c2bU, 0x70b999a9U, 0x9448fa11U, 0xe9642247U, + 0xfc8cc4a8U, 0xf03f1aa0U, 0x7d2cd856U, 0x3390ef22U, + 0x494ec787U, 0x38d1c1d9U, 0xcaa2fe8cU, 0xd40b3698U, + 0xf581cfa6U, 0x7ade28a5U, 0xb78e26daU, 0xadbfa43fU, + 0x3a9de42cU, 0x78920d50U, 0x5fcc9b6aU, 0x7e466254U, + 0x8d13c2f6U, 0xd8b8e890U, 0x39f75e2eU, 0xc3aff582U, + 0x5d80be9fU, 0xd0937c69U, 0xd52da96fU, 0x2512b3cfU, + 0xac993bc8U, 0x187da710U, 0x9c636ee8U, 0x3bbb7bdbU, + 0x267809cdU, 0x5918f46eU, 0x9ab701ecU, 0x4f9aa883U, + 0x956e65e6U, 0xffe67eaaU, 0xbccf0821U, 0x15e8e6efU, + 0xe79bd9baU, 0x6f36ce4aU, 0x9f09d4eaU, 0xb07cd629U, + 0xa4b2af31U, 0x3f23312aU, 0xa59430c6U, 0xa266c035U, + 0x4ebc3774U, 0x82caa6fcU, 0x90d0b0e0U, 0xa7d81533U, + 0x04984af1U, 0xecdaf741U, 0xcd500e7fU, 0x91f62f17U, + 0x4dd68d76U, 0xefb04d43U, 0xaa4d54ccU, 0x9604dfe4U, + 0xd1b5e39eU, 0x6a881b4cU, 0x2c1fb8c1U, 0x65517f46U, + 0x5eea049dU, 0x8c355d01U, 0x877473faU, 0x0b412efbU, + 0x671d5ab3U, 0xdbd25292U, 0x105633e9U, 0xd647136dU, + 0xd7618c9aU, 0xa10c7a37U, 0xf8148e59U, 0x133c89ebU, + 0xa927eeceU, 0x61c935b7U, 0x1ce5ede1U, 0x47b13c7aU, + 0xd2df599cU, 0xf2733f55U, 0x14ce7918U, 0xc737bf73U, + 0xf7cdea53U, 0xfdaa5b5fU, 0x3d6f14dfU, 0x44db8678U, + 0xaff381caU, 0x68c43eb9U, 0x24342c38U, 0xa3405fc2U, + 0x1dc37216U, 0xe2250cbcU, 0x3c498b28U, 0x0d9541ffU, + 0xa8017139U, 0x0cb3de08U, 0xb4e49cd8U, 0x56c19064U, + 0xcb84617bU, 0x32b670d5U, 0x6c5c7448U, 0xb85742d0U, +}; +static const u32 Td4[256] = { + 0x52525252U, 0x09090909U, 0x6a6a6a6aU, 0xd5d5d5d5U, + 0x30303030U, 0x36363636U, 0xa5a5a5a5U, 0x38383838U, + 0xbfbfbfbfU, 0x40404040U, 0xa3a3a3a3U, 0x9e9e9e9eU, + 0x81818181U, 0xf3f3f3f3U, 0xd7d7d7d7U, 0xfbfbfbfbU, + 0x7c7c7c7cU, 0xe3e3e3e3U, 0x39393939U, 0x82828282U, + 0x9b9b9b9bU, 0x2f2f2f2fU, 0xffffffffU, 0x87878787U, + 0x34343434U, 0x8e8e8e8eU, 0x43434343U, 0x44444444U, + 0xc4c4c4c4U, 0xdedededeU, 0xe9e9e9e9U, 0xcbcbcbcbU, + 0x54545454U, 0x7b7b7b7bU, 0x94949494U, 0x32323232U, + 0xa6a6a6a6U, 0xc2c2c2c2U, 0x23232323U, 0x3d3d3d3dU, + 0xeeeeeeeeU, 0x4c4c4c4cU, 0x95959595U, 0x0b0b0b0bU, + 0x42424242U, 0xfafafafaU, 0xc3c3c3c3U, 0x4e4e4e4eU, + 0x08080808U, 0x2e2e2e2eU, 0xa1a1a1a1U, 0x66666666U, + 0x28282828U, 0xd9d9d9d9U, 0x24242424U, 0xb2b2b2b2U, + 0x76767676U, 0x5b5b5b5bU, 0xa2a2a2a2U, 0x49494949U, + 0x6d6d6d6dU, 0x8b8b8b8bU, 0xd1d1d1d1U, 0x25252525U, + 0x72727272U, 0xf8f8f8f8U, 0xf6f6f6f6U, 0x64646464U, + 0x86868686U, 0x68686868U, 0x98989898U, 0x16161616U, + 0xd4d4d4d4U, 0xa4a4a4a4U, 0x5c5c5c5cU, 0xccccccccU, + 0x5d5d5d5dU, 0x65656565U, 0xb6b6b6b6U, 0x92929292U, + 0x6c6c6c6cU, 0x70707070U, 0x48484848U, 0x50505050U, + 0xfdfdfdfdU, 0xededededU, 0xb9b9b9b9U, 0xdadadadaU, + 0x5e5e5e5eU, 0x15151515U, 0x46464646U, 0x57575757U, + 0xa7a7a7a7U, 0x8d8d8d8dU, 0x9d9d9d9dU, 0x84848484U, + 0x90909090U, 0xd8d8d8d8U, 0xababababU, 0x00000000U, + 0x8c8c8c8cU, 0xbcbcbcbcU, 0xd3d3d3d3U, 0x0a0a0a0aU, + 0xf7f7f7f7U, 0xe4e4e4e4U, 0x58585858U, 0x05050505U, + 0xb8b8b8b8U, 0xb3b3b3b3U, 0x45454545U, 0x06060606U, + 0xd0d0d0d0U, 0x2c2c2c2cU, 0x1e1e1e1eU, 0x8f8f8f8fU, + 0xcacacacaU, 0x3f3f3f3fU, 0x0f0f0f0fU, 0x02020202U, + 0xc1c1c1c1U, 0xafafafafU, 0xbdbdbdbdU, 0x03030303U, + 0x01010101U, 0x13131313U, 0x8a8a8a8aU, 0x6b6b6b6bU, + 0x3a3a3a3aU, 0x91919191U, 0x11111111U, 0x41414141U, + 0x4f4f4f4fU, 0x67676767U, 0xdcdcdcdcU, 0xeaeaeaeaU, + 0x97979797U, 0xf2f2f2f2U, 0xcfcfcfcfU, 0xcecececeU, + 0xf0f0f0f0U, 0xb4b4b4b4U, 0xe6e6e6e6U, 0x73737373U, + 0x96969696U, 0xacacacacU, 0x74747474U, 0x22222222U, + 0xe7e7e7e7U, 0xadadadadU, 0x35353535U, 0x85858585U, + 0xe2e2e2e2U, 0xf9f9f9f9U, 0x37373737U, 0xe8e8e8e8U, + 0x1c1c1c1cU, 0x75757575U, 0xdfdfdfdfU, 0x6e6e6e6eU, + 0x47474747U, 0xf1f1f1f1U, 0x1a1a1a1aU, 0x71717171U, + 0x1d1d1d1dU, 0x29292929U, 0xc5c5c5c5U, 0x89898989U, + 0x6f6f6f6fU, 0xb7b7b7b7U, 0x62626262U, 0x0e0e0e0eU, + 0xaaaaaaaaU, 0x18181818U, 0xbebebebeU, 0x1b1b1b1bU, + 0xfcfcfcfcU, 0x56565656U, 0x3e3e3e3eU, 0x4b4b4b4bU, + 0xc6c6c6c6U, 0xd2d2d2d2U, 0x79797979U, 0x20202020U, + 0x9a9a9a9aU, 0xdbdbdbdbU, 0xc0c0c0c0U, 0xfefefefeU, + 0x78787878U, 0xcdcdcdcdU, 0x5a5a5a5aU, 0xf4f4f4f4U, + 0x1f1f1f1fU, 0xddddddddU, 0xa8a8a8a8U, 0x33333333U, + 0x88888888U, 0x07070707U, 0xc7c7c7c7U, 0x31313131U, + 0xb1b1b1b1U, 0x12121212U, 0x10101010U, 0x59595959U, + 0x27272727U, 0x80808080U, 0xececececU, 0x5f5f5f5fU, + 0x60606060U, 0x51515151U, 0x7f7f7f7fU, 0xa9a9a9a9U, + 0x19191919U, 0xb5b5b5b5U, 0x4a4a4a4aU, 0x0d0d0d0dU, + 0x2d2d2d2dU, 0xe5e5e5e5U, 0x7a7a7a7aU, 0x9f9f9f9fU, + 0x93939393U, 0xc9c9c9c9U, 0x9c9c9c9cU, 0xefefefefU, + 0xa0a0a0a0U, 0xe0e0e0e0U, 0x3b3b3b3bU, 0x4d4d4d4dU, + 0xaeaeaeaeU, 0x2a2a2a2aU, 0xf5f5f5f5U, 0xb0b0b0b0U, + 0xc8c8c8c8U, 0xebebebebU, 0xbbbbbbbbU, 0x3c3c3c3cU, + 0x83838383U, 0x53535353U, 0x99999999U, 0x61616161U, + 0x17171717U, 0x2b2b2b2bU, 0x04040404U, 0x7e7e7e7eU, + 0xbabababaU, 0x77777777U, 0xd6d6d6d6U, 0x26262626U, + 0xe1e1e1e1U, 0x69696969U, 0x14141414U, 0x63636363U, + 0x55555555U, 0x21212121U, 0x0c0c0c0cU, 0x7d7d7d7dU, +}; +static const u32 rcon[] = { + 0x01000000, 0x02000000, 0x04000000, 0x08000000, + 0x10000000, 0x20000000, 0x40000000, 0x80000000, + 0x1B000000, 0x36000000, /* for 128-bit blocks, Rijndael never uses more than 10 rcon values */ +}; + +#define SWAP(x) (_lrotl(x, 8) & 0x00ff00ff | _lrotr(x, 8) & 0xff00ff00) + +#ifdef _MSC_VER +#define GETU32(p) SWAP(*((u32 *)(p))) +#define PUTU32(ct, st) { *((u32 *)(ct)) = SWAP((st)); } +#else +#define GETU32(pt) (((u32)(pt)[0] << 24) ^ ((u32)(pt)[1] << 16) ^ \ +((u32)(pt)[2] << 8) ^ ((u32)(pt)[3])) +#define PUTU32(ct, st) { \ +(ct)[0] = (u8)((st) >> 24); (ct)[1] = (u8)((st) >> 16); \ +(ct)[2] = (u8)((st) >> 8); (ct)[3] = (u8)(st); } +#endif + +/** + * Expand the cipher key into the encryption key schedule. + * + * @return the number of rounds for the given cipher key size. + */ +void rijndaelKeySetupEnc(u32 rk[/*44*/], const u8 cipherKey[]) +{ + int i; + u32 temp; + + rk[0] = GETU32(cipherKey ); + rk[1] = GETU32(cipherKey + 4); + rk[2] = GETU32(cipherKey + 8); + rk[3] = GETU32(cipherKey + 12); + for (i = 0; i < 10; i++) { + temp = rk[3]; + rk[4] = rk[0] ^ + (Te4[(temp >> 16) & 0xff] & 0xff000000) ^ + (Te4[(temp >> 8) & 0xff] & 0x00ff0000) ^ + (Te4[(temp ) & 0xff] & 0x0000ff00) ^ + (Te4[(temp >> 24) ] & 0x000000ff) ^ + rcon[i]; + rk[5] = rk[1] ^ rk[4]; + rk[6] = rk[2] ^ rk[5]; + rk[7] = rk[3] ^ rk[6]; + rk += 4; + } +} + +/** + * Expand the cipher key into the decryption key schedule. + * + * @return the number of rounds for the given cipher key size. + */ +void rijndaelKeySetupDec(u32 rk[/*44*/], const u8 cipherKey[]) +{ + int Nr = 10, i, j; + u32 temp; + + /* expand the cipher key: */ + rijndaelKeySetupEnc(rk, cipherKey); + /* invert the order of the round keys: */ + for (i = 0, j = 4*Nr; i < j; i += 4, j -= 4) { + temp = rk[i ]; rk[i ] = rk[j ]; rk[j ] = temp; + temp = rk[i + 1]; rk[i + 1] = rk[j + 1]; rk[j + 1] = temp; + temp = rk[i + 2]; rk[i + 2] = rk[j + 2]; rk[j + 2] = temp; + temp = rk[i + 3]; rk[i + 3] = rk[j + 3]; rk[j + 3] = temp; + } + /* apply the inverse MixColumn transform to all round keys but the + * first and the last: */ + for (i = 1; i < Nr; i++) { + rk += 4; + rk[0] = + Td0[Te4[(rk[0] >> 24) ] & 0xff] ^ + Td1[Te4[(rk[0] >> 16) & 0xff] & 0xff] ^ + Td2[Te4[(rk[0] >> 8) & 0xff] & 0xff] ^ + Td3[Te4[(rk[0] ) & 0xff] & 0xff]; + rk[1] = + Td0[Te4[(rk[1] >> 24) ] & 0xff] ^ + Td1[Te4[(rk[1] >> 16) & 0xff] & 0xff] ^ + Td2[Te4[(rk[1] >> 8) & 0xff] & 0xff] ^ + Td3[Te4[(rk[1] ) & 0xff] & 0xff]; + rk[2] = + Td0[Te4[(rk[2] >> 24) ] & 0xff] ^ + Td1[Te4[(rk[2] >> 16) & 0xff] & 0xff] ^ + Td2[Te4[(rk[2] >> 8) & 0xff] & 0xff] ^ + Td3[Te4[(rk[2] ) & 0xff] & 0xff]; + rk[3] = + Td0[Te4[(rk[3] >> 24) ] & 0xff] ^ + Td1[Te4[(rk[3] >> 16) & 0xff] & 0xff] ^ + Td2[Te4[(rk[3] >> 8) & 0xff] & 0xff] ^ + Td3[Te4[(rk[3] ) & 0xff] & 0xff]; + } +} + +void rijndaelEncrypt(const u32 rk[/*44*/], const u8 pt[16], u8 ct[16]) +{ + u32 s0, s1, s2, s3, t0, t1, t2, t3; + int Nr = 10; +#ifndef FULL_UNROLL + int r; +#endif /* ?FULL_UNROLL */ + + /* + * map byte array block to cipher state + * and add initial round key: + */ + s0 = GETU32(pt ) ^ rk[0]; + s1 = GETU32(pt + 4) ^ rk[1]; + s2 = GETU32(pt + 8) ^ rk[2]; + s3 = GETU32(pt + 12) ^ rk[3]; +#ifdef FULL_UNROLL + /* round 1: */ + t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[ 4]; + t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[ 5]; + t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[ 6]; + t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[ 7]; + /* round 2: */ + s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[ 8]; + s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[ 9]; + s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[10]; + s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[11]; + /* round 3: */ + t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[12]; + t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[13]; + t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[14]; + t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[15]; + /* round 4: */ + s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[16]; + s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[17]; + s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[18]; + s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[19]; + /* round 5: */ + t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[20]; + t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[21]; + t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[22]; + t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[23]; + /* round 6: */ + s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[24]; + s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[25]; + s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[26]; + s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[27]; + /* round 7: */ + t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[28]; + t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[29]; + t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[30]; + t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[31]; + /* round 8: */ + s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[32]; + s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[33]; + s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[34]; + s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[35]; + /* round 9: */ + t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[36]; + t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[37]; + t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[38]; + t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[39]; + rk += Nr << 2; +#else /* !FULL_UNROLL */ + /* + * Nr - 1 full rounds: + */ + r = Nr >> 1; + for (;;) { + t0 = + Te0[(s0 >> 24) ] ^ + Te1[(s1 >> 16) & 0xff] ^ + Te2[(s2 >> 8) & 0xff] ^ + Te3[(s3 ) & 0xff] ^ + rk[4]; + t1 = + Te0[(s1 >> 24) ] ^ + Te1[(s2 >> 16) & 0xff] ^ + Te2[(s3 >> 8) & 0xff] ^ + Te3[(s0 ) & 0xff] ^ + rk[5]; + t2 = + Te0[(s2 >> 24) ] ^ + Te1[(s3 >> 16) & 0xff] ^ + Te2[(s0 >> 8) & 0xff] ^ + Te3[(s1 ) & 0xff] ^ + rk[6]; + t3 = + Te0[(s3 >> 24) ] ^ + Te1[(s0 >> 16) & 0xff] ^ + Te2[(s1 >> 8) & 0xff] ^ + Te3[(s2 ) & 0xff] ^ + rk[7]; + + rk += 8; + if (--r == 0) { + break; + } + + s0 = + Te0[(t0 >> 24) ] ^ + Te1[(t1 >> 16) & 0xff] ^ + Te2[(t2 >> 8) & 0xff] ^ + Te3[(t3 ) & 0xff] ^ + rk[0]; + s1 = + Te0[(t1 >> 24) ] ^ + Te1[(t2 >> 16) & 0xff] ^ + Te2[(t3 >> 8) & 0xff] ^ + Te3[(t0 ) & 0xff] ^ + rk[1]; + s2 = + Te0[(t2 >> 24) ] ^ + Te1[(t3 >> 16) & 0xff] ^ + Te2[(t0 >> 8) & 0xff] ^ + Te3[(t1 ) & 0xff] ^ + rk[2]; + s3 = + Te0[(t3 >> 24) ] ^ + Te1[(t0 >> 16) & 0xff] ^ + Te2[(t1 >> 8) & 0xff] ^ + Te3[(t2 ) & 0xff] ^ + rk[3]; + } +#endif /* ?FULL_UNROLL */ + /* + * apply last round and + * map cipher state to byte array block: + */ + s0 = + (Te4[(t0 >> 24) ] & 0xff000000) ^ + (Te4[(t1 >> 16) & 0xff] & 0x00ff0000) ^ + (Te4[(t2 >> 8) & 0xff] & 0x0000ff00) ^ + (Te4[(t3 ) & 0xff] & 0x000000ff) ^ + rk[0]; + PUTU32(ct , s0); + s1 = + (Te4[(t1 >> 24) ] & 0xff000000) ^ + (Te4[(t2 >> 16) & 0xff] & 0x00ff0000) ^ + (Te4[(t3 >> 8) & 0xff] & 0x0000ff00) ^ + (Te4[(t0 ) & 0xff] & 0x000000ff) ^ + rk[1]; + PUTU32(ct + 4, s1); + s2 = + (Te4[(t2 >> 24) ] & 0xff000000) ^ + (Te4[(t3 >> 16) & 0xff] & 0x00ff0000) ^ + (Te4[(t0 >> 8) & 0xff] & 0x0000ff00) ^ + (Te4[(t1 ) & 0xff] & 0x000000ff) ^ + rk[2]; + PUTU32(ct + 8, s2); + s3 = + (Te4[(t3 >> 24) ] & 0xff000000) ^ + (Te4[(t0 >> 16) & 0xff] & 0x00ff0000) ^ + (Te4[(t1 >> 8) & 0xff] & 0x0000ff00) ^ + (Te4[(t2 ) & 0xff] & 0x000000ff) ^ + rk[3]; + PUTU32(ct + 12, s3); +} + +void rijndaelDecrypt(const u32 rk[/*44*/], const u8 ct[16], u8 pt[16]) +{ + u32 s0, s1, s2, s3, t0, t1, t2, t3; + int Nr = 10; +#ifndef FULL_UNROLL + int r; +#endif /* ?FULL_UNROLL */ + + /* + * map byte array block to cipher state + * and add initial round key: + */ + s0 = GETU32(ct ) ^ rk[0]; + s1 = GETU32(ct + 4) ^ rk[1]; + s2 = GETU32(ct + 8) ^ rk[2]; + s3 = GETU32(ct + 12) ^ rk[3]; +#ifdef FULL_UNROLL + /* round 1: */ + t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[ 4]; + t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[ 5]; + t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[ 6]; + t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[ 7]; + /* round 2: */ + s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[ 8]; + s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[ 9]; + s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[10]; + s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[11]; + /* round 3: */ + t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[12]; + t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[13]; + t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[14]; + t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[15]; + /* round 4: */ + s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[16]; + s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[17]; + s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[18]; + s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[19]; + /* round 5: */ + t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[20]; + t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[21]; + t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[22]; + t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[23]; + /* round 6: */ + s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[24]; + s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[25]; + s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[26]; + s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[27]; + /* round 7: */ + t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[28]; + t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[29]; + t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[30]; + t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[31]; + /* round 8: */ + s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[32]; + s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[33]; + s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[34]; + s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[35]; + /* round 9: */ + t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[36]; + t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[37]; + t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[38]; + t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[39]; + rk += Nr << 2; +#else /* !FULL_UNROLL */ + /* + * Nr - 1 full rounds: + */ + r = Nr >> 1; + for (;;) { + t0 = + Td0[(s0 >> 24) ] ^ + Td1[(s3 >> 16) & 0xff] ^ + Td2[(s2 >> 8) & 0xff] ^ + Td3[(s1 ) & 0xff] ^ + rk[4]; + t1 = + Td0[(s1 >> 24) ] ^ + Td1[(s0 >> 16) & 0xff] ^ + Td2[(s3 >> 8) & 0xff] ^ + Td3[(s2 ) & 0xff] ^ + rk[5]; + t2 = + Td0[(s2 >> 24) ] ^ + Td1[(s1 >> 16) & 0xff] ^ + Td2[(s0 >> 8) & 0xff] ^ + Td3[(s3 ) & 0xff] ^ + rk[6]; + t3 = + Td0[(s3 >> 24) ] ^ + Td1[(s2 >> 16) & 0xff] ^ + Td2[(s1 >> 8) & 0xff] ^ + Td3[(s0 ) & 0xff] ^ + rk[7]; + + rk += 8; + if (--r == 0) { + break; + } + + s0 = + Td0[(t0 >> 24) ] ^ + Td1[(t3 >> 16) & 0xff] ^ + Td2[(t2 >> 8) & 0xff] ^ + Td3[(t1 ) & 0xff] ^ + rk[0]; + s1 = + Td0[(t1 >> 24) ] ^ + Td1[(t0 >> 16) & 0xff] ^ + Td2[(t3 >> 8) & 0xff] ^ + Td3[(t2 ) & 0xff] ^ + rk[1]; + s2 = + Td0[(t2 >> 24) ] ^ + Td1[(t1 >> 16) & 0xff] ^ + Td2[(t0 >> 8) & 0xff] ^ + Td3[(t3 ) & 0xff] ^ + rk[2]; + s3 = + Td0[(t3 >> 24) ] ^ + Td1[(t2 >> 16) & 0xff] ^ + Td2[(t1 >> 8) & 0xff] ^ + Td3[(t0 ) & 0xff] ^ + rk[3]; + } +#endif /* ?FULL_UNROLL */ + /* + * apply last round and + * map cipher state to byte array block: + */ + s0 = + (Td4[(t0 >> 24) ] & 0xff000000) ^ + (Td4[(t3 >> 16) & 0xff] & 0x00ff0000) ^ + (Td4[(t2 >> 8) & 0xff] & 0x0000ff00) ^ + (Td4[(t1 ) & 0xff] & 0x000000ff) ^ + rk[0]; + PUTU32(pt , s0); + s1 = + (Td4[(t1 >> 24) ] & 0xff000000) ^ + (Td4[(t0 >> 16) & 0xff] & 0x00ff0000) ^ + (Td4[(t3 >> 8) & 0xff] & 0x0000ff00) ^ + (Td4[(t2 ) & 0xff] & 0x000000ff) ^ + rk[1]; + PUTU32(pt + 4, s1); + s2 = + (Td4[(t2 >> 24) ] & 0xff000000) ^ + (Td4[(t1 >> 16) & 0xff] & 0x00ff0000) ^ + (Td4[(t0 >> 8) & 0xff] & 0x0000ff00) ^ + (Td4[(t3 ) & 0xff] & 0x000000ff) ^ + rk[2]; + PUTU32(pt + 8, s2); + s3 = + (Td4[(t3 >> 24) ] & 0xff000000) ^ + (Td4[(t2 >> 16) & 0xff] & 0x00ff0000) ^ + (Td4[(t1 >> 8) & 0xff] & 0x0000ff00) ^ + (Td4[(t0 ) & 0xff] & 0x000000ff) ^ + rk[3]; + PUTU32(pt + 12, s3); +} diff --git a/contrib/wpa_supplicant/aes_wrap.c b/contrib/wpa_supplicant/aes_wrap.c new file mode 100644 index 000000000000..dbcc136517e7 --- /dev/null +++ b/contrib/wpa_supplicant/aes_wrap.c @@ -0,0 +1,642 @@ +/* + * AES Key Wrap Algorithm (128-bit KEK) (RFC3394) + * One-Key CBC MAC (OMAC1) hash with AES-128 + * AES-128 CTR mode encryption + * AES-128 EAX mode encryption/decryption + * AES-128 CBC + * Copyright (c) 2003-2004, Jouni Malinen <jkmaline@cc.hut.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include "common.h" +#include "aes_wrap.h" + +#ifdef EAP_TLS_FUNCS + +#include <openssl/aes.h> + +#else /* EAP_TLS_FUNCS */ + +#include "aes.c" + +struct aes_key_st { + u32 rk[44]; +}; +typedef struct aes_key_st AES_KEY; + +#define AES_set_encrypt_key(userKey, bits, key) \ + rijndaelKeySetupEnc((key)->rk, (userKey)) +#define AES_set_decrypt_key(userKey, bits, key) \ + rijndaelKeySetupDec((key)->rk, (userKey)) +#define AES_encrypt(in, out, key) \ + rijndaelEncrypt((key)->rk, in, out) +#define AES_decrypt(in, out, key) \ + rijndaelDecrypt((key)->rk, in, out) + +#endif /* EAP_TLS_FUNCS */ + + +/* + * @kek: key encryption key (KEK) + * @n: length of the wrapped key in 64-bit units; e.g., 2 = 128-bit = 16 bytes + * @plain: plaintext key to be wrapped, n * 64 bit + * @cipher: wrapped key, (n + 1) * 64 bit + */ +void aes_wrap(u8 *kek, int n, u8 *plain, u8 *cipher) +{ + u8 *a, *r, b[16]; + int i, j; + AES_KEY key; + + a = cipher; + r = cipher + 8; + + /* 1) Initialize variables. */ + memset(a, 0xa6, 8); + memcpy(r, plain, 8 * n); + + AES_set_encrypt_key(kek, 128, &key); + + /* 2) Calculate intermediate values. + * For j = 0 to 5 + * For i=1 to n + * B = AES(K, A | R[i]) + * A = MSB(64, B) ^ t where t = (n*j)+i + * R[i] = LSB(64, B) + */ + for (j = 0; j <= 5; j++) { + r = cipher + 8; + for (i = 1; i <= n; i++) { + memcpy(b, a, 8); + memcpy(b + 8, r, 8); + AES_encrypt(b, b, &key); + memcpy(a, b, 8); + a[7] ^= n * j + i; + memcpy(r, b + 8, 8); + r += 8; + } + } + + /* 3) Output the results. + * + * These are already in @cipher due to the location of temporary + * variables. + */ +} + + +/* + * @kek: key encryption key (KEK) + * @n: length of the wrapped key in 64-bit units; e.g., 2 = 128-bit = 16 bytes + * @cipher: wrapped key to be unwrapped, (n + 1) * 64 bit + * @plain: plaintext key, n * 64 bit + */ +int aes_unwrap(u8 *kek, int n, u8 *cipher, u8 *plain) +{ + u8 a[8], *r, b[16]; + int i, j; + AES_KEY key; + + /* 1) Initialize variables. */ + memcpy(a, cipher, 8); + r = plain; + memcpy(r, cipher + 8, 8 * n); + + AES_set_decrypt_key(kek, 128, &key); + + /* 2) Compute intermediate values. + * For j = 5 to 0 + * For i = n to 1 + * B = AES-1(K, (A ^ t) | R[i]) where t = n*j+i + * A = MSB(64, B) + * R[i] = LSB(64, B) + */ + for (j = 5; j >= 0; j--) { + r = plain + (n - 1) * 8; + for (i = n; i >= 1; i--) { + memcpy(b, a, 8); + b[7] ^= n * j + i; + + memcpy(b + 8, r, 8); + AES_decrypt(b, b, &key); + memcpy(a, b, 8); + memcpy(r, b + 8, 8); + r -= 8; + } + } + + /* 3) Output results. + * + * These are already in @plain due to the location of temporary + * variables. Just verify that the IV matches with the expected value. + */ + for (i = 0; i < 8; i++) { + if (a[i] != 0xa6) + return -1; + } + + return 0; +} + + +#define BLOCK_SIZE 16 + +static void gf_mulx(u8 *pad) +{ + int i, carry; + + carry = pad[0] & 0x80; + for (i = 0; i < BLOCK_SIZE - 1; i++) + pad[i] = (pad[i] << 1) | (pad[i + 1] >> 7); + pad[BLOCK_SIZE - 1] <<= 1; + if (carry) + pad[BLOCK_SIZE - 1] ^= 0x87; +} + + +void omac1_aes_128(const u8 *key, const u8 *data, size_t data_len, u8 *mac) +{ + AES_KEY akey; + u8 cbc[BLOCK_SIZE], pad[BLOCK_SIZE]; + const u8 *pos = data; + int i; + size_t left = data_len; + + AES_set_encrypt_key(key, 128, &akey); + memset(cbc, 0, BLOCK_SIZE); + + while (left >= BLOCK_SIZE) { + for (i = 0; i < BLOCK_SIZE; i++) + cbc[i] ^= *pos++; + if (left > BLOCK_SIZE) + AES_encrypt(cbc, cbc, &akey); + left -= BLOCK_SIZE; + } + + memset(pad, 0, BLOCK_SIZE); + AES_encrypt(pad, pad, &akey); + gf_mulx(pad); + + if (left || data_len == 0) { + for (i = 0; i < left; i++) + cbc[i] ^= *pos++; + cbc[left] ^= 0x80; + gf_mulx(pad); + } + + for (i = 0; i < BLOCK_SIZE; i++) + pad[i] ^= cbc[i]; + AES_encrypt(pad, mac, &akey); +} + + +void aes_128_encrypt_block(const u8 *key, const u8 *in, u8 *out) +{ + AES_KEY akey; + AES_set_encrypt_key(key, 128, &akey); + AES_encrypt(in, out, &akey); +} + + +void aes_128_ctr_encrypt(const u8 *key, const u8 *nonce, + u8 *data, size_t data_len) +{ + AES_KEY akey; + size_t len, left = data_len; + int i; + u8 *pos = data; + u8 counter[BLOCK_SIZE], buf[BLOCK_SIZE]; + + AES_set_encrypt_key(key, 128, &akey); + memcpy(counter, nonce, BLOCK_SIZE); + + while (left > 0) { + AES_encrypt(counter, buf, &akey); + + len = (left < BLOCK_SIZE) ? left : BLOCK_SIZE; + for (i = 0; i < len; i++) + pos[i] ^= buf[i]; + pos += len; + left -= len; + + for (i = BLOCK_SIZE - 1; i >= 0; i--) { + counter[i]++; + if (counter[i]) + break; + } + } +} + + +int aes_128_eax_encrypt(const u8 *key, const u8 *nonce, size_t nonce_len, + const u8 *hdr, size_t hdr_len, + u8 *data, size_t data_len, u8 *tag) +{ + u8 *buf; + size_t buf_len; + u8 nonce_mac[BLOCK_SIZE], hdr_mac[BLOCK_SIZE], data_mac[BLOCK_SIZE]; + int i; + + if (nonce_len > data_len) + buf_len = nonce_len; + else + buf_len = data_len; + if (hdr_len > buf_len) + buf_len = hdr_len; + buf_len += 16; + + buf = malloc(buf_len); + if (buf == NULL) + return -1; + + memset(buf, 0, 15); + + buf[15] = 0; + memcpy(buf + 16, nonce, nonce_len); + omac1_aes_128(key, buf, 16 + nonce_len, nonce_mac); + + buf[15] = 1; + memcpy(buf + 16, hdr, hdr_len); + omac1_aes_128(key, buf, 16 + hdr_len, hdr_mac); + + aes_128_ctr_encrypt(key, nonce_mac, data, data_len); + buf[15] = 2; + memcpy(buf + 16, data, data_len); + omac1_aes_128(key, buf, 16 + data_len, data_mac); + + free(buf); + + for (i = 0; i < BLOCK_SIZE; i++) + tag[i] = nonce_mac[i] ^ data_mac[i] ^ hdr_mac[i]; + + return 0; +} + + +int aes_128_eax_decrypt(const u8 *key, const u8 *nonce, size_t nonce_len, + const u8 *hdr, size_t hdr_len, + u8 *data, size_t data_len, const u8 *tag) +{ + u8 *buf; + size_t buf_len; + u8 nonce_mac[BLOCK_SIZE], hdr_mac[BLOCK_SIZE], data_mac[BLOCK_SIZE]; + int i; + + if (nonce_len > data_len) + buf_len = nonce_len; + else + buf_len = data_len; + if (hdr_len > buf_len) + buf_len = hdr_len; + buf_len += 16; + + buf = malloc(buf_len); + if (buf == NULL) + return -1; + + memset(buf, 0, 15); + + buf[15] = 0; + memcpy(buf + 16, nonce, nonce_len); + omac1_aes_128(key, buf, 16 + nonce_len, nonce_mac); + + buf[15] = 1; + memcpy(buf + 16, hdr, hdr_len); + omac1_aes_128(key, buf, 16 + hdr_len, hdr_mac); + + buf[15] = 2; + memcpy(buf + 16, data, data_len); + omac1_aes_128(key, buf, 16 + data_len, data_mac); + + free(buf); + + for (i = 0; i < BLOCK_SIZE; i++) { + if (tag[i] != (nonce_mac[i] ^ data_mac[i] ^ hdr_mac[i])) + return -2; + } + + aes_128_ctr_encrypt(key, nonce_mac, data, data_len); + + return 0; +} + + +void aes_128_cbc_encrypt(const u8 *key, const u8 *iv, u8 *data, + size_t data_len) +{ + AES_KEY akey; + u8 cbc[BLOCK_SIZE]; + u8 *pos = data; + int i, j, blocks; + + AES_set_encrypt_key(key, 128, &akey); + memcpy(cbc, iv, BLOCK_SIZE); + + blocks = data_len / BLOCK_SIZE; + for (i = 0; i < blocks; i++) { + for (j = 0; j < BLOCK_SIZE; j++) + cbc[j] ^= pos[j]; + AES_encrypt(cbc, cbc, &akey); + memcpy(pos, cbc, BLOCK_SIZE); + pos += BLOCK_SIZE; + } +} + + +void aes_128_cbc_decrypt(const u8 *key, const u8 *iv, u8 *data, + size_t data_len) +{ + AES_KEY akey; + u8 cbc[BLOCK_SIZE], tmp[BLOCK_SIZE]; + u8 *pos = data; + int i, j, blocks; + + AES_set_decrypt_key(key, 128, &akey); + memcpy(cbc, iv, BLOCK_SIZE); + + blocks = data_len / BLOCK_SIZE; + for (i = 0; i < blocks; i++) { + memcpy(tmp, pos, BLOCK_SIZE); + AES_decrypt(pos, pos, &akey); + for (j = 0; j < BLOCK_SIZE; j++) + pos[j] ^= cbc[j]; + memcpy(cbc, tmp, BLOCK_SIZE); + pos += BLOCK_SIZE; + } +} + + +#ifdef TEST_MAIN + +#ifdef __i386__ +#define rdtscll(val) \ + __asm__ __volatile__("rdtsc" : "=A" (val)) + +static void test_aes_perf(void) +{ + const int num_iters = 10; + int i; + unsigned int start, end; + AES_KEY akey; + u8 key[16], pt[16], ct[16]; + + printf("keySetupEnc:"); + for (i = 0; i < num_iters; i++) { + rdtscll(start); + AES_set_encrypt_key(key, 128, &akey); + rdtscll(end); + printf(" %d", end - start); + } + printf("\n"); + + printf("Encrypt:"); + for (i = 0; i < num_iters; i++) { + rdtscll(start); + AES_encrypt(pt, ct, &akey); + rdtscll(end); + printf(" %d", end - start); + } + printf("\n"); +} +#endif /* __i386__ */ + + +static int test_eax(void) +{ + u8 msg[] = { 0xF7, 0xFB }; + u8 key[] = { 0x91, 0x94, 0x5D, 0x3F, 0x4D, 0xCB, 0xEE, 0x0B, + 0xF4, 0x5E, 0xF5, 0x22, 0x55, 0xF0, 0x95, 0xA4 }; + u8 nonce[] = { 0xBE, 0xCA, 0xF0, 0x43, 0xB0, 0xA2, 0x3D, 0x84, + 0x31, 0x94, 0xBA, 0x97, 0x2C, 0x66, 0xDE, 0xBD }; + u8 hdr[] = { 0xFA, 0x3B, 0xFD, 0x48, 0x06, 0xEB, 0x53, 0xFA }; + u8 cipher[] = { 0x19, 0xDD, 0x5C, 0x4C, 0x93, 0x31, 0x04, 0x9D, + 0x0B, 0xDA, 0xB0, 0x27, 0x74, 0x08, 0xF6, 0x79, + 0x67, 0xE5 }; + u8 data[sizeof(msg)], tag[BLOCK_SIZE]; + + memcpy(data, msg, sizeof(msg)); + if (aes_128_eax_encrypt(key, nonce, sizeof(nonce), hdr, sizeof(hdr), + data, sizeof(data), tag)) { + printf("AES-128 EAX mode encryption failed\n"); + return 1; + } + if (memcmp(data, cipher, sizeof(data)) != 0) { + printf("AES-128 EAX mode encryption returned invalid cipher " + "text\n"); + return 1; + } + if (memcmp(tag, cipher + sizeof(data), BLOCK_SIZE) != 0) { + printf("AES-128 EAX mode encryption returned invalid tag\n"); + return 1; + } + + if (aes_128_eax_decrypt(key, nonce, sizeof(nonce), hdr, sizeof(hdr), + data, sizeof(data), tag)) { + printf("AES-128 EAX mode decryption failed\n"); + return 1; + } + if (memcmp(data, msg, sizeof(data)) != 0) { + printf("AES-128 EAX mode decryption returned invalid plain " + "text\n"); + return 1; + } + + return 0; +} + + +static int test_cbc(void) +{ + struct cbc_test_vector { + u8 key[16]; + u8 iv[16]; + u8 plain[32]; + u8 cipher[32]; + size_t len; + } vectors[] = { + { + { 0x06, 0xa9, 0x21, 0x40, 0x36, 0xb8, 0xa1, 0x5b, + 0x51, 0x2e, 0x03, 0xd5, 0x34, 0x12, 0x00, 0x06 }, + { 0x3d, 0xaf, 0xba, 0x42, 0x9d, 0x9e, 0xb4, 0x30, + 0xb4, 0x22, 0xda, 0x80, 0x2c, 0x9f, 0xac, 0x41 }, + "Single block msg", + { 0xe3, 0x53, 0x77, 0x9c, 0x10, 0x79, 0xae, 0xb8, + 0x27, 0x08, 0x94, 0x2d, 0xbe, 0x77, 0x18, 0x1a }, + 16 + }, + { + { 0xc2, 0x86, 0x69, 0x6d, 0x88, 0x7c, 0x9a, 0xa0, + 0x61, 0x1b, 0xbb, 0x3e, 0x20, 0x25, 0xa4, 0x5a }, + { 0x56, 0x2e, 0x17, 0x99, 0x6d, 0x09, 0x3d, 0x28, + 0xdd, 0xb3, 0xba, 0x69, 0x5a, 0x2e, 0x6f, 0x58 }, + { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f }, + { 0xd2, 0x96, 0xcd, 0x94, 0xc2, 0xcc, 0xcf, 0x8a, + 0x3a, 0x86, 0x30, 0x28, 0xb5, 0xe1, 0xdc, 0x0a, + 0x75, 0x86, 0x60, 0x2d, 0x25, 0x3c, 0xff, 0xf9, + 0x1b, 0x82, 0x66, 0xbe, 0xa6, 0xd6, 0x1a, 0xb1 }, + 32 + } + }; + int i, ret = 0; + u8 *buf; + + for (i = 0; i < sizeof(vectors) / sizeof(vectors[0]); i++) { + struct cbc_test_vector *tv = &vectors[i]; + buf = malloc(tv->len); + if (buf == NULL) { + ret++; + break; + } + memcpy(buf, tv->plain, tv->len); + aes_128_cbc_encrypt(tv->key, tv->iv, buf, tv->len); + if (memcmp(buf, tv->cipher, tv->len) != 0) { + printf("AES-CBC encrypt %d failed\n", i); + ret++; + } + memcpy(buf, tv->cipher, tv->len); + aes_128_cbc_decrypt(tv->key, tv->iv, buf, tv->len); + if (memcmp(buf, tv->plain, tv->len) != 0) { + printf("AES-CBC decrypt %d failed\n", i); + ret++; + } + free(buf); + } + + return ret; +} + + +/* OMAC1 AES-128 test vectors from + * http://csrc.nist.gov/CryptoToolkit/modes/proposedmodes/omac/omac-ad.pdf + */ + +struct omac1_test_vector { + u8 k[16]; + u8 msg[64]; + int msg_len; + u8 tag[16]; +}; + +static struct omac1_test_vector test_vectors[] = +{ + { + { 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, + 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c }, + { }, + 0, + { 0xbb, 0x1d, 0x69, 0x29, 0xe9, 0x59, 0x37, 0x28, + 0x7f, 0xa3, 0x7d, 0x12, 0x9b, 0x75, 0x67, 0x46 } + }, + { + { 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, + 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c }, + { 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, + 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a}, + 16, + { 0x07, 0x0a, 0x16, 0xb4, 0x6b, 0x4d, 0x41, 0x44, + 0xf7, 0x9b, 0xdd, 0x9d, 0xd0, 0x4a, 0x28, 0x7c } + }, + { + { 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, + 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c }, + { 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, + 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a, + 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, + 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51, + 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11 }, + 40, + { 0xdf, 0xa6, 0x67, 0x47, 0xde, 0x9a, 0xe6, 0x30, + 0x30, 0xca, 0x32, 0x61, 0x14, 0x97, 0xc8, 0x27 } + }, + { + { 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, + 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c }, + { 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, + 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a, + 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, + 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51, + 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11, + 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef, + 0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17, + 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10 }, + 64, + { 0x51, 0xf0, 0xbe, 0xbf, 0x7e, 0x3b, 0x9d, 0x92, + 0xfc, 0x49, 0x74, 0x17, 0x79, 0x36, 0x3c, 0xfe } + }, +}; + + +int main(int argc, char *argv[]) +{ + u8 kek[] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f + }; + u8 plain[] = { + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, + 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff + }; + u8 crypt[] = { + 0x1F, 0xA6, 0x8B, 0x0A, 0x81, 0x12, 0xB4, 0x47, + 0xAE, 0xF3, 0x4B, 0xD8, 0xFB, 0x5A, 0x7B, 0x82, + 0x9D, 0x3E, 0x86, 0x23, 0x71, 0xD2, 0xCF, 0xE5 + }; + u8 result[24]; + int ret = 0, i; + struct omac1_test_vector *tv; + + aes_wrap(kek, 2, plain, result); + if (memcmp(result, crypt, 24) != 0) { + printf("AES-WRAP-128-128 failed\n"); + ret++; + } + if (aes_unwrap(kek, 2, crypt, result)) { + printf("AES-UNWRAP-128-128 reported failure\n"); + ret++; + } + if (memcmp(result, plain, 16) != 0) { + int i; + printf("AES-UNWRAP-128-128 failed\n"); + ret++; + for (i = 0; i < 16; i++) + printf(" %02x", result[i]); + printf("\n"); + } + +#ifdef __i386__ + test_aes_perf(); +#endif /* __i386__ */ + + for (i = 0; i < sizeof(test_vectors) / sizeof(test_vectors[0]); i++) { + tv = &test_vectors[i]; + omac1_aes_128(tv->k, tv->msg, tv->msg_len, result); + if (memcmp(result, tv->tag, 16) != 0) { + printf("OMAC1-AES-128 test vector %d failed\n", i); + ret++; + } + } + + ret += test_eax(); + + ret += test_cbc(); + + if (ret) + printf("FAILED!\n"); + + return ret; +} +#endif /* TEST_MAIN */ diff --git a/contrib/wpa_supplicant/aes_wrap.h b/contrib/wpa_supplicant/aes_wrap.h new file mode 100644 index 000000000000..70e83ea09d73 --- /dev/null +++ b/contrib/wpa_supplicant/aes_wrap.h @@ -0,0 +1,21 @@ +#ifndef AES_WRAP_H +#define AES_WRAP_H + +void aes_wrap(u8 *kek, int n, u8 *plain, u8 *cipher); +int aes_unwrap(u8 *kek, int n, u8 *cipher, u8 *plain); +void omac1_aes_128(const u8 *key, const u8 *data, size_t data_len, u8 *mac); +void aes_128_encrypt_block(const u8 *key, const u8 *in, u8 *out); +void aes_128_ctr_encrypt(const u8 *key, const u8 *nonce, + u8 *data, size_t data_len); +int aes_128_eax_encrypt(const u8 *key, const u8 *nonce, size_t nonce_len, + const u8 *hdr, size_t hdr_len, + u8 *data, size_t data_len, u8 *tag); +int aes_128_eax_decrypt(const u8 *key, const u8 *nonce, size_t nonce_len, + const u8 *hdr, size_t hdr_len, + u8 *data, size_t data_len, const u8 *tag); +void aes_128_cbc_encrypt(const u8 *key, const u8 *iv, u8 *data, + size_t data_len); +void aes_128_cbc_decrypt(const u8 *key, const u8 *iv, u8 *data, + size_t data_len); + +#endif /* AES_WRAP_H */ diff --git a/contrib/wpa_supplicant/common.c b/contrib/wpa_supplicant/common.c new file mode 100644 index 000000000000..071ffe87c1d1 --- /dev/null +++ b/contrib/wpa_supplicant/common.c @@ -0,0 +1,335 @@ +/* + * Host AP (software wireless LAN access point) user space daemon for + * Host AP kernel driver / common helper functions, etc. + * Copyright (c) 2002-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <errno.h> +#include <stdarg.h> +#include <ctype.h> +#include <time.h> +#include <sys/time.h> + +#include "common.h" + + +int wpa_debug_level = MSG_INFO; +int wpa_debug_show_keys = 0; +int wpa_debug_timestamp = 0; + + +int hostapd_get_rand(u8 *buf, size_t len) +{ +#ifdef CONFIG_NATIVE_WINDOWS + int i; + /* FIX: use more secure pseudo random number generator */ + for (i = 0; i < len; i++) { + buf[i] = rand(); + } + return 0; +#else /* CONFIG_NATIVE_WINDOWS */ + FILE *f; + size_t rc; + + f = fopen("/dev/urandom", "r"); + if (f == NULL) { + printf("Could not open /dev/urandom.\n"); + return -1; + } + + rc = fread(buf, 1, len, f); + fclose(f); + + return rc != len ? -1 : 0; +#endif /* CONFIG_NATIVE_WINDOWS */ +} + + +void hostapd_hexdump(const char *title, const u8 *buf, size_t len) +{ + size_t i; + printf("%s - hexdump(len=%lu):", title, (unsigned long) len); + for (i = 0; i < len; i++) + printf(" %02x", buf[i]); + printf("\n"); +} + + +static int hex2num(char c) +{ + if (c >= '0' && c <= '9') + return c - '0'; + if (c >= 'a' && c <= 'f') + return c - 'a' + 10; + if (c >= 'A' && c <= 'F') + return c - 'A' + 10; + return -1; +} + + +static int hex2byte(const char *hex) +{ + int a, b; + a = hex2num(*hex++); + if (a < 0) + return -1; + b = hex2num(*hex++); + if (b < 0) + return -1; + return (a << 4) | b; +} + + +int hwaddr_aton(const char *txt, u8 *addr) +{ + int i; + + for (i = 0; i < 6; i++) { + int a, b; + + a = hex2num(*txt++); + if (a < 0) + return -1; + b = hex2num(*txt++); + if (b < 0) + return -1; + *addr++ = (a << 4) | b; + if (i < 5 && *txt++ != ':') + return -1; + } + + return 0; +} + + +int hexstr2bin(const char *hex, u8 *buf, size_t len) +{ + int i, a; + const char *ipos = hex; + u8 *opos = buf; + + for (i = 0; i < len; i++) { + a = hex2byte(ipos); + if (a < 0) + return -1; + *opos++ = a; + ipos += 2; + } + return 0; +} + + +char * rel2abs_path(const char *rel_path) +{ + char *buf = NULL, *cwd, *ret; + size_t len = 128, cwd_len, rel_len, ret_len; + + if (rel_path[0] == '/') + return strdup(rel_path); + + for (;;) { + buf = malloc(len); + if (buf == NULL) + return NULL; + cwd = getcwd(buf, len); + if (cwd == NULL) { + free(buf); + if (errno != ERANGE) { + return NULL; + } + len *= 2; + } else { + break; + } + } + + cwd_len = strlen(cwd); + rel_len = strlen(rel_path); + ret_len = cwd_len + 1 + rel_len + 1; + ret = malloc(ret_len); + if (ret) { + memcpy(ret, cwd, cwd_len); + ret[cwd_len] = '/'; + memcpy(ret + cwd_len + 1, rel_path, rel_len); + ret[ret_len - 1] = '\0'; + } + free(buf); + return ret; +} + + +void inc_byte_array(u8 *counter, size_t len) +{ + int pos = len - 1; + while (pos >= 0) { + counter[pos]++; + if (counter[pos] != 0) + break; + pos--; + } +} + + +void print_char(char c) +{ + if (c >= 32 && c < 127) + printf("%c", c); + else + printf("<%02x>", c); +} + + +void fprint_char(FILE *f, char c) +{ + if (c >= 32 && c < 127) + fprintf(f, "%c", c); + else + fprintf(f, "<%02x>", c); +} + + +static void wpa_debug_print_timestamp(void) +{ + struct timeval tv; + char buf[16]; + + if (!wpa_debug_timestamp) + return; + + gettimeofday(&tv, NULL); + if (strftime(buf, sizeof(buf), "%b %d %H:%M:%S", + localtime((const time_t *) &tv.tv_sec)) <= 0) { + snprintf(buf, sizeof(buf), "%u", (int) tv.tv_sec); + } + printf("%s.%06u: ", buf, (unsigned int) tv.tv_usec); +} + + +void wpa_printf(int level, char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + if (level >= wpa_debug_level) { + wpa_debug_print_timestamp(); + vprintf(fmt, ap); + printf("\n"); + } + va_end(ap); +} + + +static void _wpa_hexdump(int level, const char *title, const u8 *buf, + size_t len, int show) +{ + size_t i; + if (level < wpa_debug_level) + return; + wpa_debug_print_timestamp(); + printf("%s - hexdump(len=%lu):", title, (unsigned long) len); + if (show) { + for (i = 0; i < len; i++) + printf(" %02x", buf[i]); + } else { + printf(" [REMOVED]"); + } + printf("\n"); +} + +void wpa_hexdump(int level, const char *title, const u8 *buf, size_t len) +{ + _wpa_hexdump(level, title, buf, len, 1); +} + + +void wpa_hexdump_key(int level, const char *title, const u8 *buf, size_t len) +{ + _wpa_hexdump(level, title, buf, len, wpa_debug_show_keys); +} + + +static void _wpa_hexdump_ascii(int level, const char *title, const u8 *buf, + size_t len, int show) +{ + int i, llen; + const u8 *pos = buf; + const int line_len = 16; + + if (level < wpa_debug_level) + return; + wpa_debug_print_timestamp(); + if (!show) { + printf("%s - hexdump_ascii(len=%lu): [REMOVED]\n", + title, (unsigned long) len); + return; + } + printf("%s - hexdump_ascii(len=%lu):\n", title, (unsigned long) len); + while (len) { + llen = len > line_len ? line_len : len; + printf(" "); + for (i = 0; i < llen; i++) + printf(" %02x", pos[i]); + for (i = llen; i < line_len; i++) + printf(" "); + printf(" "); + for (i = 0; i < llen; i++) { + if (isprint(pos[i])) + printf("%c", pos[i]); + else + printf("_"); + } + for (i = llen; i < line_len; i++) + printf(" "); + printf("\n"); + pos += llen; + len -= llen; + } +} + + +void wpa_hexdump_ascii(int level, const char *title, const u8 *buf, size_t len) +{ + _wpa_hexdump_ascii(level, title, buf, len, 1); +} + + +void wpa_hexdump_ascii_key(int level, const char *title, const u8 *buf, + size_t len) +{ + _wpa_hexdump_ascii(level, title, buf, len, wpa_debug_show_keys); +} + + +#ifdef CONFIG_NATIVE_WINDOWS + +#define EPOCHFILETIME (116444736000000000ULL) + +int gettimeofday(struct timeval *tv, struct timezone *tz) +{ + FILETIME ft; + LARGE_INTEGER li; + ULONGLONG t; + + GetSystemTimeAsFileTime(&ft); + li.LowPart = ft.dwLowDateTime; + li.HighPart = ft.dwHighDateTime; + t = (li.QuadPart - EPOCHFILETIME) / 10; + tv->tv_sec = (long) (t / 1000000); + tv->tv_usec = (long) (t % 1000000); + + return 0; +} +#endif /* CONFIG_NATIVE_WINDOWS */ diff --git a/contrib/wpa_supplicant/common.h b/contrib/wpa_supplicant/common.h new file mode 100644 index 000000000000..aa6429c76b96 --- /dev/null +++ b/contrib/wpa_supplicant/common.h @@ -0,0 +1,222 @@ +#ifndef COMMON_H +#define COMMON_H + +#ifdef __linux__ +#include <endian.h> +#include <byteswap.h> +#endif +#ifdef __FreeBSD__ +#include <sys/types.h> +#include <sys/endian.h> +#define bswap_16 bswap16 +#define bswap_32 bswap32 +#endif + +#ifdef CONFIG_NATIVE_WINDOWS +#include <winsock.h> +#include <winsock2.h> + +static inline int daemon(int nochdir, int noclose) +{ + printf("Windows - daemon() not supported yet\n"); + return -1; +} + +static inline void sleep(int seconds) +{ + Sleep(seconds * 1000); +} + +static inline void usleep(unsigned long usec) +{ + Sleep(usec / 1000); +} + +#ifndef timersub +#define timersub(a, b, res) do { \ + (res)->tv_sec = (a)->tv_sec - (b)->tv_sec; \ + (res)->tv_usec = (a)->tv_usec - (b)->tv_usec; \ + if ((res)->tv_usec < 0) { \ + (res)->tv_sec--; \ + (res)->tv_usec += 1000000; \ + } \ +} while (0) +#endif + +struct timezone { + int tz_minuteswest; + int tz_dsttime; +}; + +int gettimeofday(struct timeval *tv, struct timezone *tz); + +#endif /* CONFIG_NATIVE_WINDOWS */ + +#if defined(__CYGWIN__) || defined(CONFIG_NATIVE_WINDOWS) + +static inline unsigned short wpa_swap_16(unsigned short v) +{ + return ((v & 0xff) << 8) | (v >> 8); +} + +static inline unsigned int wpa_swap_32(unsigned int v) +{ + return ((v & 0xff) << 24) | ((v & 0xff00) << 8) | + ((v & 0xff0000) >> 8) | (v >> 24); +} + +#define le_to_host16(n) (n) +#define host_to_le16(n) (n) +#define be_to_host16(n) wpa_swap_16(n) +#define host_to_be16(n) wpa_swap_16(n) +#define le_to_host32(n) (n) +#define be_to_host32(n) wpa_swap_32(n) +#define host_to_be32(n) wpa_swap_32(n) + +#else /* __CYGWIN__ */ + +#if __BYTE_ORDER == __LITTLE_ENDIAN +#define le_to_host16(n) (n) +#define host_to_le16(n) (n) +#define be_to_host16(n) bswap_16(n) +#define host_to_be16(n) bswap_16(n) +#define le_to_host32(n) (n) +#define be_to_host32(n) bswap_32(n) +#define host_to_be32(n) bswap_32(n) +#elif __BYTE_ORDER == __BIG_ENDIAN +#define le_to_host16(n) bswap_16(n) +#define host_to_le16(n) bswap_16(n) +#define be_to_host16(n) (n) +#define host_to_be16(n) (n) +#define le_to_host32(n) bswap_32(n) +#define be_to_host32(n) (n) +#define host_to_be32(n) (n) +#ifndef WORDS_BIGENDIAN +#define WORDS_BIGENDIAN +#endif +#else +#error Could not determine CPU byte order +#endif + +#endif /* __CYGWIN__ */ + + +#ifndef ETH_ALEN +#define ETH_ALEN 6 +#endif + +#include <stdint.h> +typedef uint64_t u64; +typedef uint32_t u32; +typedef uint16_t u16; +typedef uint8_t u8; +typedef int64_t s64; +typedef int32_t s32; +typedef int16_t s16; +typedef int8_t s8; + +int hostapd_get_rand(u8 *buf, size_t len); +void hostapd_hexdump(const char *title, const u8 *buf, size_t len); +int hwaddr_aton(const char *txt, u8 *addr); +int hexstr2bin(const char *hex, u8 *buf, size_t len); +char * rel2abs_path(const char *rel_path); +void inc_byte_array(u8 *counter, size_t len); +void print_char(char c); +void fprint_char(FILE *f, char c); + + +/* Debugging function - conditional printf and hex dump. Driver wrappers can + * use these for debugging purposes. */ + +enum { MSG_MSGDUMP, MSG_DEBUG, MSG_INFO, MSG_WARNING, MSG_ERROR }; + +/** + * wpa_printf - conditional printf + * @level: priority level (MSG_*) of the message + * @fmt: printf format string, followed by optional arguments + * + * This function is used to print conditional debugging and error messages. The + * output may be directed to stdout, stderr, and/or syslog based on + * configuration. + * + * Note: New line '\n' is added to the end of the text when printing to stdout. + */ +void wpa_printf(int level, char *fmt, ...) +__attribute__ ((format (printf, 2, 3))); + +/** + * wpa_hexdump - conditional hex dump + * @level: priority level (MSG_*) of the message + * @title: title of for the message + * @buf: data buffer to be dumped + * @len: length of the @buf + * + * This function is used to print conditional debugging and error messages. The + * output may be directed to stdout, stderr, and/or syslog based on + * configuration. The contents of @buf is printed out has hex dump. + */ +void wpa_hexdump(int level, const char *title, const u8 *buf, size_t len); + +/** + * wpa_hexdump_key - conditional hex dump, hide keys + * @level: priority level (MSG_*) of the message + * @title: title of for the message + * @buf: data buffer to be dumped + * @len: length of the @buf + * + * This function is used to print conditional debugging and error messages. The + * output may be directed to stdout, stderr, and/or syslog based on + * configuration. The contents of @buf is printed out has hex dump. This works + * like wpa_hexdump(), but by default, does not include secret keys (passwords, + * etc.) in debug output. + */ +void wpa_hexdump_key(int level, const char *title, const u8 *buf, size_t len); + +/** + * wpa_hexdump_ascii - conditional hex dump + * @level: priority level (MSG_*) of the message + * @title: title of for the message + * @buf: data buffer to be dumped + * @len: length of the @buf + * + * This function is used to print conditional debugging and error messages. The + * output may be directed to stdout, stderr, and/or syslog based on + * configuration. The contents of @buf is printed out has hex dump with both + * the hex numbers and ASCII characters (for printable range) are shown. 16 + * bytes per line will be shown. + */ +void wpa_hexdump_ascii(int level, const char *title, const u8 *buf, + size_t len); + +/** + * wpa_hexdump_ascii_key - conditional hex dump, hide keys + * @level: priority level (MSG_*) of the message + * @title: title of for the message + * @buf: data buffer to be dumped + * @len: length of the @buf + * + * This function is used to print conditional debugging and error messages. The + * output may be directed to stdout, stderr, and/or syslog based on + * configuration. The contents of @buf is printed out has hex dump with both + * the hex numbers and ASCII characters (for printable range) are shown. 16 + * bytes per line will be shown. This works like wpa_hexdump_ascii(), but by + * default, does not include secret keys (passwords, etc.) in debug output. + */ +void wpa_hexdump_ascii_key(int level, const char *title, const u8 *buf, + size_t len); + +#ifdef EAPOL_TEST +#define WPA_ASSERT(a) \ + do { \ + if (!(a)) { \ + printf("WPA_ASSERT FAILED '" #a "' " \ + "%s %s:%d\n", \ + __FUNCTION__, __FILE__, __LINE__); \ + exit(1); \ + } \ + } while (0) +#else +#define WPA_ASSERT(a) do { } while (0) +#endif + +#endif /* COMMON_H */ diff --git a/contrib/wpa_supplicant/config.c b/contrib/wpa_supplicant/config.c new file mode 100644 index 000000000000..241c75565f88 --- /dev/null +++ b/contrib/wpa_supplicant/config.c @@ -0,0 +1,1051 @@ +/* + * WPA Supplicant / Configuration file parser + * Copyright (c) 2003-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include "common.h" +#include "wpa.h" +#include "config.h" +#include "sha1.h" +#include "wpa_supplicant.h" +#include "eapol_sm.h" +#include "eap.h" +#include "config.h" + + +struct parse_data { + char *name; + int (*parser)(struct parse_data *data, int line, const char *value); + void *param1, *param2, *param3, *param4; + struct wpa_ssid *ssid; + int key_data; +}; + + +static char * wpa_config_get_line(char *s, int size, FILE *stream, int *line) +{ + char *pos, *end, *sstart; + + while (fgets(s, size, stream)) { + (*line)++; + s[size - 1] = '\0'; + pos = s; + + while (*pos == ' ' || *pos == '\t' || *pos == '\r') + pos++; + if (*pos == '#' || *pos == '\n' || *pos == '\0' || + *pos == '\r') + continue; + + /* Remove # comments unless they are within a double quoted + * string. Remove trailing white space. */ + sstart = strchr(pos, '"'); + if (sstart) + sstart = strchr(sstart + 1, '"'); + if (!sstart) + sstart = pos; + end = strchr(sstart, '#'); + if (end) + *end-- = '\0'; + else + end = pos + strlen(pos) - 1; + while (end > pos && + (*end == '\n' || *end == ' ' || *end == '\t' || + *end == '\r')) { + *end-- = '\0'; + } + if (*pos == '\0') + continue; + + return pos; + } + + return NULL; +} + + +static char * wpa_config_parse_string(const char *value, size_t *len) +{ + if (*value == '"') { + char *pos; + value++; + pos = strchr(value, '"'); + if (pos == NULL || pos[1] != '\0') + return NULL; + *pos = '\0'; + *len = strlen(value); + return strdup(value); + } else { + u8 *str; + int hlen = strlen(value); + if (hlen % 1) + return NULL; + *len = hlen / 2; + str = malloc(*len); + if (str == NULL) + return NULL; + if (hexstr2bin(value, str, *len)) { + free(str); + return NULL; + } + return (char *) str; + } +} + + +static int wpa_config_parse_str(struct parse_data *data, + int line, const char *value) +{ + size_t res_len, *dst_len; + char **dst; + + dst = (char **) (((u8 *) data->ssid) + (long) data->param1); + dst_len = (size_t *) (((u8 *) data->ssid) + (long) data->param2); + + free(*dst); + *dst = wpa_config_parse_string(value, &res_len); + if (*dst == NULL) { + wpa_printf(MSG_ERROR, "Line %d: failed to parse %s '%s'.", + line, data->name, value); + return -1; + } + if (data->param2) + *dst_len = res_len; + + if (data->key_data) { + wpa_hexdump_ascii_key(MSG_MSGDUMP, data->name, + (u8 *) *dst, res_len); + } else { + wpa_hexdump_ascii(MSG_MSGDUMP, data->name, + (u8 *) *dst, res_len); + } + + if (data->param3 && res_len < (size_t) data->param3) { + wpa_printf(MSG_ERROR, "Line %d: too short %s (len=%lu " + "min_len=%ld)", line, data->name, + (unsigned long) res_len, (long) data->param3); + free(*dst); + *dst = NULL; + return -1; + } + + if (data->param4 && res_len > (size_t) data->param4) { + wpa_printf(MSG_ERROR, "Line %d: too long %s (len=%lu " + "max_len=%ld)", line, data->name, + (unsigned long) res_len, (long) data->param4); + free(*dst); + *dst = NULL; + return -1; + } + + return 0; +} + + +static int wpa_config_parse_int(struct parse_data *data, + int line, const char *value) +{ + int *dst; + + dst = (int *) (((u8 *) data->ssid) + (long) data->param1); + *dst = atoi(value); + wpa_printf(MSG_MSGDUMP, "%s=%d (0x%x)", data->name, *dst, *dst); + + if (data->param3 && *dst < (long) data->param3) { + wpa_printf(MSG_ERROR, "Line %d: too small %s (value=%d " + "min_value=%ld)", line, data->name, *dst, + (long) data->param3); + *dst = (long) data->param3; + return -1; + } + + if (data->param4 && *dst > (long) data->param4) { + wpa_printf(MSG_ERROR, "Line %d: too large %s (value=%d " + "max_value=%ld)", line, data->name, *dst, + (long) data->param4); + *dst = (long) data->param4; + return -1; + } + + return 0; +} + + +static int wpa_config_parse_bssid(struct parse_data *data, int line, + const char *value) +{ + if (hwaddr_aton(value, data->ssid->bssid)) { + wpa_printf(MSG_ERROR, "Line %d: Invalid BSSID '%s'.", + line, value); + return -1; + } + data->ssid->bssid_set = 1; + wpa_hexdump(MSG_MSGDUMP, "BSSID", data->ssid->bssid, ETH_ALEN); + return 0; +} + + +static int wpa_config_parse_psk(struct parse_data *data, int line, + const char *value) +{ + if (*value == '"') { + char *pos; + int len; + + value++; + pos = strrchr(value, '"'); + if (pos) + *pos = '\0'; + len = strlen(value); + if (len < 8 || len > 63) { + wpa_printf(MSG_ERROR, "Line %d: Invalid passphrase " + "length %d (expected: 8..63) '%s'.", + line, len, value); + return -1; + } + wpa_hexdump_ascii_key(MSG_MSGDUMP, "PSK (ASCII passphrase)", + (u8 *) value, len); + data->ssid->passphrase = strdup(value); + return data->ssid->passphrase == NULL ? -1 : 0; + } + + if (hexstr2bin(value, data->ssid->psk, PMK_LEN) || + value[PMK_LEN * 2] != '\0') { + wpa_printf(MSG_ERROR, "Line %d: Invalid PSK '%s'.", + line, value); + return -1; + } + data->ssid->psk_set = 1; + wpa_hexdump_key(MSG_MSGDUMP, "PSK", data->ssid->psk, PMK_LEN); + return 0; +} + + +static int wpa_config_parse_proto(struct parse_data *data, int line, + const char *value) +{ + int val = 0, last, errors = 0; + char *start, *end, *buf; + + buf = strdup(value); + if (buf == NULL) + return -1; + start = buf; + + while (start != '\0') { + while (*start == ' ' || *start == '\t') + start++; + if (*start == '\0') + break; + end = start; + while (*end != ' ' && *end != '\t' && *end != '\0') + end++; + last = *end == '\0'; + *end = '\0'; + if (strcmp(start, "WPA") == 0) + val |= WPA_PROTO_WPA; + else if (strcmp(start, "RSN") == 0 || + strcmp(start, "WPA2") == 0) + val |= WPA_PROTO_RSN; + else { + wpa_printf(MSG_ERROR, "Line %d: invalid proto '%s'", + line, start); + errors++; + } + + if (last) + break; + start = end + 1; + } + free(buf); + + if (val == 0) { + wpa_printf(MSG_ERROR, + "Line %d: no proto values configured.", line); + errors++; + } + + wpa_printf(MSG_MSGDUMP, "proto: 0x%x", val); + data->ssid->proto = val; + return errors ? -1 : 0; +} + + +static int wpa_config_parse_key_mgmt(struct parse_data *data, int line, + const char *value) +{ + int val = 0, last, errors = 0; + char *start, *end, *buf; + + buf = strdup(value); + if (buf == NULL) + return -1; + start = buf; + + while (start != '\0') { + while (*start == ' ' || *start == '\t') + start++; + if (*start == '\0') + break; + end = start; + while (*end != ' ' && *end != '\t' && *end != '\0') + end++; + last = *end == '\0'; + *end = '\0'; + if (strcmp(start, "WPA-PSK") == 0) + val |= WPA_KEY_MGMT_PSK; + else if (strcmp(start, "WPA-EAP") == 0) + val |= WPA_KEY_MGMT_IEEE8021X; + else if (strcmp(start, "IEEE8021X") == 0) + val |= WPA_KEY_MGMT_IEEE8021X_NO_WPA; + else if (strcmp(start, "NONE") == 0) + val |= WPA_KEY_MGMT_NONE; + else if (strcmp(start, "WPA-NONE") == 0) + val |= WPA_KEY_MGMT_WPA_NONE; + else { + wpa_printf(MSG_ERROR, "Line %d: invalid key_mgmt '%s'", + line, start); + errors++; + } + + if (last) + break; + start = end + 1; + } + free(buf); + + if (val == 0) { + wpa_printf(MSG_ERROR, + "Line %d: no key_mgmt values configured.", line); + errors++; + } + + wpa_printf(MSG_MSGDUMP, "key_mgmt: 0x%x", val); + data->ssid->key_mgmt = val; + return errors ? -1 : 0; +} + + +static int wpa_config_parse_cipher(int line, const char *value) +{ + int val = 0, last; + char *start, *end, *buf; + + buf = strdup(value); + if (buf == NULL) + return -1; + start = buf; + + while (start != '\0') { + while (*start == ' ' || *start == '\t') + start++; + if (*start == '\0') + break; + end = start; + while (*end != ' ' && *end != '\t' && *end != '\0') + end++; + last = *end == '\0'; + *end = '\0'; + if (strcmp(start, "CCMP") == 0) + val |= WPA_CIPHER_CCMP; + else if (strcmp(start, "TKIP") == 0) + val |= WPA_CIPHER_TKIP; + else if (strcmp(start, "WEP104") == 0) + val |= WPA_CIPHER_WEP104; + else if (strcmp(start, "WEP40") == 0) + val |= WPA_CIPHER_WEP40; + else if (strcmp(start, "NONE") == 0) + val |= WPA_CIPHER_NONE; + else { + wpa_printf(MSG_ERROR, "Line %d: invalid cipher '%s'.", + line, start); + free(buf); + return -1; + } + + if (last) + break; + start = end + 1; + } + free(buf); + + if (val == 0) { + wpa_printf(MSG_ERROR, "Line %d: no cipher values configured.", + line); + return -1; + } + return val; +} + + +static int wpa_config_parse_pairwise(struct parse_data *data, int line, + const char *value) +{ + int val; + val = wpa_config_parse_cipher(line, value); + if (val == -1) + return -1; + if (val & ~(WPA_CIPHER_CCMP | WPA_CIPHER_TKIP | WPA_CIPHER_NONE)) { + wpa_printf(MSG_ERROR, "Line %d: not allowed pairwise cipher " + "(0x%x).", line, val); + return -1; + } + + wpa_printf(MSG_MSGDUMP, "pairwise: 0x%x", val); + data->ssid->pairwise_cipher = val; + return 0; +} + + +static int wpa_config_parse_group(struct parse_data *data, int line, + const char *value) +{ + int val; + val = wpa_config_parse_cipher(line, value); + if (val == -1) + return -1; + if (val & ~(WPA_CIPHER_CCMP | WPA_CIPHER_TKIP | WPA_CIPHER_WEP104 | + WPA_CIPHER_WEP40)) { + wpa_printf(MSG_ERROR, "Line %d: not allowed group cipher " + "(0x%x).", line, val); + return -1; + } + + wpa_printf(MSG_MSGDUMP, "group: 0x%x", val); + data->ssid->group_cipher = val; + return 0; +} + + +static int wpa_config_parse_auth_alg(struct parse_data *data, int line, + const char *value) +{ + int val = 0, last, errors = 0; + char *start, *end, *buf; + + buf = strdup(value); + if (buf == NULL) + return -1; + start = buf; + + while (start != '\0') { + while (*start == ' ' || *start == '\t') + start++; + if (*start == '\0') + break; + end = start; + while (*end != ' ' && *end != '\t' && *end != '\0') + end++; + last = *end == '\0'; + *end = '\0'; + if (strcmp(start, "OPEN") == 0) + val |= WPA_AUTH_ALG_OPEN; + else if (strcmp(start, "SHARED") == 0) + val |= WPA_AUTH_ALG_SHARED; + else if (strcmp(start, "LEAP") == 0) + val |= WPA_AUTH_ALG_LEAP; + else { + wpa_printf(MSG_ERROR, "Line %d: invalid auth_alg '%s'", + line, start); + errors++; + } + + if (last) + break; + start = end + 1; + } + free(buf); + + if (val == 0) { + wpa_printf(MSG_ERROR, + "Line %d: no auth_alg values configured.", line); + errors++; + } + + wpa_printf(MSG_MSGDUMP, "auth_alg: 0x%x", val); + data->ssid->auth_alg = val; + return errors ? -1 : 0; +} + + +static int wpa_config_parse_eap(struct parse_data *data, int line, + const char *value) +{ + int last, errors = 0; + char *start, *end, *buf; + u8 *methods = NULL, *tmp; + size_t num_methods = 0; + + buf = strdup(value); + if (buf == NULL) + return -1; + start = buf; + + while (start != '\0') { + while (*start == ' ' || *start == '\t') + start++; + if (*start == '\0') + break; + end = start; + while (*end != ' ' && *end != '\t' && *end != '\0') + end++; + last = *end == '\0'; + *end = '\0'; + tmp = methods; + methods = realloc(methods, num_methods + 1); + if (methods == NULL) { + free(tmp); + return -1; + } + methods[num_methods] = eap_get_type(start); + if (methods[num_methods] == EAP_TYPE_NONE) { + wpa_printf(MSG_ERROR, "Line %d: unknown EAP method " + "'%s'", line, start); + wpa_printf(MSG_ERROR, "You may need to add support for" + " this EAP method during wpa_supplicant\n" + "build time configuration.\n" + "See README for more information."); + errors++; + } else if (methods[num_methods] == EAP_TYPE_LEAP) + data->ssid->leap++; + else + data->ssid->non_leap++; + num_methods++; + if (last) + break; + start = end + 1; + } + free(buf); + + tmp = methods; + methods = realloc(methods, num_methods + 1); + if (methods == NULL) { + free(tmp); + return -1; + } + methods[num_methods] = EAP_TYPE_NONE; + num_methods++; + + wpa_hexdump(MSG_MSGDUMP, "eap methods", methods, num_methods); + data->ssid->eap_methods = methods; + return errors ? -1 : 0; +} + + +static int wpa_config_parse_wep_key(u8 *key, size_t *len, int line, + const char *value, int idx) +{ + char *buf, title[20]; + + buf = wpa_config_parse_string(value, len); + if (buf == NULL) { + wpa_printf(MSG_ERROR, "Line %d: Invalid WEP key %d '%s'.", + line, idx, value); + return -1; + } + if (*len > MAX_WEP_KEY_LEN) { + wpa_printf(MSG_ERROR, "Line %d: Too long WEP key %d '%s'.", + line, idx, value); + free(buf); + return -1; + } + memcpy(key, buf, *len); + free(buf); + snprintf(title, sizeof(title), "wep_key%d", idx); + wpa_hexdump_key(MSG_MSGDUMP, title, key, *len); + return 0; +} + + +static int wpa_config_parse_wep_key0(struct parse_data *data, int line, + const char *value) +{ + return wpa_config_parse_wep_key(data->ssid->wep_key[0], + &data->ssid->wep_key_len[0], line, + value, 0); +} + + +static int wpa_config_parse_wep_key1(struct parse_data *data, int line, + const char *value) +{ + return wpa_config_parse_wep_key(data->ssid->wep_key[1], + &data->ssid->wep_key_len[1], line, + value, 1); +} + + +static int wpa_config_parse_wep_key2(struct parse_data *data, int line, + const char *value) +{ + return wpa_config_parse_wep_key(data->ssid->wep_key[2], + &data->ssid->wep_key_len[2], line, + value, 2); +} + + +static int wpa_config_parse_wep_key3(struct parse_data *data, int line, + const char *value) +{ + return wpa_config_parse_wep_key(data->ssid->wep_key[3], + &data->ssid->wep_key_len[3], line, + value, 3); +} + + +#define OFFSET(v) ((void *) &((struct wpa_ssid *) 0)->v) +#define STR(f) .name = #f, .parser = wpa_config_parse_str, .param1 = OFFSET(f) +#define STR_LEN(f) STR(f), .param2 = OFFSET(f ## _len) +#define STR_RANGE(f, min, max) STR_LEN(f), .param3 = (void *) (min), \ + .param4 = (void *) (max) +#define INT(f) .name = #f, .parser = wpa_config_parse_int, \ + .param1 = OFFSET(f), .param2 = (void *) 0 +#define INT_RANGE(f, min, max) INT(f), .param3 = (void *) (min), \ + .param4 = (void *) (max) +#define FUNC(f) .name = #f, .parser = wpa_config_parse_ ## f + +static struct parse_data ssid_fields[] = { + { STR_RANGE(ssid, 0, MAX_SSID_LEN) }, + { INT_RANGE(scan_ssid, 0, 1) }, + { FUNC(bssid) }, + { FUNC(psk), .key_data = 1 }, + { FUNC(proto) }, + { FUNC(key_mgmt) }, + { FUNC(pairwise) }, + { FUNC(group) }, + { FUNC(auth_alg) }, + { FUNC(eap) }, + { STR_LEN(identity) }, + { STR_LEN(anonymous_identity) }, + { STR_RANGE(eappsk, EAP_PSK_LEN, EAP_PSK_LEN), .key_data = 1 }, + { STR_LEN(nai) }, + { STR_LEN(server_nai) }, + { STR_LEN(password), .key_data = 1 }, + { STR(ca_cert) }, + { STR(client_cert) }, + { STR(private_key) }, + { STR(private_key_passwd), .key_data = 1 }, + { STR(dh_file) }, + { STR(subject_match) }, + { STR(ca_cert2) }, + { STR(client_cert2) }, + { STR(private_key2) }, + { STR(private_key2_passwd), .key_data = 1 }, + { STR(dh_file2) }, + { STR(subject_match2) }, + { STR(phase1) }, + { STR(phase2) }, + { STR(pcsc) }, + { STR(pin), .key_data = 1 }, + { INT(eapol_flags) }, + { FUNC(wep_key0), .key_data = 1 }, + { FUNC(wep_key1), .key_data = 1 }, + { FUNC(wep_key2), .key_data = 1 }, + { FUNC(wep_key3), .key_data = 1 }, + { INT(wep_tx_keyidx) }, + { INT(priority) }, + { INT(eap_workaround) }, + { STR(pac_file) }, + { INT_RANGE(mode, 0, 1) }, +}; + +#undef OFFSET +#undef STR +#undef STR_LEN +#undef STR_RANGE +#undef INT +#undef INT_RANGE +#undef FUNC +#define NUM_SSID_FIELDS (sizeof(ssid_fields) / sizeof(ssid_fields[0])) + + +static struct wpa_ssid * wpa_config_read_network(FILE *f, int *line, int id) +{ + struct wpa_ssid *ssid; + int errors = 0, i, end = 0; + char buf[256], *pos, *pos2; + + wpa_printf(MSG_MSGDUMP, "Line: %d - start of a new network block", + *line); + ssid = (struct wpa_ssid *) malloc(sizeof(*ssid)); + if (ssid == NULL) + return NULL; + memset(ssid, 0, sizeof(*ssid)); + ssid->id = id; + + ssid->proto = WPA_PROTO_WPA | WPA_PROTO_RSN; + ssid->pairwise_cipher = WPA_CIPHER_CCMP | WPA_CIPHER_TKIP; + ssid->group_cipher = WPA_CIPHER_CCMP | WPA_CIPHER_TKIP | + WPA_CIPHER_WEP104 | WPA_CIPHER_WEP40; + ssid->key_mgmt = WPA_KEY_MGMT_PSK | WPA_KEY_MGMT_IEEE8021X; + ssid->eapol_flags = EAPOL_FLAG_REQUIRE_KEY_UNICAST | + EAPOL_FLAG_REQUIRE_KEY_BROADCAST; + ssid->eap_workaround = (unsigned int) -1; + + while ((pos = wpa_config_get_line(buf, sizeof(buf), f, line))) { + if (strcmp(pos, "}") == 0) { + end = 1; + break; + } + + pos2 = strchr(pos, '='); + if (pos2 == NULL) { + wpa_printf(MSG_ERROR, "Line %d: Invalid SSID line " + "'%s'.", *line, pos); + errors++; + continue; + } + + *pos2++ = '\0'; + if (*pos2 == '"') { + if (strchr(pos2 + 1, '"') == NULL) { + wpa_printf(MSG_ERROR, "Line %d: invalid " + "quotation '%s'.", *line, pos2); + errors++; + continue; + } + } + + for (i = 0; i < NUM_SSID_FIELDS; i++) { + struct parse_data *field = &ssid_fields[i]; + if (strcmp(pos, field->name) != 0) + continue; + + field->ssid = ssid; + if (field->parser(field, *line, pos2)) { + wpa_printf(MSG_ERROR, "Line %d: failed to " + "parse %s '%s'.", *line, pos, pos2); + errors++; + } + break; + } + if (i == NUM_SSID_FIELDS) { + wpa_printf(MSG_ERROR, "Line %d: unknown network field " + "'%s'.", *line, pos); + errors++; + } + } + + if (!end) { + wpa_printf(MSG_ERROR, "Line %d: network block was not " + "terminated properly.", *line); + errors++; + } + + if (ssid->passphrase) { + if (ssid->psk_set) { + wpa_printf(MSG_ERROR, "Line %d: both PSK and " + "passphrase configured.", *line); + errors++; + } + pbkdf2_sha1(ssid->passphrase, + (char *) ssid->ssid, ssid->ssid_len, 4096, + ssid->psk, PMK_LEN); + wpa_hexdump_key(MSG_MSGDUMP, "PSK (from passphrase)", + ssid->psk, PMK_LEN); + ssid->psk_set = 1; + } + + if ((ssid->key_mgmt & WPA_KEY_MGMT_PSK) && !ssid->psk_set) { + wpa_printf(MSG_ERROR, "Line %d: WPA-PSK accepted for key " + "management, but no PSK configured.", *line); + errors++; + } + + if ((ssid->group_cipher & WPA_CIPHER_CCMP) && + !(ssid->pairwise_cipher & WPA_CIPHER_CCMP)) { + /* Group cipher cannot be stronger than the pairwise cipher. */ + wpa_printf(MSG_DEBUG, "Line %d: removed CCMP from group cipher" + " list since it was not allowed for pairwise " + "cipher", *line); + ssid->group_cipher &= ~WPA_CIPHER_CCMP; + } + + if (errors) { + free(ssid); + ssid = NULL; + } + + return ssid; +} + + +static int wpa_config_add_prio_network(struct wpa_config *config, + struct wpa_ssid *ssid) +{ + int prio; + struct wpa_ssid *prev, **nlist; + + for (prio = 0; prio < config->num_prio; prio++) { + prev = config->pssid[prio]; + if (prev->priority == ssid->priority) { + while (prev->pnext) + prev = prev->pnext; + prev->pnext = ssid; + return 0; + } + } + + /* First network for this priority - add new priority list */ + nlist = realloc(config->pssid, + (config->num_prio + 1) * sizeof(struct wpa_ssid *)); + if (nlist == NULL) + return -1; + + for (prio = 0; prio < config->num_prio; prio++) { + if (nlist[prio]->priority < ssid->priority) + break; + } + + memmove(&nlist[prio + 1], &nlist[prio], + (config->num_prio - prio) * sizeof(struct wpa_ssid *)); + + nlist[prio] = ssid; + config->num_prio++; + config->pssid = nlist; + + return 0; +} + + +struct wpa_config * wpa_config_read(const char *config_file) +{ + FILE *f; + char buf[256], *pos; + int errors = 0, line = 0; + struct wpa_ssid *ssid, *tail = NULL, *head = NULL; + struct wpa_config *config; + int id = 0, prio; + + config = malloc(sizeof(*config)); + if (config == NULL) + return NULL; + memset(config, 0, sizeof(*config)); + config->eapol_version = 1; + config->ap_scan = 1; + config->fast_reauth = 1; + wpa_printf(MSG_DEBUG, "Reading configuration file '%s'", + config_file); + f = fopen(config_file, "r"); + if (f == NULL) { + free(config); + return NULL; + } + + while ((pos = wpa_config_get_line(buf, sizeof(buf), f, &line))) { + if (strcmp(pos, "network={") == 0) { + ssid = wpa_config_read_network(f, &line, id++); + if (ssid == NULL) { + wpa_printf(MSG_ERROR, "Line %d: failed to " + "parse network block.", line); + errors++; + continue; + } + if (head == NULL) { + head = tail = ssid; + } else { + tail->next = ssid; + tail = ssid; + } + if (wpa_config_add_prio_network(config, ssid)) { + wpa_printf(MSG_ERROR, "Line %d: failed to add " + "network block to priority list.", + line); + errors++; + continue; + } +#ifdef CONFIG_CTRL_IFACE + } else if (strncmp(pos, "ctrl_interface=", 15) == 0) { + free(config->ctrl_interface); + config->ctrl_interface = strdup(pos + 15); + wpa_printf(MSG_DEBUG, "ctrl_interface='%s'", + config->ctrl_interface); +#ifndef CONFIG_CTRL_IFACE_UDP + } else if (strncmp(pos, "ctrl_interface_group=", 21) == 0) { + struct group *grp; + char *endp; + const char *group = pos + 21; + + grp = getgrnam(group); + if (grp) { + config->ctrl_interface_gid = grp->gr_gid; + config->ctrl_interface_gid_set = 1; + wpa_printf(MSG_DEBUG, "ctrl_interface_group=%d" + " (from group name '%s')", + (int) config->ctrl_interface_gid, + group); + continue; + } + + /* Group name not found - try to parse this as gid */ + config->ctrl_interface_gid = strtol(group, &endp, 10); + if (*group == '\0' || *endp != '\0') { + wpa_printf(MSG_DEBUG, "Line %d: Invalid group " + "'%s'", line, group); + errors++; + continue; + } + wpa_printf(MSG_DEBUG, "ctrl_interface_group=%d", + (int) config->ctrl_interface_gid); +#endif /* CONFIG_CTRL_IFACE_UDP */ +#endif /* CONFIG_CTRL_IFACE */ + } else if (strncmp(pos, "eapol_version=", 14) == 0) { + config->eapol_version = atoi(pos + 14); + if (config->eapol_version < 1 || + config->eapol_version > 2) { + wpa_printf(MSG_ERROR, "Line %d: Invalid EAPOL " + "version (%d): '%s'.", + line, config->eapol_version, pos); + errors++; + continue; + } + wpa_printf(MSG_DEBUG, "eapol_version=%d", + config->eapol_version); + } else if (strncmp(pos, "ap_scan=", 8) == 0) { + config->ap_scan = atoi(pos + 8); + wpa_printf(MSG_DEBUG, "ap_scan=%d", config->ap_scan); + } else if (strncmp(pos, "fast_reauth=", 12) == 0) { + config->fast_reauth = atoi(pos + 12); + wpa_printf(MSG_DEBUG, "fast_reauth=%d", + config->fast_reauth); + } else { + wpa_printf(MSG_ERROR, "Line %d: Invalid configuration " + "line '%s'.", line, pos); + errors++; + continue; + } + } + + fclose(f); + + config->ssid = head; + for (prio = 0; prio < config->num_prio; prio++) { + ssid = config->pssid[prio]; + wpa_printf(MSG_DEBUG, "Priority group %d", + ssid->priority); + while (ssid) { + wpa_printf(MSG_DEBUG, " id=%d ssid='%s'", + ssid->id, + wpa_ssid_txt(ssid->ssid, ssid->ssid_len)); + ssid = ssid->pnext; + } + } + if (errors) { + wpa_config_free(config); + config = NULL; + head = NULL; + } + + return config; +} + + +void wpa_config_free(struct wpa_config *config) +{ + struct wpa_ssid *ssid, *prev = NULL; + ssid = config->ssid; + while (ssid) { + prev = ssid; + ssid = ssid->next; + free(prev->ssid); + free(prev->passphrase); + free(prev->eap_methods); + free(prev->identity); + free(prev->anonymous_identity); + free(prev->eappsk); + free(prev->nai); + free(prev->server_nai); + free(prev->password); + free(prev->ca_cert); + free(prev->client_cert); + free(prev->private_key); + free(prev->private_key_passwd); + free(prev->dh_file); + free(prev->subject_match); + free(prev->ca_cert2); + free(prev->client_cert2); + free(prev->private_key2); + free(prev->private_key2_passwd); + free(prev->dh_file2); + free(prev->subject_match2); + free(prev->phase1); + free(prev->phase2); + free(prev->pcsc); + free(prev->pin); + free(prev->otp); + free(prev->pending_req_otp); + free(prev->pac_file); + free(prev); + } + free(config->ctrl_interface); + free(config->pssid); + free(config); +} + + +int wpa_config_allowed_eap_method(struct wpa_ssid *ssid, int method) +{ + u8 *pos; + + if (ssid == NULL || ssid->eap_methods == NULL) + return 1; + + pos = ssid->eap_methods; + while (*pos != EAP_TYPE_NONE) { + if (*pos == method) + return 1; + pos++; + } + return 0; +} + + +const char * wpa_cipher_txt(int cipher) +{ + switch (cipher) { + case WPA_CIPHER_NONE: + return "NONE"; + case WPA_CIPHER_WEP40: + return "WEP-40"; + case WPA_CIPHER_WEP104: + return "WEP-104"; + case WPA_CIPHER_TKIP: + return "TKIP"; + case WPA_CIPHER_CCMP: + return "CCMP"; + default: + return "UNKNOWN"; + } +} + + +const char * wpa_key_mgmt_txt(int key_mgmt, int proto) +{ + switch (key_mgmt) { + case WPA_KEY_MGMT_IEEE8021X: + return proto == WPA_PROTO_RSN ? + "WPA2/IEEE 802.1X/EAP" : "WPA/IEEE 802.1X/EAP"; + case WPA_KEY_MGMT_PSK: + return proto == WPA_PROTO_RSN ? + "WPA2-PSK" : "WPA-PSK"; + case WPA_KEY_MGMT_NONE: + return "NONE"; + case WPA_KEY_MGMT_IEEE8021X_NO_WPA: + return "IEEE 802.1X (no WPA)"; + default: + return "UNKNOWN"; + } +} diff --git a/contrib/wpa_supplicant/config.h b/contrib/wpa_supplicant/config.h new file mode 100644 index 000000000000..13deb3e3cb58 --- /dev/null +++ b/contrib/wpa_supplicant/config.h @@ -0,0 +1,33 @@ +#ifndef CONFIG_H +#define CONFIG_H + +#ifdef CONFIG_CTRL_IFACE +#ifndef CONFIG_CTRL_IFACE_UDP +#include <grp.h> +#endif /* CONFIG_CTRL_IFACE_UDP */ +#endif /* CONFIG_CTRL_IFACE */ + +#include "config_ssid.h" + +struct wpa_config { + struct wpa_ssid *ssid; /* global network list */ + struct wpa_ssid **pssid; /* per priority network lists (in priority + * order) */ + int num_prio; /* number of different priorities */ + int eapol_version; + int ap_scan; + char *ctrl_interface; /* directory for UNIX domain sockets */ +#ifdef CONFIG_CTRL_IFACE +#ifndef CONFIG_CTRL_IFACE_UDP + gid_t ctrl_interface_gid; +#endif /* CONFIG_CTRL_IFACE_UDP */ + int ctrl_interface_gid_set; +#endif /* CONFIG_CTRL_IFACE */ + int fast_reauth; +}; + + +struct wpa_config * wpa_config_read(const char *config_file); +void wpa_config_free(struct wpa_config *ssid); + +#endif /* CONFIG_H */ diff --git a/contrib/wpa_supplicant/config_ssid.h b/contrib/wpa_supplicant/config_ssid.h new file mode 100644 index 000000000000..44bc98947ced --- /dev/null +++ b/contrib/wpa_supplicant/config_ssid.h @@ -0,0 +1,109 @@ +#ifndef CONFIG_SSID_H +#define CONFIG_SSID_H + +#define WPA_CIPHER_NONE BIT(0) +#define WPA_CIPHER_WEP40 BIT(1) +#define WPA_CIPHER_WEP104 BIT(2) +#define WPA_CIPHER_TKIP BIT(3) +#define WPA_CIPHER_CCMP BIT(4) + +#define WPA_KEY_MGMT_IEEE8021X BIT(0) +#define WPA_KEY_MGMT_PSK BIT(1) +#define WPA_KEY_MGMT_NONE BIT(2) +#define WPA_KEY_MGMT_IEEE8021X_NO_WPA BIT(3) +#define WPA_KEY_MGMT_WPA_NONE BIT(4) + +#define WPA_PROTO_WPA BIT(0) +#define WPA_PROTO_RSN BIT(1) + +#define WPA_AUTH_ALG_OPEN BIT(0) +#define WPA_AUTH_ALG_SHARED BIT(1) +#define WPA_AUTH_ALG_LEAP BIT(2) + +#define MAX_SSID_LEN 32 +#define PMK_LEN 32 +#define EAP_PSK_LEN 16 + +struct wpa_ssid { + struct wpa_ssid *next; /* next network in global list */ + struct wpa_ssid *pnext; /* next network in per-priority list */ + int id; /* unique id for ctrl_iface */ + int priority; + u8 *ssid; + size_t ssid_len; + u8 bssid[ETH_ALEN]; + int bssid_set; + u8 psk[PMK_LEN]; + int psk_set; + char *passphrase; + /* Bitfields of allowed Pairwise/Group Ciphers, WPA_CIPHER_* */ + int pairwise_cipher; + int group_cipher; + int key_mgmt; + int proto; /* Bitfield of allowed protocols (WPA_PROTO_*) */ + int auth_alg; /* Bitfield of allow authentication algorithms + * (WPA_AUTH_ALG_*) */ + int scan_ssid; /* scan this SSID with Probe Requests */ + u8 *identity; /* EAP Identity */ + size_t identity_len; + u8 *anonymous_identity; /* Anonymous EAP Identity (for unencrypted use + * with EAP types that support different + * tunnelled identity, e.g., EAP-TTLS) */ + size_t anonymous_identity_len; + u8 *eappsk; + size_t eappsk_len; + u8 *nai; + size_t nai_len; + u8 *server_nai; + size_t server_nai_len; + u8 *password; + size_t password_len; + u8 *ca_cert; + u8 *client_cert; + u8 *private_key; + u8 *private_key_passwd; + u8 *dh_file; + u8 *subject_match; + u8 *ca_cert2; + u8 *client_cert2; + u8 *private_key2; + u8 *private_key2_passwd; + u8 *dh_file2; + u8 *subject_match2; + u8 *eap_methods; /* zero (EAP_TYPE_NONE) terminated list of allowed + * EAP methods or NULL = any */ + char *phase1; + char *phase2; + char *pcsc; + char *pin; + +#define EAPOL_FLAG_REQUIRE_KEY_UNICAST BIT(0) +#define EAPOL_FLAG_REQUIRE_KEY_BROADCAST BIT(1) + int eapol_flags; /* bit field of IEEE 802.1X/EAPOL options */ + +#define NUM_WEP_KEYS 4 +#define MAX_WEP_KEY_LEN 16 + u8 wep_key[NUM_WEP_KEYS][MAX_WEP_KEY_LEN]; + size_t wep_key_len[NUM_WEP_KEYS]; + int wep_tx_keyidx; + + /* Per SSID variables that are not read from the configuration file */ + u8 *otp; + size_t otp_len; + int pending_req_identity, pending_req_password; + char *pending_req_otp; + size_t pending_req_otp_len; + int leap, non_leap; + + unsigned int eap_workaround; + + char *pac_file; + + int mode; +}; + +int wpa_config_allowed_eap_method(struct wpa_ssid *ssid, int method); +const char * wpa_cipher_txt(int cipher); +const char * wpa_key_mgmt_txt(int key_mgmt, int proto); + +#endif /* CONFIG_SSID_H */ diff --git a/contrib/wpa_supplicant/crypto.c b/contrib/wpa_supplicant/crypto.c new file mode 100644 index 000000000000..cd278e0fae59 --- /dev/null +++ b/contrib/wpa_supplicant/crypto.c @@ -0,0 +1,71 @@ +/* + * WPA Supplicant / wrapper functions for libcrypto + * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include <openssl/md4.h> +#include <openssl/des.h> + +#include "common.h" + + +#if OPENSSL_VERSION_NUMBER < 0x00907000 +#define DES_key_schedule des_key_schedule +#define DES_cblock des_cblock +#define DES_set_key(key, schedule) des_set_key((key), *(schedule)) +#define DES_ecb_encrypt(input, output, ks, enc) \ + des_ecb_encrypt((input), (output), *(ks), (enc)) +#endif /* openssl < 0.9.7 */ + + +void md4_vector(size_t num_elem, const u8 *addr[], size_t *len, u8 *mac) +{ + MD4_CTX ctx; + int i; + + MD4_Init(&ctx); + for (i = 0; i < num_elem; i++) + MD4_Update(&ctx, addr[i], len[i]); + MD4_Final(mac, &ctx); +} + + +void md4(const u8 *addr, size_t len, u8 *mac) +{ + md4_vector(1, &addr, &len, mac); +} + + +/** + * @clear: 8 octets (in) + * @key: 7 octets (in) (no parity bits included) + * @cypher: 8 octets (out) + */ +void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher) +{ + u8 pkey[8], next, tmp; + int i; + DES_key_schedule ks; + + /* Add parity bits to the key */ + next = 0; + for (i = 0; i < 7; i++) { + tmp = key[i]; + pkey[i] = (tmp >> i) | next | 1; + next = tmp << (7 - i); + } + pkey[i] = next | 1; + + DES_set_key(&pkey, &ks); + DES_ecb_encrypt((DES_cblock *) clear, (DES_cblock *) cypher, &ks, + DES_ENCRYPT); +} diff --git a/contrib/wpa_supplicant/crypto.h b/contrib/wpa_supplicant/crypto.h new file mode 100644 index 000000000000..3e1a0e5cd3f8 --- /dev/null +++ b/contrib/wpa_supplicant/crypto.h @@ -0,0 +1,8 @@ +#ifndef CRYPTO_H +#define CRYPTO_H + +void md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac); +void md4(const u8 *addr, size_t len, u8 *mac); +void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher); + +#endif /* CRYPTO_H */ diff --git a/contrib/wpa_supplicant/ctrl_iface.c b/contrib/wpa_supplicant/ctrl_iface.c new file mode 100644 index 000000000000..e41881104ede --- /dev/null +++ b/contrib/wpa_supplicant/ctrl_iface.c @@ -0,0 +1,728 @@ +/* + * WPA Supplicant / UNIX domain socket -based control interface + * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <errno.h> +#ifndef CONFIG_NATIVE_WINDOWS +#include <sys/socket.h> +#include <sys/un.h> +#include <sys/uio.h> +#endif /* CONFIG_NATIVE_WINDOWS */ + +#include "common.h" +#include "eloop.h" +#include "wpa.h" +#include "wpa_supplicant.h" +#include "config.h" +#include "eapol_sm.h" +#include "wpa_supplicant_i.h" +#include "ctrl_iface.h" +#include "l2_packet.h" + + +#ifdef CONFIG_NATIVE_WINDOWS +typedef int socklen_t; +#endif /* CONFIG_NATIVE_WINDOWS */ + +#ifdef CONFIG_CTRL_IFACE_UDP +#define CTRL_IFACE_SOCK struct sockaddr_in +#else /* CONFIG_CTRL_IFACE_UDP */ +#define CTRL_IFACE_SOCK struct sockaddr_un +#endif /* CONFIG_CTRL_IFACE_UDP */ + + +struct wpa_ctrl_dst { + struct wpa_ctrl_dst *next; + CTRL_IFACE_SOCK addr; + socklen_t addrlen; + int debug_level; + int errors; +}; + + +static const char * wpa_state_txt(int state) +{ + switch (state) { + case WPA_DISCONNECTED: + return "DISCONNECTED"; + case WPA_SCANNING: + return "SCANNING"; + case WPA_ASSOCIATING: + return "ASSOCIATING"; + case WPA_ASSOCIATED: + return "ASSOCIATED"; + case WPA_4WAY_HANDSHAKE: + return "4WAY_HANDSHAKE"; + case WPA_GROUP_HANDSHAKE: + return "GROUP_HANDSHAKE"; + case WPA_COMPLETED: + return "COMPLETED"; + default: + return "UNKNOWN"; + } +} + + +static int wpa_supplicant_ctrl_iface_set(struct wpa_supplicant *wpa_s, + char *cmd) +{ + char *value; + + value = strchr(cmd, ' '); + if (value == NULL) + return -1; + *value++ = '\0'; + + wpa_printf(MSG_DEBUG, "CTRL_IFACE SET '%s'='%s'", cmd, value); + if (strcasecmp(cmd, "EAPOL::heldPeriod") == 0) { + eapol_sm_configure(wpa_s->eapol, + atoi(value), -1, -1, -1); + } else if (strcasecmp(cmd, "EAPOL::authPeriod") == 0) { + eapol_sm_configure(wpa_s->eapol, + -1, atoi(value), -1, -1); + } else if (strcasecmp(cmd, "EAPOL::startPeriod") == 0) { + eapol_sm_configure(wpa_s->eapol, + -1, -1, atoi(value), -1); + } else if (strcasecmp(cmd, "EAPOL::maxStart") == 0) { + eapol_sm_configure(wpa_s->eapol, + -1, -1, -1, atoi(value)); + } else + return -1; + return 0; +} + + +static int wpa_supplicant_ctrl_iface_preauth(struct wpa_supplicant *wpa_s, + char *addr) +{ + u8 bssid[ETH_ALEN]; + + if (hwaddr_aton(addr, bssid)) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE PREAUTH: invalid address " + "'%s'", addr); + return -1; + } + + wpa_printf(MSG_DEBUG, "CTRL_IFACE PREAUTH " MACSTR, MAC2STR(bssid)); + rsn_preauth_deinit(wpa_s); + if (rsn_preauth_init(wpa_s, bssid)) + return -1; + + return 0; +} + + +static int wpa_supplicant_ctrl_iface_attach(struct wpa_supplicant *wpa_s, + CTRL_IFACE_SOCK *from, + socklen_t fromlen) +{ + struct wpa_ctrl_dst *dst; + + dst = malloc(sizeof(*dst)); + if (dst == NULL) + return -1; + memset(dst, 0, sizeof(*dst)); + memcpy(&dst->addr, from, sizeof(CTRL_IFACE_SOCK)); + dst->addrlen = fromlen; + dst->debug_level = MSG_INFO; + dst->next = wpa_s->ctrl_dst; + wpa_s->ctrl_dst = dst; +#ifdef CONFIG_CTRL_IFACE_UDP + wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor attached %s:%d", + inet_ntoa(from->sin_addr), ntohs(from->sin_port)); +#else /* CONFIG_CTRL_IFACE_UDP */ + wpa_hexdump(MSG_DEBUG, "CTRL_IFACE monitor attached", + (u8 *) from->sun_path, fromlen); +#endif /* CONFIG_CTRL_IFACE_UDP */ + return 0; +} + + +static int wpa_supplicant_ctrl_iface_detach(struct wpa_supplicant *wpa_s, + CTRL_IFACE_SOCK *from, + socklen_t fromlen) +{ + struct wpa_ctrl_dst *dst, *prev = NULL; + + dst = wpa_s->ctrl_dst; + while (dst) { +#ifdef CONFIG_CTRL_IFACE_UDP + if (from->sin_addr.s_addr == dst->addr.sin_addr.s_addr && + from->sin_port == dst->addr.sin_port) { + if (prev == NULL) + wpa_s->ctrl_dst = dst->next; + else + prev->next = dst->next; + free(dst); + wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor detached " + "%s:%d", inet_ntoa(from->sin_addr), + ntohs(from->sin_port)); + return 0; + } +#else /* CONFIG_CTRL_IFACE_UDP */ + if (fromlen == dst->addrlen && + memcmp(from->sun_path, dst->addr.sun_path, fromlen) == 0) { + if (prev == NULL) + wpa_s->ctrl_dst = dst->next; + else + prev->next = dst->next; + free(dst); + wpa_hexdump(MSG_DEBUG, "CTRL_IFACE monitor detached", + (u8 *) from->sun_path, fromlen); + return 0; + } +#endif /* CONFIG_CTRL_IFACE_UDP */ + prev = dst; + dst = dst->next; + } + return -1; +} + + +static int wpa_supplicant_ctrl_iface_level(struct wpa_supplicant *wpa_s, + CTRL_IFACE_SOCK *from, + socklen_t fromlen, + char *level) +{ + struct wpa_ctrl_dst *dst; + + wpa_printf(MSG_DEBUG, "CTRL_IFACE LEVEL %s", level); + + dst = wpa_s->ctrl_dst; + while (dst) { +#ifdef CONFIG_CTRL_IFACE_UDP + if (from->sin_addr.s_addr == dst->addr.sin_addr.s_addr && + from->sin_port == dst->addr.sin_port) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE changed monitor " + "level %s:%d", inet_ntoa(from->sin_addr), + ntohs(from->sin_port)); + dst->debug_level = atoi(level); + return 0; + } +#else /* CONFIG_CTRL_IFACE_UDP */ + if (fromlen == dst->addrlen && + memcmp(from->sun_path, dst->addr.sun_path, fromlen) == 0) { + wpa_hexdump(MSG_DEBUG, "CTRL_IFACE changed monitor " + "level", (u8 *) from->sun_path, fromlen); + dst->debug_level = atoi(level); + return 0; + } +#endif /* CONFIG_CTRL_IFACE_UDP */ + dst = dst->next; + } + + return -1; +} + + +static int wpa_supplicant_ctrl_iface_ctrl_rsp(struct wpa_supplicant *wpa_s, + char *rsp) +{ + char *pos, *id_pos; + int id; + struct wpa_ssid *ssid; + + pos = strchr(rsp, '-'); + if (pos == NULL) + return -1; + *pos++ = '\0'; + id_pos = pos; + pos = strchr(pos, ':'); + if (pos == NULL) + return -1; + *pos++ = '\0'; + id = atoi(id_pos); + wpa_printf(MSG_DEBUG, "CTRL_IFACE: field=%s id=%d value='%s'", + rsp, id, pos); + + ssid = wpa_s->conf->ssid; + while (ssid) { + if (id == ssid->id) + break; + ssid = ssid->next; + } + + if (ssid == NULL) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find SSID id=%d " + "to update", id); + return -1; + } + + if (strcmp(rsp, "IDENTITY") == 0) { + free(ssid->identity); + ssid->identity = (u8 *) strdup(pos); + ssid->identity_len = strlen(pos); + ssid->pending_req_identity = 0; + if (ssid == wpa_s->current_ssid) + wpa_s->reassociate = 1; + } else if (strcmp(rsp, "PASSWORD") == 0) { + free(ssid->password); + ssid->password = (u8 *) strdup(pos); + ssid->password_len = strlen(pos); + ssid->pending_req_password = 0; + if (ssid == wpa_s->current_ssid) + wpa_s->reassociate = 1; + } else if (strcmp(rsp, "OTP") == 0) { + free(ssid->otp); + ssid->otp = (u8 *) strdup(pos); + ssid->otp_len = strlen(pos); + free(ssid->pending_req_otp); + ssid->pending_req_otp = NULL; + ssid->pending_req_otp_len = 0; + } else { + wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown field '%s'", rsp); + return -1; + } + + return 0; +} + + +static int wpa_supplicant_ctrl_iface_status(struct wpa_supplicant *wpa_s, + const char *params, + char *buf, size_t buflen) +{ + char *pos, *end; + int res, verbose; + + verbose = strcmp(params, "-VERBOSE") == 0; + pos = buf; + end = buf + buflen; + pos += snprintf(pos, end - pos, "bssid=" MACSTR "\n", + MAC2STR(wpa_s->bssid)); + if (wpa_s->current_ssid) { + pos += snprintf(pos, end - pos, "ssid=%s\n", + wpa_ssid_txt(wpa_s->current_ssid->ssid, + wpa_s->current_ssid->ssid_len)); + } + pos += snprintf(pos, end - pos, + "pairwise_cipher=%s\n" + "group_cipher=%s\n" + "key_mgmt=%s\n" + "wpa_state=%s\n", + wpa_cipher_txt(wpa_s->pairwise_cipher), + wpa_cipher_txt(wpa_s->group_cipher), + wpa_key_mgmt_txt(wpa_s->key_mgmt, wpa_s->proto), + wpa_state_txt(wpa_s->wpa_state)); + + res = eapol_sm_get_status(wpa_s->eapol, pos, end - pos, verbose); + if (res >= 0) + pos += res; + + if (wpa_s->preauth_eapol) { + pos += snprintf(pos, end - pos, "Pre-authentication " + "EAPOL state machines:\n"); + res = eapol_sm_get_status(wpa_s->preauth_eapol, + pos, end - pos, verbose); + if (res >= 0) + pos += res; + } + + return pos - buf; +} + + +static void wpa_supplicant_ctrl_iface_receive(int sock, void *eloop_ctx, + void *sock_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + char buf[256]; + int res; + CTRL_IFACE_SOCK from; + socklen_t fromlen = sizeof(from); + char *reply; + const int reply_size = 2048; + int reply_len; + int new_attached = 0, ctrl_rsp = 0; + + res = recvfrom(sock, buf, sizeof(buf) - 1, 0, + (struct sockaddr *) &from, &fromlen); + if (res < 0) { + perror("recvfrom(ctrl_iface)"); + return; + } + buf[res] = '\0'; + if (strncmp(buf, "CTRL-RSP-", 9) == 0) { + wpa_hexdump_ascii_key(MSG_DEBUG, "RX ctrl_iface", + (u8 *) buf, res); + } else { + wpa_hexdump_ascii(MSG_DEBUG, "RX ctrl_iface", (u8 *) buf, res); + } + + reply = malloc(reply_size); + if (reply == NULL) { + sendto(sock, "FAIL\n", 5, 0, (struct sockaddr *) &from, + fromlen); + return; + } + + memcpy(reply, "OK\n", 3); + reply_len = 3; + + if (strcmp(buf, "PING") == 0) { + memcpy(reply, "PONG\n", 5); + reply_len = 5; + } else if (strcmp(buf, "MIB") == 0) { + reply_len = wpa_get_mib(wpa_s, reply, reply_size); + if (reply_len >= 0) { + res = eapol_sm_get_mib(wpa_s->eapol, reply + reply_len, + reply_size - reply_len); + if (res < 0) + reply_len = -1; + else + reply_len += res; + } + } else if (strncmp(buf, "STATUS", 6) == 0) { + reply_len = wpa_supplicant_ctrl_iface_status( + wpa_s, buf + 6, reply, reply_size); + } else if (strcmp(buf, "PMKSA") == 0) { + reply_len = pmksa_cache_list(wpa_s, reply, reply_size); + } else if (strncmp(buf, "SET ", 4) == 0) { + if (wpa_supplicant_ctrl_iface_set(wpa_s, buf + 4)) + reply_len = -1; + } else if (strcmp(buf, "LOGON") == 0) { + eapol_sm_notify_logoff(wpa_s->eapol, FALSE); + } else if (strcmp(buf, "LOGOFF") == 0) { + eapol_sm_notify_logoff(wpa_s->eapol, TRUE); + } else if (strcmp(buf, "REASSOCIATE") == 0) { + wpa_s->reassociate = 1; + wpa_supplicant_req_scan(wpa_s, 0, 0); + } else if (strncmp(buf, "PREAUTH ", 8) == 0) { + if (wpa_supplicant_ctrl_iface_preauth(wpa_s, buf + 8)) + reply_len = -1; + } else if (strcmp(buf, "ATTACH") == 0) { + if (wpa_supplicant_ctrl_iface_attach(wpa_s, &from, fromlen)) + reply_len = -1; + else + new_attached = 1; + } else if (strcmp(buf, "DETACH") == 0) { + if (wpa_supplicant_ctrl_iface_detach(wpa_s, &from, fromlen)) + reply_len = -1; + } else if (strncmp(buf, "LEVEL ", 6) == 0) { + if (wpa_supplicant_ctrl_iface_level(wpa_s, &from, fromlen, + buf + 6)) + reply_len = -1; + } else if (strncmp(buf, "CTRL-RSP-", 9) == 0) { + if (wpa_supplicant_ctrl_iface_ctrl_rsp(wpa_s, buf + 9)) + reply_len = -1; + else + ctrl_rsp = 1; + } else if (strcmp(buf, "RECONFIGURE") == 0) { + if (wpa_supplicant_reload_configuration(wpa_s)) + reply_len = -1; + } else if (strcmp(buf, "TERMINATE") == 0) { + eloop_terminate(); + } else { + memcpy(reply, "UNKNOWN COMMAND\n", 16); + reply_len = 16; + } + + if (reply_len < 0) { + memcpy(reply, "FAIL\n", 5); + reply_len = 5; + } + sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from, fromlen); + free(reply); + + if (new_attached) + eapol_sm_notify_ctrl_attached(wpa_s->eapol); + if (ctrl_rsp) + eapol_sm_notify_ctrl_response(wpa_s->eapol); +} + + +static char * wpa_supplicant_ctrl_iface_path(struct wpa_supplicant *wpa_s) +{ + char *buf; + size_t len; + + if (wpa_s->conf->ctrl_interface == NULL) + return NULL; + + len = strlen(wpa_s->conf->ctrl_interface) + strlen(wpa_s->ifname) + 2; + buf = malloc(len); + if (buf == NULL) + return NULL; + + snprintf(buf, len, "%s/%s", + wpa_s->conf->ctrl_interface, wpa_s->ifname); +#ifdef __CYGWIN__ + { + /* Windows/WinPcap uses interface names that are not suitable + * as a file name - convert invalid chars to underscores */ + char *pos = buf; + while (*pos) { + if (*pos == '\\') + *pos = '_'; + pos++; + } + } +#endif /* __CYGWIN__ */ + return buf; +} + + +int wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s) +{ + CTRL_IFACE_SOCK addr; + int s = -1; + char *fname = NULL; + + wpa_s->ctrl_sock = -1; + + if (wpa_s->conf->ctrl_interface == NULL) + return 0; + +#ifdef CONFIG_CTRL_IFACE_UDP + s = socket(PF_INET, SOCK_DGRAM, 0); + if (s < 0) { + perror("socket(PF_INET)"); + goto fail; + } + + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = htonl((127 << 24) | 1); + addr.sin_port = htons(9877); + if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + perror("bind(AF_UNIX)"); + goto fail; + } +#else /* CONFIG_CTRL_IFACE_UDP */ + if (mkdir(wpa_s->conf->ctrl_interface, S_IRWXU | S_IRWXG) < 0) { + if (errno == EEXIST) { + wpa_printf(MSG_DEBUG, "Using existing control " + "interface directory."); + } else { + perror("mkdir[ctrl_interface]"); + goto fail; + } + } + + if (wpa_s->conf->ctrl_interface_gid_set && + chown(wpa_s->conf->ctrl_interface, 0, + wpa_s->conf->ctrl_interface_gid) < 0) { + perror("chown[ctrl_interface]"); + return -1; + } + + if (strlen(wpa_s->conf->ctrl_interface) + 1 + strlen(wpa_s->ifname) >= + sizeof(addr.sun_path)) + goto fail; + + s = socket(PF_UNIX, SOCK_DGRAM, 0); + if (s < 0) { + perror("socket(PF_UNIX)"); + goto fail; + } + + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + fname = wpa_supplicant_ctrl_iface_path(wpa_s); + if (fname == NULL) + goto fail; + strncpy(addr.sun_path, fname, sizeof(addr.sun_path)); + if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + perror("bind(PF_UNIX)"); + if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + wpa_printf(MSG_DEBUG, "ctrl_iface exists, but does not" + " allow connections - assuming it was left" + "over from forced program termination"); + if (unlink(fname) < 0) { + perror("unlink[ctrl_iface]"); + wpa_printf(MSG_ERROR, "Could not unlink " + "existing ctrl_iface socket '%s'", + fname); + goto fail; + } + if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < + 0) { + perror("bind(PF_UNIX)"); + goto fail; + } + wpa_printf(MSG_DEBUG, "Successfully replaced leftover " + "ctrl_iface socket '%s'", fname); + } else { + wpa_printf(MSG_INFO, "ctrl_iface exists and seems to " + "be in use - cannot override it"); + wpa_printf(MSG_INFO, "Delete '%s' manually if it is " + "not used anymore", fname); + free(fname); + fname = NULL; + goto fail; + } + } + + if (wpa_s->conf->ctrl_interface_gid_set && + chown(fname, 0, wpa_s->conf->ctrl_interface_gid) < 0) { + perror("chown[ctrl_interface/ifname]"); + goto fail; + } + + if (chmod(fname, S_IRWXU | S_IRWXG) < 0) { + perror("chmod[ctrl_interface/ifname]"); + goto fail; + } + free(fname); +#endif /* CONFIG_CTRL_IFACE_UDP */ + + wpa_s->ctrl_sock = s; + eloop_register_read_sock(s, wpa_supplicant_ctrl_iface_receive, wpa_s, + NULL); + + return 0; + +fail: + if (s >= 0) + close(s); + if (fname) { + unlink(fname); + free(fname); + } + return -1; +} + + +void wpa_supplicant_ctrl_iface_deinit(struct wpa_supplicant *wpa_s) +{ + struct wpa_ctrl_dst *dst, *prev; + + if (wpa_s->ctrl_sock > -1) { + char *fname; + eloop_unregister_read_sock(wpa_s->ctrl_sock); + close(wpa_s->ctrl_sock); + wpa_s->ctrl_sock = -1; + fname = wpa_supplicant_ctrl_iface_path(wpa_s); + if (fname) + unlink(fname); + free(fname); + + if (rmdir(wpa_s->conf->ctrl_interface) < 0) { + if (errno == ENOTEMPTY) { + wpa_printf(MSG_DEBUG, "Control interface " + "directory not empty - leaving it " + "behind"); + } else { + perror("rmdir[ctrl_interface]"); + } + } + } + + dst = wpa_s->ctrl_dst; + while (dst) { + prev = dst; + dst = dst->next; + free(prev); + } +} + + +void wpa_supplicant_ctrl_iface_send(struct wpa_supplicant *wpa_s, int level, + char *buf, size_t len) +{ + struct wpa_ctrl_dst *dst, *next; + char levelstr[10]; + int idx; +#ifdef CONFIG_CTRL_IFACE_UDP + char *sbuf; + int llen; + + dst = wpa_s->ctrl_dst; + if (wpa_s->ctrl_sock < 0 || dst == NULL) + return; + + snprintf(levelstr, sizeof(levelstr), "<%d>", level); + + llen = strlen(levelstr); + sbuf = malloc(llen + len); + if (sbuf == NULL) + return; + + memcpy(sbuf, levelstr, llen); + memcpy(sbuf + llen, buf, len); + + idx = 0; + while (dst) { + next = dst->next; + if (level >= dst->debug_level) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor send %s:%d", + inet_ntoa(dst->addr.sin_addr), + ntohs(dst->addr.sin_port)); + if (sendto(wpa_s->ctrl_sock, sbuf, llen + len, 0, + (struct sockaddr *) &dst->addr, + sizeof(dst->addr)) < 0) { + fprintf(stderr, "CTRL_IFACE monitor[%d]: ", + idx); + perror("sendto"); + dst->errors++; + if (dst->errors > 10) { + wpa_supplicant_ctrl_iface_detach( + wpa_s, &dst->addr, + dst->addrlen); + } + } else + dst->errors = 0; + } + idx++; + dst = next; + } + free(sbuf); +#else /* CONFIG_CTRL_IFACE_UDP */ + struct msghdr msg; + struct iovec io[2]; + + dst = wpa_s->ctrl_dst; + if (wpa_s->ctrl_sock < 0 || dst == NULL) + return; + + snprintf(levelstr, sizeof(levelstr), "<%d>", level); + io[0].iov_base = levelstr; + io[0].iov_len = strlen(levelstr); + io[1].iov_base = buf; + io[1].iov_len = len; + memset(&msg, 0, sizeof(msg)); + msg.msg_iov = io; + msg.msg_iovlen = 2; + + idx = 0; + while (dst) { + next = dst->next; + if (level >= dst->debug_level) { + wpa_hexdump(MSG_DEBUG, "CTRL_IFACE monitor send", + (u8 *) dst->addr.sun_path, dst->addrlen); + msg.msg_name = &dst->addr; + msg.msg_namelen = dst->addrlen; + if (sendmsg(wpa_s->ctrl_sock, &msg, 0) < 0) { + fprintf(stderr, "CTRL_IFACE monitor[%d]: ", + idx); + perror("sendmsg"); + dst->errors++; + if (dst->errors > 10) { + wpa_supplicant_ctrl_iface_detach( + wpa_s, &dst->addr, + dst->addrlen); + } + } else + dst->errors = 0; + } + idx++; + dst = next; + } +#endif /* CONFIG_CTRL_IFACE_UDP */ +} diff --git a/contrib/wpa_supplicant/ctrl_iface.h b/contrib/wpa_supplicant/ctrl_iface.h new file mode 100644 index 000000000000..d7ad6dfe670a --- /dev/null +++ b/contrib/wpa_supplicant/ctrl_iface.h @@ -0,0 +1,31 @@ +#ifndef CTRL_IFACE_H +#define CTRL_IFACE_H + +#ifdef CONFIG_CTRL_IFACE + +int wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s); +void wpa_supplicant_ctrl_iface_deinit(struct wpa_supplicant *wpa_s); +void wpa_supplicant_ctrl_iface_send(struct wpa_supplicant *wpa_s, int level, + char *buf, size_t len); + +#else /* CONFIG_CTRL_IFACE */ + +static inline int wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s) +{ + return 0; +} + +static inline void +wpa_supplicant_ctrl_iface_deinit(struct wpa_supplicant *wpa_s) +{ +} + +static inline void +wpa_supplicant_ctrl_iface_send(struct wpa_supplicant *wpa_s, int level, + char *buf, size_t len) +{ +} + +#endif /* CONFIG_CTRL_IFACE */ + +#endif /* CTRL_IFACE_H */ diff --git a/contrib/wpa_supplicant/defconfig b/contrib/wpa_supplicant/defconfig new file mode 100644 index 000000000000..7e8e3811d06f --- /dev/null +++ b/contrib/wpa_supplicant/defconfig @@ -0,0 +1,154 @@ +# Example wpa_supplicant build time configuration +# +# This file lists the configuration options that are used when building the +# hostapd binary. All lines starting with # are ignored. Configuration option +# lines must be commented out complete, if they are not to be included, i.e., +# just setting VARIABLE=n is not disabling that variable. +# +# This file is included in Makefile, so variables like CFLAGS and LIBS can also +# be modified from here. In most cass, these lines should use += in order not +# to override previous values of the variables. + + +# Uncomment following two lines and fix the paths if you have installed openssl +# in non-default location +#CFLAGS += -I/usr/local/openssl/include +#LIBS += -L/usr/local/openssl/lib + +# Example configuration for various cross-compilation platforms + +#### sveasoft (e.g., for Linksys WRT54G) ###################################### +#CC=mipsel-uclibc-gcc +#CC=/opt/brcm/hndtools-mipsel-uclibc/bin/mipsel-uclibc-gcc +#CFLAGS += -Os +#CPPFLAGS += -I../src/include -I../../src/router/openssl/include +#LIBS += -L/opt/brcm/hndtools-mipsel-uclibc-0.9.19/lib -lssl +############################################################################### + +#### openwrt (e.g., for Linksys WRT54G) ####################################### +#CC=mipsel-uclibc-gcc +#CC=/opt/brcm/hndtools-mipsel-uclibc/bin/mipsel-uclibc-gcc +#CFLAGS += -Os +#CPPFLAGS=-I../src/include -I../openssl-0.9.7d/include \ +# -I../WRT54GS/release/src/include +#LIBS = -lssl +############################################################################### + + +# Driver interface for Host AP driver +CONFIG_DRIVER_HOSTAP=y + +# Driver interface for Agere driver +#CONFIG_DRIVER_HERMES=y +# Change include directories to match with the local setup +#CFLAGS += -I../../hcf -I../../include -I../../include/hcf +#CFLAGS += -I../../include/wireless + +# Driver interface for madwifi driver +#CONFIG_DRIVER_MADWIFI=y +# Change include directories to match with the local setup +#CFLAGS += -I../madwifi/wpa + +# Driver interface for Prism54 driver +CONFIG_DRIVER_PRISM54=y + +# Driver interface for ndiswrapper +#CONFIG_DRIVER_NDISWRAPPER=y + +# Driver interface for Atmel driver +CONFIG_DRIVER_ATMEL=y + +# Driver interface for Broadcom driver +#CONFIG_DRIVER_BROADCOM=y +# Example path for wlioctl.h; change to match your configuration +#CFLAGS += -I/opt/WRT54GS/release/src/include + +# Driver interface for Intel ipw2100/2200 driver +#CONFIG_DRIVER_IPW=y + +# Driver interface for generic Linux wireless extensions +CONFIG_DRIVER_WEXT=y + +# Driver interface for FreeBSD net80211 layer (e.g., Atheros driver) +#CONFIG_DRIVER_BSD=y +#CFLAGS += -I/usr/local/include +#LIBS += -L/usr/local/lib + +# Driver interface for Windows NDIS +#CONFIG_DRIVER_NDIS=y +#CFLAGS += -I/usr/include/w32api/ddk +#LIBS += -L/usr/local/lib +# For native build using mingw +#CONFIG_NATIVE_WINDOWS=y +# Additional directories for cross-compilation on Linux host for mingw target +#CFLAGS += -I/opt/mingw/mingw32/include/ddk +#LIBS += -L/opt/mingw/mingw32/lib +#CC=mingw32-gcc + +# Driver interface for development testing +#CONFIG_DRIVER_TEST=y + +# Enable IEEE 802.1X Supplicant (automatically included if any EAP method is +# included) +CONFIG_IEEE8021X_EAPOL=y + +# EAP-MD5 (automatically included if EAP-TTLS is enabled) +CONFIG_EAP_MD5=y + +# EAP-MSCHAPv2 (automatically included if EAP-PEAP is enabled) +CONFIG_EAP_MSCHAPV2=y + +# EAP-TLS +CONFIG_EAP_TLS=y + +# EAL-PEAP +CONFIG_EAP_PEAP=y + +# EAP-TTLS +CONFIG_EAP_TTLS=y + +# EAP-GTC +CONFIG_EAP_GTC=y + +# EAP-OTP +CONFIG_EAP_OTP=y + +# EAP-SIM (enable CONFIG_PCSC, if EAP-SIM is used) +#CONFIG_EAP_SIM=y + +# EAP-PSK (experimental; this is _not_ needed for WPA-PSK) +#CONFIG_EAP_PSK=y + +# LEAP +CONFIG_EAP_LEAP=y + +# EAP-AKA (enable CONFIG_PCSC, if EAP-AKA is used) +#CONFIG_EAP_AKA=y + +# PKCS#12 (PFX) support (used to read private key and certificate file from +# a file that usually has extension .p12 or .pfx) +CONFIG_PKCS12=y + +# PC/SC interface for smartcards (USIM, GSM SIM) +# Enable this if EAP-SIM or EAP-AKA is included +#CONFIG_PCSC=y + +# Development testing +#CONFIG_EAPOL_TEST=y + +# Replace native Linux implementation of packet sockets with libdnet/libpcap. +# This will be automatically set for non-Linux OS. +#CONFIG_DNET_PCAP=y + +# Include control interface for external programs, e.g, wpa_cli +CONFIG_CTRL_IFACE=y + +# Include interface for using external supplicant (Xsupplicant) for EAP +# authentication +#CONFIG_XSUPPLICANT_IFACE=y + +# Include support for GNU Readline and History Libraries in wpa_cli. +# When building a wpa_cli binary for distribution, please note that these +# libraries are licensed under GPL and as such, BSD license may not apply for +# the resulting binary. +#CONFIG_READLINE=y diff --git a/contrib/wpa_supplicant/defs.h b/contrib/wpa_supplicant/defs.h new file mode 100644 index 000000000000..a5a515c552f5 --- /dev/null +++ b/contrib/wpa_supplicant/defs.h @@ -0,0 +1,14 @@ +#ifndef DEFS_H +#define DEFS_H + +#ifdef CONFIG_NATIVE_WINDOWS +#ifdef FALSE +#undef FALSE +#endif +#ifdef TRUE +#undef TRUE +#endif +#endif /* CONFIG_NATIVE_WINDOWS */ +typedef enum { FALSE = 0, TRUE = 1 } Boolean; + +#endif /* DEFS_H */ diff --git a/contrib/wpa_supplicant/developer.txt b/contrib/wpa_supplicant/developer.txt new file mode 100644 index 000000000000..bc5b34645d1a --- /dev/null +++ b/contrib/wpa_supplicant/developer.txt @@ -0,0 +1,458 @@ +Developer notes for wpa_supplicant +================================== + +The design goal for wpa_supplicant was to use hardware, driver, and OS +independent, portable C code for all WPA functionality. All +hardware/driver specific functionality is in separate files that +implement a well-defined driver API. + +The goal of this file and the comments in the header files is to give +enough information for other developers to be able to port the example +code. If any information is missing, feel free to contact Jouni Malinen +<jkmaline@cc.hut.fi> for more information. Contributions as patch files +are also very welcome at the same address. + +Structure of the source code +---------------------------- + +Program initialization, main control loop and event handling is +implemented in wpa_supplicant.c. WPA state machines and 4-Way/Group +Key Handshake processing in in wpa.c. IEEE 802.1X/EAPOL processing and +state machines are in eapol_sm.c. EAP state machine is in eap.c. EAP +methods for the internal EAP peer are in eap_*.c. Parser for the +configuration file is implemented in config.c. + +Driver interface API is defined in driver.h and all hardware/driver +dependent functionality is implemented in driver_*.c (see below). + + +Generic helper functions +------------------------ + +wpa_supplicant uses generic helper functions some of which are shared +with with hostapd. The following C files are currently used: + +eloop.[ch] + event loop (select() loop with registerable timeouts, socket read + callbacks, and signal callbacks) + +common.[ch] + common helper functions + +defs.h + definitions shared by multiple files + +l2_packet.[ch] + Layer 2 (link) access wrapper (includes native Linux implementation + and wrappers for libdnet/libpcap) + +pcsc_funcs.[ch] + Wrapper for PC/SC lite SIM and smart card readers + + +Cryptographic functions +----------------------- + +md5.c + MD5 (replaced with openssl/crypto if TLS support is included) + HMAC-MD5 (keyed checksum for message authenticity validation) + +rc4.c + RC4 (broadcast/default key encryption) + +sha1.c + SHA-1 (replaced with openssl/crypto if TLS support is included) + HMAC-SHA-1 (keyed checksum for message authenticity validation) + PRF-SHA-1 (pseudorandom (key/nonce generation) function) + PBKDF2-SHA-1 (ASCII passphrase to shared secret) + T-PRF (for EAP-FAST) + TLS-PRF (RFC 2246) + +aes_wrap.[ch], aes.c + AES + AES Key Wrap Algorithm with 128-bit KEK, RFC3394 (broadcast/default + key encryption) + One-Key CBC MAC (OMAC1) hash with AES-128 + AES-128 CTR mode encryption + AES-128 EAX mode encryption/decryption + AES-128 CBC + +crypto.[ch] + Wrapper functions for libcrypto (MD4 and DES) + +ms_funcs.[ch] + Helper functions for MSCHAPV2 and LEAP + +tls.h + Definition of TLS library wrapper + +tls_none.c + Dummy implementation of TLS library wrapper for cases where TLS + functionality is not included. + +tls_openssl.c + TLS library wrapper for openssl + + +Configuration +------------- + +config_ssid.h + Definition of per network configuration items + +config.h + Definition of the wpa_supplicant configuration + +config.c + Configuration file parser + + +Control interface +----------------- + +wpa_supplicant has a control interface that can be used to get status +information and manage operations from external programs. An example, +command line interface, wpa_cli, for this interface is included in the +wpa_supplicant distribution. + +ctrl_iface.[ch] + wpa_supplicant-side of the control interface + +wpa_ctrl.[ch] + Library functions for external programs to provide access to the + wpa_supplicant control interface + +wpa_cli.c + Example program for using wpa_supplicant control interface + + +EAP peer +-------- + +eap.[ch] + EAP state machine + +eap_defs.h + Common EAP definitions + +eap_i.h + Internal definitions for EAP state machine and EAP methods + +eap_sim_common.[ch] + Common code for EAP-SIM and EAP-AKA + +eap_tls_common.[ch] + Common code for EAP-PEAP, EAP-TTLS, and EAP-FAST + +eap_tlv.[ch] + EAP-TLV code for EAP-PEAP and EAP-FAST + +eap_{aka,fast,gtc,leap,md5,mschapv2,otp,peap,psk,sim,tls,ttls}.c + EAP method implementations + + +EAPOL supplicant +---------------- + +eapol_sm.[ch] + EAPOL supplicant state machine and IEEE 802.1X processing + + +Windows port +------------ + +ndis_events.cpp + External program for receiving NdisMIndicateStatus() events and + delivering them to wpa_supplicant in more easier to use form + +win_if_list.c + External program for listing current network interface + + +Test programs +------------- + +radius_client.[ch] + RADIUS authentication client implementation for eapol_test + +eapol_test.c + Standalone EAP testing tool with integrated RADIUS authentication + client + +preauth_test.c + Standalone RSN pre-authentication tool + + +wpa_supplicant.c +---------------- + +main() +- parse command line +- call config file parser +- initialize Supplicant data structures +- call functions to initialize WPA support in the driver +- initialize event loop +- cleanup when exiting + +wpa_supplicant_dot1x_receive() +- receive master session key update from Xsupplicant (optional) + +wpa_supplicant_get_beacon_ie() + +wpa_supplicant_deauthenticate() + +wpa_supplicant_disassociate() + +wpa_supplicant_scan() + +wpa_supplicant_reconfig() +- SIGHUP signal processing + +wpa_supplicant_terminate() +- SIGINT and SIGTERM processing + +wpa_supplicant_reload_configuration() + +wpa_supplicant_event() +- receive driver events (through driver wrapper functions) + * wpa_supplicant_scan_results(): process scan result event, BSS selection + * wpa_supplicant_associnfo(): process association information event + +wpa_supplicant_associate() +- control association (select cipher and key management suites, initiate + association) + +wpa_supplicant_req_auth_timeout() + +wpa_supplicant_cancel_auth_timeout() + +wpa_supplicant_req_scan() + +wpa_supplicant_cancel_scan() + +wpa_supplicant_notify_eapol_done() + +wpa_eapol_send() +- send EAPOL frames + +wpa_eapol_send_preauth() +- send RSN preauthentication frames + +wpa_msg() +- event/debug function + + +wpa_supplicant.h +---------------- + +- driver event definition +- common function definition (e.g., wpa_msg) + + +wpa_supplicant_i.h +------------------ +- internal definitions for wpa_supplicant; must not be included into + common code, EAP methods, driver interface implementations + + +wpa.[ch] +-------- +- WPA supplicant state machine and 4-Way/Group Key Handshake processing +- PMKSA cache and RSN pre-authentication + +pmksa_cache_free() + +pmksa_cache_get() + +pmksa_cache_list() + +pmksa_candidate_free() + +wpa_parse_wpa_ie() +- WPA/RSN IE parsing + +wpa_gen_wpa_ei() +- WPA/RSN IE generation + +wpa_supplicant_get_ssid() + +wpa_supplicant_key_request() +- trigger function to start key requests + +wpa_sm_rx_eapol() +- WPA processing for received EAPOL-Key frames + * wpa_supplicant_process_1_of_4() (message 1 of 4-Way Handshake) + * wpa_supplicant_process_3_of_4() (message 3 of 4-Way Handshake) + * wpa_supplicant_process_1_of_2() (message 1 of Group Key Handshake) + +wpa_supplicant_rx_eapol() +- l2_packet RX callback for EAPOL frames; sends the frames to WPA and EAPOL + state machines for further processing + +wpa_get_mib() + +rsn_preauth_receive() +- l2_packet RX callback for preauthentication frames + +rsn_preauth_eapol_cb() +- callback function to be called when EAPOL authentication has been completed + (either successfully or unsuccessfully) for RSN pre-authentication + +rsn_preauth_init() +rsn_preauth_deinit() + +pmksa_candidate_add() +- add a BSSID to PMKSA candidate list + +rsn_preauth_scan_results() +- update RSN pre-authentication candidate list based on scan results + + +Driver wrapper implementation (driver.h, drivers.c) +--------------------------------------------------- + +All hardware and driver dependent functionality is implemented in as a +separate C file(s) implementing defined wrapper functions. Other parts +of the wpa_supplicant are designed to be hardware, driver, and operating +system independent. + +Driver wrappers need to implement whatever calls are used in the +target operating system/driver for controlling wireless LAN +devices. As an example, in case of Linux, these are mostly some glue +code and ioctl() calls and netlink message parsing for Linux Wireless +Extensions. Since all features required for WPA are not yet included +in Wireless Extensions, some driver specific code is used in the +example implementation for Host AP driver. These driver dependent parts +are to be replaced with generic code once the needed changes are +included in the Wireless Extensions. After that, all Linux drivers, at +least in theory, could use the same driver wrapper code. + +A driver wrapper needs to implement some or all of the functions +defined in driver.h (see that file for detailed documentation of the +functions). Hardware independent parts of wpa_supplicant will call +these functions to control the driver/wlan card. In addition, support +for driver events is required. The event callback function, +wpa_supplicant_event(), and its parameters are documented in +wpa_supplicant.h. In addition, pointer to the 'struct wpa_driver_ops' +needs to be registered in drivers.c file. + +When porting to other operating systems, driver wrapper should be +modified to use the native interface of the target OS. It is possible +that some extra requirements for the interface between the driver +wrapper and generic wpa_supplicant code are discovered during porting +to a new operating system. These will be addresses on case by case +basic by modifying the interface and updating the other driver +wrappers for this. The goal is to avoid changing this interface +without very good reasons in order to limit the number of changes +needed to other wrappers and hardware independent parts of +wpa_supplicant. + +Generic Linux Wireless Extensions functions are implemented in +driver_wext.c. All Linux driver wrappers can use these when the kernel +driver supports the generic ioctl()s and wireless events. Driver +specific functions are implemented in separate C files, e.g., +driver_hostap.c. These files need to define struct wpa_driver_ops +entry that will be used in wpa_supplicant.c when calling driver +functions. These entries need to be added to the lists in +wpa_supplicant_set_driver() and usage() functions in wpa_supplicant.c. + +In general, it is likely to be useful to first take a look at couple +of the driver interfaces before starting on implementing a new +one. driver_hostap.c and driver_wext.c include a complete +implementation for Linux drivers that use wpa_supplicant-based control +of WPA IE and roaming. driver_ndis.c (with help from driver_ndis_.c) +is an example of a complete interface for Windows NDIS interface for +drivers that generate WPA IE themselves and decide when to roam. These +example implementations include full support for all security modes. + + +Driver requirements for WPA +--------------------------- + +WPA introduces new requirements for the device driver. At least some +of these need to be implemented in order to provide enough support for +wpa_supplicant. + +TKIP/CCMP + +WPA requires that the pairwise cipher suite (encryption algorithm for +unicast data packets) is TKIP or CCMP. These are new encryption +protocols and thus, the driver will need to be modified to support +them. Depending on the used wlan hardware, some parts of these may be +implemented by the hardware/firmware. + +Specification for both TKIP and CCMP is available from IEEE (IEEE +802.11i draft version 3.0). Fully functional, hardware independent +implementation of both encryption protocols is also available in Host +AP driver (driver/modules/hostap_{tkip,ccmp}.c). + +The driver will also need to provide configuration mechanism to allow +user space programs to configure TKIP and CCMP. Current Linux Wireless +Extensions (v16) does not yet support these algorithms or +individual/non-default keys. Host AP driver has an example of private +ioctl()s for this. Eventually, this should be replaced with modified +Linux Wireless Extensions. + +Roaming control and scanning support + +wpa_supplicant controls AP selections based on the information +received from Beacon and/or Probe Response frames. This means that the +driver should support external control for scan process. In case of +Linux, use of new Wireless Extensions scan support (i.e., 'iwlist +wlan0 scan') is recommended. The current driver wrapper (driver_wext.c) +uses this for scan results. + +Scan results must also include WPA information element. This is not +yet defined in Linux Wireless Extensions and Host AP driver uses a +custom event to provide the full WPA IE (including element id and +length) as a hex string that is included in the scan results. +Eventually, this should be defined as a Wireless Extensions ioctl +that can be used both with scan results and with configuration of WPA IE +for association request (and Beacon/Probe Response in case of an +AP/IBSS). + +wpa_supplicant needs to also be able to request the driver to +associate with a specific BSS. Current Host AP driver and matching +driver_hostap.c wrapper uses following sequence for this +request. Similar/identical mechanism should be usable also with other +drivers. + +- set WPA IE for AssocReq with private ioctl +- set SSID with SIOCSIWESSID +- set channel/frequency with SIOCSIWFREQ +- set BSSID with SIOCSIWAP + (this last ioctl will trigger the driver to request association) + +WPA IE generation + +wpa_supplicant selects which cipher suites and key management suites +are used. Based on this information, it generates a WPA IE. This is +provided to the driver interface in the associate call. This does not +match with Windows NDIS drivers which generate the WPA IE +themselves. + +wpa_supplicant allows Windows NDIS-like behavior by providing the +selected cipher and key management suites in the associate call. If +the driver generates its own WPA IE and that differs from the one +generated by wpa_supplicant, the driver has to inform wpa_supplicant +about the used WPA IE (i.e., the one it used in (Re)Associate +Request). This notification is done using EVENT_ASSOCINFO event (see +wpa_supplicant.h). + +Driver events + +wpa_supplicant needs to receive event callbacks when certain events +occur (association, disassociation, Michael MIC failure, scan results +available, PMKSA caching candidate). These events and the callback +details are defined in wpa_supplicant.h. + +On Linux, association and disassociation can use existing Wireless +Extensions event that is reporting new AP with SIOCGIWAP +event. Similarly, completion of scan can be reported with SIOCGIWSCAN +event. + +Michael MIC failure event is not yet included in Wireless Extensions, +so this needs a custom event. Host AP driver uses custom event with +following contents: MLME-MICHAELMICFAILURE.indication(keyid=# +broadcast/unicast addr=addr2). This is the recommended format until +the event is added to Linux Wireless Extensions. diff --git a/contrib/wpa_supplicant/doc/wpa_supplicant.fig b/contrib/wpa_supplicant/doc/wpa_supplicant.fig new file mode 100644 index 000000000000..dc1d9dfcf738 --- /dev/null +++ b/contrib/wpa_supplicant/doc/wpa_supplicant.fig @@ -0,0 +1,221 @@ +#FIG 3.2 +Landscape +Center +Inches +Letter +100.00 +Single +-2 +1200 2 +6 1875 4050 2925 4350 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 1875 4050 2925 4050 2925 4350 1875 4350 1875 4050 +4 0 0 50 -1 0 12 0.0000 4 180 735 2025 4275 l2_packet\001 +-6 +6 3450 1200 4275 1500 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 3450 1200 4275 1200 4275 1500 3450 1500 3450 1200 +4 0 0 50 -1 0 12 0.0000 4 180 585 3600 1425 wpa_cli\001 +-6 +6 4725 1200 5925 1500 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 4725 1200 5925 1200 5925 1500 4725 1500 4725 1200 +4 0 0 50 -1 0 12 0.0000 4 135 1005 4800 1425 GUI frontend\001 +-6 +6 6000 2700 7200 3225 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 6000 2700 7200 2700 7200 3225 6000 3225 6000 2700 +4 0 0 50 -1 0 12 0.0000 4 135 975 6075 2925 WPA/WPA2\001 +4 0 0 50 -1 0 12 0.0000 4 135 1065 6075 3150 state machine\001 +-6 +6 6000 4950 7200 5475 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 6000 4950 7200 4950 7200 5475 6000 5475 6000 4950 +4 0 0 50 -1 0 12 0.0000 4 135 360 6075 5175 EAP\001 +4 0 0 50 -1 0 12 0.0000 4 135 1065 6075 5400 state machine\001 +-6 +6 8700 3000 9375 3300 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 8700 3000 9375 3000 9375 3300 8700 3300 8700 3000 +4 0 0 50 -1 0 12 0.0000 4 150 480 8775 3225 crypto\001 +-6 +6 4350 3900 5025 4425 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 4350 3900 5025 3900 5025 4425 4350 4425 4350 3900 +4 0 0 50 -1 0 12 0.0000 4 105 420 4500 4125 event\001 +4 0 0 50 -1 0 12 0.0000 4 180 315 4500 4350 loop\001 +-6 +6 4275 2550 5100 2850 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 4275 2550 5100 2550 5100 2850 4275 2850 4275 2550 +4 0 0 50 -1 0 12 0.0000 4 135 450 4425 2775 ctrl i/f\001 +-6 +6 6000 3900 7200 4425 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 6000 3900 7200 3900 7200 4425 6000 4425 6000 3900 +4 0 0 50 -1 0 12 0.0000 4 135 600 6075 4125 EAPOL\001 +4 0 0 50 -1 0 12 0.0000 4 135 1065 6075 4350 state machine\001 +-6 +6 1800 6000 7800 8100 +6 1800 6000 7800 7200 +6 1800 6900 2700 7200 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 1800 6900 2700 6900 2700 7200 1800 7200 1800 6900 +4 0 0 50 -1 0 12 0.0000 4 105 375 1875 7125 wext\001 +-6 +6 4725 6900 5625 7200 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 4725 6900 5625 6900 5625 7200 4725 7200 4725 6900 +4 0 0 50 -1 0 12 0.0000 4 135 555 4800 7125 hermes\001 +-6 +6 6675 6900 7800 7200 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 6675 6900 7800 6900 7800 7200 6675 7200 6675 6900 +4 0 0 50 -1 0 12 0.0000 4 180 930 6750 7125 ndiswrapper\001 +-6 +6 5700 6900 6600 7200 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 5700 6900 6600 6900 6600 7200 5700 7200 5700 6900 +4 0 0 50 -1 0 12 0.0000 4 135 420 5775 7125 atmel\001 +-6 +6 4275 6000 5100 6300 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 4275 6000 5100 6000 5100 6300 4275 6300 4275 6000 +4 0 0 50 -1 0 12 0.0000 4 135 630 4350 6225 driver i/f\001 +-6 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 2775 6900 3675 6900 3675 7200 2775 7200 2775 6900 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 3750 6900 4650 6900 4650 7200 3750 7200 3750 6900 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 4 + 2250 6900 2250 6600 7200 6600 7200 6900 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 3225 6900 3225 6600 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 4200 6900 4200 6600 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 5175 6900 5175 6600 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 6150 6900 6150 6600 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 4650 6600 4650 6300 +4 0 0 50 -1 0 12 0.0000 4 180 510 2850 7125 hostap\001 +4 0 0 50 -1 0 12 0.0000 4 135 600 3825 7125 madwifi\001 +-6 +6 3525 7800 5775 8100 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 3525 7800 5775 7800 5775 8100 3525 8100 3525 7800 +4 0 0 50 -1 0 12 0.0000 4 135 2145 3600 8025 kernel network device driver\001 +-6 +2 1 1 1 0 7 50 -1 -1 4.000 0 0 -1 0 0 2 + 2250 7200 4200 7800 +2 1 1 1 0 7 50 -1 -1 4.000 0 0 -1 0 0 2 + 7200 7200 5100 7800 +-6 +6 9600 3000 10275 3300 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 9600 3000 10275 3000 10275 3300 9600 3300 9600 3000 +4 0 0 50 -1 0 12 0.0000 4 135 315 9750 3225 TLS\001 +-6 +6 8100 4425 10425 6975 +6 8175 4725 9225 5025 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 8175 4725 9225 4725 9225 5025 8175 5025 8175 4725 +4 0 0 50 -1 0 12 0.0000 4 135 735 8250 4950 EAP-TLS\001 +-6 +6 9300 4725 10350 5025 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 9300 4725 10350 4725 10350 5025 9300 5025 9300 4725 +4 0 0 50 -1 0 12 0.0000 4 135 810 9375 4950 EAP-MD5\001 +-6 +6 8175 5100 9225 5400 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 8175 5100 9225 5100 9225 5400 8175 5400 8175 5100 +4 0 0 50 -1 0 12 0.0000 4 135 885 8250 5325 EAP-PEAP\001 +-6 +6 9300 5100 10350 5400 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 9300 5100 10350 5100 10350 5400 9300 5400 9300 5100 +4 0 0 50 -1 0 12 0.0000 4 135 840 9375 5325 EAP-TTLS\001 +-6 +6 8175 5475 9225 5775 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 8175 5475 9225 5475 9225 5775 8175 5775 8175 5475 +4 0 0 50 -1 0 12 0.0000 4 135 780 8250 5700 EAP-GTC\001 +-6 +6 9300 5475 10350 5775 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 9300 5475 10350 5475 10350 5775 9300 5775 9300 5475 +4 0 0 50 -1 0 12 0.0000 4 135 765 9375 5700 EAP-OTP\001 +-6 +6 8175 5850 9225 6150 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 8175 5850 9225 5850 9225 6150 8175 6150 8175 5850 +4 0 0 50 -1 0 12 0.0000 4 135 750 8250 6075 EAP-SIM\001 +-6 +6 8175 6600 9675 6900 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 8175 6600 9675 6600 9675 6900 8175 6900 8175 6600 +4 0 0 50 -1 0 12 0.0000 4 135 1365 8250 6825 EAP-MSCHAPv2\001 +-6 +6 9300 6225 10350 6525 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 9300 6225 10350 6225 10350 6525 9300 6525 9300 6225 +4 0 0 50 -1 0 12 0.0000 4 135 465 9375 6450 LEAP\001 +-6 +6 8175 6225 9225 6525 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 8175 6225 9225 6225 9225 6525 8175 6525 8175 6225 +4 0 0 50 -1 0 12 0.0000 4 135 765 8250 6450 EAP-PSK\001 +-6 +6 9300 5850 10350 6150 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 9300 5850 10350 5850 10350 6150 9300 6150 9300 5850 +4 0 0 50 -1 0 12 0.0000 4 135 825 9375 6075 EAP-AKA\001 +-6 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 8100 6975 10425 6975 10425 4425 8100 4425 8100 6975 +4 0 0 50 -1 0 12 0.0000 4 135 1050 8700 4650 EAP methods\001 +-6 +2 1 1 1 0 7 50 -1 -1 3.000 0 0 -1 0 0 2 + 1275 4200 1875 4200 +2 1 1 1 0 7 50 -1 -1 4.000 0 0 -1 0 0 2 + 4500 2550 3900 1500 +2 1 1 1 0 7 50 -1 -1 4.000 0 0 -1 0 0 2 + 4800 2550 5400 1500 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 2925 4200 4350 4200 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 5025 3900 6000 3000 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 5025 4200 6000 4200 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 4650 6000 4650 4425 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 6600 4425 6600 4950 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 6600 3225 6600 3900 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 7200 5250 8100 5250 +2 1 0 1 2 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 9075 4425 9075 3300 +2 1 0 1 2 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 7200 3000 8700 3150 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 4650 3900 4650 2850 +2 1 0 1 2 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 7200 4125 8700 3300 +2 1 0 1 2 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 6000 4350 5025 6000 +2 1 0 1 2 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 6000 3150 4875 6000 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 1500 2100 10800 2100 10800 7500 1500 7500 1500 2100 +2 1 0 1 2 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 9900 4425 9900 3300 +4 0 0 50 -1 0 12 0.0000 4 135 915 375 3975 EAPOL and\001 +4 0 0 50 -1 0 12 0.0000 4 180 630 375 4200 pre-auth\001 +4 0 0 50 -1 0 12 0.0000 4 180 810 375 4425 ethertypes\001 +4 0 0 50 -1 0 12 0.0000 4 135 1050 375 4650 from/to kernel\001 +4 0 0 50 -1 0 12 0.0000 4 135 1920 3675 1875 frontend control interface\001 +4 0 0 50 -1 2 14 0.0000 4 195 1425 1637 2371 wpa_supplicant\001 diff --git a/contrib/wpa_supplicant/driver.h b/contrib/wpa_supplicant/driver.h new file mode 100644 index 000000000000..da28014c9270 --- /dev/null +++ b/contrib/wpa_supplicant/driver.h @@ -0,0 +1,436 @@ +#ifndef DRIVER_H +#define DRIVER_H + +#define WPA_SUPPLICANT_DRIVER_VERSION 2 + +typedef enum { WPA_ALG_NONE, WPA_ALG_WEP, WPA_ALG_TKIP, WPA_ALG_CCMP } wpa_alg; +typedef enum { CIPHER_NONE, CIPHER_WEP40, CIPHER_TKIP, CIPHER_CCMP, + CIPHER_WEP104 } wpa_cipher; +typedef enum { KEY_MGMT_802_1X, KEY_MGMT_PSK, KEY_MGMT_NONE, + KEY_MGMT_802_1X_NO_WPA, KEY_MGMT_WPA_NONE } wpa_key_mgmt; + +#define AUTH_ALG_OPEN_SYSTEM 0x01 +#define AUTH_ALG_SHARED_KEY 0x02 +#define AUTH_ALG_LEAP 0x04 + +#define IEEE80211_MODE_INFRA 0 +#define IEEE80211_MODE_IBSS 1 + +#define SSID_MAX_WPA_IE_LEN 40 +struct wpa_scan_result { + u8 bssid[ETH_ALEN]; + u8 ssid[32]; + size_t ssid_len; + u8 wpa_ie[SSID_MAX_WPA_IE_LEN]; + size_t wpa_ie_len; + u8 rsn_ie[SSID_MAX_WPA_IE_LEN]; + size_t rsn_ie_len; + int freq; /* MHz */ + int caps; /* e.g. privacy */ + int qual; /* signal quality */ + int noise; + int level; + int maxrate; +}; + +/* Parameters for associate driver_ops. */ +struct wpa_driver_associate_params { + /* BSSID of the selected AP */ + const u8 *bssid; + + /* The selected SSID and its length in bytes */ + const u8 *ssid; + size_t ssid_len; + + /* frequency that the selected AP is using (in MHz as + * reported in the scan results) */ + int freq; + + /* WPA information element to be included in (Re)Association + * Request (including information element id and length). Use + * of this WPA IE is optional. If the driver generates the WPA + * IE, it can use @pairwise_suite, @group_suite, and + * @key_mgmt_suite to select proper algorithms. In this case, + * the driver has to notify wpa_supplicant about the used WPA + * IE by generating an event that the interface code will + * convert into EVENT_ASSOCINFO data (see wpa_supplicant.h). + * When using WPA2/IEEE 802.11i, @wpa_ie is used for RSN IE + * instead. The driver can determine which version is used by + * looking at the first byte of the IE (0xdd for WPA, 0x30 for + * WPA2/RSN). @wpa_ie_len: length of the @wpa_ie */ + const u8 *wpa_ie; + size_t wpa_ie_len; + + /* The selected pairwise/group cipher and key management + * suites. These are usually ignored if @wpa_ie is used. */ + wpa_cipher pairwise_suite; + wpa_cipher group_suite; + wpa_key_mgmt key_mgmt_suite; + + int auth_alg; /* bit field of AUTH_ALG_* */ + int mode; /* IEEE80211_MODE_* */ +}; + +struct wpa_driver_capa { +#define WPA_DRIVER_CAPA_KEY_MGMT_WPA 0x00000001 +#define WPA_DRIVER_CAPA_KEY_MGMT_WPA2 0x00000002 +#define WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK 0x00000004 +#define WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK 0x00000008 +#define WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE 0x00000010 + unsigned int key_mgmt; + +#define WPA_DRIVER_CAPA_ENC_WEP40 0x00000001 +#define WPA_DRIVER_CAPA_ENC_WEP104 0x00000002 +#define WPA_DRIVER_CAPA_ENC_TKIP 0x00000004 +#define WPA_DRIVER_CAPA_ENC_CCMP 0x00000008 + unsigned int enc; + +#define WPA_DRIVER_AUTH_OPEN 0x00000001 +#define WPA_DRIVER_AUTH_SHARED 0x00000002 +#define WPA_DRIVER_AUTH_LEAP 0x00000004 + unsigned int auth; + +/* Driver generated WPA/RSN IE */ +#define WPA_DRIVER_FLAGS_DRIVER_IE 0x00000001 +#define WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC 0x00000002 + unsigned int flags; +}; + + +struct wpa_driver_ops { + /* name of the driver interface */ + const char *name; + /* one line description of the driver interface */ + const char *desc; + + /** + * get_bssid - get the current BSSID + * @priv: private driver interface data + * @bssid: buffer for BSSID (ETH_ALEN = 6 bytes) + * + * Returns: 0 on success, -1 on failure + * + * Query kernel driver for the current BSSID and copy it to @bssid. + * Setting @bssid to 00:00:00:00:00:00 is recommended if the STA is not + * associated. + */ + int (*get_bssid)(void *priv, u8 *bssid); + + /** + * get_ssid - get the current SSID + * @priv: private driver interface data + * @ssid: buffer for SSID (at least 32 bytes) + * + * Returns: length of the SSID on success, -1 on failure + * + * Query kernel driver for the current SSID and copy it to @ssid. + * Returning zero is recommended if the STA is not associated. + * + * Note: SSID is an array of octets, i.e., it is not nul terminated and + * can, at least in theory, contain control characters (including nul) + * and as such, should be processed as binary data, not a printable + * string. + */ + int (*get_ssid)(void *priv, u8 *ssid); + + /** + * set_wpa - enable/disable WPA support + * @priv: private driver interface data + * @enabled: 1 = enable, 0 = disable + * + * Returns: 0 on success, -1 on failure + * + * Configure the kernel driver to enable/disable WPA support. This may + * be empty function, if WPA support is always enabled. Common + * configuration items are WPA IE (clearing it when WPA support is + * disabled), Privacy flag for capability field, roaming mode (need to + * allow wpa_supplicant to control roaming). + */ + int (*set_wpa)(void *priv, int enabled); + + /** + * set_key - configure encryption key + * @priv: private driver interface data + * @alg: encryption algorithm (%WPA_ALG_NONE, %WPA_ALG_WEP, + * %WPA_ALG_TKIP, %WPA_ALG_CCMP); %WPA_ALG_NONE clears the key. + * @addr: address of the peer STA or ff:ff:ff:ff:ff:ff for + * broadcast/default keys + * @key_idx: key index (0..3), always 0 for unicast keys + * @set_tx: configure this key as the default Tx key (only used when + * driver does not support separate unicast/individual key + * @seq: sequence number/packet number, @seq_len octets, the next + * packet number to be used for in replay protection; configured + * for Rx keys (in most cases, this is only used with broadcast + * keys and set to zero for unicast keys) + * @seq_len: length of the @seq, depends on the algorithm: + * TKIP: 6 octets, CCMP: 6 octets + * @key: key buffer; TKIP: 16-byte temporal key, 8-byte Tx Mic key, + * 8-byte Rx Mic Key + * @key_len: length of the key buffer in octets (WEP: 5 or 13, + * TKIP: 32, CCMP: 16) + * + * Returns: 0 on success, -1 on failure + * + * Configure the given key for the kernel driver. If the driver + * supports separate individual keys (4 default keys + 1 individual), + * @addr can be used to determine whether the key is default or + * individual. If only 4 keys are supported, the default key with key + * index 0 is used as the individual key. STA must be configured to use + * it as the default Tx key (@set_tx is set) and accept Rx for all the + * key indexes. In most cases, WPA uses only key indexes 1 and 2 for + * broadcast keys, so key index 0 is available for this kind of + * configuration. + */ + int (*set_key)(void *priv, wpa_alg alg, const u8 *addr, + int key_idx, int set_tx, const u8 *seq, size_t seq_len, + const u8 *key, size_t key_len); + + /** + * init - initialize driver interface + * @ctx: context to be used when calling wpa_supplicant functions, + * e.g., wpa_supplicant_event() + * @ifname: interface name, e.g., wlan0 + * + * Return: pointer to private data, %NULL on failure + * + * Initialize driver interface, including event processing for kernel + * driver events (e.g., associated, scan results, Michael MIC failure). + * This function can allocate a private configuration data area for + * @ctx, file descriptor, interface name, etc. information that may be + * needed in future driver operations. If this is not used, non-NULL + * value will need to be returned because %NULL is used to indicate + * failure. The returned value will be used as 'void *priv' data for + * all other driver_ops functions. + * + * The main event loop (eloop.c) of wpa_supplicant can be used to + * register callback for read sockets (eloop_register_read_sock()). + * + * See wpa_supplicant.h for more information about events and + * wpa_supplicant_event() function. + */ + void * (*init)(void *ctx, const char *ifname); + + /** + * deinit - deinitialize driver interface + * @priv: pointer to private data (from matching + * wpa_driver_events_init()) + * + * Shut down driver interface and processing of driver events. Free + * private data buffer if one was allocated in init() handler. + */ + void (*deinit)(void *priv); + + /** + * set_countermeasures - enable/disable TKIP countermeasures + * @priv: private driver interface data + * @enabled: 1 = countermeasures enabled, 0 = disabled + * + * Return: 0 on success, -1 on failure + * + * Configure TKIP countermeasures. When these are enabled, the driver + * should drop all received and queued frames that are using TKIP. + */ + int (*set_countermeasures)(void *priv, int enabled); + + /** + * set_drop_unencrypted - enable/disable unencrypted frame filtering + * @priv: private driver interface data + * @enabled: 1 = unencrypted Tx/Rx frames will be dropped, 0 = disabled + * + * Return: 0 on success, -1 on failure + * + * Configure the driver to drop all non-EAPOL frames (both receive and + * transmit paths). Unencrypted EAPOL frames (ethertype 0x888e) must + * still be allowed for key negotiation. + */ + int (*set_drop_unencrypted)(void *priv, int enabled); + + /** + * scan - request the driver to initiate scan + * @priv: private driver interface data + * @ssid: specific SSID to scan for (ProbeReq) or %NULL to scan for + * all SSIDs (either active scan with broadcast SSID or passive + * scan + * @ssid_len: length of the SSID + * + * Return: 0 on success, -1 on failure + * + * Once the scan results are ready, the driver should report scan + * results event for wpa_supplicant which will eventually request the + * results with wpa_driver_get_scan_results(). + */ + int (*scan)(void *priv, const u8 *ssid, size_t ssid_len); + + /** + * get_scan_results - fetch the latest scan results + * @priv: private driver interface data + * @results: pointer to buffer for scan results + * @max_size: maximum number of entries (buffer size) + * + * Return: number of scan result entries used on success, -1 on failure + * + * If scan results include more than @max_size BSSes, @max_size will be + * returned and the remaining entries will not be included in the + * buffer. + */ + int (*get_scan_results)(void *priv, + struct wpa_scan_result *results, + size_t max_size); + + /** + * deauthenticate - request driver to deauthenticate + * @priv: private driver interface data + * @addr: peer address (BSSID of the AP) + * @reason_code: 16-bit reason code to be sent in the deauthentication + * frame + * + * Return: 0 on success, -1 on failure + */ + int (*deauthenticate)(void *priv, const u8 *addr, int reason_code); + + /** + * disassociate - request driver to disassociate + * @priv: private driver interface data + * @addr: peer address (BSSID of the AP) + * @reason_code: 16-bit reason code to be sent in the disassociation + * frame + * + * Return: 0 on success, -1 on failure + */ + int (*disassociate)(void *priv, const u8 *addr, int reason_code); + + /** + * associate - request driver to associate + * @priv: private driver interface data + * @params: association parameters + * + * Return: 0 on success, -1 on failure + */ + int (*associate)(void *priv, + struct wpa_driver_associate_params *params); + + /** + * set_auth_alg - set IEEE 802.11 authentication algorithm + * @priv: private driver interface data + * @auth_alg: bit field of AUTH_ALG_* + * + * If the driver supports more than one authentication algorithm at the + * same time, it should configure all supported algorithms. If not, one + * algorithm needs to be selected arbitrarily. Open System + * authentication should be ok for most cases and it is recommended to + * be used if other options are not supported. Static WEP configuration + * may also use Shared Key authentication and LEAP requires its own + * algorithm number. For LEAP, user can make sure that only one + * algorithm is used at a time by configuring LEAP as the only + * supported EAP method. This information is also available in + * associate() params, so set_auth_alg may not be needed in case of + * most drivers. + * + * Return: 0 on success, -1 on failure + */ + int (*set_auth_alg)(void *priv, int auth_alg); + + /** + * add_pmkid - add PMKSA cache entry to the driver + * @priv: private driver interface data + * @bssid: BSSID for the PMKSA cache entry + * @pmkid: PMKID for the PMKSA cache entry + * + * Return: 0 on success, -1 on failure + * + * This function is called when a new PMK is received, as a result of + * either normal authentication or RSN pre-authentication. + * + * If the driver generates RSN IE, i.e., it does not use @wpa_ie in + * associate(), add_pmkid() can be used to add new PMKSA cache entries + * in the driver. If the driver uses @wpa_ie from wpa_supplicant, this + * driver_ops function does not need to be implemented. Likewise, if + * the driver does not support WPA, this function is not needed. + */ + int (*add_pmkid)(void *priv, const u8 *bssid, const u8 *pmkid); + + /** + * remove_pmkid - remove PMKSA cache entry to the driver + * @priv: private driver interface data + * @bssid: BSSID for the PMKSA cache entry + * @pmkid: PMKID for the PMKSA cache entry + * + * Return: 0 on success, -1 on failure + * + * This function is called when the supplicant drops a PMKSA cache + * entry for any reason. + * + * If the driver generates RSN IE, i.e., it does not use @wpa_ie in + * associate(), remove_pmkid() can be used to synchronize PMKSA caches + * between the driver and wpa_supplicant. If the driver uses @wpa_ie + * from wpa_supplicant, this driver_ops function does not need to be + * implemented. Likewise, if the driver does not support WPA, this + * function is not needed. + */ + int (*remove_pmkid)(void *priv, const u8 *bssid, const u8 *pmkid); + + /** + * flush_pmkid - flush PMKSA cache + * @priv: private driver interface data + * + * Return: 0 on success, -1 on failure + * + * This function is called when the supplicant drops all PMKSA cache + * entries for any reason. + * + * If the driver generates RSN IE, i.e., it does not use @wpa_ie in + * associate(), remove_pmkid() can be used to synchronize PMKSA caches + * between the driver and wpa_supplicant. If the driver uses @wpa_ie + * from wpa_supplicant, this driver_ops function does not need to be + * implemented. Likewise, if the driver does not support WPA, this + * function is not needed. + */ + int (*flush_pmkid)(void *priv); + + /** + * flush_pmkid - flush PMKSA cache + * @priv: private driver interface data + * + * Return: 0 on success, -1 on failure + * + * Get driver/firmware/hardware capabilities. + */ + int (*get_capa)(void *priv, struct wpa_driver_capa *capa); + + /** + * poll - poll driver for association information + * @priv: private driver interface data + * + * This is an option callback that can be used when the driver does not + * provide event mechanism for association events. This is called when + * receiving WPA EAPOL-Key messages that require association + * information. The driver interface is supposed to generate associnfo + * event before returning from this callback function. In addition, the + * driver interface should generate an association event after having + * sent out associnfo. + */ + void (*poll)(void *priv); + + /** + * get_ifname - get interface name + * + * This optional function can be used to allow the driver interface to + * replace the interface name with something else, e.g., based on an + * interface mapping from a more descriptive name. + * + * Returns a pointer to the interface name. This can differ from the + * interface name used in init() call. + */ + const char * (*get_ifname)(void *priv); + + /** + * get_mac_addr - get own MAC address + * + * This optional function can be used to get the own MAC address of the + * device from the driver interface code. This is only needed if the + * l2_packet implementation for the OS does not provide easy access to + * a MAC address. */ + const u8 * (*get_mac_addr)(void *priv); +}; + +#endif /* DRIVER_H */ diff --git a/contrib/wpa_supplicant/drivers.c b/contrib/wpa_supplicant/drivers.c new file mode 100644 index 000000000000..cb016ba803e5 --- /dev/null +++ b/contrib/wpa_supplicant/drivers.c @@ -0,0 +1,96 @@ +/* + * WPA Supplicant / driver interface list + * Copyright (c) 2004, Jouni Malinen <jkmaline@cc.hut.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include <stdlib.h> + + +#ifdef CONFIG_DRIVER_HOSTAP +extern struct wpa_driver_ops wpa_driver_hostap_ops; /* driver_hostap.c */ +#endif /* CONFIG_DRIVER_HOSTAP */ +#ifdef CONFIG_DRIVER_PRISM54 +extern struct wpa_driver_ops wpa_driver_prism54_ops; /* driver_prism54.c */ +#endif /* CONFIG_DRIVER_PRISM54 */ +#ifdef CONFIG_DRIVER_HERMES +extern struct wpa_driver_ops wpa_driver_hermes_ops; /* driver_hermes.c */ +#endif /* CONFIG_DRIVER_HERMES */ +#ifdef CONFIG_DRIVER_MADWIFI +extern struct wpa_driver_ops wpa_driver_madwifi_ops; /* driver_madwifi.c */ +#endif /* CONFIG_DRIVER_MADWIFI */ +#ifdef CONFIG_DRIVER_ATMEL +extern struct wpa_driver_ops wpa_driver_atmel_ops; /* driver_atmel.c */ +#endif /* CONFIG_DRIVER_ATMEL */ +#ifdef CONFIG_DRIVER_WEXT +extern struct wpa_driver_ops wpa_driver_wext_ops; /* driver_wext.c */ +#endif /* CONFIG_DRIVER_WEXT */ +#ifdef CONFIG_DRIVER_NDISWRAPPER +/* driver_ndiswrapper.c */ +extern struct wpa_driver_ops wpa_driver_ndiswrapper_ops; +#endif /* CONFIG_DRIVER_NDISWRAPPER */ +#ifdef CONFIG_DRIVER_BROADCOM +extern struct wpa_driver_ops wpa_driver_broadcom_ops; /* driver_broadcom.c */ +#endif /* CONFIG_DRIVER_BROADCOM */ +#ifdef CONFIG_DRIVER_IPW +extern struct wpa_driver_ops wpa_driver_ipw_ops; /* driver_ipw.c */ +#endif /* CONFIG_DRIVER_IPW */ +#ifdef CONFIG_DRIVER_BSD +extern struct wpa_driver_ops wpa_driver_bsd_ops; /* driver_bsd.c */ +#endif /* CONFIG_DRIVER_BSD */ +#ifdef CONFIG_DRIVER_NDIS +extern struct wpa_driver_ops wpa_driver_ndis_ops; /* driver_ndis.c */ +#endif /* CONFIG_DRIVER_NDIS */ +#ifdef CONFIG_DRIVER_TEST +extern struct wpa_driver_ops wpa_driver_test_ops; /* driver_test.c */ +#endif /* CONFIG_DRIVER_TEST */ + + +struct wpa_driver_ops *wpa_supplicant_drivers[] = +{ +#ifdef CONFIG_DRIVER_HOSTAP + &wpa_driver_hostap_ops, +#endif /* CONFIG_DRIVER_HOSTAP */ +#ifdef CONFIG_DRIVER_PRISM54 + &wpa_driver_prism54_ops, +#endif /* CONFIG_DRIVER_PRISM54 */ +#ifdef CONFIG_DRIVER_HERMES + &wpa_driver_hermes_ops, +#endif /* CONFIG_DRIVER_HERMES */ +#ifdef CONFIG_DRIVER_MADWIFI + &wpa_driver_madwifi_ops, +#endif /* CONFIG_DRIVER_MADWIFI */ +#ifdef CONFIG_DRIVER_ATMEL + &wpa_driver_atmel_ops, +#endif /* CONFIG_DRIVER_ATMEL */ +#ifdef CONFIG_DRIVER_WEXT + &wpa_driver_wext_ops, +#endif /* CONFIG_DRIVER_WEXT */ +#ifdef CONFIG_DRIVER_NDISWRAPPER + &wpa_driver_ndiswrapper_ops, +#endif /* CONFIG_DRIVER_NDISWRAPPER */ +#ifdef CONFIG_DRIVER_BROADCOM + &wpa_driver_broadcom_ops, +#endif /* CONFIG_DRIVER_BROADCOM */ +#ifdef CONFIG_DRIVER_IPW + &wpa_driver_ipw_ops, +#endif /* CONFIG_DRIVER_IPW */ +#ifdef CONFIG_DRIVER_BSD + &wpa_driver_bsd_ops, +#endif /* CONFIG_DRIVER_BSD */ +#ifdef CONFIG_DRIVER_NDIS + &wpa_driver_ndis_ops, +#endif /* CONFIG_DRIVER_NDIS */ +#ifdef CONFIG_DRIVER_TEST + &wpa_driver_test_ops, +#endif /* CONFIG_DRIVER_TEST */ + NULL +}; diff --git a/contrib/wpa_supplicant/eap.c b/contrib/wpa_supplicant/eap.c new file mode 100644 index 000000000000..a76b942464e8 --- /dev/null +++ b/contrib/wpa_supplicant/eap.c @@ -0,0 +1,1243 @@ +/* + * WPA Supplicant / EAP state machines + * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include "common.h" +#include "eap_i.h" +#include "wpa_supplicant.h" +#include "config_ssid.h" +#include "tls.h" +#include "md5.h" + + +#define EAP_MAX_AUTH_ROUNDS 50 + + +#ifdef EAP_MD5 +extern const struct eap_method eap_method_md5; +#endif +#ifdef EAP_TLS +extern const struct eap_method eap_method_tls; +#endif +#ifdef EAP_MSCHAPv2 +extern const struct eap_method eap_method_mschapv2; +#endif +#ifdef EAP_PEAP +extern const struct eap_method eap_method_peap; +#endif +#ifdef EAP_TTLS +extern const struct eap_method eap_method_ttls; +#endif +#ifdef EAP_GTC +extern const struct eap_method eap_method_gtc; +#endif +#ifdef EAP_OTP +extern const struct eap_method eap_method_otp; +#endif +#ifdef EAP_SIM +extern const struct eap_method eap_method_sim; +#endif +#ifdef EAP_LEAP +extern const struct eap_method eap_method_leap; +#endif +#ifdef EAP_PSK +extern const struct eap_method eap_method_psk; +#endif +#ifdef EAP_AKA +extern const struct eap_method eap_method_aka; +#endif +#ifdef EAP_FAST +extern const struct eap_method eap_method_fast; +#endif + +static const struct eap_method *eap_methods[] = +{ +#ifdef EAP_MD5 + &eap_method_md5, +#endif +#ifdef EAP_TLS + &eap_method_tls, +#endif +#ifdef EAP_MSCHAPv2 + &eap_method_mschapv2, +#endif +#ifdef EAP_PEAP + &eap_method_peap, +#endif +#ifdef EAP_TTLS + &eap_method_ttls, +#endif +#ifdef EAP_GTC + &eap_method_gtc, +#endif +#ifdef EAP_OTP + &eap_method_otp, +#endif +#ifdef EAP_SIM + &eap_method_sim, +#endif +#ifdef EAP_LEAP + &eap_method_leap, +#endif +#ifdef EAP_PSK + &eap_method_psk, +#endif +#ifdef EAP_AKA + &eap_method_aka, +#endif +#ifdef EAP_FAST + &eap_method_fast, +#endif +}; +#define NUM_EAP_METHODS (sizeof(eap_methods) / sizeof(eap_methods[0])) + + +const struct eap_method * eap_sm_get_eap_methods(int method) +{ + int i; + for (i = 0; i < NUM_EAP_METHODS; i++) { + if (eap_methods[i]->method == method) + return eap_methods[i]; + } + return NULL; +} + + +static Boolean eap_sm_allowMethod(struct eap_sm *sm, EapType method); +static u8 * eap_sm_buildNak(struct eap_sm *sm, int id, size_t *len); +static void eap_sm_processIdentity(struct eap_sm *sm, u8 *req, size_t len); +static void eap_sm_processNotify(struct eap_sm *sm, u8 *req, size_t len); +static u8 * eap_sm_buildNotify(struct eap_sm *sm, int id, size_t *len); +static void eap_sm_parseEapReq(struct eap_sm *sm, u8 *req, size_t len); +static const char * eap_sm_method_state_txt(int state); +static const char * eap_sm_decision_txt(int decision); + + +/* Definitions for clarifying state machine implementation */ +#define SM_STATE(machine, state) \ +static void sm_ ## machine ## _ ## state ## _Enter(struct eap_sm *sm, \ + int global) + +#define SM_ENTRY(machine, state) \ +if (!global || sm->machine ## _state != machine ## _ ## state) { \ + sm->changed = TRUE; \ + wpa_printf(MSG_DEBUG, "EAP: " #machine " entering state " #state); \ +} \ +sm->machine ## _state = machine ## _ ## state; + +#define SM_ENTER(machine, state) \ +sm_ ## machine ## _ ## state ## _Enter(sm, 0) +#define SM_ENTER_GLOBAL(machine, state) \ +sm_ ## machine ## _ ## state ## _Enter(sm, 1) + +#define SM_STEP(machine) \ +static void sm_ ## machine ## _Step(struct eap_sm *sm) + +#define SM_STEP_RUN(machine) sm_ ## machine ## _Step(sm) + + +static Boolean eapol_get_bool(struct eap_sm *sm, enum eapol_bool_var var) +{ + return sm->eapol_cb->get_bool(sm->eapol_ctx, var); +} + + +static void eapol_set_bool(struct eap_sm *sm, enum eapol_bool_var var, + Boolean value) +{ + sm->eapol_cb->set_bool(sm->eapol_ctx, var, value); +} + + +static unsigned int eapol_get_int(struct eap_sm *sm, enum eapol_int_var var) +{ + return sm->eapol_cb->get_int(sm->eapol_ctx, var); +} + + +static void eapol_set_int(struct eap_sm *sm, enum eapol_int_var var, + unsigned int value) +{ + sm->eapol_cb->set_int(sm->eapol_ctx, var, value); +} + + +static u8 * eapol_get_eapReqData(struct eap_sm *sm, size_t *len) +{ + return sm->eapol_cb->get_eapReqData(sm->eapol_ctx, len); +} + + +static void eap_deinit_prev_method(struct eap_sm *sm, const char *txt) +{ + if (sm->m == NULL || sm->eap_method_priv == NULL) + return; + + wpa_printf(MSG_DEBUG, "EAP: deinitialize previously used EAP method " + "(%d, %s) at %s", sm->selectedMethod, sm->m->name, txt); + sm->m->deinit(sm, sm->eap_method_priv); + sm->eap_method_priv = NULL; + sm->m = NULL; +} + + +SM_STATE(EAP, INITIALIZE) +{ + SM_ENTRY(EAP, INITIALIZE); + if (sm->fast_reauth && sm->m && sm->m->has_reauth_data && + sm->m->has_reauth_data(sm, sm->eap_method_priv)) { + wpa_printf(MSG_DEBUG, "EAP: maintaining EAP method data for " + "fast reauthentication"); + sm->m->deinit_for_reauth(sm, sm->eap_method_priv); + } else { + eap_deinit_prev_method(sm, "INITIALIZE"); + } + sm->selectedMethod = EAP_TYPE_NONE; + sm->methodState = METHOD_NONE; + sm->allowNotifications = TRUE; + sm->decision = DECISION_FAIL; + eapol_set_int(sm, EAPOL_idleWhile, sm->ClientTimeout); + eapol_set_bool(sm, EAPOL_eapSuccess, FALSE); + eapol_set_bool(sm, EAPOL_eapFail, FALSE); + free(sm->eapKeyData); + sm->eapKeyData = NULL; + sm->eapKeyAvailable = FALSE; + eapol_set_bool(sm, EAPOL_eapRestart, FALSE); + sm->lastId = -1; /* new session - make sure this does not match with + * the first EAP-Packet */ + /* draft-ietf-eap-statemachine-02.pdf does not reset eapResp and + * eapNoResp here. However, this seemed to be able to trigger cases + * where both were set and if EAPOL state machine uses eapNoResp first, + * it may end up not sending a real reply correctly. This occurred + * when the workaround in FAIL state set eapNoResp = TRUE.. Maybe that + * workaround needs to be fixed to do something else(?) */ + eapol_set_bool(sm, EAPOL_eapResp, FALSE); + eapol_set_bool(sm, EAPOL_eapNoResp, FALSE); + sm->num_rounds = 0; +} + + +SM_STATE(EAP, DISABLED) +{ + SM_ENTRY(EAP, DISABLED); + sm->num_rounds = 0; +} + + +SM_STATE(EAP, IDLE) +{ + SM_ENTRY(EAP, IDLE); +} + + +SM_STATE(EAP, RECEIVED) +{ + u8 *eapReqData; + size_t eapReqDataLen; + + SM_ENTRY(EAP, RECEIVED); + eapReqData = eapol_get_eapReqData(sm, &eapReqDataLen); + /* parse rxReq, rxSuccess, rxFailure, reqId, reqMethod */ + eap_sm_parseEapReq(sm, eapReqData, eapReqDataLen); + sm->num_rounds++; +} + + +SM_STATE(EAP, GET_METHOD) +{ + SM_ENTRY(EAP, GET_METHOD); + if (eap_sm_allowMethod(sm, sm->reqMethod)) { + int reinit = 0; + if (sm->fast_reauth && + sm->m && sm->m->method == sm->reqMethod && + sm->m->has_reauth_data && + sm->m->has_reauth_data(sm, sm->eap_method_priv)) { + wpa_printf(MSG_DEBUG, "EAP: using previous method data" + " for fast re-authentication"); + reinit = 1; + } else + eap_deinit_prev_method(sm, "GET_METHOD"); + sm->selectedMethod = sm->reqMethod; + if (sm->m == NULL) + sm->m = eap_sm_get_eap_methods(sm->selectedMethod); + if (sm->m) { + wpa_printf(MSG_DEBUG, "EAP: initialize selected EAP " + "method (%d, %s)", + sm->selectedMethod, sm->m->name); + if (reinit) + sm->eap_method_priv = sm->m->init_for_reauth( + sm, sm->eap_method_priv); + else + sm->eap_method_priv = sm->m->init(sm); + if (sm->eap_method_priv == NULL) { + wpa_printf(MSG_DEBUG, "EAP: Failed to " + "initialize EAP method %d", + sm->selectedMethod); + sm->m = NULL; + sm->methodState = METHOD_NONE; + sm->selectedMethod = EAP_TYPE_NONE; + } else { + sm->methodState = METHOD_INIT; + return; + } + } + } + + free(sm->eapRespData); + sm->eapRespData = eap_sm_buildNak(sm, sm->reqId, &sm->eapRespDataLen); +} + + +SM_STATE(EAP, METHOD) +{ + u8 *eapReqData; + size_t eapReqDataLen; + struct eap_method_ret ret; + + SM_ENTRY(EAP, METHOD); + if (sm->m == NULL) { + wpa_printf(MSG_WARNING, "EAP::METHOD - method not selected"); + return; + } + + eapReqData = eapol_get_eapReqData(sm, &eapReqDataLen); + + /* Get ignore, methodState, decision, allowNotifications, and + * eapRespData. */ + memset(&ret, 0, sizeof(ret)); + ret.ignore = sm->ignore; + ret.methodState = sm->methodState; + ret.decision = sm->decision; + ret.allowNotifications = sm->allowNotifications; + free(sm->eapRespData); + sm->eapRespData = sm->m->process(sm, sm->eap_method_priv, &ret, + eapReqData, eapReqDataLen, + &sm->eapRespDataLen); + wpa_printf(MSG_DEBUG, "EAP: method process -> ignore=%s " + "methodState=%s decision=%s", + ret.ignore ? "TRUE" : "FALSE", + eap_sm_method_state_txt(ret.methodState), + eap_sm_decision_txt(ret.decision)); + + sm->ignore = ret.ignore; + if (sm->ignore) + return; + sm->methodState = ret.methodState; + sm->decision = ret.decision; + sm->allowNotifications = ret.allowNotifications; + + if (sm->m->isKeyAvailable && sm->m->getKey && + sm->m->isKeyAvailable(sm, sm->eap_method_priv)) { + free(sm->eapKeyData); + sm->eapKeyData = sm->m->getKey(sm, sm->eap_method_priv, + &sm->eapKeyDataLen); + } +} + + +SM_STATE(EAP, SEND_RESPONSE) +{ + SM_ENTRY(EAP, SEND_RESPONSE); + free(sm->lastRespData); + if (sm->eapRespData) { + if (sm->workaround) + memcpy(sm->last_md5, sm->req_md5, 16); + sm->lastId = sm->reqId; + sm->lastRespData = malloc(sm->eapRespDataLen); + if (sm->lastRespData) { + memcpy(sm->lastRespData, sm->eapRespData, + sm->eapRespDataLen); + sm->lastRespDataLen = sm->eapRespDataLen; + } + eapol_set_bool(sm, EAPOL_eapResp, TRUE); + } else + sm->lastRespData = NULL; + eapol_set_bool(sm, EAPOL_eapReq, FALSE); + eapol_set_int(sm, EAPOL_idleWhile, sm->ClientTimeout); +} + + +SM_STATE(EAP, DISCARD) +{ + SM_ENTRY(EAP, DISCARD); + eapol_set_bool(sm, EAPOL_eapReq, FALSE); + eapol_set_bool(sm, EAPOL_eapNoResp, TRUE); +} + + +SM_STATE(EAP, IDENTITY) +{ + u8 *eapReqData; + size_t eapReqDataLen; + + SM_ENTRY(EAP, IDENTITY); + eapReqData = eapol_get_eapReqData(sm, &eapReqDataLen); + eap_sm_processIdentity(sm, eapReqData, eapReqDataLen); + free(sm->eapRespData); + sm->eapRespData = eap_sm_buildIdentity(sm, sm->reqId, + &sm->eapRespDataLen, 0); +} + + +SM_STATE(EAP, NOTIFICATION) +{ + u8 *eapReqData; + size_t eapReqDataLen; + + SM_ENTRY(EAP, NOTIFICATION); + eapReqData = eapol_get_eapReqData(sm, &eapReqDataLen); + eap_sm_processNotify(sm, eapReqData, eapReqDataLen); + free(sm->eapRespData); + sm->eapRespData = eap_sm_buildNotify(sm, sm->reqId, + &sm->eapRespDataLen); +} + + +SM_STATE(EAP, RETRANSMIT) +{ + SM_ENTRY(EAP, RETRANSMIT); + free(sm->eapRespData); + if (sm->lastRespData) { + sm->eapRespData = malloc(sm->lastRespDataLen); + if (sm->eapRespData) { + memcpy(sm->eapRespData, sm->lastRespData, + sm->lastRespDataLen); + sm->eapRespDataLen = sm->lastRespDataLen; + } + } else + sm->eapRespData = NULL; +} + + +SM_STATE(EAP, SUCCESS) +{ + SM_ENTRY(EAP, SUCCESS); + if (sm->eapKeyData != NULL) + sm->eapKeyAvailable = TRUE; + eapol_set_bool(sm, EAPOL_eapSuccess, TRUE); + /* draft-ietf-eap-statemachine-02.pdf does not clear eapReq here, but + * this seems to be required to avoid processing the same request + * twice when state machine is initialized. */ + eapol_set_bool(sm, EAPOL_eapReq, FALSE); + /* draft-ietf-eap-statemachine-02.pdf does not set eapNoResp here, but + * this seems to be required to get EAPOL Supplicant backend state + * machine into SUCCESS state. In addition, either eapResp or eapNoResp + * is required to be set after processing the received EAP frame. */ + eapol_set_bool(sm, EAPOL_eapNoResp, TRUE); +} + + +SM_STATE(EAP, FAILURE) +{ + SM_ENTRY(EAP, FAILURE); + eapol_set_bool(sm, EAPOL_eapFail, TRUE); + /* draft-ietf-eap-statemachine-02.pdf does not clear eapReq here, but + * this seems to be required to avoid processing the same request + * twice when state machine is initialized. */ + eapol_set_bool(sm, EAPOL_eapReq, FALSE); + /* draft-ietf-eap-statemachine-02.pdf does not set eapNoResp here. + * However, either eapResp or eapNoResp is required to be set after + * processing the received EAP frame. */ + eapol_set_bool(sm, EAPOL_eapNoResp, TRUE); +} + + +static int eap_success_workaround(struct eap_sm *sm, int reqId, int lastId) +{ + /* At least Microsoft IAS and Meetinghouse Aegis seem to be sending + * EAP-Success/Failure with lastId + 1 even though RFC 3748 and + * draft-ietf-eap-statemachine-05.pdf require that reqId == lastId. + * Accept this kind of Id if EAP workarounds are enabled. These are + * unauthenticated plaintext messages, so this should have minimal + * security implications (bit easier to fake EAP-Success/Failure). */ + if (sm->workaround && reqId == ((lastId + 1) & 0xff)) { + wpa_printf(MSG_DEBUG, "EAP: Workaround for unexpected " + "identifier field in EAP Success: " + "reqId=%d lastId=%d (these are supposed to be " + "same)", reqId, lastId); + return 1; + } + return 0; +} + + +SM_STEP(EAP) +{ + int duplicate; + + if (eapol_get_bool(sm, EAPOL_eapRestart) && + eapol_get_bool(sm, EAPOL_portEnabled)) + SM_ENTER_GLOBAL(EAP, INITIALIZE); + else if (!eapol_get_bool(sm, EAPOL_portEnabled)) + SM_ENTER_GLOBAL(EAP, DISABLED); + else if (sm->num_rounds > EAP_MAX_AUTH_ROUNDS) { + if (sm->num_rounds == EAP_MAX_AUTH_ROUNDS + 1) { + wpa_printf(MSG_DEBUG, "EAP: more than %d " + "authentication rounds - abort", + EAP_MAX_AUTH_ROUNDS); + sm->num_rounds++; + SM_ENTER_GLOBAL(EAP, FAILURE); + } + } else switch (sm->EAP_state) { + case EAP_INITIALIZE: + SM_ENTER(EAP, IDLE); + break; + case EAP_DISABLED: + if (eapol_get_bool(sm, EAPOL_portEnabled)) + SM_ENTER(EAP, INITIALIZE); + break; + case EAP_IDLE: + if (eapol_get_bool(sm, EAPOL_eapReq)) + SM_ENTER(EAP, RECEIVED); + else if ((eapol_get_bool(sm, EAPOL_altAccept) && + sm->decision != DECISION_FAIL) || + (eapol_get_int(sm, EAPOL_idleWhile) == 0 && + sm->decision == DECISION_UNCOND_SUCC)) + SM_ENTER(EAP, SUCCESS); + else if (eapol_get_bool(sm, EAPOL_altReject) || + (eapol_get_int(sm, EAPOL_idleWhile) == 0 && + sm->decision != DECISION_UNCOND_SUCC) || + (eapol_get_bool(sm, EAPOL_altAccept) && + sm->methodState != METHOD_CONT && + sm->decision == DECISION_FAIL)) + SM_ENTER(EAP, FAILURE); + else if (sm->selectedMethod == EAP_TYPE_LEAP && + sm->leap_done && sm->decision != DECISION_FAIL && + sm->methodState == METHOD_DONE) + SM_ENTER(EAP, SUCCESS); + else if (sm->selectedMethod == EAP_TYPE_PEAP && + sm->peap_done && sm->decision != DECISION_FAIL && + sm->methodState == METHOD_DONE) + SM_ENTER(EAP, SUCCESS); + break; + case EAP_RECEIVED: + duplicate = sm->reqId == sm->lastId; + if (sm->workaround && duplicate && + memcmp(sm->req_md5, sm->last_md5, 16) != 0) { + /* draft-ietf-eap-statemachine-05.txt uses + * (reqId == lastId) as the only verification for + * duplicate EAP requests. However, this misses cases + * where the AS is incorrectly using the same id again; + * and unfortunately, such implementations exist. Use + * MD5 hash as an extra verification for the packets + * being duplicate to workaround these issues. */ + wpa_printf(MSG_DEBUG, "EAP: AS used the same Id again," + " but EAP packets were not identical"); + wpa_printf(MSG_DEBUG, "EAP: workaround - assume this " + "is not a duplicate packet"); + duplicate = 0; + } + + if (sm->rxSuccess && + (sm->reqId == sm->lastId || + eap_success_workaround(sm, sm->reqId, sm->lastId)) && + sm->decision != DECISION_FAIL) + SM_ENTER(EAP, SUCCESS); + else if (sm->methodState != METHOD_CONT && + ((sm->rxFailure && + sm->decision != DECISION_UNCOND_SUCC) || + (sm->rxSuccess && sm->decision == DECISION_FAIL)) && + (sm->reqId == sm->lastId || + eap_success_workaround(sm, sm->reqId, sm->lastId))) + SM_ENTER(EAP, FAILURE); + else if (sm->rxReq && duplicate) + SM_ENTER(EAP, RETRANSMIT); + else if (sm->rxReq && !duplicate && + sm->reqMethod == EAP_TYPE_NOTIFICATION && + sm->allowNotifications) + SM_ENTER(EAP, NOTIFICATION); + else if (sm->rxReq && !duplicate && + sm->selectedMethod == EAP_TYPE_NONE && + sm->reqMethod == EAP_TYPE_IDENTITY) + SM_ENTER(EAP, IDENTITY); + else if (sm->rxReq && !duplicate && + sm->selectedMethod == EAP_TYPE_NONE && + sm->reqMethod != EAP_TYPE_IDENTITY && + sm->reqMethod != EAP_TYPE_NOTIFICATION) + SM_ENTER(EAP, GET_METHOD); + else if (sm->rxReq && !duplicate && + sm->reqMethod == sm->selectedMethod && + sm->methodState != METHOD_DONE) + SM_ENTER(EAP, METHOD); + else if (sm->selectedMethod == EAP_TYPE_LEAP && + (sm->rxSuccess || sm->rxResp)) + SM_ENTER(EAP, METHOD); + else + SM_ENTER(EAP, DISCARD); + break; + case EAP_GET_METHOD: + if (sm->selectedMethod == sm->reqMethod) + SM_ENTER(EAP, METHOD); + else + SM_ENTER(EAP, SEND_RESPONSE); + break; + case EAP_METHOD: + if (sm->ignore) + SM_ENTER(EAP, DISCARD); + else + SM_ENTER(EAP, SEND_RESPONSE); + break; + case EAP_SEND_RESPONSE: + SM_ENTER(EAP, IDLE); + break; + case EAP_DISCARD: + SM_ENTER(EAP, IDLE); + break; + case EAP_IDENTITY: + SM_ENTER(EAP, SEND_RESPONSE); + break; + case EAP_NOTIFICATION: + SM_ENTER(EAP, SEND_RESPONSE); + break; + case EAP_RETRANSMIT: + SM_ENTER(EAP, SEND_RESPONSE); + break; + case EAP_SUCCESS: + break; + case EAP_FAILURE: + break; + } +} + + +static Boolean eap_sm_allowMethod(struct eap_sm *sm, EapType method) +{ + struct wpa_ssid *config = eap_get_config(sm); + int i; + + if (!wpa_config_allowed_eap_method(config, method)) + return FALSE; + for (i = 0; i < NUM_EAP_METHODS; i++) { + if (eap_methods[i]->method == method) + return TRUE; + } + return FALSE; +} + + +static u8 *eap_sm_buildNak(struct eap_sm *sm, int id, size_t *len) +{ + struct wpa_ssid *config = eap_get_config(sm); + struct eap_hdr *resp; + u8 *pos; + int i, found = 0; + + wpa_printf(MSG_DEBUG, "EAP: Building EAP-Nak (requested type %d not " + "allowed)", sm->reqMethod); + *len = sizeof(struct eap_hdr) + 1; + resp = malloc(*len + NUM_EAP_METHODS); + if (resp == NULL) + return NULL; + + resp->code = EAP_CODE_RESPONSE; + resp->identifier = id; + pos = (u8 *) (resp + 1); + *pos++ = EAP_TYPE_NAK; + + for (i = 0; i < NUM_EAP_METHODS; i++) { + if (wpa_config_allowed_eap_method(config, + eap_methods[i]->method)) { + *pos++ = eap_methods[i]->method; + (*len)++; + found++; + } + } + if (!found) { + *pos = EAP_TYPE_NONE; + (*len)++; + } + wpa_hexdump(MSG_DEBUG, "EAP: allowed methods", + ((u8 *) (resp + 1)) + 1, found); + + resp->length = host_to_be16(*len); + + return (u8 *) resp; +} + + +static void eap_sm_processIdentity(struct eap_sm *sm, u8 *req, size_t len) +{ + struct eap_hdr *hdr = (struct eap_hdr *) req; + u8 *pos = (u8 *) (hdr + 1); + pos++; + /* TODO: could save displayable message so that it can be shown to the + * user in case of interaction is required */ + wpa_hexdump_ascii(MSG_DEBUG, "EAP: EAP-Request Identity data", + pos, be_to_host16(hdr->length) - 5); +} + + +u8 *eap_sm_buildIdentity(struct eap_sm *sm, int id, size_t *len, + int encrypted) +{ + struct wpa_ssid *config = eap_get_config(sm); + struct eap_hdr *resp; + u8 *pos; + const u8 *identity; + size_t identity_len; + + if (config == NULL) { + wpa_printf(MSG_WARNING, "EAP: buildIdentity: configuration " + "was not available"); + return NULL; + } + + if (sm->m && sm->m->get_identity && + (identity = sm->m->get_identity(sm, sm->eap_method_priv, + &identity_len)) != NULL) { + wpa_hexdump_ascii(MSG_DEBUG, "EAP: using method re-auth " + "identity", identity, identity_len); + } else if (!encrypted && config->anonymous_identity) { + identity = config->anonymous_identity; + identity_len = config->anonymous_identity_len; + wpa_hexdump_ascii(MSG_DEBUG, "EAP: using anonymous identity", + identity, identity_len); + } else { + identity = config->identity; + identity_len = config->identity_len; + wpa_hexdump_ascii(MSG_DEBUG, "EAP: using real identity", + identity, identity_len); + } + + if (identity == NULL) { + wpa_printf(MSG_WARNING, "EAP: buildIdentity: identity " + "configuration was not available"); + eap_sm_request_identity(sm, config); + return NULL; + } + + + *len = sizeof(struct eap_hdr) + 1 + identity_len; + resp = malloc(*len); + if (resp == NULL) + return NULL; + + resp->code = EAP_CODE_RESPONSE; + resp->identifier = id; + resp->length = host_to_be16(*len); + pos = (u8 *) (resp + 1); + *pos++ = EAP_TYPE_IDENTITY; + memcpy(pos, identity, identity_len); + + return (u8 *) resp; +} + + +static void eap_sm_processNotify(struct eap_sm *sm, u8 *req, size_t len) +{ + struct eap_hdr *hdr = (struct eap_hdr *) req; + u8 *pos = (u8 *) (hdr + 1); + pos++; + /* TODO: log the Notification Request and make it available for UI */ + wpa_hexdump_ascii(MSG_DEBUG, "EAP: EAP-Request Notification data", + pos, be_to_host16(hdr->length) - 5); +} + + +static u8 *eap_sm_buildNotify(struct eap_sm *sm, int id, size_t *len) +{ + struct eap_hdr *resp; + u8 *pos; + + wpa_printf(MSG_DEBUG, "EAP: Generating EAP-Response Notification"); + *len = sizeof(struct eap_hdr) + 1; + resp = malloc(*len); + if (resp == NULL) + return NULL; + + resp->code = EAP_CODE_RESPONSE; + resp->identifier = id; + resp->length = host_to_be16(*len); + pos = (u8 *) (resp + 1); + *pos = EAP_TYPE_NOTIFICATION; + + return (u8 *) resp; +} + + +static void eap_sm_parseEapReq(struct eap_sm *sm, u8 *req, size_t len) +{ + struct eap_hdr *hdr; + size_t plen; + MD5_CTX context; + + sm->rxReq = sm->rxSuccess = sm->rxFailure = FALSE; + sm->reqId = 0; + sm->reqMethod = EAP_TYPE_NONE; + + if (req == NULL || len < sizeof(*hdr)) + return; + + hdr = (struct eap_hdr *) req; + plen = be_to_host16(hdr->length); + if (plen > len) { + wpa_printf(MSG_DEBUG, "EAP: Ignored truncated EAP-Packet " + "(len=%lu plen=%lu)", + (unsigned long) len, (unsigned long) plen); + return; + } + + sm->reqId = hdr->identifier; + + if (sm->workaround) { + MD5Init(&context); + MD5Update(&context, req, len); + MD5Final(sm->req_md5, &context); + } + + switch (hdr->code) { + case EAP_CODE_REQUEST: + sm->rxReq = TRUE; + if (plen > sizeof(*hdr)) + sm->reqMethod = *((u8 *) (hdr + 1)); + wpa_printf(MSG_DEBUG, "EAP: Received EAP-Request method=%d " + "id=%d", sm->reqMethod, sm->reqId); + break; + case EAP_CODE_RESPONSE: + if (sm->selectedMethod == EAP_TYPE_LEAP) { + sm->rxResp = TRUE; + if (plen > sizeof(*hdr)) + sm->reqMethod = *((u8 *) (hdr + 1)); + wpa_printf(MSG_DEBUG, "EAP: Received EAP-Response for " + "LEAP method=%d id=%d", + sm->reqMethod, sm->reqId); + break; + } + wpa_printf(MSG_DEBUG, "EAP: Ignored EAP-Response"); + break; + case EAP_CODE_SUCCESS: + wpa_printf(MSG_DEBUG, "EAP: Received EAP-Success"); + sm->rxSuccess = TRUE; + break; + case EAP_CODE_FAILURE: + wpa_printf(MSG_DEBUG, "EAP: Received EAP-Failure"); + sm->rxFailure = TRUE; + break; + default: + wpa_printf(MSG_DEBUG, "EAP: Ignored EAP-Packet with unknown " + "code %d", hdr->code); + break; + } +} + + +struct eap_sm *eap_sm_init(void *eapol_ctx, struct eapol_callbacks *eapol_cb, + void *msg_ctx) +{ + struct eap_sm *sm; + + sm = malloc(sizeof(*sm)); + if (sm == NULL) + return NULL; + memset(sm, 0, sizeof(*sm)); + sm->eapol_ctx = eapol_ctx; + sm->eapol_cb = eapol_cb; + sm->msg_ctx = msg_ctx; + sm->ClientTimeout = 60; + + sm->ssl_ctx = tls_init(); + if (sm->ssl_ctx == NULL) { + wpa_printf(MSG_WARNING, "SSL: Failed to initialize TLS " + "context."); + free(sm); + return NULL; + } + + return sm; +} + + +void eap_sm_deinit(struct eap_sm *sm) +{ + if (sm == NULL) + return; + eap_deinit_prev_method(sm, "EAP deinit"); + free(sm->lastRespData); + free(sm->eapRespData); + free(sm->eapKeyData); + tls_deinit(sm->ssl_ctx); + free(sm); +} + + +int eap_sm_step(struct eap_sm *sm) +{ + int res = 0; + do { + sm->changed = FALSE; + SM_STEP_RUN(EAP); + if (sm->changed) + res = 1; + } while (sm->changed); + return res; +} + + +void eap_sm_abort(struct eap_sm *sm) +{ + /* release system resources that may have been allocated for the + * authentication session */ + free(sm->eapRespData); + sm->eapRespData = NULL; + free(sm->eapKeyData); + sm->eapKeyData = NULL; +} + + +static const char * eap_sm_state_txt(int state) +{ + switch (state) { + case EAP_INITIALIZE: + return "INITIALIZE"; + case EAP_DISABLED: + return "DISABLED"; + case EAP_IDLE: + return "IDLE"; + case EAP_RECEIVED: + return "RECEIVED"; + case EAP_GET_METHOD: + return "GET_METHOD"; + case EAP_METHOD: + return "METHOD"; + case EAP_SEND_RESPONSE: + return "SEND_RESPONSE"; + case EAP_DISCARD: + return "DISCARD"; + case EAP_IDENTITY: + return "IDENTITY"; + case EAP_NOTIFICATION: + return "NOTIFICATION"; + case EAP_RETRANSMIT: + return "RETRANSMIT"; + case EAP_SUCCESS: + return "SUCCESS"; + case EAP_FAILURE: + return "FAILURE"; + default: + return "UNKNOWN"; + } +} + + +static const char * eap_sm_method_state_txt(int state) +{ + switch (state) { + case METHOD_NONE: + return "NONE"; + case METHOD_INIT: + return "INIT"; + case METHOD_CONT: + return "CONT"; + case METHOD_MAY_CONT: + return "MAY_CONT"; + case METHOD_DONE: + return "DONE"; + default: + return "UNKNOWN"; + } +} + + +static const char * eap_sm_decision_txt(int decision) +{ + switch (decision) { + case DECISION_FAIL: + return "FAIL"; + case DECISION_COND_SUCC: + return "COND_SUCC"; + case DECISION_UNCOND_SUCC: + return "UNCOND_SUCC"; + default: + return "UNKNOWN"; + } +} + + +int eap_sm_get_status(struct eap_sm *sm, char *buf, size_t buflen, int verbose) +{ + int len; + + if (sm == NULL) + return 0; + + len = snprintf(buf, buflen, + "EAP state=%s\n", + eap_sm_state_txt(sm->EAP_state)); + + if (sm->selectedMethod != EAP_TYPE_NONE) { + const char *name; + if (sm->m) { + name = sm->m->name; + } else { + const struct eap_method *m = + eap_sm_get_eap_methods(sm->selectedMethod); + if (m) + name = m->name; + else + name = "?"; + } + len += snprintf(buf + len, buflen - len, + "selectedMethod=%d (EAP-%s)\n", + sm->selectedMethod, name); + + if (sm->m && sm->m->get_status) { + len += sm->m->get_status(sm, sm->eap_method_priv, + buf + len, buflen - len, + verbose); + } + } + + if (verbose) { + len += snprintf(buf + len, buflen - len, + "reqMethod=%d\n" + "methodState=%s\n" + "decision=%s\n" + "ClientTimeout=%d\n", + sm->reqMethod, + eap_sm_method_state_txt(sm->methodState), + eap_sm_decision_txt(sm->decision), + sm->ClientTimeout); + } + + return len; +} + + +typedef enum { TYPE_IDENTITY, TYPE_PASSWORD, TYPE_OTP } eap_ctrl_req_type; + +static void eap_sm_request(struct eap_sm *sm, struct wpa_ssid *config, + eap_ctrl_req_type type, char *msg, size_t msglen) +{ + char *buf; + size_t buflen; + int len; + char *field; + char *txt, *tmp; + + if (config == NULL || sm == NULL) + return; + + switch (type) { + case TYPE_IDENTITY: + field = "IDENTITY"; + txt = "Identity"; + config->pending_req_identity++; + break; + case TYPE_PASSWORD: + field = "PASSWORD"; + txt = "Password"; + config->pending_req_password++; + break; + case TYPE_OTP: + field = "OTP"; + if (msg) { + tmp = malloc(msglen + 3); + if (tmp == NULL) + return; + tmp[0] = '['; + memcpy(tmp + 1, msg, msglen); + tmp[msglen + 1] = ']'; + tmp[msglen + 2] = '\0'; + txt = tmp; + free(config->pending_req_otp); + config->pending_req_otp = tmp; + config->pending_req_otp_len = msglen + 3; + } else { + if (config->pending_req_otp == NULL) + return; + txt = config->pending_req_otp; + } + break; + default: + return; + } + + buflen = 100 + strlen(txt) + config->ssid_len; + buf = malloc(buflen); + if (buf == NULL) + return; + len = snprintf(buf, buflen, "CTRL-REQ-%s-%d:%s needed for SSID ", + field, config->id, txt); + if (config->ssid && buflen > len + config->ssid_len) { + memcpy(buf + len, config->ssid, config->ssid_len); + len += config->ssid_len; + buf[len] = '\0'; + } + wpa_msg(sm->msg_ctx, MSG_INFO, buf); + free(buf); +} + + +void eap_sm_request_identity(struct eap_sm *sm, struct wpa_ssid *config) +{ + eap_sm_request(sm, config, TYPE_IDENTITY, NULL, 0); +} + + +void eap_sm_request_password(struct eap_sm *sm, struct wpa_ssid *config) +{ + eap_sm_request(sm, config, TYPE_PASSWORD, NULL, 0); +} + + +void eap_sm_request_otp(struct eap_sm *sm, struct wpa_ssid *config, + char *msg, size_t msg_len) +{ + eap_sm_request(sm, config, TYPE_OTP, msg, msg_len); +} + + +void eap_sm_notify_ctrl_attached(struct eap_sm *sm) +{ + struct wpa_ssid *config = eap_get_config(sm); + + if (config == NULL) + return; + + /* Re-send any pending requests for user data since a new control + * interface was added. This handles cases where the EAP authentication + * starts immediately after system startup when the user interface is + * not yet running. */ + if (config->pending_req_identity) + eap_sm_request_identity(sm, config); + if (config->pending_req_password) + eap_sm_request_password(sm, config); + if (config->pending_req_otp) + eap_sm_request_otp(sm, config, NULL, 0); +} + + +u8 eap_get_type(const char *name) +{ + int i; + for (i = 0; i < NUM_EAP_METHODS; i++) { + if (strcmp(eap_methods[i]->name, name) == 0) + return eap_methods[i]->method; + } + return EAP_TYPE_NONE; +} + + +static int eap_allowed_phase2_type(int type) +{ + return type != EAP_TYPE_PEAP && type != EAP_TYPE_TTLS && + type != EAP_TYPE_FAST; +} + + +u8 eap_get_phase2_type(const char *name) +{ + u8 type = eap_get_type(name); + if (eap_allowed_phase2_type(type)) + return type; + return EAP_TYPE_NONE; +} + + +u8 *eap_get_phase2_types(struct wpa_ssid *config, size_t *count) +{ + u8 *buf, method; + int i; + + *count = 0; + buf = malloc(NUM_EAP_METHODS); + if (buf == NULL) + return NULL; + + for (i = 0; i < NUM_EAP_METHODS; i++) { + method = eap_methods[i]->method; + if (eap_allowed_phase2_type(method)) { + if (method == EAP_TYPE_TLS && config && + config->private_key2 == NULL) + continue; + buf[*count] = method; + (*count)++; + } + } + + return buf; +} + + +void eap_set_fast_reauth(struct eap_sm *sm, int enabled) +{ + sm->fast_reauth = enabled; +} + + +void eap_set_workaround(struct eap_sm *sm, unsigned int workaround) +{ + sm->workaround = workaround; +} + + +struct wpa_ssid * eap_get_config(struct eap_sm *sm) +{ + return sm->eapol_cb->get_config(sm->eapol_ctx); +} + + +int eap_key_available(struct eap_sm *sm) +{ + return sm ? sm->eapKeyAvailable : 0; +} + + +void eap_notify_success(struct eap_sm *sm) +{ + if (sm) { + sm->decision = DECISION_COND_SUCC; + sm->EAP_state = EAP_SUCCESS; + } +} + + +u8 * eap_get_eapKeyData(struct eap_sm *sm, size_t *len) +{ + if (sm == NULL || sm->eapKeyData == NULL) { + *len = 0; + return NULL; + } + + *len = sm->eapKeyDataLen; + return sm->eapKeyData; +} + + +u8 * eap_get_eapRespData(struct eap_sm *sm, size_t *len) +{ + u8 *resp; + + if (sm == NULL || sm->eapRespData == NULL) { + *len = 0; + return NULL; + } + + resp = sm->eapRespData; + *len = sm->eapRespDataLen; + sm->eapRespData = NULL; + sm->eapRespDataLen = 0; + + return resp; +} + + +void eap_register_scard_ctx(struct eap_sm *sm, void *ctx) +{ + if (sm) + sm->scard_ctx = ctx; +} diff --git a/contrib/wpa_supplicant/eap.h b/contrib/wpa_supplicant/eap.h new file mode 100644 index 000000000000..3d7cc7210d7a --- /dev/null +++ b/contrib/wpa_supplicant/eap.h @@ -0,0 +1,70 @@ +#ifndef EAP_H +#define EAP_H + +#include "defs.h" +#include "eap_defs.h" + +struct eap_sm; +struct wpa_ssid; + + +#ifdef IEEE8021X_EAPOL + +enum eapol_bool_var { + EAPOL_eapSuccess, EAPOL_eapRestart, EAPOL_eapFail, EAPOL_eapResp, + EAPOL_eapNoResp, EAPOL_eapReq, EAPOL_portEnabled, EAPOL_altAccept, + EAPOL_altReject +}; + +enum eapol_int_var { + EAPOL_idleWhile +}; + +struct eapol_callbacks { + struct wpa_ssid * (*get_config)(void *ctx); + Boolean (*get_bool)(void *ctx, enum eapol_bool_var variable); + void (*set_bool)(void *ctx, enum eapol_bool_var variable, + Boolean value); + unsigned int (*get_int)(void *ctx, enum eapol_int_var variable); + void (*set_int)(void *ctx, enum eapol_int_var variable, + unsigned int value); + u8 * (*get_eapReqData)(void *ctx, size_t *len); +}; + +struct eap_sm *eap_sm_init(void *eapol_ctx, struct eapol_callbacks *eapol_cb, + void *msg_ctx); +void eap_sm_deinit(struct eap_sm *sm); +int eap_sm_step(struct eap_sm *sm); +void eap_sm_abort(struct eap_sm *sm); +int eap_sm_get_status(struct eap_sm *sm, char *buf, size_t buflen, + int verbose); +u8 *eap_sm_buildIdentity(struct eap_sm *sm, int id, size_t *len, + int encrypted); +const struct eap_method * eap_sm_get_eap_methods(int method); +void eap_sm_request_identity(struct eap_sm *sm, struct wpa_ssid *config); +void eap_sm_request_password(struct eap_sm *sm, struct wpa_ssid *config); +void eap_sm_request_otp(struct eap_sm *sm, struct wpa_ssid *config, + char *msg, size_t msg_len); +void eap_sm_notify_ctrl_attached(struct eap_sm *sm); +u8 eap_get_type(const char *name); +u8 eap_get_phase2_type(const char *name); +u8 *eap_get_phase2_types(struct wpa_ssid *config, size_t *count); +void eap_set_fast_reauth(struct eap_sm *sm, int enabled); +void eap_set_workaround(struct eap_sm *sm, unsigned int workaround); +struct wpa_ssid * eap_get_config(struct eap_sm *sm); +int eap_key_available(struct eap_sm *sm); +void eap_notify_success(struct eap_sm *sm); +u8 * eap_get_eapKeyData(struct eap_sm *sm, size_t *len); +u8 * eap_get_eapRespData(struct eap_sm *sm, size_t *len); +void eap_register_scard_ctx(struct eap_sm *sm, void *ctx); + +#else /* IEEE8021X_EAPOL */ + +static inline u8 eap_get_type(const char *name) +{ + return EAP_TYPE_NONE; +} + +#endif /* IEEE8021X_EAPOL */ + +#endif /* EAP_H */ diff --git a/contrib/wpa_supplicant/eap_aka.c b/contrib/wpa_supplicant/eap_aka.c new file mode 100644 index 000000000000..7fe6449ab896 --- /dev/null +++ b/contrib/wpa_supplicant/eap_aka.c @@ -0,0 +1,915 @@ +/* + * WPA Supplicant / EAP-AKA (draft-arkko-pppext-eap-aka-12.txt) + * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include "common.h" +#include "eap_i.h" +#include "wpa_supplicant.h" +#include "config_ssid.h" +#include "sha1.h" +#include "pcsc_funcs.h" +#include "eap_sim_common.h" + +/* EAP-AKA Subtypes */ +#define EAP_AKA_SUBTYPE_CHALLENGE 1 +#define EAP_AKA_SUBTYPE_AUTHENTICATION_REJECT 2 +#define EAP_AKA_SUBTYPE_SYNCHRONIZATION_FAILURE 4 +#define EAP_AKA_SUBTYPE_IDENTITY 5 +#define EAP_AKA_SUBTYPE_NOTIFICATION 12 +#define EAP_AKA_SUBTYPE_REAUTHENTICATION 13 +#define EAP_AKA_SUBTYPE_CLIENT_ERROR 14 + +/* AT_CLIENT_ERROR_CODE error codes */ +#define EAP_AKA_UNABLE_TO_PROCESS_PACKET 0 + +#define AKA_AUTS_LEN 14 +#define RES_MAX_LEN 16 +#define IK_LEN 16 +#define CK_LEN 16 +#define EAP_AKA_MAX_FAST_REAUTHS 1000 + +struct eap_aka_data { + u8 ik[IK_LEN], ck[CK_LEN], res[RES_MAX_LEN]; + size_t res_len; + u8 nonce_s[EAP_SIM_NONCE_S_LEN]; + u8 mk[EAP_SIM_MK_LEN]; + u8 k_aut[EAP_SIM_K_AUT_LEN]; + u8 k_encr[EAP_SIM_K_ENCR_LEN]; + u8 msk[EAP_SIM_KEYING_DATA_LEN]; + u8 rand[AKA_RAND_LEN], autn[AKA_AUTN_LEN]; + u8 auts[AKA_AUTS_LEN]; + + int num_id_req, num_notification; + u8 *pseudonym; + size_t pseudonym_len; + u8 *reauth_id; + size_t reauth_id_len; + int reauth; + unsigned int counter, counter_too_small; + u8 *last_eap_identity; + size_t last_eap_identity_len; + enum { CONTINUE, SUCCESS, FAILURE } state; +}; + + +static void * eap_aka_init(struct eap_sm *sm) +{ + struct eap_aka_data *data; + data = malloc(sizeof(*data)); + if (data == NULL) + return NULL; + memset(data, 0, sizeof(*data)); + + data->state = CONTINUE; + + return data; +} + + +static void eap_aka_deinit(struct eap_sm *sm, void *priv) +{ + struct eap_aka_data *data = priv; + if (data) { + free(data->pseudonym); + free(data->reauth_id); + free(data->last_eap_identity); + free(data); + } +} + + +static int eap_aka_umts_auth(struct eap_sm *sm, struct eap_aka_data *data) +{ + wpa_printf(MSG_DEBUG, "EAP-AKA: UMTS authentication algorithm"); +#ifdef PCSC_FUNCS + return scard_umts_auth(sm->scard_ctx, data->rand, + data->autn, data->res, &data->res_len, + data->ik, data->ck, data->auts); +#else /* PCSC_FUNCS */ + /* These hardcoded Kc and SRES values are used for testing. + * Could consider making them configurable. */ + memset(data->res, '2', RES_MAX_LEN); + data->res_len = 16; + memset(data->ik, '3', IK_LEN); + memset(data->ck, '4', CK_LEN); + { + u8 autn[AKA_AUTN_LEN]; + memset(autn, '1', AKA_AUTN_LEN); + if (memcmp(autn, data->autn, AKA_AUTN_LEN) != 0) { + wpa_printf(MSG_WARNING, "EAP-AKA: AUTN did not match " + "with expected value"); + return -1; + } + } + return 0; +#endif /* PCSC_FUNCS */ +} + + +static void eap_aka_derive_mk(struct eap_aka_data *data, + const u8 *identity, size_t identity_len) +{ + const u8 *addr[3]; + size_t len[3]; + + addr[0] = identity; + len[0] = identity_len; + addr[1] = data->ik; + len[1] = IK_LEN; + addr[2] = data->ck; + len[2] = CK_LEN; + + /* MK = SHA1(Identity|IK|CK) */ + sha1_vector(3, addr, len, data->mk); + wpa_hexdump_key(MSG_DEBUG, "EAP-AKA: IK", data->ik, IK_LEN); + wpa_hexdump_key(MSG_DEBUG, "EAP-AKA: CK", data->ck, CK_LEN); + wpa_hexdump_key(MSG_DEBUG, "EAP-AKA: MK", data->mk, EAP_SIM_MK_LEN); +} + + +#define CLEAR_PSEUDONYM 0x01 +#define CLEAR_REAUTH_ID 0x02 +#define CLEAR_EAP_ID 0x04 + +static void eap_aka_clear_identities(struct eap_aka_data *data, int id) +{ + wpa_printf(MSG_DEBUG, "EAP-AKA: forgetting old%s%s%s", + id & CLEAR_PSEUDONYM ? " pseudonym" : "", + id & CLEAR_REAUTH_ID ? " reauth_id" : "", + id & CLEAR_EAP_ID ? " eap_id" : ""); + if (id & CLEAR_PSEUDONYM) { + free(data->pseudonym); + data->pseudonym = NULL; + data->pseudonym_len = 0; + } + if (id & CLEAR_REAUTH_ID) { + free(data->reauth_id); + data->reauth_id = NULL; + data->reauth_id_len = 0; + } + if (id & CLEAR_EAP_ID) { + free(data->last_eap_identity); + data->last_eap_identity = NULL; + data->last_eap_identity_len = 0; + } +} + + +static int eap_aka_learn_ids(struct eap_aka_data *data, + struct eap_sim_attrs *attr) +{ + if (attr->next_pseudonym) { + free(data->pseudonym); + data->pseudonym = malloc(attr->next_pseudonym_len); + if (data->pseudonym == NULL) { + wpa_printf(MSG_INFO, "EAP-AKA: (encr) No memory for " + "next pseudonym"); + return -1; + } + memcpy(data->pseudonym, attr->next_pseudonym, + attr->next_pseudonym_len); + data->pseudonym_len = attr->next_pseudonym_len; + wpa_hexdump_ascii(MSG_DEBUG, + "EAP-AKA: (encr) AT_NEXT_PSEUDONYM", + data->pseudonym, + data->pseudonym_len); + } + + if (attr->next_reauth_id) { + free(data->reauth_id); + data->reauth_id = malloc(attr->next_reauth_id_len); + if (data->reauth_id == NULL) { + wpa_printf(MSG_INFO, "EAP-AKA: (encr) No memory for " + "next reauth_id"); + return -1; + } + memcpy(data->reauth_id, attr->next_reauth_id, + attr->next_reauth_id_len); + data->reauth_id_len = attr->next_reauth_id_len; + wpa_hexdump_ascii(MSG_DEBUG, + "EAP-AKA: (encr) AT_NEXT_REAUTH_ID", + data->reauth_id, + data->reauth_id_len); + } + + return 0; +} + + +static u8 * eap_aka_client_error(struct eap_sm *sm, struct eap_aka_data *data, + struct eap_hdr *req, + size_t *respDataLen, int err) +{ + struct eap_sim_msg *msg; + + data->state = FAILURE; + data->num_id_req = 0; + data->num_notification = 0; + + msg = eap_sim_msg_init(EAP_CODE_RESPONSE, req->identifier, + EAP_TYPE_AKA, EAP_AKA_SUBTYPE_CLIENT_ERROR); + eap_sim_msg_add(msg, EAP_SIM_AT_CLIENT_ERROR_CODE, err, NULL, 0); + return eap_sim_msg_finish(msg, respDataLen, NULL, NULL, 0); +} + + +static u8 * eap_aka_authentication_reject(struct eap_sm *sm, + struct eap_aka_data *data, + struct eap_hdr *req, + size_t *respDataLen) +{ + struct eap_sim_msg *msg; + + data->state = FAILURE; + data->num_id_req = 0; + data->num_notification = 0; + + wpa_printf(MSG_DEBUG, "Generating EAP-AKA Authentication-Reject " + "(id=%d)", req->identifier); + msg = eap_sim_msg_init(EAP_CODE_RESPONSE, req->identifier, + EAP_TYPE_AKA, + EAP_AKA_SUBTYPE_AUTHENTICATION_REJECT); + return eap_sim_msg_finish(msg, respDataLen, NULL, NULL, 0); +} + + +static u8 * eap_aka_synchronization_failure(struct eap_sm *sm, + struct eap_aka_data *data, + struct eap_hdr *req, + size_t *respDataLen) +{ + struct eap_sim_msg *msg; + + data->state = FAILURE; + data->num_id_req = 0; + data->num_notification = 0; + + wpa_printf(MSG_DEBUG, "Generating EAP-AKA Synchronization-Failure " + "(id=%d)", req->identifier); + msg = eap_sim_msg_init(EAP_CODE_RESPONSE, req->identifier, + EAP_TYPE_AKA, + EAP_AKA_SUBTYPE_SYNCHRONIZATION_FAILURE); + wpa_printf(MSG_DEBUG, " AT_AUTS"); + eap_sim_msg_add_full(msg, EAP_SIM_AT_AUTS, data->auts, AKA_AUTS_LEN); + return eap_sim_msg_finish(msg, respDataLen, NULL, NULL, 0); +} + + +static u8 * eap_aka_response_identity(struct eap_sm *sm, + struct eap_aka_data *data, + struct eap_hdr *req, + size_t *respDataLen, + enum eap_sim_id_req id_req) +{ + struct wpa_ssid *config = eap_get_config(sm); + u8 *identity = NULL; + size_t identity_len = 0; + struct eap_sim_msg *msg; + + data->reauth = 0; + if (id_req == ANY_ID && data->reauth_id) { + identity = data->reauth_id; + identity_len = data->reauth_id_len; + data->reauth = 1; + } else if ((id_req == ANY_ID || id_req == FULLAUTH_ID) && + data->pseudonym) { + identity = data->pseudonym; + identity_len = data->pseudonym_len; + eap_aka_clear_identities(data, CLEAR_REAUTH_ID); + } else if (id_req != NO_ID_REQ && config && config->identity) { + identity = config->identity; + identity_len = config->identity_len; + eap_aka_clear_identities(data, + CLEAR_PSEUDONYM | CLEAR_REAUTH_ID); + } + if (id_req != NO_ID_REQ) + eap_aka_clear_identities(data, CLEAR_EAP_ID); + + wpa_printf(MSG_DEBUG, "Generating EAP-AKA Identity (id=%d)", + req->identifier); + msg = eap_sim_msg_init(EAP_CODE_RESPONSE, req->identifier, + EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY); + + if (identity) { + wpa_hexdump_ascii(MSG_DEBUG, " AT_IDENTITY", + identity, identity_len); + eap_sim_msg_add(msg, EAP_SIM_AT_IDENTITY, identity_len, + identity, identity_len); + } + + return eap_sim_msg_finish(msg, respDataLen, NULL, NULL, 0); +} + + +static u8 * eap_aka_response_challenge(struct eap_sm *sm, + struct eap_aka_data *data, + struct eap_hdr *req, + size_t *respDataLen) +{ + struct eap_sim_msg *msg; + + wpa_printf(MSG_DEBUG, "Generating EAP-AKA Challenge (id=%d)", + req->identifier); + msg = eap_sim_msg_init(EAP_CODE_RESPONSE, req->identifier, + EAP_TYPE_AKA, EAP_AKA_SUBTYPE_CHALLENGE); + wpa_printf(MSG_DEBUG, " AT_RES"); + eap_sim_msg_add(msg, EAP_SIM_AT_RES, data->res_len, + data->res, data->res_len); + wpa_printf(MSG_DEBUG, " AT_MAC"); + eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC); + return eap_sim_msg_finish(msg, respDataLen, data->k_aut, (u8 *) "", 0); +} + + +static u8 * eap_aka_response_reauth(struct eap_sm *sm, + struct eap_aka_data *data, + struct eap_hdr *req, + size_t *respDataLen, int counter_too_small) +{ + struct eap_sim_msg *msg; + unsigned int counter; + + wpa_printf(MSG_DEBUG, "Generating EAP-AKA Reauthentication (id=%d)", + req->identifier); + msg = eap_sim_msg_init(EAP_CODE_RESPONSE, req->identifier, + EAP_TYPE_AKA, + EAP_AKA_SUBTYPE_REAUTHENTICATION); + wpa_printf(MSG_DEBUG, " AT_IV"); + wpa_printf(MSG_DEBUG, " AT_ENCR_DATA"); + eap_sim_msg_add_encr_start(msg, EAP_SIM_AT_IV, EAP_SIM_AT_ENCR_DATA); + + if (counter_too_small) { + wpa_printf(MSG_DEBUG, " *AT_COUNTER_TOO_SMALL"); + eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER_TOO_SMALL, 0, NULL, 0); + counter = data->counter_too_small; + } else + counter = data->counter; + + wpa_printf(MSG_DEBUG, " *AT_COUNTER %d", counter); + eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER, counter, NULL, 0); + + if (eap_sim_msg_add_encr_end(msg, data->k_encr, EAP_SIM_AT_PADDING)) { + wpa_printf(MSG_WARNING, "EAP-AKA: Failed to encrypt " + "AT_ENCR_DATA"); + eap_sim_msg_free(msg); + return NULL; + } + wpa_printf(MSG_DEBUG, " AT_MAC"); + eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC); + return eap_sim_msg_finish(msg, respDataLen, data->k_aut, data->nonce_s, + EAP_SIM_NONCE_S_LEN); +} + + +static u8 * eap_aka_response_notification(struct eap_sm *sm, + struct eap_aka_data *data, + struct eap_hdr *req, + size_t *respDataLen, + u16 notification) +{ + struct eap_sim_msg *msg; + u8 *k_aut = (notification & 0x4000) == 0 ? data->k_aut : NULL; + + wpa_printf(MSG_DEBUG, "Generating EAP-AKA Notification (id=%d)", + req->identifier); + msg = eap_sim_msg_init(EAP_CODE_RESPONSE, req->identifier, + EAP_TYPE_AKA, EAP_AKA_SUBTYPE_NOTIFICATION); + wpa_printf(MSG_DEBUG, " AT_NOTIFICATION"); + eap_sim_msg_add(msg, EAP_SIM_AT_NOTIFICATION, notification, NULL, 0); + if (k_aut && data->reauth) { + wpa_printf(MSG_DEBUG, " AT_IV"); + wpa_printf(MSG_DEBUG, " AT_ENCR_DATA"); + eap_sim_msg_add_encr_start(msg, EAP_SIM_AT_IV, + EAP_SIM_AT_ENCR_DATA); + wpa_printf(MSG_DEBUG, " *AT_COUNTER %d", data->counter); + eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER, data->counter, + NULL, 0); + if (eap_sim_msg_add_encr_end(msg, data->k_encr, + EAP_SIM_AT_PADDING)) { + wpa_printf(MSG_WARNING, "EAP-AKA: Failed to encrypt " + "AT_ENCR_DATA"); + eap_sim_msg_free(msg); + return NULL; + } + } + if (k_aut) { + wpa_printf(MSG_DEBUG, " AT_MAC"); + eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC); + } + return eap_sim_msg_finish(msg, respDataLen, k_aut, (u8 *) "", 0); +} + + +static u8 * eap_aka_process_identity(struct eap_sm *sm, + struct eap_aka_data *data, + struct eap_hdr *req, size_t reqDataLen, + size_t *respDataLen, + struct eap_sim_attrs *attr) +{ + int id_error; + + wpa_printf(MSG_DEBUG, "EAP-AKA: subtype Identity"); + + id_error = 0; + switch (attr->id_req) { + case NO_ID_REQ: + break; + case ANY_ID: + if (data->num_id_req > 0) + id_error++; + data->num_id_req++; + break; + case FULLAUTH_ID: + if (data->num_id_req > 1) + id_error++; + data->num_id_req++; + break; + case PERMANENT_ID: + if (data->num_id_req > 2) + id_error++; + data->num_id_req++; + break; + } + if (id_error) { + wpa_printf(MSG_INFO, "EAP-AKA: Too many ID requests " + "used within one authentication"); + return eap_aka_client_error(sm, data, req, respDataLen, + EAP_AKA_UNABLE_TO_PROCESS_PACKET); + } + + return eap_aka_response_identity(sm, data, req, respDataLen, + attr->id_req); +} + + +static u8 * eap_aka_process_challenge(struct eap_sm *sm, + struct eap_aka_data *data, + struct eap_hdr *req, size_t reqDataLen, + size_t *respDataLen, + struct eap_sim_attrs *attr) +{ + struct wpa_ssid *config = eap_get_config(sm); + u8 *identity; + size_t identity_len; + int res; + struct eap_sim_attrs eattr; + + wpa_printf(MSG_DEBUG, "EAP-AKA: subtype Challenge"); + data->reauth = 0; + if (!attr->mac || !attr->rand || !attr->autn) { + wpa_printf(MSG_WARNING, "EAP-AKA: Challenge message " + "did not include%s%s%s", + !attr->mac ? " AT_MAC" : "", + !attr->rand ? " AT_RAND" : "", + !attr->autn ? " AT_AUTN" : ""); + return eap_aka_client_error(sm, data, req, respDataLen, + EAP_AKA_UNABLE_TO_PROCESS_PACKET); + } + memcpy(data->rand, attr->rand, AKA_RAND_LEN); + memcpy(data->autn, attr->autn, AKA_AUTN_LEN); + + res = eap_aka_umts_auth(sm, data); + if (res == -1) { + wpa_printf(MSG_WARNING, "EAP-AKA: UMTS authentication " + "failed (AUTN)"); + return eap_aka_authentication_reject(sm, data, req, + respDataLen); + } else if (res == -2) { + wpa_printf(MSG_WARNING, "EAP-AKA: UMTS authentication " + "failed (AUTN seq# -> AUTS)"); + return eap_aka_synchronization_failure(sm, data, req, + respDataLen); + } else if (res) { + wpa_printf(MSG_WARNING, "EAP-AKA: UMTS authentication failed"); + return eap_aka_client_error(sm, data, req, respDataLen, + EAP_AKA_UNABLE_TO_PROCESS_PACKET); + } + if (data->last_eap_identity) { + identity = data->last_eap_identity; + identity_len = data->last_eap_identity_len; + } else if (data->pseudonym) { + identity = data->pseudonym; + identity_len = data->pseudonym_len; + } else { + identity = config->identity; + identity_len = config->identity_len; + } + wpa_hexdump_ascii(MSG_DEBUG, "EAP-AKA: Selected identity for MK " + "derivation", identity, identity_len); + eap_aka_derive_mk(data, identity, identity_len); + eap_sim_derive_keys(data->mk, data->k_encr, data->k_aut, data->msk); + if (eap_sim_verify_mac(data->k_aut, (u8 *) req, reqDataLen, attr->mac, + (u8 *) "", 0)) { + wpa_printf(MSG_WARNING, "EAP-AKA: Challenge message " + "used invalid AT_MAC"); + return eap_aka_client_error(sm, data, req, respDataLen, + EAP_AKA_UNABLE_TO_PROCESS_PACKET); + } + + /* Old reauthentication and pseudonym identities must not be used + * anymore. In other words, if no new identities are received, full + * authentication will be used on next reauthentication. */ + eap_aka_clear_identities(data, CLEAR_PSEUDONYM | CLEAR_REAUTH_ID | + CLEAR_EAP_ID); + + if (attr->encr_data) { + if (eap_sim_parse_encr(data->k_encr, attr->encr_data, + attr->encr_data_len, attr->iv, &eattr, + 0)) { + return eap_aka_client_error( + sm, data, req, respDataLen, + EAP_AKA_UNABLE_TO_PROCESS_PACKET); + } + eap_aka_learn_ids(data, &eattr); + } + + if (data->state != FAILURE) + data->state = SUCCESS; + + data->num_id_req = 0; + data->num_notification = 0; + /* draft-arkko-pppext-eap-aka-12.txt specifies that counter + * is initialized to one after fullauth, but initializing it to + * zero makes it easier to implement reauth verification. */ + data->counter = 0; + return eap_aka_response_challenge(sm, data, req, respDataLen); +} + + +static int eap_aka_process_notification_reauth(struct eap_aka_data *data, + struct eap_hdr *req, + size_t reqDataLen, + struct eap_sim_attrs *attr) +{ + struct eap_sim_attrs eattr; + + if (attr->encr_data == NULL || attr->iv == NULL) { + wpa_printf(MSG_WARNING, "EAP-AKA: Notification message after " + "reauth did not include encrypted data"); + return -1; + } + + if (eap_sim_parse_encr(data->k_encr, attr->encr_data, + attr->encr_data_len, attr->iv, &eattr, 0)) { + wpa_printf(MSG_WARNING, "EAP-AKA: Failed to parse encrypted " + "data from notification message"); + return -1; + } + + if (eattr.counter != data->counter) { + wpa_printf(MSG_WARNING, "EAP-AKA: Counter in notification " + "message does not match with counter in reauth " + "message"); + return -1; + } + + return 0; +} + + +static int eap_aka_process_notification_auth(struct eap_aka_data *data, + struct eap_hdr *req, + size_t reqDataLen, + struct eap_sim_attrs *attr) +{ + if (attr->mac == NULL) { + wpa_printf(MSG_INFO, "EAP-AKA: no AT_MAC in after_auth " + "Notification message"); + return -1; + } + + if (eap_sim_verify_mac(data->k_aut, (u8 *) req, reqDataLen, attr->mac, + (u8 *) "", 0)) { + wpa_printf(MSG_WARNING, "EAP-AKA: Notification message " + "used invalid AT_MAC"); + return -1; + } + + if (data->reauth && + eap_aka_process_notification_reauth(data, req, reqDataLen, attr)) { + wpa_printf(MSG_WARNING, "EAP-AKA: Invalid notification " + "message after reauth"); + return -1; + } + + return 0; +} + + +static u8 * eap_aka_process_notification(struct eap_sm *sm, + struct eap_aka_data *data, + struct eap_hdr *req, + size_t reqDataLen, + size_t *respDataLen, + struct eap_sim_attrs *attr) +{ + wpa_printf(MSG_DEBUG, "EAP-AKA: subtype Notification"); + if (data->num_notification > 0) { + wpa_printf(MSG_INFO, "EAP-AKA: too many notification " + "rounds (only one allowed)"); + return eap_aka_client_error(sm, data, req, respDataLen, + EAP_AKA_UNABLE_TO_PROCESS_PACKET); + } + data->num_notification++; + if (attr->notification == -1) { + wpa_printf(MSG_INFO, "EAP-AKA: no AT_NOTIFICATION in " + "Notification message"); + return eap_aka_client_error(sm, data, req, respDataLen, + EAP_AKA_UNABLE_TO_PROCESS_PACKET); + } + + if ((attr->notification & 0x4000) == 0 && + eap_aka_process_notification_auth(data, req, reqDataLen, attr)) { + return eap_aka_client_error(sm, data, req, respDataLen, + EAP_AKA_UNABLE_TO_PROCESS_PACKET); + } + + eap_sim_report_notification(sm->msg_ctx, attr->notification, 1); + if (attr->notification >= 0 && attr->notification < 32768) { + data->state = FAILURE; + } + return eap_aka_response_notification(sm, data, req, respDataLen, + attr->notification); +} + + +static u8 * eap_aka_process_reauthentication(struct eap_sm *sm, + struct eap_aka_data *data, + struct eap_hdr *req, + size_t reqDataLen, + size_t *respDataLen, + struct eap_sim_attrs *attr) +{ + struct eap_sim_attrs eattr; + + wpa_printf(MSG_DEBUG, "EAP-AKA: subtype Reauthentication"); + + if (data->reauth_id == NULL) { + wpa_printf(MSG_WARNING, "EAP-AKA: Server is trying " + "reauthentication, but no reauth_id available"); + return eap_aka_client_error(sm, data, req, respDataLen, + EAP_AKA_UNABLE_TO_PROCESS_PACKET); + } + + data->reauth = 1; + if (eap_sim_verify_mac(data->k_aut, (u8 *) req, reqDataLen, + attr->mac, (u8 *) "", 0)) { + wpa_printf(MSG_WARNING, "EAP-AKA: Reauthentication " + "did not have valid AT_MAC"); + return eap_aka_client_error(sm, data, req, respDataLen, + EAP_AKA_UNABLE_TO_PROCESS_PACKET); + } + + if (attr->encr_data == NULL || attr->iv == NULL) { + wpa_printf(MSG_WARNING, "EAP-AKA: Reauthentication " + "message did not include encrypted data"); + return eap_aka_client_error(sm, data, req, respDataLen, + EAP_AKA_UNABLE_TO_PROCESS_PACKET); + } + + if (eap_sim_parse_encr(data->k_encr, attr->encr_data, + attr->encr_data_len, attr->iv, &eattr, 0)) { + wpa_printf(MSG_WARNING, "EAP-AKA: Failed to parse encrypted " + "data from reauthentication message"); + return eap_aka_client_error(sm, data, req, respDataLen, + EAP_AKA_UNABLE_TO_PROCESS_PACKET); + } + + if (eattr.nonce_s == NULL || eattr.counter < 0) { + wpa_printf(MSG_INFO, "EAP-AKA: (encr) No%s%s in reauth packet", + !eattr.nonce_s ? " AT_NONCE_S" : "", + eattr.counter < 0 ? " AT_COUNTER" : ""); + return eap_aka_client_error(sm, data, req, respDataLen, + EAP_AKA_UNABLE_TO_PROCESS_PACKET); + } + + if (eattr.counter <= data->counter) { + wpa_printf(MSG_INFO, "EAP-AKA: (encr) Invalid counter " + "(%d <= %d)", eattr.counter, data->counter); + data->counter_too_small = eattr.counter; + /* Reply using Re-auth w/ AT_COUNTER_TOO_SMALL. The current + * reauth_id must not be used to start a new reauthentication. + * However, since it was used in the last EAP-Response-Identity + * packet, it has to saved for the following fullauth to be + * used in MK derivation. */ + free(data->last_eap_identity); + data->last_eap_identity = data->reauth_id; + data->last_eap_identity_len = data->reauth_id_len; + data->reauth_id = NULL; + data->reauth_id_len = 0; + return eap_aka_response_reauth(sm, data, req, respDataLen, 1); + } + data->counter = eattr.counter; + + memcpy(data->nonce_s, eattr.nonce_s, EAP_SIM_NONCE_S_LEN); + wpa_hexdump(MSG_DEBUG, "EAP-AKA: (encr) AT_NONCE_S", + data->nonce_s, EAP_SIM_NONCE_S_LEN); + + eap_sim_derive_keys_reauth(data->counter, + data->reauth_id, data->reauth_id_len, + data->nonce_s, data->mk, data->msk); + eap_aka_clear_identities(data, CLEAR_REAUTH_ID | CLEAR_EAP_ID); + eap_aka_learn_ids(data, &eattr); + + if (data->state != FAILURE) + data->state = SUCCESS; + + data->num_id_req = 0; + data->num_notification = 0; + if (data->counter > EAP_AKA_MAX_FAST_REAUTHS) { + wpa_printf(MSG_DEBUG, "EAP-AKA: Maximum number of " + "fast reauths performed - force fullauth"); + eap_aka_clear_identities(data, CLEAR_REAUTH_ID | CLEAR_EAP_ID); + } + return eap_aka_response_reauth(sm, data, req, respDataLen, 0); +} + + +static u8 * eap_aka_process(struct eap_sm *sm, void *priv, + struct eap_method_ret *ret, + u8 *reqData, size_t reqDataLen, + size_t *respDataLen) +{ + struct eap_aka_data *data = priv; + struct wpa_ssid *config = eap_get_config(sm); + struct eap_hdr *req; + u8 *pos, subtype, *res; + struct eap_sim_attrs attr; + size_t len; + + wpa_hexdump(MSG_DEBUG, "EAP-AKA: EAP data", reqData, reqDataLen); + if (config == NULL || config->identity == NULL) { + wpa_printf(MSG_INFO, "EAP-AKA: Identity not configured"); + eap_sm_request_identity(sm, config); + ret->ignore = TRUE; + return NULL; + } + + req = (struct eap_hdr *) reqData; + pos = (u8 *) (req + 1); + if (reqDataLen < sizeof(*req) + 4 || *pos != EAP_TYPE_AKA || + (len = be_to_host16(req->length)) > reqDataLen) { + wpa_printf(MSG_INFO, "EAP-AKA: Invalid frame"); + ret->ignore = TRUE; + return NULL; + } + + ret->ignore = FALSE; + ret->methodState = METHOD_CONT; + ret->decision = DECISION_FAIL; + ret->allowNotifications = TRUE; + + pos++; + subtype = *pos++; + wpa_printf(MSG_DEBUG, "EAP-AKA: Subtype=%d", subtype); + pos += 2; /* Reserved */ + + if (eap_sim_parse_attr(pos, reqData + len, &attr, 1, 0)) { + res = eap_aka_client_error(sm, data, req, respDataLen, + EAP_AKA_UNABLE_TO_PROCESS_PACKET); + goto done; + } + + switch (subtype) { + case EAP_AKA_SUBTYPE_IDENTITY: + res = eap_aka_process_identity(sm, data, req, len, + respDataLen, &attr); + break; + case EAP_AKA_SUBTYPE_CHALLENGE: + res = eap_aka_process_challenge(sm, data, req, len, + respDataLen, &attr); + break; + case EAP_AKA_SUBTYPE_NOTIFICATION: + res = eap_aka_process_notification(sm, data, req, len, + respDataLen, &attr); + break; + case EAP_AKA_SUBTYPE_REAUTHENTICATION: + res = eap_aka_process_reauthentication(sm, data, req, len, + respDataLen, &attr); + break; + case EAP_AKA_SUBTYPE_CLIENT_ERROR: + wpa_printf(MSG_DEBUG, "EAP-AKA: subtype Client-Error"); + res = eap_aka_client_error(sm, data, req, respDataLen, + EAP_AKA_UNABLE_TO_PROCESS_PACKET); + break; + default: + wpa_printf(MSG_DEBUG, "EAP-AKA: Unknown subtype=%d", subtype); + res = eap_aka_client_error(sm, data, req, respDataLen, + EAP_AKA_UNABLE_TO_PROCESS_PACKET); + break; + } + +done: + if (data->state == FAILURE) { + ret->decision = DECISION_FAIL; + ret->methodState = METHOD_DONE; + } else if (data->state == SUCCESS) { + ret->decision = DECISION_UNCOND_SUCC; + ret->methodState = METHOD_DONE; + } + + if (ret->methodState == METHOD_DONE) { + ret->allowNotifications = FALSE; + } + + return res; +} + + +static Boolean eap_aka_has_reauth_data(struct eap_sm *sm, void *priv) +{ + struct eap_aka_data *data = priv; + return data->pseudonym || data->reauth_id; +} + + +static void eap_aka_deinit_for_reauth(struct eap_sm *sm, void *priv) +{ + struct eap_aka_data *data = priv; + eap_aka_clear_identities(data, CLEAR_EAP_ID); +} + + +static void * eap_aka_init_for_reauth(struct eap_sm *sm, void *priv) +{ + struct eap_aka_data *data = priv; + data->num_id_req = 0; + data->num_notification = 0; + data->state = CONTINUE; + return priv; +} + + +static const u8 * eap_aka_get_identity(struct eap_sm *sm, void *priv, + size_t *len) +{ + struct eap_aka_data *data = priv; + + if (data->reauth_id) { + *len = data->reauth_id_len; + return data->reauth_id; + } + + if (data->pseudonym) { + *len = data->pseudonym_len; + return data->pseudonym; + } + + return NULL; +} + + +static Boolean eap_aka_isKeyAvailable(struct eap_sm *sm, void *priv) +{ + struct eap_aka_data *data = priv; + return data->state == SUCCESS; +} + + +static u8 * eap_aka_getKey(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_aka_data *data = priv; + u8 *key; + + if (data->state != SUCCESS) + return NULL; + + key = malloc(EAP_SIM_KEYING_DATA_LEN); + if (key == NULL) + return NULL; + + *len = EAP_SIM_KEYING_DATA_LEN; + memcpy(key, data->msk, EAP_SIM_KEYING_DATA_LEN); + + return key; +} + + +const struct eap_method eap_method_aka = +{ + .method = EAP_TYPE_AKA, + .name = "AKA", + .init = eap_aka_init, + .deinit = eap_aka_deinit, + .process = eap_aka_process, + .isKeyAvailable = eap_aka_isKeyAvailable, + .getKey = eap_aka_getKey, + .has_reauth_data = eap_aka_has_reauth_data, + .deinit_for_reauth = eap_aka_deinit_for_reauth, + .init_for_reauth = eap_aka_init_for_reauth, + .get_identity = eap_aka_get_identity, +}; diff --git a/contrib/wpa_supplicant/eap_defs.h b/contrib/wpa_supplicant/eap_defs.h new file mode 100644 index 000000000000..effe665f0919 --- /dev/null +++ b/contrib/wpa_supplicant/eap_defs.h @@ -0,0 +1,41 @@ +#ifndef EAP_DEFS_H +#define EAP_DEFS_H + +/* RFC 3748 - Extensible Authentication Protocol (EAP) */ + +struct eap_hdr { + u8 code; + u8 identifier; + u16 length; /* including code and identifier */ + /* followed by length-4 octets of data */ +} __attribute__ ((packed)); + +enum { EAP_CODE_REQUEST = 1, EAP_CODE_RESPONSE = 2, EAP_CODE_SUCCESS = 3, + EAP_CODE_FAILURE = 4 }; + +/* EAP Request and Response data begins with one octet Type. Success and + * Failure do not have additional data. */ + +typedef enum { + EAP_TYPE_NONE = 0, + EAP_TYPE_IDENTITY = 1, + EAP_TYPE_NOTIFICATION = 2, + EAP_TYPE_NAK = 3 /* Response only */, + EAP_TYPE_MD5 = 4, + EAP_TYPE_OTP = 5 /* RFC 2284 */, + EAP_TYPE_GTC = 6, /* RFC 2284 */ + EAP_TYPE_TLS = 13 /* RFC 2716 */, + EAP_TYPE_LEAP = 17 /* Cisco proprietary */, + EAP_TYPE_SIM = 18 /* draft-haverinen-pppext-eap-sim-12.txt */, + EAP_TYPE_TTLS = 21 /* draft-ietf-pppext-eap-ttls-02.txt */, + EAP_TYPE_AKA = 23 /* draft-arkko-pppext-eap-aka-12.txt */, + EAP_TYPE_PEAP = 25 /* draft-josefsson-pppext-eap-tls-eap-06.txt */, + EAP_TYPE_MSCHAPV2 = 26 /* draft-kamath-pppext-eap-mschapv2-00.txt */, + EAP_TYPE_TLV = 33 /* draft-josefsson-pppext-eap-tls-eap-07.txt */, + EAP_TYPE_FAST = 43 /* draft-cam-winget-eap-fast-00.txt */, + EAP_TYPE_EXPANDED_NAK = 254 /* RFC 3748 */, + EAP_TYPE_PSK = 255 /* EXPERIMENTAL - type not yet allocated + * draft-bersani-eap-psk-03 */ +} EapType; + +#endif /* EAP_DEFS_H */ diff --git a/contrib/wpa_supplicant/eap_fast.c b/contrib/wpa_supplicant/eap_fast.c new file mode 100644 index 000000000000..1c9c3ff2370f --- /dev/null +++ b/contrib/wpa_supplicant/eap_fast.c @@ -0,0 +1,1906 @@ +/* + * WPA Supplicant / EAP-FAST (draft-cam-winget-eap-fast-00.txt) + * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include "common.h" +#include "eap_i.h" +#include "eap_tls_common.h" +#include "wpa_supplicant.h" +#include "config_ssid.h" +#include "tls.h" +#include "eap_tlv.h" +#include "sha1.h" + +/* TODO: + * - encrypt PAC-Key in the PAC file + * - test session resumption and enable it if it interoperates + */ + +#define EAP_FAST_VERSION 1 +#define EAP_FAST_KEY_LEN 64 +#define EAP_FAST_PAC_KEY_LEN 32 + +#define TLS_EXT_PAC_OPAQUE 35 + +static char *pac_file_hdr = "wpa_supplicant EAP-FAST PAC file - version 1"; + + +static void eap_fast_deinit(struct eap_sm *sm, void *priv); + + +#define PAC_TYPE_PAC_KEY 1 +#define PAC_TYPE_PAC_OPAQUE 2 +#define PAC_TYPE_CRED_LIFETIME 3 +#define PAC_TYPE_A_ID 4 +#define PAC_TYPE_I_ID 5 +#define PAC_TYPE_SERVER_PROTECTED_DATA 6 +#define PAC_TYPE_A_ID_INFO 7 +#define PAC_TYPE_PAC_ACKNOWLEDGEMENT 8 +#define PAC_TYPE_PAC_INFO 9 + +struct pac_tlv_hdr { + u16 type; + u16 len; +}; + + +/* draft-cam-winget-eap-fast-00.txt, 6.2 EAP-FAST Authentication Phase 1 */ +struct eap_fast_key_block_auth { + /* RC4-128-SHA */ + u8 client_write_MAC_secret[20]; + u8 server_write_MAC_secret[20]; + u8 client_write_key[16]; + u8 server_write_key[16]; + /* u8 client_write_IV[0]; */ + /* u8 server_write_IV[0]; */ + u8 session_key_seed[40]; +}; + + +/* draft-cam-winget-eap-fast-00.txt, 7.3 EAP-FAST Provisioning Exchange */ +struct eap_fast_key_block_provisioning { + /* AES128-SHA */ + u8 client_write_MAC_secret[20]; + u8 server_write_MAC_secret[20]; + u8 client_write_key[16]; + u8 server_write_key[16]; + u8 client_write_IV[16]; + u8 server_write_IV[16]; + u8 session_key_seed[40]; + u8 server_challenge[16]; + u8 client_challenge[16]; +}; + + +struct eap_fast_pac { + struct eap_fast_pac *next; + + u8 pac_key[EAP_FAST_PAC_KEY_LEN]; + u8 *pac_opaque; + size_t pac_opaque_len; + u8 *pac_info; + size_t pac_info_len; + u8 *a_id; + size_t a_id_len; + u8 *i_id; + size_t i_id_len; + u8 *a_id_info; + size_t a_id_info_len; +}; + + +struct eap_fast_data { + struct eap_ssl_data ssl; + + int fast_version; + + const struct eap_method *phase2_method; + void *phase2_priv; + int phase2_success; + + u8 phase2_type; + u8 *phase2_types; + size_t num_phase2_types; + int resuming; /* starting a resumed session */ + struct eap_fast_key_block_auth *key_block_a; + struct eap_fast_key_block_provisioning *key_block_p; + int provisioning_allowed; /* is PAC provisioning allowed */ + int provisioning; /* doing PAC provisioning (not the normal auth) */ + + u8 key_data[EAP_FAST_KEY_LEN]; + int success; + + struct eap_fast_pac *pac; + struct eap_fast_pac *current_pac; + + int tls_master_secret_set; +}; + + +static void eap_fast_free_pac(struct eap_fast_pac *pac) +{ + free(pac->pac_opaque); + free(pac->pac_info); + free(pac->a_id); + free(pac->i_id); + free(pac->a_id_info); + free(pac); +} + + +static struct eap_fast_pac * eap_fast_get_pac(struct eap_fast_data *data, + const u8 *a_id, size_t a_id_len) +{ + struct eap_fast_pac *pac = data->pac; + + while (pac) { + if (pac->a_id_len == a_id_len && + memcmp(pac->a_id, a_id, a_id_len) == 0) { + return pac; + } + pac = pac->next; + } + return NULL; +} + + +static int eap_fast_add_pac(struct eap_fast_data *data, + struct eap_fast_pac *entry) +{ + struct eap_fast_pac *pac, *prev; + + if (entry == NULL || entry->a_id == NULL) + return -1; + + /* Remove a possible old entry for the matching A-ID. */ + pac = data->pac; + prev = NULL; + while (pac) { + if (pac->a_id_len == entry->a_id_len && + memcmp(pac->a_id, entry->a_id, pac->a_id_len) == 0) { + if (prev == NULL) { + data->pac = pac->next; + } else { + prev->next = pac->next; + } + if (data->current_pac == pac) + data->current_pac = NULL; + eap_fast_free_pac(pac); + break; + } + prev = pac; + pac = pac->next; + } + + /* Allocate a new entry and add it to the list of PACs. */ + pac = malloc(sizeof(*pac)); + if (pac == NULL) + return -1; + + memset(pac, 0, sizeof(*pac)); + memcpy(pac->pac_key, entry->pac_key, EAP_FAST_PAC_KEY_LEN); + if (entry->pac_opaque) { + pac->pac_opaque = malloc(entry->pac_opaque_len); + if (pac->pac_opaque == NULL) { + eap_fast_free_pac(pac); + return -1; + } + memcpy(pac->pac_opaque, entry->pac_opaque, + entry->pac_opaque_len); + pac->pac_opaque_len = entry->pac_opaque_len; + } + if (entry->pac_info) { + pac->pac_info = malloc(entry->pac_info_len); + if (pac->pac_info == NULL) { + eap_fast_free_pac(pac); + return -1; + } + memcpy(pac->pac_info, entry->pac_info, + entry->pac_info_len); + pac->pac_info_len = entry->pac_info_len; + } + if (entry->a_id) { + pac->a_id = malloc(entry->a_id_len); + if (pac->a_id == NULL) { + eap_fast_free_pac(pac); + return -1; + } + memcpy(pac->a_id, entry->a_id, + entry->a_id_len); + pac->a_id_len = entry->a_id_len; + } + if (entry->i_id) { + pac->i_id = malloc(entry->i_id_len); + if (pac->i_id == NULL) { + eap_fast_free_pac(pac); + return -1; + } + memcpy(pac->i_id, entry->i_id, + entry->i_id_len); + pac->i_id_len = entry->i_id_len; + } + if (entry->a_id_info) { + pac->a_id_info = malloc(entry->a_id_info_len); + if (pac->a_id_info == NULL) { + eap_fast_free_pac(pac); + return -1; + } + memcpy(pac->a_id_info, entry->a_id_info, + entry->a_id_info_len); + pac->a_id_info_len = entry->a_id_info_len; + } + pac->next = data->pac; + data->pac = pac; + return 0; +} + + +static int eap_fast_read_line(FILE *f, char *buf, size_t buf_len) +{ + char *pos; + + if (fgets(buf, buf_len, f) == NULL) { + return -1; + } + + buf[buf_len - 1] = '\0'; + pos = buf; + while (*pos != '\0') { + if (*pos == '\n' || *pos == '\r') { + *pos = '\0'; + break; + } + pos++; + } + + return 0; +} + + +static u8 * eap_fast_parse_hex(const char *value, size_t *len) +{ + int hlen; + u8 *buf; + + if (value == NULL) + return NULL; + hlen = strlen(value); + if (hlen & 1) + return NULL; + *len = hlen / 2; + buf = malloc(*len); + if (buf == NULL) + return NULL; + if (hexstr2bin(value, buf, *len)) { + free(buf); + return NULL; + } + return buf; +} + + +static int eap_fast_load_pac(struct eap_fast_data *data, const char *pac_file) +{ + FILE *f; + struct eap_fast_pac *pac = NULL; + int count = 0; + char *buf, *pos; + const int buf_len = 2048; + int ret = 0, line = 0; + + if (pac_file == NULL) + return -1; + + f = fopen(pac_file, "r"); + if (f == NULL) { + wpa_printf(MSG_INFO, "EAP-FAST: No PAC file '%s' - assume no " + "PAC entries have been provisioned", pac_file); + return 0; + } + + buf = malloc(buf_len); + if (buf == NULL) { + return -1; + } + + line++; + if (eap_fast_read_line(f, buf, buf_len) < 0 || + strcmp(pac_file_hdr, buf) != 0) { + wpa_printf(MSG_INFO, "EAP-FAST: Unrecognized header line in " + "PAC file '%s'", pac_file); + free(buf); + fclose(f); + return -1; + } + + while (eap_fast_read_line(f, buf, buf_len) == 0) { + line++; + pos = strchr(buf, '='); + if (pos) { + *pos++ = '\0'; + } + + if (strcmp(buf, "START") == 0) { + if (pac) { + wpa_printf(MSG_INFO, "EAP-FAST: START line " + "without END in '%s:%d'", + pac_file, line); + ret = -1; + break; + } + pac = malloc(sizeof(*pac)); + if (pac == NULL) { + wpa_printf(MSG_INFO, "EAP-FAST: No memory for " + "PAC entry"); + ret = -1; + break; + } + memset(pac, 0, sizeof(*pac)); + } else if (strcmp(buf, "END") == 0) { + if (pac == NULL) { + wpa_printf(MSG_INFO, "EAP-FAST: END line " + "without START in '%s:%d'", + pac_file, line); + ret = -1; + break; + } + pac->next = data->pac; + data->pac = pac; + pac = NULL; + count++; + } else if (pac && strcmp(buf, "PAC-Key") == 0) { + u8 *key; + size_t key_len; + key = eap_fast_parse_hex(pos, &key_len); + if (key == NULL || key_len != EAP_FAST_PAC_KEY_LEN) { + wpa_printf(MSG_INFO, "EAP-FAST: Invalid " + "PAC-Key '%s:%d'", pac_file, line); + ret = -1; + free(key); + break; + } + + memcpy(pac->pac_key, key, EAP_FAST_PAC_KEY_LEN); + free(key); + } else if (pac && strcmp(buf, "PAC-Opaque") == 0) { + pac->pac_opaque = + eap_fast_parse_hex(pos, &pac->pac_opaque_len); + if (pac->pac_opaque == NULL) { + wpa_printf(MSG_INFO, "EAP-FAST: Invalid " + "PAC-Opaque '%s:%d'", + pac_file, line); + ret = -1; + break; + } + } else if (pac && strcmp(buf, "A-ID") == 0) { + pac->a_id = eap_fast_parse_hex(pos, &pac->a_id_len); + if (pac->a_id == NULL) { + wpa_printf(MSG_INFO, "EAP-FAST: Invalid " + "A-ID '%s:%d'", pac_file, line); + ret = -1; + break; + } + } else if (pac && strcmp(buf, "I-ID") == 0) { + pac->i_id = eap_fast_parse_hex(pos, &pac->i_id_len); + if (pac->i_id == NULL) { + wpa_printf(MSG_INFO, "EAP-FAST: Invalid " + "I-ID '%s:%d'", pac_file, line); + ret = -1; + break; + } + } else if (pac && strcmp(buf, "A-ID-Info") == 0) { + pac->a_id_info = + eap_fast_parse_hex(pos, &pac->a_id_info_len); + if (pac->a_id_info == NULL) { + wpa_printf(MSG_INFO, "EAP-FAST: Invalid " + "A-ID-Info '%s:%d'", + pac_file, line); + ret = -1; + break; + } + } + } + + if (pac) { + wpa_printf(MSG_INFO, "EAP-FAST: PAC block not terminated with " + "END in '%s'", pac_file); + eap_fast_free_pac(pac); + ret = -1; + } + + free(buf); + fclose(f); + + if (ret == 0) { + wpa_printf(MSG_DEBUG, "EAP-FAST: read %d PAC entries from " + "'%s'", count, pac_file); + } + + return ret; +} + + +static void eap_fast_write(FILE *f, const char *field, const u8 *data, + size_t len, int txt) +{ + int i; + + if (data == NULL) + return; + + fprintf(f, "%s=", field); + for (i = 0; i < len; i++) { + fprintf(f, "%02x", data[i]); + } + fprintf(f, "\n"); + + if (txt) { + fprintf(f, "%s-txt=", field); + for (i = 0; i < len; i++) { + fprintf(f, "%c", data[i]); + } + fprintf(f, "\n"); + } +} + + +static int eap_fast_save_pac(struct eap_fast_data *data, const char *pac_file) +{ + FILE *f; + struct eap_fast_pac *pac; + int count = 0; + + if (pac_file == NULL) + return -1; + + f = fopen(pac_file, "w"); + if (f == NULL) { + wpa_printf(MSG_INFO, "EAP-FAST: Failed to open PAC file '%s' " + "for writing", pac_file); + return -1; + } + + fprintf(f, "%s\n", pac_file_hdr); + + pac = data->pac; + while (pac) { + fprintf(f, "START\n"); + eap_fast_write(f, "PAC-Key", pac->pac_key, + EAP_FAST_PAC_KEY_LEN, 0); + eap_fast_write(f, "PAC-Opaque", pac->pac_opaque, + pac->pac_opaque_len, 0); + eap_fast_write(f, "PAC-Info", pac->pac_info, + pac->pac_info_len, 0); + eap_fast_write(f, "A-ID", pac->a_id, pac->a_id_len, 0); + eap_fast_write(f, "I-ID", pac->i_id, pac->i_id_len, 1); + eap_fast_write(f, "A-ID-Info", pac->a_id_info, + pac->a_id_info_len, 1); + fprintf(f, "END\n"); + count++; + pac = pac->next; + } + + fclose(f); + + wpa_printf(MSG_DEBUG, "EAP-FAST: wrote %d PAC entries into '%s'", + count, pac_file); + + return 0; +} + + +static void * eap_fast_init(struct eap_sm *sm) +{ + struct eap_fast_data *data; + struct wpa_ssid *config = eap_get_config(sm); + + data = malloc(sizeof(*data)); + if (data == NULL) + return NULL; + memset(data, 0, sizeof(*data)); + data->fast_version = EAP_FAST_VERSION; + + if (config && config->phase1) { + if (strstr(config->phase1, "fast_provisioning=1")) { + data->provisioning_allowed = 1; + wpa_printf(MSG_DEBUG, "EAP-FAST: Automatic PAC " + "provisioning is allowed"); + } + } + + if (config && config->phase2) { + char *start, *pos, *buf; + u8 method, *methods = NULL, *_methods; + size_t num_methods = 0; + start = buf = strdup(config->phase2); + if (buf == NULL) { + eap_fast_deinit(sm, data); + return NULL; + } + while (start && *start != '\0') { + pos = strstr(start, "auth="); + if (pos == NULL) + break; + if (start != pos && *(pos - 1) != ' ') { + start = pos + 5; + continue; + } + + start = pos + 5; + pos = strchr(start, ' '); + if (pos) + *pos++ = '\0'; + method = eap_get_phase2_type(start); + if (method == EAP_TYPE_NONE) { + wpa_printf(MSG_ERROR, "EAP-FAST: Unsupported " + "Phase2 method '%s'", start); + } else { + num_methods++; + _methods = realloc(methods, num_methods); + if (_methods == NULL) { + free(methods); + eap_fast_deinit(sm, data); + return NULL; + } + methods = _methods; + methods[num_methods - 1] = method; + } + + start = pos; + } + free(buf); + data->phase2_types = methods; + data->num_phase2_types = num_methods; + } + if (data->phase2_types == NULL) { + data->phase2_types = + eap_get_phase2_types(config, &data->num_phase2_types); + } + if (data->phase2_types == NULL) { + wpa_printf(MSG_ERROR, "EAP-FAST: No Phase2 method available"); + eap_fast_deinit(sm, data); + return NULL; + } + wpa_hexdump(MSG_DEBUG, "EAP-FAST: Phase2 EAP types", + data->phase2_types, data->num_phase2_types); + data->phase2_type = EAP_TYPE_NONE; + + if (eap_tls_ssl_init(sm, &data->ssl, config)) { + wpa_printf(MSG_INFO, "EAP-FAST: Failed to initialize SSL."); + eap_fast_deinit(sm, data); + return NULL; + } + + /* The local RADIUS server in a Cisco AP does not seem to like empty + * fragments before data, so disable that workaround for CBC. + * TODO: consider making this configurable */ + tls_connection_enable_workaround(sm->ssl_ctx, data->ssl.conn); + + if (eap_fast_load_pac(data, config->pac_file) < 0) { + eap_fast_deinit(sm, data); + return NULL; + } + + if (data->pac == NULL && !data->provisioning_allowed) { + wpa_printf(MSG_INFO, "EAP-FAST: No PAC configured and " + "provisioning disabled"); + eap_fast_deinit(sm, data); + return NULL; + } + + return data; +} + + +static void eap_fast_deinit(struct eap_sm *sm, void *priv) +{ + struct eap_fast_data *data = priv; + struct eap_fast_pac *pac, *prev; + + if (data == NULL) + return; + if (data->phase2_priv && data->phase2_method) + data->phase2_method->deinit(sm, data->phase2_priv); + free(data->phase2_types); + free(data->key_block_a); + free(data->key_block_p); + eap_tls_ssl_deinit(sm, &data->ssl); + + pac = data->pac; + prev = NULL; + while (pac) { + prev = pac; + pac = pac->next; + eap_fast_free_pac(prev); + } + free(data); +} + + +static int eap_fast_encrypt(struct eap_sm *sm, struct eap_fast_data *data, + int id, u8 *plain, size_t plain_len, + u8 **out_data, size_t *out_len) +{ + int res; + u8 *pos; + struct eap_hdr *resp; + + /* TODO: add support for fragmentation, if needed. This will need to + * add TLS Message Length field, if the frame is fragmented. */ + resp = malloc(sizeof(struct eap_hdr) + 2 + data->ssl.tls_out_limit); + if (resp == NULL) + return 0; + + resp->code = EAP_CODE_RESPONSE; + resp->identifier = id; + + pos = (u8 *) (resp + 1); + *pos++ = EAP_TYPE_FAST; + *pos++ = data->fast_version; + + res = tls_connection_encrypt(sm->ssl_ctx, data->ssl.conn, + plain, plain_len, + pos, data->ssl.tls_out_limit); + if (res < 0) { + wpa_printf(MSG_INFO, "EAP-FAST: Failed to encrypt Phase 2 " + "data"); + free(resp); + return 0; + } + + *out_len = sizeof(struct eap_hdr) + 2 + res; + resp->length = host_to_be16(*out_len); + *out_data = (u8 *) resp; + return 0; +} + + +static int eap_fast_phase2_nak(struct eap_sm *sm, + struct eap_fast_data *data, + struct eap_hdr *hdr, + u8 **resp, size_t *resp_len) +{ + struct eap_hdr *resp_hdr; + u8 *pos = (u8 *) (hdr + 1); + + wpa_printf(MSG_DEBUG, "EAP-FAST: Phase 2 Request: Nak type=%d", *pos); + wpa_hexdump(MSG_DEBUG, "EAP-FAST: Allowed Phase2 EAP types", + data->phase2_types, data->num_phase2_types); + *resp_len = sizeof(struct eap_hdr) + 1 + data->num_phase2_types; + *resp = malloc(*resp_len); + if (*resp == NULL) + return -1; + + resp_hdr = (struct eap_hdr *) (*resp); + resp_hdr->code = EAP_CODE_RESPONSE; + resp_hdr->identifier = hdr->identifier; + resp_hdr->length = host_to_be16(*resp_len); + pos = (u8 *) (resp_hdr + 1); + *pos++ = EAP_TYPE_NAK; + memcpy(pos, data->phase2_types, data->num_phase2_types); + + return 0; +} + + +static int eap_fast_derive_msk(struct eap_sm *sm, struct eap_fast_data *data) +{ + u8 isk[32]; + u8 imck[60]; + + if (data->key_block_a == NULL) + return -1; + + memset(isk, 0, sizeof(isk)); + sha1_t_prf(data->key_block_a->session_key_seed, + sizeof(data->key_block_a->session_key_seed), + "Inner Methods Compound Keys", + isk, sizeof(isk), imck, sizeof(imck)); + sha1_t_prf(imck, 40, "Session Key Generating Function", (u8 *) "", 0, + data->key_data, EAP_FAST_KEY_LEN); + + wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: Derived key (MSK)", + data->key_data, EAP_FAST_KEY_LEN); + + data->success = 1; + + return 0; +} + + +static int eap_fast_set_tls_master_secret(struct eap_sm *sm, + struct eap_fast_data *data, + const u8 *tls, size_t tls_len) +{ + struct tls_keys keys; + u8 master_secret[48], *seed; + const u8 *server_random; + size_t seed_len, server_random_len; + + if (data->tls_master_secret_set || !data->current_pac || + tls_connection_get_keys(sm->ssl_ctx, data->ssl.conn, &keys)) { + return 0; + } + + wpa_hexdump(MSG_DEBUG, "EAP-FAST: client_random", + keys.client_random, keys.client_random_len); + + /* TLS master secret is needed before TLS library has processed this + * message which includes both ServerHello and an encrypted handshake + * message, so we need to parse server_random from this message before + * passing it to TLS library. + * + * Example TLS packet header: + * (16 03 01 00 2a 02 00 00 26 03 01 <32 bytes server_random>) + * Content Type: Handshake: 0x16 + * Version: TLS 1.0 (0x0301) + * Lenghth: 42 (0x002a) + * Handshake Type: Server Hello: 0x02 + * Length: 38 (0x000026) + * Version TLS 1.0 (0x0301) + * Random: 32 bytes + */ + if (tls_len < 43 || tls[0] != 0x16 || + tls[1] != 0x03 || tls[2] != 0x01 || + tls[5] != 0x02 || tls[9] != 0x03 || tls[10] != 0x01) { + wpa_hexdump(MSG_DEBUG, "EAP-FAST: unrecognized TLS " + "ServerHello", tls, tls_len); + return -1; + } + server_random = tls + 11; + server_random_len = 32; + wpa_hexdump(MSG_DEBUG, "EAP-FAST: server_random", + server_random, server_random_len); + + + seed_len = keys.client_random_len + server_random_len; + seed = malloc(seed_len); + if (seed == NULL) + return -1; + memcpy(seed, server_random, server_random_len); + memcpy(seed + server_random_len, + keys.client_random, keys.client_random_len); + + wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: T-PRF seed", seed, seed_len); + wpa_hexdump_key(MSG_MSGDUMP, "EAP-FAST: PAC-Key", + data->current_pac->pac_key, EAP_FAST_PAC_KEY_LEN); + /* master_secret = T-PRF(PAC-Key, "PAC to master secret label hash", + * server_random + client_random, 48) */ + sha1_t_prf(data->current_pac->pac_key, EAP_FAST_PAC_KEY_LEN, + "PAC to master secret label hash", + seed, seed_len, master_secret, sizeof(master_secret)); + free(seed); + wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: TLS pre-master-secret", + master_secret, sizeof(master_secret)); + + data->tls_master_secret_set = 1; + + return tls_connection_set_master_key(sm->ssl_ctx, data->ssl.conn, + master_secret, + sizeof(master_secret)); +} + + +static u8 * eap_fast_derive_key(struct eap_sm *sm, struct eap_ssl_data *data, + char *label, size_t len) +{ + struct tls_keys keys; + u8 *random; + u8 *out; + + if (tls_connection_get_keys(sm->ssl_ctx, data->conn, &keys)) + return NULL; + out = malloc(len); + random = malloc(keys.client_random_len + keys.server_random_len); + if (out == NULL || random == NULL) { + free(out); + free(random); + return NULL; + } + memcpy(random, keys.server_random, keys.server_random_len); + memcpy(random + keys.server_random_len, keys.client_random, + keys.client_random_len); + + wpa_hexdump_key(MSG_MSGDUMP, "EAP-FAST: master_secret for key " + "expansion", keys.master_key, keys.master_key_len); + if (tls_prf(keys.master_key, keys.master_key_len, + label, random, keys.client_random_len + + keys.server_random_len, out, len)) { + free(random); + free(out); + return NULL; + } + free(random); + return out; +} + + +static void eap_fast_derive_key_auth(struct eap_sm *sm, + struct eap_fast_data *data) +{ + free(data->key_block_a); + data->key_block_a = (struct eap_fast_key_block_auth *) + eap_fast_derive_key(sm, &data->ssl, "key expansion", + sizeof(*data->key_block_a)); + wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: session_key_seed", + data->key_block_a->session_key_seed, + sizeof(data->key_block_a->session_key_seed)); +} + + +static void eap_fast_derive_key_provisioning(struct eap_sm *sm, + struct eap_fast_data *data) +{ + free(data->key_block_p); + data->key_block_p = (struct eap_fast_key_block_provisioning *) + eap_fast_derive_key(sm, &data->ssl, "key expansion", + sizeof(*data->key_block_p)); + if (data->key_block_p == NULL) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to derive key block"); + return; + } + wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: session_key_seed", + data->key_block_p->session_key_seed, + sizeof(data->key_block_p->session_key_seed)); + wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: server_challenge", + data->key_block_p->server_challenge, + sizeof(data->key_block_p->server_challenge)); + wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: client_challenge", + data->key_block_p->client_challenge, + sizeof(data->key_block_p->client_challenge)); +} + + +static void eap_fast_derive_keys(struct eap_sm *sm, struct eap_fast_data *data) +{ + if (data->current_pac) { + eap_fast_derive_key_auth(sm, data); + } else { + eap_fast_derive_key_provisioning(sm, data); + } +} + + +static int eap_fast_phase2_request(struct eap_sm *sm, + struct eap_fast_data *data, + struct eap_method_ret *ret, + struct eap_hdr *req, + struct eap_hdr *hdr, + u8 **resp, size_t *resp_len) +{ + size_t len = be_to_host16(hdr->length); + u8 *pos; + struct eap_method_ret iret; + + if (len <= sizeof(struct eap_hdr)) { + wpa_printf(MSG_INFO, "EAP-FAST: too short " + "Phase 2 request (len=%lu)", (unsigned long) len); + return -1; + } + pos = (u8 *) (hdr + 1); + wpa_printf(MSG_DEBUG, "EAP-FAST: Phase 2 Request: type=%d", *pos); + switch (*pos) { + case EAP_TYPE_IDENTITY: + *resp = eap_sm_buildIdentity(sm, req->identifier, resp_len, 1); + break; + default: + if (data->phase2_type == EAP_TYPE_NONE) { + int i; + for (i = 0; i < data->num_phase2_types; i++) { + if (data->phase2_types[i] != *pos) + continue; + + data->phase2_type = *pos; + wpa_printf(MSG_DEBUG, "EAP-FAST: Selected " + "Phase 2 EAP method %d", + data->phase2_type); + break; + } + } + if (*pos != data->phase2_type || *pos == EAP_TYPE_NONE) { + if (eap_fast_phase2_nak(sm, data, hdr, resp, resp_len)) + return -1; + return 0; + } + + if (data->phase2_priv == NULL) { + data->phase2_method = eap_sm_get_eap_methods(*pos); + if (data->phase2_method) { + if (data->key_block_p) { + sm->auth_challenge = + data->key_block_p-> + server_challenge; + sm->peer_challenge = + data->key_block_p-> + client_challenge; + } + sm->init_phase2 = 1; + data->phase2_priv = + data->phase2_method->init(sm); + sm->init_phase2 = 0; + sm->auth_challenge = NULL; + sm->peer_challenge = NULL; + } + } + if (data->phase2_priv == NULL || data->phase2_method == NULL) { + wpa_printf(MSG_INFO, "EAP-FAST: failed to initialize " + "Phase 2 EAP method %d", *pos); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + return -1; + } + memset(&iret, 0, sizeof(iret)); + *resp = data->phase2_method->process(sm, data->phase2_priv, + &iret, (u8 *) hdr, len, + resp_len); + if (*resp == NULL || + (iret.methodState == METHOD_DONE && + iret.decision == DECISION_FAIL)) { + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + } else if ((iret.methodState == METHOD_DONE || + iret.methodState == METHOD_MAY_CONT) && + (iret.decision == DECISION_UNCOND_SUCC || + iret.decision == DECISION_COND_SUCC)) { + data->phase2_success = 1; + } + if (*resp == NULL) + return -1; + break; + } + return 0; +} + + +static u8 * eap_fast_tlv_nak(int vendor_id, int tlv_type, size_t *len) +{ + struct eap_tlv_nak_tlv *nak; + *len = sizeof(*nak); + nak = malloc(*len); + if (nak == NULL) + return NULL; + nak->tlv_type = host_to_be16(EAP_TLV_TYPE_MANDATORY | EAP_TLV_NAK_TLV); + nak->length = host_to_be16(6); + nak->vendor_id = host_to_be32(vendor_id); + nak->nak_type = host_to_be16(tlv_type); + return (u8 *) nak; +} + + +static u8 * eap_fast_tlv_result(int status, int intermediate, size_t *len) +{ + struct eap_tlv_intermediate_result_tlv *result; + *len = sizeof(*result); + result = malloc(*len); + if (result == NULL) + return NULL; + result->tlv_type = host_to_be16(EAP_TLV_TYPE_MANDATORY | + (intermediate ? + EAP_TLV_INTERMEDIATE_RESULT_TLV : + EAP_TLV_RESULT_TLV)); + result->length = host_to_be16(2); + result->status = host_to_be16(status); + return (u8 *) result; +} + + +static u8 * eap_fast_tlv_pac_ack(size_t *len) +{ + struct eap_tlv_result_tlv *res; + struct eap_tlv_pac_ack_tlv *ack; + + *len = sizeof(*res) + sizeof(*ack); + res = malloc(*len); + if (res == NULL) + return NULL; + + memset(res, 0, *len); + res->tlv_type = host_to_be16(EAP_TLV_RESULT_TLV | + EAP_TLV_TYPE_MANDATORY); + res->length = host_to_be16(sizeof(*res) - sizeof(struct eap_tlv_hdr)); + res->status = host_to_be16(EAP_TLV_RESULT_SUCCESS); + + ack = (struct eap_tlv_pac_ack_tlv *) (res + 1); + ack->tlv_type = host_to_be16(EAP_TLV_PAC_TLV | + EAP_TLV_TYPE_MANDATORY); + ack->length = host_to_be16(sizeof(*ack) - sizeof(struct eap_tlv_hdr)); + ack->pac_type = host_to_be16(PAC_TYPE_PAC_ACKNOWLEDGEMENT); + ack->pac_len = host_to_be16(2); + ack->result = host_to_be16(EAP_TLV_RESULT_SUCCESS); + + return (u8 *) res; +} + + +static u8 * eap_fast_tlv_eap_payload(u8 *buf, size_t *len) +{ + struct eap_tlv_hdr *tlv; + + /* Encapsulate EAP packet in EAP Payload TLV */ + tlv = malloc(sizeof(*tlv) + *len); + if (tlv == NULL) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to " + "allocate memory for TLV " + "encapsulation"); + free(buf); + return NULL; + } + tlv->tlv_type = host_to_be16(EAP_TLV_TYPE_MANDATORY | + EAP_TLV_EAP_PAYLOAD_TLV); + tlv->length = host_to_be16(*len); + memcpy(tlv + 1, buf, *len); + free(buf); + *len += sizeof(*tlv); + return (u8 *) tlv; +} + + +static u8 * eap_fast_process_crypto_binding( + struct eap_sm *sm, struct eap_fast_data *data, + struct eap_method_ret *ret, + struct eap_tlv_crypto_binding__tlv *bind, size_t bind_len, + size_t *resp_len, int final) +{ + u8 *resp, *sks = NULL; + struct eap_tlv_intermediate_result_tlv *rresult; + struct eap_tlv_crypto_binding__tlv *rbind; + u8 isk[32], imck[60], *cmk, cmac[20], *key; + size_t key_len; + int res; + + wpa_printf(MSG_DEBUG, "EAP-FAST: Crypto-Binding TLV: Version %d " + "Received Version %d SubType %d", + bind->version, bind->received_version, bind->subtype); + wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: NONCE", + bind->nonce, sizeof(bind->nonce)); + wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Compound MAC", + bind->compound_mac, sizeof(bind->compound_mac)); + + if (bind->version != EAP_FAST_VERSION || + bind->received_version != EAP_FAST_VERSION || + bind->subtype != EAP_TLV_CRYPTO_BINDING_SUBTYPE_REQUEST) { + wpa_printf(MSG_INFO, "EAP-FAST: Invalid version/subtype in " + "Crypto-Binding TLV: Version %d " + "Received Version %d SubType %d", + bind->version, bind->received_version, + bind->subtype); + resp = eap_fast_tlv_result(EAP_TLV_RESULT_FAILURE, 1, + resp_len); + return resp; + } + + + if (data->provisioning) { + if (data->key_block_p) { + sks = data->key_block_p->session_key_seed; + } + } else { + if (data->key_block_a) { + sks = data->key_block_a->session_key_seed; + } + } + if (sks == NULL) { + wpa_printf(MSG_INFO, "EAP-FAST: No Session Key Seed available " + "for processing Crypto-Binding TLV"); + return NULL; + } + + wpa_printf(MSG_DEBUG, "EAP-FAST: Determining CMK for Compound MIC " + "calculation"); + wpa_hexdump_key(MSG_MSGDUMP, "EAP-FAST: S-IMCK[0] = SKS", sks, 40); + + memset(isk, 0, sizeof(isk)); + if (data->phase2_method == NULL || data->phase2_priv == NULL) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Phase 2 method not " + "available"); + return NULL; + } + if (data->phase2_method->isKeyAvailable && data->phase2_method->getKey) + { + if (!data->phase2_method->isKeyAvailable(sm, data->phase2_priv) + || + (key = data->phase2_method->getKey(sm, data->phase2_priv, + &key_len)) == NULL) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Could not get key " + "material from Phase 2"); + return NULL; + } + if (key_len > sizeof(isk)) + key_len = sizeof(isk); + /* FIX: which end is being padded? */ +#if 0 + memcpy(isk + (sizeof(isk) - key_len), key, key_len); +#else + memcpy(isk, key, key_len); +#endif + free(key); + } + wpa_hexdump_key(MSG_MSGDUMP, "EAP-FAST: ISK[0]", isk, sizeof(isk)); + sha1_t_prf(sks, 40, "Inner Methods Compound Keys", + isk, sizeof(isk), imck, sizeof(imck)); + /* S-IMCK[1] = imkc[0..39] */ + wpa_hexdump_key(MSG_MSGDUMP, "EAP-FAST: S-IMCK[1]", imck, 40); + cmk = imck + 40; + wpa_hexdump_key(MSG_MSGDUMP, "EAP-FAST: CMK", cmk, 20); + + memcpy(cmac, bind->compound_mac, sizeof(cmac)); + memset(bind->compound_mac, 0, sizeof(cmac)); + wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Crypto-Binding TLV for Compound " + "MAC calculation", (u8 *) bind, bind_len); + hmac_sha1(cmk, 20, (u8 *) bind, bind_len, bind->compound_mac); + res = memcmp(cmac, bind->compound_mac, sizeof(cmac)); + wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Received Compound MAC", + cmac, sizeof(cmac)); + wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Calculated Compound MAC", + bind->compound_mac, sizeof(cmac)); + if (res != 0) { + wpa_printf(MSG_INFO, "EAP-FAST: Compound MAC did not match"); + resp = eap_fast_tlv_result(EAP_TLV_RESULT_FAILURE, 1, + resp_len); + memcpy(bind->compound_mac, cmac, sizeof(cmac)); + return resp; + } + + *resp_len = sizeof(*rresult) + sizeof(*rbind); + resp = malloc(*resp_len); + if (resp == NULL) + return NULL; + memset(resp, 0, *resp_len); + + /* Both intermediate and final Result TLVs are identical, so ok to use + * the same structure definition for them. */ + rresult = (struct eap_tlv_intermediate_result_tlv *) resp; + rresult->tlv_type = host_to_be16(EAP_TLV_TYPE_MANDATORY | + (final ? EAP_TLV_RESULT_TLV : + EAP_TLV_INTERMEDIATE_RESULT_TLV)); + rresult->length = host_to_be16(2); + rresult->status = host_to_be16(EAP_TLV_RESULT_SUCCESS); + + if (!data->provisioning && data->phase2_success && + eap_fast_derive_msk(sm, data) < 0) { + wpa_printf(MSG_INFO, "EAP-FAST: Failed to generate MSK"); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + rresult->status = host_to_be16(EAP_TLV_RESULT_FAILURE); + data->phase2_success = 0; + } + + rbind = (struct eap_tlv_crypto_binding__tlv *) (rresult + 1); + rbind->tlv_type = host_to_be16(EAP_TLV_TYPE_MANDATORY | + EAP_TLV_CRYPTO_BINDING_TLV_); + rbind->length = host_to_be16(sizeof(*rbind) - + sizeof(struct eap_tlv_hdr)); + rbind->version = EAP_FAST_VERSION; + rbind->received_version = bind->version; + rbind->subtype = EAP_TLV_CRYPTO_BINDING_SUBTYPE_RESPONSE; + memcpy(rbind->nonce, bind->nonce, sizeof(bind->nonce)); + inc_byte_array(rbind->nonce, sizeof(bind->nonce)); + hmac_sha1(cmk, 20, (u8 *) rbind, sizeof(*rbind), rbind->compound_mac); + + wpa_printf(MSG_DEBUG, "EAP-FAST: Reply Crypto-Binding TLV: Version %d " + "Received Version %d SubType %d", + rbind->version, rbind->received_version, rbind->subtype); + wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: NONCE", + rbind->nonce, sizeof(rbind->nonce)); + wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Compound MAC", + rbind->compound_mac, sizeof(rbind->compound_mac)); + + if (final && data->phase2_success) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Authentication completed " + "successfully."); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_UNCOND_SUCC; + } + + return resp; +} + + +static u8 * eap_fast_process_pac(struct eap_sm *sm, struct eap_fast_data *data, + struct eap_method_ret *ret, + u8 *pac, size_t pac_len, size_t *resp_len) +{ + struct wpa_ssid *config = eap_get_config(sm); + struct pac_tlv_hdr *hdr; + u8 *pos; + size_t left, len; + int type, pac_key_found = 0; + struct eap_fast_pac entry; + + memset(&entry, 0, sizeof(entry)); + pos = pac; + left = pac_len; + while (left > sizeof(*hdr)) { + hdr = (struct pac_tlv_hdr *) pos; + type = be_to_host16(hdr->type); + len = be_to_host16(hdr->len); + pos += sizeof(*hdr); + left -= sizeof(*hdr); + if (len > left) { + wpa_printf(MSG_DEBUG, "EAP-FAST: PAC TLV overrun " + "(type=%d len=%lu left=%lu)", + type, (unsigned long) len, + (unsigned long) left); + return eap_fast_tlv_result(EAP_TLV_RESULT_FAILURE, 0, + resp_len); + } + switch (type) { + case PAC_TYPE_PAC_KEY: + wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: PAC-Key", + pos, len); + if (len != EAP_FAST_PAC_KEY_LEN) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Invalid " + "PAC-Key length %lu", + (unsigned long) len); + break; + } + pac_key_found = 1; + memcpy(entry.pac_key, pos, len); + break; + case PAC_TYPE_PAC_OPAQUE: + wpa_hexdump(MSG_DEBUG, "EAP-FAST: PAC-Opaque", + pos, len); + entry.pac_opaque = pos; + entry.pac_opaque_len = len; + break; + case PAC_TYPE_PAC_INFO: + wpa_hexdump(MSG_DEBUG, "EAP-FAST: PAC-Info", + pos, len); + entry.pac_info = pos; + entry.pac_info_len = len; + break; + default: + wpa_printf(MSG_DEBUG, "EAP-FAST: Ignored unknown PAC " + "type %d", type); + break; + } + + pos += len; + left -= len; + } + + if (!pac_key_found || !entry.pac_opaque || !entry.pac_info) { + wpa_printf(MSG_DEBUG, "EAP-FAST: PAC TLV does not include " + "all the required fields"); + return eap_fast_tlv_result(EAP_TLV_RESULT_FAILURE, 0, + resp_len); + } + + pos = entry.pac_info; + left = entry.pac_info_len; + while (left > sizeof(*hdr)) { + hdr = (struct pac_tlv_hdr *) pos; + type = be_to_host16(hdr->type); + len = be_to_host16(hdr->len); + pos += sizeof(*hdr); + left -= sizeof(*hdr); + if (len > left) { + wpa_printf(MSG_DEBUG, "EAP-FAST: PAC-Info overrun " + "(type=%d len=%lu left=%lu)", + type, (unsigned long) len, + (unsigned long) left); + return eap_fast_tlv_result(EAP_TLV_RESULT_FAILURE, 0, + resp_len); + } + switch (type) { + case PAC_TYPE_A_ID: + wpa_hexdump_ascii(MSG_DEBUG, "EAP-FAST: PAC-Info - " + "A-ID", pos, len); + entry.a_id = pos; + entry.a_id_len = len; + break; + case PAC_TYPE_I_ID: + wpa_hexdump_ascii(MSG_DEBUG, "EAP-FAST: PAC-Info - " + "I-ID", pos, len); + entry.i_id = pos; + entry.i_id_len = len; + break; + case PAC_TYPE_A_ID_INFO: + wpa_hexdump_ascii(MSG_DEBUG, "EAP-FAST: PAC-Info - " + "A-ID-Info", pos, len); + entry.a_id_info = pos; + entry.a_id_info_len = len; + break; + default: + wpa_printf(MSG_DEBUG, "EAP-FAST: Ignored unknown " + "PAC-Info type %d", type); + break; + } + + pos += len; + left -= len; + } + + if (entry.a_id == NULL || entry.a_id_info == NULL) { + wpa_printf(MSG_DEBUG, "EAP-FAST: PAC-Info does not include " + "all the required fields"); + return eap_fast_tlv_result(EAP_TLV_RESULT_FAILURE, 0, + resp_len); + } + + eap_fast_add_pac(data, &entry); + eap_fast_save_pac(data, config->pac_file); + + if (data->provisioning) { + /* EAP-FAST provisioning does not provide keying material and + * must end with an EAP-Failure. Authentication will be done + * separately after this. */ + data->success = 0; + ret->decision = DECISION_FAIL; + wpa_printf(MSG_DEBUG, "EAP-FAST: Send PAC-Acknowledgement TLV " + "- Provisioning completed successfully"); + } else { + /* This is PAC refreshing, i.e., normal authentication that is + * expected to be completed with an EAP-Success. */ + wpa_printf(MSG_DEBUG, "EAP-FAST: Send PAC-Acknowledgement TLV " + "- PAC refreshing completed successfully"); + ret->decision = DECISION_UNCOND_SUCC; + } + ret->methodState = METHOD_DONE; + return eap_fast_tlv_pac_ack(resp_len); +} + + +static int eap_fast_decrypt(struct eap_sm *sm, struct eap_fast_data *data, + struct eap_method_ret *ret, struct eap_hdr *req, + u8 *in_data, size_t in_len, + u8 **out_data, size_t *out_len) +{ + u8 *in_decrypted, *pos, *end; + int buf_len, len_decrypted, len, res; + struct eap_hdr *hdr; + u8 *resp = NULL; + size_t resp_len; + int mandatory, tlv_type; + u8 *eap_payload_tlv = NULL, *pac = NULL; + size_t eap_payload_tlv_len = 0, pac_len = 0; + int iresult = 0, result = 0; + struct eap_tlv_crypto_binding__tlv *crypto_binding = NULL; + size_t crypto_binding_len = 0; + + wpa_printf(MSG_DEBUG, "EAP-FAST: received %lu bytes encrypted data for" + " Phase 2", (unsigned long) in_len); + + res = eap_tls_data_reassemble(sm, &data->ssl, &in_data, &in_len); + if (res < 0 || res == 1) + return res; + + buf_len = in_len; + if (data->ssl.tls_in_total > buf_len) + buf_len = data->ssl.tls_in_total; + in_decrypted = malloc(buf_len); + if (in_decrypted == NULL) { + free(data->ssl.tls_in); + data->ssl.tls_in = NULL; + data->ssl.tls_in_len = 0; + wpa_printf(MSG_WARNING, "EAP-FAST: failed to allocate memory " + "for decryption"); + return -1; + } + + len_decrypted = tls_connection_decrypt(sm->ssl_ctx, data->ssl.conn, + in_data, in_len, + in_decrypted, buf_len); + free(data->ssl.tls_in); + data->ssl.tls_in = NULL; + data->ssl.tls_in_len = 0; + if (len_decrypted < 0) { + wpa_printf(MSG_INFO, "EAP-FAST: Failed to decrypt Phase 2 " + "data"); + free(in_decrypted); + return -1; + } + + wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Decrypted Phase 2 TLV(s)", + in_decrypted, len_decrypted); + + if (len_decrypted < 4) { + free(in_decrypted); + wpa_printf(MSG_INFO, "EAP-FAST: Too short Phase 2 " + "TLV frame (len=%d)", len_decrypted); + return -1; + } + + pos = in_decrypted; + end = in_decrypted + len_decrypted; + while (pos < end) { + mandatory = pos[0] & 0x80; + tlv_type = ((pos[0] & 0x3f) << 8) | pos[1]; + len = (pos[2] << 8) | pos[3]; + pos += 4; + if (pos + len > end) { + free(in_decrypted); + wpa_printf(MSG_INFO, "EAP-FAST: TLV overflow"); + return 0; + } + wpa_printf(MSG_DEBUG, "EAP-FAST: received Phase 2: " + "TLV type %d length %d%s", + tlv_type, len, mandatory ? " (mandatory)" : ""); + + switch (tlv_type) { + case EAP_TLV_EAP_PAYLOAD_TLV: + wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: EAP Payload TLV", + pos, len); + eap_payload_tlv = pos; + eap_payload_tlv_len = len; + break; + case EAP_TLV_RESULT_TLV: + wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Result TLV", + pos, len); + if (len < 2) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Too short " + "Result TLV"); + result = EAP_TLV_RESULT_FAILURE; + break; + } + result = (pos[0] << 16) | pos[1]; + if (result != EAP_TLV_RESULT_SUCCESS && + result != EAP_TLV_RESULT_FAILURE) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Unknown " + "Result %d", result); + result = EAP_TLV_RESULT_FAILURE; + } + wpa_printf(MSG_DEBUG, "EAP-FAST: Result: %s", + result == EAP_TLV_RESULT_SUCCESS ? + "Success" : "Failure"); + break; + case EAP_TLV_INTERMEDIATE_RESULT_TLV: + wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Intermediate " + "Result TLV", pos, len); + if (len < 2) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Too short " + "Intermediate Result TLV"); + iresult = EAP_TLV_RESULT_FAILURE; + break; + } + iresult = (pos[0] << 16) | pos[1]; + if (iresult != EAP_TLV_RESULT_SUCCESS && + iresult != EAP_TLV_RESULT_FAILURE) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Unknown " + "Intermediate Result %d", iresult); + iresult = EAP_TLV_RESULT_FAILURE; + } + wpa_printf(MSG_DEBUG, + "EAP-FAST: Intermediate Result: %s", + iresult == EAP_TLV_RESULT_SUCCESS ? + "Success" : "Failure"); + break; + case EAP_TLV_CRYPTO_BINDING_TLV_: + wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Crypto-Binding " + "TLV", pos, len); + crypto_binding_len = sizeof(struct eap_tlv_hdr) + len; + if (crypto_binding_len < sizeof(*crypto_binding)) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Too short " + "Crypto-Binding TLV"); + iresult = EAP_TLV_RESULT_FAILURE; + pos = end; + break; + } + crypto_binding = + (struct eap_tlv_crypto_binding__tlv *) + (pos - sizeof(struct eap_tlv_hdr)); + break; + case EAP_TLV_PAC_TLV: + wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: PAC TLV", + pos, len); + pac = pos; + pac_len = len; + break; + default: + if (mandatory) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Nak unknown " + "mandatory TLV type %d", tlv_type); + resp = eap_fast_tlv_nak(0, tlv_type, + &resp_len); + pos = end; + } else { + wpa_printf(MSG_DEBUG, "EAP-FAST: ignored " + "unknown optional TLV type %d", + tlv_type); + } + break; + } + + pos += len; + } + + if (!resp && result == EAP_TLV_RESULT_FAILURE) { + resp = eap_fast_tlv_result(EAP_TLV_RESULT_FAILURE, 0, + &resp_len); + if (!resp) { + free(in_decrypted); + return 0; + } + } + + if (!resp && iresult == EAP_TLV_RESULT_FAILURE) { + resp = eap_fast_tlv_result(EAP_TLV_RESULT_FAILURE, 1, + &resp_len); + if (!resp) { + free(in_decrypted); + return 0; + } + } + + if (!resp && eap_payload_tlv) { + if (eap_payload_tlv_len < sizeof(*hdr)) { + wpa_printf(MSG_DEBUG, "EAP-FAST: too short EAP " + "Payload TLV (len=%lu)", + (unsigned long) eap_payload_tlv_len); + free(in_decrypted); + return 0; + } + hdr = (struct eap_hdr *) eap_payload_tlv; + if (be_to_host16(hdr->length) > eap_payload_tlv_len) { + wpa_printf(MSG_DEBUG, "EAP-FAST: EAP packet overflow " + "in EAP Payload TLV"); + } + if (hdr->code == EAP_CODE_REQUEST) { + if (eap_fast_phase2_request(sm, data, ret, req, hdr, + &resp, &resp_len)) { + free(in_decrypted); + wpa_printf(MSG_INFO, "EAP-FAST: Phase2 " + "Request processing failed"); + return 0; + } + resp = eap_fast_tlv_eap_payload(resp, &resp_len); + if (resp == NULL) { + free(in_decrypted); + return 0; + } + } else { + wpa_printf(MSG_INFO, "EAP-FAST: Unexpected code=%d in " + "Phase 2 EAP header", hdr->code); + free(in_decrypted); + return 0; + } + } + + if (!resp && crypto_binding) { + int final = result == EAP_TLV_RESULT_SUCCESS; + resp = eap_fast_process_crypto_binding(sm, data, ret, + crypto_binding, + crypto_binding_len, + &resp_len, final); + if (!resp) { + free(in_decrypted); + return 0; + } + } + + if (!resp && pac && result != EAP_TLV_RESULT_SUCCESS) { + wpa_printf(MSG_DEBUG, "EAP-FAST: PAC TLV without Result TLV " + "acnowledging success"); + resp = eap_fast_tlv_result(EAP_TLV_RESULT_FAILURE, 0, + &resp_len); + if (!resp) { + free(in_decrypted); + return 0; + } + } + + if (!resp && pac && result == EAP_TLV_RESULT_SUCCESS) { + resp = eap_fast_process_pac(sm, data, ret, pac, pac_len, + &resp_len); + if (!resp) { + free(in_decrypted); + return 0; + } + } + + free(in_decrypted); + + if (resp == NULL) { + wpa_printf(MSG_DEBUG, "EAP-FAST: No recognized TLVs - send " + "empty response packet"); + resp = malloc(0); + if (resp == NULL) + return 0; + resp_len = 0; + } + + wpa_hexdump(MSG_DEBUG, "EAP-FAST: Encrypting Phase 2 data", + resp, resp_len); + if (eap_fast_encrypt(sm, data, req->identifier, resp, resp_len, + out_data, out_len)) { + wpa_printf(MSG_INFO, "EAP-FAST: Failed to encrypt a Phase 2 " + "frame"); + } + free(resp); + + return 0; +} + + +static u8 * eap_fast_process(struct eap_sm *sm, void *priv, + struct eap_method_ret *ret, + u8 *reqData, size_t reqDataLen, + size_t *respDataLen) +{ + struct eap_hdr *req; + int left, res; + unsigned int tls_msg_len; + u8 flags, *pos, *resp, id; + struct eap_fast_data *data = priv; + + if (tls_get_errors(sm->ssl_ctx)) { + wpa_printf(MSG_INFO, "EAP-FAST: TLS errors detected"); + ret->ignore = TRUE; + return NULL; + } + + req = (struct eap_hdr *) reqData; + pos = (u8 *) (req + 1); + if (reqDataLen < sizeof(*req) + 2 || *pos != EAP_TYPE_FAST || + (left = host_to_be16(req->length)) > reqDataLen) { + wpa_printf(MSG_INFO, "EAP-FAST: Invalid frame"); + ret->ignore = TRUE; + return NULL; + } + left -= sizeof(struct eap_hdr); + id = req->identifier; + pos++; + flags = *pos++; + left -= 2; + wpa_printf(MSG_DEBUG, "EAP-FAST: Received packet(len=%lu) - " + "Flags 0x%02x", (unsigned long) reqDataLen, flags); + if (flags & EAP_TLS_FLAGS_LENGTH_INCLUDED) { + if (left < 4) { + wpa_printf(MSG_INFO, "EAP-FAST: Short frame with TLS " + "length"); + ret->ignore = TRUE; + return NULL; + } + tls_msg_len = (pos[0] << 24) | (pos[1] << 16) | (pos[2] << 8) | + pos[3]; + wpa_printf(MSG_DEBUG, "EAP-FAST: TLS Message Length: %d", + tls_msg_len); + if (data->ssl.tls_in_left == 0) { + data->ssl.tls_in_total = tls_msg_len; + data->ssl.tls_in_left = tls_msg_len; + free(data->ssl.tls_in); + data->ssl.tls_in = NULL; + data->ssl.tls_in_len = 0; + } + pos += 4; + left -= 4; + } + + ret->ignore = FALSE; + ret->methodState = METHOD_CONT; + ret->decision = DECISION_FAIL; + ret->allowNotifications = TRUE; + + if (flags & EAP_TLS_FLAGS_START) { + u8 *a_id; + size_t a_id_len; + struct pac_tlv_hdr *hdr; + + wpa_printf(MSG_DEBUG, "EAP-FAST: Start (server ver=%d, own " + "ver=%d)", flags & EAP_PEAP_VERSION_MASK, + data->fast_version); + if ((flags & EAP_PEAP_VERSION_MASK) < data->fast_version) + data->fast_version = flags & EAP_PEAP_VERSION_MASK; + wpa_printf(MSG_DEBUG, "EAP-FAST: Using FAST version %d", + data->fast_version); + + a_id = pos; + a_id_len = left; + if (left > sizeof(*hdr)) { + int tlen; + hdr = (struct pac_tlv_hdr *) pos; + tlen = be_to_host16(hdr->len); + if (be_to_host16(hdr->type) == PAC_TYPE_A_ID && + sizeof(*hdr) + tlen <= left) { + a_id = (u8 *) (hdr + 1); + a_id_len = tlen; + } + } + wpa_hexdump_ascii(MSG_DEBUG, "EAP-FAST: A-ID", a_id, a_id_len); + + data->current_pac = eap_fast_get_pac(data, a_id, a_id_len); + if (data->current_pac) { + wpa_printf(MSG_DEBUG, "EAP-FAST: PAC found for this " + "A-ID"); + wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-FAST: A-ID-Info", + data->current_pac->a_id_info, + data->current_pac->a_id_info_len); + } + + if (data->resuming && data->current_pac) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Trying to resume " + "session - do not add PAC-Opaque to TLS " + "ClientHello"); + if (tls_connection_client_hello_ext( + sm->ssl_ctx, data->ssl.conn, + TLS_EXT_PAC_OPAQUE, NULL, 0) < 0) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to " + "remove PAC-Opaque TLS extension"); + return NULL; + } + + } else if (data->current_pac) { + u8 *tlv; + size_t tlv_len, olen; + struct eap_tlv_hdr *hdr; + olen = data->current_pac->pac_opaque_len; + tlv_len = sizeof(*hdr) + olen; + tlv = malloc(tlv_len); + if (tlv) { + hdr = (struct eap_tlv_hdr *) tlv; + hdr->tlv_type = + host_to_be16(PAC_TYPE_PAC_OPAQUE); + hdr->length = host_to_be16(olen); + memcpy(hdr + 1, data->current_pac->pac_opaque, + olen); + } + if (tlv == NULL || + tls_connection_client_hello_ext( + sm->ssl_ctx, data->ssl.conn, + TLS_EXT_PAC_OPAQUE, tlv, tlv_len) < 0) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to " + "add PAC-Opaque TLS extension"); + free(tlv); + return NULL; + } + free(tlv); + } else { + if (!data->provisioning_allowed) { + wpa_printf(MSG_DEBUG, "EAP-FAST: No PAC found " + "and provisioning disabled"); + return NULL; + } + wpa_printf(MSG_DEBUG, "EAP-FAST: No PAC found - " + "starting provisioning"); + if (tls_connection_set_anon_dh(sm->ssl_ctx, + data->ssl.conn)) { + wpa_printf(MSG_INFO, "EAP-FAST: Could not " + "configure anonymous DH for TLS " + "connection"); + return NULL; + } + if (tls_connection_client_hello_ext( + sm->ssl_ctx, data->ssl.conn, + TLS_EXT_PAC_OPAQUE, NULL, 0) < 0) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to " + "remove PAC-Opaque TLS extension"); + return NULL; + } + data->provisioning = 1; + } + + left = 0; /* A-ID is not used in further packet processing */ + } + + resp = NULL; + if (tls_connection_established(sm->ssl_ctx, data->ssl.conn) && + !data->resuming) { + res = eap_fast_decrypt(sm, data, ret, req, pos, left, + &resp, respDataLen); + if (res < 0) { + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + /* Ack possible Alert that may have caused failure in + * decryption */ + res = 1; + } + } else { + if (eap_fast_set_tls_master_secret(sm, data, pos, left) < 0) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to configure " + "TLS master secret"); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + return NULL; + } + + res = eap_tls_process_helper(sm, &data->ssl, EAP_TYPE_FAST, + data->fast_version, id, pos, left, + &resp, respDataLen); + + if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) { + wpa_printf(MSG_DEBUG, + "EAP-FAST: TLS done, proceed to Phase 2"); + data->resuming = 0; + eap_fast_derive_keys(sm, data); + } + } + + if (res == 1) + return eap_tls_build_ack(&data->ssl, respDataLen, id, + EAP_TYPE_FAST, data->fast_version); + return resp; +} + + +#if 0 /* FIX */ +static Boolean eap_fast_has_reauth_data(struct eap_sm *sm, void *priv) +{ + struct eap_fast_data *data = priv; + return tls_connection_established(sm->ssl_ctx, data->ssl.conn); +} + + +static void eap_fast_deinit_for_reauth(struct eap_sm *sm, void *priv) +{ +} + + +static void * eap_fast_init_for_reauth(struct eap_sm *sm, void *priv) +{ + struct eap_fast_data *data = priv; + if (eap_tls_reauth_init(sm, &data->ssl)) { + free(data); + return NULL; + } + data->phase2_success = 0; + data->resuming = 1; + data->provisioning = 0; + return priv; +} +#endif + + +static int eap_fast_get_status(struct eap_sm *sm, void *priv, char *buf, + size_t buflen, int verbose) +{ + struct eap_fast_data *data = priv; + return eap_tls_status(sm, &data->ssl, buf, buflen, verbose); +} + + +static Boolean eap_fast_isKeyAvailable(struct eap_sm *sm, void *priv) +{ + struct eap_fast_data *data = priv; + return data->success; +} + + +static u8 * eap_fast_getKey(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_fast_data *data = priv; + u8 *key; + + if (!data->success) + return NULL; + + key = malloc(EAP_FAST_KEY_LEN); + if (key == NULL) + return NULL; + + *len = EAP_FAST_KEY_LEN; + memcpy(key, data->key_data, EAP_FAST_KEY_LEN); + + return key; +} + + +const struct eap_method eap_method_fast = +{ + .method = EAP_TYPE_FAST, + .name = "FAST", + .init = eap_fast_init, + .deinit = eap_fast_deinit, + .process = eap_fast_process, + .isKeyAvailable = eap_fast_isKeyAvailable, + .getKey = eap_fast_getKey, + .get_status = eap_fast_get_status, +#if 0 + .has_reauth_data = eap_fast_has_reauth_data, + .deinit_for_reauth = eap_fast_deinit_for_reauth, + .init_for_reauth = eap_fast_init_for_reauth, +#endif +}; diff --git a/contrib/wpa_supplicant/eap_gtc.c b/contrib/wpa_supplicant/eap_gtc.c new file mode 100644 index 000000000000..42a7a49acaf1 --- /dev/null +++ b/contrib/wpa_supplicant/eap_gtc.c @@ -0,0 +1,164 @@ +/* + * WPA Supplicant / EAP-GTC (RFC 2284) + * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include "common.h" +#include "eap_i.h" +#include "wpa_supplicant.h" +#include "config_ssid.h" + + +struct eap_gtc_data { + int prefix; +}; + + +static void * eap_gtc_init(struct eap_sm *sm) +{ + struct eap_gtc_data *data; + data = malloc(sizeof(*data)); + if (data == NULL) + return NULL; + memset(data, 0, sizeof(*data)); + + if (sm->m && sm->m->method == EAP_TYPE_FAST) { + wpa_printf(MSG_DEBUG, "EAP-GTC: EAP-FAST tunnel - use prefix " + "with challenge/response"); + data->prefix = 1; + } + return data; +} + + +static void eap_gtc_deinit(struct eap_sm *sm, void *priv) +{ + struct eap_gtc_data *data = priv; + free(data); +} + + +static u8 * eap_gtc_process(struct eap_sm *sm, void *priv, + struct eap_method_ret *ret, + u8 *reqData, size_t reqDataLen, + size_t *respDataLen) +{ + struct eap_gtc_data *data = priv; + struct wpa_ssid *config = eap_get_config(sm); + struct eap_hdr *req, *resp; + u8 *pos, *password; + size_t password_len, len, msg_len; + + req = (struct eap_hdr *) reqData; + pos = (u8 *) (req + 1); + if (reqDataLen < sizeof(*req) + 1 || *pos != EAP_TYPE_GTC || + (len = be_to_host16(req->length)) > reqDataLen) { + wpa_printf(MSG_INFO, "EAP-GTC: Invalid frame"); + ret->ignore = TRUE; + return NULL; + } + pos++; + msg_len = len - sizeof(*req) - 1; + wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-GTC: Request message", + pos, msg_len); + if (data->prefix && + (msg_len < 10 || memcmp(pos, "CHALLENGE=", 10) != 0)) { + wpa_printf(MSG_DEBUG, "EAP-GTC: Challenge did not start with " + "expected prefix"); + + /* Send an empty response in order to allow tunneled + * acknowledgement of the failure. This will also cover the + * error case which seems to use EAP-MSCHAPv2 like error + * reporting with EAP-GTC inside EAP-FAST tunnel. */ + *respDataLen = sizeof(struct eap_hdr) + 1; + resp = malloc(*respDataLen); + if (resp == NULL) + return NULL; + resp->code = EAP_CODE_RESPONSE; + resp->identifier = req->identifier; + resp->length = host_to_be16(*respDataLen); + pos = (u8 *) (resp + 1); + *pos++ = EAP_TYPE_GTC; + return (u8 *) resp; + } + + if (config == NULL || + (config->password == NULL && config->otp == NULL)) { + wpa_printf(MSG_INFO, "EAP-GTC: Password not configured"); + eap_sm_request_otp(sm, config, (char *) pos, + len - sizeof(*req) - 1); + ret->ignore = TRUE; + return NULL; + } + + if (config->otp) { + password = config->otp; + password_len = config->otp_len; + } else { + password = config->password; + password_len = config->password_len; + } + + ret->ignore = FALSE; + + ret->methodState = data->prefix ? METHOD_MAY_CONT : METHOD_DONE; + ret->decision = DECISION_COND_SUCC; + ret->allowNotifications = FALSE; + + *respDataLen = sizeof(struct eap_hdr) + 1 + password_len; + if (data->prefix) { + *respDataLen += 9 + config->identity_len + 1; + } + resp = malloc(*respDataLen); + if (resp == NULL) + return NULL; + resp->code = EAP_CODE_RESPONSE; + resp->identifier = req->identifier; + resp->length = host_to_be16(*respDataLen); + pos = (u8 *) (resp + 1); + *pos++ = EAP_TYPE_GTC; + if (data->prefix) { + memcpy(pos, "RESPONSE=", 9); + pos += 9; + memcpy(pos, config->identity, config->identity_len); + pos += config->identity_len; + *pos++ = '\0'; + } + memcpy(pos, password, password_len); + wpa_hexdump_ascii_key(MSG_MSGDUMP, "EAP-GTC: Response", + (u8 *) (resp + 1) + 1, + *respDataLen - sizeof(struct eap_hdr) - 1); + + if (config->otp) { + wpa_printf(MSG_DEBUG, "EAP-GTC: Forgetting used password"); + memset(config->otp, 0, config->otp_len); + free(config->otp); + config->otp = NULL; + config->otp_len = 0; + } + + return (u8 *) resp; +} + + +const struct eap_method eap_method_gtc = +{ + .method = EAP_TYPE_GTC, + .name = "GTC", + .init = eap_gtc_init, + .deinit = eap_gtc_deinit, + .process = eap_gtc_process, +}; diff --git a/contrib/wpa_supplicant/eap_i.h b/contrib/wpa_supplicant/eap_i.h new file mode 100644 index 000000000000..c71981255783 --- /dev/null +++ b/contrib/wpa_supplicant/eap_i.h @@ -0,0 +1,106 @@ +#ifndef EAP_I_H +#define EAP_I_H + +#include "eap.h" + +/* draft-ietf-eap-statemachine-05.pdf - Peer state machine */ + +typedef enum { + DECISION_FAIL, DECISION_COND_SUCC, DECISION_UNCOND_SUCC +} EapDecision; + +typedef enum { + METHOD_NONE, METHOD_INIT, METHOD_CONT, METHOD_MAY_CONT, METHOD_DONE +} EapMethodState; + +struct eap_method_ret { + Boolean ignore; + EapMethodState methodState; + EapDecision decision; + Boolean allowNotifications; +}; + + +struct eap_method { + EapType method; + const char *name; + + void * (*init)(struct eap_sm *sm); + void (*deinit)(struct eap_sm *sm, void *priv); + u8 * (*process)(struct eap_sm *sm, void *priv, + struct eap_method_ret *ret, + u8 *reqData, size_t reqDataLen, + size_t *respDataLen); + Boolean (*isKeyAvailable)(struct eap_sm *sm, void *priv); + u8 * (*getKey)(struct eap_sm *sm, void *priv, size_t *len); + int (*get_status)(struct eap_sm *sm, void *priv, char *buf, + size_t buflen, int verbose); + + /* Optional handlers for fast re-authentication */ + Boolean (*has_reauth_data)(struct eap_sm *sm, void *priv); + void (*deinit_for_reauth)(struct eap_sm *sm, void *priv); + void * (*init_for_reauth)(struct eap_sm *sm, void *priv); + const u8 * (*get_identity)(struct eap_sm *sm, void *priv, size_t *len); +}; + + +struct eap_sm { + enum { + EAP_INITIALIZE, EAP_DISABLED, EAP_IDLE, EAP_RECEIVED, + EAP_GET_METHOD, EAP_METHOD, EAP_SEND_RESPONSE, EAP_DISCARD, + EAP_IDENTITY, EAP_NOTIFICATION, EAP_RETRANSMIT, EAP_SUCCESS, + EAP_FAILURE + } EAP_state; + /* Long-term local variables */ + EapType selectedMethod; + EapMethodState methodState; + int lastId; + u8 *lastRespData; + size_t lastRespDataLen; + EapDecision decision; + /* Short-term local variables */ + Boolean rxReq; + Boolean rxSuccess; + Boolean rxFailure; + int reqId; + EapType reqMethod; + Boolean ignore; + /* Constants */ + int ClientTimeout; + + /* Miscellaneous variables */ + Boolean allowNotifications; /* peer state machine <-> methods */ + u8 *eapRespData; /* peer to lower layer */ + size_t eapRespDataLen; /* peer to lower layer */ + Boolean eapKeyAvailable; /* peer to lower layer */ + u8 *eapKeyData; /* peer to lower layer */ + size_t eapKeyDataLen; /* peer to lower layer */ + const struct eap_method *m; /* selected EAP method */ + /* not defined in draft-ietf-eap-statemachine-02 */ + Boolean changed; + void *eapol_ctx; + struct eapol_callbacks *eapol_cb; + void *eap_method_priv; + int init_phase2; + int fast_reauth; + + Boolean rxResp /* LEAP only */; + Boolean leap_done; + Boolean peap_done; + u8 req_md5[16]; /* MD5() of the current EAP packet */ + u8 last_md5[16]; /* MD5() of the previously received EAP packet; used + * in duplicate request detection. */ + + void *msg_ctx; + void *scard_ctx; + void *ssl_ctx; + + unsigned int workaround; + + /* Optional challenges generated in Phase 1 (EAP-FAST) */ + u8 *peer_challenge, *auth_challenge; + + int num_rounds; +}; + +#endif /* EAP_I_H */ diff --git a/contrib/wpa_supplicant/eap_leap.c b/contrib/wpa_supplicant/eap_leap.c new file mode 100644 index 000000000000..6409a6855f92 --- /dev/null +++ b/contrib/wpa_supplicant/eap_leap.c @@ -0,0 +1,379 @@ +/* + * WPA Supplicant / EAP-LEAP + * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include "common.h" +#include "eap_i.h" +#include "wpa_supplicant.h" +#include "config_ssid.h" +#include "ms_funcs.h" +#include "md5.h" + +#define LEAP_VERSION 1 +#define LEAP_CHALLENGE_LEN 8 +#define LEAP_RESPONSE_LEN 24 +#define LEAP_KEY_LEN 16 + + +struct eap_leap_data { + enum { + LEAP_WAIT_CHALLENGE, + LEAP_WAIT_SUCCESS, + LEAP_WAIT_RESPONSE, + LEAP_DONE + } state; + + u8 peer_challenge[LEAP_CHALLENGE_LEN]; + u8 peer_response[LEAP_RESPONSE_LEN]; + + u8 ap_challenge[LEAP_CHALLENGE_LEN]; + u8 ap_response[LEAP_RESPONSE_LEN]; +}; + + +static void * eap_leap_init(struct eap_sm *sm) +{ + struct eap_leap_data *data; + + data = malloc(sizeof(*data)); + if (data == NULL) + return NULL; + memset(data, 0, sizeof(*data)); + data->state = LEAP_WAIT_CHALLENGE; + + sm->leap_done = FALSE; + return data; +} + + +static void eap_leap_deinit(struct eap_sm *sm, void *priv) +{ + free(priv); +} + + +static u8 * eap_leap_process_request(struct eap_sm *sm, void *priv, + struct eap_method_ret *ret, + u8 *reqData, size_t reqDataLen, + size_t *respDataLen) +{ + struct eap_leap_data *data = priv; + struct wpa_ssid *config = eap_get_config(sm); + struct eap_hdr *req, *resp; + u8 *pos, *challenge, challenge_len; + + wpa_printf(MSG_DEBUG, "EAP-LEAP: Processing EAP-Request"); + + req = (struct eap_hdr *) reqData; + pos = (u8 *) (req + 1); + if (reqDataLen < sizeof(*req) + 4 || *pos != EAP_TYPE_LEAP) { + wpa_printf(MSG_INFO, "EAP-LEAP: Invalid EAP-Request frame"); + ret->ignore = TRUE; + return NULL; + } + pos++; + + if (*pos != LEAP_VERSION) { + wpa_printf(MSG_WARNING, "EAP-LEAP: Unsupported LEAP version " + "%d", *pos); + ret->ignore = TRUE; + return NULL; + } + pos++; + + pos++; /* skip unused byte */ + + challenge_len = *pos++; + if (challenge_len != LEAP_CHALLENGE_LEN || + challenge_len > reqDataLen - sizeof(*req) - 4) { + wpa_printf(MSG_INFO, "EAP-LEAP: Invalid challenge " + "(challenge_len=%d reqDataLen=%lu", + challenge_len, (unsigned long) reqDataLen); + ret->ignore = TRUE; + return NULL; + } + challenge = pos; + memcpy(data->peer_challenge, challenge, LEAP_CHALLENGE_LEN); + wpa_hexdump(MSG_MSGDUMP, "EAP-LEAP: Challenge from AP", + challenge, LEAP_CHALLENGE_LEN); + + wpa_printf(MSG_DEBUG, "EAP-LEAP: Generating Challenge Response"); + + *respDataLen = sizeof(struct eap_hdr) + 1 + 3 + LEAP_RESPONSE_LEN + + config->identity_len; + resp = malloc(*respDataLen); + if (resp == NULL) + return NULL; + resp->code = EAP_CODE_RESPONSE; + resp->identifier = req->identifier; + resp->length = host_to_be16(*respDataLen); + pos = (u8 *) (resp + 1); + *pos++ = EAP_TYPE_LEAP; + *pos++ = LEAP_VERSION; + *pos++ = 0; /* unused */ + *pos++ = LEAP_RESPONSE_LEN; + nt_challenge_response(challenge, + config->password, config->password_len, pos); + memcpy(data->peer_response, pos, LEAP_RESPONSE_LEN); + wpa_hexdump(MSG_MSGDUMP, "EAP-LEAP: Response", pos, LEAP_RESPONSE_LEN); + pos += LEAP_RESPONSE_LEN; + memcpy(pos, config->identity, config->identity_len); + + data->state = LEAP_WAIT_SUCCESS; + + return (u8 *) resp; +} + + +static u8 * eap_leap_process_success(struct eap_sm *sm, void *priv, + struct eap_method_ret *ret, + u8 *reqData, size_t reqDataLen, + size_t *respDataLen) +{ + struct eap_leap_data *data = priv; + struct wpa_ssid *config = eap_get_config(sm); + struct eap_hdr *req, *resp; + u8 *pos; + + wpa_printf(MSG_DEBUG, "EAP-LEAP: Processing EAP-Success"); + + if (data->state != LEAP_WAIT_SUCCESS) { + wpa_printf(MSG_INFO, "EAP-LEAP: EAP-Success received in " + "unexpected state (%d) - ignored", data->state); + ret->ignore = TRUE; + return NULL; + } + + req = (struct eap_hdr *) reqData; + + *respDataLen = sizeof(struct eap_hdr) + 1 + 3 + LEAP_CHALLENGE_LEN + + config->identity_len; + resp = malloc(*respDataLen); + if (resp == NULL) + return NULL; + resp->code = EAP_CODE_REQUEST; + resp->identifier = req->identifier; + resp->length = host_to_be16(*respDataLen); + pos = (u8 *) (resp + 1); + *pos++ = EAP_TYPE_LEAP; + *pos++ = LEAP_VERSION; + *pos++ = 0; /* unused */ + *pos++ = LEAP_CHALLENGE_LEN; + if (hostapd_get_rand(pos, LEAP_CHALLENGE_LEN)) { + wpa_printf(MSG_WARNING, "EAP-LEAP: Failed to read random data " + "for challenge"); + free(resp); + ret->ignore = TRUE; + return NULL; + } + memcpy(data->ap_challenge, pos, LEAP_CHALLENGE_LEN); + wpa_hexdump(MSG_MSGDUMP, "EAP-LEAP: Challenge to AP/AS", pos, + LEAP_CHALLENGE_LEN); + pos += LEAP_CHALLENGE_LEN; + memcpy(pos, config->identity, config->identity_len); + + data->state = LEAP_WAIT_RESPONSE; + + return (u8 *) resp; +} + + +static u8 * eap_leap_process_response(struct eap_sm *sm, void *priv, + struct eap_method_ret *ret, + u8 *reqData, size_t reqDataLen, + size_t *respDataLen) +{ + struct eap_leap_data *data = priv; + struct wpa_ssid *config = eap_get_config(sm); + struct eap_hdr *resp; + u8 *pos, response_len, pw_hash[16], pw_hash_hash[16], + expected[LEAP_RESPONSE_LEN]; + + wpa_printf(MSG_DEBUG, "EAP-LEAP: Processing EAP-Response"); + + resp = (struct eap_hdr *) reqData; + pos = (u8 *) (resp + 1); + if (reqDataLen < sizeof(*resp) + 4 || *pos != EAP_TYPE_LEAP) { + wpa_printf(MSG_INFO, "EAP-LEAP: Invalid EAP-Response frame"); + ret->ignore = TRUE; + return NULL; + } + pos++; + + if (*pos != LEAP_VERSION) { + wpa_printf(MSG_WARNING, "EAP-LEAP: Unsupported LEAP version " + "%d", *pos); + ret->ignore = TRUE; + return NULL; + } + pos++; + + pos++; /* skip unused byte */ + + response_len = *pos++; + if (response_len != LEAP_RESPONSE_LEN || + response_len > reqDataLen - sizeof(*resp) - 4) { + wpa_printf(MSG_INFO, "EAP-LEAP: Invalid response " + "(response_len=%d reqDataLen=%lu", + response_len, (unsigned long) reqDataLen); + ret->ignore = TRUE; + return NULL; + } + + wpa_hexdump(MSG_DEBUG, "EAP-LEAP: Response from AP", + pos, LEAP_RESPONSE_LEN); + memcpy(data->ap_response, pos, LEAP_RESPONSE_LEN); + + nt_password_hash(config->password, config->password_len, pw_hash); + hash_nt_password_hash(pw_hash, pw_hash_hash); + challenge_response(data->ap_challenge, pw_hash_hash, expected); + + ret->methodState = METHOD_DONE; + ret->allowNotifications = FALSE; + + if (memcmp(pos, expected, LEAP_RESPONSE_LEN) != 0) { + wpa_printf(MSG_WARNING, "EAP-LEAP: AP sent an invalid " + "response - authentication failed"); + wpa_hexdump(MSG_DEBUG, "EAP-LEAP: Expected response from AP", + expected, LEAP_RESPONSE_LEN); + ret->decision = DECISION_FAIL; + return NULL; + } + + ret->decision = DECISION_UNCOND_SUCC; + + /* LEAP is somewhat odd method since it sends EAP-Success in the middle + * of the authentication. Use special variable to transit EAP state + * machine to SUCCESS state. */ + sm->leap_done = TRUE; + data->state = LEAP_DONE; + + /* No more authentication messages expected; AP will send EAPOL-Key + * frames if encryption is enabled. */ + return NULL; +} + + +static u8 * eap_leap_process(struct eap_sm *sm, void *priv, + struct eap_method_ret *ret, + u8 *reqData, size_t reqDataLen, + size_t *respDataLen) +{ + struct wpa_ssid *config = eap_get_config(sm); + struct eap_hdr *eap; + size_t len; + + if (config == NULL || config->password == NULL) { + wpa_printf(MSG_INFO, "EAP-LEAP: Password not configured"); + eap_sm_request_password(sm, config); + ret->ignore = TRUE; + return NULL; + } + + eap = (struct eap_hdr *) reqData; + + if (reqDataLen < sizeof(*eap) || + (len = be_to_host16(eap->length)) > reqDataLen) { + wpa_printf(MSG_INFO, "EAP-LEAP: Invalid frame"); + ret->ignore = TRUE; + return NULL; + } + + ret->ignore = FALSE; + ret->allowNotifications = TRUE; + ret->methodState = METHOD_CONT; + ret->decision = DECISION_FAIL; + + sm->leap_done = FALSE; + + switch (eap->code) { + case EAP_CODE_REQUEST: + return eap_leap_process_request(sm, priv, ret, reqData, len, + respDataLen); + case EAP_CODE_SUCCESS: + return eap_leap_process_success(sm, priv, ret, reqData, len, + respDataLen); + case EAP_CODE_RESPONSE: + return eap_leap_process_response(sm, priv, ret, reqData, len, + respDataLen); + default: + wpa_printf(MSG_INFO, "EAP-LEAP: Unexpected EAP code (%d) - " + "ignored", eap->code); + ret->ignore = TRUE; + return NULL; + } +} + + +static Boolean eap_leap_isKeyAvailable(struct eap_sm *sm, void *priv) +{ + struct eap_leap_data *data = priv; + return data->state == LEAP_DONE; +} + + +static u8 * eap_leap_getKey(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_leap_data *data = priv; + struct wpa_ssid *config = eap_get_config(sm); + u8 *key, pw_hash_hash[16], pw_hash[16]; + MD5_CTX context; + + if (data->state != LEAP_DONE) + return NULL; + + key = malloc(LEAP_KEY_LEN); + if (key == NULL) + return NULL; + + nt_password_hash(config->password, config->password_len, pw_hash); + hash_nt_password_hash(pw_hash, pw_hash_hash); + wpa_hexdump_key(MSG_DEBUG, "EAP-LEAP: pw_hash_hash", + pw_hash_hash, 16); + wpa_hexdump(MSG_DEBUG, "EAP-LEAP: peer_challenge", + data->peer_challenge, LEAP_CHALLENGE_LEN); + wpa_hexdump(MSG_DEBUG, "EAP-LEAP: peer_response", + data->peer_response, LEAP_RESPONSE_LEN); + wpa_hexdump(MSG_DEBUG, "EAP-LEAP: ap_challenge", + data->ap_challenge, LEAP_CHALLENGE_LEN); + wpa_hexdump(MSG_DEBUG, "EAP-LEAP: ap_response", + data->ap_response, LEAP_RESPONSE_LEN); + + MD5Init(&context); + MD5Update(&context, pw_hash_hash, 16); + MD5Update(&context, data->ap_challenge, LEAP_CHALLENGE_LEN); + MD5Update(&context, data->ap_response, LEAP_RESPONSE_LEN); + MD5Update(&context, data->peer_challenge, LEAP_CHALLENGE_LEN); + MD5Update(&context, data->peer_response, LEAP_RESPONSE_LEN); + MD5Final(key, &context); + wpa_hexdump_key(MSG_DEBUG, "EAP-LEAP: master key", key, LEAP_KEY_LEN); + *len = LEAP_KEY_LEN; + + return key; +} + + +const struct eap_method eap_method_leap = +{ + .method = EAP_TYPE_LEAP, + .name = "LEAP", + .init = eap_leap_init, + .deinit = eap_leap_deinit, + .process = eap_leap_process, + .isKeyAvailable = eap_leap_isKeyAvailable, + .getKey = eap_leap_getKey, +}; diff --git a/contrib/wpa_supplicant/eap_md5.c b/contrib/wpa_supplicant/eap_md5.c new file mode 100644 index 000000000000..bcb5558be97e --- /dev/null +++ b/contrib/wpa_supplicant/eap_md5.c @@ -0,0 +1,112 @@ +/* + * WPA Supplicant / EAP-MD5 + * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include <stdlib.h> +#include <stdio.h> + +#include "common.h" +#include "eap_i.h" +#include "wpa_supplicant.h" +#include "config_ssid.h" +#include "md5.h" + + +static void * eap_md5_init(struct eap_sm *sm) +{ + return (void *) 1; +} + + +static void eap_md5_deinit(struct eap_sm *sm, void *priv) +{ +} + + +static u8 * eap_md5_process(struct eap_sm *sm, void *priv, + struct eap_method_ret *ret, + u8 *reqData, size_t reqDataLen, + size_t *respDataLen) +{ + struct wpa_ssid *config = eap_get_config(sm); + struct eap_hdr *req, *resp; + u8 *pos, *challenge; + int challenge_len; + MD5_CTX context; + size_t len; + + if (config == NULL || config->password == NULL) { + wpa_printf(MSG_INFO, "EAP-MD5: Password not configured"); + eap_sm_request_password(sm, config); + ret->ignore = TRUE; + return NULL; + } + + req = (struct eap_hdr *) reqData; + pos = (u8 *) (req + 1); + if (reqDataLen < sizeof(*req) + 2 || *pos != EAP_TYPE_MD5 || + (len = be_to_host16(req->length)) > reqDataLen) { + wpa_printf(MSG_INFO, "EAP-MD5: Invalid frame"); + ret->ignore = TRUE; + return NULL; + } + pos++; + challenge_len = *pos++; + if (challenge_len == 0 || + challenge_len > len - sizeof(*req) - 2) { + wpa_printf(MSG_INFO, "EAP-MD5: Invalid challenge " + "(challenge_len=%d len=%lu", + challenge_len, (unsigned long) len); + ret->ignore = TRUE; + return NULL; + } + ret->ignore = FALSE; + challenge = pos; + wpa_hexdump(MSG_MSGDUMP, "EAP-MD5: Challenge", + challenge, challenge_len); + + wpa_printf(MSG_DEBUG, "EAP-MD5: generating Challenge Response"); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_UNCOND_SUCC; + ret->allowNotifications = TRUE; + + *respDataLen = sizeof(struct eap_hdr) + 1 + 1 + MD5_MAC_LEN; + resp = malloc(*respDataLen); + if (resp == NULL) + return NULL; + resp->code = EAP_CODE_RESPONSE; + resp->identifier = req->identifier; + resp->length = host_to_be16(*respDataLen); + pos = (u8 *) (resp + 1); + *pos++ = EAP_TYPE_MD5; + *pos++ = MD5_MAC_LEN; /* Value-Size */ + + MD5Init(&context); + MD5Update(&context, &resp->identifier, 1); + MD5Update(&context, config->password, config->password_len); + MD5Update(&context, challenge, challenge_len); + MD5Final(pos, &context); + wpa_hexdump(MSG_MSGDUMP, "EAP-MD5: Response", pos, MD5_MAC_LEN); + + return (u8 *) resp; +} + + +const struct eap_method eap_method_md5 = +{ + .method = EAP_TYPE_MD5, + .name = "MD5", + .init = eap_md5_init, + .deinit = eap_md5_deinit, + .process = eap_md5_process, +}; diff --git a/contrib/wpa_supplicant/eap_mschapv2.c b/contrib/wpa_supplicant/eap_mschapv2.c new file mode 100644 index 000000000000..a39a5d330a78 --- /dev/null +++ b/contrib/wpa_supplicant/eap_mschapv2.c @@ -0,0 +1,564 @@ +/* + * WPA Supplicant / EAP-MSCHAPV2 (draft-kamath-pppext-eap-mschapv2-00.txt) + * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include "common.h" +#include "eap_i.h" +#include "wpa_supplicant.h" +#include "config_ssid.h" +#include "ms_funcs.h" + + +struct eap_mschapv2_hdr { + u8 code; + u8 identifier; + u16 length; /* including code, identifier, and length */ + u8 type; /* EAP_TYPE_MSCHAPV2 */ + u8 op_code; /* MSCHAPV2_OP_* */ + u8 mschapv2_id; /* usually same as identifier */ + u8 ms_length[2]; /* Note: misaligned; length - 5 */ + /* followed by data */ +} __attribute__ ((packed)); + +#define MSCHAPV2_OP_CHALLENGE 1 +#define MSCHAPV2_OP_RESPONSE 2 +#define MSCHAPV2_OP_SUCCESS 3 +#define MSCHAPV2_OP_FAILURE 4 +#define MSCHAPV2_OP_CHANGE_PASSWORD 7 + +#define MSCHAPV2_RESP_LEN 49 + +#define ERROR_RESTRICTED_LOGON_HOURS 646 +#define ERROR_ACCT_DISABLED 647 +#define ERROR_PASSWD_EXPIRED 648 +#define ERROR_NO_DIALIN_PERMISSION 649 +#define ERROR_AUTHENTICATION_FAILURE 691 +#define ERROR_CHANGING_PASSWORD 709 + +#define PASSWD_CHANGE_CHAL_LEN 16 +#define MSCHAPV2_KEY_LEN 16 + + +struct eap_mschapv2_data { + u8 auth_response[20]; + int auth_response_valid; + + int prev_error; + u8 passwd_change_challenge[PASSWD_CHANGE_CHAL_LEN]; + int passwd_change_challenge_valid; + int passwd_change_version; + + /* Optional challenge values generated in EAP-FAST Phase 1 negotiation + */ + u8 *peer_challenge; + u8 *auth_challenge; + + int phase2; + u8 master_key[16]; + int master_key_valid; + int success; +}; + + +static void eap_mschapv2_deinit(struct eap_sm *sm, void *priv); + + +static void * eap_mschapv2_init(struct eap_sm *sm) +{ + struct eap_mschapv2_data *data; + data = malloc(sizeof(*data)); + if (data == NULL) + return NULL; + memset(data, 0, sizeof(*data)); + + if (sm->peer_challenge) { + data->peer_challenge = malloc(16); + if (data->peer_challenge == NULL) { + eap_mschapv2_deinit(sm, data); + return NULL; + } + memcpy(data->peer_challenge, sm->peer_challenge, 16); + } + + if (sm->auth_challenge) { + data->auth_challenge = malloc(16); + if (data->auth_challenge == NULL) { + eap_mschapv2_deinit(sm, data); + return NULL; + } + memcpy(data->auth_challenge, sm->auth_challenge, 16); + } + + data->phase2 = sm->init_phase2; + + return data; +} + + +static void eap_mschapv2_deinit(struct eap_sm *sm, void *priv) +{ + struct eap_mschapv2_data *data = priv; + free(data->peer_challenge); + free(data->auth_challenge); + free(data); +} + + +static u8 * eap_mschapv2_challenge(struct eap_sm *sm, + struct eap_mschapv2_data *data, + struct eap_method_ret *ret, + struct eap_mschapv2_hdr *req, + size_t *respDataLen) +{ + struct wpa_ssid *config = eap_get_config(sm); + u8 *challenge, *peer_challenge, *username, *pos; + int challenge_len, i, ms_len; + size_t len, username_len; + struct eap_mschapv2_hdr *resp; + u8 password_hash[16], password_hash_hash[16]; + + /* MSCHAPv2 does not include optional domain name in the + * challenge-response calculation, so remove domain prefix + * (if present). */ + username = config->identity; + username_len = config->identity_len; + for (i = 0; i < username_len; i++) { + if (username[i] == '\\') { + username_len -= i + 1; + username += i + 1; + break; + } + } + + wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received challenge"); + len = be_to_host16(req->length); + pos = (u8 *) (req + 1); + challenge_len = *pos++; + if (challenge_len != 16) { + wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Invalid challenge length " + "%d", challenge_len); + ret->ignore = TRUE; + return NULL; + } + + if (len - challenge_len - 10 < 0) { + wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Too short challenge" + " packet: len=%lu challenge_len=%d", + (unsigned long) len, challenge_len); + } + + challenge = pos; + pos += challenge_len; + wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Authentication Servername", + pos, len - challenge_len - 10); + + ret->ignore = FALSE; + ret->methodState = METHOD_CONT; + ret->decision = DECISION_FAIL; + ret->allowNotifications = TRUE; + + wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Generating Challenge Response"); + + *respDataLen = sizeof(*resp) + 1 + MSCHAPV2_RESP_LEN + + config->identity_len; + resp = malloc(*respDataLen); + if (resp == NULL) + return NULL; + memset(resp, 0, *respDataLen); + resp->code = EAP_CODE_RESPONSE; + resp->identifier = req->identifier; + resp->length = host_to_be16(*respDataLen); + resp->type = EAP_TYPE_MSCHAPV2; + resp->op_code = MSCHAPV2_OP_RESPONSE; + resp->mschapv2_id = req->mschapv2_id; + ms_len = *respDataLen - 5; + resp->ms_length[0] = ms_len >> 8; + resp->ms_length[1] = ms_len & 0xff; + pos = (u8 *) (resp + 1); + *pos++ = MSCHAPV2_RESP_LEN; /* Value-Size */ + + /* Response */ + peer_challenge = pos; + if (data->peer_challenge) { + wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: peer_challenge generated " + "in Phase 1"); + peer_challenge = data->peer_challenge; + } else if (hostapd_get_rand(peer_challenge, 16)) { + free(resp); + return NULL; + } + pos += 16; + pos += 8; /* Reserved, must be zero */ + if (data->auth_challenge) { + wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: auth_challenge generated " + "in Phase 1"); + challenge = data->auth_challenge; + } + wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: auth_challenge", challenge, 16); + wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: peer_challenge", + peer_challenge, 16); + wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: username", + username, username_len); + wpa_hexdump_ascii_key(MSG_DEBUG, "EAP-MSCHAPV2: password", + config->password, config->password_len); + generate_nt_response(challenge, peer_challenge, + username, username_len, + config->password, config->password_len, + pos); + wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: response", pos, 24); + /* Authenticator response is not really needed yet, but calculate it + * here so that challenges need not be saved. */ + generate_authenticator_response(config->password, config->password_len, + peer_challenge, challenge, + username, username_len, pos, + data->auth_response); + data->auth_response_valid = 1; + + /* Likewise, generate master_key here since we have the needed data + * available. */ + nt_password_hash(config->password, config->password_len, + password_hash); + hash_nt_password_hash(password_hash, password_hash_hash); + get_master_key(password_hash_hash, pos /* nt_response */, + data->master_key); + data->master_key_valid = 1; + + pos += 24; + pos++; /* Flag / reserved, must be zero */ + + memcpy(pos, config->identity, config->identity_len); + return (u8 *) resp; +} + + +static u8 * eap_mschapv2_success(struct eap_sm *sm, + struct eap_mschapv2_data *data, + struct eap_method_ret *ret, + struct eap_mschapv2_hdr *req, + size_t *respDataLen) +{ + struct eap_mschapv2_hdr *resp; + u8 *pos, recv_response[20]; + int len, left; + + wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received success"); + len = be_to_host16(req->length); + pos = (u8 *) (req + 1); + if (!data->auth_response_valid || len < sizeof(*req) + 42 || + pos[0] != 'S' || pos[1] != '=' || + hexstr2bin((char *) (pos + 2), recv_response, 20) || + memcmp(data->auth_response, recv_response, 20) != 0) { + wpa_printf(MSG_WARNING, "EAP-MSCHAPV2: Invalid authenticator " + "response in success request"); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + return NULL; + } + pos += 42; + left = len - sizeof(*req) - 42; + while (left > 0 && *pos == ' ') { + pos++; + left--; + } + wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Success message", + pos, left); + wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Authentication succeeded"); + *respDataLen = 6; + resp = malloc(6); + if (resp == NULL) { + wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Failed to allocate " + "buffer for success response"); + ret->ignore = TRUE; + return NULL; + } + + resp->code = EAP_CODE_RESPONSE; + resp->identifier = req->identifier; + resp->length = host_to_be16(6); + resp->type = EAP_TYPE_MSCHAPV2; + resp->op_code = MSCHAPV2_OP_SUCCESS; + + ret->methodState = METHOD_DONE; + ret->decision = DECISION_UNCOND_SUCC; + ret->allowNotifications = FALSE; + data->success = 1; + + return (u8 *) resp; +} + + +static int eap_mschapv2_failure_txt(struct eap_sm *sm, + struct eap_mschapv2_data *data, char *txt) +{ + char *pos, *msg = ""; + int retry = 1; + struct wpa_ssid *config = eap_get_config(sm); + + /* For example: + * E=691 R=1 C=<32 octets hex challenge> V=3 M=Authentication Failure + */ + + pos = txt; + + if (pos && strncmp(pos, "E=", 2) == 0) { + pos += 2; + data->prev_error = atoi(pos); + wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: error %d", + data->prev_error); + pos = strchr(pos, ' '); + if (pos) + pos++; + } + + if (pos && strncmp(pos, "R=", 2) == 0) { + pos += 2; + retry = atoi(pos); + wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: retry is %sallowed", + retry == 1 ? "" : "not "); + pos = strchr(pos, ' '); + if (pos) + pos++; + } + + if (pos && strncmp(pos, "C=", 2) == 0) { + int hex_len; + pos += 2; + hex_len = strchr(pos, ' ') - (char *) pos; + if (hex_len == PASSWD_CHANGE_CHAL_LEN * 2) { + if (hexstr2bin(pos, data->passwd_change_challenge, + PASSWD_CHANGE_CHAL_LEN)) { + wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: invalid " + "failure challenge"); + } else { + wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: failure " + "challenge", + data->passwd_change_challenge, + PASSWD_CHANGE_CHAL_LEN); + data->passwd_change_challenge_valid = 1; + } + } else { + wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: invalid failure " + "challenge len %d", hex_len); + } + pos = strchr(pos, ' '); + if (pos) + pos++; + } else { + wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: required challenge field " + "was not present in failure message"); + } + + if (pos && strncmp(pos, "V=", 2) == 0) { + pos += 2; + data->passwd_change_version = atoi(pos); + wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: password changing " + "protocol version %d", data->passwd_change_version); + pos = strchr(pos, ' '); + if (pos) + pos++; + } + + if (pos && strncmp(pos, "M=", 2) == 0) { + pos += 2; + msg = pos; + } + wpa_msg(sm->msg_ctx, MSG_WARNING, + "EAP-MSCHAPV2: failure message: '%s' (retry %sallowed, error " + "%d)", + msg, retry == 1 ? "" : "not ", data->prev_error); + if (retry == 1 && config) { + /* TODO: could prevent the current password from being used + * again at least for some period of time */ + eap_sm_request_identity(sm, config); + eap_sm_request_password(sm, config); + } else if (config) { + /* TODO: prevent retries using same username/password */ + } + + return retry == 1; +} + + +static u8 * eap_mschapv2_failure(struct eap_sm *sm, + struct eap_mschapv2_data *data, + struct eap_method_ret *ret, + struct eap_mschapv2_hdr *req, + size_t *respDataLen) +{ + struct eap_mschapv2_hdr *resp; + u8 *msdata = (u8 *) (req + 1); + char *buf; + int len = be_to_host16(req->length) - sizeof(*req); + int retry = 0; + + wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received failure"); + wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Failure data", + msdata, len); + buf = malloc(len + 1); + if (buf) { + memcpy(buf, msdata, len); + buf[len] = '\0'; + retry = eap_mschapv2_failure_txt(sm, data, buf); + free(buf); + } + + ret->ignore = FALSE; + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + ret->allowNotifications = FALSE; + + if (retry) { + /* TODO: could try to retry authentication, e.g, after having + * changed the username/password. In this case, EAP MS-CHAP-v2 + * Failure Response would not be sent here. */ + } + + *respDataLen = 6; + resp = malloc(6); + if (resp == NULL) { + return NULL; + } + + resp->code = EAP_CODE_RESPONSE; + resp->identifier = req->identifier; + resp->length = host_to_be16(6); + resp->type = EAP_TYPE_MSCHAPV2; + resp->op_code = MSCHAPV2_OP_FAILURE; + + return (u8 *) resp; +} + + +static u8 * eap_mschapv2_process(struct eap_sm *sm, void *priv, + struct eap_method_ret *ret, + u8 *reqData, size_t reqDataLen, + size_t *respDataLen) +{ + struct eap_mschapv2_data *data = priv; + struct wpa_ssid *config = eap_get_config(sm); + struct eap_mschapv2_hdr *req; + int ms_len, len; + + if (config == NULL || config->identity == NULL) { + wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Identity not configured"); + eap_sm_request_identity(sm, config); + ret->ignore = TRUE; + return NULL; + } + + if (config->password == NULL) { + wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Password not configured"); + eap_sm_request_password(sm, config); + ret->ignore = TRUE; + return NULL; + } + + req = (struct eap_mschapv2_hdr *) reqData; + len = be_to_host16(req->length); + if (len < sizeof(*req) + 2 || req->type != EAP_TYPE_MSCHAPV2) { + wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Invalid frame"); + ret->ignore = TRUE; + return NULL; + } + + ms_len = ((int) req->ms_length[0] << 8) | req->ms_length[1]; + if (ms_len != len - 5) { + wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Invalid header: len=%d " + "ms_len=%d", len, ms_len); + if (sm->workaround) { + /* Some authentication servers use invalid ms_len, + * ignore it for interoperability. */ + wpa_printf(MSG_INFO, "EAP-MSCHAPV2: workaround, ignore" + " invalid ms_len"); + } else { + ret->ignore = TRUE; + return NULL; + } + } + + switch (req->op_code) { + case MSCHAPV2_OP_CHALLENGE: + return eap_mschapv2_challenge(sm, data, ret, req, respDataLen); + case MSCHAPV2_OP_SUCCESS: + return eap_mschapv2_success(sm, data, ret, req, respDataLen); + case MSCHAPV2_OP_FAILURE: + return eap_mschapv2_failure(sm, data, ret, req, respDataLen); + default: + wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Unknown op %d - ignored", + req->op_code); + ret->ignore = TRUE; + return NULL; + } +} + + +static Boolean eap_mschapv2_isKeyAvailable(struct eap_sm *sm, void *priv) +{ + struct eap_mschapv2_data *data = priv; + return data->success && data->master_key_valid; +} + + +static u8 * eap_mschapv2_getKey(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_mschapv2_data *data = priv; + u8 *key; + int key_len; + + if (!data->master_key_valid || !data->success) + return NULL; + + if (data->peer_challenge) { + /* EAP-FAST needs both send and receive keys */ + key_len = 2 * MSCHAPV2_KEY_LEN; + } else { + key_len = MSCHAPV2_KEY_LEN; + } + + key = malloc(key_len); + if (key == NULL) + return NULL; + + if (data->peer_challenge) { + get_asymetric_start_key(data->master_key, key, + MSCHAPV2_KEY_LEN, 0, 0); + get_asymetric_start_key(data->master_key, + key + MSCHAPV2_KEY_LEN, + MSCHAPV2_KEY_LEN, 1, 0); + } else { + get_asymetric_start_key(data->master_key, key, + MSCHAPV2_KEY_LEN, 1, 0); + } + + wpa_hexdump_key(MSG_DEBUG, "EAP-MSCHAPV2: Derived key", + key, key_len); + + *len = key_len; + return key; +} + + +const struct eap_method eap_method_mschapv2 = +{ + .method = EAP_TYPE_MSCHAPV2, + .name = "MSCHAPV2", + .init = eap_mschapv2_init, + .deinit = eap_mschapv2_deinit, + .process = eap_mschapv2_process, + .isKeyAvailable = eap_mschapv2_isKeyAvailable, + .getKey = eap_mschapv2_getKey, +}; diff --git a/contrib/wpa_supplicant/eap_otp.c b/contrib/wpa_supplicant/eap_otp.c new file mode 100644 index 000000000000..e50de636accd --- /dev/null +++ b/contrib/wpa_supplicant/eap_otp.c @@ -0,0 +1,113 @@ +/* + * WPA Supplicant / EAP-OTP (RFC 3748) + * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include "common.h" +#include "eap_i.h" +#include "wpa_supplicant.h" +#include "config_ssid.h" + + +static void * eap_otp_init(struct eap_sm *sm) +{ + return (void *) 1; +} + + +static void eap_otp_deinit(struct eap_sm *sm, void *priv) +{ +} + + +static u8 * eap_otp_process(struct eap_sm *sm, void *priv, + struct eap_method_ret *ret, + u8 *reqData, size_t reqDataLen, + size_t *respDataLen) +{ + struct wpa_ssid *config = eap_get_config(sm); + struct eap_hdr *req, *resp; + u8 *pos, *password; + size_t password_len, len; + + req = (struct eap_hdr *) reqData; + pos = (u8 *) (req + 1); + if (reqDataLen < sizeof(*req) + 1 || *pos != EAP_TYPE_OTP || + (len = be_to_host16(req->length)) > reqDataLen) { + wpa_printf(MSG_INFO, "EAP-OTP: Invalid frame"); + ret->ignore = TRUE; + return NULL; + } + pos++; + wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-OTP: Request message", + pos, len - sizeof(*req) - 1); + + if (config == NULL || + (config->password == NULL && config->otp == NULL)) { + wpa_printf(MSG_INFO, "EAP-OTP: Password not configured"); + eap_sm_request_otp(sm, config, (char *) pos, + len - sizeof(*req) - 1); + ret->ignore = TRUE; + return NULL; + } + + if (config->otp) { + password = config->otp; + password_len = config->otp_len; + } else { + password = config->password; + password_len = config->password_len; + } + + ret->ignore = FALSE; + + ret->methodState = METHOD_DONE; + ret->decision = DECISION_COND_SUCC; + ret->allowNotifications = FALSE; + + *respDataLen = sizeof(struct eap_hdr) + 1 + password_len; + resp = malloc(*respDataLen); + if (resp == NULL) + return NULL; + resp->code = EAP_CODE_RESPONSE; + resp->identifier = req->identifier; + resp->length = host_to_be16(*respDataLen); + pos = (u8 *) (resp + 1); + *pos++ = EAP_TYPE_OTP; + memcpy(pos, password, password_len); + wpa_hexdump_ascii_key(MSG_MSGDUMP, "EAP-OTP: Response", + password, password_len); + + if (config->otp) { + wpa_printf(MSG_DEBUG, "EAP-OTP: Forgetting used password"); + memset(config->otp, 0, config->otp_len); + free(config->otp); + config->otp = NULL; + config->otp_len = 0; + } + + return (u8 *) resp; +} + + +const struct eap_method eap_method_otp = +{ + .method = EAP_TYPE_OTP, + .name = "OTP", + .init = eap_otp_init, + .deinit = eap_otp_deinit, + .process = eap_otp_process, +}; diff --git a/contrib/wpa_supplicant/eap_peap.c b/contrib/wpa_supplicant/eap_peap.c new file mode 100644 index 000000000000..27f0793cb8c1 --- /dev/null +++ b/contrib/wpa_supplicant/eap_peap.c @@ -0,0 +1,820 @@ +/* + * WPA Supplicant / EAP-PEAP (draft-josefsson-pppext-eap-tls-eap-07.txt) + * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include "common.h" +#include "eap_i.h" +#include "eap_tls_common.h" +#include "wpa_supplicant.h" +#include "config_ssid.h" +#include "tls.h" +#include "eap_tlv.h" + + +/* Maximum supported PEAP version + * 0 = Microsoft's PEAP version 0; draft-kamath-pppext-peapv0-00.txt + * 1 = draft-josefsson-ppext-eap-tls-eap-05.txt + * 2 = draft-josefsson-ppext-eap-tls-eap-07.txt + */ +#define EAP_PEAP_VERSION 1 + + +static void eap_peap_deinit(struct eap_sm *sm, void *priv); + + +struct eap_peap_data { + struct eap_ssl_data ssl; + + int peap_version, force_peap_version, force_new_label; + + const struct eap_method *phase2_method; + void *phase2_priv; + int phase2_success; + + u8 phase2_type; + u8 *phase2_types; + size_t num_phase2_types; + + int peap_outer_success; /* 0 = PEAP terminated on Phase 2 inner + * EAP-Success + * 1 = reply with tunneled EAP-Success to inner + * EAP-Success and expect AS to send outer + * (unencrypted) EAP-Success after this + * 2 = reply with PEAP/TLS ACK to inner + * EAP-Success and expect AS to send outer + * (unencrypted) EAP-Success after this */ + int resuming; /* starting a resumed session */ + u8 *key_data; + + u8 *pending_phase2_req; + size_t pending_phase2_req_len; +}; + + +static void * eap_peap_init(struct eap_sm *sm) +{ + struct eap_peap_data *data; + struct wpa_ssid *config = eap_get_config(sm); + + data = malloc(sizeof(*data)); + if (data == NULL) + return NULL; + sm->peap_done = FALSE; + memset(data, 0, sizeof(*data)); + data->peap_version = EAP_PEAP_VERSION; + data->force_peap_version = -1; + data->peap_outer_success = 2; + + if (config && config->phase1) { + char *pos = strstr(config->phase1, "peapver="); + if (pos) { + data->force_peap_version = atoi(pos + 8); + data->peap_version = data->force_peap_version; + wpa_printf(MSG_DEBUG, "EAP-PEAP: Forced PEAP version " + "%d", data->force_peap_version); + } + + if (strstr(config->phase1, "peaplabel=1")) { + data->force_new_label = 1; + wpa_printf(MSG_DEBUG, "EAP-PEAP: Force new label for " + "key derivation"); + } + + if (strstr(config->phase1, "peap_outer_success=0")) { + data->peap_outer_success = 0; + wpa_printf(MSG_DEBUG, "EAP-PEAP: terminate " + "authentication on tunneled EAP-Success"); + } else if (strstr(config->phase1, "peap_outer_success=1")) { + data->peap_outer_success = 1; + wpa_printf(MSG_DEBUG, "EAP-PEAP: send tunneled " + "EAP-Success after receiving tunneled " + "EAP-Success"); + } else if (strstr(config->phase1, "peap_outer_success=2")) { + data->peap_outer_success = 2; + wpa_printf(MSG_DEBUG, "EAP-PEAP: send PEAP/TLS ACK " + "after receiving tunneled EAP-Success"); + } + } + + if (config && config->phase2) { + char *start, *pos, *buf; + u8 method, *methods = NULL, *_methods; + size_t num_methods = 0; + start = buf = strdup(config->phase2); + if (buf == NULL) { + eap_peap_deinit(sm, data); + return NULL; + } + while (start && *start != '\0') { + pos = strstr(start, "auth="); + if (pos == NULL) + break; + if (start != pos && *(pos - 1) != ' ') { + start = pos + 5; + continue; + } + + start = pos + 5; + pos = strchr(start, ' '); + if (pos) + *pos++ = '\0'; + method = eap_get_phase2_type(start); + if (method == EAP_TYPE_NONE) { + wpa_printf(MSG_ERROR, "EAP-PEAP: Unsupported " + "Phase2 method '%s'", start); + } else { + num_methods++; + _methods = realloc(methods, num_methods); + if (_methods == NULL) { + free(methods); + eap_peap_deinit(sm, data); + return NULL; + } + methods = _methods; + methods[num_methods - 1] = method; + } + + start = pos; + } + free(buf); + data->phase2_types = methods; + data->num_phase2_types = num_methods; + } + if (data->phase2_types == NULL) { + data->phase2_types = + eap_get_phase2_types(config, &data->num_phase2_types); + } + if (data->phase2_types == NULL) { + wpa_printf(MSG_ERROR, "EAP-PEAP: No Phase2 method available"); + eap_peap_deinit(sm, data); + return NULL; + } + wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Phase2 EAP types", + data->phase2_types, data->num_phase2_types); + data->phase2_type = EAP_TYPE_NONE; + + if (eap_tls_ssl_init(sm, &data->ssl, config)) { + wpa_printf(MSG_INFO, "EAP-PEAP: Failed to initialize SSL."); + eap_peap_deinit(sm, data); + return NULL; + } + + return data; +} + + +static void eap_peap_deinit(struct eap_sm *sm, void *priv) +{ + struct eap_peap_data *data = priv; + if (data == NULL) + return; + if (data->phase2_priv && data->phase2_method) + data->phase2_method->deinit(sm, data->phase2_priv); + free(data->phase2_types); + eap_tls_ssl_deinit(sm, &data->ssl); + free(data->key_data); + free(data->pending_phase2_req); + free(data); +} + + +static int eap_peap_encrypt(struct eap_sm *sm, struct eap_peap_data *data, + int id, u8 *plain, size_t plain_len, + u8 **out_data, size_t *out_len) +{ + int res; + u8 *pos; + struct eap_hdr *resp; + + /* TODO: add support for fragmentation, if needed. This will need to + * add TLS Message Length field, if the frame is fragmented. + * Note: Microsoft IAS did not seem to like TLS Message Length with + * PEAP/MSCHAPv2. */ + resp = malloc(sizeof(struct eap_hdr) + 2 + data->ssl.tls_out_limit); + if (resp == NULL) + return -1; + + resp->code = EAP_CODE_RESPONSE; + resp->identifier = id; + + pos = (u8 *) (resp + 1); + *pos++ = EAP_TYPE_PEAP; + *pos++ = data->peap_version; + + res = tls_connection_encrypt(sm->ssl_ctx, data->ssl.conn, + plain, plain_len, + pos, data->ssl.tls_out_limit); + if (res < 0) { + wpa_printf(MSG_INFO, "EAP-PEAP: Failed to encrypt Phase 2 " + "data"); + free(resp); + return -1; + } + + *out_len = sizeof(struct eap_hdr) + 2 + res; + resp->length = host_to_be16(*out_len); + *out_data = (u8 *) resp; + return 0; +} + + +static int eap_peap_phase2_nak(struct eap_sm *sm, + struct eap_peap_data *data, + struct eap_hdr *hdr, + u8 **resp, size_t *resp_len) +{ + struct eap_hdr *resp_hdr; + u8 *pos = (u8 *) (hdr + 1); + + wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 Request: Nak type=%d", *pos); + wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Allowed Phase2 EAP types", + data->phase2_types, data->num_phase2_types); + *resp_len = sizeof(struct eap_hdr) + 1 + data->num_phase2_types; + *resp = malloc(*resp_len); + if (*resp == NULL) + return -1; + + resp_hdr = (struct eap_hdr *) (*resp); + resp_hdr->code = EAP_CODE_RESPONSE; + resp_hdr->identifier = hdr->identifier; + resp_hdr->length = host_to_be16(*resp_len); + pos = (u8 *) (resp_hdr + 1); + *pos++ = EAP_TYPE_NAK; + memcpy(pos, data->phase2_types, data->num_phase2_types); + + return 0; +} + + +static int eap_peap_phase2_request(struct eap_sm *sm, + struct eap_peap_data *data, + struct eap_method_ret *ret, + struct eap_hdr *req, + struct eap_hdr *hdr, + u8 **resp, size_t *resp_len) +{ + size_t len = be_to_host16(hdr->length); + u8 *pos; + struct eap_method_ret iret; + struct wpa_ssid *config = eap_get_config(sm); + + if (len <= sizeof(struct eap_hdr)) { + wpa_printf(MSG_INFO, "EAP-PEAP: too short " + "Phase 2 request (len=%lu)", (unsigned long) len); + return -1; + } + pos = (u8 *) (hdr + 1); + wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 Request: type=%d", *pos); + switch (*pos) { + case EAP_TYPE_IDENTITY: + *resp = eap_sm_buildIdentity(sm, req->identifier, resp_len, 1); + break; + case EAP_TYPE_TLV: + memset(&iret, 0, sizeof(iret)); + if (eap_tlv_process(sm, &iret, hdr, resp, resp_len)) { + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + return -1; + } + if (iret.methodState == METHOD_DONE || + iret.methodState == METHOD_MAY_CONT) { + ret->methodState = iret.methodState; + ret->decision = iret.decision; + data->phase2_success = 1; + } + break; + default: + if (data->phase2_type == EAP_TYPE_NONE) { + int i; + for (i = 0; i < data->num_phase2_types; i++) { + if (data->phase2_types[i] != *pos) + continue; + + data->phase2_type = *pos; + wpa_printf(MSG_DEBUG, "EAP-PEAP: Selected " + "Phase 2 EAP method %d", + data->phase2_type); + break; + } + } + if (*pos != data->phase2_type || *pos == EAP_TYPE_NONE) { + if (eap_peap_phase2_nak(sm, data, hdr, resp, resp_len)) + return -1; + return 0; + } + + if (data->phase2_priv == NULL) { + data->phase2_method = eap_sm_get_eap_methods(*pos); + if (data->phase2_method) { + sm->init_phase2 = 1; + data->phase2_priv = + data->phase2_method->init(sm); + sm->init_phase2 = 0; + } + } + if (data->phase2_priv == NULL || data->phase2_method == NULL) { + wpa_printf(MSG_INFO, "EAP-PEAP: failed to initialize " + "Phase 2 EAP method %d", *pos); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + return -1; + } + memset(&iret, 0, sizeof(iret)); + *resp = data->phase2_method->process(sm, data->phase2_priv, + &iret, (u8 *) hdr, len, + resp_len); + if ((iret.methodState == METHOD_DONE || + iret.methodState == METHOD_MAY_CONT) && + (iret.decision == DECISION_UNCOND_SUCC || + iret.decision == DECISION_COND_SUCC)) { + data->phase2_success = 1; + } + break; + } + + if (*resp == NULL && + (config->pending_req_identity || config->pending_req_password || + config->pending_req_otp)) { + free(data->pending_phase2_req); + data->pending_phase2_req = malloc(len); + if (data->pending_phase2_req) { + memcpy(data->pending_phase2_req, hdr, len); + data->pending_phase2_req_len = len; + } + } + + return 0; +} + + +static int eap_peap_decrypt(struct eap_sm *sm, + struct eap_peap_data *data, + struct eap_method_ret *ret, + struct eap_hdr *req, + u8 *in_data, size_t in_len, + u8 **out_data, size_t *out_len) +{ + u8 *in_decrypted; + int buf_len, len_decrypted, len, skip_change = 0, res; + struct eap_hdr *hdr, *rhdr; + u8 *resp = NULL; + size_t resp_len; + + wpa_printf(MSG_DEBUG, "EAP-PEAP: received %lu bytes encrypted data for" + " Phase 2", (unsigned long) in_len); + + if (data->pending_phase2_req) { + wpa_printf(MSG_DEBUG, "EAP-PEAP: Pending Phase 2 request - " + "skip decryption and use old data"); + in_decrypted = data->pending_phase2_req; + data->pending_phase2_req = NULL; + len_decrypted = data->pending_phase2_req_len; + skip_change = 1; + goto continue_req; + } + + res = eap_tls_data_reassemble(sm, &data->ssl, &in_data, &in_len); + if (res < 0 || res == 1) + return res; + + buf_len = in_len; + if (data->ssl.tls_in_total > buf_len) + buf_len = data->ssl.tls_in_total; + in_decrypted = malloc(buf_len); + if (in_decrypted == NULL) { + free(data->ssl.tls_in); + data->ssl.tls_in = NULL; + data->ssl.tls_in_len = 0; + wpa_printf(MSG_WARNING, "EAP-PEAP: failed to allocate memory " + "for decryption"); + return -1; + } + + len_decrypted = tls_connection_decrypt(sm->ssl_ctx, data->ssl.conn, + in_data, in_len, + in_decrypted, buf_len); + free(data->ssl.tls_in); + data->ssl.tls_in = NULL; + data->ssl.tls_in_len = 0; + if (len_decrypted < 0) { + wpa_printf(MSG_INFO, "EAP-PEAP: Failed to decrypt Phase 2 " + "data"); + free(in_decrypted); + return 0; + } + +continue_req: + wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Decrypted Phase 2 EAP", in_decrypted, + len_decrypted); + + hdr = (struct eap_hdr *) in_decrypted; + if (len_decrypted == 5 && hdr->code == EAP_CODE_REQUEST && + be_to_host16(hdr->length) == 5 && + in_decrypted[4] == EAP_TYPE_IDENTITY) { + /* At least FreeRADIUS seems to send full EAP header with + * EAP Request Identity */ + skip_change = 1; + } + if (len_decrypted >= 5 && hdr->code == EAP_CODE_REQUEST && + in_decrypted[4] == EAP_TYPE_TLV) { + skip_change = 1; + } + + if (data->peap_version == 0 && !skip_change) { + struct eap_hdr *nhdr = malloc(sizeof(struct eap_hdr) + + len_decrypted); + if (nhdr == NULL) { + free(in_decrypted); + return 0; + } + memcpy((u8 *) (nhdr + 1), in_decrypted, len_decrypted); + free(in_decrypted); + nhdr->code = req->code; + nhdr->identifier = req->identifier; + nhdr->length = host_to_be16(sizeof(struct eap_hdr) + + len_decrypted); + + len_decrypted += sizeof(struct eap_hdr); + in_decrypted = (u8 *) nhdr; + } + hdr = (struct eap_hdr *) in_decrypted; + if (len_decrypted < sizeof(*hdr)) { + free(in_decrypted); + wpa_printf(MSG_INFO, "EAP-PEAP: Too short Phase 2 " + "EAP frame (len=%d)", len_decrypted); + return 0; + } + len = be_to_host16(hdr->length); + if (len > len_decrypted) { + free(in_decrypted); + wpa_printf(MSG_INFO, "EAP-PEAP: Length mismatch in " + "Phase 2 EAP frame (len=%d hdr->length=%d)", + len_decrypted, len); + return 0; + } + if (len < len_decrypted) { + wpa_printf(MSG_INFO, "EAP-PEAP: Odd.. Phase 2 EAP header has " + "shorter length than full decrypted data (%d < %d)", + len, len_decrypted); + if (sm->workaround && len == 4 && len_decrypted == 5 && + in_decrypted[4] == EAP_TYPE_IDENTITY) { + /* Radiator 3.9 seems to set Phase 2 EAP header to use + * incorrect length for the EAP-Request Identity + * packet, so fix the inner header to interoperate.. + * This was fixed in 2004-06-23 patch for Radiator and + * this workaround can be removed at some point. */ + wpa_printf(MSG_INFO, "EAP-PEAP: workaround -> replace " + "Phase 2 EAP header len (%d) with real " + "decrypted len (%d)", len, len_decrypted); + len = len_decrypted; + hdr->length = host_to_be16(len); + } + } + wpa_printf(MSG_DEBUG, "EAP-PEAP: received Phase 2: code=%d " + "identifier=%d length=%d", hdr->code, hdr->identifier, len); + switch (hdr->code) { + case EAP_CODE_REQUEST: + if (eap_peap_phase2_request(sm, data, ret, req, hdr, + &resp, &resp_len)) { + free(in_decrypted); + wpa_printf(MSG_INFO, "EAP-PEAP: Phase2 Request " + "processing failed"); + return 0; + } + break; + case EAP_CODE_SUCCESS: + wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 Success"); + if (data->peap_version == 1) { + /* EAP-Success within TLS tunnel is used to indicate + * shutdown of the TLS channel. The authentication has + * been completed. */ + wpa_printf(MSG_DEBUG, "EAP-PEAP: Version 1 - " + "EAP-Success within TLS tunnel - " + "authentication completed"); + ret->decision = DECISION_UNCOND_SUCC; + ret->methodState = METHOD_DONE; + data->phase2_success = 1; + if (data->peap_outer_success == 2) { + free(in_decrypted); + wpa_printf(MSG_DEBUG, "EAP-PEAP: Use TLS ACK " + "to finish authentication"); + return 1; + } else if (data->peap_outer_success == 1) { + /* Reply with EAP-Success within the TLS + * channel to complete the authentication. */ + resp_len = sizeof(struct eap_hdr); + resp = malloc(resp_len); + if (resp) { + memset(resp, 0, resp_len); + rhdr = (struct eap_hdr *) resp; + rhdr->code = EAP_CODE_SUCCESS; + rhdr->identifier = hdr->identifier; + rhdr->length = host_to_be16(resp_len); + } + } else { + /* No EAP-Success expected for Phase 1 (outer, + * unencrypted auth), so force EAP state + * machine to SUCCESS state. */ + sm->peap_done = TRUE; + } + } else { + /* FIX: ? */ + } + break; + case EAP_CODE_FAILURE: + wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 Failure"); + ret->decision = DECISION_FAIL; + ret->methodState = METHOD_MAY_CONT; + ret->allowNotifications = FALSE; + /* Reply with EAP-Failure within the TLS channel to complete + * failure reporting. */ + resp_len = sizeof(struct eap_hdr); + resp = malloc(resp_len); + if (resp) { + memset(resp, 0, resp_len); + rhdr = (struct eap_hdr *) resp; + rhdr->code = EAP_CODE_FAILURE; + rhdr->identifier = hdr->identifier; + rhdr->length = host_to_be16(resp_len); + } + break; + default: + wpa_printf(MSG_INFO, "EAP-PEAP: Unexpected code=%d in " + "Phase 2 EAP header", hdr->code); + break; + } + + free(in_decrypted); + + if (resp) { + u8 *resp_pos; + size_t resp_send_len; + int skip_change = 0; + + wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: Encrypting Phase 2 data", + resp, resp_len); + /* PEAP version changes */ + if (resp_len >= 5 && resp[0] == EAP_CODE_RESPONSE && + resp[4] == EAP_TYPE_TLV) + skip_change = 1; + if (data->peap_version == 0 && !skip_change) { + resp_pos = resp + sizeof(struct eap_hdr); + resp_send_len = resp_len - sizeof(struct eap_hdr); + } else { + resp_pos = resp; + resp_send_len = resp_len; + } + + if (eap_peap_encrypt(sm, data, req->identifier, + resp_pos, resp_send_len, + out_data, out_len)) { + wpa_printf(MSG_INFO, "EAP-PEAP: Failed to encrypt " + "a Phase 2 frame"); + } + free(resp); + } + + return 0; +} + + +static u8 * eap_peap_process(struct eap_sm *sm, void *priv, + struct eap_method_ret *ret, + u8 *reqData, size_t reqDataLen, + size_t *respDataLen) +{ + struct eap_hdr *req; + int left, res; + unsigned int tls_msg_len; + u8 flags, *pos, *resp, id; + struct eap_peap_data *data = priv; + + if (tls_get_errors(sm->ssl_ctx)) { + wpa_printf(MSG_INFO, "EAP-PEAP: TLS errors detected"); + ret->ignore = TRUE; + return NULL; + } + + req = (struct eap_hdr *) reqData; + pos = (u8 *) (req + 1); + if (reqDataLen < sizeof(*req) + 2 || *pos != EAP_TYPE_PEAP || + (left = be_to_host16(req->length)) > reqDataLen) { + wpa_printf(MSG_INFO, "EAP-PEAP: Invalid frame"); + ret->ignore = TRUE; + return NULL; + } + left -= sizeof(struct eap_hdr); + id = req->identifier; + pos++; + flags = *pos++; + left -= 2; + wpa_printf(MSG_DEBUG, "EAP-PEAP: Received packet(len=%lu) - " + "Flags 0x%02x", (unsigned long) reqDataLen, flags); + if (flags & EAP_TLS_FLAGS_LENGTH_INCLUDED) { + if (left < 4) { + wpa_printf(MSG_INFO, "EAP-PEAP: Short frame with TLS " + "length"); + ret->ignore = TRUE; + return NULL; + } + tls_msg_len = (pos[0] << 24) | (pos[1] << 16) | (pos[2] << 8) | + pos[3]; + wpa_printf(MSG_DEBUG, "EAP-PEAP: TLS Message Length: %d", + tls_msg_len); + if (data->ssl.tls_in_left == 0) { + data->ssl.tls_in_total = tls_msg_len; + data->ssl.tls_in_left = tls_msg_len; + free(data->ssl.tls_in); + data->ssl.tls_in = NULL; + data->ssl.tls_in_len = 0; + } + pos += 4; + left -= 4; + } + + ret->ignore = FALSE; + ret->methodState = METHOD_CONT; + ret->decision = DECISION_FAIL; + ret->allowNotifications = TRUE; + + if (flags & EAP_TLS_FLAGS_START) { + wpa_printf(MSG_DEBUG, "EAP-PEAP: Start (server ver=%d, own " + "ver=%d)", flags & EAP_PEAP_VERSION_MASK, + data->peap_version); + if ((flags & EAP_PEAP_VERSION_MASK) < data->peap_version) + data->peap_version = flags & EAP_PEAP_VERSION_MASK; + if (data->force_peap_version >= 0 && + data->force_peap_version != data->peap_version) { + wpa_printf(MSG_WARNING, "EAP-PEAP: Failed to select " + "forced PEAP version %d", + data->force_peap_version); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + ret->allowNotifications = FALSE; + return NULL; + } + wpa_printf(MSG_DEBUG, "EAP-PEAP: Using PEAP version %d", + data->peap_version); + left = 0; /* make sure that this frame is empty, even though it + * should always be, anyway */ + } + + resp = NULL; + if (tls_connection_established(sm->ssl_ctx, data->ssl.conn) && + !data->resuming) { + res = eap_peap_decrypt(sm, data, ret, req, pos, left, + &resp, respDataLen); + } else { + res = eap_tls_process_helper(sm, &data->ssl, EAP_TYPE_PEAP, + data->peap_version, id, pos, left, + &resp, respDataLen); + + if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) { + char *label; + wpa_printf(MSG_DEBUG, + "EAP-PEAP: TLS done, proceed to Phase 2"); + free(data->key_data); + /* draft-josefsson-ppext-eap-tls-eap-05.txt + * specifies that PEAPv1 would use "client PEAP + * encryption" as the label. However, most existing + * PEAPv1 implementations seem to be using the old + * label, "client EAP encryption", instead. Use the old + * label by default, but allow it to be configured with + * phase1 parameter peaplabel=1. */ + if (data->peap_version > 1 || data->force_new_label) + label = "client PEAP encryption"; + else + label = "client EAP encryption"; + wpa_printf(MSG_DEBUG, "EAP-PEAP: using label '%s' in " + "key derivation", label); + data->key_data = + eap_tls_derive_key(sm, &data->ssl, label, + EAP_TLS_KEY_LEN); + if (data->key_data) { + wpa_hexdump_key(MSG_DEBUG, + "EAP-PEAP: Derived key", + data->key_data, + EAP_TLS_KEY_LEN); + } else { + wpa_printf(MSG_DEBUG, "EAP-PEAP: Failed to " + "derive key"); + } + data->resuming = 0; + } + } + + if (ret->methodState == METHOD_DONE) { + ret->allowNotifications = FALSE; + } + + if (res == 1) { + return eap_tls_build_ack(&data->ssl, respDataLen, id, + EAP_TYPE_PEAP, data->peap_version); + } + + return resp; +} + + +static Boolean eap_peap_has_reauth_data(struct eap_sm *sm, void *priv) +{ + struct eap_peap_data *data = priv; + return tls_connection_established(sm->ssl_ctx, data->ssl.conn) && + data->phase2_success; +} + + +static void eap_peap_deinit_for_reauth(struct eap_sm *sm, void *priv) +{ + struct eap_peap_data *data = priv; + free(data->pending_phase2_req); + data->pending_phase2_req = NULL; +} + + +static void * eap_peap_init_for_reauth(struct eap_sm *sm, void *priv) +{ + struct eap_peap_data *data = priv; + free(data->key_data); + data->key_data = NULL; + if (eap_tls_reauth_init(sm, &data->ssl)) { + free(data); + return NULL; + } + data->phase2_success = 0; + data->resuming = 1; + sm->peap_done = FALSE; + return priv; +} + + +static int eap_peap_get_status(struct eap_sm *sm, void *priv, char *buf, + size_t buflen, int verbose) +{ + struct eap_peap_data *data = priv; + int len; + + len = eap_tls_status(sm, &data->ssl, buf, buflen, verbose); + if (data->phase2_method) { + len += snprintf(buf + len, buflen - len, + "EAP-PEAPv%d Phase2 method=%s\n", + data->peap_version, data->phase2_method->name); + } + return len; +} + + +static Boolean eap_peap_isKeyAvailable(struct eap_sm *sm, void *priv) +{ + struct eap_peap_data *data = priv; + return data->key_data != NULL && data->phase2_success; +} + + +static u8 * eap_peap_getKey(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_peap_data *data = priv; + u8 *key; + + if (data->key_data == NULL || !data->phase2_success) + return NULL; + + key = malloc(EAP_TLS_KEY_LEN); + if (key == NULL) + return NULL; + + *len = EAP_TLS_KEY_LEN; + memcpy(key, data->key_data, EAP_TLS_KEY_LEN); + + return key; +} + + +const struct eap_method eap_method_peap = +{ + .method = EAP_TYPE_PEAP, + .name = "PEAP", + .init = eap_peap_init, + .deinit = eap_peap_deinit, + .process = eap_peap_process, + .isKeyAvailable = eap_peap_isKeyAvailable, + .getKey = eap_peap_getKey, + .get_status = eap_peap_get_status, + .has_reauth_data = eap_peap_has_reauth_data, + .deinit_for_reauth = eap_peap_deinit_for_reauth, + .init_for_reauth = eap_peap_init_for_reauth, +}; diff --git a/contrib/wpa_supplicant/eap_psk.c b/contrib/wpa_supplicant/eap_psk.c new file mode 100644 index 000000000000..3b325b59dd91 --- /dev/null +++ b/contrib/wpa_supplicant/eap_psk.c @@ -0,0 +1,563 @@ +/* + * WPA Supplicant / EAP-PSK (draft-bersani-eap-psk-05.txt) + * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include "common.h" +#include "eap_i.h" +#include "wpa_supplicant.h" +#include "config_ssid.h" +#include "md5.h" +#include "aes_wrap.h" + + +/* draft-bersani-eap-psk-03.txt mode. This is retained for interop testing and + * will be removed once an AS that supports draft5 becomes available. */ +#define EAP_PSK_DRAFT3 + +#define EAP_PSK_RAND_LEN 16 +#define EAP_PSK_MAC_LEN 16 +#define EAP_PSK_TEK_LEN 16 +#define EAP_PSK_MSK_LEN 64 + +#define EAP_PSK_R_FLAG_CONT 1 +#define EAP_PSK_R_FLAG_DONE_SUCCESS 2 +#define EAP_PSK_R_FLAG_DONE_FAILURE 3 + +/* EAP-PSK First Message (AS -> Supplicant) */ +struct eap_psk_hdr_1 { + u8 code; + u8 identifier; + u16 length; /* including code, identifier, and length */ + u8 type; /* EAP_TYPE_PSK */ +#ifndef EAP_PSK_DRAFT3 + u8 flags; +#endif /* EAP_PSK_DRAFT3 */ + u8 rand_s[EAP_PSK_RAND_LEN]; +#ifndef EAP_PSK_DRAFT3 + /* Followed by variable length ID_S */ +#endif /* EAP_PSK_DRAFT3 */ +} __attribute__ ((packed)); + +/* EAP-PSK Second Message (Supplicant -> AS) */ +struct eap_psk_hdr_2 { + u8 code; + u8 identifier; + u16 length; /* including code, identifier, and length */ + u8 type; /* EAP_TYPE_PSK */ +#ifndef EAP_PSK_DRAFT3 + u8 flags; + u8 rand_s[EAP_PSK_RAND_LEN]; +#endif /* EAP_PSK_DRAFT3 */ + u8 rand_p[EAP_PSK_RAND_LEN]; + u8 mac_p[EAP_PSK_MAC_LEN]; + /* Followed by variable length ID_P */ +} __attribute__ ((packed)); + +/* EAP-PSK Third Message (AS -> Supplicant) */ +struct eap_psk_hdr_3 { + u8 code; + u8 identifier; + u16 length; /* including code, identifier, and length */ + u8 type; /* EAP_TYPE_PSK */ +#ifndef EAP_PSK_DRAFT3 + u8 flags; + u8 rand_s[EAP_PSK_RAND_LEN]; +#endif /* EAP_PSK_DRAFT3 */ + u8 mac_s[EAP_PSK_MAC_LEN]; + /* Followed by variable length PCHANNEL */ +} __attribute__ ((packed)); + +/* EAP-PSK Fourth Message (Supplicant -> AS) */ +struct eap_psk_hdr_4 { + u8 code; + u8 identifier; + u16 length; /* including code, identifier, and length */ + u8 type; /* EAP_TYPE_PSK */ +#ifndef EAP_PSK_DRAFT3 + u8 flags; + u8 rand_s[EAP_PSK_RAND_LEN]; +#endif /* EAP_PSK_DRAFT3 */ + /* Followed by variable length PCHANNEL */ +} __attribute__ ((packed)); + + + +struct eap_psk_data { + enum { PSK_INIT, PSK_MAC_SENT, PSK_DONE } state; + u8 rand_s[EAP_PSK_RAND_LEN]; + u8 rand_p[EAP_PSK_RAND_LEN]; + u8 ak[16], kdk[16], tek[EAP_PSK_TEK_LEN]; + u8 *id_s, *id_p; + size_t id_s_len, id_p_len; + u8 key_data[EAP_PSK_MSK_LEN]; +}; + + +#define aes_block_size 16 + + +static void eap_psk_key_setup(const u8 *psk, u8 *ak, u8 *kdk) +{ + memset(ak, 0, aes_block_size); + aes_128_encrypt_block(psk, ak, ak); + memcpy(kdk, ak, aes_block_size); + ak[aes_block_size - 1] ^= 0x01; + kdk[aes_block_size - 1] ^= 0x02; + aes_128_encrypt_block(psk, ak, ak); + aes_128_encrypt_block(psk, kdk, kdk); +} + + +static void eap_psk_derive_keys(const u8 *kdk, const u8 *rb, u8 *tek, u8 *msk) +{ + u8 hash[aes_block_size]; + u8 counter = 1; + int i; + + aes_128_encrypt_block(kdk, rb, hash); + + hash[aes_block_size - 1] ^= counter; + aes_128_encrypt_block(kdk, hash, tek); + hash[aes_block_size - 1] ^= counter; + counter++; + + for (i = 0; i < EAP_PSK_MSK_LEN / aes_block_size; i++) { + hash[aes_block_size - 1] ^= counter; + aes_128_encrypt_block(kdk, hash, &msk[i * aes_block_size]); + hash[aes_block_size - 1] ^= counter; + counter++; + } +} + + +static void * eap_psk_init(struct eap_sm *sm) +{ + struct wpa_ssid *config = eap_get_config(sm); + struct eap_psk_data *data; + + if (config == NULL || !config->eappsk) { + wpa_printf(MSG_INFO, "EAP-PSK: pre-shared key not configured"); + return NULL; + } + + data = malloc(sizeof(*data)); + if (data == NULL) + return NULL; + memset(data, 0, sizeof(*data)); + eap_psk_key_setup(config->eappsk, data->ak, data->kdk); + wpa_hexdump_key(MSG_DEBUG, "EAP-PSK: AK", data->ak, 16); + wpa_hexdump_key(MSG_DEBUG, "EAP-PSK: KDK", data->kdk, 16); + data->state = PSK_INIT; + + if (config->nai) { + data->id_p = malloc(config->nai_len); + if (data->id_p) + memcpy(data->id_p, config->nai, config->nai_len); + data->id_p_len = config->nai_len; + } + if (data->id_p == NULL) { + wpa_printf(MSG_INFO, "EAP-PSK: could not get own identity"); + free(data); + return NULL; + } + +#ifdef EAP_PSK_DRAFT3 + if (config->server_nai) { + data->id_s = malloc(config->server_nai_len); + if (data->id_s) + memcpy(data->id_s, config->server_nai, + config->server_nai_len); + data->id_s_len = config->server_nai_len; + } + if (data->id_s == NULL) { + wpa_printf(MSG_INFO, "EAP-PSK: could not get server identity"); + free(data->id_p); + free(data); + return NULL; + } +#endif /* EAP_PSK_DRAFT3 */ + + return data; +} + + +static void eap_psk_deinit(struct eap_sm *sm, void *priv) +{ + struct eap_psk_data *data = priv; + free(data->id_s); + free(data->id_p); + free(data); +} + + +static u8 * eap_psk_process_1(struct eap_sm *sm, struct eap_psk_data *data, + struct eap_method_ret *ret, + u8 *reqData, size_t reqDataLen, + size_t *respDataLen) +{ + struct eap_psk_hdr_1 *hdr1; + struct eap_psk_hdr_2 *hdr2; + u8 *resp, *buf, *pos; + size_t buflen; + + wpa_printf(MSG_DEBUG, "EAP-PSK: in INIT state"); + + hdr1 = (struct eap_psk_hdr_1 *) reqData; + if (reqDataLen < sizeof(*hdr1) || + be_to_host16(hdr1->length) < sizeof(*hdr1) || + be_to_host16(hdr1->length) > reqDataLen) { + wpa_printf(MSG_INFO, "EAP-PSK: Invalid first message " + "length (%lu %d; expected %lu or more)", + (unsigned long) reqDataLen, + be_to_host16(hdr1->length), + (unsigned long) sizeof(*hdr1)); + ret->ignore = TRUE; + return NULL; + } +#ifndef EAP_PSK_DRAFT3 + wpa_printf(MSG_DEBUG, "EAP-PSK: Flags=0x%x", hdr1->flags); + if ((hdr1->flags & 0x03) != 0) { + wpa_printf(MSG_INFO, "EAP-PSK: Unexpected T=%d (expected 0)", + hdr1->flags & 0x03); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + return NULL; + } +#endif /* EAP_PSK_DRAFT3 */ + wpa_hexdump(MSG_DEBUG, "EAP-PSK: RAND_S", hdr1->rand_s, + EAP_PSK_RAND_LEN); + memcpy(data->rand_s, hdr1->rand_s, EAP_PSK_RAND_LEN); +#ifndef EAP_PSK_DRAFT3 + free(data->id_s); + data->id_s_len = be_to_host16(hdr1->length) - sizeof(*hdr1); + data->id_s = malloc(data->id_s_len); + if (data->id_s == NULL) { + wpa_printf(MSG_ERROR, "EAP-PSK: Failed to allocate memory for " + "ID_S (len=%d)", data->id_s_len); + ret->ignore = TRUE; + return NULL; + } + memcpy(data->id_s, (u8 *) (hdr1 + 1), data->id_s_len); + wpa_hexdump_ascii(MSG_DEBUG, "EAP-PSK: ID_S", + data->id_s, data->id_s_len); +#endif /* EAP_PSK_DRAFT3 */ + + if (hostapd_get_rand(data->rand_p, EAP_PSK_RAND_LEN)) { + wpa_printf(MSG_ERROR, "EAP-PSK: Failed to get random data"); + ret->ignore = TRUE; + return NULL; + } + + *respDataLen = sizeof(*hdr2) + data->id_p_len; + resp = malloc(*respDataLen); + if (resp == NULL) + return NULL; + hdr2 = (struct eap_psk_hdr_2 *) resp; + hdr2->code = EAP_CODE_RESPONSE; + hdr2->identifier = hdr1->identifier; + hdr2->length = host_to_be16(*respDataLen); + hdr2->type = EAP_TYPE_PSK; +#ifndef EAP_PSK_DRAFT3 + hdr2->flags = 1; /* T=1 */ + memcpy(hdr2->rand_s, hdr1->rand_s, EAP_PSK_RAND_LEN); +#endif /* EAP_PSK_DRAFT3 */ + memcpy(hdr2->rand_p, data->rand_p, EAP_PSK_RAND_LEN); + memcpy((u8 *) (hdr2 + 1), data->id_p, data->id_p_len); + /* MAC_P = OMAC1-AES-128(AK, ID_P||ID_S||RAND_S||RAND_P) */ + buflen = data->id_p_len + data->id_s_len + 2 * EAP_PSK_RAND_LEN; + buf = malloc(buflen); + if (buf == NULL) { + free(resp); + return NULL; + } + memcpy(buf, data->id_p, data->id_p_len); + pos = buf + data->id_p_len; + memcpy(pos, data->id_s, data->id_s_len); + pos += data->id_s_len; + memcpy(pos, data->rand_s, EAP_PSK_RAND_LEN); + pos += EAP_PSK_RAND_LEN; + memcpy(pos, data->rand_p, EAP_PSK_RAND_LEN); + omac1_aes_128(data->ak, buf, buflen, hdr2->mac_p); + free(buf); + wpa_hexdump(MSG_DEBUG, "EAP-PSK: RAND_P", hdr2->rand_p, + EAP_PSK_RAND_LEN); + wpa_hexdump(MSG_DEBUG, "EAP-PSK: MAC_P", hdr2->mac_p, EAP_PSK_MAC_LEN); + wpa_hexdump_ascii(MSG_DEBUG, "EAP-PSK: ID_P", + (u8 *) (hdr2 + 1), data->id_p_len); + + data->state = PSK_MAC_SENT; + + return resp; +} + + +static u8 * eap_psk_process_3(struct eap_sm *sm, struct eap_psk_data *data, + struct eap_method_ret *ret, + u8 *reqData, size_t reqDataLen, + size_t *respDataLen) +{ + struct eap_psk_hdr_3 *hdr3; + struct eap_psk_hdr_4 *hdr4; + u8 *resp, *buf, *pchannel, *tag, *msg, nonce[16]; + u8 mac[EAP_PSK_MAC_LEN]; + size_t buflen, left; + int failed = 0; + + wpa_printf(MSG_DEBUG, "EAP-PSK: in MAC_SENT state"); + + hdr3 = (struct eap_psk_hdr_3 *) reqData; + left = be_to_host16(hdr3->length); + if (left < sizeof(*hdr3) || reqDataLen < left) { + wpa_printf(MSG_INFO, "EAP-PSK: Invalid third message " + "length (%lu %d; expected %lu)", + (unsigned long) reqDataLen, + be_to_host16(hdr3->length), + (unsigned long) sizeof(*hdr3)); + ret->ignore = TRUE; + return NULL; + } + left -= sizeof(*hdr3); + pchannel = (u8 *) (hdr3 + 1); +#ifndef EAP_PSK_DRAFT3 + wpa_printf(MSG_DEBUG, "EAP-PSK: Flags=0x%x", hdr3->flags); + if ((hdr3->flags & 0x03) != 2) { + wpa_printf(MSG_INFO, "EAP-PSK: Unexpected T=%d (expected 2)", + hdr3->flags & 0x03); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + return NULL; + } + wpa_hexdump(MSG_DEBUG, "EAP-PSK: RAND_S", hdr3->rand_s, + EAP_PSK_RAND_LEN); + /* TODO: would not need to store RAND_S since it is available in this + * message. For now, since we store this anyway, verify that it matches + * with whatever the server is sending. */ + if (memcmp(hdr3->rand_s, data->rand_s, EAP_PSK_RAND_LEN) != 0) { + wpa_printf(MSG_ERROR, "EAP-PSK: RAND_S did not match"); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + return NULL; + } +#endif /* EAP_PSK_DRAFT3 */ + wpa_hexdump(MSG_DEBUG, "EAP-PSK: MAC_S", hdr3->mac_s, EAP_PSK_MAC_LEN); + wpa_hexdump(MSG_DEBUG, "EAP-PSK: PCHANNEL", pchannel, left); + + if (left < 4 + 16 + 1) { + wpa_printf(MSG_INFO, "EAP-PSK: Too short PCHANNEL data in " + "third message (len=%lu, expected 21)", + (unsigned long) left); + ret->ignore = TRUE; + return NULL; + } + + /* MAC_S = OMAC1-AES-128(AK, ID_S||RAND_P) */ + buflen = data->id_s_len + EAP_PSK_RAND_LEN; + buf = malloc(buflen); + if (buf == NULL) + return NULL; + memcpy(buf, data->id_s, data->id_s_len); + memcpy(buf + data->id_s_len, data->rand_p, EAP_PSK_RAND_LEN); + omac1_aes_128(data->ak, buf, buflen, mac); + free(buf); + if (memcmp(mac, hdr3->mac_s, EAP_PSK_MAC_LEN) != 0) { + wpa_printf(MSG_WARNING, "EAP-PSK: Invalid MAC_S in third " + "message"); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + return NULL; + } + wpa_printf(MSG_DEBUG, "EAP-PSK: MAC_S verified successfully"); + + eap_psk_derive_keys(data->kdk, data->rand_p, data->tek, + data->key_data); + wpa_hexdump_key(MSG_DEBUG, "EAP-PSK: TEK", data->tek, EAP_PSK_TEK_LEN); + wpa_hexdump_key(MSG_DEBUG, "EAP-PSK: MSK", data->key_data, + EAP_PSK_MSK_LEN); + + memset(nonce, 0, 12); + memcpy(nonce + 12, pchannel, 4); + pchannel += 4; + left -= 4; + + tag = pchannel; + pchannel += 16; + left -= 16; + + msg = pchannel; + + wpa_hexdump(MSG_MSGDUMP, "EAP-PSK: PCHANNEL - nonce", + nonce, sizeof(nonce)); + wpa_hexdump(MSG_MSGDUMP, "EAP-PSK: PCHANNEL - hdr", reqData, 5); + wpa_hexdump(MSG_MSGDUMP, "EAP-PSK: PCHANNEL - cipher msg", msg, left); + +#ifdef EAP_PSK_DRAFT3 + if (aes_128_eax_decrypt(data->tek, nonce, sizeof(nonce), + reqData, 5, msg, left, tag)) +#else /* EAP_PSK_DRAFT3 */ + if (aes_128_eax_decrypt(data->tek, nonce, sizeof(nonce), + reqData, 22, msg, left, tag)) +#endif /* EAP_PSK_DRAFT3 */ + { + wpa_printf(MSG_WARNING, "EAP-PSK: PCHANNEL decryption failed"); + return NULL; + } + wpa_hexdump(MSG_DEBUG, "EAP-PSK: Decrypted PCHANNEL message", + msg, left); + + /* Verify R flag */ + switch (msg[0] >> 6) { + case EAP_PSK_R_FLAG_CONT: + wpa_printf(MSG_DEBUG, "EAP-PSK: R flag - CONT - unsupported"); + return NULL; + case EAP_PSK_R_FLAG_DONE_SUCCESS: + wpa_printf(MSG_DEBUG, "EAP-PSK: R flag - DONE_SUCCESS"); + break; + case EAP_PSK_R_FLAG_DONE_FAILURE: + wpa_printf(MSG_DEBUG, "EAP-PSK: R flag - DONE_FAILURE"); + wpa_printf(MSG_INFO, "EAP-PSK: Authentication server rejected " + "authentication"); + failed = 1; + break; + } + + *respDataLen = sizeof(*hdr4) + 4 + 16 + 1; + resp = malloc(*respDataLen); + if (resp == NULL) + return NULL; + hdr4 = (struct eap_psk_hdr_4 *) resp; + hdr4->code = EAP_CODE_RESPONSE; + hdr4->identifier = hdr3->identifier; + hdr4->length = host_to_be16(*respDataLen); + hdr4->type = EAP_TYPE_PSK; +#ifndef EAP_PSK_DRAFT3 + hdr4->flags = 3; /* T=3 */ + memcpy(hdr4->rand_s, hdr3->rand_s, EAP_PSK_RAND_LEN); +#endif /* EAP_PSK_DRAFT3 */ + pchannel = (u8 *) (hdr4 + 1); + + /* nonce++ */ + inc_byte_array(nonce, sizeof(nonce)); + memcpy(pchannel, nonce + 12, 4); + + pchannel[4 + 16] = EAP_PSK_R_FLAG_DONE_SUCCESS << 6; + + wpa_hexdump(MSG_DEBUG, "EAP-PSK: reply message (plaintext)", + pchannel + 4 + 16, 1); +#ifdef EAP_PSK_DRAFT3 + aes_128_eax_encrypt(data->tek, nonce, sizeof(nonce), resp, 5, + pchannel + 4 + 16, 1, pchannel + 4); +#else /* EAP_PSK_DRAFT3 */ + aes_128_eax_encrypt(data->tek, nonce, sizeof(nonce), resp, 22, + pchannel + 4 + 16, 1, pchannel + 4); +#endif /* EAP_PSK_DRAFT3 */ + wpa_hexdump(MSG_DEBUG, "EAP-PSK: reply message (PCHANNEL)", + pchannel, 4 + 16 + 1); + + wpa_printf(MSG_DEBUG, "EAP-PSK: Completed %ssuccessfully", + failed ? "un" : ""); + data->state = PSK_DONE; + ret->methodState = METHOD_DONE; + ret->decision = failed ? DECISION_FAIL : DECISION_UNCOND_SUCC; + + return resp; +} + + +static u8 * eap_psk_process(struct eap_sm *sm, void *priv, + struct eap_method_ret *ret, + u8 *reqData, size_t reqDataLen, + size_t *respDataLen) +{ + struct eap_psk_data *data = priv; + struct eap_hdr *req; + u8 *pos, *resp = NULL; + size_t len; + + req = (struct eap_hdr *) reqData; + pos = (u8 *) (req + 1); + if (reqDataLen < sizeof(*req) + 1 || *pos != EAP_TYPE_PSK || + (len = be_to_host16(req->length)) > reqDataLen) { + wpa_printf(MSG_INFO, "EAP-PSK: Invalid frame"); + ret->ignore = TRUE; + return NULL; + } + + ret->ignore = FALSE; + ret->methodState = METHOD_CONT; + ret->decision = DECISION_FAIL; + ret->allowNotifications = TRUE; + + switch (data->state) { + case PSK_INIT: + resp = eap_psk_process_1(sm, data, ret, reqData, len, + respDataLen); + break; + case PSK_MAC_SENT: + resp = eap_psk_process_3(sm, data, ret, reqData, len, + respDataLen); + break; + case PSK_DONE: + wpa_printf(MSG_DEBUG, "EAP-PSK: in DONE state - ignore " + "unexpected message"); + ret->ignore = TRUE; + return NULL; + } + + if (ret->methodState == METHOD_DONE) { + ret->allowNotifications = FALSE; + } + + return resp; +} + + +static Boolean eap_psk_isKeyAvailable(struct eap_sm *sm, void *priv) +{ + struct eap_psk_data *data = priv; + return data->state == PSK_DONE; +} + + +static u8 * eap_psk_getKey(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_psk_data *data = priv; + u8 *key; + + if (data->state != PSK_DONE) + return NULL; + + key = malloc(EAP_PSK_MSK_LEN); + if (key == NULL) + return NULL; + + *len = EAP_PSK_MSK_LEN; + memcpy(key, data->key_data, EAP_PSK_MSK_LEN); + + return key; +} + + +const struct eap_method eap_method_psk = +{ + .method = EAP_TYPE_PSK, + .name = "PSK", + .init = eap_psk_init, + .deinit = eap_psk_deinit, + .process = eap_psk_process, + .isKeyAvailable = eap_psk_isKeyAvailable, + .getKey = eap_psk_getKey, +}; diff --git a/contrib/wpa_supplicant/eap_sim.c b/contrib/wpa_supplicant/eap_sim.c new file mode 100644 index 000000000000..f7ce191a6dda --- /dev/null +++ b/contrib/wpa_supplicant/eap_sim.c @@ -0,0 +1,1004 @@ +/* + * WPA Supplicant / EAP-SIM (draft-haverinen-pppext-eap-sim-13.txt) + * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include "common.h" +#include "eap_i.h" +#include "wpa_supplicant.h" +#include "config_ssid.h" +#include "sha1.h" +#include "pcsc_funcs.h" +#include "eap_sim_common.h" + +#define EAP_SIM_VERSION 1 + +/* EAP-SIM Subtypes */ +#define EAP_SIM_SUBTYPE_START 10 +#define EAP_SIM_SUBTYPE_CHALLENGE 11 +#define EAP_SIM_SUBTYPE_NOTIFICATION 12 +#define EAP_SIM_SUBTYPE_REAUTHENTICATION 13 +#define EAP_SIM_SUBTYPE_CLIENT_ERROR 14 + +/* AT_CLIENT_ERROR_CODE error codes */ +#define EAP_SIM_UNABLE_TO_PROCESS_PACKET 0 +#define EAP_SIM_UNSUPPORTED_VERSION 1 +#define EAP_SIM_INSUFFICIENT_NUM_OF_CHAL 2 +#define EAP_SIM_RAND_NOT_FRESH 3 + +#define KC_LEN 8 +#define SRES_LEN 4 +#define EAP_SIM_MAX_FAST_REAUTHS 1000 + +struct eap_sim_data { + u8 *ver_list; + size_t ver_list_len; + int selected_version; + int min_num_chal, num_chal; + + u8 kc[3][KC_LEN]; + u8 sres[3][SRES_LEN]; + u8 nonce_mt[EAP_SIM_NONCE_MT_LEN], nonce_s[EAP_SIM_NONCE_S_LEN]; + u8 mk[EAP_SIM_MK_LEN]; + u8 k_aut[EAP_SIM_K_AUT_LEN]; + u8 k_encr[EAP_SIM_K_ENCR_LEN]; + u8 msk[EAP_SIM_KEYING_DATA_LEN]; + u8 rand[3][GSM_RAND_LEN]; + + int num_id_req, num_notification; + u8 *pseudonym; + size_t pseudonym_len; + u8 *reauth_id; + size_t reauth_id_len; + int reauth; + unsigned int counter, counter_too_small; + u8 *last_eap_identity; + size_t last_eap_identity_len; + enum { CONTINUE, SUCCESS, FAILURE } state; +}; + + +static void * eap_sim_init(struct eap_sm *sm) +{ + struct eap_sim_data *data; + struct wpa_ssid *config = eap_get_config(sm); + + data = malloc(sizeof(*data)); + if (data == NULL) + return NULL; + memset(data, 0, sizeof(*data)); + + if (hostapd_get_rand(data->nonce_mt, EAP_SIM_NONCE_MT_LEN)) { + wpa_printf(MSG_WARNING, "EAP-SIM: Failed to get random data " + "for NONCE_MT"); + free(data); + return NULL; + } + + data->min_num_chal = 2; + if (config && config->phase1) { + char *pos = strstr(config->phase1, "sim_min_num_chal="); + if (pos) { + data->min_num_chal = atoi(pos + 17); + if (data->min_num_chal < 2 || data->min_num_chal > 3) { + wpa_printf(MSG_WARNING, "EAP-SIM: Invalid " + "sim_min_num_chal configuration " + "(%d, expected 2 or 3)", + data->min_num_chal); + free(data); + return NULL; + } + wpa_printf(MSG_DEBUG, "EAP-SIM: Set minimum number of " + "challenges to %d", data->min_num_chal); + } + } + + data->state = CONTINUE; + + return data; +} + + +static void eap_sim_deinit(struct eap_sm *sm, void *priv) +{ + struct eap_sim_data *data = priv; + if (data) { + free(data->ver_list); + free(data->pseudonym); + free(data->reauth_id); + free(data->last_eap_identity); + free(data); + } +} + + +static int eap_sim_gsm_auth(struct eap_sm *sm, struct eap_sim_data *data) +{ + wpa_printf(MSG_DEBUG, "EAP-SIM: GSM authentication algorithm"); +#ifdef PCSC_FUNCS + if (scard_gsm_auth(sm->scard_ctx, data->rand[0], + data->sres[0], data->kc[0]) || + scard_gsm_auth(sm->scard_ctx, data->rand[1], + data->sres[1], data->kc[1]) || + (data->num_chal > 2 && + scard_gsm_auth(sm->scard_ctx, data->rand[2], + data->sres[2], data->kc[2]))) { + wpa_printf(MSG_DEBUG, "EAP-SIM: GSM SIM authentication could " + "not be completed"); + return -1; + } +#else /* PCSC_FUNCS */ + /* These hardcoded Kc and SRES values are used for testing. RAND to + * KC/SREC mapping is very bogus as far as real authentication is + * concerned, but it is quite useful for cases where the AS is rotating + * the order of pre-configured values. */ + { + int i; + for (i = 0; i < data->num_chal; i++) { + if (data->rand[i][0] == 0xaa) { + memcpy(data->kc[i], + "\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7", + KC_LEN); + memcpy(data->sres[i], "\xd1\xd2\xd3\xd4", + SRES_LEN); + } else if (data->rand[i][0] == 0xbb) { + memcpy(data->kc[i], + "\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7", + KC_LEN); + memcpy(data->sres[i], "\xe1\xe2\xe3\xe4", + SRES_LEN); + } else { + memcpy(data->kc[i], + "\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7", + KC_LEN); + memcpy(data->sres[i], "\xf1\xf2\xf3\xf4", + SRES_LEN); + } + } + } +#endif /* PCSC_FUNCS */ + return 0; +} + + +static int eap_sim_supported_ver(struct eap_sim_data *data, int version) +{ + return version == EAP_SIM_VERSION; +} + + +static void eap_sim_derive_mk(struct eap_sim_data *data, + const u8 *identity, size_t identity_len) +{ + u8 sel_ver[2]; + const unsigned char *addr[5]; + size_t len[5]; + + addr[0] = identity; + len[0] = identity_len; + addr[1] = (u8 *) data->kc; + len[1] = data->num_chal * KC_LEN; + addr[2] = data->nonce_mt; + len[2] = EAP_SIM_NONCE_MT_LEN; + addr[3] = data->ver_list; + len[3] = data->ver_list_len; + addr[4] = sel_ver; + len[4] = 2; + + sel_ver[0] = data->selected_version >> 8; + sel_ver[1] = data->selected_version & 0xff; + + /* MK = SHA1(Identity|n*Kc|NONCE_MT|Version List|Selected Version) */ + sha1_vector(5, addr, len, data->mk); + wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: MK", data->mk, EAP_SIM_MK_LEN); +} + + +#define CLEAR_PSEUDONYM 0x01 +#define CLEAR_REAUTH_ID 0x02 +#define CLEAR_EAP_ID 0x04 + +static void eap_sim_clear_identities(struct eap_sim_data *data, int id) +{ + wpa_printf(MSG_DEBUG, "EAP-SIM: forgetting old%s%s%s", + id & CLEAR_PSEUDONYM ? " pseudonym" : "", + id & CLEAR_REAUTH_ID ? " reauth_id" : "", + id & CLEAR_EAP_ID ? " eap_id" : ""); + if (id & CLEAR_PSEUDONYM) { + free(data->pseudonym); + data->pseudonym = NULL; + data->pseudonym_len = 0; + } + if (id & CLEAR_REAUTH_ID) { + free(data->reauth_id); + data->reauth_id = NULL; + data->reauth_id_len = 0; + } + if (id & CLEAR_EAP_ID) { + free(data->last_eap_identity); + data->last_eap_identity = NULL; + data->last_eap_identity_len = 0; + } +} + + +static int eap_sim_learn_ids(struct eap_sim_data *data, + struct eap_sim_attrs *attr) +{ + if (attr->next_pseudonym) { + free(data->pseudonym); + data->pseudonym = malloc(attr->next_pseudonym_len); + if (data->pseudonym == NULL) { + wpa_printf(MSG_INFO, "EAP-SIM: (encr) No memory for " + "next pseudonym"); + return -1; + } + memcpy(data->pseudonym, attr->next_pseudonym, + attr->next_pseudonym_len); + data->pseudonym_len = attr->next_pseudonym_len; + wpa_hexdump_ascii(MSG_DEBUG, + "EAP-SIM: (encr) AT_NEXT_PSEUDONYM", + data->pseudonym, + data->pseudonym_len); + } + + if (attr->next_reauth_id) { + free(data->reauth_id); + data->reauth_id = malloc(attr->next_reauth_id_len); + if (data->reauth_id == NULL) { + wpa_printf(MSG_INFO, "EAP-SIM: (encr) No memory for " + "next reauth_id"); + return -1; + } + memcpy(data->reauth_id, attr->next_reauth_id, + attr->next_reauth_id_len); + data->reauth_id_len = attr->next_reauth_id_len; + wpa_hexdump_ascii(MSG_DEBUG, + "EAP-SIM: (encr) AT_NEXT_REAUTH_ID", + data->reauth_id, + data->reauth_id_len); + } + + return 0; +} + + +static u8 * eap_sim_client_error(struct eap_sm *sm, struct eap_sim_data *data, + struct eap_hdr *req, + size_t *respDataLen, int err) +{ + struct eap_sim_msg *msg; + + data->state = FAILURE; + data->num_id_req = 0; + data->num_notification = 0; + + msg = eap_sim_msg_init(EAP_CODE_RESPONSE, req->identifier, + EAP_TYPE_SIM, EAP_SIM_SUBTYPE_CLIENT_ERROR); + eap_sim_msg_add(msg, EAP_SIM_AT_CLIENT_ERROR_CODE, err, NULL, 0); + return eap_sim_msg_finish(msg, respDataLen, NULL, NULL, 0); +} + + +static u8 * eap_sim_response_start(struct eap_sm *sm, + struct eap_sim_data *data, + struct eap_hdr *req, + size_t *respDataLen, + enum eap_sim_id_req id_req) +{ + struct wpa_ssid *config = eap_get_config(sm); + u8 *identity = NULL; + size_t identity_len = 0; + struct eap_sim_msg *msg; + + data->reauth = 0; + if (id_req == ANY_ID && data->reauth_id) { + identity = data->reauth_id; + identity_len = data->reauth_id_len; + data->reauth = 1; + } else if ((id_req == ANY_ID || id_req == FULLAUTH_ID) && + data->pseudonym) { + identity = data->pseudonym; + identity_len = data->pseudonym_len; + eap_sim_clear_identities(data, CLEAR_REAUTH_ID); + } else if (id_req != NO_ID_REQ && config && config->identity) { + identity = config->identity; + identity_len = config->identity_len; + eap_sim_clear_identities(data, + CLEAR_PSEUDONYM | CLEAR_REAUTH_ID); + } + if (id_req != NO_ID_REQ) + eap_sim_clear_identities(data, CLEAR_EAP_ID); + + wpa_printf(MSG_DEBUG, "Generating EAP-SIM Start (id=%d)", + req->identifier); + msg = eap_sim_msg_init(EAP_CODE_RESPONSE, req->identifier, + EAP_TYPE_SIM, EAP_SIM_SUBTYPE_START); + wpa_hexdump(MSG_DEBUG, " AT_NONCE_MT", + data->nonce_mt, EAP_SIM_NONCE_MT_LEN); + eap_sim_msg_add(msg, EAP_SIM_AT_NONCE_MT, 0, + data->nonce_mt, EAP_SIM_NONCE_MT_LEN); + wpa_printf(MSG_DEBUG, " AT_SELECTED_VERSION %d", + data->selected_version); + eap_sim_msg_add(msg, EAP_SIM_AT_SELECTED_VERSION, + data->selected_version, NULL, 0); + + if (identity) { + wpa_hexdump_ascii(MSG_DEBUG, " AT_IDENTITY", + identity, identity_len); + eap_sim_msg_add(msg, EAP_SIM_AT_IDENTITY, identity_len, + identity, identity_len); + } + + return eap_sim_msg_finish(msg, respDataLen, NULL, NULL, 0); +} + + +static u8 * eap_sim_response_challenge(struct eap_sm *sm, + struct eap_sim_data *data, + struct eap_hdr *req, + size_t *respDataLen) +{ + struct eap_sim_msg *msg; + + wpa_printf(MSG_DEBUG, "Generating EAP-SIM Challenge (id=%d)", + req->identifier); + msg = eap_sim_msg_init(EAP_CODE_RESPONSE, req->identifier, + EAP_TYPE_SIM, EAP_SIM_SUBTYPE_CHALLENGE); + wpa_printf(MSG_DEBUG, " AT_MAC"); + eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC); + return eap_sim_msg_finish(msg, respDataLen, data->k_aut, + (u8 *) data->sres, + data->num_chal * SRES_LEN); +} + + +static u8 * eap_sim_response_reauth(struct eap_sm *sm, + struct eap_sim_data *data, + struct eap_hdr *req, + size_t *respDataLen, int counter_too_small) +{ + struct eap_sim_msg *msg; + unsigned int counter; + + wpa_printf(MSG_DEBUG, "Generating EAP-SIM Reauthentication (id=%d)", + req->identifier); + msg = eap_sim_msg_init(EAP_CODE_RESPONSE, req->identifier, + EAP_TYPE_SIM, + EAP_SIM_SUBTYPE_REAUTHENTICATION); + wpa_printf(MSG_DEBUG, " AT_IV"); + wpa_printf(MSG_DEBUG, " AT_ENCR_DATA"); + eap_sim_msg_add_encr_start(msg, EAP_SIM_AT_IV, EAP_SIM_AT_ENCR_DATA); + + if (counter_too_small) { + wpa_printf(MSG_DEBUG, " *AT_COUNTER_TOO_SMALL"); + eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER_TOO_SMALL, 0, NULL, 0); + counter = data->counter_too_small; + } else + counter = data->counter; + + wpa_printf(MSG_DEBUG, " *AT_COUNTER %d", counter); + eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER, counter, NULL, 0); + + if (eap_sim_msg_add_encr_end(msg, data->k_encr, EAP_SIM_AT_PADDING)) { + wpa_printf(MSG_WARNING, "EAP-SIM: Failed to encrypt " + "AT_ENCR_DATA"); + eap_sim_msg_free(msg); + return NULL; + } + wpa_printf(MSG_DEBUG, " AT_MAC"); + eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC); + return eap_sim_msg_finish(msg, respDataLen, data->k_aut, data->nonce_s, + EAP_SIM_NONCE_S_LEN); +} + + +static u8 * eap_sim_response_notification(struct eap_sm *sm, + struct eap_sim_data *data, + struct eap_hdr *req, + size_t *respDataLen, + u16 notification) +{ + struct eap_sim_msg *msg; + u8 *k_aut = (notification & 0x4000) == 0 ? data->k_aut : NULL; + + wpa_printf(MSG_DEBUG, "Generating EAP-SIM Notification (id=%d)", + req->identifier); + msg = eap_sim_msg_init(EAP_CODE_RESPONSE, req->identifier, + EAP_TYPE_SIM, EAP_SIM_SUBTYPE_NOTIFICATION); + wpa_printf(MSG_DEBUG, " AT_NOTIFICATION"); + eap_sim_msg_add(msg, EAP_SIM_AT_NOTIFICATION, notification, NULL, 0); + if (k_aut && data->reauth) { + wpa_printf(MSG_DEBUG, " AT_IV"); + wpa_printf(MSG_DEBUG, " AT_ENCR_DATA"); + eap_sim_msg_add_encr_start(msg, EAP_SIM_AT_IV, + EAP_SIM_AT_ENCR_DATA); + wpa_printf(MSG_DEBUG, " *AT_COUNTER %d", data->counter); + eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER, data->counter, + NULL, 0); + if (eap_sim_msg_add_encr_end(msg, data->k_encr, + EAP_SIM_AT_PADDING)) { + wpa_printf(MSG_WARNING, "EAP-SIM: Failed to encrypt " + "AT_ENCR_DATA"); + eap_sim_msg_free(msg); + return NULL; + } + } + if (k_aut) { + wpa_printf(MSG_DEBUG, " AT_MAC"); + eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC); + } + return eap_sim_msg_finish(msg, respDataLen, k_aut, (u8 *) "", 0); +} + + +static u8 * eap_sim_process_start(struct eap_sm *sm, struct eap_sim_data *data, + struct eap_hdr *req, size_t reqDataLen, + size_t *respDataLen, + struct eap_sim_attrs *attr) +{ + int i, selected_version = -1, id_error; + u8 *pos; + + wpa_printf(MSG_DEBUG, "EAP-SIM: subtype Start"); + if (attr->version_list == NULL) { + wpa_printf(MSG_INFO, "EAP-SIM: No AT_VERSION_LIST in " + "SIM/Start"); + return eap_sim_client_error(sm, data, req, respDataLen, + EAP_SIM_UNSUPPORTED_VERSION); + } + + free(data->ver_list); + data->ver_list = malloc(attr->version_list_len); + if (data->ver_list == NULL) { + wpa_printf(MSG_DEBUG, "EAP-SIM: Failed to allocate " + "memory for version list"); + return eap_sim_client_error(sm, data, req, respDataLen, + EAP_SIM_UNABLE_TO_PROCESS_PACKET); + } + memcpy(data->ver_list, attr->version_list, attr->version_list_len); + data->ver_list_len = attr->version_list_len; + pos = data->ver_list; + for (i = 0; i < data->ver_list_len / 2; i++) { + int ver = pos[0] * 256 + pos[1]; + pos += 2; + if (eap_sim_supported_ver(data, ver)) { + selected_version = ver; + break; + } + } + if (selected_version < 0) { + wpa_printf(MSG_INFO, "EAP-SIM: Could not find a supported " + "version"); + return eap_sim_client_error(sm, data, req, respDataLen, + EAP_SIM_UNSUPPORTED_VERSION); + } + wpa_printf(MSG_DEBUG, "EAP-SIM: Selected Version %d", + selected_version); + data->selected_version = selected_version; + + id_error = 0; + switch (attr->id_req) { + case NO_ID_REQ: + break; + case ANY_ID: + if (data->num_id_req > 0) + id_error++; + data->num_id_req++; + break; + case FULLAUTH_ID: + if (data->num_id_req > 1) + id_error++; + data->num_id_req++; + break; + case PERMANENT_ID: + if (data->num_id_req > 2) + id_error++; + data->num_id_req++; + break; + } + if (id_error) { + wpa_printf(MSG_INFO, "EAP-SIM: Too many ID requests " + "used within one authentication"); + return eap_sim_client_error(sm, data, req, respDataLen, + EAP_SIM_UNABLE_TO_PROCESS_PACKET); + } + + return eap_sim_response_start(sm, data, req, respDataLen, + attr->id_req); +} + + +static u8 * eap_sim_process_challenge(struct eap_sm *sm, + struct eap_sim_data *data, + struct eap_hdr *req, size_t reqDataLen, + size_t *respDataLen, + struct eap_sim_attrs *attr) +{ + struct wpa_ssid *config = eap_get_config(sm); + u8 *identity; + size_t identity_len; + struct eap_sim_attrs eattr; + + wpa_printf(MSG_DEBUG, "EAP-SIM: subtype Challenge"); + data->reauth = 0; + if (!attr->mac || !attr->rand) { + wpa_printf(MSG_WARNING, "EAP-SIM: Challenge message " + "did not include%s%s", + !attr->mac ? " AT_MAC" : "", + !attr->rand ? " AT_RAND" : ""); + return eap_sim_client_error(sm, data, req, respDataLen, + EAP_SIM_UNABLE_TO_PROCESS_PACKET); + } + + wpa_printf(MSG_DEBUG, "EAP-SIM: %lu challenges", + (unsigned long) attr->num_chal); + if (attr->num_chal < data->min_num_chal) { + wpa_printf(MSG_INFO, "EAP-SIM: Insufficient number of " + "challenges (%lu)", (unsigned long) attr->num_chal); + return eap_sim_client_error(sm, data, req, respDataLen, + EAP_SIM_INSUFFICIENT_NUM_OF_CHAL); + } + if (attr->num_chal > 3) { + wpa_printf(MSG_INFO, "EAP-SIM: Too many challenges " + "(%lu)", (unsigned long) attr->num_chal); + return eap_sim_client_error(sm, data, req, respDataLen, + EAP_SIM_UNABLE_TO_PROCESS_PACKET); + } + + /* Verify that RANDs are different */ + if (memcmp(attr->rand, attr->rand + GSM_RAND_LEN, + GSM_RAND_LEN) == 0 || + (attr->num_chal > 2 && + (memcmp(attr->rand, attr->rand + 2 * GSM_RAND_LEN, + GSM_RAND_LEN) == 0 || + memcmp(attr->rand + GSM_RAND_LEN, + attr->rand + 2 * GSM_RAND_LEN, + GSM_RAND_LEN) == 0))) { + wpa_printf(MSG_INFO, "EAP-SIM: Same RAND used multiple times"); + return eap_sim_client_error(sm, data, req, respDataLen, + EAP_SIM_RAND_NOT_FRESH); + } + + memcpy(data->rand, attr->rand, attr->num_chal * GSM_RAND_LEN); + data->num_chal = attr->num_chal; + + if (eap_sim_gsm_auth(sm, data)) { + wpa_printf(MSG_WARNING, "EAP-SIM: GSM authentication failed"); + return eap_sim_client_error(sm, data, req, respDataLen, + EAP_SIM_UNABLE_TO_PROCESS_PACKET); + } + if (data->last_eap_identity) { + identity = data->last_eap_identity; + identity_len = data->last_eap_identity_len; + } else if (data->pseudonym) { + identity = data->pseudonym; + identity_len = data->pseudonym_len; + } else { + identity = config->identity; + identity_len = config->identity_len; + } + wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM: Selected identity for MK " + "derivation", identity, identity_len); + eap_sim_derive_mk(data, identity, identity_len); + eap_sim_derive_keys(data->mk, data->k_encr, data->k_aut, data->msk); + if (eap_sim_verify_mac(data->k_aut, (u8 *) req, reqDataLen, attr->mac, + data->nonce_mt, EAP_SIM_NONCE_MT_LEN)) { + wpa_printf(MSG_WARNING, "EAP-SIM: Challenge message " + "used invalid AT_MAC"); + return eap_sim_client_error(sm, data, req, respDataLen, + EAP_SIM_UNABLE_TO_PROCESS_PACKET); + } + + /* Old reauthentication and pseudonym identities must not be used + * anymore. In other words, if no new identities are received, full + * authentication will be used on next reauthentication. */ + eap_sim_clear_identities(data, CLEAR_PSEUDONYM | CLEAR_REAUTH_ID | + CLEAR_EAP_ID); + + if (attr->encr_data) { + if (eap_sim_parse_encr(data->k_encr, attr->encr_data, + attr->encr_data_len, attr->iv, &eattr, + 0)) { + return eap_sim_client_error( + sm, data, req, respDataLen, + EAP_SIM_UNABLE_TO_PROCESS_PACKET); + } + eap_sim_learn_ids(data, &eattr); + } + + if (data->state != FAILURE) + data->state = SUCCESS; + + data->num_id_req = 0; + data->num_notification = 0; + /* draft-haverinen-pppext-eap-sim-13.txt specifies that counter + * is initialized to one after fullauth, but initializing it to + * zero makes it easier to implement reauth verification. */ + data->counter = 0; + return eap_sim_response_challenge(sm, data, req, respDataLen); +} + + +static int eap_sim_process_notification_reauth(struct eap_sim_data *data, + struct eap_hdr *req, + size_t reqDataLen, + struct eap_sim_attrs *attr) +{ + struct eap_sim_attrs eattr; + + if (attr->encr_data == NULL || attr->iv == NULL) { + wpa_printf(MSG_WARNING, "EAP-SIM: Notification message after " + "reauth did not include encrypted data"); + return -1; + } + + if (eap_sim_parse_encr(data->k_encr, attr->encr_data, + attr->encr_data_len, attr->iv, &eattr, 0)) { + wpa_printf(MSG_WARNING, "EAP-SIM: Failed to parse encrypted " + "data from notification message"); + return -1; + } + + if (eattr.counter != data->counter) { + wpa_printf(MSG_WARNING, "EAP-SIM: Counter in notification " + "message does not match with counter in reauth " + "message"); + return -1; + } + + return 0; +} + + +static int eap_sim_process_notification_auth(struct eap_sim_data *data, + struct eap_hdr *req, + size_t reqDataLen, + struct eap_sim_attrs *attr) +{ + if (attr->mac == NULL) { + wpa_printf(MSG_INFO, "EAP-SIM: no AT_MAC in after_auth " + "Notification message"); + return -1; + } + + if (eap_sim_verify_mac(data->k_aut, (u8 *) req, reqDataLen, attr->mac, + (u8 *) "", 0)) { + wpa_printf(MSG_WARNING, "EAP-SIM: Notification message " + "used invalid AT_MAC"); + return -1; + } + + if (data->reauth && + eap_sim_process_notification_reauth(data, req, reqDataLen, attr)) { + wpa_printf(MSG_WARNING, "EAP-SIM: Invalid notification " + "message after reauth"); + return -1; + } + + return 0; +} + + +static u8 * eap_sim_process_notification(struct eap_sm *sm, + struct eap_sim_data *data, + struct eap_hdr *req, + size_t reqDataLen, + size_t *respDataLen, + struct eap_sim_attrs *attr) +{ + wpa_printf(MSG_DEBUG, "EAP-SIM: subtype Notification"); + if (data->num_notification > 0) { + wpa_printf(MSG_INFO, "EAP-SIM: too many notification " + "rounds (only one allowed)"); + return eap_sim_client_error(sm, data, req, respDataLen, + EAP_SIM_UNABLE_TO_PROCESS_PACKET); + } + data->num_notification++; + if (attr->notification == -1) { + wpa_printf(MSG_INFO, "EAP-SIM: no AT_NOTIFICATION in " + "Notification message"); + return eap_sim_client_error(sm, data, req, respDataLen, + EAP_SIM_UNABLE_TO_PROCESS_PACKET); + } + + if ((attr->notification & 0x4000) == 0 && + eap_sim_process_notification_auth(data, req, reqDataLen, attr)) { + return eap_sim_client_error(sm, data, req, respDataLen, + EAP_SIM_UNABLE_TO_PROCESS_PACKET); + } + + eap_sim_report_notification(sm->msg_ctx, attr->notification, 0); + if (attr->notification >= 0 && attr->notification < 32768) { + data->state = FAILURE; + } + return eap_sim_response_notification(sm, data, req, respDataLen, + attr->notification); +} + + +static u8 * eap_sim_process_reauthentication(struct eap_sm *sm, + struct eap_sim_data *data, + struct eap_hdr *req, + size_t reqDataLen, + size_t *respDataLen, + struct eap_sim_attrs *attr) +{ + struct eap_sim_attrs eattr; + + wpa_printf(MSG_DEBUG, "EAP-SIM: subtype Reauthentication"); + + if (data->reauth_id == NULL) { + wpa_printf(MSG_WARNING, "EAP-SIM: Server is trying " + "reauthentication, but no reauth_id available"); + return eap_sim_client_error(sm, data, req, respDataLen, + EAP_SIM_UNABLE_TO_PROCESS_PACKET); + } + + data->reauth = 1; + if (eap_sim_verify_mac(data->k_aut, (u8 *) req, reqDataLen, + attr->mac, (u8 *) "", 0)) { + wpa_printf(MSG_WARNING, "EAP-SIM: Reauthentication " + "did not have valid AT_MAC"); + return eap_sim_client_error(sm, data, req, respDataLen, + EAP_SIM_UNABLE_TO_PROCESS_PACKET); + } + + if (attr->encr_data == NULL || attr->iv == NULL) { + wpa_printf(MSG_WARNING, "EAP-SIM: Reauthentication " + "message did not include encrypted data"); + return eap_sim_client_error(sm, data, req, respDataLen, + EAP_SIM_UNABLE_TO_PROCESS_PACKET); + } + + if (eap_sim_parse_encr(data->k_encr, attr->encr_data, + attr->encr_data_len, attr->iv, &eattr, 0)) { + wpa_printf(MSG_WARNING, "EAP-SIM: Failed to parse encrypted " + "data from reauthentication message"); + return eap_sim_client_error(sm, data, req, respDataLen, + EAP_SIM_UNABLE_TO_PROCESS_PACKET); + } + + if (eattr.nonce_s == NULL || eattr.counter < 0) { + wpa_printf(MSG_INFO, "EAP-SIM: (encr) No%s%s in reauth packet", + !eattr.nonce_s ? " AT_NONCE_S" : "", + eattr.counter < 0 ? " AT_COUNTER" : ""); + return eap_sim_client_error(sm, data, req, respDataLen, + EAP_SIM_UNABLE_TO_PROCESS_PACKET); + } + + if (eattr.counter <= data->counter) { + wpa_printf(MSG_INFO, "EAP-SIM: (encr) Invalid counter " + "(%d <= %d)", eattr.counter, data->counter); + data->counter_too_small = eattr.counter; + /* Reply using Re-auth w/ AT_COUNTER_TOO_SMALL. The current + * reauth_id must not be used to start a new reauthentication. + * However, since it was used in the last EAP-Response-Identity + * packet, it has to saved for the following fullauth to be + * used in MK derivation. */ + free(data->last_eap_identity); + data->last_eap_identity = data->reauth_id; + data->last_eap_identity_len = data->reauth_id_len; + data->reauth_id = NULL; + data->reauth_id_len = 0; + return eap_sim_response_reauth(sm, data, req, respDataLen, 1); + } + data->counter = eattr.counter; + + memcpy(data->nonce_s, eattr.nonce_s, EAP_SIM_NONCE_S_LEN); + wpa_hexdump(MSG_DEBUG, "EAP-SIM: (encr) AT_NONCE_S", + data->nonce_s, EAP_SIM_NONCE_S_LEN); + + eap_sim_derive_keys_reauth(data->counter, + data->reauth_id, data->reauth_id_len, + data->nonce_s, data->mk, data->msk); + eap_sim_clear_identities(data, CLEAR_REAUTH_ID | CLEAR_EAP_ID); + eap_sim_learn_ids(data, &eattr); + + if (data->state != FAILURE) + data->state = SUCCESS; + + data->num_id_req = 0; + data->num_notification = 0; + if (data->counter > EAP_SIM_MAX_FAST_REAUTHS) { + wpa_printf(MSG_DEBUG, "EAP-SIM: Maximum number of " + "fast reauths performed - force fullauth"); + eap_sim_clear_identities(data, CLEAR_REAUTH_ID | CLEAR_EAP_ID); + } + return eap_sim_response_reauth(sm, data, req, respDataLen, 0); +} + + +static u8 * eap_sim_process(struct eap_sm *sm, void *priv, + struct eap_method_ret *ret, + u8 *reqData, size_t reqDataLen, + size_t *respDataLen) +{ + struct eap_sim_data *data = priv; + struct wpa_ssid *config = eap_get_config(sm); + struct eap_hdr *req; + u8 *pos, subtype, *res; + struct eap_sim_attrs attr; + size_t len; + + wpa_hexdump(MSG_DEBUG, "EAP-SIM: EAP data", reqData, reqDataLen); + if (config == NULL || config->identity == NULL) { + wpa_printf(MSG_INFO, "EAP-SIM: Identity not configured"); + eap_sm_request_identity(sm, config); + ret->ignore = TRUE; + return NULL; + } + + req = (struct eap_hdr *) reqData; + pos = (u8 *) (req + 1); + if (reqDataLen < sizeof(*req) + 4 || *pos != EAP_TYPE_SIM || + (len = be_to_host16(req->length)) > reqDataLen) { + wpa_printf(MSG_INFO, "EAP-SIM: Invalid frame"); + ret->ignore = TRUE; + return NULL; + } + + ret->ignore = FALSE; + ret->methodState = METHOD_CONT; + ret->decision = DECISION_FAIL; + ret->allowNotifications = TRUE; + + pos++; + subtype = *pos++; + wpa_printf(MSG_DEBUG, "EAP-SIM: Subtype=%d", subtype); + pos += 2; /* Reserved */ + + if (eap_sim_parse_attr(pos, reqData + len, &attr, 0, 0)) { + res = eap_sim_client_error(sm, data, req, respDataLen, + EAP_SIM_UNABLE_TO_PROCESS_PACKET); + goto done; + } + + switch (subtype) { + case EAP_SIM_SUBTYPE_START: + res = eap_sim_process_start(sm, data, req, len, + respDataLen, &attr); + break; + case EAP_SIM_SUBTYPE_CHALLENGE: + res = eap_sim_process_challenge(sm, data, req, len, + respDataLen, &attr); + break; + case EAP_SIM_SUBTYPE_NOTIFICATION: + res = eap_sim_process_notification(sm, data, req, len, + respDataLen, &attr); + break; + case EAP_SIM_SUBTYPE_REAUTHENTICATION: + res = eap_sim_process_reauthentication(sm, data, req, len, + respDataLen, &attr); + break; + case EAP_SIM_SUBTYPE_CLIENT_ERROR: + wpa_printf(MSG_DEBUG, "EAP-SIM: subtype Client-Error"); + res = eap_sim_client_error(sm, data, req, respDataLen, + EAP_SIM_UNABLE_TO_PROCESS_PACKET); + break; + default: + wpa_printf(MSG_DEBUG, "EAP-SIM: Unknown subtype=%d", subtype); + res = eap_sim_client_error(sm, data, req, respDataLen, + EAP_SIM_UNABLE_TO_PROCESS_PACKET); + break; + } + +done: + if (data->state == FAILURE) { + ret->decision = DECISION_FAIL; + ret->methodState = METHOD_DONE; + } else if (data->state == SUCCESS) { + ret->decision = DECISION_UNCOND_SUCC; + ret->methodState = METHOD_DONE; + } + + if (ret->methodState == METHOD_DONE) { + ret->allowNotifications = FALSE; + } + + return res; +} + + +static Boolean eap_sim_has_reauth_data(struct eap_sm *sm, void *priv) +{ + struct eap_sim_data *data = priv; + return data->pseudonym || data->reauth_id; +} + + +static void eap_sim_deinit_for_reauth(struct eap_sm *sm, void *priv) +{ + struct eap_sim_data *data = priv; + eap_sim_clear_identities(data, CLEAR_EAP_ID); +} + + +static void * eap_sim_init_for_reauth(struct eap_sm *sm, void *priv) +{ + struct eap_sim_data *data = priv; + if (hostapd_get_rand(data->nonce_mt, EAP_SIM_NONCE_MT_LEN)) { + wpa_printf(MSG_WARNING, "EAP-SIM: Failed to get random data " + "for NONCE_MT"); + free(data); + return NULL; + } + data->num_id_req = 0; + data->num_notification = 0; + data->state = CONTINUE; + return priv; +} + + +static const u8 * eap_sim_get_identity(struct eap_sm *sm, void *priv, + size_t *len) +{ + struct eap_sim_data *data = priv; + + if (data->reauth_id) { + *len = data->reauth_id_len; + return data->reauth_id; + } + + if (data->pseudonym) { + *len = data->pseudonym_len; + return data->pseudonym; + } + + return NULL; +} + + +static Boolean eap_sim_isKeyAvailable(struct eap_sm *sm, void *priv) +{ + struct eap_sim_data *data = priv; + return data->state == SUCCESS; +} + + +static u8 * eap_sim_getKey(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_sim_data *data = priv; + u8 *key; + + if (data->state != SUCCESS) + return NULL; + + key = malloc(EAP_SIM_KEYING_DATA_LEN); + if (key == NULL) + return NULL; + + *len = EAP_SIM_KEYING_DATA_LEN; + memcpy(key, data->msk, EAP_SIM_KEYING_DATA_LEN); + + return key; +} + + +const struct eap_method eap_method_sim = +{ + .method = EAP_TYPE_SIM, + .name = "SIM", + .init = eap_sim_init, + .deinit = eap_sim_deinit, + .process = eap_sim_process, + .isKeyAvailable = eap_sim_isKeyAvailable, + .getKey = eap_sim_getKey, + .has_reauth_data = eap_sim_has_reauth_data, + .deinit_for_reauth = eap_sim_deinit_for_reauth, + .init_for_reauth = eap_sim_init_for_reauth, + .get_identity = eap_sim_get_identity, +}; diff --git a/contrib/wpa_supplicant/eap_sim_common.c b/contrib/wpa_supplicant/eap_sim_common.c new file mode 100644 index 000000000000..98f4fb7d3014 --- /dev/null +++ b/contrib/wpa_supplicant/eap_sim_common.c @@ -0,0 +1,788 @@ +/* + * WPA Supplicant / EAP-SIM/AKA shared routines + * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include "common.h" +#include "eap_i.h" +#include "sha1.h" +#include "aes_wrap.h" +#include "eap_sim_common.h" + + +#define MSK_LEN 8 +#define EMSK_LEN 8 + + +static void eap_sim_prf(const u8 *key, u8 *x, size_t xlen) +{ + u8 xkey[64]; + u32 t[5], _t[5]; + int i, j, m, k; + u8 *xpos = x; + u32 carry; + + /* FIPS 186-2 + change notice 1 */ + + memcpy(xkey, key, EAP_SIM_MK_LEN); + memset(xkey + EAP_SIM_MK_LEN, 0, 64 - EAP_SIM_MK_LEN); + t[0] = 0x67452301; + t[1] = 0xEFCDAB89; + t[2] = 0x98BADCFE; + t[3] = 0x10325476; + t[4] = 0xC3D2E1F0; + + m = xlen / 40; + for (j = 0; j < m; j++) { + /* XSEED_j = 0 */ + for (i = 0; i < 2; i++) { + /* XVAL = (XKEY + XSEED_j) mod 2^b */ + + /* w_i = G(t, XVAL) */ + memcpy(_t, t, 20); + sha1_transform((u8 *) _t, xkey); + _t[0] = host_to_be32(_t[0]); + _t[1] = host_to_be32(_t[1]); + _t[2] = host_to_be32(_t[2]); + _t[3] = host_to_be32(_t[3]); + _t[4] = host_to_be32(_t[4]); + memcpy(xpos, _t, 20); + + /* XKEY = (1 + XKEY + w_i) mod 2^b */ + carry = 1; + for (k = 19; k >= 0; k--) { + carry += xkey[k] + xpos[k]; + xkey[k] = carry & 0xff; + carry >>= 8; + } + + xpos += SHA1_MAC_LEN; + } + /* x_j = w_0|w_1 */ + } +} + + +void eap_sim_derive_keys(const u8 *mk, u8 *k_encr, u8 *k_aut, u8 *msk) +{ + u8 buf[120], *pos; + eap_sim_prf(mk, buf, 120); + pos = buf; + memcpy(k_encr, pos, EAP_SIM_K_ENCR_LEN); + pos += EAP_SIM_K_ENCR_LEN; + memcpy(k_aut, pos, EAP_SIM_K_AUT_LEN); + pos += EAP_SIM_K_AUT_LEN; + memcpy(msk, pos, EAP_SIM_KEYING_DATA_LEN); + pos += MSK_LEN; + + wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: K_encr", + k_encr, EAP_SIM_K_ENCR_LEN); + wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: K_aut", + k_aut, EAP_SIM_K_ENCR_LEN); + wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: MSK", + msk, MSK_LEN); + wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: Ext. MSK", + msk + MSK_LEN, EMSK_LEN); + wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: keying material", + msk, EAP_SIM_KEYING_DATA_LEN); +} + + +void eap_sim_derive_keys_reauth(unsigned int _counter, + const u8 *identity, size_t identity_len, + const u8 *nonce_s, const u8 *mk, u8 *msk) +{ + u8 xkey[SHA1_MAC_LEN]; + u8 counter[2]; + const u8 *addr[4]; + size_t len[4]; + + addr[0] = identity; + len[0] = identity_len; + addr[1] = counter; + len[1] = 2; + addr[2] = nonce_s; + len[2] = EAP_SIM_NONCE_S_LEN; + addr[3] = mk; + len[3] = EAP_SIM_MK_LEN; + + counter[0] = _counter >> 8; + counter[1] = _counter & 0xff; + + wpa_printf(MSG_DEBUG, "EAP-SIM: Deriving keying data from reauth"); + wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM: Identity", + identity, identity_len); + wpa_hexdump(MSG_DEBUG, "EAP-SIM: counter", counter, 2); + wpa_hexdump(MSG_DEBUG, "EAP-SIM: NONCE_S", nonce_s, + EAP_SIM_NONCE_S_LEN); + wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: MK", mk, EAP_SIM_MK_LEN); + + /* XKEY' = SHA1(Identity|counter|NONCE_S|MK) */ + sha1_vector(4, addr, len, xkey); + wpa_hexdump(MSG_DEBUG, "EAP-SIM: XKEY'", xkey, SHA1_MAC_LEN); + + eap_sim_prf(xkey, msk, EAP_SIM_KEYING_DATA_LEN); + wpa_hexdump(MSG_DEBUG, "EAP-SIM: MSK", msk, MSK_LEN); + wpa_hexdump(MSG_DEBUG, "EAP-SIM: Ext. MSK", msk + MSK_LEN, EMSK_LEN); + wpa_hexdump(MSG_DEBUG, "EAP-SIM: keying material", + msk, EAP_SIM_KEYING_DATA_LEN); +} + + +int eap_sim_verify_mac(const u8 *k_aut, u8 *req, size_t req_len, u8 *mac, + u8 *extra, size_t extra_len) +{ + unsigned char hmac[SHA1_MAC_LEN]; + const u8 *addr[2]; + size_t len[2]; + u8 rx_mac[EAP_SIM_MAC_LEN]; + + if (mac == NULL) + return -1; + + addr[0] = req; + len[0] = req_len; + addr[1] = extra; + len[1] = extra_len; + + /* HMAC-SHA1-128 */ + memcpy(rx_mac, mac, EAP_SIM_MAC_LEN); + memset(mac, 0, EAP_SIM_MAC_LEN); + hmac_sha1_vector(k_aut, EAP_SIM_K_AUT_LEN, 2, addr, len, hmac); + memcpy(mac, rx_mac, EAP_SIM_MAC_LEN); + + return (memcmp(hmac, mac, EAP_SIM_MAC_LEN) == 0) ? 0 : 1; +} + + +void eap_sim_add_mac(const u8 *k_aut, u8 *msg, size_t msg_len, u8 *mac, + const u8 *extra, size_t extra_len) +{ + unsigned char hmac[SHA1_MAC_LEN]; + const u8 *addr[2]; + size_t len[2]; + + addr[0] = msg; + len[0] = msg_len; + addr[1] = extra; + len[1] = extra_len; + + /* HMAC-SHA1-128 */ + memset(mac, 0, EAP_SIM_MAC_LEN); + hmac_sha1_vector(k_aut, EAP_SIM_K_AUT_LEN, 2, addr, len, hmac); + memcpy(mac, hmac, EAP_SIM_MAC_LEN); +} + + +int eap_sim_parse_attr(u8 *start, u8 *end, struct eap_sim_attrs *attr, int aka, + int encr) +{ + u8 *pos = start, *apos; + size_t alen, plen; + int list_len, i; + + memset(attr, 0, sizeof(*attr)); + attr->id_req = NO_ID_REQ; + attr->notification = -1; + attr->counter = -1; + attr->selected_version = -1; + attr->client_error_code = -1; + + while (pos < end) { + if (pos + 2 > end) { + wpa_printf(MSG_INFO, "EAP-SIM: Attribute overflow(1)"); + return -1; + } + wpa_printf(MSG_MSGDUMP, "EAP-SIM: Attribute: Type=%d Len=%d", + pos[0], pos[1] * 4); + if (pos + pos[1] * 4 > end) { + wpa_printf(MSG_INFO, "EAP-SIM: Attribute overflow " + "(pos=%p len=%d end=%p)", + pos, pos[1] * 4, end); + return -1; + } + apos = pos + 2; + alen = pos[1] * 4 - 2; + wpa_hexdump(MSG_MSGDUMP, "EAP-SIM: Attribute data", + apos, alen); + + switch (pos[0]) { + case EAP_SIM_AT_RAND: + wpa_printf(MSG_DEBUG, "EAP-SIM: AT_RAND"); + apos += 2; + alen -= 2; + if ((!aka && (alen % GSM_RAND_LEN)) || + (aka && alen != AKA_RAND_LEN)) { + wpa_printf(MSG_INFO, "EAP-SIM: Invalid AT_RAND" + " (len %lu)", + (unsigned long) alen); + return -1; + } + attr->rand = apos; + attr->num_chal = alen / GSM_RAND_LEN; + break; + case EAP_SIM_AT_AUTN: + wpa_printf(MSG_DEBUG, "EAP-AKA: AT_AUTN"); + if (!aka) { + wpa_printf(MSG_DEBUG, "EAP-SIM: " + "Unexpected AT_AUTN"); + return -1; + } + apos += 2; + alen -= 2; + if (alen != AKA_AUTN_LEN) { + wpa_printf(MSG_INFO, "EAP-AKA: Invalid AT_AUTN" + " (len %lu)", + (unsigned long) alen); + return -1; + } + attr->autn = apos; + break; + case EAP_SIM_AT_PADDING: + if (!encr) { + wpa_printf(MSG_ERROR, "EAP-SIM: Unencrypted " + "AT_PADDING"); + return -1; + } + wpa_printf(MSG_DEBUG, "EAP-SIM: (encr) AT_PADDING"); + for (i = 2; i < alen; i++) { + if (apos[i] != 0) { + wpa_printf(MSG_INFO, "EAP-SIM: (encr) " + "AT_PADDING used a non-zero" + " padding byte"); + wpa_hexdump(MSG_DEBUG, "EAP-SIM: " + "(encr) padding bytes", + apos + 2, alen - 2); + return -1; + } + } + break; + case EAP_SIM_AT_NONCE_MT: + wpa_printf(MSG_DEBUG, "EAP-SIM: AT_NONCE_MT"); + if (alen != 2 + EAP_SIM_NONCE_MT_LEN) { + wpa_printf(MSG_INFO, "EAP-SIM: Invalid " + "AT_NONCE_MT length"); + return -1; + } + attr->nonce_mt = apos + 2; + break; + case EAP_SIM_AT_PERMANENT_ID_REQ: + wpa_printf(MSG_DEBUG, "EAP-SIM: AT_PERMANENT_ID_REQ"); + attr->id_req = PERMANENT_ID; + break; + case EAP_SIM_AT_MAC: + wpa_printf(MSG_DEBUG, "EAP-SIM: AT_MAC"); + if (alen != 2 + EAP_SIM_MAC_LEN) { + wpa_printf(MSG_INFO, "EAP-SIM: Invalid AT_MAC " + "length"); + return -1; + } + attr->mac = apos + 2; + break; + case EAP_SIM_AT_NOTIFICATION: + if (alen != 2) { + wpa_printf(MSG_INFO, "EAP-SIM: Invalid " + "AT_NOTIFICATION length %lu", + (unsigned long) alen); + return -1; + } + attr->notification = apos[0] * 256 + apos[1]; + wpa_printf(MSG_DEBUG, "EAP-SIM: AT_NOTIFICATION %d", + attr->notification); + break; + case EAP_SIM_AT_ANY_ID_REQ: + wpa_printf(MSG_DEBUG, "EAP-SIM: AT_ANY_ID_REQ"); + attr->id_req = ANY_ID; + break; + case EAP_SIM_AT_IDENTITY: + wpa_printf(MSG_DEBUG, "EAP-SIM: AT_IDENTITY"); + attr->identity = apos + 2; + attr->identity_len = alen - 2; + break; + case EAP_SIM_AT_VERSION_LIST: + if (aka) { + wpa_printf(MSG_DEBUG, "EAP-AKA: " + "Unexpected AT_VERSION_LIST"); + return -1; + } + list_len = apos[0] * 256 + apos[1]; + wpa_printf(MSG_DEBUG, "EAP-SIM: AT_VERSION_LIST"); + if (list_len < 2 || list_len > alen - 2) { + wpa_printf(MSG_WARNING, "EAP-SIM: Invalid " + "AT_VERSION_LIST (list_len=%d " + "attr_len=%lu)", list_len, + (unsigned long) alen); + return -1; + } + attr->version_list = apos + 2; + attr->version_list_len = list_len; + break; + case EAP_SIM_AT_SELECTED_VERSION: + wpa_printf(MSG_DEBUG, "EAP-SIM: AT_SELECTED_VERSION"); + if (alen != 2) { + wpa_printf(MSG_INFO, "EAP-SIM: Invalid " + "AT_SELECTED_VERSION length %lu", + (unsigned long) alen); + return -1; + } + attr->selected_version = apos[0] * 256 + apos[1]; + wpa_printf(MSG_DEBUG, "EAP-SIM: AT_SELECTED_VERSION " + "%d", attr->selected_version); + break; + case EAP_SIM_AT_FULLAUTH_ID_REQ: + wpa_printf(MSG_DEBUG, "EAP-SIM: AT_FULLAUTH_ID_REQ"); + attr->id_req = FULLAUTH_ID; + break; + case EAP_SIM_AT_COUNTER: + if (!encr) { + wpa_printf(MSG_ERROR, "EAP-SIM: Unencrypted " + "AT_COUNTER"); + return -1; + } + if (alen != 2) { + wpa_printf(MSG_INFO, "EAP-SIM: (encr) Invalid " + "AT_COUNTER (alen=%lu)", + (unsigned long) alen); + return -1; + } + attr->counter = apos[0] * 256 + apos[1]; + wpa_printf(MSG_DEBUG, "EAP-SIM: (encr) AT_COUNTER %d", + attr->counter); + break; + case EAP_SIM_AT_NONCE_S: + if (!encr) { + wpa_printf(MSG_ERROR, "EAP-SIM: Unencrypted " + "AT_NONCE_S"); + return -1; + } + wpa_printf(MSG_DEBUG, "EAP-SIM: (encr) " + "AT_NONCE_S"); + if (alen != 2 + EAP_SIM_NONCE_S_LEN) { + wpa_printf(MSG_INFO, "EAP-SIM: (encr) Invalid " + "AT_NONCE_S (alen=%lu)", + (unsigned long) alen); + return -1; + } + attr->nonce_s = apos + 2; + break; + case EAP_SIM_AT_CLIENT_ERROR_CODE: + if (alen != 2) { + wpa_printf(MSG_INFO, "EAP-SIM: Invalid " + "AT_CLIENT_ERROR_CODE length %lu", + (unsigned long) alen); + return -1; + } + attr->client_error_code = apos[0] * 256 + apos[1]; + wpa_printf(MSG_DEBUG, "EAP-SIM: AT_CLIENT_ERROR_CODE " + "%d", attr->client_error_code); + break; + case EAP_SIM_AT_IV: + wpa_printf(MSG_DEBUG, "EAP-SIM: AT_IV"); + if (alen != 2 + EAP_SIM_MAC_LEN) { + wpa_printf(MSG_INFO, "EAP-SIM: Invalid AT_IV " + "length %lu", (unsigned long) alen); + return -1; + } + attr->iv = apos + 2; + break; + case EAP_SIM_AT_ENCR_DATA: + wpa_printf(MSG_DEBUG, "EAP-SIM: AT_ENCR_DATA"); + attr->encr_data = apos + 2; + attr->encr_data_len = alen - 2; + if (attr->encr_data_len % 16) { + wpa_printf(MSG_INFO, "EAP-SIM: Invalid " + "AT_ENCR_DATA length %lu", + (unsigned long) + attr->encr_data_len); + return -1; + } + break; + case EAP_SIM_AT_NEXT_PSEUDONYM: + if (!encr) { + wpa_printf(MSG_ERROR, "EAP-SIM: Unencrypted " + "AT_NEXT_PSEUDONYM"); + return -1; + } + wpa_printf(MSG_DEBUG, "EAP-SIM: (encr) " + "AT_NEXT_PSEUDONYM"); + plen = apos[0] * 256 + apos[1]; + if (plen > alen - 2) { + wpa_printf(MSG_INFO, "EAP-SIM: (encr) Invalid" + " AT_NEXT_PSEUDONYM (actual" + " len %lu, attr len %lu)", + (unsigned long) plen, + (unsigned long) alen); + return -1; + } + attr->next_pseudonym = pos + 4; + attr->next_pseudonym_len = plen; + break; + case EAP_SIM_AT_NEXT_REAUTH_ID: + if (!encr) { + wpa_printf(MSG_ERROR, "EAP-SIM: Unencrypted " + "AT_NEXT_REAUTH_ID"); + return -1; + } + wpa_printf(MSG_DEBUG, "EAP-SIM: (encr) " + "AT_NEXT_REAUTH_ID"); + plen = apos[0] * 256 + apos[1]; + if (plen > alen - 2) { + wpa_printf(MSG_INFO, "EAP-SIM: (encr) Invalid" + " AT_NEXT_REAUTH_ID (actual" + " len %lu, attr len %lu)", + (unsigned long) plen, + (unsigned long) alen); + return -1; + } + attr->next_reauth_id = pos + 4; + attr->next_reauth_id_len = plen; + break; + default: + if (pos[0] < 128) { + wpa_printf(MSG_INFO, "EAP-SIM: Unrecognized " + "non-skippable attribute %d", + pos[0]); + return -1; + } + + wpa_printf(MSG_DEBUG, "EAP-SIM: Unrecognized skippable" + " attribute %d ignored", pos[0]); + break; + } + + pos += pos[1] * 4; + } + + wpa_printf(MSG_DEBUG, "EAP-SIM: Attributes parsed successfully " + "(aka=%d encr=%d)", aka, encr); + + return 0; +} + + +int eap_sim_parse_encr(const u8 *k_encr, u8 *encr_data, size_t encr_data_len, + const u8 *iv, struct eap_sim_attrs *attr, int aka) +{ + if (!iv) { + wpa_printf(MSG_INFO, "EAP-SIM: Encrypted data, but no IV"); + return -1; + } + aes_128_cbc_decrypt(k_encr, iv, encr_data, encr_data_len); + wpa_hexdump(MSG_MSGDUMP, "EAP-SIM: Decrypted AT_ENCR_DATA", + encr_data, encr_data_len); + + if (eap_sim_parse_attr(encr_data, encr_data + encr_data_len, attr, + aka, 1)) { + wpa_printf(MSG_INFO, "EAP-SIM: (encr) Failed to parse " + "decrypted AT_ENCR_DATA"); + return -1; + } + + return 0; +} + + +#define EAP_SIM_INIT_LEN 128 + +struct eap_sim_msg { + u8 *buf; + size_t buf_len, used; + size_t mac, iv, encr; /* index from buf */ +}; + + +struct eap_sim_msg * eap_sim_msg_init(int code, int id, int type, int subtype) +{ + struct eap_sim_msg *msg; + struct eap_hdr *eap; + u8 *pos; + + msg = malloc(sizeof(*msg)); + if (msg == NULL) + return NULL; + memset(msg, 0, sizeof(*msg)); + + msg->buf = malloc(EAP_SIM_INIT_LEN); + if (msg->buf == NULL) { + free(msg); + return NULL; + } + memset(msg->buf, 0, EAP_SIM_INIT_LEN); + msg->buf_len = EAP_SIM_INIT_LEN; + eap = (struct eap_hdr *) msg->buf; + eap->code = code; + eap->identifier = id; + msg->used = sizeof(*eap); + + pos = (u8 *) (eap + 1); + *pos++ = type; + *pos++ = subtype; + *pos++ = 0; /* Reserved */ + *pos++ = 0; /* Reserved */ + msg->used += 4; + + return msg; +} + + +u8 * eap_sim_msg_finish(struct eap_sim_msg *msg, size_t *len, const u8 *k_aut, + const u8 *extra, size_t extra_len) +{ + struct eap_hdr *eap; + u8 *buf; + + if (msg == NULL) + return NULL; + + eap = (struct eap_hdr *) msg->buf; + eap->length = host_to_be16(msg->used); + + if (k_aut && msg->mac) { + eap_sim_add_mac(k_aut, msg->buf, msg->used, + msg->buf + msg->mac, extra, extra_len); + } + + *len = msg->used; + buf = msg->buf; + free(msg); + return buf; +} + + +void eap_sim_msg_free(struct eap_sim_msg *msg) +{ + if (msg) { + free(msg->buf); + free(msg); + } +} + + +static int eap_sim_msg_resize(struct eap_sim_msg *msg, size_t add_len) +{ + if (msg->used + add_len > msg->buf_len) { + u8 *nbuf = realloc(msg->buf, msg->used + add_len); + if (nbuf == NULL) + return -1; + msg->buf = nbuf; + msg->buf_len = msg->used + add_len; + } + return 0; +} + + +u8 * eap_sim_msg_add_full(struct eap_sim_msg *msg, u8 attr, + const u8 *data, size_t len) +{ + int attr_len = 2 + len; + int pad_len; + u8 *start, *pos; + + if (msg == NULL) + return NULL; + + pad_len = (4 - attr_len % 4) % 4; + attr_len += pad_len; + if (eap_sim_msg_resize(msg, attr_len)) + return NULL; + start = pos = msg->buf + msg->used; + *pos++ = attr; + *pos++ = attr_len / 4; + memcpy(pos, data, len); + if (pad_len) { + pos += len; + memset(pos, 0, pad_len); + } + msg->used += attr_len; + return start; +} + + +u8 * eap_sim_msg_add(struct eap_sim_msg *msg, u8 attr, u16 value, + const u8 *data, size_t len) +{ + int attr_len = 4 + len; + int pad_len; + u8 *start, *pos; + + if (msg == NULL) + return NULL; + + pad_len = (4 - attr_len % 4) % 4; + attr_len += pad_len; + if (eap_sim_msg_resize(msg, attr_len)) + return NULL; + start = pos = msg->buf + msg->used; + *pos++ = attr; + *pos++ = attr_len / 4; + *pos++ = value >> 8; + *pos++ = value & 0xff; + if (data) + memcpy(pos, data, len); + if (pad_len) { + pos += len; + memset(pos, 0, pad_len); + } + msg->used += attr_len; + return start; +} + + +u8 * eap_sim_msg_add_mac(struct eap_sim_msg *msg, u8 attr) +{ + u8 *pos = eap_sim_msg_add(msg, attr, 0, NULL, EAP_SIM_MAC_LEN); + if (pos) + msg->mac = (pos - msg->buf) + 4; + return pos; +} + + +int eap_sim_msg_add_encr_start(struct eap_sim_msg *msg, u8 attr_iv, + u8 attr_encr) +{ + u8 *pos = eap_sim_msg_add(msg, attr_iv, 0, NULL, EAP_SIM_IV_LEN); + if (pos == NULL) + return -1; + msg->iv = (pos - msg->buf) + 4; + if (hostapd_get_rand(msg->buf + msg->iv, EAP_SIM_IV_LEN)) { + msg->iv = 0; + return -1; + } + + pos = eap_sim_msg_add(msg, attr_encr, 0, NULL, 0); + if (pos == NULL) { + msg->iv = 0; + return -1; + } + msg->encr = pos - msg->buf; + + return 0; +} + + +int eap_sim_msg_add_encr_end(struct eap_sim_msg *msg, u8 *k_encr, int attr_pad) +{ + size_t encr_len; + + if (k_encr == NULL || msg->iv == 0 || msg->encr == 0) + return -1; + + encr_len = msg->used - msg->encr - 4; + if (encr_len % 16) { + u8 *pos; + int pad_len = 16 - (encr_len % 16); + if (pad_len < 4) { + wpa_printf(MSG_WARNING, "EAP-SIM: " + "eap_sim_msg_add_encr_end - invalid pad_len" + " %d", pad_len); + return -1; + } + wpa_printf(MSG_DEBUG, " *AT_PADDING"); + pos = eap_sim_msg_add(msg, attr_pad, 0, NULL, pad_len - 4); + if (pos == NULL) + return -1; + memset(pos + 4, 0, pad_len - 4); + encr_len += pad_len; + } + wpa_printf(MSG_DEBUG, " (AT_ENCR_DATA data len %lu)", + (unsigned long) encr_len); + msg->buf[msg->encr + 1] = encr_len / 4 + 1; + aes_128_cbc_encrypt(k_encr, msg->buf + msg->iv, + msg->buf + msg->encr + 4, encr_len); + + return 0; +} + + +void eap_sim_report_notification(void *msg_ctx, int notification, int aka) +{ + const char *type = aka ? "AKA" : "SIM"; + + switch (notification) { + case EAP_SIM_GENERAL_FAILURE_AFTER_AUTH: + wpa_printf(MSG_WARNING, "EAP-%s: General failure " + "notification (after authentication)", type); + break; + case EAP_SIM_TEMPORARILY_DENIED: + wpa_printf(MSG_WARNING, "EAP-%s: Failure notification: " + "User has been temporarily denied access to the " + "requested service", type); + break; + case EAP_SIM_NOT_SUBSCRIBED: + wpa_printf(MSG_WARNING, "EAP-%s: Failure notification: " + "User has not subscribed to the requested service", + type); + break; + case EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH: + wpa_printf(MSG_WARNING, "EAP-%s: General failure " + "notification (before authentication)", type); + break; + case EAP_SIM_SUCCESS: + wpa_printf(MSG_INFO, "EAP-%s: Successful authentication " + "notification", type); + break; + default: + if (notification >= 32768) { + wpa_printf(MSG_INFO, "EAP-%s: Unrecognized " + "non-failure notification %d", + type, notification); + } else { + wpa_printf(MSG_WARNING, "EAP-%s: Unrecognized " + "failure notification %d", + type, notification); + } + } +} + + +#ifdef TEST_MAIN_EAP_SIM_COMMON +static int test_eap_sim_prf(void) +{ + /* http://csrc.nist.gov/encryption/dss/Examples-1024bit.pdf */ + u8 xkey[] = { + 0xbd, 0x02, 0x9b, 0xbe, 0x7f, 0x51, 0x96, 0x0b, + 0xcf, 0x9e, 0xdb, 0x2b, 0x61, 0xf0, 0x6f, 0x0f, + 0xeb, 0x5a, 0x38, 0xb6 + }; + u8 w[] = { + 0x20, 0x70, 0xb3, 0x22, 0x3d, 0xba, 0x37, 0x2f, + 0xde, 0x1c, 0x0f, 0xfc, 0x7b, 0x2e, 0x3b, 0x49, + 0x8b, 0x26, 0x06, 0x14, 0x3c, 0x6c, 0x18, 0xba, + 0xcb, 0x0f, 0x6c, 0x55, 0xba, 0xbb, 0x13, 0x78, + 0x8e, 0x20, 0xd7, 0x37, 0xa3, 0x27, 0x51, 0x16 + }; + u8 buf[40]; + + printf("Testing EAP-SIM PRF (FIPS 186-2 + change notice 1)\n"); + eap_sim_prf(xkey, buf, sizeof(buf)); + if (memcmp(w, buf, sizeof(w) != 0)) { + printf("eap_sim_prf failed\n"); + return 1; + } + + return 0; +} + + +int main(int argc, char *argv[]) +{ + int errors = 0; + + errors += test_eap_sim_prf(); + + return errors; +} +#endif /* TEST_MAIN_EAP_SIM_COMMON */ diff --git a/contrib/wpa_supplicant/eap_sim_common.h b/contrib/wpa_supplicant/eap_sim_common.h new file mode 100644 index 000000000000..c89e04e410b9 --- /dev/null +++ b/contrib/wpa_supplicant/eap_sim_common.h @@ -0,0 +1,101 @@ +#ifndef EAP_SIM_COMMON_H +#define EAP_SIM_COMMON_H + +#define EAP_SIM_NONCE_S_LEN 16 +#define EAP_SIM_NONCE_MT_LEN 16 +#define EAP_SIM_MAC_LEN 16 +#define EAP_SIM_MK_LEN 20 +#define EAP_SIM_K_AUT_LEN 16 +#define EAP_SIM_K_ENCR_LEN 16 +#define EAP_SIM_KEYING_DATA_LEN 64 +#define EAP_SIM_IV_LEN 16 + +#define GSM_RAND_LEN 16 + +#define AKA_RAND_LEN 16 +#define AKA_AUTN_LEN 16 + +void eap_sim_derive_keys(const u8 *mk, u8 *k_encr, u8 *k_aut, u8 *msk); +void eap_sim_derive_keys_reauth(unsigned int _counter, + const u8 *identity, size_t identity_len, + const u8 *nonce_s, const u8 *mk, u8 *msk); +int eap_sim_verify_mac(const u8 *k_aut, u8 *req, size_t req_len, u8 *mac, + u8 *extra, size_t extra_len); +void eap_sim_add_mac(const u8 *k_aut, u8 *msg, size_t msg_len, u8 *mac, + const u8 *extra, size_t extra_len); + + +/* EAP-SIM/AKA Attributes (0..127 non-skippable) */ +#define EAP_SIM_AT_RAND 1 +#define EAP_SIM_AT_AUTN 2 /* only AKA */ +#define EAP_SIM_AT_RES 3 /* only AKA, only send */ +#define EAP_SIM_AT_AUTS 4 /* only AKA, only send */ +#define EAP_SIM_AT_PADDING 6 /* only encrypted */ +#define EAP_SIM_AT_NONCE_MT 7 /* only SIM, only send */ +#define EAP_SIM_AT_PERMANENT_ID_REQ 10 +#define EAP_SIM_AT_MAC 11 +#define EAP_SIM_AT_NOTIFICATION 12 +#define EAP_SIM_AT_ANY_ID_REQ 13 +#define EAP_SIM_AT_IDENTITY 14 /* only send */ +#define EAP_SIM_AT_VERSION_LIST 15 /* only SIM */ +#define EAP_SIM_AT_SELECTED_VERSION 16 /* only SIM */ +#define EAP_SIM_AT_FULLAUTH_ID_REQ 17 +#define EAP_SIM_AT_COUNTER 19 /* only encrypted */ +#define EAP_SIM_AT_COUNTER_TOO_SMALL 20 /* only encrypted */ +#define EAP_SIM_AT_NONCE_S 21 /* only encrypted */ +#define EAP_SIM_AT_CLIENT_ERROR_CODE 22 /* only send */ +#define EAP_SIM_AT_IV 129 +#define EAP_SIM_AT_ENCR_DATA 130 +#define EAP_SIM_AT_NEXT_PSEUDONYM 132 /* only encrypted */ +#define EAP_SIM_AT_NEXT_REAUTH_ID 133 /* only encrypted */ +#define EAP_SIM_AT_CHECKCODE 134 /* only AKA */ +#define EAP_SIM_AT_RESULT_IND 135 + +/* AT_NOTIFICATION notification code values */ +#define EAP_SIM_GENERAL_FAILURE_AFTER_AUTH 0 +#define EAP_SIM_TEMPORARILY_DENIED 1026 +#define EAP_SIM_NOT_SUBSCRIBED 1031 +#define EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH 16384 +#define EAP_SIM_SUCCESS 32768 + + +enum eap_sim_id_req { + NO_ID_REQ, ANY_ID, FULLAUTH_ID, PERMANENT_ID +}; + + +struct eap_sim_attrs { + u8 *rand, *autn, *mac, *iv, *encr_data, *version_list, *nonce_s; + u8 *next_pseudonym, *next_reauth_id; + u8 *nonce_mt, *identity; + size_t num_chal, version_list_len, encr_data_len; + size_t next_pseudonym_len, next_reauth_id_len, identity_len; + enum eap_sim_id_req id_req; + int notification, counter, selected_version, client_error_code; +}; + +int eap_sim_parse_attr(u8 *start, u8 *end, struct eap_sim_attrs *attr, + int aka, int encr); +int eap_sim_parse_encr(const u8 *k_encr, u8 *encr_data, size_t encr_data_len, + const u8 *iv, struct eap_sim_attrs *attr, int aka); + + +struct eap_sim_msg; + +struct eap_sim_msg * eap_sim_msg_init(int code, int id, int type, int subtype); +u8 * eap_sim_msg_finish(struct eap_sim_msg *msg, size_t *len, const u8 *k_aut, + const u8 *extra, size_t extra_len); +void eap_sim_msg_free(struct eap_sim_msg *msg); +u8 * eap_sim_msg_add_full(struct eap_sim_msg *msg, u8 attr, + const u8 *data, size_t len); +u8 * eap_sim_msg_add(struct eap_sim_msg *msg, u8 attr, + u16 value, const u8 *data, size_t len); +u8 * eap_sim_msg_add_mac(struct eap_sim_msg *msg, u8 attr); +int eap_sim_msg_add_encr_start(struct eap_sim_msg *msg, u8 attr_iv, + u8 attr_encr); +int eap_sim_msg_add_encr_end(struct eap_sim_msg *msg, u8 *k_encr, + int attr_pad); + +void eap_sim_report_notification(void *msg_ctx, int notification, int aka); + +#endif /* EAP_SIM_COMMON_H */ diff --git a/contrib/wpa_supplicant/eap_testing.txt b/contrib/wpa_supplicant/eap_testing.txt new file mode 100644 index 000000000000..03ef2858e425 --- /dev/null +++ b/contrib/wpa_supplicant/eap_testing.txt @@ -0,0 +1,349 @@ +Automatic regression and interoperability testing of wpa_supplicant's +IEEE 802.1X/EAPOL authentication + +Test program: +- Linked some parts of IEEE 802.1X Authenticator implementation from + hostapd (RADIUS client and RADIUS processing, EAP<->RADIUS + encapsulation/decapsulation) into wpa_supplicant. +- Replaced wpa_supplicant.c and wpa.c with test code that trigger + IEEE 802.1X authentication automatically without need for wireless + client card or AP. +- For EAP methods that generate keying material, the key derived by the + Supplicant is verified to match with the one received by the (now + integrated) Authenticator. + +The full automated test suite can now be run in couple of seconds, but +I'm more than willing to add new RADIUS authentication servers to make +this take a bit more time.. ;-) As an extra bonus, this can also be +seen as automatic regression/interoperability testing for the RADIUS +server, too. + +In order for me to be able to use a new authentication server, the +server need to be available from Internet (at least from one static IP +address) and I will need to get suitable user name/password pairs, +certificates, and private keys for testing use. Other alternative +would be to get an evaluation version of the server so that I can +install it on my own test setup. If you are interested in providing +either server access or evaluation version, please contact me +(jkmaline@cc.hut.fi). + + +Test matrix + ++) tested successfully +F) failed +-) server did not support +?) not tested + +hostapd --------------------------------------------------------. +Cisco Aironet 1200 AP (local RADIUS server) ----------------. | +Corriente Elektron -------------------------------------. | | +Lucent NavisRadiator -------------------------------. | | | +Interlink RAD-Series ---------------------------. | | | | +Radiator -----------------------------------. | | | | | +Meetinghouse Aegis ---------------------. | | | | | | +Funk Steel-Belted ------------------. | | | | | | | +Funk Odyssey -------------------. | | | | | | | | +Microsoft IAS --------------. | | | | | | | | | +FreeRADIUS -------------. | | | | | | | | | | + | | | | | | | | | | | + +EAP-MD5 + - - + + + + + - - + +EAP-GTC + - - ? + + + + - - + +EAP-OTP - - - - - + - - - - - +EAP-MSCHAPv2 + - - + + + + + - - + +EAP-TLS + + + + + + + + - - + +EAP-PEAPv0/MSCHAPv2 + + + + + + + + + - + +EAP-PEAPv0/GTC + - + - + + + + - - + +EAP-PEAPv0/OTP - - - - - + - - - - - +EAP-PEAPv0/MD5 + - - + + + + + - - + +EAP-PEAPv0/TLS - + - + + + F + - - - +EAP-PEAPv1/MSCHAPv2 - - + + + +1 + +5 +8 - + +EAP-PEAPv1/GTC - - + + + +1 + +5 - - + +EAP-PEAPv1/OTP - - - - - +1 - - - - - +EAP-PEAPv1/MD5 - - - + + +1 + +5 - - + +EAP-PEAPv1/TLS - - - + + +1 F +5 - - - +EAP-TTLS/CHAP + - +2 + + + + + + - + +EAP-TTLS/MSCHAP + - + + + + + + + - + +EAP-TTLS/MSCHAPv2 + - + + + + + + + - + +EAP-TTLS/PAP + - + + + + + + + - + +EAP-TTLS/EAP-MD5 + - +2 + + + + + - - + +EAP-TTLS/EAP-GTC + - +2 ? + + + + - - + +EAP-TTLS/EAP-OTP - - - - - + - - - - - +EAP-TTLS/EAP-MSCHAPv2 + - +2 + + + + + + - + +EAP-TTLS/EAP-TLS - - +2 + F + + + - - - +EAP-SIM +3 - - ? - + - ? - - + +EAP-AKA - - - - - + - - - - - +EAP-PSK +7 - - - - - - - - - - +EAP-FAST - - - - - - - - - + - +LEAP + - + + + + F +6 - + - + +1) PEAPv1 required new label, "client PEAP encryption" instead of "client EAP + encryption", during key derivation (requires phase1="peaplabel=1" in the + network configuration in wpa_supplicant.conf) +2) used FreeRADIUS as inner auth server +3) required a patch to FreeRADIUS to fix EAP-SIM +5) PEAPv1 required termination of negotiation on tunneled EAP-Success and new + label in key deriviation + (phase1="peap_outer_success=0 peaplabel=1") (in "IETF Draft 5" mode) +6) Authenticator simulator required patching for handling Access-Accept within + negotiation (for the first EAP-Success of LEAP) +7) EAP-PSK is not included in FreeRADIUS distribution; used external + rlm_eap_psk implementation from + http://perso.rd.francetelecom.fr/bersani/EAP_PSK/ + EAP-PSKWindowsimplementations.html +8) PEAPv1 used non-standard version negotiation (client had to force v1 even + though server reported v0 as the highest supported version) + + +Automated tests: + +FreeRADIUS (1.0pre and CVS snapshot) +- EAP-MD5-Challenge +- EAP-GTC +- EAP-MSCHAPv2 +- EAP-TLS +- EAP-PEAPv0 / MSCHAPv2 +- EAP-PEAPv0 / GTC +- EAP-PEAPv0 / MD5-Challenge +- EAP-TTLS / EAP-MD5-Challenge +- EAP-TTLS / EAP-GTC +- EAP-TTLS / EAP-MSCHAPv2 +- EAP-TTLS / CHAP +- EAP-TTLS / PAP +- EAP-TTLS / MSCHAP +- EAP-TTLS / MSCHAPv2 +- EAP-SIM +* not supported in FreeRADIUS + - EAP-PEAP / TLS (Unable to tunnel TLS inside of TLS) + - EAP-TTLS / EAP-TLS (Unable to tunnel TLS inside of TLS) + +Microsoft Windows Server 2003 / IAS +- EAP-TLS +- EAP-PEAPv0 / MSCHAPv2 +- EAP-PEAPv0 / TLS +- EAP-MD5 +* IAS does not seem to support other EAP methods + +Funk Odyssey 2.01.00.653 +- EAP-TLS +- EAP-PEAPv0 / MSCHAPv2 +- EAP-PEAPv0 / GTC +- EAP-PEAPv1 / MSCHAPv2 +- EAP-PEAPv1 / GTC + Note: PEAPv1 requires TLS key derivation to use label "client EAP encryption" +- EAP-TTLS / CHAP (using FreeRADIUS as inner auth srv) +- EAP-TTLS / MSCHAP +- EAP-TTLS / MSCHAPv2 +- EAP-TTLS / PAP +- EAP-TTLS / EAP-MD5-Challenge (using FreeRADIUS as inner auth srv) +- EAP-TTLS / EAP-GTC (using FreeRADIUS as inner auth srv) +- EAP-TTLS / EAP-MSCHAPv2 (using FreeRADIUS as inner auth srv) +- EAP-TTLS / EAP-TLS (using FreeRADIUS as inner auth srv) +* not supported in Odyssey: + - EAP-MD5-Challenge + - EAP-GTC + - EAP-MSCHAPv2 + - EAP-PEAP / MD5-Challenge + - EAP-PEAP / TLS + +Funk Steel-Belted Radius Enterprise Edition v4.71.739 +- EAP-MD5-Challenge +- EAP-MSCHAPv2 +- EAP-TLS +- EAP-PEAPv0 / MSCHAPv2 +- EAP-PEAPv0 / MD5 +- EAP-PEAPv0 / TLS +- EAP-PEAPv1 / MSCHAPv2 +- EAP-PEAPv1 / MD5 +- EAP-PEAPv1 / GTC +- EAP-PEAPv1 / TLS + Note: PEAPv1 requires TLS key derivation to use label "client EAP encryption" +- EAP-TTLS / CHAP +- EAP-TTLS / MSCHAP +- EAP-TTLS / MSCHAPv2 +- EAP-TTLS / PAP +- EAP-TTLS / EAP-MD5-Challenge +- EAP-TTLS / EAP-MSCHAPv2 +- EAP-TTLS / EAP-TLS + +Meetinghouse Aegis 1.1.4 +- EAP-MD5-Challenge +- EAP-GTC +- EAP-MSCHAPv2 +- EAP-TLS +- EAP-PEAPv0 / MSCHAPv2 +- EAP-PEAPv0 / TLS +- EAP-PEAPv0 / GTC +- EAP-PEAPv0 / MD5-Challenge +- EAP-PEAPv1 / MSCHAPv2 +- EAP-PEAPv1 / TLS +- EAP-PEAPv1 / GTC +- EAP-PEAPv1 / MD5-Challenge + Note: PEAPv1 requires TLS key derivation to use label "client EAP encryption" +- EAP-TTLS / CHAP +- EAP-TTLS / MSCHAP +- EAP-TTLS / MSCHAPv2 +- EAP-TTLS / PAP +- EAP-TTLS / EAP-MD5-Challenge +- EAP-TTLS / EAP-GTC +- EAP-TTLS / EAP-MSCHAPv2 +* did not work + - EAP-TTLS / EAP-TLS + (Server rejects authentication without any reason in debug log. It + looks like the inner TLS negotiation starts properly and the last + packet from Supplicant looks like the one sent in the Phase 1. The + server generates a valid looking reply in the same way as in Phase + 1, but then ends up sending Access-Reject. Maybe an issue with TTLS + fragmentation in the Aegis server(?) The packet seems to include + 1328 bytes of EAP-Message and this may go beyond the fragmentation + limit with AVP encapsulation and TLS tunneling. Note: EAP-PEAP/TLS + did work, so this issue seems to be with something TTLS specific.) + +Radiator 3.9 (eval, with all patches up to and including 2004-08-30) +- EAP-MD5-Challenge +- EAP-GTC +- EAP-OTP +- EAP-MSCHAPv2 +- EAP-TLS +- EAP-PEAPv0 / MSCHAPv2 +- EAP-PEAPv0 / GTC +- EAP-PEAPv0 / OTP +- EAP-PEAPv0 / MD5-Challenge +- EAP-PEAPv0 / TLS + Note: Needed to use unknown identity in outer auth and some times the server + seems to get confused and fails to send proper Phase 2 data. +- EAP-PEAPv1 / MSCHAPv2 +- EAP-PEAPv1 / GTC +- EAP-PEAPv1 / OTP +- EAP-PEAPv1 / MD5-Challenge +- EAP-PEAPv1 / TLS + Note: This has some additional requirements for EAPTLS_MaxFragmentSize. + Using 1300 for outer auth and 500 for inner auth seemed to work. + Note: Needed to use unknown identity in outer auth and some times the server + seems to get confused and fails to send proper Phase 2 data. +- EAP-TTLS / CHAP +- EAP-TTLS / MSCHAP +- EAP-TTLS / MSCHAPv2 +- EAP-TTLS / PAP +- EAP-TTLS / EAP-MD5-Challenge +- EAP-TTLS / EAP-GTC +- EAP-TTLS / EAP-OTP +- EAP-TTLS / EAP-MSCHAPv2 +- EAP-TTLS / EAP-TLS + Note: This has some additional requirements for EAPTLS_MaxFragmentSize. + Using 1300 for outer auth and 500 for inner auth seemed to work. +- EAP-SIM +- EAP-AKA + +Interlink Networks RAD-Series 6.1.2.7 +- EAP-MD5-Challenge +- EAP-GTC +- EAP-MSCHAPv2 +- EAP-TLS +- EAP-PEAPv0 / MSCHAPv2 +- EAP-PEAPv0 / GTC +- EAP-PEAPv0 / MD5-Challenge +- EAP-PEAPv1 / MSCHAPv2 +- EAP-PEAPv1 / GTC +- EAP-PEAPv1 / MD5-Challenge + Note: PEAPv1 requires TLS key derivation to use label "client EAP encryption" +- EAP-TTLS / CHAP +- EAP-TTLS / MSCHAP +- EAP-TTLS / MSCHAPv2 +- EAP-TTLS / PAP +- EAP-TTLS / EAP-MD5-Challenge +- EAP-TTLS / EAP-GTC +- EAP-TTLS / EAP-MSCHAPv2 +- EAP-TTLS / EAP-TLS +* did not work + - EAP-PEAPv0 / TLS + - EAP-PEAPv1 / TLS + (Failed to decrypt Phase 2 data) + +Lucent NavisRadius 4.4.0 +- EAP-MD5-Challenge +- EAP-GTC +- EAP-MSCHAPv2 +- EAP-TLS +- EAP-PEAPv0 / MD5-Challenge +- EAP-PEAPv0 / MSCHAPv2 +- EAP-PEAPv0 / GTC +- EAP-PEAPv0 / TLS +- EAP-PEAPv1 / MD5-Challenge +- EAP-PEAPv1 / MSCHAPv2 +- EAP-PEAPv1 / GTC +- EAP-PEAPv1 / TLS + "IETF Draft 5" mode requires phase1="peap_outer_success=0 peaplabel=1" + 'Cisco ACU 5.05' mode works without phase1 configuration +- EAP-TTLS / CHAP +- EAP-TTLS / MSCHAP +- EAP-TTLS / MSCHAPv2 +- EAP-TTLS / PAP +- EAP-TTLS / EAP-MD5-Challenge +- EAP-TTLS / EAP-MSCHAPv2 +- EAP-TTLS / EAP-GTC +- EAP-TTLS / EAP-TLS + +Note: user certificate from NavisRadius had private key in a format +that wpa_supplicant could not use. Converting this to PKCS#12 and then +back to PEM allowed wpa_supplicant to use the key. + + +hostapd v0.3.3 +- EAP-MD5-Challenge +- EAP-GTC +- EAP-MSCHAPv2 +- EAP-TLS +- EAP-PEAPv0 / MSCHAPv2 +- EAP-PEAPv0 / GTC +- EAP-PEAPv0 / MD5-Challenge +- EAP-PEAPv1 / MSCHAPv2 +- EAP-PEAPv1 / GTC +- EAP-PEAPv1 / MD5-Challenge +- EAP-TTLS / CHAP +- EAP-TTLS / MSCHAP +- EAP-TTLS / MSCHAPv2 +- EAP-TTLS / PAP +- EAP-TTLS / EAP-MD5-Challenge +- EAP-TTLS / EAP-GTC +- EAP-TTLS / EAP-MSCHAPv2 +- EAP-SIM + + + +PEAPv1: + +Funk Odyssey 2.01.00.653: +- uses tunneled EAP-Success, expects reply in tunnel or TLS ACK, sends MPPE + keys with outer EAP-Success message after this +- uses label "client EAP encryption" +- (peap_outer_success 1 and 2 work) + +Funk Steel-Belted Radius Enterprise Edition v4.71.739 +- uses tunneled EAP-Success, expects reply in tunnel or TLS ACK, sends MPPE + keys with outer EAP-Success message after this +- uses label "client EAP encryption" +- (peap_outer_success 1 and 2 work) + +Radiator 3.9: +- uses TLV Success and Reply, sends MPPE keys with outer EAP-Success message + after this +- uses label "client PEAP encryption" + +Lucent NavisRadius 4.4.0 (in "IETF Draft 5" mode): +- sends tunneled EAP-Success with MPPE keys and expects the authentication to + terminate at this point (gets somewhat confused with reply to this) +- uses label "client PEAP encryption" +- phase1="peap_outer_success=0 peaplabel=1" + +Lucent NavisRadius 4.4.0 (in "Cisco ACU 5.05" mode): +- sends tunneled EAP-Success with MPPE keys and expects to receive TLS ACK + as a reply +- uses label "client EAP encryption" + +Meetinghouse Aegis 1.1.4 +- uses tunneled EAP-Success, expects reply in tunnel or TLS ACK, sends MPPE + keys with outer EAP-Success message after this +- uses label "client EAP encryption" +- peap_outer_success 1 and 2 work diff --git a/contrib/wpa_supplicant/eap_tls.c b/contrib/wpa_supplicant/eap_tls.c new file mode 100644 index 000000000000..4b02cca14e1f --- /dev/null +++ b/contrib/wpa_supplicant/eap_tls.c @@ -0,0 +1,245 @@ +/* + * WPA Supplicant / EAP-TLS (RFC 2716) + * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include "common.h" +#include "eap_i.h" +#include "eap_tls_common.h" +#include "wpa_supplicant.h" +#include "config_ssid.h" +#include "tls.h" + + +static void eap_tls_deinit(struct eap_sm *sm, void *priv); + + +struct eap_tls_data { + struct eap_ssl_data ssl; + u8 *key_data; +}; + + +static void * eap_tls_init(struct eap_sm *sm) +{ + struct eap_tls_data *data; + struct wpa_ssid *config = eap_get_config(sm); + if (config == NULL || + (sm->init_phase2 ? config->private_key2 : config->private_key) + == NULL) { + wpa_printf(MSG_INFO, "EAP-TLS: Private key not configured"); + return NULL; + } + + data = malloc(sizeof(*data)); + if (data == NULL) + return NULL; + memset(data, 0, sizeof(*data)); + + if (eap_tls_ssl_init(sm, &data->ssl, config)) { + wpa_printf(MSG_INFO, "EAP-TLS: Failed to initialize SSL."); + eap_tls_deinit(sm, data); + return NULL; + } + + return data; +} + + +static void eap_tls_deinit(struct eap_sm *sm, void *priv) +{ + struct eap_tls_data *data = priv; + if (data == NULL) + return; + eap_tls_ssl_deinit(sm, &data->ssl); + free(data->key_data); + free(data); +} + + +static u8 * eap_tls_process(struct eap_sm *sm, void *priv, + struct eap_method_ret *ret, + u8 *reqData, size_t reqDataLen, + size_t *respDataLen) +{ + struct eap_hdr *req; + int left, res; + unsigned int tls_msg_len; + u8 flags, *pos, *resp, id; + struct eap_tls_data *data = priv; + + if (tls_get_errors(sm->ssl_ctx)) { + wpa_printf(MSG_INFO, "EAP-TLS: TLS errors detected"); + ret->ignore = TRUE; + return NULL; + } + + req = (struct eap_hdr *) reqData; + pos = (u8 *) (req + 1); + if (reqDataLen < sizeof(*req) + 2 || *pos != EAP_TYPE_TLS) { + wpa_printf(MSG_INFO, "EAP-TLS: Invalid frame"); + ret->ignore = TRUE; + return NULL; + } + id = req->identifier; + pos++; + flags = *pos++; + left = be_to_host16(req->length) - sizeof(struct eap_hdr) - 2; + wpa_printf(MSG_DEBUG, "EAP-TLS: Received packet(len=%lu) - " + "Flags 0x%02x", (unsigned long) reqDataLen, flags); + if (flags & EAP_TLS_FLAGS_LENGTH_INCLUDED) { + if (left < 4) { + wpa_printf(MSG_INFO, "EAP-TLS: Short frame with TLS " + "length"); + ret->ignore = TRUE; + return NULL; + } + tls_msg_len = (pos[0] << 24) | (pos[1] << 16) | (pos[2] << 8) | + pos[3]; + wpa_printf(MSG_DEBUG, "EAP-TLS: TLS Message Length: %d", + tls_msg_len); + if (data->ssl.tls_in_left == 0) { + data->ssl.tls_in_total = tls_msg_len; + data->ssl.tls_in_left = tls_msg_len; + free(data->ssl.tls_in); + data->ssl.tls_in = NULL; + data->ssl.tls_in_len = 0; + } + pos += 4; + left -= 4; + } + + ret->ignore = FALSE; + + ret->methodState = METHOD_CONT; + ret->decision = DECISION_COND_SUCC; + ret->allowNotifications = TRUE; + + if (flags & EAP_TLS_FLAGS_START) { + wpa_printf(MSG_DEBUG, "EAP-TLS: Start"); + left = 0; /* make sure that this frame is empty, even though it + * should always be, anyway */ + } + + resp = NULL; + res = eap_tls_process_helper(sm, &data->ssl, EAP_TYPE_TLS, 0, id, pos, + left, &resp, respDataLen); + + if (res < 0) { + wpa_printf(MSG_DEBUG, "EAP-TLS: TLS processing failed"); + ret->methodState = METHOD_MAY_CONT; + ret->decision = DECISION_FAIL; + return eap_tls_build_ack(&data->ssl, respDataLen, id, + EAP_TYPE_TLS, 0); + } + + if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) { + wpa_printf(MSG_DEBUG, "EAP-TLS: Done"); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_UNCOND_SUCC; + free(data->key_data); + data->key_data = eap_tls_derive_key(sm, &data->ssl, + "client EAP encryption", + EAP_TLS_KEY_LEN); + if (data->key_data) { + wpa_hexdump_key(MSG_DEBUG, "EAP-TLS: Derived key", + data->key_data, EAP_TLS_KEY_LEN); + } else { + wpa_printf(MSG_DEBUG, "EAP-TLS: Failed to derive key"); + } + } + + if (res == 1) { + return eap_tls_build_ack(&data->ssl, respDataLen, id, + EAP_TYPE_TLS, 0); + } + return resp; +} + + +static Boolean eap_tls_has_reauth_data(struct eap_sm *sm, void *priv) +{ + struct eap_tls_data *data = priv; + return tls_connection_established(sm->ssl_ctx, data->ssl.conn); +} + + +static void eap_tls_deinit_for_reauth(struct eap_sm *sm, void *priv) +{ +} + + +static void * eap_tls_init_for_reauth(struct eap_sm *sm, void *priv) +{ + struct eap_tls_data *data = priv; + free(data->key_data); + data->key_data = NULL; + if (eap_tls_reauth_init(sm, &data->ssl)) { + free(data); + return NULL; + } + return priv; +} + + +static int eap_tls_get_status(struct eap_sm *sm, void *priv, char *buf, + size_t buflen, int verbose) +{ + struct eap_tls_data *data = priv; + return eap_tls_status(sm, &data->ssl, buf, buflen, verbose); +} + + +static Boolean eap_tls_isKeyAvailable(struct eap_sm *sm, void *priv) +{ + struct eap_tls_data *data = priv; + return data->key_data != NULL; +} + + +static u8 * eap_tls_getKey(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_tls_data *data = priv; + u8 *key; + + if (data->key_data == NULL) + return NULL; + + key = malloc(EAP_TLS_KEY_LEN); + if (key == NULL) + return NULL; + + *len = EAP_TLS_KEY_LEN; + memcpy(key, data->key_data, EAP_TLS_KEY_LEN); + + return key; +} + + +const struct eap_method eap_method_tls = +{ + .method = EAP_TYPE_TLS, + .name = "TLS", + .init = eap_tls_init, + .deinit = eap_tls_deinit, + .process = eap_tls_process, + .isKeyAvailable = eap_tls_isKeyAvailable, + .getKey = eap_tls_getKey, + .get_status = eap_tls_get_status, + .has_reauth_data = eap_tls_has_reauth_data, + .deinit_for_reauth = eap_tls_deinit_for_reauth, + .init_for_reauth = eap_tls_init_for_reauth, +}; diff --git a/contrib/wpa_supplicant/eap_tls_common.c b/contrib/wpa_supplicant/eap_tls_common.c new file mode 100644 index 000000000000..20b141846790 --- /dev/null +++ b/contrib/wpa_supplicant/eap_tls_common.c @@ -0,0 +1,338 @@ +/* + * WPA Supplicant / EAP-TLS/PEAP/TTLS/FAST common functions + * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include "common.h" +#include "eap_i.h" +#include "eap_tls_common.h" +#include "wpa_supplicant.h" +#include "config_ssid.h" +#include "md5.h" +#include "sha1.h" +#include "tls.h" + + +int eap_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data, + struct wpa_ssid *config) +{ + int ret = -1; + char *ca_cert, *client_cert, *private_key, *private_key_passwd, + *dh_file, *subject_match; + + data->eap = sm; + data->phase2 = sm->init_phase2; + if (config == NULL) { + ca_cert = NULL; + client_cert = NULL; + private_key = NULL; + private_key_passwd = NULL; + dh_file = NULL; + subject_match = NULL; + } else if (data->phase2) { + ca_cert = (char *) config->ca_cert2; + client_cert = (char *) config->client_cert2; + private_key = (char *) config->private_key2; + private_key_passwd = (char *) config->private_key2_passwd; + dh_file = (char *) config->dh_file2; + subject_match = (char *) config->subject_match2; + } else { + ca_cert = (char *) config->ca_cert; + client_cert = (char *) config->client_cert; + private_key = (char *) config->private_key; + private_key_passwd = (char *) config->private_key_passwd; + dh_file = (char *) config->dh_file; + subject_match = (char *) config->subject_match; + } + data->conn = tls_connection_init(sm->ssl_ctx); + if (data->conn == NULL) { + wpa_printf(MSG_INFO, "SSL: Failed to initialize new TLS " + "connection"); + goto done; + } + + if (tls_connection_ca_cert(sm->ssl_ctx, data->conn, ca_cert, + subject_match)) { + wpa_printf(MSG_INFO, "TLS: Failed to load root certificate " + "'%s'", ca_cert); + goto done; + } + + if (tls_connection_client_cert(sm->ssl_ctx, data->conn, client_cert)) { + wpa_printf(MSG_INFO, "TLS: Failed to load client certificate " + "'%s'", client_cert); + goto done; + } + + if (tls_connection_private_key(sm->ssl_ctx, data->conn, private_key, + private_key_passwd)) { + wpa_printf(MSG_INFO, "TLS: Failed to load private key '%s'", + private_key); + goto done; + } + + if (dh_file && tls_connection_dh(sm->ssl_ctx, data->conn, dh_file)) { + wpa_printf(MSG_INFO, "TLS: Failed to load DH file '%s'", + dh_file); + goto done; + } + + /* TODO: make this configurable */ + data->tls_out_limit = 1398; + if (data->phase2) { + /* Limit the fragment size in the inner TLS authentication + * since the outer authentication with EAP-PEAP does not yet + * support fragmentation */ + if (data->tls_out_limit > 100) + data->tls_out_limit -= 100; + } + + if (config && config->phase1 && + strstr(config->phase1, "include_tls_length=1")) { + wpa_printf(MSG_DEBUG, "TLS: Include TLS Message Length in " + "unfragmented packets"); + data->include_tls_length = 1; + } + + ret = 0; + +done: + return ret; +} + + +void eap_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data) +{ + tls_connection_deinit(sm->ssl_ctx, data->conn); + free(data->tls_in); + free(data->tls_out); +} + + +u8 * eap_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data, + char *label, size_t len) +{ + struct tls_keys keys; + u8 *random; + u8 *out; + + if (tls_connection_get_keys(sm->ssl_ctx, data->conn, &keys)) + return NULL; + out = malloc(len); + random = malloc(keys.client_random_len + keys.server_random_len); + if (out == NULL || random == NULL) { + free(out); + free(random); + return NULL; + } + memcpy(random, keys.client_random, keys.client_random_len); + memcpy(random + keys.client_random_len, keys.server_random, + keys.server_random_len); + + if (tls_prf(keys.master_key, keys.master_key_len, + label, random, keys.client_random_len + + keys.server_random_len, out, len)) { + free(random); + free(out); + return NULL; + } + free(random); + return out; +} + + +int eap_tls_data_reassemble(struct eap_sm *sm, struct eap_ssl_data *data, + u8 **in_data, size_t *in_len) +{ + u8 *buf; + + if (data->tls_in_left > *in_len || data->tls_in) { + buf = realloc(data->tls_in, data->tls_in_len + *in_len); + if (buf == NULL) { + free(data->tls_in); + data->tls_in = NULL; + data->tls_in_len = 0; + wpa_printf(MSG_INFO, "SSL: Could not allocate memory " + "for TLS data"); + return -1; + } + memcpy(buf + data->tls_in_len, *in_data, *in_len); + data->tls_in = buf; + data->tls_in_len += *in_len; + if (*in_len > data->tls_in_left) { + wpa_printf(MSG_INFO, "SSL: more data than TLS message " + "length indicated"); + data->tls_in_left = 0; + return -1; + } + data->tls_in_left -= *in_len; + if (data->tls_in_left > 0) { + wpa_printf(MSG_DEBUG, "SSL: Need %lu bytes more input " + "data", (unsigned long) data->tls_in_left); + return 1; + } + + *in_data = data->tls_in; + *in_len = data->tls_in_len; + } else + data->tls_in_left = 0; + + return 0; +} + + +int eap_tls_process_helper(struct eap_sm *sm, struct eap_ssl_data *data, + int eap_type, int peap_version, + u8 id, u8 *in_data, size_t in_len, + u8 **out_data, size_t *out_len) +{ + size_t len; + u8 *pos, *flags; + struct eap_hdr *resp; + + WPA_ASSERT(data->tls_out_len == 0 || in_len == 0); + *out_len = 0; + + if (data->tls_out_len == 0) { + /* No more data to send out - expect to receive more data from + * the AS. */ + int res = eap_tls_data_reassemble(sm, data, &in_data, &in_len); + if (res < 0 || res == 1) + return res; + /* Full TLS message reassembled - continue handshake processing + */ + if (data->tls_out) { + /* This should not happen.. */ + wpa_printf(MSG_INFO, "SSL: eap_tls_process_helper - " + "pending tls_out data even though " + "tls_out_len = 0"); + free(data->tls_out); + WPA_ASSERT(data->tls_out == NULL); + } + data->tls_out = tls_connection_handshake(sm->ssl_ctx, + data->conn, + in_data, in_len, + &data->tls_out_len); + + /* Clear reassembled input data (if the buffer was needed). */ + data->tls_in_left = data->tls_in_total = data->tls_in_len = 0; + free(data->tls_in); + data->tls_in = NULL; + } + + if (data->tls_out == NULL) { + data->tls_out_len = 0; + return -1; + } + if (data->tls_out_len == 0) { + /* TLS negotiation should now be complete since all other cases + * needing more that should have been catched above based on + * the TLS Message Length field. */ + wpa_printf(MSG_DEBUG, "SSL: No data to be sent out"); + free(data->tls_out); + data->tls_out = NULL; + return 1; + } + + wpa_printf(MSG_DEBUG, "SSL: %lu bytes left to be sent out (of total " + "%lu bytes)", + (unsigned long) data->tls_out_len - data->tls_out_pos, + (unsigned long) data->tls_out_len); + resp = malloc(sizeof(struct eap_hdr) + 2 + 4 + data->tls_out_limit); + if (resp == NULL) { + *out_data = NULL; + return -1; + } + resp->code = EAP_CODE_RESPONSE; + resp->identifier = id; + pos = (u8 *) (resp + 1); + *pos++ = eap_type; + flags = pos++; + *flags = peap_version; + if (data->tls_out_pos == 0 && + (data->tls_out_len > data->tls_out_limit || + data->include_tls_length)) { + *flags |= EAP_TLS_FLAGS_LENGTH_INCLUDED; + *pos++ = (data->tls_out_len >> 24) & 0xff; + *pos++ = (data->tls_out_len >> 16) & 0xff; + *pos++ = (data->tls_out_len >> 8) & 0xff; + *pos++ = data->tls_out_len & 0xff; + } + + len = data->tls_out_len - data->tls_out_pos; + if (len > data->tls_out_limit) { + *flags |= EAP_TLS_FLAGS_MORE_FRAGMENTS; + len = data->tls_out_limit; + wpa_printf(MSG_DEBUG, "SSL: sending %lu bytes, more fragments " + "will follow", (unsigned long) len); + } + memcpy(pos, &data->tls_out[data->tls_out_pos], len); + data->tls_out_pos += len; + *out_len = (pos - (u8 *) resp) + len; + resp->length = host_to_be16(*out_len); + *out_data = (u8 *) resp; + + if (!(*flags & EAP_TLS_FLAGS_MORE_FRAGMENTS)) { + data->tls_out_len = 0; + data->tls_out_pos = 0; + free(data->tls_out); + data->tls_out = NULL; + } + + return 0; +} + + +u8 * eap_tls_build_ack(struct eap_ssl_data *data, size_t *respDataLen, u8 id, + int eap_type, int peap_version) +{ + struct eap_hdr *resp; + u8 *pos; + + *respDataLen = sizeof(struct eap_hdr) + 2; + resp = malloc(*respDataLen); + if (resp == NULL) + return NULL; + wpa_printf(MSG_DEBUG, "SSL: Building ACK"); + resp->code = EAP_CODE_RESPONSE; + resp->identifier = id; + resp->length = host_to_be16(*respDataLen); + pos = (u8 *) (resp + 1); + *pos++ = eap_type; /* Type */ + *pos = peap_version; /* Flags */ + return (u8 *) resp; +} + + +int eap_tls_reauth_init(struct eap_sm *sm, struct eap_ssl_data *data) +{ + return tls_connection_shutdown(sm->ssl_ctx, data->conn); +} + + +int eap_tls_status(struct eap_sm *sm, struct eap_ssl_data *data, char *buf, + size_t buflen, int verbose) +{ + char name[128]; + int len = 0; + + if (tls_get_cipher(sm->ssl_ctx, data->conn, name, sizeof(name)) == 0) { + len += snprintf(buf + len, buflen - len, + "EAP TLS cipher=%s\n", name); + } + + return len; +} diff --git a/contrib/wpa_supplicant/eap_tls_common.h b/contrib/wpa_supplicant/eap_tls_common.h new file mode 100644 index 000000000000..499cae4c0e10 --- /dev/null +++ b/contrib/wpa_supplicant/eap_tls_common.h @@ -0,0 +1,51 @@ +#ifndef EAP_TLS_COMMON_H +#define EAP_TLS_COMMON_H + +struct eap_ssl_data { + struct tls_connection *conn; + + u8 *tls_out; + size_t tls_out_len; + size_t tls_out_pos; + size_t tls_out_limit; + u8 *tls_in; + size_t tls_in_len; + size_t tls_in_left; + size_t tls_in_total; + + int phase2; + int include_tls_length; /* include TLS length field even if the TLS + * data is not fragmented */ + + struct eap_sm *eap; +}; + + +/* EAP TLS Flags */ +#define EAP_TLS_FLAGS_LENGTH_INCLUDED 0x80 +#define EAP_TLS_FLAGS_MORE_FRAGMENTS 0x40 +#define EAP_TLS_FLAGS_START 0x20 +#define EAP_PEAP_VERSION_MASK 0x07 + + /* could be up to 128 bytes, but only the first 64 bytes are used */ +#define EAP_TLS_KEY_LEN 64 + + +int eap_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data, + struct wpa_ssid *config); +void eap_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data); +u8 * eap_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data, + char *label, size_t len); +int eap_tls_data_reassemble(struct eap_sm *sm, struct eap_ssl_data *data, + u8 **in_data, size_t *in_len); +int eap_tls_process_helper(struct eap_sm *sm, struct eap_ssl_data *data, + int eap_type, int peap_version, + u8 id, u8 *in_data, size_t in_len, + u8 **out_data, size_t *out_len); +u8 * eap_tls_build_ack(struct eap_ssl_data *data, size_t *respDataLen, u8 id, + int eap_type, int peap_version); +int eap_tls_reauth_init(struct eap_sm *sm, struct eap_ssl_data *data); +int eap_tls_status(struct eap_sm *sm, struct eap_ssl_data *data, char *buf, + size_t buflen, int verbose); + +#endif /* EAP_TLS_COMMON_H */ diff --git a/contrib/wpa_supplicant/eap_tlv.c b/contrib/wpa_supplicant/eap_tlv.c new file mode 100644 index 000000000000..5571d8bdf2cb --- /dev/null +++ b/contrib/wpa_supplicant/eap_tlv.c @@ -0,0 +1,176 @@ +/* + * WPA Supplicant / EAP-TLV (draft-josefsson-pppext-eap-tls-eap-07.txt) + * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include "common.h" +#include "wpa_supplicant.h" +#include "eap_i.h" +#include "eap_tlv.h" + + +u8 * eap_tlv_build_nak(int id, int nak_type, size_t *resp_len) +{ + struct eap_hdr *hdr; + u8 *pos; + + *resp_len = sizeof(struct eap_hdr) + 1 + 10; + hdr = malloc(*resp_len); + if (hdr == NULL) + return NULL; + + hdr->code = EAP_CODE_RESPONSE; + hdr->identifier = id; + hdr->length = host_to_be16(*resp_len); + pos = (u8 *) (hdr + 1); + *pos++ = EAP_TYPE_TLV; + *pos++ = 0x80; /* Mandatory */ + *pos++ = EAP_TLV_NAK_TLV; + /* Length */ + *pos++ = 0; + *pos++ = 6; + /* Vendor-Id */ + *pos++ = 0; + *pos++ = 0; + *pos++ = 0; + *pos++ = 0; + /* NAK-Type */ + *pos++ = nak_type >> 8; + *pos++ = nak_type & 0xff; + + return (u8 *) hdr; +} + + +u8 * eap_tlv_build_result(int id, int status, size_t *resp_len) +{ + struct eap_hdr *hdr; + u8 *pos; + + *resp_len = sizeof(struct eap_hdr) + 1 + 6; + hdr = malloc(*resp_len); + if (hdr == NULL) + return NULL; + + hdr->code = EAP_CODE_RESPONSE; + hdr->identifier = id; + hdr->length = host_to_be16(*resp_len); + pos = (u8 *) (hdr + 1); + *pos++ = EAP_TYPE_TLV; + *pos++ = 0x80; /* Mandatory */ + *pos++ = EAP_TLV_RESULT_TLV; + /* Length */ + *pos++ = 0; + *pos++ = 2; + /* Status */ + *pos++ = status >> 8; + *pos++ = status & 0xff; + + return (u8 *) hdr; +} + + +int eap_tlv_process(struct eap_sm *sm, struct eap_method_ret *ret, + struct eap_hdr *hdr, u8 **resp, size_t *resp_len) +{ + size_t left; + u8 *pos; + u8 *result_tlv = NULL; + size_t result_tlv_len = 0; + int tlv_type, mandatory, tlv_len; + + /* Parse TLVs */ + left = be_to_host16(hdr->length) - sizeof(struct eap_hdr) - 1; + pos = (u8 *) (hdr + 1); + pos++; + wpa_hexdump(MSG_DEBUG, "EAP-TLV: Received TLVs", pos, left); + while (left >= 4) { + mandatory = !!(pos[0] & 0x80); + tlv_type = pos[0] & 0x3f; + tlv_type = (tlv_type << 8) | pos[1]; + tlv_len = ((int) pos[2] << 8) | pos[3]; + pos += 4; + left -= 4; + if (tlv_len > left) { + wpa_printf(MSG_DEBUG, "EAP-TLV: TLV underrun " + "(tlv_len=%d left=%lu)", tlv_len, + (unsigned long) left); + return -1; + } + switch (tlv_type) { + case EAP_TLV_RESULT_TLV: + result_tlv = pos; + result_tlv_len = tlv_len; + break; + default: + wpa_printf(MSG_DEBUG, "EAP-TLV: Unsupported TLV Type " + "%d%s", tlv_type, + mandatory ? " (mandatory)" : ""); + if (mandatory) { + /* NAK TLV and ignore all TLVs in this packet. + */ + *resp = eap_tlv_build_nak(hdr->identifier, + tlv_type, resp_len); + return *resp == NULL ? -1 : 0; + } + /* Ignore this TLV, but process other TLVs */ + break; + } + + pos += tlv_len; + left -= tlv_len; + } + if (left) { + wpa_printf(MSG_DEBUG, "EAP-TLV: Last TLV too short in " + "Request (left=%lu)", (unsigned long) left); + return -1; + } + + /* Process supported TLVs */ + if (result_tlv) { + int status, resp_status; + wpa_hexdump(MSG_DEBUG, "EAP-TLV: Result TLV", + result_tlv, result_tlv_len); + if (result_tlv_len < 2) { + wpa_printf(MSG_INFO, "EAP-TLV: Too short Result TLV " + "(len=%lu)", + (unsigned long) result_tlv_len); + return -1; + } + status = ((int) result_tlv[0] << 8) | result_tlv[1]; + if (status == EAP_TLV_RESULT_SUCCESS) { + wpa_printf(MSG_INFO, "EAP-TLV: TLV Result - Success " + "- EAP-TLV/Phase2 Completed"); + resp_status = EAP_TLV_RESULT_SUCCESS; + ret->decision = DECISION_UNCOND_SUCC; + } else if (status == EAP_TLV_RESULT_FAILURE) { + wpa_printf(MSG_INFO, "EAP-TLV: TLV Result - Failure"); + resp_status = EAP_TLV_RESULT_FAILURE; + ret->decision = DECISION_FAIL; + } else { + wpa_printf(MSG_INFO, "EAP-TLV: Unknown TLV Result " + "Status %d", status); + resp_status = EAP_TLV_RESULT_FAILURE; + ret->decision = DECISION_FAIL; + } + ret->methodState = METHOD_DONE; + + *resp = eap_tlv_build_result(hdr->identifier, resp_status, + resp_len); + } + + return 0; +} diff --git a/contrib/wpa_supplicant/eap_tlv.h b/contrib/wpa_supplicant/eap_tlv.h new file mode 100644 index 000000000000..439155263716 --- /dev/null +++ b/contrib/wpa_supplicant/eap_tlv.h @@ -0,0 +1,73 @@ +#ifndef EAP_TLV_H +#define EAP_TLV_H + +/* EAP-TLV TLVs (draft-josefsson-ppext-eap-tls-eap-07.txt) */ +#define EAP_TLV_RESULT_TLV 3 /* Acknowledged Result */ +#define EAP_TLV_NAK_TLV 4 +#define EAP_TLV_CRYPTO_BINDING_TLV 5 +#define EAP_TLV_CONNECTION_BINDING_TLV 6 +#define EAP_TLV_VENDOR_SPECIFIC_TLV 7 +#define EAP_TLV_URI_TLV 8 +#define EAP_TLV_EAP_PAYLOAD_TLV 9 +#define EAP_TLV_INTERMEDIATE_RESULT_TLV 10 +#define EAP_TLV_PAC_TLV 11 /* draft-cam-winget-eap-fast-01.txt */ +#define EAP_TLV_CRYPTO_BINDING_TLV_ 12 /* draft-cam-winget-eap-fast-01.txt */ + +#define EAP_TLV_RESULT_SUCCESS 1 +#define EAP_TLV_RESULT_FAILURE 2 + +#define EAP_TLV_TYPE_MANDATORY 0x8000 + +struct eap_tlv_hdr { + u16 tlv_type; + u16 length; +}; + +struct eap_tlv_nak_tlv { + u16 tlv_type; + u16 length; + u32 vendor_id; + u16 nak_type; +} __attribute__((packed)); + +struct eap_tlv_result_tlv { + u16 tlv_type; + u16 length; + u16 status; +} __attribute__((packed)); + +struct eap_tlv_intermediate_result_tlv { + u16 tlv_type; + u16 length; + u16 status; +} __attribute__((packed)); + +struct eap_tlv_crypto_binding__tlv { + u16 tlv_type; + u16 length; + u8 reserved; + u8 version; + u8 received_version; + u8 subtype; + u8 nonce[32]; + u8 compound_mac[20]; +} __attribute__((packed)); + +struct eap_tlv_pac_ack_tlv { + u16 tlv_type; + u16 length; + u16 pac_type; + u16 pac_len; + u16 result; +} __attribute__((packed)); + +#define EAP_TLV_CRYPTO_BINDING_SUBTYPE_REQUEST 0 +#define EAP_TLV_CRYPTO_BINDING_SUBTYPE_RESPONSE 1 + + +u8 * eap_tlv_build_nak(int id, int nak_type, size_t *resp_len); +u8 * eap_tlv_build_result(int id, int status, size_t *resp_len); +int eap_tlv_process(struct eap_sm *sm, struct eap_method_ret *ret, + struct eap_hdr *hdr, u8 **resp, size_t *resp_len); + +#endif /* EAP_TLV_H */ diff --git a/contrib/wpa_supplicant/eap_ttls.c b/contrib/wpa_supplicant/eap_ttls.c new file mode 100644 index 000000000000..b639fcd54dc8 --- /dev/null +++ b/contrib/wpa_supplicant/eap_ttls.c @@ -0,0 +1,1384 @@ +/* + * WPA Supplicant / EAP-TTLS (draft-ietf-pppext-eap-ttls-03.txt) + * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include "common.h" +#include "eap_i.h" +#include "eap_tls_common.h" +#include "wpa_supplicant.h" +#include "config_ssid.h" +#include "ms_funcs.h" +#include "md5.h" +#include "tls.h" +#include "eap_ttls.h" + + +static void eap_ttls_deinit(struct eap_sm *sm, void *priv); + + +struct eap_ttls_data { + struct eap_ssl_data ssl; + + const struct eap_method *phase2_method; + void *phase2_priv; + int phase2_success; + int phase2_start; + + enum { + EAP_TTLS_PHASE2_EAP, + EAP_TTLS_PHASE2_MSCHAPV2, + EAP_TTLS_PHASE2_MSCHAP, + EAP_TTLS_PHASE2_PAP, + EAP_TTLS_PHASE2_CHAP + } phase2_type; + u8 phase2_eap_type; + u8 *phase2_eap_types; + size_t num_phase2_eap_types; + + u8 auth_response[20]; + int auth_response_valid; + u8 ident; + int resuming; /* starting a resumed session */ + int reauth; /* reauthentication */ + u8 *key_data; + + u8 *pending_phase2_req; + size_t pending_phase2_req_len; +}; + + +static void * eap_ttls_init(struct eap_sm *sm) +{ + struct eap_ttls_data *data; + struct wpa_ssid *config = eap_get_config(sm); + char *selected; + + data = malloc(sizeof(*data)); + if (data == NULL) + return NULL; + memset(data, 0, sizeof(*data)); + selected = "EAP"; + data->phase2_type = EAP_TTLS_PHASE2_EAP; + if (config && config->phase2) { + if (strstr(config->phase2, "autheap=")) { + selected = "EAP"; + data->phase2_type = EAP_TTLS_PHASE2_EAP; + } else if (strstr(config->phase2, "auth=MSCHAPV2")) { + selected = "MSCHAPV2"; + data->phase2_type = EAP_TTLS_PHASE2_MSCHAPV2; + } else if (strstr(config->phase2, "auth=MSCHAP")) { + selected = "MSCHAP"; + data->phase2_type = EAP_TTLS_PHASE2_MSCHAP; + } else if (strstr(config->phase2, "auth=PAP")) { + selected = "PAP"; + data->phase2_type = EAP_TTLS_PHASE2_PAP; + } else if (strstr(config->phase2, "auth=CHAP")) { + selected = "CHAP"; + data->phase2_type = EAP_TTLS_PHASE2_CHAP; + } + } + wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase2 type: %s", selected); + + if (data->phase2_type == EAP_TTLS_PHASE2_EAP) { + if (config && config->phase2) { + char *start, *pos, *buf; + u8 method, *methods = NULL, *_methods; + size_t num_methods = 0; + start = buf = strdup(config->phase2); + if (buf == NULL) { + eap_ttls_deinit(sm, data); + return NULL; + } + while (start && *start != '\0') { + pos = strstr(start, "autheap="); + if (pos == NULL) + break; + if (start != pos && *(pos - 1) != ' ') { + start = pos + 8; + continue; + } + + start = pos + 8; + pos = strchr(start, ' '); + if (pos) + *pos++ = '\0'; + method = eap_get_phase2_type(start); + if (method == EAP_TYPE_NONE) { + wpa_printf(MSG_ERROR, "EAP-TTLS: " + "Unsupported Phase2 EAP " + "method '%s'", start); + } else { + num_methods++; + _methods = realloc(methods, + num_methods); + if (_methods == NULL) { + free(methods); + eap_ttls_deinit(sm, data); + return NULL; + } + methods = _methods; + methods[num_methods - 1] = method; + } + + start = pos; + } + free(buf); + data->phase2_eap_types = methods; + data->num_phase2_eap_types = num_methods; + } + if (data->phase2_eap_types == NULL) { + data->phase2_eap_types = eap_get_phase2_types( + config, &data->num_phase2_eap_types); + } + if (data->phase2_eap_types == NULL) { + wpa_printf(MSG_ERROR, "EAP-TTLS: No Phase2 EAP method " + "available"); + eap_ttls_deinit(sm, data); + return NULL; + } + wpa_hexdump(MSG_DEBUG, "EAP-TTLS: Phase2 EAP types", + data->phase2_eap_types, + data->num_phase2_eap_types); + data->phase2_eap_type = EAP_TYPE_NONE; + } + + + if (eap_tls_ssl_init(sm, &data->ssl, config)) { + wpa_printf(MSG_INFO, "EAP-TTLS: Failed to initialize SSL."); + eap_ttls_deinit(sm, data); + return NULL; + } + + return data; +} + + +static void eap_ttls_deinit(struct eap_sm *sm, void *priv) +{ + struct eap_ttls_data *data = priv; + if (data == NULL) + return; + if (data->phase2_priv && data->phase2_method) + data->phase2_method->deinit(sm, data->phase2_priv); + free(data->phase2_eap_types); + eap_tls_ssl_deinit(sm, &data->ssl); + free(data->key_data); + free(data->pending_phase2_req); + free(data); +} + + +static int eap_ttls_encrypt(struct eap_sm *sm, struct eap_ttls_data *data, + int id, u8 *plain, size_t plain_len, + u8 **out_data, size_t *out_len) +{ + int res; + u8 *pos; + struct eap_hdr *resp; + + /* TODO: add support for fragmentation, if needed. This will need to + * add TLS Message Length field, if the frame is fragmented. */ + resp = malloc(sizeof(struct eap_hdr) + 2 + data->ssl.tls_out_limit); + if (resp == NULL) + return 0; + + resp->code = EAP_CODE_RESPONSE; + resp->identifier = id; + + pos = (u8 *) (resp + 1); + *pos++ = EAP_TYPE_TTLS; + *pos++ = 0; + + res = tls_connection_encrypt(sm->ssl_ctx, data->ssl.conn, + plain, plain_len, + pos, data->ssl.tls_out_limit); + if (res < 0) { + wpa_printf(MSG_INFO, "EAP-TTLS: Failed to encrypt Phase 2 " + "data"); + free(resp); + return 0; + } + + *out_len = sizeof(struct eap_hdr) + 2 + res; + resp->length = host_to_be16(*out_len); + *out_data = (u8 *) resp; + return 0; +} + + +static u8 * eap_ttls_avp_hdr(u8 *avphdr, u32 avp_code, u32 vendor_id, + int mandatory, size_t len) +{ + struct ttls_avp_vendor *avp; + u8 flags; + size_t hdrlen; + + avp = (struct ttls_avp_vendor *) avphdr; + flags = mandatory ? AVP_FLAGS_MANDATORY : 0; + if (vendor_id) { + flags |= AVP_FLAGS_VENDOR; + hdrlen = sizeof(*avp); + avp->vendor_id = host_to_be32(vendor_id); + } else { + hdrlen = sizeof(struct ttls_avp); + } + + avp->avp_code = host_to_be32(avp_code); + avp->avp_length = host_to_be32((flags << 24) | (hdrlen + len)); + + return avphdr + hdrlen; +} + + +static u8 * eap_ttls_avp_add(u8 *start, u8 *avphdr, u32 avp_code, + u32 vendor_id, int mandatory, + u8 *data, size_t len) +{ + u8 *pos; + pos = eap_ttls_avp_hdr(avphdr, avp_code, vendor_id, mandatory, len); + memcpy(pos, data, len); + pos += len; + AVP_PAD(start, pos); + return pos; +} + + +static int eap_ttls_avp_encapsulate(u8 **resp, size_t *resp_len, u32 avp_code, + int mandatory) +{ + u8 *avp, *pos; + + avp = malloc(sizeof(struct ttls_avp) + *resp_len + 4); + if (avp == |