diff options
author | sam <sam@FreeBSD.org> | 2009-03-02 02:23:47 +0000 |
---|---|---|
committer | sam <sam@FreeBSD.org> | 2009-03-02 02:23:47 +0000 |
commit | 2af41b09fa9d6ff3f4c736a224f545663be143d2 (patch) | |
tree | dafc9df301d15cbf876d2639326ce6bf658e6dea /contrib/wpa/hostapd | |
parent | 5d319a10b1559b57e7042e8c644949049d7c0c56 (diff) | |
parent | ced3a3de988600636bda6479d27de8823307f171 (diff) | |
download | FreeBSD-src-2af41b09fa9d6ff3f4c736a224f545663be143d2.zip FreeBSD-src-2af41b09fa9d6ff3f4c736a224f545663be143d2.tar.gz |
connect vendor wpa area to contrib
Diffstat (limited to 'contrib/wpa/hostapd')
82 files changed, 30500 insertions, 0 deletions
diff --git a/contrib/wpa/hostapd/.gitignore b/contrib/wpa/hostapd/.gitignore new file mode 100644 index 0000000..6dd2c2f --- /dev/null +++ b/contrib/wpa/hostapd/.gitignore @@ -0,0 +1,7 @@ +*.d +.config +driver_conf.c +hostapd +hostapd_cli +hlr_auc_gw +nt_password_hash diff --git a/contrib/wpa/hostapd/ChangeLog b/contrib/wpa/hostapd/ChangeLog new file mode 100644 index 0000000..46f0852 --- /dev/null +++ b/contrib/wpa/hostapd/ChangeLog @@ -0,0 +1,565 @@ +ChangeLog for hostapd + +2009-02-15 - v0.6.8 + * increased hostapd_cli ping interval to 5 seconds and made this + configurable with a new command line options (-G<seconds>) + * driver_nl80211: use Linux socket filter to improve performance + * added support for external Registrars with WPS (UPnP transport) + +2009-01-06 - v0.6.7 + * added support for Wi-Fi Protected Setup (WPS) + (hostapd can now be configured to act as an integrated WPS Registrar + and provision credentials for WPS Enrollees using PIN and PBC + methods; external wireless Registrar can configure the AP, but + external WLAN Manager Registrars are not supported); WPS support can + be enabled by adding CONFIG_WPS=y into .config and setting the + runtime configuration variables in hostapd.conf (see WPS section in + the example configuration file); new hostapd_cli commands wps_pin and + wps_pbc are used to configure WPS negotiation; see README-WPS for + more details + * added IEEE 802.11n HT capability configuration (ht_capab) + * added support for generating Country IE based on nl80211 regulatory + information (added if ieee80211d=1 in configuration) + * fixed WEP authentication (both Open System and Shared Key) with + mac80211 + * added support for EAP-AKA' (draft-arkko-eap-aka-kdf) + * added support for using driver_test over UDP socket + * changed EAP-GPSK to use the IANA assigned EAP method type 51 + * updated management frame protection to use IEEE 802.11w/D7.0 + * fixed retransmission of EAP requests if no response is received + +2008-11-23 - v0.6.6 + * added a new configuration option, wpa_ptk_rekey, that can be used to + enforce frequent PTK rekeying, e.g., to mitigate some attacks against + TKIP deficiencies + * updated OpenSSL code for EAP-FAST to use an updated version of the + session ticket overriding API that was included into the upstream + OpenSSL 0.9.9 tree on 2008-11-15 (no additional OpenSSL patch is + needed with that version anymore) + * changed channel flags configuration to read the information from + the driver (e.g., via driver_nl80211 when using mac80211) instead of + using hostapd as the source of the regulatory information (i.e., + information from CRDA is now used with mac80211); this allows 5 GHz + channels to be used with hostapd (if allowed in the current + regulatory domain) + * fixed EAP-TLS message processing for the last TLS message if it is + large enough to require fragmentation (e.g., if a large Session + Ticket data is included) + * fixed listen interval configuration for nl80211 drivers + +2008-11-01 - v0.6.5 + * added support for SHA-256 as X.509 certificate digest when using the + internal X.509/TLSv1 implementation + * fixed EAP-FAST PAC-Opaque padding (0.6.4 broke this for some peer + identity lengths) + * fixed internal TLSv1 implementation for abbreviated handshake (used + by EAP-FAST server) + * added support for setting VLAN ID for STAs based on local MAC ACL + (accept_mac_file) as an alternative for RADIUS server-based + configuration + * updated management frame protection to use IEEE 802.11w/D6.0 + (adds a new association ping to protect against unauthenticated + authenticate or (re)associate request frames dropping association) + * added support for using SHA256-based stronger key derivation for WPA2 + (IEEE 802.11w) + * added new "driver wrapper" for RADIUS-only configuration + (driver=none in hostapd.conf; CONFIG_DRIVER_NONE=y in .config) + * fixed WPA/RSN IE validation to verify that the proto (WPA vs. WPA2) + is enabled in configuration + * changed EAP-FAST configuration to use separate fields for A-ID and + A-ID-Info (eap_fast_a_id_info) to allow A-ID to be set to a fixed + 16-octet len binary value for better interoperability with some peer + implementations; eap_fast_a_id is now configured as a hex string + * driver_nl80211: Updated to match the current Linux mac80211 AP mode + configuration (wireless-testing.git and Linux kernel releases + starting from 2.6.29) + +2008-08-10 - v0.6.4 + * added peer identity into EAP-FAST PAC-Opaque and skip Phase 2 + Identity Request if identity is already known + * added support for EAP Sequences in EAP-FAST Phase 2 + * added support for EAP-TNC (Trusted Network Connect) + (this version implements the EAP-TNC method and EAP-TTLS/EAP-FAST + changes needed to run two methods in sequence (IF-T) and the IF-IMV + and IF-TNCCS interfaces from TNCS) + * added support for optional cryptobinding with PEAPv0 + * added fragmentation support for EAP-TNC + * added support for fragmenting EAP-TTLS/PEAP/FAST Phase 2 (tunneled) + data + * added support for opportunistic key caching (OKC) + +2008-02-22 - v0.6.3 + * fixed Reassociation Response callback processing when using internal + MLME (driver_{hostap,nl80211,test}.c) + * updated FT support to use the latest draft, IEEE 802.11r/D9.0 + * copy optional Proxy-State attributes into RADIUS response when acting + as a RADIUS authentication server + * fixed EAPOL state machine to handle a case in which no response is + received from the RADIUS authentication server; previous version + could have triggered a crash in some cases after a timeout + * fixed EAP-SIM/AKA realm processing to allow decorated usernames to + be used + * added a workaround for EAP-SIM/AKA peers that include incorrect null + termination in the username + * fixed EAP-SIM/AKA protected result indication to include AT_COUNTER + attribute in notification messages only when using fast + reauthentication + * fixed EAP-SIM Start response processing for fast reauthentication + case + * added support for pending EAP processing in EAP-{PEAP,TTLS,FAST} + phase 2 to allow EAP-SIM and EAP-AKA to be used as the Phase 2 method + +2008-01-01 - v0.6.2 + * fixed EAP-SIM and EAP-AKA message parser to validate attribute + lengths properly to avoid potential crash caused by invalid messages + * added data structure for storing allocated buffers (struct wpabuf); + this does not affect hostapd usage, but many of the APIs changed + and various interfaces (e.g., EAP) is not compatible with old + versions + * added support for protecting EAP-AKA/Identity messages with + AT_CHECKCODE (optional feature in RFC 4187) + * added support for protected result indication with AT_RESULT_IND for + EAP-SIM and EAP-AKA (eap_sim_aka_result_ind=1) + * added support for configuring EAP-TTLS phase 2 non-EAP methods in + EAP server configuration; previously all four were enabled for every + phase 2 user, now all four are disabled by default and need to be + enabled with new method names TTLS-PAP, TTLS-CHAP, TTLS-MSCHAP, + TTLS-MSCHAPV2 + * removed old debug printing mechanism and the related 'debug' + parameter in the configuration file; debug verbosity is now set with + -d (or -dd) command line arguments + * added support for EAP-IKEv2 (draft-tschofenig-eap-ikev2-15.txt); + only shared key/password authentication is supported in this version + +2007-11-24 - v0.6.1 + * added experimental, integrated TLSv1 server implementation with the + needed X.509/ASN.1/RSA/bignum processing (this can be enabled by + setting CONFIG_TLS=internal and CONFIG_INTERNAL_LIBTOMMATH=y in + .config); this can be useful, e.g., if the target system does not + have a suitable TLS library and a minimal code size is required + * added support for EAP-FAST server method to the integrated EAP + server + * updated EAP Generalized Pre-Shared Key (EAP-GPSK) to use the latest + draft (draft-ietf-emu-eap-gpsk-07.txt) + * added a new configuration parameter, rsn_pairwise, to allow different + pairwise cipher suites to be enabled for WPA and RSN/WPA2 + (note: if wpa_pairwise differs from rsn_pairwise, the driver will + either need to support this or will have to use the WPA/RSN IEs from + hostapd; currently, the included madwifi and bsd driver interfaces do + not have support for this) + * updated FT support to use the latest draft, IEEE 802.11r/D8.0 + +2007-05-28 - v0.6.0 + * added experimental IEEE 802.11r/D6.0 support + * updated EAP-SAKE to RFC 4763 and the IANA-allocated EAP type 48 + * updated EAP-PSK to use the IANA-allocated EAP type 47 + * fixed EAP-PSK bit ordering of the Flags field + * fixed configuration reloading (SIGHUP) to re-initialize WPA PSKs + by reading wpa_psk_file [Bug 181] + * fixed EAP-TTLS AVP parser processing for too short AVP lengths + * fixed IPv6 connection to RADIUS accounting server + * updated EAP Generalized Pre-Shared Key (EAP-GPSK) to use the latest + draft (draft-ietf-emu-eap-gpsk-04.txt) + * hlr_auc_gw: read GSM triplet file into memory and rotate through the + entries instead of only using the same three triplets every time + (this does not work properly with tests using multiple clients, but + provides bit better triplet data for testing a single client; anyway, + if a better quality triplets are needed, GSM-Milenage should be used + instead of hardcoded triplet file) + * fixed EAP-MSCHAPv2 server to use a space between S and M parameters + in Success Request [Bug 203] + * added support for sending EAP-AKA Notifications in error cases + * updated to use IEEE 802.11w/D2.0 for management frame protection + (still experimental) + * RADIUS server: added support for processing duplicate messages + (retransmissions from RADIUS client) by replying with the previous + reply + +2006-11-24 - v0.5.6 + * added support for configuring and controlling multiple BSSes per + radio interface (bss=<ifname> in hostapd.conf); this is only + available with Devicescape and test driver interfaces + * fixed PMKSA cache update in the end of successful RSN + pre-authentication + * added support for dynamic VLAN configuration (i.e., selecting VLAN-ID + for each STA based on RADIUS Access-Accept attributes); this requires + VLAN support from the kernel driver/802.11 stack and this is + currently only available with Devicescape and test driver interfaces + * driver_madwifi: fixed configuration of unencrypted modes (plaintext + and IEEE 802.1X without WEP) + * removed STAKey handshake since PeerKey handshake has replaced it in + IEEE 802.11ma and there are no known deployments of STAKey + * updated EAP Generalized Pre-Shared Key (EAP-GPSK) to use the latest + draft (draft-ietf-emu-eap-gpsk-01.txt) + * added preliminary implementation of IEEE 802.11w/D1.0 (management + frame protection) + (Note: this requires driver support to work properly.) + (Note2: IEEE 802.11w is an unapproved draft and subject to change.) + * hlr_auc_gw: added support for GSM-Milenage (for EAP-SIM) + * hlr_auc_gw: added support for reading per-IMSI Milenage keys and + parameters from a text file to make it possible to implement proper + GSM/UMTS authentication server for multiple SIM/USIM cards using + EAP-SIM/EAP-AKA + * fixed session timeout processing with drivers that do not use + ieee802_11.c (e.g., madwifi) + +2006-08-27 - v0.5.5 + * added 'hostapd_cli new_sta <addr>' command for adding a new STA into + hostapd (e.g., to initialize wired network authentication based on an + external signal) + * fixed hostapd to add PMKID KDE into 4-Way Handshake Message 1 when + using WPA2 even if PMKSA caching is not used + * added -P<pid file> argument for hostapd to write the current process + id into a file + * added support for RADIUS Authentication Server MIB (RFC 2619) + +2006-06-20 - v0.5.4 + * fixed nt_password_hash build [Bug 144] + * added PeerKey handshake implementation for IEEE 802.11e + direct link setup (DLS) to replace STAKey handshake + * added support for EAP Generalized Pre-Shared Key (EAP-GPSK, + draft-clancy-emu-eap-shared-secret-00.txt) + * fixed a segmentation fault when RSN pre-authentication was completed + successfully [Bug 152] + +2006-04-27 - v0.5.3 + * do not build nt_password_hash and hlr_auc_gw by default to avoid + requiring a TLS library for a successful build; these programs can be + build with 'make nt_password_hash' and 'make hlr_auc_gw' + * added a new configuration option, eapol_version, that can be used to + set EAPOL version to 1 (default is 2) to work around broken client + implementations that drop EAPOL frames which use version number 2 + [Bug 89] + * added support for EAP-SAKE (no EAP method number allocated yet, so + this is using the same experimental type 255 as EAP-PSK) + * fixed EAP-MSCHAPv2 message length validation + +2006-03-19 - v0.5.2 + * fixed stdarg use in hostapd_logger(): if both stdout and syslog + logging was enabled, hostapd could trigger a segmentation fault in + vsyslog on some CPU -- C library combinations + * moved HLR/AuC gateway implementation for EAP-SIM/AKA into an external + program to make it easier to use for implementing real SS7 gateway; + eap_sim_db is not anymore used as a file name for GSM authentication + triplets; instead, it is path to UNIX domain socket that will be used + to communicate with the external gateway program (e.g., hlr_auc_gw) + * added example HLR/AuC gateway implementation, hlr_auc_gw, that uses + local information (GSM authentication triplets from a text file and + hardcoded AKA authentication data); this can be used to test EAP-SIM + and EAP-AKA + * added Milenage algorithm (example 3GPP AKA algorithm) to hlr_auc_gw + to make it possible to test EAP-AKA with real USIM cards (this is + disabled by default; define AKA_USE_MILENAGE when building hlr_auc_gw + to enable this) + * driver_madwifi: added support for getting station RSN IE from + madwifi-ng svn r1453 and newer; this fixes RSN that was apparently + broken with earlier change (r1357) in the driver + * changed EAP method registration to use a dynamic list of methods + instead of a static list generated at build time + * fixed WPA message 3/4 not to encrypt Key Data field (WPA IE) + [Bug 125] + * added ap_max_inactivity configuration parameter + +2006-01-29 - v0.5.1 + * driver_test: added better support for multiple APs and STAs by using + a directory with sockets that include MAC address for each device in + the name (test_socket=DIR:/tmp/test) + * added support for EAP expanded type (vendor specific EAP methods) + +2005-12-18 - v0.5.0 (beginning of 0.5.x development releases) + * added experimental STAKey handshake implementation for IEEE 802.11e + direct link setup (DLS); note: this is disabled by default in both + build and runtime configuration (can be enabled with CONFIG_STAKEY=y + and stakey=1) + * added support for EAP methods to use callbacks to external programs + by buffering a pending request and processing it after the EAP method + is ready to continue + * improved EAP-SIM database interface to allow external request to GSM + HLR/AuC without blocking hostapd process + * added support for using EAP-SIM pseudonyms and fast re-authentication + * added support for EAP-AKA in the integrated EAP authenticator + * added support for matching EAP identity prefixes (e.g., "1"*) in EAP + user database to allow EAP-SIM/AKA selection without extra roundtrip + for EAP-Nak negotiation + * added support for storing EAP user password as NtPasswordHash instead + of plaintext password when using MSCHAP or MSCHAPv2 for + authentication (hash:<16-octet hex value>); added nt_password_hash + tool for hashing password to generate NtPasswordHash + +2005-11-20 - v0.4.7 (beginning of 0.4.x stable releases) + * driver_wired: fixed EAPOL sending to optionally use PAE group address + as the destination instead of supplicant MAC address; this is + disabled by default, but should be enabled with use_pae_group_addr=1 + in configuration file if the wired interface is used by only one + device at the time (common switch configuration) + * driver_madwifi: configure driver to use TKIP countermeasures in order + to get correct behavior (IEEE 802.11 association failing; previously, + association succeeded, but hostpad forced disassociation immediately) + * driver_madwifi: added support for madwifi-ng + +2005-10-27 - v0.4.6 + * added support for replacing user identity from EAP with RADIUS + User-Name attribute from Access-Accept message, if that is included, + for the RADIUS accounting messages (e.g., for EAP-PEAP/TTLS to get + tunneled identity into accounting messages when the RADIUS server + does not support better way of doing this with Class attribute) + * driver_madwifi: fixed EAPOL packet receive for configuration where + ath# is part of a bridge interface + * added a configuration file and log analyzer script for logwatch + * fixed EAPOL state machine step function to process all state + transitions before processing new events; this resolves a race + condition in which EAPOL-Start message could trigger hostapd to send + two EAP-Response/Identity frames to the authentication server + +2005-09-25 - v0.4.5 + * added client CA list to the TLS certificate request in order to make + it easier for the client to select which certificate to use + * added experimental support for EAP-PSK + * added support for WE-19 (hostap, madwifi) + +2005-08-21 - v0.4.4 + * fixed build without CONFIG_RSN_PREAUTH + * fixed FreeBSD build + +2005-06-26 - v0.4.3 + * fixed PMKSA caching to copy User-Name and Class attributes so that + RADIUS accounting gets correct information + * start RADIUS accounting only after successful completion of WPA + 4-Way Handshake if WPA-PSK is used + * fixed PMKSA caching for the case where STA (re)associates without + first disassociating + +2005-06-12 - v0.4.2 + * EAP-PAX is now registered as EAP type 46 + * fixed EAP-PAX MAC calculation + * fixed EAP-PAX CK and ICK key derivation + * renamed eap_authenticator configuration variable to eap_server to + better match with RFC 3748 (EAP) terminology + * driver_test: added support for testing hostapd with wpa_supplicant + by using test driver interface without any kernel drivers or network + cards + +2005-05-22 - v0.4.1 + * fixed RADIUS server initialization when only auth or acct server + is configured and the other one is left empty + * driver_madwifi: added support for RADIUS accounting + * driver_madwifi: added preliminary support for compiling against 'BSD' + branch of madwifi CVS tree + * driver_madwifi: fixed pairwise key removal to allow WPA reauth + without disassociation + * added support for reading additional certificates from PKCS#12 files + and adding them to the certificate chain + * fixed RADIUS Class attribute processing to only use Access-Accept + packets to update Class; previously, other RADIUS authentication + packets could have cleared Class attribute + * added support for more than one Class attribute in RADIUS packets + * added support for verifying certificate revocation list (CRL) when + using integrated EAP authenticator for EAP-TLS; new hostapd.conf + options 'check_crl'; CRL must be included in the ca_cert file for now + +2005-04-25 - v0.4.0 (beginning of 0.4.x development releases) + * added support for including network information into + EAP-Request/Identity message (ASCII-0 (nul) in eap_message) + (e.g., to implement draft-adrange-eap-network-discovery-07.txt) + * fixed a bug which caused some RSN pre-authentication cases to use + freed memory and potentially crash hostapd + * fixed private key loading for cases where passphrase is not set + * added support for sending TLS alerts and aborting authentication + when receiving a TLS alert + * fixed WPA2 to add PMKSA cache entry when using integrated EAP + authenticator + * fixed PMKSA caching (EAP authentication was not skipped correctly + with the new state machine changes from IEEE 802.1X draft) + * added support for RADIUS over IPv6; own_ip_addr, auth_server_addr, + and acct_server_addr can now be IPv6 addresses (CONFIG_IPV6=y needs + to be added to .config to include IPv6 support); for RADIUS server, + radius_server_ipv6=1 needs to be set in hostapd.conf and addresses + in RADIUS clients file can then use IPv6 format + * added experimental support for EAP-PAX + * replaced hostapd control interface library (hostapd_ctrl.[ch]) with + the same implementation that wpa_supplicant is using (wpa_ctrl.[ch]) + +2005-02-12 - v0.3.7 (beginning of 0.3.x stable releases) + +2005-01-23 - v0.3.5 + * added support for configuring a forced PEAP version based on the + Phase 1 identity + * fixed PEAPv1 to use tunneled EAP-Success/Failure instead of EAP-TLV + to terminate authentication + * fixed EAP identifier duplicate processing with the new IEEE 802.1X + draft + * clear accounting data in the driver when starting a new accounting + session + * driver_madwifi: filter wireless events based on ifindex to allow more + than one network interface to be used + * fixed WPA message 2/4 processing not to cancel timeout for TimeoutEvt + setting if the packet does not pass MIC verification (e.g., due to + incorrect PSK); previously, message 1/4 was not tried again if an + invalid message 2/4 was received + * fixed reconfiguration of RADIUS client retransmission timer when + adding a new message to the pending list; previously, timer was not + updated at this point and if there was a pending message with long + time for the next retry, the new message needed to wait that long for + its first retry, too + +2005-01-09 - v0.3.4 + * added support for configuring multiple allowed EAP types for Phase 2 + authentication (EAP-PEAP, EAP-TTLS) + * fixed EAPOL-Start processing to trigger WPA reauthentication + (previously, only EAPOL authentication was done) + +2005-01-02 - v0.3.3 + * added support for EAP-PEAP in the integrated EAP authenticator + * added support for EAP-GTC in the integrated EAP authenticator + * added support for configuring list of EAP methods for Phase 1 so that + the integrated EAP authenticator can, e.g., use the wildcard entry + for EAP-TLS and EAP-PEAP + * added support for EAP-TTLS in the integrated EAP authenticator + * added support for EAP-SIM in the integrated EAP authenticator + * added support for using hostapd as a RADIUS authentication server + with the integrated EAP authenticator taking care of EAP + authentication (new hostapd.conf options: radius_server_clients and + radius_server_auth_port); this is not included in default build; use + CONFIG_RADIUS_SERVER=y in .config to include + +2004-12-19 - v0.3.2 + * removed 'daemonize' configuration file option since it has not really + been used at all for more than year + * driver_madwifi: fixed group key setup and added get_ssid method + * added support for EAP-MSCHAPv2 in the integrated EAP authenticator + +2004-12-12 - v0.3.1 + * added support for integrated EAP-TLS authentication (new hostapd.conf + variables: ca_cert, server_cert, private_key, private_key_passwd); + this enabled dynamic keying (WPA2/WPA/IEEE 802.1X/WEP) without + external RADIUS server + * added support for reading PKCS#12 (PFX) files (as a replacement for + PEM/DER) to get certificate and private key (CONFIG_PKCS12) + +2004-12-05 - v0.3.0 (beginning of 0.3.x development releases) + * added support for Acct-{Input,Output}-Gigawords + * added support for Event-Timestamp (in RADIUS Accounting-Requests) + * added support for RADIUS Authentication Client MIB (RFC2618) + * added support for RADIUS Accounting Client MIB (RFC2620) + * made EAP re-authentication period configurable (eap_reauth_period) + * fixed EAPOL reauthentication to trigger WPA/WPA2 reauthentication + * fixed EAPOL state machine to stop if STA is removed during + eapol_sm_step(); this fixes at least one segfault triggering bug with + IEEE 802.11i pre-authentication + * added support for multiple WPA pre-shared keys (e.g., one for each + client MAC address or keys shared by a group of clients); + new hostapd.conf field wpa_psk_file for setting path to a text file + containing PSKs, see hostapd.wpa_psk for an example + * added support for multiple driver interfaces to allow hostapd to be + used with other drivers + * added wired authenticator driver interface (driver=wired in + hostapd.conf, see wired.conf for example configuration) + * added madwifi driver interface (driver=madwifi in hostapd.conf, see + madwifi.conf for example configuration; Note: include files from + madwifi project is needed for building and a configuration file, + .config, needs to be created in hostapd directory with + CONFIG_DRIVER_MADWIFI=y to include this driver interface in hostapd + build) + * fixed an alignment issue that could cause SHA-1 to fail on some + platforms (e.g., Intel ixp425 with a compiler that does not 32-bit + align variables) + * fixed RADIUS reconnection after an error in sending interim + accounting packets + * added hostapd control interface for external programs and an example + CLI, hostapd_cli (like wpa_cli for wpa_supplicant) + * started adding dot11, dot1x, radius MIBs ('hostapd_cli mib', + 'hostapd_cli sta <addr>') + * finished update from IEEE 802.1X-2001 to IEEE 802.1X-REV (now d11) + * added support for strict GTK rekeying (wpa_strict_rekey in + hostapd.conf) + * updated IAPP to use UDP port 3517 and multicast address 224.0.1.178 + (instead of broadcast) for IAPP ADD-notify (moved from draft 3 to + IEEE 802.11F-2003) + * added Prism54 driver interface (driver=prism54 in hostapd.conf; + note: .config needs to be created in hostapd directory with + CONFIG_DRIVER_PRISM54=y to include this driver interface in hostapd + build) + * dual-licensed hostapd (GPLv2 and BSD licenses) + * fixed RADIUS accounting to generate a new session id for cases where + a station reassociates without first being complete deauthenticated + * fixed STA disassociation handler to mark next timeout state to + deauthenticate the station, i.e., skip long wait for inactivity poll + and extra disassociation, if the STA disassociates without + deauthenticating + * added integrated EAP authenticator that can be used instead of + external RADIUS authentication server; currently, only EAP-MD5 is + supported, so this cannot yet be used for key distribution; the EAP + method interface is generic, though, so adding new EAP methods should + be straightforward; new hostapd.conf variables: 'eap_authenticator' + and 'eap_user_file'; this obsoletes "minimal authentication server" + ('minimal_eap' in hostapd.conf) which is now removed + * added support for FreeBSD and driver interface for the BSD net80211 + layer (driver=bsd in hostapd.conf and CONFIG_DRIVER_BSD=y in + .config); please note that some of the required kernel mods have not + yet been committed + +2004-07-17 - v0.2.4 (beginning of 0.2.x stable releases) + * fixed some accounting cases where Accounting-Start was sent when + IEEE 802.1X port was being deauthorized + +2004-06-20 - v0.2.3 + * modified RADIUS client to re-connect the socket in case of certain + error codes that are generated when a network interface state is + changes (e.g., when IP address changes or the interface is set UP) + * fixed couple of cases where EAPOL state for a station was freed + twice causing a segfault for hostapd + * fixed couple of bugs in processing WPA deauthentication (freed data + was used) + +2004-05-31 - v0.2.2 + * fixed WPA/WPA2 group rekeying to use key index correctly (GN/GM) + * fixed group rekeying to send zero TSC in EAPOL-Key messages to fix + cases where STAs dropped multicast frames as replay attacks + * added support for copying RADIUS Attribute 'Class' from + authentication messages into accounting messages + * send canned EAP failure if RADIUS server sends Access-Reject without + EAP message (previously, Supplicant was not notified in this case) + * fixed mixed WPA-PSK and WPA-EAP mode to work with WPA-PSK (i.e., do + not start EAPOL state machines if the STA selected to use WPA-PSK) + +2004-05-06 - v0.2.1 + * added WPA and IEEE 802.11i/RSN (WPA2) Authenticator functionality + - based on IEEE 802.11i/D10.0 but modified to interoperate with WPA + (i.e., IEEE 802.11i/D3.0) + - supports WPA-only, RSN-only, and mixed WPA/RSN mode + - both WPA-PSK and WPA-RADIUS/EAP are supported + - PMKSA caching and pre-authentication + - new hostapd.conf variables: wpa, wpa_psk, wpa_passphrase, + wpa_key_mgmt, wpa_pairwise, wpa_group_rekey, wpa_gmk_rekey, + rsn_preauth, rsn_preauth_interfaces + * fixed interim accounting to remove any pending accounting messages + to the STA before sending a new one + +2004-02-15 - v0.2.0 + * added support for Acct-Interim-Interval: + - draft-ietf-radius-acct-interim-01.txt + - use Acct-Interim-Interval attribute from Access-Accept if local + 'radius_acct_interim_interval' is not set + - allow different update intervals for each STA + * fixed event loop to call signal handlers only after returning from + the real signal handler + * reset sta->timeout_next after successful association to make sure + that the previously registered inactivity timer will not remove the + STA immediately (e.g., if STA deauthenticates and re-associates + before the timer is triggered). + * added new hostapd.conf variable, nas_identifier, that can be used to + add an optional RADIUS Attribute, NAS-Identifier, into authentication + and accounting messages + * added support for Accounting-On and Accounting-Off messages + * fixed accounting session handling to send Accounting-Start only once + per session and not to send Accounting-Stop if the session was not + initialized properly + * fixed Accounting-Stop statistics in cases where the message was + previously sent after the kernel entry for the STA (and/or IEEE + 802.1X data) was removed + + +Note: + +Older changes up to and including v0.1.0 are included in the ChangeLog +of the Host AP driver. diff --git a/contrib/wpa/hostapd/README b/contrib/wpa/hostapd/README new file mode 100644 index 0000000..eb9aa48 --- /dev/null +++ b/contrib/wpa/hostapd/README @@ -0,0 +1,390 @@ +hostapd - user space IEEE 802.11 AP and IEEE 802.1X/WPA/WPA2/EAP + Authenticator and RADIUS authentication server +================================================================ + +Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi> and contributors +All Rights Reserved. + +This program is dual-licensed under both the GPL version 2 and BSD +license. Either license may be used at your option. + + + +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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +(this copy of the license is in COPYING file) + + +Alternatively, this software may be distributed, used, and modified +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<path>) + + Prism54 driver for Intersil/Conexant Prism GT/Duette/Indigo + (http://www.prism54.org/) + + mac80211-based drivers that support AP mode (with driver=nl80211). + This includes drivers for Atheros (ath9k) and Broadcom (b43) + chipsets. + + 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. + +The normal configuration of the Authenticator would use an external +Authentication Server. hostapd supports RADIUS encapsulation of EAP +packets, so the Authentication Server should be a RADIUS server, like +FreeRADIUS (http://www.freeradius.org/). The Authenticator in hostapd +relays the frames between the Supplicant and the Authentication +Server. It also controls the PAE functionality in the kernel driver by +controlling virtual port authorization, i.e., station-AP +connection, based on the IEEE 802.1X state. + +When a station would like to use the services of an access point, it +will first perform IEEE 802.11 authentication. This is normally done +with open systems authentication, so there is no security. After +this, IEEE 802.11 association is performed. If IEEE 802.1X is +configured to be used, the virtual port for the station is set in +Unauthorized state and only IEEE 802.1X frames are accepted at this +point. The Authenticator will then ask the Supplicant to authenticate +with the Authentication Server. After this is completed successfully, +the virtual port is set to Authorized state and frames from and to the +station are accepted. + +Host AP configuration for IEEE 802.1X +------------------------------------- + +The user space daemon has its own configuration file that can be used to +define AP options. Distribution package contains an example +configuration file (hostapd/hostapd.conf) that can be used as a basis +for configuration. It includes examples of all supported configuration +options and short description of each option. hostapd should be started +with full path to the configuration file as the command line argument, +e.g., './hostapd /etc/hostapd.conf'. If you have more that one wireless +LAN card, you can use one hostapd process for multiple interfaces by +giving a list of configuration files (one per interface) in the command +line. + +hostapd includes a minimal co-located IEEE 802.1X server which can be +used to test IEEE 802.1X authentication. However, it should not be +used in normal use since it does not provide any security. This can be +configured by setting ieee8021x and minimal_eap options in the +configuration file. + +An external Authentication Server (RADIUS) is configured with +auth_server_{addr,port,shared_secret} options. In addition, +ieee8021x and own_ip_addr must be set for this mode. With such +configuration, the co-located Authentication Server is not used and EAP +frames will be relayed using EAPOL between the Supplicant and the +Authenticator and RADIUS encapsulation between the Authenticator and +the Authentication Server. Other than this, the functionality is similar +to the case with the co-located Authentication Server. + +Authentication Server and Supplicant +------------------------------------ + +Any RADIUS server supporting EAP should be usable as an IEEE 802.1X +Authentication Server with hostapd Authenticator. FreeRADIUS +(http://www.freeradius.org/) has been successfully tested with hostapd +Authenticator and both Xsupplicant (http://www.open1x.org) and Windows +XP Supplicants. EAP/TLS was used with Xsupplicant and +EAP/MD5-Challenge with Windows XP. + +http://www.missl.cs.umd.edu/wireless/eaptls/ has useful information +about using EAP/TLS with FreeRADIUS and Xsupplicant (just replace +Cisco access point with Host AP driver, hostapd daemon, and a Prism2 +card ;-). http://www.freeradius.org/doc/EAP-MD5.html has information +about using EAP/MD5 with FreeRADIUS, including instructions for WinXP +configuration. http://www.denobula.com/EAPTLS.pdf has a HOWTO on +EAP/TLS use with WinXP Supplicant. + +Automatic WEP key configuration +------------------------------- + +EAP/TLS generates a session key that can be used to send WEP keys from +an AP to authenticated stations. The Authenticator in hostapd can be +configured to automatically select a random default/broadcast key +(shared by all authenticated stations) with wep_key_len_broadcast +option (5 for 40-bit WEP or 13 for 104-bit WEP). In addition, +wep_key_len_unicast option can be used to configure individual unicast +keys for stations. This requires support for individual keys in the +station driver. + +WEP keys can be automatically updated by configuring rekeying. This +will improve security of the network since same WEP key will only be +used for a limited period of time. wep_rekey_period option sets the +interval for rekeying in seconds. + + +WPA/WPA2 +======== + +Features +-------- + +Supported WPA/IEEE 802.11i features: +- WPA-PSK ("WPA-Personal") +- WPA with EAP (e.g., with RADIUS authentication server) ("WPA-Enterprise") +- key management for CCMP, TKIP, WEP104, WEP40 +- RSN/WPA2 (IEEE 802.11i), including PMKSA caching and pre-authentication + +WPA +--- + +The original security mechanism of IEEE 802.11 standard was not +designed to be strong and has proved to be insufficient for most +networks that require some kind of security. Task group I (Security) +of IEEE 802.11 working group (http://www.ieee802.org/11/) has worked +to address the flaws of the base standard and has in practice +completed its work in May 2004. The IEEE 802.11i amendment to the IEEE +802.11 standard was approved in June 2004 and this amendment is likely +to be published in July 2004. + +Wi-Fi Alliance (http://www.wi-fi.org/) used a draft version of the +IEEE 802.11i work (draft 3.0) to define a subset of the security +enhancements that can be implemented with existing wlan hardware. This +is called Wi-Fi Protected Access<TM> (WPA). This has now become a +mandatory component of interoperability testing and certification done +by Wi-Fi Alliance. Wi-Fi provides information about WPA at its web +site (http://www.wi-fi.org/OpenSection/protected_access.asp). + +IEEE 802.11 standard defined wired equivalent privacy (WEP) algorithm +for protecting wireless networks. WEP uses RC4 with 40-bit keys, +24-bit initialization vector (IV), and CRC32 to protect against packet +forgery. All these choices have proven to be insufficient: key space is +too small against current attacks, RC4 key scheduling is insufficient +(beginning of the pseudorandom stream should be skipped), IV space is +too small and IV reuse makes attacks easier, there is no replay +protection, and non-keyed authentication does not protect against bit +flipping packet data. + +WPA is an intermediate solution for the security issues. It uses +Temporal Key Integrity Protocol (TKIP) to replace WEP. TKIP is a +compromise on strong security and possibility to use existing +hardware. It still uses RC4 for the encryption like WEP, but with +per-packet RC4 keys. In addition, it implements replay protection, +keyed packet authentication mechanism (Michael MIC). + +Keys can be managed using two different mechanisms. WPA can either use +an external authentication server (e.g., RADIUS) and EAP just like +IEEE 802.1X is using or pre-shared keys without need for additional +servers. Wi-Fi calls these "WPA-Enterprise" and "WPA-Personal", +respectively. Both mechanisms will generate a master session key for +the Authenticator (AP) and Supplicant (client station). + +WPA implements a new key handshake (4-Way Handshake and Group Key +Handshake) for generating and exchanging data encryption keys between +the Authenticator and Supplicant. This handshake is also used to +verify that both Authenticator and Supplicant know the master session +key. These handshakes are identical regardless of the selected key +management mechanism (only the method for generating master session +key changes). + + +IEEE 802.11i / WPA2 +------------------- + +The design for parts of IEEE 802.11i that were not included in WPA has +finished (May 2004) and this amendment to IEEE 802.11 was approved in +June 2004. Wi-Fi Alliance is using the final IEEE 802.11i as a new +version of WPA called WPA2. This includes, e.g., support for more +robust encryption algorithm (CCMP: AES in Counter mode with CBC-MAC) +to replace TKIP and optimizations for handoff (reduced number of +messages in initial key handshake, pre-authentication, and PMKSA caching). + +Some wireless LAN vendors are already providing support for CCMP in +their WPA products. There is no "official" interoperability +certification for CCMP and/or mixed modes using both TKIP and CCMP, so +some interoperability issues can be expected even though many +combinations seem to be working with equipment from different vendors. +Testing for WPA2 is likely to start during the second half of 2004. + +hostapd configuration for WPA/WPA2 +---------------------------------- + +TODO + +# Enable WPA. Setting this variable configures the AP to require WPA (either +# WPA-PSK or WPA-RADIUS/EAP based on other configuration). For WPA-PSK, either +# wpa_psk or wpa_passphrase must be set and wpa_key_mgmt must include WPA-PSK. +# For WPA-RADIUS/EAP, ieee8021x must be set (but without dynamic WEP keys), +# RADIUS authentication server must be configured, and WPA-EAP must be included +# in wpa_key_mgmt. +# This field is a bit field that can be used to enable WPA (IEEE 802.11i/D3.0) +# and/or WPA2 (full IEEE 802.11i/RSN): +# bit0 = WPA +# bit1 = IEEE 802.11i/RSN (WPA2) +#wpa=1 + +# WPA pre-shared keys for WPA-PSK. This can be either entered as a 256-bit +# secret in hex format (64 hex digits), wpa_psk, or as an ASCII passphrase +# (8..63 characters) that will be converted to PSK. This conversion uses SSID +# so the PSK changes when ASCII passphrase is used and the SSID is changed. +#wpa_psk=0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef +#wpa_passphrase=secret passphrase + +# Set of accepted key management algorithms (WPA-PSK, WPA-EAP, or both). The +# entries are separated with a space. +#wpa_key_mgmt=WPA-PSK WPA-EAP + +# Set of accepted cipher suites (encryption algorithms) for pairwise keys +# (unicast packets). This is a space separated list of algorithms: +# CCMP = AES in Counter mode with CBC-MAC [RFC 3610, IEEE 802.11i] +# TKIP = Temporal Key Integrity Protocol [IEEE 802.11i] +# Group cipher suite (encryption algorithm for broadcast and multicast frames) +# is automatically selected based on this configuration. If only CCMP is +# allowed as the pairwise cipher, group cipher will also be CCMP. Otherwise, +# TKIP will be used as the group cipher. +#wpa_pairwise=TKIP CCMP + +# Time interval for rekeying GTK (broadcast/multicast encryption keys) in +# seconds. +#wpa_group_rekey=600 + +# Time interval for rekeying GMK (master key used internally to generate GTKs +# (in seconds). +#wpa_gmk_rekey=86400 + +# Enable IEEE 802.11i/RSN/WPA2 pre-authentication. This is used to speed up +# roaming be pre-authenticating IEEE 802.1X/EAP part of the full RSN +# authentication and key handshake before actually associating with a new AP. +#rsn_preauth=1 +# +# Space separated list of interfaces from which pre-authentication frames are +# accepted (e.g., 'eth0' or 'eth0 wlan0wds0'. This list should include all +# interface that are used for connections to other APs. This could include +# wired interfaces and WDS links. The normal wireless data interface towards +# associated stations (e.g., wlan0) should not be added, since +# pre-authentication is only used with APs other than the currently associated +# one. +#rsn_preauth_interfaces=eth0 diff --git a/contrib/wpa/hostapd/README-WPS b/contrib/wpa/hostapd/README-WPS new file mode 100644 index 0000000..b46d767 --- /dev/null +++ b/contrib/wpa/hostapd/README-WPS @@ -0,0 +1,232 @@ +hostapd and Wi-Fi Protected Setup (WPS) +======================================= + +This document describes how the WPS implementation in hostapd can be +configured and how an external component on an AP (e.g., web UI) is +used to enable enrollment of client devices. + + +Introduction to WPS +------------------- + +Wi-Fi Protected Setup (WPS) is a mechanism for easy configuration of a +wireless network. It allows automated generation of random keys (WPA +passphrase/PSK) and configuration of an access point and client +devices. WPS includes number of methods for setting up connections +with PIN method and push-button configuration (PBC) being the most +commonly deployed options. + +While WPS can enable more home networks to use encryption in the +wireless network, it should be noted that the use of the PIN and +especially PBC mechanisms for authenticating the initial key setup is +not very secure. As such, use of WPS may not be suitable for +environments that require secure network access without chance for +allowing outsiders to gain access during the setup phase. + +WPS uses following terms to describe the entities participating in the +network setup: +- access point: the WLAN access point +- Registrar: a device that control a network and can authorize + addition of new devices); this may be either in the AP ("internal + Registrar") or in an external device, e.g., a laptop, ("external + Registrar") +- Enrollee: a device that is being authorized to use the network + +It should also be noted that the AP and a client device may change +roles (i.e., AP acts as an Enrollee and client device as a Registrar) +when WPS is used to configure the access point. + + +More information about WPS is available from Wi-Fi Alliance: +http://www.wi-fi.org/wifi-protected-setup + + +hostapd implementation +---------------------- + +hostapd includes an optional WPS component that can be used as an +internal WPS Registrar to manage addition of new WPS enabled clients +to the network. In addition, WPS Enrollee functionality in hostapd can +be used to allow external WPS Registrars to configure the access +point, e.g., for initial network setup. In addition, hostapd can proxy a +WPS registration between a wireless Enrollee and an external Registrar +(e.g., Microsoft Vista or Atheros JumpStart) with UPnP. + + +hostapd configuration +--------------------- + +WPS is an optional component that needs to be enabled in hostapd build +configuration (.config). Here is an example configuration that +includes WPS support and uses madwifi driver interface: + +CONFIG_DRIVER_MADWIFI=y +CFLAGS += -I/usr/src/madwifi-0.9.3 +CONFIG_EAP=y +CONFIG_WPS=y +CONFIG_WPS_UPNP=y + + +Following section shows an example runtime configuration +(hostapd.conf) that enables WPS: + +# Configure the driver and network interface +driver=madwifi +interface=ath0 + +# WPA2-Personal configuration for the AP +ssid=wps-test +wpa=2 +wpa_key_mgmt=WPA-PSK +wpa_pairwise=CCMP +# Default WPA passphrase for legacy (non-WPS) clients +wpa_passphrase=12345678 +# Enable random per-device PSK generation for WPS clients +# Please note that the file has to exists for hostapd to start (i.e., create an +# empty file as a starting point). +wpa_psk_file=/etc/hostapd.psk + +# Enable control interface for PBC/PIN entry +ctrl_interface=/var/run/hostapd + +# Enable internal EAP server for EAP-WSC (part of Wi-Fi Protected Setup) +eap_server=1 + +# WPS configuration (AP configured, do not allow external WPS Registrars) +wps_state=2 +ap_setup_locked=1 +# If UUID is not configured, it will be generated based on local MAC address. +uuid=87654321-9abc-def0-1234-56789abc0000 +wps_pin_requests=/var/run/hostapd.pin-req +device_name=Wireless AP +manufacturer=Company +model_name=WAP +model_number=123 +serial_number=12345 +device_type=6-0050F204-1 +os_version=01020300 +config_methods=label display push_button keypad + +# if external Registrars are allowed, UPnP support could be added: +#upnp_iface=br0 +#friendly_name=WPS Access Point + + +External operations +------------------- + +WPS requires either a device PIN code (usually, 8-digit number) or a +pushbutton event (for PBC) to allow a new WPS Enrollee to join the +network. hostapd uses the control interface as an input channel for +these events. + +When a client device (WPS Enrollee) connects to hostapd (WPS +Registrar) in order to start PIN mode negotiation for WPS, an +identifier (Enrollee UUID) is sent. hostapd will need to be configured +with a device password (PIN) for this Enrollee. This is an operation +that requires user interaction (assuming there are no pre-configured +PINs on the AP for a set of Enrollee). + +The PIN request with information about the device is appended to the +wps_pin_requests file (/var/run/hostapd.pin-req in this example). In +addition, hostapd control interface event is sent as a notification of +a new device. The AP could use, e.g., a web UI for showing active +Enrollees to the user and request a PIN for an Enrollee. + +The PIN request file has one line for every Enrollee that connected to +the AP, but for which there was no PIN. Following information is +provided for each Enrollee (separated with tabulators): +- timestamp (seconds from 1970-01-01) +- Enrollee UUID +- MAC address +- Device name +- Manufacturer +- Model Name +- Model Number +- Serial Number +- Device category + +Example line in the /var/run/hostapd.pin-req file: +1200188391 53b63a98-d29e-4457-a2ed-094d7e6a669c Intel(R) Centrino(R) Intel Corporation Intel(R) Centrino(R) - - 1-0050F204-1 + +Control interface data: +WPS-PIN-NEEDED [UUID-E|MAC Address|Device Name|Manufacturer|Model Name|Model Number|Serial Number|Device Category] +For example: +<2>WPS-PIN-NEEDED [53b63a98-d29e-4457-a2ed-094d7e6a669c|02:12:34:56:78:9a|Device|Manuf|Model|Model Number|Serial Number|1-0050F204-1] + +When the user enters a PIN for a pending Enrollee, e.g., on the web +UI), hostapd needs to be notified of the new PIN over the control +interface. This can be done either by using the UNIX domain socket +-based control interface directly (src/common/wpa_ctrl.c provides +helper functions for using the interface) or by calling hostapd_cli. + +Example command to add a PIN (12345670) for an Enrollee: + +hostapd_cli wps_pin 53b63a98-d29e-4457-a2ed-094d7e6a669c 12345670 + +If the UUID-E is not available (e.g., Enrollee waits for the Registrar +to be selected before connecting), wildcard UUID may be used to allow the PIN to be used once with any UUID: + +hostapd_cli wps_pin any 12345670 + + +After this, the Enrollee can connect to the AP again and complete WPS +negotiation. At that point, a new, random WPA PSK is generated for the +client device and the client can then use that key to connect to the +AP to access the network. + + +If the AP includes a pushbutton, WPS PBC mode can be used. It is +enabled by pushing a button on both the AP and the client at about the +same time (2 minute window). hostapd needs to be notified about the AP +button pushed event over the control interface, e.g., by calling +hostapd_cli: + +hostapd_cli wps_pbc + +At this point, the client has two minutes to complete WPS negotiation +which will generate a new WPA PSK in the same way as the PIN method +described above. + + +Credential generation and configuration changes +----------------------------------------------- + +By default, hostapd generates credentials for Enrollees and processing +AP configuration updates internally. However, it is possible to +control these operations from external programs, if desired. + +The internal credential generation can be disabled with +skip_cred_build=1 option in the configuration. extra_cred option will +then need to be used to provide pre-configured Credential attribute(s) +for hostapd to use. The exact data from this binary file will be sent, +i.e., it will have to include valid WPS attributes. extra_cred can +also be used to add additional networks if the Registrar is used to +configure credentials for multiple networks. + +Processing of received configuration updates can be disabled with +wps_cred_processing=1 option. When this is used, an external program +is responsible for creating hostapd configuration files and processing +configuration updates based on messages received from hostapd over +control interface. This will also include the initial configuration on +first successful registration if the AP is initially set in +unconfigured state. + +Following control interface messages are sent out for external programs: + +WPS-REG-SUCCESS <Enrollee MAC address <UUID-E> +For example: +<2>WPS-REG-SUCCESS 02:66:a0:ee:17:27 2b7093f1-d6fb-5108-adbb-bea66bb87333 + +This can be used to tricker change from unconfigured to configured +state (random configuration based on the first successful WPS +registration). In addition, this can be used to update AP UI about the +status of WPS registration progress. + + +WPS-NEW-AP-SETTINGS <hexdump of AP Setup attributes> +For example: +<2>WPS-NEW-AP-SETTINGS 10260001011045000c6a6b6d2d7770732d74657374100300020020100f00020008102700403065346230343536633236366665306433396164313535346131663462663731323433376163666462376633393965353466316631623032306164343438623510200006024231cede15101e000844 + +This can be used to update the externally stored AP configuration and +then update hostapd configuration (followed by restarting of hostapd). diff --git a/contrib/wpa/hostapd/accounting.c b/contrib/wpa/hostapd/accounting.c new file mode 100644 index 0000000..ce71678 --- /dev/null +++ b/contrib/wpa/hostapd/accounting.c @@ -0,0 +1,510 @@ +/* + * hostapd / RADIUS Accounting + * Copyright (c) 2002-2008, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "hostapd.h" +#include "radius/radius.h" +#include "radius/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 void accounting_sta_get_id(struct hostapd_data *hapd, + struct sta_info *sta); + + +static struct radius_msg * accounting_msg(struct hostapd_data *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)); + + os_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, os_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) { + os_snprintf(buf, sizeof(buf), RADIUS_ADDR_FORMAT, + MAC2STR(sta->addr)); + val = (u8 *) buf; + len = os_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, + os_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; + } + + os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT ":%s", + MAC2STR(hapd->own_addr), hapd->conf->ssid.ssid); + if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLED_STATION_ID, + (u8 *) buf, os_strlen(buf))) { + printf("Could not add Called-Station-Id\n"); + goto fail; + } + + if (sta) { + os_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, os_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; + } + + os_snprintf(buf, sizeof(buf), "CONNECT %d%sMbps %s", + radius_sta_rate(hapd, sta) / 2, + (radius_sta_rate(hapd, sta) & 1) ? ".5" : "", + radius_mode_txt(hapd)); + if (!radius_msg_add_attr(msg, RADIUS_ATTR_CONNECT_INFO, + (u8 *) buf, os_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); + os_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) +{ + struct hostapd_data *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); +} + + +/** + * accounting_sta_start - Start STA accounting + * @hapd: hostapd BSS data + * @sta: The station + */ +void accounting_sta_start(struct hostapd_data *hapd, struct sta_info *sta) +{ + struct radius_msg *msg; + int interval; + + if (sta->acct_session_started) + return; + + accounting_sta_get_id(hapd, sta); + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS, + HOSTAPD_LEVEL_INFO, + "starting accounting session %08X-%08X", + sta->acct_session_id_hi, sta->acct_session_id_lo); + + 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; +} + + +static void accounting_sta_report(struct hostapd_data *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); + os_free(msg); +} + + +/** + * accounting_sta_interim - Send a interim STA accounting report + * @hapd: hostapd BSS data + * @sta: The station + */ +void accounting_sta_interim(struct hostapd_data *hapd, struct sta_info *sta) +{ + if (sta->acct_session_started) + accounting_sta_report(hapd, sta, 0); +} + + +/** + * accounting_sta_stop - Stop STA accounting + * @hapd: hostapd BSS data + * @sta: The station + */ +void accounting_sta_stop(struct hostapd_data *hapd, struct sta_info *sta) +{ + if (sta->acct_session_started) { + accounting_sta_report(hapd, sta, 1); + eloop_cancel_timeout(accounting_interim_update, hapd, sta); + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS, + HOSTAPD_LEVEL_INFO, + "stopped accounting session %08X-%08X", + sta->acct_session_id_hi, + sta->acct_session_id_lo); + sta->acct_session_started = 0; + } +} + + +static 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; +} + + +/** + * accounting_receive - Process the RADIUS frames from Accounting Server + * @msg: RADIUS response message + * @req: RADIUS request message + * @shared_secret: RADIUS shared secret + * @shared_secret_len: Length of shared_secret in octets + * @data: Context data (struct hostapd_data *) + * Returns: Processing status + */ +static RadiusRxResult +accounting_receive(struct radius_msg *msg, struct radius_msg *req, + const 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); + os_free(msg); + return; + } + + radius_client_send(hapd->radius, msg, RADIUS_ACCT, NULL); +} + + +/** + * accounting_init: Initialize accounting + * @hapd: hostapd BSS data + * Returns: 0 on success, -1 on failure + */ +int accounting_init(struct hostapd_data *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; +} + + +/** + * accounting_deinit: Deinitilize accounting + * @hapd: hostapd BSS data + */ +void accounting_deinit(struct hostapd_data *hapd) +{ + accounting_report_state(hapd, 0); +} + + +int accounting_reconfig(struct hostapd_data *hapd, + struct hostapd_config *oldconf) +{ + if (!hapd->radius_client_reconfigured) + return 0; + + accounting_deinit(hapd); + return accounting_init(hapd); +} diff --git a/contrib/wpa/hostapd/accounting.h b/contrib/wpa/hostapd/accounting.h new file mode 100644 index 0000000..51e6b4d --- /dev/null +++ b/contrib/wpa/hostapd/accounting.h @@ -0,0 +1,26 @@ +/* + * hostapd / RADIUS Accounting + * Copyright (c) 2002-2005, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef ACCOUNTING_H +#define ACCOUNTING_H + +void accounting_sta_start(struct hostapd_data *hapd, struct sta_info *sta); +void accounting_sta_interim(struct hostapd_data *hapd, struct sta_info *sta); +void accounting_sta_stop(struct hostapd_data *hapd, struct sta_info *sta); +int accounting_init(struct hostapd_data *hapd); +void accounting_deinit(struct hostapd_data *hapd); +int accounting_reconfig(struct hostapd_data *hapd, + struct hostapd_config *oldconf); + +#endif /* ACCOUNTING_H */ diff --git a/contrib/wpa/hostapd/ap.h b/contrib/wpa/hostapd/ap.h new file mode 100644 index 0000000..98f8ee7 --- /dev/null +++ b/contrib/wpa/hostapd/ap.h @@ -0,0 +1,139 @@ +/* + * hostapd / Station table data structures + * Copyright (c) 2002-2008, Jouni Malinen <j@w1.fi> + * Copyright (c) 2007-2008, Intel Corporation + * + * 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 AP_H +#define AP_H + +#ifdef CONFIG_IEEE80211N +#include "ieee802_11_defs.h" +#endif /* CONFIG_IEEE80211N */ + +/* 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_SHORT_PREAMBLE BIT(7) +#define WLAN_STA_PREAUTH BIT(8) +#define WLAN_STA_WME BIT(9) +#define WLAN_STA_MFP BIT(10) +#define WLAN_STA_HT BIT(11) +#define WLAN_STA_WPS BIT(12) +#define WLAN_STA_MAYBE_WPS BIT(13) +#define WLAN_STA_NONERP BIT(31) + +/* Maximum number of supported rates (from both Supported Rates and Extended + * Supported Rates IEs). */ +#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]; + int supported_rates_len; + + unsigned int nonerp_set:1; + unsigned int no_short_slot_time_set:1; + unsigned int no_short_preamble_set:1; + unsigned int no_ht_gf_set:1; + unsigned int no_ht_set:1; + unsigned int ht_20mhz_set:1; + + u16 auth_alg; + u8 previous_ap[6]; + + 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 */ + + struct wpa_state_machine *wpa_sm; + struct rsn_preauth_interface *preauth_iface; + + struct hostapd_ssid *ssid; /* SSID selection based on (Re)AssocReq */ + struct hostapd_ssid *ssid_probe; /* SSID selection based on ProbeReq */ + + int vlan_id; + +#ifdef CONFIG_IEEE80211N + struct ht_cap_ie ht_capabilities; /* IEEE 802.11n capabilities */ +#endif /* CONFIG_IEEE80211N */ + +#ifdef CONFIG_IEEE80211W + int sa_query_count; /* number of pending SA Query requests; + * 0 = no SA Query in progress */ + int sa_query_timed_out; + u8 *sa_query_trans_id; /* buffer of WLAN_SA_QUERY_TR_ID_LEN * + * sa_query_count octets of pending SA Query + * transaction identifiers */ + struct os_time sa_query_start; +#endif /* CONFIG_IEEE80211W */ + + struct wpabuf *wps_ie; /* WPS IE from (Re)Association Request */ +}; + + +/* 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) +/* Number of seconds to keep STA entry with Authenticated flag after it has + * been disassociated. */ +#define AP_MAX_INACTIVITY_AFTER_DISASSOC (1 * 30) +/* Number of seconds to keep STA entry after it has been deauthenticated. */ +#define AP_MAX_INACTIVITY_AFTER_DEAUTH (1 * 5) + +#endif /* AP_H */ diff --git a/contrib/wpa/hostapd/ap_list.c b/contrib/wpa/hostapd/ap_list.c new file mode 100644 index 0000000..4f217dc --- /dev/null +++ b/contrib/wpa/hostapd/ap_list.c @@ -0,0 +1,501 @@ +/* + * hostapd / AP table + * Copyright (c) 2002-2003, Jouni Malinen <j@w1.fi> + * Copyright (c) 2003-2004, Instant802 Networks, Inc. + * Copyright (c) 2006, Devicescape Software, Inc. + * Copyright (c) 2007-2008, Intel Corporation + * + * 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 "includes.h" + +#include "hostapd.h" +#include "ieee802_11.h" +#include "eloop.h" +#include "ap_list.h" +#include "hw_features.h" +#include "beacon.h" + + +struct ieee80211_frame_info { + u32 version; + u32 length; + u64 mactime; + u64 hosttime; + u32 phytype; + u32 channel; + u32 datarate; + u32 antenna; + u32 priority; + u32 ssi_type; + u32 ssi_signal; + u32 ssi_noise; + u32 preamble; + u32 encoding; + + /* Note: this structure is otherwise identical to capture format used + * in linux-wlan-ng, but this additional field is used to provide meta + * data about the frame to hostapd. This was the easiest method for + * providing this information, but this might change in the future. */ + u32 msg_type; +} __attribute__ ((packed)); + + +enum ieee80211_phytype { + ieee80211_phytype_fhss_dot11_97 = 1, + ieee80211_phytype_dsss_dot11_97 = 2, + ieee80211_phytype_irbaseband = 3, + ieee80211_phytype_dsss_dot11_b = 4, + ieee80211_phytype_pbcc_dot11_b = 5, + ieee80211_phytype_ofdm_dot11_g = 6, + ieee80211_phytype_pbcc_dot11_g = 7, + ieee80211_phytype_ofdm_dot11_a = 8, + ieee80211_phytype_dsss_dot11_turbog = 255, + ieee80211_phytype_dsss_dot11_turbo = 256, +}; + + +/* AP list is a double linked list with head->prev pointing to the end of the + * list and tail->next = NULL. Entries are moved to the head of the list + * whenever a beacon has been received from the AP in question. The tail entry + * in this link will thus be the least recently used entry. */ + + +static void ap_list_new_ap(struct hostapd_iface *iface, struct ap_info *ap) +{ + wpa_printf(MSG_DEBUG, "New AP detected: " MACSTR, MAC2STR(ap->addr)); + + /* TODO: could send a notification message to an external program that + * would then determine whether a rogue AP has been detected */ +} + + +static void ap_list_expired_ap(struct hostapd_iface *iface, struct ap_info *ap) +{ + wpa_printf(MSG_DEBUG, "AP info expired: " MACSTR, MAC2STR(ap->addr)); + + /* TODO: could send a notification message to an external program */ +} + + +static int ap_list_beacon_olbc(struct hostapd_iface *iface, struct ap_info *ap) +{ + int i; + + if (iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G || + ap->phytype != ieee80211_phytype_pbcc_dot11_g || + iface->conf->channel != ap->channel) + return 0; + + if (ap->erp != -1 && (ap->erp & ERP_INFO_NON_ERP_PRESENT)) + return 1; + + for (i = 0; i < WLAN_SUPP_RATES_MAX; i++) { + int rate = (ap->supported_rates[i] & 0x7f) * 5; + if (rate == 60 || rate == 90 || rate > 110) + return 0; + } + + return 1; +} + + +#ifdef CONFIG_IEEE80211N +static int ap_list_beacon_olbc_ht(struct hostapd_iface *iface, + struct ap_info *ap) +{ + return !ap->ht_support; +} +#endif /* CONFIG_IEEE80211N */ + + +struct ap_info * ap_get_ap(struct hostapd_iface *iface, u8 *ap) +{ + struct ap_info *s; + + s = iface->ap_hash[STA_HASH(ap)]; + while (s != NULL && os_memcmp(s->addr, ap, ETH_ALEN) != 0) + s = s->hnext; + return s; +} + + +static void ap_ap_list_add(struct hostapd_iface *iface, struct ap_info *ap) +{ + if (iface->ap_list) { + ap->prev = iface->ap_list->prev; + iface->ap_list->prev = ap; + } else + ap->prev = ap; + ap->next = iface->ap_list; + iface->ap_list = ap; +} + + +static void ap_ap_list_del(struct hostapd_iface *iface, struct ap_info *ap) +{ + if (iface->ap_list == ap) + iface->ap_list = ap->next; + else + ap->prev->next = ap->next; + + if (ap->next) + ap->next->prev = ap->prev; + else if (iface->ap_list) + iface->ap_list->prev = ap->prev; +} + + +static void ap_ap_iter_list_add(struct hostapd_iface *iface, + struct ap_info *ap) +{ + if (iface->ap_iter_list) { + ap->iter_prev = iface->ap_iter_list->iter_prev; + iface->ap_iter_list->iter_prev = ap; + } else + ap->iter_prev = ap; + ap->iter_next = iface->ap_iter_list; + iface->ap_iter_list = ap; +} + + +static void ap_ap_iter_list_del(struct hostapd_iface *iface, + struct ap_info *ap) +{ + if (iface->ap_iter_list == ap) + iface->ap_iter_list = ap->iter_next; + else + ap->iter_prev->iter_next = ap->iter_next; + + if (ap->iter_next) + ap->iter_next->iter_prev = ap->iter_prev; + else if (iface->ap_iter_list) + iface->ap_iter_list->iter_prev = ap->iter_prev; +} + + +static void ap_ap_hash_add(struct hostapd_iface *iface, struct ap_info *ap) +{ + ap->hnext = iface->ap_hash[STA_HASH(ap->addr)]; + iface->ap_hash[STA_HASH(ap->addr)] = ap; +} + + +static void ap_ap_hash_del(struct hostapd_iface *iface, struct ap_info *ap) +{ + struct ap_info *s; + + s = iface->ap_hash[STA_HASH(ap->addr)]; + if (s == NULL) return; + if (os_memcmp(s->addr, ap->addr, ETH_ALEN) == 0) { + iface->ap_hash[STA_HASH(ap->addr)] = s->hnext; + return; + } + + while (s->hnext != NULL && + os_memcmp(s->hnext->addr, ap->addr, ETH_ALEN) != 0) + s = s->hnext; + if (s->hnext != NULL) + s->hnext = s->hnext->hnext; + else + printf("AP: could not remove AP " MACSTR " from hash table\n", + MAC2STR(ap->addr)); +} + + +static void ap_free_ap(struct hostapd_iface *iface, struct ap_info *ap) +{ + ap_ap_hash_del(iface, ap); + ap_ap_list_del(iface, ap); + ap_ap_iter_list_del(iface, ap); + + iface->num_ap--; + os_free(ap); +} + + +static void hostapd_free_aps(struct hostapd_iface *iface) +{ + struct ap_info *ap, *prev; + + ap = iface->ap_list; + + while (ap) { + prev = ap; + ap = ap->next; + ap_free_ap(iface, prev); + } + + iface->ap_list = NULL; +} + + +int ap_ap_for_each(struct hostapd_iface *iface, + int (*func)(struct ap_info *s, void *data), void *data) +{ + struct ap_info *s; + int ret = 0; + + s = iface->ap_list; + + while (s) { + ret = func(s, data); + if (ret) + break; + s = s->next; + } + + return ret; +} + + +static struct ap_info * ap_ap_add(struct hostapd_iface *iface, u8 *addr) +{ + struct ap_info *ap; + + ap = os_zalloc(sizeof(struct ap_info)); + if (ap == NULL) + return NULL; + + /* initialize AP info data */ + os_memcpy(ap->addr, addr, ETH_ALEN); + ap_ap_list_add(iface, ap); + iface->num_ap++; + ap_ap_hash_add(iface, ap); + ap_ap_iter_list_add(iface, ap); + + if (iface->num_ap > iface->conf->ap_table_max_size && ap != ap->prev) { + wpa_printf(MSG_DEBUG, "Removing the least recently used AP " + MACSTR " from AP table", MAC2STR(ap->prev->addr)); + if (iface->conf->passive_scan_interval > 0) + ap_list_expired_ap(iface, ap->prev); + ap_free_ap(iface, ap->prev); + } + + return ap; +} + + +void ap_list_process_beacon(struct hostapd_iface *iface, + struct ieee80211_mgmt *mgmt, + struct ieee802_11_elems *elems, + struct hostapd_frame_info *fi) +{ + struct ap_info *ap; + int new_ap = 0; + size_t len; + int set_beacon = 0; + + if (iface->conf->ap_table_max_size < 1) + return; + + ap = ap_get_ap(iface, mgmt->bssid); + if (!ap) { + ap = ap_ap_add(iface, mgmt->bssid); + if (!ap) { + printf("Failed to allocate AP information entry\n"); + return; + } + new_ap = 1; + } + + ap->beacon_int = le_to_host16(mgmt->u.beacon.beacon_int); + ap->capability = le_to_host16(mgmt->u.beacon.capab_info); + + if (elems->ssid) { + len = elems->ssid_len; + if (len >= sizeof(ap->ssid)) + len = sizeof(ap->ssid) - 1; + os_memcpy(ap->ssid, elems->ssid, len); + ap->ssid[len] = '\0'; + ap->ssid_len = len; + } + + os_memset(ap->supported_rates, 0, WLAN_SUPP_RATES_MAX); + len = 0; + if (elems->supp_rates) { + len = elems->supp_rates_len; + if (len > WLAN_SUPP_RATES_MAX) + len = WLAN_SUPP_RATES_MAX; + os_memcpy(ap->supported_rates, elems->supp_rates, len); + } + if (elems->ext_supp_rates) { + int len2; + if (len + elems->ext_supp_rates_len > WLAN_SUPP_RATES_MAX) + len2 = WLAN_SUPP_RATES_MAX - len; + else + len2 = elems->ext_supp_rates_len; + os_memcpy(ap->supported_rates + len, elems->ext_supp_rates, + len2); + } + + ap->wpa = elems->wpa_ie != NULL; + + if (elems->erp_info && elems->erp_info_len == 1) + ap->erp = elems->erp_info[0]; + else + ap->erp = -1; + + if (elems->ds_params && elems->ds_params_len == 1) + ap->channel = elems->ds_params[0]; + else if (fi) + ap->channel = fi->channel; + + if (elems->ht_capabilities) + ap->ht_support = 1; + else + ap->ht_support = 0; + + ap->num_beacons++; + time(&ap->last_beacon); + if (fi) { + ap->phytype = fi->phytype; + ap->ssi_signal = fi->ssi_signal; + ap->datarate = fi->datarate; + } + + if (new_ap) { + if (iface->conf->passive_scan_interval > 0) + ap_list_new_ap(iface, ap); + } else if (ap != iface->ap_list) { + /* move AP entry into the beginning of the list so that the + * oldest entry is always in the end of the list */ + ap_ap_list_del(iface, ap); + ap_ap_list_add(iface, ap); + } + + if (!iface->olbc && + ap_list_beacon_olbc(iface, ap)) { + iface->olbc = 1; + wpa_printf(MSG_DEBUG, "OLBC AP detected: " MACSTR " - enable " + "protection", MAC2STR(ap->addr)); + set_beacon++; + } + +#ifdef CONFIG_IEEE80211N + if (!iface->olbc_ht && ap_list_beacon_olbc_ht(iface, ap)) { + iface->olbc_ht = 1; + hostapd_ht_operation_update(iface); + wpa_printf(MSG_DEBUG, "OLBC HT AP detected: " MACSTR + " - enable protection", MAC2STR(ap->addr)); + set_beacon++; + } +#endif /* CONFIG_IEEE80211N */ + + if (set_beacon) + ieee802_11_set_beacons(iface); +} + + +static void ap_list_timer(void *eloop_ctx, void *timeout_ctx) +{ + struct hostapd_iface *iface = eloop_ctx; + time_t now; + struct ap_info *ap; + int set_beacon = 0; + + eloop_register_timeout(10, 0, ap_list_timer, iface, NULL); + + if (!iface->ap_list) + return; + + time(&now); + + /* FIX: it looks like jkm-Purina ended up in busy loop in this + * function. Apparently, something can still cause a loop in the AP + * list.. */ + + while (iface->ap_list) { + ap = iface->ap_list->prev; + if (ap->last_beacon + iface->conf->ap_table_expiration_time >= + now) + break; + + if (iface->conf->passive_scan_interval > 0) + ap_list_expired_ap(iface, ap); + ap_free_ap(iface, ap); + } + + if (iface->olbc || iface->olbc_ht) { + int olbc = 0; + int olbc_ht = 0; + + ap = iface->ap_list; + while (ap && (olbc == 0 || olbc_ht == 0)) { + if (ap_list_beacon_olbc(iface, ap)) + olbc = 1; +#ifdef CONFIG_IEEE80211N + if (ap_list_beacon_olbc_ht(iface, ap)) + olbc_ht = 1; +#endif /* CONFIG_IEEE80211N */ + ap = ap->next; + } + if (!olbc && iface->olbc) { + wpa_printf(MSG_DEBUG, "OLBC not detected anymore"); + iface->olbc = 0; + set_beacon++; + } +#ifdef CONFIG_IEEE80211N + if (!olbc_ht && iface->olbc_ht) { + wpa_printf(MSG_DEBUG, "OLBC HT not detected anymore"); + iface->olbc_ht = 0; + hostapd_ht_operation_update(iface); + set_beacon++; + } +#endif /* CONFIG_IEEE80211N */ + } + + if (set_beacon) + ieee802_11_set_beacons(iface); +} + + +int ap_list_init(struct hostapd_iface *iface) +{ + eloop_register_timeout(10, 0, ap_list_timer, iface, NULL); + return 0; +} + + +void ap_list_deinit(struct hostapd_iface *iface) +{ + eloop_cancel_timeout(ap_list_timer, iface, NULL); + hostapd_free_aps(iface); +} + + +int ap_list_reconfig(struct hostapd_iface *iface, + struct hostapd_config *oldconf) +{ + time_t now; + struct ap_info *ap; + + if (iface->conf->ap_table_max_size == oldconf->ap_table_max_size && + iface->conf->ap_table_expiration_time == + oldconf->ap_table_expiration_time) + return 0; + + time(&now); + + while (iface->ap_list) { + ap = iface->ap_list->prev; + if (iface->num_ap <= iface->conf->ap_table_max_size && + ap->last_beacon + iface->conf->ap_table_expiration_time >= + now) + break; + + if (iface->conf->passive_scan_interval > 0) + ap_list_expired_ap(iface, iface->ap_list->prev); + ap_free_ap(iface, iface->ap_list->prev); + } + + return 0; +} diff --git a/contrib/wpa/hostapd/ap_list.h b/contrib/wpa/hostapd/ap_list.h new file mode 100644 index 0000000..93704f8 --- /dev/null +++ b/contrib/wpa/hostapd/ap_list.h @@ -0,0 +1,71 @@ +/* + * hostapd / AP table + * Copyright (c) 2002-2003, Jouni Malinen <j@w1.fi> + * Copyright (c) 2003-2004, Instant802 Networks, Inc. + * Copyright (c) 2006, Devicescape Software, Inc. + * Copyright (c) 2007-2008, Intel Corporation + * + * 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 AP_LIST_H +#define AP_LIST_H + +struct ap_info { + /* Note: next/prev pointers are updated whenever a new beacon is + * received because these are used to find the least recently used + * entries. iter_next/iter_prev are updated only when adding new BSSes + * and when removing old ones. These should be used when iterating + * through the table in a manner that allows beacons to be received + * during the iteration. */ + struct ap_info *next; /* next entry in AP list */ + struct ap_info *prev; /* previous entry in AP list */ + struct ap_info *hnext; /* next entry in hash table list */ + struct ap_info *iter_next; /* next entry in AP iteration list */ + struct ap_info *iter_prev; /* previous entry in AP iteration list */ + u8 addr[6]; + u16 beacon_int; + u16 capability; + u8 supported_rates[WLAN_SUPP_RATES_MAX]; + u8 ssid[33]; + size_t ssid_len; + int wpa; + int erp; /* ERP Info or -1 if ERP info element not present */ + + int phytype; /* .11a / .11b / .11g / Atheros Turbo */ + int channel; + int datarate; /* in 100 kbps */ + int ssi_signal; + + int ht_support; + + unsigned int num_beacons; /* number of beacon frames received */ + time_t last_beacon; + + int already_seen; /* whether API call AP-NEW has already fetched + * information about this AP */ +}; + +struct ieee802_11_elems; +struct hostapd_frame_info; + +struct ap_info * ap_get_ap(struct hostapd_iface *iface, u8 *sta); +int ap_ap_for_each(struct hostapd_iface *iface, + int (*func)(struct ap_info *s, void *data), void *data); +void ap_list_process_beacon(struct hostapd_iface *iface, + struct ieee80211_mgmt *mgmt, + struct ieee802_11_elems *elems, + struct hostapd_frame_info *fi); +int ap_list_init(struct hostapd_iface *iface); +void ap_list_deinit(struct hostapd_iface *iface); +int ap_list_reconfig(struct hostapd_iface *iface, + struct hostapd_config *oldconf); + +#endif /* AP_LIST_H */ diff --git a/contrib/wpa/hostapd/beacon.c b/contrib/wpa/hostapd/beacon.c new file mode 100644 index 0000000..31323e8 --- /dev/null +++ b/contrib/wpa/hostapd/beacon.c @@ -0,0 +1,467 @@ +/* + * hostapd / IEEE 802.11 Management: Beacon and Probe Request/Response + * Copyright (c) 2002-2004, Instant802 Networks, Inc. + * Copyright (c) 2005-2006, Devicescape Software, Inc. + * Copyright (c) 2008, Jouni Malinen <j@w1.fi> + * Copyright (c) 2007-2008, Intel Corporation + * + * 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 "includes.h" + +#ifndef CONFIG_NATIVE_WINDOWS + +#include "hostapd.h" +#include "ieee802_11.h" +#include "wpa.h" +#include "wme.h" +#include "beacon.h" +#include "hw_features.h" +#include "driver.h" +#include "sta_info.h" +#include "wps_hostapd.h" + + +static u8 ieee802_11_erp_info(struct hostapd_data *hapd) +{ + u8 erp = 0; + + if (hapd->iface->current_mode == NULL || + hapd->iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G) + return 0; + + switch (hapd->iconf->cts_protection_type) { + case CTS_PROTECTION_FORCE_ENABLED: + erp |= ERP_INFO_NON_ERP_PRESENT | ERP_INFO_USE_PROTECTION; + break; + case CTS_PROTECTION_FORCE_DISABLED: + erp = 0; + break; + case CTS_PROTECTION_AUTOMATIC: + if (hapd->iface->olbc) + erp |= ERP_INFO_USE_PROTECTION; + /* continue */ + case CTS_PROTECTION_AUTOMATIC_NO_OLBC: + if (hapd->iface->num_sta_non_erp > 0) { + erp |= ERP_INFO_NON_ERP_PRESENT | + ERP_INFO_USE_PROTECTION; + } + break; + } + if (hapd->iface->num_sta_no_short_preamble > 0) + erp |= ERP_INFO_BARKER_PREAMBLE_MODE; + + return erp; +} + + +static u8 * hostapd_eid_ds_params(struct hostapd_data *hapd, u8 *eid) +{ + *eid++ = WLAN_EID_DS_PARAMS; + *eid++ = 1; + *eid++ = hapd->iconf->channel; + return eid; +} + + +static u8 * hostapd_eid_erp_info(struct hostapd_data *hapd, u8 *eid) +{ + if (hapd->iface->current_mode == NULL || + hapd->iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G) + return eid; + + /* Set NonERP_present and use_protection bits if there + * are any associated NonERP stations. */ + /* TODO: use_protection bit can be set to zero even if + * there are NonERP stations present. This optimization + * might be useful if NonERP stations are "quiet". + * See 802.11g/D6 E-1 for recommended practice. + * In addition, Non ERP present might be set, if AP detects Non ERP + * operation on other APs. */ + + /* Add ERP Information element */ + *eid++ = WLAN_EID_ERP_INFO; + *eid++ = 1; + *eid++ = ieee802_11_erp_info(hapd); + + return eid; +} + + +static u8 * hostapd_eid_country_add(u8 *pos, u8 *end, int chan_spacing, + struct hostapd_channel_data *start, + struct hostapd_channel_data *prev) +{ + if (end - pos < 3) + return pos; + + /* first channel number */ + *pos++ = start->chan; + /* number of channels */ + *pos++ = (prev->chan - start->chan) / chan_spacing + 1; + /* maximum transmit power level */ + *pos++ = start->max_tx_power; + + return pos; +} + + +static u8 * hostapd_eid_country(struct hostapd_data *hapd, u8 *eid, + int max_len) +{ + u8 *pos = eid; + u8 *end = eid + max_len; + int i; + struct hostapd_hw_modes *mode; + struct hostapd_channel_data *start, *prev; + int chan_spacing = 1; + + if (!hapd->iconf->ieee80211d || max_len < 6 || + hapd->iface->current_mode == NULL) + return eid; + + *pos++ = WLAN_EID_COUNTRY; + pos++; /* length will be set later */ + os_memcpy(pos, hapd->iconf->country, 3); /* e.g., 'US ' */ + pos += 3; + + mode = hapd->iface->current_mode; + if (mode->mode == HOSTAPD_MODE_IEEE80211A) + chan_spacing = 4; + + start = prev = NULL; + for (i = 0; i < mode->num_channels; i++) { + struct hostapd_channel_data *chan = &mode->channels[i]; + if (chan->flag & HOSTAPD_CHAN_DISABLED) + continue; + if (start && prev && + prev->chan + chan_spacing == chan->chan && + start->max_tx_power == chan->max_tx_power) { + prev = chan; + continue; /* can use same entry */ + } + + if (start) { + pos = hostapd_eid_country_add(pos, end, chan_spacing, + start, prev); + start = NULL; + } + + /* Start new group */ + start = prev = chan; + } + + if (start) { + pos = hostapd_eid_country_add(pos, end, chan_spacing, + start, prev); + } + + if ((pos - eid) & 1) { + if (end - pos < 1) + return eid; + *pos++ = 0; /* pad for 16-bit alignment */ + } + + eid[1] = (pos - eid) - 2; + + return pos; +} + + +static u8 * hostapd_eid_wpa(struct hostapd_data *hapd, u8 *eid, size_t len, + struct sta_info *sta) +{ + const u8 *ie; + size_t ielen; + + ie = wpa_auth_get_wpa_ie(hapd->wpa_auth, &ielen); + if (ie == NULL || ielen > len) + return eid; + + os_memcpy(eid, ie, ielen); + return eid + ielen; +} + + +void handle_probe_req(struct hostapd_data *hapd, struct ieee80211_mgmt *mgmt, + size_t len) +{ + struct ieee80211_mgmt *resp; + struct ieee802_11_elems elems; + char *ssid; + u8 *pos, *epos, *ie; + size_t ssid_len, ie_len; + struct sta_info *sta = NULL; + + ie = mgmt->u.probe_req.variable; + ie_len = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req)); + + hostapd_wps_probe_req_rx(hapd, mgmt->sa, ie, ie_len); + + if (!hapd->iconf->send_probe_response) + return; + + if (ieee802_11_parse_elems(ie, ie_len, &elems, 0) == ParseFailed) { + wpa_printf(MSG_DEBUG, "Could not parse ProbeReq from " MACSTR, + MAC2STR(mgmt->sa)); + return; + } + + ssid = NULL; + ssid_len = 0; + + if ((!elems.ssid || !elems.supp_rates)) { + wpa_printf(MSG_DEBUG, "STA " MACSTR " sent probe request " + "without SSID or supported rates element", + MAC2STR(mgmt->sa)); + return; + } + + if (hapd->conf->ignore_broadcast_ssid && elems.ssid_len == 0) { + wpa_printf(MSG_MSGDUMP, "Probe Request from " MACSTR " for " + "broadcast SSID ignored", MAC2STR(mgmt->sa)); + return; + } + + sta = ap_get_sta(hapd, mgmt->sa); + + if (elems.ssid_len == 0 || + (elems.ssid_len == hapd->conf->ssid.ssid_len && + os_memcmp(elems.ssid, hapd->conf->ssid.ssid, elems.ssid_len) == + 0)) { + ssid = hapd->conf->ssid.ssid; + ssid_len = hapd->conf->ssid.ssid_len; + if (sta) + sta->ssid_probe = &hapd->conf->ssid; + } + + if (!ssid) { + if (!(mgmt->da[0] & 0x01)) { + char ssid_txt[33]; + ieee802_11_print_ssid(ssid_txt, elems.ssid, + elems.ssid_len); + wpa_printf(MSG_MSGDUMP, "Probe Request from " MACSTR + " for foreign SSID '%s'", + MAC2STR(mgmt->sa), ssid_txt); + } + return; + } + + /* TODO: verify that supp_rates contains at least one matching rate + * with AP configuration */ +#define MAX_PROBERESP_LEN 768 + resp = os_zalloc(MAX_PROBERESP_LEN); + if (resp == NULL) + return; + epos = ((u8 *) resp) + MAX_PROBERESP_LEN; + + resp->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, + WLAN_FC_STYPE_PROBE_RESP); + os_memcpy(resp->da, mgmt->sa, ETH_ALEN); + os_memcpy(resp->sa, hapd->own_addr, ETH_ALEN); + + os_memcpy(resp->bssid, hapd->own_addr, ETH_ALEN); + resp->u.probe_resp.beacon_int = + host_to_le16(hapd->iconf->beacon_int); + + /* hardware or low-level driver will setup seq_ctrl and timestamp */ + resp->u.probe_resp.capab_info = + host_to_le16(hostapd_own_capab_info(hapd, sta, 1)); + + pos = resp->u.probe_resp.variable; + *pos++ = WLAN_EID_SSID; + *pos++ = ssid_len; + os_memcpy(pos, ssid, ssid_len); + pos += ssid_len; + + /* Supported rates */ + pos = hostapd_eid_supp_rates(hapd, pos); + + /* DS Params */ + pos = hostapd_eid_ds_params(hapd, pos); + + pos = hostapd_eid_country(hapd, pos, epos - pos); + + /* ERP Information element */ + pos = hostapd_eid_erp_info(hapd, pos); + + /* Extended supported rates */ + pos = hostapd_eid_ext_supp_rates(hapd, pos); + + pos = hostapd_eid_wpa(hapd, pos, epos - pos, sta); + + /* Wi-Fi Wireless Multimedia Extensions */ + pos = hostapd_eid_wme(hapd, pos); + + pos = hostapd_eid_ht_capabilities_info(hapd, pos); + pos = hostapd_eid_ht_operation(hapd, pos); + +#ifdef CONFIG_WPS + if (hapd->conf->wps_state && hapd->wps_probe_resp_ie) { + os_memcpy(pos, hapd->wps_probe_resp_ie, + hapd->wps_probe_resp_ie_len); + pos += hapd->wps_probe_resp_ie_len; + } +#endif /* CONFIG_WPS */ + + if (hostapd_send_mgmt_frame(hapd, resp, pos - (u8 *) resp, 0) < 0) + perror("handle_probe_req: send"); + + os_free(resp); + + wpa_printf(MSG_MSGDUMP, "STA " MACSTR " sent probe request for %s " + "SSID", MAC2STR(mgmt->sa), + elems.ssid_len == 0 ? "broadcast" : "our"); +} + + +void ieee802_11_set_beacon(struct hostapd_data *hapd) +{ + struct ieee80211_mgmt *head; + u8 *pos, *tail, *tailpos; + int preamble; + u16 capab_info; + size_t head_len, tail_len; + int cts_protection = ((ieee802_11_erp_info(hapd) & + ERP_INFO_USE_PROTECTION) ? 1 : 0); + +#define BEACON_HEAD_BUF_SIZE 256 +#define BEACON_TAIL_BUF_SIZE 512 + head = os_zalloc(BEACON_HEAD_BUF_SIZE); + tailpos = tail = os_malloc(BEACON_TAIL_BUF_SIZE); + if (head == NULL || tail == NULL) { + wpa_printf(MSG_ERROR, "Failed to set beacon data"); + os_free(head); + os_free(tail); + return; + } + + head->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, + WLAN_FC_STYPE_BEACON); + head->duration = host_to_le16(0); + os_memset(head->da, 0xff, ETH_ALEN); + + os_memcpy(head->sa, hapd->own_addr, ETH_ALEN); + os_memcpy(head->bssid, hapd->own_addr, ETH_ALEN); + head->u.beacon.beacon_int = + host_to_le16(hapd->iconf->beacon_int); + + /* hardware or low-level driver will setup seq_ctrl and timestamp */ + capab_info = hostapd_own_capab_info(hapd, NULL, 0); + head->u.beacon.capab_info = host_to_le16(capab_info); + pos = &head->u.beacon.variable[0]; + + /* SSID */ + *pos++ = WLAN_EID_SSID; + if (hapd->conf->ignore_broadcast_ssid == 2) { + /* clear the data, but keep the correct length of the SSID */ + *pos++ = hapd->conf->ssid.ssid_len; + os_memset(pos, 0, hapd->conf->ssid.ssid_len); + pos += hapd->conf->ssid.ssid_len; + } else if (hapd->conf->ignore_broadcast_ssid) { + *pos++ = 0; /* empty SSID */ + } else { + *pos++ = hapd->conf->ssid.ssid_len; + os_memcpy(pos, hapd->conf->ssid.ssid, + hapd->conf->ssid.ssid_len); + pos += hapd->conf->ssid.ssid_len; + } + + /* Supported rates */ + pos = hostapd_eid_supp_rates(hapd, pos); + + /* DS Params */ + pos = hostapd_eid_ds_params(hapd, pos); + + head_len = pos - (u8 *) head; + + tailpos = hostapd_eid_country(hapd, tailpos, + tail + BEACON_TAIL_BUF_SIZE - tailpos); + + /* ERP Information element */ + tailpos = hostapd_eid_erp_info(hapd, tailpos); + + /* Extended supported rates */ + tailpos = hostapd_eid_ext_supp_rates(hapd, tailpos); + + tailpos = hostapd_eid_wpa(hapd, tailpos, tail + BEACON_TAIL_BUF_SIZE - + tailpos, NULL); + + /* Wi-Fi Wireless Multimedia Extensions */ + tailpos = hostapd_eid_wme(hapd, tailpos); + +#ifdef CONFIG_IEEE80211N + if (hapd->iconf->ieee80211n) { + u8 *ht_capab, *ht_oper; + ht_capab = tailpos; + tailpos = hostapd_eid_ht_capabilities_info(hapd, tailpos); + + ht_oper = tailpos; + tailpos = hostapd_eid_ht_operation(hapd, tailpos); + + if (tailpos > ht_oper && ht_oper > ht_capab && + hostapd_set_ht_params(hapd->conf->iface, hapd, + ht_capab + 2, ht_capab[1], + ht_oper + 2, ht_oper[1])) { + wpa_printf(MSG_ERROR, "Could not set HT capabilities " + "for kernel driver"); + } + } +#endif /* CONFIG_IEEE80211N */ + +#ifdef CONFIG_WPS + if (hapd->conf->wps_state && hapd->wps_beacon_ie) { + os_memcpy(tailpos, hapd->wps_beacon_ie, + hapd->wps_beacon_ie_len); + tailpos += hapd->wps_beacon_ie_len; + } +#endif /* CONFIG_WPS */ + + tail_len = tailpos > tail ? tailpos - tail : 0; + + if (hostapd_set_beacon(hapd->conf->iface, hapd, (u8 *) head, head_len, + tail, tail_len)) + wpa_printf(MSG_ERROR, "Failed to set beacon head/tail"); + + os_free(tail); + os_free(head); + + if (hostapd_set_cts_protect(hapd, cts_protection)) + wpa_printf(MSG_ERROR, "Failed to set CTS protect in kernel " + "driver"); + + if (hapd->iface->current_mode && + hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G && + hostapd_set_short_slot_time(hapd, + hapd->iface->num_sta_no_short_slot_time + > 0 ? 0 : 1)) + wpa_printf(MSG_ERROR, "Failed to set Short Slot Time option " + "in kernel driver"); + + if (hapd->iface->num_sta_no_short_preamble == 0 && + hapd->iconf->preamble == SHORT_PREAMBLE) + preamble = SHORT_PREAMBLE; + else + preamble = LONG_PREAMBLE; + if (hostapd_set_preamble(hapd, preamble)) + wpa_printf(MSG_ERROR, "Could not set preamble for kernel " + "driver"); +} + + +void ieee802_11_set_beacons(struct hostapd_iface *iface) +{ + size_t i; + for (i = 0; i < iface->num_bss; i++) + ieee802_11_set_beacon(iface->bss[i]); +} + +#endif /* CONFIG_NATIVE_WINDOWS */ diff --git a/contrib/wpa/hostapd/beacon.h b/contrib/wpa/hostapd/beacon.h new file mode 100644 index 0000000..18e0da2 --- /dev/null +++ b/contrib/wpa/hostapd/beacon.h @@ -0,0 +1,24 @@ +/* + * hostapd / IEEE 802.11 Management: Beacon and Probe Request/Response + * Copyright (c) 2002-2004, Instant802 Networks, Inc. + * Copyright (c) 2005-2006, Devicescape Software, Inc. + * + * 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 BEACON_H +#define BEACON_H + +void handle_probe_req(struct hostapd_data *hapd, struct ieee80211_mgmt *mgmt, + size_t len); +void ieee802_11_set_beacon(struct hostapd_data *hapd); +void ieee802_11_set_beacons(struct hostapd_iface *iface); + +#endif /* BEACON_H */ diff --git a/contrib/wpa/hostapd/config.c b/contrib/wpa/hostapd/config.c new file mode 100644 index 0000000..6ad14d2 --- /dev/null +++ b/contrib/wpa/hostapd/config.c @@ -0,0 +1,2617 @@ +/* + * hostapd / Configuration file + * Copyright (c) 2003-2008, Jouni Malinen <j@w1.fi> + * Copyright (c) 2007-2008, Intel Corporation + * + * 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 "includes.h" +#ifndef CONFIG_NATIVE_WINDOWS +#include <grp.h> +#endif /* CONFIG_NATIVE_WINDOWS */ + +#include "hostapd.h" +#include "driver.h" +#include "sha1.h" +#include "eap_server/eap.h" +#include "radius/radius_client.h" +#include "wpa_common.h" +#include "wpa.h" +#include "uuid.h" +#include "eap_common/eap_wsc_common.h" + + +#define MAX_STA_COUNT 2007 + +extern struct wpa_driver_ops *hostapd_drivers[]; + + +static int hostapd_config_read_vlan_file(struct hostapd_bss_config *bss, + const char *fname) +{ + FILE *f; + char buf[128], *pos, *pos2; + int line = 0, vlan_id; + struct hostapd_vlan *vlan; + + f = fopen(fname, "r"); + if (!f) { + wpa_printf(MSG_ERROR, "VLAN file '%s' not readable.", 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 (buf[0] == '*') { + vlan_id = VLAN_ID_WILDCARD; + pos = buf + 1; + } else { + vlan_id = strtol(buf, &pos, 10); + if (buf == pos || vlan_id < 1 || + vlan_id > MAX_VLAN_ID) { + wpa_printf(MSG_ERROR, "Invalid VLAN ID at " + "line %d in '%s'", line, fname); + fclose(f); + return -1; + } + } + + while (*pos == ' ' || *pos == '\t') + pos++; + pos2 = pos; + while (*pos2 != ' ' && *pos2 != '\t' && *pos2 != '\0') + pos2++; + *pos2 = '\0'; + if (*pos == '\0' || os_strlen(pos) > IFNAMSIZ) { + wpa_printf(MSG_ERROR, "Invalid VLAN ifname at line %d " + "in '%s'", line, fname); + fclose(f); + return -1; + } + + vlan = os_malloc(sizeof(*vlan)); + if (vlan == NULL) { + wpa_printf(MSG_ERROR, "Out of memory while reading " + "VLAN interfaces from '%s'", fname); + fclose(f); + return -1; + } + + os_memset(vlan, 0, sizeof(*vlan)); + vlan->vlan_id = vlan_id; + os_strlcpy(vlan->ifname, pos, sizeof(vlan->ifname)); + if (bss->vlan_tail) + bss->vlan_tail->next = vlan; + else + bss->vlan = vlan; + bss->vlan_tail = vlan; + } + + fclose(f); + + return 0; +} + + +static void hostapd_config_free_vlan(struct hostapd_bss_config *bss) +{ + struct hostapd_vlan *vlan, *prev; + + vlan = bss->vlan; + prev = NULL; + while (vlan) { + prev = vlan; + vlan = vlan->next; + os_free(prev); + } + + bss->vlan = NULL; +} + + +/* convert floats with one decimal place to value*10 int, i.e., + * "1.5" will return 15 */ +static int hostapd_config_read_int10(const char *value) +{ + int i, d; + char *pos; + + i = atoi(value); + pos = os_strchr(value, '.'); + d = 0; + if (pos) { + pos++; + if (*pos >= '0' && *pos <= '9') + d = *pos - '0'; + } + + return i * 10 + d; +} + + +static void hostapd_config_defaults_bss(struct hostapd_bss_config *bss) +{ + bss->logger_syslog_level = HOSTAPD_LEVEL_INFO; + bss->logger_stdout_level = HOSTAPD_LEVEL_INFO; + bss->logger_syslog = (unsigned int) -1; + bss->logger_stdout = (unsigned int) -1; + + bss->auth_algs = WPA_AUTH_ALG_OPEN | WPA_AUTH_ALG_SHARED; + + bss->wep_rekeying_period = 300; + /* use key0 in individual key and key1 in broadcast key */ + bss->broadcast_key_idx_min = 1; + bss->broadcast_key_idx_max = 2; + bss->eap_reauth_period = 3600; + + bss->wpa_group_rekey = 600; + bss->wpa_gmk_rekey = 86400; + bss->wpa_key_mgmt = WPA_KEY_MGMT_PSK; + bss->wpa_pairwise = WPA_CIPHER_TKIP; + bss->wpa_group = WPA_CIPHER_TKIP; + bss->rsn_pairwise = 0; + + bss->max_num_sta = MAX_STA_COUNT; + + bss->dtim_period = 2; + + bss->radius_server_auth_port = 1812; + bss->ap_max_inactivity = AP_MAX_INACTIVITY; + bss->eapol_version = EAPOL_VERSION; + + bss->max_listen_interval = 65535; + +#ifdef CONFIG_IEEE80211W + bss->assoc_sa_query_max_timeout = 1000; + bss->assoc_sa_query_retry_timeout = 201; +#endif /* CONFIG_IEEE80211W */ +#ifdef EAP_FAST + /* both anonymous and authenticated provisioning */ + bss->eap_fast_prov = 3; + bss->pac_key_lifetime = 7 * 24 * 60 * 60; + bss->pac_key_refresh_time = 1 * 24 * 60 * 60; +#endif /* EAP_FAST */ +} + + +static struct hostapd_config * hostapd_config_defaults(void) +{ + struct hostapd_config *conf; + struct hostapd_bss_config *bss; + int i; + const int aCWmin = 15, aCWmax = 1024; + const struct hostapd_wme_ac_params ac_bk = + { aCWmin, aCWmax, 7, 0, 0 }; /* background traffic */ + const struct hostapd_wme_ac_params ac_be = + { aCWmin, aCWmax, 3, 0, 0 }; /* best effort traffic */ + const struct hostapd_wme_ac_params ac_vi = /* video traffic */ + { aCWmin >> 1, aCWmin, 2, 3000 / 32, 1 }; + const struct hostapd_wme_ac_params ac_vo = /* voice traffic */ + { aCWmin >> 2, aCWmin >> 1, 2, 1500 / 32, 1 }; + + conf = os_zalloc(sizeof(*conf)); + bss = os_zalloc(sizeof(*bss)); + if (conf == NULL || bss == NULL) { + wpa_printf(MSG_ERROR, "Failed to allocate memory for " + "configuration data."); + os_free(conf); + os_free(bss); + return NULL; + } + + /* set default driver based on configuration */ + conf->driver = hostapd_drivers[0]; + if (conf->driver == NULL) { + wpa_printf(MSG_ERROR, "No driver wrappers registered!"); + os_free(conf); + os_free(bss); + return NULL; + } + + bss->radius = os_zalloc(sizeof(*bss->radius)); + if (bss->radius == NULL) { + os_free(conf); + os_free(bss); + return NULL; + } + + hostapd_config_defaults_bss(bss); + + conf->num_bss = 1; + conf->bss = bss; + + conf->beacon_int = 100; + conf->rts_threshold = -1; /* use driver default: 2347 */ + conf->fragm_threshold = -1; /* user driver default: 2346 */ + conf->send_probe_response = 1; + conf->bridge_packets = INTERNAL_BRIDGE_DO_NOT_CONTROL; + + for (i = 0; i < NUM_TX_QUEUES; i++) + conf->tx_queue[i].aifs = -1; /* use hw default */ + + conf->wme_ac_params[0] = ac_be; + conf->wme_ac_params[1] = ac_bk; + conf->wme_ac_params[2] = ac_vi; + conf->wme_ac_params[3] = ac_vo; + +#ifdef CONFIG_IEEE80211N + conf->ht_capab = HT_CAP_INFO_SMPS_DISABLED; +#endif /* CONFIG_IEEE80211N */ + + return conf; +} + + +int hostapd_mac_comp(const void *a, const void *b) +{ + return os_memcmp(a, b, sizeof(macaddr)); +} + + +int hostapd_mac_comp_empty(const void *a) +{ + macaddr empty = { 0 }; + return os_memcmp(a, empty, sizeof(macaddr)); +} + + +static int hostapd_acl_comp(const void *a, const void *b) +{ + const struct mac_acl_entry *aa = a; + const struct mac_acl_entry *bb = b; + return os_memcmp(aa->addr, bb->addr, sizeof(macaddr)); +} + + +static int hostapd_config_read_maclist(const char *fname, + struct mac_acl_entry **acl, int *num) +{ + FILE *f; + char buf[128], *pos; + int line = 0; + u8 addr[ETH_ALEN]; + struct mac_acl_entry *newacl; + int vlan_id; + + if (!fname) + return 0; + + f = fopen(fname, "r"); + if (!f) { + wpa_printf(MSG_ERROR, "MAC list file '%s' not found.", 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)) { + wpa_printf(MSG_ERROR, "Invalid MAC address '%s' at " + "line %d in '%s'", buf, line, fname); + fclose(f); + return -1; + } + + vlan_id = 0; + pos = buf; + while (*pos != '\0' && *pos != ' ' && *pos != '\t') + pos++; + while (*pos == ' ' || *pos == '\t') + pos++; + if (*pos != '\0') + vlan_id = atoi(pos); + + newacl = os_realloc(*acl, (*num + 1) * sizeof(**acl)); + if (newacl == NULL) { + wpa_printf(MSG_ERROR, "MAC list reallocation failed"); + fclose(f); + return -1; + } + + *acl = newacl; + os_memcpy((*acl)[*num].addr, addr, ETH_ALEN); + (*acl)[*num].vlan_id = vlan_id; + (*num)++; + } + + fclose(f); + + qsort(*acl, *num, sizeof(**acl), hostapd_acl_comp); + + return 0; +} + + +static int hostapd_config_read_wpa_psk(const char *fname, + struct hostapd_ssid *ssid) +{ + 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) { + wpa_printf(MSG_ERROR, "WPA PSK file '%s' not found.", 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)) { + wpa_printf(MSG_ERROR, "Invalid MAC address '%s' on " + "line %d in '%s'", buf, line, fname); + ret = -1; + break; + } + + psk = os_zalloc(sizeof(*psk)); + if (psk == NULL) { + wpa_printf(MSG_ERROR, "WPA PSK allocation failed"); + ret = -1; + break; + } + if (is_zero_ether_addr(addr)) + psk->group = 1; + else + os_memcpy(psk->addr, addr, ETH_ALEN); + + pos = buf + 17; + if (*pos == '\0') { + wpa_printf(MSG_ERROR, "No PSK on line %d in '%s'", + line, fname); + os_free(psk); + ret = -1; + break; + } + pos++; + + ok = 0; + len = os_strlen(pos); + if (len == 64 && hexstr2bin(pos, psk->psk, PMK_LEN) == 0) + ok = 1; + else if (len >= 8 && len < 64) { + pbkdf2_sha1(pos, ssid->ssid, ssid->ssid_len, + 4096, psk->psk, PMK_LEN); + ok = 1; + } + if (!ok) { + wpa_printf(MSG_ERROR, "Invalid PSK '%s' on line %d in " + "'%s'", pos, line, fname); + os_free(psk); + ret = -1; + break; + } + + psk->next = ssid->wpa_psk; + ssid->wpa_psk = psk; + } + + fclose(f); + + return ret; +} + + +int hostapd_setup_wpa_psk(struct hostapd_bss_config *conf) +{ + struct hostapd_ssid *ssid = &conf->ssid; + + if (ssid->wpa_passphrase != NULL) { + if (ssid->wpa_psk != NULL) { + wpa_printf(MSG_ERROR, "Warning: both WPA PSK and " + "passphrase set. Using passphrase."); + os_free(ssid->wpa_psk); + } + ssid->wpa_psk = os_zalloc(sizeof(struct hostapd_wpa_psk)); + if (ssid->wpa_psk == NULL) { + wpa_printf(MSG_ERROR, "Unable to alloc space for PSK"); + return -1; + } + wpa_hexdump_ascii(MSG_DEBUG, "SSID", + (u8 *) ssid->ssid, ssid->ssid_len); + wpa_hexdump_ascii(MSG_DEBUG, "PSK (ASCII passphrase)", + (u8 *) ssid->wpa_passphrase, + os_strlen(ssid->wpa_passphrase)); + pbkdf2_sha1(ssid->wpa_passphrase, + ssid->ssid, ssid->ssid_len, + 4096, ssid->wpa_psk->psk, PMK_LEN); + wpa_hexdump(MSG_DEBUG, "PSK (from passphrase)", + ssid->wpa_psk->psk, PMK_LEN); + ssid->wpa_psk->group = 1; + } + + if (ssid->wpa_psk_file) { + if (hostapd_config_read_wpa_psk(ssid->wpa_psk_file, + &conf->ssid)) + return -1; + } + + return 0; +} + + +#ifdef EAP_SERVER +static int hostapd_config_read_eap_user(const char *fname, + struct hostapd_bss_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) { + wpa_printf(MSG_ERROR, "EAP user file '%s' not found.", 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] != '*') { + wpa_printf(MSG_ERROR, "Invalid EAP identity (no \" in " + "start) on line %d in '%s'", line, fname); + goto failed; + } + + user = os_zalloc(sizeof(*user)); + if (user == NULL) { + wpa_printf(MSG_ERROR, "EAP user allocation failed"); + goto failed; + } + user->force_version = -1; + + if (buf[0] == '*') { + pos = buf; + } else { + pos = buf + 1; + start = pos; + while (*pos != '"' && *pos != '\0') + pos++; + if (*pos == '\0') { + wpa_printf(MSG_ERROR, "Invalid EAP identity " + "(no \" in end) on line %d in '%s'", + line, fname); + goto failed; + } + + user->identity = os_malloc(pos - start); + if (user->identity == NULL) { + wpa_printf(MSG_ERROR, "Failed to allocate " + "memory for EAP identity"); + goto failed; + } + os_memcpy(user->identity, start, pos - start); + user->identity_len = pos - start; + + if (pos[0] == '"' && pos[1] == '*') { + user->wildcard_prefix = 1; + pos++; + } + } + pos++; + while (*pos == ' ' || *pos == '\t') + pos++; + + if (*pos == '\0') { + wpa_printf(MSG_ERROR, "No EAP method on line %d in " + "'%s'", 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 *pos3 = os_strchr(start, ','); + if (pos3) { + *pos3++ = '\0'; + } + user->methods[num_methods].method = + eap_server_get_type( + start, + &user->methods[num_methods].vendor); + if (user->methods[num_methods].vendor == + EAP_VENDOR_IETF && + user->methods[num_methods].method == EAP_TYPE_NONE) + { + if (os_strcmp(start, "TTLS-PAP") == 0) { + user->ttls_auth |= EAP_TTLS_AUTH_PAP; + goto skip_eap; + } + if (os_strcmp(start, "TTLS-CHAP") == 0) { + user->ttls_auth |= EAP_TTLS_AUTH_CHAP; + goto skip_eap; + } + if (os_strcmp(start, "TTLS-MSCHAP") == 0) { + user->ttls_auth |= + EAP_TTLS_AUTH_MSCHAP; + goto skip_eap; + } + if (os_strcmp(start, "TTLS-MSCHAPV2") == 0) { + user->ttls_auth |= + EAP_TTLS_AUTH_MSCHAPV2; + goto skip_eap; + } + wpa_printf(MSG_ERROR, "Unsupported EAP type " + "'%s' on line %d in '%s'", + start, line, fname); + goto failed; + } + + num_methods++; + if (num_methods >= EAP_USER_MAX_METHODS) + break; + skip_eap: + if (pos3 == NULL) + break; + start = pos3; + } + if (num_methods == 0 && user->ttls_auth == 0) { + wpa_printf(MSG_ERROR, "No EAP types configured on " + "line %d in '%s'", line, fname); + goto failed; + } + + if (pos == NULL) + goto done; + + while (*pos == ' ' || *pos == '\t') + pos++; + if (*pos == '\0') + goto done; + + if (os_strncmp(pos, "[ver=0]", 7) == 0) { + user->force_version = 0; + goto done; + } + + if (os_strncmp(pos, "[ver=1]", 7) == 0) { + user->force_version = 1; + goto done; + } + + if (os_strncmp(pos, "[2]", 3) == 0) { + user->phase2 = 1; + goto done; + } + + if (*pos == '"') { + pos++; + start = pos; + while (*pos != '"' && *pos != '\0') + pos++; + if (*pos == '\0') { + wpa_printf(MSG_ERROR, "Invalid EAP password " + "(no \" in end) on line %d in '%s'", + line, fname); + goto failed; + } + + user->password = os_malloc(pos - start); + if (user->password == NULL) { + wpa_printf(MSG_ERROR, "Failed to allocate " + "memory for EAP password"); + goto failed; + } + os_memcpy(user->password, start, pos - start); + user->password_len = pos - start; + + pos++; + } else if (os_strncmp(pos, "hash:", 5) == 0) { + pos += 5; + pos2 = pos; + while (*pos2 != '\0' && *pos2 != ' ' && + *pos2 != '\t' && *pos2 != '#') + pos2++; + if (pos2 - pos != 32) { + wpa_printf(MSG_ERROR, "Invalid password hash " + "on line %d in '%s'", line, fname); + goto failed; + } + user->password = os_malloc(16); + if (user->password == NULL) { + wpa_printf(MSG_ERROR, "Failed to allocate " + "memory for EAP password hash"); + goto failed; + } + if (hexstr2bin(pos, user->password, 16) < 0) { + wpa_printf(MSG_ERROR, "Invalid hash password " + "on line %d in '%s'", line, fname); + goto failed; + } + user->password_len = 16; + user->password_hash = 1; + pos = pos2; + } else { + pos2 = pos; + while (*pos2 != '\0' && *pos2 != ' ' && + *pos2 != '\t' && *pos2 != '#') + pos2++; + if ((pos2 - pos) & 1) { + wpa_printf(MSG_ERROR, "Invalid hex password " + "on line %d in '%s'", line, fname); + goto failed; + } + user->password = os_malloc((pos2 - pos) / 2); + if (user->password == NULL) { + wpa_printf(MSG_ERROR, "Failed to allocate " + "memory for EAP password"); + goto failed; + } + if (hexstr2bin(pos, user->password, + (pos2 - pos) / 2) < 0) { + wpa_printf(MSG_ERROR, "Invalid hex password " + "on line %d in '%s'", line, fname); + goto failed; + } + user->password_len = (pos2 - pos) / 2; + pos = pos2; + } + + while (*pos == ' ' || *pos == '\t') + pos++; + if (os_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) { + os_free(user->password); + os_free(user->identity); + os_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 = os_realloc(*server, (*num_server + 1) * sizeof(*nserv)); + if (nserv == NULL) + return -1; + + *server = nserv; + nserv = &nserv[*num_server]; + (*num_server)++; + (*curr_serv) = nserv; + + os_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 = os_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 (os_strcmp(start, "WPA-PSK") == 0) + val |= WPA_KEY_MGMT_PSK; + else if (os_strcmp(start, "WPA-EAP") == 0) + val |= WPA_KEY_MGMT_IEEE8021X; +#ifdef CONFIG_IEEE80211R + else if (os_strcmp(start, "FT-PSK") == 0) + val |= WPA_KEY_MGMT_FT_PSK; + else if (os_strcmp(start, "FT-EAP") == 0) + val |= WPA_KEY_MGMT_FT_IEEE8021X; +#endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_IEEE80211W + else if (os_strcmp(start, "WPA-PSK-SHA256") == 0) + val |= WPA_KEY_MGMT_PSK_SHA256; + else if (os_strcmp(start, "WPA-EAP-SHA256") == 0) + val |= WPA_KEY_MGMT_IEEE8021X_SHA256; +#endif /* CONFIG_IEEE80211W */ + else { + wpa_printf(MSG_ERROR, "Line %d: invalid key_mgmt '%s'", + line, start); + os_free(buf); + return -1; + } + + if (last) + break; + start = end + 1; + } + + os_free(buf); + if (val == 0) { + wpa_printf(MSG_ERROR, "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 = os_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 (os_strcmp(start, "CCMP") == 0) + val |= WPA_CIPHER_CCMP; + else if (os_strcmp(start, "TKIP") == 0) + val |= WPA_CIPHER_TKIP; + else if (os_strcmp(start, "WEP104") == 0) + val |= WPA_CIPHER_WEP104; + else if (os_strcmp(start, "WEP40") == 0) + val |= WPA_CIPHER_WEP40; + else if (os_strcmp(start, "NONE") == 0) + val |= WPA_CIPHER_NONE; + else { + wpa_printf(MSG_ERROR, "Line %d: invalid cipher '%s'.", + line, start); + os_free(buf); + return -1; + } + + if (last) + break; + start = end + 1; + } + os_free(buf); + + if (val == 0) { + wpa_printf(MSG_ERROR, "Line %d: no cipher values configured.", + line); + return -1; + } + return val; +} + + +static int hostapd_config_check_bss(struct hostapd_bss_config *bss, + struct hostapd_config *conf) +{ + if (bss->ieee802_1x && !bss->eap_server && + !bss->radius->auth_servers) { + wpa_printf(MSG_ERROR, "Invalid IEEE 802.1X configuration (no " + "EAP authenticator configured)."); + return -1; + } + + if (bss->wpa && (bss->wpa_key_mgmt & WPA_KEY_MGMT_PSK) && + bss->ssid.wpa_psk == NULL && bss->ssid.wpa_passphrase == NULL && + bss->ssid.wpa_psk_file == NULL) { + wpa_printf(MSG_ERROR, "WPA-PSK enabled, but PSK or passphrase " + "is not configured."); + return -1; + } + + if (hostapd_mac_comp_empty(bss->bssid) != 0) { + size_t i; + + for (i = 0; i < conf->num_bss; i++) { + if ((&conf->bss[i] != bss) && + (hostapd_mac_comp(conf->bss[i].bssid, + bss->bssid) == 0)) { + wpa_printf(MSG_ERROR, "Duplicate BSSID " MACSTR + " on interface '%s' and '%s'.", + MAC2STR(bss->bssid), + conf->bss[i].iface, bss->iface); + return -1; + } + } + } + +#ifdef CONFIG_IEEE80211R + if ((bss->wpa_key_mgmt & + (WPA_KEY_MGMT_FT_PSK | WPA_KEY_MGMT_FT_IEEE8021X)) && + (bss->nas_identifier == NULL || + os_strlen(bss->nas_identifier) < 1 || + os_strlen(bss->nas_identifier) > FT_R0KH_ID_MAX_LEN)) { + wpa_printf(MSG_ERROR, "FT (IEEE 802.11r) requires " + "nas_identifier to be configured as a 1..48 octet " + "string"); + return -1; + } +#endif /* CONFIG_IEEE80211R */ + +#ifdef CONFIG_IEEE80211N + if (conf->ieee80211n && bss->wpa && + !(bss->wpa_pairwise & WPA_CIPHER_CCMP) && + !(bss->rsn_pairwise & WPA_CIPHER_CCMP)) { + wpa_printf(MSG_ERROR, "HT (IEEE 802.11n) with WPA/WPA2 " + "requires CCMP to be enabled"); + return -1; + } +#endif /* CONFIG_IEEE80211N */ + + return 0; +} + + +static int hostapd_config_check(struct hostapd_config *conf) +{ + size_t i; + + if (conf->ieee80211d && (!conf->country[0] || !conf->country[1])) { + wpa_printf(MSG_ERROR, "Cannot enable IEEE 802.11d without " + "setting the country_code"); + return -1; + } + + for (i = 0; i < conf->num_bss; i++) { + if (hostapd_config_check_bss(&conf->bss[i], conf)) + return -1; + } + + return 0; +} + + +static int hostapd_config_read_wep(struct hostapd_wep_keys *wep, int keyidx, + char *val) +{ + size_t len = os_strlen(val); + + if (keyidx < 0 || keyidx > 3 || wep->key[keyidx] != NULL) + return -1; + + if (val[0] == '"') { + if (len < 2 || val[len - 1] != '"') + return -1; + len -= 2; + wep->key[keyidx] = os_malloc(len); + if (wep->key[keyidx] == NULL) + return -1; + os_memcpy(wep->key[keyidx], val + 1, len); + wep->len[keyidx] = len; + } else { + if (len & 1) + return -1; + len /= 2; + wep->key[keyidx] = os_malloc(len); + if (wep->key[keyidx] == NULL) + return -1; + wep->len[keyidx] = len; + if (hexstr2bin(val, wep->key[keyidx], len) < 0) + return -1; + } + + wep->keys_set++; + + return 0; +} + + +static int hostapd_parse_rates(int **rate_list, char *val) +{ + int *list; + int count; + char *pos, *end; + + os_free(*rate_list); + *rate_list = NULL; + + pos = val; + count = 0; + while (*pos != '\0') { + if (*pos == ' ') + count++; + pos++; + } + + list = os_malloc(sizeof(int) * (count + 2)); + if (list == NULL) + return -1; + pos = val; + count = 0; + while (*pos != '\0') { + end = os_strchr(pos, ' '); + if (end) + *end = '\0'; + + list[count++] = atoi(pos); + if (!end) + break; + pos = end + 1; + } + list[count] = -1; + + *rate_list = list; + return 0; +} + + +static int hostapd_config_bss(struct hostapd_config *conf, const char *ifname) +{ + struct hostapd_bss_config *bss; + + if (*ifname == '\0') + return -1; + + bss = os_realloc(conf->bss, (conf->num_bss + 1) * + sizeof(struct hostapd_bss_config)); + if (bss == NULL) { + wpa_printf(MSG_ERROR, "Failed to allocate memory for " + "multi-BSS entry"); + return -1; + } + conf->bss = bss; + + bss = &(conf->bss[conf->num_bss]); + os_memset(bss, 0, sizeof(*bss)); + bss->radius = os_zalloc(sizeof(*bss->radius)); + if (bss->radius == NULL) { + wpa_printf(MSG_ERROR, "Failed to allocate memory for " + "multi-BSS RADIUS data"); + return -1; + } + + conf->num_bss++; + conf->last_bss = bss; + + hostapd_config_defaults_bss(bss); + os_strlcpy(bss->iface, ifname, sizeof(bss->iface)); + os_memcpy(bss->ssid.vlan, bss->iface, IFNAMSIZ + 1); + + return 0; +} + + +static int valid_cw(int cw) +{ + return (cw == 1 || cw == 3 || cw == 7 || cw == 15 || cw == 31 || + cw == 63 || cw == 127 || cw == 255 || cw == 511 || cw == 1023); +} + + +enum { + IEEE80211_TX_QUEUE_DATA0 = 0, /* used for EDCA AC_VO data */ + IEEE80211_TX_QUEUE_DATA1 = 1, /* used for EDCA AC_VI data */ + IEEE80211_TX_QUEUE_DATA2 = 2, /* used for EDCA AC_BE data */ + IEEE80211_TX_QUEUE_DATA3 = 3, /* used for EDCA AC_BK data */ + IEEE80211_TX_QUEUE_DATA4 = 4, + IEEE80211_TX_QUEUE_AFTER_BEACON = 6, + IEEE80211_TX_QUEUE_BEACON = 7 +}; + +static int hostapd_config_tx_queue(struct hostapd_config *conf, char *name, + char *val) +{ + int num; + char *pos; + struct hostapd_tx_queue_params *queue; + + /* skip 'tx_queue_' prefix */ + pos = name + 9; + if (os_strncmp(pos, "data", 4) == 0 && + pos[4] >= '0' && pos[4] <= '9' && pos[5] == '_') { + num = pos[4] - '0'; + pos += 6; + } else if (os_strncmp(pos, "after_beacon_", 13) == 0) { + num = IEEE80211_TX_QUEUE_AFTER_BEACON; + pos += 13; + } else if (os_strncmp(pos, "beacon_", 7) == 0) { + num = IEEE80211_TX_QUEUE_BEACON; + pos += 7; + } else { + wpa_printf(MSG_ERROR, "Unknown tx_queue name '%s'", pos); + return -1; + } + + queue = &conf->tx_queue[num]; + + if (os_strcmp(pos, "aifs") == 0) { + queue->aifs = atoi(val); + if (queue->aifs < 0 || queue->aifs > 255) { + wpa_printf(MSG_ERROR, "Invalid AIFS value %d", + queue->aifs); + return -1; + } + } else if (os_strcmp(pos, "cwmin") == 0) { + queue->cwmin = atoi(val); + if (!valid_cw(queue->cwmin)) { + wpa_printf(MSG_ERROR, "Invalid cwMin value %d", + queue->cwmin); + return -1; + } + } else if (os_strcmp(pos, "cwmax") == 0) { + queue->cwmax = atoi(val); + if (!valid_cw(queue->cwmax)) { + wpa_printf(MSG_ERROR, "Invalid cwMax value %d", + queue->cwmax); + return -1; + } + } else if (os_strcmp(pos, "burst") == 0) { + queue->burst = hostapd_config_read_int10(val); + } else { + wpa_printf(MSG_ERROR, "Unknown tx_queue field '%s'", pos); + return -1; + } + + queue->configured = 1; + + return 0; +} + + +static int hostapd_config_wme_ac(struct hostapd_config *conf, char *name, + char *val) +{ + int num, v; + char *pos; + struct hostapd_wme_ac_params *ac; + + /* skip 'wme_ac_' prefix */ + pos = name + 7; + if (os_strncmp(pos, "be_", 3) == 0) { + num = 0; + pos += 3; + } else if (os_strncmp(pos, "bk_", 3) == 0) { + num = 1; + pos += 3; + } else if (os_strncmp(pos, "vi_", 3) == 0) { + num = 2; + pos += 3; + } else if (os_strncmp(pos, "vo_", 3) == 0) { + num = 3; + pos += 3; + } else { + wpa_printf(MSG_ERROR, "Unknown wme name '%s'", pos); + return -1; + } + + ac = &conf->wme_ac_params[num]; + + if (os_strcmp(pos, "aifs") == 0) { + v = atoi(val); + if (v < 1 || v > 255) { + wpa_printf(MSG_ERROR, "Invalid AIFS value %d", v); + return -1; + } + ac->aifs = v; + } else if (os_strcmp(pos, "cwmin") == 0) { + v = atoi(val); + if (v < 0 || v > 12) { + wpa_printf(MSG_ERROR, "Invalid cwMin value %d", v); + return -1; + } + ac->cwmin = v; + } else if (os_strcmp(pos, "cwmax") == 0) { + v = atoi(val); + if (v < 0 || v > 12) { + wpa_printf(MSG_ERROR, "Invalid cwMax value %d", v); + return -1; + } + ac->cwmax = v; + } else if (os_strcmp(pos, "txop_limit") == 0) { + v = atoi(val); + if (v < 0 || v > 0xffff) { + wpa_printf(MSG_ERROR, "Invalid txop value %d", v); + return -1; + } + ac->txopLimit = v; + } else if (os_strcmp(pos, "acm") == 0) { + v = atoi(val); + if (v < 0 || v > 1) { + wpa_printf(MSG_ERROR, "Invalid acm value %d", v); + return -1; + } + ac->admission_control_mandatory = v; + } else { + wpa_printf(MSG_ERROR, "Unknown wme_ac_ field '%s'", pos); + return -1; + } + + return 0; +} + + +#ifdef CONFIG_IEEE80211R +static int add_r0kh(struct hostapd_bss_config *bss, char *value) +{ + struct ft_remote_r0kh *r0kh; + char *pos, *next; + + r0kh = os_zalloc(sizeof(*r0kh)); + if (r0kh == NULL) + return -1; + + /* 02:01:02:03:04:05 a.example.com 000102030405060708090a0b0c0d0e0f */ + pos = value; + next = os_strchr(pos, ' '); + if (next) + *next++ = '\0'; + if (next == NULL || hwaddr_aton(pos, r0kh->addr)) { + wpa_printf(MSG_ERROR, "Invalid R0KH MAC address: '%s'", pos); + os_free(r0kh); + return -1; + } + + pos = next; + next = os_strchr(pos, ' '); + if (next) + *next++ = '\0'; + if (next == NULL || next - pos > FT_R0KH_ID_MAX_LEN) { + wpa_printf(MSG_ERROR, "Invalid R0KH-ID: '%s'", pos); + os_free(r0kh); + return -1; + } + r0kh->id_len = next - pos - 1; + os_memcpy(r0kh->id, pos, r0kh->id_len); + + pos = next; + if (hexstr2bin(pos, r0kh->key, sizeof(r0kh->key))) { + wpa_printf(MSG_ERROR, "Invalid R0KH key: '%s'", pos); + os_free(r0kh); + return -1; + } + + r0kh->next = bss->r0kh_list; + bss->r0kh_list = r0kh; + + return 0; +} + + +static int add_r1kh(struct hostapd_bss_config *bss, char *value) +{ + struct ft_remote_r1kh *r1kh; + char *pos, *next; + + r1kh = os_zalloc(sizeof(*r1kh)); + if (r1kh == NULL) + return -1; + + /* 02:01:02:03:04:05 02:01:02:03:04:05 + * 000102030405060708090a0b0c0d0e0f */ + pos = value; + next = os_strchr(pos, ' '); + if (next) + *next++ = '\0'; + if (next == NULL || hwaddr_aton(pos, r1kh->addr)) { + wpa_printf(MSG_ERROR, "Invalid R1KH MAC address: '%s'", pos); + os_free(r1kh); + return -1; + } + + pos = next; + next = os_strchr(pos, ' '); + if (next) + *next++ = '\0'; + if (next == NULL || hwaddr_aton(pos, r1kh->id)) { + wpa_printf(MSG_ERROR, "Invalid R1KH-ID: '%s'", pos); + os_free(r1kh); + return -1; + } + + pos = next; + if (hexstr2bin(pos, r1kh->key, sizeof(r1kh->key))) { + wpa_printf(MSG_ERROR, "Invalid R1KH key: '%s'", pos); + os_free(r1kh); + return -1; + } + + r1kh->next = bss->r1kh_list; + bss->r1kh_list = r1kh; + + return 0; +} +#endif /* CONFIG_IEEE80211R */ + + +#ifdef CONFIG_IEEE80211N +static int hostapd_config_ht_capab(struct hostapd_config *conf, + const char *capab) +{ + if (os_strstr(capab, "[LDPC]")) + conf->ht_capab |= HT_CAP_INFO_LDPC_CODING_CAP; + if (os_strstr(capab, "[HT40-]")) { + conf->ht_capab |= HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET; + conf->secondary_channel = -1; + } + if (os_strstr(capab, "[HT40+]")) { + conf->ht_capab |= HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET; + conf->secondary_channel = 1; + } + if (os_strstr(capab, "[SMPS-STATIC]")) { + conf->ht_capab &= ~HT_CAP_INFO_SMPS_MASK; + conf->ht_capab |= HT_CAP_INFO_SMPS_STATIC; + } + if (os_strstr(capab, "[SMPS-DYNAMIC]")) { + conf->ht_capab &= ~HT_CAP_INFO_SMPS_MASK; + conf->ht_capab |= HT_CAP_INFO_SMPS_DYNAMIC; + } + if (os_strstr(capab, "[GF]")) + conf->ht_capab |= HT_CAP_INFO_GREEN_FIELD; + if (os_strstr(capab, "[SHORT-GI-20]")) + conf->ht_capab |= HT_CAP_INFO_SHORT_GI20MHZ; + if (os_strstr(capab, "[SHORT-GI-40]")) + conf->ht_capab |= HT_CAP_INFO_SHORT_GI40MHZ; + if (os_strstr(capab, "[TX-STBC]")) + conf->ht_capab |= HT_CAP_INFO_TX_STBC; + if (os_strstr(capab, "[RX-STBC1]")) { + conf->ht_capab &= ~HT_CAP_INFO_RX_STBC_MASK; + conf->ht_capab |= HT_CAP_INFO_RX_STBC_1; + } + if (os_strstr(capab, "[RX-STBC12]")) { + conf->ht_capab &= ~HT_CAP_INFO_RX_STBC_MASK; + conf->ht_capab |= HT_CAP_INFO_RX_STBC_12; + } + if (os_strstr(capab, "[RX-STBC123]")) { + conf->ht_capab &= ~HT_CAP_INFO_RX_STBC_MASK; + conf->ht_capab |= HT_CAP_INFO_RX_STBC_123; + } + if (os_strstr(capab, "[DELAYED-BA]")) + conf->ht_capab |= HT_CAP_INFO_DELAYED_BA; + if (os_strstr(capab, "[MAX-AMSDU-7935]")) + conf->ht_capab |= HT_CAP_INFO_MAX_AMSDU_SIZE; + if (os_strstr(capab, "[DSSS_CCK-40]")) + conf->ht_capab |= HT_CAP_INFO_DSSS_CCK40MHZ; + if (os_strstr(capab, "[PSMP]")) + conf->ht_capab |= HT_CAP_INFO_PSMP_SUPP; + if (os_strstr(capab, "[LSIG-TXOP-PROT]")) + conf->ht_capab |= HT_CAP_INFO_LSIG_TXOP_PROTECT_SUPPORT; + + return 0; +} +#endif /* CONFIG_IEEE80211N */ + + +/** + * hostapd_config_read - Read and parse a configuration file + * @fname: Configuration file name (including path, if needed) + * Returns: Allocated configuration data structure + */ +struct hostapd_config * hostapd_config_read(const char *fname) +{ + struct hostapd_config *conf; + struct hostapd_bss_config *bss; + FILE *f; + char buf[256], *pos; + int line = 0; + int errors = 0; + int pairwise; + size_t i; + + f = fopen(fname, "r"); + if (f == NULL) { + wpa_printf(MSG_ERROR, "Could not open configuration file '%s' " + "for reading.", fname); + return NULL; + } + + conf = hostapd_config_defaults(); + if (conf == NULL) { + fclose(f); + return NULL; + } + bss = conf->last_bss = conf->bss; + + while (fgets(buf, sizeof(buf), f)) { + bss = conf->last_bss; + line++; + + if (buf[0] == '#') + continue; + pos = buf; + while (*pos != '\0') { + if (*pos == '\n') { + *pos = '\0'; + break; + } + pos++; + } + if (buf[0] == '\0') + continue; + + pos = os_strchr(buf, '='); + if (pos == NULL) { + wpa_printf(MSG_ERROR, "Line %d: invalid line '%s'", + line, buf); + errors++; + continue; + } + *pos = '\0'; + pos++; + + if (os_strcmp(buf, "interface") == 0) { + os_strlcpy(conf->bss[0].iface, pos, + sizeof(conf->bss[0].iface)); + } else if (os_strcmp(buf, "bridge") == 0) { + os_strlcpy(bss->bridge, pos, sizeof(bss->bridge)); + } else if (os_strcmp(buf, "driver") == 0) { + int i; + /* clear to get error below if setting is invalid */ + conf->driver = NULL; + for (i = 0; hostapd_drivers[i]; i++) { + if (os_strcmp(pos, hostapd_drivers[i]->name) == + 0) { + conf->driver = hostapd_drivers[i]; + break; + } + } + if (conf->driver == NULL) { + wpa_printf(MSG_ERROR, "Line %d: invalid/" + "unknown driver '%s'", line, pos); + errors++; + } + } else if (os_strcmp(buf, "debug") == 0) { + wpa_printf(MSG_DEBUG, "Line %d: DEPRECATED: 'debug' " + "configuration variable is not used " + "anymore", line); + } else if (os_strcmp(buf, "logger_syslog_level") == 0) { + bss->logger_syslog_level = atoi(pos); + } else if (os_strcmp(buf, "logger_stdout_level") == 0) { + bss->logger_stdout_level = atoi(pos); + } else if (os_strcmp(buf, "logger_syslog") == 0) { + bss->logger_syslog = atoi(pos); + } else if (os_strcmp(buf, "logger_stdout") == 0) { + bss->logger_stdout = atoi(pos); + } else if (os_strcmp(buf, "dump_file") == 0) { + bss->dump_log_name = os_strdup(pos); + } else if (os_strcmp(buf, "ssid") == 0) { + bss->ssid.ssid_len = os_strlen(pos); + if (bss->ssid.ssid_len > HOSTAPD_MAX_SSID_LEN || + bss->ssid.ssid_len < 1) { + wpa_printf(MSG_ERROR, "Line %d: invalid SSID " + "'%s'", line, pos); + errors++; + } else { + os_memcpy(bss->ssid.ssid, pos, + bss->ssid.ssid_len); + bss->ssid.ssid[bss->ssid.ssid_len] = '\0'; + bss->ssid.ssid_set = 1; + } + } else if (os_strcmp(buf, "macaddr_acl") == 0) { + bss->macaddr_acl = atoi(pos); + if (bss->macaddr_acl != ACCEPT_UNLESS_DENIED && + bss->macaddr_acl != DENY_UNLESS_ACCEPTED && + bss->macaddr_acl != USE_EXTERNAL_RADIUS_AUTH) { + wpa_printf(MSG_ERROR, "Line %d: unknown " + "macaddr_acl %d", + line, bss->macaddr_acl); + } + } else if (os_strcmp(buf, "accept_mac_file") == 0) { + if (hostapd_config_read_maclist(pos, &bss->accept_mac, + &bss->num_accept_mac)) + { + wpa_printf(MSG_ERROR, "Line %d: Failed to " + "read accept_mac_file '%s'", + line, pos); + errors++; + } + } else if (os_strcmp(buf, "deny_mac_file") == 0) { + if (hostapd_config_read_maclist(pos, &bss->deny_mac, + &bss->num_deny_mac)) { + wpa_printf(MSG_ERROR, "Line %d: Failed to " + "read deny_mac_file '%s'", + line, pos); + errors++; + } + } else if (os_strcmp(buf, "ap_max_inactivity") == 0) { + bss->ap_max_inactivity = atoi(pos); + } else if (os_strcmp(buf, "country_code") == 0) { + os_memcpy(conf->country, pos, 2); + /* FIX: make this configurable */ + conf->country[2] = ' '; + } else if (os_strcmp(buf, "ieee80211d") == 0) { + conf->ieee80211d = atoi(pos); + } else if (os_strcmp(buf, "ieee8021x") == 0) { + bss->ieee802_1x = atoi(pos); + } else if (os_strcmp(buf, "eapol_version") == 0) { + bss->eapol_version = atoi(pos); + if (bss->eapol_version < 1 || + bss->eapol_version > 2) { + wpa_printf(MSG_ERROR, "Line %d: invalid EAPOL " + "version (%d): '%s'.", + line, bss->eapol_version, pos); + errors++; + } else + wpa_printf(MSG_DEBUG, "eapol_version=%d", + bss->eapol_version); +#ifdef EAP_SERVER + } else if (os_strcmp(buf, "eap_authenticator") == 0) { + bss->eap_server = atoi(pos); + wpa_printf(MSG_ERROR, "Line %d: obsolete " + "eap_authenticator used; this has been " + "renamed to eap_server", line); + } else if (os_strcmp(buf, "eap_server") == 0) { + bss->eap_server = atoi(pos); + } else if (os_strcmp(buf, "eap_user_file") == 0) { + if (hostapd_config_read_eap_user(pos, bss)) + errors++; + } else if (os_strcmp(buf, "ca_cert") == 0) { + os_free(bss->ca_cert); + bss->ca_cert = os_strdup(pos); + } else if (os_strcmp(buf, "server_cert") == 0) { + os_free(bss->server_cert); + bss->server_cert = os_strdup(pos); + } else if (os_strcmp(buf, "private_key") == 0) { + os_free(bss->private_key); + bss->private_key = os_strdup(pos); + } else if (os_strcmp(buf, "private_key_passwd") == 0) { + os_free(bss->private_key_passwd); + bss->private_key_passwd = os_strdup(pos); + } else if (os_strcmp(buf, "check_crl") == 0) { + bss->check_crl = atoi(pos); + } else if (os_strcmp(buf, "dh_file") == 0) { + os_free(bss->dh_file); + bss->dh_file = os_strdup(pos); +#ifdef EAP_FAST + } else if (os_strcmp(buf, "pac_opaque_encr_key") == 0) { + os_free(bss->pac_opaque_encr_key); + bss->pac_opaque_encr_key = os_malloc(16); + if (bss->pac_opaque_encr_key == NULL) { + wpa_printf(MSG_ERROR, "Line %d: No memory for " + "pac_opaque_encr_key", line); + errors++; + } else if (hexstr2bin(pos, bss->pac_opaque_encr_key, + 16)) { + wpa_printf(MSG_ERROR, "Line %d: Invalid " + "pac_opaque_encr_key", line); + errors++; + } + } else if (os_strcmp(buf, "eap_fast_a_id") == 0) { + size_t idlen = os_strlen(pos); + if (idlen & 1) { + wpa_printf(MSG_ERROR, "Line %d: Invalid " + "eap_fast_a_id", line); + errors++; + } else { + os_free(bss->eap_fast_a_id); + bss->eap_fast_a_id = os_malloc(idlen / 2); + if (bss->eap_fast_a_id == NULL || + hexstr2bin(pos, bss->eap_fast_a_id, + idlen / 2)) { + wpa_printf(MSG_ERROR, "Line %d: " + "Failed to parse " + "eap_fast_a_id", line); + errors++; + } else + bss->eap_fast_a_id_len = idlen / 2; + } + } else if (os_strcmp(buf, "eap_fast_a_id_info") == 0) { + os_free(bss->eap_fast_a_id_info); + bss->eap_fast_a_id_info = os_strdup(pos); + } else if (os_strcmp(buf, "eap_fast_prov") == 0) { + bss->eap_fast_prov = atoi(pos); + } else if (os_strcmp(buf, "pac_key_lifetime") == 0) { + bss->pac_key_lifetime = atoi(pos); + } else if (os_strcmp(buf, "pac_key_refresh_time") == 0) { + bss->pac_key_refresh_time = atoi(pos); +#endif /* EAP_FAST */ +#ifdef EAP_SIM + } else if (os_strcmp(buf, "eap_sim_db") == 0) { + os_free(bss->eap_sim_db); + bss->eap_sim_db = os_strdup(pos); + } else if (os_strcmp(buf, "eap_sim_aka_result_ind") == 0) { + bss->eap_sim_aka_result_ind = atoi(pos); +#endif /* EAP_SIM */ +#ifdef EAP_TNC + } else if (os_strcmp(buf, "tnc") == 0) { + bss->tnc = atoi(pos); +#endif /* EAP_TNC */ +#endif /* EAP_SERVER */ + } else if (os_strcmp(buf, "eap_message") == 0) { + char *term; + bss->eap_req_id_text = os_strdup(pos); + if (bss->eap_req_id_text == NULL) { + wpa_printf(MSG_ERROR, "Line %d: Failed to " + "allocate memory for " + "eap_req_id_text", line); + errors++; + continue; + } + bss->eap_req_id_text_len = + os_strlen(bss->eap_req_id_text); + term = os_strstr(bss->eap_req_id_text, "\\0"); + if (term) { + *term++ = '\0'; + os_memmove(term, term + 1, + bss->eap_req_id_text_len - + (term - bss->eap_req_id_text) - 1); + bss->eap_req_id_text_len--; + } + } else if (os_strcmp(buf, "wep_key_len_broadcast") == 0) { + bss->default_wep_key_len = atoi(pos); + if (bss->default_wep_key_len > 13) { + wpa_printf(MSG_ERROR, "Line %d: invalid WEP " + "key len %lu (= %lu bits)", line, + (unsigned long) + bss->default_wep_key_len, + (unsigned long) + bss->default_wep_key_len * 8); + errors++; + } + } else if (os_strcmp(buf, "wep_key_len_unicast") == 0) { + bss->individual_wep_key_len = atoi(pos); + if (bss->individual_wep_key_len < 0 || + bss->individual_wep_key_len > 13) { + wpa_printf(MSG_ERROR, "Line %d: invalid WEP " + "key len %d (= %d bits)", line, + bss->individual_wep_key_len, + bss->individual_wep_key_len * 8); + errors++; + } + } else if (os_strcmp(buf, "wep_rekey_period") == 0) { + bss->wep_rekeying_period = atoi(pos); + if (bss->wep_rekeying_period < 0) { + wpa_printf(MSG_ERROR, "Line %d: invalid " + "period %d", + line, bss->wep_rekeying_period); + errors++; + } + } else if (os_strcmp(buf, "eap_reauth_period") == 0) { + bss->eap_reauth_period = atoi(pos); + if (bss->eap_reauth_period < 0) { + wpa_printf(MSG_ERROR, "Line %d: invalid " + "period %d", + line, bss->eap_reauth_period); + errors++; + } + } else if (os_strcmp(buf, "eapol_key_index_workaround") == 0) { + bss->eapol_key_index_workaround = atoi(pos); +#ifdef CONFIG_IAPP + } else if (os_strcmp(buf, "iapp_interface") == 0) { + bss->ieee802_11f = 1; + os_strlcpy(bss->iapp_iface, pos, + sizeof(bss->iapp_iface)); +#endif /* CONFIG_IAPP */ + } else if (os_strcmp(buf, "own_ip_addr") == 0) { + if (hostapd_parse_ip_addr(pos, &bss->own_ip_addr)) { + wpa_printf(MSG_ERROR, "Line %d: invalid IP " + "address '%s'", line, pos); + errors++; + } + } else if (os_strcmp(buf, "nas_identifier") == 0) { + bss->nas_identifier = os_strdup(pos); + } else if (os_strcmp(buf, "auth_server_addr") == 0) { + if (hostapd_config_read_radius_addr( + &bss->radius->auth_servers, + &bss->radius->num_auth_servers, pos, 1812, + &bss->radius->auth_server)) { + wpa_printf(MSG_ERROR, "Line %d: invalid IP " + "address '%s'", line, pos); + errors++; + } + } else if (bss->radius->auth_server && + os_strcmp(buf, "auth_server_port") == 0) { + bss->radius->auth_server->port = atoi(pos); + } else if (bss->radius->auth_server && + os_strcmp(buf, "auth_server_shared_secret") == 0) { + int len = os_strlen(pos); + if (len == 0) { + /* RFC 2865, Ch. 3 */ + wpa_printf(MSG_ERROR, "Line %d: empty shared " + "secret is not allowed.", line); + errors++; + } + bss->radius->auth_server->shared_secret = + (u8 *) os_strdup(pos); + bss->radius->auth_server->shared_secret_len = len; + } else if (os_strcmp(buf, "acct_server_addr") == 0) { + if (hostapd_config_read_radius_addr( + &bss->radius->acct_servers, + &bss->radius->num_acct_servers, pos, 1813, + &bss->radius->acct_server)) { + wpa_printf(MSG_ERROR, "Line %d: invalid IP " + "address '%s'", line, pos); + errors++; + } + } else if (bss->radius->acct_server && + os_strcmp(buf, "acct_server_port") == 0) { + bss->radius->acct_server->port = atoi(pos); + } else if (bss->radius->acct_server && + os_strcmp(buf, "acct_server_shared_secret") == 0) { + int len = os_strlen(pos); + if (len == 0) { + /* RFC 2865, Ch. 3 */ + wpa_printf(MSG_ERROR, "Line %d: empty shared " + "secret is not allowed.", line); + errors++; + } + bss->radius->acct_server->shared_secret = + (u8 *) os_strdup(pos); + bss->radius->acct_server->shared_secret_len = len; + } else if (os_strcmp(buf, "radius_retry_primary_interval") == + 0) { + bss->radius->retry_primary_interval = atoi(pos); + } else if (os_strcmp(buf, "radius_acct_interim_interval") == 0) + { + bss->radius->acct_interim_interval = atoi(pos); + } else if (os_strcmp(buf, "auth_algs") == 0) { + bss->auth_algs = atoi(pos); + if (bss->auth_algs == 0) { + wpa_printf(MSG_ERROR, "Line %d: no " + "authentication algorithms allowed", + line); + errors++; + } + } else if (os_strcmp(buf, "max_num_sta") == 0) { + bss->max_num_sta = atoi(pos); + if (bss->max_num_sta < 0 || + bss->max_num_sta > MAX_STA_COUNT) { + wpa_printf(MSG_ERROR, "Line %d: Invalid " + "max_num_sta=%d; allowed range " + "0..%d", line, bss->max_num_sta, + MAX_STA_COUNT); + errors++; + } + } else if (os_strcmp(buf, "wpa") == 0) { + bss->wpa = atoi(pos); + } else if (os_strcmp(buf, "wpa_group_rekey") == 0) { + bss->wpa_group_rekey = atoi(pos); + } else if (os_strcmp(buf, "wpa_strict_rekey") == 0) { + bss->wpa_strict_rekey = atoi(pos); + } else if (os_strcmp(buf, "wpa_gmk_rekey") == 0) { + bss->wpa_gmk_rekey = atoi(pos); + } else if (os_strcmp(buf, "wpa_ptk_rekey") == 0) { + bss->wpa_ptk_rekey = atoi(pos); + } else if (os_strcmp(buf, "wpa_passphrase") == 0) { + int len = os_strlen(pos); + if (len < 8 || len > 63) { + wpa_printf(MSG_ERROR, "Line %d: invalid WPA " + "passphrase length %d (expected " + "8..63)", line, len); + errors++; + } else { + os_free(bss->ssid.wpa_passphrase); + bss->ssid.wpa_passphrase = os_strdup(pos); + } + } else if (os_strcmp(buf, "wpa_psk") == 0) { + os_free(bss->ssid.wpa_psk); + bss->ssid.wpa_psk = + os_zalloc(sizeof(struct hostapd_wpa_psk)); + if (bss->ssid.wpa_psk == NULL) + errors++; + else if (hexstr2bin(pos, bss->ssid.wpa_psk->psk, + PMK_LEN) || + pos[PMK_LEN * 2] != '\0') { + wpa_printf(MSG_ERROR, "Line %d: Invalid PSK " + "'%s'.", line, pos); + errors++; + } else { + bss->ssid.wpa_psk->group = 1; + } + } else if (os_strcmp(buf, "wpa_psk_file") == 0) { + os_free(bss->ssid.wpa_psk_file); + bss->ssid.wpa_psk_file = os_strdup(pos); + if (!bss->ssid.wpa_psk_file) { + wpa_printf(MSG_ERROR, "Line %d: allocation " + "failed", line); + errors++; + } + } else if (os_strcmp(buf, "wpa_key_mgmt") == 0) { + bss->wpa_key_mgmt = + hostapd_config_parse_key_mgmt(line, pos); + if (bss->wpa_key_mgmt == -1) + errors++; + } else if (os_strcmp(buf, "wpa_pairwise") == 0) { + bss->wpa_pairwise = + hostapd_config_parse_cipher(line, pos); + if (bss->wpa_pairwise == -1 || + bss->wpa_pairwise == 0) + errors++; + else if (bss->wpa_pairwise & + (WPA_CIPHER_NONE | WPA_CIPHER_WEP40 | + WPA_CIPHER_WEP104)) { + wpa_printf(MSG_ERROR, "Line %d: unsupported " + "pairwise cipher suite '%s'", + bss->wpa_pairwise, pos); + errors++; + } + } else if (os_strcmp(buf, "rsn_pairwise") == 0) { + bss->rsn_pairwise = + hostapd_config_parse_cipher(line, pos); + if (bss->rsn_pairwise == -1 || + bss->rsn_pairwise == 0) + errors++; + else if (bss->rsn_pairwise & + (WPA_CIPHER_NONE | WPA_CIPHER_WEP40 | + WPA_CIPHER_WEP104)) { + wpa_printf(MSG_ERROR, "Line %d: unsupported " + "pairwise cipher suite '%s'", + bss->rsn_pairwise, pos); + errors++; + } +#ifdef CONFIG_RSN_PREAUTH + } else if (os_strcmp(buf, "rsn_preauth") == 0) { + bss->rsn_preauth = atoi(pos); + } else if (os_strcmp(buf, "rsn_preauth_interfaces") == 0) { + bss->rsn_preauth_interfaces = os_strdup(pos); +#endif /* CONFIG_RSN_PREAUTH */ +#ifdef CONFIG_PEERKEY + } else if (os_strcmp(buf, "peerkey") == 0) { + bss->peerkey = atoi(pos); +#endif /* CONFIG_PEERKEY */ +#ifdef CONFIG_IEEE80211R + } else if (os_strcmp(buf, "mobility_domain") == 0) { + if (os_strlen(pos) != 2 * MOBILITY_DOMAIN_ID_LEN || + hexstr2bin(pos, bss->mobility_domain, + MOBILITY_DOMAIN_ID_LEN) != 0) { + wpa_printf(MSG_DEBUG, "Line %d: Invalid " + "mobility_domain '%s'", line, pos); + errors++; + continue; + } + } else if (os_strcmp(buf, "r1_key_holder") == 0) { + if (os_strlen(pos) != 2 * FT_R1KH_ID_LEN || + hexstr2bin(pos, bss->r1_key_holder, + FT_R1KH_ID_LEN) != 0) { + wpa_printf(MSG_DEBUG, "Line %d: Invalid " + "r1_key_holder '%s'", line, pos); + errors++; + continue; + } + } else if (os_strcmp(buf, "r0_key_lifetime") == 0) { + bss->r0_key_lifetime = atoi(pos); + } else if (os_strcmp(buf, "reassociation_deadline") == 0) { + bss->reassociation_deadline = atoi(pos); + } else if (os_strcmp(buf, "r0kh") == 0) { + if (add_r0kh(bss, pos) < 0) { + wpa_printf(MSG_DEBUG, "Line %d: Invalid " + "r0kh '%s'", line, pos); + errors++; + continue; + } + } else if (os_strcmp(buf, "r1kh") == 0) { + if (add_r1kh(bss, pos) < 0) { + wpa_printf(MSG_DEBUG, "Line %d: Invalid " + "r1kh '%s'", line, pos); + errors++; + continue; + } + } else if (os_strcmp(buf, "pmk_r1_push") == 0) { + bss->pmk_r1_push = atoi(pos); +#endif /* CONFIG_IEEE80211R */ + } else if (os_strcmp(buf, "ctrl_interface") == 0) { + os_free(bss->ctrl_interface); + bss->ctrl_interface = os_strdup(pos); + } else if (os_strcmp(buf, "ctrl_interface_group") == 0) { +#ifndef CONFIG_NATIVE_WINDOWS + struct group *grp; + char *endp; + const char *group = pos; + + grp = getgrnam(group); + if (grp) { + bss->ctrl_interface_gid = grp->gr_gid; + bss->ctrl_interface_gid_set = 1; + wpa_printf(MSG_DEBUG, "ctrl_interface_group=%d" + " (from group name '%s')", + bss->ctrl_interface_gid, group); + continue; + } + + /* Group name not found - try to parse this as gid */ + bss->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; + } + bss->ctrl_interface_gid_set = 1; + wpa_printf(MSG_DEBUG, "ctrl_interface_group=%d", + bss->ctrl_interface_gid); +#endif /* CONFIG_NATIVE_WINDOWS */ +#ifdef RADIUS_SERVER + } else if (os_strcmp(buf, "radius_server_clients") == 0) { + os_free(bss->radius_server_clients); + bss->radius_server_clients = os_strdup(pos); + } else if (os_strcmp(buf, "radius_server_auth_port") == 0) { + bss->radius_server_auth_port = atoi(pos); + } else if (os_strcmp(buf, "radius_server_ipv6") == 0) { + bss->radius_server_ipv6 = atoi(pos); +#endif /* RADIUS_SERVER */ + } else if (os_strcmp(buf, "test_socket") == 0) { + os_free(bss->test_socket); + bss->test_socket = os_strdup(pos); + } else if (os_strcmp(buf, "use_pae_group_addr") == 0) { + bss->use_pae_group_addr = atoi(pos); + } else if (os_strcmp(buf, "hw_mode") == 0) { + if (os_strcmp(pos, "a") == 0) + conf->hw_mode = HOSTAPD_MODE_IEEE80211A; + else if (os_strcmp(pos, "b") == 0) + conf->hw_mode = HOSTAPD_MODE_IEEE80211B; + else if (os_strcmp(pos, "g") == 0) + conf->hw_mode = HOSTAPD_MODE_IEEE80211G; + else { + wpa_printf(MSG_ERROR, "Line %d: unknown " + "hw_mode '%s'", line, pos); + errors++; + } + } else if (os_strcmp(buf, "channel") == 0) { + conf->channel = atoi(pos); + } else if (os_strcmp(buf, "beacon_int") == 0) { + int val = atoi(pos); + /* MIB defines range as 1..65535, but very small values + * cause problems with the current implementation. + * Since it is unlikely that this small numbers are + * useful in real life scenarios, do not allow beacon + * period to be set below 15 TU. */ + if (val < 15 || val > 65535) { + wpa_printf(MSG_ERROR, "Line %d: invalid " + "beacon_int %d (expected " + "15..65535)", line, val); + errors++; + } else + conf->beacon_int = val; + } else if (os_strcmp(buf, "dtim_period") == 0) { + bss->dtim_period = atoi(pos); + if (bss->dtim_period < 1 || bss->dtim_period > 255) { + wpa_printf(MSG_ERROR, "Line %d: invalid " + "dtim_period %d", + line, bss->dtim_period); + errors++; + } + } else if (os_strcmp(buf, "rts_threshold") == 0) { + conf->rts_threshold = atoi(pos); + if (conf->rts_threshold < 0 || + conf->rts_threshold > 2347) { + wpa_printf(MSG_ERROR, "Line %d: invalid " + "rts_threshold %d", + line, conf->rts_threshold); + errors++; + } + } else if (os_strcmp(buf, "fragm_threshold") == 0) { + conf->fragm_threshold = atoi(pos); + if (conf->fragm_threshold < 256 || + conf->fragm_threshold > 2346) { + wpa_printf(MSG_ERROR, "Line %d: invalid " + "fragm_threshold %d", + line, conf->fragm_threshold); + errors++; + } + } else if (os_strcmp(buf, "send_probe_response") == 0) { + int val = atoi(pos); + if (val != 0 && val != 1) { + wpa_printf(MSG_ERROR, "Line %d: invalid " + "send_probe_response %d (expected " + "0 or 1)", line, val); + } else + conf->send_probe_response = val; + } else if (os_strcmp(buf, "supported_rates") == 0) { + if (hostapd_parse_rates(&conf->supported_rates, pos)) { + wpa_printf(MSG_ERROR, "Line %d: invalid rate " + "list", line); + errors++; + } + } else if (os_strcmp(buf, "basic_rates") == 0) { + if (hostapd_parse_rates(&conf->basic_rates, pos)) { + wpa_printf(MSG_ERROR, "Line %d: invalid rate " + "list", line); + errors++; + } + } else if (os_strcmp(buf, "preamble") == 0) { + if (atoi(pos)) + conf->preamble = SHORT_PREAMBLE; + else + conf->preamble = LONG_PREAMBLE; + } else if (os_strcmp(buf, "ignore_broadcast_ssid") == 0) { + bss->ignore_broadcast_ssid = atoi(pos); + } else if (os_strcmp(buf, "bridge_packets") == 0) { + conf->bridge_packets = atoi(pos); + } else if (os_strcmp(buf, "wep_default_key") == 0) { + bss->ssid.wep.idx = atoi(pos); + if (bss->ssid.wep.idx > 3) { + wpa_printf(MSG_ERROR, "Invalid " + "wep_default_key index %d", + bss->ssid.wep.idx); + errors++; + } + } else if (os_strcmp(buf, "wep_key0") == 0 || + os_strcmp(buf, "wep_key1") == 0 || + os_strcmp(buf, "wep_key2") == 0 || + os_strcmp(buf, "wep_key3") == 0) { + if (hostapd_config_read_wep(&bss->ssid.wep, + buf[7] - '0', pos)) { + wpa_printf(MSG_ERROR, "Line %d: invalid WEP " + "key '%s'", line, buf); + errors++; + } + } else if (os_strcmp(buf, "dynamic_vlan") == 0) { + bss->ssid.dynamic_vlan = atoi(pos); + } else if (os_strcmp(buf, "vlan_file") == 0) { + if (hostapd_config_read_vlan_file(bss, pos)) { + wpa_printf(MSG_ERROR, "Line %d: failed to " + "read VLAN file '%s'", line, pos); + errors++; + } +#ifdef CONFIG_FULL_DYNAMIC_VLAN + } else if (os_strcmp(buf, "vlan_tagged_interface") == 0) { + bss->ssid.vlan_tagged_interface = os_strdup(pos); +#endif /* CONFIG_FULL_DYNAMIC_VLAN */ + } else if (os_strcmp(buf, "passive_scan_interval") == 0) { + conf->passive_scan_interval = atoi(pos); + } else if (os_strcmp(buf, "passive_scan_listen") == 0) { + conf->passive_scan_listen = atoi(pos); + } else if (os_strcmp(buf, "passive_scan_mode") == 0) { + conf->passive_scan_mode = atoi(pos); + } else if (os_strcmp(buf, "ap_table_max_size") == 0) { + conf->ap_table_max_size = atoi(pos); + } else if (os_strcmp(buf, "ap_table_expiration_time") == 0) { + conf->ap_table_expiration_time = atoi(pos); + } else if (os_strncmp(buf, "tx_queue_", 9) == 0) { + if (hostapd_config_tx_queue(conf, buf, pos)) { + wpa_printf(MSG_ERROR, "Line %d: invalid TX " + "queue item", line); + errors++; + } + } else if (os_strcmp(buf, "wme_enabled") == 0) { + bss->wme_enabled = atoi(pos); + } else if (os_strncmp(buf, "wme_ac_", 7) == 0) { + if (hostapd_config_wme_ac(conf, buf, pos)) { + wpa_printf(MSG_ERROR, "Line %d: invalid wme " + "ac item", line); + errors++; + } + } else if (os_strcmp(buf, "bss") == 0) { + if (hostapd_config_bss(conf, pos)) { + wpa_printf(MSG_ERROR, "Line %d: invalid bss " + "item", line); + errors++; + } + } else if (os_strcmp(buf, "bssid") == 0) { + if (bss == conf->bss && + (!conf->driver || !conf->driver->init_bssid)) { + wpa_printf(MSG_ERROR, "Line %d: bssid item " + "not allowed for the default " + "interface and this driver", line); + errors++; + } else if (hwaddr_aton(pos, bss->bssid)) { + wpa_printf(MSG_ERROR, "Line %d: invalid bssid " + "item", line); + errors++; + } +#ifdef CONFIG_IEEE80211W + } else if (os_strcmp(buf, "ieee80211w") == 0) { + bss->ieee80211w = atoi(pos); + } else if (os_strcmp(buf, "assoc_sa_query_max_timeout") == 0) { + bss->assoc_sa_query_max_timeout = atoi(pos); + if (bss->assoc_sa_query_max_timeout == 0) { + wpa_printf(MSG_ERROR, "Line %d: invalid " + "assoc_sa_query_max_timeout", line); + errors++; + } + } else if (os_strcmp(buf, "assoc_sa_query_retry_timeout") == 0) + { + bss->assoc_sa_query_retry_timeout = atoi(pos); + if (bss->assoc_sa_query_retry_timeout == 0) { + wpa_printf(MSG_ERROR, "Line %d: invalid " + "assoc_sa_query_retry_timeout", + line); + errors++; + } +#endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_IEEE80211N + } else if (os_strcmp(buf, "ieee80211n") == 0) { + conf->ieee80211n = atoi(pos); + } else if (os_strcmp(buf, "ht_capab") == 0) { + if (hostapd_config_ht_capab(conf, pos) < 0) { + wpa_printf(MSG_ERROR, "Line %d: invalid " + "ht_capab", line); + errors++; + } +#endif /* CONFIG_IEEE80211N */ + } else if (os_strcmp(buf, "max_listen_interval") == 0) { + bss->max_listen_interval = atoi(pos); + } else if (os_strcmp(buf, "okc") == 0) { + bss->okc = atoi(pos); +#ifdef CONFIG_WPS + } else if (os_strcmp(buf, "wps_state") == 0) { + bss->wps_state = atoi(pos); + if (bss->wps_state < 0 || bss->wps_state > 2) { + wpa_printf(MSG_ERROR, "Line %d: invalid " + "wps_state", line); + errors++; + } + } else if (os_strcmp(buf, "ap_setup_locked") == 0) { + bss->ap_setup_locked = atoi(pos); + } else if (os_strcmp(buf, "uuid") == 0) { + if (uuid_str2bin(pos, bss->uuid)) { + wpa_printf(MSG_ERROR, "Line %d: invalid UUID", + line); + errors++; + } + } else if (os_strcmp(buf, "wps_pin_requests") == 0) { + os_free(bss->wps_pin_requests); + bss->wps_pin_requests = os_strdup(pos); + } else if (os_strcmp(buf, "device_name") == 0) { + if (os_strlen(pos) > 32) { + wpa_printf(MSG_ERROR, "Line %d: Too long " + "device_name", line); + errors++; + } + os_free(bss->device_name); + bss->device_name = os_strdup(pos); + } else if (os_strcmp(buf, "manufacturer") == 0) { + if (os_strlen(pos) > 64) { + wpa_printf(MSG_ERROR, "Line %d: Too long " + "manufacturer", line); + errors++; + } + os_free(bss->manufacturer); + bss->manufacturer = os_strdup(pos); + } else if (os_strcmp(buf, "model_name") == 0) { + if (os_strlen(pos) > 32) { + wpa_printf(MSG_ERROR, "Line %d: Too long " + "model_name", line); + errors++; + } + os_free(bss->model_name); + bss->model_name = os_strdup(pos); + } else if (os_strcmp(buf, "model_number") == 0) { + if (os_strlen(pos) > 32) { + wpa_printf(MSG_ERROR, "Line %d: Too long " + "model_number", line); + errors++; + } + os_free(bss->model_number); + bss->model_number = os_strdup(pos); + } else if (os_strcmp(buf, "serial_number") == 0) { + if (os_strlen(pos) > 32) { + wpa_printf(MSG_ERROR, "Line %d: Too long " + "serial_number", line); + errors++; + } + os_free(bss->serial_number); + bss->serial_number = os_strdup(pos); + } else if (os_strcmp(buf, "device_type") == 0) { + os_free(bss->device_type); + bss->device_type = os_strdup(pos); + } else if (os_strcmp(buf, "config_methods") == 0) { + os_free(bss->config_methods); + bss->config_methods = os_strdup(pos); + } else if (os_strcmp(buf, "os_version") == 0) { + if (hexstr2bin(pos, bss->os_version, 4)) { + wpa_printf(MSG_ERROR, "Line %d: invalid " + "os_version", line); + errors++; + } + } else if (os_strcmp(buf, "ap_pin") == 0) { + os_free(bss->ap_pin); + bss->ap_pin = os_strdup(pos); + } else if (os_strcmp(buf, "skip_cred_build") == 0) { + bss->skip_cred_build = atoi(pos); + } else if (os_strcmp(buf, "extra_cred") == 0) { + os_free(bss->extra_cred); + bss->extra_cred = + (u8 *) os_readfile(pos, &bss->extra_cred_len); + if (bss->extra_cred == NULL) { + wpa_printf(MSG_ERROR, "Line %d: could not " + "read Credentials from '%s'", + line, pos); + errors++; + } + } else if (os_strcmp(buf, "wps_cred_processing") == 0) { + bss->wps_cred_processing = atoi(pos); + } else if (os_strcmp(buf, "ap_settings") == 0) { + os_free(bss->ap_settings); + bss->ap_settings = + (u8 *) os_readfile(pos, &bss->ap_settings_len); + if (bss->ap_settings == NULL) { + wpa_printf(MSG_ERROR, "Line %d: could not " + "read AP Settings from '%s'", + line, pos); + errors++; + } + } else if (os_strcmp(buf, "upnp_iface") == 0) { + bss->upnp_iface = os_strdup(pos); + } else if (os_strcmp(buf, "friendly_name") == 0) { + os_free(bss->friendly_name); + bss->friendly_name = os_strdup(pos); + } else if (os_strcmp(buf, "manufacturer_url") == 0) { + os_free(bss->manufacturer_url); + bss->manufacturer_url = os_strdup(pos); + } else if (os_strcmp(buf, "model_description") == 0) { + os_free(bss->model_description); + bss->model_description = os_strdup(pos); + } else if (os_strcmp(buf, "model_url") == 0) { + os_free(bss->model_url); + bss->model_url = os_strdup(pos); + } else if (os_strcmp(buf, "upc") == 0) { + os_free(bss->upc); + bss->upc = os_strdup(pos); +#endif /* CONFIG_WPS */ + } else { + wpa_printf(MSG_ERROR, "Line %d: unknown configuration " + "item '%s'", line, buf); + errors++; + } + } + + fclose(f); + + if (bss->individual_wep_key_len == 0) { + /* individual keys are not use; can use key idx0 for broadcast + * keys */ + bss->broadcast_key_idx_min = 0; + } + + /* Select group cipher based on the enabled pairwise cipher suites */ + pairwise = 0; + if (bss->wpa & 1) + pairwise |= bss->wpa_pairwise; + if (bss->wpa & 2) { + if (bss->rsn_pairwise == 0) + bss->rsn_pairwise = bss->wpa_pairwise; + pairwise |= bss->rsn_pairwise; + } + if (pairwise & WPA_CIPHER_TKIP) + bss->wpa_group = WPA_CIPHER_TKIP; + else + bss->wpa_group = WPA_CIPHER_CCMP; + + for (i = 0; i < conf->num_bss; i++) { + bss = &conf->bss[i]; + + bss->radius->auth_server = bss->radius->auth_servers; + bss->radius->acct_server = bss->radius->acct_servers; + + if (bss->wpa && bss->ieee802_1x) { + bss->ssid.security_policy = SECURITY_WPA; + } else if (bss->wpa) { + bss->ssid.security_policy = SECURITY_WPA_PSK; + } else if (bss->ieee802_1x) { + bss->ssid.security_policy = SECURITY_IEEE_802_1X; + bss->ssid.wep.default_len = bss->default_wep_key_len; + } else if (bss->ssid.wep.keys_set) + bss->ssid.security_policy = SECURITY_STATIC_WEP; + else + bss->ssid.security_policy = SECURITY_PLAINTEXT; + } + + if (hostapd_config_check(conf)) + errors++; + + if (errors) { + wpa_printf(MSG_ERROR, "%d errors found in configuration file " + "'%s'", errors, fname); + hostapd_config_free(conf); + conf = NULL; + } + + return conf; +} + + +int hostapd_wep_key_cmp(struct hostapd_wep_keys *a, struct hostapd_wep_keys *b) +{ + int i; + + if (a->idx != b->idx || a->default_len != b->default_len) + return 1; + for (i = 0; i < NUM_WEP_KEYS; i++) + if (a->len[i] != b->len[i] || + os_memcmp(a->key[i], b->key[i], a->len[i]) != 0) + return 1; + return 0; +} + + +static void hostapd_config_free_radius(struct hostapd_radius_server *servers, + int num_servers) +{ + int i; + + for (i = 0; i < num_servers; i++) { + os_free(servers[i].shared_secret); + } + os_free(servers); +} + + +static void hostapd_config_free_eap_user(struct hostapd_eap_user *user) +{ + os_free(user->identity); + os_free(user->password); + os_free(user); +} + + +static void hostapd_config_free_wep(struct hostapd_wep_keys *keys) +{ + int i; + for (i = 0; i < NUM_WEP_KEYS; i++) { + os_free(keys->key[i]); + keys->key[i] = NULL; + } +} + + +static void hostapd_config_free_bss(struct hostapd_bss_config *conf) +{ + struct hostapd_wpa_psk *psk, *prev; + struct hostapd_eap_user *user, *prev_user; + + if (conf == NULL) + return; + + psk = conf->ssid.wpa_psk; + while (psk) { + prev = psk; + psk = psk->next; + os_free(prev); + } + + os_free(conf->ssid.wpa_passphrase); + os_free(conf->ssid.wpa_psk_file); +#ifdef CONFIG_FULL_DYNAMIC_VLAN + os_free(conf->ssid.vlan_tagged_interface); +#endif /* CONFIG_FULL_DYNAMIC_VLAN */ + + user = conf->eap_user; + while (user) { + prev_user = user; + user = user->next; + hostapd_config_free_eap_user(prev_user); + } + + os_free(conf->dump_log_name); + os_free(conf->eap_req_id_text); + os_free(conf->accept_mac); + os_free(conf->deny_mac); + os_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); + os_free(conf->rsn_preauth_interfaces); + os_free(conf->ctrl_interface); + os_free(conf->ca_cert); + os_free(conf->server_cert); + os_free(conf->private_key); + os_free(conf->private_key_passwd); + os_free(conf->dh_file); + os_free(conf->pac_opaque_encr_key); + os_free(conf->eap_fast_a_id); + os_free(conf->eap_fast_a_id_info); + os_free(conf->eap_sim_db); + os_free(conf->radius_server_clients); + os_free(conf->test_socket); + os_free(conf->radius); + hostapd_config_free_vlan(conf); + if (conf->ssid.dyn_vlan_keys) { + struct hostapd_ssid *ssid = &conf->ssid; + size_t i; + for (i = 0; i <= ssid->max_dyn_vlan_keys; i++) { + if (ssid->dyn_vlan_keys[i] == NULL) + continue; + hostapd_config_free_wep(ssid->dyn_vlan_keys[i]); + os_free(ssid->dyn_vlan_keys[i]); + } + os_free(ssid->dyn_vlan_keys); + ssid->dyn_vlan_keys = NULL; + } + +#ifdef CONFIG_IEEE80211R + { + struct ft_remote_r0kh *r0kh, *r0kh_prev; + struct ft_remote_r1kh *r1kh, *r1kh_prev; + + r0kh = conf->r0kh_list; + conf->r0kh_list = NULL; + while (r0kh) { + r0kh_prev = r0kh; + r0kh = r0kh->next; + os_free(r0kh_prev); + } + + r1kh = conf->r1kh_list; + conf->r1kh_list = NULL; + while (r1kh) { + r1kh_prev = r1kh; + r1kh = r1kh->next; + os_free(r1kh_prev); + } + } +#endif /* CONFIG_IEEE80211R */ + +#ifdef CONFIG_WPS + os_free(conf->wps_pin_requests); + os_free(conf->device_name); + os_free(conf->manufacturer); + os_free(conf->model_name); + os_free(conf->model_number); + os_free(conf->serial_number); + os_free(conf->device_type); + os_free(conf->config_methods); + os_free(conf->ap_pin); + os_free(conf->extra_cred); + os_free(conf->ap_settings); + os_free(conf->upnp_iface); + os_free(conf->friendly_name); + os_free(conf->manufacturer_url); + os_free(conf->model_description); + os_free(conf->model_url); + os_free(conf->upc); +#endif /* CONFIG_WPS */ +} + + +/** + * hostapd_config_free - Free hostapd configuration + * @conf: Configuration data from hostapd_config_read(). + */ +void hostapd_config_free(struct hostapd_config *conf) +{ + size_t i; + + if (conf == NULL) + return; + + for (i = 0; i < conf->num_bss; i++) + hostapd_config_free_bss(&conf->bss[i]); + os_free(conf->bss); + + os_free(conf); +} + + +/** + * hostapd_maclist_found - Find a MAC address from a list + * @list: MAC address list + * @num_entries: Number of addresses in the list + * @addr: Address to search for + * @vlan_id: Buffer for returning VLAN ID or %NULL if not needed + * Returns: 1 if address is in the list or 0 if not. + * + * Perform a binary search for given MAC address from a pre-sorted list. + */ +int hostapd_maclist_found(struct mac_acl_entry *list, int num_entries, + const u8 *addr, int *vlan_id) +{ + int start, end, middle, res; + + start = 0; + end = num_entries - 1; + + while (start <= end) { + middle = (start + end) / 2; + res = os_memcmp(list[middle].addr, addr, ETH_ALEN); + if (res == 0) { + if (vlan_id) + *vlan_id = list[middle].vlan_id; + return 1; + } + if (res < 0) + start = middle + 1; + else + end = middle - 1; + } + + return 0; +} + + +int hostapd_rate_found(int *list, int rate) +{ + int i; + + if (list == NULL) + return 0; + + for (i = 0; list[i] >= 0; i++) + if (list[i] == rate) + return 1; + + return 0; +} + + +const char * hostapd_get_vlan_id_ifname(struct hostapd_vlan *vlan, int vlan_id) +{ + struct hostapd_vlan *v = vlan; + while (v) { + if (v->vlan_id == vlan_id || v->vlan_id == VLAN_ID_WILDCARD) + return v->ifname; + v = v->next; + } + return NULL; +} + + +const u8 * hostapd_get_psk(const struct hostapd_bss_config *conf, + const u8 *addr, const u8 *prev_psk) +{ + struct hostapd_wpa_psk *psk; + int next_ok = prev_psk == NULL; + + for (psk = conf->ssid.wpa_psk; psk != NULL; psk = psk->next) { + if (next_ok && + (psk->group || os_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_bss_config *conf, const u8 *identity, + size_t identity_len, int phase2) +{ + struct hostapd_eap_user *user = conf->eap_user; + +#ifdef CONFIG_WPS + if (conf->wps_state && identity_len == WSC_ID_ENROLLEE_LEN && + os_memcmp(identity, WSC_ID_ENROLLEE, WSC_ID_ENROLLEE_LEN) == 0) { + static struct hostapd_eap_user wsc_enrollee; + os_memset(&wsc_enrollee, 0, sizeof(wsc_enrollee)); + wsc_enrollee.methods[0].method = eap_server_get_type( + "WSC", &wsc_enrollee.methods[0].vendor); + return &wsc_enrollee; + } + + if (conf->wps_state && conf->ap_pin && + identity_len == WSC_ID_REGISTRAR_LEN && + os_memcmp(identity, WSC_ID_REGISTRAR, WSC_ID_REGISTRAR_LEN) == 0) { + static struct hostapd_eap_user wsc_registrar; + os_memset(&wsc_registrar, 0, sizeof(wsc_registrar)); + wsc_registrar.methods[0].method = eap_server_get_type( + "WSC", &wsc_registrar.methods[0].vendor); + wsc_registrar.password = (u8 *) conf->ap_pin; + wsc_registrar.password_len = os_strlen(conf->ap_pin); + return &wsc_registrar; + } +#endif /* CONFIG_WPS */ + + while (user) { + if (!phase2 && user->identity == NULL) { + /* Wildcard match */ + break; + } + + if (user->phase2 == !!phase2 && user->wildcard_prefix && + identity_len >= user->identity_len && + os_memcmp(user->identity, identity, user->identity_len) == + 0) { + /* Wildcard prefix match */ + break; + } + + if (user->phase2 == !!phase2 && + user->identity_len == identity_len && + os_memcmp(user->identity, identity, identity_len) == 0) + break; + user = user->next; + } + + return user; +} diff --git a/contrib/wpa/hostapd/config.h b/contrib/wpa/hostapd/config.h new file mode 100644 index 0000000..a3247f2 --- /dev/null +++ b/contrib/wpa/hostapd/config.h @@ -0,0 +1,415 @@ +/* + * hostapd / Configuration file + * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi> + * Copyright (c) 2007-2008, Intel Corporation + * + * 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 CONFIG_H +#define CONFIG_H + +#include "defs.h" +#include "ip_addr.h" +#include "wpa_common.h" + +#ifndef IFNAMSIZ +#define IFNAMSIZ 16 +#endif + +typedef u8 macaddr[ETH_ALEN]; + +struct mac_acl_entry { + macaddr addr; + int vlan_id; +}; + +struct hostapd_radius_servers; +struct ft_remote_r0kh; +struct ft_remote_r1kh; + +#define HOSTAPD_MAX_SSID_LEN 32 + +#define NUM_WEP_KEYS 4 +struct hostapd_wep_keys { + u8 idx; + u8 *key[NUM_WEP_KEYS]; + size_t len[NUM_WEP_KEYS]; + int keys_set; + size_t default_len; /* key length used for dynamic key generation */ +}; + +typedef enum hostap_security_policy { + SECURITY_PLAINTEXT = 0, + SECURITY_STATIC_WEP = 1, + SECURITY_IEEE_802_1X = 2, + SECURITY_WPA_PSK = 3, + SECURITY_WPA = 4 +} secpolicy; + +struct hostapd_ssid { + char ssid[HOSTAPD_MAX_SSID_LEN + 1]; + size_t ssid_len; + int ssid_set; + + char vlan[IFNAMSIZ + 1]; + secpolicy security_policy; + + struct hostapd_wpa_psk *wpa_psk; + char *wpa_passphrase; + char *wpa_psk_file; + + struct hostapd_wep_keys wep; + +#define DYNAMIC_VLAN_DISABLED 0 +#define DYNAMIC_VLAN_OPTIONAL 1 +#define DYNAMIC_VLAN_REQUIRED 2 + int dynamic_vlan; +#ifdef CONFIG_FULL_DYNAMIC_VLAN + char *vlan_tagged_interface; +#endif /* CONFIG_FULL_DYNAMIC_VLAN */ + struct hostapd_wep_keys **dyn_vlan_keys; + size_t max_dyn_vlan_keys; +}; + + +#define VLAN_ID_WILDCARD -1 + +struct hostapd_vlan { + struct hostapd_vlan *next; + int vlan_id; /* VLAN ID or -1 (VLAN_ID_WILDCARD) for wildcard entry */ + char ifname[IFNAMSIZ + 1]; + int dynamic_vlan; +#ifdef CONFIG_FULL_DYNAMIC_VLAN + +#define DVLAN_CLEAN_BR 0x1 +#define DVLAN_CLEAN_VLAN 0x2 +#define DVLAN_CLEAN_VLAN_PORT 0x4 +#define DVLAN_CLEAN_WLAN_PORT 0x8 + int clean; +#endif /* CONFIG_FULL_DYNAMIC_VLAN */ +}; + +#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; + struct { + int vendor; + u32 method; + } methods[EAP_USER_MAX_METHODS]; + u8 *password; + size_t password_len; + int phase2; + int force_version; + unsigned int wildcard_prefix:1; + unsigned int password_hash:1; /* whether password is hashed with + * nt_password_hash() */ + int ttls_auth; /* EAP_TTLS_AUTH_* bitfield */ +}; + + +#define NUM_TX_QUEUES 8 + +struct hostapd_tx_queue_params { + int aifs; + int cwmin; + int cwmax; + int burst; /* maximum burst time in 0.1 ms, i.e., 10 = 1 ms */ + int configured; +}; + +struct hostapd_wme_ac_params { + int cwmin; + int cwmax; + int aifs; + int txopLimit; /* in units of 32us */ + int admission_control_mandatory; +}; + + +/** + * struct hostapd_bss_config - Per-BSS configuration + */ +struct hostapd_bss_config { + char iface[IFNAMSIZ + 1]; + char bridge[IFNAMSIZ + 1]; + + enum hostapd_logger_level logger_syslog_level, logger_stdout_level; + + unsigned int logger_syslog; /* module bitfield */ + unsigned int logger_stdout; /* module bitfield */ + + char *dump_log_name; /* file name for state dump (SIGUSR1) */ + + int max_num_sta; /* maximum number of STAs in station table */ + + int dtim_period; + + 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; + + struct hostapd_ssid ssid; + + 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 broadcast_key_idx_min, broadcast_key_idx_max; + int eap_reauth_period; + + int ieee802_11f; /* use IEEE 802.11f (IAPP) */ + char iapp_iface[IFNAMSIZ + 1]; /* interface used with IAPP broadcast + * frames */ + + enum { + ACCEPT_UNLESS_DENIED = 0, + DENY_UNLESS_ACCEPTED = 1, + USE_EXTERNAL_RADIUS_AUTH = 2 + } macaddr_acl; + struct mac_acl_entry *accept_mac; + int num_accept_mac; + struct mac_acl_entry *deny_mac; + int num_deny_mac; + + int auth_algs; /* bitfield of allowed IEEE 802.11 authentication + * algorithms, WPA_AUTH_ALG_{OPEN,SHARED,LEAP} */ + + int wpa; /* bitfield of WPA_PROTO_WPA, WPA_PROTO_RSN */ + int wpa_key_mgmt; +#ifdef CONFIG_IEEE80211W + enum { + NO_IEEE80211W = 0, + IEEE80211W_OPTIONAL = 1, + IEEE80211W_REQUIRED = 2 + } ieee80211w; + /* dot11AssociationSAQueryMaximumTimeout (in TUs) */ + unsigned int assoc_sa_query_max_timeout; + /* dot11AssociationSAQueryRetryTimeout (in TUs) */ + int assoc_sa_query_retry_timeout; +#endif /* CONFIG_IEEE80211W */ + int wpa_pairwise; + int wpa_group; + int wpa_group_rekey; + int wpa_strict_rekey; + int wpa_gmk_rekey; + int wpa_ptk_rekey; + int rsn_pairwise; + int rsn_preauth; + char *rsn_preauth_interfaces; + int peerkey; + +#ifdef CONFIG_IEEE80211R + /* IEEE 802.11r - Fast BSS Transition */ + u8 mobility_domain[MOBILITY_DOMAIN_ID_LEN]; + u8 r1_key_holder[FT_R1KH_ID_LEN]; + u32 r0_key_lifetime; + u32 reassociation_deadline; + struct ft_remote_r0kh *r0kh_list; + struct ft_remote_r1kh *r1kh_list; + int pmk_r1_push; +#endif /* CONFIG_IEEE80211R */ + + 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 *dh_file; + u8 *pac_opaque_encr_key; + u8 *eap_fast_a_id; + size_t eap_fast_a_id_len; + char *eap_fast_a_id_info; + int eap_fast_prov; + int pac_key_lifetime; + int pac_key_refresh_time; + int eap_sim_aka_result_ind; + int tnc; + + 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). + */ + + int ap_max_inactivity; + int ignore_broadcast_ssid; + + int wme_enabled; + + struct hostapd_vlan *vlan, *vlan_tail; + + macaddr bssid; + + /* + * Maximum listen interval that STAs can use when associating with this + * BSS. If a STA tries to use larger value, the association will be + * denied with status code 51. + */ + u16 max_listen_interval; + + int okc; /* Opportunistic Key Caching */ + + int wps_state; +#ifdef CONFIG_WPS + int ap_setup_locked; + u8 uuid[16]; + char *wps_pin_requests; + char *device_name; + char *manufacturer; + char *model_name; + char *model_number; + char *serial_number; + char *device_type; + char *config_methods; + u8 os_version[4]; + char *ap_pin; + int skip_cred_build; + u8 *extra_cred; + size_t extra_cred_len; + int wps_cred_processing; + u8 *ap_settings; + size_t ap_settings_len; + char *upnp_iface; + char *friendly_name; + char *manufacturer_url; + char *model_description; + char *model_url; + char *upc; +#endif /* CONFIG_WPS */ +}; + + +typedef enum { + HOSTAPD_MODE_IEEE80211B, + HOSTAPD_MODE_IEEE80211G, + HOSTAPD_MODE_IEEE80211A, + NUM_HOSTAPD_MODES +} hostapd_hw_mode; + + +/** + * struct hostapd_config - Per-radio interface configuration + */ +struct hostapd_config { + struct hostapd_bss_config *bss, *last_bss; + size_t num_bss; + + u16 beacon_int; + int rts_threshold; + int fragm_threshold; + u8 send_probe_response; + u8 channel; + hostapd_hw_mode hw_mode; /* HOSTAPD_MODE_IEEE80211A, .. */ + enum { + LONG_PREAMBLE = 0, + SHORT_PREAMBLE = 1 + } preamble; + enum { + CTS_PROTECTION_AUTOMATIC = 0, + CTS_PROTECTION_FORCE_ENABLED = 1, + CTS_PROTECTION_FORCE_DISABLED = 2, + CTS_PROTECTION_AUTOMATIC_NO_OLBC = 3, + } cts_protection_type; + + int *supported_rates; + int *basic_rates; + + const struct wpa_driver_ops *driver; + + int passive_scan_interval; /* seconds, 0 = disabled */ + int passive_scan_listen; /* usec */ + int passive_scan_mode; + int ap_table_max_size; + int ap_table_expiration_time; + + char country[3]; /* first two octets: country code as described in + * ISO/IEC 3166-1. Third octet: + * ' ' (ascii 32): all environments + * 'O': Outdoor environemnt only + * 'I': Indoor environment only + */ + + int ieee80211d; + + struct hostapd_tx_queue_params tx_queue[NUM_TX_QUEUES]; + + /* + * WME AC parameters, in same order as 802.1D, i.e. + * 0 = BE (best effort) + * 1 = BK (background) + * 2 = VI (video) + * 3 = VO (voice) + */ + struct hostapd_wme_ac_params wme_ac_params[4]; + + enum { + INTERNAL_BRIDGE_DO_NOT_CONTROL = -1, + INTERNAL_BRIDGE_DISABLED = 0, + INTERNAL_BRIDGE_ENABLED = 1 + } bridge_packets; + +#ifdef CONFIG_IEEE80211N + int ht_op_mode_fixed; + u16 ht_capab; +#endif /* CONFIG_IEEE80211N */ + int ieee80211n; + int secondary_channel; +}; + + +int hostapd_mac_comp(const void *a, const void *b); +int hostapd_mac_comp_empty(const void *a); +struct hostapd_config * hostapd_config_read(const char *fname); +void hostapd_config_free(struct hostapd_config *conf); +int hostapd_maclist_found(struct mac_acl_entry *list, int num_entries, + const u8 *addr, int *vlan_id); +int hostapd_rate_found(int *list, int rate); +int hostapd_wep_key_cmp(struct hostapd_wep_keys *a, + struct hostapd_wep_keys *b); +const u8 * hostapd_get_psk(const struct hostapd_bss_config *conf, + const u8 *addr, const u8 *prev_psk); +int hostapd_setup_wpa_psk(struct hostapd_bss_config *conf); +const char * hostapd_get_vlan_id_ifname(struct hostapd_vlan *vlan, + int vlan_id); +const struct hostapd_eap_user * +hostapd_get_eap_user(const struct hostapd_bss_config *conf, const u8 *identity, + size_t identity_len, int phase2); + +#endif /* CONFIG_H */ diff --git a/contrib/wpa/hostapd/ctrl_iface.c b/contrib/wpa/hostapd/ctrl_iface.c new file mode 100644 index 0000000..fe63e7c --- /dev/null +++ b/contrib/wpa/hostapd/ctrl_iface.c @@ -0,0 +1,560 @@ +/* + * hostapd / UNIX domain socket -based control interface + * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#ifndef CONFIG_NATIVE_WINDOWS + +#include <sys/un.h> +#include <sys/stat.h> + +#include "hostapd.h" +#include "eloop.h" +#include "config.h" +#include "ieee802_1x.h" +#include "wpa.h" +#include "radius/radius_client.h" +#include "ieee802_11.h" +#include "ctrl_iface.h" +#include "sta_info.h" +#include "accounting.h" +#include "wps_hostapd.h" + + +struct wpa_ctrl_dst { + struct wpa_ctrl_dst *next; + struct sockaddr_un addr; + socklen_t addrlen; + int debug_level; + int errors; +}; + + +static void hostapd_ctrl_iface_send(struct hostapd_data *hapd, int level, + const char *buf, size_t len); + + +static int hostapd_ctrl_iface_attach(struct hostapd_data *hapd, + struct sockaddr_un *from, + socklen_t fromlen) +{ + struct wpa_ctrl_dst *dst; + + dst = os_zalloc(sizeof(*dst)); + if (dst == NULL) + return -1; + os_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 && + os_memcmp(from->sun_path, dst->addr.sun_path, fromlen) == + 0) { + if (prev == NULL) + hapd->ctrl_dst = dst->next; + else + prev->next = dst->next; + os_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 && + os_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, ret; + + if (sta == NULL) { + ret = os_snprintf(buf, buflen, "FAIL\n"); + if (ret < 0 || (size_t) ret >= buflen) + return 0; + return ret; + } + + len = 0; + ret = os_snprintf(buf + len, buflen - len, MACSTR "\n", + MAC2STR(sta->addr)); + if (ret < 0 || (size_t) ret >= buflen - len) + return len; + len += ret; + + res = ieee802_11_get_mib_sta(hapd, sta, buf + len, buflen - len); + if (res >= 0) + len += res; + res = wpa_get_mib_sta(sta->wpa_sm, 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]; + int ret; + + if (hwaddr_aton(txtaddr, addr)) { + ret = os_snprintf(buf, buflen, "FAIL\n"); + if (ret < 0 || (size_t) ret >= buflen) + return 0; + return ret; + } + 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; + int ret; + + if (hwaddr_aton(txtaddr, addr) || + (sta = ap_get_sta(hapd, addr)) == NULL) { + ret = os_snprintf(buf, buflen, "FAIL\n"); + if (ret < 0 || (size_t) ret >= buflen) + return 0; + return ret; + } + return hostapd_ctrl_iface_sta_mib(hapd, sta->next, buf, buflen); +} + + +static int hostapd_ctrl_iface_new_sta(struct hostapd_data *hapd, + const char *txtaddr) +{ + u8 addr[ETH_ALEN]; + struct sta_info *sta; + + wpa_printf(MSG_DEBUG, "CTRL_IFACE NEW_STA %s", txtaddr); + + if (hwaddr_aton(txtaddr, addr)) + return -1; + + sta = ap_get_sta(hapd, addr); + if (sta) + return 0; + + wpa_printf(MSG_DEBUG, "Add new STA " MACSTR " based on ctrl_iface " + "notification", MAC2STR(addr)); + sta = ap_sta_add(hapd, addr); + if (sta == NULL) + return -1; + + hostapd_new_assoc_sta(hapd, sta, 0); + return 0; +} + + +#ifdef CONFIG_IEEE80211W +static int hostapd_ctrl_iface_sa_query(struct hostapd_data *hapd, + const char *txtaddr) +{ + u8 addr[ETH_ALEN]; + u8 trans_id[WLAN_SA_QUERY_TR_ID_LEN]; + + wpa_printf(MSG_DEBUG, "CTRL_IFACE SA_QUERY %s", txtaddr); + + if (hwaddr_aton(txtaddr, addr)) + return -1; + + os_get_random(trans_id, WLAN_SA_QUERY_TR_ID_LEN); + ieee802_11_send_sa_query_req(hapd, addr, trans_id); + + return 0; +} +#endif /* CONFIG_IEEE80211W */ + + +#ifdef CONFIG_WPS +static int hostapd_ctrl_iface_wps_pin(struct hostapd_data *hapd, char *txt) +{ + char *pin = os_strchr(txt, ' '); + if (pin == NULL) + return -1; + *pin++ = '\0'; + return hostapd_wps_add_pin(hapd, txt, pin); +} +#endif /* CONFIG_WPS */ + + +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 = os_malloc(reply_size); + if (reply == NULL) { + sendto(sock, "FAIL\n", 5, 0, (struct sockaddr *) &from, + fromlen); + return; + } + + os_memcpy(reply, "OK\n", 3); + reply_len = 3; + + if (os_strcmp(buf, "PING") == 0) { + os_memcpy(reply, "PONG\n", 5); + reply_len = 5; + } else if (os_strcmp(buf, "MIB") == 0) { + reply_len = ieee802_11_get_mib(hapd, reply, reply_size); + if (reply_len >= 0) { + res = wpa_get_mib(hapd->wpa_auth, 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 (os_strcmp(buf, "STA-FIRST") == 0) { + reply_len = hostapd_ctrl_iface_sta_first(hapd, reply, + reply_size); + } else if (os_strncmp(buf, "STA ", 4) == 0) { + reply_len = hostapd_ctrl_iface_sta(hapd, buf + 4, reply, + reply_size); + } else if (os_strncmp(buf, "STA-NEXT ", 9) == 0) { + reply_len = hostapd_ctrl_iface_sta_next(hapd, buf + 9, reply, + reply_size); + } else if (os_strcmp(buf, "ATTACH") == 0) { + if (hostapd_ctrl_iface_attach(hapd, &from, fromlen)) + reply_len = -1; + } else if (os_strcmp(buf, "DETACH") == 0) { + if (hostapd_ctrl_iface_detach(hapd, &from, fromlen)) + reply_len = -1; + } else if (os_strncmp(buf, "LEVEL ", 6) == 0) { + if (hostapd_ctrl_iface_level(hapd, &from, fromlen, + buf + 6)) + reply_len = -1; + } else if (os_strncmp(buf, "NEW_STA ", 8) == 0) { + if (hostapd_ctrl_iface_new_sta(hapd, buf + 8)) + reply_len = -1; +#ifdef CONFIG_IEEE80211W + } else if (os_strncmp(buf, "SA_QUERY ", 9) == 0) { + if (hostapd_ctrl_iface_sa_query(hapd, buf + 9)) + reply_len = -1; +#endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_WPS + } else if (os_strncmp(buf, "WPS_PIN ", 8) == 0) { + if (hostapd_ctrl_iface_wps_pin(hapd, buf + 8)) + reply_len = -1; + } else if (os_strcmp(buf, "WPS_PBC") == 0) { + if (hostapd_wps_button_pushed(hapd)) + reply_len = -1; +#endif /* CONFIG_WPS */ + } else { + os_memcpy(reply, "UNKNOWN COMMAND\n", 16); + reply_len = 16; + } + + if (reply_len < 0) { + os_memcpy(reply, "FAIL\n", 5); + reply_len = 5; + } + sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from, fromlen); + os_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 = os_strlen(hapd->conf->ctrl_interface) + + os_strlen(hapd->conf->iface) + 2; + buf = os_malloc(len); + if (buf == NULL) + return NULL; + + os_snprintf(buf, len, "%s/%s", + hapd->conf->ctrl_interface, hapd->conf->iface); + buf[len - 1] = '\0'; + return buf; +} + + +static void hostapd_ctrl_iface_msg_cb(void *ctx, int level, + const char *txt, size_t len) +{ + struct hostapd_data *hapd = ctx; + if (hapd == NULL) + return; + hostapd_ctrl_iface_send(hapd, level, txt, len); +} + + +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 (os_strlen(hapd->conf->ctrl_interface) + 1 + + os_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; + } + + os_memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + fname = hostapd_ctrl_iface_path(hapd); + if (fname == NULL) + goto fail; + os_strlcpy(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; + } + os_free(fname); + + hapd->ctrl_sock = s; + eloop_register_read_sock(s, hostapd_ctrl_iface_receive, hapd, + NULL); + wpa_msg_register_cb(hostapd_ctrl_iface_msg_cb); + + return 0; + +fail: + if (s >= 0) + close(s); + if (fname) { + unlink(fname); + os_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); + os_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; + os_free(prev); + } +} + + +static void hostapd_ctrl_iface_send(struct hostapd_data *hapd, int level, + const 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; + + os_snprintf(levelstr, sizeof(levelstr), "<%d>", level); + io[0].iov_base = levelstr; + io[0].iov_len = os_strlen(levelstr); + io[1].iov_base = (char *) buf; + io[1].iov_len = len; + os_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; + } +} + +#endif /* CONFIG_NATIVE_WINDOWS */ diff --git a/contrib/wpa/hostapd/ctrl_iface.h b/contrib/wpa/hostapd/ctrl_iface.h new file mode 100644 index 0000000..d86de8c --- /dev/null +++ b/contrib/wpa/hostapd/ctrl_iface.h @@ -0,0 +1,21 @@ +/* + * hostapd / UNIX domain socket -based control interface + * Copyright (c) 2004, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#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); + +#endif /* CTRL_IFACE_H */ diff --git a/contrib/wpa/hostapd/defconfig b/contrib/wpa/hostapd/defconfig new file mode 100644 index 0000000..96a023d --- /dev/null +++ b/contrib/wpa/hostapd/defconfig @@ -0,0 +1,144 @@ +# 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../../madwifi # change to the madwifi source directory + +# Driver interface for Prism54 driver +#CONFIG_DRIVER_PRISM54=y + +# Driver interface for drivers using the nl80211 kernel interface +#CONFIG_DRIVER_NL80211=y +# driver_nl80211.c requires a rather new libnl (version 1.1) which may not be +# shipped with your distribution yet. If that is the case, you need to build +# newer libnl version and point the hostapd build to use it. +#LIBNL=/usr/src/libnl +#CFLAGS += -I$(LIBNL)/include +#LIBS += -L$(LIBNL)/lib + +# Driver interface for FreeBSD net80211 layer (e.g., Atheros driver) +#CONFIG_DRIVER_BSD=y +#CFLAGS += -I/usr/local/include +#LIBS += -L/usr/local/lib + +# Driver interface for no driver (e.g., RADIUS server only) +#CONFIG_DRIVER_NONE=y + +# IEEE 802.11F/IAPP +CONFIG_IAPP=y + +# WPA2/IEEE 802.11i RSN pre-authentication +CONFIG_RSN_PREAUTH=y + +# PeerKey handshake for Station to Station Link (IEEE 802.11e DLS) +CONFIG_PEERKEY=y + +# IEEE 802.11w (management frame protection) +# This version is an experimental implementation based on IEEE 802.11w/D1.0 +# draft and is subject to change since the standard has not yet been finalized. +# Driver support is also needed for IEEE 802.11w. +#CONFIG_IEEE80211W=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-AKA for the integrated EAP server +#CONFIG_EAP_AKA=y + +# EAP-AKA' for the integrated EAP server +# This requires CONFIG_EAP_AKA to be enabled, too. +#CONFIG_EAP_AKA_PRIME=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 + +# EAP-SAKE for the integrated EAP server +#CONFIG_EAP_SAKE=y + +# EAP-GPSK for the integrated EAP server +#CONFIG_EAP_GPSK=y +# Include support for optional SHA256 cipher suite in EAP-GPSK +#CONFIG_EAP_GPSK_SHA256=y + +# EAP-FAST for the integrated EAP server +# Note: Default OpenSSL package does not include support for all the +# functionality needed for EAP-FAST. If EAP-FAST is enabled with OpenSSL, +# the OpenSSL library must be patched (openssl-0.9.9-session-ticket.patch) +# to add the needed functions. +#CONFIG_EAP_FAST=y + +# Wi-Fi Protected Setup (WPS) +#CONFIG_WPS=y +# Enable UPnP support for external WPS Registrars +#CONFIG_WPS_UPNP=y + +# EAP-IKEv2 +#CONFIG_EAP_IKEV2=y + +# Trusted Network Connect (EAP-TNC) +#CONFIG_EAP_TNC=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 + +# IEEE Std 802.11r-2008 (Fast BSS Transition) +#CONFIG_IEEE80211R=y + +# Use the hostapd's IEEE 802.11 authentication (ACL), but without +# the IEEE 802.11 Management capability (e.g., madwifi or FreeBSD/net80211) +#CONFIG_DRIVER_RADIUS_ACL=y + +# IEEE 802.11n (High Throughput) support +#CONFIG_IEEE80211N=y + +# Remove debugging code that is printing out debug messages to stdout. +# This can be used to reduce the size of the hostapd considerably if debugging +# code is not needed. +#CONFIG_NO_STDOUT_DEBUG=y diff --git a/contrib/wpa/hostapd/doc/.gitignore b/contrib/wpa/hostapd/doc/.gitignore new file mode 100644 index 0000000..987a5e9 --- /dev/null +++ b/contrib/wpa/hostapd/doc/.gitignore @@ -0,0 +1,4 @@ +html +latex +hostapd.eps +hostapd.png diff --git a/contrib/wpa/hostapd/doc/code_structure.doxygen b/contrib/wpa/hostapd/doc/code_structure.doxygen new file mode 100644 index 0000000..fdcf725 --- /dev/null +++ b/contrib/wpa/hostapd/doc/code_structure.doxygen @@ -0,0 +1,5 @@ +/** +\page code_structure Structure of the source code + + +*/ diff --git a/contrib/wpa/hostapd/doc/ctrl_iface.doxygen b/contrib/wpa/hostapd/doc/ctrl_iface.doxygen new file mode 100644 index 0000000..76cfc6a --- /dev/null +++ b/contrib/wpa/hostapd/doc/ctrl_iface.doxygen @@ -0,0 +1,66 @@ +/** +\page ctrl_iface_page Control interface + +hostapd implements a control interface that can be used by +external programs to control the operations of the hostapd +daemon and to get status information and event notifications. There is +a small C library, in a form of a single C file, wpa_ctrl.c, that +provides helper functions to facilitate the use of the control +interface. External programs can link this file into them and then use +the library functions documented in wpa_ctrl.h to interact with +%wpa_supplicant. This library can also be used with C++. hostapd_cli.c +is an example program using this library. + +There are multiple mechanisms for inter-process communication. For +example, Linux version of hostapd is using UNIX domain sockets for the +control interface. The use of the functions defined in wpa_ctrl.h can +be used to hide the details of the used IPC from external programs. + + +\section using_ctrl_iface Using the control interface + +External programs, e.g., a GUI or a configuration utility, that need to +communicate with hostapd should link in wpa_ctrl.c. This +allows them to use helper functions to open connection to the control +interface with wpa_ctrl_open() and to send commands with +wpa_ctrl_request(). + +hostapd uses the control interface for two types of communication: +commands and unsolicited event messages. Commands are a pair of +messages, a request from the external program and a response from +hostapd. These can be executed using wpa_ctrl_request(). +Unsolicited event messages are sent by hostapd to the control +interface connection without specific request from the external program +for receiving each message. However, the external program needs to +attach to the control interface with wpa_ctrl_attach() to receive these +unsolicited messages. + +If the control interface connection is used both for commands and +unsolicited event messages, there is potential for receiving an +unsolicited message between the command request and response. +wpa_ctrl_request() caller will need to supply a callback, msg_cb, +for processing these messages. Often it is easier to open two +control interface connections by calling wpa_ctrl_open() twice and +then use one of the connections for commands and the other one for +unsolicited messages. This way command request/response pairs will +not be broken by unsolicited messages. wpa_cli is an example of how +to use only one connection for both purposes and wpa_gui demonstrates +how to use two separate connections. + +Once the control interface connection is not needed anymore, it should +be closed by calling wpa_ctrl_close(). If the connection was used for +unsolicited event messages, it should be first detached by calling +wpa_ctrl_detach(). + + +\section ctrl_iface_cmds Control interface commands + +Following commands can be used with wpa_ctrl_request(): + +\subsection ctrl_iface_PING PING + +This command can be used to test whether hostapd is replying +to the control interface commands. The expected reply is \c PONG if the +connection is open and hostapd is processing commands. + +*/ diff --git a/contrib/wpa/hostapd/doc/doxygen.fast b/contrib/wpa/hostapd/doc/doxygen.fast new file mode 100644 index 0000000..650c73d --- /dev/null +++ b/contrib/wpa/hostapd/doc/doxygen.fast @@ -0,0 +1,238 @@ +# Doxyfile 1.4.4 + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- +PROJECT_NAME = hostapd +PROJECT_NUMBER = 0.6.x +OUTPUT_DIRECTORY = hostapd/doc +CREATE_SUBDIRS = NO +OUTPUT_LANGUAGE = English +BRIEF_MEMBER_DESC = YES +REPEAT_BRIEF = YES +ABBREVIATE_BRIEF = "The $name class" \ + "The $name widget" \ + "The $name file" \ + is \ + provides \ + specifies \ + contains \ + represents \ + a \ + an \ + the +ALWAYS_DETAILED_SEC = NO +INLINE_INHERITED_MEMB = NO +FULL_PATH_NAMES = YES +STRIP_FROM_PATH = +STRIP_FROM_INC_PATH = +SHORT_NAMES = NO +JAVADOC_AUTOBRIEF = NO +MULTILINE_CPP_IS_BRIEF = NO +DETAILS_AT_TOP = NO +INHERIT_DOCS = YES +DISTRIBUTE_GROUP_DOC = NO +SEPARATE_MEMBER_PAGES = NO +TAB_SIZE = 8 +ALIASES = +OPTIMIZE_OUTPUT_FOR_C = YES +OPTIMIZE_OUTPUT_JAVA = NO +SUBGROUPING = YES +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- +EXTRACT_ALL = NO +EXTRACT_PRIVATE = NO +EXTRACT_STATIC = NO +EXTRACT_LOCAL_CLASSES = YES +EXTRACT_LOCAL_METHODS = NO +HIDE_UNDOC_MEMBERS = NO +HIDE_UNDOC_CLASSES = NO +HIDE_FRIEND_COMPOUNDS = NO +HIDE_IN_BODY_DOCS = NO +INTERNAL_DOCS = NO +CASE_SENSE_NAMES = YES +HIDE_SCOPE_NAMES = NO +SHOW_INCLUDE_FILES = YES +INLINE_INFO = YES +SORT_MEMBER_DOCS = YES +SORT_BRIEF_DOCS = NO +SORT_BY_SCOPE_NAME = NO +GENERATE_TODOLIST = YES +GENERATE_TESTLIST = YES +GENERATE_BUGLIST = YES +GENERATE_DEPRECATEDLIST= YES +ENABLED_SECTIONS = +MAX_INITIALIZER_LINES = 30 +SHOW_USED_FILES = YES +SHOW_DIRECTORIES = YES +FILE_VERSION_FILTER = +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- +QUIET = NO +WARNINGS = YES +WARN_IF_UNDOCUMENTED = NO +WARN_IF_DOC_ERROR = YES +WARN_NO_PARAMDOC = YES +WARN_FORMAT = "$file:$line: $text" +WARN_LOGFILE = +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- +INPUT = hostapd \ + src/common \ + src/crypto \ + src/eap_common \ + src/eap_server \ + src/l2_packet \ + src/radius \ + src/rsn_supp \ + src/tls \ + src/utils \ + src/wps +FILE_PATTERNS = *.c *.h *.doxygen +RECURSIVE = YES +EXCLUDE = +EXCLUDE_SYMLINKS = NO +EXCLUDE_PATTERNS = +EXAMPLE_PATH = +EXAMPLE_PATTERNS = * +EXAMPLE_RECURSIVE = NO +IMAGE_PATH = hostapd/doc +INPUT_FILTER = kerneldoc2doxygen.pl +FILTER_PATTERNS = +FILTER_SOURCE_FILES = YES +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- +SOURCE_BROWSER = YES +INLINE_SOURCES = NO +STRIP_CODE_COMMENTS = YES +REFERENCED_BY_RELATION = NO +REFERENCES_RELATION = NO +VERBATIM_HEADERS = NO +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- +ALPHABETICAL_INDEX = YES +COLS_IN_ALPHA_INDEX = 3 +IGNORE_PREFIX = +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- +GENERATE_HTML = YES +HTML_OUTPUT = html +HTML_FILE_EXTENSION = .html +HTML_HEADER = +HTML_FOOTER = +HTML_STYLESHEET = +HTML_ALIGN_MEMBERS = YES +GENERATE_HTMLHELP = NO +CHM_FILE = +HHC_LOCATION = +GENERATE_CHI = NO +BINARY_TOC = NO +TOC_EXPAND = NO +DISABLE_INDEX = NO +ENUM_VALUES_PER_LINE = 4 +GENERATE_TREEVIEW = NO +TREEVIEW_WIDTH = 250 +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- +GENERATE_LATEX = NO +LATEX_OUTPUT = latex +LATEX_CMD_NAME = latex +MAKEINDEX_CMD_NAME = makeindex +COMPACT_LATEX = NO +PAPER_TYPE = a4wide +EXTRA_PACKAGES = +LATEX_HEADER = +PDF_HYPERLINKS = YES +USE_PDFLATEX = YES +LATEX_BATCHMODE = NO +LATEX_HIDE_INDICES = NO +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- +GENERATE_RTF = NO +RTF_OUTPUT = rtf +COMPACT_RTF = NO +RTF_HYPERLINKS = NO +RTF_STYLESHEET_FILE = +RTF_EXTENSIONS_FILE = +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- +GENERATE_MAN = NO +MAN_OUTPUT = man +MAN_EXTENSION = .3 +MAN_LINKS = NO +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- +GENERATE_XML = NO +XML_OUTPUT = xml +XML_SCHEMA = +XML_DTD = +XML_PROGRAMLISTING = YES +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- +GENERATE_AUTOGEN_DEF = NO +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- +GENERATE_PERLMOD = NO +PERLMOD_LATEX = NO +PERLMOD_PRETTY = YES +PERLMOD_MAKEVAR_PREFIX = +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- +ENABLE_PREPROCESSING = YES +MACRO_EXPANSION = NO +EXPAND_ONLY_PREDEF = NO +SEARCH_INCLUDES = YES +INCLUDE_PATH = +INCLUDE_FILE_PATTERNS = +PREDEFINED = RADIUS_SERVER EAP_SERVER EAP_SIM +EXPAND_AS_DEFINED = +SKIP_FUNCTION_MACROS = YES +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- +TAGFILES = +GENERATE_TAGFILE = +ALLEXTERNALS = NO +EXTERNAL_GROUPS = YES +PERL_PATH = /usr/bin/perl +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- +CLASS_DIAGRAMS = NO +HIDE_UNDOC_RELATIONS = YES +HAVE_DOT = NO +CLASS_GRAPH = YES +COLLABORATION_GRAPH = YES +GROUP_GRAPHS = YES +UML_LOOK = NO +TEMPLATE_RELATIONS = NO +INCLUDE_GRAPH = YES +INCLUDED_BY_GRAPH = YES +CALL_GRAPH = YES +GRAPHICAL_HIERARCHY = YES +DIRECTORY_GRAPH = NO +DOT_IMAGE_FORMAT = png +DOT_PATH = +DOTFILE_DIRS = +MAX_DOT_GRAPH_DEPTH = 1000 +DOT_TRANSPARENT = NO +DOT_MULTI_TARGETS = NO +GENERATE_LEGEND = YES +DOT_CLEANUP = YES +#--------------------------------------------------------------------------- +# Configuration::additions related to the search engine +#--------------------------------------------------------------------------- +SEARCHENGINE = NO diff --git a/contrib/wpa/hostapd/doc/doxygen.full b/contrib/wpa/hostapd/doc/doxygen.full new file mode 100644 index 0000000..f8c49bf --- /dev/null +++ b/contrib/wpa/hostapd/doc/doxygen.full @@ -0,0 +1,238 @@ +# Doxyfile 1.4.4 + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- +PROJECT_NAME = hostapd +PROJECT_NUMBER = 0.6.x +OUTPUT_DIRECTORY = hostapd/doc +CREATE_SUBDIRS = NO +OUTPUT_LANGUAGE = English +BRIEF_MEMBER_DESC = YES +REPEAT_BRIEF = YES +ABBREVIATE_BRIEF = "The $name class" \ + "The $name widget" \ + "The $name file" \ + is \ + provides \ + specifies \ + contains \ + represents \ + a \ + an \ + the +ALWAYS_DETAILED_SEC = NO +INLINE_INHERITED_MEMB = NO +FULL_PATH_NAMES = YES +STRIP_FROM_PATH = +STRIP_FROM_INC_PATH = +SHORT_NAMES = NO +JAVADOC_AUTOBRIEF = NO +MULTILINE_CPP_IS_BRIEF = NO +DETAILS_AT_TOP = NO +INHERIT_DOCS = YES +DISTRIBUTE_GROUP_DOC = NO +SEPARATE_MEMBER_PAGES = NO +TAB_SIZE = 8 +ALIASES = +OPTIMIZE_OUTPUT_FOR_C = YES +OPTIMIZE_OUTPUT_JAVA = NO +SUBGROUPING = YES +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- +EXTRACT_ALL = NO +EXTRACT_PRIVATE = NO +EXTRACT_STATIC = NO +EXTRACT_LOCAL_CLASSES = YES +EXTRACT_LOCAL_METHODS = NO +HIDE_UNDOC_MEMBERS = NO +HIDE_UNDOC_CLASSES = NO +HIDE_FRIEND_COMPOUNDS = NO +HIDE_IN_BODY_DOCS = NO +INTERNAL_DOCS = NO +CASE_SENSE_NAMES = YES +HIDE_SCOPE_NAMES = NO +SHOW_INCLUDE_FILES = YES +INLINE_INFO = YES +SORT_MEMBER_DOCS = YES +SORT_BRIEF_DOCS = NO +SORT_BY_SCOPE_NAME = NO +GENERATE_TODOLIST = YES +GENERATE_TESTLIST = YES +GENERATE_BUGLIST = YES +GENERATE_DEPRECATEDLIST= YES +ENABLED_SECTIONS = +MAX_INITIALIZER_LINES = 30 +SHOW_USED_FILES = YES +SHOW_DIRECTORIES = YES +FILE_VERSION_FILTER = +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- +QUIET = NO +WARNINGS = YES +WARN_IF_UNDOCUMENTED = NO +WARN_IF_DOC_ERROR = YES +WARN_NO_PARAMDOC = YES +WARN_FORMAT = "$file:$line: $text" +WARN_LOGFILE = +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- +INPUT = hostapd \ + src/common \ + src/crypto \ + src/eap_common \ + src/eap_server \ + src/l2_packet \ + src/radius \ + src/rsn_supp \ + src/tls \ + src/utils \ + src/wps +FILE_PATTERNS = *.c *.h *.doxygen +RECURSIVE = YES +EXCLUDE = +EXCLUDE_SYMLINKS = NO +EXCLUDE_PATTERNS = +EXAMPLE_PATH = +EXAMPLE_PATTERNS = * +EXAMPLE_RECURSIVE = NO +IMAGE_PATH = hostapd/doc +INPUT_FILTER = kerneldoc2doxygen.pl +FILTER_PATTERNS = +FILTER_SOURCE_FILES = YES +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- +SOURCE_BROWSER = YES +INLINE_SOURCES = NO +STRIP_CODE_COMMENTS = YES +REFERENCED_BY_RELATION = NO +REFERENCES_RELATION = NO +VERBATIM_HEADERS = NO +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- +ALPHABETICAL_INDEX = YES +COLS_IN_ALPHA_INDEX = 3 +IGNORE_PREFIX = +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- +GENERATE_HTML = YES +HTML_OUTPUT = html +HTML_FILE_EXTENSION = .html +HTML_HEADER = +HTML_FOOTER = +HTML_STYLESHEET = +HTML_ALIGN_MEMBERS = YES +GENERATE_HTMLHELP = NO +CHM_FILE = +HHC_LOCATION = +GENERATE_CHI = NO +BINARY_TOC = NO +TOC_EXPAND = NO +DISABLE_INDEX = NO +ENUM_VALUES_PER_LINE = 4 +GENERATE_TREEVIEW = NO +TREEVIEW_WIDTH = 250 +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- +GENERATE_LATEX = YES +LATEX_OUTPUT = latex +LATEX_CMD_NAME = latex +MAKEINDEX_CMD_NAME = makeindex +COMPACT_LATEX = NO +PAPER_TYPE = a4wide +EXTRA_PACKAGES = +LATEX_HEADER = +PDF_HYPERLINKS = YES +USE_PDFLATEX = YES +LATEX_BATCHMODE = NO +LATEX_HIDE_INDICES = NO +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- +GENERATE_RTF = NO +RTF_OUTPUT = rtf +COMPACT_RTF = NO +RTF_HYPERLINKS = NO +RTF_STYLESHEET_FILE = +RTF_EXTENSIONS_FILE = +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- +GENERATE_MAN = NO +MAN_OUTPUT = man +MAN_EXTENSION = .3 +MAN_LINKS = NO +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- +GENERATE_XML = NO +XML_OUTPUT = xml +XML_SCHEMA = +XML_DTD = +XML_PROGRAMLISTING = YES +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- +GENERATE_AUTOGEN_DEF = NO +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- +GENERATE_PERLMOD = NO +PERLMOD_LATEX = NO +PERLMOD_PRETTY = YES +PERLMOD_MAKEVAR_PREFIX = +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- +ENABLE_PREPROCESSING = YES +MACRO_EXPANSION = NO +EXPAND_ONLY_PREDEF = NO +SEARCH_INCLUDES = YES +INCLUDE_PATH = +INCLUDE_FILE_PATTERNS = +PREDEFINED = RADIUS_SERVER EAP_SERVER EAP_SIM +EXPAND_AS_DEFINED = +SKIP_FUNCTION_MACROS = YES +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- +TAGFILES = +GENERATE_TAGFILE = +ALLEXTERNALS = NO +EXTERNAL_GROUPS = YES +PERL_PATH = /usr/bin/perl +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- +CLASS_DIAGRAMS = NO +HIDE_UNDOC_RELATIONS = YES +HAVE_DOT = YES +CLASS_GRAPH = YES +COLLABORATION_GRAPH = YES +GROUP_GRAPHS = YES +UML_LOOK = NO +TEMPLATE_RELATIONS = NO +INCLUDE_GRAPH = YES +INCLUDED_BY_GRAPH = YES +CALL_GRAPH = YES +GRAPHICAL_HIERARCHY = YES +DIRECTORY_GRAPH = NO +DOT_IMAGE_FORMAT = png +DOT_PATH = +DOTFILE_DIRS = +MAX_DOT_GRAPH_DEPTH = 1000 +DOT_TRANSPARENT = NO +DOT_MULTI_TARGETS = NO +GENERATE_LEGEND = YES +DOT_CLEANUP = YES +#--------------------------------------------------------------------------- +# Configuration::additions related to the search engine +#--------------------------------------------------------------------------- +SEARCHENGINE = YES diff --git a/contrib/wpa/hostapd/doc/driver_wrapper.doxygen b/contrib/wpa/hostapd/doc/driver_wrapper.doxygen new file mode 100644 index 0000000..0ad196f --- /dev/null +++ b/contrib/wpa/hostapd/doc/driver_wrapper.doxygen @@ -0,0 +1,20 @@ +/** +\page driver_wrapper Driver wrapper implementation (driver.h, drivers.c) + +All hardware and driver dependent functionality is in separate C files +that implement defined wrapper functions. Other parts +of the hostapd are designed to be hardware, driver, and operating +system independent. + +Driver wrappers need to implement whatever calls are used in the +target operating system/driver for controlling wireless LAN +devices. As an example, in case of Linux, these are mostly some glue +code and ioctl() calls and netlink message parsing for Linux Wireless +Extensions (WE). Since features required for WPA were added only recently to +Linux Wireless Extensions (in version 18), some driver specific code is used +in number of driver interface implementations. These driver dependent parts +can be replaced with generic code in driver_wext.c once the target driver +includes full support for WE-18. After that, all Linux drivers, at +least in theory, could use the same driver wrapper code. + +*/ diff --git a/contrib/wpa/hostapd/doc/eap.doxygen b/contrib/wpa/hostapd/doc/eap.doxygen new file mode 100644 index 0000000..f0f135a --- /dev/null +++ b/contrib/wpa/hostapd/doc/eap.doxygen @@ -0,0 +1,56 @@ +/** +\page eap_module EAP server implementation + +Extensible Authentication Protocol (EAP) is an authentication framework +defined in RFC 3748. hostapd uses a separate code module for EAP server +implementation. This module was designed to use only a minimal set of +direct function calls (mainly, to debug/event functions) in order for +it to be usable in other programs. The design of the EAP +implementation is based loosely on RFC 4137. The state machine is +defined in this RFC and so is the interface between the server state +machine and methods. As such, this RFC provides useful information for +understanding the EAP server implementation in hostapd. + +Some of the terminology used in EAP state machine is referring to +EAPOL (IEEE 802.1X), but there is no strict requirement on the lower +layer being IEEE 802.1X if EAP module is built for other programs than +%wpa_supplicant. These terms should be understood to refer to the +lower layer as defined in RFC 4137. + + +\section adding_eap_methods Adding EAP methods + +Each EAP method is implemented as a separate module, usually as one C +file named eap_<name of the method>.c, e.g., eap_md5.c. All EAP +methods use the same interface between the server state machine and +method specific functions. This allows new EAP methods to be added +without modifying the core EAP state machine implementation. + +New EAP methods need to be registered by adding them into the build +(Makefile) and the EAP method registration list in the +eap_server_register_methods() function of eap_methods.c. Each EAP +method should use a build-time configuration option, e.g., EAP_TLS, in +order to make it possible to select which of the methods are included +in the build. + +EAP methods must implement the interface defined in eap_i.h. struct +eap_method defines the needed function pointers that each EAP method +must provide. In addition, the EAP type and name are registered using +this structure. This interface is based on section 4.4 of RFC 4137. + +It is recommended that the EAP methods would use generic helper +functions, eap_msg_alloc() and eap_hdr_validate() when processing +messages. This allows code sharing and can avoid missing some of the +needed validation steps for received packets. In addition, these +functions make it easier to change between expanded and legacy EAP +header, if needed. + +When adding an EAP method that uses a vendor specific EAP type +(Expanded Type as defined in RFC 3748, Chapter 5.7), the new method +must be registered by passing vendor id instead of EAP_VENDOR_IETF to +eap_server_method_alloc(). These methods must not try to emulate +expanded types by registering a legacy EAP method for type 254. See +eap_vendor_test.c for an example of an EAP method implementation that +is implemented as an expanded type. + +*/ diff --git a/contrib/wpa/hostapd/doc/hostapd.fig b/contrib/wpa/hostapd/doc/hostapd.fig new file mode 100644 index 0000000..af3f0be --- /dev/null +++ b/contrib/wpa/hostapd/doc/hostapd.fig @@ -0,0 +1,264 @@ +#FIG 3.2 +Landscape +Center +Inches +Letter +100.00 +Single +-2 +1200 2 +6 1875 4050 2925 4350 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 1875 4050 2925 4050 2925 4350 1875 4350 1875 4050 +4 0 0 50 -1 0 12 0.0000 4 180 735 2025 4275 l2_packet\001 +-6 +6 4725 1200 5925 1500 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 4725 1200 5925 1200 5925 1500 4725 1500 4725 1200 +4 0 0 50 -1 0 12 0.0000 4 135 1005 4800 1425 GUI frontend\001 +-6 +6 6000 2700 7200 3225 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 6000 2700 7200 2700 7200 3225 6000 3225 6000 2700 +4 0 0 50 -1 0 12 0.0000 4 135 975 6075 2925 WPA/WPA2\001 +4 0 0 50 -1 0 12 0.0000 4 135 1065 6075 3150 state machine\001 +-6 +6 6000 4950 7200 5475 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 6000 4950 7200 4950 7200 5475 6000 5475 6000 4950 +4 0 0 50 -1 0 12 0.0000 4 135 360 6075 5175 EAP\001 +4 0 0 50 -1 0 12 0.0000 4 135 1065 6075 5400 state machine\001 +-6 +6 4350 3900 5025 4425 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 4350 3900 5025 3900 5025 4425 4350 4425 4350 3900 +4 0 0 50 -1 0 12 0.0000 4 105 420 4500 4125 event\001 +4 0 0 50 -1 0 12 0.0000 4 180 315 4500 4350 loop\001 +-6 +6 4275 2550 5100 2850 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 4275 2550 5100 2550 5100 2850 4275 2850 4275 2550 +4 0 0 50 -1 0 12 0.0000 4 135 450 4425 2775 ctrl i/f\001 +-6 +6 6000 3900 7200 4425 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 6000 3900 7200 3900 7200 4425 6000 4425 6000 3900 +4 0 0 50 -1 0 12 0.0000 4 135 600 6075 4125 EAPOL\001 +4 0 0 50 -1 0 12 0.0000 4 135 1065 6075 4350 state machine\001 +-6 +6 2775 3150 4050 3450 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 2775 3150 4050 3150 4050 3450 2775 3450 2775 3150 +4 0 0 50 -1 0 12 0.0000 4 180 990 2925 3375 configuration\001 +-6 +6 3450 1200 4575 1500 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 3450 1200 4575 1200 4575 1500 3450 1500 3450 1200 +4 0 0 50 -1 0 12 0.0000 4 180 870 3600 1425 hostapd_cli\001 +-6 +6 3525 7800 5775 8100 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 3525 7800 5775 7800 5775 8100 3525 8100 3525 7800 +4 0 0 50 -1 0 12 0.0000 4 135 2145 3600 8025 kernel network device driver\001 +-6 +6 4275 6000 5100 6300 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 4275 6000 5100 6000 5100 6300 4275 6300 4275 6000 +4 0 0 50 -1 0 12 0.0000 4 135 630 4350 6225 driver i/f\001 +-6 +6 8175 4725 9225 5025 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 8175 4725 9225 4725 9225 5025 8175 5025 8175 4725 +4 0 0 50 -1 0 12 0.0000 4 135 735 8250 4950 EAP-TLS\001 +-6 +6 9300 4725 10350 5025 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 9300 4725 10350 4725 10350 5025 9300 5025 9300 4725 +4 0 0 50 -1 0 12 0.0000 4 135 810 9375 4950 EAP-MD5\001 +-6 +6 8175 5100 9225 5400 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 8175 5100 9225 5100 9225 5400 8175 5400 8175 5100 +4 0 0 50 -1 0 12 0.0000 4 135 885 8250 5325 EAP-PEAP\001 +-6 +6 9300 5100 10350 5400 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 9300 5100 10350 5100 10350 5400 9300 5400 9300 5100 +4 0 0 50 -1 0 12 0.0000 4 135 840 9375 5325 EAP-TTLS\001 +-6 +6 8175 5475 9225 5775 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 8175 5475 9225 5475 9225 5775 8175 5775 8175 5475 +4 0 0 50 -1 0 12 0.0000 4 135 780 8250 5700 EAP-GTC\001 +-6 +6 8175 5850 9225 6150 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 8175 5850 9225 5850 9225 6150 8175 6150 8175 5850 +4 0 0 50 -1 0 12 0.0000 4 135 750 8250 6075 EAP-SIM\001 +-6 +6 8175 6225 9225 6525 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 8175 6225 9225 6225 9225 6525 8175 6525 8175 6225 +4 0 0 50 -1 0 12 0.0000 4 135 765 8250 6450 EAP-PSK\001 +-6 +6 9300 5850 10350 6150 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 9300 5850 10350 5850 10350 6150 9300 6150 9300 5850 +4 0 0 50 -1 0 12 0.0000 4 135 825 9375 6075 EAP-AKA\001 +-6 +6 9300 5475 10350 5775 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 9300 5475 10350 5475 10350 5775 9300 5775 9300 5475 +4 0 0 50 -1 0 12 0.0000 4 135 795 9375 5700 EAP-PAX\001 +-6 +6 8175 6600 9675 6900 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 8175 6600 9675 6600 9675 6900 8175 6900 8175 6600 +4 0 0 50 -1 0 12 0.0000 4 135 1365 8250 6825 EAP-MSCHAPv2\001 +-6 +6 8700 3450 9375 3750 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 8700 3450 9375 3450 9375 3750 8700 3750 8700 3450 +4 0 0 50 -1 0 12 0.0000 4 150 480 8775 3675 crypto\001 +-6 +6 9600 3450 10275 3750 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 9600 3450 10275 3450 10275 3750 9600 3750 9600 3450 +4 0 0 50 -1 0 12 0.0000 4 135 315 9750 3675 TLS\001 +-6 +6 6000 5775 7200 6300 +6 6000 5775 7200 6300 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 6000 5775 7200 5775 7200 6300 6000 6300 6000 5775 +4 0 0 50 -1 0 12 0.0000 4 135 690 6075 6000 RADIUS\001 +-6 +4 0 0 50 -1 0 12 0.0000 4 90 480 6075 6225 server\001 +-6 +6 8100 2250 8925 2775 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 8100 2250 8925 2250 8925 2775 8100 2775 8100 2250 +4 0 0 50 -1 0 12 0.0000 4 135 690 8175 2475 RADIUS\001 +4 0 0 50 -1 0 12 0.0000 4 135 420 8175 2700 client\001 +-6 +6 3150 5475 4425 5775 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 3150 5475 4425 5475 4425 5775 3150 5775 3150 5475 +4 0 0 50 -1 0 12 0.0000 4 135 990 3300 5700 driver events\001 +-6 +6 1950 5550 2625 6075 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 1950 5550 2625 5550 2625 6075 1950 6075 1950 5550 +4 0 0 50 -1 0 12 0.0000 4 135 540 2025 5775 Station\001 +4 0 0 50 -1 0 12 0.0000 4 135 375 2025 6000 table\001 +-6 +6 1875 4725 2925 5250 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 1875 4725 2925 4725 2925 5250 1875 5250 1875 4725 +4 0 0 50 -1 0 12 0.0000 4 135 960 1950 4950 IEEE 802.11\001 +4 0 0 50 -1 0 12 0.0000 4 135 555 1950 5175 MLME\001 +-6 +2 1 1 1 0 7 50 -1 -1 3.000 0 0 -1 0 0 2 + 1275 4200 1875 4200 +2 1 1 1 0 7 50 -1 -1 4.000 0 0 -1 0 0 2 + 4500 2550 3900 1500 +2 1 1 1 0 7 50 -1 -1 4.000 0 0 -1 0 0 2 + 4800 2550 5400 1500 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 2925 4200 4350 4200 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 5025 3900 6000 3000 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 5025 4200 6000 4200 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 4650 6000 4650 4425 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 6600 4425 6600 4950 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 6600 3225 6600 3900 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 7200 5250 8100 5250 +2 1 0 1 2 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 9075 4425 9075 3750 +2 1 0 1 2 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 7200 3000 8700 3525 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 4650 3900 4650 2850 +2 1 0 1 2 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 7200 4125 8700 3675 +2 1 0 1 2 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 6000 4350 5025 6000 +2 1 0 1 2 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 6000 3150 4875 6000 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 1500 2100 10800 2100 10800 7500 1500 7500 1500 2100 +2 1 0 1 2 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 9900 4425 9900 3750 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 1 + 4350 3900 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 4350 3900 4050 3450 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 4350 4425 4050 5475 +2 1 1 1 0 7 50 -1 -1 4.000 0 0 -1 0 0 2 + 2250 7200 4200 7800 +2 1 1 1 0 7 50 -1 -1 4.000 0 0 -1 0 0 2 + 7200 7200 5100 7800 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 2775 6900 3675 6900 3675 7200 2775 7200 2775 6900 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 3750 6900 4650 6900 4650 7200 3750 7200 3750 6900 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 4 + 2250 6900 2250 6600 7200 6600 7200 6900 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 3225 6900 3225 6600 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 4200 6900 4200 6600 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 5175 6900 5175 6600 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 6150 6900 6150 6600 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 4650 6600 4650 6300 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 1800 6900 2700 6900 2700 7200 1800 7200 1800 6900 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 4725 6900 5625 6900 5625 7200 4725 7200 4725 6900 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 5700 6900 6600 6900 6600 7200 5700 7200 5700 6900 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 6675 6900 7800 6900 7800 7200 6675 7200 6675 6900 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 8100 6975 10425 6975 10425 4425 8100 4425 8100 6975 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 6600 5475 6600 5775 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 5025 4425 6000 5775 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 3 + 4800 3900 5925 2550 8100 2550 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 7200 3900 8475 2775 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 9450 2250 10425 2250 10425 2775 9450 2775 9450 2250 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 8925 2475 9450 2475 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 2325 5550 2325 5250 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 2925 4950 4350 4275 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 3 + 2850 4725 5775 2400 8100 2400 +4 0 0 50 -1 0 12 0.0000 4 135 915 375 3975 EAPOL and\001 +4 0 0 50 -1 0 12 0.0000 4 180 630 375 4200 pre-auth\001 +4 0 0 50 -1 0 12 0.0000 4 180 810 375 4425 ethertypes\001 +4 0 0 50 -1 0 12 0.0000 4 135 1050 375 4650 from/to kernel\001 +4 0 0 50 -1 0 12 0.0000 4 135 1920 3675 1875 frontend control interface\001 +4 0 0 50 -1 2 14 0.0000 4 195 720 1637 2371 hostapd\001 +4 0 0 50 -1 0 12 0.0000 4 180 600 3825 7125 prism54\001 +4 0 0 50 -1 0 12 0.0000 4 180 510 1875 7125 hostap\001 +4 0 0 50 -1 0 12 0.0000 4 135 600 2850 7125 madwifi\001 +4 0 0 50 -1 0 12 0.0000 4 135 270 4800 7125 bsd\001 +4 0 0 50 -1 0 12 0.0000 4 105 300 6750 7125 test\001 +4 0 0 50 -1 0 12 0.0000 4 135 420 5775 7125 wired\001 +4 0 0 50 -1 0 12 0.0000 4 135 1050 8700 4650 EAP methods\001 +4 0 0 50 -1 0 12 0.0000 4 135 690 9525 2475 RADIUS\001 +4 0 0 50 -1 0 12 0.0000 4 180 825 9525 2700 accounting\001 diff --git a/contrib/wpa/hostapd/doc/kerneldoc2doxygen.pl b/contrib/wpa/hostapd/doc/kerneldoc2doxygen.pl new file mode 100755 index 0000000..68835a1 --- /dev/null +++ b/contrib/wpa/hostapd/doc/kerneldoc2doxygen.pl @@ -0,0 +1,129 @@ +#!/usr/bin/perl -w +# +########################################################################## +# Convert kernel-doc style comments to Doxygen comments. +########################################################################## +# +# This script reads a C source file from stdin, and writes +# to stdout. Normal usage: +# +# $ mv file.c file.c.gtkdoc +# $ kerneldoc2doxygen.pl <file.c.gtkdoc >file.c +# +# Or to do the same thing with multiple files: +# $ perl -i.gtkdoc kerneldoc2doxygen.pl *.c *.h +# +# This script may also be suitable for use as a Doxygen input filter, +# but that has not been tested. +# +# Back up your source files before using this script!! +# +########################################################################## +# Copyright (C) 2003 Jonathan Foster <jon@jon-foster.co.uk> +# Copyright (C) 2005 Jouni Malinen <j@w1.fi> +# (modified for kerneldoc format used in wpa_supplicant) +# +# 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +# or look at http://www.gnu.org/licenses/gpl.html +########################################################################## + + +########################################################################## +# +# This function converts a single comment from gtk-doc to Doxygen format. +# The parameter does not include the opening or closing lines +# (i.e. given a comment like this: +# "/**\n" +# " * FunctionName:\n" +# " * @foo: This describes the foo parameter\n" +# " * @bar: This describes the bar parameter\n" +# " * @Returns: This describes the return value\n" +# " *\n" +# " * This describes the function.\n" +# " */\n" +# This function gets: +# " * FunctionName:\n" +# " * @foo: This describes the foo parameter\n" +# " * @bar: This describes the bar parameter\n" +# " * @Returns: This describes the return value\n" +# " *\n" +# " * This describes the function.\n" +# And it returns: +# " * This describes the function.\n" +# " *\n" +# " * @param foo This describes the foo parameter\n" +# " * @param bar This describes the bar parameter\n" +# " * @return This describes the return value\n" +# ) +# +sub fixcomment { + $t = $_[0]; + + # " * func: foo" --> "\brief foo\n" + # " * struct bar: foo" --> "\brief foo\n" + # If this fails, not a kernel-doc comment ==> return unmodified. + ($t =~ s/^[\t ]*\*[\t ]*(struct )?([^ \t\n]*) - ([^\n]*)/\\brief $3\n/s) + or return $t; + + # " * Returns: foo" --> "\return foo" + $t =~ s/\n[\t ]*\*[\t ]*Returns:/\n\\return/sig; + + # " * @foo: bar" --> "\param foo bar" + # Handle two common typos: No ":", or "," instead of ":". + $t =~ s/\n[\t ]*\*[\t ]*\@([^ :,]*)[:,]?[\t ]*/\n\\param $1 /sg; + + return $t; +} + +########################################################################## +# Start of main code + +# Read entire stdin into memory - one multi-line string. +$_ = do { local $/; <> }; + +s{^/\*\n \*}{/\*\* \\file\n\\brief}; +s{ \* Copyright}{\\par Copyright\nCopyright}; + +# Fix any comments like "/*************" so they don't match. +# "/***" ===> "/* *" +s{/\*\*\*}{/\* \*}gs; + +# The main comment-detection code. +s{ + ( # $1 = Open comment + /\*\* # Open comment + (?!\*) # Do not match /*** (redundant due to fixup above). + [\t ]*\n? # If 1st line is whitespace, match the lot (including the newline). + ) + (.*?) # $2 = Body of comment (multi-line) + ( # $3 = Close comment + ( # If possible, match the whitespace before the close-comment + (?<=\n) # This part only matches after a newline + [\t ]* # Eat whitespace + )? + \*/ # Close comment + ) + } + { + $1 . fixcomment($2) . $3 + }gesx; +# ^^^^ Modes: g - Global, match all occurances. +# e - Evaluate the replacement as an expression. +# s - Single-line - allows the pattern to match across newlines. +# x - eXtended pattern, ignore embedded whitespace +# and allow comments. + +# Write results to stdout +print $_; + diff --git a/contrib/wpa/hostapd/doc/mainpage.doxygen b/contrib/wpa/hostapd/doc/mainpage.doxygen new file mode 100644 index 0000000..7cf95de --- /dev/null +++ b/contrib/wpa/hostapd/doc/mainpage.doxygen @@ -0,0 +1,52 @@ +/** +\mainpage Developers' documentation for hostapd + +hostapd includes IEEE 802.11 access point management (authentication / +association), IEEE 802.1X/WPA/WPA2 Authenticator, EAP server, and +RADIUS authentication server functionality. It can be build with +various configuration option, e.g., a standalone AP management +solution or a RADIUS authentication server with support for number of +EAP methods. + +The goal of this documentation and comments in the source code is to +give enough information for other developers to understand how hostapd +has been implemented, how it can be modified, how new drivers can be +supported, and how hostapd can be ported to other operating +systems. If any information is missing, feel free to contact Jouni +Malinen <j@w1.fi> for more information. Contributions as +patch files are also very welcome at the same address. Please note +that hostapd is licensed under dual license, GPLv2 or BSD at user's +choice. All contributions to hostapd are expected to use compatible +licensing terms. + +The source code and read-only access to hostapd CVS repository +is available from the project home page at +http://hostap.epitest.fi/hostapd/. This developers' documentation +is also available as a PDF file from +http://hostap.epitest.fi/hostapd/hostapd-devel.pdf . + +The design goal for hostapd was to use hardware, driver, and +OS independent, portable C code for all WPA functionality. The source +code is divided into separate C files as shown on the \ref +code_structure "code structure page". All hardware/driver specific +functionality is in separate files that implement a \ref +driver_wrapper "well-defined driver API". Information about porting +to different target boards and operating systems is available on +the \ref porting "porting page". + +EAPOL (IEEE 802.1X) state machines are implemented as a separate +module that interacts with \ref eap_module "EAP server implementation". +Similarly, RADIUS authentication server is in its own separate module. +Both IEEE 802.1X and RADIUS authentication server can use EAP server +functionality. + +hostapd implements a \ref ctrl_iface_page "control interface" that can +be used by external programs to control the operations of the hostapdt +daemon and to get status information and event notifications. There is +a small C library that provides helper functions to facilitate the use +of the control interface. This library can also be used with C++. + +\image html hostapd.png "hostapd modules" +\image latex hostapd.eps "hostapd modules" width=15cm + +*/ diff --git a/contrib/wpa/hostapd/doc/porting.doxygen b/contrib/wpa/hostapd/doc/porting.doxygen new file mode 100644 index 0000000..0621791 --- /dev/null +++ b/contrib/wpa/hostapd/doc/porting.doxygen @@ -0,0 +1,5 @@ +/** +\page porting Porting to different target boards and operating systems + + +*/ diff --git a/contrib/wpa/hostapd/driver.h b/contrib/wpa/hostapd/driver.h new file mode 100644 index 0000000..45f5460 --- /dev/null +++ b/contrib/wpa/hostapd/driver.h @@ -0,0 +1,798 @@ +/* + * hostapd - driver interface definition + * Copyright (c) 2002-2007, Jouni Malinen <j@w1.fi> + * Copyright (c) 2007-2008, Intel Corporation + * + * 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 DRIVER_H +#define DRIVER_H + +struct hostapd_sta_add_params { + const u8 *addr; + u16 aid; + u16 capability; + const u8 *supp_rates; + size_t supp_rates_len; + int flags; + u16 listen_interval; + const struct ht_cap_ie *ht_capabilities; +}; + +struct hostapd_freq_params { + int mode; + int freq; + int ht_enabled; + int sec_channel_offset; /* 0 = HT40 disabled, -1 = HT40 enabled, + * secondary channel below primary, 1 = HT40 + * enabled, secondary channel above primary */ +}; + +enum hostapd_driver_if_type { + HOSTAPD_IF_VLAN, HOSTAPD_IF_WDS +}; + +struct wpa_driver_ops { + const char *name; /* as appears in the config file */ + + void * (*init)(struct hostapd_data *hapd); + void * (*init_bssid)(struct hostapd_data *hapd, const u8 *bssid); + void (*deinit)(void *priv); + + int (*wireless_event_init)(void *priv); + void (*wireless_event_deinit)(void *priv); + + /** + * set_8021x - enable/disable IEEE 802.1X support + * @ifname: Interface name (for multi-SSID/VLAN 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)(const char *ifname, 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)(const char *ifname, void *priv, int enabled); + + int (*set_encryption)(const char *ifname, void *priv, const char *alg, + const u8 *addr, int idx, + const u8 *key, size_t key_len, int txkey); + int (*get_seqnum)(const char *ifname, void *priv, const u8 *addr, + int idx, u8 *seq); + int (*get_seqnum_igtk)(const char *ifname, void *priv, const u8 *addr, + int idx, u8 *seq); + int (*flush)(void *priv); + int (*set_generic_elem)(const char *ifname, void *priv, const u8 *elem, + size_t elem_len); + + int (*read_sta_data)(void *priv, struct hostap_sta_driver_data *data, + const u8 *addr); + int (*send_eapol)(void *priv, const u8 *addr, const u8 *data, + size_t data_len, int encrypt, const u8 *own_addr); + int (*sta_deauth)(void *priv, const u8 *addr, int reason); + int (*sta_disassoc)(void *priv, const u8 *addr, int reason); + int (*sta_remove)(void *priv, const u8 *addr); + int (*get_ssid)(const char *ifname, void *priv, u8 *buf, int len); + int (*set_ssid)(const char *ifname, void *priv, const 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, const u8 *addr); + /* note: sta_add() is deprecated; use sta_add2() instead */ + int (*sta_add)(const char *ifname, void *priv, const u8 *addr, u16 aid, + u16 capability, u8 *supp_rates, size_t supp_rates_len, + int flags, u16 listen_interval); + int (*sta_add2)(const char *ifname, void *priv, + struct hostapd_sta_add_params *params); + int (*get_inact_sec)(void *priv, const u8 *addr); + int (*sta_clear_stats)(void *priv, const u8 *addr); + + /* note: set_freq() is deprecated; use set_freq2() instead */ + int (*set_freq)(void *priv, int mode, int freq); + int (*set_freq2)(void *priv, struct hostapd_freq_params *freq); + int (*set_rts)(void *priv, int rts); + int (*get_rts)(void *priv, int *rts); + int (*set_frag)(void *priv, int frag); + int (*get_frag)(void *priv, int *frag); + int (*set_retry)(void *priv, int short_retry, int long_retry); + int (*get_retry)(void *priv, int *short_retry, int *long_retry); + + int (*sta_set_flags)(void *priv, const u8 *addr, + int total_flags, int flags_or, int flags_and); + int (*set_rate_sets)(void *priv, int *supp_rates, int *basic_rates, + int mode); + int (*set_regulatory_domain)(void *priv, unsigned int rd); + int (*set_country)(void *priv, const char *country); + int (*set_ieee80211d)(void *priv, int enabled); + int (*set_beacon)(const char *ifname, void *priv, + u8 *head, size_t head_len, + u8 *tail, size_t tail_len); + + /* Configure internal bridge: + * 0 = disabled, i.e., client separation is enabled (no bridging of + * packets between associated STAs + * 1 = enabled, i.e., bridge packets between associated STAs (default) + */ + int (*set_internal_bridge)(void *priv, int value); + int (*set_beacon_int)(void *priv, int value); + int (*set_dtim_period)(const char *ifname, void *priv, int value); + /* Configure broadcast SSID mode: + * 0 = include SSID in Beacon frames and reply to Probe Request frames + * that use broadcast SSID + * 1 = hide SSID from Beacon frames and ignore Probe Request frames for + * broadcast SSID + */ + int (*set_broadcast_ssid)(void *priv, int value); + int (*set_cts_protect)(void *priv, int value); + int (*set_key_tx_rx_threshold)(void *priv, int value); + int (*set_preamble)(void *priv, int value); + int (*set_short_slot_time)(void *priv, int value); + int (*set_tx_queue_params)(void *priv, int queue, int aifs, int cw_min, + int cw_max, int burst_time); + int (*bss_add)(void *priv, const char *ifname, const u8 *bssid); + int (*bss_remove)(void *priv, const char *ifname); + int (*valid_bss_mask)(void *priv, const u8 *addr, const u8 *mask); + int (*passive_scan)(void *priv, int now, int our_mode_only, + int interval, int _listen, int *channel, + int *last_rx); + struct hostapd_hw_modes * (*get_hw_feature_data)(void *priv, + u16 *num_modes, + u16 *flags); + int (*if_add)(const char *iface, void *priv, + enum hostapd_driver_if_type type, char *ifname, + const u8 *addr); + int (*if_update)(void *priv, enum hostapd_driver_if_type type, + char *ifname, const u8 *addr); + int (*if_remove)(void *priv, enum hostapd_driver_if_type type, + const char *ifname, const u8 *addr); + int (*set_sta_vlan)(void *priv, const u8 *addr, const char *ifname, + int vlan_id); + /** + * commit - Optional commit changes handler + * @priv: driver private data + * Returns: 0 on success, -1 on failure + * + * This optional handler function can be registered if the driver + * interface implementation needs to commit changes (e.g., by setting + * network interface up) at the end of initial configuration. If set, + * this handler will be called after initial setup has been completed. + */ + int (*commit)(void *priv); + + int (*send_ether)(void *priv, const u8 *dst, const u8 *src, u16 proto, + const u8 *data, size_t data_len); + + int (*set_radius_acl_auth)(void *priv, const u8 *mac, int accepted, + u32 session_timeout); + int (*set_radius_acl_expire)(void *priv, const u8 *mac); + + int (*set_ht_params)(const char *ifname, void *priv, + const u8 *ht_capab, size_t ht_capab_len, + const u8 *ht_oper, size_t ht_oper_len); + + int (*set_wps_beacon_ie)(const char *ifname, void *priv, + const u8 *ie, size_t len); + int (*set_wps_probe_resp_ie)(const char *ifname, void *priv, + const u8 *ie, size_t len); +}; + +static inline void * +hostapd_driver_init(struct hostapd_data *hapd) +{ + if (hapd->driver == NULL || hapd->driver->init == NULL) + return NULL; + return hapd->driver->init(hapd); +} + +static inline void * +hostapd_driver_init_bssid(struct hostapd_data *hapd, const u8 *bssid) +{ + if (hapd->driver == NULL || hapd->driver->init_bssid == NULL) + return NULL; + return hapd->driver->init_bssid(hapd, bssid); +} + +static inline void +hostapd_driver_deinit(struct hostapd_data *hapd) +{ + if (hapd->driver == NULL || hapd->driver->deinit == NULL) + return; + hapd->driver->deinit(hapd->drv_priv); +} + +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->drv_priv); +} + +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->drv_priv); +} + +static inline int +hostapd_set_ieee8021x(const char *ifname, struct hostapd_data *hapd, + int enabled) +{ + if (hapd->driver == NULL || hapd->driver->set_ieee8021x == NULL) + return 0; + return hapd->driver->set_ieee8021x(ifname, hapd->drv_priv, 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->conf->iface, hapd->drv_priv, + enabled); +} + +static inline int +hostapd_set_encryption(const char *ifname, struct hostapd_data *hapd, + const char *alg, const u8 *addr, int idx, + u8 *key, size_t key_len, int txkey) +{ + if (hapd->driver == NULL || hapd->driver->set_encryption == NULL) + return 0; + return hapd->driver->set_encryption(ifname, hapd->drv_priv, alg, addr, + idx, key, key_len, txkey); +} + +static inline int +hostapd_get_seqnum(const char *ifname, struct hostapd_data *hapd, + const u8 *addr, int idx, u8 *seq) +{ + if (hapd->driver == NULL || hapd->driver->get_seqnum == NULL) + return 0; + return hapd->driver->get_seqnum(ifname, hapd->drv_priv, addr, idx, + seq); +} + +static inline int +hostapd_get_seqnum_igtk(const char *ifname, struct hostapd_data *hapd, + const u8 *addr, int idx, u8 *seq) +{ + if (hapd->driver == NULL || hapd->driver->get_seqnum_igtk == NULL) + return -1; + return hapd->driver->get_seqnum_igtk(ifname, hapd->drv_priv, 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->drv_priv); +} + +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->conf->iface, + hapd->drv_priv, elem, elem_len); +} + +static inline int +hostapd_read_sta_data(struct hostapd_data *hapd, + struct hostap_sta_driver_data *data, const u8 *addr) +{ + if (hapd->driver == NULL || hapd->driver->read_sta_data == NULL) + return -1; + return hapd->driver->read_sta_data(hapd->drv_priv, data, addr); +} + +static inline int +hostapd_send_eapol(struct hostapd_data *hapd, const u8 *addr, const 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->drv_priv, addr, data, data_len, + encrypt, hapd->own_addr); +} + +static inline int +hostapd_sta_deauth(struct hostapd_data *hapd, const u8 *addr, int reason) +{ + if (hapd->driver == NULL || hapd->driver->sta_deauth == NULL) + return 0; + return hapd->driver->sta_deauth(hapd->drv_priv, addr, reason); +} + +static inline int +hostapd_sta_disassoc(struct hostapd_data *hapd, const u8 *addr, int reason) +{ + if (hapd->driver == NULL || hapd->driver->sta_disassoc == NULL) + return 0; + return hapd->driver->sta_disassoc(hapd->drv_priv, addr, reason); +} + +static inline int +hostapd_sta_remove(struct hostapd_data *hapd, const u8 *addr) +{ + if (hapd->driver == NULL || hapd->driver->sta_remove == NULL) + return 0; + return hapd->driver->sta_remove(hapd->drv_priv, 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->conf->iface, hapd->drv_priv, buf, + len); +} + +static inline int +hostapd_set_ssid(struct hostapd_data *hapd, const u8 *buf, size_t len) +{ + if (hapd->driver == NULL || hapd->driver->set_ssid == NULL) + return 0; + return hapd->driver->set_ssid(hapd->conf->iface, hapd->drv_priv, 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->drv_priv, msg, len, flags); +} + +static inline int +hostapd_set_assoc_ap(struct hostapd_data *hapd, const u8 *addr) +{ + if (hapd->driver == NULL || hapd->driver->set_assoc_ap == NULL) + return 0; + return hapd->driver->set_assoc_ap(hapd->drv_priv, 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->drv_priv, enabled); +} + +static inline int +hostapd_sta_add(const char *ifname, struct hostapd_data *hapd, const u8 *addr, + u16 aid, u16 capability, const u8 *supp_rates, + size_t supp_rates_len, int flags, u16 listen_interval, + const struct ht_cap_ie *ht_capabilities) +{ + if (hapd->driver == NULL) + return 0; + + if (hapd->driver->sta_add2) { + struct hostapd_sta_add_params params; + os_memset(¶ms, 0, sizeof(params)); + params.addr = addr; + params.aid = aid; + params.capability = capability; + params.supp_rates = supp_rates; + params.supp_rates_len = supp_rates_len; + params.flags = flags; + params.listen_interval = listen_interval; + params.ht_capabilities = ht_capabilities; + return hapd->driver->sta_add2(ifname, hapd->drv_priv, ¶ms); + } + + if (hapd->driver->sta_add == NULL) + return 0; + return hapd->driver->sta_add(ifname, hapd->drv_priv, addr, aid, + capability, (u8 *) supp_rates, + supp_rates_len, + flags, listen_interval); +} + +static inline int +hostapd_get_inact_sec(struct hostapd_data *hapd, const u8 *addr) +{ + if (hapd->driver == NULL || hapd->driver->get_inact_sec == NULL) + return 0; + return hapd->driver->get_inact_sec(hapd->drv_priv, addr); +} + +static inline int +hostapd_set_freq(struct hostapd_data *hapd, int mode, int freq, int ht_enabled, + int sec_channel_offset) +{ + if (hapd->driver == NULL) + return 0; + if (hapd->driver->set_freq2) { + struct hostapd_freq_params data; + os_memset(&data, 0, sizeof(data)); + data.mode = mode; + data.freq = freq; + data.ht_enabled = ht_enabled; + data.sec_channel_offset = sec_channel_offset; + return hapd->driver->set_freq2(hapd->drv_priv, &data); + } + + if (hapd->driver->set_freq == NULL) + return 0; + return hapd->driver->set_freq(hapd->drv_priv, mode, freq); +} + +static inline int +hostapd_set_rts(struct hostapd_data *hapd, int rts) +{ + if (hapd->driver == NULL || hapd->driver->set_rts == NULL) + return 0; + return hapd->driver->set_rts(hapd->drv_priv, rts); +} + +static inline int +hostapd_get_rts(struct hostapd_data *hapd, int *rts) +{ + if (hapd->driver == NULL || hapd->driver->get_rts == NULL) + return 0; + return hapd->driver->get_rts(hapd->drv_priv, rts); +} + +static inline int +hostapd_set_frag(struct hostapd_data *hapd, int frag) +{ + if (hapd->driver == NULL || hapd->driver->set_frag == NULL) + return 0; + return hapd->driver->set_frag(hapd->drv_priv, frag); +} + +static inline int +hostapd_get_frag(struct hostapd_data *hapd, int *frag) +{ + if (hapd->driver == NULL || hapd->driver->get_frag == NULL) + return 0; + return hapd->driver->get_frag(hapd->drv_priv, frag); +} + +static inline int +hostapd_set_retry(struct hostapd_data *hapd, int short_retry, int long_retry) +{ + if (hapd->driver == NULL || hapd->driver->set_retry == NULL) + return 0; + return hapd->driver->set_retry(hapd->drv_priv, short_retry, + long_retry); +} + +static inline int +hostapd_get_retry(struct hostapd_data *hapd, int *short_retry, int *long_retry) +{ + if (hapd->driver == NULL || hapd->driver->get_retry == NULL) + return 0; + return hapd->driver->get_retry(hapd->drv_priv, short_retry, + long_retry); +} + +static inline int +hostapd_sta_set_flags(struct hostapd_data *hapd, u8 *addr, + int total_flags, int flags_or, int flags_and) +{ + if (hapd->driver == NULL || hapd->driver->sta_set_flags == NULL) + return 0; + return hapd->driver->sta_set_flags(hapd->drv_priv, addr, total_flags, + flags_or, flags_and); +} + +static inline int +hostapd_set_rate_sets(struct hostapd_data *hapd, int *supp_rates, + int *basic_rates, int mode) +{ + if (hapd->driver == NULL || hapd->driver->set_rate_sets == NULL) + return 0; + return hapd->driver->set_rate_sets(hapd->drv_priv, supp_rates, + basic_rates, mode); +} + +static inline int +hostapd_set_regulatory_domain(struct hostapd_data *hapd, unsigned int rd) +{ + if (hapd->driver == NULL || + hapd->driver->set_regulatory_domain == NULL) + return 0; + return hapd->driver->set_regulatory_domain(hapd->drv_priv, rd); +} + +static inline int +hostapd_set_country(struct hostapd_data *hapd, const char *country) +{ + if (hapd->driver == NULL || + hapd->driver->set_country == NULL) + return 0; + return hapd->driver->set_country(hapd->drv_priv, country); +} + +static inline int +hostapd_set_ieee80211d(struct hostapd_data *hapd, int enabled) +{ + if (hapd->driver == NULL || + hapd->driver->set_ieee80211d == NULL) + return 0; + return hapd->driver->set_ieee80211d(hapd->drv_priv, enabled); +} + +static inline int +hostapd_sta_clear_stats(struct hostapd_data *hapd, const u8 *addr) +{ + if (hapd->driver == NULL || hapd->driver->sta_clear_stats == NULL) + return 0; + return hapd->driver->sta_clear_stats(hapd->drv_priv, addr); +} + +static inline int +hostapd_set_beacon(const char *ifname, struct hostapd_data *hapd, + u8 *head, size_t head_len, + u8 *tail, size_t tail_len) +{ + if (hapd->driver == NULL || hapd->driver->set_beacon == NULL) + return 0; + return hapd->driver->set_beacon(ifname, hapd->drv_priv, head, head_len, + tail, tail_len); +} + +static inline int +hostapd_set_internal_bridge(struct hostapd_data *hapd, int value) +{ + if (hapd->driver == NULL || hapd->driver->set_internal_bridge == NULL) + return 0; + return hapd->driver->set_internal_bridge(hapd->drv_priv, value); +} + +static inline int +hostapd_set_beacon_int(struct hostapd_data *hapd, int value) +{ + if (hapd->driver == NULL || hapd->driver->set_beacon_int == NULL) + return 0; + return hapd->driver->set_beacon_int(hapd->drv_priv, value); +} + +static inline int +hostapd_set_dtim_period(struct hostapd_data *hapd, int value) +{ + if (hapd->driver == NULL || hapd->driver->set_dtim_period == NULL) + return 0; + return hapd->driver->set_dtim_period(hapd->conf->iface, hapd->drv_priv, + value); +} + +static inline int +hostapd_set_broadcast_ssid(struct hostapd_data *hapd, int value) +{ + if (hapd->driver == NULL || hapd->driver->set_broadcast_ssid == NULL) + return 0; + return hapd->driver->set_broadcast_ssid(hapd->drv_priv, value); +} + +static inline int +hostapd_set_cts_protect(struct hostapd_data *hapd, int value) +{ + if (hapd->driver == NULL || hapd->driver->set_cts_protect == NULL) + return 0; + return hapd->driver->set_cts_protect(hapd->drv_priv, value); +} + +static inline int +hostapd_set_key_tx_rx_threshold(struct hostapd_data *hapd, int value) +{ + if (hapd->driver == NULL || + hapd->driver->set_key_tx_rx_threshold == NULL) + return 0; + return hapd->driver->set_key_tx_rx_threshold(hapd->drv_priv, value); +} + +static inline int +hostapd_set_preamble(struct hostapd_data *hapd, int value) +{ + if (hapd->driver == NULL || hapd->driver->set_preamble == NULL) + return 0; + return hapd->driver->set_preamble(hapd->drv_priv, value); +} + +static inline int +hostapd_set_short_slot_time(struct hostapd_data *hapd, int value) +{ + if (hapd->driver == NULL || hapd->driver->set_short_slot_time == NULL) + return 0; + return hapd->driver->set_short_slot_time(hapd->drv_priv, value); +} + +static inline int +hostapd_set_tx_queue_params(struct hostapd_data *hapd, int queue, int aifs, + int cw_min, int cw_max, int burst_time) +{ + if (hapd->driver == NULL || hapd->driver->set_tx_queue_params == NULL) + return 0; + return hapd->driver->set_tx_queue_params(hapd->drv_priv, queue, aifs, + cw_min, cw_max, burst_time); +} + +static inline int +hostapd_bss_add(struct hostapd_data *hapd, const char *ifname, const u8 *bssid) +{ + if (hapd->driver == NULL || hapd->driver->bss_add == NULL) + return 0; + return hapd->driver->bss_add(hapd->drv_priv, ifname, bssid); +} + +static inline int +hostapd_bss_remove(struct hostapd_data *hapd, const char *ifname) +{ + if (hapd->driver == NULL || hapd->driver->bss_remove == NULL) + return 0; + return hapd->driver->bss_remove(hapd->drv_priv, ifname); +} + +static inline int +hostapd_valid_bss_mask(struct hostapd_data *hapd, const u8 *addr, + const u8 *mask) +{ + if (hapd->driver == NULL || hapd->driver->valid_bss_mask == NULL) + return 1; + return hapd->driver->valid_bss_mask(hapd->drv_priv, addr, mask); +} + +static inline int +hostapd_if_add(struct hostapd_data *hapd, enum hostapd_driver_if_type type, + char *ifname, const u8 *addr) +{ + if (hapd->driver == NULL || hapd->driver->if_add == NULL) + return -1; + return hapd->driver->if_add(hapd->conf->iface, hapd->drv_priv, type, + ifname, addr); +} + +static inline int +hostapd_if_update(struct hostapd_data *hapd, enum hostapd_driver_if_type type, + char *ifname, const u8 *addr) +{ + if (hapd->driver == NULL || hapd->driver->if_update == NULL) + return -1; + return hapd->driver->if_update(hapd->drv_priv, type, ifname, addr); +} + +static inline int +hostapd_if_remove(struct hostapd_data *hapd, enum hostapd_driver_if_type type, + char *ifname, const u8 *addr) +{ + if (hapd->driver == NULL || hapd->driver->if_remove == NULL) + return -1; + return hapd->driver->if_remove(hapd->drv_priv, type, ifname, addr); +} + +static inline int +hostapd_passive_scan(struct hostapd_data *hapd, int now, int our_mode_only, + int interval, int _listen, int *channel, + int *last_rx) +{ + if (hapd->driver == NULL || hapd->driver->passive_scan == NULL) + return -1; + return hapd->driver->passive_scan(hapd->drv_priv, now, our_mode_only, + interval, _listen, channel, last_rx); +} + +static inline struct hostapd_hw_modes * +hostapd_get_hw_feature_data(struct hostapd_data *hapd, u16 *num_modes, + u16 *flags) +{ + if (hapd->driver == NULL || hapd->driver->get_hw_feature_data == NULL) + return NULL; + return hapd->driver->get_hw_feature_data(hapd->drv_priv, num_modes, + flags); +} + +static inline int +hostapd_set_sta_vlan(const char *ifname, struct hostapd_data *hapd, + const u8 *addr, int vlan_id) +{ + if (hapd->driver == NULL || hapd->driver->set_sta_vlan == NULL) + return 0; + return hapd->driver->set_sta_vlan(hapd->drv_priv, addr, ifname, vlan_id); +} + +static inline int +hostapd_driver_commit(struct hostapd_data *hapd) +{ + if (hapd->driver == NULL || hapd->driver->commit == NULL) + return 0; + return hapd->driver->commit(hapd->drv_priv); +} + +static inline int +hostapd_set_radius_acl_auth(struct hostapd_data *hapd, const u8 *mac, + int accepted, u32 session_timeout) +{ + if (hapd->driver == NULL || hapd->driver->set_radius_acl_auth == NULL) + return 0; + return hapd->driver->set_radius_acl_auth(hapd->drv_priv, mac, accepted, + session_timeout); +} + +static inline int +hostapd_set_radius_acl_expire(struct hostapd_data *hapd, const u8 *mac) +{ + if (hapd->driver == NULL || + hapd->driver->set_radius_acl_expire == NULL) + return 0; + return hapd->driver->set_radius_acl_expire(hapd->drv_priv, mac); +} + +#ifdef CONFIG_IEEE80211N +static inline int +hostapd_set_ht_params(const char *ifname, struct hostapd_data *hapd, + const u8 *ht_capab, size_t ht_capab_len, + const u8 *ht_oper, size_t ht_oper_len) +{ + if (hapd->driver == NULL || hapd->driver->set_ht_params == NULL || + ht_capab == NULL || ht_oper == NULL) + return 0; + return hapd->driver->set_ht_params( + ifname, hapd->drv_priv, ht_capab, ht_capab_len, + ht_oper, ht_oper_len); +} +#endif /* CONFIG_IEEE80211N */ + +static inline int +hostapd_drv_none(struct hostapd_data *hapd) +{ + return hapd->driver && os_strcmp(hapd->driver->name, "none") == 0; +} + +static inline int +hostapd_set_wps_beacon_ie(struct hostapd_data *hapd, const u8 *ie, size_t len) +{ + if (hapd->driver == NULL || hapd->driver->set_wps_beacon_ie == NULL) + return 0; + return hapd->driver->set_wps_beacon_ie(hapd->conf->iface, + hapd->drv_priv, ie, len); +} + +static inline int +hostapd_set_wps_probe_resp_ie(struct hostapd_data *hapd, const u8 *ie, + size_t len) +{ + if (hapd->driver == NULL || + hapd->driver->set_wps_probe_resp_ie == NULL) + return 0; + return hapd->driver->set_wps_probe_resp_ie(hapd->conf->iface, + hapd->drv_priv, ie, len); +} + +#endif /* DRIVER_H */ diff --git a/contrib/wpa/hostapd/drivers.c b/contrib/wpa/hostapd/drivers.c new file mode 100644 index 0000000..3006190 --- /dev/null +++ b/contrib/wpa/hostapd/drivers.c @@ -0,0 +1,71 @@ +/* + * hostapd / driver interface list + * Copyright (c) 2007, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + + +#ifdef CONFIG_DRIVER_HOSTAP +extern struct wpa_driver_ops wpa_driver_hostap_ops; /* driver_hostap.c */ +#endif /* CONFIG_DRIVER_HOSTAP */ +#ifdef CONFIG_DRIVER_NL80211 +extern struct wpa_driver_ops wpa_driver_nl80211_ops; /* driver_nl80211.c */ +#endif /* CONFIG_DRIVER_NL80211 */ +#ifdef CONFIG_DRIVER_PRISM54 +extern struct wpa_driver_ops wpa_driver_prism54_ops; /* driver_prism54.c */ +#endif /* CONFIG_DRIVER_PRISM54 */ +#ifdef CONFIG_DRIVER_MADWIFI +extern struct wpa_driver_ops wpa_driver_madwifi_ops; /* driver_madwifi.c */ +#endif /* CONFIG_DRIVER_MADWIFI */ +#ifdef CONFIG_DRIVER_BSD +extern struct wpa_driver_ops wpa_driver_bsd_ops; /* driver_bsd.c */ +#endif /* CONFIG_DRIVER_BSD */ +#ifdef CONFIG_DRIVER_WIRED +extern struct wpa_driver_ops wpa_driver_wired_ops; /* driver_wired.c */ +#endif /* CONFIG_DRIVER_WIRED */ +#ifdef CONFIG_DRIVER_TEST +extern struct wpa_driver_ops wpa_driver_test_ops; /* driver_test.c */ +#endif /* CONFIG_DRIVER_TEST */ +#ifdef CONFIG_DRIVER_NONE +extern struct wpa_driver_ops wpa_driver_none_ops; /* driver_none.c */ +#endif /* CONFIG_DRIVER_NONE */ + + +struct wpa_driver_ops *hostapd_drivers[] = +{ +#ifdef CONFIG_DRIVER_HOSTAP + &wpa_driver_hostap_ops, +#endif /* CONFIG_DRIVER_HOSTAP */ +#ifdef CONFIG_DRIVER_NL80211 + &wpa_driver_nl80211_ops, +#endif /* CONFIG_DRIVER_NL80211 */ +#ifdef CONFIG_DRIVER_PRISM54 + &wpa_driver_prism54_ops, +#endif /* CONFIG_DRIVER_PRISM54 */ +#ifdef CONFIG_DRIVER_MADWIFI + &wpa_driver_madwifi_ops, +#endif /* CONFIG_DRIVER_MADWIFI */ +#ifdef CONFIG_DRIVER_BSD + &wpa_driver_bsd_ops, +#endif /* CONFIG_DRIVER_BSD */ +#ifdef CONFIG_DRIVER_WIRED + &wpa_driver_wired_ops, +#endif /* CONFIG_DRIVER_WIRED */ +#ifdef CONFIG_DRIVER_TEST + &wpa_driver_test_ops, +#endif /* CONFIG_DRIVER_TEST */ +#ifdef CONFIG_DRIVER_NONE + &wpa_driver_none_ops, +#endif /* CONFIG_DRIVER_NONE */ + NULL +}; diff --git a/contrib/wpa/hostapd/eap_testing.txt b/contrib/wpa/hostapd/eap_testing.txt new file mode 100644 index 0000000..04468c3 --- /dev/null +++ b/contrib/wpa/hostapd/eap_testing.txt @@ -0,0 +1,77 @@ +Interoperability testing of hostapd's IEEE 802.1X/EAPOL authentication + +Test matrix + ++) tested successfully +F) failed +-) peer did not support +?) not tested + +XSupplicant --------------------------------. +Intel PROSet ---------------------------. | +Windows XP -------------------------. | | +Mac OS X 10.4 ------------------. | | | +Nokia S60 ------------------. | | | | +wpa_supplicant ---------. | | | | | + | | | | | | + +EAP-MD5 + - ? ? - +EAP-GTC + - ? - - +EAP-MSCHAPv2 + - ? - - +EAP-TLS + + +1 + + +EAP-PEAPv0/MSCHAPv2 + + + + + + +EAP-PEAPv0/GTC + + + - + +EAP-PEAPv0/MD5 + - + - - +EAP-PEAPv0/TLS + F - + + +EAP-PEAPv0/SIM + + - - - +EAP-PEAPv0/AKA + + - - - +EAP-PEAPv0/PSK + - - - - +EAP-PEAPv0/PAX + - - - - +EAP-PEAPv0/SAKE + - - - - +EAP-PEAPv0/GPSK + - - - - +EAP-PEAPv1/MSCHAPv2 + + + - + + +EAP-PEAPv1/GTC + + + - + +EAP-PEAPv1/MD5 + - + - - +EAP-PEAPv1/TLS + F - - + +EAP-PEAPv1/SIM + + - - - +EAP-PEAPv1/AKA + + - - - +EAP-PEAPv1/PSK + - - - - +EAP-PEAPv1/PAX + - - - - +EAP-PEAPv1/SAKE + - - - - +EAP-PEAPv1/GPSK + - - - - +EAP-TTLS/CHAP + - + - + + +EAP-TTLS/MSCHAP + - + - + + +EAP-TTLS/MSCHAPv2 + + + - + + +EAP-TTLS/PAP + - + - + + +EAP-TTLS/EAP-MD5 + - - - - + +EAP-TTLS/EAP-GTC + + - - - +EAP-TTLS/EAP-MSCHAPv2 + + - - - +EAP-TTLS/EAP-TLS + F - - - +EAP-TTLS/EAP-SIM + + - - - +EAP-TTLS/EAP-AKA + + - - - +EAP-TTLS + TNC + - - - - +EAP-SIM + + - - + +EAP-AKA + + - - - +EAP-PAX + - - - - +EAP-SAKE + - - - - +EAP-GPSK + - - - - +EAP-FAST/MSCHAPv2(prov) + - F - F +EAP-FAST/GTC(auth) + - + - + +EAP-FAST/MSCHAPv2(aprov)+ - F - F +EAP-FAST/GTC(aprov) + - F - F +EAP-FAST/MD5(aprov) + - - - - +EAP-FAST/TLS(aprov) + - - - - +EAP-FAST/SIM(aprov) + - - - - +EAP-FAST/AKA(aprov) + - - - - +EAP-FAST/MSCHAPv2(auth) + - + - + +EAP-FAST/MD5(auth) + - + - - +EAP-FAST/TLS(auth) + - - - - +EAP-FAST/SIM(auth) + - - - - +EAP-FAST/AKA(auth) + - - - - +EAP-FAST + TNC + - - - - +EAP-IKEv2 + - - - - +EAP-TNC + - - - - + +1) EAP-TLS itself worked, but peer certificate validation failed at + least when using the internal TLS server (peer included incorrect + certificates in the chain?) diff --git a/contrib/wpa/hostapd/eapol_sm.c b/contrib/wpa/hostapd/eapol_sm.c new file mode 100644 index 0000000..8e9d56c --- /dev/null +++ b/contrib/wpa/hostapd/eapol_sm.c @@ -0,0 +1,1342 @@ +/* + * hostapd / IEEE 802.1X-2004 Authenticator - EAPOL state machine + * Copyright (c) 2002-2008, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "hostapd.h" +#include "ieee802_1x.h" +#include "eapol_sm.h" +#include "eloop.h" +#include "wpa.h" +#include "preauth.h" +#include "sta_info.h" +#include "eap_server/eap.h" +#include "state_machine.h" +#include "eap_common/eap_common.h" + +#define STATE_MACHINE_DATA struct eapol_state_machine +#define STATE_MACHINE_DEBUG_PREFIX "IEEE 802.1X" +#define STATE_MACHINE_ADDR sm->addr + +static struct eapol_callbacks eapol_cb; + +/* EAPOL state machines are described in IEEE Std 802.1X-2004, Chap. 8.2 */ + +#define setPortAuthorized() \ +sm->eapol->cb.set_port_authorized(sm->hapd, sm->sta, 1) +#define setPortUnauthorized() \ +sm->eapol->cb.set_port_authorized(sm->hapd, sm->sta, 0) + +/* procedures */ +#define txCannedFail() eapol_auth_tx_canned_eap(sm, 0) +#define txCannedSuccess() eapol_auth_tx_canned_eap(sm, 1) +#define txReq() eapol_auth_tx_req(sm) +#define abortAuth() sm->eapol->cb.abort_auth(sm->hapd, sm->sta) +#define txKey() sm->eapol->cb.tx_key(sm->hapd, sm->sta) +#define processKey() do { } while (0) + + +static void eapol_sm_step_run(struct eapol_state_machine *sm); +static void eapol_sm_step_cb(void *eloop_ctx, void *timeout_ctx); + + +static void eapol_auth_logger(struct eapol_authenticator *eapol, + const u8 *addr, logger_level level, + const char *txt) +{ + if (eapol->cb.logger == NULL) + return; + eapol->cb.logger(eapol->conf.hapd, addr, level, txt); +} + + +static void eapol_auth_vlogger(struct eapol_authenticator *eapol, + const u8 *addr, logger_level level, + const char *fmt, ...) +{ + char *format; + int maxlen; + va_list ap; + + if (eapol->cb.logger == NULL) + return; + + maxlen = os_strlen(fmt) + 100; + format = os_malloc(maxlen); + if (!format) + return; + + va_start(ap, fmt); + vsnprintf(format, maxlen, fmt, ap); + va_end(ap); + + eapol_auth_logger(eapol, addr, level, format); + + os_free(format); +} + + +static void eapol_auth_tx_canned_eap(struct eapol_state_machine *sm, + int success) +{ + struct eap_hdr eap; + + os_memset(&eap, 0, sizeof(eap)); + + eap.code = success ? EAP_CODE_SUCCESS : EAP_CODE_FAILURE; + eap.identifier = ++sm->last_eap_id; + eap.length = host_to_be16(sizeof(eap)); + + eapol_auth_vlogger(sm->eapol, sm->addr, EAPOL_LOGGER_DEBUG, + "Sending canned EAP packet %s (identifier %d)", + success ? "SUCCESS" : "FAILURE", eap.identifier); + sm->eapol->cb.eapol_send(sm->hapd, sm->sta, IEEE802_1X_TYPE_EAP_PACKET, + (u8 *) &eap, sizeof(eap)); + sm->dot1xAuthEapolFramesTx++; +} + + +static void eapol_auth_tx_req(struct eapol_state_machine *sm) +{ + if (sm->eap_if->eapReqData == NULL || + wpabuf_len(sm->eap_if->eapReqData) < sizeof(struct eap_hdr)) { + eapol_auth_logger(sm->eapol, sm->addr, + EAPOL_LOGGER_DEBUG, + "TxReq called, but there is no EAP request " + "from authentication server"); + return; + } + + if (sm->flags & EAPOL_SM_WAIT_START) { + wpa_printf(MSG_DEBUG, "EAPOL: Drop EAPOL TX to " MACSTR + " while waiting for EAPOL-Start", + MAC2STR(sm->addr)); + return; + } + + sm->last_eap_id = eap_get_id(sm->eap_if->eapReqData); + eapol_auth_vlogger(sm->eapol, sm->addr, EAPOL_LOGGER_DEBUG, + "Sending EAP Packet (identifier %d)", + sm->last_eap_id); + sm->eapol->cb.eapol_send(sm->hapd, sm->sta, IEEE802_1X_TYPE_EAP_PACKET, + wpabuf_head(sm->eap_if->eapReqData), + wpabuf_len(sm->eap_if->eapReqData)); + sm->dot1xAuthEapolFramesTx++; + if (eap_get_type(sm->eap_if->eapReqData) == EAP_TYPE_IDENTITY) + sm->dot1xAuthEapolReqIdFramesTx++; + else + sm->dot1xAuthEapolReqFramesTx++; +} + + +/** + * eapol_port_timers_tick - Port Timers state machine + * @eloop_ctx: struct eapol_state_machine * + * @timeout_ctx: Not used + * + * This statemachine is 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)); + } + } + + if (state->eap_if->retransWhile > 0) { + state->eap_if->retransWhile--; + if (state->eap_if->retransWhile == 0) { + wpa_printf(MSG_DEBUG, "IEEE 802.1X: " MACSTR + " - (EAP) retransWhile --> 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_MA(AUTH_PAE, INITIALIZE, auth_pae); + sm->portMode = Auto; +} + + +SM_STATE(AUTH_PAE, DISCONNECTED) +{ + int from_initialize = sm->auth_pae_state == AUTH_PAE_INITIALIZE; + + if (sm->eapolLogoff) { + if (sm->auth_pae_state == AUTH_PAE_CONNECTING) + sm->authEapLogoffsWhileConnecting++; + else if (sm->auth_pae_state == AUTH_PAE_AUTHENTICATED) + sm->authAuthEapLogoffWhileAuthenticated++; + } + + SM_ENTRY_MA(AUTH_PAE, DISCONNECTED, auth_pae); + + sm->authPortStatus = Unauthorized; + setPortUnauthorized(); + sm->reAuthCount = 0; + sm->eapolLogoff = FALSE; + if (!from_initialize) { + sm->eapol->cb.finished(sm->hapd, sm->sta, 0, + sm->flags & EAPOL_SM_PREAUTH); + } +} + + +SM_STATE(AUTH_PAE, RESTART) +{ + if (sm->auth_pae_state == AUTH_PAE_AUTHENTICATED) { + if (sm->reAuthenticate) + sm->authAuthReauthsWhileAuthenticated++; + if (sm->eapolStart) + sm->authAuthEapStartsWhileAuthenticated++; + if (sm->eapolLogoff) + sm->authAuthEapLogoffWhileAuthenticated++; + } + + SM_ENTRY_MA(AUTH_PAE, RESTART, auth_pae); + + sm->eap_if->eapRestart = TRUE; +} + + +SM_STATE(AUTH_PAE, CONNECTING) +{ + if (sm->auth_pae_state != AUTH_PAE_CONNECTING) + sm->authEntersConnecting++; + + SM_ENTRY_MA(AUTH_PAE, CONNECTING, auth_pae); + + sm->reAuthenticate = FALSE; + sm->reAuthCount++; +} + + +SM_STATE(AUTH_PAE, HELD) +{ + if (sm->auth_pae_state == AUTH_PAE_AUTHENTICATING && sm->authFail) + sm->authAuthFailWhileAuthenticating++; + + SM_ENTRY_MA(AUTH_PAE, HELD, auth_pae); + + sm->authPortStatus = Unauthorized; + setPortUnauthorized(); + sm->quietWhile = sm->quietPeriod; + sm->eapolLogoff = FALSE; + + eapol_auth_vlogger(sm->eapol, sm->addr, EAPOL_LOGGER_WARNING, + "authentication failed - EAP type: %d (%s)", + sm->eap_type_authsrv, + eap_type_text(sm->eap_type_authsrv)); + if (sm->eap_type_authsrv != sm->eap_type_supp) { + eapol_auth_vlogger(sm->eapol, sm->addr, EAPOL_LOGGER_INFO, + "Supplicant used different EAP type: " + "%d (%s)", sm->eap_type_supp, + eap_type_text(sm->eap_type_supp)); + } + sm->eapol->cb.finished(sm->hapd, sm->sta, 0, + sm->flags & EAPOL_SM_PREAUTH); +} + + +SM_STATE(AUTH_PAE, AUTHENTICATED) +{ + char *extra = ""; + + if (sm->auth_pae_state == AUTH_PAE_AUTHENTICATING && sm->authSuccess) + sm->authAuthSuccessesWhileAuthenticating++; + + SM_ENTRY_MA(AUTH_PAE, AUTHENTICATED, auth_pae); + + sm->authPortStatus = Authorized; + setPortAuthorized(); + sm->reAuthCount = 0; + if (sm->flags & EAPOL_SM_PREAUTH) + extra = " (pre-authentication)"; + else if (wpa_auth_sta_get_pmksa(sm->sta->wpa_sm)) + extra = " (PMKSA cache)"; + eapol_auth_vlogger(sm->eapol, sm->addr, EAPOL_LOGGER_INFO, + "authenticated - EAP type: %d (%s)%s", + sm->eap_type_authsrv, + eap_type_text(sm->eap_type_authsrv), extra); + sm->eapol->cb.finished(sm->hapd, sm->sta, 1, + sm->flags & EAPOL_SM_PREAUTH); +} + + +SM_STATE(AUTH_PAE, AUTHENTICATING) +{ + SM_ENTRY_MA(AUTH_PAE, AUTHENTICATING, auth_pae); + + sm->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->authAuthTimeoutsWhileAuthenticating++; + if (sm->eapolStart) + sm->authAuthEapStartsWhileAuthenticating++; + if (sm->eapolLogoff) + sm->authAuthEapLogoffWhileAuthenticating++; + } + + SM_ENTRY_MA(AUTH_PAE, ABORTING, auth_pae); + + sm->authAbort = TRUE; + sm->keyRun = FALSE; + sm->keyDone = FALSE; +} + + +SM_STATE(AUTH_PAE, FORCE_AUTH) +{ + SM_ENTRY_MA(AUTH_PAE, FORCE_AUTH, auth_pae); + + sm->authPortStatus = Authorized; + setPortAuthorized(); + sm->portMode = ForceAuthorized; + sm->eapolStart = FALSE; + txCannedSuccess(); +} + + +SM_STATE(AUTH_PAE, FORCE_UNAUTH) +{ + SM_ENTRY_MA(AUTH_PAE, FORCE_UNAUTH, auth_pae); + + sm->authPortStatus = Unauthorized; + setPortUnauthorized(); + sm->portMode = ForceUnauthorized; + sm->eapolStart = FALSE; + txCannedFail(); +} + + +SM_STEP(AUTH_PAE) +{ + if ((sm->portControl == Auto && sm->portMode != sm->portControl) || + sm->initialize || !sm->eap_if->portEnabled) + SM_ENTER_GLOBAL(AUTH_PAE, INITIALIZE); + else if (sm->portControl == ForceAuthorized && + sm->portMode != sm->portControl && + !(sm->initialize || !sm->eap_if->portEnabled)) + SM_ENTER_GLOBAL(AUTH_PAE, FORCE_AUTH); + else if (sm->portControl == ForceUnauthorized && + sm->portMode != sm->portControl && + !(sm->initialize || !sm->eap_if->portEnabled)) + SM_ENTER_GLOBAL(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->eap_if->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->eapolLogoff || sm->reAuthCount > sm->reAuthMax) + SM_ENTER(AUTH_PAE, DISCONNECTED); + else if ((sm->eap_if->eapReq && + sm->reAuthCount <= sm->reAuthMax) || + sm->eap_if->eapSuccess || sm->eap_if->eapFail) + SM_ENTER(AUTH_PAE, AUTHENTICATING); + break; + case AUTH_PAE_AUTHENTICATED: + if (sm->eapolStart || sm->reAuthenticate) + SM_ENTER(AUTH_PAE, RESTART); + else if (sm->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->eapolStart || sm->eapolLogoff || + sm->authTimeout) + SM_ENTER(AUTH_PAE, ABORTING); + break; + case AUTH_PAE_ABORTING: + if (sm->eapolLogoff && !sm->authAbort) + SM_ENTER(AUTH_PAE, DISCONNECTED); + else if (!sm->eapolLogoff && !sm->authAbort) + SM_ENTER(AUTH_PAE, RESTART); + break; + case AUTH_PAE_FORCE_AUTH: + if (sm->eapolStart) + SM_ENTER(AUTH_PAE, FORCE_AUTH); + break; + case AUTH_PAE_FORCE_UNAUTH: + if (sm->eapolStart) + SM_ENTER(AUTH_PAE, FORCE_UNAUTH); + break; + } + } +} + + + +/* Backend Authentication state machine */ + +SM_STATE(BE_AUTH, INITIALIZE) +{ + SM_ENTRY_MA(BE_AUTH, INITIALIZE, be_auth); + + abortAuth(); + sm->eap_if->eapNoReq = FALSE; + sm->authAbort = FALSE; +} + + +SM_STATE(BE_AUTH, REQUEST) +{ + SM_ENTRY_MA(BE_AUTH, REQUEST, be_auth); + + txReq(); + sm->eap_if->eapReq = FALSE; + sm->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_MA(BE_AUTH, RESPONSE, be_auth); + + sm->authTimeout = FALSE; + sm->eapolEap = FALSE; + sm->eap_if->eapNoReq = FALSE; + sm->aWhile = sm->serverTimeout; + sm->eap_if->eapResp = TRUE; + /* sendRespToServer(); */ + sm->backendResponses++; +} + + +SM_STATE(BE_AUTH, SUCCESS) +{ + SM_ENTRY_MA(BE_AUTH, SUCCESS, be_auth); + + txReq(); + sm->authSuccess = TRUE; + sm->keyRun = TRUE; +} + + +SM_STATE(BE_AUTH, FAIL) +{ + SM_ENTRY_MA(BE_AUTH, FAIL, be_auth); + + txReq(); + sm->authFail = TRUE; +} + + +SM_STATE(BE_AUTH, TIMEOUT) +{ + SM_ENTRY_MA(BE_AUTH, TIMEOUT, be_auth); + + sm->authTimeout = TRUE; +} + + +SM_STATE(BE_AUTH, IDLE) +{ + SM_ENTRY_MA(BE_AUTH, IDLE, be_auth); + + sm->authStart = FALSE; +} + + +SM_STATE(BE_AUTH, IGNORE) +{ + SM_ENTRY_MA(BE_AUTH, IGNORE, be_auth); + + sm->eap_if->eapNoReq = FALSE; +} + + +SM_STEP(BE_AUTH) +{ + if (sm->portControl != Auto || sm->initialize || sm->authAbort) { + SM_ENTER_GLOBAL(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->eap_if->eapReq) + SM_ENTER(BE_AUTH, REQUEST); + else if (sm->eap_if->eapTimeout) + SM_ENTER(BE_AUTH, TIMEOUT); + break; + case BE_AUTH_RESPONSE: + if (sm->eap_if->eapNoReq) + SM_ENTER(BE_AUTH, IGNORE); + if (sm->eap_if->eapReq) { + sm->backendAccessChallenges++; + SM_ENTER(BE_AUTH, REQUEST); + } else if (sm->aWhile == 0) + SM_ENTER(BE_AUTH, TIMEOUT); + else if (sm->eap_if->eapFail) { + sm->backendAuthFails++; + SM_ENTER(BE_AUTH, FAIL); + } else if (sm->eap_if->eapSuccess) { + sm->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->eap_if->eapFail && sm->authStart) + SM_ENTER(BE_AUTH, FAIL); + else if (sm->eap_if->eapReq && sm->authStart) + SM_ENTER(BE_AUTH, REQUEST); + else if (sm->eap_if->eapSuccess && sm->authStart) + SM_ENTER(BE_AUTH, SUCCESS); + break; + case BE_AUTH_IGNORE: + if (sm->eapolEap) + SM_ENTER(BE_AUTH, RESPONSE); + else if (sm->eap_if->eapReq) + SM_ENTER(BE_AUTH, REQUEST); + else if (sm->eap_if->eapTimeout) + SM_ENTER(BE_AUTH, TIMEOUT); + break; + } +} + + + +/* Reauthentication Timer state machine */ + +SM_STATE(REAUTH_TIMER, INITIALIZE) +{ + SM_ENTRY_MA(REAUTH_TIMER, INITIALIZE, reauth_timer); + + sm->reAuthWhen = sm->reAuthPeriod; +} + + +SM_STATE(REAUTH_TIMER, REAUTHENTICATE) +{ + SM_ENTRY_MA(REAUTH_TIMER, REAUTHENTICATE, reauth_timer); + + sm->reAuthenticate = TRUE; + wpa_auth_sm_event(sm->sta->wpa_sm, WPA_REAUTH_EAPOL); +} + + +SM_STEP(REAUTH_TIMER) +{ + if (sm->portControl != Auto || sm->initialize || + sm->authPortStatus == Unauthorized || !sm->reAuthEnabled) { + SM_ENTER_GLOBAL(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_MA(AUTH_KEY_TX, NO_KEY_TRANSMIT, auth_key_tx); +} + + +SM_STATE(AUTH_KEY_TX, KEY_TRANSMIT) +{ + SM_ENTRY_MA(AUTH_KEY_TX, KEY_TRANSMIT, auth_key_tx); + + txKey(); + sm->eap_if->eapKeyAvailable = FALSE; + sm->keyDone = TRUE; +} + + +SM_STEP(AUTH_KEY_TX) +{ + if (sm->initialize || sm->portControl != Auto) { + SM_ENTER_GLOBAL(AUTH_KEY_TX, NO_KEY_TRANSMIT); + return; + } + + switch (sm->auth_key_tx_state) { + case AUTH_KEY_TX_NO_KEY_TRANSMIT: + if (sm->keyTxEnabled && sm->eap_if->eapKeyAvailable && + sm->keyRun && !wpa_auth_sta_wpa_version(sm->sta->wpa_sm)) + 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->eap_if->eapKeyAvailable) + SM_ENTER(AUTH_KEY_TX, KEY_TRANSMIT); + break; + } +} + + + +/* Key Receive state machine */ + +SM_STATE(KEY_RX, NO_KEY_RECEIVE) +{ + SM_ENTRY_MA(KEY_RX, NO_KEY_RECEIVE, key_rx); +} + + +SM_STATE(KEY_RX, KEY_RECEIVE) +{ + SM_ENTRY_MA(KEY_RX, KEY_RECEIVE, key_rx); + + processKey(); + sm->rxKey = FALSE; +} + + +SM_STEP(KEY_RX) +{ + if (sm->initialize || !sm->eap_if->portEnabled) { + SM_ENTER_GLOBAL(KEY_RX, NO_KEY_RECEIVE); + return; + } + + switch (sm->key_rx_state) { + case KEY_RX_NO_KEY_RECEIVE: + if (sm->rxKey) + SM_ENTER(KEY_RX, KEY_RECEIVE); + break; + case KEY_RX_KEY_RECEIVE: + if (sm->rxKey) + SM_ENTER(KEY_RX, KEY_RECEIVE); + break; + } +} + + + +/* Controlled Directions state machine */ + +SM_STATE(CTRL_DIR, FORCE_BOTH) +{ + SM_ENTRY_MA(CTRL_DIR, FORCE_BOTH, ctrl_dir); + sm->operControlledDirections = Both; +} + + +SM_STATE(CTRL_DIR, IN_OR_BOTH) +{ + SM_ENTRY_MA(CTRL_DIR, IN_OR_BOTH, ctrl_dir); + sm->operControlledDirections = sm->adminControlledDirections; +} + + +SM_STEP(CTRL_DIR) +{ + if (sm->initialize) { + SM_ENTER_GLOBAL(CTRL_DIR, IN_OR_BOTH); + return; + } + + switch (sm->ctrl_dir_state) { + case CTRL_DIR_FORCE_BOTH: + if (sm->eap_if->portEnabled && sm->operEdge) + SM_ENTER(CTRL_DIR, IN_OR_BOTH); + break; + case CTRL_DIR_IN_OR_BOTH: + if (sm->operControlledDirections != + sm->adminControlledDirections) + SM_ENTER(CTRL_DIR, IN_OR_BOTH); + if (!sm->eap_if->portEnabled || !sm->operEdge) + SM_ENTER(CTRL_DIR, FORCE_BOTH); + break; + } +} + + + +struct eapol_state_machine * +eapol_auth_alloc(struct eapol_authenticator *eapol, const u8 *addr, + int preauth, struct sta_info *sta) +{ + struct eapol_state_machine *sm; + struct hostapd_data *hapd; /* TODO: to be removed */ + struct eap_config eap_conf; + + if (eapol == NULL) + return NULL; + hapd = eapol->conf.hapd; + + sm = os_zalloc(sizeof(*sm)); + if (sm == NULL) { + wpa_printf(MSG_DEBUG, "IEEE 802.1X state machine allocation " + "failed"); + return NULL; + } + sm->radius_identifier = -1; + os_memcpy(sm->addr, addr, ETH_ALEN); + if (preauth) + sm->flags |= EAPOL_SM_PREAUTH; + + sm->hapd = hapd; + sm->eapol = eapol; + sm->sta = sta; + + /* Set default values for state machine constants */ + sm->auth_pae_state = AUTH_PAE_INITIALIZE; + sm->quietPeriod = AUTH_PAE_DEFAULT_quietPeriod; + sm->reAuthMax = AUTH_PAE_DEFAULT_reAuthMax; + + sm->be_auth_state = BE_AUTH_INITIALIZE; + sm->serverTimeout = BE_AUTH_DEFAULT_serverTimeout; + + sm->reauth_timer_state = REAUTH_TIMER_INITIALIZE; + sm->reAuthPeriod = eapol->conf.eap_reauth_period; + sm->reAuthEnabled = eapol->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->portControl = Auto; + + if (!eapol->conf.wpa && + (hapd->default_wep_key || eapol->conf.individual_wep_key_len > 0)) + sm->keyTxEnabled = TRUE; + else + sm->keyTxEnabled = FALSE; + if (eapol->conf.wpa) + sm->portValid = FALSE; + else + sm->portValid = TRUE; + + os_memset(&eap_conf, 0, sizeof(eap_conf)); + eap_conf.eap_server = eapol->conf.eap_server; + eap_conf.ssl_ctx = eapol->conf.ssl_ctx; + eap_conf.eap_sim_db_priv = eapol->conf.eap_sim_db_priv; + eap_conf.pac_opaque_encr_key = eapol->conf.pac_opaque_encr_key; + eap_conf.eap_fast_a_id = eapol->conf.eap_fast_a_id; + eap_conf.eap_fast_a_id_len = eapol->conf.eap_fast_a_id_len; + eap_conf.eap_fast_a_id_info = eapol->conf.eap_fast_a_id_info; + eap_conf.eap_fast_prov = eapol->conf.eap_fast_prov; + eap_conf.pac_key_lifetime = eapol->conf.pac_key_lifetime; + eap_conf.pac_key_refresh_time = eapol->conf.pac_key_refresh_time; + eap_conf.eap_sim_aka_result_ind = eapol->conf.eap_sim_aka_result_ind; + eap_conf.tnc = eapol->conf.tnc; + eap_conf.wps = eapol->conf.wps; + eap_conf.assoc_wps_ie = sta->wps_ie; + sm->eap = eap_server_sm_init(sm, &eapol_cb, &eap_conf); + if (sm->eap == NULL) { + eapol_auth_free(sm); + return NULL; + } + sm->eap_if = eap_get_interface(sm->eap); + + eapol_auth_initialize(sm); + + return sm; +} + + +void eapol_auth_free(struct eapol_state_machine *sm) +{ + if (sm == NULL) + return; + + eloop_cancel_timeout(eapol_port_timers_tick, NULL, sm); + eloop_cancel_timeout(eapol_sm_step_cb, sm, NULL); + if (sm->eap) + eap_server_sm_deinit(sm->eap); + os_free(sm); +} + + +static int eapol_sm_sta_entry_alive(struct eapol_authenticator *eapol, + const u8 *addr) +{ + return eapol->cb.sta_entry_alive(eapol->conf.hapd, addr); +} + + +static void eapol_sm_step_run(struct eapol_state_machine *sm) +{ + struct eapol_authenticator *eapol = sm->eapol; + u8 addr[ETH_ALEN]; + unsigned int prev_auth_pae, prev_be_auth, prev_reauth_timer, + prev_auth_key_tx, prev_key_rx, prev_ctrl_dir; + int max_steps = 100; + + os_memcpy(addr, sm->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(eapol, addr)) + SM_STEP_RUN(BE_AUTH); + if (sm->initializing || eapol_sm_sta_entry_alive(eapol, addr)) + SM_STEP_RUN(REAUTH_TIMER); + if (sm->initializing || eapol_sm_sta_entry_alive(eapol, addr)) + SM_STEP_RUN(AUTH_KEY_TX); + if (sm->initializing || eapol_sm_sta_entry_alive(eapol, addr)) + SM_STEP_RUN(KEY_RX); + if (sm->initializing || eapol_sm_sta_entry_alive(eapol, 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_auth_step(sm); + return; + } + + if (eapol_sm_sta_entry_alive(eapol, addr) && sm->eap) { + if (eap_server_sm_step(sm->eap)) { + if (--max_steps > 0) + goto restart; + /* Re-run from eloop timeout */ + eapol_auth_step(sm); + return; + } + + /* TODO: find a better location for this */ + if (sm->eap_if->aaaEapResp) { + sm->eap_if->aaaEapResp = FALSE; + if (sm->eap_if->aaaEapRespData == NULL) { + wpa_printf(MSG_DEBUG, "EAPOL: aaaEapResp set, " + "but no aaaEapRespData available"); + return; + } + sm->eapol->cb.aaa_send( + sm->hapd, sm->sta, + wpabuf_head(sm->eap_if->aaaEapRespData), + wpabuf_len(sm->eap_if->aaaEapRespData)); + } + } + + if (eapol_sm_sta_entry_alive(eapol, addr)) + wpa_auth_sm_notify(sm->sta->wpa_sm); +} + + +static void eapol_sm_step_cb(void *eloop_ctx, void *timeout_ctx) +{ + struct eapol_state_machine *sm = eloop_ctx; + eapol_sm_step_run(sm); +} + + +/** + * eapol_auth_step - Advance EAPOL state machines + * @sm: EAPOL state machine + * + * This function is called to advance EAPOL state machines after any change + * that could affect their state. + */ +void eapol_auth_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_auth_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, NULL, sm); + eloop_register_timeout(1, 0, eapol_port_timers_tick, NULL, 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_auth_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->eap_if->eapFail), _SB(sm->eapolEap), + prefix, _SB(sm->eap_if->eapSuccess), + _SB(sm->eap_if->eapTimeout), + _SB(sm->initialize), _SB(sm->eap_if->eapKeyAvailable), + prefix, _SB(sm->keyDone), _SB(sm->keyRun), + _SB(sm->keyTxEnabled), port_type_txt(sm->portControl), + prefix, _SB(sm->eap_if->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->eapolLogoff), _SB(sm->eapolStart), + _SB(sm->eap_if->eapRestart), + prefix, port_type_txt(sm->portMode), sm->reAuthCount, + prefix, sm->quietPeriod, sm->reAuthMax, + prefix, sm->authEntersConnecting, + prefix, sm->authEapLogoffsWhileConnecting, + prefix, sm->authEntersAuthenticating, + prefix, sm->authAuthSuccessesWhileAuthenticating, + prefix, sm->authAuthTimeoutsWhileAuthenticating, + prefix, sm->authAuthFailWhileAuthenticating, + prefix, sm->authAuthEapStartsWhileAuthenticating, + prefix, sm->authAuthEapLogoffWhileAuthenticating, + prefix, sm->authAuthReauthsWhileAuthenticated, + prefix, sm->authAuthEapStartsWhileAuthenticated, + prefix, sm->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->eap_if->eapNoReq), _SB(sm->eap_if->eapReq), + _SB(sm->eap_if->eapResp), + prefix, sm->serverTimeout, + prefix, sm->backendResponses, + prefix, sm->backendAccessChallenges, + prefix, sm->backendOtherRequestsToSupplicant, + prefix, sm->backendAuthSuccesses, + prefix, sm->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->reAuthPeriod, _SB(sm->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->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->adminControlledDirections), + ctrl_dir_txt(sm->operControlledDirections), + prefix, _SB(sm->operEdge)); +#undef _SB +} +#endif /* HOSTAPD_DUMP_STATE */ + + +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; + return sm->eapol->cb.get_eap_user(sm->hapd, identity, identity_len, + phase2, user); +} + + +static const char * eapol_sm_get_eap_req_id_text(void *ctx, size_t *len) +{ + struct eapol_state_machine *sm = ctx; + *len = sm->eapol->conf.eap_req_id_text_len; + return sm->eapol->conf.eap_req_id_text; +} + + +static struct eapol_callbacks eapol_cb = +{ + .get_eap_user = eapol_sm_get_eap_user, + .get_eap_req_id_text = eapol_sm_get_eap_req_id_text, +}; + + +int eapol_auth_eap_pending_cb(struct eapol_state_machine *sm, void *ctx) +{ + if (sm == NULL || ctx != sm->eap) + return -1; + + eap_sm_pending_cb(sm->eap); + eapol_auth_step(sm); + + return 0; +} + + +static int eapol_auth_conf_clone(struct eapol_auth_config *dst, + struct eapol_auth_config *src) +{ + dst->hapd = src->hapd; + dst->eap_reauth_period = src->eap_reauth_period; + dst->wpa = src->wpa; + dst->individual_wep_key_len = src->individual_wep_key_len; + dst->eap_server = src->eap_server; + dst->ssl_ctx = src->ssl_ctx; + dst->eap_sim_db_priv = src->eap_sim_db_priv; + os_free(dst->eap_req_id_text); + if (src->eap_req_id_text) { + dst->eap_req_id_text = os_malloc(src->eap_req_id_text_len); + if (dst->eap_req_id_text == NULL) + return -1; + os_memcpy(dst->eap_req_id_text, src->eap_req_id_text, + src->eap_req_id_text_len); + dst->eap_req_id_text_len = src->eap_req_id_text_len; + } else { + dst->eap_req_id_text = NULL; + dst->eap_req_id_text_len = 0; + } + if (src->pac_opaque_encr_key) { + dst->pac_opaque_encr_key = os_malloc(16); + os_memcpy(dst->pac_opaque_encr_key, src->pac_opaque_encr_key, + 16); + } else + dst->pac_opaque_encr_key = NULL; + if (src->eap_fast_a_id) { + dst->eap_fast_a_id = os_malloc(src->eap_fast_a_id_len); + if (dst->eap_fast_a_id == NULL) { + os_free(dst->eap_req_id_text); + return -1; + } + os_memcpy(dst->eap_fast_a_id, src->eap_fast_a_id, + src->eap_fast_a_id_len); + dst->eap_fast_a_id_len = src->eap_fast_a_id_len; + } else + dst->eap_fast_a_id = NULL; + if (src->eap_fast_a_id_info) { + dst->eap_fast_a_id_info = os_strdup(src->eap_fast_a_id_info); + if (dst->eap_fast_a_id_info == NULL) { + os_free(dst->eap_req_id_text); + os_free(dst->eap_fast_a_id); + return -1; + } + } else + dst->eap_fast_a_id_info = NULL; + dst->eap_fast_prov = src->eap_fast_prov; + dst->pac_key_lifetime = src->pac_key_lifetime; + dst->pac_key_refresh_time = src->pac_key_refresh_time; + dst->eap_sim_aka_result_ind = src->eap_sim_aka_result_ind; + dst->tnc = src->tnc; + dst->wps = src->wps; + return 0; +} + + +static void eapol_auth_conf_free(struct eapol_auth_config *conf) +{ + os_free(conf->eap_req_id_text); + conf->eap_req_id_text = NULL; + os_free(conf->pac_opaque_encr_key); + conf->pac_opaque_encr_key = NULL; + os_free(conf->eap_fast_a_id); + conf->eap_fast_a_id = NULL; + os_free(conf->eap_fast_a_id_info); + conf->eap_fast_a_id_info = NULL; +} + + +struct eapol_authenticator * eapol_auth_init(struct eapol_auth_config *conf, + struct eapol_auth_cb *cb) +{ + struct eapol_authenticator *eapol; + + eapol = os_zalloc(sizeof(*eapol)); + if (eapol == NULL) + return NULL; + + if (eapol_auth_conf_clone(&eapol->conf, conf) < 0) { + os_free(eapol); + return NULL; + } + + eapol->cb.eapol_send = cb->eapol_send; + eapol->cb.aaa_send = cb->aaa_send; + eapol->cb.finished = cb->finished; + eapol->cb.get_eap_user = cb->get_eap_user; + eapol->cb.sta_entry_alive = cb->sta_entry_alive; + eapol->cb.logger = cb->logger; + eapol->cb.set_port_authorized = cb->set_port_authorized; + eapol->cb.abort_auth = cb->abort_auth; + eapol->cb.tx_key = cb->tx_key; + + return eapol; +} + + +void eapol_auth_deinit(struct eapol_authenticator *eapol) +{ + if (eapol == NULL) + return; + + eapol_auth_conf_free(&eapol->conf); + os_free(eapol); +} diff --git a/contrib/wpa/hostapd/eapol_sm.h b/contrib/wpa/hostapd/eapol_sm.h new file mode 100644 index 0000000..7a13e8e --- /dev/null +++ b/contrib/wpa/hostapd/eapol_sm.h @@ -0,0 +1,260 @@ +/* + * hostapd / IEEE 802.1X-2004 Authenticator - EAPOL state machine + * Copyright (c) 2002-2007, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef EAPOL_SM_H +#define EAPOL_SM_H + +#include "defs.h" + +/* IEEE Std 802.1X-2004, 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; + +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_auth_config { + int eap_reauth_period; + int wpa; + int individual_wep_key_len; + int eap_server; + void *ssl_ctx; + void *eap_sim_db_priv; + char *eap_req_id_text; /* a copy of this will be allocated */ + size_t eap_req_id_text_len; + u8 *pac_opaque_encr_key; + u8 *eap_fast_a_id; + size_t eap_fast_a_id_len; + char *eap_fast_a_id_info; + int eap_fast_prov; + int pac_key_lifetime; + int pac_key_refresh_time; + int eap_sim_aka_result_ind; + int tnc; + struct wps_context *wps; + + /* + * Pointer to hostapd data. This is a temporary workaround for + * transition phase and will be removed once IEEE 802.1X/EAPOL code is + * separated more cleanly from rest of hostapd. + */ + struct hostapd_data *hapd; +}; + +struct eap_user; + +typedef enum { + EAPOL_LOGGER_DEBUG, EAPOL_LOGGER_INFO, EAPOL_LOGGER_WARNING +} eapol_logger_level; + +struct eapol_auth_cb { + void (*eapol_send)(void *ctx, void *sta_ctx, u8 type, const u8 *data, + size_t datalen); + void (*aaa_send)(void *ctx, void *sta_ctx, const u8 *data, + size_t datalen); + void (*finished)(void *ctx, void *sta_ctx, int success, int preauth); + int (*get_eap_user)(void *ctx, const u8 *identity, size_t identity_len, + int phase2, struct eap_user *user); + int (*sta_entry_alive)(void *ctx, const u8 *addr); + void (*logger)(void *ctx, const u8 *addr, eapol_logger_level level, + const char *txt); + void (*set_port_authorized)(void *ctx, void *sta_ctx, int authorized); + void (*abort_auth)(void *ctx, void *sta_ctx); + void (*tx_key)(void *ctx, void *sta_ctx); +}; + +/** + * struct eapol_authenticator - Global EAPOL authenticator data + */ +struct eapol_authenticator { + struct eapol_auth_config conf; + struct eapol_auth_cb cb; +}; + + +/** + * struct eapol_state_machine - Per-Supplicant Authenticator state machines + */ +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 eapolEap; + Boolean initialize; + Boolean keyDone; + Boolean keyRun; + Boolean keyTxEnabled; + PortTypes portControl; + Boolean portValid; + Boolean reAuthenticate; + + /* Port Timers state machine */ + /* 'Boolean tick' implicitly handled as registered timeout */ + + /* Authenticator PAE state machine */ + 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 } auth_pae_state; + /* variables */ + Boolean eapolLogoff; + Boolean eapolStart; + 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; + + /* Backend Authentication state machine */ + 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 + } be_auth_state; + /* 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; + + /* Reauthentication Timer state machine */ + enum { REAUTH_TIMER_INITIALIZE, REAUTH_TIMER_REAUTHENTICATE + } reauth_timer_state; + /* constants */ + unsigned int reAuthPeriod; /* default 3600 s */ + Boolean reAuthEnabled; + + /* Authenticator Key Transmit state machine */ + enum { AUTH_KEY_TX_NO_KEY_TRANSMIT, AUTH_KEY_TX_KEY_TRANSMIT + } auth_key_tx_state; + + /* Key Receive state machine */ + enum { KEY_RX_NO_KEY_RECEIVE, KEY_RX_KEY_RECEIVE } key_rx_state; + /* variables */ + Boolean rxKey; + + /* Controlled Directions state machine */ + enum { CTRL_DIR_FORCE_BOTH, CTRL_DIR_IN_OR_BOTH } ctrl_dir_state; + /* variables */ + ControlledDirection adminControlledDirections; + ControlledDirection operControlledDirections; + Boolean operEdge; + + /* 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) +#define EAPOL_SM_WAIT_START BIT(1) + int flags; /* EAPOL_SM_* */ + + /* EAPOL/AAA <-> EAP full authenticator interface */ + struct eap_eapol_interface *eap_if; + + int radius_identifier; + /* TODO: check when the last messages can be released */ + struct radius_msg *last_recv_radius; + u8 last_eap_id; /* last used EAP Identifier */ + u8 *identity; + size_t identity_len; + u8 eap_type_authsrv; /* EAP type of the last EAP packet from + * Authentication server */ + u8 eap_type_supp; /* EAP type of the last EAP packet from Supplicant */ + 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; + + struct eap_sm *eap; + + Boolean initializing; /* in process of initializing state machines */ + Boolean changed; + + struct eapol_authenticator *eapol; + + /* 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_authenticator * eapol_auth_init(struct eapol_auth_config *conf, + struct eapol_auth_cb *cb); +void eapol_auth_deinit(struct eapol_authenticator *eapol); +struct eapol_state_machine * +eapol_auth_alloc(struct eapol_authenticator *eapol, const u8 *addr, + int preauth, struct sta_info *sta); +void eapol_auth_free(struct eapol_state_machine *sm); +void eapol_auth_step(struct eapol_state_machine *sm); +void eapol_auth_initialize(struct eapol_state_machine *sm); +void eapol_auth_dump_state(FILE *f, const char *prefix, + struct eapol_state_machine *sm); +int eapol_auth_eap_pending_cb(struct eapol_state_machine *sm, void *ctx); + +#endif /* EAPOL_SM_H */ diff --git a/contrib/wpa/hostapd/hostap_common.h b/contrib/wpa/hostapd/hostap_common.h new file mode 100644 index 0000000..5a57dca --- /dev/null +++ b/contrib/wpa/hostapd/hostap_common.h @@ -0,0 +1,216 @@ +/* + * hostapd / Kernel driver communication with Linux Host AP driver + * Copyright (c) 2002-2006, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef HOSTAP_COMMON_H +#define HOSTAP_COMMON_H + +/* 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 \ +((size_t) (&((struct prism2_hostapd_param *) 0)->u.rid.data)) +#define PRISM2_HOSTAPD_GENERIC_ELEMENT_HDR_LEN \ +((size_t) (&((struct prism2_hostapd_param *) 0)->u.generic_elem.data)) + +/* Maximum length for algorithm names (-1 for nul termination) used in ioctl() + */ +#define HOSTAP_CRYPT_ALG_NAME_LEN 16 + + +struct prism2_hostapd_param { + u32 cmd; + u8 sta_addr[ETH_ALEN]; + union { + struct { + u16 aid; + u16 capability; + u8 tx_supp_rates; + } add_sta; + struct { + u32 inactive_sec; + } get_info_sta; + struct { + u8 alg[HOSTAP_CRYPT_ALG_NAME_LEN]; + u32 flags; + u32 err; + u8 idx; + u8 seq[8]; /* sequence counter (set: RX, get: TX) */ + u16 key_len; + u8 key[0]; + } crypt; + struct { + u32 flags_and; + u32 flags_or; + } set_flags_sta; + struct { + u16 rid; + u16 len; + u8 data[0]; + } rid; + struct { + u8 len; + u8 data[0]; + } generic_elem; + struct { +#define MLME_STA_DEAUTH 0 +#define MLME_STA_DISASSOC 1 + u16 cmd; + u16 reason_code; + } mlme; + struct { + u8 ssid_len; + u8 ssid[32]; + } scan_req; + } u; +}; + +#define HOSTAP_CRYPT_FLAG_SET_TX_KEY BIT(0) +#define HOSTAP_CRYPT_FLAG_PERMANENT BIT(1) + +#define HOSTAP_CRYPT_ERR_UNKNOWN_ALG 2 +#define HOSTAP_CRYPT_ERR_UNKNOWN_ADDR 3 +#define HOSTAP_CRYPT_ERR_CRYPT_INIT_FAILED 4 +#define HOSTAP_CRYPT_ERR_KEY_SET_FAILED 5 +#define HOSTAP_CRYPT_ERR_TX_KEY_SET_FAILED 6 +#define HOSTAP_CRYPT_ERR_CARD_CONF_FAILED 7 + +#endif /* HOSTAP_COMMON_H */ diff --git a/contrib/wpa/hostapd/hostapd.8 b/contrib/wpa/hostapd/hostapd.8 new file mode 100644 index 0000000..9258512 --- /dev/null +++ b/contrib/wpa/hostapd/hostapd.8 @@ -0,0 +1,59 @@ +.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 +[-hdBKtv] [-P <PID file>] <configuration file(s)> +.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 \-P <PID file> +Path to PID file. +.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 <j@w1.fi>. +.PP +This manual page was written by Faidon Liambotis <faidon@cube.gr>, +for the Debian project (but may be used by others). diff --git a/contrib/wpa/hostapd/hostapd.accept b/contrib/wpa/hostapd/hostapd.accept new file mode 100644 index 0000000..2d2a0a2 --- /dev/null +++ b/contrib/wpa/hostapd/hostapd.accept @@ -0,0 +1,6 @@ +# List of MAC addresses that are allowed to authenticate (IEEE 802.11) +# with the AP. Optional VLAN ID can be assigned for clients based on the +# MAC address if dynamic VLANs (hostapd.conf dynamic_vlan option) are used. +00:11:22:33:44:55 +00:66:77:88:99:aa +00:00:22:33:44:55 1 diff --git a/contrib/wpa/hostapd/hostapd.c b/contrib/wpa/hostapd/hostapd.c new file mode 100644 index 0000000..df6062b --- /dev/null +++ b/contrib/wpa/hostapd/hostapd.c @@ -0,0 +1,2027 @@ +/* + * hostapd / Initialization and configuration + * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" +#ifndef CONFIG_NATIVE_WINDOWS +#include <syslog.h> +#endif /* CONFIG_NATIVE_WINDOWS */ + +#include "eloop.h" +#include "hostapd.h" +#include "ieee802_1x.h" +#include "ieee802_11.h" +#include "beacon.h" +#include "hw_features.h" +#include "accounting.h" +#include "eapol_sm.h" +#include "iapp.h" +#include "ap.h" +#include "ieee802_11_auth.h" +#include "ap_list.h" +#include "sta_info.h" +#include "driver.h" +#include "radius/radius_client.h" +#include "radius/radius_server.h" +#include "wpa.h" +#include "preauth.h" +#include "wme.h" +#include "vlan_init.h" +#include "ctrl_iface.h" +#include "tls.h" +#include "eap_server/eap_sim_db.h" +#include "eap_server/eap.h" +#include "eap_server/tncs.h" +#include "version.h" +#include "l2_packet/l2_packet.h" +#include "wps_hostapd.h" + + +static int hostapd_radius_get_eap_user(void *ctx, const u8 *identity, + size_t identity_len, int phase2, + struct eap_user *user); +static int hostapd_flush_old_stations(struct hostapd_data *hapd); +static int hostapd_setup_wpa(struct hostapd_data *hapd); +static int hostapd_setup_encryption(char *iface, struct hostapd_data *hapd); + +struct hapd_interfaces { + size_t count; + struct hostapd_iface **iface; +}; + +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; + + +static void hostapd_logger_cb(void *ctx, const u8 *addr, unsigned int module, + int level, const char *txt, size_t len) +{ + struct hostapd_data *hapd = ctx; + char *format, *module_str; + int maxlen; + int conf_syslog_level, conf_stdout_level; + unsigned int conf_syslog, conf_stdout; + + maxlen = len + 100; + format = os_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; + case HOSTAPD_MODULE_MLME: + module_str = "MLME"; + break; + default: + module_str = NULL; + break; + } + + if (hapd && hapd->conf && addr) + os_snprintf(format, maxlen, "%s: STA " MACSTR "%s%s: %s", + hapd->conf->iface, MAC2STR(addr), + module_str ? " " : "", module_str, txt); + else if (hapd && hapd->conf) + os_snprintf(format, maxlen, "%s:%s%s %s", + hapd->conf->iface, module_str ? " " : "", + module_str, txt); + else if (addr) + os_snprintf(format, maxlen, "STA " MACSTR "%s%s: %s", + MAC2STR(addr), module_str ? " " : "", + module_str, txt); + else + os_snprintf(format, maxlen, "%s%s%s", + module_str, module_str ? ": " : "", txt); + + if ((conf_stdout & module) && level >= conf_stdout_level) { + wpa_debug_print_timestamp(); + printf("%s\n", format); + } + +#ifndef CONFIG_NATIVE_WINDOWS + 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; + } + syslog(priority, "%s", format); + } +#endif /* CONFIG_NATIVE_WINDOWS */ + + os_free(format); +} + + +static void hostapd_deauth_all_stas(struct hostapd_data *hapd) +{ + u8 addr[ETH_ALEN]; + + /* 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 the hostap + * driver. */ + + if (hapd->driver && os_strcmp(hapd->driver->name, "hostap") != 0) { + os_memset(addr, 0xff, ETH_ALEN); + hostapd_sta_deauth(hapd, addr, + WLAN_REASON_PREV_AUTH_NOT_VALID); + } +} + + +/** + * hostapd_prune_associations - Remove extraneous associations + * @hapd: Pointer to BSS data for the most recent association + * @sta: Pointer to the associated STA data + * + * This function looks through all radios and BSS's for previous + * (stale) associations of STA. If any are found they are removed. + */ +static void hostapd_prune_associations(struct hostapd_data *hapd, + struct sta_info *sta) +{ + struct sta_info *osta; + struct hostapd_data *ohapd; + size_t i, j; + struct hapd_interfaces *interfaces = eloop_get_user_data(); + + for (i = 0; i < interfaces->count; i++) { + for (j = 0; j < interfaces->iface[i]->num_bss; j++) { + ohapd = interfaces->iface[i]->bss[j]; + if (ohapd == hapd) + continue; + osta = ap_get_sta(ohapd, sta->addr); + if (!osta) + continue; + + ap_sta_disassociate(ohapd, osta, + WLAN_REASON_UNSPECIFIED); + } + } +} + + +/** + * hostapd_new_assoc_sta - Notify that a new station associated with the AP + * @hapd: Pointer to BSS data + * @sta: Pointer to the associated STA data + * @reassoc: 1 to indicate this was a re-association; 0 = first association + * + * This function will be called whenever a station associates with the AP. It + * can be called for ieee802_11.c for drivers that export MLME to hostapd and + * from driver_*.c for drivers that take care of management frames (IEEE 802.11 + * authentication and association) internally. + */ +void hostapd_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta, + int reassoc) +{ + if (hapd->tkip_countermeasures) { + hostapd_sta_deauth(hapd, sta->addr, + WLAN_REASON_MICHAEL_MIC_FAILURE); + return; + } + + hostapd_prune_associations(hapd, sta); + + /* 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); + + hostapd_wme_sta_config(hapd, sta); + + /* Start IEEE 802.1X authentication process for new stations */ + ieee802_1x_new_station(hapd, sta); + if (reassoc) { + if (sta->auth_alg != WLAN_AUTH_FT && + !(sta->flags & (WLAN_STA_WPS | WLAN_STA_MAYBE_WPS))) + wpa_auth_sm_event(sta->wpa_sm, WPA_REAUTH); + } else + wpa_auth_sta_associated(hapd->wpa_auth, sta->wpa_sm); +} + + +#ifdef EAP_SERVER +static int hostapd_sim_db_cb_sta(struct hostapd_data *hapd, + struct sta_info *sta, void *ctx) +{ + if (eapol_auth_eap_pending_cb(sta->eapol_sm, ctx) == 0) + return 1; + return 0; +} + + +static void hostapd_sim_db_cb(void *ctx, void *session_ctx) +{ + struct hostapd_data *hapd = ctx; + if (ap_for_each_sta(hapd, hostapd_sim_db_cb_sta, session_ctx) == 0) + radius_server_eap_pending_cb(hapd->radius_srv, session_ctx); +} +#endif /* EAP_SERVER */ + + +/** + * handle_term - SIGINT and SIGTERM handler to terminate hostapd process + */ +static void handle_term(int sig, void *eloop_ctx, void *signal_ctx) +{ + wpa_printf(MSG_DEBUG, "Signal %d received - terminating", sig); + eloop_terminate(); +} + + +static void hostapd_wpa_auth_conf(struct hostapd_bss_config *conf, + struct wpa_auth_config *wconf) +{ + wconf->wpa = conf->wpa; + wconf->wpa_key_mgmt = conf->wpa_key_mgmt; + wconf->wpa_pairwise = conf->wpa_pairwise; + wconf->wpa_group = conf->wpa_group; + wconf->wpa_group_rekey = conf->wpa_group_rekey; + wconf->wpa_strict_rekey = conf->wpa_strict_rekey; + wconf->wpa_gmk_rekey = conf->wpa_gmk_rekey; + wconf->wpa_ptk_rekey = conf->wpa_ptk_rekey; + wconf->rsn_pairwise = conf->rsn_pairwise; + wconf->rsn_preauth = conf->rsn_preauth; + wconf->eapol_version = conf->eapol_version; + wconf->peerkey = conf->peerkey; + wconf->wme_enabled = conf->wme_enabled; + wconf->okc = conf->okc; +#ifdef CONFIG_IEEE80211W + wconf->ieee80211w = conf->ieee80211w; +#endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_IEEE80211R + wconf->ssid_len = conf->ssid.ssid_len; + if (wconf->ssid_len > SSID_LEN) + wconf->ssid_len = SSID_LEN; + os_memcpy(wconf->ssid, conf->ssid.ssid, wconf->ssid_len); + os_memcpy(wconf->mobility_domain, conf->mobility_domain, + MOBILITY_DOMAIN_ID_LEN); + if (conf->nas_identifier && + os_strlen(conf->nas_identifier) <= FT_R0KH_ID_MAX_LEN) { + wconf->r0_key_holder_len = os_strlen(conf->nas_identifier); + os_memcpy(wconf->r0_key_holder, conf->nas_identifier, + wconf->r0_key_holder_len); + } + os_memcpy(wconf->r1_key_holder, conf->r1_key_holder, FT_R1KH_ID_LEN); + wconf->r0_key_lifetime = conf->r0_key_lifetime; + wconf->reassociation_deadline = conf->reassociation_deadline; + wconf->r0kh_list = conf->r0kh_list; + wconf->r1kh_list = conf->r1kh_list; + wconf->pmk_r1_push = conf->pmk_r1_push; +#endif /* CONFIG_IEEE80211R */ +} + + +int hostapd_reload_config(struct hostapd_iface *iface) +{ + struct hostapd_data *hapd = iface->bss[0]; + struct hostapd_config *newconf, *oldconf; + struct wpa_auth_config wpa_auth_conf; + + newconf = hostapd_config_read(iface->config_fname); + if (newconf == NULL) + return -1; + + /* + * Deauthenticate all stations since the new configuration may not + * allow them to use the BSS anymore. + */ + hostapd_flush_old_stations(hapd); + + /* TODO: update dynamic data based on changed configuration + * items (e.g., open/close sockets, etc.) */ + radius_client_flush(hapd->radius, 0); + + oldconf = hapd->iconf; + hapd->iconf = newconf; + hapd->conf = &newconf->bss[0]; + iface->conf = newconf; + + if (hostapd_setup_wpa_psk(hapd->conf)) { + wpa_printf(MSG_ERROR, "Failed to re-configure WPA PSK " + "after reloading configuration"); + } + + if (hapd->conf->wpa && hapd->wpa_auth == NULL) + hostapd_setup_wpa(hapd); + else if (hapd->conf->wpa) { + hostapd_wpa_auth_conf(&newconf->bss[0], &wpa_auth_conf); + wpa_reconfig(hapd->wpa_auth, &wpa_auth_conf); + } else if (hapd->wpa_auth) { + wpa_deinit(hapd->wpa_auth); + hapd->wpa_auth = NULL; + hostapd_set_privacy(hapd, 0); + hostapd_setup_encryption(hapd->conf->iface, hapd); + } + + ieee802_11_set_beacon(hapd); + + hostapd_config_free(oldconf); + + wpa_printf(MSG_DEBUG, "Reconfigured interface %s", hapd->conf->iface); + + return 0; +} + + +#ifndef CONFIG_NATIVE_WINDOWS +/** + * handle_reload - SIGHUP handler to reload configuration + */ +static void handle_reload(int sig, void *eloop_ctx, void *signal_ctx) +{ + struct hapd_interfaces *hapds = (struct hapd_interfaces *) eloop_ctx; + size_t i; + + wpa_printf(MSG_DEBUG, "Signal %d received - reloading configuration", + sig); + + for (i = 0; i < hapds->count; i++) { + if (hostapd_reload_config(hapds->iface[i]) < 0) { + wpa_printf(MSG_WARNING, "Failed to read new " + "configuration file - continuing with " + "old."); + continue; + } + } +} + + +#ifdef HOSTAPD_DUMP_STATE +/** + * hostapd_dump_state - SIGUSR1 handler to dump hostapd state to a text file + */ +static void hostapd_dump_state(struct hostapd_data *hapd) +{ + FILE *f; + time_t now; + struct sta_info *sta; + int i; + char *buf; + + if (!hapd->conf->dump_log_name) { + wpa_printf(MSG_DEBUG, "Dump file not defined - ignoring dump " + "request"); + return; + } + + wpa_printf(MSG_DEBUG, "Dumping hostapd state to '%s'", + hapd->conf->dump_log_name); + f = fopen(hapd->conf->dump_log_name, "w"); + if (f == NULL) { + wpa_printf(MSG_WARNING, "Could not open dump file '%s' for " + "writing.", hapd->conf->dump_log_name); + return; + } + + time(&now); + fprintf(f, "hostapd state dump - %s", ctime(&now)); + fprintf(f, "num_sta=%d num_sta_non_erp=%d " + "num_sta_no_short_slot_time=%d\n" + "num_sta_no_short_preamble=%d\n", + hapd->num_sta, hapd->iface->num_sta_non_erp, + hapd->iface->num_sta_no_short_slot_time, + hapd->iface->num_sta_no_short_preamble); + + 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%s%s%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->flags & WLAN_STA_PENDING_POLL ? "[PENDING_POLL" : + ""), + (sta->flags & WLAN_STA_SHORT_PREAMBLE ? + "[SHORT_PREAMBLE]" : ""), + (sta->flags & WLAN_STA_PREAUTH ? "[PREAUTH]" : ""), + (sta->flags & WLAN_STA_WME ? "[WME]" : ""), + (sta->flags & WLAN_STA_MFP ? "[MFP]" : ""), + (sta->flags & WLAN_STA_WPS ? "[WPS]" : ""), + (sta->flags & WLAN_STA_MAYBE_WPS ? "[MAYBE_WPS]" : ""), + (sta->flags & WLAN_STA_NONERP ? "[NonERP]" : ""), + sta->capability, + sta->listen_interval); + + fprintf(f, " supported_rates="); + for (i = 0; i < sta->supported_rates_len; i++) + fprintf(f, "%02x ", sta->supported_rates[i]); + fprintf(f, "\n"); + + 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 = os_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); + os_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; + size_t i, j; + + for (i = 0; i < hapds->count; i++) { + struct hostapd_iface *hapd_iface = hapds->iface[i]; + for (j = 0; j < hapd_iface->num_bss; j++) + hostapd_dump_state(hapd_iface->bss[j]); + } +#endif /* HOSTAPD_DUMP_STATE */ +} +#endif /* CONFIG_NATIVE_WINDOWS */ + +static void hostapd_broadcast_key_clear_iface(struct hostapd_data *hapd, + char *ifname) +{ + int i; + + for (i = 0; i < NUM_WEP_KEYS; i++) { + if (hostapd_set_encryption(ifname, hapd, "none", NULL, i, NULL, + 0, i == 0 ? 1 : 0)) { + wpa_printf(MSG_DEBUG, "Failed to clear default " + "encryption keys (ifname=%s keyidx=%d)", + ifname, i); + } + } +#ifdef CONFIG_IEEE80211W + if (hapd->conf->ieee80211w) { + for (i = NUM_WEP_KEYS; i < NUM_WEP_KEYS + 2; i++) { + if (hostapd_set_encryption(ifname, hapd, "none", NULL, + i, NULL, 0, + i == 0 ? 1 : 0)) { + wpa_printf(MSG_DEBUG, "Failed to clear " + "default mgmt encryption keys " + "(ifname=%s keyidx=%d)", ifname, i); + } + } + } +#endif /* CONFIG_IEEE80211W */ +} + + +static int hostapd_broadcast_wep_clear(struct hostapd_data *hapd) +{ + hostapd_broadcast_key_clear_iface(hapd, hapd->conf->iface); + return 0; +} + + +static int hostapd_broadcast_wep_set(struct hostapd_data *hapd) +{ + int errors = 0, idx; + struct hostapd_ssid *ssid = &hapd->conf->ssid; + + idx = ssid->wep.idx; + if (ssid->wep.default_len && + hostapd_set_encryption(hapd->conf->iface, + hapd, "WEP", NULL, idx, + ssid->wep.key[idx], + ssid->wep.len[idx], + idx == ssid->wep.idx)) { + wpa_printf(MSG_WARNING, "Could not set WEP encryption."); + errors++; + } + + if (ssid->dyn_vlan_keys) { + size_t i; + for (i = 0; i <= ssid->max_dyn_vlan_keys; i++) { + const char *ifname; + struct hostapd_wep_keys *key = ssid->dyn_vlan_keys[i]; + if (key == NULL) + continue; + ifname = hostapd_get_vlan_id_ifname(hapd->conf->vlan, + i); + if (ifname == NULL) + continue; + + idx = key->idx; + if (hostapd_set_encryption(ifname, hapd, "WEP", NULL, + idx, key->key[idx], + key->len[idx], + idx == key->idx)) { + wpa_printf(MSG_WARNING, "Could not set " + "dynamic VLAN WEP encryption."); + errors++; + } + } + } + + return errors; +} + +/** + * hostapd_cleanup - Per-BSS cleanup (deinitialization) + * @hapd: Pointer to BSS data + * + * This function is used to free all per-BSS data structures and resources. + * This gets called in a loop for each BSS between calls to + * hostapd_cleanup_iface_pre() and hostapd_cleanup_iface() when an interface + * is deinitialized. Most of the modules that are initialized in + * hostapd_setup_bss() are deinitialized here. + */ +static void hostapd_cleanup(struct hostapd_data *hapd) +{ + hostapd_ctrl_iface_deinit(hapd); + + os_free(hapd->default_wep_key); + hapd->default_wep_key = NULL; + iapp_deinit(hapd->iapp); + hapd->iapp = NULL; + accounting_deinit(hapd); + rsn_preauth_iface_deinit(hapd); + if (hapd->wpa_auth) { + wpa_deinit(hapd->wpa_auth); + hapd->wpa_auth = NULL; + + if (hostapd_set_privacy(hapd, 0)) { + wpa_printf(MSG_DEBUG, "Could not disable " + "PrivacyInvoked for interface %s", + hapd->conf->iface); + } + + if (hostapd_set_generic_elem(hapd, (u8 *) "", 0)) { + wpa_printf(MSG_DEBUG, "Could not remove generic " + "information element from interface %s", + hapd->conf->iface); + } + } + ieee802_1x_deinit(hapd); + vlan_deinit(hapd); + hostapd_acl_deinit(hapd); + radius_client_deinit(hapd->radius); + hapd->radius = NULL; + radius_server_deinit(hapd->radius_srv); + hapd->radius_srv = NULL; + +#ifdef CONFIG_IEEE80211R + l2_packet_deinit(hapd->l2); +#endif /* CONFIG_IEEE80211R */ + + hostapd_deinit_wps(hapd); + + hostapd_wireless_event_deinit(hapd); + +#ifdef EAP_TLS_FUNCS + if (hapd->ssl_ctx) { + tls_deinit(hapd->ssl_ctx); + hapd->ssl_ctx = NULL; + } +#endif /* EAP_TLS_FUNCS */ + +#ifdef EAP_SERVER + if (hapd->eap_sim_db_priv) { + eap_sim_db_deinit(hapd->eap_sim_db_priv); + hapd->eap_sim_db_priv = NULL; + } +#endif /* EAP_SERVER */ + + if (hapd->interface_added && + hostapd_bss_remove(hapd, hapd->conf->iface)) { + wpa_printf(MSG_WARNING, "Failed to remove BSS interface %s", + hapd->conf->iface); + } +} + + +/** + * hostapd_cleanup_iface_pre - Preliminary per-interface cleanup + * @iface: Pointer to interface data + * + * This function is called before per-BSS data structures are deinitialized + * with hostapd_cleanup(). + */ +static void hostapd_cleanup_iface_pre(struct hostapd_iface *iface) +{ +} + + +/** + * hostapd_cleanup_iface - Complete per-interface cleanup + * @iface: Pointer to interface data + * + * This function is called after per-BSS data structures are deinitialized + * with hostapd_cleanup(). + */ +static void hostapd_cleanup_iface(struct hostapd_iface *iface) +{ + hostapd_free_hw_features(iface->hw_features, iface->num_hw_features); + iface->hw_features = NULL; + os_free(iface->current_rates); + iface->current_rates = NULL; + ap_list_deinit(iface); + hostapd_config_free(iface->conf); + iface->conf = NULL; + + os_free(iface->config_fname); + os_free(iface->bss); + os_free(iface); +} + + +static int hostapd_setup_encryption(char *iface, struct hostapd_data *hapd) +{ + int i; + + hostapd_broadcast_wep_set(hapd); + + if (hapd->conf->ssid.wep.default_len) + return 0; + + for (i = 0; i < 4; i++) { + if (hapd->conf->ssid.wep.key[i] && + hostapd_set_encryption(iface, hapd, "WEP", NULL, + i, hapd->conf->ssid.wep.key[i], + hapd->conf->ssid.wep.len[i], + i == hapd->conf->ssid.wep.idx)) { + wpa_printf(MSG_WARNING, "Could not set WEP " + "encryption."); + return -1; + } + if (hapd->conf->ssid.wep.key[i] && + i == hapd->conf->ssid.wep.idx) + hostapd_set_privacy(hapd, 1); + } + + return 0; +} + + +static int hostapd_flush_old_stations(struct hostapd_data *hapd) +{ + int ret = 0; + + if (hostapd_drv_none(hapd)) + return 0; + + wpa_printf(MSG_DEBUG, "Flushing old station entries"); + if (hostapd_flush(hapd)) { + wpa_printf(MSG_WARNING, "Could not connect to kernel driver."); + ret = -1; + } + wpa_printf(MSG_DEBUG, "Deauthenticate all stations"); + hostapd_deauth_all_stas(hapd); + + return ret; +} + + +static void hostapd_wpa_auth_logger(void *ctx, const u8 *addr, + logger_level level, const char *txt) +{ + struct hostapd_data *hapd = ctx; + int hlevel; + + switch (level) { + case LOGGER_WARNING: + hlevel = HOSTAPD_LEVEL_WARNING; + break; + case LOGGER_INFO: + hlevel = HOSTAPD_LEVEL_INFO; + break; + case LOGGER_DEBUG: + default: + hlevel = HOSTAPD_LEVEL_DEBUG; + break; + } + + hostapd_logger(hapd, addr, HOSTAPD_MODULE_WPA, hlevel, "%s", txt); +} + + +static void hostapd_wpa_auth_disconnect(void *ctx, const u8 *addr, + u16 reason) +{ + struct hostapd_data *hapd = ctx; + struct sta_info *sta; + + wpa_printf(MSG_DEBUG, "%s: WPA authenticator requests disconnect: " + "STA " MACSTR " reason %d", + __func__, MAC2STR(addr), reason); + + sta = ap_get_sta(hapd, addr); + hostapd_sta_deauth(hapd, addr, reason); + if (sta == NULL) + return; + 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 hostapd_wpa_auth_mic_failure_report(void *ctx, const u8 *addr) +{ + struct hostapd_data *hapd = ctx; + ieee80211_michael_mic_failure(hapd, addr, 0); +} + + +static void hostapd_wpa_auth_set_eapol(void *ctx, const u8 *addr, + wpa_eapol_variable var, int value) +{ + struct hostapd_data *hapd = ctx; + struct sta_info *sta = ap_get_sta(hapd, addr); + if (sta == NULL) + return; + switch (var) { + case WPA_EAPOL_portEnabled: + ieee802_1x_notify_port_enabled(sta->eapol_sm, value); + break; + case WPA_EAPOL_portValid: + ieee802_1x_notify_port_valid(sta->eapol_sm, value); + break; + case WPA_EAPOL_authorized: + ieee802_1x_set_sta_authorized(hapd, sta, value); + break; + case WPA_EAPOL_portControl_Auto: + if (sta->eapol_sm) + sta->eapol_sm->portControl = Auto; + break; + case WPA_EAPOL_keyRun: + if (sta->eapol_sm) + sta->eapol_sm->keyRun = value ? TRUE : FALSE; + break; + case WPA_EAPOL_keyAvailable: + if (sta->eapol_sm) + sta->eapol_sm->eap_if->eapKeyAvailable = + value ? TRUE : FALSE; + break; + case WPA_EAPOL_keyDone: + if (sta->eapol_sm) + sta->eapol_sm->keyDone = value ? TRUE : FALSE; + break; + case WPA_EAPOL_inc_EapolFramesTx: + if (sta->eapol_sm) + sta->eapol_sm->dot1xAuthEapolFramesTx++; + break; + } +} + + +static int hostapd_wpa_auth_get_eapol(void *ctx, const u8 *addr, + wpa_eapol_variable var) +{ + struct hostapd_data *hapd = ctx; + struct sta_info *sta = ap_get_sta(hapd, addr); + if (sta == NULL || sta->eapol_sm == NULL) + return -1; + switch (var) { + case WPA_EAPOL_keyRun: + return sta->eapol_sm->keyRun; + case WPA_EAPOL_keyAvailable: + return sta->eapol_sm->eap_if->eapKeyAvailable; + default: + return -1; + } +} + + +static const u8 * hostapd_wpa_auth_get_psk(void *ctx, const u8 *addr, + const u8 *prev_psk) +{ + struct hostapd_data *hapd = ctx; + return hostapd_get_psk(hapd->conf, addr, prev_psk); +} + + +static int hostapd_wpa_auth_get_msk(void *ctx, const u8 *addr, u8 *msk, + size_t *len) +{ + struct hostapd_data *hapd = ctx; + const u8 *key; + size_t keylen; + struct sta_info *sta; + + sta = ap_get_sta(hapd, addr); + if (sta == NULL) + return -1; + + key = ieee802_1x_get_key(sta->eapol_sm, &keylen); + if (key == NULL) + return -1; + + if (keylen > *len) + keylen = *len; + os_memcpy(msk, key, keylen); + *len = keylen; + + return 0; +} + + +static int hostapd_wpa_auth_set_key(void *ctx, int vlan_id, const char *alg, + const u8 *addr, int idx, u8 *key, + size_t key_len) +{ + struct hostapd_data *hapd = ctx; + const char *ifname = hapd->conf->iface; + + if (vlan_id > 0) { + ifname = hostapd_get_vlan_id_ifname(hapd->conf->vlan, vlan_id); + if (ifname == NULL) + return -1; + } + + return hostapd_set_encryption(ifname, hapd, alg, addr, idx, + key, key_len, 1); +} + + +static int hostapd_wpa_auth_get_seqnum(void *ctx, const u8 *addr, int idx, + u8 *seq) +{ + struct hostapd_data *hapd = ctx; + return hostapd_get_seqnum(hapd->conf->iface, hapd, addr, idx, seq); +} + + +static int hostapd_wpa_auth_get_seqnum_igtk(void *ctx, const u8 *addr, int idx, + u8 *seq) +{ + struct hostapd_data *hapd = ctx; + return hostapd_get_seqnum_igtk(hapd->conf->iface, hapd, addr, idx, + seq); +} + + +static int hostapd_wpa_auth_send_eapol(void *ctx, const u8 *addr, + const u8 *data, size_t data_len, + int encrypt) +{ + struct hostapd_data *hapd = ctx; + return hostapd_send_eapol(hapd, addr, data, data_len, encrypt); +} + + +static int hostapd_wpa_auth_for_each_sta( + void *ctx, int (*cb)(struct wpa_state_machine *sm, void *ctx), + void *cb_ctx) +{ + struct hostapd_data *hapd = ctx; + struct sta_info *sta; + + for (sta = hapd->sta_list; sta; sta = sta->next) { + if (sta->wpa_sm && cb(sta->wpa_sm, cb_ctx)) + return 1; + } + return 0; +} + + +static int hostapd_wpa_auth_for_each_auth( + void *ctx, int (*cb)(struct wpa_authenticator *sm, void *ctx), + void *cb_ctx) +{ + struct hostapd_data *ohapd; + size_t i, j; + struct hapd_interfaces *interfaces = eloop_get_user_data(); + + for (i = 0; i < interfaces->count; i++) { + for (j = 0; j < interfaces->iface[i]->num_bss; j++) { + ohapd = interfaces->iface[i]->bss[j]; + if (cb(ohapd->wpa_auth, cb_ctx)) + return 1; + } + } + + return 0; +} + + +static int hostapd_wpa_auth_send_ether(void *ctx, const u8 *dst, u16 proto, + const u8 *data, size_t data_len) +{ + struct hostapd_data *hapd = ctx; + + if (hapd->driver && hapd->driver->send_ether) + return hapd->driver->send_ether(hapd->drv_priv, dst, + hapd->own_addr, proto, + data, data_len); + if (hapd->l2 == NULL) + return -1; + return l2_packet_send(hapd->l2, dst, proto, data, data_len); +} + + +#ifdef CONFIG_IEEE80211R + +static int hostapd_wpa_auth_send_ft_action(void *ctx, const u8 *dst, + const u8 *data, size_t data_len) +{ + struct hostapd_data *hapd = ctx; + int res; + struct ieee80211_mgmt *m; + size_t mlen; + struct sta_info *sta; + + sta = ap_get_sta(hapd, dst); + if (sta == NULL || sta->wpa_sm == NULL) + return -1; + + m = os_zalloc(sizeof(*m) + data_len); + if (m == NULL) + return -1; + mlen = ((u8 *) &m->u - (u8 *) m) + data_len; + m->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, + WLAN_FC_STYPE_ACTION); + os_memcpy(m->da, dst, ETH_ALEN); + os_memcpy(m->sa, hapd->own_addr, ETH_ALEN); + os_memcpy(m->bssid, hapd->own_addr, ETH_ALEN); + os_memcpy(&m->u, data, data_len); + + res = hostapd_send_mgmt_frame(hapd, (u8 *) m, mlen, 0); + os_free(m); + return res; +} + + +static struct wpa_state_machine * +hostapd_wpa_auth_add_sta(void *ctx, const u8 *sta_addr) +{ + struct hostapd_data *hapd = ctx; + struct sta_info *sta; + + sta = ap_sta_add(hapd, sta_addr); + if (sta == NULL) + return NULL; + if (sta->wpa_sm) + return sta->wpa_sm; + + sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth, sta->addr); + if (sta->wpa_sm == NULL) { + ap_free_sta(hapd, sta); + return NULL; + } + sta->auth_alg = WLAN_AUTH_FT; + + return sta->wpa_sm; +} + + +static void hostapd_rrb_receive(void *ctx, const u8 *src_addr, const u8 *buf, + size_t len) +{ + struct hostapd_data *hapd = ctx; + wpa_ft_rrb_rx(hapd->wpa_auth, src_addr, buf, len); +} + +#endif /* CONFIG_IEEE80211R */ + + +/** + * hostapd_validate_bssid_configuration - Validate BSSID configuration + * @iface: Pointer to interface data + * Returns: 0 on success, -1 on failure + * + * This function is used to validate that the configured BSSIDs are valid. + */ +static int hostapd_validate_bssid_configuration(struct hostapd_iface *iface) +{ + u8 mask[ETH_ALEN] = { 0 }; + struct hostapd_data *hapd = iface->bss[0]; + unsigned int i = iface->conf->num_bss, bits = 0, j; + int res; + + if (hostapd_drv_none(hapd)) + return 0; + + /* Generate BSSID mask that is large enough to cover the BSSIDs. */ + + /* Determine the bits necessary to cover the number of BSSIDs. */ + for (i--; i; i >>= 1) + bits++; + + /* Determine the bits necessary to any configured BSSIDs, + if they are higher than the number of BSSIDs. */ + for (j = 0; j < iface->conf->num_bss; j++) { + if (hostapd_mac_comp_empty(iface->conf->bss[j].bssid) == 0) + continue; + + for (i = 0; i < ETH_ALEN; i++) { + mask[i] |= + iface->conf->bss[j].bssid[i] ^ + hapd->own_addr[i]; + } + } + + for (i = 0; i < ETH_ALEN && mask[i] == 0; i++) + ; + j = 0; + if (i < ETH_ALEN) { + j = (5 - i) * 8; + + while (mask[i] != 0) { + mask[i] >>= 1; + j++; + } + } + + if (bits < j) + bits = j; + + if (bits > 40) + return -1; + + os_memset(mask, 0xff, ETH_ALEN); + j = bits / 8; + for (i = 5; i > 5 - j; i--) + mask[i] = 0; + j = bits % 8; + while (j--) + mask[i] <<= 1; + + wpa_printf(MSG_DEBUG, "BSS count %lu, BSSID mask " MACSTR " (%d bits)", + (unsigned long) iface->conf->num_bss, MAC2STR(mask), bits); + + res = hostapd_valid_bss_mask(hapd, hapd->own_addr, mask); + if (res == 0) + return 0; + + if (res < 0) { + wpa_printf(MSG_ERROR, "Driver did not accept BSSID mask " + MACSTR " for start address " MACSTR ".", + MAC2STR(mask), MAC2STR(hapd->own_addr)); + return -1; + } + + for (i = 0; i < ETH_ALEN; i++) { + if ((hapd->own_addr[i] & mask[i]) != hapd->own_addr[i]) { + wpa_printf(MSG_ERROR, "Invalid BSSID mask " MACSTR + " for start address " MACSTR ".", + MAC2STR(mask), MAC2STR(hapd->own_addr)); + wpa_printf(MSG_ERROR, "Start address must be the " + "first address in the block (i.e., addr " + "AND mask == addr)."); + return -1; + } + } + + return 0; +} + + +static int mac_in_conf(struct hostapd_config *conf, const void *a) +{ + size_t i; + + for (i = 0; i < conf->num_bss; i++) { + if (hostapd_mac_comp(conf->bss[i].bssid, a) == 0) { + return 1; + } + } + + return 0; +} + + +static int hostapd_setup_wpa(struct hostapd_data *hapd) +{ + struct wpa_auth_config _conf; + struct wpa_auth_callbacks cb; + const u8 *wpa_ie; + size_t wpa_ie_len; + + hostapd_wpa_auth_conf(hapd->conf, &_conf); + os_memset(&cb, 0, sizeof(cb)); + cb.ctx = hapd; + cb.logger = hostapd_wpa_auth_logger; + cb.disconnect = hostapd_wpa_auth_disconnect; + cb.mic_failure_report = hostapd_wpa_auth_mic_failure_report; + cb.set_eapol = hostapd_wpa_auth_set_eapol; + cb.get_eapol = hostapd_wpa_auth_get_eapol; + cb.get_psk = hostapd_wpa_auth_get_psk; + cb.get_msk = hostapd_wpa_auth_get_msk; + cb.set_key = hostapd_wpa_auth_set_key; + cb.get_seqnum = hostapd_wpa_auth_get_seqnum; + cb.get_seqnum_igtk = hostapd_wpa_auth_get_seqnum_igtk; + cb.send_eapol = hostapd_wpa_auth_send_eapol; + cb.for_each_sta = hostapd_wpa_auth_for_each_sta; + cb.for_each_auth = hostapd_wpa_auth_for_each_auth; + cb.send_ether = hostapd_wpa_auth_send_ether; +#ifdef CONFIG_IEEE80211R + cb.send_ft_action = hostapd_wpa_auth_send_ft_action; + cb.add_sta = hostapd_wpa_auth_add_sta; +#endif /* CONFIG_IEEE80211R */ + hapd->wpa_auth = wpa_init(hapd->own_addr, &_conf, &cb); + if (hapd->wpa_auth == NULL) { + wpa_printf(MSG_ERROR, "WPA initialization failed."); + return -1; + } + + if (hostapd_set_privacy(hapd, 1)) { + wpa_printf(MSG_ERROR, "Could not set PrivacyInvoked " + "for interface %s", hapd->conf->iface); + return -1; + } + + wpa_ie = wpa_auth_get_wpa_ie(hapd->wpa_auth, &wpa_ie_len); + if (hostapd_set_generic_elem(hapd, wpa_ie, wpa_ie_len)) { + wpa_printf(MSG_ERROR, "Failed to configure WPA IE for " + "the kernel driver."); + return -1; + } + + if (rsn_preauth_iface_init(hapd)) { + wpa_printf(MSG_ERROR, "Initialization of RSN " + "pre-authentication failed."); + return -1; + } + + return 0; + +} + + +static int hostapd_setup_radius_srv(struct hostapd_data *hapd, + struct hostapd_bss_config *conf) +{ + struct radius_server_conf srv; + os_memset(&srv, 0, sizeof(srv)); + srv.client_file = conf->radius_server_clients; + srv.auth_port = conf->radius_server_auth_port; + srv.conf_ctx = conf; + srv.eap_sim_db_priv = hapd->eap_sim_db_priv; + srv.ssl_ctx = hapd->ssl_ctx; + srv.pac_opaque_encr_key = conf->pac_opaque_encr_key; + srv.eap_fast_a_id = conf->eap_fast_a_id; + srv.eap_fast_a_id_len = conf->eap_fast_a_id_len; + srv.eap_fast_a_id_info = conf->eap_fast_a_id_info; + srv.eap_fast_prov = conf->eap_fast_prov; + srv.pac_key_lifetime = conf->pac_key_lifetime; + srv.pac_key_refresh_time = conf->pac_key_refresh_time; + srv.eap_sim_aka_result_ind = conf->eap_sim_aka_result_ind; + srv.tnc = conf->tnc; + srv.wps = hapd->wps; + srv.ipv6 = conf->radius_server_ipv6; + srv.get_eap_user = hostapd_radius_get_eap_user; + srv.eap_req_id_text = conf->eap_req_id_text; + srv.eap_req_id_text_len = conf->eap_req_id_text_len; + + hapd->radius_srv = radius_server_init(&srv); + if (hapd->radius_srv == NULL) { + wpa_printf(MSG_ERROR, "RADIUS server initialization failed."); + return -1; + } + + return 0; +} + + +/** + * hostapd_setup_bss - Per-BSS setup (initialization) + * @hapd: Pointer to BSS data + * @first: Whether this BSS is the first BSS of an interface + * + * This function is used to initialize all per-BSS data structures and + * resources. This gets called in a loop for each BSS when an interface is + * initialized. Most of the modules that are initialized here will be + * deinitialized in hostapd_cleanup(). + */ +static int hostapd_setup_bss(struct hostapd_data *hapd, int first) +{ + struct hostapd_bss_config *conf = hapd->conf; + u8 ssid[HOSTAPD_MAX_SSID_LEN + 1]; + int ssid_len, set_ssid; + + if (!first) { + if (hostapd_mac_comp_empty(hapd->conf->bssid) == 0) { + /* Allocate the next available BSSID. */ + do { + inc_byte_array(hapd->own_addr, ETH_ALEN); + } while (mac_in_conf(hapd->iconf, hapd->own_addr)); + } else { + /* Allocate the configured BSSID. */ + os_memcpy(hapd->own_addr, hapd->conf->bssid, ETH_ALEN); + + if (hostapd_mac_comp(hapd->own_addr, + hapd->iface->bss[0]->own_addr) == + 0) { + wpa_printf(MSG_ERROR, "BSS '%s' may not have " + "BSSID set to the MAC address of " + "the radio", hapd->conf->iface); + return -1; + } + } + + hapd->interface_added = 1; + if (hostapd_bss_add(hapd->iface->bss[0], hapd->conf->iface, + hapd->own_addr)) { + wpa_printf(MSG_ERROR, "Failed to add BSS (BSSID=" + MACSTR ")", MAC2STR(hapd->own_addr)); + 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) { + wpa_printf(MSG_ERROR, "Could not read SSID from system"); + return -1; + } + if (conf->ssid.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.ssid_len != (size_t) ssid_len || + os_memcmp(conf->ssid.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.ssid_len = ssid_len; + os_memcpy(conf->ssid.ssid, ssid, conf->ssid.ssid_len); + conf->ssid.ssid[conf->ssid.ssid_len] = '\0'; + } + + if (!hostapd_drv_none(hapd)) { + wpa_printf(MSG_ERROR, "Using interface %s with hwaddr " MACSTR + " and ssid '%s'", + hapd->conf->iface, MAC2STR(hapd->own_addr), + hapd->conf->ssid.ssid); + } + + if (hostapd_setup_wpa_psk(conf)) { + wpa_printf(MSG_ERROR, "WPA-PSK setup failed."); + return -1; + } + + /* Set flag for whether SSID is broadcast in beacons */ + if (hostapd_set_broadcast_ssid(hapd, + !!hapd->conf->ignore_broadcast_ssid)) { + wpa_printf(MSG_ERROR, "Could not set broadcast SSID flag for " + "kernel driver"); + return -1; + } + + if (hostapd_set_dtim_period(hapd, hapd->conf->dtim_period)) { + wpa_printf(MSG_ERROR, "Could not set DTIM period for kernel " + "driver"); + 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.ssid, + conf->ssid.ssid_len)) { + wpa_printf(MSG_ERROR, "Could not set SSID for kernel driver"); + return -1; + } + + if (wpa_debug_level == MSG_MSGDUMP) + conf->radius->msg_dumps = 1; + hapd->radius = radius_client_init(hapd, conf->radius); + if (hapd->radius == NULL) { + wpa_printf(MSG_ERROR, "RADIUS client initialization failed."); + return -1; + } + + if (hostapd_acl_init(hapd)) { + wpa_printf(MSG_ERROR, "ACL initialization failed."); + return -1; + } + if (hostapd_init_wps(hapd, conf)) + return -1; + + if (ieee802_1x_init(hapd)) { + wpa_printf(MSG_ERROR, "IEEE 802.1X initialization failed."); + return -1; + } + + if (hapd->conf->wpa && hostapd_setup_wpa(hapd)) + return -1; + + if (accounting_init(hapd)) { + wpa_printf(MSG_ERROR, "Accounting initialization failed."); + return -1; + } + + if (hapd->conf->ieee802_11f && + (hapd->iapp = iapp_init(hapd, hapd->conf->iapp_iface)) == NULL) { + wpa_printf(MSG_ERROR, "IEEE 802.11F (IAPP) initialization " + "failed."); + return -1; + } + + if (hostapd_ctrl_iface_init(hapd)) { + wpa_printf(MSG_ERROR, "Failed to setup control interface"); + return -1; + } + + if (!hostapd_drv_none(hapd) && vlan_init(hapd)) { + wpa_printf(MSG_ERROR, "VLAN initialization failed."); + return -1; + } + +#ifdef CONFIG_IEEE80211R + if (!hostapd_drv_none(hapd)) { + hapd->l2 = l2_packet_init(hapd->conf->iface, NULL, ETH_P_RRB, + hostapd_rrb_receive, hapd, 0); + if (hapd->l2 == NULL && + (hapd->driver == NULL || + hapd->driver->send_ether == NULL)) { + wpa_printf(MSG_ERROR, "Failed to open l2_packet " + "interface"); + return -1; + } + } +#endif /* CONFIG_IEEE80211R */ + + ieee802_11_set_beacon(hapd); + + if (conf->radius_server_clients && + hostapd_setup_radius_srv(hapd, conf)) + return -1; + + return 0; +} + + +static void hostapd_tx_queue_params(struct hostapd_iface *iface) +{ + struct hostapd_data *hapd = iface->bss[0]; + int i; + struct hostapd_tx_queue_params *p; + + for (i = 0; i < NUM_TX_QUEUES; i++) { + p = &iface->conf->tx_queue[i]; + + if (!p->configured) + continue; + + if (hostapd_set_tx_queue_params(hapd, i, p->aifs, p->cwmin, + p->cwmax, p->burst)) { + wpa_printf(MSG_DEBUG, "Failed to set TX queue " + "parameters for queue %d.", i); + /* Continue anyway */ + } + } +} + + +static int hostapd_radius_get_eap_user(void *ctx, const u8 *identity, + size_t identity_len, int phase2, + struct eap_user *user) +{ + const struct hostapd_eap_user *eap_user; + int i, count; + + eap_user = hostapd_get_eap_user(ctx, identity, identity_len, phase2); + if (eap_user == NULL) + return -1; + + if (user == NULL) + return 0; + + os_memset(user, 0, sizeof(*user)); + count = EAP_USER_MAX_METHODS; + if (count > EAP_MAX_METHODS) + count = EAP_MAX_METHODS; + for (i = 0; i < count; i++) { + user->methods[i].vendor = eap_user->methods[i].vendor; + user->methods[i].method = eap_user->methods[i].method; + } + + if (eap_user->password) { + user->password = os_malloc(eap_user->password_len); + if (user->password == NULL) + return -1; + os_memcpy(user->password, eap_user->password, + eap_user->password_len); + user->password_len = eap_user->password_len; + user->password_hash = eap_user->password_hash; + } + user->force_version = eap_user->force_version; + user->ttls_auth = eap_user->ttls_auth; + + return 0; +} + + +static int setup_interface(struct hostapd_iface *iface) +{ + struct hostapd_data *hapd = iface->bss[0]; + struct hostapd_bss_config *conf = hapd->conf; + size_t i; + char country[4]; + u8 *b = conf->bssid; + int freq; + size_t j; + int ret = 0; + u8 *prev_addr; + + /* + * Initialize the driver interface and make sure that all BSSes get + * configured with a pointer to this driver interface. + */ + if (b[0] | b[1] | b[2] | b[3] | b[4] | b[5]) { + hapd->drv_priv = hostapd_driver_init_bssid(hapd, b); + } else { + hapd->drv_priv = hostapd_driver_init(hapd); + } + + if (hapd->drv_priv == NULL) { + wpa_printf(MSG_ERROR, "%s driver initialization failed.", + hapd->driver ? hapd->driver->name : "Unknown"); + hapd->driver = NULL; + return -1; + } + for (i = 0; i < iface->num_bss; i++) { + iface->bss[i]->driver = hapd->driver; + iface->bss[i]->drv_priv = hapd->drv_priv; + } + + if (hostapd_validate_bssid_configuration(iface)) + return -1; + +#ifdef CONFIG_IEEE80211N + SET_2BIT_LE16(&iface->ht_op_mode, + HT_INFO_OPERATION_MODE_OP_MODE_OFFSET, + OP_MODE_PURE); +#endif /* CONFIG_IEEE80211N */ + + if (hapd->iconf->country[0] && hapd->iconf->country[1]) { + os_memcpy(country, hapd->iconf->country, 3); + country[3] = '\0'; + if (hostapd_set_country(hapd, country) < 0) { + wpa_printf(MSG_ERROR, "Failed to set country code"); + return -1; + } + } + + if (hapd->iconf->ieee80211d && + hostapd_set_ieee80211d(hapd, 1) < 0) { + wpa_printf(MSG_ERROR, "Failed to set ieee80211d (%d)", + hapd->iconf->ieee80211d); + return -1; + } + + if (hapd->iconf->bridge_packets != INTERNAL_BRIDGE_DO_NOT_CONTROL && + hostapd_set_internal_bridge(hapd, hapd->iconf->bridge_packets)) { + wpa_printf(MSG_ERROR, "Failed to set bridge_packets for " + "kernel driver"); + return -1; + } + + /* TODO: merge with hostapd_driver_init() ? */ + if (hostapd_wireless_event_init(hapd) < 0) + return -1; + + if (hostapd_get_hw_features(iface)) { + /* Not all drivers support this yet, so continue without hw + * feature data. */ + } else { + int ret = hostapd_select_hw_mode(iface); + if (ret < 0) { + wpa_printf(MSG_ERROR, "Could not select hw_mode and " + "channel. (%d)", ret); + return -1; + } + } + + hostapd_flush_old_stations(hapd); + hostapd_set_privacy(hapd, 0); + + if (hapd->iconf->channel) { + freq = hostapd_hw_get_freq(hapd, hapd->iconf->channel); + wpa_printf(MSG_DEBUG, "Mode: %s Channel: %d " + "Frequency: %d MHz", + hostapd_hw_mode_txt(hapd->iconf->hw_mode), + hapd->iconf->channel, freq); + + if (hostapd_set_freq(hapd, hapd->iconf->hw_mode, freq, + hapd->iconf->ieee80211n, + hapd->iconf->secondary_channel)) { + wpa_printf(MSG_ERROR, "Could not set channel for " + "kernel driver"); + return -1; + } + } + + hostapd_broadcast_wep_clear(hapd); + if (hostapd_setup_encryption(hapd->conf->iface, hapd)) + return -1; + + hostapd_set_beacon_int(hapd, hapd->iconf->beacon_int); + ieee802_11_set_beacon(hapd); + + if (hapd->iconf->rts_threshold > -1 && + hostapd_set_rts(hapd, hapd->iconf->rts_threshold)) { + wpa_printf(MSG_ERROR, "Could not set RTS threshold for " + "kernel driver"); + return -1; + } + + if (hapd->iconf->fragm_threshold > -1 && + hostapd_set_frag(hapd, hapd->iconf->fragm_threshold)) { + wpa_printf(MSG_ERROR, "Could not set fragmentation threshold " + "for kernel driver"); + return -1; + } + + prev_addr = hapd->own_addr; + + for (j = 0; j < iface->num_bss; j++) { + hapd = iface->bss[j]; + if (j) + os_memcpy(hapd->own_addr, prev_addr, ETH_ALEN); + if (hostapd_setup_bss(hapd, j == 0)) + return -1; + if (hostapd_mac_comp_empty(hapd->conf->bssid) == 0) + prev_addr = hapd->own_addr; + } + + hostapd_tx_queue_params(iface); + + ap_list_init(iface); + + if (hostapd_driver_commit(hapd) < 0) { + wpa_printf(MSG_ERROR, "%s: Failed to commit driver " + "configuration", __func__); + return -1; + } + + return ret; +} + + +/** + * hostapd_setup_interface - Setup of an interface + * @iface: Pointer to interface data. + * Returns: 0 on success, -1 on failure + * + * Initializes the driver interface, validates the configuration, + * and sets driver parameters based on the configuration. + * Flushes old stations, sets the channel, encryption, + * beacons, and WDS links based on the configuration. + */ +static int hostapd_setup_interface(struct hostapd_iface *iface) +{ + int ret; + + ret = setup_interface(iface); + if (ret) { + wpa_printf(MSG_DEBUG, "%s: Unable to setup interface.", + iface->bss[0]->conf->iface); + eloop_terminate(); + return -1; + } else if (!hostapd_drv_none(iface->bss[0])) { + wpa_printf(MSG_DEBUG, "%s: Setup of interface done.", + iface->bss[0]->conf->iface); + } + + return 0; +} + + +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-2009, Jouni Malinen <j@w1.fi> " + "and contributors\n"); +} + + +static void usage(void) +{ + show_version(); + fprintf(stderr, + "\n" + "usage: hostapd [-hdBKtv] [-P <PID file>] " + "<configuration file(s)>\n" + "\n" + "options:\n" + " -h show this usage\n" + " -d show more debug messages (-dd for even more)\n" + " -B run daemon in the background\n" + " -P PID file\n" + " -K include key data in debug messages\n" + " -t include timestamps in some debug messages\n" + " -v show hostapd version\n"); + + exit(1); +} + + +/** + * hostapd_alloc_bss_data - Allocate and initialize per-BSS data + * @hapd_iface: Pointer to interface data + * @conf: Pointer to per-interface configuration + * @bss: Pointer to per-BSS configuration for this BSS + * Returns: Pointer to allocated BSS data + * + * This function is used to allocate per-BSS data structure. This data will be + * freed after hostapd_cleanup() is called for it during interface + * deinitialization. + */ +static struct hostapd_data * +hostapd_alloc_bss_data(struct hostapd_iface *hapd_iface, + struct hostapd_config *conf, + struct hostapd_bss_config *bss) +{ + struct hostapd_data *hapd; + + hapd = os_zalloc(sizeof(*hapd)); + if (hapd == NULL) + return NULL; + + hapd->iconf = conf; + hapd->conf = bss; + hapd->iface = hapd_iface; + + 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->conf->dh_file)) { + struct tls_connection_params params; + + hapd->ssl_ctx = tls_init(NULL); + if (hapd->ssl_ctx == NULL) { + wpa_printf(MSG_ERROR, "Failed to initialize TLS"); + goto fail; + } + + os_memset(¶ms, 0, sizeof(params)); + params.ca_cert = hapd->conf->ca_cert; + params.client_cert = hapd->conf->server_cert; + params.private_key = hapd->conf->private_key; + params.private_key_passwd = hapd->conf->private_key_passwd; + params.dh_file = hapd->conf->dh_file; + + if (tls_global_set_params(hapd->ssl_ctx, ¶ms)) { + wpa_printf(MSG_ERROR, "Failed to set TLS parameters"); + goto fail; + } + + if (tls_global_set_verify(hapd->ssl_ctx, + hapd->conf->check_crl)) { + wpa_printf(MSG_ERROR, "Failed to enable check_crl"); + goto fail; + } + } +#endif /* EAP_TLS_FUNCS */ + +#ifdef EAP_SERVER + if (hapd->conf->eap_sim_db) { + hapd->eap_sim_db_priv = + eap_sim_db_init(hapd->conf->eap_sim_db, + hostapd_sim_db_cb, hapd); + if (hapd->eap_sim_db_priv == NULL) { + wpa_printf(MSG_ERROR, "Failed to initialize EAP-SIM " + "database interface"); + goto fail; + } + } +#endif /* EAP_SERVER */ + + hapd->driver = hapd->iconf->driver; + + return hapd; + +#if defined(EAP_TLS_FUNCS) || defined(EAP_SERVER) +fail: +#endif + /* TODO: cleanup allocated resources(?) */ + os_free(hapd); + return NULL; +} + + +/** + * hostapd_init - Allocate and initialize per-interface data + * @config_file: Path to the configuration file + * Returns: Pointer to the allocated interface data or %NULL on failure + * + * This function is used to allocate main data structures for per-interface + * data. The allocated data buffer will be freed by calling + * hostapd_cleanup_iface(). + */ +static struct hostapd_iface * hostapd_init(const char *config_file) +{ + struct hostapd_iface *hapd_iface = NULL; + struct hostapd_config *conf = NULL; + struct hostapd_data *hapd; + size_t i; + + hapd_iface = os_zalloc(sizeof(*hapd_iface)); + if (hapd_iface == NULL) + goto fail; + + hapd_iface->config_fname = os_strdup(config_file); + if (hapd_iface->config_fname == NULL) + goto fail; + + conf = hostapd_config_read(hapd_iface->config_fname); + if (conf == NULL) + goto fail; + hapd_iface->conf = conf; + + hapd_iface->num_bss = conf->num_bss; + hapd_iface->bss = os_zalloc(conf->num_bss * + sizeof(struct hostapd_data *)); + if (hapd_iface->bss == NULL) + goto fail; + + for (i = 0; i < conf->num_bss; i++) { + hapd = hapd_iface->bss[i] = + hostapd_alloc_bss_data(hapd_iface, conf, + &conf->bss[i]); + if (hapd == NULL) + goto fail; + } + + return hapd_iface; + +fail: + if (conf) + hostapd_config_free(conf); + if (hapd_iface) { + for (i = 0; hapd_iface->bss && i < hapd_iface->num_bss; i++) { + hapd = hapd_iface->bss[i]; + if (hapd && hapd->ssl_ctx) + tls_deinit(hapd->ssl_ctx); + } + + os_free(hapd_iface->config_fname); + os_free(hapd_iface->bss); + os_free(hapd_iface); + } + return NULL; +} + + +int main(int argc, char *argv[]) +{ + struct hapd_interfaces interfaces; + int ret = 1, k; + size_t i, j; + int c, debug = 0, daemonize = 0, tnc = 0; + const char *pid_file = NULL; + + hostapd_logger_register_cb(hostapd_logger_cb); + + for (;;) { + c = getopt(argc, argv, "BdhKP:tv"); + if (c < 0) + break; + switch (c) { + case 'h': + usage(); + break; + case 'd': + debug++; + if (wpa_debug_level > 0) + wpa_debug_level--; + break; + case 'B': + daemonize++; + break; + case 'K': + wpa_debug_show_keys++; + break; + case 'P': + pid_file = optarg; + break; + case 't': + wpa_debug_timestamp++; + break; + case 'v': + show_version(); + exit(1); + break; + + default: + usage(); + break; + } + } + + if (optind == argc) + usage(); + + if (eap_server_register_methods()) { + wpa_printf(MSG_ERROR, "Failed to register EAP methods"); + return -1; + } + + interfaces.count = argc - optind; + + interfaces.iface = os_malloc(interfaces.count * + sizeof(struct hostapd_iface *)); + if (interfaces.iface == NULL) { + wpa_printf(MSG_ERROR, "malloc failed\n"); + return -1; + } + + if (eloop_init(&interfaces)) { + wpa_printf(MSG_ERROR, "Failed to initialize event loop"); + return -1; + } + +#ifndef CONFIG_NATIVE_WINDOWS + eloop_register_signal(SIGHUP, handle_reload, NULL); + eloop_register_signal(SIGUSR1, handle_dump_state, NULL); +#endif /* CONFIG_NATIVE_WINDOWS */ + eloop_register_signal_terminate(handle_term, NULL); + + /* Initialize interfaces */ + for (i = 0; i < interfaces.count; i++) { + wpa_printf(MSG_ERROR, "Configuration file: %s", + argv[optind + i]); + interfaces.iface[i] = hostapd_init(argv[optind + i]); + if (!interfaces.iface[i]) + goto out; + for (k = 0; k < debug; k++) { + if (interfaces.iface[i]->bss[0]->conf-> + logger_stdout_level > 0) + interfaces.iface[i]->bss[0]->conf-> + logger_stdout_level--; + } + + ret = hostapd_setup_interface(interfaces.iface[i]); + if (ret) + goto out; + + for (k = 0; k < (int) interfaces.iface[i]->num_bss; k++) { + if (interfaces.iface[i]->bss[0]->conf->tnc) + tnc++; + } + } + +#ifdef EAP_TNC + if (tnc && tncs_global_init() < 0) { + wpa_printf(MSG_ERROR, "Failed to initialize TNCS"); + goto out; + } +#endif /* EAP_TNC */ + + if (daemonize && os_daemonize(pid_file)) { + perror("daemon"); + goto out; + } + +#ifndef CONFIG_NATIVE_WINDOWS + openlog("hostapd", 0, LOG_DAEMON); +#endif /* CONFIG_NATIVE_WINDOWS */ + + eloop_run(); + + /* Disconnect associated stations from all interfaces and BSSes */ + for (i = 0; i < interfaces.count; i++) { + for (j = 0; j < interfaces.iface[i]->num_bss; j++) { + struct hostapd_data *hapd = + interfaces.iface[i]->bss[j]; + hostapd_free_stas(hapd); + hostapd_flush_old_stations(hapd); + } + } + + ret = 0; + + out: + /* Deinitialize all interfaces */ + for (i = 0; i < interfaces.count; i++) { + if (!interfaces.iface[i]) + continue; + hostapd_cleanup_iface_pre(interfaces.iface[i]); + for (j = 0; j < interfaces.iface[i]->num_bss; j++) { + struct hostapd_data *hapd = + interfaces.iface[i]->bss[j]; + hostapd_cleanup(hapd); + if (j == interfaces.iface[i]->num_bss - 1 && + hapd->driver) + hostapd_driver_deinit(hapd); + } + for (j = 0; j < interfaces.iface[i]->num_bss; j++) + os_free(interfaces.iface[i]->bss[j]); + hostapd_cleanup_iface(interfaces.iface[i]); + } + os_free(interfaces.iface); + +#ifdef EAP_TNC + tncs_global_deinit(); +#endif /* EAP_TNC */ + + eloop_destroy(); + +#ifndef CONFIG_NATIVE_WINDOWS + closelog(); +#endif /* CONFIG_NATIVE_WINDOWS */ + + eap_server_unregister_methods(); + + os_daemonize_terminate(pid_file); + + return ret; +} diff --git a/contrib/wpa/hostapd/hostapd.conf b/contrib/wpa/hostapd/hostapd.conf new file mode 100644 index 0000000..0602cb0 --- /dev/null +++ b/contrib/wpa/hostapd/hostapd.conf @@ -0,0 +1,1024 @@ +##### 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 and nl80211 driver interfaces, 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/test/none/nl80211/bsd); +# default: hostap). nl80211 is used with all Linux mac80211 drivers. +# Use driver=none if building hostapd as a standalone RADIUS server that does +# not control any wireless/wired driver. +# 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 +# bit 6 (64) = MLME +# +# 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 + +# 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 + +# Country code (ISO/IEC 3166-1). Used to set regulatory domain. +# Set as needed to indicate country in which device is operating. +# This can limit available channels and transmit power. +#country_code=US + +# Enable IEEE 802.11d. This advertises the country_code and the set of allowed +# channels and transmit power levels based on the regulatory limits. The +# country_code setting must be configured with the correct country for +# IEEE 802.11d functions. +# (default: 0 = disabled) +#ieee80211d=1 + +# Operation mode (a = IEEE 802.11a, b = IEEE 802.11b, g = IEEE 802.11g, +# Default: IEEE 802.11b +hw_mode=a + +# Channel number (IEEE 802.11) +# (default: 0, i.e., not set) +# Please note that some drivers (e.g., madwifi) do not use this value from +# hostapd and the channel will need to be configuration separately with +# iwconfig. +channel=60 + +# Beacon interval in kus (1.024 ms) (default: 100; range 15..65535) +beacon_int=100 + +# DTIM (delivery trafic information message) period (range 1..255): +# number of beacons between DTIMs (1 = every beacon includes DTIM element) +# (default: 2) +dtim_period=2 + +# Maximum number of stations allowed in station table. New stations will be +# rejected after the station table is full. IEEE 802.11 has a limit of 2007 +# different association IDs, so this number should not be larger than that. +# (default: 2007) +max_num_sta=255 + +# RTS/CTS threshold; 2347 = disabled (default); range 0..2347 +# If this field is not included in hostapd.conf, hostapd will not control +# RTS threshold and 'iwconfig wlan# rts <val>' can be used to set it. +rts_threshold=2347 + +# Fragmentation threshold; 2346 = disabled (default); range 256..2346 +# If this field is not included in hostapd.conf, hostapd will not control +# fragmentation threshold and 'iwconfig wlan# frag <val>' can be used to set +# it. +fragm_threshold=2346 + +# Rate configuration +# Default is to enable all rates supported by the hardware. This configuration +# item allows this list be filtered so that only the listed rates will be left +# in the list. If the list is empty, all rates are used. This list can have +# entries that are not in the list of rates the hardware supports (such entries +# are ignored). The entries in this list are in 100 kbps, i.e., 11 Mbps = 110. +# If this item is present, at least one rate have to be matching with the rates +# hardware supports. +# default: use the most common supported rate setting for the selected +# hw_mode (i.e., this line can be removed from configuration file in most +# cases) +#supported_rates=10 20 55 110 60 90 120 180 240 360 480 540 + +# Basic rate set configuration +# List of rates (in 100 kbps) that are included in the basic rate set. +# If this item is not included, usually reasonable default set is used. +#basic_rates=10 20 +#basic_rates=10 20 55 110 +#basic_rates=60 120 240 + +# Short Preamble +# This parameter can be used to enable optional use of short preamble for +# frames sent at 2 Mbps, 5.5 Mbps, and 11 Mbps to improve network performance. +# This applies only to IEEE 802.11b-compatible networks and this should only be +# enabled if the local hardware supports use of short preamble. If any of the +# associated STAs do not support short preamble, use of short preamble will be +# disabled (and enabled when such STAs disassociate) dynamically. +# 0 = do not allow use of short preamble (default) +# 1 = allow use of short preamble +#preamble=1 + +# Station MAC address -based authentication +# Please note that this kind of access control requires a driver that uses +# hostapd to take care of management frame processing and as such, this can be +# used with driver=hostap or driver=nl80211, but not with driver=madwifi. +# 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 + +# Send empty SSID in beacons and ignore probe request frames that do not +# specify full SSID, i.e., require stations to know SSID. +# default: disabled (0) +# 1 = send empty (length=0) SSID in beacon and ignore probe request for +# broadcast SSID +# 2 = clear SSID (ASCII 0), but keep the original length (this may be required +# with some clients that do not support empty SSID) and ignore probe +# requests for broadcast SSID +ignore_broadcast_ssid=0 + +# TX queue parameters (EDCF / bursting) +# default for all these fields: not set, use hardware defaults +# tx_queue_<queue name>_<param> +# queues: data0, data1, data2, data3, after_beacon, beacon +# (data0 is the highest priority queue) +# parameters: +# aifs: AIFS (default 2) +# cwmin: cwMin (1, 3, 7, 15, 31, 63, 127, 255, 511, 1023) +# cwmax: cwMax (1, 3, 7, 15, 31, 63, 127, 255, 511, 1023); cwMax >= cwMin +# burst: maximum length (in milliseconds with precision of up to 0.1 ms) for +# bursting +# +# Default WMM parameters (IEEE 802.11 draft; 11-03-0504-03-000e): +# These parameters are used by the access point when transmitting frames +# to the clients. +# +# Low priority / AC_BK = background +#tx_queue_data3_aifs=7 +#tx_queue_data3_cwmin=15 +#tx_queue_data3_cwmax=1023 +#tx_queue_data3_burst=0 +# Note: for IEEE 802.11b mode: cWmin=31 cWmax=1023 burst=0 +# +# Normal priority / AC_BE = best effort +#tx_queue_data2_aifs=3 +#tx_queue_data2_cwmin=15 +#tx_queue_data2_cwmax=63 +#tx_queue_data2_burst=0 +# Note: for IEEE 802.11b mode: cWmin=31 cWmax=127 burst=0 +# +# High priority / AC_VI = video +#tx_queue_data1_aifs=1 +#tx_queue_data1_cwmin=7 +#tx_queue_data1_cwmax=15 +#tx_queue_data1_burst=3.0 +# Note: for IEEE 802.11b mode: cWmin=15 cWmax=31 burst=6.0 +# +# Highest priority / AC_VO = voice +#tx_queue_data0_aifs=1 +#tx_queue_data0_cwmin=3 +#tx_queue_data0_cwmax=7 +#tx_queue_data0_burst=1.5 +# Note: for IEEE 802.11b mode: cWmin=7 cWmax=15 burst=3.3 +# +# Special queues; normally not user configurable +# +#tx_queue_after_beacon_aifs=2 +#tx_queue_after_beacon_cwmin=15 +#tx_queue_after_beacon_cwmax=1023 +#tx_queue_after_beacon_burst=0 +# +#tx_queue_beacon_aifs=2 +#tx_queue_beacon_cwmin=3 +#tx_queue_beacon_cwmax=7 +#tx_queue_beacon_burst=1.5 + +# 802.1D Tag to AC mappings +# WMM specifies following mapping of data frames to different ACs. This mapping +# can be configured using Linux QoS/tc and sch_pktpri.o module. +# 802.1D Tag 802.1D Designation Access Category WMM Designation +# 1 BK AC_BK Background +# 2 - AC_BK Background +# 0 BE AC_BE Best Effort +# 3 EE AC_VI Video +# 4 CL AC_VI Video +# 5 VI AC_VI Video +# 6 VO AC_VO Voice +# 7 NC AC_VO Voice +# Data frames with no priority information: AC_BE +# Management frames: AC_VO +# PS-Poll frames: AC_BE + +# Default WMM parameters (IEEE 802.11 draft; 11-03-0504-03-000e): +# for 802.11a or 802.11g networks +# These parameters are sent to WMM clients when they associate. +# The parameters will be used by WMM clients for frames transmitted to the +# access point. +# +# note - txop_limit is in units of 32microseconds +# note - acm is admission control mandatory flag. 0 = admission control not +# required, 1 = mandatory +# note - here cwMin and cmMax are in exponent form. the actual cw value used +# will be (2^n)-1 where n is the value given here +# +wme_enabled=1 +# +# Low priority / AC_BK = background +wme_ac_bk_cwmin=4 +wme_ac_bk_cwmax=10 +wme_ac_bk_aifs=7 +wme_ac_bk_txop_limit=0 +wme_ac_bk_acm=0 +# Note: for IEEE 802.11b mode: cWmin=5 cWmax=10 +# +# Normal priority / AC_BE = best effort +wme_ac_be_aifs=3 +wme_ac_be_cwmin=4 +wme_ac_be_cwmax=10 +wme_ac_be_txop_limit=0 +wme_ac_be_acm=0 +# Note: for IEEE 802.11b mode: cWmin=5 cWmax=7 +# +# High priority / AC_VI = video +wme_ac_vi_aifs=2 +wme_ac_vi_cwmin=3 +wme_ac_vi_cwmax=4 +wme_ac_vi_txop_limit=94 +wme_ac_vi_acm=0 +# Note: for IEEE 802.11b mode: cWmin=4 cWmax=5 txop_limit=188 +# +# Highest priority / AC_VO = voice +wme_ac_vo_aifs=2 +wme_ac_vo_cwmin=2 +wme_ac_vo_cwmax=3 +wme_ac_vo_txop_limit=47 +wme_ac_vo_acm=0 +# Note: for IEEE 802.11b mode: cWmin=3 cWmax=4 burst=102 + +# Static WEP key configuration +# +# The key number to use when transmitting. +# It must be between 0 and 3, and the corresponding key must be set. +# default: not set +#wep_default_key=0 +# The WEP keys to use. +# A key may be a quoted string or unquoted hexadecimal digits. +# The key length should be 5, 13, or 16 characters, or 10, 26, or 32 +# digits, depending on whether 40-bit (64-bit), 104-bit (128-bit), or +# 128-bit (152-bit) WEP is used. +# Only the default key must be supplied; the others are optional. +# default: not set +#wep_key0=123456789a +#wep_key1="vwxyz" +#wep_key2=0102030405060708090a0b0c0d +#wep_key3=".2.4.6.8.0.23" + +# Station inactivity limit +# +# If a station does not send anything in ap_max_inactivity seconds, an +# empty data frame is sent to it in order to verify whether it is +# still in range. If this frame is not ACKed, the station will be +# disassociated and then deauthenticated. This feature is used to +# clear station table of old entries when the STAs move out of the +# range. +# +# The station can associate again with the AP if it is still in range; +# this inactivity poll is just used as a nicer way of verifying +# inactivity; i.e., client will not report broken connection because +# disassociation frame is not sent immediately without first polling +# the STA with a data frame. +# default: 300 (i.e., 5 minutes) +#ap_max_inactivity=300 + +# Enable/disable internal bridge for packets between associated stations. +# +# When IEEE 802.11 is used in managed mode, packets are usually send through +# the AP even if they are from a wireless station to another wireless station. +# This functionality requires that the AP has a bridge functionality that sends +# frames back to the same interface if their destination is another associated +# station. In addition, broadcast/multicast frames from wireless stations will +# be sent both to the host system net stack (e.g., to eventually wired network) +# and back to the wireless interface. +# +# The internal bridge is implemented within the wireless kernel module and it +# bypasses kernel filtering (netfilter/iptables/ebtables). If direct +# communication between the stations needs to be prevented, the internal +# bridge can be disabled by setting bridge_packets=0. +# +# Note: If this variable is not included in hostapd.conf, hostapd does not +# change the configuration and iwpriv can be used to set the value with +# 'iwpriv wlan# param 10 0' command. If the variable is in hostapd.conf, +# hostapd will override possible iwpriv configuration whenever configuration +# file is reloaded. +# +# default: do not control from hostapd (80211.o defaults to 1=enabled) +#bridge_packets=1 + +# Maximum allowed Listen Interval (how many Beacon periods STAs are allowed to +# remain asleep). Default: 65535 (no limit apart from field size) +#max_listen_interval=100 + +##### IEEE 802.11n related configuration ###################################### + +# ieee80211n: Whether IEEE 802.11n (HT) is enabled +# 0 = disabled (default) +# 1 = enabled +#ieee80211n=1 + +# ht_capab: HT capabilities (list of flags) +# LDPC coding capability: [LDPC] = supported +# Supported channel width set: [HT40-] = both 20 MHz and 40 MHz with secondary +# channel below the primary channel; [HT40+] = both 20 MHz and 40 MHz +# with secondary channel below the primary channel +# (20 MHz only if neither is set) +# Note: There are limits on which channels can be used with HT40- and +# HT40+. Following table shows the channels that may be available for +# HT40- and HT40+ use per IEEE 802.11n Annex J: +# freq HT40- HT40+ +# 2.4 GHz 5-13 1-7 (1-9 in Europe/Japan) +# 5 GHz 40,48,56,64 36,44,52,60 +# (depending on the location, not all of these channels may be available +# for use) +# Spatial Multiplexing (SM) Power Save: [SMPS-STATIC] or [SMPS-DYNAMIC] +# (SMPS disabled if neither is set) +# HT-greenfield: [GF] (disabled if not set) +# Short GI for 20 MHz: [SHORT-GI-20] (disabled if not set) +# Short GI for 40 MHz: [SHORT-GI-40] (disabled if not set) +# Tx STBC: [TX-STBC] (disabled if not set) +# Rx STBC: [RX-STBC1] (one spatial stream), [RX-STBC12] (one or two spatial +# streams), or [RX-STBC123] (one, two, or three spatial streams); Rx STBC +# disabled if none of these set +# HT-delayed Block Ack: [DELAYED-BA] (disabled if not set) +# Maximum A-MSDU length: [MAX-AMSDU-7935] for 7935 octets (3839 octets if not +# set) +# DSSS/CCK Mode in 40 MHz: [DSSS_CCK-40] = allowed (not allowed if not set) +# PSMP support: [PSMP] (disabled if not set) +# L-SIG TXOP protection support: [LSIG-TXOP-PROT] (disabled if not set) +#ht_capab=[HT40-][SHORT-GI-20][SHORT-GI-40] + +##### 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., RFC 4284. +#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 + +# dh_file: File path to DH/DSA parameters file (in PEM format) +# This is an optional configuration file for setting parameters for an +# ephemeral DH key exchange. In most cases, the default RSA authentication does +# not use this configuration. However, it is possible setup RSA to use +# ephemeral DH key exchange. In addition, ciphers with DSA keys always use +# ephemeral DH keys. This can be used to achieve forward secrecy. If the file +# is in DSA parameters format, it will be automatically converted into DH +# params. This parameter is required if anonymous EAP-FAST is used. +# You can generate DH parameters file with OpenSSL, e.g., +# "openssl dhparam -out /etc/hostapd.dh.pem 1024" +#dh_file=/etc/hostapd.dh.pem + +# 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 UNIX domain socket name for +# the HLR/AuC gateway (e.g., hlr_auc_gw). In this case, the path uses "unix:" +# prefix. +#eap_sim_db=unix:/tmp/hlr_auc_gw.sock + +# Encryption key for EAP-FAST PAC-Opaque values. This key must be a secret, +# random value. It is configured as a 16-octet value in hex format. It can be +# generated, e.g., with the following command: +# od -tx1 -v -N16 /dev/random | colrm 1 8 | tr -d ' ' +#pac_opaque_encr_key=000102030405060708090a0b0c0d0e0f + +# EAP-FAST authority identity (A-ID) +# A-ID indicates the identity of the authority that issues PACs. The A-ID +# should be unique across all issuing servers. In theory, this is a variable +# length field, but due to some existing implementations required A-ID to be +# 16 octets in length, it is strongly recommended to use that length for the +# field to provided interoperability with deployed peer implementation. This +# field is configured in hex format. +#eap_fast_a_id=101112131415161718191a1b1c1d1e1f + +# EAP-FAST authority identifier information (A-ID-Info) +# This is a user-friendly name for the A-ID. For example, the enterprise name +# and server name in a human-readable format. This field is encoded as UTF-8. +#eap_fast_a_id_info=test server + +# Enable/disable different EAP-FAST provisioning modes: +#0 = provisioning disabled +#1 = only anonymous provisioning allowed +#2 = only authenticated provisioning allowed +#3 = both provisioning modes allowed (default) +#eap_fast_prov=3 + +# EAP-FAST PAC-Key lifetime in seconds (hard limit) +#pac_key_lifetime=604800 + +# EAP-FAST PAC-Key refresh time in seconds (soft limit on remaining hard +# limit). The server will generate a new PAC-Key when this number of seconds +# (or fewer) of the lifetime remains. +#pac_key_refresh_time=86400 + +# EAP-SIM and EAP-AKA protected success/failure indication using AT_RESULT_IND +# (default: 0 = disabled). +#eap_sim_aka_result_ind=1 + +# Trusted Network Connect (TNC) +# If enabled, TNC validation will be required before the peer is allowed to +# connect. Note: This is only used with EAP-TTLS and EAP-FAST. If any other +# EAP method is enabled, the peer will be allowed to connect without TNC. +#tnc=1 + + +##### 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. +# When using IEEE 802.11r, nas_identifier must be set and must be between 1 and +# 48 octets long. +#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 + +# Dynamic VLAN mode; allow RADIUS authentication server to decide which VLAN +# is used for the stations. This information is parsed from following RADIUS +# attributes based on RFC 3580 and RFC 2868: Tunnel-Type (value 13 = VLAN), +# Tunnel-Medium-Type (value 6 = IEEE 802), Tunnel-Private-Group-ID (value +# VLANID as a string). vlan_file option below must be configured if dynamic +# VLANs are used. Optionally, the local MAC ACL list (accept_mac_file) can be +# used to set static client MAC address to VLAN ID mapping. +# 0 = disabled (default) +# 1 = option; use default interface if RADIUS server does not include VLAN ID +# 2 = required; reject authentication if RADIUS server does not include VLAN ID +#dynamic_vlan=0 + +# VLAN interface list for dynamic VLAN mode is read from a separate text file. +# This list is used to map VLAN ID from the RADIUS server to a network +# interface. Each station is bound to one interface in the same way as with +# multiple BSSIDs or SSIDs. Each line in this text file is defining a new +# interface and the line must include VLAN ID and interface name separated by +# white space (space or tab). +#vlan_file=/etc/hostapd.vlan + +# Interface where 802.1q tagged packets should appear when a RADIUS server is +# used to determine which VLAN a station is on. hostapd creates a bridge for +# each VLAN. Then hostapd adds a VLAN interface (associated with the interface +# indicated by 'vlan_tagged_interface') and the appropriate wireless interface +# to the bridge. +#vlan_tagged_interface=eth0 + + +##### RADIUS authentication server configuration ############################## + +# hostapd can be used as a RADIUS authentication server for other hosts. This +# requires that the integrated EAP server 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. WPA-PSK-SHA256 and WPA-EAP-SHA256 can be +# added to enable SHA256-based stronger algorithms. +# (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) +# Pairwise cipher for WPA (v1) (default: TKIP) +#wpa_pairwise=TKIP CCMP +# Pairwise cipher for RSN/WPA2 (default: use wpa_pairwise value) +#rsn_pairwise=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 + +# Maximum lifetime for PTK in seconds. This can be used to enforce rekeying of +# PTK to mitigate some attacks against TKIP deficiencies. +#wpa_ptk_rekey=600 + +# 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 + +# peerkey: Whether PeerKey negotiation for direct links (IEEE 802.11e) is +# allowed. This is only used with RSN/WPA2. +# 0 = disabled (default) +# 1 = enabled +#peerkey=1 + +# ieee80211w: Whether management frame protection (MFP) is enabled +# 0 = disabled (default) +# 1 = optional +# 2 = required +#ieee80211w=0 + +# Association SA Query maximum timeout (in TU = 1.024 ms; for MFP) +# (maximum time to wait for a SA Query response) +# dot11AssociationSAQueryMaximumTimeout, 1...4294967295 +#assoc_sa_query_max_timeout=1000 + +# Association SA Query retry timeout (in TU = 1.024 ms; for MFP) +# (time between two subsequent SA Query requests) +# dot11AssociationSAQueryRetryTimeout, 1...4294967295 +#assoc_sa_query_retry_timeout=201 + + +# okc: Opportunistic Key Caching (aka Proactive Key Caching) +# Allow PMK cache to be shared opportunistically among configured interfaces +# and BSSes (i.e., all configurations within a single hostapd process). +# 0 = disabled (default) +# 1 = enabled +#okc=1 + + +##### IEEE 802.11r configuration ############################################## + +# Mobility Domain identifier (dot11FTMobilityDomainID, MDID) +# MDID is used to indicate a group of APs (within an ESS, i.e., sharing the +# same SSID) between which a STA can use Fast BSS Transition. +# 2-octet identifier as a hex string. +#mobility_domain=a1b2 + +# PMK-R0 Key Holder identifier (dot11FTR0KeyHolderID) +# 1 to 48 octet identifier. +# This is configured with nas_identifier (see RADIUS client section above). + +# Default lifetime of the PMK-RO in minutes; range 1..65535 +# (dot11FTR0KeyLifetime) +#r0_key_lifetime=10000 + +# PMK-R1 Key Holder identifier (dot11FTR1KeyHolderID) +# 6-octet identifier as a hex string. +#r1_key_holder=000102030405 + +# Reassociation deadline in time units (TUs / 1.024 ms; range 1000..65535) +# (dot11FTReassociationDeadline) +#reassociation_deadline=1000 + +# List of R0KHs in the same Mobility Domain +# format: <MAC address> <NAS Identifier> <128-bit key as hex string> +# This list is used to map R0KH-ID (NAS Identifier) to a destination MAC +# address when requesting PMK-R1 key from the R0KH that the STA used during the +# Initial Mobility Domain Association. +#r0kh=02:01:02:03:04:05 r0kh-1.example.com 000102030405060708090a0b0c0d0e0f +#r0kh=02:01:02:03:04:06 r0kh-2.example.com 00112233445566778899aabbccddeeff +# And so on.. One line per R0KH. + +# List of R1KHs in the same Mobility Domain +# format: <MAC address> <R0KH-ID> <128-bit key as hex string> +# This list is used to map R1KH-ID to a destination MAC address when sending +# PMK-R1 key from the R0KH. This is also the list of authorized R1KHs in the MD +# that can request PMK-R1 keys. +#r1kh=02:01:02:03:04:05 02:11:22:33:44:55 000102030405060708090a0b0c0d0e0f +#r1kh=02:01:02:03:04:06 02:11:22:33:44:66 00112233445566778899aabbccddeeff +# And so on.. One line per R1KH. + +# Whether PMK-R1 push is enabled at R0KH +# 0 = do not push PMK-R1 to all configured R1KHs (default) +# 1 = push PMK-R1 to all configured R1KHs whenever a new PMK-R0 is derived +#pmk_r1_push=1 + +##### Passive scanning ######################################################## +# Scan different channels every N seconds. 0 = disable passive scanning. +#passive_scan_interval=60 + +# Listen N usecs on each channel when doing passive scanning. +# This value plus the time needed for changing channels should be less than +# 32 milliseconds (i.e. 32000 usec) to avoid interruptions to normal +# operations. Time needed for channel changing varies based on the used wlan +# hardware. +# default: disabled (0) +#passive_scan_listen=10000 + +# Passive scanning mode: +# 0 = scan all supported modes (802.11a/b/g/Turbo) (default) +# 1 = scan only the mode that is currently used for normal operations +#passive_scan_mode=1 + +# Maximum number of entries kept in AP table (either for passive scanning or +# for detecting Overlapping Legacy BSS Condition). The oldest entry will be +# removed when adding a new entry that would make the list grow over this +# limit. Note! Wi-Fi certification for IEEE 802.11g requires that OLBC is +# enabled, so this field should not be set to 0 when using IEEE 802.11g. +# default: 255 +#ap_table_max_size=255 + +# Number of seconds of no frames received after which entries may be deleted +# from the AP table. Since passive scanning is not usually performed frequently +# this should not be set to very small value. In addition, there is no +# guarantee that every scan cycle will receive beacon frames from the +# neighboring APs. +# default: 60 +#ap_table_expiration_time=3600 + + +##### Wi-Fi Protected Setup (WPS) ############################################# + +# WPS state +# 0 = WPS disabled (default) +# 1 = WPS enabled, not configured +# 2 = WPS enabled, configured +#wps_state=2 + +# AP can be configured into a locked state where new WPS Registrar are not +# accepted, but previously authorized Registrars (including the internal one) +# can continue to add new Enrollees. +#ap_setup_locked=1 + +# Universally Unique IDentifier (UUID; see RFC 4122) of the device +# This value is used as the UUID for the internal WPS Registrar. If the AP +# is also using UPnP, this value should be set to the device's UPnP UUID. +# If not configured, UUID will be generated based on the local MAC address. +#uuid=12345678-9abc-def0-1234-56789abcdef0 + +# Note: If wpa_psk_file is set, WPS is used to generate random, per-device PSKs +# that will be appended to the wpa_psk_file. If wpa_psk_file is not set, the +# default PSK (wpa_psk/wpa_passphrase) will be delivered to Enrollees. Use of +# per-device PSKs is recommended as the more secure option (i.e., make sure to +# set wpa_psk_file when using WPS with WPA-PSK). + +# When an Enrollee requests access to the network with PIN method, the Enrollee +# PIN will need to be entered for the Registrar. PIN request notifications are +# sent to hostapd ctrl_iface monitor. In addition, they can be written to a +# text file that could be used, e.g., to populate the AP administration UI with +# pending PIN requests. If the following variable is set, the PIN requests will +# be written to the configured file. +#wps_pin_requests=/var/run/hostapd_wps_pin_requests + +# Device Name +# User-friendly description of device; up to 32 octets encoded in UTF-8 +#device_name=Wireless AP + +# Manufacturer +# The manufacturer of the device (up to 64 ASCII characters) +#manufacturer=Company + +# Model Name +# Model of the device (up to 32 ASCII characters) +#model_name=WAP + +# Model Number +# Additional device description (up to 32 ASCII characters) +#model_number=123 + +# Serial Number +# Serial number of the device (up to 32 characters) +#serial_number=12345 + +# Primary Device Type +# Used format: <categ>-<OUI>-<subcateg> +# categ = Category as an integer value +# OUI = OUI and type octet as a 4-octet hex-encoded value; 0050F204 for +# default WPS OUI +# subcateg = OUI-specific Sub Category as an integer value +# Examples: +# 1-0050F204-1 (Computer / PC) +# 1-0050F204-2 (Computer / Server) +# 5-0050F204-1 (Storage / NAS) +# 6-0050F204-1 (Network Infrastructure / AP) +#device_type=6-0050F204-1 + +# OS Version +# 4-octet operating system version number (hex string) +#os_version=01020300 + +# Config Methods +# List of the supported configuration methods +#config_methods=label display push_button keypad + +# Access point PIN for initial configuration and adding Registrars +# If not set, hostapd will not allow external WPS Registrars to control the +# access point. +#ap_pin=12345670 + +# Skip building of automatic WPS credential +# This can be used to allow the automatically generated Credential attribute to +# be replaced with pre-configured Credential(s). +#skip_cred_build=1 + +# Additional Credential attribute(s) +# This option can be used to add pre-configured Credential attributes into M8 +# message when acting as a Registrar. If skip_cred_build=1, this data will also +# be able to override the Credential attribute that would have otherwise been +# automatically generated based on network configuration. This configuration +# option points to an external file that much contain the WPS Credential +# attribute(s) as binary data. +#extra_cred=hostapd.cred + +# Credential processing +# 0 = process received credentials internally (default) +# 1 = do not process received credentials; just pass them over ctrl_iface to +# external program(s) +# 2 = process received credentials internally and pass them over ctrl_iface +# to external program(s) +# Note: With wps_cred_processing=1, skip_cred_build should be set to 1 and +# extra_cred be used to provide the Credential data for Enrollees. +# +# wps_cred_processing=1 will disabled automatic updates of hostapd.conf file +# both for Credential processing and for marking AP Setup Locked based on +# validation failures of AP PIN. An external program is responsible on updating +# the configuration appropriately in this case. +#wps_cred_processing=0 + +# AP Settings Attributes for M7 +# By default, hostapd generates the AP Settings Attributes for M7 based on the +# current configuration. It is possible to override this by providing a file +# with pre-configured attributes. This is similar to extra_cred file format, +# but the AP Settings attributes are not encapsulated in a Credential +# attribute. +#ap_settings=hostapd.ap_settings + +# WPS UPnP interface +# If set, support for external Registrars is enabled. +#upnp_iface=br0 + +# Friendly Name (required for UPnP) +# Short description for end use. Should be less than 64 characters. +#friendly_name=WPS Access Point + +# Manufacturer URL (optional for UPnP) +#manufacturer_url=http://www.example.com/ + +# Model Description (recommended for UPnP) +# Long description for end user. Should be less than 128 characters. +#model_description=Wireless Access Point + +# Model URL (optional for UPnP) +#model_url=http://www.example.com/model/ + +# Universal Product Code (optional for UPnP) +# 12-digit, all-numeric code that identifies the consumer package. +#upc=123456789012 + +##### Multiple BSSID support ################################################## +# +# Above configuration is using the default interface (wlan#, or multi-SSID VLAN +# interfaces). Other BSSIDs can be added by using separator 'bss' with +# default interface name to be allocated for the data packets of the new BSS. +# +# hostapd will generate BSSID mask based on the BSSIDs that are +# configured. hostapd will verify that dev_addr & MASK == dev_addr. If this is +# not the case, the MAC address of the radio must be changed before starting +# hostapd (ifconfig wlan0 hw ether <MAC addr>). +# +# BSSIDs are assigned in order to each BSS, unless an explicit BSSID is +# specified using the 'bssid' parameter. +# If an explicit BSSID is specified, it must be chosen such that it: +# - results in a valid MASK that covers it and the dev_addr +# - is not the same as the MAC address of the radio +# - is not the same as any other explicitly specified BSSID +# +# Please note that hostapd uses some of the values configured for the first BSS +# as the defaults for the following BSSes. However, it is recommended that all +# BSSes include explicit configuration of all relevant configuration items. +# +#bss=wlan0_0 +#ssid=test2 +# most of the above items can be used here (apart from radio interface specific +# items, like channel) + +#bss=wlan0_1 +#bssid=00:13:10:95:fe:0b +# ... diff --git a/contrib/wpa/hostapd/hostapd.deny b/contrib/wpa/hostapd/hostapd.deny new file mode 100644 index 0000000..1616678 --- /dev/null +++ b/contrib/wpa/hostapd/hostapd.deny @@ -0,0 +1,5 @@ +# List of MAC addresses that are not allowed to authenticate (IEEE 802.11) +# with the AP. +00:20:30:40:50:60 +00:ab:cd:ef:12:34 +00:00:30:40:50:60 diff --git a/contrib/wpa/hostapd/hostapd.eap_user b/contrib/wpa/hostapd/hostapd.eap_user new file mode 100644 index 0000000..ac9a5d8 --- /dev/null +++ b/contrib/wpa/hostapd/hostapd.eap_user @@ -0,0 +1,91 @@ +# hostapd user database for integrated EAP server + +# 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"). Password can alternatively be stored as +# NtPasswordHash (16-byte MD4 hash of the unicode presentation of the password +# in unicode) if it is used for MSCHAP or MSCHAPv2 authentication. This means +# that the plaintext password does not need to be included in the user file. +# Password hash is stored as hash:<16-octets of hex data> without quotation +# marks. + +# [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, EAP-FAST, EAP-SIM, and EAP-AKA do not use +# password option. +# EAP-MD5, EAP-MSCHAPV2, EAP-GTC, EAP-PAX, EAP-PSK, and EAP-SAKE require a +# password. +# EAP-PEAP, EAP-TTLS, and EAP-FAST 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. +# +# "prefix"* can be used to match the given prefix and anything after this. The +# main purpose for this is to be able to avoid EAP method negotiation when the +# method is using known prefix in identities (e.g., EAP-SIM and EAP-AKA). This +# is only allowed for phase 1 identities. +# +# 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. +# +# EAP-TTLS supports both EAP and non-EAP authentication inside the tunnel. +# Tunneled EAP methods are configured with standard EAP method name and [2] +# flag. Non-EAP methods can be enabled by following method names: TTLS-PAP, +# TTLS-CHAP, TTLS-MSCHAP, TTLS-MSCHAPV2. TTLS-PAP and TTLS-CHAP require a +# plaintext password while TTLS-MSCHAP and TTLS-MSCHAPV2 can use NT password +# hash. + +# 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 +"sake.user@example.com" SAKE 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef +"ttls" TTLS +"not anonymous" PEAP +# Default to EAP-SIM and EAP-AKA based on fixed identity prefixes +"0"* AKA,TTLS,TLS,PEAP,SIM +"1"* SIM,TTLS,TLS,PEAP,AKA +"2"* AKA,TTLS,TLS,PEAP,SIM +"3"* SIM,TTLS,TLS,PEAP,AKA +"4"* AKA,TTLS,TLS,PEAP,SIM +"5"* SIM,TTLS,TLS,PEAP,AKA + +# Wildcard for all other identities +* PEAP,TTLS,TLS,SIM,AKA + +# 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] +"test user" MSCHAPV2 hash:000102030405060708090a0b0c0d0e0f [2] +"ttls-user" TTLS-PAP,TTLS-CHAP,TTLS-MSCHAP,TTLS-MSCHAPV2 "password" [2] + +# Default to EAP-SIM and EAP-AKA based on fixed identity prefixes in phase 2 +"0"* AKA [2] +"1"* SIM [2] +"2"* AKA [2] +"3"* SIM [2] +"4"* AKA [2] +"5"* SIM [2] diff --git a/contrib/wpa/hostapd/hostapd.h b/contrib/wpa/hostapd/hostapd.h new file mode 100644 index 0000000..26f30d7 --- /dev/null +++ b/contrib/wpa/hostapd/hostapd.h @@ -0,0 +1,238 @@ +/* + * hostapd / Initialization and configuration + * Host AP kernel driver + * Copyright (c) 2002-2008, Jouni Malinen <j@w1.fi> + * Copyright (c) 2007-2008, Intel Corporation + * + * 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 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 ETH_P_EAPOL +#define ETH_P_EAPOL ETH_P_PAE +#endif /* ETH_P_EAPOL */ + +#ifndef ETH_P_RRB +#define ETH_P_RRB 0x890D +#endif /* ETH_P_RRB */ + +#include "config.h" + +#ifdef _MSC_VER +#pragma pack(push, 1) +#endif /* _MSC_VER */ + +#define MAX_VLAN_ID 4094 + +struct ieee8023_hdr { + u8 dest[6]; + u8 src[6]; + u16 ethertype; +} STRUCT_PACKED; + + +struct ieee80211_hdr { + le16 frame_control; + le16 duration_id; + u8 addr1[6]; + u8 addr2[6]; + u8 addr3[6]; + le16 seq_ctrl; + /* followed by 'u8 addr4[6];' if ToDS and FromDS is set in data frame + */ +} STRUCT_PACKED; + +#ifdef _MSC_VER +#pragma pack(pop) +#endif /* _MSC_VER */ + +#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]; + +struct hostap_sta_driver_data { + unsigned long rx_packets, tx_packets, rx_bytes, tx_bytes; + unsigned long current_tx_rate; + unsigned long inactive_msec; + unsigned long flags; + unsigned long num_ps_buf_frames; + unsigned long tx_retry_failed; + unsigned long tx_retry_count; + int last_rssi; + int last_ack_rssi; +}; + +struct wpa_driver_ops; +struct wpa_ctrl_dst; +struct radius_server_data; +struct upnp_wps_device_sm; + +#ifdef CONFIG_FULL_DYNAMIC_VLAN +struct full_dynamic_vlan; +#endif /* CONFIG_FULL_DYNAMIC_VLAN */ + +/** + * struct hostapd_data - hostapd per-BSS data structure + */ +struct hostapd_data { + struct hostapd_iface *iface; + struct hostapd_config *iconf; + struct hostapd_bss_config *conf; + int interface_added; /* virtual interface added for this BSS */ + + u8 own_addr[ETH_ALEN]; + + 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]; + + const struct wpa_driver_ops *driver; + void *drv_priv; + + u8 *default_wep_key; + u8 default_wep_key_idx; + + struct radius_client_data *radius; + int radius_client_reconfigured; + u32 acct_session_id_hi, acct_session_id_lo; + + struct iapp_data *iapp; + + struct hostapd_cached_radius_acl *acl_cache; + struct hostapd_acl_query_data *acl_queries; + + struct wpa_authenticator *wpa_auth; + struct eapol_authenticator *eapol_auth; + + 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; + + int parameter_set_count; + +#ifdef CONFIG_FULL_DYNAMIC_VLAN + struct full_dynamic_vlan *full_dynamic_vlan; +#endif /* CONFIG_FULL_DYNAMIC_VLAN */ + + struct l2_packet_data *l2; + struct wps_context *wps; + +#ifdef CONFIG_WPS + u8 *wps_beacon_ie; + size_t wps_beacon_ie_len; + u8 *wps_probe_resp_ie; + size_t wps_probe_resp_ie_len; + unsigned int ap_pin_failures; + struct upnp_wps_device_sm *wps_upnp; +#endif /* CONFIG_WPS */ +}; + + +/** + * struct hostapd_iface - hostapd per-interface data structure + */ +struct hostapd_iface { + char *config_fname; + struct hostapd_config *conf; + + size_t num_bss; + struct hostapd_data **bss; + + int num_ap; /* number of entries in ap_list */ + struct ap_info *ap_list; /* AP info list head */ + struct ap_info *ap_hash[STA_HASH_SIZE]; + struct ap_info *ap_iter_list; + + struct hostapd_hw_modes *hw_features; + int num_hw_features; + struct hostapd_hw_modes *current_mode; + /* Rates that are currently used (i.e., filtered copy of + * current_mode->channels */ + int num_rates; + struct hostapd_rate_data *current_rates; + + u16 hw_flags; + + /* Number of associated Non-ERP stations (i.e., stations using 802.11b + * in 802.11g BSS) */ + int num_sta_non_erp; + + /* Number of associated stations that do not support Short Slot Time */ + int num_sta_no_short_slot_time; + + /* Number of associated stations that do not support Short Preamble */ + int num_sta_no_short_preamble; + + int olbc; /* Overlapping Legacy BSS Condition */ + + /* Number of HT associated stations that do not support greenfield */ + int num_sta_ht_no_gf; + + /* Number of associated non-HT stations */ + int num_sta_no_ht; + + /* Number of HT associated stations 20 MHz */ + int num_sta_ht_20mhz; + + /* Overlapping BSS information */ + int olbc_ht; + +#ifdef CONFIG_IEEE80211N + u16 ht_op_mode; +#endif /* CONFIG_IEEE80211N */ +}; + +void hostapd_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta, + int reassoc); +int hostapd_reload_config(struct hostapd_iface *iface); + +#endif /* HOSTAPD_H */ diff --git a/contrib/wpa/hostapd/hostapd.radius_clients b/contrib/wpa/hostapd/hostapd.radius_clients new file mode 100644 index 0000000..3980427 --- /dev/null +++ b/contrib/wpa/hostapd/hostapd.radius_clients @@ -0,0 +1,4 @@ +# RADIUS client configuration for the RADIUS server +10.1.2.3 secret passphrase +192.168.1.0/24 another very secret passphrase +0.0.0.0/0 radius diff --git a/contrib/wpa/hostapd/hostapd.sim_db b/contrib/wpa/hostapd/hostapd.sim_db new file mode 100644 index 0000000..01c593d --- /dev/null +++ b/contrib/wpa/hostapd/hostapd.sim_db @@ -0,0 +1,9 @@ +# Example GSM authentication triplet file for EAP-SIM authenticator +# IMSI:Kc:SRES:RAND +# IMSI: ASCII string (numbers) +# Kc: hex, 8 octets +# SRES: hex, 4 octets +# RAND: hex, 16 octets +234567898765432:A0A1A2A3A4A5A6A7:D1D2D3D4:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +234567898765432:B0B1B2B3B4B5B6B7:E1E2E3E4:BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB +234567898765432:C0C1C2C3C4C5C6C7:F1F2F3F4:CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC diff --git a/contrib/wpa/hostapd/hostapd.vlan b/contrib/wpa/hostapd/hostapd.vlan new file mode 100644 index 0000000..98254fa --- /dev/null +++ b/contrib/wpa/hostapd/hostapd.vlan @@ -0,0 +1,9 @@ +# VLAN ID to network interface mapping +1 vlan1 +2 vlan2 +3 vlan3 +100 guest +# Optional wildcard entry matching all VLAN IDs. The first # in the interface +# name will be replaced with the VLAN ID. The network interfaces are created +# (and removed) dynamically based on the use. +* vlan# diff --git a/contrib/wpa/hostapd/hostapd.wpa_psk b/contrib/wpa/hostapd/hostapd.wpa_psk new file mode 100644 index 0000000..0a9499a --- /dev/null +++ b/contrib/wpa/hostapd/hostapd.wpa_psk @@ -0,0 +1,9 @@ +# List of WPA PSKs. Each line, except for empty lines and lines starting +# with #, must contain a MAC address and PSK separated with a space. +# Special MAC address 00:00:00:00:00:00 can be used to configure PSKs that +# anyone can use. PSK can be configured as an ASCII passphrase of 8..63 +# characters or as a 256-bit hex PSK (64 hex digits). +00:00:00:00:00:00 secret passphrase +00:11:22:33:44:55 another passphrase +00:22:33:44:55:66 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef +00:00:00:00:00:00 another passphrase for all STAs diff --git a/contrib/wpa/hostapd/hostapd_cli.1 b/contrib/wpa/hostapd/hostapd_cli.1 new file mode 100644 index 0000000..8d6587f --- /dev/null +++ b/contrib/wpa/hostapd/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<path>] [-i<ifname>] [-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> +Path to find control sockets. + +Default: /var/run/hostapd +.TP +.B \-i<ifname> +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 <addr> +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 <debug 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 <j@w1.fi>. +.PP +This manual page was written by Faidon Liambotis <faidon@cube.gr>, +for the Debian project (but may be used by others). diff --git a/contrib/wpa/hostapd/hostapd_cli.c b/contrib/wpa/hostapd/hostapd_cli.c new file mode 100644 index 0000000..2614113 --- /dev/null +++ b/contrib/wpa/hostapd/hostapd_cli.c @@ -0,0 +1,673 @@ +/* + * hostapd - command line interface for hostapd daemon + * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" +#include <dirent.h> + +#include "wpa_ctrl.h" +#include "common.h" +#include "version.h" + + +static const char *hostapd_cli_version = +"hostapd_cli v" VERSION_STR "\n" +"Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi> and contributors"; + + +static const char *hostapd_cli_license = +"This program is free software. You can distribute it and/or modify it\n" +"under the terms of the GNU General Public License version 2.\n" +"\n" +"Alternatively, this software may be distributed under the terms of the\n" +"BSD license. See README and COPYING for more details.\n"; + +static const char *hostapd_cli_full_license = +"This program is free software; you can redistribute it and/or modify\n" +"it under the terms of the GNU General Public License version 2 as\n" +"published by the Free Software Foundation.\n" +"\n" +"This program is distributed in the hope that it will be useful,\n" +"but WITHOUT ANY WARRANTY; without even the implied warranty of\n" +"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" +"GNU General Public License for more details.\n" +"\n" +"You should have received a copy of the GNU General Public License\n" +"along with this program; if not, write to the Free Software\n" +"Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA\n" +"\n" +"Alternatively, this software may be distributed under the terms of the\n" +"BSD license.\n" +"\n" +"Redistribution and use in source and binary forms, with or without\n" +"modification, are permitted provided that the following conditions are\n" +"met:\n" +"\n" +"1. Redistributions of source code must retain the above copyright\n" +" notice, this list of conditions and the following disclaimer.\n" +"\n" +"2. Redistributions in binary form must reproduce the above copyright\n" +" notice, this list of conditions and the following disclaimer in the\n" +" documentation and/or other materials provided with the distribution.\n" +"\n" +"3. Neither the name(s) of the above-listed copyright holder(s) nor the\n" +" names of its contributors may be used to endorse or promote products\n" +" derived from this software without specific prior written permission.\n" +"\n" +"THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n" +"\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n" +"LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n" +"A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n" +"OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n" +"SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n" +"LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n" +"DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n" +"THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n" +"(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n" +"OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n" +"\n"; + +static const char *commands_help = +"Commands:\n" +" mib get MIB variables (dot1x, dot11, radius)\n" +" sta <addr> get MIB variables for one station\n" +" all_sta get MIB variables for all stations\n" +" new_sta <addr> add a new station\n" +#ifdef CONFIG_IEEE80211W +" sa_query <addr> send SA Query to a station\n" +#endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_WPS +" wps_pin <uuid> <pin> add WPS Enrollee PIN (Device Password)\n" +" wps_pbc indicate button pushed to initiate PBC\n" +#endif /* CONFIG_WPS */ +" help show this usage help\n" +" interface [ifname] show interfaces/select interface\n" +" level <debug level> change debug level\n" +" license show full hostapd_cli license\n" +" quit exit hostapd_cli\n"; + +static struct 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 int ping_interval = 5; + + +static void usage(void) +{ + fprintf(stderr, "%s\n", hostapd_cli_version); + fprintf(stderr, + "\n" + "usage: hostapd_cli [-p<path>] [-i<ifname>] [-hv] " + "[-G<ping interval>] \\\n" + " [command..]\n" + "\n" + "Options:\n" + " -h help (show this usage text)\n" + " -v shown version information\n" + " -p<path> path to find control sockets (default: " + "/var/run/hostapd)\n" + " -i<ifname> 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 hostapd_cli_cmd_new_sta(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char buf[64]; + if (argc != 1) { + printf("Invalid 'new_sta' command - exactly one argument, STA " + "address, is required.\n"); + return -1; + } + snprintf(buf, sizeof(buf), "NEW_STA %s", argv[0]); + return wpa_ctrl_command(ctrl, buf); +} + + +#ifdef CONFIG_IEEE80211W +static int hostapd_cli_cmd_sa_query(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char buf[64]; + if (argc != 1) { + printf("Invalid 'sa_query' command - exactly one argument, " + "STA address, is required.\n"); + return -1; + } + snprintf(buf, sizeof(buf), "SA_QUERY %s", argv[0]); + return wpa_ctrl_command(ctrl, buf); +} +#endif /* CONFIG_IEEE80211W */ + + +#ifdef CONFIG_WPS +static int hostapd_cli_cmd_wps_pin(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char buf[64]; + if (argc != 2) { + printf("Invalid 'wps_pin' command - exactly two arguments, " + "UUID and PIN, are required.\n"); + return -1; + } + snprintf(buf, sizeof(buf), "WPS_PIN %s %s", argv[0], argv[1]); + return wpa_ctrl_command(ctrl, buf); +} + + +static int hostapd_cli_cmd_wps_pbc(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "WPS_PBC"); +} +#endif /* CONFIG_WPS */ + + +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'; + os_strlcpy(addr, buf, addr_len); + 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 }, + { "new_sta", hostapd_cli_cmd_new_sta }, +#ifdef CONFIG_IEEE80211W + { "sa_query", hostapd_cli_cmd_sa_query }, +#endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_WPS + { "wps_pin", hostapd_cli_cmd_wps_pin }, + { "wps_pbc", hostapd_cli_cmd_wps_pbc }, +#endif /* CONFIG_WPS */ + { "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(ping_interval); + 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(ping_interval); +} + + +int main(int argc, char *argv[]) +{ + int interactive; + int warning_displayed = 0; + int c; + + for (;;) { + c = getopt(argc, argv, "hG:i:p:v"); + if (c < 0) + break; + switch (c) { + case 'G': + ping_interval = atoi(optarg); + break; + case 'h': + usage(); + return 0; + case 'v': + printf("%s\n", hostapd_cli_version); + return 0; + case 'i': + free(ctrl_ifname); + 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/wpa/hostapd/hw_features.c b/contrib/wpa/hostapd/hw_features.c new file mode 100644 index 0000000..f1288e0 --- /dev/null +++ b/contrib/wpa/hostapd/hw_features.c @@ -0,0 +1,487 @@ +/* + * hostapd / Hardware feature query and different modes + * Copyright 2002-2003, Instant802 Networks, Inc. + * Copyright 2005-2006, Devicescape Software, Inc. + * Copyright (c) 2008, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "hostapd.h" +#include "hw_features.h" +#include "driver.h" +#include "config.h" + + +void hostapd_free_hw_features(struct hostapd_hw_modes *hw_features, + size_t num_hw_features) +{ + size_t i; + + if (hw_features == NULL) + return; + + for (i = 0; i < num_hw_features; i++) { + os_free(hw_features[i].channels); + os_free(hw_features[i].rates); + } + + os_free(hw_features); +} + + +int hostapd_get_hw_features(struct hostapd_iface *iface) +{ + struct hostapd_data *hapd = iface->bss[0]; + int ret = 0, i, j; + u16 num_modes, flags; + struct hostapd_hw_modes *modes; + + if (hostapd_drv_none(hapd)) + return -1; + modes = hostapd_get_hw_feature_data(hapd, &num_modes, &flags); + if (modes == NULL) { + hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "Fetching hardware channel/rate support not " + "supported."); + return -1; + } + + iface->hw_flags = flags; + + hostapd_free_hw_features(iface->hw_features, iface->num_hw_features); + iface->hw_features = modes; + iface->num_hw_features = num_modes; + + for (i = 0; i < num_modes; i++) { + struct hostapd_hw_modes *feature = &modes[i]; + /* set flag for channels we can use in current regulatory + * domain */ + for (j = 0; j < feature->num_channels; j++) { + /* + * Disable all channels that are marked not to allow + * IBSS operation or active scanning. In addition, + * disable all channels that require radar detection, + * since that (in addition to full DFS) is not yet + * supported. + */ + if (feature->channels[j].flag & + (HOSTAPD_CHAN_NO_IBSS | + HOSTAPD_CHAN_PASSIVE_SCAN | + HOSTAPD_CHAN_RADAR)) + feature->channels[j].flag |= + HOSTAPD_CHAN_DISABLED; + if (feature->channels[j].flag & HOSTAPD_CHAN_DISABLED) + continue; + wpa_printf(MSG_MSGDUMP, "Allowed channel: mode=%d " + "chan=%d freq=%d MHz max_tx_power=%d dBm", + feature->mode, + feature->channels[j].chan, + feature->channels[j].freq, + feature->channels[j].max_tx_power); + } + } + + return ret; +} + + +static int hostapd_prepare_rates(struct hostapd_data *hapd, + struct hostapd_hw_modes *mode) +{ + int i, num_basic_rates = 0; + int basic_rates_a[] = { 60, 120, 240, -1 }; + int basic_rates_b[] = { 10, 20, -1 }; + int basic_rates_g[] = { 10, 20, 55, 110, -1 }; + int *basic_rates; + + if (hapd->iconf->basic_rates) + basic_rates = hapd->iconf->basic_rates; + else switch (mode->mode) { + case HOSTAPD_MODE_IEEE80211A: + basic_rates = basic_rates_a; + break; + case HOSTAPD_MODE_IEEE80211B: + basic_rates = basic_rates_b; + break; + case HOSTAPD_MODE_IEEE80211G: + basic_rates = basic_rates_g; + break; + default: + return -1; + } + + if (hostapd_set_rate_sets(hapd, hapd->iconf->supported_rates, + basic_rates, mode->mode)) { + wpa_printf(MSG_ERROR, "Failed to update rate sets in kernel " + "module"); + } + + os_free(hapd->iface->current_rates); + hapd->iface->num_rates = 0; + + hapd->iface->current_rates = + os_malloc(mode->num_rates * sizeof(struct hostapd_rate_data)); + if (!hapd->iface->current_rates) { + wpa_printf(MSG_ERROR, "Failed to allocate memory for rate " + "table."); + return -1; + } + + for (i = 0; i < mode->num_rates; i++) { + struct hostapd_rate_data *rate; + + if (hapd->iconf->supported_rates && + !hostapd_rate_found(hapd->iconf->supported_rates, + mode->rates[i].rate)) + continue; + + rate = &hapd->iface->current_rates[hapd->iface->num_rates]; + os_memcpy(rate, &mode->rates[i], + sizeof(struct hostapd_rate_data)); + if (hostapd_rate_found(basic_rates, rate->rate)) { + rate->flags |= HOSTAPD_RATE_BASIC; + num_basic_rates++; + } else + rate->flags &= ~HOSTAPD_RATE_BASIC; + wpa_printf(MSG_DEBUG, "RATE[%d] rate=%d flags=0x%x", + hapd->iface->num_rates, rate->rate, rate->flags); + hapd->iface->num_rates++; + } + + if (hapd->iface->num_rates == 0 || num_basic_rates == 0) { + wpa_printf(MSG_ERROR, "No rates remaining in supported/basic " + "rate sets (%d,%d).", + hapd->iface->num_rates, num_basic_rates); + return -1; + } + + return 0; +} + + +#ifdef CONFIG_IEEE80211N +static int ieee80211n_allowed_ht40_channel_pair(struct hostapd_iface *iface) +{ + int sec_chan, ok, j, first; + int allowed[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 149, 157, + 184, 192 }; + size_t k; + + if (!iface->conf->secondary_channel) + return 1; /* HT40 not used */ + + sec_chan = iface->conf->channel + iface->conf->secondary_channel * 4; + wpa_printf(MSG_DEBUG, "HT40: control channel: %d " + "secondary channel: %d", + iface->conf->channel, sec_chan); + + /* Verify that HT40 secondary channel is an allowed 20 MHz + * channel */ + ok = 0; + for (j = 0; j < iface->current_mode->num_channels; j++) { + struct hostapd_channel_data *chan = + &iface->current_mode->channels[j]; + if (!(chan->flag & HOSTAPD_CHAN_DISABLED) && + chan->chan == sec_chan) { + ok = 1; + break; + } + } + if (!ok) { + wpa_printf(MSG_ERROR, "HT40 secondary channel %d not allowed", + sec_chan); + return 0; + } + + /* + * Verify that HT40 primary,secondary channel pair is allowed per + * IEEE 802.11n Annex J. This is only needed for 5 GHz band since + * 2.4 GHz rules allow all cases where the secondary channel fits into + * the list of allowed channels (already checked above). + */ + if (iface->current_mode->mode != HOSTAPD_MODE_IEEE80211A) + return 1; + + if (iface->conf->secondary_channel > 0) + first = iface->conf->channel; + else + first = sec_chan; + + ok = 0; + for (k = 0; k < sizeof(allowed) / sizeof(allowed[0]); k++) { + if (first == allowed[k]) { + ok = 1; + break; + } + } + if (!ok) { + wpa_printf(MSG_ERROR, "HT40 channel pair (%d, %d) not allowed", + iface->conf->channel, + iface->conf->secondary_channel); + return 0; + } + + return 1; +} + + +static int ieee80211n_supported_ht_capab(struct hostapd_iface *iface) +{ + u16 hw = iface->current_mode->ht_capab; + u16 conf = iface->conf->ht_capab; + + if (!iface->conf->ieee80211n) + return 1; + + if ((conf & HT_CAP_INFO_LDPC_CODING_CAP) && + !(hw & HT_CAP_INFO_LDPC_CODING_CAP)) { + wpa_printf(MSG_ERROR, "Driver does not support configured " + "HT capability [LDPC]"); + return 0; + } + + if ((conf & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET) && + !(hw & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)) { + wpa_printf(MSG_ERROR, "Driver does not support configured " + "HT capability [HT40*]"); + return 0; + } + + if ((conf & HT_CAP_INFO_SMPS_MASK) != (hw & HT_CAP_INFO_SMPS_MASK) && + (conf & HT_CAP_INFO_SMPS_MASK) != HT_CAP_INFO_SMPS_DISABLED) { + wpa_printf(MSG_ERROR, "Driver does not support configured " + "HT capability [SMPS-*]"); + return 0; + } + + if ((conf & HT_CAP_INFO_GREEN_FIELD) && + !(hw & HT_CAP_INFO_GREEN_FIELD)) { + wpa_printf(MSG_ERROR, "Driver does not support configured " + "HT capability [GF]"); + return 0; + } + + if ((conf & HT_CAP_INFO_SHORT_GI20MHZ) && + !(hw & HT_CAP_INFO_SHORT_GI20MHZ)) { + wpa_printf(MSG_ERROR, "Driver does not support configured " + "HT capability [SHORT-GI-20]"); + return 0; + } + + if ((conf & HT_CAP_INFO_SHORT_GI40MHZ) && + !(hw & HT_CAP_INFO_SHORT_GI40MHZ)) { + wpa_printf(MSG_ERROR, "Driver does not support configured " + "HT capability [SHORT-GI-40]"); + return 0; + } + + if ((conf & HT_CAP_INFO_TX_STBC) && !(hw & HT_CAP_INFO_TX_STBC)) { + wpa_printf(MSG_ERROR, "Driver does not support configured " + "HT capability [TX-STBC]"); + return 0; + } + + if ((conf & HT_CAP_INFO_RX_STBC_MASK) > + (hw & HT_CAP_INFO_RX_STBC_MASK)) { + wpa_printf(MSG_ERROR, "Driver does not support configured " + "HT capability [RX-STBC*]"); + return 0; + } + + if ((conf & HT_CAP_INFO_DELAYED_BA) && + !(hw & HT_CAP_INFO_DELAYED_BA)) { + wpa_printf(MSG_ERROR, "Driver does not support configured " + "HT capability [DELAYED-BA]"); + return 0; + } + + if ((conf & HT_CAP_INFO_MAX_AMSDU_SIZE) && + !(hw & HT_CAP_INFO_MAX_AMSDU_SIZE)) { + wpa_printf(MSG_ERROR, "Driver does not support configured " + "HT capability [MAX-AMSDU-7935]"); + return 0; + } + + if ((conf & HT_CAP_INFO_DSSS_CCK40MHZ) && + !(hw & HT_CAP_INFO_DSSS_CCK40MHZ)) { + wpa_printf(MSG_ERROR, "Driver does not support configured " + "HT capability [DSSS_CCK-40]"); + return 0; + } + + if ((conf & HT_CAP_INFO_PSMP_SUPP) && !(hw & HT_CAP_INFO_PSMP_SUPP)) { + wpa_printf(MSG_ERROR, "Driver does not support configured " + "HT capability [PSMP]"); + return 0; + } + + if ((conf & HT_CAP_INFO_LSIG_TXOP_PROTECT_SUPPORT) && + !(hw & HT_CAP_INFO_LSIG_TXOP_PROTECT_SUPPORT)) { + wpa_printf(MSG_ERROR, "Driver does not support configured " + "HT capability [LSIG-TXOP-PROT]"); + return 0; + } + + return 1; +} +#endif /* CONFIG_IEEE80211N */ + + +/** + * hostapd_select_hw_mode - Select the hardware mode + * @iface: Pointer to interface data. + * Returns: 0 on success, -1 on failure + * + * Sets up the hardware mode, channel, rates, and passive scanning + * based on the configuration. + */ +int hostapd_select_hw_mode(struct hostapd_iface *iface) +{ + int i, j, ok, ret; + + if (iface->num_hw_features < 1) + return -1; + + iface->current_mode = NULL; + for (i = 0; i < iface->num_hw_features; i++) { + struct hostapd_hw_modes *mode = &iface->hw_features[i]; + if (mode->mode == (int) iface->conf->hw_mode) { + iface->current_mode = mode; + break; + } + } + + if (iface->current_mode == NULL) { + wpa_printf(MSG_ERROR, "Hardware does not support configured " + "mode"); + hostapd_logger(iface->bss[0], NULL, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_WARNING, + "Hardware does not support configured mode " + "(%d)", (int) iface->conf->hw_mode); + return -1; + } + + ok = 0; + for (j = 0; j < iface->current_mode->num_channels; j++) { + struct hostapd_channel_data *chan = + &iface->current_mode->channels[j]; + if (!(chan->flag & HOSTAPD_CHAN_DISABLED) && + (chan->chan == iface->conf->channel)) { + ok = 1; + break; + } + } + if (ok == 0 && iface->conf->channel != 0) { + hostapd_logger(iface->bss[0], NULL, + HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_WARNING, + "Configured channel (%d) not found from the " + "channel list of current mode (%d) %s", + iface->conf->channel, + iface->current_mode->mode, + hostapd_hw_mode_txt(iface->current_mode->mode)); + iface->current_mode = NULL; + } + + if (iface->current_mode == NULL) { + hostapd_logger(iface->bss[0], NULL, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_WARNING, + "Hardware does not support configured channel"); + return -1; + } + +#ifdef CONFIG_IEEE80211N + if (!ieee80211n_allowed_ht40_channel_pair(iface)) + return -1; + if (!ieee80211n_supported_ht_capab(iface)) + return -1; +#endif /* CONFIG_IEEE80211N */ + + if (hostapd_prepare_rates(iface->bss[0], iface->current_mode)) { + wpa_printf(MSG_ERROR, "Failed to prepare rates table."); + hostapd_logger(iface->bss[0], NULL, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_WARNING, + "Failed to prepare rates table."); + return -1; + } + + ret = hostapd_passive_scan(iface->bss[0], 0, + iface->conf->passive_scan_mode, + iface->conf->passive_scan_interval, + iface->conf->passive_scan_listen, + NULL, NULL); + if (ret) { + if (ret == -1) { + wpa_printf(MSG_DEBUG, "Passive scanning not " + "supported"); + } else { + wpa_printf(MSG_ERROR, "Could not set passive " + "scanning: %s", strerror(ret)); + } + ret = 0; + } + + return ret; +} + + +const char * hostapd_hw_mode_txt(int mode) +{ + switch (mode) { + case HOSTAPD_MODE_IEEE80211A: + return "IEEE 802.11a"; + case HOSTAPD_MODE_IEEE80211B: + return "IEEE 802.11b"; + case HOSTAPD_MODE_IEEE80211G: + return "IEEE 802.11g"; + default: + return "UNKNOWN"; + } +} + + +int hostapd_hw_get_freq(struct hostapd_data *hapd, int chan) +{ + int i; + + if (!hapd->iface->current_mode) + return 0; + + for (i = 0; i < hapd->iface->current_mode->num_channels; i++) { + struct hostapd_channel_data *ch = + &hapd->iface->current_mode->channels[i]; + if (ch->chan == chan) + return ch->freq; + } + + return 0; +} + + +int hostapd_hw_get_channel(struct hostapd_data *hapd, int freq) +{ + int i; + + if (!hapd->iface->current_mode) + return 0; + + for (i = 0; i < hapd->iface->current_mode->num_channels; i++) { + struct hostapd_channel_data *ch = + &hapd->iface->current_mode->channels[i]; + if (ch->freq == freq) + return ch->chan; + } + + return 0; +} diff --git a/contrib/wpa/hostapd/hw_features.h b/contrib/wpa/hostapd/hw_features.h new file mode 100644 index 0000000..7d43c89 --- /dev/null +++ b/contrib/wpa/hostapd/hw_features.h @@ -0,0 +1,62 @@ +/* + * hostapd / Hardware feature query and different modes + * Copyright 2002-2003, Instant802 Networks, Inc. + * Copyright 2005-2006, Devicescape Software, Inc. + * + * 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 HW_FEATURES_H +#define HW_FEATURES_H + +#define HOSTAPD_CHAN_DISABLED 0x00000001 +#define HOSTAPD_CHAN_PASSIVE_SCAN 0x00000002 +#define HOSTAPD_CHAN_NO_IBSS 0x00000004 +#define HOSTAPD_CHAN_RADAR 0x00000008 + +struct hostapd_channel_data { + short chan; /* channel number (IEEE 802.11) */ + short freq; /* frequency in MHz */ + int flag; /* flag for hostapd use (HOSTAPD_CHAN_*) */ + u8 max_tx_power; /* maximum transmit power in dBm */ +}; + +#define HOSTAPD_RATE_ERP 0x00000001 +#define HOSTAPD_RATE_BASIC 0x00000002 +#define HOSTAPD_RATE_PREAMBLE2 0x00000004 +#define HOSTAPD_RATE_SUPPORTED 0x00000010 +#define HOSTAPD_RATE_OFDM 0x00000020 +#define HOSTAPD_RATE_CCK 0x00000040 +#define HOSTAPD_RATE_MANDATORY 0x00000100 + +struct hostapd_rate_data { + int rate; /* rate in 100 kbps */ + int flags; /* HOSTAPD_RATE_ flags */ +}; + +struct hostapd_hw_modes { + int mode; + int num_channels; + struct hostapd_channel_data *channels; + int num_rates; + struct hostapd_rate_data *rates; + u16 ht_capab; +}; + + +void hostapd_free_hw_features(struct hostapd_hw_modes *hw_features, + size_t num_hw_features); +int hostapd_get_hw_features(struct hostapd_iface *iface); +int hostapd_select_hw_mode(struct hostapd_iface *iface); +const char * hostapd_hw_mode_txt(int mode); +int hostapd_hw_get_freq(struct hostapd_data *hapd, int chan); +int hostapd_hw_get_channel(struct hostapd_data *hapd, int freq); + +#endif /* HW_FEATURES_H */ diff --git a/contrib/wpa/hostapd/iapp.c b/contrib/wpa/hostapd/iapp.c new file mode 100644 index 0000000..6d6dba8 --- /dev/null +++ b/contrib/wpa/hostapd/iapp.c @@ -0,0 +1,553 @@ +/* + * hostapd / IEEE 802.11F-2003 Inter-Access Point Protocol (IAPP) + * Copyright (c) 2002-2007, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + * + * Note: IEEE 802.11F-2003 was a experimental use specification. It has expired + * and IEEE has withdrawn it. In other words, it is likely better to look at + * using some other mechanism for AP-to-AP communication than extending the + * implementation here. + */ + +/* 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 "includes.h" +#include <net/if.h> +#include <sys/ioctl.h> +#ifdef USE_KERNEL_HEADERS +#include <linux/if_packet.h> +#else /* USE_KERNEL_HEADERS */ +#include <netpacket/packet.h> +#endif /* USE_KERNEL_HEADERS */ + +#include "hostapd.h" +#include "ieee802_11.h" +#include "iapp.h" +#include "eloop.h" +#include "sta_info.h" + + +#define IAPP_MULTICAST "224.0.1.178" +#define IAPP_UDP_PORT 3517 +#define IAPP_TCP_PORT 3517 + +struct iapp_hdr { + u8 version; + u8 command; + be16 identifier; + be16 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]; + be16 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 */ + be16 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 *mac_addr, 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; + os_memcpy(add->mac_addr, mac_addr, ETH_ALEN); + + add->seq_num = host_to_be16(seq_num); + + os_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 */ + + os_memset(msg.da, 0xff, ETH_ALEN); + os_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]"); +} + + +/** + * iapp_new_station - IAPP processing for a new STA + * @iapp: IAPP data + * @sta: The associated station + */ +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; +} + + +/** + * iapp_receive_udp - Process IAPP UDP frames + * @sock: File descriptor for the socket + * @eloop_ctx: IAPP data (struct iapp_data *) + * @sock_ctx: Not used + */ +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"); + return; + } + + 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 < (int) sizeof(*hdr) ? " (too short)" : ""); + + if (len < (int) 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 = os_zalloc(sizeof(*iapp)); + if (iapp == NULL) + return NULL; + 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; + } + + os_memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(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); + + os_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; + } + + os_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]"); + iapp_deinit(iapp); + 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]"); + iapp_deinit(iapp); + return NULL; + } + + os_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]"); + iapp_deinit(iapp); + return NULL; + } + + if (eloop_register_read_sock(iapp->udp_sock, iapp_receive_udp, + iapp, NULL)) { + printf("Could not register read socket for IAPP.\n"); + iapp_deinit(iapp); + 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) { + os_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); + } + os_free(iapp); +} + +int iapp_reconfig(struct hostapd_data *hapd, struct hostapd_config *oldconf, + struct hostapd_bss_config *oldbss) +{ + if (hapd->conf->ieee802_11f != oldbss->ieee802_11f || + os_strcmp(hapd->conf->iapp_iface, oldbss->iapp_iface) != 0) { + iapp_deinit(hapd->iapp); + hapd->iapp = NULL; + + if (hapd->conf->ieee802_11f) { + hapd->iapp = iapp_init(hapd, hapd->conf->iapp_iface); + if (hapd->iapp == NULL) + return -1; + } + } + + return 0; +} diff --git a/contrib/wpa/hostapd/iapp.h b/contrib/wpa/hostapd/iapp.h new file mode 100644 index 0000000..86de592 --- /dev/null +++ b/contrib/wpa/hostapd/iapp.h @@ -0,0 +1,54 @@ +/* + * hostapd / IEEE 802.11F-2003 Inter-Access Point Protocol (IAPP) + * Copyright (c) 2002-2005, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#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); +int iapp_reconfig(struct hostapd_data *hapd, struct hostapd_config *oldconf, + struct hostapd_bss_config *oldbss); + +#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) +{ +} + +static inline int +iapp_reconfig(struct hostapd_data *hapd, struct hostapd_config *oldconf, + struct hostapd_bss_config *oldbss) +{ + return 0; +} + +#endif /* CONFIG_IAPP */ + +#endif /* IAPP_H */ diff --git a/contrib/wpa/hostapd/ieee802_11.c b/contrib/wpa/hostapd/ieee802_11.c new file mode 100644 index 0000000..8399d88 --- /dev/null +++ b/contrib/wpa/hostapd/ieee802_11.c @@ -0,0 +1,1768 @@ +/* + * hostapd / IEEE 802.11 Management + * Copyright (c) 2002-2008, Jouni Malinen <j@w1.fi> + * Copyright (c) 2007-2008, Intel Corporation + * + * 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 "includes.h" + +#ifndef CONFIG_NATIVE_WINDOWS + +#include <net/if.h> + +#include "eloop.h" +#include "hostapd.h" +#include "ieee802_11.h" +#include "beacon.h" +#include "hw_features.h" +#include "radius/radius.h" +#include "radius/radius_client.h" +#include "ieee802_11_auth.h" +#include "sta_info.h" +#include "rc4.h" +#include "ieee802_1x.h" +#include "wpa.h" +#include "wme.h" +#include "ap_list.h" +#include "accounting.h" +#include "driver.h" +#include "mlme.h" + + +u8 * hostapd_eid_supp_rates(struct hostapd_data *hapd, u8 *eid) +{ + u8 *pos = eid; + int i, num, count; + + if (hapd->iface->current_rates == NULL) + return eid; + + *pos++ = WLAN_EID_SUPP_RATES; + num = hapd->iface->num_rates; + if (num > 8) { + /* rest of the rates are encoded in Extended supported + * rates element */ + num = 8; + } + + *pos++ = num; + count = 0; + for (i = 0, count = 0; i < hapd->iface->num_rates && count < num; + i++) { + count++; + *pos = hapd->iface->current_rates[i].rate / 5; + if (hapd->iface->current_rates[i].flags & HOSTAPD_RATE_BASIC) + *pos |= 0x80; + pos++; + } + + return pos; +} + + +u8 * hostapd_eid_ext_supp_rates(struct hostapd_data *hapd, u8 *eid) +{ + u8 *pos = eid; + int i, num, count; + + if (hapd->iface->current_rates == NULL) + return eid; + + num = hapd->iface->num_rates; + if (num <= 8) + return eid; + num -= 8; + + *pos++ = WLAN_EID_EXT_SUPP_RATES; + *pos++ = num; + count = 0; + for (i = 0, count = 0; i < hapd->iface->num_rates && count < num + 8; + i++) { + count++; + if (count <= 8) + continue; /* already in SuppRates IE */ + *pos = hapd->iface->current_rates[i].rate / 5; + if (hapd->iface->current_rates[i].flags & HOSTAPD_RATE_BASIC) + *pos |= 0x80; + pos++; + } + + return pos; +} + + +u8 * hostapd_eid_ht_capabilities_info(struct hostapd_data *hapd, u8 *eid) +{ +#ifdef CONFIG_IEEE80211N + struct ieee80211_ht_capability *cap; + u8 *pos = eid; + + if (!hapd->iconf->ieee80211n) + return eid; + + *pos++ = WLAN_EID_HT_CAP; + *pos++ = sizeof(*cap); + + cap = (struct ieee80211_ht_capability *) pos; + os_memset(cap, 0, sizeof(*cap)); + SET_2BIT_U8(&cap->mac_ht_params_info, + MAC_HT_PARAM_INFO_MAX_RX_AMPDU_FACTOR_OFFSET, + MAX_RX_AMPDU_FACTOR_64KB); + + cap->capabilities_info = host_to_le16(hapd->iconf->ht_capab); + + cap->supported_mcs_set[0] = 0xff; + cap->supported_mcs_set[1] = 0xff; + + pos += sizeof(*cap); + + return pos; +#else /* CONFIG_IEEE80211N */ + return eid; +#endif /* CONFIG_IEEE80211N */ +} + + +u8 * hostapd_eid_ht_operation(struct hostapd_data *hapd, u8 *eid) +{ +#ifdef CONFIG_IEEE80211N + struct ieee80211_ht_operation *oper; + u8 *pos = eid; + + if (!hapd->iconf->ieee80211n) + return eid; + + *pos++ = WLAN_EID_HT_OPERATION; + *pos++ = sizeof(*oper); + + oper = (struct ieee80211_ht_operation *) pos; + os_memset(oper, 0, sizeof(*oper)); + + oper->control_chan = hapd->iconf->channel; + oper->operation_mode = host_to_le16(hapd->iface->ht_op_mode); + if (hapd->iconf->secondary_channel == 1) + oper->ht_param |= HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE | + HT_INFO_HT_PARAM_REC_TRANS_CHNL_WIDTH; + if (hapd->iconf->secondary_channel == -1) + oper->ht_param |= HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW | + HT_INFO_HT_PARAM_REC_TRANS_CHNL_WIDTH; + + pos += sizeof(*oper); + + return pos; +#else /* CONFIG_IEEE80211N */ + return eid; +#endif /* CONFIG_IEEE80211N */ +} + + +#ifdef CONFIG_IEEE80211N + +/* +op_mode +Set to 0 (HT pure) under the followign conditions + - all STAs in the BSS are 20/40 MHz HT in 20/40 MHz BSS or + - all STAs in the BSS are 20 MHz HT in 20 MHz BSS +Set to 1 (HT non-member protection) if there may be non-HT STAs + in both the primary and the secondary channel +Set to 2 if only HT STAs are associated in BSS, + however and at least one 20 MHz HT STA is associated +Set to 3 (HT mixed mode) when one or more non-HT STAs are associated + (currently non-GF HT station is considered as non-HT STA also) +*/ +int hostapd_ht_operation_update(struct hostapd_iface *iface) +{ + u16 cur_op_mode, new_op_mode; + int op_mode_changes = 0; + + if (!iface->conf->ieee80211n || iface->conf->ht_op_mode_fixed) + return 0; + + wpa_printf(MSG_DEBUG, "%s current operation mode=0x%X", + __func__, iface->ht_op_mode); + + if (!(iface->ht_op_mode & HT_INFO_OPERATION_MODE_NON_GF_DEVS_PRESENT) + && iface->num_sta_ht_no_gf) { + iface->ht_op_mode |= + HT_INFO_OPERATION_MODE_NON_GF_DEVS_PRESENT; + op_mode_changes++; + } else if ((iface->ht_op_mode & + HT_INFO_OPERATION_MODE_NON_GF_DEVS_PRESENT) && + iface->num_sta_ht_no_gf == 0) { + iface->ht_op_mode &= + ~HT_INFO_OPERATION_MODE_NON_GF_DEVS_PRESENT; + op_mode_changes++; + } + + if (!(iface->ht_op_mode & HT_INFO_OPERATION_MODE_NON_HT_STA_PRESENT) && + (iface->num_sta_no_ht || iface->olbc_ht)) { + iface->ht_op_mode |= HT_INFO_OPERATION_MODE_NON_HT_STA_PRESENT; + op_mode_changes++; + } else if ((iface->ht_op_mode & + HT_INFO_OPERATION_MODE_NON_HT_STA_PRESENT) && + (iface->num_sta_no_ht == 0 && !iface->olbc_ht)) { + iface->ht_op_mode &= + ~HT_INFO_OPERATION_MODE_NON_HT_STA_PRESENT; + op_mode_changes++; + } + + /* Note: currently we switch to the MIXED op mode if HT non-greenfield + * station is associated. Probably it's a theoretical case, since + * it looks like all known HT STAs support greenfield. + */ + new_op_mode = 0; + if (iface->num_sta_no_ht || + (iface->ht_op_mode & HT_INFO_OPERATION_MODE_NON_GF_DEVS_PRESENT)) + new_op_mode = OP_MODE_MIXED; + else if ((iface->conf->ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET) + && iface->num_sta_ht_20mhz) + new_op_mode = OP_MODE_20MHZ_HT_STA_ASSOCED; + else if (iface->olbc_ht) + new_op_mode = OP_MODE_MAY_BE_LEGACY_STAS; + else + new_op_mode = OP_MODE_PURE; + + cur_op_mode = iface->ht_op_mode & HT_INFO_OPERATION_MODE_OP_MODE_MASK; + if (cur_op_mode != new_op_mode) { + iface->ht_op_mode &= ~HT_INFO_OPERATION_MODE_OP_MODE_MASK; + iface->ht_op_mode |= new_op_mode; + op_mode_changes++; + } + + wpa_printf(MSG_DEBUG, "%s new operation mode=0x%X changes=%d", + __func__, iface->ht_op_mode, op_mode_changes); + + return op_mode_changes; +} + +#endif /* CONFIG_IEEE80211N */ + + +u16 hostapd_own_capab_info(struct hostapd_data *hapd, struct sta_info *sta, + int probe) +{ + int capab = WLAN_CAPABILITY_ESS; + int privacy; + + if (hapd->iface->num_sta_no_short_preamble == 0 && + hapd->iconf->preamble == SHORT_PREAMBLE) + capab |= WLAN_CAPABILITY_SHORT_PREAMBLE; + + privacy = hapd->conf->ssid.wep.keys_set; + + if (hapd->conf->ieee802_1x && + (hapd->conf->default_wep_key_len || + hapd->conf->individual_wep_key_len)) + privacy = 1; + + if (hapd->conf->wpa) + privacy = 1; + + if (sta) { + int policy, def_klen; + if (probe && sta->ssid_probe) { + policy = sta->ssid_probe->security_policy; + def_klen = sta->ssid_probe->wep.default_len; + } else { + policy = sta->ssid->security_policy; + def_klen = sta->ssid->wep.default_len; + } + privacy = policy != SECURITY_PLAINTEXT; + if (policy == SECURITY_IEEE_802_1X && def_klen == 0) + privacy = 0; + } + + if (privacy) + capab |= WLAN_CAPABILITY_PRIVACY; + + if (hapd->iface->current_mode && + hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G && + hapd->iface->num_sta_no_short_slot_time == 0) + capab |= WLAN_CAPABILITY_SHORT_SLOT_TIME; + + return capab; +} + + +#ifdef CONFIG_IEEE80211W +static u8 * hostapd_eid_assoc_comeback_time(struct hostapd_data *hapd, + struct sta_info *sta, u8 *eid) +{ + u8 *pos = eid; + u32 timeout, tu; + struct os_time now, passed; + + *pos++ = WLAN_EID_TIMEOUT_INTERVAL; + *pos++ = 5; + *pos++ = WLAN_TIMEOUT_ASSOC_COMEBACK; + os_get_time(&now); + os_time_sub(&now, &sta->sa_query_start, &passed); + tu = (passed.sec * 1000000 + passed.usec) / 1024; + if (hapd->conf->assoc_sa_query_max_timeout > tu) + timeout = hapd->conf->assoc_sa_query_max_timeout - tu; + else + timeout = 0; + if (timeout < hapd->conf->assoc_sa_query_max_timeout) + timeout++; /* add some extra time for local timers */ + WPA_PUT_LE32(pos, timeout); + pos += 4; + + return pos; +} +#endif /* CONFIG_IEEE80211W */ + + +void ieee802_11_print_ssid(char *buf, const u8 *ssid, u8 len) +{ + int i; + if (len > HOSTAPD_MAX_SSID_LEN) + len = HOSTAPD_MAX_SSID_LEN; + for (i = 0; i < len; i++) { + if (ssid[i] >= 32 && ssid[i] < 127) + buf[i] = ssid[i]; + else + buf[i] = '.'; + } + buf[len] = '\0'; +} + + +/** + * ieee802_11_send_deauth - Send Deauthentication frame + * @hapd: hostapd BSS data + * @addr: Address of the destination STA + * @reason: Reason code for Deauthentication + */ +void ieee802_11_send_deauth(struct hostapd_data *hapd, u8 *addr, u16 reason) +{ + struct ieee80211_mgmt mgmt; + + hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "deauthenticate - reason %d", reason); + os_memset(&mgmt, 0, sizeof(mgmt)); + mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, + WLAN_FC_STYPE_DEAUTH); + os_memcpy(mgmt.da, addr, ETH_ALEN); + os_memcpy(mgmt.sa, hapd->own_addr, ETH_ALEN); + os_memcpy(mgmt.bssid, hapd->own_addr, ETH_ALEN); + mgmt.u.deauth.reason_code = host_to_le16(reason); + if (hostapd_send_mgmt_frame(hapd, &mgmt, IEEE80211_HDRLEN + + sizeof(mgmt.u.deauth), 0) < 0) + perror("ieee802_11_send_deauth: send"); +} + + +static u16 auth_shared_key(struct hostapd_data *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 = os_zalloc(WLAN_AUTH_CHALLENGE_LEN); + if (sta->challenge == NULL) + return WLAN_STATUS_UNSPECIFIED_FAILURE; + + now = time(NULL); + r = random(); + os_memcpy(key, &now, 4); + os_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 || + os_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_auth_sm_event(sta->wpa_sm, WPA_AUTH); +#endif + os_free(sta->challenge); + sta->challenge = NULL; + + return 0; +} + + +static void send_auth_reply(struct hostapd_data *hapd, + const u8 *dst, const u8 *bssid, + u16 auth_alg, u16 auth_transaction, u16 resp, + const u8 *ies, size_t ies_len) +{ + struct ieee80211_mgmt *reply; + u8 *buf; + size_t rlen; + + rlen = IEEE80211_HDRLEN + sizeof(reply->u.auth) + ies_len; + buf = os_zalloc(rlen); + if (buf == NULL) + return; + + reply = (struct ieee80211_mgmt *) buf; + reply->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, + WLAN_FC_STYPE_AUTH); + os_memcpy(reply->da, dst, ETH_ALEN); + os_memcpy(reply->sa, hapd->own_addr, ETH_ALEN); + os_memcpy(reply->bssid, 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); + + if (ies && ies_len) + os_memcpy(reply->u.auth.variable, ies, ies_len); + + wpa_printf(MSG_DEBUG, "authentication reply: STA=" MACSTR + " auth_alg=%d auth_transaction=%d resp=%d (IE len=%lu)", + MAC2STR(dst), auth_alg, auth_transaction, + resp, (unsigned long) ies_len); + if (hostapd_send_mgmt_frame(hapd, reply, rlen, 0) < 0) + perror("send_auth_reply: send"); + + os_free(buf); +} + + +#ifdef CONFIG_IEEE80211R +static void handle_auth_ft_finish(void *ctx, const u8 *dst, const u8 *bssid, + u16 auth_transaction, u16 status, + const u8 *ies, size_t ies_len) +{ + struct hostapd_data *hapd = ctx; + struct sta_info *sta; + + send_auth_reply(hapd, dst, bssid, WLAN_AUTH_FT, auth_transaction, + status, ies, ies_len); + + if (status != WLAN_STATUS_SUCCESS) + return; + + sta = ap_get_sta(hapd, dst); + if (sta == NULL) + return; + + hostapd_logger(hapd, dst, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, "authentication OK (FT)"); + sta->flags |= WLAN_STA_AUTH; + mlme_authenticate_indication(hapd, sta); +} +#endif /* CONFIG_IEEE80211R */ + + +static void handle_auth(struct hostapd_data *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; + int vlan_id = 0; + u8 resp_ies[2 + WLAN_AUTH_CHALLENGE_LEN]; + size_t resp_ies_len = 0; + + 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]; + + wpa_printf(MSG_DEBUG, "authentication: STA=" MACSTR " auth_alg=%d " + "auth_transaction=%d status_code=%d wep=%d%s", + MAC2STR(mgmt->sa), auth_alg, auth_transaction, + status_code, !!(fc & WLAN_FC_ISWEP), + challenge ? " challenge" : ""); + + if (hapd->tkip_countermeasures) { + resp = WLAN_REASON_MICHAEL_MIC_FAILURE; + goto fail; + } + + if (!(((hapd->conf->auth_algs & WPA_AUTH_ALG_OPEN) && + auth_alg == WLAN_AUTH_OPEN) || +#ifdef CONFIG_IEEE80211R + (hapd->conf->wpa && + (hapd->conf->wpa_key_mgmt & + (WPA_KEY_MGMT_FT_IEEE8021X | WPA_KEY_MGMT_FT_PSK)) && + auth_alg == WLAN_AUTH_FT) || +#endif /* CONFIG_IEEE80211R */ + ((hapd->conf->auth_algs & WPA_AUTH_ALG_SHARED) && + 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 (os_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, &vlan_id); + 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) { + wpa_printf(MSG_DEBUG, "Authentication frame from " MACSTR + " waiting for an external authentication", + 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; + } + + if (vlan_id > 0) { + if (hostapd_get_vlan_id_ifname(hapd->conf->vlan, + sta->vlan_id) == NULL) { + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS, + HOSTAPD_LEVEL_INFO, "Invalid VLAN ID " + "%d received from RADIUS server", + vlan_id); + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + sta->vlan_id = vlan_id; + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS, + HOSTAPD_LEVEL_INFO, "VLAN ID %d", sta->vlan_id); + } + + 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_auth_sm_event(sta->wpa_sm, WPA_AUTH); + sta->auth_alg = WLAN_AUTH_OPEN; + mlme_authenticate_indication(hapd, sta); +#endif + break; + case WLAN_AUTH_SHARED_KEY: + resp = auth_shared_key(hapd, sta, auth_transaction, challenge, + fc & WLAN_FC_ISWEP); + sta->auth_alg = WLAN_AUTH_SHARED_KEY; + mlme_authenticate_indication(hapd, sta); + if (sta->challenge && auth_transaction == 1) { + resp_ies[0] = WLAN_EID_CHALLENGE; + resp_ies[1] = WLAN_AUTH_CHALLENGE_LEN; + os_memcpy(resp_ies + 2, sta->challenge, + WLAN_AUTH_CHALLENGE_LEN); + resp_ies_len = 2 + WLAN_AUTH_CHALLENGE_LEN; + } + break; +#ifdef CONFIG_IEEE80211R + case WLAN_AUTH_FT: + sta->auth_alg = WLAN_AUTH_FT; + if (sta->wpa_sm == NULL) + sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth, + sta->addr); + if (sta->wpa_sm == NULL) { + wpa_printf(MSG_DEBUG, "FT: Failed to initialize WPA " + "state machine"); + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + wpa_ft_process_auth(sta->wpa_sm, mgmt->bssid, + auth_transaction, mgmt->u.auth.variable, + len - IEEE80211_HDRLEN - + sizeof(mgmt->u.auth), + handle_auth_ft_finish, hapd); + /* handle_auth_ft_finish() callback will complete auth. */ + return; +#endif /* CONFIG_IEEE80211R */ + } + + fail: + send_auth_reply(hapd, mgmt->sa, mgmt->bssid, auth_alg, + auth_transaction + 1, resp, resp_ies, resp_ies_len); +} + + +static void handle_assoc(struct hostapd_data *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; + u8 buf[sizeof(struct ieee80211_mgmt) + 512]; + struct ieee80211_mgmt *reply; + + 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); + wpa_printf(MSG_DEBUG, "reassociation request: STA=" MACSTR + " capab_info=0x%02x listen_interval=%d current_ap=" + MACSTR, + 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); + wpa_printf(MSG_DEBUG, "association request: STA=" MACSTR + " capab_info=0x%02x listen_interval=%d", + 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); +#ifdef CONFIG_IEEE80211R + if (sta && sta->auth_alg == WLAN_AUTH_FT && + (sta->flags & WLAN_STA_AUTH) == 0) { + wpa_printf(MSG_DEBUG, "FT: Allow STA " MACSTR " to associate " + "prior to authentication since it is using " + "over-the-DS FT", MAC2STR(mgmt->sa)); + } else +#endif /* CONFIG_IEEE80211R */ + 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; + } + + if (listen_interval > hapd->conf->max_listen_interval) { + hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "Too large Listen Interval (%d)", + listen_interval); + resp = WLAN_STATUS_ASSOC_DENIED_LISTEN_INT_TOO_LARGE; + goto fail; + } + + sta->capability = capab_info; + sta->listen_interval = listen_interval; + + /* followed by SSID and Supported rates; and HT capabilities if 802.11n + * is used */ + if (ieee802_11_parse_elems(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.ssid_len || + os_memcmp(elems.ssid, hapd->conf->ssid.ssid, elems.ssid_len) != 0) + { + char ssid_txt[33]; + ieee802_11_print_ssid(ssid_txt, elems.ssid, elems.ssid_len); + printf("Station " MACSTR " tried to associate with " + "unknown SSID '%s'\n", MAC2STR(sta->addr), ssid_txt); + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + + sta->flags &= ~WLAN_STA_WME; + if (elems.wme && hapd->conf->wme_enabled) { + if (hostapd_eid_wme_valid(hapd, elems.wme, elems.wme_len)) + hostapd_logger(hapd, sta->addr, + HOSTAPD_MODULE_WPA, + HOSTAPD_LEVEL_DEBUG, + "invalid WME element in association " + "request"); + else + sta->flags |= WLAN_STA_WME; + } + + if (!elems.supp_rates) { + hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "No supported rates element in AssocReq"); + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + + if (elems.supp_rates_len > sizeof(sta->supported_rates)) { + hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "Invalid supported rates element length %d", + elems.supp_rates_len); + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + + os_memset(sta->supported_rates, 0, sizeof(sta->supported_rates)); + os_memcpy(sta->supported_rates, elems.supp_rates, + elems.supp_rates_len); + sta->supported_rates_len = elems.supp_rates_len; + + if (elems.ext_supp_rates) { + if (elems.supp_rates_len + elems.ext_supp_rates_len > + sizeof(sta->supported_rates)) { + hostapd_logger(hapd, mgmt->sa, + HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "Invalid supported rates element length" + " %d+%d", elems.supp_rates_len, + elems.ext_supp_rates_len); + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + + os_memcpy(sta->supported_rates + elems.supp_rates_len, + elems.ext_supp_rates, elems.ext_supp_rates_len); + sta->supported_rates_len += elems.ext_supp_rates_len; + } + +#ifdef CONFIG_IEEE80211N + /* save HT capabilities in the sta object */ + os_memset(&sta->ht_capabilities, 0, sizeof(sta->ht_capabilities)); + if (elems.ht_capabilities && + elems.ht_capabilities_len >= + sizeof(struct ieee80211_ht_capability)) { + sta->flags |= WLAN_STA_HT; + sta->ht_capabilities.id = WLAN_EID_HT_CAP; + sta->ht_capabilities.length = + sizeof(struct ieee80211_ht_capability); + os_memcpy(&sta->ht_capabilities.data, + elems.ht_capabilities, + sizeof(struct ieee80211_ht_capability)); + } else + sta->flags &= ~WLAN_STA_HT; +#endif /* CONFIG_IEEE80211N */ + + if ((hapd->conf->wpa & WPA_PROTO_RSN) && elems.rsn_ie) { + wpa_ie = elems.rsn_ie; + wpa_ie_len = elems.rsn_ie_len; + } else if ((hapd->conf->wpa & WPA_PROTO_WPA) && + elems.wpa_ie) { + wpa_ie = elems.wpa_ie; + wpa_ie_len = elems.wpa_ie_len; + } else { + wpa_ie = NULL; + wpa_ie_len = 0; + } +#ifdef CONFIG_WPS + sta->flags &= ~(WLAN_STA_WPS | WLAN_STA_MAYBE_WPS); + if (hapd->conf->wps_state && wpa_ie == NULL) { + if (elems.wps_ie) { + wpa_printf(MSG_DEBUG, "STA included WPS IE in " + "(Re)Association Request - assume WPS is " + "used"); + sta->flags |= WLAN_STA_WPS; + wpabuf_free(sta->wps_ie); + sta->wps_ie = wpabuf_alloc_copy(elems.wps_ie + 4, + elems.wps_ie_len - 4); + } else { + wpa_printf(MSG_DEBUG, "STA did not include WPA/RSN IE " + "in (Re)Association Request - possible WPS " + "use"); + sta->flags |= WLAN_STA_MAYBE_WPS; + } + } else +#endif /* CONFIG_WPS */ + 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 && wpa_ie) { + int res; + wpa_ie -= 2; + wpa_ie_len += 2; + if (sta->wpa_sm == NULL) + sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth, + sta->addr); + if (sta->wpa_sm == NULL) { + printf("Failed to initialize WPA state machine\n"); + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm, + wpa_ie, wpa_ie_len, + elems.mdie, elems.mdie_len); + 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_ALLOC_FAIL) + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; +#ifdef CONFIG_IEEE80211W + else if (res == WPA_MGMT_FRAME_PROTECTION_VIOLATION) + resp = WLAN_STATUS_ROBUST_MGMT_FRAME_POLICY_VIOLATION; + else if (res == WPA_INVALID_MGMT_GROUP_CIPHER) + resp = WLAN_STATUS_ROBUST_MGMT_FRAME_POLICY_VIOLATION; +#endif /* CONFIG_IEEE80211W */ + else if (res == WPA_INVALID_MDIE) + resp = WLAN_STATUS_INVALID_MDIE; + else if (res != WPA_IE_OK) + resp = WLAN_STATUS_INVALID_IE; + if (resp != WLAN_STATUS_SUCCESS) + goto fail; +#ifdef CONFIG_IEEE80211W + if ((sta->flags & WLAN_STA_MFP) && !sta->sa_query_timed_out && + sta->sa_query_count > 0) + ap_check_sa_query_timeout(hapd, sta); + if ((sta->flags & WLAN_STA_MFP) && !sta->sa_query_timed_out && + (!reassoc || sta->auth_alg != WLAN_AUTH_FT)) { + /* + * STA has already been associated with MFP and SA + * Query timeout has not been reached. Reject the + * association attempt temporarily and start SA Query, + * if one is not pending. + */ + + if (sta->sa_query_count == 0) + ap_sta_start_sa_query(hapd, sta); + + resp = WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY; + goto fail; + } + + if (wpa_auth_uses_mfp(sta->wpa_sm)) + sta->flags |= WLAN_STA_MFP; + else + sta->flags &= ~WLAN_STA_MFP; +#endif /* CONFIG_IEEE80211W */ + +#ifdef CONFIG_IEEE80211R + if (sta->auth_alg == WLAN_AUTH_FT) { + if (!reassoc) { + wpa_printf(MSG_DEBUG, "FT: " MACSTR " tried " + "to use association (not " + "re-association) with FT auth_alg", + MAC2STR(sta->addr)); + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + + resp = wpa_ft_validate_reassoc(sta->wpa_sm, pos, left); + if (resp != WLAN_STATUS_SUCCESS) + goto fail; + } +#endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_IEEE80211N + if ((sta->flags & WLAN_STA_HT) && + wpa_auth_get_pairwise(sta->wpa_sm) == WPA_CIPHER_TKIP) { + wpa_printf(MSG_DEBUG, "HT: " MACSTR " tried to " + "use TKIP with HT association", + MAC2STR(sta->addr)); + resp = WLAN_STATUS_CIPHER_REJECTED_PER_POLICY; + goto fail; + } +#endif /* CONFIG_IEEE80211N */ + } else + wpa_auth_sta_no_wpa(sta->wpa_sm); + + if (hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G) + sta->flags |= WLAN_STA_NONERP; + for (i = 0; i < sta->supported_rates_len; i++) { + if ((sta->supported_rates[i] & 0x7f) > 22) { + sta->flags &= ~WLAN_STA_NONERP; + break; + } + } + if (sta->flags & WLAN_STA_NONERP && !sta->nonerp_set) { + sta->nonerp_set = 1; + hapd->iface->num_sta_non_erp++; + if (hapd->iface->num_sta_non_erp == 1) + ieee802_11_set_beacons(hapd->iface); + } + + if (!(sta->capability & WLAN_CAPABILITY_SHORT_SLOT_TIME) && + !sta->no_short_slot_time_set) { + sta->no_short_slot_time_set = 1; + hapd->iface->num_sta_no_short_slot_time++; + if (hapd->iface->current_mode->mode == + HOSTAPD_MODE_IEEE80211G && + hapd->iface->num_sta_no_short_slot_time == 1) + ieee802_11_set_beacons(hapd->iface); + } + + if (sta->capability & WLAN_CAPABILITY_SHORT_PREAMBLE) + sta->flags |= WLAN_STA_SHORT_PREAMBLE; + else + sta->flags &= ~WLAN_STA_SHORT_PREAMBLE; + + if (!(sta->capability & WLAN_CAPABILITY_SHORT_PREAMBLE) && + !sta->no_short_preamble_set) { + sta->no_short_preamble_set = 1; + hapd->iface->num_sta_no_short_preamble++; + if (hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G + && hapd->iface->num_sta_no_short_preamble == 1) + ieee802_11_set_beacons(hapd->iface); + } + +#ifdef CONFIG_IEEE80211N + if (sta->flags & WLAN_STA_HT) { + u16 ht_capab = le_to_host16( + sta->ht_capabilities.data.capabilities_info); + wpa_printf(MSG_DEBUG, "HT: STA " MACSTR " HT Capabilities " + "Info: 0x%04x", MAC2STR(sta->addr), ht_capab); + if ((ht_capab & HT_CAP_INFO_GREEN_FIELD) == 0) { + if (!sta->no_ht_gf_set) { + sta->no_ht_gf_set = 1; + hapd->iface->num_sta_ht_no_gf++; + } + wpa_printf(MSG_DEBUG, "%s STA " MACSTR " - no " + "greenfield, num of non-gf stations %d", + __func__, MAC2STR(sta->addr), + hapd->iface->num_sta_ht_no_gf); + } + if ((ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET) == 0) { + if (!sta->ht_20mhz_set) { + sta->ht_20mhz_set = 1; + hapd->iface->num_sta_ht_20mhz++; + } + wpa_printf(MSG_DEBUG, "%s STA " MACSTR " - 20 MHz HT, " + "num of 20MHz HT STAs %d", + __func__, MAC2STR(sta->addr), + hapd->iface->num_sta_ht_20mhz); + } + } else { + if (!sta->no_ht_set) { + sta->no_ht_set = 1; + hapd->iface->num_sta_no_ht++; + } + if (hapd->iconf->ieee80211n) { + wpa_printf(MSG_DEBUG, "%s STA " MACSTR + " - no HT, num of non-HT stations %d", + __func__, MAC2STR(sta->addr), + hapd->iface->num_sta_no_ht); + } + } + + if (hostapd_ht_operation_update(hapd->iface) > 0) + ieee802_11_set_beacons(hapd->iface); +#endif /* CONFIG_IEEE80211N */ + + /* get a unique AID */ + if (sta->aid > 0) { + wpa_printf(MSG_DEBUG, " old AID %d", 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; + wpa_printf(MSG_ERROR, " no room for more AIDs"); + goto fail; + } else { + hapd->sta_aid[sta->aid - 1] = sta; + wpa_printf(MSG_DEBUG, " new AID %d", 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 + */ + +#ifdef CONFIG_IEEE80211W + if ((sta->flags & WLAN_STA_MFP) && sta->sa_query_timed_out) { + wpa_printf(MSG_DEBUG, "Allowing %sassociation after timed out " + "SA Query procedure", reassoc ? "re" : ""); + /* TODO: Send a protected Disassociate frame to the STA using + * the old key and Reason Code "Previous Authentication no + * longer valid". Make sure this is only sent protected since + * unprotected frame would be received by the STA that is now + * trying to associate. + */ + } +#endif /* CONFIG_IEEE80211W */ + + if (reassoc) { + os_memcpy(sta->previous_ap, mgmt->u.reassoc_req.current_ap, + ETH_ALEN); + } + + if (sta->last_assoc_req) + os_free(sta->last_assoc_req); + sta->last_assoc_req = os_malloc(len); + if (sta->last_assoc_req) + os_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: + os_memset(buf, 0, sizeof(buf)); + reply = (struct ieee80211_mgmt *) buf; + reply->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))); + os_memcpy(reply->da, mgmt->sa, ETH_ALEN); + os_memcpy(reply->sa, hapd->own_addr, ETH_ALEN); + os_memcpy(reply->bssid, mgmt->bssid, ETH_ALEN); + + send_len = IEEE80211_HDRLEN; + if (send_deauth) { + send_len += sizeof(reply->u.deauth); + reply->u.deauth.reason_code = host_to_le16(resp); + } else { + u8 *p; + send_len += sizeof(reply->u.assoc_resp); + reply->u.assoc_resp.capab_info = + host_to_le16(hostapd_own_capab_info(hapd, sta, 0)); + reply->u.assoc_resp.status_code = host_to_le16(resp); + reply->u.assoc_resp.aid = host_to_le16((sta ? sta->aid : 0) + | BIT(14) | BIT(15)); + /* Supported rates */ + p = hostapd_eid_supp_rates(hapd, reply->u.assoc_resp.variable); + /* Extended supported rates */ + p = hostapd_eid_ext_supp_rates(hapd, p); + if (sta->flags & WLAN_STA_WME) + p = hostapd_eid_wme(hapd, p); + + p = hostapd_eid_ht_capabilities_info(hapd, p); + p = hostapd_eid_ht_operation(hapd, p); + +#ifdef CONFIG_IEEE80211R + if (resp == WLAN_STATUS_SUCCESS) { + /* IEEE 802.11r: Mobility Domain Information, Fast BSS + * Transition Information, RSN */ + p = wpa_sm_write_assoc_resp_ies(sta->wpa_sm, p, + buf + sizeof(buf) - p, + sta->auth_alg); + } +#endif /* CONFIG_IEEE80211R */ + +#ifdef CONFIG_IEEE80211W + if (resp == WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY) + p = hostapd_eid_assoc_comeback_time(hapd, sta, p); +#endif /* CONFIG_IEEE80211W */ + + send_len += p - reply->u.assoc_resp.variable; + } + + if (hostapd_send_mgmt_frame(hapd, reply, send_len, 0) < 0) + perror("handle_assoc: send"); +} + + +static void handle_disassoc(struct hostapd_data *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; + } + + wpa_printf(MSG_DEBUG, "disassocation: STA=" MACSTR " reason_code=%d", + MAC2STR(mgmt->sa), + le_to_host16(mgmt->u.disassoc.reason_code)); + + 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_auth_sm_event(sta->wpa_sm, 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_notify_port_enabled(sta->eapol_sm, 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); + } + + mlme_disassociate_indication( + hapd, sta, le_to_host16(mgmt->u.disassoc.reason_code)); +} + + +static void handle_deauth(struct hostapd_data *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; + } + + wpa_printf(MSG_DEBUG, "deauthentication: STA=" MACSTR + " reason_code=%d", + MAC2STR(mgmt->sa), + le_to_host16(mgmt->u.deauth.reason_code)); + + 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_auth_sm_event(sta->wpa_sm, WPA_DEAUTH); + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, "deauthenticated"); + mlme_deauthenticate_indication( + hapd, sta, le_to_host16(mgmt->u.deauth.reason_code)); + sta->acct_terminate_cause = RADIUS_ACCT_TERMINATE_CAUSE_USER_REQUEST; + ieee802_1x_notify_port_enabled(sta->eapol_sm, 0); + ap_free_sta(hapd, sta); +} + + +static void handle_beacon(struct hostapd_data *hapd, + struct ieee80211_mgmt *mgmt, size_t len, + struct hostapd_frame_info *fi) +{ + 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(mgmt->u.beacon.variable, + len - (IEEE80211_HDRLEN + + sizeof(mgmt->u.beacon)), &elems, + 0); + + ap_list_process_beacon(hapd->iface, mgmt, &elems, fi); +} + + +#ifdef CONFIG_IEEE80211W + +/* MLME-SAQuery.request */ +void ieee802_11_send_sa_query_req(struct hostapd_data *hapd, + const u8 *addr, const u8 *trans_id) +{ + struct ieee80211_mgmt mgmt; + u8 *end; + + os_memset(&mgmt, 0, sizeof(mgmt)); + mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, + WLAN_FC_STYPE_ACTION); + os_memcpy(mgmt.da, addr, ETH_ALEN); + os_memcpy(mgmt.sa, hapd->own_addr, ETH_ALEN); + os_memcpy(mgmt.bssid, hapd->own_addr, ETH_ALEN); + mgmt.u.action.category = WLAN_ACTION_SA_QUERY; + mgmt.u.action.u.sa_query_req.action = WLAN_SA_QUERY_REQUEST; + os_memcpy(mgmt.u.action.u.sa_query_req.trans_id, trans_id, + WLAN_SA_QUERY_TR_ID_LEN); + end = mgmt.u.action.u.sa_query_req.trans_id + WLAN_SA_QUERY_TR_ID_LEN; + if (hostapd_send_mgmt_frame(hapd, &mgmt, end - (u8 *) &mgmt, 0) < 0) + perror("ieee802_11_send_sa_query_req: send"); +} + + +static void hostapd_sa_query_action(struct hostapd_data *hapd, + struct ieee80211_mgmt *mgmt, size_t len) +{ + struct sta_info *sta; + u8 *end; + int i; + + end = mgmt->u.action.u.sa_query_resp.trans_id + + WLAN_SA_QUERY_TR_ID_LEN; + if (((u8 *) mgmt) + len < end) { + wpa_printf(MSG_DEBUG, "IEEE 802.11: Too short SA Query Action " + "frame (len=%lu)", (unsigned long) len); + return; + } + + if (mgmt->u.action.u.sa_query_resp.action != WLAN_SA_QUERY_RESPONSE) { + wpa_printf(MSG_DEBUG, "IEEE 802.11: Unexpected SA Query " + "Action %d", mgmt->u.action.u.sa_query_resp.action); + return; + } + + /* MLME-SAQuery.confirm */ + + sta = ap_get_sta(hapd, mgmt->sa); + if (sta == NULL || sta->sa_query_trans_id == NULL) { + wpa_printf(MSG_DEBUG, "IEEE 802.11: No matching STA with " + "pending SA Query request found"); + return; + } + + for (i = 0; i < sta->sa_query_count; i++) { + if (os_memcmp(sta->sa_query_trans_id + + i * WLAN_SA_QUERY_TR_ID_LEN, + mgmt->u.action.u.sa_query_resp.trans_id, + WLAN_SA_QUERY_TR_ID_LEN) == 0) + break; + } + + if (i >= sta->sa_query_count) { + wpa_printf(MSG_DEBUG, "IEEE 802.11: No matching SA Query " + "transaction identifier found"); + return; + } + + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "Reply to pending SA Query received"); + ap_sta_stop_sa_query(hapd, sta); +} +#endif /* CONFIG_IEEE80211W */ + + +static void handle_action(struct hostapd_data *hapd, + struct ieee80211_mgmt *mgmt, size_t len) +{ + if (len < IEEE80211_HDRLEN + 1) { + hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "handle_action - too short payload (len=%lu)", + (unsigned long) len); + return; + } + + switch (mgmt->u.action.category) { +#ifdef CONFIG_IEEE80211R + case WLAN_ACTION_FT: + { + struct sta_info *sta; + + sta = ap_get_sta(hapd, mgmt->sa); + if (sta == NULL || !(sta->flags & WLAN_STA_ASSOC)) { + wpa_printf(MSG_DEBUG, "IEEE 802.11: Ignored FT Action " + "frame from unassociated STA " MACSTR, + MAC2STR(mgmt->sa)); + return; + } + + if (wpa_ft_action_rx(sta->wpa_sm, (u8 *) &mgmt->u.action, + len - IEEE80211_HDRLEN)) + break; + + return; + } +#endif /* CONFIG_IEEE80211R */ + case WLAN_ACTION_WMM: + hostapd_wme_action(hapd, mgmt, len); + return; +#ifdef CONFIG_IEEE80211W + case WLAN_ACTION_SA_QUERY: + hostapd_sa_query_action(hapd, mgmt, len); + return; +#endif /* CONFIG_IEEE80211W */ + } + + hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "handle_action - unknown action category %d or invalid " + "frame", + mgmt->u.action.category); + if (!(mgmt->da[0] & 0x01) && !(mgmt->u.action.category & 0x80) && + !(mgmt->sa[0] & 0x01)) { + /* + * IEEE 802.11-REVma/D9.0 - 7.3.1.11 + * Return the Action frame to the source without change + * except that MSB of the Category set to 1. + */ + wpa_printf(MSG_DEBUG, "IEEE 802.11: Return unknown Action " + "frame back to sender"); + os_memcpy(mgmt->da, mgmt->sa, ETH_ALEN); + os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN); + os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN); + mgmt->u.action.category |= 0x80; + + hostapd_send_mgmt_frame(hapd, mgmt, len, 0); + } +} + + +/** + * ieee802_11_mgmt - process incoming IEEE 802.11 management frames + * @hapd: hostapd BSS data structure (the BSS to which the management frame was + * sent to) + * @buf: management frame data (starting from IEEE 802.11 header) + * @len: length of frame data in octets + * @stype: management frame subtype from frame control field + * @fi: meta data about received frame (signal level, etc.) + * + * Process all incoming IEEE 802.11 management frames. This will be called for + * each frame received from the kernel driver through wlan#ap interface. In + * addition, it can be called to re-inserted pending frames (e.g., when using + * external RADIUS server as an MAC ACL). + */ +void ieee802_11_mgmt(struct hostapd_data *hapd, u8 *buf, size_t len, u16 stype, + struct hostapd_frame_info *fi) +{ + struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *) buf; + int broadcast; + + if (stype == WLAN_FC_STYPE_BEACON) { + handle_beacon(hapd, mgmt, len, fi); + return; + } + + if (fi && fi->passive_scan) + return; + + broadcast = mgmt->bssid[0] == 0xff && mgmt->bssid[1] == 0xff && + mgmt->bssid[2] == 0xff && mgmt->bssid[3] == 0xff && + mgmt->bssid[4] == 0xff && mgmt->bssid[5] == 0xff; + + if (!broadcast && + os_memcmp(mgmt->bssid, hapd->own_addr, ETH_ALEN) != 0) { + printf("MGMT: BSSID=" MACSTR " not our address\n", + MAC2STR(mgmt->bssid)); + return; + } + + + if (stype == WLAN_FC_STYPE_PROBE_REQ) { + handle_probe_req(hapd, mgmt, len); + return; + } + + if (os_memcmp(mgmt->da, hapd->own_addr, ETH_ALEN) != 0) { + hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "MGMT: DA=" MACSTR " not our address", + MAC2STR(mgmt->da)); + return; + } + + switch (stype) { + case WLAN_FC_STYPE_AUTH: + wpa_printf(MSG_DEBUG, "mgmt::auth"); + handle_auth(hapd, mgmt, len); + break; + case WLAN_FC_STYPE_ASSOC_REQ: + wpa_printf(MSG_DEBUG, "mgmt::assoc_req"); + handle_assoc(hapd, mgmt, len, 0); + break; + case WLAN_FC_STYPE_REASSOC_REQ: + wpa_printf(MSG_DEBUG, "mgmt::reassoc_req"); + handle_assoc(hapd, mgmt, len, 1); + break; + case WLAN_FC_STYPE_DISASSOC: + wpa_printf(MSG_DEBUG, "mgmt::disassoc"); + handle_disassoc(hapd, mgmt, len); + break; + case WLAN_FC_STYPE_DEAUTH: + wpa_printf(MSG_DEBUG, "mgmt::deauth"); + handle_deauth(hapd, mgmt, len); + break; + case WLAN_FC_STYPE_ACTION: + wpa_printf(MSG_DEBUG, "mgmt::action"); + handle_action(hapd, mgmt, len); + break; + default: + hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "unknown mgmt frame subtype %d", stype); + break; + } +} + + +static void handle_auth_cb(struct hostapd_data *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(struct hostapd_data *hapd, + struct ieee80211_mgmt *mgmt, + size_t len, int reassoc, int ok) +{ + u16 status; + struct sta_info *sta; + int new_assoc = 1; + struct ht_cap_ie *ht_cap = NULL; + + 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_resp) : + sizeof(mgmt->u.assoc_resp))) { + 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); + + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_INFO, + "associated (aid %d)", + sta->aid); + + if (sta->flags & WLAN_STA_ASSOC) + new_assoc = 0; + sta->flags |= WLAN_STA_ASSOC; + + if (reassoc) + mlme_reassociate_indication(hapd, sta); + else + mlme_associate_indication(hapd, sta); + +#ifdef CONFIG_IEEE80211N + if (sta->flags & WLAN_STA_HT) + ht_cap = &sta->ht_capabilities; +#endif /* CONFIG_IEEE80211N */ + +#ifdef CONFIG_IEEE80211W + sta->sa_query_timed_out = 0; +#endif /* CONFIG_IEEE80211W */ + + if (hostapd_sta_add(hapd->conf->iface, hapd, sta->addr, sta->aid, + sta->capability, sta->supported_rates, + sta->supported_rates_len, 0, sta->listen_interval, + ht_cap)) + { + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_NOTICE, + "Could not add STA to kernel driver"); + } + + if (sta->eapol_sm == NULL) { + /* + * This STA does not use RADIUS server for EAP authentication, + * so bind it to the selected VLAN interface now, since the + * interface selection is not going to change anymore. + */ + ap_sta_bind_vlan(hapd, sta, 0); + } else if (sta->vlan_id) { + /* VLAN ID already set (e.g., by PMKSA caching), so bind STA */ + ap_sta_bind_vlan(hapd, sta, 0); + } + if (sta->flags & WLAN_STA_SHORT_PREAMBLE) { + hostapd_sta_set_flags(hapd, sta->addr, sta->flags, + WLAN_STA_SHORT_PREAMBLE, ~0); + } else { + hostapd_sta_set_flags(hapd, sta->addr, sta->flags, + 0, ~WLAN_STA_SHORT_PREAMBLE); + } + + if (sta->auth_alg == WLAN_AUTH_FT) + wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC_FT); + else + wpa_auth_sm_event(sta->wpa_sm, 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) { + os_free(sta->last_assoc_req); + sta->last_assoc_req = NULL; + } +} + + +/** + * ieee802_11_mgmt_cb - Process management frame TX status callback + * @hapd: hostapd BSS data structure (the BSS from which the management frame + * was sent from) + * @buf: management frame data (starting from IEEE 802.11 header) + * @len: length of frame data in octets + * @stype: management frame subtype from frame control field + * @ok: Whether the frame was ACK'ed + */ +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: + wpa_printf(MSG_DEBUG, "mgmt::auth cb"); + handle_auth_cb(hapd, mgmt, len, ok); + break; + case WLAN_FC_STYPE_ASSOC_RESP: + wpa_printf(MSG_DEBUG, "mgmt::assoc_resp cb"); + handle_assoc_cb(hapd, mgmt, len, 0, ok); + break; + case WLAN_FC_STYPE_REASSOC_RESP: + wpa_printf(MSG_DEBUG, "mgmt::reassoc_resp cb"); + handle_assoc_cb(hapd, mgmt, len, 1, ok); + break; + case WLAN_FC_STYPE_PROBE_RESP: + wpa_printf(MSG_DEBUG, "mgmt::proberesp cb"); + break; + case WLAN_FC_STYPE_DEAUTH: + /* ignore */ + break; + case WLAN_FC_STYPE_ACTION: + wpa_printf(MSG_DEBUG, "mgmt::action cb"); + 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"); + + wpa_auth_countermeasures_start(hapd->wpa_auth); + hapd->tkip_countermeasures = 1; + hostapd_set_countermeasures(hapd, 1); + wpa_gtk_rekey(hapd->wpa_auth); + 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, const u8 *addr, + int local) +{ + time_t now; + + if (addr && local) { + struct sta_info *sta = ap_get_sta(hapd, addr); + if (sta != NULL) { + wpa_auth_sta_local_mic_failure_report(sta->wpa_sm); + hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_INFO, + "Michael MIC failure detected in " + "received frame"); + mlme_michaelmicfailure_indication(hapd, addr); + } else { + wpa_printf(MSG_DEBUG, + "MLME-MICHAELMICFAILURE.indication " + "for not associated STA (" MACSTR + ") ignored", 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; +} + +#endif /* CONFIG_NATIVE_WINDOWS */ diff --git a/contrib/wpa/hostapd/ieee802_11.h b/contrib/wpa/hostapd/ieee802_11.h new file mode 100644 index 0000000..ca8ef93 --- /dev/null +++ b/contrib/wpa/hostapd/ieee802_11.h @@ -0,0 +1,56 @@ +/* + * hostapd / IEEE 802.11 Management + * Copyright (c) 2002-2006, Jouni Malinen <j@w1.fi> + * Copyright (c) 2007-2008, Intel Corporation + * + * 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 IEEE802_11_H +#define IEEE802_11_H + +#include "ieee802_11_defs.h" +#include "ieee802_11_common.h" + +struct hostapd_frame_info { + u32 phytype; + u32 channel; + u32 datarate; + u32 ssi_signal; + + unsigned int passive_scan:1; +}; + +struct hostapd_iface; +struct hostapd_data; +struct sta_info; + +void ieee802_11_send_deauth(struct hostapd_data *hapd, u8 *addr, u16 reason); +void ieee802_11_mgmt(struct hostapd_data *hapd, u8 *buf, size_t len, + u16 stype, struct hostapd_frame_info *fi); +void ieee802_11_mgmt_cb(struct hostapd_data *hapd, u8 *buf, size_t len, + u16 stype, int ok); +void ieee802_11_print_ssid(char *buf, const u8 *ssid, u8 len); +void ieee80211_michael_mic_failure(struct hostapd_data *hapd, const 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); +u16 hostapd_own_capab_info(struct hostapd_data *hapd, struct sta_info *sta, + int probe); +u8 * hostapd_eid_supp_rates(struct hostapd_data *hapd, u8 *eid); +u8 * hostapd_eid_ext_supp_rates(struct hostapd_data *hapd, u8 *eid); +u8 * hostapd_eid_ht_capabilities_info(struct hostapd_data *hapd, u8 *eid); +u8 * hostapd_eid_ht_operation(struct hostapd_data *hapd, u8 *eid); +int hostapd_ht_operation_update(struct hostapd_iface *iface); +void ieee802_11_send_sa_query_req(struct hostapd_data *hapd, + const u8 *addr, const u8 *trans_id); + +#endif /* IEEE802_11_H */ diff --git a/contrib/wpa/hostapd/ieee802_11_auth.c b/contrib/wpa/hostapd/ieee802_11_auth.c new file mode 100644 index 0000000..9aba1fe --- /dev/null +++ b/contrib/wpa/hostapd/ieee802_11_auth.c @@ -0,0 +1,523 @@ +/* + * hostapd / IEEE 802.11 authentication (ACL) + * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + * + * Access control list for IEEE 802.11 authentication can uses statically + * configured ACL from configuration files or an external RADIUS server. + * Results from external RADIUS queries are cached to allow faster + * authentication frame processing. + */ + +#include "includes.h" + +#ifndef CONFIG_NATIVE_WINDOWS + +#include "hostapd.h" +#include "ieee802_11.h" +#include "ieee802_11_auth.h" +#include "radius/radius.h" +#include "radius/radius_client.h" +#include "eloop.h" +#include "driver.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; + int vlan_id; +}; + + +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; + os_free(prev); + } +} + + +static int hostapd_acl_cache_get(struct hostapd_data *hapd, const u8 *addr, + u32 *session_timeout, + u32 *acct_interim_interval, int *vlan_id) +{ + struct hostapd_cached_radius_acl *entry; + time_t now; + + time(&now); + entry = hapd->acl_cache; + + while (entry) { + if (os_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) + if (session_timeout) + *session_timeout = + entry->session_timeout; + if (acct_interim_interval) + *acct_interim_interval = + entry->acct_interim_interval; + if (vlan_id) + *vlan_id = entry->vlan_id; + return entry->accepted; + } + + entry = entry->next; + } + + return -1; +} + + +static void hostapd_acl_query_free(struct hostapd_acl_query_data *query) +{ + if (query == NULL) + return; + os_free(query->auth_msg); + os_free(query); +} + + +static int hostapd_radius_acl_query(struct hostapd_data *hapd, const 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); + + os_snprintf(buf, sizeof(buf), RADIUS_ADDR_FORMAT, MAC2STR(addr)); + if (!radius_msg_add_attr(msg, RADIUS_ATTR_USER_NAME, (u8 *) buf, + os_strlen(buf))) { + wpa_printf(MSG_DEBUG, "Could not add User-Name"); + goto fail; + } + + if (!radius_msg_add_attr_user_password( + msg, (u8 *) buf, os_strlen(buf), + hapd->conf->radius->auth_server->shared_secret, + hapd->conf->radius->auth_server->shared_secret_len)) { + wpa_printf(MSG_DEBUG, "Could not add User-Password"); + 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)) { + wpa_printf(MSG_DEBUG, "Could not add NAS-IP-Address"); + 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)) { + wpa_printf(MSG_DEBUG, "Could not add NAS-IPv6-Address"); + goto fail; + } +#endif /* CONFIG_IPV6 */ + + if (hapd->conf->nas_identifier && + !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IDENTIFIER, + (u8 *) hapd->conf->nas_identifier, + os_strlen(hapd->conf->nas_identifier))) { + wpa_printf(MSG_DEBUG, "Could not add NAS-Identifier"); + goto fail; + } + + os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT ":%s", + MAC2STR(hapd->own_addr), hapd->conf->ssid.ssid); + if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLED_STATION_ID, + (u8 *) buf, os_strlen(buf))) { + wpa_printf(MSG_DEBUG, "Could not add Called-Station-Id"); + goto fail; + } + + os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT, + MAC2STR(addr)); + if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLING_STATION_ID, + (u8 *) buf, os_strlen(buf))) { + wpa_printf(MSG_DEBUG, "Could not add Calling-Station-Id"); + goto fail; + } + + if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_NAS_PORT_TYPE, + RADIUS_NAS_PORT_TYPE_IEEE_802_11)) { + wpa_printf(MSG_DEBUG, "Could not add NAS-Port-Type"); + goto fail; + } + + os_snprintf(buf, sizeof(buf), "CONNECT 11Mbps 802.11b"); + if (!radius_msg_add_attr(msg, RADIUS_ATTR_CONNECT_INFO, + (u8 *) buf, os_strlen(buf))) { + wpa_printf(MSG_DEBUG, "Could not add Connect-Info"); + goto fail; + } + + radius_client_send(hapd->radius, msg, RADIUS_AUTH, addr); + return 0; + + fail: + radius_msg_free(msg); + os_free(msg); + return -1; +} + + +/** + * hostapd_allowed_address - Check whether a specified STA can be authenticated + * @hapd: hostapd BSS data + * @addr: MAC address of the STA + * @msg: Authentication message + * @len: Length of msg in octets + * @session_timeout: Buffer for returning session timeout (from RADIUS) + * @acct_interim_interval: Buffer for returning account interval (from RADIUS) + * @vlan_id: Buffer for returning VLAN ID + * Returns: HOSTAPD_ACL_ACCEPT, HOSTAPD_ACL_REJECT, or HOSTAPD_ACL_PENDING + */ +int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr, + const u8 *msg, size_t len, u32 *session_timeout, + u32 *acct_interim_interval, int *vlan_id) +{ + if (session_timeout) + *session_timeout = 0; + if (acct_interim_interval) + *acct_interim_interval = 0; + if (vlan_id) + *vlan_id = 0; + + if (hostapd_maclist_found(hapd->conf->accept_mac, + hapd->conf->num_accept_mac, addr, vlan_id)) + return HOSTAPD_ACL_ACCEPT; + + if (hostapd_maclist_found(hapd->conf->deny_mac, + hapd->conf->num_deny_mac, addr, vlan_id)) + 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, + vlan_id); + 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 (os_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 = os_zalloc(sizeof(*query)); + if (query == NULL) { + wpa_printf(MSG_ERROR, "malloc for query data failed"); + return HOSTAPD_ACL_REJECT; + } + time(&query->timestamp); + os_memcpy(query->addr, addr, ETH_ALEN); + if (hostapd_radius_acl_query(hapd, addr, query)) { + wpa_printf(MSG_DEBUG, "Failed to send Access-Request " + "for ACL query."); + hostapd_acl_query_free(query); + return HOSTAPD_ACL_REJECT; + } + + query->auth_msg = os_malloc(len); + if (query->auth_msg == NULL) { + wpa_printf(MSG_ERROR, "Failed to allocate memory for " + "auth frame."); + hostapd_acl_query_free(query); + return HOSTAPD_ACL_REJECT; + } + os_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(struct hostapd_data *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) { + wpa_printf(MSG_DEBUG, "Cached ACL entry for " MACSTR + " has expired.", MAC2STR(entry->addr)); + if (prev) + prev->next = entry->next; + else + hapd->acl_cache = entry->next; +#ifdef CONFIG_DRIVER_RADIUS_ACL + hostapd_set_radius_acl_expire(hapd, entry->addr); +#endif /* CONFIG_DRIVER_RADIUS_ACL */ + tmp = entry; + entry = entry->next; + os_free(tmp); + continue; + } + + prev = entry; + entry = entry->next; + } +} + + +static void hostapd_acl_expire_queries(struct hostapd_data *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) { + wpa_printf(MSG_DEBUG, "ACL query for " MACSTR + " has expired.", 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; + } +} + + +/** + * hostapd_acl_expire - ACL cache expiration callback + * @eloop_ctx: struct hostapd_data * + * @timeout_ctx: Not used + */ +static void hostapd_acl_expire(void *eloop_ctx, void *timeout_ctx) +{ + struct hostapd_data *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); +} + + +/** + * hostapd_acl_recv_radius - Process incoming RADIUS Authentication messages + * @msg: RADIUS response message + * @req: RADIUS request message + * @shared_secret: RADIUS shared secret + * @shared_secret_len: Length of shared_secret in octets + * @data: Context data (struct hostapd_data *) + * Returns: RADIUS_RX_PROCESSED if RADIUS message was a reply to ACL query (and + * was processed here) or RADIUS_RX_UNKNOWN if not. + */ +static RadiusRxResult +hostapd_acl_recv_radius(struct radius_msg *msg, struct radius_msg *req, + const 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; + + wpa_printf(MSG_DEBUG, "Found matching Access-Request for RADIUS " + "message (id=%d)", query->radius_id); + + if (radius_msg_verify(msg, shared_secret, shared_secret_len, req, 0)) { + wpa_printf(MSG_INFO, "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) { + wpa_printf(MSG_DEBUG, "Unknown RADIUS message code %d to ACL " + "query", msg->hdr->code); + return RADIUS_RX_UNKNOWN; + } + + /* Insert Accept/Reject info into ACL cache */ + cache = os_zalloc(sizeof(*cache)); + if (cache == NULL) { + wpa_printf(MSG_DEBUG, "Failed to add ACL cache entry"); + goto done; + } + time(&cache->timestamp); + os_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) { + wpa_printf(MSG_DEBUG, "Ignored too small " + "Acct-Interim-Interval %d for STA " MACSTR, + cache->acct_interim_interval, + MAC2STR(query->addr)); + cache->acct_interim_interval = 0; + } + + cache->vlan_id = radius_msg_get_vlanid(msg); + } else + cache->accepted = HOSTAPD_ACL_REJECT; + cache->next = hapd->acl_cache; + hapd->acl_cache = cache; + +#ifdef CONFIG_DRIVER_RADIUS_ACL + hostapd_set_radius_acl_auth(hapd, query->addr, cache->accepted, + cache->session_timeout); +#else /* CONFIG_DRIVER_RADIUS_ACL */ + /* Re-send original authentication frame for 802.11 processing */ + wpa_printf(MSG_DEBUG, "Re-sending authentication frame after " + "successful RADIUS ACL query"); + ieee802_11_mgmt(hapd, query->auth_msg, query->auth_msg_len, + WLAN_FC_STYPE_AUTH, NULL); +#endif /* CONFIG_DRIVER_RADIUS_ACL */ + + done: + if (prev == NULL) + hapd->acl_queries = query->next; + else + prev->next = query->next; + + hostapd_acl_query_free(query); + + return RADIUS_RX_PROCESSED; +} + + +/** + * hostapd_acl_init: Initialize IEEE 802.11 ACL + * @hapd: hostapd BSS data + * Returns: 0 on success, -1 on failure + */ +int hostapd_acl_init(struct hostapd_data *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; +} + + +/** + * hostapd_acl_deinit - Deinitialize IEEE 802.11 ACL + * @hapd: hostapd BSS data + */ +void hostapd_acl_deinit(struct hostapd_data *hapd) +{ + struct hostapd_acl_query_data *query, *prev; + + eloop_cancel_timeout(hostapd_acl_expire, hapd, NULL); + + hostapd_acl_cache_free(hapd->acl_cache); + + query = hapd->acl_queries; + while (query) { + prev = query; + query = query->next; + hostapd_acl_query_free(prev); + } +} + + +int hostapd_acl_reconfig(struct hostapd_data *hapd, + struct hostapd_config *oldconf) +{ + if (!hapd->radius_client_reconfigured) + return 0; + + hostapd_acl_deinit(hapd); + return hostapd_acl_init(hapd); +} + +#endif /* CONFIG_NATIVE_WINDOWS */ diff --git a/contrib/wpa/hostapd/ieee802_11_auth.h b/contrib/wpa/hostapd/ieee802_11_auth.h new file mode 100644 index 0000000..0eed825 --- /dev/null +++ b/contrib/wpa/hostapd/ieee802_11_auth.h @@ -0,0 +1,33 @@ +/* + * hostapd / IEEE 802.11 authentication (ACL) + * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#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(struct hostapd_data *hapd, const u8 *addr, + const u8 *msg, size_t len, u32 *session_timeout, + u32 *acct_interim_interval, int *vlan_id); +int hostapd_acl_init(struct hostapd_data *hapd); +void hostapd_acl_deinit(struct hostapd_data *hapd); +int hostapd_acl_reconfig(struct hostapd_data *hapd, + struct hostapd_config *oldconf); + +#endif /* IEEE802_11_AUTH_H */ diff --git a/contrib/wpa/hostapd/ieee802_1x.c b/contrib/wpa/hostapd/ieee802_1x.c new file mode 100644 index 0000000..9b096d4 --- /dev/null +++ b/contrib/wpa/hostapd/ieee802_1x.c @@ -0,0 +1,2042 @@ +/* + * hostapd / IEEE 802.1X-2004 Authenticator + * Copyright (c) 2002-2008, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "hostapd.h" +#include "ieee802_1x.h" +#include "accounting.h" +#include "radius/radius.h" +#include "radius/radius_client.h" +#include "eapol_sm.h" +#include "md5.h" +#include "rc4.h" +#include "eloop.h" +#include "sta_info.h" +#include "wpa.h" +#include "preauth.h" +#include "pmksa_cache.h" +#include "driver.h" +#include "hw_features.h" +#include "eap_server/eap.h" +#include "ieee802_11_defs.h" + + +static void ieee802_1x_finished(struct hostapd_data *hapd, + struct sta_info *sta, int success); + + +static void ieee802_1x_send(struct hostapd_data *hapd, struct sta_info *sta, + u8 type, const u8 *data, size_t datalen) +{ + u8 *buf; + struct ieee802_1x_hdr *xhdr; + size_t len; + int encrypt = 0; + + len = sizeof(*xhdr) + datalen; + buf = os_zalloc(len); + if (buf == NULL) { + wpa_printf(MSG_ERROR, "malloc() failed for " + "ieee802_1x_send(len=%lu)", + (unsigned long) len); + return; + } + + xhdr = (struct ieee802_1x_hdr *) buf; + xhdr->version = hapd->conf->eapol_version; + xhdr->type = type; + xhdr->length = host_to_be16(datalen); + + if (datalen > 0 && data != NULL) + os_memcpy(xhdr + 1, data, datalen); + + if (wpa_auth_pairwise_set(sta->wpa_sm)) + 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); + } + + os_free(buf); +} + + +void ieee802_1x_set_sta_authorized(struct hostapd_data *hapd, + struct sta_info *sta, int authorized) +{ + int res; + + if (sta->flags & WLAN_STA_PREAUTH) + return; + + if (authorized) { + sta->flags |= WLAN_STA_AUTHORIZED; + res = hostapd_sta_set_flags(hapd, sta->addr, sta->flags, + WLAN_STA_AUTHORIZED, ~0); + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, + HOSTAPD_LEVEL_DEBUG, "authorizing port"); + } else { + sta->flags &= ~WLAN_STA_AUTHORIZED; + res = hostapd_sta_set_flags(hapd, sta->addr, sta->flags, + 0, ~WLAN_STA_AUTHORIZED); + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, + HOSTAPD_LEVEL_DEBUG, "unauthorizing port"); + } + + if (res && errno != ENOENT) { + printf("Could not set station " MACSTR " flags for kernel " + "driver (errno=%d).\n", MAC2STR(sta->addr), errno); + } + + if (authorized) + accounting_sta_start(hapd, sta); +} + + +static void ieee802_1x_tx_key_one(struct hostapd_data *hapd, + struct sta_info *sta, + int idx, 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 = os_zalloc(sizeof(*hdr) + len); + if (buf == NULL) + return; + + 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); + wpa_get_ntp_timestamp(key->replay_counter); + + if (os_get_random(key->key_iv, sizeof(key->key_iv))) { + wpa_printf(MSG_ERROR, "Could not get random numbers"); + os_free(buf); + return; + } + + key->key_index = idx | (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 + MSK[0..31]" as the RC4-key and + * MSK[32..63] is used to sign the message. */ + if (sm->eap_if->eapKeyData == NULL || sm->eap_if->eapKeyDataLen < 64) { + wpa_printf(MSG_ERROR, "No eapKeyData available for encrypting " + "and signing EAPOL-Key"); + os_free(buf); + return; + } + os_memcpy((u8 *) (key + 1), key_data, key_len); + ekey_len = sizeof(key->key_iv) + 32; + ekey = os_malloc(ekey_len); + if (ekey == NULL) { + wpa_printf(MSG_ERROR, "Could not encrypt key"); + os_free(buf); + return; + } + os_memcpy(ekey, key->key_iv, sizeof(key->key_iv)); + os_memcpy(ekey + sizeof(key->key_iv), sm->eap_if->eapKeyData, 32); + rc4((u8 *) (key + 1), key_len, ekey, ekey_len); + os_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 = host_to_be16(len); + hmac_md5(sm->eap_if->eapKeyData + 32, 32, buf, sizeof(*hdr) + len, + key->key_signature); + + wpa_printf(MSG_DEBUG, "IEEE 802.1X: Sending EAPOL-Key to " MACSTR + " (%s index=%d)", MAC2STR(sm->addr), + broadcast ? "broadcast" : "unicast", idx); + ieee802_1x_send(hapd, sta, IEEE802_1X_TYPE_EAPOL_KEY, (u8 *) key, len); + if (sta->eapol_sm) + sta->eapol_sm->dot1xAuthEapolFramesTx++; + os_free(buf); +} + + +static struct hostapd_wep_keys * +ieee802_1x_group_alloc(struct hostapd_data *hapd, const char *ifname) +{ + struct hostapd_wep_keys *key; + + key = os_zalloc(sizeof(*key)); + if (key == NULL) + return NULL; + + key->default_len = hapd->conf->default_wep_key_len; + + if (key->idx >= hapd->conf->broadcast_key_idx_max || + key->idx < hapd->conf->broadcast_key_idx_min) + key->idx = hapd->conf->broadcast_key_idx_min; + else + key->idx++; + + if (!key->key[key->idx]) + key->key[key->idx] = os_malloc(key->default_len); + if (key->key[key->idx] == NULL || + os_get_random(key->key[key->idx], key->default_len)) { + printf("Could not generate random WEP key (dynamic VLAN).\n"); + os_free(key->key[key->idx]); + key->key[key->idx] = NULL; + os_free(key); + return NULL; + } + key->len[key->idx] = key->default_len; + + wpa_printf(MSG_DEBUG, "%s: Default WEP idx %d for dynamic VLAN\n", + ifname, key->idx); + wpa_hexdump_key(MSG_DEBUG, "Default WEP key (dynamic VLAN)", + key->key[key->idx], key->len[key->idx]); + + if (hostapd_set_encryption(ifname, hapd, "WEP", NULL, key->idx, + key->key[key->idx], key->len[key->idx], 1)) + printf("Could not set dynamic VLAN WEP encryption key.\n"); + + hostapd_set_ieee8021x(ifname, hapd, 1); + + return key; +} + + +static struct hostapd_wep_keys * +ieee802_1x_get_group(struct hostapd_data *hapd, struct hostapd_ssid *ssid, + size_t vlan_id) +{ + const char *ifname; + + if (vlan_id == 0) + return &ssid->wep; + + if (vlan_id <= ssid->max_dyn_vlan_keys && ssid->dyn_vlan_keys && + ssid->dyn_vlan_keys[vlan_id]) + return ssid->dyn_vlan_keys[vlan_id]; + + wpa_printf(MSG_DEBUG, "IEEE 802.1X: Creating new group " + "state machine for VLAN ID %lu", + (unsigned long) vlan_id); + + ifname = hostapd_get_vlan_id_ifname(hapd->conf->vlan, vlan_id); + if (ifname == NULL) { + wpa_printf(MSG_DEBUG, "IEEE 802.1X: Unknown VLAN ID %lu - " + "cannot create group key state machine", + (unsigned long) vlan_id); + return NULL; + } + + if (ssid->dyn_vlan_keys == NULL) { + int size = (vlan_id + 1) * sizeof(ssid->dyn_vlan_keys[0]); + ssid->dyn_vlan_keys = os_zalloc(size); + if (ssid->dyn_vlan_keys == NULL) + return NULL; + ssid->max_dyn_vlan_keys = vlan_id; + } + + if (ssid->max_dyn_vlan_keys < vlan_id) { + struct hostapd_wep_keys **na; + int size = (vlan_id + 1) * sizeof(ssid->dyn_vlan_keys[0]); + na = os_realloc(ssid->dyn_vlan_keys, size); + if (na == NULL) + return NULL; + ssid->dyn_vlan_keys = na; + os_memset(&ssid->dyn_vlan_keys[ssid->max_dyn_vlan_keys + 1], 0, + (vlan_id - ssid->max_dyn_vlan_keys) * + sizeof(ssid->dyn_vlan_keys[0])); + ssid->max_dyn_vlan_keys = vlan_id; + } + + ssid->dyn_vlan_keys[vlan_id] = ieee802_1x_group_alloc(hapd, ifname); + + return ssid->dyn_vlan_keys[vlan_id]; +} + + +void ieee802_1x_tx_key(struct hostapd_data *hapd, struct sta_info *sta) +{ + struct hostapd_wep_keys *key = NULL; + struct eapol_state_machine *sm = sta->eapol_sm; + int vlan_id; + + if (sm == NULL || !sm->eap_if->eapKeyData) + return; + + wpa_printf(MSG_DEBUG, "IEEE 802.1X: Sending EAPOL-Key(s) to " MACSTR, + MAC2STR(sta->addr)); + + vlan_id = sta->vlan_id; + if (vlan_id < 0 || vlan_id > MAX_VLAN_ID) + vlan_id = 0; + + if (vlan_id) { + key = ieee802_1x_get_group(hapd, sta->ssid, vlan_id); + if (key && key->key[key->idx]) + ieee802_1x_tx_key_one(hapd, sta, key->idx, 1, + key->key[key->idx], + key->len[key->idx]); + } else 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 = os_malloc(hapd->conf->individual_wep_key_len); + if (ikey == NULL || + os_get_random(ikey, hapd->conf->individual_wep_key_len)) { + wpa_printf(MSG_ERROR, "Could not generate random " + "individual WEP key."); + os_free(ikey); + return; + } + + wpa_hexdump_key(MSG_DEBUG, "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->conf->iface, hapd, "WEP", + sta->addr, 0, ikey, + hapd->conf->individual_wep_key_len, + 1)) { + wpa_printf(MSG_ERROR, "Could not set individual WEP " + "encryption."); + } + + os_free(ikey); + } +} + + +const char *radius_mode_txt(struct hostapd_data *hapd) +{ + if (hapd->iface->current_mode == NULL) + return "802.11"; + + switch (hapd->iface->current_mode->mode) { + case HOSTAPD_MODE_IEEE80211A: + return "802.11a"; + case HOSTAPD_MODE_IEEE80211G: + return "802.11g"; + case HOSTAPD_MODE_IEEE80211B: + default: + return "802.11b"; + } +} + + +int radius_sta_rate(struct hostapd_data *hapd, struct sta_info *sta) +{ + int i; + u8 rate = 0; + + for (i = 0; i < sta->supported_rates_len; i++) + if ((sta->supported_rates[i] & 0x7f) > rate) + rate = sta->supported_rates[i] & 0x7f; + + return rate; +} + + +static void ieee802_1x_learn_identity(struct hostapd_data *hapd, + struct eapol_state_machine *sm, + const u8 *eap, size_t len) +{ + const u8 *identity; + size_t identity_len; + + if (len <= sizeof(struct eap_hdr) || + eap[sizeof(struct eap_hdr)] != EAP_TYPE_IDENTITY) + return; + + identity = eap_get_identity(sm->eap, &identity_len); + if (identity == NULL) + return; + + /* Save station identity for future RADIUS packets */ + os_free(sm->identity); + sm->identity = os_malloc(identity_len + 1); + if (sm->identity == NULL) { + sm->identity_len = 0; + return; + } + + os_memcpy(sm->identity, identity, identity_len); + sm->identity_len = identity_len; + sm->identity[identity_len] = '\0'; + hostapd_logger(hapd, sm->addr, HOSTAPD_MODULE_IEEE8021X, + HOSTAPD_LEVEL_DEBUG, "STA identity '%s'", sm->identity); + sm->dot1xAuthEapolRespIdFramesRx++; +} + + +static void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd, + struct sta_info *sta, + const u8 *eap, size_t len) +{ + struct radius_msg *msg; + char buf[128]; + struct eapol_state_machine *sm = sta->eapol_sm; + + if (sm == NULL) + return; + + ieee802_1x_learn_identity(hapd, sm, eap, len); + + wpa_printf(MSG_DEBUG, "Encapsulating EAP message into a RADIUS " + "packet"); + + 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, + os_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; + } + + os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT ":%s", + MAC2STR(hapd->own_addr), hapd->conf->ssid.ssid); + buf[sizeof(buf) - 1] = '\0'; + if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLED_STATION_ID, + (u8 *) buf, os_strlen(buf))) { + printf("Could not add Called-Station-Id\n"); + goto fail; + } + + os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT, + MAC2STR(sta->addr)); + buf[sizeof(buf) - 1] = '\0'; + if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLING_STATION_ID, + (u8 *) buf, os_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; + } + + if (sta->flags & WLAN_STA_PREAUTH) { + os_strlcpy(buf, "IEEE 802.11i Pre-Authentication", + sizeof(buf)); + } else { + os_snprintf(buf, sizeof(buf), "CONNECT %d%sMbps %s", + radius_sta_rate(hapd, sta) / 2, + (radius_sta_rate(hapd, sta) & 1) ? ".5" : "", + radius_mode_txt(hapd)); + buf[sizeof(buf) - 1] = '\0'; + } + if (!radius_msg_add_attr(msg, RADIUS_ATTR_CONNECT_INFO, + (u8 *) buf, os_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) { + wpa_printf(MSG_DEBUG, "Copied RADIUS State Attribute"); + } + } + + radius_client_send(hapd->radius, msg, RADIUS_AUTH, sta->addr); + return; + + fail: + radius_msg_free(msg); + os_free(msg); +} + + +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"; + case EAP_TYPE_SIM: return "SIM"; + case EAP_TYPE_FAST: return "FAST"; + case EAP_TYPE_SAKE: return "SAKE"; + case EAP_TYPE_PSK: return "PSK"; + case EAP_TYPE_PAX: return "PAX"; + default: return "Unknown"; + } +} + + +static void handle_eap_response(struct hostapd_data *hapd, + struct sta_info *sta, struct eap_hdr *eap, + size_t len) +{ + u8 type, *data; + struct eapol_state_machine *sm = sta->eapol_sm; + if (sm == NULL) + return; + + data = (u8 *) (eap + 1); + + if (len < sizeof(*eap) + 1) { + printf("handle_eap_response: too short response data\n"); + return; + } + + sm->eap_type_supp = type = data[0]; + + 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, be_to_host16(eap->length), + eap_type_text(type), type); + + sm->dot1xAuthEapolRespFramesRx++; + + wpabuf_free(sm->eap_if->eapRespData); + sm->eap_if->eapRespData = wpabuf_alloc_copy(eap, len); + sm->eapolEap = TRUE; +} + + +/* Process incoming EAP packet from Supplicant */ +static void handle_eap(struct hostapd_data *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 = be_to_host16(eap->length); + wpa_printf(MSG_DEBUG, "EAP: code=%d identifier=%d length=%d", + eap->code, eap->identifier, eap_len); + if (eap_len < sizeof(*eap)) { + wpa_printf(MSG_DEBUG, " Invalid EAP length"); + return; + } else if (eap_len > len) { + wpa_printf(MSG_DEBUG, " Too short frame to contain this EAP " + "packet"); + return; + } else if (eap_len < len) { + wpa_printf(MSG_DEBUG, " Ignoring %lu extra bytes after EAP " + "packet", (unsigned long) len - eap_len); + } + + switch (eap->code) { + case EAP_CODE_REQUEST: + wpa_printf(MSG_DEBUG, " (request)"); + return; + case EAP_CODE_RESPONSE: + wpa_printf(MSG_DEBUG, " (response)"); + handle_eap_response(hapd, sta, eap, eap_len); + break; + case EAP_CODE_SUCCESS: + wpa_printf(MSG_DEBUG, " (success)"); + return; + case EAP_CODE_FAILURE: + wpa_printf(MSG_DEBUG, " (failure)"); + return; + default: + wpa_printf(MSG_DEBUG, " (unknown code)"); + return; + } +} + + +/** + * ieee802_1x_receive - Process the EAPOL frames from the Supplicant + * @hapd: hostapd BSS data + * @sa: Source address (sender of the EAPOL frame) + * @buf: EAPOL frame + * @len: Length of buf in octets + * + * This function is called for each incoming EAPOL frame from the interface + */ +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; + struct rsn_pmksa_cache_entry *pmksa; + + if (!hapd->conf->ieee802_1x && !hapd->conf->wpa && + !hapd->conf->wps_state) + return; + + wpa_printf(MSG_DEBUG, "IEEE 802.1X: %lu bytes from " MACSTR, + (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 = be_to_host16(hdr->length); + wpa_printf(MSG_DEBUG, " IEEE 802.1X: version=%d type=%d length=%d", + 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) { + wpa_printf(MSG_DEBUG, " ignoring %lu extra octets after " + "IEEE 802.1X packet", + (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->wpa_auth, sta->wpa_sm, (u8 *) hdr, + sizeof(*hdr) + datalen); + return; + } + + if ((!hapd->conf->ieee802_1x && + !(sta->flags & (WLAN_STA_WPS | WLAN_STA_MAYBE_WPS))) || + wpa_key_mgmt_wpa_psk(wpa_auth_sta_key_mgmt(sta->wpa_sm))) + return; + + if (!sta->eapol_sm) { + sta->eapol_sm = eapol_auth_alloc(hapd->eapol_auth, sta->addr, + sta->flags & WLAN_STA_PREAUTH, + sta); + if (!sta->eapol_sm) + return; + +#ifdef CONFIG_WPS + if (!hapd->conf->ieee802_1x && + ((sta->flags & (WLAN_STA_WPS | WLAN_STA_MAYBE_WPS)) == + WLAN_STA_MAYBE_WPS)) { + /* + * Delay EAPOL frame transmission until a possible WPS + * STA initiates the handshake with EAPOL-Start. + */ + sta->eapol_sm->flags |= EAPOL_SM_WAIT_START; + } +#endif /* CONFIG_WPS */ + + sta->eapol_sm->eap_if->portEnabled = TRUE; + } + + /* 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"); + sta->eapol_sm->flags &= ~EAPOL_SM_WAIT_START; + pmksa = wpa_auth_sta_get_pmksa(sta->wpa_sm); + if (pmksa) { + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, + HOSTAPD_LEVEL_DEBUG, "cached PMKSA " + "available - ignore it since " + "STA sent EAPOL-Start"); + wpa_auth_sta_clear_pmksa(sta->wpa_sm, pmksa); + } + sta->eapol_sm->eapolStart = TRUE; + sta->eapol_sm->dot1xAuthEapolStartFramesRx++; + wpa_auth_sm_event(sta->wpa_sm, 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; + accounting_sta_stop(hapd, sta); + sta->eapol_sm->eapolLogoff = TRUE; + sta->eapol_sm->dot1xAuthEapolLogoffFramesRx++; + break; + + case IEEE802_1X_TYPE_EAPOL_KEY: + wpa_printf(MSG_DEBUG, " EAPOL-Key"); + if (!(sta->flags & WLAN_STA_AUTHORIZED)) { + wpa_printf(MSG_DEBUG, " Dropped key data from " + "unauthorized Supplicant"); + break; + } + break; + + case IEEE802_1X_TYPE_EAPOL_ENCAPSULATED_ASF_ALERT: + wpa_printf(MSG_DEBUG, " EAPOL-Encapsulated-ASF-Alert"); + /* TODO: implement support for this; show data */ + break; + + default: + wpa_printf(MSG_DEBUG, " unknown IEEE 802.1X packet type"); + sta->eapol_sm->dot1xAuthInvalidEapolFramesRx++; + break; + } + + eapol_auth_step(sta->eapol_sm); +} + + +/** + * ieee802_1x_new_station - Start IEEE 802.1X authentication + * @hapd: hostapd BSS data + * @sta: The station + * + * This function is called to start IEEE 802.1X authentication when a new + * station completes IEEE 802.11 association. + */ +void ieee802_1x_new_station(struct hostapd_data *hapd, struct sta_info *sta) +{ + struct rsn_pmksa_cache_entry *pmksa; + int reassoc = 1; + int force_1x = 0; + +#ifdef CONFIG_WPS + if (hapd->conf->wps_state && hapd->conf->wpa && + (sta->flags & (WLAN_STA_WPS | WLAN_STA_MAYBE_WPS))) { + /* + * Need to enable IEEE 802.1X/EAPOL state machines for possible + * WPS handshake even if IEEE 802.1X/EAPOL is not used for + * authentication in this BSS. + */ + force_1x = 1; + } +#endif /* CONFIG_WPS */ + + if ((!force_1x && !hapd->conf->ieee802_1x) || + wpa_key_mgmt_wpa_psk(wpa_auth_sta_key_mgmt(sta->wpa_sm))) + return; + + if (sta->eapol_sm == NULL) { + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, + HOSTAPD_LEVEL_DEBUG, "start authentication"); + sta->eapol_sm = eapol_auth_alloc(hapd->eapol_auth, sta->addr, + sta->flags & WLAN_STA_PREAUTH, + sta); + if (sta->eapol_sm == NULL) { + hostapd_logger(hapd, sta->addr, + HOSTAPD_MODULE_IEEE8021X, + HOSTAPD_LEVEL_INFO, + "failed to allocate state machine"); + return; + } + reassoc = 0; + } + +#ifdef CONFIG_WPS + sta->eapol_sm->flags &= ~EAPOL_SM_WAIT_START; + if (!hapd->conf->ieee802_1x && !(sta->flags & WLAN_STA_WPS)) { + /* + * Delay EAPOL frame transmission until a possible WPS + * initiates the handshake with EAPOL-Start. + */ + sta->eapol_sm->flags |= EAPOL_SM_WAIT_START; + } +#endif /* CONFIG_WPS */ + + sta->eapol_sm->eap_if->portEnabled = TRUE; + + pmksa = wpa_auth_sta_get_pmksa(sta->wpa_sm); + if (pmksa) { + int old_vlanid; + + 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->eap_if->eapKeyAvailable = 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); + old_vlanid = sta->vlan_id; + pmksa_cache_to_eapol_data(pmksa, sta->eapol_sm); + if (sta->ssid->dynamic_vlan == DYNAMIC_VLAN_DISABLED) + sta->vlan_id = 0; + ap_sta_bind_vlan(hapd, sta, old_vlanid); + } else { + if (reassoc) { + /* + * Force EAPOL state machines to start + * re-authentication without having to wait for the + * Supplicant to send EAPOL-Start. + */ + sta->eapol_sm->reAuthenticate = TRUE; + } + eapol_auth_step(sta->eapol_sm); + } +} + + +void ieee802_1x_free_radius_class(struct radius_class_data *class) +{ + size_t i; + if (class == NULL) + return; + for (i = 0; i < class->count; i++) + os_free(class->attr[i].data); + os_free(class->attr); + class->attr = NULL; + class->count = 0; +} + + +int ieee802_1x_copy_radius_class(struct radius_class_data *dst, + const struct radius_class_data *src) +{ + size_t i; + + if (src->attr == NULL) + return 0; + + dst->attr = os_zalloc(src->count * sizeof(struct radius_attr_data)); + if (dst->attr == NULL) + return -1; + + dst->count = 0; + + for (i = 0; i < src->count; i++) { + dst->attr[i].data = os_malloc(src->attr[i].len); + if (dst->attr[i].data == NULL) + break; + dst->count++; + os_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; + + if (sm == NULL) + return; + + sta->eapol_sm = NULL; + + if (sm->last_recv_radius) { + radius_msg_free(sm->last_recv_radius); + os_free(sm->last_recv_radius); + } + + os_free(sm->identity); + ieee802_1x_free_radius_class(&sm->radius_class); + eapol_auth_free(sm); +} + + +static void ieee802_1x_decapsulate_radius(struct hostapd_data *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->eap_if->aaaEapNoReq = TRUE; + return; + } + + msg = sm->last_recv_radius; + + eap = radius_msg_get_eap(msg, &len); + if (eap == NULL) { + /* RFC 3579, 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"); + sm->eap_if->aaaEapNoReq = 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"); + os_free(eap); + sm->eap_if->aaaEapNoReq = TRUE; + return; + } + + if (len > sizeof(*hdr)) + eap_type = eap[sizeof(*hdr)]; + + hdr = (struct eap_hdr *) eap; + switch (hdr->code) { + case EAP_CODE_REQUEST: + if (eap_type >= 0) + sm->eap_type_authsrv = eap_type; + os_snprintf(buf, sizeof(buf), "EAP-Request-%s (%d)", + eap_type >= 0 ? eap_type_text(eap_type) : "??", + eap_type); + break; + case EAP_CODE_RESPONSE: + os_snprintf(buf, sizeof(buf), "EAP Response-%s (%d)", + eap_type >= 0 ? eap_type_text(eap_type) : "??", + eap_type); + break; + case EAP_CODE_SUCCESS: + os_strlcpy(buf, "EAP Success", sizeof(buf)); + break; + case EAP_CODE_FAILURE: + os_strlcpy(buf, "EAP Failure", sizeof(buf)); + break; + default: + os_strlcpy(buf, "unknown EAP code", sizeof(buf)); + break; + } + buf[sizeof(buf) - 1] = '\0'; + 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, be_to_host16(hdr->length), + buf); + sm->eap_if->aaaEapReq = TRUE; + + wpabuf_free(sm->eap_if->aaaEapReqData); + sm->eap_if->aaaEapReqData = wpabuf_alloc_ext_data(eap, len); +} + + +static void ieee802_1x_get_keys(struct hostapd_data *hapd, + struct sta_info *sta, struct radius_msg *msg, + struct radius_msg *req, + const 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 && keys->send && keys->recv) { + size_t len = keys->send_len + keys->recv_len; + wpa_hexdump_key(MSG_DEBUG, "MS-MPPE-Send-Key", + keys->send, keys->send_len); + wpa_hexdump_key(MSG_DEBUG, "MS-MPPE-Recv-Key", + keys->recv, keys->recv_len); + + os_free(sm->eap_if->aaaEapKeyData); + sm->eap_if->aaaEapKeyData = os_malloc(len); + if (sm->eap_if->aaaEapKeyData) { + os_memcpy(sm->eap_if->aaaEapKeyData, keys->recv, + keys->recv_len); + os_memcpy(sm->eap_if->aaaEapKeyData + keys->recv_len, + keys->send, keys->send_len); + sm->eap_if->aaaEapKeyDataLen = len; + sm->eap_if->aaaEapKeyAvailable = TRUE; + } + } + + if (keys) { + os_free(keys->send); + os_free(keys->recv); + os_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 = os_zalloc(count * sizeof(struct radius_attr_data)); + if (nclass == NULL) + return; + + nclass_count = 0; + + 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 = os_malloc(class_len); + if (nclass[nclass_count].data == NULL) + break; + + os_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; + wpa_printf(MSG_DEBUG, "IEEE 802.1X: Stored %lu RADIUS Class " + "attributes for " MACSTR, + (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 = os_malloc(len + 1); + if (identity == NULL) + return; + + os_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); + + os_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; +} + + +/** + * ieee802_1x_receive_auth - Process RADIUS frames from Authentication Server + * @msg: RADIUS response message + * @req: RADIUS request message + * @shared_secret: RADIUS shared secret + * @shared_secret_len: Length of shared_secret in octets + * @data: Context data (struct hostapd_data *) + * Returns: Processing status + */ +static RadiusRxResult +ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req, + const 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, old_vlanid = 0; + struct eapol_state_machine *sm; + int override_eapReq = 0; + + sm = ieee802_1x_search_radius_identifier(hapd, msg->hdr->identifier); + if (sm == NULL) { + wpa_printf(MSG_DEBUG, "IEEE 802.1X: Could not find matching " + "station for this RADIUS message"); + 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) { + wpa_printf(MSG_DEBUG, "Allowing RADIUS Access-Reject without " + "Message-Authenticator since it does not include " + "EAP-Message"); + } 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; + wpa_printf(MSG_DEBUG, "RADIUS packet matching with station " MACSTR, + MAC2STR(sta->addr)); + + if (sm->last_recv_radius) { + radius_msg_free(sm->last_recv_radius); + os_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: + if (sta->ssid->dynamic_vlan == DYNAMIC_VLAN_DISABLED) + sta->vlan_id = 0; + else { + old_vlanid = sta->vlan_id; + sta->vlan_id = radius_msg_get_vlanid(msg); + } + if (sta->vlan_id > 0 && + hostapd_get_vlan_id_ifname(hapd->conf->vlan, + sta->vlan_id)) { + hostapd_logger(hapd, sta->addr, + HOSTAPD_MODULE_RADIUS, + HOSTAPD_LEVEL_INFO, + "VLAN ID %d", sta->vlan_id); + } else if (sta->ssid->dynamic_vlan == DYNAMIC_VLAN_REQUIRED) { + sta->eapol_sm->authFail = TRUE; + hostapd_logger(hapd, sta->addr, + HOSTAPD_MODULE_IEEE8021X, + HOSTAPD_LEVEL_INFO, "authentication " + "server did not include required VLAN " + "ID in Access-Accept"); + break; + } + + ap_sta_bind_vlan(hapd, sta, old_vlanid); + + /* RFC 3580, Ch. 3.17 */ + if (session_timeout_set && termination_action == + RADIUS_TERMINATION_ACTION_RADIUS_REQUEST) { + sm->reAuthPeriod = session_timeout; + } else if (session_timeout_set) + ap_sta_session_timeout(hapd, sta, session_timeout); + + sm->eap_if->aaaSuccess = 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->eap_if->eapKeyAvailable && + wpa_auth_pmksa_add(sta->wpa_sm, sm->eapol_key_crypt, + session_timeout_set ? + (int) session_timeout : -1, sm) == 0) { + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, + HOSTAPD_LEVEL_DEBUG, + "Added PMKSA cache entry"); + } + break; + case RADIUS_CODE_ACCESS_REJECT: + sm->eap_if->aaaFail = TRUE; + override_eapReq = 1; + break; + case RADIUS_CODE_ACCESS_CHALLENGE: + sm->eap_if->aaaEapReq = TRUE; + if (session_timeout_set) { + /* RFC 2869, Ch. 2.3.2; RFC 3580, Ch. 3.17 */ + sm->eap_if->aaaMethodTimeout = session_timeout; + hostapd_logger(hapd, sm->addr, + HOSTAPD_MODULE_IEEE8021X, + HOSTAPD_LEVEL_DEBUG, + "using EAP timeout of %d seconds (from " + "RADIUS)", + sm->eap_if->aaaMethodTimeout); + } else { + /* + * Use dynamic retransmission behavior per EAP + * specification. + */ + sm->eap_if->aaaMethodTimeout = 0; + } + break; + } + + ieee802_1x_decapsulate_radius(hapd, sta); + if (override_eapReq) + sm->eap_if->aaaEapReq = FALSE; + + eapol_auth_step(sm); + + return RADIUS_RX_QUEUED; +} + + +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); + os_free(sm->last_recv_radius); + sm->last_recv_radius = NULL; + } + + if (sm->eap_if->eapTimeout) { + /* + * Disconnect the STA since it did not reply to the last EAP + * request and we cannot continue EAP processing (EAP-Failure + * could only be sent if the EAP peer actually replied). + */ + sm->eap_if->portEnabled = FALSE; + 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; + } +} + + +#ifdef HOSTAPD_DUMP_STATE +static void fprint_char(FILE *f, char c) +{ + if (c >= 32 && c < 127) + fprintf(f, "%c", c); + else + fprintf(f, "<%02x>", c); +} + + +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, "%slast EAP type: Authentication Server: %d (%s) " + "Supplicant: %d (%s)\n", prefix, + sm->eap_type_authsrv, eap_type_text(sm->eap_type_authsrv), + sm->eap_type_supp, eap_type_text(sm->eap_type_supp)); + + fprintf(f, "%scached_packets=%s\n", prefix, + sm->last_recv_radius ? "[RX RADIUS]" : ""); + + eapol_auth_dump_state(f, prefix, sm); +} +#endif /* HOSTAPD_DUMP_STATE */ + + +static int ieee802_1x_rekey_broadcast(struct hostapd_data *hapd) +{ + if (hapd->conf->default_wep_key_len < 1) + return 0; + + os_free(hapd->default_wep_key); + hapd->default_wep_key = os_malloc(hapd->conf->default_wep_key_len); + if (hapd->default_wep_key == NULL || + os_get_random(hapd->default_wep_key, + hapd->conf->default_wep_key_len)) { + printf("Could not generate random WEP key.\n"); + os_free(hapd->default_wep_key); + hapd->default_wep_key = NULL; + return -1; + } + + wpa_hexdump_key(MSG_DEBUG, "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->eap_if->eapKeyAvailable = TRUE; + eapol_auth_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++; + + wpa_printf(MSG_DEBUG, "IEEE 802.1X: New default WEP key index %d", + 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"); + os_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->conf->iface, hapd, "WEP", NULL, + hapd->default_wep_key_idx, + hapd->default_wep_key, + hapd->conf->default_wep_key_len, 1)) { + hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE8021X, + HOSTAPD_LEVEL_WARNING, "failed to configure a " + "new broadcast key"); + os_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); + } +} + + +static void ieee802_1x_eapol_send(void *ctx, void *sta_ctx, u8 type, + const u8 *data, size_t datalen) +{ + ieee802_1x_send(ctx, sta_ctx, type, data, datalen); +} + + +static void ieee802_1x_aaa_send(void *ctx, void *sta_ctx, + const u8 *data, size_t datalen) +{ + struct hostapd_data *hapd = ctx; + struct sta_info *sta = sta_ctx; + + ieee802_1x_encapsulate_radius(hapd, sta, data, datalen); +} + + +static void _ieee802_1x_finished(void *ctx, void *sta_ctx, int success, + int preauth) +{ + struct hostapd_data *hapd = ctx; + struct sta_info *sta = sta_ctx; + if (preauth) + rsn_preauth_finished(hapd, sta, success); + else + ieee802_1x_finished(hapd, sta, success); +} + + +static int ieee802_1x_get_eap_user(void *ctx, const u8 *identity, + size_t identity_len, int phase2, + struct eap_user *user) +{ + struct hostapd_data *hapd = ctx; + const struct hostapd_eap_user *eap_user; + int i, count; + + eap_user = hostapd_get_eap_user(hapd->conf, identity, + identity_len, phase2); + if (eap_user == NULL) + return -1; + + os_memset(user, 0, sizeof(*user)); + user->phase2 = phase2; + count = EAP_USER_MAX_METHODS; + if (count > EAP_MAX_METHODS) + count = EAP_MAX_METHODS; + for (i = 0; i < count; i++) { + user->methods[i].vendor = eap_user->methods[i].vendor; + user->methods[i].method = eap_user->methods[i].method; + } + + if (eap_user->password) { + user->password = os_malloc(eap_user->password_len); + if (user->password == NULL) + return -1; + os_memcpy(user->password, eap_user->password, + eap_user->password_len); + user->password_len = eap_user->password_len; + } + user->force_version = eap_user->force_version; + user->ttls_auth = eap_user->ttls_auth; + + return 0; +} + + +static int ieee802_1x_sta_entry_alive(void *ctx, const u8 *addr) +{ + struct hostapd_data *hapd = ctx; + struct sta_info *sta; + sta = ap_get_sta(hapd, addr); + if (sta == NULL || sta->eapol_sm == NULL) + return 0; + return 1; +} + + +static void ieee802_1x_logger(void *ctx, const u8 *addr, + eapol_logger_level level, const char *txt) +{ + struct hostapd_data *hapd = ctx; + int hlevel; + + switch (level) { + case EAPOL_LOGGER_WARNING: + hlevel = HOSTAPD_LEVEL_WARNING; + break; + case EAPOL_LOGGER_INFO: + hlevel = HOSTAPD_LEVEL_INFO; + break; + case EAPOL_LOGGER_DEBUG: + default: + hlevel = HOSTAPD_LEVEL_DEBUG; + break; + } + + hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE8021X, hlevel, "%s", + txt); +} + + +static void ieee802_1x_set_port_authorized(void *ctx, void *sta_ctx, + int authorized) +{ + struct hostapd_data *hapd = ctx; + struct sta_info *sta = sta_ctx; + ieee802_1x_set_sta_authorized(hapd, sta, authorized); +} + + +static void _ieee802_1x_abort_auth(void *ctx, void *sta_ctx) +{ + struct hostapd_data *hapd = ctx; + struct sta_info *sta = sta_ctx; + ieee802_1x_abort_auth(hapd, sta); +} + + +static void _ieee802_1x_tx_key(void *ctx, void *sta_ctx) +{ + struct hostapd_data *hapd = ctx; + struct sta_info *sta = sta_ctx; + ieee802_1x_tx_key(hapd, sta); +} + + +int ieee802_1x_init(struct hostapd_data *hapd) +{ + int i; + struct eapol_auth_config conf; + struct eapol_auth_cb cb; + + os_memset(&conf, 0, sizeof(conf)); + conf.hapd = hapd; + conf.eap_reauth_period = hapd->conf->eap_reauth_period; + conf.wpa = hapd->conf->wpa; + conf.individual_wep_key_len = hapd->conf->individual_wep_key_len; + conf.eap_server = hapd->conf->eap_server; + conf.ssl_ctx = hapd->ssl_ctx; + conf.eap_sim_db_priv = hapd->eap_sim_db_priv; + conf.eap_req_id_text = hapd->conf->eap_req_id_text; + conf.eap_req_id_text_len = hapd->conf->eap_req_id_text_len; + conf.pac_opaque_encr_key = hapd->conf->pac_opaque_encr_key; + conf.eap_fast_a_id = hapd->conf->eap_fast_a_id; + conf.eap_fast_a_id_len = hapd->conf->eap_fast_a_id_len; + conf.eap_fast_a_id_info = hapd->conf->eap_fast_a_id_info; + conf.eap_fast_prov = hapd->conf->eap_fast_prov; + conf.pac_key_lifetime = hapd->conf->pac_key_lifetime; + conf.pac_key_refresh_time = hapd->conf->pac_key_refresh_time; + conf.eap_sim_aka_result_ind = hapd->conf->eap_sim_aka_result_ind; + conf.tnc = hapd->conf->tnc; + conf.wps = hapd->wps; + + os_memset(&cb, 0, sizeof(cb)); + cb.eapol_send = ieee802_1x_eapol_send; + cb.aaa_send = ieee802_1x_aaa_send; + cb.finished = _ieee802_1x_finished; + cb.get_eap_user = ieee802_1x_get_eap_user; + cb.sta_entry_alive = ieee802_1x_sta_entry_alive; + cb.logger = ieee802_1x_logger; + cb.set_port_authorized = ieee802_1x_set_port_authorized; + cb.abort_auth = _ieee802_1x_abort_auth; + cb.tx_key = _ieee802_1x_tx_key; + + hapd->eapol_auth = eapol_auth_init(&conf, &cb); + if (hapd->eapol_auth == NULL) + return -1; + + if ((hapd->conf->ieee802_1x || hapd->conf->wpa) && + hostapd_set_ieee8021x(hapd->conf->iface, 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) { + hostapd_set_privacy(hapd, 1); + + for (i = 0; i < 4; i++) + hostapd_set_encryption(hapd->conf->iface, hapd, + "none", NULL, i, NULL, 0, 0); + + ieee802_1x_rekey(hapd, NULL); + + if (hapd->default_wep_key == NULL) + return -1; + } + + return 0; +} + + +void ieee802_1x_deinit(struct hostapd_data *hapd) +{ + eloop_cancel_timeout(ieee802_1x_rekey, hapd, NULL); + + if (hapd->driver != NULL && + (hapd->conf->ieee802_1x || hapd->conf->wpa)) + hostapd_set_ieee8021x(hapd->conf->iface, hapd, 0); + + eapol_auth_deinit(hapd->eapol_auth); + hapd->eapol_auth = NULL; +} + + +int ieee802_1x_reconfig(struct hostapd_data *hapd, + struct hostapd_config *oldconf, + struct hostapd_bss_config *oldbss) +{ + ieee802_1x_deinit(hapd); + return ieee802_1x_init(hapd); +} + + +int ieee802_1x_tx_status(struct hostapd_data *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; + const unsigned char rfc1042_hdr[ETH_ALEN] = + { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 }; + + if (sta == NULL) + return -1; + if (len < sizeof(*hdr) + sizeof(rfc1042_hdr) + 2 + sizeof(*xhdr)) + return 0; + + hdr = (struct ieee80211_hdr *) buf; + pos = (u8 *) (hdr + 1); + if (os_memcmp(pos, rfc1042_hdr, sizeof(rfc1042_hdr)) != 0) + return 0; + pos += sizeof(rfc1042_hdr); + if (WPA_GET_BE16(pos) != ETH_P_PAE) + return 0; + pos += 2; + + xhdr = (struct ieee802_1x_hdr *) pos; + pos += sizeof(*xhdr); + + wpa_printf(MSG_DEBUG, "IEEE 802.1X: " MACSTR " TX status - version=%d " + "type=%d length=%d - ack=%d", + MAC2STR(sta->addr), xhdr->version, xhdr->type, + be_to_host16(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 >= (int) sm->radius_class.count) + return NULL; + + *len = sm->radius_class.attr[idx].len; + return sm->radius_class.attr[idx].data; +} + + +const u8 * ieee802_1x_get_key(struct eapol_state_machine *sm, size_t *len) +{ + if (sm == NULL) + return NULL; + + *len = sm->eap_if->eapKeyDataLen; + return sm->eap_if->eapKeyData; +} + + +void ieee802_1x_notify_port_enabled(struct eapol_state_machine *sm, + int enabled) +{ + if (sm == NULL) + return; + sm->eap_if->portEnabled = enabled ? TRUE : FALSE; + eapol_auth_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_auth_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, ret; + struct eapol_state_machine *sm = sta->eapol_sm; + + if (sm == NULL) + return 0; + + ret = os_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); + if (ret < 0 || (size_t) ret >= buflen - len) + return len; + len += ret; + + /* dot1xAuthConfigTable */ + ret = os_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->adminControlledDirections, + sm->operControlledDirections, + sm->authPortStatus, + sm->portControl, + sm->quietPeriod, + sm->serverTimeout, + sm->reAuthPeriod, + bool_txt(sm->reAuthEnabled), + bool_txt(sm->keyTxEnabled)); + if (ret < 0 || (size_t) ret >= buflen - len) + return len; + len += ret; + + /* dot1xAuthStatsTable */ + ret = os_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)); + if (ret < 0 || (size_t) ret >= buflen - len) + return len; + len += ret; + + /* dot1xAuthDiagTable */ + ret = os_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->authEntersConnecting, + sm->authEapLogoffsWhileConnecting, + sm->authEntersAuthenticating, + sm->authAuthSuccessesWhileAuthenticating, + sm->authAuthTimeoutsWhileAuthenticating, + sm->authAuthFailWhileAuthenticating, + sm->authAuthEapStartsWhileAuthenticating, + sm->authAuthEapLogoffWhileAuthenticating, + sm->authAuthReauthsWhileAuthenticated, + sm->authAuthEapStartsWhileAuthenticated, + sm->authAuthEapLogoffWhileAuthenticated, + sm->backendResponses, + sm->backendAccessChallenges, + sm->backendOtherRequestsToSupplicant, + sm->backendAuthSuccesses, + sm->backendAuthFails); + if (ret < 0 || (size_t) ret >= buflen - len) + return len; + len += ret; + + /* dot1xAuthSessionStatsTable */ + ret = os_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, + (wpa_key_mgmt_wpa_ieee8021x( + wpa_auth_sta_key_mgmt(sta->wpa_sm))) ? + 1 : 2, + (unsigned int) (time(NULL) - + sta->acct_session_start), + sm->identity); + if (ret < 0 || (size_t) ret >= buflen - len) + return len; + len += ret; + + return len; +} + + +static void ieee802_1x_finished(struct hostapd_data *hapd, + struct sta_info *sta, int success) +{ + const u8 *key; + size_t len; + /* TODO: get PMKLifetime from WPA parameters */ + static const int dot11RSNAConfigPMKLifetime = 43200; + + key = ieee802_1x_get_key(sta->eapol_sm, &len); + if (success && key && len >= PMK_LEN && + wpa_auth_pmksa_add(sta->wpa_sm, key, dot11RSNAConfigPMKLifetime, + sta->eapol_sm) == 0) { + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, + HOSTAPD_LEVEL_DEBUG, + "Added PMKSA cache entry (IEEE 802.1X)"); + } +} diff --git a/contrib/wpa/hostapd/ieee802_1x.h b/contrib/wpa/hostapd/ieee802_1x.h new file mode 100644 index 0000000..94cff93 --- /dev/null +++ b/contrib/wpa/hostapd/ieee802_1x.h @@ -0,0 +1,90 @@ +/* + * hostapd / IEEE 802.1X-2004 Authenticator + * Copyright (c) 2002-2007, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef IEEE802_1X_H +#define IEEE802_1X_H + +struct hostapd_data; +struct sta_info; +struct eapol_state_machine; +struct hostapd_config; +struct hostapd_bss_config; + +/* RFC 3580, 4. RC4 EAPOL-Key Frame */ + +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)); + + +void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf, + size_t len); +void ieee802_1x_new_station(struct hostapd_data *hapd, struct sta_info *sta); +void ieee802_1x_free_station(struct sta_info *sta); + +void ieee802_1x_tx_key(struct hostapd_data *hapd, struct sta_info *sta); +void ieee802_1x_abort_auth(struct hostapd_data *hapd, struct sta_info *sta); +void ieee802_1x_set_sta_authorized(struct hostapd_data *hapd, + struct sta_info *sta, int authorized); +void ieee802_1x_dump_state(FILE *f, const char *prefix, struct sta_info *sta); +int ieee802_1x_init(struct hostapd_data *hapd); +void ieee802_1x_deinit(struct hostapd_data *hapd); +int ieee802_1x_reconfig(struct hostapd_data *hapd, + struct hostapd_config *oldconf, + struct hostapd_bss_config *oldbss); +int ieee802_1x_tx_status(struct hostapd_data *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); +const u8 * ieee802_1x_get_key(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); +char *eap_type_text(u8 type); + +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, + const struct radius_class_data *src); + +const char *radius_mode_txt(struct hostapd_data *hapd); +int radius_sta_rate(struct hostapd_data *hapd, struct sta_info *sta); + +#endif /* IEEE802_1X_H */ diff --git a/contrib/wpa/hostapd/logwatch/README b/contrib/wpa/hostapd/logwatch/README new file mode 100644 index 0000000..3cba511 --- /dev/null +++ b/contrib/wpa/hostapd/logwatch/README @@ -0,0 +1,9 @@ +Logwatch is a utility for analyzing system logs and provide a human +readable summary. This directory has a configuration file and a log +analyzer script for parsing hostapd system log entries for logwatch. +These files can be installed by copying them to following locations: + +/etc/log.d/conf/services/hostapd.conf +/etc/log.d/scripts/services/hostapd + +More information about logwatch is available from http://www.logwatch.org/ diff --git a/contrib/wpa/hostapd/logwatch/hostapd b/contrib/wpa/hostapd/logwatch/hostapd new file mode 100755 index 0000000..97b24ef --- /dev/null +++ b/contrib/wpa/hostapd/logwatch/hostapd @@ -0,0 +1,65 @@ +#!/usr/bin/perl -w +# +# Logwatch script for hostapd +# +# Copyright 2005 Henrik Brix Andersen <brix@gentoo.org> +# Distributed under the terms of the GNU General Public License v2 +# Alternatively, this file may be distributed under the terms of the BSD License + +use strict; + +my $debug = $ENV{'LOGWATCH_DEBUG'} || 0; +my $detail = $ENV{'LOGWATCH_DETAIL_LEVEL'} || 0; +my $debugcounter = 1; + +my %hostapd; +my @unmatched; + +if ($debug >= 5) { + print STDERR "\n\nDEBUG: Inside HOSTAPD Filter\n\n"; +} + +while (defined(my $line = <STDIN>)) { + if ($debug >= 5) { + print STDERR "DEBUG($debugcounter): $line"; + $debugcounter++; + } + chomp($line); + + if (my ($iface,$mac,$layer,$details) = ($line =~ /(.*?): STA (.*?) (.*?): (.*?)$/i)) { + unless ($detail == 10) { + # collapse association events + $details =~ s/^(associated) .*$/$1/i; + } + $hostapd{$iface}->{$mac}->{$layer}->{$details}++; + } else { + push @unmatched, "$line\n"; + } +} + +if (keys %hostapd) { + foreach my $iface (sort keys %hostapd) { + print "Interface $iface:\n"; + foreach my $mac (sort keys %{$hostapd{$iface}}) { + print " Client MAC Address $mac:\n"; + foreach my $layer (sort keys %{$hostapd{$iface}->{$mac}}) { + print " $layer:\n"; + foreach my $details (sort keys %{$hostapd{$iface}->{$mac}->{$layer}}) { + print " $details"; + my $count = $hostapd{$iface}->{$mac}->{$layer}->{$details}; + if ($count > 1) { + print ": " . $count . " Times"; + } + print "\n"; + } + } + } + } +} + +if ($#unmatched >= 0) { + print "\n**Unmatched Entries**\n"; + print @unmatched; +} + +exit(0); diff --git a/contrib/wpa/hostapd/logwatch/hostapd.conf b/contrib/wpa/hostapd/logwatch/hostapd.conf new file mode 100644 index 0000000..5bebe6a --- /dev/null +++ b/contrib/wpa/hostapd/logwatch/hostapd.conf @@ -0,0 +1,10 @@ +# Logwatch configuration for hostapd +# +# Copyright 2005 Henrik Brix Andersen <brix@gentoo.org> +# Distributed under the terms of the GNU General Public License v2 +# Alternatively, this file may be distributed under the terms of the BSD License + +Title = "hostapd" +LogFile = messages +*OnlyService = hostapd +*RemoveHeaders diff --git a/contrib/wpa/hostapd/mlme.c b/contrib/wpa/hostapd/mlme.c new file mode 100644 index 0000000..d883931 --- /dev/null +++ b/contrib/wpa/hostapd/mlme.c @@ -0,0 +1,180 @@ +/* + * hostapd / IEEE 802.11 MLME + * Copyright 2003-2006, Jouni Malinen <j@w1.fi> + * Copyright 2003-2004, Instant802 Networks, Inc. + * Copyright 2005-2006, Devicescape Software, Inc. + * + * 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 "includes.h" + +#include "hostapd.h" +#include "ieee802_11.h" +#include "wpa.h" +#include "mlme.h" + + +static const char * mlme_auth_alg_str(int alg) +{ + switch (alg) { + case WLAN_AUTH_OPEN: + return "OPEN_SYSTEM"; + case WLAN_AUTH_SHARED_KEY: + return "SHARED_KEY"; + case WLAN_AUTH_FT: + return "FT"; + } + + return "unknown"; +} + + +/** + * mlme_authenticate_indication - Report the establishment of an authentication + * relationship with a specific peer MAC entity + * @hapd: BSS data + * @sta: peer STA data + * + * MLME calls this function as a result of the establishment of an + * authentication relationship with a specific peer MAC entity that + * resulted from an authentication procedure that was initiated by + * that specific peer MAC entity. + * + * PeerSTAAddress = sta->addr + * AuthenticationType = sta->auth_alg (WLAN_AUTH_OPEN / WLAN_AUTH_SHARED_KEY) + */ +void mlme_authenticate_indication(struct hostapd_data *hapd, + struct sta_info *sta) +{ + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_MLME, + HOSTAPD_LEVEL_DEBUG, + "MLME-AUTHENTICATE.indication(" MACSTR ", %s)", + MAC2STR(sta->addr), mlme_auth_alg_str(sta->auth_alg)); + if (sta->auth_alg != WLAN_AUTH_FT && !(sta->flags & WLAN_STA_MFP)) + mlme_deletekeys_request(hapd, sta); +} + + +/** + * mlme_deauthenticate_indication - Report the invalidation of an + * authentication relationship with a specific peer MAC entity + * @hapd: BSS data + * @sta: Peer STA data + * @reason_code: ReasonCode from Deauthentication frame + * + * MLME calls this function as a result of the invalidation of an + * authentication relationship with a specific peer MAC entity. + * + * PeerSTAAddress = sta->addr + */ +void mlme_deauthenticate_indication(struct hostapd_data *hapd, + struct sta_info *sta, u16 reason_code) +{ + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_MLME, + HOSTAPD_LEVEL_DEBUG, + "MLME-DEAUTHENTICATE.indication(" MACSTR ", %d)", + MAC2STR(sta->addr), reason_code); + mlme_deletekeys_request(hapd, sta); +} + + +/** + * mlme_associate_indication - Report the establishment of an association with + * a specific peer MAC entity + * @hapd: BSS data + * @sta: peer STA data + * + * MLME calls this function as a result of the establishment of an + * association with a specific peer MAC entity that resulted from an + * association procedure that was initiated by that specific peer MAC entity. + * + * PeerSTAAddress = sta->addr + */ +void mlme_associate_indication(struct hostapd_data *hapd, struct sta_info *sta) +{ + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_MLME, + HOSTAPD_LEVEL_DEBUG, + "MLME-ASSOCIATE.indication(" MACSTR ")", + MAC2STR(sta->addr)); + if (sta->auth_alg != WLAN_AUTH_FT) + mlme_deletekeys_request(hapd, sta); +} + + +/** + * mlme_reassociate_indication - Report the establishment of an reassociation + * with a specific peer MAC entity + * @hapd: BSS data + * @sta: peer STA data + * + * MLME calls this function as a result of the establishment of an + * reassociation with a specific peer MAC entity that resulted from a + * reassociation procedure that was initiated by that specific peer MAC entity. + * + * PeerSTAAddress = sta->addr + * + * sta->previous_ap contains the "Current AP" information from ReassocReq. + */ +void mlme_reassociate_indication(struct hostapd_data *hapd, + struct sta_info *sta) +{ + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_MLME, + HOSTAPD_LEVEL_DEBUG, + "MLME-REASSOCIATE.indication(" MACSTR ")", + MAC2STR(sta->addr)); + if (sta->auth_alg != WLAN_AUTH_FT) + mlme_deletekeys_request(hapd, sta); +} + + +/** + * mlme_disassociate_indication - Report disassociation with a specific peer + * MAC entity + * @hapd: BSS data + * @sta: Peer STA data + * @reason_code: ReasonCode from Disassociation frame + * + * MLME calls this function as a result of the invalidation of an association + * relationship with a specific peer MAC entity. + * + * PeerSTAAddress = sta->addr + */ +void mlme_disassociate_indication(struct hostapd_data *hapd, + struct sta_info *sta, u16 reason_code) +{ + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_MLME, + HOSTAPD_LEVEL_DEBUG, + "MLME-DISASSOCIATE.indication(" MACSTR ", %d)", + MAC2STR(sta->addr), reason_code); + mlme_deletekeys_request(hapd, sta); +} + + +void mlme_michaelmicfailure_indication(struct hostapd_data *hapd, + const u8 *addr) +{ + hostapd_logger(hapd, addr, HOSTAPD_MODULE_MLME, + HOSTAPD_LEVEL_DEBUG, + "MLME-MichaelMICFailure.indication(" MACSTR ")", + MAC2STR(addr)); +} + + +void mlme_deletekeys_request(struct hostapd_data *hapd, struct sta_info *sta) +{ + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_MLME, + HOSTAPD_LEVEL_DEBUG, + "MLME-DELETEKEYS.request(" MACSTR ")", + MAC2STR(sta->addr)); + + if (sta->wpa_sm) + wpa_remove_ptk(sta->wpa_sm); +} diff --git a/contrib/wpa/hostapd/mlme.h b/contrib/wpa/hostapd/mlme.h new file mode 100644 index 0000000..c77a939 --- /dev/null +++ b/contrib/wpa/hostapd/mlme.h @@ -0,0 +1,40 @@ +/* + * hostapd / IEEE 802.11 MLME + * Copyright 2003, Jouni Malinen <j@w1.fi> + * Copyright 2003-2004, Instant802 Networks, Inc. + * Copyright 2005-2006, Devicescape Software, Inc. + * + * 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 MLME_H +#define MLME_H + +void mlme_authenticate_indication(struct hostapd_data *hapd, + struct sta_info *sta); + +void mlme_deauthenticate_indication(struct hostapd_data *hapd, + struct sta_info *sta, u16 reason_code); + +void mlme_associate_indication(struct hostapd_data *hapd, + struct sta_info *sta); + +void mlme_reassociate_indication(struct hostapd_data *hapd, + struct sta_info *sta); + +void mlme_disassociate_indication(struct hostapd_data *hapd, + struct sta_info *sta, u16 reason_code); + +void mlme_michaelmicfailure_indication(struct hostapd_data *hapd, + const u8 *addr); + +void mlme_deletekeys_request(struct hostapd_data *hapd, struct sta_info *sta); + +#endif /* MLME_H */ diff --git a/contrib/wpa/hostapd/nt_password_hash.c b/contrib/wpa/hostapd/nt_password_hash.c new file mode 100644 index 0000000..9df307d --- /dev/null +++ b/contrib/wpa/hostapd/nt_password_hash.c @@ -0,0 +1,52 @@ +/* + * hostapd - Plaintext password to NtPasswordHash + * Copyright (c) 2005, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "ms_funcs.h" + + +int main(int argc, char *argv[]) +{ + unsigned char password_hash[16]; + size_t i; + char *password, buf[64], *pos; + + if (argc > 1) + password = argv[1]; + else { + if (fgets(buf, sizeof(buf), stdin) == NULL) { + printf("Failed to read password\n"); + return 1; + } + buf[sizeof(buf) - 1] = '\0'; + pos = buf; + while (*pos != '\0') { + if (*pos == '\r' || *pos == '\n') { + *pos = '\0'; + break; + } + pos++; + } + password = buf; + } + + nt_password_hash((u8 *) password, strlen(password), password_hash); + for (i = 0; i < sizeof(password_hash); i++) + printf("%02x", password_hash[i]); + printf("\n"); + + return 0; +} diff --git a/contrib/wpa/hostapd/peerkey.c b/contrib/wpa/hostapd/peerkey.c new file mode 100644 index 0000000..83f3ce5 --- /dev/null +++ b/contrib/wpa/hostapd/peerkey.c @@ -0,0 +1,402 @@ +/* + * hostapd - PeerKey for Direct Link Setup (DLS) + * Copyright (c) 2006-2008, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "eloop.h" +#include "sha1.h" +#include "sha256.h" +#include "wpa.h" +#include "defs.h" +#include "wpa_auth_i.h" +#include "wpa_auth_ie.h" + +#ifdef CONFIG_PEERKEY + +static void wpa_stsl_step(void *eloop_ctx, void *timeout_ctx) +{ +#if 0 + struct wpa_authenticator *wpa_auth = eloop_ctx; + struct wpa_stsl_negotiation *neg = timeout_ctx; +#endif + + /* TODO: ? */ +} + + +struct wpa_stsl_search { + const u8 *addr; + struct wpa_state_machine *sm; +}; + + +static int wpa_stsl_select_sta(struct wpa_state_machine *sm, void *ctx) +{ + struct wpa_stsl_search *search = ctx; + if (os_memcmp(search->addr, sm->addr, ETH_ALEN) == 0) { + search->sm = sm; + return 1; + } + return 0; +} + + +static void wpa_smk_send_error(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm, const u8 *peer, + u16 mui, u16 error_type) +{ + u8 kde[2 + RSN_SELECTOR_LEN + ETH_ALEN + + 2 + RSN_SELECTOR_LEN + sizeof(struct rsn_error_kde)]; + u8 *pos; + struct rsn_error_kde error; + + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG, + "Sending SMK Error"); + + pos = kde; + + if (peer) { + pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, peer, ETH_ALEN, + NULL, 0); + } + + error.mui = host_to_be16(mui); + error.error_type = host_to_be16(error_type); + pos = wpa_add_kde(pos, RSN_KEY_DATA_ERROR, + (u8 *) &error, sizeof(error), NULL, 0); + + __wpa_send_eapol(wpa_auth, sm, + WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC | + WPA_KEY_INFO_SMK_MESSAGE | WPA_KEY_INFO_ERROR, + NULL, NULL, kde, pos - kde, 0, 0, 0); +} + + +void wpa_smk_m1(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm, struct wpa_eapol_key *key) +{ + struct wpa_eapol_ie_parse kde; + struct wpa_stsl_search search; + u8 *buf, *pos; + size_t buf_len; + + if (wpa_parse_kde_ies((const u8 *) (key + 1), + WPA_GET_BE16(key->key_data_length), &kde) < 0) { + wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK M1"); + return; + } + + if (kde.rsn_ie == NULL || kde.mac_addr == NULL || + kde.mac_addr_len < ETH_ALEN) { + wpa_printf(MSG_INFO, "RSN: No RSN IE or MAC address KDE in " + "SMK M1"); + return; + } + + /* Initiator = sm->addr; Peer = kde.mac_addr */ + + search.addr = kde.mac_addr; + search.sm = NULL; + if (wpa_auth_for_each_sta(wpa_auth, wpa_stsl_select_sta, &search) == + 0 || search.sm == NULL) { + wpa_printf(MSG_DEBUG, "RSN: SMK handshake with " MACSTR + " aborted - STA not associated anymore", + MAC2STR(kde.mac_addr)); + wpa_smk_send_error(wpa_auth, sm, kde.mac_addr, STK_MUI_SMK, + STK_ERR_STA_NR); + /* FIX: wpa_stsl_remove(wpa_auth, neg); */ + return; + } + + buf_len = kde.rsn_ie_len + 2 + RSN_SELECTOR_LEN + ETH_ALEN; + buf = os_malloc(buf_len); + if (buf == NULL) + return; + /* Initiator RSN IE */ + os_memcpy(buf, kde.rsn_ie, kde.rsn_ie_len); + pos = buf + kde.rsn_ie_len; + /* Initiator MAC Address */ + pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, sm->addr, ETH_ALEN, + NULL, 0); + + /* SMK M2: + * EAPOL-Key(S=1, M=1, A=1, I=0, K=0, SM=1, KeyRSC=0, Nonce=INonce, + * MIC=MIC, DataKDs=(RSNIE_I, MAC_I KDE) + */ + + wpa_auth_logger(wpa_auth, search.sm->addr, LOGGER_DEBUG, + "Sending SMK M2"); + + __wpa_send_eapol(wpa_auth, search.sm, + WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC | + WPA_KEY_INFO_ACK | WPA_KEY_INFO_SMK_MESSAGE, + NULL, key->key_nonce, buf, pos - buf, 0, 0, 0); + + os_free(buf); +} + + +static void wpa_send_smk_m4(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm, + struct wpa_eapol_key *key, + struct wpa_eapol_ie_parse *kde, + const u8 *smk) +{ + u8 *buf, *pos; + size_t buf_len; + u32 lifetime; + + /* SMK M4: + * EAPOL-Key(S=1, M=1, A=0, I=1, K=0, SM=1, KeyRSC=0, Nonce=PNonce, + * MIC=MIC, DataKDs=(MAC_I KDE, INonce KDE, SMK KDE, + * Lifetime KDE) + */ + + buf_len = 2 + RSN_SELECTOR_LEN + ETH_ALEN + + 2 + RSN_SELECTOR_LEN + WPA_NONCE_LEN + + 2 + RSN_SELECTOR_LEN + PMK_LEN + WPA_NONCE_LEN + + 2 + RSN_SELECTOR_LEN + sizeof(lifetime); + pos = buf = os_malloc(buf_len); + if (buf == NULL) + return; + + /* Initiator MAC Address */ + pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, kde->mac_addr, ETH_ALEN, + NULL, 0); + + /* Initiator Nonce */ + pos = wpa_add_kde(pos, RSN_KEY_DATA_NONCE, kde->nonce, WPA_NONCE_LEN, + NULL, 0); + + /* SMK with PNonce */ + pos = wpa_add_kde(pos, RSN_KEY_DATA_SMK, smk, PMK_LEN, + key->key_nonce, WPA_NONCE_LEN); + + /* Lifetime */ + lifetime = htonl(43200); /* dot11RSNAConfigSMKLifetime */ + pos = wpa_add_kde(pos, RSN_KEY_DATA_LIFETIME, + (u8 *) &lifetime, sizeof(lifetime), NULL, 0); + + wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, + "Sending SMK M4"); + + __wpa_send_eapol(wpa_auth, sm, + WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC | + WPA_KEY_INFO_INSTALL | WPA_KEY_INFO_SMK_MESSAGE, + NULL, key->key_nonce, buf, pos - buf, 0, 1, 0); + + os_free(buf); +} + + +static void wpa_send_smk_m5(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm, + struct wpa_eapol_key *key, + struct wpa_eapol_ie_parse *kde, + const u8 *smk, const u8 *peer) +{ + u8 *buf, *pos; + size_t buf_len; + u32 lifetime; + + /* SMK M5: + * EAPOL-Key(S=1, M=1, A=0, I=0, K=0, SM=1, KeyRSC=0, Nonce=INonce, + * MIC=MIC, DataKDs=(RSNIE_P, MAC_P KDE, PNonce, SMK KDE, + * Lifetime KDE)) + */ + + buf_len = kde->rsn_ie_len + + 2 + RSN_SELECTOR_LEN + ETH_ALEN + + 2 + RSN_SELECTOR_LEN + WPA_NONCE_LEN + + 2 + RSN_SELECTOR_LEN + PMK_LEN + WPA_NONCE_LEN + + 2 + RSN_SELECTOR_LEN + sizeof(lifetime); + pos = buf = os_malloc(buf_len); + if (buf == NULL) + return; + + /* Peer RSN IE */ + os_memcpy(buf, kde->rsn_ie, kde->rsn_ie_len); + pos = buf + kde->rsn_ie_len; + + /* Peer MAC Address */ + pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, peer, ETH_ALEN, NULL, 0); + + /* PNonce */ + pos = wpa_add_kde(pos, RSN_KEY_DATA_NONCE, key->key_nonce, + WPA_NONCE_LEN, NULL, 0); + + /* SMK and INonce */ + pos = wpa_add_kde(pos, RSN_KEY_DATA_SMK, smk, PMK_LEN, + kde->nonce, WPA_NONCE_LEN); + + /* Lifetime */ + lifetime = htonl(43200); /* dot11RSNAConfigSMKLifetime */ + pos = wpa_add_kde(pos, RSN_KEY_DATA_LIFETIME, + (u8 *) &lifetime, sizeof(lifetime), NULL, 0); + + wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, + "Sending SMK M5"); + + __wpa_send_eapol(wpa_auth, sm, + WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC | + WPA_KEY_INFO_SMK_MESSAGE, + NULL, kde->nonce, buf, pos - buf, 0, 1, 0); + + os_free(buf); +} + + +void wpa_smk_m3(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm, struct wpa_eapol_key *key) +{ + struct wpa_eapol_ie_parse kde; + struct wpa_stsl_search search; + u8 smk[32], buf[ETH_ALEN + 8 + 2 * WPA_NONCE_LEN], *pos; + + if (wpa_parse_kde_ies((const u8 *) (key + 1), + WPA_GET_BE16(key->key_data_length), &kde) < 0) { + wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK M3"); + return; + } + + if (kde.rsn_ie == NULL || + kde.mac_addr == NULL || kde.mac_addr_len < ETH_ALEN || + kde.nonce == NULL || kde.nonce_len < WPA_NONCE_LEN) { + wpa_printf(MSG_INFO, "RSN: No RSN IE, MAC address KDE, or " + "Nonce KDE in SMK M3"); + return; + } + + /* Peer = sm->addr; Initiator = kde.mac_addr; + * Peer Nonce = key->key_nonce; Initiator Nonce = kde.nonce */ + + search.addr = kde.mac_addr; + search.sm = NULL; + if (wpa_auth_for_each_sta(wpa_auth, wpa_stsl_select_sta, &search) == + 0 || search.sm == NULL) { + wpa_printf(MSG_DEBUG, "RSN: SMK handshake with " MACSTR + " aborted - STA not associated anymore", + MAC2STR(kde.mac_addr)); + wpa_smk_send_error(wpa_auth, sm, kde.mac_addr, STK_MUI_SMK, + STK_ERR_STA_NR); + /* FIX: wpa_stsl_remove(wpa_auth, neg); */ + return; + } + + if (os_get_random(smk, PMK_LEN)) { + wpa_printf(MSG_DEBUG, "RSN: Failed to generate SMK"); + return; + } + + /* SMK = PRF-256(Random number, "SMK Derivation", + * AA || Time || INonce || PNonce) + */ + os_memcpy(buf, wpa_auth->addr, ETH_ALEN); + pos = buf + ETH_ALEN; + wpa_get_ntp_timestamp(pos); + pos += 8; + os_memcpy(pos, kde.nonce, WPA_NONCE_LEN); + pos += WPA_NONCE_LEN; + os_memcpy(pos, key->key_nonce, WPA_NONCE_LEN); +#ifdef CONFIG_IEEE80211W + sha256_prf(smk, PMK_LEN, "SMK Derivation", buf, sizeof(buf), + smk, PMK_LEN); +#else /* CONFIG_IEEE80211W */ + sha1_prf(smk, PMK_LEN, "SMK Derivation", buf, sizeof(buf), + smk, PMK_LEN); +#endif /* CONFIG_IEEE80211W */ + + wpa_hexdump_key(MSG_DEBUG, "RSN: SMK", smk, PMK_LEN); + + wpa_send_smk_m4(wpa_auth, sm, key, &kde, smk); + wpa_send_smk_m5(wpa_auth, search.sm, key, &kde, smk, sm->addr); + + /* Authenticator does not need SMK anymore and it is required to forget + * it. */ + os_memset(smk, 0, sizeof(*smk)); +} + + +void wpa_smk_error(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm, struct wpa_eapol_key *key) +{ + struct wpa_eapol_ie_parse kde; + struct wpa_stsl_search search; + struct rsn_error_kde error; + u16 mui, error_type; + + if (wpa_parse_kde_ies((const u8 *) (key + 1), + WPA_GET_BE16(key->key_data_length), &kde) < 0) { + wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK Error"); + return; + } + + if (kde.mac_addr == NULL || kde.mac_addr_len < ETH_ALEN || + kde.error == NULL || kde.error_len < sizeof(error)) { + wpa_printf(MSG_INFO, "RSN: No MAC address or Error KDE in " + "SMK Error"); + return; + } + + search.addr = kde.mac_addr; + search.sm = NULL; + if (wpa_auth_for_each_sta(wpa_auth, wpa_stsl_select_sta, &search) == + 0 || search.sm == NULL) { + wpa_printf(MSG_DEBUG, "RSN: Peer STA " MACSTR " not " + "associated for SMK Error message from " MACSTR, + MAC2STR(kde.mac_addr), MAC2STR(sm->addr)); + return; + } + + os_memcpy(&error, kde.error, sizeof(error)); + mui = be_to_host16(error.mui); + error_type = be_to_host16(error.error_type); + wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO, + "STA reported SMK Error: Peer " MACSTR + " MUI %d Error Type %d", + MAC2STR(kde.mac_addr), mui, error_type); + + wpa_smk_send_error(wpa_auth, search.sm, sm->addr, mui, error_type); +} + + +int wpa_stsl_remove(struct wpa_authenticator *wpa_auth, + struct wpa_stsl_negotiation *neg) +{ + struct wpa_stsl_negotiation *pos, *prev; + + if (wpa_auth == NULL) + return -1; + pos = wpa_auth->stsl_negotiations; + prev = NULL; + while (pos) { + if (pos == neg) { + if (prev) + prev->next = pos->next; + else + wpa_auth->stsl_negotiations = pos->next; + + eloop_cancel_timeout(wpa_stsl_step, wpa_auth, pos); + os_free(pos); + return 0; + } + prev = pos; + pos = pos->next; + } + + return -1; +} + +#endif /* CONFIG_PEERKEY */ diff --git a/contrib/wpa/hostapd/pmksa_cache.c b/contrib/wpa/hostapd/pmksa_cache.c new file mode 100644 index 0000000..5f54a34 --- /dev/null +++ b/contrib/wpa/hostapd/pmksa_cache.c @@ -0,0 +1,455 @@ +/* + * hostapd - PMKSA cache for IEEE 802.11i RSN + * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "ap.h" +#include "config.h" +#include "common.h" +#include "eloop.h" +#include "sha1.h" +#include "sha256.h" +#include "ieee802_1x.h" +#include "eapol_sm.h" +#include "pmksa_cache.h" + + +static const int pmksa_cache_max_entries = 1024; +static const int dot11RSNAConfigPMKLifetime = 43200; + +struct rsn_pmksa_cache { +#define PMKID_HASH_SIZE 128 +#define PMKID_HASH(pmkid) (unsigned int) ((pmkid)[0] & 0x7f) + struct rsn_pmksa_cache_entry *pmkid[PMKID_HASH_SIZE]; + struct rsn_pmksa_cache_entry *pmksa; + int pmksa_count; + + void (*free_cb)(struct rsn_pmksa_cache_entry *entry, void *ctx); + void *ctx; +}; + + +/** + * rsn_pmkid - Calculate PMK identifier + * @pmk: Pairwise master key + * @pmk_len: Length of pmk in bytes + * @aa: Authenticator address + * @spa: Supplicant address + * @pmkid: Buffer for PMKID + * @use_sha256: Whether to use SHA256-based KDF + * + * IEEE Std 802.11i-2004 - 8.5.1.2 Pairwise key hierarchy + * PMKID = HMAC-SHA1-128(PMK, "PMK Name" || AA || SPA) + */ +void rsn_pmkid(const u8 *pmk, size_t pmk_len, const u8 *aa, const u8 *spa, + u8 *pmkid, int use_sha256) +{ + char *title = "PMK Name"; + const u8 *addr[3]; + const size_t len[3] = { 8, ETH_ALEN, ETH_ALEN }; + unsigned char hash[SHA256_MAC_LEN]; + + addr[0] = (u8 *) title; + addr[1] = aa; + addr[2] = spa; + +#ifdef CONFIG_IEEE80211W + if (use_sha256) + hmac_sha256_vector(pmk, pmk_len, 3, addr, len, hash); + else +#endif /* CONFIG_IEEE80211W */ + hmac_sha1_vector(pmk, pmk_len, 3, addr, len, hash); + os_memcpy(pmkid, hash, PMKID_LEN); +} + + +static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa); + + +static void _pmksa_cache_free_entry(struct rsn_pmksa_cache_entry *entry) +{ + if (entry == NULL) + return; + os_free(entry->identity); + ieee802_1x_free_radius_class(&entry->radius_class); + os_free(entry); +} + + +static void pmksa_cache_free_entry(struct rsn_pmksa_cache *pmksa, + struct rsn_pmksa_cache_entry *entry) +{ + struct rsn_pmksa_cache_entry *pos, *prev; + + pmksa->pmksa_count--; + pmksa->free_cb(entry, pmksa->ctx); + pos = pmksa->pmkid[PMKID_HASH(entry->pmkid)]; + prev = NULL; + while (pos) { + if (pos == entry) { + if (prev != NULL) { + prev->hnext = pos->hnext; + } else { + pmksa->pmkid[PMKID_HASH(entry->pmkid)] = + pos->hnext; + } + break; + } + prev = pos; + pos = pos->hnext; + } + + pos = pmksa->pmksa; + prev = NULL; + while (pos) { + if (pos == entry) { + if (prev != NULL) + prev->next = pos->next; + else + pmksa->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 rsn_pmksa_cache *pmksa = eloop_ctx; + struct os_time now; + + os_get_time(&now); + while (pmksa->pmksa && pmksa->pmksa->expiration <= now.sec) { + struct rsn_pmksa_cache_entry *entry = pmksa->pmksa; + pmksa->pmksa = entry->next; + wpa_printf(MSG_DEBUG, "RSN: expired PMKSA cache entry for " + MACSTR, MAC2STR(entry->spa)); + pmksa_cache_free_entry(pmksa, entry); + } + + pmksa_cache_set_expiration(pmksa); +} + + +static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa) +{ + int sec; + struct os_time now; + + eloop_cancel_timeout(pmksa_cache_expire, pmksa, NULL); + if (pmksa->pmksa == NULL) + return; + os_get_time(&now); + sec = pmksa->pmksa->expiration - now.sec; + if (sec < 0) + sec = 0; + eloop_register_timeout(sec + 1, 0, pmksa_cache_expire, pmksa, NULL); +} + + +static void pmksa_cache_from_eapol_data(struct rsn_pmksa_cache_entry *entry, + struct eapol_state_machine *eapol) +{ + if (eapol == NULL) + return; + + if (eapol->identity) { + entry->identity = os_malloc(eapol->identity_len); + if (entry->identity) { + entry->identity_len = eapol->identity_len; + os_memcpy(entry->identity, eapol->identity, + eapol->identity_len); + } + } + + ieee802_1x_copy_radius_class(&entry->radius_class, + &eapol->radius_class); + + entry->eap_type_authsrv = eapol->eap_type_authsrv; + entry->vlan_id = eapol->sta->vlan_id; +} + + +void pmksa_cache_to_eapol_data(struct rsn_pmksa_cache_entry *entry, + struct eapol_state_machine *eapol) +{ + if (entry == NULL || eapol == NULL) + return; + + if (entry->identity) { + os_free(eapol->identity); + eapol->identity = os_malloc(entry->identity_len); + if (eapol->identity) { + eapol->identity_len = entry->identity_len; + os_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); + } + + eapol->eap_type_authsrv = entry->eap_type_authsrv; + eapol->sta->vlan_id = entry->vlan_id; +} + + +static void pmksa_cache_link_entry(struct rsn_pmksa_cache *pmksa, + struct rsn_pmksa_cache_entry *entry) +{ + struct rsn_pmksa_cache_entry *pos, *prev; + + /* Add the new entry; order by expiration time */ + pos = pmksa->pmksa; + prev = NULL; + while (pos) { + if (pos->expiration > entry->expiration) + break; + prev = pos; + pos = pos->next; + } + if (prev == NULL) { + entry->next = pmksa->pmksa; + pmksa->pmksa = entry; + } else { + entry->next = prev->next; + prev->next = entry; + } + entry->hnext = pmksa->pmkid[PMKID_HASH(entry->pmkid)]; + pmksa->pmkid[PMKID_HASH(entry->pmkid)] = entry; + + pmksa->pmksa_count++; + wpa_printf(MSG_DEBUG, "RSN: added PMKSA cache entry for " MACSTR, + MAC2STR(entry->spa)); + wpa_hexdump(MSG_DEBUG, "RSN: added PMKID", entry->pmkid, PMKID_LEN); +} + + +/** + * pmksa_cache_add - Add a PMKSA cache entry + * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init() + * @pmk: The new pairwise master key + * @pmk_len: PMK length in bytes, usually PMK_LEN (32) + * @aa: Authenticator address + * @spa: Supplicant address + * @session_timeout: Session timeout + * @eapol: Pointer to EAPOL state machine data + * @akmp: WPA_KEY_MGMT_* used in key derivation + * Returns: Pointer to the added PMKSA cache entry or %NULL on error + * + * This function create a PMKSA entry for a new PMK and adds it to the PMKSA + * cache. If an old entry is already in the cache for the same Supplicant, + * this entry will be replaced with the new entry. PMKID will be calculated + * based on the PMK. + */ +struct rsn_pmksa_cache_entry * +pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len, + const u8 *aa, const u8 *spa, int session_timeout, + struct eapol_state_machine *eapol, int akmp) +{ + struct rsn_pmksa_cache_entry *entry, *pos; + struct os_time now; + + if (pmk_len > PMK_LEN) + return NULL; + + entry = os_zalloc(sizeof(*entry)); + if (entry == NULL) + return NULL; + os_memcpy(entry->pmk, pmk, pmk_len); + entry->pmk_len = pmk_len; + rsn_pmkid(pmk, pmk_len, aa, spa, entry->pmkid, + wpa_key_mgmt_sha256(akmp)); + os_get_time(&now); + entry->expiration = now.sec; + if (session_timeout > 0) + entry->expiration += session_timeout; + else + entry->expiration += dot11RSNAConfigPMKLifetime; + entry->akmp = akmp; + os_memcpy(entry->spa, spa, ETH_ALEN); + pmksa_cache_from_eapol_data(entry, eapol); + + /* Replace an old entry for the same STA (if found) with the new entry + */ + pos = pmksa_cache_get(pmksa, spa, NULL); + if (pos) + pmksa_cache_free_entry(pmksa, pos); + + if (pmksa->pmksa_count >= pmksa_cache_max_entries && pmksa->pmksa) { + /* Remove the oldest entry to make room for the new entry */ + wpa_printf(MSG_DEBUG, "RSN: removed the oldest PMKSA cache " + "entry (for " MACSTR ") to make room for new one", + MAC2STR(pmksa->pmksa->spa)); + pmksa_cache_free_entry(pmksa, pmksa->pmksa); + } + + pmksa_cache_link_entry(pmksa, entry); + + return entry; +} + + +struct rsn_pmksa_cache_entry * +pmksa_cache_add_okc(struct rsn_pmksa_cache *pmksa, + const struct rsn_pmksa_cache_entry *old_entry, + const u8 *aa, const u8 *pmkid) +{ + struct rsn_pmksa_cache_entry *entry; + + entry = os_zalloc(sizeof(*entry)); + if (entry == NULL) + return NULL; + os_memcpy(entry->pmkid, pmkid, PMKID_LEN); + os_memcpy(entry->pmk, old_entry->pmk, old_entry->pmk_len); + entry->pmk_len = old_entry->pmk_len; + entry->expiration = old_entry->expiration; + entry->akmp = old_entry->akmp; + os_memcpy(entry->spa, old_entry->spa, ETH_ALEN); + entry->opportunistic = 1; + if (old_entry->identity) { + entry->identity = os_malloc(old_entry->identity_len); + if (entry->identity) { + entry->identity_len = old_entry->identity_len; + os_memcpy(entry->identity, old_entry->identity, + old_entry->identity_len); + } + } + ieee802_1x_copy_radius_class(&entry->radius_class, + &old_entry->radius_class); + entry->eap_type_authsrv = old_entry->eap_type_authsrv; + entry->vlan_id = old_entry->vlan_id; + entry->opportunistic = 1; + + pmksa_cache_link_entry(pmksa, entry); + + return entry; +} + + +/** + * pmksa_cache_deinit - Free all entries in PMKSA cache + * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init() + */ +void pmksa_cache_deinit(struct rsn_pmksa_cache *pmksa) +{ + struct rsn_pmksa_cache_entry *entry, *prev; + int i; + + if (pmksa == NULL) + return; + + entry = pmksa->pmksa; + while (entry) { + prev = entry; + entry = entry->next; + _pmksa_cache_free_entry(prev); + } + eloop_cancel_timeout(pmksa_cache_expire, pmksa, NULL); + for (i = 0; i < PMKID_HASH_SIZE; i++) + pmksa->pmkid[i] = NULL; + os_free(pmksa); +} + + +/** + * pmksa_cache_get - Fetch a PMKSA cache entry + * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init() + * @spa: Supplicant address or %NULL to match any + * @pmkid: PMKID or %NULL to match any + * Returns: Pointer to PMKSA cache entry or %NULL if no match was found + */ +struct rsn_pmksa_cache_entry * pmksa_cache_get(struct rsn_pmksa_cache *pmksa, + const u8 *spa, const u8 *pmkid) +{ + struct rsn_pmksa_cache_entry *entry; + + if (pmkid) + entry = pmksa->pmkid[PMKID_HASH(pmkid)]; + else + entry = pmksa->pmksa; + while (entry) { + if ((spa == NULL || + os_memcmp(entry->spa, spa, ETH_ALEN) == 0) && + (pmkid == NULL || + os_memcmp(entry->pmkid, pmkid, PMKID_LEN) == 0)) + return entry; + entry = pmkid ? entry->hnext : entry->next; + } + return NULL; +} + + +/** + * pmksa_cache_get_okc - Fetch a PMKSA cache entry using OKC + * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init() + * @aa: Authenticator address + * @spa: Supplicant address + * @pmkid: PMKID + * Returns: Pointer to PMKSA cache entry or %NULL if no match was found + * + * Use opportunistic key caching (OKC) to find a PMK for a supplicant. + */ +struct rsn_pmksa_cache_entry * pmksa_cache_get_okc( + struct rsn_pmksa_cache *pmksa, const u8 *aa, const u8 *spa, + const u8 *pmkid) +{ + struct rsn_pmksa_cache_entry *entry; + u8 new_pmkid[PMKID_LEN]; + + entry = pmksa->pmksa; + while (entry) { + if (os_memcmp(entry->spa, spa, ETH_ALEN) != 0) + continue; + rsn_pmkid(entry->pmk, entry->pmk_len, aa, spa, new_pmkid, + wpa_key_mgmt_sha256(entry->akmp)); + if (os_memcmp(new_pmkid, pmkid, PMKID_LEN) == 0) + return entry; + entry = entry->next; + } + return NULL; +} + + +/** + * pmksa_cache_init - Initialize PMKSA cache + * @free_cb: Callback function to be called when a PMKSA cache entry is freed + * @ctx: Context pointer for free_cb function + * Returns: Pointer to PMKSA cache data or %NULL on failure + */ +struct rsn_pmksa_cache * +pmksa_cache_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry, + void *ctx), void *ctx) +{ + struct rsn_pmksa_cache *pmksa; + + pmksa = os_zalloc(sizeof(*pmksa)); + if (pmksa) { + pmksa->free_cb = free_cb; + pmksa->ctx = ctx; + } + + return pmksa; +} diff --git a/contrib/wpa/hostapd/pmksa_cache.h b/contrib/wpa/hostapd/pmksa_cache.h new file mode 100644 index 0000000..6ba2da6 --- /dev/null +++ b/contrib/wpa/hostapd/pmksa_cache.h @@ -0,0 +1,62 @@ +/* + * hostapd - PMKSA cache for IEEE 802.11i RSN + * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef PMKSA_CACHE_H +#define PMKSA_CACHE_H + +/** + * struct rsn_pmksa_cache_entry - PMKSA cache entry + */ +struct rsn_pmksa_cache_entry { + struct rsn_pmksa_cache_entry *next, *hnext; + u8 pmkid[PMKID_LEN]; + u8 pmk[PMK_LEN]; + size_t pmk_len; + os_time_t expiration; + int akmp; /* WPA_KEY_MGMT_* */ + u8 spa[ETH_ALEN]; + + u8 *identity; + size_t identity_len; + struct radius_class_data radius_class; + u8 eap_type_authsrv; + int vlan_id; + int opportunistic; +}; + +struct rsn_pmksa_cache; + +struct rsn_pmksa_cache * +pmksa_cache_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry, + void *ctx), void *ctx); +void pmksa_cache_deinit(struct rsn_pmksa_cache *pmksa); +struct rsn_pmksa_cache_entry * pmksa_cache_get(struct rsn_pmksa_cache *pmksa, + const u8 *spa, const u8 *pmkid); +struct rsn_pmksa_cache_entry * pmksa_cache_get_okc( + struct rsn_pmksa_cache *pmksa, const u8 *spa, const u8 *aa, + const u8 *pmkid); +struct rsn_pmksa_cache_entry * +pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len, + const u8 *aa, const u8 *spa, int session_timeout, + struct eapol_state_machine *eapol, int akmp); +struct rsn_pmksa_cache_entry * +pmksa_cache_add_okc(struct rsn_pmksa_cache *pmksa, + const struct rsn_pmksa_cache_entry *old_entry, + const u8 *aa, const u8 *pmkid); +void pmksa_cache_to_eapol_data(struct rsn_pmksa_cache_entry *entry, + struct eapol_state_machine *eapol); +void rsn_pmkid(const u8 *pmk, size_t pmk_len, const u8 *aa, const u8 *spa, + u8 *pmkid, int use_sha256); + +#endif /* PMKSA_CACHE_H */ diff --git a/contrib/wpa/hostapd/preauth.c b/contrib/wpa/hostapd/preauth.c new file mode 100644 index 0000000..36af4e3 --- /dev/null +++ b/contrib/wpa/hostapd/preauth.c @@ -0,0 +1,275 @@ +/* + * hostapd - Authenticator for IEEE 802.11i RSN pre-authentication + * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#ifdef CONFIG_RSN_PREAUTH + +#include "hostapd.h" +#include "l2_packet/l2_packet.h" +#include "ieee802_1x.h" +#include "eloop.h" +#include "sta_info.h" +#include "wpa_common.h" +#include "eapol_sm.h" +#include "wpa.h" +#include "preauth.h" + +#ifndef ETH_P_PREAUTH +#define ETH_P_PREAUTH 0x88C7 /* IEEE 802.11i pre-authentication */ +#endif /* ETH_P_PREAUTH */ + +static const int dot11RSNAConfigPMKLifetime = 43200; + +struct rsn_preauth_interface { + struct rsn_preauth_interface *next; + struct hostapd_data *hapd; + struct l2_packet_data *l2; + char *ifname; + int ifindex; +}; + + +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; + + wpa_printf(MSG_DEBUG, "RSN: receive pre-auth packet " + "from interface '%s'", piface->ifname); + if (len < sizeof(*ethhdr) + sizeof(*hdr)) { + wpa_printf(MSG_DEBUG, "RSN: too short pre-auth packet " + "(len=%lu)", (unsigned long) len); + return; + } + + ethhdr = (struct l2_ethhdr *) buf; + hdr = (struct ieee802_1x_hdr *) (ethhdr + 1); + + if (os_memcmp(ethhdr->h_dest, hapd->own_addr, ETH_ALEN) != 0) { + wpa_printf(MSG_DEBUG, "RSN: pre-auth for foreign address " + MACSTR, MAC2STR(ethhdr->h_dest)); + return; + } + + sta = ap_get_sta(hapd, ethhdr->h_source); + if (sta && (sta->flags & WLAN_STA_ASSOC)) { + wpa_printf(MSG_DEBUG, "RSN: pre-auth for already association " + "STA " MACSTR, MAC2STR(sta->addr)); + return; + } + if (!sta && hdr->type == IEEE802_1X_TYPE_EAPOL_START) { + sta = ap_sta_add(hapd, ethhdr->h_source); + if (sta == NULL) + return; + sta->flags = WLAN_STA_PREAUTH; + + 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; + + wpa_printf(MSG_DEBUG, "RSN pre-auth interface '%s'", ifname); + + piface = os_zalloc(sizeof(*piface)); + if (piface == NULL) + return -1; + piface->hapd = hapd; + + piface->ifname = os_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) { + wpa_printf(MSG_ERROR, "Failed to open register layer 2 access " + "to ETH_P_PREAUTH"); + goto fail2; + } + + piface->next = hapd->preauth_iface; + hapd->preauth_iface = piface; + return 0; + +fail2: + os_free(piface->ifname); +fail1: + os_free(piface); + return -1; +} + + +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); + os_free(prev->ifname); + os_free(prev); + } +} + + +int rsn_preauth_iface_init(struct hostapd_data *hapd) +{ + char *tmp, *start, *end; + + if (hapd->conf->rsn_preauth_interfaces == NULL) + return 0; + + tmp = os_strdup(hapd->conf->rsn_preauth_interfaces); + if (tmp == NULL) + return -1; + start = tmp; + for (;;) { + while (*start == ' ') + start++; + if (*start == '\0') + break; + end = os_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; + } + os_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) +{ + const 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(sta->eapol_sm, &len); + if (len > PMK_LEN) + len = PMK_LEN; + if (success && key) { + if (wpa_auth_pmksa_add_preauth(hapd->wpa_auth, key, len, + sta->addr, + dot11RSNAConfigPMKLifetime, + sta->eapol_sm) == 0) { + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, + HOSTAPD_LEVEL_DEBUG, + "added PMKSA cache entry (pre-auth)"); + } else { + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, + HOSTAPD_LEVEL_DEBUG, + "failed to add PMKSA cache entry " + "(pre-auth)"); + } + } + + /* + * 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) { + wpa_printf(MSG_DEBUG, "RSN: Could not find pre-authentication " + "interface for " MACSTR, MAC2STR(sta->addr)); + return; + } + + ethhdr = os_malloc(sizeof(*ethhdr) + len); + if (ethhdr == NULL) + return; + + os_memcpy(ethhdr->h_dest, sta->addr, ETH_ALEN); + os_memcpy(ethhdr->h_source, hapd->own_addr, ETH_ALEN); + ethhdr->h_proto = htons(ETH_P_PREAUTH); + os_memcpy(ethhdr + 1, buf, len); + + if (l2_packet_send(piface->l2, sta->addr, ETH_P_PREAUTH, (u8 *) ethhdr, + sizeof(*ethhdr) + len) < 0) { + wpa_printf(MSG_ERROR, "Failed to send preauth packet using " + "l2_packet_send\n"); + } + os_free(ethhdr); +} + + +void rsn_preauth_free_station(struct hostapd_data *hapd, struct sta_info *sta) +{ + eloop_cancel_timeout(rsn_preauth_finished_cb, hapd, sta); +} + +#endif /* CONFIG_RSN_PREAUTH */ diff --git a/contrib/wpa/hostapd/preauth.h b/contrib/wpa/hostapd/preauth.h new file mode 100644 index 0000000..5348bee --- /dev/null +++ b/contrib/wpa/hostapd/preauth.h @@ -0,0 +1,58 @@ +/* + * hostapd - Authenticator for IEEE 802.11i RSN pre-authentication + * Copyright (c) 2004-2005, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef PREAUTH_H +#define PREAUTH_H + +#ifdef CONFIG_RSN_PREAUTH + +int rsn_preauth_iface_init(struct hostapd_data *hapd); +void rsn_preauth_iface_deinit(struct hostapd_data *hapd); +void rsn_preauth_finished(struct hostapd_data *hapd, struct sta_info *sta, + int success); +void rsn_preauth_send(struct hostapd_data *hapd, struct sta_info *sta, + u8 *buf, size_t len); +void rsn_preauth_free_station(struct hostapd_data *hapd, struct sta_info *sta); + +#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 inline void rsn_preauth_finished(struct hostapd_data *hapd, + struct sta_info *sta, + int success) +{ +} + +static inline void rsn_preauth_send(struct hostapd_data *hapd, + struct sta_info *sta, + u8 *buf, size_t len) +{ +} + +static inline void rsn_preauth_free_station(struct hostapd_data *hapd, + struct sta_info *sta) +{ +} + +#endif /* CONFIG_RSN_PREAUTH */ + +#endif /* PREAUTH_H */ diff --git a/contrib/wpa/hostapd/sta_info.c b/contrib/wpa/hostapd/sta_info.c new file mode 100644 index 0000000..a139ba9 --- /dev/null +++ b/contrib/wpa/hostapd/sta_info.c @@ -0,0 +1,711 @@ +/* + * hostapd / Station table + * Copyright (c) 2002-2008, Jouni Malinen <j@w1.fi> + * Copyright (c) 2007-2008, Intel Corporation + * + * 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 "includes.h" + +#include "hostapd.h" +#include "sta_info.h" +#include "eloop.h" +#include "accounting.h" +#include "ieee802_1x.h" +#include "ieee802_11.h" +#include "radius/radius.h" +#include "wpa.h" +#include "preauth.h" +#include "radius/radius_client.h" +#include "driver.h" +#include "beacon.h" +#include "hw_features.h" +#include "mlme.h" +#include "vlan_init.h" + +static int ap_sta_in_other_bss(struct hostapd_data *hapd, + struct sta_info *sta, u32 flags); +static void ap_handle_session_timer(void *eloop_ctx, void *timeout_ctx); +#ifdef CONFIG_IEEE80211W +static void ap_sa_query_timer(void *eloop_ctx, void *timeout_ctx); +#endif /* CONFIG_IEEE80211W */ + +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 && os_memcmp(s->addr, sta, 6) != 0) + s = s->hnext; + return s; +} + + +static void ap_sta_list_del(struct hostapd_data *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) { + wpa_printf(MSG_DEBUG, "Could not remove STA " MACSTR " from " + "list.", MAC2STR(sta->addr)); + } else + tmp->next = sta->next; +} + + +void ap_sta_hash_add(struct hostapd_data *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(struct hostapd_data *hapd, struct sta_info *sta) +{ + struct sta_info *s; + + s = hapd->sta_hash[STA_HASH(sta->addr)]; + if (s == NULL) return; + if (os_memcmp(s->addr, sta->addr, 6) == 0) { + hapd->sta_hash[STA_HASH(sta->addr)] = s->hnext; + return; + } + + while (s->hnext != NULL && + os_memcmp(s->hnext->addr, sta->addr, ETH_ALEN) != 0) + s = s->hnext; + if (s->hnext != NULL) + s->hnext = s->hnext->hnext; + else + wpa_printf(MSG_DEBUG, "AP: could not remove STA " MACSTR + " from hash table", MAC2STR(sta->addr)); +} + + +void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta) +{ + int set_beacon = 0; + + accounting_sta_stop(hapd, sta); + + if (!ap_sta_in_other_bss(hapd, sta, WLAN_STA_ASSOC) && + !(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--; + if (sta->nonerp_set) { + sta->nonerp_set = 0; + hapd->iface->num_sta_non_erp--; + if (hapd->iface->num_sta_non_erp == 0) + set_beacon++; + } + + if (sta->no_short_slot_time_set) { + sta->no_short_slot_time_set = 0; + hapd->iface->num_sta_no_short_slot_time--; + if (hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G + && hapd->iface->num_sta_no_short_slot_time == 0) + set_beacon++; + } + + if (sta->no_short_preamble_set) { + sta->no_short_preamble_set = 0; + hapd->iface->num_sta_no_short_preamble--; + if (hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G + && hapd->iface->num_sta_no_short_preamble == 0) + set_beacon++; + } + +#ifdef CONFIG_IEEE80211N + if (sta->no_ht_gf_set) { + sta->no_ht_gf_set = 0; + hapd->iface->num_sta_ht_no_gf--; + } + + if (sta->no_ht_set) { + sta->no_ht_set = 0; + hapd->iface->num_sta_no_ht--; + } + + if (sta->ht_20mhz_set) { + sta->ht_20mhz_set = 0; + hapd->iface->num_sta_ht_20mhz--; + } + + if (hostapd_ht_operation_update(hapd->iface) > 0) + set_beacon++; +#endif /* CONFIG_IEEE80211N */ + + if (set_beacon) + ieee802_11_set_beacons(hapd->iface); + + eloop_cancel_timeout(ap_handle_timer, hapd, sta); + eloop_cancel_timeout(ap_handle_session_timer, hapd, sta); + + ieee802_1x_free_station(sta); + wpa_auth_sta_deinit(sta->wpa_sm); + rsn_preauth_free_station(hapd, sta); + radius_client_flush_auth(hapd->radius, sta->addr); + + os_free(sta->last_assoc_req); + os_free(sta->challenge); + +#ifdef CONFIG_IEEE80211W + os_free(sta->sa_query_trans_id); + eloop_cancel_timeout(ap_sa_query_timer, hapd, sta); +#endif /* CONFIG_IEEE80211W */ + + wpabuf_free(sta->wps_ie); + + os_free(sta); +} + + +void hostapd_free_stas(struct hostapd_data *hapd) +{ + struct sta_info *sta, *prev; + + sta = hapd->sta_list; + + while (sta) { + prev = sta; + if (sta->flags & WLAN_STA_AUTH) { + mlme_deauthenticate_indication( + hapd, sta, WLAN_REASON_UNSPECIFIED); + } + sta = sta->next; + wpa_printf(MSG_DEBUG, "Removing station " MACSTR, + MAC2STR(prev->addr)); + ap_free_sta(hapd, prev); + } +} + + +/** + * ap_handle_timer - Per STA timer handler + * @eloop_ctx: struct hostapd_data * + * @timeout_ctx: struct sta_info * + * + * This function is called to check station activity and to remove inactive + * stations. + */ +void ap_handle_timer(void *eloop_ctx, void *timeout_ctx) +{ + struct hostapd_data *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; + wpa_printf(MSG_DEBUG, "Checking STA " MACSTR " inactivity:", + MAC2STR(sta->addr)); + inactive_sec = hostapd_get_inact_sec(hapd, sta->addr); + if (inactive_sec == -1) { + wpa_printf(MSG_DEBUG, "Could not get station info " + "from kernel driver for " MACSTR ".", + MAC2STR(sta->addr)); + } else if (inactive_sec < hapd->conf->ap_max_inactivity && + sta->flags & WLAN_STA_ASSOC) { + /* station activity detected; reset timeout state */ + wpa_printf(MSG_DEBUG, " Station has been active"); + sta->timeout_next = STA_NULLFUNC; + next_time = hapd->conf->ap_max_inactivity - + inactive_sec; + } + } + + if ((sta->flags & WLAN_STA_ASSOC) && + sta->timeout_next == STA_DISASSOC && + !(sta->flags & WLAN_STA_PENDING_POLL)) { + wpa_printf(MSG_DEBUG, " Station has ACKed data poll"); + /* data nullfunc frame poll did not produce TX errors; assume + * station ACKed it */ + sta->timeout_next = STA_NULLFUNC; + next_time = hapd->conf->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; + + wpa_printf(MSG_DEBUG, " Polling STA with data frame"); + sta->flags |= WLAN_STA_PENDING_POLL; + +#ifndef CONFIG_NATIVE_WINDOWS + os_memset(&hdr, 0, sizeof(hdr)); + if (hapd->driver && + os_strcmp(hapd->driver->name, "hostap") == 0) { + /* + * WLAN_FC_STYPE_NULLFUNC would be more appropriate, + * but it is apparently not retried so TX Exc events + * are not received for it. + */ + hdr.frame_control = + IEEE80211_FC(WLAN_FC_TYPE_DATA, + WLAN_FC_STYPE_DATA); + } else { + hdr.frame_control = + IEEE80211_FC(WLAN_FC_TYPE_DATA, + WLAN_FC_STYPE_NULLFUNC); + } + + hdr.frame_control |= host_to_le16(WLAN_FC_FROMDS); + os_memcpy(hdr.IEEE80211_DA_FROMDS, sta->addr, ETH_ALEN); + os_memcpy(hdr.IEEE80211_BSSID_FROMDS, hapd->own_addr, + ETH_ALEN); + os_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"); +#endif /* CONFIG_NATIVE_WINDOWS */ + } else if (sta->timeout_next != STA_REMOVE) { + int deauth = sta->timeout_next == STA_DEAUTH; + + wpa_printf(MSG_DEBUG, "Sending %s info to STA " MACSTR, + 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_notify_port_enabled(sta->eapol_sm, 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); + mlme_disassociate_indication( + hapd, sta, WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY); + 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; + mlme_deauthenticate_indication( + hapd, sta, + WLAN_REASON_PREV_AUTH_NOT_VALID); + ap_free_sta(hapd, sta); + break; + } +} + + +static void ap_handle_session_timer(void *eloop_ctx, void *timeout_ctx) +{ + struct hostapd_data *hapd = eloop_ctx; + struct sta_info *sta = timeout_ctx; + u8 addr[ETH_ALEN]; + + if (!(sta->flags & WLAN_STA_AUTH)) + return; + + mlme_deauthenticate_indication(hapd, sta, + 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; + os_memcpy(addr, sta->addr, ETH_ALEN); + ap_free_sta(hapd, sta); + hostapd_sta_deauth(hapd, addr, WLAN_REASON_PREV_AUTH_NOT_VALID); +} + + +void ap_sta_session_timeout(struct hostapd_data *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(struct hostapd_data *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; + + wpa_printf(MSG_DEBUG, " New STA"); + if (hapd->num_sta >= hapd->conf->max_num_sta) { + /* FIX: might try to remove some old STAs first? */ + wpa_printf(MSG_DEBUG, "no more room for new STAs (%d/%d)", + hapd->num_sta, hapd->conf->max_num_sta); + return NULL; + } + + sta = os_zalloc(sizeof(struct sta_info)); + if (sta == NULL) { + wpa_printf(MSG_ERROR, "malloc failed"); + return NULL; + } + sta->acct_interim_interval = hapd->conf->radius->acct_interim_interval; + + /* initialize STA info data */ + eloop_register_timeout(hapd->conf->ap_max_inactivity, 0, + ap_handle_timer, hapd, sta); + os_memcpy(sta->addr, addr, ETH_ALEN); + sta->next = hapd->sta_list; + hapd->sta_list = sta; + hapd->num_sta++; + ap_sta_hash_add(hapd, sta); + sta->ssid = &hapd->conf->ssid; + + return sta; +} + + +static int ap_sta_remove(struct hostapd_data *hapd, struct sta_info *sta) +{ + ieee802_1x_notify_port_enabled(sta->eapol_sm, 0); + + wpa_printf(MSG_DEBUG, "Removing STA " MACSTR " from kernel driver", + MAC2STR(sta->addr)); + if (hostapd_sta_remove(hapd, sta->addr) && + sta->flags & WLAN_STA_ASSOC) { + wpa_printf(MSG_DEBUG, "Could not remove station " MACSTR + " from kernel driver.", MAC2STR(sta->addr)); + return -1; + } + return 0; +} + + +static int ap_sta_in_other_bss(struct hostapd_data *hapd, + struct sta_info *sta, u32 flags) +{ + struct hostapd_iface *iface = hapd->iface; + size_t i; + + for (i = 0; i < iface->num_bss; i++) { + struct hostapd_data *bss = iface->bss[i]; + struct sta_info *sta2; + /* bss should always be set during operation, but it may be + * NULL during reconfiguration. Assume the STA is not + * associated to another BSS in that case to avoid NULL pointer + * dereferences. */ + if (bss == hapd || bss == NULL) + continue; + sta2 = ap_get_sta(bss, sta->addr); + if (sta2 && ((sta2->flags & flags) == flags)) + return 1; + } + + return 0; +} + + +void ap_sta_disassociate(struct hostapd_data *hapd, struct sta_info *sta, + u16 reason) +{ + wpa_printf(MSG_DEBUG, "%s: disassociate STA " MACSTR, + hapd->conf->iface, MAC2STR(sta->addr)); + sta->flags &= ~WLAN_STA_ASSOC; + if (!ap_sta_in_other_bss(hapd, sta, WLAN_STA_ASSOC)) + ap_sta_remove(hapd, sta); + sta->timeout_next = STA_DEAUTH; + eloop_cancel_timeout(ap_handle_timer, hapd, sta); + eloop_register_timeout(AP_MAX_INACTIVITY_AFTER_DISASSOC, 0, + ap_handle_timer, hapd, sta); + accounting_sta_stop(hapd, sta); + ieee802_1x_free_station(sta); + + mlme_disassociate_indication(hapd, sta, reason); +} + + +void ap_sta_deauthenticate(struct hostapd_data *hapd, struct sta_info *sta, + u16 reason) +{ + wpa_printf(MSG_DEBUG, "%s: deauthenticate STA " MACSTR, + hapd->conf->iface, MAC2STR(sta->addr)); + sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC); + if (!ap_sta_in_other_bss(hapd, sta, WLAN_STA_ASSOC)) + ap_sta_remove(hapd, sta); + sta->timeout_next = STA_REMOVE; + eloop_cancel_timeout(ap_handle_timer, hapd, sta); + eloop_register_timeout(AP_MAX_INACTIVITY_AFTER_DEAUTH, 0, + ap_handle_timer, hapd, sta); + accounting_sta_stop(hapd, sta); + ieee802_1x_free_station(sta); + + mlme_deauthenticate_indication(hapd, sta, reason); +} + + +int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta, + int old_vlanid) +{ + const char *iface; + struct hostapd_vlan *vlan = NULL; + + /* + * Do not proceed furthur if the vlan id remains same. We do not want + * duplicate dynamic vlan entries. + */ + if (sta->vlan_id == old_vlanid) + return 0; + + /* + * During 1x reauth, if the vlan id changes, then remove the old id and + * proceed furthur to add the new one. + */ + if (old_vlanid > 0) + vlan_remove_dynamic(hapd, old_vlanid); + + iface = hapd->conf->iface; + if (sta->ssid->vlan[0]) + iface = sta->ssid->vlan; + + if (sta->ssid->dynamic_vlan == DYNAMIC_VLAN_DISABLED) + sta->vlan_id = 0; + else if (sta->vlan_id > 0) { + vlan = hapd->conf->vlan; + while (vlan) { + if (vlan->vlan_id == sta->vlan_id || + vlan->vlan_id == VLAN_ID_WILDCARD) { + iface = vlan->ifname; + break; + } + vlan = vlan->next; + } + } + + if (sta->vlan_id > 0 && vlan == NULL) { + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, "could not find VLAN for " + "binding station to (vlan_id=%d)", + sta->vlan_id); + return -1; + } else if (sta->vlan_id > 0 && vlan->vlan_id == VLAN_ID_WILDCARD) { + vlan = vlan_add_dynamic(hapd, vlan, sta->vlan_id); + if (vlan == NULL) { + hostapd_logger(hapd, sta->addr, + HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, "could not add " + "dynamic VLAN interface for vlan_id=%d", + sta->vlan_id); + return -1; + } + + iface = vlan->ifname; + if (vlan_setup_encryption_dyn(hapd, sta->ssid, iface) != 0) { + hostapd_logger(hapd, sta->addr, + HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, "could not " + "configure encryption for dynamic VLAN " + "interface for vlan_id=%d", + sta->vlan_id); + } + + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, "added new dynamic VLAN " + "interface '%s'", iface); + } else if (vlan && vlan->vlan_id == sta->vlan_id) { + if (sta->vlan_id > 0) { + vlan->dynamic_vlan++; + hostapd_logger(hapd, sta->addr, + HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, "updated existing " + "dynamic VLAN interface '%s'", iface); + } + + /* + * Update encryption configuration for statically generated + * VLAN interface. This is only used for static WEP + * configuration for the case where hostapd did not yet know + * which keys are to be used when the interface was added. + */ + if (vlan_setup_encryption_dyn(hapd, sta->ssid, iface) != 0) { + hostapd_logger(hapd, sta->addr, + HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, "could not " + "configure encryption for VLAN " + "interface for vlan_id=%d", + sta->vlan_id); + } + } + + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, "binding station to interface " + "'%s'", iface); + + if (wpa_auth_sta_set_vlan(sta->wpa_sm, sta->vlan_id) < 0) + wpa_printf(MSG_INFO, "Failed to update VLAN-ID for WPA"); + + return hostapd_set_sta_vlan(iface, hapd, sta->addr, sta->vlan_id); +} + + +#ifdef CONFIG_IEEE80211W + +int ap_check_sa_query_timeout(struct hostapd_data *hapd, struct sta_info *sta) +{ + u32 tu; + struct os_time now, passed; + os_get_time(&now); + os_time_sub(&now, &sta->sa_query_start, &passed); + tu = (passed.sec * 1000000 + passed.usec) / 1024; + if (hapd->conf->assoc_sa_query_max_timeout < tu) { + hostapd_logger(hapd, sta->addr, + HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "association SA Query timed out"); + sta->sa_query_timed_out = 1; + os_free(sta->sa_query_trans_id); + sta->sa_query_trans_id = NULL; + sta->sa_query_count = 0; + eloop_cancel_timeout(ap_sa_query_timer, hapd, sta); + return 1; + } + + return 0; +} + + +static void ap_sa_query_timer(void *eloop_ctx, void *timeout_ctx) +{ + struct hostapd_data *hapd = eloop_ctx; + struct sta_info *sta = timeout_ctx; + unsigned int timeout, sec, usec; + u8 *trans_id, *nbuf; + + if (sta->sa_query_count > 0 && + ap_check_sa_query_timeout(hapd, sta)) + return; + + nbuf = os_realloc(sta->sa_query_trans_id, + (sta->sa_query_count + 1) * WLAN_SA_QUERY_TR_ID_LEN); + if (nbuf == NULL) + return; + if (sta->sa_query_count == 0) { + /* Starting a new SA Query procedure */ + os_get_time(&sta->sa_query_start); + } + trans_id = nbuf + sta->sa_query_count * WLAN_SA_QUERY_TR_ID_LEN; + sta->sa_query_trans_id = nbuf; + sta->sa_query_count++; + + os_get_random(trans_id, WLAN_SA_QUERY_TR_ID_LEN); + + timeout = hapd->conf->assoc_sa_query_retry_timeout; + sec = ((timeout / 1000) * 1024) / 1000; + usec = (timeout % 1000) * 1024; + eloop_register_timeout(sec, usec, ap_sa_query_timer, hapd, sta); + + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "association SA Query attempt %d", sta->sa_query_count); + + ieee802_11_send_sa_query_req(hapd, sta->addr, trans_id); +} + + +void ap_sta_start_sa_query(struct hostapd_data *hapd, struct sta_info *sta) +{ + ap_sa_query_timer(hapd, sta); +} + + +void ap_sta_stop_sa_query(struct hostapd_data *hapd, struct sta_info *sta) +{ + eloop_cancel_timeout(ap_sa_query_timer, hapd, sta); + os_free(sta->sa_query_trans_id); + sta->sa_query_trans_id = NULL; + sta->sa_query_count = 0; +} + +#endif /* CONFIG_IEEE80211W */ diff --git a/contrib/wpa/hostapd/sta_info.h b/contrib/wpa/hostapd/sta_info.h new file mode 100644 index 0000000..e835970 --- /dev/null +++ b/contrib/wpa/hostapd/sta_info.h @@ -0,0 +1,43 @@ +/* + * hostapd / Station table + * Copyright (c) 2002-2008, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#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(struct hostapd_data *hapd, struct sta_info *sta); +void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta); +void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta); +void hostapd_free_stas(struct hostapd_data *hapd); +void ap_handle_timer(void *eloop_ctx, void *timeout_ctx); +void ap_sta_session_timeout(struct hostapd_data *hapd, struct sta_info *sta, + u32 session_timeout); +void ap_sta_no_session_timeout(struct hostapd_data *hapd, + struct sta_info *sta); +struct sta_info * ap_sta_add(struct hostapd_data *hapd, const u8 *addr); +void ap_sta_disassociate(struct hostapd_data *hapd, struct sta_info *sta, + u16 reason); +void ap_sta_deauthenticate(struct hostapd_data *hapd, struct sta_info *sta, + u16 reason); +int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta, + int old_vlanid); +void ap_sta_start_sa_query(struct hostapd_data *hapd, struct sta_info *sta); +void ap_sta_stop_sa_query(struct hostapd_data *hapd, struct sta_info *sta); +int ap_check_sa_query_timeout(struct hostapd_data *hapd, struct sta_info *sta); + +#endif /* STA_INFO_H */ diff --git a/contrib/wpa/hostapd/vlan_init.c b/contrib/wpa/hostapd/vlan_init.c new file mode 100644 index 0000000..87c61e2 --- /dev/null +++ b/contrib/wpa/hostapd/vlan_init.c @@ -0,0 +1,826 @@ +/* + * hostapd / VLAN initialization + * Copyright 2003, Instant802 Networks, Inc. + * Copyright 2005-2006, Devicescape Software, Inc. + * + * 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 "includes.h" + +#include "hostapd.h" +#include "driver.h" +#include "vlan_init.h" + + +#ifdef CONFIG_FULL_DYNAMIC_VLAN + +#include <net/if.h> +#include <sys/ioctl.h> +#include <linux/sockios.h> +#include <linux/if_vlan.h> +#include <linux/if_bridge.h> + +#include "priv_netlink.h" +#include "eloop.h" + + +struct full_dynamic_vlan { + int s; /* socket on which to listen for new/removed interfaces. */ +}; + + +static int ifconfig_helper(const char *if_name, int up) +{ + int fd; + struct ifreq ifr; + + if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + perror("socket[AF_INET,SOCK_STREAM]"); + return -1; + } + + os_memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, if_name, IFNAMSIZ); + + if (ioctl(fd, SIOCGIFFLAGS, &ifr) != 0) { + perror("ioctl[SIOCGIFFLAGS]"); + close(fd); + return -1; + } + + if (up) + ifr.ifr_flags |= IFF_UP; + else + ifr.ifr_flags &= ~IFF_UP; + + if (ioctl(fd, SIOCSIFFLAGS, &ifr) != 0) { + perror("ioctl[SIOCSIFFLAGS]"); + close(fd); + return -1; + } + + close(fd); + return 0; +} + + +static int ifconfig_up(const char *if_name) +{ + return ifconfig_helper(if_name, 1); +} + + +static int ifconfig_down(const char *if_name) +{ + return ifconfig_helper(if_name, 0); +} + + +/* + * These are only available in recent linux headers (without the leading + * underscore). + */ +#define _GET_VLAN_REALDEV_NAME_CMD 8 +#define _GET_VLAN_VID_CMD 9 + +/* This value should be 256 ONLY. If it is something else, then hostapd + * might crash!, as this value has been hard-coded in 2.4.x kernel + * bridging code. + */ +#define MAX_BR_PORTS 256 + +static int br_delif(const char *br_name, const char *if_name) +{ + int fd; + struct ifreq ifr; + unsigned long args[2]; + int if_index; + + if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + perror("socket[AF_INET,SOCK_STREAM]"); + return -1; + } + + if_index = if_nametoindex(if_name); + + if (if_index == 0) { + printf("Failure determining interface index for '%s'\n", + if_name); + close(fd); + return -1; + } + + args[0] = BRCTL_DEL_IF; + args[1] = if_index; + + os_strlcpy(ifr.ifr_name, br_name, sizeof(ifr.ifr_name)); + ifr.ifr_data = (__caddr_t) args; + + if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0 && errno != EINVAL) { + /* No error if interface already removed. */ + perror("ioctl[SIOCDEVPRIVATE,BRCTL_DEL_IF]"); + close(fd); + return -1; + } + + close(fd); + return 0; +} + + +/* + Add interface 'if_name' to the bridge 'br_name' + + returns -1 on error + returns 1 if the interface is already part of the bridge + returns 0 otherwise +*/ +static int br_addif(const char *br_name, const char *if_name) +{ + int fd; + struct ifreq ifr; + unsigned long args[2]; + int if_index; + + if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + perror("socket[AF_INET,SOCK_STREAM]"); + return -1; + } + + if_index = if_nametoindex(if_name); + + if (if_index == 0) { + printf("Failure determining interface index for '%s'\n", + if_name); + close(fd); + return -1; + } + + args[0] = BRCTL_ADD_IF; + args[1] = if_index; + + os_strlcpy(ifr.ifr_name, br_name, sizeof(ifr.ifr_name)); + ifr.ifr_data = (__caddr_t) args; + + if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0) { + if (errno == EBUSY) { + /* The interface is already added. */ + close(fd); + return 1; + } + + perror("ioctl[SIOCDEVPRIVATE,BRCTL_ADD_IF]"); + close(fd); + return -1; + } + + close(fd); + return 0; +} + + +static int br_delbr(const char *br_name) +{ + int fd; + unsigned long arg[2]; + + if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + perror("socket[AF_INET,SOCK_STREAM]"); + return -1; + } + + arg[0] = BRCTL_DEL_BRIDGE; + arg[1] = (unsigned long) br_name; + + if (ioctl(fd, SIOCGIFBR, arg) < 0 && errno != ENXIO) { + /* No error if bridge already removed. */ + perror("ioctl[BRCTL_DEL_BRIDGE]"); + close(fd); + return -1; + } + + close(fd); + return 0; +} + + +/* + Add a bridge with the name 'br_name'. + + returns -1 on error + returns 1 if the bridge already exists + returns 0 otherwise +*/ +static int br_addbr(const char *br_name) +{ + int fd; + unsigned long arg[2]; + + if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + perror("socket[AF_INET,SOCK_STREAM]"); + return -1; + } + + arg[0] = BRCTL_ADD_BRIDGE; + arg[1] = (unsigned long) br_name; + + if (ioctl(fd, SIOCGIFBR, arg) < 0) { + if (errno == EEXIST) { + /* The bridge is already added. */ + close(fd); + return 1; + } else { + perror("ioctl[BRCTL_ADD_BRIDGE]"); + close(fd); + return -1; + } + } + + close(fd); + return 0; +} + + +static int br_getnumports(const char *br_name) +{ + int fd; + int i; + int port_cnt = 0; + unsigned long arg[4]; + int ifindices[MAX_BR_PORTS]; + struct ifreq ifr; + + if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + perror("socket[AF_INET,SOCK_STREAM]"); + return -1; + } + + arg[0] = BRCTL_GET_PORT_LIST; + arg[1] = (unsigned long) ifindices; + arg[2] = MAX_BR_PORTS; + arg[3] = 0; + + os_memset(ifindices, 0, sizeof(ifindices)); + os_strlcpy(ifr.ifr_name, br_name, sizeof(ifr.ifr_name)); + ifr.ifr_data = (__caddr_t) arg; + + if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0) { + perror("ioctl[SIOCDEVPRIVATE,BRCTL_GET_PORT_LIST]"); + close(fd); + return -1; + } + + for (i = 1; i < MAX_BR_PORTS; i++) { + if (ifindices[i] > 0) { + port_cnt++; + } + } + + close(fd); + return port_cnt; +} + + +static int vlan_rem(const char *if_name) +{ + int fd; + struct vlan_ioctl_args if_request; + + if ((os_strlen(if_name) + 1) > sizeof(if_request.device1)) { + fprintf(stderr, "Interface name to long.\n"); + return -1; + } + + if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + perror("socket[AF_INET,SOCK_STREAM]"); + return -1; + } + + os_memset(&if_request, 0, sizeof(if_request)); + + os_strlcpy(if_request.device1, if_name, sizeof(if_request.device1)); + if_request.cmd = DEL_VLAN_CMD; + + if (ioctl(fd, SIOCSIFVLAN, &if_request) < 0) { + perror("ioctl[SIOCSIFVLAN,DEL_VLAN_CMD]"); + close(fd); + return -1; + } + + close(fd); + return 0; +} + + +/* + Add a vlan interface with VLAN ID 'vid' and tagged interface + 'if_name'. + + returns -1 on error + returns 1 if the interface already exists + returns 0 otherwise +*/ +static int vlan_add(const char *if_name, int vid) +{ + int fd; + struct vlan_ioctl_args if_request; + + ifconfig_up(if_name); + + if ((os_strlen(if_name) + 1) > sizeof(if_request.device1)) { + fprintf(stderr, "Interface name to long.\n"); + return -1; + } + + if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + perror("socket[AF_INET,SOCK_STREAM]"); + return -1; + } + + os_memset(&if_request, 0, sizeof(if_request)); + + /* Determine if a suitable vlan device already exists. */ + + os_snprintf(if_request.device1, sizeof(if_request.device1), "vlan%d", + vid); + + if_request.cmd = _GET_VLAN_VID_CMD; + + if (ioctl(fd, SIOCSIFVLAN, &if_request) == 0) { + + if (if_request.u.VID == vid) { + if_request.cmd = _GET_VLAN_REALDEV_NAME_CMD; + + if (ioctl(fd, SIOCSIFVLAN, &if_request) == 0 && + os_strncmp(if_request.u.device2, if_name, + sizeof(if_request.u.device2)) == 0) { + close(fd); + return 1; + } + } + } + + /* A suitable vlan device does not already exist, add one. */ + + os_memset(&if_request, 0, sizeof(if_request)); + os_strlcpy(if_request.device1, if_name, sizeof(if_request.device1)); + if_request.u.VID = vid; + if_request.cmd = ADD_VLAN_CMD; + + if (ioctl(fd, SIOCSIFVLAN, &if_request) < 0) { + perror("ioctl[SIOCSIFVLAN,ADD_VLAN_CMD]"); + close(fd); + return -1; + } + + close(fd); + return 0; +} + + +static int vlan_set_name_type(unsigned int name_type) +{ + int fd; + struct vlan_ioctl_args if_request; + + if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + perror("socket[AF_INET,SOCK_STREAM]"); + return -1; + } + + os_memset(&if_request, 0, sizeof(if_request)); + + if_request.u.name_type = name_type; + if_request.cmd = SET_VLAN_NAME_TYPE_CMD; + if (ioctl(fd, SIOCSIFVLAN, &if_request) < 0) { + perror("ioctl[SIOCSIFVLAN,SET_VLAN_NAME_TYPE_CMD]"); + close(fd); + return -1; + } + + close(fd); + return 0; +} + + +static void vlan_newlink(char *ifname, struct hostapd_data *hapd) +{ + char vlan_ifname[IFNAMSIZ]; + char br_name[IFNAMSIZ]; + struct hostapd_vlan *vlan = hapd->conf->vlan; + char *tagged_interface = hapd->conf->ssid.vlan_tagged_interface; + + while (vlan) { + if (os_strcmp(ifname, vlan->ifname) == 0) { + + os_snprintf(br_name, sizeof(br_name), "brvlan%d", + vlan->vlan_id); + + if (!br_addbr(br_name)) + vlan->clean |= DVLAN_CLEAN_BR; + + ifconfig_up(br_name); + + if (tagged_interface) { + + if (!vlan_add(tagged_interface, vlan->vlan_id)) + vlan->clean |= DVLAN_CLEAN_VLAN; + + os_snprintf(vlan_ifname, sizeof(vlan_ifname), + "vlan%d", vlan->vlan_id); + + if (!br_addif(br_name, vlan_ifname)) + vlan->clean |= DVLAN_CLEAN_VLAN_PORT; + + ifconfig_up(vlan_ifname); + } + + if (!br_addif(br_name, ifname)) + vlan->clean |= DVLAN_CLEAN_WLAN_PORT; + + ifconfig_up(ifname); + + break; + } + vlan = vlan->next; + } +} + + +static void vlan_dellink(char *ifname, struct hostapd_data *hapd) +{ + char vlan_ifname[IFNAMSIZ]; + char br_name[IFNAMSIZ]; + struct hostapd_vlan *first, *prev, *vlan = hapd->conf->vlan; + char *tagged_interface = hapd->conf->ssid.vlan_tagged_interface; + int numports; + + first = prev = vlan; + + while (vlan) { + if (os_strcmp(ifname, vlan->ifname) == 0) { + os_snprintf(br_name, sizeof(br_name), "brvlan%d", + vlan->vlan_id); + + if (tagged_interface) { + os_snprintf(vlan_ifname, sizeof(vlan_ifname), + "vlan%d", vlan->vlan_id); + + numports = br_getnumports(br_name); + if (numports == 1) { + br_delif(br_name, vlan_ifname); + + vlan_rem(vlan_ifname); + + ifconfig_down(br_name); + br_delbr(br_name); + } + } + + if (vlan == first) { + hapd->conf->vlan = vlan->next; + } else { + prev->next = vlan->next; + } + os_free(vlan); + + break; + } + prev = vlan; + vlan = vlan->next; + } +} + + +static void +vlan_read_ifnames(struct nlmsghdr *h, size_t len, int del, + struct hostapd_data *hapd) +{ + struct ifinfomsg *ifi; + int attrlen, nlmsg_len, rta_len; + struct rtattr *attr; + + if (len < sizeof(*ifi)) + return; + + ifi = NLMSG_DATA(h); + + nlmsg_len = NLMSG_ALIGN(sizeof(struct ifinfomsg)); + + attrlen = h->nlmsg_len - nlmsg_len; + if (attrlen < 0) + return; + + attr = (struct rtattr *) (((char *) ifi) + nlmsg_len); + + rta_len = RTA_ALIGN(sizeof(struct rtattr)); + while (RTA_OK(attr, attrlen)) { + char ifname[IFNAMSIZ + 1]; + + if (attr->rta_type == IFLA_IFNAME) { + int n = attr->rta_len - rta_len; + if (n < 0) + break; + + os_memset(ifname, 0, sizeof(ifname)); + + if ((size_t) n > sizeof(ifname)) + n = sizeof(ifname); + os_memcpy(ifname, ((char *) attr) + rta_len, n); + + if (del) + vlan_dellink(ifname, hapd); + else + vlan_newlink(ifname, hapd); + } + + attr = RTA_NEXT(attr, attrlen); + } +} + + +static void vlan_event_receive(int sock, void *eloop_ctx, void *sock_ctx) +{ + char buf[8192]; + int left; + struct sockaddr_nl from; + socklen_t fromlen; + struct nlmsghdr *h; + struct hostapd_data *hapd = eloop_ctx; + + fromlen = sizeof(from); + left = recvfrom(sock, buf, sizeof(buf), MSG_DONTWAIT, + (struct sockaddr *) &from, &fromlen); + if (left < 0) { + if (errno != EINTR && errno != EAGAIN) + perror("recvfrom(netlink)"); + return; + } + + h = (struct nlmsghdr *) buf; + while (left >= (int) sizeof(*h)) { + int len, plen; + + len = h->nlmsg_len; + plen = len - sizeof(*h); + if (len > left || plen < 0) { + printf("Malformed netlink message: " + "len=%d left=%d plen=%d", len, left, plen); + break; + } + + switch (h->nlmsg_type) { + case RTM_NEWLINK: + vlan_read_ifnames(h, plen, 0, hapd); + break; + case RTM_DELLINK: + vlan_read_ifnames(h, plen, 1, hapd); + break; + } + + len = NLMSG_ALIGN(len); + left -= len; + h = (struct nlmsghdr *) ((char *) h + len); + } + + if (left > 0) { + printf("%d extra bytes in the end of netlink message", + left); + } +} + + +static struct full_dynamic_vlan * +full_dynamic_vlan_init(struct hostapd_data *hapd) +{ + struct sockaddr_nl local; + struct full_dynamic_vlan *priv; + + priv = os_zalloc(sizeof(*priv)); + if (priv == NULL) + return NULL; + + vlan_set_name_type(VLAN_NAME_TYPE_PLUS_VID_NO_PAD); + + priv->s = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (priv->s < 0) { + perror("socket(PF_NETLINK,SOCK_RAW,NETLINK_ROUTE)"); + os_free(priv); + return NULL; + } + + os_memset(&local, 0, sizeof(local)); + local.nl_family = AF_NETLINK; + local.nl_groups = RTMGRP_LINK; + if (bind(priv->s, (struct sockaddr *) &local, sizeof(local)) < 0) { + perror("bind(netlink)"); + close(priv->s); + os_free(priv); + return NULL; + } + + if (eloop_register_read_sock(priv->s, vlan_event_receive, hapd, NULL)) + { + close(priv->s); + os_free(priv); + return NULL; + } + + return priv; +} + + +static void full_dynamic_vlan_deinit(struct full_dynamic_vlan *priv) +{ + if (priv == NULL) + return; + eloop_unregister_read_sock(priv->s); + close(priv->s); + os_free(priv); +} +#endif /* CONFIG_FULL_DYNAMIC_VLAN */ + + +int vlan_setup_encryption_dyn(struct hostapd_data *hapd, + struct hostapd_ssid *mssid, const char *dyn_vlan) +{ + int i; + + if (dyn_vlan == NULL) + return 0; + + /* Static WEP keys are set here; IEEE 802.1X and WPA uses their own + * functions for setting up dynamic broadcast keys. */ + for (i = 0; i < 4; i++) { + if (mssid->wep.key[i] && + hostapd_set_encryption(dyn_vlan, hapd, "WEP", NULL, + i, mssid->wep.key[i], + mssid->wep.len[i], + i == mssid->wep.idx)) { + printf("VLAN: Could not set WEP encryption for " + "dynamic VLAN.\n"); + return -1; + } + } + + return 0; +} + + +static int vlan_dynamic_add(struct hostapd_data *hapd, + struct hostapd_vlan *vlan) +{ + while (vlan) { + if (vlan->vlan_id != VLAN_ID_WILDCARD && + hostapd_if_add(hapd, HOSTAPD_IF_VLAN, vlan->ifname, NULL)) + { + if (errno != EEXIST) { + printf("Could not add VLAN iface: %s: %s\n", + vlan->ifname, strerror(errno)); + return -1; + } + } + + vlan = vlan->next; + } + + return 0; +} + + +static void vlan_dynamic_remove(struct hostapd_data *hapd, + struct hostapd_vlan *vlan) +{ + struct hostapd_vlan *next; + + while (vlan) { + next = vlan->next; + + if (vlan->vlan_id != VLAN_ID_WILDCARD && + hostapd_if_remove(hapd, HOSTAPD_IF_VLAN, vlan->ifname, + NULL)) { + printf("Could not remove VLAN iface: %s: %s\n", + vlan->ifname, strerror(errno)); + } +#ifdef CONFIG_FULL_DYNAMIC_VLAN + if (vlan->clean) + vlan_dellink(vlan->ifname, hapd); +#endif /* CONFIG_FULL_DYNAMIC_VLAN */ + + vlan = next; + } +} + + +int vlan_init(struct hostapd_data *hapd) +{ + if (vlan_dynamic_add(hapd, hapd->conf->vlan)) + return -1; + +#ifdef CONFIG_FULL_DYNAMIC_VLAN + hapd->full_dynamic_vlan = full_dynamic_vlan_init(hapd); +#endif /* CONFIG_FULL_DYNAMIC_VLAN */ + + return 0; +} + + +void vlan_deinit(struct hostapd_data *hapd) +{ + vlan_dynamic_remove(hapd, hapd->conf->vlan); + +#ifdef CONFIG_FULL_DYNAMIC_VLAN + full_dynamic_vlan_deinit(hapd->full_dynamic_vlan); +#endif /* CONFIG_FULL_DYNAMIC_VLAN */ +} + + +int vlan_reconfig(struct hostapd_data *hapd, struct hostapd_config *oldconf, + struct hostapd_bss_config *oldbss) +{ + vlan_dynamic_remove(hapd, oldbss->vlan); + if (vlan_dynamic_add(hapd, hapd->conf->vlan)) + return -1; + + return 0; +} + + +struct hostapd_vlan * vlan_add_dynamic(struct hostapd_data *hapd, + struct hostapd_vlan *vlan, + int vlan_id) +{ + struct hostapd_vlan *n; + char *ifname, *pos; + + if (vlan == NULL || vlan_id <= 0 || vlan_id > MAX_VLAN_ID || + vlan->vlan_id != VLAN_ID_WILDCARD) + return NULL; + + ifname = os_strdup(vlan->ifname); + if (ifname == NULL) + return NULL; + pos = os_strchr(ifname, '#'); + if (pos == NULL) { + os_free(ifname); + return NULL; + } + *pos++ = '\0'; + + n = os_zalloc(sizeof(*n)); + if (n == NULL) { + os_free(ifname); + return NULL; + } + + n->vlan_id = vlan_id; + n->dynamic_vlan = 1; + + os_snprintf(n->ifname, sizeof(n->ifname), "%s%d%s", ifname, vlan_id, + pos); + os_free(ifname); + + if (hostapd_if_add(hapd, HOSTAPD_IF_VLAN, n->ifname, NULL)) { + os_free(n); + return NULL; + } + + n->next = hapd->conf->vlan; + hapd->conf->vlan = n; + + return n; +} + + +int vlan_remove_dynamic(struct hostapd_data *hapd, int vlan_id) +{ + struct hostapd_vlan *vlan; + + if (vlan_id <= 0 || vlan_id > MAX_VLAN_ID) + return 1; + + vlan = hapd->conf->vlan; + while (vlan) { + if (vlan->vlan_id == vlan_id && vlan->dynamic_vlan > 0) { + vlan->dynamic_vlan--; + break; + } + vlan = vlan->next; + } + + if (vlan == NULL) + return 1; + + if (vlan->dynamic_vlan == 0) + hostapd_if_remove(hapd, HOSTAPD_IF_VLAN, vlan->ifname, NULL); + + return 0; +} diff --git a/contrib/wpa/hostapd/vlan_init.h b/contrib/wpa/hostapd/vlan_init.h new file mode 100644 index 0000000..cf55ac2 --- /dev/null +++ b/contrib/wpa/hostapd/vlan_init.h @@ -0,0 +1,31 @@ +/* + * hostapd / VLAN initialization + * Copyright 2003, Instant802 Networks, Inc. + * Copyright 2005, Devicescape Software, Inc. + * + * 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 VLAN_INIT_H +#define VLAN_INIT_H + +int vlan_init(struct hostapd_data *hapd); +void vlan_deinit(struct hostapd_data *hapd); +int vlan_reconfig(struct hostapd_data *hapd, struct hostapd_config *oldconf, + struct hostapd_bss_config *oldbss); +struct hostapd_vlan * vlan_add_dynamic(struct hostapd_data *hapd, + struct hostapd_vlan *vlan, + int vlan_id); +int vlan_remove_dynamic(struct hostapd_data *hapd, int vlan_id); +int vlan_setup_encryption_dyn(struct hostapd_data *hapd, + struct hostapd_ssid *mssid, + const char *dyn_vlan); + +#endif /* VLAN_INIT_H */ diff --git a/contrib/wpa/hostapd/wired.conf b/contrib/wpa/hostapd/wired.conf new file mode 100644 index 0000000..956f8c5 --- /dev/null +++ b/contrib/wpa/hostapd/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/wpa/hostapd/wme.c b/contrib/wpa/hostapd/wme.c new file mode 100644 index 0000000..727ee7e --- /dev/null +++ b/contrib/wpa/hostapd/wme.c @@ -0,0 +1,262 @@ +/* + * hostapd / WMM (Wi-Fi Multimedia) + * Copyright 2002-2003, Instant802 Networks, Inc. + * Copyright 2005-2006, Devicescape Software, Inc. + * + * 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 "includes.h" + +#include "hostapd.h" +#include "ieee802_11.h" +#include "wme.h" +#include "sta_info.h" +#include "driver.h" + + +/* TODO: maintain separate sequence and fragment numbers for each AC + * TODO: IGMP snooping to track which multicasts to forward - and use QOS-DATA + * if only WME stations are receiving a certain group */ + + +static u8 wme_oui[3] = { 0x00, 0x50, 0xf2 }; + + +/* Add WME Parameter Element to Beacon and Probe Response frames. */ +u8 * hostapd_eid_wme(struct hostapd_data *hapd, u8 *eid) +{ + u8 *pos = eid; + struct wme_parameter_element *wme = + (struct wme_parameter_element *) (pos + 2); + int e; + + if (!hapd->conf->wme_enabled) + return eid; + eid[0] = WLAN_EID_VENDOR_SPECIFIC; + wme->oui[0] = 0x00; + wme->oui[1] = 0x50; + wme->oui[2] = 0xf2; + wme->oui_type = WME_OUI_TYPE; + wme->oui_subtype = WME_OUI_SUBTYPE_PARAMETER_ELEMENT; + wme->version = WME_VERSION; + wme->acInfo = hapd->parameter_set_count & 0xf; + + /* fill in a parameter set record for each AC */ + for (e = 0; e < 4; e++) { + struct wme_ac_parameter *ac = &wme->ac[e]; + struct hostapd_wme_ac_params *acp = + &hapd->iconf->wme_ac_params[e]; + + ac->aifsn = acp->aifs; + ac->acm = acp->admission_control_mandatory; + ac->aci = e; + ac->reserved = 0; + ac->eCWmin = acp->cwmin; + ac->eCWmax = acp->cwmax; + ac->txopLimit = host_to_le16(acp->txopLimit); + } + + pos = (u8 *) (wme + 1); + eid[1] = pos - eid - 2; /* element length */ + + return pos; +} + + +/* This function is called when a station sends an association request with + * WME info element. The function returns zero on success or non-zero on any + * error in WME element. eid does not include Element ID and Length octets. */ +int hostapd_eid_wme_valid(struct hostapd_data *hapd, u8 *eid, size_t len) +{ + struct wme_information_element *wme; + + wpa_hexdump(MSG_MSGDUMP, "WME IE", eid, len); + + if (len < sizeof(struct wme_information_element)) { + wpa_printf(MSG_DEBUG, "Too short WME IE (len=%lu)", + (unsigned long) len); + return -1; + } + + wme = (struct wme_information_element *) eid; + wpa_printf(MSG_DEBUG, "Validating WME IE: OUI %02x:%02x:%02x " + "OUI type %d OUI sub-type %d version %d", + wme->oui[0], wme->oui[1], wme->oui[2], wme->oui_type, + wme->oui_subtype, wme->version); + if (os_memcmp(wme->oui, wme_oui, sizeof(wme_oui)) != 0 || + wme->oui_type != WME_OUI_TYPE || + wme->oui_subtype != WME_OUI_SUBTYPE_INFORMATION_ELEMENT || + wme->version != WME_VERSION) { + wpa_printf(MSG_DEBUG, "Unsupported WME IE OUI/Type/Subtype/" + "Version"); + return -1; + } + + return 0; +} + + +/* This function is called when a station sends an ACK frame for an AssocResp + * frame (status=success) and the matching AssocReq contained a WME element. + */ +int hostapd_wme_sta_config(struct hostapd_data *hapd, struct sta_info *sta) +{ + /* update kernel STA data for WME related items (WLAN_STA_WPA flag) */ + if (sta->flags & WLAN_STA_WME) + hostapd_sta_set_flags(hapd, sta->addr, sta->flags, + WLAN_STA_WME, ~0); + else + hostapd_sta_set_flags(hapd, sta->addr, sta->flags, + 0, ~WLAN_STA_WME); + + return 0; +} + + +static void wme_send_action(struct hostapd_data *hapd, const u8 *addr, + const struct wme_tspec_info_element *tspec, + u8 action_code, u8 dialogue_token, u8 status_code) +{ + u8 buf[256]; + struct ieee80211_mgmt *m = (struct ieee80211_mgmt *) buf; + struct wme_tspec_info_element *t = + (struct wme_tspec_info_element *) + m->u.action.u.wme_action.variable; + int len; + + hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "action response - reason %d", status_code); + os_memset(buf, 0, sizeof(buf)); + m->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, + WLAN_FC_STYPE_ACTION); + os_memcpy(m->da, addr, ETH_ALEN); + os_memcpy(m->sa, hapd->own_addr, ETH_ALEN); + os_memcpy(m->bssid, hapd->own_addr, ETH_ALEN); + m->u.action.category = WLAN_ACTION_WMM; + m->u.action.u.wme_action.action_code = action_code; + m->u.action.u.wme_action.dialog_token = dialogue_token; + m->u.action.u.wme_action.status_code = status_code; + os_memcpy(t, tspec, sizeof(struct wme_tspec_info_element)); + len = ((u8 *) (t + 1)) - buf; + + if (hostapd_send_mgmt_frame(hapd, m, len, 0) < 0) + perror("wme_send_action: send"); +} + + +/* given frame data payload size in bytes, and data_rate in bits per second + * returns time to complete frame exchange */ +/* FIX: should not use floating point types */ +static double wme_frame_exchange_time(int bytes, int data_rate, int encryption, + int cts_protection) +{ + /* TODO: account for MAC/PHY headers correctly */ + /* TODO: account for encryption headers */ + /* TODO: account for WDS headers */ + /* TODO: account for CTS protection */ + /* TODO: account for SIFS + ACK at minimum PHY rate */ + return (bytes + 400) * 8.0 / data_rate; +} + + +static void wme_setup_request(struct hostapd_data *hapd, + struct ieee80211_mgmt *mgmt, + struct wme_tspec_info_element *tspec, size_t len) +{ + /* FIX: should not use floating point types */ + double medium_time, pps; + + /* TODO: account for airtime and answer no to tspec setup requests + * when none left!! */ + + pps = (tspec->mean_data_rate / 8.0) / tspec->nominal_msdu_size; + medium_time = (tspec->surplus_bandwidth_allowance / 8) * pps * + wme_frame_exchange_time(tspec->nominal_msdu_size, + tspec->minimum_phy_rate, 0, 0); + tspec->medium_time = medium_time * 1000000.0 / 32.0; + + wme_send_action(hapd, mgmt->sa, tspec, WME_ACTION_CODE_SETUP_RESPONSE, + mgmt->u.action.u.wme_action.dialog_token, + WME_SETUP_RESPONSE_STATUS_ADMISSION_ACCEPTED); +} + + +void hostapd_wme_action(struct hostapd_data *hapd, struct ieee80211_mgmt *mgmt, + size_t len) +{ + int action_code; + int left = len - IEEE80211_HDRLEN - 4; + u8 *pos = ((u8 *) mgmt) + IEEE80211_HDRLEN + 4; + struct ieee802_11_elems elems; + struct sta_info *sta = ap_get_sta(hapd, mgmt->sa); + + /* check that the request comes from a valid station */ + if (!sta || + (sta->flags & (WLAN_STA_ASSOC | WLAN_STA_WME)) != + (WLAN_STA_ASSOC | WLAN_STA_WME)) { + hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "wme action received is not from associated wme" + " station"); + /* TODO: respond with action frame refused status code */ + return; + } + + /* extract the tspec info element */ + if (ieee802_11_parse_elems(pos, left, &elems, 1) == ParseFailed) { + hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "hostapd_wme_action - could not parse wme " + "action"); + /* TODO: respond with action frame invalid parameters status + * code */ + return; + } + + if (!elems.wme_tspec || + elems.wme_tspec_len != (sizeof(struct wme_tspec_info_element) - 2)) + { + hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "hostapd_wme_action - missing or wrong length " + "tspec"); + /* TODO: respond with action frame invalid parameters status + * code */ + return; + } + + /* TODO: check the request is for an AC with ACM set, if not, refuse + * request */ + + action_code = mgmt->u.action.u.wme_action.action_code; + switch (action_code) { + case WME_ACTION_CODE_SETUP_REQUEST: + wme_setup_request(hapd, mgmt, (struct wme_tspec_info_element *) + elems.wme_tspec, len); + return; +#if 0 + /* TODO: needed for client implementation */ + case WME_ACTION_CODE_SETUP_RESPONSE: + wme_setup_request(hapd, mgmt, len); + return; + /* TODO: handle station teardown requests */ + case WME_ACTION_CODE_TEARDOWN: + wme_teardown(hapd, mgmt, len); + return; +#endif + } + + hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "hostapd_wme_action - unknown action code %d", + action_code); +} diff --git a/contrib/wpa/hostapd/wme.h b/contrib/wpa/hostapd/wme.h new file mode 100644 index 0000000..4ee281a --- /dev/null +++ b/contrib/wpa/hostapd/wme.h @@ -0,0 +1,129 @@ +/* + * hostapd / WMM (Wi-Fi Multimedia) + * Copyright 2002-2003, Instant802 Networks, Inc. + * Copyright 2005-2006, Devicescape Software, Inc. + * + * 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 WME_H +#define WME_H + +#ifdef __linux__ +#include <endian.h> +#endif /* __linux__ */ + +#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__DragonFly__) +#include <sys/types.h> +#include <sys/endian.h> +#endif /* defined(__FreeBSD__) || defined(__NetBSD__) || + * defined(__DragonFly__) */ + + +extern inline u16 tsinfo(int tag1d, int contention_based, int direction) +{ + return (tag1d << 11) | (contention_based << 7) | (direction << 5) | + (tag1d << 1); +} + + +struct wme_information_element { + /* required fields for WME version 1 */ + u8 oui[3]; + u8 oui_type; + u8 oui_subtype; + u8 version; + u8 acInfo; + +} __attribute__ ((packed)); + +struct wme_ac_parameter { +#if __BYTE_ORDER == __LITTLE_ENDIAN + /* byte 1 */ + u8 aifsn:4, + acm:1, + aci:2, + reserved:1; + + /* byte 2 */ + u8 eCWmin:4, + eCWmax:4; +#elif __BYTE_ORDER == __BIG_ENDIAN + /* byte 1 */ + u8 reserved:1, + aci:2, + acm:1, + aifsn:4; + + /* byte 2 */ + u8 eCWmax:4, + eCWmin:4; +#else +#error "Please fix <endian.h>" +#endif + + /* bytes 3 & 4 */ + le16 txopLimit; +} __attribute__ ((packed)); + +struct wme_parameter_element { + /* required fields for WME version 1 */ + u8 oui[3]; + u8 oui_type; + u8 oui_subtype; + u8 version; + u8 acInfo; + u8 reserved; + struct wme_ac_parameter ac[4]; + +} __attribute__ ((packed)); + +struct wme_tspec_info_element { + u8 eid; + u8 length; + u8 oui[3]; + u8 oui_type; + u8 oui_subtype; + u8 version; + u16 ts_info; + u16 nominal_msdu_size; + u16 maximum_msdu_size; + u32 minimum_service_interval; + u32 maximum_service_interval; + u32 inactivity_interval; + u32 start_time; + u32 minimum_data_rate; + u32 mean_data_rate; + u32 maximum_burst_size; + u32 minimum_phy_rate; + u32 peak_data_rate; + u32 delay_bound; + u16 surplus_bandwidth_allowance; + u16 medium_time; +} __attribute__ ((packed)); + + +/* Access Categories */ +enum { + WME_AC_BK = 1, + WME_AC_BE = 0, + WME_AC_VI = 2, + WME_AC_VO = 3 +}; + +struct ieee80211_mgmt; + +u8 * hostapd_eid_wme(struct hostapd_data *hapd, u8 *eid); +int hostapd_eid_wme_valid(struct hostapd_data *hapd, u8 *eid, size_t len); +int hostapd_wme_sta_config(struct hostapd_data *hapd, struct sta_info *sta); +void hostapd_wme_action(struct hostapd_data *hapd, struct ieee80211_mgmt *mgmt, + size_t len); + +#endif /* WME_H */ diff --git a/contrib/wpa/hostapd/wpa.c b/contrib/wpa/hostapd/wpa.c new file mode 100644 index 0000000..cf285b6 --- /dev/null +++ b/contrib/wpa/hostapd/wpa.c @@ -0,0 +1,2447 @@ +/* + * hostapd - IEEE 802.11i-2004 / WPA Authenticator + * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#ifndef CONFIG_NATIVE_WINDOWS + +#include "common.h" +#include "config.h" +#include "eapol_sm.h" +#include "wpa.h" +#include "sha1.h" +#include "sha256.h" +#include "rc4.h" +#include "aes_wrap.h" +#include "crypto.h" +#include "eloop.h" +#include "ieee802_11.h" +#include "pmksa_cache.h" +#include "state_machine.h" +#include "wpa_auth_i.h" +#include "wpa_auth_ie.h" + +#define STATE_MACHINE_DATA struct wpa_state_machine +#define STATE_MACHINE_DEBUG_PREFIX "WPA" +#define STATE_MACHINE_ADDR sm->addr + + +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 wpa_authenticator *wpa_auth, + struct wpa_group *group); +static void wpa_request_new_ptk(struct wpa_state_machine *sm); + +static const u32 dot11RSNAConfigGroupUpdateCount = 4; +static const u32 dot11RSNAConfigPairwiseUpdateCount = 4; +static const u32 eapol_key_timeout_first = 100; /* ms */ +static const u32 eapol_key_timeout_subseq = 1000; /* ms */ + +/* TODO: make these configurable */ +static const int dot11RSNAConfigPMKLifetime = 43200; +static const int dot11RSNAConfigPMKReauthThreshold = 70; +static const int dot11RSNAConfigSATimeout = 60; + + +static inline void wpa_auth_mic_failure_report( + struct wpa_authenticator *wpa_auth, const u8 *addr) +{ + if (wpa_auth->cb.mic_failure_report) + wpa_auth->cb.mic_failure_report(wpa_auth->cb.ctx, addr); +} + + +static inline void wpa_auth_set_eapol(struct wpa_authenticator *wpa_auth, + const u8 *addr, wpa_eapol_variable var, + int value) +{ + if (wpa_auth->cb.set_eapol) + wpa_auth->cb.set_eapol(wpa_auth->cb.ctx, addr, var, value); +} + + +static inline int wpa_auth_get_eapol(struct wpa_authenticator *wpa_auth, + const u8 *addr, wpa_eapol_variable var) +{ + if (wpa_auth->cb.get_eapol == NULL) + return -1; + return wpa_auth->cb.get_eapol(wpa_auth->cb.ctx, addr, var); +} + + +static inline const u8 * wpa_auth_get_psk(struct wpa_authenticator *wpa_auth, + const u8 *addr, const u8 *prev_psk) +{ + if (wpa_auth->cb.get_psk == NULL) + return NULL; + return wpa_auth->cb.get_psk(wpa_auth->cb.ctx, addr, prev_psk); +} + + +static inline int wpa_auth_get_msk(struct wpa_authenticator *wpa_auth, + const u8 *addr, u8 *msk, size_t *len) +{ + if (wpa_auth->cb.get_msk == NULL) + return -1; + return wpa_auth->cb.get_msk(wpa_auth->cb.ctx, addr, msk, len); +} + + +static inline int wpa_auth_set_key(struct wpa_authenticator *wpa_auth, + int vlan_id, + const char *alg, const u8 *addr, int idx, + u8 *key, size_t key_len) +{ + if (wpa_auth->cb.set_key == NULL) + return -1; + return wpa_auth->cb.set_key(wpa_auth->cb.ctx, vlan_id, alg, addr, idx, + key, key_len); +} + + +static inline int wpa_auth_get_seqnum(struct wpa_authenticator *wpa_auth, + const u8 *addr, int idx, u8 *seq) +{ + if (wpa_auth->cb.get_seqnum == NULL) + return -1; + return wpa_auth->cb.get_seqnum(wpa_auth->cb.ctx, addr, idx, seq); +} + + +static inline int wpa_auth_get_seqnum_igtk(struct wpa_authenticator *wpa_auth, + const u8 *addr, int idx, u8 *seq) +{ + if (wpa_auth->cb.get_seqnum_igtk == NULL) + return -1; + return wpa_auth->cb.get_seqnum_igtk(wpa_auth->cb.ctx, addr, idx, seq); +} + + +static inline int +wpa_auth_send_eapol(struct wpa_authenticator *wpa_auth, const u8 *addr, + const u8 *data, size_t data_len, int encrypt) +{ + if (wpa_auth->cb.send_eapol == NULL) + return -1; + return wpa_auth->cb.send_eapol(wpa_auth->cb.ctx, addr, data, data_len, + encrypt); +} + + +int wpa_auth_for_each_sta(struct wpa_authenticator *wpa_auth, + int (*cb)(struct wpa_state_machine *sm, void *ctx), + void *cb_ctx) +{ + if (wpa_auth->cb.for_each_sta == NULL) + return 0; + return wpa_auth->cb.for_each_sta(wpa_auth->cb.ctx, cb, cb_ctx); +} + + +int wpa_auth_for_each_auth(struct wpa_authenticator *wpa_auth, + int (*cb)(struct wpa_authenticator *a, void *ctx), + void *cb_ctx) +{ + if (wpa_auth->cb.for_each_auth == NULL) + return 0; + return wpa_auth->cb.for_each_auth(wpa_auth->cb.ctx, cb, cb_ctx); +} + + +void wpa_auth_logger(struct wpa_authenticator *wpa_auth, const u8 *addr, + logger_level level, const char *txt) +{ + if (wpa_auth->cb.logger == NULL) + return; + wpa_auth->cb.logger(wpa_auth->cb.ctx, addr, level, txt); +} + + +void wpa_auth_vlogger(struct wpa_authenticator *wpa_auth, const u8 *addr, + logger_level level, const char *fmt, ...) +{ + char *format; + int maxlen; + va_list ap; + + if (wpa_auth->cb.logger == NULL) + return; + + maxlen = os_strlen(fmt) + 100; + format = os_malloc(maxlen); + if (!format) + return; + + va_start(ap, fmt); + vsnprintf(format, maxlen, fmt, ap); + va_end(ap); + + wpa_auth_logger(wpa_auth, addr, level, format); + + os_free(format); +} + + +static void wpa_sta_disconnect(struct wpa_authenticator *wpa_auth, + const u8 *addr) +{ + if (wpa_auth->cb.disconnect == NULL) + return; + wpa_auth->cb.disconnect(wpa_auth->cb.ctx, addr, + WLAN_REASON_PREV_AUTH_NOT_VALID); +} + + +static int wpa_use_aes_cmac(struct wpa_state_machine *sm) +{ + int ret = 0; +#ifdef CONFIG_IEEE80211R + if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) + ret = 1; +#endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_IEEE80211W + if (wpa_key_mgmt_sha256(sm->wpa_key_mgmt)) + ret = 1; +#endif /* CONFIG_IEEE80211W */ + return ret; +} + + +static void wpa_rekey_gmk(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_authenticator *wpa_auth = eloop_ctx; + + if (os_get_random(wpa_auth->group->GMK, WPA_GMK_LEN)) { + wpa_printf(MSG_ERROR, "Failed to get random data for WPA " + "initialization."); + } else { + wpa_auth_logger(wpa_auth, NULL, LOGGER_DEBUG, "GMK rekeyd"); + } + + if (wpa_auth->conf.wpa_gmk_rekey) { + eloop_register_timeout(wpa_auth->conf.wpa_gmk_rekey, 0, + wpa_rekey_gmk, wpa_auth, NULL); + } +} + + +static void wpa_rekey_gtk(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_authenticator *wpa_auth = eloop_ctx; + struct wpa_group *group; + + wpa_auth_logger(wpa_auth, NULL, LOGGER_DEBUG, "rekeying GTK"); + for (group = wpa_auth->group; group; group = group->next) { + group->GTKReKey = TRUE; + do { + group->changed = FALSE; + wpa_group_sm_step(wpa_auth, group); + } while (group->changed); + } + + if (wpa_auth->conf.wpa_group_rekey) { + eloop_register_timeout(wpa_auth->conf.wpa_group_rekey, + 0, wpa_rekey_gtk, wpa_auth, NULL); + } +} + + +static void wpa_rekey_ptk(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_authenticator *wpa_auth = eloop_ctx; + struct wpa_state_machine *sm = timeout_ctx; + + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG, "rekeying PTK"); + wpa_request_new_ptk(sm); + wpa_sm_step(sm); +} + + +static int wpa_auth_pmksa_clear_cb(struct wpa_state_machine *sm, void *ctx) +{ + if (sm->pmksa == ctx) + sm->pmksa = NULL; + return 0; +} + + +static void wpa_auth_pmksa_free_cb(struct rsn_pmksa_cache_entry *entry, + void *ctx) +{ + struct wpa_authenticator *wpa_auth = ctx; + wpa_auth_for_each_sta(wpa_auth, wpa_auth_pmksa_clear_cb, entry); +} + + +static struct wpa_group * wpa_group_init(struct wpa_authenticator *wpa_auth, + int vlan_id) +{ + struct wpa_group *group; + u8 buf[ETH_ALEN + 8 + sizeof(group)]; + u8 rkey[32]; + + group = os_zalloc(sizeof(struct wpa_group)); + if (group == NULL) + return NULL; + + group->GTKAuthenticator = TRUE; + group->vlan_id = vlan_id; + + switch (wpa_auth->conf.wpa_group) { + case WPA_CIPHER_CCMP: + group->GTK_len = 16; + break; + case WPA_CIPHER_TKIP: + group->GTK_len = 32; + break; + case WPA_CIPHER_WEP104: + group->GTK_len = 13; + break; + case WPA_CIPHER_WEP40: + group->GTK_len = 5; + break; + } + + /* Counter = PRF-256(Random number, "Init Counter", + * Local MAC Address || Time) + */ + os_memcpy(buf, wpa_auth->addr, ETH_ALEN); + wpa_get_ntp_timestamp(buf + ETH_ALEN); + os_memcpy(buf + ETH_ALEN + 8, &group, sizeof(group)); + if (os_get_random(rkey, sizeof(rkey)) || + os_get_random(group->GMK, WPA_GMK_LEN)) { + wpa_printf(MSG_ERROR, "Failed to get random data for WPA " + "initialization."); + os_free(group); + return NULL; + } + + sha1_prf(rkey, sizeof(rkey), "Init Counter", buf, sizeof(buf), + group->Counter, WPA_NONCE_LEN); + + group->GInit = TRUE; + wpa_group_sm_step(wpa_auth, group); + group->GInit = FALSE; + wpa_group_sm_step(wpa_auth, group); + + return group; +} + + +/** + * wpa_init - Initialize WPA authenticator + * @addr: Authenticator address + * @conf: Configuration for WPA authenticator + * @cb: Callback functions for WPA authenticator + * Returns: Pointer to WPA authenticator data or %NULL on failure + */ +struct wpa_authenticator * wpa_init(const u8 *addr, + struct wpa_auth_config *conf, + struct wpa_auth_callbacks *cb) +{ + struct wpa_authenticator *wpa_auth; + + wpa_auth = os_zalloc(sizeof(struct wpa_authenticator)); + if (wpa_auth == NULL) + return NULL; + os_memcpy(wpa_auth->addr, addr, ETH_ALEN); + os_memcpy(&wpa_auth->conf, conf, sizeof(*conf)); + os_memcpy(&wpa_auth->cb, cb, sizeof(*cb)); + + if (wpa_auth_gen_wpa_ie(wpa_auth)) { + wpa_printf(MSG_ERROR, "Could not generate WPA IE."); + os_free(wpa_auth); + return NULL; + } + + wpa_auth->group = wpa_group_init(wpa_auth, 0); + if (wpa_auth->group == NULL) { + os_free(wpa_auth->wpa_ie); + os_free(wpa_auth); + return NULL; + } + + wpa_auth->pmksa = pmksa_cache_init(wpa_auth_pmksa_free_cb, wpa_auth); + if (wpa_auth->pmksa == NULL) { + wpa_printf(MSG_ERROR, "PMKSA cache initialization failed."); + os_free(wpa_auth->wpa_ie); + os_free(wpa_auth); + return NULL; + } + +#ifdef CONFIG_IEEE80211R + wpa_auth->ft_pmk_cache = wpa_ft_pmk_cache_init(); + if (wpa_auth->ft_pmk_cache == NULL) { + wpa_printf(MSG_ERROR, "FT PMK cache initialization failed."); + os_free(wpa_auth->wpa_ie); + pmksa_cache_deinit(wpa_auth->pmksa); + os_free(wpa_auth); + return NULL; + } +#endif /* CONFIG_IEEE80211R */ + + if (wpa_auth->conf.wpa_gmk_rekey) { + eloop_register_timeout(wpa_auth->conf.wpa_gmk_rekey, 0, + wpa_rekey_gmk, wpa_auth, NULL); + } + + if (wpa_auth->conf.wpa_group_rekey) { + eloop_register_timeout(wpa_auth->conf.wpa_group_rekey, 0, + wpa_rekey_gtk, wpa_auth, NULL); + } + + return wpa_auth; +} + + +/** + * wpa_deinit - Deinitialize WPA authenticator + * @wpa_auth: Pointer to WPA authenticator data from wpa_init() + */ +void wpa_deinit(struct wpa_authenticator *wpa_auth) +{ + struct wpa_group *group, *prev; + + eloop_cancel_timeout(wpa_rekey_gmk, wpa_auth, NULL); + eloop_cancel_timeout(wpa_rekey_gtk, wpa_auth, NULL); + +#ifdef CONFIG_PEERKEY + while (wpa_auth->stsl_negotiations) + wpa_stsl_remove(wpa_auth, wpa_auth->stsl_negotiations); +#endif /* CONFIG_PEERKEY */ + + pmksa_cache_deinit(wpa_auth->pmksa); + +#ifdef CONFIG_IEEE80211R + wpa_ft_pmk_cache_deinit(wpa_auth->ft_pmk_cache); + wpa_auth->ft_pmk_cache = NULL; +#endif /* CONFIG_IEEE80211R */ + + os_free(wpa_auth->wpa_ie); + + group = wpa_auth->group; + while (group) { + prev = group; + group = group->next; + os_free(prev); + } + + os_free(wpa_auth); +} + + +/** + * wpa_reconfig - Update WPA authenticator configuration + * @wpa_auth: Pointer to WPA authenticator data from wpa_init() + * @conf: Configuration for WPA authenticator + */ +int wpa_reconfig(struct wpa_authenticator *wpa_auth, + struct wpa_auth_config *conf) +{ + if (wpa_auth == NULL) + return 0; + + os_memcpy(&wpa_auth->conf, conf, sizeof(*conf)); + if (wpa_auth_gen_wpa_ie(wpa_auth)) { + wpa_printf(MSG_ERROR, "Could not generate WPA IE."); + return -1; + } + + return 0; +} + + +struct wpa_state_machine * +wpa_auth_sta_init(struct wpa_authenticator *wpa_auth, const u8 *addr) +{ + struct wpa_state_machine *sm; + + sm = os_zalloc(sizeof(struct wpa_state_machine)); + if (sm == NULL) + return NULL; + os_memcpy(sm->addr, addr, ETH_ALEN); + + sm->wpa_auth = wpa_auth; + sm->group = wpa_auth->group; + + return sm; +} + + +void wpa_auth_sta_associated(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm) +{ + if (wpa_auth == NULL || !wpa_auth->conf.wpa || sm == NULL) + return; + +#ifdef CONFIG_IEEE80211R + if (sm->ft_completed) { + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG, + "FT authentication already completed - do not " + "start 4-way handshake"); + return; + } +#endif /* CONFIG_IEEE80211R */ + + if (sm->started) { + os_memset(&sm->key_replay, 0, sizeof(sm->key_replay)); + sm->ReAuthenticationRequest = TRUE; + wpa_sm_step(sm); + return; + } + + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG, + "start authentication"); + sm->started = 1; + + sm->Init = TRUE; + wpa_sm_step(sm); + sm->Init = FALSE; + sm->AuthenticationRequest = TRUE; + wpa_sm_step(sm); +} + + +void wpa_auth_sta_no_wpa(struct wpa_state_machine *sm) +{ + /* WPA/RSN was not used - clear WPA state. This is needed if the STA + * reassociates back to the same AP while the previous entry for the + * STA has not yet been removed. */ + if (sm == NULL) + return; + + sm->wpa_key_mgmt = 0; +} + + +static void wpa_free_sta_sm(struct wpa_state_machine *sm) +{ + os_free(sm->last_rx_eapol_key); + os_free(sm->wpa_ie); + os_free(sm); +} + + +void wpa_auth_sta_deinit(struct wpa_state_machine *sm) +{ + if (sm == NULL) + return; + + if (sm->wpa_auth->conf.wpa_strict_rekey && sm->has_GTK) { + wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, + "strict rekeying - force GTK rekey since STA " + "is leaving"); + eloop_cancel_timeout(wpa_rekey_gtk, sm->wpa_auth, NULL); + eloop_register_timeout(0, 500000, wpa_rekey_gtk, sm->wpa_auth, + NULL); + } + + eloop_cancel_timeout(wpa_send_eapol_timeout, sm->wpa_auth, sm); + eloop_cancel_timeout(wpa_sm_call_step, sm, NULL); + eloop_cancel_timeout(wpa_rekey_ptk, sm->wpa_auth, sm); + if (sm->in_step_loop) { + /* Must not free state machine while wpa_sm_step() is running. + * Freeing will be completed in the end of wpa_sm_step(). */ + wpa_printf(MSG_DEBUG, "WPA: Registering pending STA state " + "machine deinit for " MACSTR, MAC2STR(sm->addr)); + sm->pending_deinit = 1; + } else + wpa_free_sta_sm(sm); +} + + +static void wpa_request_new_ptk(struct wpa_state_machine *sm) +{ + if (sm == NULL) + return; + + sm->PTKRequest = TRUE; + sm->PTK_valid = 0; +} + + +static int wpa_replay_counter_valid(struct wpa_state_machine *sm, + const u8 *replay_counter) +{ + int i; + for (i = 0; i < RSNA_MAX_EAPOL_RETRIES; i++) { + if (!sm->key_replay[i].valid) + break; + if (os_memcmp(replay_counter, sm->key_replay[i].counter, + WPA_REPLAY_COUNTER_LEN) == 0) + return 1; + } + return 0; +} + + +void wpa_receive(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm, + u8 *data, size_t data_len) +{ + struct ieee802_1x_hdr *hdr; + struct wpa_eapol_key *key; + u16 key_info, key_data_length; + enum { PAIRWISE_2, PAIRWISE_4, GROUP_2, REQUEST, + SMK_M1, SMK_M3, SMK_ERROR } msg; + char *msgtxt; + struct wpa_eapol_ie_parse kde; + + if (wpa_auth == NULL || !wpa_auth->conf.wpa || 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 = WPA_GET_BE16(key->key_info); + key_data_length = WPA_GET_BE16(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_SMK_MESSAGE | WPA_KEY_INFO_REQUEST)) == + (WPA_KEY_INFO_SMK_MESSAGE | WPA_KEY_INFO_REQUEST)) { + if (key_info & WPA_KEY_INFO_ERROR) { + msg = SMK_ERROR; + msgtxt = "SMK Error"; + } else { + msg = SMK_M1; + msgtxt = "SMK M1"; + } + } else if (key_info & WPA_KEY_INFO_SMK_MESSAGE) { + msg = SMK_M3; + msgtxt = "SMK M3"; + } else 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"; + } + + /* TODO: key_info type validation for PeerKey */ + if (msg == REQUEST || msg == PAIRWISE_2 || msg == PAIRWISE_4 || + msg == GROUP_2) { + u16 ver = key_info & WPA_KEY_INFO_TYPE_MASK; + if (sm->pairwise == WPA_CIPHER_CCMP) { + if (wpa_use_aes_cmac(sm) && + ver != WPA_KEY_INFO_TYPE_AES_128_CMAC) { + wpa_auth_logger(wpa_auth, sm->addr, + LOGGER_WARNING, + "advertised support for " + "AES-128-CMAC, but did not " + "use it"); + return; + } + + if (!wpa_use_aes_cmac(sm) && + ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) { + wpa_auth_logger(wpa_auth, sm->addr, + LOGGER_WARNING, + "did not use HMAC-SHA1-AES " + "with CCMP"); + return; + } + } + } + + if (key_info & WPA_KEY_INFO_REQUEST) { + if (sm->req_replay_counter_used && + os_memcmp(key->replay_counter, sm->req_replay_counter, + WPA_REPLAY_COUNTER_LEN) <= 0) { + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_WARNING, + "received EAPOL-Key request with " + "replayed counter"); + return; + } + } + + if (!(key_info & WPA_KEY_INFO_REQUEST) && + !wpa_replay_counter_valid(sm, key->replay_counter)) { + int i; + wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO, + "received EAPOL-Key %s with unexpected " + "replay counter", msgtxt); + for (i = 0; i < RSNA_MAX_EAPOL_RETRIES; i++) { + if (!sm->key_replay[i].valid) + break; + wpa_hexdump(MSG_DEBUG, "pending replay counter", + sm->key_replay[i].counter, + WPA_REPLAY_COUNTER_LEN); + } + wpa_hexdump(MSG_DEBUG, "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) { + wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO, + "received EAPOL-Key msg 2/4 in " + "invalid state (%d) - dropped", + sm->wpa_ptk_state); + return; + } + if (sm->wpa_ie == NULL || + sm->wpa_ie_len != key_data_length || + os_memcmp(sm->wpa_ie, key + 1, key_data_length) != 0) { + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, + "WPA IE from (Re)AssocReq did not " + "match with msg 2/4"); + if (sm->wpa_ie) { + wpa_hexdump(MSG_DEBUG, "WPA IE in AssocReq", + sm->wpa_ie, sm->wpa_ie_len); + } + wpa_hexdump(MSG_DEBUG, "WPA IE in msg 2/4", + (u8 *) (key + 1), key_data_length); + /* MLME-DEAUTHENTICATE.request */ + wpa_sta_disconnect(wpa_auth, sm->addr); + return; + } + break; + case PAIRWISE_4: + if (sm->wpa_ptk_state != WPA_PTK_PTKINITNEGOTIATING || + !sm->PTK_valid) { + wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_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) { + wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO, + "received EAPOL-Key msg 2/2 in " + "invalid state (%d) - dropped", + sm->wpa_ptk_group_state); + return; + } + break; +#ifdef CONFIG_PEERKEY + case SMK_M1: + case SMK_M3: + case SMK_ERROR: + if (!wpa_auth->conf.peerkey) { + wpa_printf(MSG_DEBUG, "RSN: SMK M1/M3/Error, but " + "PeerKey use disabled - ignoring message"); + return; + } + if (!sm->PTK_valid) { + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, + "received EAPOL-Key msg SMK in " + "invalid state - dropped"); + return; + } + break; +#else /* CONFIG_PEERKEY */ + case SMK_M1: + case SMK_M3: + case SMK_ERROR: + return; /* STSL disabled - ignore SMK messages */ +#endif /* CONFIG_PEERKEY */ + case REQUEST: + break; + } + + wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG, + "received EAPOL-Key frame (%s)", msgtxt); + + if (key_info & WPA_KEY_INFO_ACK) { + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, + "received invalid EAPOL-Key: Key Ack set"); + return; + } + + if (!(key_info & WPA_KEY_INFO_MIC)) { + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_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)) { + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, + "received EAPOL-Key with invalid MIC"); + return; + } + sm->MICVerified = TRUE; + eloop_cancel_timeout(wpa_send_eapol_timeout, wpa_auth, sm); + } + + if (key_info & WPA_KEY_INFO_REQUEST) { + if (sm->MICVerified) { + sm->req_replay_counter_used = 1; + os_memcpy(sm->req_replay_counter, key->replay_counter, + WPA_REPLAY_COUNTER_LEN); + } else { + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, + "received EAPOL-Key request with " + "invalid MIC"); + return; + } + + /* + * TODO: should decrypt key data field if encryption was used; + * even though MAC address KDE is not normally encrypted, + * supplicant is allowed to encrypt it. + */ + if (msg == SMK_ERROR) { +#ifdef CONFIG_PEERKEY + wpa_smk_error(wpa_auth, sm, key); +#endif /* CONFIG_PEERKEY */ + return; + } else if (key_info & WPA_KEY_INFO_ERROR) { + /* Supplicant reported a Michael MIC error */ + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, + "received EAPOL-Key Error Request " + "(STA detected Michael MIC failure)"); + wpa_auth_mic_failure_report(wpa_auth, sm->addr); + sm->dot11RSNAStatsTKIPRemoteMICFailures++; + 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(sm); + } else if (key_info & WPA_KEY_INFO_KEY_TYPE) { + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, + "received EAPOL-Key Request for new " + "4-Way Handshake"); + wpa_request_new_ptk(sm); +#ifdef CONFIG_PEERKEY + } else if (msg == SMK_M1) { + wpa_smk_m1(wpa_auth, sm, key); +#endif /* CONFIG_PEERKEY */ + } else if (key_data_length > 0 && + wpa_parse_kde_ies((const u8 *) (key + 1), + key_data_length, &kde) == 0 && + kde.mac_addr) { + } else { + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, + "received EAPOL-Key Request for GTK " + "rekeying"); + /* FIX: why was this triggering PTK rekeying for the + * STA that requested Group Key rekeying?? */ + /* wpa_request_new_ptk(sta->wpa_sm); */ + eloop_cancel_timeout(wpa_rekey_gtk, wpa_auth, NULL); + wpa_rekey_gtk(wpa_auth, NULL); + } + } else { + /* Do not allow the same key replay counter to be reused. This + * does also invalidate all other pending replay counters if + * retransmissions were used, i.e., we will only process one of + * the pending replies and ignore rest if more than one is + * received. */ + sm->key_replay[0].valid = FALSE; + } + +#ifdef CONFIG_PEERKEY + if (msg == SMK_M3) { + wpa_smk_m3(wpa_auth, sm, key); + return; + } +#endif /* CONFIG_PEERKEY */ + + os_free(sm->last_rx_eapol_key); + sm->last_rx_eapol_key = os_malloc(data_len); + if (sm->last_rx_eapol_key == NULL) + return; + os_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); + os_memcpy(sm->SNonce, key->key_nonce, WPA_NONCE_LEN); + wpa_sm_step(sm); +} + + +static void wpa_gmk_to_gtk(const u8 *gmk, const u8 *addr, const u8 *gnonce, + u8 *gtk, size_t gtk_len) +{ + u8 data[ETH_ALEN + WPA_NONCE_LEN]; + + /* GTK = PRF-X(GMK, "Group key expansion", AA || GNonce) */ + os_memcpy(data, addr, ETH_ALEN); + os_memcpy(data + ETH_ALEN, gnonce, WPA_NONCE_LEN); + +#ifdef CONFIG_IEEE80211W + sha256_prf(gmk, WPA_GMK_LEN, "Group key expansion", + data, sizeof(data), gtk, gtk_len); +#else /* CONFIG_IEEE80211W */ + sha1_prf(gmk, WPA_GMK_LEN, "Group key expansion", + data, sizeof(data), gtk, gtk_len); +#endif /* CONFIG_IEEE80211W */ + + wpa_hexdump_key(MSG_DEBUG, "GMK", gmk, WPA_GMK_LEN); + wpa_hexdump_key(MSG_DEBUG, "GTK", gtk, gtk_len); +} + + +static void wpa_send_eapol_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_authenticator *wpa_auth = eloop_ctx; + struct wpa_state_machine *sm = timeout_ctx; + + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG, "EAPOL-Key timeout"); + sm->TimeoutEvt = TRUE; + wpa_sm_step(sm); +} + + +void __wpa_send_eapol(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm, int key_info, + const u8 *key_rsc, const u8 *nonce, + const u8 *kde, size_t kde_len, + int keyidx, int encr, int force_version) +{ + struct ieee802_1x_hdr *hdr; + struct wpa_eapol_key *key; + size_t len; + int alg; + int key_data_len, pad_len = 0; + u8 *buf, *pos; + int version, pairwise; + int i; + + len = sizeof(struct ieee802_1x_hdr) + sizeof(struct wpa_eapol_key); + + if (force_version) + version = force_version; + else if (wpa_use_aes_cmac(sm)) + version = WPA_KEY_INFO_TYPE_AES_128_CMAC; + else if (sm->pairwise == WPA_CIPHER_CCMP) + version = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES; + else + version = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4; + + pairwise = key_info & WPA_KEY_INFO_KEY_TYPE; + + wpa_printf(MSG_DEBUG, "WPA: Send EAPOL(version=%d secure=%d mic=%d " + "ack=%d install=%d pairwise=%d kde_len=%lu keyidx=%d " + "encr=%d)", + version, + (key_info & WPA_KEY_INFO_SECURE) ? 1 : 0, + (key_info & WPA_KEY_INFO_MIC) ? 1 : 0, + (key_info & WPA_KEY_INFO_ACK) ? 1 : 0, + (key_info & WPA_KEY_INFO_INSTALL) ? 1 : 0, + pairwise, (unsigned long) kde_len, keyidx, encr); + + key_data_len = kde_len; + + if ((version == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES || + version == WPA_KEY_INFO_TYPE_AES_128_CMAC) && encr) { + pad_len = key_data_len % 8; + if (pad_len) + pad_len = 8 - pad_len; + key_data_len += pad_len + 8; + } + + len += key_data_len; + + hdr = os_zalloc(len); + if (hdr == NULL) + return; + hdr->version = wpa_auth->conf.eapol_version; + hdr->type = IEEE802_1X_TYPE_EAPOL_KEY; + hdr->length = host_to_be16(len - sizeof(*hdr)); + key = (struct wpa_eapol_key *) (hdr + 1); + + key->type = sm->wpa == WPA_VERSION_WPA2 ? + EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA; + key_info |= version; + if (encr && sm->wpa == WPA_VERSION_WPA2) + key_info |= WPA_KEY_INFO_ENCR_KEY_DATA; + if (sm->wpa != WPA_VERSION_WPA2) + key_info |= keyidx << WPA_KEY_INFO_KEY_INDEX_SHIFT; + WPA_PUT_BE16(key->key_info, key_info); + + alg = pairwise ? sm->pairwise : wpa_auth->conf.wpa_group; + switch (alg) { + case WPA_CIPHER_CCMP: + WPA_PUT_BE16(key->key_length, 16); + break; + case WPA_CIPHER_TKIP: + WPA_PUT_BE16(key->key_length, 32); + break; + case WPA_CIPHER_WEP40: + WPA_PUT_BE16(key->key_length, 5); + break; + case WPA_CIPHER_WEP104: + WPA_PUT_BE16(key->key_length, 13); + break; + } + if (key_info & WPA_KEY_INFO_SMK_MESSAGE) + WPA_PUT_BE16(key->key_length, 0); + + /* FIX: STSL: what to use as key_replay_counter? */ + for (i = RSNA_MAX_EAPOL_RETRIES - 1; i > 0; i--) { + sm->key_replay[i].valid = sm->key_replay[i - 1].valid; + os_memcpy(sm->key_replay[i].counter, + sm->key_replay[i - 1].counter, + WPA_REPLAY_COUNTER_LEN); + } + inc_byte_array(sm->key_replay[0].counter, WPA_REPLAY_COUNTER_LEN); + os_memcpy(key->replay_counter, sm->key_replay[0].counter, + WPA_REPLAY_COUNTER_LEN); + sm->key_replay[0].valid = TRUE; + + if (nonce) + os_memcpy(key->key_nonce, nonce, WPA_NONCE_LEN); + + if (key_rsc) + os_memcpy(key->key_rsc, key_rsc, WPA_KEY_RSC_LEN); + + if (kde && !encr) { + os_memcpy(key + 1, kde, kde_len); + WPA_PUT_BE16(key->key_data_length, kde_len); + } else if (encr && kde) { + buf = os_zalloc(key_data_len); + if (buf == NULL) { + os_free(hdr); + return; + } + pos = buf; + os_memcpy(pos, kde, kde_len); + pos += kde_len; + + if (pad_len) + *pos++ = 0xdd; + + wpa_hexdump_key(MSG_DEBUG, "Plaintext EAPOL-Key Key Data", + buf, key_data_len); + if (version == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES || + version == WPA_KEY_INFO_TYPE_AES_128_CMAC) { + if (aes_wrap(sm->PTK.kek, (key_data_len - 8) / 8, buf, + (u8 *) (key + 1))) { + os_free(hdr); + os_free(buf); + return; + } + WPA_PUT_BE16(key->key_data_length, key_data_len); + } else { + u8 ek[32]; + os_memcpy(key->key_iv, + sm->group->Counter + WPA_NONCE_LEN - 16, 16); + inc_byte_array(sm->group->Counter, WPA_NONCE_LEN); + os_memcpy(ek, key->key_iv, 16); + os_memcpy(ek + 16, sm->PTK.kek, 16); + os_memcpy(key + 1, buf, key_data_len); + rc4_skip(ek, 32, 256, (u8 *) (key + 1), key_data_len); + WPA_PUT_BE16(key->key_data_length, key_data_len); + } + os_free(buf); + } + + if (key_info & WPA_KEY_INFO_MIC) { + if (!sm->PTK_valid) { + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG, + "PTK not valid when sending EAPOL-Key " + "frame"); + os_free(hdr); + return; + } + wpa_eapol_key_mic(sm->PTK.kck, version, (u8 *) hdr, len, + key->key_mic); + } + + wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_inc_EapolFramesTx, + 1); + wpa_auth_send_eapol(wpa_auth, sm->addr, (u8 *) hdr, len, + sm->pairwise_set); + os_free(hdr); +} + + +static void wpa_send_eapol(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm, int key_info, + const u8 *key_rsc, const u8 *nonce, + const u8 *kde, size_t kde_len, + int keyidx, int encr) +{ + int timeout_ms; + int pairwise = key_info & WPA_KEY_INFO_KEY_TYPE; + int ctr; + + if (sm == NULL) + return; + + __wpa_send_eapol(wpa_auth, sm, key_info, key_rsc, nonce, kde, kde_len, + keyidx, encr, 0); + + ctr = pairwise ? sm->TimeoutCtr : sm->GTimeoutCtr; + if (ctr == 1) + timeout_ms = eapol_key_timeout_first; + else + timeout_ms = eapol_key_timeout_subseq; + eloop_register_timeout(timeout_ms / 1000, (timeout_ms % 1000) * 1000, + wpa_send_eapol_timeout, wpa_auth, sm); +} + + +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 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 = WPA_GET_BE16(key->key_info); + os_memcpy(mic, key->key_mic, 16); + os_memset(key->key_mic, 0, 16); + if (wpa_eapol_key_mic(PTK->kck, key_info & WPA_KEY_INFO_TYPE_MASK, + data, data_len, key->key_mic) || + os_memcmp(mic, key->key_mic, 16) != 0) + ret = -1; + os_memcpy(key->key_mic, mic, 16); + return ret; +} + + +void wpa_remove_ptk(struct wpa_state_machine *sm) +{ + sm->PTK_valid = FALSE; + os_memset(&sm->PTK, 0, sizeof(sm->PTK)); + wpa_auth_set_key(sm->wpa_auth, 0, "none", sm->addr, 0, (u8 *) "", 0); + sm->pairwise_set = FALSE; + eloop_cancel_timeout(wpa_rekey_ptk, sm->wpa_auth, sm); +} + + +void wpa_auth_sm_event(struct wpa_state_machine *sm, wpa_event event) +{ + int remove_ptk = 1; + + if (sm == NULL) + return; + + wpa_auth_vlogger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, + "event %d notification", event); + + 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: + if (sm->GUpdateStationKeys) { + /* + * Reauthentication cancels the pending group key + * update for this STA. + */ + sm->group->GKeyDoneStations--; + sm->GUpdateStationKeys = FALSE; + sm->PtkGroupInit = TRUE; + } + sm->ReAuthenticationRequest = TRUE; + break; + case WPA_ASSOC_FT: +#ifdef CONFIG_IEEE80211R + /* Using FT protocol, not WPA auth state machine */ + sm->ft_completed = 1; + return; +#else /* CONFIG_IEEE80211R */ + break; +#endif /* CONFIG_IEEE80211R */ + } + +#ifdef CONFIG_IEEE80211R + sm->ft_completed = 0; +#endif /* CONFIG_IEEE80211R */ + +#ifdef CONFIG_IEEE80211W + if (sm->mgmt_frame_prot && event == WPA_AUTH) + remove_ptk = 0; +#endif /* CONFIG_IEEE80211W */ + + if (remove_ptk) { + sm->PTK_valid = FALSE; + os_memset(&sm->PTK, 0, sizeof(sm->PTK)); + + if (event != WPA_REAUTH_EAPOL) + wpa_remove_ptk(sm); + } + + 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 ""; + } +} + + +SM_STATE(WPA_PTK, INITIALIZE) +{ + SM_ENTRY_MA(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) + sm->group->GKeyDoneStations--; + sm->GUpdateStationKeys = FALSE; + if (sm->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; + } + wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_portEnabled, 0); + wpa_remove_ptk(sm); + wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_portValid, 0); + sm->TimeoutCtr = 0; + if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) { + wpa_auth_set_eapol(sm->wpa_auth, sm->addr, + WPA_EAPOL_authorized, 0); + } +} + + +SM_STATE(WPA_PTK, DISCONNECT) +{ + SM_ENTRY_MA(WPA_PTK, DISCONNECT, wpa_ptk); + sm->Disconnect = FALSE; + wpa_sta_disconnect(sm->wpa_auth, sm->addr); +} + + +SM_STATE(WPA_PTK, DISCONNECTED) +{ + SM_ENTRY_MA(WPA_PTK, DISCONNECTED, wpa_ptk); + sm->DeauthenticationRequest = FALSE; +} + + +SM_STATE(WPA_PTK, AUTHENTICATION) +{ + SM_ENTRY_MA(WPA_PTK, AUTHENTICATION, wpa_ptk); + os_memset(&sm->PTK, 0, sizeof(sm->PTK)); + sm->PTK_valid = FALSE; + wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_portControl_Auto, + 1); + wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_portEnabled, 1); + sm->AuthenticationRequest = FALSE; +} + + +SM_STATE(WPA_PTK, AUTHENTICATION2) +{ + SM_ENTRY_MA(WPA_PTK, AUTHENTICATION2, wpa_ptk); + os_memcpy(sm->ANonce, sm->group->Counter, WPA_NONCE_LEN); + inc_byte_array(sm->group->Counter, WPA_NONCE_LEN); + sm->ReAuthenticationRequest = FALSE; + /* IEEE 802.11i 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 msk[2 * PMK_LEN]; + size_t len = 2 * PMK_LEN; + + SM_ENTRY_MA(WPA_PTK, INITPMK, wpa_ptk); +#ifdef CONFIG_IEEE80211R + sm->xxkey_len = 0; +#endif /* CONFIG_IEEE80211R */ + if (sm->pmksa) { + wpa_printf(MSG_DEBUG, "WPA: PMK from PMKSA cache"); + os_memcpy(sm->PMK, sm->pmksa->pmk, PMK_LEN); + } else if (wpa_auth_get_msk(sm->wpa_auth, sm->addr, msk, &len) == 0) { + wpa_printf(MSG_DEBUG, "WPA: PMK from EAPOL state machine " + "(len=%lu)", (unsigned long) len); + os_memcpy(sm->PMK, msk, PMK_LEN); +#ifdef CONFIG_IEEE80211R + if (len >= 2 * PMK_LEN) { + os_memcpy(sm->xxkey, msk + PMK_LEN, PMK_LEN); + sm->xxkey_len = PMK_LEN; + } +#endif /* CONFIG_IEEE80211R */ + } else { + wpa_printf(MSG_DEBUG, "WPA: Could not get PMK"); + } + + sm->req_replay_counter_used = 0; + /* IEEE 802.11i 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. */ + wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_keyRun, 0); +} + + +SM_STATE(WPA_PTK, INITPSK) +{ + const u8 *psk; + SM_ENTRY_MA(WPA_PTK, INITPSK, wpa_ptk); + psk = wpa_auth_get_psk(sm->wpa_auth, sm->addr, NULL); + if (psk) { + os_memcpy(sm->PMK, psk, PMK_LEN); +#ifdef CONFIG_IEEE80211R + os_memcpy(sm->xxkey, psk, PMK_LEN); + sm->xxkey_len = PMK_LEN; +#endif /* CONFIG_IEEE80211R */ + } + sm->req_replay_counter_used = 0; +} + + +SM_STATE(WPA_PTK, PTKSTART) +{ + u8 buf[2 + RSN_SELECTOR_LEN + PMKID_LEN], *pmkid = NULL; + size_t pmkid_len = 0; + + SM_ENTRY_MA(WPA_PTK, PTKSTART, wpa_ptk); + sm->PTKRequest = FALSE; + sm->TimeoutEvt = FALSE; + + sm->TimeoutCtr++; + if (sm->TimeoutCtr > (int) dot11RSNAConfigPairwiseUpdateCount) { + /* No point in sending the EAPOL-Key - we will disconnect + * immediately following this. */ + return; + } + + wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, + "sending 1/4 msg of 4-Way Handshake"); + /* + * TODO: Could add PMKID even with WPA2-PSK, but only if there is only + * one possible PSK for this STA. + */ + if (sm->wpa == WPA_VERSION_WPA2 && + wpa_key_mgmt_wpa_ieee8021x(sm->wpa_key_mgmt)) { + pmkid = buf; + pmkid_len = 2 + RSN_SELECTOR_LEN + PMKID_LEN; + pmkid[0] = WLAN_EID_VENDOR_SPECIFIC; + pmkid[1] = RSN_SELECTOR_LEN + PMKID_LEN; + RSN_SELECTOR_PUT(&pmkid[2], RSN_KEY_DATA_PMKID); + if (sm->pmksa) + os_memcpy(&pmkid[2 + RSN_SELECTOR_LEN], + sm->pmksa->pmkid, PMKID_LEN); + else { + /* + * Calculate PMKID since no PMKSA cache entry was + * available with pre-calculated PMKID. + */ + rsn_pmkid(sm->PMK, PMK_LEN, sm->wpa_auth->addr, + sm->addr, &pmkid[2 + RSN_SELECTOR_LEN], + wpa_key_mgmt_sha256(sm->wpa_key_mgmt)); + } + } + wpa_send_eapol(sm->wpa_auth, sm, + WPA_KEY_INFO_ACK | WPA_KEY_INFO_KEY_TYPE, NULL, + sm->ANonce, pmkid, pmkid_len, 0, 0); +} + + +static int wpa_derive_ptk(struct wpa_state_machine *sm, const u8 *pmk, + struct wpa_ptk *ptk) +{ +#ifdef CONFIG_IEEE80211R + if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) + return wpa_auth_derive_ptk_ft(sm, pmk, ptk); +#endif /* CONFIG_IEEE80211R */ + + wpa_pmk_to_ptk(pmk, PMK_LEN, "Pairwise key expansion", + sm->wpa_auth->addr, sm->addr, sm->ANonce, sm->SNonce, + (u8 *) ptk, sizeof(*ptk), + wpa_key_mgmt_sha256(sm->wpa_key_mgmt)); + + return 0; +} + + +SM_STATE(WPA_PTK, PTKCALCNEGOTIATING) +{ + struct wpa_ptk PTK; + int ok = 0; + const u8 *pmk = NULL; + + SM_ENTRY_MA(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 (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) { + pmk = wpa_auth_get_psk(sm->wpa_auth, sm->addr, pmk); + if (pmk == NULL) + break; + } else + pmk = sm->PMK; + + wpa_derive_ptk(sm, pmk, &PTK); + + if (wpa_verify_key_mic(&PTK, sm->last_rx_eapol_key, + sm->last_rx_eapol_key_len) == 0) { + ok = 1; + break; + } + + if (!wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) + break; + } + + if (!ok) { + wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, + "invalid MIC in msg 2/4 of 4-Way Handshake"); + return; + } + + eloop_cancel_timeout(wpa_send_eapol_timeout, sm->wpa_auth, sm); + + if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) { + /* PSK may have changed from the previous choice, so update + * state machine data based on whatever PSK was selected here. + */ + os_memcpy(sm->PMK, pmk, PMK_LEN); + } + + sm->MICVerified = TRUE; + + os_memcpy(&sm->PTK, &PTK, sizeof(PTK)); + sm->PTK_valid = TRUE; +} + + +SM_STATE(WPA_PTK, PTKCALCNEGOTIATING2) +{ + SM_ENTRY_MA(WPA_PTK, PTKCALCNEGOTIATING2, wpa_ptk); + sm->TimeoutCtr = 0; +} + + +#ifdef CONFIG_IEEE80211W + +static int ieee80211w_kde_len(struct wpa_state_machine *sm) +{ + if (sm->mgmt_frame_prot) { + return 2 + RSN_SELECTOR_LEN + sizeof(struct wpa_igtk_kde); + } + + return 0; +} + + +static u8 * ieee80211w_kde_add(struct wpa_state_machine *sm, u8 *pos) +{ + struct wpa_igtk_kde igtk; + struct wpa_group *gsm = sm->group; + + if (!sm->mgmt_frame_prot) + return pos; + + igtk.keyid[0] = gsm->GN_igtk; + igtk.keyid[1] = 0; + if (wpa_auth_get_seqnum_igtk(sm->wpa_auth, NULL, gsm->GN_igtk, igtk.pn) + < 0) + os_memset(igtk.pn, 0, sizeof(igtk.pn)); + os_memcpy(igtk.igtk, gsm->IGTK[gsm->GN_igtk - 4], WPA_IGTK_LEN); + pos = wpa_add_kde(pos, RSN_KEY_DATA_IGTK, + (const u8 *) &igtk, sizeof(igtk), NULL, 0); + + return pos; +} + +#else /* CONFIG_IEEE80211W */ + +static int ieee80211w_kde_len(struct wpa_state_machine *sm) +{ + return 0; +} + + +static u8 * ieee80211w_kde_add(struct wpa_state_machine *sm, u8 *pos) +{ + return pos; +} + +#endif /* CONFIG_IEEE80211W */ + + +SM_STATE(WPA_PTK, PTKINITNEGOTIATING) +{ + u8 rsc[WPA_KEY_RSC_LEN], *_rsc, *gtk, *kde, *pos; + size_t gtk_len, kde_len; + struct wpa_group *gsm = sm->group; + u8 *wpa_ie; + int wpa_ie_len, secure, keyidx, encr = 0; + + SM_ENTRY_MA(WPA_PTK, PTKINITNEGOTIATING, wpa_ptk); + sm->TimeoutEvt = FALSE; + + sm->TimeoutCtr++; + if (sm->TimeoutCtr > (int) dot11RSNAConfigPairwiseUpdateCount) { + /* No point in sending the EAPOL-Key - we will disconnect + * immediately following this. */ + return; + } + + /* Send EAPOL(1, 1, 1, Pair, P, RSC, ANonce, MIC(PTK), RSNIE, GTK[GN]) + */ + os_memset(rsc, 0, WPA_KEY_RSC_LEN); + wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN, rsc); + wpa_ie = sm->wpa_auth->wpa_ie; + wpa_ie_len = sm->wpa_auth->wpa_ie_len; + if (sm->wpa == WPA_VERSION_WPA && + (sm->wpa_auth->conf.wpa & WPA_PROTO_RSN) && + 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; + } + wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, + "sending 3/4 msg of 4-Way Handshake"); + if (sm->wpa == WPA_VERSION_WPA2) { + /* WPA2 send GTK in the 4-way handshake */ + secure = 1; + gtk = gsm->GTK[gsm->GN - 1]; + gtk_len = gsm->GTK_len; + keyidx = gsm->GN; + _rsc = rsc; + encr = 1; + } else { + /* WPA does not include GTK in msg 3/4 */ + secure = 0; + gtk = NULL; + gtk_len = 0; + keyidx = 0; + _rsc = NULL; + } + + kde_len = wpa_ie_len + ieee80211w_kde_len(sm); + if (gtk) + kde_len += 2 + RSN_SELECTOR_LEN + 2 + gtk_len; + kde = os_malloc(kde_len); + if (kde == NULL) + return; + + pos = kde; + os_memcpy(pos, wpa_ie, wpa_ie_len); + pos += wpa_ie_len; + if (gtk) { + u8 hdr[2]; + hdr[0] = keyidx & 0x03; + hdr[1] = 0; + pos = wpa_add_kde(pos, RSN_KEY_DATA_GROUPKEY, hdr, 2, + gtk, gtk_len); + } + pos = ieee80211w_kde_add(sm, pos); + + wpa_send_eapol(sm->wpa_auth, sm, + (secure ? WPA_KEY_INFO_SECURE : 0) | WPA_KEY_INFO_MIC | + WPA_KEY_INFO_ACK | WPA_KEY_INFO_INSTALL | + WPA_KEY_INFO_KEY_TYPE, + _rsc, sm->ANonce, kde, pos - kde, keyidx, encr); + os_free(kde); +} + + +SM_STATE(WPA_PTK, PTKINITDONE) +{ + SM_ENTRY_MA(WPA_PTK, PTKINITDONE, wpa_ptk); + sm->EAPOLKeyReceived = FALSE; + if (sm->Pair) { + char *alg; + int klen; + if (sm->pairwise == WPA_CIPHER_TKIP) { + alg = "TKIP"; + klen = 32; + } else { + alg = "CCMP"; + klen = 16; + } + if (wpa_auth_set_key(sm->wpa_auth, 0, alg, sm->addr, 0, + sm->PTK.tk1, klen)) { + wpa_sta_disconnect(sm->wpa_auth, sm->addr); + return; + } + /* FIX: MLME-SetProtection.Request(TA, Tx_Rx) */ + sm->pairwise_set = TRUE; + + if (sm->wpa_auth->conf.wpa_ptk_rekey) { + eloop_cancel_timeout(wpa_rekey_ptk, sm->wpa_auth, sm); + eloop_register_timeout(sm->wpa_auth->conf. + wpa_ptk_rekey, 0, wpa_rekey_ptk, + sm->wpa_auth, sm); + } + + if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) { + wpa_auth_set_eapol(sm->wpa_auth, sm->addr, + WPA_EAPOL_authorized, 1); + } + } + + if (0 /* IBSS == TRUE */) { + sm->keycount++; + if (sm->keycount == 2) { + wpa_auth_set_eapol(sm->wpa_auth, sm->addr, + WPA_EAPOL_portValid, 1); + } + } else { + wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_portValid, + 1); + } + wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_keyAvailable, 0); + wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_keyDone, 1); + if (sm->wpa == WPA_VERSION_WPA) + sm->PInitAKeys = TRUE; + else + sm->has_GTK = TRUE; + wpa_auth_vlogger(sm->wpa_auth, sm->addr, LOGGER_INFO, + "pairwise key handshake completed (%s)", + sm->wpa == WPA_VERSION_WPA ? "WPA" : "RSN"); + +#ifdef CONFIG_IEEE80211R + wpa_ft_push_pmk_r1(sm->wpa_auth, sm->addr); +#endif /* CONFIG_IEEE80211R */ +} + + +SM_STEP(WPA_PTK) +{ + struct wpa_authenticator *wpa_auth = sm->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 (wpa_key_mgmt_wpa_ieee8021x(sm->wpa_key_mgmt) && + wpa_auth_get_eapol(sm->wpa_auth, sm->addr, + WPA_EAPOL_keyRun) > 0) + SM_ENTER(WPA_PTK, INITPMK); + else if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) + /* FIX: && 802.1X::keyRun */) + SM_ENTER(WPA_PTK, INITPSK); + break; + case WPA_PTK_INITPMK: + if (wpa_auth_get_eapol(sm->wpa_auth, sm->addr, + WPA_EAPOL_keyAvailable) > 0) + SM_ENTER(WPA_PTK, PTKSTART); + else { + wpa_auth->dot11RSNA4WayHandshakeFailures++; + SM_ENTER(WPA_PTK, DISCONNECT); + } + break; + case WPA_PTK_INITPSK: + if (wpa_auth_get_psk(sm->wpa_auth, sm->addr, NULL)) + SM_ENTER(WPA_PTK, PTKSTART); + else { + wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_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 > + (int) 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 > + (int) 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_MA(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_group *gsm = sm->group; + u8 *kde, *pos, hdr[2]; + size_t kde_len; + + SM_ENTRY_MA(WPA_PTK_GROUP, REKEYNEGOTIATING, wpa_ptk_group); + + sm->GTimeoutCtr++; + if (sm->GTimeoutCtr > (int) dot11RSNAConfigGroupUpdateCount) { + /* No point in sending the EAPOL-Key - we will disconnect + * immediately following this. */ + return; + } + + if (sm->wpa == WPA_VERSION_WPA) + sm->PInitAKeys = FALSE; + sm->TimeoutEvt = FALSE; + /* Send EAPOL(1, 1, 1, !Pair, G, RSC, GNonce, MIC(PTK), GTK[GN]) */ + os_memset(rsc, 0, WPA_KEY_RSC_LEN); + if (gsm->wpa_group_state == WPA_GROUP_SETKEYSDONE) + wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN, rsc); + wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, + "sending 1/2 msg of Group Key Handshake"); + + if (sm->wpa == WPA_VERSION_WPA2) { + kde_len = 2 + RSN_SELECTOR_LEN + 2 + gsm->GTK_len + + ieee80211w_kde_len(sm); + kde = os_malloc(kde_len); + if (kde == NULL) + return; + + pos = kde; + hdr[0] = gsm->GN & 0x03; + hdr[1] = 0; + pos = wpa_add_kde(pos, RSN_KEY_DATA_GROUPKEY, hdr, 2, + gsm->GTK[gsm->GN - 1], gsm->GTK_len); + pos = ieee80211w_kde_add(sm, pos); + } else { + kde = gsm->GTK[gsm->GN - 1]; + pos = kde + gsm->GTK_len; + } + + wpa_send_eapol(sm->wpa_auth, sm, + WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC | + WPA_KEY_INFO_ACK | + (!sm->Pair ? WPA_KEY_INFO_INSTALL : 0), + rsc, gsm->GNonce, kde, pos - kde, gsm->GN, 1); + if (sm->wpa == WPA_VERSION_WPA2) + os_free(kde); +} + + +SM_STATE(WPA_PTK_GROUP, REKEYESTABLISHED) +{ + SM_ENTRY_MA(WPA_PTK_GROUP, REKEYESTABLISHED, wpa_ptk_group); + sm->EAPOLKeyReceived = FALSE; + if (sm->GUpdateStationKeys) + sm->group->GKeyDoneStations--; + sm->GUpdateStationKeys = FALSE; + sm->GTimeoutCtr = 0; + /* FIX: MLME.SetProtection.Request(TA, Tx_Rx) */ + wpa_auth_vlogger(sm->wpa_auth, sm->addr, LOGGER_INFO, + "group key handshake completed (%s)", + sm->wpa == WPA_VERSION_WPA ? "WPA" : "RSN"); + sm->has_GTK = TRUE; +} + + +SM_STATE(WPA_PTK_GROUP, KEYERROR) +{ + SM_ENTRY_MA(WPA_PTK_GROUP, KEYERROR, wpa_ptk_group); + if (sm->GUpdateStationKeys) + sm->group->GKeyDoneStations--; + sm->GUpdateStationKeys = FALSE; + sm->Disconnect = TRUE; +} + + +SM_STEP(WPA_PTK_GROUP) +{ + if (sm->Init || sm->PtkGroupInit) { + SM_ENTER(WPA_PTK_GROUP, IDLE); + sm->PtkGroupInit = FALSE; + } else switch (sm->wpa_ptk_group_state) { + case WPA_PTK_GROUP_IDLE: + if (sm->GUpdateStationKeys || + (sm->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 > + (int) 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 int wpa_gtk_update(struct wpa_authenticator *wpa_auth, + struct wpa_group *group) +{ + int ret = 0; + + /* FIX: is this the correct way of getting GNonce? */ + os_memcpy(group->GNonce, group->Counter, WPA_NONCE_LEN); + inc_byte_array(group->Counter, WPA_NONCE_LEN); + wpa_gmk_to_gtk(group->GMK, wpa_auth->addr, group->GNonce, + group->GTK[group->GN - 1], group->GTK_len); + +#ifdef CONFIG_IEEE80211W + if (wpa_auth->conf.ieee80211w != WPA_NO_IEEE80211W) { + if (os_get_random(group->IGTK[group->GN_igtk - 4], + WPA_IGTK_LEN) < 0) { + wpa_printf(MSG_INFO, "RSN: Failed to get new random " + "IGTK"); + ret = -1; + } + wpa_hexdump_key(MSG_DEBUG, "IGTK", + group->IGTK[group->GN_igtk - 4], WPA_IGTK_LEN); + } +#endif /* CONFIG_IEEE80211W */ + + return ret; +} + + +static void wpa_group_gtk_init(struct wpa_authenticator *wpa_auth, + struct wpa_group *group) +{ + wpa_printf(MSG_DEBUG, "WPA: group state machine entering state " + "GTK_INIT (VLAN-ID %d)", group->vlan_id); + group->changed = FALSE; /* GInit is not cleared here; avoid loop */ + group->wpa_group_state = WPA_GROUP_GTK_INIT; + + /* GTK[0..N] = 0 */ + os_memset(group->GTK, 0, sizeof(group->GTK)); + group->GN = 1; + group->GM = 2; +#ifdef CONFIG_IEEE80211W + group->GN_igtk = 4; + group->GM_igtk = 5; +#endif /* CONFIG_IEEE80211W */ + /* GTK[GN] = CalcGTK() */ + wpa_gtk_update(wpa_auth, group); +} + + +static int wpa_group_update_sta(struct wpa_state_machine *sm, void *ctx) +{ + if (sm->wpa_ptk_state != WPA_PTK_PTKINITDONE) { + wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, + "Not in PTKINITDONE; skip Group Key update"); + return 0; + } + if (sm->GUpdateStationKeys) { + /* + * This should not really happen, but just in case, make sure + * we do not count the same STA twice in GKeyDoneStations. + */ + wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, + "GUpdateStationKeys already set - do not " + "increment GKeyDoneStations"); + } else { + sm->group->GKeyDoneStations++; + sm->GUpdateStationKeys = TRUE; + } + wpa_sm_step(sm); + return 0; +} + + +static void wpa_group_setkeys(struct wpa_authenticator *wpa_auth, + struct wpa_group *group) +{ + int tmp; + + wpa_printf(MSG_DEBUG, "WPA: group state machine entering state " + "SETKEYS (VLAN-ID %d)", group->vlan_id); + group->changed = TRUE; + group->wpa_group_state = WPA_GROUP_SETKEYS; + group->GTKReKey = FALSE; + tmp = group->GM; + group->GM = group->GN; + group->GN = tmp; +#ifdef CONFIG_IEEE80211W + tmp = group->GM_igtk; + group->GM_igtk = group->GN_igtk; + group->GN_igtk = tmp; +#endif /* CONFIG_IEEE80211W */ + /* "GKeyDoneStations = GNoStations" is done in more robust way by + * counting the STAs that are marked with GUpdateStationKeys instead of + * including all STAs that could be in not-yet-completed state. */ + wpa_gtk_update(wpa_auth, group); + + wpa_auth_for_each_sta(wpa_auth, wpa_group_update_sta, NULL); + wpa_printf(MSG_DEBUG, "wpa_group_setkeys: GKeyDoneStations=%d", + group->GKeyDoneStations); +} + + +static void wpa_group_setkeysdone(struct wpa_authenticator *wpa_auth, + struct wpa_group *group) +{ + wpa_printf(MSG_DEBUG, "WPA: group state machine entering state " + "SETKEYSDONE (VLAN-ID %d)", group->vlan_id); + group->changed = TRUE; + group->wpa_group_state = WPA_GROUP_SETKEYSDONE; + wpa_auth_set_key(wpa_auth, group->vlan_id, + wpa_alg_txt(wpa_auth->conf.wpa_group), + NULL, group->GN, group->GTK[group->GN - 1], + group->GTK_len); + +#ifdef CONFIG_IEEE80211W + if (wpa_auth->conf.ieee80211w != WPA_NO_IEEE80211W) { + wpa_auth_set_key(wpa_auth, group->vlan_id, "IGTK", + NULL, group->GN_igtk, + group->IGTK[group->GN_igtk - 4], + WPA_IGTK_LEN); + } +#endif /* CONFIG_IEEE80211W */ +} + + +static void wpa_group_sm_step(struct wpa_authenticator *wpa_auth, + struct wpa_group *group) +{ + if (group->GInit) { + wpa_group_gtk_init(wpa_auth, group); + } else if (group->wpa_group_state == WPA_GROUP_GTK_INIT && + group->GTKAuthenticator) { + wpa_group_setkeysdone(wpa_auth, group); + } else if (group->wpa_group_state == WPA_GROUP_SETKEYSDONE && + group->GTKReKey) { + wpa_group_setkeys(wpa_auth, group); + } else if (group->wpa_group_state == WPA_GROUP_SETKEYS) { + if (group->GKeyDoneStations == 0) + wpa_group_setkeysdone(wpa_auth, group); + else if (group->GTKReKey) + wpa_group_setkeys(wpa_auth, group); + } +} + + +static void wpa_sm_step(struct wpa_state_machine *sm) +{ + if (sm == NULL) + return; + + if (sm->in_step_loop) { + /* This should not happen, but if it does, make sure we do not + * end up freeing the state machine too early by exiting the + * recursive call. */ + wpa_printf(MSG_ERROR, "WPA: wpa_sm_step() called recursively"); + return; + } + + sm->in_step_loop = 1; + do { + if (sm->pending_deinit) + break; + + sm->changed = FALSE; + sm->wpa_auth->group->changed = FALSE; + + SM_STEP_RUN(WPA_PTK); + if (sm->pending_deinit) + break; + SM_STEP_RUN(WPA_PTK_GROUP); + if (sm->pending_deinit) + break; + wpa_group_sm_step(sm->wpa_auth, sm->group); + } while (sm->changed || sm->wpa_auth->group->changed); + sm->in_step_loop = 0; + + if (sm->pending_deinit) { + wpa_printf(MSG_DEBUG, "WPA: Completing pending STA state " + "machine deinit for " MACSTR, MAC2STR(sm->addr)); + wpa_free_sta_sm(sm); + } +} + + +static void wpa_sm_call_step(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_state_machine *sm = eloop_ctx; + wpa_sm_step(sm); +} + + +void wpa_auth_sm_notify(struct wpa_state_machine *sm) +{ + if (sm == NULL) + return; + eloop_register_timeout(0, 0, wpa_sm_call_step, sm, NULL); +} + + +void wpa_gtk_rekey(struct wpa_authenticator *wpa_auth) +{ + int tmp, i; + struct wpa_group *group; + + if (wpa_auth == NULL) + return; + + group = wpa_auth->group; + + for (i = 0; i < 2; i++) { + tmp = group->GM; + group->GM = group->GN; + group->GN = tmp; +#ifdef CONFIG_IEEE80211W + tmp = group->GM_igtk; + group->GM_igtk = group->GN_igtk; + group->GN_igtk = tmp; +#endif /* CONFIG_IEEE80211W */ + wpa_gtk_update(wpa_auth, group); + } +} + + +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) >> 24) & 0xff, ((s) >> 16) & 0xff, ((s) >> 8) & 0xff, (s) & 0xff + +int wpa_get_mib(struct wpa_authenticator *wpa_auth, char *buf, size_t buflen) +{ + int len = 0, ret; + char pmkid_txt[PMKID_LEN * 2 + 1]; + + if (wpa_auth == NULL) + return len; + + ret = os_snprintf(buf + len, buflen - len, + "dot11RSNAOptionImplemented=TRUE\n" +#ifdef CONFIG_RSN_PREAUTH + "dot11RSNAPreauthenticationImplemented=TRUE\n" +#else /* CONFIG_RSN_PREAUTH */ + "dot11RSNAPreauthenticationImplemented=FALSE\n" +#endif /* CONFIG_RSN_PREAUTH */ + "dot11RSNAEnabled=%s\n" + "dot11RSNAPreauthenticationEnabled=%s\n", + wpa_bool_txt(wpa_auth->conf.wpa & WPA_PROTO_RSN), + wpa_bool_txt(wpa_auth->conf.rsn_preauth)); + if (ret < 0 || (size_t) ret >= buflen - len) + return len; + len += ret; + + wpa_snprintf_hex(pmkid_txt, sizeof(pmkid_txt), + wpa_auth->dot11RSNAPMKIDUsed, PMKID_LEN); + + ret = os_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, + !!wpa_auth->conf.wpa_strict_rekey, + dot11RSNAConfigGroupUpdateCount, + dot11RSNAConfigPairwiseUpdateCount, + wpa_cipher_bits(wpa_auth->conf.wpa_group), + dot11RSNAConfigPMKLifetime, + dot11RSNAConfigPMKReauthThreshold, + dot11RSNAConfigSATimeout, + RSN_SUITE_ARG(wpa_auth->dot11RSNAAuthenticationSuiteSelected), + RSN_SUITE_ARG(wpa_auth->dot11RSNAPairwiseCipherSelected), + RSN_SUITE_ARG(wpa_auth->dot11RSNAGroupCipherSelected), + pmkid_txt, + RSN_SUITE_ARG(wpa_auth->dot11RSNAAuthenticationSuiteRequested), + RSN_SUITE_ARG(wpa_auth->dot11RSNAPairwiseCipherRequested), + RSN_SUITE_ARG(wpa_auth->dot11RSNAGroupCipherRequested), + wpa_auth->dot11RSNATKIPCounterMeasuresInvoked, + wpa_auth->dot11RSNA4WayHandshakeFailures); + if (ret < 0 || (size_t) ret >= buflen - len) + return len; + len += ret; + + /* TODO: dot11RSNAConfigPairwiseCiphersTable */ + /* TODO: dot11RSNAConfigAuthenticationSuitesTable */ + + /* Private MIB */ + ret = os_snprintf(buf + len, buflen - len, "hostapdWPAGroupState=%d\n", + wpa_auth->group->wpa_group_state); + if (ret < 0 || (size_t) ret >= buflen - len) + return len; + len += ret; + + return len; +} + + +int wpa_get_mib_sta(struct wpa_state_machine *sm, char *buf, size_t buflen) +{ + int len = 0, ret; + u32 pairwise = 0; + + if (sm == NULL) + return 0; + + /* TODO: FF-FF-FF-FF-FF-FF entry for broadcast/multicast stats */ + + /* dot11RSNAStatsEntry */ + + if (sm->wpa == WPA_VERSION_WPA) { + if (sm->pairwise == WPA_CIPHER_CCMP) + pairwise = WPA_CIPHER_SUITE_CCMP; + else if (sm->pairwise == WPA_CIPHER_TKIP) + pairwise = WPA_CIPHER_SUITE_TKIP; + else if (sm->pairwise == WPA_CIPHER_WEP104) + pairwise = WPA_CIPHER_SUITE_WEP104; + else if (sm->pairwise == WPA_CIPHER_WEP40) + pairwise = WPA_CIPHER_SUITE_WEP40; + else if (sm->pairwise == WPA_CIPHER_NONE) + pairwise = WPA_CIPHER_SUITE_NONE; + } else if (sm->wpa == WPA_VERSION_WPA2) { + if (sm->pairwise == WPA_CIPHER_CCMP) + pairwise = RSN_CIPHER_SUITE_CCMP; + else if (sm->pairwise == WPA_CIPHER_TKIP) + pairwise = RSN_CIPHER_SUITE_TKIP; + else if (sm->pairwise == WPA_CIPHER_WEP104) + pairwise = RSN_CIPHER_SUITE_WEP104; + else if (sm->pairwise == WPA_CIPHER_WEP40) + pairwise = RSN_CIPHER_SUITE_WEP40; + else if (sm->pairwise == WPA_CIPHER_NONE) + pairwise = RSN_CIPHER_SUITE_NONE; + } else + return 0; + + ret = os_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(sm->addr), + RSN_SUITE_ARG(pairwise), + sm->dot11RSNAStatsTKIPLocalMICFailures, + sm->dot11RSNAStatsTKIPRemoteMICFailures); + if (ret < 0 || (size_t) ret >= buflen - len) + return len; + len += ret; + + /* Private MIB */ + ret = os_snprintf(buf + len, buflen - len, + "hostapdWPAPTKState=%d\n" + "hostapdWPAPTKGroupState=%d\n", + sm->wpa_ptk_state, + sm->wpa_ptk_group_state); + if (ret < 0 || (size_t) ret >= buflen - len) + return len; + len += ret; + + return len; +} + + +void wpa_auth_countermeasures_start(struct wpa_authenticator *wpa_auth) +{ + if (wpa_auth) + wpa_auth->dot11RSNATKIPCounterMeasuresInvoked++; +} + + +int wpa_auth_pairwise_set(struct wpa_state_machine *sm) +{ + return sm && sm->pairwise_set; +} + + +int wpa_auth_get_pairwise(struct wpa_state_machine *sm) +{ + return sm->pairwise; +} + + +int wpa_auth_sta_key_mgmt(struct wpa_state_machine *sm) +{ + if (sm == NULL) + return -1; + return sm->wpa_key_mgmt; +} + + +int wpa_auth_sta_wpa_version(struct wpa_state_machine *sm) +{ + if (sm == NULL) + return 0; + return sm->wpa; +} + + +int wpa_auth_sta_clear_pmksa(struct wpa_state_machine *sm, + struct rsn_pmksa_cache_entry *entry) +{ + if (sm == NULL || sm->pmksa != entry) + return -1; + sm->pmksa = NULL; + return 0; +} + + +struct rsn_pmksa_cache_entry * +wpa_auth_sta_get_pmksa(struct wpa_state_machine *sm) +{ + return sm ? sm->pmksa : NULL; +} + + +void wpa_auth_sta_local_mic_failure_report(struct wpa_state_machine *sm) +{ + if (sm) + sm->dot11RSNAStatsTKIPLocalMICFailures++; +} + + +const u8 * wpa_auth_get_wpa_ie(struct wpa_authenticator *wpa_auth, size_t *len) +{ + if (wpa_auth == NULL) + return NULL; + *len = wpa_auth->wpa_ie_len; + return wpa_auth->wpa_ie; +} + + +int wpa_auth_pmksa_add(struct wpa_state_machine *sm, const u8 *pmk, + int session_timeout, struct eapol_state_machine *eapol) +{ + if (sm == NULL || sm->wpa != WPA_VERSION_WPA2) + return -1; + + if (pmksa_cache_add(sm->wpa_auth->pmksa, pmk, PMK_LEN, + sm->wpa_auth->addr, sm->addr, session_timeout, + eapol, sm->wpa_key_mgmt)) + return 0; + + return -1; +} + + +int wpa_auth_pmksa_add_preauth(struct wpa_authenticator *wpa_auth, + const u8 *pmk, size_t len, const u8 *sta_addr, + int session_timeout, + struct eapol_state_machine *eapol) +{ + if (wpa_auth == NULL) + return -1; + + if (pmksa_cache_add(wpa_auth->pmksa, pmk, len, wpa_auth->addr, + sta_addr, session_timeout, eapol, + WPA_KEY_MGMT_IEEE8021X)) + return 0; + + return -1; +} + + +static struct wpa_group * +wpa_auth_add_group(struct wpa_authenticator *wpa_auth, int vlan_id) +{ + struct wpa_group *group; + + if (wpa_auth == NULL || wpa_auth->group == NULL) + return NULL; + + wpa_printf(MSG_DEBUG, "WPA: Add group state machine for VLAN-ID %d", + vlan_id); + group = wpa_group_init(wpa_auth, vlan_id); + if (group == NULL) + return NULL; + + group->next = wpa_auth->group->next; + wpa_auth->group->next = group; + + return group; +} + + +int wpa_auth_sta_set_vlan(struct wpa_state_machine *sm, int vlan_id) +{ + struct wpa_group *group; + + if (sm == NULL || sm->wpa_auth == NULL) + return 0; + + group = sm->wpa_auth->group; + while (group) { + if (group->vlan_id == vlan_id) + break; + group = group->next; + } + + if (group == NULL) { + group = wpa_auth_add_group(sm->wpa_auth, vlan_id); + if (group == NULL) + return -1; + } + + if (sm->group == group) + return 0; + + wpa_printf(MSG_DEBUG, "WPA: Moving STA " MACSTR " to use group state " + "machine for VLAN ID %d", MAC2STR(sm->addr), vlan_id); + + sm->group = group; + return 0; +} + +#endif /* CONFIG_NATIVE_WINDOWS */ diff --git a/contrib/wpa/hostapd/wpa.h b/contrib/wpa/hostapd/wpa.h new file mode 100644 index 0000000..d353844 --- /dev/null +++ b/contrib/wpa/hostapd/wpa.h @@ -0,0 +1,284 @@ +/* + * hostapd - IEEE 802.11i-2004 / WPA Authenticator + * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef WPA_AUTH_H +#define WPA_AUTH_H + +#include "eapol_common.h" +#include "wpa_common.h" + +#ifdef _MSC_VER +#pragma pack(push, 1) +#endif /* _MSC_VER */ + +/* IEEE Std 802.11r-2008, 11A.10.3 - Remote request/response frame definition + */ +struct ft_rrb_frame { + u8 frame_type; /* RSN_REMOTE_FRAME_TYPE_FT_RRB */ + u8 packet_type; /* FT_PACKET_REQUEST/FT_PACKET_RESPONSE */ + le16 action_length; /* little endian length of action_frame */ + u8 ap_address[ETH_ALEN]; + /* + * Followed by action_length bytes of FT Action frame (from Category + * field to the end of Action Frame body. + */ +} STRUCT_PACKED; + +#define RSN_REMOTE_FRAME_TYPE_FT_RRB 1 + +#define FT_PACKET_REQUEST 0 +#define FT_PACKET_RESPONSE 1 +/* Vendor-specific types for R0KH-R1KH protocol; not defined in 802.11r */ +#define FT_PACKET_R0KH_R1KH_PULL 200 +#define FT_PACKET_R0KH_R1KH_RESP 201 +#define FT_PACKET_R0KH_R1KH_PUSH 202 + +#ifndef ETH_P_RRB +#define ETH_P_RRB 0x890D +#endif /* ETH_P_RRB */ + +#define FT_R0KH_R1KH_PULL_DATA_LEN 44 +#define FT_R0KH_R1KH_RESP_DATA_LEN 76 +#define FT_R0KH_R1KH_PUSH_DATA_LEN 80 + +struct ft_r0kh_r1kh_pull_frame { + u8 frame_type; /* RSN_REMOTE_FRAME_TYPE_FT_RRB */ + u8 packet_type; /* FT_PACKET_R0KH_R1KH_PULL */ + le16 data_length; /* little endian length of data (44) */ + u8 ap_address[ETH_ALEN]; + + u8 nonce[16]; + u8 pmk_r0_name[WPA_PMK_NAME_LEN]; + u8 r1kh_id[FT_R1KH_ID_LEN]; + u8 s1kh_id[ETH_ALEN]; + u8 pad[4]; /* 8-octet boundary for AES key wrap */ + u8 key_wrap_extra[8]; +} STRUCT_PACKED; + +struct ft_r0kh_r1kh_resp_frame { + u8 frame_type; /* RSN_REMOTE_FRAME_TYPE_FT_RRB */ + u8 packet_type; /* FT_PACKET_R0KH_R1KH_RESP */ + le16 data_length; /* little endian length of data (76) */ + u8 ap_address[ETH_ALEN]; + + u8 nonce[16]; /* copied from pull */ + u8 r1kh_id[FT_R1KH_ID_LEN]; /* copied from pull */ + u8 s1kh_id[ETH_ALEN]; /* copied from pull */ + u8 pmk_r1[PMK_LEN]; + u8 pmk_r1_name[WPA_PMK_NAME_LEN]; + u8 pad[4]; /* 8-octet boundary for AES key wrap */ + u8 key_wrap_extra[8]; +} STRUCT_PACKED; + +struct ft_r0kh_r1kh_push_frame { + u8 frame_type; /* RSN_REMOTE_FRAME_TYPE_FT_RRB */ + u8 packet_type; /* FT_PACKET_R0KH_R1KH_PUSH */ + le16 data_length; /* little endian length of data (80) */ + u8 ap_address[ETH_ALEN]; + + /* Encrypted with AES key-wrap */ + u8 timestamp[4]; /* current time in seconds since unix epoch, little + * endian */ + u8 r1kh_id[FT_R1KH_ID_LEN]; + u8 s1kh_id[ETH_ALEN]; + u8 pmk_r0_name[WPA_PMK_NAME_LEN]; + u8 pmk_r1[PMK_LEN]; + u8 pmk_r1_name[WPA_PMK_NAME_LEN]; + u8 key_wrap_extra[8]; +} STRUCT_PACKED; + +#ifdef _MSC_VER +#pragma pack(pop) +#endif /* _MSC_VER */ + + +/* per STA state machine data */ + +struct wpa_authenticator; +struct wpa_state_machine; +struct rsn_pmksa_cache_entry; +struct eapol_state_machine; + + +struct ft_remote_r0kh { + struct ft_remote_r0kh *next; + u8 addr[ETH_ALEN]; + u8 id[FT_R0KH_ID_MAX_LEN]; + size_t id_len; + u8 key[16]; +}; + + +struct ft_remote_r1kh { + struct ft_remote_r1kh *next; + u8 addr[ETH_ALEN]; + u8 id[FT_R1KH_ID_LEN]; + u8 key[16]; +}; + + +struct wpa_auth_config { + int wpa; + int wpa_key_mgmt; + int wpa_pairwise; + int wpa_group; + int wpa_group_rekey; + int wpa_strict_rekey; + int wpa_gmk_rekey; + int wpa_ptk_rekey; + int rsn_pairwise; + int rsn_preauth; + int eapol_version; + int peerkey; + int wme_enabled; + int okc; +#ifdef CONFIG_IEEE80211W + enum { + WPA_NO_IEEE80211W = 0, + WPA_IEEE80211W_OPTIONAL = 1, + WPA_IEEE80211W_REQUIRED = 2 + } ieee80211w; +#endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_IEEE80211R +#define SSID_LEN 32 + u8 ssid[SSID_LEN]; + size_t ssid_len; + u8 mobility_domain[MOBILITY_DOMAIN_ID_LEN]; + u8 r0_key_holder[FT_R0KH_ID_MAX_LEN]; + size_t r0_key_holder_len; + u8 r1_key_holder[FT_R1KH_ID_LEN]; + u32 r0_key_lifetime; + u32 reassociation_deadline; + struct ft_remote_r0kh *r0kh_list; + struct ft_remote_r1kh *r1kh_list; + int pmk_r1_push; +#endif /* CONFIG_IEEE80211R */ +}; + +typedef enum { + LOGGER_DEBUG, LOGGER_INFO, LOGGER_WARNING +} logger_level; + +typedef enum { + WPA_EAPOL_portEnabled, WPA_EAPOL_portValid, WPA_EAPOL_authorized, + WPA_EAPOL_portControl_Auto, WPA_EAPOL_keyRun, WPA_EAPOL_keyAvailable, + WPA_EAPOL_keyDone, WPA_EAPOL_inc_EapolFramesTx +} wpa_eapol_variable; + +struct wpa_auth_callbacks { + void *ctx; + void (*logger)(void *ctx, const u8 *addr, logger_level level, + const char *txt); + void (*disconnect)(void *ctx, const u8 *addr, u16 reason); + void (*mic_failure_report)(void *ctx, const u8 *addr); + void (*set_eapol)(void *ctx, const u8 *addr, wpa_eapol_variable var, + int value); + int (*get_eapol)(void *ctx, const u8 *addr, wpa_eapol_variable var); + const u8 * (*get_psk)(void *ctx, const u8 *addr, const u8 *prev_psk); + int (*get_msk)(void *ctx, const u8 *addr, u8 *msk, size_t *len); + int (*set_key)(void *ctx, int vlan_id, const char *alg, const u8 *addr, + int idx, u8 *key, size_t key_len); + int (*get_seqnum)(void *ctx, const u8 *addr, int idx, u8 *seq); + int (*get_seqnum_igtk)(void *ctx, const u8 *addr, int idx, u8 *seq); + int (*send_eapol)(void *ctx, const u8 *addr, const u8 *data, + size_t data_len, int encrypt); + int (*for_each_sta)(void *ctx, int (*cb)(struct wpa_state_machine *sm, + void *ctx), void *cb_ctx); + int (*for_each_auth)(void *ctx, int (*cb)(struct wpa_authenticator *a, + void *ctx), void *cb_ctx); + int (*send_ether)(void *ctx, const u8 *dst, u16 proto, const u8 *data, + size_t data_len); +#ifdef CONFIG_IEEE80211R + struct wpa_state_machine * (*add_sta)(void *ctx, const u8 *sta_addr); + int (*send_ft_action)(void *ctx, const u8 *dst, + const u8 *data, size_t data_len); +#endif /* CONFIG_IEEE80211R */ +}; + +struct wpa_authenticator * wpa_init(const u8 *addr, + struct wpa_auth_config *conf, + struct wpa_auth_callbacks *cb); +void wpa_deinit(struct wpa_authenticator *wpa_auth); +int wpa_reconfig(struct wpa_authenticator *wpa_auth, + struct wpa_auth_config *conf); + +enum { + WPA_IE_OK, WPA_INVALID_IE, WPA_INVALID_GROUP, WPA_INVALID_PAIRWISE, + WPA_INVALID_AKMP, WPA_NOT_ENABLED, WPA_ALLOC_FAIL, + WPA_MGMT_FRAME_PROTECTION_VIOLATION, WPA_INVALID_MGMT_GROUP_CIPHER, + WPA_INVALID_MDIE, WPA_INVALID_PROTO +}; + +int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm, + const u8 *wpa_ie, size_t wpa_ie_len, + const u8 *mdie, size_t mdie_len); +int wpa_auth_uses_mfp(struct wpa_state_machine *sm); +struct wpa_state_machine * +wpa_auth_sta_init(struct wpa_authenticator *wpa_auth, const u8 *addr); +void wpa_auth_sta_associated(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm); +void wpa_auth_sta_no_wpa(struct wpa_state_machine *sm); +void wpa_auth_sta_deinit(struct wpa_state_machine *sm); +void wpa_receive(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm, + u8 *data, size_t data_len); +typedef enum { + WPA_AUTH, WPA_ASSOC, WPA_DISASSOC, WPA_DEAUTH, WPA_REAUTH, + WPA_REAUTH_EAPOL, WPA_ASSOC_FT +} wpa_event; +void wpa_remove_ptk(struct wpa_state_machine *sm); +void wpa_auth_sm_event(struct wpa_state_machine *sm, wpa_event event); +void wpa_auth_sm_notify(struct wpa_state_machine *sm); +void wpa_gtk_rekey(struct wpa_authenticator *wpa_auth); +int wpa_get_mib(struct wpa_authenticator *wpa_auth, char *buf, size_t buflen); +int wpa_get_mib_sta(struct wpa_state_machine *sm, char *buf, size_t buflen); +void wpa_auth_countermeasures_start(struct wpa_authenticator *wpa_auth); +int wpa_auth_pairwise_set(struct wpa_state_machine *sm); +int wpa_auth_get_pairwise(struct wpa_state_machine *sm); +int wpa_auth_sta_key_mgmt(struct wpa_state_machine *sm); +int wpa_auth_sta_wpa_version(struct wpa_state_machine *sm); +int wpa_auth_sta_clear_pmksa(struct wpa_state_machine *sm, + struct rsn_pmksa_cache_entry *entry); +struct rsn_pmksa_cache_entry * +wpa_auth_sta_get_pmksa(struct wpa_state_machine *sm); +void wpa_auth_sta_local_mic_failure_report(struct wpa_state_machine *sm); +const u8 * wpa_auth_get_wpa_ie(struct wpa_authenticator *wpa_auth, + size_t *len); +int wpa_auth_pmksa_add(struct wpa_state_machine *sm, const u8 *pmk, + int session_timeout, struct eapol_state_machine *eapol); +int wpa_auth_pmksa_add_preauth(struct wpa_authenticator *wpa_auth, + const u8 *pmk, size_t len, const u8 *sta_addr, + int session_timeout, + struct eapol_state_machine *eapol); +int wpa_auth_sta_set_vlan(struct wpa_state_machine *sm, int vlan_id); + +#ifdef CONFIG_IEEE80211R +u8 * wpa_sm_write_assoc_resp_ies(struct wpa_state_machine *sm, u8 *pos, + size_t max_len, int auth_alg); +void wpa_ft_process_auth(struct wpa_state_machine *sm, const u8 *bssid, + u16 auth_transaction, const u8 *ies, size_t ies_len, + void (*cb)(void *ctx, const u8 *dst, const u8 *bssid, + u16 auth_transaction, u16 resp, + const u8 *ies, size_t ies_len), + void *ctx); +u16 wpa_ft_validate_reassoc(struct wpa_state_machine *sm, const u8 *ies, + size_t ies_len); +int wpa_ft_action_rx(struct wpa_state_machine *sm, const u8 *data, size_t len); +int wpa_ft_rrb_rx(struct wpa_authenticator *wpa_auth, const u8 *src_addr, + const u8 *data, size_t data_len); +void wpa_ft_push_pmk_r1(struct wpa_authenticator *wpa_auth, const u8 *addr); +#endif /* CONFIG_IEEE80211R */ + +#endif /* WPA_AUTH_H */ diff --git a/contrib/wpa/hostapd/wpa_auth_i.h b/contrib/wpa/hostapd/wpa_auth_i.h new file mode 100644 index 0000000..bcaeda5 --- /dev/null +++ b/contrib/wpa/hostapd/wpa_auth_i.h @@ -0,0 +1,221 @@ +/* + * hostapd - IEEE 802.11i-2004 / WPA Authenticator: Internal definitions + * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef WPA_AUTH_I_H +#define WPA_AUTH_I_H + +/* max(dot11RSNAConfigGroupUpdateCount,dot11RSNAConfigPairwiseUpdateCount) */ +#define RSNA_MAX_EAPOL_RETRIES 4 + +struct wpa_group; + +struct wpa_stsl_negotiation { + struct wpa_stsl_negotiation *next; + u8 initiator[ETH_ALEN]; + u8 peer[ETH_ALEN]; +}; + + +struct wpa_state_machine { + struct wpa_authenticator *wpa_auth; + struct wpa_group *group; + + u8 addr[ETH_ALEN]; + + 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[PMK_LEN]; + struct wpa_ptk PTK; + Boolean PTK_valid; + Boolean pairwise_set; + int keycount; + Boolean Pair; + struct { + u8 counter[WPA_REPLAY_COUNTER_LEN]; + Boolean valid; + } key_replay[RSNA_MAX_EAPOL_RETRIES]; + Boolean PInitAKeys; /* WPA only, not in IEEE 802.11i */ + Boolean PTKRequest; /* not in IEEE 802.11i state machine */ + Boolean has_GTK; + Boolean PtkGroupInit; /* init request for PTK Group state machine */ + + u8 *last_rx_eapol_key; /* starting from IEEE 802.1X header */ + size_t last_rx_eapol_key_len; + + unsigned int changed:1; + unsigned int in_step_loop:1; + unsigned int pending_deinit:1; + unsigned int started:1; + unsigned int mgmt_frame_prot:1; +#ifdef CONFIG_IEEE80211R + unsigned int ft_completed:1; + unsigned int pmk_r1_name_valid:1; +#endif /* CONFIG_IEEE80211R */ + + u8 req_replay_counter[WPA_REPLAY_COUNTER_LEN]; + int req_replay_counter_used; + + u8 *wpa_ie; + size_t wpa_ie_len; + + 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 pairwise; /* Pairwise cipher suite, WPA_CIPHER_* */ + int wpa_key_mgmt; /* the selected WPA_KEY_MGMT_* */ + struct rsn_pmksa_cache_entry *pmksa; + + u32 dot11RSNAStatsTKIPLocalMICFailures; + u32 dot11RSNAStatsTKIPRemoteMICFailures; + +#ifdef CONFIG_IEEE80211R + u8 xxkey[PMK_LEN]; /* PSK or the second 256 bits of MSK */ + size_t xxkey_len; + u8 pmk_r1_name[WPA_PMK_NAME_LEN]; /* PMKR1Name derived from FT Auth + * Request */ + u8 r0kh_id[FT_R0KH_ID_MAX_LEN]; /* R0KH-ID from FT Auth Request */ + size_t r0kh_id_len; +#endif /* CONFIG_IEEE80211R */ +}; + + +/* per group key state machine data */ +struct wpa_group { + struct wpa_group *next; + int vlan_id; + + Boolean GInit; + 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; +#ifdef CONFIG_IEEE80211W + u8 IGTK[2][WPA_IGTK_LEN]; + int GN_igtk, GM_igtk; +#endif /* CONFIG_IEEE80211W */ +}; + + +struct wpa_ft_pmk_cache; + +/* per authenticator data */ +struct wpa_authenticator { + struct wpa_group *group; + + unsigned int dot11RSNAStatsTKIPRemoteMICFailures; + u32 dot11RSNAAuthenticationSuiteSelected; + u32 dot11RSNAPairwiseCipherSelected; + u32 dot11RSNAGroupCipherSelected; + u8 dot11RSNAPMKIDUsed[PMKID_LEN]; + u32 dot11RSNAAuthenticationSuiteRequested; /* FIX: update */ + u32 dot11RSNAPairwiseCipherRequested; /* FIX: update */ + u32 dot11RSNAGroupCipherRequested; /* FIX: update */ + unsigned int dot11RSNATKIPCounterMeasuresInvoked; + unsigned int dot11RSNA4WayHandshakeFailures; + + struct wpa_stsl_negotiation *stsl_negotiations; + + struct wpa_auth_config conf; + struct wpa_auth_callbacks cb; + + u8 *wpa_ie; + size_t wpa_ie_len; + + u8 addr[ETH_ALEN]; + + struct rsn_pmksa_cache *pmksa; + struct wpa_ft_pmk_cache *ft_pmk_cache; +}; + + +int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len, + const u8 *pmkid); +void wpa_auth_logger(struct wpa_authenticator *wpa_auth, const u8 *addr, + logger_level level, const char *txt); +void wpa_auth_vlogger(struct wpa_authenticator *wpa_auth, const u8 *addr, + logger_level level, const char *fmt, ...); +void __wpa_send_eapol(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm, int key_info, + const u8 *key_rsc, const u8 *nonce, + const u8 *kde, size_t kde_len, + int keyidx, int encr, int force_version); +int wpa_auth_for_each_sta(struct wpa_authenticator *wpa_auth, + int (*cb)(struct wpa_state_machine *sm, void *ctx), + void *cb_ctx); +int wpa_auth_for_each_auth(struct wpa_authenticator *wpa_auth, + int (*cb)(struct wpa_authenticator *a, void *ctx), + void *cb_ctx); + +#ifdef CONFIG_PEERKEY +int wpa_stsl_remove(struct wpa_authenticator *wpa_auth, + struct wpa_stsl_negotiation *neg); +void wpa_smk_error(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm, struct wpa_eapol_key *key); +void wpa_smk_m1(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm, struct wpa_eapol_key *key); +void wpa_smk_m3(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm, struct wpa_eapol_key *key); +#endif /* CONFIG_PEERKEY */ + +#ifdef CONFIG_IEEE80211R +int wpa_write_mdie(struct wpa_auth_config *conf, u8 *buf, size_t len); +int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, const u8 *pmk, + struct wpa_ptk *ptk); +struct wpa_ft_pmk_cache * wpa_ft_pmk_cache_init(void); +void wpa_ft_pmk_cache_deinit(struct wpa_ft_pmk_cache *cache); +#endif /* CONFIG_IEEE80211R */ + +#endif /* WPA_AUTH_I_H */ diff --git a/contrib/wpa/hostapd/wpa_auth_ie.c b/contrib/wpa/hostapd/wpa_auth_ie.c new file mode 100644 index 0000000..3ac9d67 --- /dev/null +++ b/contrib/wpa/hostapd/wpa_auth_ie.c @@ -0,0 +1,864 @@ +/* + * hostapd - WPA/RSN IE and KDE definitions + * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "config.h" +#include "ieee802_11.h" +#include "eapol_sm.h" +#include "wpa.h" +#include "pmksa_cache.h" +#include "wpa_auth_ie.h" +#include "wpa_auth_i.h" + + +static int wpa_write_wpa_ie(struct wpa_auth_config *conf, 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_VENDOR_SPECIFIC; + RSN_SELECTOR_PUT(hdr->oui, WPA_OUI_TYPE); + WPA_PUT_LE16(hdr->version, WPA_VERSION); + pos = (u8 *) (hdr + 1); + + if (conf->wpa_group == WPA_CIPHER_CCMP) { + RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_CCMP); + } else if (conf->wpa_group == WPA_CIPHER_TKIP) { + RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_TKIP); + } else if (conf->wpa_group == WPA_CIPHER_WEP104) { + RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_WEP104); + } else if (conf->wpa_group == WPA_CIPHER_WEP40) { + RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_WEP40); + } else { + wpa_printf(MSG_DEBUG, "Invalid group cipher (%d).", + conf->wpa_group); + return -1; + } + pos += WPA_SELECTOR_LEN; + + num_suites = 0; + count = pos; + pos += 2; + + if (conf->wpa_pairwise & WPA_CIPHER_CCMP) { + RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_CCMP); + pos += WPA_SELECTOR_LEN; + num_suites++; + } + if (conf->wpa_pairwise & WPA_CIPHER_TKIP) { + RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_TKIP); + pos += WPA_SELECTOR_LEN; + num_suites++; + } + if (conf->wpa_pairwise & WPA_CIPHER_NONE) { + RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_NONE); + pos += WPA_SELECTOR_LEN; + num_suites++; + } + + if (num_suites == 0) { + wpa_printf(MSG_DEBUG, "Invalid pairwise cipher (%d).", + conf->wpa_pairwise); + return -1; + } + WPA_PUT_LE16(count, num_suites); + + num_suites = 0; + count = pos; + pos += 2; + + if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X) { + RSN_SELECTOR_PUT(pos, WPA_AUTH_KEY_MGMT_UNSPEC_802_1X); + pos += WPA_SELECTOR_LEN; + num_suites++; + } + if (conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK) { + RSN_SELECTOR_PUT(pos, WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X); + pos += WPA_SELECTOR_LEN; + num_suites++; + } + + if (num_suites == 0) { + wpa_printf(MSG_DEBUG, "Invalid key management type (%d).", + conf->wpa_key_mgmt); + return -1; + } + WPA_PUT_LE16(count, num_suites); + + /* WPA Capabilities; use defaults, so no need to include it */ + + hdr->len = (pos - buf) - 2; + + return pos - buf; +} + + +int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len, + const u8 *pmkid) +{ + struct rsn_ie_hdr *hdr; + int num_suites; + u8 *pos, *count; + u16 capab; + + hdr = (struct rsn_ie_hdr *) buf; + hdr->elem_id = WLAN_EID_RSN; + WPA_PUT_LE16(hdr->version, RSN_VERSION); + pos = (u8 *) (hdr + 1); + + if (conf->wpa_group == WPA_CIPHER_CCMP) { + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP); + } else if (conf->wpa_group == WPA_CIPHER_TKIP) { + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_TKIP); + } else if (conf->wpa_group == WPA_CIPHER_WEP104) { + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_WEP104); + } else if (conf->wpa_group == WPA_CIPHER_WEP40) { + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_WEP40); + } else { + wpa_printf(MSG_DEBUG, "Invalid group cipher (%d).", + conf->wpa_group); + return -1; + } + pos += RSN_SELECTOR_LEN; + + num_suites = 0; + count = pos; + pos += 2; + + if (conf->rsn_pairwise & WPA_CIPHER_CCMP) { + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP); + pos += RSN_SELECTOR_LEN; + num_suites++; + } + if (conf->rsn_pairwise & WPA_CIPHER_TKIP) { + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_TKIP); + pos += RSN_SELECTOR_LEN; + num_suites++; + } + if (conf->rsn_pairwise & WPA_CIPHER_NONE) { + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_NONE); + pos += RSN_SELECTOR_LEN; + num_suites++; + } + + if (num_suites == 0) { + wpa_printf(MSG_DEBUG, "Invalid pairwise cipher (%d).", + conf->rsn_pairwise); + return -1; + } + WPA_PUT_LE16(count, num_suites); + + num_suites = 0; + count = pos; + pos += 2; + + if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_UNSPEC_802_1X); + pos += RSN_SELECTOR_LEN; + num_suites++; + } + if (conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X); + pos += RSN_SELECTOR_LEN; + num_suites++; + } +#ifdef CONFIG_IEEE80211R + if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_802_1X); + pos += RSN_SELECTOR_LEN; + num_suites++; + } + if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_PSK) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_PSK); + pos += RSN_SELECTOR_LEN; + num_suites++; + } +#endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_IEEE80211W + if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_802_1X_SHA256); + pos += RSN_SELECTOR_LEN; + num_suites++; + } + if (conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK_SHA256) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_PSK_SHA256); + pos += RSN_SELECTOR_LEN; + num_suites++; + } +#endif /* CONFIG_IEEE80211W */ + + if (num_suites == 0) { + wpa_printf(MSG_DEBUG, "Invalid key management type (%d).", + conf->wpa_key_mgmt); + return -1; + } + WPA_PUT_LE16(count, num_suites); + + /* RSN Capabilities */ + capab = 0; + if (conf->rsn_preauth) + capab |= WPA_CAPABILITY_PREAUTH; + if (conf->peerkey) + capab |= WPA_CAPABILITY_PEERKEY_ENABLED; + if (conf->wme_enabled) { + /* 4 PTKSA replay counters when using WME */ + capab |= (RSN_NUM_REPLAY_COUNTERS_16 << 2); + } +#ifdef CONFIG_IEEE80211W + if (conf->ieee80211w != WPA_NO_IEEE80211W) { + capab |= WPA_CAPABILITY_MFPC; + if (conf->ieee80211w == IEEE80211W_REQUIRED) + capab |= WPA_CAPABILITY_MFPR; + } +#endif /* CONFIG_IEEE80211W */ + WPA_PUT_LE16(pos, capab); + pos += 2; + + if (pmkid) { + if (pos + 2 + PMKID_LEN > buf + len) + return -1; + /* PMKID Count */ + WPA_PUT_LE16(pos, 1); + pos += 2; + os_memcpy(pos, pmkid, PMKID_LEN); + pos += PMKID_LEN; + } + +#ifdef CONFIG_IEEE80211W + if (conf->ieee80211w != WPA_NO_IEEE80211W) { + if (pos + 2 + 4 > buf + len) + return -1; + if (pmkid == NULL) { + /* PMKID Count */ + WPA_PUT_LE16(pos, 0); + pos += 2; + } + + /* Management Group Cipher Suite */ + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_AES_128_CMAC); + pos += RSN_SELECTOR_LEN; + } +#endif /* CONFIG_IEEE80211W */ + + hdr->len = (pos - buf) - 2; + + return pos - buf; +} + + +int wpa_auth_gen_wpa_ie(struct wpa_authenticator *wpa_auth) +{ + u8 *pos, buf[128]; + int res; + + pos = buf; + + if (wpa_auth->conf.wpa & WPA_PROTO_RSN) { + res = wpa_write_rsn_ie(&wpa_auth->conf, + pos, buf + sizeof(buf) - pos, NULL); + if (res < 0) + return res; + pos += res; + } +#ifdef CONFIG_IEEE80211R + if (wpa_auth->conf.wpa_key_mgmt & + (WPA_KEY_MGMT_FT_IEEE8021X | WPA_KEY_MGMT_FT_PSK)) { + res = wpa_write_mdie(&wpa_auth->conf, pos, + buf + sizeof(buf) - pos); + if (res < 0) + return res; + pos += res; + } +#endif /* CONFIG_IEEE80211R */ + if (wpa_auth->conf.wpa & WPA_PROTO_WPA) { + res = wpa_write_wpa_ie(&wpa_auth->conf, + pos, buf + sizeof(buf) - pos); + if (res < 0) + return res; + pos += res; + } + + os_free(wpa_auth->wpa_ie); + wpa_auth->wpa_ie = os_malloc(pos - buf); + if (wpa_auth->wpa_ie == NULL) + return -1; + os_memcpy(wpa_auth->wpa_ie, buf, pos - buf); + wpa_auth->wpa_ie_len = pos - buf; + + return 0; +} + + +u8 * wpa_add_kde(u8 *pos, u32 kde, const u8 *data, size_t data_len, + const u8 *data2, size_t data2_len) +{ + *pos++ = WLAN_EID_VENDOR_SPECIFIC; + *pos++ = RSN_SELECTOR_LEN + data_len + data2_len; + RSN_SELECTOR_PUT(pos, kde); + pos += RSN_SELECTOR_LEN; + os_memcpy(pos, data, data_len); + pos += data_len; + if (data2) { + os_memcpy(pos, data2, data2_len); + pos += data2_len; + } + return pos; +} + + +static int wpa_selector_to_bitfield(const u8 *s) +{ + if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_NONE) + return WPA_CIPHER_NONE; + if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_WEP40) + return WPA_CIPHER_WEP40; + if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_TKIP) + return WPA_CIPHER_TKIP; + if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_CCMP) + return WPA_CIPHER_CCMP; + if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_WEP104) + return WPA_CIPHER_WEP104; + return 0; +} + + +static int wpa_key_mgmt_to_bitfield(const u8 *s) +{ + if (RSN_SELECTOR_GET(s) == WPA_AUTH_KEY_MGMT_UNSPEC_802_1X) + return WPA_KEY_MGMT_IEEE8021X; + if (RSN_SELECTOR_GET(s) == WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X) + return WPA_KEY_MGMT_PSK; + if (RSN_SELECTOR_GET(s) == WPA_AUTH_KEY_MGMT_NONE) + return WPA_KEY_MGMT_WPA_NONE; + return 0; +} + + +static int wpa_parse_wpa_ie_wpa(const u8 *wpa_ie, size_t wpa_ie_len, + struct wpa_ie_data *data) +{ + const struct wpa_ie_hdr *hdr; + const u8 *pos; + int left; + int i, count; + + os_memset(data, 0, sizeof(*data)); + data->pairwise_cipher = WPA_CIPHER_TKIP; + data->group_cipher = WPA_CIPHER_TKIP; + data->key_mgmt = WPA_KEY_MGMT_IEEE8021X; + data->mgmt_group_cipher = 0; + + if (wpa_ie_len < sizeof(struct wpa_ie_hdr)) + return -1; + + hdr = (const struct wpa_ie_hdr *) wpa_ie; + + if (hdr->elem_id != WLAN_EID_VENDOR_SPECIFIC || + hdr->len != wpa_ie_len - 2 || + RSN_SELECTOR_GET(hdr->oui) != WPA_OUI_TYPE || + WPA_GET_LE16(hdr->version) != WPA_VERSION) { + return -2; + } + + pos = (const 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 = WPA_GET_LE16(pos); + 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 = WPA_GET_LE16(pos); + 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 = WPA_GET_LE16(pos); + pos += 2; + left -= 2; + } + + if (left > 0) { + return -8; + } + + return 0; +} + + +struct wpa_auth_okc_iter_data { + struct rsn_pmksa_cache_entry *pmksa; + const u8 *aa; + const u8 *spa; + const u8 *pmkid; +}; + + +static int wpa_auth_okc_iter(struct wpa_authenticator *a, void *ctx) +{ + struct wpa_auth_okc_iter_data *data = ctx; + data->pmksa = pmksa_cache_get_okc(a->pmksa, data->aa, data->spa, + data->pmkid); + if (data->pmksa) + return 1; + return 0; +} + + +int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm, + const u8 *wpa_ie, size_t wpa_ie_len, + const u8 *mdie, size_t mdie_len) +{ + struct wpa_ie_data data; + int ciphers, key_mgmt, res, version; + u32 selector; + size_t i; + const u8 *pmkid = NULL; + + if (wpa_auth == NULL || sm == NULL) + return WPA_NOT_ENABLED; + + if (wpa_ie == NULL || wpa_ie_len < 1) + return WPA_INVALID_IE; + + if (wpa_ie[0] == WLAN_EID_RSN) + version = WPA_PROTO_RSN; + else + version = WPA_PROTO_WPA; + + if (!(wpa_auth->conf.wpa & version)) { + wpa_printf(MSG_DEBUG, "Invalid WPA proto (%d) from " MACSTR, + version, MAC2STR(sm->addr)); + return WPA_INVALID_PROTO; + } + + if (version == WPA_PROTO_RSN) { + res = wpa_parse_wpa_ie_rsn(wpa_ie, wpa_ie_len, &data); + + selector = RSN_AUTH_KEY_MGMT_UNSPEC_802_1X; + if (0) { + } +#ifdef CONFIG_IEEE80211R + else if (data.key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X) + selector = RSN_AUTH_KEY_MGMT_FT_802_1X; + else if (data.key_mgmt & WPA_KEY_MGMT_FT_PSK) + selector = RSN_AUTH_KEY_MGMT_FT_PSK; +#endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_IEEE80211W + else if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256) + selector = RSN_AUTH_KEY_MGMT_802_1X_SHA256; + else if (data.key_mgmt & WPA_KEY_MGMT_PSK_SHA256) + selector = RSN_AUTH_KEY_MGMT_PSK_SHA256; +#endif /* CONFIG_IEEE80211W */ + else 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; + wpa_auth->dot11RSNAAuthenticationSuiteSelected = selector; + + 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; + wpa_auth->dot11RSNAPairwiseCipherSelected = selector; + + 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; + wpa_auth->dot11RSNAGroupCipherSelected = selector; + } 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; + wpa_auth->dot11RSNAAuthenticationSuiteSelected = selector; + + 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; + wpa_auth->dot11RSNAPairwiseCipherSelected = selector; + + 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; + wpa_auth->dot11RSNAGroupCipherSelected = selector; + } + if (res) { + wpa_printf(MSG_DEBUG, "Failed to parse WPA/RSN IE from " + MACSTR " (res=%d)", MAC2STR(sm->addr), res); + wpa_hexdump(MSG_DEBUG, "WPA/RSN IE", wpa_ie, wpa_ie_len); + return WPA_INVALID_IE; + } + + if (data.group_cipher != wpa_auth->conf.wpa_group) { + wpa_printf(MSG_DEBUG, "Invalid WPA group cipher (0x%x) from " + MACSTR, data.group_cipher, MAC2STR(sm->addr)); + return WPA_INVALID_GROUP; + } + + key_mgmt = data.key_mgmt & wpa_auth->conf.wpa_key_mgmt; + if (!key_mgmt) { + wpa_printf(MSG_DEBUG, "Invalid WPA key mgmt (0x%x) from " + MACSTR, data.key_mgmt, MAC2STR(sm->addr)); + return WPA_INVALID_AKMP; + } + if (0) { + } +#ifdef CONFIG_IEEE80211R + else if (key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X) + sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_IEEE8021X; + else if (key_mgmt & WPA_KEY_MGMT_FT_PSK) + sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_PSK; +#endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_IEEE80211W + else if (key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256) + sm->wpa_key_mgmt = WPA_KEY_MGMT_IEEE8021X_SHA256; + else if (key_mgmt & WPA_KEY_MGMT_PSK_SHA256) + sm->wpa_key_mgmt = WPA_KEY_MGMT_PSK_SHA256; +#endif /* CONFIG_IEEE80211W */ + else if (key_mgmt & WPA_KEY_MGMT_IEEE8021X) + sm->wpa_key_mgmt = WPA_KEY_MGMT_IEEE8021X; + else + sm->wpa_key_mgmt = WPA_KEY_MGMT_PSK; + + if (version == WPA_PROTO_RSN) + ciphers = data.pairwise_cipher & wpa_auth->conf.rsn_pairwise; + else + ciphers = data.pairwise_cipher & wpa_auth->conf.wpa_pairwise; + if (!ciphers) { + wpa_printf(MSG_DEBUG, "Invalid %s pairwise cipher (0x%x) " + "from " MACSTR, + version == WPA_PROTO_RSN ? "RSN" : "WPA", + data.pairwise_cipher, MAC2STR(sm->addr)); + return WPA_INVALID_PAIRWISE; + } + +#ifdef CONFIG_IEEE80211W + if (wpa_auth->conf.ieee80211w == WPA_IEEE80211W_REQUIRED) { + if (!(data.capabilities & WPA_CAPABILITY_MFPC)) { + wpa_printf(MSG_DEBUG, "Management frame protection " + "required, but client did not enable it"); + return WPA_MGMT_FRAME_PROTECTION_VIOLATION; + } + + if (ciphers & WPA_CIPHER_TKIP) { + wpa_printf(MSG_DEBUG, "Management frame protection " + "cannot use TKIP"); + return WPA_MGMT_FRAME_PROTECTION_VIOLATION; + } + + if (data.mgmt_group_cipher != WPA_CIPHER_AES_128_CMAC) { + wpa_printf(MSG_DEBUG, "Unsupported management group " + "cipher %d", data.mgmt_group_cipher); + return WPA_INVALID_MGMT_GROUP_CIPHER; + } + } + + if (wpa_auth->conf.ieee80211w == WPA_NO_IEEE80211W || + !(data.capabilities & WPA_CAPABILITY_MFPC)) + sm->mgmt_frame_prot = 0; + else + sm->mgmt_frame_prot = 1; +#endif /* CONFIG_IEEE80211W */ + +#ifdef CONFIG_IEEE80211R + if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) { + if (mdie == NULL || mdie_len < MOBILITY_DOMAIN_ID_LEN + 1) { + wpa_printf(MSG_DEBUG, "RSN: Trying to use FT, but " + "MDIE not included"); + return WPA_INVALID_MDIE; + } + if (os_memcmp(mdie, wpa_auth->conf.mobility_domain, + MOBILITY_DOMAIN_ID_LEN) != 0) { + wpa_hexdump(MSG_DEBUG, "RSN: Attempted to use unknown " + "MDIE", mdie, MOBILITY_DOMAIN_ID_LEN); + return WPA_INVALID_MDIE; + } + } +#endif /* CONFIG_IEEE80211R */ + + if (ciphers & WPA_CIPHER_CCMP) + sm->pairwise = WPA_CIPHER_CCMP; + else + sm->pairwise = WPA_CIPHER_TKIP; + + /* TODO: clear WPA/WPA2 state if STA changes from one to another */ + if (wpa_ie[0] == WLAN_EID_RSN) + sm->wpa = WPA_VERSION_WPA2; + else + sm->wpa = WPA_VERSION_WPA; + + sm->pmksa = NULL; + for (i = 0; i < data.num_pmkid; i++) { + wpa_hexdump(MSG_DEBUG, "RSN IE: STA PMKID", + &data.pmkid[i * PMKID_LEN], PMKID_LEN); + sm->pmksa = pmksa_cache_get(wpa_auth->pmksa, sm->addr, + &data.pmkid[i * PMKID_LEN]); + if (sm->pmksa) { + pmkid = sm->pmksa->pmkid; + break; + } + } + for (i = 0; sm->pmksa == NULL && wpa_auth->conf.okc && + i < data.num_pmkid; i++) { + struct wpa_auth_okc_iter_data idata; + idata.pmksa = NULL; + idata.aa = wpa_auth->addr; + idata.spa = sm->addr; + idata.pmkid = &data.pmkid[i * PMKID_LEN]; + wpa_auth_for_each_auth(wpa_auth, wpa_auth_okc_iter, &idata); + if (idata.pmksa) { + wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG, + "OKC match for PMKID"); + sm->pmksa = pmksa_cache_add_okc(wpa_auth->pmksa, + idata.pmksa, + wpa_auth->addr, + idata.pmkid); + pmkid = idata.pmkid; + break; + } + } + if (sm->pmksa) { + wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG, + "PMKID found from PMKSA cache " + "eap_type=%d vlan_id=%d", + sm->pmksa->eap_type_authsrv, + sm->pmksa->vlan_id); + os_memcpy(wpa_auth->dot11RSNAPMKIDUsed, pmkid, PMKID_LEN); + } + + if (sm->wpa_ie == NULL || sm->wpa_ie_len < wpa_ie_len) { + os_free(sm->wpa_ie); + sm->wpa_ie = os_malloc(wpa_ie_len); + if (sm->wpa_ie == NULL) + return WPA_ALLOC_FAIL; + } + os_memcpy(sm->wpa_ie, wpa_ie, wpa_ie_len); + sm->wpa_ie_len = wpa_ie_len; + + return WPA_IE_OK; +} + + +/** + * wpa_parse_generic - Parse EAPOL-Key Key Data Generic IEs + * @pos: Pointer to the IE header + * @end: Pointer to the end of the Key Data buffer + * @ie: Pointer to parsed IE data + * Returns: 0 on success, 1 if end mark is found, -1 on failure + */ +static int wpa_parse_generic(const u8 *pos, const u8 *end, + struct wpa_eapol_ie_parse *ie) +{ + if (pos[1] == 0) + return 1; + + if (pos[1] >= 6 && + RSN_SELECTOR_GET(pos + 2) == WPA_OUI_TYPE && + pos[2 + WPA_SELECTOR_LEN] == 1 && + pos[2 + WPA_SELECTOR_LEN + 1] == 0) { + ie->wpa_ie = pos; + ie->wpa_ie_len = pos[1] + 2; + return 0; + } + + if (pos + 1 + RSN_SELECTOR_LEN < end && + pos[1] >= RSN_SELECTOR_LEN + PMKID_LEN && + RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_PMKID) { + ie->pmkid = pos + 2 + RSN_SELECTOR_LEN; + return 0; + } + + if (pos[1] > RSN_SELECTOR_LEN + 2 && + RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_GROUPKEY) { + ie->gtk = pos + 2 + RSN_SELECTOR_LEN; + ie->gtk_len = pos[1] - RSN_SELECTOR_LEN; + return 0; + } + + if (pos[1] > RSN_SELECTOR_LEN + 2 && + RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_MAC_ADDR) { + ie->mac_addr = pos + 2 + RSN_SELECTOR_LEN; + ie->mac_addr_len = pos[1] - RSN_SELECTOR_LEN; + return 0; + } + +#ifdef CONFIG_PEERKEY + if (pos[1] > RSN_SELECTOR_LEN + 2 && + RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_SMK) { + ie->smk = pos + 2 + RSN_SELECTOR_LEN; + ie->smk_len = pos[1] - RSN_SELECTOR_LEN; + return 0; + } + + if (pos[1] > RSN_SELECTOR_LEN + 2 && + RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_NONCE) { + ie->nonce = pos + 2 + RSN_SELECTOR_LEN; + ie->nonce_len = pos[1] - RSN_SELECTOR_LEN; + return 0; + } + + if (pos[1] > RSN_SELECTOR_LEN + 2 && + RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_LIFETIME) { + ie->lifetime = pos + 2 + RSN_SELECTOR_LEN; + ie->lifetime_len = pos[1] - RSN_SELECTOR_LEN; + return 0; + } + + if (pos[1] > RSN_SELECTOR_LEN + 2 && + RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_ERROR) { + ie->error = pos + 2 + RSN_SELECTOR_LEN; + ie->error_len = pos[1] - RSN_SELECTOR_LEN; + return 0; + } +#endif /* CONFIG_PEERKEY */ + +#ifdef CONFIG_IEEE80211W + if (pos[1] > RSN_SELECTOR_LEN + 2 && + RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_IGTK) { + ie->igtk = pos + 2 + RSN_SELECTOR_LEN; + ie->igtk_len = pos[1] - RSN_SELECTOR_LEN; + return 0; + } +#endif /* CONFIG_IEEE80211W */ + + return 0; +} + + +/** + * wpa_parse_kde_ies - Parse EAPOL-Key Key Data IEs + * @buf: Pointer to the Key Data buffer + * @len: Key Data Length + * @ie: Pointer to parsed IE data + * Returns: 0 on success, -1 on failure + */ +int wpa_parse_kde_ies(const u8 *buf, size_t len, struct wpa_eapol_ie_parse *ie) +{ + const u8 *pos, *end; + int ret = 0; + + os_memset(ie, 0, sizeof(*ie)); + for (pos = buf, end = pos + len; pos + 1 < end; pos += 2 + pos[1]) { + if (pos[0] == 0xdd && + ((pos == buf + len - 1) || pos[1] == 0)) { + /* Ignore padding */ + break; + } + if (pos + 2 + pos[1] > end) { + wpa_printf(MSG_DEBUG, "WPA: EAPOL-Key Key Data " + "underflow (ie=%d len=%d pos=%d)", + pos[0], pos[1], (int) (pos - buf)); + wpa_hexdump_key(MSG_DEBUG, "WPA: Key Data", + buf, len); + ret = -1; + break; + } + if (*pos == WLAN_EID_RSN) { + ie->rsn_ie = pos; + ie->rsn_ie_len = pos[1] + 2; +#ifdef CONFIG_IEEE80211R + } else if (*pos == WLAN_EID_MOBILITY_DOMAIN) { + ie->mdie = pos; + ie->mdie_len = pos[1] + 2; +#endif /* CONFIG_IEEE80211R */ + } else if (*pos == WLAN_EID_VENDOR_SPECIFIC) { + ret = wpa_parse_generic(pos, end, ie); + if (ret < 0) + break; + if (ret > 0) { + ret = 0; + break; + } + } else { + wpa_hexdump(MSG_DEBUG, "WPA: Unrecognized EAPOL-Key " + "Key Data IE", pos, 2 + pos[1]); + } + } + + return ret; +} + + +int wpa_auth_uses_mfp(struct wpa_state_machine *sm) +{ + return sm ? sm->mgmt_frame_prot : 0; +} diff --git a/contrib/wpa/hostapd/wpa_auth_ie.h b/contrib/wpa/hostapd/wpa_auth_ie.h new file mode 100644 index 0000000..9968d2d --- /dev/null +++ b/contrib/wpa/hostapd/wpa_auth_ie.h @@ -0,0 +1,54 @@ +/* + * hostapd - WPA/RSN IE and KDE definitions + * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef WPA_AUTH_IE_H +#define WPA_AUTH_IE_H + +struct wpa_eapol_ie_parse { + const u8 *wpa_ie; + size_t wpa_ie_len; + const u8 *rsn_ie; + size_t rsn_ie_len; + const u8 *pmkid; + const u8 *gtk; + size_t gtk_len; + const u8 *mac_addr; + size_t mac_addr_len; +#ifdef CONFIG_PEERKEY + const u8 *smk; + size_t smk_len; + const u8 *nonce; + size_t nonce_len; + const u8 *lifetime; + size_t lifetime_len; + const u8 *error; + size_t error_len; +#endif /* CONFIG_PEERKEY */ +#ifdef CONFIG_IEEE80211W + const u8 *igtk; + size_t igtk_len; +#endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_IEEE80211R + const u8 *mdie; + size_t mdie_len; +#endif /* CONFIG_IEEE80211R */ +}; + +int wpa_parse_kde_ies(const u8 *buf, size_t len, + struct wpa_eapol_ie_parse *ie); +u8 * wpa_add_kde(u8 *pos, u32 kde, const u8 *data, size_t data_len, + const u8 *data2, size_t data2_len); +int wpa_auth_gen_wpa_ie(struct wpa_authenticator *wpa_auth); + +#endif /* WPA_AUTH_IE_H */ diff --git a/contrib/wpa/hostapd/wpa_ft.c b/contrib/wpa/hostapd/wpa_ft.c new file mode 100644 index 0000000..9cf6713 --- /dev/null +++ b/contrib/wpa/hostapd/wpa_ft.c @@ -0,0 +1,1499 @@ +/* + * hostapd - IEEE 802.11r - Fast BSS Transition + * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "config.h" +#include "wpa.h" +#include "aes_wrap.h" +#include "ieee802_11.h" +#include "defs.h" +#include "wpa_auth_i.h" +#include "wpa_auth_ie.h" + + +#ifdef CONFIG_IEEE80211R + +static int wpa_ft_rrb_send(struct wpa_authenticator *wpa_auth, const u8 *dst, + const u8 *data, size_t data_len) +{ + if (wpa_auth->cb.send_ether == NULL) + return -1; + return wpa_auth->cb.send_ether(wpa_auth->cb.ctx, dst, ETH_P_RRB, + data, data_len); +} + + +static int wpa_ft_action_send(struct wpa_authenticator *wpa_auth, + const u8 *dst, const u8 *data, size_t data_len) +{ + if (wpa_auth->cb.send_ft_action == NULL) + return -1; + return wpa_auth->cb.send_ft_action(wpa_auth->cb.ctx, dst, + data, data_len); +} + + +static struct wpa_state_machine * +wpa_ft_add_sta(struct wpa_authenticator *wpa_auth, const u8 *sta_addr) +{ + if (wpa_auth->cb.add_sta == NULL) + return NULL; + return wpa_auth->cb.add_sta(wpa_auth->cb.ctx, sta_addr); +} + + +int wpa_write_mdie(struct wpa_auth_config *conf, u8 *buf, size_t len) +{ + u8 *pos = buf; + u8 capab; + if (len < 2 + sizeof(struct rsn_mdie)) + return -1; + + *pos++ = WLAN_EID_MOBILITY_DOMAIN; + *pos++ = MOBILITY_DOMAIN_ID_LEN + 1; + os_memcpy(pos, conf->mobility_domain, MOBILITY_DOMAIN_ID_LEN); + pos += MOBILITY_DOMAIN_ID_LEN; + capab = RSN_FT_CAPAB_FT_OVER_DS; + *pos++ = capab; + + return pos - buf; +} + + +static int wpa_write_ftie(struct wpa_auth_config *conf, const u8 *r0kh_id, + size_t r0kh_id_len, + const u8 *anonce, const u8 *snonce, + u8 *buf, size_t len, const u8 *subelem, + size_t subelem_len) +{ + u8 *pos = buf, *ielen; + struct rsn_ftie *hdr; + + if (len < 2 + sizeof(*hdr) + 2 + FT_R1KH_ID_LEN + 2 + r0kh_id_len + + subelem_len) + return -1; + + *pos++ = WLAN_EID_FAST_BSS_TRANSITION; + ielen = pos++; + + hdr = (struct rsn_ftie *) pos; + os_memset(hdr, 0, sizeof(*hdr)); + pos += sizeof(*hdr); + WPA_PUT_LE16(hdr->mic_control, 0); + if (anonce) + os_memcpy(hdr->anonce, anonce, WPA_NONCE_LEN); + if (snonce) + os_memcpy(hdr->snonce, snonce, WPA_NONCE_LEN); + + /* Optional Parameters */ + *pos++ = FTIE_SUBELEM_R1KH_ID; + *pos++ = FT_R1KH_ID_LEN; + os_memcpy(pos, conf->r1_key_holder, FT_R1KH_ID_LEN); + pos += FT_R1KH_ID_LEN; + + if (r0kh_id) { + *pos++ = FTIE_SUBELEM_R0KH_ID; + *pos++ = r0kh_id_len; + os_memcpy(pos, r0kh_id, r0kh_id_len); + pos += r0kh_id_len; + } + + if (subelem) { + os_memcpy(pos, subelem, subelem_len); + pos += subelem_len; + } + + *ielen = pos - buf - 2; + + return pos - buf; +} + + +struct wpa_ft_pmk_r0_sa { + struct wpa_ft_pmk_r0_sa *next; + u8 pmk_r0[PMK_LEN]; + u8 pmk_r0_name[WPA_PMK_NAME_LEN]; + u8 spa[ETH_ALEN]; + /* TODO: expiration, identity, radius_class, EAP type, VLAN ID */ + int pmk_r1_pushed; +}; + +struct wpa_ft_pmk_r1_sa { + struct wpa_ft_pmk_r1_sa *next; + u8 pmk_r1[PMK_LEN]; + u8 pmk_r1_name[WPA_PMK_NAME_LEN]; + u8 spa[ETH_ALEN]; + /* TODO: expiration, identity, radius_class, EAP type, VLAN ID */ +}; + +struct wpa_ft_pmk_cache { + struct wpa_ft_pmk_r0_sa *pmk_r0; + struct wpa_ft_pmk_r1_sa *pmk_r1; +}; + +struct wpa_ft_pmk_cache * wpa_ft_pmk_cache_init(void) +{ + struct wpa_ft_pmk_cache *cache; + + cache = os_zalloc(sizeof(*cache)); + + return cache; +} + + +void wpa_ft_pmk_cache_deinit(struct wpa_ft_pmk_cache *cache) +{ + struct wpa_ft_pmk_r0_sa *r0, *r0prev; + struct wpa_ft_pmk_r1_sa *r1, *r1prev; + + r0 = cache->pmk_r0; + while (r0) { + r0prev = r0; + r0 = r0->next; + os_memset(r0prev->pmk_r0, 0, PMK_LEN); + os_free(r0prev); + } + + r1 = cache->pmk_r1; + while (r1) { + r1prev = r1; + r1 = r1->next; + os_memset(r1prev->pmk_r1, 0, PMK_LEN); + os_free(r1prev); + } + + os_free(cache); +} + + +static int wpa_ft_store_pmk_r0(struct wpa_authenticator *wpa_auth, + const u8 *spa, const u8 *pmk_r0, + const u8 *pmk_r0_name) +{ + struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache; + struct wpa_ft_pmk_r0_sa *r0; + + /* TODO: add expiration and limit on number of entries in cache */ + + r0 = os_zalloc(sizeof(*r0)); + if (r0 == NULL) + return -1; + + os_memcpy(r0->pmk_r0, pmk_r0, PMK_LEN); + os_memcpy(r0->pmk_r0_name, pmk_r0_name, WPA_PMK_NAME_LEN); + os_memcpy(r0->spa, spa, ETH_ALEN); + + r0->next = cache->pmk_r0; + cache->pmk_r0 = r0; + + return 0; +} + + +static int wpa_ft_fetch_pmk_r0(struct wpa_authenticator *wpa_auth, + const u8 *spa, const u8 *pmk_r0_name, + u8 *pmk_r0) +{ + struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache; + struct wpa_ft_pmk_r0_sa *r0; + + r0 = cache->pmk_r0; + while (r0) { + if (os_memcmp(r0->spa, spa, ETH_ALEN) == 0 && + os_memcmp(r0->pmk_r0_name, pmk_r0_name, WPA_PMK_NAME_LEN) + == 0) { + os_memcpy(pmk_r0, r0->pmk_r0, PMK_LEN); + return 0; + } + + r0 = r0->next; + } + + return -1; +} + + +static int wpa_ft_store_pmk_r1(struct wpa_authenticator *wpa_auth, + const u8 *spa, const u8 *pmk_r1, + const u8 *pmk_r1_name) +{ + struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache; + struct wpa_ft_pmk_r1_sa *r1; + + /* TODO: add expiration and limit on number of entries in cache */ + + r1 = os_zalloc(sizeof(*r1)); + if (r1 == NULL) + return -1; + + os_memcpy(r1->pmk_r1, pmk_r1, PMK_LEN); + os_memcpy(r1->pmk_r1_name, pmk_r1_name, WPA_PMK_NAME_LEN); + os_memcpy(r1->spa, spa, ETH_ALEN); + + r1->next = cache->pmk_r1; + cache->pmk_r1 = r1; + + return 0; +} + + +static int wpa_ft_fetch_pmk_r1(struct wpa_authenticator *wpa_auth, + const u8 *spa, const u8 *pmk_r1_name, + u8 *pmk_r1) +{ + struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache; + struct wpa_ft_pmk_r1_sa *r1; + + r1 = cache->pmk_r1; + while (r1) { + if (os_memcmp(r1->spa, spa, ETH_ALEN) == 0 && + os_memcmp(r1->pmk_r1_name, pmk_r1_name, WPA_PMK_NAME_LEN) + == 0) { + os_memcpy(pmk_r1, r1->pmk_r1, PMK_LEN); + return 0; + } + + r1 = r1->next; + } + + return -1; +} + + +static int wpa_ft_pull_pmk_r1(struct wpa_authenticator *wpa_auth, + const u8 *s1kh_id, const u8 *r0kh_id, + size_t r0kh_id_len, const u8 *pmk_r0_name) +{ + struct ft_remote_r0kh *r0kh; + struct ft_r0kh_r1kh_pull_frame frame, f; + + r0kh = wpa_auth->conf.r0kh_list; + while (r0kh) { + if (r0kh->id_len == r0kh_id_len && + os_memcmp(r0kh->id, r0kh_id, r0kh_id_len) == 0) + break; + r0kh = r0kh->next; + } + if (r0kh == NULL) + return -1; + + wpa_printf(MSG_DEBUG, "FT: Send PMK-R1 pull request to remote R0KH " + "address " MACSTR, MAC2STR(r0kh->addr)); + + os_memset(&frame, 0, sizeof(frame)); + frame.frame_type = RSN_REMOTE_FRAME_TYPE_FT_RRB; + frame.packet_type = FT_PACKET_R0KH_R1KH_PULL; + frame.data_length = host_to_le16(FT_R0KH_R1KH_PULL_DATA_LEN); + os_memcpy(frame.ap_address, wpa_auth->addr, ETH_ALEN); + + /* aes_wrap() does not support inplace encryption, so use a temporary + * buffer for the data. */ + if (os_get_random(f.nonce, sizeof(f.nonce))) { + wpa_printf(MSG_DEBUG, "FT: Failed to get random data for " + "nonce"); + return -1; + } + os_memcpy(f.pmk_r0_name, pmk_r0_name, WPA_PMK_NAME_LEN); + os_memcpy(f.r1kh_id, wpa_auth->conf.r1_key_holder, FT_R1KH_ID_LEN); + os_memcpy(f.s1kh_id, s1kh_id, ETH_ALEN); + + if (aes_wrap(r0kh->key, (FT_R0KH_R1KH_PULL_DATA_LEN + 7) / 8, + f.nonce, frame.nonce) < 0) + return -1; + + wpa_ft_rrb_send(wpa_auth, r0kh->addr, (u8 *) &frame, sizeof(frame)); + + return 0; +} + + +int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, const u8 *pmk, + struct wpa_ptk *ptk) +{ + u8 pmk_r0[PMK_LEN], pmk_r0_name[WPA_PMK_NAME_LEN]; + u8 pmk_r1[PMK_LEN], pmk_r1_name[WPA_PMK_NAME_LEN]; + u8 ptk_name[WPA_PMK_NAME_LEN]; + const u8 *mdid = sm->wpa_auth->conf.mobility_domain; + const u8 *r0kh = sm->wpa_auth->conf.r0_key_holder; + size_t r0kh_len = sm->wpa_auth->conf.r0_key_holder_len; + const u8 *r1kh = sm->wpa_auth->conf.r1_key_holder; + const u8 *ssid = sm->wpa_auth->conf.ssid; + size_t ssid_len = sm->wpa_auth->conf.ssid_len; + + + if (sm->xxkey_len == 0) { + wpa_printf(MSG_DEBUG, "FT: XXKey not available for key " + "derivation"); + return -1; + } + + wpa_derive_pmk_r0(sm->xxkey, sm->xxkey_len, ssid, ssid_len, mdid, + r0kh, r0kh_len, sm->addr, pmk_r0, pmk_r0_name); + wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R0", pmk_r0, PMK_LEN); + wpa_hexdump(MSG_DEBUG, "FT: PMKR0Name", pmk_r0_name, WPA_PMK_NAME_LEN); + wpa_ft_store_pmk_r0(sm->wpa_auth, sm->addr, pmk_r0, pmk_r0_name); + + wpa_derive_pmk_r1(pmk_r0, pmk_r0_name, r1kh, sm->addr, + pmk_r1, pmk_r1_name); + wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", pmk_r1, PMK_LEN); + wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", pmk_r1_name, WPA_PMK_NAME_LEN); + wpa_ft_store_pmk_r1(sm->wpa_auth, sm->addr, pmk_r1, pmk_r1_name); + + wpa_pmk_r1_to_ptk(pmk_r1, sm->SNonce, sm->ANonce, sm->addr, + sm->wpa_auth->addr, pmk_r1_name, + (u8 *) ptk, sizeof(*ptk), ptk_name); + wpa_hexdump_key(MSG_DEBUG, "FT: PTK", (u8 *) ptk, sizeof(*ptk)); + wpa_hexdump(MSG_DEBUG, "FT: PTKName", ptk_name, WPA_PMK_NAME_LEN); + + return 0; +} + + +static inline int wpa_auth_get_seqnum(struct wpa_authenticator *wpa_auth, + const u8 *addr, int idx, u8 *seq) +{ + if (wpa_auth->cb.get_seqnum == NULL) + return -1; + return wpa_auth->cb.get_seqnum(wpa_auth->cb.ctx, addr, idx, seq); +} + + +#ifdef CONFIG_IEEE80211W +static inline int wpa_auth_get_seqnum_igtk(struct wpa_authenticator *wpa_auth, + const u8 *addr, int idx, u8 *seq) +{ + if (wpa_auth->cb.get_seqnum_igtk == NULL) + return -1; + return wpa_auth->cb.get_seqnum_igtk(wpa_auth->cb.ctx, addr, idx, seq); +} +#endif /* CONFIG_IEEE80211W */ + + +static u8 * wpa_ft_gtk_subelem(struct wpa_state_machine *sm, size_t *len) +{ + u8 *subelem; + struct wpa_group *gsm = sm->group; + size_t subelem_len, pad_len; + const u8 *key; + size_t key_len; + u8 keybuf[32]; + + key_len = gsm->GTK_len; + if (key_len > sizeof(keybuf)) + return NULL; + + /* + * Pad key for AES Key Wrap if it is not multiple of 8 bytes or is less + * than 16 bytes. + */ + pad_len = key_len % 8; + if (pad_len) + pad_len = 8 - pad_len; + if (key_len + pad_len < 16) + pad_len += 8; + if (pad_len) { + os_memcpy(keybuf, gsm->GTK[gsm->GN - 1], key_len); + os_memset(keybuf + key_len, 0, pad_len); + keybuf[key_len] = 0xdd; + key_len += pad_len; + key = keybuf; + } else + key = gsm->GTK[gsm->GN - 1]; + + /* + * Sub-elem ID[1] | Length[1] | Key Info[1] | Key Length[1] | RSC[8] | + * Key[5..32]. + */ + subelem_len = 12 + key_len + 8; + subelem = os_zalloc(subelem_len); + if (subelem == NULL) + return NULL; + + subelem[0] = FTIE_SUBELEM_GTK; + subelem[1] = 10 + key_len + 8; + subelem[2] = gsm->GN & 0x03; /* Key ID in B0-B1 of Key Info */ + subelem[3] = gsm->GTK_len; + wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN, subelem + 4); + if (aes_wrap(sm->PTK.kek, key_len / 8, key, subelem + 12)) { + os_free(subelem); + return NULL; + } + + *len = subelem_len; + return subelem; +} + + +#ifdef CONFIG_IEEE80211W +static u8 * wpa_ft_igtk_subelem(struct wpa_state_machine *sm, size_t *len) +{ + u8 *subelem, *pos; + struct wpa_group *gsm = sm->group; + size_t subelem_len; + + /* Sub-elem ID[1] | Length[1] | KeyID[2] | IPN[6] | Key Length[1] | + * Key[16+8] */ + subelem_len = 1 + 1 + 2 + 6 + 1 + WPA_IGTK_LEN + 8; + subelem = os_zalloc(subelem_len); + if (subelem == NULL) + return NULL; + + pos = subelem; + *pos++ = FTIE_SUBELEM_IGTK; + *pos++ = subelem_len - 2; + WPA_PUT_LE16(pos, gsm->GN_igtk); + pos += 2; + wpa_auth_get_seqnum_igtk(sm->wpa_auth, NULL, gsm->GN_igtk, pos); + pos += 6; + *pos++ = WPA_IGTK_LEN; + if (aes_wrap(sm->PTK.kek, WPA_IGTK_LEN / 8, + gsm->IGTK[gsm->GN_igtk - 4], pos)) { + os_free(subelem); + return NULL; + } + + *len = subelem_len; + return subelem; +} +#endif /* CONFIG_IEEE80211W */ + + +u8 * wpa_sm_write_assoc_resp_ies(struct wpa_state_machine *sm, u8 *pos, + size_t max_len, int auth_alg) +{ + u8 *end, *mdie, *ftie, *rsnie, *r0kh_id, *subelem = NULL; + size_t mdie_len, ftie_len, rsnie_len, r0kh_id_len, subelem_len = 0; + int res; + struct wpa_auth_config *conf; + struct rsn_ftie *_ftie; + + if (sm == NULL) + return pos; + + conf = &sm->wpa_auth->conf; + + if (sm->wpa_key_mgmt != WPA_KEY_MGMT_FT_IEEE8021X && + sm->wpa_key_mgmt != WPA_KEY_MGMT_FT_PSK) + return pos; + + end = pos + max_len; + + /* RSN */ + res = wpa_write_rsn_ie(conf, pos, end - pos, sm->pmk_r1_name); + if (res < 0) + return pos; + rsnie = pos; + rsnie_len = res; + pos += res; + + /* Mobility Domain Information */ + res = wpa_write_mdie(conf, pos, end - pos); + if (res < 0) + return pos; + mdie = pos; + mdie_len = res; + pos += res; + + /* Fast BSS Transition Information */ + if (auth_alg == WLAN_AUTH_FT) { + subelem = wpa_ft_gtk_subelem(sm, &subelem_len); + r0kh_id = sm->r0kh_id; + r0kh_id_len = sm->r0kh_id_len; +#ifdef CONFIG_IEEE80211W + if (sm->mgmt_frame_prot) { + u8 *igtk; + size_t igtk_len; + u8 *nbuf; + igtk = wpa_ft_igtk_subelem(sm, &igtk_len); + if (igtk == NULL) { + os_free(subelem); + return pos; + } + nbuf = os_realloc(subelem, subelem_len + igtk_len); + if (nbuf == NULL) { + os_free(subelem); + os_free(igtk); + return pos; + } + subelem = nbuf; + os_memcpy(subelem + subelem_len, igtk, igtk_len); + subelem_len += igtk_len; + os_free(igtk); + } +#endif /* CONFIG_IEEE80211W */ + } else { + r0kh_id = conf->r0_key_holder; + r0kh_id_len = conf->r0_key_holder_len; + } + res = wpa_write_ftie(conf, r0kh_id, r0kh_id_len, NULL, NULL, pos, + end - pos, subelem, subelem_len); + os_free(subelem); + if (res < 0) + return pos; + ftie = pos; + ftie_len = res; + pos += res; + + _ftie = (struct rsn_ftie *) (ftie + 2); + _ftie->mic_control[1] = 3; /* Information element count */ + if (wpa_ft_mic(sm->PTK.kck, sm->addr, sm->wpa_auth->addr, 6, + mdie, mdie_len, ftie, ftie_len, + rsnie, rsnie_len, NULL, 0, _ftie->mic) < 0) + wpa_printf(MSG_DEBUG, "FT: Failed to calculate MIC"); + + return pos; +} + + +struct wpa_ft_ies { + const u8 *mdie; + size_t mdie_len; + const u8 *ftie; + size_t ftie_len; + const u8 *r1kh_id; + const u8 *gtk; + size_t gtk_len; + const u8 *r0kh_id; + size_t r0kh_id_len; + const u8 *rsn; + size_t rsn_len; + const u8 *rsn_pmkid; +}; + + +static int wpa_ft_parse_ftie(const u8 *ie, size_t ie_len, + struct wpa_ft_ies *parse) +{ + const u8 *end, *pos; + + parse->ftie = ie; + parse->ftie_len = ie_len; + + pos = ie + sizeof(struct rsn_ftie); + end = ie + ie_len; + + while (pos + 2 <= end && pos + 2 + pos[1] <= end) { + switch (pos[0]) { + case FTIE_SUBELEM_R1KH_ID: + if (pos[1] != FT_R1KH_ID_LEN) { + wpa_printf(MSG_DEBUG, "FT: Invalid R1KH-ID " + "length in FTIE: %d", pos[1]); + return -1; + } + parse->r1kh_id = pos + 2; + break; + case FTIE_SUBELEM_GTK: + parse->gtk = pos + 2; + parse->gtk_len = pos[1]; + break; + case FTIE_SUBELEM_R0KH_ID: + if (pos[1] < 1 || pos[1] > FT_R0KH_ID_MAX_LEN) { + wpa_printf(MSG_DEBUG, "FT: Invalid R0KH-ID " + "length in FTIE: %d", pos[1]); + return -1; + } + parse->r0kh_id = pos + 2; + parse->r0kh_id_len = pos[1]; + break; + } + + pos += 2 + pos[1]; + } + + return 0; +} + + +static int wpa_ft_parse_ies(const u8 *ies, size_t ies_len, + struct wpa_ft_ies *parse) +{ + const u8 *end, *pos; + struct wpa_ie_data data; + int ret; + + os_memset(parse, 0, sizeof(*parse)); + if (ies == NULL) + return 0; + + pos = ies; + end = ies + ies_len; + while (pos + 2 <= end && pos + 2 + pos[1] <= end) { + switch (pos[0]) { + case WLAN_EID_RSN: + parse->rsn = pos + 2; + parse->rsn_len = pos[1]; + ret = wpa_parse_wpa_ie_rsn(parse->rsn - 2, + parse->rsn_len + 2, + &data); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "FT: Failed to parse " + "RSN IE: %d", ret); + return -1; + } + if (data.num_pmkid == 1 && data.pmkid) + parse->rsn_pmkid = data.pmkid; + break; + case WLAN_EID_MOBILITY_DOMAIN: + parse->mdie = pos + 2; + parse->mdie_len = pos[1]; + break; + case WLAN_EID_FAST_BSS_TRANSITION: + if (wpa_ft_parse_ftie(pos + 2, pos[1], parse) < 0) + return -1; + break; + } + + pos += 2 + pos[1]; + } + + return 0; +} + + +static inline int wpa_auth_set_key(struct wpa_authenticator *wpa_auth, + int vlan_id, + const char *alg, const u8 *addr, int idx, + u8 *key, size_t key_len) +{ + if (wpa_auth->cb.set_key == NULL) + return -1; + return wpa_auth->cb.set_key(wpa_auth->cb.ctx, vlan_id, alg, addr, idx, + key, key_len); +} + + +static void wpa_ft_install_ptk(struct wpa_state_machine *sm) +{ + char *alg; + int klen; + + /* MLME-SETKEYS.request(PTK) */ + if (sm->pairwise == WPA_CIPHER_TKIP) { + alg = "TKIP"; + klen = 32; + } else if (sm->pairwise == WPA_CIPHER_CCMP) { + alg = "CCMP"; + klen = 16; + } else + return; + + /* FIX: add STA entry to kernel/driver here? The set_key will fail + * most likely without this.. At the moment, STA entry is added only + * after association has been completed. Alternatively, could + * re-configure PTK at that point(?). + */ + if (wpa_auth_set_key(sm->wpa_auth, 0, alg, sm->addr, 0, + sm->PTK.tk1, klen)) + return; + + /* FIX: MLME-SetProtection.Request(TA, Tx_Rx) */ + sm->pairwise_set = TRUE; +} + + +static u16 wpa_ft_process_auth_req(struct wpa_state_machine *sm, + const u8 *ies, size_t ies_len, + u8 **resp_ies, size_t *resp_ies_len) +{ + struct rsn_mdie *mdie; + struct rsn_ftie *ftie; + u8 pmk_r1[PMK_LEN], pmk_r1_name[WPA_PMK_NAME_LEN]; + u8 ptk_name[WPA_PMK_NAME_LEN]; + struct wpa_auth_config *conf; + struct wpa_ft_ies parse; + size_t buflen; + int ret; + u8 *pos, *end; + + *resp_ies = NULL; + *resp_ies_len = 0; + + sm->pmk_r1_name_valid = 0; + conf = &sm->wpa_auth->conf; + + wpa_hexdump(MSG_DEBUG, "FT: Received authentication frame IEs", + ies, ies_len); + + if (wpa_ft_parse_ies(ies, ies_len, &parse) < 0) { + wpa_printf(MSG_DEBUG, "FT: Failed to parse FT IEs"); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + mdie = (struct rsn_mdie *) parse.mdie; + if (mdie == NULL || parse.mdie_len < sizeof(*mdie) || + os_memcmp(mdie->mobility_domain, + sm->wpa_auth->conf.mobility_domain, + MOBILITY_DOMAIN_ID_LEN) != 0) { + wpa_printf(MSG_DEBUG, "FT: Invalid MDIE"); + return WLAN_STATUS_INVALID_MDIE; + } + + ftie = (struct rsn_ftie *) parse.ftie; + if (ftie == NULL || parse.ftie_len < sizeof(*ftie)) { + wpa_printf(MSG_DEBUG, "FT: Invalid FTIE"); + return WLAN_STATUS_INVALID_FTIE; + } + + os_memcpy(sm->SNonce, ftie->snonce, WPA_NONCE_LEN); + + if (parse.r0kh_id == NULL) { + wpa_printf(MSG_DEBUG, "FT: Invalid FTIE - no R0KH-ID"); + return WLAN_STATUS_INVALID_FTIE; + } + + wpa_hexdump(MSG_DEBUG, "FT: STA R0KH-ID", + parse.r0kh_id, parse.r0kh_id_len); + os_memcpy(sm->r0kh_id, parse.r0kh_id, parse.r0kh_id_len); + sm->r0kh_id_len = parse.r0kh_id_len; + + if (parse.rsn_pmkid == NULL) { + wpa_printf(MSG_DEBUG, "FT: No PMKID in RSNIE"); + return WLAN_STATUS_INVALID_PMKID; + } + + wpa_hexdump(MSG_DEBUG, "FT: Requested PMKR0Name", + parse.rsn_pmkid, WPA_PMK_NAME_LEN); + wpa_derive_pmk_r1_name(parse.rsn_pmkid, + sm->wpa_auth->conf.r1_key_holder, sm->addr, + pmk_r1_name); + wpa_hexdump(MSG_DEBUG, "FT: Derived requested PMKR1Name", + pmk_r1_name, WPA_PMK_NAME_LEN); + + if (wpa_ft_fetch_pmk_r1(sm->wpa_auth, sm->addr, pmk_r1_name, pmk_r1) < + 0) { + if (wpa_ft_pull_pmk_r1(sm->wpa_auth, sm->addr, sm->r0kh_id, + sm->r0kh_id_len, parse.rsn_pmkid) < 0) { + wpa_printf(MSG_DEBUG, "FT: Did not have matching " + "PMK-R1 and unknown R0KH-ID"); + return WLAN_STATUS_INVALID_PMKID; + } + + /* + * TODO: Should return "status pending" (and the caller should + * not send out response now). The real response will be sent + * once the response from R0KH is received. + */ + return WLAN_STATUS_INVALID_PMKID; + } + + wpa_hexdump_key(MSG_DEBUG, "FT: Selected PMK-R1", pmk_r1, PMK_LEN); + sm->pmk_r1_name_valid = 1; + os_memcpy(sm->pmk_r1_name, pmk_r1_name, WPA_PMK_NAME_LEN); + + if (os_get_random(sm->ANonce, WPA_NONCE_LEN)) { + wpa_printf(MSG_DEBUG, "FT: Failed to get random data for " + "ANonce"); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + wpa_hexdump(MSG_DEBUG, "FT: Received SNonce", + sm->SNonce, WPA_NONCE_LEN); + wpa_hexdump(MSG_DEBUG, "FT: Generated ANonce", + sm->ANonce, WPA_NONCE_LEN); + + wpa_pmk_r1_to_ptk(pmk_r1, sm->SNonce, sm->ANonce, sm->addr, + sm->wpa_auth->addr, pmk_r1_name, + (u8 *) &sm->PTK, sizeof(sm->PTK), ptk_name); + wpa_hexdump_key(MSG_DEBUG, "FT: PTK", + (u8 *) &sm->PTK, sizeof(sm->PTK)); + wpa_hexdump(MSG_DEBUG, "FT: PTKName", ptk_name, WPA_PMK_NAME_LEN); + + wpa_ft_install_ptk(sm); + + buflen = 2 + sizeof(struct rsn_mdie) + 2 + sizeof(struct rsn_ftie) + + 2 + FT_R1KH_ID_LEN + 200; + *resp_ies = os_zalloc(buflen); + if (*resp_ies == NULL) { + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + pos = *resp_ies; + end = *resp_ies + buflen; + + ret = wpa_write_rsn_ie(conf, pos, end - pos, parse.rsn_pmkid); + if (ret < 0) { + os_free(*resp_ies); + *resp_ies = NULL; + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + pos += ret; + + ret = wpa_write_mdie(conf, pos, end - pos); + if (ret < 0) { + os_free(*resp_ies); + *resp_ies = NULL; + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + pos += ret; + + ret = wpa_write_ftie(conf, parse.r0kh_id, parse.r0kh_id_len, + sm->ANonce, sm->SNonce, pos, end - pos, NULL, 0); + if (ret < 0) { + os_free(*resp_ies); + *resp_ies = NULL; + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + pos += ret; + + *resp_ies_len = pos - *resp_ies; + + return WLAN_STATUS_SUCCESS; +} + + +void wpa_ft_process_auth(struct wpa_state_machine *sm, const u8 *bssid, + u16 auth_transaction, const u8 *ies, size_t ies_len, + void (*cb)(void *ctx, const u8 *dst, const u8 *bssid, + u16 auth_transaction, u16 status, + const u8 *ies, size_t ies_len), + void *ctx) +{ + u16 status; + u8 *resp_ies; + size_t resp_ies_len; + + if (sm == NULL) { + wpa_printf(MSG_DEBUG, "FT: Received authentication frame, but " + "WPA SM not available"); + return; + } + + wpa_printf(MSG_DEBUG, "FT: Received authentication frame: STA=" MACSTR + " BSSID=" MACSTR " transaction=%d", + MAC2STR(sm->addr), MAC2STR(bssid), auth_transaction); + status = wpa_ft_process_auth_req(sm, ies, ies_len, &resp_ies, + &resp_ies_len); + + wpa_printf(MSG_DEBUG, "FT: FT authentication response: dst=" MACSTR + " auth_transaction=%d status=%d", + MAC2STR(sm->addr), auth_transaction + 1, status); + wpa_hexdump(MSG_DEBUG, "FT: Response IEs", resp_ies, resp_ies_len); + cb(ctx, sm->addr, bssid, auth_transaction + 1, status, + resp_ies, resp_ies_len); + os_free(resp_ies); +} + + +u16 wpa_ft_validate_reassoc(struct wpa_state_machine *sm, const u8 *ies, + size_t ies_len) +{ + struct wpa_ft_ies parse; + struct rsn_mdie *mdie; + struct rsn_ftie *ftie; + u8 mic[16]; + + if (sm == NULL) + return WLAN_STATUS_UNSPECIFIED_FAILURE; + + wpa_hexdump(MSG_DEBUG, "FT: Reassoc Req IEs", ies, ies_len); + + if (wpa_ft_parse_ies(ies, ies_len, &parse) < 0) { + wpa_printf(MSG_DEBUG, "FT: Failed to parse FT IEs"); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + if (parse.rsn == NULL) { + wpa_printf(MSG_DEBUG, "FT: No RSNIE in Reassoc Req"); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + if (parse.rsn_pmkid == NULL) { + wpa_printf(MSG_DEBUG, "FT: No PMKID in RSNIE"); + return WLAN_STATUS_INVALID_PMKID; + } + + if (os_memcmp(parse.rsn_pmkid, sm->pmk_r1_name, WPA_PMK_NAME_LEN) != 0) + { + wpa_printf(MSG_DEBUG, "FT: PMKID in Reassoc Req did not match " + "with the PMKR1Name derived from auth request"); + return WLAN_STATUS_INVALID_PMKID; + } + + mdie = (struct rsn_mdie *) parse.mdie; + if (mdie == NULL || parse.mdie_len < sizeof(*mdie) || + os_memcmp(mdie->mobility_domain, + sm->wpa_auth->conf.mobility_domain, + MOBILITY_DOMAIN_ID_LEN) != 0) { + wpa_printf(MSG_DEBUG, "FT: Invalid MDIE"); + return WLAN_STATUS_INVALID_MDIE; + } + + ftie = (struct rsn_ftie *) parse.ftie; + if (ftie == NULL || parse.ftie_len < sizeof(*ftie)) { + wpa_printf(MSG_DEBUG, "FT: Invalid FTIE"); + return WLAN_STATUS_INVALID_FTIE; + } + + /* + * Assume that MDIE, FTIE, and RSN IE are protected and that there is + * no RIC, so total of 3 protected IEs. + */ + if (ftie->mic_control[1] != 3) { + wpa_printf(MSG_DEBUG, "FT: Unexpected IE count in FTIE (%d)", + ftie->mic_control[1]); + return WLAN_STATUS_INVALID_FTIE; + } + + if (wpa_ft_mic(sm->PTK.kck, sm->addr, sm->wpa_auth->addr, 5, + parse.mdie - 2, parse.mdie_len + 2, + parse.ftie - 2, parse.ftie_len + 2, + parse.rsn - 2, parse.rsn_len + 2, NULL, 0, + mic) < 0) { + wpa_printf(MSG_DEBUG, "FT: Failed to calculate MIC"); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + if (os_memcmp(mic, ftie->mic, 16) != 0) { + wpa_printf(MSG_DEBUG, "FT: Invalid MIC in FTIE"); + wpa_hexdump(MSG_MSGDUMP, "FT: Received MIC", ftie->mic, 16); + wpa_hexdump(MSG_MSGDUMP, "FT: Calculated MIC", mic, 16); + return WLAN_STATUS_INVALID_FTIE; + } + + return WLAN_STATUS_SUCCESS; +} + + +int wpa_ft_action_rx(struct wpa_state_machine *sm, const u8 *data, size_t len) +{ + const u8 *sta_addr, *target_ap; + const u8 *ies; + size_t ies_len; + u8 action; + struct ft_rrb_frame *frame; + + if (sm == NULL) + return -1; + + /* + * data: Category[1] Action[1] STA_Address[6] Target_AP_Address[6] + * FT Request action frame body[variable] + */ + + if (len < 14) { + wpa_printf(MSG_DEBUG, "FT: Too short FT Action frame " + "(len=%lu)", (unsigned long) len); + return -1; + } + + action = data[1]; + sta_addr = data + 2; + target_ap = data + 8; + ies = data + 14; + ies_len = len - 14; + + wpa_printf(MSG_DEBUG, "FT: Received FT Action frame (STA=" MACSTR + " Target AP=" MACSTR " Action=%d)", + MAC2STR(sta_addr), MAC2STR(target_ap), action); + + if (os_memcmp(sta_addr, sm->addr, ETH_ALEN) != 0) { + wpa_printf(MSG_DEBUG, "FT: Mismatch in FT Action STA address: " + "STA=" MACSTR " STA-Address=" MACSTR, + MAC2STR(sm->addr), MAC2STR(sta_addr)); + return -1; + } + + /* + * Do some sanity checking on the target AP address (not own and not + * broadcast. This could be extended to filter based on a list of known + * APs in the MD (if such a list were configured). + */ + if ((target_ap[0] & 0x01) || + os_memcmp(target_ap, sm->wpa_auth->addr, ETH_ALEN) == 0) { + wpa_printf(MSG_DEBUG, "FT: Invalid Target AP in FT Action " + "frame"); + return -1; + } + + wpa_hexdump(MSG_MSGDUMP, "FT: Action frame body", ies, ies_len); + + /* RRB - Forward action frame to the target AP */ + frame = os_malloc(sizeof(*frame) + len); + frame->frame_type = RSN_REMOTE_FRAME_TYPE_FT_RRB; + frame->packet_type = FT_PACKET_REQUEST; + frame->action_length = host_to_le16(len); + os_memcpy(frame->ap_address, sm->wpa_auth->addr, ETH_ALEN); + os_memcpy(frame + 1, data, len); + + wpa_ft_rrb_send(sm->wpa_auth, target_ap, (u8 *) frame, + sizeof(*frame) + len); + os_free(frame); + + return 0; +} + + +static int wpa_ft_rrb_rx_request(struct wpa_authenticator *wpa_auth, + const u8 *current_ap, const u8 *sta_addr, + const u8 *body, size_t len) +{ + struct wpa_state_machine *sm; + u16 status; + u8 *resp_ies, *pos; + size_t resp_ies_len, rlen; + struct ft_rrb_frame *frame; + + sm = wpa_ft_add_sta(wpa_auth, sta_addr); + if (sm == NULL) { + wpa_printf(MSG_DEBUG, "FT: Failed to add new STA based on " + "RRB Request"); + return -1; + } + + wpa_hexdump(MSG_MSGDUMP, "FT: RRB Request Frame body", body, len); + + status = wpa_ft_process_auth_req(sm, body, len, &resp_ies, + &resp_ies_len); + + wpa_printf(MSG_DEBUG, "FT: RRB authentication response: STA=" MACSTR + " CurrentAP=" MACSTR " status=%d", + MAC2STR(sm->addr), MAC2STR(current_ap), status); + wpa_hexdump(MSG_DEBUG, "FT: Response IEs", resp_ies, resp_ies_len); + + /* RRB - Forward action frame response to the Current AP */ + + /* + * data: Category[1] Action[1] STA_Address[6] Target_AP_Address[6] + * Status_Code[2] FT Request action frame body[variable] + */ + rlen = 2 + 2 * ETH_ALEN + 2 + resp_ies_len; + + frame = os_malloc(sizeof(*frame) + rlen); + frame->frame_type = RSN_REMOTE_FRAME_TYPE_FT_RRB; + frame->packet_type = FT_PACKET_RESPONSE; + frame->action_length = host_to_le16(rlen); + os_memcpy(frame->ap_address, wpa_auth->addr, ETH_ALEN); + pos = (u8 *) (frame + 1); + *pos++ = WLAN_ACTION_FT; + *pos++ = 2; /* Action: Response */ + os_memcpy(pos, sta_addr, ETH_ALEN); + pos += ETH_ALEN; + os_memcpy(pos, wpa_auth->addr, ETH_ALEN); + pos += ETH_ALEN; + WPA_PUT_LE16(pos, status); + pos += 2; + if (resp_ies) { + os_memcpy(pos, resp_ies, resp_ies_len); + os_free(resp_ies); + } + + wpa_ft_rrb_send(wpa_auth, current_ap, (u8 *) frame, + sizeof(*frame) + rlen); + os_free(frame); + + return 0; +} + + +static int wpa_ft_rrb_rx_pull(struct wpa_authenticator *wpa_auth, + const u8 *src_addr, + const u8 *data, size_t data_len) +{ + struct ft_r0kh_r1kh_pull_frame *frame, f; + struct ft_remote_r1kh *r1kh; + struct ft_r0kh_r1kh_resp_frame resp, r; + u8 pmk_r0[PMK_LEN]; + + wpa_printf(MSG_DEBUG, "FT: Received PMK-R1 pull"); + + if (data_len < sizeof(*frame)) + return -1; + + r1kh = wpa_auth->conf.r1kh_list; + while (r1kh) { + if (os_memcmp(r1kh->addr, src_addr, ETH_ALEN) == 0) + break; + r1kh = r1kh->next; + } + if (r1kh == NULL) { + wpa_printf(MSG_DEBUG, "FT: No matching R1KH address found for " + "PMK-R1 pull source address " MACSTR, + MAC2STR(src_addr)); + return -1; + } + + frame = (struct ft_r0kh_r1kh_pull_frame *) data; + /* aes_unwrap() does not support inplace decryption, so use a temporary + * buffer for the data. */ + if (aes_unwrap(r1kh->key, (FT_R0KH_R1KH_PULL_DATA_LEN + 7) / 8, + frame->nonce, f.nonce) < 0) { + wpa_printf(MSG_DEBUG, "FT: Failed to decrypt PMK-R1 pull " + "request from " MACSTR, MAC2STR(src_addr)); + return -1; + } + + wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 pull - nonce", + f.nonce, sizeof(f.nonce)); + wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 pull - PMKR0Name", + f.pmk_r0_name, WPA_PMK_NAME_LEN); + wpa_printf(MSG_DEBUG, "FT: PMK-R1 pull - R1KH-ID=" MACSTR "S1KH-ID=" + MACSTR, MAC2STR(f.r1kh_id), MAC2STR(f.s1kh_id)); + + os_memset(&resp, 0, sizeof(resp)); + resp.frame_type = RSN_REMOTE_FRAME_TYPE_FT_RRB; + resp.packet_type = FT_PACKET_R0KH_R1KH_RESP; + resp.data_length = host_to_le16(FT_R0KH_R1KH_RESP_DATA_LEN); + os_memcpy(resp.ap_address, wpa_auth->addr, ETH_ALEN); + + /* aes_wrap() does not support inplace encryption, so use a temporary + * buffer for the data. */ + os_memcpy(r.nonce, f.nonce, sizeof(f.nonce)); + os_memcpy(r.r1kh_id, f.r1kh_id, FT_R1KH_ID_LEN); + os_memcpy(r.s1kh_id, f.s1kh_id, ETH_ALEN); + if (wpa_ft_fetch_pmk_r0(wpa_auth, f.s1kh_id, f.pmk_r0_name, pmk_r0) < + 0) { + wpa_printf(MSG_DEBUG, "FT: No matching PMKR0Name found for " + "PMK-R1 pull"); + return -1; + } + + wpa_derive_pmk_r1(pmk_r0, f.pmk_r0_name, f.r1kh_id, f.s1kh_id, + r.pmk_r1, r.pmk_r1_name); + wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", r.pmk_r1, PMK_LEN); + wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", r.pmk_r1_name, + WPA_PMK_NAME_LEN); + + if (aes_wrap(r1kh->key, (FT_R0KH_R1KH_RESP_DATA_LEN + 7) / 8, + r.nonce, resp.nonce) < 0) { + os_memset(pmk_r0, 0, PMK_LEN); + return -1; + } + + os_memset(pmk_r0, 0, PMK_LEN); + + wpa_ft_rrb_send(wpa_auth, src_addr, (u8 *) &resp, sizeof(resp)); + + return 0; +} + + +static int wpa_ft_rrb_rx_resp(struct wpa_authenticator *wpa_auth, + const u8 *src_addr, + const u8 *data, size_t data_len) +{ + struct ft_r0kh_r1kh_resp_frame *frame, f; + struct ft_remote_r0kh *r0kh; + + wpa_printf(MSG_DEBUG, "FT: Received PMK-R1 pull response"); + + if (data_len < sizeof(*frame)) + return -1; + + r0kh = wpa_auth->conf.r0kh_list; + while (r0kh) { + if (os_memcmp(r0kh->addr, src_addr, ETH_ALEN) == 0) + break; + r0kh = r0kh->next; + } + if (r0kh == NULL) { + wpa_printf(MSG_DEBUG, "FT: No matching R0KH address found for " + "PMK-R0 pull response source address " MACSTR, + MAC2STR(src_addr)); + return -1; + } + + frame = (struct ft_r0kh_r1kh_resp_frame *) data; + /* aes_unwrap() does not support inplace decryption, so use a temporary + * buffer for the data. */ + if (aes_unwrap(r0kh->key, (FT_R0KH_R1KH_RESP_DATA_LEN + 7) / 8, + frame->nonce, f.nonce) < 0) { + wpa_printf(MSG_DEBUG, "FT: Failed to decrypt PMK-R1 pull " + "response from " MACSTR, MAC2STR(src_addr)); + return -1; + } + + if (os_memcmp(f.r1kh_id, wpa_auth->conf.r1_key_holder, FT_R1KH_ID_LEN) + != 0) { + wpa_printf(MSG_DEBUG, "FT: PMK-R1 pull response did not use a " + "matching R1KH-ID"); + return -1; + } + + /* TODO: verify that <nonce,s1kh_id> matches with a pending request + * and call this requests callback function to finish request + * processing */ + + wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 pull - nonce", + f.nonce, sizeof(f.nonce)); + wpa_printf(MSG_DEBUG, "FT: PMK-R1 pull - R1KH-ID=" MACSTR "S1KH-ID=" + MACSTR, MAC2STR(f.r1kh_id), MAC2STR(f.s1kh_id)); + wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1 pull - PMK-R1", + f.pmk_r1, PMK_LEN); + wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 pull - PMKR1Name", + f.pmk_r1_name, WPA_PMK_NAME_LEN); + + wpa_ft_store_pmk_r1(wpa_auth, f.s1kh_id, f.pmk_r1, f.pmk_r1_name); + os_memset(f.pmk_r1, 0, PMK_LEN); + + return 0; +} + + +static int wpa_ft_rrb_rx_push(struct wpa_authenticator *wpa_auth, + const u8 *src_addr, + const u8 *data, size_t data_len) +{ + struct ft_r0kh_r1kh_push_frame *frame, f; + struct ft_remote_r0kh *r0kh; + struct os_time now; + os_time_t tsend; + + wpa_printf(MSG_DEBUG, "FT: Received PMK-R1 push"); + + if (data_len < sizeof(*frame)) + return -1; + + r0kh = wpa_auth->conf.r0kh_list; + while (r0kh) { + if (os_memcmp(r0kh->addr, src_addr, ETH_ALEN) == 0) + break; + r0kh = r0kh->next; + } + if (r0kh == NULL) { + wpa_printf(MSG_DEBUG, "FT: No matching R0KH address found for " + "PMK-R0 push source address " MACSTR, + MAC2STR(src_addr)); + return -1; + } + + frame = (struct ft_r0kh_r1kh_push_frame *) data; + /* aes_unwrap() does not support inplace decryption, so use a temporary + * buffer for the data. */ + if (aes_unwrap(r0kh->key, (FT_R0KH_R1KH_PUSH_DATA_LEN + 7) / 8, + frame->timestamp, f.timestamp) < 0) { + wpa_printf(MSG_DEBUG, "FT: Failed to decrypt PMK-R1 push from " + MACSTR, MAC2STR(src_addr)); + return -1; + } + + os_get_time(&now); + tsend = WPA_GET_LE32(f.timestamp); + if ((now.sec > tsend && now.sec - tsend > 60) || + (now.sec < tsend && tsend - now.sec > 60)) { + wpa_printf(MSG_DEBUG, "FT: PMK-R1 push did not have a valid " + "timestamp: sender time %d own time %d\n", + (int) tsend, (int) now.sec); + return -1; + } + + if (os_memcmp(f.r1kh_id, wpa_auth->conf.r1_key_holder, FT_R1KH_ID_LEN) + != 0) { + wpa_printf(MSG_DEBUG, "FT: PMK-R1 push did not use a matching " + "R1KH-ID (received " MACSTR " own " MACSTR ")", + MAC2STR(f.r1kh_id), + MAC2STR(wpa_auth->conf.r1_key_holder)); + return -1; + } + + wpa_printf(MSG_DEBUG, "FT: PMK-R1 push - R1KH-ID=" MACSTR " S1KH-ID=" + MACSTR, MAC2STR(f.r1kh_id), MAC2STR(f.s1kh_id)); + wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1 push - PMK-R1", + f.pmk_r1, PMK_LEN); + wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 push - PMKR1Name", + f.pmk_r1_name, WPA_PMK_NAME_LEN); + + wpa_ft_store_pmk_r1(wpa_auth, f.s1kh_id, f.pmk_r1, f.pmk_r1_name); + os_memset(f.pmk_r1, 0, PMK_LEN); + + return 0; +} + + +int wpa_ft_rrb_rx(struct wpa_authenticator *wpa_auth, const u8 *src_addr, + const u8 *data, size_t data_len) +{ + struct ft_rrb_frame *frame; + u16 alen; + const u8 *pos, *end, *start; + u8 action; + const u8 *sta_addr, *target_ap_addr; + + wpa_printf(MSG_DEBUG, "FT: RRB received frame from remote AP " MACSTR, + MAC2STR(src_addr)); + + if (data_len < sizeof(*frame)) { + wpa_printf(MSG_DEBUG, "FT: Too short RRB frame (data_len=%lu)", + (unsigned long) data_len); + return -1; + } + + pos = data; + frame = (struct ft_rrb_frame *) pos; + pos += sizeof(*frame); + + alen = le_to_host16(frame->action_length); + wpa_printf(MSG_DEBUG, "FT: RRB frame - frame_type=%d packet_type=%d " + "action_length=%d ap_address=" MACSTR, + frame->frame_type, frame->packet_type, alen, + MAC2STR(frame->ap_address)); + + if (frame->frame_type != RSN_REMOTE_FRAME_TYPE_FT_RRB) { + /* Discard frame per IEEE Std 802.11r-2008, 11A.10.3 */ + wpa_printf(MSG_DEBUG, "FT: RRB discarded frame with " + "unrecognized type %d", frame->frame_type); + return -1; + } + + if (alen > data_len - sizeof(*frame)) { + wpa_printf(MSG_DEBUG, "FT: RRB frame too short for action " + "frame"); + return -1; + } + + if (frame->packet_type == FT_PACKET_R0KH_R1KH_PULL) + return wpa_ft_rrb_rx_pull(wpa_auth, src_addr, data, data_len); + if (frame->packet_type == FT_PACKET_R0KH_R1KH_RESP) + return wpa_ft_rrb_rx_resp(wpa_auth, src_addr, data, data_len); + if (frame->packet_type == FT_PACKET_R0KH_R1KH_PUSH) + return wpa_ft_rrb_rx_push(wpa_auth, src_addr, data, data_len); + + wpa_hexdump(MSG_MSGDUMP, "FT: RRB - FT Action frame", pos, alen); + + if (alen < 1 + 1 + 2 * ETH_ALEN) { + wpa_printf(MSG_DEBUG, "FT: Too short RRB frame (not enough " + "room for Action Frame body); alen=%lu", + (unsigned long) alen); + return -1; + } + start = pos; + end = pos + alen; + + if (*pos != WLAN_ACTION_FT) { + wpa_printf(MSG_DEBUG, "FT: Unexpected Action frame category " + "%d", *pos); + return -1; + } + + pos++; + action = *pos++; + sta_addr = pos; + pos += ETH_ALEN; + target_ap_addr = pos; + pos += ETH_ALEN; + wpa_printf(MSG_DEBUG, "FT: RRB Action Frame: action=%d sta_addr=" + MACSTR " target_ap_addr=" MACSTR, + action, MAC2STR(sta_addr), MAC2STR(target_ap_addr)); + + if (frame->packet_type == FT_PACKET_REQUEST) { + wpa_printf(MSG_DEBUG, "FT: FT Packet Type - Request"); + + if (action != 1) { + wpa_printf(MSG_DEBUG, "FT: Unexpected Action %d in " + "RRB Request", action); + return -1; + } + + if (os_memcmp(target_ap_addr, wpa_auth->addr, ETH_ALEN) != 0) { + wpa_printf(MSG_DEBUG, "FT: Target AP address in the " + "RRB Request does not match with own " + "address"); + return -1; + } + + if (wpa_ft_rrb_rx_request(wpa_auth, frame->ap_address, + sta_addr, pos, end - pos) < 0) + return -1; + } else if (frame->packet_type == FT_PACKET_RESPONSE) { + u16 status_code; + + if (end - pos < 2) { + wpa_printf(MSG_DEBUG, "FT: Not enough room for status " + "code in RRB Response"); + return -1; + } + status_code = WPA_GET_LE16(pos); + pos += 2; + + wpa_printf(MSG_DEBUG, "FT: FT Packet Type - Response " + "(status_code=%d)", status_code); + + if (wpa_ft_action_send(wpa_auth, sta_addr, start, alen) < 0) + return -1; + } else { + wpa_printf(MSG_DEBUG, "FT: RRB discarded frame with unknown " + "packet_type %d", frame->packet_type); + return -1; + } + + return 0; +} + + +static void wpa_ft_generate_pmk_r1(struct wpa_authenticator *wpa_auth, + struct wpa_ft_pmk_r0_sa *pmk_r0, + struct ft_remote_r1kh *r1kh, + const u8 *s1kh_id) +{ + struct ft_r0kh_r1kh_push_frame frame, f; + struct os_time now; + + os_memset(&frame, 0, sizeof(frame)); + frame.frame_type = RSN_REMOTE_FRAME_TYPE_FT_RRB; + frame.packet_type = FT_PACKET_R0KH_R1KH_PUSH; + frame.data_length = host_to_le16(FT_R0KH_R1KH_PUSH_DATA_LEN); + os_memcpy(frame.ap_address, wpa_auth->addr, ETH_ALEN); + + /* aes_wrap() does not support inplace encryption, so use a temporary + * buffer for the data. */ + os_memcpy(f.r1kh_id, r1kh->id, FT_R1KH_ID_LEN); + os_memcpy(f.s1kh_id, s1kh_id, ETH_ALEN); + os_memcpy(f.pmk_r0_name, pmk_r0->pmk_r0_name, WPA_PMK_NAME_LEN); + wpa_derive_pmk_r1(pmk_r0->pmk_r0, pmk_r0->pmk_r0_name, r1kh->id, + s1kh_id, f.pmk_r1, f.pmk_r1_name); + wpa_printf(MSG_DEBUG, "FT: R1KH-ID " MACSTR, MAC2STR(r1kh->id)); + wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", f.pmk_r1, PMK_LEN); + wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", f.pmk_r1_name, + WPA_PMK_NAME_LEN); + os_get_time(&now); + WPA_PUT_LE32(f.timestamp, now.sec); + if (aes_wrap(r1kh->key, (FT_R0KH_R1KH_PUSH_DATA_LEN + 7) / 8, + f.timestamp, frame.timestamp) < 0) + return; + + wpa_ft_rrb_send(wpa_auth, r1kh->addr, (u8 *) &frame, sizeof(frame)); +} + + +void wpa_ft_push_pmk_r1(struct wpa_authenticator *wpa_auth, const u8 *addr) +{ + struct wpa_ft_pmk_r0_sa *r0; + struct ft_remote_r1kh *r1kh; + + if (!wpa_auth->conf.pmk_r1_push) + return; + + r0 = wpa_auth->ft_pmk_cache->pmk_r0; + while (r0) { + if (os_memcmp(r0->spa, addr, ETH_ALEN) == 0) + break; + r0 = r0->next; + } + + if (r0 == NULL || r0->pmk_r1_pushed) + return; + r0->pmk_r1_pushed = 1; + + wpa_printf(MSG_DEBUG, "FT: Deriving and pushing PMK-R1 keys to R1KHs " + "for STA " MACSTR, MAC2STR(addr)); + + r1kh = wpa_auth->conf.r1kh_list; + while (r1kh) { + wpa_ft_generate_pmk_r1(wpa_auth, r0, r1kh, addr); + r1kh = r1kh->next; + } +} + +#endif /* CONFIG_IEEE80211R */ diff --git a/contrib/wpa/hostapd/wps_hostapd.c b/contrib/wpa/hostapd/wps_hostapd.c new file mode 100644 index 0000000..a824c16 --- /dev/null +++ b/contrib/wpa/hostapd/wps_hostapd.c @@ -0,0 +1,998 @@ +/* + * hostapd / WPS integration + * Copyright (c) 2008, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "hostapd.h" +#include "driver.h" +#include "eloop.h" +#include "uuid.h" +#include "wpa_ctrl.h" +#include "ieee802_11_defs.h" +#include "sta_info.h" +#include "eapol_sm.h" +#include "wps/wps.h" +#include "wps/wps_defs.h" +#include "wps/wps_dev_attr.h" +#include "wps_hostapd.h" + + +#ifdef CONFIG_WPS_UPNP +#include "wps/wps_upnp.h" +static int hostapd_wps_upnp_init(struct hostapd_data *hapd, + struct wps_context *wps); +static void hostapd_wps_upnp_deinit(struct hostapd_data *hapd); +#endif /* CONFIG_WPS_UPNP */ + + +static int hostapd_wps_new_psk_cb(void *ctx, const u8 *mac_addr, const u8 *psk, + size_t psk_len) +{ + struct hostapd_data *hapd = ctx; + struct hostapd_wpa_psk *p; + struct hostapd_ssid *ssid = &hapd->conf->ssid; + + wpa_printf(MSG_DEBUG, "Received new WPA/WPA2-PSK from WPS for STA " + MACSTR, MAC2STR(mac_addr)); + wpa_hexdump_key(MSG_DEBUG, "Per-device PSK", psk, psk_len); + + if (psk_len != PMK_LEN) { + wpa_printf(MSG_DEBUG, "Unexpected PSK length %lu", + (unsigned long) psk_len); + return -1; + } + + /* Add the new PSK to runtime PSK list */ + p = os_zalloc(sizeof(*p)); + if (p == NULL) + return -1; + os_memcpy(p->addr, mac_addr, ETH_ALEN); + os_memcpy(p->psk, psk, PMK_LEN); + + p->next = ssid->wpa_psk; + ssid->wpa_psk = p; + + if (ssid->wpa_psk_file) { + FILE *f; + char hex[PMK_LEN * 2 + 1]; + /* Add the new PSK to PSK list file */ + f = fopen(ssid->wpa_psk_file, "a"); + if (f == NULL) { + wpa_printf(MSG_DEBUG, "Failed to add the PSK to " + "'%s'", ssid->wpa_psk_file); + return -1; + } + + wpa_snprintf_hex(hex, sizeof(hex), psk, psk_len); + fprintf(f, MACSTR " %s\n", MAC2STR(mac_addr), hex); + fclose(f); + } + + return 0; +} + + +static int hostapd_wps_set_ie_cb(void *ctx, const u8 *beacon_ie, + size_t beacon_ie_len, const u8 *probe_resp_ie, + size_t probe_resp_ie_len) +{ + struct hostapd_data *hapd = ctx; + + os_free(hapd->wps_beacon_ie); + if (beacon_ie_len == 0) { + hapd->wps_beacon_ie = NULL; + hapd->wps_beacon_ie_len = 0; + } else { + hapd->wps_beacon_ie = os_malloc(beacon_ie_len); + if (hapd->wps_beacon_ie == NULL) { + hapd->wps_beacon_ie_len = 0; + return -1; + } + os_memcpy(hapd->wps_beacon_ie, beacon_ie, beacon_ie_len); + hapd->wps_beacon_ie_len = beacon_ie_len; + } + hostapd_set_wps_beacon_ie(hapd, hapd->wps_beacon_ie, + hapd->wps_beacon_ie_len); + + os_free(hapd->wps_probe_resp_ie); + if (probe_resp_ie_len == 0) { + hapd->wps_probe_resp_ie = NULL; + hapd->wps_probe_resp_ie_len = 0; + } else { + hapd->wps_probe_resp_ie = os_malloc(probe_resp_ie_len); + if (hapd->wps_probe_resp_ie == NULL) { + hapd->wps_probe_resp_ie_len = 0; + return -1; + } + os_memcpy(hapd->wps_probe_resp_ie, probe_resp_ie, + probe_resp_ie_len); + hapd->wps_probe_resp_ie_len = probe_resp_ie_len; + } + hostapd_set_wps_probe_resp_ie(hapd, hapd->wps_probe_resp_ie, + hapd->wps_probe_resp_ie_len); + + return 0; +} + + +static void hostapd_wps_pin_needed_cb(void *ctx, const u8 *uuid_e, + const struct wps_device_data *dev) +{ + struct hostapd_data *hapd = ctx; + char uuid[40], txt[400]; + int len; + if (uuid_bin2str(uuid_e, uuid, sizeof(uuid))) + return; + wpa_printf(MSG_DEBUG, "WPS: PIN needed for E-UUID %s", uuid); + len = os_snprintf(txt, sizeof(txt), WPS_EVENT_PIN_NEEDED + "%s " MACSTR " [%s|%s|%s|%s|%s|%d-%08X-%d]", + uuid, MAC2STR(dev->mac_addr), dev->device_name, + dev->manufacturer, dev->model_name, + dev->model_number, dev->serial_number, + dev->categ, dev->oui, dev->sub_categ); + if (len > 0 && len < (int) sizeof(txt)) + wpa_msg(hapd, MSG_INFO, "%s", txt); + + if (hapd->conf->wps_pin_requests) { + FILE *f; + struct os_time t; + f = fopen(hapd->conf->wps_pin_requests, "a"); + if (f == NULL) + return; + os_get_time(&t); + fprintf(f, "%ld\t%s\t" MACSTR "\t%s\t%s\t%s\t%s\t%s" + "\t%d-%08X-%d\n", + t.sec, uuid, MAC2STR(dev->mac_addr), dev->device_name, + dev->manufacturer, dev->model_name, dev->model_number, + dev->serial_number, + dev->categ, dev->oui, dev->sub_categ); + fclose(f); + } +} + + +static void hostapd_wps_reg_success_cb(void *ctx, const u8 *mac_addr, + const u8 *uuid_e) +{ + struct hostapd_data *hapd = ctx; + char uuid[40]; + if (uuid_bin2str(uuid_e, uuid, sizeof(uuid))) + return; + wpa_msg(hapd, MSG_INFO, WPS_EVENT_REG_SUCCESS MACSTR " %s", + MAC2STR(mac_addr), uuid); +} + + +static int str_starts(const char *str, const char *start) +{ + return os_strncmp(str, start, os_strlen(start)) == 0; +} + + +static void wps_reload_config(void *eloop_data, void *user_ctx) +{ + struct hostapd_iface *iface = eloop_data; + + wpa_printf(MSG_DEBUG, "WPS: Reload configuration data"); + if (hostapd_reload_config(iface) < 0) { + wpa_printf(MSG_WARNING, "WPS: Failed to reload the updated " + "configuration"); + } +} + + +static int hostapd_wps_cred_cb(void *ctx, const struct wps_credential *cred) +{ + struct hostapd_data *hapd = ctx; + FILE *oconf, *nconf; + size_t len, i; + char *tmp_fname; + char buf[1024]; + int multi_bss; + int wpa; + + wpa_hexdump_key(MSG_DEBUG, "WPS: Received Credential attribute", + cred->cred_attr, cred->cred_attr_len); + + wpa_printf(MSG_DEBUG, "WPS: Received new AP Settings"); + wpa_hexdump_ascii(MSG_DEBUG, "WPS: SSID", cred->ssid, cred->ssid_len); + wpa_printf(MSG_DEBUG, "WPS: Authentication Type 0x%x", + cred->auth_type); + wpa_printf(MSG_DEBUG, "WPS: Encryption Type 0x%x", cred->encr_type); + wpa_printf(MSG_DEBUG, "WPS: Network Key Index %d", cred->key_idx); + wpa_hexdump_key(MSG_DEBUG, "WPS: Network Key", + cred->key, cred->key_len); + wpa_printf(MSG_DEBUG, "WPS: MAC Address " MACSTR, + MAC2STR(cred->mac_addr)); + + if ((hapd->conf->wps_cred_processing == 1 || + hapd->conf->wps_cred_processing == 2) && cred->cred_attr) { + size_t blen = cred->cred_attr_len * 2 + 1; + char *buf = os_malloc(blen); + if (buf) { + wpa_snprintf_hex(buf, blen, + cred->cred_attr, cred->cred_attr_len); + wpa_msg(hapd, MSG_INFO, "%s%s", + WPS_EVENT_NEW_AP_SETTINGS, buf); + os_free(buf); + } + } else + wpa_msg(hapd, MSG_INFO, WPS_EVENT_NEW_AP_SETTINGS); + + if (hapd->conf->wps_cred_processing == 1) + return 0; + + len = os_strlen(hapd->iface->config_fname) + 5; + tmp_fname = os_malloc(len); + if (tmp_fname == NULL) + return -1; + os_snprintf(tmp_fname, len, "%s-new", hapd->iface->config_fname); + + oconf = fopen(hapd->iface->config_fname, "r"); + if (oconf == NULL) { + wpa_printf(MSG_WARNING, "WPS: Could not open current " + "configuration file"); + os_free(tmp_fname); + return -1; + } + + nconf = fopen(tmp_fname, "w"); + if (nconf == NULL) { + wpa_printf(MSG_WARNING, "WPS: Could not write updated " + "configuration file"); + os_free(tmp_fname); + fclose(oconf); + return -1; + } + + fprintf(nconf, "# WPS configuration - START\n"); + + fprintf(nconf, "wps_state=2\n"); + + fprintf(nconf, "ssid="); + for (i = 0; i < cred->ssid_len; i++) + fputc(cred->ssid[i], nconf); + fprintf(nconf, "\n"); + + if ((cred->auth_type & (WPS_AUTH_WPA2 | WPS_AUTH_WPA2PSK)) && + (cred->auth_type & (WPS_AUTH_WPA | WPS_AUTH_WPAPSK))) + wpa = 3; + else if (cred->auth_type & (WPS_AUTH_WPA2 | WPS_AUTH_WPA2PSK)) + wpa = 2; + else if (cred->auth_type & (WPS_AUTH_WPA | WPS_AUTH_WPAPSK)) + wpa = 1; + else + wpa = 0; + + if (wpa) { + char *prefix; + fprintf(nconf, "wpa=%d\n", wpa); + + fprintf(nconf, "wpa_key_mgmt="); + prefix = ""; + if (cred->auth_type & (WPS_AUTH_WPA2 | WPS_AUTH_WPA)) { + fprintf(nconf, "WPA-EAP"); + prefix = " "; + } + if (cred->auth_type & (WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK)) + fprintf(nconf, "%sWPA-PSK", prefix); + fprintf(nconf, "\n"); + + fprintf(nconf, "wpa_pairwise="); + prefix = ""; + if (cred->encr_type & WPS_ENCR_AES) { + fprintf(nconf, "CCMP"); + prefix = " "; + } + if (cred->encr_type & WPS_ENCR_TKIP) { + fprintf(nconf, "%sTKIP", prefix); + } + fprintf(nconf, "\n"); + + if (cred->key_len >= 8 && cred->key_len < 64) { + fprintf(nconf, "wpa_passphrase="); + for (i = 0; i < cred->key_len; i++) + fputc(cred->key[i], nconf); + fprintf(nconf, "\n"); + } else if (cred->key_len == 64) { + fprintf(nconf, "wpa_psk="); + for (i = 0; i < cred->key_len; i++) + fputc(cred->key[i], nconf); + fprintf(nconf, "\n"); + } else { + wpa_printf(MSG_WARNING, "WPS: Invalid key length %lu " + "for WPA/WPA2", + (unsigned long) cred->key_len); + } + + fprintf(nconf, "auth_algs=1\n"); + } else { + if ((cred->auth_type & WPS_AUTH_OPEN) && + (cred->auth_type & WPS_AUTH_SHARED)) + fprintf(nconf, "auth_algs=3\n"); + else if (cred->auth_type & WPS_AUTH_SHARED) + fprintf(nconf, "auth_algs=2\n"); + else + fprintf(nconf, "auth_algs=1\n"); + + if (cred->encr_type & WPS_ENCR_WEP && cred->key_idx < 4) { + fprintf(nconf, "wep_default_key=%d\n", cred->key_idx); + fprintf(nconf, "wep_key%d=", cred->key_idx); + if (cred->key_len != 10 && cred->key_len != 26) + fputc('"', nconf); + for (i = 0; i < cred->key_len; i++) + fputc(cred->key[i], nconf); + if (cred->key_len != 10 && cred->key_len != 26) + fputc('"', nconf); + fprintf(nconf, "\n"); + } + } + + fprintf(nconf, "# WPS configuration - END\n"); + + multi_bss = 0; + while (fgets(buf, sizeof(buf), oconf)) { + if (os_strncmp(buf, "bss=", 4) == 0) + multi_bss = 1; + if (!multi_bss && + (str_starts(buf, "ssid=") || + str_starts(buf, "auth_algs=") || + str_starts(buf, "wps_state=") || + str_starts(buf, "wpa=") || + str_starts(buf, "wpa_psk=") || + str_starts(buf, "wpa_pairwise=") || + str_starts(buf, "rsn_pairwise=") || + str_starts(buf, "wpa_key_mgmt=") || + str_starts(buf, "wpa_passphrase="))) { + fprintf(nconf, "#WPS# %s", buf); + } else + fprintf(nconf, "%s", buf); + } + + fclose(nconf); + fclose(oconf); + + if (rename(tmp_fname, hapd->iface->config_fname) < 0) { + wpa_printf(MSG_WARNING, "WPS: Failed to rename the updated " + "configuration file: %s", strerror(errno)); + os_free(tmp_fname); + return -1; + } + + os_free(tmp_fname); + + /* Schedule configuration reload after short period of time to allow + * EAP-WSC to be finished. + */ + eloop_register_timeout(0, 100000, wps_reload_config, hapd->iface, + NULL); + + /* TODO: dualband AP may need to update multiple configuration files */ + + wpa_printf(MSG_DEBUG, "WPS: AP configuration updated"); + + return 0; +} + + +static void hostapd_pwd_auth_fail(struct hostapd_data *hapd, + struct wps_event_pwd_auth_fail *data) +{ + FILE *f; + + if (!data->enrollee) + return; + + /* + * Registrar failed to prove its knowledge of the AP PIN. Lock AP setup + * if this happens multiple times. + */ + hapd->ap_pin_failures++; + if (hapd->ap_pin_failures < 4) + return; + + wpa_msg(hapd, MSG_INFO, WPS_EVENT_AP_SETUP_LOCKED); + hapd->wps->ap_setup_locked = 1; + + wps_registrar_update_ie(hapd->wps->registrar); + + if (hapd->conf->wps_cred_processing == 1) + return; + + f = fopen(hapd->iface->config_fname, "a"); + if (f == NULL) { + wpa_printf(MSG_WARNING, "WPS: Could not append to the current " + "configuration file"); + return; + } + + fprintf(f, "# WPS AP Setup Locked based on possible attack\n"); + fprintf(f, "ap_setup_locked=1\n"); + fclose(f); + + /* TODO: dualband AP may need to update multiple configuration files */ + + wpa_printf(MSG_DEBUG, "WPS: AP configuration updated"); +} + + +static void hostapd_wps_event_cb(void *ctx, enum wps_event event, + union wps_event_data *data) +{ + struct hostapd_data *hapd = ctx; + + if (event == WPS_EV_PWD_AUTH_FAIL) + hostapd_pwd_auth_fail(hapd, &data->pwd_auth_fail); +} + + +static void hostapd_wps_clear_ies(struct hostapd_data *hapd) +{ + os_free(hapd->wps_beacon_ie); + hapd->wps_beacon_ie = NULL; + hapd->wps_beacon_ie_len = 0; + hostapd_set_wps_beacon_ie(hapd, NULL, 0); + + os_free(hapd->wps_probe_resp_ie); + hapd->wps_probe_resp_ie = NULL; + hapd->wps_probe_resp_ie_len = 0; + hostapd_set_wps_probe_resp_ie(hapd, NULL, 0); +} + + +int hostapd_init_wps(struct hostapd_data *hapd, + struct hostapd_bss_config *conf) +{ + struct wps_context *wps; + struct wps_registrar_config cfg; + + if (conf->wps_state == 0) { + hostapd_wps_clear_ies(hapd); + return 0; + } + + wps = os_zalloc(sizeof(*wps)); + if (wps == NULL) + return -1; + + wps->cred_cb = hostapd_wps_cred_cb; + wps->event_cb = hostapd_wps_event_cb; + wps->cb_ctx = hapd; + + os_memset(&cfg, 0, sizeof(cfg)); + wps->wps_state = hapd->conf->wps_state; + wps->ap_setup_locked = hapd->conf->ap_setup_locked; + if (is_nil_uuid(hapd->conf->uuid)) { + uuid_gen_mac_addr(hapd->own_addr, wps->uuid); + wpa_hexdump(MSG_DEBUG, "WPS: UUID based on MAC address", + wps->uuid, UUID_LEN); + } else + os_memcpy(wps->uuid, hapd->conf->uuid, UUID_LEN); + wps->ssid_len = hapd->conf->ssid.ssid_len; + os_memcpy(wps->ssid, hapd->conf->ssid.ssid, wps->ssid_len); + wps->ap = 1; + os_memcpy(wps->dev.mac_addr, hapd->own_addr, ETH_ALEN); + wps->dev.device_name = hapd->conf->device_name ? + os_strdup(hapd->conf->device_name) : NULL; + wps->dev.manufacturer = hapd->conf->manufacturer ? + os_strdup(hapd->conf->manufacturer) : NULL; + wps->dev.model_name = hapd->conf->model_name ? + os_strdup(hapd->conf->model_name) : NULL; + wps->dev.model_number = hapd->conf->model_number ? + os_strdup(hapd->conf->model_number) : NULL; + wps->dev.serial_number = hapd->conf->serial_number ? + os_strdup(hapd->conf->serial_number) : NULL; + if (hapd->conf->config_methods) { + char *m = hapd->conf->config_methods; + if (os_strstr(m, "label")) + wps->config_methods |= WPS_CONFIG_LABEL; + if (os_strstr(m, "display")) + wps->config_methods |= WPS_CONFIG_DISPLAY; + if (os_strstr(m, "push_button")) + wps->config_methods |= WPS_CONFIG_PUSHBUTTON; + if (os_strstr(m, "keypad")) + wps->config_methods |= WPS_CONFIG_KEYPAD; + } + if (hapd->conf->device_type) { + char *pos; + u8 oui[4]; + /* <categ>-<OUI>-<subcateg> */ + wps->dev.categ = atoi(hapd->conf->device_type); + pos = os_strchr(hapd->conf->device_type, '-'); + if (pos == NULL) { + wpa_printf(MSG_ERROR, "WPS: Invalid device_type"); + os_free(wps); + return -1; + } + pos++; + if (hexstr2bin(pos, oui, 4)) { + wpa_printf(MSG_ERROR, "WPS: Invalid device_type OUI"); + os_free(wps); + return -1; + } + wps->dev.oui = WPA_GET_BE32(oui); + pos = os_strchr(pos, '-'); + if (pos == NULL) { + wpa_printf(MSG_ERROR, "WPS: Invalid device_type"); + os_free(wps); + return -1; + } + pos++; + wps->dev.sub_categ = atoi(pos); + } + wps->dev.os_version = WPA_GET_BE32(hapd->conf->os_version); + wps->dev.rf_bands = hapd->iconf->hw_mode == HOSTAPD_MODE_IEEE80211A ? + WPS_RF_50GHZ : WPS_RF_24GHZ; /* FIX: dualband AP */ + + if (conf->wpa & WPA_PROTO_RSN) { + if (conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK) + wps->auth_types |= WPS_AUTH_WPA2PSK; + if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X) + wps->auth_types |= WPS_AUTH_WPA2; + + if (conf->rsn_pairwise & WPA_CIPHER_CCMP) + wps->encr_types |= WPS_ENCR_AES; + if (conf->rsn_pairwise & WPA_CIPHER_TKIP) + wps->encr_types |= WPS_ENCR_TKIP; + } + + if (conf->wpa & WPA_PROTO_WPA) { + if (conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK) + wps->auth_types |= WPS_AUTH_WPAPSK; + if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X) + wps->auth_types |= WPS_AUTH_WPA; + + if (conf->wpa_pairwise & WPA_CIPHER_CCMP) + wps->encr_types |= WPS_ENCR_AES; + if (conf->wpa_pairwise & WPA_CIPHER_TKIP) + wps->encr_types |= WPS_ENCR_TKIP; + } + + if (conf->ssid.security_policy == SECURITY_PLAINTEXT) { + wps->encr_types |= WPS_ENCR_NONE; + wps->auth_types |= WPS_AUTH_OPEN; + } else if (conf->ssid.security_policy == SECURITY_STATIC_WEP) { + wps->encr_types |= WPS_ENCR_WEP; + if (conf->auth_algs & WPA_AUTH_ALG_OPEN) + wps->auth_types |= WPS_AUTH_OPEN; + if (conf->auth_algs & WPA_AUTH_ALG_SHARED) + wps->auth_types |= WPS_AUTH_SHARED; + } else if (conf->ssid.security_policy == SECURITY_IEEE_802_1X) { + wps->auth_types |= WPS_AUTH_OPEN; + if (conf->default_wep_key_len) + wps->encr_types |= WPS_ENCR_WEP; + else + wps->encr_types |= WPS_ENCR_NONE; + } + + if (conf->ssid.wpa_psk_file) { + /* Use per-device PSKs */ + } else if (conf->ssid.wpa_passphrase) { + wps->network_key = (u8 *) os_strdup(conf->ssid.wpa_passphrase); + wps->network_key_len = os_strlen(conf->ssid.wpa_passphrase); + } else if (conf->ssid.wpa_psk) { + wps->network_key = os_malloc(2 * PMK_LEN + 1); + if (wps->network_key == NULL) { + os_free(wps); + return -1; + } + wpa_snprintf_hex((char *) wps->network_key, 2 * PMK_LEN + 1, + conf->ssid.wpa_psk->psk, PMK_LEN); + wps->network_key_len = 2 * PMK_LEN; + } else if (conf->ssid.wep.keys_set && conf->ssid.wep.key[0]) { + wps->network_key = os_malloc(conf->ssid.wep.len[0]); + if (wps->network_key == NULL) { + os_free(wps); + return -1; + } + os_memcpy(wps->network_key, conf->ssid.wep.key[0], + conf->ssid.wep.len[0]); + wps->network_key_len = conf->ssid.wep.len[0]; + } + + if (conf->wps_state == WPS_STATE_NOT_CONFIGURED) { + /* Override parameters to enable security by default */ + wps->auth_types = WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK; + wps->encr_types = WPS_ENCR_AES | WPS_ENCR_TKIP; + } + + wps->ap_settings = conf->ap_settings; + wps->ap_settings_len = conf->ap_settings_len; + + cfg.new_psk_cb = hostapd_wps_new_psk_cb; + cfg.set_ie_cb = hostapd_wps_set_ie_cb; + cfg.pin_needed_cb = hostapd_wps_pin_needed_cb; + cfg.reg_success_cb = hostapd_wps_reg_success_cb; + cfg.cb_ctx = hapd; + cfg.skip_cred_build = conf->skip_cred_build; + cfg.extra_cred = conf->extra_cred; + cfg.extra_cred_len = conf->extra_cred_len; + cfg.disable_auto_conf = (hapd->conf->wps_cred_processing == 1) && + conf->skip_cred_build; + + wps->registrar = wps_registrar_init(wps, &cfg); + if (wps->registrar == NULL) { + printf("Failed to initialize WPS Registrar\n"); + os_free(wps->network_key); + os_free(wps); + return -1; + } + +#ifdef CONFIG_WPS_UPNP + wps->friendly_name = hapd->conf->friendly_name; + wps->manufacturer_url = hapd->conf->manufacturer_url; + wps->model_description = hapd->conf->model_description; + wps->model_url = hapd->conf->model_url; + wps->upc = hapd->conf->upc; + + if (hostapd_wps_upnp_init(hapd, wps) < 0) { + wpa_printf(MSG_ERROR, "Failed to initialize WPS UPnP"); + wps_registrar_deinit(wps->registrar); + os_free(wps->network_key); + os_free(wps); + return -1; + } +#endif /* CONFIG_WPS_UPNP */ + + hapd->wps = wps; + + return 0; +} + + +void hostapd_deinit_wps(struct hostapd_data *hapd) +{ + if (hapd->wps == NULL) + return; +#ifdef CONFIG_WPS_UPNP + hostapd_wps_upnp_deinit(hapd); +#endif /* CONFIG_WPS_UPNP */ + wps_registrar_deinit(hapd->wps->registrar); + os_free(hapd->wps->network_key); + wps_device_data_free(&hapd->wps->dev); + wps_free_pending_msgs(hapd->wps->upnp_msgs); + os_free(hapd->wps); + hapd->wps = NULL; + hostapd_wps_clear_ies(hapd); +} + + +int hostapd_wps_add_pin(struct hostapd_data *hapd, const char *uuid, + const char *pin) +{ + u8 u[UUID_LEN]; + int any = 0; + + if (hapd->wps == NULL) + return -1; + if (os_strcmp(uuid, "any") == 0) + any = 1; + else if (uuid_str2bin(uuid, u)) + return -1; + return wps_registrar_add_pin(hapd->wps->registrar, any ? NULL : u, + (const u8 *) pin, os_strlen(pin)); +} + + +int hostapd_wps_button_pushed(struct hostapd_data *hapd) +{ + if (hapd->wps == NULL) + return -1; + return wps_registrar_button_pushed(hapd->wps->registrar); +} + + +void hostapd_wps_probe_req_rx(struct hostapd_data *hapd, const u8 *addr, + const u8 *ie, size_t ie_len) +{ + struct wpabuf *wps_ie; + const u8 *end, *pos, *wps; + + if (hapd->wps == NULL) + return; + + pos = ie; + end = ie + ie_len; + wps = NULL; + + while (pos + 1 < end) { + if (pos + 2 + pos[1] > end) + return; + if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 && + WPA_GET_BE32(&pos[2]) == WPS_DEV_OUI_WFA) { + wps = pos; + break; + } + pos += 2 + pos[1]; + } + + if (wps == NULL) + return; /* No WPS IE in Probe Request */ + + wps_ie = wpabuf_alloc(ie_len); + if (wps_ie == NULL) + return; + + /* There may be multiple WPS IEs in the message, so need to concatenate + * their WPS Data fields */ + while (pos + 1 < end) { + if (pos + 2 + pos[1] > end) + break; + if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 && + WPA_GET_BE32(&pos[2]) == WPS_DEV_OUI_WFA) + wpabuf_put_data(wps_ie, pos + 6, pos[1] - 4); + pos += 2 + pos[1]; + } + + if (wpabuf_len(wps_ie) > 0) { + wps_registrar_probe_req_rx(hapd->wps->registrar, addr, wps_ie); +#ifdef CONFIG_WPS_UPNP + /* FIX: what exactly should be included in the WLANEvent? + * WPS attributes? Full ProbeReq frame? */ + upnp_wps_device_send_wlan_event(hapd->wps_upnp, addr, + UPNP_WPS_WLANEVENT_TYPE_PROBE, + wps_ie); +#endif /* CONFIG_WPS_UPNP */ + } + + wpabuf_free(wps_ie); +} + + +#ifdef CONFIG_WPS_UPNP + +static struct wpabuf * +hostapd_rx_req_get_device_info(void *priv, struct upnp_wps_peer *peer) +{ + struct hostapd_data *hapd = priv; + struct wps_config cfg; + struct wps_data *wps; + enum wsc_op_code op_code; + struct wpabuf *m1; + + /* + * Request for DeviceInfo, i.e., M1 TLVs. This is a start of WPS + * registration over UPnP with the AP acting as an Enrollee. It should + * be noted that this is frequently used just to get the device data, + * i.e., there may not be any intent to actually complete the + * registration. + */ + + if (peer->wps) + wps_deinit(peer->wps); + + os_memset(&cfg, 0, sizeof(cfg)); + cfg.wps = hapd->wps; + cfg.pin = (u8 *) hapd->conf->ap_pin; + cfg.pin_len = os_strlen(hapd->conf->ap_pin); + wps = wps_init(&cfg); + if (wps == NULL) + return NULL; + + m1 = wps_get_msg(wps, &op_code); + if (m1 == NULL) { + wps_deinit(wps); + return NULL; + } + + peer->wps = wps; + + return m1; +} + + +static struct wpabuf * +hostapd_rx_req_put_message(void *priv, struct upnp_wps_peer *peer, + const struct wpabuf *msg) +{ + enum wps_process_res res; + enum wsc_op_code op_code; + + /* PutMessage: msg = InMessage, return OutMessage */ + res = wps_process_msg(peer->wps, WSC_UPnP, msg); + if (res == WPS_FAILURE) + return NULL; + return wps_get_msg(peer->wps, &op_code); +} + + +static struct wpabuf * +hostapd_rx_req_get_ap_settings(void *priv, const struct wpabuf *msg) +{ + wpa_printf(MSG_DEBUG, "WPS UPnP: TODO %s", __func__); + return NULL; +} + + +static int hostapd_rx_req_set_ap_settings(void *priv, const struct wpabuf *msg) +{ + wpa_printf(MSG_DEBUG, "WPS UPnP: TODO %s", __func__); + return -1; +} + + +static int hostapd_rx_req_del_ap_settings(void *priv, const struct wpabuf *msg) +{ + wpa_printf(MSG_DEBUG, "WPS UPnP: TODO %s", __func__); + return -1; +} + + +static struct wpabuf * +hostapd_rx_req_get_sta_settings(void *priv, const struct wpabuf *msg) +{ + wpa_printf(MSG_DEBUG, "WPS UPnP: TODO %s", __func__); + return NULL; +} + + +static int hostapd_rx_req_set_sta_settings(void *priv, + const struct wpabuf *msg) +{ + wpa_printf(MSG_DEBUG, "WPS UPnP: TODO %s", __func__); + return -1; +} + + +static int hostapd_rx_req_del_sta_settings(void *priv, + const struct wpabuf *msg) +{ + wpa_printf(MSG_DEBUG, "WPS UPnP: TODO %s", __func__); + return -1; +} + + +static int hostapd_rx_req_put_wlan_response( + void *priv, enum upnp_wps_wlanevent_type ev_type, + const u8 *mac_addr, const struct wpabuf *msg, + enum wps_msg_type msg_type) +{ + struct hostapd_data *hapd = priv; + struct sta_info *sta; + struct upnp_pending_message *p; + + wpa_printf(MSG_DEBUG, "WPS UPnP: PutWLANResponse ev_type=%d mac_addr=" + MACSTR, ev_type, MAC2STR(mac_addr)); + wpa_hexdump_ascii(MSG_MSGDUMP, "WPS UPnP: PutWLANResponse NewMessage", + wpabuf_head(msg), wpabuf_len(msg)); + if (ev_type != UPNP_WPS_WLANEVENT_TYPE_EAP) { + wpa_printf(MSG_DEBUG, "WPS UPnP: Ignored unexpected " + "PutWLANResponse WLANEventType %d", ev_type); + return -1; + } + + /* + * EAP response to ongoing to WPS Registration. Send it to EAP-WSC + * server implementation for delivery to the peer. + */ + + sta = ap_get_sta(hapd, mac_addr); + if (!sta) { + /* + * Workaround - Intel wsccmd uses bogus NewWLANEventMAC: + * Pick STA that is in an ongoing WPS registration without + * checking the MAC address. + */ + wpa_printf(MSG_DEBUG, "WPS UPnP: No matching STA found based " + "on NewWLANEventMAC; try wildcard match"); + for (sta = hapd->sta_list; sta; sta = sta->next) { + if (sta->eapol_sm && (sta->flags & WLAN_STA_WPS)) + break; + } + } + + if (!sta) { + wpa_printf(MSG_DEBUG, "WPS UPnP: No matching STA found"); + return 0; + } + + p = os_zalloc(sizeof(*p)); + if (p == NULL) + return -1; + os_memcpy(p->addr, sta->addr, ETH_ALEN); + p->msg = wpabuf_dup(msg); + p->type = msg_type; + p->next = hapd->wps->upnp_msgs; + hapd->wps->upnp_msgs = p; + + return eapol_auth_eap_pending_cb(sta->eapol_sm, sta->eapol_sm->eap); +} + + +static int hostapd_rx_req_set_selected_registrar(void *priv, + const struct wpabuf *msg) +{ + struct hostapd_data *hapd = priv; + return wps_registrar_set_selected_registrar(hapd->wps->registrar, msg); +} + + +static int hostapd_rx_req_reboot_ap(void *priv, const struct wpabuf *msg) +{ + wpa_printf(MSG_DEBUG, "WPS UPnP: TODO %s", __func__); + return -1; +} + + +static int hostapd_rx_req_reset_ap(void *priv, const struct wpabuf *msg) +{ + wpa_printf(MSG_DEBUG, "WPS UPnP: TODO %s", __func__); + return -1; +} + + +static int hostapd_rx_req_reboot_sta(void *priv, const struct wpabuf *msg) +{ + wpa_printf(MSG_DEBUG, "WPS UPnP: TODO %s", __func__); + return -1; +} + + +static int hostapd_rx_req_reset_sta(void *priv, const struct wpabuf *msg) +{ + wpa_printf(MSG_DEBUG, "WPS UPnP: TODO %s", __func__); + return -1; +} + + +static int hostapd_wps_upnp_init(struct hostapd_data *hapd, + struct wps_context *wps) +{ + struct upnp_wps_device_ctx *ctx; + + if (!hapd->conf->upnp_iface) + return 0; + ctx = os_zalloc(sizeof(*ctx)); + if (ctx == NULL) + return -1; + + ctx->rx_req_get_device_info = hostapd_rx_req_get_device_info; + ctx->rx_req_put_message = hostapd_rx_req_put_message; + ctx->rx_req_get_ap_settings = hostapd_rx_req_get_ap_settings; + ctx->rx_req_set_ap_settings = hostapd_rx_req_set_ap_settings; + ctx->rx_req_del_ap_settings = hostapd_rx_req_del_ap_settings; + ctx->rx_req_get_sta_settings = hostapd_rx_req_get_sta_settings; + ctx->rx_req_set_sta_settings = hostapd_rx_req_set_sta_settings; + ctx->rx_req_del_sta_settings = hostapd_rx_req_del_sta_settings; + ctx->rx_req_put_wlan_response = hostapd_rx_req_put_wlan_response; + ctx->rx_req_set_selected_registrar = + hostapd_rx_req_set_selected_registrar; + ctx->rx_req_reboot_ap = hostapd_rx_req_reboot_ap; + ctx->rx_req_reset_ap = hostapd_rx_req_reset_ap; + ctx->rx_req_reboot_sta = hostapd_rx_req_reboot_sta; + ctx->rx_req_reset_sta = hostapd_rx_req_reset_sta; + + hapd->wps_upnp = upnp_wps_device_init(ctx, wps, hapd); + if (hapd->wps_upnp == NULL) { + os_free(ctx); + return -1; + } + wps->wps_upnp = hapd->wps_upnp; + + if (upnp_wps_device_start(hapd->wps_upnp, hapd->conf->upnp_iface)) { + upnp_wps_device_deinit(hapd->wps_upnp); + hapd->wps_upnp = NULL; + return -1; + } + + return 0; +} + + +static void hostapd_wps_upnp_deinit(struct hostapd_data *hapd) +{ + upnp_wps_device_deinit(hapd->wps_upnp); +} + +#endif /* CONFIG_WPS_UPNP */ diff --git a/contrib/wpa/hostapd/wps_hostapd.h b/contrib/wpa/hostapd/wps_hostapd.h new file mode 100644 index 0000000..6615c62a --- /dev/null +++ b/contrib/wpa/hostapd/wps_hostapd.h @@ -0,0 +1,48 @@ +/* + * hostapd / WPS integration + * Copyright (c) 2008, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef WPS_HOSTAPD_H +#define WPS_HOSTAPD_H + +#ifdef CONFIG_WPS + +int hostapd_init_wps(struct hostapd_data *hapd, + struct hostapd_bss_config *conf); +void hostapd_deinit_wps(struct hostapd_data *hapd); +int hostapd_wps_add_pin(struct hostapd_data *hapd, const char *uuid, + const char *pin); +int hostapd_wps_button_pushed(struct hostapd_data *hapd); +void hostapd_wps_probe_req_rx(struct hostapd_data *hapd, const u8 *addr, + const u8 *ie, size_t ie_len); + +#else /* CONFIG_WPS */ + +static inline int hostapd_init_wps(struct hostapd_data *hapd, + struct hostapd_bss_config *conf) +{ + return 0; +} + +static inline void hostapd_deinit_wps(struct hostapd_data *hapd) +{ +} + +static inline void hostapd_wps_probe_req_rx(struct hostapd_data *hapd, + const u8 *addr, + const u8 *ie, size_t ie_len) +{ +} +#endif /* CONFIG_WPS */ + +#endif /* WPS_HOSTAPD_H */ |