From b415c9e95aca3f90f072ffa35692b1a925d7de94 Mon Sep 17 00:00:00 2001 From: Sepherosa Ziehau Date: Sat, 2 Sep 2006 05:28:36 +0000 Subject: [PATCH] Import of hostapd 0.4.9 --- contrib/hostapd-0.4.9/COPYING | 340 +++ contrib/hostapd-0.4.9/README | 395 ++++ contrib/hostapd-0.4.9/README.DELETE | 18 + contrib/hostapd-0.4.9/README.DRAGONFLY | 4 + contrib/hostapd-0.4.9/accounting.c | 457 ++++ contrib/hostapd-0.4.9/accounting.h | 13 + contrib/hostapd-0.4.9/aes.c | 1105 ++++++++++ contrib/hostapd-0.4.9/aes_wrap.c | 725 +++++++ contrib/hostapd-0.4.9/aes_wrap.h | 42 + contrib/hostapd-0.4.9/ap.h | 99 + contrib/hostapd-0.4.9/common.c | 388 ++++ contrib/hostapd-0.4.9/common.h | 293 +++ contrib/hostapd-0.4.9/config.c | 1230 +++++++++++ contrib/hostapd-0.4.9/config.h | 160 ++ contrib/hostapd-0.4.9/config_types.h | 14 + contrib/hostapd-0.4.9/crypto.c | 157 ++ contrib/hostapd-0.4.9/crypto.h | 123 ++ contrib/hostapd-0.4.9/ctrl_iface.c | 456 ++++ contrib/hostapd-0.4.9/ctrl_iface.h | 9 + contrib/hostapd-0.4.9/defconfig | 75 + contrib/hostapd-0.4.9/defs.h | 131 ++ contrib/hostapd-0.4.9/developer.txt | 219 ++ contrib/hostapd-0.4.9/driver.h | 271 +++ contrib/hostapd-0.4.9/driver_wired.c | 399 ++++ contrib/hostapd-0.4.9/eap.c | 944 +++++++++ contrib/hostapd-0.4.9/eap.h | 89 + contrib/hostapd-0.4.9/eap_defs.h | 56 + contrib/hostapd-0.4.9/eap_gtc.c | 158 ++ contrib/hostapd-0.4.9/eap_i.h | 112 + contrib/hostapd-0.4.9/eap_identity.c | 185 ++ contrib/hostapd-0.4.9/eap_md5.c | 184 ++ contrib/hostapd-0.4.9/eap_mschapv2.c | 483 +++++ contrib/hostapd-0.4.9/eap_pax.c | 519 +++++ contrib/hostapd-0.4.9/eap_pax_common.c | 152 ++ contrib/hostapd-0.4.9/eap_pax_common.h | 84 + contrib/hostapd-0.4.9/eap_peap.c | 726 +++++++ contrib/hostapd-0.4.9/eap_psk.c | 458 ++++ contrib/hostapd-0.4.9/eap_psk_common.c | 57 + contrib/hostapd-0.4.9/eap_psk_common.h | 92 + contrib/hostapd-0.4.9/eap_sim.c | 431 ++++ contrib/hostapd-0.4.9/eap_sim_common.c | 794 +++++++ contrib/hostapd-0.4.9/eap_sim_common.h | 116 ++ contrib/hostapd-0.4.9/eap_sim_db.c | 243 +++ contrib/hostapd-0.4.9/eap_sim_db.h | 44 + contrib/hostapd-0.4.9/eap_tls.c | 253 +++ contrib/hostapd-0.4.9/eap_tls_common.c | 278 +++ contrib/hostapd-0.4.9/eap_tls_common.h | 47 + contrib/hostapd-0.4.9/eap_tlv.c | 248 +++ contrib/hostapd-0.4.9/eap_ttls.c | 1193 +++++++++++ contrib/hostapd-0.4.9/eap_ttls.h | 71 + contrib/hostapd-0.4.9/eapol_sm.c | 1259 +++++++++++ contrib/hostapd-0.4.9/eapol_sm.h | 224 ++ contrib/hostapd-0.4.9/eloop.c | 395 ++++ contrib/hostapd-0.4.9/eloop.h | 154 ++ contrib/hostapd-0.4.9/hostap_common.h | 558 +++++ contrib/hostapd-0.4.9/hostapd.8 | 56 + contrib/hostapd-0.4.9/hostapd.accept | 5 + contrib/hostapd-0.4.9/hostapd.c | 846 ++++++++ contrib/hostapd-0.4.9/hostapd.conf | 341 +++ contrib/hostapd-0.4.9/hostapd.deny | 5 + contrib/hostapd-0.4.9/hostapd.eap_user | 49 + contrib/hostapd-0.4.9/hostapd.h | 149 ++ contrib/hostapd-0.4.9/hostapd.radius_clients | 4 + contrib/hostapd-0.4.9/hostapd.sim_db | 9 + contrib/hostapd-0.4.9/hostapd.wpa_psk | 9 + contrib/hostapd-0.4.9/hostapd_cli.1 | 83 + contrib/hostapd-0.4.9/hostapd_cli.c | 601 ++++++ contrib/hostapd-0.4.9/iapp.c | 522 +++++ contrib/hostapd-0.4.9/iapp.h | 31 + contrib/hostapd-0.4.9/ieee802_11.c | 1220 +++++++++++ contrib/hostapd-0.4.9/ieee802_11.h | 99 + contrib/hostapd-0.4.9/ieee802_11_auth.c | 458 ++++ contrib/hostapd-0.4.9/ieee802_11_auth.h | 16 + contrib/hostapd-0.4.9/ieee802_1x.c | 1788 ++++++++++++++++ contrib/hostapd-0.4.9/ieee802_1x.h | 94 + contrib/hostapd-0.4.9/l2_packet.h | 133 ++ contrib/hostapd-0.4.9/madwifi.conf | 278 +++ contrib/hostapd-0.4.9/md5.c | 395 ++++ contrib/hostapd-0.4.9/md5.h | 25 + contrib/hostapd-0.4.9/ms_funcs.c | 503 +++++ contrib/hostapd-0.4.9/ms_funcs.h | 49 + contrib/hostapd-0.4.9/radius.c | 1158 ++++++++++ contrib/hostapd-0.4.9/radius.h | 226 ++ contrib/hostapd-0.4.9/radius_client.c | 1116 ++++++++++ contrib/hostapd-0.4.9/radius_client.h | 87 + contrib/hostapd-0.4.9/radius_server.c | 1074 ++++++++++ contrib/hostapd-0.4.9/radius_server.h | 46 + contrib/hostapd-0.4.9/rc4.c | 85 + contrib/hostapd-0.4.9/rc4.h | 22 + contrib/hostapd-0.4.9/sha1.c | 994 +++++++++ contrib/hostapd-0.4.9/sha1.h | 33 + contrib/hostapd-0.4.9/sta_info.c | 355 ++++ contrib/hostapd-0.4.9/sta_info.h | 19 + contrib/hostapd-0.4.9/tls.h | 463 ++++ contrib/hostapd-0.4.9/tls_none.c | 28 + contrib/hostapd-0.4.9/tls_openssl.c | 2144 +++++++++++++++++++ contrib/hostapd-0.4.9/version.h | 6 + contrib/hostapd-0.4.9/wired.conf | 40 + contrib/hostapd-0.4.9/wpa.c | 2897 ++++++++++++++++++++++++++ contrib/hostapd-0.4.9/wpa.h | 196 ++ contrib/hostapd-0.4.9/wpa_ctrl.c | 239 +++ contrib/hostapd-0.4.9/wpa_ctrl.h | 185 ++ 102 files changed, 36911 insertions(+) create mode 100644 contrib/hostapd-0.4.9/COPYING create mode 100644 contrib/hostapd-0.4.9/README create mode 100644 contrib/hostapd-0.4.9/README.DELETE create mode 100644 contrib/hostapd-0.4.9/README.DRAGONFLY create mode 100644 contrib/hostapd-0.4.9/accounting.c create mode 100644 contrib/hostapd-0.4.9/accounting.h create mode 100644 contrib/hostapd-0.4.9/aes.c create mode 100644 contrib/hostapd-0.4.9/aes_wrap.c create mode 100644 contrib/hostapd-0.4.9/aes_wrap.h create mode 100644 contrib/hostapd-0.4.9/ap.h create mode 100644 contrib/hostapd-0.4.9/common.c create mode 100644 contrib/hostapd-0.4.9/common.h create mode 100644 contrib/hostapd-0.4.9/config.c create mode 100644 contrib/hostapd-0.4.9/config.h create mode 100644 contrib/hostapd-0.4.9/config_types.h create mode 100644 contrib/hostapd-0.4.9/crypto.c create mode 100644 contrib/hostapd-0.4.9/crypto.h create mode 100644 contrib/hostapd-0.4.9/ctrl_iface.c create mode 100644 contrib/hostapd-0.4.9/ctrl_iface.h create mode 100644 contrib/hostapd-0.4.9/defconfig create mode 100644 contrib/hostapd-0.4.9/defs.h create mode 100644 contrib/hostapd-0.4.9/developer.txt create mode 100644 contrib/hostapd-0.4.9/driver.h create mode 100644 contrib/hostapd-0.4.9/driver_wired.c create mode 100644 contrib/hostapd-0.4.9/eap.c create mode 100644 contrib/hostapd-0.4.9/eap.h create mode 100644 contrib/hostapd-0.4.9/eap_defs.h create mode 100644 contrib/hostapd-0.4.9/eap_gtc.c create mode 100644 contrib/hostapd-0.4.9/eap_i.h create mode 100644 contrib/hostapd-0.4.9/eap_identity.c create mode 100644 contrib/hostapd-0.4.9/eap_md5.c create mode 100644 contrib/hostapd-0.4.9/eap_mschapv2.c create mode 100644 contrib/hostapd-0.4.9/eap_pax.c create mode 100644 contrib/hostapd-0.4.9/eap_pax_common.c create mode 100644 contrib/hostapd-0.4.9/eap_pax_common.h create mode 100644 contrib/hostapd-0.4.9/eap_peap.c create mode 100644 contrib/hostapd-0.4.9/eap_psk.c create mode 100644 contrib/hostapd-0.4.9/eap_psk_common.c create mode 100644 contrib/hostapd-0.4.9/eap_psk_common.h create mode 100644 contrib/hostapd-0.4.9/eap_sim.c create mode 100644 contrib/hostapd-0.4.9/eap_sim_common.c create mode 100644 contrib/hostapd-0.4.9/eap_sim_common.h create mode 100644 contrib/hostapd-0.4.9/eap_sim_db.c create mode 100644 contrib/hostapd-0.4.9/eap_sim_db.h create mode 100644 contrib/hostapd-0.4.9/eap_tls.c create mode 100644 contrib/hostapd-0.4.9/eap_tls_common.c create mode 100644 contrib/hostapd-0.4.9/eap_tls_common.h create mode 100644 contrib/hostapd-0.4.9/eap_tlv.c create mode 100644 contrib/hostapd-0.4.9/eap_ttls.c create mode 100644 contrib/hostapd-0.4.9/eap_ttls.h create mode 100644 contrib/hostapd-0.4.9/eapol_sm.c create mode 100644 contrib/hostapd-0.4.9/eapol_sm.h create mode 100644 contrib/hostapd-0.4.9/eloop.c create mode 100644 contrib/hostapd-0.4.9/eloop.h create mode 100644 contrib/hostapd-0.4.9/hostap_common.h create mode 100644 contrib/hostapd-0.4.9/hostapd.8 create mode 100644 contrib/hostapd-0.4.9/hostapd.accept create mode 100644 contrib/hostapd-0.4.9/hostapd.c create mode 100644 contrib/hostapd-0.4.9/hostapd.conf create mode 100644 contrib/hostapd-0.4.9/hostapd.deny create mode 100644 contrib/hostapd-0.4.9/hostapd.eap_user create mode 100644 contrib/hostapd-0.4.9/hostapd.h create mode 100644 contrib/hostapd-0.4.9/hostapd.radius_clients create mode 100644 contrib/hostapd-0.4.9/hostapd.sim_db create mode 100644 contrib/hostapd-0.4.9/hostapd.wpa_psk create mode 100644 contrib/hostapd-0.4.9/hostapd_cli.1 create mode 100644 contrib/hostapd-0.4.9/hostapd_cli.c create mode 100644 contrib/hostapd-0.4.9/iapp.c create mode 100644 contrib/hostapd-0.4.9/iapp.h create mode 100644 contrib/hostapd-0.4.9/ieee802_11.c create mode 100644 contrib/hostapd-0.4.9/ieee802_11.h create mode 100644 contrib/hostapd-0.4.9/ieee802_11_auth.c create mode 100644 contrib/hostapd-0.4.9/ieee802_11_auth.h create mode 100644 contrib/hostapd-0.4.9/ieee802_1x.c create mode 100644 contrib/hostapd-0.4.9/ieee802_1x.h create mode 100644 contrib/hostapd-0.4.9/l2_packet.h create mode 100644 contrib/hostapd-0.4.9/madwifi.conf create mode 100644 contrib/hostapd-0.4.9/md5.c create mode 100644 contrib/hostapd-0.4.9/md5.h create mode 100644 contrib/hostapd-0.4.9/ms_funcs.c create mode 100644 contrib/hostapd-0.4.9/ms_funcs.h create mode 100644 contrib/hostapd-0.4.9/radius.c create mode 100644 contrib/hostapd-0.4.9/radius.h create mode 100644 contrib/hostapd-0.4.9/radius_client.c create mode 100644 contrib/hostapd-0.4.9/radius_client.h create mode 100644 contrib/hostapd-0.4.9/radius_server.c create mode 100644 contrib/hostapd-0.4.9/radius_server.h create mode 100644 contrib/hostapd-0.4.9/rc4.c create mode 100644 contrib/hostapd-0.4.9/rc4.h create mode 100644 contrib/hostapd-0.4.9/sha1.c create mode 100644 contrib/hostapd-0.4.9/sha1.h create mode 100644 contrib/hostapd-0.4.9/sta_info.c create mode 100644 contrib/hostapd-0.4.9/sta_info.h create mode 100644 contrib/hostapd-0.4.9/tls.h create mode 100644 contrib/hostapd-0.4.9/tls_none.c create mode 100644 contrib/hostapd-0.4.9/tls_openssl.c create mode 100644 contrib/hostapd-0.4.9/version.h create mode 100644 contrib/hostapd-0.4.9/wired.conf create mode 100644 contrib/hostapd-0.4.9/wpa.c create mode 100644 contrib/hostapd-0.4.9/wpa.h create mode 100644 contrib/hostapd-0.4.9/wpa_ctrl.c create mode 100644 contrib/hostapd-0.4.9/wpa_ctrl.h diff --git a/contrib/hostapd-0.4.9/COPYING b/contrib/hostapd-0.4.9/COPYING new file mode 100644 index 0000000000..60549be514 --- /dev/null +++ b/contrib/hostapd-0.4.9/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. + + + Copyright (C) 19yy + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 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. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/contrib/hostapd-0.4.9/README b/contrib/hostapd-0.4.9/README new file mode 100644 index 0000000000..13f38ee051 --- /dev/null +++ b/contrib/hostapd-0.4.9/README @@ -0,0 +1,395 @@ +hostapd - user space IEEE 802.11 AP and IEEE 802.1X/WPA/WPA2/EAP + Authenticator and RADIUS authentication server +================================================================ + +Copyright (c) 2002-2006, Jouni Malinen and +contributors +All Rights Reserved. + +This program is dual-licensed under both the GPL version 2 and BSD +license. Either license may be used at your option. + + + +License +------- + +GPL v2: + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License version 2 as +published by the Free Software Foundation. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +(this copy of the license is in COPYING file) + + +Alternatively, this software may be distributed under the terms of BSD +license: + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +3. Neither the name(s) of the above-listed copyright holder(s) nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + +Introduction +============ + +Originally, hostapd was an optional user space component for Host AP +driver. It adds more features to the basic IEEE 802.11 management +included in the kernel driver: using external RADIUS authentication +server for MAC address based access control, IEEE 802.1X Authenticator +and dynamic WEP keying, RADIUS accounting, WPA/WPA2 (IEEE 802.11i/RSN) +Authenticator and dynamic TKIP/CCMP keying. + +The current version includes support for other drivers, an integrated +EAP server (i.e., allow full authentication without requiring +an external RADIUS authentication server), and RADIUS authentication +server for EAP authentication. + + +Requirements +------------ + +Current hardware/software requirements: +- drivers: + Host AP driver for Prism2/2.5/3. + (http://hostap.epitest.fi/) + Please note that station firmware version needs to be 1.7.0 or newer + to work in WPA mode. + + madwifi driver for cards based on Atheros chip set (ar521x) + (http://sourceforge.net/projects/madwifi/) + Please note that you will need to add the correct path for + madwifi driver root directory in .config (see defconfig file for + an example: CFLAGS += -I) + + Prism54 driver for Intersil/Conexant Prism GT/Duette/Indigo + (http://www.prism54.org/) + + Any wired Ethernet driver for wired IEEE 802.1X authentication + (experimental code) + + FreeBSD -current (with some kernel mods that have not yet been + committed when hostapd v0.3.0 was released) + BSD net80211 layer (e.g., Atheros driver) + + +Build configuration +------------------- + +In order to be able to build hostapd, you will need to create a build +time configuration file, .config that selects which optional +components are included. See defconfig file for example configuration +and list of available options. + + + +IEEE 802.1X +=========== + +IEEE Std 802.1X-2001 is a standard for port-based network access +control. In case of IEEE 802.11 networks, a "virtual port" is used +between each associated station and the AP. IEEE 802.11 specifies +minimal authentication mechanism for stations, whereas IEEE 802.1X +introduces a extensible mechanism for authenticating and authorizing +users. + +IEEE 802.1X uses elements called Supplicant, Authenticator, Port +Access Entity, and Authentication Server. Supplicant is a component in +a station and it performs the authentication with the Authentication +Server. An access point includes an Authenticator that relays the packets +between a Supplicant and an Authentication Server. In addition, it has a +Port Access Entity (PAE) with Authenticator functionality for +controlling the virtual port authorization, i.e., whether to accept +packets from or to the station. + +IEEE 802.1X uses Extensible Authentication Protocol (EAP). The frames +between a Supplicant and an Authenticator are sent using EAP over LAN +(EAPOL) and the Authenticator relays these frames to the Authentication +Server (and similarly, relays the messages from the Authentication +Server to the Supplicant). The Authentication Server can be colocated with the +Authenticator, in which case there is no need for additional protocol +for EAP frame transmission. However, a more common configuration is to +use an external Authentication Server and encapsulate EAP frame in the +frames used by that server. RADIUS is suitable for this, but IEEE +802.1X would also allow other mechanisms. + +Host AP driver includes PAE functionality in the kernel driver. It +is a relatively simple mechanism for denying normal frames going to +or coming from an unauthorized port. PAE allows IEEE 802.1X related +frames to be passed between the Supplicant and the Authenticator even +on an unauthorized port. + +User space daemon, hostapd, includes Authenticator functionality. It +receives 802.1X (EAPOL) frames from the Supplicant using the wlan#ap +device that is also used with IEEE 802.11 management frames. The +frames to the Supplicant are sent using the same device. + +hostapd includes a minimal colocated Authentication Server for testing +purposes. It only requests the identity of the Supplicant and +authorizes any host that is able to send a valid EAP Response +frame. This can be used for quick testing since it does not require an +external Authentication Server, but it should not be used for any real +authentication purposes since no keys are required and anyone can +authenticate. + +The normal configuration of the Authenticator would use an external +Authentication Server. hostapd supports RADIUS encapsulation of EAP +packets, so the Authentication Server should be a RADIUS server, like +FreeRADIUS (http://www.freeradius.org/). The Authenticator in hostapd +relays the frames between the Supplicant and the Authentication +Server. It also controls the PAE functionality in the kernel driver by +controlling virtual port authorization, i.e., station-AP +connection, based on the IEEE 802.1X state. + +When a station would like to use the services of an access point, it +will first perform IEEE 802.11 authentication. This is normally done +with open systems authentication, so there is no security. After +this, IEEE 802.11 association is performed. If IEEE 802.1X is +configured to be used, the virtual port for the station is set in +Unauthorized state and only IEEE 802.1X frames are accepted at this +point. The Authenticator will then ask the Supplicant to authenticate +with the Authentication Server. After this is completed successfully, +the virtual port is set to Authorized state and frames from and to the +station are accepted. + +Host AP configuration for IEEE 802.1X +------------------------------------- + +The user space daemon has its own configuration file that can be used to +define AP options. Distribution package contains an example +configuration file (hostapd/hostapd.conf) that can be used as a basis +for configuration. It includes examples of all supported configuration +options and short description of each option. hostapd should be started +with full path to the configuration file as the command line argument, +e.g., './hostapd /etc/hostapd.conf'. If you have more that one wireless +LAN card, you can use one hostapd process for multiple interfaces by +giving a list of configuration files (one per interface) in the command +line. + +hostapd includes a minimal co-located IEEE 802.1X server which can be +used to test IEEE 802.1X authentication. However, it should not be +used in normal use since it does not provide any security. This can be +configured by setting ieee8021x and minimal_eap options in the +configuration file. + +An external Authentication Server (RADIUS) is configured with +auth_server_{addr,port,shared_secret} options. In addition, +ieee8021x and own_ip_addr must be set for this mode. With such +configuration, the co-located Authentication Server is not used and EAP +frames will be relayed using EAPOL between the Supplicant and the +Authenticator and RADIUS encapsulation between the Authenticator and +the Authentication Server. Other than this, the functionality is similar +to the case with the co-located Authentication Server. + +Authentication Server and Supplicant +------------------------------------ + +Any RADIUS server supporting EAP should be usable as an IEEE 802.1X +Authentication Server with hostapd Authenticator. FreeRADIUS +(http://www.freeradius.org/) has been successfully tested with hostapd +Authenticator and both Xsupplicant (http://www.open1x.org) and Windows +XP Supplicants. EAP/TLS was used with Xsupplicant and +EAP/MD5-Challenge with Windows XP. + +http://www.missl.cs.umd.edu/wireless/eaptls/ has useful information +about using EAP/TLS with FreeRADIUS and Xsupplicant (just replace +Cisco access point with Host AP driver, hostapd daemon, and a Prism2 +card ;-). http://www.freeradius.org/doc/EAP-MD5.html has information +about using EAP/MD5 with FreeRADIUS, including instructions for WinXP +configuration. http://www.denobula.com/EAPTLS.pdf has a HOWTO on +EAP/TLS use with WinXP Supplicant. + +Automatic WEP key configuration +------------------------------- + +EAP/TLS generates a session key that can be used to send WEP keys from +an AP to authenticated stations. The Authenticator in hostapd can be +configured to automatically select a random default/broadcast key +(shared by all authenticated stations) with wep_key_len_broadcast +option (5 for 40-bit WEP or 13 for 104-bit WEP). In addition, +wep_key_len_unicast option can be used to configure individual unicast +keys for stations. This requires support for individual keys in the +station driver. + +WEP keys can be automatically updated by configuring rekeying. This +will improve security of the network since same WEP key will only be +used for a limited period of time. wep_rekey_period option sets the +interval for rekeying in seconds. + + +WPA/WPA2 +======== + +Features +-------- + +Supported WPA/IEEE 802.11i features: +- WPA-PSK ("WPA-Personal") +- WPA with EAP (e.g., with RADIUS authentication server) ("WPA-Enterprise") +- key management for CCMP, TKIP, WEP104, WEP40 +- RSN/WPA2 (IEEE 802.11i), including PMKSA caching and pre-authentication + +WPA +--- + +The original security mechanism of IEEE 802.11 standard was not +designed to be strong and has proved to be insufficient for most +networks that require some kind of security. Task group I (Security) +of IEEE 802.11 working group (http://www.ieee802.org/11/) has worked +to address the flaws of the base standard and has in practice +completed its work in May 2004. The IEEE 802.11i amendment to the IEEE +802.11 standard was approved in June 2004 and this amendment is likely +to be published in July 2004. + +Wi-Fi Alliance (http://www.wi-fi.org/) used a draft version of the +IEEE 802.11i work (draft 3.0) to define a subset of the security +enhancements that can be implemented with existing wlan hardware. This +is called Wi-Fi Protected Access (WPA). This has now become a +mandatory component of interoperability testing and certification done +by Wi-Fi Alliance. Wi-Fi provides information about WPA at its web +site (http://www.wi-fi.org/OpenSection/protected_access.asp). + +IEEE 802.11 standard defined wired equivalent privacy (WEP) algorithm +for protecting wireless networks. WEP uses RC4 with 40-bit keys, +24-bit initialization vector (IV), and CRC32 to protect against packet +forgery. All these choices have proven to be insufficient: key space is +too small against current attacks, RC4 key scheduling is insufficient +(beginning of the pseudorandom stream should be skipped), IV space is +too small and IV reuse makes attacks easier, there is no replay +protection, and non-keyed authentication does not protect against bit +flipping packet data. + +WPA is an intermediate solution for the security issues. It uses +Temporal Key Integrity Protocol (TKIP) to replace WEP. TKIP is a +compromise on strong security and possibility to use existing +hardware. It still uses RC4 for the encryption like WEP, but with +per-packet RC4 keys. In addition, it implements replay protection, +keyed packet authentication mechanism (Michael MIC). + +Keys can be managed using two different mechanisms. WPA can either use +an external authentication server (e.g., RADIUS) and EAP just like +IEEE 802.1X is using or pre-shared keys without need for additional +servers. Wi-Fi calls these "WPA-Enterprise" and "WPA-Personal", +respectively. Both mechanisms will generate a master session key for +the Authenticator (AP) and Supplicant (client station). + +WPA implements a new key handshake (4-Way Handshake and Group Key +Handshake) for generating and exchanging data encryption keys between +the Authenticator and Supplicant. This handshake is also used to +verify that both Authenticator and Supplicant know the master session +key. These handshakes are identical regardless of the selected key +management mechanism (only the method for generating master session +key changes). + + +IEEE 802.11i / WPA2 +------------------- + +The design for parts of IEEE 802.11i that were not included in WPA has +finished (May 2004) and this amendment to IEEE 802.11 was approved in +June 2004. Wi-Fi Alliance is using the final IEEE 802.11i as a new +version of WPA called WPA2. This includes, e.g., support for more +robust encryption algorithm (CCMP: AES in Counter mode with CBC-MAC) +to replace TKIP and optimizations for handoff (reduced number of +messages in initial key handshake, pre-authentication, and PMKSA caching). + +Some wireless LAN vendors are already providing support for CCMP in +their WPA products. There is no "official" interoperability +certification for CCMP and/or mixed modes using both TKIP and CCMP, so +some interoperability issues can be expected even though many +combinations seem to be working with equipment from different vendors. +Testing for WPA2 is likely to start during the second half of 2004. + +hostapd configuration for WPA/WPA2 +---------------------------------- + +TODO + +# Enable WPA. Setting this variable configures the AP to require WPA (either +# WPA-PSK or WPA-RADIUS/EAP based on other configuration). For WPA-PSK, either +# wpa_psk or wpa_passphrase must be set and wpa_key_mgmt must include WPA-PSK. +# For WPA-RADIUS/EAP, ieee8021x must be set (but without dynamic WEP keys), +# RADIUS authentication server must be configured, and WPA-EAP must be included +# in wpa_key_mgmt. +# This field is a bit field that can be used to enable WPA (IEEE 802.11i/D3.0) +# and/or WPA2 (full IEEE 802.11i/RSN): +# bit0 = WPA +# bit1 = IEEE 802.11i/RSN (WPA2) +#wpa=1 + +# WPA pre-shared keys for WPA-PSK. This can be either entered as a 256-bit +# secret in hex format (64 hex digits), wpa_psk, or as an ASCII passphrase +# (8..63 characters) that will be converted to PSK. This conversion uses SSID +# so the PSK changes when ASCII passphrase is used and the SSID is changed. +#wpa_psk=0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef +#wpa_passphrase=secret passphrase + +# Set of accepted key management algorithms (WPA-PSK, WPA-EAP, or both). The +# entries are separated with a space. +#wpa_key_mgmt=WPA-PSK WPA-EAP + +# Set of accepted cipher suites (encryption algorithms) for pairwise keys +# (unicast packets). This is a space separated list of algorithms: +# CCMP = AES in Counter mode with CBC-MAC [RFC 3610, IEEE 802.11i] +# TKIP = Temporal Key Integrity Protocol [IEEE 802.11i] +# Group cipher suite (encryption algorithm for broadcast and multicast frames) +# is automatically selected based on this configuration. If only CCMP is +# allowed as the pairwise cipher, group cipher will also be CCMP. Otherwise, +# TKIP will be used as the group cipher. +#wpa_pairwise=TKIP CCMP + +# Time interval for rekeying GTK (broadcast/multicast encryption keys) in +# seconds. +#wpa_group_rekey=600 + +# Time interval for rekeying GMK (master key used internally to generate GTKs +# (in seconds). +#wpa_gmk_rekey=86400 + +# Enable IEEE 802.11i/RSN/WPA2 pre-authentication. This is used to speed up +# roaming be pre-authenticating IEEE 802.1X/EAP part of the full RSN +# authentication and key handshake before actually associating with a new AP. +#rsn_preauth=1 +# +# Space separated list of interfaces from which pre-authentication frames are +# accepted (e.g., 'eth0' or 'eth0 wlan0wds0'. This list should include all +# interface that are used for connections to other APs. This could include +# wired interfaces and WDS links. The normal wireless data interface towards +# associated stations (e.g., wlan0) should not be added, since +# pre-authentication is only used with APs other than the currently associated +# one. +#rsn_preauth_interfaces=eth0 diff --git a/contrib/hostapd-0.4.9/README.DELETE b/contrib/hostapd-0.4.9/README.DELETE new file mode 100644 index 0000000000..7c7b59d55d --- /dev/null +++ b/contrib/hostapd-0.4.9/README.DELETE @@ -0,0 +1,18 @@ +.cvsignore +ChangeLog +Makefile +README.DELETE +driver.c +driver_bsd.c +driver_madwifi.c +driver_prism54.c +driver_test.c +l2_packet_freebsd.c +l2_packet_linux.c +l2_packet_pcap.c +logwatch/README +logwatch/hostapd +logwatch/hostapd.conf +prism54.h +priv_netlink.h +wireless_copy.h diff --git a/contrib/hostapd-0.4.9/README.DRAGONFLY b/contrib/hostapd-0.4.9/README.DRAGONFLY new file mode 100644 index 0000000000..219b8d75a7 --- /dev/null +++ b/contrib/hostapd-0.4.9/README.DRAGONFLY @@ -0,0 +1,4 @@ +Original source can be downloaded at: + + +A list of deleted files is in README.DELETED. diff --git a/contrib/hostapd-0.4.9/accounting.c b/contrib/hostapd-0.4.9/accounting.c new file mode 100644 index 0000000000..5ee3d750f9 --- /dev/null +++ b/contrib/hostapd-0.4.9/accounting.c @@ -0,0 +1,457 @@ +/* + * Host AP (software wireless LAN access point) user space daemon for + * Host AP kernel driver / Accounting + * Copyright (c) 2002-2005, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include "hostapd.h" +#include "radius.h" +#include "radius_client.h" +#include "eloop.h" +#include "accounting.h" +#include "ieee802_1x.h" +#include "driver.h" + + +/* Default interval in seconds for polling TX/RX octets from the driver if + * STA is not using interim accounting. This detects wrap arounds for + * input/output octets and updates Acct-{Input,Output}-Gigawords. */ +#define ACCT_DEFAULT_UPDATE_INTERVAL 300 + +static struct radius_msg * accounting_msg(hostapd *hapd, struct sta_info *sta, + int status_type) +{ + struct radius_msg *msg; + char buf[128]; + u8 *val; + size_t len; + int i; + + msg = radius_msg_new(RADIUS_CODE_ACCOUNTING_REQUEST, + radius_client_get_id(hapd->radius)); + if (msg == NULL) { + printf("Could not create net RADIUS packet\n"); + return NULL; + } + + if (sta) { + radius_msg_make_authenticator(msg, (u8 *) sta, sizeof(*sta)); + + snprintf(buf, sizeof(buf), "%08X-%08X", + sta->acct_session_id_hi, sta->acct_session_id_lo); + if (!radius_msg_add_attr(msg, RADIUS_ATTR_ACCT_SESSION_ID, + (u8 *) buf, strlen(buf))) { + printf("Could not add Acct-Session-Id\n"); + goto fail; + } + } else { + radius_msg_make_authenticator(msg, (u8 *) hapd, sizeof(*hapd)); + } + + if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_STATUS_TYPE, + status_type)) { + printf("Could not add Acct-Status-Type\n"); + goto fail; + } + + if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_AUTHENTIC, + hapd->conf->ieee802_1x ? + RADIUS_ACCT_AUTHENTIC_RADIUS : + RADIUS_ACCT_AUTHENTIC_LOCAL)) { + printf("Could not add Acct-Authentic\n"); + goto fail; + } + + if (sta) { + val = ieee802_1x_get_identity(sta->eapol_sm, &len); + if (!val) { + snprintf(buf, sizeof(buf), RADIUS_ADDR_FORMAT, + MAC2STR(sta->addr)); + val = (u8 *) buf; + len = strlen(buf); + } + + if (!radius_msg_add_attr(msg, RADIUS_ATTR_USER_NAME, val, + len)) { + printf("Could not add User-Name\n"); + goto fail; + } + } + + if (hapd->conf->own_ip_addr.af == AF_INET && + !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IP_ADDRESS, + (u8 *) &hapd->conf->own_ip_addr.u.v4, 4)) { + printf("Could not add NAS-IP-Address\n"); + goto fail; + } + +#ifdef CONFIG_IPV6 + if (hapd->conf->own_ip_addr.af == AF_INET6 && + !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IPV6_ADDRESS, + (u8 *) &hapd->conf->own_ip_addr.u.v6, 16)) { + printf("Could not add NAS-IPv6-Address\n"); + goto fail; + } +#endif /* CONFIG_IPV6 */ + + if (hapd->conf->nas_identifier && + !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IDENTIFIER, + (u8 *) hapd->conf->nas_identifier, + strlen(hapd->conf->nas_identifier))) { + printf("Could not add NAS-Identifier\n"); + goto fail; + } + + if (sta && + !radius_msg_add_attr_int32(msg, RADIUS_ATTR_NAS_PORT, sta->aid)) { + printf("Could not add NAS-Port\n"); + goto fail; + } + + snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT ":%s", + MAC2STR(hapd->own_addr), hapd->conf->ssid); + if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLED_STATION_ID, + (u8 *) buf, strlen(buf))) { + printf("Could not add Called-Station-Id\n"); + goto fail; + } + + if (sta) { + snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT, + MAC2STR(sta->addr)); + if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLING_STATION_ID, + (u8 *) buf, strlen(buf))) { + printf("Could not add Calling-Station-Id\n"); + goto fail; + } + + if (!radius_msg_add_attr_int32( + msg, RADIUS_ATTR_NAS_PORT_TYPE, + RADIUS_NAS_PORT_TYPE_IEEE_802_11)) { + printf("Could not add NAS-Port-Type\n"); + goto fail; + } + + snprintf(buf, sizeof(buf), "CONNECT 11Mbps 802.11b"); + if (!radius_msg_add_attr(msg, RADIUS_ATTR_CONNECT_INFO, + (u8 *) buf, strlen(buf))) { + printf("Could not add Connect-Info\n"); + goto fail; + } + + for (i = 0; ; i++) { + val = ieee802_1x_get_radius_class(sta->eapol_sm, &len, + i); + if (val == NULL) + break; + + if (!radius_msg_add_attr(msg, RADIUS_ATTR_CLASS, + val, len)) { + printf("Could not add Class\n"); + goto fail; + } + } + } + + return msg; + + fail: + radius_msg_free(msg); + free(msg); + return NULL; +} + + +static int accounting_sta_update_stats(struct hostapd_data *hapd, + struct sta_info *sta, + struct hostap_sta_driver_data *data) +{ + if (hostapd_read_sta_data(hapd, data, sta->addr)) + return -1; + + if (sta->last_rx_bytes > data->rx_bytes) + sta->acct_input_gigawords++; + if (sta->last_tx_bytes > data->tx_bytes) + sta->acct_output_gigawords++; + sta->last_rx_bytes = data->rx_bytes; + sta->last_tx_bytes = data->tx_bytes; + + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS, + HOSTAPD_LEVEL_DEBUG, "updated TX/RX stats: " + "Acct-Input-Octets=%lu Acct-Input-Gigawords=%u " + "Acct-Output-Octets=%lu Acct-Output-Gigawords=%u", + sta->last_rx_bytes, sta->acct_input_gigawords, + sta->last_tx_bytes, sta->acct_output_gigawords); + + return 0; +} + + +static void accounting_interim_update(void *eloop_ctx, void *timeout_ctx) +{ + hostapd *hapd = eloop_ctx; + struct sta_info *sta = timeout_ctx; + int interval; + + if (sta->acct_interim_interval) { + accounting_sta_interim(hapd, sta); + interval = sta->acct_interim_interval; + } else { + struct hostap_sta_driver_data data; + accounting_sta_update_stats(hapd, sta, &data); + interval = ACCT_DEFAULT_UPDATE_INTERVAL; + } + + eloop_register_timeout(interval, 0, accounting_interim_update, + hapd, sta); +} + + +void accounting_sta_start(hostapd *hapd, struct sta_info *sta) +{ + struct radius_msg *msg; + int interval; + + if (sta->acct_session_started) + return; + + time(&sta->acct_session_start); + sta->last_rx_bytes = sta->last_tx_bytes = 0; + sta->acct_input_gigawords = sta->acct_output_gigawords = 0; + hostapd_sta_clear_stats(hapd, sta->addr); + + if (!hapd->conf->radius->acct_server) + return; + + if (sta->acct_interim_interval) + interval = sta->acct_interim_interval; + else + interval = ACCT_DEFAULT_UPDATE_INTERVAL; + eloop_register_timeout(interval, 0, accounting_interim_update, + hapd, sta); + + msg = accounting_msg(hapd, sta, RADIUS_ACCT_STATUS_TYPE_START); + if (msg) + radius_client_send(hapd->radius, msg, RADIUS_ACCT, sta->addr); + + sta->acct_session_started = 1; +} + + +void accounting_sta_report(hostapd *hapd, struct sta_info *sta, int stop) +{ + struct radius_msg *msg; + int cause = sta->acct_terminate_cause; + struct hostap_sta_driver_data data; + u32 gigawords; + + if (!hapd->conf->radius->acct_server) + return; + + msg = accounting_msg(hapd, sta, + stop ? RADIUS_ACCT_STATUS_TYPE_STOP : + RADIUS_ACCT_STATUS_TYPE_INTERIM_UPDATE); + if (!msg) { + printf("Could not create RADIUS Accounting message\n"); + return; + } + + if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_SESSION_TIME, + time(NULL) - sta->acct_session_start)) { + printf("Could not add Acct-Session-Time\n"); + goto fail; + } + + if (accounting_sta_update_stats(hapd, sta, &data) == 0) { + if (!radius_msg_add_attr_int32(msg, + RADIUS_ATTR_ACCT_INPUT_PACKETS, + data.rx_packets)) { + printf("Could not add Acct-Input-Packets\n"); + goto fail; + } + if (!radius_msg_add_attr_int32(msg, + RADIUS_ATTR_ACCT_OUTPUT_PACKETS, + data.tx_packets)) { + printf("Could not add Acct-Output-Packets\n"); + goto fail; + } + if (!radius_msg_add_attr_int32(msg, + RADIUS_ATTR_ACCT_INPUT_OCTETS, + data.rx_bytes)) { + printf("Could not add Acct-Input-Octets\n"); + goto fail; + } + gigawords = sta->acct_input_gigawords; +#if __WORDSIZE == 64 + gigawords += data.rx_bytes >> 32; +#endif + if (gigawords && + !radius_msg_add_attr_int32( + msg, RADIUS_ATTR_ACCT_INPUT_GIGAWORDS, + gigawords)) { + printf("Could not add Acct-Input-Gigawords\n"); + goto fail; + } + if (!radius_msg_add_attr_int32(msg, + RADIUS_ATTR_ACCT_OUTPUT_OCTETS, + data.tx_bytes)) { + printf("Could not add Acct-Output-Octets\n"); + goto fail; + } + gigawords = sta->acct_output_gigawords; +#if __WORDSIZE == 64 + gigawords += data.tx_bytes >> 32; +#endif + if (gigawords && + !radius_msg_add_attr_int32( + msg, RADIUS_ATTR_ACCT_OUTPUT_GIGAWORDS, + gigawords)) { + printf("Could not add Acct-Output-Gigawords\n"); + goto fail; + } + } + + if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_EVENT_TIMESTAMP, + time(NULL))) { + printf("Could not add Event-Timestamp\n"); + goto fail; + } + + if (eloop_terminated()) + cause = RADIUS_ACCT_TERMINATE_CAUSE_ADMIN_REBOOT; + + if (stop && cause && + !radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_TERMINATE_CAUSE, + cause)) { + printf("Could not add Acct-Terminate-Cause\n"); + goto fail; + } + + radius_client_send(hapd->radius, msg, + stop ? RADIUS_ACCT : RADIUS_ACCT_INTERIM, + sta->addr); + return; + + fail: + radius_msg_free(msg); + free(msg); +} + + +void accounting_sta_interim(hostapd *hapd, struct sta_info *sta) +{ + if (sta->acct_session_started) + accounting_sta_report(hapd, sta, 0); +} + + +void accounting_sta_stop(hostapd *hapd, struct sta_info *sta) +{ + if (sta->acct_session_started) { + accounting_sta_report(hapd, sta, 1); + eloop_cancel_timeout(accounting_interim_update, hapd, sta); + sta->acct_session_started = 0; + } +} + + +void accounting_sta_get_id(struct hostapd_data *hapd, struct sta_info *sta) +{ + sta->acct_session_id_lo = hapd->acct_session_id_lo++; + if (hapd->acct_session_id_lo == 0) { + hapd->acct_session_id_hi++; + } + sta->acct_session_id_hi = hapd->acct_session_id_hi; +} + + +/* Process the RADIUS frames from Accounting Server */ +static RadiusRxResult +accounting_receive(struct radius_msg *msg, struct radius_msg *req, + u8 *shared_secret, size_t shared_secret_len, void *data) +{ + if (msg->hdr->code != RADIUS_CODE_ACCOUNTING_RESPONSE) { + printf("Unknown RADIUS message code\n"); + return RADIUS_RX_UNKNOWN; + } + + if (radius_msg_verify(msg, shared_secret, shared_secret_len, req, 0)) { + printf("Incoming RADIUS packet did not have correct " + "Authenticator - dropped\n"); + return RADIUS_RX_INVALID_AUTHENTICATOR; + } + + return RADIUS_RX_PROCESSED; +} + + +static void accounting_report_state(struct hostapd_data *hapd, int on) +{ + struct radius_msg *msg; + + if (!hapd->conf->radius->acct_server || hapd->radius == NULL) + return; + + /* Inform RADIUS server that accounting will start/stop so that the + * server can close old accounting sessions. */ + msg = accounting_msg(hapd, NULL, + on ? RADIUS_ACCT_STATUS_TYPE_ACCOUNTING_ON : + RADIUS_ACCT_STATUS_TYPE_ACCOUNTING_OFF); + if (!msg) + return; + + if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_TERMINATE_CAUSE, + RADIUS_ACCT_TERMINATE_CAUSE_NAS_REBOOT)) + { + printf("Could not add Acct-Terminate-Cause\n"); + radius_msg_free(msg); + free(msg); + return; + } + + radius_client_send(hapd->radius, msg, RADIUS_ACCT, NULL); +} + + +int accounting_init(hostapd *hapd) +{ + /* Acct-Session-Id should be unique over reboots. If reliable clock is + * not available, this could be replaced with reboot counter, etc. */ + hapd->acct_session_id_hi = time(NULL); + + if (radius_client_register(hapd->radius, RADIUS_ACCT, + accounting_receive, hapd)) + return -1; + + accounting_report_state(hapd, 1); + + return 0; +} + + +void accounting_deinit(hostapd *hapd) +{ + accounting_report_state(hapd, 0); +} diff --git a/contrib/hostapd-0.4.9/accounting.h b/contrib/hostapd-0.4.9/accounting.h new file mode 100644 index 0000000000..8af3eac59c --- /dev/null +++ b/contrib/hostapd-0.4.9/accounting.h @@ -0,0 +1,13 @@ +#ifndef ACCOUNTING_H +#define ACCOUNTING_H + + +void accounting_sta_start(hostapd *hapd, struct sta_info *sta); +void accounting_sta_interim(hostapd *hapd, struct sta_info *sta); +void accounting_sta_stop(hostapd *hapd, struct sta_info *sta); +void accounting_sta_get_id(struct hostapd_data *hapd, struct sta_info *sta); +int accounting_init(hostapd *hapd); +void accounting_deinit(hostapd *hapd); + + +#endif /* ACCOUNTING_H */ diff --git a/contrib/hostapd-0.4.9/aes.c b/contrib/hostapd-0.4.9/aes.c new file mode 100644 index 0000000000..ce94778dd6 --- /dev/null +++ b/contrib/hostapd-0.4.9/aes.c @@ -0,0 +1,1105 @@ +/* + * AES (Rijndael) cipher + * + * Modifications to public domain implementation: + * - support only 128-bit keys + * - cleanup + * - use C pre-processor to make it easier to change S table access + * - added option (AES_SMALL_TABLES) for reducing code size by about 8 kB at + * cost of reduced throughput (quite small difference on Pentium 4, + * 10-25% when using -O1 or -O2 optimization) + * + * Copyright (c) 2003-2005, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published 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 + * @author Antoon Bosselaers + * @author Paulo Barreto + * + * 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 */ +#define AES_SMALL_TABLES + + +/* +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, +}; +#ifndef AES_SMALL_TABLES +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, +}; +#endif /* AES_SMALL_TABLES */ +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, +}; +#ifndef AES_SMALL_TABLES +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 */ +}; +#else /* AES_SMALL_TABLES */ +static const u8 Td4s[256] = { + 0x52U, 0x09U, 0x6aU, 0xd5U, 0x30U, 0x36U, 0xa5U, 0x38U, + 0xbfU, 0x40U, 0xa3U, 0x9eU, 0x81U, 0xf3U, 0xd7U, 0xfbU, + 0x7cU, 0xe3U, 0x39U, 0x82U, 0x9bU, 0x2fU, 0xffU, 0x87U, + 0x34U, 0x8eU, 0x43U, 0x44U, 0xc4U, 0xdeU, 0xe9U, 0xcbU, + 0x54U, 0x7bU, 0x94U, 0x32U, 0xa6U, 0xc2U, 0x23U, 0x3dU, + 0xeeU, 0x4cU, 0x95U, 0x0bU, 0x42U, 0xfaU, 0xc3U, 0x4eU, + 0x08U, 0x2eU, 0xa1U, 0x66U, 0x28U, 0xd9U, 0x24U, 0xb2U, + 0x76U, 0x5bU, 0xa2U, 0x49U, 0x6dU, 0x8bU, 0xd1U, 0x25U, + 0x72U, 0xf8U, 0xf6U, 0x64U, 0x86U, 0x68U, 0x98U, 0x16U, + 0xd4U, 0xa4U, 0x5cU, 0xccU, 0x5dU, 0x65U, 0xb6U, 0x92U, + 0x6cU, 0x70U, 0x48U, 0x50U, 0xfdU, 0xedU, 0xb9U, 0xdaU, + 0x5eU, 0x15U, 0x46U, 0x57U, 0xa7U, 0x8dU, 0x9dU, 0x84U, + 0x90U, 0xd8U, 0xabU, 0x00U, 0x8cU, 0xbcU, 0xd3U, 0x0aU, + 0xf7U, 0xe4U, 0x58U, 0x05U, 0xb8U, 0xb3U, 0x45U, 0x06U, + 0xd0U, 0x2cU, 0x1eU, 0x8fU, 0xcaU, 0x3fU, 0x0fU, 0x02U, + 0xc1U, 0xafU, 0xbdU, 0x03U, 0x01U, 0x13U, 0x8aU, 0x6bU, + 0x3aU, 0x91U, 0x11U, 0x41U, 0x4fU, 0x67U, 0xdcU, 0xeaU, + 0x97U, 0xf2U, 0xcfU, 0xceU, 0xf0U, 0xb4U, 0xe6U, 0x73U, + 0x96U, 0xacU, 0x74U, 0x22U, 0xe7U, 0xadU, 0x35U, 0x85U, + 0xe2U, 0xf9U, 0x37U, 0xe8U, 0x1cU, 0x75U, 0xdfU, 0x6eU, + 0x47U, 0xf1U, 0x1aU, 0x71U, 0x1dU, 0x29U, 0xc5U, 0x89U, + 0x6fU, 0xb7U, 0x62U, 0x0eU, 0xaaU, 0x18U, 0xbeU, 0x1bU, + 0xfcU, 0x56U, 0x3eU, 0x4bU, 0xc6U, 0xd2U, 0x79U, 0x20U, + 0x9aU, 0xdbU, 0xc0U, 0xfeU, 0x78U, 0xcdU, 0x5aU, 0xf4U, + 0x1fU, 0xddU, 0xa8U, 0x33U, 0x88U, 0x07U, 0xc7U, 0x31U, + 0xb1U, 0x12U, 0x10U, 0x59U, 0x27U, 0x80U, 0xecU, 0x5fU, + 0x60U, 0x51U, 0x7fU, 0xa9U, 0x19U, 0xb5U, 0x4aU, 0x0dU, + 0x2dU, 0xe5U, 0x7aU, 0x9fU, 0x93U, 0xc9U, 0x9cU, 0xefU, + 0xa0U, 0xe0U, 0x3bU, 0x4dU, 0xaeU, 0x2aU, 0xf5U, 0xb0U, + 0xc8U, 0xebU, 0xbbU, 0x3cU, 0x83U, 0x53U, 0x99U, 0x61U, + 0x17U, 0x2bU, 0x04U, 0x7eU, 0xbaU, 0x77U, 0xd6U, 0x26U, + 0xe1U, 0x69U, 0x14U, 0x63U, 0x55U, 0x21U, 0x0cU, 0x7dU, +}; +static const u8 rcons[] = { + 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1B, 0x36 + /* for 128-bit blocks, Rijndael never uses more than 10 rcon values */ +}; +#endif /* AES_SMALL_TABLES */ + + +#ifndef AES_SMALL_TABLES + +#define RCON(i) rcon[(i)] + +#define TE0(i) Te0[((i) >> 24) & 0xff] +#define TE1(i) Te1[((i) >> 16) & 0xff] +#define TE2(i) Te2[((i) >> 8) & 0xff] +#define TE3(i) Te3[(i) & 0xff] +#define TE41(i) (Te4[((i) >> 24) & 0xff] & 0xff000000) +#define TE42(i) (Te4[((i) >> 16) & 0xff] & 0x00ff0000) +#define TE43(i) (Te4[((i) >> 8) & 0xff] & 0x0000ff00) +#define TE44(i) (Te4[(i) & 0xff] & 0x000000ff) +#define TE421(i) (Te4[((i) >> 16) & 0xff] & 0xff000000) +#define TE432(i) (Te4[((i) >> 8) & 0xff] & 0x00ff0000) +#define TE443(i) (Te4[(i) & 0xff] & 0x0000ff00) +#define TE414(i) (Te4[((i) >> 24) & 0xff] & 0x000000ff) +#define TE4(i) (Te4[(i)] & 0x000000ff) + +#define TD0(i) Td0[((i) >> 24) & 0xff] +#define TD1(i) Td1[((i) >> 16) & 0xff] +#define TD2(i) Td2[((i) >> 8) & 0xff] +#define TD3(i) Td3[(i) & 0xff] +#define TD41(i) (Td4[((i) >> 24) & 0xff] & 0xff000000) +#define TD42(i) (Td4[((i) >> 16) & 0xff] & 0x00ff0000) +#define TD43(i) (Td4[((i) >> 8) & 0xff] & 0x0000ff00) +#define TD44(i) (Td4[(i) & 0xff] & 0x000000ff) +#define TD0_(i) Td0[(i) & 0xff] +#define TD1_(i) Td1[(i) & 0xff] +#define TD2_(i) Td2[(i) & 0xff] +#define TD3_(i) Td3[(i) & 0xff] + +#else /* AES_SMALL_TABLES */ + +#define RCON(i) (rcons[(i)] << 24) + +static inline u32 rotr(u32 val, int bits) +{ + return (val >> bits) | (val << (32 - bits)); +} + +#define TE0(i) Te0[((i) >> 24) & 0xff] +#define TE1(i) rotr(Te0[((i) >> 16) & 0xff], 8) +#define TE2(i) rotr(Te0[((i) >> 8) & 0xff], 16) +#define TE3(i) rotr(Te0[(i) & 0xff], 24) +#define TE41(i) ((Te0[((i) >> 24) & 0xff] << 8) & 0xff000000) +#define TE42(i) (Te0[((i) >> 16) & 0xff] & 0x00ff0000) +#define TE43(i) (Te0[((i) >> 8) & 0xff] & 0x0000ff00) +#define TE44(i) ((Te0[(i) & 0xff] >> 8) & 0x000000ff) +#define TE421(i) ((Te0[((i) >> 16) & 0xff] << 8) & 0xff000000) +#define TE432(i) (Te0[((i) >> 8) & 0xff] & 0x00ff0000) +#define TE443(i) (Te0[(i) & 0xff] & 0x0000ff00) +#define TE414(i) ((Te0[((i) >> 24) & 0xff] >> 8) & 0x000000ff) +#define TE4(i) ((Te0[(i)] >> 8) & 0x000000ff) + +#define TD0(i) Td0[((i) >> 24) & 0xff] +#define TD1(i) rotr(Td0[((i) >> 16) & 0xff], 8) +#define TD2(i) rotr(Td0[((i) >> 8) & 0xff], 16) +#define TD3(i) rotr(Td0[(i) & 0xff], 24) +#define TD41(i) (Td4s[((i) >> 24) & 0xff] << 24) +#define TD42(i) (Td4s[((i) >> 16) & 0xff] << 16) +#define TD43(i) (Td4s[((i) >> 8) & 0xff] << 8) +#define TD44(i) (Td4s[(i) & 0xff]) +#define TD0_(i) Td0[(i) & 0xff] +#define TD1_(i) rotr(Td0[(i) & 0xff], 8) +#define TD2_(i) rotr(Td0[(i) & 0xff], 16) +#define TD3_(i) rotr(Td0[(i) & 0xff], 24) + +#endif /* AES_SMALL_TABLES */ + +#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] ^ + TE421(temp) ^ TE432(temp) ^ TE443(temp) ^ TE414(temp) ^ + RCON(i); + rk[5] = rk[1] ^ rk[4]; + rk[6] = rk[2] ^ rk[5]; + rk[7] = rk[3] ^ rk[6]; + rk += 4; + } +} + +/** + * 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; + for (j = 0; j < 4; j++) { + rk[j] = TD0_(TE4((rk[j] >> 24) )) ^ + TD1_(TE4((rk[j] >> 16) & 0xff)) ^ + TD2_(TE4((rk[j] >> 8) & 0xff)) ^ + TD3_(TE4((rk[j] ) & 0xff)); + } + } +} + +void rijndaelEncrypt(const u32 rk[/*44*/], const u8 pt[16], u8 ct[16]) +{ + u32 s0, s1, s2, s3, t0, t1, t2, t3; + const int Nr = 10; +#ifndef FULL_UNROLL + int r; +#endif /* ?FULL_UNROLL */ + + /* + * 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]; + +#define ROUND(i,d,s) \ +d##0 = TE0(s##0) ^ TE1(s##1) ^ TE2(s##2) ^ TE3(s##3) ^ rk[4 * i]; \ +d##1 = TE0(s##1) ^ TE1(s##2) ^ TE2(s##3) ^ TE3(s##0) ^ rk[4 * i + 1]; \ +d##2 = TE0(s##2) ^ TE1(s##3) ^ TE2(s##0) ^ TE3(s##1) ^ rk[4 * i + 2]; \ +d##3 = TE0(s##3) ^ TE1(s##0) ^ TE2(s##1) ^ TE3(s##2) ^ rk[4 * i + 3] + +#ifdef FULL_UNROLL + + ROUND(1,t,s); + ROUND(2,s,t); + ROUND(3,t,s); + ROUND(4,s,t); + ROUND(5,t,s); + ROUND(6,s,t); + ROUND(7,t,s); + ROUND(8,s,t); + ROUND(9,t,s); + + rk += Nr << 2; + +#else /* !FULL_UNROLL */ + + /* Nr - 1 full rounds: */ + r = Nr >> 1; + for (;;) { + ROUND(1,t,s); + rk += 8; + if (--r == 0) + break; + ROUND(0,s,t); + } + +#endif /* ?FULL_UNROLL */ + +#undef ROUND + + /* + * apply last round and + * map cipher state to byte array block: + */ + s0 = TE41(t0) ^ TE42(t1) ^ TE43(t2) ^ TE44(t3) ^ rk[0]; + PUTU32(ct , s0); + s1 = TE41(t1) ^ TE42(t2) ^ TE43(t3) ^ TE44(t0) ^ rk[1]; + PUTU32(ct + 4, s1); + s2 = TE41(t2) ^ TE42(t3) ^ TE43(t0) ^ TE44(t1) ^ rk[2]; + PUTU32(ct + 8, s2); + s3 = TE41(t3) ^ TE42(t0) ^ TE43(t1) ^ TE44(t2) ^ 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; + const 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]; + +#define ROUND(i,d,s) \ +d##0 = TD0(s##0) ^ TD1(s##3) ^ TD2(s##2) ^ TD3(s##1) ^ rk[4 * i]; \ +d##1 = TD0(s##1) ^ TD1(s##0) ^ TD2(s##3) ^ TD3(s##2) ^ rk[4 * i + 1]; \ +d##2 = TD0(s##2) ^ TD1(s##1) ^ TD2(s##0) ^ TD3(s##3) ^ rk[4 * i + 2]; \ +d##3 = TD0(s##3) ^ TD1(s##2) ^ TD2(s##1) ^ TD3(s##0) ^ rk[4 * i + 3] + +#ifdef FULL_UNROLL + + ROUND(1,t,s); + ROUND(2,s,t); + ROUND(3,t,s); + ROUND(4,s,t); + ROUND(5,t,s); + ROUND(6,s,t); + ROUND(7,t,s); + ROUND(8,s,t); + ROUND(9,t,s); + + rk += Nr << 2; + +#else /* !FULL_UNROLL */ + + /* Nr - 1 full rounds: */ + r = Nr >> 1; + for (;;) { + ROUND(1,t,s); + rk += 8; + if (--r == 0) + break; + ROUND(0,s,t); + } + +#endif /* ?FULL_UNROLL */ + +#undef ROUND + + /* + * apply last round and + * map cipher state to byte array block: + */ + s0 = TD41(t0) ^ TD42(t3) ^ TD43(t2) ^ TD44(t1) ^ rk[0]; + PUTU32(pt , s0); + s1 = TD41(t1) ^ TD42(t0) ^ TD43(t3) ^ TD44(t2) ^ rk[1]; + PUTU32(pt + 4, s1); + s2 = TD41(t2) ^ TD42(t1) ^ TD43(t0) ^ TD44(t3) ^ rk[2]; + PUTU32(pt + 8, s2); + s3 = TD41(t3) ^ TD42(t2) ^ TD43(t1) ^ TD44(t0) ^ rk[3]; + PUTU32(pt + 12, s3); +} + + + +/* Generic wrapper functions for AES functions */ + +void * aes_encrypt_init(const u8 *key, size_t len) +{ + u32 *rk; + if (len != 16) + return NULL; + rk = malloc(4 * 44); + if (rk == NULL) + return NULL; + rijndaelKeySetupEnc(rk, key); + return rk; +} + + +void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt) +{ + rijndaelEncrypt(ctx, plain, crypt); +} + + +void aes_encrypt_deinit(void *ctx) +{ + free(ctx); +} + + +void * aes_decrypt_init(const u8 *key, size_t len) +{ + u32 *rk; + if (len != 16) + return NULL; + rk = malloc(4 * 44); + if (rk == NULL) + return NULL; + rijndaelKeySetupDec(rk, key); + return rk; +} + + +void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain) +{ + rijndaelDecrypt(ctx, crypt, plain); +} + + +void aes_decrypt_deinit(void *ctx) +{ + free(ctx); +} diff --git a/contrib/hostapd-0.4.9/aes_wrap.c b/contrib/hostapd-0.4.9/aes_wrap.c new file mode 100644 index 0000000000..a5925ca2ec --- /dev/null +++ b/contrib/hostapd-0.4.9/aes_wrap.c @@ -0,0 +1,725 @@ +/* + * AES-based functions + * + * - 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-2005, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include +#include +#include +#include "common.h" +#include "aes_wrap.h" +#include "crypto.h" + +#ifndef EAP_TLS_FUNCS +#include "aes.c" +#endif /* EAP_TLS_FUNCS */ + + +/** + * aes_wrap - Wrap keys with AES Key Wrap Algorithm (128-bit KEK) (RFC3394) + * @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 + * Returns: 0 on success, -1 on failure + */ +int aes_wrap(const u8 *kek, int n, const u8 *plain, u8 *cipher) +{ + u8 *a, *r, b[16]; + int i, j; + void *ctx; + + a = cipher; + r = cipher + 8; + + /* 1) Initialize variables. */ + memset(a, 0xa6, 8); + memcpy(r, plain, 8 * n); + + ctx = aes_encrypt_init(kek, 16); + if (ctx == NULL) + return -1; + + /* 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(ctx, b, b); + memcpy(a, b, 8); + a[7] ^= n * j + i; + memcpy(r, b + 8, 8); + r += 8; + } + } + aes_encrypt_deinit(ctx); + + /* 3) Output the results. + * + * These are already in @cipher due to the location of temporary + * variables. + */ + + return 0; +} + + +/** + * aes_unwrap - Unwrap key with AES Key Wrap Algorithm (128-bit KEK) (RFC3394) + * @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 + * Returns: 0 on success, -1 on failure (e.g., integrity verification failed) + */ +int aes_unwrap(const u8 *kek, int n, const u8 *cipher, u8 *plain) +{ + u8 a[8], *r, b[16]; + int i, j; + void *ctx; + + /* 1) Initialize variables. */ + memcpy(a, cipher, 8); + r = plain; + memcpy(r, cipher + 8, 8 * n); + + ctx = aes_decrypt_init(kek, 16); + if (ctx == NULL) + return -1; + + /* 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(ctx, b, b); + memcpy(a, b, 8); + memcpy(r, b + 8, 8); + r -= 8; + } + } + aes_decrypt_deinit(ctx); + + /* 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; +} + + +/** + * omac1_aes_128 - One-Key CBC MAC (OMAC1) hash with AES-128 + * @key: Key for the hash operation + * @data: Data buffer for which a MAC is determined + * @data: Length of data buffer in bytes + * @mac: Buffer for MAC (128 bits, i.e., 16 bytes) + * Returns: 0 on success, -1 on failure + */ +int omac1_aes_128(const u8 *key, const u8 *data, size_t data_len, u8 *mac) +{ + void *ctx; + u8 cbc[BLOCK_SIZE], pad[BLOCK_SIZE]; + const u8 *pos = data; + int i; + size_t left = data_len; + + ctx = aes_encrypt_init(key, 16); + if (ctx == NULL) + return -1; + 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(ctx, cbc, cbc); + left -= BLOCK_SIZE; + } + + memset(pad, 0, BLOCK_SIZE); + aes_encrypt(ctx, pad, pad); + 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(ctx, pad, mac); + aes_encrypt_deinit(ctx); + return 0; +} + + +/** + * aes_128_encrypt_block - Perform one AES 128-bit block operation + * @key: Key for AES + * @in: Input data (16 bytes) + * @out: Output of the AES block operation (16 bytes) + * Returns: 0 on success, -1 on failure + */ +int aes_128_encrypt_block(const u8 *key, const u8 *in, u8 *out) +{ + void *ctx; + ctx = aes_encrypt_init(key, 16); + if (ctx == NULL) + return -1; + aes_encrypt(ctx, in, out); + aes_encrypt_deinit(ctx); + return 0; +} + + +/** + * aes_128_ctr_encrypt - AES-128 CTR mode encryption + * @key: Key for encryption (16 bytes) + * @nonce: Nonce for counter mode (16 bytes) + * @data: Data to encrypt in-place + * @data_len: Length of data in bytes + * Returns: 0 on success, -1 on failure + */ +int aes_128_ctr_encrypt(const u8 *key, const u8 *nonce, + u8 *data, size_t data_len) +{ + void *ctx; + size_t len, left = data_len; + int i; + u8 *pos = data; + u8 counter[BLOCK_SIZE], buf[BLOCK_SIZE]; + + ctx = aes_encrypt_init(key, 16); + if (ctx == NULL) + return -1; + memcpy(counter, nonce, BLOCK_SIZE); + + while (left > 0) { + aes_encrypt(ctx, counter, buf); + + 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; + } + } + aes_encrypt_deinit(ctx); + return 0; +} + + +/** + * aes_128_eax_encrypt - AES-128 EAX mode encryption + * @key: Key for encryption (16 bytes) + * @nonce: Nonce for counter mode + * @nonce_len: Nonce length in bytes + * @hdr: Header data to be authenticity protected + * @hdr_len: Length of the header data bytes + * @data: Data to encrypt in-place + * @data_len: Length of data in bytes + * @tag: 16-byte tag value + * Returns: 0 on success, -1 on failure + */ +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; +} + + +/** + * aes_128_eax_decrypt - AES-128 EAX mode decryption + * @key: Key for decryption (16 bytes) + * @nonce: Nonce for counter mode + * @nonce_len: Nonce length in bytes + * @hdr: Header data to be authenticity protected + * @hdr_len: Length of the header data bytes + * @data: Data to encrypt in-place + * @data_len: Length of data in bytes + * @tag: 16-byte tag value + * Returns: 0 on success, -1 on failure, -2 if tag does not match + */ +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; +} + + +/** + * aes_128_cbc_encrypt - AES-128 CBC encryption + * @key: Encryption key + * @iv: Encryption IV for CBC mode (16 bytes) + * @data: Data to encrypt in-place + * @data_len: Length of data in bytes (must be divisible by 16) + * Returns: 0 on success, -1 on failure + */ +int aes_128_cbc_encrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len) +{ + void *ctx; + u8 cbc[BLOCK_SIZE]; + u8 *pos = data; + int i, j, blocks; + + ctx = aes_encrypt_init(key, 16); + if (ctx == NULL) + return -1; + 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(ctx, cbc, cbc); + memcpy(pos, cbc, BLOCK_SIZE); + pos += BLOCK_SIZE; + } + aes_encrypt_deinit(ctx); + return 0; +} + + +/** + * aes_128_cbc_decrypt - AES-128 CBC decryption + * @key: Decryption key + * @iv: Decryption IV for CBC mode (16 bytes) + * @data: Data to decrypt in-place + * @data_len: Length of data in bytes (must be divisible by 16) + * Returns: 0 on success, -1 on failure + */ +int aes_128_cbc_decrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len) +{ + void *ctx; + u8 cbc[BLOCK_SIZE], tmp[BLOCK_SIZE]; + u8 *pos = data; + int i, j, blocks; + + ctx = aes_decrypt_init(key, 16); + if (ctx == NULL) + return -1; + memcpy(cbc, iv, BLOCK_SIZE); + + blocks = data_len / BLOCK_SIZE; + for (i = 0; i < blocks; i++) { + memcpy(tmp, pos, BLOCK_SIZE); + aes_decrypt(ctx, pos, pos); + for (j = 0; j < BLOCK_SIZE; j++) + pos[j] ^= cbc[j]; + memcpy(cbc, tmp, BLOCK_SIZE); + pos += BLOCK_SIZE; + } + aes_decrypt_deinit(ctx); + return 0; +} + + +#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; + u8 key[16], pt[16], ct[16]; + void *ctx; + + printf("keySetupEnc:"); + for (i = 0; i < num_iters; i++) { + rdtscll(start); + ctx = aes_encrypt_init(key, 16); + rdtscll(end); + aes_encrypt_deinit(ctx); + printf(" %d", end - start); + } + printf("\n"); + + printf("Encrypt:"); + ctx = aes_encrypt_init(key, 16); + for (i = 0; i < num_iters; i++) { + rdtscll(start); + aes_encrypt(ctx, pt, ct); + rdtscll(end); + printf(" %d", end - start); + } + aes_encrypt_deinit(ctx); + 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; + + if (aes_wrap(kek, 2, plain, result)) { + printf("AES-WRAP-128-128 reported failure\n"); + ret++; + } + if (memcmp(result, crypt, 24) != 0) { + printf("AES-WRAP-128-128 failed\n"); + ret++; + } + if (aes_unwrap(kek, 2, crypt, result)) { + printf("AES-UNWRAP-128-128 reported failure\n"); + ret++; + } + if (memcmp(result, plain, 16) != 0) { + int i; + printf("AES-UNWRAP-128-128 failed\n"); + ret++; + for (i = 0; i < 16; i++) + printf(" %02x", result[i]); + printf("\n"); + } + +#ifdef __i386__ + test_aes_perf(); +#endif /* __i386__ */ + + for (i = 0; i < sizeof(test_vectors) / sizeof(test_vectors[0]); i++) { + tv = &test_vectors[i]; + omac1_aes_128(tv->k, tv->msg, tv->msg_len, result); + if (memcmp(result, tv->tag, 16) != 0) { + printf("OMAC1-AES-128 test vector %d failed\n", i); + ret++; + } + } + + ret += test_eax(); + + ret += test_cbc(); + + if (ret) + printf("FAILED!\n"); + + return ret; +} +#endif /* TEST_MAIN */ diff --git a/contrib/hostapd-0.4.9/aes_wrap.h b/contrib/hostapd-0.4.9/aes_wrap.h new file mode 100644 index 0000000000..cb1a539677 --- /dev/null +++ b/contrib/hostapd-0.4.9/aes_wrap.h @@ -0,0 +1,42 @@ +/* + * AES-based functions + * + * - 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-2005, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef AES_WRAP_H +#define AES_WRAP_H + +int aes_wrap(const u8 *kek, int n, const u8 *plain, u8 *cipher); +int aes_unwrap(const u8 *kek, int n, const u8 *cipher, u8 *plain); +int omac1_aes_128(const u8 *key, const u8 *data, size_t data_len, u8 *mac); +int aes_128_encrypt_block(const u8 *key, const u8 *in, u8 *out); +int 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); +int aes_128_cbc_encrypt(const u8 *key, const u8 *iv, u8 *data, + size_t data_len); +int aes_128_cbc_decrypt(const u8 *key, const u8 *iv, u8 *data, + size_t data_len); + +#endif /* AES_WRAP_H */ diff --git a/contrib/hostapd-0.4.9/ap.h b/contrib/hostapd-0.4.9/ap.h new file mode 100644 index 0000000000..e874ffd2b2 --- /dev/null +++ b/contrib/hostapd-0.4.9/ap.h @@ -0,0 +1,99 @@ +#ifndef AP_H +#define AP_H + +/* STA flags */ +#define WLAN_STA_AUTH BIT(0) +#define WLAN_STA_ASSOC BIT(1) +#define WLAN_STA_PS BIT(2) +#define WLAN_STA_TIM BIT(3) +#define WLAN_STA_PERM BIT(4) +#define WLAN_STA_AUTHORIZED BIT(5) +#define WLAN_STA_PENDING_POLL BIT(6) /* pending activity poll not ACKed */ +#define WLAN_STA_PREAUTH BIT(7) + +#define WLAN_RATE_1M BIT(0) +#define WLAN_RATE_2M BIT(1) +#define WLAN_RATE_5M5 BIT(2) +#define WLAN_RATE_11M BIT(3) +#define WLAN_RATE_COUNT 4 + +/* Maximum size of Supported Rates info element. IEEE 802.11 has a limit of 8, + * but some pre-standard IEEE 802.11g products use longer elements. */ +#define WLAN_SUPP_RATES_MAX 32 + + +struct sta_info { + struct sta_info *next; /* next entry in sta list */ + struct sta_info *hnext; /* next entry in hash table list */ + u8 addr[6]; + u16 aid; /* STA's unique AID (1 .. 2007) or 0 if not yet assigned */ + u32 flags; + u16 capability; + u16 listen_interval; /* or beacon_int for APs */ + u8 supported_rates[WLAN_SUPP_RATES_MAX]; + u8 tx_supp_rates; + + enum { + STA_NULLFUNC = 0, STA_DISASSOC, STA_DEAUTH, STA_REMOVE + } timeout_next; + + /* IEEE 802.1X related data */ + struct eapol_state_machine *eapol_sm; + + /* IEEE 802.11f (IAPP) related data */ + struct ieee80211_mgmt *last_assoc_req; + + u32 acct_session_id_hi; + u32 acct_session_id_lo; + time_t acct_session_start; + int acct_session_started; + int acct_terminate_cause; /* Acct-Terminate-Cause */ + int acct_interim_interval; /* Acct-Interim-Interval */ + + unsigned long last_rx_bytes; + unsigned long last_tx_bytes; + u32 acct_input_gigawords; /* Acct-Input-Gigawords */ + u32 acct_output_gigawords; /* Acct-Output-Gigawords */ + + u8 *challenge; /* IEEE 802.11 Shared Key Authentication Challenge */ + + int pairwise; /* Pairwise cipher suite, WPA_CIPHER_* */ + u8 *wpa_ie; + size_t wpa_ie_len; + struct wpa_state_machine *wpa_sm; + enum { + WPA_VERSION_NO_WPA = 0 /* WPA not used */, + WPA_VERSION_WPA = 1 /* WPA / IEEE 802.11i/D3.0 */, + WPA_VERSION_WPA2 = 2 /* WPA2 / IEEE 802.11i */ + } wpa; + int wpa_key_mgmt; /* the selected WPA_KEY_MGMT_* */ + struct rsn_pmksa_cache *pmksa; + struct rsn_preauth_interface *preauth_iface; + u8 req_replay_counter[8 /* WPA_REPLAY_COUNTER_LEN */]; + int req_replay_counter_used; + u32 dot11RSNAStatsTKIPLocalMICFailures; + u32 dot11RSNAStatsTKIPRemoteMICFailures; +}; + + +#define MAX_STA_COUNT 1024 + +/* Maximum number of AIDs to use for STAs; must be 2007 or lower + * (8802.11 limitation) */ +#define MAX_AID_TABLE_SIZE 128 + +#define STA_HASH_SIZE 256 +#define STA_HASH(sta) (sta[5]) + + +/* Default value for maximum station inactivity. After AP_MAX_INACTIVITY has + * passed since last received frame from the station, a nullfunc data frame is + * sent to the station. If this frame is not acknowledged and no other frames + * have been received, the station will be disassociated after + * AP_DISASSOC_DELAY seconds. Similarily, the station will be deauthenticated + * after AP_DEAUTH_DELAY seconds has passed after disassociation. */ +#define AP_MAX_INACTIVITY (5 * 60) +#define AP_DISASSOC_DELAY (1) +#define AP_DEAUTH_DELAY (1) + +#endif /* AP_H */ diff --git a/contrib/hostapd-0.4.9/common.c b/contrib/hostapd-0.4.9/common.c new file mode 100644 index 0000000000..4b756d8f92 --- /dev/null +++ b/contrib/hostapd-0.4.9/common.c @@ -0,0 +1,388 @@ +/* + * wpa_supplicant/hostapd / common helper functions, etc. + * Copyright (c) 2002-2005, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_NATIVE_WINDOWS +#include +#include +#endif /* CONFIG_NATIVE_WINDOWS */ + +#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 + HCRYPTPROV prov; + BOOL ret; + + if (!CryptAcquireContext(&prov, NULL, NULL, PROV_RSA_FULL, + CRYPT_VERIFYCONTEXT)) + return -1; + + ret = CryptGenRandom(prov, len, buf); + CryptReleaseContext(prov, 0); + + return ret ? 0 : -1; +#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; +} + + +/** + * hwaddr_aton - Convert ASCII string to MAC address + * @txt: MAC address as a string (e.g., "00:11:22:33:44:55") + * @addr: Buffer for the MAC address (ETH_ALEN = 6 bytes) + * Returns: 0 on success, -1 on failure (e.g., string not a MAC address) + */ +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; +} + + +/** + * hexstr2bin - Convert ASCII hex string into binary data + * @hex: ASCII hex string (e.g., "01ab") + * @buf: Buffer for the binary data + * @len: Length of the text to convert in bytes (of buf); hex will be double + * this size + * Returns: 0 on success, -1 on failure (invalid hex string) + */ +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; +} + + +/** + * inc_byte_array - Increment arbitrary length byte array by one + * @counter: Pointer to byte array + * @len: Length of the counter in bytes + * + * This function increments the last byte of the counter by one and continues + * rolling over to more significant bytes if the byte was incremented from + * 0xff to 0x00. + */ +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); +} + + +#ifndef CONFIG_NO_STDOUT_DEBUG + +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); +} + + +/** + * 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, ...) +{ + 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 (buf == NULL) { + printf(" [NULL]"); + } else 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; + } + if (buf == NULL) { + printf("%s - hexdump_ascii(len=%lu): [NULL]\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); +} + +#endif /* CONFIG_NO_STDOUT_DEBUG */ + + +#ifdef CONFIG_NATIVE_WINDOWS + +#define EPOCHFILETIME (116444736000000000ULL) + +int gettimeofday(struct timeval *tv, struct timezone *tz) +{ + FILETIME ft; + LARGE_INTEGER li; + ULONGLONG t; + + GetSystemTimeAsFileTime(&ft); + li.LowPart = ft.dwLowDateTime; + li.HighPart = ft.dwHighDateTime; + t = (li.QuadPart - EPOCHFILETIME) / 10; + tv->tv_sec = (long) (t / 1000000); + tv->tv_usec = (long) (t % 1000000); + + return 0; +} +#endif /* CONFIG_NATIVE_WINDOWS */ diff --git a/contrib/hostapd-0.4.9/common.h b/contrib/hostapd-0.4.9/common.h new file mode 100644 index 0000000000..2ce8a5acfc --- /dev/null +++ b/contrib/hostapd-0.4.9/common.h @@ -0,0 +1,293 @@ +/* + * wpa_supplicant/hostapd / common helper functions, etc. + * Copyright (c) 2002-2005, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef COMMON_H +#define COMMON_H + +#ifdef __linux__ +#include +#include +#endif /* __linux__ */ + +#if defined(__FreeBSD__) || defined(__NetBSD__) +#include +#include +#define __BYTE_ORDER _BYTE_ORDER +#define __LITTLE_ENDIAN _LITTLE_ENDIAN +#define __BIG_ENDIAN _BIG_ENDIAN +#define bswap_16 bswap16 +#define bswap_32 bswap32 +#define bswap_64 bswap64 +#endif /* defined(__FreeBSD__) || defined(__NetBSD__) */ + +#ifdef CONFIG_NATIVE_WINDOWS +#include + +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); + +static inline long int random(void) +{ + return rand(); +} + +typedef int gid_t; +typedef int socklen_t; + +#ifndef MSG_DONTWAIT +#define MSG_DONTWAIT 0 /* not supported */ +#endif + +#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__ */ + +/* Macros for handling unaligned 16-bit variables */ +#define WPA_GET_BE16(a) ((u16) (((a)[0] << 8) | (a)[1])) +#define WPA_PUT_BE16(a, val) \ + do { \ + (a)[0] = ((u16) (val)) >> 8; \ + (a)[1] = ((u16) (val)) & 0xff; \ + } while (0) + +#define WPA_GET_LE16(a) ((u16) (((a)[1] << 8) | (a)[0])) +#define WPA_PUT_LE16(a, val) \ + do { \ + (a)[1] = ((u16) (val)) >> 8; \ + (a)[0] = ((u16) (val)) & 0xff; \ + } while (0) + +#define WPA_GET_BE32(a) ((((u32) (a)[0]) << 24) | (((u32) (a)[1]) << 16) | \ + (((u32) (a)[2]) << 8) | ((u32) (a)[3])) + + +#ifndef ETH_ALEN +#define ETH_ALEN 6 +#endif + +#include +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 }; + +#ifdef CONFIG_NO_STDOUT_DEBUG + +#define wpa_debug_print_timestamp() do { } while (0) +#define wpa_printf(args...) do { } while (0) +#define wpa_hexdump(args...) do { } while (0) +#define wpa_hexdump_key(args...) do { } while (0) +#define wpa_hexdump_ascii(args...) do { } while (0) +#define wpa_hexdump_ascii_key(args...) do { } while (0) + +#else /* CONFIG_NO_STDOUT_DEBUG */ + +/** + * wpa_debug_printf_timestamp - Print timestamp for debug output + * + * This function prints a timestamp in . + * format if debug output has been configured to include timestamps in debug + * messages. + */ +void wpa_debug_print_timestamp(void); + +/** + * 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); + +#endif /* CONFIG_NO_STDOUT_DEBUG */ + + +#ifdef EAPOL_TEST +#define WPA_ASSERT(a) \ + do { \ + if (!(a)) { \ + printf("WPA_ASSERT FAILED '" #a "' " \ + "%s %s:%d\n", \ + __FUNCTION__, __FILE__, __LINE__); \ + exit(1); \ + } \ + } while (0) +#else +#define WPA_ASSERT(a) do { } while (0) +#endif + +#endif /* COMMON_H */ diff --git a/contrib/hostapd-0.4.9/config.c b/contrib/hostapd-0.4.9/config.c new file mode 100644 index 0000000000..1c4f84e46a --- /dev/null +++ b/contrib/hostapd-0.4.9/config.c @@ -0,0 +1,1230 @@ +/* + * Host AP (software wireless LAN access point) user space daemon for + * Host AP kernel driver / Configuration file + * Copyright (c) 2003-2006, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "hostapd.h" +#include "driver.h" +#include "sha1.h" +#include "eap.h" +#include "radius_client.h" +#include "ieee802_1x.h" + + +static struct hostapd_config *hostapd_config_defaults(void) +{ + struct hostapd_config *conf; + + conf = malloc(sizeof(*conf) + sizeof(struct hostapd_radius_servers)); + if (conf == NULL) { + printf("Failed to allocate memory for configuration data.\n"); + return NULL; + } + memset(conf, 0, sizeof(*conf) + sizeof(struct hostapd_radius_servers)); + conf->radius = (struct hostapd_radius_servers *) (conf + 1); + + /* set default driver based on configuration */ + conf->driver = driver_lookup("default"); + if (conf->driver == NULL) { + printf("No default driver registered!\n"); + free(conf); + return NULL; + } + + conf->wep_rekeying_period = 300; + conf->eap_reauth_period = 3600; + + conf->logger_syslog_level = HOSTAPD_LEVEL_INFO; + conf->logger_stdout_level = HOSTAPD_LEVEL_INFO; + conf->logger_syslog = (unsigned int) -1; + conf->logger_stdout = (unsigned int) -1; + + conf->auth_algs = HOSTAPD_AUTH_OPEN | HOSTAPD_AUTH_SHARED_KEY; + conf->eapol_version = EAPOL_VERSION; + + conf->wpa_group_rekey = 600; + conf->wpa_gmk_rekey = 86400; + conf->wpa_key_mgmt = WPA_KEY_MGMT_PSK; + conf->wpa_pairwise = WPA_CIPHER_TKIP; + conf->wpa_group = WPA_CIPHER_TKIP; + + conf->radius_server_auth_port = 1812; + + return conf; +} + + +static int hostapd_parse_ip_addr(const char *txt, struct hostapd_ip_addr *addr) +{ + if (inet_aton(txt, &addr->u.v4)) { + addr->af = AF_INET; + return 0; + } + +#ifdef CONFIG_IPV6 + if (inet_pton(AF_INET6, txt, &addr->u.v6) > 0) { + addr->af = AF_INET6; + return 0; + } +#endif /* CONFIG_IPV6 */ + + return -1; +} + + +static int mac_comp(const void *a, const void *b) +{ + return memcmp(a, b, sizeof(macaddr)); +} + + +static int hostapd_config_read_maclist(const char *fname, macaddr **acl, + int *num) +{ + FILE *f; + char buf[128], *pos; + int line = 0; + u8 addr[ETH_ALEN]; + macaddr *newacl; + + if (!fname) + return 0; + + f = fopen(fname, "r"); + if (!f) { + printf("MAC list file '%s' not found.\n", fname); + return -1; + } + + while (fgets(buf, sizeof(buf), f)) { + line++; + + if (buf[0] == '#') + continue; + pos = buf; + while (*pos != '\0') { + if (*pos == '\n') { + *pos = '\0'; + break; + } + pos++; + } + if (buf[0] == '\0') + continue; + + if (hwaddr_aton(buf, addr)) { + printf("Invalid MAC address '%s' at line %d in '%s'\n", + buf, line, fname); + fclose(f); + return -1; + } + + newacl = (macaddr *) realloc(*acl, (*num + 1) * ETH_ALEN); + if (newacl == NULL) { + printf("MAC list reallocation failed\n"); + fclose(f); + return -1; + } + + *acl = newacl; + memcpy((*acl)[*num], addr, ETH_ALEN); + (*num)++; + } + + fclose(f); + + qsort(*acl, *num, sizeof(macaddr), mac_comp); + + return 0; +} + + +static int hostapd_config_read_wpa_psk(const char *fname, + struct hostapd_config *conf) +{ + FILE *f; + char buf[128], *pos; + int line = 0, ret = 0, len, ok; + u8 addr[ETH_ALEN]; + struct hostapd_wpa_psk *psk; + + if (!fname) + return 0; + + f = fopen(fname, "r"); + if (!f) { + printf("WPA PSK file '%s' not found.\n", fname); + return -1; + } + + while (fgets(buf, sizeof(buf), f)) { + line++; + + if (buf[0] == '#') + continue; + pos = buf; + while (*pos != '\0') { + if (*pos == '\n') { + *pos = '\0'; + break; + } + pos++; + } + if (buf[0] == '\0') + continue; + + if (hwaddr_aton(buf, addr)) { + printf("Invalid MAC address '%s' on line %d in '%s'\n", + buf, line, fname); + ret = -1; + break; + } + + psk = malloc(sizeof(*psk)); + if (psk == NULL) { + printf("WPA PSK allocation failed\n"); + ret = -1; + break; + } + memset(psk, 0, sizeof(*psk)); + if (memcmp(addr, "\x00\x00\x00\x00\x00\x00", ETH_ALEN) == 0) + psk->group = 1; + else + memcpy(psk->addr, addr, ETH_ALEN); + + pos = buf + 17; + if (pos == '\0') { + printf("No PSK on line %d in '%s'\n", line, fname); + free(psk); + ret = -1; + break; + } + pos++; + + ok = 0; + len = strlen(pos); + if (len == 64 && hexstr2bin(pos, psk->psk, PMK_LEN) == 0) + ok = 1; + else if (len >= 8 && len < 64) { + pbkdf2_sha1(pos, conf->ssid, conf->ssid_len, + 4096, psk->psk, PMK_LEN); + ok = 1; + } + if (!ok) { + printf("Invalid PSK '%s' on line %d in '%s'\n", + pos, line, fname); + free(psk); + ret = -1; + break; + } + + psk->next = conf->wpa_psk; + conf->wpa_psk = psk; + } + + fclose(f); + + return ret; +} + + +int hostapd_setup_wpa_psk(struct hostapd_config *conf) +{ + if (conf->wpa_passphrase != NULL) { + if (conf->wpa_psk != NULL) { + printf("Warning: both WPA PSK and passphrase set. " + "Using passphrase.\n"); + free(conf->wpa_psk); + } + conf->wpa_psk = malloc(sizeof(struct hostapd_wpa_psk)); + if (conf->wpa_psk == NULL) { + printf("Unable to alloc space for PSK\n"); + return -1; + } + wpa_hexdump_ascii(MSG_DEBUG, "SSID", + (u8 *) conf->ssid, conf->ssid_len); + wpa_hexdump_ascii(MSG_DEBUG, "PSK (ASCII passphrase)", + (u8 *) conf->wpa_passphrase, + strlen(conf->wpa_passphrase)); + memset(conf->wpa_psk, 0, sizeof(struct hostapd_wpa_psk)); + pbkdf2_sha1(conf->wpa_passphrase, + conf->ssid, conf->ssid_len, + 4096, conf->wpa_psk->psk, PMK_LEN); + wpa_hexdump(MSG_DEBUG, "PSK (from passphrase)", + conf->wpa_psk->psk, PMK_LEN); + conf->wpa_psk->group = 1; + + memset(conf->wpa_passphrase, 0, strlen(conf->wpa_passphrase)); + free(conf->wpa_passphrase); + conf->wpa_passphrase = 0; + } + + if (conf->wpa_psk_file) { + if (hostapd_config_read_wpa_psk(conf->wpa_psk_file, conf)) + return -1; + free(conf->wpa_psk_file); + conf->wpa_psk_file = NULL; + } + + return 0; +} + + +#ifdef EAP_SERVER +static int hostapd_config_read_eap_user(const char *fname, + struct hostapd_config *conf) +{ + FILE *f; + char buf[512], *pos, *start, *pos2; + int line = 0, ret = 0, num_methods; + struct hostapd_eap_user *user, *tail = NULL; + + if (!fname) + return 0; + + f = fopen(fname, "r"); + if (!f) { + printf("EAP user file '%s' not found.\n", fname); + return -1; + } + + /* Lines: "user" METHOD,METHOD2 "password" (password optional) */ + while (fgets(buf, sizeof(buf), f)) { + line++; + + if (buf[0] == '#') + continue; + pos = buf; + while (*pos != '\0') { + if (*pos == '\n') { + *pos = '\0'; + break; + } + pos++; + } + if (buf[0] == '\0') + continue; + + user = NULL; + + if (buf[0] != '"' && buf[0] != '*') { + printf("Invalid EAP identity (no \" in start) on " + "line %d in '%s'\n", line, fname); + goto failed; + } + + user = malloc(sizeof(*user)); + if (user == NULL) { + printf("EAP user allocation failed\n"); + goto failed; + } + memset(user, 0, sizeof(*user)); + user->force_version = -1; + + if (buf[0] == '*') { + pos = buf; + } else { + pos = buf + 1; + start = pos; + while (*pos != '"' && *pos != '\0') + pos++; + if (*pos == '\0') { + printf("Invalid EAP identity (no \" in end) on" + " line %d in '%s'\n", line, fname); + goto failed; + } + + user->identity = malloc(pos - start); + if (user->identity == NULL) { + printf("Failed to allocate memory for EAP " + "identity\n"); + goto failed; + } + memcpy(user->identity, start, pos - start); + user->identity_len = pos - start; + } + pos++; + while (*pos == ' ' || *pos == '\t') + pos++; + + if (*pos == '\0') { + printf("No EAP method on line %d in '%s'\n", + line, fname); + goto failed; + } + + start = pos; + while (*pos != ' ' && *pos != '\t' && *pos != '\0') + pos++; + if (*pos == '\0') { + pos = NULL; + } else { + *pos = '\0'; + pos++; + } + num_methods = 0; + while (*start) { + char *pos2 = strchr(start, ','); + if (pos2) { + *pos2++ = '\0'; + } + user->methods[num_methods] = eap_get_type(start); + if (user->methods[num_methods] == EAP_TYPE_NONE) { + printf("Unsupported EAP type '%s' on line %d " + "in '%s'\n", start, line, fname); + goto failed; + } + + num_methods++; + if (num_methods >= EAP_USER_MAX_METHODS) + break; + if (pos2 == NULL) + break; + start = pos2; + } + if (num_methods == 0) { + printf("No EAP types configured on line %d in '%s'\n", + line, fname); + goto failed; + } + + if (pos == NULL) + goto done; + + while (*pos == ' ' || *pos == '\t') + pos++; + if (*pos == '\0') + goto done; + + if (strncmp(pos, "[ver=0]", 7) == 0) { + user->force_version = 0; + goto done; + } + + if (strncmp(pos, "[ver=1]", 7) == 0) { + user->force_version = 1; + goto done; + } + + if (strncmp(pos, "[2]", 3) == 0) { + user->phase2 = 1; + goto done; + } + + if (*pos == '"') { + pos++; + start = pos; + while (*pos != '"' && *pos != '\0') + pos++; + if (*pos == '\0') { + printf("Invalid EAP password (no \" in end) " + "on line %d in '%s'\n", line, fname); + goto failed; + } + + user->password = malloc(pos - start); + if (user->password == NULL) { + printf("Failed to allocate memory for EAP " + "password\n"); + goto failed; + } + memcpy(user->password, start, pos - start); + user->password_len = pos - start; + + pos++; + } else { + pos2 = pos; + while (*pos2 != '\0' && *pos2 != ' ' && + *pos2 != '\t' && *pos2 != '#') + pos2++; + if ((pos2 - pos) & 1) { + printf("Invalid hex password on line %d in " + "'%s'\n", line, fname); + goto failed; + } + user->password = malloc((pos2 - pos) / 2); + if (user->password == NULL) { + printf("Failed to allocate memory for EAP " + "password\n"); + goto failed; + } + if (hexstr2bin(pos, user->password, + (pos2 - pos) / 2) < 0) { + printf("Invalid hex password on line %d in " + "'%s'\n", line, fname); + goto failed; + } + user->password_len = (pos2 - pos) / 2; + pos = pos2; + } + + while (*pos == ' ' || *pos == '\t') + pos++; + if (strncmp(pos, "[2]", 3) == 0) { + user->phase2 = 1; + } + + done: + if (tail == NULL) { + tail = conf->eap_user = user; + } else { + tail->next = user; + tail = user; + } + continue; + + failed: + if (user) { + free(user->identity); + free(user); + } + ret = -1; + break; + } + + fclose(f); + + return ret; +} +#endif /* EAP_SERVER */ + + +static int +hostapd_config_read_radius_addr(struct hostapd_radius_server **server, + int *num_server, const char *val, int def_port, + struct hostapd_radius_server **curr_serv) +{ + struct hostapd_radius_server *nserv; + int ret; + static int server_index = 1; + + nserv = realloc(*server, (*num_server + 1) * sizeof(*nserv)); + if (nserv == NULL) + return -1; + + *server = nserv; + nserv = &nserv[*num_server]; + (*num_server)++; + (*curr_serv) = nserv; + + memset(nserv, 0, sizeof(*nserv)); + nserv->port = def_port; + ret = hostapd_parse_ip_addr(val, &nserv->addr); + nserv->index = server_index++; + + return ret; +} + + +static int hostapd_config_parse_key_mgmt(int line, const char *value) +{ + int val = 0, last; + char *start, *end, *buf; + + buf = strdup(value); + if (buf == NULL) + return -1; + start = buf; + + while (start != '\0') { + while (*start == ' ' || *start == '\t') + start++; + if (*start == '\0') + break; + end = start; + while (*end != ' ' && *end != '\t' && *end != '\0') + end++; + last = *end == '\0'; + *end = '\0'; + if (strcmp(start, "WPA-PSK") == 0) + val |= WPA_KEY_MGMT_PSK; + else if (strcmp(start, "WPA-EAP") == 0) + val |= WPA_KEY_MGMT_IEEE8021X; + else { + printf("Line %d: invalid key_mgmt '%s'", line, start); + free(buf); + return -1; + } + + if (last) + break; + start = end + 1; + } + + free(buf); + if (val == 0) { + printf("Line %d: no key_mgmt values configured.", line); + return -1; + } + + return val; +} + + +static int hostapd_config_parse_cipher(int line, const char *value) +{ + int val = 0, last; + char *start, *end, *buf; + + buf = strdup(value); + if (buf == NULL) + return -1; + start = buf; + + while (start != '\0') { + while (*start == ' ' || *start == '\t') + start++; + if (*start == '\0') + break; + end = start; + while (*end != ' ' && *end != '\t' && *end != '\0') + end++; + last = *end == '\0'; + *end = '\0'; + if (strcmp(start, "CCMP") == 0) + val |= WPA_CIPHER_CCMP; + else if (strcmp(start, "TKIP") == 0) + val |= WPA_CIPHER_TKIP; + else if (strcmp(start, "WEP104") == 0) + val |= WPA_CIPHER_WEP104; + else if (strcmp(start, "WEP40") == 0) + val |= WPA_CIPHER_WEP40; + else if (strcmp(start, "NONE") == 0) + val |= WPA_CIPHER_NONE; + else { + printf("Line %d: invalid cipher '%s'.", line, start); + free(buf); + return -1; + } + + if (last) + break; + start = end + 1; + } + free(buf); + + if (val == 0) { + printf("Line %d: no cipher values configured.", line); + return -1; + } + return val; +} + + +static int hostapd_config_check(struct hostapd_config *conf) +{ + if (conf->ieee802_1x && !conf->eap_server && + !conf->radius->auth_servers) { + printf("Invalid IEEE 802.1X configuration (no EAP " + "authenticator configured).\n"); + return -1; + } + + if (conf->wpa && (conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK) && + conf->wpa_psk == NULL && conf->wpa_passphrase == NULL && + conf->wpa_psk_file == NULL) { + printf("WPA-PSK enabled, but PSK or passphrase is not " + "configured.\n"); + return -1; + } + + return 0; +} + + +struct hostapd_config * hostapd_config_read(const char *fname) +{ + struct hostapd_config *conf; + FILE *f; + char buf[256], *pos; + int line = 0; + int errors = 0; + char *accept_mac_file = NULL, *deny_mac_file = NULL; +#ifdef EAP_SERVER + char *eap_user_file = NULL; +#endif /* EAP_SERVER */ + + f = fopen(fname, "r"); + if (f == NULL) { + printf("Could not open configuration file '%s' for reading.\n", + fname); + return NULL; + } + + conf = hostapd_config_defaults(); + if (conf == NULL) { + fclose(f); + return NULL; + } + + while (fgets(buf, sizeof(buf), f)) { + line++; + + if (buf[0] == '#') + continue; + pos = buf; + while (*pos != '\0') { + if (*pos == '\n') { + *pos = '\0'; + break; + } + pos++; + } + if (buf[0] == '\0') + continue; + + pos = strchr(buf, '='); + if (pos == NULL) { + printf("Line %d: invalid line '%s'\n", line, buf); + errors++; + continue; + } + *pos = '\0'; + pos++; + + if (strcmp(buf, "interface") == 0) { + snprintf(conf->iface, sizeof(conf->iface), "%s", pos); + } else if (strcmp(buf, "bridge") == 0) { + snprintf(conf->bridge, sizeof(conf->bridge), "%s", + pos); + } else if (strcmp(buf, "driver") == 0) { + conf->driver = driver_lookup(pos); + if (conf->driver == NULL) { + printf("Line %d: invalid/unknown driver " + "'%s'\n", line, pos); + errors++; + } + } else if (strcmp(buf, "debug") == 0) { + conf->debug = atoi(pos); + } else if (strcmp(buf, "logger_syslog_level") == 0) { + conf->logger_syslog_level = atoi(pos); + } else if (strcmp(buf, "logger_stdout_level") == 0) { + conf->logger_stdout_level = atoi(pos); + } else if (strcmp(buf, "logger_syslog") == 0) { + conf->logger_syslog = atoi(pos); + } else if (strcmp(buf, "logger_stdout") == 0) { + conf->logger_stdout = atoi(pos); + } else if (strcmp(buf, "dump_file") == 0) { + conf->dump_log_name = strdup(pos); + } else if (strcmp(buf, "ssid") == 0) { + conf->ssid_len = strlen(pos); + if (conf->ssid_len >= HOSTAPD_SSID_LEN || + conf->ssid_len < 1) { + printf("Line %d: invalid SSID '%s'\n", line, + pos); + errors++; + } + memcpy(conf->ssid, pos, conf->ssid_len); + conf->ssid[conf->ssid_len] = '\0'; + conf->ssid_set = 1; + } else if (strcmp(buf, "macaddr_acl") == 0) { + conf->macaddr_acl = atoi(pos); + if (conf->macaddr_acl != ACCEPT_UNLESS_DENIED && + conf->macaddr_acl != DENY_UNLESS_ACCEPTED && + conf->macaddr_acl != USE_EXTERNAL_RADIUS_AUTH) { + printf("Line %d: unknown macaddr_acl %d\n", + line, conf->macaddr_acl); + } + } else if (strcmp(buf, "accept_mac_file") == 0) { + accept_mac_file = strdup(pos); + if (!accept_mac_file) { + printf("Line %d: allocation failed\n", line); + errors++; + } + } else if (strcmp(buf, "deny_mac_file") == 0) { + deny_mac_file = strdup(pos); + if (!deny_mac_file) { + printf("Line %d: allocation failed\n", line); + errors++; + } + } else if (strcmp(buf, "assoc_ap_addr") == 0) { + if (hwaddr_aton(pos, conf->assoc_ap_addr)) { + printf("Line %d: invalid MAC address '%s'\n", + line, pos); + errors++; + } + conf->assoc_ap = 1; + } else if (strcmp(buf, "ieee8021x") == 0) { + conf->ieee802_1x = atoi(pos); + } else if (strcmp(buf, "eapol_version") == 0) { + conf->eapol_version = atoi(pos); + if (conf->eapol_version < 1 || + conf->eapol_version > 2) { + printf("Line %d: invalid EAPOL " + "version (%d): '%s'.\n", + line, conf->eapol_version, pos); + errors++; + } else + wpa_printf(MSG_DEBUG, "eapol_version=%d", + conf->eapol_version); +#ifdef EAP_SERVER + } else if (strcmp(buf, "eap_authenticator") == 0) { + conf->eap_server = atoi(pos); + printf("Line %d: obsolete eap_authenticator used; " + "this has been renamed to eap_server\n", line); + } else if (strcmp(buf, "eap_server") == 0) { + conf->eap_server = atoi(pos); + } else if (strcmp(buf, "eap_user_file") == 0) { + free(eap_user_file); + eap_user_file = strdup(pos); + if (!eap_user_file) { + printf("Line %d: allocation failed\n", line); + errors++; + } + } else if (strcmp(buf, "ca_cert") == 0) { + free(conf->ca_cert); + conf->ca_cert = strdup(pos); + } else if (strcmp(buf, "server_cert") == 0) { + free(conf->server_cert); + conf->server_cert = strdup(pos); + } else if (strcmp(buf, "private_key") == 0) { + free(conf->private_key); + conf->private_key = strdup(pos); + } else if (strcmp(buf, "private_key_passwd") == 0) { + free(conf->private_key_passwd); + conf->private_key_passwd = strdup(pos); + } else if (strcmp(buf, "check_crl") == 0) { + conf->check_crl = atoi(pos); +#ifdef EAP_SIM + } else if (strcmp(buf, "eap_sim_db") == 0) { + free(conf->eap_sim_db); + conf->eap_sim_db = strdup(pos); +#endif /* EAP_SIM */ +#endif /* EAP_SERVER */ + } else if (strcmp(buf, "eap_message") == 0) { + char *term; + conf->eap_req_id_text = strdup(pos); + if (conf->eap_req_id_text == NULL) { + printf("Line %d: Failed to allocate memory " + "for eap_req_id_text\n", line); + errors++; + continue; + } + conf->eap_req_id_text_len = + strlen(conf->eap_req_id_text); + term = strstr(conf->eap_req_id_text, "\\0"); + if (term) { + *term++ = '\0'; + memmove(term, term + 1, + conf->eap_req_id_text_len - + (term - conf->eap_req_id_text) - 1); + conf->eap_req_id_text_len--; + } + } else if (strcmp(buf, "wep_key_len_broadcast") == 0) { + conf->default_wep_key_len = atoi(pos); + if (conf->default_wep_key_len > 13) { + printf("Line %d: invalid WEP key len %lu " + "(= %lu bits)\n", line, + (unsigned long) + conf->default_wep_key_len, + (unsigned long) + conf->default_wep_key_len * 8); + errors++; + } + } else if (strcmp(buf, "wep_key_len_unicast") == 0) { + conf->individual_wep_key_len = atoi(pos); + if (conf->individual_wep_key_len < 0 || + conf->individual_wep_key_len > 13) { + printf("Line %d: invalid WEP key len %d " + "(= %d bits)\n", line, + conf->individual_wep_key_len, + conf->individual_wep_key_len * 8); + errors++; + } + } else if (strcmp(buf, "wep_rekey_period") == 0) { + conf->wep_rekeying_period = atoi(pos); + if (conf->wep_rekeying_period < 0) { + printf("Line %d: invalid period %d\n", + line, conf->wep_rekeying_period); + errors++; + } + } else if (strcmp(buf, "eap_reauth_period") == 0) { + conf->eap_reauth_period = atoi(pos); + if (conf->eap_reauth_period < 0) { + printf("Line %d: invalid period %d\n", + line, conf->eap_reauth_period); + errors++; + } + } else if (strcmp(buf, "eapol_key_index_workaround") == 0) { + conf->eapol_key_index_workaround = atoi(pos); +#ifdef CONFIG_IAPP + } else if (strcmp(buf, "iapp_interface") == 0) { + conf->ieee802_11f = 1; + snprintf(conf->iapp_iface, sizeof(conf->iapp_iface), + "%s", pos); +#endif /* CONFIG_IAPP */ + } else if (strcmp(buf, "own_ip_addr") == 0) { + if (hostapd_parse_ip_addr(pos, &conf->own_ip_addr)) { + printf("Line %d: invalid IP address '%s'\n", + line, pos); + errors++; + } + } else if (strcmp(buf, "nas_identifier") == 0) { + conf->nas_identifier = strdup(pos); + } else if (strcmp(buf, "auth_server_addr") == 0) { + if (hostapd_config_read_radius_addr( + &conf->radius->auth_servers, + &conf->radius->num_auth_servers, pos, 1812, + &conf->radius->auth_server)) { + printf("Line %d: invalid IP address '%s'\n", + line, pos); + errors++; + } + } else if (conf->radius->auth_server && + strcmp(buf, "auth_server_port") == 0) { + conf->radius->auth_server->port = atoi(pos); + } else if (conf->radius->auth_server && + strcmp(buf, "auth_server_shared_secret") == 0) { + int len = strlen(pos); + if (len == 0) { + /* RFC 2865, Ch. 3 */ + printf("Line %d: empty shared secret is not " + "allowed.\n", line); + errors++; + } + conf->radius->auth_server->shared_secret = + (u8 *) strdup(pos); + conf->radius->auth_server->shared_secret_len = len; + } else if (strcmp(buf, "acct_server_addr") == 0) { + if (hostapd_config_read_radius_addr( + &conf->radius->acct_servers, + &conf->radius->num_acct_servers, pos, 1813, + &conf->radius->acct_server)) { + printf("Line %d: invalid IP address '%s'\n", + line, pos); + errors++; + } + } else if (conf->radius->acct_server && + strcmp(buf, "acct_server_port") == 0) { + conf->radius->acct_server->port = atoi(pos); + } else if (conf->radius->acct_server && + strcmp(buf, "acct_server_shared_secret") == 0) { + int len = strlen(pos); + if (len == 0) { + /* RFC 2865, Ch. 3 */ + printf("Line %d: empty shared secret is not " + "allowed.\n", line); + errors++; + } + conf->radius->acct_server->shared_secret = + (u8 *) strdup(pos); + conf->radius->acct_server->shared_secret_len = len; + } else if (strcmp(buf, "radius_retry_primary_interval") == 0) { + conf->radius->retry_primary_interval = atoi(pos); + } else if (strcmp(buf, "radius_acct_interim_interval") == 0) { + conf->radius->acct_interim_interval = atoi(pos); + } else if (strcmp(buf, "auth_algs") == 0) { + conf->auth_algs = atoi(pos); + if (conf->auth_algs == 0) { + printf("Line %d: no authentication algorithms " + "allowed\n", + line); + errors++; + } + } else if (strcmp(buf, "wpa") == 0) { + conf->wpa = atoi(pos); + } else if (strcmp(buf, "wpa_group_rekey") == 0) { + conf->wpa_group_rekey = atoi(pos); + } else if (strcmp(buf, "wpa_strict_rekey") == 0) { + conf->wpa_strict_rekey = atoi(pos); + } else if (strcmp(buf, "wpa_gmk_rekey") == 0) { + conf->wpa_gmk_rekey = atoi(pos); + } else if (strcmp(buf, "wpa_passphrase") == 0) { + int len = strlen(pos); + if (len < 8 || len > 63) { + printf("Line %d: invalid WPA passphrase length" + " %d (expected 8..63)\n", line, len); + errors++; + } else { + free(conf->wpa_passphrase); + conf->wpa_passphrase = strdup(pos); + } + } else if (strcmp(buf, "wpa_psk") == 0) { + free(conf->wpa_psk); + conf->wpa_psk = malloc(sizeof(struct hostapd_wpa_psk)); + if (conf->wpa_psk) { + memset(conf->wpa_psk, 0, + sizeof(struct hostapd_wpa_psk)); + } + if (conf->wpa_psk == NULL) + errors++; + else if (hexstr2bin(pos, conf->wpa_psk->psk, PMK_LEN) + || pos[PMK_LEN * 2] != '\0') { + printf("Line %d: Invalid PSK '%s'.\n", line, + pos); + errors++; + } else { + conf->wpa_psk->group = 1; + } + } else if (strcmp(buf, "wpa_psk_file") == 0) { + free(conf->wpa_psk_file); + conf->wpa_psk_file = strdup(pos); + if (!conf->wpa_psk_file) { + printf("Line %d: allocation failed\n", line); + errors++; + } + } else if (strcmp(buf, "wpa_key_mgmt") == 0) { + conf->wpa_key_mgmt = + hostapd_config_parse_key_mgmt(line, pos); + if (conf->wpa_key_mgmt == -1) + errors++; + } else if (strcmp(buf, "wpa_pairwise") == 0) { + conf->wpa_pairwise = + hostapd_config_parse_cipher(line, pos); + if (conf->wpa_pairwise == -1 || + conf->wpa_pairwise == 0) + errors++; + else if (conf->wpa_pairwise & + (WPA_CIPHER_NONE | WPA_CIPHER_WEP40 | + WPA_CIPHER_WEP104)) { + printf("Line %d: unsupported pairwise " + "cipher suite '%s'\n", + conf->wpa_pairwise, pos); + errors++; + } else { + if (conf->wpa_pairwise & WPA_CIPHER_TKIP) + conf->wpa_group = WPA_CIPHER_TKIP; + else + conf->wpa_group = WPA_CIPHER_CCMP; + } +#ifdef CONFIG_RSN_PREAUTH + } else if (strcmp(buf, "rsn_preauth") == 0) { + conf->rsn_preauth = atoi(pos); + } else if (strcmp(buf, "rsn_preauth_interfaces") == 0) { + conf->rsn_preauth_interfaces = strdup(pos); +#endif /* CONFIG_RSN_PREAUTH */ + } else if (strcmp(buf, "ctrl_interface") == 0) { + free(conf->ctrl_interface); + conf->ctrl_interface = strdup(pos); + } else if (strcmp(buf, "ctrl_interface_group") == 0) { + struct group *grp; + char *endp; + const char *group = pos; + + grp = getgrnam(group); + if (grp) { + conf->ctrl_interface_gid = grp->gr_gid; + conf->ctrl_interface_gid_set = 1; + wpa_printf(MSG_DEBUG, "ctrl_interface_group=%d" + " (from group name '%s')", + conf->ctrl_interface_gid, group); + continue; + } + + /* Group name not found - try to parse this as gid */ + conf->ctrl_interface_gid = strtol(group, &endp, 10); + if (*group == '\0' || *endp != '\0') { + wpa_printf(MSG_DEBUG, "Line %d: Invalid group " + "'%s'", line, group); + errors++; + continue; + } + conf->ctrl_interface_gid_set = 1; + wpa_printf(MSG_DEBUG, "ctrl_interface_group=%d", + conf->ctrl_interface_gid); +#ifdef RADIUS_SERVER + } else if (strcmp(buf, "radius_server_clients") == 0) { + free(conf->radius_server_clients); + conf->radius_server_clients = strdup(pos); + } else if (strcmp(buf, "radius_server_auth_port") == 0) { + conf->radius_server_auth_port = atoi(pos); + } else if (strcmp(buf, "radius_server_ipv6") == 0) { + conf->radius_server_ipv6 = atoi(pos); +#endif /* RADIUS_SERVER */ + } else if (strcmp(buf, "test_socket") == 0) { + free(conf->test_socket); + conf->test_socket = strdup(pos); + } else if (strcmp(buf, "use_pae_group_addr") == 0) { + conf->use_pae_group_addr = atoi(pos); + } else { + printf("Line %d: unknown configuration item '%s'\n", + line, buf); + errors++; + } + } + + fclose(f); + + if (hostapd_config_read_maclist(accept_mac_file, &conf->accept_mac, + &conf->num_accept_mac)) + errors++; + free(accept_mac_file); + if (hostapd_config_read_maclist(deny_mac_file, &conf->deny_mac, + &conf->num_deny_mac)) + errors++; + free(deny_mac_file); + +#ifdef EAP_SERVER + if (hostapd_config_read_eap_user(eap_user_file, conf)) + errors++; + free(eap_user_file); +#endif /* EAP_SERVER */ + + conf->radius->auth_server = conf->radius->auth_servers; + conf->radius->acct_server = conf->radius->acct_servers; + + if (hostapd_config_check(conf)) + errors++; + + if (errors) { + printf("%d errors found in configuration file '%s'\n", + errors, fname); + hostapd_config_free(conf); + conf = NULL; + } + + return conf; +} + + +static void hostapd_config_free_radius(struct hostapd_radius_server *servers, + int num_servers) +{ + int i; + + for (i = 0; i < num_servers; i++) { + free(servers[i].shared_secret); + } + free(servers); +} + + +static void hostapd_config_free_eap_user(struct hostapd_eap_user *user) +{ + free(user->identity); + free(user->password); + free(user); +} + + +void hostapd_config_free(struct hostapd_config *conf) +{ + struct hostapd_wpa_psk *psk, *prev; + struct hostapd_eap_user *user, *prev_user; + + if (conf == NULL) + return; + + psk = conf->wpa_psk; + while (psk) { + prev = psk; + psk = psk->next; + free(prev); + } + + free(conf->wpa_passphrase); + free(conf->wpa_psk_file); + + user = conf->eap_user; + while (user) { + prev_user = user; + user = user->next; + hostapd_config_free_eap_user(prev_user); + } + + free(conf->dump_log_name); + free(conf->eap_req_id_text); + free(conf->accept_mac); + free(conf->deny_mac); + free(conf->nas_identifier); + hostapd_config_free_radius(conf->radius->auth_servers, + conf->radius->num_auth_servers); + hostapd_config_free_radius(conf->radius->acct_servers, + conf->radius->num_acct_servers); + free(conf->rsn_preauth_interfaces); + free(conf->ctrl_interface); + free(conf->ca_cert); + free(conf->server_cert); + free(conf->private_key); + free(conf->private_key_passwd); + free(conf->eap_sim_db); + free(conf->radius_server_clients); + free(conf->test_socket); + free(conf); +} + + +/* Perform a binary search for given MAC address from a pre-sorted list. + * Returns 1 if address is in the list or 0 if not. */ +int hostapd_maclist_found(macaddr *list, int num_entries, u8 *addr) +{ + int start, end, middle, res; + + start = 0; + end = num_entries - 1; + + while (start <= end) { + middle = (start + end) / 2; + res = memcmp(list[middle], addr, ETH_ALEN); + if (res == 0) + return 1; + if (res < 0) + start = middle + 1; + else + end = middle - 1; + } + + return 0; +} + + +const u8 * hostapd_get_psk(const struct hostapd_config *conf, const u8 *addr, + const u8 *prev_psk) +{ + struct hostapd_wpa_psk *psk; + int next_ok = prev_psk == NULL; + + for (psk = conf->wpa_psk; psk != NULL; psk = psk->next) { + if (next_ok && + (psk->group || memcmp(psk->addr, addr, ETH_ALEN) == 0)) + return psk->psk; + + if (psk->psk == prev_psk) + next_ok = 1; + } + + return NULL; +} + + +const struct hostapd_eap_user * +hostapd_get_eap_user(const struct hostapd_config *conf, const u8 *identity, + size_t identity_len, int phase2) +{ + struct hostapd_eap_user *user = conf->eap_user; + + while (user) { + if (!phase2 && user->identity == NULL) { + /* Wildcard match */ + break; + } + if (user->phase2 == !!phase2 && + user->identity_len == identity_len && + memcmp(user->identity, identity, identity_len) == 0) + break; + user = user->next; + } + + return user; +} diff --git a/contrib/hostapd-0.4.9/config.h b/contrib/hostapd-0.4.9/config.h new file mode 100644 index 0000000000..df411a5621 --- /dev/null +++ b/contrib/hostapd-0.4.9/config.h @@ -0,0 +1,160 @@ +#ifndef CONFIG_H +#define CONFIG_H + +#include "config_types.h" + +typedef u8 macaddr[ETH_ALEN]; + +struct hostapd_radius_servers; + +#define PMK_LEN 32 +struct hostapd_wpa_psk { + struct hostapd_wpa_psk *next; + int group; + u8 psk[PMK_LEN]; + u8 addr[ETH_ALEN]; +}; + +#define EAP_USER_MAX_METHODS 8 +struct hostapd_eap_user { + struct hostapd_eap_user *next; + u8 *identity; + size_t identity_len; + u8 methods[EAP_USER_MAX_METHODS]; + u8 *password; + size_t password_len; + int phase2; + int force_version; +}; + +struct hostapd_config { + char iface[IFNAMSIZ + 1]; + char bridge[IFNAMSIZ + 1]; + + const struct driver_ops *driver; + + enum { + HOSTAPD_LEVEL_DEBUG_VERBOSE = 0, + HOSTAPD_LEVEL_DEBUG = 1, + HOSTAPD_LEVEL_INFO = 2, + HOSTAPD_LEVEL_NOTICE = 3, + HOSTAPD_LEVEL_WARNING = 4 + } logger_syslog_level, logger_stdout_level; + +#define HOSTAPD_MODULE_IEEE80211 BIT(0) +#define HOSTAPD_MODULE_IEEE8021X BIT(1) +#define HOSTAPD_MODULE_RADIUS BIT(2) +#define HOSTAPD_MODULE_WPA BIT(3) +#define HOSTAPD_MODULE_DRIVER BIT(4) +#define HOSTAPD_MODULE_IAPP BIT(5) + unsigned int logger_syslog; /* module bitfield */ + unsigned int logger_stdout; /* module bitfield */ + + enum { HOSTAPD_DEBUG_NO = 0, HOSTAPD_DEBUG_MINIMAL = 1, + HOSTAPD_DEBUG_VERBOSE = 2, + HOSTAPD_DEBUG_MSGDUMPS = 3, + HOSTAPD_DEBUG_EXCESSIVE = 4 } debug; /* debug verbosity level */ + char *dump_log_name; /* file name for state dump (SIGUSR1) */ + + int ieee802_1x; /* use IEEE 802.1X */ + int eapol_version; + int eap_server; /* Use internal EAP server instead of external + * RADIUS server */ + struct hostapd_eap_user *eap_user; + char *eap_sim_db; + struct hostapd_ip_addr own_ip_addr; + char *nas_identifier; + struct hostapd_radius_servers *radius; + +#define HOSTAPD_SSID_LEN 32 + char ssid[HOSTAPD_SSID_LEN + 1]; + size_t ssid_len; + int ssid_set; + char *eap_req_id_text; /* optional displayable message sent with + * EAP Request-Identity */ + size_t eap_req_id_text_len; + int eapol_key_index_workaround; + + size_t default_wep_key_len; + int individual_wep_key_len; + int wep_rekeying_period; + int eap_reauth_period; + + int ieee802_11f; /* use IEEE 802.11f (IAPP) */ + char iapp_iface[IFNAMSIZ + 1]; /* interface used with IAPP broadcast + * frames */ + + u8 assoc_ap_addr[ETH_ALEN]; + int assoc_ap; /* whether assoc_ap_addr is set */ + + enum { + ACCEPT_UNLESS_DENIED = 0, + DENY_UNLESS_ACCEPTED = 1, + USE_EXTERNAL_RADIUS_AUTH = 2 + } macaddr_acl; + macaddr *accept_mac; + int num_accept_mac; + macaddr *deny_mac; + int num_deny_mac; + +#define HOSTAPD_AUTH_OPEN BIT(0) +#define HOSTAPD_AUTH_SHARED_KEY BIT(1) + int auth_algs; /* bitfield of allowed IEEE 802.11 authentication + * algorithms */ + +#define HOSTAPD_WPA_VERSION_WPA BIT(0) +#define HOSTAPD_WPA_VERSION_WPA2 BIT(1) + int wpa; + struct hostapd_wpa_psk *wpa_psk; + char *wpa_passphrase; + char *wpa_psk_file; +#define WPA_KEY_MGMT_IEEE8021X BIT(0) +#define WPA_KEY_MGMT_PSK BIT(1) + int wpa_key_mgmt; +#define WPA_CIPHER_NONE BIT(0) +#define WPA_CIPHER_WEP40 BIT(1) +#define WPA_CIPHER_WEP104 BIT(2) +#define WPA_CIPHER_TKIP BIT(3) +#define WPA_CIPHER_CCMP BIT(4) + int wpa_pairwise; + int wpa_group; + int wpa_group_rekey; + int wpa_strict_rekey; + int wpa_gmk_rekey; + int rsn_preauth; + char *rsn_preauth_interfaces; + + char *ctrl_interface; /* directory for UNIX domain sockets */ + gid_t ctrl_interface_gid; + int ctrl_interface_gid_set; + + char *ca_cert; + char *server_cert; + char *private_key; + char *private_key_passwd; + int check_crl; + + char *radius_server_clients; + int radius_server_auth_port; + int radius_server_ipv6; + + char *test_socket; /* UNIX domain socket path for driver_test */ + + int use_pae_group_addr; /* Whether to send EAPOL frames to PAE group + * address instead of individual address + * (for driver_wired.c). + */ +}; + + +struct hostapd_config * hostapd_config_read(const char *fname); +void hostapd_config_free(struct hostapd_config *conf); +int hostapd_maclist_found(macaddr *list, int num_entries, u8 *addr); +const u8 * hostapd_get_psk(const struct hostapd_config *conf, const u8 *addr, + const u8 *prev_psk); +int hostapd_setup_wpa_psk(struct hostapd_config *conf); +const struct hostapd_eap_user * +hostapd_get_eap_user(const struct hostapd_config *conf, const u8 *identity, + size_t identity_len, int phase2); + +#endif /* CONFIG_H */ diff --git a/contrib/hostapd-0.4.9/config_types.h b/contrib/hostapd-0.4.9/config_types.h new file mode 100644 index 0000000000..12b57cb4ac --- /dev/null +++ b/contrib/hostapd-0.4.9/config_types.h @@ -0,0 +1,14 @@ +#ifndef CONFIG_TYPES_H +#define CONFIG_TYPES_H + +struct hostapd_ip_addr { + union { + struct in_addr v4; +#ifdef CONFIG_IPV6 + struct in6_addr v6; +#endif /* CONFIG_IPV6 */ + } u; + int af; /* AF_INET / AF_INET6 */ +}; + +#endif /* CONFIG_TYPES_H */ diff --git a/contrib/hostapd-0.4.9/crypto.c b/contrib/hostapd-0.4.9/crypto.c new file mode 100644 index 0000000000..1b136711d1 --- /dev/null +++ b/contrib/hostapd-0.4.9/crypto.c @@ -0,0 +1,157 @@ +/* + * WPA Supplicant / wrapper functions for libcrypto + * Copyright (c) 2004-2005, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "common.h" +#include "crypto.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[], const 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 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); +} + + +#ifdef EAP_TLS_FUNCS +void md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +{ + MD5_CTX ctx; + int i; + + MD5_Init(&ctx); + for (i = 0; i < num_elem; i++) + MD5_Update(&ctx, addr[i], len[i]); + MD5_Final(mac, &ctx); +} + + +void sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +{ + SHA_CTX ctx; + int i; + + SHA1_Init(&ctx); + for (i = 0; i < num_elem; i++) + SHA1_Update(&ctx, addr[i], len[i]); + SHA1_Final(mac, &ctx); +} + + +void sha1_transform(u8 *state, const u8 data[64]) +{ + SHA_CTX context; + memset(&context, 0, sizeof(context)); + memcpy(&context.h0, state, 5 * 4); + SHA1_Transform(&context, data); + memcpy(state, &context.h0, 5 * 4); +} + + +void * aes_encrypt_init(const u8 *key, size_t len) +{ + AES_KEY *ak; + ak = malloc(sizeof(*ak)); + if (ak == NULL) + return NULL; + if (AES_set_encrypt_key(key, 8 * len, ak) < 0) { + free(ak); + return NULL; + } + return ak; +} + + +void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt) +{ + AES_encrypt(plain, crypt, ctx); +} + + +void aes_encrypt_deinit(void *ctx) +{ + free(ctx); +} + + +void * aes_decrypt_init(const u8 *key, size_t len) +{ + AES_KEY *ak; + ak = malloc(sizeof(*ak)); + if (ak == NULL) + return NULL; + if (AES_set_decrypt_key(key, 8 * len, ak) < 0) { + free(ak); + return NULL; + } + return ak; +} + + +void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain) +{ + AES_decrypt(crypt, plain, ctx); +} + + +void aes_decrypt_deinit(void *ctx) +{ + free(ctx); +} +#endif /* EAP_TLS_FUNCS */ diff --git a/contrib/hostapd-0.4.9/crypto.h b/contrib/hostapd-0.4.9/crypto.h new file mode 100644 index 0000000000..e664861afe --- /dev/null +++ b/contrib/hostapd-0.4.9/crypto.h @@ -0,0 +1,123 @@ +/* + * WPA Supplicant / wrapper functions for crypto libraries + * Copyright (c) 2004-2005, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + * + * This file defines the cryptographic functions that need to be implemented + * for wpa_supplicant and hostapd. When TLS is not used, internal + * implementation of MD5, SHA1, and AES is used and no external libraries are + * required. When TLS is enabled (e.g., by enabling EAP-TLS or EAP-PEAP), the + * crypto library used by the TLS implementation is expected to be used for + * non-TLS needs, too, in order to save space by not implementing these + * functions twice. + * + * Wrapper code for using each crypto library is in its own file (crypto*.c) + * and one of these files is build and linked in to provide the functions + * defined here. + */ + +#ifndef CRYPTO_H +#define CRYPTO_H + +/** + * md4_vector - MD4 hash for data vector + * @num_elem: Number of elements in the data vector + * @addr: Pointers to the data areas + * @len: Lengths of the data blocks + * @mac: Buffer for the hash + */ +void md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac); + +/** + * md5_vector - MD5 hash for data vector + * @num_elem: Number of elements in the data vector + * @addr: Pointers to the data areas + * @len: Lengths of the data blocks + * @mac: Buffer for the hash + */ +void md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac); + +/** + * sha1_vector - SHA-1 hash for data vector + * @num_elem: Number of elements in the data vector + * @addr: Pointers to the data areas + * @len: Lengths of the data blocks + * @mac: Buffer for the hash + */ +void sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, + u8 *mac); + +/** + * sha1_transform - Perform one SHA-1 transform step + * @state: SHA-1 state + * @data: Input data for the SHA-1 transform + * + * This function is used to implement random number generation specified in + * NIST FIPS Publication 186-2 for EAP-SIM. This PRF uses a function that is + * similar to SHA-1, but has different message padding and as such, access to + * just part of the SHA-1 is needed. + */ +void sha1_transform(u8 *state, const u8 data[64]); + +/** + * des_encrypt - Encrypt one block with DES + * @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); + +/** + * aes_encrypt_init - Initialize AES for encryption + * @key: Encryption key + * @len: Key length in bytes (usually 16, i.e., 128 bits) + * Returns: Pointer to context data or %NULL on failure + */ +void * aes_encrypt_init(const u8 *key, size_t len); + +/** + * aes_encrypt - Encrypt one AES block + * @ctx: Context pointer from aes_encrypt_init() + * @plain: Plaintext data to be encrypted (16 bytes) + * @crypt: Buffer for the encrypted data (16 bytes) + */ +void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt); + +/** + * aes_encrypt_deinit - Deinitialize AES encryption + * @ctx: Context pointer from aes_encrypt_init() + */ +void aes_encrypt_deinit(void *ctx); + +/** + * aes_decrypt_init - Initialize AES for decryption + * @key: Decryption key + * @len: Key length in bytes (usually 16, i.e., 128 bits) + * Returns: Pointer to context data or %NULL on failure + */ +void * aes_decrypt_init(const u8 *key, size_t len); + +/** + * aes_decrypt - Decrypt one AES block + * @ctx: Context pointer from aes_encrypt_init() + * @crypt: Encrypted data (16 bytes) + * @plain: Buffer for the decrypted data (16 bytes) + */ +void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain); + +/** + * aes_decrypt_deinit - Deinitialize AES decryption + * @ctx: Context pointer from aes_encrypt_init() + */ +void aes_decrypt_deinit(void *ctx); + + +#endif /* CRYPTO_H */ diff --git a/contrib/hostapd-0.4.9/ctrl_iface.c b/contrib/hostapd-0.4.9/ctrl_iface.c new file mode 100644 index 0000000000..ff730d4a95 --- /dev/null +++ b/contrib/hostapd-0.4.9/ctrl_iface.c @@ -0,0 +1,456 @@ +/* + * Host AP (software wireless LAN access point) user space daemon for + * Host AP kernel driver / UNIX domain socket -based control interface + * Copyright (c) 2004, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "hostapd.h" +#include "eloop.h" +#include "config.h" +#include "eapol_sm.h" +#include "ieee802_1x.h" +#include "wpa.h" +#include "radius_client.h" +#include "ieee802_11.h" +#include "ctrl_iface.h" +#include "sta_info.h" + + +struct wpa_ctrl_dst { + struct wpa_ctrl_dst *next; + struct sockaddr_un addr; + socklen_t addrlen; + int debug_level; + int errors; +}; + + +static int hostapd_ctrl_iface_attach(struct hostapd_data *hapd, + struct sockaddr_un *from, + socklen_t fromlen) +{ + struct wpa_ctrl_dst *dst; + + dst = malloc(sizeof(*dst)); + if (dst == NULL) + return -1; + memset(dst, 0, sizeof(*dst)); + memcpy(&dst->addr, from, sizeof(struct sockaddr_un)); + dst->addrlen = fromlen; + dst->debug_level = MSG_INFO; + dst->next = hapd->ctrl_dst; + hapd->ctrl_dst = dst; + wpa_hexdump(MSG_DEBUG, "CTRL_IFACE monitor attached", + (u8 *) from->sun_path, fromlen); + return 0; +} + + +static int hostapd_ctrl_iface_detach(struct hostapd_data *hapd, + struct sockaddr_un *from, + socklen_t fromlen) +{ + struct wpa_ctrl_dst *dst, *prev = NULL; + + dst = hapd->ctrl_dst; + while (dst) { + if (fromlen == dst->addrlen && + memcmp(from->sun_path, dst->addr.sun_path, fromlen) == 0) { + if (prev == NULL) + hapd->ctrl_dst = dst->next; + else + prev->next = dst->next; + free(dst); + wpa_hexdump(MSG_DEBUG, "CTRL_IFACE monitor detached", + (u8 *) from->sun_path, fromlen); + return 0; + } + prev = dst; + dst = dst->next; + } + return -1; +} + + +static int hostapd_ctrl_iface_level(struct hostapd_data *hapd, + struct sockaddr_un *from, + socklen_t fromlen, + char *level) +{ + struct wpa_ctrl_dst *dst; + + wpa_printf(MSG_DEBUG, "CTRL_IFACE LEVEL %s", level); + + dst = hapd->ctrl_dst; + while (dst) { + if (fromlen == dst->addrlen && + memcmp(from->sun_path, dst->addr.sun_path, fromlen) == 0) { + wpa_hexdump(MSG_DEBUG, "CTRL_IFACE changed monitor " + "level", (u8 *) from->sun_path, fromlen); + dst->debug_level = atoi(level); + return 0; + } + dst = dst->next; + } + + return -1; +} + + +static int hostapd_ctrl_iface_sta_mib(struct hostapd_data *hapd, + struct sta_info *sta, + char *buf, size_t buflen) +{ + int len, res; + + if (sta == NULL) { + return snprintf(buf, buflen, "FAIL\n"); + } + + len = 0; + len += snprintf(buf + len, buflen - len, MACSTR "\n", + MAC2STR(sta->addr)); + + res = ieee802_11_get_mib_sta(hapd, sta, buf + len, buflen - len); + if (res >= 0) + len += res; + res = wpa_get_mib_sta(hapd, sta, buf + len, buflen - len); + if (res >= 0) + len += res; + res = ieee802_1x_get_mib_sta(hapd, sta, buf + len, buflen - len); + if (res >= 0) + len += res; + + return len; +} + + +static int hostapd_ctrl_iface_sta_first(struct hostapd_data *hapd, + char *buf, size_t buflen) +{ + return hostapd_ctrl_iface_sta_mib(hapd, hapd->sta_list, buf, buflen); +} + + +static int hostapd_ctrl_iface_sta(struct hostapd_data *hapd, + const char *txtaddr, + char *buf, size_t buflen) +{ + u8 addr[ETH_ALEN]; + + if (hwaddr_aton(txtaddr, addr)) + return snprintf(buf, buflen, "FAIL\n"); + return hostapd_ctrl_iface_sta_mib(hapd, ap_get_sta(hapd, addr), + buf, buflen); +} + + +static int hostapd_ctrl_iface_sta_next(struct hostapd_data *hapd, + const char *txtaddr, + char *buf, size_t buflen) +{ + u8 addr[ETH_ALEN]; + struct sta_info *sta; + + if (hwaddr_aton(txtaddr, addr) || + (sta = ap_get_sta(hapd, addr)) == NULL) + return snprintf(buf, buflen, "FAIL\n"); + return hostapd_ctrl_iface_sta_mib(hapd, sta->next, buf, buflen); +} + + +static void hostapd_ctrl_iface_receive(int sock, void *eloop_ctx, + void *sock_ctx) +{ + struct hostapd_data *hapd = eloop_ctx; + char buf[256]; + int res; + struct sockaddr_un from; + socklen_t fromlen = sizeof(from); + char *reply; + const int reply_size = 4096; + int reply_len; + + res = recvfrom(sock, buf, sizeof(buf) - 1, 0, + (struct sockaddr *) &from, &fromlen); + if (res < 0) { + perror("recvfrom(ctrl_iface)"); + return; + } + buf[res] = '\0'; + wpa_hexdump_ascii(MSG_DEBUG, "RX ctrl_iface", (u8 *) buf, res); + + reply = malloc(reply_size); + if (reply == NULL) { + sendto(sock, "FAIL\n", 5, 0, (struct sockaddr *) &from, + fromlen); + return; + } + + memcpy(reply, "OK\n", 3); + reply_len = 3; + + if (strcmp(buf, "PING") == 0) { + memcpy(reply, "PONG\n", 5); + reply_len = 5; + } else if (strcmp(buf, "MIB") == 0) { + reply_len = ieee802_11_get_mib(hapd, reply, reply_size); + if (reply_len >= 0) { + res = wpa_get_mib(hapd, reply + reply_len, + reply_size - reply_len); + if (res < 0) + reply_len = -1; + else + reply_len += res; + } + if (reply_len >= 0) { + res = ieee802_1x_get_mib(hapd, reply + reply_len, + reply_size - reply_len); + if (res < 0) + reply_len = -1; + else + reply_len += res; + } + if (reply_len >= 0) { + res = radius_client_get_mib(hapd->radius, + reply + reply_len, + reply_size - reply_len); + if (res < 0) + reply_len = -1; + else + reply_len += res; + } + } else if (strcmp(buf, "STA-FIRST") == 0) { + reply_len = hostapd_ctrl_iface_sta_first(hapd, reply, + reply_size); + } else if (strncmp(buf, "STA ", 4) == 0) { + reply_len = hostapd_ctrl_iface_sta(hapd, buf + 4, reply, + reply_size); + } else if (strncmp(buf, "STA-NEXT ", 9) == 0) { + reply_len = hostapd_ctrl_iface_sta_next(hapd, buf + 9, reply, + reply_size); + } else if (strcmp(buf, "ATTACH") == 0) { + if (hostapd_ctrl_iface_attach(hapd, &from, fromlen)) + reply_len = -1; + } else if (strcmp(buf, "DETACH") == 0) { + if (hostapd_ctrl_iface_detach(hapd, &from, fromlen)) + reply_len = -1; + } else if (strncmp(buf, "LEVEL ", 6) == 0) { + if (hostapd_ctrl_iface_level(hapd, &from, fromlen, + buf + 6)) + reply_len = -1; + } else { + memcpy(reply, "UNKNOWN COMMAND\n", 16); + reply_len = 16; + } + + if (reply_len < 0) { + memcpy(reply, "FAIL\n", 5); + reply_len = 5; + } + sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from, fromlen); + free(reply); +} + + +static char * hostapd_ctrl_iface_path(struct hostapd_data *hapd) +{ + char *buf; + size_t len; + + if (hapd->conf->ctrl_interface == NULL) + return NULL; + + len = strlen(hapd->conf->ctrl_interface) + strlen(hapd->conf->iface) + + 2; + buf = malloc(len); + if (buf == NULL) + return NULL; + + snprintf(buf, len, "%s/%s", + hapd->conf->ctrl_interface, hapd->conf->iface); + return buf; +} + + +int hostapd_ctrl_iface_init(struct hostapd_data *hapd) +{ + struct sockaddr_un addr; + int s = -1; + char *fname = NULL; + + hapd->ctrl_sock = -1; + + if (hapd->conf->ctrl_interface == NULL) + return 0; + + if (mkdir(hapd->conf->ctrl_interface, S_IRWXU | S_IRWXG) < 0) { + if (errno == EEXIST) { + wpa_printf(MSG_DEBUG, "Using existing control " + "interface directory."); + } else { + perror("mkdir[ctrl_interface]"); + goto fail; + } + } + + if (hapd->conf->ctrl_interface_gid_set && + chown(hapd->conf->ctrl_interface, 0, + hapd->conf->ctrl_interface_gid) < 0) { + perror("chown[ctrl_interface]"); + return -1; + } + + if (strlen(hapd->conf->ctrl_interface) + 1 + strlen(hapd->conf->iface) + >= sizeof(addr.sun_path)) + goto fail; + + s = socket(PF_UNIX, SOCK_DGRAM, 0); + if (s < 0) { + perror("socket(PF_UNIX)"); + goto fail; + } + + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + fname = hostapd_ctrl_iface_path(hapd); + if (fname == NULL) + goto fail; + strncpy(addr.sun_path, fname, sizeof(addr.sun_path)); + if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + perror("bind(PF_UNIX)"); + goto fail; + } + + if (hapd->conf->ctrl_interface_gid_set && + chown(fname, 0, hapd->conf->ctrl_interface_gid) < 0) { + perror("chown[ctrl_interface/ifname]"); + goto fail; + } + + if (chmod(fname, S_IRWXU | S_IRWXG) < 0) { + perror("chmod[ctrl_interface/ifname]"); + goto fail; + } + free(fname); + + hapd->ctrl_sock = s; + eloop_register_read_sock(s, hostapd_ctrl_iface_receive, hapd, + NULL); + + return 0; + +fail: + if (s >= 0) + close(s); + if (fname) { + unlink(fname); + free(fname); + } + return -1; +} + + +void hostapd_ctrl_iface_deinit(struct hostapd_data *hapd) +{ + struct wpa_ctrl_dst *dst, *prev; + + if (hapd->ctrl_sock > -1) { + char *fname; + eloop_unregister_read_sock(hapd->ctrl_sock); + close(hapd->ctrl_sock); + hapd->ctrl_sock = -1; + fname = hostapd_ctrl_iface_path(hapd); + if (fname) + unlink(fname); + free(fname); + + if (hapd->conf->ctrl_interface && + rmdir(hapd->conf->ctrl_interface) < 0) { + if (errno == ENOTEMPTY) { + wpa_printf(MSG_DEBUG, "Control interface " + "directory not empty - leaving it " + "behind"); + } else { + perror("rmdir[ctrl_interface]"); + } + } + } + + dst = hapd->ctrl_dst; + while (dst) { + prev = dst; + dst = dst->next; + free(prev); + } +} + + +void hostapd_ctrl_iface_send(struct hostapd_data *hapd, int level, + char *buf, size_t len) +{ + struct wpa_ctrl_dst *dst, *next; + struct msghdr msg; + int idx; + struct iovec io[2]; + char levelstr[10]; + + dst = hapd->ctrl_dst; + if (hapd->ctrl_sock < 0 || dst == NULL) + return; + + snprintf(levelstr, sizeof(levelstr), "<%d>", level); + io[0].iov_base = levelstr; + io[0].iov_len = strlen(levelstr); + io[1].iov_base = buf; + io[1].iov_len = len; + memset(&msg, 0, sizeof(msg)); + msg.msg_iov = io; + msg.msg_iovlen = 2; + + idx = 0; + while (dst) { + next = dst->next; + if (level >= dst->debug_level) { + wpa_hexdump(MSG_DEBUG, "CTRL_IFACE monitor send", + (u8 *) dst->addr.sun_path, dst->addrlen); + msg.msg_name = &dst->addr; + msg.msg_namelen = dst->addrlen; + if (sendmsg(hapd->ctrl_sock, &msg, 0) < 0) { + fprintf(stderr, "CTRL_IFACE monitor[%d]: ", + idx); + perror("sendmsg"); + dst->errors++; + if (dst->errors > 10) { + hostapd_ctrl_iface_detach( + hapd, &dst->addr, + dst->addrlen); + } + } else + dst->errors = 0; + } + idx++; + dst = next; + } +} diff --git a/contrib/hostapd-0.4.9/ctrl_iface.h b/contrib/hostapd-0.4.9/ctrl_iface.h new file mode 100644 index 0000000000..ef1a541c94 --- /dev/null +++ b/contrib/hostapd-0.4.9/ctrl_iface.h @@ -0,0 +1,9 @@ +#ifndef CTRL_IFACE_H +#define CTRL_IFACE_H + +int hostapd_ctrl_iface_init(struct hostapd_data *hapd); +void hostapd_ctrl_iface_deinit(struct hostapd_data *hapd); +void hostapd_ctrl_iface_send(struct hostapd_data *hapd, int level, + char *buf, size_t len); + +#endif /* CTRL_IFACE_H */ diff --git a/contrib/hostapd-0.4.9/defconfig b/contrib/hostapd-0.4.9/defconfig new file mode 100644 index 0000000000..e8f4e4fc53 --- /dev/null +++ b/contrib/hostapd-0.4.9/defconfig @@ -0,0 +1,75 @@ +# Example hostapd build time configuration +# +# This file lists the configuration options that are used when building the +# hostapd binary. All lines starting with # are ignored. Configuration option +# lines must be commented out complete, if they are not to be included, i.e., +# just setting VARIABLE=n is not disabling that variable. +# +# This file is included in Makefile, so variables like CFLAGS and LIBS can also +# be modified from here. In most cass, these lines should use += in order not +# to override previous values of the variables. + +# Driver interface for Host AP driver +CONFIG_DRIVER_HOSTAP=y + +# Driver interface for wired authenticator +#CONFIG_DRIVER_WIRED=y + +# Driver interface for madwifi driver +#CONFIG_DRIVER_MADWIFI=y +#CFLAGS += -I../head # change to reflect local setup; directory for madwifi src + +# Driver interface for Prism54 driver +#CONFIG_DRIVER_PRISM54=y + +# Driver interface for FreeBSD net80211 layer (e.g., Atheros driver) +#CONFIG_DRIVER_BSD=y +#CFLAGS += -I/usr/local/include +#LIBS += -L/usr/local/lib + +# IEEE 802.11F/IAPP +CONFIG_IAPP=y + +# WPA2/IEEE 802.11i RSN pre-authentication +CONFIG_RSN_PREAUTH=y + +# Integrated EAP server +CONFIG_EAP=y + +# EAP-MD5 for the integrated EAP server +CONFIG_EAP_MD5=y + +# EAP-TLS for the integrated EAP server +CONFIG_EAP_TLS=y + +# EAP-MSCHAPv2 for the integrated EAP server +CONFIG_EAP_MSCHAPV2=y + +# EAP-PEAP for the integrated EAP server +CONFIG_EAP_PEAP=y + +# EAP-GTC for the integrated EAP server +CONFIG_EAP_GTC=y + +# EAP-TTLS for the integrated EAP server +CONFIG_EAP_TTLS=y + +# EAP-SIM for the integrated EAP server +#CONFIG_EAP_SIM=y + +# EAP-PAX for the integrated EAP server +#CONFIG_EAP_PAX=y + +# EAP-PSK for the integrated EAP server (this is _not_ needed for WPA-PSK) +#CONFIG_EAP_PSK=y + +# PKCS#12 (PFX) support (used to read private key and certificate file from +# a file that usually has extension .p12 or .pfx) +CONFIG_PKCS12=y + +# RADIUS authentication server. This provides access to the integrated EAP +# server from external hosts using RADIUS. +#CONFIG_RADIUS_SERVER=y + +# Build IPv6 support for RADIUS operations +CONFIG_IPV6=y diff --git a/contrib/hostapd-0.4.9/defs.h b/contrib/hostapd-0.4.9/defs.h new file mode 100644 index 0000000000..6f9881d71d --- /dev/null +++ b/contrib/hostapd-0.4.9/defs.h @@ -0,0 +1,131 @@ +/* + * WPA Supplicant - Common definitions + * Copyright (c) 2004-2005, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef DEFS_H +#define DEFS_H + +#ifdef FALSE +#undef FALSE +#endif +#ifdef TRUE +#undef TRUE +#endif +typedef enum { FALSE = 0, TRUE = 1 } Boolean; + + +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; + +/** + * enum wpa_states - wpa_supplicant state + * + * These enumeration values are used to indicate the current wpa_supplicant + * state (wpa_s->wpa_state). The current state can be retrieved with + * wpa_supplicant_get_state() function and the state can be changed by calling + * wpa_supplicant_set_state(). In WPA state machine (wpa.c and preauth.c), the + * wrapper functions wpa_sm_get_state() and wpa_sm_set_state() should be used + * to access the state variable. + */ +typedef enum { + /** + * WPA_DISCONNECTED - Disconnected state + * + * This state indicates that client is not associated, but is likely to + * start looking for an access point. This state is entered when a + * connection is lost. + */ + WPA_DISCONNECTED, + + /** + * WPA_INACTIVE - Inactive state (wpa_supplicant disabled) + * + * This state is entered if there are no enabled networks in the + * configuration. wpa_supplicant is not trying to associate with a new + * network and external interaction (e.g., ctrl_iface call to add or + * enable a network) is needed to start association. + */ + WPA_INACTIVE, + + /** + * WPA_SCANNING - Scanning for a network + * + * This state is entered when wpa_supplicant starts scanning for a + * network. + */ + WPA_SCANNING, + + /** + * WPA_ASSOCIATING - Trying to associate with a BSS/SSID + * + * This state is entered when wpa_supplicant has found a suitable BSS + * to associate with and the driver is configured to try to associate + * with this BSS in ap_scan=1 mode. When using ap_scan=2 mode, this + * state is entered when the driver is configured to try to associate + * with a network using the configured SSID and security policy. + */ + WPA_ASSOCIATING, + + /** + * WPA_ASSOCIATED - Association completed + * + * This state is entered when the driver reports that association has + * been successfully completed with an AP. If IEEE 802.1X is used + * (with or without WPA/WPA2), wpa_supplicant remains in this state + * until the IEEE 802.1X/EAPOL authentication has been completed. + */ + WPA_ASSOCIATED, + + /** + * WPA_4WAY_HANDSHAKE - WPA 4-Way Key Handshake in progress + * + * This state is entered when WPA/WPA2 4-Way Handshake is started. In + * case of WPA-PSK, this happens when receiving the first EAPOL-Key + * frame after association. In case of WPA-EAP, this state is entered + * when the IEEE 802.1X/EAPOL authentication has been completed. + */ + WPA_4WAY_HANDSHAKE, + + /** + * WPA_GROUP_HANDSHAKE - WPA Group Key Handshake in progress + * + * This state is entered when 4-Way Key Handshake has been completed + * (i.e., when the supplicant sends out message 4/4) and when Group + * Key rekeying is started by the AP (i.e., when supplicant receives + * message 1/2). + */ + WPA_GROUP_HANDSHAKE, + + /** + * WPA_COMPLETED - All authentication completed + * + * This state is entered when the full authentication process is + * completed. In case of WPA2, this happens when the 4-Way Handshake is + * successfully completed. With WPA, this state is entered after the + * Group Key Handshake; with IEEE 802.1X (non-WPA) connection is + * completed after dynamic keys are received (or if not used, after + * the EAP authentication has been completed). With static WEP keys and + * plaintext connections, this state is entered when an association + * has been completed. + * + * This state indicates that the supplicant has completed its + * processing for the association phase and that data connection is + * fully configured. + */ + WPA_COMPLETED +} wpa_states; + +#endif /* DEFS_H */ diff --git a/contrib/hostapd-0.4.9/developer.txt b/contrib/hostapd-0.4.9/developer.txt new file mode 100644 index 0000000000..e1d3163413 --- /dev/null +++ b/contrib/hostapd-0.4.9/developer.txt @@ -0,0 +1,219 @@ +Developer notes for hostapd +=========================== + +hostapd daemon setup, operations, and shutdown +---------------------------------------------- + +Files: hostapd.[ch] + +Externally called functions: + hostapd_new_assoc_sta() is called when a station associates with the AP + +Event loop functions: + handle_term() is called on SIGINT and SIGTERM to terminate hostapd process + handle_reload() is called on SIGHUP to reload configuration + handle_dump_state() is called on SIGUSR1 to dump station state data to a + text file + hostapd_rotate_wep() is called to periodically change WEP keys + + +Configuration parsing +--------------------- + +Configuration file parsing and data structure definition. + +Files: config.[ch] + +Externally called functions: + hostapd_config_read() is called to read and parse a configuration file; + allocates and returns configuration data structure + hostapd_config_free() is called to free configuration data structure + hostapd_maclist_found() is called to check whether a given address is found + in a list of MAC addresses + + +Kernel driver access +-------------------- + +Helper functions for configuring the Host AP kernel driver and +accessing data from it. + +Files: driver.[ch] + + +IEEE 802.11 frame handling (netdevice wlan#ap) +---------------------------------------------- + +Receive all incoming IEEE 802.11 frames from the kernel driver via +wlan#ap interface. + +Files: receive.c + +Externally called functions: + hostapd_init_sockets() is called to initialize sockets for receiving and + sending IEEE 802.11 frames via wlan#ap interface + +Event loop functions: + handle_read() is called for each incoming packet from wlan#ap net device + + +Station table +------------- + +Files: sta_info.[ch], ap.h + +Event loop functions: + ap_handle_timer() is called to check station activity and to remove + inactive stations + + +IEEE 802.11 management +---------------------- + +IEEE 802.11 management frame sending and processing (mainly, +authentication and association). IEEE 802.11 station functionality +(authenticate and associate with another AP as an station). + +Files: ieee802_11.[ch] + +Externally called functions: + ieee802_11_mgmt() is called for each received IEEE 802.11 management frame + (from handle_frame() in hostapd.c) + ieee802_11_mgmt_cb() is called for each received TX callback of IEEE 802.11 + management frame (from handle_tx_callback() in hostapd.c) + ieee802_11_send_deauth() is called to send deauthentication frame + ieee802_11_send_disassoc() is called to send disassociation frame + ieee802_11_parse_elems() is used to parse information elements in + IEEE 802.11 management frames + +Event loop functions: + ieee802_11_sta_authenticate() called to retry authentication (with another + AP) + ieee802_11_sta_associate() called to retry association (with another AP) + + +IEEE 802.11 authentication +-------------------------- + +Access control list for IEEE 802.11 authentication. Uses staticly +configured ACL from configuration files or an external RADIUS +server. Results from external RADIUS queries are cached to allow +faster authentication frame processing. + +Files: ieee802_11_auth.[ch] + +Externally called functions: + hostapd_acl_init() called once during hostapd startup + hostapd_acl_deinit() called once during hostapd shutdown + hostapd_acl_recv_radius() called by IEEE 802.1X code for incoming RADIUS + Authentication messages (returns 0 if message was processed) + hostapd_allowed_address() called to check whether a specified station can be + authenticated + +Event loop functions: + hostapd_acl_expire() is called to expire ACL cache entries + + +IEEE 802.1X Authenticator +------------------------- + +Files: ieee802_1x.[ch] + + +Externally called functions: + ieee802_1x_receive() is called for each incoming EAPOL frame from the + wireless interface + ieee802_1x_new_station() is called to start IEEE 802.1X authentication when + a new station completes IEEE 802.11 association + +Event loop functions: + ieee802_1x_receive_auth() called for each incoming RADIUS Authentication + message + + +EAPOL state machine +------------------- + +IEEE 802.1X state machine for EAPOL. + +Files: eapol_sm.[ch] + +Externally called functions: + eapol_sm_step() is called to advance EAPOL state machines after any change + that could affect their state + +Event loop functions: + eapol_port_timers_tick() called once per second to advance Port Timers state + machine + + +IEEE 802.11f (IAPP) +------------------- + +Files: iapp.[ch] + +Externally called functions: + iapp_new_station() is called to start accounting session when a new station + completes IEEE 802.11 association or IEEE 802.1X authentication + +Event loop functions: + iapp_receive_udp() is called for incoming IAPP frames over UDP + + +Per station accounting +---------------------- + +Send RADIUS Accounting start and stop messages to a RADIUS Accounting +server. Process incoming RADIUS Accounting messages. + +Files: accounting.[ch] + +Externally called functions: + accounting_init() called once during hostapd startup + accounting_deinit() called once during hostapd shutdown + accounting_sta_start() called when a station starts new session + accounting_sta_stop() called when a station session is terminated + +Event loop functions: + accounting_receive() called for each incoming RADIUS Accounting message + accounting_list_timer() called to retransmit accounting messages and to + remove expired entries + + +RADIUS messages +--------------- + +RADIUS message generation and parsing functions. + +Files: radius.[ch] + + +Event loop +---------- + +Event loop for registering timeout calls, signal handlers, and socket +read events. + +Files: eloop.[ch] + + +RC4 +--- + +RC4 encryption + +Files: rc4.[ch] + + +MD5 +--- + +MD5 hash and HMAC-MD5. + +Files: md5.[ch] + + +Miscellaneous helper functions +------------------------------ + +Files: common.[ch] diff --git a/contrib/hostapd-0.4.9/driver.h b/contrib/hostapd-0.4.9/driver.h new file mode 100644 index 0000000000..ed9ecbfdbc --- /dev/null +++ b/contrib/hostapd-0.4.9/driver.h @@ -0,0 +1,271 @@ +#ifndef DRIVER_H +#define DRIVER_H + +struct driver_ops { + const char *name; /* as appears in the config file */ + + int (*init)(struct hostapd_data *hapd); + void (*deinit)(void *priv); + + int (*wireless_event_init)(void *priv); + void (*wireless_event_deinit)(void *priv); + + /** + * set_8021x - enable/disable IEEE 802.1X support + * @priv: driver private data + * @enabled: 1 = enable, 0 = disable + * + * Returns: 0 on success, -1 on failure + * + * Configure the kernel driver to enable/disable 802.1X support. + * This may be an empty function if 802.1X support is always enabled. + */ + int (*set_ieee8021x)(void *priv, int enabled); + + /** + * set_privacy - enable/disable privacy + * @priv: driver private data + * @enabled: 1 = privacy enabled, 0 = disabled + * + * Return: 0 on success, -1 on failure + * + * Configure privacy. + */ + int (*set_privacy)(void *priv, int enabled); + + int (*set_encryption)(void *priv, const char *alg, u8 *addr, + int idx, u8 *key, size_t key_len); + int (*get_seqnum)(void *priv, u8 *addr, int idx, u8 *seq); + int (*flush)(void *priv); + int (*set_generic_elem)(void *priv, const u8 *elem, size_t elem_len); + + int (*read_sta_data)(void *priv, struct hostap_sta_driver_data *data, + u8 *addr); + int (*send_eapol)(void *priv, u8 *addr, u8 *data, size_t data_len, + int encrypt); + int (*set_sta_authorized)(void *driver, u8 *addr, int authorized); + int (*sta_deauth)(void *priv, u8 *addr, int reason); + int (*sta_disassoc)(void *priv, u8 *addr, int reason); + int (*sta_remove)(void *priv, u8 *addr); + int (*get_ssid)(void *priv, u8 *buf, int len); + int (*set_ssid)(void *priv, u8 *buf, int len); + int (*set_countermeasures)(void *priv, int enabled); + int (*send_mgmt_frame)(void *priv, const void *msg, size_t len, + int flags); + int (*set_assoc_ap)(void *priv, u8 *addr); + int (*sta_add)(void *priv, u8 *addr, u16 aid, u16 capability, + u8 tx_supp_rates); + int (*get_inact_sec)(void *priv, u8 *addr); + int (*sta_clear_stats)(void *priv, u8 *addr); +}; + +static inline int +hostapd_driver_init(struct hostapd_data *hapd) +{ + if (hapd->driver == NULL || hapd->driver->init == NULL) + return -1; + return hapd->driver->init(hapd); +} + +static inline void +hostapd_driver_deinit(struct hostapd_data *hapd) +{ + if (hapd->driver == NULL || hapd->driver->deinit == NULL) + return; + hapd->driver->deinit(hapd->driver); +} + +static inline int +hostapd_wireless_event_init(struct hostapd_data *hapd) +{ + if (hapd->driver == NULL || + hapd->driver->wireless_event_init == NULL) + return 0; + return hapd->driver->wireless_event_init(hapd->driver); +} + +static inline void +hostapd_wireless_event_deinit(struct hostapd_data *hapd) +{ + if (hapd->driver == NULL || + hapd->driver->wireless_event_deinit == NULL) + return; + hapd->driver->wireless_event_deinit(hapd->driver); +} + +static inline int +hostapd_set_ieee8021x(struct hostapd_data *hapd, int enabled) +{ + if (hapd->driver == NULL || hapd->driver->set_ieee8021x == NULL) + return 0; + return hapd->driver->set_ieee8021x(hapd->driver, enabled); +} + +static inline int +hostapd_set_privacy(struct hostapd_data *hapd, int enabled) +{ + if (hapd->driver == NULL || hapd->driver->set_privacy == NULL) + return 0; + return hapd->driver->set_privacy(hapd->driver, enabled); +} + +static inline int +hostapd_set_encryption(struct hostapd_data *hapd, const char *alg, u8 *addr, + int idx, u8 *key, size_t key_len) +{ + if (hapd->driver == NULL || hapd->driver->set_encryption == NULL) + return 0; + return hapd->driver->set_encryption(hapd->driver, alg, addr, idx, key, + key_len); +} + +static inline int +hostapd_get_seqnum(struct hostapd_data *hapd, u8 *addr, int idx, u8 *seq) +{ + if (hapd->driver == NULL || hapd->driver->get_seqnum == NULL) + return 0; + return hapd->driver->get_seqnum(hapd->driver, addr, idx, seq); +} + +static inline int +hostapd_flush(struct hostapd_data *hapd) +{ + if (hapd->driver == NULL || hapd->driver->flush == NULL) + return 0; + return hapd->driver->flush(hapd->driver); +} + +static inline int +hostapd_set_generic_elem(struct hostapd_data *hapd, const u8 *elem, + size_t elem_len) +{ + if (hapd->driver == NULL || hapd->driver->set_generic_elem == NULL) + return 0; + return hapd->driver->set_generic_elem(hapd->driver, elem, elem_len); +} + +static inline int +hostapd_read_sta_data(struct hostapd_data *hapd, + struct hostap_sta_driver_data *data, u8 *addr) +{ + if (hapd->driver == NULL || hapd->driver->read_sta_data == NULL) + return -1; + return hapd->driver->read_sta_data(hapd->driver, data, addr); +} + +static inline int +hostapd_send_eapol(struct hostapd_data *hapd, u8 *addr, u8 *data, + size_t data_len, int encrypt) +{ + if (hapd->driver == NULL || hapd->driver->send_eapol == NULL) + return 0; + return hapd->driver->send_eapol(hapd->driver, addr, data, data_len, + encrypt); +} + +static inline int +hostapd_set_sta_authorized(struct hostapd_data *hapd, u8 *addr, int authorized) +{ + if (hapd->driver == NULL || hapd->driver->set_sta_authorized == NULL) + return 0; + return hapd->driver->set_sta_authorized(hapd->driver, addr, + authorized); +} + +static inline int +hostapd_sta_deauth(struct hostapd_data *hapd, u8 *addr, int reason) +{ + if (hapd->driver == NULL || hapd->driver->sta_deauth == NULL) + return 0; + return hapd->driver->sta_deauth(hapd->driver, addr, reason); +} + +static inline int +hostapd_sta_disassoc(struct hostapd_data *hapd, u8 *addr, int reason) +{ + if (hapd->driver == NULL || hapd->driver->sta_disassoc == NULL) + return 0; + return hapd->driver->sta_disassoc(hapd->driver, addr, reason); +} + +static inline int +hostapd_sta_remove(struct hostapd_data *hapd, u8 *addr) +{ + if (hapd->driver == NULL || hapd->driver->sta_remove == NULL) + return 0; + return hapd->driver->sta_remove(hapd->driver, addr); +} + +static inline int +hostapd_get_ssid(struct hostapd_data *hapd, u8 *buf, size_t len) +{ + if (hapd->driver == NULL || hapd->driver->get_ssid == NULL) + return 0; + return hapd->driver->get_ssid(hapd->driver, buf, len); +} + +static inline int +hostapd_set_ssid(struct hostapd_data *hapd, u8 *buf, size_t len) +{ + if (hapd->driver == NULL || hapd->driver->set_ssid == NULL) + return 0; + return hapd->driver->set_ssid(hapd->driver, buf, len); +} + +static inline int +hostapd_send_mgmt_frame(struct hostapd_data *hapd, const void *msg, size_t len, + int flags) +{ + if (hapd->driver == NULL || hapd->driver->send_mgmt_frame == NULL) + return 0; + return hapd->driver->send_mgmt_frame(hapd->driver, msg, len, flags); +} + +static inline int +hostapd_set_assoc_ap(struct hostapd_data *hapd, u8 *addr) +{ + if (hapd->driver == NULL || hapd->driver->set_assoc_ap == NULL) + return 0; + return hapd->driver->set_assoc_ap(hapd->driver, addr); +} + +static inline int +hostapd_set_countermeasures(struct hostapd_data *hapd, int enabled) +{ + if (hapd->driver == NULL || hapd->driver->set_countermeasures == NULL) + return 0; + return hapd->driver->set_countermeasures(hapd->driver, enabled); +} + +static inline int +hostapd_sta_add(struct hostapd_data *hapd, u8 *addr, u16 aid, u16 capability, + u8 tx_supp_rates) +{ + if (hapd->driver == NULL || hapd->driver->sta_add == NULL) + return 0; + return hapd->driver->sta_add(hapd->driver, addr, aid, capability, + tx_supp_rates); +} + +static inline int +hostapd_get_inact_sec(struct hostapd_data *hapd, u8 *addr) +{ + if (hapd->driver == NULL || hapd->driver->get_inact_sec == NULL) + return 0; + return hapd->driver->get_inact_sec(hapd->driver, addr); +} + + +void driver_register(const char *name, const struct driver_ops *ops); +void driver_unregister(const char *name); +const struct driver_ops *driver_lookup(const char *name); + +static inline int +hostapd_sta_clear_stats(struct hostapd_data *hapd, u8 *addr) +{ + if (hapd->driver == NULL || hapd->driver->sta_clear_stats == NULL) + return 0; + return hapd->driver->sta_clear_stats(hapd->driver, addr); +} + +#endif /* DRIVER_H */ diff --git a/contrib/hostapd-0.4.9/driver_wired.c b/contrib/hostapd-0.4.9/driver_wired.c new file mode 100644 index 0000000000..09eb31990c --- /dev/null +++ b/contrib/hostapd-0.4.9/driver_wired.c @@ -0,0 +1,399 @@ +/* + * Host AP (software wireless LAN access point) user space daemon for + * Host AP kernel driver / Kernel driver communication + * Copyright (c) 2002-2005, Jouni Malinen + * Copyright (c) 2004, Gunter Burchardt + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef USE_KERNEL_HEADERS +#include +#include +#include /* The L2 protocols */ +#include +#include +#else /* USE_KERNEL_HEADERS */ +#include +#include +#include +#endif /* USE_KERNEL_HEADERS */ + +#include "hostapd.h" +#include "ieee802_1x.h" +#include "eloop.h" +#include "sta_info.h" +#include "driver.h" +#include "accounting.h" + + +struct wired_driver_data { + struct driver_ops ops; + struct hostapd_data *hapd; + + int sock; /* raw packet socket for driver access */ + int dhcp_sock; /* socket for dhcp packets */ + int use_pae_group_addr; +}; + +static const struct driver_ops wired_driver_ops; + + +#define WIRED_EAPOL_MULTICAST_GROUP {0x01,0x80,0xc2,0x00,0x00,0x03} + + +/* TODO: detecting new devices should eventually be changed from using DHCP + * snooping to trigger on any packet from a new layer 2 MAC address, e.g., + * based on ebtables, etc. */ + +struct dhcp_message { + u_int8_t op; + u_int8_t htype; + u_int8_t hlen; + u_int8_t hops; + u_int32_t xid; + u_int16_t secs; + u_int16_t flags; + u_int32_t ciaddr; + u_int32_t yiaddr; + u_int32_t siaddr; + u_int32_t giaddr; + u_int8_t chaddr[16]; + u_int8_t sname[64]; + u_int8_t file[128]; + u_int32_t cookie; + u_int8_t options[308]; /* 312 - cookie */ +}; + + +static void wired_possible_new_sta(struct hostapd_data *hapd, u8 *addr) +{ + struct sta_info *sta; + + sta = ap_get_sta(hapd, addr); + if (sta) + return; + + HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "Data frame from unknown STA " + MACSTR " - adding a new STA\n", MAC2STR(addr)); + sta = ap_sta_add(hapd, addr); + if (sta) { + hostapd_new_assoc_sta(hapd, sta, 0); + accounting_sta_get_id(hapd, sta); + } else { + HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "Failed to add STA entry " + "for " MACSTR "\n", MAC2STR(addr)); + } +} + + +static void handle_data(struct hostapd_data *hapd, unsigned char *buf, + size_t len) +{ + struct ieee8023_hdr *hdr; + u8 *pos, *sa; + size_t left; + + /* must contain at least ieee8023_hdr 6 byte source, 6 byte dest, + * 2 byte ethertype */ + if (len < 14) { + HOSTAPD_DEBUG(HOSTAPD_DEBUG_VERBOSE, "handle_data: too short " + "(%lu)\n", (unsigned long) len); + return; + } + + hdr = (struct ieee8023_hdr *) buf; + + switch (ntohs(hdr->ethertype)) { + case ETH_P_PAE: + HOSTAPD_DEBUG(HOSTAPD_DEBUG_VERBOSE, + "Received EAPOL packet\n"); + sa = hdr->src; + wired_possible_new_sta(hapd, sa); + + pos = (u8 *) (hdr + 1); + left = len - sizeof(*hdr); + + ieee802_1x_receive(hapd, sa, pos, left); + break; + + default: + HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, + "Unknown ethertype 0x%04x in data frame\n", + ntohs(hdr->ethertype)); + break; + } +} + + +static void handle_read(int sock, void *eloop_ctx, void *sock_ctx) +{ + struct hostapd_data *hapd = (struct hostapd_data *) eloop_ctx; + int len; + unsigned char buf[3000]; + + len = recv(sock, buf, sizeof(buf), 0); + if (len < 0) { + perror("recv"); + return; + } + + handle_data(hapd, buf, len); +} + + +static void handle_dhcp(int sock, void *eloop_ctx, void *sock_ctx) +{ + struct hostapd_data *hapd = (struct hostapd_data *) eloop_ctx; + int len; + unsigned char buf[3000]; + struct dhcp_message *msg; + u8 *mac_address; + + len = recv(sock, buf, sizeof(buf), 0); + if (len < 0) { + perror("recv"); + return; + } + + /* must contain at least dhcp_message->chaddr */ + if (len < 44) { + HOSTAPD_DEBUG(HOSTAPD_DEBUG_VERBOSE, "handle_dhcp: too short " + "(%d)\n", len); + return; + } + + msg = (struct dhcp_message *) buf; + mac_address = (u8 *) &(msg->chaddr); + + HOSTAPD_DEBUG(HOSTAPD_DEBUG_VERBOSE, + "Got DHCP broadcast packet from " MACSTR "\n", + MAC2STR(mac_address)); + + wired_possible_new_sta(hapd, mac_address); +} + + +static int wired_init_sockets(struct wired_driver_data *drv) +{ + struct hostapd_data *hapd = drv->hapd; + struct ifreq ifr; + struct sockaddr_ll addr; + struct sockaddr_in addr2; + struct packet_mreq mreq; + u8 multicastgroup_eapol[6] = WIRED_EAPOL_MULTICAST_GROUP; + int n = 1; + + drv->sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_PAE)); + if (drv->sock < 0) { + perror("socket[PF_PACKET,SOCK_RAW]"); + return -1; + } + + if (eloop_register_read_sock(drv->sock, handle_read, hapd, NULL)) { + printf("Could not register read socket\n"); + return -1; + } + + memset(&ifr, 0, sizeof(ifr)); + snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s", + hapd->conf->iface); + if (ioctl(drv->sock, SIOCGIFINDEX, &ifr) != 0) { + perror("ioctl(SIOCGIFINDEX)"); + return -1; + } + + + memset(&addr, 0, sizeof(addr)); + addr.sll_family = AF_PACKET; + addr.sll_ifindex = ifr.ifr_ifindex; + HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, + "Opening raw packet socket for ifindex %d\n", + addr.sll_ifindex); + + if (bind(drv->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + perror("bind"); + return -1; + } + + /* filter multicast address */ + memset(&mreq, 0, sizeof(mreq)); + mreq.mr_ifindex = ifr.ifr_ifindex; + mreq.mr_type = PACKET_MR_MULTICAST; + mreq.mr_alen = 6; + memcpy(mreq.mr_address, multicastgroup_eapol, mreq.mr_alen); + + if (setsockopt(drv->sock, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq, + sizeof(mreq)) < 0) { + perror("setsockopt[SOL_SOCKET,PACKET_ADD_MEMBERSHIP]"); + return -1; + } + + memset(&ifr, 0, sizeof(ifr)); + snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s", hapd->conf->iface); + if (ioctl(drv->sock, SIOCGIFHWADDR, &ifr) != 0) { + perror("ioctl(SIOCGIFHWADDR)"); + return -1; + } + + if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) { + printf("Invalid HW-addr family 0x%04x\n", + ifr.ifr_hwaddr.sa_family); + return -1; + } + memcpy(hapd->own_addr, ifr.ifr_hwaddr.sa_data, ETH_ALEN); + + /* setup dhcp listen socket for sta detection */ + if ((drv->dhcp_sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) { + perror("socket call failed for dhcp"); + return -1; + } + + if (eloop_register_read_sock(drv->dhcp_sock, handle_dhcp, hapd, NULL)) + { + printf("Could not register read socket\n"); + return -1; + } + + memset(&addr2, 0, sizeof(addr2)); + addr2.sin_family = AF_INET; + addr2.sin_port = htons(67); + addr2.sin_addr.s_addr = INADDR_ANY; + + if (setsockopt(drv->dhcp_sock, SOL_SOCKET, SO_REUSEADDR, (char *) &n, + sizeof(n)) == -1) { + perror("setsockopt[SOL_SOCKET,SO_REUSEADDR]"); + return -1; + } + if (setsockopt(drv->dhcp_sock, SOL_SOCKET, SO_BROADCAST, (char *) &n, + sizeof(n)) == -1) { + perror("setsockopt[SOL_SOCKET,SO_BROADCAST]"); + return -1; + } + + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_ifrn.ifrn_name, hapd->conf->iface, IFNAMSIZ); + if (setsockopt(drv->dhcp_sock, SOL_SOCKET, SO_BINDTODEVICE, + (char *) &ifr, sizeof(ifr)) < 0) { + perror("setsockopt[SOL_SOCKET,SO_BINDTODEVICE]"); + return -1; + } + + if (bind(drv->dhcp_sock, (struct sockaddr *) &addr2, + sizeof(struct sockaddr)) == -1) { + perror("bind"); + return -1; + } + + return 0; +} + + +static int wired_send_eapol(void *priv, u8 *addr, + u8 *data, size_t data_len, int encrypt) +{ + struct wired_driver_data *drv = priv; + u8 pae_group_addr[ETH_ALEN] = WIRED_EAPOL_MULTICAST_GROUP; + struct ieee8023_hdr *hdr; + size_t len; + u8 *pos; + int res; + + len = sizeof(*hdr) + data_len; + hdr = malloc(len); + if (hdr == NULL) { + printf("malloc() failed for wired_send_eapol(len=%lu)\n", + (unsigned long) len); + return -1; + } + + memset(hdr, 0, len); + memcpy(hdr->dest, drv->use_pae_group_addr ? pae_group_addr : addr, + ETH_ALEN); + memcpy(hdr->src, drv->hapd->own_addr, ETH_ALEN); + hdr->ethertype = htons(ETH_P_PAE); + + pos = (u8 *) (hdr + 1); + memcpy(pos, data, data_len); + + res = send(drv->sock, (u8 *) hdr, len, 0); + free(hdr); + + if (res < 0) { + perror("wired_send_eapol: send"); + printf("wired_send_eapol - packet len: %lu - failed\n", + (unsigned long) len); + } + + return res; +} + + +static int wired_driver_init(struct hostapd_data *hapd) +{ + struct wired_driver_data *drv; + + drv = malloc(sizeof(struct wired_driver_data)); + if (drv == NULL) { + printf("Could not allocate memory for wired driver data\n"); + return -1; + } + + memset(drv, 0, sizeof(*drv)); + drv->ops = wired_driver_ops; + drv->hapd = hapd; + drv->use_pae_group_addr = hapd->conf->use_pae_group_addr; + + if (wired_init_sockets(drv)) + return -1; + + hapd->driver = &drv->ops; + return 0; +} + + +static void wired_driver_deinit(void *priv) +{ + struct wired_driver_data *drv = priv; + + drv->hapd->driver = NULL; + + if (drv->sock >= 0) + close(drv->sock); + + if (drv->dhcp_sock >= 0) + close(drv->dhcp_sock); + + free(drv); +} + + +static const struct driver_ops wired_driver_ops = { + .name = "wired", + .init = wired_driver_init, + .deinit = wired_driver_deinit, + .send_eapol = wired_send_eapol, +}; + +void wired_driver_register(void) +{ + driver_register(wired_driver_ops.name, &wired_driver_ops); +} diff --git a/contrib/hostapd-0.4.9/eap.c b/contrib/hostapd-0.4.9/eap.c new file mode 100644 index 0000000000..a20147e79e --- /dev/null +++ b/contrib/hostapd-0.4.9/eap.c @@ -0,0 +1,944 @@ +/* + * hostapd / EAP Standalone Authenticator state machine + * Copyright (c) 2004-2005, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include +#include +#include +#include +#include +#include + +#include "hostapd.h" +#include "eloop.h" +#include "sta_info.h" +#include "eap_i.h" + +#define EAP_MAX_AUTH_ROUNDS 50 + +extern const struct eap_method eap_method_identity; +#ifdef EAP_MD5 +extern const struct eap_method eap_method_md5; +#endif /* EAP_MD5 */ +#ifdef EAP_TLS +extern const struct eap_method eap_method_tls; +#endif /* EAP_TLS */ +#ifdef EAP_MSCHAPv2 +extern const struct eap_method eap_method_mschapv2; +#endif /* EAP_MSCHAPv2 */ +#ifdef EAP_PEAP +extern const struct eap_method eap_method_peap; +#endif /* EAP_PEAP */ +#ifdef EAP_TLV +extern const struct eap_method eap_method_tlv; +#endif /* EAP_TLV */ +#ifdef EAP_GTC +extern const struct eap_method eap_method_gtc; +#endif /* EAP_GTC */ +#ifdef EAP_TTLS +extern const struct eap_method eap_method_ttls; +#endif /* EAP_TTLS */ +#ifdef EAP_SIM +extern const struct eap_method eap_method_sim; +#endif /* EAP_SIM */ +#ifdef EAP_PAX +extern const struct eap_method eap_method_pax; +#endif /* EAP_PAX */ +#ifdef EAP_PSK +extern const struct eap_method eap_method_psk; +#endif /* EAP_PSK */ + +static const struct eap_method *eap_methods[] = +{ + &eap_method_identity, +#ifdef EAP_MD5 + &eap_method_md5, +#endif /* EAP_MD5 */ +#ifdef EAP_TLS + &eap_method_tls, +#endif /* EAP_TLS */ +#ifdef EAP_MSCHAPv2 + &eap_method_mschapv2, +#endif /* EAP_MSCHAPv2 */ +#ifdef EAP_PEAP + &eap_method_peap, +#endif /* EAP_PEAP */ +#ifdef EAP_TTLS + &eap_method_ttls, +#endif /* EAP_TTLS */ +#ifdef EAP_TLV + &eap_method_tlv, +#endif /* EAP_TLV */ +#ifdef EAP_GTC + &eap_method_gtc, +#endif /* EAP_GTC */ +#ifdef EAP_SIM + &eap_method_sim, +#endif /* EAP_SIM */ +#ifdef EAP_PAX + &eap_method_pax, +#endif /* EAP_PAX */ +#ifdef EAP_PSK + &eap_method_psk, +#endif /* EAP_PSK */ +}; +#define NUM_EAP_METHODS (sizeof(eap_methods) / sizeof(eap_methods[0])) + + +const struct eap_method * eap_sm_get_eap_methods(int method) +{ + int i; + for (i = 0; i < NUM_EAP_METHODS; i++) { + if (eap_methods[i]->method == method) + return eap_methods[i]; + } + return NULL; +} + +static void eap_user_free(struct eap_user *user); + + +/* EAP state machines are described in draft-ietf-eap-statemachine-05.txt */ + +static int eap_sm_calculateTimeout(struct eap_sm *sm, int retransCount, + int eapSRTT, int eapRTTVAR, + int methodTimeout); +static void eap_sm_parseEapResp(struct eap_sm *sm, u8 *resp, size_t len); +static u8 * eap_sm_buildSuccess(struct eap_sm *sm, int id, size_t *len); +static u8 * eap_sm_buildFailure(struct eap_sm *sm, int id, size_t *len); +static int eap_sm_nextId(struct eap_sm *sm, int id); +static void eap_sm_Policy_update(struct eap_sm *sm, u8 *nak_list, size_t len); +static EapType eap_sm_Policy_getNextMethod(struct eap_sm *sm); +static int eap_sm_Policy_getDecision(struct eap_sm *sm); +static Boolean eap_sm_Policy_doPickUp(struct eap_sm *sm, EapType method); + + +/* Definitions for clarifying state machine implementation */ +#define SM_STATE(machine, state) \ +static void sm_ ## machine ## _ ## state ## _Enter(struct eap_sm *sm, \ + int global) + +#define SM_ENTRY(machine, state) \ +if (!global || sm->machine ## _state != machine ## _ ## state) { \ + sm->changed = TRUE; \ + wpa_printf(MSG_DEBUG, "EAP: " #machine " entering state " #state); \ +} \ +sm->machine ## _state = machine ## _ ## state; + +#define SM_ENTER(machine, state) \ +sm_ ## machine ## _ ## state ## _Enter(sm, 0) +#define SM_ENTER_GLOBAL(machine, state) \ +sm_ ## machine ## _ ## state ## _Enter(sm, 1) + +#define SM_STEP(machine) \ +static void sm_ ## machine ## _Step(struct eap_sm *sm) + +#define SM_STEP_RUN(machine) sm_ ## machine ## _Step(sm) + + +static Boolean eapol_get_bool(struct eap_sm *sm, enum eapol_bool_var var) +{ + return sm->eapol_cb->get_bool(sm->eapol_ctx, var); +} + + +static void eapol_set_bool(struct eap_sm *sm, enum eapol_bool_var var, + Boolean value) +{ + sm->eapol_cb->set_bool(sm->eapol_ctx, var, value); +} + + +static void eapol_set_eapReqData(struct eap_sm *sm, + const u8 *eapReqData, size_t eapReqDataLen) +{ + wpa_hexdump(MSG_MSGDUMP, "EAP: eapReqData -> EAPOL", + sm->eapReqData, sm->eapReqDataLen); + sm->eapol_cb->set_eapReqData(sm->eapol_ctx, eapReqData, eapReqDataLen); +} + + +static void eapol_set_eapKeyData(struct eap_sm *sm, + const u8 *eapKeyData, size_t eapKeyDataLen) +{ + wpa_hexdump(MSG_MSGDUMP, "EAP: eapKeyData -> EAPOL", + sm->eapKeyData, sm->eapKeyDataLen); + sm->eapol_cb->set_eapKeyData(sm->eapol_ctx, eapKeyData, eapKeyDataLen); +} + + +int eap_user_get(struct eap_sm *sm, const u8 *identity, size_t identity_len, + int phase2) +{ + struct eap_user *user; + + if (sm == NULL || sm->eapol_cb == NULL || + sm->eapol_cb->get_eap_user == NULL) + return -1; + + eap_user_free(sm->user); + sm->user = NULL; + + user = malloc(sizeof(*user)); + if (user == NULL) + return -1; + memset(user, 0, sizeof(*user)); + + if (sm->eapol_cb->get_eap_user(sm->eapol_ctx, identity, + identity_len, phase2, user) != 0) { + eap_user_free(user); + return -1; + } + + sm->user = user; + sm->user_eap_method_index = 0; + + return 0; +} + + +SM_STATE(EAP, DISABLED) +{ + SM_ENTRY(EAP, DISABLED); + sm->num_rounds = 0; +} + + +SM_STATE(EAP, INITIALIZE) +{ + SM_ENTRY(EAP, INITIALIZE); + + sm->currentId = -1; + eapol_set_bool(sm, EAPOL_eapSuccess, FALSE); + eapol_set_bool(sm, EAPOL_eapFail, FALSE); + eapol_set_bool(sm, EAPOL_eapTimeout, FALSE); + free(sm->eapKeyData); + sm->eapKeyData = NULL; + sm->eapKeyDataLen = 0; + /* eapKeyAvailable = FALSE */ + eapol_set_bool(sm, EAPOL_eapRestart, FALSE); + + /* This is not defined in draft-ietf-eap-statemachine-05.txt, but + * method state needs to be reseted here so that it does not remain in + * success state when re-authentication starts. */ + if (sm->m && sm->eap_method_priv) { + sm->m->reset(sm, sm->eap_method_priv); + sm->eap_method_priv = NULL; + } + sm->m = NULL; + sm->user_eap_method_index = 0; + + if (sm->backend_auth) { + sm->currentMethod = EAP_TYPE_NONE; + /* parse rxResp, respId, respMethod */ + eap_sm_parseEapResp(sm, sm->eapRespData, sm->eapRespDataLen); + if (sm->rxResp) { + sm->currentId = sm->respId; + } + } + sm->num_rounds = 0; +} + + +SM_STATE(EAP, PICK_UP_METHOD) +{ + SM_ENTRY(EAP, PICK_UP_METHOD); + + if (eap_sm_Policy_doPickUp(sm, sm->respMethod)) { + sm->currentMethod = sm->respMethod; + if (sm->m && sm->eap_method_priv) { + sm->m->reset(sm, sm->eap_method_priv); + sm->eap_method_priv = NULL; + } + sm->m = eap_sm_get_eap_methods(sm->currentMethod); + if (sm->m && sm->m->initPickUp) { + sm->eap_method_priv = sm->m->initPickUp(sm); + if (sm->eap_method_priv == NULL) { + wpa_printf(MSG_DEBUG, "EAP: Failed to " + "initialize EAP method %d", + sm->currentMethod); + sm->m = NULL; + sm->currentMethod = EAP_TYPE_NONE; + } + } else { + sm->m = NULL; + sm->currentMethod = EAP_TYPE_NONE; + } + } +} + + +SM_STATE(EAP, IDLE) +{ + SM_ENTRY(EAP, IDLE); + + sm->retransWhile = eap_sm_calculateTimeout(sm, sm->retransCount, + sm->eapSRTT, sm->eapRTTVAR, + sm->methodTimeout); +} + + +SM_STATE(EAP, RETRANSMIT) +{ + SM_ENTRY(EAP, RETRANSMIT); + + /* TODO: Is this needed since EAPOL state machines take care of + * retransmit? */ +} + + +SM_STATE(EAP, RECEIVED) +{ + SM_ENTRY(EAP, RECEIVED); + + /* parse rxResp, respId, respMethod */ + eap_sm_parseEapResp(sm, sm->eapRespData, sm->eapRespDataLen); + sm->num_rounds++; +} + + +SM_STATE(EAP, DISCARD) +{ + SM_ENTRY(EAP, DISCARD); + eapol_set_bool(sm, EAPOL_eapResp, FALSE); + eapol_set_bool(sm, EAPOL_eapNoReq, TRUE); +} + + +SM_STATE(EAP, SEND_REQUEST) +{ + SM_ENTRY(EAP, SEND_REQUEST); + + sm->retransCount = 0; + if (sm->eapReqData) { + eapol_set_eapReqData(sm, sm->eapReqData, sm->eapReqDataLen); + free(sm->lastReqData); + sm->lastReqData = sm->eapReqData; + sm->lastReqDataLen = sm->eapReqDataLen; + sm->eapReqData = NULL; + sm->eapReqDataLen = 0; + eapol_set_bool(sm, EAPOL_eapResp, FALSE); + eapol_set_bool(sm, EAPOL_eapReq, TRUE); + } else { + wpa_printf(MSG_INFO, "EAP: SEND_REQUEST - no eapReqData"); + eapol_set_bool(sm, EAPOL_eapResp, FALSE); + eapol_set_bool(sm, EAPOL_eapReq, FALSE); + eapol_set_bool(sm, EAPOL_eapNoReq, TRUE); + } +} + + +SM_STATE(EAP, INTEGRITY_CHECK) +{ + SM_ENTRY(EAP, INTEGRITY_CHECK); + + if (sm->m->check) { + sm->ignore = sm->m->check(sm, sm->eap_method_priv, + sm->eapRespData, sm->eapRespDataLen); + } +} + + +SM_STATE(EAP, METHOD_REQUEST) +{ + SM_ENTRY(EAP, METHOD_REQUEST); + + if (sm->m == NULL) { + wpa_printf(MSG_DEBUG, "EAP: method not initialized"); + return; + } + + sm->currentId = eap_sm_nextId(sm, sm->currentId); + wpa_printf(MSG_DEBUG, "EAP: building EAP-Request: Identifier %d", + sm->currentId); + sm->lastId = sm->currentId; + free(sm->eapReqData); + sm->eapReqData = sm->m->buildReq(sm, sm->eap_method_priv, + sm->currentId, &sm->eapReqDataLen); + if (sm->m->getTimeout) + sm->methodTimeout = sm->m->getTimeout(sm, sm->eap_method_priv); + else + sm->methodTimeout = 0; +} + + +SM_STATE(EAP, METHOD_RESPONSE) +{ + SM_ENTRY(EAP, METHOD_RESPONSE); + + sm->m->process(sm, sm->eap_method_priv, sm->eapRespData, + sm->eapRespDataLen); + if (sm->m->isDone(sm, sm->eap_method_priv)) { + eap_sm_Policy_update(sm, NULL, 0); + free(sm->eapKeyData); + if (sm->m->getKey) { + sm->eapKeyData = sm->m->getKey(sm, sm->eap_method_priv, + &sm->eapKeyDataLen); + } else { + sm->eapKeyData = NULL; + sm->eapKeyDataLen = 0; + } + sm->methodState = METHOD_END; + } else { + sm->methodState = METHOD_CONTINUE; + } +} + + +SM_STATE(EAP, PROPOSE_METHOD) +{ + SM_ENTRY(EAP, PROPOSE_METHOD); + + sm->currentMethod = eap_sm_Policy_getNextMethod(sm); + if (sm->m && sm->eap_method_priv) { + sm->m->reset(sm, sm->eap_method_priv); + sm->eap_method_priv = NULL; + } + sm->m = eap_sm_get_eap_methods(sm->currentMethod); + if (sm->m) { + sm->eap_method_priv = sm->m->init(sm); + if (sm->eap_method_priv == NULL) { + wpa_printf(MSG_DEBUG, "EAP: Failed to initialize EAP " + "method %d", sm->currentMethod); + sm->m = NULL; + sm->currentMethod = EAP_TYPE_NONE; + } + } + if (sm->currentMethod == EAP_TYPE_IDENTITY || + sm->currentMethod == EAP_TYPE_NOTIFICATION) + sm->methodState = METHOD_CONTINUE; + else + sm->methodState = METHOD_PROPOSED; +} + + +SM_STATE(EAP, NAK) +{ + struct eap_hdr *nak; + size_t len = 0; + u8 *pos, *nak_list = NULL; + + SM_ENTRY(EAP, NAK); + + if (sm->eap_method_priv) { + sm->m->reset(sm, sm->eap_method_priv); + sm->eap_method_priv = NULL; + } + sm->m = NULL; + + nak = (struct eap_hdr *) sm->eapRespData; + if (nak && sm->eapRespDataLen > sizeof(*nak)) { + len = ntohs(nak->length); + if (len > sm->eapRespDataLen) + len = sm->eapRespDataLen; + pos = (u8 *) (nak + 1); + len -= sizeof(*nak); + if (*pos == EAP_TYPE_NAK) { + pos++; + len--; + nak_list = pos; + } + } + eap_sm_Policy_update(sm, nak_list, len); +} + + +SM_STATE(EAP, SELECT_ACTION) +{ + SM_ENTRY(EAP, SELECT_ACTION); + + sm->decision = eap_sm_Policy_getDecision(sm); +} + + +SM_STATE(EAP, TIMEOUT_FAILURE) +{ + SM_ENTRY(EAP, TIMEOUT_FAILURE); + + eapol_set_bool(sm, EAPOL_eapTimeout, TRUE); +} + + +SM_STATE(EAP, FAILURE) +{ + SM_ENTRY(EAP, FAILURE); + + free(sm->eapReqData); + sm->eapReqData = eap_sm_buildFailure(sm, sm->currentId, + &sm->eapReqDataLen); + if (sm->eapReqData) { + eapol_set_eapReqData(sm, sm->eapReqData, sm->eapReqDataLen); + free(sm->eapReqData); + sm->eapReqData = NULL; + sm->eapReqDataLen = 0; + } + free(sm->lastReqData); + sm->lastReqData = NULL; + sm->lastReqDataLen = 0; + eapol_set_bool(sm, EAPOL_eapFail, TRUE); +} + + +SM_STATE(EAP, SUCCESS) +{ + SM_ENTRY(EAP, SUCCESS); + + free(sm->eapReqData); + sm->eapReqData = eap_sm_buildSuccess(sm, sm->currentId, + &sm->eapReqDataLen); + if (sm->eapReqData) { + eapol_set_eapReqData(sm, sm->eapReqData, sm->eapReqDataLen); + free(sm->eapReqData); + sm->eapReqData = NULL; + sm->eapReqDataLen = 0; + } + free(sm->lastReqData); + sm->lastReqData = NULL; + sm->lastReqDataLen = 0; + if (sm->eapKeyData) { + eapol_set_eapKeyData(sm, sm->eapKeyData, sm->eapKeyDataLen); + } + eapol_set_bool(sm, EAPOL_eapSuccess, TRUE); +} + + +SM_STEP(EAP) +{ + if (eapol_get_bool(sm, EAPOL_eapRestart) && + eapol_get_bool(sm, EAPOL_portEnabled)) + SM_ENTER_GLOBAL(EAP, INITIALIZE); + else if (!eapol_get_bool(sm, EAPOL_portEnabled)) + SM_ENTER_GLOBAL(EAP, DISABLED); + else 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: + if (sm->backend_auth) { + if (!sm->rxResp) + SM_ENTER(EAP, SELECT_ACTION); + else if (sm->rxResp && + (sm->respMethod == EAP_TYPE_NAK || + sm->respMethod == EAP_TYPE_EXPANDED_NAK)) + SM_ENTER(EAP, NAK); + else + SM_ENTER(EAP, PICK_UP_METHOD); + } else { + SM_ENTER(EAP, SELECT_ACTION); + } + break; + case EAP_PICK_UP_METHOD: + if (sm->currentMethod == EAP_TYPE_NONE) { + SM_ENTER(EAP, SELECT_ACTION); + } else { + SM_ENTER(EAP, METHOD_RESPONSE); + } + break; + case EAP_DISABLED: + if (eapol_get_bool(sm, EAPOL_portEnabled)) + SM_ENTER(EAP, INITIALIZE); + break; + case EAP_IDLE: + if (sm->retransWhile == 0) + SM_ENTER(EAP, RETRANSMIT); + else if (eapol_get_bool(sm, EAPOL_eapResp)) + SM_ENTER(EAP, RECEIVED); + break; + case EAP_RETRANSMIT: + if (sm->retransCount > sm->MaxRetrans) + SM_ENTER(EAP, TIMEOUT_FAILURE); + else + SM_ENTER(EAP, IDLE); + break; + case EAP_RECEIVED: + if (sm->rxResp && (sm->respId == sm->currentId) && + (sm->respMethod == EAP_TYPE_NAK || + sm->respMethod == EAP_TYPE_EXPANDED_NAK) + && (sm->methodState == METHOD_PROPOSED)) + SM_ENTER(EAP, NAK); + else if (sm->rxResp && (sm->respId == sm->currentId) && + (sm->respMethod == sm->currentMethod)) + SM_ENTER(EAP, INTEGRITY_CHECK); + else + SM_ENTER(EAP, DISCARD); + break; + case EAP_DISCARD: + SM_ENTER(EAP, IDLE); + break; + case EAP_SEND_REQUEST: + SM_ENTER(EAP, IDLE); + break; + case EAP_INTEGRITY_CHECK: + if (sm->ignore) + SM_ENTER(EAP, DISCARD); + else + SM_ENTER(EAP, METHOD_RESPONSE); + break; + case EAP_METHOD_REQUEST: + SM_ENTER(EAP, SEND_REQUEST); + break; + case EAP_METHOD_RESPONSE: + if (sm->methodState == METHOD_END) + SM_ENTER(EAP, SELECT_ACTION); + else + SM_ENTER(EAP, METHOD_REQUEST); + break; + case EAP_PROPOSE_METHOD: + SM_ENTER(EAP, METHOD_REQUEST); + break; + case EAP_NAK: + SM_ENTER(EAP, SELECT_ACTION); + break; + case EAP_SELECT_ACTION: + if (sm->decision == DECISION_FAILURE) + SM_ENTER(EAP, FAILURE); + else if (sm->decision == DECISION_SUCCESS) + SM_ENTER(EAP, SUCCESS); + else + SM_ENTER(EAP, PROPOSE_METHOD); + break; + case EAP_TIMEOUT_FAILURE: + break; + case EAP_FAILURE: + break; + case EAP_SUCCESS: + break; + } +} + + +static int eap_sm_calculateTimeout(struct eap_sm *sm, int retransCount, + int eapSRTT, int eapRTTVAR, + int methodTimeout) +{ + /* For now, retransmission is done in EAPOL state machines, so make + * sure EAP state machine does not end up trying to retransmit packets. + */ + return 1; +} + + +static void eap_sm_parseEapResp(struct eap_sm *sm, u8 *resp, size_t len) +{ + struct eap_hdr *hdr; + size_t plen; + + /* parse rxResp, respId, respMethod */ + sm->rxResp = FALSE; + sm->respId = -1; + sm->respMethod = EAP_TYPE_NONE; + + if (resp == NULL || len < sizeof(*hdr)) + return; + + hdr = (struct eap_hdr *) resp; + plen = ntohs(hdr->length); + if (plen > len) { + wpa_printf(MSG_DEBUG, "EAP: Ignored truncated EAP-Packet " + "(len=%lu plen=%lu)", (unsigned long) len, + (unsigned long) plen); + return; + } + + sm->respId = hdr->identifier; + + if (hdr->code == EAP_CODE_RESPONSE) + sm->rxResp = TRUE; + + if (len > sizeof(*hdr)) + sm->respMethod = *((u8 *) (hdr + 1)); + + wpa_printf(MSG_DEBUG, "EAP: parseEapResp: rxResp=%d respId=%d " + "respMethod=%d", sm->rxResp, sm->respId, sm->respMethod); +} + + +static u8 * eap_sm_buildSuccess(struct eap_sm *sm, int id, size_t *len) +{ + struct eap_hdr *resp; + wpa_printf(MSG_DEBUG, "EAP: Building EAP-Success (id=%d)", id); + + *len = sizeof(*resp); + resp = malloc(*len); + if (resp == NULL) + return NULL; + resp->code = EAP_CODE_SUCCESS; + resp->identifier = id; + resp->length = htons(*len); + + return (u8 *) resp; +} + + +static u8 * eap_sm_buildFailure(struct eap_sm *sm, int id, size_t *len) +{ + struct eap_hdr *resp; + wpa_printf(MSG_DEBUG, "EAP: Building EAP-Failure (id=%d)", id); + + *len = sizeof(*resp); + resp = malloc(*len); + if (resp == NULL) + return NULL; + resp->code = EAP_CODE_FAILURE; + resp->identifier = id; + resp->length = htons(*len); + + return (u8 *) resp; +} + + +static int eap_sm_nextId(struct eap_sm *sm, int id) +{ + if (id < 0) { + /* RFC 3748 Ch 4.1: recommended to initalize Identifier with a + * random number */ + id = rand() & 0xff; + if (id != sm->lastId) + return id; + } + return (id + 1) & 0xff; +} + + +void eap_sm_process_nak(struct eap_sm *sm, u8 *nak_list, size_t len) +{ + int i, j; + + wpa_printf(MSG_MSGDUMP, "EAP: processing NAK (current EAP method " + "index %d)", sm->user_eap_method_index); + + wpa_hexdump(MSG_MSGDUMP, "EAP: configured methods", + sm->user->methods, EAP_MAX_METHODS); + wpa_hexdump(MSG_MSGDUMP, "EAP: list of methods supported by the peer", + nak_list, len); + + i = sm->user_eap_method_index; + while (i < EAP_MAX_METHODS && sm->user->methods[i] != EAP_TYPE_NONE) { + for (j = 0; j < len; j++) { + if (nak_list[j] == sm->user->methods[i]) { + break; + } + } + + if (j < len) { + /* found */ + i++; + continue; + } + + /* not found - remove from the list */ + memmove(&sm->user->methods[i], &sm->user->methods[i + 1], + EAP_MAX_METHODS - i - 1); + sm->user->methods[EAP_MAX_METHODS - 1] = EAP_TYPE_NONE; + } + + wpa_hexdump(MSG_MSGDUMP, "EAP: new list of configured methods", + sm->user->methods, EAP_MAX_METHODS); +} + + +static void eap_sm_Policy_update(struct eap_sm *sm, u8 *nak_list, size_t len) +{ + if (nak_list == NULL || sm == NULL || sm->user == NULL) + return; + + if (sm->user->phase2) { + wpa_printf(MSG_DEBUG, "EAP: EAP-Nak received after Phase2 user" + " info was selected - reject"); + sm->decision = DECISION_FAILURE; + return; + } + + eap_sm_process_nak(sm, nak_list, len); +} + + +static EapType eap_sm_Policy_getNextMethod(struct eap_sm *sm) +{ + EapType next; + + /* In theory, there should be no problems with starting + * re-authentication with something else than EAP-Request/Identity and + * this does indeed work with wpa_supplicant. However, at least Funk + * Supplicant seemed to ignore re-auth if it skipped + * EAP-Request/Identity. + * Re-auth sets currentId == -1, so that can be used here to select + * whether Identity needs to be requested again. */ + if (sm->identity == NULL || sm->currentId == -1) { + next = EAP_TYPE_IDENTITY; + sm->update_user = TRUE; + } else if (sm->user && sm->user_eap_method_index < EAP_MAX_METHODS && + sm->user->methods[sm->user_eap_method_index] != + EAP_TYPE_NONE) { + next = sm->user->methods[sm->user_eap_method_index++]; + } else { + next = EAP_TYPE_NONE; + } + wpa_printf(MSG_DEBUG, "EAP: getNextMethod: type %d", next); + return next; +} + + +static int eap_sm_Policy_getDecision(struct eap_sm *sm) +{ + if (sm->m && sm->currentMethod != EAP_TYPE_IDENTITY && + sm->m->isSuccess(sm, sm->eap_method_priv)) { + wpa_printf(MSG_DEBUG, "EAP: getDecision: method succeeded -> " + "SUCCESS"); + sm->update_user = TRUE; + return DECISION_SUCCESS; + } + + if (sm->m && sm->m->isDone(sm, sm->eap_method_priv) && + !sm->m->isSuccess(sm, sm->eap_method_priv)) { + wpa_printf(MSG_DEBUG, "EAP: getDecision: method failed -> " + "FAILURE"); + sm->update_user = TRUE; + return DECISION_FAILURE; + } + + if ((sm->user == NULL || sm->update_user) && sm->identity) { + if (eap_user_get(sm, sm->identity, sm->identity_len, 0) != 0) { + wpa_printf(MSG_DEBUG, "EAP: getDecision: user not " + "found from database -> FAILURE"); + return DECISION_FAILURE; + } + sm->update_user = FALSE; + } + + if (sm->user && sm->user_eap_method_index < EAP_MAX_METHODS && + sm->user->methods[sm->user_eap_method_index] != EAP_TYPE_NONE) { + wpa_printf(MSG_DEBUG, "EAP: getDecision: another method " + "available -> CONTINUE"); + return DECISION_CONTINUE; + } + + if (sm->identity == NULL || sm->currentId == -1) { + wpa_printf(MSG_DEBUG, "EAP: getDecision: no identity known " + "yet -> CONTINUE"); + return DECISION_CONTINUE; + } + + wpa_printf(MSG_DEBUG, "EAP: getDecision: no more methods available -> " + "FAILURE"); + return DECISION_FAILURE; +} + + +static Boolean eap_sm_Policy_doPickUp(struct eap_sm *sm, EapType method) +{ + return method == EAP_TYPE_IDENTITY ? TRUE : FALSE; +} + + +int eap_sm_step(struct eap_sm *sm) +{ + int res = 0; + do { + sm->changed = FALSE; + SM_STEP_RUN(EAP); + if (sm->changed) + res = 1; + } while (sm->changed); + return res; +} + + +u8 eap_get_type(const char *name) +{ + int i; + for (i = 0; i < NUM_EAP_METHODS; i++) { + if (strcmp(eap_methods[i]->name, name) == 0) + return eap_methods[i]->method; + } + return EAP_TYPE_NONE; +} + + +void eap_set_eapRespData(struct eap_sm *sm, const u8 *eapRespData, + size_t eapRespDataLen) +{ + if (sm == NULL) + return; + free(sm->eapRespData); + sm->eapRespData = malloc(eapRespDataLen); + if (sm->eapRespData == NULL) + return; + memcpy(sm->eapRespData, eapRespData, eapRespDataLen); + sm->eapRespDataLen = eapRespDataLen; + wpa_hexdump(MSG_MSGDUMP, "EAP: EAP-Response received", + eapRespData, eapRespDataLen); +} + + +static void eap_user_free(struct eap_user *user) +{ + if (user == NULL) + return; + free(user->password); + user->password = NULL; + free(user); +} + + +struct eap_sm * eap_sm_init(void *eapol_ctx, struct eapol_callbacks *eapol_cb, + struct eap_config *eap_conf) +{ + struct eap_sm *sm; + + sm = malloc(sizeof(*sm)); + if (sm == NULL) + return NULL; + memset(sm, 0, sizeof(*sm)); + sm->eapol_ctx = eapol_ctx; + sm->eapol_cb = eapol_cb; + sm->MaxRetrans = 10; + sm->ssl_ctx = eap_conf->ssl_ctx; + sm->eap_sim_db_priv = eap_conf->eap_sim_db_priv; + sm->backend_auth = eap_conf->backend_auth; + + wpa_printf(MSG_DEBUG, "EAP: State machine created"); + + return sm; +} + + +void eap_sm_deinit(struct eap_sm *sm) +{ + if (sm == NULL) + return; + wpa_printf(MSG_DEBUG, "EAP: State machine removed"); + if (sm->m && sm->eap_method_priv) + sm->m->reset(sm, sm->eap_method_priv); + free(sm->eapReqData); + free(sm->eapKeyData); + free(sm->lastReqData); + free(sm->eapRespData); + free(sm->identity); + eap_user_free(sm->user); + free(sm); +} + + +void eap_sm_notify_cached(struct eap_sm *sm) +{ + if (sm == NULL) + return; + + sm->EAP_state = EAP_SUCCESS; +} diff --git a/contrib/hostapd-0.4.9/eap.h b/contrib/hostapd-0.4.9/eap.h new file mode 100644 index 0000000000..c5c62eb57c --- /dev/null +++ b/contrib/hostapd-0.4.9/eap.h @@ -0,0 +1,89 @@ +#ifndef EAP_H +#define EAP_H + +#include "defs.h" +#include "eap_defs.h" + +struct eap_sm; + +#define EAP_MAX_METHODS 8 +struct eap_user { + u8 methods[EAP_MAX_METHODS]; + u8 *password; + size_t password_len; + int phase2; + int force_version; +}; + +enum eapol_bool_var { + EAPOL_eapSuccess, EAPOL_eapRestart, EAPOL_eapFail, EAPOL_eapResp, + EAPOL_eapReq, EAPOL_eapNoReq, EAPOL_portEnabled, EAPOL_eapTimeout +}; + +struct eapol_callbacks { + Boolean (*get_bool)(void *ctx, enum eapol_bool_var variable); + void (*set_bool)(void *ctx, enum eapol_bool_var variable, + Boolean value); + void (*set_eapReqData)(void *ctx, const u8 *eapReqData, + size_t eapReqDataLen); + void (*set_eapKeyData)(void *ctx, const u8 *eapKeyData, + size_t eapKeyDataLen); + int (*get_eap_user)(void *ctx, const u8 *identity, size_t identity_len, + int phase2, struct eap_user *user); + const char * (*get_eap_req_id_text)(void *ctx, size_t *len); +}; + +struct eap_config { + void *ssl_ctx; + void *eap_sim_db_priv; + Boolean backend_auth; +}; + + +#ifdef EAP_SERVER + +struct eap_sm * eap_sm_init(void *eapol_ctx, struct eapol_callbacks *eapol_cb, + struct eap_config *eap_conf); +void eap_sm_deinit(struct eap_sm *sm); +int eap_sm_step(struct eap_sm *sm); +u8 eap_get_type(const char *name); +void eap_set_eapRespData(struct eap_sm *sm, const u8 *eapRespData, + size_t eapRespDataLen); +void eap_sm_notify_cached(struct eap_sm *sm); + +#else /* EAP_SERVER */ + +static inline struct eap_sm * eap_sm_init(void *eapol_ctx, + struct eapol_callbacks *eapol_cb, + struct eap_config *eap_conf) +{ + return NULL; +} + +static inline void eap_sm_deinit(struct eap_sm *sm) +{ +} + +static inline int eap_sm_step(struct eap_sm *sm) +{ + return 0; +} + +static inline u8 eap_get_type(const char *name) +{ + return EAP_TYPE_NONE; +} + +static inline void eap_set_eapRespData(struct eap_sm *sm, + const u8 *eapRespData, + size_t eapRespDataLen) +{ +} + +static inline void eap_sm_notify_cached(struct eap_sm *sm) +{ +} + +#endif /* EAP_SERVER */ + +#endif /* EAP_H */ diff --git a/contrib/hostapd-0.4.9/eap_defs.h b/contrib/hostapd-0.4.9/eap_defs.h new file mode 100644 index 0000000000..9cd4490515 --- /dev/null +++ b/contrib/hostapd-0.4.9/eap_defs.h @@ -0,0 +1,56 @@ +/* + * WPA Supplicant/hostapd / Shared EAP definitions + * Copyright (c) 2004-2005, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#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; network byte order */ + /* 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 /* RFC 3748 */, + EAP_TYPE_NOTIFICATION = 2 /* RFC 3748 */, + EAP_TYPE_NAK = 3 /* Response only, RFC 3748 */, + EAP_TYPE_MD5 = 4, /* RFC 3748 */ + EAP_TYPE_OTP = 5 /* RFC 3748 */, + EAP_TYPE_GTC = 6, /* RFC 3748 */ + 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_PAX = 46, /* draft-clancy-eap-pax-04.txt */ + EAP_TYPE_EXPANDED_NAK = 254 /* RFC 3748 */, + EAP_TYPE_PSK = 255 /* EXPERIMENTAL - type not yet allocated + * draft-bersani-eap-psk-09 */ +} EapType; + +#endif /* EAP_DEFS_H */ diff --git a/contrib/hostapd-0.4.9/eap_gtc.c b/contrib/hostapd-0.4.9/eap_gtc.c new file mode 100644 index 0000000000..674f83735f --- /dev/null +++ b/contrib/hostapd-0.4.9/eap_gtc.c @@ -0,0 +1,158 @@ +/* + * hostapd / EAP-GTC (RFC 3748) + * Copyright (c) 2004, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include +#include +#include +#include + +#include "hostapd.h" +#include "common.h" +#include "eap_i.h" + + +struct eap_gtc_data { + enum { CONTINUE, SUCCESS, FAILURE } state; +}; + + +static void * eap_gtc_init(struct eap_sm *sm) +{ + struct eap_gtc_data *data; + + data = malloc(sizeof(*data)); + if (data == NULL) + return data; + memset(data, 0, sizeof(*data)); + data->state = CONTINUE; + + return data; +} + + +static void eap_gtc_reset(struct eap_sm *sm, void *priv) +{ + struct eap_gtc_data *data = priv; + free(data); +} + + +static u8 * eap_gtc_buildReq(struct eap_sm *sm, void *priv, int id, + size_t *reqDataLen) +{ + struct eap_gtc_data *data = priv; + struct eap_hdr *req; + u8 *pos; + char *msg = "Password"; + size_t msg_len; + + msg_len = strlen(msg); + *reqDataLen = sizeof(*req) + 1 + msg_len; + req = malloc(*reqDataLen); + if (req == NULL) { + wpa_printf(MSG_ERROR, "EAP-GTC: Failed to allocate memory for " + "request"); + data->state = FAILURE; + return NULL; + } + + req->code = EAP_CODE_REQUEST; + req->identifier = id; + req->length = htons(*reqDataLen); + pos = (u8 *) (req + 1); + *pos++ = EAP_TYPE_GTC; + memcpy(pos, msg, msg_len); + + data->state = CONTINUE; + + return (u8 *) req; +} + + +static Boolean eap_gtc_check(struct eap_sm *sm, void *priv, + u8 *respData, size_t respDataLen) +{ + struct eap_hdr *resp; + u8 *pos; + size_t len; + + resp = (struct eap_hdr *) respData; + pos = (u8 *) (resp + 1); + if (respDataLen < sizeof(*resp) + 2 || *pos != EAP_TYPE_GTC || + (len = ntohs(resp->length)) > respDataLen) { + wpa_printf(MSG_INFO, "EAP-GTC: Invalid frame"); + return TRUE; + } + + return FALSE; +} + + +static void eap_gtc_process(struct eap_sm *sm, void *priv, + u8 *respData, size_t respDataLen) +{ + struct eap_gtc_data *data = priv; + struct eap_hdr *resp; + u8 *pos; + size_t rlen; + + if (sm->user == NULL || sm->user->password == NULL) { + wpa_printf(MSG_INFO, "EAP-GTC: Password not configured"); + data->state = FAILURE; + return; + } + + resp = (struct eap_hdr *) respData; + pos = (u8 *) (resp + 1); + pos++; + rlen = ntohs(resp->length) - sizeof(*resp) - 1; + wpa_hexdump_key(MSG_MSGDUMP, "EAP-GTC: Response", pos, rlen); + + if (rlen != sm->user->password_len || + memcmp(pos, sm->user->password, rlen) != 0) { + wpa_printf(MSG_DEBUG, "EAP-GTC: Done - Failure"); + data->state = FAILURE; + } else { + wpa_printf(MSG_DEBUG, "EAP-GTC: Done - Success"); + data->state = SUCCESS; + } +} + + +static Boolean eap_gtc_isDone(struct eap_sm *sm, void *priv) +{ + struct eap_gtc_data *data = priv; + return data->state != CONTINUE; +} + + +static Boolean eap_gtc_isSuccess(struct eap_sm *sm, void *priv) +{ + struct eap_gtc_data *data = priv; + return data->state == SUCCESS; +} + + +const struct eap_method eap_method_gtc = +{ + .method = EAP_TYPE_GTC, + .name = "GTC", + .init = eap_gtc_init, + .reset = eap_gtc_reset, + .buildReq = eap_gtc_buildReq, + .check = eap_gtc_check, + .process = eap_gtc_process, + .isDone = eap_gtc_isDone, + .isSuccess = eap_gtc_isSuccess, +}; diff --git a/contrib/hostapd-0.4.9/eap_i.h b/contrib/hostapd-0.4.9/eap_i.h new file mode 100644 index 0000000000..4e803f905d --- /dev/null +++ b/contrib/hostapd-0.4.9/eap_i.h @@ -0,0 +1,112 @@ +#ifndef EAP_I_H +#define EAP_I_H + +#include "eap.h" + +/* draft-ietf-eap-statemachine-05.pdf - EAP Standalone Authenticator */ + +struct eap_method { + EapType method; + const char *name; + + void * (*init)(struct eap_sm *sm); + void * (*initPickUp)(struct eap_sm *sm); + void (*reset)(struct eap_sm *sm, void *priv); + + u8 * (*buildReq)(struct eap_sm *sm, void *priv, int id, + size_t *reqDataLen); + int (*getTimeout)(struct eap_sm *sm, void *priv); + Boolean (*check)(struct eap_sm *sm, void *priv, + u8 *respData, size_t respDataLen); + void (*process)(struct eap_sm *sm, void *priv, + u8 *respData, size_t respDataLen); + Boolean (*isDone)(struct eap_sm *sm, void *priv); + u8 * (*getKey)(struct eap_sm *sm, void *priv, size_t *len); + /* isSuccess is not specified in draft-ietf-eap-statemachine-05.txt, + * but it is useful in implementing Policy.getDecision() */ + Boolean (*isSuccess)(struct eap_sm *sm, void *priv); +}; + +struct eap_sm { + enum { + EAP_DISABLED, EAP_INITIALIZE, EAP_IDLE, EAP_RECEIVED, + EAP_INTEGRITY_CHECK, EAP_METHOD_RESPONSE, EAP_METHOD_REQUEST, + EAP_PROPOSE_METHOD, EAP_SELECT_ACTION, EAP_SEND_REQUEST, + EAP_DISCARD, EAP_NAK, EAP_RETRANSMIT, EAP_SUCCESS, EAP_FAILURE, + EAP_TIMEOUT_FAILURE, EAP_PICK_UP_METHOD + } EAP_state; + + /* Constants */ + int MaxRetrans; + + /* Lower layer to standalone authenticator variables */ + /* eapResp: eapol_sm->be_auth.eapResp */ + /* portEnabled: eapol_sm->portEnabled */ + /* eapRestart: eapol_sm->auth_pae.eapRestart */ + u8 *eapRespData; + size_t eapRespDataLen; + int retransWhile; + int eapSRTT; + int eapRTTVAR; + + /* Standalone authenticator to lower layer variables */ + /* eapReq: eapol_sm->be_auth.eapReq */ + /* eapNoReq: eapol_sm->be_auth.eapNoReq */ + /* eapSuccess: eapol_sm->eapSuccess */ + /* eapFail: eapol_sm->eapFail */ + /* eapTimeout: eapol_sm->eapTimeout */ + u8 *eapReqData; + size_t eapReqDataLen; + u8 *eapKeyData; /* also eapKeyAvailable (boolean) */ + size_t eapKeyDataLen; + + /* Standalone authenticator state machine local variables */ + + /* Long-term (maintained betwen packets) */ + EapType currentMethod; + int currentId; + enum { + METHOD_PROPOSED, METHOD_CONTINUE, METHOD_END + } methodState; + int retransCount; + u8 *lastReqData; + size_t lastReqDataLen; + int methodTimeout; + + /* Short-term (not maintained between packets) */ + Boolean rxResp; + int respId; + EapType respMethod; + Boolean ignore; + enum { + DECISION_SUCCESS, DECISION_FAILURE, DECISION_CONTINUE + } decision; + + /* Miscellaneous variables */ + const struct eap_method *m; /* selected EAP method */ + /* not defined in draft-ietf-eap-statemachine-02 */ + Boolean changed; + void *eapol_ctx, *msg_ctx; + struct eapol_callbacks *eapol_cb; + void *eap_method_priv; + u8 *identity; + size_t identity_len; + int lastId; /* Identifier used in the last EAP-Packet */ + struct eap_user *user; + int user_eap_method_index; + int init_phase2; + void *ssl_ctx; + enum { TLV_REQ_NONE, TLV_REQ_SUCCESS, TLV_REQ_FAILURE } tlv_request; + void *eap_sim_db_priv; + Boolean backend_auth; + Boolean update_user; + + int num_rounds; +}; + +const struct eap_method * eap_sm_get_eap_methods(int method); +int eap_user_get(struct eap_sm *sm, const u8 *identity, size_t identity_len, + int phase2); +void eap_sm_process_nak(struct eap_sm *sm, u8 *nak_list, size_t len); + +#endif /* EAP_I_H */ diff --git a/contrib/hostapd-0.4.9/eap_identity.c b/contrib/hostapd-0.4.9/eap_identity.c new file mode 100644 index 0000000000..54efc47961 --- /dev/null +++ b/contrib/hostapd-0.4.9/eap_identity.c @@ -0,0 +1,185 @@ +/* + * hostapd / EAP-Identity + * Copyright (c) 2004-2005, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include +#include +#include +#include + +#include "hostapd.h" +#include "common.h" +#include "eap_i.h" + + +struct eap_identity_data { + enum { CONTINUE, SUCCESS, FAILURE } state; + int pick_up; +}; + + +static void * eap_identity_init(struct eap_sm *sm) +{ + struct eap_identity_data *data; + + data = malloc(sizeof(*data)); + if (data == NULL) + return data; + memset(data, 0, sizeof(*data)); + data->state = CONTINUE; + + return data; +} + + +static void * eap_identity_initPickUp(struct eap_sm *sm) +{ + struct eap_identity_data *data; + data = eap_identity_init(sm); + if (data) { + data->pick_up = 1; + } + return data; +} + + +static void eap_identity_reset(struct eap_sm *sm, void *priv) +{ + struct eap_identity_data *data = priv; + free(data); +} + + +static u8 * eap_identity_buildReq(struct eap_sm *sm, void *priv, int id, + size_t *reqDataLen) +{ + struct eap_identity_data *data = priv; + struct eap_hdr *req; + u8 *pos; + const char *req_data; + size_t req_data_len; + + if (sm->eapol_cb->get_eap_req_id_text) { + req_data = sm->eapol_cb->get_eap_req_id_text(sm->eapol_ctx, + &req_data_len); + } else { + req_data = NULL; + req_data_len = 0; + } + *reqDataLen = sizeof(*req) + 1 + req_data_len; + req = malloc(*reqDataLen); + if (req == NULL) { + wpa_printf(MSG_ERROR, "EAP-Identity: Failed to allocate " + "memory for request"); + data->state = FAILURE; + return NULL; + } + + req->code = EAP_CODE_REQUEST; + req->identifier = id; + req->length = htons(*reqDataLen); + pos = (u8 *) (req + 1); + *pos++ = EAP_TYPE_IDENTITY; + if (req_data) + memcpy(pos, req_data, req_data_len); + + return (u8 *) req; +} + + +static Boolean eap_identity_check(struct eap_sm *sm, void *priv, + u8 *respData, size_t respDataLen) +{ + struct eap_hdr *resp; + u8 *pos; + size_t len; + + resp = (struct eap_hdr *) respData; + pos = (u8 *) (resp + 1); + if (respDataLen < sizeof(*resp) + 1 || *pos != EAP_TYPE_IDENTITY || + (len = ntohs(resp->length)) > respDataLen) { + wpa_printf(MSG_INFO, "EAP-Identity: Invalid frame"); + return TRUE; + } + + return FALSE; +} + + +static void eap_identity_process(struct eap_sm *sm, void *priv, + u8 *respData, size_t respDataLen) +{ + struct eap_identity_data *data = priv; + struct eap_hdr *resp; + u8 *pos; + int len; + + if (data->pick_up) { + if (eap_identity_check(sm, data, respData, respDataLen)) { + wpa_printf(MSG_DEBUG, "EAP-Identity: failed to pick " + "up already started negotiation"); + data->state = FAILURE; + return; + } + data->pick_up = 0; + } + + resp = (struct eap_hdr *) respData; + len = ntohs(resp->length); + pos = (u8 *) (resp + 1); + pos++; + len -= sizeof(*resp) + 1; + if (len < 0) { + data->state = FAILURE; + return; + } + wpa_hexdump_ascii(MSG_DEBUG, "EAP-Identity: Peer identity", pos, len); + free(sm->identity); + sm->identity = malloc(len); + if (sm->identity == NULL) { + data->state = FAILURE; + } else { + memcpy(sm->identity, pos, len); + sm->identity_len = len; + data->state = SUCCESS; + } +} + + +static Boolean eap_identity_isDone(struct eap_sm *sm, void *priv) +{ + struct eap_identity_data *data = priv; + return data->state != CONTINUE; +} + + +static Boolean eap_identity_isSuccess(struct eap_sm *sm, void *priv) +{ + struct eap_identity_data *data = priv; + return data->state == SUCCESS; +} + + +const struct eap_method eap_method_identity = +{ + .method = EAP_TYPE_IDENTITY, + .name = "Identity", + .init = eap_identity_init, + .initPickUp = eap_identity_initPickUp, + .reset = eap_identity_reset, + .buildReq = eap_identity_buildReq, + .check = eap_identity_check, + .process = eap_identity_process, + .isDone = eap_identity_isDone, + .isSuccess = eap_identity_isSuccess, +}; diff --git a/contrib/hostapd-0.4.9/eap_md5.c b/contrib/hostapd-0.4.9/eap_md5.c new file mode 100644 index 0000000000..d776c8c827 --- /dev/null +++ b/contrib/hostapd-0.4.9/eap_md5.c @@ -0,0 +1,184 @@ +/* + * hostapd / EAP-MD5 server + * Copyright (c) 2004-2005, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include +#include +#include +#include + +#include "hostapd.h" +#include "common.h" +#include "eap_i.h" +#include "md5.h" +#include "crypto.h" + + +#define CHALLENGE_LEN 16 + +struct eap_md5_data { + u8 challenge[CHALLENGE_LEN]; + enum { CONTINUE, SUCCESS, FAILURE } state; +}; + + +static void * eap_md5_init(struct eap_sm *sm) +{ + struct eap_md5_data *data; + + data = malloc(sizeof(*data)); + if (data == NULL) + return data; + memset(data, 0, sizeof(*data)); + data->state = CONTINUE; + + return data; +} + + +static void eap_md5_reset(struct eap_sm *sm, void *priv) +{ + struct eap_md5_data *data = priv; + free(data); +} + + +static u8 * eap_md5_buildReq(struct eap_sm *sm, void *priv, int id, + size_t *reqDataLen) +{ + struct eap_md5_data *data = priv; + struct eap_hdr *req; + u8 *pos; + + if (hostapd_get_rand(data->challenge, CHALLENGE_LEN)) { + wpa_printf(MSG_ERROR, "EAP-MD5: Failed to get random data"); + data->state = FAILURE; + return NULL; + } + + *reqDataLen = sizeof(*req) + 2 + CHALLENGE_LEN; + req = malloc(*reqDataLen); + if (req == NULL) { + wpa_printf(MSG_ERROR, "EAP-MD5: Failed to allocate memory for " + "request"); + data->state = FAILURE; + return NULL; + } + + req->code = EAP_CODE_REQUEST; + req->identifier = id; + req->length = htons(*reqDataLen); + pos = (u8 *) (req + 1); + *pos++ = EAP_TYPE_MD5; + *pos++ = CHALLENGE_LEN; + memcpy(pos, data->challenge, CHALLENGE_LEN); + wpa_hexdump(MSG_MSGDUMP, "EAP-MD5: Challenge", pos, CHALLENGE_LEN); + + data->state = CONTINUE; + + return (u8 *) req; +} + + +static Boolean eap_md5_check(struct eap_sm *sm, void *priv, + u8 *respData, size_t respDataLen) +{ + struct eap_hdr *resp; + u8 *pos; + size_t len; + + resp = (struct eap_hdr *) respData; + pos = (u8 *) (resp + 1); + if (respDataLen < sizeof(*resp) + 2 || *pos != EAP_TYPE_MD5 || + (len = ntohs(resp->length)) > respDataLen) { + wpa_printf(MSG_INFO, "EAP-MD5: Invalid frame"); + return TRUE; + } + pos++; + if (*pos != MD5_MAC_LEN || + sizeof(*resp) + 2 + MD5_MAC_LEN > len) { + wpa_printf(MSG_INFO, "EAP-MD5: Invalid response " + "(response_len=%d respDataLen=%lu", + *pos, (unsigned long) respDataLen); + return TRUE; + } + + return FALSE; +} + + +static void eap_md5_process(struct eap_sm *sm, void *priv, + u8 *respData, size_t respDataLen) +{ + struct eap_md5_data *data = priv; + struct eap_hdr *resp; + u8 *pos; + const u8 *addr[3]; + size_t len[3]; + u8 hash[MD5_MAC_LEN]; + + if (sm->user == NULL || sm->user->password == NULL) { + wpa_printf(MSG_INFO, "EAP-MD5: Password not configured"); + data->state = FAILURE; + return; + } + + resp = (struct eap_hdr *) respData; + pos = (u8 *) (resp + 1); + pos += 2; /* Skip type and len */ + wpa_hexdump(MSG_MSGDUMP, "EAP-MD5: Response", pos, MD5_MAC_LEN); + + addr[0] = &resp->identifier; + len[0] = 1; + addr[1] = sm->user->password; + len[1] = sm->user->password_len; + addr[2] = data->challenge; + len[2] = CHALLENGE_LEN; + md5_vector(3, addr, len, hash); + + if (memcmp(hash, pos, MD5_MAC_LEN) == 0) { + wpa_printf(MSG_DEBUG, "EAP-MD5: Done - Success"); + data->state = SUCCESS; + } else { + wpa_printf(MSG_DEBUG, "EAP-MD5: Done - Failure"); + data->state = FAILURE; + } +} + + +static Boolean eap_md5_isDone(struct eap_sm *sm, void *priv) +{ + struct eap_md5_data *data = priv; + return data->state != CONTINUE; +} + + +static Boolean eap_md5_isSuccess(struct eap_sm *sm, void *priv) +{ + struct eap_md5_data *data = priv; + return data->state == SUCCESS; +} + + +const struct eap_method eap_method_md5 = +{ + .method = EAP_TYPE_MD5, + .name = "MD5", + .init = eap_md5_init, + .reset = eap_md5_reset, + .buildReq = eap_md5_buildReq, + .check = eap_md5_check, + .process = eap_md5_process, + .isDone = eap_md5_isDone, + .isSuccess = eap_md5_isSuccess, +}; diff --git a/contrib/hostapd-0.4.9/eap_mschapv2.c b/contrib/hostapd-0.4.9/eap_mschapv2.c new file mode 100644 index 0000000000..097da25417 --- /dev/null +++ b/contrib/hostapd-0.4.9/eap_mschapv2.c @@ -0,0 +1,483 @@ +/* + * hostapd / EAP-MSCHAPv2 (draft-kamath-pppext-eap-mschapv2-00.txt) server + * Copyright (c) 2004-2005, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include +#include +#include +#include + +#include "hostapd.h" +#include "common.h" +#include "eap_i.h" +#include "ms_funcs.h" + + +struct eap_mschapv2_hdr { + u8 code; + u8 identifier; + u16 length; /* including code, identifier, and length */ + u8 type; /* EAP_TYPE_MSCHAPV2 */ + u8 op_code; /* MSCHAPV2_OP_* */ + u8 mschapv2_id; /* must be changed for challenges, but not for + * success/failure */ + u8 ms_length[2]; /* Note: misaligned; length - 5 */ + /* followed by data */ +} __attribute__ ((packed)); + +#define MSCHAPV2_OP_CHALLENGE 1 +#define MSCHAPV2_OP_RESPONSE 2 +#define MSCHAPV2_OP_SUCCESS 3 +#define MSCHAPV2_OP_FAILURE 4 +#define MSCHAPV2_OP_CHANGE_PASSWORD 7 + +#define MSCHAPV2_RESP_LEN 49 + +#define ERROR_RESTRICTED_LOGON_HOURS 646 +#define ERROR_ACCT_DISABLED 647 +#define ERROR_PASSWD_EXPIRED 648 +#define ERROR_NO_DIALIN_PERMISSION 649 +#define ERROR_AUTHENTICATION_FAILURE 691 +#define ERROR_CHANGING_PASSWORD 709 + +#define PASSWD_CHANGE_CHAL_LEN 16 + + +#define CHALLENGE_LEN 16 + +struct eap_mschapv2_data { + u8 auth_challenge[CHALLENGE_LEN]; + u8 auth_response[20]; + enum { CHALLENGE, SUCCESS_REQ, FAILURE_REQ, SUCCESS, FAILURE } state; + u8 resp_mschapv2_id; +}; + + +static void * eap_mschapv2_init(struct eap_sm *sm) +{ + struct eap_mschapv2_data *data; + + data = malloc(sizeof(*data)); + if (data == NULL) + return data; + memset(data, 0, sizeof(*data)); + data->state = CHALLENGE; + + return data; +} + + +static void eap_mschapv2_reset(struct eap_sm *sm, void *priv) +{ + struct eap_mschapv2_data *data = priv; + free(data); +} + + +static u8 * eap_mschapv2_build_challenge(struct eap_sm *sm, + struct eap_mschapv2_data *data, + int id, size_t *reqDataLen) +{ + struct eap_mschapv2_hdr *req; + u8 *pos; + char *name = "hostapd"; /* TODO: make this configurable */ + + if (hostapd_get_rand(data->auth_challenge, CHALLENGE_LEN)) { + wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to get random " + "data"); + data->state = FAILURE; + return NULL; + } + + *reqDataLen = sizeof(*req) + 1 + CHALLENGE_LEN + strlen(name); + req = malloc(*reqDataLen); + if (req == NULL) { + wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to allocate memory" + " for request"); + data->state = FAILURE; + return NULL; + } + + req->code = EAP_CODE_REQUEST; + req->identifier = id; + req->length = htons(*reqDataLen); + req->type = EAP_TYPE_MSCHAPV2; + req->op_code = MSCHAPV2_OP_CHALLENGE; + req->mschapv2_id = id; + req->ms_length[0] = (*reqDataLen - 5) >> 8; + req->ms_length[1] = (*reqDataLen - 5) & 0xff; + pos = (u8 *) (req + 1); + *pos++ = CHALLENGE_LEN; + memcpy(pos, data->auth_challenge, CHALLENGE_LEN); + wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: Challenge", pos, + CHALLENGE_LEN); + pos += CHALLENGE_LEN; + memcpy(pos, name, strlen(name)); + + return (u8 *) req; +} + + +static u8 * eap_mschapv2_build_success_req(struct eap_sm *sm, + struct eap_mschapv2_data *data, + int id, size_t *reqDataLen) +{ + struct eap_mschapv2_hdr *req; + u8 *pos, *msg, *end; + char *message = "OK"; + size_t msg_len; + int i; + + msg_len = 2 + 2 * sizeof(data->auth_response) + 3 + strlen(message); + *reqDataLen = sizeof(*req) + msg_len; + req = malloc(*reqDataLen + 1); + if (req == NULL) { + wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to allocate memory" + " for request"); + data->state = FAILURE; + return NULL; + } + + req->code = EAP_CODE_REQUEST; + req->identifier = id; + req->length = htons(*reqDataLen); + req->type = EAP_TYPE_MSCHAPV2; + req->op_code = MSCHAPV2_OP_SUCCESS; + req->mschapv2_id = data->resp_mschapv2_id; + req->ms_length[0] = (*reqDataLen - 5) >> 8; + req->ms_length[1] = (*reqDataLen - 5) & 0xff; + + msg = pos = (u8 *) (req + 1); + end = ((u8 *) req) + *reqDataLen + 1; + + pos += snprintf((char *) pos, end - pos, "S="); + for (i = 0; i < sizeof(data->auth_response); i++) { + pos += snprintf((char *) pos, end - pos, "%02X", + data->auth_response[i]); + } + pos += snprintf((char *) pos, end - pos, " M=%s", message); + + wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: Success Request Message", + msg, msg_len); + + return (u8 *) req; +} + + +static u8 * eap_mschapv2_build_failure_req(struct eap_sm *sm, + struct eap_mschapv2_data *data, + int id, size_t *reqDataLen) +{ + struct eap_mschapv2_hdr *req; + u8 *pos; + char *message = "E=691 R=0 C=00000000000000000000000000000000 V=3 " + "M=FAILED"; + size_t msg_len; + + msg_len = strlen(message); + *reqDataLen = sizeof(*req) + msg_len; + req = malloc(*reqDataLen + 1); + if (req == NULL) { + wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to allocate memory" + " for request"); + data->state = FAILURE; + return NULL; + } + + req->code = EAP_CODE_REQUEST; + req->identifier = id; + req->length = htons(*reqDataLen); + req->type = EAP_TYPE_MSCHAPV2; + req->op_code = MSCHAPV2_OP_FAILURE; + req->mschapv2_id = data->resp_mschapv2_id; + req->ms_length[0] = (*reqDataLen - 5) >> 8; + req->ms_length[1] = (*reqDataLen - 5) & 0xff; + + pos = (u8 *) (req + 1); + memcpy(pos, message, msg_len); + + wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: Failure Request Message", + (u8 *) message, msg_len); + + return (u8 *) req; +} + + +static u8 * eap_mschapv2_buildReq(struct eap_sm *sm, void *priv, int id, + size_t *reqDataLen) +{ + struct eap_mschapv2_data *data = priv; + + switch (data->state) { + case CHALLENGE: + return eap_mschapv2_build_challenge(sm, data, id, reqDataLen); + case SUCCESS_REQ: + return eap_mschapv2_build_success_req(sm, data, id, + reqDataLen); + case FAILURE_REQ: + return eap_mschapv2_build_failure_req(sm, data, id, + reqDataLen); + default: + wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Unknown state %d in " + "buildReq", data->state); + break; + } + return NULL; +} + + +static Boolean eap_mschapv2_check(struct eap_sm *sm, void *priv, + u8 *respData, size_t respDataLen) +{ + struct eap_mschapv2_data *data = priv; + struct eap_mschapv2_hdr *resp; + u8 *pos; + size_t len; + + resp = (struct eap_mschapv2_hdr *) respData; + pos = (u8 *) (resp + 1); + if (respDataLen < 6 || resp->type != EAP_TYPE_MSCHAPV2 || + (len = ntohs(resp->length)) > respDataLen) { + wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Invalid frame"); + return TRUE; + } + + if (data->state == CHALLENGE && + resp->op_code != MSCHAPV2_OP_RESPONSE) { + wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Expected Response - " + "ignore op %d", resp->op_code); + return TRUE; + } + + if (data->state == SUCCESS_REQ && + resp->op_code != MSCHAPV2_OP_SUCCESS && + resp->op_code != MSCHAPV2_OP_FAILURE) { + wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Expected Success or " + "Failure - ignore op %d", resp->op_code); + return TRUE; + } + + if (data->state == FAILURE_REQ && + resp->op_code != MSCHAPV2_OP_FAILURE) { + wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Expected Failure " + "- ignore op %d", resp->op_code); + return TRUE; + } + + return FALSE; +} + + +static void eap_mschapv2_process_response(struct eap_sm *sm, + struct eap_mschapv2_data *data, + u8 *respData, size_t respDataLen) +{ + struct eap_mschapv2_hdr *resp; + u8 *pos; + u8 *peer_challenge, *nt_response, flags, *name; + size_t name_len; + u8 expected[24]; + int i; + u8 *username, *user; + size_t username_len, user_len; + + resp = (struct eap_mschapv2_hdr *) respData; + pos = (u8 *) (resp + 1); + + if (respDataLen < sizeof(*resp) + 1 + 49 || + resp->op_code != MSCHAPV2_OP_RESPONSE || + pos[0] != 49) { + wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: Invalid response", + respData, respDataLen); + data->state = FAILURE; + return; + } + data->resp_mschapv2_id = resp->mschapv2_id; + pos++; + peer_challenge = pos; + pos += 16 + 8; + nt_response = pos; + pos += 24; + flags = *pos++; + name = pos; + name_len = respData + respDataLen - name; + + wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: Peer-Challenge", + peer_challenge, 16); + wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: NT-Response", nt_response, 24); + wpa_printf(MSG_MSGDUMP, "EAP-MSCHAPV2: Flags 0x%x", flags); + wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: Name", name, name_len); + + /* MSCHAPv2 does not include optional domain name in the + * challenge-response calculation, so remove domain prefix + * (if present). */ + username = sm->identity; + username_len = sm->identity_len; + for (i = 0; i < username_len; i++) { + if (username[i] == '\\') { + username_len -= i + 1; + username += i + 1; + break; + } + } + + user = name; + user_len = name_len; + for (i = 0; i < user_len; i++) { + if (user[i] == '\\') { + user_len -= i + 1; + user += i + 1; + break; + } + } + + if (username_len != user_len || + memcmp(username, user, username_len) != 0) { + wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Mismatch in user names"); + wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Expected user " + "name", username, username_len); + wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Received user " + "name", user, user_len); + data->state = FAILURE; + return; + } + + wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: User name", + username, username_len); + + generate_nt_response(data->auth_challenge, peer_challenge, + username, username_len, + sm->user->password, sm->user->password_len, + expected); + + if (memcmp(nt_response, expected, 24) == 0) { + wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Correct NT-Response"); + data->state = SUCCESS_REQ; + + + /* Authenticator response is not really needed yet, but + * calculate it here so that peer_challenge and username need + * not be saved. */ + generate_authenticator_response(sm->user->password, + sm->user->password_len, + peer_challenge, + data->auth_challenge, + username, username_len, + nt_response, + data->auth_response); + } else { + wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: Expected NT-Response", + expected, 24); + wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Invalid NT-Response"); + data->state = FAILURE_REQ; + } +} + + +static void eap_mschapv2_process_success_resp(struct eap_sm *sm, + struct eap_mschapv2_data *data, + u8 *respData, size_t respDataLen) +{ + struct eap_mschapv2_hdr *resp; + + resp = (struct eap_mschapv2_hdr *) respData; + + if (resp->op_code == MSCHAPV2_OP_SUCCESS) { + wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received Success Response" + " - authentication completed successfully"); + data->state = SUCCESS; + } else { + wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Did not receive Success " + "Response - peer rejected authentication"); + data->state = FAILURE; + } +} + + +static void eap_mschapv2_process_failure_resp(struct eap_sm *sm, + struct eap_mschapv2_data *data, + u8 *respData, size_t respDataLen) +{ + struct eap_mschapv2_hdr *resp; + + resp = (struct eap_mschapv2_hdr *) respData; + + if (resp->op_code == MSCHAPV2_OP_FAILURE) { + wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received Failure Response" + " - authentication failed"); + } else { + wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Did not receive Failure " + "Response - authentication failed"); + } + + data->state = FAILURE; +} + + +static void eap_mschapv2_process(struct eap_sm *sm, void *priv, + u8 *respData, size_t respDataLen) +{ + struct eap_mschapv2_data *data = priv; + + if (sm->user == NULL || sm->user->password == NULL) { + wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Password not configured"); + data->state = FAILURE; + return; + } + + switch (data->state) { + case CHALLENGE: + eap_mschapv2_process_response(sm, data, respData, respDataLen); + break; + case SUCCESS_REQ: + eap_mschapv2_process_success_resp(sm, data, respData, + respDataLen); + break; + case FAILURE_REQ: + eap_mschapv2_process_failure_resp(sm, data, respData, + respDataLen); + break; + default: + wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Unknown state %d in " + "process", data->state); + break; + } +} + + +static Boolean eap_mschapv2_isDone(struct eap_sm *sm, void *priv) +{ + struct eap_mschapv2_data *data = priv; + return data->state == SUCCESS || data->state == FAILURE; +} + + +static Boolean eap_mschapv2_isSuccess(struct eap_sm *sm, void *priv) +{ + struct eap_mschapv2_data *data = priv; + return data->state == SUCCESS; +} + + +const struct eap_method eap_method_mschapv2 = +{ + .method = EAP_TYPE_MSCHAPV2, + .name = "MSCHAPV2", + .init = eap_mschapv2_init, + .reset = eap_mschapv2_reset, + .buildReq = eap_mschapv2_buildReq, + .check = eap_mschapv2_check, + .process = eap_mschapv2_process, + .isDone = eap_mschapv2_isDone, + .isSuccess = eap_mschapv2_isSuccess, +}; diff --git a/contrib/hostapd-0.4.9/eap_pax.c b/contrib/hostapd-0.4.9/eap_pax.c new file mode 100644 index 0000000000..2645ba58f2 --- /dev/null +++ b/contrib/hostapd-0.4.9/eap_pax.c @@ -0,0 +1,519 @@ +/* + * hostapd / EAP-PAX (draft-clancy-eap-pax-04.txt) server + * Copyright (c) 2005, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include +#include +#include +#include + +#include "hostapd.h" +#include "common.h" +#include "eap_i.h" +#include "eap_pax_common.h" + +/* + * Note: only PAX_STD subprotocol is currently supported + */ + +struct eap_pax_data { + enum { PAX_STD_1, PAX_STD_3, SUCCESS, FAILURE } state; + u8 mac_id; + union { + u8 e[2 * EAP_PAX_RAND_LEN]; + struct { + u8 x[EAP_PAX_RAND_LEN]; /* server rand */ + u8 y[EAP_PAX_RAND_LEN]; /* client rand */ + } r; + } rand; + u8 ak[EAP_PAX_AK_LEN]; + u8 mk[EAP_PAX_MK_LEN]; + u8 ck[EAP_PAX_CK_LEN]; + u8 ick[EAP_PAX_ICK_LEN]; + int keys_set; + char *cid; + size_t cid_len; +}; + + +static void * eap_pax_init(struct eap_sm *sm) +{ + struct eap_pax_data *data; + + data = malloc(sizeof(*data)); + if (data == NULL) + return data; + memset(data, 0, sizeof(*data)); + data->state = PAX_STD_1; + /* + * TODO: make this configurable once EAP_PAX_MAC_AES_CBC_MAC_128 is + * supported + */ + data->mac_id = EAP_PAX_MAC_HMAC_SHA1_128; + + return data; +} + + +static void eap_pax_reset(struct eap_sm *sm, void *priv) +{ + struct eap_pax_data *data = priv; + free(data->cid); + free(data); +} + + +static u8 * eap_pax_build_std_1(struct eap_sm *sm, + struct eap_pax_data *data, + int id, size_t *reqDataLen) +{ + struct eap_pax_hdr *req; + u8 *pos; + + wpa_printf(MSG_DEBUG, "EAP-PAX: PAX_STD-1 (sending)"); + + if (hostapd_get_rand(data->rand.r.x, EAP_PAX_RAND_LEN)) { + wpa_printf(MSG_ERROR, "EAP-PAX: Failed to get random data"); + data->state = FAILURE; + return NULL; + } + + *reqDataLen = sizeof(*req) + 2 + EAP_PAX_RAND_LEN + EAP_PAX_ICV_LEN; + req = malloc(*reqDataLen); + if (req == NULL) { + wpa_printf(MSG_ERROR, "EAP-PAX: Failed to allocate memory " + "request"); + data->state = FAILURE; + return NULL; + } + + req->code = EAP_CODE_REQUEST; + req->identifier = id; + req->length = htons(*reqDataLen); + req->type = EAP_TYPE_PAX; + req->op_code = EAP_PAX_OP_STD_1; + req->flags = 0; + req->mac_id = data->mac_id; + req->dh_group_id = EAP_PAX_DH_GROUP_NONE; + req->public_key_id = EAP_PAX_PUBLIC_KEY_NONE; + pos = (u8 *) (req + 1); + *pos++ = 0; + *pos++ = EAP_PAX_RAND_LEN; + memcpy(pos, data->rand.r.x, EAP_PAX_RAND_LEN); + wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: A = X (server rand)", + pos, EAP_PAX_RAND_LEN); + pos += EAP_PAX_RAND_LEN; + + eap_pax_mac(data->mac_id, (u8 *) "", 0, + (u8 *) req, *reqDataLen - EAP_PAX_ICV_LEN, + NULL, 0, NULL, 0, pos); + wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", pos, EAP_PAX_ICV_LEN); + pos += EAP_PAX_ICV_LEN; + + return (u8 *) req; +} + + +static u8 * eap_pax_build_std_3(struct eap_sm *sm, + struct eap_pax_data *data, + int id, size_t *reqDataLen) +{ + struct eap_pax_hdr *req; + u8 *pos; + + wpa_printf(MSG_DEBUG, "EAP-PAX: PAX_STD-3 (sending)"); + + *reqDataLen = sizeof(*req) + 2 + EAP_PAX_MAC_LEN + EAP_PAX_ICV_LEN; + req = malloc(*reqDataLen); + if (req == NULL) { + wpa_printf(MSG_ERROR, "EAP-PAX: Failed to allocate memory " + "request"); + data->state = FAILURE; + return NULL; + } + + req->code = EAP_CODE_REQUEST; + req->identifier = id; + req->length = htons(*reqDataLen); + req->type = EAP_TYPE_PAX; + req->op_code = EAP_PAX_OP_STD_3; + req->flags = 0; + req->mac_id = data->mac_id; + req->dh_group_id = EAP_PAX_DH_GROUP_NONE; + req->public_key_id = EAP_PAX_PUBLIC_KEY_NONE; + pos = (u8 *) (req + 1); + *pos++ = 0; + *pos++ = EAP_PAX_MAC_LEN; + eap_pax_mac(data->mac_id, data->ck, EAP_PAX_CK_LEN, + data->rand.r.y, EAP_PAX_RAND_LEN, + (u8 *) data->cid, data->cid_len, NULL, 0, pos); + wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: MAC_CK(B, CID)", + pos, EAP_PAX_MAC_LEN); + pos += EAP_PAX_MAC_LEN; + + eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN, + (u8 *) req, *reqDataLen - EAP_PAX_ICV_LEN, + NULL, 0, NULL, 0, pos); + wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", pos, EAP_PAX_ICV_LEN); + pos += EAP_PAX_ICV_LEN; + + return (u8 *) req; +} + + +static u8 * eap_pax_buildReq(struct eap_sm *sm, void *priv, int id, + size_t *reqDataLen) +{ + struct eap_pax_data *data = priv; + + switch (data->state) { + case PAX_STD_1: + return eap_pax_build_std_1(sm, data, id, reqDataLen); + case PAX_STD_3: + return eap_pax_build_std_3(sm, data, id, reqDataLen); + default: + wpa_printf(MSG_DEBUG, "EAP-PAX: Unknown state %d in buildReq", + data->state); + break; + } + return NULL; +} + + +static Boolean eap_pax_check(struct eap_sm *sm, void *priv, + u8 *respData, size_t respDataLen) +{ + struct eap_pax_data *data = priv; + struct eap_pax_hdr *resp; + size_t len; + u8 icvbuf[EAP_PAX_ICV_LEN], *icv; + + resp = (struct eap_pax_hdr *) respData; + if (respDataLen < sizeof(*resp) || resp->type != EAP_TYPE_PAX || + (len = ntohs(resp->length)) > respDataLen || + len < sizeof(*resp) + EAP_PAX_ICV_LEN) { + wpa_printf(MSG_INFO, "EAP-PAX: Invalid frame"); + return TRUE; + } + + wpa_printf(MSG_DEBUG, "EAP-PAX: received frame: op_code 0x%x " + "flags 0x%x mac_id 0x%x dh_group_id 0x%x " + "public_key_id 0x%x", + resp->op_code, resp->flags, resp->mac_id, resp->dh_group_id, + resp->public_key_id); + wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: received payload", + (u8 *) (resp + 1), len - sizeof(*resp) - EAP_PAX_ICV_LEN); + + if (data->state == PAX_STD_1 && + resp->op_code != EAP_PAX_OP_STD_2) { + wpa_printf(MSG_DEBUG, "EAP-PAX: Expected PAX_STD-2 - " + "ignore op %d", resp->op_code); + return TRUE; + } + + if (data->state == PAX_STD_3 && + resp->op_code != EAP_PAX_OP_ACK) { + wpa_printf(MSG_DEBUG, "EAP-PAX: Expected PAX-ACK - " + "ignore op %d", resp->op_code); + return TRUE; + } + + if (resp->op_code != EAP_PAX_OP_STD_2 && + resp->op_code != EAP_PAX_OP_ACK) { + wpa_printf(MSG_DEBUG, "EAP-PAX: Unknown op_code 0x%x", + resp->op_code); + } + + if (data->mac_id != resp->mac_id) { + wpa_printf(MSG_DEBUG, "EAP-PAX: Expected MAC ID 0x%x, " + "received 0x%x", data->mac_id, resp->mac_id); + return TRUE; + } + + if (resp->dh_group_id != EAP_PAX_DH_GROUP_NONE) { + wpa_printf(MSG_INFO, "EAP-PAX: Expected DH Group ID 0x%x, " + "received 0x%x", EAP_PAX_DH_GROUP_NONE, + resp->dh_group_id); + return TRUE; + } + + if (resp->public_key_id != EAP_PAX_PUBLIC_KEY_NONE) { + wpa_printf(MSG_INFO, "EAP-PAX: Expected Public Key ID 0x%x, " + "received 0x%x", EAP_PAX_PUBLIC_KEY_NONE, + resp->public_key_id); + return TRUE; + } + + if (resp->flags & EAP_PAX_FLAGS_MF) { + /* TODO: add support for reassembling fragments */ + wpa_printf(MSG_INFO, "EAP-PAX: fragmentation not supported"); + return TRUE; + } + + if (resp->flags & EAP_PAX_FLAGS_CE) { + wpa_printf(MSG_INFO, "EAP-PAX: Unexpected CE flag"); + return TRUE; + } + + if (data->keys_set) { + if (len - sizeof(*resp) < EAP_PAX_ICV_LEN) { + wpa_printf(MSG_INFO, "EAP-PAX: No ICV in the packet"); + return TRUE; + } + icv = respData + len - EAP_PAX_ICV_LEN; + wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", icv, EAP_PAX_ICV_LEN); + eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN, + respData, len - EAP_PAX_ICV_LEN, NULL, 0, NULL, 0, + icvbuf); + if (memcmp(icvbuf, icv, EAP_PAX_ICV_LEN) != 0) { + wpa_printf(MSG_INFO, "EAP-PAX: Invalid ICV"); + wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: Expected ICV", + icvbuf, EAP_PAX_ICV_LEN); + return TRUE; + } + } + + return FALSE; +} + + +static void eap_pax_process_std_2(struct eap_sm *sm, + struct eap_pax_data *data, + u8 *respData, size_t respDataLen) +{ + struct eap_pax_hdr *resp; + u8 *pos, mac[EAP_PAX_MAC_LEN], icvbuf[EAP_PAX_ICV_LEN]; + size_t len, left; + int i; + + if (data->state != PAX_STD_1) + return; + + wpa_printf(MSG_DEBUG, "EAP-PAX: Received PAX_STD-2"); + + resp = (struct eap_pax_hdr *) respData; + len = ntohs(resp->length); + pos = (u8 *) (resp + 1); + left = len - sizeof(*resp); + + if (left < 2 + EAP_PAX_RAND_LEN || + ((pos[0] << 8) | pos[1]) != EAP_PAX_RAND_LEN) { + wpa_printf(MSG_INFO, "EAP-PAX: Too short PAX_STD-2 (B)"); + return; + } + pos += 2; + left -= 2; + memcpy(data->rand.r.y, pos, EAP_PAX_RAND_LEN); + wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: Y (client rand)", + data->rand.r.y, EAP_PAX_RAND_LEN); + pos += EAP_PAX_RAND_LEN; + left -= EAP_PAX_RAND_LEN; + + if (left < 2 || 2 + ((pos[0] << 8) | pos[1]) > left) { + wpa_printf(MSG_INFO, "EAP-PAX: Too short PAX_STD-2 (CID)"); + return; + } + data->cid_len = (pos[0] << 8) | pos[1]; + free(data->cid); + data->cid = malloc(data->cid_len); + if (data->cid == NULL) { + wpa_printf(MSG_INFO, "EAP-PAX: Failed to allocate memory for " + "CID"); + return; + } + memcpy (data->cid, pos + 2, data->cid_len); + pos += 2 + data->cid_len; + left -= 2 + data->cid_len; + wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-PAX: CID", + (u8 *) data->cid, data->cid_len); + + if (left < 2 + EAP_PAX_MAC_LEN || + ((pos[0] << 8) | pos[1]) != EAP_PAX_MAC_LEN) { + wpa_printf(MSG_INFO, "EAP-PAX: Too short PAX_STD-2 (MAC_CK)"); + return; + } + pos += 2; + left -= 2; + wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: MAC_CK(A, B, CID)", + pos, EAP_PAX_MAC_LEN); + + if (eap_user_get(sm, (u8 *) data->cid, data->cid_len, 0) < 0) { + wpa_hexdump_ascii(MSG_DEBUG, "EAP-PAX: unknown CID", + (u8 *) data->cid, data->cid_len); + data->state = FAILURE; + return; + } + + for (i = 0; + i < EAP_MAX_METHODS && sm->user->methods[i] != EAP_TYPE_NONE; + i++) { + if (sm->user->methods[i] == EAP_TYPE_PAX) + break; + } + + if (sm->user->methods[i] != EAP_TYPE_PAX) { + wpa_hexdump_ascii(MSG_DEBUG, + "EAP-PAX: EAP-PAX not enabled for CID", + (u8 *) data->cid, data->cid_len); + data->state = FAILURE; + return; + } + + if (sm->user->password == NULL || + sm->user->password_len != EAP_PAX_AK_LEN) { + wpa_hexdump_ascii(MSG_DEBUG, "EAP-PAX: invalid password in " + "user database for CID", + (u8 *) data->cid, data->cid_len); + data->state = FAILURE; + return; + } + memcpy(data->ak, sm->user->password, EAP_PAX_AK_LEN); + + if (eap_pax_initial_key_derivation(data->mac_id, data->ak, + data->rand.e, data->mk, data->ck, + data->ick) < 0) { + wpa_printf(MSG_INFO, "EAP-PAX: Failed to complete initial " + "key derivation"); + data->state = FAILURE; + return; + } + data->keys_set = 1; + + eap_pax_mac(data->mac_id, data->ck, EAP_PAX_CK_LEN, + data->rand.r.x, EAP_PAX_RAND_LEN, + data->rand.r.y, EAP_PAX_RAND_LEN, + (u8 *) data->cid, data->cid_len, mac); + if (memcmp(mac, pos, EAP_PAX_MAC_LEN) != 0) { + wpa_printf(MSG_INFO, "EAP-PAX: Invalid MAC_CK(A, B, CID) in " + "PAX_STD-2"); + wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: Expected MAC_CK(A, B, CID)", + mac, EAP_PAX_MAC_LEN); + data->state = FAILURE; + return; + } + + pos += EAP_PAX_MAC_LEN; + left -= EAP_PAX_MAC_LEN; + + if (left < EAP_PAX_ICV_LEN) { + wpa_printf(MSG_INFO, "EAP-PAX: Too short ICV (%lu) in " + "PAX_STD-2", (unsigned long) left); + return; + } + wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", pos, EAP_PAX_ICV_LEN); + eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN, + respData, len - EAP_PAX_ICV_LEN, NULL, 0, NULL, 0, icvbuf); + if (memcmp(icvbuf, pos, EAP_PAX_ICV_LEN) != 0) { + wpa_printf(MSG_INFO, "EAP-PAX: Invalid ICV in PAX_STD-2"); + wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: Expected ICV", + icvbuf, EAP_PAX_ICV_LEN); + return; + } + pos += EAP_PAX_ICV_LEN; + left -= EAP_PAX_ICV_LEN; + + if (left > 0) { + wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ignored extra payload", + pos, left); + } + + data->state = PAX_STD_3; +} + + +static void eap_pax_process_ack(struct eap_sm *sm, + struct eap_pax_data *data, + u8 *respData, size_t respDataLen) +{ + if (data->state != PAX_STD_3) + return; + + wpa_printf(MSG_DEBUG, "EAP-PAX: Received PAX-ACK - authentication " + "completed successfully"); + data->state = SUCCESS; +} + + +static void eap_pax_process(struct eap_sm *sm, void *priv, + u8 *respData, size_t respDataLen) +{ + struct eap_pax_data *data = priv; + struct eap_pax_hdr *resp; + + if (sm->user == NULL || sm->user->password == NULL) { + wpa_printf(MSG_INFO, "EAP-PAX: Password not configured"); + data->state = FAILURE; + return; + } + + resp = (struct eap_pax_hdr *) respData; + + switch (resp->op_code) { + case EAP_PAX_OP_STD_2: + eap_pax_process_std_2(sm, data, respData, respDataLen); + break; + case EAP_PAX_OP_ACK: + eap_pax_process_ack(sm, data, respData, respDataLen); + break; + } +} + + +static Boolean eap_pax_isDone(struct eap_sm *sm, void *priv) +{ + struct eap_pax_data *data = priv; + return data->state == SUCCESS || data->state == FAILURE; +} + + +static u8 * eap_pax_getKey(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_pax_data *data = priv; + u8 *key; + + if (data->state != SUCCESS) + return NULL; + + key = malloc(EAP_PAX_MSK_LEN); + if (key == NULL) + return NULL; + + *len = EAP_PAX_MSK_LEN; + eap_pax_kdf(data->mac_id, data->mk, EAP_PAX_MK_LEN, + "Master Session Key", data->rand.e, 2 * EAP_PAX_RAND_LEN, + EAP_PAX_MSK_LEN, key); + + return key; +} + + +static Boolean eap_pax_isSuccess(struct eap_sm *sm, void *priv) +{ + struct eap_pax_data *data = priv; + return data->state == SUCCESS; +} + + +const struct eap_method eap_method_pax = +{ + .method = EAP_TYPE_PAX, + .name = "PAX", + .init = eap_pax_init, + .reset = eap_pax_reset, + .buildReq = eap_pax_buildReq, + .check = eap_pax_check, + .process = eap_pax_process, + .isDone = eap_pax_isDone, + .getKey = eap_pax_getKey, + .isSuccess = eap_pax_isSuccess, +}; diff --git a/contrib/hostapd-0.4.9/eap_pax_common.c b/contrib/hostapd-0.4.9/eap_pax_common.c new file mode 100644 index 0000000000..d8f4016a6a --- /dev/null +++ b/contrib/hostapd-0.4.9/eap_pax_common.c @@ -0,0 +1,152 @@ +/* + * WPA Supplicant / EAP-PAX shared routines + * Copyright (c) 2005, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include +#include +#include + +#include "common.h" +#include "sha1.h" +#include "eap_pax_common.h" + + +/** + * eap_pax_kdf - PAX Key Derivation Function + * @mac_id: MAC ID (EAP_PAX_MAC_*) / currently, only HMAC_SHA1_128 is supported + * @key: Secret key (X) + * @key_len: Length of the secret key in bytes + * @identifier: Public identifier for the key (Y) + * @entropy: Exchanged entropy to seed the KDF (Z) + * @entropy_len: Length of the entropy in bytes + * @output_len: Output len in bytes (W) + * @output: Buffer for the derived key + * Returns: 0 on success, -1 failed + * + * draft-clancy-eap-pax-04.txt, chap. 2.5: PAX-KDF-W(X, Y, Z) + */ +int eap_pax_kdf(u8 mac_id, const u8 *key, size_t key_len, + const char *identifier, + const u8 *entropy, size_t entropy_len, + size_t output_len, u8 *output) +{ + u8 mac[SHA1_MAC_LEN]; + u8 counter, *pos; + const u8 *addr[3]; + size_t len[3]; + size_t num_blocks, left; + + num_blocks = (output_len + EAP_PAX_MAC_LEN - 1) / EAP_PAX_MAC_LEN; + if (identifier == NULL || num_blocks >= 255) + return -1; + + /* TODO: add support for EAP_PAX_MAC_AES_CBC_MAC_128 */ + if (mac_id != EAP_PAX_MAC_HMAC_SHA1_128) + return -1; + + addr[0] = (const u8 *) identifier; + len[0] = strlen(identifier); + addr[1] = entropy; + len[1] = entropy_len; + addr[2] = &counter; + len[2] = 1; + + pos = output; + left = output_len; + for (counter = 1; counter <= (u8) num_blocks; counter++) { + size_t clen = left > EAP_PAX_MAC_LEN ? EAP_PAX_MAC_LEN : left; + hmac_sha1_vector(key, key_len, 3, addr, len, mac); + memcpy(pos, mac, clen); + pos += clen; + left -= clen; + } + + return 0; +} + + +/** + * eap_pax_mac - EAP-PAX MAC + * @mac_id: MAC ID (EAP_PAX_MAC_*) / currently, only HMAC_SHA1_128 is supported + * @key: Secret key + * @key_len: Length of the secret key in bytes + * @data1: Optional data, first block; %NULL if not used + * @data1_len: Length of data1 in bytes + * @data2: Optional data, second block; %NULL if not used + * @data2_len: Length of data2 in bytes + * @data3: Optional data, third block; %NULL if not used + * @data3_len: Length of data3 in bytes + * @mac: Buffer for the MAC value (EAP_PAX_MAC_LEN = 16 bytes) + * Returns: 0 on success, -1 on failure + * + * Wrapper function to calculate EAP-PAX MAC. + */ +int eap_pax_mac(u8 mac_id, const u8 *key, size_t key_len, + const u8 *data1, size_t data1_len, + const u8 *data2, size_t data2_len, + const u8 *data3, size_t data3_len, + u8 *mac) +{ + u8 hash[SHA1_MAC_LEN]; + const u8 *addr[3]; + size_t len[3]; + size_t count; + + /* TODO: add support for EAP_PAX_MAC_AES_CBC_MAC_128 */ + if (mac_id != EAP_PAX_MAC_HMAC_SHA1_128) + return -1; + + addr[0] = data1; + len[0] = data1_len; + addr[1] = data2; + len[1] = data2_len; + addr[2] = data3; + len[2] = data3_len; + + count = (data1 ? 1 : 0) + (data2 ? 1 : 0) + (data3 ? 1 : 0); + hmac_sha1_vector(key, key_len, count, addr, len, hash); + memcpy(mac, hash, EAP_PAX_MAC_LEN); + + return 0; +} + + +/** + * eap_pax_initial_key_derivation - EAP-PAX initial key derivation + * @mac_id: MAC ID (EAP_PAX_MAC_*) / currently, only HMAC_SHA1_128 is supported + * @ak: Authentication Key + * @e: Entropy + * @mk: Buffer for the derived Master Key + * @ck: Buffer for the derived Confirmation Key + * @ick: Buffer for the derived Integrity Check Key + * Returns: 0 on success, -1 on failure + */ +int eap_pax_initial_key_derivation(u8 mac_id, const u8 *ak, const u8 *e, + u8 *mk, u8 *ck, u8 *ick) +{ + wpa_printf(MSG_DEBUG, "EAP-PAX: initial key derivation"); + if (eap_pax_kdf(mac_id, ak, EAP_PAX_AK_LEN, "Master Key", + e, 2 * EAP_PAX_RAND_LEN, EAP_PAX_MK_LEN, mk) || + eap_pax_kdf(mac_id, mk, EAP_PAX_MK_LEN, "Confirmation Key", + e, 2 * EAP_PAX_RAND_LEN, EAP_PAX_CK_LEN, ck) || + eap_pax_kdf(mac_id, mk, EAP_PAX_MK_LEN, "Integrity Check Key", + e, 2 * EAP_PAX_RAND_LEN, EAP_PAX_ICK_LEN, ick)) + return -1; + + wpa_hexdump_key(MSG_MSGDUMP, "EAP-PAX: AK", ak, EAP_PAX_AK_LEN); + wpa_hexdump_key(MSG_MSGDUMP, "EAP-PAX: MK", mk, EAP_PAX_MK_LEN); + wpa_hexdump_key(MSG_MSGDUMP, "EAP-PAX: CK", ck, EAP_PAX_CK_LEN); + wpa_hexdump_key(MSG_MSGDUMP, "EAP-PAX: ICK", ick, EAP_PAX_ICK_LEN); + + return 0; +} diff --git a/contrib/hostapd-0.4.9/eap_pax_common.h b/contrib/hostapd-0.4.9/eap_pax_common.h new file mode 100644 index 0000000000..b5ad6af1f2 --- /dev/null +++ b/contrib/hostapd-0.4.9/eap_pax_common.h @@ -0,0 +1,84 @@ +/* + * WPA Supplicant / EAP-PAX shared routines + * Copyright (c) 2005, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef EAP_PAX_COMMON_H +#define EAP_PAX_COMMON_H + +struct eap_pax_hdr { + u8 code; + u8 identifier; + u16 length; /* including code, identifier, and length */ + u8 type; /* EAP_TYPE_PAX */ + u8 op_code; + u8 flags; + u8 mac_id; + u8 dh_group_id; + u8 public_key_id; + /* Followed by variable length payload and ICV */ +} __attribute__ ((packed)); + + +/* op_code: */ +enum { + EAP_PAX_OP_STD_1 = 0x01, + EAP_PAX_OP_STD_2 = 0x02, + EAP_PAX_OP_STD_3 = 0x03, + EAP_PAX_OP_SEC_1 = 0x11, + EAP_PAX_OP_SEC_2 = 0x12, + EAP_PAX_OP_SEC_3 = 0x13, + EAP_PAX_OP_SEC_4 = 0x14, + EAP_PAX_OP_SEC_5 = 0x15, + EAP_PAX_OP_ACK = 0x21 +}; + +/* flags: */ +#define EAP_PAX_FLAGS_MF 0x01 +#define EAP_PAX_FLAGS_CE 0x02 + +/* mac_id: */ +#define EAP_PAX_MAC_HMAC_SHA1_128 0x01 +#define EAP_PAX_MAC_AES_CBC_MAC_128 0x02 + +/* dh_group_id: */ +#define EAP_PAX_DH_GROUP_NONE 0x00 +#define EAP_PAX_DH_GROUP_3072_MODP 0x01 + +/* public_key_id: */ +#define EAP_PAX_PUBLIC_KEY_NONE 0x00 +#define EAP_PAX_PUBLIC_KEY_RSA_OAEP_2048 0x01 + + +#define EAP_PAX_RAND_LEN 32 +#define EAP_PAX_MSK_LEN 64 +#define EAP_PAX_MAC_LEN 16 +#define EAP_PAX_ICV_LEN 16 +#define EAP_PAX_AK_LEN 16 +#define EAP_PAX_MK_LEN 16 +#define EAP_PAX_CK_LEN 16 +#define EAP_PAX_ICK_LEN 16 + + +int eap_pax_kdf(u8 mac_id, const u8 *key, size_t key_len, + const char *identifier, + const u8 *entropy, size_t entropy_len, + size_t output_len, u8 *output); +int eap_pax_mac(u8 mac_id, const u8 *key, size_t key_len, + const u8 *data1, size_t data1_len, + const u8 *data2, size_t data2_len, + const u8 *data3, size_t data3_len, + u8 *mac); +int eap_pax_initial_key_derivation(u8 mac_id, const u8 *ak, const u8 *e, + u8 *mk, u8 *ck, u8 *ick); + +#endif /* EAP_PAX_COMMON_H */ diff --git a/contrib/hostapd-0.4.9/eap_peap.c b/contrib/hostapd-0.4.9/eap_peap.c new file mode 100644 index 0000000000..9eb61a6b13 --- /dev/null +++ b/contrib/hostapd-0.4.9/eap_peap.c @@ -0,0 +1,726 @@ +/* + * hostapd / EAP-PEAP (draft-josefsson-pppext-eap-tls-eap-07.txt) + * Copyright (c) 2004-2005, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include +#include +#include +#include + +#include "hostapd.h" +#include "common.h" +#include "eap_i.h" +#include "eap_tls_common.h" +#include "tls.h" + + +/* Maximum supported PEAP version + * 0 = Microsoft's PEAP version 0; draft-kamath-pppext-peapv0-00.txt + * 1 = draft-josefsson-ppext-eap-tls-eap-05.txt + * 2 = draft-josefsson-ppext-eap-tls-eap-07.txt + */ +#define EAP_PEAP_VERSION 1 + + +static void eap_peap_reset(struct eap_sm *sm, void *priv); + + +struct eap_peap_data { + struct eap_ssl_data ssl; + enum { + START, PHASE1, PHASE2_START, PHASE2_ID, PHASE2_METHOD, + PHASE2_TLV, SUCCESS_REQ, FAILURE_REQ, SUCCESS, FAILURE + } state; + + int peap_version; + const struct eap_method *phase2_method; + void *phase2_priv; + int force_version; +}; + + +static const char * eap_peap_state_txt(int state) +{ + switch (state) { + case START: + return "START"; + case PHASE1: + return "PHASE1"; + case PHASE2_START: + return "PHASE2_START"; + case PHASE2_ID: + return "PHASE2_ID"; + case PHASE2_METHOD: + return "PHASE2_METHOD"; + case PHASE2_TLV: + return "PHASE2_TLV"; + case SUCCESS_REQ: + return "SUCCESS_REQ"; + case FAILURE_REQ: + return "FAILURE_REQ"; + case SUCCESS: + return "SUCCESS"; + case FAILURE: + return "FAILURE"; + default: + return "Unknown?!"; + } +} + + +static void eap_peap_state(struct eap_peap_data *data, int state) +{ + wpa_printf(MSG_DEBUG, "EAP-PEAP: %s -> %s", + eap_peap_state_txt(data->state), + eap_peap_state_txt(state)); + data->state = state; +} + + +static EapType eap_peap_req_success(struct eap_sm *sm, + struct eap_peap_data *data) +{ + if (data->state == FAILURE || data->state == FAILURE_REQ) { + eap_peap_state(data, FAILURE); + return EAP_TYPE_NONE; + } + + if (data->peap_version == 0) { + sm->tlv_request = TLV_REQ_SUCCESS; + eap_peap_state(data, PHASE2_TLV); + return EAP_TYPE_TLV; + } else { + eap_peap_state(data, SUCCESS_REQ); + return EAP_TYPE_NONE; + } +} + + +static EapType eap_peap_req_failure(struct eap_sm *sm, + struct eap_peap_data *data) +{ + if (data->state == FAILURE || data->state == FAILURE_REQ || + data->state == SUCCESS_REQ || + (data->phase2_method && + data->phase2_method->method == EAP_TYPE_TLV)) { + eap_peap_state(data, FAILURE); + return EAP_TYPE_NONE; + } + + if (data->peap_version == 0) { + sm->tlv_request = TLV_REQ_FAILURE; + eap_peap_state(data, PHASE2_TLV); + return EAP_TYPE_TLV; + } else { + eap_peap_state(data, FAILURE_REQ); + return EAP_TYPE_NONE; + } +} + + +static void * eap_peap_init(struct eap_sm *sm) +{ + struct eap_peap_data *data; + + data = malloc(sizeof(*data)); + if (data == NULL) + return data; + memset(data, 0, sizeof(*data)); + data->peap_version = EAP_PEAP_VERSION; + data->force_version = -1; + if (sm->user && sm->user->force_version >= 0) { + data->force_version = sm->user->force_version; + wpa_printf(MSG_DEBUG, "EAP-PEAP: forcing version %d", + data->force_version); + data->peap_version = data->force_version; + } + data->state = START; + + if (eap_tls_ssl_init(sm, &data->ssl, 0)) { + wpa_printf(MSG_INFO, "EAP-PEAP: Failed to initialize SSL."); + eap_peap_reset(sm, data); + return NULL; + } + + return data; +} + + +static void eap_peap_reset(struct eap_sm *sm, void *priv) +{ + struct eap_peap_data *data = priv; + if (data == NULL) + return; + if (data->phase2_priv && data->phase2_method) + data->phase2_method->reset(sm, data->phase2_priv); + eap_tls_ssl_deinit(sm, &data->ssl); + free(data); +} + + +static u8 * eap_peap_build_start(struct eap_sm *sm, struct eap_peap_data *data, + int id, size_t *reqDataLen) +{ + struct eap_hdr *req; + u8 *pos; + + *reqDataLen = sizeof(*req) + 2; + req = malloc(*reqDataLen); + if (req == NULL) { + wpa_printf(MSG_ERROR, "EAP-PEAP: Failed to allocate memory for" + " request"); + eap_peap_state(data, FAILURE); + return NULL; + } + + req->code = EAP_CODE_REQUEST; + req->identifier = id; + req->length = htons(*reqDataLen); + pos = (u8 *) (req + 1); + *pos++ = EAP_TYPE_PEAP; + *pos = EAP_TLS_FLAGS_START | data->peap_version; + + eap_peap_state(data, PHASE1); + + return (u8 *) req; +} + + +static u8 * eap_peap_build_req(struct eap_sm *sm, struct eap_peap_data *data, + int id, size_t *reqDataLen) +{ + int res; + u8 *req; + + res = eap_tls_buildReq_helper(sm, &data->ssl, EAP_TYPE_PEAP, + data->peap_version, id, &req, + reqDataLen); + + if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) { + wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase1 done, starting " + "Phase2"); + eap_peap_state(data, PHASE2_START); + } + + if (res == 1) + return eap_tls_build_ack(reqDataLen, id, EAP_TYPE_PEAP, + data->peap_version); + return req; +} + + +static u8 * eap_peap_encrypt(struct eap_sm *sm, struct eap_peap_data *data, + int id, u8 *plain, size_t plain_len, + size_t *out_len) +{ + int res; + u8 *pos; + struct eap_hdr *req; + + /* TODO: add support for fragmentation, if needed. This will need to + * add TLS Message Length field, if the frame is fragmented. */ + req = malloc(sizeof(struct eap_hdr) + 2 + data->ssl.tls_out_limit); + if (req == NULL) + return NULL; + + req->code = EAP_CODE_REQUEST; + req->identifier = id; + + pos = (u8 *) (req + 1); + *pos++ = EAP_TYPE_PEAP; + *pos++ = data->peap_version; + + res = tls_connection_encrypt(sm->ssl_ctx, data->ssl.conn, + plain, plain_len, + pos, data->ssl.tls_out_limit); + if (res < 0) { + wpa_printf(MSG_INFO, "EAP-PEAP: Failed to encrypt Phase 2 " + "data"); + free(req); + return NULL; + } + + *out_len = sizeof(struct eap_hdr) + 2 + res; + req->length = host_to_be16(*out_len); + return (u8 *) req; +} + + +static u8 * eap_peap_build_phase2_req(struct eap_sm *sm, + struct eap_peap_data *data, + int id, size_t *reqDataLen) +{ + u8 *req, *buf, *encr_req; + size_t req_len; + + buf = req = data->phase2_method->buildReq(sm, data->phase2_priv, id, + &req_len); + if (req == NULL) + return NULL; + + wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: Encrypting Phase 2 data", + req, req_len); + + if (data->peap_version == 0 && + data->phase2_method->method != EAP_TYPE_TLV) { + req += sizeof(struct eap_hdr); + req_len -= sizeof(struct eap_hdr); + } + + encr_req = eap_peap_encrypt(sm, data, id, req, req_len, reqDataLen); + free(buf); + + return encr_req; +} + + +static u8 * eap_peap_build_phase2_term(struct eap_sm *sm, + struct eap_peap_data *data, + int id, size_t *reqDataLen, int success) +{ + u8 *encr_req; + size_t req_len; + struct eap_hdr *hdr; + + req_len = sizeof(*hdr); + hdr = malloc(req_len); + if (hdr == NULL) { + return NULL; + } + + memset(hdr, 0, req_len); + hdr->code = success ? EAP_CODE_SUCCESS : EAP_CODE_FAILURE; + hdr->identifier = id; + hdr->length = htons(req_len); + + wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: Encrypting Phase 2 data", + (u8 *) hdr, req_len); + + encr_req = eap_peap_encrypt(sm, data, id, (u8 *) hdr, req_len, + reqDataLen); + free(hdr); + + return encr_req; +} + + +static u8 * eap_peap_buildReq(struct eap_sm *sm, void *priv, int id, + size_t *reqDataLen) +{ + struct eap_peap_data *data = priv; + + switch (data->state) { + case START: + return eap_peap_build_start(sm, data, id, reqDataLen); + case PHASE1: + return eap_peap_build_req(sm, data, id, reqDataLen); + case PHASE2_ID: + case PHASE2_METHOD: + case PHASE2_TLV: + return eap_peap_build_phase2_req(sm, data, id, reqDataLen); + case SUCCESS_REQ: + return eap_peap_build_phase2_term(sm, data, id, reqDataLen, 1); + case FAILURE_REQ: + return eap_peap_build_phase2_term(sm, data, id, reqDataLen, 0); + default: + wpa_printf(MSG_DEBUG, "EAP-PEAP: %s - unexpected state %d", + __func__, data->state); + return NULL; + } +} + + +static Boolean eap_peap_check(struct eap_sm *sm, void *priv, + u8 *respData, size_t respDataLen) +{ + struct eap_hdr *resp; + u8 *pos; + size_t len; + + resp = (struct eap_hdr *) respData; + pos = (u8 *) (resp + 1); + if (respDataLen < sizeof(*resp) + 2 || *pos != EAP_TYPE_PEAP || + (len = ntohs(resp->length)) > respDataLen) { + wpa_printf(MSG_INFO, "EAP-PEAP: Invalid frame"); + return TRUE; + } + + return FALSE; +} + + +static int eap_peap_phase2_init(struct eap_sm *sm, struct eap_peap_data *data, + u8 eap_type) +{ + if (data->phase2_priv && data->phase2_method) { + data->phase2_method->reset(sm, data->phase2_priv); + data->phase2_method = NULL; + data->phase2_priv = NULL; + } + data->phase2_method = eap_sm_get_eap_methods(eap_type); + if (!data->phase2_method) + return -1; + + sm->init_phase2 = 1; + data->phase2_priv = data->phase2_method->init(sm); + sm->init_phase2 = 0; + return 0; +} + + +static void eap_peap_process_phase2_response(struct eap_sm *sm, + struct eap_peap_data *data, + u8 *in_data, size_t in_len) +{ + u8 next_type = EAP_TYPE_NONE; + struct eap_hdr *hdr; + u8 *pos; + size_t left; + + if (data->phase2_priv == NULL) { + wpa_printf(MSG_DEBUG, "EAP-PEAP: %s - Phase2 not " + "initialized?!", __func__); + return; + } + + hdr = (struct eap_hdr *) in_data; + pos = (u8 *) (hdr + 1); + left = in_len - sizeof(*hdr); + + if (in_len > sizeof(*hdr) && *pos == EAP_TYPE_NAK) { + wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Phase2 type Nak'ed; " + "allowed types", pos + 1, left - 1); + eap_sm_process_nak(sm, pos + 1, left - 1); + if (sm->user && sm->user_eap_method_index < EAP_MAX_METHODS && + sm->user->methods[sm->user_eap_method_index] != + EAP_TYPE_NONE) { + next_type = + sm->user->methods[sm->user_eap_method_index++]; + wpa_printf(MSG_DEBUG, "EAP-PEAP: try EAP type %d", + next_type); + } else { + next_type = eap_peap_req_failure(sm, data); + } + eap_peap_phase2_init(sm, data, next_type); + return; + } + + if (data->phase2_method->check(sm, data->phase2_priv, in_data, + in_len)) { + wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase2 check() asked to " + "ignore the packet"); + return; + } + + data->phase2_method->process(sm, data->phase2_priv, in_data, in_len); + + if (!data->phase2_method->isDone(sm, data->phase2_priv)) + return; + + + if (!data->phase2_method->isSuccess(sm, data->phase2_priv)) { + wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase2 method failed"); + next_type = eap_peap_req_failure(sm, data); + eap_peap_phase2_init(sm, data, next_type); + return; + } + + switch (data->state) { + case PHASE2_ID: + if (eap_user_get(sm, sm->identity, sm->identity_len, 1) != 0) { + wpa_hexdump_ascii(MSG_DEBUG, "EAP_PEAP: Phase2 " + "Identity not found in the user " + "database", + sm->identity, sm->identity_len); + next_type = eap_peap_req_failure(sm, data); + break; + } + + eap_peap_state(data, PHASE2_METHOD); + next_type = sm->user->methods[0]; + sm->user_eap_method_index = 1; + wpa_printf(MSG_DEBUG, "EAP-PEAP: try EAP type %d", next_type); + break; + case PHASE2_METHOD: + next_type = eap_peap_req_success(sm, data); + break; + case PHASE2_TLV: + if (sm->tlv_request == TLV_REQ_SUCCESS || + data->state == SUCCESS_REQ) { + eap_peap_state(data, SUCCESS); + } else { + eap_peap_state(data, FAILURE); + } + break; + case FAILURE: + break; + default: + wpa_printf(MSG_DEBUG, "EAP-PEAP: %s - unexpected state %d", + __func__, data->state); + break; + } + + eap_peap_phase2_init(sm, data, next_type); +} + + +static void eap_peap_process_phase2(struct eap_sm *sm, + struct eap_peap_data *data, + struct eap_hdr *resp, + u8 *in_data, size_t in_len) +{ + u8 *in_decrypted; + int buf_len, len_decrypted, len, res; + struct eap_hdr *hdr; + + wpa_printf(MSG_DEBUG, "EAP-PEAP: received %lu bytes encrypted data for" + " Phase 2", (unsigned long) in_len); + + res = eap_tls_data_reassemble(sm, &data->ssl, &in_data, &in_len); + if (res < 0 || res == 1) + return; + + buf_len = in_len; + if (data->ssl.tls_in_total > buf_len) + buf_len = data->ssl.tls_in_total; + in_decrypted = malloc(buf_len); + if (in_decrypted == NULL) { + free(data->ssl.tls_in); + data->ssl.tls_in = NULL; + data->ssl.tls_in_len = 0; + wpa_printf(MSG_WARNING, "EAP-PEAP: failed to allocate memory " + "for decryption"); + return; + } + + len_decrypted = tls_connection_decrypt(sm->ssl_ctx, data->ssl.conn, + in_data, in_len, + in_decrypted, buf_len); + free(data->ssl.tls_in); + data->ssl.tls_in = NULL; + data->ssl.tls_in_len = 0; + if (len_decrypted < 0) { + wpa_printf(MSG_INFO, "EAP-PEAP: Failed to decrypt Phase 2 " + "data"); + free(in_decrypted); + eap_peap_state(data, FAILURE); + return; + } + + wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: Decrypted Phase 2 EAP", + in_decrypted, len_decrypted); + + hdr = (struct eap_hdr *) in_decrypted; + + if (data->peap_version == 0 && data->state != PHASE2_TLV) { + struct eap_hdr *nhdr = malloc(sizeof(struct eap_hdr) + + len_decrypted); + if (nhdr == NULL) { + free(in_decrypted); + return; + } + memcpy((u8 *) (nhdr + 1), in_decrypted, len_decrypted); + free(in_decrypted); + nhdr->code = resp->code; + nhdr->identifier = resp->identifier; + nhdr->length = host_to_be16(sizeof(struct eap_hdr) + + len_decrypted); + + len_decrypted += sizeof(struct eap_hdr); + in_decrypted = (u8 *) nhdr; + } + hdr = (struct eap_hdr *) in_decrypted; + if (len_decrypted < sizeof(*hdr)) { + free(in_decrypted); + wpa_printf(MSG_INFO, "EAP-PEAP: Too short Phase 2 " + "EAP frame (len=%d)", len_decrypted); + eap_peap_req_failure(sm, data); + return; + } + len = be_to_host16(hdr->length); + if (len > len_decrypted) { + free(in_decrypted); + wpa_printf(MSG_INFO, "EAP-PEAP: Length mismatch in " + "Phase 2 EAP frame (len=%d hdr->length=%d)", + len_decrypted, len); + eap_peap_req_failure(sm, data); + return; + } + wpa_printf(MSG_DEBUG, "EAP-PEAP: received Phase 2: code=%d " + "identifier=%d length=%d", hdr->code, hdr->identifier, len); + switch (hdr->code) { + case EAP_CODE_RESPONSE: + eap_peap_process_phase2_response(sm, data, (u8 *) hdr, len); + break; + case EAP_CODE_SUCCESS: + wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 Success"); + if (data->state == SUCCESS_REQ) { + eap_peap_state(data, SUCCESS); + } + break; + case EAP_CODE_FAILURE: + wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 Failure"); + eap_peap_state(data, FAILURE); + break; + default: + wpa_printf(MSG_INFO, "EAP-PEAP: Unexpected code=%d in " + "Phase 2 EAP header", hdr->code); + break; + } + + free(in_decrypted); + } + + +static void eap_peap_process(struct eap_sm *sm, void *priv, + u8 *respData, size_t respDataLen) +{ + struct eap_peap_data *data = priv; + struct eap_hdr *resp; + u8 *pos, flags; + int left; + unsigned int tls_msg_len; + int peer_version; + + resp = (struct eap_hdr *) respData; + pos = (u8 *) (resp + 1); + pos++; + flags = *pos++; + left = htons(resp->length) - sizeof(struct eap_hdr) - 2; + wpa_printf(MSG_DEBUG, "EAP-PEAP: Received packet(len=%lu) - " + "Flags 0x%02x", (unsigned long) respDataLen, flags); + peer_version = flags & EAP_PEAP_VERSION_MASK; + if (data->force_version >= 0 && peer_version != data->force_version) { + wpa_printf(MSG_INFO, "EAP-PEAP: peer did not select the forced" + " version (forced=%d peer=%d) - reject", + data->force_version, peer_version); + eap_peap_state(data, FAILURE); + return; + } + if (peer_version < data->peap_version) { + wpa_printf(MSG_DEBUG, "EAP-PEAP: peer ver=%d, own ver=%d; " + "use version %d", + peer_version, data->peap_version, peer_version); + data->peap_version = peer_version; + + } + if (flags & EAP_TLS_FLAGS_LENGTH_INCLUDED) { + if (left < 4) { + wpa_printf(MSG_INFO, "EAP-PEAP: Short frame with TLS " + "length"); + eap_peap_state(data, FAILURE); + return; + } + tls_msg_len = (pos[0] << 24) | (pos[1] << 16) | (pos[2] << 8) | + pos[3]; + wpa_printf(MSG_DEBUG, "EAP-PEAP: TLS Message Length: %d", + tls_msg_len); + if (data->ssl.tls_in_left == 0) { + data->ssl.tls_in_total = tls_msg_len; + data->ssl.tls_in_left = tls_msg_len; + free(data->ssl.tls_in); + data->ssl.tls_in = NULL; + data->ssl.tls_in_len = 0; + } + pos += 4; + left -= 4; + } + + switch (data->state) { + case PHASE1: + if (eap_tls_process_helper(sm, &data->ssl, pos, left) < 0) { + wpa_printf(MSG_INFO, "EAP-PEAP: TLS processing " + "failed"); + eap_peap_state(data, FAILURE); + } + break; + case PHASE2_START: + eap_peap_state(data, PHASE2_ID); + eap_peap_phase2_init(sm, data, EAP_TYPE_IDENTITY); + break; + case PHASE2_ID: + case PHASE2_METHOD: + case PHASE2_TLV: + eap_peap_process_phase2(sm, data, resp, pos, left); + break; + case SUCCESS_REQ: + eap_peap_state(data, SUCCESS); + break; + case FAILURE_REQ: + eap_peap_state(data, FAILURE); + break; + default: + wpa_printf(MSG_DEBUG, "EAP-PEAP: Unexpected state %d in %s", + data->state, __func__); + break; + } + + if (tls_connection_get_write_alerts(sm->ssl_ctx, data->ssl.conn) > 1) { + wpa_printf(MSG_INFO, "EAP-PEAP: Locally detected fatal error " + "in TLS processing"); + eap_peap_state(data, FAILURE); + } +} + + +static Boolean eap_peap_isDone(struct eap_sm *sm, void *priv) +{ + struct eap_peap_data *data = priv; + return data->state == SUCCESS || data->state == FAILURE; +} + + +static u8 * eap_peap_getKey(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_peap_data *data = priv; + u8 *eapKeyData; + + if (data->state != SUCCESS) + return NULL; + + /* TODO: PEAPv1 - different label in some cases */ + eapKeyData = eap_tls_derive_key(sm, &data->ssl, + "client EAP encryption", + EAP_TLS_KEY_LEN); + if (eapKeyData) { + *len = EAP_TLS_KEY_LEN; + wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Derived key", + eapKeyData, EAP_TLS_KEY_LEN); + } else { + wpa_printf(MSG_DEBUG, "EAP-PEAP: Failed to derive key"); + } + + return eapKeyData; +} + + +static Boolean eap_peap_isSuccess(struct eap_sm *sm, void *priv) +{ + struct eap_peap_data *data = priv; + return data->state == SUCCESS; +} + + +const struct eap_method eap_method_peap = +{ + .method = EAP_TYPE_PEAP, + .name = "PEAP", + .init = eap_peap_init, + .reset = eap_peap_reset, + .buildReq = eap_peap_buildReq, + .check = eap_peap_check, + .process = eap_peap_process, + .isDone = eap_peap_isDone, + .getKey = eap_peap_getKey, + .isSuccess = eap_peap_isSuccess, +}; diff --git a/contrib/hostapd-0.4.9/eap_psk.c b/contrib/hostapd-0.4.9/eap_psk.c new file mode 100644 index 0000000000..f688e61d10 --- /dev/null +++ b/contrib/hostapd-0.4.9/eap_psk.c @@ -0,0 +1,458 @@ +/* + * hostapd / EAP-PSK (draft-bersani-eap-psk-09.txt) server + * Copyright (c) 2005, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + * + * Note: EAP-PSK is an EAP authentication method and as such, completely + * different from WPA-PSK. This file is not needed for WPA-PSK functionality. + */ + +#include +#include +#include +#include + +#include "hostapd.h" +#include "common.h" +#include "eap_i.h" +#include "aes_wrap.h" +#include "eap_psk_common.h" + + +struct eap_psk_data { + enum { PSK_1, PSK_3, SUCCESS, FAILURE } state; + u8 rand_s[EAP_PSK_RAND_LEN]; + u8 rand_p[EAP_PSK_RAND_LEN]; + u8 *id_p, *id_s; + size_t id_p_len, id_s_len; + u8 ak[EAP_PSK_AK_LEN], kdk[EAP_PSK_KDK_LEN], tek[EAP_PSK_TEK_LEN]; + u8 msk[EAP_PSK_MSK_LEN]; +}; + + +static void * eap_psk_init(struct eap_sm *sm) +{ + struct eap_psk_data *data; + + data = malloc(sizeof(*data)); + if (data == NULL) + return data; + memset(data, 0, sizeof(*data)); + data->state = PSK_1; + data->id_s = "hostapd"; + data->id_s_len = 7; + + return data; +} + + +static void eap_psk_reset(struct eap_sm *sm, void *priv) +{ + struct eap_psk_data *data = priv; + free(data->id_p); + free(data); +} + + +static u8 * eap_psk_build_1(struct eap_sm *sm, struct eap_psk_data *data, + int id, size_t *reqDataLen) +{ + struct eap_psk_hdr_1 *req; + + wpa_printf(MSG_DEBUG, "EAP-PSK: PSK-1 (sending)"); + + if (hostapd_get_rand(data->rand_s, EAP_PSK_RAND_LEN)) { + wpa_printf(MSG_ERROR, "EAP-PSK: Failed to get random data"); + data->state = FAILURE; + return NULL; + } + wpa_hexdump(MSG_MSGDUMP, "EAP-PSK: RAND_S (server rand)", + data->rand_s, EAP_PSK_RAND_LEN); + + *reqDataLen = sizeof(*req) + data->id_s_len; + req = malloc(*reqDataLen); + if (req == NULL) { + wpa_printf(MSG_ERROR, "EAP-PSK: Failed to allocate memory " + "request"); + data->state = FAILURE; + return NULL; + } + + req->code = EAP_CODE_REQUEST; + req->identifier = id; + req->length = htons(*reqDataLen); + req->type = EAP_TYPE_PSK; + req->flags = 0; /* T=0 */ + memcpy(req->rand_s, data->rand_s, EAP_PSK_RAND_LEN); + memcpy((u8 *) (req + 1), data->id_s, data->id_s_len); + + return (u8 *) req; +} + + +static u8 * eap_psk_build_3(struct eap_sm *sm, struct eap_psk_data *data, + int id, size_t *reqDataLen) +{ + struct eap_psk_hdr_3 *req; + u8 *buf, *pchannel, nonce[16]; + size_t buflen; + + wpa_printf(MSG_DEBUG, "EAP-PSK: PSK-3 (sending)"); + + *reqDataLen = sizeof(*req) + 4 + 16 + 1; + req = malloc(*reqDataLen); + if (req == NULL) { + wpa_printf(MSG_ERROR, "EAP-PSK: Failed to allocate memory " + "request"); + data->state = FAILURE; + return NULL; + } + + req->code = EAP_CODE_REQUEST; + req->identifier = id; + req->length = htons(*reqDataLen); + req->type = EAP_TYPE_PSK; + req->flags = 2; /* T=2 */ + memcpy(req->rand_s, data->rand_s, EAP_PSK_RAND_LEN); + + /* MAC_S = OMAC1-AES-128(AK, ID_S||RAND_P) */ + buflen = data->id_s_len + EAP_PSK_RAND_LEN; + buf = malloc(buflen); + if (buf == NULL) { + data->state = FAILURE; + 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, req->mac_s); + free(buf); + + eap_psk_derive_keys(data->kdk, data->rand_p, data->tek, data->msk); + wpa_hexdump_key(MSG_DEBUG, "EAP-PSK: TEK", data->tek, EAP_PSK_TEK_LEN); + wpa_hexdump_key(MSG_DEBUG, "EAP-PSK: MSK", data->msk, EAP_PSK_MSK_LEN); + + memset(nonce, 0, sizeof(nonce)); + pchannel = (u8 *) (req + 1); + memcpy(pchannel, nonce + 12, 4); + memset(pchannel + 4, 0, 16); /* Tag */ + pchannel[4 + 16] = EAP_PSK_R_FLAG_DONE_SUCCESS << 6; + wpa_hexdump(MSG_DEBUG, "EAP-PSK: PCHANNEL (plaintext)", + pchannel, 4 + 16 + 1); + aes_128_eax_encrypt(data->tek, nonce, sizeof(nonce), (u8 *) req, 22, + pchannel + 4 + 16, 1, pchannel + 4); + wpa_hexdump(MSG_DEBUG, "EAP-PSK: PCHANNEL (encrypted)", + pchannel, 4 + 16 + 1); + + return (u8 *) req; +} + + +static u8 * eap_psk_buildReq(struct eap_sm *sm, void *priv, int id, + size_t *reqDataLen) +{ + struct eap_psk_data *data = priv; + + switch (data->state) { + case PSK_1: + return eap_psk_build_1(sm, data, id, reqDataLen); + case PSK_3: + return eap_psk_build_3(sm, data, id, reqDataLen); + default: + wpa_printf(MSG_DEBUG, "EAP-PSK: Unknown state %d in buildReq", + data->state); + break; + } + return NULL; +} + + +static Boolean eap_psk_check(struct eap_sm *sm, void *priv, + u8 *respData, size_t respDataLen) +{ + struct eap_psk_data *data = priv; + struct eap_psk_hdr *resp; + size_t len; + u8 t; + + resp = (struct eap_psk_hdr *) respData; + if (respDataLen < sizeof(*resp) || resp->type != EAP_TYPE_PSK || + (len = ntohs(resp->length)) > respDataLen || + len < sizeof(*resp)) { + wpa_printf(MSG_INFO, "EAP-PSK: Invalid frame"); + return TRUE; + } + t = resp->flags & 0x03; + + wpa_printf(MSG_DEBUG, "EAP-PSK: received frame: T=%d", t); + + if (data->state == PSK_1 && t != 1) { + wpa_printf(MSG_DEBUG, "EAP-PSK: Expected PSK-2 - " + "ignore T=%d", t); + return TRUE; + } + + if (data->state == PSK_3 && t != 3) { + wpa_printf(MSG_DEBUG, "EAP-PSK: Expected PSK-4 - " + "ignore T=%d", t); + return TRUE; + } + + if ((t == 1 && len < sizeof(struct eap_psk_hdr_2)) || + (t == 3 && len < sizeof(struct eap_psk_hdr_4))) { + wpa_printf(MSG_DEBUG, "EAP-PSK: Too short frame"); + return TRUE; + } + + return FALSE; +} + + +static void eap_psk_process_2(struct eap_sm *sm, + struct eap_psk_data *data, + u8 *respData, size_t respDataLen) +{ + struct eap_psk_hdr_2 *resp; + u8 *pos, mac[EAP_PSK_MAC_LEN], *buf; + size_t len, left, buflen; + int i; + + if (data->state != PSK_1) + return; + + wpa_printf(MSG_DEBUG, "EAP-PSK: Received PSK-2"); + + resp = (struct eap_psk_hdr_2 *) respData; + len = ntohs(resp->length); + pos = (u8 *) (resp + 1); + left = len - sizeof(*resp); + + free(data->id_p); + data->id_p = malloc(left); + if (data->id_p == NULL) { + wpa_printf(MSG_INFO, "EAP-PSK: Failed to allocate memory for " + "ID_P"); + return; + } + memcpy(data->id_p, pos, left); + data->id_p_len = left; + wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-PSK: ID_P", + data->id_p, data->id_p_len); + + if (eap_user_get(sm, data->id_p, data->id_p_len, 0) < 0) { + wpa_hexdump_ascii(MSG_DEBUG, "EAP-PSK: unknown ID_P", + data->id_p, data->id_p_len); + data->state = FAILURE; + return; + } + + for (i = 0; + i < EAP_MAX_METHODS && sm->user->methods[i] != EAP_TYPE_NONE; + i++) { + if (sm->user->methods[i] == EAP_TYPE_PSK) + break; + } + + if (sm->user->methods[i] != EAP_TYPE_PSK) { + wpa_hexdump_ascii(MSG_DEBUG, + "EAP-PSK: EAP-PSK not enabled for ID_P", + data->id_p, data->id_p_len); + data->state = FAILURE; + return; + } + + if (sm->user->password == NULL || + sm->user->password_len != EAP_PSK_PSK_LEN) { + wpa_hexdump_ascii(MSG_DEBUG, "EAP-PSK: invalid password in " + "user database for ID_P", + data->id_p, data->id_p_len); + data->state = FAILURE; + return; + } + eap_psk_key_setup(sm->user->password, data->ak, data->kdk); + wpa_hexdump_key(MSG_DEBUG, "EAP-PSK: AK", data->ak, EAP_PSK_AK_LEN); + wpa_hexdump_key(MSG_DEBUG, "EAP-PSK: KDK", data->kdk, EAP_PSK_KDK_LEN); + + wpa_hexdump(MSG_MSGDUMP, "EAP-PSK: RAND_P (client rand)", + resp->rand_p, EAP_PSK_RAND_LEN); + memcpy(data->rand_p, resp->rand_p, EAP_PSK_RAND_LEN); + + /* MAC_P = OMAC1-AES-128(AK, ID_P||ID_S||RAND_S||RAND_P) */ + buflen = data->id_p_len + data->id_s_len + 2 * EAP_PSK_RAND_LEN; + buf = malloc(buflen); + if (buf == NULL) { + data->state = FAILURE; + return; + } + 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, mac); + free(buf); + wpa_hexdump(MSG_DEBUG, "EAP-PSK: MAC_P", resp->mac_p, EAP_PSK_MAC_LEN); + if (memcmp(mac, resp->mac_p, EAP_PSK_MAC_LEN) != 0) { + wpa_printf(MSG_INFO, "EAP-PSK: Invalid MAC_P"); + wpa_hexdump(MSG_MSGDUMP, "EAP-PSK: Expected MAC_P", + mac, EAP_PSK_MAC_LEN); + data->state = FAILURE; + return; + } + + data->state = PSK_3; +} + + +static void eap_psk_process_4(struct eap_sm *sm, + struct eap_psk_data *data, + u8 *respData, size_t respDataLen) +{ + struct eap_psk_hdr_4 *resp; + u8 *pos, *decrypted, nonce[16], *tag; + size_t left; + + if (data->state != PSK_3) + return; + + wpa_printf(MSG_DEBUG, "EAP-PSK: Received PSK-4"); + + resp = (struct eap_psk_hdr_4 *) respData; + pos = (u8 *) (resp + 1); + left = ntohs(resp->length) - sizeof(*resp); + + wpa_hexdump(MSG_MSGDUMP, "EAP-PSK: Encrypted PCHANNEL", pos, left); + + if (left < 4 + 16 + 1) { + wpa_printf(MSG_INFO, "EAP-PSK: Too short PCHANNEL data in " + "PSK-4 (len=%lu, expected 21)", + (unsigned long) left); + return; + } + + if (pos[0] == 0 && pos[1] == 0 && pos[2] == 0 && pos[3] == 0) { + wpa_printf(MSG_DEBUG, "EAP-PSK: Nonce did not increase"); + return; + } + + memset(nonce, 0, 12); + memcpy(nonce + 12, pos, 4); + pos += 4; + left -= 4; + tag = pos; + pos += 16; + left -= 16; + + decrypted = malloc(left); + if (decrypted == NULL) + return; + memcpy(decrypted, pos, left); + + if (aes_128_eax_decrypt(data->tek, nonce, sizeof(nonce), + respData, 22, decrypted, left, tag)) { + wpa_printf(MSG_WARNING, "EAP-PSK: PCHANNEL decryption failed"); + free(decrypted); + data->state = FAILURE; + return; + } + wpa_hexdump(MSG_DEBUG, "EAP-PSK: Decrypted PCHANNEL message", + decrypted, left); + + /* Verify R flag */ + switch (decrypted[0] >> 6) { + case EAP_PSK_R_FLAG_CONT: + wpa_printf(MSG_DEBUG, "EAP-PSK: R flag - CONT - unsupported"); + data->state = FAILURE; + break; + case EAP_PSK_R_FLAG_DONE_SUCCESS: + wpa_printf(MSG_DEBUG, "EAP-PSK: R flag - DONE_SUCCESS"); + data->state = SUCCESS; + break; + case EAP_PSK_R_FLAG_DONE_FAILURE: + wpa_printf(MSG_DEBUG, "EAP-PSK: R flag - DONE_FAILURE"); + data->state = FAILURE; + break; + } + free(decrypted); +} + + +static void eap_psk_process(struct eap_sm *sm, void *priv, + u8 *respData, size_t respDataLen) +{ + struct eap_psk_data *data = priv; + struct eap_psk_hdr *resp; + + if (sm->user == NULL || sm->user->password == NULL) { + wpa_printf(MSG_INFO, "EAP-PSK: Password not configured"); + data->state = FAILURE; + return; + } + + resp = (struct eap_psk_hdr *) respData; + + switch (resp->flags & 0x03) { + case 1: + eap_psk_process_2(sm, data, respData, respDataLen); + break; + case 3: + eap_psk_process_4(sm, data, respData, respDataLen); + break; + } +} + + +static Boolean eap_psk_isDone(struct eap_sm *sm, void *priv) +{ + struct eap_psk_data *data = priv; + return data->state == SUCCESS || data->state == FAILURE; +} + + +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 != SUCCESS) + return NULL; + + key = malloc(EAP_PSK_MSK_LEN); + if (key == NULL) + return NULL; + memcpy(key, data->msk, EAP_PSK_MSK_LEN); + *len = EAP_PSK_MSK_LEN; + + return key; +} + + +static Boolean eap_psk_isSuccess(struct eap_sm *sm, void *priv) +{ + struct eap_psk_data *data = priv; + return data->state == SUCCESS; +} + + +const struct eap_method eap_method_psk = +{ + .method = EAP_TYPE_PSK, + .name = "PSK", + .init = eap_psk_init, + .reset = eap_psk_reset, + .buildReq = eap_psk_buildReq, + .check = eap_psk_check, + .process = eap_psk_process, + .isDone = eap_psk_isDone, + .getKey = eap_psk_getKey, + .isSuccess = eap_psk_isSuccess, +}; diff --git a/contrib/hostapd-0.4.9/eap_psk_common.c b/contrib/hostapd-0.4.9/eap_psk_common.c new file mode 100644 index 0000000000..24de66cf0c --- /dev/null +++ b/contrib/hostapd-0.4.9/eap_psk_common.c @@ -0,0 +1,57 @@ +/* + * WPA Supplicant / EAP-PSK shared routines + * Copyright (c) 2004-2005, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include +#include +#include + +#include "common.h" +#include "aes_wrap.h" +#include "eap_psk_common.h" + +#define aes_block_size 16 + + +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); +} + + +void eap_psk_derive_keys(const u8 *kdk, const u8 *rand_p, u8 *tek, u8 *msk) +{ + u8 hash[aes_block_size]; + u8 counter = 1; + int i; + + aes_128_encrypt_block(kdk, rand_p, 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++; + } +} diff --git a/contrib/hostapd-0.4.9/eap_psk_common.h b/contrib/hostapd-0.4.9/eap_psk_common.h new file mode 100644 index 0000000000..5dd3a1042d --- /dev/null +++ b/contrib/hostapd-0.4.9/eap_psk_common.h @@ -0,0 +1,92 @@ +/* + * WPA Supplicant / EAP-PSK shared routines + * Copyright (c) 2004-2005, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef EAP_PSK_COMMON_H +#define EAP_PSK_COMMON_H + + +#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_PSK_LEN 16 +#define EAP_PSK_AK_LEN 16 +#define EAP_PSK_KDK_LEN 16 + +#define EAP_PSK_R_FLAG_CONT 1 +#define EAP_PSK_R_FLAG_DONE_SUCCESS 2 +#define EAP_PSK_R_FLAG_DONE_FAILURE 3 +#define EAP_PSK_E_FLAG 0x20 + +/* Shared prefix for all EAP-PSK frames */ +struct eap_psk_hdr { + u8 code; + u8 identifier; + u16 length; /* including code, identifier, and length */ + u8 type; /* EAP_TYPE_PSK */ + u8 flags; +} __attribute__ ((packed)); + +/* 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 */ + u8 flags; + u8 rand_s[EAP_PSK_RAND_LEN]; + /* Followed by variable length ID_S */ +} __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 */ + u8 flags; + u8 rand_s[EAP_PSK_RAND_LEN]; + 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 */ + u8 flags; + u8 rand_s[EAP_PSK_RAND_LEN]; + 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 */ + u8 flags; + u8 rand_s[EAP_PSK_RAND_LEN]; + /* Followed by variable length PCHANNEL */ +} __attribute__ ((packed)); + + +void eap_psk_key_setup(const u8 *psk, u8 *ak, u8 *kdk); +void eap_psk_derive_keys(const u8 *kdk, const u8 *rand_p, u8 *tek, u8 *msk); + +#endif /* EAP_PSK_COMMON_H */ diff --git a/contrib/hostapd-0.4.9/eap_sim.c b/contrib/hostapd-0.4.9/eap_sim.c new file mode 100644 index 0000000000..fa60cf54d1 --- /dev/null +++ b/contrib/hostapd-0.4.9/eap_sim.c @@ -0,0 +1,431 @@ +/* + * hostapd / EAP-SIM (draft-haverinen-pppext-eap-sim-15.txt) + * Copyright (c) 2005, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include +#include +#include +#include + +#include "hostapd.h" +#include "common.h" +#include "crypto.h" +#include "eap_i.h" +#include "eap_sim_common.h" +#include "eap_sim_db.h" + + +#define EAP_SIM_VERSION 1 + +/* EAP-SIM Subtypes */ +#define EAP_SIM_SUBTYPE_START 10 +#define EAP_SIM_SUBTYPE_CHALLENGE 11 +#define EAP_SIM_SUBTYPE_NOTIFICATION 12 +#define EAP_SIM_SUBTYPE_REAUTHENTICATION 13 +#define EAP_SIM_SUBTYPE_CLIENT_ERROR 14 + +/* AT_CLIENT_ERROR_CODE error codes */ +#define EAP_SIM_UNABLE_TO_PROCESS_PACKET 0 +#define EAP_SIM_UNSUPPORTED_VERSION 1 +#define EAP_SIM_INSUFFICIENT_NUM_OF_CHAL 2 +#define EAP_SIM_RAND_NOT_FRESH 3 + +#define KC_LEN 8 +#define SRES_LEN 4 +#define EAP_SIM_MAX_FAST_REAUTHS 1000 + +#define EAP_SIM_MAX_CHAL 3 + +struct eap_sim_data { + u8 mk[EAP_SIM_MK_LEN]; + u8 nonce_mt[EAP_SIM_NONCE_MT_LEN]; + u8 k_aut[EAP_SIM_K_AUT_LEN]; + u8 k_encr[EAP_SIM_K_ENCR_LEN]; + u8 msk[EAP_SIM_KEYING_DATA_LEN]; + u8 kc[EAP_SIM_MAX_CHAL][KC_LEN]; + u8 sres[EAP_SIM_MAX_CHAL][SRES_LEN]; + u8 rand[EAP_SIM_MAX_CHAL][GSM_RAND_LEN]; + int num_chal; + enum { START, CHALLENGE, SUCCESS, FAILURE } state; +}; + + +static const char * eap_sim_state_txt(int state) +{ + switch (state) { + case START: + return "START"; + case CHALLENGE: + return "CHALLENGE"; + case SUCCESS: + return "SUCCESS"; + case FAILURE: + return "FAILURE"; + default: + return "Unknown?!"; + } +} + + +static void eap_sim_state(struct eap_sim_data *data, int state) +{ + wpa_printf(MSG_DEBUG, "EAP-SIM %s -> %s", + eap_sim_state_txt(data->state), + eap_sim_state_txt(state)); + data->state = state; +} + + +static void * eap_sim_init(struct eap_sm *sm) +{ + struct eap_sim_data *data; + + if (sm->eap_sim_db_priv == NULL) { + wpa_printf(MSG_WARNING, "EAP-SIM: eap_sim_db not configured"); + return NULL; + } + + data = malloc(sizeof(*data)); + if (data == NULL) + return data; + memset(data, 0, sizeof(*data)); + data->state = START; + + return data; +} + + +static void eap_sim_reset(struct eap_sm *sm, void *priv) +{ + struct eap_sim_data *data = priv; + free(data); +} + + +static u8 * eap_sim_build_start(struct eap_sm *sm, struct eap_sim_data *data, + int id, size_t *reqDataLen) +{ + struct eap_sim_msg *msg; + u8 ver[2]; + + msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, EAP_TYPE_SIM, + EAP_SIM_SUBTYPE_START); + if (eap_sim_db_identity_known(sm->eap_sim_db_priv, sm->identity, + sm->identity_len)) { + eap_sim_msg_add(msg, EAP_SIM_AT_PERMANENT_ID_REQ, 0, NULL, 0); + } + ver[0] = 0; + ver[1] = EAP_SIM_VERSION; + eap_sim_msg_add(msg, EAP_SIM_AT_VERSION_LIST, sizeof(ver), + ver, sizeof(ver)); + return eap_sim_msg_finish(msg, reqDataLen, NULL, NULL, 0); +} + + +static u8 * eap_sim_build_challenge(struct eap_sm *sm, + struct eap_sim_data *data, + int id, size_t *reqDataLen) +{ + struct eap_sim_msg *msg; + + msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, EAP_TYPE_SIM, + EAP_SIM_SUBTYPE_CHALLENGE); + eap_sim_msg_add(msg, EAP_SIM_AT_RAND, 0, (u8 *) data->rand, + data->num_chal * GSM_RAND_LEN); + eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC); + return eap_sim_msg_finish(msg, reqDataLen, data->k_aut, data->nonce_mt, + EAP_SIM_NONCE_MT_LEN); +} + + +static u8 * eap_sim_buildReq(struct eap_sm *sm, void *priv, int id, + size_t *reqDataLen) +{ + struct eap_sim_data *data = priv; + + switch (data->state) { + case START: + return eap_sim_build_start(sm, data, id, reqDataLen); + case CHALLENGE: + return eap_sim_build_challenge(sm, data, id, reqDataLen); + default: + wpa_printf(MSG_DEBUG, "EAP-SIM: Unknown state %d in " + "buildReq", data->state); + break; + } + return NULL; +} + + +static Boolean eap_sim_check(struct eap_sm *sm, void *priv, + u8 *respData, size_t respDataLen) +{ + struct eap_sim_data *data = priv; + struct eap_hdr *resp; + u8 *pos, subtype; + size_t len; + + resp = (struct eap_hdr *) respData; + pos = (u8 *) (resp + 1); + if (respDataLen < sizeof(*resp) + 4 || *pos != EAP_TYPE_SIM || + (len = ntohs(resp->length)) > respDataLen) { + wpa_printf(MSG_INFO, "EAP-SIM: Invalid frame"); + return TRUE; + } + subtype = pos[1]; + + if (subtype == EAP_SIM_SUBTYPE_CLIENT_ERROR) + return FALSE; + + switch (data->state) { + case START: + if (subtype != EAP_SIM_SUBTYPE_START) { + wpa_printf(MSG_INFO, "EAP-SIM: Unexpected response " + "subtype %d", subtype); + return TRUE; + } + break; + case CHALLENGE: + if (subtype != EAP_SIM_SUBTYPE_CHALLENGE) { + wpa_printf(MSG_INFO, "EAP-SIM: Unexpected response " + "subtype %d", subtype); + return TRUE; + } + break; + default: + wpa_printf(MSG_INFO, "EAP-SIM: Unexpected state (%d) for " + "processing a response", data->state); + return TRUE; + } + + return FALSE; +} + + +static int eap_sim_supported_ver(struct eap_sim_data *data, int version) +{ + return version == EAP_SIM_VERSION; +} + + +static void eap_sim_derive_mk(struct eap_sim_data *data, + const u8 *identity, size_t identity_len, + const u8 *nonce_mt, int selected_version, + int num_chal, const u8 *kc) +{ + u8 sel_ver[2], ver_list[2]; + const unsigned char *addr[5]; + size_t len[5]; + + addr[0] = identity; + addr[1] = kc; + addr[2] = nonce_mt; + addr[3] = ver_list; + addr[4] = sel_ver; + + len[0] = identity_len; + len[1] = num_chal * KC_LEN; + len[2] = EAP_SIM_NONCE_MT_LEN; + len[3] = sizeof(ver_list); + len[4] = sizeof(sel_ver); + + ver_list[0] = 0; + ver_list[1] = EAP_SIM_VERSION; + sel_ver[0] = selected_version >> 8; + sel_ver[1] = selected_version & 0xff; + + /* MK = SHA1(Identity|n*Kc|NONCE_MT|Version List|Selected Version) */ + sha1_vector(5, addr, len, data->mk); + wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: MK", data->mk, EAP_SIM_MK_LEN); +} + + +static void eap_sim_process_start(struct eap_sm *sm, + struct eap_sim_data *data, + u8 *respData, size_t respDataLen, + struct eap_sim_attrs *attr) +{ + wpa_printf(MSG_DEBUG, "EAP-SIM: Receive start response"); + + if (attr->nonce_mt == NULL || attr->selected_version < 0) { + wpa_printf(MSG_DEBUG, "EAP-SIM: Start/Response missing " + "required attributes"); + eap_sim_state(data, FAILURE); + return; + } + + if (!eap_sim_supported_ver(data, attr->selected_version)) { + wpa_printf(MSG_DEBUG, "EAP-SIM: Peer selected unsupported " + "version %d", attr->selected_version); + eap_sim_state(data, FAILURE); + return; + } + + if (attr->identity) { + free(sm->identity); + sm->identity = malloc(attr->identity_len); + if (sm->identity) { + memcpy(sm->identity, attr->identity, + attr->identity_len); + sm->identity_len = attr->identity_len; + } + } + + if (sm->identity == NULL || sm->identity_len < 1 || + sm->identity[0] != '1') { + wpa_printf(MSG_DEBUG, "EAP-SIM: Could not get proper permanent" + " user name"); + eap_sim_state(data, FAILURE); + return; + } + + wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM: Identity", + sm->identity, sm->identity_len); + + data->num_chal = eap_sim_db_get_gsm_triplets( + sm->eap_sim_db_priv, sm->identity, sm->identity_len, + EAP_SIM_MAX_CHAL, + (u8 *) data->rand, (u8 *) data->kc, (u8 *) data->sres); + if (data->num_chal < 2) { + wpa_printf(MSG_INFO, "EAP-SIM: Failed to get GSM " + "authentication triplets for the peer"); + eap_sim_state(data, FAILURE); + return; + } + + memcpy(data->nonce_mt, attr->nonce_mt, EAP_SIM_NONCE_MT_LEN); + eap_sim_derive_mk(data, sm->identity, sm->identity_len, attr->nonce_mt, + attr->selected_version, data->num_chal, + (u8 *) data->kc); + eap_sim_derive_keys(data->mk, data->k_encr, data->k_aut, data->msk); + + eap_sim_state(data, CHALLENGE); +} + + +static void eap_sim_process_challenge(struct eap_sm *sm, + struct eap_sim_data *data, + u8 *respData, size_t respDataLen, + struct eap_sim_attrs *attr) +{ + if (attr->mac == NULL || + eap_sim_verify_mac(data->k_aut, respData, respDataLen, attr->mac, + (u8 *) data->sres, data->num_chal * SRES_LEN)) { + wpa_printf(MSG_WARNING, "EAP-SIM: Challenge message " + "did not include valid AT_MAC"); + eap_sim_state(data, FAILURE); + return; + } + + wpa_printf(MSG_DEBUG, "EAP-SIM: Challenge response includes the " + "correct AT_MAC"); + eap_sim_state(data, SUCCESS); +} + + +static void eap_sim_process_client_error(struct eap_sm *sm, + struct eap_sim_data *data, + u8 *respData, size_t respDataLen, + struct eap_sim_attrs *attr) +{ + wpa_printf(MSG_DEBUG, "EAP-SIM: Client reported error %d", + attr->client_error_code); + eap_sim_state(data, FAILURE); +} + + +static void eap_sim_process(struct eap_sm *sm, void *priv, + u8 *respData, size_t respDataLen) +{ + struct eap_sim_data *data = priv; + struct eap_hdr *resp; + u8 *pos, subtype; + size_t len; + struct eap_sim_attrs attr; + + resp = (struct eap_hdr *) respData; + pos = (u8 *) (resp + 1); + subtype = pos[1]; + len = ntohs(resp->length); + pos += 4; + + if (eap_sim_parse_attr(pos, respData + len, &attr, 0, 0)) { + wpa_printf(MSG_DEBUG, "EAP-SIM: Failed to parse attributes"); + eap_sim_state(data, FAILURE); + return; + } + + if (subtype == EAP_SIM_SUBTYPE_CLIENT_ERROR) { + eap_sim_process_client_error(sm, data, respData, len, &attr); + return; + } + + switch (data->state) { + case START: + eap_sim_process_start(sm, data, respData, len, &attr); + break; + case CHALLENGE: + eap_sim_process_challenge(sm, data, respData, len, &attr); + break; + default: + wpa_printf(MSG_DEBUG, "EAP-SIM: Unknown state %d in " + "process", data->state); + break; + } +} + + +static Boolean eap_sim_isDone(struct eap_sm *sm, void *priv) +{ + struct eap_sim_data *data = priv; + return data->state == SUCCESS || data->state == FAILURE; +} + + +static u8 * eap_sim_getKey(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_sim_data *data = priv; + u8 *key; + + if (data->state != SUCCESS) + return NULL; + + key = malloc(EAP_SIM_KEYING_DATA_LEN); + if (key == NULL) + return NULL; + memcpy(key, data->msk, EAP_SIM_KEYING_DATA_LEN); + *len = EAP_SIM_KEYING_DATA_LEN; + return key; +} + + +static Boolean eap_sim_isSuccess(struct eap_sm *sm, void *priv) +{ + struct eap_sim_data *data = priv; + return data->state == SUCCESS; +} + + +const struct eap_method eap_method_sim = +{ + .method = EAP_TYPE_SIM, + .name = "SIM", + .init = eap_sim_init, + .reset = eap_sim_reset, + .buildReq = eap_sim_buildReq, + .check = eap_sim_check, + .process = eap_sim_process, + .isDone = eap_sim_isDone, + .getKey = eap_sim_getKey, + .isSuccess = eap_sim_isSuccess, +}; diff --git a/contrib/hostapd-0.4.9/eap_sim_common.c b/contrib/hostapd-0.4.9/eap_sim_common.c new file mode 100644 index 0000000000..75947b7995 --- /dev/null +++ b/contrib/hostapd-0.4.9/eap_sim_common.c @@ -0,0 +1,794 @@ +/* + * WPA Supplicant / EAP-SIM/AKA shared routines + * Copyright (c) 2004-2005, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include +#include +#include + +#include "common.h" +#include "eap_i.h" +#include "sha1.h" +#include "crypto.h" +#include "aes_wrap.h" +#include "eap_sim_common.h" + + +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); + + 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: keying material", + msk, EAP_SIM_KEYING_DATA_LEN); +} + + +void eap_sim_derive_keys_reauth(u16 _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; + + WPA_PUT_BE16(counter, _counter); + + 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: keying material", + msk, EAP_SIM_KEYING_DATA_LEN); +} + + +int eap_sim_verify_mac(const u8 *k_aut, const u8 *req, size_t req_len, + const u8 *mac, const u8 *extra, size_t extra_len) +{ + unsigned char hmac[SHA1_MAC_LEN]; + const u8 *addr[2]; + size_t len[2]; + u8 *tmp; + + if (mac == NULL || req_len < EAP_SIM_MAC_LEN || mac < req || + mac > req + req_len - EAP_SIM_MAC_LEN) + return -1; + + tmp = malloc(req_len); + if (tmp == NULL) + return -1; + + addr[0] = tmp; + len[0] = req_len; + addr[1] = extra; + len[1] = extra_len; + + /* HMAC-SHA1-128 */ + memcpy(tmp, req, req_len); + memset(tmp + (mac - req), 0, EAP_SIM_MAC_LEN); + hmac_sha1_vector(k_aut, EAP_SIM_K_AUT_LEN, 2, addr, len, hmac); + free(tmp); + + 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(const u8 *start, const u8 *end, + struct eap_sim_attrs *attr, int aka, int encr) +{ + const 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; +} + + +u8 * eap_sim_parse_encr(const u8 *k_encr, const u8 *encr_data, + size_t encr_data_len, const u8 *iv, + struct eap_sim_attrs *attr, int aka) +{ + u8 *decrypted; + + if (!iv) { + wpa_printf(MSG_INFO, "EAP-SIM: Encrypted data, but no IV"); + return NULL; + } + + decrypted = malloc(encr_data_len); + if (decrypted == NULL) + return NULL; + memcpy(decrypted, encr_data, encr_data_len); + + aes_128_cbc_decrypt(k_encr, iv, decrypted, encr_data_len); + wpa_hexdump(MSG_MSGDUMP, "EAP-SIM: Decrypted AT_ENCR_DATA", + decrypted, encr_data_len); + + if (eap_sim_parse_attr(decrypted, decrypted + encr_data_len, attr, + aka, 1)) { + wpa_printf(MSG_INFO, "EAP-SIM: (encr) Failed to parse " + "decrypted AT_ENCR_DATA"); + free(decrypted); + return NULL; + } + + return decrypted; +} + + +#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; + WPA_PUT_BE16(pos, value); + pos += 2; + 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) +{ +#ifndef CONFIG_NO_STDOUT_DEBUG + const char *type = aka ? "AKA" : "SIM"; +#endif /* CONFIG_NO_STDOUT_DEBUG */ + + switch (notification) { + case EAP_SIM_GENERAL_FAILURE_AFTER_AUTH: + wpa_printf(MSG_WARNING, "EAP-%s: General failure " + "notification (after authentication)", type); + break; + case EAP_SIM_TEMPORARILY_DENIED: + wpa_printf(MSG_WARNING, "EAP-%s: Failure notification: " + "User has been temporarily denied access to the " + "requested service", type); + break; + case EAP_SIM_NOT_SUBSCRIBED: + wpa_printf(MSG_WARNING, "EAP-%s: Failure notification: " + "User has not subscribed to the requested service", + type); + break; + case EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH: + wpa_printf(MSG_WARNING, "EAP-%s: General failure " + "notification (before authentication)", type); + break; + case EAP_SIM_SUCCESS: + wpa_printf(MSG_INFO, "EAP-%s: Successful authentication " + "notification", type); + break; + default: + if (notification >= 32768) { + wpa_printf(MSG_INFO, "EAP-%s: Unrecognized " + "non-failure notification %d", + type, notification); + } else { + wpa_printf(MSG_WARNING, "EAP-%s: Unrecognized " + "failure notification %d", + type, notification); + } + } +} + + +#ifdef TEST_MAIN_EAP_SIM_COMMON +static int test_eap_sim_prf(void) +{ + /* http://csrc.nist.gov/encryption/dss/Examples-1024bit.pdf */ + u8 xkey[] = { + 0xbd, 0x02, 0x9b, 0xbe, 0x7f, 0x51, 0x96, 0x0b, + 0xcf, 0x9e, 0xdb, 0x2b, 0x61, 0xf0, 0x6f, 0x0f, + 0xeb, 0x5a, 0x38, 0xb6 + }; + u8 w[] = { + 0x20, 0x70, 0xb3, 0x22, 0x3d, 0xba, 0x37, 0x2f, + 0xde, 0x1c, 0x0f, 0xfc, 0x7b, 0x2e, 0x3b, 0x49, + 0x8b, 0x26, 0x06, 0x14, 0x3c, 0x6c, 0x18, 0xba, + 0xcb, 0x0f, 0x6c, 0x55, 0xba, 0xbb, 0x13, 0x78, + 0x8e, 0x20, 0xd7, 0x37, 0xa3, 0x27, 0x51, 0x16 + }; + u8 buf[40]; + + printf("Testing EAP-SIM PRF (FIPS 186-2 + change notice 1)\n"); + eap_sim_prf(xkey, buf, sizeof(buf)); + if (memcmp(w, buf, sizeof(w) != 0)) { + printf("eap_sim_prf failed\n"); + return 1; + } + + return 0; +} + + +int main(int argc, char *argv[]) +{ + int errors = 0; + + errors += test_eap_sim_prf(); + + return errors; +} +#endif /* TEST_MAIN_EAP_SIM_COMMON */ diff --git a/contrib/hostapd-0.4.9/eap_sim_common.h b/contrib/hostapd-0.4.9/eap_sim_common.h new file mode 100644 index 0000000000..6715c369c0 --- /dev/null +++ b/contrib/hostapd-0.4.9/eap_sim_common.h @@ -0,0 +1,116 @@ +/* + * WPA Supplicant / EAP-SIM/AKA shared routines + * Copyright (c) 2004-2005, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#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(u16 _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, const u8 *req, size_t req_len, + const u8 *mac, const 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 { + const u8 *rand, *autn, *mac, *iv, *encr_data, *version_list, *nonce_s; + const u8 *next_pseudonym, *next_reauth_id; + const 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(const u8 *start, const u8 *end, + struct eap_sim_attrs *attr, int aka, int encr); +u8 * eap_sim_parse_encr(const u8 *k_encr, const u8 *encr_data, + size_t encr_data_len, const u8 *iv, + struct eap_sim_attrs *attr, int aka); + + +struct eap_sim_msg; + +struct eap_sim_msg * eap_sim_msg_init(int code, int id, int type, int subtype); +u8 * eap_sim_msg_finish(struct eap_sim_msg *msg, size_t *len, const u8 *k_aut, + const u8 *extra, size_t extra_len); +void eap_sim_msg_free(struct eap_sim_msg *msg); +u8 * eap_sim_msg_add_full(struct eap_sim_msg *msg, u8 attr, + const u8 *data, size_t len); +u8 * eap_sim_msg_add(struct eap_sim_msg *msg, u8 attr, + u16 value, const u8 *data, size_t len); +u8 * eap_sim_msg_add_mac(struct eap_sim_msg *msg, u8 attr); +int eap_sim_msg_add_encr_start(struct eap_sim_msg *msg, u8 attr_iv, + u8 attr_encr); +int eap_sim_msg_add_encr_end(struct eap_sim_msg *msg, u8 *k_encr, + int attr_pad); + +void eap_sim_report_notification(void *msg_ctx, int notification, int aka); + +#endif /* EAP_SIM_COMMON_H */ diff --git a/contrib/hostapd-0.4.9/eap_sim_db.c b/contrib/hostapd-0.4.9/eap_sim_db.c new file mode 100644 index 0000000000..a965fa461d --- /dev/null +++ b/contrib/hostapd-0.4.9/eap_sim_db.c @@ -0,0 +1,243 @@ +/* + * hostapd / EAP-SIM database/authenticator gateway + * Copyright (c) 2005, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +/* This is an example implementation of the EAP-SIM database/authentication + * gateway interface that is expected to be replaced with an implementation of + * SS7 gateway to GSM authentication center (HLR/AuC) or a local + * implementation of SIM triplet generator. + * + * The example implementation here reads triplets from a text file in + * IMSI:Kc:SRES:RAND format, IMSI in ASCII, other fields as hex strings. This + * is used to simulate an HLR/AuC. As such, it is not very useful for real life + * authentication, but it is useful both as an example implementation and for + * EAP-SIM testing. + */ + +#include +#include +#include + +#include "common.h" +#include "eap_sim_common.h" +#include "eap_sim_db.h" + + +/* TODO: add an alternative callback based version of the interface. This is + * needed to work better with the single threaded design of hostapd. For this, + * the EAP data has to be stored somewhere and eap_sim_db is given a context + * pointer for this and a callback function. The callback function will re-send + * the EAP data through normal operations which will eventually end up calling + * eap_sim_db_get_gsm_triplets() again for the same user. This time, eap_sim_db + * should have the triplets available immediately. */ + + +struct eap_sim_db_data { + char *fname; +}; + +#define KC_LEN 8 +#define SRES_LEN 4 +#define RAND_LEN 16 + + +/* Initialize EAP-SIM database/authentication gateway interface. + * Returns pointer to a private data structure. */ +void * eap_sim_db_init(const char *config) +{ + struct eap_sim_db_data *data; + + data = malloc(sizeof(*data)); + if (data == NULL) { + return NULL; + } + + memset(data, 0, sizeof(*data)); + data->fname = strdup(config); + if (data->fname == NULL) { + free(data); + return NULL; + } + + return data; +} + +/* Deinitialize EAP-SIM database/authentication gateway interface. + * priv is the pointer from eap_sim_db_init(). */ +void eap_sim_db_deinit(void *priv) +{ + struct eap_sim_db_data *data = priv; + free(data->fname); + free(data); +} + + +/* Get GSM triplets for user name identity (identity_len bytes). In most cases, + * the user name is '1' | IMSI, i.e., 1 followed by the IMSI in ASCII format. + * The identity may also include NAI realm (@realm). + * priv is the pointer from eap_sim_db_init(). + * Returns the number of triplets received (has to be less than or equal to + * max_chal) or -1 on error (e.g., user not found). rand, kc, and sres are + * pointers to data areas for the triplets. */ +int eap_sim_db_get_gsm_triplets(void *priv, const u8 *identity, + size_t identity_len, int max_chal, + u8 *rand, u8 *kc, u8 *sres) +{ + struct eap_sim_db_data *data = priv; + FILE *f; + int count, i; + char buf[80], *pos, *next; + + f = fopen(data->fname, "r"); + if (f == NULL) { + wpa_printf(MSG_DEBUG, "EAP-SIM DB: could not open triplet " + "file '%s'", data->fname); + return -1; + } + + if (identity_len < 2 || identity[0] != '1') { + wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity", + identity, identity_len); + fclose(f); + return -1; + } + identity++; + identity_len--; + for (i = 0; i < identity_len; i++) { + if (identity[i] == '@') { + identity_len = i; + break; + } + } + wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: get triplets for IMSI", + identity, identity_len); + + count = 0; + while (count < max_chal && fgets(buf, sizeof(buf), f)) { + /* Parse IMSI:Kc:SRES:RAND and match IMSI with identity. */ + buf[sizeof(buf) - 1] = '\0'; + pos = buf; + while (*pos != '\0' && *pos != '\n') + pos++; + if (*pos == '\n') + *pos = '\0'; + if (pos - buf < 60 || pos[0] == '#') + continue; + + pos = strchr(buf, ':'); + if (pos == NULL) + continue; + *pos++ = '\0'; + if (strlen(buf) != identity_len || + memcmp(buf, identity, identity_len) != 0) + continue; + + next = strchr(pos, ':'); + if (next == NULL) + continue; + *next++ = '\0'; + if (hexstr2bin(pos, &kc[count * KC_LEN], KC_LEN) < 0) + continue; + + pos = next; + next = strchr(pos, ':'); + if (next == NULL) + continue; + *next++ = '\0'; + if (hexstr2bin(pos, &sres[count * SRES_LEN], SRES_LEN) < 0) + continue; + + if (hexstr2bin(next, &rand[count * RAND_LEN], RAND_LEN) < 0) + continue; + + count++; + } + + fclose(f); + + if (count == 0) { + wpa_printf(MSG_DEBUG, "EAP-SIM DB: no triplets found"); + count = -1; + } + + return count; +} + + +/* Verify whether the given user identity (identity_len bytes) is known. In + * most cases, the user name is '1' | IMSI, i.e., 1 followed by the IMSI in + * ASCII format. + * priv is the pointer from eap_sim_db_init(). + * Returns 0 if the user is found and GSM triplets would be available for it or + * -1 on error (e.g., user not found or no triplets available). */ +int eap_sim_db_identity_known(void *priv, const u8 *identity, + size_t identity_len) +{ + struct eap_sim_db_data *data = priv; + FILE *f; + char buf[80], *pos; + int i; + + if (identity_len < 1 || identity[0] != '1') { + return -1; + } + + f = fopen(data->fname, "r"); + if (f == NULL) { + wpa_printf(MSG_DEBUG, "EAP-SIM DB: could not open triplet " + "file '%s'", data->fname); + return -1; + } + + if (identity_len < 2 || identity[0] != '1') { + wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity", + identity, identity_len); + return -1; + } + identity++; + identity_len--; + for (i = 0; i < identity_len; i++) { + if (identity[i] == '@') { + identity_len = i; + break; + } + } + + while (fgets(buf, sizeof(buf), f)) { + /* Parse IMSI:Kc:SRES:RAND and match IMSI with identity. */ + buf[sizeof(buf) - 1] = '\0'; + pos = buf; + while (*pos != '\0' && *pos != '\n') + pos++; + if (*pos == '\n') + *pos = '\0'; + if (pos - buf < 60 || pos[0] == '#') + continue; + + pos = strchr(buf, ':'); + if (pos == NULL) + continue; + *pos++ = '\0'; + if (strlen(buf) != identity_len || + memcmp(buf, identity, identity_len) != 0) + continue; + + fclose(f); + return 0; + } + + /* IMSI not found */ + + fclose(f); + return -1; +} diff --git a/contrib/hostapd-0.4.9/eap_sim_db.h b/contrib/hostapd-0.4.9/eap_sim_db.h new file mode 100644 index 0000000000..57a9871073 --- /dev/null +++ b/contrib/hostapd-0.4.9/eap_sim_db.h @@ -0,0 +1,44 @@ +#ifndef EAP_SIM_DB_H +#define EAP_SIM_DB_H + +#ifdef EAP_SIM + +/* Initialize EAP-SIM database/authentication gateway interface. + * Returns pointer to a private data structure. */ +void * eap_sim_db_init(const char *config); + +/* Deinitialize EAP-SIM database/authentication gateway interface. + * priv is the pointer from eap_sim_db_init(). */ +void eap_sim_db_deinit(void *priv); + +/* Get GSM triplets for user name identity (identity_len bytes). In most cases, + * the user name is '1' | IMSI, i.e., 1 followed by the IMSI in ASCII format. + * priv is the pointer from eap_sim_db_init(). + * Returns the number of triplets received (has to be less than or equal to + * max_chal) or -1 on error (e.g., user not found). rand, kc, and sres are + * pointers to data areas for the triplets. */ +int eap_sim_db_get_gsm_triplets(void *priv, const u8 *identity, + size_t identity_len, int max_chal, + u8 *rand, u8 *kc, u8 *sres); + +/* Verify whether the given user identity (identity_len bytes) is known. In + * most cases, the user name is '1' | IMSI, i.e., 1 followed by the IMSI in + * ASCII format. + * priv is the pointer from eap_sim_db_init(). + * Returns 0 if the user is found and GSM triplets would be available for it or + * -1 on error (e.g., user not found or no triplets available). */ +int eap_sim_db_identity_known(void *priv, const u8 *identity, + size_t identity_len); + +#else /* EAP_SIM */ +static inline void * eap_sim_db_init(const char *config) +{ + return NULL; +} + +static inline void eap_sim_db_deinit(void *priv) +{ +} +#endif /* EAP_SIM */ + +#endif /* EAP_SIM_DB_H */ diff --git a/contrib/hostapd-0.4.9/eap_tls.c b/contrib/hostapd-0.4.9/eap_tls.c new file mode 100644 index 0000000000..bf76f5a504 --- /dev/null +++ b/contrib/hostapd-0.4.9/eap_tls.c @@ -0,0 +1,253 @@ +/* + * hostapd / EAP-TLS (RFC 2716) + * Copyright (c) 2004-2005, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include +#include +#include +#include + +#include "hostapd.h" +#include "common.h" +#include "eap_i.h" +#include "eap_tls_common.h" +#include "tls.h" + + +static void eap_tls_reset(struct eap_sm *sm, void *priv); + + +struct eap_tls_data { + struct eap_ssl_data ssl; + enum { START, CONTINUE, SUCCESS, FAILURE } state; +}; + + +static void * eap_tls_init(struct eap_sm *sm) +{ + struct eap_tls_data *data; + + data = malloc(sizeof(*data)); + if (data == NULL) + return data; + memset(data, 0, sizeof(*data)); + data->state = START; + + if (eap_tls_ssl_init(sm, &data->ssl, 1)) { + wpa_printf(MSG_INFO, "EAP-TLS: Failed to initialize SSL."); + eap_tls_reset(sm, data); + return NULL; + } + + return data; +} + + +static void eap_tls_reset(struct eap_sm *sm, void *priv) +{ + struct eap_tls_data *data = priv; + if (data == NULL) + return; + eap_tls_ssl_deinit(sm, &data->ssl); + free(data); +} + + +static u8 * eap_tls_build_start(struct eap_sm *sm, struct eap_tls_data *data, + int id, size_t *reqDataLen) +{ + struct eap_hdr *req; + u8 *pos; + + *reqDataLen = sizeof(*req) + 2; + req = malloc(*reqDataLen); + if (req == NULL) { + wpa_printf(MSG_ERROR, "EAP-TLS: Failed to allocate memory for " + "request"); + data->state = FAILURE; + return NULL; + } + + req->code = EAP_CODE_REQUEST; + req->identifier = id; + req->length = htons(*reqDataLen); + pos = (u8 *) (req + 1); + *pos++ = EAP_TYPE_TLS; + *pos = EAP_TLS_FLAGS_START; + + data->state = CONTINUE; + + return (u8 *) req; +} + + +static u8 * eap_tls_build_req(struct eap_sm *sm, struct eap_tls_data *data, + int id, size_t *reqDataLen) +{ + int res; + u8 *req; + + res = eap_tls_buildReq_helper(sm, &data->ssl, EAP_TYPE_TLS, 0, id, + &req, reqDataLen); + + if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) { + wpa_printf(MSG_DEBUG, "EAP-TLS: Done"); + data->state = SUCCESS; + } + + if (res == 1) + return eap_tls_build_ack(reqDataLen, id, EAP_TYPE_TLS, 0); + return req; +} + + +static u8 * eap_tls_buildReq(struct eap_sm *sm, void *priv, int id, + size_t *reqDataLen) +{ + struct eap_tls_data *data = priv; + + switch (data->state) { + case START: + return eap_tls_build_start(sm, data, id, reqDataLen); + case CONTINUE: + return eap_tls_build_req(sm, data, id, reqDataLen); + default: + wpa_printf(MSG_DEBUG, "EAP-TLS: %s - unexpected state %d", + __func__, data->state); + return NULL; + } +} + + +static Boolean eap_tls_check(struct eap_sm *sm, void *priv, + u8 *respData, size_t respDataLen) +{ + struct eap_hdr *resp; + u8 *pos; + size_t len; + + resp = (struct eap_hdr *) respData; + pos = (u8 *) (resp + 1); + if (respDataLen < sizeof(*resp) + 2 || *pos != EAP_TYPE_TLS || + (len = ntohs(resp->length)) > respDataLen) { + wpa_printf(MSG_INFO, "EAP-TLS: Invalid frame"); + return TRUE; + } + + return FALSE; +} + + +static void eap_tls_process(struct eap_sm *sm, void *priv, + u8 *respData, size_t respDataLen) +{ + struct eap_tls_data *data = priv; + struct eap_hdr *resp; + u8 *pos, flags; + int left; + unsigned int tls_msg_len; + + resp = (struct eap_hdr *) respData; + pos = (u8 *) (resp + 1); + pos++; + flags = *pos++; + left = htons(resp->length) - sizeof(struct eap_hdr) - 2; + wpa_printf(MSG_DEBUG, "EAP-TLS: Received packet(len=%lu) - " + "Flags 0x%02x", (unsigned long) respDataLen, flags); + if (flags & EAP_TLS_FLAGS_LENGTH_INCLUDED) { + if (left < 4) { + wpa_printf(MSG_INFO, "EAP-TLS: Short frame with TLS " + "length"); + data->state = FAILURE; + return; + } + tls_msg_len = (pos[0] << 24) | (pos[1] << 16) | (pos[2] << 8) | + pos[3]; + wpa_printf(MSG_DEBUG, "EAP-TLS: TLS Message Length: %d", + tls_msg_len); + if (data->ssl.tls_in_left == 0) { + data->ssl.tls_in_total = tls_msg_len; + data->ssl.tls_in_left = tls_msg_len; + free(data->ssl.tls_in); + data->ssl.tls_in = NULL; + data->ssl.tls_in_len = 0; + } + pos += 4; + left -= 4; + } + + if (eap_tls_process_helper(sm, &data->ssl, pos, left) < 0) { + wpa_printf(MSG_INFO, "EAP-TLS: TLS processing failed"); + data->state = FAILURE; + return; + } + + if (tls_connection_get_write_alerts(sm->ssl_ctx, data->ssl.conn) > 1) { + wpa_printf(MSG_INFO, "EAP-TLS: Locally detected fatal error " + "in TLS processing"); + data->state = FAILURE; + return; + } +} + + +static Boolean eap_tls_isDone(struct eap_sm *sm, void *priv) +{ + struct eap_tls_data *data = priv; + return data->state == SUCCESS || data->state == FAILURE; +} + + +static u8 * eap_tls_getKey(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_tls_data *data = priv; + u8 *eapKeyData; + + if (data->state != SUCCESS) + return NULL; + + eapKeyData = eap_tls_derive_key(sm, &data->ssl, + "client EAP encryption", + EAP_TLS_KEY_LEN); + if (eapKeyData) { + *len = EAP_TLS_KEY_LEN; + wpa_hexdump(MSG_DEBUG, "EAP-TLS: Derived key", + eapKeyData, EAP_TLS_KEY_LEN); + } else { + wpa_printf(MSG_DEBUG, "EAP-TLS: Failed to derive key"); + } + + return eapKeyData; +} + + +static Boolean eap_tls_isSuccess(struct eap_sm *sm, void *priv) +{ + struct eap_tls_data *data = priv; + return data->state == SUCCESS; +} + + +const struct eap_method eap_method_tls = +{ + .method = EAP_TYPE_TLS, + .name = "TLS", + .init = eap_tls_init, + .reset = eap_tls_reset, + .buildReq = eap_tls_buildReq, + .check = eap_tls_check, + .process = eap_tls_process, + .isDone = eap_tls_isDone, + .getKey = eap_tls_getKey, + .isSuccess = eap_tls_isSuccess, +}; diff --git a/contrib/hostapd-0.4.9/eap_tls_common.c b/contrib/hostapd-0.4.9/eap_tls_common.c new file mode 100644 index 0000000000..d573064b61 --- /dev/null +++ b/contrib/hostapd-0.4.9/eap_tls_common.c @@ -0,0 +1,278 @@ +/* + * hostapd / EAP-TLS/PEAP/TTLS common functions + * Copyright (c) 2004-2005, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include +#include +#include +#include + +#include "hostapd.h" +#include "common.h" +#include "eap_i.h" +#include "eap_tls_common.h" +#include "sha1.h" +#include "tls.h" + + +int eap_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data, + int verify_peer) +{ + data->eap = sm; + data->phase2 = sm->init_phase2; + + data->conn = tls_connection_init(sm->ssl_ctx); + if (data->conn == NULL) { + wpa_printf(MSG_INFO, "SSL: Failed to initialize new TLS " + "connection"); + return -1; + } + + if (tls_connection_set_verify(sm->ssl_ctx, data->conn, verify_peer)) { + wpa_printf(MSG_INFO, "SSL: Failed to configure verification " + "of TLS peer certificate"); + tls_connection_deinit(sm->ssl_ctx, data->conn); + data->conn = NULL; + return -1; + } + + /* TODO: make this configurable */ + data->tls_out_limit = 1398; + if (data->phase2) { + /* Limit the fragment size in the inner TLS authentication + * since the outer authentication with EAP-PEAP does not yet + * support fragmentation */ + if (data->tls_out_limit > 100) + data->tls_out_limit -= 100; + } + return 0; +} + + +void eap_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data) +{ + tls_connection_deinit(sm->ssl_ctx, data->conn); + free(data->tls_in); + free(data->tls_out); +} + + +u8 * eap_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data, + char *label, size_t len) +{ + struct tls_keys keys; + u8 *random; + u8 *out; + + if (tls_connection_get_keys(sm->ssl_ctx, data->conn, &keys)) + return NULL; + out = malloc(len); + random = malloc(keys.client_random_len + keys.server_random_len); + if (out == NULL || random == NULL) { + free(out); + free(random); + return NULL; + } + memcpy(random, keys.client_random, keys.client_random_len); + memcpy(random + keys.client_random_len, keys.server_random, + keys.server_random_len); + + if (tls_prf(keys.master_key, keys.master_key_len, + label, random, keys.client_random_len + + keys.server_random_len, out, len)) { + free(random); + free(out); + return NULL; + } + free(random); + return out; +} + + +int eap_tls_data_reassemble(struct eap_sm *sm, struct eap_ssl_data *data, + u8 **in_data, size_t *in_len) +{ + u8 *buf; + + if (data->tls_in_left > *in_len || data->tls_in) { + buf = realloc(data->tls_in, data->tls_in_len + *in_len); + if (buf == NULL) { + free(data->tls_in); + data->tls_in = NULL; + data->tls_in_len = 0; + wpa_printf(MSG_INFO, "SSL: Could not allocate memory " + "for TLS data"); + return -1; + } + memcpy(buf + data->tls_in_len, *in_data, *in_len); + data->tls_in = buf; + data->tls_in_len += *in_len; + if (*in_len > data->tls_in_left) { + wpa_printf(MSG_INFO, "SSL: more data than TLS message " + "length indicated"); + data->tls_in_left = 0; + return -1; + } + data->tls_in_left -= *in_len; + if (data->tls_in_left > 0) { + wpa_printf(MSG_DEBUG, "SSL: Need %lu bytes more input " + "data", (unsigned long) data->tls_in_left); + return 1; + } + + *in_data = data->tls_in; + *in_len = data->tls_in_len; + } else + data->tls_in_left = 0; + + return 0; +} + + +int eap_tls_process_helper(struct eap_sm *sm, struct eap_ssl_data *data, + u8 *in_data, size_t in_len) +{ + WPA_ASSERT(data->tls_out_len == 0 || in_len == 0); + + if (data->tls_out_len == 0) { + /* No more data to send out - expect to receive more data from + * the peer. */ + int res = eap_tls_data_reassemble(sm, data, &in_data, &in_len); + if (res < 0 || res == 1) { + wpa_printf(MSG_DEBUG, "SSL: data reassembly failed"); + return res; + } + /* Full TLS message reassembled - continue handshake processing + */ + if (data->tls_out) { + /* This should not happen.. */ + wpa_printf(MSG_INFO, "SSL: eap_tls_process_helper - " + "pending tls_out data even though " + "tls_out_len = 0"); + free(data->tls_out); + WPA_ASSERT(data->tls_out == NULL); + } + data->tls_out = tls_connection_server_handshake( + sm->ssl_ctx, data->conn, in_data, in_len, + &data->tls_out_len); + + /* Clear reassembled input data (if the buffer was needed). */ + data->tls_in_left = data->tls_in_total = data->tls_in_len = 0; + free(data->tls_in); + data->tls_in = NULL; + } + + if (data->tls_out == NULL) { + wpa_printf(MSG_DEBUG, "SSL: failed to generate output data"); + data->tls_out_len = 0; + return -1; + } + if (data->tls_out_len == 0) { + /* TLS negotiation should now be complete since all other cases + * needing more that should have been catched above based on + * the TLS Message Length field. */ + wpa_printf(MSG_DEBUG, "SSL: No data to be sent out"); + free(data->tls_out); + data->tls_out = NULL; + + if (tls_connection_get_read_alerts(sm->ssl_ctx, data->conn)) { + wpa_printf(MSG_DEBUG, "SSL: Remote end sent a fatal " + "alert - abort handshake"); + return -1; + } + + return 1; + } + + wpa_printf(MSG_DEBUG, "SSL: %lu bytes left to be sent out (of total " + "%lu bytes)", + (unsigned long) data->tls_out_len - data->tls_out_pos, + (unsigned long) data->tls_out_len); + + return 0; +} + + +int eap_tls_buildReq_helper(struct eap_sm *sm, struct eap_ssl_data *data, + int eap_type, int peap_version, u8 id, + u8 **out_data, size_t *out_len) +{ + size_t len; + u8 *pos, *flags; + struct eap_hdr *req; + + *out_len = 0; + + req = malloc(sizeof(struct eap_hdr) + 2 + 4 + data->tls_out_limit); + if (req == NULL) { + *out_data = NULL; + return -1; + } + req->code = EAP_CODE_REQUEST; + req->identifier = id; + pos = (u8 *) (req + 1); + *pos++ = eap_type; + flags = pos++; + *flags = peap_version; + if (data->tls_out_pos == 0 && + data->tls_out_len > data->tls_out_limit) { + *flags |= EAP_TLS_FLAGS_LENGTH_INCLUDED; + *pos++ = (data->tls_out_len >> 24) & 0xff; + *pos++ = (data->tls_out_len >> 16) & 0xff; + *pos++ = (data->tls_out_len >> 8) & 0xff; + *pos++ = data->tls_out_len & 0xff; + } + + len = data->tls_out_len - data->tls_out_pos; + if (len > data->tls_out_limit) { + *flags |= EAP_TLS_FLAGS_MORE_FRAGMENTS; + len = data->tls_out_limit; + wpa_printf(MSG_DEBUG, "SSL: sending %lu bytes, more fragments " + "will follow", (unsigned long) len); + } + memcpy(pos, &data->tls_out[data->tls_out_pos], len); + data->tls_out_pos += len; + *out_len = (pos - (u8 *) req) + len; + req->length = htons(*out_len); + *out_data = (u8 *) req; + + if (!(*flags & EAP_TLS_FLAGS_MORE_FRAGMENTS)) { + data->tls_out_len = 0; + data->tls_out_pos = 0; + free(data->tls_out); + data->tls_out = NULL; + } + + return 0; +} + + +u8 * eap_tls_build_ack(size_t *reqDataLen, u8 id, int eap_type, + int peap_version) +{ + struct eap_hdr *req; + u8 *pos; + + *reqDataLen = sizeof(struct eap_hdr) + 2; + req = malloc(*reqDataLen); + if (req == NULL) + return NULL; + wpa_printf(MSG_DEBUG, "SSL: Building ACK"); + req->code = EAP_CODE_REQUEST; + req->identifier = id; + req->length = htons(*reqDataLen); + pos = (u8 *) (req + 1); + *pos++ = eap_type; /* Type */ + *pos = peap_version; /* Flags */ + return (u8 *) req; +} diff --git a/contrib/hostapd-0.4.9/eap_tls_common.h b/contrib/hostapd-0.4.9/eap_tls_common.h new file mode 100644 index 0000000000..659ee849f7 --- /dev/null +++ b/contrib/hostapd-0.4.9/eap_tls_common.h @@ -0,0 +1,47 @@ +#ifndef EAP_TLS_COMMON_H +#define EAP_TLS_COMMON_H + +struct eap_ssl_data { + struct tls_connection *conn; + + u8 *tls_out; + size_t tls_out_len; + size_t tls_out_pos; + size_t tls_out_limit; + u8 *tls_in; + size_t tls_in_len; + size_t tls_in_left; + size_t tls_in_total; + + int phase2; + + struct eap_sm *eap; +}; + + +/* EAP TLS Flags */ +#define EAP_TLS_FLAGS_LENGTH_INCLUDED 0x80 +#define EAP_TLS_FLAGS_MORE_FRAGMENTS 0x40 +#define EAP_TLS_FLAGS_START 0x20 +#define EAP_PEAP_VERSION_MASK 0x07 + + /* could be up to 128 bytes, but only the first 64 bytes are used */ +#define EAP_TLS_KEY_LEN 64 + + +int eap_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data, + int verify_peer); +void eap_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data); +u8 * eap_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data, + char *label, size_t len); +int eap_tls_data_reassemble(struct eap_sm *sm, struct eap_ssl_data *data, + u8 **in_data, size_t *in_len); +int eap_tls_process_helper(struct eap_sm *sm, struct eap_ssl_data *data, + u8 *in_data, size_t in_len); +int eap_tls_buildReq_helper(struct eap_sm *sm, struct eap_ssl_data *data, + int eap_type, int peap_version, u8 id, + u8 **out_data, size_t *out_len); +u8 * eap_tls_build_ack(size_t *reqDataLen, u8 id, int eap_type, + int peap_version); + +#endif /* EAP_TLS_COMMON_H */ diff --git a/contrib/hostapd-0.4.9/eap_tlv.c b/contrib/hostapd-0.4.9/eap_tlv.c new file mode 100644 index 0000000000..b7609dcf2e --- /dev/null +++ b/contrib/hostapd-0.4.9/eap_tlv.c @@ -0,0 +1,248 @@ +/* + * hostapd / EAP-TLV (draft-josefsson-pppext-eap-tls-eap-07.txt) + * Copyright (c) 2004, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include +#include +#include +#include + +#include "hostapd.h" +#include "common.h" +#include "eap_i.h" + + +/* EAP-TLV TLVs (draft-josefsson-ppext-eap-tls-eap-07.txt) */ +#define EAP_TLV_RESULT_TLV 3 /* Acknowledged Result */ +#define EAP_TLV_NAK_TLV 4 +#define EAP_TLV_CRYPTO_BINDING_TLV 5 +#define EAP_TLV_CONNECTION_BINDING_TLV 6 +#define EAP_TLV_VENDOR_SPECIFIC_TLV 7 +#define EAP_TLV_URI_TLV 8 +#define EAP_TLV_EAP_PAYLOAD_TLV 9 +#define EAP_TLV_INTERMEDIATE_RESULT_TLV 10 + +#define EAP_TLV_RESULT_SUCCESS 1 +#define EAP_TLV_RESULT_FAILURE 2 + + +struct eap_tlv_data { + enum { CONTINUE, SUCCESS, FAILURE } state; +}; + + +static void * eap_tlv_init(struct eap_sm *sm) +{ + struct eap_tlv_data *data; + + data = malloc(sizeof(*data)); + if (data == NULL) + return data; + memset(data, 0, sizeof(*data)); + data->state = CONTINUE; + + return data; +} + + +static void eap_tlv_reset(struct eap_sm *sm, void *priv) +{ + struct eap_tlv_data *data = priv; + free(data); +} + + +static u8 * eap_tlv_buildReq(struct eap_sm *sm, void *priv, int id, + size_t *reqDataLen) +{ + struct eap_hdr *req; + u8 *pos; + u16 status; + + if (sm->tlv_request == TLV_REQ_SUCCESS) { + status = EAP_TLV_RESULT_SUCCESS; + } else { + status = EAP_TLV_RESULT_FAILURE; + } + + *reqDataLen = sizeof(struct eap_hdr) + 1 + 6; + req = malloc(*reqDataLen); + if (req == NULL) + return NULL; + + req->code = EAP_CODE_REQUEST; + req->identifier = id; + req->length = host_to_be16(*reqDataLen); + pos = (u8 *) (req + 1); + *pos++ = EAP_TYPE_TLV; + *pos++ = 0x80; /* Mandatory */ + *pos++ = EAP_TLV_RESULT_TLV; + /* Length */ + *pos++ = 0; + *pos++ = 2; + /* Status */ + *pos++ = status >> 8; + *pos++ = status & 0xff; + + return (u8 *) req; +} + + +static Boolean eap_tlv_check(struct eap_sm *sm, void *priv, + u8 *respData, size_t respDataLen) +{ + struct eap_hdr *resp; + u8 *pos; + size_t len; + + resp = (struct eap_hdr *) respData; + pos = (u8 *) (resp + 1); + if (respDataLen < sizeof(*resp) + 1 || *pos != EAP_TYPE_TLV || + (len = ntohs(resp->length)) > respDataLen) { + wpa_printf(MSG_INFO, "EAP-TLV: Invalid frame"); + return TRUE; + } + + return FALSE; +} + + +static void eap_tlv_process(struct eap_sm *sm, void *priv, + u8 *respData, size_t respDataLen) +{ + struct eap_tlv_data *data = priv; + struct eap_hdr *resp; + u8 *pos; + int len; + size_t left; + u8 *result_tlv = NULL; + size_t result_tlv_len = 0; + int tlv_type, mandatory, tlv_len; + + resp = (struct eap_hdr *) respData; + len = ntohs(resp->length); + pos = (u8 *) (resp + 1); + + /* Parse TLVs */ + left = be_to_host16(resp->length) - sizeof(struct eap_hdr) - 1; + pos = (u8 *) (resp + 1); + pos++; + wpa_hexdump(MSG_DEBUG, "EAP-TLV: Received TLVs", pos, left); + while (left >= 4) { + mandatory = !!(pos[0] & 0x80); + tlv_type = pos[0] & 0x3f; + tlv_type = (tlv_type << 8) | pos[1]; + tlv_len = ((int) pos[2] << 8) | pos[3]; + pos += 4; + left -= 4; + if (tlv_len > left) { + wpa_printf(MSG_DEBUG, "EAP-TLV: TLV underrun " + "(tlv_len=%d left=%lu)", tlv_len, + (unsigned long) left); + data->state = FAILURE; + return; + } + switch (tlv_type) { + case EAP_TLV_RESULT_TLV: + result_tlv = pos; + result_tlv_len = tlv_len; + break; + default: + wpa_printf(MSG_DEBUG, "EAP-TLV: Unsupported TLV Type " + "%d%s", tlv_type, + mandatory ? " (mandatory)" : ""); + if (mandatory) { + data->state = FAILURE; + return; + } + /* Ignore this TLV, but process other TLVs */ + break; + } + + pos += tlv_len; + left -= tlv_len; + } + if (left) { + wpa_printf(MSG_DEBUG, "EAP-TLV: Last TLV too short in " + "Request (left=%lu)", (unsigned long) left); + data->state = FAILURE; + return; + } + + /* Process supported TLVs */ + if (result_tlv) { + int status; + const char *requested; + + wpa_hexdump(MSG_DEBUG, "EAP-TLV: Result TLV", + result_tlv, result_tlv_len); + if (result_tlv_len < 2) { + wpa_printf(MSG_INFO, "EAP-TLV: Too short Result TLV " + "(len=%lu)", + (unsigned long) result_tlv_len); + data->state = FAILURE; + return; + } + requested = sm->tlv_request == TLV_REQ_SUCCESS ? "Success" : + "Failure"; + status = ((int) result_tlv[0] << 8) | result_tlv[1]; + if (status == EAP_TLV_RESULT_SUCCESS) { + wpa_printf(MSG_INFO, "EAP-TLV: TLV Result - Success " + "- requested %s", requested); + if (sm->tlv_request == TLV_REQ_SUCCESS) + data->state = SUCCESS; + else + data->state = FAILURE; + + } else if (status == EAP_TLV_RESULT_FAILURE) { + wpa_printf(MSG_INFO, "EAP-TLV: TLV Result - Failure - " + "requested %s", requested); + if (sm->tlv_request == TLV_REQ_FAILURE) + data->state = SUCCESS; + else + data->state = FAILURE; + } else { + wpa_printf(MSG_INFO, "EAP-TLV: Unknown TLV Result " + "Status %d", status); + data->state = FAILURE; + } + } +} + + +static Boolean eap_tlv_isDone(struct eap_sm *sm, void *priv) +{ + struct eap_tlv_data *data = priv; + return data->state != CONTINUE; +} + + +static Boolean eap_tlv_isSuccess(struct eap_sm *sm, void *priv) +{ + struct eap_tlv_data *data = priv; + return data->state == SUCCESS; +} + + +const struct eap_method eap_method_tlv = +{ + .method = EAP_TYPE_TLV, + .name = "TLV", + .init = eap_tlv_init, + .reset = eap_tlv_reset, + .buildReq = eap_tlv_buildReq, + .check = eap_tlv_check, + .process = eap_tlv_process, + .isDone = eap_tlv_isDone, + .isSuccess = eap_tlv_isSuccess, +}; diff --git a/contrib/hostapd-0.4.9/eap_ttls.c b/contrib/hostapd-0.4.9/eap_ttls.c new file mode 100644 index 0000000000..569b1c3cc2 --- /dev/null +++ b/contrib/hostapd-0.4.9/eap_ttls.c @@ -0,0 +1,1193 @@ +/* + * hostapd / EAP-TTLS (draft-ietf-pppext-eap-ttls-05.txt) + * Copyright (c) 2004-2005, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include +#include +#include +#include + +#include "hostapd.h" +#include "common.h" +#include "eap_i.h" +#include "eap_tls_common.h" +#include "ms_funcs.h" +#include "md5.h" +#include "crypto.h" +#include "tls.h" +#include "eap_ttls.h" + +#define EAP_TTLS_VERSION 0 + + +static void eap_ttls_reset(struct eap_sm *sm, void *priv); + + +struct eap_ttls_data { + struct eap_ssl_data ssl; + enum { + START, PHASE1, PHASE2_START, PHASE2_METHOD, + PHASE2_MSCHAPV2_RESP, SUCCESS, FAILURE + } state; + + int ttls_version; + const struct eap_method *phase2_method; + void *phase2_priv; + int mschapv2_resp_ok; + u8 mschapv2_auth_response[20]; + u8 mschapv2_ident; +}; + + +static const char * eap_ttls_state_txt(int state) +{ + switch (state) { + case START: + return "START"; + case PHASE1: + return "PHASE1"; + case PHASE2_START: + return "PHASE2_START"; + case PHASE2_METHOD: + return "PHASE2_METHOD"; + case PHASE2_MSCHAPV2_RESP: + return "PHASE2_MSCHAPV2_RESP"; + case SUCCESS: + return "SUCCESS"; + case FAILURE: + return "FAILURE"; + default: + return "Unknown?!"; + } +} + + +static void eap_ttls_state(struct eap_ttls_data *data, int state) +{ + wpa_printf(MSG_DEBUG, "EAP-TTLS: %s -> %s", + eap_ttls_state_txt(data->state), + eap_ttls_state_txt(state)); + data->state = state; +} + + +static u8 * eap_ttls_avp_hdr(u8 *avphdr, u32 avp_code, u32 vendor_id, + int mandatory, size_t len) +{ + struct ttls_avp_vendor *avp; + u8 flags; + size_t hdrlen; + + avp = (struct ttls_avp_vendor *) avphdr; + flags = mandatory ? AVP_FLAGS_MANDATORY : 0; + if (vendor_id) { + flags |= AVP_FLAGS_VENDOR; + hdrlen = sizeof(*avp); + avp->vendor_id = host_to_be32(vendor_id); + } else { + hdrlen = sizeof(struct ttls_avp); + } + + avp->avp_code = host_to_be32(avp_code); + avp->avp_length = host_to_be32((flags << 24) | (hdrlen + len)); + + return avphdr + hdrlen; +} + + +static int eap_ttls_avp_encapsulate(u8 **resp, size_t *resp_len, u32 avp_code, + int mandatory) +{ + u8 *avp, *pos; + + avp = malloc(sizeof(struct ttls_avp) + *resp_len + 4); + if (avp == NULL) { + free(*resp); + *resp_len = 0; + return -1; + } + + pos = eap_ttls_avp_hdr(avp, avp_code, 0, mandatory, *resp_len); + memcpy(pos, *resp, *resp_len); + pos += *resp_len; + AVP_PAD(avp, pos); + free(*resp); + *resp = avp; + *resp_len = pos - avp; + return 0; +} + + +struct eap_ttls_avp { + /* Note: eap is allocated memory; caller is responsible for freeing + * it. All the other pointers are pointing to the packet data, i.e., + * they must not be freed separately. */ + u8 *eap; + size_t eap_len; + u8 *user_name; + size_t user_name_len; + u8 *user_password; + size_t user_password_len; + u8 *chap_challenge; + size_t chap_challenge_len; + u8 *chap_password; + size_t chap_password_len; + u8 *mschap_challenge; + size_t mschap_challenge_len; + u8 *mschap_response; + size_t mschap_response_len; + u8 *mschap2_response; + size_t mschap2_response_len; +}; + + +static int eap_ttls_avp_parse(u8 *buf, size_t len, struct eap_ttls_avp *parse) +{ + struct ttls_avp *avp; + u8 *pos; + int left; + + pos = buf; + left = len; + memset(parse, 0, sizeof(*parse)); + + while (left > 0) { + u32 avp_code, avp_length, vendor_id = 0; + u8 avp_flags, *dpos; + size_t pad, dlen; + avp = (struct ttls_avp *) pos; + avp_code = be_to_host32(avp->avp_code); + avp_length = be_to_host32(avp->avp_length); + avp_flags = (avp_length >> 24) & 0xff; + avp_length &= 0xffffff; + wpa_printf(MSG_DEBUG, "EAP-TTLS: AVP: code=%d flags=0x%02x " + "length=%d", (int) avp_code, avp_flags, + (int) avp_length); + if (avp_length > left) { + wpa_printf(MSG_WARNING, "EAP-TTLS: AVP overflow " + "(len=%d, left=%d) - dropped", + (int) avp_length, left); + return -1; + } + dpos = (u8 *) (avp + 1); + dlen = avp_length - sizeof(*avp); + if (avp_flags & AVP_FLAGS_VENDOR) { + if (dlen < 4) { + wpa_printf(MSG_WARNING, "EAP-TTLS: vendor AVP " + "underflow"); + return -1; + } + vendor_id = be_to_host32(* (u32 *) dpos); + wpa_printf(MSG_DEBUG, "EAP-TTLS: AVP vendor_id %d", + (int) vendor_id); + dpos += 4; + dlen -= 4; + } + + wpa_hexdump(MSG_DEBUG, "EAP-TTLS: AVP data", dpos, dlen); + + if (vendor_id == 0 && avp_code == RADIUS_ATTR_EAP_MESSAGE) { + wpa_printf(MSG_DEBUG, "EAP-TTLS: AVP - EAP Message"); + if (parse->eap == NULL) { + parse->eap = malloc(dlen); + if (parse->eap == NULL) { + wpa_printf(MSG_WARNING, "EAP-TTLS: " + "failed to allocate memory " + "for Phase 2 EAP data"); + return -1; + } + memcpy(parse->eap, dpos, dlen); + parse->eap_len = dlen; + } else { + u8 *neweap = realloc(parse->eap, + parse->eap_len + dlen); + if (neweap == NULL) { + wpa_printf(MSG_WARNING, "EAP-TTLS: " + "failed to allocate memory " + "for Phase 2 EAP data"); + free(parse->eap); + parse->eap = NULL; + return -1; + } + memcpy(neweap + parse->eap_len, dpos, dlen); + parse->eap = neweap; + parse->eap_len += dlen; + } + } else if (vendor_id == 0 && + avp_code == RADIUS_ATTR_USER_NAME) { + wpa_hexdump_ascii(MSG_DEBUG, "EAP-TTLS: User-Name", + dpos, dlen); + parse->user_name = dpos; + parse->user_name_len = dlen; + } else if (vendor_id == 0 && + avp_code == RADIUS_ATTR_USER_PASSWORD) { + u8 *password = dpos; + size_t password_len = dlen; + while (password_len > 0 && + password[password_len - 1] == '\0') { + password_len--; + } + wpa_hexdump_ascii_key(MSG_DEBUG, "EAP-TTLS: " + "User-Password (PAP)", + password, password_len); + parse->user_password = password; + parse->user_password_len = password_len; + } else if (vendor_id == 0 && + avp_code == RADIUS_ATTR_CHAP_CHALLENGE) { + wpa_hexdump(MSG_DEBUG, + "EAP-TTLS: CHAP-Challenge (CHAP)", + dpos, dlen); + parse->chap_challenge = dpos; + parse->chap_challenge_len = dlen; + } else if (vendor_id == 0 && + avp_code == RADIUS_ATTR_CHAP_PASSWORD) { + wpa_hexdump(MSG_DEBUG, + "EAP-TTLS: CHAP-Password (CHAP)", + dpos, dlen); + parse->chap_password = dpos; + parse->chap_password_len = dlen; + } else if (vendor_id == RADIUS_VENDOR_ID_MICROSOFT && + avp_code == RADIUS_ATTR_MS_CHAP_CHALLENGE) { + wpa_hexdump(MSG_DEBUG, + "EAP-TTLS: MS-CHAP-Challenge", + dpos, dlen); + parse->mschap_challenge = dpos; + parse->mschap_challenge_len = dlen; + } else if (vendor_id == RADIUS_VENDOR_ID_MICROSOFT && + avp_code == RADIUS_ATTR_MS_CHAP_RESPONSE) { + wpa_hexdump(MSG_DEBUG, + "EAP-TTLS: MS-CHAP-Response (MSCHAP)", + dpos, dlen); + parse->mschap_response = dpos; + parse->mschap_response_len = dlen; + } else if (vendor_id == RADIUS_VENDOR_ID_MICROSOFT && + avp_code == RADIUS_ATTR_MS_CHAP2_RESPONSE) { + wpa_hexdump(MSG_DEBUG, + "EAP-TTLS: MS-CHAP2-Response (MSCHAPV2)", + dpos, dlen); + parse->mschap2_response = dpos; + parse->mschap2_response_len = dlen; + } else if (avp_flags & AVP_FLAGS_MANDATORY) { + wpa_printf(MSG_WARNING, "EAP-TTLS: Unsupported " + "mandatory AVP code %d vendor_id %d - " + "dropped", (int) avp_code, (int) vendor_id); + return -1; + } else { + wpa_printf(MSG_DEBUG, "EAP-TTLS: Ignoring unsupported " + "AVP code %d vendor_id %d", + (int) avp_code, (int) vendor_id); + } + + pad = (4 - (avp_length & 3)) & 3; + pos += avp_length + pad; + left -= avp_length + pad; + } + + return 0; +} + + +static void * eap_ttls_init(struct eap_sm *sm) +{ + struct eap_ttls_data *data; + + data = malloc(sizeof(*data)); + if (data == NULL) + return data; + memset(data, 0, sizeof(*data)); + data->ttls_version = EAP_TTLS_VERSION; + data->state = START; + + if (eap_tls_ssl_init(sm, &data->ssl, 0)) { + wpa_printf(MSG_INFO, "EAP-TTLS: Failed to initialize SSL."); + eap_ttls_reset(sm, data); + return NULL; + } + + return data; +} + + +static void eap_ttls_reset(struct eap_sm *sm, void *priv) +{ + struct eap_ttls_data *data = priv; + if (data == NULL) + return; + if (data->phase2_priv && data->phase2_method) + data->phase2_method->reset(sm, data->phase2_priv); + eap_tls_ssl_deinit(sm, &data->ssl); + free(data); +} + + +static u8 * eap_ttls_build_start(struct eap_sm *sm, struct eap_ttls_data *data, + int id, size_t *reqDataLen) +{ + struct eap_hdr *req; + u8 *pos; + + *reqDataLen = sizeof(*req) + 2; + req = malloc(*reqDataLen); + if (req == NULL) { + wpa_printf(MSG_ERROR, "EAP-TTLS: Failed to allocate memory for" + " request"); + eap_ttls_state(data, FAILURE); + return NULL; + } + + req->code = EAP_CODE_REQUEST; + req->identifier = id; + req->length = htons(*reqDataLen); + pos = (u8 *) (req + 1); + *pos++ = EAP_TYPE_TTLS; + *pos = EAP_TLS_FLAGS_START | data->ttls_version; + + eap_ttls_state(data, PHASE1); + + return (u8 *) req; +} + + +static u8 * eap_ttls_build_req(struct eap_sm *sm, struct eap_ttls_data *data, + int id, size_t *reqDataLen) +{ + int res; + u8 *req; + + res = eap_tls_buildReq_helper(sm, &data->ssl, EAP_TYPE_TTLS, + data->ttls_version, id, &req, + reqDataLen); + + if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) { + wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase1 done, starting " + "Phase2"); + eap_ttls_state(data, PHASE2_START); + } + + if (res == 1) + return eap_tls_build_ack(reqDataLen, id, EAP_TYPE_TTLS, + data->ttls_version); + return req; +} + + +static u8 * eap_ttls_encrypt(struct eap_sm *sm, struct eap_ttls_data *data, + int id, u8 *plain, size_t plain_len, + size_t *out_len) +{ + int res; + u8 *pos; + struct eap_hdr *req; + + /* TODO: add support for fragmentation, if needed. This will need to + * add TLS Message Length field, if the frame is fragmented. */ + req = malloc(sizeof(struct eap_hdr) + 2 + data->ssl.tls_out_limit); + if (req == NULL) + return NULL; + + req->code = EAP_CODE_REQUEST; + req->identifier = id; + + pos = (u8 *) (req + 1); + *pos++ = EAP_TYPE_TTLS; + *pos++ = data->ttls_version; + + res = tls_connection_encrypt(sm->ssl_ctx, data->ssl.conn, + plain, plain_len, + pos, data->ssl.tls_out_limit); + if (res < 0) { + wpa_printf(MSG_INFO, "EAP-TTLS: Failed to encrypt Phase 2 " + "data"); + free(req); + return NULL; + } + + *out_len = sizeof(struct eap_hdr) + 2 + res; + req->length = host_to_be16(*out_len); + return (u8 *) req; +} + + +static u8 * eap_ttls_build_phase2_eap_req(struct eap_sm *sm, + struct eap_ttls_data *data, + int id, size_t *reqDataLen) +{ + u8 *req, *encr_req; + size_t req_len; + + + req = data->phase2_method->buildReq(sm, data->phase2_priv, id, + &req_len); + if (req == NULL) + return NULL; + + wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS/EAP: Encapsulate Phase 2 data", + req, req_len); + + if (eap_ttls_avp_encapsulate(&req, &req_len, RADIUS_ATTR_EAP_MESSAGE, + 1) < 0) { + wpa_printf(MSG_DEBUG, "EAP-TTLS/EAP: Failed to encapsulate " + "packet"); + return NULL; + } + + wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS/EAP: Encrypt encapsulated Phase " + "2 data", req, req_len); + + encr_req = eap_ttls_encrypt(sm, data, id, req, req_len, reqDataLen); + free(req); + + return encr_req; +} + + +static u8 * eap_ttls_build_phase2_mschapv2(struct eap_sm *sm, + struct eap_ttls_data *data, + int id, size_t *reqDataLen) +{ + u8 *req, *encr_req, *pos, *end; + size_t req_len; + int i; + + pos = req = malloc(100); + if (req == NULL) + return NULL; + end = req + 200; + + if (data->mschapv2_resp_ok) { + pos = eap_ttls_avp_hdr(pos, RADIUS_ATTR_MS_CHAP2_SUCCESS, + RADIUS_VENDOR_ID_MICROSOFT, 1, 43); + *pos++ = data->mschapv2_ident; + pos += snprintf((char *) pos, end - pos, "S="); + for (i = 0; i < sizeof(data->mschapv2_auth_response); i++) { + pos += snprintf((char *) pos, end - pos, "%02X", + data->mschapv2_auth_response[i]); + } + } else { + pos = eap_ttls_avp_hdr(pos, RADIUS_ATTR_MS_CHAP_ERROR, + RADIUS_VENDOR_ID_MICROSOFT, 1, 6); + memcpy(pos, "Failed", 6); + pos += 6; + AVP_PAD(req, pos); + } + + req_len = pos - req; + wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Encrypting Phase 2 " + "data", req, req_len); + + encr_req = eap_ttls_encrypt(sm, data, id, req, req_len, reqDataLen); + free(req); + + return encr_req; +} + + +static u8 * eap_ttls_buildReq(struct eap_sm *sm, void *priv, int id, + size_t *reqDataLen) +{ + struct eap_ttls_data *data = priv; + + switch (data->state) { + case START: + return eap_ttls_build_start(sm, data, id, reqDataLen); + case PHASE1: + return eap_ttls_build_req(sm, data, id, reqDataLen); + case PHASE2_METHOD: + return eap_ttls_build_phase2_eap_req(sm, data, id, reqDataLen); + case PHASE2_MSCHAPV2_RESP: + return eap_ttls_build_phase2_mschapv2(sm, data, id, + reqDataLen); + default: + wpa_printf(MSG_DEBUG, "EAP-TTLS: %s - unexpected state %d", + __func__, data->state); + return NULL; + } +} + + +static Boolean eap_ttls_check(struct eap_sm *sm, void *priv, + u8 *respData, size_t respDataLen) +{ + struct eap_hdr *resp; + u8 *pos; + size_t len; + + resp = (struct eap_hdr *) respData; + pos = (u8 *) (resp + 1); + if (respDataLen < sizeof(*resp) + 2 || *pos != EAP_TYPE_TTLS || + (len = ntohs(resp->length)) > respDataLen) { + wpa_printf(MSG_INFO, "EAP-TTLS: Invalid frame"); + return TRUE; + } + + return FALSE; +} + + +static void eap_ttls_process_phase2_pap(struct eap_sm *sm, + struct eap_ttls_data *data, + const u8 *user_password, + size_t user_password_len) +{ + /* TODO: add support for verifying that the user entry accepts + * EAP-TTLS/PAP. */ + if (!sm->user || !sm->user->password) { + wpa_printf(MSG_DEBUG, "EAP-TTLS/PAP: No user password " + "configured"); + eap_ttls_state(data, FAILURE); + return; + } + + if (sm->user->password_len != user_password_len || + memcmp(sm->user->password, user_password, user_password_len) != 0) + { + wpa_printf(MSG_DEBUG, "EAP-TTLS/PAP: Invalid user password"); + eap_ttls_state(data, FAILURE); + return; + } + + wpa_printf(MSG_DEBUG, "EAP-TTLS/PAP: Correct user password"); + eap_ttls_state(data, SUCCESS); +} + + +static void eap_ttls_process_phase2_chap(struct eap_sm *sm, + struct eap_ttls_data *data, + const u8 *challenge, + size_t challenge_len, + const u8 *password, + size_t password_len) +{ + u8 *chal, hash[MD5_MAC_LEN]; + const u8 *addr[3]; + size_t len[3]; + + if (challenge == NULL || password == NULL || + challenge_len != EAP_TTLS_CHAP_CHALLENGE_LEN || + password_len != 1 + EAP_TTLS_CHAP_PASSWORD_LEN) { + wpa_printf(MSG_DEBUG, "EAP-TTLS/CHAP: Invalid CHAP attributes " + "(challenge len %lu password len %lu)", + (unsigned long) challenge_len, + (unsigned long) password_len); + eap_ttls_state(data, FAILURE); + return; + } + + /* TODO: add support for verifying that the user entry accepts + * EAP-TTLS/CHAP. */ + if (!sm->user || !sm->user->password) { + wpa_printf(MSG_DEBUG, "EAP-TTLS/CHAP: No user password " + "configured"); + eap_ttls_state(data, FAILURE); + return; + } + + chal = eap_tls_derive_key(sm, &data->ssl, "ttls challenge", + EAP_TTLS_CHAP_CHALLENGE_LEN + 1); + if (chal == NULL) { + wpa_printf(MSG_DEBUG, "EAP-TTLS/CHAP: Failed to generate " + "challenge from TLS data"); + eap_ttls_state(data, FAILURE); + return; + } + + if (memcmp(challenge, chal, EAP_TTLS_CHAP_CHALLENGE_LEN) != 0 || + password[0] != chal[EAP_TTLS_CHAP_CHALLENGE_LEN]) { + wpa_printf(MSG_DEBUG, "EAP-TTLS/CHAP: Challenge mismatch"); + free(chal); + eap_ttls_state(data, FAILURE); + return; + } + free(chal); + + /* MD5(Ident + Password + Challenge) */ + addr[0] = password; + len[0] = 1; + addr[1] = sm->user->password; + len[1] = sm->user->password_len; + addr[2] = challenge; + len[2] = challenge_len; + md5_vector(3, addr, len, hash); + + if (memcmp(hash, password + 1, EAP_TTLS_CHAP_PASSWORD_LEN) == 0) { + wpa_printf(MSG_DEBUG, "EAP-TTLS/CHAP: Correct user password"); + eap_ttls_state(data, SUCCESS); + } else { + wpa_printf(MSG_DEBUG, "EAP-TTLS/CHAP: Invalid user password"); + eap_ttls_state(data, FAILURE); + } +} + + +static void eap_ttls_process_phase2_mschap(struct eap_sm *sm, + struct eap_ttls_data *data, + u8 *challenge, size_t challenge_len, + u8 *response, size_t response_len) +{ + u8 *chal, nt_response[24]; + + if (challenge == NULL || response == NULL || + challenge_len != EAP_TTLS_MSCHAP_CHALLENGE_LEN || + response_len != EAP_TTLS_MSCHAP_RESPONSE_LEN) { + wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAP: Invalid MS-CHAP " + "attributes (challenge len %lu response len %lu)", + (unsigned long) challenge_len, + (unsigned long) response_len); + eap_ttls_state(data, FAILURE); + return; + } + + /* TODO: add support for verifying that the user entry accepts + * EAP-TTLS/MSCHAP. */ + if (!sm->user || !sm->user->password) { + wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAP: No user password " + "configured"); + eap_ttls_state(data, FAILURE); + return; + } + + chal = eap_tls_derive_key(sm, &data->ssl, "ttls challenge", + EAP_TTLS_MSCHAP_CHALLENGE_LEN + 1); + if (chal == NULL) { + wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAP: Failed to generate " + "challenge from TLS data"); + eap_ttls_state(data, FAILURE); + return; + } + + if (memcmp(challenge, chal, EAP_TTLS_MSCHAP_CHALLENGE_LEN) != 0 || + response[0] != chal[EAP_TTLS_MSCHAP_CHALLENGE_LEN]) { + wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAP: Challenge mismatch"); + free(chal); + eap_ttls_state(data, FAILURE); + return; + } + free(chal); + + nt_challenge_response(challenge, sm->user->password, + sm->user->password_len, nt_response); + + if (memcmp(nt_response, response + 2 + 24, 24) == 0) { + wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAP: Correct response"); + eap_ttls_state(data, SUCCESS); + } else { + wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAP: Invalid NT-Response"); + wpa_hexdump(MSG_MSGDUMP, "EAP-TTLS/MSCHAP: Received", + response + 2 + 24, 24); + wpa_hexdump(MSG_MSGDUMP, "EAP-TTLS/MSCHAP: Expected", + nt_response, 24); + eap_ttls_state(data, FAILURE); + } +} + + +static void eap_ttls_process_phase2_mschapv2(struct eap_sm *sm, + struct eap_ttls_data *data, + u8 *challenge, + size_t challenge_len, + u8 *response, size_t response_len) +{ + u8 *chal, *username, nt_response[24], *pos, *rx_resp, *peer_challenge, + *auth_challenge; + size_t username_len; + int i; + + if (challenge == NULL || response == NULL || + challenge_len != EAP_TTLS_MSCHAPV2_CHALLENGE_LEN || + response_len != EAP_TTLS_MSCHAPV2_RESPONSE_LEN) { + wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Invalid MS-CHAP2 " + "attributes (challenge len %lu response len %lu)", + (unsigned long) challenge_len, + (unsigned long) response_len); + eap_ttls_state(data, FAILURE); + return; + } + + /* TODO: add support for verifying that the user entry accepts + * EAP-TTLS/MSCHAPV2. */ + if (!sm->user || !sm->user->password) { + wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: No user password " + "configured"); + eap_ttls_state(data, FAILURE); + return; + } + + /* MSCHAPv2 does not include optional domain name in the + * challenge-response calculation, so remove domain prefix + * (if present). */ + username = sm->identity; + username_len = sm->identity_len; + pos = username; + for (i = 0; i < username_len; i++) { + if (username[i] == '\\') { + username_len -= i + 1; + username += i + 1; + break; + } + } + + chal = eap_tls_derive_key(sm, &data->ssl, "ttls challenge", + EAP_TTLS_MSCHAPV2_CHALLENGE_LEN + 1); + if (chal == NULL) { + wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Failed to generate " + "challenge from TLS data"); + eap_ttls_state(data, FAILURE); + return; + } + + if (memcmp(challenge, chal, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN) != 0 || + response[0] != chal[EAP_TTLS_MSCHAPV2_CHALLENGE_LEN]) { + wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Challenge mismatch"); + free(chal); + eap_ttls_state(data, FAILURE); + return; + } + free(chal); + + auth_challenge = challenge; + peer_challenge = response + 2; + + wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-TTLS/MSCHAPV2: User", + username, username_len); + wpa_hexdump(MSG_MSGDUMP, "EAP-TTLS/MSCHAPV2: auth_challenge", + auth_challenge, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN); + wpa_hexdump(MSG_MSGDUMP, "EAP-TTLS/MSCHAPV2: peer_challenge", + peer_challenge, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN); + + generate_nt_response(auth_challenge, peer_challenge, + username, username_len, + sm->user->password, sm->user->password_len, + nt_response); + + rx_resp = response + 2 + EAP_TTLS_MSCHAPV2_CHALLENGE_LEN + 8; + if (memcmp(nt_response, rx_resp, 24) == 0) { + wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Correct " + "NT-Response"); + data->mschapv2_resp_ok = 1; + + generate_authenticator_response(sm->user->password, + sm->user->password_len, + peer_challenge, + auth_challenge, + username, username_len, + nt_response, + data->mschapv2_auth_response); + + } else { + wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Invalid " + "NT-Response"); + wpa_hexdump(MSG_MSGDUMP, "EAP-TTLS/MSCHAPV2: Received", + rx_resp, 24); + wpa_hexdump(MSG_MSGDUMP, "EAP-TTLS/MSCHAPV2: Expected", + nt_response, 24); + data->mschapv2_resp_ok = 0; + } + eap_ttls_state(data, PHASE2_MSCHAPV2_RESP); + data->mschapv2_ident = response[0]; +} + + +static int eap_ttls_phase2_eap_init(struct eap_sm *sm, + struct eap_ttls_data *data, u8 eap_type) +{ + if (data->phase2_priv && data->phase2_method) { + data->phase2_method->reset(sm, data->phase2_priv); + data->phase2_method = NULL; + data->phase2_priv = NULL; + } + data->phase2_method = eap_sm_get_eap_methods(eap_type); + if (!data->phase2_method) + return -1; + + sm->init_phase2 = 1; + data->phase2_priv = data->phase2_method->init(sm); + sm->init_phase2 = 0; + return 0; +} + + +static void eap_ttls_process_phase2_eap_response(struct eap_sm *sm, + struct eap_ttls_data *data, + u8 *in_data, size_t in_len) +{ + u8 next_type = EAP_TYPE_NONE; + struct eap_hdr *hdr; + u8 *pos; + size_t left; + + if (data->phase2_priv == NULL) { + wpa_printf(MSG_DEBUG, "EAP-TTLS/EAP: %s - Phase2 not " + "initialized?!", __func__); + return; + } + + hdr = (struct eap_hdr *) in_data; + pos = (u8 *) (hdr + 1); + left = in_len - sizeof(*hdr); + + if (in_len > sizeof(*hdr) && *pos == EAP_TYPE_NAK) { + wpa_hexdump(MSG_DEBUG, "EAP-TTLS/EAP: Phase2 type Nak'ed; " + "allowed types", pos + 1, left - 1); + eap_sm_process_nak(sm, pos + 1, left - 1); + if (sm->user && sm->user_eap_method_index < EAP_MAX_METHODS && + sm->user->methods[sm->user_eap_method_index] != + EAP_TYPE_NONE) { + next_type = + sm->user->methods[sm->user_eap_method_index++]; + wpa_printf(MSG_DEBUG, "EAP-TTLS: try EAP type %d", + next_type); + eap_ttls_phase2_eap_init(sm, data, next_type); + } else { + eap_ttls_state(data, FAILURE); + } + return; + } + + if (data->phase2_method->check(sm, data->phase2_priv, in_data, + in_len)) { + wpa_printf(MSG_DEBUG, "EAP-TTLS/EAP: Phase2 check() asked to " + "ignore the packet"); + return; + } + + data->phase2_method->process(sm, data->phase2_priv, in_data, in_len); + + if (!data->phase2_method->isDone(sm, data->phase2_priv)) + return; + + if (!data->phase2_method->isSuccess(sm, data->phase2_priv)) { + wpa_printf(MSG_DEBUG, "EAP-TTLS/EAP: Phase2 method failed"); + eap_ttls_state(data, FAILURE); + return; + } + + switch (data->state) { + case PHASE2_START: + if (eap_user_get(sm, sm->identity, sm->identity_len, 1) != 0) { + wpa_hexdump_ascii(MSG_DEBUG, "EAP_TTLS: Phase2 " + "Identity not found in the user " + "database", + sm->identity, sm->identity_len); + eap_ttls_state(data, FAILURE); + break; + } + + eap_ttls_state(data, PHASE2_METHOD); + next_type = sm->user->methods[0]; + sm->user_eap_method_index = 1; + wpa_printf(MSG_DEBUG, "EAP-TTLS: try EAP type %d", next_type); + break; + case PHASE2_METHOD: + eap_ttls_state(data, SUCCESS); + break; + case FAILURE: + break; + default: + wpa_printf(MSG_DEBUG, "EAP-TTLS: %s - unexpected state %d", + __func__, data->state); + break; + } + + eap_ttls_phase2_eap_init(sm, data, next_type); +} + + +static void eap_ttls_process_phase2_eap(struct eap_sm *sm, + struct eap_ttls_data *data, + const u8 *eap, size_t eap_len) +{ + struct eap_hdr *hdr; + size_t len; + + if (data->state == PHASE2_START) { + wpa_printf(MSG_DEBUG, "EAP-TTLS/EAP: initializing Phase 2"); + if (eap_ttls_phase2_eap_init(sm, data, EAP_TYPE_IDENTITY) < 0) + { + wpa_printf(MSG_DEBUG, "EAP-TTLS/EAP: failed to " + "initialize EAP-Identity"); + return; + } + } + + if (eap_len < sizeof(*hdr)) { + wpa_printf(MSG_DEBUG, "EAP-TTLS/EAP: too short Phase 2 EAP " + "packet (len=%lu)", (unsigned long) eap_len); + return; + } + + hdr = (struct eap_hdr *) eap; + len = be_to_host16(hdr->length); + wpa_printf(MSG_DEBUG, "EAP-TTLS/EAP: received Phase 2 EAP: code=%d " + "identifier=%d length=%lu", hdr->code, hdr->identifier, + (unsigned long) len); + if (len > eap_len) { + wpa_printf(MSG_INFO, "EAP-TTLS/EAP: Length mismatch in Phase 2" + " EAP frame (hdr len=%lu, data len in AVP=%lu)", + (unsigned long) len, (unsigned long) eap_len); + return; + } + + switch (hdr->code) { + case EAP_CODE_RESPONSE: + eap_ttls_process_phase2_eap_response(sm, data, (u8 *) hdr, + len); + break; + default: + wpa_printf(MSG_INFO, "EAP-TTLS/EAP: Unexpected code=%d in " + "Phase 2 EAP header", hdr->code); + break; + } +} + + +static void eap_ttls_process_phase2(struct eap_sm *sm, + struct eap_ttls_data *data, + struct eap_hdr *resp, + u8 *in_data, size_t in_len) +{ + u8 *in_decrypted; + int buf_len, len_decrypted, res; + struct eap_ttls_avp parse; + + wpa_printf(MSG_DEBUG, "EAP-TTLS: received %lu bytes encrypted data for" + " Phase 2", (unsigned long) in_len); + + res = eap_tls_data_reassemble(sm, &data->ssl, &in_data, &in_len); + if (res < 0 || res == 1) + return; + + buf_len = in_len; + if (data->ssl.tls_in_total > buf_len) + buf_len = data->ssl.tls_in_total; + in_decrypted = malloc(buf_len); + if (in_decrypted == NULL) { + free(data->ssl.tls_in); + data->ssl.tls_in = NULL; + data->ssl.tls_in_len = 0; + wpa_printf(MSG_WARNING, "EAP-TTLS: failed to allocate memory " + "for decryption"); + return; + } + + len_decrypted = tls_connection_decrypt(sm->ssl_ctx, data->ssl.conn, + in_data, in_len, + in_decrypted, buf_len); + free(data->ssl.tls_in); + data->ssl.tls_in = NULL; + data->ssl.tls_in_len = 0; + if (len_decrypted < 0) { + wpa_printf(MSG_INFO, "EAP-TTLS: Failed to decrypt Phase 2 " + "data"); + free(in_decrypted); + eap_ttls_state(data, FAILURE); + return; + } + + wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: Decrypted Phase 2 EAP", + in_decrypted, len_decrypted); + + if (eap_ttls_avp_parse(in_decrypted, len_decrypted, &parse) < 0) { + wpa_printf(MSG_DEBUG, "EAP-TTLS: Failed to parse AVPs"); + free(in_decrypted); + eap_ttls_state(data, FAILURE); + return; + } + + if (parse.user_name) { + free(sm->identity); + sm->identity = malloc(parse.user_name_len); + if (sm->identity) { + memcpy(sm->identity, parse.user_name, + parse.user_name_len); + sm->identity_len = parse.user_name_len; + } + if (eap_user_get(sm, parse.user_name, parse.user_name_len, 1) + != 0) { + wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase2 Identity not " + "found in the user database"); + eap_ttls_state(data, FAILURE); + goto done; + } + } + + if (parse.eap) { + eap_ttls_process_phase2_eap(sm, data, parse.eap, + parse.eap_len); + } else if (parse.user_password) { + eap_ttls_process_phase2_pap(sm, data, parse.user_password, + parse.user_password_len); + } else if (parse.chap_password) { + eap_ttls_process_phase2_chap(sm, data, + parse.chap_challenge, + parse.chap_challenge_len, + parse.chap_password, + parse.chap_password_len); + } else if (parse.mschap_response) { + eap_ttls_process_phase2_mschap(sm, data, + parse.mschap_challenge, + parse.mschap_challenge_len, + parse.mschap_response, + parse.mschap_response_len); + } else if (parse.mschap2_response) { + eap_ttls_process_phase2_mschapv2(sm, data, + parse.mschap_challenge, + parse.mschap_challenge_len, + parse.mschap2_response, + parse.mschap2_response_len); + } + +done: + free(in_decrypted); + free(parse.eap); + } + + +static void eap_ttls_process(struct eap_sm *sm, void *priv, + u8 *respData, size_t respDataLen) +{ + struct eap_ttls_data *data = priv; + struct eap_hdr *resp; + u8 *pos, flags; + int left; + unsigned int tls_msg_len; + int peer_version; + + resp = (struct eap_hdr *) respData; + pos = (u8 *) (resp + 1); + pos++; + flags = *pos++; + left = htons(resp->length) - sizeof(struct eap_hdr) - 2; + wpa_printf(MSG_DEBUG, "EAP-TTLS: Received packet(len=%lu) - " + "Flags 0x%02x", (unsigned long) respDataLen, flags); + peer_version = flags & EAP_PEAP_VERSION_MASK; + if (peer_version < data->ttls_version) { + wpa_printf(MSG_DEBUG, "EAP-TTLS: peer ver=%d, own ver=%d; " + "use version %d", + peer_version, data->ttls_version, peer_version); + data->ttls_version = peer_version; + + } + if (flags & EAP_TLS_FLAGS_LENGTH_INCLUDED) { + if (left < 4) { + wpa_printf(MSG_INFO, "EAP-TTLS: Short frame with TLS " + "length"); + eap_ttls_state(data, FAILURE); + return; + } + tls_msg_len = (pos[0] << 24) | (pos[1] << 16) | (pos[2] << 8) | + pos[3]; + wpa_printf(MSG_DEBUG, "EAP-TTLS: TLS Message Length: %d", + tls_msg_len); + if (data->ssl.tls_in_left == 0) { + data->ssl.tls_in_total = tls_msg_len; + data->ssl.tls_in_left = tls_msg_len; + free(data->ssl.tls_in); + data->ssl.tls_in = NULL; + data->ssl.tls_in_len = 0; + } + pos += 4; + left -= 4; + } + + switch (data->state) { + case PHASE1: + if (eap_tls_process_helper(sm, &data->ssl, pos, left) < 0) { + wpa_printf(MSG_INFO, "EAP-TTLS: TLS processing " + "failed"); + eap_ttls_state(data, FAILURE); + } + break; + case PHASE2_START: + case PHASE2_METHOD: + eap_ttls_process_phase2(sm, data, resp, pos, left); + break; + case PHASE2_MSCHAPV2_RESP: + if (data->mschapv2_resp_ok && left == 0) { + wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Peer " + "acknowledged response"); + eap_ttls_state(data, SUCCESS); + } else if (!data->mschapv2_resp_ok) { + wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Peer " + "acknowledged error"); + eap_ttls_state(data, FAILURE); + } else { + wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Unexpected " + "frame from peer (payload len %d, expected " + "empty frame)", left); + eap_ttls_state(data, FAILURE); + } + break; + default: + wpa_printf(MSG_DEBUG, "EAP-TTLS: Unexpected state %d in %s", + data->state, __func__); + break; + } + + if (tls_connection_get_write_alerts(sm->ssl_ctx, data->ssl.conn) > 1) { + wpa_printf(MSG_INFO, "EAP-TTLS: Locally detected fatal error " + "in TLS processing"); + eap_ttls_state(data, FAILURE); + } +} + + +static Boolean eap_ttls_isDone(struct eap_sm *sm, void *priv) +{ + struct eap_ttls_data *data = priv; + return data->state == SUCCESS || data->state == FAILURE; +} + + +static u8 * eap_ttls_getKey(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_ttls_data *data = priv; + u8 *eapKeyData; + + if (data->state != SUCCESS) + return NULL; + + eapKeyData = eap_tls_derive_key(sm, &data->ssl, + "ttls keying material", + EAP_TLS_KEY_LEN); + if (eapKeyData) { + *len = EAP_TLS_KEY_LEN; + wpa_hexdump(MSG_DEBUG, "EAP-TTLS: Derived key", + eapKeyData, EAP_TLS_KEY_LEN); + } else { + wpa_printf(MSG_DEBUG, "EAP-TTLS: Failed to derive key"); + } + + return eapKeyData; +} + + +static Boolean eap_ttls_isSuccess(struct eap_sm *sm, void *priv) +{ + struct eap_ttls_data *data = priv; + return data->state == SUCCESS; +} + + +const struct eap_method eap_method_ttls = +{ + .method = EAP_TYPE_TTLS, + .name = "TTLS", + .init = eap_ttls_init, + .reset = eap_ttls_reset, + .buildReq = eap_ttls_buildReq, + .check = eap_ttls_check, + .process = eap_ttls_process, + .isDone = eap_ttls_isDone, + .getKey = eap_ttls_getKey, + .isSuccess = eap_ttls_isSuccess, +}; diff --git a/contrib/hostapd-0.4.9/eap_ttls.h b/contrib/hostapd-0.4.9/eap_ttls.h new file mode 100644 index 0000000000..f35f5a9059 --- /dev/null +++ b/contrib/hostapd-0.4.9/eap_ttls.h @@ -0,0 +1,71 @@ +/* + * WPA Supplicant / EAP-TTLS (draft-ietf-pppext-eap-ttls-03.txt) + * Copyright (c) 2004-2005, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef EAP_TTLS_H +#define EAP_TTLS_H + +struct ttls_avp { + u32 avp_code; + u32 avp_length; /* 8-bit flags, 24-bit length; + * length includes AVP header */ + /* optional 32-bit Vendor-ID */ + /* Data */ +}; + +struct ttls_avp_vendor { + u32 avp_code; + u32 avp_length; /* 8-bit flags, 24-bit length; + * length includes AVP header */ + u32 vendor_id; + /* Data */ +}; + +#define AVP_FLAGS_VENDOR 0x80 +#define AVP_FLAGS_MANDATORY 0x40 + +#define AVP_PAD(start, pos) \ +do { \ + int pad; \ + pad = (4 - (((pos) - (start)) & 3)) & 3; \ + memset((pos), 0, pad); \ + pos += pad; \ +} while(0) + + +/* RFC 2865 */ +#define RADIUS_ATTR_USER_NAME 1 +#define RADIUS_ATTR_USER_PASSWORD 2 +#define RADIUS_ATTR_CHAP_PASSWORD 3 +#define RADIUS_ATTR_REPLY_MESSAGE 18 +#define RADIUS_ATTR_CHAP_CHALLENGE 60 +#define RADIUS_ATTR_EAP_MESSAGE 79 + +/* RFC 2548 */ +#define RADIUS_VENDOR_ID_MICROSOFT 311 +#define RADIUS_ATTR_MS_CHAP_RESPONSE 1 +#define RADIUS_ATTR_MS_CHAP_ERROR 2 +#define RADIUS_ATTR_MS_CHAP_NT_ENC_PW 6 +#define RADIUS_ATTR_MS_CHAP_CHALLENGE 11 +#define RADIUS_ATTR_MS_CHAP2_RESPONSE 25 +#define RADIUS_ATTR_MS_CHAP2_SUCCESS 26 +#define RADIUS_ATTR_MS_CHAP2_CPW 27 + +#define EAP_TTLS_MSCHAPV2_CHALLENGE_LEN 16 +#define EAP_TTLS_MSCHAPV2_RESPONSE_LEN 50 +#define EAP_TTLS_MSCHAP_CHALLENGE_LEN 8 +#define EAP_TTLS_MSCHAP_RESPONSE_LEN 50 +#define EAP_TTLS_CHAP_CHALLENGE_LEN 16 +#define EAP_TTLS_CHAP_PASSWORD_LEN 16 + +#endif /* EAP_TTLS_H */ diff --git a/contrib/hostapd-0.4.9/eapol_sm.c b/contrib/hostapd-0.4.9/eapol_sm.c new file mode 100644 index 0000000000..f2d5ec7c52 --- /dev/null +++ b/contrib/hostapd-0.4.9/eapol_sm.c @@ -0,0 +1,1259 @@ +/* + * Host AP (software wireless LAN access point) user space daemon for + * Host AP kernel driver / IEEE 802.1X Authenticator - EAPOL state machine + * Copyright (c) 2002-2005, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include +#include +#include +#include +#include +#include + +#include "hostapd.h" +#include "ieee802_1x.h" +#include "eapol_sm.h" +#include "eloop.h" +#include "wpa.h" +#include "sta_info.h" +#include "eap.h" + +static struct eapol_callbacks eapol_cb; + +/* EAPOL state machines are described in IEEE Std 802.1X-REV-d11, Chap. 8.2 */ + +#define setPortAuthorized() \ +ieee802_1x_set_sta_authorized(sm->hapd, sm->sta, 1) +#define setPortUnauthorized() \ +ieee802_1x_set_sta_authorized(sm->hapd, sm->sta, 0) + +/* procedures */ +#define txCannedFail() ieee802_1x_tx_canned_eap(sm->hapd, sm->sta, 0) +#define txCannedSuccess() ieee802_1x_tx_canned_eap(sm->hapd, sm->sta, 1) +#define txReq() ieee802_1x_tx_req(sm->hapd, sm->sta) +#define sendRespToServer() ieee802_1x_send_resp_to_server(sm->hapd, sm->sta) +#define abortAuth() ieee802_1x_abort_auth(sm->hapd, sm->sta) +#define txKey() ieee802_1x_tx_key(sm->hapd, sm->sta) +#define processKey() do { } while (0) + + +/* Definitions for clarifying state machine implementation */ +#define SM_STATE(machine, state) \ +static void sm_ ## machine ## _ ## state ## _Enter(struct eapol_state_machine \ +*sm) + +#define SM_ENTRY(machine, _state, _data) \ +sm->_data.state = machine ## _ ## _state; \ +if (sm->hapd->conf->debug >= HOSTAPD_DEBUG_MINIMAL) \ + printf("IEEE 802.1X: " MACSTR " " #machine " entering state " #_state \ + "\n", MAC2STR(sm->addr)); + +#define SM_ENTER(machine, state) sm_ ## machine ## _ ## state ## _Enter(sm) + +#define SM_STEP(machine) \ +static void sm_ ## machine ## _Step(struct eapol_state_machine *sm) + +#define SM_STEP_RUN(machine) sm_ ## machine ## _Step(sm) + + +static void eapol_sm_step_run(struct eapol_state_machine *sm); +static void eapol_sm_step_cb(void *eloop_ctx, void *timeout_ctx); + + +/* Port Timers state machine - implemented as a function that will be called + * once a second as a registered event loop timeout */ + +static void eapol_port_timers_tick(void *eloop_ctx, void *timeout_ctx) +{ + struct eapol_state_machine *state = timeout_ctx; + + if (state->aWhile > 0) { + state->aWhile--; + if (state->aWhile == 0) { + wpa_printf(MSG_DEBUG, "IEEE 802.1X: " MACSTR + " - aWhile --> 0", + MAC2STR(state->addr)); + } + } + + if (state->quietWhile > 0) { + state->quietWhile--; + if (state->quietWhile == 0) { + wpa_printf(MSG_DEBUG, "IEEE 802.1X: " MACSTR + " - quietWhile --> 0", + MAC2STR(state->addr)); + } + } + + if (state->reAuthWhen > 0) { + state->reAuthWhen--; + if (state->reAuthWhen == 0) { + wpa_printf(MSG_DEBUG, "IEEE 802.1X: " MACSTR + " - reAuthWhen --> 0", + MAC2STR(state->addr)); + } + } + + eapol_sm_step_run(state); + + eloop_register_timeout(1, 0, eapol_port_timers_tick, eloop_ctx, state); +} + + + +/* Authenticator PAE state machine */ + +SM_STATE(AUTH_PAE, INITIALIZE) +{ + SM_ENTRY(AUTH_PAE, INITIALIZE, auth_pae); + sm->auth_pae.portMode = Auto; + + sm->currentId = 255; +} + + +SM_STATE(AUTH_PAE, DISCONNECTED) +{ + int from_initialize = sm->auth_pae.state == AUTH_PAE_INITIALIZE; + + if (sm->auth_pae.eapolLogoff) { + if (sm->auth_pae.state == AUTH_PAE_CONNECTING) + sm->auth_pae.authEapLogoffsWhileConnecting++; + else if (sm->auth_pae.state == AUTH_PAE_AUTHENTICATED) + sm->auth_pae.authAuthEapLogoffWhileAuthenticated++; + } + + SM_ENTRY(AUTH_PAE, DISCONNECTED, auth_pae); + + sm->authPortStatus = Unauthorized; + setPortUnauthorized(); + sm->auth_pae.reAuthCount = 0; + sm->auth_pae.eapolLogoff = FALSE; + if (!from_initialize) { + if (sm->flags & EAPOL_SM_PREAUTH) + rsn_preauth_finished(sm->hapd, sm->sta, 0); + else + ieee802_1x_finished(sm->hapd, sm->sta, 0); + } +} + + +SM_STATE(AUTH_PAE, RESTART) +{ + if (sm->auth_pae.state == AUTH_PAE_AUTHENTICATED) { + if (sm->reAuthenticate) + sm->auth_pae.authAuthReauthsWhileAuthenticated++; + if (sm->auth_pae.eapolStart) + sm->auth_pae.authAuthEapStartsWhileAuthenticated++; + if (sm->auth_pae.eapolLogoff) + sm->auth_pae.authAuthEapLogoffWhileAuthenticated++; + } + + SM_ENTRY(AUTH_PAE, RESTART, auth_pae); + + sm->auth_pae.eapRestart = TRUE; + ieee802_1x_request_identity(sm->hapd, sm->sta); +} + + +SM_STATE(AUTH_PAE, CONNECTING) +{ + if (sm->auth_pae.state != AUTH_PAE_CONNECTING) + sm->auth_pae.authEntersConnecting++; + + SM_ENTRY(AUTH_PAE, CONNECTING, auth_pae); + + sm->reAuthenticate = FALSE; + sm->auth_pae.reAuthCount++; +} + + +SM_STATE(AUTH_PAE, HELD) +{ + if (sm->auth_pae.state == AUTH_PAE_AUTHENTICATING && sm->authFail) + sm->auth_pae.authAuthFailWhileAuthenticating++; + + SM_ENTRY(AUTH_PAE, HELD, auth_pae); + + sm->authPortStatus = Unauthorized; + setPortUnauthorized(); + sm->quietWhile = sm->auth_pae.quietPeriod; + sm->auth_pae.eapolLogoff = FALSE; + + hostapd_logger(sm->hapd, sm->addr, HOSTAPD_MODULE_IEEE8021X, + HOSTAPD_LEVEL_WARNING, "authentication failed"); + if (sm->flags & EAPOL_SM_PREAUTH) + rsn_preauth_finished(sm->hapd, sm->sta, 0); + else + ieee802_1x_finished(sm->hapd, sm->sta, 0); +} + + +SM_STATE(AUTH_PAE, AUTHENTICATED) +{ + if (sm->auth_pae.state == AUTH_PAE_AUTHENTICATING && sm->authSuccess) + sm->auth_pae.authAuthSuccessesWhileAuthenticating++; + + SM_ENTRY(AUTH_PAE, AUTHENTICATED, auth_pae); + + sm->authPortStatus = Authorized; + setPortAuthorized(); + sm->auth_pae.reAuthCount = 0; + hostapd_logger(sm->hapd, sm->addr, HOSTAPD_MODULE_IEEE8021X, + HOSTAPD_LEVEL_INFO, "authenticated"); + if (sm->flags & EAPOL_SM_PREAUTH) + rsn_preauth_finished(sm->hapd, sm->sta, 1); + else + ieee802_1x_finished(sm->hapd, sm->sta, 1); +} + + +SM_STATE(AUTH_PAE, AUTHENTICATING) +{ + if (sm->auth_pae.state == AUTH_PAE_CONNECTING && sm->rx_identity) { + sm->auth_pae.authEntersAuthenticating++; + sm->rx_identity = FALSE; + } + + SM_ENTRY(AUTH_PAE, AUTHENTICATING, auth_pae); + + sm->auth_pae.eapolStart = FALSE; + sm->authSuccess = FALSE; + sm->authFail = FALSE; + sm->authTimeout = FALSE; + sm->authStart = TRUE; + sm->keyRun = FALSE; + sm->keyDone = FALSE; +} + + +SM_STATE(AUTH_PAE, ABORTING) +{ + if (sm->auth_pae.state == AUTH_PAE_AUTHENTICATING) { + if (sm->authTimeout) + sm->auth_pae.authAuthTimeoutsWhileAuthenticating++; + if (sm->auth_pae.eapolStart) + sm->auth_pae.authAuthEapStartsWhileAuthenticating++; + if (sm->auth_pae.eapolLogoff) + sm->auth_pae.authAuthEapLogoffWhileAuthenticating++; + } + + SM_ENTRY(AUTH_PAE, ABORTING, auth_pae); + + sm->authAbort = TRUE; + sm->keyRun = FALSE; + sm->keyDone = FALSE; +} + + +SM_STATE(AUTH_PAE, FORCE_AUTH) +{ + SM_ENTRY(AUTH_PAE, FORCE_AUTH, auth_pae); + + sm->authPortStatus = Authorized; + setPortAuthorized(); + sm->auth_pae.portMode = ForceAuthorized; + sm->auth_pae.eapolStart = FALSE; + txCannedSuccess(); +} + + +SM_STATE(AUTH_PAE, FORCE_UNAUTH) +{ + SM_ENTRY(AUTH_PAE, FORCE_UNAUTH, auth_pae); + + sm->authPortStatus = Unauthorized; + setPortUnauthorized(); + sm->auth_pae.portMode = ForceUnauthorized; + sm->auth_pae.eapolStart = FALSE; + txCannedFail(); +} + + +SM_STEP(AUTH_PAE) +{ + if ((sm->portControl == Auto && + sm->auth_pae.portMode != sm->portControl) || + sm->initialize || !sm->portEnabled) + SM_ENTER(AUTH_PAE, INITIALIZE); + else if (sm->portControl == ForceAuthorized && + sm->auth_pae.portMode != sm->portControl && + !(sm->initialize || !sm->portEnabled)) + SM_ENTER(AUTH_PAE, FORCE_AUTH); + else if (sm->portControl == ForceUnauthorized && + sm->auth_pae.portMode != sm->portControl && + !(sm->initialize || !sm->portEnabled)) + SM_ENTER(AUTH_PAE, FORCE_UNAUTH); + else { + switch (sm->auth_pae.state) { + case AUTH_PAE_INITIALIZE: + SM_ENTER(AUTH_PAE, DISCONNECTED); + break; + case AUTH_PAE_DISCONNECTED: + SM_ENTER(AUTH_PAE, RESTART); + break; + case AUTH_PAE_RESTART: + if (!sm->auth_pae.eapRestart) + SM_ENTER(AUTH_PAE, CONNECTING); + break; + case AUTH_PAE_HELD: + if (sm->quietWhile == 0) + SM_ENTER(AUTH_PAE, RESTART); + break; + case AUTH_PAE_CONNECTING: + if (sm->auth_pae.eapolLogoff || + sm->auth_pae.reAuthCount > sm->auth_pae.reAuthMax) + SM_ENTER(AUTH_PAE, DISCONNECTED); + else if ((sm->be_auth.eapReq && + sm->auth_pae.reAuthCount <= + sm->auth_pae.reAuthMax) || + sm->eapSuccess || sm->eapFail) + SM_ENTER(AUTH_PAE, AUTHENTICATING); + break; + case AUTH_PAE_AUTHENTICATED: + if (sm->auth_pae.eapolStart || sm->reAuthenticate) + SM_ENTER(AUTH_PAE, RESTART); + else if (sm->auth_pae.eapolLogoff || !sm->portValid) + SM_ENTER(AUTH_PAE, DISCONNECTED); + break; + case AUTH_PAE_AUTHENTICATING: + if (sm->authSuccess && sm->portValid) + SM_ENTER(AUTH_PAE, AUTHENTICATED); + else if (sm->authFail || + (sm->keyDone && !sm->portValid)) + SM_ENTER(AUTH_PAE, HELD); + else if (sm->auth_pae.eapolStart || + sm->auth_pae.eapolLogoff || sm->authTimeout) + SM_ENTER(AUTH_PAE, ABORTING); + break; + case AUTH_PAE_ABORTING: + if (sm->auth_pae.eapolLogoff && !sm->authAbort) + SM_ENTER(AUTH_PAE, DISCONNECTED); + else if (!sm->auth_pae.eapolLogoff && !sm->authAbort) + SM_ENTER(AUTH_PAE, RESTART); + break; + case AUTH_PAE_FORCE_AUTH: + if (sm->auth_pae.eapolStart) + SM_ENTER(AUTH_PAE, FORCE_AUTH); + break; + case AUTH_PAE_FORCE_UNAUTH: + if (sm->auth_pae.eapolStart) + SM_ENTER(AUTH_PAE, FORCE_UNAUTH); + break; + } + } +} + + + +/* Backend Authentication state machine */ + +SM_STATE(BE_AUTH, INITIALIZE) +{ + SM_ENTRY(BE_AUTH, INITIALIZE, be_auth); + + abortAuth(); + sm->be_auth.eapNoReq = FALSE; + sm->authAbort = FALSE; +} + + +SM_STATE(BE_AUTH, REQUEST) +{ + SM_ENTRY(BE_AUTH, REQUEST, be_auth); + + txReq(); + sm->be_auth.eapReq = FALSE; + sm->be_auth.backendOtherRequestsToSupplicant++; + + /* + * Clearing eapolEap here is not specified in IEEE Std 802.1X-2004, but + * it looks like this would be logical thing to do there since the old + * EAP response would not be valid anymore after the new EAP request + * was sent out. + * + * A race condition has been reported, in which hostapd ended up + * sending out EAP-Response/Identity as a response to the first + * EAP-Request from the main EAP method. This can be avoided by + * clearing eapolEap here. + */ + sm->eapolEap = FALSE; +} + + +SM_STATE(BE_AUTH, RESPONSE) +{ + SM_ENTRY(BE_AUTH, RESPONSE, be_auth); + + sm->authTimeout = FALSE; + sm->eapolEap = FALSE; + sm->be_auth.eapNoReq = FALSE; + sm->aWhile = sm->be_auth.serverTimeout; + sm->be_auth.eapResp = TRUE; + sendRespToServer(); + sm->be_auth.backendResponses++; +} + + +SM_STATE(BE_AUTH, SUCCESS) +{ + SM_ENTRY(BE_AUTH, SUCCESS, be_auth); + + txReq(); + sm->authSuccess = TRUE; + sm->keyRun = TRUE; +} + + +SM_STATE(BE_AUTH, FAIL) +{ + SM_ENTRY(BE_AUTH, FAIL, be_auth); + + /* Note: IEEE 802.1X-REV-d11 has unconditional txReq() here. + * txCannelFail() is used as a workaround for the case where + * authentication server does not include EAP-Message with + * Access-Reject. */ + if (sm->last_eap_radius == NULL) + txCannedFail(); + else + txReq(); + sm->authFail = TRUE; +} + + +SM_STATE(BE_AUTH, TIMEOUT) +{ + SM_ENTRY(BE_AUTH, TIMEOUT, be_auth); + + sm->authTimeout = TRUE; +} + + +SM_STATE(BE_AUTH, IDLE) +{ + SM_ENTRY(BE_AUTH, IDLE, be_auth); + + sm->authStart = FALSE; +} + + +SM_STATE(BE_AUTH, IGNORE) +{ + SM_ENTRY(BE_AUTH, IGNORE, be_auth); + + sm->be_auth.eapNoReq = FALSE; +} + + +SM_STEP(BE_AUTH) +{ + if (sm->portControl != Auto || sm->initialize || sm->authAbort) { + SM_ENTER(BE_AUTH, INITIALIZE); + return; + } + + switch (sm->be_auth.state) { + case BE_AUTH_INITIALIZE: + SM_ENTER(BE_AUTH, IDLE); + break; + case BE_AUTH_REQUEST: + if (sm->eapolEap) + SM_ENTER(BE_AUTH, RESPONSE); + else if (sm->be_auth.eapReq) + SM_ENTER(BE_AUTH, REQUEST); + else if (sm->eapTimeout) + SM_ENTER(BE_AUTH, TIMEOUT); + break; + case BE_AUTH_RESPONSE: + if (sm->be_auth.eapNoReq) + SM_ENTER(BE_AUTH, IGNORE); + if (sm->be_auth.eapReq) { + sm->be_auth.backendAccessChallenges++; + SM_ENTER(BE_AUTH, REQUEST); + } else if (sm->aWhile == 0) + SM_ENTER(BE_AUTH, TIMEOUT); + else if (sm->eapFail) { + sm->be_auth.backendAuthFails++; + SM_ENTER(BE_AUTH, FAIL); + } else if (sm->eapSuccess) { + sm->be_auth.backendAuthSuccesses++; + SM_ENTER(BE_AUTH, SUCCESS); + } + break; + case BE_AUTH_SUCCESS: + SM_ENTER(BE_AUTH, IDLE); + break; + case BE_AUTH_FAIL: + SM_ENTER(BE_AUTH, IDLE); + break; + case BE_AUTH_TIMEOUT: + SM_ENTER(BE_AUTH, IDLE); + break; + case BE_AUTH_IDLE: + if (sm->eapFail && sm->authStart) + SM_ENTER(BE_AUTH, FAIL); + else if (sm->be_auth.eapReq && sm->authStart) + SM_ENTER(BE_AUTH, REQUEST); + else if (sm->eapSuccess && sm->authStart) + SM_ENTER(BE_AUTH, SUCCESS); + break; + case BE_AUTH_IGNORE: + if (sm->eapolEap) + SM_ENTER(BE_AUTH, RESPONSE); + else if (sm->be_auth.eapReq) + SM_ENTER(BE_AUTH, REQUEST); + else if (sm->eapTimeout) + SM_ENTER(BE_AUTH, TIMEOUT); + break; + } +} + + + +/* Reauthentication Timer state machine */ + +SM_STATE(REAUTH_TIMER, INITIALIZE) +{ + SM_ENTRY(REAUTH_TIMER, INITIALIZE, reauth_timer); + + sm->reAuthWhen = sm->reauth_timer.reAuthPeriod; +} + + +SM_STATE(REAUTH_TIMER, REAUTHENTICATE) +{ + SM_ENTRY(REAUTH_TIMER, REAUTHENTICATE, reauth_timer); + + sm->reAuthenticate = TRUE; + wpa_sm_event(sm->hapd, sm->sta, WPA_REAUTH_EAPOL); +} + + +SM_STEP(REAUTH_TIMER) +{ + if (sm->portControl != Auto || sm->initialize || + sm->authPortStatus == Unauthorized || + !sm->reauth_timer.reAuthEnabled) { + SM_ENTER(REAUTH_TIMER, INITIALIZE); + return; + } + + switch (sm->reauth_timer.state) { + case REAUTH_TIMER_INITIALIZE: + if (sm->reAuthWhen == 0) + SM_ENTER(REAUTH_TIMER, REAUTHENTICATE); + break; + case REAUTH_TIMER_REAUTHENTICATE: + SM_ENTER(REAUTH_TIMER, INITIALIZE); + break; + } +} + + + +/* Authenticator Key Transmit state machine */ + +SM_STATE(AUTH_KEY_TX, NO_KEY_TRANSMIT) +{ + SM_ENTRY(AUTH_KEY_TX, NO_KEY_TRANSMIT, auth_key_tx); +} + + +SM_STATE(AUTH_KEY_TX, KEY_TRANSMIT) +{ + SM_ENTRY(AUTH_KEY_TX, KEY_TRANSMIT, auth_key_tx); + + txKey(); + sm->keyAvailable = FALSE; + sm->keyDone = TRUE; +} + + +SM_STEP(AUTH_KEY_TX) +{ + if (sm->initialize || sm->portControl != Auto) { + SM_ENTER(AUTH_KEY_TX, NO_KEY_TRANSMIT); + return; + } + + switch (sm->auth_key_tx.state) { + case AUTH_KEY_TX_NO_KEY_TRANSMIT: + if (sm->keyTxEnabled && sm->keyAvailable && sm->keyRun && + !sm->sta->wpa) + SM_ENTER(AUTH_KEY_TX, KEY_TRANSMIT); + break; + case AUTH_KEY_TX_KEY_TRANSMIT: + if (!sm->keyTxEnabled || !sm->keyRun) + SM_ENTER(AUTH_KEY_TX, NO_KEY_TRANSMIT); + else if (sm->keyAvailable) + SM_ENTER(AUTH_KEY_TX, KEY_TRANSMIT); + break; + } +} + + + +/* Key Receive state machine */ + +SM_STATE(KEY_RX, NO_KEY_RECEIVE) +{ + SM_ENTRY(KEY_RX, NO_KEY_RECEIVE, key_rx); +} + + +SM_STATE(KEY_RX, KEY_RECEIVE) +{ + SM_ENTRY(KEY_RX, KEY_RECEIVE, key_rx); + + processKey(); + sm->key_rx.rxKey = FALSE; +} + + +SM_STEP(KEY_RX) +{ + if (sm->initialize || !sm->portEnabled) { + SM_ENTER(KEY_RX, NO_KEY_RECEIVE); + return; + } + + switch (sm->key_rx.state) { + case KEY_RX_NO_KEY_RECEIVE: + if (sm->key_rx.rxKey) + SM_ENTER(KEY_RX, KEY_RECEIVE); + break; + case KEY_RX_KEY_RECEIVE: + if (sm->key_rx.rxKey) + SM_ENTER(KEY_RX, KEY_RECEIVE); + break; + } +} + + + +/* Controlled Directions state machine */ + +SM_STATE(CTRL_DIR, FORCE_BOTH) +{ + SM_ENTRY(CTRL_DIR, FORCE_BOTH, ctrl_dir); + sm->ctrl_dir.operControlledDirections = Both; +} + + +SM_STATE(CTRL_DIR, IN_OR_BOTH) +{ + SM_ENTRY(CTRL_DIR, IN_OR_BOTH, ctrl_dir); + sm->ctrl_dir.operControlledDirections = + sm->ctrl_dir.adminControlledDirections; +} + + +SM_STEP(CTRL_DIR) +{ + if (sm->initialize) { + SM_ENTER(CTRL_DIR, IN_OR_BOTH); + return; + } + + switch (sm->ctrl_dir.state) { + case CTRL_DIR_FORCE_BOTH: + if (sm->portEnabled && sm->ctrl_dir.operEdge) + SM_ENTER(CTRL_DIR, IN_OR_BOTH); + break; + case CTRL_DIR_IN_OR_BOTH: + if (sm->ctrl_dir.operControlledDirections != + sm->ctrl_dir.adminControlledDirections) + SM_ENTER(CTRL_DIR, IN_OR_BOTH); + if (!sm->portEnabled || !sm->ctrl_dir.operEdge) + SM_ENTER(CTRL_DIR, FORCE_BOTH); + break; + } +} + + + +struct eapol_state_machine * +eapol_sm_alloc(hostapd *hapd, struct sta_info *sta) +{ + struct eapol_state_machine *sm; + + sm = (struct eapol_state_machine *) malloc(sizeof(*sm)); + if (sm == NULL) { + printf("IEEE 802.1X port state allocation failed\n"); + return NULL; + } + memset(sm, 0, sizeof(*sm)); + sm->radius_identifier = -1; + memcpy(sm->addr, sta->addr, ETH_ALEN); + if (sta->flags & WLAN_STA_PREAUTH) + sm->flags |= EAPOL_SM_PREAUTH; + + sm->hapd = hapd; + sm->sta = sta; + + /* Set default values for state machine constants */ + sm->auth_pae.state = AUTH_PAE_INITIALIZE; + sm->auth_pae.quietPeriod = AUTH_PAE_DEFAULT_quietPeriod; + sm->auth_pae.reAuthMax = AUTH_PAE_DEFAULT_reAuthMax; + + sm->be_auth.state = BE_AUTH_INITIALIZE; + sm->be_auth.serverTimeout = BE_AUTH_DEFAULT_serverTimeout; + + sm->reauth_timer.state = REAUTH_TIMER_INITIALIZE; + sm->reauth_timer.reAuthPeriod = hapd->conf->eap_reauth_period; + sm->reauth_timer.reAuthEnabled = hapd->conf->eap_reauth_period > 0 ? + TRUE : FALSE; + + sm->auth_key_tx.state = AUTH_KEY_TX_NO_KEY_TRANSMIT; + + sm->key_rx.state = KEY_RX_NO_KEY_RECEIVE; + + sm->ctrl_dir.state = CTRL_DIR_IN_OR_BOTH; + + sm->portEnabled = FALSE; + sm->portControl = Auto; + + sm->keyAvailable = FALSE; + if (!hapd->conf->wpa && + (hapd->default_wep_key || hapd->conf->individual_wep_key_len > 0)) + sm->keyTxEnabled = TRUE; + else + sm->keyTxEnabled = FALSE; + if (hapd->conf->wpa) + sm->portValid = FALSE; + else + sm->portValid = TRUE; + + if (hapd->conf->eap_server) { + struct eap_config eap_conf; + memset(&eap_conf, 0, sizeof(eap_conf)); + eap_conf.ssl_ctx = hapd->ssl_ctx; + eap_conf.eap_sim_db_priv = hapd->eap_sim_db_priv; + sm->eap = eap_sm_init(sm, &eapol_cb, &eap_conf); + if (sm->eap == NULL) { + eapol_sm_free(sm); + return NULL; + } + } + + eapol_sm_initialize(sm); + + return sm; +} + + +void eapol_sm_free(struct eapol_state_machine *sm) +{ + if (sm == NULL) + return; + + eloop_cancel_timeout(eapol_port_timers_tick, sm->hapd, sm); + eloop_cancel_timeout(eapol_sm_step_cb, sm, NULL); + if (sm->eap) + eap_sm_deinit(sm->eap); + free(sm); +} + + +static int eapol_sm_sta_entry_alive(struct hostapd_data *hapd, u8 *addr) +{ + struct sta_info *sta; + sta = ap_get_sta(hapd, addr); + if (sta == NULL || sta->eapol_sm == NULL) + return 0; + return 1; +} + + +static void eapol_sm_step_run(struct eapol_state_machine *sm) +{ + struct hostapd_data *hapd = sm->hapd; + u8 addr[ETH_ALEN]; + int prev_auth_pae, prev_be_auth, prev_reauth_timer, prev_auth_key_tx, + prev_key_rx, prev_ctrl_dir; + int max_steps = 100; + + memcpy(addr, sm->sta->addr, ETH_ALEN); + + /* + * Allow EAPOL state machines to run as long as there are state + * changes, but exit and return here through event loop if more than + * 100 steps is needed as a precaution against infinite loops inside + * eloop callback. + */ +restart: + prev_auth_pae = sm->auth_pae.state; + prev_be_auth = sm->be_auth.state; + prev_reauth_timer = sm->reauth_timer.state; + prev_auth_key_tx = sm->auth_key_tx.state; + prev_key_rx = sm->key_rx.state; + prev_ctrl_dir = sm->ctrl_dir.state; + + SM_STEP_RUN(AUTH_PAE); + if (sm->initializing || eapol_sm_sta_entry_alive(hapd, addr)) + SM_STEP_RUN(BE_AUTH); + if (sm->initializing || eapol_sm_sta_entry_alive(hapd, addr)) + SM_STEP_RUN(REAUTH_TIMER); + if (sm->initializing || eapol_sm_sta_entry_alive(hapd, addr)) + SM_STEP_RUN(AUTH_KEY_TX); + if (sm->initializing || eapol_sm_sta_entry_alive(hapd, addr)) + SM_STEP_RUN(KEY_RX); + if (sm->initializing || eapol_sm_sta_entry_alive(hapd, addr)) + SM_STEP_RUN(CTRL_DIR); + + if (prev_auth_pae != sm->auth_pae.state || + prev_be_auth != sm->be_auth.state || + prev_reauth_timer != sm->reauth_timer.state || + prev_auth_key_tx != sm->auth_key_tx.state || + prev_key_rx != sm->key_rx.state || + prev_ctrl_dir != sm->ctrl_dir.state) { + if (--max_steps > 0) + goto restart; + /* Re-run from eloop timeout */ + eapol_sm_step(sm); + return; + } + + if (eapol_sm_sta_entry_alive(hapd, addr) && sm->eap) { + if (eap_sm_step(sm->eap)) { + if (--max_steps > 0) + goto restart; + /* Re-run from eloop timeout */ + eapol_sm_step(sm); + return; + } + } + + if (eapol_sm_sta_entry_alive(hapd, addr)) + wpa_sm_notify(sm->hapd, sm->sta); +} + + +static void eapol_sm_step_cb(void *eloop_ctx, void *timeout_ctx) +{ + struct eapol_state_machine *sm = eloop_ctx; + eapol_sm_step_run(sm); +} + + +void eapol_sm_step(struct eapol_state_machine *sm) +{ + /* + * Run eapol_sm_step_run from a registered timeout to make sure that + * other possible timeouts/events are processed and to avoid long + * function call chains. + */ + + eloop_register_timeout(0, 0, eapol_sm_step_cb, sm, NULL); +} + + +void eapol_sm_initialize(struct eapol_state_machine *sm) +{ + sm->initializing = TRUE; + /* Initialize the state machines by asserting initialize and then + * deasserting it after one step */ + sm->initialize = TRUE; + eapol_sm_step_run(sm); + sm->initialize = FALSE; + eapol_sm_step_run(sm); + sm->initializing = FALSE; + + /* Start one second tick for port timers state machine */ + eloop_cancel_timeout(eapol_port_timers_tick, sm->hapd, sm); + eloop_register_timeout(1, 0, eapol_port_timers_tick, sm->hapd, sm); +} + + +#ifdef HOSTAPD_DUMP_STATE +static inline const char * port_type_txt(PortTypes pt) +{ + switch (pt) { + case ForceUnauthorized: return "ForceUnauthorized"; + case ForceAuthorized: return "ForceAuthorized"; + case Auto: return "Auto"; + default: return "Unknown"; + } +} + + +static inline const char * port_state_txt(PortState ps) +{ + switch (ps) { + case Unauthorized: return "Unauthorized"; + case Authorized: return "Authorized"; + default: return "Unknown"; + } +} + + +static inline const char * ctrl_dir_txt(ControlledDirection dir) +{ + switch (dir) { + case Both: return "Both"; + case In: return "In"; + default: return "Unknown"; + } +} + + +static inline const char * auth_pae_state_txt(int s) +{ + switch (s) { + case AUTH_PAE_INITIALIZE: return "INITIALIZE"; + case AUTH_PAE_DISCONNECTED: return "DISCONNECTED"; + case AUTH_PAE_CONNECTING: return "CONNECTING"; + case AUTH_PAE_AUTHENTICATING: return "AUTHENTICATING"; + case AUTH_PAE_AUTHENTICATED: return "AUTHENTICATED"; + case AUTH_PAE_ABORTING: return "ABORTING"; + case AUTH_PAE_HELD: return "HELD"; + case AUTH_PAE_FORCE_AUTH: return "FORCE_AUTH"; + case AUTH_PAE_FORCE_UNAUTH: return "FORCE_UNAUTH"; + case AUTH_PAE_RESTART: return "RESTART"; + default: return "Unknown"; + } +} + + +static inline const char * be_auth_state_txt(int s) +{ + switch (s) { + case BE_AUTH_REQUEST: return "REQUEST"; + case BE_AUTH_RESPONSE: return "RESPONSE"; + case BE_AUTH_SUCCESS: return "SUCCESS"; + case BE_AUTH_FAIL: return "FAIL"; + case BE_AUTH_TIMEOUT: return "TIMEOUT"; + case BE_AUTH_IDLE: return "IDLE"; + case BE_AUTH_INITIALIZE: return "INITIALIZE"; + case BE_AUTH_IGNORE: return "IGNORE"; + default: return "Unknown"; + } +} + + +static inline const char * reauth_timer_state_txt(int s) +{ + switch (s) { + case REAUTH_TIMER_INITIALIZE: return "INITIALIZE"; + case REAUTH_TIMER_REAUTHENTICATE: return "REAUTHENTICATE"; + default: return "Unknown"; + } +} + + +static inline const char * auth_key_tx_state_txt(int s) +{ + switch (s) { + case AUTH_KEY_TX_NO_KEY_TRANSMIT: return "NO_KEY_TRANSMIT"; + case AUTH_KEY_TX_KEY_TRANSMIT: return "KEY_TRANSMIT"; + default: return "Unknown"; + } +} + + +static inline const char * key_rx_state_txt(int s) +{ + switch (s) { + case KEY_RX_NO_KEY_RECEIVE: return "NO_KEY_RECEIVE"; + case KEY_RX_KEY_RECEIVE: return "KEY_RECEIVE"; + default: return "Unknown"; + } +} + + +static inline const char * ctrl_dir_state_txt(int s) +{ + switch (s) { + case CTRL_DIR_FORCE_BOTH: return "FORCE_BOTH"; + case CTRL_DIR_IN_OR_BOTH: return "IN_OR_BOTH"; + default: return "Unknown"; + } +} + + +void eapol_sm_dump_state(FILE *f, const char *prefix, + struct eapol_state_machine *sm) +{ + fprintf(f, "%sEAPOL state machine:\n", prefix); + fprintf(f, "%s aWhile=%d quietWhile=%d reAuthWhen=%d\n", prefix, + sm->aWhile, sm->quietWhile, sm->reAuthWhen); +#define _SB(b) ((b) ? "TRUE" : "FALSE") + fprintf(f, + "%s authAbort=%s authFail=%s authPortStatus=%s authStart=%s\n" + "%s authTimeout=%s authSuccess=%s eapFail=%s eapolEap=%s\n" + "%s eapSuccess=%s eapTimeout=%s initialize=%s " + "keyAvailable=%s\n" + "%s keyDone=%s keyRun=%s keyTxEnabled=%s portControl=%s\n" + "%s portEnabled=%s portValid=%s reAuthenticate=%s\n", + prefix, _SB(sm->authAbort), _SB(sm->authFail), + port_state_txt(sm->authPortStatus), _SB(sm->authStart), + prefix, _SB(sm->authTimeout), _SB(sm->authSuccess), + _SB(sm->eapFail), _SB(sm->eapolEap), + prefix, _SB(sm->eapSuccess), _SB(sm->eapTimeout), + _SB(sm->initialize), _SB(sm->keyAvailable), + prefix, _SB(sm->keyDone), _SB(sm->keyRun), + _SB(sm->keyTxEnabled), port_type_txt(sm->portControl), + prefix, _SB(sm->portEnabled), _SB(sm->portValid), + _SB(sm->reAuthenticate)); + + fprintf(f, "%s Authenticator PAE:\n" + "%s state=%s\n" + "%s eapolLogoff=%s eapolStart=%s eapRestart=%s\n" + "%s portMode=%s reAuthCount=%d\n" + "%s quietPeriod=%d reAuthMax=%d\n" + "%s authEntersConnecting=%d\n" + "%s authEapLogoffsWhileConnecting=%d\n" + "%s authEntersAuthenticating=%d\n" + "%s authAuthSuccessesWhileAuthenticating=%d\n" + "%s authAuthTimeoutsWhileAuthenticating=%d\n" + "%s authAuthFailWhileAuthenticating=%d\n" + "%s authAuthEapStartsWhileAuthenticating=%d\n" + "%s authAuthEapLogoffWhileAuthenticating=%d\n" + "%s authAuthReauthsWhileAuthenticated=%d\n" + "%s authAuthEapStartsWhileAuthenticated=%d\n" + "%s authAuthEapLogoffWhileAuthenticated=%d\n", + prefix, prefix, auth_pae_state_txt(sm->auth_pae.state), prefix, + _SB(sm->auth_pae.eapolLogoff), _SB(sm->auth_pae.eapolStart), + _SB(sm->auth_pae.eapRestart), prefix, + port_type_txt(sm->auth_pae.portMode), sm->auth_pae.reAuthCount, + prefix, sm->auth_pae.quietPeriod, sm->auth_pae.reAuthMax, + prefix, sm->auth_pae.authEntersConnecting, + prefix, sm->auth_pae.authEapLogoffsWhileConnecting, + prefix, sm->auth_pae.authEntersAuthenticating, + prefix, sm->auth_pae.authAuthSuccessesWhileAuthenticating, + prefix, sm->auth_pae.authAuthTimeoutsWhileAuthenticating, + prefix, sm->auth_pae.authAuthFailWhileAuthenticating, + prefix, sm->auth_pae.authAuthEapStartsWhileAuthenticating, + prefix, sm->auth_pae.authAuthEapLogoffWhileAuthenticating, + prefix, sm->auth_pae.authAuthReauthsWhileAuthenticated, + prefix, sm->auth_pae.authAuthEapStartsWhileAuthenticated, + prefix, sm->auth_pae.authAuthEapLogoffWhileAuthenticated); + + fprintf(f, "%s Backend Authentication:\n" + "%s state=%s\n" + "%s eapNoReq=%s eapReq=%s eapResp=%s\n" + "%s serverTimeout=%d\n" + "%s backendResponses=%d\n" + "%s backendAccessChallenges=%d\n" + "%s backendOtherRequestsToSupplicant=%d\n" + "%s backendAuthSuccesses=%d\n" + "%s backendAuthFails=%d\n", + prefix, prefix, + be_auth_state_txt(sm->be_auth.state), + prefix, _SB(sm->be_auth.eapNoReq), _SB(sm->be_auth.eapReq), + _SB(sm->be_auth.eapResp), + prefix, sm->be_auth.serverTimeout, + prefix, sm->be_auth.backendResponses, + prefix, sm->be_auth.backendAccessChallenges, + prefix, sm->be_auth.backendOtherRequestsToSupplicant, + prefix, sm->be_auth.backendAuthSuccesses, + prefix, sm->be_auth.backendAuthFails); + + fprintf(f, "%s Reauthentication Timer:\n" + "%s state=%s\n" + "%s reAuthPeriod=%d reAuthEnabled=%s\n", prefix, prefix, + reauth_timer_state_txt(sm->reauth_timer.state), prefix, + sm->reauth_timer.reAuthPeriod, + _SB(sm->reauth_timer.reAuthEnabled)); + + fprintf(f, "%s Authenticator Key Transmit:\n" + "%s state=%s\n", prefix, prefix, + auth_key_tx_state_txt(sm->auth_key_tx.state)); + + fprintf(f, "%s Key Receive:\n" + "%s state=%s\n" + "%s rxKey=%s\n", prefix, prefix, + key_rx_state_txt(sm->key_rx.state), + prefix, _SB(sm->key_rx.rxKey)); + + fprintf(f, "%s Controlled Directions:\n" + "%s state=%s\n" + "%s adminControlledDirections=%s " + "operControlledDirections=%s\n" + "%s operEdge=%s\n", prefix, prefix, + ctrl_dir_state_txt(sm->ctrl_dir.state), + prefix, ctrl_dir_txt(sm->ctrl_dir.adminControlledDirections), + ctrl_dir_txt(sm->ctrl_dir.operControlledDirections), + prefix, _SB(sm->ctrl_dir.operEdge)); +#undef _SB +} +#endif /* HOSTAPD_DUMP_STATE */ + + +static Boolean eapol_sm_get_bool(void *ctx, enum eapol_bool_var variable) +{ + struct eapol_state_machine *sm = ctx; + if (sm == NULL) + return FALSE; + switch (variable) { + case EAPOL_eapSuccess: + return sm->eapSuccess; + case EAPOL_eapRestart: + return sm->auth_pae.eapRestart; + case EAPOL_eapFail: + return sm->eapFail; + case EAPOL_eapResp: + return sm->be_auth.eapResp; + case EAPOL_eapReq: + return sm->be_auth.eapReq; + case EAPOL_eapNoReq: + return sm->be_auth.eapNoReq; + case EAPOL_portEnabled: + return sm->portEnabled; + case EAPOL_eapTimeout: + return sm->eapTimeout; + } + return FALSE; +} + + +static void eapol_sm_set_bool(void *ctx, enum eapol_bool_var variable, + Boolean value) +{ + struct eapol_state_machine *sm = ctx; + if (sm == NULL) + return; + switch (variable) { + case EAPOL_eapSuccess: + sm->eapSuccess = value; + break; + case EAPOL_eapRestart: + sm->auth_pae.eapRestart = value; + break; + case EAPOL_eapFail: + sm->eapFail = value; + break; + case EAPOL_eapResp: + sm->be_auth.eapResp = value; + break; + case EAPOL_eapReq: + sm->be_auth.eapReq = value; + break; + case EAPOL_eapNoReq: + sm->be_auth.eapNoReq = value; + break; + case EAPOL_portEnabled: + sm->portEnabled = value; + break; + case EAPOL_eapTimeout: + sm->eapTimeout = value; + break; + } +} + + +static void eapol_sm_set_eapReqData(void *ctx, const u8 *eapReqData, + size_t eapReqDataLen) +{ + struct eapol_state_machine *sm = ctx; + if (sm == NULL) + return; + + free(sm->last_eap_radius); + sm->last_eap_radius = malloc(eapReqDataLen); + if (sm->last_eap_radius == NULL) + return; + memcpy(sm->last_eap_radius, eapReqData, eapReqDataLen); + sm->last_eap_radius_len = eapReqDataLen; +} + + +static void eapol_sm_set_eapKeyData(void *ctx, const u8 *eapKeyData, + size_t eapKeyDataLen) +{ + struct eapol_state_machine *sm = ctx; + struct hostapd_data *hapd; + + if (sm == NULL) + return; + + hapd = sm->hapd; + + if (eapKeyData && eapKeyDataLen >= 64) { + free(sm->eapol_key_sign); + free(sm->eapol_key_crypt); + sm->eapol_key_crypt = malloc(32); + if (sm->eapol_key_crypt) { + memcpy(sm->eapol_key_crypt, eapKeyData, 32); + sm->eapol_key_crypt_len = 32; + } + sm->eapol_key_sign = malloc(32); + if (sm->eapol_key_sign) { + memcpy(sm->eapol_key_sign, eapKeyData + 32, 32); + sm->eapol_key_sign_len = 32; + } + if (hapd->default_wep_key || + hapd->conf->individual_wep_key_len > 0 || + hapd->conf->wpa) + sm->keyAvailable = TRUE; + } else { + free(sm->eapol_key_sign); + free(sm->eapol_key_crypt); + sm->eapol_key_sign = NULL; + sm->eapol_key_crypt = NULL; + sm->eapol_key_sign_len = 0; + sm->eapol_key_crypt_len = 0; + sm->keyAvailable = FALSE; + } +} + + +static int eapol_sm_get_eap_user(void *ctx, const u8 *identity, + size_t identity_len, int phase2, + struct eap_user *user) +{ + struct eapol_state_machine *sm = ctx; + const struct hostapd_eap_user *eap_user; + + eap_user = hostapd_get_eap_user(sm->hapd->conf, identity, + identity_len, phase2); + if (eap_user == NULL) + return -1; + + memset(user, 0, sizeof(*user)); + user->phase2 = phase2; + memcpy(user->methods, eap_user->methods, + EAP_USER_MAX_METHODS > EAP_MAX_METHODS ? + EAP_USER_MAX_METHODS : EAP_MAX_METHODS); + + if (eap_user->password) { + user->password = malloc(eap_user->password_len); + if (user->password == NULL) + return -1; + memcpy(user->password, eap_user->password, + eap_user->password_len); + user->password_len = eap_user->password_len; + } + user->force_version = eap_user->force_version; + + return 0; +} + + +static const char * eapol_sm_get_eap_req_id_text(void *ctx, size_t *len) +{ + struct eapol_state_machine *sm = ctx; + *len = sm->hapd->conf->eap_req_id_text_len; + return sm->hapd->conf->eap_req_id_text; +} + + +static struct eapol_callbacks eapol_cb = +{ + .get_bool = eapol_sm_get_bool, + .set_bool = eapol_sm_set_bool, + .set_eapReqData = eapol_sm_set_eapReqData, + .set_eapKeyData = eapol_sm_set_eapKeyData, + .get_eap_user = eapol_sm_get_eap_user, + .get_eap_req_id_text = eapol_sm_get_eap_req_id_text, +}; diff --git a/contrib/hostapd-0.4.9/eapol_sm.h b/contrib/hostapd-0.4.9/eapol_sm.h new file mode 100644 index 0000000000..0c34b4fcc2 --- /dev/null +++ b/contrib/hostapd-0.4.9/eapol_sm.h @@ -0,0 +1,224 @@ +#ifndef EAPOL_SM_H +#define EAPOL_SM_H + +#include "defs.h" + +/* IEEE Std 802.1X-REV-d11, Ch. 8.2 */ + +typedef enum { ForceUnauthorized = 1, ForceAuthorized = 3, Auto = 2 } + PortTypes; +typedef enum { Unauthorized = 2, Authorized = 1 } PortState; +typedef enum { Both = 0, In = 1 } ControlledDirection; +typedef unsigned int Counter; + + +/* Authenticator PAE state machine */ +struct eapol_auth_pae_sm { + /* variables */ + Boolean eapolLogoff; + Boolean eapolStart; + Boolean eapRestart; + PortTypes portMode; + unsigned int reAuthCount; + + /* constants */ + unsigned int quietPeriod; /* default 60; 0..65535 */ +#define AUTH_PAE_DEFAULT_quietPeriod 60 + unsigned int reAuthMax; /* default 2 */ +#define AUTH_PAE_DEFAULT_reAuthMax 2 + + /* counters */ + Counter authEntersConnecting; + Counter authEapLogoffsWhileConnecting; + Counter authEntersAuthenticating; + Counter authAuthSuccessesWhileAuthenticating; + Counter authAuthTimeoutsWhileAuthenticating; + Counter authAuthFailWhileAuthenticating; + Counter authAuthEapStartsWhileAuthenticating; + Counter authAuthEapLogoffWhileAuthenticating; + Counter authAuthReauthsWhileAuthenticated; + Counter authAuthEapStartsWhileAuthenticated; + Counter authAuthEapLogoffWhileAuthenticated; + + enum { AUTH_PAE_INITIALIZE, AUTH_PAE_DISCONNECTED, AUTH_PAE_CONNECTING, + AUTH_PAE_AUTHENTICATING, AUTH_PAE_AUTHENTICATED, + AUTH_PAE_ABORTING, AUTH_PAE_HELD, AUTH_PAE_FORCE_AUTH, + AUTH_PAE_FORCE_UNAUTH, AUTH_PAE_RESTART } state; +}; + + +/* Backend Authentication state machine */ +struct eapol_backend_auth_sm { + /* variables */ + Boolean eapNoReq; + Boolean eapReq; + Boolean eapResp; + + /* constants */ + unsigned int serverTimeout; /* default 30; 1..X */ +#define BE_AUTH_DEFAULT_serverTimeout 30 + + /* counters */ + Counter backendResponses; + Counter backendAccessChallenges; + Counter backendOtherRequestsToSupplicant; + Counter backendAuthSuccesses; + Counter backendAuthFails; + + enum { BE_AUTH_REQUEST, BE_AUTH_RESPONSE, BE_AUTH_SUCCESS, + BE_AUTH_FAIL, BE_AUTH_TIMEOUT, BE_AUTH_IDLE, BE_AUTH_INITIALIZE, + BE_AUTH_IGNORE + } state; +}; + + +/* Reauthentication Timer state machine */ +struct eapol_reauth_timer_sm { + /* constants */ + unsigned int reAuthPeriod; /* default 3600 s */ + Boolean reAuthEnabled; + + enum { REAUTH_TIMER_INITIALIZE, REAUTH_TIMER_REAUTHENTICATE } state; +}; + + +/* Authenticator Key Transmit state machine */ +struct eapol_auth_key_tx { + enum { AUTH_KEY_TX_NO_KEY_TRANSMIT, AUTH_KEY_TX_KEY_TRANSMIT } state; +}; + + +/* Key Receive state machine */ +struct eapol_key_rx { + /* variables */ + Boolean rxKey; + + enum { KEY_RX_NO_KEY_RECEIVE, KEY_RX_KEY_RECEIVE } state; +}; + + +/* Controlled Directions state machine */ +struct eapol_ctrl_dir { + /* variables */ + ControlledDirection adminControlledDirections; + ControlledDirection operControlledDirections; + Boolean operEdge; + + enum { CTRL_DIR_FORCE_BOTH, CTRL_DIR_IN_OR_BOTH } state; +}; + + +struct eap_sm; + +struct radius_attr_data { + u8 *data; + size_t len; +}; + +struct radius_class_data { + struct radius_attr_data *attr; + size_t count; +}; + +struct eapol_state_machine { + /* timers */ + int aWhile; + int quietWhile; + int reAuthWhen; + + /* global variables */ + Boolean authAbort; + Boolean authFail; + PortState authPortStatus; + Boolean authStart; + Boolean authTimeout; + Boolean authSuccess; + Boolean eapFail; + Boolean eapolEap; + Boolean eapSuccess; + Boolean eapTimeout; + Boolean initialize; + Boolean keyAvailable; + Boolean keyDone; + Boolean keyRun; + Boolean keyTxEnabled; + PortTypes portControl; + Boolean portEnabled; + Boolean portValid; + Boolean reAuthenticate; + + /* Port Timers state machine */ + /* 'Boolean tick' implicitly handled as registered timeout */ + + struct eapol_auth_pae_sm auth_pae; + struct eapol_backend_auth_sm be_auth; + struct eapol_reauth_timer_sm reauth_timer; + struct eapol_auth_key_tx auth_key_tx; + struct eapol_key_rx key_rx; + struct eapol_ctrl_dir ctrl_dir; + + /* Authenticator Statistics Table */ + Counter dot1xAuthEapolFramesRx; + Counter dot1xAuthEapolFramesTx; + Counter dot1xAuthEapolStartFramesRx; + Counter dot1xAuthEapolLogoffFramesRx; + Counter dot1xAuthEapolRespIdFramesRx; + Counter dot1xAuthEapolRespFramesRx; + Counter dot1xAuthEapolReqIdFramesTx; + Counter dot1xAuthEapolReqFramesTx; + Counter dot1xAuthInvalidEapolFramesRx; + Counter dot1xAuthEapLengthErrorFramesRx; + Counter dot1xAuthLastEapolFrameVersion; + + /* Other variables - not defined in IEEE 802.1X */ + u8 addr[ETH_ALEN]; /* Supplicant address */ +#define EAPOL_SM_PREAUTH BIT(0) + int flags; /* EAPOL_SM_* */ + + int radius_identifier; + /* TODO: check when the last messages can be released */ + struct radius_msg *last_recv_radius; + u8 *last_eap_supp; /* last received EAP Response from Supplicant */ + size_t last_eap_supp_len; + u8 *last_eap_radius; /* last received EAP Response from Authentication + * Server */ + size_t last_eap_radius_len; + u8 *identity; + size_t identity_len; + struct radius_class_data radius_class; + + /* Keys for encrypting and signing EAPOL-Key frames */ + u8 *eapol_key_sign; + size_t eapol_key_sign_len; + u8 *eapol_key_crypt; + size_t eapol_key_crypt_len; + + Boolean rx_identity; /* set to TRUE on reception of + * EAP-Response/Identity */ + + struct eap_sm *eap; + + /* currentId was removed in IEEE 802.1X-REV, but it is needed to filter + * out EAP-Responses to old packets (e.g., to two EAP-Request/Identity + * packets that are often sent in the beginning of the authentication). + */ + u8 currentId; + + Boolean initializing; /* in process of initializing state machines */ + + /* Somewhat nasty pointers to global hostapd and STA data to avoid + * passing these to every function */ + struct hostapd_data *hapd; + struct sta_info *sta; +}; + + +struct eapol_state_machine *eapol_sm_alloc(hostapd *hapd, + struct sta_info *sta); +void eapol_sm_free(struct eapol_state_machine *sm); +void eapol_sm_step(struct eapol_state_machine *sm); +void eapol_sm_initialize(struct eapol_state_machine *sm); +void eapol_sm_dump_state(FILE *f, const char *prefix, + struct eapol_state_machine *sm); + +#endif /* EAPOL_SM_H */ diff --git a/contrib/hostapd-0.4.9/eloop.c b/contrib/hostapd-0.4.9/eloop.c new file mode 100644 index 0000000000..b482f81cce --- /dev/null +++ b/contrib/hostapd-0.4.9/eloop.c @@ -0,0 +1,395 @@ +/* + * Event loop based on select() loop + * Copyright (c) 2002-2005, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_NATIVE_WINDOWS +#include "common.h" +#endif /* CONFIG_NATIVE_WINDOWS */ + +#include "eloop.h" + + +struct eloop_sock { + int sock; + void *eloop_data; + void *user_data; + void (*handler)(int sock, void *eloop_ctx, void *sock_ctx); +}; + +struct eloop_timeout { + struct timeval time; + void *eloop_data; + void *user_data; + void (*handler)(void *eloop_ctx, void *sock_ctx); + struct eloop_timeout *next; +}; + +struct eloop_signal { + int sig; + void *user_data; + void (*handler)(int sig, void *eloop_ctx, void *signal_ctx); + int signaled; +}; + +struct eloop_data { + void *user_data; + + int max_sock, reader_count; + struct eloop_sock *readers; + + struct eloop_timeout *timeout; + + int signal_count; + struct eloop_signal *signals; + int signaled; + int pending_terminate; + + int terminate; + int reader_table_changed; +}; + +static struct eloop_data eloop; + + +void eloop_init(void *user_data) +{ + memset(&eloop, 0, sizeof(eloop)); + eloop.user_data = user_data; +} + + +int eloop_register_read_sock(int sock, + void (*handler)(int sock, void *eloop_ctx, + void *sock_ctx), + void *eloop_data, void *user_data) +{ + struct eloop_sock *tmp; + + tmp = (struct eloop_sock *) + realloc(eloop.readers, + (eloop.reader_count + 1) * sizeof(struct eloop_sock)); + if (tmp == NULL) + return -1; + + tmp[eloop.reader_count].sock = sock; + tmp[eloop.reader_count].eloop_data = eloop_data; + tmp[eloop.reader_count].user_data = user_data; + tmp[eloop.reader_count].handler = handler; + eloop.reader_count++; + eloop.readers = tmp; + if (sock > eloop.max_sock) + eloop.max_sock = sock; + eloop.reader_table_changed = 1; + + return 0; +} + + +void eloop_unregister_read_sock(int sock) +{ + int i; + + if (eloop.readers == NULL || eloop.reader_count == 0) + return; + + for (i = 0; i < eloop.reader_count; i++) { + if (eloop.readers[i].sock == sock) + break; + } + if (i == eloop.reader_count) + return; + if (i != eloop.reader_count - 1) { + memmove(&eloop.readers[i], &eloop.readers[i + 1], + (eloop.reader_count - i - 1) * + sizeof(struct eloop_sock)); + } + eloop.reader_count--; + eloop.reader_table_changed = 1; +} + + +int eloop_register_timeout(unsigned int secs, unsigned int usecs, + void (*handler)(void *eloop_ctx, void *timeout_ctx), + void *eloop_data, void *user_data) +{ + struct eloop_timeout *timeout, *tmp, *prev; + + timeout = (struct eloop_timeout *) malloc(sizeof(*timeout)); + if (timeout == NULL) + return -1; + gettimeofday(&timeout->time, NULL); + timeout->time.tv_sec += secs; + timeout->time.tv_usec += usecs; + while (timeout->time.tv_usec >= 1000000) { + timeout->time.tv_sec++; + timeout->time.tv_usec -= 1000000; + } + timeout->eloop_data = eloop_data; + timeout->user_data = user_data; + timeout->handler = handler; + timeout->next = NULL; + + if (eloop.timeout == NULL) { + eloop.timeout = timeout; + return 0; + } + + prev = NULL; + tmp = eloop.timeout; + while (tmp != NULL) { + if (timercmp(&timeout->time, &tmp->time, <)) + break; + prev = tmp; + tmp = tmp->next; + } + + if (prev == NULL) { + timeout->next = eloop.timeout; + eloop.timeout = timeout; + } else { + timeout->next = prev->next; + prev->next = timeout; + } + + return 0; +} + + +int eloop_cancel_timeout(void (*handler)(void *eloop_ctx, void *sock_ctx), + void *eloop_data, void *user_data) +{ + struct eloop_timeout *timeout, *prev, *next; + int removed = 0; + + prev = NULL; + timeout = eloop.timeout; + while (timeout != NULL) { + next = timeout->next; + + if (timeout->handler == handler && + (timeout->eloop_data == eloop_data || + eloop_data == ELOOP_ALL_CTX) && + (timeout->user_data == user_data || + user_data == ELOOP_ALL_CTX)) { + if (prev == NULL) + eloop.timeout = next; + else + prev->next = next; + free(timeout); + removed++; + } else + prev = timeout; + + timeout = next; + } + + return removed; +} + + +#ifndef CONFIG_NATIVE_WINDOWS +static void eloop_handle_alarm(int sig) +{ + fprintf(stderr, "eloop: could not process SIGINT or SIGTERM in two " + "seconds. Looks like there\n" + "is a bug that ends up in a busy loop that " + "prevents clean shutdown.\n" + "Killing program forcefully.\n"); + exit(1); +} +#endif /* CONFIG_NATIVE_WINDOWS */ + + +static void eloop_handle_signal(int sig) +{ + int i; + +#ifndef CONFIG_NATIVE_WINDOWS + if ((sig == SIGINT || sig == SIGTERM) && !eloop.pending_terminate) { + /* Use SIGALRM to break out from potential busy loops that + * would not allow the program to be killed. */ + eloop.pending_terminate = 1; + signal(SIGALRM, eloop_handle_alarm); + alarm(2); + } +#endif /* CONFIG_NATIVE_WINDOWS */ + + eloop.signaled++; + for (i = 0; i < eloop.signal_count; i++) { + if (eloop.signals[i].sig == sig) { + eloop.signals[i].signaled++; + break; + } + } +} + + +static void eloop_process_pending_signals(void) +{ + int i; + + if (eloop.signaled == 0) + return; + eloop.signaled = 0; + + if (eloop.pending_terminate) { +#ifndef CONFIG_NATIVE_WINDOWS + alarm(0); +#endif /* CONFIG_NATIVE_WINDOWS */ + eloop.pending_terminate = 0; + } + + for (i = 0; i < eloop.signal_count; i++) { + if (eloop.signals[i].signaled) { + eloop.signals[i].signaled = 0; + eloop.signals[i].handler(eloop.signals[i].sig, + eloop.user_data, + eloop.signals[i].user_data); + } + } +} + + +int eloop_register_signal(int sig, + void (*handler)(int sig, void *eloop_ctx, + void *signal_ctx), + void *user_data) +{ + struct eloop_signal *tmp; + + tmp = (struct eloop_signal *) + realloc(eloop.signals, + (eloop.signal_count + 1) * + sizeof(struct eloop_signal)); + if (tmp == NULL) + return -1; + + tmp[eloop.signal_count].sig = sig; + tmp[eloop.signal_count].user_data = user_data; + tmp[eloop.signal_count].handler = handler; + tmp[eloop.signal_count].signaled = 0; + eloop.signal_count++; + eloop.signals = tmp; + signal(sig, eloop_handle_signal); + + return 0; +} + + +void eloop_run(void) +{ + fd_set *rfds; + int i, res; + struct timeval tv, now; + + rfds = malloc(sizeof(*rfds)); + if (rfds == NULL) { + printf("eloop_run - malloc failed\n"); + return; + } + + while (!eloop.terminate && + (eloop.timeout || eloop.reader_count > 0)) { + if (eloop.timeout) { + gettimeofday(&now, NULL); + if (timercmp(&now, &eloop.timeout->time, <)) + timersub(&eloop.timeout->time, &now, &tv); + else + tv.tv_sec = tv.tv_usec = 0; +#if 0 + printf("next timeout in %lu.%06lu sec\n", + tv.tv_sec, tv.tv_usec); +#endif + } + + FD_ZERO(rfds); + for (i = 0; i < eloop.reader_count; i++) + FD_SET(eloop.readers[i].sock, rfds); + res = select(eloop.max_sock + 1, rfds, NULL, NULL, + eloop.timeout ? &tv : NULL); + if (res < 0 && errno != EINTR) { + perror("select"); + free(rfds); + return; + } + eloop_process_pending_signals(); + + /* check if some registered timeouts have occurred */ + if (eloop.timeout) { + struct eloop_timeout *tmp; + + gettimeofday(&now, NULL); + if (!timercmp(&now, &eloop.timeout->time, <)) { + tmp = eloop.timeout; + eloop.timeout = eloop.timeout->next; + tmp->handler(tmp->eloop_data, + tmp->user_data); + free(tmp); + } + + } + + if (res <= 0) + continue; + + eloop.reader_table_changed = 0; + for (i = 0; i < eloop.reader_count; i++) { + if (FD_ISSET(eloop.readers[i].sock, rfds)) { + eloop.readers[i].handler( + eloop.readers[i].sock, + eloop.readers[i].eloop_data, + eloop.readers[i].user_data); + if (eloop.reader_table_changed) + break; + } + } + } + + free(rfds); +} + + +void eloop_terminate(void) +{ + eloop.terminate = 1; +} + + +void eloop_destroy(void) +{ + struct eloop_timeout *timeout, *prev; + + timeout = eloop.timeout; + while (timeout != NULL) { + prev = timeout; + timeout = timeout->next; + free(prev); + } + free(eloop.readers); + free(eloop.signals); +} + + +int eloop_terminated(void) +{ + return eloop.terminate; +} diff --git a/contrib/hostapd-0.4.9/eloop.h b/contrib/hostapd-0.4.9/eloop.h new file mode 100644 index 0000000000..884d4e1b2c --- /dev/null +++ b/contrib/hostapd-0.4.9/eloop.h @@ -0,0 +1,154 @@ +/* + * Event loop + * Copyright (c) 2002-2005, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + * + * This file defines an event loop interface that supports processing events + * from registered timeouts (i.e., do something after N seconds), sockets + * (e.g., a new packet available for reading), and signals. eloop.c is an + * implementation of this interface using select() and sockets. This is + * suitable for most UNIX/POSIX systems. When porting to other operating + * systems, it may be necessary to replace that implementation with OS specific + * mechanisms. + */ + +#ifndef ELOOP_H +#define ELOOP_H + +/** + * ELOOP_ALL_CTX - eloop_cancel_timeout() magic number to match all timeouts + */ +#define ELOOP_ALL_CTX (void *) -1 + +/** + * eloop_init() - Initialize global event loop data + * @user_data: Pointer to global data passed as eloop_ctx to signal handlers + * + * This function must be called before any other eloop_* function. user_data + * can be used to configure a global (to the process) pointer that will be + * passed as eloop_ctx parameter to signal handlers. + */ +void eloop_init(void *user_data); + +/** + * eloop_register_read_sock - Register handler for read events + * @sock: File descriptor number for the socket + * @handler: Callback function to be called when data is available for reading + * @eloop_data: Callback context data (eloop_ctx) + * @user_data: Callback context data (sock_ctx) + * Returns: 0 on success, -1 on failure + * + * Register a read socket notifier for the given file descriptor. The handler + * function will be called whenever data is available for reading from the + * socket. + */ +int eloop_register_read_sock(int sock, + void (*handler)(int sock, void *eloop_ctx, + void *sock_ctx), + void *eloop_data, void *user_data); + +/** + * eloop_unregister_read_sock - Unregister handler for read events + * @sock: File descriptor number for the socket + * + * Unregister a read socket notifier that was previously registered with + * eloop_register_read_sock(). + */ +void eloop_unregister_read_sock(int sock); + +/** + * eloop_register_timeout - Register timeout + * @secs: Number of seconds to the timeout + * @usecs: Number of microseconds to the timeout + * @handler: Callback function to be called when timeout occurs + * @eloop_data: Callback context data (eloop_ctx) + * @user_data: Callback context data (sock_ctx) + * Returns: 0 on success, -1 on failure + * + * Register a timeout that will cause the handler function to be called after + * given time. + */ +int eloop_register_timeout(unsigned int secs, unsigned int usecs, + void (*handler)(void *eloop_ctx, void *timeout_ctx), + void *eloop_data, void *user_data); + +/** + * eloop_cancel_timeout - Cancel timeouts + * @handler: Matching callback function + * @eloop_data: Matching eloop_data or %ELOOP_ALL_CTX to match all + * @user_data: Matching user_data or %ELOOP_ALL_CTX to match all + * Returns: Number of cancelled timeouts + * + * Cancel matching timeouts registered with + * eloop_register_timeout(). ELOOP_ALL_CTX can be used as a wildcard for + * cancelling all timeouts regardless of eloop_data/user_data. + */ +int eloop_cancel_timeout(void (*handler)(void *eloop_ctx, void *sock_ctx), + void *eloop_data, void *user_data); + +/** + * eloop_register_signal - Register handler for signals + * @sig: Signal number (e.g., SIGHUP) + * @handler: Callback function to be called when the signal is received + * @user_data: Callback context data (signal_ctx) + * Returns: 0 on success, -1 on failure + * + * Register a callback function that will be called when a signal is received. + * The calback function is actually called only after the system signal handler + * has returned. This means that the normal limits for sighandlers (i.e., only + * "safe functions" allowed) do not apply for the registered callback. + * + * Signals are 'global' events and there is no local eloop_data pointer like + * with other handlers. The global user_data pointer registered with + * eloop_init() will be used as eloop_ctx for signal handlers. + */ +int eloop_register_signal(int sig, + void (*handler)(int sig, void *eloop_ctx, + void *signal_ctx), + void *user_data); + +/** + * eloop_run - Start the event loop + * + * Start the event loop and continue running as long as there are any + * registered event handlers. This function is run after event loop has been + * initialized with event_init() and one or more events have been registered. + */ +void eloop_run(void); + +/** + * eloop_terminate - Terminate event loop + * + * Terminate event loop even if there are registered events. This can be used + * to request the program to be terminated cleanly. + */ +void eloop_terminate(void); + +/** + * eloop_destroy - Free any resources allocated for the event loop + * + * After calling eloop_destroy(), other eloop_* functions must not be called + * before re-running eloop_init(). + */ +void eloop_destroy(void); + +/** + * eloop_terminated - Check whether event loop has been terminated + * Returns: 1 = event loop terminate, 0 = event loop still running + * + * This function can be used to check whether eloop_terminate() has been called + * to request termination of the event loop. This is normally used to abort + * operations that may still be queued to be run when eloop_terminate() was + * called. + */ +int eloop_terminated(void); + +#endif /* ELOOP_H */ diff --git a/contrib/hostapd-0.4.9/hostap_common.h b/contrib/hostapd-0.4.9/hostap_common.h new file mode 100644 index 0000000000..78af287c3c --- /dev/null +++ b/contrib/hostapd-0.4.9/hostap_common.h @@ -0,0 +1,558 @@ +#ifndef HOSTAP_COMMON_H +#define HOSTAP_COMMON_H + +#define BIT(x) (1 << (x)) + +#define MAC2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5] +#define MACSTR "%02x:%02x:%02x:%02x:%02x:%02x" + + +#ifndef ETH_P_PAE +#define ETH_P_PAE 0x888E /* Port Access Entity (IEEE 802.1X) */ +#endif /* ETH_P_PAE */ + +#define ETH_P_PREAUTH 0x88C7 /* IEEE 802.11i pre-authentication */ + + + +/* IEEE 802.11 defines */ + +#define WLAN_FC_PVER (BIT(1) | BIT(0)) +#define WLAN_FC_TODS BIT(8) +#define WLAN_FC_FROMDS BIT(9) +#define WLAN_FC_MOREFRAG BIT(10) +#define WLAN_FC_RETRY BIT(11) +#define WLAN_FC_PWRMGT BIT(12) +#define WLAN_FC_MOREDATA BIT(13) +#define WLAN_FC_ISWEP BIT(14) +#define WLAN_FC_ORDER BIT(15) + +#define WLAN_FC_GET_TYPE(fc) (((fc) & (BIT(3) | BIT(2))) >> 2) +#define WLAN_FC_GET_STYPE(fc) \ + (((fc) & (BIT(7) | BIT(6) | BIT(5) | BIT(4))) >> 4) + +#define WLAN_GET_SEQ_FRAG(seq) ((seq) & (BIT(3) | BIT(2) | BIT(1) | BIT(0))) +#define WLAN_GET_SEQ_SEQ(seq) \ + (((seq) & (~(BIT(3) | BIT(2) | BIT(1) | BIT(0)))) >> 4) + +#define WLAN_FC_TYPE_MGMT 0 +#define WLAN_FC_TYPE_CTRL 1 +#define WLAN_FC_TYPE_DATA 2 + +/* management */ +#define WLAN_FC_STYPE_ASSOC_REQ 0 +#define WLAN_FC_STYPE_ASSOC_RESP 1 +#define WLAN_FC_STYPE_REASSOC_REQ 2 +#define WLAN_FC_STYPE_REASSOC_RESP 3 +#define WLAN_FC_STYPE_PROBE_REQ 4 +#define WLAN_FC_STYPE_PROBE_RESP 5 +#define WLAN_FC_STYPE_BEACON 8 +#define WLAN_FC_STYPE_ATIM 9 +#define WLAN_FC_STYPE_DISASSOC 10 +#define WLAN_FC_STYPE_AUTH 11 +#define WLAN_FC_STYPE_DEAUTH 12 + +/* control */ +#define WLAN_FC_STYPE_PSPOLL 10 +#define WLAN_FC_STYPE_RTS 11 +#define WLAN_FC_STYPE_CTS 12 +#define WLAN_FC_STYPE_ACK 13 +#define WLAN_FC_STYPE_CFEND 14 +#define WLAN_FC_STYPE_CFENDACK 15 + +/* data */ +#define WLAN_FC_STYPE_DATA 0 +#define WLAN_FC_STYPE_DATA_CFACK 1 +#define WLAN_FC_STYPE_DATA_CFPOLL 2 +#define WLAN_FC_STYPE_DATA_CFACKPOLL 3 +#define WLAN_FC_STYPE_NULLFUNC 4 +#define WLAN_FC_STYPE_CFACK 5 +#define WLAN_FC_STYPE_CFPOLL 6 +#define WLAN_FC_STYPE_CFACKPOLL 7 + +/* Authentication algorithms */ +#define WLAN_AUTH_OPEN 0 +#define WLAN_AUTH_SHARED_KEY 1 + +#define WLAN_AUTH_CHALLENGE_LEN 128 + +#define WLAN_CAPABILITY_ESS BIT(0) +#define WLAN_CAPABILITY_IBSS BIT(1) +#define WLAN_CAPABILITY_CF_POLLABLE BIT(2) +#define WLAN_CAPABILITY_CF_POLL_REQUEST BIT(3) +#define WLAN_CAPABILITY_PRIVACY BIT(4) + +/* Status codes */ +#define WLAN_STATUS_SUCCESS 0 +#define WLAN_STATUS_UNSPECIFIED_FAILURE 1 +#define WLAN_STATUS_CAPS_UNSUPPORTED 10 +#define WLAN_STATUS_REASSOC_NO_ASSOC 11 +#define WLAN_STATUS_ASSOC_DENIED_UNSPEC 12 +#define WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG 13 +#define WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION 14 +#define WLAN_STATUS_CHALLENGE_FAIL 15 +#define WLAN_STATUS_AUTH_TIMEOUT 16 +#define WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA 17 +#define WLAN_STATUS_ASSOC_DENIED_RATES 18 +/* 802.11b */ +#define WLAN_STATUS_ASSOC_DENIED_NOSHORT 19 +#define WLAN_STATUS_ASSOC_DENIED_NOPBCC 20 +#define WLAN_STATUS_ASSOC_DENIED_NOAGILITY 21 +/* IEEE 802.11i */ +#define WLAN_STATUS_INVALID_IE 40 +#define WLAN_STATUS_GROUP_CIPHER_NOT_VALID 41 +#define WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID 42 +#define WLAN_STATUS_AKMP_NOT_VALID 43 +#define WLAN_STATUS_UNSUPPORTED_RSN_IE_VERSION 44 +#define WLAN_STATUS_INVALID_RSN_IE_CAPAB 45 +#define WLAN_STATUS_CIPHER_REJECTED_PER_POLICY 46 + +/* Reason codes */ +#define WLAN_REASON_UNSPECIFIED 1 +#define WLAN_REASON_PREV_AUTH_NOT_VALID 2 +#define WLAN_REASON_DEAUTH_LEAVING 3 +#define WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY 4 +#define WLAN_REASON_DISASSOC_AP_BUSY 5 +#define WLAN_REASON_CLASS2_FRAME_FROM_NONAUTH_STA 6 +#define WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA 7 +#define WLAN_REASON_DISASSOC_STA_HAS_LEFT 8 +#define WLAN_REASON_STA_REQ_ASSOC_WITHOUT_AUTH 9 +/* IEEE 802.11i */ +#define WLAN_REASON_INVALID_IE 13 +#define WLAN_REASON_MICHAEL_MIC_FAILURE 14 +#define WLAN_REASON_4WAY_HANDSHAKE_TIMEOUT 15 +#define WLAN_REASON_GROUP_KEY_UPDATE_TIMEOUT 16 +#define WLAN_REASON_IE_IN_4WAY_DIFFERS 17 +#define WLAN_REASON_GROUP_CIPHER_NOT_VALID 18 +#define WLAN_REASON_PAIRWISE_CIPHER_NOT_VALID 19 +#define WLAN_REASON_AKMP_NOT_VALID 20 +#define WLAN_REASON_UNSUPPORTED_RSN_IE_VERSION 21 +#define WLAN_REASON_INVALID_RSN_IE_CAPAB 22 +#define WLAN_REASON_IEEE_802_1X_AUTH_FAILED 23 +#define WLAN_REASON_CIPHER_SUITE_REJECTED 24 + + +/* Information Element IDs */ +#define WLAN_EID_SSID 0 +#define WLAN_EID_SUPP_RATES 1 +#define WLAN_EID_FH_PARAMS 2 +#define WLAN_EID_DS_PARAMS 3 +#define WLAN_EID_CF_PARAMS 4 +#define WLAN_EID_TIM 5 +#define WLAN_EID_IBSS_PARAMS 6 +#define WLAN_EID_CHALLENGE 16 +#define WLAN_EID_RSN 48 +#define WLAN_EID_GENERIC 221 + + +/* HFA384X Configuration RIDs */ +#define HFA384X_RID_CNFPORTTYPE 0xFC00 +#define HFA384X_RID_CNFOWNMACADDR 0xFC01 +#define HFA384X_RID_CNFDESIREDSSID 0xFC02 +#define HFA384X_RID_CNFOWNCHANNEL 0xFC03 +#define HFA384X_RID_CNFOWNSSID 0xFC04 +#define HFA384X_RID_CNFOWNATIMWINDOW 0xFC05 +#define HFA384X_RID_CNFSYSTEMSCALE 0xFC06 +#define HFA384X_RID_CNFMAXDATALEN 0xFC07 +#define HFA384X_RID_CNFWDSADDRESS 0xFC08 +#define HFA384X_RID_CNFPMENABLED 0xFC09 +#define HFA384X_RID_CNFPMEPS 0xFC0A +#define HFA384X_RID_CNFMULTICASTRECEIVE 0xFC0B +#define HFA384X_RID_CNFMAXSLEEPDURATION 0xFC0C +#define HFA384X_RID_CNFPMHOLDOVERDURATION 0xFC0D +#define HFA384X_RID_CNFOWNNAME 0xFC0E +#define HFA384X_RID_CNFOWNDTIMPERIOD 0xFC10 +#define HFA384X_RID_CNFWDSADDRESS1 0xFC11 /* AP f/w only */ +#define HFA384X_RID_CNFWDSADDRESS2 0xFC12 /* AP f/w only */ +#define HFA384X_RID_CNFWDSADDRESS3 0xFC13 /* AP f/w only */ +#define HFA384X_RID_CNFWDSADDRESS4 0xFC14 /* AP f/w only */ +#define HFA384X_RID_CNFWDSADDRESS5 0xFC15 /* AP f/w only */ +#define HFA384X_RID_CNFWDSADDRESS6 0xFC16 /* AP f/w only */ +#define HFA384X_RID_CNFMULTICASTPMBUFFERING 0xFC17 /* AP f/w only */ +#define HFA384X_RID_UNKNOWN1 0xFC20 +#define HFA384X_RID_UNKNOWN2 0xFC21 +#define HFA384X_RID_CNFWEPDEFAULTKEYID 0xFC23 +#define HFA384X_RID_CNFDEFAULTKEY0 0xFC24 +#define HFA384X_RID_CNFDEFAULTKEY1 0xFC25 +#define HFA384X_RID_CNFDEFAULTKEY2 0xFC26 +#define HFA384X_RID_CNFDEFAULTKEY3 0xFC27 +#define HFA384X_RID_CNFWEPFLAGS 0xFC28 +#define HFA384X_RID_CNFWEPKEYMAPPINGTABLE 0xFC29 +#define HFA384X_RID_CNFAUTHENTICATION 0xFC2A +#define HFA384X_RID_CNFMAXASSOCSTA 0xFC2B /* AP f/w only */ +#define HFA384X_RID_CNFTXCONTROL 0xFC2C +#define HFA384X_RID_CNFROAMINGMODE 0xFC2D +#define HFA384X_RID_CNFHOSTAUTHENTICATION 0xFC2E /* AP f/w only */ +#define HFA384X_RID_CNFRCVCRCERROR 0xFC30 +#define HFA384X_RID_CNFMMLIFE 0xFC31 +#define HFA384X_RID_CNFALTRETRYCOUNT 0xFC32 +#define HFA384X_RID_CNFBEACONINT 0xFC33 +#define HFA384X_RID_CNFAPPCFINFO 0xFC34 /* AP f/w only */ +#define HFA384X_RID_CNFSTAPCFINFO 0xFC35 +#define HFA384X_RID_CNFPRIORITYQUSAGE 0xFC37 +#define HFA384X_RID_CNFTIMCTRL 0xFC40 +#define HFA384X_RID_UNKNOWN3 0xFC41 /* added in STA f/w 0.7.x */ +#define HFA384X_RID_CNFTHIRTY2TALLY 0xFC42 /* added in STA f/w 0.8.0 */ +#define HFA384X_RID_CNFENHSECURITY 0xFC43 /* AP f/w or STA f/w >= 1.6.3 */ +#define HFA384X_RID_CNFDBMADJUST 0xFC46 /* added in STA f/w 1.3.1 */ +#define HFA384X_RID_GENERICELEMENT 0xFC48 /* added in STA f/w 1.7.0; + * write only */ +#define HFA384X_RID_PROPAGATIONDELAY 0xFC49 /* added in STA f/w 1.7.6 */ +#define HFA384X_RID_GROUPADDRESSES 0xFC80 +#define HFA384X_RID_CREATEIBSS 0xFC81 +#define HFA384X_RID_FRAGMENTATIONTHRESHOLD 0xFC82 +#define HFA384X_RID_RTSTHRESHOLD 0xFC83 +#define HFA384X_RID_TXRATECONTROL 0xFC84 +#define HFA384X_RID_PROMISCUOUSMODE 0xFC85 +#define HFA384X_RID_FRAGMENTATIONTHRESHOLD0 0xFC90 /* AP f/w only */ +#define HFA384X_RID_FRAGMENTATIONTHRESHOLD1 0xFC91 /* AP f/w only */ +#define HFA384X_RID_FRAGMENTATIONTHRESHOLD2 0xFC92 /* AP f/w only */ +#define HFA384X_RID_FRAGMENTATIONTHRESHOLD3 0xFC93 /* AP f/w only */ +#define HFA384X_RID_FRAGMENTATIONTHRESHOLD4 0xFC94 /* AP f/w only */ +#define HFA384X_RID_FRAGMENTATIONTHRESHOLD5 0xFC95 /* AP f/w only */ +#define HFA384X_RID_FRAGMENTATIONTHRESHOLD6 0xFC96 /* AP f/w only */ +#define HFA384X_RID_RTSTHRESHOLD0 0xFC97 /* AP f/w only */ +#define HFA384X_RID_RTSTHRESHOLD1 0xFC98 /* AP f/w only */ +#define HFA384X_RID_RTSTHRESHOLD2 0xFC99 /* AP f/w only */ +#define HFA384X_RID_RTSTHRESHOLD3 0xFC9A /* AP f/w only */ +#define HFA384X_RID_RTSTHRESHOLD4 0xFC9B /* AP f/w only */ +#define HFA384X_RID_RTSTHRESHOLD5 0xFC9C /* AP f/w only */ +#define HFA384X_RID_RTSTHRESHOLD6 0xFC9D /* AP f/w only */ +#define HFA384X_RID_TXRATECONTROL0 0xFC9E /* AP f/w only */ +#define HFA384X_RID_TXRATECONTROL1 0xFC9F /* AP f/w only */ +#define HFA384X_RID_TXRATECONTROL2 0xFCA0 /* AP f/w only */ +#define HFA384X_RID_TXRATECONTROL3 0xFCA1 /* AP f/w only */ +#define HFA384X_RID_TXRATECONTROL4 0xFCA2 /* AP f/w only */ +#define HFA384X_RID_TXRATECONTROL5 0xFCA3 /* AP f/w only */ +#define HFA384X_RID_TXRATECONTROL6 0xFCA4 /* AP f/w only */ +#define HFA384X_RID_CNFSHORTPREAMBLE 0xFCB0 +#define HFA384X_RID_CNFEXCLUDELONGPREAMBLE 0xFCB1 +#define HFA384X_RID_CNFAUTHENTICATIONRSPTO 0xFCB2 +#define HFA384X_RID_CNFBASICRATES 0xFCB3 +#define HFA384X_RID_CNFSUPPORTEDRATES 0xFCB4 +#define HFA384X_RID_CNFFALLBACKCTRL 0xFCB5 /* added in STA f/w 1.3.1 */ +#define HFA384X_RID_WEPKEYDISABLE 0xFCB6 /* added in STA f/w 1.3.1 */ +#define HFA384X_RID_WEPKEYMAPINDEX 0xFCB7 /* ? */ +#define HFA384X_RID_BROADCASTKEYID 0xFCB8 /* ? */ +#define HFA384X_RID_ENTSECFLAGEYID 0xFCB9 /* ? */ +#define HFA384X_RID_CNFPASSIVESCANCTRL 0xFCBA /* added in STA f/w 1.5.0 */ +#define HFA384X_RID_SSNHANDLINGMODE 0xFCBB /* added in STA f/w 1.7.0 */ +#define HFA384X_RID_MDCCONTROL 0xFCBC /* added in STA f/w 1.7.0 */ +#define HFA384X_RID_MDCCOUNTRY 0xFCBD /* added in STA f/w 1.7.0 */ +#define HFA384X_RID_TXPOWERMAX 0xFCBE /* added in STA f/w 1.7.0 */ +#define HFA384X_RID_CNFLFOENABLED 0xFCBF /* added in STA f/w 1.6.3 */ +#define HFA384X_RID_CAPINFO 0xFCC0 /* added in STA f/w 1.7.0 */ +#define HFA384X_RID_LISTENINTERVAL 0xFCC1 /* added in STA f/w 1.7.0 */ +#define HFA384X_RID_SW_ANT_DIV 0xFCC2 /* added in STA f/w 1.7.0; Prism3 */ +#define HFA384X_RID_LED_CTRL 0xFCC4 /* added in STA f/w 1.7.6 */ +#define HFA384X_RID_HFODELAY 0xFCC5 /* added in STA f/w 1.7.6 */ +#define HFA384X_RID_DISALLOWEDBSSID 0xFCC6 /* added in STA f/w 1.8.0 */ +#define HFA384X_RID_TICKTIME 0xFCE0 +#define HFA384X_RID_SCANREQUEST 0xFCE1 +#define HFA384X_RID_JOINREQUEST 0xFCE2 +#define HFA384X_RID_AUTHENTICATESTATION 0xFCE3 /* AP f/w only */ +#define HFA384X_RID_CHANNELINFOREQUEST 0xFCE4 /* AP f/w only */ +#define HFA384X_RID_HOSTSCAN 0xFCE5 /* added in STA f/w 1.3.1 */ + +/* HFA384X Information RIDs */ +#define HFA384X_RID_MAXLOADTIME 0xFD00 +#define HFA384X_RID_DOWNLOADBUFFER 0xFD01 +#define HFA384X_RID_PRIID 0xFD02 +#define HFA384X_RID_PRISUPRANGE 0xFD03 +#define HFA384X_RID_CFIACTRANGES 0xFD04 +#define HFA384X_RID_NICSERNUM 0xFD0A +#define HFA384X_RID_NICID 0xFD0B +#define HFA384X_RID_MFISUPRANGE 0xFD0C +#define HFA384X_RID_CFISUPRANGE 0xFD0D +#define HFA384X_RID_CHANNELLIST 0xFD10 +#define HFA384X_RID_REGULATORYDOMAINS 0xFD11 +#define HFA384X_RID_TEMPTYPE 0xFD12 +#define HFA384X_RID_CIS 0xFD13 +#define HFA384X_RID_STAID 0xFD20 +#define HFA384X_RID_STASUPRANGE 0xFD21 +#define HFA384X_RID_MFIACTRANGES 0xFD22 +#define HFA384X_RID_CFIACTRANGES2 0xFD23 +#define HFA384X_RID_PRODUCTNAME 0xFD24 /* added in STA f/w 1.3.1; + * only Prism2.5(?) */ +#define HFA384X_RID_PORTSTATUS 0xFD40 +#define HFA384X_RID_CURRENTSSID 0xFD41 +#define HFA384X_RID_CURRENTBSSID 0xFD42 +#define HFA384X_RID_COMMSQUALITY 0xFD43 +#define HFA384X_RID_CURRENTTXRATE 0xFD44 +#define HFA384X_RID_CURRENTBEACONINTERVAL 0xFD45 +#define HFA384X_RID_CURRENTSCALETHRESHOLDS 0xFD46 +#define HFA384X_RID_PROTOCOLRSPTIME 0xFD47 +#define HFA384X_RID_SHORTRETRYLIMIT 0xFD48 +#define HFA384X_RID_LONGRETRYLIMIT 0xFD49 +#define HFA384X_RID_MAXTRANSMITLIFETIME 0xFD4A +#define HFA384X_RID_MAXRECEIVELIFETIME 0xFD4B +#define HFA384X_RID_CFPOLLABLE 0xFD4C +#define HFA384X_RID_AUTHENTICATIONALGORITHMS 0xFD4D +#define HFA384X_RID_PRIVACYOPTIONIMPLEMENTED 0xFD4F +#define HFA384X_RID_DBMCOMMSQUALITY 0xFD51 /* added in STA f/w 1.3.1 */ +#define HFA384X_RID_CURRENTTXRATE1 0xFD80 /* AP f/w only */ +#define HFA384X_RID_CURRENTTXRATE2 0xFD81 /* AP f/w only */ +#define HFA384X_RID_CURRENTTXRATE3 0xFD82 /* AP f/w only */ +#define HFA384X_RID_CURRENTTXRATE4 0xFD83 /* AP f/w only */ +#define HFA384X_RID_CURRENTTXRATE5 0xFD84 /* AP f/w only */ +#define HFA384X_RID_CURRENTTXRATE6 0xFD85 /* AP f/w only */ +#define HFA384X_RID_OWNMACADDR 0xFD86 /* AP f/w only */ +#define HFA384X_RID_SCANRESULTSTABLE 0xFD88 /* added in STA f/w 0.8.3 */ +#define HFA384X_RID_HOSTSCANRESULTS 0xFD89 /* added in STA f/w 1.3.1 */ +#define HFA384X_RID_AUTHENTICATIONUSED 0xFD8A /* added in STA f/w 1.3.4 */ +#define HFA384X_RID_CNFFAASWITCHCTRL 0xFD8B /* added in STA f/w 1.6.3 */ +#define HFA384X_RID_ASSOCIATIONFAILURE 0xFD8D /* added in STA f/w 1.8.0 */ +#define HFA384X_RID_PHYTYPE 0xFDC0 +#define HFA384X_RID_CURRENTCHANNEL 0xFDC1 +#define HFA384X_RID_CURRENTPOWERSTATE 0xFDC2 +#define HFA384X_RID_CCAMODE 0xFDC3 +#define HFA384X_RID_SUPPORTEDDATARATES 0xFDC6 +#define HFA384X_RID_LFO_VOLT_REG_TEST_RES 0xFDC7 /* added in STA f/w 1.7.1 */ +#define HFA384X_RID_BUILDSEQ 0xFFFE +#define HFA384X_RID_FWID 0xFFFF + + +struct hfa384x_comp_ident +{ + u16 id; + u16 variant; + u16 major; + u16 minor; +} __attribute__ ((packed)); + +#define HFA384X_COMP_ID_PRI 0x15 +#define HFA384X_COMP_ID_STA 0x1f +#define HFA384X_COMP_ID_FW_AP 0x14b + +struct hfa384x_sup_range +{ + u16 role; + u16 id; + u16 variant; + u16 bottom; + u16 top; +} __attribute__ ((packed)); + + +struct hfa384x_build_id +{ + u16 pri_seq; + u16 sec_seq; +} __attribute__ ((packed)); + +/* FD01 - Download Buffer */ +struct hfa384x_rid_download_buffer +{ + u16 page; + u16 offset; + u16 length; +} __attribute__ ((packed)); + +/* BSS connection quality (RID FD43 range, RID FD51 dBm-normalized) */ +struct hfa384x_comms_quality { + u16 comm_qual; /* 0 .. 92 */ + u16 signal_level; /* 27 .. 154 */ + u16 noise_level; /* 27 .. 154 */ +} __attribute__ ((packed)); + + +/* netdevice private ioctls (used, e.g., with iwpriv from user space) */ + +/* New wireless extensions API - SET/GET convention (even ioctl numbers are + * root only) + */ +#define PRISM2_IOCTL_PRISM2_PARAM (SIOCIWFIRSTPRIV + 0) +#define PRISM2_IOCTL_GET_PRISM2_PARAM (SIOCIWFIRSTPRIV + 1) +#define PRISM2_IOCTL_WRITEMIF (SIOCIWFIRSTPRIV + 2) +#define PRISM2_IOCTL_READMIF (SIOCIWFIRSTPRIV + 3) +#define PRISM2_IOCTL_MONITOR (SIOCIWFIRSTPRIV + 4) +#define PRISM2_IOCTL_RESET (SIOCIWFIRSTPRIV + 6) +#define PRISM2_IOCTL_INQUIRE (SIOCIWFIRSTPRIV + 8) +#define PRISM2_IOCTL_WDS_ADD (SIOCIWFIRSTPRIV + 10) +#define PRISM2_IOCTL_WDS_DEL (SIOCIWFIRSTPRIV + 12) +#define PRISM2_IOCTL_SET_RID_WORD (SIOCIWFIRSTPRIV + 14) +#define PRISM2_IOCTL_MACCMD (SIOCIWFIRSTPRIV + 16) +#define PRISM2_IOCTL_ADDMAC (SIOCIWFIRSTPRIV + 18) +#define PRISM2_IOCTL_DELMAC (SIOCIWFIRSTPRIV + 20) +#define PRISM2_IOCTL_KICKMAC (SIOCIWFIRSTPRIV + 22) + +/* following are not in SIOCGIWPRIV list; check permission in the driver code + */ +#define PRISM2_IOCTL_DOWNLOAD (SIOCDEVPRIVATE + 13) +#define PRISM2_IOCTL_HOSTAPD (SIOCDEVPRIVATE + 14) + + +/* PRISM2_IOCTL_PRISM2_PARAM ioctl() subtypes: */ +enum { + /* PRISM2_PARAM_PTYPE = 1, */ /* REMOVED 2003-10-22 */ + PRISM2_PARAM_TXRATECTRL = 2, + PRISM2_PARAM_BEACON_INT = 3, + PRISM2_PARAM_PSEUDO_IBSS = 4, + PRISM2_PARAM_ALC = 5, + /* PRISM2_PARAM_TXPOWER = 6, */ /* REMOVED 2003-10-22 */ + PRISM2_PARAM_DUMP = 7, + PRISM2_PARAM_OTHER_AP_POLICY = 8, + PRISM2_PARAM_AP_MAX_INACTIVITY = 9, + PRISM2_PARAM_AP_BRIDGE_PACKETS = 10, + PRISM2_PARAM_DTIM_PERIOD = 11, + PRISM2_PARAM_AP_NULLFUNC_ACK = 12, + PRISM2_PARAM_MAX_WDS = 13, + PRISM2_PARAM_AP_AUTOM_AP_WDS = 14, + PRISM2_PARAM_AP_AUTH_ALGS = 15, + PRISM2_PARAM_MONITOR_ALLOW_FCSERR = 16, + PRISM2_PARAM_HOST_ENCRYPT = 17, + PRISM2_PARAM_HOST_DECRYPT = 18, + PRISM2_PARAM_BUS_MASTER_THRESHOLD_RX = 19, + PRISM2_PARAM_BUS_MASTER_THRESHOLD_TX = 20, + PRISM2_PARAM_HOST_ROAMING = 21, + PRISM2_PARAM_BCRX_STA_KEY = 22, + PRISM2_PARAM_IEEE_802_1X = 23, + PRISM2_PARAM_ANTSEL_TX = 24, + PRISM2_PARAM_ANTSEL_RX = 25, + PRISM2_PARAM_MONITOR_TYPE = 26, + PRISM2_PARAM_WDS_TYPE = 27, + PRISM2_PARAM_HOSTSCAN = 28, + PRISM2_PARAM_AP_SCAN = 29, + PRISM2_PARAM_ENH_SEC = 30, + PRISM2_PARAM_IO_DEBUG = 31, + PRISM2_PARAM_BASIC_RATES = 32, + PRISM2_PARAM_OPER_RATES = 33, + PRISM2_PARAM_HOSTAPD = 34, + PRISM2_PARAM_HOSTAPD_STA = 35, + PRISM2_PARAM_WPA = 36, + PRISM2_PARAM_PRIVACY_INVOKED = 37, + PRISM2_PARAM_TKIP_COUNTERMEASURES = 38, + PRISM2_PARAM_DROP_UNENCRYPTED = 39, + PRISM2_PARAM_SCAN_CHANNEL_MASK = 40, +}; + +enum { HOSTAP_ANTSEL_DO_NOT_TOUCH = 0, HOSTAP_ANTSEL_DIVERSITY = 1, + HOSTAP_ANTSEL_LOW = 2, HOSTAP_ANTSEL_HIGH = 3 }; + + +/* PRISM2_IOCTL_MACCMD ioctl() subcommands: */ +enum { AP_MAC_CMD_POLICY_OPEN = 0, AP_MAC_CMD_POLICY_ALLOW = 1, + AP_MAC_CMD_POLICY_DENY = 2, AP_MAC_CMD_FLUSH = 3, + AP_MAC_CMD_KICKALL = 4 }; + + +/* PRISM2_IOCTL_DOWNLOAD ioctl() dl_cmd: */ +enum { + PRISM2_DOWNLOAD_VOLATILE = 1 /* RAM */, + /* Note! Old versions of prism2_srec have a fatal error in CRC-16 + * calculation, which will corrupt all non-volatile downloads. + * PRISM2_DOWNLOAD_NON_VOLATILE used to be 2, but it is now 3 to + * prevent use of old versions of prism2_srec for non-volatile + * download. */ + PRISM2_DOWNLOAD_NON_VOLATILE = 3 /* FLASH */, + PRISM2_DOWNLOAD_VOLATILE_GENESIS = 4 /* RAM in Genesis mode */, + /* Persistent versions of volatile download commands (keep firmware + * data in memory and automatically re-download after hw_reset */ + PRISM2_DOWNLOAD_VOLATILE_PERSISTENT = 5, + PRISM2_DOWNLOAD_VOLATILE_GENESIS_PERSISTENT = 6, +}; + +struct prism2_download_param { + u32 dl_cmd; + u32 start_addr; + u32 num_areas; + struct prism2_download_area { + u32 addr; /* wlan card address */ + u32 len; + caddr_t ptr; /* pointer to data in user space */ + } data[0]; +}; + +#define PRISM2_MAX_DOWNLOAD_AREA_LEN 131072 +#define PRISM2_MAX_DOWNLOAD_LEN 262144 + + +/* PRISM2_IOCTL_HOSTAPD ioctl() cmd: */ +enum { + PRISM2_HOSTAPD_FLUSH = 1, + PRISM2_HOSTAPD_ADD_STA = 2, + PRISM2_HOSTAPD_REMOVE_STA = 3, + PRISM2_HOSTAPD_GET_INFO_STA = 4, + /* REMOVED: PRISM2_HOSTAPD_RESET_TXEXC_STA = 5, */ + PRISM2_SET_ENCRYPTION = 6, + PRISM2_GET_ENCRYPTION = 7, + PRISM2_HOSTAPD_SET_FLAGS_STA = 8, + PRISM2_HOSTAPD_GET_RID = 9, + PRISM2_HOSTAPD_SET_RID = 10, + PRISM2_HOSTAPD_SET_ASSOC_AP_ADDR = 11, + PRISM2_HOSTAPD_SET_GENERIC_ELEMENT = 12, + PRISM2_HOSTAPD_MLME = 13, + PRISM2_HOSTAPD_SCAN_REQ = 14, + PRISM2_HOSTAPD_STA_CLEAR_STATS = 15, +}; + +#define PRISM2_HOSTAPD_MAX_BUF_SIZE 1024 +#define PRISM2_HOSTAPD_RID_HDR_LEN \ +((int) (&((struct prism2_hostapd_param *) 0)->u.rid.data)) +#define PRISM2_HOSTAPD_GENERIC_ELEMENT_HDR_LEN \ +((int) (&((struct prism2_hostapd_param *) 0)->u.generic_elem.data)) + +/* Maximum length for algorithm names (-1 for nul termination) used in ioctl() + */ +#define HOSTAP_CRYPT_ALG_NAME_LEN 16 + + +struct prism2_hostapd_param { + u32 cmd; + u8 sta_addr[ETH_ALEN]; + union { + struct { + u16 aid; + u16 capability; + u8 tx_supp_rates; + } add_sta; + struct { + u32 inactive_sec; + } get_info_sta; + struct { + u8 alg[HOSTAP_CRYPT_ALG_NAME_LEN]; + u32 flags; + u32 err; + u8 idx; + u8 seq[8]; /* sequence counter (set: RX, get: TX) */ + u16 key_len; + u8 key[0]; + } crypt; + struct { + u32 flags_and; + u32 flags_or; + } set_flags_sta; + struct { + u16 rid; + u16 len; + u8 data[0]; + } rid; + struct { + u8 len; + u8 data[0]; + } generic_elem; + struct { +#define MLME_STA_DEAUTH 0 +#define MLME_STA_DISASSOC 1 + u16 cmd; + u16 reason_code; + } mlme; + struct { + u8 ssid_len; + u8 ssid[32]; + } scan_req; + } u; +}; + +#define HOSTAP_CRYPT_FLAG_SET_TX_KEY BIT(0) +#define HOSTAP_CRYPT_FLAG_PERMANENT BIT(1) + +#define HOSTAP_CRYPT_ERR_UNKNOWN_ALG 2 +#define HOSTAP_CRYPT_ERR_UNKNOWN_ADDR 3 +#define HOSTAP_CRYPT_ERR_CRYPT_INIT_FAILED 4 +#define HOSTAP_CRYPT_ERR_KEY_SET_FAILED 5 +#define HOSTAP_CRYPT_ERR_TX_KEY_SET_FAILED 6 +#define HOSTAP_CRYPT_ERR_CARD_CONF_FAILED 7 + + +#endif /* HOSTAP_COMMON_H */ diff --git a/contrib/hostapd-0.4.9/hostapd.8 b/contrib/hostapd-0.4.9/hostapd.8 new file mode 100644 index 0000000000..8b02497803 --- /dev/null +++ b/contrib/hostapd-0.4.9/hostapd.8 @@ -0,0 +1,56 @@ +.TH HOSTAPD 8 "April 7, 2005" hostapd hostapd +.SH NAME +hostapd \- IEEE 802.11 AP, IEEE 802.1X/WPA/WPA2/EAP/RADIUS Authenticator +.SH SYNOPSIS +.B hostapd +[-hdBKt] +.SH DESCRIPTION +This manual page documents briefly the +.B hostapd +daemon. +.PP +.B hostapd +is a user space daemon for access point and authentication servers. +It implements IEEE 802.11 access point management, IEEE 802.1X/WPA/WPA2/EAP Authenticators and RADIUS authentication server. +The current version supports Linux (Host AP, madwifi, Prism54 drivers) and FreeBSD (net80211). + +.B hostapd +is designed to be a "daemon" program that runs in the background and acts as the backend component controlling authentication. +.B hostapd +supports separate frontend programs and an example text-based frontend, +.BR hostapd_cli , +is included with +.BR hostapd . +.SH OPTIONS +A summary of options is included below. +For a complete description, run +.BR hostapd +from the command line. +.TP +.B \-h +Show usage. +.TP +.B \-d +Show more debug messages. +.TP +.B \-dd +Show even more debug messages. +.TP +.B \-B +Run daemon in the background. +.TP +.B \-K +Include key data in debug messages. +.TP +.B \-t +Include timestamps in some debug messages. +.TP +.B \-v +Show hostapd version. +.SH SEE ALSO +.BR hostapd_cli (1). +.SH AUTHOR +hostapd was written by Jouni Malinen . +.PP +This manual page was written by Faidon Liambotis , +for the Debian project (but may be used by others). diff --git a/contrib/hostapd-0.4.9/hostapd.accept b/contrib/hostapd-0.4.9/hostapd.accept new file mode 100644 index 0000000000..57122b6634 --- /dev/null +++ b/contrib/hostapd-0.4.9/hostapd.accept @@ -0,0 +1,5 @@ +# List of MAC addresses that are allowed to authenticate (IEEE 802.11) +# with the AP. +00:11:22:33:44:55 +00:66:77:88:99:aa +00:00:22:33:44:55 diff --git a/contrib/hostapd-0.4.9/hostapd.c b/contrib/hostapd-0.4.9/hostapd.c new file mode 100644 index 0000000000..086af6291b --- /dev/null +++ b/contrib/hostapd-0.4.9/hostapd.c @@ -0,0 +1,846 @@ +/* + * Host AP (software wireless LAN access point) user space daemon for + * Host AP kernel driver + * Copyright (c) 2002-2005, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "eloop.h" +#include "hostapd.h" +#include "ieee802_1x.h" +#include "ieee802_11.h" +#include "accounting.h" +#include "eapol_sm.h" +#include "iapp.h" +#include "ap.h" +#include "ieee802_11_auth.h" +#include "sta_info.h" +#include "driver.h" +#include "radius_client.h" +#include "radius_server.h" +#include "wpa.h" +#include "ctrl_iface.h" +#include "tls.h" +#include "eap_sim_db.h" +#include "version.h" +#include "hostap_common.h" + + +struct hapd_interfaces { + int count; + hostapd **hapd; +}; + +unsigned char rfc1042_header[6] = { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 }; + + +extern int wpa_debug_level; +extern int wpa_debug_show_keys; +extern int wpa_debug_timestamp; + + +void hostapd_logger(struct hostapd_data *hapd, const u8 *addr, + unsigned int module, int level, const char *fmt, ...) +{ + char *format, *module_str; + int maxlen; + va_list ap; + int conf_syslog_level, conf_stdout_level; + unsigned int conf_syslog, conf_stdout; + + maxlen = strlen(fmt) + 100; + format = malloc(maxlen); + if (!format) + return; + + if (hapd && hapd->conf) { + conf_syslog_level = hapd->conf->logger_syslog_level; + conf_stdout_level = hapd->conf->logger_stdout_level; + conf_syslog = hapd->conf->logger_syslog; + conf_stdout = hapd->conf->logger_stdout; + } else { + conf_syslog_level = conf_stdout_level = 0; + conf_syslog = conf_stdout = (unsigned int) -1; + } + + switch (module) { + case HOSTAPD_MODULE_IEEE80211: + module_str = "IEEE 802.11"; + break; + case HOSTAPD_MODULE_IEEE8021X: + module_str = "IEEE 802.1X"; + break; + case HOSTAPD_MODULE_RADIUS: + module_str = "RADIUS"; + break; + case HOSTAPD_MODULE_WPA: + module_str = "WPA"; + break; + case HOSTAPD_MODULE_DRIVER: + module_str = "DRIVER"; + break; + case HOSTAPD_MODULE_IAPP: + module_str = "IAPP"; + break; + default: + module_str = NULL; + break; + } + + if (hapd && hapd->conf && addr) + snprintf(format, maxlen, "%s: STA " MACSTR "%s%s: %s", + hapd->conf->iface, MAC2STR(addr), + module_str ? " " : "", module_str, fmt); + else if (hapd && hapd->conf) + snprintf(format, maxlen, "%s:%s%s %s", + hapd->conf->iface, module_str ? " " : "", + module_str, fmt); + else if (addr) + snprintf(format, maxlen, "STA " MACSTR "%s%s: %s", + MAC2STR(addr), module_str ? " " : "", + module_str, fmt); + else + snprintf(format, maxlen, "%s%s%s", + module_str, module_str ? ": " : "", fmt); + + if ((conf_stdout & module) && level >= conf_stdout_level) { + wpa_debug_print_timestamp(); + va_start(ap, fmt); + vprintf(format, ap); + va_end(ap); + printf("\n"); + } + + if ((conf_syslog & module) && level >= conf_syslog_level) { + int priority; + switch (level) { + case HOSTAPD_LEVEL_DEBUG_VERBOSE: + case HOSTAPD_LEVEL_DEBUG: + priority = LOG_DEBUG; + break; + case HOSTAPD_LEVEL_INFO: + priority = LOG_INFO; + break; + case HOSTAPD_LEVEL_NOTICE: + priority = LOG_NOTICE; + break; + case HOSTAPD_LEVEL_WARNING: + priority = LOG_WARNING; + break; + default: + priority = LOG_INFO; + break; + } + va_start(ap, fmt); + vsyslog(priority, format, ap); + va_end(ap); + } + + free(format); +} + + +const char * hostapd_ip_txt(const struct hostapd_ip_addr *addr, char *buf, + size_t buflen) +{ + if (buflen == 0 || addr == NULL) + return NULL; + + if (addr->af == AF_INET) { + snprintf(buf, buflen, "%s", inet_ntoa(addr->u.v4)); + } else { + buf[0] = '\0'; + } +#ifdef CONFIG_IPV6 + if (addr->af == AF_INET6) { + if (inet_ntop(AF_INET6, &addr->u.v6, buf, buflen) == NULL) + buf[0] = '\0'; + } +#endif /* CONFIG_IPV6 */ + + return buf; +} + + +static void hostapd_deauth_all_stas(hostapd *hapd) +{ +#if 0 + u8 addr[ETH_ALEN]; + + memset(addr, 0xff, ETH_ALEN); + hostapd_sta_deauth(hapd, addr, WLAN_REASON_PREV_AUTH_NOT_VALID); +#else + /* New Prism2.5/3 STA firmware versions seem to have issues with this + * broadcast deauth frame. This gets the firmware in odd state where + * nothing works correctly, so let's skip sending this for a while + * until the issue has been resolved. */ +#endif +} + + +/* This function will be called whenever a station associates with the AP */ +void hostapd_new_assoc_sta(hostapd *hapd, struct sta_info *sta, int reassoc) +{ + if (hapd->tkip_countermeasures) { + hostapd_sta_deauth(hapd, sta->addr, + WLAN_REASON_MICHAEL_MIC_FAILURE); + return; + } + + /* IEEE 802.11F (IAPP) */ + if (hapd->conf->ieee802_11f) + iapp_new_station(hapd->iapp, sta); + + /* Start accounting here, if IEEE 802.1X and WPA are not used. + * IEEE 802.1X/WPA code will start accounting after the station has + * been authorized. */ + if (!hapd->conf->ieee802_1x && !hapd->conf->wpa) + accounting_sta_start(hapd, sta); + + /* Start IEEE 802.1X authentication process for new stations */ + ieee802_1x_new_station(hapd, sta); + if (reassoc) + wpa_sm_event(hapd, sta, WPA_REAUTH); + else + wpa_new_station(hapd, sta); +} + + +static void handle_term(int sig, void *eloop_ctx, void *signal_ctx) +{ + printf("Signal %d received - terminating\n", sig); + eloop_terminate(); +} + + +static void handle_reload(int sig, void *eloop_ctx, void *signal_ctx) +{ + struct hapd_interfaces *hapds = (struct hapd_interfaces *) eloop_ctx; + struct hostapd_config *newconf; + int i; + + printf("Signal %d received - reloading configuration\n", sig); + + for (i = 0; i < hapds->count; i++) { + hostapd *hapd = hapds->hapd[i]; + newconf = hostapd_config_read(hapd->config_fname); + if (newconf == NULL) { + printf("Failed to read new configuration file - " + "continuing with old.\n"); + continue; + } + /* TODO: update dynamic data based on changed configuration + * items (e.g., open/close sockets, remove stations added to + * deny list, etc.) */ + radius_client_flush(hapd->radius); + hostapd_config_free(hapd->conf); + hapd->conf = newconf; + } +} + + +#ifdef HOSTAPD_DUMP_STATE +static void hostapd_dump_state(hostapd *hapd) +{ + FILE *f; + time_t now; + struct sta_info *sta; + int i; + char *buf; + + if (!hapd->conf->dump_log_name) { + printf("Dump file not defined - ignoring dump request\n"); + return; + } + + printf("Dumping hostapd state to '%s'\n", hapd->conf->dump_log_name); + f = fopen(hapd->conf->dump_log_name, "w"); + if (f == NULL) { + printf("Could not open dump file '%s' for writing.\n", + hapd->conf->dump_log_name); + return; + } + + time(&now); + fprintf(f, "hostapd state dump - %s", ctime(&now)); + + for (sta = hapd->sta_list; sta != NULL; sta = sta->next) { + fprintf(f, "\nSTA=" MACSTR "\n", MAC2STR(sta->addr)); + + fprintf(f, + " AID=%d flags=0x%x %s%s%s%s%s%s\n" + " capability=0x%x listen_interval=%d\n", + sta->aid, + sta->flags, + (sta->flags & WLAN_STA_AUTH ? "[AUTH]" : ""), + (sta->flags & WLAN_STA_ASSOC ? "[ASSOC]" : ""), + (sta->flags & WLAN_STA_PS ? "[PS]" : ""), + (sta->flags & WLAN_STA_TIM ? "[TIM]" : ""), + (sta->flags & WLAN_STA_PERM ? "[PERM]" : ""), + (sta->flags & WLAN_STA_AUTHORIZED ? "[AUTHORIZED]" : + ""), + sta->capability, + sta->listen_interval); + + fprintf(f, " supported_rates="); + for (i = 0; i < sizeof(sta->supported_rates); i++) + if (sta->supported_rates[i] != 0) + fprintf(f, "%02x ", sta->supported_rates[i]); + fprintf(f, "%s%s%s%s\n", + (sta->tx_supp_rates & WLAN_RATE_1M ? "[1M]" : ""), + (sta->tx_supp_rates & WLAN_RATE_2M ? "[2M]" : ""), + (sta->tx_supp_rates & WLAN_RATE_5M5 ? "[5.5M]" : ""), + (sta->tx_supp_rates & WLAN_RATE_11M ? "[11M]" : "")); + + fprintf(f, + " timeout_next=%s\n", + (sta->timeout_next == STA_NULLFUNC ? "NULLFUNC POLL" : + (sta->timeout_next == STA_DISASSOC ? "DISASSOC" : + "DEAUTH"))); + + ieee802_1x_dump_state(f, " ", sta); + } + + buf = malloc(4096); + if (buf) { + int count = radius_client_get_mib(hapd->radius, buf, 4096); + if (count < 0) + count = 0; + else if (count > 4095) + count = 4095; + buf[count] = '\0'; + fprintf(f, "%s", buf); + + count = radius_server_get_mib(hapd->radius_srv, buf, 4096); + if (count < 0) + count = 0; + else if (count > 4095) + count = 4095; + buf[count] = '\0'; + fprintf(f, "%s", buf); + free(buf); + } + fclose(f); +} +#endif /* HOSTAPD_DUMP_STATE */ + + +static void handle_dump_state(int sig, void *eloop_ctx, void *signal_ctx) +{ +#ifdef HOSTAPD_DUMP_STATE + struct hapd_interfaces *hapds = (struct hapd_interfaces *) eloop_ctx; + int i; + + for (i = 0; i < hapds->count; i++) { + hostapd *hapd = hapds->hapd[i]; + hostapd_dump_state(hapd); + } +#endif /* HOSTAPD_DUMP_STATE */ +} + + +static void hostapd_cleanup(struct hostapd_data *hapd) +{ + hostapd_ctrl_iface_deinit(hapd); + + free(hapd->default_wep_key); + hapd->default_wep_key = NULL; + iapp_deinit(hapd->iapp); + accounting_deinit(hapd); + wpa_deinit(hapd); + ieee802_1x_deinit(hapd); + hostapd_acl_deinit(hapd); + radius_client_deinit(hapd->radius); + hapd->radius = NULL; + radius_server_deinit(hapd->radius_srv); + hapd->radius_srv = NULL; + + hostapd_wireless_event_deinit(hapd); + + if (hapd->driver) + hostapd_driver_deinit(hapd); + + hostapd_config_free(hapd->conf); + hapd->conf = NULL; + + free(hapd->config_fname); + +#ifdef EAP_TLS_FUNCS + if (hapd->ssl_ctx) { + tls_deinit(hapd->ssl_ctx); + hapd->ssl_ctx = NULL; + } +#endif /* EAP_TLS_FUNCS */ + + if (hapd->eap_sim_db_priv) + eap_sim_db_deinit(hapd->eap_sim_db_priv); +} + + +static int hostapd_flush_old_stations(hostapd *hapd) +{ + int ret = 0; + + printf("Flushing old station entries\n"); + if (hostapd_flush(hapd)) { + printf("Could not connect to kernel driver.\n"); + ret = -1; + } + printf("Deauthenticate all stations\n"); + hostapd_deauth_all_stas(hapd); + + return ret; +} + + +static int hostapd_setup_interface(struct hostapd_data *hapd) +{ + struct hostapd_config *conf = hapd->conf; + u8 ssid[HOSTAPD_SSID_LEN + 1]; + int ssid_len, set_ssid; + int ret = 0; + + if (hostapd_driver_init(hapd)) { + printf("%s driver initialization failed.\n", + hapd->driver ? hapd->driver->name : "Unknown"); + hapd->driver = NULL; + return -1; + } + + /* + * Fetch the SSID from the system and use it or, + * if one was specified in the config file, verify they + * match. + */ + ssid_len = hostapd_get_ssid(hapd, ssid, sizeof(ssid)); + if (ssid_len < 0) { + printf("Could not read SSID from system\n"); + return -1; + } + if (conf->ssid_set) { + /* + * If SSID is specified in the config file and it differs + * from what is being used then force installation of the + * new SSID. + */ + set_ssid = (conf->ssid_len != ssid_len || + memcmp(conf->ssid, ssid, ssid_len) != 0); + } else { + /* + * No SSID in the config file; just use the one we got + * from the system. + */ + set_ssid = 0; + conf->ssid_len = ssid_len; + memcpy(conf->ssid, ssid, conf->ssid_len); + conf->ssid[conf->ssid_len] = '\0'; + } + + printf("Using interface %s with hwaddr " MACSTR " and ssid '%s'\n", + hapd->conf->iface, MAC2STR(hapd->own_addr), hapd->conf->ssid); + + if (hostapd_setup_wpa_psk(conf)) { + printf("WPA-PSK setup failed.\n"); + return -1; + } + + /* Set SSID for the kernel driver (to be used in beacon and probe + * response frames) */ + if (set_ssid && hostapd_set_ssid(hapd, (u8 *) conf->ssid, + conf->ssid_len)) { + printf("Could not set SSID for kernel driver\n"); + return -1; + } + + if (HOSTAPD_DEBUG_COND(HOSTAPD_DEBUG_MSGDUMPS)) + conf->radius->msg_dumps = 1; + hapd->radius = radius_client_init(hapd, conf->radius); + if (hapd->radius == NULL) { + printf("RADIUS client initialization failed.\n"); + return -1; + } + if (conf->radius_server_clients) { + struct radius_server_conf srv; + memset(&srv, 0, sizeof(srv)); + srv.client_file = conf->radius_server_clients; + srv.auth_port = conf->radius_server_auth_port; + srv.hostapd_conf = conf; + srv.eap_sim_db_priv = hapd->eap_sim_db_priv; + srv.ssl_ctx = hapd->ssl_ctx; + srv.ipv6 = conf->radius_server_ipv6; + hapd->radius_srv = radius_server_init(&srv); + if (hapd->radius_srv == NULL) { + printf("RADIUS server initialization failed.\n"); + return -1; + } + } + if (hostapd_acl_init(hapd)) { + printf("ACL initialization failed.\n"); + return -1; + } + if (ieee802_1x_init(hapd)) { + printf("IEEE 802.1X initialization failed.\n"); + return -1; + } + + if (hapd->conf->wpa && wpa_init(hapd)) { + printf("WPA initialization failed.\n"); + return -1; + } + + if (accounting_init(hapd)) { + printf("Accounting initialization failed.\n"); + return -1; + } + + if (hapd->conf->ieee802_11f && + (hapd->iapp = iapp_init(hapd, hapd->conf->iapp_iface)) == NULL) { + printf("IEEE 802.11F (IAPP) initialization failed.\n"); + return -1; + } + + if (hostapd_wireless_event_init(hapd) < 0) + return -1; + + if (hostapd_flush_old_stations(hapd)) + return -1; + + if (hostapd_ctrl_iface_init(hapd)) { + printf("Failed to setup control interface\n"); + ret = -1; + } + + return ret; +} + + +struct driver { + struct driver *next; + char *name; + const struct driver_ops *ops; +}; +static struct driver *drivers = NULL; + +void driver_register(const char *name, const struct driver_ops *ops) +{ + struct driver *d; + + d = malloc(sizeof(struct driver)); + if (d == NULL) { + printf("Failed to register driver %s!\n", name); + return; + } + d->name = strdup(name); + if (d->name == NULL) { + printf("Failed to register driver %s!\n", name); + free(d); + return; + } + d->ops = ops; + + d->next = drivers; + drivers = d; +} + + +void driver_unregister(const char *name) +{ + struct driver *p, **pp; + + for (pp = &drivers; (p = *pp) != NULL; pp = &p->next) { + if (strcasecmp(p->name, name) == 0) { + *pp = p->next; + p->next = NULL; + free(p->name); + free(p); + break; + } + } +} + + +static void driver_unregister_all(void) +{ + struct driver *p, *pp; + p = drivers; + drivers = NULL; + while (p) { + pp = p; + p = p->next; + free(pp->name); + free(pp); + } +} + + +const struct driver_ops * driver_lookup(const char *name) +{ + struct driver *p; + + if (strcmp(name, "default") == 0) { + p = drivers; + while (p && p->next) + p = p->next; + return p->ops; + } + + for (p = drivers; p != NULL; p = p->next) { + if (strcasecmp(p->name, name) == 0) + return p->ops; + } + + return NULL; +} + + +static void show_version(void) +{ + fprintf(stderr, + "hostapd v" VERSION_STR "\n" + "User space daemon for IEEE 802.11 AP management,\n" + "IEEE 802.1X/WPA/WPA2/EAP/RADIUS Authenticator\n" + "Copyright (c) 2002-2005, Jouni Malinen " + "and contributors\n"); +} + + +static void usage(void) +{ + show_version(); + fprintf(stderr, + "\n" + "usage: hostapd [-hdBKt] \n" + "\n" + "options:\n" + " -h show this usage\n" + " -d show more debug messages (-dd for even more)\n" + " -B run daemon in the background\n" + " -K include key data in debug messages\n" + " -t include timestamps in some debug messages\n" + " -v show hostapd version\n"); + + exit(1); +} + + +static hostapd * hostapd_init(const char *config_file) +{ + hostapd *hapd; + + hapd = malloc(sizeof(*hapd)); + if (hapd == NULL) { + printf("Could not allocate memory for hostapd data\n"); + goto fail; + } + memset(hapd, 0, sizeof(*hapd)); + + hapd->config_fname = strdup(config_file); + if (hapd->config_fname == NULL) { + printf("Could not allocate memory for config_fname\n"); + goto fail; + } + + hapd->conf = hostapd_config_read(hapd->config_fname); + if (hapd->conf == NULL) { + goto fail; + } + + if (hapd->conf->individual_wep_key_len > 0) { + /* use key0 in individual key and key1 in broadcast key */ + hapd->default_wep_key_idx = 1; + } + +#ifdef EAP_TLS_FUNCS + if (hapd->conf->eap_server && + (hapd->conf->ca_cert || hapd->conf->server_cert)) { + hapd->ssl_ctx = tls_init(NULL); + if (hapd->ssl_ctx == NULL) { + printf("Failed to initialize TLS\n"); + goto fail; + } + if (tls_global_ca_cert(hapd->ssl_ctx, hapd->conf->ca_cert)) { + printf("Failed to load CA certificate (%s)\n", + hapd->conf->ca_cert); + goto fail; + } + if (tls_global_client_cert(hapd->ssl_ctx, + hapd->conf->server_cert)) { + printf("Failed to load server certificate (%s)\n", + hapd->conf->server_cert); + goto fail; + } + if (tls_global_private_key(hapd->ssl_ctx, + hapd->conf->private_key, + hapd->conf->private_key_passwd)) { + printf("Failed to load private key (%s)\n", + hapd->conf->private_key); + goto fail; + } + if (tls_global_set_verify(hapd->ssl_ctx, + hapd->conf->check_crl)) { + printf("Failed to enable check_crl\n"); + goto fail; + } + } +#endif /* EAP_TLS_FUNCS */ + + if (hapd->conf->eap_sim_db) { + hapd->eap_sim_db_priv = + eap_sim_db_init(hapd->conf->eap_sim_db); + if (hapd->eap_sim_db_priv == NULL) { + printf("Failed to initialize EAP-SIM database " + "interface\n"); + goto fail; + } + } + + if (hapd->conf->assoc_ap) + hapd->assoc_ap_state = WAIT_BEACON; + + /* FIX: need to fix this const vs. not */ + hapd->driver = (struct driver_ops *) hapd->conf->driver; + + return hapd; + +fail: + if (hapd) { + if (hapd->ssl_ctx) + tls_deinit(hapd->ssl_ctx); + if (hapd->conf) + hostapd_config_free(hapd->conf); + free(hapd->config_fname); + free(hapd); + } + return NULL; +} + + +void register_drivers(void); + +int main(int argc, char *argv[]) +{ + struct hapd_interfaces interfaces; + int ret = 1, i, j; + int c, debug = 0, daemonize = 0; + + for (;;) { + c = getopt(argc, argv, "BdhKtv"); + if (c < 0) + break; + switch (c) { + case 'h': + usage(); + break; + case 'd': + debug++; + break; + case 'B': + daemonize++; + break; + case 'K': + wpa_debug_show_keys++; + break; + case 't': + wpa_debug_timestamp++; + break; + case 'v': + show_version(); + exit(1); + break; + + default: + usage(); + break; + } + } + + if (optind == argc) + usage(); + + register_drivers(); /* NB: generated by Makefile */ + + interfaces.count = argc - optind; + + interfaces.hapd = malloc(interfaces.count * sizeof(hostapd *)); + if (interfaces.hapd == NULL) { + printf("malloc failed\n"); + exit(1); + } + + eloop_init(&interfaces); + eloop_register_signal(SIGHUP, handle_reload, NULL); + eloop_register_signal(SIGINT, handle_term, NULL); + eloop_register_signal(SIGTERM, handle_term, NULL); + eloop_register_signal(SIGUSR1, handle_dump_state, NULL); + + for (i = 0; i < interfaces.count; i++) { + printf("Configuration file: %s\n", argv[optind + i]); + interfaces.hapd[i] = hostapd_init(argv[optind + i]); + if (!interfaces.hapd[i]) + goto out; + for (j = 0; j < debug; j++) { + if (interfaces.hapd[i]->conf->logger_stdout_level > 0) + interfaces.hapd[i]->conf-> + logger_stdout_level--; + interfaces.hapd[i]->conf->debug++; + } + if (hostapd_setup_interface(interfaces.hapd[i])) + goto out; + wpa_debug_level -= interfaces.hapd[0]->conf->debug; + } + + if (daemonize && daemon(0, 0)) { + perror("daemon"); + goto out; + } + + openlog("hostapd", 0, LOG_DAEMON); + + eloop_run(); + + for (i = 0; i < interfaces.count; i++) { + hostapd_free_stas(interfaces.hapd[i]); + hostapd_flush_old_stations(interfaces.hapd[i]); + } + + ret = 0; + + out: + for (i = 0; i < interfaces.count; i++) { + if (!interfaces.hapd[i]) + continue; + + hostapd_cleanup(interfaces.hapd[i]); + free(interfaces.hapd[i]); + } + free(interfaces.hapd); + + eloop_destroy(); + + closelog(); + + driver_unregister_all(); + + return ret; +} diff --git a/contrib/hostapd-0.4.9/hostapd.conf b/contrib/hostapd-0.4.9/hostapd.conf new file mode 100644 index 0000000000..747579f094 --- /dev/null +++ b/contrib/hostapd-0.4.9/hostapd.conf @@ -0,0 +1,341 @@ +##### hostapd configuration file ############################################## +# Empty lines and lines starting with # are ignored + +# AP netdevice name (without 'ap' postfix, i.e., wlan0 uses wlan0ap for +# management frames); ath0 for madwifi +interface=wlan0 + +# In case of madwifi driver, an additional configuration parameter, bridge, +# must be used to notify hostapd if the interface is included in a bridge. This +# parameter is not used with Host AP driver. +#bridge=br0 + +# Driver interface type (hostap/wired/madwifi/prism54; default: hostap) +# driver=hostap + +# hostapd event logger configuration +# +# Two output method: syslog and stdout (only usable if not forking to +# background). +# +# Module bitfield (ORed bitfield of modules that will be logged; -1 = all +# modules): +# bit 0 (1) = IEEE 802.11 +# bit 1 (2) = IEEE 802.1X +# bit 2 (4) = RADIUS +# bit 3 (8) = WPA +# bit 4 (16) = driver interface +# bit 5 (32) = IAPP +# +# Levels (minimum value for logged events): +# 0 = verbose debugging +# 1 = debugging +# 2 = informational messages +# 3 = notification +# 4 = warning +# +logger_syslog=-1 +logger_syslog_level=2 +logger_stdout=-1 +logger_stdout_level=2 + +# Debugging: 0 = no, 1 = minimal, 2 = verbose, 3 = msg dumps, 4 = excessive +debug=0 + +# Dump file for state information (on SIGUSR1) +dump_file=/tmp/hostapd.dump + +# Interface for separate control program. If this is specified, hostapd +# will create this directory and a UNIX domain socket for listening to requests +# from external programs (CLI/GUI, etc.) for status information and +# configuration. The socket file will be named based on the interface name, so +# multiple hostapd processes/interfaces can be run at the same time if more +# than one interface is used. +# /var/run/hostapd is the recommended directory for sockets and by default, +# hostapd_cli will use it when trying to connect with hostapd. +ctrl_interface=/var/run/hostapd + +# Access control for the control interface can be configured by setting the +# directory to allow only members of a group to use sockets. This way, it is +# possible to run hostapd as root (since it needs to change network +# configuration and open raw sockets) and still allow GUI/CLI components to be +# run as non-root users. However, since the control interface can be used to +# change the network configuration, this access needs to be protected in many +# cases. By default, hostapd is configured to use gid 0 (root). If you +# want to allow non-root users to use the contron interface, add a new group +# and change this value to match with that group. Add users that should have +# control interface access to this group. +# +# This variable can be a group name or gid. +#ctrl_interface_group=wheel +ctrl_interface_group=0 + + +##### IEEE 802.11 related configuration ####################################### + +# SSID to be used in IEEE 802.11 management frames +ssid=test + +# Station MAC address -based authentication +# 0 = accept unless in deny list +# 1 = deny unless in accept list +# 2 = use external RADIUS server (accept/deny lists are searched first) +macaddr_acl=0 + +# Accept/deny lists are read from separate files (containing list of +# MAC addresses, one per line). Use absolute path name to make sure that the +# files can be read on SIGHUP configuration reloads. +#accept_mac_file=/etc/hostapd.accept +#deny_mac_file=/etc/hostapd.deny + +# IEEE 802.11 specifies two authentication algorithms. hostapd can be +# configured to allow both of these or only one. Open system authentication +# should be used with IEEE 802.1X. +# Bit fields of allowed authentication algorithms: +# bit 0 = Open System Authentication +# bit 1 = Shared Key Authentication (requires WEP) +auth_algs=3 + +# Associate as a station to another AP while still acting as an AP on the same +# channel. +#assoc_ap_addr=00:12:34:56:78:9a + + +##### IEEE 802.1X-2004 related configuration ################################## + +# Require IEEE 802.1X authorization +#ieee8021x=1 + +# IEEE 802.1X/EAPOL version +# hostapd is implemented based on IEEE Std 802.1X-2004 which defines EAPOL +# version 2. However, there are many client implementations that do not handle +# the new version number correctly (they seem to drop the frames completely). +# In order to make hostapd interoperate with these clients, the version number +# can be set to the older version (1) with this configuration value. +#eapol_version=2 + +# Optional displayable message sent with EAP Request-Identity. The first \0 +# in this string will be converted to ASCII-0 (nul). This can be used to +# separate network info (comma separated list of attribute=value pairs); see, +# e.g., draft-adrangi-eap-network-discovery-07.txt. +#eap_message=hello +#eap_message=hello\0networkid=netw,nasid=foo,portid=0,NAIRealms=example.com + +# WEP rekeying (disabled if key lengths are not set or are set to 0) +# Key lengths for default/broadcast and individual/unicast keys: +# 5 = 40-bit WEP (also known as 64-bit WEP with 40 secret bits) +# 13 = 104-bit WEP (also known as 128-bit WEP with 104 secret bits) +#wep_key_len_broadcast=5 +#wep_key_len_unicast=5 +# Rekeying period in seconds. 0 = do not rekey (i.e., set keys only once) +#wep_rekey_period=300 + +# EAPOL-Key index workaround (set bit7) for WinXP Supplicant (needed only if +# only broadcast keys are used) +eapol_key_index_workaround=0 + +# EAP reauthentication period in seconds (default: 3600 seconds; 0 = disable +# reauthentication). +#eap_reauth_period=3600 + +# Use PAE group address (01:80:c2:00:00:03) instead of individual target +# address when sending EAPOL frames with driver=wired. This is the most common +# mechanism used in wired authentication, but it also requires that the port +# is only used by one station. +#use_pae_group_addr=1 + +##### Integrated EAP server ################################################### + +# Optionally, hostapd can be configured to use an integrated EAP server +# to process EAP authentication locally without need for an external RADIUS +# server. This functionality can be used both as a local authentication server +# for IEEE 802.1X/EAPOL and as a RADIUS server for other devices. + +# Use integrated EAP server instead of external RADIUS authentication +# server. This is also needed if hostapd is configured to act as a RADIUS +# authentication server. +eap_server=0 + +# Path for EAP server user database +#eap_user_file=/etc/hostapd.eap_user + +# CA certificate (PEM or DER file) for EAP-TLS/PEAP/TTLS +#ca_cert=/etc/hostapd.ca.pem + +# Server certificate (PEM or DER file) for EAP-TLS/PEAP/TTLS +#server_cert=/etc/hostapd.server.pem + +# Private key matching with the server certificate for EAP-TLS/PEAP/TTLS +# This may point to the same file as server_cert if both certificate and key +# are included in a single file. PKCS#12 (PFX) file (.p12/.pfx) can also be +# used by commenting out server_cert and specifying the PFX file as the +# private_key. +#private_key=/etc/hostapd.server.prv + +# Passphrase for private key +#private_key_passwd=secret passphrase + +# Enable CRL verification. +# Note: hostapd does not yet support CRL downloading based on CDP. Thus, a +# valid CRL signed by the CA is required to be included in the ca_cert file. +# This can be done by using PEM format for CA certificate and CRL and +# concatenating these into one file. Whenever CRL changes, hostapd needs to be +# restarted to take the new CRL into use. +# 0 = do not verify CRLs (default) +# 1 = check the CRL of the user certificate +# 2 = check all CRLs in the certificate path +#check_crl=1 + +# Configuration data for EAP-SIM database/authentication gateway interface. +# This is a text string in implementation specific format. The example +# implementation in eap_sim_db.c uses this as the file name for the GSM +# authentication triplets. +#eap_sim_db=/etc/hostapd.sim_db + + +##### IEEE 802.11f - Inter-Access Point Protocol (IAPP) ####################### + +# Interface to be used for IAPP broadcast packets +#iapp_interface=eth0 + + +##### RADIUS client configuration ############################################# +# for IEEE 802.1X with external Authentication Server, IEEE 802.11 +# authentication with external ACL for MAC addresses, and accounting + +# The own IP address of the access point (used as NAS-IP-Address) +own_ip_addr=127.0.0.1 + +# Optional NAS-Identifier string for RADIUS messages. When used, this should be +# a unique to the NAS within the scope of the RADIUS server. For example, a +# fully qualified domain name can be used here. +#nas_identifier=ap.example.com + +# RADIUS authentication server +#auth_server_addr=127.0.0.1 +#auth_server_port=1812 +#auth_server_shared_secret=secret + +# RADIUS accounting server +#acct_server_addr=127.0.0.1 +#acct_server_port=1813 +#acct_server_shared_secret=secret + +# Secondary RADIUS servers; to be used if primary one does not reply to +# RADIUS packets. These are optional and there can be more than one secondary +# server listed. +#auth_server_addr=127.0.0.2 +#auth_server_port=1812 +#auth_server_shared_secret=secret2 +# +#acct_server_addr=127.0.0.2 +#acct_server_port=1813 +#acct_server_shared_secret=secret2 + +# Retry interval for trying to return to the primary RADIUS server (in +# seconds). RADIUS client code will automatically try to use the next server +# when the current server is not replying to requests. If this interval is set, +# primary server will be retried after configured amount of time even if the +# currently used secondary server is still working. +#radius_retry_primary_interval=600 + + +# Interim accounting update interval +# If this is set (larger than 0) and acct_server is configured, hostapd will +# send interim accounting updates every N seconds. Note: if set, this overrides +# possible Acct-Interim-Interval attribute in Access-Accept message. Thus, this +# value should not be configured in hostapd.conf, if RADIUS server is used to +# control the interim interval. +# This value should not be less 600 (10 minutes) and must not be less than +# 60 (1 minute). +#radius_acct_interim_interval=600 + + +##### RADIUS authentication server configuration ############################## + +# hostapd can be used as a RADIUS authentication server for other hosts. This +# requires that the integrated EAP authenticator is also enabled and both +# authentication services are sharing the same configuration. + +# File name of the RADIUS clients configuration for the RADIUS server. If this +# commented out, RADIUS server is disabled. +#radius_server_clients=/etc/hostapd.radius_clients + +# The UDP port number for the RADIUS authentication server +#radius_server_auth_port=1812 + +# Use IPv6 with RADIUS server (IPv4 will also be supported using IPv6 API) +#radius_server_ipv6=1 + + +##### WPA/IEEE 802.11i configuration ########################################## + +# Enable WPA. Setting this variable configures the AP to require WPA (either +# WPA-PSK or WPA-RADIUS/EAP based on other configuration). For WPA-PSK, either +# wpa_psk or wpa_passphrase must be set and wpa_key_mgmt must include WPA-PSK. +# For WPA-RADIUS/EAP, ieee8021x must be set (but without dynamic WEP keys), +# RADIUS authentication server must be configured, and WPA-EAP must be included +# in wpa_key_mgmt. +# This field is a bit field that can be used to enable WPA (IEEE 802.11i/D3.0) +# and/or WPA2 (full IEEE 802.11i/RSN): +# bit0 = WPA +# bit1 = IEEE 802.11i/RSN (WPA2) (dot11RSNAEnabled) +#wpa=1 + +# WPA pre-shared keys for WPA-PSK. This can be either entered as a 256-bit +# secret in hex format (64 hex digits), wpa_psk, or as an ASCII passphrase +# (8..63 characters) that will be converted to PSK. This conversion uses SSID +# so the PSK changes when ASCII passphrase is used and the SSID is changed. +# wpa_psk (dot11RSNAConfigPSKValue) +# wpa_passphrase (dot11RSNAConfigPSKPassPhrase) +#wpa_psk=0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef +#wpa_passphrase=secret passphrase + +# Optionally, WPA PSKs can be read from a separate text file (containing list +# of (PSK,MAC address) pairs. This allows more than one PSK to be configured. +# Use absolute path name to make sure that the files can be read on SIGHUP +# configuration reloads. +#wpa_psk_file=/etc/hostapd.wpa_psk + +# Set of accepted key management algorithms (WPA-PSK, WPA-EAP, or both). The +# entries are separated with a space. +# (dot11RSNAConfigAuthenticationSuitesTable) +#wpa_key_mgmt=WPA-PSK WPA-EAP + +# Set of accepted cipher suites (encryption algorithms) for pairwise keys +# (unicast packets). This is a space separated list of algorithms: +# CCMP = AES in Counter mode with CBC-MAC [RFC 3610, IEEE 802.11i/D7.0] +# TKIP = Temporal Key Integrity Protocol [IEEE 802.11i/D7.0] +# Group cipher suite (encryption algorithm for broadcast and multicast frames) +# is automatically selected based on this configuration. If only CCMP is +# allowed as the pairwise cipher, group cipher will also be CCMP. Otherwise, +# TKIP will be used as the group cipher. +# (dot11RSNAConfigPairwiseCiphersTable) +#wpa_pairwise=TKIP CCMP + +# Time interval for rekeying GTK (broadcast/multicast encryption keys) in +# seconds. (dot11RSNAConfigGroupRekeyTime) +#wpa_group_rekey=600 + +# Rekey GTK when any STA that possesses the current GTK is leaving the BSS. +# (dot11RSNAConfigGroupRekeyStrict) +#wpa_strict_rekey=1 + +# Time interval for rekeying GMK (master key used internally to generate GTKs +# (in seconds). +#wpa_gmk_rekey=86400 + +# Enable IEEE 802.11i/RSN/WPA2 pre-authentication. This is used to speed up +# roaming be pre-authenticating IEEE 802.1X/EAP part of the full RSN +# authentication and key handshake before actually associating with a new AP. +# (dot11RSNAPreauthenticationEnabled) +#rsn_preauth=1 +# +# Space separated list of interfaces from which pre-authentication frames are +# accepted (e.g., 'eth0' or 'eth0 wlan0wds0'. This list should include all +# interface that are used for connections to other APs. This could include +# wired interfaces and WDS links. The normal wireless data interface towards +# associated stations (e.g., wlan0) should not be added, since +# pre-authentication is only used with APs other than the currently associated +# one. +#rsn_preauth_interfaces=eth0 diff --git a/contrib/hostapd-0.4.9/hostapd.deny b/contrib/hostapd-0.4.9/hostapd.deny new file mode 100644 index 0000000000..1616678f57 --- /dev/null +++ b/contrib/hostapd-0.4.9/hostapd.deny @@ -0,0 +1,5 @@ +# List of MAC addresses that are not allowed to authenticate (IEEE 802.11) +# with the AP. +00:20:30:40:50:60 +00:ab:cd:ef:12:34 +00:00:30:40:50:60 diff --git a/contrib/hostapd-0.4.9/hostapd.eap_user b/contrib/hostapd-0.4.9/hostapd.eap_user new file mode 100644 index 0000000000..fd7b420fb6 --- /dev/null +++ b/contrib/hostapd-0.4.9/hostapd.eap_user @@ -0,0 +1,49 @@ +# hostapd user database for integrated EAP authenticator +# Each line must contain an identity, EAP method(s), and an optional password +# separated with whitespace (space or tab). The identity and password must be +# double quoted ("user"). [2] flag in the end of the line can be used to mark +# users for tunneled phase 2 authentication (e.g., within EAP-PEAP). In these +# cases, an anonymous identity can be used in the unencrypted phase 1 and the +# real user identity is transmitted only within the encrypted tunnel in phase +# 2. If non-anonymous access is needed, two user entries is needed, one for +# phase 1 and another with the same username for phase 2. +# +# EAP-TLS, EAP-PEAP, EAP-TTLS, and EAP-SIM do not use password option. +# EAP-MD5, EAP-MSCHAPV2, EAP-GTC, EAP-PAX, and EAP-PSK require a password. +# EAP-PEAP and EAP-TTLS require Phase 2 configuration. +# +# * can be used as a wildcard to match any user identity. The main purposes for +# this are to set anonymous phase 1 identity for EAP-PEAP and EAP-TTLS and to +# avoid having to configure every certificate for EAP-TLS authentication. The +# first matching entry is selected, so * should be used as the last phase 1 +# user entry. +# +# Multiple methods can be configured to make the authenticator try them one by +# one until the peer accepts one. The method names are separated with a +# comma (,). +# +# [ver=0] and [ver=1] flags after EAP type PEAP can be used to force PEAP +# version based on the Phase 1 identity. Without this flag, the EAP +# authenticator advertises the highest supported version and select the version +# based on the first PEAP packet from the supplicant. + +# Phase 1 users +"user" MD5 "password" +"test user" MD5 "secret" +"example user" TLS +"DOMAIN\user" MSCHAPV2 "password" +"gtc user" GTC "password" +"pax user" PAX "unknown" +"pax.user@example.com" PAX 0123456789abcdef0123456789abcdef +"psk user" PSK "unknown" +"psk.user@example.com" PSK 0123456789abcdef0123456789abcdef +"ttls" TTLS +"not anonymous" PEAP +* PEAP,TTLS,TLS,SIM + +# Phase 2 (tunnelled within EAP-PEAP or EAP-TTLS) users +"t-md5" MD5 "password" [2] +"DOMAIN\t-mschapv2" MSCHAPV2 "password" [2] +"t-gtc" GTC "password" [2] +"not anonymous" MSCHAPV2 "password" [2] +"user" MD5,GTC,MSCHAPV2 "password" [2] diff --git a/contrib/hostapd-0.4.9/hostapd.h b/contrib/hostapd-0.4.9/hostapd.h new file mode 100644 index 0000000000..fdcb420ee2 --- /dev/null +++ b/contrib/hostapd-0.4.9/hostapd.h @@ -0,0 +1,149 @@ +#ifndef HOSTAPD_H +#define HOSTAPD_H + +#include "common.h" +#include "ap.h" + +#ifndef ETH_ALEN +#define ETH_ALEN 6 +#endif +#ifndef IFNAMSIZ +#define IFNAMSIZ 16 +#endif +#ifndef ETH_P_ALL +#define ETH_P_ALL 0x0003 +#endif +#ifndef ETH_P_PAE +#define ETH_P_PAE 0x888E /* Port Access Entity (IEEE 802.1X) */ +#endif /* ETH_P_PAE */ + +#ifndef BIT +#define BIT(x) (1 << (x)) +#endif + +#ifndef MAC2STR +#define MAC2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5] +#define MACSTR "%02x:%02x:%02x:%02x:%02x:%02x" +#endif + +#include "config.h" + +struct ieee8023_hdr { + u8 dest[6]; + u8 src[6]; + u16 ethertype; +} __attribute__ ((packed)); + + +struct ieee80211_hdr { + u16 frame_control; + u16 duration_id; + u8 addr1[6]; + u8 addr2[6]; + u8 addr3[6]; + u16 seq_ctrl; + /* followed by 'u8 addr4[6];' if ToDS and FromDS is set in data frame + */ +} __attribute__ ((packed)); + +#define IEEE80211_DA_FROMDS addr1 +#define IEEE80211_BSSID_FROMDS addr2 +#define IEEE80211_SA_FROMDS addr3 + +#define IEEE80211_HDRLEN (sizeof(struct ieee80211_hdr)) + +#define IEEE80211_FC(type, stype) host_to_le16((type << 2) | (stype << 4)) + +/* MTU to be set for the wlan#ap device; this is mainly needed for IEEE 802.1X + * frames that might be longer than normal default MTU and they are not + * fragmented */ +#define HOSTAPD_MTU 2290 + +extern unsigned char rfc1042_header[6]; + +typedef struct hostapd_data hostapd; + +struct hostap_sta_driver_data { + unsigned long rx_packets, tx_packets, rx_bytes, tx_bytes; +}; + +struct driver_ops; +struct wpa_ctrl_dst; +struct radius_server_data; + +struct hostapd_data { + struct hostapd_config *conf; + char *config_fname; + + u8 own_addr[6]; + + int num_sta; /* number of entries in sta_list */ + struct sta_info *sta_list; /* STA info list head */ + struct sta_info *sta_hash[STA_HASH_SIZE]; + + /* pointers to STA info; based on allocated AID or NULL if AID free + * AID is in the range 1-2007, so sta_aid[0] corresponders to AID 1 + * and so on + */ + struct sta_info *sta_aid[MAX_AID_TABLE_SIZE]; + + struct driver_ops *driver; + + u8 *default_wep_key; + u8 default_wep_key_idx; + + struct radius_client_data *radius; + u32 acct_session_id_hi, acct_session_id_lo; + + struct iapp_data *iapp; + + enum { DO_NOT_ASSOC = 0, WAIT_BEACON, AUTHENTICATE, ASSOCIATE, + ASSOCIATED } assoc_ap_state; + char assoc_ap_ssid[33]; + int assoc_ap_ssid_len; + u16 assoc_ap_aid; + + struct hostapd_cached_radius_acl *acl_cache; + struct hostapd_acl_query_data *acl_queries; + + u8 *wpa_ie; + size_t wpa_ie_len; + struct wpa_authenticator *wpa_auth; + +#define PMKID_HASH_SIZE 128 +#define PMKID_HASH(pmkid) (unsigned int) ((pmkid)[0] & 0x7f) + struct rsn_pmksa_cache *pmkid[PMKID_HASH_SIZE]; + struct rsn_pmksa_cache *pmksa; + int pmksa_count; + + struct rsn_preauth_interface *preauth_iface; + time_t michael_mic_failure; + int michael_mic_failures; + int tkip_countermeasures; + + int ctrl_sock; + struct wpa_ctrl_dst *ctrl_dst; + + void *ssl_ctx; + void *eap_sim_db_priv; + struct radius_server_data *radius_srv; +}; + +void hostapd_new_assoc_sta(hostapd *hapd, struct sta_info *sta, int reassoc); +void hostapd_logger(struct hostapd_data *hapd, const u8 *addr, + unsigned int module, int level, const char *fmt, + ...) __attribute__ ((format (printf, 5, 6))); + + +#define HOSTAPD_DEBUG(level, args...) \ +do { \ + if (hapd->conf == NULL || hapd->conf->debug >= (level)) \ + printf(args); \ +} while (0) + +#define HOSTAPD_DEBUG_COND(level) (hapd->conf->debug >= (level)) + +const char * hostapd_ip_txt(const struct hostapd_ip_addr *addr, char *buf, + size_t buflen); + +#endif /* HOSTAPD_H */ diff --git a/contrib/hostapd-0.4.9/hostapd.radius_clients b/contrib/hostapd-0.4.9/hostapd.radius_clients new file mode 100644 index 0000000000..3980427253 --- /dev/null +++ b/contrib/hostapd-0.4.9/hostapd.radius_clients @@ -0,0 +1,4 @@ +# RADIUS client configuration for the RADIUS server +10.1.2.3 secret passphrase +192.168.1.0/24 another very secret passphrase +0.0.0.0/0 radius diff --git a/contrib/hostapd-0.4.9/hostapd.sim_db b/contrib/hostapd-0.4.9/hostapd.sim_db new file mode 100644 index 0000000000..01c593de8d --- /dev/null +++ b/contrib/hostapd-0.4.9/hostapd.sim_db @@ -0,0 +1,9 @@ +# Example GSM authentication triplet file for EAP-SIM authenticator +# IMSI:Kc:SRES:RAND +# IMSI: ASCII string (numbers) +# Kc: hex, 8 octets +# SRES: hex, 4 octets +# RAND: hex, 16 octets +234567898765432:A0A1A2A3A4A5A6A7:D1D2D3D4:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +234567898765432:B0B1B2B3B4B5B6B7:E1E2E3E4:BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB +234567898765432:C0C1C2C3C4C5C6C7:F1F2F3F4:CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC diff --git a/contrib/hostapd-0.4.9/hostapd.wpa_psk b/contrib/hostapd-0.4.9/hostapd.wpa_psk new file mode 100644 index 0000000000..0a9499acd7 --- /dev/null +++ b/contrib/hostapd-0.4.9/hostapd.wpa_psk @@ -0,0 +1,9 @@ +# List of WPA PSKs. Each line, except for empty lines and lines starting +# with #, must contain a MAC address and PSK separated with a space. +# Special MAC address 00:00:00:00:00:00 can be used to configure PSKs that +# anyone can use. PSK can be configured as an ASCII passphrase of 8..63 +# characters or as a 256-bit hex PSK (64 hex digits). +00:00:00:00:00:00 secret passphrase +00:11:22:33:44:55 another passphrase +00:22:33:44:55:66 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef +00:00:00:00:00:00 another passphrase for all STAs diff --git a/contrib/hostapd-0.4.9/hostapd_cli.1 b/contrib/hostapd-0.4.9/hostapd_cli.1 new file mode 100644 index 0000000000..062fc78589 --- /dev/null +++ b/contrib/hostapd-0.4.9/hostapd_cli.1 @@ -0,0 +1,83 @@ +.TH HOSTAPD_CLI 1 "April 7, 2005" hostapd_cli "hostapd command-line interface" +.SH NAME +hostapd_cli \- hostapd command-line interface +.SH SYNOPSIS +.B hostapd_cli +[-p] [-i] [-hv] [command..] +.SH DESCRIPTION +This manual page documents briefly the +.B hostapd_cli +utility. +.PP +.B hostapd_cli +is a command-line interface for the +.B hostapd +daemon. + +.B hostapd +is a user space daemon for access point and authentication servers. +It implements IEEE 802.11 access point management, IEEE 802.1X/WPA/WPA2/EAP Authenticators and RADIUS authentication server. +For more information about +.B hostapd +refer to the +.BR hostapd (8) +man page. +.SH OPTIONS +A summary of options is included below. +For a complete description, run +.BR hostapd_cli +from the command line. +.TP +.B \-p +Path to find control sockets. + +Default: /var/run/hostapd +.TP +.B \-i +Interface to listen on. + +Default: first interface found in socket path. +.TP +.B \-h +Show usage. +.TP +.B \-v +Show hostapd_cli version. +.SH COMMANDS +A summary of commands is included below. +For a complete description, run +.BR hostapd_cli +from the command line. +.TP +.B mib +Get MIB variables (dot1x, dot11, radius). +.TP +.B sta +Get MIB variables for one station. +.TP +.B all_sta +Get MIB variables for all stations. +.TP +.B help +Get usage help. +.TP +.B interface [ifname] +Show interfaces/select interface. +.TP +.B level +Change debug level. +.TP +.B license +Show full +.B hostapd_cli +license. +.TP +.B quit +Exit hostapd_cli. +.SH SEE ALSO +.BR hostapd (8). +.SH AUTHOR +hostapd_cli was written by Jouni Malinen . +.PP +This manual page was written by Faidon Liambotis , +for the Debian project (but may be used by others). diff --git a/contrib/hostapd-0.4.9/hostapd_cli.c b/contrib/hostapd-0.4.9/hostapd_cli.c new file mode 100644 index 0000000000..d93c41d57f --- /dev/null +++ b/contrib/hostapd-0.4.9/hostapd_cli.c @@ -0,0 +1,601 @@ +/* + * hostapd - command line interface for hostapd daemon + * Copyright (c) 2004-2005, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include +#include +#include +#include +#include +#include + +#include "wpa_ctrl.h" +#include "version.h" + + +static const char *hostapd_cli_version = +"hostapd_cli v" VERSION_STR "\n" +"Copyright (c) 2004-2005, Jouni Malinen and contributors"; + + +static const char *hostapd_cli_license = +"This program is free software. You can distribute it and/or modify it\n" +"under the terms of the GNU General Public License version 2.\n" +"\n" +"Alternatively, this software may be distributed under the terms of the\n" +"BSD license. See README and COPYING for more details.\n"; + +static const char *hostapd_cli_full_license = +"This program is free software; you can redistribute it and/or modify\n" +"it under the terms of the GNU General Public License version 2 as\n" +"published by the Free Software Foundation.\n" +"\n" +"This program is distributed in the hope that it will be useful,\n" +"but WITHOUT ANY WARRANTY; without even the implied warranty of\n" +"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" +"GNU General Public License for more details.\n" +"\n" +"You should have received a copy of the GNU General Public License\n" +"along with this program; if not, write to the Free Software\n" +"Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\n" +"\n" +"Alternatively, this software may be distributed under the terms of the\n" +"BSD license.\n" +"\n" +"Redistribution and use in source and binary forms, with or without\n" +"modification, are permitted provided that the following conditions are\n" +"met:\n" +"\n" +"1. Redistributions of source code must retain the above copyright\n" +" notice, this list of conditions and the following disclaimer.\n" +"\n" +"2. Redistributions in binary form must reproduce the above copyright\n" +" notice, this list of conditions and the following disclaimer in the\n" +" documentation and/or other materials provided with the distribution.\n" +"\n" +"3. Neither the name(s) of the above-listed copyright holder(s) nor the\n" +" names of its contributors may be used to endorse or promote products\n" +" derived from this software without specific prior written permission.\n" +"\n" +"THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n" +"\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n" +"LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n" +"A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n" +"OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n" +"SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n" +"LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n" +"DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n" +"THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n" +"(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n" +"OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n" +"\n"; + +static const char *commands_help = +"Commands:\n" +" mib get MIB variables (dot1x, dot11, radius)\n" +" sta get MIB vatiables for one station\n" +" all_sta get MIB variables for all stations\n" +" help show this usage help\n" +" interface [ifname] show interfaces/select interface\n" +" level change debug level\n" +" license show full hostapd_cli license\n" +" quit exit hostapd_cli\n"; + +static struct wpa_ctrl *ctrl_conn; +static int hostapd_cli_quit = 0; +static int hostapd_cli_attached = 0; +static const char *ctrl_iface_dir = "/var/run/hostapd"; +static char *ctrl_ifname = NULL; + + +static void usage(void) +{ + fprintf(stderr, "%s\n", hostapd_cli_version); + fprintf(stderr, + "\n" + "usage: hostapd_cli [-p] [-i] [-hv] " + "[command..]\n" + "\n" + "Options:\n" + " -h help (show this usage text)\n" + " -v shown version information\n" + " -p path to find control sockets (default: " + "/var/run/hostapd)\n" + " -i Interface to listen on (default: first " + "interface found in the\n" + " socket path)\n\n" + "%s", + commands_help); +} + + +static struct wpa_ctrl * hostapd_cli_open_connection(const char *ifname) +{ + char *cfile; + int flen; + + if (ifname == NULL) + return NULL; + + flen = strlen(ctrl_iface_dir) + strlen(ifname) + 2; + cfile = malloc(flen); + if (cfile == NULL) + return NULL; + snprintf(cfile, flen, "%s/%s", ctrl_iface_dir, ifname); + + ctrl_conn = wpa_ctrl_open(cfile); + free(cfile); + return ctrl_conn; +} + + +static void hostapd_cli_close_connection(void) +{ + if (ctrl_conn == NULL) + return; + + if (hostapd_cli_attached) { + wpa_ctrl_detach(ctrl_conn); + hostapd_cli_attached = 0; + } + wpa_ctrl_close(ctrl_conn); + ctrl_conn = NULL; +} + + +static void hostapd_cli_msg_cb(char *msg, size_t len) +{ + printf("%s\n", msg); +} + + +static int _wpa_ctrl_command(struct wpa_ctrl *ctrl, char *cmd, int print) +{ + char buf[4096]; + size_t len; + int ret; + + if (ctrl_conn == NULL) { + printf("Not connected to hostapd - command dropped.\n"); + return -1; + } + len = sizeof(buf) - 1; + ret = wpa_ctrl_request(ctrl, cmd, strlen(cmd), buf, &len, + hostapd_cli_msg_cb); + if (ret == -2) { + printf("'%s' command timed out.\n", cmd); + return -2; + } else if (ret < 0) { + printf("'%s' command failed.\n", cmd); + return -1; + } + if (print) { + buf[len] = '\0'; + printf("%s", buf); + } + return 0; +} + + +static inline int wpa_ctrl_command(struct wpa_ctrl *ctrl, char *cmd) +{ + return _wpa_ctrl_command(ctrl, cmd, 1); +} + + +static int hostapd_cli_cmd_ping(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + return wpa_ctrl_command(ctrl, "PING"); +} + + +static int hostapd_cli_cmd_mib(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + return wpa_ctrl_command(ctrl, "MIB"); +} + + +static int hostapd_cli_cmd_sta(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + char buf[64]; + if (argc != 1) { + printf("Invalid 'sta' command - exactly one argument, STA " + "address, is required.\n"); + return -1; + } + snprintf(buf, sizeof(buf), "STA %s", argv[0]); + return wpa_ctrl_command(ctrl, buf); +} + + +static int wpa_ctrl_command_sta(struct wpa_ctrl *ctrl, char *cmd, + char *addr, size_t addr_len) +{ + char buf[4096], *pos; + size_t len; + int ret; + + if (ctrl_conn == NULL) { + printf("Not connected to hostapd - command dropped.\n"); + return -1; + } + len = sizeof(buf) - 1; + ret = wpa_ctrl_request(ctrl, cmd, strlen(cmd), buf, &len, + hostapd_cli_msg_cb); + if (ret == -2) { + printf("'%s' command timed out.\n", cmd); + return -2; + } else if (ret < 0) { + printf("'%s' command failed.\n", cmd); + return -1; + } + + buf[len] = '\0'; + if (memcmp(buf, "FAIL", 4) == 0) + return -1; + printf("%s", buf); + + pos = buf; + while (*pos != '\0' && *pos != '\n') + pos++; + *pos = '\0'; + snprintf(addr, addr_len, "%s", buf); + return 0; +} + + +static int hostapd_cli_cmd_all_sta(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char addr[32], cmd[64]; + + if (wpa_ctrl_command_sta(ctrl, "STA-FIRST", addr, sizeof(addr))) + return 0; + do { + snprintf(cmd, sizeof(cmd), "STA-NEXT %s", addr); + } while (wpa_ctrl_command_sta(ctrl, cmd, addr, sizeof(addr)) == 0); + + return -1; +} + + +static int hostapd_cli_cmd_help(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + printf("%s", commands_help); + return 0; +} + + +static int hostapd_cli_cmd_license(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + printf("%s\n\n%s\n", hostapd_cli_version, hostapd_cli_full_license); + return 0; +} + + +static int hostapd_cli_cmd_quit(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + hostapd_cli_quit = 1; + return 0; +} + + +static int hostapd_cli_cmd_level(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + char cmd[256]; + if (argc != 1) { + printf("Invalid LEVEL command: needs one argument (debug " + "level)\n"); + return 0; + } + snprintf(cmd, sizeof(cmd), "LEVEL %s", argv[0]); + return wpa_ctrl_command(ctrl, cmd); +} + + +static void hostapd_cli_list_interfaces(struct wpa_ctrl *ctrl) +{ + struct dirent *dent; + DIR *dir; + + dir = opendir(ctrl_iface_dir); + if (dir == NULL) { + printf("Control interface directory '%s' could not be " + "openned.\n", ctrl_iface_dir); + return; + } + + printf("Available interfaces:\n"); + while ((dent = readdir(dir))) { + if (strcmp(dent->d_name, ".") == 0 || + strcmp(dent->d_name, "..") == 0) + continue; + printf("%s\n", dent->d_name); + } + closedir(dir); +} + + +static int hostapd_cli_cmd_interface(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + if (argc < 1) { + hostapd_cli_list_interfaces(ctrl); + return 0; + } + + hostapd_cli_close_connection(); + free(ctrl_ifname); + ctrl_ifname = strdup(argv[0]); + + if (hostapd_cli_open_connection(ctrl_ifname)) { + printf("Connected to interface '%s.\n", ctrl_ifname); + if (wpa_ctrl_attach(ctrl_conn) == 0) { + hostapd_cli_attached = 1; + } else { + printf("Warning: Failed to attach to " + "hostapd.\n"); + } + } else { + printf("Could not connect to interface '%s' - re-trying\n", + ctrl_ifname); + } + return 0; +} + + +struct hostapd_cli_cmd { + const char *cmd; + int (*handler)(struct wpa_ctrl *ctrl, int argc, char *argv[]); +}; + +static struct hostapd_cli_cmd hostapd_cli_commands[] = { + { "ping", hostapd_cli_cmd_ping }, + { "mib", hostapd_cli_cmd_mib }, + { "sta", hostapd_cli_cmd_sta }, + { "all_sta", hostapd_cli_cmd_all_sta }, + { "help", hostapd_cli_cmd_help }, + { "interface", hostapd_cli_cmd_interface }, + { "level", hostapd_cli_cmd_level }, + { "license", hostapd_cli_cmd_license }, + { "quit", hostapd_cli_cmd_quit }, + { NULL, NULL } +}; + + +static void wpa_request(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + struct hostapd_cli_cmd *cmd, *match = NULL; + int count; + + count = 0; + cmd = hostapd_cli_commands; + while (cmd->cmd) { + if (strncasecmp(cmd->cmd, argv[0], strlen(argv[0])) == 0) { + match = cmd; + count++; + } + cmd++; + } + + if (count > 1) { + printf("Ambiguous command '%s'; possible commands:", argv[0]); + cmd = hostapd_cli_commands; + while (cmd->cmd) { + if (strncasecmp(cmd->cmd, argv[0], strlen(argv[0])) == + 0) { + printf(" %s", cmd->cmd); + } + cmd++; + } + printf("\n"); + } else if (count == 0) { + printf("Unknown command '%s'\n", argv[0]); + } else { + match->handler(ctrl, argc - 1, &argv[1]); + } +} + + +static void hostapd_cli_recv_pending(struct wpa_ctrl *ctrl, int in_read) +{ + int first = 1; + if (ctrl_conn == NULL) + return; + while (wpa_ctrl_pending(ctrl)) { + char buf[256]; + size_t len = sizeof(buf) - 1; + if (wpa_ctrl_recv(ctrl, buf, &len) == 0) { + buf[len] = '\0'; + if (in_read && first) + printf("\n"); + first = 0; + printf("%s\n", buf); + } else { + printf("Could not read pending message.\n"); + break; + } + } +} + + +static void hostapd_cli_interactive(void) +{ + const int max_args = 10; + char cmd[256], *res, *argv[max_args], *pos; + int argc; + + printf("\nInteractive mode\n\n"); + + do { + hostapd_cli_recv_pending(ctrl_conn, 0); + printf("> "); + alarm(1); + res = fgets(cmd, sizeof(cmd), stdin); + alarm(0); + if (res == NULL) + break; + pos = cmd; + while (*pos != '\0') { + if (*pos == '\n') { + *pos = '\0'; + break; + } + pos++; + } + argc = 0; + pos = cmd; + for (;;) { + while (*pos == ' ') + pos++; + if (*pos == '\0') + break; + argv[argc] = pos; + argc++; + if (argc == max_args) + break; + while (*pos != '\0' && *pos != ' ') + pos++; + if (*pos == ' ') + *pos++ = '\0'; + } + if (argc) + wpa_request(ctrl_conn, argc, argv); + } while (!hostapd_cli_quit); +} + + +static void hostapd_cli_terminate(int sig) +{ + hostapd_cli_close_connection(); + exit(0); +} + + +static void hostapd_cli_alarm(int sig) +{ + if (ctrl_conn && _wpa_ctrl_command(ctrl_conn, "PING", 0)) { + printf("Connection to hostapd lost - trying to reconnect\n"); + hostapd_cli_close_connection(); + } + if (!ctrl_conn) { + ctrl_conn = hostapd_cli_open_connection(ctrl_ifname); + if (ctrl_conn) { + printf("Connection to hostapd re-established\n"); + if (wpa_ctrl_attach(ctrl_conn) == 0) { + hostapd_cli_attached = 1; + } else { + printf("Warning: Failed to attach to " + "hostapd.\n"); + } + } + } + if (ctrl_conn) + hostapd_cli_recv_pending(ctrl_conn, 1); + alarm(1); +} + + +int main(int argc, char *argv[]) +{ + int interactive; + int warning_displayed = 0; + int c; + + for (;;) { + c = getopt(argc, argv, "hi:p:v"); + if (c < 0) + break; + switch (c) { + case 'h': + usage(); + return 0; + case 'v': + printf("%s\n", hostapd_cli_version); + return 0; + case 'i': + ctrl_ifname = strdup(optarg); + break; + case 'p': + ctrl_iface_dir = optarg; + break; + default: + usage(); + return -1; + } + } + + interactive = argc == optind; + + if (interactive) { + printf("%s\n\n%s\n\n", hostapd_cli_version, + hostapd_cli_license); + } + + for (;;) { + if (ctrl_ifname == NULL) { + struct dirent *dent; + DIR *dir = opendir(ctrl_iface_dir); + if (dir) { + while ((dent = readdir(dir))) { + if (strcmp(dent->d_name, ".") == 0 || + strcmp(dent->d_name, "..") == 0) + continue; + printf("Selected interface '%s'\n", + dent->d_name); + ctrl_ifname = strdup(dent->d_name); + break; + } + closedir(dir); + } + } + ctrl_conn = hostapd_cli_open_connection(ctrl_ifname); + if (ctrl_conn) { + if (warning_displayed) + printf("Connection established.\n"); + break; + } + + if (!interactive) { + perror("Failed to connect to hostapd - " + "wpa_ctrl_open"); + return -1; + } + + if (!warning_displayed) { + printf("Could not connect to hostapd - re-trying\n"); + warning_displayed = 1; + } + sleep(1); + continue; + } + + signal(SIGINT, hostapd_cli_terminate); + signal(SIGTERM, hostapd_cli_terminate); + signal(SIGALRM, hostapd_cli_alarm); + + if (interactive) { + if (wpa_ctrl_attach(ctrl_conn) == 0) { + hostapd_cli_attached = 1; + } else { + printf("Warning: Failed to attach to hostapd.\n"); + } + hostapd_cli_interactive(); + } else + wpa_request(ctrl_conn, argc - optind, &argv[optind]); + + free(ctrl_ifname); + hostapd_cli_close_connection(); + return 0; +} diff --git a/contrib/hostapd-0.4.9/iapp.c b/contrib/hostapd-0.4.9/iapp.c new file mode 100644 index 0000000000..f4dd5573cf --- /dev/null +++ b/contrib/hostapd-0.4.9/iapp.c @@ -0,0 +1,522 @@ +/* + * Host AP (software wireless LAN access point) user space daemon for + * Host AP kernel driver / IEEE 802.11F-2003 Inter-Access Point Protocol (IAPP) + * Copyright (c) 2002-2005, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +/* TODO: + * Level 1: no administrative or security support + * (e.g., static BSSID to IP address mapping in each AP) + * Level 2: support for dynamic mapping of BSSID to IP address + * Level 3: support for encryption and authentication of IAPP messages + * - add support for MOVE-notify and MOVE-response (this requires support for + * finding out IP address for previous AP using RADIUS) + * - add support for Send- and ACK-Security-Block to speedup IEEE 802.1X during + * reassociation to another AP + * - implement counters etc. for IAPP MIB + * - verify endianness of fields in IAPP messages; are they big-endian as + * used here? + * - RADIUS connection for AP registration and BSSID to IP address mapping + * - TCP connection for IAPP MOVE, CACHE + * - broadcast ESP for IAPP ADD-notify + * - ESP for IAPP MOVE messages + * - security block sending/processing + * - IEEE 802.11 context transfer + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef USE_KERNEL_HEADERS +#include +#else /* USE_KERNEL_HEADERS */ +#include +#endif /* USE_KERNEL_HEADERS */ + +#include "hostapd.h" +#include "ieee802_11.h" +#include "iapp.h" +#include "eloop.h" +#include "sta_info.h" +#include "hostap_common.h" + + +#define IAPP_MULTICAST "224.0.1.178" +#define IAPP_UDP_PORT 3517 +#define IAPP_TCP_PORT 3517 + +struct iapp_hdr { + u8 version; + u8 command; + u16 identifier; + u16 length; + /* followed by length-6 octets of data */ +} __attribute__ ((packed)); + +#define IAPP_VERSION 0 + +enum IAPP_COMMAND { + IAPP_CMD_ADD_notify = 0, + IAPP_CMD_MOVE_notify = 1, + IAPP_CMD_MOVE_response = 2, + IAPP_CMD_Send_Security_Block = 3, + IAPP_CMD_ACK_Security_Block = 4, + IAPP_CMD_CACHE_notify = 5, + IAPP_CMD_CACHE_response = 6, +}; + + +/* ADD-notify - multicast UDP on the local LAN */ +struct iapp_add_notify { + u8 addr_len; /* ETH_ALEN */ + u8 reserved; + u8 mac_addr[ETH_ALEN]; + u16 seq_num; +} __attribute__ ((packed)); + + +/* Layer 2 Update frame (802.2 Type 1 LLC XID Update response) */ +struct iapp_layer2_update { + u8 da[ETH_ALEN]; /* broadcast */ + u8 sa[ETH_ALEN]; /* STA addr */ + u16 len; /* 6 */ + u8 dsap; /* null DSAP address */ + u8 ssap; /* null SSAP address, CR=Response */ + u8 control; + u8 xid_info[3]; +} __attribute__ ((packed)); + + +/* MOVE-notify - unicast TCP */ +struct iapp_move_notify { + u8 addr_len; /* ETH_ALEN */ + u8 reserved; + u8 mac_addr[ETH_ALEN]; + u16 seq_num; + u16 ctx_block_len; + /* followed by ctx_block_len bytes */ +} __attribute__ ((packed)); + + +/* MOVE-response - unicast TCP */ +struct iapp_move_response { + u8 addr_len; /* ETH_ALEN */ + u8 status; + u8 mac_addr[ETH_ALEN]; + u16 seq_num; + u16 ctx_block_len; + /* followed by ctx_block_len bytes */ +} __attribute__ ((packed)); + +enum { + IAPP_MOVE_SUCCESSFUL = 0, + IAPP_MOVE_DENIED = 1, + IAPP_MOVE_STALE_MOVE = 2, +}; + + +/* CACHE-notify */ +struct iapp_cache_notify { + u8 addr_len; /* ETH_ALEN */ + u8 reserved; + u8 mac_addr[ETH_ALEN]; + u16 seq_num; + u8 current_ap[ETH_ALEN]; + u16 ctx_block_len; + /* ctx_block_len bytes of context block followed by 16-bit context + * timeout */ +} __attribute__ ((packed)); + + +/* CACHE-response - unicast TCP */ +struct iapp_cache_response { + u8 addr_len; /* ETH_ALEN */ + u8 status; + u8 mac_addr[ETH_ALEN]; + u16 seq_num; +} __attribute__ ((packed)); + +enum { + IAPP_CACHE_SUCCESSFUL = 0, + IAPP_CACHE_STALE_CACHE = 1, +}; + + +/* Send-Security-Block - unicast TCP */ +struct iapp_send_security_block { + u8 iv[8]; + u16 sec_block_len; + /* followed by sec_block_len bytes of security block */ +} __attribute__ ((packed)); + + +/* ACK-Security-Block - unicast TCP */ +struct iapp_ack_security_block { + u8 iv[8]; + u8 new_ap_ack_authenticator[48]; +} __attribute__ ((packed)); + + +struct iapp_data { + struct hostapd_data *hapd; + u16 identifier; /* next IAPP identifier */ + struct in_addr own, multicast; + int udp_sock; + int packet_sock; +}; + + +static void iapp_send_add(struct iapp_data *iapp, u8 *macaddr, u16 seq_num) +{ + char buf[128]; + struct iapp_hdr *hdr; + struct iapp_add_notify *add; + struct sockaddr_in addr; + + /* Send IAPP ADD-notify to remove possible association from other APs + */ + + hdr = (struct iapp_hdr *) buf; + hdr->version = IAPP_VERSION; + hdr->command = IAPP_CMD_ADD_notify; + hdr->identifier = host_to_be16(iapp->identifier++); + hdr->length = host_to_be16(sizeof(*hdr) + sizeof(*add)); + + add = (struct iapp_add_notify *) (hdr + 1); + add->addr_len = ETH_ALEN; + add->reserved = 0; + memcpy(add->mac_addr, macaddr, ETH_ALEN); + + add->seq_num = host_to_be16(seq_num); + + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = iapp->multicast.s_addr; + addr.sin_port = htons(IAPP_UDP_PORT); + if (sendto(iapp->udp_sock, buf, (char *) (add + 1) - buf, 0, + (struct sockaddr *) &addr, sizeof(addr)) < 0) + perror("sendto[IAPP-ADD]"); +} + + +static void iapp_send_layer2_update(struct iapp_data *iapp, u8 *addr) +{ + struct iapp_layer2_update msg; + + /* Send Level 2 Update Frame to update forwarding tables in layer 2 + * bridge devices */ + + /* 802.2 Type 1 Logical Link Control (LLC) Exchange Identifier (XID) + * Update response frame; IEEE Std 802.2-1998, 5.4.1.2.1 */ + + memset(msg.da, 0xff, ETH_ALEN); + memcpy(msg.sa, addr, ETH_ALEN); + msg.len = host_to_be16(6); + msg.dsap = 0; /* NULL DSAP address */ + msg.ssap = 0x01; /* NULL SSAP address, CR Bit: Response */ + msg.control = 0xaf; /* XID response lsb.1111F101. + * F=0 (no poll command; unsolicited frame) */ + msg.xid_info[0] = 0x81; /* XID format identifier */ + msg.xid_info[1] = 1; /* LLC types/classes: Type 1 LLC */ + msg.xid_info[2] = 1 << 1; /* XID sender's receive window size (RW) + * FIX: what is correct RW with 802.11? */ + + if (send(iapp->packet_sock, &msg, sizeof(msg), 0) < 0) + perror("send[L2 Update]"); +} + + +void iapp_new_station(struct iapp_data *iapp, struct sta_info *sta) +{ + struct ieee80211_mgmt *assoc; + u16 seq; + + if (iapp == NULL) + return; + + assoc = sta->last_assoc_req; + seq = assoc ? WLAN_GET_SEQ_SEQ(le_to_host16(assoc->seq_ctrl)) : 0; + + /* IAPP-ADD.request(MAC Address, Sequence Number, Timeout) */ + hostapd_logger(iapp->hapd, sta->addr, HOSTAPD_MODULE_IAPP, + HOSTAPD_LEVEL_DEBUG, "IAPP-ADD.request(seq=%d)", seq); + iapp_send_layer2_update(iapp, sta->addr); + iapp_send_add(iapp, sta->addr, seq); + + if (assoc && WLAN_FC_GET_STYPE(le_to_host16(assoc->frame_control)) == + WLAN_FC_STYPE_REASSOC_REQ) { + /* IAPP-MOVE.request(MAC Address, Sequence Number, Old AP, + * Context Block, Timeout) + */ + /* TODO: Send IAPP-MOVE to the old AP; Map Old AP BSSID to + * IP address */ + } +} + + +static void iapp_process_add_notify(struct iapp_data *iapp, + struct sockaddr_in *from, + struct iapp_hdr *hdr, int len) +{ + struct iapp_add_notify *add = (struct iapp_add_notify *) (hdr + 1); + struct sta_info *sta; + + if (len != sizeof(*add)) { + printf("Invalid IAPP-ADD packet length %d (expected %lu)\n", + len, (unsigned long) sizeof(*add)); + return; + } + + sta = ap_get_sta(iapp->hapd, add->mac_addr); + + /* IAPP-ADD.indication(MAC Address, Sequence Number) */ + hostapd_logger(iapp->hapd, add->mac_addr, HOSTAPD_MODULE_IAPP, + HOSTAPD_LEVEL_INFO, + "Received IAPP ADD-notify (seq# %d) from %s:%d%s", + be_to_host16(add->seq_num), + inet_ntoa(from->sin_addr), ntohs(from->sin_port), + sta ? "" : " (STA not found)"); + + if (!sta) + return; + + /* TODO: could use seq_num to try to determine whether last association + * to this AP is newer than the one advertised in IAPP-ADD. Although, + * this is not really a reliable verification. */ + + hostapd_logger(iapp->hapd, add->mac_addr, HOSTAPD_MODULE_IAPP, + HOSTAPD_LEVEL_DEBUG, + "Removing STA due to IAPP ADD-notify"); + sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC | WLAN_STA_AUTHORIZED); + eloop_cancel_timeout(ap_handle_timer, iapp->hapd, sta); + eloop_register_timeout(0, 0, ap_handle_timer, iapp->hapd, sta); + sta->timeout_next = STA_REMOVE; +} + + +static void iapp_receive_udp(int sock, void *eloop_ctx, void *sock_ctx) +{ + struct iapp_data *iapp = eloop_ctx; + int len, hlen; + unsigned char buf[128]; + struct sockaddr_in from; + socklen_t fromlen; + struct iapp_hdr *hdr; + + /* Handle incoming IAPP frames (over UDP/IP) */ + + fromlen = sizeof(from); + len = recvfrom(iapp->udp_sock, buf, sizeof(buf), 0, + (struct sockaddr *) &from, &fromlen); + if (len < 0) + perror("recvfrom"); + + if (from.sin_addr.s_addr == iapp->own.s_addr) + return; /* ignore own IAPP messages */ + + hostapd_logger(iapp->hapd, NULL, HOSTAPD_MODULE_IAPP, + HOSTAPD_LEVEL_DEBUG, + "Received %d byte IAPP frame from %s%s\n", + len, inet_ntoa(from.sin_addr), + len < sizeof(*hdr) ? " (too short)" : ""); + + if (len < sizeof(*hdr)) + return; + + hdr = (struct iapp_hdr *) buf; + hlen = be_to_host16(hdr->length); + hostapd_logger(iapp->hapd, NULL, HOSTAPD_MODULE_IAPP, + HOSTAPD_LEVEL_DEBUG, + "RX: version=%d command=%d id=%d len=%d\n", + hdr->version, hdr->command, + be_to_host16(hdr->identifier), hlen); + if (hdr->version != IAPP_VERSION) { + printf("Dropping IAPP frame with unknown version %d\n", + hdr->version); + return; + } + if (hlen > len) { + printf("Underflow IAPP frame (hlen=%d len=%d)\n", hlen, len); + return; + } + if (hlen < len) { + printf("Ignoring %d extra bytes from IAPP frame\n", + len - hlen); + len = hlen; + } + + switch (hdr->command) { + case IAPP_CMD_ADD_notify: + iapp_process_add_notify(iapp, &from, hdr, hlen - sizeof(*hdr)); + break; + case IAPP_CMD_MOVE_notify: + /* TODO: MOVE is using TCP; so move this to TCP handler once it + * is implemented.. */ + /* IAPP-MOVE.indication(MAC Address, New BSSID, + * Sequence Number, AP Address, Context Block) */ + /* TODO: process */ + break; + default: + printf("Unknown IAPP command %d\n", hdr->command); + break; + } +} + + +struct iapp_data * iapp_init(struct hostapd_data *hapd, const char *iface) +{ + struct ifreq ifr; + struct sockaddr_ll addr; + int ifindex; + struct sockaddr_in *paddr, uaddr; + struct iapp_data *iapp; + struct ip_mreqn mreq; + + iapp = malloc(sizeof(*iapp)); + if (iapp == NULL) + return NULL; + memset(iapp, 0, sizeof(*iapp)); + iapp->hapd = hapd; + iapp->udp_sock = iapp->packet_sock = -1; + + /* TODO: + * open socket for sending and receiving IAPP frames over TCP + */ + + iapp->udp_sock = socket(PF_INET, SOCK_DGRAM, 0); + if (iapp->udp_sock < 0) { + perror("socket[PF_INET,SOCK_DGRAM]"); + iapp_deinit(iapp); + return NULL; + } + + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, iface, sizeof(ifr.ifr_name)); + if (ioctl(iapp->udp_sock, SIOCGIFINDEX, &ifr) != 0) { + perror("ioctl(SIOCGIFINDEX)"); + iapp_deinit(iapp); + return NULL; + } + ifindex = ifr.ifr_ifindex; + + if (ioctl(iapp->udp_sock, SIOCGIFADDR, &ifr) != 0) { + perror("ioctl(SIOCGIFADDR)"); + iapp_deinit(iapp); + return NULL; + } + paddr = (struct sockaddr_in *) &ifr.ifr_addr; + if (paddr->sin_family != AF_INET) { + printf("Invalid address family %i (SIOCGIFADDR)\n", + paddr->sin_family); + iapp_deinit(iapp); + return NULL; + } + iapp->own.s_addr = paddr->sin_addr.s_addr; + + if (ioctl(iapp->udp_sock, SIOCGIFBRDADDR, &ifr) != 0) { + perror("ioctl(SIOCGIFBRDADDR)"); + iapp_deinit(iapp); + return NULL; + } + paddr = (struct sockaddr_in *) &ifr.ifr_addr; + if (paddr->sin_family != AF_INET) { + printf("Invalid address family %i (SIOCGIFBRDADDR)\n", + paddr->sin_family); + iapp_deinit(iapp); + return NULL; + } + inet_aton(IAPP_MULTICAST, &iapp->multicast); + + memset(&uaddr, 0, sizeof(uaddr)); + uaddr.sin_family = AF_INET; + uaddr.sin_port = htons(IAPP_UDP_PORT); + if (bind(iapp->udp_sock, (struct sockaddr *) &uaddr, + sizeof(uaddr)) < 0) { + perror("bind[UDP]"); + iapp_deinit(iapp); + return NULL; + } + + memset(&mreq, 0, sizeof(mreq)); + mreq.imr_multiaddr = iapp->multicast; + mreq.imr_address.s_addr = INADDR_ANY; + mreq.imr_ifindex = 0; + if (setsockopt(iapp->udp_sock, SOL_IP, IP_ADD_MEMBERSHIP, &mreq, + sizeof(mreq)) < 0) { + perror("setsockopt[UDP,IP_ADD_MEMBERSHIP]"); + return NULL; + } + + iapp->packet_sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); + if (iapp->packet_sock < 0) { + perror("socket[PF_PACKET,SOCK_RAW]"); + return NULL; + } + + memset(&addr, 0, sizeof(addr)); + addr.sll_family = AF_PACKET; + addr.sll_ifindex = ifindex; + if (bind(iapp->packet_sock, (struct sockaddr *) &addr, + sizeof(addr)) < 0) { + perror("bind[PACKET]"); + return NULL; + } + + if (eloop_register_read_sock(iapp->udp_sock, iapp_receive_udp, + iapp, NULL)) { + printf("Could not register read socket for IAPP.\n"); + return NULL; + } + + printf("IEEE 802.11F (IAPP) using interface %s\n", iface); + + /* TODO: For levels 2 and 3: send RADIUS Initiate-Request, receive + * RADIUS Initiate-Accept or Initiate-Reject. IAPP port should actually + * be openned only after receiving Initiate-Accept. If Initiate-Reject + * is received, IAPP is not started. */ + + return iapp; +} + + +void iapp_deinit(struct iapp_data *iapp) +{ + struct ip_mreqn mreq; + + if (iapp == NULL) + return; + + if (iapp->udp_sock >= 0) { + memset(&mreq, 0, sizeof(mreq)); + mreq.imr_multiaddr = iapp->multicast; + mreq.imr_address.s_addr = INADDR_ANY; + mreq.imr_ifindex = 0; + if (setsockopt(iapp->udp_sock, SOL_IP, IP_DROP_MEMBERSHIP, + &mreq, sizeof(mreq)) < 0) { + perror("setsockopt[UDP,IP_DEL_MEMBERSHIP]"); + } + + eloop_unregister_read_sock(iapp->udp_sock); + close(iapp->udp_sock); + } + if (iapp->packet_sock >= 0) { + eloop_unregister_read_sock(iapp->packet_sock); + close(iapp->packet_sock); + } + free(iapp); +} diff --git a/contrib/hostapd-0.4.9/iapp.h b/contrib/hostapd-0.4.9/iapp.h new file mode 100644 index 0000000000..d6e8f6570e --- /dev/null +++ b/contrib/hostapd-0.4.9/iapp.h @@ -0,0 +1,31 @@ +#ifndef IAPP_H +#define IAPP_H + +struct iapp_data; + +#ifdef CONFIG_IAPP + +void iapp_new_station(struct iapp_data *iapp, struct sta_info *sta); +struct iapp_data * iapp_init(struct hostapd_data *hapd, const char *iface); +void iapp_deinit(struct iapp_data *iapp); + +#else /* CONFIG_IAPP */ + +static inline void iapp_new_station(struct iapp_data *iapp, + struct sta_info *sta) +{ +} + +static inline struct iapp_data * iapp_init(struct hostapd_data *hapd, + const char *iface) +{ + return NULL; +} + +static inline void iapp_deinit(struct iapp_data *iapp) +{ +} + +#endif /* CONFIG_IAPP */ + +#endif /* IAPP_H */ diff --git a/contrib/hostapd-0.4.9/ieee802_11.c b/contrib/hostapd-0.4.9/ieee802_11.c new file mode 100644 index 0000000000..d5ad58cbb5 --- /dev/null +++ b/contrib/hostapd-0.4.9/ieee802_11.c @@ -0,0 +1,1220 @@ +/* + * Host AP (software wireless LAN access point) user space daemon for + * Host AP kernel driver / IEEE 802.11 Management + * Copyright (c) 2002-2005, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "eloop.h" +#include "hostapd.h" +#include "ieee802_11.h" +#include "radius.h" +#include "radius_client.h" +#include "ieee802_11_auth.h" +#include "sta_info.h" +#include "eapol_sm.h" +#include "rc4.h" +#include "ieee802_1x.h" +#include "wpa.h" +#include "accounting.h" +#include "driver.h" +#include "hostap_common.h" + + +static u8 * hostapd_eid_supp_rates(hostapd *hapd, u8 *eid) +{ + u8 *pos = eid; + + *pos++ = WLAN_EID_SUPP_RATES; + *pos++ = 4; /* len */ + *pos++ = 0x82; /* 1 Mbps, base set */ + *pos++ = 0x84; /* 2 Mbps, base set */ + *pos++ = 0x0b; /* 5.5 Mbps */ + *pos++ = 0x16; /* 11 Mbps */ + + return pos; +} + + +static u16 hostapd_own_capab_info(hostapd *hapd) +{ + int capab = WLAN_CAPABILITY_ESS; + if (hapd->conf->wpa || + (hapd->conf->ieee802_1x && + (hapd->conf->default_wep_key_len || + hapd->conf->individual_wep_key_len))) + capab |= WLAN_CAPABILITY_PRIVACY; + return capab; +} + + +static const u8 WPA_OUI_TYPE[] = { 0x00, 0x50, 0xf2, 1 }; + +ParseRes ieee802_11_parse_elems(struct hostapd_data *hapd, u8 *start, + size_t len, + struct ieee802_11_elems *elems, + int show_errors) +{ + size_t left = len; + u8 *pos = start; + int unknown = 0; + + memset(elems, 0, sizeof(*elems)); + + while (left >= 2) { + u8 id, elen; + + id = *pos++; + elen = *pos++; + left -= 2; + + if (elen > left) { + if (show_errors) { + HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, + "IEEE 802.11 element parse " + "failed (id=%d elen=%d " + "left=%lu)\n", + id, elen, (unsigned long) left); + if (HOSTAPD_DEBUG_COND(HOSTAPD_DEBUG_MINIMAL)) + hostapd_hexdump("IEs", start, len); + } + return ParseFailed; + } + + switch (id) { + case WLAN_EID_SSID: + elems->ssid = pos; + elems->ssid_len = elen; + break; + case WLAN_EID_SUPP_RATES: + elems->supp_rates = pos; + elems->supp_rates_len = elen; + break; + case WLAN_EID_FH_PARAMS: + elems->fh_params = pos; + elems->fh_params_len = elen; + break; + case WLAN_EID_DS_PARAMS: + elems->ds_params = pos; + elems->ds_params_len = elen; + break; + case WLAN_EID_CF_PARAMS: + elems->cf_params = pos; + elems->cf_params_len = elen; + break; + case WLAN_EID_TIM: + elems->tim = pos; + elems->tim_len = elen; + break; + case WLAN_EID_IBSS_PARAMS: + elems->ibss_params = pos; + elems->ibss_params_len = elen; + break; + case WLAN_EID_CHALLENGE: + elems->challenge = pos; + elems->challenge_len = elen; + break; + case WLAN_EID_GENERIC: + if (elen > 4 && memcmp(pos, WPA_OUI_TYPE, 4) == 0) { + elems->wpa_ie = pos; + elems->wpa_ie_len = elen; + } else if (show_errors) { + HOSTAPD_DEBUG(HOSTAPD_DEBUG_EXCESSIVE, + "IEEE 802.11 element parse " + "ignored unknown generic element" + " (id=%d elen=%d OUI:type=" + "%02x-%02x-%02x:%d)\n", + id, elen, + elen >= 1 ? pos[0] : 0, + elen >= 2 ? pos[1] : 0, + elen >= 3 ? pos[2] : 0, + elen >= 4 ? pos[3] : 0); + unknown++; + } else { + unknown++; + } + break; + case WLAN_EID_RSN: + elems->rsn_ie = pos; + elems->rsn_ie_len = elen; + break; + default: + unknown++; + if (!show_errors) + break; + HOSTAPD_DEBUG(HOSTAPD_DEBUG_EXCESSIVE, + "IEEE 802.11 element parse ignored " + "unknown element (id=%d elen=%d)\n", + id, elen); + break; + } + + left -= elen; + pos += elen; + } + + if (left) + return ParseFailed; + + return unknown ? ParseUnknown : ParseOK; +} + + +static void ieee802_11_print_ssid(const u8 *ssid, u8 len) +{ + int i; + for (i = 0; i < len; i++) { + if (ssid[i] >= 32 && ssid[i] < 127) + printf("%c", ssid[i]); + else + printf("<%02x>", ssid[i]); + } +} + + +static void ieee802_11_sta_authenticate(void *eloop_ctx, void *timeout_ctx) +{ + hostapd *hapd = eloop_ctx; + struct ieee80211_mgmt mgmt; + + if (hapd->assoc_ap_state == WAIT_BEACON) + hapd->assoc_ap_state = AUTHENTICATE; + if (hapd->assoc_ap_state != AUTHENTICATE) + return; + + printf("Authenticate with AP " MACSTR " SSID=", + MAC2STR(hapd->conf->assoc_ap_addr)); + ieee802_11_print_ssid((u8 *) hapd->assoc_ap_ssid, + hapd->assoc_ap_ssid_len); + printf(" (as station)\n"); + + memset(&mgmt, 0, sizeof(mgmt)); + mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, + WLAN_FC_STYPE_AUTH); + /* Request TX callback */ + mgmt.frame_control |= host_to_le16(BIT(1)); + memcpy(mgmt.da, hapd->conf->assoc_ap_addr, ETH_ALEN); + memcpy(mgmt.sa, hapd->own_addr, ETH_ALEN); + memcpy(mgmt.bssid, hapd->conf->assoc_ap_addr, ETH_ALEN); + mgmt.u.auth.auth_alg = host_to_le16(WLAN_AUTH_OPEN); + mgmt.u.auth.auth_transaction = host_to_le16(1); + mgmt.u.auth.status_code = host_to_le16(0); + if (hostapd_send_mgmt_frame(hapd, &mgmt, IEEE80211_HDRLEN + + sizeof(mgmt.u.auth), 0) < 0) + perror("ieee802_11_sta_authenticate: send"); + + /* Try to authenticate again, if this attempt fails or times out. */ + eloop_register_timeout(5, 0, ieee802_11_sta_authenticate, hapd, NULL); +} + + +static void ieee802_11_sta_associate(void *eloop_ctx, void *timeout_ctx) +{ + hostapd *hapd = eloop_ctx; + u8 buf[256]; + struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *) buf; + u8 *p; + + if (hapd->assoc_ap_state == AUTHENTICATE) + hapd->assoc_ap_state = ASSOCIATE; + if (hapd->assoc_ap_state != ASSOCIATE) + return; + + printf("Associate with AP " MACSTR " SSID=", + MAC2STR(hapd->conf->assoc_ap_addr)); + ieee802_11_print_ssid((u8 *) hapd->assoc_ap_ssid, + hapd->assoc_ap_ssid_len); + printf(" (as station)\n"); + + memset(mgmt, 0, sizeof(*mgmt)); + mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, + WLAN_FC_STYPE_ASSOC_REQ); + /* Request TX callback */ + mgmt->frame_control |= host_to_le16(BIT(1)); + memcpy(mgmt->da, hapd->conf->assoc_ap_addr, ETH_ALEN); + memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN); + memcpy(mgmt->bssid, hapd->conf->assoc_ap_addr, ETH_ALEN); + mgmt->u.assoc_req.capab_info = host_to_le16(0); + mgmt->u.assoc_req.listen_interval = host_to_le16(1); + p = &mgmt->u.assoc_req.variable[0]; + + *p++ = WLAN_EID_SSID; + *p++ = hapd->assoc_ap_ssid_len; + memcpy(p, hapd->assoc_ap_ssid, hapd->assoc_ap_ssid_len); + p += hapd->assoc_ap_ssid_len; + + p = hostapd_eid_supp_rates(hapd, p); + + if (hostapd_send_mgmt_frame(hapd, mgmt, p - (u8 *) mgmt, 0) < 0) + perror("ieee802_11_sta_associate: send"); + + /* Try to authenticate again, if this attempt fails or times out. */ + eloop_register_timeout(5, 0, ieee802_11_sta_associate, hapd, NULL); +} + + +static u16 auth_shared_key(hostapd *hapd, struct sta_info *sta, + u16 auth_transaction, u8 *challenge, int iswep) +{ + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "authentication (shared key, transaction %d)", + auth_transaction); + + if (auth_transaction == 1) { + if (!sta->challenge) { + /* Generate a pseudo-random challenge */ + u8 key[8]; + time_t now; + int r; + sta->challenge = malloc(WLAN_AUTH_CHALLENGE_LEN); + if (!sta->challenge) + return WLAN_STATUS_UNSPECIFIED_FAILURE; + memset(sta->challenge, 0, WLAN_AUTH_CHALLENGE_LEN); + + now = time(NULL); + r = random(); + memcpy(key, &now, 4); + memcpy(key + 4, &r, 4); + rc4(sta->challenge, WLAN_AUTH_CHALLENGE_LEN, + key, sizeof(key)); + } + return 0; + } + + if (auth_transaction != 3) + return WLAN_STATUS_UNSPECIFIED_FAILURE; + + /* Transaction 3 */ + if (!iswep || !sta->challenge || !challenge || + memcmp(sta->challenge, challenge, WLAN_AUTH_CHALLENGE_LEN)) { + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_INFO, + "shared key authentication - invalid " + "challenge-response"); + return WLAN_STATUS_CHALLENGE_FAIL; + } + + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "authentication OK (shared key)"); +#ifdef IEEE80211_REQUIRE_AUTH_ACK + /* Station will be marked authenticated if it ACKs the + * authentication reply. */ +#else + sta->flags |= WLAN_STA_AUTH; + wpa_sm_event(hapd, sta, WPA_AUTH); +#endif + free(sta->challenge); + sta->challenge = NULL; + + return 0; +} + + +static void send_auth_reply(hostapd *hapd, struct ieee80211_mgmt *mgmt, + u16 auth_alg, u16 auth_transaction, u16 resp, + u8 *challenge) +{ + u8 buf[IEEE80211_HDRLEN + sizeof(mgmt->u.auth) + 2 + + WLAN_AUTH_CHALLENGE_LEN]; + struct ieee80211_mgmt *reply; + size_t rlen; + + memset(buf, 0, sizeof(buf)); + reply = (struct ieee80211_mgmt *) buf; + reply->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, + WLAN_FC_STYPE_AUTH); + /* Request TX callback */ + reply->frame_control |= host_to_le16(BIT(1)); + memcpy(reply->da, mgmt->sa, ETH_ALEN); + memcpy(reply->sa, hapd->own_addr, ETH_ALEN); + memcpy(reply->bssid, mgmt->bssid, ETH_ALEN); + + reply->u.auth.auth_alg = host_to_le16(auth_alg); + reply->u.auth.auth_transaction = host_to_le16(auth_transaction); + reply->u.auth.status_code = host_to_le16(resp); + rlen = IEEE80211_HDRLEN + sizeof(reply->u.auth); + if (auth_alg == WLAN_AUTH_SHARED_KEY && auth_transaction == 2 && + challenge) { + u8 *p = reply->u.auth.variable; + *p++ = WLAN_EID_CHALLENGE; + *p++ = WLAN_AUTH_CHALLENGE_LEN; + memcpy(p, challenge, WLAN_AUTH_CHALLENGE_LEN); + rlen += 2 + WLAN_AUTH_CHALLENGE_LEN; + } else + challenge = NULL; + + HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, + "authentication reply: STA=" MACSTR " auth_alg=%d " + "auth_transaction=%d resp=%d%s\n", + MAC2STR(mgmt->sa), auth_alg, auth_transaction, + resp, challenge ? " challenge" : ""); + if (hostapd_send_mgmt_frame(hapd, reply, rlen, 0) < 0) + perror("send_auth_reply: send"); +} + + +static void handle_auth(hostapd *hapd, struct ieee80211_mgmt *mgmt, size_t len) +{ + u16 auth_alg, auth_transaction, status_code; + u16 resp = WLAN_STATUS_SUCCESS; + struct sta_info *sta = NULL; + int res; + u16 fc; + u8 *challenge = NULL; + u32 session_timeout, acct_interim_interval; + + if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth)) { + printf("handle_auth - too short payload (len=%lu)\n", + (unsigned long) len); + return; + } + + auth_alg = le_to_host16(mgmt->u.auth.auth_alg); + auth_transaction = le_to_host16(mgmt->u.auth.auth_transaction); + status_code = le_to_host16(mgmt->u.auth.status_code); + fc = le_to_host16(mgmt->frame_control); + + if (len >= IEEE80211_HDRLEN + sizeof(mgmt->u.auth) + + 2 + WLAN_AUTH_CHALLENGE_LEN && + mgmt->u.auth.variable[0] == WLAN_EID_CHALLENGE && + mgmt->u.auth.variable[1] == WLAN_AUTH_CHALLENGE_LEN) + challenge = &mgmt->u.auth.variable[2]; + + HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, + "authentication: STA=" MACSTR " auth_alg=%d " + "auth_transaction=%d status_code=%d wep=%d%s\n", + MAC2STR(mgmt->sa), auth_alg, auth_transaction, + status_code, !!(fc & WLAN_FC_ISWEP), + challenge ? " challenge" : ""); + + if (hapd->assoc_ap_state == AUTHENTICATE && auth_transaction == 2 && + memcmp(mgmt->sa, hapd->conf->assoc_ap_addr, ETH_ALEN) == 0 && + memcmp(mgmt->bssid, hapd->conf->assoc_ap_addr, ETH_ALEN) == 0) { + if (status_code != 0) { + printf("Authentication (as station) with AP " + MACSTR " failed (status_code=%d)\n", + MAC2STR(hapd->conf->assoc_ap_addr), + status_code); + return; + } + printf("Authenticated (as station) with AP " MACSTR "\n", + MAC2STR(hapd->conf->assoc_ap_addr)); + ieee802_11_sta_associate(hapd, NULL); + return; + } + + if (hapd->tkip_countermeasures) { + resp = WLAN_REASON_MICHAEL_MIC_FAILURE; + goto fail; + } + + if (!(((hapd->conf->auth_algs & HOSTAPD_AUTH_OPEN) && + auth_alg == WLAN_AUTH_OPEN) || + ((hapd->conf->auth_algs & HOSTAPD_AUTH_SHARED_KEY) && + auth_alg == WLAN_AUTH_SHARED_KEY))) { + printf("Unsupported authentication algorithm (%d)\n", + auth_alg); + resp = WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG; + goto fail; + } + + if (!(auth_transaction == 1 || + (auth_alg == WLAN_AUTH_SHARED_KEY && auth_transaction == 3))) { + printf("Unknown authentication transaction number (%d)\n", + auth_transaction); + resp = WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION; + goto fail; + } + + if (memcmp(mgmt->sa, hapd->own_addr, ETH_ALEN) == 0) { + printf("Station " MACSTR " not allowed to authenticate.\n", + MAC2STR(mgmt->sa)); + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + + res = hostapd_allowed_address(hapd, mgmt->sa, (u8 *) mgmt, len, + &session_timeout, + &acct_interim_interval); + if (res == HOSTAPD_ACL_REJECT) { + printf("Station " MACSTR " not allowed to authenticate.\n", + MAC2STR(mgmt->sa)); + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + if (res == HOSTAPD_ACL_PENDING) { + HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "Authentication frame " + "from " MACSTR " waiting for an external " + "authentication\n", MAC2STR(mgmt->sa)); + /* Authentication code will re-send the authentication frame + * after it has received (and cached) information from the + * external source. */ + return; + } + + sta = ap_sta_add(hapd, mgmt->sa); + if (!sta) { + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + sta->flags &= ~WLAN_STA_PREAUTH; + ieee802_1x_notify_pre_auth(sta->eapol_sm, 0); + + if (hapd->conf->radius->acct_interim_interval == 0 && + acct_interim_interval) + sta->acct_interim_interval = acct_interim_interval; + if (res == HOSTAPD_ACL_ACCEPT_TIMEOUT) + ap_sta_session_timeout(hapd, sta, session_timeout); + else + ap_sta_no_session_timeout(hapd, sta); + + switch (auth_alg) { + case WLAN_AUTH_OPEN: + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "authentication OK (open system)"); +#ifdef IEEE80211_REQUIRE_AUTH_ACK + /* Station will be marked authenticated if it ACKs the + * authentication reply. */ +#else + sta->flags |= WLAN_STA_AUTH; + wpa_sm_event(hapd, sta, WPA_AUTH); +#endif + break; + case WLAN_AUTH_SHARED_KEY: + resp = auth_shared_key(hapd, sta, auth_transaction, challenge, + fc & WLAN_FC_ISWEP); + break; + } + + fail: + send_auth_reply(hapd, mgmt, auth_alg, auth_transaction + 1, resp, + sta ? sta->challenge : NULL); +} + + +static void handle_assoc(hostapd *hapd, struct ieee80211_mgmt *mgmt, + size_t len, int reassoc) +{ + u16 capab_info, listen_interval; + u16 resp = WLAN_STATUS_SUCCESS; + u8 *pos, *wpa_ie; + size_t wpa_ie_len; + int send_deauth = 0, send_len, left, i; + struct sta_info *sta; + struct ieee802_11_elems elems; + + if (len < IEEE80211_HDRLEN + (reassoc ? sizeof(mgmt->u.reassoc_req) : + sizeof(mgmt->u.assoc_req))) { + printf("handle_assoc(reassoc=%d) - too short payload (len=%lu)" + "\n", reassoc, (unsigned long) len); + return; + } + + if (reassoc) { + capab_info = le_to_host16(mgmt->u.reassoc_req.capab_info); + listen_interval = le_to_host16( + mgmt->u.reassoc_req.listen_interval); + HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, + "reassociation request: STA=" MACSTR + " capab_info=0x%02x " + "listen_interval=%d current_ap=" MACSTR "\n", + MAC2STR(mgmt->sa), capab_info, listen_interval, + MAC2STR(mgmt->u.reassoc_req.current_ap)); + left = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.reassoc_req)); + pos = mgmt->u.reassoc_req.variable; + } else { + capab_info = le_to_host16(mgmt->u.assoc_req.capab_info); + listen_interval = le_to_host16( + mgmt->u.assoc_req.listen_interval); + HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, + "association request: STA=" MACSTR + " capab_info=0x%02x listen_interval=%d\n", + MAC2STR(mgmt->sa), capab_info, listen_interval); + left = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.assoc_req)); + pos = mgmt->u.assoc_req.variable; + } + + sta = ap_get_sta(hapd, mgmt->sa); + if (sta == NULL || (sta->flags & WLAN_STA_AUTH) == 0) { + printf("STA " MACSTR " trying to associate before " + "authentication\n", MAC2STR(mgmt->sa)); + if (sta) { + printf(" sta: addr=" MACSTR " aid=%d flags=0x%04x\n", + MAC2STR(sta->addr), sta->aid, sta->flags); + } + send_deauth = 1; + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + + if (hapd->tkip_countermeasures) { + resp = WLAN_REASON_MICHAEL_MIC_FAILURE; + goto fail; + } + + sta->capability = capab_info; + + /* followed by SSID and Supported rates */ + if (ieee802_11_parse_elems(hapd, pos, left, &elems, 1) == ParseFailed + || !elems.ssid) { + printf("STA " MACSTR " sent invalid association request\n", + MAC2STR(sta->addr)); + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + + if (elems.ssid_len != hapd->conf->ssid_len || + memcmp(elems.ssid, hapd->conf->ssid, elems.ssid_len) != 0) { + printf("Station " MACSTR " tried to associate with " + "unknown SSID '", MAC2STR(sta->addr)); + ieee802_11_print_ssid(elems.ssid, elems.ssid_len); + printf("'\n"); + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + + if (elems.supp_rates) { + if (elems.supp_rates_len > sizeof(sta->supported_rates)) { + printf("STA " MACSTR ": Invalid supported rates " + "element length %d\n", MAC2STR(sta->addr), + elems.supp_rates_len); + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + + memset(sta->supported_rates, 0, sizeof(sta->supported_rates)); + memcpy(sta->supported_rates, elems.supp_rates, + elems.supp_rates_len); + + sta->tx_supp_rates = 0; + for (i = 0; i < elems.supp_rates_len; i++) { + if ((sta->supported_rates[i] & 0x7f) == 2) + sta->tx_supp_rates |= WLAN_RATE_1M; + if ((sta->supported_rates[i] & 0x7f) == 4) + sta->tx_supp_rates |= WLAN_RATE_2M; + if ((sta->supported_rates[i] & 0x7f) == 11) + sta->tx_supp_rates |= WLAN_RATE_5M5; + if ((sta->supported_rates[i] & 0x7f) == 22) + sta->tx_supp_rates |= WLAN_RATE_11M; + } + } else + sta->tx_supp_rates = 0xff; + + if ((hapd->conf->wpa & HOSTAPD_WPA_VERSION_WPA2) && elems.rsn_ie) { + wpa_ie = elems.rsn_ie; + wpa_ie_len = elems.rsn_ie_len; + } else if ((hapd->conf->wpa & HOSTAPD_WPA_VERSION_WPA) && + elems.wpa_ie) { + wpa_ie = elems.wpa_ie; + wpa_ie_len = elems.wpa_ie_len; + } else { + wpa_ie = NULL; + wpa_ie_len = 0; + } + if (hapd->conf->wpa && wpa_ie == NULL) { + printf("STA " MACSTR ": No WPA/RSN IE in association " + "request\n", MAC2STR(sta->addr)); + resp = WLAN_STATUS_INVALID_IE; + goto fail; + } + + if (hapd->conf->wpa) { + int res; + wpa_ie -= 2; + wpa_ie_len += 2; + res = wpa_validate_wpa_ie(hapd, sta, wpa_ie, wpa_ie_len, + elems.rsn_ie ? + HOSTAPD_WPA_VERSION_WPA2 : + HOSTAPD_WPA_VERSION_WPA); + if (res == WPA_INVALID_GROUP) + resp = WLAN_STATUS_GROUP_CIPHER_NOT_VALID; + else if (res == WPA_INVALID_PAIRWISE) + resp = WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID; + else if (res == WPA_INVALID_AKMP) + resp = WLAN_STATUS_AKMP_NOT_VALID; + else if (res != WPA_IE_OK) + resp = WLAN_STATUS_INVALID_IE; + if (resp != WLAN_STATUS_SUCCESS) + goto fail; + free(sta->wpa_ie); + sta->wpa_ie = malloc(wpa_ie_len); + if (sta->wpa_ie == NULL) { + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + sta->wpa_ie_len = wpa_ie_len; + memcpy(sta->wpa_ie, wpa_ie, wpa_ie_len); + } + + /* get a unique AID */ + if (sta->aid > 0) { + HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, + " old AID %d\n", sta->aid); + } else { + for (sta->aid = 1; sta->aid <= MAX_AID_TABLE_SIZE; sta->aid++) + if (hapd->sta_aid[sta->aid - 1] == NULL) + break; + if (sta->aid > MAX_AID_TABLE_SIZE) { + sta->aid = 0; + resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA; + printf(" no room for more AIDs\n"); + goto fail; + } else { + hapd->sta_aid[sta->aid - 1] = sta; + HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, + " new AID %d\n", sta->aid); + } + } + + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "association OK (aid %d)", sta->aid); + /* Station will be marked associated, after it acknowledges AssocResp + */ + + if (sta->last_assoc_req) + free(sta->last_assoc_req); + sta->last_assoc_req = (struct ieee80211_mgmt *) malloc(len); + if (sta->last_assoc_req) + memcpy(sta->last_assoc_req, mgmt, len); + + /* Make sure that the previously registered inactivity timer will not + * remove the STA immediately. */ + sta->timeout_next = STA_NULLFUNC; + + fail: + + /* use the queued buffer for transmission because it is large enough + * and not needed anymore */ + mgmt->frame_control = + IEEE80211_FC(WLAN_FC_TYPE_MGMT, + (send_deauth ? WLAN_FC_STYPE_DEAUTH : + (reassoc ? WLAN_FC_STYPE_REASSOC_RESP : + WLAN_FC_STYPE_ASSOC_RESP))); + memcpy(mgmt->da, mgmt->sa, ETH_ALEN); + memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN); + /* Addr3 = BSSID - already set */ + + send_len = IEEE80211_HDRLEN; + if (send_deauth) { + send_len += sizeof(mgmt->u.deauth); + mgmt->u.deauth.reason_code = host_to_le16(resp); + } else { + u8 *p; + send_len += sizeof(mgmt->u.assoc_resp); + mgmt->u.assoc_resp.capab_info = + host_to_le16(hostapd_own_capab_info(hapd)); + mgmt->u.assoc_resp.status_code = host_to_le16(resp); + mgmt->u.assoc_resp.aid = host_to_le16((sta ? sta->aid : 0) + | BIT(14) | BIT(15)); + /* Supported rates */ + p = hostapd_eid_supp_rates(hapd, mgmt->u.assoc_resp.variable); + send_len += p - mgmt->u.assoc_resp.variable; + + /* Request TX callback */ + mgmt->frame_control |= host_to_le16(BIT(1)); + } + + if (hostapd_send_mgmt_frame(hapd, mgmt, send_len, 0) < 0) + perror("handle_assoc: send"); +} + + +static void handle_assoc_resp(hostapd *hapd, struct ieee80211_mgmt *mgmt, + size_t len) +{ + u16 status_code, aid; + + if (hapd->assoc_ap_state != ASSOCIATE) { + printf("Unexpected association response received from " MACSTR + "\n", MAC2STR(mgmt->sa)); + return; + } + + if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.assoc_resp)) { + printf("handle_assoc_resp - too short payload (len=%lu)\n", + (unsigned long) len); + return; + } + + if (memcmp(mgmt->sa, hapd->conf->assoc_ap_addr, ETH_ALEN) != 0 || + memcmp(mgmt->bssid, hapd->conf->assoc_ap_addr, ETH_ALEN) != 0) { + printf("Received association response from unexpected address " + "(SA=" MACSTR " BSSID=" MACSTR "\n", + MAC2STR(mgmt->sa), MAC2STR(mgmt->bssid)); + return; + } + + status_code = le_to_host16(mgmt->u.assoc_resp.status_code); + aid = le_to_host16(mgmt->u.assoc_resp.aid); + aid &= ~(BIT(14) | BIT(15)); + + if (status_code != 0) { + printf("Association (as station) with AP " MACSTR " failed " + "(status_code=%d)\n", + MAC2STR(hapd->conf->assoc_ap_addr), status_code); + /* Try to authenticate again */ + hapd->assoc_ap_state = AUTHENTICATE; + eloop_register_timeout(5, 0, ieee802_11_sta_authenticate, + hapd, NULL); + } + + printf("Associated (as station) with AP " MACSTR " (aid=%d)\n", + MAC2STR(hapd->conf->assoc_ap_addr), aid); + hapd->assoc_ap_aid = aid; + hapd->assoc_ap_state = ASSOCIATED; + + if (hostapd_set_assoc_ap(hapd, hapd->conf->assoc_ap_addr)) { + printf("Could not set associated AP address to kernel " + "driver.\n"); + } +} + + +static void handle_disassoc(hostapd *hapd, struct ieee80211_mgmt *mgmt, + size_t len) +{ + struct sta_info *sta; + + if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.disassoc)) { + printf("handle_disassoc - too short payload (len=%lu)\n", + (unsigned long) len); + return; + } + + HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, + "disassocation: STA=" MACSTR " reason_code=%d\n", + MAC2STR(mgmt->sa), + le_to_host16(mgmt->u.disassoc.reason_code)); + + if (hapd->assoc_ap_state != DO_NOT_ASSOC && + memcmp(mgmt->sa, hapd->conf->assoc_ap_addr, ETH_ALEN) == 0) { + printf("Assoc AP " MACSTR " sent disassociation " + "(reason_code=%d) - try to authenticate\n", + MAC2STR(hapd->conf->assoc_ap_addr), + le_to_host16(mgmt->u.disassoc.reason_code)); + hapd->assoc_ap_state = AUTHENTICATE; + ieee802_11_sta_authenticate(hapd, NULL); + eloop_register_timeout(0, 500000, ieee802_11_sta_authenticate, + hapd, NULL); + return; + } + + sta = ap_get_sta(hapd, mgmt->sa); + if (sta == NULL) { + printf("Station " MACSTR " trying to disassociate, but it " + "is not associated.\n", MAC2STR(mgmt->sa)); + return; + } + + sta->flags &= ~WLAN_STA_ASSOC; + wpa_sm_event(hapd, sta, WPA_DISASSOC); + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_INFO, "disassociated"); + sta->acct_terminate_cause = RADIUS_ACCT_TERMINATE_CAUSE_USER_REQUEST; + ieee802_1x_set_port_enabled(hapd, sta, 0); + /* Stop Accounting and IEEE 802.1X sessions, but leave the STA + * authenticated. */ + accounting_sta_stop(hapd, sta); + ieee802_1x_free_station(sta); + hostapd_sta_remove(hapd, sta->addr); + + if (sta->timeout_next == STA_NULLFUNC || + sta->timeout_next == STA_DISASSOC) { + sta->timeout_next = STA_DEAUTH; + eloop_cancel_timeout(ap_handle_timer, hapd, sta); + eloop_register_timeout(AP_DEAUTH_DELAY, 0, ap_handle_timer, + hapd, sta); + } +} + + +static void handle_deauth(hostapd *hapd, struct ieee80211_mgmt *mgmt, + size_t len) +{ + struct sta_info *sta; + + if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.deauth)) { + printf("handle_deauth - too short payload (len=%lu)\n", + (unsigned long) len); + return; + } + + HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, + "deauthentication: STA=" MACSTR " reason_code=%d\n", + MAC2STR(mgmt->sa), + le_to_host16(mgmt->u.deauth.reason_code)); + + if (hapd->assoc_ap_state != DO_NOT_ASSOC && + memcmp(mgmt->sa, hapd->conf->assoc_ap_addr, ETH_ALEN) == 0) { + printf("Assoc AP " MACSTR " sent deauthentication " + "(reason_code=%d) - try to authenticate\n", + MAC2STR(hapd->conf->assoc_ap_addr), + le_to_host16(mgmt->u.deauth.reason_code)); + hapd->assoc_ap_state = AUTHENTICATE; + eloop_register_timeout(0, 500000, ieee802_11_sta_authenticate, + hapd, NULL); + return; + } + + sta = ap_get_sta(hapd, mgmt->sa); + if (sta == NULL) { + printf("Station " MACSTR " trying to deauthenticate, but it " + "is not authenticated.\n", MAC2STR(mgmt->sa)); + return; + } + + sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC); + wpa_sm_event(hapd, sta, WPA_DEAUTH); + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, "deauthenticated"); + sta->acct_terminate_cause = RADIUS_ACCT_TERMINATE_CAUSE_USER_REQUEST; + ieee802_1x_set_port_enabled(hapd, sta, 0); + ap_free_sta(hapd, sta); +} + + +static void handle_beacon(hostapd *hapd, struct ieee80211_mgmt *mgmt, + size_t len) +{ + struct ieee802_11_elems elems; + + if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.beacon)) { + printf("handle_beacon - too short payload (len=%lu)\n", + (unsigned long) len); + return; + } + + (void) ieee802_11_parse_elems(hapd, mgmt->u.beacon.variable, + len - (IEEE80211_HDRLEN + + sizeof(mgmt->u.beacon)), &elems, + 0); + + if (hapd->assoc_ap_state == WAIT_BEACON && + memcmp(mgmt->sa, hapd->conf->assoc_ap_addr, ETH_ALEN) == 0) { + if (elems.ssid && elems.ssid_len <= 32) { + memcpy(hapd->assoc_ap_ssid, elems.ssid, + elems.ssid_len); + hapd->assoc_ap_ssid[elems.ssid_len] = '\0'; + hapd->assoc_ap_ssid_len = elems.ssid_len; + } + ieee802_11_sta_authenticate(hapd, NULL); + } + + if (!HOSTAPD_DEBUG_COND(HOSTAPD_DEBUG_EXCESSIVE)) + return; + + printf("Beacon from " MACSTR, MAC2STR(mgmt->sa)); + if (elems.ssid) { + printf(" SSID='"); + ieee802_11_print_ssid(elems.ssid, elems.ssid_len); + printf("'"); + } + if (elems.ds_params && elems.ds_params_len == 1) + printf(" CHAN=%d", elems.ds_params[0]); + printf("\n"); +} + + +void ieee802_11_mgmt(struct hostapd_data *hapd, u8 *buf, size_t len, u16 stype) +{ + struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *) buf; + + if (stype == WLAN_FC_STYPE_BEACON) { + HOSTAPD_DEBUG(HOSTAPD_DEBUG_EXCESSIVE, "mgmt::beacon\n"); + handle_beacon(hapd, mgmt, len); + return; + } + + if (memcmp(mgmt->bssid, hapd->own_addr, ETH_ALEN) != 0 && + (hapd->assoc_ap_state == DO_NOT_ASSOC || + memcmp(mgmt->bssid, hapd->conf->assoc_ap_addr, ETH_ALEN) != 0)) { + printf("MGMT: BSSID=" MACSTR " not our address\n", + MAC2STR(mgmt->bssid)); + return; + } + + + if (stype == WLAN_FC_STYPE_PROBE_REQ) { + printf("mgmt::probe_req\n"); + return; + } + + if (memcmp(mgmt->da, hapd->own_addr, ETH_ALEN) != 0) { + printf("MGMT: DA=" MACSTR " not our address\n", + MAC2STR(mgmt->da)); + return; + } + + switch (stype) { + case WLAN_FC_STYPE_AUTH: + HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "mgmt::auth\n"); + handle_auth(hapd, mgmt, len); + break; + case WLAN_FC_STYPE_ASSOC_REQ: + HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "mgmt::assoc_req\n"); + handle_assoc(hapd, mgmt, len, 0); + break; + case WLAN_FC_STYPE_ASSOC_RESP: + HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "mgmt::assoc_resp\n"); + handle_assoc_resp(hapd, mgmt, len); + break; + case WLAN_FC_STYPE_REASSOC_REQ: + HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "mgmt::reassoc_req\n"); + handle_assoc(hapd, mgmt, len, 1); + break; + case WLAN_FC_STYPE_DISASSOC: + HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "mgmt::disassoc\n"); + handle_disassoc(hapd, mgmt, len); + break; + case WLAN_FC_STYPE_DEAUTH: + HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "mgmt::deauth\n"); + handle_deauth(hapd, mgmt, len); + break; + default: + printf("unknown mgmt frame subtype %d\n", stype); + break; + } +} + + +static void handle_auth_cb(hostapd *hapd, struct ieee80211_mgmt *mgmt, + size_t len, int ok) +{ + u16 auth_alg, auth_transaction, status_code; + struct sta_info *sta; + + if (!ok) { + hostapd_logger(hapd, mgmt->da, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_NOTICE, + "did not acknowledge authentication response"); + return; + } + + if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth)) { + printf("handle_auth_cb - too short payload (len=%lu)\n", + (unsigned long) len); + return; + } + + auth_alg = le_to_host16(mgmt->u.auth.auth_alg); + auth_transaction = le_to_host16(mgmt->u.auth.auth_transaction); + status_code = le_to_host16(mgmt->u.auth.status_code); + + sta = ap_get_sta(hapd, mgmt->da); + if (!sta) { + printf("handle_auth_cb: STA " MACSTR " not found\n", + MAC2STR(mgmt->da)); + return; + } + + if (status_code == WLAN_STATUS_SUCCESS && + ((auth_alg == WLAN_AUTH_OPEN && auth_transaction == 2) || + (auth_alg == WLAN_AUTH_SHARED_KEY && auth_transaction == 4))) { + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_INFO, "authenticated"); + sta->flags |= WLAN_STA_AUTH; + } +} + + +static void handle_assoc_cb(hostapd *hapd, struct ieee80211_mgmt *mgmt, + size_t len, int reassoc, int ok) +{ + u16 status; + struct sta_info *sta; + int new_assoc = 1; + + if (!ok) { + hostapd_logger(hapd, mgmt->da, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "did not acknowledge association response"); + return; + } + + if (len < IEEE80211_HDRLEN + (reassoc ? sizeof(mgmt->u.reassoc_req) : + sizeof(mgmt->u.assoc_req))) { + printf("handle_assoc_cb(reassoc=%d) - too short payload " + "(len=%lu)\n", reassoc, (unsigned long) len); + return; + } + + if (reassoc) + status = le_to_host16(mgmt->u.reassoc_resp.status_code); + else + status = le_to_host16(mgmt->u.assoc_resp.status_code); + + sta = ap_get_sta(hapd, mgmt->da); + if (!sta) { + printf("handle_assoc_cb: STA " MACSTR " not found\n", + MAC2STR(mgmt->da)); + return; + } + + if (status != WLAN_STATUS_SUCCESS) + goto fail; + + /* Stop previous accounting session, if one is started, and allocate + * new session id for the new session. */ + accounting_sta_stop(hapd, sta); + accounting_sta_get_id(hapd, sta); + + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_INFO, + "associated (aid %d, accounting session %08X-%08X)", + sta->aid, sta->acct_session_id_hi, + sta->acct_session_id_lo); + + if (sta->flags & WLAN_STA_ASSOC) + new_assoc = 0; + sta->flags |= WLAN_STA_ASSOC; + + if (hostapd_sta_add(hapd, sta->addr, sta->aid, sta->capability, + sta->tx_supp_rates)) { + printf("Could not add station to kernel driver.\n"); + } + + wpa_sm_event(hapd, sta, WPA_ASSOC); + hostapd_new_assoc_sta(hapd, sta, !new_assoc); + + ieee802_1x_notify_port_enabled(sta->eapol_sm, 1); + + fail: + /* Copy of the association request is not needed anymore */ + if (sta->last_assoc_req) { + free(sta->last_assoc_req); + sta->last_assoc_req = NULL; + } +} + + +void ieee802_11_mgmt_cb(struct hostapd_data *hapd, u8 *buf, size_t len, + u16 stype, int ok) +{ + struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *) buf; + + switch (stype) { + case WLAN_FC_STYPE_AUTH: + HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "mgmt::auth cb\n"); + handle_auth_cb(hapd, mgmt, len, ok); + break; + case WLAN_FC_STYPE_ASSOC_RESP: + HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, + "mgmt::assoc_resp cb\n"); + handle_assoc_cb(hapd, mgmt, len, 0, ok); + break; + case WLAN_FC_STYPE_REASSOC_RESP: + HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, + "mgmt::reassoc_resp cb\n"); + handle_assoc_cb(hapd, mgmt, len, 1, ok); + break; + default: + printf("unknown mgmt cb frame subtype %d\n", stype); + break; + } +} + + +static void ieee80211_tkip_countermeasures_stop(void *eloop_ctx, + void *timeout_ctx) +{ + struct hostapd_data *hapd = eloop_ctx; + hapd->tkip_countermeasures = 0; + hostapd_set_countermeasures(hapd, 0); + hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_INFO, "TKIP countermeasures ended"); +} + + +static void ieee80211_tkip_countermeasures_start(struct hostapd_data *hapd) +{ + struct sta_info *sta; + + hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_INFO, "TKIP countermeasures initiated"); + + if (hapd->wpa_auth) + hapd->wpa_auth->dot11RSNATKIPCounterMeasuresInvoked++; + hapd->tkip_countermeasures = 1; + hostapd_set_countermeasures(hapd, 1); + wpa_gtk_rekey(hapd); + eloop_cancel_timeout(ieee80211_tkip_countermeasures_stop, hapd, NULL); + eloop_register_timeout(60, 0, ieee80211_tkip_countermeasures_stop, + hapd, NULL); + for (sta = hapd->sta_list; sta != NULL; sta = sta->next) { + hostapd_sta_deauth(hapd, sta->addr, + WLAN_REASON_MICHAEL_MIC_FAILURE); + sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC | + WLAN_STA_AUTHORIZED); + hostapd_sta_remove(hapd, sta->addr); + } +} + + +void ieee80211_michael_mic_failure(struct hostapd_data *hapd, u8 *addr, + int local) +{ + time_t now; + + if (addr && local) { + struct sta_info *sta = ap_get_sta(hapd, addr); + if (sta != NULL) { + sta->dot11RSNAStatsTKIPLocalMICFailures++; + hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_INFO, + "Michael MIC failure detected in " + "received frame"); + } else { + HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, + "MLME-MICHAELMICFAILURE.indication " + "for not associated STA (" MACSTR + ") ignored\n", MAC2STR(addr)); + return; + } + } + + time(&now); + if (now > hapd->michael_mic_failure + 60) { + hapd->michael_mic_failures = 1; + } else { + hapd->michael_mic_failures++; + if (hapd->michael_mic_failures > 1) + ieee80211_tkip_countermeasures_start(hapd); + } + hapd->michael_mic_failure = now; +} + + +int ieee802_11_get_mib(struct hostapd_data *hapd, char *buf, size_t buflen) +{ + /* TODO */ + return 0; +} + + +int ieee802_11_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta, + char *buf, size_t buflen) +{ + /* TODO */ + return 0; +} diff --git a/contrib/hostapd-0.4.9/ieee802_11.h b/contrib/hostapd-0.4.9/ieee802_11.h new file mode 100644 index 0000000000..39cc34207b --- /dev/null +++ b/contrib/hostapd-0.4.9/ieee802_11.h @@ -0,0 +1,99 @@ +#ifndef IEEE802_11_H +#define IEEE802_11_H + + +struct ieee80211_mgmt { + u16 frame_control; + u16 duration; + u8 da[6]; + u8 sa[6]; + u8 bssid[6]; + u16 seq_ctrl; + union { + struct { + u16 auth_alg; + u16 auth_transaction; + u16 status_code; + /* possibly followed by Challenge text */ + u8 variable[0]; + } __attribute__ ((packed)) auth; + struct { + u16 reason_code; + } __attribute__ ((packed)) deauth; + struct { + u16 capab_info; + u16 listen_interval; + /* followed by SSID and Supported rates */ + u8 variable[0]; + } __attribute__ ((packed)) assoc_req; + struct { + u16 capab_info; + u16 status_code; + u16 aid; + /* followed by Supported rates */ + u8 variable[0]; + } __attribute__ ((packed)) assoc_resp, reassoc_resp; + struct { + u16 capab_info; + u16 listen_interval; + u8 current_ap[6]; + /* followed by SSID and Supported rates */ + u8 variable[0]; + } __attribute__ ((packed)) reassoc_req; + struct { + u16 reason_code; + } __attribute__ ((packed)) disassoc; + struct { + u8 timestamp[8]; + u16 beacon_int; + u16 capab_info; + /* followed by some of SSID, Supported rates, + * FH Params, DS Params, CF Params, IBSS Params, TIM */ + u8 variable[0]; + } __attribute__ ((packed)) beacon; + } u; +} __attribute__ ((packed)); + + +/* Parsed Information Elements */ +struct ieee802_11_elems { + u8 *ssid; + u8 ssid_len; + u8 *supp_rates; + u8 supp_rates_len; + u8 *fh_params; + u8 fh_params_len; + u8 *ds_params; + u8 ds_params_len; + u8 *cf_params; + u8 cf_params_len; + u8 *tim; + u8 tim_len; + u8 *ibss_params; + u8 ibss_params_len; + u8 *challenge; + u8 challenge_len; + u8 *wpa_ie; + u8 wpa_ie_len; + u8 *rsn_ie; + u8 rsn_ie_len; +}; + +typedef enum { ParseOK = 0, ParseUnknown = 1, ParseFailed = -1 } ParseRes; + + +void ieee802_11_mgmt(struct hostapd_data *hapd, u8 *buf, size_t len, + u16 stype); +void ieee802_11_mgmt_cb(struct hostapd_data *hapd, u8 *buf, size_t len, + u16 stype, int ok); +ParseRes ieee802_11_parse_elems(struct hostapd_data *hapd, u8 *start, + size_t len, + struct ieee802_11_elems *elems, + int show_errors); +void ieee80211_michael_mic_failure(struct hostapd_data *hapd, u8 *addr, + int local); +int ieee802_11_get_mib(struct hostapd_data *hapd, char *buf, size_t buflen); +int ieee802_11_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta, + char *buf, size_t buflen); + +#endif /* IEEE802_11_H */ diff --git a/contrib/hostapd-0.4.9/ieee802_11_auth.c b/contrib/hostapd-0.4.9/ieee802_11_auth.c new file mode 100644 index 0000000000..296c64017a --- /dev/null +++ b/contrib/hostapd-0.4.9/ieee802_11_auth.c @@ -0,0 +1,458 @@ +/* + * Host AP (software wireless LAN access point) user space daemon for + * Host AP kernel driver / IEEE 802.11 authentication (ACL) + * Copyright (c) 2003-2005, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "hostapd.h" +#include "ieee802_11.h" +#include "ieee802_11_auth.h" +#include "radius.h" +#include "radius_client.h" +#include "eloop.h" +#include "hostap_common.h" + +#define RADIUS_ACL_TIMEOUT 30 + + +struct hostapd_cached_radius_acl { + time_t timestamp; + macaddr addr; + int accepted; /* HOSTAPD_ACL_* */ + struct hostapd_cached_radius_acl *next; + u32 session_timeout; + u32 acct_interim_interval; +}; + + +struct hostapd_acl_query_data { + time_t timestamp; + u8 radius_id; + macaddr addr; + u8 *auth_msg; /* IEEE 802.11 authentication frame from station */ + size_t auth_msg_len; + struct hostapd_acl_query_data *next; +}; + + +static void hostapd_acl_cache_free(struct hostapd_cached_radius_acl *acl_cache) +{ + struct hostapd_cached_radius_acl *prev; + + while (acl_cache) { + prev = acl_cache; + acl_cache = acl_cache->next; + free(prev); + } +} + + +static int hostapd_acl_cache_get(struct hostapd_data *hapd, u8 *addr, + u32 *session_timeout, + u32 *acct_interim_interval) +{ + struct hostapd_cached_radius_acl *entry; + time_t now; + + time(&now); + entry = hapd->acl_cache; + + while (entry) { + if (memcmp(entry->addr, addr, ETH_ALEN) == 0) { + if (now - entry->timestamp > RADIUS_ACL_TIMEOUT) + return -1; /* entry has expired */ + if (entry->accepted == HOSTAPD_ACL_ACCEPT_TIMEOUT) + *session_timeout = entry->session_timeout; + *acct_interim_interval = entry->acct_interim_interval; + return entry->accepted; + } + + entry = entry->next; + } + + return -1; +} + + +static void hostapd_acl_query_free(struct hostapd_acl_query_data *query) +{ + if (query == NULL) + return; + free(query->auth_msg); + free(query); +} + + +static int hostapd_radius_acl_query(hostapd *hapd, u8 *addr, + struct hostapd_acl_query_data *query) +{ + struct radius_msg *msg; + char buf[128]; + + query->radius_id = radius_client_get_id(hapd->radius); + msg = radius_msg_new(RADIUS_CODE_ACCESS_REQUEST, query->radius_id); + if (msg == NULL) + return -1; + + radius_msg_make_authenticator(msg, addr, ETH_ALEN); + + snprintf(buf, sizeof(buf), RADIUS_ADDR_FORMAT, MAC2STR(addr)); + if (!radius_msg_add_attr(msg, RADIUS_ATTR_USER_NAME, (u8 *) buf, + strlen(buf))) { + printf("Could not add User-Name\n"); + goto fail; + } + + if (!radius_msg_add_attr_user_password( + msg, (u8 *) buf, strlen(buf), + hapd->conf->radius->auth_server->shared_secret, + hapd->conf->radius->auth_server->shared_secret_len)) { + printf("Could not add User-Password\n"); + goto fail; + } + + if (hapd->conf->own_ip_addr.af == AF_INET && + !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IP_ADDRESS, + (u8 *) &hapd->conf->own_ip_addr.u.v4, 4)) { + printf("Could not add NAS-IP-Address\n"); + goto fail; + } + +#ifdef CONFIG_IPV6 + if (hapd->conf->own_ip_addr.af == AF_INET6 && + !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IPV6_ADDRESS, + (u8 *) &hapd->conf->own_ip_addr.u.v6, 16)) { + printf("Could not add NAS-IPv6-Address\n"); + goto fail; + } +#endif /* CONFIG_IPV6 */ + + if (hapd->conf->nas_identifier && + !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IDENTIFIER, + (u8 *) hapd->conf->nas_identifier, + strlen(hapd->conf->nas_identifier))) { + printf("Could not add NAS-Identifier\n"); + goto fail; + } + + snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT ":%s", + MAC2STR(hapd->own_addr), hapd->conf->ssid); + if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLED_STATION_ID, + (u8 *) buf, strlen(buf))) { + printf("Could not add Called-Station-Id\n"); + goto fail; + } + + snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT, + MAC2STR(addr)); + if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLING_STATION_ID, + (u8 *) buf, strlen(buf))) { + printf("Could not add Calling-Station-Id\n"); + goto fail; + } + + if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_NAS_PORT_TYPE, + RADIUS_NAS_PORT_TYPE_IEEE_802_11)) { + printf("Could not add NAS-Port-Type\n"); + goto fail; + } + + snprintf(buf, sizeof(buf), "CONNECT 11Mbps 802.11b"); + if (!radius_msg_add_attr(msg, RADIUS_ATTR_CONNECT_INFO, + (u8 *) buf, strlen(buf))) { + printf("Could not add Connect-Info\n"); + goto fail; + } + + radius_client_send(hapd->radius, msg, RADIUS_AUTH, addr); + return 0; + + fail: + radius_msg_free(msg); + free(msg); + return -1; +} + + +int hostapd_allowed_address(hostapd *hapd, u8 *addr, u8 *msg, size_t len, + u32 *session_timeout, u32 *acct_interim_interval) +{ + *session_timeout = 0; + *acct_interim_interval = 0; + + if (hostapd_maclist_found(hapd->conf->accept_mac, + hapd->conf->num_accept_mac, addr)) + return HOSTAPD_ACL_ACCEPT; + + if (hostapd_maclist_found(hapd->conf->deny_mac, + hapd->conf->num_deny_mac, addr)) + return HOSTAPD_ACL_REJECT; + + if (hapd->conf->macaddr_acl == ACCEPT_UNLESS_DENIED) + return HOSTAPD_ACL_ACCEPT; + if (hapd->conf->macaddr_acl == DENY_UNLESS_ACCEPTED) + return HOSTAPD_ACL_REJECT; + + if (hapd->conf->macaddr_acl == USE_EXTERNAL_RADIUS_AUTH) { + struct hostapd_acl_query_data *query; + + /* Check whether ACL cache has an entry for this station */ + int res = hostapd_acl_cache_get(hapd, addr, session_timeout, + acct_interim_interval); + if (res == HOSTAPD_ACL_ACCEPT || + res == HOSTAPD_ACL_ACCEPT_TIMEOUT) + return res; + if (res == HOSTAPD_ACL_REJECT) + return HOSTAPD_ACL_REJECT; + + query = hapd->acl_queries; + while (query) { + if (memcmp(query->addr, addr, ETH_ALEN) == 0) { + /* pending query in RADIUS retransmit queue; + * do not generate a new one */ + return HOSTAPD_ACL_PENDING; + } + query = query->next; + } + + if (!hapd->conf->radius->auth_server) + return HOSTAPD_ACL_REJECT; + + /* No entry in the cache - query external RADIUS server */ + query = malloc(sizeof(*query)); + if (query == NULL) { + printf("malloc for query data failed\n"); + return HOSTAPD_ACL_REJECT; + } + memset(query, 0, sizeof(*query)); + time(&query->timestamp); + memcpy(query->addr, addr, ETH_ALEN); + if (hostapd_radius_acl_query(hapd, addr, query)) { + printf("Failed to send Access-Request for ACL " + "query.\n"); + hostapd_acl_query_free(query); + return HOSTAPD_ACL_REJECT; + } + + query->auth_msg = malloc(len); + if (query->auth_msg == NULL) { + printf("Failed to allocate memory for auth frame.\n"); + hostapd_acl_query_free(query); + return HOSTAPD_ACL_REJECT; + } + memcpy(query->auth_msg, msg, len); + query->auth_msg_len = len; + query->next = hapd->acl_queries; + hapd->acl_queries = query; + + /* Queued data will be processed in hostapd_acl_recv_radius() + * when RADIUS server replies to the sent Access-Request. */ + return HOSTAPD_ACL_PENDING; + } + + return HOSTAPD_ACL_REJECT; +} + + +static void hostapd_acl_expire_cache(hostapd *hapd, time_t now) +{ + struct hostapd_cached_radius_acl *prev, *entry, *tmp; + + prev = NULL; + entry = hapd->acl_cache; + + while (entry) { + if (now - entry->timestamp > RADIUS_ACL_TIMEOUT) { + HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, + "Cached ACL entry for " MACSTR + " has expired.\n", MAC2STR(entry->addr)); + if (prev) + prev->next = entry->next; + else + hapd->acl_cache = entry->next; + + tmp = entry; + entry = entry->next; + free(tmp); + continue; + } + + prev = entry; + entry = entry->next; + } +} + + +static void hostapd_acl_expire_queries(hostapd *hapd, time_t now) +{ + struct hostapd_acl_query_data *prev, *entry, *tmp; + + prev = NULL; + entry = hapd->acl_queries; + + while (entry) { + if (now - entry->timestamp > RADIUS_ACL_TIMEOUT) { + HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, + "ACL query for " MACSTR + " has expired.\n", MAC2STR(entry->addr)); + if (prev) + prev->next = entry->next; + else + hapd->acl_queries = entry->next; + + tmp = entry; + entry = entry->next; + hostapd_acl_query_free(tmp); + continue; + } + + prev = entry; + entry = entry->next; + } +} + + +static void hostapd_acl_expire(void *eloop_ctx, void *timeout_ctx) +{ + hostapd *hapd = eloop_ctx; + time_t now; + + time(&now); + hostapd_acl_expire_cache(hapd, now); + hostapd_acl_expire_queries(hapd, now); + + eloop_register_timeout(10, 0, hostapd_acl_expire, hapd, NULL); +} + + +/* Return 0 if RADIUS message was a reply to ACL query (and was processed here) + * or -1 if not. */ +static RadiusRxResult +hostapd_acl_recv_radius(struct radius_msg *msg, struct radius_msg *req, + u8 *shared_secret, size_t shared_secret_len, + void *data) +{ + struct hostapd_data *hapd = data; + struct hostapd_acl_query_data *query, *prev; + struct hostapd_cached_radius_acl *cache; + + query = hapd->acl_queries; + prev = NULL; + while (query) { + if (query->radius_id == msg->hdr->identifier) + break; + prev = query; + query = query->next; + } + if (query == NULL) + return RADIUS_RX_UNKNOWN; + + HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "Found matching Access-Request " + "for RADIUS message (id=%d)\n", query->radius_id); + + if (radius_msg_verify(msg, shared_secret, shared_secret_len, req, 0)) { + printf("Incoming RADIUS packet did not have correct " + "authenticator - dropped\n"); + return RADIUS_RX_INVALID_AUTHENTICATOR; + } + + if (msg->hdr->code != RADIUS_CODE_ACCESS_ACCEPT && + msg->hdr->code != RADIUS_CODE_ACCESS_REJECT) { + printf("Unknown RADIUS message code %d to ACL query\n", + msg->hdr->code); + return RADIUS_RX_UNKNOWN; + } + + /* Insert Accept/Reject info into ACL cache */ + cache = malloc(sizeof(*cache)); + if (cache == NULL) { + printf("Failed to add ACL cache entry\n"); + goto done; + } + memset(cache, 0, sizeof(*cache)); + time(&cache->timestamp); + memcpy(cache->addr, query->addr, sizeof(cache->addr)); + if (msg->hdr->code == RADIUS_CODE_ACCESS_ACCEPT) { + if (radius_msg_get_attr_int32(msg, RADIUS_ATTR_SESSION_TIMEOUT, + &cache->session_timeout) == 0) + cache->accepted = HOSTAPD_ACL_ACCEPT_TIMEOUT; + else + cache->accepted = HOSTAPD_ACL_ACCEPT; + + if (radius_msg_get_attr_int32( + msg, RADIUS_ATTR_ACCT_INTERIM_INTERVAL, + &cache->acct_interim_interval) == 0 && + cache->acct_interim_interval < 60) { + HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "Ignored too " + "small Acct-Interim-Interval %d for " + "STA " MACSTR "\n", + cache->acct_interim_interval, + MAC2STR(query->addr)); + cache->acct_interim_interval = 0; + } + } else + cache->accepted = HOSTAPD_ACL_REJECT; + cache->next = hapd->acl_cache; + hapd->acl_cache = cache; + + /* Re-send original authentication frame for 802.11 processing */ + HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "Re-sending authentication frame " + "after successful RADIUS ACL query\n"); + ieee802_11_mgmt(hapd, query->auth_msg, query->auth_msg_len, + WLAN_FC_STYPE_AUTH); + + done: + if (prev == NULL) + hapd->acl_queries = query->next; + else + prev->next = query->next; + + hostapd_acl_query_free(query); + + return RADIUS_RX_PROCESSED; +} + + +int hostapd_acl_init(hostapd *hapd) +{ + if (radius_client_register(hapd->radius, RADIUS_AUTH, + hostapd_acl_recv_radius, hapd)) + return -1; + + eloop_register_timeout(10, 0, hostapd_acl_expire, hapd, NULL); + + return 0; +} + + +void hostapd_acl_deinit(hostapd *hapd) +{ + struct hostapd_acl_query_data *query, *prev; + + hostapd_acl_cache_free(hapd->acl_cache); + + query = hapd->acl_queries; + while (query) { + prev = query; + query = query->next; + hostapd_acl_query_free(prev); + } +} diff --git a/contrib/hostapd-0.4.9/ieee802_11_auth.h b/contrib/hostapd-0.4.9/ieee802_11_auth.h new file mode 100644 index 0000000000..90adc8f174 --- /dev/null +++ b/contrib/hostapd-0.4.9/ieee802_11_auth.h @@ -0,0 +1,16 @@ +#ifndef IEEE802_11_AUTH_H +#define IEEE802_11_AUTH_H + +enum { + HOSTAPD_ACL_REJECT = 0, + HOSTAPD_ACL_ACCEPT = 1, + HOSTAPD_ACL_PENDING = 2, + HOSTAPD_ACL_ACCEPT_TIMEOUT = 3 +}; + +int hostapd_allowed_address(hostapd *hapd, u8 *addr, u8 *msg, size_t len, + u32 *session_timeout, u32 *acct_interim_interval); +int hostapd_acl_init(hostapd *hapd); +void hostapd_acl_deinit(hostapd *hapd); + +#endif /* IEEE802_11_AUTH_H */ diff --git a/contrib/hostapd-0.4.9/ieee802_1x.c b/contrib/hostapd-0.4.9/ieee802_1x.c new file mode 100644 index 0000000000..b5792e3ef0 --- /dev/null +++ b/contrib/hostapd-0.4.9/ieee802_1x.c @@ -0,0 +1,1788 @@ +/* + * Host AP (software wireless LAN access point) user space daemon for + * Host AP kernel driver / IEEE 802.1X Authenticator + * Copyright (c) 2002-2006, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "hostapd.h" +#include "ieee802_1x.h" +#include "accounting.h" +#include "radius.h" +#include "radius_client.h" +#include "eapol_sm.h" +#include "md5.h" +#include "rc4.h" +#include "eloop.h" +#include "sta_info.h" +#include "wpa.h" +#include "driver.h" +#include "eap.h" + + +static void ieee802_1x_new_auth_session(struct hostapd_data *hapd, + struct sta_info *sta); + + +static void ieee802_1x_send(hostapd *hapd, struct sta_info *sta, u8 type, + u8 *data, size_t datalen) +{ + u8 *buf; + struct ieee802_1x_hdr *xhdr; + size_t len; + int encrypt = 0; + + len = sizeof(*xhdr) + datalen; + buf = malloc(len); + if (buf == NULL) { + printf("malloc() failed for ieee802_1x_send(len=%lu)\n", + (unsigned long) len); + return; + } + + memset(buf, 0, len); +#if 0 + /* TODO: + * According to IEEE 802.1aa/D4 EAPOL-Key should be sent before any + * remaining EAP frames, if possible. This would allow rest of the + * frames to be encrypted. This code could be used to request + * encryption from the kernel driver. */ + if (sta->eapol_sm && + sta->eapol_sm->be_auth.state == BE_AUTH_SUCCESS && + sta->eapol_sm->keyTxEnabled) + encrypt = 1; +#endif + + xhdr = (struct ieee802_1x_hdr *) buf; + xhdr->version = hapd->conf->eapol_version; + xhdr->type = type; + xhdr->length = htons(datalen); + + if (datalen > 0 && data != NULL) + memcpy(xhdr + 1, data, datalen); + + if (sta->wpa_sm && sta->wpa_sm->pairwise_set) + encrypt = 1; + if (sta->flags & WLAN_STA_PREAUTH) { + rsn_preauth_send(hapd, sta, buf, len); + } else { + hostapd_send_eapol(hapd, sta->addr, buf, len, encrypt); + } + + free(buf); +} + + +void ieee802_1x_set_sta_authorized(hostapd *hapd, struct sta_info *sta, + int authorized) +{ + if (sta->flags & WLAN_STA_PREAUTH) + return; + + if (authorized) { + sta->flags |= WLAN_STA_AUTHORIZED; + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, + HOSTAPD_LEVEL_DEBUG, "authorizing port"); + } else { + sta->flags &= ~WLAN_STA_AUTHORIZED; + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, + HOSTAPD_LEVEL_DEBUG, "unauthorizing port"); + } + + hostapd_set_sta_authorized(hapd, sta->addr, authorized); + if (authorized) + accounting_sta_start(hapd, sta); +} + + +static void ieee802_1x_eap_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct sta_info *sta = eloop_ctx; + struct eapol_state_machine *sm = sta->eapol_sm; + if (sm == NULL) + return; + hostapd_logger(sm->hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, + HOSTAPD_LEVEL_DEBUG, "EAP timeout"); + sm->eapTimeout = TRUE; + eapol_sm_step(sm); +} + + +void ieee802_1x_request_identity(struct hostapd_data *hapd, + struct sta_info *sta) +{ + u8 *buf; + struct eap_hdr *eap; + int tlen; + u8 *pos; + struct eapol_state_machine *sm = sta->eapol_sm; + + if (hapd->conf->eap_server) { + HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, + "IEEE 802.1X: Integrated EAP server in " + "use - do not generate EAP-Request/Identity\n"); + return; + } + + if (sm == NULL || !sm->auth_pae.eapRestart) + return; + + ieee802_1x_new_auth_session(hapd, sta); + + tlen = sizeof(*eap) + 1 + hapd->conf->eap_req_id_text_len; + + buf = malloc(tlen); + if (buf == NULL) { + printf("Could not allocate memory for identity request\n"); + return; + } + + memset(buf, 0, tlen); + + eap = (struct eap_hdr *) buf; + eap->code = EAP_CODE_REQUEST; + eap->identifier = ++sm->currentId; + eap->length = htons(tlen); + pos = (u8 *) (eap + 1); + *pos++ = EAP_TYPE_IDENTITY; + if (hapd->conf->eap_req_id_text) { + memcpy(pos, hapd->conf->eap_req_id_text, + hapd->conf->eap_req_id_text_len); + } + + sm->be_auth.eapReq = TRUE; + free(sm->last_eap_radius); + sm->last_eap_radius = buf; + sm->last_eap_radius_len = tlen; + + eloop_cancel_timeout(ieee802_1x_eap_timeout, sta, NULL); + eloop_register_timeout(30, 0, ieee802_1x_eap_timeout, sta, NULL); + sm->eapTimeout = FALSE; + + HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, + "IEEE 802.1X: Generated EAP Request-Identity for " MACSTR + " (identifier %d, timeout 30)\n", MAC2STR(sta->addr), + eap->identifier); + + sm->auth_pae.eapRestart = FALSE; +} + + +void ieee802_1x_tx_canned_eap(struct hostapd_data *hapd, struct sta_info *sta, + int success) +{ + struct eap_hdr eap; + struct eapol_state_machine *sm = sta->eapol_sm; + + memset(&eap, 0, sizeof(eap)); + + eap.code = success ? EAP_CODE_SUCCESS : EAP_CODE_FAILURE; + eap.identifier = 1; + if (sm && sm->last_eap_radius) { + struct eap_hdr *hdr = (struct eap_hdr *) sm->last_eap_radius; + eap.identifier = hdr->identifier + 1; + } + eap.length = htons(sizeof(eap)); + + HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, + "IEEE 802.1X: Sending canned EAP packet %s to " MACSTR + " (identifier %d)\n", success ? "SUCCESS" : "FAILURE", + MAC2STR(sta->addr), eap.identifier); + ieee802_1x_send(hapd, sta, IEEE802_1X_TYPE_EAP_PACKET, (u8 *) &eap, + sizeof(eap)); + if (sm) + sm->dot1xAuthEapolFramesTx++; +} + + +void ieee802_1x_tx_req(struct hostapd_data *hapd, struct sta_info *sta) +{ + struct eap_hdr *eap; + struct eapol_state_machine *sm = sta->eapol_sm; + u8 *type; + if (sm == NULL) + return; + + if (sm->last_eap_radius == NULL) { + printf("Error: TxReq called for station " MACSTR ", but there " + "is no EAP request from the authentication server\n", + MAC2STR(sm->addr)); + return; + } + + eap = (struct eap_hdr *) sm->last_eap_radius; + HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, + "IEEE 802.1X: Sending EAP Packet to " MACSTR + " (identifier %d)\n", MAC2STR(sm->addr), + eap->identifier); + sm->currentId = eap->identifier; + ieee802_1x_send(hapd, sta, IEEE802_1X_TYPE_EAP_PACKET, + sm->last_eap_radius, sm->last_eap_radius_len); + sm->dot1xAuthEapolFramesTx++; + type = (u8 *) (eap + 1); + if (sm->last_eap_radius_len > sizeof(*eap) && + *type == EAP_TYPE_IDENTITY) + sm->dot1xAuthEapolReqIdFramesTx++; + else + sm->dot1xAuthEapolReqFramesTx++; +} + + +void hostapd_get_ntp_timestamp(u8 *buf) +{ + struct timeval now; + u32 sec, usec; + + /* 64-bit NTP timestamp (time from 1900-01-01 00:00:00) */ + gettimeofday(&now, NULL); + sec = htonl(now.tv_sec + 2208988800U); /* Epoch to 1900 */ + /* Estimate 2^32/10^6 = 4295 - 1/32 - 1/512 */ + usec = now.tv_usec; + usec = htonl(4295 * usec - (usec >> 5) - (usec >> 9)); + memcpy(buf, (u8 *) &sec, 4); + memcpy(buf + 4, (u8 *) &usec, 4); +} + + +static void ieee802_1x_tx_key_one(hostapd *hapd, struct sta_info *sta, + int index, int broadcast, + u8 *key_data, size_t key_len) +{ + u8 *buf, *ekey; + struct ieee802_1x_hdr *hdr; + struct ieee802_1x_eapol_key *key; + size_t len, ekey_len; + struct eapol_state_machine *sm = sta->eapol_sm; + + if (sm == NULL) + return; + + len = sizeof(*key) + key_len; + buf = malloc(sizeof(*hdr) + len); + if (buf == NULL) + return; + + memset(buf, 0, sizeof(*hdr) + len); + hdr = (struct ieee802_1x_hdr *) buf; + key = (struct ieee802_1x_eapol_key *) (hdr + 1); + key->type = EAPOL_KEY_TYPE_RC4; + key->key_length = htons(key_len); + hostapd_get_ntp_timestamp(key->replay_counter); + + if (hostapd_get_rand(key->key_iv, sizeof(key->key_iv))) { + printf("Could not get random numbers\n"); + free(buf); + return; + } + + key->key_index = index | (broadcast ? 0 : BIT(7)); + if (hapd->conf->eapol_key_index_workaround) { + /* According to some information, WinXP Supplicant seems to + * interpret bit7 as an indication whether the key is to be + * activated, so make it possible to enable workaround that + * sets this bit for all keys. */ + key->key_index |= BIT(7); + } + + /* Key is encrypted using "Key-IV + sm->eapol_key_crypt" as the + * RC4-key */ + memcpy((u8 *) (key + 1), key_data, key_len); + ekey_len = sizeof(key->key_iv) + sm->eapol_key_crypt_len; + ekey = malloc(ekey_len); + if (ekey == NULL) { + printf("Could not encrypt key\n"); + free(buf); + return; + } + memcpy(ekey, key->key_iv, sizeof(key->key_iv)); + memcpy(ekey + sizeof(key->key_iv), sm->eapol_key_crypt, + sm->eapol_key_crypt_len); + rc4((u8 *) (key + 1), key_len, ekey, ekey_len); + free(ekey); + + /* This header is needed here for HMAC-MD5, but it will be regenerated + * in ieee802_1x_send() */ + hdr->version = hapd->conf->eapol_version; + hdr->type = IEEE802_1X_TYPE_EAPOL_KEY; + hdr->length = htons(len); + hmac_md5(sm->eapol_key_sign, sm->eapol_key_sign_len, + buf, sizeof(*hdr) + len, + key->key_signature); + + HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, + "IEEE 802.1X: Sending EAPOL-Key to " MACSTR + " (%s index=%d)\n", MAC2STR(sm->addr), + broadcast ? "broadcast" : "unicast", index); + ieee802_1x_send(hapd, sta, IEEE802_1X_TYPE_EAPOL_KEY, (u8 *) key, len); + if (sta->eapol_sm) + sta->eapol_sm->dot1xAuthEapolFramesTx++; + free(buf); +} + + +void ieee802_1x_tx_key(struct hostapd_data *hapd, struct sta_info *sta) +{ + struct eapol_state_machine *sm = sta->eapol_sm; + + if (sm == NULL || !sm->eapol_key_sign || !sm->eapol_key_crypt) + return; + + HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, + "IEEE 802.1X: Sending EAPOL-Key(s) to " MACSTR "\n", + MAC2STR(sta->addr)); + + if (hapd->default_wep_key) + ieee802_1x_tx_key_one(hapd, sta, hapd->default_wep_key_idx, 1, + hapd->default_wep_key, + hapd->conf->default_wep_key_len); + + if (hapd->conf->individual_wep_key_len > 0) { + u8 *ikey; + ikey = malloc(hapd->conf->individual_wep_key_len); + if (ikey == NULL || + hostapd_get_rand(ikey, + hapd->conf->individual_wep_key_len)) { + printf("Could not generate random individual WEP " + "key.\n"); + free(ikey); + return; + } + + if (HOSTAPD_DEBUG_COND(HOSTAPD_DEBUG_MINIMAL)) + hostapd_hexdump("Individual WEP key", + ikey, + hapd->conf->individual_wep_key_len); + + ieee802_1x_tx_key_one(hapd, sta, 0, 0, ikey, + hapd->conf->individual_wep_key_len); + + /* TODO: set encryption in TX callback, i.e., only after STA + * has ACKed EAPOL-Key frame */ + if (hostapd_set_encryption(hapd, "WEP", sta->addr, 0, ikey, + hapd->conf-> + individual_wep_key_len)) { + printf("Could not set individual WEP encryption.\n"); + } + + free(ikey); + } +} + + +static void ieee802_1x_encapsulate_radius(hostapd *hapd, struct sta_info *sta, + u8 *eap, size_t len) +{ + struct radius_msg *msg; + char buf[128]; + struct eapol_state_machine *sm = sta->eapol_sm; + + if (sm == NULL) + return; + + HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, + "Encapsulating EAP message into a RADIUS packet\n"); + + sm->radius_identifier = radius_client_get_id(hapd->radius); + msg = radius_msg_new(RADIUS_CODE_ACCESS_REQUEST, + sm->radius_identifier); + if (msg == NULL) { + printf("Could not create net RADIUS packet\n"); + return; + } + + radius_msg_make_authenticator(msg, (u8 *) sta, sizeof(*sta)); + + if (sm->identity && + !radius_msg_add_attr(msg, RADIUS_ATTR_USER_NAME, + sm->identity, sm->identity_len)) { + printf("Could not add User-Name\n"); + goto fail; + } + + if (hapd->conf->own_ip_addr.af == AF_INET && + !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IP_ADDRESS, + (u8 *) &hapd->conf->own_ip_addr.u.v4, 4)) { + printf("Could not add NAS-IP-Address\n"); + goto fail; + } + +#ifdef CONFIG_IPV6 + if (hapd->conf->own_ip_addr.af == AF_INET6 && + !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IPV6_ADDRESS, + (u8 *) &hapd->conf->own_ip_addr.u.v6, 16)) { + printf("Could not add NAS-IPv6-Address\n"); + goto fail; + } +#endif /* CONFIG_IPV6 */ + + if (hapd->conf->nas_identifier && + !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IDENTIFIER, + (u8 *) hapd->conf->nas_identifier, + strlen(hapd->conf->nas_identifier))) { + printf("Could not add NAS-Identifier\n"); + goto fail; + } + + if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_NAS_PORT, sta->aid)) { + printf("Could not add NAS-Port\n"); + goto fail; + } + + snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT ":%s", + MAC2STR(hapd->own_addr), hapd->conf->ssid); + if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLED_STATION_ID, + (u8 *) buf, strlen(buf))) { + printf("Could not add Called-Station-Id\n"); + goto fail; + } + + snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT, + MAC2STR(sta->addr)); + if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLING_STATION_ID, + (u8 *) buf, strlen(buf))) { + printf("Could not add Calling-Station-Id\n"); + goto fail; + } + + /* TODO: should probably check MTU from driver config; 2304 is max for + * IEEE 802.11, but use 1400 to avoid problems with too large packets + */ + if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_FRAMED_MTU, 1400)) { + printf("Could not add Framed-MTU\n"); + goto fail; + } + + if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_NAS_PORT_TYPE, + RADIUS_NAS_PORT_TYPE_IEEE_802_11)) { + printf("Could not add NAS-Port-Type\n"); + goto fail; + } + + snprintf(buf, sizeof(buf), "CONNECT 11Mbps 802.11b"); + if (!radius_msg_add_attr(msg, RADIUS_ATTR_CONNECT_INFO, + (u8 *) buf, strlen(buf))) { + printf("Could not add Connect-Info\n"); + goto fail; + } + + if (eap && !radius_msg_add_eap(msg, eap, len)) { + printf("Could not add EAP-Message\n"); + goto fail; + } + + /* State attribute must be copied if and only if this packet is + * Access-Request reply to the previous Access-Challenge */ + if (sm->last_recv_radius && sm->last_recv_radius->hdr->code == + RADIUS_CODE_ACCESS_CHALLENGE) { + int res = radius_msg_copy_attr(msg, sm->last_recv_radius, + RADIUS_ATTR_STATE); + if (res < 0) { + printf("Could not copy State attribute from previous " + "Access-Challenge\n"); + goto fail; + } + if (res > 0) { + HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, + " Copied RADIUS State Attribute\n"); + } + } + + radius_client_send(hapd->radius, msg, RADIUS_AUTH, sta->addr); + return; + + fail: + radius_msg_free(msg); + free(msg); +} + + +static char *eap_type_text(u8 type) +{ + switch (type) { + case EAP_TYPE_IDENTITY: return "Identity"; + case EAP_TYPE_NOTIFICATION: return "Notification"; + case EAP_TYPE_NAK: return "Nak"; + case EAP_TYPE_MD5: return "MD5-Challenge"; + case EAP_TYPE_OTP: return "One-Time Password"; + case EAP_TYPE_GTC: return "Generic Token Card"; + case EAP_TYPE_TLS: return "TLS"; + case EAP_TYPE_TTLS: return "TTLS"; + case EAP_TYPE_PEAP: return "PEAP"; + default: return "Unknown"; + } +} + + +static void handle_eap_response(hostapd *hapd, struct sta_info *sta, + struct eap_hdr *eap, u8 *data, size_t len) +{ + u8 type; + struct eapol_state_machine *sm = sta->eapol_sm; + if (sm == NULL) + return; + + if (eap->identifier != sm->currentId) { + hostapd_logger(hapd, sm->addr, HOSTAPD_MODULE_IEEE8021X, + HOSTAPD_LEVEL_DEBUG, + "EAP Identifier of the Response-Identity does " + "not match (was %d, expected %d) - ignored", + eap->identifier, sm->currentId); + return; + } + + if (len < 1) { + printf("handle_eap_response: too short response data\n"); + return; + } + + eloop_cancel_timeout(ieee802_1x_eap_timeout, sta, NULL); + + free(sm->last_eap_supp); + sm->last_eap_supp_len = sizeof(*eap) + len; + sm->last_eap_supp = (u8 *) malloc(sm->last_eap_supp_len); + if (sm->last_eap_supp == NULL) { + printf("Could not alloc memory for last EAP Response\n"); + return; + } + memcpy(sm->last_eap_supp, eap, sizeof(*eap)); + memcpy(sm->last_eap_supp + sizeof(*eap), data, len); + + type = data[0]; + data++; + len--; + + hostapd_logger(hapd, sm->addr, HOSTAPD_MODULE_IEEE8021X, + HOSTAPD_LEVEL_DEBUG, "received EAP packet (code=%d " + "id=%d len=%d) from STA: EAP Response-%s (%d)", + eap->code, eap->identifier, ntohs(eap->length), + eap_type_text(type), type); + + if (type == EAP_TYPE_IDENTITY) { + char *buf, *pos; + int i; + buf = malloc(4 * len + 1); + if (buf) { + pos = buf; + for (i = 0; i < len; i++) { + if (data[i] >= 32 && data[i] < 127) + *pos++ = data[i]; + else { + snprintf(pos, 5, "{%02x}", data[i]); + pos += 4; + } + } + *pos = '\0'; + hostapd_logger(hapd, sm->addr, + HOSTAPD_MODULE_IEEE8021X, + HOSTAPD_LEVEL_DEBUG, + "STA identity '%s'", buf); + free(buf); + } + + sm->rx_identity = TRUE; + sm->dot1xAuthEapolRespIdFramesRx++; + + /* Save station identity for future RADIUS packets */ + free(sm->identity); + sm->identity = malloc(len + 1); + if (sm->identity) { + memcpy(sm->identity, data, len); + sm->identity[len] = '\0'; + sm->identity_len = len; + } + } else + sm->dot1xAuthEapolRespFramesRx++; + + sm->eapolEap = TRUE; +} + + +/* Process incoming EAP packet from Supplicant */ +static void handle_eap(hostapd *hapd, struct sta_info *sta, u8 *buf, + size_t len) +{ + struct eap_hdr *eap; + u16 eap_len; + + if (len < sizeof(*eap)) { + printf(" too short EAP packet\n"); + return; + } + + eap = (struct eap_hdr *) buf; + + eap_len = ntohs(eap->length); + HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, + " EAP: code=%d identifier=%d length=%d", + eap->code, eap->identifier, eap_len); + if (eap_len < sizeof(*eap)) { + printf(" Invalid EAP length\n"); + return; + } else if (eap_len > len) { + printf(" Too short frame to contain this EAP packet\n"); + return; + } else if (eap_len < len) { + printf(" Ignoring %lu extra bytes after EAP packet\n", + (unsigned long) len - eap_len); + } + + eap_len -= sizeof(*eap); + + switch (eap->code) { + case EAP_CODE_REQUEST: + HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, " (request)\n"); + return; + case EAP_CODE_RESPONSE: + HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, " (response)\n"); + handle_eap_response(hapd, sta, eap, (u8 *) (eap + 1), eap_len); + break; + case EAP_CODE_SUCCESS: + HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, " (success)\n"); + return; + case EAP_CODE_FAILURE: + HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, " (failure)\n"); + return; + default: + HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, " (unknown code)\n"); + return; + } +} + + +/* Process the EAPOL frames from the Supplicant */ +void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf, + size_t len) +{ + struct sta_info *sta; + struct ieee802_1x_hdr *hdr; + struct ieee802_1x_eapol_key *key; + u16 datalen; + + if (!hapd->conf->ieee802_1x && !hapd->conf->wpa) + return; + + HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, + "IEEE 802.1X: %lu bytes from " MACSTR "\n", + (unsigned long) len, MAC2STR(sa)); + sta = ap_get_sta(hapd, sa); + if (!sta) { + printf(" no station information available\n"); + return; + } + + if (len < sizeof(*hdr)) { + printf(" too short IEEE 802.1X packet\n"); + return; + } + + hdr = (struct ieee802_1x_hdr *) buf; + datalen = ntohs(hdr->length); + HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, + " IEEE 802.1X: version=%d type=%d length=%d\n", + hdr->version, hdr->type, datalen); + + if (len - sizeof(*hdr) < datalen) { + printf(" frame too short for this IEEE 802.1X packet\n"); + if (sta->eapol_sm) + sta->eapol_sm->dot1xAuthEapLengthErrorFramesRx++; + return; + } + if (len - sizeof(*hdr) > datalen) { + HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, + " ignoring %lu extra octets after IEEE 802.1X " + "packet\n", + (unsigned long) len - sizeof(*hdr) - datalen); + } + + if (sta->eapol_sm) { + sta->eapol_sm->dot1xAuthLastEapolFrameVersion = hdr->version; + sta->eapol_sm->dot1xAuthEapolFramesRx++; + } + + key = (struct ieee802_1x_eapol_key *) (hdr + 1); + if (datalen >= sizeof(struct ieee802_1x_eapol_key) && + hdr->type == IEEE802_1X_TYPE_EAPOL_KEY && + (key->type == EAPOL_KEY_TYPE_WPA || + key->type == EAPOL_KEY_TYPE_RSN)) { + wpa_receive(hapd, sta, (u8 *) hdr, sizeof(*hdr) + datalen); + return; + } + + if (!hapd->conf->ieee802_1x || sta->wpa_key_mgmt == WPA_KEY_MGMT_PSK) + return; + + if (!sta->eapol_sm) { + sta->eapol_sm = eapol_sm_alloc(hapd, sta); + if (!sta->eapol_sm) + return; + } + + /* since we support version 1, we can ignore version field and proceed + * as specified in version 1 standard [IEEE Std 802.1X-2001, 7.5.5] */ + /* TODO: actually, we are not version 1 anymore.. However, Version 2 + * does not change frame contents, so should be ok to process frames + * more or less identically. Some changes might be needed for + * verification of fields. */ + + switch (hdr->type) { + case IEEE802_1X_TYPE_EAP_PACKET: + handle_eap(hapd, sta, (u8 *) (hdr + 1), datalen); + break; + + case IEEE802_1X_TYPE_EAPOL_START: + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, + HOSTAPD_LEVEL_DEBUG, "received EAPOL-Start " + "from STA"); + if (sta->pmksa) { + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, + HOSTAPD_LEVEL_DEBUG, "cached PMKSA " + "available - ignore it since " + "STA sent EAPOL-Start"); + sta->pmksa = NULL; + } + sta->eapol_sm->auth_pae.eapolStart = TRUE; + sta->eapol_sm->dot1xAuthEapolStartFramesRx++; + wpa_sm_event(hapd, sta, WPA_REAUTH_EAPOL); + break; + + case IEEE802_1X_TYPE_EAPOL_LOGOFF: + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, + HOSTAPD_LEVEL_DEBUG, "received EAPOL-Logoff " + "from STA"); + sta->acct_terminate_cause = + RADIUS_ACCT_TERMINATE_CAUSE_USER_REQUEST; + sta->eapol_sm->auth_pae.eapolLogoff = TRUE; + sta->eapol_sm->dot1xAuthEapolLogoffFramesRx++; + break; + + case IEEE802_1X_TYPE_EAPOL_KEY: + HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, " EAPOL-Key\n"); + if (!(sta->flags & WLAN_STA_AUTHORIZED)) { + printf(" Dropped key data from unauthorized " + "Supplicant\n"); + break; + } + break; + + case IEEE802_1X_TYPE_EAPOL_ENCAPSULATED_ASF_ALERT: + HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, + " EAPOL-Encapsulated-ASF-Alert\n"); + /* TODO: implement support for this; show data */ + break; + + default: + HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, + " unknown IEEE 802.1X packet type\n"); + sta->eapol_sm->dot1xAuthInvalidEapolFramesRx++; + break; + } + + eapol_sm_step(sta->eapol_sm); +} + + +void ieee802_1x_new_station(hostapd *hapd, struct sta_info *sta) +{ + if (!hapd->conf->ieee802_1x || sta->wpa_key_mgmt == WPA_KEY_MGMT_PSK) + return; + + if (sta->eapol_sm == NULL) { + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, + HOSTAPD_LEVEL_DEBUG, "start authentication"); + sta->eapol_sm = eapol_sm_alloc(hapd, sta); + if (sta->eapol_sm == NULL) { + hostapd_logger(hapd, sta->addr, + HOSTAPD_MODULE_IEEE8021X, + HOSTAPD_LEVEL_INFO, + "failed to allocate state machine"); + return; + } + } + + sta->eapol_sm->portEnabled = TRUE; + + if (sta->pmksa) { + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, + HOSTAPD_LEVEL_DEBUG, + "PMK from PMKSA cache - skip IEEE 802.1X/EAP"); + /* Setup EAPOL state machines to already authenticated state + * because of existing PMKSA information in the cache. */ + sta->eapol_sm->keyRun = TRUE; + sta->eapol_sm->keyAvailable = TRUE; + sta->eapol_sm->auth_pae.state = AUTH_PAE_AUTHENTICATING; + sta->eapol_sm->be_auth.state = BE_AUTH_SUCCESS; + sta->eapol_sm->authSuccess = TRUE; + if (sta->eapol_sm->eap) + eap_sm_notify_cached(sta->eapol_sm->eap); + } else + eapol_sm_step(sta->eapol_sm); +} + + +void ieee802_1x_free_radius_class(struct radius_class_data *class) +{ + int i; + if (class == NULL) + return; + for (i = 0; i < class->count; i++) + free(class->attr[i].data); + free(class->attr); + class->attr = NULL; + class->count = 0; +} + + +int ieee802_1x_copy_radius_class(struct radius_class_data *dst, + struct radius_class_data *src) +{ + size_t i; + + if (src->attr == NULL) + return 0; + + dst->attr = malloc(src->count * sizeof(struct radius_attr_data)); + if (dst->attr == NULL) + return -1; + + memset(dst->attr, 0, src->count * sizeof(struct radius_attr_data)); + dst->count = 0; + + for (i = 0; i < src->count; i++) { + dst->attr[i].data = malloc(src->attr[i].len); + if (dst->attr[i].data == NULL) + break; + dst->count++; + memcpy(dst->attr[i].data, src->attr[i].data, src->attr[i].len); + dst->attr[i].len = src->attr[i].len; + } + + return 0; +} + + +void ieee802_1x_free_station(struct sta_info *sta) +{ + struct eapol_state_machine *sm = sta->eapol_sm; + + eloop_cancel_timeout(ieee802_1x_eap_timeout, sta, NULL); + + if (sm == NULL) + return; + + sta->eapol_sm = NULL; + + if (sm->last_recv_radius) { + radius_msg_free(sm->last_recv_radius); + free(sm->last_recv_radius); + } + + free(sm->last_eap_supp); + free(sm->last_eap_radius); + free(sm->identity); + ieee802_1x_free_radius_class(&sm->radius_class); + free(sm->eapol_key_sign); + free(sm->eapol_key_crypt); + eapol_sm_free(sm); +} + + +static void ieee802_1x_decapsulate_radius(hostapd *hapd, struct sta_info *sta) +{ + u8 *eap; + size_t len; + struct eap_hdr *hdr; + int eap_type = -1; + char buf[64]; + struct radius_msg *msg; + struct eapol_state_machine *sm = sta->eapol_sm; + + if (sm == NULL || sm->last_recv_radius == NULL) { + if (sm) + sm->be_auth.eapNoReq = TRUE; + return; + } + + msg = sm->last_recv_radius; + + eap = radius_msg_get_eap(msg, &len); + if (eap == NULL) { + /* draft-aboba-radius-rfc2869bis-20.txt, Chap. 2.6.3: + * RADIUS server SHOULD NOT send Access-Reject/no EAP-Message + * attribute */ + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, + HOSTAPD_LEVEL_WARNING, "could not extract " + "EAP-Message from RADIUS message"); + free(sm->last_eap_radius); + sm->last_eap_radius = NULL; + sm->last_eap_radius_len = 0; + sm->be_auth.eapNoReq = TRUE; + return; + } + + if (len < sizeof(*hdr)) { + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, + HOSTAPD_LEVEL_WARNING, "too short EAP packet " + "received from authentication server"); + free(eap); + sm->be_auth.eapNoReq = TRUE; + return; + } + + if (len > sizeof(*hdr)) + eap_type = eap[sizeof(*hdr)]; + + hdr = (struct eap_hdr *) eap; + switch (hdr->code) { + case EAP_CODE_REQUEST: + snprintf(buf, sizeof(buf), "EAP-Request-%s (%d)", + eap_type >= 0 ? eap_type_text(eap_type) : "??", + eap_type); + break; + case EAP_CODE_RESPONSE: + snprintf(buf, sizeof(buf), "EAP Response-%s (%d)", + eap_type >= 0 ? eap_type_text(eap_type) : "??", + eap_type); + break; + case EAP_CODE_SUCCESS: + snprintf(buf, sizeof(buf), "EAP Success"); + break; + case EAP_CODE_FAILURE: + snprintf(buf, sizeof(buf), "EAP Failure"); + break; + default: + snprintf(buf, sizeof(buf), "unknown EAP code"); + break; + } + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, + HOSTAPD_LEVEL_DEBUG, "decapsulated EAP packet (code=%d " + "id=%d len=%d) from RADIUS server: %s", + hdr->code, hdr->identifier, ntohs(hdr->length), buf); + sm->be_auth.eapReq = TRUE; + + free(sm->last_eap_radius); + sm->last_eap_radius = eap; + sm->last_eap_radius_len = len; +} + + +static void ieee802_1x_get_keys(hostapd *hapd, struct sta_info *sta, + struct radius_msg *msg, struct radius_msg *req, + u8 *shared_secret, size_t shared_secret_len) +{ + struct radius_ms_mppe_keys *keys; + struct eapol_state_machine *sm = sta->eapol_sm; + if (sm == NULL) + return; + + keys = radius_msg_get_ms_keys(msg, req, shared_secret, + shared_secret_len); + + if (keys) { + if (HOSTAPD_DEBUG_COND(HOSTAPD_DEBUG_MINIMAL) && keys->send) { + size_t i; + printf("MS-MPPE-Send-Key (len=%lu):", + (unsigned long) keys->send_len); + for (i = 0; i < keys->send_len; i++) + printf(" %02x", keys->send[i]); + printf("\n"); + } + if (HOSTAPD_DEBUG_COND(HOSTAPD_DEBUG_MINIMAL) && keys->recv) { + size_t i; + printf("MS-MPPE-Recv-Key (len=%lu):", + (unsigned long) keys->recv_len); + for (i = 0; i < keys->recv_len; i++) + printf(" %02x", keys->recv[i]); + printf("\n"); + } + + if (keys->send && keys->recv) { + free(sm->eapol_key_sign); + free(sm->eapol_key_crypt); + sm->eapol_key_sign = keys->send; + sm->eapol_key_sign_len = keys->send_len; + sm->eapol_key_crypt = keys->recv; + sm->eapol_key_crypt_len = keys->recv_len; + if (hapd->default_wep_key || + hapd->conf->individual_wep_key_len > 0 || + hapd->conf->wpa) + sta->eapol_sm->keyAvailable = TRUE; + } else { + free(keys->send); + free(keys->recv); + } + free(keys); + } +} + + +static void ieee802_1x_store_radius_class(struct hostapd_data *hapd, + struct sta_info *sta, + struct radius_msg *msg) +{ + u8 *class; + size_t class_len; + struct eapol_state_machine *sm = sta->eapol_sm; + int count, i; + struct radius_attr_data *nclass; + size_t nclass_count; + + if (!hapd->conf->radius->acct_server || hapd->radius == NULL || + sm == NULL) + return; + + ieee802_1x_free_radius_class(&sm->radius_class); + count = radius_msg_count_attr(msg, RADIUS_ATTR_CLASS, 1); + if (count <= 0) + return; + + nclass = malloc(count * sizeof(struct radius_attr_data)); + if (nclass == NULL) + return; + + nclass_count = 0; + memset(nclass, 0, count * sizeof(struct radius_attr_data)); + + class = NULL; + for (i = 0; i < count; i++) { + do { + if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_CLASS, + &class, &class_len, + class) < 0) { + i = count; + break; + } + } while (class_len < 1); + + nclass[nclass_count].data = malloc(class_len); + if (nclass[nclass_count].data == NULL) + break; + + memcpy(nclass[nclass_count].data, class, class_len); + nclass[nclass_count].len = class_len; + nclass_count++; + } + + sm->radius_class.attr = nclass; + sm->radius_class.count = nclass_count; + HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "IEEE 802.1X: Stored %lu RADIUS " + "Class attributes for " MACSTR "\n", + (unsigned long) sm->radius_class.count, + MAC2STR(sta->addr)); +} + + +/* Update sta->identity based on User-Name attribute in Access-Accept */ +static void ieee802_1x_update_sta_identity(struct hostapd_data *hapd, + struct sta_info *sta, + struct radius_msg *msg) +{ + u8 *buf, *identity; + size_t len; + struct eapol_state_machine *sm = sta->eapol_sm; + + if (sm == NULL) + return; + + if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_USER_NAME, &buf, &len, + NULL) < 0) + return; + + identity = malloc(len + 1); + if (identity == NULL) + return; + + memcpy(identity, buf, len); + identity[len] = '\0'; + + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, + HOSTAPD_LEVEL_DEBUG, "old identity '%s' updated with " + "User-Name from Access-Accept '%s'", + sm->identity ? (char *) sm->identity : "N/A", + (char *) identity); + + free(sm->identity); + sm->identity = identity; + sm->identity_len = len; +} + + +struct sta_id_search { + u8 identifier; + struct eapol_state_machine *sm; +}; + + +static int ieee802_1x_select_radius_identifier(struct hostapd_data *hapd, + struct sta_info *sta, + void *ctx) +{ + struct sta_id_search *id_search = ctx; + struct eapol_state_machine *sm = sta->eapol_sm; + + if (sm && sm->radius_identifier >= 0 && + sm->radius_identifier == id_search->identifier) { + id_search->sm = sm; + return 1; + } + return 0; +} + + +static struct eapol_state_machine * +ieee802_1x_search_radius_identifier(struct hostapd_data *hapd, u8 identifier) +{ + struct sta_id_search id_search; + id_search.identifier = identifier; + id_search.sm = NULL; + ap_for_each_sta(hapd, ieee802_1x_select_radius_identifier, &id_search); + return id_search.sm; +} + + +/* Process the RADIUS frames from Authentication Server */ +static RadiusRxResult +ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req, + u8 *shared_secret, size_t shared_secret_len, + void *data) +{ + struct hostapd_data *hapd = data; + struct sta_info *sta; + u32 session_timeout = 0, termination_action, acct_interim_interval; + int session_timeout_set; + int eap_timeout; + struct eapol_state_machine *sm; + int override_eapReq = 0; + + sm = ieee802_1x_search_radius_identifier(hapd, msg->hdr->identifier); + if (sm == NULL) { + HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "IEEE 802.1X: Could not " + "find matching station for this RADIUS " + "message\n"); + return RADIUS_RX_UNKNOWN; + } + sta = sm->sta; + + /* RFC 2869, Ch. 5.13: valid Message-Authenticator attribute MUST be + * present when packet contains an EAP-Message attribute */ + if (msg->hdr->code == RADIUS_CODE_ACCESS_REJECT && + radius_msg_get_attr(msg, RADIUS_ATTR_MESSAGE_AUTHENTICATOR, NULL, + 0) < 0 && + radius_msg_get_attr(msg, RADIUS_ATTR_EAP_MESSAGE, NULL, 0) < 0) { + HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "Allowing RADIUS " + "Access-Reject without Message-Authenticator " + "since it does not include EAP-Message\n"); + } else if (radius_msg_verify(msg, shared_secret, shared_secret_len, + req, 1)) { + printf("Incoming RADIUS packet did not have correct " + "Message-Authenticator - dropped\n"); + return RADIUS_RX_INVALID_AUTHENTICATOR; + } + + if (msg->hdr->code != RADIUS_CODE_ACCESS_ACCEPT && + msg->hdr->code != RADIUS_CODE_ACCESS_REJECT && + msg->hdr->code != RADIUS_CODE_ACCESS_CHALLENGE) { + printf("Unknown RADIUS message code\n"); + return RADIUS_RX_UNKNOWN; + } + + sm->radius_identifier = -1; + HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, + "RADIUS packet matching with station " MACSTR "\n", + MAC2STR(sta->addr)); + + if (sm->last_recv_radius) { + radius_msg_free(sm->last_recv_radius); + free(sm->last_recv_radius); + } + + sm->last_recv_radius = msg; + + session_timeout_set = + !radius_msg_get_attr_int32(msg, RADIUS_ATTR_SESSION_TIMEOUT, + &session_timeout); + if (radius_msg_get_attr_int32(msg, RADIUS_ATTR_TERMINATION_ACTION, + &termination_action)) + termination_action = RADIUS_TERMINATION_ACTION_DEFAULT; + + if (hapd->conf->radius->acct_interim_interval == 0 && + msg->hdr->code == RADIUS_CODE_ACCESS_ACCEPT && + radius_msg_get_attr_int32(msg, RADIUS_ATTR_ACCT_INTERIM_INTERVAL, + &acct_interim_interval) == 0) { + if (acct_interim_interval < 60) { + hostapd_logger(hapd, sta->addr, + HOSTAPD_MODULE_IEEE8021X, + HOSTAPD_LEVEL_INFO, + "ignored too small " + "Acct-Interim-Interval %d", + acct_interim_interval); + } else + sta->acct_interim_interval = acct_interim_interval; + } + + + switch (msg->hdr->code) { + case RADIUS_CODE_ACCESS_ACCEPT: + /* RFC 3580, Ch. 3.17 */ + if (session_timeout_set && termination_action == + RADIUS_TERMINATION_ACTION_RADIUS_REQUEST) { + sm->reauth_timer.reAuthPeriod = session_timeout; + } else if (session_timeout_set) + ap_sta_session_timeout(hapd, sta, session_timeout); + + sm->eapSuccess = TRUE; + override_eapReq = 1; + ieee802_1x_get_keys(hapd, sta, msg, req, shared_secret, + shared_secret_len); + ieee802_1x_store_radius_class(hapd, sta, msg); + ieee802_1x_update_sta_identity(hapd, sta, msg); + if (sm->keyAvailable) { + pmksa_cache_add(hapd, sta, sm->eapol_key_crypt, + session_timeout_set ? + session_timeout : -1); + } + break; + case RADIUS_CODE_ACCESS_REJECT: + sm->eapFail = TRUE; + override_eapReq = 1; + break; + case RADIUS_CODE_ACCESS_CHALLENGE: + if (session_timeout_set) { + /* RFC 2869, Ch. 2.3.2; RFC 3580, Ch. 3.17 */ + eap_timeout = session_timeout; + } else + eap_timeout = 30; + hostapd_logger(hapd, sm->addr, HOSTAPD_MODULE_IEEE8021X, + HOSTAPD_LEVEL_DEBUG, + "using EAP timeout of %d seconds%s", + eap_timeout, + session_timeout_set ? " (from RADIUS)" : ""); + eloop_cancel_timeout(ieee802_1x_eap_timeout, sta, NULL); + eloop_register_timeout(eap_timeout, 0, ieee802_1x_eap_timeout, + sta, NULL); + sm->eapTimeout = FALSE; + break; + } + + ieee802_1x_decapsulate_radius(hapd, sta); + if (override_eapReq) + sm->be_auth.eapReq = FALSE; + + eapol_sm_step(sm); + + return RADIUS_RX_QUEUED; +} + + +/* Handler for EAPOL Backend Authentication state machine sendRespToServer. + * Forward the EAP Response from Supplicant to Authentication Server. */ +void ieee802_1x_send_resp_to_server(hostapd *hapd, struct sta_info *sta) +{ + struct eapol_state_machine *sm = sta->eapol_sm; + if (sm == NULL) + return; + + if (hapd->conf->eap_server) { + eap_set_eapRespData(sm->eap, sm->last_eap_supp, + sm->last_eap_supp_len); + } else { + ieee802_1x_encapsulate_radius(hapd, sta, sm->last_eap_supp, + sm->last_eap_supp_len); + } +} + + +void ieee802_1x_abort_auth(struct hostapd_data *hapd, struct sta_info *sta) +{ + struct eapol_state_machine *sm = sta->eapol_sm; + if (sm == NULL) + return; + + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, + HOSTAPD_LEVEL_DEBUG, "aborting authentication"); + + if (sm->last_recv_radius) { + radius_msg_free(sm->last_recv_radius); + free(sm->last_recv_radius); + sm->last_recv_radius = NULL; + } + free(sm->last_eap_supp); + sm->last_eap_supp = NULL; + sm->last_eap_supp_len = 0; + free(sm->last_eap_radius); + sm->last_eap_radius = NULL; + sm->last_eap_radius_len = 0; +} + + +void ieee802_1x_set_port_enabled(hostapd *hapd, struct sta_info *sta, + int enabled) +{ + if (!sta->eapol_sm) + return; + + HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, + "IEEE 802.1X: station " MACSTR " port %s\n", + MAC2STR(sta->addr), enabled ? "enabled" : "disabled"); + sta->eapol_sm->portEnabled = enabled ? TRUE : FALSE; + eapol_sm_step(sta->eapol_sm); +} + + +#ifdef HOSTAPD_DUMP_STATE +void ieee802_1x_dump_state(FILE *f, const char *prefix, struct sta_info *sta) +{ + struct eapol_state_machine *sm = sta->eapol_sm; + if (sm == NULL) + return; + + fprintf(f, "%sIEEE 802.1X:\n", prefix); + + if (sm->identity) { + size_t i; + fprintf(f, "%sidentity=", prefix); + for (i = 0; i < sm->identity_len; i++) + fprint_char(f, sm->identity[i]); + fprintf(f, "\n"); + } + + fprintf(f, "%scached_packets=%s%s%s\n", prefix, + sm->last_recv_radius ? "[RX RADIUS]" : "", + sm->last_eap_radius ? "[EAP RADIUS]" : "", + sm->last_eap_supp ? "[EAP SUPPLICANT]" : ""); + + eapol_sm_dump_state(f, prefix, sm); +} +#endif /* HOSTAPD_DUMP_STATE */ + + +static int ieee802_1x_rekey_broadcast(hostapd *hapd) +{ + if (hapd->conf->default_wep_key_len < 1) + return 0; + + free(hapd->default_wep_key); + hapd->default_wep_key = malloc(hapd->conf->default_wep_key_len); + if (hapd->default_wep_key == NULL || + hostapd_get_rand(hapd->default_wep_key, + hapd->conf->default_wep_key_len)) { + printf("Could not generate random WEP key.\n"); + free(hapd->default_wep_key); + hapd->default_wep_key = NULL; + return -1; + } + + if (HOSTAPD_DEBUG_COND(HOSTAPD_DEBUG_MINIMAL)) { + hostapd_hexdump("IEEE 802.1X: New default WEP key", + hapd->default_wep_key, + hapd->conf->default_wep_key_len); + } + + return 0; +} + + +static int ieee802_1x_sta_key_available(struct hostapd_data *hapd, + struct sta_info *sta, void *ctx) +{ + if (sta->eapol_sm) { + sta->eapol_sm->keyAvailable = TRUE; + eapol_sm_step(sta->eapol_sm); + } + return 0; +} + + +static void ieee802_1x_rekey(void *eloop_ctx, void *timeout_ctx) +{ + struct hostapd_data *hapd = eloop_ctx; + + if (hapd->default_wep_key_idx >= 3) + hapd->default_wep_key_idx = + hapd->conf->individual_wep_key_len > 0 ? 1 : 0; + else + hapd->default_wep_key_idx++; + + HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "IEEE 802.1X: New default WEP " + "key index %d\n", hapd->default_wep_key_idx); + + if (ieee802_1x_rekey_broadcast(hapd)) { + hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE8021X, + HOSTAPD_LEVEL_WARNING, "failed to generate a " + "new broadcast key"); + free(hapd->default_wep_key); + hapd->default_wep_key = NULL; + return; + } + + /* TODO: Could setup key for RX here, but change default TX keyid only + * after new broadcast key has been sent to all stations. */ + if (hostapd_set_encryption(hapd, "WEP", NULL, + hapd->default_wep_key_idx, + hapd->default_wep_key, + hapd->conf->default_wep_key_len)) { + hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE8021X, + HOSTAPD_LEVEL_WARNING, "failed to configure a " + "new broadcast key"); + free(hapd->default_wep_key); + hapd->default_wep_key = NULL; + return; + } + + ap_for_each_sta(hapd, ieee802_1x_sta_key_available, NULL); + + if (hapd->conf->wep_rekeying_period > 0) { + eloop_register_timeout(hapd->conf->wep_rekeying_period, 0, + ieee802_1x_rekey, hapd, NULL); + } +} + + +int ieee802_1x_init(hostapd *hapd) +{ + int i; + + if ((hapd->conf->ieee802_1x || hapd->conf->wpa) && + hostapd_set_ieee8021x(hapd, 1)) + return -1; + + if (radius_client_register(hapd->radius, RADIUS_AUTH, + ieee802_1x_receive_auth, hapd)) + return -1; + + if (hapd->conf->default_wep_key_len) { + for (i = 0; i < 4; i++) + hostapd_set_encryption(hapd, "none", NULL, i, NULL, 0); + + ieee802_1x_rekey(hapd, NULL); + + if (hapd->default_wep_key == NULL) + return -1; + } + + return 0; +} + + +void ieee802_1x_deinit(hostapd *hapd) +{ + if (hapd->driver != NULL && + (hapd->conf->ieee802_1x || hapd->conf->wpa)) + hostapd_set_ieee8021x(hapd, 0); +} + + +static void ieee802_1x_new_auth_session(struct hostapd_data *hapd, + struct sta_info *sta) +{ + struct eapol_state_machine *sm = sta->eapol_sm; + if (sm == NULL) + return; + + HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, + "IEEE 802.1X: station " MACSTR " - new auth session, " + "clearing State\n", MAC2STR(sta->addr)); + + if (sm->last_recv_radius) { + radius_msg_free(sm->last_recv_radius); + free(sm->last_recv_radius); + sm->last_recv_radius = NULL; + } + + sm->eapSuccess = FALSE; + sm->eapFail = FALSE; +} + + +int ieee802_1x_tx_status(hostapd *hapd, struct sta_info *sta, u8 *buf, + size_t len, int ack) +{ + struct ieee80211_hdr *hdr; + struct ieee802_1x_hdr *xhdr; + struct ieee802_1x_eapol_key *key; + u8 *pos; + + if (sta == NULL) + return -1; + if (len < sizeof(*hdr) + sizeof(rfc1042_header) + 2 + sizeof(*xhdr)) + return 0; + + hdr = (struct ieee80211_hdr *) buf; + pos = (u8 *) (hdr + 1); + if (memcmp(pos, rfc1042_header, sizeof(rfc1042_header)) != 0) + return 0; + pos += sizeof(rfc1042_header); + if (((pos[0] << 8) | pos[1]) != ETH_P_PAE) + return 0; + pos += 2; + + xhdr = (struct ieee802_1x_hdr *) pos; + pos += sizeof(*xhdr); + + HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "IEEE 802.1X: " MACSTR + " TX status - version=%d type=%d length=%d - ack=%d\n", + MAC2STR(sta->addr), xhdr->version, xhdr->type, + ntohs(xhdr->length), ack); + + /* EAPOL EAP-Packet packets are eventually re-sent by either Supplicant + * or Authenticator state machines, but EAPOL-Key packets are not + * retransmitted in case of failure. Try to re-sent failed EAPOL-Key + * packets couple of times because otherwise STA keys become + * unsynchronized with AP. */ + if (xhdr->type == IEEE802_1X_TYPE_EAPOL_KEY && !ack && + pos + sizeof(*key) <= buf + len) { + key = (struct ieee802_1x_eapol_key *) pos; + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, + HOSTAPD_LEVEL_DEBUG, "did not Ack EAPOL-Key " + "frame (%scast index=%d)", + key->key_index & BIT(7) ? "uni" : "broad", + key->key_index & ~BIT(7)); + /* TODO: re-send EAPOL-Key couple of times (with short delay + * between them?). If all attempt fail, report error and + * deauthenticate STA so that it will get new keys when + * authenticating again (e.g., after returning in range). + * Separate limit/transmit state needed both for unicast and + * broadcast keys(?) */ + } + /* TODO: could move unicast key configuration from ieee802_1x_tx_key() + * to here and change the key only if the EAPOL-Key packet was Acked. + */ + + return 1; +} + + +u8 * ieee802_1x_get_identity(struct eapol_state_machine *sm, size_t *len) +{ + if (sm == NULL || sm->identity == NULL) + return NULL; + + *len = sm->identity_len; + return sm->identity; +} + + +u8 * ieee802_1x_get_radius_class(struct eapol_state_machine *sm, size_t *len, + int idx) +{ + if (sm == NULL || sm->radius_class.attr == NULL || + idx >= sm->radius_class.count) + return NULL; + + *len = sm->radius_class.attr[idx].len; + return sm->radius_class.attr[idx].data; +} + + +u8 * ieee802_1x_get_key_crypt(struct eapol_state_machine *sm, size_t *len) +{ + if (sm == NULL) + return NULL; + + *len = sm->eapol_key_crypt_len; + return sm->eapol_key_crypt; +} + + +void ieee802_1x_notify_port_enabled(struct eapol_state_machine *sm, + int enabled) +{ + if (sm == NULL) + return; + sm->portEnabled = enabled ? TRUE : FALSE; + eapol_sm_step(sm); +} + + +void ieee802_1x_notify_port_valid(struct eapol_state_machine *sm, + int valid) +{ + if (sm == NULL) + return; + sm->portValid = valid ? TRUE : FALSE; + eapol_sm_step(sm); +} + + +void ieee802_1x_notify_pre_auth(struct eapol_state_machine *sm, int pre_auth) +{ + if (sm == NULL) + return; + if (pre_auth) + sm->flags |= EAPOL_SM_PREAUTH; + else + sm->flags &= ~EAPOL_SM_PREAUTH; +} + + +static const char * bool_txt(Boolean bool) +{ + return bool ? "TRUE" : "FALSE"; +} + + +int ieee802_1x_get_mib(struct hostapd_data *hapd, char *buf, size_t buflen) +{ + /* TODO */ + return 0; +} + + +int ieee802_1x_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta, + char *buf, size_t buflen) +{ + int len = 0; + struct eapol_state_machine *sm = sta->eapol_sm; + + if (sm == NULL) + return 0; + + len += snprintf(buf + len, buflen - len, + "dot1xPaePortNumber=%d\n" + "dot1xPaePortProtocolVersion=%d\n" + "dot1xPaePortCapabilities=1\n" + "dot1xPaePortInitialize=%d\n" + "dot1xPaePortReauthenticate=FALSE\n", + sta->aid, + EAPOL_VERSION, + sm->initialize); + + /* dot1xAuthConfigTable */ + len += snprintf(buf + len, buflen - len, + "dot1xAuthPaeState=%d\n" + "dot1xAuthBackendAuthState=%d\n" + "dot1xAuthAdminControlledDirections=%d\n" + "dot1xAuthOperControlledDirections=%d\n" + "dot1xAuthAuthControlledPortStatus=%d\n" + "dot1xAuthAuthControlledPortControl=%d\n" + "dot1xAuthQuietPeriod=%u\n" + "dot1xAuthServerTimeout=%u\n" + "dot1xAuthReAuthPeriod=%u\n" + "dot1xAuthReAuthEnabled=%s\n" + "dot1xAuthKeyTxEnabled=%s\n", + sm->auth_pae.state + 1, + sm->be_auth.state + 1, + sm->ctrl_dir.adminControlledDirections, + sm->ctrl_dir.operControlledDirections, + sm->authPortStatus, + sm->portControl, + sm->auth_pae.quietPeriod, + sm->be_auth.serverTimeout, + sm->reauth_timer.reAuthPeriod, + bool_txt(sm->reauth_timer.reAuthEnabled), + bool_txt(sm->keyTxEnabled)); + + /* dot1xAuthStatsTable */ + len += snprintf(buf + len, buflen - len, + "dot1xAuthEapolFramesRx=%u\n" + "dot1xAuthEapolFramesTx=%u\n" + "dot1xAuthEapolStartFramesRx=%u\n" + "dot1xAuthEapolLogoffFramesRx=%u\n" + "dot1xAuthEapolRespIdFramesRx=%u\n" + "dot1xAuthEapolRespFramesRx=%u\n" + "dot1xAuthEapolReqIdFramesTx=%u\n" + "dot1xAuthEapolReqFramesTx=%u\n" + "dot1xAuthInvalidEapolFramesRx=%u\n" + "dot1xAuthEapLengthErrorFramesRx=%u\n" + "dot1xAuthLastEapolFrameVersion=%u\n" + "dot1xAuthLastEapolFrameSource=" MACSTR "\n", + sm->dot1xAuthEapolFramesRx, + sm->dot1xAuthEapolFramesTx, + sm->dot1xAuthEapolStartFramesRx, + sm->dot1xAuthEapolLogoffFramesRx, + sm->dot1xAuthEapolRespIdFramesRx, + sm->dot1xAuthEapolRespFramesRx, + sm->dot1xAuthEapolReqIdFramesTx, + sm->dot1xAuthEapolReqFramesTx, + sm->dot1xAuthInvalidEapolFramesRx, + sm->dot1xAuthEapLengthErrorFramesRx, + sm->dot1xAuthLastEapolFrameVersion, + MAC2STR(sm->addr)); + + /* dot1xAuthDiagTable */ + len += snprintf(buf + len, buflen - len, + "dot1xAuthEntersConnecting=%u\n" + "dot1xAuthEapLogoffsWhileConnecting=%u\n" + "dot1xAuthEntersAuthenticating=%u\n" + "dot1xAuthAuthSuccessesWhileAuthenticating=%u\n" + "dot1xAuthAuthTimeoutsWhileAuthenticating=%u\n" + "dot1xAuthAuthFailWhileAuthenticating=%u\n" + "dot1xAuthAuthEapStartsWhileAuthenticating=%u\n" + "dot1xAuthAuthEapLogoffWhileAuthenticating=%u\n" + "dot1xAuthAuthReauthsWhileAuthenticated=%u\n" + "dot1xAuthAuthEapStartsWhileAuthenticated=%u\n" + "dot1xAuthAuthEapLogoffWhileAuthenticated=%u\n" + "dot1xAuthBackendResponses=%u\n" + "dot1xAuthBackendAccessChallenges=%u\n" + "dot1xAuthBackendOtherRequestsToSupplicant=%u\n" + "dot1xAuthBackendAuthSuccesses=%u\n" + "dot1xAuthBackendAuthFails=%u\n", + sm->auth_pae.authEntersConnecting, + sm->auth_pae.authEapLogoffsWhileConnecting, + sm->auth_pae.authEntersAuthenticating, + sm->auth_pae.authAuthSuccessesWhileAuthenticating, + sm->auth_pae.authAuthTimeoutsWhileAuthenticating, + sm->auth_pae.authAuthFailWhileAuthenticating, + sm->auth_pae.authAuthEapStartsWhileAuthenticating, + sm->auth_pae.authAuthEapLogoffWhileAuthenticating, + sm->auth_pae.authAuthReauthsWhileAuthenticated, + sm->auth_pae.authAuthEapStartsWhileAuthenticated, + sm->auth_pae.authAuthEapLogoffWhileAuthenticated, + sm->be_auth.backendResponses, + sm->be_auth.backendAccessChallenges, + sm->be_auth.backendOtherRequestsToSupplicant, + sm->be_auth.backendAuthSuccesses, + sm->be_auth.backendAuthFails); + + /* dot1xAuthSessionStatsTable */ + len += snprintf(buf + len, buflen - len, + /* TODO: dot1xAuthSessionOctetsRx */ + /* TODO: dot1xAuthSessionOctetsTx */ + /* TODO: dot1xAuthSessionFramesRx */ + /* TODO: dot1xAuthSessionFramesTx */ + "dot1xAuthSessionId=%08X-%08X\n" + "dot1xAuthSessionAuthenticMethod=%d\n" + "dot1xAuthSessionTime=%u\n" + "dot1xAuthSessionTerminateCause=999\n" + "dot1xAuthSessionUserName=%s\n", + sta->acct_session_id_hi, sta->acct_session_id_lo, + sta->wpa_key_mgmt == WPA_KEY_MGMT_IEEE8021X ? 1 : 2, + (unsigned int) (time(NULL) - sta->acct_session_start), + sm->identity); + + return len; +} + + +void ieee802_1x_finished(struct hostapd_data *hapd, struct sta_info *sta, + int success) +{ + u8 *key; + size_t len; + /* TODO: get PMKLifetime from WPA parameters */ + static const int dot11RSNAConfigPMKLifetime = 43200; + + key = ieee802_1x_get_key_crypt(sta->eapol_sm, &len); + if (success && key) { + pmksa_cache_add(hapd, sta, key, dot11RSNAConfigPMKLifetime); + } +} diff --git a/contrib/hostapd-0.4.9/ieee802_1x.h b/contrib/hostapd-0.4.9/ieee802_1x.h new file mode 100644 index 0000000000..0ac06b24bd --- /dev/null +++ b/contrib/hostapd-0.4.9/ieee802_1x.h @@ -0,0 +1,94 @@ +#ifndef IEEE802_1X_H +#define IEEE802_1X_H + +/* IEEE Std 802.1X-REV-d11, 7.2 */ + +struct ieee802_1x_hdr { + u8 version; + u8 type; + u16 length; + /* followed by length octets of data */ +} __attribute__ ((packed)); + +#define EAPOL_VERSION 2 + +enum { IEEE802_1X_TYPE_EAP_PACKET = 0, + IEEE802_1X_TYPE_EAPOL_START = 1, + IEEE802_1X_TYPE_EAPOL_LOGOFF = 2, + IEEE802_1X_TYPE_EAPOL_KEY = 3, + IEEE802_1X_TYPE_EAPOL_ENCAPSULATED_ASF_ALERT = 4 +}; + +/* draft-congdon-radius-8021x-20.txt */ + +struct ieee802_1x_eapol_key { + u8 type; + u16 key_length; + u8 replay_counter[8]; /* does not repeat within the life of the keying + * material used to encrypt the Key field; + * 64-bit NTP timestamp MAY be used here */ + u8 key_iv[16]; /* cryptographically random number */ + u8 key_index; /* key flag in the most significant bit: + * 0 = broadcast (default key), + * 1 = unicast (key mapping key); key index is in the + * 7 least significant bits */ + u8 key_signature[16]; /* HMAC-MD5 message integrity check computed with + * MS-MPPE-Send-Key as the key */ + + /* followed by key: if packet body length = 44 + key length, then the + * key field (of key_length bytes) contains the key in encrypted form; + * if packet body length = 44, key field is absent and key_length + * represents the number of least significant octets from + * MS-MPPE-Send-Key attribute to be used as the keying material; + * RC4 key used in encryption = Key-IV + MS-MPPE-Recv-Key */ +} __attribute__ ((packed)); + +enum { EAPOL_KEY_TYPE_RC4 = 1, EAPOL_KEY_TYPE_RSN = 2, + EAPOL_KEY_TYPE_WPA = 254 }; + + +void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf, + size_t len); +void ieee802_1x_new_station(hostapd *hapd, struct sta_info *sta); +void ieee802_1x_free_station(struct sta_info *sta); + +void ieee802_1x_request_identity(struct hostapd_data *hapd, + struct sta_info *sta); +void ieee802_1x_tx_canned_eap(struct hostapd_data *hapd, struct sta_info *sta, + int success); +void ieee802_1x_tx_req(hostapd *hapd, struct sta_info *sta); +void ieee802_1x_tx_key(struct hostapd_data *hapd, struct sta_info *sta); +void ieee802_1x_send_resp_to_server(hostapd *hapd, struct sta_info *sta); +void ieee802_1x_abort_auth(struct hostapd_data *hapd, struct sta_info *sta); +void ieee802_1x_set_sta_authorized(hostapd *hapd, struct sta_info *sta, + int authorized); +void ieee802_1x_set_port_enabled(hostapd *hapd, struct sta_info *sta, + int enabled); +void ieee802_1x_dump_state(FILE *f, const char *prefix, struct sta_info *sta); +int ieee802_1x_init(hostapd *hapd); +void ieee802_1x_deinit(hostapd *hapd); +int ieee802_1x_tx_status(hostapd *hapd, struct sta_info *sta, u8 *buf, + size_t len, int ack); +u8 * ieee802_1x_get_identity(struct eapol_state_machine *sm, size_t *len); +u8 * ieee802_1x_get_radius_class(struct eapol_state_machine *sm, size_t *len, + int idx); +u8 * ieee802_1x_get_key_crypt(struct eapol_state_machine *sm, size_t *len); +void ieee802_1x_notify_port_enabled(struct eapol_state_machine *sm, + int enabled); +void ieee802_1x_notify_port_valid(struct eapol_state_machine *sm, + int valid); +void ieee802_1x_notify_pre_auth(struct eapol_state_machine *sm, int pre_auth); +int ieee802_1x_get_mib(struct hostapd_data *hapd, char *buf, size_t buflen); +int ieee802_1x_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta, + char *buf, size_t buflen); +void hostapd_get_ntp_timestamp(u8 *buf); +void ieee802_1x_finished(struct hostapd_data *hapd, struct sta_info *sta, + int success); + +struct radius_class_data; + +void ieee802_1x_free_radius_class(struct radius_class_data *class); +int ieee802_1x_copy_radius_class(struct radius_class_data *dst, + struct radius_class_data *src); + +#endif /* IEEE802_1X_H */ diff --git a/contrib/hostapd-0.4.9/l2_packet.h b/contrib/hostapd-0.4.9/l2_packet.h new file mode 100644 index 0000000000..eb966d39ed --- /dev/null +++ b/contrib/hostapd-0.4.9/l2_packet.h @@ -0,0 +1,133 @@ +/* + * WPA Supplicant - Layer2 packet interface definition + * Copyright (c) 2003-2005, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + * + * This file defines an interface for layer 2 (link layer) packet sending and + * receiving. l2_packet_linux.c is one implementation for such a layer 2 + * implementation using Linux packet sockets and l2_packet_pcap.c another one + * using libpcap and libdnet. When porting %wpa_supplicant to other operating + * systems, a new l2_packet implementation may need to be added. + */ + +#ifndef L2_PACKET_H +#define L2_PACKET_H + +#define MAC2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5] +#define MACSTR "%02x:%02x:%02x:%02x:%02x:%02x" + +#ifndef ETH_P_EAPOL +#define ETH_P_EAPOL 0x888e +#endif + +#ifndef ETH_P_RSN_PREAUTH +#define ETH_P_RSN_PREAUTH 0x88c7 +#endif + +/** + * struct l2_packet_data - Internal l2_packet data structure + * + * This structure is used by the l2_packet implementation to store its private + * data. Other files use a pointer to this data when calling the l2_packet + * functions, but the contents of this structure should not be used directly + * outside l2_packet implementation. + */ +struct l2_packet_data; + +struct l2_ethhdr { + u8 h_dest[ETH_ALEN]; + u8 h_source[ETH_ALEN]; + u16 h_proto; +} __attribute__ ((packed)); + +/** + * l2_packet_init - Initialize l2_packet interface + * @ifname: Interface name + * @own_addr: Optional own MAC address if available from driver interface or + * %NULL if not available + * @protocol: Ethernet protocol number in host byte order + * @rx_callback: Callback function that will be called for each received packet + * @rx_callback_ctx: Callback data (ctx) for calls to rx_callback() + * @l2_hdr: 1 = include layer 2 header, 0 = do not include header + * Returns: Pointer to internal data or %NULL on failure + * + * rx_callback function will be called with src_addr pointing to the source + * address (MAC address) of the the packet. If l2_hdr is set to 0, buf + * points to len bytes of the payload after the layer 2 header and similarly, + * TX buffers start with payload. This behavior can be changed by setting + * l2_hdr=1 to include the layer 2 header in the data buffer. + */ +struct l2_packet_data * l2_packet_init( + const char *ifname, const u8 *own_addr, unsigned short protocol, + void (*rx_callback)(void *ctx, const u8 *src_addr, + const u8 *buf, size_t len), + void *rx_callback_ctx, int l2_hdr); + +/** + * l2_packet_deinit - Deinitialize l2_packet interface + * @l2: Pointer to internal l2_packet data from l2_packet_init() + */ +void l2_packet_deinit(struct l2_packet_data *l2); + +/** + * l2_packet_get_own_addr - Get own layer 2 address + * @l2: Pointer to internal l2_packet data from l2_packet_init() + * @addr: Buffer for the own address (6 bytes) + * Returns: 0 on success, -1 on failure + */ +int l2_packet_get_own_addr(struct l2_packet_data *l2, u8 *addr); + +/** + * l2_packet_send - Send a packet + * @l2: Pointer to internal l2_packet data from l2_packet_init() + * @dst_addr: Destination address for the packet (only used if l2_hdr == 0) + * @proto: Protocol/ethertype for the packet in host byte order (only used if + * l2_hdr == 0) + * @buf: Packet contents to be sent; including layer 2 header if l2_hdr was + * set to 1 in l2_packet_init() call. Otherwise, only the payload of the packet + * is included. + * @len: Length of the buffer (including l2 header only if l2_hdr == 1) + * Returns: >=0 on success, <0 on failure + */ +int l2_packet_send(struct l2_packet_data *l2, const u8 *dst_addr, u16 proto, + const u8 *buf, size_t len); + +/** + * l2_packet_get_ip_addr - Get the current IP address from the interface + * @l2: Pointer to internal l2_packet data from l2_packet_init() + * @buf: Buffer for the IP address in text format + * @len: Maximum buffer length + * Returns: 0 on success, -1 on failure + * + * This function can be used to get the current IP address from the interface + * bound to the l2_packet. This is mainly for status information and the IP + * address will be stored as an ASCII string. This function is not essential + * for %wpa_supplicant operation, so full implementation is not required. + * l2_packet implementation will need to define the function, but it can return + * -1 if the IP address information is not available. + */ +int l2_packet_get_ip_addr(struct l2_packet_data *l2, char *buf, size_t len); + + +/** + * l2_packet_notify_auth_start - Notify l2_packet about start of authentication + * @l2: Pointer to internal l2_packet data from l2_packet_init() + * + * This function is called when authentication is expected to start, e.g., when + * association has been completed, in order to prepare l2_packet implementation + * for EAPOL frames. This function is used mainly if the l2_packet code needs + * to do polling in which case it can increasing polling frequency. This can + * also be an empty function if the l2_packet implementation does not benefit + * from knowing about the starting authentication. + */ +void l2_packet_notify_auth_start(struct l2_packet_data *l2); + +#endif /* L2_PACKET_H */ diff --git a/contrib/hostapd-0.4.9/madwifi.conf b/contrib/hostapd-0.4.9/madwifi.conf new file mode 100644 index 0000000000..a9bf539d23 --- /dev/null +++ b/contrib/hostapd-0.4.9/madwifi.conf @@ -0,0 +1,278 @@ +##### hostapd configuration file ############################################## +# Empty lines and lines starting with # are ignored + +# AP netdevice name (without 'ap' prefix, i.e., wlan0 uses wlan0ap for +# management frames) +interface=ath0 + +# In case of madwifi driver, an additional configuration parameter, bridge, +# must be used to notify hostapd if the interface is included in a bridge. This +# parameter is not used with Host AP driver. +bridge=br0 + +# Driver interface type (hostap/wired/madwifi; default: hostap) +driver=madwifi + +# hostapd event logger configuration +# +# Two output method: syslog and stdout (only usable if not forking to +# background). +# +# Module bitfield (ORed bitfield of modules that will be logged; -1 = all +# modules): +# bit 0 (1) = IEEE 802.11 +# bit 1 (2) = IEEE 802.1X +# bit 2 (4) = RADIUS +# bit 3 (8) = WPA +# bit 4 (16) = driver interface +# bit 5 (32) = IAPP +# +# Levels (minimum value for logged events): +# 0 = verbose debugging +# 1 = debugging +# 2 = informational messages +# 3 = notification +# 4 = warning +# +logger_syslog=-1 +logger_syslog_level=2 +logger_stdout=-1 +logger_stdout_level=2 + +# Debugging: 0 = no, 1 = minimal, 2 = verbose, 3 = msg dumps, 4 = excessive +debug=0 + +# Dump file for state information (on SIGUSR1) +dump_file=/tmp/hostapd.dump + + +##### IEEE 802.11 related configuration ####################################### + +# SSID to be used in IEEE 802.11 management frames +ssid=wpa-test + +##### IEEE 802.1X-REV related configuration ################################### + +# Require IEEE 802.1X authorization +#ieee8021x=1 + +# Optional displayable message sent with EAP Request-Identity. The first \0 +# in this string will be converted to ASCII-0 (nul). This can be used to +# separate network info (comma separated list of attribute=value pairs); see, +# e.g., draft-adrangi-eap-network-discovery-07.txt. +#eap_message=hello +#eap_message=hello\0networkid=netw,nasid=foo,portid=0,NAIRealms=example.com + +# WEP rekeying (disabled if key lengths are not set or are set to 0) +# Key lengths for default/broadcast and individual/unicast keys: +# 5 = 40-bit WEP (also known as 64-bit WEP with 40 secret bits) +# 13 = 104-bit WEP (also known as 128-bit WEP with 104 secret bits) +#wep_key_len_broadcast=5 +#wep_key_len_unicast=5 +# Rekeying period in seconds. 0 = do not rekey (i.e., set keys only once) +#wep_rekey_period=300 + +# EAPOL-Key index workaround (set bit7) for WinXP Supplicant (needed only if +# only broadcast keys are used) +eapol_key_index_workaround=0 + +# EAP reauthentication period in seconds (default: 3600 seconds; 0 = disable +# reauthentication). +#eap_reauth_period=3600 + + +##### Integrated EAP server ################################################### + +# Optionally, hostapd can be configured to use an integrated EAP server +# to process EAP authentication locally without need for an external RADIUS +# server. This functionality can be used both as a local authentication server +# for IEEE 802.1X/EAPOL and as a RADIUS server for other devices. + +# Use integrated EAP server instead of external RADIUS authentication +# server. This is also needed if hostapd is configured to act as a RADIUS +# authentication server. +eap_server=0 + +# Path for EAP server user database +#eap_user_file=/etc/hostapd.eap_user + +# CA certificate (PEM or DER file) for EAP-TLS/PEAP/TTLS +#ca_cert=/etc/hostapd.ca.pem + +# Server certificate (PEM or DER file) for EAP-TLS/PEAP/TTLS +#server_cert=/etc/hostapd.server.pem + +# Private key matching with the server certificate for EAP-TLS/PEAP/TTLS +# This may point to the same file as server_cert if both certificate and key +# are included in a single file. PKCS#12 (PFX) file (.p12/.pfx) can also be +# used by commenting out server_cert and specifying the PFX file as the +# private_key. +#private_key=/etc/hostapd.server.prv + +# Passphrase for private key +#private_key_passwd=secret passphrase + +# Enable CRL verification. +# Note: hostapd does not yet support CRL downloading based on CDP. Thus, a +# valid CRL signed by the CA is required to be included in the ca_cert file. +# This can be done by using PEM format for CA certificate and CRL and +# concatenating these into one file. Whenever CRL changes, hostapd needs to be +# restarted to take the new CRL into use. +# 0 = do not verify CRLs (default) +# 1 = check the CRL of the user certificate +# 2 = check all CRLs in the certificate path +#check_crl=1 + +# Configuration data for EAP-SIM database/authentication gateway interface. +# This is a text string in implementation specific format. The example +# implementation in eap_sim_db.c uses this as the file name for the GSM +# authentication triplets. +#eap_sim_db=/etc/hostapd.sim_db + + +##### IEEE 802.11f - Inter-Access Point Protocol (IAPP) ####################### + +# Interface to be used for IAPP broadcast packets +#iapp_interface=eth0 + + +##### RADIUS client configuration ############################################# +# for IEEE 802.1X with external Authentication Server, IEEE 802.11 +# authentication with external ACL for MAC addresses, and accounting + +# The own IP address of the access point (used as NAS-IP-Address) +own_ip_addr=127.0.0.1 + +# Optional NAS-Identifier string for RADIUS messages. When used, this should be +# a unique to the NAS within the scope of the RADIUS server. For example, a +# fully qualified domain name can be used here. +#nas_identifier=ap.example.com + +# RADIUS authentication server +#auth_server_addr=127.0.0.1 +#auth_server_port=1812 +#auth_server_shared_secret=secret + +# RADIUS accounting server +#acct_server_addr=127.0.0.1 +#acct_server_port=1813 +#acct_server_shared_secret=secret + +# Secondary RADIUS servers; to be used if primary one does not reply to +# RADIUS packets. These are optional and there can be more than one secondary +# server listed. +#auth_server_addr=127.0.0.2 +#auth_server_port=1812 +#auth_server_shared_secret=secret2 +# +#acct_server_addr=127.0.0.2 +#acct_server_port=1813 +#acct_server_shared_secret=secret2 + +# Retry interval for trying to return to the primary RADIUS server (in +# seconds). RADIUS client code will automatically try to use the next server +# when the current server is not replying to requests. If this interval is set, +# primary server will be retried after configured amount of time even if the +# currently used secondary server is still working. +#radius_retry_primary_interval=600 + + +# Interim accounting update interval +# If this is set (larger than 0) and acct_server is configured, hostapd will +# send interim accounting updates every N seconds. Note: if set, this overrides +# possible Acct-Interim-Interval attribute in Access-Accept message. Thus, this +# value should not be configured in hostapd.conf, if RADIUS server is used to +# control the interim interval. +# This value should not be less 600 (10 minutes) and must not be less than +# 60 (1 minute). +#radius_acct_interim_interval=600 + + +##### RADIUS authentication server configuration ############################## + +# hostapd can be used as a RADIUS authentication server for other hosts. This +# requires that the integrated EAP authenticator is also enabled and both +# authentication services are sharing the same configuration. + +# File name of the RADIUS clients configuration for the RADIUS server. If this +# commented out, RADIUS server is disabled. +#radius_server_clients=/etc/hostapd.radius_clients + +# The UDP port number for the RADIUS authentication server +#radius_server_auth_port=1812 + +# Use IPv6 with RADIUS server (IPv4 will also be supported using IPv6 API) +#radius_server_ipv6=1 + + +##### WPA/IEEE 802.11i configuration ########################################## + +# Enable WPA. Setting this variable configures the AP to require WPA (either +# WPA-PSK or WPA-RADIUS/EAP based on other configuration). For WPA-PSK, either +# wpa_psk or wpa_passphrase must be set and wpa_key_mgmt must include WPA-PSK. +# For WPA-RADIUS/EAP, ieee8021x must be set (but without dynamic WEP keys), +# RADIUS authentication server must be configured, and WPA-EAP must be included +# in wpa_key_mgmt. +# This field is a bit field that can be used to enable WPA (IEEE 802.11i/D3.0) +# and/or WPA2 (full IEEE 802.11i/RSN): +# bit0 = WPA +# bit1 = IEEE 802.11i/RSN (WPA2) (dot11RSNAEnabled) +#wpa=1 + +# WPA pre-shared keys for WPA-PSK. This can be either entered as a 256-bit +# secret in hex format (64 hex digits), wpa_psk, or as an ASCII passphrase +# (8..63 characters) that will be converted to PSK. This conversion uses SSID +# so the PSK changes when ASCII passphrase is used and the SSID is changed. +# wpa_psk (dot11RSNAConfigPSKValue) +# wpa_passphrase (dot11RSNAConfigPSKPassPhrase) +#wpa_psk=0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef +#wpa_passphrase=secret passphrase + +# Optionally, WPA PSKs can be read from a separate text file (containing list +# of (PSK,MAC address) pairs. This allows more than one PSK to be configured. +# Use absolute path name to make sure that the files can be read on SIGHUP +# configuration reloads. +#wpa_psk_file=/etc/hostapd.wpa_psk + +# Set of accepted key management algorithms (WPA-PSK, WPA-EAP, or both). The +# entries are separated with a space. +# (dot11RSNAConfigAuthenticationSuitesTable) +#wpa_key_mgmt=WPA-PSK WPA-EAP + +# Set of accepted cipher suites (encryption algorithms) for pairwise keys +# (unicast packets). This is a space separated list of algorithms: +# CCMP = AES in Counter mode with CBC-MAC [RFC 3610, IEEE 802.11i/D7.0] +# TKIP = Temporal Key Integrity Protocol [IEEE 802.11i/D7.0] +# Group cipher suite (encryption algorithm for broadcast and multicast frames) +# is automatically selected based on this configuration. If only CCMP is +# allowed as the pairwise cipher, group cipher will also be CCMP. Otherwise, +# TKIP will be used as the group cipher. +# (dot11RSNAConfigPairwiseCiphersTable) +#wpa_pairwise=TKIP CCMP + +# Time interval for rekeying GTK (broadcast/multicast encryption keys) in +# seconds. (dot11RSNAConfigGroupRekeyTime) +#wpa_group_rekey=600 + +# Rekey GTK when any STA that possesses the current GTK is leaving the BSS. +# (dot11RSNAConfigGroupRekeyStrict) +#wpa_strict_rekey=1 + +# Time interval for rekeying GMK (master key used internally to generate GTKs +# (in seconds). +#wpa_gmk_rekey=86400 + +# Enable IEEE 802.11i/RSN/WPA2 pre-authentication. This is used to speed up +# roaming be pre-authenticating IEEE 802.1X/EAP part of the full RSN +# authentication and key handshake before actually associating with a new AP. +# (dot11RSNAPreauthenticationEnabled) +#rsn_preauth=1 +# +# Space separated list of interfaces from which pre-authentication frames are +# accepted (e.g., 'eth0' or 'eth0 wlan0wds0'. This list should include all +# interface that are used for connections to other APs. This could include +# wired interfaces and WDS links. The normal wireless data interface towards +# associated stations (e.g., wlan0) should not be added, since +# pre-authentication is only used with APs other than the currently associated +# one. +#rsn_preauth_interfaces=eth0 diff --git a/contrib/hostapd-0.4.9/md5.c b/contrib/hostapd-0.4.9/md5.c new file mode 100644 index 0000000000..82388e0867 --- /dev/null +++ b/contrib/hostapd-0.4.9/md5.c @@ -0,0 +1,395 @@ +/* + * MD5 hash implementation and interface functions + * Copyright (c) 2003-2005, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include +#include +#include + +#include "common.h" +#include "md5.h" +#include "crypto.h" + + +/** + * hmac_md5_vector - HMAC-MD5 over data vector (RFC 2104) + * @key: Key for HMAC operations + * @key_len: Length of the key in bytes + * @num_elem: Number of elements in the data vector + * @addr: Pointers to the data areas + * @len: Lengths of the data blocks + * @mac: Buffer for the hash (16 bytes) + */ +void hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + u8 k_pad[64]; /* padding - key XORd with ipad/opad */ + u8 tk[16]; + int i; + const u8 *_addr[6]; + size_t _len[6]; + + if (num_elem > 5) { + /* + * Fixed limit on the number of fragments to avoid having to + * allocate memory (which could fail). + */ + return; + } + + /* if key is longer than 64 bytes reset it to key = MD5(key) */ + if (key_len > 64) { + md5_vector(1, &key, &key_len, tk); + key = tk; + key_len = 16; + } + + /* the HMAC_MD5 transform looks like: + * + * MD5(K XOR opad, MD5(K XOR ipad, text)) + * + * where K is an n byte key + * ipad is the byte 0x36 repeated 64 times + * opad is the byte 0x5c repeated 64 times + * and text is the data being protected */ + + /* start out by storing key in ipad */ + memset(k_pad, 0, sizeof(k_pad)); + memcpy(k_pad, key, key_len); + + /* XOR key with ipad values */ + for (i = 0; i < 64; i++) + k_pad[i] ^= 0x36; + + /* perform inner MD5 */ + _addr[0] = k_pad; + _len[0] = 64; + for (i = 0; i < num_elem; i++) { + _addr[i + 1] = addr[i]; + _len[i + 1] = len[i]; + } + md5_vector(1 + num_elem, _addr, _len, mac); + + memset(k_pad, 0, sizeof(k_pad)); + memcpy(k_pad, key, key_len); + /* XOR key with opad values */ + for (i = 0; i < 64; i++) + k_pad[i] ^= 0x5c; + + /* perform outer MD5 */ + _addr[0] = k_pad; + _len[0] = 64; + _addr[1] = mac; + _len[1] = MD5_MAC_LEN; + md5_vector(2, _addr, _len, mac); +} + + +/** + * hmac_md5 - HMAC-MD5 over data buffer (RFC 2104) + * @key: Key for HMAC operations + * @key_len: Length of the key in bytes + * @data: Pointers to the data area + * @data_len: Length of the data area + * @mac: Buffer for the hash (16 bytes) + */ +void hmac_md5(const u8 *key, size_t key_len, const u8 *data, size_t data_len, + u8 *mac) +{ + hmac_md5_vector(key, key_len, 1, &data, &data_len, mac); +} + + +#ifndef EAP_TLS_FUNCS + +struct MD5Context { + u32 buf[4]; + u32 bits[2]; + u8 in[64]; +}; + +static void MD5Init(struct MD5Context *context); +static void MD5Update(struct MD5Context *context, unsigned char const *buf, + unsigned len); +static void MD5Final(unsigned char digest[16], struct MD5Context *context); +static void MD5Transform(u32 buf[4], u32 const in[16]); + +typedef struct MD5Context MD5_CTX; + + +/** + * md5_vector - MD5 hash for data vector + * @num_elem: Number of elements in the data vector + * @addr: Pointers to the data areas + * @len: Lengths of the data blocks + * @mac: Buffer for the hash + */ +void md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +{ + MD5_CTX ctx; + int i; + + MD5Init(&ctx); + for (i = 0; i < num_elem; i++) + MD5Update(&ctx, addr[i], len[i]); + MD5Final(mac, &ctx); +} + + +/* ===== start - public domain MD5 implementation ===== */ +/* + * This code implements the MD5 message-digest algorithm. + * The algorithm is due to Ron Rivest. This code was + * written by Colin Plumb in 1993, no copyright is claimed. + * This code is in the public domain; do with it what you wish. + * + * Equivalent code is available from RSA Data Security, Inc. + * This code has been tested against that, and is equivalent, + * except that you don't need to include two pages of legalese + * with every copy. + * + * To compute the message digest of a chunk of bytes, declare an + * MD5Context structure, pass it to MD5Init, call MD5Update as + * needed on buffers full of bytes, and then call MD5Final, which + * will fill a supplied 16-byte array with the digest. + */ + +#ifndef WORDS_BIGENDIAN +#define byteReverse(buf, len) /* Nothing */ +#else +/* + * Note: this code is harmless on little-endian machines. + */ +static void byteReverse(unsigned char *buf, unsigned longs) +{ + u32 t; + do { + t = (u32) ((unsigned) buf[3] << 8 | buf[2]) << 16 | + ((unsigned) buf[1] << 8 | buf[0]); + *(u32 *) buf = t; + buf += 4; + } while (--longs); +} +#endif + +/* + * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious + * initialization constants. + */ +static void MD5Init(struct MD5Context *ctx) +{ + ctx->buf[0] = 0x67452301; + ctx->buf[1] = 0xefcdab89; + ctx->buf[2] = 0x98badcfe; + ctx->buf[3] = 0x10325476; + + ctx->bits[0] = 0; + ctx->bits[1] = 0; +} + +/* + * Update context to reflect the concatenation of another buffer full + * of bytes. + */ +static void MD5Update(struct MD5Context *ctx, unsigned char const *buf, + unsigned len) +{ + u32 t; + + /* Update bitcount */ + + t = ctx->bits[0]; + if ((ctx->bits[0] = t + ((u32) len << 3)) < t) + ctx->bits[1]++; /* Carry from low to high */ + ctx->bits[1] += len >> 29; + + t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */ + + /* Handle any leading odd-sized chunks */ + + if (t) { + unsigned char *p = (unsigned char *) ctx->in + t; + + t = 64 - t; + if (len < t) { + memcpy(p, buf, len); + return; + } + memcpy(p, buf, t); + byteReverse(ctx->in, 16); + MD5Transform(ctx->buf, (u32 *) ctx->in); + buf += t; + len -= t; + } + /* Process data in 64-byte chunks */ + + while (len >= 64) { + memcpy(ctx->in, buf, 64); + byteReverse(ctx->in, 16); + MD5Transform(ctx->buf, (u32 *) ctx->in); + buf += 64; + len -= 64; + } + + /* Handle any remaining bytes of data. */ + + memcpy(ctx->in, buf, len); +} + +/* + * Final wrapup - pad to 64-byte boundary with the bit pattern + * 1 0* (64-bit count of bits processed, MSB-first) + */ +static void MD5Final(unsigned char digest[16], struct MD5Context *ctx) +{ + unsigned count; + unsigned char *p; + + /* Compute number of bytes mod 64 */ + count = (ctx->bits[0] >> 3) & 0x3F; + + /* Set the first char of padding to 0x80. This is safe since there is + always at least one byte free */ + p = ctx->in + count; + *p++ = 0x80; + + /* Bytes of padding needed to make 64 bytes */ + count = 64 - 1 - count; + + /* Pad out to 56 mod 64 */ + if (count < 8) { + /* Two lots of padding: Pad the first block to 64 bytes */ + memset(p, 0, count); + byteReverse(ctx->in, 16); + MD5Transform(ctx->buf, (u32 *) ctx->in); + + /* Now fill the next block with 56 bytes */ + memset(ctx->in, 0, 56); + } else { + /* Pad block to 56 bytes */ + memset(p, 0, count - 8); + } + byteReverse(ctx->in, 14); + + /* Append length in bits and transform */ + ((u32 *) ctx->in)[14] = ctx->bits[0]; + ((u32 *) ctx->in)[15] = ctx->bits[1]; + + MD5Transform(ctx->buf, (u32 *) ctx->in); + byteReverse((unsigned char *) ctx->buf, 4); + memcpy(digest, ctx->buf, 16); + memset(ctx, 0, sizeof(ctx)); /* In case it's sensitive */ +} + +/* The four core functions - F1 is optimized somewhat */ + +/* #define F1(x, y, z) (x & y | ~x & z) */ +#define F1(x, y, z) (z ^ (x & (y ^ z))) +#define F2(x, y, z) F1(z, x, y) +#define F3(x, y, z) (x ^ y ^ z) +#define F4(x, y, z) (y ^ (x | ~z)) + +/* This is the central step in the MD5 algorithm. */ +#define MD5STEP(f, w, x, y, z, data, s) \ + ( w += f(x, y, z) + data, w = w<>(32-s), w += x ) + +/* + * The core of the MD5 algorithm, this alters an existing MD5 hash to + * reflect the addition of 16 longwords of new data. MD5Update blocks + * the data and converts bytes into longwords for this routine. + */ +static void MD5Transform(u32 buf[4], u32 const in[16]) +{ + register u32 a, b, c, d; + + a = buf[0]; + b = buf[1]; + c = buf[2]; + d = buf[3]; + + MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7); + MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12); + MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17); + MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22); + MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7); + MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12); + MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17); + MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22); + MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7); + MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12); + MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17); + MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22); + MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7); + MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12); + MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17); + MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22); + + MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5); + MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9); + MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14); + MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20); + MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5); + MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9); + MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14); + MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20); + MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5); + MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9); + MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14); + MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20); + MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5); + MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9); + MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14); + MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20); + + MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4); + MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11); + MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16); + MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23); + MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4); + MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11); + MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16); + MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23); + MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4); + MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11); + MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16); + MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23); + MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4); + MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11); + MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16); + MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23); + + MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6); + MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10); + MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15); + MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21); + MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6); + MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10); + MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15); + MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21); + MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6); + MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10); + MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15); + MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21); + MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6); + MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10); + MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15); + MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21); + + buf[0] += a; + buf[1] += b; + buf[2] += c; + buf[3] += d; +} +/* ===== end - public domain MD5 implementation ===== */ + +#endif /* !EAP_TLS_FUNCS */ diff --git a/contrib/hostapd-0.4.9/md5.h b/contrib/hostapd-0.4.9/md5.h new file mode 100644 index 0000000000..a724804943 --- /dev/null +++ b/contrib/hostapd-0.4.9/md5.h @@ -0,0 +1,25 @@ +/* + * MD5 hash implementation and interface functions + * Copyright (c) 2003-2005, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef MD5_H +#define MD5_H + +#define MD5_MAC_LEN 16 + +void hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac); +void hmac_md5(const u8 *key, size_t key_len, const u8 *data, size_t data_len, + u8 *mac); + +#endif /* MD5_H */ diff --git a/contrib/hostapd-0.4.9/ms_funcs.c b/contrib/hostapd-0.4.9/ms_funcs.c new file mode 100644 index 0000000000..a72cf27481 --- /dev/null +++ b/contrib/hostapd-0.4.9/ms_funcs.c @@ -0,0 +1,503 @@ +/* + * WPA Supplicant / shared MSCHAPV2 helper functions / RFC 2433 / RFC 2759 + * Copyright (c) 2004-2006, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include +#include +#include + +#include "common.h" +#include "sha1.h" +#include "ms_funcs.h" +#include "crypto.h" +#include "rc4.h" + + +/** + * challenge_hash - ChallengeHash() - RFC 2759, Sect. 8.2 + * @peer_challenge: 16-octet PeerChallenge (IN) + * @auth_challenge: 16-octet AuthenticatorChallenge (IN) + * @username: 0-to-256-char UserName (IN) + * @username_len: Length of username + * @challenge: 8-octet Challenge (OUT) + */ +static void challenge_hash(const u8 *peer_challenge, const u8 *auth_challenge, + const u8 *username, size_t username_len, + u8 *challenge) +{ + u8 hash[SHA1_MAC_LEN]; + const unsigned char *addr[3]; + size_t len[3]; + + addr[0] = peer_challenge; + len[0] = 16; + addr[1] = auth_challenge; + len[1] = 16; + addr[2] = username; + len[2] = username_len; + + sha1_vector(3, addr, len, hash); + memcpy(challenge, hash, 8); +} + + +/** + * nt_password_hash - NtPasswordHash() - RFC 2759, Sect. 8.3 + * @password: 0-to-256-unicode-char Password (IN; ASCII) + * @password_len: Length of password + * @password_hash: 16-octet PasswordHash (OUT) + */ +void nt_password_hash(const u8 *password, size_t password_len, + u8 *password_hash) +{ + u8 buf[512], *pos; + size_t i, len; + + if (password_len > 256) + return; + + /* Convert password into unicode */ + for (i = 0; i < password_len; i++) { + buf[2 * i] = password[i]; + buf[2 * i + 1] = 0; + } + + len = password_len * 2; + pos = buf; + md4_vector(1, (const u8 **) &pos, &len, password_hash); +} + + +/** + * hash_nt_password_hash - HashNtPasswordHash() - RFC 2759, Sect. 8.4 + * @password_hash: 16-octet PasswordHash (IN) + * @password_hash_hash: 16-octet PasswordHashHash (OUT) + */ +void hash_nt_password_hash(const u8 *password_hash, u8 *password_hash_hash) +{ + size_t len = 16; + md4_vector(1, &password_hash, &len, password_hash_hash); +} + + +/** + * challenge_response - ChallengeResponse() - RFC 2759, Sect. 8.5 + * @challenge: 8-octet Challenge (IN) + * @password_hash: 16-octet PasswordHash (IN) + * @response: 24-octet Response (OUT) + */ +void challenge_response(const u8 *challenge, const u8 *password_hash, + u8 *response) +{ + u8 zpwd[7]; + des_encrypt(challenge, password_hash, response); + des_encrypt(challenge, password_hash + 7, response + 8); + zpwd[0] = password_hash[14]; + zpwd[1] = password_hash[15]; + memset(zpwd + 2, 0, 5); + des_encrypt(challenge, zpwd, response + 16); +} + + +/** + * generate_nt_response - GenerateNTResponse() - RFC 2759, Sect. 8.1 + * @auth_challenge: 16-octet AuthenticatorChallenge (IN) + * @peer_hallenge: 16-octet PeerChallenge (IN) + * @username: 0-to-256-char UserName (IN) + * @username_len: Length of username + * @password: 0-to-256-unicode-char Password (IN; ASCII) + * @password_len: Length of password + * @response: 24-octet Response (OUT) + */ +void generate_nt_response(const u8 *auth_challenge, const u8 *peer_challenge, + const u8 *username, size_t username_len, + const u8 *password, size_t password_len, + u8 *response) +{ + u8 challenge[8]; + u8 password_hash[16]; + + challenge_hash(peer_challenge, auth_challenge, username, username_len, + challenge); + nt_password_hash(password, password_len, password_hash); + challenge_response(challenge, password_hash, response); +} + + +/** + * generate_authenticator_response - GenerateAuthenticatorResponse() - RFC 2759, Sect. 8.7 + * @password: 0-to-256-unicode-char Password (IN) + * @password_len: Length of password + * @nt_response: 24-octet NT-Response (IN) + * @peer_challenge: 16-octet PeerChallenge (IN) + * @auth_challenge: 16-octet AuthenticatorChallenge (IN) + * @username: 0-to-256-char UserName (IN) + * @username_len: Length of username + * @response: 20-octet AuthenticatorResponse (OUT) (note: this value is usually + * encoded as a 42-octet ASCII string (S=) + */ +void generate_authenticator_response(const u8 *password, size_t password_len, + const u8 *peer_challenge, + const u8 *auth_challenge, + const u8 *username, size_t username_len, + const u8 *nt_response, u8 *response) +{ + static const u8 magic1[39] = { + 0x4D, 0x61, 0x67, 0x69, 0x63, 0x20, 0x73, 0x65, 0x72, 0x76, + 0x65, 0x72, 0x20, 0x74, 0x6F, 0x20, 0x63, 0x6C, 0x69, 0x65, + 0x6E, 0x74, 0x20, 0x73, 0x69, 0x67, 0x6E, 0x69, 0x6E, 0x67, + 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x61, 0x6E, 0x74 + }; + static const u8 magic2[41] = { + 0x50, 0x61, 0x64, 0x20, 0x74, 0x6F, 0x20, 0x6D, 0x61, 0x6B, + 0x65, 0x20, 0x69, 0x74, 0x20, 0x64, 0x6F, 0x20, 0x6D, 0x6F, + 0x72, 0x65, 0x20, 0x74, 0x68, 0x61, 0x6E, 0x20, 0x6F, 0x6E, + 0x65, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6F, + 0x6E + }; + + u8 password_hash[16], password_hash_hash[16], challenge[8]; + const unsigned char *addr1[3]; + const size_t len1[3] = { 16, 24, sizeof(magic1) }; + const unsigned char *addr2[3]; + const size_t len2[3] = { SHA1_MAC_LEN, 8, sizeof(magic2) }; + + addr1[0] = password_hash_hash; + addr1[1] = nt_response; + addr1[2] = magic1; + + addr2[0] = response; + addr2[1] = challenge; + addr2[2] = magic2; + + nt_password_hash(password, password_len, password_hash); + hash_nt_password_hash(password_hash, password_hash_hash); + sha1_vector(3, addr1, len1, response); + + challenge_hash(peer_challenge, auth_challenge, username, username_len, + challenge); + sha1_vector(3, addr2, len2, response); +} + + +/** + * nt_challenge_response - NtChallengeResponse() - RFC 2433, Sect. A.5 + * @challenge: 8-octet Challenge (IN) + * @password: 0-to-256-unicode-char Password (IN; ASCII) + * @password_len: Length of password + * @response: 24-octet Response (OUT) + */ +void nt_challenge_response(const u8 *challenge, const u8 *password, + size_t password_len, u8 *response) +{ + u8 password_hash[16]; + nt_password_hash(password, password_len, password_hash); + challenge_response(challenge, password_hash, response); +} + + +/** + * get_master_key - GetMasterKey() - RFC 3079, Sect. 3.4 + * @password_hash_hash: 16-octet PasswordHashHash (IN) + * @nt_response: 24-octet NTResponse (IN) + * @master_key: 16-octet MasterKey (OUT) + */ +void get_master_key(const u8 *password_hash_hash, const u8 *nt_response, + u8 *master_key) +{ + static const u8 magic1[27] = { + 0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, + 0x68, 0x65, 0x20, 0x4d, 0x50, 0x50, 0x45, 0x20, 0x4d, + 0x61, 0x73, 0x74, 0x65, 0x72, 0x20, 0x4b, 0x65, 0x79 + }; + const unsigned char *addr[3]; + const size_t len[3] = { 16, 24, sizeof(magic1) }; + u8 hash[SHA1_MAC_LEN]; + + addr[0] = password_hash_hash; + addr[1] = nt_response; + addr[2] = magic1; + + sha1_vector(3, addr, len, hash); + memcpy(master_key, hash, 16); +} + + +/** + * get_asymetric_start_key - GetAsymetricStartKey() - RFC 3079, Sect. 3.4 + * @master_key: 16-octet MasterKey (IN) + * @session_key: 8-to-16 octet SessionKey (OUT) + * @session_key_len: SessionKeyLength (Length of session_key) (IN) + * @is_send: IsSend (IN, BOOLEAN) + * @is_server: IsServer (IN, BOOLEAN) + */ +void get_asymetric_start_key(const u8 *master_key, u8 *session_key, + size_t session_key_len, int is_send, + int is_server) +{ + static const u8 magic2[84] = { + 0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69, + 0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20, + 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68, + 0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20, 0x6b, 0x65, 0x79, + 0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, + 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73, 0x69, 0x64, 0x65, + 0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68, + 0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20, + 0x6b, 0x65, 0x79, 0x2e + }; + static const u8 magic3[84] = { + 0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69, + 0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20, + 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68, + 0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20, + 0x6b, 0x65, 0x79, 0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68, + 0x65, 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73, + 0x69, 0x64, 0x65, 0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73, + 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20, + 0x6b, 0x65, 0x79, 0x2e + }; + static const u8 shs_pad1[40] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + + static const u8 shs_pad2[40] = { + 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, + 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, + 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, + 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2 + }; + u8 digest[SHA1_MAC_LEN]; + const unsigned char *addr[4]; + const size_t len[4] = { 16, 40, 84, 40 }; + + addr[0] = master_key; + addr[1] = shs_pad1; + if (is_send) { + addr[2] = is_server ? magic3 : magic2; + } else { + addr[2] = is_server ? magic2 : magic3; + } + addr[3] = shs_pad2; + + sha1_vector(4, addr, len, digest); + + if (session_key_len > SHA1_MAC_LEN) + session_key_len = SHA1_MAC_LEN; + memcpy(session_key, digest, session_key_len); +} + + +#define PWBLOCK_LEN 516 + +/** + * encrypt_pw_block_with_password_hash - EncryptPwBlockWithPasswordHash() - RFC 2759, Sect. 8.10 + * @password: 0-to-256-unicode-char Password (IN; ASCII) + * @password_len: Length of password + * @password_hash: 16-octet PasswordHash (IN) + * @pw_block: 516-byte PwBlock (OUT) + */ +static void encrypt_pw_block_with_password_hash( + const u8 *password, size_t password_len, + const u8 *password_hash, u8 *pw_block) +{ + size_t i, offset; + u8 *pos; + + if (password_len > 256) + return; + + memset(pw_block, 0, PWBLOCK_LEN); + offset = (256 - password_len) * 2; + hostapd_get_rand(pw_block, offset); + for (i = 0; i < password_len; i++) + pw_block[offset + i * 2] = password[i]; + /* + * PasswordLength is 4 octets, but since the maximum password length is + * 256, only first two (in little endian byte order) can be non-zero. + */ + pos = &pw_block[2 * 256]; + WPA_PUT_LE16(pos, password_len * 2); + rc4(pw_block, PWBLOCK_LEN, password_hash, 16); +} + + +/** + * new_password_encrypted_with_old_nt_password_hash - NewPasswordEncryptedWithOldNtPasswordHash() - RFC 2759, Sect. 8.9 + * @new_password: 0-to-256-unicode-char NewPassword (IN; ASCII) + * @new_password_len: Length of new_password + * @old_password: 0-to-256-unicode-char OldPassword (IN; ASCII) + * @old_password_len: Length of old_password + * @encrypted_pw_block: 516-octet EncryptedPwBlock (OUT) + */ +void new_password_encrypted_with_old_nt_password_hash( + const u8 *new_password, size_t new_password_len, + const u8 *old_password, size_t old_password_len, + u8 *encrypted_pw_block) +{ + u8 password_hash[16]; + + nt_password_hash(old_password, old_password_len, password_hash); + encrypt_pw_block_with_password_hash(new_password, new_password_len, + password_hash, encrypted_pw_block); +} + + +/** + * nt_password_hash_encrypted_with_block - NtPasswordHashEncryptedWithBlock() - RFC 2759, Sect 8.13 + * @password_hash: 16-octer PasswordHash (IN) + * @block: 16-octet Block (IN) + * @cypher: 16-octer Cypher (OUT) + */ +static void nt_password_hash_encrypted_with_block(const u8 *password_hash, + const u8 *block, + u8 *cypher) +{ + des_encrypt(password_hash, block, cypher); + des_encrypt(password_hash + 8, block + 7, cypher + 8); +} + + +/** + * old_nt_password_hash_encrypted_with_new_nt_password_hash - OldNtPasswordHashEncryptedWithNewNtPasswordHash() - RFC 2759, Sect. 8.12 + * @new_password: 0-to-256-unicode-char NewPassword (IN; ASCII) + * @new_password_len: Length of new_password + * @old_password: 0-to-256-unicode-char OldPassword (IN; ASCII) + * @old_password_len: Length of old_password + * @encrypted_password_ash: 16-octet EncryptedPasswordHash (OUT) + */ +void old_nt_password_hash_encrypted_with_new_nt_password_hash( + const u8 *new_password, size_t new_password_len, + const u8 *old_password, size_t old_password_len, + u8 *encrypted_password_hash) +{ + u8 old_password_hash[16], new_password_hash[16]; + + nt_password_hash(old_password, old_password_len, old_password_hash); + nt_password_hash(new_password, new_password_len, new_password_hash); + nt_password_hash_encrypted_with_block(old_password_hash, + new_password_hash, + encrypted_password_hash); +} + + +#ifdef TEST_MAIN_MS_FUNCS + +#include "rc4.c" + +int main(int argc, char *argv[]) +{ + /* Test vector from RFC2759 example */ + u8 *username = "User"; + u8 *password = "clientPass"; + u8 auth_challenge[] = { + 0x5B, 0x5D, 0x7C, 0x7D, 0x7B, 0x3F, 0x2F, 0x3E, + 0x3C, 0x2C, 0x60, 0x21, 0x32, 0x26, 0x26, 0x28 + }; + u8 peer_challenge[] = { + 0x21, 0x40, 0x23, 0x24, 0x25, 0x5E, 0x26, 0x2A, + 0x28, 0x29, 0x5F, 0x2B, 0x3A, 0x33, 0x7C, 0x7E + }; + u8 challenge[] = { 0xD0, 0x2E, 0x43, 0x86, 0xBC, 0xE9, 0x12, 0x26 }; + u8 password_hash[] = { + 0x44, 0xEB, 0xBA, 0x8D, 0x53, 0x12, 0xB8, 0xD6, + 0x11, 0x47, 0x44, 0x11, 0xF5, 0x69, 0x89, 0xAE + }; + u8 nt_response[] = { + 0x82, 0x30, 0x9E, 0xCD, 0x8D, 0x70, 0x8B, 0x5E, + 0xA0, 0x8F, 0xAA, 0x39, 0x81, 0xCD, 0x83, 0x54, + 0x42, 0x33, 0x11, 0x4A, 0x3D, 0x85, 0xD6, 0xDF + }; + u8 password_hash_hash[] = { + 0x41, 0xC0, 0x0C, 0x58, 0x4B, 0xD2, 0xD9, 0x1C, + 0x40, 0x17, 0xA2, 0xA1, 0x2F, 0xA5, 0x9F, 0x3F + }; + u8 authenticator_response[] = { + 0x40, 0x7A, 0x55, 0x89, 0x11, 0x5F, 0xD0, 0xD6, + 0x20, 0x9F, 0x51, 0x0F, 0xE9, 0xC0, 0x45, 0x66, + 0x93, 0x2C, 0xDA, 0x56 + }; + u8 master_key[] = { + 0xFD, 0xEC, 0xE3, 0x71, 0x7A, 0x8C, 0x83, 0x8C, + 0xB3, 0x88, 0xE5, 0x27, 0xAE, 0x3C, 0xDD, 0x31 + }; + u8 send_start_key[] = { + 0x8B, 0x7C, 0xDC, 0x14, 0x9B, 0x99, 0x3A, 0x1B, + 0xA1, 0x18, 0xCB, 0x15, 0x3F, 0x56, 0xDC, 0xCB + }; + u8 buf[32]; + + int errors = 0; + + printf("Testing ms_funcs.c\n"); + + challenge_hash(peer_challenge, auth_challenge, + username, strlen(username), + buf); + if (memcmp(challenge, buf, sizeof(challenge)) != 0) { + printf("challenge_hash failed\n"); + errors++; + } + + nt_password_hash(password, strlen(password), buf); + if (memcmp(password_hash, buf, sizeof(password_hash)) != 0) { + printf("nt_password_hash failed\n"); + errors++; + } + + generate_nt_response(auth_challenge, peer_challenge, + username, strlen(username), + password, strlen(password), + buf); + if (memcmp(nt_response, buf, sizeof(nt_response)) != 0) { + printf("generate_nt_response failed\n"); + errors++; + } + + hash_nt_password_hash(password_hash, buf); + if (memcmp(password_hash_hash, buf, sizeof(password_hash_hash)) != 0) { + printf("hash_nt_password_hash failed\n"); + errors++; + } + + generate_authenticator_response(password, strlen(password), + peer_challenge, auth_challenge, + username, strlen(username), + nt_response, buf); + if (memcmp(authenticator_response, buf, sizeof(authenticator_response)) + != 0) { + printf("generate_authenticator_response failed\n"); + errors++; + } + + get_master_key(password_hash_hash, nt_response, buf); + if (memcmp(master_key, buf, sizeof(master_key)) != 0) { + printf("get_master_key failed\n"); + errors++; + } + + get_asymetric_start_key(master_key, buf, sizeof(send_start_key), 1, 1); + if (memcmp(send_start_key, buf, sizeof(send_start_key)) != 0) { + printf("get_asymetric_start_key failed\n"); + errors++; + } + + if (errors) + printf("FAILED! %d errors\n", errors); + + return errors; +} +#endif /* TEST_MAIN_MS_FUNCS */ diff --git a/contrib/hostapd-0.4.9/ms_funcs.h b/contrib/hostapd-0.4.9/ms_funcs.h new file mode 100644 index 0000000000..fc43a370f1 --- /dev/null +++ b/contrib/hostapd-0.4.9/ms_funcs.h @@ -0,0 +1,49 @@ +/* + * WPA Supplicant / shared MSCHAPV2 helper functions / RFC 2433 / RFC 2759 + * Copyright (c) 2004-2005, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef MS_FUNCS_H +#define MS_FUNCS_H + +void generate_nt_response(const u8 *auth_challenge, const u8 *peer_challenge, + const u8 *username, size_t username_len, + const u8 *password, size_t password_len, + u8 *response); +void generate_authenticator_response(const u8 *password, size_t password_len, + const u8 *peer_challenge, + const u8 *auth_challenge, + const u8 *username, size_t username_len, + const u8 *nt_response, u8 *response); +void nt_challenge_response(const u8 *challenge, const u8 *password, + size_t password_len, u8 *response); + +void challenge_response(const u8 *challenge, const u8 *password_hash, + u8 *response); +void nt_password_hash(const u8 *password, size_t password_len, + u8 *password_hash); +void hash_nt_password_hash(const u8 *password_hash, u8 *password_hash_hash); +void get_master_key(const u8 *password_hash_hash, const u8 *nt_response, + u8 *master_key); +void get_asymetric_start_key(const u8 *master_key, u8 *session_key, + size_t session_key_len, int is_send, + int is_server); +void new_password_encrypted_with_old_nt_password_hash( + const u8 *new_password, size_t new_password_len, + const u8 *old_password, size_t old_password_len, + u8 *encrypted_pw_block); +void old_nt_password_hash_encrypted_with_new_nt_password_hash( + const u8 *new_password, size_t new_password_len, + const u8 *old_password, size_t old_password_len, + u8 *encrypted_password_hash); + +#endif /* MS_FUNCS_H */ diff --git a/contrib/hostapd-0.4.9/radius.c b/contrib/hostapd-0.4.9/radius.c new file mode 100644 index 0000000000..86f96cb6e7 --- /dev/null +++ b/contrib/hostapd-0.4.9/radius.c @@ -0,0 +1,1158 @@ +/* + * Host AP (software wireless LAN access point) user space daemon for + * Host AP kernel driver / RADIUS client + * Copyright (c) 2002-2005, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include +#include +#include +#include +#include +#include +#ifndef CONFIG_NATIVE_WINDOWS +#include +#include +#include +#include +#endif /* CONFIG_NATIVE_WINDOWS */ + +#include "common.h" +#include "radius.h" +#include "md5.h" +#include "crypto.h" + + +struct radius_msg *radius_msg_new(u8 code, u8 identifier) +{ + struct radius_msg *msg; + + msg = (struct radius_msg *) malloc(sizeof(*msg)); + if (msg == NULL) + return NULL; + + if (radius_msg_initialize(msg, RADIUS_DEFAULT_MSG_SIZE)) { + free(msg); + return NULL; + } + + radius_msg_set_hdr(msg, code, identifier); + + return msg; +} + + +int radius_msg_initialize(struct radius_msg *msg, size_t init_len) +{ + if (msg == NULL || init_len < sizeof(struct radius_hdr)) + return -1; + + memset(msg, 0, sizeof(*msg)); + msg->buf = (unsigned char *) malloc(init_len); + if (msg->buf == NULL) + return -1; + memset(msg->buf, 0, init_len); + + msg->buf_size = init_len; + msg->hdr = (struct radius_hdr *) msg->buf; + msg->buf_used = sizeof(*msg->hdr); + + msg->attrs = (struct radius_attr_hdr **) + malloc(RADIUS_DEFAULT_ATTR_COUNT * sizeof(*msg->attrs)); + if (msg->attrs == NULL) { + free(msg->buf); + msg->buf = NULL; + msg->hdr = NULL; + return -1; + } + + msg->attr_size = RADIUS_DEFAULT_ATTR_COUNT; + msg->attr_used = 0; + + return 0; +} + + +void radius_msg_set_hdr(struct radius_msg *msg, u8 code, u8 identifier) +{ + msg->hdr->code = code; + msg->hdr->identifier = identifier; +} + + +void radius_msg_free(struct radius_msg *msg) +{ + if (msg->buf != NULL) { + free(msg->buf); + msg->buf = NULL; + msg->hdr = NULL; + } + msg->buf_size = msg->buf_used = 0; + + if (msg->attrs != NULL) { + free(msg->attrs); + msg->attrs = NULL; + } + msg->attr_size = msg->attr_used = 0; +} + + +static const char *radius_code_string(u8 code) +{ + switch (code) { + case RADIUS_CODE_ACCESS_REQUEST: return "Access-Request"; + case RADIUS_CODE_ACCESS_ACCEPT: return "Access-Accept"; + case RADIUS_CODE_ACCESS_REJECT: return "Access-Reject"; + case RADIUS_CODE_ACCOUNTING_REQUEST: return "Accounting-Request"; + case RADIUS_CODE_ACCOUNTING_RESPONSE: return "Accounting-Response"; + case RADIUS_CODE_ACCESS_CHALLENGE: return "Access-Challenge"; + case RADIUS_CODE_STATUS_SERVER: return "Status-Server"; + case RADIUS_CODE_STATUS_CLIENT: return "Status-Client"; + case RADIUS_CODE_RESERVED: return "Reserved"; + default: return "?Unknown?"; + } +} + + +struct radius_attr_type { + u8 type; + char *name; + enum { + RADIUS_ATTR_UNDIST, RADIUS_ATTR_TEXT, RADIUS_ATTR_IP, + RADIUS_ATTR_HEXDUMP, RADIUS_ATTR_INT32, RADIUS_ATTR_IPV6 + } data_type; +}; + +static struct radius_attr_type radius_attrs[] = +{ + { RADIUS_ATTR_USER_NAME, "User-Name", RADIUS_ATTR_TEXT }, + { RADIUS_ATTR_USER_PASSWORD, "User-Password", RADIUS_ATTR_UNDIST }, + { RADIUS_ATTR_NAS_IP_ADDRESS, "NAS-IP-Address", RADIUS_ATTR_IP }, + { RADIUS_ATTR_NAS_PORT, "NAS-Port", RADIUS_ATTR_INT32 }, + { RADIUS_ATTR_FRAMED_MTU, "Framed-MTU", RADIUS_ATTR_INT32 }, + { RADIUS_ATTR_STATE, "State", RADIUS_ATTR_UNDIST }, + { RADIUS_ATTR_CLASS, "Class", RADIUS_ATTR_UNDIST }, + { RADIUS_ATTR_VENDOR_SPECIFIC, "Vendor-Specific", RADIUS_ATTR_UNDIST }, + { RADIUS_ATTR_SESSION_TIMEOUT, "Session-Timeout", RADIUS_ATTR_INT32 }, + { RADIUS_ATTR_IDLE_TIMEOUT, "Idle-Timeout", RADIUS_ATTR_INT32 }, + { RADIUS_ATTR_TERMINATION_ACTION, "Termination-Action", + RADIUS_ATTR_INT32 }, + { RADIUS_ATTR_CALLED_STATION_ID, "Called-Station-Id", + RADIUS_ATTR_TEXT }, + { RADIUS_ATTR_CALLING_STATION_ID, "Calling-Station-Id", + RADIUS_ATTR_TEXT }, + { RADIUS_ATTR_NAS_IDENTIFIER, "NAS-Identifier", RADIUS_ATTR_TEXT }, + { RADIUS_ATTR_ACCT_STATUS_TYPE, "Acct-Status-Type", + RADIUS_ATTR_INT32 }, + { RADIUS_ATTR_ACCT_DELAY_TIME, "Acct-Delay-Time", RADIUS_ATTR_INT32 }, + { RADIUS_ATTR_ACCT_INPUT_OCTETS, "Acct-Input-Octets", + RADIUS_ATTR_INT32 }, + { RADIUS_ATTR_ACCT_OUTPUT_OCTETS, "Acct-Output-Octets", + RADIUS_ATTR_INT32 }, + { RADIUS_ATTR_ACCT_SESSION_ID, "Acct-Session-Id", RADIUS_ATTR_TEXT }, + { RADIUS_ATTR_ACCT_AUTHENTIC, "Acct-Authentic", RADIUS_ATTR_INT32 }, + { RADIUS_ATTR_ACCT_SESSION_TIME, "Acct-Session-Time", + RADIUS_ATTR_INT32 }, + { RADIUS_ATTR_ACCT_INPUT_PACKETS, "Acct-Input-Packets", + RADIUS_ATTR_INT32 }, + { RADIUS_ATTR_ACCT_OUTPUT_PACKETS, "Acct-Output-Packets", + RADIUS_ATTR_INT32 }, + { RADIUS_ATTR_ACCT_TERMINATE_CAUSE, "Acct-Terminate-Cause", + RADIUS_ATTR_INT32 }, + { RADIUS_ATTR_ACCT_MULTI_SESSION_ID, "Acct-Multi-Session-Id", + RADIUS_ATTR_TEXT }, + { RADIUS_ATTR_ACCT_LINK_COUNT, "Acct-Link-Count", RADIUS_ATTR_INT32 }, + { RADIUS_ATTR_ACCT_INPUT_GIGAWORDS, "Acct-Input-Gigawords", + RADIUS_ATTR_INT32 }, + { RADIUS_ATTR_ACCT_OUTPUT_GIGAWORDS, "Acct-Output-Gigawords", + RADIUS_ATTR_INT32 }, + { RADIUS_ATTR_EVENT_TIMESTAMP, "Event-Timestamp", + RADIUS_ATTR_INT32 }, + { RADIUS_ATTR_NAS_PORT_TYPE, "NAS-Port-Type", RADIUS_ATTR_INT32 }, + { RADIUS_ATTR_CONNECT_INFO, "Connect-Info", RADIUS_ATTR_TEXT }, + { RADIUS_ATTR_EAP_MESSAGE, "EAP-Message", RADIUS_ATTR_UNDIST }, + { RADIUS_ATTR_MESSAGE_AUTHENTICATOR, "Message-Authenticator", + RADIUS_ATTR_UNDIST }, + { RADIUS_ATTR_ACCT_INTERIM_INTERVAL, "Acct-Interim-Interval", + RADIUS_ATTR_INT32 }, + { RADIUS_ATTR_NAS_IPV6_ADDRESS, "NAS-IPv6-Address", RADIUS_ATTR_IPV6 }, +}; +#define RADIUS_ATTRS (sizeof(radius_attrs) / sizeof(radius_attrs[0])) + + +static struct radius_attr_type *radius_get_attr_type(u8 type) +{ + int i; + + for (i = 0; i < RADIUS_ATTRS; i++) { + if (type == radius_attrs[i].type) + return &radius_attrs[i]; + } + + return NULL; +} + + +static void radius_msg_dump_attr(struct radius_attr_hdr *hdr) +{ + struct radius_attr_type *attr; + int i, len; + unsigned char *pos; + + attr = radius_get_attr_type(hdr->type); + + printf(" Attribute %d (%s) length=%d\n", + hdr->type, attr ? attr->name : "?Unknown?", hdr->length); + + if (attr == NULL) + return; + + len = hdr->length - sizeof(struct radius_attr_hdr); + pos = (unsigned char *) (hdr + 1); + + switch (attr->data_type) { + case RADIUS_ATTR_TEXT: + printf(" Value: '"); + for (i = 0; i < len; i++) + print_char(pos[i]); + printf("'\n"); + break; + + case RADIUS_ATTR_IP: + if (len == 4) { + struct in_addr *addr = (struct in_addr *) pos; + printf(" Value: %s\n", inet_ntoa(*addr)); + } else + printf(" Invalid IP address length %d\n", len); + break; + +#ifdef CONFIG_IPV6 + case RADIUS_ATTR_IPV6: + if (len == 16) { + char buf[128]; + const char *atxt; + struct in6_addr *addr = (struct in6_addr *) pos; + atxt = inet_ntop(AF_INET6, addr, buf, sizeof(buf)); + printf(" Value: %s\n", atxt ? atxt : "?"); + } else + printf(" Invalid IPv6 address length %d\n", len); + break; +#endif /* CONFIG_IPV6 */ + + case RADIUS_ATTR_HEXDUMP: + case RADIUS_ATTR_UNDIST: + printf(" Value:"); + for (i = 0; i < len; i++) + printf(" %02x", pos[i]); + printf("\n"); + break; + + case RADIUS_ATTR_INT32: + if (len == 4) + printf(" Value: %u\n", WPA_GET_BE32(pos)); + else + printf(" Invalid INT32 length %d\n", len); + break; + + default: + break; + } +} + + +void radius_msg_dump(struct radius_msg *msg) +{ + int i; + + printf("RADIUS message: code=%d (%s) identifier=%d length=%d\n", + msg->hdr->code, radius_code_string(msg->hdr->code), + msg->hdr->identifier, ntohs(msg->hdr->length)); + + for (i = 0; i < msg->attr_used; i++) { + radius_msg_dump_attr(msg->attrs[i]); + } +} + + +int radius_msg_finish(struct radius_msg *msg, u8 *secret, size_t secret_len) +{ + if (secret) { + u8 auth[MD5_MAC_LEN]; + struct radius_attr_hdr *attr; + + memset(auth, 0, MD5_MAC_LEN); + attr = radius_msg_add_attr(msg, + RADIUS_ATTR_MESSAGE_AUTHENTICATOR, + auth, MD5_MAC_LEN); + if (attr == NULL) { + printf("WARNING: Could not add " + "Message-Authenticator\n"); + return -1; + } + msg->hdr->length = htons(msg->buf_used); + hmac_md5(secret, secret_len, msg->buf, msg->buf_used, + (u8 *) (attr + 1)); + } else + msg->hdr->length = htons(msg->buf_used); + + if (msg->buf_used > 0xffff) { + printf("WARNING: too long RADIUS message (%lu)\n", + (unsigned long) msg->buf_used); + return -1; + } + return 0; +} + + +int radius_msg_finish_srv(struct radius_msg *msg, const u8 *secret, + size_t secret_len, const u8 *req_authenticator) +{ + u8 auth[MD5_MAC_LEN]; + struct radius_attr_hdr *attr; + const u8 *addr[4]; + size_t len[4]; + + memset(auth, 0, MD5_MAC_LEN); + attr = radius_msg_add_attr(msg, RADIUS_ATTR_MESSAGE_AUTHENTICATOR, + auth, MD5_MAC_LEN); + if (attr == NULL) { + printf("WARNING: Could not add Message-Authenticator\n"); + return -1; + } + msg->hdr->length = htons(msg->buf_used); + memcpy(msg->hdr->authenticator, req_authenticator, + sizeof(msg->hdr->authenticator)); + hmac_md5(secret, secret_len, msg->buf, msg->buf_used, + (u8 *) (attr + 1)); + + /* ResponseAuth = MD5(Code+ID+Length+RequestAuth+Attributes+Secret) */ + addr[0] = (u8 *) msg->hdr; + len[0] = 1 + 1 + 2; + addr[1] = req_authenticator; + len[1] = MD5_MAC_LEN; + addr[2] = (u8 *) (msg->hdr + 1); + len[2] = msg->buf_used - sizeof(*msg->hdr); + addr[3] = secret; + len[3] = secret_len; + md5_vector(4, addr, len, msg->hdr->authenticator); + + if (msg->buf_used > 0xffff) { + printf("WARNING: too long RADIUS message (%lu)\n", + (unsigned long) msg->buf_used); + return -1; + } + return 0; +} + + +void radius_msg_finish_acct(struct radius_msg *msg, u8 *secret, + size_t secret_len) +{ + const u8 *addr[2]; + size_t len[2]; + + msg->hdr->length = htons(msg->buf_used); + memset(msg->hdr->authenticator, 0, MD5_MAC_LEN); + addr[0] = msg->buf; + len[0] = msg->buf_used; + addr[1] = secret; + len[1] = secret_len; + md5_vector(2, addr, len, msg->hdr->authenticator); + + if (msg->buf_used > 0xffff) { + printf("WARNING: too long RADIUS messages (%lu)\n", + (unsigned long) msg->buf_used); + } +} + + +static int radius_msg_add_attr_to_array(struct radius_msg *msg, + struct radius_attr_hdr *attr) +{ + if (msg->attr_used >= msg->attr_size) { + struct radius_attr_hdr **nattrs; + int nlen = msg->attr_size * 2; + + nattrs = (struct radius_attr_hdr **) + realloc(msg->attrs, nlen * sizeof(*msg->attrs)); + + if (nattrs == NULL) + return -1; + + msg->attrs = nattrs; + msg->attr_size = nlen; + } + + msg->attrs[msg->attr_used++] = attr; + + return 0; +} + + +struct radius_attr_hdr *radius_msg_add_attr(struct radius_msg *msg, u8 type, + const u8 *data, size_t data_len) +{ + size_t buf_needed; + struct radius_attr_hdr *attr; + + if (data_len > RADIUS_MAX_ATTR_LEN) { + printf("radius_msg_add_attr: too long attribute (%lu bytes)\n", + (unsigned long) data_len); + return NULL; + } + + buf_needed = msg->buf_used + sizeof(*attr) + data_len; + + if (msg->buf_size < buf_needed) { + /* allocate more space for message buffer */ + unsigned char *nbuf; + int nlen = msg->buf_size; + int diff, i; + + while (nlen < buf_needed) + nlen *= 2; + nbuf = (unsigned char *) realloc(msg->buf, nlen); + if (nbuf == NULL) + return NULL; + diff = nbuf - msg->buf; + msg->buf = nbuf; + msg->hdr = (struct radius_hdr *) msg->buf; + /* adjust attr pointers to match with the new buffer */ + for (i = 0; i < msg->attr_used; i++) + msg->attrs[i] = (struct radius_attr_hdr *) + (((u8 *) msg->attrs[i]) + diff); + memset(msg->buf + msg->buf_size, 0, nlen - msg->buf_size); + msg->buf_size = nlen; + } + + attr = (struct radius_attr_hdr *) (msg->buf + msg->buf_used); + attr->type = type; + attr->length = sizeof(*attr) + data_len; + if (data_len > 0) + memcpy(attr + 1, data, data_len); + + msg->buf_used += sizeof(*attr) + data_len; + + if (radius_msg_add_attr_to_array(msg, attr)) + return NULL; + + return attr; +} + + +struct radius_msg *radius_msg_parse(const u8 *data, size_t len) +{ + struct radius_msg *msg; + struct radius_hdr *hdr; + struct radius_attr_hdr *attr; + size_t msg_len; + unsigned char *pos, *end; + + if (data == NULL || len < sizeof(*hdr)) + return NULL; + + hdr = (struct radius_hdr *) data; + + msg_len = ntohs(hdr->length); + if (msg_len < sizeof(*hdr) || msg_len > len) { + printf("Invalid RADIUS message length\n"); + return NULL; + } + + if (msg_len < len) { + printf("Ignored %lu extra bytes after RADIUS message\n", + (unsigned long) len - msg_len); + } + + msg = (struct radius_msg *) malloc(sizeof(*msg)); + if (msg == NULL) + return NULL; + + if (radius_msg_initialize(msg, msg_len)) { + free(msg); + return NULL; + } + + memcpy(msg->buf, data, msg_len); + msg->buf_size = msg->buf_used = msg_len; + + /* parse attributes */ + pos = (unsigned char *) (msg->hdr + 1); + end = msg->buf + msg->buf_used; + while (pos < end) { + if (end - pos < sizeof(*attr)) + goto fail; + + attr = (struct radius_attr_hdr *) pos; + + if (pos + attr->length > end || attr->length < sizeof(*attr)) + goto fail; + + /* TODO: check that attr->length is suitable for attr->type */ + + if (radius_msg_add_attr_to_array(msg, attr)) + goto fail; + + pos += attr->length; + } + + return msg; + + fail: + radius_msg_free(msg); + free(msg); + return NULL; +} + + +int radius_msg_add_eap(struct radius_msg *msg, const u8 *data, size_t data_len) +{ + const u8 *pos = data; + size_t left = data_len; + + while (left > 0) { + int len; + if (left > RADIUS_MAX_ATTR_LEN) + len = RADIUS_MAX_ATTR_LEN; + else + len = left; + + if (!radius_msg_add_attr(msg, RADIUS_ATTR_EAP_MESSAGE, + pos, len)) + return 0; + + pos += len; + left -= len; + } + + return 1; +} + + +u8 *radius_msg_get_eap(struct radius_msg *msg, size_t *eap_len) +{ + u8 *eap, *pos; + size_t len; + int i; + + if (msg == NULL) + return NULL; + + len = 0; + for (i = 0; i < msg->attr_used; i++) { + if (msg->attrs[i]->type == RADIUS_ATTR_EAP_MESSAGE) + len += msg->attrs[i]->length - + sizeof(struct radius_attr_hdr); + } + + if (len == 0) + return NULL; + + eap = malloc(len); + if (eap == NULL) + return NULL; + + pos = eap; + for (i = 0; i < msg->attr_used; i++) { + if (msg->attrs[i]->type == RADIUS_ATTR_EAP_MESSAGE) { + struct radius_attr_hdr *attr = msg->attrs[i]; + int flen = attr->length - sizeof(*attr); + memcpy(pos, attr + 1, flen); + pos += flen; + } + } + + if (eap_len) + *eap_len = len; + + return eap; +} + + +int radius_msg_verify_msg_auth(struct radius_msg *msg, const u8 *secret, + size_t secret_len, const u8 *req_auth) +{ + u8 auth[MD5_MAC_LEN], orig[MD5_MAC_LEN]; + u8 orig_authenticator[16]; + struct radius_attr_hdr *attr = NULL; + int i; + + for (i = 0; i < msg->attr_used; i++) { + if (msg->attrs[i]->type == RADIUS_ATTR_MESSAGE_AUTHENTICATOR) { + if (attr != NULL) { + printf("Multiple Message-Authenticator " + "attributes in RADIUS message\n"); + return 1; + } + attr = msg->attrs[i]; + } + } + + if (attr == NULL) { + printf("No Message-Authenticator attribute found\n"); + return 1; + } + + memcpy(orig, attr + 1, MD5_MAC_LEN); + memset(attr + 1, 0, MD5_MAC_LEN); + if (req_auth) { + memcpy(orig_authenticator, msg->hdr->authenticator, + sizeof(orig_authenticator)); + memcpy(msg->hdr->authenticator, req_auth, + sizeof(msg->hdr->authenticator)); + } + hmac_md5(secret, secret_len, msg->buf, msg->buf_used, auth); + memcpy(attr + 1, orig, MD5_MAC_LEN); + if (req_auth) { + memcpy(msg->hdr->authenticator, orig_authenticator, + sizeof(orig_authenticator)); + } + + if (memcmp(orig, auth, MD5_MAC_LEN) != 0) { + printf("Invalid Message-Authenticator!\n"); + return 1; + } + + return 0; +} + + +int radius_msg_verify(struct radius_msg *msg, const u8 *secret, + size_t secret_len, struct radius_msg *sent_msg, int auth) +{ + const u8 *addr[4]; + size_t len[4]; + u8 hash[MD5_MAC_LEN]; + + if (sent_msg == NULL) { + printf("No matching Access-Request message found\n"); + return 1; + } + + if (auth && + radius_msg_verify_msg_auth(msg, secret, secret_len, + sent_msg->hdr->authenticator)) { + return 1; + } + + /* ResponseAuth = MD5(Code+ID+Length+RequestAuth+Attributes+Secret) */ + addr[0] = (u8 *) msg->hdr; + len[0] = 1 + 1 + 2; + addr[1] = sent_msg->hdr->authenticator; + len[1] = MD5_MAC_LEN; + addr[2] = (u8 *) (msg->hdr + 1); + len[2] = msg->buf_used - sizeof(*msg->hdr); + addr[3] = secret; + len[3] = secret_len; + md5_vector(4, addr, len, hash); + if (memcmp(hash, msg->hdr->authenticator, MD5_MAC_LEN) != 0) { + printf("Response Authenticator invalid!\n"); + return 1; + } + + return 0; + +} + + +int radius_msg_copy_attr(struct radius_msg *dst, struct radius_msg *src, + u8 type) +{ + struct radius_attr_hdr *attr = NULL; + int i; + + for (i = 0; i < src->attr_used; i++) { + if (src->attrs[i]->type == type) { + attr = src->attrs[i]; + break; + } + } + + if (attr == NULL) + return 0; + + if (!radius_msg_add_attr(dst, type, (u8 *) (attr + 1), + attr->length - sizeof(*attr))) + return -1; + + return 1; +} + + +/* Create Request Authenticator. The value should be unique over the lifetime + * of the shared secret between authenticator and authentication server. + * Use one-way MD5 hash calculated from current timestamp and some data given + * by the caller. */ +void radius_msg_make_authenticator(struct radius_msg *msg, + u8 *data, size_t len) +{ + struct timeval tv; + long int l; + const u8 *addr[3]; + size_t elen[3]; + + gettimeofday(&tv, NULL); + l = random(); + addr[0] = (u8 *) &tv; + elen[0] = sizeof(tv); + addr[1] = data; + elen[1] = len; + addr[2] = (u8 *) &l; + elen[2] = sizeof(l); + md5_vector(3, addr, elen, msg->hdr->authenticator); +} + + +/* Get Vendor-specific RADIUS Attribute from a parsed RADIUS message. + * Returns the Attribute payload and sets alen to indicate the length of the + * payload if a vendor attribute with subtype is found, otherwise returns NULL. + * The returned payload is allocated with malloc() and caller must free it. + */ +static u8 *radius_msg_get_vendor_attr(struct radius_msg *msg, u32 vendor, + u8 subtype, size_t *alen) +{ + u8 *data, *pos; + int i; + size_t len; + + if (msg == NULL) + return NULL; + + for (i = 0; i < msg->attr_used; i++) { + struct radius_attr_hdr *attr = msg->attrs[i]; + int left; + u32 vendor_id; + struct radius_attr_vendor *vhdr; + + if (attr->type != RADIUS_ATTR_VENDOR_SPECIFIC) + continue; + + left = attr->length - sizeof(*attr); + if (left < 4) + continue; + + pos = (u8 *) (attr + 1); + + memcpy(&vendor_id, pos, 4); + pos += 4; + left -= 4; + + if (ntohl(vendor_id) != vendor) + continue; + + while (left >= sizeof(*vhdr)) { + vhdr = (struct radius_attr_vendor *) pos; + if (vhdr->vendor_length > left || + vhdr->vendor_length < sizeof(*vhdr)) { + left = 0; + break; + } + if (vhdr->vendor_type != subtype) { + pos += vhdr->vendor_length; + left -= vhdr->vendor_length; + continue; + } + + len = vhdr->vendor_length - sizeof(*vhdr); + data = malloc(len); + if (data == NULL) + return NULL; + memcpy(data, pos + sizeof(*vhdr), len); + if (alen) + *alen = len; + return data; + } + } + + return NULL; +} + + +static u8 * decrypt_ms_key(const u8 *key, size_t len, + const u8 *req_authenticator, + const u8 *secret, size_t secret_len, size_t *reslen) +{ + u8 *plain, *ppos, *res; + const u8 *pos; + size_t left, plen; + u8 hash[MD5_MAC_LEN]; + int i, first = 1; + const u8 *addr[3]; + size_t elen[3]; + + /* key: 16-bit salt followed by encrypted key info */ + + if (len < 2 + 16) + return NULL; + + pos = key + 2; + left = len - 2; + if (left % 16) { + printf("Invalid ms key len %lu\n", (unsigned long) left); + return NULL; + } + + plen = left; + ppos = plain = malloc(plen); + if (plain == NULL) + return NULL; + + while (left > 0) { + /* b(1) = MD5(Secret + Request-Authenticator + Salt) + * b(i) = MD5(Secret + c(i - 1)) for i > 1 */ + + addr[0] = secret; + elen[0] = secret_len; + if (first) { + addr[1] = req_authenticator; + elen[1] = MD5_MAC_LEN; + addr[2] = key; + elen[2] = 2; /* Salt */ + } else { + addr[1] = pos - MD5_MAC_LEN; + elen[1] = MD5_MAC_LEN; + } + md5_vector(first ? 3 : 2, addr, elen, hash); + first = 0; + + for (i = 0; i < MD5_MAC_LEN; i++) + *ppos++ = *pos++ ^ hash[i]; + left -= MD5_MAC_LEN; + } + + if (plain[0] > plen - 1) { + printf("Failed to decrypt MPPE key\n"); + free(plain); + return NULL; + } + + res = malloc(plain[0]); + if (res == NULL) { + free(plain); + return NULL; + } + memcpy(res, plain + 1, plain[0]); + if (reslen) + *reslen = plain[0]; + free(plain); + return res; +} + + +static void encrypt_ms_key(const u8 *key, size_t key_len, u16 salt, + const u8 *req_authenticator, + const u8 *secret, size_t secret_len, + u8 *ebuf, size_t *elen) +{ + int i, len, first = 1; + u8 hash[MD5_MAC_LEN], saltbuf[2], *pos; + const u8 *addr[3]; + size_t _len[3]; + + saltbuf[0] = salt >> 8; + saltbuf[1] = salt; + + len = 1 + key_len; + if (len & 0x0f) { + len = (len & 0xf0) + 16; + } + memset(ebuf, 0, len); + ebuf[0] = key_len; + memcpy(ebuf + 1, key, key_len); + + *elen = len; + + pos = ebuf; + while (len > 0) { + /* b(1) = MD5(Secret + Request-Authenticator + Salt) + * b(i) = MD5(Secret + c(i - 1)) for i > 1 */ + addr[0] = secret; + _len[0] = secret_len; + if (first) { + addr[1] = req_authenticator; + _len[1] = MD5_MAC_LEN; + addr[2] = saltbuf; + _len[2] = sizeof(saltbuf); + } else { + addr[1] = pos - MD5_MAC_LEN; + _len[1] = MD5_MAC_LEN; + } + md5_vector(first ? 3 : 2, addr, _len, hash); + first = 0; + + for (i = 0; i < MD5_MAC_LEN; i++) + *pos++ ^= hash[i]; + + len -= MD5_MAC_LEN; + } +} + + +struct radius_ms_mppe_keys * +radius_msg_get_ms_keys(struct radius_msg *msg, struct radius_msg *sent_msg, + u8 *secret, size_t secret_len) +{ + u8 *key; + size_t keylen; + struct radius_ms_mppe_keys *keys; + + if (msg == NULL || sent_msg == NULL) + return NULL; + + keys = (struct radius_ms_mppe_keys *) malloc(sizeof(*keys)); + if (keys == NULL) + return NULL; + + memset(keys, 0, sizeof(*keys)); + + key = radius_msg_get_vendor_attr(msg, RADIUS_VENDOR_ID_MICROSOFT, + RADIUS_VENDOR_ATTR_MS_MPPE_SEND_KEY, + &keylen); + if (key) { + keys->send = decrypt_ms_key(key, keylen, + sent_msg->hdr->authenticator, + secret, secret_len, + &keys->send_len); + free(key); + } + + key = radius_msg_get_vendor_attr(msg, RADIUS_VENDOR_ID_MICROSOFT, + RADIUS_VENDOR_ATTR_MS_MPPE_RECV_KEY, + &keylen); + if (key) { + keys->recv = decrypt_ms_key(key, keylen, + sent_msg->hdr->authenticator, + secret, secret_len, + &keys->recv_len); + free(key); + } + + return keys; +} + + +struct radius_ms_mppe_keys * +radius_msg_get_cisco_keys(struct radius_msg *msg, struct radius_msg *sent_msg, + u8 *secret, size_t secret_len) +{ + u8 *key; + size_t keylen; + struct radius_ms_mppe_keys *keys; + + if (msg == NULL || sent_msg == NULL) + return NULL; + + keys = (struct radius_ms_mppe_keys *) malloc(sizeof(*keys)); + if (keys == NULL) + return NULL; + + memset(keys, 0, sizeof(*keys)); + + key = radius_msg_get_vendor_attr(msg, RADIUS_VENDOR_ID_CISCO, + RADIUS_CISCO_AV_PAIR, &keylen); + if (key && keylen == 51 && memcmp(key, "leap:session-key=", 17) == 0) { + keys->recv = decrypt_ms_key(key + 17, keylen - 17, + sent_msg->hdr->authenticator, + secret, secret_len, + &keys->recv_len); + } + free(key); + + return keys; +} + + +int radius_msg_add_mppe_keys(struct radius_msg *msg, + const u8 *req_authenticator, + const u8 *secret, size_t secret_len, + const u8 *send_key, size_t send_key_len, + const u8 *recv_key, size_t recv_key_len) +{ + struct radius_attr_hdr *attr; + u32 vendor_id = htonl(RADIUS_VENDOR_ID_MICROSOFT); + u8 *buf; + struct radius_attr_vendor *vhdr; + u8 *pos; + size_t elen; + int hlen; + u16 salt; + + hlen = sizeof(vendor_id) + sizeof(*vhdr) + 2; + + /* MS-MPPE-Send-Key */ + buf = malloc(hlen + send_key_len + 16); + if (buf == NULL) { + return 0; + } + pos = buf; + memcpy(pos, &vendor_id, sizeof(vendor_id)); + pos += sizeof(vendor_id); + vhdr = (struct radius_attr_vendor *) pos; + vhdr->vendor_type = RADIUS_VENDOR_ATTR_MS_MPPE_SEND_KEY; + pos = (u8 *) (vhdr + 1); + salt = random() | 0x8000; + *pos++ = salt >> 8; + *pos++ = salt; + encrypt_ms_key(send_key, send_key_len, salt, req_authenticator, secret, + secret_len, pos, &elen); + vhdr->vendor_length = hlen + elen - sizeof(vendor_id); + + attr = radius_msg_add_attr(msg, RADIUS_ATTR_VENDOR_SPECIFIC, + buf, hlen + elen); + free(buf); + if (attr == NULL) { + return 0; + } + + /* MS-MPPE-Recv-Key */ + buf = malloc(hlen + send_key_len + 16); + if (buf == NULL) { + return 0; + } + pos = buf; + memcpy(pos, &vendor_id, sizeof(vendor_id)); + pos += sizeof(vendor_id); + vhdr = (struct radius_attr_vendor *) pos; + vhdr->vendor_type = RADIUS_VENDOR_ATTR_MS_MPPE_RECV_KEY; + pos = (u8 *) (vhdr + 1); + salt ^= 1; + *pos++ = salt >> 8; + *pos++ = salt; + encrypt_ms_key(recv_key, recv_key_len, salt, req_authenticator, secret, + secret_len, pos, &elen); + vhdr->vendor_length = hlen + elen - sizeof(vendor_id); + + attr = radius_msg_add_attr(msg, RADIUS_ATTR_VENDOR_SPECIFIC, + buf, hlen + elen); + free(buf); + if (attr == NULL) { + return 0; + } + + return 1; +} + + +/* Add User-Password attribute to a RADIUS message and encrypt it as specified + * in RFC 2865, Chap. 5.2 */ +struct radius_attr_hdr * +radius_msg_add_attr_user_password(struct radius_msg *msg, + u8 *data, size_t data_len, + u8 *secret, size_t secret_len) +{ + u8 buf[128]; + int padlen, i, pos; + size_t buf_len; + const u8 *addr[2]; + size_t len[2]; + u8 hash[16]; + + if (data_len > 128) + return NULL; + + memcpy(buf, data, data_len); + buf_len = data_len; + + padlen = data_len % 16; + if (padlen) { + padlen = 16 - padlen; + memset(buf + data_len, 0, padlen); + buf_len += padlen; + } + + addr[0] = secret; + len[0] = secret_len; + addr[1] = msg->hdr->authenticator; + len[1] = 16; + md5_vector(2, addr, len, hash); + + for (i = 0; i < 16; i++) + buf[i] ^= hash[i]; + pos = 16; + + while (pos < buf_len) { + addr[0] = secret; + len[0] = secret_len; + addr[1] = &buf[pos - 16]; + len[1] = 16; + md5_vector(2, addr, len, hash); + + for (i = 0; i < 16; i++) + buf[pos + i] ^= hash[i]; + + pos += 16; + } + + return radius_msg_add_attr(msg, RADIUS_ATTR_USER_PASSWORD, + buf, buf_len); +} + + +int radius_msg_get_attr(struct radius_msg *msg, u8 type, u8 *buf, size_t len) +{ + int i; + struct radius_attr_hdr *attr = NULL; + size_t dlen; + + for (i = 0; i < msg->attr_used; i++) { + if (msg->attrs[i]->type == type) { + attr = msg->attrs[i]; + break; + } + } + + if (!attr) + return -1; + + dlen = attr->length - sizeof(*attr); + if (buf) + memcpy(buf, (attr + 1), dlen > len ? len : dlen); + return dlen; +} + + +int radius_msg_get_attr_ptr(struct radius_msg *msg, u8 type, u8 **buf, + size_t *len, const u8 *start) +{ + int i; + struct radius_attr_hdr *attr = NULL; + + for (i = 0; i < msg->attr_used; i++) { + if (msg->attrs[i]->type == type && + (start == NULL || (u8 *) msg->attrs[i] > start)) { + attr = msg->attrs[i]; + break; + } + } + + if (!attr) + return -1; + + *buf = (u8 *) (attr + 1); + *len = attr->length - sizeof(*attr); + return 0; +} + + +int radius_msg_count_attr(struct radius_msg *msg, u8 type, int min_len) +{ + int i, count; + + for (count = 0, i = 0; i < msg->attr_used; i++) { + if (msg->attrs[i]->type == type && + msg->attrs[i]->length >= + sizeof(struct radius_attr_hdr) + min_len) + count++; + } + + return count; +} diff --git a/contrib/hostapd-0.4.9/radius.h b/contrib/hostapd-0.4.9/radius.h new file mode 100644 index 0000000000..b9f8977f42 --- /dev/null +++ b/contrib/hostapd-0.4.9/radius.h @@ -0,0 +1,226 @@ +#ifndef RADIUS_H +#define RADIUS_H + +/* RFC 2865 - RADIUS */ + +struct radius_hdr { + u8 code; + u8 identifier; + u16 length; /* including this header */ + u8 authenticator[16]; + /* followed by length-20 octets of attributes */ +} __attribute__ ((packed)); + +enum { RADIUS_CODE_ACCESS_REQUEST = 1, + RADIUS_CODE_ACCESS_ACCEPT = 2, + RADIUS_CODE_ACCESS_REJECT = 3, + RADIUS_CODE_ACCOUNTING_REQUEST = 4, + RADIUS_CODE_ACCOUNTING_RESPONSE = 5, + RADIUS_CODE_ACCESS_CHALLENGE = 11, + RADIUS_CODE_STATUS_SERVER = 12, + RADIUS_CODE_STATUS_CLIENT = 13, + RADIUS_CODE_RESERVED = 255 +}; + +struct radius_attr_hdr { + u8 type; + u8 length; /* including this header */ + /* followed by length-2 octets of attribute value */ +} __attribute__ ((packed)); + +#define RADIUS_MAX_ATTR_LEN (255 - sizeof(struct radius_attr_hdr)) + +enum { RADIUS_ATTR_USER_NAME = 1, + RADIUS_ATTR_USER_PASSWORD = 2, + RADIUS_ATTR_NAS_IP_ADDRESS = 4, + RADIUS_ATTR_NAS_PORT = 5, + RADIUS_ATTR_FRAMED_MTU = 12, + RADIUS_ATTR_STATE = 24, + RADIUS_ATTR_CLASS = 25, + RADIUS_ATTR_VENDOR_SPECIFIC = 26, + RADIUS_ATTR_SESSION_TIMEOUT = 27, + RADIUS_ATTR_IDLE_TIMEOUT = 28, + RADIUS_ATTR_TERMINATION_ACTION = 29, + RADIUS_ATTR_CALLED_STATION_ID = 30, + RADIUS_ATTR_CALLING_STATION_ID = 31, + RADIUS_ATTR_NAS_IDENTIFIER = 32, + RADIUS_ATTR_ACCT_STATUS_TYPE = 40, + RADIUS_ATTR_ACCT_DELAY_TIME = 41, + RADIUS_ATTR_ACCT_INPUT_OCTETS = 42, + RADIUS_ATTR_ACCT_OUTPUT_OCTETS = 43, + RADIUS_ATTR_ACCT_SESSION_ID = 44, + RADIUS_ATTR_ACCT_AUTHENTIC = 45, + RADIUS_ATTR_ACCT_SESSION_TIME = 46, + RADIUS_ATTR_ACCT_INPUT_PACKETS = 47, + RADIUS_ATTR_ACCT_OUTPUT_PACKETS = 48, + RADIUS_ATTR_ACCT_TERMINATE_CAUSE = 49, + RADIUS_ATTR_ACCT_MULTI_SESSION_ID = 50, + RADIUS_ATTR_ACCT_LINK_COUNT = 51, + RADIUS_ATTR_ACCT_INPUT_GIGAWORDS = 52, + RADIUS_ATTR_ACCT_OUTPUT_GIGAWORDS = 53, + RADIUS_ATTR_EVENT_TIMESTAMP = 55, + RADIUS_ATTR_NAS_PORT_TYPE = 61, + RADIUS_ATTR_CONNECT_INFO = 77, + RADIUS_ATTR_EAP_MESSAGE = 79, + RADIUS_ATTR_MESSAGE_AUTHENTICATOR = 80, + RADIUS_ATTR_ACCT_INTERIM_INTERVAL = 85, + RADIUS_ATTR_NAS_IPV6_ADDRESS = 95 +}; + + +/* Termination-Action */ +#define RADIUS_TERMINATION_ACTION_DEFAULT 0 +#define RADIUS_TERMINATION_ACTION_RADIUS_REQUEST 1 + +/* NAS-Port-Type */ +#define RADIUS_NAS_PORT_TYPE_IEEE_802_11 19 + +/* Acct-Status-Type */ +#define RADIUS_ACCT_STATUS_TYPE_START 1 +#define RADIUS_ACCT_STATUS_TYPE_STOP 2 +#define RADIUS_ACCT_STATUS_TYPE_INTERIM_UPDATE 3 +#define RADIUS_ACCT_STATUS_TYPE_ACCOUNTING_ON 7 +#define RADIUS_ACCT_STATUS_TYPE_ACCOUNTING_OFF 8 + +/* Acct-Authentic */ +#define RADIUS_ACCT_AUTHENTIC_RADIUS 1 +#define RADIUS_ACCT_AUTHENTIC_LOCAL 2 +#define RADIUS_ACCT_AUTHENTIC_REMOTE 3 + +/* Acct-Terminate-Cause */ +#define RADIUS_ACCT_TERMINATE_CAUSE_USER_REQUEST 1 +#define RADIUS_ACCT_TERMINATE_CAUSE_LOST_CARRIER 2 +#define RADIUS_ACCT_TERMINATE_CAUSE_LOST_SERVICE 3 +#define RADIUS_ACCT_TERMINATE_CAUSE_IDLE_TIMEOUT 4 +#define RADIUS_ACCT_TERMINATE_CAUSE_SESSION_TIMEOUT 5 +#define RADIUS_ACCT_TERMINATE_CAUSE_ADMIN_RESET 6 +#define RADIUS_ACCT_TERMINATE_CAUSE_ADMIN_REBOOT 7 +#define RADIUS_ACCT_TERMINATE_CAUSE_PORT_ERROR 8 +#define RADIUS_ACCT_TERMINATE_CAUSE_NAS_ERROR 9 +#define RADIUS_ACCT_TERMINATE_CAUSE_NAS_REQUEST 10 +#define RADIUS_ACCT_TERMINATE_CAUSE_NAS_REBOOT 11 +#define RADIUS_ACCT_TERMINATE_CAUSE_PORT_UNNEEDED 12 +#define RADIUS_ACCT_TERMINATE_CAUSE_PORT_PREEMPTED 13 +#define RADIUS_ACCT_TERMINATE_CAUSE_PORT_SUSPENDED 14 +#define RADIUS_ACCT_TERMINATE_CAUSE_SERVICE_UNAVAILABLE 15 +#define RADIUS_ACCT_TERMINATE_CAUSE_CALLBACK 16 +#define RADIUS_ACCT_TERMINATE_CAUSE_USER_ERROR 17 +#define RADIUS_ACCT_TERMINATE_CAUSE_HOST_REQUEST 18 + + +struct radius_attr_vendor { + u8 vendor_type; + u8 vendor_length; +} __attribute__ ((packed)); + +#define RADIUS_VENDOR_ID_CISCO 9 +#define RADIUS_CISCO_AV_PAIR 1 + +/* RFC 2548 - Microsoft Vendor-specific RADIUS Attributes */ +#define RADIUS_VENDOR_ID_MICROSOFT 311 + +enum { RADIUS_VENDOR_ATTR_MS_MPPE_SEND_KEY = 16, + RADIUS_VENDOR_ATTR_MS_MPPE_RECV_KEY = 17 +}; + +struct radius_ms_mppe_keys { + u8 *send; + size_t send_len; + u8 *recv; + size_t recv_len; +}; + + +/* RADIUS message structure for new and parsed messages */ +struct radius_msg { + unsigned char *buf; + size_t buf_size; /* total size allocated for buf */ + size_t buf_used; /* bytes used in buf */ + + struct radius_hdr *hdr; + + struct radius_attr_hdr **attrs; /* array of pointers to attributes */ + size_t attr_size; /* total size of the attribute pointer array */ + size_t attr_used; /* total number of attributes in the array */ +}; + + +/* Default size to be allocated for new RADIUS messages */ +#define RADIUS_DEFAULT_MSG_SIZE 1024 + +/* Default size to be allocated for attribute array */ +#define RADIUS_DEFAULT_ATTR_COUNT 16 + + +/* MAC address ASCII format for IEEE 802.1X use + * (draft-congdon-radius-8021x-20.txt) */ +#define RADIUS_802_1X_ADDR_FORMAT "%02X-%02X-%02X-%02X-%02X-%02X" +/* MAC address ASCII format for non-802.1X use */ +#define RADIUS_ADDR_FORMAT "%02x%02x%02x%02x%02x%02x" + +struct radius_msg *radius_msg_new(u8 code, u8 identifier); +int radius_msg_initialize(struct radius_msg *msg, size_t init_len); +void radius_msg_set_hdr(struct radius_msg *msg, u8 code, u8 identifier); +void radius_msg_free(struct radius_msg *msg); +void radius_msg_dump(struct radius_msg *msg); +int radius_msg_finish(struct radius_msg *msg, u8 *secret, size_t secret_len); +int radius_msg_finish_srv(struct radius_msg *msg, const u8 *secret, + size_t secret_len, const u8 *req_authenticator); +void radius_msg_finish_acct(struct radius_msg *msg, u8 *secret, + size_t secret_len); +struct radius_attr_hdr *radius_msg_add_attr(struct radius_msg *msg, u8 type, + const u8 *data, size_t data_len); +struct radius_msg *radius_msg_parse(const u8 *data, size_t len); +int radius_msg_add_eap(struct radius_msg *msg, const u8 *data, + size_t data_len); +u8 *radius_msg_get_eap(struct radius_msg *msg, size_t *len); +int radius_msg_verify(struct radius_msg *msg, const u8 *secret, + size_t secret_len, struct radius_msg *sent_msg, + int auth); +int radius_msg_verify_msg_auth(struct radius_msg *msg, const u8 *secret, + size_t secret_len, const u8 *req_auth); +int radius_msg_copy_attr(struct radius_msg *dst, struct radius_msg *src, + u8 type); +void radius_msg_make_authenticator(struct radius_msg *msg, + u8 *data, size_t len); +struct radius_ms_mppe_keys * +radius_msg_get_ms_keys(struct radius_msg *msg, struct radius_msg *sent_msg, + u8 *secret, size_t secret_len); +struct radius_ms_mppe_keys * +radius_msg_get_cisco_keys(struct radius_msg *msg, struct radius_msg *sent_msg, + u8 *secret, size_t secret_len); +int radius_msg_add_mppe_keys(struct radius_msg *msg, + const u8 *req_authenticator, + const u8 *secret, size_t secret_len, + const u8 *send_key, size_t send_key_len, + const u8 *recv_key, size_t recv_key_len); +struct radius_attr_hdr * +radius_msg_add_attr_user_password(struct radius_msg *msg, + u8 *data, size_t data_len, + u8 *secret, size_t secret_len); +int radius_msg_get_attr(struct radius_msg *msg, u8 type, u8 *buf, size_t len); + +static inline int radius_msg_add_attr_int32(struct radius_msg *msg, u8 type, + u32 value) +{ + u32 val = htonl(value); + return radius_msg_add_attr(msg, type, (u8 *) &val, 4) != NULL; +} + +static inline int radius_msg_get_attr_int32(struct radius_msg *msg, u8 type, + u32 *value) +{ + u32 val; + int res; + res = radius_msg_get_attr(msg, type, (u8 *) &val, 4); + if (res != 4) + return -1; + + *value = ntohl(val); + return 0; +} +int radius_msg_get_attr_ptr(struct radius_msg *msg, u8 type, u8 **buf, + size_t *len, const u8 *start); +int radius_msg_count_attr(struct radius_msg *msg, u8 type, int min_len); + +#endif /* RADIUS_H */ diff --git a/contrib/hostapd-0.4.9/radius_client.c b/contrib/hostapd-0.4.9/radius_client.c new file mode 100644 index 0000000000..690208445b --- /dev/null +++ b/contrib/hostapd-0.4.9/radius_client.c @@ -0,0 +1,1116 @@ +/* + * Host AP (software wireless LAN access point) user space daemon for + * Host AP kernel driver / RADIUS client + * Copyright (c) 2002-2005, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#ifndef CONFIG_NATIVE_WINDOWS +#include +#include +#include +#endif /* CONFIG_NATIVE_WINDOWS */ + +#include "hostapd.h" +#include "radius.h" +#include "radius_client.h" +#include "eloop.h" + +/* Defaults for RADIUS retransmit values (exponential backoff) */ +#define RADIUS_CLIENT_FIRST_WAIT 3 /* seconds */ +#define RADIUS_CLIENT_MAX_WAIT 120 /* seconds */ +#define RADIUS_CLIENT_MAX_RETRIES 10 /* maximum number of retransmit attempts + * before entry is removed from retransmit + * list */ +#define RADIUS_CLIENT_MAX_ENTRIES 30 /* maximum number of entries in retransmit + * list (oldest will be removed, if this + * limit is exceeded) */ +#define RADIUS_CLIENT_NUM_FAILOVER 4 /* try to change RADIUS server after this + * many failed retry attempts */ + + +struct radius_rx_handler { + RadiusRxResult (*handler)(struct radius_msg *msg, + struct radius_msg *req, + u8 *shared_secret, size_t shared_secret_len, + void *data); + void *data; +}; + + +/* RADIUS message retransmit list */ +struct radius_msg_list { + u8 addr[ETH_ALEN]; /* STA/client address; used to find RADIUS messages + * for the same STA. */ + struct radius_msg *msg; + RadiusType msg_type; + time_t first_try; + time_t next_try; + int attempts; + int next_wait; + struct timeval last_attempt; + + u8 *shared_secret; + size_t shared_secret_len; + + /* TODO: server config with failover to backup server(s) */ + + struct radius_msg_list *next; +}; + + +struct radius_client_data { + void *ctx; + struct hostapd_radius_servers *conf; + + int auth_serv_sock; /* socket for authentication RADIUS messages */ + int acct_serv_sock; /* socket for accounting RADIUS messages */ + int auth_serv_sock6; + int acct_serv_sock6; + int auth_sock; /* currently used socket */ + int acct_sock; /* currently used socket */ + + struct radius_rx_handler *auth_handlers; + size_t num_auth_handlers; + struct radius_rx_handler *acct_handlers; + size_t num_acct_handlers; + + struct radius_msg_list *msgs; + size_t num_msgs; + + u8 next_radius_identifier; +}; + + +static int +radius_change_server(struct radius_client_data *radius, + struct hostapd_radius_server *nserv, + struct hostapd_radius_server *oserv, + int sock, int sock6, int auth); +static int radius_client_init_acct(struct radius_client_data *radius); +static int radius_client_init_auth(struct radius_client_data *radius); + + +static void radius_client_msg_free(struct radius_msg_list *req) +{ + radius_msg_free(req->msg); + free(req->msg); + free(req); +} + + +int radius_client_register(struct radius_client_data *radius, + RadiusType msg_type, + RadiusRxResult (*handler)(struct radius_msg *msg, + struct radius_msg *req, + u8 *shared_secret, + size_t shared_secret_len, + void *data), + void *data) +{ + struct radius_rx_handler **handlers, *newh; + size_t *num; + + if (msg_type == RADIUS_ACCT) { + handlers = &radius->acct_handlers; + num = &radius->num_acct_handlers; + } else { + handlers = &radius->auth_handlers; + num = &radius->num_auth_handlers; + } + + newh = (struct radius_rx_handler *) + realloc(*handlers, + (*num + 1) * sizeof(struct radius_rx_handler)); + if (newh == NULL) + return -1; + + newh[*num].handler = handler; + newh[*num].data = data; + (*num)++; + *handlers = newh; + + return 0; +} + + +static void radius_client_handle_send_error(struct radius_client_data *radius, + int s, RadiusType msg_type) +{ +#ifndef CONFIG_NATIVE_WINDOWS + int _errno = errno; + perror("send[RADIUS]"); + if (_errno == ENOTCONN || _errno == EDESTADDRREQ || _errno == EINVAL) { + hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, + HOSTAPD_LEVEL_INFO, + "Send failed - maybe interface status changed -" + " try to connect again"); + eloop_unregister_read_sock(s); + close(s); + if (msg_type == RADIUS_ACCT || msg_type == RADIUS_ACCT_INTERIM) + radius_client_init_acct(radius); + else + radius_client_init_auth(radius); + } +#endif /* CONFIG_NATIVE_WINDOWS */ +} + + +static int radius_client_retransmit(struct radius_client_data *radius, + struct radius_msg_list *entry, time_t now) +{ + struct hostapd_radius_servers *conf = radius->conf; + int s; + + if (entry->msg_type == RADIUS_ACCT || + entry->msg_type == RADIUS_ACCT_INTERIM) { + s = radius->acct_sock; + if (entry->attempts == 0) + conf->acct_server->requests++; + else { + conf->acct_server->timeouts++; + conf->acct_server->retransmissions++; + } + } else { + s = radius->auth_sock; + if (entry->attempts == 0) + conf->auth_server->requests++; + else { + conf->auth_server->timeouts++; + conf->auth_server->retransmissions++; + } + } + + /* retransmit; remove entry if too many attempts */ + entry->attempts++; + hostapd_logger(radius->ctx, entry->addr, HOSTAPD_MODULE_RADIUS, + HOSTAPD_LEVEL_DEBUG, "Resending RADIUS message (id=%d)", + entry->msg->hdr->identifier); + + gettimeofday(&entry->last_attempt, NULL); + if (send(s, entry->msg->buf, entry->msg->buf_used, 0) < 0) + radius_client_handle_send_error(radius, s, entry->msg_type); + + entry->next_try = now + entry->next_wait; + entry->next_wait *= 2; + if (entry->next_wait > RADIUS_CLIENT_MAX_WAIT) + entry->next_wait = RADIUS_CLIENT_MAX_WAIT; + if (entry->attempts >= RADIUS_CLIENT_MAX_RETRIES) { + printf("Removing un-ACKed RADIUS message due to too many " + "failed retransmit attempts\n"); + return 1; + } + + return 0; +} + + +static void radius_client_timer(void *eloop_ctx, void *timeout_ctx) +{ + struct radius_client_data *radius = eloop_ctx; + struct hostapd_radius_servers *conf = radius->conf; + time_t now, first; + struct radius_msg_list *entry, *prev, *tmp; + int auth_failover = 0, acct_failover = 0; + char abuf[50]; + + entry = radius->msgs; + if (!entry) + return; + + time(&now); + first = 0; + + prev = NULL; + while (entry) { + if (now >= entry->next_try && + radius_client_retransmit(radius, entry, now)) { + if (prev) + prev->next = entry->next; + else + radius->msgs = entry->next; + + tmp = entry; + entry = entry->next; + radius_client_msg_free(tmp); + radius->num_msgs--; + continue; + } + + if (entry->attempts > RADIUS_CLIENT_NUM_FAILOVER) { + if (entry->msg_type == RADIUS_ACCT || + entry->msg_type == RADIUS_ACCT_INTERIM) + acct_failover++; + else + auth_failover++; + } + + if (first == 0 || entry->next_try < first) + first = entry->next_try; + + prev = entry; + entry = entry->next; + } + + if (radius->msgs) { + if (first < now) + first = now; + eloop_register_timeout(first - now, 0, + radius_client_timer, radius, NULL); + hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, + HOSTAPD_LEVEL_DEBUG, "Next RADIUS client " + "retransmit in %ld seconds", + (long int) (first - now)); + } + + if (auth_failover && conf->num_auth_servers > 1) { + struct hostapd_radius_server *next, *old; + old = conf->auth_server; + hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, + HOSTAPD_LEVEL_NOTICE, + "No response from Authentication server " + "%s:%d - failover", + hostapd_ip_txt(&old->addr, abuf, sizeof(abuf)), + old->port); + + for (entry = radius->msgs; entry; entry = entry->next) { + if (entry->msg_type == RADIUS_AUTH) + old->timeouts++; + } + + next = old + 1; + if (next > &(conf->auth_servers[conf->num_auth_servers - 1])) + next = conf->auth_servers; + conf->auth_server = next; + radius_change_server(radius, next, old, + radius->auth_serv_sock, + radius->auth_serv_sock6, 1); + } + + if (acct_failover && conf->num_acct_servers > 1) { + struct hostapd_radius_server *next, *old; + old = conf->acct_server; + hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, + HOSTAPD_LEVEL_NOTICE, + "No response from Accounting server " + "%s:%d - failover", + hostapd_ip_txt(&old->addr, abuf, sizeof(abuf)), + old->port); + + for (entry = radius->msgs; entry; entry = entry->next) { + if (entry->msg_type == RADIUS_ACCT || + entry->msg_type == RADIUS_ACCT_INTERIM) + old->timeouts++; + } + + next = old + 1; + if (next > &conf->acct_servers[conf->num_acct_servers - 1]) + next = conf->acct_servers; + conf->acct_server = next; + radius_change_server(radius, next, old, + radius->acct_serv_sock, + radius->acct_serv_sock6, 0); + } +} + + +static void radius_client_update_timeout(struct radius_client_data *radius) +{ + time_t now, first; + struct radius_msg_list *entry; + + eloop_cancel_timeout(radius_client_timer, radius, NULL); + + if (radius->msgs == NULL) { + return; + } + + first = 0; + for (entry = radius->msgs; entry; entry = entry->next) { + if (first == 0 || entry->next_try < first) + first = entry->next_try; + } + + time(&now); + if (first < now) + first = now; + eloop_register_timeout(first - now, 0, radius_client_timer, radius, + NULL); + hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, + HOSTAPD_LEVEL_DEBUG, "Next RADIUS client retransmit in" + " %ld seconds\n", (long int) (first - now)); +} + + +static void radius_client_list_add(struct radius_client_data *radius, + struct radius_msg *msg, + RadiusType msg_type, u8 *shared_secret, + size_t shared_secret_len, u8 *addr) +{ + struct radius_msg_list *entry, *prev; + + if (eloop_terminated()) { + /* No point in adding entries to retransmit queue since event + * loop has already been terminated. */ + radius_msg_free(msg); + free(msg); + return; + } + + entry = malloc(sizeof(*entry)); + if (entry == NULL) { + printf("Failed to add RADIUS packet into retransmit list\n"); + radius_msg_free(msg); + free(msg); + return; + } + + memset(entry, 0, sizeof(*entry)); + if (addr) + memcpy(entry->addr, addr, ETH_ALEN); + entry->msg = msg; + entry->msg_type = msg_type; + entry->shared_secret = shared_secret; + entry->shared_secret_len = shared_secret_len; + time(&entry->first_try); + entry->next_try = entry->first_try + RADIUS_CLIENT_FIRST_WAIT; + entry->attempts = 1; + gettimeofday(&entry->last_attempt, NULL); + entry->next_wait = RADIUS_CLIENT_FIRST_WAIT * 2; + entry->next = radius->msgs; + radius->msgs = entry; + radius_client_update_timeout(radius); + + if (radius->num_msgs >= RADIUS_CLIENT_MAX_ENTRIES) { + printf("Removing the oldest un-ACKed RADIUS packet due to " + "retransmit list limits.\n"); + prev = NULL; + while (entry->next) { + prev = entry; + entry = entry->next; + } + if (prev) { + prev->next = NULL; + radius_client_msg_free(entry); + } + } else + radius->num_msgs++; +} + + +static void radius_client_list_del(struct radius_client_data *radius, + RadiusType msg_type, u8 *addr) +{ + struct radius_msg_list *entry, *prev, *tmp; + + if (addr == NULL) + return; + + entry = radius->msgs; + prev = NULL; + while (entry) { + if (entry->msg_type == msg_type && + memcmp(entry->addr, addr, ETH_ALEN) == 0) { + if (prev) + prev->next = entry->next; + else + radius->msgs = entry->next; + tmp = entry; + entry = entry->next; + hostapd_logger(radius->ctx, addr, + HOSTAPD_MODULE_RADIUS, + HOSTAPD_LEVEL_DEBUG, + "Removing matching RADIUS message"); + radius_client_msg_free(tmp); + radius->num_msgs--; + continue; + } + prev = entry; + entry = entry->next; + } +} + + +int radius_client_send(struct radius_client_data *radius, + struct radius_msg *msg, RadiusType msg_type, u8 *addr) +{ + struct hostapd_radius_servers *conf = radius->conf; + u8 *shared_secret; + size_t shared_secret_len; + char *name; + int s, res; + + if (msg_type == RADIUS_ACCT_INTERIM) { + /* Remove any pending interim acct update for the same STA. */ + radius_client_list_del(radius, msg_type, addr); + } + + if (msg_type == RADIUS_ACCT || msg_type == RADIUS_ACCT_INTERIM) { + shared_secret = conf->acct_server->shared_secret; + shared_secret_len = conf->acct_server->shared_secret_len; + radius_msg_finish_acct(msg, shared_secret, shared_secret_len); + name = "accounting"; + s = radius->acct_sock; + conf->acct_server->requests++; + } else { + shared_secret = conf->auth_server->shared_secret; + shared_secret_len = conf->auth_server->shared_secret_len; + radius_msg_finish(msg, shared_secret, shared_secret_len); + name = "authentication"; + s = radius->auth_sock; + conf->auth_server->requests++; + } + + hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, + HOSTAPD_LEVEL_DEBUG, "Sending RADIUS message to %s " + "server", name); + if (conf->msg_dumps) + radius_msg_dump(msg); + + res = send(s, msg->buf, msg->buf_used, 0); + if (res < 0) + radius_client_handle_send_error(radius, s, msg_type); + + radius_client_list_add(radius, msg, msg_type, shared_secret, + shared_secret_len, addr); + + return res; +} + + +static void radius_client_receive(int sock, void *eloop_ctx, void *sock_ctx) +{ + struct radius_client_data *radius = eloop_ctx; + struct hostapd_radius_servers *conf = radius->conf; + RadiusType msg_type = (RadiusType) sock_ctx; + int len, i, roundtrip; + unsigned char buf[3000]; + struct radius_msg *msg; + struct radius_rx_handler *handlers; + size_t num_handlers; + struct radius_msg_list *req, *prev_req; + struct timeval tv; + struct hostapd_radius_server *rconf; + int invalid_authenticator = 0; + + if (msg_type == RADIUS_ACCT) { + handlers = radius->acct_handlers; + num_handlers = radius->num_acct_handlers; + rconf = conf->acct_server; + } else { + handlers = radius->auth_handlers; + num_handlers = radius->num_auth_handlers; + rconf = conf->auth_server; + } + + len = recv(sock, buf, sizeof(buf), MSG_DONTWAIT); + if (len < 0) { + perror("recv[RADIUS]"); + return; + } + hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, + HOSTAPD_LEVEL_DEBUG, "Received %d bytes from RADIUS " + "server", len); + if (len == sizeof(buf)) { + printf("Possibly too long UDP frame for our buffer - " + "dropping it\n"); + return; + } + + msg = radius_msg_parse(buf, len); + if (msg == NULL) { + printf("Parsing incoming RADIUS frame failed\n"); + rconf->malformed_responses++; + return; + } + + hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, + HOSTAPD_LEVEL_DEBUG, "Received RADIUS message"); + if (conf->msg_dumps) + radius_msg_dump(msg); + + switch (msg->hdr->code) { + case RADIUS_CODE_ACCESS_ACCEPT: + rconf->access_accepts++; + break; + case RADIUS_CODE_ACCESS_REJECT: + rconf->access_rejects++; + break; + case RADIUS_CODE_ACCESS_CHALLENGE: + rconf->access_challenges++; + break; + case RADIUS_CODE_ACCOUNTING_RESPONSE: + rconf->responses++; + break; + } + + prev_req = NULL; + req = radius->msgs; + while (req) { + /* TODO: also match by src addr:port of the packet when using + * alternative RADIUS servers (?) */ + if ((req->msg_type == msg_type || + (req->msg_type == RADIUS_ACCT_INTERIM && + msg_type == RADIUS_ACCT)) && + req->msg->hdr->identifier == msg->hdr->identifier) + break; + + prev_req = req; + req = req->next; + } + + if (req == NULL) { + hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, + HOSTAPD_LEVEL_DEBUG, + "No matching RADIUS request found (type=%d " + "id=%d) - dropping packet", + msg_type, msg->hdr->identifier); + goto fail; + } + + gettimeofday(&tv, NULL); + roundtrip = (tv.tv_sec - req->last_attempt.tv_sec) * 100 + + (tv.tv_usec - req->last_attempt.tv_usec) / 10000; + hostapd_logger(radius->ctx, req->addr, HOSTAPD_MODULE_RADIUS, + HOSTAPD_LEVEL_DEBUG, + "Received RADIUS packet matched with a pending " + "request, round trip time %d.%02d sec", + roundtrip / 100, roundtrip % 100); + rconf->round_trip_time = roundtrip; + + /* Remove ACKed RADIUS packet from retransmit list */ + if (prev_req) + prev_req->next = req->next; + else + radius->msgs = req->next; + radius->num_msgs--; + + for (i = 0; i < num_handlers; i++) { + RadiusRxResult res; + res = handlers[i].handler(msg, req->msg, req->shared_secret, + req->shared_secret_len, + handlers[i].data); + switch (res) { + case RADIUS_RX_PROCESSED: + radius_msg_free(msg); + free(msg); + /* continue */ + case RADIUS_RX_QUEUED: + radius_client_msg_free(req); + return; + case RADIUS_RX_INVALID_AUTHENTICATOR: + invalid_authenticator++; + /* continue */ + case RADIUS_RX_UNKNOWN: + /* continue with next handler */ + break; + } + } + + if (invalid_authenticator) + rconf->bad_authenticators++; + else + rconf->unknown_types++; + hostapd_logger(radius->ctx, req->addr, HOSTAPD_MODULE_RADIUS, + HOSTAPD_LEVEL_DEBUG, "No RADIUS RX handler found " + "(type=%d code=%d id=%d)%s - dropping packet", + msg_type, msg->hdr->code, msg->hdr->identifier, + invalid_authenticator ? " [INVALID AUTHENTICATOR]" : + ""); + radius_client_msg_free(req); + + fail: + radius_msg_free(msg); + free(msg); +} + + +u8 radius_client_get_id(struct radius_client_data *radius) +{ + struct radius_msg_list *entry, *prev, *remove; + u8 id = radius->next_radius_identifier++; + + /* remove entries with matching id from retransmit list to avoid + * using new reply from the RADIUS server with an old request */ + entry = radius->msgs; + prev = NULL; + while (entry) { + if (entry->msg->hdr->identifier == id) { + hostapd_logger(radius->ctx, entry->addr, + HOSTAPD_MODULE_RADIUS, + HOSTAPD_LEVEL_DEBUG, + "Removing pending RADIUS message, " + "since its id (%d) is reused", id); + if (prev) + prev->next = entry->next; + else + radius->msgs = entry->next; + remove = entry; + } else { + remove = NULL; + prev = entry; + } + entry = entry->next; + + if (remove) + radius_client_msg_free(remove); + } + + return id; +} + + +void radius_client_flush(struct radius_client_data *radius) +{ + struct radius_msg_list *entry, *prev; + + if (!radius) + return; + + eloop_cancel_timeout(radius_client_timer, radius, NULL); + + entry = radius->msgs; + radius->msgs = NULL; + radius->num_msgs = 0; + while (entry) { + prev = entry; + entry = entry->next; + radius_client_msg_free(prev); + } +} + + +static int +radius_change_server(struct radius_client_data *radius, + struct hostapd_radius_server *nserv, + struct hostapd_radius_server *oserv, + int sock, int sock6, int auth) +{ + struct sockaddr_in serv; +#ifdef CONFIG_IPV6 + struct sockaddr_in6 serv6; +#endif /* CONFIG_IPV6 */ + struct sockaddr *addr; + socklen_t addrlen; + char abuf[50]; + int sel_sock; + + hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, + HOSTAPD_LEVEL_INFO, + "%s server %s:%d", + auth ? "Authentication" : "Accounting", + hostapd_ip_txt(&nserv->addr, abuf, sizeof(abuf)), + nserv->port); + + if (!oserv || nserv->shared_secret_len != oserv->shared_secret_len || + memcmp(nserv->shared_secret, oserv->shared_secret, + nserv->shared_secret_len) != 0) { + /* Pending RADIUS packets used different shared + * secret, so they would need to be modified. Could + * update all message authenticators and + * User-Passwords, etc. and retry with new server. For + * now, just drop all pending packets. */ + radius_client_flush(radius); + } else { + /* Reset retry counters for the new server */ + struct radius_msg_list *entry; + entry = radius->msgs; + while (entry) { + entry->next_try = entry->first_try + + RADIUS_CLIENT_FIRST_WAIT; + entry->attempts = 0; + entry->next_wait = RADIUS_CLIENT_FIRST_WAIT * 2; + entry = entry->next; + } + if (radius->msgs) { + eloop_cancel_timeout(radius_client_timer, radius, + NULL); + eloop_register_timeout(RADIUS_CLIENT_FIRST_WAIT, 0, + radius_client_timer, radius, + NULL); + } + } + + switch (nserv->addr.af) { + case AF_INET: + memset(&serv, 0, sizeof(serv)); + serv.sin_family = AF_INET; + serv.sin_addr.s_addr = nserv->addr.u.v4.s_addr; + serv.sin_port = htons(nserv->port); + addr = (struct sockaddr *) &serv; + addrlen = sizeof(serv); + sel_sock = sock; + break; +#ifdef CONFIG_IPV6 + case AF_INET6: + memset(&serv6, 0, sizeof(serv6)); + serv6.sin6_family = AF_INET6; + memcpy(&serv6.sin6_addr, &nserv->addr.u.v6, + sizeof(struct in6_addr)); + serv6.sin6_port = htons(nserv->port); + addr = (struct sockaddr *) &serv6; + addrlen = sizeof(serv6); + sel_sock = sock6; + break; +#endif /* CONFIG_IPV6 */ + default: + return -1; + } + + if (connect(sel_sock, addr, addrlen) < 0) { + perror("connect[radius]"); + return -1; + } + + if (auth) + radius->auth_sock = sel_sock; + else + radius->acct_sock = sel_sock; + + return 0; +} + + +static void radius_retry_primary_timer(void *eloop_ctx, void *timeout_ctx) +{ + struct radius_client_data *radius = eloop_ctx; + struct hostapd_radius_servers *conf = radius->conf; + struct hostapd_radius_server *oserv; + + if (radius->auth_sock >= 0 && conf->auth_servers && + conf->auth_server != conf->auth_servers) { + oserv = conf->auth_server; + conf->auth_server = conf->auth_servers; + radius_change_server(radius, conf->auth_server, oserv, + radius->auth_serv_sock, + radius->auth_serv_sock6, 1); + } + + if (radius->acct_sock >= 0 && conf->acct_servers && + conf->acct_server != conf->acct_servers) { + oserv = conf->acct_server; + conf->acct_server = conf->acct_servers; + radius_change_server(radius, conf->acct_server, oserv, + radius->acct_serv_sock, + radius->acct_serv_sock6, 0); + } + + if (conf->retry_primary_interval) + eloop_register_timeout(conf->retry_primary_interval, 0, + radius_retry_primary_timer, radius, + NULL); +} + + +static int radius_client_init_auth(struct radius_client_data *radius) +{ + struct hostapd_radius_servers *conf = radius->conf; + int ok = 0; + + radius->auth_serv_sock = socket(PF_INET, SOCK_DGRAM, 0); + if (radius->auth_serv_sock < 0) + perror("socket[PF_INET,SOCK_DGRAM]"); + else + ok++; + +#ifdef CONFIG_IPV6 + radius->auth_serv_sock6 = socket(PF_INET6, SOCK_DGRAM, 0); + if (radius->auth_serv_sock6 < 0) + perror("socket[PF_INET6,SOCK_DGRAM]"); + else + ok++; +#endif /* CONFIG_IPV6 */ + + if (ok == 0) + return -1; + + radius_change_server(radius, conf->auth_server, NULL, + radius->auth_serv_sock, radius->auth_serv_sock6, + 1); + + if (radius->auth_serv_sock >= 0 && + eloop_register_read_sock(radius->auth_serv_sock, + radius_client_receive, radius, + (void *) RADIUS_AUTH)) { + printf("Could not register read socket for authentication " + "server\n"); + return -1; + } + +#ifdef CONFIG_IPV6 + if (radius->auth_serv_sock6 >= 0 && + eloop_register_read_sock(radius->auth_serv_sock6, + radius_client_receive, radius, + (void *) RADIUS_AUTH)) { + printf("Could not register read socket for authentication " + "server\n"); + return -1; + } +#endif /* CONFIG_IPV6 */ + + return 0; +} + + +static int radius_client_init_acct(struct radius_client_data *radius) +{ + struct hostapd_radius_servers *conf = radius->conf; + int ok = 0; + + radius->acct_serv_sock = socket(PF_INET, SOCK_DGRAM, 0); + if (radius->acct_serv_sock < 0) + perror("socket[PF_INET,SOCK_DGRAM]"); + else + ok++; + + radius_change_server(radius, conf->acct_server, NULL, + radius->acct_serv_sock, radius->acct_serv_sock6, + 0); + + if (radius->acct_serv_sock >= 0 && + eloop_register_read_sock(radius->acct_serv_sock, + radius_client_receive, radius, + (void *) RADIUS_ACCT)) { + printf("Could not register read socket for accounting " + "server\n"); + return -1; + } + +#ifdef CONFIG_IPV6 + if (radius->acct_serv_sock6 >= 0 && + eloop_register_read_sock(radius->acct_serv_sock6, + radius_client_receive, radius, + (void *) RADIUS_ACCT)) { + printf("Could not register read socket for accounting " + "server\n"); + return -1; + } +#endif /* CONFIG_IPV6 */ + + return 0; +} + + +struct radius_client_data * +radius_client_init(void *ctx, struct hostapd_radius_servers *conf) +{ + struct radius_client_data *radius; + + radius = malloc(sizeof(struct radius_client_data)); + if (radius == NULL) + return NULL; + + memset(radius, 0, sizeof(struct radius_client_data)); + radius->ctx = ctx; + radius->conf = conf; + radius->auth_serv_sock = radius->acct_serv_sock = + radius->auth_serv_sock6 = radius->acct_serv_sock6 = + radius->auth_sock = radius->acct_sock = -1; + + if (conf->auth_server && radius_client_init_auth(radius)) { + radius_client_deinit(radius); + return NULL; + } + + if (conf->acct_server && radius_client_init_acct(radius)) { + radius_client_deinit(radius); + return NULL; + } + + if (conf->retry_primary_interval) + eloop_register_timeout(conf->retry_primary_interval, 0, + radius_retry_primary_timer, radius, + NULL); + + return radius; +} + + +void radius_client_deinit(struct radius_client_data *radius) +{ + if (!radius) + return; + + eloop_cancel_timeout(radius_retry_primary_timer, radius, NULL); + + radius_client_flush(radius); + free(radius->auth_handlers); + free(radius->acct_handlers); + free(radius); +} + + +void radius_client_flush_auth(struct radius_client_data *radius, u8 *addr) +{ + struct radius_msg_list *entry, *prev, *tmp; + + prev = NULL; + entry = radius->msgs; + while (entry) { + if (entry->msg_type == RADIUS_AUTH && + memcmp(entry->addr, addr, ETH_ALEN) == 0) { + hostapd_logger(radius->ctx, addr, + HOSTAPD_MODULE_RADIUS, + HOSTAPD_LEVEL_DEBUG, + "Removing pending RADIUS authentication" + " message for removed client"); + + if (prev) + prev->next = entry->next; + else + radius->msgs = entry->next; + + tmp = entry; + entry = entry->next; + radius_client_msg_free(tmp); + radius->num_msgs--; + continue; + } + + prev = entry; + entry = entry->next; + } +} + + +static int radius_client_dump_auth_server(char *buf, size_t buflen, + struct hostapd_radius_server *serv, + struct radius_client_data *cli) +{ + int pending = 0; + struct radius_msg_list *msg; + char abuf[50]; + + if (cli) { + for (msg = cli->msgs; msg; msg = msg->next) { + if (msg->msg_type == RADIUS_AUTH) + pending++; + } + } + + return snprintf(buf, buflen, + "radiusAuthServerIndex=%d\n" + "radiusAuthServerAddress=%s\n" + "radiusAuthClientServerPortNumber=%d\n" + "radiusAuthClientRoundTripTime=%d\n" + "radiusAuthClientAccessRequests=%u\n" + "radiusAuthClientAccessRetransmissions=%u\n" + "radiusAuthClientAccessAccepts=%u\n" + "radiusAuthClientAccessRejects=%u\n" + "radiusAuthClientAccessChallenges=%u\n" + "radiusAuthClientMalformedAccessResponses=%u\n" + "radiusAuthClientBadAuthenticators=%u\n" + "radiusAuthClientPendingRequests=%u\n" + "radiusAuthClientTimeouts=%u\n" + "radiusAuthClientUnknownTypes=%u\n" + "radiusAuthClientPacketsDropped=%u\n", + serv->index, + hostapd_ip_txt(&serv->addr, abuf, sizeof(abuf)), + serv->port, + serv->round_trip_time, + serv->requests, + serv->retransmissions, + serv->access_accepts, + serv->access_rejects, + serv->access_challenges, + serv->malformed_responses, + serv->bad_authenticators, + pending, + serv->timeouts, + serv->unknown_types, + serv->packets_dropped); +} + + +static int radius_client_dump_acct_server(char *buf, size_t buflen, + struct hostapd_radius_server *serv, + struct radius_client_data *cli) +{ + int pending = 0; + struct radius_msg_list *msg; + char abuf[50]; + + if (cli) { + for (msg = cli->msgs; msg; msg = msg->next) { + if (msg->msg_type == RADIUS_ACCT || + msg->msg_type == RADIUS_ACCT_INTERIM) + pending++; + } + } + + return snprintf(buf, buflen, + "radiusAccServerIndex=%d\n" + "radiusAccServerAddress=%s\n" + "radiusAccClientServerPortNumber=%d\n" + "radiusAccClientRoundTripTime=%d\n" + "radiusAccClientRequests=%u\n" + "radiusAccClientRetransmissions=%u\n" + "radiusAccClientResponses=%u\n" + "radiusAccClientMalformedResponses=%u\n" + "radiusAccClientBadAuthenticators=%u\n" + "radiusAccClientPendingRequests=%u\n" + "radiusAccClientTimeouts=%u\n" + "radiusAccClientUnknownTypes=%u\n" + "radiusAccClientPacketsDropped=%u\n", + serv->index, + hostapd_ip_txt(&serv->addr, abuf, sizeof(abuf)), + serv->port, + serv->round_trip_time, + serv->requests, + serv->retransmissions, + serv->responses, + serv->malformed_responses, + serv->bad_authenticators, + pending, + serv->timeouts, + serv->unknown_types, + serv->packets_dropped); +} + + +int radius_client_get_mib(struct radius_client_data *radius, char *buf, + size_t buflen) +{ + struct hostapd_radius_servers *conf = radius->conf; + int i; + struct hostapd_radius_server *serv; + int count = 0; + + if (conf->auth_servers) { + for (i = 0; i < conf->num_auth_servers; i++) { + serv = &conf->auth_servers[i]; + count += radius_client_dump_auth_server( + buf + count, buflen - count, serv, + serv == conf->auth_server ? + radius : NULL); + } + } + + if (conf->acct_servers) { + for (i = 0; i < conf->num_acct_servers; i++) { + serv = &conf->acct_servers[i]; + count += radius_client_dump_acct_server( + buf + count, buflen - count, serv, + serv == conf->acct_server ? + radius : NULL); + } + } + + return count; +} diff --git a/contrib/hostapd-0.4.9/radius_client.h b/contrib/hostapd-0.4.9/radius_client.h new file mode 100644 index 0000000000..d21ca83fae --- /dev/null +++ b/contrib/hostapd-0.4.9/radius_client.h @@ -0,0 +1,87 @@ +#ifndef RADIUS_CLIENT_H +#define RADIUS_CLIENT_H + +#include "config_types.h" + +struct radius_msg; + +struct hostapd_radius_server { + /* MIB prefix for shared variables: + * @ = radiusAuth or radiusAcc depending on the type of the server */ + struct hostapd_ip_addr addr; /* @ServerAddress */ + int port; /* @ClientServerPortNumber */ + u8 *shared_secret; + size_t shared_secret_len; + + /* Dynamic (not from configuration file) MIB data */ + int index; /* @ServerIndex */ + int round_trip_time; /* @ClientRoundTripTime; in hundredths of a + * second */ + u32 requests; /* @Client{Access,}Requests */ + u32 retransmissions; /* @Client{Access,}Retransmissions */ + u32 access_accepts; /* radiusAuthClientAccessAccepts */ + u32 access_rejects; /* radiusAuthClientAccessRejects */ + u32 access_challenges; /* radiusAuthClientAccessChallenges */ + u32 responses; /* radiusAccClientResponses */ + u32 malformed_responses; /* @ClientMalformed{Access,}Responses */ + u32 bad_authenticators; /* @ClientBadAuthenticators */ + u32 timeouts; /* @ClientTimeouts */ + u32 unknown_types; /* @ClientUnknownTypes */ + u32 packets_dropped; /* @ClientPacketsDropped */ + /* @ClientPendingRequests: length of hapd->radius->msgs for matching + * msg_type */ +}; + +struct hostapd_radius_servers { + /* RADIUS Authentication and Accounting servers in priority order */ + struct hostapd_radius_server *auth_servers, *auth_server; + int num_auth_servers; + struct hostapd_radius_server *acct_servers, *acct_server; + int num_acct_servers; + + int retry_primary_interval; + int acct_interim_interval; + + int msg_dumps; +}; + + +typedef enum { + RADIUS_AUTH, + RADIUS_ACCT, + RADIUS_ACCT_INTERIM /* used only with radius_client_send(); just like + * RADIUS_ACCT, but removes any pending interim + * RADIUS Accounting packages for the same STA + * before sending the new interim update */ +} RadiusType; + +typedef enum { + RADIUS_RX_PROCESSED, + RADIUS_RX_QUEUED, + RADIUS_RX_UNKNOWN, + RADIUS_RX_INVALID_AUTHENTICATOR +} RadiusRxResult; + +struct radius_client_data; + +int radius_client_register(struct radius_client_data *radius, + RadiusType msg_type, + RadiusRxResult (*handler) + (struct radius_msg *msg, struct radius_msg *req, + u8 *shared_secret, size_t shared_secret_len, + void *data), + void *data); +int radius_client_send(struct radius_client_data *radius, + struct radius_msg *msg, + RadiusType msg_type, u8 *addr); +u8 radius_client_get_id(struct radius_client_data *radius); + +void radius_client_flush(struct radius_client_data *radius); +struct radius_client_data * +radius_client_init(void *ctx, struct hostapd_radius_servers *conf); +void radius_client_deinit(struct radius_client_data *radius); +void radius_client_flush_auth(struct radius_client_data *radius, u8 *addr); +int radius_client_get_mib(struct radius_client_data *radius, char *buf, + size_t buflen); + +#endif /* RADIUS_CLIENT_H */ diff --git a/contrib/hostapd-0.4.9/radius_server.c b/contrib/hostapd-0.4.9/radius_server.c new file mode 100644 index 0000000000..13c17744e0 --- /dev/null +++ b/contrib/hostapd-0.4.9/radius_server.c @@ -0,0 +1,1074 @@ +/* + * hostapd / RADIUS authentication server + * Copyright (c) 2005, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "common.h" +#include "radius.h" +#include "eloop.h" +#include "config.h" +#include "eap.h" +#include "radius_server.h" + +#define RADIUS_SESSION_TIMEOUT 60 +#define RADIUS_MAX_SESSION 100 +#define RADIUS_MAX_MSG_LEN 3000 + +static struct eapol_callbacks radius_server_eapol_cb; + +struct radius_client; +struct radius_server_data; + +struct radius_session { + struct radius_session *next; + struct radius_client *client; + struct radius_server_data *server; + unsigned int sess_id; + struct eap_sm *eap; + u8 *eapKeyData, *eapReqData; + size_t eapKeyDataLen, eapReqDataLen; + Boolean eapSuccess, eapRestart, eapFail, eapResp, eapReq, eapNoReq; + Boolean portEnabled, eapTimeout; +}; + +struct radius_client { + struct radius_client *next; + struct in_addr addr; + struct in_addr mask; +#ifdef CONFIG_IPV6 + struct in6_addr addr6; + struct in6_addr mask6; +#endif /* CONFIG_IPV6 */ + char *shared_secret; + int shared_secret_len; + struct radius_session *sessions; +}; + +struct radius_server_data { + int auth_sock; + struct radius_client *clients; + struct radius_server_session *sessions; + unsigned int next_sess_id; + void *hostapd_conf; + int num_sess; + void *eap_sim_db_priv; + void *ssl_ctx; + int ipv6; +}; + + +extern int wpa_debug_level; + +#define RADIUS_DEBUG(args...) \ +wpa_printf(MSG_DEBUG, "RADIUS SRV: " args) +#define RADIUS_ERROR(args...) \ +wpa_printf(MSG_ERROR, "RADIUS SRV: " args) +#define RADIUS_DUMP(args...) \ +wpa_hexdump(MSG_MSGDUMP, "RADIUS SRV: " args) +#define RADIUS_DUMP_ASCII(args...) \ +wpa_hexdump_ascii(MSG_MSGDUMP, "RADIUS SRV: " args) + + +static void radius_server_session_timeout(void *eloop_ctx, void *timeout_ctx); + + + +static struct radius_client * +radius_server_get_client(struct radius_server_data *data, struct in_addr *addr, + int ipv6) +{ + struct radius_client *client = data->clients; + + while (client) { +#ifdef CONFIG_IPV6 + if (ipv6) { + struct in6_addr *addr6; + int i; + + addr6 = (struct in6_addr *) addr; + for (i = 0; i < 16; i++) { + if ((addr6->s6_addr[i] & + client->mask6.s6_addr[i]) != + (client->addr6.s6_addr[i] & + client->mask6.s6_addr[i])) { + i = 17; + break; + } + } + if (i == 16) { + break; + } + } +#endif /* CONFIG_IPV6 */ + if (!ipv6 && (client->addr.s_addr & client->mask.s_addr) == + (addr->s_addr & client->mask.s_addr)) { + break; + } + + client = client->next; + } + + return client; +} + + +static struct radius_session * +radius_server_get_session(struct radius_client *client, unsigned int sess_id) +{ + struct radius_session *sess = client->sessions; + + while (sess) { + if (sess->sess_id == sess_id) { + break; + } + sess = sess->next; + } + + return sess; +} + + +static void radius_server_session_free(struct radius_server_data *data, + struct radius_session *sess) +{ + eloop_cancel_timeout(radius_server_session_timeout, data, sess); + free(sess->eapKeyData); + free(sess->eapReqData); + eap_sm_deinit(sess->eap); + free(sess); + data->num_sess--; +} + + +static void radius_server_session_remove(struct radius_server_data *data, + struct radius_session *sess) +{ + struct radius_client *client = sess->client; + struct radius_session *session, *prev; + + prev = NULL; + session = client->sessions; + while (session) { + if (session == sess) { + if (prev == NULL) { + client->sessions = sess->next; + } else { + prev->next = sess->next; + } + radius_server_session_free(data, sess); + break; + } + prev = session; + session = session->next; + } +} + + +static void radius_server_session_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct radius_server_data *data = eloop_ctx; + struct radius_session *sess = timeout_ctx; + + RADIUS_DEBUG("Timing out authentication session 0x%x", sess->sess_id); + radius_server_session_remove(data, sess); +} + + +static struct radius_session * +radius_server_new_session(struct radius_server_data *data, + struct radius_client *client) +{ + struct radius_session *sess; + + if (data->num_sess >= RADIUS_MAX_SESSION) { + RADIUS_DEBUG("Maximum number of existing session - no room " + "for a new session"); + return NULL; + } + + sess = malloc(sizeof(*sess)); + if (sess == NULL) { + return NULL; + } + memset(sess, 0, sizeof(*sess)); + sess->server = data; + sess->client = client; + sess->sess_id = data->next_sess_id++; + sess->next = client->sessions; + client->sessions = sess; + eloop_register_timeout(RADIUS_SESSION_TIMEOUT, 0, + radius_server_session_timeout, data, sess); + data->num_sess++; + return sess; +} + + +static struct radius_session * +radius_server_get_new_session(struct radius_server_data *data, + struct radius_client *client, + struct radius_msg *msg) +{ + u8 *user; + size_t user_len; + const struct hostapd_eap_user *eap_user; + int res; + struct radius_session *sess; + struct eap_config eap_conf; + + RADIUS_DEBUG("Creating a new session"); + + user = malloc(256); + if (user == NULL) { + return NULL; + } + res = radius_msg_get_attr(msg, RADIUS_ATTR_USER_NAME, user, 256); + if (res < 0 || res > 256) { + RADIUS_DEBUG("Could not get User-Name"); + free(user); + return NULL; + } + user_len = res; + RADIUS_DUMP_ASCII("User-Name", user, user_len); + + eap_user = hostapd_get_eap_user(data->hostapd_conf, user, user_len, 0); + free(user); + + if (eap_user) { + RADIUS_DEBUG("Matching user entry found"); + sess = radius_server_new_session(data, client); + if (sess == NULL) { + RADIUS_DEBUG("Failed to create a new session"); + return NULL; + } + } else { + RADIUS_DEBUG("User-Name not found from user database"); + return NULL; + } + + memset(&eap_conf, 0, sizeof(eap_conf)); + eap_conf.ssl_ctx = data->ssl_ctx; + eap_conf.eap_sim_db_priv = data->eap_sim_db_priv; + eap_conf.backend_auth = TRUE; + sess->eap = eap_sm_init(sess, &radius_server_eapol_cb, &eap_conf); + if (sess->eap == NULL) { + RADIUS_DEBUG("Failed to initialize EAP state machine for the " + "new session"); + radius_server_session_free(data, sess); + return NULL; + } + sess->eapRestart = TRUE; + sess->portEnabled = TRUE; + + RADIUS_DEBUG("New session 0x%x initialized", sess->sess_id); + + return sess; +} + + +static struct radius_msg * +radius_server_encapsulate_eap(struct radius_server_data *data, + struct radius_client *client, + struct radius_session *sess, + struct radius_msg *request) +{ + struct radius_msg *msg; + int code; + unsigned int sess_id; + + if (sess->eapFail) { + code = RADIUS_CODE_ACCESS_REJECT; + } else if (sess->eapSuccess) { + code = RADIUS_CODE_ACCESS_ACCEPT; + } else { + code = RADIUS_CODE_ACCESS_CHALLENGE; + } + + msg = radius_msg_new(code, request->hdr->identifier); + if (msg == NULL) { + return NULL; + } + + sess_id = htonl(sess->sess_id); + if (code == RADIUS_CODE_ACCESS_CHALLENGE && + !radius_msg_add_attr(msg, RADIUS_ATTR_STATE, + (u8 *) &sess_id, sizeof(sess_id))) { + RADIUS_DEBUG("Failed to add State attribute"); + } + + if (sess->eapReqData && + !radius_msg_add_eap(msg, sess->eapReqData, sess->eapReqDataLen)) { + RADIUS_DEBUG("Failed to add EAP-Message attribute"); + } + + if (code == RADIUS_CODE_ACCESS_ACCEPT && sess->eapKeyData) { + int len; + if (sess->eapKeyDataLen > 64) { + len = 32; + } else { + len = sess->eapKeyDataLen / 2; + } + if (!radius_msg_add_mppe_keys(msg, request->hdr->authenticator, + (u8 *) client->shared_secret, + client->shared_secret_len, + sess->eapKeyData + len, len, + sess->eapKeyData, len)) { + RADIUS_DEBUG("Failed to add MPPE key attributes"); + } + } + + if (radius_msg_finish_srv(msg, (u8 *) client->shared_secret, + client->shared_secret_len, + request->hdr->authenticator) < 0) { + RADIUS_DEBUG("Failed to add Message-Authenticator attribute"); + } + + return msg; +} + + +static int radius_server_reject(struct radius_server_data *data, + struct radius_client *client, + struct radius_msg *request, + struct sockaddr *from, socklen_t fromlen, + const char *from_addr, int from_port) +{ + struct radius_msg *msg; + int ret = 0; + struct eap_hdr eapfail; + + RADIUS_DEBUG("Reject invalid request from %s:%d", + from_addr, from_port); + + msg = radius_msg_new(RADIUS_CODE_ACCESS_REJECT, + request->hdr->identifier); + if (msg == NULL) { + return -1; + } + + memset(&eapfail, 0, sizeof(eapfail)); + eapfail.code = EAP_CODE_FAILURE; + eapfail.identifier = 0; + eapfail.length = htons(sizeof(eapfail)); + + if (!radius_msg_add_eap(msg, (u8 *) &eapfail, sizeof(eapfail))) { + RADIUS_DEBUG("Failed to add EAP-Message attribute"); + } + + + if (radius_msg_finish_srv(msg, (u8 *) client->shared_secret, + client->shared_secret_len, + request->hdr->authenticator) < 0) { + RADIUS_DEBUG("Failed to add Message-Authenticator attribute"); + } + + if (wpa_debug_level <= MSG_MSGDUMP) { + radius_msg_dump(msg); + } + + if (sendto(data->auth_sock, msg->buf, msg->buf_used, 0, + (struct sockaddr *) from, sizeof(*from)) < 0) { + perror("sendto[RADIUS SRV]"); + ret = -1; + } + + radius_msg_free(msg); + free(msg); + + return ret; +} + + +static int radius_server_request(struct radius_server_data *data, + struct radius_msg *msg, + struct sockaddr *from, socklen_t fromlen, + struct radius_client *client, + const char *from_addr, int from_port) +{ + u8 *eap = NULL; + size_t eap_len; + int res, state_included = 0; + u8 statebuf[4], resp_id; + unsigned int state; + struct radius_session *sess; + struct radius_msg *reply; + struct eap_hdr *hdr; + + /* TODO: Implement duplicate packet processing */ + + res = radius_msg_get_attr(msg, RADIUS_ATTR_STATE, statebuf, + sizeof(statebuf)); + state_included = res >= 0; + if (res == sizeof(statebuf)) { + state = (statebuf[0] << 24) | (statebuf[1] << 16) | + (statebuf[2] << 8) | statebuf[3]; + sess = radius_server_get_session(client, state); + } else { + sess = NULL; + } + + if (sess) { + RADIUS_DEBUG("Request for session 0x%x", sess->sess_id); + } else if (state_included) { + RADIUS_DEBUG("State attribute included but no session found"); + radius_server_reject(data, client, msg, from, fromlen, + from_addr, from_port); + return -1; + } else { + sess = radius_server_get_new_session(data, client, msg); + if (sess == NULL) { + RADIUS_DEBUG("Could not create a new session"); + radius_server_reject(data, client, msg, from, fromlen, + from_addr, from_port); + return -1; + } + } + + eap = radius_msg_get_eap(msg, &eap_len); + if (eap == NULL) { + RADIUS_DEBUG("No EAP-Message in RADIUS packet from %s", + from_addr); + return -1; + } + + RADIUS_DUMP("Received EAP data", eap, eap_len); + if (eap_len >= sizeof(*hdr)) { + hdr = (struct eap_hdr *) eap; + resp_id = hdr->identifier; + } else { + resp_id = 0; + } + + /* FIX: if Code is Request, Success, or Failure, send Access-Reject; + * RFC3579 Sect. 2.6.2. + * Include EAP-Response/Nak with no preferred method if + * code == request. + * If code is not 1-4, discard the packet silently. + * Or is this already done by the EAP state machine? */ + + eap_set_eapRespData(sess->eap, eap, eap_len); + free(eap); + eap = NULL; + sess->eapResp = TRUE; + eap_sm_step(sess->eap); + + if (sess->eapReqData) { + RADIUS_DUMP("EAP data from the state machine", + sess->eapReqData, sess->eapReqDataLen); + } else if (sess->eapFail) { + RADIUS_DEBUG("No EAP data from the state machine, but eapFail " + "set - generate EAP-Failure"); + hdr = malloc(sizeof(*hdr)); + if (hdr) { + memset(hdr, 0, sizeof(*hdr)); + hdr->identifier = resp_id; + hdr->length = htons(sizeof(*hdr)); + sess->eapReqData = (u8 *) hdr; + sess->eapReqDataLen = sizeof(*hdr); + } + } else { + RADIUS_DEBUG("No EAP data from the state machine - ignore this" + " Access-Request silently (assuming it was a " + "duplicate)"); + return -1; + } + + reply = radius_server_encapsulate_eap(data, client, sess, msg); + + free(sess->eapReqData); + sess->eapReqData = NULL; + sess->eapReqDataLen = 0; + + if (reply) { + RADIUS_DEBUG("Reply to %s:%d", from_addr, from_port); + if (wpa_debug_level <= MSG_MSGDUMP) { + radius_msg_dump(reply); + } + + res = sendto(data->auth_sock, reply->buf, reply->buf_used, 0, + (struct sockaddr *) from, fromlen); + if (res < 0) { + perror("sendto[RADIUS SRV]"); + } + radius_msg_free(reply); + free(reply); + } + + if (sess->eapSuccess || sess->eapFail) { + RADIUS_DEBUG("Removing completed session 0x%x", sess->sess_id); + radius_server_session_remove(data, sess); + } + + return 0; +} + + +static void radius_server_receive_auth(int sock, void *eloop_ctx, + void *sock_ctx) +{ + struct radius_server_data *data = eloop_ctx; + u8 *buf = NULL; + struct sockaddr_storage from; + socklen_t fromlen; + int len; + struct radius_client *client = NULL; + struct radius_msg *msg = NULL; + char abuf[50]; + int from_port = 0; + + buf = malloc(RADIUS_MAX_MSG_LEN); + if (buf == NULL) { + goto fail; + } + + fromlen = sizeof(from); + len = recvfrom(sock, buf, RADIUS_MAX_MSG_LEN, 0, + (struct sockaddr *) &from, &fromlen); + if (len < 0) { + perror("recvfrom[radius_server]"); + goto fail; + } + +#ifdef CONFIG_IPV6 + if (data->ipv6) { + struct sockaddr_in6 *from6 = (struct sockaddr_in6 *) &from; + if (inet_ntop(AF_INET6, &from6->sin6_addr, abuf, sizeof(abuf)) + == NULL) + abuf[0] = '\0'; + from_port = ntohs(from6->sin6_port); + RADIUS_DEBUG("Received %d bytes from %s:%d", + len, abuf, from_port); + + client = radius_server_get_client(data, + (struct in_addr *) + &from6->sin6_addr, 1); + } +#endif /* CONFIG_IPV6 */ + + if (!data->ipv6) { + struct sockaddr_in *from4 = (struct sockaddr_in *) &from; + snprintf(abuf, sizeof(abuf), "%s", inet_ntoa(from4->sin_addr)); + from_port = ntohs(from4->sin_port); + RADIUS_DEBUG("Received %d bytes from %s:%d", + len, abuf, from_port); + + client = radius_server_get_client(data, &from4->sin_addr, 0); + } + + RADIUS_DUMP("Received data", buf, len); + + if (client == NULL) { + RADIUS_DEBUG("Unknown client %s - packet ignored", abuf); + goto fail; + } + + msg = radius_msg_parse(buf, len); + if (msg == NULL) { + RADIUS_DEBUG("Parsing incoming RADIUS frame failed"); + goto fail; + } + + free(buf); + buf = NULL; + + if (wpa_debug_level <= MSG_MSGDUMP) { + radius_msg_dump(msg); + } + + if (msg->hdr->code != RADIUS_CODE_ACCESS_REQUEST) { + RADIUS_DEBUG("Unexpected RADIUS code %d", msg->hdr->code); + goto fail; + } + + if (radius_msg_verify_msg_auth(msg, (u8 *) client->shared_secret, + client->shared_secret_len, NULL)) { + RADIUS_DEBUG("Invalid Message-Authenticator from %s", abuf); + goto fail; + } + + radius_server_request(data, msg, (struct sockaddr *) &from, fromlen, + client, abuf, from_port); + +fail: + if (msg) { + radius_msg_free(msg); + free(msg); + } + free(buf); +} + + +static int radius_server_open_socket(int port) +{ + int s; + struct sockaddr_in addr; + + s = socket(PF_INET, SOCK_DGRAM, 0); + if (s < 0) { + perror("socket"); + return -1; + } + + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(port); + if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + perror("bind"); + close(s); + return -1; + } + + return s; +} + + +#ifdef CONFIG_IPV6 +static int radius_server_open_socket6(int port) +{ + int s; + struct sockaddr_in6 addr; + + s = socket(PF_INET6, SOCK_DGRAM, 0); + if (s < 0) { + perror("socket[IPv6]"); + return -1; + } + + memset(&addr, 0, sizeof(addr)); + addr.sin6_family = AF_INET6; + memcpy(&addr.sin6_addr, &in6addr_any, sizeof(in6addr_any)); + addr.sin6_port = htons(port); + if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + perror("bind"); + close(s); + return -1; + } + + return s; +} +#endif /* CONFIG_IPV6 */ + + +static void radius_server_free_sessions(struct radius_server_data *data, + struct radius_session *sessions) +{ + struct radius_session *session, *prev; + + session = sessions; + while (session) { + prev = session; + session = session->next; + radius_server_session_free(data, prev); + } +} + + +static void radius_server_free_clients(struct radius_server_data *data, + struct radius_client *clients) +{ + struct radius_client *client, *prev; + + client = clients; + while (client) { + prev = client; + client = client->next; + + radius_server_free_sessions(data, prev->sessions); + free(prev->shared_secret); + free(prev); + } +} + + +static struct radius_client * +radius_server_read_clients(const char *client_file, int ipv6) +{ + FILE *f; + const int buf_size = 1024; + char *buf, *pos; + struct radius_client *clients, *tail, *entry; + int line = 0, mask, failed = 0, i; + struct in_addr addr; +#ifdef CONFIG_IPV6 + struct in6_addr addr6; +#endif /* CONFIG_IPV6 */ + unsigned int val; + + f = fopen(client_file, "r"); + if (f == NULL) { + RADIUS_ERROR("Could not open client file '%s'", client_file); + return NULL; + } + + buf = malloc(buf_size); + if (buf == NULL) { + fclose(f); + return NULL; + } + + clients = tail = NULL; + while (fgets(buf, buf_size, f)) { + /* Configuration file format: + * 192.168.1.0/24 secret + * 192.168.1.2 secret + * fe80::211:22ff:fe33:4455/64 secretipv6 + */ + line++; + buf[buf_size - 1] = '\0'; + pos = buf; + while (*pos != '\0' && *pos != '\n') + pos++; + if (*pos == '\n') + *pos = '\0'; + if (*buf == '\0' || *buf == '#') + continue; + + pos = buf; + while ((*pos >= '0' && *pos <= '9') || *pos == '.' || + (*pos >= 'a' && *pos <= 'f') || *pos == ':' || + (*pos >= 'A' && *pos <= 'F')) { + pos++; + } + + if (*pos == '\0') { + failed = 1; + break; + } + + if (*pos == '/') { + char *end; + *pos++ = '\0'; + mask = strtol(pos, &end, 10); + if ((pos == end) || + (mask < 0 || mask > (ipv6 ? 128 : 32))) { + failed = 1; + break; + } + pos = end; + } else { + mask = ipv6 ? 128 : 32; + *pos++ = '\0'; + } + + if (!ipv6 && inet_aton(buf, &addr) == 0) { + failed = 1; + break; + } +#ifdef CONFIG_IPV6 + if (ipv6 && inet_pton(AF_INET6, buf, &addr6) <= 0) { + if (inet_pton(AF_INET, buf, &addr) <= 0) { + failed = 1; + break; + } + /* Convert IPv4 address to IPv6 */ + if (mask <= 32) + mask += (128 - 32); + memset(addr6.s6_addr, 0, 10); + addr6.s6_addr[10] = 0xff; + addr6.s6_addr[11] = 0xff; + memcpy(addr6.s6_addr + 12, (char *) &addr.s_addr, 4); + } +#endif /* CONFIG_IPV6 */ + + while (*pos == ' ' || *pos == '\t') { + pos++; + } + + if (*pos == '\0') { + failed = 1; + break; + } + + entry = malloc(sizeof(*entry)); + if (entry == NULL) { + failed = 1; + break; + } + memset(entry, 0, sizeof(*entry)); + entry->shared_secret = strdup(pos); + if (entry->shared_secret == NULL) { + failed = 1; + free(entry); + break; + } + entry->shared_secret_len = strlen(entry->shared_secret); + entry->addr.s_addr = addr.s_addr; + if (!ipv6) { + val = 0; + for (i = 0; i < mask; i++) + val |= 1 << (31 - i); + entry->mask.s_addr = htonl(val); + } +#ifdef CONFIG_IPV6 + if (ipv6) { + int offset = mask / 8; + + memcpy(entry->addr6.s6_addr, addr6.s6_addr, 16); + memset(entry->mask6.s6_addr, 0xff, offset); + val = 0; + for (i = 0; i < (mask % 8); i++) + val |= 1 << (7 - i); + if (offset < 16) + entry->mask6.s6_addr[offset] = val; + } +#endif /* CONFIG_IPV6 */ + + if (tail == NULL) { + clients = tail = entry; + } else { + tail->next = entry; + tail = entry; + } + } + + if (failed) { + RADIUS_ERROR("Invalid line %d in '%s'", line, client_file); + radius_server_free_clients(NULL, clients); + clients = NULL; + } + + free(buf); + fclose(f); + + return clients; +} + + +struct radius_server_data * +radius_server_init(struct radius_server_conf *conf) +{ + struct radius_server_data *data; + +#ifndef CONFIG_IPV6 + if (conf->ipv6) { + fprintf(stderr, "RADIUS server compiled without IPv6 " + "support.\n"); + return NULL; + } +#endif /* CONFIG_IPV6 */ + + data = malloc(sizeof(*data)); + if (data == NULL) { + return NULL; + } + memset(data, 0, sizeof(*data)); + data->hostapd_conf = conf->hostapd_conf; + data->eap_sim_db_priv = conf->eap_sim_db_priv; + data->ssl_ctx = conf->ssl_ctx; + data->ipv6 = conf->ipv6; + + data->clients = radius_server_read_clients(conf->client_file, + conf->ipv6); + if (data->clients == NULL) { + printf("No RADIUS clients configured.\n"); + radius_server_deinit(data); + return NULL; + } + +#ifdef CONFIG_IPV6 + if (conf->ipv6) + data->auth_sock = radius_server_open_socket6(conf->auth_port); + else +#endif /* CONFIG_IPV6 */ + data->auth_sock = radius_server_open_socket(conf->auth_port); + if (data->auth_sock < 0) { + printf("Failed to open UDP socket for RADIUS authentication " + "server\n"); + radius_server_deinit(data); + return NULL; + } + if (eloop_register_read_sock(data->auth_sock, + radius_server_receive_auth, + data, NULL)) { + radius_server_deinit(data); + return NULL; + } + + return data; +} + + +void radius_server_deinit(struct radius_server_data *data) +{ + if (data == NULL) + return; + + if (data->auth_sock >= 0) { + eloop_unregister_read_sock(data->auth_sock); + close(data->auth_sock); + } + + radius_server_free_clients(data, data->clients); + + free(data); +} + + +int radius_server_get_mib(struct radius_server_data *data, char *buf, + size_t buflen) +{ + /* TODO: add support for RADIUS authentication server MIB */ + return 0; +} + + +static Boolean radius_server_get_bool(void *ctx, enum eapol_bool_var variable) +{ + struct radius_session *sess = ctx; + if (sess == NULL) + return FALSE; + switch (variable) { + case EAPOL_eapSuccess: + return sess->eapSuccess; + case EAPOL_eapRestart: + return sess->eapRestart; + case EAPOL_eapFail: + return sess->eapFail; + case EAPOL_eapResp: + return sess->eapResp; + case EAPOL_eapReq: + return sess->eapReq; + case EAPOL_eapNoReq: + return sess->eapNoReq; + case EAPOL_portEnabled: + return sess->portEnabled; + case EAPOL_eapTimeout: + return sess->eapTimeout; + } + return FALSE; +} + + +static void radius_server_set_bool(void *ctx, enum eapol_bool_var variable, + Boolean value) +{ + struct radius_session *sess = ctx; + if (sess == NULL) + return; + switch (variable) { + case EAPOL_eapSuccess: + sess->eapSuccess = value; + break; + case EAPOL_eapRestart: + sess->eapRestart = value; + break; + case EAPOL_eapFail: + sess->eapFail = value; + break; + case EAPOL_eapResp: + sess->eapResp = value; + break; + case EAPOL_eapReq: + sess->eapReq = value; + break; + case EAPOL_eapNoReq: + sess->eapNoReq = value; + break; + case EAPOL_portEnabled: + sess->portEnabled = value; + break; + case EAPOL_eapTimeout: + sess->eapTimeout = value; + break; + } +} + + +static void radius_server_set_eapReqData(void *ctx, const u8 *eapReqData, + size_t eapReqDataLen) +{ + struct radius_session *sess = ctx; + if (sess == NULL) + return; + + free(sess->eapReqData); + sess->eapReqData = malloc(eapReqDataLen); + if (sess->eapReqData) { + memcpy(sess->eapReqData, eapReqData, eapReqDataLen); + sess->eapReqDataLen = eapReqDataLen; + } else { + sess->eapReqDataLen = 0; + } +} + + +static void radius_server_set_eapKeyData(void *ctx, const u8 *eapKeyData, + size_t eapKeyDataLen) +{ + struct radius_session *sess = ctx; + + if (sess == NULL) + return; + + free(sess->eapKeyData); + if (eapKeyData) { + sess->eapKeyData = malloc(eapKeyDataLen); + if (sess->eapKeyData) { + memcpy(sess->eapKeyData, eapKeyData, eapKeyDataLen); + sess->eapKeyDataLen = eapKeyDataLen; + } else { + sess->eapKeyDataLen = 0; + } + } else { + sess->eapKeyData = NULL; + sess->eapKeyDataLen = 0; + } +} + + +static int radius_server_get_eap_user(void *ctx, const u8 *identity, + size_t identity_len, int phase2, + struct eap_user *user) +{ + struct radius_session *sess = ctx; + const struct hostapd_eap_user *eap_user; + + eap_user = hostapd_get_eap_user(sess->server->hostapd_conf, identity, + identity_len, phase2); + if (eap_user == NULL) + return -1; + + memset(user, 0, sizeof(*user)); + memcpy(user->methods, eap_user->methods, + EAP_USER_MAX_METHODS > EAP_MAX_METHODS ? + EAP_USER_MAX_METHODS : EAP_MAX_METHODS); + + if (eap_user->password) { + user->password = malloc(eap_user->password_len); + if (user->password == NULL) + return -1; + memcpy(user->password, eap_user->password, + eap_user->password_len); + user->password_len = eap_user->password_len; + } + user->force_version = eap_user->force_version; + + return 0; +} + + +static struct eapol_callbacks radius_server_eapol_cb = +{ + .get_bool = radius_server_get_bool, + .set_bool = radius_server_set_bool, + .set_eapReqData = radius_server_set_eapReqData, + .set_eapKeyData = radius_server_set_eapKeyData, + .get_eap_user = radius_server_get_eap_user, +}; diff --git a/contrib/hostapd-0.4.9/radius_server.h b/contrib/hostapd-0.4.9/radius_server.h new file mode 100644 index 0000000000..5c4c39c37e --- /dev/null +++ b/contrib/hostapd-0.4.9/radius_server.h @@ -0,0 +1,46 @@ +#ifndef RADIUS_SERVER_H +#define RADIUS_SERVER_H + +struct radius_server_data; + +struct radius_server_conf { + int auth_port; + char *client_file; + void *hostapd_conf; + void *eap_sim_db_priv; + void *ssl_ctx; + int ipv6; +}; + + +#ifdef RADIUS_SERVER + +struct radius_server_data * +radius_server_init(struct radius_server_conf *conf); + +void radius_server_deinit(struct radius_server_data *data); + +int radius_server_get_mib(struct radius_server_data *data, char *buf, + size_t buflen); + +#else /* RADIUS_SERVER */ + +static inline struct radius_server_data * +radius_server_init(struct radius_server_conf *conf) +{ + return NULL; +} + +static inline void radius_server_deinit(struct radius_server_data *data) +{ +} + +static inline int radius_server_get_mib(struct radius_server_data *data, + char *buf, size_t buflen) +{ + return 0; +} + +#endif /* RADIUS_SERVER */ + +#endif /* RADIUS_SERVER_H */ diff --git a/contrib/hostapd-0.4.9/rc4.c b/contrib/hostapd-0.4.9/rc4.c new file mode 100644 index 0000000000..4cf14d91d1 --- /dev/null +++ b/contrib/hostapd-0.4.9/rc4.c @@ -0,0 +1,85 @@ +/* + * RC4 stream cipher + * Copyright (c) 2002-2005, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include +#include "common.h" +#include "rc4.h" + +#define S_SWAP(a,b) do { u8 t = S[a]; S[a] = S[b]; S[b] = t; } while(0) + +/** + * rc4 - XOR RC4 stream to given data with skip-stream-start + * @key: RC4 key + * @keylen: RC4 key length + * @skip: number of bytes to skip from the beginning of the RC4 stream + * @data: data to be XOR'ed with RC4 stream + * @data_len: buf length + * + * Generate RC4 pseudo random stream for the given key, skip beginning of the + * stream, and XOR the end result with the data buffer to perform RC4 + * encryption/decryption. + */ +void rc4_skip(const u8 *key, size_t keylen, size_t skip, + u8 *data, size_t data_len) +{ + u32 i, j, k; + u8 S[256], *pos; + int kpos; + + /* Setup RC4 state */ + for (i = 0; i < 256; i++) + S[i] = i; + j = 0; + kpos = 0; + for (i = 0; i < 256; i++) { + j = (j + S[i] + key[kpos]) & 0xff; + kpos++; + if (kpos >= keylen) + kpos = 0; + S_SWAP(i, j); + } + + /* Skip the start of the stream */ + i = j = 0; + for (k = 0; k < skip; k++) { + i = (i + 1) & 0xff; + j = (j + S[i]) & 0xff; + S_SWAP(i, j); + } + + /* Apply RC4 to data */ + pos = data; + for (k = 0; k < data_len; k++) { + i = (i + 1) & 0xff; + j = (j + S[i]) & 0xff; + S_SWAP(i, j); + *pos++ ^= S[(S[i] + S[j]) & 0xff]; + } +} + + +/** + * rc4 - XOR RC4 stream to given data + * @buf: data to be XOR'ed with RC4 stream + * @len: buf length + * @key: RC4 key + * @key_len: RC4 key length + * + * Generate RC4 pseudo random stream for the given key and XOR this with the + * data buffer to perform RC4 encryption/decryption. + */ +void rc4(u8 *buf, size_t len, const u8 *key, size_t key_len) +{ + rc4_skip(key, key_len, 0, buf, len); +} diff --git a/contrib/hostapd-0.4.9/rc4.h b/contrib/hostapd-0.4.9/rc4.h new file mode 100644 index 0000000000..3873240496 --- /dev/null +++ b/contrib/hostapd-0.4.9/rc4.h @@ -0,0 +1,22 @@ +/* + * RC4 stream cipher + * Copyright (c) 2002-2005, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef RC4_H +#define RC4_H + +void rc4_skip(const u8 *key, size_t keylen, size_t skip, + u8 *data, size_t data_len); +void rc4(u8 *buf, size_t len, const u8 *key, size_t key_len); + +#endif /* RC4_H */ diff --git a/contrib/hostapd-0.4.9/sha1.c b/contrib/hostapd-0.4.9/sha1.c new file mode 100644 index 0000000000..7e32e31ca1 --- /dev/null +++ b/contrib/hostapd-0.4.9/sha1.c @@ -0,0 +1,994 @@ +/* + * SHA1 hash implementation and interface functions + * Copyright (c) 2003-2005, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include +#include +#include + +#include "common.h" +#include "sha1.h" +#include "md5.h" +#include "crypto.h" + + +/** + * hmac_sha1_vector - HMAC-SHA1 over data vector (RFC 2104) + * @key: Key for HMAC operations + * @key_len: Length of the key in bytes + * @num_elem: Number of elements in the data vector + * @addr: Pointers to the data areas + * @len: Lengths of the data blocks + * @mac: Buffer for the hash (20 bytes) + */ +void hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + unsigned char k_pad[64]; /* padding - key XORd with ipad/opad */ + unsigned char tk[20]; + int i; + const u8 *_addr[6]; + size_t _len[6]; + + if (num_elem > 5) { + /* + * Fixed limit on the number of fragments to avoid having to + * allocate memory (which could fail). + */ + return; + } + + /* if key is longer than 64 bytes reset it to key = SHA1(key) */ + if (key_len > 64) { + sha1_vector(1, &key, &key_len, tk); + key = tk; + key_len = 20; + } + + /* the HMAC_SHA1 transform looks like: + * + * SHA1(K XOR opad, SHA1(K XOR ipad, text)) + * + * where K is an n byte key + * ipad is the byte 0x36 repeated 64 times + * opad is the byte 0x5c repeated 64 times + * and text is the data being protected */ + + /* start out by storing key in ipad */ + memset(k_pad, 0, sizeof(k_pad)); + memcpy(k_pad, key, key_len); + /* XOR key with ipad values */ + for (i = 0; i < 64; i++) + k_pad[i] ^= 0x36; + + /* perform inner SHA1 */ + _addr[0] = k_pad; + _len[0] = 64; + for (i = 0; i < num_elem; i++) { + _addr[i + 1] = addr[i]; + _len[i + 1] = len[i]; + } + sha1_vector(1 + num_elem, _addr, _len, mac); + + memset(k_pad, 0, sizeof(k_pad)); + memcpy(k_pad, key, key_len); + /* XOR key with opad values */ + for (i = 0; i < 64; i++) + k_pad[i] ^= 0x5c; + + /* perform outer SHA1 */ + _addr[0] = k_pad; + _len[0] = 64; + _addr[1] = mac; + _len[1] = SHA1_MAC_LEN; + sha1_vector(2, _addr, _len, mac); +} + + +/** + * hmac_sha1 - HMAC-SHA1 over data buffer (RFC 2104) + * @key: Key for HMAC operations + * @key_len: Length of the key in bytes + * @data: Pointers to the data area + * @data_len: Length of the data area + * @mac: Buffer for the hash (20 bytes) + */ +void hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len, + u8 *mac) +{ + hmac_sha1_vector(key, key_len, 1, &data, &data_len, mac); +} + + +/** + * sha1_prf - SHA1-based Pseudo-Random Function (PRF) (IEEE 802.11i, 8.5.1.1) + * @key: Key for PRF + * @key_len: Length of the key in bytes + * @label: A unique label for each purpose of the PRF + * @data: Extra data to bind into the key + * @data_len: Length of the data + * @buf: Buffer for the generated pseudo-random key + * @buf_len: Number of bytes of key to generate + * + * This function is used to derive new, cryptographically separate keys from a + * given key (e.g., PMK in IEEE 802.11i). + */ +void sha1_prf(const u8 *key, size_t key_len, const char *label, + const u8 *data, size_t data_len, u8 *buf, size_t buf_len) +{ + u8 zero = 0, counter = 0; + size_t pos, plen; + u8 hash[SHA1_MAC_LEN]; + size_t label_len = strlen(label); + const unsigned char *addr[4]; + size_t len[4]; + + addr[0] = (u8 *) label; + len[0] = label_len; + addr[1] = &zero; + len[1] = 1; + addr[2] = data; + len[2] = data_len; + addr[3] = &counter; + len[3] = 1; + + pos = 0; + while (pos < buf_len) { + plen = buf_len - pos; + if (plen >= SHA1_MAC_LEN) { + hmac_sha1_vector(key, key_len, 4, addr, len, + &buf[pos]); + pos += SHA1_MAC_LEN; + } else { + hmac_sha1_vector(key, key_len, 4, addr, len, + hash); + memcpy(&buf[pos], hash, plen); + break; + } + counter++; + } +} + + +/** + * sha1_t_prf - EAP-FAST Pseudo-Random Function (T-PRF) + * @key: Key for PRF + * @key_len: Length of the key in bytes + * @label: A unique label for each purpose of the PRF + * @seed: Seed value to bind into the key + * @seed_len: Length of the seed + * @buf: Buffer for the generated pseudo-random key + * @buf_len: Number of bytes of key to generate + * + * This function is used to derive new, cryptographically separate keys from a + * given key for EAP-FAST. T-PRF is defined in + * draft-cam-winget-eap-fast-02.txt, Appendix B. + */ +void sha1_t_prf(const u8 *key, size_t key_len, const char *label, + const u8 *seed, size_t seed_len, u8 *buf, size_t buf_len) +{ + unsigned char counter = 0; + size_t pos, plen; + u8 hash[SHA1_MAC_LEN]; + size_t label_len = strlen(label); + u8 output_len[2]; + const unsigned char *addr[5]; + size_t len[5]; + + addr[0] = hash; + len[0] = 0; + addr[1] = (unsigned char *) label; + len[1] = label_len + 1; + addr[2] = seed; + len[2] = seed_len; + addr[3] = output_len; + len[3] = 2; + addr[4] = &counter; + len[4] = 1; + + output_len[0] = (buf_len >> 8) & 0xff; + output_len[1] = buf_len & 0xff; + pos = 0; + while (pos < buf_len) { + counter++; + plen = buf_len - pos; + hmac_sha1_vector(key, key_len, 5, addr, len, hash); + if (plen >= SHA1_MAC_LEN) { + memcpy(&buf[pos], hash, SHA1_MAC_LEN); + pos += SHA1_MAC_LEN; + } else { + memcpy(&buf[pos], hash, plen); + break; + } + len[0] = SHA1_MAC_LEN; + } +} + + +/** + * tls_prf - Pseudo-Random Function for TLS (TLS-PRF, RFC 2246) + * @secret: Key for PRF + * @secret_len: Length of the key in bytes + * @label: A unique label for each purpose of the PRF + * @seed: Seed value to bind into the key + * @seed_len: Length of the seed + * @out: Buffer for the generated pseudo-random key + * @outlen: Number of bytes of key to generate +* + * This function is used to derive new, cryptographically separate keys from a + * given key in TLS. This PRF is defined in RFC 2246, Chapter 5. + */ +int tls_prf(const u8 *secret, size_t secret_len, const char *label, + const u8 *seed, size_t seed_len, u8 *out, size_t outlen) +{ + size_t L_S1, L_S2; + const u8 *S1, *S2; + u8 A_MD5[MD5_MAC_LEN], A_SHA1[SHA1_MAC_LEN]; + u8 P_MD5[MD5_MAC_LEN], P_SHA1[SHA1_MAC_LEN]; + int i, MD5_pos, SHA1_pos; + const u8 *MD5_addr[3]; + size_t MD5_len[3]; + const unsigned char *SHA1_addr[3]; + size_t SHA1_len[3]; + + if (secret_len & 1) + return -1; + + MD5_addr[0] = A_MD5; + MD5_len[0] = MD5_MAC_LEN; + MD5_addr[1] = (unsigned char *) label; + MD5_len[1] = strlen(label); + MD5_addr[2] = seed; + MD5_len[2] = seed_len; + + SHA1_addr[0] = A_SHA1; + SHA1_len[0] = SHA1_MAC_LEN; + SHA1_addr[1] = (unsigned char *) label; + SHA1_len[1] = strlen(label); + SHA1_addr[2] = seed; + SHA1_len[2] = seed_len; + + /* RFC 2246, Chapter 5 + * A(0) = seed, A(i) = HMAC(secret, A(i-1)) + * P_hash = HMAC(secret, A(1) + seed) + HMAC(secret, A(2) + seed) + .. + * PRF = P_MD5(S1, label + seed) XOR P_SHA-1(S2, label + seed) + */ + + L_S1 = L_S2 = (secret_len + 1) / 2; + S1 = secret; + S2 = secret + L_S1; + + hmac_md5_vector(S1, L_S1, 2, &MD5_addr[1], &MD5_len[1], A_MD5); + hmac_sha1_vector(S2, L_S2, 2, &SHA1_addr[1], &SHA1_len[1], A_SHA1); + + MD5_pos = MD5_MAC_LEN; + SHA1_pos = SHA1_MAC_LEN; + for (i = 0; i < outlen; i++) { + if (MD5_pos == MD5_MAC_LEN) { + hmac_md5_vector(S1, L_S1, 3, MD5_addr, MD5_len, P_MD5); + MD5_pos = 0; + hmac_md5(S1, L_S1, A_MD5, MD5_MAC_LEN, A_MD5); + } + if (SHA1_pos == SHA1_MAC_LEN) { + hmac_sha1_vector(S2, L_S2, 3, SHA1_addr, SHA1_len, + P_SHA1); + SHA1_pos = 0; + hmac_sha1(S2, L_S2, A_SHA1, SHA1_MAC_LEN, A_SHA1); + } + + out[i] = P_MD5[MD5_pos] ^ P_SHA1[SHA1_pos]; + + MD5_pos++; + SHA1_pos++; + } + + return 0; +} + + +static void pbkdf2_sha1_f(const char *passphrase, const char *ssid, + size_t ssid_len, int iterations, int count, + u8 *digest) +{ + unsigned char tmp[SHA1_MAC_LEN], tmp2[SHA1_MAC_LEN]; + int i, j; + unsigned char count_buf[4]; + const u8 *addr[2]; + size_t len[2]; + size_t passphrase_len = strlen(passphrase); + + addr[0] = (u8 *) ssid; + len[0] = ssid_len; + addr[1] = count_buf; + len[1] = 4; + + /* F(P, S, c, i) = U1 xor U2 xor ... Uc + * U1 = PRF(P, S || i) + * U2 = PRF(P, U1) + * Uc = PRF(P, Uc-1) + */ + + count_buf[0] = (count >> 24) & 0xff; + count_buf[1] = (count >> 16) & 0xff; + count_buf[2] = (count >> 8) & 0xff; + count_buf[3] = count & 0xff; + hmac_sha1_vector((u8 *) passphrase, passphrase_len, 2, addr, len, tmp); + memcpy(digest, tmp, SHA1_MAC_LEN); + + for (i = 1; i < iterations; i++) { + hmac_sha1((u8 *) passphrase, passphrase_len, tmp, SHA1_MAC_LEN, + tmp2); + memcpy(tmp, tmp2, SHA1_MAC_LEN); + for (j = 0; j < SHA1_MAC_LEN; j++) + digest[j] ^= tmp2[j]; + } +} + + +/** + * pbkdf2_sha1 - SHA1-based key derivation function (PBKDF2) for IEEE 802.11i + * @passphrase: ASCII passphrase + * @ssid: SSID + * @ssid_len: SSID length in bytes + * @interations: Number of iterations to run + * @buf: Buffer for the generated key + * @buflen: Length of the buffer in bytes + * + * This function is used to derive PSK for WPA-PSK. For this protocol, + * iterations is set to 4096 and buflen to 32. This function is described in + * IEEE Std 802.11-2004, Clause H.4. The main construction is from PKCS#5 v2.0. + */ +void pbkdf2_sha1(const char *passphrase, const char *ssid, size_t ssid_len, + int iterations, u8 *buf, size_t buflen) +{ + int count = 0; + unsigned char *pos = buf; + size_t left = buflen, plen; + unsigned char digest[SHA1_MAC_LEN]; + + while (left > 0) { + count++; + pbkdf2_sha1_f(passphrase, ssid, ssid_len, iterations, count, + digest); + plen = left > SHA1_MAC_LEN ? SHA1_MAC_LEN : left; + memcpy(pos, digest, plen); + pos += plen; + left -= plen; + } +} + + +#ifndef EAP_TLS_FUNCS + +typedef struct { + u32 state[5]; + u32 count[2]; + unsigned char buffer[64]; +} SHA1_CTX; + +static void SHA1Init(SHA1_CTX *context); +static void SHA1Update(SHA1_CTX *context, const void *data, u32 len); +static void SHA1Final(unsigned char digest[20], SHA1_CTX* context); +static void SHA1Transform(u32 state[5], const unsigned char buffer[64]); + + +/** + * sha1_vector - SHA-1 hash for data vector + * @num_elem: Number of elements in the data vector + * @addr: Pointers to the data areas + * @len: Lengths of the data blocks + * @mac: Buffer for the hash + */ +void sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, + u8 *mac) +{ + SHA1_CTX ctx; + int i; + + SHA1Init(&ctx); + for (i = 0; i < num_elem; i++) + SHA1Update(&ctx, addr[i], len[i]); + SHA1Final(mac, &ctx); +} + + +/** + * sha1_transform - Perform one SHA-1 transform step + * @state: SHA-1 state + * @data: Input data for the SHA-1 transform + * + * This function is used to implement random number generation specified in + * NIST FIPS Publication 186-2 for EAP-SIM. This PRF uses a function that is + * similar to SHA-1, but has different message padding and as such, access to + * just part of the SHA-1 is needed. + */ +void sha1_transform(u8 *state, const u8 data[64]) +{ + SHA1Transform((u32 *) state, data); +} + + +/* ===== start - public domain SHA1 implementation ===== */ + +/* +SHA-1 in C +By Steve Reid +100% Public Domain + +----------------- +Modified 7/98 +By James H. Brown +Still 100% Public Domain + +Corrected a problem which generated improper hash values on 16 bit machines +Routine SHA1Update changed from + void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned int +len) +to + void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned +long len) + +The 'len' parameter was declared an int which works fine on 32 bit machines. +However, on 16 bit machines an int is too small for the shifts being done +against +it. This caused the hash function to generate incorrect values if len was +greater than 8191 (8K - 1) due to the 'len << 3' on line 3 of SHA1Update(). + +Since the file IO in main() reads 16K at a time, any file 8K or larger would +be guaranteed to generate the wrong hash (e.g. Test Vector #3, a million +"a"s). + +I also changed the declaration of variables i & j in SHA1Update to +unsigned long from unsigned int for the same reason. + +These changes should make no difference to any 32 bit implementations since +an +int and a long are the same size in those environments. + +-- +I also corrected a few compiler warnings generated by Borland C. +1. Added #include for exit() prototype +2. Removed unused variable 'j' in SHA1Final +3. Changed exit(0) to return(0) at end of main. + +ALL changes I made can be located by searching for comments containing 'JHB' +----------------- +Modified 8/98 +By Steve Reid +Still 100% public domain + +1- Removed #include and used return() instead of exit() +2- Fixed overwriting of finalcount in SHA1Final() (discovered by Chris Hall) +3- Changed email address from steve@edmweb.com to sreid@sea-to-sky.net + +----------------- +Modified 4/01 +By Saul Kravitz +Still 100% PD +Modified to run on Compaq Alpha hardware. + +----------------- +Modified 4/01 +By Jouni Malinen +Minor changes to match the coding style used in Dynamics. + +Modified September 24, 2004 +By Jouni Malinen +Fixed alignment issue in SHA1Transform when SHA1HANDSOFF is defined. + +*/ + +/* +Test Vectors (from FIPS PUB 180-1) +"abc" + A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D +"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" + 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1 +A million repetitions of "a" + 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F +*/ + +#define SHA1HANDSOFF + +#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) + +/* blk0() and blk() perform the initial expand. */ +/* I got the idea of expanding during the round function from SSLeay */ +#ifndef WORDS_BIGENDIAN +#define blk0(i) (block->l[i] = (rol(block->l[i], 24) & 0xFF00FF00) | \ + (rol(block->l[i], 8) & 0x00FF00FF)) +#else +#define blk0(i) block->l[i] +#endif +#define blk(i) (block->l[i & 15] = rol(block->l[(i + 13) & 15] ^ \ + block->l[(i + 8) & 15] ^ block->l[(i + 2) & 15] ^ block->l[i & 15], 1)) + +/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */ +#define R0(v,w,x,y,z,i) \ + z += ((w & (x ^ y)) ^ y) + blk0(i) + 0x5A827999 + rol(v, 5); \ + w = rol(w, 30); +#define R1(v,w,x,y,z,i) \ + z += ((w & (x ^ y)) ^ y) + blk(i) + 0x5A827999 + rol(v, 5); \ + w = rol(w, 30); +#define R2(v,w,x,y,z,i) \ + z += (w ^ x ^ y) + blk(i) + 0x6ED9EBA1 + rol(v, 5); w = rol(w, 30); +#define R3(v,w,x,y,z,i) \ + z += (((w | x) & y) | (w & x)) + blk(i) + 0x8F1BBCDC + rol(v, 5); \ + w = rol(w, 30); +#define R4(v,w,x,y,z,i) \ + z += (w ^ x ^ y) + blk(i) + 0xCA62C1D6 + rol(v, 5); \ + w=rol(w, 30); + + +#ifdef VERBOSE /* SAK */ +void SHAPrintContext(SHA1_CTX *context, char *msg) +{ + printf("%s (%d,%d) %x %x %x %x %x\n", + msg, + context->count[0], context->count[1], + context->state[0], + context->state[1], + context->state[2], + context->state[3], + context->state[4]); +} +#endif + +/* Hash a single 512-bit block. This is the core of the algorithm. */ + +static void SHA1Transform(u32 state[5], const unsigned char buffer[64]) +{ + u32 a, b, c, d, e; + typedef union { + unsigned char c[64]; + u32 l[16]; + } CHAR64LONG16; + CHAR64LONG16* block; +#ifdef SHA1HANDSOFF + u32 workspace[16]; + block = (CHAR64LONG16 *) workspace; + memcpy(block, buffer, 64); +#else + block = (CHAR64LONG16 *) buffer; +#endif + /* Copy context->state[] to working vars */ + a = state[0]; + b = state[1]; + c = state[2]; + d = state[3]; + e = state[4]; + /* 4 rounds of 20 operations each. Loop unrolled. */ + R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3); + R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7); + R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11); + R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15); + R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19); + R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23); + R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27); + R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31); + R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35); + R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39); + R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43); + R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47); + R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51); + R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55); + R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59); + R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63); + R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67); + R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71); + R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75); + R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79); + /* Add the working vars back into context.state[] */ + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + state[4] += e; + /* Wipe variables */ + a = b = c = d = e = 0; +#ifdef SHA1HANDSOFF + memset(block, 0, 64); +#endif +} + + +/* SHA1Init - Initialize new context */ + +static void SHA1Init(SHA1_CTX* context) +{ + /* SHA1 initialization constants */ + context->state[0] = 0x67452301; + context->state[1] = 0xEFCDAB89; + context->state[2] = 0x98BADCFE; + context->state[3] = 0x10325476; + context->state[4] = 0xC3D2E1F0; + context->count[0] = context->count[1] = 0; +} + + +/* Run your data through this. */ + +static void SHA1Update(SHA1_CTX* context, const void *_data, u32 len) +{ + u32 i, j; + const unsigned char *data = _data; + +#ifdef VERBOSE + SHAPrintContext(context, "before"); +#endif + j = (context->count[0] >> 3) & 63; + if ((context->count[0] += len << 3) < (len << 3)) + context->count[1]++; + context->count[1] += (len >> 29); + if ((j + len) > 63) { + memcpy(&context->buffer[j], data, (i = 64-j)); + SHA1Transform(context->state, context->buffer); + for ( ; i + 63 < len; i += 64) { + SHA1Transform(context->state, &data[i]); + } + j = 0; + } + else i = 0; + memcpy(&context->buffer[j], &data[i], len - i); +#ifdef VERBOSE + SHAPrintContext(context, "after "); +#endif +} + + +/* Add padding and return the message digest. */ + +static void SHA1Final(unsigned char digest[20], SHA1_CTX* context) +{ + u32 i; + unsigned char finalcount[8]; + + for (i = 0; i < 8; i++) { + finalcount[i] = (unsigned char) + ((context->count[(i >= 4 ? 0 : 1)] >> + ((3-(i & 3)) * 8) ) & 255); /* Endian independent */ + } + SHA1Update(context, (unsigned char *) "\200", 1); + while ((context->count[0] & 504) != 448) { + SHA1Update(context, (unsigned char *) "\0", 1); + } + SHA1Update(context, finalcount, 8); /* Should cause a SHA1Transform() + */ + for (i = 0; i < 20; i++) { + digest[i] = (unsigned char) + ((context->state[i >> 2] >> ((3 - (i & 3)) * 8)) & + 255); + } + /* Wipe variables */ + i = 0; + memset(context->buffer, 0, 64); + memset(context->state, 0, 20); + memset(context->count, 0, 8); + memset(finalcount, 0, 8); +} + +/* ===== end - public domain SHA1 implementation ===== */ + +#endif /* EAP_TLS_FUNCS */ + + +#ifdef TEST_MAIN + +#include "md5.c" + +static int test_eap_fast(void) +{ + /* draft-cam-winget-eap-fast-01.txt */ + const u8 pac_key[] = { + 0x0B, 0x97, 0x39, 0x0F, 0x37, 0x51, 0x78, 0x09, + 0x81, 0x1E, 0xFD, 0x9C, 0x6E, 0x65, 0x94, 0x2B, + 0x63, 0x2C, 0xE9, 0x53, 0x89, 0x38, 0x08, 0xBA, + 0x36, 0x0B, 0x03, 0x7C, 0xD1, 0x85, 0xE4, 0x14 + }; + const u8 seed[] = { + 0x3F, 0xFB, 0x11, 0xC4, 0x6C, 0xBF, 0xA5, 0x7A, + 0x54, 0x40, 0xDA, 0xE8, 0x22, 0xD3, 0x11, 0xD3, + 0xF7, 0x6D, 0xE4, 0x1D, 0xD9, 0x33, 0xE5, 0x93, + 0x70, 0x97, 0xEB, 0xA9, 0xB3, 0x66, 0xF4, 0x2A, + 0x00, 0x00, 0x00, 0x02, 0x6A, 0x66, 0x43, 0x2A, + 0x8D, 0x14, 0x43, 0x2C, 0xEC, 0x58, 0x2D, 0x2F, + 0xC7, 0x9C, 0x33, 0x64, 0xBA, 0x04, 0xAD, 0x3A, + 0x52, 0x54, 0xD6, 0xA5, 0x79, 0xAD, 0x1E, 0x00 + }; + const u8 master_secret[] = { + 0x4A, 0x1A, 0x51, 0x2C, 0x01, 0x60, 0xBC, 0x02, + 0x3C, 0xCF, 0xBC, 0x83, 0x3F, 0x03, 0xBC, 0x64, + 0x88, 0xC1, 0x31, 0x2F, 0x0B, 0xA9, 0xA2, 0x77, + 0x16, 0xA8, 0xD8, 0xE8, 0xBD, 0xC9, 0xD2, 0x29, + 0x38, 0x4B, 0x7A, 0x85, 0xBE, 0x16, 0x4D, 0x27, + 0x33, 0xD5, 0x24, 0x79, 0x87, 0xB1, 0xC5, 0xA2 + }; + const u8 key_block[] = { + 0x59, 0x59, 0xBE, 0x8E, 0x41, 0x3A, 0x77, 0x74, + 0x8B, 0xB2, 0xE5, 0xD3, 0x60, 0xAC, 0x4D, 0x35, + 0xDF, 0xFB, 0xC8, 0x1E, 0x9C, 0x24, 0x9C, 0x8B, + 0x0E, 0xC3, 0x1D, 0x72, 0xC8, 0x84, 0x9D, 0x57, + 0x48, 0x51, 0x2E, 0x45, 0x97, 0x6C, 0x88, 0x70, + 0xBE, 0x5F, 0x01, 0xD3, 0x64, 0xE7, 0x4C, 0xBB, + 0x11, 0x24, 0xE3, 0x49, 0xE2, 0x3B, 0xCD, 0xEF, + 0x7A, 0xB3, 0x05, 0x39, 0x5D, 0x64, 0x8A, 0x44, + 0x11, 0xB6, 0x69, 0x88, 0x34, 0x2E, 0x8E, 0x29, + 0xD6, 0x4B, 0x7D, 0x72, 0x17, 0x59, 0x28, 0x05, + 0xAF, 0xF9, 0xB7, 0xFF, 0x66, 0x6D, 0xA1, 0x96, + 0x8F, 0x0B, 0x5E, 0x06, 0x46, 0x7A, 0x44, 0x84, + 0x64, 0xC1, 0xC8, 0x0C, 0x96, 0x44, 0x09, 0x98, + 0xFF, 0x92, 0xA8, 0xB4, 0xC6, 0x42, 0x28, 0x71 + }; + const u8 sks[] = { + 0xD6, 0x4B, 0x7D, 0x72, 0x17, 0x59, 0x28, 0x05, + 0xAF, 0xF9, 0xB7, 0xFF, 0x66, 0x6D, 0xA1, 0x96, + 0x8F, 0x0B, 0x5E, 0x06, 0x46, 0x7A, 0x44, 0x84, + 0x64, 0xC1, 0xC8, 0x0C, 0x96, 0x44, 0x09, 0x98, + 0xFF, 0x92, 0xA8, 0xB4, 0xC6, 0x42, 0x28, 0x71 + }; + const u8 isk[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + const u8 imck[] = { + 0x16, 0x15, 0x3C, 0x3F, 0x21, 0x55, 0xEF, 0xD9, + 0x7F, 0x34, 0xAE, 0xC8, 0x1A, 0x4E, 0x66, 0x80, + 0x4C, 0xC3, 0x76, 0xF2, 0x8A, 0xA9, 0x6F, 0x96, + 0xC2, 0x54, 0x5F, 0x8C, 0xAB, 0x65, 0x02, 0xE1, + 0x18, 0x40, 0x7B, 0x56, 0xBE, 0xEA, 0xA7, 0xC5, + 0x76, 0x5D, 0x8F, 0x0B, 0xC5, 0x07, 0xC6, 0xB9, + 0x04, 0xD0, 0x69, 0x56, 0x72, 0x8B, 0x6B, 0xB8, + 0x15, 0xEC, 0x57, 0x7B + }; + const u8 msk[] = { + 0x4D, 0x83, 0xA9, 0xBE, 0x6F, 0x8A, 0x74, 0xED, + 0x6A, 0x02, 0x66, 0x0A, 0x63, 0x4D, 0x2C, 0x33, + 0xC2, 0xDA, 0x60, 0x15, 0xC6, 0x37, 0x04, 0x51, + 0x90, 0x38, 0x63, 0xDA, 0x54, 0x3E, 0x14, 0xB9, + 0x27, 0x99, 0x18, 0x1E, 0x07, 0xBF, 0x0F, 0x5A, + 0x5E, 0x3C, 0x32, 0x93, 0x80, 0x8C, 0x6C, 0x49, + 0x67, 0xED, 0x24, 0xFE, 0x45, 0x40, 0xA0, 0x59, + 0x5E, 0x37, 0xC2, 0xE9, 0xD0, 0x5D, 0x0A, 0xE3 + }; + u8 tlv[] = { + 0x80, 0x0C, 0x00, 0x38, 0x00, 0x01, 0x01, 0x00, + 0xD8, 0x6A, 0x8C, 0x68, 0x3C, 0x32, 0x31, 0xA8, + 0x56, 0x63, 0xB6, 0x40, 0x21, 0xFE, 0x21, 0x14, + 0x4E, 0xE7, 0x54, 0x20, 0x79, 0x2D, 0x42, 0x62, + 0xC9, 0xBF, 0x53, 0x7F, 0x54, 0xFD, 0xAC, 0x58, + 0x43, 0x24, 0x6E, 0x30, 0x92, 0x17, 0x6D, 0xCF, + 0xE6, 0xE0, 0x69, 0xEB, 0x33, 0x61, 0x6A, 0xCC, + 0x05, 0xC5, 0x5B, 0xB7 + }; + const u8 compound_mac[] = { + 0x43, 0x24, 0x6E, 0x30, 0x92, 0x17, 0x6D, 0xCF, + 0xE6, 0xE0, 0x69, 0xEB, 0x33, 0x61, 0x6A, 0xCC, + 0x05, 0xC5, 0x5B, 0xB7 + }; + u8 buf[512]; + const u8 *simck, *cmk; + int errors = 0; + + printf("EAP-FAST test cases\n"); + + printf("- T-PRF (SHA1) test case / master_secret\n"); + sha1_t_prf(pac_key, sizeof(pac_key), "PAC to master secret label hash", + seed, sizeof(seed), buf, sizeof(master_secret)); + if (memcmp(master_secret, buf, sizeof(master_secret)) != 0) { + printf("T-PRF test - FAILED!\n"); + errors++; + } + + printf("- PRF (TLS, SHA1/MD5) test case / key_block\n"); + tls_prf(master_secret, sizeof(master_secret), "key expansion", + seed, sizeof(seed), buf, sizeof(key_block)); + if (memcmp(key_block, buf, sizeof(key_block)) != 0) { + printf("PRF test - FAILED!\n"); + errors++; + } + + printf("- T-PRF (SHA1) test case / IMCK\n"); + sha1_t_prf(sks, sizeof(sks), "Inner Methods Compound Keys", + isk, sizeof(isk), buf, sizeof(imck)); + if (memcmp(imck, buf, sizeof(imck)) != 0) { + printf("T-PRF test - FAILED!\n"); + errors++; + } + + simck = imck; + cmk = imck + 40; + + printf("- T-PRF (SHA1) test case / MSK\n"); + sha1_t_prf(simck, 40, "Session Key Generating Function", + "", 0, buf, sizeof(msk)); + if (memcmp(msk, buf, sizeof(msk)) != 0) { + printf("T-PRF test - FAILED!\n"); + errors++; + } + + printf("- Compound MAC test case\n"); + memset(tlv + sizeof(tlv) - 20, 0, 20); + hmac_sha1(cmk, 20, tlv, sizeof(tlv), tlv + sizeof(tlv) - 20); + if (memcmp(tlv + sizeof(tlv) - 20, compound_mac, sizeof(compound_mac)) + != 0) { + printf("Compound MAC test - FAILED!\n"); + errors++; + } + + return errors; +} + + +static u8 key0[] = +{ + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b +}; +static u8 data0[] = "Hi There"; +static u8 prf0[] = +{ + 0xbc, 0xd4, 0xc6, 0x50, 0xb3, 0x0b, 0x96, 0x84, + 0x95, 0x18, 0x29, 0xe0, 0xd7, 0x5f, 0x9d, 0x54, + 0xb8, 0x62, 0x17, 0x5e, 0xd9, 0xf0, 0x06, 0x06, + 0xe1, 0x7d, 0x8d, 0xa3, 0x54, 0x02, 0xff, 0xee, + 0x75, 0xdf, 0x78, 0xc3, 0xd3, 0x1e, 0x0f, 0x88, + 0x9f, 0x01, 0x21, 0x20, 0xc0, 0x86, 0x2b, 0xeb, + 0x67, 0x75, 0x3e, 0x74, 0x39, 0xae, 0x24, 0x2e, + 0xdb, 0x83, 0x73, 0x69, 0x83, 0x56, 0xcf, 0x5a +}; + +static u8 key1[] = "Jefe"; +static u8 data1[] = "what do ya want for nothing?"; +static u8 prf1[] = +{ + 0x51, 0xf4, 0xde, 0x5b, 0x33, 0xf2, 0x49, 0xad, + 0xf8, 0x1a, 0xeb, 0x71, 0x3a, 0x3c, 0x20, 0xf4, + 0xfe, 0x63, 0x14, 0x46, 0xfa, 0xbd, 0xfa, 0x58, + 0x24, 0x47, 0x59, 0xae, 0x58, 0xef, 0x90, 0x09, + 0xa9, 0x9a, 0xbf, 0x4e, 0xac, 0x2c, 0xa5, 0xfa, + 0x87, 0xe6, 0x92, 0xc4, 0x40, 0xeb, 0x40, 0x02, + 0x3e, 0x7b, 0xab, 0xb2, 0x06, 0xd6, 0x1d, 0xe7, + 0xb9, 0x2f, 0x41, 0x52, 0x90, 0x92, 0xb8, 0xfc +}; + + +static u8 key2[] = +{ + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa +}; +static u8 data2[] = +{ + 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, + 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, + 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, + 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, + 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, + 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, + 0xdd, 0xdd +}; +static u8 prf2[] = +{ + 0xe1, 0xac, 0x54, 0x6e, 0xc4, 0xcb, 0x63, 0x6f, + 0x99, 0x76, 0x48, 0x7b, 0xe5, 0xc8, 0x6b, 0xe1, + 0x7a, 0x02, 0x52, 0xca, 0x5d, 0x8d, 0x8d, 0xf1, + 0x2c, 0xfb, 0x04, 0x73, 0x52, 0x52, 0x49, 0xce, + 0x9d, 0xd8, 0xd1, 0x77, 0xea, 0xd7, 0x10, 0xbc, + 0x9b, 0x59, 0x05, 0x47, 0x23, 0x91, 0x07, 0xae, + 0xf7, 0xb4, 0xab, 0xd4, 0x3d, 0x87, 0xf0, 0xa6, + 0x8f, 0x1c, 0xbd, 0x9e, 0x2b, 0x6f, 0x76, 0x07 +}; + + +struct passphrase_test { + char *passphrase; + char *ssid; + char psk[32]; +}; + +static struct passphrase_test passphrase_tests[] = +{ + { + "password", + "IEEE", + { + 0xf4, 0x2c, 0x6f, 0xc5, 0x2d, 0xf0, 0xeb, 0xef, + 0x9e, 0xbb, 0x4b, 0x90, 0xb3, 0x8a, 0x5f, 0x90, + 0x2e, 0x83, 0xfe, 0x1b, 0x13, 0x5a, 0x70, 0xe2, + 0x3a, 0xed, 0x76, 0x2e, 0x97, 0x10, 0xa1, 0x2e + } + }, + { + "ThisIsAPassword", + "ThisIsASSID", + { + 0x0d, 0xc0, 0xd6, 0xeb, 0x90, 0x55, 0x5e, 0xd6, + 0x41, 0x97, 0x56, 0xb9, 0xa1, 0x5e, 0xc3, 0xe3, + 0x20, 0x9b, 0x63, 0xdf, 0x70, 0x7d, 0xd5, 0x08, + 0xd1, 0x45, 0x81, 0xf8, 0x98, 0x27, 0x21, 0xaf + } + }, + { + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ", + { + 0xbe, 0xcb, 0x93, 0x86, 0x6b, 0xb8, 0xc3, 0x83, + 0x2c, 0xb7, 0x77, 0xc2, 0xf5, 0x59, 0x80, 0x7c, + 0x8c, 0x59, 0xaf, 0xcb, 0x6e, 0xae, 0x73, 0x48, + 0x85, 0x00, 0x13, 0x00, 0xa9, 0x81, 0xcc, 0x62 + } + }, +}; + +#define NUM_PASSPHRASE_TESTS \ +(sizeof(passphrase_tests) / sizeof(passphrase_tests[0])) + + +int main(int argc, char *argv[]) +{ + u8 res[512]; + int ret = 0, i; + + printf("PRF-SHA1 test cases:\n"); + + sha1_prf(key0, sizeof(key0), "prefix", data0, sizeof(data0) - 1, + res, sizeof(prf0)); + if (memcmp(res, prf0, sizeof(prf0)) == 0) + printf("Test case 0 - OK\n"); + else { + printf("Test case 0 - FAILED!\n"); + ret++; + } + + sha1_prf(key1, sizeof(key1) - 1, "prefix", data1, sizeof(data1) - 1, + res, sizeof(prf1)); + if (memcmp(res, prf1, sizeof(prf1)) == 0) + printf("Test case 1 - OK\n"); + else { + printf("Test case 1 - FAILED!\n"); + ret++; + } + + sha1_prf(key2, sizeof(key2), "prefix", data2, sizeof(data2), + res, sizeof(prf2)); + if (memcmp(res, prf2, sizeof(prf2)) == 0) + printf("Test case 2 - OK\n"); + else { + printf("Test case 2 - FAILED!\n"); + ret++; + } + + ret += test_eap_fast(); + + printf("PBKDF2-SHA1 Passphrase test cases:\n"); + for (i = 0; i < NUM_PASSPHRASE_TESTS; i++) { + u8 psk[32]; + struct passphrase_test *test = &passphrase_tests[i]; + pbkdf2_sha1(test->passphrase, + test->ssid, strlen(test->ssid), + 4096, psk, 32); + if (memcmp(psk, test->psk, 32) == 0) + printf("Test case %d - OK\n", i); + else { + printf("Test case %d - FAILED!\n", i); + ret++; + } + } + + return ret; +} +#endif /* TEST_MAIN */ diff --git a/contrib/hostapd-0.4.9/sha1.h b/contrib/hostapd-0.4.9/sha1.h new file mode 100644 index 0000000000..3c6d915146 --- /dev/null +++ b/contrib/hostapd-0.4.9/sha1.h @@ -0,0 +1,33 @@ +/* + * SHA1 hash implementation and interface functions + * Copyright (c) 2003-2005, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef SHA1_H +#define SHA1_H + +#define SHA1_MAC_LEN 20 + +void hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac); +void hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len, + u8 *mac); +void sha1_prf(const u8 *key, size_t key_len, const char *label, + const u8 *data, size_t data_len, u8 *buf, size_t buf_len); +void sha1_t_prf(const u8 *key, size_t key_len, const char *label, + const u8 *seed, size_t seed_len, u8 *buf, size_t buf_len); +int tls_prf(const u8 *secret, size_t secret_len, const char *label, + const u8 *seed, size_t seed_len, u8 *out, size_t outlen); +void pbkdf2_sha1(const char *passphrase, const char *ssid, size_t ssid_len, + int iterations, u8 *buf, size_t buflen); + +#endif /* SHA1_H */ diff --git a/contrib/hostapd-0.4.9/sta_info.c b/contrib/hostapd-0.4.9/sta_info.c new file mode 100644 index 0000000000..9eb0bca2e3 --- /dev/null +++ b/contrib/hostapd-0.4.9/sta_info.c @@ -0,0 +1,355 @@ +/* + * Host AP (software wireless LAN access point) user space daemon for + * Host AP kernel driver / Station table + * Copyright (c) 2002-2004, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include +#include +#include +#include +#include +#include + +#include "hostapd.h" +#include "sta_info.h" +#include "eloop.h" +#include "accounting.h" +#include "ieee802_1x.h" +#include "ieee802_11.h" +#include "radius.h" +#include "eapol_sm.h" +#include "wpa.h" +#include "radius_client.h" +#include "driver.h" +#include "hostap_common.h" + + +int ap_for_each_sta(struct hostapd_data *hapd, + int (*cb)(struct hostapd_data *hapd, struct sta_info *sta, + void *ctx), + void *ctx) +{ + struct sta_info *sta; + + for (sta = hapd->sta_list; sta; sta = sta->next) { + if (cb(hapd, sta, ctx)) + return 1; + } + + return 0; +} + + +struct sta_info * ap_get_sta(struct hostapd_data *hapd, const u8 *sta) +{ + struct sta_info *s; + + s = hapd->sta_hash[STA_HASH(sta)]; + while (s != NULL && memcmp(s->addr, sta, 6) != 0) + s = s->hnext; + return s; +} + + +static void ap_sta_list_del(hostapd *hapd, struct sta_info *sta) +{ + struct sta_info *tmp; + + if (hapd->sta_list == sta) { + hapd->sta_list = sta->next; + return; + } + + tmp = hapd->sta_list; + while (tmp != NULL && tmp->next != sta) + tmp = tmp->next; + if (tmp == NULL) { + printf("Could not remove STA " MACSTR " from list.\n", + MAC2STR(sta->addr)); + } else + tmp->next = sta->next; +} + + +void ap_sta_hash_add(hostapd *hapd, struct sta_info *sta) +{ + sta->hnext = hapd->sta_hash[STA_HASH(sta->addr)]; + hapd->sta_hash[STA_HASH(sta->addr)] = sta; +} + + +static void ap_sta_hash_del(hostapd *hapd, struct sta_info *sta) +{ + struct sta_info *s; + + s = hapd->sta_hash[STA_HASH(sta->addr)]; + if (s == NULL) return; + if (memcmp(s->addr, sta->addr, 6) == 0) { + hapd->sta_hash[STA_HASH(sta->addr)] = s->hnext; + return; + } + + while (s->hnext != NULL && memcmp(s->hnext->addr, sta->addr, 6) != 0) + s = s->hnext; + if (s->hnext != NULL) + s->hnext = s->hnext->hnext; + else + printf("AP: could not remove STA " MACSTR " from hash table\n", + MAC2STR(sta->addr)); +} + + +void ap_free_sta(hostapd *hapd, struct sta_info *sta) +{ + accounting_sta_stop(hapd, sta); + if (!(sta->flags & WLAN_STA_PREAUTH)) + hostapd_sta_remove(hapd, sta->addr); + + ap_sta_hash_del(hapd, sta); + ap_sta_list_del(hapd, sta); + + if (sta->aid > 0) + hapd->sta_aid[sta->aid - 1] = NULL; + + hapd->num_sta--; + eloop_cancel_timeout(ap_handle_timer, hapd, sta); + + ieee802_1x_free_station(sta); + wpa_free_station(sta); + radius_client_flush_auth(hapd->radius, sta->addr); + + if (sta->last_assoc_req) + free(sta->last_assoc_req); + + free(sta->challenge); + free(sta->wpa_ie); + + free(sta); +} + + +void hostapd_free_stas(hostapd *hapd) +{ + struct sta_info *sta, *prev; + + sta = hapd->sta_list; + + while (sta) { + prev = sta; + sta = sta->next; + printf("Removing station " MACSTR "\n", MAC2STR(prev->addr)); + ap_free_sta(hapd, prev); + } +} + + +void ap_handle_timer(void *eloop_ctx, void *timeout_ctx) +{ + hostapd *hapd = eloop_ctx; + struct sta_info *sta = timeout_ctx; + unsigned long next_time = 0; + + if (sta->timeout_next == STA_REMOVE) { + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_INFO, "deauthenticated due to " + "local deauth request"); + ap_free_sta(hapd, sta); + return; + } + + if ((sta->flags & WLAN_STA_ASSOC) && + (sta->timeout_next == STA_NULLFUNC || + sta->timeout_next == STA_DISASSOC)) { + int inactive_sec; + HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, + "Checking STA " MACSTR " inactivity:\n", + MAC2STR(sta->addr)); + inactive_sec = hostapd_get_inact_sec(hapd, sta->addr); + if (inactive_sec == -1) { + printf(" Could not get station info from kernel " + "driver for " MACSTR ".\n", + MAC2STR(sta->addr)); + } else if (inactive_sec < AP_MAX_INACTIVITY && + sta->flags & WLAN_STA_ASSOC) { + /* station activity detected; reset timeout state */ + HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, + " Station has been active\n"); + sta->timeout_next = STA_NULLFUNC; + next_time = AP_MAX_INACTIVITY - inactive_sec; + } + } + + if ((sta->flags & WLAN_STA_ASSOC) && + sta->timeout_next == STA_DISASSOC && + !(sta->flags & WLAN_STA_PENDING_POLL)) { + HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, + " Station has ACKed data poll\n"); + /* data nullfunc frame poll did not produce TX errors; assume + * station ACKed it */ + sta->timeout_next = STA_NULLFUNC; + next_time = AP_MAX_INACTIVITY; + } + + if (next_time) { + eloop_register_timeout(next_time, 0, ap_handle_timer, hapd, + sta); + return; + } + + if (sta->timeout_next == STA_NULLFUNC && + (sta->flags & WLAN_STA_ASSOC)) { + /* send data frame to poll STA and check whether this frame + * is ACKed */ + struct ieee80211_hdr hdr; + + HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, + " Polling STA with data frame\n"); + sta->flags |= WLAN_STA_PENDING_POLL; + + /* FIX: WLAN_FC_STYPE_NULLFUNC would be more appropriate, but + * it is apparently not retried so TX Exc events are not + * received for it */ + memset(&hdr, 0, sizeof(hdr)); + hdr.frame_control = + IEEE80211_FC(WLAN_FC_TYPE_DATA, WLAN_FC_STYPE_DATA); + hdr.frame_control |= host_to_le16(BIT(1)); + hdr.frame_control |= host_to_le16(WLAN_FC_FROMDS); + memcpy(hdr.IEEE80211_DA_FROMDS, sta->addr, ETH_ALEN); + memcpy(hdr.IEEE80211_BSSID_FROMDS, hapd->own_addr, ETH_ALEN); + memcpy(hdr.IEEE80211_SA_FROMDS, hapd->own_addr, ETH_ALEN); + + if (hostapd_send_mgmt_frame(hapd, &hdr, sizeof(hdr), 0) < 0) + perror("ap_handle_timer: send"); + } else if (sta->timeout_next != STA_REMOVE) { + int deauth = sta->timeout_next == STA_DEAUTH; + + printf(" Sending %s info to STA " MACSTR "\n", + deauth ? "deauthentication" : "disassociation", + MAC2STR(sta->addr)); + + if (deauth) { + hostapd_sta_deauth(hapd, sta->addr, + WLAN_REASON_PREV_AUTH_NOT_VALID); + } else { + hostapd_sta_disassoc( + hapd, sta->addr, + WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY); + } + } + + switch (sta->timeout_next) { + case STA_NULLFUNC: + sta->timeout_next = STA_DISASSOC; + eloop_register_timeout(AP_DISASSOC_DELAY, 0, ap_handle_timer, + hapd, sta); + break; + case STA_DISASSOC: + sta->flags &= ~WLAN_STA_ASSOC; + ieee802_1x_set_port_enabled(hapd, sta, 0); + if (!sta->acct_terminate_cause) + sta->acct_terminate_cause = + RADIUS_ACCT_TERMINATE_CAUSE_IDLE_TIMEOUT; + accounting_sta_stop(hapd, sta); + ieee802_1x_free_station(sta); + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_INFO, "disassociated due to " + "inactivity"); + sta->timeout_next = STA_DEAUTH; + eloop_register_timeout(AP_DEAUTH_DELAY, 0, ap_handle_timer, + hapd, sta); + break; + case STA_DEAUTH: + case STA_REMOVE: + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_INFO, "deauthenticated due to " + "inactivity"); + if (!sta->acct_terminate_cause) + sta->acct_terminate_cause = + RADIUS_ACCT_TERMINATE_CAUSE_IDLE_TIMEOUT; + ap_free_sta(hapd, sta); + break; + } +} + + +void ap_handle_session_timer(void *eloop_ctx, void *timeout_ctx) +{ + hostapd *hapd = eloop_ctx; + struct sta_info *sta = timeout_ctx; + + if (!(sta->flags & WLAN_STA_AUTH)) + return; + + hostapd_sta_deauth(hapd, sta->addr, WLAN_REASON_PREV_AUTH_NOT_VALID); + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_INFO, "deauthenticated due to " + "session timeout"); + sta->acct_terminate_cause = + RADIUS_ACCT_TERMINATE_CAUSE_SESSION_TIMEOUT; + ap_free_sta(hapd, sta); +} + + +void ap_sta_session_timeout(hostapd *hapd, struct sta_info *sta, + u32 session_timeout) +{ + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, "setting session timeout to %d " + "seconds", session_timeout); + eloop_cancel_timeout(ap_handle_session_timer, hapd, sta); + eloop_register_timeout(session_timeout, 0, ap_handle_session_timer, + hapd, sta); +} + + +void ap_sta_no_session_timeout(hostapd *hapd, struct sta_info *sta) +{ + eloop_cancel_timeout(ap_handle_session_timer, hapd, sta); +} + + +struct sta_info * ap_sta_add(struct hostapd_data *hapd, const u8 *addr) +{ + struct sta_info *sta; + + sta = ap_get_sta(hapd, addr); + if (sta) + return sta; + + HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, " New STA\n"); + if (hapd->num_sta >= MAX_STA_COUNT) { + /* FIX: might try to remove some old STAs first? */ + printf(" no more room for new STAs (%d/%d)\n", + hapd->num_sta, MAX_STA_COUNT); + return NULL; + } + + sta = (struct sta_info *) malloc(sizeof(struct sta_info)); + if (sta == NULL) { + printf(" malloc failed\n"); + return NULL; + } + memset(sta, 0, sizeof(struct sta_info)); + sta->acct_interim_interval = hapd->conf->radius->acct_interim_interval; + + /* initialize STA info data */ + eloop_register_timeout(AP_MAX_INACTIVITY, 0, ap_handle_timer, + hapd, sta); + memcpy(sta->addr, addr, ETH_ALEN); + sta->next = hapd->sta_list; + hapd->sta_list = sta; + hapd->num_sta++; + ap_sta_hash_add(hapd, sta); + + return sta; +} diff --git a/contrib/hostapd-0.4.9/sta_info.h b/contrib/hostapd-0.4.9/sta_info.h new file mode 100644 index 0000000000..e2d7b4ef4b --- /dev/null +++ b/contrib/hostapd-0.4.9/sta_info.h @@ -0,0 +1,19 @@ +#ifndef STA_INFO_H +#define STA_INFO_H + +int ap_for_each_sta(struct hostapd_data *hapd, + int (*cb)(struct hostapd_data *hapd, struct sta_info *sta, + void *ctx), + void *ctx); +struct sta_info * ap_get_sta(struct hostapd_data *hapd, const u8 *sta); +void ap_sta_hash_add(hostapd *hapd, struct sta_info *sta); +void ap_free_sta(hostapd *hapd, struct sta_info *sta); +void ap_free_sta(hostapd *hapd, struct sta_info *sta); +void hostapd_free_stas(hostapd *hapd); +void ap_handle_timer(void *eloop_ctx, void *timeout_ctx); +void ap_sta_session_timeout(hostapd *hapd, struct sta_info *sta, + u32 session_timeout); +void ap_sta_no_session_timeout(hostapd *hapd, struct sta_info *sta); +struct sta_info * ap_sta_add(struct hostapd_data *hapd, const u8 *addr); + +#endif /* STA_INFO_H */ diff --git a/contrib/hostapd-0.4.9/tls.h b/contrib/hostapd-0.4.9/tls.h new file mode 100644 index 0000000000..a6a8110c7d --- /dev/null +++ b/contrib/hostapd-0.4.9/tls.h @@ -0,0 +1,463 @@ +/* + * WPA Supplicant / SSL/TLS interface definition + * Copyright (c) 2004-2006, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef TLS_H +#define TLS_H + +struct tls_connection; + +struct tls_keys { + const u8 *master_key; + size_t master_key_len; + const u8 *client_random; + size_t client_random_len; + const u8 *server_random; + size_t server_random_len; + + /* + * If TLS library does not provide access to master_key, but only to + * EAP key block, this pointer can be set to point to the result of + * PRF(master_secret, "client EAP encryption", + * client_random + server_random). + */ + const u8 *eap_tls_prf; + size_t eap_tls_prf_len; +}; + +struct tls_config { + const char *opensc_engine_path; + const char *pkcs11_engine_path; + const char *pkcs11_module_path; +}; + +/** + * struct tls_connection_params - Parameters for TLS connection + * @ca_cert: File or reference name for CA X.509 certificate in PEM or DER + * format + * @ca_cert_blob: ca_cert as inlined data or %NULL if not used + * @ca_cert_blob_len: ca_cert_blob length + * @ca_path: Path to CA certificates (OpenSSL specific) + * @subject_match: String to match in the subject of the peer certificate or + * %NULL to allow all subjects + * @altsubject_match: String to match in the alternative subject of the peer + * certificate or %NULL to allow all alternative subjects + * @client_cert: File or reference name for client X.509 certificate in PEM or + * DER format + * @client_cert_blob: client_cert as inlined data or %NULL if not used + * @client_cert_blob_len: client_cert_blob length + * @private_key: File or reference name for client private key in PEM or DER + * format (traditional format (RSA PRIVATE KEY) or PKCS#8 (PRIVATE KEY) + * @private_key_blob: private_key as inlined data or %NULL if not used + * @private_key_blob_len: private_key_blob length + * @private_key_passwd: Passphrase for decrypted private key, %NULL if no + * passphrase is used. + * @dh_file: File name for DH/DSA data in PEM format, or %NULL if not used + * @dh_blob: dh_file as inlined data or %NULL if not used + * @dh_blob_len: dh_blob length + * @engine: 1 = use engine (e.g., a smartcard) for private key operations + * (this is OpenSSL specific for now) + * @engine_id: engine id string (this is OpenSSL specific for now) + * @ppin: pointer to the pin variable in the configuration + * (this is OpenSSL specific for now) + * @key_id: the private key's key id (this is OpenSSL specific for now) + * + * TLS connection parameters to be configured with tls_connection_set_params(). + * + * Certificates and private key can be configured either as a reference name + * (file path or reference to certificate store) or by providing the same data + * as a pointer to the data in memory. Only one option will be used for each + * field. + */ +struct tls_connection_params { + const char *ca_cert; + const u8 *ca_cert_blob; + size_t ca_cert_blob_len; + const char *ca_path; + const char *subject_match; + const char *altsubject_match; + const char *client_cert; + const u8 *client_cert_blob; + size_t client_cert_blob_len; + const char *private_key; + const u8 *private_key_blob; + size_t private_key_blob_len; + const char *private_key_passwd; + const char *dh_file; + const u8 *dh_blob; + size_t dh_blob_len; + + /* OpenSSL specific variables */ + int engine; + const char *engine_id; + const char *pin; + const char *key_id; +}; + + +/** + * tls_init - Initialize TLS library + * @conf: Configuration data for TLS library + * Returns: Context data to be used as tls_ctx in calls to other functions, + * or %NULL on failure. + * + * Called once during program startup and once for each RSN pre-authentication + * session. In other words, there can be two concurrent TLS contexts. If global + * library initialization is needed (i.e., one that is shared between both + * authentication types), the TLS library wrapper should maintain a reference + * counter and do global initialization only when moving from 0 to 1 reference. + */ +void * tls_init(const struct tls_config *conf); + +/** + * tls_deinit - Deinitialize TLS library + * @tls_ctx: TLS context data from tls_init() + * + * Called once during program shutdown and once for each RSN pre-authentication + * session. If global library deinitialization is needed (i.e., one that is + * shared between both authentication types), the TLS library wrapper should + * maintain a reference counter and do global deinitialization only when moving + * from 1 to 0 references. + */ +void tls_deinit(void *tls_ctx); + +/** + * tls_get_errors - Process pending errors + * @tls_ctx: TLS context data from tls_init() + * + * Returns: Number of found error, 0 if no errors detected. + * + * Process all pending TLS errors. + */ +int tls_get_errors(void *tls_ctx); + +/** + * tls_connection_init - Initialize a new TLS connection + * @tls_ctx: TLS context data from tls_init() + * + * Returns: Connection context data, conn for other function calls + */ +struct tls_connection * tls_connection_init(void *tls_ctx); + +/** + * tls_connection_deinit - Free TLS connection data + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * + * Release all resources allocated for TLS connection. + */ +void tls_connection_deinit(void *tls_ctx, struct tls_connection *conn); + +/** + * tls_connection_established - Has the TLS connection been completed? + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * + * Returns: 1 if TLS connection has been completed, 0 if not. + */ +int tls_connection_established(void *tls_ctx, struct tls_connection *conn); + +/** + * tls_connection_shutdown - Shutdown TLS connection data. + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * + * Returns: 0 on success, -1 on failure + * + * Shutdown current TLS connection without releasing all resources. New + * connection can be started by using the same conn without having to call + * tls_connection_init() or setting certificates etc. again. The new + * connection should try to use session resumption. + */ +int tls_connection_shutdown(void *tls_ctx, struct tls_connection *conn); + +enum { + TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED = -3, + TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED = -2 +}; +/** + * tls_connection_set_params - Set TLS connection parameters + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * @params: Connection parameters + * + * Returns: 0 on success, -1 on failure, + * TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED (-2) on possible PIN error causing + * PKCS#11 engine failure, or + * TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED (-3) on failure to verify the + * PKCS#11 engine private key. + */ +int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, + const struct tls_connection_params *params); + +/** + * tls_global_ca_cert - Set trusted CA certificate for all TLS connections + * @tls_ctx: TLS context data from tls_init() + * @ca_cert: File name for CA certificate in PEM or DER format + * %NULL to allow all subjects + * + * Returns: 0 on success, -1 on failure + */ +int tls_global_ca_cert(void *tls_ctx, const char *ca_cert); + +/** + * tls_global_set_verify - Set global certificate verification options + * @tls_ctx: TLS context data from tls_init() + * @check_crl: 0 = do not verify CRLs, 1 = verify CRL for the user certificate, + * 2 = verify CRL for all certificates + * + * Returns: 0 on success, -1 on failure + */ +int tls_global_set_verify(void *tls_ctx, int check_crl); + +/** + * tls_connection_set_verify - Set certificate verification options + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * @verify_peer: 1 = verify peer certificate + * + * Returns: 0 on success, -1 on failure + */ +int tls_connection_set_verify(void *tls_ctx, struct tls_connection *conn, + int verify_peer); + +/** + * tls_global_client_cert - Set client certificate for all TLS connections + * @tls_ctx: TLS context data from tls_init() + * @client_cert: File name for client certificate in PEM or DER format + * + * Returns: 0 on success, -1 on failure + */ +int tls_global_client_cert(void *tls_ctx, const char *client_cert); + +/** + * tls_global_private_key - Set private key for all TLS connections + * @tls_ctx: TLS context data from tls_init() + * @private_key: File name for client private key in PEM or DER format + * @private_key_passwd: Passphrase for decrypted private key, %NULL if no + * passphrase is used. + * + * Returns: 0 on success, -1 on failure + */ +int tls_global_private_key(void *tls_ctx, const char *private_key, + const char *private_key_passwd); + +/** + * tls_connection_get_keys - Get master key and random data from TLS connection + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * @keys: Structure of key/random data (filled on success) + * + * Returns: 0 on success, -1 on failure + */ +int tls_connection_get_keys(void *tls_ctx, struct tls_connection *conn, + struct tls_keys *keys); + +/** + * tls_connection_handshake - Process TLS handshake (client side) + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * @in_data: Input data from TLS peer + * @in_len: Input data length + * @out_len: Length of the output buffer. + * + * Returns: Pointer to output data, %NULL on failure + * + * Caller is responsible for freeing returned output data. + * + * This function is used during TLS handshake. The first call is done with + * in_data == %NULL and the library is expected to return ClientHello packet. + * This packet is then send to the server and a response from server is given + * to TLS library by calling this function again with in_data pointing to the + * TLS message from the server. + * + * If the TLS handshake fails, this function may return %NULL. However, if the + * TLS library has a TLS alert to send out, that should be returned as the + * output data. In this case, tls_connection_get_failed() must return failure + * (> 0). + * + * tls_connection_established() should return 1 once the TLS handshake has been + * completed successfully. + */ +u8 * tls_connection_handshake(void *tls_ctx, struct tls_connection *conn, + const u8 *in_data, size_t in_len, + size_t *out_len); + +/** + * tls_connection_server_handshake - Process TLS handshake (server side) + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * @in_data: Input data from TLS peer + * @in_len: Input data length + * @out_len: Length of the output buffer. + * + * Returns: pointer to output data, %NULL on failure + * + * Caller is responsible for freeing returned output data. + */ +u8 * tls_connection_server_handshake(void *tls_ctx, + struct tls_connection *conn, + const u8 *in_data, size_t in_len, + size_t *out_len); + +/** + * tls_connection_encrypt - Encrypt data into TLS tunnel + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * @in_data: Pointer to plaintext data to be encrypted + * @in_len: Input buffer length + * @out_data: Pointer to output buffer (encrypted TLS data) + * @out_len: Maximum out_data length + * + * Returns: Number of bytes written to out_data, -1 on failure + * + * This function is used after TLS handshake has been completed successfully to + * send data in the encrypted tunnel. + */ +int tls_connection_encrypt(void *tls_ctx, struct tls_connection *conn, + const u8 *in_data, size_t in_len, + u8 *out_data, size_t out_len); + +/** + * tls_connection_decrypt - Decrypt data from TLS tunnel + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * @in_data: Pointer to input buffer (encrypted TLS data) + * @in_len: Input buffer length + * @out_data: Pointer to output buffer (decrypted data from TLS tunnel) + * @out_len: Maximum out_data length + * + * Returns: Number of bytes written to out_data, -1 on failure + * + * This function is used after TLS handshake has been completed successfully to + * receive data from the encrypted tunnel. + */ +int tls_connection_decrypt(void *tls_ctx, struct tls_connection *conn, + const u8 *in_data, size_t in_len, + u8 *out_data, size_t out_len); + +/** + * tls_connection_resumed - Was session resumption used + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * + * Returns: 1 if current session used session resumption, 0 if not + */ +int tls_connection_resumed(void *tls_ctx, struct tls_connection *conn); + +/** + * tls_connection_set_master_key - Configure master secret for TLS connection + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * @key: TLS pre-master-secret + * @key_len: length of key in bytes + * + * Returns: 0 on success, -1 on failure + */ +int tls_connection_set_master_key(void *tls_ctx, struct tls_connection *conn, + const u8 *key, size_t key_len); + +/** + * tls_connection_set_anon_dh - Configure TLS connection to use anonymous DH + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * + * Returns: 0 on success, -1 on failure + * + * TODO: consider changing this to more generic routine for configuring allowed + * ciphers + */ +int tls_connection_set_anon_dh(void *tls_ctx, struct tls_connection *conn); + +/** + * tls_get_cipher - Get current cipher name + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * @buf: Buffer for the cipher name + * @buflen: buf size + * + * Returns: 0 on success, -1 on failure + * + * Get the name of the currently used cipher. + */ +int tls_get_cipher(void *tls_ctx, struct tls_connection *conn, + char *buf, size_t buflen); + +/** + * tls_connection_enable_workaround - Enable TLS workaround options + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * + * Returns: 0 on success, -1 on failure + * + * This function is used to enable connection-specific workaround options for + * buffer SSL/TLS implementations. + */ +int tls_connection_enable_workaround(void *tls_ctx, + struct tls_connection *conn); + +/** + * tls_connection_client_hello_ext - Set TLS extension for ClientHello + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * @ext_type: Extension type + * @data: Extension payload (NULL to remove extension) + * @data_len: Extension payload length + * + * Returns: 0 on success, -1 on failure + */ +int tls_connection_client_hello_ext(void *tls_ctx, struct tls_connection *conn, + int ext_type, const u8 *data, + size_t data_len); + +/** + * tls_connection_get_failed - Get connection failure status + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * + * Returns >0 if connection has failed, 0 if not. + */ +int tls_connection_get_failed(void *tls_ctx, struct tls_connection *conn); + +/** + * tls_connection_get_read_alerts - Get connection read alert status + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * + * Returns: Number of times a fatal read (remote end reported error) has + * happened during this connection. + */ +int tls_connection_get_read_alerts(void *tls_ctx, struct tls_connection *conn); + +/** + * tls_connection_get_write_alerts - Get connection write alert status + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * + * Returns: Number of times a fatal write (locally detected error) has happened + * during this connection. + */ +int tls_connection_get_write_alerts(void *tls_ctx, + struct tls_connection *conn); + +/** + * tls_connection_get_keyblock_size - Get TLS key_block size + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * Returns: Size of the key_block for the negotiated cipher suite or -1 on + * failure + */ +int tls_connection_get_keyblock_size(void *tls_ctx, + struct tls_connection *conn); + +#endif /* TLS_H */ diff --git a/contrib/hostapd-0.4.9/tls_none.c b/contrib/hostapd-0.4.9/tls_none.c new file mode 100644 index 0000000000..07fd879ec8 --- /dev/null +++ b/contrib/hostapd-0.4.9/tls_none.c @@ -0,0 +1,28 @@ +/* + * WPA Supplicant / SSL/TLS interface functions for no TLS case + * Copyright (c) 2004, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include +#include + +#include "common.h" +#include "tls.h" + +void * tls_init(const struct tls_config *conf) +{ + return (void *) 1; +} + +void tls_deinit(void *ssl_ctx) +{ +} diff --git a/contrib/hostapd-0.4.9/tls_openssl.c b/contrib/hostapd-0.4.9/tls_openssl.c new file mode 100644 index 0000000000..37878c7c20 --- /dev/null +++ b/contrib/hostapd-0.4.9/tls_openssl.c @@ -0,0 +1,2144 @@ +/* + * WPA Supplicant / SSL/TLS interface functions for openssl + * Copyright (c) 2004-2006, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include +#include +#include + +#ifndef CONFIG_SMARTCARD +#ifndef OPENSSL_NO_ENGINE +#define OPENSSL_NO_ENGINE +#endif +#endif + +#include +#include +#include +#include +#ifndef OPENSSL_NO_ENGINE +#include +#endif /* OPENSSL_NO_ENGINE */ + +#include "common.h" +#include "tls.h" + +#if OPENSSL_VERSION_NUMBER >= 0x0090800fL +#define OPENSSL_d2i_TYPE const unsigned char ** +#else +#define OPENSSL_d2i_TYPE unsigned char ** +#endif + +static int tls_openssl_ref_count = 0; + +struct tls_connection { + SSL *ssl; + BIO *ssl_in, *ssl_out; +#ifndef OPENSSL_NO_ENGINE + ENGINE *engine; /* functional reference to the engine */ + EVP_PKEY *private_key; /* the private key if using engine */ +#endif /* OPENSSL_NO_ENGINE */ + char *subject_match, *altsubject_match; + int read_alerts, write_alerts, failed; + + u8 *pre_shared_secret; + size_t pre_shared_secret_len; +}; + + +#ifdef CONFIG_NO_STDOUT_DEBUG + +static void _tls_show_errors(void) +{ + unsigned long err; + + while ((err = ERR_get_error())) { + /* Just ignore the errors, since stdout is disabled */ + } +} +#define tls_show_errors(l, f, t) _tls_show_errors() + +#else /* CONFIG_NO_STDOUT_DEBUG */ + +static void tls_show_errors(int level, const char *func, const char *txt) +{ + unsigned long err; + + wpa_printf(level, "OpenSSL: %s - %s %s", + func, txt, ERR_error_string(ERR_get_error(), NULL)); + + while ((err = ERR_get_error())) { + wpa_printf(MSG_INFO, "OpenSSL: pending error: %s", + ERR_error_string(err, NULL)); + } +} + +#endif /* CONFIG_NO_STDOUT_DEBUG */ + + +#ifdef CONFIG_NATIVE_WINDOWS + +/* Windows CryptoAPI and access to certificate stores */ +#include + +#ifdef __MINGW32_VERSION +/* + * MinGW does not yet include all the needed definitions for CryptoAPI, so + * define here whatever extra is needed. + */ +#define CALG_SSL3_SHAMD5 (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_SSL3SHAMD5) +#define CERT_SYSTEM_STORE_CURRENT_USER (1 << 16) +#define CERT_STORE_READONLY_FLAG 0x00008000 +#define CERT_STORE_OPEN_EXISTING_FLAG 0x00004000 +#define CRYPT_ACQUIRE_COMPARE_KEY_FLAG 0x00000004 + +static BOOL WINAPI +(*CryptAcquireCertificatePrivateKey)(PCCERT_CONTEXT pCert, DWORD dwFlags, + void *pvReserved, HCRYPTPROV *phCryptProv, + DWORD *pdwKeySpec, BOOL *pfCallerFreeProv) += NULL; /* to be loaded from crypt32.dll */ + +static PCCERT_CONTEXT WINAPI +(*CertEnumCertificatesInStore)(HCERTSTORE hCertStore, + PCCERT_CONTEXT pPrevCertContext) += NULL; /* to be loaded from crypt32.dll */ + +static int mingw_load_crypto_func(void) +{ + HINSTANCE dll; + + /* MinGW does not yet have full CryptoAPI support, so load the needed + * function here. */ + + if (CryptAcquireCertificatePrivateKey) + return 0; + + dll = LoadLibrary("crypt32"); + if (dll == NULL) { + wpa_printf(MSG_DEBUG, "CryptoAPI: Could not load crypt32 " + "library"); + return -1; + } + + CryptAcquireCertificatePrivateKey = GetProcAddress( + dll, "CryptAcquireCertificatePrivateKey"); + if (CryptAcquireCertificatePrivateKey == NULL) { + wpa_printf(MSG_DEBUG, "CryptoAPI: Could not get " + "CryptAcquireCertificatePrivateKey() address from " + "crypt32 library"); + return -1; + } + + CertEnumCertificatesInStore = (void *) GetProcAddress( + dll, "CertEnumCertificatesInStore"); + if (CertEnumCertificatesInStore == NULL) { + wpa_printf(MSG_DEBUG, "CryptoAPI: Could not get " + "CertEnumCertificatesInStore() address from " + "crypt32 library"); + return -1; + } + + return 0; +} + +#else /* __MINGW32_VERSION */ + +static int mingw_load_crypto_func(void) +{ + return 0; +} + +#endif /* __MINGW32_VERSION */ + + +struct cryptoapi_rsa_data { + const CERT_CONTEXT *cert; + HCRYPTPROV crypt_prov; + DWORD key_spec; + BOOL free_crypt_prov; +}; + + +static void cryptoapi_error(const char *msg) +{ + wpa_printf(MSG_INFO, "CryptoAPI: %s; err=%u", + msg, (unsigned int) GetLastError()); +} + + +static int cryptoapi_rsa_pub_enc(int flen, const unsigned char *from, + unsigned char *to, RSA *rsa, int padding) +{ + wpa_printf(MSG_DEBUG, "%s - not implemented", __func__); + return 0; +} + + +static int cryptoapi_rsa_pub_dec(int flen, const unsigned char *from, + unsigned char *to, RSA *rsa, int padding) +{ + wpa_printf(MSG_DEBUG, "%s - not implemented", __func__); + return 0; +} + + +static int cryptoapi_rsa_priv_enc(int flen, const unsigned char *from, + unsigned char *to, RSA *rsa, int padding) +{ + struct cryptoapi_rsa_data *priv = + (struct cryptoapi_rsa_data *) rsa->meth->app_data; + HCRYPTHASH hash; + DWORD hash_size, len, i; + unsigned char *buf = NULL; + int ret = 0; + + if (priv == NULL) { + RSAerr(RSA_F_RSA_EAY_PRIVATE_ENCRYPT, + ERR_R_PASSED_NULL_PARAMETER); + return 0; + } + + if (padding != RSA_PKCS1_PADDING) { + RSAerr(RSA_F_RSA_EAY_PRIVATE_ENCRYPT, + RSA_R_UNKNOWN_PADDING_TYPE); + return 0; + } + + if (flen != 16 /* MD5 */ + 20 /* SHA-1 */) { + wpa_printf(MSG_INFO, "%s - only MD5-SHA1 hash supported", + __func__); + RSAerr(RSA_F_RSA_EAY_PRIVATE_ENCRYPT, + RSA_R_INVALID_MESSAGE_LENGTH); + return 0; + } + + if (!CryptCreateHash(priv->crypt_prov, CALG_SSL3_SHAMD5, 0, 0, &hash)) + { + cryptoapi_error("CryptCreateHash failed"); + return 0; + } + + len = sizeof(hash_size); + if (!CryptGetHashParam(hash, HP_HASHSIZE, (BYTE *) &hash_size, &len, + 0)) { + cryptoapi_error("CryptGetHashParam failed"); + goto err; + } + + if (hash_size != flen) { + wpa_printf(MSG_INFO, "CryptoAPI: Invalid hash size (%u != %d)", + (unsigned) hash_size, flen); + RSAerr(RSA_F_RSA_EAY_PRIVATE_ENCRYPT, + RSA_R_INVALID_MESSAGE_LENGTH); + goto err; + } + if (!CryptSetHashParam(hash, HP_HASHVAL, (BYTE * ) from, 0)) { + cryptoapi_error("CryptSetHashParam failed"); + goto err; + } + + len = RSA_size(rsa); + buf = malloc(len); + if (buf == NULL) { + RSAerr(RSA_F_RSA_EAY_PRIVATE_ENCRYPT, ERR_R_MALLOC_FAILURE); + goto err; + } + + if (!CryptSignHash(hash, priv->key_spec, NULL, 0, buf, &len)) { + cryptoapi_error("CryptSignHash failed"); + goto err; + } + + for (i = 0; i < len; i++) + to[i] = buf[len - i - 1]; + ret = len; + +err: + free(buf); + CryptDestroyHash(hash); + + return ret; +} + + +static int cryptoapi_rsa_priv_dec(int flen, const unsigned char *from, + unsigned char *to, RSA *rsa, int padding) +{ + wpa_printf(MSG_DEBUG, "%s - not implemented", __func__); + return 0; +} + + +static void cryptoapi_free_data(struct cryptoapi_rsa_data *priv) +{ + if (priv == NULL) + return; + if (priv->crypt_prov && priv->free_crypt_prov) + CryptReleaseContext(priv->crypt_prov, 0); + if (priv->cert) + CertFreeCertificateContext(priv->cert); + free(priv); +} + + +static int cryptoapi_finish(RSA *rsa) +{ + cryptoapi_free_data((struct cryptoapi_rsa_data *) rsa->meth->app_data); + free((void *) rsa->meth); + rsa->meth = NULL; + return 1; +} + + +static const CERT_CONTEXT * cryptoapi_find_cert(const char *name, DWORD store) +{ + HCERTSTORE cs; + const CERT_CONTEXT *ret = NULL; + + cs = CertOpenStore((LPCSTR) CERT_STORE_PROV_SYSTEM, 0, 0, + store | CERT_STORE_OPEN_EXISTING_FLAG | + CERT_STORE_READONLY_FLAG, L"MY"); + if (cs == NULL) { + cryptoapi_error("Failed to open 'My system store'"); + return NULL; + } + + if (strncmp(name, "cert://", 7) == 0) { + unsigned short wbuf[255]; + MultiByteToWideChar(CP_ACP, 0, name + 7, -1, + wbuf, sizeof(wbuf)); + ret = CertFindCertificateInStore(cs, X509_ASN_ENCODING | + PKCS_7_ASN_ENCODING, + 0, CERT_FIND_SUBJECT_STR, + wbuf, NULL); + } else if (strncmp(name, "hash://", 7) == 0) { + CRYPT_HASH_BLOB blob; + int len; + const char *hash = name + 7; + unsigned char *buf; + + len = strlen(hash) / 2; + buf = malloc(len); + if (buf && hexstr2bin(hash, buf, len) == 0) { + blob.cbData = len; + blob.pbData = buf; + ret = CertFindCertificateInStore(cs, + X509_ASN_ENCODING | + PKCS_7_ASN_ENCODING, + 0, CERT_FIND_HASH, + &blob, NULL); + } + free(buf); + } + + CertCloseStore(cs, 0); + + return ret; +} + + +static int tls_cryptoapi_cert(SSL *ssl, const char *name) +{ + X509 *cert = NULL; + RSA *rsa = NULL, *pub_rsa; + struct cryptoapi_rsa_data *priv; + RSA_METHOD *rsa_meth; + + if (name == NULL || + (strncmp(name, "cert://", 7) != 0 && + strncmp(name, "hash://", 7) != 0)) + return -1; + + priv = malloc(sizeof(*priv)); + rsa_meth = malloc(sizeof(*rsa_meth)); + if (priv == NULL || rsa_meth == NULL) { + wpa_printf(MSG_WARNING, "CryptoAPI: Failed to allocate memory " + "for CryptoAPI RSA method"); + free(priv); + free(rsa_meth); + return -1; + } + memset(priv, 0, sizeof(*priv)); + memset(rsa_meth, 0, sizeof(*rsa_meth)); + + priv->cert = cryptoapi_find_cert(name, CERT_SYSTEM_STORE_CURRENT_USER); + if (priv->cert == NULL) { + priv->cert = cryptoapi_find_cert( + name, CERT_SYSTEM_STORE_LOCAL_MACHINE); + } + if (priv->cert == NULL) { + wpa_printf(MSG_INFO, "CryptoAPI: Could not find certificate " + "'%s'", name); + goto err; + } + + cert = d2i_X509(NULL, (OPENSSL_d2i_TYPE) &priv->cert->pbCertEncoded, + priv->cert->cbCertEncoded); + if (cert == NULL) { + wpa_printf(MSG_INFO, "CryptoAPI: Could not process X509 DER " + "encoding"); + goto err; + } + + if (mingw_load_crypto_func()) + goto err; + + if (!CryptAcquireCertificatePrivateKey(priv->cert, + CRYPT_ACQUIRE_COMPARE_KEY_FLAG, + NULL, &priv->crypt_prov, + &priv->key_spec, + &priv->free_crypt_prov)) { + cryptoapi_error("Failed to acquire a private key for the " + "certificate"); + goto err; + } + + rsa_meth->name = "Microsoft CryptoAPI RSA Method"; + rsa_meth->rsa_pub_enc = cryptoapi_rsa_pub_enc; + rsa_meth->rsa_pub_dec = cryptoapi_rsa_pub_dec; + rsa_meth->rsa_priv_enc = cryptoapi_rsa_priv_enc; + rsa_meth->rsa_priv_dec = cryptoapi_rsa_priv_dec; + rsa_meth->finish = cryptoapi_finish; + rsa_meth->flags = RSA_METHOD_FLAG_NO_CHECK; + rsa_meth->app_data = (char *) priv; + + rsa = RSA_new(); + if (rsa == NULL) { + SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_FILE, + ERR_R_MALLOC_FAILURE); + goto err; + } + + if (!SSL_use_certificate(ssl, cert)) + goto err; + pub_rsa = cert->cert_info->key->pkey->pkey.rsa; + X509_free(cert); + cert = NULL; + + rsa->n = BN_dup(pub_rsa->n); + rsa->e = BN_dup(pub_rsa->e); + if (!RSA_set_method(rsa, rsa_meth)) + goto err; + + if (!SSL_use_RSAPrivateKey(ssl, rsa)) + goto err; + RSA_free(rsa); + + return 0; + +err: + if (cert) + X509_free(cert); + if (rsa) + RSA_free(rsa); + else { + free(rsa_meth); + cryptoapi_free_data(priv); + } + return -1; +} + + +static int tls_cryptoapi_ca_cert(SSL_CTX *ssl_ctx, SSL *ssl, const char *name) +{ + HCERTSTORE cs; + PCCERT_CONTEXT ctx = NULL; + X509 *cert; + char buf[128]; + + if (mingw_load_crypto_func()) + return -1; + + if (name == NULL || strncmp(name, "cert_store://", 13) != 0) + return -1; + + cs = CertOpenSystemStore(0, name + 13); + if (cs == NULL) { + wpa_printf(MSG_DEBUG, "%s: failed to open system cert store " + "'%s': error=%d", __func__, name + 13, + (int) GetLastError()); + return -1; + } + + while ((ctx = CertEnumCertificatesInStore(cs, ctx))) { + cert = d2i_X509(NULL, (OPENSSL_d2i_TYPE) &ctx->pbCertEncoded, + ctx->cbCertEncoded); + if (cert == NULL) { + wpa_printf(MSG_INFO, "CryptoAPI: Could not process " + "X509 DER encoding for CA cert"); + continue; + } + + X509_NAME_oneline(X509_get_subject_name(cert), buf, + sizeof(buf)); + wpa_printf(MSG_DEBUG, "OpenSSL: Loaded CA certificate for " + "system certificate store: subject='%s'", buf); + + if (!X509_STORE_add_cert(ssl_ctx->cert_store, cert)) { + tls_show_errors(MSG_WARNING, __func__, + "Failed to add ca_cert to OpenSSL " + "certificate store"); + } + + X509_free(cert); + } + + if (!CertCloseStore(cs, 0)) { + wpa_printf(MSG_DEBUG, "%s: failed to close system cert store " + "'%s': error=%d", __func__, name + 13, + (int) GetLastError()); + } + + return 0; +} + + +#else /* CONFIG_NATIVE_WINDOWS */ + +static int tls_cryptoapi_cert(SSL *ssl, const char *name) +{ + return -1; +} + +#endif /* CONFIG_NATIVE_WINDOWS */ + + +static void ssl_info_cb(const SSL *ssl, int where, int ret) +{ + const char *str; + int w; + + wpa_printf(MSG_DEBUG, "SSL: (where=0x%x ret=0x%x)", where, ret); + w = where & ~SSL_ST_MASK; + if (w & SSL_ST_CONNECT) + str = "SSL_connect"; + else if (w & SSL_ST_ACCEPT) + str = "SSL_accept"; + else + str = "undefined"; + + if (where & SSL_CB_LOOP) { + wpa_printf(MSG_DEBUG, "SSL: %s:%s", + str, SSL_state_string_long(ssl)); + } else if (where & SSL_CB_ALERT) { + wpa_printf(MSG_INFO, "SSL: SSL3 alert: %s:%s:%s", + where & SSL_CB_READ ? + "read (remote end reported an error)" : + "write (local SSL3 detected an error)", + SSL_alert_type_string_long(ret), + SSL_alert_desc_string_long(ret)); + if ((ret >> 8) == SSL3_AL_FATAL) { + struct tls_connection *conn = + SSL_get_app_data((SSL *) ssl); + if (where & SSL_CB_READ) + conn->read_alerts++; + else + conn->write_alerts++; + } + } else if (where & SSL_CB_EXIT && ret <= 0) { + wpa_printf(MSG_DEBUG, "SSL: %s:%s in %s", + str, ret == 0 ? "failed" : "error", + SSL_state_string_long(ssl)); + } +} + + +#ifndef OPENSSL_NO_ENGINE +/** + * tls_engine_load_dynamic_generic - load any openssl engine + * @pre: an array of commands and values that load an engine initialized + * in the engine specific function + * @post: an array of commands and values that initialize an already loaded + * engine (or %NULL if not required) + * @id: the engine id of the engine to load (only required if post is not %NULL + * + * This function is a generic function that loads any openssl engine. + * + * Returns: 0 on success, -1 on failure + */ +static int tls_engine_load_dynamic_generic(const char *pre[], + const char *post[], const char *id) +{ + ENGINE *engine; + const char *dynamic_id = "dynamic"; + + engine = ENGINE_by_id(id); + if (engine) { + ENGINE_free(engine); + wpa_printf(MSG_DEBUG, "ENGINE: engine '%s' is already " + "available", id); + return 0; + } + ERR_clear_error(); + + engine = ENGINE_by_id(dynamic_id); + if (engine == NULL) { + wpa_printf(MSG_INFO, "ENGINE: Can't find engine %s [%s]", + dynamic_id, + ERR_error_string(ERR_get_error(), NULL)); + return -1; + } + + /* Perform the pre commands. This will load the engine. */ + while (pre && pre[0]) { + wpa_printf(MSG_DEBUG, "ENGINE: '%s' '%s'", pre[0], pre[1]); + if (ENGINE_ctrl_cmd_string(engine, pre[0], pre[1], 0) == 0) { + wpa_printf(MSG_INFO, "ENGINE: ctrl cmd_string failed: " + "%s %s [%s]", pre[0], pre[1], + ERR_error_string(ERR_get_error(), NULL)); + ENGINE_free(engine); + return -1; + } + pre += 2; + } + + /* + * Free the reference to the "dynamic" engine. The loaded engine can + * now be looked up using ENGINE_by_id(). + */ + ENGINE_free(engine); + + engine = ENGINE_by_id(id); + if (engine == NULL) { + wpa_printf(MSG_INFO, "ENGINE: Can't find engine %s [%s]", + id, ERR_error_string(ERR_get_error(), NULL)); + return -1; + } + + while (post && post[0]) { + wpa_printf(MSG_DEBUG, "ENGINE: '%s' '%s'", post[0], post[1]); + if (ENGINE_ctrl_cmd_string(engine, post[0], post[1], 0) == 0) { + wpa_printf(MSG_DEBUG, "ENGINE: ctrl cmd_string failed:" + " %s %s [%s]", post[0], post[1], + ERR_error_string(ERR_get_error(), NULL)); + ENGINE_remove(engine); + ENGINE_free(engine); + return -1; + } + post += 2; + } + ENGINE_free(engine); + + return 0; +} + + +/** + * tls_engine_load_dynamic_pkcs11 - load the pkcs11 engine provided by opensc + * @pkcs11_so_path: pksc11_so_path from the configuration + * @pcks11_module_path: pkcs11_module_path from the configuration + */ +static int tls_engine_load_dynamic_pkcs11(const char *pkcs11_so_path, + const char *pkcs11_module_path) +{ + char *engine_id = "pkcs11"; + const char *pre_cmd[] = { + "SO_PATH", pkcs11_so_path, + "ID", engine_id, + "LIST_ADD", "1", + /* "NO_VCHECK", "1", */ + "LOAD", NULL, + NULL, NULL + }; + const char *post_cmd[] = { + "MODULE_PATH", pkcs11_module_path, + NULL, NULL + }; + + if (!pkcs11_so_path || !pkcs11_module_path) + return 0; + + wpa_printf(MSG_DEBUG, "ENGINE: Loading pkcs11 Engine from %s", + pkcs11_so_path); + + return tls_engine_load_dynamic_generic(pre_cmd, post_cmd, engine_id); +} + + +/** + * tls_engine_load_dynamic_opensc - load the opensc engine provided by opensc + * @opensc_so_path: opensc_so_path from the configuration + */ +static int tls_engine_load_dynamic_opensc(const char *opensc_so_path) +{ + char *engine_id = "opensc"; + const char *pre_cmd[] = { + "SO_PATH", opensc_so_path, + "ID", engine_id, + "LIST_ADD", "1", + "LOAD", NULL, + NULL, NULL + }; + + if (!opensc_so_path) + return 0; + + wpa_printf(MSG_DEBUG, "ENGINE: Loading OpenSC Engine from %s", + opensc_so_path); + + return tls_engine_load_dynamic_generic(pre_cmd, NULL, engine_id); +} +#endif /* OPENSSL_NO_ENGINE */ + + +void * tls_init(const struct tls_config *conf) +{ + SSL_CTX *ssl; + + if (tls_openssl_ref_count == 0) { + SSL_load_error_strings(); + SSL_library_init(); + /* TODO: if /dev/urandom is available, PRNG is seeded + * automatically. If this is not the case, random data should + * be added here. */ + +#ifdef PKCS12_FUNCS + PKCS12_PBE_add(); +#endif /* PKCS12_FUNCS */ + } + tls_openssl_ref_count++; + + ssl = SSL_CTX_new(TLSv1_method()); + if (ssl == NULL) + return NULL; + + SSL_CTX_set_info_callback(ssl, ssl_info_cb); + +#ifndef OPENSSL_NO_ENGINE + if (conf && + (conf->opensc_engine_path || conf->pkcs11_engine_path || + conf->pkcs11_module_path)) { + wpa_printf(MSG_DEBUG, "ENGINE: Loading dynamic engine"); + ERR_load_ENGINE_strings(); + ENGINE_load_dynamic(); + + if (tls_engine_load_dynamic_opensc(conf->opensc_engine_path) || + tls_engine_load_dynamic_pkcs11(conf->pkcs11_engine_path, + conf->pkcs11_module_path)) { + tls_deinit(ssl); + return NULL; + } + } +#endif /* OPENSSL_NO_ENGINE */ + + return ssl; +} + + +void tls_deinit(void *ssl_ctx) +{ + SSL_CTX *ssl = ssl_ctx; + SSL_CTX_free(ssl); + + tls_openssl_ref_count--; + if (tls_openssl_ref_count == 0) { +#ifndef OPENSSL_NO_ENGINE + ENGINE_cleanup(); +#endif /* OPENSSL_NO_ENGINE */ + ERR_free_strings(); + EVP_cleanup(); + } +} + + +static int tls_engine_init(struct tls_connection *conn, const char *engine_id, + const char *pin, const char *key_id) +{ +#ifndef OPENSSL_NO_ENGINE + int ret = -1; + if (engine_id == NULL) { + wpa_printf(MSG_ERROR, "ENGINE: Engine ID not set"); + return -1; + } + if (pin == NULL) { + wpa_printf(MSG_ERROR, "ENGINE: Smartcard PIN not set"); + return -1; + } + if (key_id == NULL) { + wpa_printf(MSG_ERROR, "ENGINE: Key Id not set"); + return -1; + } + + ERR_clear_error(); + conn->engine = ENGINE_by_id(engine_id); + if (!conn->engine) { + wpa_printf(MSG_ERROR, "ENGINE: engine %s not available [%s]", + engine_id, ERR_error_string(ERR_get_error(), NULL)); + goto err; + } + if (ENGINE_init(conn->engine) != 1) { + wpa_printf(MSG_ERROR, "ENGINE: engine init failed " + "(engine: %s) [%s]", engine_id, + ERR_error_string(ERR_get_error(), NULL)); + goto err; + } + wpa_printf(MSG_DEBUG, "ENGINE: engine initialized"); + + if (ENGINE_ctrl_cmd_string(conn->engine, "PIN", pin, 0) == 0) { + wpa_printf(MSG_ERROR, "ENGINE: cannot set pin [%s]", + ERR_error_string(ERR_get_error(), NULL)); + goto err; + } + conn->private_key = ENGINE_load_private_key(conn->engine, + key_id, NULL, NULL); + if (!conn->private_key) { + wpa_printf(MSG_ERROR, "ENGINE: cannot load private key with id" + " '%s' [%s]", key_id, + ERR_error_string(ERR_get_error(), NULL)); + ret = TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED; + goto err; + } + return 0; + +err: + if (conn->engine) { + ENGINE_free(conn->engine); + conn->engine = NULL; + } + + if (conn->private_key) { + EVP_PKEY_free(conn->private_key); + conn->private_key = NULL; + } + + return ret; +#else /* OPENSSL_NO_ENGINE */ + return 0; +#endif /* OPENSSL_NO_ENGINE */ +} + + +static void tls_engine_deinit(struct tls_connection *conn) +{ +#ifndef OPENSSL_NO_ENGINE + wpa_printf(MSG_DEBUG, "ENGINE: engine deinit"); + if (conn->private_key) { + EVP_PKEY_free(conn->private_key); + conn->private_key = NULL; + } + if (conn->engine) { + ENGINE_finish(conn->engine); + conn->engine = NULL; + } +#endif /* OPENSSL_NO_ENGINE */ +} + + +int tls_get_errors(void *ssl_ctx) +{ + int count = 0; + unsigned long err; + + while ((err = ERR_get_error())) { + wpa_printf(MSG_INFO, "TLS - SSL error: %s", + ERR_error_string(err, NULL)); + count++; + } + + return count; +} + +struct tls_connection * tls_connection_init(void *ssl_ctx) +{ + SSL_CTX *ssl = ssl_ctx; + struct tls_connection *conn; + + conn = malloc(sizeof(*conn)); + if (conn == NULL) + return NULL; + memset(conn, 0, sizeof(*conn)); + conn->ssl = SSL_new(ssl); + if (conn->ssl == NULL) { + tls_show_errors(MSG_INFO, __func__, + "Failed to initialize new SSL connection"); + free(conn); + return NULL; + } + + SSL_set_app_data(conn->ssl, conn); + SSL_set_options(conn->ssl, + SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | + SSL_OP_SINGLE_DH_USE); + + conn->ssl_in = BIO_new(BIO_s_mem()); + if (!conn->ssl_in) { + tls_show_errors(MSG_INFO, __func__, + "Failed to create a new BIO for ssl_in"); + SSL_free(conn->ssl); + free(conn); + return NULL; + } + + conn->ssl_out = BIO_new(BIO_s_mem()); + if (!conn->ssl_out) { + tls_show_errors(MSG_INFO, __func__, + "Failed to create a new BIO for ssl_out"); + SSL_free(conn->ssl); + BIO_free(conn->ssl_in); + free(conn); + return NULL; + } + + SSL_set_bio(conn->ssl, conn->ssl_in, conn->ssl_out); + + return conn; +} + + +void tls_connection_deinit(void *ssl_ctx, struct tls_connection *conn) +{ + if (conn == NULL) + return; + free(conn->pre_shared_secret); + SSL_free(conn->ssl); + tls_engine_deinit(conn); + free(conn->subject_match); + free(conn->altsubject_match); + free(conn); +} + + +int tls_connection_established(void *ssl_ctx, struct tls_connection *conn) +{ + return conn ? SSL_is_init_finished(conn->ssl) : 0; +} + + +int tls_connection_shutdown(void *ssl_ctx, struct tls_connection *conn) +{ + if (conn == NULL) + return -1; + + /* Shutdown previous TLS connection without notifying the peer + * because the connection was already terminated in practice + * and "close notify" shutdown alert would confuse AS. */ + SSL_set_quiet_shutdown(conn->ssl, 1); + SSL_shutdown(conn->ssl); + return 0; +} + + +static int tls_match_altsubject(X509 *cert, const char *match) +{ + GENERAL_NAME *gen; + char *field, *tmp; + void *ext; + int i, found = 0; + size_t len; + + ext = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL); + + for (i = 0; ext && i < sk_GENERAL_NAME_num(ext); i++) { + gen = sk_GENERAL_NAME_value(ext, i); + switch (gen->type) { + case GEN_EMAIL: + field = "EMAIL"; + break; + case GEN_DNS: + field = "DNS"; + break; + case GEN_URI: + field = "URI"; + break; + default: + field = NULL; + wpa_printf(MSG_DEBUG, "TLS: altSubjectName: " + "unsupported type=%d", gen->type); + break; + } + + if (!field) + continue; + + wpa_printf(MSG_DEBUG, "TLS: altSubjectName: %s:%s", + field, gen->d.ia5->data); + len = strlen(field) + 1 + strlen((char *) gen->d.ia5->data) + + 1; + tmp = malloc(len); + if (tmp == NULL) + continue; + snprintf(tmp, len, "%s:%s", field, gen->d.ia5->data); + if (strstr(tmp, match)) + found++; + free(tmp); + } + + return found; +} + + +static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx) +{ + char buf[256]; + X509 *err_cert; + int err, depth; + SSL *ssl; + struct tls_connection *conn; + char *match, *altmatch; + + err_cert = X509_STORE_CTX_get_current_cert(x509_ctx); + err = X509_STORE_CTX_get_error(x509_ctx); + depth = X509_STORE_CTX_get_error_depth(x509_ctx); + ssl = X509_STORE_CTX_get_ex_data(x509_ctx, + SSL_get_ex_data_X509_STORE_CTX_idx()); + X509_NAME_oneline(X509_get_subject_name(err_cert), buf, sizeof(buf)); + + conn = SSL_get_app_data(ssl); + match = conn ? conn->subject_match : NULL; + altmatch = conn ? conn->altsubject_match : NULL; + + if (!preverify_ok) { + wpa_printf(MSG_WARNING, "TLS: Certificate verification failed," + " error %d (%s) depth %d for '%s'", err, + X509_verify_cert_error_string(err), depth, buf); + } else { + wpa_printf(MSG_DEBUG, "TLS: tls_verify_cb - " + "preverify_ok=%d err=%d (%s) depth=%d buf='%s'", + preverify_ok, err, + X509_verify_cert_error_string(err), depth, buf); + if (depth == 0 && match && strstr(buf, match) == NULL) { + wpa_printf(MSG_WARNING, "TLS: Subject '%s' did not " + "match with '%s'", buf, match); + preverify_ok = 0; + } else if (depth == 0 && altmatch && + !tls_match_altsubject(err_cert, altmatch)) { + wpa_printf(MSG_WARNING, "TLS: altSubjectName match " + "'%s' not found", altmatch); + preverify_ok = 0; + } + } + + return preverify_ok; +} + + +#ifndef OPENSSL_NO_STDIO +static int tls_load_ca_der(void *_ssl_ctx, const char *ca_cert) +{ + SSL_CTX *ssl_ctx = _ssl_ctx; + X509_LOOKUP *lookup; + int ret = 0; + + lookup = X509_STORE_add_lookup(ssl_ctx->cert_store, + X509_LOOKUP_file()); + if (lookup == NULL) { + tls_show_errors(MSG_WARNING, __func__, + "Failed add lookup for X509 store"); + return -1; + } + + if (!X509_LOOKUP_load_file(lookup, ca_cert, X509_FILETYPE_ASN1)) { + unsigned long err = ERR_peek_error(); + tls_show_errors(MSG_WARNING, __func__, + "Failed load CA in DER format"); + if (ERR_GET_LIB(err) == ERR_LIB_X509 && + ERR_GET_REASON(err) == X509_R_CERT_ALREADY_IN_HASH_TABLE) { + wpa_printf(MSG_DEBUG, "OpenSSL: %s - ignoring " + "cert already in hash table error", + __func__); + } else + ret = -1; + } + + return ret; +} +#endif /* OPENSSL_NO_STDIO */ + + +static int tls_connection_ca_cert(void *_ssl_ctx, struct tls_connection *conn, + const char *ca_cert, const u8 *ca_cert_blob, + size_t ca_cert_blob_len, const char *ca_path) +{ + SSL_CTX *ssl_ctx = _ssl_ctx; + + if (ca_cert_blob) { + X509 *cert = d2i_X509(NULL, (OPENSSL_d2i_TYPE) &ca_cert_blob, + ca_cert_blob_len); + if (cert == NULL) { + tls_show_errors(MSG_WARNING, __func__, + "Failed to parse ca_cert_blob"); + return -1; + } + + if (!X509_STORE_add_cert(ssl_ctx->cert_store, cert)) { + tls_show_errors(MSG_WARNING, __func__, + "Failed to add ca_cert_blob to " + "certificate store"); + X509_free(cert); + return -1; + } + X509_free(cert); + wpa_printf(MSG_DEBUG, "OpenSSL: %s - added ca_cert_blob " + "to certificate store", __func__); + SSL_set_verify(conn->ssl, SSL_VERIFY_PEER, tls_verify_cb); + return 0; + } + +#ifdef CONFIG_NATIVE_WINDOWS + if (ca_cert && tls_cryptoapi_ca_cert(ssl_ctx, conn->ssl, ca_cert) == + 0) { + wpa_printf(MSG_DEBUG, "OpenSSL: Added CA certificates from " + "system certificate store"); + SSL_set_verify(conn->ssl, SSL_VERIFY_PEER, tls_verify_cb); + return 0; + } +#endif /* CONFIG_NATIVE_WINDOWS */ + + if (ca_cert || ca_path) { +#ifndef OPENSSL_NO_STDIO + if (SSL_CTX_load_verify_locations(ssl_ctx, ca_cert, ca_path) != + 1) { + tls_show_errors(MSG_WARNING, __func__, + "Failed to load root certificates"); + if (ca_cert && + tls_load_ca_der(ssl_ctx, ca_cert) == 0) { + wpa_printf(MSG_DEBUG, "OpenSSL: %s - loaded " + "DER format CA certificate", + __func__); + } else + return -1; + } else { + wpa_printf(MSG_DEBUG, "TLS: Trusted root " + "certificate(s) loaded"); + tls_get_errors(ssl_ctx); + } + SSL_set_verify(conn->ssl, SSL_VERIFY_PEER, tls_verify_cb); +#else /* OPENSSL_NO_STDIO */ + wpa_printf(MSG_DEBUG, "OpenSSL: %s - OPENSSL_NO_STDIO", + __func__); + return -1; +#endif /* OPENSSL_NO_STDIO */ + } else { + /* No ca_cert configured - do not try to verify server + * certificate */ + SSL_set_verify(conn->ssl, SSL_VERIFY_NONE, NULL); + } + + return 0; +} + + +int tls_global_ca_cert(void *_ssl_ctx, const char *ca_cert) +{ + SSL_CTX *ssl_ctx = _ssl_ctx; + if (ca_cert) { + if (SSL_CTX_load_verify_locations(ssl_ctx, ca_cert, NULL) != 1) + { + tls_show_errors(MSG_WARNING, __func__, + "Failed to load root certificates"); + return -1; + } + + wpa_printf(MSG_DEBUG, "TLS: Trusted root " + "certificate(s) loaded"); + +#ifndef OPENSSL_NO_STDIO + /* Add the same CAs to the client certificate requests */ + SSL_CTX_set_client_CA_list(ssl_ctx, + SSL_load_client_CA_file(ca_cert)); +#endif /* OPENSSL_NO_STDIO */ + } + + return 0; +} + + +int tls_global_set_verify(void *ssl_ctx, int check_crl) +{ + int flags; + + if (check_crl) { + X509_STORE *cs = SSL_CTX_get_cert_store(ssl_ctx); + if (cs == NULL) { + tls_show_errors(MSG_INFO, __func__, "Failed to get " + "certificate store when enabling " + "check_crl"); + return -1; + } + flags = X509_V_FLAG_CRL_CHECK; + if (check_crl == 2) + flags |= X509_V_FLAG_CRL_CHECK_ALL; + X509_STORE_set_flags(cs, flags); + } + return 0; +} + + +static int tls_connection_set_subject_match(void *ssl_ctx, + struct tls_connection *conn, + const char *subject_match, + const char *altsubject_match) +{ + free(conn->subject_match); + conn->subject_match = NULL; + if (subject_match) { + conn->subject_match = strdup(subject_match); + if (conn->subject_match == NULL) + return -1; + } + + free(conn->altsubject_match); + conn->altsubject_match = NULL; + if (altsubject_match) { + conn->altsubject_match = strdup(altsubject_match); + if (conn->altsubject_match == NULL) + return -1; + } + + return 0; +} + + +int tls_connection_set_verify(void *ssl_ctx, struct tls_connection *conn, + int verify_peer) +{ + if (conn == NULL) + return -1; + + if (verify_peer) { + SSL_set_verify(conn->ssl, SSL_VERIFY_PEER | + SSL_VERIFY_FAIL_IF_NO_PEER_CERT | + SSL_VERIFY_CLIENT_ONCE, tls_verify_cb); + } else { + SSL_set_verify(conn->ssl, SSL_VERIFY_NONE, NULL); + } + + SSL_set_accept_state(conn->ssl); + + return 0; +} + + +static int tls_connection_client_cert(void *ssl_ctx, + struct tls_connection *conn, + const char *client_cert, + const u8 *client_cert_blob, + size_t client_cert_blob_len) +{ + if (client_cert == NULL && client_cert_blob == NULL) + return 0; + + if (client_cert_blob && + SSL_use_certificate_ASN1(conn->ssl, (u8 *) client_cert_blob, + client_cert_blob_len) == 1) { + wpa_printf(MSG_DEBUG, "OpenSSL: SSL_use_certificate_ASN1 --> " + "OK"); + return 0; + } else if (client_cert_blob) { + tls_show_errors(MSG_DEBUG, __func__, + "SSL_use_certificate_ASN1 failed"); + } + + if (client_cert == NULL) + return -1; + +#ifndef OPENSSL_NO_STDIO + if (SSL_use_certificate_file(conn->ssl, client_cert, + SSL_FILETYPE_ASN1) == 1) { + wpa_printf(MSG_DEBUG, "OpenSSL: SSL_use_certificate_file (DER)" + " --> OK"); + return 0; + } else { + tls_show_errors(MSG_DEBUG, __func__, + "SSL_use_certificate_file (DER) failed"); + } + + if (SSL_use_certificate_file(conn->ssl, client_cert, + SSL_FILETYPE_PEM) == 1) { + wpa_printf(MSG_DEBUG, "OpenSSL: SSL_use_certificate_file (PEM)" + " --> OK"); + return 0; + } else { + tls_show_errors(MSG_DEBUG, __func__, + "SSL_use_certificate_file (PEM) failed"); + } +#else /* OPENSSL_NO_STDIO */ + wpa_printf(MSG_DEBUG, "OpenSSL: %s - OPENSSL_NO_STDIO", __func__); +#endif /* OPENSSL_NO_STDIO */ + + return -1; +} + + +int tls_global_client_cert(void *_ssl_ctx, const char *client_cert) +{ +#ifndef OPENSSL_NO_STDIO + SSL_CTX *ssl_ctx = _ssl_ctx; + if (client_cert == NULL) + return 0; + + if (SSL_CTX_use_certificate_file(ssl_ctx, client_cert, + SSL_FILETYPE_ASN1) != 1 && + SSL_CTX_use_certificate_file(ssl_ctx, client_cert, + SSL_FILETYPE_PEM) != 1) { + tls_show_errors(MSG_INFO, __func__, + "Failed to load client certificate"); + return -1; + } + return 0; +#else /* OPENSSL_NO_STDIO */ + if (client_cert == NULL) + return 0; + wpa_printf(MSG_DEBUG, "OpenSSL: %s - OPENSSL_NO_STDIO", __func__); + return -1; +#endif /* OPENSSL_NO_STDIO */ +} + + +static int tls_passwd_cb(char *buf, int size, int rwflag, void *password) +{ + if (password == NULL) { + return 0; + } + strncpy(buf, (char *) password, size); + buf[size - 1] = '\0'; + return strlen(buf); +} + + +#ifdef PKCS12_FUNCS +static int tls_parse_pkcs12(SSL_CTX *ssl_ctx, SSL *ssl, PKCS12 *p12, + const char *passwd) +{ + EVP_PKEY *pkey; + X509 *cert; + STACK_OF(X509) *certs; + int res = 0; + char buf[256]; + + pkey = NULL; + cert = NULL; + certs = NULL; + if (!PKCS12_parse(p12, passwd, &pkey, &cert, &certs)) { + tls_show_errors(MSG_DEBUG, __func__, + "Failed to parse PKCS12 file"); + PKCS12_free(p12); + return -1; + } + wpa_printf(MSG_DEBUG, "TLS: Successfully parsed PKCS12 data"); + + if (cert) { + X509_NAME_oneline(X509_get_subject_name(cert), buf, + sizeof(buf)); + wpa_printf(MSG_DEBUG, "TLS: Got certificate from PKCS12: " + "subject='%s'", buf); + if (ssl) { + if (SSL_use_certificate(ssl, cert) != 1) + res = -1; + } else { + if (SSL_CTX_use_certificate(ssl_ctx, cert) != 1) + res = -1; + } + X509_free(cert); + } + + if (pkey) { + wpa_printf(MSG_DEBUG, "TLS: Got private key from PKCS12"); + if (ssl) { + if (SSL_use_PrivateKey(ssl, pkey) != 1) + res = -1; + } else { + if (SSL_CTX_use_PrivateKey(ssl_ctx, pkey) != 1) + res = -1; + } + EVP_PKEY_free(pkey); + } + + if (certs) { + while ((cert = sk_X509_pop(certs)) != NULL) { + X509_NAME_oneline(X509_get_subject_name(cert), buf, + sizeof(buf)); + wpa_printf(MSG_DEBUG, "TLS: additional certificate" + " from PKCS12: subject='%s'", buf); + /* + * There is no SSL equivalent for the chain cert - so + * always add it to the context... + */ + if (SSL_CTX_add_extra_chain_cert(ssl_ctx, cert) != 1) { + res = -1; + break; + } + } + sk_X509_free(certs); + } + + PKCS12_free(p12); + + if (res < 0) + tls_get_errors(ssl_ctx); + + return res; +} +#endif /* PKCS12_FUNCS */ + + +static int tls_read_pkcs12(SSL_CTX *ssl_ctx, SSL *ssl, const char *private_key, + const char *passwd) +{ +#ifdef PKCS12_FUNCS + FILE *f; + PKCS12 *p12; + + f = fopen(private_key, "r"); + if (f == NULL) + return -1; + + p12 = d2i_PKCS12_fp(f, NULL); + fclose(f); + + if (p12 == NULL) { + tls_show_errors(MSG_INFO, __func__, + "Failed to use PKCS#12 file"); + return -1; + } + + return tls_parse_pkcs12(ssl_ctx, ssl, p12, passwd); + +#else /* PKCS12_FUNCS */ + wpa_printf(MSG_INFO, "TLS: PKCS12 support disabled - cannot read " + "p12/pfx files"); + return -1; +#endif /* PKCS12_FUNCS */ +} + + +static int tls_read_pkcs12_blob(SSL_CTX *ssl_ctx, SSL *ssl, + const u8 *blob, size_t len, const char *passwd) +{ +#ifdef PKCS12_FUNCS + PKCS12 *p12; + + p12 = d2i_PKCS12(NULL, (OPENSSL_d2i_TYPE) &blob, len); + if (p12 == NULL) { + tls_show_errors(MSG_INFO, __func__, + "Failed to use PKCS#12 blob"); + return -1; + } + + return tls_parse_pkcs12(ssl_ctx, ssl, p12, passwd); + +#else /* PKCS12_FUNCS */ + wpa_printf(MSG_INFO, "TLS: PKCS12 support disabled - cannot parse " + "p12/pfx blobs"); + return -1; +#endif /* PKCS12_FUNCS */ +} + + +static int tls_connection_engine_private_key(void *_ssl_ctx, + struct tls_connection *conn) +{ +#ifndef OPENSSL_NO_ENGINE + if (SSL_use_PrivateKey(conn->ssl, conn->private_key) != 1) { + tls_show_errors(MSG_ERROR, __func__, + "ENGINE: cannot use private key for TLS"); + return -1; + } + if (!SSL_check_private_key(conn->ssl)) { + tls_show_errors(MSG_INFO, __func__, + "Private key failed verification"); + return -1; + } + return 0; +#else /* OPENSSL_NO_ENGINE */ + wpa_printf(MSG_ERROR, "SSL: Configuration uses engine, but " + "engine support was not compiled in"); + return -1; +#endif /* OPENSSL_NO_ENGINE */ +} + + +static int tls_connection_private_key(void *_ssl_ctx, + struct tls_connection *conn, + const char *private_key, + const char *private_key_passwd, + const u8 *private_key_blob, + size_t private_key_blob_len) +{ + SSL_CTX *ssl_ctx = _ssl_ctx; + char *passwd; + int ok; + + if (private_key == NULL && private_key_blob == NULL) + return 0; + + if (private_key_passwd) { + passwd = strdup(private_key_passwd); + if (passwd == NULL) + return -1; + } else + passwd = NULL; + + SSL_CTX_set_default_passwd_cb(ssl_ctx, tls_passwd_cb); + SSL_CTX_set_default_passwd_cb_userdata(ssl_ctx, passwd); + + ok = 0; + while (private_key_blob) { + if (SSL_use_PrivateKey_ASN1(EVP_PKEY_RSA, conn->ssl, + (u8 *) private_key_blob, + private_key_blob_len) == 1) { + wpa_printf(MSG_DEBUG, "OpenSSL: SSL_use_PrivateKey_" + "ASN1(EVP_PKEY_RSA) --> OK"); + ok = 1; + break; + } else { + tls_show_errors(MSG_DEBUG, __func__, + "SSL_use_PrivateKey_ASN1(EVP_PKEY_RSA)" + " failed"); + } + + if (SSL_use_PrivateKey_ASN1(EVP_PKEY_DSA, conn->ssl, + (u8 *) private_key_blob, + private_key_blob_len) == 1) { + wpa_printf(MSG_DEBUG, "OpenSSL: SSL_use_PrivateKey_" + "ASN1(EVP_PKEY_DSA) --> OK"); + ok = 1; + break; + } else { + tls_show_errors(MSG_DEBUG, __func__, + "SSL_use_PrivateKey_ASN1(EVP_PKEY_DSA)" + " failed"); + } + + if (SSL_use_RSAPrivateKey_ASN1(conn->ssl, + (u8 *) private_key_blob, + private_key_blob_len) == 1) { + wpa_printf(MSG_DEBUG, "OpenSSL: " + "SSL_use_RSAPrivateKey_ASN1 --> OK"); + ok = 1; + break; + } else { + tls_show_errors(MSG_DEBUG, __func__, + "SSL_use_RSAPrivateKey_ASN1 failed"); + } + + if (tls_read_pkcs12_blob(ssl_ctx, conn->ssl, private_key_blob, + private_key_blob_len, passwd) == 0) { + wpa_printf(MSG_DEBUG, "OpenSSL: PKCS#12 as blob --> " + "OK"); + ok = 1; + break; + } + + break; + } + + while (!ok && private_key) { +#ifndef OPENSSL_NO_STDIO + if (SSL_use_PrivateKey_file(conn->ssl, private_key, + SSL_FILETYPE_ASN1) == 1) { + wpa_printf(MSG_DEBUG, "OpenSSL: " + "SSL_use_PrivateKey_File (DER) --> OK"); + ok = 1; + break; + } else { + tls_show_errors(MSG_DEBUG, __func__, + "SSL_use_PrivateKey_File (DER) " + "failed"); + } + + if (SSL_use_PrivateKey_file(conn->ssl, private_key, + SSL_FILETYPE_PEM) == 1) { + wpa_printf(MSG_DEBUG, "OpenSSL: " + "SSL_use_PrivateKey_File (PEM) --> OK"); + ok = 1; + break; + } else { + tls_show_errors(MSG_DEBUG, __func__, + "SSL_use_PrivateKey_File (PEM) " + "failed"); + } +#else /* OPENSSL_NO_STDIO */ + wpa_printf(MSG_DEBUG, "OpenSSL: %s - OPENSSL_NO_STDIO", + __func__); +#endif /* OPENSSL_NO_STDIO */ + + if (tls_read_pkcs12(ssl_ctx, conn->ssl, private_key, passwd) + == 0) { + wpa_printf(MSG_DEBUG, "OpenSSL: Reading PKCS#12 file " + "--> OK"); + ok = 1; + break; + } + + if (tls_cryptoapi_cert(conn->ssl, private_key) == 0) { + wpa_printf(MSG_DEBUG, "OpenSSL: Using CryptoAPI to " + "access certificate store --> OK"); + ok = 1; + break; + } + + break; + } + + if (!ok) { + wpa_printf(MSG_INFO, "OpenSSL: Failed to load private key"); + free(passwd); + ERR_clear_error(); + return -1; + } + ERR_clear_error(); + SSL_CTX_set_default_passwd_cb(ssl_ctx, NULL); + free(passwd); + + if (!SSL_check_private_key(conn->ssl)) { + tls_show_errors(MSG_INFO, __func__, "Private key failed " + "verification"); + return -1; + } + + wpa_printf(MSG_DEBUG, "SSL: Private key loaded successfully"); + return 0; +} + + +int tls_global_private_key(void *_ssl_ctx, const char *private_key, + const char *private_key_passwd) +{ + SSL_CTX *ssl_ctx = _ssl_ctx; + char *passwd; + + if (private_key == NULL) + return 0; + + if (private_key_passwd) { + passwd = strdup(private_key_passwd); + if (passwd == NULL) + return -1; + } else + passwd = NULL; + + SSL_CTX_set_default_passwd_cb(ssl_ctx, tls_passwd_cb); + SSL_CTX_set_default_passwd_cb_userdata(ssl_ctx, passwd); + if ( +#ifndef OPENSSL_NO_STDIO + SSL_CTX_use_PrivateKey_file(ssl_ctx, private_key, + SSL_FILETYPE_ASN1) != 1 && + SSL_CTX_use_PrivateKey_file(ssl_ctx, private_key, + SSL_FILETYPE_PEM) != 1 && +#endif /* OPENSSL_NO_STDIO */ + tls_read_pkcs12(ssl_ctx, NULL, private_key, passwd)) { + tls_show_errors(MSG_INFO, __func__, + "Failed to load private key"); + free(passwd); + ERR_clear_error(); + return -1; + } + free(passwd); + ERR_clear_error(); + SSL_CTX_set_default_passwd_cb(ssl_ctx, NULL); + + if (!SSL_CTX_check_private_key(ssl_ctx)) { + tls_show_errors(MSG_INFO, __func__, + "Private key failed verification"); + return -1; + } + + return 0; +} + + +static int tls_connection_dh(void *ssl_ctx, struct tls_connection *conn, + const char *dh_file) +{ +#ifdef OPENSSL_NO_DH + if (dh_file == NULL) + return 0; + wpa_printf(MSG_ERROR, "TLS: openssl does not include DH support, but " + "dh_file specified"); + return -1; +#else /* OPENSSL_NO_DH */ + DH *dh; + BIO *bio; + + /* TODO: add support for dh_blob */ + if (dh_file == NULL) + return 0; + if (conn == NULL) + return -1; + + bio = BIO_new_file(dh_file, "r"); + if (bio == NULL) { + wpa_printf(MSG_INFO, "TLS: Failed to open DH file '%s': %s", + dh_file, ERR_error_string(ERR_get_error(), NULL)); + return -1; + } + dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL); + BIO_free(bio); +#ifndef OPENSSL_NO_DSA + while (dh == NULL) { + DSA *dsa; + wpa_printf(MSG_DEBUG, "TLS: Failed to parse DH file '%s': %s -" + " trying to parse as DSA params", dh_file, + ERR_error_string(ERR_get_error(), NULL)); + bio = BIO_new_file(dh_file, "r"); + if (bio == NULL) + break; + dsa = PEM_read_bio_DSAparams(bio, NULL, NULL, NULL); + BIO_free(bio); + if (!dsa) { + wpa_printf(MSG_DEBUG, "TLS: Failed to parse DSA file " + "'%s': %s", dh_file, + ERR_error_string(ERR_get_error(), NULL)); + break; + } + + wpa_printf(MSG_DEBUG, "TLS: DH file in DSA param format"); + dh = DSA_dup_DH(dsa); + DSA_free(dsa); + if (dh == NULL) { + wpa_printf(MSG_INFO, "TLS: Failed to convert DSA " + "params into DH params"); + break; + } + break; + } +#endif /* !OPENSSL_NO_DSA */ + if (dh == NULL) { + wpa_printf(MSG_INFO, "TLS: Failed to read/parse DH/DSA file " + "'%s'", dh_file); + return -1; + } + + if (SSL_set_tmp_dh(conn->ssl, dh) != 1) { + wpa_printf(MSG_INFO, "TLS: Failed to set DH params from '%s': " + "%s", dh_file, + ERR_error_string(ERR_get_error(), NULL)); + DH_free(dh); + return -1; + } + DH_free(dh); + return 0; +#endif /* OPENSSL_NO_DH */ +} + + +int tls_connection_get_keys(void *ssl_ctx, struct tls_connection *conn, + struct tls_keys *keys) +{ + SSL *ssl; + + if (conn == NULL || keys == NULL) + return -1; + ssl = conn->ssl; + if (ssl == NULL || ssl->s3 == NULL || ssl->session == NULL) + return -1; + + memset(keys, 0, sizeof(*keys)); + keys->master_key = ssl->session->master_key; + keys->master_key_len = ssl->session->master_key_length; + keys->client_random = ssl->s3->client_random; + keys->client_random_len = SSL3_RANDOM_SIZE; + keys->server_random = ssl->s3->server_random; + keys->server_random_len = SSL3_RANDOM_SIZE; + + return 0; +} + + +u8 * tls_connection_handshake(void *ssl_ctx, struct tls_connection *conn, + const u8 *in_data, size_t in_len, + size_t *out_len) +{ + int res; + u8 *out_data; + + /* + * Give TLS handshake data from the server (if available) to OpenSSL + * for processing. + */ + if (in_data && + BIO_write(conn->ssl_in, in_data, in_len) < 0) { + tls_show_errors(MSG_INFO, __func__, + "Handshake failed - BIO_write"); + return NULL; + } + + /* Initiate TLS handshake or continue the existing handshake */ + res = SSL_connect(conn->ssl); + if (res != 1) { + int err = SSL_get_error(conn->ssl, res); + if (err == SSL_ERROR_WANT_READ) + wpa_printf(MSG_DEBUG, "SSL: SSL_connect - want " + "more data"); + else if (err == SSL_ERROR_WANT_WRITE) + wpa_printf(MSG_DEBUG, "SSL: SSL_connect - want to " + "write"); + else { + tls_show_errors(MSG_INFO, __func__, "SSL_connect"); + conn->failed++; + } + } + + /* Get the TLS handshake data to be sent to the server */ + res = BIO_ctrl_pending(conn->ssl_out); + wpa_printf(MSG_DEBUG, "SSL: %d bytes pending from ssl_out", res); + out_data = malloc(res == 0 ? 1 : res); + if (out_data == NULL) { + wpa_printf(MSG_DEBUG, "SSL: Failed to allocate memory for " + "handshake output (%d bytes)", res); + if (BIO_reset(conn->ssl_out) < 0) { + tls_show_errors(MSG_INFO, __func__, + "BIO_reset failed"); + } + *out_len = 0; + return NULL; + } + res = res == 0 ? 0 : BIO_read(conn->ssl_out, out_data, res); + if (res < 0) { + tls_show_errors(MSG_INFO, __func__, + "Handshake failed - BIO_read"); + if (BIO_reset(conn->ssl_out) < 0) { + tls_show_errors(MSG_INFO, __func__, + "BIO_reset failed"); + } + *out_len = 0; + return NULL; + } + *out_len = res; + return out_data; +} + + +u8 * tls_connection_server_handshake(void *ssl_ctx, + struct tls_connection *conn, + const u8 *in_data, size_t in_len, + size_t *out_len) +{ + int res; + u8 *out_data; + char buf[10]; + + if (in_data && + BIO_write(conn->ssl_in, in_data, in_len) < 0) { + tls_show_errors(MSG_INFO, __func__, + "Handshake failed - BIO_write"); + return NULL; + } + + res = SSL_read(conn->ssl, buf, sizeof(buf)); + if (res >= 0) { + wpa_printf(MSG_DEBUG, "SSL: Unexpected data from SSL_read " + "(res=%d)", res); + } + + res = BIO_ctrl_pending(conn->ssl_out); + wpa_printf(MSG_DEBUG, "SSL: %d bytes pending from ssl_out", res); + out_data = malloc(res == 0 ? 1 : res); + if (out_data == NULL) { + wpa_printf(MSG_DEBUG, "SSL: Failed to allocate memory for " + "handshake output (%d bytes)", res); + if (BIO_reset(conn->ssl_out) < 0) { + tls_show_errors(MSG_INFO, __func__, + "BIO_reset failed"); + } + *out_len = 0; + return NULL; + } + res = res == 0 ? 0 : BIO_read(conn->ssl_out, out_data, res); + if (res < 0) { + tls_show_errors(MSG_INFO, __func__, + "Handshake failed - BIO_read"); + if (BIO_reset(conn->ssl_out) < 0) { + tls_show_errors(MSG_INFO, __func__, + "BIO_reset failed"); + } + *out_len = 0; + return NULL; + } + *out_len = res; + return out_data; +} + + +int tls_connection_encrypt(void *ssl_ctx, struct tls_connection *conn, + const u8 *in_data, size_t in_len, + u8 *out_data, size_t out_len) +{ + int res; + + if (conn == NULL) + return -1; + + /* Give plaintext data for OpenSSL to encrypt into the TLS tunnel. */ + if ((res = BIO_reset(conn->ssl_in)) < 0 || + (res = BIO_reset(conn->ssl_out)) < 0) { + tls_show_errors(MSG_INFO, __func__, "BIO_reset failed"); + return res; + } + res = SSL_write(conn->ssl, in_data, in_len); + if (res < 0) { + tls_show_errors(MSG_INFO, __func__, + "Encryption failed - SSL_write"); + return res; + } + + /* Read encrypted data to be sent to the server */ + res = BIO_read(conn->ssl_out, out_data, out_len); + if (res < 0) { + tls_show_errors(MSG_INFO, __func__, + "Encryption failed - BIO_read"); + return res; + } + + return res; +} + + +int tls_connection_decrypt(void *ssl_ctx, struct tls_connection *conn, + const u8 *in_data, size_t in_len, + u8 *out_data, size_t out_len) +{ + int res; + + /* Give encrypted data from TLS tunnel for OpenSSL to decrypt. */ + res = BIO_write(conn->ssl_in, in_data, in_len); + if (res < 0) { + tls_show_errors(MSG_INFO, __func__, + "Decryption failed - BIO_write"); + return res; + } + if (BIO_reset(conn->ssl_out) < 0) { + tls_show_errors(MSG_INFO, __func__, "BIO_reset failed"); + return res; + } + + /* Read decrypted data for further processing */ + res = SSL_read(conn->ssl, out_data, out_len); + if (res < 0) { + tls_show_errors(MSG_INFO, __func__, + "Decryption failed - SSL_read"); + return res; + } + + return res; +} + + +int tls_connection_resumed(void *ssl_ctx, struct tls_connection *conn) +{ + return conn ? conn->ssl->hit : 0; +} + + +#ifdef EAP_FAST +/* Pre-shared secred requires a patch to openssl, so this function is + * commented out unless explicitly needed for EAP-FAST in order to be able to + * build this file with unmodified openssl. */ + +static int tls_sess_sec_cb(SSL *s, void *secret, int *secret_len, + STACK_OF(SSL_CIPHER) *peer_ciphers, + SSL_CIPHER **cipher, void *arg) +{ + struct tls_connection *conn = arg; + + if (conn == NULL || conn->pre_shared_secret == 0) + return 0; + + memcpy(secret, conn->pre_shared_secret, conn->pre_shared_secret_len); + *secret_len = conn->pre_shared_secret_len; + + return 1; +} + + +int tls_connection_set_master_key(void *ssl_ctx, struct tls_connection *conn, + const u8 *key, size_t key_len) +{ + if (conn == NULL || key_len > SSL_MAX_MASTER_KEY_LENGTH) + return -1; + + free(conn->pre_shared_secret); + conn->pre_shared_secret = NULL; + conn->pre_shared_secret_len = 0; + + if (key) { + conn->pre_shared_secret = malloc(key_len); + if (conn->pre_shared_secret) { + memcpy(conn->pre_shared_secret, key, key_len); + conn->pre_shared_secret_len = key_len; + } + if (SSL_set_session_secret_cb(conn->ssl, tls_sess_sec_cb, + conn) != 1) + return -1; + } else { + if (SSL_set_session_secret_cb(conn->ssl, NULL, NULL) != 1) + return -1; + } + + return 0; +} +#endif /* EAP_FAST */ + + +int tls_connection_set_anon_dh(void *ssl_ctx, struct tls_connection *conn) +{ + if (conn == NULL || conn->ssl == NULL) + return -1; + + if (SSL_set_cipher_list(conn->ssl, "ADH-AES128-SHA") != 1) { + tls_show_errors(MSG_INFO, __func__, + "Anon DH configuration failed"); + return -1; + } + + return 0; +} + + +int tls_get_cipher(void *ssl_ctx, struct tls_connection *conn, + char *buf, size_t buflen) +{ + const char *name; + if (conn == NULL || conn->ssl == NULL) + return -1; + + name = SSL_get_cipher(conn->ssl); + if (name == NULL) + return -1; + + snprintf(buf, buflen, "%s", name); + return 0; +} + + +int tls_connection_enable_workaround(void *ssl_ctx, + struct tls_connection *conn) +{ + SSL_set_options(conn->ssl, SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS); + + return 0; +} + + +#ifdef EAP_FAST +/* ClientHello TLS extensions require a patch to openssl, so this function is + * commented out unless explicitly needed for EAP-FAST in order to be able to + * build this file with unmodified openssl. */ +int tls_connection_client_hello_ext(void *ssl_ctx, struct tls_connection *conn, + int ext_type, const u8 *data, + size_t data_len) +{ + if (conn == NULL || conn->ssl == NULL) + return -1; + + if (SSL_set_hello_extension(conn->ssl, ext_type, (void *) data, + data_len) != 1) + return -1; + + return 0; +} +#endif /* EAP_FAST */ + + +int tls_connection_get_failed(void *ssl_ctx, struct tls_connection *conn) +{ + if (conn == NULL) + return -1; + return conn->failed; +} + + +int tls_connection_get_read_alerts(void *ssl_ctx, struct tls_connection *conn) +{ + if (conn == NULL) + return -1; + return conn->read_alerts; +} + + +int tls_connection_get_write_alerts(void *ssl_ctx, struct tls_connection *conn) +{ + if (conn == NULL) + return -1; + return conn->write_alerts; +} + + +int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, + const struct tls_connection_params *params) +{ + int ret; + unsigned long err; + + if (conn == NULL) + return -1; + + while ((err = ERR_get_error())) { + wpa_printf(MSG_INFO, "%s: Clearing pending SSL error: %s", + __func__, ERR_error_string(err, NULL)); + } + + if (tls_connection_set_subject_match(tls_ctx, conn, + params->subject_match, + params->altsubject_match)) + return -1; + if (tls_connection_ca_cert(tls_ctx, conn, params->ca_cert, + params->ca_cert_blob, + params->ca_cert_blob_len, + params->ca_path)) + return -1; + if (tls_connection_client_cert(tls_ctx, conn, params->client_cert, + params->client_cert_blob, + params->client_cert_blob_len)) + return -1; + + if (params->engine) { + wpa_printf(MSG_DEBUG, "SSL: Initializing TLS engine"); + ret = tls_engine_init(conn, params->engine_id, params->pin, + params->key_id); + if (ret) + return ret; + if (tls_connection_engine_private_key(tls_ctx, conn)) + return TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED; + } else if (tls_connection_private_key(tls_ctx, conn, + params->private_key, + params->private_key_passwd, + params->private_key_blob, + params->private_key_blob_len)) { + wpa_printf(MSG_INFO, "TLS: Failed to load private key '%s'", + params->private_key); + return -1; + } + + if (tls_connection_dh(tls_ctx, conn, params->dh_file)) { + wpa_printf(MSG_INFO, "TLS: Failed to load DH file '%s'", + params->dh_file); + return -1; + } + + tls_get_errors(tls_ctx); + + return 0; +} + + +int tls_connection_get_keyblock_size(void *tls_ctx, + struct tls_connection *conn) +{ + const EVP_CIPHER *c; + const EVP_MD *h; + + if (conn == NULL || conn->ssl == NULL || + conn->ssl->enc_read_ctx == NULL || + conn->ssl->enc_read_ctx->cipher == NULL || + conn->ssl->read_hash == NULL) + return -1; + + c = conn->ssl->enc_read_ctx->cipher; + h = conn->ssl->read_hash; + + return 2 * (EVP_CIPHER_key_length(c) + + EVP_MD_size(h) + + EVP_CIPHER_iv_length(c)); +} diff --git a/contrib/hostapd-0.4.9/version.h b/contrib/hostapd-0.4.9/version.h new file mode 100644 index 0000000000..819bfcfe78 --- /dev/null +++ b/contrib/hostapd-0.4.9/version.h @@ -0,0 +1,6 @@ +#ifndef VERSION_H +#define VERSION_H + +#define VERSION_STR "0.4.9" + +#endif /* VERSION_H */ diff --git a/contrib/hostapd-0.4.9/wired.conf b/contrib/hostapd-0.4.9/wired.conf new file mode 100644 index 0000000000..956f8c53c5 --- /dev/null +++ b/contrib/hostapd-0.4.9/wired.conf @@ -0,0 +1,40 @@ +##### hostapd configuration file ############################################## +# Empty lines and lines starting with # are ignored + +# Example configuration file for wired authenticator. See hostapd.conf for +# more details. + +interface=eth0 +driver=wired +logger_stdout=-1 +logger_stdout_level=1 +debug=2 +dump_file=/tmp/hostapd.dump + +ieee8021x=1 +eap_reauth_period=3600 + +use_pae_group_addr=1 + + +##### RADIUS configuration #################################################### +# for IEEE 802.1X with external Authentication Server, IEEE 802.11 +# authentication with external ACL for MAC addresses, and accounting + +# The own IP address of the access point (used as NAS-IP-Address) +own_ip_addr=127.0.0.1 + +# Optional NAS-Identifier string for RADIUS messages. When used, this should be +# a unique to the NAS within the scope of the RADIUS server. For example, a +# fully qualified domain name can be used here. +nas_identifier=ap.example.com + +# RADIUS authentication server +auth_server_addr=127.0.0.1 +auth_server_port=1812 +auth_server_shared_secret=radius + +# RADIUS accounting server +acct_server_addr=127.0.0.1 +acct_server_port=1813 +acct_server_shared_secret=radius diff --git a/contrib/hostapd-0.4.9/wpa.c b/contrib/hostapd-0.4.9/wpa.c new file mode 100644 index 0000000000..08f8f3aa88 --- /dev/null +++ b/contrib/hostapd-0.4.9/wpa.c @@ -0,0 +1,2897 @@ +/* + * Host AP (software wireless LAN access point) user space daemon for + * Host AP kernel driver / WPA Authenticator + * Copyright (c) 2004-2005, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "hostapd.h" +#include "eapol_sm.h" +#include "wpa.h" +#include "driver.h" +#include "sha1.h" +#include "md5.h" +#include "rc4.h" +#include "aes_wrap.h" +#include "ieee802_1x.h" +#include "ieee802_11.h" +#include "eloop.h" +#include "sta_info.h" +#include "l2_packet.h" +#include "accounting.h" +#include "hostap_common.h" + + +static void wpa_send_eapol_timeout(void *eloop_ctx, void *timeout_ctx); +static void wpa_sm_step(struct wpa_state_machine *sm); +static int wpa_verify_key_mic(struct wpa_ptk *PTK, u8 *data, size_t data_len); +static void wpa_sm_call_step(void *eloop_ctx, void *timeout_ctx); +static void wpa_group_sm_step(struct hostapd_data *hapd); +static void pmksa_cache_free(struct hostapd_data *hapd); +static struct rsn_pmksa_cache * pmksa_cache_get(struct hostapd_data *hapd, + u8 *spa, u8 *pmkid); + + +/* Default timeouts are 100 ms, but this seems to be a bit too fast for most + * WPA Supplicants, so use a bit longer timeout. */ +static const u32 dot11RSNAConfigGroupUpdateTimeOut = 1000; /* ms */ +static const u32 dot11RSNAConfigGroupUpdateCount = 3; +static const u32 dot11RSNAConfigPairwiseUpdateTimeOut = 1000; /* ms */ +static const u32 dot11RSNAConfigPairwiseUpdateCount = 3; + +/* TODO: make these configurable */ +static const int dot11RSNAConfigPMKLifetime = 43200; +static const int dot11RSNAConfigPMKReauthThreshold = 70; +static const int dot11RSNAConfigSATimeout = 60; +static const int pmksa_cache_max_entries = 1024; + + +static const int WPA_SELECTOR_LEN = 4; +static const u8 WPA_OUI_TYPE[] = { 0x00, 0x50, 0xf2, 1 }; +static const u16 WPA_VERSION = 1; +static const u8 WPA_AUTH_KEY_MGMT_UNSPEC_802_1X[] = { 0x00, 0x50, 0xf2, 1 }; +static const u8 WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X[] = { 0x00, 0x50, 0xf2, 2 }; +static const u8 WPA_CIPHER_SUITE_NONE[] = { 0x00, 0x50, 0xf2, 0 }; +static const u8 WPA_CIPHER_SUITE_WEP40[] = { 0x00, 0x50, 0xf2, 1 }; +static const u8 WPA_CIPHER_SUITE_TKIP[] = { 0x00, 0x50, 0xf2, 2 }; +static const u8 WPA_CIPHER_SUITE_WRAP[] = { 0x00, 0x50, 0xf2, 3 }; +static const u8 WPA_CIPHER_SUITE_CCMP[] = { 0x00, 0x50, 0xf2, 4 }; +static const u8 WPA_CIPHER_SUITE_WEP104[] = { 0x00, 0x50, 0xf2, 5 }; + +static const int RSN_SELECTOR_LEN = 4; +static const u16 RSN_VERSION = 1; +static const u8 RSN_AUTH_KEY_MGMT_UNSPEC_802_1X[] = { 0x00, 0x0f, 0xac, 1 }; +static const u8 RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X[] = { 0x00, 0x0f, 0xac, 2 }; +static const u8 RSN_CIPHER_SUITE_NONE[] = { 0x00, 0x0f, 0xac, 0 }; +static const u8 RSN_CIPHER_SUITE_WEP40[] = { 0x00, 0x0f, 0xac, 1 }; +static const u8 RSN_CIPHER_SUITE_TKIP[] = { 0x00, 0x0f, 0xac, 2 }; +static const u8 RSN_CIPHER_SUITE_WRAP[] = { 0x00, 0x0f, 0xac, 3 }; +static const u8 RSN_CIPHER_SUITE_CCMP[] = { 0x00, 0x0f, 0xac, 4 }; +static const u8 RSN_CIPHER_SUITE_WEP104[] = { 0x00, 0x0f, 0xac, 5 }; + +/* EAPOL-Key Key Data Encapsulation + * GroupKey and STAKey require encryption, otherwise, encryption is optional. + */ +static const u8 RSN_KEY_DATA_GROUPKEY[] = { 0x00, 0x0f, 0xac, 1 }; +static const u8 RSN_KEY_DATA_STAKEY[] = { 0x00, 0x0f, 0xac, 2 }; +static const u8 RSN_KEY_DATA_MAC_ADDR[] = { 0x00, 0x0f, 0xac, 3 }; +static const u8 RSN_KEY_DATA_PMKID[] = { 0x00, 0x0f, 0xac, 4 }; + +/* WPA IE version 1 + * 00-50-f2:1 (OUI:OUI type) + * 0x01 0x00 (version; little endian) + * (all following fields are optional:) + * Group Suite Selector (4 octets) (default: TKIP) + * Pairwise Suite Count (2 octets, little endian) (default: 1) + * Pairwise Suite List (4 * n octets) (default: TKIP) + * Authenticated Key Management Suite Count (2 octets, little endian) + * (default: 1) + * Authenticated Key Management Suite List (4 * n octets) + * (default: unspec 802.1X) + * WPA Capabilities (2 octets, little endian) (default: 0) + */ + +struct wpa_ie_hdr { + u8 elem_id; + u8 len; + u8 oui[3]; + u8 oui_type; + u16 version; +} __attribute__ ((packed)); + + +/* RSN IE version 1 + * 0x01 0x00 (version; little endian) + * (all following fields are optional:) + * Group Suite Selector (4 octets) (default: CCMP) + * Pairwise Suite Count (2 octets, little endian) (default: 1) + * Pairwise Suite List (4 * n octets) (default: CCMP) + * Authenticated Key Management Suite Count (2 octets, little endian) + * (default: 1) + * Authenticated Key Management Suite List (4 * n octets) + * (default: unspec 802.1X) + * RSN Capabilities (2 octets, little endian) (default: 0) + * PMKID Count (2 octets) (default: 0) + * PMKID List (16 * n octets) + */ + +struct rsn_ie_hdr { + u8 elem_id; /* WLAN_EID_RSN */ + u8 len; + u16 version; +} __attribute__ ((packed)); + + +static int wpa_write_wpa_ie(struct hostapd_data *hapd, u8 *buf, size_t len) +{ + struct wpa_ie_hdr *hdr; + int num_suites; + u8 *pos, *count; + + hdr = (struct wpa_ie_hdr *) buf; + hdr->elem_id = WLAN_EID_GENERIC; + memcpy(&hdr->oui, WPA_OUI_TYPE, WPA_SELECTOR_LEN); + hdr->version = host_to_le16(WPA_VERSION); + pos = (u8 *) (hdr + 1); + + if (hapd->conf->wpa_group == WPA_CIPHER_CCMP) { + memcpy(pos, WPA_CIPHER_SUITE_CCMP, WPA_SELECTOR_LEN); + } else if (hapd->conf->wpa_group == WPA_CIPHER_TKIP) { + memcpy(pos, WPA_CIPHER_SUITE_TKIP, WPA_SELECTOR_LEN); + } else if (hapd->conf->wpa_group == WPA_CIPHER_WEP104) { + memcpy(pos, WPA_CIPHER_SUITE_WEP104, WPA_SELECTOR_LEN); + } else if (hapd->conf->wpa_group == WPA_CIPHER_WEP40) { + memcpy(pos, WPA_CIPHER_SUITE_WEP40, WPA_SELECTOR_LEN); + } else { + printf("Invalid group cipher (%d).\n", hapd->conf->wpa_group); + return -1; + } + pos += WPA_SELECTOR_LEN; + + num_suites = 0; + count = pos; + pos += 2; + + if (hapd->conf->wpa_pairwise & WPA_CIPHER_CCMP) { + memcpy(pos, WPA_CIPHER_SUITE_CCMP, WPA_SELECTOR_LEN); + pos += WPA_SELECTOR_LEN; + num_suites++; + } + if (hapd->conf->wpa_pairwise & WPA_CIPHER_TKIP) { + memcpy(pos, WPA_CIPHER_SUITE_TKIP, WPA_SELECTOR_LEN); + pos += WPA_SELECTOR_LEN; + num_suites++; + } + if (hapd->conf->wpa_pairwise & WPA_CIPHER_NONE) { + memcpy(pos, WPA_CIPHER_SUITE_NONE, WPA_SELECTOR_LEN); + pos += WPA_SELECTOR_LEN; + num_suites++; + } + + if (num_suites == 0) { + printf("Invalid pairwise cipher (%d).\n", + hapd->conf->wpa_pairwise); + return -1; + } + *count++ = num_suites & 0xff; + *count = (num_suites >> 8) & 0xff; + + num_suites = 0; + count = pos; + pos += 2; + + if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X) { + memcpy(pos, WPA_AUTH_KEY_MGMT_UNSPEC_802_1X, WPA_SELECTOR_LEN); + pos += WPA_SELECTOR_LEN; + num_suites++; + } + if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK) { + memcpy(pos, WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X, + WPA_SELECTOR_LEN); + pos += WPA_SELECTOR_LEN; + num_suites++; + } + + if (num_suites == 0) { + printf("Invalid key management type (%d).\n", + hapd->conf->wpa_key_mgmt); + return -1; + } + *count++ = num_suites & 0xff; + *count = (num_suites >> 8) & 0xff; + + /* WPA Capabilities; use defaults, so no need to include it */ + + hdr->len = (pos - buf) - 2; + + return pos - buf; +} + + +static int wpa_write_rsn_ie(struct hostapd_data *hapd, u8 *buf, size_t len) +{ + struct rsn_ie_hdr *hdr; + int num_suites; + u8 *pos, *count; + + hdr = (struct rsn_ie_hdr *) buf; + hdr->elem_id = WLAN_EID_RSN; + pos = (u8 *) &hdr->version; + *pos++ = RSN_VERSION & 0xff; + *pos++ = RSN_VERSION >> 8; + pos = (u8 *) (hdr + 1); + + if (hapd->conf->wpa_group == WPA_CIPHER_CCMP) { + memcpy(pos, RSN_CIPHER_SUITE_CCMP, RSN_SELECTOR_LEN); + } else if (hapd->conf->wpa_group == WPA_CIPHER_TKIP) { + memcpy(pos, RSN_CIPHER_SUITE_TKIP, RSN_SELECTOR_LEN); + } else if (hapd->conf->wpa_group == WPA_CIPHER_WEP104) { + memcpy(pos, RSN_CIPHER_SUITE_WEP104, RSN_SELECTOR_LEN); + } else if (hapd->conf->wpa_group == WPA_CIPHER_WEP40) { + memcpy(pos, RSN_CIPHER_SUITE_WEP40, RSN_SELECTOR_LEN); + } else { + printf("Invalid group cipher (%d).\n", hapd->conf->wpa_group); + return -1; + } + pos += RSN_SELECTOR_LEN; + + num_suites = 0; + count = pos; + pos += 2; + + if (hapd->conf->wpa_pairwise & WPA_CIPHER_CCMP) { + memcpy(pos, RSN_CIPHER_SUITE_CCMP, RSN_SELECTOR_LEN); + pos += RSN_SELECTOR_LEN; + num_suites++; + } + if (hapd->conf->wpa_pairwise & WPA_CIPHER_TKIP) { + memcpy(pos, RSN_CIPHER_SUITE_TKIP, RSN_SELECTOR_LEN); + pos += RSN_SELECTOR_LEN; + num_suites++; + } + if (hapd->conf->wpa_pairwise & WPA_CIPHER_NONE) { + memcpy(pos, RSN_CIPHER_SUITE_NONE, RSN_SELECTOR_LEN); + pos += RSN_SELECTOR_LEN; + num_suites++; + } + + if (num_suites == 0) { + printf("Invalid pairwise cipher (%d).\n", + hapd->conf->wpa_pairwise); + return -1; + } + *count++ = num_suites & 0xff; + *count = (num_suites >> 8) & 0xff; + + num_suites = 0; + count = pos; + pos += 2; + + if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X) { + memcpy(pos, RSN_AUTH_KEY_MGMT_UNSPEC_802_1X, RSN_SELECTOR_LEN); + pos += RSN_SELECTOR_LEN; + num_suites++; + } + if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK) { + memcpy(pos, RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X, + RSN_SELECTOR_LEN); + pos += RSN_SELECTOR_LEN; + num_suites++; + } + + if (num_suites == 0) { + printf("Invalid key management type (%d).\n", + hapd->conf->wpa_key_mgmt); + return -1; + } + *count++ = num_suites & 0xff; + *count = (num_suites >> 8) & 0xff; + + /* RSN Capabilities */ + *pos++ = hapd->conf->rsn_preauth ? BIT(0) : 0; + *pos++ = 0; + + hdr->len = (pos - buf) - 2; + + return pos - buf; +} + + +static int wpa_gen_wpa_ie(struct hostapd_data *hapd) +{ + u8 *pos, buf[100]; + int res; + + pos = buf; + + if (hapd->conf->wpa & HOSTAPD_WPA_VERSION_WPA2) { + res = wpa_write_rsn_ie(hapd, pos, buf + sizeof(buf) - pos); + if (res < 0) + return res; + pos += res; + } + if (hapd->conf->wpa & HOSTAPD_WPA_VERSION_WPA) { + res = wpa_write_wpa_ie(hapd, pos, buf + sizeof(buf) - pos); + if (res < 0) + return res; + pos += res; + } + + free(hapd->wpa_ie); + hapd->wpa_ie = malloc(pos - buf); + if (hapd->wpa_ie == NULL) + return -1; + memcpy(hapd->wpa_ie, buf, pos - buf); + hapd->wpa_ie_len = pos - buf; + + return 0; +} + + +static void wpa_sta_disconnect(struct hostapd_data *hapd, struct sta_info *sta) +{ + hostapd_sta_deauth(hapd, sta->addr, WLAN_REASON_PREV_AUTH_NOT_VALID); + sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC | WLAN_STA_AUTHORIZED); + eloop_cancel_timeout(ap_handle_timer, hapd, sta); + eloop_register_timeout(0, 0, ap_handle_timer, hapd, sta); + sta->timeout_next = STA_REMOVE; +} + + +static void wpa_rekey_gmk(void *eloop_ctx, void *timeout_ctx) +{ + struct hostapd_data *hapd = eloop_ctx; + + if (hapd->wpa_auth) { + if (hostapd_get_rand(hapd->wpa_auth->GMK, WPA_GMK_LEN)) { + printf("Failed to get random data for WPA " + "initialization.\n"); + } else { + hostapd_logger(hapd, NULL, HOSTAPD_MODULE_WPA, + HOSTAPD_LEVEL_DEBUG, + "GMK rekeyd"); + } + } + + if (hapd->conf->wpa_gmk_rekey) { + eloop_register_timeout(hapd->conf->wpa_gmk_rekey, 0, + wpa_rekey_gmk, hapd, NULL); + } +} + + +static void wpa_rekey_gtk(void *eloop_ctx, void *timeout_ctx) +{ + struct hostapd_data *hapd = eloop_ctx; + + if (hapd->wpa_auth) { + hostapd_logger(hapd, NULL, HOSTAPD_MODULE_WPA, + HOSTAPD_LEVEL_DEBUG, "rekeying GTK"); + hapd->wpa_auth->GTKReKey = TRUE; + do { + hapd->wpa_auth->changed = FALSE; + wpa_group_sm_step(hapd); + } while (hapd->wpa_auth->changed); + } + if (hapd->conf->wpa_group_rekey) { + eloop_register_timeout(hapd->conf->wpa_group_rekey, 0, + wpa_rekey_gtk, hapd, NULL); + } +} + + +#ifdef CONFIG_RSN_PREAUTH + +static void rsn_preauth_receive(void *ctx, const u8 *src_addr, + const u8 *buf, size_t len) +{ + struct rsn_preauth_interface *piface = ctx; + struct hostapd_data *hapd = piface->hapd; + struct ieee802_1x_hdr *hdr; + struct sta_info *sta; + struct l2_ethhdr *ethhdr; + + HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "RSN: receive pre-auth packet " + "from interface '%s'\n", piface->ifname); + if (len < sizeof(*ethhdr) + sizeof(*hdr)) { + HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "RSN: too short pre-auth " + "packet (len=%lu)\n", (unsigned long) len); + return; + } + + ethhdr = (struct l2_ethhdr *) buf; + hdr = (struct ieee802_1x_hdr *) (ethhdr + 1); + + if (memcmp(ethhdr->h_dest, hapd->own_addr, ETH_ALEN) != 0) { + HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "RSN: pre-auth for " + "foreign address " MACSTR "\n", + MAC2STR(ethhdr->h_dest)); + return; + } + + sta = ap_get_sta(hapd, ethhdr->h_source); + if (sta && (sta->flags & WLAN_STA_ASSOC)) { + HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "RSN: pre-auth for " + "already association STA " MACSTR "\n", + MAC2STR(sta->addr)); + return; + } + if (!sta && hdr->type == IEEE802_1X_TYPE_EAPOL_START) { + sta = (struct sta_info *) malloc(sizeof(struct sta_info)); + if (sta == NULL) + return; + memset(sta, 0, sizeof(*sta)); + memcpy(sta->addr, ethhdr->h_source, ETH_ALEN); + sta->flags = WLAN_STA_PREAUTH; + sta->next = hapd->sta_list; + sta->wpa = WPA_VERSION_WPA2; + hapd->sta_list = sta; + hapd->num_sta++; + ap_sta_hash_add(hapd, sta); + + ieee802_1x_new_station(hapd, sta); + if (sta->eapol_sm == NULL) { + ap_free_sta(hapd, sta); + sta = NULL; + } else { + sta->eapol_sm->radius_identifier = -1; + sta->eapol_sm->portValid = TRUE; + sta->eapol_sm->flags |= EAPOL_SM_PREAUTH; + } + } + if (sta == NULL) + return; + sta->preauth_iface = piface; + ieee802_1x_receive(hapd, ethhdr->h_source, (u8 *) (ethhdr + 1), + len - sizeof(*ethhdr)); +} + + +static int rsn_preauth_iface_add(struct hostapd_data *hapd, const char *ifname) +{ + struct rsn_preauth_interface *piface; + + HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "RSN pre-auth interface '%s'\n", + ifname); + + piface = malloc(sizeof(*piface)); + if (piface == NULL) + return -1; + memset(piface, 0, sizeof(*piface)); + piface->hapd = hapd; + + piface->ifname = strdup(ifname); + if (piface->ifname == NULL) { + goto fail1; + } + + piface->l2 = l2_packet_init(piface->ifname, NULL, ETH_P_PREAUTH, + rsn_preauth_receive, piface, 1); + if (piface->l2 == NULL) { + printf("Failed to open register layer 2 access to " + "ETH_P_PREAUTH\n"); + goto fail2; + } + + piface->next = hapd->preauth_iface; + hapd->preauth_iface = piface; + return 0; + +fail2: + free(piface->ifname); +fail1: + free(piface); + return -1; +} + + +static void rsn_preauth_iface_deinit(struct hostapd_data *hapd) +{ + struct rsn_preauth_interface *piface, *prev; + + piface = hapd->preauth_iface; + hapd->preauth_iface = NULL; + while (piface) { + prev = piface; + piface = piface->next; + l2_packet_deinit(prev->l2); + free(prev->ifname); + free(prev); + } +} + + +static int rsn_preauth_iface_init(struct hostapd_data *hapd) +{ + char *tmp, *start, *end; + + if (hapd->conf->rsn_preauth_interfaces == NULL) + return 0; + + tmp = strdup(hapd->conf->rsn_preauth_interfaces); + if (tmp == NULL) + return -1; + start = tmp; + for (;;) { + while (*start == ' ') + start++; + if (*start == '\0') + break; + end = strchr(start, ' '); + if (end) + *end = '\0'; + + if (rsn_preauth_iface_add(hapd, start)) { + rsn_preauth_iface_deinit(hapd); + return -1; + } + + if (end) + start = end + 1; + else + break; + } + free(tmp); + return 0; +} + + +static void rsn_preauth_finished_cb(void *eloop_ctx, void *timeout_ctx) +{ + struct hostapd_data *hapd = eloop_ctx; + struct sta_info *sta = timeout_ctx; + wpa_printf(MSG_DEBUG, "RSN: Removing pre-authentication STA entry for " + MACSTR, MAC2STR(sta->addr)); + ap_free_sta(hapd, sta); +} + + +void rsn_preauth_finished(struct hostapd_data *hapd, struct sta_info *sta, + int success) +{ + u8 *key; + size_t len; + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, + HOSTAPD_LEVEL_INFO, "pre-authentication %s", + success ? "succeeded" : "failed"); + + key = ieee802_1x_get_key_crypt(sta->eapol_sm, &len); + if (success && key) { + pmksa_cache_add(hapd, sta, key, dot11RSNAConfigPMKLifetime); + } + + /* + * Finish STA entry removal from timeout in order to avoid freeing + * STA data before the caller has finished processing. + */ + eloop_register_timeout(0, 0, rsn_preauth_finished_cb, hapd, sta); +} + + +void rsn_preauth_send(struct hostapd_data *hapd, struct sta_info *sta, + u8 *buf, size_t len) +{ + struct rsn_preauth_interface *piface; + struct l2_ethhdr *ethhdr; + + piface = hapd->preauth_iface; + while (piface) { + if (piface == sta->preauth_iface) + break; + piface = piface->next; + } + + if (piface == NULL) { + HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "RSN: Could not find " + "pre-authentication interface for " MACSTR "\n", + MAC2STR(sta->addr)); + return; + } + + ethhdr = malloc(sizeof(*ethhdr) + len); + if (ethhdr == NULL) + return; + + memcpy(ethhdr->h_dest, sta->addr, ETH_ALEN); + memcpy(ethhdr->h_source, hapd->own_addr, ETH_ALEN); + ethhdr->h_proto = htons(ETH_P_PREAUTH); + memcpy(ethhdr + 1, buf, len); + + if (l2_packet_send(piface->l2, sta->addr, ETH_P_PREAUTH, (u8 *) ethhdr, + sizeof(*ethhdr) + len) < 0) { + printf("Failed to send preauth packet using l2_packet_send\n"); + } + free(ethhdr); +} + +#else /* CONFIG_RSN_PREAUTH */ + +static inline int rsn_preauth_iface_init(struct hostapd_data *hapd) +{ + return 0; +} + +static inline void rsn_preauth_iface_deinit(struct hostapd_data *hapd) +{ +} + +static void rsn_preauth_finished_cb(void *eloop_ctx, void *timeout_ctx) +{ +} + +void rsn_preauth_finished(struct hostapd_data *hapd, struct sta_info *sta, + int success) +{ +} + +void rsn_preauth_send(struct hostapd_data *hapd, struct sta_info *sta, + u8 *buf, size_t len) +{ +} + +#endif /* CONFIG_RSN_PREAUTH */ + + +int wpa_init(struct hostapd_data *hapd) +{ + u8 rkey[32]; + u8 buf[ETH_ALEN + 8]; + + if (rsn_preauth_iface_init(hapd)) + return -1; + + if (hostapd_set_privacy(hapd, 1)) { + printf("Could not set PrivacyInvoked for interface %s\n", + hapd->conf->iface); + return -1; + } + + if (wpa_gen_wpa_ie(hapd)) { + printf("Could not generate WPA IE.\n"); + return -1; + } + + if (hostapd_set_generic_elem(hapd, hapd->wpa_ie, hapd->wpa_ie_len)) { + printf("Failed to configure WPA IE for the kernel driver.\n"); + return -1; + } + + hapd->wpa_auth = malloc(sizeof(struct wpa_authenticator)); + if (hapd->wpa_auth == NULL) + return -1; + memset(hapd->wpa_auth, 0, sizeof(struct wpa_authenticator)); + hapd->wpa_auth->GTKAuthenticator = TRUE; + switch (hapd->conf->wpa_group) { + case WPA_CIPHER_CCMP: + hapd->wpa_auth->GTK_len = 16; + break; + case WPA_CIPHER_TKIP: + hapd->wpa_auth->GTK_len = 32; + break; + case WPA_CIPHER_WEP104: + hapd->wpa_auth->GTK_len = 13; + break; + case WPA_CIPHER_WEP40: + hapd->wpa_auth->GTK_len = 5; + break; + } + + /* Counter = PRF-256(Random number, "Init Counter", + * Local MAC Address || Time) + */ + memcpy(buf, hapd->own_addr, ETH_ALEN); + hostapd_get_ntp_timestamp(buf + ETH_ALEN); + if (hostapd_get_rand(rkey, sizeof(rkey)) || + hostapd_get_rand(hapd->wpa_auth->GMK, WPA_GMK_LEN)) { + printf("Failed to get random data for WPA initialization.\n"); + free(hapd->wpa_auth); + hapd->wpa_auth = NULL; + return -1; + } + + sha1_prf(rkey, sizeof(rkey), "Init Counter", buf, sizeof(buf), + hapd->wpa_auth->Counter, WPA_NONCE_LEN); + + if (hapd->conf->wpa_gmk_rekey) { + eloop_register_timeout(hapd->conf->wpa_gmk_rekey, 0, + wpa_rekey_gmk, hapd, NULL); + } + + if (hapd->conf->wpa_group_rekey) { + eloop_register_timeout(hapd->conf->wpa_group_rekey, 0, + wpa_rekey_gtk, hapd, NULL); + } + + hapd->wpa_auth->GInit = TRUE; + wpa_group_sm_step(hapd); + hapd->wpa_auth->GInit = FALSE; + wpa_group_sm_step(hapd); + + return 0; +} + + +void wpa_deinit(struct hostapd_data *hapd) +{ + rsn_preauth_iface_deinit(hapd); + + eloop_cancel_timeout(wpa_rekey_gmk, hapd, NULL); + eloop_cancel_timeout(wpa_rekey_gtk, hapd, NULL); + + if (hostapd_set_privacy(hapd, 0)) { + printf("Could not disable PrivacyInvoked for interface %s\n", + hapd->conf->iface); + } + + if (hostapd_set_generic_elem(hapd, (u8 *) "", 0)) { + printf("Could not remove generic information element from " + "interface %s\n", hapd->conf->iface); + } + + free(hapd->wpa_ie); + hapd->wpa_ie = NULL; + free(hapd->wpa_auth); + hapd->wpa_auth = NULL; + + pmksa_cache_free(hapd); +} + + +static int wpa_selector_to_bitfield(u8 *s) +{ + if (memcmp(s, WPA_CIPHER_SUITE_NONE, WPA_SELECTOR_LEN) == 0) + return WPA_CIPHER_NONE; + if (memcmp(s, WPA_CIPHER_SUITE_WEP40, WPA_SELECTOR_LEN) == 0) + return WPA_CIPHER_WEP40; + if (memcmp(s, WPA_CIPHER_SUITE_TKIP, WPA_SELECTOR_LEN) == 0) + return WPA_CIPHER_TKIP; + if (memcmp(s, WPA_CIPHER_SUITE_CCMP, WPA_SELECTOR_LEN) == 0) + return WPA_CIPHER_CCMP; + if (memcmp(s, WPA_CIPHER_SUITE_WEP104, WPA_SELECTOR_LEN) == 0) + return WPA_CIPHER_WEP104; + return 0; +} + + +static int wpa_key_mgmt_to_bitfield(u8 *s) +{ + if (memcmp(s, WPA_AUTH_KEY_MGMT_UNSPEC_802_1X, WPA_SELECTOR_LEN) == 0) + return WPA_KEY_MGMT_IEEE8021X; + if (memcmp(s, WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X, WPA_SELECTOR_LEN) == + 0) + return WPA_KEY_MGMT_PSK; + return 0; +} + + +static int rsn_selector_to_bitfield(u8 *s) +{ + if (memcmp(s, RSN_CIPHER_SUITE_NONE, RSN_SELECTOR_LEN) == 0) + return WPA_CIPHER_NONE; + if (memcmp(s, RSN_CIPHER_SUITE_WEP40, RSN_SELECTOR_LEN) == 0) + return WPA_CIPHER_WEP40; + if (memcmp(s, RSN_CIPHER_SUITE_TKIP, RSN_SELECTOR_LEN) == 0) + return WPA_CIPHER_TKIP; + if (memcmp(s, RSN_CIPHER_SUITE_CCMP, RSN_SELECTOR_LEN) == 0) + return WPA_CIPHER_CCMP; + if (memcmp(s, RSN_CIPHER_SUITE_WEP104, RSN_SELECTOR_LEN) == 0) + return WPA_CIPHER_WEP104; + return 0; +} + + +static int rsn_key_mgmt_to_bitfield(u8 *s) +{ + if (memcmp(s, RSN_AUTH_KEY_MGMT_UNSPEC_802_1X, RSN_SELECTOR_LEN) == 0) + return WPA_KEY_MGMT_IEEE8021X; + if (memcmp(s, RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X, RSN_SELECTOR_LEN) == + 0) + return WPA_KEY_MGMT_PSK; + return 0; +} + + +static void rsn_pmkid(const u8 *pmk, const u8 *aa, const u8 *spa, u8 *pmkid) +{ + char *title = "PMK Name"; + const u8 *addr[3]; + const size_t len[3] = { 8, ETH_ALEN, ETH_ALEN }; + unsigned char hash[SHA1_MAC_LEN]; + + addr[0] = (u8 *) title; + addr[1] = aa; + addr[2] = spa; + + hmac_sha1_vector(pmk, PMK_LEN, 3, addr, len, hash); + memcpy(pmkid, hash, PMKID_LEN); +} + + +static void pmksa_cache_set_expiration(struct hostapd_data *hapd); + + +static void _pmksa_cache_free_entry(struct rsn_pmksa_cache *entry) +{ + if (entry == NULL) + return; + free(entry->identity); + ieee802_1x_free_radius_class(&entry->radius_class); + free(entry); +} + + +static void pmksa_cache_free_entry(struct hostapd_data *hapd, + struct rsn_pmksa_cache *entry) +{ + struct sta_info *sta; + struct rsn_pmksa_cache *pos, *prev; + hapd->pmksa_count--; + for (sta = hapd->sta_list; sta != NULL; sta = sta->next) { + if (sta->pmksa == entry) + sta->pmksa = NULL; + } + pos = hapd->pmkid[PMKID_HASH(entry->pmkid)]; + prev = NULL; + while (pos) { + if (pos == entry) { + if (prev != NULL) { + prev->hnext = pos->hnext; + } else { + hapd->pmkid[PMKID_HASH(entry->pmkid)] = + pos->hnext; + } + break; + } + prev = pos; + pos = pos->hnext; + } + + pos = hapd->pmksa; + prev = NULL; + while (pos) { + if (pos == entry) { + if (prev != NULL) + prev->next = pos->next; + else + hapd->pmksa = pos->next; + break; + } + prev = pos; + pos = pos->next; + } + _pmksa_cache_free_entry(entry); +} + + +static void pmksa_cache_expire(void *eloop_ctx, void *timeout_ctx) +{ + struct hostapd_data *hapd = eloop_ctx; + time_t now; + + time(&now); + while (hapd->pmksa && hapd->pmksa->expiration <= now) { + struct rsn_pmksa_cache *entry = hapd->pmksa; + hapd->pmksa = entry->next; + HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, + "RSN: expired PMKSA cache entry for " + MACSTR, MAC2STR(entry->spa)); + pmksa_cache_free_entry(hapd, entry); + } + + pmksa_cache_set_expiration(hapd); +} + + +static void pmksa_cache_set_expiration(struct hostapd_data *hapd) +{ + int sec; + eloop_cancel_timeout(pmksa_cache_expire, hapd, NULL); + if (hapd->pmksa == NULL) + return; + sec = hapd->pmksa->expiration - time(NULL); + if (sec < 0) + sec = 0; + eloop_register_timeout(sec + 1, 0, pmksa_cache_expire, hapd, NULL); +} + + +static void pmksa_cache_from_eapol_data(struct rsn_pmksa_cache *entry, + struct eapol_state_machine *eapol) +{ + if (eapol == NULL) + return; + + if (eapol->identity) { + entry->identity = malloc(eapol->identity_len); + if (entry->identity) { + entry->identity_len = eapol->identity_len; + memcpy(entry->identity, eapol->identity, + eapol->identity_len); + } + } + + ieee802_1x_copy_radius_class(&entry->radius_class, + &eapol->radius_class); +} + + +static void pmksa_cache_to_eapol_data(struct rsn_pmksa_cache *entry, + struct eapol_state_machine *eapol) +{ + if (entry == NULL || eapol == NULL) + return; + + if (entry->identity) { + free(eapol->identity); + eapol->identity = malloc(entry->identity_len); + if (eapol->identity) { + eapol->identity_len = entry->identity_len; + memcpy(eapol->identity, entry->identity, + entry->identity_len); + } + wpa_hexdump_ascii(MSG_DEBUG, "STA identity from PMKSA", + eapol->identity, eapol->identity_len); + } + + ieee802_1x_free_radius_class(&eapol->radius_class); + ieee802_1x_copy_radius_class(&eapol->radius_class, + &entry->radius_class); + if (eapol->radius_class.attr) { + wpa_printf(MSG_DEBUG, "Copied %lu Class attribute(s) from " + "PMKSA", (unsigned long) eapol->radius_class.count); + } +} + + +void pmksa_cache_add(struct hostapd_data *hapd, struct sta_info *sta, u8 *pmk, + int session_timeout) +{ + struct rsn_pmksa_cache *entry, *pos, *prev; + + if (sta->wpa != WPA_VERSION_WPA2) + return; + + entry = malloc(sizeof(*entry)); + if (entry == NULL) + return; + memset(entry, 0, sizeof(*entry)); + memcpy(entry->pmk, pmk, PMK_LEN); + rsn_pmkid(pmk, hapd->own_addr, sta->addr, entry->pmkid); + time(&entry->expiration); + if (session_timeout > 0) + entry->expiration += session_timeout; + else + entry->expiration += dot11RSNAConfigPMKLifetime; + entry->akmp = WPA_KEY_MGMT_IEEE8021X; + memcpy(entry->spa, sta->addr, ETH_ALEN); + pmksa_cache_from_eapol_data(entry, sta->eapol_sm); + + /* Replace an old entry for the same STA (if found) with the new entry + */ + pos = pmksa_cache_get(hapd, sta->addr, NULL); + if (pos) + pmksa_cache_free_entry(hapd, pos); + + if (hapd->pmksa_count >= pmksa_cache_max_entries && hapd->pmksa) { + /* Remove the oldest entry to make room for the new entry */ + HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, + "RSN: removed the oldest PMKSA cache entry (for " + MACSTR ") to make room for new one", + MAC2STR(hapd->pmksa->spa)); + pmksa_cache_free_entry(hapd, hapd->pmksa); + } + + /* Add the new entry; order by expiration time */ + pos = hapd->pmksa; + prev = NULL; + while (pos) { + if (pos->expiration > entry->expiration) + break; + prev = pos; + pos = pos->next; + } + if (prev == NULL) { + entry->next = hapd->pmksa; + hapd->pmksa = entry; + } else { + entry->next = prev->next; + prev->next = entry; + } + entry->hnext = hapd->pmkid[PMKID_HASH(entry->pmkid)]; + hapd->pmkid[PMKID_HASH(entry->pmkid)] = entry; + + hapd->pmksa_count++; + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, + HOSTAPD_LEVEL_DEBUG, + "added PMKSA cache entry"); + if (HOSTAPD_DEBUG_COND(HOSTAPD_DEBUG_MINIMAL)) { + hostapd_hexdump("RSN: added PMKID", entry->pmkid, PMKID_LEN); + } +} + + +static void pmksa_cache_free(struct hostapd_data *hapd) +{ + struct rsn_pmksa_cache *entry, *prev; + int i; + struct sta_info *sta; + + entry = hapd->pmksa; + hapd->pmksa = NULL; + while (entry) { + prev = entry; + entry = entry->next; + _pmksa_cache_free_entry(prev); + } + eloop_cancel_timeout(pmksa_cache_expire, hapd, NULL); + for (i = 0; i < PMKID_HASH_SIZE; i++) + hapd->pmkid[i] = NULL; + for (sta = hapd->sta_list; sta; sta = sta->next) + sta->pmksa = NULL; +} + + +static struct rsn_pmksa_cache * pmksa_cache_get(struct hostapd_data *hapd, + u8 *spa, u8 *pmkid) +{ + struct rsn_pmksa_cache *entry; + + if (pmkid) + entry = hapd->pmkid[PMKID_HASH(pmkid)]; + else + entry = hapd->pmksa; + while (entry) { + if ((spa == NULL || memcmp(entry->spa, spa, ETH_ALEN) == 0) && + (pmkid == NULL || + memcmp(entry->pmkid, pmkid, PMKID_LEN) == 0)) + return entry; + entry = pmkid ? entry->hnext : entry->next; + } + return NULL; +} + + +struct wpa_ie_data { + int pairwise_cipher; + int group_cipher; + int key_mgmt; + int capabilities; + size_t num_pmkid; + u8 *pmkid; +}; + + +static int wpa_parse_wpa_ie_wpa(const u8 *wpa_ie, size_t wpa_ie_len, + struct wpa_ie_data *data) +{ + struct wpa_ie_hdr *hdr; + u8 *pos; + int left; + int i, count; + + memset(data, 0, sizeof(*data)); + data->pairwise_cipher = WPA_CIPHER_TKIP; + data->group_cipher = WPA_CIPHER_TKIP; + data->key_mgmt = WPA_KEY_MGMT_IEEE8021X; + + if (wpa_ie_len < sizeof(struct wpa_ie_hdr)) + return -1; + + hdr = (struct wpa_ie_hdr *) wpa_ie; + + if (hdr->elem_id != WLAN_EID_GENERIC || + hdr->len != wpa_ie_len - 2 || + memcmp(&hdr->oui, WPA_OUI_TYPE, WPA_SELECTOR_LEN) != 0 || + le_to_host16(hdr->version) != WPA_VERSION) { + return -2; + } + + pos = (u8 *) (hdr + 1); + left = wpa_ie_len - sizeof(*hdr); + + if (left >= WPA_SELECTOR_LEN) { + data->group_cipher = wpa_selector_to_bitfield(pos); + pos += WPA_SELECTOR_LEN; + left -= WPA_SELECTOR_LEN; + } else if (left > 0) + return -3; + + if (left >= 2) { + data->pairwise_cipher = 0; + count = pos[0] | (pos[1] << 8); + pos += 2; + left -= 2; + if (count == 0 || left < count * WPA_SELECTOR_LEN) + return -4; + for (i = 0; i < count; i++) { + data->pairwise_cipher |= wpa_selector_to_bitfield(pos); + pos += WPA_SELECTOR_LEN; + left -= WPA_SELECTOR_LEN; + } + } else if (left == 1) + return -5; + + if (left >= 2) { + data->key_mgmt = 0; + count = pos[0] | (pos[1] << 8); + pos += 2; + left -= 2; + if (count == 0 || left < count * WPA_SELECTOR_LEN) + return -6; + for (i = 0; i < count; i++) { + data->key_mgmt |= wpa_key_mgmt_to_bitfield(pos); + pos += WPA_SELECTOR_LEN; + left -= WPA_SELECTOR_LEN; + } + } else if (left == 1) + return -7; + + if (left >= 2) { + data->capabilities = pos[0] | (pos[1] << 8); + pos += 2; + left -= 2; + } + + if (left > 0) { + return -8; + } + + return 0; +} + + +static int wpa_parse_wpa_ie_rsn(const u8 *rsn_ie, size_t rsn_ie_len, + struct wpa_ie_data *data) +{ + struct rsn_ie_hdr *hdr; + u8 *pos; + int left; + int i, count; + + memset(data, 0, sizeof(*data)); + data->pairwise_cipher = WPA_CIPHER_CCMP; + data->group_cipher = WPA_CIPHER_CCMP; + data->key_mgmt = WPA_KEY_MGMT_IEEE8021X; + + if (rsn_ie_len < sizeof(struct rsn_ie_hdr)) + return -1; + + hdr = (struct rsn_ie_hdr *) rsn_ie; + + if (hdr->elem_id != WLAN_EID_RSN || + hdr->len != rsn_ie_len - 2 || + le_to_host16(hdr->version) != RSN_VERSION) { + return -2; + } + + pos = (u8 *) (hdr + 1); + left = rsn_ie_len - sizeof(*hdr); + + if (left >= RSN_SELECTOR_LEN) { + data->group_cipher = rsn_selector_to_bitfield(pos); + pos += RSN_SELECTOR_LEN; + left -= RSN_SELECTOR_LEN; + } else if (left > 0) + return -3; + + if (left >= 2) { + data->pairwise_cipher = 0; + count = pos[0] | (pos[1] << 8); + pos += 2; + left -= 2; + if (count == 0 || left < count * RSN_SELECTOR_LEN) + return -4; + for (i = 0; i < count; i++) { + data->pairwise_cipher |= rsn_selector_to_bitfield(pos); + pos += RSN_SELECTOR_LEN; + left -= RSN_SELECTOR_LEN; + } + } else if (left == 1) + return -5; + + if (left >= 2) { + data->key_mgmt = 0; + count = pos[0] | (pos[1] << 8); + pos += 2; + left -= 2; + if (count == 0 || left < count * RSN_SELECTOR_LEN) + return -6; + for (i = 0; i < count; i++) { + data->key_mgmt |= rsn_key_mgmt_to_bitfield(pos); + pos += RSN_SELECTOR_LEN; + left -= RSN_SELECTOR_LEN; + } + } else if (left == 1) + return -7; + + if (left >= 2) { + data->capabilities = pos[0] | (pos[1] << 8); + pos += 2; + left -= 2; + } + + if (left >= 2) { + data->num_pmkid = pos[0] | (pos[1] << 8); + pos += 2; + left -= 2; + if (left < data->num_pmkid * PMKID_LEN) { + printf("RSN: too short RSN IE for PMKIDs " + "(num=%lu, left=%d)\n", + (unsigned long) data->num_pmkid, left); + return -9; + } + data->pmkid = pos; + pos += data->num_pmkid * PMKID_LEN; + left -= data->num_pmkid * PMKID_LEN; + } + + if (left > 0) { + return -8; + } + + return 0; +} + + +int wpa_validate_wpa_ie(struct hostapd_data *hapd, struct sta_info *sta, + const u8 *wpa_ie, size_t wpa_ie_len, int version) +{ + struct wpa_ie_data data; + int ciphers, key_mgmt, res, i; + const u8 *selector; + + if (version == HOSTAPD_WPA_VERSION_WPA2) { + res = wpa_parse_wpa_ie_rsn(wpa_ie, wpa_ie_len, &data); + + selector = RSN_AUTH_KEY_MGMT_UNSPEC_802_1X; + if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X) + selector = RSN_AUTH_KEY_MGMT_UNSPEC_802_1X; + else if (data.key_mgmt & WPA_KEY_MGMT_PSK) + selector = RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X; + memcpy(hapd->wpa_auth->dot11RSNAAuthenticationSuiteSelected, + selector, RSN_SELECTOR_LEN); + + selector = RSN_CIPHER_SUITE_CCMP; + if (data.pairwise_cipher & WPA_CIPHER_CCMP) + selector = RSN_CIPHER_SUITE_CCMP; + else if (data.pairwise_cipher & WPA_CIPHER_TKIP) + selector = RSN_CIPHER_SUITE_TKIP; + else if (data.pairwise_cipher & WPA_CIPHER_WEP104) + selector = RSN_CIPHER_SUITE_WEP104; + else if (data.pairwise_cipher & WPA_CIPHER_WEP40) + selector = RSN_CIPHER_SUITE_WEP40; + else if (data.pairwise_cipher & WPA_CIPHER_NONE) + selector = RSN_CIPHER_SUITE_NONE; + memcpy(hapd->wpa_auth->dot11RSNAPairwiseCipherSelected, + selector, RSN_SELECTOR_LEN); + + selector = RSN_CIPHER_SUITE_CCMP; + if (data.group_cipher & WPA_CIPHER_CCMP) + selector = RSN_CIPHER_SUITE_CCMP; + else if (data.group_cipher & WPA_CIPHER_TKIP) + selector = RSN_CIPHER_SUITE_TKIP; + else if (data.group_cipher & WPA_CIPHER_WEP104) + selector = RSN_CIPHER_SUITE_WEP104; + else if (data.group_cipher & WPA_CIPHER_WEP40) + selector = RSN_CIPHER_SUITE_WEP40; + else if (data.group_cipher & WPA_CIPHER_NONE) + selector = RSN_CIPHER_SUITE_NONE; + memcpy(hapd->wpa_auth->dot11RSNAGroupCipherSelected, + selector, RSN_SELECTOR_LEN); + } else { + res = wpa_parse_wpa_ie_wpa(wpa_ie, wpa_ie_len, &data); + + selector = WPA_AUTH_KEY_MGMT_UNSPEC_802_1X; + if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X) + selector = WPA_AUTH_KEY_MGMT_UNSPEC_802_1X; + else if (data.key_mgmt & WPA_KEY_MGMT_PSK) + selector = WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X; + memcpy(hapd->wpa_auth->dot11RSNAAuthenticationSuiteSelected, + selector, WPA_SELECTOR_LEN); + + selector = WPA_CIPHER_SUITE_TKIP; + if (data.pairwise_cipher & WPA_CIPHER_CCMP) + selector = WPA_CIPHER_SUITE_CCMP; + else if (data.pairwise_cipher & WPA_CIPHER_TKIP) + selector = WPA_CIPHER_SUITE_TKIP; + else if (data.pairwise_cipher & WPA_CIPHER_WEP104) + selector = WPA_CIPHER_SUITE_WEP104; + else if (data.pairwise_cipher & WPA_CIPHER_WEP40) + selector = WPA_CIPHER_SUITE_WEP40; + else if (data.pairwise_cipher & WPA_CIPHER_NONE) + selector = WPA_CIPHER_SUITE_NONE; + memcpy(hapd->wpa_auth->dot11RSNAPairwiseCipherSelected, + selector, WPA_SELECTOR_LEN); + + selector = WPA_CIPHER_SUITE_TKIP; + if (data.group_cipher & WPA_CIPHER_CCMP) + selector = WPA_CIPHER_SUITE_CCMP; + else if (data.group_cipher & WPA_CIPHER_TKIP) + selector = WPA_CIPHER_SUITE_TKIP; + else if (data.group_cipher & WPA_CIPHER_WEP104) + selector = WPA_CIPHER_SUITE_WEP104; + else if (data.group_cipher & WPA_CIPHER_WEP40) + selector = WPA_CIPHER_SUITE_WEP40; + else if (data.group_cipher & WPA_CIPHER_NONE) + selector = WPA_CIPHER_SUITE_NONE; + memcpy(hapd->wpa_auth->dot11RSNAGroupCipherSelected, + selector, WPA_SELECTOR_LEN); + } + if (res) { + printf("Failed to parse WPA/RSN IE from " MACSTR " (res=%d)\n", + MAC2STR(sta->addr), res); + hostapd_hexdump("WPA/RSN IE", wpa_ie, wpa_ie_len); + return WPA_INVALID_IE; + } + + if (data.group_cipher != hapd->conf->wpa_group) { + printf("Invalid WPA group cipher (0x%x) from " MACSTR "\n", + data.group_cipher, MAC2STR(sta->addr)); + return WPA_INVALID_GROUP; + } + + key_mgmt = data.key_mgmt & hapd->conf->wpa_key_mgmt; + if (!key_mgmt) { + printf("Invalid WPA key mgmt (0x%x) from " MACSTR "\n", + data.key_mgmt, MAC2STR(sta->addr)); + return WPA_INVALID_AKMP; + } + if (key_mgmt & WPA_KEY_MGMT_IEEE8021X) + sta->wpa_key_mgmt = WPA_KEY_MGMT_IEEE8021X; + else + sta->wpa_key_mgmt = WPA_KEY_MGMT_PSK; + + ciphers = data.pairwise_cipher & hapd->conf->wpa_pairwise; + if (!ciphers) { + printf("Invalid WPA pairwise cipher (0x%x) from " MACSTR "\n", + data.pairwise_cipher, MAC2STR(sta->addr)); + return WPA_INVALID_PAIRWISE; + } + + if (ciphers & WPA_CIPHER_CCMP) + sta->pairwise = WPA_CIPHER_CCMP; + else + sta->pairwise = WPA_CIPHER_TKIP; + + /* TODO: clear WPA/WPA2 state if STA changes from one to another */ + if (wpa_ie[0] == WLAN_EID_RSN) + sta->wpa = WPA_VERSION_WPA2; + else + sta->wpa = WPA_VERSION_WPA; + + for (i = 0; i < data.num_pmkid; i++) { + if (HOSTAPD_DEBUG_COND(HOSTAPD_DEBUG_MINIMAL)) { + hostapd_hexdump("RSN IE: STA PMKID", + &data.pmkid[i * PMKID_LEN], PMKID_LEN); + } + sta->pmksa = pmksa_cache_get(hapd, sta->addr, + &data.pmkid[i * PMKID_LEN]); + if (sta->pmksa) { + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, + HOSTAPD_LEVEL_DEBUG, + "PMKID found from PMKSA cache"); + if (hapd->wpa_auth) { + memcpy(hapd->wpa_auth->dot11RSNAPMKIDUsed, + sta->pmksa->pmkid, PMKID_LEN); + } + break; + } + } + + return WPA_IE_OK; +} + + +void wpa_new_station(struct hostapd_data *hapd, struct sta_info *sta) +{ + struct wpa_state_machine *sm; + + if (!hapd->conf->wpa) + return; + + if (sta->wpa_sm) { + sm = sta->wpa_sm; + memset(sm->key_replay_counter, 0, WPA_REPLAY_COUNTER_LEN); + sm->ReAuthenticationRequest = TRUE; + wpa_sm_step(sm); + return; + } + + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, + HOSTAPD_LEVEL_DEBUG, "start authentication"); + sm = malloc(sizeof(struct wpa_state_machine)); + if (sm == NULL) + return; + memset(sm, 0, sizeof(struct wpa_state_machine)); + + sm->hapd = hapd; + sm->sta = sta; + sta->wpa_sm = sm; + + sm->Init = TRUE; + wpa_sm_step(sm); + sm->Init = FALSE; + sm->AuthenticationRequest = TRUE; + wpa_sm_step(sm); +} + + +void wpa_free_station(struct sta_info *sta) +{ + struct wpa_state_machine *sm = sta->wpa_sm; + + if (sm == NULL) + return; + + if (sm->hapd->conf->wpa_strict_rekey && sm->has_GTK) { + hostapd_logger(sm->hapd, sta->addr, HOSTAPD_MODULE_WPA, + HOSTAPD_LEVEL_DEBUG, "strict rekeying - force " + "GTK rekey since STA is leaving"); + eloop_cancel_timeout(wpa_rekey_gtk, sm->hapd, NULL); + eloop_register_timeout(0, 500000, wpa_rekey_gtk, sm->hapd, + NULL); + } + + eloop_cancel_timeout(wpa_send_eapol_timeout, sm->hapd, sta); + eloop_cancel_timeout(wpa_sm_call_step, sm->hapd, sta->wpa_sm); + eloop_cancel_timeout(rsn_preauth_finished_cb, sm->hapd, sta); + free(sm->last_rx_eapol_key); + free(sm); + sta->wpa_sm = NULL; +} + + +static void wpa_request_new_ptk(struct hostapd_data *hapd, + struct sta_info *sta) +{ + struct wpa_state_machine *sm = sta->wpa_sm; + + if (sm == NULL) + return; + + sm->PTKRequest = TRUE; + sm->PTK_valid = 0; +} + + +void wpa_receive(struct hostapd_data *hapd, struct sta_info *sta, + u8 *data, size_t data_len) +{ + struct wpa_state_machine *sm = sta->wpa_sm; + struct ieee802_1x_hdr *hdr; + struct wpa_eapol_key *key; + u16 key_info, key_data_length; + enum { PAIRWISE_2, PAIRWISE_4, GROUP_2, REQUEST } msg; + char *msgtxt; + + if (!hapd->conf->wpa) + return; + + if (sm == NULL) + return; + + if (data_len < sizeof(*hdr) + sizeof(*key)) + return; + + hdr = (struct ieee802_1x_hdr *) data; + key = (struct wpa_eapol_key *) (hdr + 1); + key_info = ntohs(key->key_info); + key_data_length = ntohs(key->key_data_length); + if (key_data_length > data_len - sizeof(*hdr) - sizeof(*key)) { + wpa_printf(MSG_INFO, "WPA: Invalid EAPOL-Key frame - " + "key_data overflow (%d > %lu)", + key_data_length, + (unsigned long) (data_len - sizeof(*hdr) - + sizeof(*key))); + return; + } + + /* FIX: verify that the EAPOL-Key frame was encrypted if pairwise keys + * are set */ + + if (key_info & WPA_KEY_INFO_REQUEST) { + msg = REQUEST; + msgtxt = "Request"; + } else if (!(key_info & WPA_KEY_INFO_KEY_TYPE)) { + msg = GROUP_2; + msgtxt = "2/2 Group"; + } else if (key_data_length == 0) { + msg = PAIRWISE_4; + msgtxt = "4/4 Pairwise"; + } else { + msg = PAIRWISE_2; + msgtxt = "2/4 Pairwise"; + } + + if (key_info & WPA_KEY_INFO_REQUEST) { + if (sta->req_replay_counter_used && + memcmp(key->replay_counter, sta->req_replay_counter, + WPA_REPLAY_COUNTER_LEN) <= 0) { + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, + HOSTAPD_LEVEL_WARNING, + "received EAPOL-Key request with " + "replayed counter"); + return; + } + } + + if (!(key_info & WPA_KEY_INFO_REQUEST) && + (!sm->key_replay_counter_valid || + memcmp(key->replay_counter, sm->key_replay_counter, + WPA_REPLAY_COUNTER_LEN) != 0)) { + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, + HOSTAPD_LEVEL_INFO, + "received EAPOL-Key %s with unexpected replay " + "counter", msgtxt); + if (HOSTAPD_DEBUG_COND(HOSTAPD_DEBUG_MINIMAL)) { + hostapd_hexdump("expected replay counter", + sm->key_replay_counter, + WPA_REPLAY_COUNTER_LEN); + hostapd_hexdump("received replay counter", + key->replay_counter, + WPA_REPLAY_COUNTER_LEN); + } + return; + } + + switch (msg) { + case PAIRWISE_2: + if (sm->wpa_ptk_state != WPA_PTK_PTKSTART && + sm->wpa_ptk_state != WPA_PTK_PTKCALCNEGOTIATING) { + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, + HOSTAPD_LEVEL_INFO, + "received EAPOL-Key msg 2/4 in invalid" + " state (%d) - dropped", + sm->wpa_ptk_state); + return; + } + if (sta->wpa_ie == NULL || + sta->wpa_ie_len != key_data_length || + memcmp(sta->wpa_ie, key + 1, key_data_length) != 0) { + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, + HOSTAPD_LEVEL_INFO, + "WPA IE from (Re)AssocReq did not match" + " with msg 2/4"); + if (HOSTAPD_DEBUG_COND(HOSTAPD_DEBUG_MINIMAL)) { + if (sta->wpa_ie) { + hostapd_hexdump("WPA IE in AssocReq", + sta->wpa_ie, + sta->wpa_ie_len); + } + hostapd_hexdump("WPA IE in msg 2/4", + (u8 *) (key + 1), + key_data_length); + } + /* MLME-DEAUTHENTICATE.request */ + wpa_sta_disconnect(hapd, sta); + return; + } + break; + case PAIRWISE_4: + if (sm->wpa_ptk_state != WPA_PTK_PTKINITNEGOTIATING || + !sm->PTK_valid) { + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, + HOSTAPD_LEVEL_INFO, + "received EAPOL-Key msg 4/4 in invalid" + " state (%d) - dropped", + sm->wpa_ptk_state); + return; + } + break; + case GROUP_2: + if (sm->wpa_ptk_group_state != WPA_PTK_GROUP_REKEYNEGOTIATING + || !sm->PTK_valid) { + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, + HOSTAPD_LEVEL_INFO, + "received EAPOL-Key msg 2/2 in invalid" + " state (%d) - dropped", + sm->wpa_ptk_group_state); + return; + } + break; + case REQUEST: + break; + } + + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, + HOSTAPD_LEVEL_DEBUG, "received EAPOL-Key frame (%s)", + msgtxt); + + if (key_info & WPA_KEY_INFO_ACK) { + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, + HOSTAPD_LEVEL_INFO, + "received invalid EAPOL-Key: Key Ack set"); + return; + } + + if (!(key_info & WPA_KEY_INFO_MIC)) { + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, + HOSTAPD_LEVEL_INFO, + "received invalid EAPOL-Key: Key MIC not set"); + return; + } + + sm->MICVerified = FALSE; + if (sm->PTK_valid) { + if (wpa_verify_key_mic(&sm->PTK, data, data_len)) { + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, + HOSTAPD_LEVEL_INFO, + "received EAPOL-Key with invalid MIC"); + return; + } + sm->MICVerified = TRUE; + eloop_cancel_timeout(wpa_send_eapol_timeout, sta->wpa_sm->hapd, + sta); + } + + if (key_info & WPA_KEY_INFO_REQUEST) { + if (sm->MICVerified) { + sta->req_replay_counter_used = 1; + memcpy(sta->req_replay_counter, key->replay_counter, + WPA_REPLAY_COUNTER_LEN); + } else { + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, + HOSTAPD_LEVEL_INFO, + "received EAPOL-Key request with " + "invalid MIC"); + return; + } + + if (key_info & WPA_KEY_INFO_ERROR) { + /* Supplicant reported a Michael MIC error */ + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, + HOSTAPD_LEVEL_INFO, + "received EAPOL-Key Error Request " + "(STA detected Michael MIC failure)"); + ieee80211_michael_mic_failure(hapd, sta->addr, 0); + sta->dot11RSNAStatsTKIPRemoteMICFailures++; + hapd->wpa_auth->dot11RSNAStatsTKIPRemoteMICFailures++; + /* Error report is not a request for a new key + * handshake, but since Authenticator may do it, let's + * change the keys now anyway. */ + wpa_request_new_ptk(hapd, sta); + } else if (key_info & WPA_KEY_INFO_KEY_TYPE) { + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, + HOSTAPD_LEVEL_INFO, + "received EAPOL-Key Request for new " + "4-Way Handshake"); + wpa_request_new_ptk(hapd, sta); + } else { + /* TODO: this could also be a request for STAKey + * if Key Data fields contains peer MAC address KDE. + * STAKey request should have 0xdd 00-0F-AC:2 in + * the beginning of Key Data */ + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, + HOSTAPD_LEVEL_INFO, + "received EAPOL-Key Request for GTK " + "rekeying"); + wpa_request_new_ptk(hapd, sta); + eloop_cancel_timeout(wpa_rekey_gtk, hapd, NULL); + wpa_rekey_gtk(hapd, NULL); + } + } else { + /* Do not allow the same key replay counter to be reused. */ + sm->key_replay_counter_valid = FALSE; + } + + free(sm->last_rx_eapol_key); + sm->last_rx_eapol_key = malloc(data_len); + if (sm->last_rx_eapol_key == NULL) + return; + memcpy(sm->last_rx_eapol_key, data, data_len); + sm->last_rx_eapol_key_len = data_len; + + sm->EAPOLKeyReceived = TRUE; + sm->EAPOLKeyPairwise = !!(key_info & WPA_KEY_INFO_KEY_TYPE); + sm->EAPOLKeyRequest = !!(key_info & WPA_KEY_INFO_REQUEST); + memcpy(sm->SNonce, key->key_nonce, WPA_NONCE_LEN); + wpa_sm_step(sm); +} + + +static void wpa_pmk_to_ptk(struct hostapd_data *hapd, const u8 *pmk, + const u8 *addr1, const u8 *addr2, + const u8 *nonce1, const u8 *nonce2, + u8 *ptk, size_t ptk_len) +{ + u8 data[2 * ETH_ALEN + 2 * WPA_NONCE_LEN]; + + /* PTK = PRF-X(PMK, "Pairwise key expansion", + * Min(AA, SA) || Max(AA, SA) || + * Min(ANonce, SNonce) || Max(ANonce, SNonce)) */ + + if (memcmp(addr1, addr2, ETH_ALEN) < 0) { + memcpy(data, addr1, ETH_ALEN); + memcpy(data + ETH_ALEN, addr2, ETH_ALEN); + } else { + memcpy(data, addr2, ETH_ALEN); + memcpy(data + ETH_ALEN, addr1, ETH_ALEN); + } + + if (memcmp(nonce1, nonce2, WPA_NONCE_LEN) < 0) { + memcpy(data + 2 * ETH_ALEN, nonce1, WPA_NONCE_LEN); + memcpy(data + 2 * ETH_ALEN + WPA_NONCE_LEN, nonce2, + WPA_NONCE_LEN); + } else { + memcpy(data + 2 * ETH_ALEN, nonce2, WPA_NONCE_LEN); + memcpy(data + 2 * ETH_ALEN + WPA_NONCE_LEN, nonce1, + WPA_NONCE_LEN); + } + + sha1_prf(pmk, WPA_PMK_LEN, "Pairwise key expansion", + data, sizeof(data), ptk, ptk_len); + + if (HOSTAPD_DEBUG_COND(HOSTAPD_DEBUG_MINIMAL)) { + hostapd_hexdump("PMK", pmk, WPA_PMK_LEN); + hostapd_hexdump("PTK", ptk, ptk_len); + } +} + + +static void wpa_gmk_to_gtk(struct hostapd_data *hapd, u8 *gmk, + u8 *addr, u8 *gnonce, u8 *gtk, size_t gtk_len) +{ + u8 data[ETH_ALEN + WPA_NONCE_LEN]; + + /* GTK = PRF-X(GMK, "Group key expansion", AA || GNonce) */ + memcpy(data, addr, ETH_ALEN); + memcpy(data + ETH_ALEN, gnonce, WPA_NONCE_LEN); + + sha1_prf(gmk, WPA_GMK_LEN, "Group key expansion", + data, sizeof(data), gtk, gtk_len); + + if (HOSTAPD_DEBUG_COND(HOSTAPD_DEBUG_MINIMAL)) { + hostapd_hexdump("GMK", gmk, WPA_GMK_LEN); + hostapd_hexdump("GTK", gtk, gtk_len); + } +} + + +static void wpa_send_eapol_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct hostapd_data *hapd = eloop_ctx; + struct sta_info *sta = timeout_ctx; + + if (!sta->wpa_sm || !(sta->flags & WLAN_STA_ASSOC)) + return; + + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, + HOSTAPD_LEVEL_DEBUG, "EAPOL-Key timeout"); + sta->wpa_sm->TimeoutEvt = TRUE; + wpa_sm_step(sta->wpa_sm); +} + + +static int wpa_calc_eapol_key_mic(int ver, u8 *key, u8 *data, size_t len, + u8 *mic) +{ + u8 hash[SHA1_MAC_LEN]; + + switch (ver) { + case WPA_KEY_INFO_TYPE_HMAC_MD5_RC4: + hmac_md5(key, 16, data, len, mic); + break; + case WPA_KEY_INFO_TYPE_HMAC_SHA1_AES: + hmac_sha1(key, 16, data, len, hash); + memcpy(mic, hash, MD5_MAC_LEN); + break; + default: + return -1; + } + return 0; +} + + +static void wpa_send_eapol(struct hostapd_data *hapd, struct sta_info *sta, + int secure, int mic, int ack, int install, + int pairwise, u8 *key_rsc, u8 *nonce, + u8 *ie, size_t ie_len, u8 *gtk, size_t gtk_len, + int keyidx) +{ + struct wpa_state_machine *sm = sta->wpa_sm; + struct ieee802_1x_hdr *hdr; + struct wpa_eapol_key *key; + size_t len; + int key_info, alg; + int timeout_ms; + int key_data_len, pad_len = 0; + u8 *buf, *pos; + + if (sm == NULL) + return; + + len = sizeof(struct ieee802_1x_hdr) + sizeof(struct wpa_eapol_key); + + if (sta->wpa == WPA_VERSION_WPA2) { + key_data_len = ie_len + gtk_len; + if (gtk_len) + key_data_len += 2 + RSN_SELECTOR_LEN + 2; + } else { + if (pairwise) { + /* WPA does not include GTK in 4-Way Handshake */ + gtk = NULL; + gtk_len = 0; + + /* key_rsc is for group key, so mask it out in case of + * WPA Pairwise key negotiation. */ + key_rsc = NULL; + } + key_data_len = ie_len + gtk_len; + } + + if (sta->pairwise == WPA_CIPHER_CCMP) { + key_info = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES; + if (gtk) { + pad_len = key_data_len % 8; + if (pad_len) + pad_len = 8 - pad_len; + key_data_len += pad_len + 8; + } + } else { + key_info = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4; + } + + len += key_data_len; + + hdr = malloc(len); + if (hdr == NULL) + return; + memset(hdr, 0, len); + hdr->version = hapd->conf->eapol_version; + hdr->type = IEEE802_1X_TYPE_EAPOL_KEY; + hdr->length = htons(len - sizeof(*hdr)); + key = (struct wpa_eapol_key *) (hdr + 1); + + key->type = sta->wpa == WPA_VERSION_WPA2 ? + EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA; + if (secure) + key_info |= WPA_KEY_INFO_SECURE; + if (mic) + key_info |= WPA_KEY_INFO_MIC; + if (ack) + key_info |= WPA_KEY_INFO_ACK; + if (install) + key_info |= WPA_KEY_INFO_INSTALL; + if (pairwise) + key_info |= WPA_KEY_INFO_KEY_TYPE; + if (gtk && sta->wpa == WPA_VERSION_WPA2) + key_info |= WPA_KEY_INFO_ENCR_KEY_DATA; + if (sta->wpa != WPA_VERSION_WPA2) { + if (pairwise) + keyidx = 0; + key_info |= keyidx << WPA_KEY_INFO_KEY_INDEX_SHIFT; + } + key->key_info = htons(key_info); + + alg = pairwise ? sta->pairwise : hapd->conf->wpa_group; + switch (alg) { + case WPA_CIPHER_CCMP: + key->key_length = htons(16); + break; + case WPA_CIPHER_TKIP: + key->key_length = htons(32); + break; + case WPA_CIPHER_WEP40: + key->key_length = htons(5); + break; + case WPA_CIPHER_WEP104: + key->key_length = htons(13); + break; + } + + inc_byte_array(sm->key_replay_counter, WPA_REPLAY_COUNTER_LEN); + memcpy(key->replay_counter, sm->key_replay_counter, + WPA_REPLAY_COUNTER_LEN); + sm->key_replay_counter_valid = TRUE; + + if (nonce) + memcpy(key->key_nonce, nonce, WPA_NONCE_LEN); + + if (key_rsc) + memcpy(key->key_rsc, key_rsc, WPA_KEY_RSC_LEN); + + if (ie && !gtk) { + memcpy(key + 1, ie, ie_len); + key->key_data_length = htons(ie_len); + } else if (gtk) { + buf = malloc(key_data_len); + if (buf == NULL) { + free(hdr); + return; + } + memset(buf, 0, key_data_len); + pos = buf; + if (ie) { + memcpy(pos, ie, ie_len); + pos += ie_len; + } + if (sta->wpa == WPA_VERSION_WPA2) { + *pos++ = WLAN_EID_GENERIC; + *pos++ = RSN_SELECTOR_LEN + 2 + gtk_len; + memcpy(pos, RSN_KEY_DATA_GROUPKEY, RSN_SELECTOR_LEN); + pos += RSN_SELECTOR_LEN; + *pos++ = keyidx & 0x03; + *pos++ = 0; + } + memcpy(pos, gtk, gtk_len); + pos += gtk_len; + if (pad_len) + *pos++ = 0xdd; + + if (HOSTAPD_DEBUG_COND(HOSTAPD_DEBUG_MINIMAL)) { + hostapd_hexdump("Plaintext EAPOL-Key Key Data", + buf, key_data_len); + } + if (key_info & WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) { + aes_wrap(sm->PTK.encr_key, (key_data_len - 8) / 8, buf, + (u8 *) (key + 1)); + key->key_data_length = htons(key_data_len); + } else { + u8 ek[32]; + memcpy(key->key_iv, + hapd->wpa_auth->Counter + WPA_NONCE_LEN - 16, + 16); + inc_byte_array(hapd->wpa_auth->Counter, WPA_NONCE_LEN); + memcpy(ek, key->key_iv, 16); + memcpy(ek + 16, sm->PTK.encr_key, 16); + memcpy(key + 1, buf, key_data_len); + rc4_skip(ek, 32, 256, (u8 *) (key + 1), key_data_len); + key->key_data_length = htons(key_data_len); + } + free(buf); + } + + if (mic) { + if (!sm->PTK_valid) { + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, + HOSTAPD_LEVEL_DEBUG, "PTK not valid " + "when sending EAPOL-Key frame"); + free(hdr); + return; + } + wpa_calc_eapol_key_mic(key_info & WPA_KEY_INFO_TYPE_MASK, + sm->PTK.mic_key, (u8 *) hdr, len, + key->key_mic); + } + + if (sta->eapol_sm) + sta->eapol_sm->dot1xAuthEapolFramesTx++; + hostapd_send_eapol(hapd, sta->addr, (u8 *) hdr, len, sm->pairwise_set); + free(hdr); + + timeout_ms = pairwise ? dot11RSNAConfigPairwiseUpdateTimeOut : + dot11RSNAConfigGroupUpdateTimeOut; + eloop_register_timeout(timeout_ms / 1000, (timeout_ms % 1000) * 1000, + wpa_send_eapol_timeout, hapd, sta); +} + + +static int wpa_verify_key_mic(struct wpa_ptk *PTK, u8 *data, size_t data_len) +{ + struct ieee802_1x_hdr *hdr; + struct wpa_eapol_key *key; + u16 key_info; + int type, ret = 0; + u8 mic[16]; + + if (data_len < sizeof(*hdr) + sizeof(*key)) + return -1; + + hdr = (struct ieee802_1x_hdr *) data; + key = (struct wpa_eapol_key *) (hdr + 1); + key_info = ntohs(key->key_info); + type = key_info & WPA_KEY_INFO_TYPE_MASK; + memcpy(mic, key->key_mic, 16); + memset(key->key_mic, 0, 16); + if (wpa_calc_eapol_key_mic(key_info & WPA_KEY_INFO_TYPE_MASK, + PTK->mic_key, data, data_len, key->key_mic) + || memcmp(mic, key->key_mic, 16) != 0) + ret = -1; + memcpy(key->key_mic, mic, 16); + return ret; +} + + +void wpa_sm_event(struct hostapd_data *hapd, struct sta_info *sta, + wpa_event event) +{ + struct wpa_state_machine *sm = sta->wpa_sm; + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, + HOSTAPD_LEVEL_DEBUG, + "event %d notification", event); + if (sm == NULL) + return; + + switch (event) { + case WPA_AUTH: + case WPA_ASSOC: + break; + case WPA_DEAUTH: + case WPA_DISASSOC: + sm->DeauthenticationRequest = TRUE; + break; + case WPA_REAUTH: + case WPA_REAUTH_EAPOL: + sm->ReAuthenticationRequest = TRUE; + break; + } + + sm->PTK_valid = FALSE; + memset(&sm->PTK, 0, sizeof(sm->PTK)); + + if (event != WPA_REAUTH_EAPOL) { + sm->pairwise_set = FALSE; + hostapd_set_encryption(sm->hapd, "none", sm->sta->addr, 0, + (u8 *) "", 0); + } + + wpa_sm_step(sm); +} + + +static const char * wpa_alg_txt(int alg) +{ + switch (alg) { + case WPA_CIPHER_CCMP: + return "CCMP"; + case WPA_CIPHER_TKIP: + return "TKIP"; + case WPA_CIPHER_WEP104: + case WPA_CIPHER_WEP40: + return "WEP"; + default: + return ""; + } +} + + +/* Definitions for clarifying state machine implementation */ +#define SM_STATE(machine, state) \ +static void sm_ ## machine ## _ ## state ## _Enter(struct wpa_state_machine \ +*sm) + +#define SM_ENTRY(machine, _state, _data) \ +sm->changed = TRUE; \ +sm->_data ## _ ## state = machine ## _ ## _state; \ +if (sm->hapd->conf->debug >= HOSTAPD_DEBUG_MINIMAL) \ + printf("WPA: " MACSTR " " #machine " entering state " #_state \ + "\n", MAC2STR(sm->sta->addr)); + +#define SM_ENTER(machine, state) sm_ ## machine ## _ ## state ## _Enter(sm) + +#define SM_STEP(machine) \ +static void sm_ ## machine ## _Step(struct wpa_state_machine *sm) + +#define SM_STEP_RUN(machine) sm_ ## machine ## _Step(sm) + + +SM_STATE(WPA_PTK, INITIALIZE) +{ + struct hostapd_data *hapd = sm->hapd; + + SM_ENTRY(WPA_PTK, INITIALIZE, wpa_ptk); + if (sm->Init) { + /* Init flag is not cleared here, so avoid busy + * loop by claiming nothing changed. */ + sm->changed = FALSE; + } + + sm->keycount = 0; + if (sm->GUpdateStationKeys) + hapd->wpa_auth->GKeyDoneStations--; + sm->GUpdateStationKeys = FALSE; + if (sm->sta->wpa == WPA_VERSION_WPA) + sm->PInitAKeys = FALSE; + if (1 /* Unicast cipher supported AND (ESS OR ((IBSS or WDS) and + * Local AA > Remote AA)) */) { + sm->Pair = TRUE; + } + ieee802_1x_notify_port_enabled(sm->sta->eapol_sm, 0); + hostapd_set_encryption(sm->hapd, "none", sm->sta->addr, 0, (u8 *) "", + 0); + sm->pairwise_set = FALSE; + sm->PTK_valid = FALSE; + memset(&sm->PTK, 0, sizeof(sm->PTK)); + ieee802_1x_notify_port_valid(sm->sta->eapol_sm, 0); + sm->TimeoutCtr = 0; + if (sm->sta->wpa_key_mgmt == WPA_KEY_MGMT_PSK) + ieee802_1x_set_sta_authorized(sm->hapd, sm->sta, 0); +} + + +SM_STATE(WPA_PTK, DISCONNECT) +{ + SM_ENTRY(WPA_PTK, DISCONNECT, wpa_ptk); + sm->Disconnect = FALSE; + wpa_sta_disconnect(sm->hapd, sm->sta); +} + + +SM_STATE(WPA_PTK, DISCONNECTED) +{ + SM_ENTRY(WPA_PTK, DISCONNECTED, wpa_ptk); + sm->hapd->wpa_auth->GNoStations--; + sm->DeauthenticationRequest = FALSE; +} + + +SM_STATE(WPA_PTK, AUTHENTICATION) +{ + SM_ENTRY(WPA_PTK, AUTHENTICATION, wpa_ptk); + sm->hapd->wpa_auth->GNoStations++; + memset(&sm->PTK, 0, sizeof(sm->PTK)); + sm->PTK_valid = FALSE; + if (sm->sta->eapol_sm) { + sm->sta->eapol_sm->portControl = Auto; + sm->sta->eapol_sm->portEnabled = TRUE; + } + sm->AuthenticationRequest = FALSE; +} + + +SM_STATE(WPA_PTK, AUTHENTICATION2) +{ + SM_ENTRY(WPA_PTK, AUTHENTICATION2, wpa_ptk); + memcpy(sm->ANonce, sm->hapd->wpa_auth->Counter, WPA_NONCE_LEN); + inc_byte_array(sm->hapd->wpa_auth->Counter, WPA_NONCE_LEN); + sm->ReAuthenticationRequest = FALSE; + /* IEEE 802.11i/D9.0 does not clear TimeoutCtr here, but this is more + * logical place than INITIALIZE since AUTHENTICATION2 can be + * re-entered on ReAuthenticationRequest without going through + * INITIALIZE. */ + sm->TimeoutCtr = 0; +} + + +SM_STATE(WPA_PTK, INITPMK) +{ + u8 *key; + size_t len; + SM_ENTRY(WPA_PTK, INITPMK, wpa_ptk); + if (sm->sta->pmksa) { + wpa_printf(MSG_DEBUG, "WPA: PMK from PMKSA cache"); + memcpy(sm->PMK, sm->sta->pmksa->pmk, WPA_PMK_LEN); + pmksa_cache_to_eapol_data(sm->sta->pmksa, sm->sta->eapol_sm); + } else if ((key = ieee802_1x_get_key_crypt(sm->sta->eapol_sm, &len))) { + wpa_printf(MSG_DEBUG, "WPA: PMK from EAPOL state machine " + "(len=%lu)", (unsigned long) len); + if (len > WPA_PMK_LEN) + len = WPA_PMK_LEN; + memcpy(sm->PMK, key, len); + } else { + wpa_printf(MSG_DEBUG, "WPA: Could not get PMK"); + } + sm->sta->req_replay_counter_used = 0; + /* IEEE 802.11i/D9.0 does not set keyRun to FALSE, but not doing this + * will break reauthentication since EAPOL state machines may not be + * get into AUTHENTICATING state that clears keyRun before WPA state + * machine enters AUTHENTICATION2 state and goes immediately to INITPMK + * state and takes PMK from the previously used AAA Key. This will + * eventually fail in 4-Way Handshake because Supplicant uses PMK + * derived from the new AAA Key. Setting keyRun = FALSE here seems to + * be good workaround for this issue. */ + if (sm->sta->eapol_sm) + sm->sta->eapol_sm->keyRun = FALSE; +} + + +SM_STATE(WPA_PTK, INITPSK) +{ + const u8 *psk; + SM_ENTRY(WPA_PTK, INITPSK, wpa_ptk); + psk = hostapd_get_psk(sm->hapd->conf, sm->sta->addr, NULL); + if (psk) + memcpy(sm->PMK, psk, WPA_PMK_LEN); + sm->sta->req_replay_counter_used = 0; +} + + +SM_STATE(WPA_PTK, PTKSTART) +{ + u8 *pmkid = NULL; + size_t pmkid_len = 0; + + SM_ENTRY(WPA_PTK, PTKSTART, wpa_ptk); + sm->PTKRequest = FALSE; + sm->TimeoutEvt = FALSE; + hostapd_logger(sm->hapd, sm->sta->addr, HOSTAPD_MODULE_WPA, + HOSTAPD_LEVEL_DEBUG, + "sending 1/4 msg of 4-Way Handshake"); + if (sm->sta->pmksa && + (pmkid = malloc(2 + RSN_SELECTOR_LEN + PMKID_LEN))) { + pmkid_len = 2 + RSN_SELECTOR_LEN + PMKID_LEN; + pmkid[0] = WLAN_EID_GENERIC; + pmkid[1] = RSN_SELECTOR_LEN + PMKID_LEN; + memcpy(&pmkid[2], RSN_KEY_DATA_PMKID, RSN_SELECTOR_LEN); + memcpy(&pmkid[2 + RSN_SELECTOR_LEN], sm->sta->pmksa->pmkid, + PMKID_LEN); + } + wpa_send_eapol(sm->hapd, sm->sta, 0, 0, 1, 0, 1, NULL, sm->ANonce, + pmkid, pmkid_len, NULL, 0, 0); + free(pmkid); + sm->TimeoutCtr++; +} + + +SM_STATE(WPA_PTK, PTKCALCNEGOTIATING) +{ + struct wpa_ptk PTK; + int ok = 0; + const u8 *pmk = NULL; + + SM_ENTRY(WPA_PTK, PTKCALCNEGOTIATING, wpa_ptk); + sm->EAPOLKeyReceived = FALSE; + + /* WPA with IEEE 802.1X: use the derived PMK from EAP + * WPA-PSK: iterate through possible PSKs and select the one matching + * the packet */ + for (;;) { + if (sm->sta->wpa_key_mgmt == WPA_KEY_MGMT_PSK) { + pmk = hostapd_get_psk(sm->hapd->conf, sm->sta->addr, + pmk); + if (pmk == NULL) + break; + } else + pmk = sm->PMK; + + wpa_pmk_to_ptk(sm->hapd, pmk, sm->hapd->own_addr, + sm->sta->addr, sm->ANonce, sm->SNonce, + (u8 *) &PTK, sizeof(PTK)); + + if (wpa_verify_key_mic(&PTK, sm->last_rx_eapol_key, + sm->last_rx_eapol_key_len) == 0) { + ok = 1; + break; + } + + if (sm->sta->wpa_key_mgmt != WPA_KEY_MGMT_PSK) + break; + } + + if (!ok) { + hostapd_logger(sm->hapd, sm->sta->addr, HOSTAPD_MODULE_WPA, + HOSTAPD_LEVEL_DEBUG, "invalid MIC in msg 2/4 " + "of 4-Way Handshake"); + return; + } + + eloop_cancel_timeout(wpa_send_eapol_timeout, sm->hapd, sm->sta); + + if (sm->sta->wpa_key_mgmt == WPA_KEY_MGMT_PSK) { + /* PSK may have changed from the previous choice, so update + * state machine data based on whatever PSK was selected here. + */ + memcpy(sm->PMK, pmk, WPA_PMK_LEN); + } + + sm->MICVerified = TRUE; + + memcpy(&sm->PTK, &PTK, sizeof(PTK)); + sm->PTK_valid = TRUE; +} + + +SM_STATE(WPA_PTK, PTKCALCNEGOTIATING2) +{ + SM_ENTRY(WPA_PTK, PTKCALCNEGOTIATING2, wpa_ptk); + sm->TimeoutCtr = 0; +} + + +SM_STATE(WPA_PTK, PTKINITNEGOTIATING) +{ + u8 rsc[WPA_KEY_RSC_LEN]; + struct wpa_authenticator *gsm = sm->hapd->wpa_auth; + u8 *wpa_ie; + int wpa_ie_len; + + SM_ENTRY(WPA_PTK, PTKINITNEGOTIATING, wpa_ptk); + sm->TimeoutEvt = FALSE; + /* Send EAPOL(1, 1, 1, Pair, P, RSC, ANonce, MIC(PTK), RSNIE, GTK[GN]) + */ + memset(rsc, 0, WPA_KEY_RSC_LEN); + hostapd_get_seqnum(sm->hapd, NULL, gsm->GN, rsc); + wpa_ie = sm->hapd->wpa_ie; + wpa_ie_len = sm->hapd->wpa_ie_len; + if (sm->sta->wpa == WPA_VERSION_WPA && + (sm->hapd->conf->wpa & HOSTAPD_WPA_VERSION_WPA2) && + wpa_ie_len > wpa_ie[1] + 2 && wpa_ie[0] == WLAN_EID_RSN) { + /* WPA-only STA, remove RSN IE */ + wpa_ie = wpa_ie + wpa_ie[1] + 2; + wpa_ie_len = wpa_ie[1] + 2; + } + hostapd_logger(sm->hapd, sm->sta->addr, HOSTAPD_MODULE_WPA, + HOSTAPD_LEVEL_DEBUG, + "sending 3/4 msg of 4-Way Handshake"); + wpa_send_eapol(sm->hapd, sm->sta, + sm->sta->wpa == WPA_VERSION_WPA2 ? 1 : 0, + 1, 1, 1, 1, rsc, sm->ANonce, + wpa_ie, wpa_ie_len, + gsm->GTK[gsm->GN - 1], gsm->GTK_len, gsm->GN); + sm->TimeoutCtr++; +} + + +SM_STATE(WPA_PTK, PTKINITDONE) +{ + SM_ENTRY(WPA_PTK, PTKINITDONE, wpa_ptk); + sm->EAPOLKeyReceived = FALSE; + if (sm->Pair) { + char *alg; + int klen; + if (sm->sta->pairwise == WPA_CIPHER_TKIP) { + alg = "TKIP"; + klen = 32; + } else { + alg = "CCMP"; + klen = 16; + } + if (hostapd_set_encryption(sm->hapd, alg, sm->sta->addr, 0, + sm->PTK.tk1, klen)) { + wpa_sta_disconnect(sm->hapd, sm->sta); + return; + } + /* FIX: MLME-SetProtection.Request(TA, Tx_Rx) */ + sm->pairwise_set = TRUE; + + if (sm->sta->wpa_key_mgmt == WPA_KEY_MGMT_PSK) + ieee802_1x_set_sta_authorized(sm->hapd, sm->sta, 1); + } + + if (0 /* IBSS == TRUE */) { + sm->keycount++; + if (sm->keycount == 2) { + ieee802_1x_notify_port_valid(sm->sta->eapol_sm, 1); + } + } else { + ieee802_1x_notify_port_valid(sm->sta->eapol_sm, 1); + } + if (sm->sta->eapol_sm) { + sm->sta->eapol_sm->keyAvailable = FALSE; + sm->sta->eapol_sm->keyDone = TRUE; + } + if (sm->sta->wpa == WPA_VERSION_WPA) + sm->PInitAKeys = TRUE; + else + sm->has_GTK = TRUE; + hostapd_logger(sm->hapd, sm->sta->addr, HOSTAPD_MODULE_WPA, + HOSTAPD_LEVEL_INFO, "pairwise key handshake completed " + "(%s)", + sm->sta->wpa == WPA_VERSION_WPA ? "WPA" : "RSN"); + if (sm->sta->wpa_key_mgmt == WPA_KEY_MGMT_PSK) + accounting_sta_start(sm->hapd, sm->sta); +} + + +SM_STEP(WPA_PTK) +{ + struct wpa_authenticator *wpa_auth = sm->hapd->wpa_auth; + + if (sm->Init) + SM_ENTER(WPA_PTK, INITIALIZE); + else if (sm->Disconnect + /* || FIX: dot11RSNAConfigSALifetime timeout */) + SM_ENTER(WPA_PTK, DISCONNECT); + else if (sm->DeauthenticationRequest) + SM_ENTER(WPA_PTK, DISCONNECTED); + else if (sm->AuthenticationRequest) + SM_ENTER(WPA_PTK, AUTHENTICATION); + else if (sm->ReAuthenticationRequest) + SM_ENTER(WPA_PTK, AUTHENTICATION2); + else if (sm->PTKRequest) + SM_ENTER(WPA_PTK, PTKSTART); + else switch (sm->wpa_ptk_state) { + case WPA_PTK_INITIALIZE: + break; + case WPA_PTK_DISCONNECT: + SM_ENTER(WPA_PTK, DISCONNECTED); + break; + case WPA_PTK_DISCONNECTED: + SM_ENTER(WPA_PTK, INITIALIZE); + break; + case WPA_PTK_AUTHENTICATION: + SM_ENTER(WPA_PTK, AUTHENTICATION2); + break; + case WPA_PTK_AUTHENTICATION2: + if ((sm->sta->wpa_key_mgmt == WPA_KEY_MGMT_IEEE8021X) && + sm->sta->eapol_sm && sm->sta->eapol_sm->keyRun) + SM_ENTER(WPA_PTK, INITPMK); + else if ((sm->sta->wpa_key_mgmt == WPA_KEY_MGMT_PSK) + /* FIX: && 802.1X::keyRun */) + SM_ENTER(WPA_PTK, INITPSK); + break; + case WPA_PTK_INITPMK: + if (sm->sta->eapol_sm && sm->sta->eapol_sm->keyAvailable) + SM_ENTER(WPA_PTK, PTKSTART); + else { + wpa_auth->dot11RSNA4WayHandshakeFailures++; + SM_ENTER(WPA_PTK, DISCONNECT); + } + break; + case WPA_PTK_INITPSK: + if (hostapd_get_psk(sm->hapd->conf, sm->sta->addr, NULL)) + SM_ENTER(WPA_PTK, PTKSTART); + else { + hostapd_logger(sm->hapd, sm->sta->addr, + HOSTAPD_MODULE_WPA, + HOSTAPD_LEVEL_INFO, + "no PSK configured for the STA"); + wpa_auth->dot11RSNA4WayHandshakeFailures++; + SM_ENTER(WPA_PTK, DISCONNECT); + } + break; + case WPA_PTK_PTKSTART: + if (sm->EAPOLKeyReceived && !sm->EAPOLKeyRequest && + sm->EAPOLKeyPairwise) + SM_ENTER(WPA_PTK, PTKCALCNEGOTIATING); + else if (sm->TimeoutCtr > dot11RSNAConfigPairwiseUpdateCount) { + wpa_auth->dot11RSNA4WayHandshakeFailures++; + SM_ENTER(WPA_PTK, DISCONNECT); + } else if (sm->TimeoutEvt) + SM_ENTER(WPA_PTK, PTKSTART); + break; + case WPA_PTK_PTKCALCNEGOTIATING: + if (sm->MICVerified) + SM_ENTER(WPA_PTK, PTKCALCNEGOTIATING2); + else if (sm->EAPOLKeyReceived && !sm->EAPOLKeyRequest && + sm->EAPOLKeyPairwise) + SM_ENTER(WPA_PTK, PTKCALCNEGOTIATING); + else if (sm->TimeoutEvt) + SM_ENTER(WPA_PTK, PTKSTART); + break; + case WPA_PTK_PTKCALCNEGOTIATING2: + SM_ENTER(WPA_PTK, PTKINITNEGOTIATING); + break; + case WPA_PTK_PTKINITNEGOTIATING: + if (sm->EAPOLKeyReceived && !sm->EAPOLKeyRequest && + sm->EAPOLKeyPairwise && sm->MICVerified) + SM_ENTER(WPA_PTK, PTKINITDONE); + else if (sm->TimeoutCtr > dot11RSNAConfigPairwiseUpdateCount) { + wpa_auth->dot11RSNA4WayHandshakeFailures++; + SM_ENTER(WPA_PTK, DISCONNECT); + } else if (sm->TimeoutEvt) + SM_ENTER(WPA_PTK, PTKINITNEGOTIATING); + break; + case WPA_PTK_PTKINITDONE: + break; + } +} + + +SM_STATE(WPA_PTK_GROUP, IDLE) +{ + SM_ENTRY(WPA_PTK_GROUP, IDLE, wpa_ptk_group); + if (sm->Init) { + /* Init flag is not cleared here, so avoid busy + * loop by claiming nothing changed. */ + sm->changed = FALSE; + } + sm->GTimeoutCtr = 0; +} + + +SM_STATE(WPA_PTK_GROUP, REKEYNEGOTIATING) +{ + u8 rsc[WPA_KEY_RSC_LEN]; + struct wpa_authenticator *gsm = sm->hapd->wpa_auth; + + SM_ENTRY(WPA_PTK_GROUP, REKEYNEGOTIATING, wpa_ptk_group); + if (sm->sta->wpa == WPA_VERSION_WPA) + sm->PInitAKeys = FALSE; + sm->TimeoutEvt = FALSE; + /* Send EAPOL(1, 1, 1, !Pair, G, RSC, GNonce, MIC(PTK), GTK[GN]) */ + memset(rsc, 0, WPA_KEY_RSC_LEN); + if (gsm->wpa_group_state == WPA_GROUP_SETKEYSDONE) + hostapd_get_seqnum(sm->hapd, NULL, gsm->GN, rsc); + hostapd_logger(sm->hapd, sm->sta->addr, HOSTAPD_MODULE_WPA, + HOSTAPD_LEVEL_DEBUG, + "sending 1/2 msg of Group Key Handshake"); + wpa_send_eapol(sm->hapd, sm->sta, 1, 1, 1, !sm->Pair, 0, rsc, + gsm->GNonce, NULL, 0, + gsm->GTK[gsm->GN - 1], gsm->GTK_len, gsm->GN); + sm->GTimeoutCtr++; +} + + +SM_STATE(WPA_PTK_GROUP, REKEYESTABLISHED) +{ + SM_ENTRY(WPA_PTK_GROUP, REKEYESTABLISHED, wpa_ptk_group); + sm->EAPOLKeyReceived = FALSE; + sm->GUpdateStationKeys = FALSE; + sm->hapd->wpa_auth->GKeyDoneStations--; + sm->GTimeoutCtr = 0; + /* FIX: MLME.SetProtection.Request(TA, Tx_Rx) */ + hostapd_logger(sm->hapd, sm->sta->addr, HOSTAPD_MODULE_WPA, + HOSTAPD_LEVEL_INFO, "group key handshake completed " + "(%s)", + sm->sta->wpa == WPA_VERSION_WPA ? "WPA" : "RSN"); + sm->has_GTK = TRUE; +} + + +SM_STATE(WPA_PTK_GROUP, KEYERROR) +{ + SM_ENTRY(WPA_PTK_GROUP, KEYERROR, wpa_ptk_group); + sm->hapd->wpa_auth->GKeyDoneStations--; + sm->GUpdateStationKeys = FALSE; + sm->Disconnect = TRUE; +} + + +SM_STEP(WPA_PTK_GROUP) +{ + if (sm->Init) + SM_ENTER(WPA_PTK_GROUP, IDLE); + else switch (sm->wpa_ptk_group_state) { + case WPA_PTK_GROUP_IDLE: + if (sm->GUpdateStationKeys || + (sm->sta->wpa == WPA_VERSION_WPA && sm->PInitAKeys)) + SM_ENTER(WPA_PTK_GROUP, REKEYNEGOTIATING); + break; + case WPA_PTK_GROUP_REKEYNEGOTIATING: + if (sm->EAPOLKeyReceived && !sm->EAPOLKeyRequest && + !sm->EAPOLKeyPairwise && sm->MICVerified) + SM_ENTER(WPA_PTK_GROUP, REKEYESTABLISHED); + else if (sm->GTimeoutCtr > dot11RSNAConfigGroupUpdateCount) + SM_ENTER(WPA_PTK_GROUP, KEYERROR); + else if (sm->TimeoutEvt) + SM_ENTER(WPA_PTK_GROUP, REKEYNEGOTIATING); + break; + case WPA_PTK_GROUP_KEYERROR: + SM_ENTER(WPA_PTK_GROUP, IDLE); + break; + case WPA_PTK_GROUP_REKEYESTABLISHED: + SM_ENTER(WPA_PTK_GROUP, IDLE); + break; + } +} + + +static void wpa_group_gtk_init(struct hostapd_data *hapd) +{ + struct wpa_authenticator *sm = hapd->wpa_auth; + HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "WPA: group state machine " + "entering state GTK_INIT\n"); + sm->changed = FALSE; /* GInit is not cleared here; avoid loop */ + sm->wpa_group_state = WPA_GROUP_GTK_INIT; + + /* GTK[0..N] = 0 */ + memset(sm->GTK, 0, sizeof(sm->GTK)); + sm->GN = 1; + sm->GM = 2; + /* GTK[GN] = CalcGTK() */ + /* FIX: is this the correct way of getting GNonce? */ + memcpy(sm->GNonce, sm->Counter, WPA_NONCE_LEN); + inc_byte_array(sm->Counter, WPA_NONCE_LEN); + wpa_gmk_to_gtk(hapd, sm->GMK, hapd->own_addr, sm->GNonce, + sm->GTK[sm->GN - 1], sm->GTK_len); +} + + +static void wpa_group_setkeys(struct hostapd_data *hapd) +{ + struct wpa_authenticator *sm = hapd->wpa_auth; + struct sta_info *sta; + int tmp; + + HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "WPA: group state machine " + "entering state SETKEYS\n"); + sm->changed = TRUE; + sm->wpa_group_state = WPA_GROUP_SETKEYS; + sm->GTKReKey = FALSE; + tmp = sm->GM; + sm->GM = sm->GN; + sm->GN = tmp; + sm->GKeyDoneStations = sm->GNoStations; + /* FIX: is this the correct way of getting GNonce? */ + memcpy(sm->GNonce, sm->Counter, WPA_NONCE_LEN); + inc_byte_array(sm->Counter, WPA_NONCE_LEN); + wpa_gmk_to_gtk(hapd, sm->GMK, hapd->own_addr, sm->GNonce, + sm->GTK[sm->GN - 1], sm->GTK_len); + + sta = hapd->sta_list; + while (sta) { + if (sta->wpa_sm) { + sta->wpa_sm->GUpdateStationKeys = TRUE; + wpa_sm_step(sta->wpa_sm); + } + sta = sta->next; + } +} + + +static void wpa_group_setkeysdone(struct hostapd_data *hapd) +{ + struct wpa_authenticator *sm = hapd->wpa_auth; + + HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "WPA: group state machine " + "entering state SETKEYSDONE\n"); + sm->changed = TRUE; + sm->wpa_group_state = WPA_GROUP_SETKEYSDONE; + hostapd_set_encryption(hapd, wpa_alg_txt(hapd->conf->wpa_group), + NULL, sm->GN, sm->GTK[sm->GN - 1], sm->GTK_len); +} + + +static void wpa_group_sm_step(struct hostapd_data *hapd) +{ + struct wpa_authenticator *sm = hapd->wpa_auth; + + if (sm->GInit) { + wpa_group_gtk_init(hapd); + } else if (sm->wpa_group_state == WPA_GROUP_GTK_INIT && + sm->GTKAuthenticator) { + wpa_group_setkeysdone(hapd); + } else if (sm->wpa_group_state == WPA_GROUP_SETKEYSDONE && + sm->GTKReKey) { + wpa_group_setkeys(hapd); + } else if (sm->wpa_group_state == WPA_GROUP_SETKEYS) { + if (sm->GKeyDoneStations == 0) + wpa_group_setkeysdone(hapd); + else if (sm->GTKReKey) + wpa_group_setkeys(hapd); + } +} + + +static int wpa_sm_sta_entry_alive(struct hostapd_data *hapd, u8 *addr) +{ + struct sta_info *sta; + sta = ap_get_sta(hapd, addr); + if (sta == NULL || sta->wpa_sm == NULL) + return 0; + return 1; +} + + +static void wpa_sm_step(struct wpa_state_machine *sm) +{ + struct hostapd_data *hapd; + u8 addr[6]; + if (sm == NULL || sm->sta == NULL || sm->sta->wpa_sm == NULL) + return; + hapd = sm->hapd; + + memcpy(addr, sm->sta->addr, 6); + do { + sm->changed = FALSE; + sm->hapd->wpa_auth->changed = FALSE; + + SM_STEP_RUN(WPA_PTK); + if (!wpa_sm_sta_entry_alive(hapd, addr)) + break; + SM_STEP_RUN(WPA_PTK_GROUP); + if (!wpa_sm_sta_entry_alive(hapd, addr)) + break; + wpa_group_sm_step(sm->hapd); + if (!wpa_sm_sta_entry_alive(hapd, addr)) + break; + } while (sm->changed || sm->hapd->wpa_auth->changed); +} + + +static void wpa_sm_call_step(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_state_machine *sm = timeout_ctx; + wpa_sm_step(sm); +} + + +void wpa_sm_notify(struct hostapd_data *hapd, struct sta_info *sta) +{ + if (sta->wpa_sm == NULL) + return; + eloop_register_timeout(0, 0, wpa_sm_call_step, hapd, sta->wpa_sm); +} + + +void wpa_gtk_rekey(struct hostapd_data *hapd) +{ + struct wpa_authenticator *sm = hapd->wpa_auth; + int tmp, i; + + if (sm == NULL) + return; + + for (i = 0; i < 2; i++) { + tmp = sm->GM; + sm->GM = sm->GN; + sm->GN = tmp; + memcpy(sm->GNonce, sm->Counter, WPA_NONCE_LEN); + inc_byte_array(sm->Counter, WPA_NONCE_LEN); + wpa_gmk_to_gtk(hapd, sm->GMK, hapd->own_addr, sm->GNonce, + sm->GTK[sm->GN - 1], sm->GTK_len); + } +} + + +static const char * wpa_bool_txt(int bool) +{ + return bool ? "TRUE" : "FALSE"; +} + + +static int wpa_cipher_bits(int cipher) +{ + switch (cipher) { + case WPA_CIPHER_CCMP: + return 128; + case WPA_CIPHER_TKIP: + return 256; + case WPA_CIPHER_WEP104: + return 104; + case WPA_CIPHER_WEP40: + return 40; + default: + return 0; + } +} + + +#define RSN_SUITE "%02x-%02x-%02x-%d" +#define RSN_SUITE_ARG(s) (s)[0], (s)[1], (s)[2], (s)[3] + +int wpa_get_mib(struct hostapd_data *hapd, char *buf, size_t buflen) +{ + int len = 0, i; + char pmkid_txt[PMKID_LEN * 2 + 1], *pos; + + len += snprintf(buf + len, buflen - len, + "dot11RSNAOptionImplemented=TRUE\n" + "dot11RSNAPreauthenticationImplemented=TRUE\n" + "dot11RSNAEnabled=%s\n" + "dot11RSNAPreauthenticationEnabled=%s\n", + wpa_bool_txt(hapd->conf->wpa & + HOSTAPD_WPA_VERSION_WPA2), + wpa_bool_txt(hapd->conf->rsn_preauth)); + + if (hapd->wpa_auth == NULL) + return len; + + pos = pmkid_txt; + for (i = 0; i < PMKID_LEN; i++) { + pos += sprintf(pos, "%02x", + hapd->wpa_auth->dot11RSNAPMKIDUsed[i]); + } + + len += snprintf(buf + len, buflen - len, + "dot11RSNAConfigVersion=%u\n" + "dot11RSNAConfigPairwiseKeysSupported=9999\n" + /* FIX: dot11RSNAConfigGroupCipher */ + /* FIX: dot11RSNAConfigGroupRekeyMethod */ + /* FIX: dot11RSNAConfigGroupRekeyTime */ + /* FIX: dot11RSNAConfigGroupRekeyPackets */ + "dot11RSNAConfigGroupRekeyStrict=%u\n" + "dot11RSNAConfigGroupUpdateCount=%u\n" + "dot11RSNAConfigPairwiseUpdateCount=%u\n" + "dot11RSNAConfigGroupCipherSize=%u\n" + "dot11RSNAConfigPMKLifetime=%u\n" + "dot11RSNAConfigPMKReauthThreshold=%u\n" + "dot11RSNAConfigNumberOfPTKSAReplayCounters=0\n" + "dot11RSNAConfigSATimeout=%u\n" + "dot11RSNAAuthenticationSuiteSelected=" RSN_SUITE "\n" + "dot11RSNAPairwiseCipherSelected=" RSN_SUITE "\n" + "dot11RSNAGroupCipherSelected=" RSN_SUITE "\n" + "dot11RSNAPMKIDUsed=%s\n" + "dot11RSNAAuthenticationSuiteRequested=" RSN_SUITE "\n" + "dot11RSNAPairwiseCipherRequested=" RSN_SUITE "\n" + "dot11RSNAGroupCipherRequested=" RSN_SUITE "\n" + "dot11RSNATKIPCounterMeasuresInvoked=%u\n" + "dot11RSNA4WayHandshakeFailures=%u\n" + "dot11RSNAConfigNumberOfGTKSAReplayCounters=0\n", + RSN_VERSION, + !!hapd->conf->wpa_strict_rekey, + dot11RSNAConfigGroupUpdateCount, + dot11RSNAConfigPairwiseUpdateCount, + wpa_cipher_bits(hapd->conf->wpa_group), + dot11RSNAConfigPMKLifetime, + dot11RSNAConfigPMKReauthThreshold, + dot11RSNAConfigSATimeout, + RSN_SUITE_ARG(hapd->wpa_auth-> + dot11RSNAAuthenticationSuiteSelected), + RSN_SUITE_ARG(hapd->wpa_auth-> + dot11RSNAPairwiseCipherSelected), + RSN_SUITE_ARG(hapd->wpa_auth-> + dot11RSNAGroupCipherSelected), + pmkid_txt, + RSN_SUITE_ARG(hapd->wpa_auth-> + dot11RSNAAuthenticationSuiteRequested), + RSN_SUITE_ARG(hapd->wpa_auth-> + dot11RSNAPairwiseCipherRequested), + RSN_SUITE_ARG(hapd->wpa_auth-> + dot11RSNAGroupCipherRequested), + hapd->wpa_auth->dot11RSNATKIPCounterMeasuresInvoked, + hapd->wpa_auth->dot11RSNA4WayHandshakeFailures); + + /* TODO: dot11RSNAConfigPairwiseCiphersTable */ + /* TODO: dot11RSNAConfigAuthenticationSuitesTable */ + + /* Private MIB */ + len += snprintf(buf + len, buflen - len, + "hostapdWPAGroupState=%d\n", + hapd->wpa_auth->wpa_group_state); + + return len; +} + + +int wpa_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta, + char *buf, size_t buflen) +{ + int len = 0; + u8 not_used[4] = { 0, 0, 0, 0 }; + const u8 *pairwise = not_used; + + /* TODO: FF-FF-FF-FF-FF-FF entry for broadcast/multicast stats */ + + /* dot11RSNAStatsEntry */ + + if (sta->wpa == WPA_VERSION_WPA) { + if (sta->pairwise == WPA_CIPHER_CCMP) + pairwise = WPA_CIPHER_SUITE_CCMP; + else if (sta->pairwise == WPA_CIPHER_TKIP) + pairwise = WPA_CIPHER_SUITE_TKIP; + else if (sta->pairwise == WPA_CIPHER_WEP104) + pairwise = WPA_CIPHER_SUITE_WEP104; + else if (sta->pairwise == WPA_CIPHER_WEP40) + pairwise = WPA_CIPHER_SUITE_WEP40; + else if (sta->pairwise == WPA_CIPHER_NONE) + pairwise = WPA_CIPHER_SUITE_NONE; + } else if (sta->wpa == WPA_VERSION_WPA2) { + if (sta->pairwise == WPA_CIPHER_CCMP) + pairwise = RSN_CIPHER_SUITE_CCMP; + else if (sta->pairwise == WPA_CIPHER_TKIP) + pairwise = RSN_CIPHER_SUITE_TKIP; + else if (sta->pairwise == WPA_CIPHER_WEP104) + pairwise = RSN_CIPHER_SUITE_WEP104; + else if (sta->pairwise == WPA_CIPHER_WEP40) + pairwise = RSN_CIPHER_SUITE_WEP40; + else if (sta->pairwise == WPA_CIPHER_NONE) + pairwise = RSN_CIPHER_SUITE_NONE; + } else + return 0; + + len += snprintf(buf + len, buflen - len, + /* TODO: dot11RSNAStatsIndex */ + "dot11RSNAStatsSTAAddress=" MACSTR "\n" + "dot11RSNAStatsVersion=1\n" + "dot11RSNAStatsSelectedPairwiseCipher=" RSN_SUITE "\n" + /* TODO: dot11RSNAStatsTKIPICVErrors */ + "dot11RSNAStatsTKIPLocalMICFailures=%u\n" + "dot11RSNAStatsTKIPRemoveMICFailures=%u\n" + /* TODO: dot11RSNAStatsCCMPReplays */ + /* TODO: dot11RSNAStatsCCMPDecryptErrors */ + /* TODO: dot11RSNAStatsTKIPReplays */, + MAC2STR(sta->addr), + RSN_SUITE_ARG(pairwise), + sta->dot11RSNAStatsTKIPLocalMICFailures, + sta->dot11RSNAStatsTKIPRemoteMICFailures); + + if (sta->wpa_sm == NULL) + return len; + + /* Private MIB */ + len += snprintf(buf + len, buflen - len, + "hostapdWPAPTKState=%d\n" + "hostapdWPAPTKGroupState=%d\n", + sta->wpa_sm->wpa_ptk_state, + sta->wpa_sm->wpa_ptk_group_state); + + return len; +} diff --git a/contrib/hostapd-0.4.9/wpa.h b/contrib/hostapd-0.4.9/wpa.h new file mode 100644 index 0000000000..62159e78cf --- /dev/null +++ b/contrib/hostapd-0.4.9/wpa.h @@ -0,0 +1,196 @@ +#ifndef WPA_H +#define WPA_H + +#define WPA_NONCE_LEN 32 +#define WPA_PMK_LEN PMK_LEN +#define WPA_REPLAY_COUNTER_LEN 8 +#define WPA_GMK_LEN 32 +#define WPA_GTK_MAX_LEN 32 +#define WPA_KEY_RSC_LEN 8 +#define PMKID_LEN 16 + +struct rsn_pmksa_cache { + struct rsn_pmksa_cache *next, *hnext; + u8 pmkid[PMKID_LEN]; + u8 pmk[PMK_LEN]; + time_t expiration; + int akmp; /* WPA_KEY_MGMT_* */ + u8 spa[ETH_ALEN]; + u8 *identity; + size_t identity_len; + struct radius_class_data radius_class; +}; + +struct rsn_preauth_interface { + struct rsn_preauth_interface *next; + struct hostapd_data *hapd; + struct l2_packet_data *l2; + char *ifname; + int ifindex; +}; + +struct wpa_eapol_key { + u8 type; + u16 key_info; + u16 key_length; + u8 replay_counter[WPA_REPLAY_COUNTER_LEN]; + u8 key_nonce[WPA_NONCE_LEN]; + u8 key_iv[16]; + u8 key_rsc[WPA_KEY_RSC_LEN]; + u8 key_id[8]; /* Reserved */ + u8 key_mic[16]; + u16 key_data_length; + /* followed by key_data_length bytes of key_data */ +} __attribute__ ((packed)); + +#define WPA_KEY_INFO_TYPE_MASK (BIT(0) | BIT(1) | BIT(2)) +#define WPA_KEY_INFO_TYPE_HMAC_MD5_RC4 BIT(0) +#define WPA_KEY_INFO_TYPE_HMAC_SHA1_AES BIT(1) +#define WPA_KEY_INFO_KEY_TYPE BIT(3) /* 1 = Pairwise, 0 = Group key */ +/* bit4..5 is used in WPA, but is reserved in IEEE 802.11i/RSN */ +#define WPA_KEY_INFO_KEY_INDEX_MASK (BIT(4) | BIT(5)) +#define WPA_KEY_INFO_KEY_INDEX_SHIFT 4 +#define WPA_KEY_INFO_INSTALL BIT(6) /* pairwise */ +#define WPA_KEY_INFO_TXRX BIT(6) /* group */ +#define WPA_KEY_INFO_ACK BIT(7) +#define WPA_KEY_INFO_MIC BIT(8) +#define WPA_KEY_INFO_SECURE BIT(9) +#define WPA_KEY_INFO_ERROR BIT(10) +#define WPA_KEY_INFO_REQUEST BIT(11) +#define WPA_KEY_INFO_ENCR_KEY_DATA BIT(12) + + +/* per STA state machine data */ + +struct wpa_ptk { + u8 mic_key[16]; /* EAPOL-Key MIC Key (MK) */ + u8 encr_key[16]; /* EAPOL-Key Encryption Key (EK) */ + u8 tk1[16]; /* Temporal Key 1 (TK1) */ + union { + u8 tk2[16]; /* Temporal Key 2 (TK2) */ + struct { + u8 tx_mic_key[8]; + u8 rx_mic_key[8]; + } auth; + } u; +} __attribute__ ((packed)); + +struct wpa_state_machine { + struct hostapd_data *hapd; + struct sta_info *sta; + + enum { + WPA_PTK_INITIALIZE, WPA_PTK_DISCONNECT, WPA_PTK_DISCONNECTED, + WPA_PTK_AUTHENTICATION, WPA_PTK_AUTHENTICATION2, + WPA_PTK_INITPMK, WPA_PTK_INITPSK, WPA_PTK_PTKSTART, + WPA_PTK_PTKCALCNEGOTIATING, WPA_PTK_PTKCALCNEGOTIATING2, + WPA_PTK_PTKINITNEGOTIATING, WPA_PTK_PTKINITDONE + } wpa_ptk_state; + + enum { + WPA_PTK_GROUP_IDLE = 0, + WPA_PTK_GROUP_REKEYNEGOTIATING, + WPA_PTK_GROUP_REKEYESTABLISHED, + WPA_PTK_GROUP_KEYERROR + } wpa_ptk_group_state; + + Boolean Init; + Boolean DeauthenticationRequest; + Boolean AuthenticationRequest; + Boolean ReAuthenticationRequest; + Boolean Disconnect; + int TimeoutCtr; + int GTimeoutCtr; + Boolean TimeoutEvt; + Boolean EAPOLKeyReceived; + Boolean EAPOLKeyPairwise; + Boolean EAPOLKeyRequest; + Boolean MICVerified; + Boolean GUpdateStationKeys; + u8 ANonce[WPA_NONCE_LEN]; + u8 SNonce[WPA_NONCE_LEN]; + u8 PMK[WPA_PMK_LEN]; + struct wpa_ptk PTK; + Boolean PTK_valid; + Boolean pairwise_set; + int keycount; + Boolean Pair; + u8 key_replay_counter[WPA_REPLAY_COUNTER_LEN]; + Boolean key_replay_counter_valid; + Boolean PInitAKeys; /* WPA only, not in IEEE 802.11i/D8 */ + Boolean PTKRequest; /* not in IEEE 802.11i state machine */ + Boolean has_GTK; + + u8 *last_rx_eapol_key; /* starting from IEEE 802.1X header */ + size_t last_rx_eapol_key_len; + + Boolean changed; +}; + +/* per authenticator data */ +struct wpa_authenticator { + Boolean GInit; + int GNoStations; + int GKeyDoneStations; + Boolean GTKReKey; + int GTK_len; + int GN, GM; + Boolean GTKAuthenticator; + u8 Counter[WPA_NONCE_LEN]; + + enum { + WPA_GROUP_GTK_INIT = 0, + WPA_GROUP_SETKEYS, WPA_GROUP_SETKEYSDONE + } wpa_group_state; + + u8 GMK[WPA_GMK_LEN]; + u8 GTK[2][WPA_GTK_MAX_LEN]; + u8 GNonce[WPA_NONCE_LEN]; + Boolean changed; + + unsigned int dot11RSNAStatsTKIPRemoteMICFailures; + u8 dot11RSNAAuthenticationSuiteSelected[4]; + u8 dot11RSNAPairwiseCipherSelected[4]; + u8 dot11RSNAGroupCipherSelected[4]; + u8 dot11RSNAPMKIDUsed[PMKID_LEN]; + u8 dot11RSNAAuthenticationSuiteRequested[4]; /* FIX: update */ + u8 dot11RSNAPairwiseCipherRequested[4]; /* FIX: update */ + u8 dot11RSNAGroupCipherRequested[4]; /* FIX: update */ + unsigned int dot11RSNATKIPCounterMeasuresInvoked; + unsigned int dot11RSNA4WayHandshakeFailures; +}; + + +int wpa_init(struct hostapd_data *hapd); +void wpa_deinit(struct hostapd_data *hapd); + +enum { + WPA_IE_OK, WPA_INVALID_IE, WPA_INVALID_GROUP, WPA_INVALID_PAIRWISE, + WPA_INVALID_AKMP +}; + +int wpa_validate_wpa_ie(struct hostapd_data *hapd, struct sta_info *sta, + const u8 *wpa_ie, size_t wpa_ie_len, int version); +void wpa_new_station(struct hostapd_data *hapd, struct sta_info *sta); +void wpa_free_station(struct sta_info *sta); +void wpa_receive(struct hostapd_data *hapd, struct sta_info *sta, + u8 *data, size_t data_len); +typedef enum { + WPA_AUTH, WPA_ASSOC, WPA_DISASSOC, WPA_DEAUTH, WPA_REAUTH, + WPA_REAUTH_EAPOL +} wpa_event; +void wpa_sm_event(struct hostapd_data *hapd, struct sta_info *sta, + wpa_event event); +void wpa_sm_notify(struct hostapd_data *hapd, struct sta_info *sta); +void pmksa_cache_add(struct hostapd_data *hapd, struct sta_info *sta, u8 *pmk, + int session_timeout); +void rsn_preauth_finished(struct hostapd_data *hapd, struct sta_info *sta, + int success); +void rsn_preauth_send(struct hostapd_data *hapd, struct sta_info *sta, + u8 *buf, size_t len); +void wpa_gtk_rekey(struct hostapd_data *hapd); +int wpa_get_mib(struct hostapd_data *hapd, char *buf, size_t buflen); +int wpa_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta, + char *buf, size_t buflen); + +#endif /* WPA_H */ diff --git a/contrib/hostapd-0.4.9/wpa_ctrl.c b/contrib/hostapd-0.4.9/wpa_ctrl.c new file mode 100644 index 0000000000..98e0669a47 --- /dev/null +++ b/contrib/hostapd-0.4.9/wpa_ctrl.c @@ -0,0 +1,239 @@ +/* + * wpa_supplicant/hostapd control interface library + * Copyright (c) 2004-2005, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include +#include +#include +#include +#include +#include +#ifndef CONFIG_NATIVE_WINDOWS +#include +#include +#include +#endif /* CONFIG_NATIVE_WINDOWS */ + +#include "wpa_ctrl.h" +#ifdef CONFIG_NATIVE_WINDOWS +#include "common.h" +#endif /* CONFIG_NATIVE_WINDOWS */ + + +/** + * struct wpa_ctrl - Internal structure for control interface library + * + * This structure is used by the wpa_supplicant/hostapd control interface + * library to store internal data. Programs using the library should not touch + * this data directly. They can only use the pointer to the data structure as + * an identifier for the control interface connection and use this as one of + * the arguments for most of the control interface library functions. + */ +struct wpa_ctrl { + int s; +#ifdef CONFIG_CTRL_IFACE_UDP + struct sockaddr_in local; + struct sockaddr_in dest; +#else /* CONFIG_CTRL_IFACE_UDP */ + struct sockaddr_un local; + struct sockaddr_un dest; +#endif /* CONFIG_CTRL_IFACE_UDP */ +}; + + +struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path) +{ + struct wpa_ctrl *ctrl; +#ifndef CONFIG_CTRL_IFACE_UDP + static int counter = 0; +#endif /* CONFIG_CTRL_IFACE_UDP */ + + ctrl = malloc(sizeof(*ctrl)); + if (ctrl == NULL) + return NULL; + memset(ctrl, 0, sizeof(*ctrl)); + +#ifdef CONFIG_CTRL_IFACE_UDP + ctrl->s = socket(PF_INET, SOCK_DGRAM, 0); + if (ctrl->s < 0) { + perror("socket"); + free(ctrl); + return NULL; + } + + ctrl->local.sin_family = AF_INET; + ctrl->local.sin_addr.s_addr = htonl((127 << 24) | 1); + if (bind(ctrl->s, (struct sockaddr *) &ctrl->local, + sizeof(ctrl->local)) < 0) { + close(ctrl->s); + free(ctrl); + return NULL; + } + + ctrl->dest.sin_family = AF_INET; + ctrl->dest.sin_addr.s_addr = htonl((127 << 24) | 1); + ctrl->dest.sin_port = htons(WPA_CTRL_IFACE_PORT); + if (connect(ctrl->s, (struct sockaddr *) &ctrl->dest, + sizeof(ctrl->dest)) < 0) { + perror("connect"); + close(ctrl->s); + free(ctrl); + return NULL; + } +#else /* CONFIG_CTRL_IFACE_UDP */ + ctrl->s = socket(PF_UNIX, SOCK_DGRAM, 0); + if (ctrl->s < 0) { + free(ctrl); + return NULL; + } + + ctrl->local.sun_family = AF_UNIX; + snprintf(ctrl->local.sun_path, sizeof(ctrl->local.sun_path), + "/tmp/wpa_ctrl_%d-%d", getpid(), counter++); + if (bind(ctrl->s, (struct sockaddr *) &ctrl->local, + sizeof(ctrl->local)) < 0) { + close(ctrl->s); + free(ctrl); + return NULL; + } + + ctrl->dest.sun_family = AF_UNIX; + snprintf(ctrl->dest.sun_path, sizeof(ctrl->dest.sun_path), "%s", + ctrl_path); + if (connect(ctrl->s, (struct sockaddr *) &ctrl->dest, + sizeof(ctrl->dest)) < 0) { + close(ctrl->s); + unlink(ctrl->local.sun_path); + free(ctrl); + return NULL; + } +#endif /* CONFIG_CTRL_IFACE_UDP */ + + return ctrl; +} + + +void wpa_ctrl_close(struct wpa_ctrl *ctrl) +{ +#ifndef CONFIG_CTRL_IFACE_UDP + unlink(ctrl->local.sun_path); +#endif /* CONFIG_CTRL_IFACE_UDP */ + close(ctrl->s); + free(ctrl); +} + + +int wpa_ctrl_request(struct wpa_ctrl *ctrl, const char *cmd, size_t cmd_len, + char *reply, size_t *reply_len, + void (*msg_cb)(char *msg, size_t len)) +{ + struct timeval tv; + int res; + fd_set rfds; + + if (send(ctrl->s, cmd, cmd_len, 0) < 0) + return -1; + + for (;;) { + tv.tv_sec = 2; + tv.tv_usec = 0; + FD_ZERO(&rfds); + FD_SET(ctrl->s, &rfds); + res = select(ctrl->s + 1, &rfds, NULL, NULL, &tv); + if (FD_ISSET(ctrl->s, &rfds)) { + res = recv(ctrl->s, reply, *reply_len, 0); + if (res < 0) + return res; + if (res > 0 && reply[0] == '<') { + /* This is an unsolicited message from + * wpa_supplicant, not the reply to the + * request. Use msg_cb to report this to the + * caller. */ + if (msg_cb) { + /* Make sure the message is nul + * terminated. */ + if ((size_t) res == *reply_len) + res = (*reply_len) - 1; + reply[res] = '\0'; + msg_cb(reply, res); + } + continue; + } + *reply_len = res; + break; + } else { + return -2; + } + } + return 0; +} + + +static int wpa_ctrl_attach_helper(struct wpa_ctrl *ctrl, int attach) +{ + char buf[10]; + int ret; + size_t len = 10; + + ret = wpa_ctrl_request(ctrl, attach ? "ATTACH" : "DETACH", 6, + buf, &len, NULL); + if (ret < 0) + return ret; + if (len == 3 && memcmp(buf, "OK\n", 3) == 0) + return 0; + return -1; +} + + +int wpa_ctrl_attach(struct wpa_ctrl *ctrl) +{ + return wpa_ctrl_attach_helper(ctrl, 1); +} + + +int wpa_ctrl_detach(struct wpa_ctrl *ctrl) +{ + return wpa_ctrl_attach_helper(ctrl, 0); +} + + +int wpa_ctrl_recv(struct wpa_ctrl *ctrl, char *reply, size_t *reply_len) +{ + int res; + + res = recv(ctrl->s, reply, *reply_len, 0); + if (res < 0) + return res; + *reply_len = res; + return 0; +} + + +int wpa_ctrl_pending(struct wpa_ctrl *ctrl) +{ + struct timeval tv; + int res; + fd_set rfds; + tv.tv_sec = 0; + tv.tv_usec = 0; + FD_ZERO(&rfds); + FD_SET(ctrl->s, &rfds); + res = select(ctrl->s + 1, &rfds, NULL, NULL, &tv); + return FD_ISSET(ctrl->s, &rfds); +} + + +int wpa_ctrl_get_fd(struct wpa_ctrl *ctrl) +{ + return ctrl->s; +} diff --git a/contrib/hostapd-0.4.9/wpa_ctrl.h b/contrib/hostapd-0.4.9/wpa_ctrl.h new file mode 100644 index 0000000000..c8fa48d693 --- /dev/null +++ b/contrib/hostapd-0.4.9/wpa_ctrl.h @@ -0,0 +1,185 @@ +/* + * wpa_supplicant/hostapd control interface library + * Copyright (c) 2004-2005, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef WPA_CTRL_H +#define WPA_CTRL_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* wpa_supplicant control interface - fixed message prefixes */ + +/** Interactive request for identity/password/pin */ +#define WPA_CTRL_REQ "CTRL-REQ-" + +/** Response to identity/password/pin request */ +#define WPA_CTRL_RSP "CTRL-RSP-" + +/* Event messages with fixed prefix */ +/** Authentication completed successfully and data connection enabled */ +#define WPA_EVENT_CONNECTED "CTRL-EVENT-CONNECTED " +/** Disconnected, data connection is not available */ +#define WPA_EVENT_DISCONNECTED "CTRL-EVENT-DISCONNECTED " +/** wpa_supplicant is exiting */ +#define WPA_EVENT_TERMINATING "CTRL-EVENT-TERMINATING " +/** Password change was completed successfully */ +#define WPA_EVENT_PASSWORD_CHANGED "CTRL-EVENT-PASSWORD-CHANGED " +/** EAP-Request/Notification received */ +#define WPA_EVENT_EAP_NOTIFICATION "CTRL-EVENT-EAP-NOTIFICATION " +/** EAP authentication started (EAP-Request/Identity received) */ +#define WPA_EVENT_EAP_STARTED "CTRL-EVENT-EAP-STARTED " +/** EAP method selected */ +#define WPA_EVENT_EAP_METHOD "CTRL-EVENT-EAP-METHOD " +/** EAP authentication completed successfully */ +#define WPA_EVENT_EAP_SUCCESS "CTRL-EVENT-EAP-SUCCESS " +/** EAP authentication failed (EAP-Failure received) */ +#define WPA_EVENT_EAP_FAILURE "CTRL-EVENT-EAP-FAILURE " + + +/* wpa_supplicant/hostapd control interface access */ + +/** + * wpa_ctrl_open - Open a control interface to wpa_supplicant/hostapd + * @ctrl_path: Path for UNIX domain sockets; ignored if UDP sockets are used. + * Returns: Pointer to abstract control interface data or %NULL on failure + * + * This function is used to open a control interface to wpa_supplicant/hostapd. + * ctrl_path is usually /var/run/wpa_supplicant or /var/run/hostapd. This path + * is configured in wpa_supplicant/hostapd and other programs using the control + * interface need to use matching path configuration. + */ +struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path); + + +/** + * wpa_ctrl_close - Close a control interface to wpa_supplicant/hostapd + * @ctrl: Control interface data from wpa_ctrl_open() + * + * This function is used to close a control interface. + */ +void wpa_ctrl_close(struct wpa_ctrl *ctrl); + + +/** + * wpa_ctrl_request - Send a command to wpa_supplicant/hostapd + * @ctrl: Control interface data from wpa_ctrl_open() + * @cmd: Command; usually, ASCII text, e.g., "PING" + * @cmd_len: Length of the cmd in bytes + * @reply: Buffer for the response + * @reply_len: Reply buffer length + * @msg_cb: Callback function for unsolicited messages or %NULL if not used + * Returns: 0 on success, -1 on error (send or receive failed), -2 on timeout + * + * This function is used to send commands to wpa_supplicant/hostapd. Received + * response will be written to reply and reply_len is set to the actual length + * of the reply. This function will block for up to two seconds while waiting + * for the reply. If unsolicited messages are received, the blocking time may + * be longer. + * + * msg_cb can be used to register a callback function that will be called for + * unsolicited messages received while waiting for the command response. These + * messages may be received if wpa_ctrl_request() is called at the same time as + * wpa_supplicant/hostapd is sending such a message. This can happen only if + * the program has used wpa_ctrl_attach() to register itself as a monitor for + * event messages. Alternatively to msg_cb, programs can register two control + * interface connections and use one of them for commands and the other one for + * receiving event messages, in other words, call wpa_ctrl_attach() only for + * the control interface connection that will be used for event messages. + */ +int wpa_ctrl_request(struct wpa_ctrl *ctrl, const char *cmd, size_t cmd_len, + char *reply, size_t *reply_len, + void (*msg_cb)(char *msg, size_t len)); + + +/** + * wpa_ctrl_attach - Register as an event monitor for the control interface + * @ctrl: Control interface data from wpa_ctrl_open() + * Returns: 0 on success, -1 on failure, -2 on timeout + * + * This function registers the control interface connection as a monitor for + * wpa_supplicant/hostapd events. After a success wpa_ctrl_attach() call, the + * control interface connection starts receiving event messages that can be + * read with wpa_ctrl_recv(). + */ +int wpa_ctrl_attach(struct wpa_ctrl *ctrl); + + +/** + * wpa_ctrl_detach - Unregister event monitor from the control interface + * @ctrl: Control interface data from wpa_ctrl_open() + * Returns: 0 on success, -1 on failure, -2 on timeout + * + * This function unregisters the control interface connection as a monitor for + * wpa_supplicant/hostapd events, i.e., cancels the registration done with + * wpa_ctrl_attach(). + */ +int wpa_ctrl_detach(struct wpa_ctrl *ctrl); + + +/** + * wpa_ctrl_recv - Receive a pending control interface message + * @ctrl: Control interface data from wpa_ctrl_open() + * @reply: Buffer for the message data + * @reply_len: Length of the reply buffer + * Returns: 0 on success, -1 on failure + * + * This function will receive a pending control interface message. This + * function will block if no messages are available. The received response will + * be written to reply and reply_len is set to the actual length of the reply. + * wpa_ctrl_recv() is only used for event messages, i.e., wpa_ctrl_attach() + * must have been used to register the control interface as an event monitor. + */ +int wpa_ctrl_recv(struct wpa_ctrl *ctrl, char *reply, size_t *reply_len); + + +/** + * wpa_ctrl_pending - Check whether there are pending event messages + * @ctrl: Control interface data from wpa_ctrl_open() + * Returns: Non-zero if there are pending messages + * + * This function will check whether there are any pending control interface + * message available to be received with wpa_ctrl_recv(). wpa_ctrl_pending() is + * only used for event messages, i.e., wpa_ctrl_attach() must have been used to + * register the control interface as an event monitor. + */ +int wpa_ctrl_pending(struct wpa_ctrl *ctrl); + + +/** + * wpa_ctrl_get_fd - Get file descriptor used by the control interface + * @ctrl: Control interface data from wpa_ctrl_open() + * Returns: File descriptor used for the connection + * + * This function can be used to get the file descriptor that is used for the + * control interface connection. The returned value can be used, e.g., with + * select() while waiting for multiple events. + * + * The returned file descriptor must not be used directly for sending or + * receiving packets; instead, the library functions wpa_ctrl_request() and + * wpa_ctrl_recv() must be used for this. + */ +int wpa_ctrl_get_fd(struct wpa_ctrl *ctrl); + +#ifdef CONFIG_CTRL_IFACE_UDP +#define WPA_CTRL_IFACE_PORT 9877 +#define WPA_GLOBAL_CTRL_IFACE_PORT 9878 +#endif /* CONFIG_CTRL_IFACE_UDP */ + + +#ifdef __cplusplus +} +#endif + +#endif /* WPA_CTRL_H */ -- 2.11.4.GIT